From 7b15ed9ef455b6b66c6b376898a88aef5d6a9970 Mon Sep 17 00:00:00 2001 From: Ondřej Surý Date: Tue, 26 Apr 2011 09:55:32 +0200 Subject: Imported Upstream version 2011.04.13 --- AUTHORS | 6 + CONTRIBUTORS | 9 + doc/codelab/wiki/Makefile | 2 +- doc/codelab/wiki/final-noclosure.go | 2 +- doc/codelab/wiki/final-noerror.go | 4 +- doc/codelab/wiki/final-parsetemplate.go | 2 +- doc/codelab/wiki/final-template.go | 2 +- doc/codelab/wiki/index.html | 8 +- doc/codewalk/functions.xml | 115 ++ doc/codewalk/pig.go | 124 ++ doc/contrib.html | 47 +- doc/devel/release.html | 367 +++- doc/devel/roadmap.html | 32 +- doc/effective_go.html | 81 +- doc/go_faq.html | 5 +- doc/go_spec.html | 69 +- doc/install.html | 27 +- lib/codereview/codereview.py | 141 +- lib/godoc/search.txt | 4 +- misc/bbedit/Go.plist | 3 +- misc/cgo/stdio/Makefile | 3 - misc/cgo/stdio/align.go | 78 - misc/cgo/stdio/hello.go | 20 +- misc/cgo/stdio/test.go | 144 -- misc/cgo/stdio/test1.go | 29 - misc/cgo/test/Makefile | 23 + misc/cgo/test/align.go | 72 + misc/cgo/test/basic.go | 134 ++ misc/cgo/test/callback.go | 136 ++ misc/cgo/test/callback_c.c | 12 + misc/cgo/test/cgo_test.go | 5 + misc/cgo/test/issue1222.go | 29 + misc/cgo/test/issue1328.go | 30 + misc/cgo/test/issue1560.go | 46 + misc/cgo/test/runtime.c | 21 + misc/dashboard/README | 34 +- misc/dashboard/buildcontrol.py | 278 --- misc/dashboard/buildcron.sh | 58 - misc/dashboard/builder.sh | 95 - misc/dashboard/builder/doc.go | 6 +- misc/dashboard/builder/exec.go | 2 +- misc/dashboard/builder/main.go | 2 +- misc/dashboard/godashboard/gobuild.py | 4 +- misc/dashboard/godashboard/package.py | 21 +- misc/emacs/go-mode-load.el | 19 +- misc/emacs/go-mode.el | 58 +- misc/goplay/goplay.go | 2 +- misc/kate/go.xml | 18 +- misc/notepadplus/README | 8 + misc/notepadplus/go.xml | 66 + misc/notepadplus/userDefineLang.xml | 36 + misc/swig/callback/Makefile | 17 + misc/swig/callback/callback.h | 24 + misc/swig/callback/callback.swigcxx | 18 + misc/swig/callback/run | Bin 0 -> 1179384 bytes misc/swig/callback/run.go | 39 + misc/swig/stdio/Makefile | 17 + misc/swig/stdio/file.swig | 11 + misc/swig/stdio/hello | Bin 0 -> 231270 bytes misc/swig/stdio/hello.go | 11 + misc/vim/ftplugin/go/fmt.vim | 30 + misc/vim/indent/go.vim | 30 + misc/vim/readme.txt | 13 + misc/vim/syntax/go.vim | 4 +- src/Make.cmd | 4 +- src/Make.common | 9 + src/Make.inc | 35 +- src/Make.pkg | 61 +- src/all-qemu.bash | 16 + src/clean.bash | 4 +- src/cmd/5c/peep.c | 4 +- src/cmd/5c/txt.c | 4 + src/cmd/5g/ggen.c | 2 +- src/cmd/5g/gobj.c | 186 +- src/cmd/5l/asm.c | 5 + src/cmd/5l/l.h | 19 +- src/cmd/5l/noop.c | 18 +- src/cmd/5l/softfloat.c | 1 + src/cmd/5l/thumb.c | 1 + src/cmd/6c/peep.c | 4 +- src/cmd/6c/txt.c | 6 +- src/cmd/6g/ggen.c | 2 +- src/cmd/6g/gobj.c | 174 +- src/cmd/6l/asm.c | 44 +- src/cmd/6l/l.h | 25 +- src/cmd/6l/obj.c | 18 +- src/cmd/6l/pass.c | 9 +- src/cmd/8c/peep.c | 2 +- src/cmd/8c/txt.c | 4 + src/cmd/8g/ggen.c | 2 +- src/cmd/8g/gobj.c | 170 +- src/cmd/8g/peep.c | 29 +- src/cmd/8l/asm.c | 16 +- src/cmd/8l/l.h | 27 +- src/cmd/8l/pass.c | 15 +- src/cmd/Makefile | 5 +- src/cmd/cgo/ast.go | 20 +- src/cmd/cgo/gcc.go | 60 +- src/cmd/cgo/main.go | 8 +- src/cmd/cgo/out.go | 6 +- src/cmd/cgo/util.go | 20 +- src/cmd/gc/align.c | 12 + src/cmd/gc/builtin.c.boot | 6 +- src/cmd/gc/const.c | 2 +- src/cmd/gc/dcl.c | 41 +- src/cmd/gc/export.c | 15 +- src/cmd/gc/gen.c | 157 +- src/cmd/gc/go.h | 32 +- src/cmd/gc/go.y | 20 +- src/cmd/gc/lex.c | 4 - src/cmd/gc/obj.c | 54 + src/cmd/gc/print.c | 2 - src/cmd/gc/range.c | 4 +- src/cmd/gc/reflect.c | 65 +- src/cmd/gc/runtime.go | 4 +- src/cmd/gc/select.c | 70 +- src/cmd/gc/sinit.c | 2 +- src/cmd/gc/subr.c | 41 +- src/cmd/gc/typecheck.c | 21 +- src/cmd/gc/unsafe.go | 2 +- src/cmd/gc/walk.c | 15 +- src/cmd/godefs/stabs.c | 3 +- src/cmd/godoc/codewalk.go | 2 +- src/cmd/godoc/dirtrees.go | 8 +- src/cmd/godoc/format.go | 4 +- src/cmd/godoc/godoc.go | 11 +- src/cmd/godoc/index.go | 2 +- src/cmd/godoc/main.go | 9 +- src/cmd/godoc/spec.go | 6 +- src/cmd/godoc/utils.go | 2 +- src/cmd/gofix/Makefile | 24 + src/cmd/gofix/doc.go | 34 + src/cmd/gofix/fix.go | 422 +++++ src/cmd/gofix/httpserver.go | 140 ++ src/cmd/gofix/httpserver_test.go | 53 + src/cmd/gofix/main.go | 264 +++ src/cmd/gofix/main_test.go | 126 ++ src/cmd/gofix/netdial.go | 114 ++ src/cmd/gofix/netdial_test.go | 51 + src/cmd/gofix/osopen.go | 122 ++ src/cmd/gofix/osopen_test.go | 59 + src/cmd/gofix/procattr.go | 61 + src/cmd/gofix/procattr_test.go | 74 + src/cmd/gofix/reflect.go | 843 +++++++++ src/cmd/gofix/reflect_test.go | 31 + src/cmd/gofix/testdata/reflect.asn1.go.in | 815 ++++++++ src/cmd/gofix/testdata/reflect.asn1.go.out | 815 ++++++++ src/cmd/gofix/testdata/reflect.datafmt.go.in | 731 ++++++++ src/cmd/gofix/testdata/reflect.datafmt.go.out | 731 ++++++++ src/cmd/gofix/testdata/reflect.decode.go.in | 907 +++++++++ src/cmd/gofix/testdata/reflect.decode.go.out | 910 +++++++++ src/cmd/gofix/testdata/reflect.decoder.go.in | 196 ++ src/cmd/gofix/testdata/reflect.decoder.go.out | 196 ++ src/cmd/gofix/testdata/reflect.dnsmsg.go.in | 779 ++++++++ src/cmd/gofix/testdata/reflect.dnsmsg.go.out | 779 ++++++++ src/cmd/gofix/testdata/reflect.encode.go.in | 367 ++++ src/cmd/gofix/testdata/reflect.encode.go.out | 367 ++++ src/cmd/gofix/testdata/reflect.encoder.go.in | 240 +++ src/cmd/gofix/testdata/reflect.encoder.go.out | 240 +++ src/cmd/gofix/testdata/reflect.export.go.in | 400 ++++ src/cmd/gofix/testdata/reflect.export.go.out | 400 ++++ src/cmd/gofix/testdata/reflect.print.go.in | 945 ++++++++++ src/cmd/gofix/testdata/reflect.print.go.out | 945 ++++++++++ src/cmd/gofix/testdata/reflect.quick.go.in | 364 ++++ src/cmd/gofix/testdata/reflect.quick.go.out | 365 ++++ src/cmd/gofix/testdata/reflect.read.go.in | 620 +++++++ src/cmd/gofix/testdata/reflect.read.go.out | 620 +++++++ src/cmd/gofix/testdata/reflect.scan.go.in | 1084 +++++++++++ src/cmd/gofix/testdata/reflect.scan.go.out | 1084 +++++++++++ src/cmd/gofix/testdata/reflect.script.go.in | 359 ++++ src/cmd/gofix/testdata/reflect.script.go.out | 359 ++++ src/cmd/gofix/testdata/reflect.template.go.in | 1043 +++++++++++ src/cmd/gofix/testdata/reflect.template.go.out | 1044 +++++++++++ src/cmd/gofix/testdata/reflect.type.go.in | 789 ++++++++ src/cmd/gofix/testdata/reflect.type.go.out | 789 ++++++++ src/cmd/gofix/typecheck.go | 579 ++++++ src/cmd/gofmt/Makefile | 4 +- src/cmd/gofmt/doc.go | 16 +- src/cmd/gofmt/gofmt.go | 103 +- src/cmd/gofmt/gofmt_test.go | 81 + src/cmd/gofmt/rewrite.go | 118 +- src/cmd/gofmt/testdata/rewrite1.golden | 8 + src/cmd/gofmt/testdata/rewrite1.input | 8 + src/cmd/gofmt/testdata/test.sh | 65 - src/cmd/goinstall/Makefile | 15 + src/cmd/goinstall/doc.go | 1 + src/cmd/goinstall/main.go | 2 +- src/cmd/goinstall/make.go | 56 +- src/cmd/goinstall/parse.go | 69 +- src/cmd/goinstall/syslist_test.go | 61 + src/cmd/gopack/ar.c | 177 +- src/cmd/gopack/doc.go | 5 +- src/cmd/gotest/Makefile | 16 +- src/cmd/gotest/doc.go | 92 +- src/cmd/gotest/flag.go | 155 ++ src/cmd/gotest/gotest | 197 -- src/cmd/gotest/gotest.go | 415 +++++ src/cmd/gotest/gotry | 167 -- src/cmd/gotry/Makefile | 18 + src/cmd/gotry/gotry | 167 ++ src/cmd/gotype/Makefile | 17 + src/cmd/gotype/doc.go | 59 + src/cmd/gotype/gotype.go | 198 ++ src/cmd/gotype/gotype_test.go | 52 + src/cmd/gotype/testdata/test1.go | 6 + src/cmd/govet/govet.go | 116 +- src/cmd/goyacc/goyacc.go | 122 +- src/cmd/goyacc/units.y | 32 +- src/cmd/ld/data.c | 31 +- src/cmd/ld/dwarf.c | 51 +- src/cmd/ld/elf.c | 38 +- src/cmd/ld/elf.h | 4 +- src/cmd/ld/go.c | 13 +- src/cmd/ld/ldmacho.c | 32 +- src/cmd/ld/lib.c | 70 +- src/cmd/ld/lib.h | 38 +- src/cmd/ld/pe.c | 8 +- src/cmd/ld/symtab.c | 30 + src/cmd/nm/doc.go | 3 + src/cmd/prof/gopprof | 23 +- src/lib9/Makefile | 2 +- src/libmach/Makefile | 2 +- src/libmach/darwin.c | 2 +- src/libmach/freebsd.c | 5 + src/libmach/windows.c | 8 + src/make.bash | 11 + src/pkg/Makefile | 33 +- src/pkg/archive/tar/reader.go | 10 +- src/pkg/archive/tar/reader_test.go | 8 +- src/pkg/archive/tar/writer_test.go | 3 + src/pkg/archive/zip/reader.go | 61 +- src/pkg/archive/zip/reader_test.go | 6 + src/pkg/asn1/asn1.go | 102 +- src/pkg/asn1/asn1_test.go | 9 +- src/pkg/asn1/common.go | 22 +- src/pkg/asn1/marshal.go | 62 +- src/pkg/asn1/marshal_test.go | 24 + src/pkg/big/int.go | 93 +- src/pkg/big/int_test.go | 50 +- src/pkg/big/nat.go | 47 + src/pkg/bufio/bufio.go | 95 +- src/pkg/bufio/bufio_test.go | 134 +- src/pkg/bytes/buffer_test.go | 12 +- src/pkg/bytes/bytes.go | 18 +- src/pkg/bytes/bytes_test.go | 5 +- src/pkg/compress/lzw/reader_test.go | 23 +- src/pkg/compress/lzw/writer_test.go | 31 +- src/pkg/compress/zlib/writer_test.go | 4 +- src/pkg/container/vector/numbers_test.go | 9 + src/pkg/crypto/block/Makefile | 19 - src/pkg/crypto/block/cbc.go | 71 - src/pkg/crypto/block/cfb.go | 96 - src/pkg/crypto/block/cfb_aes_test.go | 311 ---- src/pkg/crypto/block/cipher.go | 57 - src/pkg/crypto/block/cmac.go | 105 -- src/pkg/crypto/block/cmac_aes_test.go | 130 -- src/pkg/crypto/block/ctr.go | 67 - src/pkg/crypto/block/eax.go | 253 --- src/pkg/crypto/block/eax_aes_test.go | 140 -- src/pkg/crypto/block/ecb.go | 270 --- src/pkg/crypto/block/ecb_aes_test.go | 127 -- src/pkg/crypto/block/ecb_test.go | 181 -- src/pkg/crypto/block/ofb.go | 60 - src/pkg/crypto/block/ofb_aes_test.go | 108 -- src/pkg/crypto/block/xor.go | 124 -- src/pkg/crypto/block/xor_test.go | 168 -- src/pkg/crypto/cipher/ctr.go | 4 + src/pkg/crypto/des/Makefile | 13 + src/pkg/crypto/des/block.go | 98 + src/pkg/crypto/des/cipher.go | 103 + src/pkg/crypto/des/const.go | 139 ++ src/pkg/crypto/des/des_test.go | 1497 +++++++++++++++ src/pkg/crypto/ecdsa/Makefile | 11 + src/pkg/crypto/ecdsa/ecdsa.go | 149 ++ src/pkg/crypto/ecdsa/ecdsa_test.go | 227 +++ src/pkg/crypto/elliptic/elliptic.go | 5 + src/pkg/crypto/elliptic/elliptic_test.go | 3 + src/pkg/crypto/openpgp/armor/armor.go | 6 +- src/pkg/crypto/openpgp/armor/encode.go | 4 +- src/pkg/crypto/openpgp/packet/packet.go | 30 +- src/pkg/crypto/openpgp/packet/packet_test.go | 20 + src/pkg/crypto/openpgp/packet/private_key.go | 31 +- src/pkg/crypto/openpgp/packet/public_key.go | 46 +- src/pkg/crypto/openpgp/packet/public_key_test.go | 12 +- src/pkg/crypto/openpgp/packet/signature.go | 108 +- src/pkg/crypto/openpgp/read_test.go | 32 +- src/pkg/crypto/openpgp/s2k/s2k_test.go | 3 + src/pkg/crypto/openpgp/write.go | 6 +- src/pkg/crypto/openpgp/write_test.go | 16 +- src/pkg/crypto/rand/rand_test.go | 6 +- src/pkg/crypto/rand/rand_unix.go | 2 +- src/pkg/crypto/rsa/pkcs1v15.go | 10 +- src/pkg/crypto/rsa/pkcs1v15_test.go | 6 +- src/pkg/crypto/rsa/rsa.go | 167 +- src/pkg/crypto/rsa/rsa_test.go | 94 +- src/pkg/crypto/tls/common.go | 14 +- src/pkg/crypto/tls/conn.go | 14 +- src/pkg/crypto/tls/generate_cert.go | 10 +- src/pkg/crypto/tls/handshake_client.go | 33 + src/pkg/crypto/tls/handshake_client_test.go | 2 +- src/pkg/crypto/tls/handshake_messages_test.go | 8 +- src/pkg/crypto/tls/tls.go | 19 +- src/pkg/crypto/x509/x509.go | 104 +- src/pkg/crypto/x509/x509_test.go | 7 +- src/pkg/debug/elf/file.go | 8 +- src/pkg/debug/gosym/Makefile | 6 + src/pkg/debug/gosym/pclntab_test.go | 3 - src/pkg/debug/macho/file.go | 2 +- src/pkg/debug/pe/file.go | 10 +- src/pkg/debug/proc/proc_darwin.go | 2 +- src/pkg/debug/proc/proc_freebsd.go | 2 +- src/pkg/debug/proc/proc_linux.go | 20 +- src/pkg/debug/proc/proc_windows.go | 2 +- src/pkg/ebnf/ebnf_test.go | 45 +- src/pkg/ebnf/parser.go | 8 +- src/pkg/encoding/binary/binary.go | 164 +- src/pkg/encoding/binary/binary_test.go | 2 +- src/pkg/exec/exec.go | 16 +- src/pkg/exec/exec_test.go | 52 + src/pkg/exp/datafmt/datafmt.go | 42 +- src/pkg/exp/datafmt/parser.go | 10 +- src/pkg/exp/draw/x11/auth.go | 2 +- src/pkg/exp/draw/x11/conn.go | 6 +- src/pkg/exp/eval/bridge.go | 91 +- src/pkg/exp/eval/eval | Bin 0 -> 3500057 bytes src/pkg/exp/eval/eval_test.go | 8 +- src/pkg/exp/eval/stmt.go | 13 +- src/pkg/exp/eval/stmt_test.go | 2 +- src/pkg/exp/eval/type.go | 2 +- src/pkg/exp/ogle/cmd.go | 6 +- src/pkg/exp/ogle/process.go | 6 +- src/pkg/exp/ogle/rruntime.go | 14 +- src/pkg/expvar/expvar.go | 2 +- src/pkg/flag/flag.go | 49 +- src/pkg/flag/flag_test.go | 7 + src/pkg/fmt/fmt_test.go | 14 + src/pkg/fmt/format.go | 22 +- src/pkg/fmt/print.go | 85 +- src/pkg/fmt/scan.go | 97 +- src/pkg/fmt/scan_test.go | 27 +- src/pkg/go/ast/Makefile | 1 + src/pkg/go/ast/ast.go | 58 +- src/pkg/go/ast/filter.go | 6 +- src/pkg/go/ast/print.go | 94 +- src/pkg/go/ast/print_test.go | 80 + src/pkg/go/ast/resolve.go | 188 ++ src/pkg/go/ast/scope.go | 268 +-- src/pkg/go/ast/walk.go | 8 +- src/pkg/go/doc/comment.go | 2 +- src/pkg/go/doc/doc.go | 8 +- src/pkg/go/parser/interface.go | 7 +- src/pkg/go/parser/parser.go | 696 +++++-- src/pkg/go/parser/parser_test.go | 3 +- src/pkg/go/printer/nodes.go | 114 +- src/pkg/go/printer/printer.go | 196 +- src/pkg/go/printer/printer_test.go | 37 +- src/pkg/go/printer/testdata/declarations.golden | 77 +- src/pkg/go/printer/testdata/declarations.input | 29 + src/pkg/go/printer/testdata/expressions.golden | 34 +- src/pkg/go/printer/testdata/expressions.input | 20 +- src/pkg/go/printer/testdata/expressions.raw | 37 +- src/pkg/go/printer/testdata/slow.golden | 85 + src/pkg/go/printer/testdata/slow.input | 85 + src/pkg/go/scanner/scanner.go | 26 +- src/pkg/go/scanner/scanner_test.go | 60 +- src/pkg/go/token/token.go | 15 +- src/pkg/go/typechecker/Makefile | 1 + src/pkg/go/typechecker/scope.go | 72 +- src/pkg/go/typechecker/testdata/test0.go | 94 - src/pkg/go/typechecker/testdata/test0.src | 94 + src/pkg/go/typechecker/testdata/test1.go | 13 - src/pkg/go/typechecker/testdata/test1.src | 13 + src/pkg/go/typechecker/testdata/test3.go | 38 - src/pkg/go/typechecker/testdata/test3.src | 41 + src/pkg/go/typechecker/testdata/test4.go | 11 - src/pkg/go/typechecker/testdata/test4.src | 11 + src/pkg/go/typechecker/type.go | 125 ++ src/pkg/go/typechecker/typechecker.go | 82 +- src/pkg/go/typechecker/typechecker_test.go | 4 +- src/pkg/go/typechecker/universe.go | 6 +- src/pkg/go/types/Makefile | 15 + src/pkg/go/types/const.go | 347 ++++ src/pkg/go/types/exportdata.go | 135 ++ src/pkg/go/types/gcimporter.go | 786 ++++++++ src/pkg/go/types/gcimporter_test.go | 111 ++ src/pkg/go/types/testdata/exports.go | 89 + src/pkg/go/types/types.go | 122 ++ src/pkg/go/types/universe.go | 113 ++ src/pkg/gob/codec_test.go | 57 +- src/pkg/gob/decode.go | 205 +- src/pkg/gob/decoder.go | 11 +- src/pkg/gob/dump.go | 2 +- src/pkg/gob/encode.go | 148 +- src/pkg/gob/encoder.go | 35 +- src/pkg/gob/encoder_test.go | 2 +- src/pkg/gob/gobencdec_test.go | 61 +- src/pkg/gob/timing_test.go | 90 + src/pkg/gob/type.go | 179 +- src/pkg/hash/fnv/Makefile | 11 + src/pkg/hash/fnv/fnv.go | 133 ++ src/pkg/hash/fnv/fnv_test.go | 167 ++ src/pkg/html/parse_test.go | 2 +- src/pkg/http/cgi/Makefile | 3 +- src/pkg/http/cgi/cgi.go | 201 -- src/pkg/http/cgi/cgi_test.go | 247 --- src/pkg/http/cgi/child.go | 192 ++ src/pkg/http/cgi/child_test.go | 83 + src/pkg/http/cgi/host.go | 221 +++ src/pkg/http/cgi/host_test.go | 273 +++ src/pkg/http/cgi/matryoshka_test.go | 74 + src/pkg/http/cgi/testdata/test.cgi | 6 +- src/pkg/http/client.go | 79 +- src/pkg/http/client_test.go | 23 +- src/pkg/http/cookie.go | 230 +-- src/pkg/http/cookie_test.go | 30 +- src/pkg/http/dump.go | 4 +- src/pkg/http/export_test.go | 34 + src/pkg/http/fs.go | 49 +- src/pkg/http/fs_test.go | 24 + src/pkg/http/httptest/recorder.go | 44 +- src/pkg/http/httptest/server.go | 106 +- src/pkg/http/persist.go | 24 +- src/pkg/http/pprof/pprof.go | 36 +- src/pkg/http/proxy_test.go | 30 +- src/pkg/http/request.go | 17 + src/pkg/http/request_test.go | 37 +- src/pkg/http/requestwrite_test.go | 5 +- src/pkg/http/response.go | 8 +- src/pkg/http/response_test.go | 28 +- src/pkg/http/responsewrite_test.go | 32 +- src/pkg/http/serve_test.go | 273 ++- src/pkg/http/server.go | 189 +- src/pkg/http/transfer.go | 11 +- src/pkg/http/transport.go | 601 +++++- src/pkg/http/transport_test.go | 450 +++++ src/pkg/http/triv.go | 7 +- src/pkg/http/url.go | 4 +- src/pkg/http/url_test.go | 4 +- src/pkg/image/decode_test.go | 2 +- src/pkg/image/format.go | 22 +- src/pkg/image/png/reader_test.go | 16 +- src/pkg/image/png/writer_test.go | 6 +- src/pkg/image/ycbcr/Makefile | 11 + src/pkg/image/ycbcr/ycbcr.go | 174 ++ src/pkg/image/ycbcr/ycbcr_test.go | 33 + src/pkg/io/io.go | 24 +- src/pkg/io/io_test.go | 33 +- src/pkg/io/ioutil/ioutil.go | 44 +- src/pkg/io/ioutil/tempfile.go | 2 +- src/pkg/io/multi.go | 6 +- src/pkg/io/pipe.go | 307 +-- src/pkg/json/decode.go | 129 +- src/pkg/json/decode_test.go | 7 +- src/pkg/json/encode.go | 46 +- src/pkg/json/scanner_test.go | 25 +- src/pkg/log/log.go | 101 +- src/pkg/log/log_test.go | 33 + src/pkg/mime/type.go | 2 +- src/pkg/net/Makefile | 6 + src/pkg/net/cgo_stub.go | 21 + src/pkg/net/dial.go | 79 +- src/pkg/net/dialgoogle_test.go | 13 +- src/pkg/net/dnsclient.go | 109 +- src/pkg/net/dnsmsg.go | 145 +- src/pkg/net/fd.go | 112 +- src/pkg/net/fd_darwin.go | 34 +- src/pkg/net/fd_freebsd.go | 34 +- src/pkg/net/fd_linux.go | 88 +- src/pkg/net/fd_windows.go | 27 +- src/pkg/net/file.go | 119 ++ src/pkg/net/file_test.go | 131 ++ src/pkg/net/file_windows.go | 25 + src/pkg/net/hosts.go | 4 +- src/pkg/net/hosts_test.go | 1 - src/pkg/net/ip.go | 122 +- src/pkg/net/ip_test.go | 90 +- src/pkg/net/ipraw_test.go | 7 +- src/pkg/net/iprawsock.go | 2 +- src/pkg/net/ipsock.go | 17 +- src/pkg/net/lookup.go | 38 + src/pkg/net/multicast_test.go | 11 + src/pkg/net/net_test.go | 23 +- src/pkg/net/newpollserver.go | 2 +- src/pkg/net/parse.go | 2 +- src/pkg/net/parse_test.go | 2 +- src/pkg/net/port.go | 4 +- src/pkg/net/port_test.go | 2 - src/pkg/net/resolv_windows.go | 41 +- src/pkg/net/server_test.go | 10 +- src/pkg/net/sock.go | 22 +- src/pkg/net/tcpsock.go | 7 +- src/pkg/net/textproto/textproto.go | 2 +- src/pkg/net/timeout_test.go | 2 +- src/pkg/net/udpsock.go | 2 +- src/pkg/netchan/common.go | 6 +- src/pkg/netchan/export.go | 24 +- src/pkg/netchan/import.go | 8 +- src/pkg/netchan/netchan_test.go | 18 +- src/pkg/os/Makefile | 25 +- src/pkg/os/dir_darwin.go | 71 - src/pkg/os/dir_freebsd.go | 66 - src/pkg/os/dir_linux.go | 69 - src/pkg/os/dir_plan9.go | 284 +++ src/pkg/os/dir_unix.go | 54 + src/pkg/os/env_plan9.go | 91 + src/pkg/os/error.go | 81 - src/pkg/os/error_plan9.go | 60 + src/pkg/os/error_posix.go | 90 + src/pkg/os/exec.go | 133 +- src/pkg/os/exec_plan9.go | 114 ++ src/pkg/os/exec_posix.go | 127 ++ src/pkg/os/file.go | 277 +-- src/pkg/os/file_plan9.go | 240 +++ src/pkg/os/file_posix.go | 246 +++ src/pkg/os/file_unix.go | 26 +- src/pkg/os/file_windows.go | 10 +- src/pkg/os/getwd.go | 2 +- src/pkg/os/inotify/inotify_linux.go | 2 +- src/pkg/os/inotify/inotify_linux_test.go | 26 +- src/pkg/os/os_test.go | 98 +- src/pkg/os/path.go | 4 +- src/pkg/os/path_test.go | 27 +- src/pkg/os/proc.go | 4 +- src/pkg/os/stat_darwin.go | 8 +- src/pkg/os/stat_freebsd.go | 8 +- src/pkg/os/stat_linux.go | 8 +- src/pkg/os/stat_plan9.go | 84 + src/pkg/os/sys_linux.go | 2 +- src/pkg/os/sys_plan9.go | 27 + src/pkg/os/time.go | 4 +- src/pkg/path/filepath/Makefile | 5 +- src/pkg/path/filepath/match.go | 55 +- src/pkg/path/filepath/match_test.go | 47 +- src/pkg/path/filepath/path.go | 128 +- src/pkg/path/filepath/path_plan9.go | 28 + src/pkg/path/filepath/path_test.go | 147 +- src/pkg/path/filepath/path_unix.go | 18 + src/pkg/path/filepath/path_windows.go | 37 + src/pkg/reflect/all_test.go | 412 ++-- src/pkg/reflect/deepequal.go | 58 +- src/pkg/reflect/tostring_test.go | 54 +- src/pkg/reflect/type.go | 757 ++++---- src/pkg/reflect/value.go | 2167 ++++++++++++---------- src/pkg/rpc/client.go | 45 +- src/pkg/rpc/jsonrpc/client.go | 2 +- src/pkg/rpc/server.go | 139 +- src/pkg/rpc/server_test.go | 62 +- src/pkg/runtime/386/asm.s | 253 ++- src/pkg/runtime/Makefile | 2 + src/pkg/runtime/amd64/asm.s | 194 +- src/pkg/runtime/amd64/traceback.c | 9 +- src/pkg/runtime/arm/asm.s | 65 +- src/pkg/runtime/arm/traceback.c | 9 +- src/pkg/runtime/cgocall.c | 219 ++- src/pkg/runtime/cgocall.h | 2 +- src/pkg/runtime/chan.c | 346 ++-- src/pkg/runtime/cpuprof.c | 421 +++++ src/pkg/runtime/darwin/386/defs.h | 29 +- src/pkg/runtime/darwin/386/signal.c | 57 +- src/pkg/runtime/darwin/386/sys.s | 5 + src/pkg/runtime/darwin/amd64/defs.h | 34 +- src/pkg/runtime/darwin/amd64/signal.c | 57 +- src/pkg/runtime/darwin/amd64/sys.s | 13 +- src/pkg/runtime/darwin/defs.c | 6 + src/pkg/runtime/darwin/os.h | 4 + src/pkg/runtime/debug.go | 84 +- src/pkg/runtime/freebsd/386/defs.h | 21 +- src/pkg/runtime/freebsd/386/signal.c | 61 +- src/pkg/runtime/freebsd/386/sys.s | 5 + src/pkg/runtime/freebsd/amd64/defs.h | 23 +- src/pkg/runtime/freebsd/amd64/signal.c | 61 +- src/pkg/runtime/freebsd/amd64/sys.s | 8 + src/pkg/runtime/freebsd/defs.c | 7 + src/pkg/runtime/freebsd/mem.c | 2 +- src/pkg/runtime/freebsd/os.h | 5 + src/pkg/runtime/linux/386/defs.h | 12 +- src/pkg/runtime/linux/386/signal.c | 58 +- src/pkg/runtime/linux/386/sys.s | 9 + src/pkg/runtime/linux/amd64/defs.h | 13 +- src/pkg/runtime/linux/amd64/signal.c | 58 +- src/pkg/runtime/linux/amd64/sys.s | 8 + src/pkg/runtime/linux/arm/defs.h | 42 +- src/pkg/runtime/linux/arm/signal.c | 59 +- src/pkg/runtime/linux/arm/sys.s | 9 + src/pkg/runtime/linux/defs.c | 7 + src/pkg/runtime/linux/defs2.c | 8 +- src/pkg/runtime/linux/defs_arm.c | 31 +- src/pkg/runtime/linux/mem.c | 2 +- src/pkg/runtime/linux/os.h | 4 + src/pkg/runtime/linux/signals.h | 2 +- src/pkg/runtime/mem.go | 69 + src/pkg/runtime/mgc0.c | 5 - src/pkg/runtime/plan9/386/signal.c | 8 + src/pkg/runtime/pprof/pprof.go | 69 + src/pkg/runtime/pprof/pprof_test.go | 77 + src/pkg/runtime/proc.c | 367 ++-- src/pkg/runtime/reflect.goc | 12 +- src/pkg/runtime/runtime-gdb.py | 14 +- src/pkg/runtime/runtime.h | 17 +- src/pkg/runtime/windows/386/signal.c | 8 + src/pkg/runtime/windows/386/sys.s | 2 +- src/pkg/scanner/scanner.go | 45 +- src/pkg/scanner/scanner_test.go | 68 +- src/pkg/smtp/smtp.go | 2 +- src/pkg/sort/sort_test.go | 9 +- src/pkg/strconv/fp_test.go | 2 +- src/pkg/strings/strings.go | 51 +- src/pkg/strings/strings_test.go | 84 +- src/pkg/sync/atomic/asm_arm.s | 41 + src/pkg/sync/atomic/atomic_test.go | 55 +- src/pkg/sync/atomic/doc.go | 5 + src/pkg/sync/rwmutex_test.go | 24 +- src/pkg/sync/waitgroup.go | 2 +- src/pkg/syscall/Makefile | 3 + src/pkg/syscall/asm_darwin_386.s | 54 + src/pkg/syscall/asm_darwin_amd64.s | 21 + src/pkg/syscall/asm_freebsd_386.s | 54 + src/pkg/syscall/asm_freebsd_amd64.s | 20 + src/pkg/syscall/asm_linux_386.s | 47 +- src/pkg/syscall/asm_linux_amd64.s | 22 + src/pkg/syscall/asm_linux_arm.s | 28 + src/pkg/syscall/asm_plan9_386.s | 151 ++ src/pkg/syscall/exec_plan9.go | 521 ++++++ src/pkg/syscall/exec_unix.go | 102 +- src/pkg/syscall/exec_windows.go | 338 ++-- src/pkg/syscall/mkall.sh | 50 +- src/pkg/syscall/mkerrors.sh | 12 +- src/pkg/syscall/mksyscall.pl | 237 +++ src/pkg/syscall/mksyscall.sh | 206 -- src/pkg/syscall/mksyscall_windows.pl | 293 +++ src/pkg/syscall/mksyscall_windows.sh | 285 --- src/pkg/syscall/mksysnum_darwin.pl | 38 + src/pkg/syscall/mksysnum_darwin.sh | 38 - src/pkg/syscall/mksysnum_freebsd.pl | 41 + src/pkg/syscall/mksysnum_freebsd.sh | 41 - src/pkg/syscall/mksysnum_linux.pl | 36 + src/pkg/syscall/mksysnum_linux.sh | 36 - src/pkg/syscall/mksysnum_plan9.sh | 25 + src/pkg/syscall/str.go | 4 +- src/pkg/syscall/syscall.go | 65 + src/pkg/syscall/syscall_bsd.go | 47 +- src/pkg/syscall/syscall_darwin.go | 76 +- src/pkg/syscall/syscall_darwin_386.go | 4 +- src/pkg/syscall/syscall_darwin_amd64.go | 2 +- src/pkg/syscall/syscall_freebsd.go | 78 +- src/pkg/syscall/syscall_freebsd_386.go | 2 + src/pkg/syscall/syscall_linux.go | 123 +- src/pkg/syscall/syscall_linux_386.go | 50 +- src/pkg/syscall/syscall_linux_amd64.go | 32 +- src/pkg/syscall/syscall_linux_arm.go | 45 +- src/pkg/syscall/syscall_plan9.go | 343 ++++ src/pkg/syscall/syscall_plan9_386.go | 5 + src/pkg/syscall/syscall_unix.go | 3 +- src/pkg/syscall/syscall_windows.go | 7 +- src/pkg/syscall/types_freebsd.c | 21 + src/pkg/syscall/types_plan9.c | 115 ++ src/pkg/syscall/zerrors_darwin_386.go | 29 +- src/pkg/syscall/zerrors_darwin_amd64.go | 29 +- src/pkg/syscall/zerrors_freebsd_386.go | 1806 ++++++++++-------- src/pkg/syscall/zerrors_freebsd_amd64.go | 1806 ++++++++++-------- src/pkg/syscall/zerrors_linux_386.go | 52 + src/pkg/syscall/zerrors_linux_amd64.go | 52 + src/pkg/syscall/zerrors_linux_arm.go | 32 + src/pkg/syscall/zerrors_plan9_386.go | 25 + src/pkg/syscall/zsyscall_darwin_386.go | 89 +- src/pkg/syscall/zsyscall_darwin_amd64.go | 89 +- src/pkg/syscall/zsyscall_freebsd_386.go | 89 +- src/pkg/syscall/zsyscall_freebsd_amd64.go | 89 +- src/pkg/syscall/zsyscall_linux_386.go | 121 +- src/pkg/syscall/zsyscall_linux_amd64.go | 133 +- src/pkg/syscall/zsyscall_linux_arm.go | 137 +- src/pkg/syscall/zsyscall_plan9_386.go | 267 +++ src/pkg/syscall/zsyscall_windows_386.go | 20 +- src/pkg/syscall/zsysnum_darwin_amd64.go | 2 +- src/pkg/syscall/zsysnum_linux_386.go | 2 +- src/pkg/syscall/zsysnum_linux_amd64.go | 2 +- src/pkg/syscall/zsysnum_plan9_386.go | 47 + src/pkg/syscall/ztypes_darwin_amd64.go | 2 +- src/pkg/syscall/ztypes_freebsd_386.go | 50 + src/pkg/syscall/ztypes_freebsd_amd64.go | 51 + src/pkg/syscall/ztypes_plan9_386.go | 74 + src/pkg/syscall/ztypes_windows_386.go | 4 + src/pkg/syslog/Makefile | 1 + src/pkg/syslog/syslog.go | 54 +- src/pkg/syslog/syslog_unix.go | 31 + src/pkg/template/template.go | 90 +- src/pkg/testing/quick/quick.go | 114 +- src/pkg/testing/script/script.go | 16 +- src/pkg/testing/testing.go | 99 +- src/pkg/time/sleep.go | 5 +- src/pkg/time/sleep_test.go | 23 +- src/pkg/time/sys.go | 14 +- src/pkg/time/time.go | 2 +- src/pkg/time/time_test.go | 12 + src/pkg/try/try.go | 8 +- src/pkg/utf8/string_test.go | 11 +- src/pkg/websocket/client.go | 4 +- src/pkg/websocket/server.go | 4 +- src/pkg/websocket/websocket_test.go | 10 +- src/pkg/xml/read.go | 116 +- src/pkg/xml/read_test.go | 4 +- src/pkg/xml/xml.go | 1 - src/quietgcc.bash | 7 +- src/run.bash | 23 +- src/version.bash | 6 +- test/bench/regex-dna-parallel.go | 2 +- test/bench/regex-dna.go | 2 +- test/bugs/bug322.dir/main.go | 10 +- test/bugs/bug324.dir/main.go | 2 +- test/bugs/bug324.go | 2 +- test/chan/perm.go | 15 +- test/chan/select3.go | 20 +- test/closedchan.go | 157 +- test/cmp6.go | 10 +- test/ddd1.go | 3 +- test/env.go | 9 +- test/fixedbugs/bug016.go | 2 +- test/fixedbugs/bug055.go | 29 +- test/fixedbugs/bug069.go | 21 +- test/fixedbugs/bug076.go | 10 +- test/fixedbugs/bug077.go | 5 +- test/fixedbugs/bug081.go | 2 +- test/fixedbugs/bug091.go | 11 +- test/fixedbugs/bug137.go | 19 +- test/fixedbugs/bug140.go | 13 +- test/fixedbugs/bug177.go | 25 +- test/fixedbugs/bug178.go | 18 +- test/fixedbugs/bug179.go | 14 +- test/fixedbugs/bug196.go | 7 +- test/fixedbugs/bug234.go | 26 +- test/fixedbugs/bug242.go | 8 +- test/fixedbugs/bug243.go | 37 +- test/fixedbugs/bug252.go | 4 +- test/fixedbugs/bug274.go | 3 +- test/fixedbugs/bug323.go | 6 +- test/fixedbugs/bug325.go | 1 + test/fixedbugs/bug326.go | 41 + test/fixedbugs/bug327.go | 24 + test/func6.go | 14 + test/gc2.go | 41 + test/golden.out | 7 +- test/init.go | 4 +- test/interface/fake.go | 24 +- test/interface/private.go | 32 + test/interface/private1.go | 18 + test/ken/cplx3.go | 6 +- test/label.go | 60 + test/label1.go | 85 + test/named1.go | 9 +- test/run | 3 + test/syntax/chan.go | 4 +- test/syntax/if.go | 3 + 752 files changed, 53567 insertions(+), 15166 deletions(-) create mode 100644 doc/codewalk/functions.xml create mode 100644 doc/codewalk/pig.go delete mode 100644 misc/cgo/stdio/align.go delete mode 100644 misc/cgo/stdio/test.go delete mode 100644 misc/cgo/stdio/test1.go create mode 100644 misc/cgo/test/Makefile create mode 100644 misc/cgo/test/align.go create mode 100644 misc/cgo/test/basic.go create mode 100644 misc/cgo/test/callback.go create mode 100644 misc/cgo/test/callback_c.c create mode 100644 misc/cgo/test/cgo_test.go create mode 100644 misc/cgo/test/issue1222.go create mode 100644 misc/cgo/test/issue1328.go create mode 100644 misc/cgo/test/issue1560.go create mode 100644 misc/cgo/test/runtime.c delete mode 100644 misc/dashboard/buildcontrol.py delete mode 100644 misc/dashboard/buildcron.sh delete mode 100644 misc/dashboard/builder.sh create mode 100755 misc/notepadplus/README create mode 100755 misc/notepadplus/go.xml create mode 100755 misc/notepadplus/userDefineLang.xml create mode 100644 misc/swig/callback/Makefile create mode 100644 misc/swig/callback/callback.h create mode 100644 misc/swig/callback/callback.swigcxx create mode 100755 misc/swig/callback/run create mode 100644 misc/swig/callback/run.go create mode 100644 misc/swig/stdio/Makefile create mode 100644 misc/swig/stdio/file.swig create mode 100755 misc/swig/stdio/hello create mode 100644 misc/swig/stdio/hello.go create mode 100644 misc/vim/ftplugin/go/fmt.vim create mode 100644 misc/vim/indent/go.vim create mode 100755 src/all-qemu.bash create mode 100644 src/cmd/gofix/Makefile create mode 100644 src/cmd/gofix/doc.go create mode 100644 src/cmd/gofix/fix.go create mode 100644 src/cmd/gofix/httpserver.go create mode 100644 src/cmd/gofix/httpserver_test.go create mode 100644 src/cmd/gofix/main.go create mode 100644 src/cmd/gofix/main_test.go create mode 100644 src/cmd/gofix/netdial.go create mode 100644 src/cmd/gofix/netdial_test.go create mode 100644 src/cmd/gofix/osopen.go create mode 100644 src/cmd/gofix/osopen_test.go create mode 100644 src/cmd/gofix/procattr.go create mode 100644 src/cmd/gofix/procattr_test.go create mode 100644 src/cmd/gofix/reflect.go create mode 100644 src/cmd/gofix/reflect_test.go create mode 100644 src/cmd/gofix/testdata/reflect.asn1.go.in create mode 100644 src/cmd/gofix/testdata/reflect.asn1.go.out create mode 100644 src/cmd/gofix/testdata/reflect.datafmt.go.in create mode 100644 src/cmd/gofix/testdata/reflect.datafmt.go.out create mode 100644 src/cmd/gofix/testdata/reflect.decode.go.in create mode 100644 src/cmd/gofix/testdata/reflect.decode.go.out create mode 100644 src/cmd/gofix/testdata/reflect.decoder.go.in create mode 100644 src/cmd/gofix/testdata/reflect.decoder.go.out create mode 100644 src/cmd/gofix/testdata/reflect.dnsmsg.go.in create mode 100644 src/cmd/gofix/testdata/reflect.dnsmsg.go.out create mode 100644 src/cmd/gofix/testdata/reflect.encode.go.in create mode 100644 src/cmd/gofix/testdata/reflect.encode.go.out create mode 100644 src/cmd/gofix/testdata/reflect.encoder.go.in create mode 100644 src/cmd/gofix/testdata/reflect.encoder.go.out create mode 100644 src/cmd/gofix/testdata/reflect.export.go.in create mode 100644 src/cmd/gofix/testdata/reflect.export.go.out create mode 100644 src/cmd/gofix/testdata/reflect.print.go.in create mode 100644 src/cmd/gofix/testdata/reflect.print.go.out create mode 100644 src/cmd/gofix/testdata/reflect.quick.go.in create mode 100644 src/cmd/gofix/testdata/reflect.quick.go.out create mode 100644 src/cmd/gofix/testdata/reflect.read.go.in create mode 100644 src/cmd/gofix/testdata/reflect.read.go.out create mode 100644 src/cmd/gofix/testdata/reflect.scan.go.in create mode 100644 src/cmd/gofix/testdata/reflect.scan.go.out create mode 100644 src/cmd/gofix/testdata/reflect.script.go.in create mode 100644 src/cmd/gofix/testdata/reflect.script.go.out create mode 100644 src/cmd/gofix/testdata/reflect.template.go.in create mode 100644 src/cmd/gofix/testdata/reflect.template.go.out create mode 100644 src/cmd/gofix/testdata/reflect.type.go.in create mode 100644 src/cmd/gofix/testdata/reflect.type.go.out create mode 100644 src/cmd/gofix/typecheck.go create mode 100644 src/cmd/gofmt/gofmt_test.go create mode 100644 src/cmd/gofmt/testdata/rewrite1.golden create mode 100644 src/cmd/gofmt/testdata/rewrite1.input delete mode 100755 src/cmd/gofmt/testdata/test.sh create mode 100644 src/cmd/goinstall/syslist_test.go create mode 100644 src/cmd/gotest/flag.go delete mode 100755 src/cmd/gotest/gotest create mode 100644 src/cmd/gotest/gotest.go delete mode 100755 src/cmd/gotest/gotry create mode 100644 src/cmd/gotry/Makefile create mode 100755 src/cmd/gotry/gotry create mode 100644 src/cmd/gotype/Makefile create mode 100644 src/cmd/gotype/doc.go create mode 100644 src/cmd/gotype/gotype.go create mode 100644 src/cmd/gotype/gotype_test.go create mode 100644 src/cmd/gotype/testdata/test1.go delete mode 100644 src/pkg/crypto/block/Makefile delete mode 100644 src/pkg/crypto/block/cbc.go delete mode 100644 src/pkg/crypto/block/cfb.go delete mode 100644 src/pkg/crypto/block/cfb_aes_test.go delete mode 100644 src/pkg/crypto/block/cipher.go delete mode 100644 src/pkg/crypto/block/cmac.go delete mode 100644 src/pkg/crypto/block/cmac_aes_test.go delete mode 100644 src/pkg/crypto/block/ctr.go delete mode 100644 src/pkg/crypto/block/eax.go delete mode 100644 src/pkg/crypto/block/eax_aes_test.go delete mode 100644 src/pkg/crypto/block/ecb.go delete mode 100644 src/pkg/crypto/block/ecb_aes_test.go delete mode 100644 src/pkg/crypto/block/ecb_test.go delete mode 100644 src/pkg/crypto/block/ofb.go delete mode 100644 src/pkg/crypto/block/ofb_aes_test.go delete mode 100644 src/pkg/crypto/block/xor.go delete mode 100644 src/pkg/crypto/block/xor_test.go create mode 100644 src/pkg/crypto/des/Makefile create mode 100644 src/pkg/crypto/des/block.go create mode 100644 src/pkg/crypto/des/cipher.go create mode 100644 src/pkg/crypto/des/const.go create mode 100644 src/pkg/crypto/des/des_test.go create mode 100644 src/pkg/crypto/ecdsa/Makefile create mode 100644 src/pkg/crypto/ecdsa/ecdsa.go create mode 100644 src/pkg/crypto/ecdsa/ecdsa_test.go create mode 100755 src/pkg/exp/eval/eval create mode 100644 src/pkg/go/ast/print_test.go create mode 100644 src/pkg/go/ast/resolve.go create mode 100644 src/pkg/go/printer/testdata/slow.golden create mode 100644 src/pkg/go/printer/testdata/slow.input delete mode 100644 src/pkg/go/typechecker/testdata/test0.go create mode 100644 src/pkg/go/typechecker/testdata/test0.src delete mode 100644 src/pkg/go/typechecker/testdata/test1.go create mode 100644 src/pkg/go/typechecker/testdata/test1.src delete mode 100644 src/pkg/go/typechecker/testdata/test3.go create mode 100644 src/pkg/go/typechecker/testdata/test3.src delete mode 100644 src/pkg/go/typechecker/testdata/test4.go create mode 100644 src/pkg/go/typechecker/testdata/test4.src create mode 100644 src/pkg/go/typechecker/type.go create mode 100644 src/pkg/go/types/Makefile create mode 100644 src/pkg/go/types/const.go create mode 100644 src/pkg/go/types/exportdata.go create mode 100644 src/pkg/go/types/gcimporter.go create mode 100644 src/pkg/go/types/gcimporter_test.go create mode 100644 src/pkg/go/types/testdata/exports.go create mode 100644 src/pkg/go/types/types.go create mode 100644 src/pkg/go/types/universe.go create mode 100644 src/pkg/gob/timing_test.go create mode 100644 src/pkg/hash/fnv/Makefile create mode 100644 src/pkg/hash/fnv/fnv.go create mode 100644 src/pkg/hash/fnv/fnv_test.go delete mode 100644 src/pkg/http/cgi/cgi.go delete mode 100644 src/pkg/http/cgi/cgi_test.go create mode 100644 src/pkg/http/cgi/child.go create mode 100644 src/pkg/http/cgi/child_test.go create mode 100644 src/pkg/http/cgi/host.go create mode 100644 src/pkg/http/cgi/host_test.go create mode 100644 src/pkg/http/cgi/matryoshka_test.go create mode 100644 src/pkg/http/export_test.go create mode 100644 src/pkg/http/transport_test.go create mode 100644 src/pkg/image/ycbcr/Makefile create mode 100644 src/pkg/image/ycbcr/ycbcr.go create mode 100644 src/pkg/image/ycbcr/ycbcr_test.go create mode 100644 src/pkg/net/cgo_stub.go create mode 100644 src/pkg/net/file.go create mode 100644 src/pkg/net/file_test.go create mode 100644 src/pkg/net/file_windows.go create mode 100644 src/pkg/net/lookup.go delete mode 100644 src/pkg/os/dir_darwin.go delete mode 100644 src/pkg/os/dir_freebsd.go delete mode 100644 src/pkg/os/dir_linux.go create mode 100644 src/pkg/os/dir_plan9.go create mode 100644 src/pkg/os/dir_unix.go create mode 100644 src/pkg/os/env_plan9.go create mode 100644 src/pkg/os/error_plan9.go create mode 100644 src/pkg/os/error_posix.go create mode 100644 src/pkg/os/exec_plan9.go create mode 100644 src/pkg/os/exec_posix.go create mode 100644 src/pkg/os/file_plan9.go create mode 100644 src/pkg/os/file_posix.go create mode 100644 src/pkg/os/stat_plan9.go create mode 100644 src/pkg/os/sys_plan9.go create mode 100644 src/pkg/path/filepath/path_plan9.go create mode 100644 src/pkg/path/filepath/path_windows.go create mode 100644 src/pkg/runtime/cpuprof.c create mode 100644 src/pkg/runtime/mem.go create mode 100644 src/pkg/runtime/pprof/pprof_test.go create mode 100644 src/pkg/syscall/asm_plan9_386.s create mode 100644 src/pkg/syscall/exec_plan9.go create mode 100755 src/pkg/syscall/mksyscall.pl delete mode 100755 src/pkg/syscall/mksyscall.sh create mode 100755 src/pkg/syscall/mksyscall_windows.pl delete mode 100755 src/pkg/syscall/mksyscall_windows.sh create mode 100755 src/pkg/syscall/mksysnum_darwin.pl delete mode 100755 src/pkg/syscall/mksysnum_darwin.sh create mode 100755 src/pkg/syscall/mksysnum_freebsd.pl delete mode 100755 src/pkg/syscall/mksysnum_freebsd.sh create mode 100755 src/pkg/syscall/mksysnum_linux.pl delete mode 100755 src/pkg/syscall/mksysnum_linux.sh create mode 100755 src/pkg/syscall/mksysnum_plan9.sh create mode 100644 src/pkg/syscall/syscall_plan9.go create mode 100644 src/pkg/syscall/syscall_plan9_386.go create mode 100644 src/pkg/syscall/types_plan9.c create mode 100644 src/pkg/syscall/zerrors_plan9_386.go create mode 100644 src/pkg/syscall/zsyscall_plan9_386.go create mode 100644 src/pkg/syscall/zsysnum_plan9_386.go create mode 100644 src/pkg/syscall/ztypes_plan9_386.go create mode 100644 src/pkg/syslog/syslog_unix.go create mode 100644 test/fixedbugs/bug326.go create mode 100644 test/fixedbugs/bug327.go create mode 100644 test/func6.go create mode 100644 test/gc2.go create mode 100644 test/interface/private.go create mode 100644 test/interface/private1.go create mode 100644 test/label.go create mode 100644 test/label1.go diff --git a/AUTHORS b/AUTHORS index cbac729b2..33f30135d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -12,6 +12,7 @@ Abhinav Gupta Adrian O'Grady Albert Strasheim Alex Brainman +Alexey Borzenkov Amrut Joshi Andrei Vieru Andrew Skiba @@ -40,10 +41,12 @@ Dan Sinclair Daniel Fleischman Daniel Theophanes Dave Cheney +David Forsythe David G. Andersen David Titarenco Dean Prichard Devon H. O'Dell +Dmitry Chestnykh Eden Li Eoghan Sherry Eric Clark @@ -81,6 +84,8 @@ Kevin Ballard Kyle Consalus Kyle Lemons Lorenzo Stoakes +Lucio De Re +Luit van Drongelen Markus Duft Martin Neubauer Mathieu Lonjaret @@ -108,6 +113,7 @@ Pieter Droogendijk Raif S. Naffah Risto Jaakko Saarelma Robert Hencke +Roger Pau Monné Roger Peppe Ross Light Ryan Hitchman diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 913116e1e..57a5f66c7 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -36,6 +36,7 @@ Adam Langley Adrian O'Grady Albert Strasheim Alex Brainman +Alexey Borzenkov Amrut Joshi Andrei Vieru Andrew Gerrand @@ -76,11 +77,13 @@ Daniel Nadasi Daniel Theophanes Dave Cheney David Anderson +David Forsythe David G. Andersen David Symonds David Titarenco Dean Prichard Devon H. O'Dell +Dmitry Chestnykh Eden Li Eoghan Sherry Eric Clark @@ -111,6 +114,7 @@ Jan Mercl Jeff R. Allen Jim McGrath Joe Poirier +John DeNero Jonathan Wills Jos Visser Jose Luis Vázquez González @@ -127,11 +131,14 @@ Kyle Consalus Kyle Lemons Larry Hosken Lorenzo Stoakes +Lucio De Re +Luit van Drongelen Luuk van Dijk Mark Zavislak Markus Duft Martin Neubauer Mathieu Lonjaret +Matt Jones Maxim Ushakov Micah Stetson Michael Elkins @@ -163,11 +170,13 @@ Risto Jaakko Saarelma Rob Pike Robert Griesemer Robert Hencke +Roger Pau Monné Roger Peppe Ross Light Russ Cox Ryan Hitchman Sam Thorogood +Sameer Ajmani Scott Lawrence Scott Schwartz Sebastien Binet diff --git a/doc/codelab/wiki/Makefile b/doc/codelab/wiki/Makefile index 43f05b21d..09c3291a0 100644 --- a/doc/codelab/wiki/Makefile +++ b/doc/codelab/wiki/Makefile @@ -8,7 +8,7 @@ all: index.html include ../../../src/Make.common -CLEANFILES+=index.html srcextract.bin htmlify.bin +CLEANFILES+=index.html srcextract.bin htmlify.bin get.bin index.html: srcextract.bin htmlify.bin PATH=.:$$PATH awk '/^!/{system(substr($$0,2)); next} {print}' < wiki.html | tr -d '\r' > index.html diff --git a/doc/codelab/wiki/final-noclosure.go b/doc/codelab/wiki/final-noclosure.go index 99121f298..d09a0d7ab 100644 --- a/doc/codelab/wiki/final-noclosure.go +++ b/doc/codelab/wiki/final-noclosure.go @@ -73,7 +73,7 @@ func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) { http.Error(w, err.String(), http.StatusInternalServerError) return } - err = t.Execute(p, w) + err = t.Execute(w, p) if err != nil { http.Error(w, err.String(), http.StatusInternalServerError) } diff --git a/doc/codelab/wiki/final-noerror.go b/doc/codelab/wiki/final-noerror.go index 0f18912d2..5fcf1de76 100644 --- a/doc/codelab/wiki/final-noerror.go +++ b/doc/codelab/wiki/final-noerror.go @@ -35,14 +35,14 @@ func editHandler(w http.ResponseWriter, r *http.Request) { p = &Page{Title: title} } t, _ := template.ParseFile("edit.html", nil) - t.Execute(p, w) + t.Execute(w, p) } func viewHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[lenPath:] p, _ := loadPage(title) t, _ := template.ParseFile("view.html", nil) - t.Execute(p, w) + t.Execute(w, p) } func main() { diff --git a/doc/codelab/wiki/final-parsetemplate.go b/doc/codelab/wiki/final-parsetemplate.go index ea8977601..f25012eed 100644 --- a/doc/codelab/wiki/final-parsetemplate.go +++ b/doc/codelab/wiki/final-parsetemplate.go @@ -61,7 +61,7 @@ func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) { http.Error(w, err.String(), http.StatusInternalServerError) return } - err = t.Execute(p, w) + err = t.Execute(w, p) if err != nil { http.Error(w, err.String(), http.StatusInternalServerError) } diff --git a/doc/codelab/wiki/final-template.go b/doc/codelab/wiki/final-template.go index 4d6a2cfab..aab536ee1 100644 --- a/doc/codelab/wiki/final-template.go +++ b/doc/codelab/wiki/final-template.go @@ -53,7 +53,7 @@ func saveHandler(w http.ResponseWriter, r *http.Request) { func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) { t, _ := template.ParseFile(tmpl+".html", nil) - t.Execute(p, w) + t.Execute(w, p) } func main() { diff --git a/doc/codelab/wiki/index.html b/doc/codelab/wiki/index.html index fc8c27bfa..d059fa027 100644 --- a/doc/codelab/wiki/index.html +++ b/doc/codelab/wiki/index.html @@ -475,7 +475,7 @@ func editHandler(w http.ResponseWriter, r *http.Request) { p = &Page{Title: title} } t, _ := template.ParseFile("edit.html", nil) - t.Execute(p, w) + t.Execute(w, p) } @@ -527,7 +527,7 @@ func viewHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[lenPath:] p, _ := loadPage(title) t, _ := template.ParseFile("view.html", nil) - t.Execute(p, w) + t.Execute(w, p) } @@ -555,7 +555,7 @@ func editHandler(w http.ResponseWriter, r *http.Request) { func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) { t, _ := template.ParseFile(tmpl+".html", nil) - t.Execute(p, w) + t.Execute(w, p) } @@ -644,7 +644,7 @@ func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) { http.Error(w, err.String(), http.StatusInternalServerError) return } - err = t.Execute(p, w) + err = t.Execute(w, p) if err != nil { http.Error(w, err.String(), http.StatusInternalServerError) } diff --git a/doc/codewalk/functions.xml b/doc/codewalk/functions.xml new file mode 100644 index 000000000..986a017e1 --- /dev/null +++ b/doc/codewalk/functions.xml @@ -0,0 +1,115 @@ + + + + Go supports first class functions, higher-order functions, user-defined + function types, function literals, closures, and multiple return values. +

+ + This rich feature set supports a functional programming style in a strongly + typed language. +

+ + In this codewalk we will look at a simple program that simulates a dice game + called Pig and evaluates + basic strategies. +
+ + + Pig is a two-player game played with a 6-sided die. Each turn, you may roll or stay. +
    +
  • If you roll a 1, you lose all points for your turn and play passes to + your opponent. Any other roll adds its value to your turn score.
  • +
  • If you stay, your turn score is added to your total score, and play passes + to your opponent.
  • +
+ + The first person to reach 100 total points wins. +

+ + The score type stores the scores of the current and opposing + players, in addition to the points accumulated during the current turn. +
+ + + In Go, functions can be passed around just like any other value. A function's + type signature describes the types of its arguments and return values. +

+ + The action type is a function that takes a score + and returns the resulting score and whether the current turn is + over. +

+ + If the turn is over, the player and opponent fields + in the resulting score should be swapped, as it is now the other player's + turn. +
+ + + Go functions can return multiple values. +

+ + The functions roll and stay each return a pair of + values. They also match the action type signature. These + action functions define the rules of Pig. +
+ + + A function can use other functions as arguments and return values. +

+ + A strategy is a function that takes a score as input + and returns an action to perform.
+ (Remember, an action is itself a function.) +
+ + + Anonymous functions can be declared in Go, as in this example. Function + literals are closures: they inherit the scope of the function in which they + are declared. +

+ + One basic strategy in Pig is to continue rolling until you have accumulated at + least k points in a turn, and then stay. The argument k is + enclosed by this function literal, which matches the strategy type + signature. +
+ + + We simulate a game of Pig by calling an action to update the + score until one player reaches 100 points. Each + action is selected by calling the strategy function + associated with the current player. + + + + Functions can be compared for equality in Go. From the + language specification: + Function values are equal if they refer to the same function or if both are nil. +

+ + We enforce that a strategy function can only return a legal + action: either roll or stay. +
+ + + The roundRobin function simulates a tournament and tallies wins. + Each strategy plays each other strategy gamesPerSeries times. + + + + Variadic functions like ratioString take a variable number of + arguments. These arguments are available as a slice inside the function. + + + + The main function defines 100 basic strategies, simulates a round + robin tournament, and then prints the win/loss record of each strategy. +

+ + Among these strategies, staying at 25 is best, but the optimal strategy for + Pig is much more complex. +
+ +
diff --git a/doc/codewalk/pig.go b/doc/codewalk/pig.go new file mode 100644 index 000000000..9e415f589 --- /dev/null +++ b/doc/codewalk/pig.go @@ -0,0 +1,124 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "rand" +) + +const ( + win = 100 // The winning score in a game of Pig + gamesPerSeries = 10 // The number of games per series to simulate +) + +// A score includes scores accumulated in previous turns for each player, +// as well as the points scored by the current player in this turn. +type score struct { + player, opponent, thisTurn int +} + +// An action transitions stochastically to a resulting score. +type action func(current score) (result score, turnIsOver bool) + +// roll returns the (result, turnIsOver) outcome of simulating a die roll. +// If the roll value is 1, then thisTurn score is abandoned, and the players' +// roles swap. Otherwise, the roll value is added to thisTurn. +func roll(s score) (score, bool) { + outcome := rand.Intn(6) + 1 // A random int in [1, 6] + if outcome == 1 { + return score{s.opponent, s.player, 0}, true + } + return score{s.player, s.opponent, outcome + s.thisTurn}, false +} + +// stay returns the (result, turnIsOver) outcome of staying. +// thisTurn score is added to the player's score, and the players' roles swap. +func stay(s score) (score, bool) { + return score{s.opponent, s.player + s.thisTurn, 0}, true +} + +// A strategy chooses an action for any given score. +type strategy func(score) action + +// stayAtK returns a strategy that rolls until thisTurn is at least k, then stays. +func stayAtK(k int) strategy { + return func(s score) action { + if s.thisTurn >= k { + return stay + } + return roll + } +} + +// play simulates a Pig game and returns the winner (0 or 1). +func play(strategy0, strategy1 strategy) int { + strategies := []strategy{strategy0, strategy1} + var s score + var turnIsOver bool + currentPlayer := rand.Intn(2) // Randomly decide who plays first + for s.player+s.thisTurn < win { + action := strategies[currentPlayer](s) + if action != roll && action != stay { + panic(fmt.Sprintf("Player %d is cheating", currentPlayer)) + } + s, turnIsOver = action(s) + if turnIsOver { + currentPlayer = (currentPlayer + 1) % 2 + } + } + return currentPlayer +} + +// roundRobin simulates a series of games between every pair of strategies. +func roundRobin(strategies []strategy) ([]int, int) { + wins := make([]int, len(strategies)) + for i := 0; i < len(strategies); i++ { + for j := i + 1; j < len(strategies); j++ { + for k := 0; k < gamesPerSeries; k++ { + winner := play(strategies[i], strategies[j]) + if winner == 0 { + wins[i]++ + } else { + wins[j]++ + } + } + } + } + gamesPerStrategy := gamesPerSeries * (len(strategies) - 1) // no self play + return wins, gamesPerStrategy +} + +// ratioString takes a list of integer values and returns a string that lists +// each value and its percentage of the sum of all values. +// e.g., ratios(1, 2, 3) = "1/6 (16.7%), 2/6 (33.3%), 3/6 (50.0%)" +func ratioString(vals ...int) string { + total := 0 + for _, val := range vals { + total += val + } + s := "" + for _, val := range vals { + if s != "" { + s += ", " + } + pct := 100 * float64(val) / float64(total) + s += fmt.Sprintf("%d/%d (%0.1f%%)", val, total, pct) + } + return s +} + +func main() { + strategies := make([]strategy, win) + for k := range strategies { + strategies[k] = stayAtK(k + 1) + } + wins, games := roundRobin(strategies) + + for k := range strategies { + fmt.Printf("Wins, losses staying at k =% 4d: %s\n", + k+1, ratioString(wins[k], games-wins[k])) + } +} diff --git a/doc/contrib.html b/doc/contrib.html index 121cc45dc..9d0c42726 100644 --- a/doc/contrib.html +++ b/doc/contrib.html @@ -2,21 +2,38 @@
-

Resources for Developers

+

How you can help

-

Issue Tracker

-

Having an issue with Go? Check the tracker to see if it's a known issue.

-

If your issue is not listed, please file a bug report.

+

Reporting issues

-

Build Status

-

View the status of Go builds across the supported operating -systems and architectures.

+

+If you spot bugs, mistakes, or inconsistencies in the Go project's code or +documentation, please let us know by +filing a ticket +on our issue tracker. +(Of course, you should check it's not an existing issue before creating +a new one.) +

+ +

+We pride ourselves on being meticulous; no issue is too small. +

+ +

Contributing code

-

Contribution Guidelines

-

So, you want to contribute code to the Go project? That's great!

-

The first step is to read these contributions guidelines for information on -design, testing, and our code review process.

+

+Go is an open source project and we welcome contributions from the community. +

+

+To get started, read these contribution +guidelines for information on design, testing, and our code review process. +

+

+Check the tracker for +open issues that interest you. Those labeled +HelpWanted +are particularly in need of outside help. +

@@ -24,11 +41,15 @@ design, testing, and our code review process.

The Go Project

+

Build Status

+

View the status of Go builds across the supported operating +systems and architectures.

+

Roadmap

Features and ideas being developed or discussed by the Go team.

Release History

-

A summarization of the changes between tagged releases of Go.

+

A summary of the changes between tagged releases of Go.

Developer Mailing List

The golang-dev diff --git a/doc/devel/release.html b/doc/devel/release.html index c7691c766..f75cbf24f 100644 --- a/doc/devel/release.html +++ b/doc/devel/release.html @@ -5,7 +5,368 @@

This page summarizes the changes between tagged releases of Go. For full details, see the Mercurial change log.

-

2011-03-07

+

2011-04-13

+ +
+weekly.2011-04-13
+
+This weekly snapshot includes major changes to the reflect package and the
+os.Open function.  Code that uses reflect or os.Open will require updating,
+which can be done mechanically using the gofix tool.
+
+The reflect package's Type and Value types have changed.  Type is now an
+interface that implements all the possible type methods.  Instead of a type
+switch on a reflect.Type t, switch on t.Kind().  Value is now a struct value
+that implements all the possible value methods.  Instead of a type switch on a
+reflect.Value v, switch on v.Kind().  See the change for the full details:
+        http://code.google.com/p/go/source/detail?r=843855f3c026
+
+The os package's Open function has been replaced by three functions:
+        OpenFile(name, flag, perm) // same as old Open
+        Open(name) // same as old Open(name, O_RDONLY, 0)
+        Create(name) // same as old Open(name, O_RDWR|O_TRUNC|O_CREAT, 0666)
+
+To update your code to use the new APIs, run "gofix path/to/code".  Gofix can’t
+handle all situations perfectly, so read and test the changes it makes before
+committing them.
+
+Other changes:
+* archive/zip: add func OpenReader, type ReadCloser (thanks Dmitry Chestnykh).
+* asn1: Implement correct marshaling of length octets (thanks Luit van Drongelen).
+* big: don't crash when printing nil ints.
+* bufio: add ReadLine, to replace encoding/line.
+* build: make the build faster, quieter.
+* codereview: automatically port old diffs forward,
+        drop Author: line on self-clpatch,
+        recognize code URL without trailing slash.
+* crypto/block: remove deprecated package.
+* crypto/des: new package implementating DES and TDEA (thanks Yasuhiro Matsumoto).
+* crypto/ecdsa, crypto/rsa: use io.ReadFull to read from random source (thanks Dmitry Chestnykh).
+* crypto/rsa: add 3-prime support,
+        add support for precomputing CRT values,
+        flip the CRT code over so that it matches PKCS#1.
+* crypto/x509: expose complete DER data (thanks Mikkel Krautz).
+* doc: new "Functions" codewalk (thanks John DeNero).
+* doc/roadmap: add sections on tools, packages.
+* fmt: allow %U for unsigned integers.
+* gc: fixes and optimizations.
+* go/printer, gofmt: use blank to separate import rename from import path.
+* go/scanner: better TokenString output.
+* go/types: new Go type hierarchy implementation for AST.
+* godashboard: show packages at launchpad.net (thanks Gustavo Niemeyer).
+* gofix: add -diff, various fixes and helpers.
+* gotest: fix a bug in error handling,
+        fixes for [^.]_test file pattern (thanks Peter Mundy),
+        handle \r\n returned by gomake on Windows (thanks Alex Brainman).
+* gotype: use go/types GcImporter.
+* govet: make name-matching for printf etc. case-insensitive.
+* http: allow override of Content-Type for ServeFile,
+        client gzip support,
+        do not listen on 0.0.0.0 during test,
+        flesh out server Expect handling + tests.
+* image/ycbcr: new package.
+* image: allow "?" wildcards when registering image formats.
+* io: fixes for Read with n > 0, os.EOF (thanks Robert Hencke).
+* ld: correct Plan 9 compiler warnings (thanks Lucio De Re),
+        ELF header function declarations (thanks Lucio De Re),
+        fix Mach-O X86_64_RELOC_SIGNED relocations (thanks Mikkel Krautz),
+        fix Mach-O bss bug (thanks Mikkel Krautz),
+        fix dwarf decoding of strings for struct's fieldnames (thanks Luuk van Dijk),
+        fixes and optimizations (25% faster).
+* log: generalize getting and setting flags and prefix.
+* misc/cgo/life: enable build and test on Windows (thanks Alex Brainman).
+* misc/vim: add plugin with Fmt command (thanks Dmitry Chestnykh),
+        update type highlighting for new reflect package.
+* net: disable multicast tests by default (thanks Dave Cheney),
+        sort records returned by LookupMX (thanks Corey Thomasson).
+* openpgp: Fix improper := shadowing (thanks Gustavo Niemeyer).
+* os: rename Open to OpenFile, add new Open, Create,
+        fix Readdir in Plan 9 (thanks Fazlul Shahriar).
+* os/inotify: use _test for test files, not _obj.
+* pkg/path: enable tests on Windows (thanks Alex Brainman).
+* reflect: new Type and Value API.
+* src/pkg/Makefile: trim per-directory make output except on failure.
+* syscall: Add DT_* and MADV_* constants on Linux (thanks Albert Strasheim),
+        add Mmap, Munmap on Linux, FreeBSD, OS X,
+        fix StartProcess in Plan 9 (thanks Fazlul Shahriar),
+        fix Windows Signaled (thanks Alex Brainman).
+* test/bench: enable build and test on Windows (thanks Alex Brainman).
+
+ +

2011-04-04

+ +
+This release includes changes to the net package. Your code will require
+changes if it uses the Dial or LookupHost functions.
+
+The laddr argument has been removed from net.Dial, and the cname return value
+has been removed from net.LookupHost. The new net.LookupCNAME function can be
+used  to find the canonical host for a given name.  You can update your
+networking code with gofix.
+
+The gotest shell script has been replaced by a Go program, making testing
+significantly faster.
+
+Other changes:
+* asn1: extensions needed for parsing Kerberos.
+* bufio: Write and WriteString cleanup (thanks Evan Shaw).
+* bytes, strings: simplify Join (thanks Evan Shaw).
+* crypto/cipher: bad CTR IV length now triggers panic.
+* crypto/tls: extend NPN support to the client,
+	added X509KeyPair function to parse a Certificate from memory.
+* crypto/x509: parse Extended Key Usage extension (thanks Mikkel Krautz).
+* debug/gosym: remove need for gotest to run preparatory commands.
+* fmt: implement precision (length of input) values for %q: %.20q.
+* go/parser: fix scoping for local type declarations (thanks Roger Peppe),
+	package name must not be the blank identifier.
+* go/printer, gofmt: remove special case for multi-line raw strings.
+* gopack: add P flag to remove prefix from filename information.
+* gotest: add -test.timeout option,
+	replace the shell script with the compiled program written in go,
+	execute gomake properly on Windows (thanks Alex Brainman).
+* gotry: move into its own directory, separate from gotest.
+* gotype: support for more tests, added one new test.
+* http: add Transport.MaxIdleConnsPerHost,
+	use upper case hex in URL escaping (thanks Matt Jones).
+* httptest: add NewTLSServer.
+* misc/kate: reorganize, remove closed() (thanks Evan Shaw).
+* misc/notepadplus: support for notepad++ (thanks Anthony Starks).
+* net: implement non-blocking connect (thanks Alexey Borzenkov).
+* os: fix MkdirAll("/thisdoesnotexist") (thanks Albert Strasheim),
+	Plan 9 support (thanks Yuval Pavel Zholkover),
+	add a few missing Plan 9 errors (thanks Andrey Mirtchovski),
+	fix FileInfo.Name returned by Stat (thanks David Forsythe).
+* path/filepath.Glob: add an error return,
+	don't drop known matches on error.
+* path/filepath: add support for Plan 9 (thanks Andrey Mirtchovski).
+* scanner: treat line comments like in Go.
+* syscall: Plan 9 support (thanks Yuval Pavel Zholkover),
+	StartProcess Chroot and Credential (thanks Albert Strasheim),
+	add BPF support for freebsd/386, freebsd/amd64 (thanks Mikio Hara),
+	make [Raw]Syscall6 pass 6th arg on linux/386 (thanks Evan Shaw).
+
+ +

2011-03-28

+ +
+This weekly release includes improved support for testing.
+
+Memory and CPU profiling is now available via the gotest tool. Gotest will
+produce memory and CPU profiling data when invoked with the -test.memprofile
+and -test.cpuprofile flags. Run "godoc gotest" for details.
+
+We have also introduced a way for tests to run quickly when an exhaustive test
+is unnecessary. Gotest’s new -test.short flag in combination with the testing
+package’s new Short function allows you to write tests that can be run in
+normal or "short" mode; short mode is now used by all.bash to reduce
+installation time.
+The Makefiles know about the flag - you can just run "make testshort".
+
+Other changes:
+* .hgignore: Ignore all goinstalled packages (thanks Evan Shaw).
+* build: add all-qemu.bash, handful of arm fixes,
+        add support for SWIG, and add two SWIG examples,
+        diagnose Ubuntu's buggy copy of gold,
+        handle broken awk in version.bash (thanks Dave Cheney),
+        reenable clean.bash without gomake (thanks Gustavo Niemeyer).
+* cgo: fix index-out-of-bounds bug.
+* codereview: permit CLs of the form weekly.DATE
+* crypto/ecdsa: truncate hash values.
+* crypto/openpgp: add DSA signature support.
+* dashboard: remove old python/bash builder, update README.
+* doc: explain release and weekly tags in install.html.
+* exec: document dir option for Run (thanks Gustavo Niemeyer).
+* flag: document Nflag function (thanks Fazlul Shahriar).
+* gc: remove interim ... error which rejects valid code.
+* go/ast: implemented NewPackage,
+        merge CaseClause and TypeCaseClause.
+* go/parser: fix memory leak by making a copy of token literals,
+        resolve identifiers properly.
+* go/printer, gofmt: avoid exponential layout algorithm,
+        gofmt: simplify struct formatting and respect line breaks.
+* go/scanner: to interpret line comments with Windows filenames (thanks Alex Brainman).
+* go/token: use array instead of map for token->string table.
+* gob: optimizations to reduce allocations,
+        use pointers in bootstrapType so interfaces behave properly.
+* gobuilder: recognize CLs of the form weekly.DATE.
+* godefs: handle volatile.
+* godoc: add -template flag to specify custom templates,
+        fix path problem for windows (thanks Yasuhiro Matsumoto).
+* gofix: httpserver - rewrite rw.SetHeader to rw.Header.Set.
+* gofmt: add profiling flag.
+* gopprof: fix bug: do not rotate 180 degrees for large scrolls,
+        update list of memory allocation functions.
+* gotest: fix gofmt issue in generated _testmain.go.
+* http: add NewProxyClientConn,
+        avoid crash when asked for multiple file ranges,
+        don't chunk 304 responses,
+        export Transport, add keep-alive support.
+* ld: return > 0 exit code on unsafe import.
+* misc/bbedit: remove closed keyword (thanks Anthony Starks).
+* misc/emacs: gofmt: don't clobber the current buffer on failure.
+* misc/vim: remove 'closed' as a builtin function.
+* net: add FileConn, FilePacketConn, FileListener (thanks Albert Strasheim),
+        don't force epoll/kqueue to wake up in order to add new events,
+        let OS-specific AddFD routine wake up polling thread,
+        use preallocated buffer for epoll and kqueue/kevent.
+* path/filepath: add EvalSymlinks function,
+        fix TestEvalSymlinks when run under symlinked GOROOT.
+* path: work for windows (thanks Yasuhiro Matsumoto).
+* rpc: increase server_test timeout (thanks Gustavo Niemeyer),
+        optimizations to reduce allocations.
+* runtime: fix darwin/amd64 thread VM footprint (thanks Alexey Borzenkov),
+        fix gdb support for goroutines,
+        more stack split fixes,
+        os-specific types and code for setitimer,
+        update defs.h for freebsd-386 (thanks Devon H. O'Dell).
+* strings: Map: avoid allocation when string is unchanged.
+* syscall: GetsockoptInt (thanks Albert Strasheim),
+        StartProcess fixes for windows (thanks Alex Brainman),
+        permit non-blocking syscalls,
+        rename from .sh to .pl, because these files are in Perl.
+* test: enable tests using v, ok := <-ch syntax (thanks Robert Hencke).
+* time: give a helpful message when we can't set the time zone for testing.
+        isolate syscall reference in sys.go.
+
+ +

2011-03-15

+ +
+This week's release introduces a new release tagging scheme. We intend to
+continue with our weekly releases, but have renamed the existing tags from
+"release" to "weekly". The "release" tag will now be applied to one hand-picked
+stable release each month or two.
+
+The revision formerly tagged "release.2011-03-07.1" (now "weekly.2011-03-07.1")
+has been nominated our first stable release, and has been given the tag
+"release.r56". As we tag each stable release we will post an announcement to
+the new golang-announce mailing list:
+  http://groups.google.com/group/golang-announce
+
+You can continue to keep your Go installation updated using "hg update
+release", but now you should only need to update once we tag a new stable
+release, which we will announce here. If you wish to stay at the leading edge,
+you should switch to the weekly tag with "hg update weekly".
+
+
+This weekly release includes significant changes to the language spec and the
+http, os, and syscall packages. Your code may need to be changed. It also
+introduces the new gofix tool.
+
+The closed function has been removed from the language. The syntax for channel
+receives has been changed to return an optional second value, a boolean value
+indicating whether the channel is closed. This code:
+	v := <-ch
+	if closed(ch) {
+		// channel is closed
+	}
+should now be written as:
+	v, ok := <-ch
+	if !ok {
+		// channel is closed
+	}
+
+It is now illegal to declare unused labels, just as it is illegal to declare
+unused local variables.
+
+The new gofix tool finds Go programs that use old APIs and rewrites them to use
+newer ones.  After you update to a new Go release, gofix helps make the
+necessary changes to your programs. Gofix will handle the http, os, and syscall
+package changes described below, and we will update the program to keep up with
+future changes to the libraries.
+
+The Hijack and Flush methods have been removed from the http.ResponseWriter
+interface and are accessible via the new http.Hijacker and http.Flusher
+interfaces. The RemoteAddr and UsingTLS methods have been moved from
+http.ResponseWriter to http.Request.
+
+The http.ResponseWriter interface's SetHeader method has been replaced by a
+Header() method that returns the response's http.Header. Caller code needs to
+change. This code:
+	rw.SetHeader("Content-Type", "text/plain")
+should now be written as:
+	rw.Header().Set("Content-Type", "text/plain")
+The os and syscall packages' StartProcess functions now take their final three
+arguments as an *os.ProcAttr and *syscall.ProcAttr values, respectively. This
+code:
+	os.StartProcess(bin, args, env, dir, fds)
+should now be written as:
+	os.StartProcess(bin, args, &os.ProcAttr{Files: fds, Dir: dir, Env: env})
+
+The gob package will now encode and decode values of types that implement the
+gob.GobEncoder and gob.GobDecoder interfaces. This allows types with unexported
+fields to transmit self-consistent descriptions; one instance is big.Int and
+big.Rat.
+
+Other changes:
+* 5l, 6l, 8l: reduce binary size about 40% by omitting symbols for type, string, go.string.
+* 5l, 8l: output missing section symbols (thanks Anthony Martin).
+* 6l, 8l: fix gdb crash.
+* Make.cmd: also clean _test* (thanks Gustavo Niemeyer).
+* big: implemented custom Gob(En/De)coder for Int type.
+* build: remove duplicate dependency in Make.cmd (thanks Robert Hencke),
+        run gotest in misc/cgo/test.
+* codereview.py: don't suggest change -d if user is not CL author (thanks Robert Hencke).
+* compress/lzw: benchmark a range of input sizes.
+* crypto/ecdsa: add package.
+* crypto/elliptic: add the N value of each curve.
+* crypto/openpgp: bug fixes and fix misnamed function.
+* crypto/tls: fix compile error (thanks Dave Cheney).
+* doc: Effective Go: some small cleanups,
+        update FAQ. hello, world is now 1.1MB, down from 1.8MB,
+        update codelab wiki to fix template.Execute argument order.
+* flag: visit the flags in sorted order, for nicer messages.
+* fmt: do not export EOF = -1.
+* fmt: make ScanState.Token more general (thanks Roger Peppe).
+* gc: diagnose unused labels,
+        fix handling of return values named _,
+        include all dependencies in export metadata,
+        make unsafe.Pointer its own kind of type, instead of an equivalent to *any.
+* go/ast, go/parser: populate identifier scopes at parse time.
+* go/ast: add FileSet parameter to ast.Print and ast.Fprint.
+* go/parser: first constant in a constant declaration must have a value.
+* gob: efficiency and reliability fixes.
+* gofmt: remove -trace and -ast flags.
+* goinstall: handle $(GOOS) and $(GOARCH) in filenames,
+        handle .c files with gc when cgo isn't used, and
+        handle .s files with gc (thanks Gustavo Niemeyer).
+* gopack: omit time stamps, makes output deterministic.
+* gotype: commandline tool to typecheck go programs.
+* govet: handle '*' in print format strings.
+* hash: new FNV-1a implementation (thanks Pascal S. de Kloe).
+* http/cgi: child support (e.g. Go CGI under Apache).
+* http: adapt Cookie code to follow IETF draft (thanks Petar Maymounkov),
+        add test for fixed HTTP/1.0 keep-alive issue,
+        don't hit external network in client_test.go,
+        fix transport crash when request URL is nil,
+        rename interface Transport to RoundTripper,
+        run tests even with DISABLE_NET_TESTS=1.
+* httptest: default the Recorder status code to 200 on a Write.
+* io/ioutil: clean-up of ReadAll and ReadFile.
+* ioutil: add NopCloser.
+* ld: preserve symbol sizes during data layout.
+* lib9, libmach: Change GOOS references to GOHOSTOS (thanks Evan Shaw).
+* libmach: correct string comparison to revive 6cov on darwin (thanks Dave Cheney).
+* misc/vim: Add indent script for Vim (thanks Ross Light).
+* net, os, syslog: fixes for Solaris support.
+* net: don't loop to drain wakeup pipe.
+* nm: document -S flag.
+* openpgp: add PublicKey KeyId string accessors.
+* rpc: optimizations, add benchmarks and memory profiling,
+        use httptest.Server for tests (thanks Robert Hencke).
+* runtime: reduce lock contention via wakeup on scheduler unlock,
+        scheduler, cgo reorganization,
+        split non-debugging malloc interface out of debug.go into mem.go.
+* spec: clarify return statement rules.
+* strings: add IndexRune tests, ASCII fast path,
+        better benchmark names; add BenchmarkIndex.
+* syscall: implement Mount and Unmount for linux,
+        implement Reboot for linux.
+* time: fix Time.ZoneOffset documentation (thanks Peter Mundy).
+* tls: move PeerCertificates to ConnectionState.
+
+ +

2011-03-07 (r56)

 This release includes changes to the reflect and path packages.
@@ -135,7 +496,7 @@ Other changes:
 * netchan: allow use of arbitrary connections (thanks Roger Peppe).
 * os: add ENODATA and ENOTCONN (thanks Albert Strasheim).
 * reflect: add a couple of sentences explaining how Methods operate,
-	add a secret method to ArrayOrSliceType to ensure it’s only implemented by arrays and slices,
+	add a secret method to ArrayOrSliceType to ensure it's only implemented by arrays and slices,
 	add pointer word to CommonType (placeholder for future work).
 * runtime-gdb.py: gdb pretty printer for go strings properly handles length.
 * runtime: various bug fixes, more complete stack traces,
@@ -186,7 +547,7 @@ Other changes:
        fix spaces in GOROOT (thanks Christopher Nielsen).
 * bytes: fix bug in buffer.ReadBytes (thanks Evan Shaw).
 * 5g: better int64 code,
-       don’t use MVN instruction.
+       don't use MVN instruction.
 * cgo: don't run cgo when not compiling (thanks Gustavo Niemeyer),
        fix _cgo_run timestamp file order (thanks Gustavo Niemeyer),
        fix handling of signed enumerations (thanks Gustavo Niemeyer),
diff --git a/doc/devel/roadmap.html b/doc/devel/roadmap.html
index 97d8a08b8..a73ec6353 100644
--- a/doc/devel/roadmap.html
+++ b/doc/devel/roadmap.html
@@ -30,6 +30,8 @@ Generics.  An active topic of discussion.
 
  • Methods for operators, to allow a type to use arithmetic notation for expressions. +
  • +Possibly allow top-level packages to be given names other than main.

    @@ -37,8 +39,7 @@ Implementation roadmap

    • -Improved garbage collector, most likely a reference counting collector -with a cycle detector running in a separate core. +Improved garbage collector.
    • Debugger.
    • @@ -48,7 +49,7 @@ Improved CGO including some mechanism for calling back from C to Go.
    • Improved implementation documentation.
    • -Comprehensive support for internationalization. +Faster, allocation-light reflection.

    @@ -77,6 +78,31 @@ Separate gcc interface from frontend proper. Use escape analysis to keep more data on stack. +

    +Tools roadmap

    + +
      +
    • +Strengthen goinstall until it can displace make for most builds. +
    + +

    +Packages roadmap

    + +
      +
    • +Faster, allocation-light reflection. +
    • +Faster, RE2-like regular expressions. +
    • +Comprehensive support for international text. +
    • +Support for international dates, times, etc. +
    • +Support for multilingual messages. +
    + +

    Done

      diff --git a/doc/effective_go.html b/doc/effective_go.html index a32179298..27bfd1bf5 100644 --- a/doc/effective_go.html +++ b/doc/effective_go.html @@ -59,13 +59,14 @@ prescriptive style guide. With Go we take an unusual approach and let the machine take care of most formatting issues. -A program, gofmt, reads a Go program +The gofmt tool reads a Go program and emits the source in a standard style of indentation and vertical alignment, retaining and if necessary reformatting comments. If you want to know how to handle some new layout situation, run gofmt; if the answer doesn't -seem right, fix the program (or file a bug), don't work around it. +seem right, rearrange your program (or file a bug about gofmt), +don't work around it.

      @@ -94,7 +95,7 @@ type T struct {

  • -All code in the libraries has been formatted with gofmt. +All Go code in the standard packages has been formatted with gofmt.

    @@ -304,7 +305,8 @@ not container_vector and not containerVector.

    The importer of a package will use the name to refer to its contents (the import . notation is intended mostly for tests and other -unusual situations), so exported names in the package can use that fact +unusual situations and should be avoided unless necessary), +so exported names in the package can use that fact to avoid stutter. For instance, the buffered reader type in the bufio package is called Reader, not BufReader, because users see it as bufio.Reader, @@ -316,8 +318,8 @@ Similarly, the function to make new instances of ring.Ring—wh is the definition of a constructor in Go—would normally be called NewRing, but since Ring is the only type exported by the package, and since the -package is called ring, it's called just New. -Clients of the package see that as ring.New. +package is called ring, it's called just New, +which clients of the package see as ring.New. Use the package structure to help you choose good names.

    @@ -331,6 +333,27 @@ to write a helpful doc comment than to attempt to put all the information into the name.

    +

    Getters

    + +

    +Go doesn't provide automatic support for getters and setters. +There's nothing wrong with providing getters and setters yourself, +and it's often appropriate to do so, but it's neither idiomatic nor necessary +to put Get into the getter's name. If you have a field called +owner (lower case, unexported), the getter method should be +called Owner (upper case, exported), not GetOwner. +The use of upper-case names for export provides the hook to discriminate +the field from the method. +A setter function, if needed, will likely be called SetOwner. +Both names read well in practice: +

    +
    +owner := obj.Owner()
    +if owner != user {
    +	obj.SetOwner(user)
    +}
    +
    +

    Interface names

    @@ -489,8 +512,8 @@ codeUsing(f)

    -This is a example of a common situation where code must analyze a -sequence of error possibilities. The code reads well if the +This is an example of a common situation where code must guard against a +sequence of error conditions. The code reads well if the successful flow of control runs down the page, eliminating error cases as they arise. Since error cases tend to end in return statements, the resulting code needs no else statements. @@ -553,8 +576,9 @@ for _, value := range m { // key is unused

    For strings, the range does more work for you, breaking out individual -Unicode characters by parsing the UTF-8 (erroneous encodings consume one byte and produce the -replacement rune U+FFFD). The loop +Unicode characters by parsing the UTF-8. +Erroneous encodings consume one byte and produce the +replacement rune U+FFFD. The loop

     for pos, char := range "日本語" {
    @@ -571,8 +595,9 @@ character 語 starts at byte position 6
     

    -Finally, since Go has no comma operator and ++ and -- -are statements not expressions, if you want to run multiple variables in a for +Finally, Go has no comma operator and ++ and -- +are statements not expressions. +Thus if you want to run multiple variables in a for you should use parallel assignment.

    @@ -676,7 +701,7 @@ case *int:
     
     

    One of Go's unusual features is that functions and methods -can return multiple values. This can be used to +can return multiple values. This form can be used to improve on a couple of clumsy idioms in C programs: in-band error returns (such as -1 for EOF) and modifying an argument. @@ -811,7 +836,7 @@ func Contents(filename string) (string, os.Error) {

    -Deferring a function like this has two advantages. First, it +Deferring a call to a function such as Close has two advantages. First, it guarantees that you will never forget to close the file, a mistake that's easy to make if you later edit the function to add a new return path. Second, it means that the close sits near the open, @@ -903,8 +928,9 @@ leaving: b For programmers accustomed to block-level resource management from other languages, defer may seem peculiar, but its most interesting and powerful applications come precisely from the fact -that it's not block-based but function based. In the section on -panic and recover we'll see an example. +that it's not block-based but function-based. In the section on +panic and recover we'll see another +example of its possibilities.

    Data

    @@ -949,7 +975,7 @@ type SyncedBuffer struct {

    Values of type SyncedBuffer are also ready to use immediately upon allocation -or just declaration. In this snippet, both p and v will work +or just declaration. In the next snippet, both p and v will work correctly without further arrangement.

    @@ -987,7 +1013,6 @@ an expression that creates a new instance each time it is evaluated.

    -
     func NewFile(fd int, name string) *File {
         if fd < 0 {
    @@ -999,7 +1024,7 @@ func NewFile(fd int, name string) *File {
     

    -Note that it's perfectly OK to return the address of a local variable; +Note that, unlike in C, it's perfectly OK to return the address of a local variable; the storage associated with the variable survives after the function returns. In fact, taking the address of a composite literal @@ -1053,7 +1078,7 @@ is that these three types are, under the covers, references to data structures t must be initialized before use. A slice, for example, is a three-item descriptor containing a pointer to the data (inside an array), the length, and the -capacity; until those items are initialized, the slice is nil. +capacity, and until those items are initialized, the slice is nil. For slices, maps, and channels, make initializes the internal data structure and prepares the value for use. @@ -1273,7 +1298,21 @@ is not present in the map will return the zero value for the type of the entries in the map. For instance, if the map contains integers, looking up a non-existent key will return 0. +A set can be implemented as a map with value type bool. +Set the map entry to true to put the value in the set, and then +test it by simple indexing.

    +
    +attended := map[string] bool {
    +    "Ann": true,
    +    "Joe": true,
    +    ...
    +}
    +
    +if attended[person] { // will be false if person is not in the map
    +    fmt.Println(person, "was at the meeting")
    +}
    +

    Sometimes you need to distinguish a missing entry from a zero value. Is there an entry for "UTC" @@ -1298,7 +1337,7 @@ func offset(tz string) int { if seconds, ok := timeZone[tz]; ok { return seconds } - log.Println("unknown time zone", tz) + log.Println("unknown time zone:", tz) return 0 } diff --git a/doc/go_faq.html b/doc/go_faq.html index 4be811068..5f92b0528 100644 --- a/doc/go_faq.html +++ b/doc/go_faq.html @@ -558,7 +558,8 @@ type Fooer interface {

    A type must then implement the ImplementsFooer method to be a -Fooer, clearly documenting the fact. +Fooer, clearly documenting the fact and announcing it in +godoc's output.

    @@ -1032,7 +1033,7 @@ type checks, reflection, and even panic-time stack traces.
     
     

    A trivial C "hello, world" program compiled and linked statically using gcc -on Linux is around 750 kB. An equivalent Go program is around 1.8 MB, but +on Linux is around 750 kB. An equivalent Go program is around 1.1 MB, but that includes more powerful run-time support. We believe that with some effort the size of Go binaries can be reduced. diff --git a/doc/go_spec.html b/doc/go_spec.html index 85dfc44bd..f8fe5974a 100644 --- a/doc/go_spec.html +++ b/doc/go_spec.html @@ -1,5 +1,5 @@ - +

    Receiving from a nil channel causes a @@ -4009,9 +4009,8 @@ iteration values for each entry will be produced at most once.

  • For channels, the iteration values produced are the successive values sent on -the channel until the channel is closed; it does not produce the zero value sent -before the channel is closed -(§close and closed). +the channel until the channel is closed +(§close).
  • @@ -4086,12 +4085,9 @@ cases all referring to communication operations. SelectStmt = "select" "{" { CommClause } "}" . CommClause = CommCase ":" { Statement ";" } . CommCase = "case" ( SendStmt | RecvStmt ) | "default" . -RecvStmt = [ Expression ( "=" | ":=" ) ] RecvExpr . +RecvStmt = [ Expression [ "," Expression ] ( "=" | ":=" ) ] RecvExpr . RecvExpr = Expression .
    -

    RecvExpr must be a receive operation. @@ -4122,27 +4118,24 @@ in the "select" statement. If multiple cases can proceed, a pseudo-random fair choice is made to decide which single communication will execute.

    - -The receive case may declare a new variable using a +The receive case may declare one or two new variables using a short variable declaration.

    -var c, c1, c2 chan int
    +var c, c1, c2, c3 chan int
     var i1, i2 int
     select {
     case i1 = <-c1:
     	print("received ", i1, " from c1\n")
     case c2 <- i2:
     	print("sent ", i2, " to c2\n")
    -
     default:
     	print("no communication\n")
     }
    @@ -4212,7 +4205,7 @@ func complex_f2() (re float64, im float64) {
     
  • The expression list may be empty if the function's result - type specifies names for its result parameters (§Function Types). + type specifies names for its result parameters (§Function Types). The result parameters act as ordinary local variables and the function may assign values to them as necessary. The "return" statement returns the values of these variables. @@ -4222,6 +4215,11 @@ func complex_f3() (re float64, im float64) { im = 4.0 return } + +func (devnull) Write(p []byte) (n int, _ os.Error) { + n = len(p) + return +}
  • @@ -4259,11 +4257,13 @@ terminates

    -L: for i < n {
    -	switch i {
    -		case 5: break L
    +L:
    +	for i < n {
    +		switch i {
    +		case 5:
    +			break L
    +		}
     	}
    -}
     

    Continue statements

    @@ -4305,8 +4305,8 @@ instance, this example:

    -goto L  // BAD
    -v := 3
    +	goto L  // BAD
    +	v := 3
     L:
     
    @@ -4396,8 +4396,7 @@ BuiltinCall = identifier "(" [ BuiltinArgs [ "," ] ] ")" . BuiltinArgs = Type [ "," ExpressionList ] | ExpressionList . - -

    Close and closed

    +

    Close

    For a channel c, the built-in function close(c) @@ -4407,12 +4406,8 @@ After calling close, and after any previously sent values have been received, receive operations will return the zero value for the channel's type without blocking. - -After at least one such zero value has been -received, closed(c) returns true. +The multi-valued receive operation +returns a received value along with an indication of whether the channel is closed.

    @@ -4700,7 +4695,7 @@ func protect(g func()) { if x := recover(); x != nil { log.Printf("run time panic: %v", x) } - } + }() log.Println("start") g() } @@ -5152,6 +5147,4 @@ The following minimal alignment properties are guaranteed:

    Implementation differences - TODO

    • Implementation does not honor the restriction on goto statements and targets (no intervening declarations).
    • -
    • Gccgo: Method expressions are partially implemented.
    • -
    • Gccgo: allows only one init() function per source file.
    diff --git a/doc/install.html b/doc/install.html index 816e6e654..843e0645f 100644 --- a/doc/install.html +++ b/doc/install.html @@ -91,7 +91,9 @@ installed as part of Xcode.

    -

    On Ubuntu/Debian, use sudo apt-get install bison ed gawk gcc libc6-dev make. +

    On Ubuntu/Debian, use sudo apt-get install bison ed gawk gcc libc6-dev +make. If you want to build 32-bit binaries on a 64-bit system you'll +also need the libc6-dev-i386 package.

    Install Mercurial, if needed

    @@ -275,7 +277,24 @@ For the full story, consult Go's extensive

    Keeping up with releases

    -

    New releases are announced on the Go Nuts mailing list. +

    +The Go project maintains two stable tags in its Mercurial repository: +release and weekly. +The weekly tag is updated about once a week, and should be used by +those who want to track the project's development. +The release tag is given, less often, to those weekly releases +that have proven themselves to be robust. +

    + +

    +Most Go users will want to keep their Go installation at the latest +release tag. +New releases are announced on the +golang-announce +mailing list. +

    + +

    To update an existing tree to the latest release, you can run:

    @@ -286,6 +305,10 @@ $ hg update release $ ./all.bash +

    +To use the weekly tag run hg update weekly instead. +

    +

    Community resources

    diff --git a/lib/codereview/codereview.py b/lib/codereview/codereview.py index fa703c711..766e827fc 100644 --- a/lib/codereview/codereview.py +++ b/lib/codereview/codereview.py @@ -779,7 +779,7 @@ def Incoming(ui, repo, opts): _, incoming, _ = findcommonincoming(repo, getremote(ui, repo, opts)) return incoming -desc_re = '^(.+: |tag release\.|release\.|fix build)' +desc_re = '^(.+: |(tag )?(release|weekly)\.|fix build)' desc_msg = '''Your CL description appears not to use the standard form. @@ -1135,12 +1135,27 @@ def clpatch(ui, repo, clname, **opts): if missing_codereview: return missing_codereview - cl, patch, err = DownloadCL(ui, repo, clname) + cl, vers, patch, err = DownloadCL(ui, repo, clname) if err != "": return err if patch == emptydiff: return "codereview issue %s has no diff" % clname + if not repo[vers]: + return "codereview issue %s is newer than the current repository; hg sync" % clname + + # find current hg version (hg identify) + ctx = repo[None] + parents = ctx.parents() + id = '+'.join([short(p.node()) for p in parents]) + + # if version does not match the patch version, + # try to update the patch line numbers. + if id != vers: + patch, err = portPatch(repo, patch, vers, id) + if err != "": + return "codereview issue %s is out of date: %s" % (clname, err) + argv = ["hgpatch"] if opts["no_incoming"]: argv += ["--checksync=false"] @@ -1163,6 +1178,67 @@ def clpatch(ui, repo, clname, **opts): cl.Flush(ui, repo) ui.write(cl.PendingText() + "\n") +# portPatch rewrites patch from being a patch against +# oldver to being a patch against newver. +def portPatch(repo, patch, oldver, newver): + lines = patch.splitlines(True) # True = keep \n + delta = None + for i in range(len(lines)): + line = lines[i] + if line.startswith('--- a/'): + file = line[6:-1] + delta = fileDeltas(repo, file, oldver, newver) + if not delta or not line.startswith('@@ '): + continue + # @@ -x,y +z,w @@ means the patch chunk replaces + # the original file's line numbers x up to x+y with the + # line numbers z up to z+w in the new file. + # Find the delta from x in the original to the same + # line in the current version and add that delta to both + # x and z. + m = re.match('@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@', line) + if not m: + return None, "error parsing patch line numbers" + n1, len1, n2, len2 = int(m.group(1)), int(m.group(2)), int(m.group(3)), int(m.group(4)) + d, err = lineDelta(delta, n1, len1) + if err != "": + return "", err + n1 += d + n2 += d + lines[i] = "@@ -%d,%d +%d,%d @@\n" % (n1, len1, n2, len2) + + newpatch = ''.join(lines) + return newpatch, "" + +# fileDelta returns the line number deltas for the given file's +# changes from oldver to newver. +# The deltas are a list of (n, len, newdelta) triples that say +# lines [n, n+len) were modified, and after that range the +# line numbers are +newdelta from what they were before. +def fileDeltas(repo, file, oldver, newver): + cmd = ["hg", "diff", "--git", "-r", oldver + ":" + newver, "path:" + file] + data = RunShell(cmd, silent_ok=True) + deltas = [] + for line in data.splitlines(): + m = re.match('@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@', line) + if not m: + continue + n1, len1, n2, len2 = int(m.group(1)), int(m.group(2)), int(m.group(3)), int(m.group(4)) + deltas.append((n1, len1, n2+len2-(n1+len1))) + return deltas + +# lineDelta finds the appropriate line number delta to apply to the lines [n, n+len). +# It returns an error if those lines were rewritten by the patch. +def lineDelta(deltas, n, len): + d = 0 + for (old, oldlen, newdelta) in deltas: + if old >= n+len: + break + if old+len > n: + return 0, "patch and recent changes conflict" + d = newdelta + return d, "" + def download(ui, repo, clname, **opts): """download a change from the code review server @@ -1172,7 +1248,7 @@ def download(ui, repo, clname, **opts): if missing_codereview: return missing_codereview - cl, patch, err = DownloadCL(ui, repo, clname) + cl, vers, patch, err = DownloadCL(ui, repo, clname) if err != "": return err ui.write(cl.EditorText() + "\n") @@ -1333,16 +1409,16 @@ def reposetup(ui, repo): def CheckContributor(ui, repo, user=None): set_status("checking CONTRIBUTORS file") - if not user: - user = ui.config("ui", "username") - if not user: - raise util.Abort("[ui] username is not configured in .hgrc") _, userline = FindContributor(ui, repo, user, warn=False) if not userline: raise util.Abort("cannot find %s in CONTRIBUTORS" % (user,)) return userline -def FindContributor(ui, repo, user, warn=True): +def FindContributor(ui, repo, user=None, warn=True): + if not user: + user = ui.config("ui", "username") + if not user: + raise util.Abort("[ui] username is not configured in .hgrc") user = user.lower() m = re.match(r".*<(.*)>", user) if m: @@ -1463,7 +1539,7 @@ def submit(ui, repo, *pats, **opts): # we're committed. upload final patch, close review, add commit message changeURL = short(node) url = other.url() - m = re.match("^https?://([^@/]+@)?([^.]+)\.googlecode\.com/hg/", url) + m = re.match("^https?://([^@/]+@)?([^.]+)\.googlecode\.com/hg/?", url) if m: changeURL = "http://code.google.com/p/%s/source/detail?r=%s" % (m.group(2), changeURL) else: @@ -1558,7 +1634,10 @@ def sync_changes(ui, repo): cl.files = Sub(cl.files, extra) cl.Flush(ui, repo) if not cl.files: - ui.warn("CL %s has no files; suggest hg change -d %s\n" % (cl.name, cl.name)) + if not cl.copied_from: + ui.warn("CL %s has no files; delete with hg change -d %s\n" % (cl.name, cl.name)) + else: + ui.warn("CL %s has no files; delete locally with hg change -D %s\n" % (cl.name, cl.name)) return def upload(ui, repo, name, **opts): @@ -1738,25 +1817,35 @@ def DownloadCL(ui, repo, clname): set_status("downloading CL " + clname) cl, err = LoadCL(ui, repo, clname) if err != "": - return None, None, "error loading CL %s: %s" % (clname, ExceptionDetail()) + return None, None, None, "error loading CL %s: %s" % (clname, err) # Grab RSS feed to learn about CL feed = XMLGet(ui, "/rss/issue/" + clname) if feed is None: - return None, None, "cannot download CL" + return None, None, None, "cannot download CL" # Find most recent diff diff = None prefix = 'http://' + server + '/' - for link in feed.findall("{http://www.w3.org/2005/Atom}entry/{http://www.w3.org/2005/Atom}link"): - if link.get('rel') != 'alternate': - continue - text = link.get('href') - if not text.startswith(prefix) or not text.endswith('.diff'): + vers = "" + for entry in feed.findall("{http://www.w3.org/2005/Atom}entry"): + thisVers = "" + for title in entry.findall("{http://www.w3.org/2005/Atom}title"): + m = re.search('diff -r ([0-9a-f]+) ', title.text) + if m: + thisVers = m.group(1) + if thisVers == "": continue - diff = text[len(prefix)-1:] + for link in entry.findall("{http://www.w3.org/2005/Atom}link"): + if link.get('rel') != 'alternate': + continue + text = link.get('href') + if not text.startswith(prefix) or not text.endswith('.diff'): + continue + diff = text[len(prefix)-1:] + vers = thisVers if diff is None: - return None, None, "CL has no diff" + return None, None, None, "CL has no diff" diffdata = MySend(diff, force_auth=False) # Find author - first entry will be author who created CL. @@ -1765,7 +1854,7 @@ def DownloadCL(ui, repo, clname): nick = author.text.strip() break if not nick: - return None, None, "CL has no author" + return None, None, None, "CL has no author" # The author is just a nickname: get the real email address. try: @@ -1775,7 +1864,7 @@ def DownloadCL(ui, repo, clname): except: ui.warn("error looking up %s: %s\n" % (nick, ExceptionDetail())) cl.copied_from = nick+"@needtofix" - return cl, diffdata, "" + return cl, vers, diffdata, "" match = re.match(r"(.*) \((.*)\)", data) if not match: return None, None, "error looking up %s: cannot parse result %s" % (nick, repr(data)) @@ -1784,10 +1873,12 @@ def DownloadCL(ui, repo, clname): email = match.group(1) # Print warning if email is not in CONTRIBUTORS file. - FindContributor(ui, repo, email) - cl.copied_from = email + him = FindContributor(ui, repo, email) + me = FindContributor(ui, repo, None) + if him != me: + cl.copied_from = email - return cl, diffdata, "" + return cl, vers, diffdata, "" def MySend(request_path, payload=None, content_type="application/octet-stream", @@ -1797,7 +1888,7 @@ def MySend(request_path, payload=None, try: return MySend1(request_path, payload, content_type, timeout, force_auth, **kwargs) except Exception, e: - if type(e) == urllib2.HTTPError and e.code == 403: # forbidden, it happens + if type(e) != urllib2.HTTPError or e.code != 500: # only retry on HTTP 500 error raise print >>sys.stderr, "Loading "+request_path+": "+ExceptionDetail()+"; trying again in 2 seconds." time.sleep(2) diff --git a/lib/godoc/search.txt b/lib/godoc/search.txt index eff4d36fc..967c1ac01 100644 --- a/lib/godoc/search.txt +++ b/lib/godoc/search.txt @@ -46,9 +46,9 @@ package {Pak.Name} {.end} {.section Textual} {.section Complete} -{Found} TEXTUAL OCCURENCES +{Found} TEXTUAL OCCURRENCES {.or} -MORE THAN {Found} TEXTUAL OCCURENCES +MORE THAN {Found} TEXTUAL OCCURRENCES {.end} {.repeated section @} diff --git a/misc/bbedit/Go.plist b/misc/bbedit/Go.plist index 9dc3bf6f1..45535350a 100755 --- a/misc/bbedit/Go.plist +++ b/misc/bbedit/Go.plist @@ -14,7 +14,6 @@ case, chan, close, - closed, complex, complex128, complex64, @@ -95,7 +94,7 @@ "Open Strings 1" = "`"; "Open Strings 2" = "\""; "Prefix for Functions" = "func"; - "Prefix for Procedures" = func; + "Prefix for Procedures" = "func"; "Terminator for Prototypes 1" = ";"; "Terminator for Prototypes 2" = ""; }; diff --git a/misc/cgo/stdio/Makefile b/misc/cgo/stdio/Makefile index fc925e607..3f7a4c01c 100644 --- a/misc/cgo/stdio/Makefile +++ b/misc/cgo/stdio/Makefile @@ -6,10 +6,7 @@ include ../../../src/Make.inc TARG=stdio CGOFILES=\ - align.go\ file.go\ - test.go\ - test1.go\ CLEANFILES+=hello fib chain run.out diff --git a/misc/cgo/stdio/align.go b/misc/cgo/stdio/align.go deleted file mode 100644 index 6cdfd902f..000000000 --- a/misc/cgo/stdio/align.go +++ /dev/null @@ -1,78 +0,0 @@ -package stdio - -/* -#include - -typedef unsigned char Uint8; -typedef unsigned short Uint16; - -typedef enum { - MOD1 = 0x0000, - MODX = 0x8000 -} SDLMod; - -typedef enum { - A = 1, - B = 322, - SDLK_LAST -} SDLKey; - -typedef struct SDL_keysym { - Uint8 scancode; - SDLKey sym; - SDLMod mod; - Uint16 unicode; -} SDL_keysym; - -typedef struct SDL_KeyboardEvent { - Uint8 typ; - Uint8 which; - Uint8 state; - SDL_keysym keysym; -} SDL_KeyboardEvent; - -void makeEvent(SDL_KeyboardEvent *event) { - unsigned char *p; - int i; - - p = (unsigned char*)event; - for (i=0; ityp == typ && e->which == which && e->state == state && e->keysym.scancode == scan && e->keysym.sym == sym && e->keysym.mod == mod && e->keysym.unicode == uni; -} - -void cTest(SDL_KeyboardEvent *event) { - printf("C: %#x %#x %#x %#x %#x %#x %#x\n", event->typ, event->which, event->state, - event->keysym.scancode, event->keysym.sym, event->keysym.mod, event->keysym.unicode); - fflush(stdout); -} - -*/ -import "C" - -import ( - "fmt" - "syscall" -) - -func TestAlign() { - if syscall.ARCH == "amd64" { - // alignment is known to be broken on amd64. - // http://code.google.com/p/go/issues/detail?id=609 - return - } - var evt C.SDL_KeyboardEvent - C.makeEvent(&evt) - if C.same(&evt, evt.typ, evt.which, evt.state, evt.keysym.scancode, evt.keysym.sym, evt.keysym.mod, evt.keysym.unicode) == 0 { - fmt.Println("*** bad alignment") - C.cTest(&evt) - fmt.Printf("Go: %#x %#x %#x %#x %#x %#x %#x\n", - evt.typ, evt.which, evt.state, evt.keysym.scancode, - evt.keysym.sym, evt.keysym.mod, evt.keysym.unicode) - fmt.Println(evt) - } -} diff --git a/misc/cgo/stdio/hello.go b/misc/cgo/stdio/hello.go index 9cb6e6884..58fc6d574 100644 --- a/misc/cgo/stdio/hello.go +++ b/misc/cgo/stdio/hello.go @@ -4,26 +4,8 @@ package main -import ( - "os" - "stdio" -) +import "stdio" func main() { stdio.Stdout.WriteString(stdio.Greeting + "\n") - - l := stdio.Atol("123") - if l != 123 { - println("Atol 123: ", l) - panic("bad atol") - } - - n, err := stdio.Strtol("asdf", 123) - if n != 0 || err != os.EINVAL { - println("Strtol: ", n, err) - panic("bad atoi2") - } - - stdio.TestAlign() - stdio.TestEnum() } diff --git a/misc/cgo/stdio/test.go b/misc/cgo/stdio/test.go deleted file mode 100644 index 8f21603ca..000000000 --- a/misc/cgo/stdio/test.go +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This file contains test cases for cgo. - -package stdio - -/* -#include -#include -#include -#include - -#define SHIFT(x, y) ((x)<<(y)) -#define KILO SHIFT(1, 10) - -enum E { - Enum1 = 1, - Enum2 = 2, -}; - -typedef unsigned char uuid_t[20]; - -void uuid_generate(uuid_t x) { - x[0] = 0; -} - -struct S { - int x; -}; - -extern enum E myConstFunc(struct S* const ctx, int const id, struct S **const filter); - -enum E myConstFunc(struct S *const ctx, int const id, struct S **const filter) { return 0; } - -// issue 1222 -typedef union { - long align; -} xxpthread_mutex_t; - -struct ibv_async_event { - union { - int x; - } element; -}; - -struct ibv_context { - xxpthread_mutex_t mutex; -}; -*/ -import "C" -import ( - "os" - "unsafe" -) - -const EINVAL = C.EINVAL /* test #define */ - -var KILO = C.KILO - -func uuidgen() { - var uuid C.uuid_t - C.uuid_generate(&uuid[0]) -} - -func Size(name string) (int64, os.Error) { - var st C.struct_stat - p := C.CString(name) - _, err := C.stat(p, &st) - C.free(unsafe.Pointer(p)) - if err != nil { - return 0, err - } - return int64(C.ulong(st.st_size)), nil -} - -func Strtol(s string, base int) (int, os.Error) { - p := C.CString(s) - n, err := C.strtol(p, nil, C.int(base)) - C.free(unsafe.Pointer(p)) - return int(n), err -} - -func Atol(s string) int { - p := C.CString(s) - n := C.atol(p) - C.free(unsafe.Pointer(p)) - return int(n) -} - -func TestConst() { - C.myConstFunc(nil, 0, nil) -} - -func TestEnum() { - if C.Enum1 != 1 || C.Enum2 != 2 { - println("bad enum", C.Enum1, C.Enum2) - } -} - -func TestAtol() { - l := Atol("123") - if l != 123 { - println("Atol 123: ", l) - panic("bad atol") - } -} - -func TestErrno() { - n, err := Strtol("asdf", 123) - if n != 0 || err != os.EINVAL { - println("Strtol: ", n, err) - panic("bad strtol") - } -} - -func TestMultipleAssign() { - p := C.CString("123") - n, m := C.strtol(p, nil, 345), C.strtol(p, nil, 10) - if n != 0 || m != 234 { - println("Strtol x2: ", n, m) - panic("bad strtol x2") - } - C.free(unsafe.Pointer(p)) -} - -var ( - uint = (C.uint)(0) - ulong C.ulong - char C.char -) - -type Context struct { - ctx *C.struct_ibv_context -} - -func Test() { - TestAlign() - TestAtol() - TestEnum() - TestErrno() - TestConst() -} diff --git a/misc/cgo/stdio/test1.go b/misc/cgo/stdio/test1.go deleted file mode 100644 index dce2ef83c..000000000 --- a/misc/cgo/stdio/test1.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This file contains test cases for cgo. - -package stdio - -/* -// issue 1222 -typedef union { - long align; -} xxpthread_mutex_t; - -struct ibv_async_event { - union { - int x; - } element; -}; - -struct ibv_context { - xxpthread_mutex_t mutex; -}; -*/ -import "C" - -type AsyncEvent struct { - event C.struct_ibv_async_event -} diff --git a/misc/cgo/test/Makefile b/misc/cgo/test/Makefile new file mode 100644 index 000000000..893540d97 --- /dev/null +++ b/misc/cgo/test/Makefile @@ -0,0 +1,23 @@ +# Copyright 2011 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +include ../../../src/Make.inc + +TARG=runtime/cgotest + +CGOFILES=\ + align.go\ + basic.go\ + callback.go\ + issue1222.go\ + issue1328.go\ + issue1560.go\ + +CGO_OFILES=\ + callback_c.o\ + +OFILES=\ + runtime.$O\ + +include ../../../src/Make.pkg diff --git a/misc/cgo/test/align.go b/misc/cgo/test/align.go new file mode 100644 index 000000000..2d2979595 --- /dev/null +++ b/misc/cgo/test/align.go @@ -0,0 +1,72 @@ +package cgotest + +/* +#include + +typedef unsigned char Uint8; +typedef unsigned short Uint16; + +typedef enum { + MOD1 = 0x0000, + MODX = 0x8000 +} SDLMod; + +typedef enum { + A = 1, + B = 322, + SDLK_LAST +} SDLKey; + +typedef struct SDL_keysym { + Uint8 scancode; + SDLKey sym; + SDLMod mod; + Uint16 unicode; +} SDL_keysym; + +typedef struct SDL_KeyboardEvent { + Uint8 typ; + Uint8 which; + Uint8 state; + SDL_keysym keysym; +} SDL_KeyboardEvent; + +void makeEvent(SDL_KeyboardEvent *event) { + unsigned char *p; + int i; + + p = (unsigned char*)event; + for (i=0; ityp == typ && e->which == which && e->state == state && e->keysym.scancode == scan && e->keysym.sym == sym && e->keysym.mod == mod && e->keysym.unicode == uni; +} + +void cTest(SDL_KeyboardEvent *event) { + printf("C: %#x %#x %#x %#x %#x %#x %#x\n", event->typ, event->which, event->state, + event->keysym.scancode, event->keysym.sym, event->keysym.mod, event->keysym.unicode); + fflush(stdout); +} + +*/ +import "C" + +import ( + "testing" +) + +func TestAlign(t *testing.T) { + var evt C.SDL_KeyboardEvent + C.makeEvent(&evt) + if C.same(&evt, evt.typ, evt.which, evt.state, evt.keysym.scancode, evt.keysym.sym, evt.keysym.mod, evt.keysym.unicode) == 0 { + t.Error("*** bad alignment") + C.cTest(&evt) + t.Errorf("Go: %#x %#x %#x %#x %#x %#x %#x\n", + evt.typ, evt.which, evt.state, evt.keysym.scancode, + evt.keysym.sym, evt.keysym.mod, evt.keysym.unicode) + t.Error(evt) + } +} diff --git a/misc/cgo/test/basic.go b/misc/cgo/test/basic.go new file mode 100644 index 000000000..a94074c52 --- /dev/null +++ b/misc/cgo/test/basic.go @@ -0,0 +1,134 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Basic test cases for cgo. + +package cgotest + +/* +#include +#include +#include +#include + +#define SHIFT(x, y) ((x)<<(y)) +#define KILO SHIFT(1, 10) + +enum E { + Enum1 = 1, + Enum2 = 2, +}; + +typedef unsigned char uuid_t[20]; + +void uuid_generate(uuid_t x) { + x[0] = 0; +} + +struct S { + int x; +}; + +extern enum E myConstFunc(struct S* const ctx, int const id, struct S **const filter); + +enum E myConstFunc(struct S *const ctx, int const id, struct S **const filter) { return 0; } + +// issue 1222 +typedef union { + long align; +} xxpthread_mutex_t; + +struct ibv_async_event { + union { + int x; + } element; +}; + +struct ibv_context { + xxpthread_mutex_t mutex; +}; +*/ +import "C" +import ( + "os" + "testing" + "unsafe" +) + +const EINVAL = C.EINVAL /* test #define */ + +var KILO = C.KILO + +func uuidgen() { + var uuid C.uuid_t + C.uuid_generate(&uuid[0]) +} + +func Size(name string) (int64, os.Error) { + var st C.struct_stat + p := C.CString(name) + _, err := C.stat(p, &st) + C.free(unsafe.Pointer(p)) + if err != nil { + return 0, err + } + return int64(C.ulong(st.st_size)), nil +} + +func Strtol(s string, base int) (int, os.Error) { + p := C.CString(s) + n, err := C.strtol(p, nil, C.int(base)) + C.free(unsafe.Pointer(p)) + return int(n), err +} + +func Atol(s string) int { + p := C.CString(s) + n := C.atol(p) + C.free(unsafe.Pointer(p)) + return int(n) +} + +func TestConst(t *testing.T) { + C.myConstFunc(nil, 0, nil) +} + +func TestEnum(t *testing.T) { + if C.Enum1 != 1 || C.Enum2 != 2 { + t.Error("bad enum", C.Enum1, C.Enum2) + } +} + +func TestAtol(t *testing.T) { + l := Atol("123") + if l != 123 { + t.Error("Atol 123: ", l) + } +} + +func TestErrno(t *testing.T) { + n, err := Strtol("asdf", 123) + if n != 0 || err != os.EINVAL { + t.Error("Strtol: ", n, err) + } +} + +func TestMultipleAssign(t *testing.T) { + p := C.CString("234") + n, m := C.strtol(p, nil, 345), C.strtol(p, nil, 10) + if n != 0 || m != 234 { + t.Fatal("Strtol x2: ", n, m) + } + C.free(unsafe.Pointer(p)) +} + +var ( + uint = (C.uint)(0) + ulong C.ulong + char C.char +) + +type Context struct { + ctx *C.struct_ibv_context +} diff --git a/misc/cgo/test/callback.go b/misc/cgo/test/callback.go new file mode 100644 index 000000000..450a7cbf2 --- /dev/null +++ b/misc/cgo/test/callback.go @@ -0,0 +1,136 @@ +package cgotest + +/* +void callback(void *f); +void callGoFoo(void) { + extern void goFoo(void); + goFoo(); +} +*/ +import "C" + +import ( + "runtime" + "testing" + "unsafe" +) + +// nestedCall calls into C, back into Go, and finally to f. +func nestedCall(f func()) { + // NOTE: Depends on representation of f. + // callback(x) calls goCallback(x) + C.callback(*(*unsafe.Pointer)(unsafe.Pointer(&f))) +} + +//export goCallback +func goCallback(p unsafe.Pointer) { + (*(*func())(unsafe.Pointer(&p)))() +} + +func TestCallback(t *testing.T) { + var x = false + nestedCall(func() { x = true }) + if !x { + t.Fatal("nestedCall did not call func") + } +} + +func TestCallbackGC(t *testing.T) { + nestedCall(runtime.GC) +} + +func lockedOSThread() bool // in runtime.c + +func TestCallbackPanic(t *testing.T) { + // Make sure panic during callback unwinds properly. + if lockedOSThread() { + t.Fatal("locked OS thread on entry to TestCallbackPanic") + } + defer func() { + s := recover() + if s == nil { + t.Fatal("did not panic") + } + if s.(string) != "callback panic" { + t.Fatal("wrong panic:", s) + } + if lockedOSThread() { + t.Fatal("locked OS thread on exit from TestCallbackPanic") + } + }() + nestedCall(func() { panic("callback panic") }) + panic("nestedCall returned") +} + +func TestCallbackPanicLoop(t *testing.T) { + // Make sure we don't blow out m->g0 stack. + for i := 0; i < 100000; i++ { + TestCallbackPanic(t) + } +} + +func TestCallbackPanicLocked(t *testing.T) { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + if !lockedOSThread() { + t.Fatal("runtime.LockOSThread didn't") + } + defer func() { + s := recover() + if s == nil { + t.Fatal("did not panic") + } + if s.(string) != "callback panic" { + t.Fatal("wrong panic:", s) + } + if !lockedOSThread() { + t.Fatal("lost lock on OS thread after panic") + } + }() + nestedCall(func() { panic("callback panic") }) + panic("nestedCall returned") +} + +// Callback with zero arguments used to make the stack misaligned, +// which broke the garbage collector and other things. +func TestZeroArgCallback(t *testing.T) { + defer func() { + s := recover() + if s != nil { + t.Fatal("panic during callback:", s) + } + }() + C.callGoFoo() +} + +//export goFoo +func goFoo() { + x := 1 + for i := 0; i < 10000; i++ { + // variadic call mallocs + writes to + variadic(x, x, x) + if x != 1 { + panic("bad x") + } + } +} + +func variadic(x ...interface{}) {} + +func TestBlocking(t *testing.T) { + c := make(chan int) + go func() { + for i := 0; i < 10; i++ { + c <- <-c + } + }() + nestedCall(func() { + for i := 0; i < 10; i++ { + c <- i + if j := <-c; j != i { + t.Errorf("out of sync %d != %d", j, i) + } + } + }) +} diff --git a/misc/cgo/test/callback_c.c b/misc/cgo/test/callback_c.c new file mode 100644 index 000000000..5983a5e11 --- /dev/null +++ b/misc/cgo/test/callback_c.c @@ -0,0 +1,12 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include "_cgo_export.h" + +void +callback(void *f) +{ + goCallback(f); +} diff --git a/misc/cgo/test/cgo_test.go b/misc/cgo/test/cgo_test.go new file mode 100644 index 000000000..967dc0e92 --- /dev/null +++ b/misc/cgo/test/cgo_test.go @@ -0,0 +1,5 @@ +package cgotest + +// dummy file so gotest thinks there are tests. +// the actual tests are in the main go files, next +// to the code they test. diff --git a/misc/cgo/test/issue1222.go b/misc/cgo/test/issue1222.go new file mode 100644 index 000000000..c396a0c41 --- /dev/null +++ b/misc/cgo/test/issue1222.go @@ -0,0 +1,29 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file contains test cases for cgo. + +package cgotest + +/* +// issue 1222 +typedef union { + long align; +} xxpthread_mutex_t; + +struct ibv_async_event { + union { + int x; + } element; +}; + +struct ibv_context { + xxpthread_mutex_t mutex; +}; +*/ +import "C" + +type AsyncEvent struct { + event C.struct_ibv_async_event +} diff --git a/misc/cgo/test/issue1328.go b/misc/cgo/test/issue1328.go new file mode 100644 index 000000000..f29d7057e --- /dev/null +++ b/misc/cgo/test/issue1328.go @@ -0,0 +1,30 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cgotest + +import "testing" + +// extern void BackIntoGo(void); +// void IntoC() { BackIntoGo(); } +import "C" + +//export BackIntoGo +func BackIntoGo() { + x := 1 + + for i := 0; i < 10000; i++ { + xvariadic(x) + if x != 1 { + panic("x is not 1?") + } + } +} + +func xvariadic(x ...interface{}) { +} + +func Test1328(t *testing.T) { + C.IntoC() +} diff --git a/misc/cgo/test/issue1560.go b/misc/cgo/test/issue1560.go new file mode 100644 index 000000000..75d31c035 --- /dev/null +++ b/misc/cgo/test/issue1560.go @@ -0,0 +1,46 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cgotest + +/* +#include + +extern void BackgroundSleep(int); +void twoSleep(int n) { + BackgroundSleep(n); + sleep(n); +} +*/ +import "C" + +import ( + "testing" + "time" +) + +var sleepDone = make(chan bool) + +func parallelSleep(n int) { + C.twoSleep(C.int(n)) + <-sleepDone +} + +//export BackgroundSleep +func BackgroundSleep(n int) { + go func() { + C.sleep(C.uint(n)) + sleepDone <- true + }() +} + +func TestParallelSleep(t *testing.T) { + dt := -time.Nanoseconds() + parallelSleep(1) + dt += time.Nanoseconds() + // bug used to run sleeps in serial, producing a 2-second delay. + if dt >= 1.3e9 { + t.Fatalf("parallel 1-second sleeps slept for %f seconds", float64(dt)/1e9) + } +} diff --git a/misc/cgo/test/runtime.c b/misc/cgo/test/runtime.c new file mode 100644 index 000000000..e087c7622 --- /dev/null +++ b/misc/cgo/test/runtime.c @@ -0,0 +1,21 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Expose some runtime functions for testing. + +typedef char bool; + +bool runtime·lockedOSThread(void); + +static void +FLUSH(void*) +{ +} + +void +·lockedOSThread(bool b) +{ + b = runtime·lockedOSThread(); + FLUSH(&b); +} diff --git a/misc/dashboard/README b/misc/dashboard/README index 72d5546a4..c00311ef7 100644 --- a/misc/dashboard/README +++ b/misc/dashboard/README @@ -4,28 +4,12 @@ The files in this directory constitute the continuous builder: -godashboard/: An AppEngine that acts as a server -builder.sh, buildcontrol.sh: used by the build slaves -buildcron.sh: a build loop that can be run regularly via cron +godashboard/: an AppEngine server +builder/: gobuilder, a Go continuous build client If you wish to run a Go builder, please email golang-dev@googlegroups.com - -To set up a Go builder automatically, run buildcron.sh -(you might want to read it first to see what it does). - -To set up a Go builder by hand: - -* (Optional) create a new user 'gobuild' -* Edit ~gobuild/.bash_profile and add the following: - -export GOROOT=/gobuild/go -export GOARCH=XXX -export GOOS=XXX -export GOBIN=/gobuild/bin -export PATH=$PATH:/gobuild/bin -export BUILDER=$GOOS-$GOARCH -export BUILDHOST=godashboard.appspot.com +To run a builder: * Write the key ~gobuild/.gobuildkey You need to get it from someone who knows the key. @@ -38,13 +22,5 @@ export BUILDHOST=godashboard.appspot.com (This is for uploading tarballs to the project downloads section, and is an optional step.) -* sudo apt-get install bison gcc libc6-dev ed make -* cd ~gobuild -* mkdir bin -* hg clone https://go.googlecode.com/hg/ $GOROOT -* copy builder.sh and buildcontrol.py to ~gobuild -* chmod a+x ./builder.sh ./buildcontrol.py -* cd go -* ../buildcontrol.py next $BUILDER (just to check that things are ok) -* cd .. -* ./builder.sh (You probably want to run this in a screen long term.) +* Build and run gobuilder (see its documentation for command-line options). + diff --git a/misc/dashboard/buildcontrol.py b/misc/dashboard/buildcontrol.py deleted file mode 100644 index ec503e7ff..000000000 --- a/misc/dashboard/buildcontrol.py +++ /dev/null @@ -1,278 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -# This is a utility script for implementing a Go build slave. - -import binascii -import httplib -import os -import struct -import subprocess -import sys -import time - -buildhost = '' -buildport = -1 -buildkey = '' - -upload_project = "go" - -def main(args): - global buildport, buildhost, buildkey - - if len(args) < 2: - return usage(args[0]) - - if 'BUILDHOST' not in os.environ: - print >>sys.stderr, "Please set $BUILDHOST" - return - buildhost = os.environ['BUILDHOST'] - - if 'BUILDPORT' not in os.environ: - buildport = 80 - else: - buildport = int(os.environ['BUILDPORT']) - - try: - buildkeyfile = file('%s/.gobuildkey-%s' % (os.environ['HOME'], os.environ['BUILDER']), 'r') - buildkey = buildkeyfile.readline().strip() - except IOError: - try: - buildkeyfile = file('%s/.gobuildkey' % os.environ['HOME'], 'r') - buildkey = buildkeyfile.readline().strip() - except IOError: - print >>sys.stderr, "Need key in ~/.gobuildkey-%s or ~/.gobuildkey" % os.environ['BUILDER'] - return - - # get upload credentials - try: - username = buildkeyfile.readline().strip() - password = buildkeyfile.readline().strip() - except: - username, password = None, None - - if args[1] == 'init': - return doInit(args) - elif args[1] == 'hwget': - return doHWGet(args) - elif args[1] == 'hwset': - return doHWSet(args) - elif args[1] == 'next': - return doNext(args) - elif args[1] == 'record': - return doRecord(args) - elif args[1] == 'benchmarks': - return doBenchmarks(args) - elif args[1] == 'upload': - return doUpload(args, username, password) - else: - return usage(args[0]) - -def usage(name): - sys.stderr.write('''Usage: %s - -Commands: - init : init the build bot with the given commit as the first in history - hwget : get the most recent revision built by the given builder - hwset : get the most recent revision built by the given builder - next : get the next revision number to by built by the given builder - record : record a build result - benchmarks : record benchmark numbers - upload

    : upload tarball to googlecode -''' % name) - return 1 - -def doInit(args): - if len(args) != 3: - return usage(args[0]) - c = getCommit(args[2]) - if c is None: - fatal('Cannot get commit %s' % args[2]) - - return command('init', {'node': c.node, 'date': c.date, 'user': c.user, 'desc': c.desc}) - -def doHWGet(args, retries = 0): - if len(args) != 3: - return usage(args[0]) - conn = httplib.HTTPConnection(buildhost, buildport, True) - conn.request('GET', '/hw-get?builder=%s' % args[2]); - reply = conn.getresponse() - if reply.status == 200: - print reply.read() - elif reply.status == 500 and retries < 3: - time.sleep(3) - return doHWGet(args, retries = retries + 1) - else: - raise Failed('get-hw returned %d' % reply.status) - return 0 - -def doHWSet(args): - if len(args) != 4: - return usage(args[0]) - c = getCommit(args[3]) - if c is None: - fatal('Cannot get commit %s' % args[3]) - - return command('hw-set', {'builder': args[2], 'hw': c.node}) - -def doNext(args): - if len(args) != 3: - return usage(args[0]) - conn = httplib.HTTPConnection(buildhost, buildport, True) - conn.request('GET', '/hw-get?builder=%s' % args[2]); - reply = conn.getresponse() - if reply.status == 200: - rev = reply.read() - else: - raise Failed('get-hw returned %d' % reply.status) - - c = getCommit(rev) - next = getCommit(str(c.num + 1)) - if next is not None and next.parent == c.node: - print c.num + 1 - else: - print "" - return 0 - -def doRecord(args): - if len(args) != 5: - return usage(args[0]) - builder = args[2] - rev = args[3] - c = getCommit(rev) - if c is None: - print >>sys.stderr, "Bad revision:", rev - return 1 - logfile = args[4] - log = '' - if logfile != 'ok': - log = file(logfile, 'r').read() - return command('build', {'node': c.node, 'parent': c.parent, 'date': c.date, 'user': c.user, 'desc': c.desc, 'log': log, 'builder': builder}) - -def doBenchmarks(args): - if len(args) != 5: - return usage(args[0]) - builder = args[2] - rev = args[3] - c = getCommit(rev) - if c is None: - print >>sys.stderr, "Bad revision:", rev - return 1 - - benchmarks = {} - for line in file(args[4], 'r').readlines(): - if 'Benchmark' in line and 'ns/op' in line: - parts = line.split() - if parts[3] == 'ns/op': - benchmarks[parts[0]] = (parts[1], parts[2]) - - e = [] - for (name, (a, b)) in benchmarks.items(): - e.append(struct.pack('>H', len(name))) - e.append(name) - e.append(struct.pack('>H', len(a))) - e.append(a) - e.append(struct.pack('>H', len(b))) - e.append(b) - return command('benchmarks', {'node': c.node, 'builder': builder, 'benchmarkdata': binascii.b2a_base64(''.join(e))}) - -def doUpload(args, username, password): - # fail gracefully if no username or password set - if not username or not password: - return - - if len(args) != 5: - return usage(args[0]) - builder = args[2] - summary = args[3] - filename = args[4] - - from googlecode_upload import upload - code, msg, url = upload( - filename, # filename - upload_project, # 'go' - username, - password, - summary, - builder.split('-'), # labels - ) - if code != 201: - raise Failed('Upload returned code %s msg "%s".' % (code, msg)) - -def encodeMultipartFormdata(fields, files): - """fields is a sequence of (name, value) elements for regular form fields. - files is a sequence of (name, filename, value) elements for data to be uploaded as files""" - BOUNDARY = '----------ThIs_Is_tHe_bouNdaRY_$' - CRLF = '\r\n' - L = [] - for (key, value) in fields.items(): - L.append('--' + BOUNDARY) - L.append('Content-Disposition: form-data; name="%s"' % key) - L.append('') - L.append(value) - for (key, filename, value) in files: - L.append('--' + BOUNDARY) - L.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename)) - L.append('Content-Type: %s' % get_content_type(filename)) - L.append('') - L.append(value) - L.append('--' + BOUNDARY + '--') - L.append('') - body = CRLF.join(L) - content_type = 'multipart/form-data; boundary=%s' % BOUNDARY - return content_type, body - -def unescapeXML(s): - return s.replace('<', '<').replace('>', '>').replace('&', '&') - -class Commit: - pass - -def getCommit(rev): - output, stderr = subprocess.Popen(['hg', 'log', '-r', rev, '-l', '1', '--template', '{rev}>{node|escape}>{author|escape}>{date}>{desc}'], stdout = subprocess.PIPE, stderr = subprocess.PIPE, close_fds = True).communicate() - if len(stderr) > 0: - return None - [n, node, user, date, desc] = output.split('>', 4) - - c = Commit() - c.num = int(n) - c.node = unescapeXML(node) - c.user = unescapeXML(user) - c.date = unescapeXML(date) - c.desc = desc - c.parent = '' - - if c.num > 0: - output, _ = subprocess.Popen(['hg', 'log', '-r', str(c.num - 1), '-l', '1', '--template', '{node}'], stdout = subprocess.PIPE, close_fds = True).communicate() - c.parent = output - - return c - -class Failed(Exception): - def __init__(self, msg): - self.msg = msg - def __str__(self): - return self.msg - -def command(cmd, args, retries = 0): - args['key'] = buildkey - contentType, body = encodeMultipartFormdata(args, []) - print body - conn = httplib.HTTPConnection(buildhost, buildport, True) - conn.request('POST', '/' + cmd, body, {'Content-Type': contentType}) - reply = conn.getresponse() - if reply.status != 200: - print "Command failed. Output:" - print reply.read() - if reply.status == 500 and retries < 3: - print "Was a 500. Waiting two seconds and trying again." - time.sleep(2) - return command(cmd, args, retries = retries + 1) - if reply.status != 200: - raise Failed('Command "%s" returned %d' % (cmd, reply.status)) - -if __name__ == '__main__': - sys.exit(main(sys.argv)) diff --git a/misc/dashboard/buildcron.sh b/misc/dashboard/buildcron.sh deleted file mode 100644 index 7aa70ce57..000000000 --- a/misc/dashboard/buildcron.sh +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env bash -# Copyright 2010 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -# This script can be run to create a new builder and then -# to keep it running via cron. First, run it by hand until it -# starts up without errors and can run the loop. Then, once -# you're confident that it works, add this to your crontab: -# -# */5 * * * * cd $HOME; path/to/buildcron.sh darwin 386 >/dev/null 2>/dev/null - -if [ $# != 2 ]; then - echo 'usage: buildcron.sh goos goarch' 1>&2 - exit 2 -fi - -export GOOS=$1 -export GOARCH=$2 - -# Check if we are already running. -# First command must not be pipeline, to avoid seeing extra processes in ps. -all=$(ps axwwu) -pid=$(echo "$all" | grep "buildcron.sh $1 $2" | grep -v "sh -c" | grep -v $$ | awk '{print $2}') -if [ "$pid" != "" ]; then - #echo already running buildcron.sh $1 $2 - #echo "$all" | grep "buildcron.sh $1 $2" | grep -v "sh -c" | grep -v $$ - exit 0 -fi - -export BUILDHOST=godashboard.appspot.com -export BUILDER=${GOOS}-${GOARCH} -export GOROOT=$HOME/go-$BUILDER/go -export GOBIN=$HOME/go-$BUILDER/bin - -if [ ! -f ~/.gobuildkey-$BUILDER ]; then - echo "need gobuildkey for $BUILDER in ~/.gobuildkey-$BUILDER" 1>&2 - exit 2 -fi - -if [ ! -d $GOROOT ]; then - mkdir -p $GOROOT - hg clone https://go.googlecode.com/hg/ $GOROOT -else - cd $GOROOT - hg pull -u || exit 1 -fi -mkdir -p $GOROOT/bin - -cd $GOROOT/.. -cp go/misc/dashboard/{builder.sh,buildcontrol.py,googlecode_upload.py} . -chmod a+x builder.sh buildcontrol.py -cd go -../buildcontrol.py next $BUILDER -cd .. -./builder.sh - - diff --git a/misc/dashboard/builder.sh b/misc/dashboard/builder.sh deleted file mode 100644 index 4a8d117bf..000000000 --- a/misc/dashboard/builder.sh +++ /dev/null @@ -1,95 +0,0 @@ -#!/usr/bin/env bash - -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -fatal() { - echo $0: $1 1>&2 - exit 1 -} - -if [ ! -d go ] ; then - fatal "Please run in directory that contains a checked out repo in 'go'" -fi - -if [ ! -f buildcontrol.py ] ; then - fatal 'Please include buildcontrol.py in this directory' -fi - -if [ "x$BUILDER" == "x" ] ; then - fatal 'Please set $BUILDER to the name of this builder' -fi - -if [ "x$BUILDHOST" == "x" ] ; then - fatal 'Please set $BUILDHOST to the hostname of the gobuild server' -fi - -if [ "x$GOARCH" == "x" -o "x$GOOS" == "x" ] ; then - fatal 'Please set $GOARCH and $GOOS' -fi - -export PATH=$PATH:`pwd`/candidate/bin -export GOBIN=`pwd`/candidate/bin -export GOROOT_FINAL=/usr/local/go - -while true ; do ( - cd go || fatal "Cannot cd into 'go'" - hg pull -u || fatal "hg sync failed" - rev=`python ../buildcontrol.py next $BUILDER` - if [ $? -ne 0 ] ; then - fatal "Cannot get next revision" - fi - cd .. || fatal "Cannot cd up" - if [ "x$rev" == "x" ] ; then - sleep 10 - continue - fi - - echo "Cloning for revision $rev" - rm -Rf candidate - hg clone -r $rev go candidate || fatal "hg clone failed" - export GOROOT=`pwd`/candidate - mkdir -p candidate/bin || fatal "Cannot create candidate/bin" - cd candidate/src || fatal "Cannot cd into candidate/src" - echo "Building revision $rev" - ALL=all.bash - if [ -f all-$GOOS.bash ]; then - ALL=all-$GOOS.bash - elif [ -f all-$GOARCH.bash ]; then - ALL=all-$GOARCH.bash - fi - ./$ALL > ../log 2>&1 - if [ $? -ne 0 ] ; then - echo "Recording failure for $rev" - python ../../buildcontrol.py record $BUILDER $rev ../log || fatal "Cannot record result" - else - echo "Recording success for $rev" - python ../../buildcontrol.py record $BUILDER $rev ok || fatal "Cannot record result" - if [ "$ALL" = "all.bash" ]; then - echo "Running benchmarks" - cd pkg || fatal "failed to cd to pkg" - make bench > ../../benchmarks 2>&1 - python ../../../buildcontrol.py benchmarks $BUILDER $rev ../../benchmarks || fatal "Cannot record benchmarks" - cd .. || fatal "failed to cd out of pkg" - fi - # check if we're at a release (via the hg summary) - # if so, package the tar.gz and upload to googlecode - SUMMARY=$(hg log -l 1 | grep summary\: | awk '{print $2}') - if [[ "x${SUMMARY:0:7}" == "xrelease" ]]; then - echo "Uploading binary to googlecode" - TARBALL="go.$SUMMARY.$BUILDER.tar.gz" - ./clean.bash --nopkg - # move contents of candidate/ to candidate/go/ for archival - cd ../.. || fatal "Cannot cd up" - mv candidate go-candidate || fatal "Cannot rename candidate" - mkdir candidate || fatal "Cannot mkdir candidate" - mv go-candidate candidate/go || fatal "Cannot mv directory" - cd candidate || fatal "Cannot cd candidate" - # build tarball - tar czf ../$TARBALL go || fatal "Cannot create tarball" - ../buildcontrol.py upload $BUILDER $SUMMARY ../$TARBALL || fatal "Cannot upload tarball" - fi - fi - sleep 10 -) done diff --git a/misc/dashboard/builder/doc.go b/misc/dashboard/builder/doc.go index a28658a95..7bb7ccbe3 100644 --- a/misc/dashboard/builder/doc.go +++ b/misc/dashboard/builder/doc.go @@ -39,7 +39,7 @@ Optional flags: -release: Build and deliver binary release archive -rev=N: Build revision N and exit - + -cmd="./all.bash": Build command (specify absolute or relative to go/src) -v: Verbose logging @@ -47,8 +47,8 @@ Optional flags: -external: External package builder mode (will not report Go build state to dashboard, issue releases, or run benchmarks) -The key file should be located at $HOME/.gobuilder or, for a builder-specific -key, $HOME/.gobuilder-$BUILDER (eg, $HOME/.gobuilder-linux-amd64). +The key file should be located at $HOME/.gobuildkey or, for a builder-specific +key, $HOME/.gobuildkey-$BUILDER (eg, $HOME/.gobuildkey-linux-amd64). The build key file is a text file of the format: diff --git a/misc/dashboard/builder/exec.go b/misc/dashboard/builder/exec.go index 53ea93ac5..3c6fbdced 100644 --- a/misc/dashboard/builder/exec.go +++ b/misc/dashboard/builder/exec.go @@ -49,7 +49,7 @@ func runLog(envv []string, logfile, dir string, argv ...string) (output string, b := new(bytes.Buffer) var w io.Writer = b if logfile != "" { - f, err := os.Open(logfile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) + f, err := os.OpenFile(logfile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666) if err != nil { return } diff --git a/misc/dashboard/builder/main.go b/misc/dashboard/builder/main.go index fc11d365e..735717e28 100644 --- a/misc/dashboard/builder/main.go +++ b/misc/dashboard/builder/main.go @@ -64,7 +64,7 @@ var ( var ( goroot string - releaseRegexp = regexp.MustCompile(`^release\.[0-9\-.]+`) + releaseRegexp = regexp.MustCompile(`^(release|weekly)\.[0-9\-.]+`) benchRequests vector.Vector ) diff --git a/misc/dashboard/godashboard/gobuild.py b/misc/dashboard/godashboard/gobuild.py index 08d70ec64..1eacdb38e 100644 --- a/misc/dashboard/godashboard/gobuild.py +++ b/misc/dashboard/godashboard/gobuild.py @@ -7,7 +7,6 @@ from google.appengine.api import mail from google.appengine.api import memcache -from google.appengine.runtime import DeadlineExceededError from google.appengine.ext import db from google.appengine.ext import webapp from google.appengine.ext.webapp import template @@ -219,7 +218,7 @@ class SetHighwater(webapp.RequestHandler): q = Commit.all() q.order('-__key__') recent = q.fetch(N+1) - for c in head: + for c in recent: if c.node == newhw: found = True break @@ -384,7 +383,6 @@ class Benchmarks(webapp.RequestHandler): self.response.headers['Content-Type'] = 'text/plain; charset=utf-8' self.response.out.write('{"benchmarks": [') - first = True sep = "\n\t" for b in bs: self.response.out.write('%s"%s"' % (sep, b.name)) diff --git a/misc/dashboard/godashboard/package.py b/misc/dashboard/godashboard/package.py index 7570d2218..a1bca1908 100644 --- a/misc/dashboard/godashboard/package.py +++ b/misc/dashboard/godashboard/package.py @@ -6,7 +6,6 @@ # It must be run by App Engine. from google.appengine.api import memcache -from google.appengine.runtime import DeadlineExceededError from google.appengine.ext import db from google.appengine.ext import webapp from google.appengine.ext.webapp import template @@ -14,15 +13,10 @@ from google.appengine.ext.webapp.util import run_wsgi_app from google.appengine.api import users from google.appengine.api import mail from google.appengine.api import urlfetch -import binascii import datetime -import hashlib -import hmac import logging import os import re -import struct -import time import urllib2 import sets @@ -52,6 +46,8 @@ class Project(db.Model): re_bitbucket = re.compile(r'^bitbucket\.org/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+$') re_googlecode = re.compile(r'^[a-z0-9\-]+\.googlecode\.com/(svn|hg)$') re_github = re.compile(r'^github\.com/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+$') +re_launchpad = re.compile(r'^launchpad\.net/([a-z0-9A-Z_.\-]+(/[a-z0-9A-Z_.\-]+)?|~[a-z0-9A-Z_.\-]+/(\+junk|[a-z0-9A-Z_.\-]+)/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]+)?$') + def vc_to_web(path): if re_bitbucket.match(path): @@ -65,6 +61,8 @@ def vc_to_web(path): elif re_googlecode.match(path): check_url = 'http://'+path web = 'http://code.google.com/p/' + path[:path.index('.')] + elif re_launchpad.match(path): + check_url = web = 'https://'+path else: return False, False return web, check_url @@ -72,7 +70,8 @@ def vc_to_web(path): re_bitbucket_web = re.compile(r'bitbucket\.org/([a-z0-9A-Z_.\-]+)/([a-z0-9A-Z_.\-]+)') re_googlecode_web = re.compile(r'code.google.com/p/([a-z0-9\-]+)') re_github_web = re.compile(r'github\.com/([a-z0-9A-Z_.\-]+)/([a-z0-9A-Z_.\-]+)') -re_striphttp = re.compile(r'http://(www\.)?') +re_launchpad_web = re.compile(r'launchpad\.net/([a-z0-9A-Z_.\-]+(/[a-z0-9A-Z_.\-]+)?|~[a-z0-9A-Z_.\-]+/(\+junk|[a-z0-9A-Z_.\-]+)/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]+)?') +re_striphttp = re.compile(r'https?://(www\.)?') def web_to_vc(url): url = re_striphttp.sub('', url) @@ -93,6 +92,9 @@ def web_to_vc(url): vcs = 'hg' except: pass return path + vcs + m = re_launchpad_web.match(url) + if m: + return m.group(0) return False MaxPathLength = 100 @@ -136,7 +138,7 @@ class PackagePage(webapp.RequestHandler): sep = ',' s += '\n]}\n' json = s - memcache.set('view-package-json', json, time=CacheTimeoout) + memcache.set('view-package-json', json, time=CacheTimeout) self.response.out.write(json) def can_get_url(self, url): @@ -150,7 +152,8 @@ class PackagePage(webapp.RequestHandler): def is_valid_package_path(self, path): return (re_bitbucket.match(path) or re_googlecode.match(path) or - re_github.match(path)) + re_github.match(path) or + re_launchpad.match(path)) def record_pkg(self, path): # sanity check string diff --git a/misc/emacs/go-mode-load.el b/misc/emacs/go-mode-load.el index c73156317..0ace46dfa 100644 --- a/misc/emacs/go-mode-load.el +++ b/misc/emacs/go-mode-load.el @@ -18,10 +18,11 @@ ;; (let ((generated-autoload-file buffer-file-name)) (update-file-autoloads "go-mode.el")) -;;;### (autoloads (go-mode) "go-mode" "go-mode.el" (19168 32439)) +;;;### (autoloads (gofmt-before-save gofmt go-mode) "go-mode" "go-mode.el" +;;;;;; (19847 61431)) ;;; Generated autoloads from go-mode.el -(autoload (quote go-mode) "go-mode" "\ +(autoload 'go-mode "go-mode" "\ Major mode for editing Go source text. This provides basic syntax highlighting for keywords, built-ins, @@ -30,7 +31,19 @@ functions, and some types. It also provides indentation that is \(fn)" t nil) -(add-to-list (quote auto-mode-alist) (cons "\\.go$" (function go-mode))) +(add-to-list 'auto-mode-alist (cons "\\.go$" #'go-mode)) + +(autoload 'gofmt "go-mode" "\ +Pipe the current buffer through the external tool `gofmt`. +Replace the current buffer on success; display errors on failure. + +\(fn)" t nil) + +(autoload 'gofmt-before-save "go-mode" "\ +Add this to .emacs to run gofmt on the current buffer when saving: + (add-hook 'before-save-hook #'gofmt-before-save) + +\(fn)" t nil) ;;;*** diff --git a/misc/emacs/go-mode.el b/misc/emacs/go-mode.el index 2624e87cb..692cabfe5 100644 --- a/misc/emacs/go-mode.el +++ b/misc/emacs/go-mode.el @@ -498,21 +498,45 @@ Useful for development work." (require 'go-mode) (go-mode)) -(provide 'go-mode) - +;;;###autoload (defun gofmt () - "Pipe the current buffer through the external tool `gofmt`." - - (interactive) - ;; for some reason save-excursion isn't working - ;; probably because shell-command-on-region deletes the contents of the - ;; region before filling in the new values - ;; so we will save the point/mark by hand - ;; similarly we can't use push-mark/pop-mark - (let ((old-mark (mark t)) (old-point (point))) - (save-restriction - (let (deactivate-mark) - (widen) - (shell-command-on-region (point-min) (point-max) "gofmt" t t shell-command-default-error-buffer))) - (goto-char (min old-point (point-max))) - (if old-mark (set-mark (min old-mark (point-max)))))) + "Pipe the current buffer through the external tool `gofmt`. +Replace the current buffer on success; display errors on failure." + + (interactive) + (let ((srcbuf (current-buffer))) + (with-temp-buffer + (let ((outbuf (current-buffer)) + (errbuf (get-buffer-create "*Gofmt Errors*"))) + (with-current-buffer errbuf (erase-buffer)) + (with-current-buffer srcbuf + (save-restriction + (let (deactivate-mark) + (widen) + (if (= 0 (shell-command-on-region (point-min) (point-max) "gofmt" + outbuf nil errbuf)) + ;; gofmt succeeded: replace the current buffer with outbuf, + ;; restore the mark and point, and discard errbuf. + (let ((old-mark (mark t)) (old-point (point))) + (erase-buffer) + (insert-buffer-substring outbuf) + (goto-char (min old-point (point-max))) + (if old-mark (set-mark (min old-mark (point-max)))) + (kill-buffer errbuf)) + + ;; gofmt failed: display the errors + (display-buffer errbuf))))) + + ;; Collapse any window opened on outbuf if shell-command-on-region + ;; displayed it. + (delete-windows-on outbuf))))) + +;;;###autoload +(defun gofmt-before-save () + "Add this to .emacs to run gofmt on the current buffer when saving: + (add-hook 'before-save-hook #'gofmt-before-save)" + + (interactive) + (when (eq major-mode 'go-mode) (gofmt))) + +(provide 'go-mode) diff --git a/misc/goplay/goplay.go b/misc/goplay/goplay.go index 3ca5ed80c..f887fbbda 100644 --- a/misc/goplay/goplay.go +++ b/misc/goplay/goplay.go @@ -83,7 +83,7 @@ func Compile(w http.ResponseWriter, req *http.Request) { } // write request Body to x.go - f, err := os.Open(src, os.O_CREAT|os.O_WRONLY|os.O_TRUNC, 0666) + f, err := os.Create(src) if err != nil { error(w, nil, err) return diff --git a/misc/kate/go.xml b/misc/kate/go.xml index b8ff59267..14d88b26a 100644 --- a/misc/kate/go.xml +++ b/misc/kate/go.xml @@ -12,13 +12,13 @@ break case + chan const continue default defer else fallthrough - false for func go @@ -26,22 +26,25 @@ if import interface - iota - nil + map package range return select struct switch - true type var + + false + iota + nil + true + bool byte - chan complex64 complex128 float32 @@ -51,7 +54,6 @@ int16 int32 int64 - map string uint uintptr @@ -64,7 +66,6 @@ append cap close - closed complex copy imag @@ -81,6 +82,8 @@ + @@ -119,6 +122,7 @@ + diff --git a/misc/notepadplus/README b/misc/notepadplus/README new file mode 100755 index 000000000..000d31746 --- /dev/null +++ b/misc/notepadplus/README @@ -0,0 +1,8 @@ +Given a Notepad++ installation at : + +1. Add the contents of userDefineLang.xml at \userDefineLang.xml + between ... + +2. Copy go.xml to \plugins\APIs + +3. Restart Notepad++ diff --git a/misc/notepadplus/go.xml b/misc/notepadplus/go.xml new file mode 100755 index 000000000..7c5d8a173 --- /dev/null +++ b/misc/notepadplus/go.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/misc/notepadplus/userDefineLang.xml b/misc/notepadplus/userDefineLang.xml new file mode 100755 index 000000000..d1927a340 --- /dev/null +++ b/misc/notepadplus/userDefineLang.xml @@ -0,0 +1,36 @@ + + + + + + + + + "`0"` + + + ( ) [ ] { } ... . , _ & ^ % > < ! = + - * | : + 1/* 2*/ 0// + append bool break byte cap case chan close complex complex128 complex64 const continue copy default defer else fallthrough false float32 float64 for func go goto if iota imag import int int16 int32 int64 int8 interface len make map new nil package panic print println range real recover return select string struct switch true type uint uint16 uint32 uint64 uint8 uintptr var + + + + + + + + + + + + + + + + + + + + + + diff --git a/misc/swig/callback/Makefile b/misc/swig/callback/Makefile new file mode 100644 index 000000000..fde0d107b --- /dev/null +++ b/misc/swig/callback/Makefile @@ -0,0 +1,17 @@ +# Copyright 2011 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +include ../../../src/Make.inc + +TARG=swig/callback +SWIGFILES=\ + callback.swigcxx + +CLEANFILES+=run + +include ../../../src/Make.pkg + +%: install %.go + $(GC) $*.go + $(LD) $(SWIG_RPATH) -o $@ $*.$O diff --git a/misc/swig/callback/callback.h b/misc/swig/callback/callback.h new file mode 100644 index 000000000..80232a8b3 --- /dev/null +++ b/misc/swig/callback/callback.h @@ -0,0 +1,24 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +class Callback { +public: + virtual ~Callback() { } + virtual std::string run() { return "Callback::run"; } +}; + +class Caller { +private: + Callback *callback_; +public: + Caller(): callback_(0) { } + ~Caller() { delCallback(); } + void delCallback() { delete callback_; callback_ = 0; } + void setCallback(Callback *cb) { delCallback(); callback_ = cb; } + std::string call() { + if (callback_ != 0) + return callback_->run(); + return ""; + } +}; diff --git a/misc/swig/callback/callback.swigcxx b/misc/swig/callback/callback.swigcxx new file mode 100644 index 000000000..0c97ef101 --- /dev/null +++ b/misc/swig/callback/callback.swigcxx @@ -0,0 +1,18 @@ +/* Copyright 2011 The Go Authors. All rights reserved. + Use of this source code is governed by a BSD-style + license that can be found in the LICENSE file. */ + +/* An example of writing a C++ virtual function in Go. */ + +%module(directors="1") callback + +%{ +#include +#include "callback.h" +%} + +%include "std_string.i" + +%feature("director"); + +%include "callback.h" diff --git a/misc/swig/callback/run b/misc/swig/callback/run new file mode 100755 index 000000000..de150ed05 Binary files /dev/null and b/misc/swig/callback/run differ diff --git a/misc/swig/callback/run.go b/misc/swig/callback/run.go new file mode 100644 index 000000000..a76e636cb --- /dev/null +++ b/misc/swig/callback/run.go @@ -0,0 +1,39 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "swig/callback" + "fmt" +) + +type GoCallback struct{} + +func (p *GoCallback) Run() string { + return "GoCallback.Run" +} + +func main() { + c := callback.NewCaller() + cb := callback.NewCallback() + + c.SetCallback(cb) + s := c.Call() + fmt.Println(s) + if s != "Callback::run" { + panic(s) + } + c.DelCallback() + + cb = callback.NewDirectorCallback(&GoCallback{}) + c.SetCallback(cb) + s = c.Call() + fmt.Println(s) + if s != "GoCallback.Run" { + panic(s) + } + c.DelCallback() + callback.DeleteDirectorCallback(cb) +} diff --git a/misc/swig/stdio/Makefile b/misc/swig/stdio/Makefile new file mode 100644 index 000000000..e7d330587 --- /dev/null +++ b/misc/swig/stdio/Makefile @@ -0,0 +1,17 @@ +# Copyright 2011 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +include ../../../src/Make.inc + +TARG=swig/file +SWIGFILES=\ + file.swig + +CLEANFILES+=hello + +include ../../../src/Make.pkg + +%: install %.go + $(GC) $*.go + $(LD) $(SWIG_RPATH) -o $@ $*.$O diff --git a/misc/swig/stdio/file.swig b/misc/swig/stdio/file.swig new file mode 100644 index 000000000..57c623f8f --- /dev/null +++ b/misc/swig/stdio/file.swig @@ -0,0 +1,11 @@ +/* Copyright 2011 The Go Authors. All rights reserved. + Use of this source code is governed by a BSD-style + license that can be found in the LICENSE file. */ + +/* A trivial example of wrapping a C library using SWIG. */ + +%{ +#include +%} + +int puts(const char *); diff --git a/misc/swig/stdio/hello b/misc/swig/stdio/hello new file mode 100755 index 000000000..10c55631f Binary files /dev/null and b/misc/swig/stdio/hello differ diff --git a/misc/swig/stdio/hello.go b/misc/swig/stdio/hello.go new file mode 100644 index 000000000..eec294278 --- /dev/null +++ b/misc/swig/stdio/hello.go @@ -0,0 +1,11 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "swig/file" + +func main() { + file.Puts("Hello, world") +} diff --git a/misc/vim/ftplugin/go/fmt.vim b/misc/vim/ftplugin/go/fmt.vim new file mode 100644 index 000000000..18a2156f5 --- /dev/null +++ b/misc/vim/ftplugin/go/fmt.vim @@ -0,0 +1,30 @@ +" Copyright 2011 The Go Authors. All rights reserved. +" Use of this source code is governed by a BSD-style +" license that can be found in the LICENSE file. +" +" fmt.vim: Vim command to format Go files with gofmt. +" +" This filetype plugin add a new commands for go buffers: +" +" :Fmt +" +" Filter the current Go buffer through gofmt. +" It tries to preserve cursor position and avoids +" replacing the buffer with stderr output. +" + +command! Fmt call s:GoFormat() + +function! s:GoFormat() + let view = winsaveview() + %!gofmt + if v:shell_error + %| " output errors returned by gofmt + " TODO(dchest): perhaps, errors should go to quickfix + undo + echohl Error | echomsg "Gofmt returned error" | echohl None + endif + call winrestview(view) +endfunction + +" vim:ts=4:sw=4:et diff --git a/misc/vim/indent/go.vim b/misc/vim/indent/go.vim new file mode 100644 index 000000000..2e9f191f5 --- /dev/null +++ b/misc/vim/indent/go.vim @@ -0,0 +1,30 @@ +" Copyright 2011 The Go Authors. All rights reserved. +" Use of this source code is governed by a BSD-style +" license that can be found in the LICENSE file. +" +" indent/go.vim: Vim indent file for Go. +" + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +" C indentation is mostly correct +setlocal cindent + +" Options set: +" +0 -- Don't indent continuation lines (because Go doesn't use semicolons +" much) +" L0 -- Don't move jump labels (NOTE: this isn't correct when working with +" gofmt, but it does keep struct literals properly indented.) +" :0 -- Align case labels with switch statement +" l1 -- Always align case body relative to case labels +" J1 -- Indent JSON-style objects (properly indents struct-literals) +" (0, Ws -- Indent lines inside of unclosed parentheses by one shiftwidth +" m1 -- Align closing parenthesis line with first non-blank of matching +" parenthesis line +" +" Known issue: Trying to do a multi-line struct literal in a short variable +" declaration will not indent properly. +setlocal cinoptions+=+0,L0,:0,l1,J1,(0,Ws,m1 diff --git a/misc/vim/readme.txt b/misc/vim/readme.txt index f836f58f3..3c3255113 100644 --- a/misc/vim/readme.txt +++ b/misc/vim/readme.txt @@ -35,3 +35,16 @@ To install one of the available filetype plugins for Go: 3. Add the following line to your .vimrc file (normally $HOME/.vimrc): filetype plugin on + + +Vim indentation plugin for Go +============================= + +To install automatic indentation for Go: + + 1. Same as 1 above. + 2. Copy or link indent/go.vim to the indent directory underneath your vim + runtime directory (normally $HOME/.vim/indent). + 3. Add the following line to your .vimrc file (normally $HOME/.vimrc): + + filetype indent on diff --git a/misc/vim/syntax/go.vim b/misc/vim/syntax/go.vim index 7507cada2..26d7defe3 100644 --- a/misc/vim/syntax/go.vim +++ b/misc/vim/syntax/go.vim @@ -85,7 +85,7 @@ syn match goType /\/ syn match goDeclaration /^func\>/ " Predefined functions and values -syn keyword goBuiltins append cap close closed complex copy imag len +syn keyword goBuiltins append cap close complex copy imag len syn keyword goBuiltins make new panic print println real recover syn keyword goConstants iota true false nil @@ -181,7 +181,7 @@ if go_highlight_extra_types != 0 syn match goExtraType /\/ syn match goExtraType /\/ syn match goExtraType /\<\(os\.Error\)\>/ - syn match goExtraType /\/ + syn match goExtraType /\/ syn match goExtraType /\/ endif diff --git a/src/Make.cmd b/src/Make.cmd index 2b9aba4a5..6f88e5cc2 100644 --- a/src/Make.cmd +++ b/src/Make.cmd @@ -23,7 +23,7 @@ install: $(QUOTED_GOBIN)/$(TARG) $(QUOTED_GOBIN)/$(TARG): $(TARG) cp -f $(TARG) $(QUOTED_GOBIN) -CLEANFILES+=$(TARG) +CLEANFILES+=$(TARG) _test _testmain.go nuke: clean rm -f $(QUOTED_GOBIN)/$(TARG) @@ -34,8 +34,6 @@ testpackage: _test/main.a testpackage-clean: rm -f _test/main.a _gotest_.$O -testpackage: _test/main.a - _test/main.a: _gotest_.$O @mkdir -p _test rm -f $@ diff --git a/src/Make.common b/src/Make.common index af6d04adc..34d7016f4 100644 --- a/src/Make.common +++ b/src/Make.common @@ -5,6 +5,15 @@ clean: rm -rf *.o *.a *.[$(OS)] [$(OS)].out $(CLEANFILES) +install.clean: install + rm -rf *.o *.a *.[$(OS)] [$(OS)].out $(CLEANFILES) + +test.clean: test + rm -rf *.o *.a *.[$(OS)] [$(OS)].out $(CLEANFILES) + +testshort.clean: testshort + rm -rf *.o *.a *.[$(OS)] [$(OS)].out $(CLEANFILES) + %.make: $(MAKE) -C $* install diff --git a/src/Make.inc b/src/Make.inc index 2889c7edf..a6edb165a 100644 --- a/src/Make.inc +++ b/src/Make.inc @@ -29,14 +29,20 @@ ifeq ($(GOOS),) GOOS:=$(GOHOSTOS) endif -ifeq ($(GOOS),darwin) -else ifeq ($(GOOS),freebsd) -else ifeq ($(GOOS),linux) -else ifeq ($(GOOS),tiny) -else ifeq ($(GOOS),plan9) -else ifeq ($(GOOS),windows) -else -$(error Invalid $$GOOS '$(GOOS)'; must be darwin, freebsd, linux, plan9, tiny, or windows) +GOOS_LIST=\ + darwin\ + freebsd\ + linux\ + plan9\ + windows\ + +GOARCH_LIST=\ + 386\ + amd64\ + arm\ + +ifeq ($(filter $(GOOS),$(GOOS_LIST)),) +$(error Invalid $$GOOS '$(GOOS)'; must be one of: $(GOOS_LIST)) endif ifeq ($(GOHOSTARCH),) @@ -59,24 +65,25 @@ ifeq ($(GOOS),darwin) GOHOSTARCH:=$(GOARCH) endif +ifeq ($(filter $(GOARCH),$(GOARCH_LIST)),) +$(error Invalid $$GOARCH '$(GOARCH)'; must be one of: $(GOARCH_LIST)) +endif + ifeq ($(GOARCH),386) O:=8 else ifeq ($(GOARCH),amd64) O:=6 else ifeq ($(GOARCH),arm) - O:=5 -ifeq ($(GOOS),linux) -else +ifneq ($(GOOS),linux) $(error Invalid $$GOOS '$(GOOS)' for GOARCH=arm; must be linux) endif - else -$(error Invalid $$GOARCH '$(GOARCH)'; must be 386, amd64, or arm) +$(error Missing $$O for '$(GOARCH)') endif # Save for recursive make to avoid recomputing. -export GOARCH GOOS GOHOSTARCH GOHOSTOS +export GOARCH GOOS GOHOSTARCH GOHOSTOS GOARCH_LIST GOOS_LIST # ugly hack to deal with whitespaces in $GOROOT nullstring := diff --git a/src/Make.pkg b/src/Make.pkg index 3d616ca99..59ce56ac0 100644 --- a/src/Make.pkg +++ b/src/Make.pkg @@ -41,17 +41,28 @@ CGO_OFILES+=$(patsubst %.go,%.cgo2.o,$(CGOFILES)) _cgo_export.o OFILES+=_cgo_defun.$O _cgo_import.$O $(CGO_OFILES) endif +ifdef SWIGFILES +GOFILES+=$(patsubst %.swig,_obj/%.go,$(patsubst %.swigcxx,%.swig,$(SWIGFILES))) +OFILES+=$(patsubst %.swig,_obj/%_gc.$O,$(patsubst %.swigcxx,%.swig,$(SWIGFILES))) +SWIG_PREFIX=$(subst /,-,$(TARG)) +SWIG_SOS+=$(patsubst %.swig,_obj/$(SWIG_PREFIX)-%.so,$(patsubst %.swigcxx,%.swig,$(SWIGFILES))) +INSTALLFILES+=$(patsubst %.swig,$(pkgdir)/swig/$(SWIG_PREFIX)-%.so,$(patsubst %.swigcxx,%.swig,$(SWIGFILES))) +endif + PREREQ+=$(patsubst %,%.make,$(DEPS)) coverage: gotest 6cov -g $(shell pwd) $O.out | grep -v '_test\.go:' -CLEANFILES+=*.so _obj _test _testmain.go *.exe _cgo* *.cgo[12].* +CLEANFILES+=*.so _obj _test _testmain.go *.exe _cgo* *.cgo[12].* test.out build.out test: gotest +testshort: + gotest -test.short -test.timeout=120 + bench: gotest -test.bench=. -test.run="Do not run tests" @@ -173,6 +184,54 @@ RUNTIME_CFLAGS=-I$(pkgdir) _cgo_defun.$O: _obj/_cgo_defun.c $(CC) $(CFLAGS) $(RUNTIME_CFLAGS) -I . -o "$@" _obj/_cgo_defun.c +# To use swig in a Go package, add a line +# +# SWIGFILES=x.swig +# +# to the main Makefile. This signals that SWIG should process the +#.swig file when building the package. +# +# To wrap C code, use an extension of .swig. To wrap C++ code, use an +# extension of .swigcxx. +# +# SWIGFILES=myclib.swig mycxxlib.swigcxx + +ifdef SWIGFILES +_obj/%._swig_run _obj/%.go _obj/%_gc.c _obj/%_wrap.c: %.swig + @mkdir -p _obj + swig -go -module $* -soname $(SWIG_PREFIX)-$*.so -o _obj/$*_wrap.c -outdir _obj $< + +_obj/%._swig_run _obj/%.go _obj/%_gc.c _obj/%_wrap.cxx: %.swigcxx + @mkdir -p _obj + swig -go -c++ -module $* -soname $(SWIG_PREFIX)-$*.so -o _obj/$*_wrap.cxx -outdir _obj $< + +_obj/%_gc.$O: _obj/%_gc.c + $(CC) $(CFLAGS) -I . -I$(pkgdir) -o "$@" _obj/$*_gc.c + +_obj/%_wrap.o: _obj/%_wrap.c + $(HOST_CC) $(_CGO_CFLAGS_$(GOARCH)) -I . -g -fPIC -O2 -o $@ -c $^ $(SWIG_CFLAGS) + +HOST_CXX=g++ + +_obj/%_wrapcxx.o: _obj/%_wrap.cxx + $(HOST_CXX) $(_CGO_CFLAGS_$(GOARCH)) -I . -g -fPIC -O2 -o $@ -c $^ $(SWIG_CXXFLAGS) + +_obj/$(SWIG_PREFIX)-%.so: _obj/%_wrap.o + $(HOST_CC) $(_CGO_CFLAGS_$(GOARCH)) -o $@ $^ $(SWIG_LDFLAGS) $(_CGO_LDFLAGS_$(GOOS)) $(_SWIG_LDFLAGS_$(GOOS)) + +_obj/$(SWIG_PREFIX)-%.so: _obj/%_wrapcxx.o + $(HOST_CXX) $(_CGO_CFLAGS_$(GOARCH)) -o $@ $^ $(SWIG_LDFLAGS) $(_CGO_LDFLAGS_$(GOOS)) $(_SWIG_LDFLAGS_$(GOOS)) + +$(pkgdir)/swig/$(SWIG_PREFIX)-%.so: _obj/$(SWIG_PREFIX)-%.so + @test -d $(QUOTED_GOROOT)/pkg && mkdir -p $(pkgdir)/swig + cp $< "$@" + +all: $(SWIG_SOS) + +SWIG_RPATH=-r $(pkgdir)/swig + +endif + # Generic build rules. # These come last so that the rules above can override them # for more specific file names. diff --git a/src/all-qemu.bash b/src/all-qemu.bash new file mode 100755 index 000000000..b2be15ac8 --- /dev/null +++ b/src/all-qemu.bash @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +# Copyright 2011 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +# Run all.bash but exclude tests that depend on functionality +# missing in QEMU's system call emulation. + +export DISABLE_NET_TESTS=1 # no external network +export NOTEST="" + +NOTEST="$NOTEST big" # xxx +NOTEST="$NOTEST http net rpc syslog websocket" # no localhost network +NOTEST="$NOTEST os" # 64-bit seek fails + +./all.bash diff --git a/src/clean.bash b/src/clean.bash index 7969e2cd0..1955b583b 100755 --- a/src/clean.bash +++ b/src/clean.bash @@ -21,7 +21,9 @@ fi rm -f "$GOROOT"/lib/*.a for i in lib9 libbio libmach cmd pkg \ ../misc/cgo/gmp ../misc/cgo/stdio \ + ../misc/cgo/life ../misc/cgo/test \ ../test/bench ../test/garbage do - gomake -C "$GOROOT/src/$i" clean + # Do not use gomake here. It may not be available. + $MAKE -C "$GOROOT/src/$i" clean done diff --git a/src/cmd/5c/peep.c b/src/cmd/5c/peep.c index 8945ee732..c15bf0fc4 100644 --- a/src/cmd/5c/peep.c +++ b/src/cmd/5c/peep.c @@ -1100,7 +1100,7 @@ copyu(Prog *p, Adr *v, Adr *s) if(v->type == D_REG) { if(v->reg <= REGEXT && v->reg > exregoffset) return 2; - if(v->reg == REGARG) + if(v->reg == (uchar)REGARG) return 2; } if(v->type == D_FREG) @@ -1118,7 +1118,7 @@ copyu(Prog *p, Adr *v, Adr *s) case ATEXT: /* funny */ if(v->type == D_REG) - if(v->reg == REGARG) + if(v->reg == (uchar)REGARG) return 3; return 0; } diff --git a/src/cmd/5c/txt.c b/src/cmd/5c/txt.c index f5619f800..4be1f6f62 100644 --- a/src/cmd/5c/txt.c +++ b/src/cmd/5c/txt.c @@ -400,6 +400,10 @@ regsalloc(Node *n, Node *nn) void regaalloc1(Node *n, Node *nn) { + if(REGARG < 0) { + fatal(n, "regaalloc1 and REGARG<0"); + return; + } nodreg(n, nn, REGARG); reg[REGARG]++; curarg = align(curarg, nn->type, Aarg1, nil); diff --git a/src/cmd/5g/ggen.c b/src/cmd/5g/ggen.c index 182d7f147..7197709d4 100644 --- a/src/cmd/5g/ggen.c +++ b/src/cmd/5g/ggen.c @@ -32,7 +32,7 @@ compile(Node *fn) return; // set up domain for labels - labellist = L; + clearlabels(); lno = setlineno(fn); diff --git a/src/cmd/5g/gobj.c b/src/cmd/5g/gobj.c index bf59534b9..acece6c0d 100644 --- a/src/cmd/5g/gobj.c +++ b/src/cmd/5g/gobj.c @@ -268,7 +268,7 @@ static Prog *estrdat; static int gflag; static Prog *savepc; -static void +void data(void) { gflag = debug['g']; @@ -285,7 +285,7 @@ data(void) pc = estrdat; } -static void +void text(void) { if(!savepc) @@ -310,6 +310,29 @@ dumpdata(void) pc = estrdat; } +int +dsname(Sym *sym, int off, char *t, int n) +{ + Prog *p; + + p = gins(ADATA, N, N); + p->from.type = D_OREG; + p->from.name = D_EXTERN; + p->from.etype = TINT32; + p->from.offset = off; + p->from.reg = NREG; + p->from.sym = sym; + + p->reg = n; + + p->to.type = D_SCONST; + p->to.name = D_NONE; + p->to.reg = NREG; + p->to.offset = 0; + memmove(p->to.sval, t, n); + return off + n; +} + /* * make a refer to the data s, s+len * emitting DATA if needed. @@ -317,76 +340,15 @@ dumpdata(void) void datastring(char *s, int len, Addr *a) { - int w; - Prog *p; - Addr ac, ao; - static int gen; - struct { - Strlit lit; - char buf[100]; - } tmp; - - // string - memset(&ao, 0, sizeof(ao)); - ao.type = D_OREG; - ao.name = D_STATIC; - ao.etype = TINT32; - ao.offset = 0; // fill in - ao.reg = NREG; - - // constant - memset(&ac, 0, sizeof(ac)); - ac.type = D_CONST; - ac.name = D_NONE; - ac.offset = 0; // fill in - ac.reg = NREG; - - // huge strings are made static to avoid long names. - if(len > 100) { - snprint(namebuf, sizeof(namebuf), ".string.%d", gen++); - ao.sym = lookup(namebuf); - ao.name = D_STATIC; - } else { - if(len > 0 && s[len-1] == '\0') - len--; - tmp.lit.len = len; - memmove(tmp.lit.s, s, len); - tmp.lit.s[len] = '\0'; - len++; - snprint(namebuf, sizeof(namebuf), "\"%Z\"", &tmp.lit); - ao.sym = pkglookup(namebuf, stringpkg); - ao.name = D_EXTERN; - } - *a = ao; - - // only generate data the first time. - if(ao.sym->flags & SymUniq) - return; - ao.sym->flags |= SymUniq; - - data(); - for(w=0; wfrom = ao; - p->from.offset = w; - - p->reg = NSNAME; - if(w+8 > len) - p->reg = len-w; - - p->to = ac; - p->to.type = D_SCONST; - p->to.offset = len; - memmove(p->to.sval, s+w, p->reg); - } - p = pc; - ggloblsym(ao.sym, len, ao.name == D_EXTERN); - if(ao.name == D_STATIC) - p->from.name = D_STATIC; - text(); + Sym *sym; + + sym = stringsym(s, len); + a->type = D_OREG; + a->name = D_EXTERN; + a->etype = TINT32; + a->offset = widthptr+4; // skip header + a->reg = NREG; + a->sym = sym; } /* @@ -396,77 +358,15 @@ datastring(char *s, int len, Addr *a) void datagostring(Strlit *sval, Addr *a) { - Prog *p; - Addr ac, ao, ap; - int32 wi, wp; - static int gen; - - memset(&ac, 0, sizeof(ac)); - memset(&ao, 0, sizeof(ao)); - memset(&ap, 0, sizeof(ap)); - - // constant - ac.type = D_CONST; - ac.name = D_NONE; - ac.offset = 0; // fill in - ac.reg = NREG; - - // string len+ptr - ao.type = D_OREG; - ao.name = D_STATIC; // fill in - ao.etype = TINT32; - ao.sym = nil; // fill in - ao.reg = NREG; - - // $string len+ptr - datastring(sval->s, sval->len, &ap); - ap.type = D_CONST; - ap.etype = TINT32; - - wi = types[TUINT32]->width; - wp = types[tptr]->width; - - if(ap.name == D_STATIC) { - // huge strings are made static to avoid long names - snprint(namebuf, sizeof(namebuf), ".gostring.%d", ++gen); - ao.sym = lookup(namebuf); - ao.name = D_STATIC; - } else { - // small strings get named by their contents, - // so that multiple modules using the same string - // can share it. - snprint(namebuf, sizeof(namebuf), "\"%Z\"", sval); - ao.sym = pkglookup(namebuf, gostringpkg); - ao.name = D_EXTERN; - } - - *a = ao; - if(ao.sym->flags & SymUniq) - return; - ao.sym->flags |= SymUniq; - - data(); - // DATA gostring, wp, $cstring - p = pc; - gins(ADATA, N, N); - p->from = ao; - p->reg = wp; - p->to = ap; - - // DATA gostring+wp, wi, $len - p = pc; - gins(ADATA, N, N); - p->from = ao; - p->from.offset = wp; - p->reg = wi; - p->to = ac; - p->to.offset = sval->len; - - p = pc; - ggloblsym(ao.sym, types[TSTRING]->width, ao.name == D_EXTERN); - if(ao.name == D_STATIC) - p->from.name = D_STATIC; - text(); + Sym *sym; + + sym = stringsym(sval->s, sval->len); + a->type = D_OREG; + a->name = D_EXTERN; + a->etype = TINT32; + a->offset = 0; // header + a->reg = NREG; + a->sym = sym; } void diff --git a/src/cmd/5l/asm.c b/src/cmd/5l/asm.c index af6d1dfda..e2583e7c3 100644 --- a/src/cmd/5l/asm.c +++ b/src/cmd/5l/asm.c @@ -1978,11 +1978,16 @@ genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) for(h=0; hhash) { + if(s->hide) + continue; switch(s->type) { case SCONST: case SRODATA: case SDATA: case SELFDATA: + case STYPE: + case SSTRING: + case SGOSTRING: if(!s->reachable) continue; put(s, s->name, 'D', s->value, s->size, s->version, s->gotype); diff --git a/src/cmd/5l/l.h b/src/cmd/5l/l.h index 2e887dad7..cf5a9990b 100644 --- a/src/cmd/5l/l.h +++ b/src/cmd/5l/l.h @@ -136,6 +136,7 @@ struct Sym uchar dynexport; uchar leaf; uchar stkcheck; + uchar hide; int32 dynid; int32 plt; int32 got; @@ -147,6 +148,7 @@ struct Sym uchar foreign; // called by arm if thumb, by thumb if arm uchar fnptr; // used as fn ptr Sym* hash; // in hash table + Sym* allsym; // in all symbol list Sym* next; // in text or data list Sym* sub; // in SSUB list Sym* outer; // container of sub @@ -202,22 +204,6 @@ struct Count enum { - Sxxx, - - /* order here is order in output file */ - STEXT = 1, - SRODATA, - SELFDATA, - SDATA, - SBSS, - - SXREF, - SFILE, - SCONST, - SDYNIMPORT, - - SSUB = 1<<8, - LFROM = 1<<0, LTO = 1<<1, LPOOL = 1<<2, @@ -280,7 +266,6 @@ enum LEAF = 1<<2, STRINGSZ = 200, - NHASH = 10007, MINSIZ = 64, NENT = 100, MAXIO = 8192, diff --git a/src/cmd/5l/noop.c b/src/cmd/5l/noop.c index a5e66f038..bdcc3cad8 100644 --- a/src/cmd/5l/noop.c +++ b/src/cmd/5l/noop.c @@ -364,14 +364,14 @@ noops(void) p = appendp(p); p->as = ABL; p->scond = C_SCOND_LO; - p->to.type = D_BRANCH; + p->to.type = D_BRANCH; p->to.sym = symmorestack; p->cond = pmorestack; // MOVW.W R14,$-autosize(SP) p = appendp(p); p->as = AMOVW; - p->scond |= C_WBIT; + p->scond |= C_WBIT; p->from.type = D_REG; p->from.reg = REGLINK; p->to.type = D_OREG; @@ -413,14 +413,14 @@ noops(void) // BL runtime.morestack(SB) // modifies LR p = appendp(p); p->as = ABL; - p->to.type = D_BRANCH; + p->to.type = D_BRANCH; p->to.sym = symmorestack; p->cond = pmorestack; // MOVW.W R14,$-autosize(SP) p = appendp(p); p->as = AMOVW; - p->scond |= C_WBIT; + p->scond |= C_WBIT; p->from.type = D_REG; p->from.reg = REGLINK; p->to.type = D_OREG; @@ -450,6 +450,8 @@ noops(void) } } if(thumb){ + diag("thumb not maintained"); + errorexit(); if(cursym->text->mark & LEAF){ if(autosize){ p->as = AADD; @@ -481,7 +483,7 @@ noops(void) q->to.type = D_REG; q->to.reg = REGSP; q->link = p->link; - p->link = q; + p->link = q; } else q = p; @@ -492,6 +494,8 @@ noops(void) break; } if(foreign) { + diag("foreign not maintained"); + errorexit(); // if(foreign) print("ABXRET 3 %s\n", cursym->name); #define R 1 p->as = AMOVW; @@ -530,7 +534,9 @@ noops(void) p->from.reg = REGSP; p->to.type = D_REG; p->to.reg = REGPC; - // no spadj because it doesn't fall through + // If there are instructions following + // this ARET, they come from a branch + // with the same stackframe, so no spadj. } break; diff --git a/src/cmd/5l/softfloat.c b/src/cmd/5l/softfloat.c index fd66b0969..03d8c6d26 100644 --- a/src/cmd/5l/softfloat.c +++ b/src/cmd/5l/softfloat.c @@ -4,6 +4,7 @@ #define EXTERN #include "l.h" +#include "../ld/lib.h" // Software floating point. diff --git a/src/cmd/5l/thumb.c b/src/cmd/5l/thumb.c index b2ba630c3..a6f729bed 100644 --- a/src/cmd/5l/thumb.c +++ b/src/cmd/5l/thumb.c @@ -29,6 +29,7 @@ // THE SOFTWARE. #include "l.h" +#include "../ld/lib.h" static int32 thumboprr(int); static int32 thumboprrr(int, int); diff --git a/src/cmd/6c/peep.c b/src/cmd/6c/peep.c index 13fd25e73..8b82adbf5 100644 --- a/src/cmd/6c/peep.c +++ b/src/cmd/6c/peep.c @@ -797,7 +797,7 @@ copyu(Prog *p, Adr *v, Adr *s) return 3; case ACALL: /* funny */ - if(REGARG >= 0 && v->type == REGARG) + if(REGARG >= 0 && v->type == (uchar)REGARG) return 2; if(s != A) { @@ -810,7 +810,7 @@ copyu(Prog *p, Adr *v, Adr *s) return 3; case ATEXT: /* funny */ - if(REGARG >= 0 && v->type == REGARG) + if(REGARG >= 0 && v->type == (uchar)REGARG) return 3; return 0; } diff --git a/src/cmd/6c/txt.c b/src/cmd/6c/txt.c index a78ba227b..12fc5b498 100644 --- a/src/cmd/6c/txt.c +++ b/src/cmd/6c/txt.c @@ -436,8 +436,10 @@ regsalloc(Node *n, Node *nn) void regaalloc1(Node *n, Node *nn) { - if(REGARG < 0) - diag(n, "regaalloc1 and REGARG<0"); + if(REGARG < 0) { + fatal(n, "regaalloc1 and REGARG<0"); + return; + } nodreg(n, nn, REGARG); reg[REGARG]++; curarg = align(curarg, nn->type, Aarg1, nil); diff --git a/src/cmd/6g/ggen.c b/src/cmd/6g/ggen.c index d9fa1793c..8d89fb164 100644 --- a/src/cmd/6g/ggen.c +++ b/src/cmd/6g/ggen.c @@ -32,7 +32,7 @@ compile(Node *fn) return; // set up domain for labels - labellist = L; + clearlabels(); lno = setlineno(fn); diff --git a/src/cmd/6g/gobj.c b/src/cmd/6g/gobj.c index b667ae48a..507764a3b 100644 --- a/src/cmd/6g/gobj.c +++ b/src/cmd/6g/gobj.c @@ -280,7 +280,7 @@ static Prog *estrdat; static int gflag; static Prog *savepc; -static void +void data(void) { gflag = debug['g']; @@ -297,7 +297,7 @@ data(void) pc = estrdat; } -static void +void text(void) { if(!savepc) @@ -322,6 +322,24 @@ dumpdata(void) pc = estrdat; } +int +dsname(Sym *s, int off, char *t, int n) +{ + Prog *p; + + p = gins(ADATA, N, N); + p->from.type = D_EXTERN; + p->from.index = D_NONE; + p->from.offset = off; + p->from.scale = n; + p->from.sym = s; + + p->to.type = D_SCONST; + p->to.index = D_NONE; + memmove(p->to.sval, t, n); + return off + n; +} + /* * make a refer to the data s, s+len * emitting DATA if needed. @@ -329,74 +347,13 @@ dumpdata(void) void datastring(char *s, int len, Addr *a) { - int w; - Prog *p; - Addr ac, ao; - static int gen; - struct { - Strlit lit; - char buf[100]; - } tmp; - - // string - memset(&ao, 0, sizeof(ao)); - ao.type = D_STATIC; - ao.index = D_NONE; - ao.etype = TINT32; - ao.offset = 0; // fill in - - // constant - memset(&ac, 0, sizeof(ac)); - ac.type = D_CONST; - ac.index = D_NONE; - ac.offset = 0; // fill in - - // huge strings are made static to avoid long names. - if(len > 100) { - snprint(namebuf, sizeof(namebuf), ".string.%d", gen++); - ao.sym = lookup(namebuf); - ao.type = D_STATIC; - } else { - if(len > 0 && s[len-1] == '\0') - len--; - tmp.lit.len = len; - memmove(tmp.lit.s, s, len); - tmp.lit.s[len] = '\0'; - len++; - snprint(namebuf, sizeof(namebuf), "\"%Z\"", &tmp.lit); - ao.sym = pkglookup(namebuf, stringpkg); - ao.type = D_EXTERN; - } - *a = ao; - - // only generate data the first time. - if(ao.sym->flags & SymUniq) - return; - ao.sym->flags |= SymUniq; - - data(); - for(w=0; wfrom = ao; - p->from.offset = w; - - p->from.scale = NSNAME; - if(w+8 > len) - p->from.scale = len-w; - - p->to = ac; - p->to.type = D_SCONST; - p->to.offset = len; - memmove(p->to.sval, s+w, p->from.scale); - } - p = pc; - ggloblsym(ao.sym, len, ao.type == D_EXTERN); - if(ao.type == D_STATIC) - p->from.type = D_STATIC; - text(); + Sym *sym; + + sym = stringsym(s, len); + a->type = D_EXTERN; + a->sym = sym; + a->offset = widthptr+4; // skip header + a->etype = TINT32; } /* @@ -406,76 +363,13 @@ datastring(char *s, int len, Addr *a) void datagostring(Strlit *sval, Addr *a) { - Prog *p; - Addr ac, ao, ap; - int32 wi, wp; - static int gen; - - memset(&ac, 0, sizeof(ac)); - memset(&ao, 0, sizeof(ao)); - memset(&ap, 0, sizeof(ap)); - - // constant - ac.type = D_CONST; - ac.index = D_NONE; - ac.offset = 0; // fill in - - // string len+ptr - ao.type = D_STATIC; // fill in - ao.index = D_NONE; - ao.etype = TINT32; - ao.sym = nil; // fill in - - // $string len+ptr - datastring(sval->s, sval->len, &ap); - ap.index = ap.type; - ap.type = D_ADDR; - ap.etype = TINT32; - - wi = types[TUINT32]->width; - wp = types[tptr]->width; - - if(ap.index == D_STATIC) { - // huge strings are made static to avoid long names - snprint(namebuf, sizeof(namebuf), ".gostring.%d", ++gen); - ao.sym = lookup(namebuf); - ao.type = D_STATIC; - } else { - // small strings get named by their contents, - // so that multiple modules using the same string - // can share it. - snprint(namebuf, sizeof(namebuf), "\"%Z\"", sval); - ao.sym = pkglookup(namebuf, gostringpkg); - ao.type = D_EXTERN; - } - - *a = ao; - if(ao.sym->flags & SymUniq) - return; - ao.sym->flags |= SymUniq; - - data(); - // DATA gostring, wp, $cstring - p = pc; - gins(ADATA, N, N); - p->from = ao; - p->from.scale = wp; - p->to = ap; - - // DATA gostring+wp, wi, $len - p = pc; - gins(ADATA, N, N); - p->from = ao; - p->from.offset = wp; - p->from.scale = wi; - p->to = ac; - p->to.offset = sval->len; - - p = pc; - ggloblsym(ao.sym, types[TSTRING]->width, ao.type == D_EXTERN); - if(ao.type == D_STATIC) - p->from.type = D_STATIC; - text(); + Sym *sym; + + sym = stringsym(sval->s, sval->len); + a->type = D_EXTERN; + a->sym = sym; + a->offset = 0; // header + a->etype = TINT32; } void diff --git a/src/cmd/6l/asm.c b/src/cmd/6l/asm.c index fb041d83a..ba2074fde 100644 --- a/src/cmd/6l/asm.c +++ b/src/cmd/6l/asm.c @@ -1101,32 +1101,34 @@ genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) { Auto *a; Sym *s; - int h; - for(h=0; hhash) { - switch(s->type&~SSUB) { - case SCONST: - case SRODATA: - case SDATA: - case SELFDATA: - case SMACHOGOT: - case SWINDOWS: - if(!s->reachable) - continue; - put(s, s->name, 'D', symaddr(s), s->size, s->version, s->gotype); + for(s=allsym; s!=S; s=s->allsym) { + if(s->hide) + continue; + switch(s->type&~SSUB) { + case SCONST: + case SRODATA: + case SDATA: + case SELFDATA: + case SMACHOGOT: + case STYPE: + case SSTRING: + case SGOSTRING: + case SWINDOWS: + if(!s->reachable) continue; + put(s, s->name, 'D', symaddr(s), s->size, s->version, s->gotype); + continue; - case SBSS: - if(!s->reachable) - continue; - put(s, s->name, 'B', symaddr(s), s->size, s->version, s->gotype); + case SBSS: + if(!s->reachable) continue; + put(s, s->name, 'B', symaddr(s), s->size, s->version, s->gotype); + continue; - case SFILE: - put(nil, s->name, 'f', s->value, 0, s->version, 0); - continue; - } + case SFILE: + put(nil, s->name, 'f', s->value, 0, s->version, 0); + continue; } } diff --git a/src/cmd/6l/l.h b/src/cmd/6l/l.h index 6933d8eb1..4fc13b94a 100644 --- a/src/cmd/6l/l.h +++ b/src/cmd/6l/l.h @@ -132,11 +132,13 @@ struct Sym uchar dynexport; uchar special; uchar stkcheck; + uchar hide; int32 dynid; int32 sig; int32 plt; int32 got; Sym* hash; // in hash table + Sym* allsym; // in all symbol list Sym* next; // in text or data list Sym* sub; // in SSUB list Sym* outer; // container of sub @@ -177,29 +179,6 @@ struct Movtab enum { - Sxxx, - - /* order here is order in output file */ - STEXT = 1, - SELFDATA, - SMACHOPLT, - SRODATA, - SDATA, - SMACHOGOT, - SWINDOWS, - SBSS, - - SXREF, - SMACHODYNSTR, - SMACHODYNSYM, - SMACHOINDIRECTPLT, - SMACHOINDIRECTGOT, - SFILE, - SCONST, - SDYNIMPORT, - SSUB = 1<<8, - - NHASH = 10007, MINSIZ = 8, STRINGSZ = 200, MINLC = 1, diff --git a/src/cmd/6l/obj.c b/src/cmd/6l/obj.c index f113e3ec1..6b43d2df4 100644 --- a/src/cmd/6l/obj.c +++ b/src/cmd/6l/obj.c @@ -287,7 +287,7 @@ zsym(char *pn, Biobuf *f, Sym *h[]) { int o; - o = Bgetc(f); + o = BGETC(f); if(o < 0 || o >= NSYM || h[o] == nil) mangle(pn); return h[o]; @@ -301,12 +301,12 @@ zaddr(char *pn, Biobuf *f, Adr *a, Sym *h[]) Sym *s; Auto *u; - t = Bgetc(f); + t = BGETC(f); a->index = D_NONE; a->scale = 0; if(t & T_INDEX) { - a->index = Bgetc(f); - a->scale = Bgetc(f); + a->index = BGETC(f); + a->scale = BGETC(f); } a->offset = 0; if(t & T_OFFSET) { @@ -330,7 +330,7 @@ zaddr(char *pn, Biobuf *f, Adr *a, Sym *h[]) a->type = D_SCONST; } if(t & T_TYPE) - a->type = Bgetc(f); + a->type = BGETC(f); if(a->type < 0 || a->type >= D_SIZE) mangle(pn); adrgotype = S; @@ -405,10 +405,10 @@ newloop: loop: if(f->state == Bracteof || Boffset(f) >= eof) goto eof; - o = Bgetc(f); + o = BGETC(f); if(o == Beof) goto eof; - o |= Bgetc(f) << 8; + o |= BGETC(f) << 8; if(o <= AXXX || o >= ALAST) { if(o < 0) goto eof; @@ -421,8 +421,8 @@ loop: sig = 0; if(o == ASIGNAME) sig = Bget4(f); - v = Bgetc(f); /* type */ - o = Bgetc(f); /* sym */ + v = BGETC(f); /* type */ + o = BGETC(f); /* sym */ r = 0; if(v == D_STATIC) r = version; diff --git a/src/cmd/6l/pass.c b/src/cmd/6l/pass.c index 8fda94392..0b0ee1253 100644 --- a/src/cmd/6l/pass.c +++ b/src/cmd/6l/pass.c @@ -274,10 +274,10 @@ patch(void) if(HEADTYPE == Hwindows) { // Windows // Convert - // op n(GS), reg + // op n(GS), reg // to // MOVL 0x58(GS), reg - // op n(reg), reg + // op n(reg), reg // The purpose of this patch is to fix some accesses // to extern register variables (TLS) on Windows, as // a different method is used to access them. @@ -674,6 +674,11 @@ dostkoff(void) p->spadj = -autoffset; p = appendp(p); p->as = ARET; + // If there are instructions following + // this ARET, they come from a branch + // with the same stackframe, so undo + // the cleanup. + p->spadj = +autoffset; } } } diff --git a/src/cmd/8c/peep.c b/src/cmd/8c/peep.c index 9e18fc94d..9511a5579 100644 --- a/src/cmd/8c/peep.c +++ b/src/cmd/8c/peep.c @@ -713,7 +713,7 @@ copyu(Prog *p, Adr *v, Adr *s) return 3; case ACALL: /* funny */ - if(REGARG >= 0 && v->type == REGARG) + if(REGARG >= 0 && v->type == (uchar)REGARG) return 2; if(s != A) { diff --git a/src/cmd/8c/txt.c b/src/cmd/8c/txt.c index 0dd387d11..b2e0148a0 100644 --- a/src/cmd/8c/txt.c +++ b/src/cmd/8c/txt.c @@ -397,6 +397,10 @@ regsalloc(Node *n, Node *nn) void regaalloc1(Node *n, Node *nn) { + if(REGARG < 0) { + fatal(n, "regaalloc1 and REGARG<0"); + return; + } nodreg(n, nn, REGARG); reg[REGARG]++; curarg = align(curarg, nn->type, Aarg1, nil); diff --git a/src/cmd/8g/ggen.c b/src/cmd/8g/ggen.c index 4dcbd4489..8db552493 100644 --- a/src/cmd/8g/ggen.c +++ b/src/cmd/8g/ggen.c @@ -32,7 +32,7 @@ compile(Node *fn) return; // set up domain for labels - labellist = L; + clearlabels(); lno = setlineno(fn); diff --git a/src/cmd/8g/gobj.c b/src/cmd/8g/gobj.c index e48ad529b..bc1dfe8bf 100644 --- a/src/cmd/8g/gobj.c +++ b/src/cmd/8g/gobj.c @@ -320,6 +320,24 @@ dumpdata(void) pc = estrdat; } +int +dsname(Sym *s, int off, char *t, int n) +{ + Prog *p; + + p = gins(ADATA, N, N); + p->from.type = D_EXTERN; + p->from.index = D_NONE; + p->from.offset = off; + p->from.scale = n; + p->from.sym = s; + + p->to.type = D_SCONST; + p->to.index = D_NONE; + memmove(p->to.sval, t, n); + return off + n; +} + /* * make a refer to the data s, s+len * emitting DATA if needed. @@ -327,74 +345,13 @@ dumpdata(void) void datastring(char *s, int len, Addr *a) { - int w; - Prog *p; - Addr ac, ao; - static int gen; - struct { - Strlit lit; - char buf[100]; - } tmp; - - // string - memset(&ao, 0, sizeof(ao)); - ao.type = D_STATIC; - ao.index = D_NONE; - ao.etype = TINT32; - ao.offset = 0; // fill in - - // constant - memset(&ac, 0, sizeof(ac)); - ac.type = D_CONST; - ac.index = D_NONE; - ac.offset = 0; // fill in - - // huge strings are made static to avoid long names. - if(len > 100) { - snprint(namebuf, sizeof(namebuf), ".string.%d", gen++); - ao.sym = lookup(namebuf); - ao.type = D_STATIC; - } else { - if(len > 0 && s[len-1] == '\0') - len--; - tmp.lit.len = len; - memmove(tmp.lit.s, s, len); - tmp.lit.s[len] = '\0'; - len++; - snprint(namebuf, sizeof(namebuf), "\"%Z\"", &tmp.lit); - ao.sym = pkglookup(namebuf, stringpkg); - ao.type = D_EXTERN; - } - *a = ao; - - // only generate data the first time. - if(ao.sym->flags & SymUniq) - return; - ao.sym->flags |= SymUniq; - - data(); - for(w=0; wfrom = ao; - p->from.offset = w; - - p->from.scale = NSNAME; - if(w+8 > len) - p->from.scale = len-w; - - p->to = ac; - p->to.type = D_SCONST; - p->to.offset = len; - memmove(p->to.sval, s+w, p->from.scale); - } - p = pc; - ggloblsym(ao.sym, len, ao.type == D_EXTERN); - if(ao.type == D_STATIC) - p->from.type = D_STATIC; - text(); + Sym *sym; + + sym = stringsym(s, len); + a->type = D_EXTERN; + a->sym = sym; + a->offset = widthptr+4; // skip header + a->etype = TINT32; } /* @@ -404,76 +361,13 @@ datastring(char *s, int len, Addr *a) void datagostring(Strlit *sval, Addr *a) { - Prog *p; - Addr ac, ao, ap; - int32 wi, wp; - static int gen; - - memset(&ac, 0, sizeof(ac)); - memset(&ao, 0, sizeof(ao)); - memset(&ap, 0, sizeof(ap)); - - // constant - ac.type = D_CONST; - ac.index = D_NONE; - ac.offset = 0; // fill in - - // string len+ptr - ao.type = D_STATIC; // fill in - ao.index = D_NONE; - ao.etype = TINT32; - ao.sym = nil; // fill in - - // $string len+ptr - datastring(sval->s, sval->len, &ap); - ap.index = ap.type; - ap.type = D_ADDR; - ap.etype = TINT32; - - wi = types[TUINT32]->width; - wp = types[tptr]->width; - - if(ap.index == D_STATIC) { - // huge strings are made static to avoid long names - snprint(namebuf, sizeof(namebuf), ".gostring.%d", ++gen); - ao.sym = lookup(namebuf); - ao.type = D_STATIC; - } else { - // small strings get named by their contents, - // so that multiple modules using the same string - // can share it. - snprint(namebuf, sizeof(namebuf), "\"%Z\"", sval); - ao.sym = pkglookup(namebuf, gostringpkg); - ao.type = D_EXTERN; - } - - *a = ao; - if(ao.sym->flags & SymUniq) - return; - ao.sym->flags |= SymUniq; - - data(); - // DATA gostring, wp, $cstring - p = pc; - gins(ADATA, N, N); - p->from = ao; - p->from.scale = wp; - p->to = ap; - - // DATA gostring+wp, wi, $len - p = pc; - gins(ADATA, N, N); - p->from = ao; - p->from.offset = wp; - p->from.scale = wi; - p->to = ac; - p->to.offset = sval->len; - - p = pc; - ggloblsym(ao.sym, types[TSTRING]->width, ao.type == D_EXTERN); - if(ao.type == D_STATIC) - p->from.type = D_STATIC; - text(); + Sym *sym; + + sym = stringsym(sval->s, sval->len); + a->type = D_EXTERN; + a->sym = sym; + a->offset = 0; // header + a->etype = TINT32; } void diff --git a/src/cmd/8g/peep.c b/src/cmd/8g/peep.c index 580b1a922..5ad29e1b2 100644 --- a/src/cmd/8g/peep.c +++ b/src/cmd/8g/peep.c @@ -120,6 +120,25 @@ peep(void) p = p->link; } } + + // movb elimination. + // movb is simulated by the linker + // when a register other than ax, bx, cx, dx + // is used, so rewrite to other instructions + // when possible. a movb into a register + // can smash the entire 32-bit register without + // causing any trouble. + for(r=firstr; r!=R; r=r->link) { + p = r->prog; + if(p->as == AMOVB && regtyp(&p->to)) { + // movb into register. + // from another register or constant can be movl. + if(regtyp(&p->from) || p->from.type == D_CONST) + p->as = AMOVL; + else + p->as = AMOVBLZX; + } + } // constant propagation // find MOV $con,R followed by @@ -152,6 +171,8 @@ loop1: for(r=firstr; r!=R; r=r->link) { p = r->prog; switch(p->as) { + case AMOVB: + case AMOVW: case AMOVL: if(regtyp(&p->to)) if(regtyp(&p->from)) { @@ -182,6 +203,7 @@ loop1: } break; + case AADDB: case AADDL: case AADDW: if(p->from.type != D_CONST || needc(p->link)) @@ -204,6 +226,7 @@ loop1: } break; + case ASUBB: case ASUBL: case ASUBW: if(p->from.type != D_CONST || needc(p->link)) @@ -380,6 +403,8 @@ subprop(Reg *r0) case AMOVSL: return 0; + case AMOVB: + case AMOVW: case AMOVL: if(p->to.type == v1->type) goto gotit; @@ -560,6 +585,8 @@ copyu(Prog *p, Adr *v, Adr *s) case ANOP: /* rhs store */ + case AMOVB: + case AMOVW: case AMOVL: case AMOVBLSX: case AMOVBLZX: @@ -624,8 +651,6 @@ copyu(Prog *p, Adr *v, Adr *s) case AXORB: case AXORL: case AXORW: - case AMOVB: - case AMOVW: if(copyas(&p->to, v)) return 2; goto caseread; diff --git a/src/cmd/8l/asm.c b/src/cmd/8l/asm.c index 1e760d89e..b9bd0dae9 100644 --- a/src/cmd/8l/asm.c +++ b/src/cmd/8l/asm.c @@ -307,6 +307,7 @@ elfsetupplt(void) int archreloc(Reloc *r, Sym *s, vlong *val) { + USED(s); switch(r->type) { case D_CONST: *val = r->add; @@ -644,7 +645,7 @@ asmb(void) { int32 v, magic; int a, dynsym; - uint32 va, fo, w, symo, startva, machlink; + uint32 symo, startva, machlink; ElfEhdr *eh; ElfPhdr *ph, *pph; ElfShdr *sh; @@ -776,7 +777,6 @@ asmb(void) lputb(0L); lputb(~0L); /* gp value ?? */ break; - lputl(0); /* x */ case Hunixcoff: /* unix coff */ /* * file header @@ -892,13 +892,10 @@ asmb(void) debug['d'] = 1; eh = getElfEhdr(); - fo = HEADR; startva = INITTEXT - HEADR; - va = startva + fo; - w = segtext.filelen; /* This null SHdr must appear before all others */ - sh = newElfShdr(elfstr[ElfStrEmpty]); + newElfShdr(elfstr[ElfStrEmpty]); /* program header info */ pph = newElfPhdr(); @@ -1158,6 +1155,8 @@ genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) for(h=0; hhash) { + if(s->hide) + continue; switch(s->type&~SSUB) { case SCONST: case SRODATA: @@ -1165,6 +1164,9 @@ genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) case SELFDATA: case SMACHO: case SMACHOGOT: + case STYPE: + case SSTRING: + case SGOSTRING: case SWINDOWS: if(!s->reachable) continue; @@ -1209,6 +1211,6 @@ genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) put(nil, a->asym->name, 'p', a->aoffset, 0, 0, a->gotype); } if(debug['v'] || debug['n']) - Bprint(&bso, "symsize = %ud\n", symsize); + Bprint(&bso, "symsize = %uld\n", symsize); Bflush(&bso); } diff --git a/src/cmd/8l/l.h b/src/cmd/8l/l.h index e4650ee58..ac0f3953f 100644 --- a/src/cmd/8l/l.h +++ b/src/cmd/8l/l.h @@ -131,6 +131,7 @@ struct Sym uchar dynexport; uchar special; uchar stkcheck; + uchar hide; int32 value; int32 size; int32 sig; @@ -138,6 +139,7 @@ struct Sym int32 plt; int32 got; Sym* hash; // in hash table + Sym* allsym; // in all symbol list Sym* next; // in text or data list Sym* sub; // in sub list Sym* outer; // container of sub @@ -168,31 +170,6 @@ struct Optab enum { - Sxxx, - - /* order here is order in output file */ - STEXT, - SELFDATA, - SMACHOPLT, - SRODATA, - SDATA, - SMACHO, /* Mach-O __nl_symbol_ptr */ - SMACHOGOT, - SWINDOWS, - SBSS, - - SXREF, - SMACHODYNSTR, - SMACHODYNSYM, - SMACHOINDIRECTPLT, - SMACHOINDIRECTGOT, - SFILE, - SCONST, - SDYNIMPORT, - - SSUB = 1<<8, /* sub-symbol, linked from parent via ->sub list */ - - NHASH = 10007, MINSIZ = 4, STRINGSZ = 200, MINLC = 1, diff --git a/src/cmd/8l/pass.c b/src/cmd/8l/pass.c index 294926f29..28589b66a 100644 --- a/src/cmd/8l/pass.c +++ b/src/cmd/8l/pass.c @@ -614,14 +614,17 @@ dostkoff(void) diag("unbalanced PUSH/POP"); if(autoffset) { - q = p; + p->as = AADJSP; + p->from.type = D_CONST; + p->from.offset = -autoffset; + p->spadj = -autoffset; p = appendp(p); p->as = ARET; - - q->as = AADJSP; - q->from.type = D_CONST; - q->from.offset = -autoffset; - p->spadj = -autoffset; + // If there are instructions following + // this ARET, they come from a branch + // with the same stackframe, so undo + // the cleanup. + p->spadj = +autoffset; } } } diff --git a/src/cmd/Makefile b/src/cmd/Makefile index 104e9f5df..5a37733de 100644 --- a/src/cmd/Makefile +++ b/src/cmd/Makefile @@ -18,7 +18,7 @@ DIRS=\ gc\ godefs\ gopack\ - gotest\ + gotry\ nm\ prof\ @@ -41,8 +41,11 @@ CLEANDIRS=\ cgo\ ebnflint\ godoc\ + gofix\ gofmt\ goinstall\ + gotest\ + gotype\ goyacc\ hgpatch\ diff --git a/src/cmd/cgo/ast.go b/src/cmd/cgo/ast.go index 2eae22aed..46e33686d 100644 --- a/src/cmd/cgo/ast.go +++ b/src/cmd/cgo/ast.go @@ -30,7 +30,7 @@ func parse(name string, flags uint) *ast.File { } os.Exit(2) } - fatal("parsing %s: %s", name, err) + fatalf("parsing %s: %s", name, err) } return ast1 } @@ -180,7 +180,7 @@ func (f *File) saveExport(x interface{}, context string) { return } for _, c := range n.Doc.List { - if string(c.Text[0:9]) != "//export " { + if !strings.HasPrefix(string(c.Text), "//export ") { continue } @@ -325,26 +325,28 @@ func (f *File) walk(x interface{}, context string, visit func(*File, interface{} f.walk(n.Results, "expr", visit) case *ast.BranchStmt: case *ast.BlockStmt: - f.walk(n.List, "stmt", visit) + f.walk(n.List, context, visit) case *ast.IfStmt: f.walk(n.Init, "stmt", visit) f.walk(&n.Cond, "expr", visit) f.walk(n.Body, "stmt", visit) f.walk(n.Else, "stmt", visit) case *ast.CaseClause: - f.walk(n.Values, "expr", visit) + if context == "typeswitch" { + context = "type" + } else { + context = "expr" + } + f.walk(n.List, context, visit) f.walk(n.Body, "stmt", visit) case *ast.SwitchStmt: f.walk(n.Init, "stmt", visit) f.walk(&n.Tag, "expr", visit) - f.walk(n.Body, "stmt", visit) - case *ast.TypeCaseClause: - f.walk(n.Types, "type", visit) - f.walk(n.Body, "stmt", visit) + f.walk(n.Body, "switch", visit) case *ast.TypeSwitchStmt: f.walk(n.Init, "stmt", visit) f.walk(n.Assign, "stmt", visit) - f.walk(n.Body, "stmt", visit) + f.walk(n.Body, "typeswitch", visit) case *ast.CommClause: f.walk(n.Comm, "stmt", visit) f.walk(n.Body, "stmt", visit) diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go index f7ecc9e14..ae5ca2c7d 100644 --- a/src/cmd/cgo/gcc.go +++ b/src/cmd/cgo/gcc.go @@ -79,7 +79,7 @@ NextLine: l = strings.TrimSpace(l[4:]) fields := strings.Split(l, ":", 2) if len(fields) != 2 { - fatal("%s: bad #cgo line: %s", srcfile, line) + fatalf("%s: bad #cgo line: %s", srcfile, line) } var k string @@ -97,17 +97,17 @@ NextLine: continue NextLine } default: - fatal("%s: bad #cgo option: %s", srcfile, fields[0]) + fatalf("%s: bad #cgo option: %s", srcfile, fields[0]) } if k != "CFLAGS" && k != "LDFLAGS" { - fatal("%s: unsupported #cgo option %s", srcfile, k) + fatalf("%s: unsupported #cgo option %s", srcfile, k) } v := strings.TrimSpace(fields[1]) args, err := splitQuoted(v) if err != nil { - fatal("%s: bad #cgo option %s: %s", srcfile, k, err.String()) + fatalf("%s: bad #cgo option %s: %s", srcfile, k, err.String()) } if oldv, ok := p.CgoFlags[k]; ok { p.CgoFlags[k] = oldv + " " + v @@ -317,7 +317,7 @@ func (p *Package) guessKinds(f *File) []*Name { b.WriteString("}\n") stderr := p.gccErrors(b.Bytes()) if stderr == "" { - fatal("gcc produced no output\non input:\n%s", b.Bytes()) + fatalf("gcc produced no output\non input:\n%s", b.Bytes()) } names := make([]*Name, len(toSniff)) @@ -383,7 +383,7 @@ func (p *Package) guessKinds(f *File) []*Name { error(token.NoPos, "could not determine kind of name for C.%s", n.Go) } if nerrors > 0 { - fatal("unresolved names") + fatalf("unresolved names") } return needType } @@ -422,7 +422,7 @@ func (p *Package) loadDWARF(f *File, names []*Name) { for { e, err := r.Next() if err != nil { - fatal("reading DWARF entry: %s", err) + fatalf("reading DWARF entry: %s", err) } if e == nil { break @@ -433,7 +433,7 @@ func (p *Package) loadDWARF(f *File, names []*Name) { for { e, err := r.Next() if err != nil { - fatal("reading DWARF entry: %s", err) + fatalf("reading DWARF entry: %s", err) } if e.Tag == 0 { break @@ -452,27 +452,27 @@ func (p *Package) loadDWARF(f *File, names []*Name) { name, _ := e.Val(dwarf.AttrName).(string) typOff, _ := e.Val(dwarf.AttrType).(dwarf.Offset) if name == "" || typOff == 0 { - fatal("malformed DWARF TagVariable entry") + fatalf("malformed DWARF TagVariable entry") } if !strings.HasPrefix(name, "__cgo__") { break } typ, err := d.Type(typOff) if err != nil { - fatal("loading DWARF type: %s", err) + fatalf("loading DWARF type: %s", err) } t, ok := typ.(*dwarf.PtrType) if !ok || t == nil { - fatal("internal error: %s has non-pointer type", name) + fatalf("internal error: %s has non-pointer type", name) } i, err := strconv.Atoi(name[7:]) if err != nil { - fatal("malformed __cgo__ name: %s", name) + fatalf("malformed __cgo__ name: %s", name) } if enums[i] != 0 { t, err := d.Type(enums[i]) if err != nil { - fatal("loading DWARF type: %s", err) + fatalf("loading DWARF type: %s", err) } types[i] = t } else { @@ -632,14 +632,14 @@ func (p *Package) gccDebug(stdin []byte) *dwarf.Data { if f, err = elf.Open(gccTmp); err != nil { if f, err = macho.Open(gccTmp); err != nil { if f, err = pe.Open(gccTmp); err != nil { - fatal("cannot parse gcc output %s as ELF or Mach-O or PE object", gccTmp) + fatalf("cannot parse gcc output %s as ELF or Mach-O or PE object", gccTmp) } } } d, err := f.DWARF() if err != nil { - fatal("cannot load DWARF debug information from %s: %s", gccTmp, err) + fatalf("cannot load DWARF debug information from %s: %s", gccTmp, err) } return d } @@ -807,7 +807,7 @@ func (tr *TypeRepr) Set(repr string, fargs ...interface{}) { func (c *typeConv) Type(dtype dwarf.Type) *Type { if t, ok := c.m[dtype]; ok { if t.Go == nil { - fatal("type conversion loop at %s", dtype) + fatalf("type conversion loop at %s", dtype) } return t } @@ -830,11 +830,11 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { switch dt := dtype.(type) { default: - fatal("unexpected type: %s", dtype) + fatalf("unexpected type: %s", dtype) case *dwarf.AddrType: if t.Size != c.ptrSize { - fatal("unexpected: %d-byte address type - %s", t.Size, dtype) + fatalf("unexpected: %d-byte address type - %s", t.Size, dtype) } t.Go = c.uintptr t.Align = t.Size @@ -860,7 +860,7 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { case *dwarf.CharType: if t.Size != 1 { - fatal("unexpected: %d-byte char type - %s", t.Size, dtype) + fatalf("unexpected: %d-byte char type - %s", t.Size, dtype) } t.Go = c.int8 t.Align = 1 @@ -880,7 +880,7 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { } switch t.Size + int64(signed) { default: - fatal("unexpected: %d-byte enum type - %s", t.Size, dtype) + fatalf("unexpected: %d-byte enum type - %s", t.Size, dtype) case 1: t.Go = c.uint8 case 2: @@ -902,7 +902,7 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { case *dwarf.FloatType: switch t.Size { default: - fatal("unexpected: %d-byte float type - %s", t.Size, dtype) + fatalf("unexpected: %d-byte float type - %s", t.Size, dtype) case 4: t.Go = c.float32 case 8: @@ -915,7 +915,7 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { case *dwarf.ComplexType: switch t.Size { default: - fatal("unexpected: %d-byte complex type - %s", t.Size, dtype) + fatalf("unexpected: %d-byte complex type - %s", t.Size, dtype) case 8: t.Go = c.complex64 case 16: @@ -933,11 +933,11 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { case *dwarf.IntType: if dt.BitSize > 0 { - fatal("unexpected: %d-bit int type - %s", dt.BitSize, dtype) + fatalf("unexpected: %d-bit int type - %s", dt.BitSize, dtype) } switch t.Size { default: - fatal("unexpected: %d-byte int type - %s", t.Size, dtype) + fatalf("unexpected: %d-byte int type - %s", t.Size, dtype) case 1: t.Go = c.int8 case 2: @@ -1022,18 +1022,18 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { case *dwarf.UcharType: if t.Size != 1 { - fatal("unexpected: %d-byte uchar type - %s", t.Size, dtype) + fatalf("unexpected: %d-byte uchar type - %s", t.Size, dtype) } t.Go = c.uint8 t.Align = 1 case *dwarf.UintType: if dt.BitSize > 0 { - fatal("unexpected: %d-bit uint type - %s", dt.BitSize, dtype) + fatalf("unexpected: %d-bit uint type - %s", dt.BitSize, dtype) } switch t.Size { default: - fatal("unexpected: %d-byte uint type - %s", t.Size, dtype) + fatalf("unexpected: %d-byte uint type - %s", t.Size, dtype) case 1: t.Go = c.uint8 case 2: @@ -1067,7 +1067,7 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { } if t.C.Empty() { - fatal("internal error: did not create C name for %s", dtype) + fatalf("internal error: did not create C name for %s", dtype) } return t @@ -1156,7 +1156,7 @@ func (c *typeConv) Opaque(n int64) ast.Expr { func (c *typeConv) intExpr(n int64) ast.Expr { return &ast.BasicLit{ Kind: token.INT, - Value: []byte(strconv.Itoa64(n)), + Value: strconv.Itoa64(n), } } @@ -1229,7 +1229,7 @@ func (c *typeConv) Struct(dt *dwarf.StructType) (expr *ast.StructType, csyntax s off = dt.ByteSize } if off != dt.ByteSize { - fatal("struct size calculation error") + fatalf("struct size calculation error") } buf.WriteString("}") csyntax = buf.String() diff --git a/src/cmd/cgo/main.go b/src/cmd/cgo/main.go index 2dc662de5..00ffc4506 100644 --- a/src/cmd/cgo/main.go +++ b/src/cmd/cgo/main.go @@ -177,11 +177,11 @@ func main() { arch := os.Getenv("GOARCH") if arch == "" { - fatal("$GOARCH is not set") + fatalf("$GOARCH is not set") } ptrSize := ptrSizeMap[arch] if ptrSize == 0 { - fatal("unknown $GOARCH %q", arch) + fatalf("unknown $GOARCH %q", arch) } // Clear locale variables so gcc emits English errors [sic]. @@ -203,9 +203,9 @@ func main() { // Use the beginning of the md5 of the input to disambiguate. h := md5.New() for _, input := range goFiles { - f, err := os.Open(input, os.O_RDONLY, 0) + f, err := os.Open(input) if err != nil { - fatal("%s", err) + fatalf("%s", err) } io.Copy(h, f) f.Close() diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index 4a5fa6a73..abf8c8bc2 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -105,7 +105,7 @@ func dynimport(obj string) (syms, imports []string) { if f, err1 = elf.Open(obj); err1 != nil { if f, err2 = pe.Open(obj); err2 != nil { if f, err3 = macho.Open(obj); err3 != nil { - fatal("cannot parse %s as ELF (%v) or PE (%v) or Mach-O (%v)", obj, err1, err2, err3) + fatalf("cannot parse %s as ELF (%v) or PE (%v) or Mach-O (%v)", obj, err1, err2, err3) } isMacho = true } @@ -114,7 +114,7 @@ func dynimport(obj string) (syms, imports []string) { var err os.Error syms, err = f.ImportedSymbols() if err != nil { - fatal("cannot load dynamic symbols: %v", err) + fatalf("cannot load dynamic symbols: %v", err) } if isMacho { // remove leading _ that OS X insists on @@ -127,7 +127,7 @@ func dynimport(obj string) (syms, imports []string) { imports, err = f.ImportedLibraries() if err != nil { - fatal("cannot load dynamic imports: %v", err) + fatalf("cannot load dynamic imports: %v", err) } return diff --git a/src/cmd/cgo/util.go b/src/cmd/cgo/util.go index 59529a6d2..1ca24103e 100644 --- a/src/cmd/cgo/util.go +++ b/src/cmd/cgo/util.go @@ -18,23 +18,23 @@ import ( func run(stdin []byte, argv []string) (stdout, stderr []byte, ok bool) { cmd, err := exec.LookPath(argv[0]) if err != nil { - fatal("exec %s: %s", argv[0], err) + fatalf("exec %s: %s", argv[0], err) } r0, w0, err := os.Pipe() if err != nil { - fatal("%s", err) + fatalf("%s", err) } r1, w1, err := os.Pipe() if err != nil { - fatal("%s", err) + fatalf("%s", err) } r2, w2, err := os.Pipe() if err != nil { - fatal("%s", err) + fatalf("%s", err) } - p, err := os.StartProcess(cmd, argv, os.Environ(), "", []*os.File{r0, w1, w2}) + p, err := os.StartProcess(cmd, argv, &os.ProcAttr{Files: []*os.File{r0, w1, w2}}) if err != nil { - fatal("%s", err) + fatalf("%s", err) } defer p.Release() r0.Close() @@ -58,14 +58,14 @@ func run(stdin []byte, argv []string) (stdout, stderr []byte, ok bool) { w, err := p.Wait(0) if err != nil { - fatal("%s", err) + fatalf("%s", err) } ok = w.Exited() && w.ExitStatus() == 0 return } // Die with an error message. -func fatal(msg string, args ...interface{}) { +func fatalf(msg string, args ...interface{}) { fmt.Fprintf(os.Stderr, msg+"\n", args...) os.Exit(2) } @@ -95,9 +95,9 @@ func isName(s string) bool { } func creat(name string) *os.File { - f, err := os.Open(name, os.O_WRONLY|os.O_CREAT|os.O_TRUNC, 0666) + f, err := os.Create(name) if err != nil { - fatal("%s", err) + fatalf("%s", err) } return f } diff --git a/src/cmd/gc/align.c b/src/cmd/gc/align.c index 833eba19a..a01e2ea46 100644 --- a/src/cmd/gc/align.c +++ b/src/cmd/gc/align.c @@ -172,6 +172,9 @@ dowidth(Type *t) w = 8; checkwidth(t->type); break; + case TUNSAFEPTR: + w = widthptr; + break; case TINTER: // implemented as 2 pointers w = 2*widthptr; t->align = widthptr; @@ -400,6 +403,13 @@ typeinit(void) types[TPTR64] = typ(TPTR64); dowidth(types[TPTR64]); + + t = typ(TUNSAFEPTR); + types[TUNSAFEPTR] = t; + t->sym = pkglookup("Pointer", unsafepkg); + t->sym->def = typenod(t); + + dowidth(types[TUNSAFEPTR]); tptr = TPTR32; if(widthptr == 8) @@ -481,6 +491,7 @@ typeinit(void) okforeq[TPTR32] = 1; okforeq[TPTR64] = 1; + okforeq[TUNSAFEPTR] = 1; okforeq[TINTER] = 1; okforeq[TMAP] = 1; okforeq[TCHAN] = 1; @@ -570,6 +581,7 @@ typeinit(void) simtype[TMAP] = tptr; simtype[TCHAN] = tptr; simtype[TFUNC] = tptr; + simtype[TUNSAFEPTR] = tptr; /* pick up the backend typedefs */ for(i=0; typedefs[i].name; i++) { diff --git a/src/cmd/gc/builtin.c.boot b/src/cmd/gc/builtin.c.boot index 48f45293f..bdbca7f78 100644 --- a/src/cmd/gc/builtin.c.boot +++ b/src/cmd/gc/builtin.c.boot @@ -66,15 +66,17 @@ char *runtimeimport = "func \"\".mapiter2 (hiter *any) (key any, val any)\n" "func \"\".makechan (elem *uint8, hint int64) chan any\n" "func \"\".chanrecv1 (hchan <-chan any) any\n" - "func \"\".chanrecv3 (hchan <-chan any) (elem any, closed bool)\n" + "func \"\".chanrecv2 (hchan <-chan any) (elem any, received bool)\n" "func \"\".chansend1 (hchan chan<- any, elem any)\n" "func \"\".closechan (hchan any)\n" "func \"\".closedchan (hchan any) bool\n" "func \"\".selectnbsend (hchan chan<- any, elem any) bool\n" "func \"\".selectnbrecv (elem *any, hchan <-chan any) bool\n" + "func \"\".selectnbrecv2 (elem *any, received *bool, hchan <-chan any) bool\n" "func \"\".newselect (size int) *uint8\n" "func \"\".selectsend (sel *uint8, hchan chan<- any, elem any) bool\n" "func \"\".selectrecv (sel *uint8, hchan <-chan any, elem *any) bool\n" + "func \"\".selectrecv2 (sel *uint8, hchan <-chan any, elem *any, received *bool) bool\n" "func \"\".selectdefault (sel *uint8) bool\n" "func \"\".selectgo (sel *uint8)\n" "func \"\".block ()\n" @@ -96,7 +98,7 @@ char *runtimeimport = "$$\n"; char *unsafeimport = "package unsafe\n" - "type \"\".Pointer *any\n" + "type \"\".Pointer uintptr\n" "func \"\".Offsetof (? any) int\n" "func \"\".Sizeof (? any) int\n" "func \"\".Alignof (? any) int\n" diff --git a/src/cmd/gc/const.c b/src/cmd/gc/const.c index a54c40f6c..a36ec68c0 100644 --- a/src/cmd/gc/const.c +++ b/src/cmd/gc/const.c @@ -136,7 +136,6 @@ convlit1(Node **np, Type *t, int explicit) case CTNIL: switch(et) { default: - yyerror("cannot use nil as %T", t); n->type = T; goto bad; @@ -155,6 +154,7 @@ convlit1(Node **np, Type *t, int explicit) case TMAP: case TCHAN: case TFUNC: + case TUNSAFEPTR: break; } break; diff --git a/src/cmd/gc/dcl.c b/src/cmd/gc/dcl.c index a71272aa2..3089a23b0 100644 --- a/src/cmd/gc/dcl.c +++ b/src/cmd/gc/dcl.c @@ -22,7 +22,6 @@ dflag(void) /* * declaration stack & operations */ -static Sym* dclstack; static void dcopy(Sym *a, Sym *b) @@ -656,10 +655,19 @@ typedcl2(Type *pt, Type *t) { Node *n; + // override declaration in unsafe.go for Pointer. + // there is no way in Go code to define unsafe.Pointer + // so we have to supply it. + if(incannedimport && + strcmp(importpkg->name, "unsafe") == 0 && + strcmp(pt->nod->sym->name, "Pointer") == 0) { + t = types[TUNSAFEPTR]; + } + if(pt->etype == TFORW) goto ok; if(!eqtype(pt->orig, t)) - yyerror("inconsistent definition for type %S during import\n\t%lT\n\t%lT", pt->sym, pt, t); + yyerror("inconsistent definition for type %S during import\n\t%lT\n\t%lT", pt->sym, pt->orig, t); return; ok: @@ -684,13 +692,13 @@ ok: * turn a parsed struct into a type */ static Type** -stotype(NodeList *l, int et, Type **t) +stotype(NodeList *l, int et, Type **t, int funarg) { Type *f, *t1, *t2, **t0; Strlit *note; int lno; NodeList *init; - Node *n; + Node *n, *left; char *what; t0 = t; @@ -707,15 +715,18 @@ stotype(NodeList *l, int et, Type **t) if(n->op != ODCLFIELD) fatal("stotype: oops %N\n", n); + left = n->left; + if(funarg && isblank(left)) + left = N; if(n->right != N) { - if(et == TINTER && n->left != N) { + if(et == TINTER && left != N) { // queue resolution of method type for later. // right now all we need is the name list. // avoids cycles for recursive interface types. n->type = typ(TINTERMETH); n->type->nname = n->right; n->right = N; - n->left->type = n->type; + left->type = n->type; queuemethod(n); } else { typecheck(&n->right, Etype); @@ -724,8 +735,8 @@ stotype(NodeList *l, int et, Type **t) *t0 = T; return t0; } - if(n->left != N) - n->left->type = n->type; + if(left != N) + left->type = n->type; n->right = N; if(n->embedded && n->type != T) { t1 = n->type; @@ -763,7 +774,7 @@ stotype(NodeList *l, int et, Type **t) break; } - if(et == TINTER && n->left == N) { + if(et == TINTER && left == N) { // embedded interface - inline the methods if(n->type->etype != TINTER) { if(n->type->etype == TFORW) @@ -796,8 +807,8 @@ stotype(NodeList *l, int et, Type **t) f->width = BADWIDTH; f->isddd = n->isddd; - if(n->left != N && n->left->op == ONAME) { - f->nname = n->left; + if(left != N && left->op == ONAME) { + f->nname = left; f->embedded = n->embedded; f->sym = f->nname->sym; if(importpkg && !exportname(f->sym->name)) @@ -839,7 +850,7 @@ dostruct(NodeList *l, int et) } t = typ(et); t->funarg = funarg; - stotype(l, et, &t->type); + stotype(l, et, &t->type, funarg); if(t->type == T && l != nil) { t->broke = 1; return t; @@ -933,8 +944,6 @@ checkarglist(NodeList *all, int input) t = n; n = N; } - if(isblank(n)) - n = N; if(n != N && n->sym == S) { t = n; n = N; @@ -1151,9 +1160,9 @@ addmethod(Sym *sf, Type *t, int local) } if(d == T) - stotype(list1(n), 0, &pa->method); + stotype(list1(n), 0, &pa->method, 0); else - stotype(list1(n), 0, &d->down); + stotype(list1(n), 0, &d->down, 0); return; } diff --git a/src/cmd/gc/export.c b/src/cmd/gc/export.c index 09b963f27..014f0c5f0 100644 --- a/src/cmd/gc/export.c +++ b/src/cmd/gc/export.c @@ -75,10 +75,15 @@ autoexport(Node *n, int ctxt) static void dumppkg(Pkg *p) { + char *suffix; + if(p == nil || p == localpkg || p->exported) return; p->exported = 1; - Bprint(bout, "\timport %s \"%Z\"\n", p->name, p->path); + suffix = ""; + if(!p->direct) + suffix = " // indirect"; + Bprint(bout, "\timport %s \"%Z\"%s\n", p->name, p->path, suffix); } static void @@ -265,7 +270,8 @@ void dumpexport(void) { NodeList *l; - int32 lno; + int32 i, lno; + Pkg *p; lno = lineno; @@ -277,6 +283,11 @@ dumpexport(void) Bprint(bout, " safe"); Bprint(bout, "\n"); + for(i=0; ilink) + if(p->direct) + dumppkg(p); + for(l=exportlist; l; l=l->next) { lineno = l->n->lineno; dumpsym(l->n->sym); diff --git a/src/cmd/gc/gen.c b/src/cmd/gc/gen.c index 04af5a7bb..8ad6c437d 100644 --- a/src/cmd/gc/gen.c +++ b/src/cmd/gc/gen.c @@ -64,62 +64,83 @@ allocparams(void) lineno = lno; } +void +clearlabels(void) +{ + Label *l; + + for(l=labellist; l!=L; l=l->link) + l->sym->label = L; + + labellist = L; + lastlabel = L; +} + static void -newlab(int op, Sym *s, Node *stmt) +newlab(int op, Node *nlab, Node *stmt) { Label *lab; + Sym *s; + int32 lno; + + s = nlab->left->sym; + lno = nlab->left->lineno; lab = mal(sizeof(*lab)); - lab->link = labellist; - labellist = lab; + if(lastlabel == nil) + labellist = lab; + else + lastlabel->link = lab; + lastlabel = lab; + lab->lineno = lno; lab->sym = s; lab->op = op; lab->label = pc; lab->stmt = stmt; + if(op == OLABEL) { + if(s->label != L) { + lineno = lno; + yyerror("label %S already defined at %L", s, s->label->lineno); + } else + s->label = lab; + } } void checklabels(void) { - Label *l, *m; + Label *l; Sym *s; + int lno; -// // print the label list -// for(l=labellist; l!=L; l=l->link) { -// print("lab %O %S\n", l->op, l->sym); -// } - + lno = lineno; + + // resolve goto using syms for(l=labellist; l!=L; l=l->link) { - switch(l->op) { - case OLABEL: - // these are definitions - + switch(l->op) { + case OGOTO: s = l->sym; - for(m=labellist; m!=L; m=m->link) { - if(m->sym != s) - continue; - switch(m->op) { - case OLABEL: - // these are definitions - - // look for redefinitions - if(l != m) - yyerror("label %S redefined", s); - break; - case OGOTO: - // these are references - - // patch to definition - patch(m->label, l->label); - m->sym = S; // mark done - break; - } + if(s->label == L) { + lineno = l->lineno; + yyerror("label %S not defined", s); + break; } + s->label->used = 1; + patch(l->label, s->label->label); + break; } } - - // diagnostic for all undefined references - for(l=labellist; l!=L; l=l->link) - if(l->op == OGOTO && l->sym != S) - yyerror("label %S not defined", l->sym); + + // diagnose unused labels + for(l=labellist; l!=L; l=l->link) { + if(l->op == OLABEL && !l->used) { + lineno = l->lineno; + yyerror("label %S defined and not used", l->sym); + } + } + + lineno = lno; } /* @@ -171,7 +192,7 @@ gen(Node *n) // insert no-op so that // L:; for { } // does not treat L as a label for the loop. - if(labellist && labellist->label == p3) + if(lastlabel != L && lastlabel->label == p3) gused(N); break; @@ -180,26 +201,27 @@ gen(Node *n) break; case OLABEL: - newlab(OLABEL, n->left->sym, n->right); + newlab(OLABEL, n, n->right); break; case OGOTO: - newlab(OGOTO, n->left->sym, N); + newlab(OGOTO, n, N); gjmp(P); break; case OBREAK: if(n->left != N) { - for(lab=labellist; lab!=L; lab=lab->link) { - if(lab->sym == n->left->sym) { - if(lab->breakpc == P) - yyerror("invalid break label %S", n->left->sym); - gjmp(lab->breakpc); - goto donebreak; - } - } - if(lab == L) + lab = n->left->sym->label; + if(lab == L) { yyerror("break label not defined: %S", n->left->sym); + break; + } + lab->used = 1; + if(lab->breakpc == P) { + yyerror("invalid break label %S", n->left->sym); + break; + } + gjmp(lab->breakpc); break; } if(breakpc == P) { @@ -207,30 +229,28 @@ gen(Node *n) break; } gjmp(breakpc); - donebreak: break; case OCONTINUE: if(n->left != N) { - for(lab=labellist; lab!=L; lab=lab->link) { - if(lab->sym == n->left->sym) { - if(lab->continpc == P) - yyerror("invalid continue label %S", n->left->sym); - gjmp(lab->continpc); - goto donecont; - } - } - if(lab == L) + lab = n->left->sym->label; + if(lab == L) { yyerror("continue label not defined: %S", n->left->sym); + break; + } + lab->used = 1; + if(lab->continpc == P) { + yyerror("invalid continue label %S", n->left->sym); + break; + } + gjmp(lab->continpc); break; } - if(continpc == P) { yyerror("continue is not in a loop"); break; } gjmp(continpc); - donecont: break; case OFOR: @@ -241,10 +261,11 @@ gen(Node *n) continpc = pc; // define break and continue labels - if((lab = labellist) != L && lab->label == p3 && lab->op == OLABEL && lab->stmt == n) { + if((lab = lastlabel) != L && lab->label == p3 && lab->op == OLABEL && lab->stmt == n) { lab->breakpc = breakpc; lab->continpc = continpc; - } + } else + lab = L; gen(n->nincr); // contin: incr patch(p1, pc); // test: @@ -254,6 +275,10 @@ gen(Node *n) patch(breakpc, pc); // done: continpc = scontin; breakpc = sbreak; + if(lab) { + lab->breakpc = P; + lab->continpc = P; + } break; case OIF: @@ -274,13 +299,17 @@ gen(Node *n) breakpc = gjmp(P); // break: goto done // define break label - if((lab = labellist) != L && lab->label == p3 && lab->op == OLABEL && lab->stmt == n) + if((lab = lastlabel) != L && lab->label == p3 && lab->op == OLABEL && lab->stmt == n) lab->breakpc = breakpc; + else + lab = L; patch(p1, pc); // test: genlist(n->nbody); // switch(test) body patch(breakpc, pc); // done: breakpc = sbreak; + if(lab != L) + lab->breakpc = P; break; case OSELECT: @@ -289,13 +318,17 @@ gen(Node *n) breakpc = gjmp(P); // break: goto done // define break label - if((lab = labellist) != L && lab->label == p3 && lab->op == OLABEL && lab->stmt == n) + if((lab = lastlabel) != L && lab->label == p3 && lab->op == OLABEL && lab->stmt == n) lab->breakpc = breakpc; + else + lab = L; patch(p1, pc); // test: genlist(n->nbody); // select() body patch(breakpc, pc); // done: breakpc = sbreak; + if(lab != L) + lab->breakpc = P; break; case OASOP: diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h index bf84c12a1..bb258a193 100644 --- a/src/cmd/gc/go.h +++ b/src/cmd/gc/go.h @@ -138,6 +138,7 @@ typedef struct Sym Sym; typedef struct Node Node; typedef struct NodeList NodeList; typedef struct Type Type; +typedef struct Label Label; struct Type { @@ -302,11 +303,14 @@ struct Sym Pkg* pkg; char* name; // variable name Node* def; // definition: ONAME OTYPE OPACK or OLITERAL + Label* label; // corresponding label (ephemeral) int32 block; // blocknumber to catch redeclaration int32 lastlineno; // last declaration for diagnostic }; #define S ((Sym*)0) +EXTERN Sym* dclstack; + struct Pkg { char* name; @@ -356,12 +360,11 @@ enum OARRAY, OARRAYBYTESTR, OARRAYRUNESTR, OSTRARRAYBYTE, OSTRARRAYRUNE, - OAS, OAS2, OAS2MAPW, OAS2FUNC, OAS2RECVCLOSED, OAS2MAPR, OAS2DOTTYPE, OASOP, + OAS, OAS2, OAS2MAPW, OAS2FUNC, OAS2RECV, OAS2MAPR, OAS2DOTTYPE, OASOP, OBAD, OCALL, OCALLFUNC, OCALLMETH, OCALLINTER, OCAP, OCLOSE, - OCLOSED, OCLOSURE, OCMPIFACE, OCMPSTR, OCOMPLIT, OMAPLIT, OSTRUCTLIT, OARRAYLIT, @@ -389,6 +392,7 @@ enum ORECV, ORUNESTR, OSELRECV, + OSELRECV2, OIOTA, OREAL, OIMAG, OCOMPLEX, @@ -441,27 +445,28 @@ enum TCOMPLEX64, // 12 TCOMPLEX128, - TFLOAT32, // 15 + TFLOAT32, // 14 TFLOAT64, - TBOOL, // 18 + TBOOL, // 16 - TPTR32, TPTR64, // 19 + TPTR32, TPTR64, // 17 - TFUNC, // 21 + TFUNC, // 19 TARRAY, T_old_DARRAY, - TSTRUCT, // 24 + TSTRUCT, // 22 TCHAN, TMAP, - TINTER, // 27 + TINTER, // 25 TFORW, TFIELD, TANY, TSTRING, + TUNSAFEPTR, // pseudo-types for literals - TIDEAL, // 32 + TIDEAL, // 31 TNIL, TBLANK, @@ -618,20 +623,22 @@ struct Magic typedef struct Prog Prog; -typedef struct Label Label; struct Label { uchar op; // OGOTO/OLABEL + uchar used; Sym* sym; Node* stmt; Prog* label; // pointer to code Prog* breakpc; // pointer to code Prog* continpc; // pointer to code Label* link; + int32 lineno; }; #define L ((Label*)0) EXTERN Label* labellist; +EXTERN Label* lastlabel; /* * note this is the runtime representation @@ -899,6 +906,7 @@ void allocparams(void); void cgen_as(Node *nl, Node *nr); void cgen_callmeth(Node *n, int proc); void checklabels(void); +void clearlabels(void); int dotoffset(Node *n, int *oary, Node **nn); void gen(Node *n); void genlist(NodeList *l); @@ -993,8 +1001,10 @@ int duint32(Sym *s, int off, uint32 v); int duint64(Sym *s, int off, uint64 v); int duint8(Sym *s, int off, uint8 v); int duintptr(Sym *s, int off, uint64 v); +int dsname(Sym *s, int off, char *dat, int ndat); void dumpobj(void); void ieeedtod(uint64 *ieee, double native); +Sym* stringsym(char*, int); /* * print.c @@ -1229,3 +1239,5 @@ void patch(Prog*, Prog*); void zfile(Biobuf *b, char *p, int n); void zhist(Biobuf *b, int line, vlong offset); void zname(Biobuf *b, Sym *s, int t); +void data(void); +void text(void); diff --git a/src/cmd/gc/go.y b/src/cmd/gc/go.y index 4b838a491..89899ae1e 100644 --- a/src/cmd/gc/go.y +++ b/src/cmd/gc/go.y @@ -461,23 +461,32 @@ case: } break; } -| LCASE expr '=' expr ':' +| LCASE expr_or_type_list '=' expr ':' { + Node *n; + // will be converted to OCASE // right will point to next case // done in casebody() poptodcl(); $$ = nod(OXCASE, N, N); - $$->list = list1(nod(OAS, $2, $4)); + if($2->next == nil) + n = nod(OAS, $2->n, $4); + else { + n = nod(OAS2, N, N); + n->list = $2; + n->rlist = list1($4); + } + $$->list = list1(n); } -| LCASE name LCOLAS expr ':' +| LCASE expr_or_type_list LCOLAS expr ':' { // will be converted to OCASE // right will point to next case // done in casebody() poptodcl(); $$ = nod(OXCASE, N, N); - $$->list = list1(colas(list1($2), list1($4))); + $$->list = list1(colas($2, list1($4))); } | LDEFAULT ':' { @@ -1230,9 +1239,10 @@ fnlitdcl: } fnliteral: - fnlitdcl '{' stmt_list '}' + fnlitdcl lbrace stmt_list '}' { $$ = closurebody($3); + fixlbrace($2); } diff --git a/src/cmd/gc/lex.c b/src/cmd/gc/lex.c index e79d3b0f8..bfd96274e 100644 --- a/src/cmd/gc/lex.c +++ b/src/cmd/gc/lex.c @@ -124,9 +124,6 @@ main(int argc, char *argv[]) runtimepkg = mkpkg(strlit("runtime")); runtimepkg->name = "runtime"; - stringpkg = mkpkg(strlit("string")); - stringpkg->name = "string"; - typepkg = mkpkg(strlit("type")); typepkg->name = "type"; @@ -1555,7 +1552,6 @@ static struct "append", LNAME, Txxx, OAPPEND, "cap", LNAME, Txxx, OCAP, "close", LNAME, Txxx, OCLOSE, - "closed", LNAME, Txxx, OCLOSED, "complex", LNAME, Txxx, OCOMPLEX, "copy", LNAME, Txxx, OCOPY, "imag", LNAME, Txxx, OIMAG, diff --git a/src/cmd/gc/obj.c b/src/cmd/gc/obj.c index fbabe0d43..9f4b7b318 100644 --- a/src/cmd/gc/obj.c +++ b/src/cmd/gc/obj.c @@ -235,3 +235,57 @@ duintptr(Sym *s, int off, uint64 v) { return duintxx(s, off, v, widthptr); } + +Sym* +stringsym(char *s, int len) +{ + static int gen; + Sym *sym; + int off, n, m; + struct { + Strlit lit; + char buf[110]; + } tmp; + Pkg *pkg; + + if(len > 100) { + // huge strings are made static to avoid long names + snprint(namebuf, sizeof(namebuf), ".gostring.%d", ++gen); + pkg = localpkg; + } else { + // small strings get named by their contents, + // so that multiple modules using the same string + // can share it. + tmp.lit.len = len; + memmove(tmp.lit.s, s, len); + tmp.lit.s[len] = '\0'; + snprint(namebuf, sizeof(namebuf), "\"%Z\"", &tmp); + pkg = gostringpkg; + } + sym = pkglookup(namebuf, pkg); + + // SymUniq flag indicates that data is generated already + if(sym->flags & SymUniq) + return sym; + sym->flags |= SymUniq; + + data(); + off = 0; + + // string header + off = dsymptr(sym, off, sym, widthptr+4); + off = duint32(sym, off, len); + + // string data + for(n=0; n len-n) + m = len-n; + off = dsname(sym, off, s+n, m); + } + off = duint8(sym, off, 0); // terminating NUL for runtime + ggloblsym(sym, off, 1); + text(); + + return sym; +} diff --git a/src/cmd/gc/print.c b/src/cmd/gc/print.c index 695a5a397..fee37f6d0 100644 --- a/src/cmd/gc/print.c +++ b/src/cmd/gc/print.c @@ -52,7 +52,6 @@ exprfmt(Fmt *f, Node *n, int prec) case OARRAYBYTESTR: case OCAP: case OCLOSE: - case OCLOSED: case OCOPY: case OLEN: case OMAKE: @@ -405,7 +404,6 @@ exprfmt(Fmt *f, Node *n, int prec) case OAPPEND: case OCAP: case OCLOSE: - case OCLOSED: case OLEN: case OCOPY: case OMAKE: diff --git a/src/cmd/gc/range.c b/src/cmd/gc/range.c index 4ee8f39a7..dfb2b8efd 100644 --- a/src/cmd/gc/range.c +++ b/src/cmd/gc/range.c @@ -203,8 +203,8 @@ walkrange(Node *n) hb = nod(OXXX, N, N); tempname(hb, types[TBOOL]); - n->ntest = nod(ONOT, hb, N); - a = nod(OAS2RECVCLOSED, N, N); + n->ntest = nod(ONE, hb, nodbool(0)); + a = nod(OAS2RECV, N, N); a->typecheck = 1; a->list = list(list1(hv1), hb); a->rlist = list1(nod(ORECV, ha, N)); diff --git a/src/cmd/gc/reflect.c b/src/cmd/gc/reflect.c index 8129bf1ce..b98e820c6 100644 --- a/src/cmd/gc/reflect.c +++ b/src/cmd/gc/reflect.c @@ -348,17 +348,19 @@ dimportpath(Pkg *p) * uncommonType * ../../pkg/runtime/type.go:/uncommonType */ -static Sym* -dextratype(Type *t) +static int +dextratype(Sym *sym, int off, Type *t, int ptroff) { int ot, n; - char *p; Sym *s; Sig *a, *m; m = methods(t); if(t->sym == nil && m == nil) - return nil; + return off; + + // fill in *extraType pointer in header + dsymptr(sym, ptroff, sym, off); n = 0; for(a=m; a; a=a->link) { @@ -366,9 +368,8 @@ dextratype(Type *t) n++; } - p = smprint("_.%#T", t); - s = pkglookup(p, typepkg); - ot = 0; + ot = off; + s = sym; if(t->sym) { ot = dgostringptr(s, ot, t->sym->name); if(t != types[t->etype]) @@ -402,9 +403,8 @@ dextratype(Type *t) else ot = duintptr(s, ot, 0); } - ggloblsym(s, ot, 0); - return s; + return ot; } enum { @@ -466,6 +466,7 @@ kinds[] = [TFUNC] = KindFunc, [TCOMPLEX64] = KindComplex64, [TCOMPLEX128] = KindComplex128, + [TUNSAFEPTR] = KindUnsafePointer, }; static char* @@ -488,6 +489,7 @@ structnames[] = [TFLOAT64] = "*runtime.FloatType", [TBOOL] = "*runtime.BoolType", [TSTRING] = "*runtime.StringType", + [TUNSAFEPTR] = "*runtime.UnsafePointerType", [TPTR32] = "*runtime.PtrType", [TPTR64] = "*runtime.PtrType", @@ -514,9 +516,6 @@ typestruct(Type *t) if(isslice(t)) name = "*runtime.SliceType"; - if(isptr[et] && t->type->etype == TANY) - name = "*runtime.UnsafePointerType"; - return pkglookup(name, typepkg); } @@ -553,6 +552,7 @@ haspointers(Type *t) case TSTRING: case TPTR32: case TPTR64: + case TUNSAFEPTR: case TINTER: case TCHAN: case TMAP: @@ -570,7 +570,6 @@ static int dcommontype(Sym *s, int ot, Type *t) { int i; - Sym *s1; Sym *sptr; char *p; @@ -582,8 +581,6 @@ dcommontype(Sym *s, int ot, Type *t) else sptr = weaktypesym(ptrto(t)); - s1 = dextratype(t); - // empty interface pointing at this type. // all the references that we emit are *interface{}; // they point here. @@ -612,8 +609,6 @@ dcommontype(Sym *s, int ot, Type *t) i = kinds[t->etype]; if(t->etype == TARRAY && t->bound < 0) i = KindSlice; - if(isptr[t->etype] && t->type->etype == TANY) - i = KindUnsafePointer; if(!haspointers(t)) i |= KindNoPointers; ot = duint8(s, ot, i); // kind @@ -622,11 +617,14 @@ dcommontype(Sym *s, int ot, Type *t) longsymnames = 0; ot = dgostringptr(s, ot, p); // string free(p); - if(s1) - ot = dsymptr(s, ot, s1, 0); // extraType - else - ot = duintptr(s, ot, 0); - ot = dsymptr(s, ot, sptr, 0); // ptr to type + + // skip pointer to extraType, + // which follows the rest of this type structure. + // caller will fill in if needed. + // otherwise linker will assume 0. + ot += widthptr; + + ot = dsymptr(s, ot, sptr, 0); // ptrto type return ot; } @@ -693,7 +691,7 @@ weaktypesym(Type *t) static Sym* dtypesym(Type *t) { - int ot, n, isddd, dupok; + int ot, xt, n, isddd, dupok; Sym *s, *s1, *s2; Sig *a, *m; Type *t1, *tbase; @@ -714,12 +712,8 @@ dtypesym(Type *t) tbase = t->type; dupok = tbase->sym == S; - if(compiling_runtime) { - if(tbase == types[tbase->etype]) // int, float, etc - goto ok; - if(tbase->etype == tptr && tbase->type->etype == TANY) // unsafe.Pointer - goto ok; - } + if(compiling_runtime && tbase == types[tbase->etype]) // int, float, etc + goto ok; // named types from other files are defined only by those files if(tbase->sym && !tbase->local) @@ -729,15 +723,18 @@ dtypesym(Type *t) ok: ot = 0; + xt = 0; switch(t->etype) { default: ot = dcommontype(s, ot, t); + xt = ot - 2*widthptr; break; case TARRAY: // ../../pkg/runtime/type.go:/ArrayType s1 = dtypesym(t->type); ot = dcommontype(s, ot, t); + xt = ot - 2*widthptr; ot = dsymptr(s, ot, s1, 0); if(t->bound < 0) ot = duintptr(s, ot, -1); @@ -749,6 +746,7 @@ ok: // ../../pkg/runtime/type.go:/ChanType s1 = dtypesym(t->type); ot = dcommontype(s, ot, t); + xt = ot - 2*widthptr; ot = dsymptr(s, ot, s1, 0); ot = duintptr(s, ot, t->chan); break; @@ -765,6 +763,7 @@ ok: dtypesym(t1->type); ot = dcommontype(s, ot, t); + xt = ot - 2*widthptr; ot = duint8(s, ot, isddd); // two slice headers: in and out. @@ -796,6 +795,7 @@ ok: // ../../pkg/runtime/type.go:/InterfaceType ot = dcommontype(s, ot, t); + xt = ot - 2*widthptr; ot = dsymptr(s, ot, s, ot+widthptr+2*4); ot = duint32(s, ot, n); ot = duint32(s, ot, n); @@ -812,6 +812,7 @@ ok: s1 = dtypesym(t->down); s2 = dtypesym(t->type); ot = dcommontype(s, ot, t); + xt = ot - 2*widthptr; ot = dsymptr(s, ot, s1, 0); ot = dsymptr(s, ot, s2, 0); break; @@ -826,6 +827,7 @@ ok: // ../../pkg/runtime/type.go:/PtrType s1 = dtypesym(t->type); ot = dcommontype(s, ot, t); + xt = ot - 2*widthptr; ot = dsymptr(s, ot, s1, 0); break; @@ -838,6 +840,7 @@ ok: n++; } ot = dcommontype(s, ot, t); + xt = ot - 2*widthptr; ot = dsymptr(s, ot, s, ot+widthptr+2*4); ot = duint32(s, ot, n); ot = duint32(s, ot, n); @@ -859,7 +862,7 @@ ok: } break; } - + ot = dextratype(s, ot, t, xt); ggloblsym(s, ot, dupok); return s; } @@ -908,7 +911,7 @@ dumptypestructs(void) for(i=1; i<=TBOOL; i++) dtypesym(ptrto(types[i])); dtypesym(ptrto(types[TSTRING])); - dtypesym(ptrto(pkglookup("Pointer", unsafepkg)->def->type)); + dtypesym(ptrto(types[TUNSAFEPTR])); // add paths for runtime and main, which 6l imports implicitly. dimportpath(runtimepkg); diff --git a/src/cmd/gc/runtime.go b/src/cmd/gc/runtime.go index bf7d045c0..35d11eca9 100644 --- a/src/cmd/gc/runtime.go +++ b/src/cmd/gc/runtime.go @@ -92,17 +92,19 @@ func mapiter2(hiter *any) (key any, val any) // *byte is really *runtime.Type func makechan(elem *byte, hint int64) (hchan chan any) func chanrecv1(hchan <-chan any) (elem any) -func chanrecv3(hchan <-chan any) (elem any, closed bool) +func chanrecv2(hchan <-chan any) (elem any, received bool) func chansend1(hchan chan<- any, elem any) func closechan(hchan any) func closedchan(hchan any) bool func selectnbsend(hchan chan<- any, elem any) bool func selectnbrecv(elem *any, hchan <-chan any) bool +func selectnbrecv2(elem *any, received *bool, hchan <-chan any) bool func newselect(size int) (sel *byte) func selectsend(sel *byte, hchan chan<- any, elem any) (selected bool) func selectrecv(sel *byte, hchan <-chan any, elem *any) (selected bool) +func selectrecv2(sel *byte, hchan <-chan any, elem *any, received *bool) (selected bool) func selectdefault(sel *byte) (selected bool) func selectgo(sel *byte) func block() diff --git a/src/cmd/gc/select.c b/src/cmd/gc/select.c index 58a147745..91d4ebfd5 100644 --- a/src/cmd/gc/select.c +++ b/src/cmd/gc/select.c @@ -58,6 +58,18 @@ typecheckselect(Node *sel) n->op = OSELRECV; break; + case OAS2RECV: + // convert x, ok = <-c into OSELRECV(x, <-c) with ntest=ok + if(n->right->op != ORECV) { + yyerror("select assignment must have receive on right hand side"); + break; + } + n->op = OSELRECV2; + n->left = n->list->n; + n->ntest = n->list->next->n; + n->right = n->rlist->n; + break; + case ORECV: // convert <-c into OSELRECV(N, <-c) n = nod(OSELRECV, N, n); @@ -122,6 +134,18 @@ walkselect(Node *sel) typecheck(&n, Etop); } break; + + case OSELRECV2: + r = n->right; + ch = cheapexpr(r->left, &l); + r->left = ch; + + a = nod(OAS2, N, N); + a->list = n->list; + a->rlist = n->rlist; + n = a; + typecheck(&n, Etop); + break; } // if ch == nil { block() }; n; @@ -146,6 +170,7 @@ walkselect(Node *sel) continue; switch(n->op) { case OSELRECV: + case OSELRECV2: ch = n->right->left; // If we can use the address of the target without @@ -154,6 +179,28 @@ walkselect(Node *sel) // Also introduce a temporary for := variables that escape, // so that we can delay the heap allocation until the case // is selected. + if(n->op == OSELRECV2) { + if(n->ntest == N || isblank(n->ntest)) + n->ntest = nodnil(); + else if(n->ntest->op == ONAME && + (!n->colas || (n->ntest->class&PHEAP) == 0) && + convertop(types[TBOOL], n->ntest->type, nil) == OCONVNOP) { + n->ntest = nod(OADDR, n->ntest, N); + n->ntest->etype = 1; // pointer does not escape + typecheck(&n->ntest, Erv); + } else { + tmp = nod(OXXX, N, N); + tempname(tmp, types[TBOOL]); + a = nod(OADDR, tmp, N); + a->etype = 1; // pointer does not escape + typecheck(&a, Erv); + r = nod(OAS, n->ntest, tmp); + typecheck(&r, Etop); + cas->nbody = concat(list1(r), cas->nbody); + n->ntest = a; + } + } + if(n->left == N || isblank(n->left)) n->left = nodnil(); else if(n->left->op == ONAME && @@ -171,10 +218,12 @@ walkselect(Node *sel) r = nod(OAS, n->left, tmp); typecheck(&r, Etop); cas->nbody = concat(list1(r), cas->nbody); - cas->nbody = concat(n->ninit, cas->nbody); - n->ninit = nil; n->left = a; } + + cas->nbody = concat(n->ninit, cas->nbody); + n->ninit = nil; + break; } } @@ -212,6 +261,16 @@ walkselect(Node *sel) mkcall1(chanfn("selectnbrecv", 2, ch->type), types[TBOOL], &r->ninit, n->left, ch)); break; + + case OSELRECV2: + // if c != nil && selectnbrecv2(&v, c) { body } else { default body } + r = nod(OIF, N, N); + r->ninit = cas->ninit; + ch = cheapexpr(n->right->left, &r->ninit); + r->ntest = nod(OANDAND, nod(ONE, ch, nodnil()), + mkcall1(chanfn("selectnbrecv2", 2, ch->type), + types[TBOOL], &r->ninit, n->left, n->ntest, ch)); + break; } typecheck(&r->ntest, Erv); r->nbody = cas->nbody; @@ -254,11 +313,18 @@ walkselect(Node *sel) r->ntest = mkcall1(chanfn("selectsend", 2, n->left->type), types[TBOOL], &init, var, n->left, n->right); break; + case OSELRECV: // selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool); r->ntest = mkcall1(chanfn("selectrecv", 2, n->right->left->type), types[TBOOL], &init, var, n->right->left, n->left); break; + + case OSELRECV2: + // selectrecv2(sel *byte, hchan *chan any, elem *any, received *bool) (selected bool); + r->ntest = mkcall1(chanfn("selectrecv2", 2, n->right->left->type), types[TBOOL], + &init, var, n->right->left, n->left, n->ntest); + break; } } r->nbody = concat(r->nbody, cas->nbody); diff --git a/src/cmd/gc/sinit.c b/src/cmd/gc/sinit.c index 31781646d..be96a1477 100644 --- a/src/cmd/gc/sinit.c +++ b/src/cmd/gc/sinit.c @@ -94,7 +94,7 @@ init1(Node *n, NodeList **out) case OAS2FUNC: case OAS2MAPR: case OAS2DOTTYPE: - case OAS2RECVCLOSED: + case OAS2RECV: if(n->defn->initorder) break; n->defn->initorder = 1; diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c index 142e5ba41..2098794a7 100644 --- a/src/cmd/gc/subr.c +++ b/src/cmd/gc/subr.c @@ -135,6 +135,7 @@ yyerror(char *fmt, ...) int i; static int lastsyntax; va_list arg; + char buf[512], *p; if(strncmp(fmt, "syntax error", 12) == 0) { nsyntaxerrors++; @@ -147,6 +148,16 @@ yyerror(char *fmt, ...) return; lastsyntax = lexlineno; + if(strstr(fmt, "{ or {")) { + // The grammar has { and LBRACE but both show up as {. + // Rewrite syntax error referring to "{ or {" to say just "{". + strecpy(buf, buf+sizeof buf, fmt); + p = strstr(buf, "{ or {"); + if(p) + memmove(p+1, p+6, strlen(p+6)+1); + fmt = buf; + } + // look for parse state-specific errors in list (see go.errors). for(i=0; isym != S && !(fp->flags&FmtLong)) { s = t->sym; - if(t == types[t->etype]) + if(t == types[t->etype] && t->etype != TUNSAFEPTR) return fmtprint(fp, "%s", s->name); if(exporting) { if(fp->flags & FmtShort) @@ -1304,6 +1314,11 @@ Tpretty(Fmt *fp, Type *t) if(t->sym) return fmtprint(fp, "undefined %S", t->sym); return fmtprint(fp, "undefined"); + + case TUNSAFEPTR: + if(exporting) + return fmtprint(fp, "\"unsafe\".Pointer"); + return fmtprint(fp, "unsafe.Pointer"); } // Don't know how to handle - fall back to detailed prints. @@ -1346,6 +1361,9 @@ Tconv(Fmt *fp) } } + if(sharp || exporting) + fatal("missing %E case during export", t->etype); + et = t->etype; fmtprint(fp, "%E ", et); if(t->sym != S) @@ -1661,6 +1679,9 @@ isselect(Node *n) if(s == n->sym) return 1; s = pkglookup("selectrecv", runtimepkg); + if(s == n->sym) + return 1; + s = pkglookup("selectrecv2", runtimepkg); if(s == n->sym) return 1; s = pkglookup("selectdefault", runtimepkg); @@ -1864,7 +1885,7 @@ assignop(Type *src, Type *dst, char **why) if(why != nil) *why = ""; - if(safemode && (isptrto(src, TANY) || isptrto(dst, TANY))) { + if(safemode && src != T && src->etype == TUNSAFEPTR) { yyerror("cannot use unsafe.Pointer"); errorexit(); } @@ -1879,8 +1900,9 @@ assignop(Type *src, Type *dst, char **why) return OCONVNOP; // 2. src and dst have identical underlying types - // and either src or dst is not a named type. - if(eqtype(src->orig, dst->orig) && (src->sym == S || dst->sym == S)) + // and either src or dst is not a named type or + // both are interface types. + if(eqtype(src->orig, dst->orig) && (src->sym == S || dst->sym == S || src->etype == TINTER)) return OCONVNOP; // 3. dst is an interface type and src implements dst. @@ -2028,11 +2050,11 @@ convertop(Type *src, Type *dst, char **why) } // 8. src is a pointer or uintptr and dst is unsafe.Pointer. - if((isptr[src->etype] || src->etype == TUINTPTR) && isptrto(dst, TANY)) + if((isptr[src->etype] || src->etype == TUINTPTR) && dst->etype == TUNSAFEPTR) return OCONVNOP; // 9. src is unsafe.Pointer and dst is a pointer or uintptr. - if(isptrto(src, TANY) && (isptr[dst->etype] || dst->etype == TUINTPTR)) + if(src->etype == TUNSAFEPTR && (isptr[dst->etype] || dst->etype == TUINTPTR)) return OCONVNOP; return 0; @@ -2043,13 +2065,16 @@ Node* assignconv(Node *n, Type *t, char *context) { int op; - Node *r; + Node *r, *old; char *why; if(n == N || n->type == T) return n; + old = n; + old->diag++; // silence errors about n; we'll issue one below defaultlit(&n, t); + old->diag--; if(t->etype == TBLANK) return n; diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c index 3e8f35877..1cc5abd5c 100644 --- a/src/cmd/gc/typecheck.c +++ b/src/cmd/gc/typecheck.c @@ -318,7 +318,7 @@ reswitch: n->left = N; goto ret; } - if(!isptr[t->etype] || (t->type != T && t->type->etype == TANY) /* unsafe.Pointer */) { + if(!isptr[t->etype]) { yyerror("invalid indirect of %+N", n->left); goto error; } @@ -921,7 +921,6 @@ reswitch: n->type = t; goto ret; - case OCLOSED: case OCLOSE: if(onearg(n, "%#O", n->op) < 0) goto error; @@ -934,11 +933,7 @@ reswitch: yyerror("invalid operation: %#N (non-chan type %T)", n, t); goto error; } - if(n->op == OCLOSED) { - n->type = types[TBOOL]; - ok |= Erv; - } else - ok |= Etop; + ok |= Etop; goto ret; case OAPPEND: @@ -1316,7 +1311,7 @@ ret: // TODO(rsc): should not need to check importpkg, // but reflect mentions unsafe.Pointer. - if(safemode && !incannedimport && !importpkg && isptrto(t, TANY)) + if(safemode && !incannedimport && !importpkg && t && t->etype == TUNSAFEPTR) yyerror("cannot use unsafe.Pointer"); evconst(n); @@ -1639,11 +1634,6 @@ typecheckaste(int op, Node *call, int isddd, Type *tstruct, NodeList *nl, char * for(tl=tstruct->type; tl; tl=tl->down) { t = tl->type; if(tl->isddd) { - if(nl != nil && nl->n->op == ONAME && nl->n->isddd && !isddd) { - // TODO(rsc): This is not actually illegal, but it will help catch bugs. - yyerror("to pass '%#N' as ...%T, use '%#N...'", nl->n, t->type, nl->n); - isddd = 1; - } if(isddd) { if(nl == nil) goto notenough; @@ -2377,8 +2367,9 @@ typecheckas2(Node *n) n->op = OAS2MAPR; goto common; case ORECV: - yyerror("cannot use multiple-value assignment for non-blocking receive; use select"); - goto out; + n->op = OAS2RECV; + n->right = n->rlist->n; + goto common; case ODOTTYPE: n->op = OAS2DOTTYPE; r->op = ODOTTYPE2; diff --git a/src/cmd/gc/unsafe.go b/src/cmd/gc/unsafe.go index bd7b7771a..b2a341d39 100644 --- a/src/cmd/gc/unsafe.go +++ b/src/cmd/gc/unsafe.go @@ -8,7 +8,7 @@ package PACKAGE -type Pointer *any +type Pointer uintptr // not really; filled in by compiler func Offsetof(any) int func Sizeof(any) int diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c index b32b6fff5..b8c6842e0 100644 --- a/src/cmd/gc/walk.c +++ b/src/cmd/gc/walk.c @@ -403,12 +403,11 @@ walkstmt(Node **np) case OAS: case OAS2: case OAS2DOTTYPE: - case OAS2RECVCLOSED: + case OAS2RECV: case OAS2FUNC: case OAS2MAPW: case OAS2MAPR: case OCLOSE: - case OCLOSED: case OCOPY: case OCALLMETH: case OCALLINTER: @@ -822,14 +821,13 @@ walkexpr(Node **np, NodeList **init) n = liststmt(concat(concat(list1(r), ll), lpost)); goto ret; - case OAS2RECVCLOSED: - // a = <-c; b = closed(c) but atomic + case OAS2RECV: *init = concat(*init, n->ninit); n->ninit = nil; r = n->rlist->n; walkexprlistsafe(n->list, init); walkexpr(&r->left, init); - fn = chanfn("chanrecv3", 2, r->left->type); + fn = chanfn("chanrecv2", 2, r->left->type); r = mkcall1(fn, getoutargx(fn->type), init, r->left); n->rlist->n = r; n->op = OAS2FUNC; @@ -1309,13 +1307,6 @@ walkexpr(Node **np, NodeList **init) n = mkcall1(fn, T, init, n->left); goto ret; - case OCLOSED: - // cannot use chanfn - closechan takes any, not chan any - fn = syslook("closedchan", 1); - argtype(fn, n->left->type); - n = mkcall1(fn, n->type, init, n->left); - goto ret; - case OMAKECHAN: n = mkcall1(chanfn("makechan", 1, n->type), n->type, init, typename(n->type->type), diff --git a/src/cmd/godefs/stabs.c b/src/cmd/godefs/stabs.c index f2bb57eb6..30a05fc70 100644 --- a/src/cmd/godefs/stabs.c +++ b/src/cmd/godefs/stabs.c @@ -219,7 +219,7 @@ parsedef(char **pp, char *name) t = emalloc(sizeof *t); switch(*p) { default: - fprint(2, "unknown type char %c\n", *p); + fprint(2, "unknown type char %c in %s\n", *p, p); *pp = ""; return t; @@ -284,6 +284,7 @@ parsedef(char **pp, char *name) return nil; break; + case 'B': // volatile case 'k': // const ++*pp; return parsedef(pp, nil); diff --git a/src/cmd/godoc/codewalk.go b/src/cmd/godoc/codewalk.go index bda63a9f6..24087eb88 100644 --- a/src/cmd/godoc/codewalk.go +++ b/src/cmd/godoc/codewalk.go @@ -115,7 +115,7 @@ func (st *Codestep) String() string { // loadCodewalk reads a codewalk from the named XML file. func loadCodewalk(file string) (*Codewalk, os.Error) { - f, err := os.Open(file, os.O_RDONLY, 0) + f, err := os.Open(file) if err != nil { return nil, err } diff --git a/src/cmd/godoc/dirtrees.go b/src/cmd/godoc/dirtrees.go index 3ad7c8cfc..97737ca5a 100644 --- a/src/cmd/godoc/dirtrees.go +++ b/src/cmd/godoc/dirtrees.go @@ -266,8 +266,8 @@ func (dir *Directory) lookupLocal(name string) *Directory { // lookup looks for the *Directory for a given path, relative to dir. func (dir *Directory) lookup(path string) *Directory { - d := strings.Split(dir.Path, "/", -1) - p := strings.Split(path, "/", -1) + d := strings.Split(dir.Path, string(filepath.Separator), -1) + p := strings.Split(path, string(filepath.Separator), -1) i := 0 for i < len(d) { if i >= len(p) || d[i] != p[i] { @@ -342,8 +342,8 @@ func (root *Directory) listing(skipRoot bool) *DirList { if strings.HasPrefix(d.Path, root.Path) { path = d.Path[len(root.Path):] } - // remove trailing '/' if any - path must be relative - if len(path) > 0 && path[0] == '/' { + // remove trailing separator if any - path must be relative + if len(path) > 0 && path[0] == filepath.Separator { path = path[1:] } p.Path = path diff --git a/src/cmd/godoc/format.go b/src/cmd/godoc/format.go index da1466b21..7e6470846 100644 --- a/src/cmd/godoc/format.go +++ b/src/cmd/godoc/format.go @@ -292,7 +292,7 @@ func rangeSelection(str string) Selection { from, _ := strconv.Atoi(m[1]) to, _ := strconv.Atoi(m[2]) if from < to { - return makeSelection([][]int{[]int{from, to}}) + return makeSelection([][]int{{from, to}}) } } return nil @@ -309,7 +309,7 @@ func rangeSelection(str string) Selection { // var startTags = [][]byte{ /* 000 */ []byte(``), - /* 001 */ []byte(``), + /* 001 */ []byte(``), /* 010 */ []byte(``), /* 011 */ []byte(``), /* 100 */ []byte(``), diff --git a/src/cmd/godoc/godoc.go b/src/cmd/godoc/godoc.go index 9dce5edf9..b8e9dbc92 100644 --- a/src/cmd/godoc/godoc.go +++ b/src/cmd/godoc/godoc.go @@ -65,6 +65,7 @@ var ( tabwidth = flag.Int("tabwidth", 4, "tab width") showTimestamps = flag.Bool("timestamps", true, "show timestamps with directory listings") maxResults = flag.Int("maxresults", 10000, "maximum number of full text search results shown") + templateDir = flag.String("templates", "", "directory containing alternate template files") // file system mapping fsMap Mapping // user-defined mapping @@ -635,6 +636,14 @@ var fmap = template.FormatterMap{ func readTemplate(name string) *template.Template { path := filepath.Join(*goroot, "lib", "godoc", name) + if *templateDir != "" { + defaultpath := path + path = filepath.Join(*templateDir, name) + if _, err := os.Stat(path); err != nil { + log.Print("readTemplate:", err) + path = defaultpath + } + } data, err := ioutil.ReadFile(path) if err != nil { log.Fatalf("ReadFile %s: %v", path, err) @@ -702,7 +711,7 @@ func servePage(w http.ResponseWriter, title, subtitle, query string, content []b func serveText(w http.ResponseWriter, text []byte) { - w.SetHeader("Content-Type", "text/plain; charset=utf-8") + w.Header().Set("Content-Type", "text/plain; charset=utf-8") w.Write(text) } diff --git a/src/cmd/godoc/index.go b/src/cmd/godoc/index.go index 5af4d15cb..5938d0b74 100644 --- a/src/cmd/godoc/index.go +++ b/src/cmd/godoc/index.go @@ -624,7 +624,7 @@ func pkgName(filename string) string { // failed (that is, if the file was not added), it returns file == nil. func (x *Indexer) addFile(filename string, goFile bool) (file *token.File, ast *ast.File) { // open file - f, err := os.Open(filename, os.O_RDONLY, 0) + f, err := os.Open(filename) if err != nil { return } diff --git a/src/cmd/godoc/main.go b/src/cmd/godoc/main.go index 1ebb80279..e426626b3 100644 --- a/src/cmd/godoc/main.go +++ b/src/cmd/godoc/main.go @@ -83,7 +83,7 @@ func exec(rw http.ResponseWriter, args []string) (status int) { if *verbose { log.Printf("executing %v", args) } - p, err := os.StartProcess(bin, args, os.Environ(), *goroot, fds) + p, err := os.StartProcess(bin, args, &os.ProcAttr{Files: fds, Dir: *goroot}) defer r.Close() w.Close() if err != nil { @@ -111,7 +111,7 @@ func exec(rw http.ResponseWriter, args []string) (status int) { os.Stderr.Write(buf.Bytes()) } if rw != nil { - rw.SetHeader("content-type", "text/plain; charset=utf-8") + rw.Header().Set("Content-Type", "text/plain; charset=utf-8") rw.Write(buf.Bytes()) } @@ -152,7 +152,7 @@ func usage() { func loggingHandler(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - log.Printf("%s\t%s", w.RemoteAddr(), req.URL) + log.Printf("%s\t%s", req.RemoteAddr, req.URL) h.ServeHTTP(w, req) }) } @@ -222,6 +222,9 @@ func main() { flag.Usage = usage flag.Parse() + // Clean goroot: normalize path separator. + *goroot = filepath.Clean(*goroot) + // Check usage: either server and no args, or command line and args if (*httpAddr != "") != (flag.NArg() == 0) { usage() diff --git a/src/cmd/godoc/spec.go b/src/cmd/godoc/spec.go index a533c1e0a..f8b95e387 100644 --- a/src/cmd/godoc/spec.go +++ b/src/cmd/godoc/spec.go @@ -27,7 +27,7 @@ type ebnfParser struct { prev int // offset of previous token pos token.Pos // token position tok token.Token // one token look-ahead - lit []byte // token literal + lit string // token literal } @@ -63,7 +63,7 @@ func (p *ebnfParser) errorExpected(pos token.Pos, msg string) { // make the error message more specific msg += ", found '" + p.tok.String() + "'" if p.tok.IsLiteral() { - msg += " " + string(p.lit) + msg += " " + p.lit } } p.Error(p.file.Position(pos), msg) @@ -81,7 +81,7 @@ func (p *ebnfParser) expect(tok token.Token) token.Pos { func (p *ebnfParser) parseIdentifier(def bool) { - name := string(p.lit) + name := p.lit p.expect(token.IDENT) if def { fmt.Fprintf(p.out, `%s`, name, name) diff --git a/src/cmd/godoc/utils.go b/src/cmd/godoc/utils.go index 9517aee7a..593b51ce0 100644 --- a/src/cmd/godoc/utils.go +++ b/src/cmd/godoc/utils.go @@ -155,7 +155,7 @@ func isTextFile(filename string) bool { // the extension is not known; read an initial chunk // of the file and check if it looks like text - f, err := os.Open(filename, os.O_RDONLY, 0) + f, err := os.Open(filename) if err != nil { return false } diff --git a/src/cmd/gofix/Makefile b/src/cmd/gofix/Makefile new file mode 100644 index 000000000..12f09b4e4 --- /dev/null +++ b/src/cmd/gofix/Makefile @@ -0,0 +1,24 @@ +# Copyright 2011 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +include ../../Make.inc + +TARG=gofix +GOFILES=\ + fix.go\ + netdial.go\ + main.go\ + osopen.go\ + httpserver.go\ + procattr.go\ + reflect.go\ + typecheck.go\ + +include ../../Make.cmd + +test: + gotest + +testshort: + gotest -test.short diff --git a/src/cmd/gofix/doc.go b/src/cmd/gofix/doc.go new file mode 100644 index 000000000..902fe76f2 --- /dev/null +++ b/src/cmd/gofix/doc.go @@ -0,0 +1,34 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Gofix finds Go programs that use old APIs and rewrites them to use +newer ones. After you update to a new Go release, gofix helps make +the necessary changes to your programs. + +Usage: + gofix [-r name,...] [path ...] + +Without an explicit path, gofix reads standard input and writes the +result to standard output. + +If the named path is a file, gofix rewrites the named files in place. +If the named path is a directory, gofix rewrites all .go files in that +directory tree. When gofix rewrites a file, it prints a line to standard +error giving the name of the file and the rewrite applied. + +The -r flag restricts the set of rewrites considered to those in the +named list. By default gofix considers all known rewrites. Gofix's +rewrites are idempotent, so that it is safe to apply gofix to updated +or partially updated code even without using the -r flag. + +Gofix prints the full list of fixes it can apply in its help output; +to see them, run gofix -?. + +Gofix does not make backup copies of the files that it edits. +Instead, use a version control system's ``diff'' functionality to inspect +the changes that gofix makes before committing them. + +*/ +package documentation diff --git a/src/cmd/gofix/fix.go b/src/cmd/gofix/fix.go new file mode 100644 index 000000000..0852ce21e --- /dev/null +++ b/src/cmd/gofix/fix.go @@ -0,0 +1,422 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "go/ast" + "go/token" + "os" + "strconv" +) + +type fix struct { + name string + f func(*ast.File) bool + desc string +} + +// main runs sort.Sort(fixes) after init process is done. +type fixlist []fix + +func (f fixlist) Len() int { return len(f) } +func (f fixlist) Swap(i, j int) { f[i], f[j] = f[j], f[i] } +func (f fixlist) Less(i, j int) bool { return f[i].name < f[j].name } + +var fixes fixlist + +func register(f fix) { + fixes = append(fixes, f) +} + +// walk traverses the AST x, calling visit(y) for each node y in the tree but +// also with a pointer to each ast.Expr, ast.Stmt, and *ast.BlockStmt, +// in a bottom-up traversal. +func walk(x interface{}, visit func(interface{})) { + walkBeforeAfter(x, nop, visit) +} + +func nop(interface{}) {} + +// walkBeforeAfter is like walk but calls before(x) before traversing +// x's children and after(x) afterward. +func walkBeforeAfter(x interface{}, before, after func(interface{})) { + before(x) + + switch n := x.(type) { + default: + panic(fmt.Errorf("unexpected type %T in walkBeforeAfter", x)) + + case nil: + + // pointers to interfaces + case *ast.Decl: + walkBeforeAfter(*n, before, after) + case *ast.Expr: + walkBeforeAfter(*n, before, after) + case *ast.Spec: + walkBeforeAfter(*n, before, after) + case *ast.Stmt: + walkBeforeAfter(*n, before, after) + + // pointers to struct pointers + case **ast.BlockStmt: + walkBeforeAfter(*n, before, after) + case **ast.CallExpr: + walkBeforeAfter(*n, before, after) + case **ast.FieldList: + walkBeforeAfter(*n, before, after) + case **ast.FuncType: + walkBeforeAfter(*n, before, after) + + // pointers to slices + case *[]ast.Stmt: + walkBeforeAfter(*n, before, after) + case *[]ast.Expr: + walkBeforeAfter(*n, before, after) + case *[]ast.Decl: + walkBeforeAfter(*n, before, after) + case *[]ast.Spec: + walkBeforeAfter(*n, before, after) + case *[]*ast.File: + walkBeforeAfter(*n, before, after) + + // These are ordered and grouped to match ../../pkg/go/ast/ast.go + case *ast.Field: + walkBeforeAfter(&n.Type, before, after) + case *ast.FieldList: + for _, field := range n.List { + walkBeforeAfter(field, before, after) + } + case *ast.BadExpr: + case *ast.Ident: + case *ast.Ellipsis: + case *ast.BasicLit: + case *ast.FuncLit: + walkBeforeAfter(&n.Type, before, after) + walkBeforeAfter(&n.Body, before, after) + case *ast.CompositeLit: + walkBeforeAfter(&n.Type, before, after) + walkBeforeAfter(&n.Elts, before, after) + case *ast.ParenExpr: + walkBeforeAfter(&n.X, before, after) + case *ast.SelectorExpr: + walkBeforeAfter(&n.X, before, after) + case *ast.IndexExpr: + walkBeforeAfter(&n.X, before, after) + walkBeforeAfter(&n.Index, before, after) + case *ast.SliceExpr: + walkBeforeAfter(&n.X, before, after) + if n.Low != nil { + walkBeforeAfter(&n.Low, before, after) + } + if n.High != nil { + walkBeforeAfter(&n.High, before, after) + } + case *ast.TypeAssertExpr: + walkBeforeAfter(&n.X, before, after) + walkBeforeAfter(&n.Type, before, after) + case *ast.CallExpr: + walkBeforeAfter(&n.Fun, before, after) + walkBeforeAfter(&n.Args, before, after) + case *ast.StarExpr: + walkBeforeAfter(&n.X, before, after) + case *ast.UnaryExpr: + walkBeforeAfter(&n.X, before, after) + case *ast.BinaryExpr: + walkBeforeAfter(&n.X, before, after) + walkBeforeAfter(&n.Y, before, after) + case *ast.KeyValueExpr: + walkBeforeAfter(&n.Key, before, after) + walkBeforeAfter(&n.Value, before, after) + + case *ast.ArrayType: + walkBeforeAfter(&n.Len, before, after) + walkBeforeAfter(&n.Elt, before, after) + case *ast.StructType: + walkBeforeAfter(&n.Fields, before, after) + case *ast.FuncType: + walkBeforeAfter(&n.Params, before, after) + if n.Results != nil { + walkBeforeAfter(&n.Results, before, after) + } + case *ast.InterfaceType: + walkBeforeAfter(&n.Methods, before, after) + case *ast.MapType: + walkBeforeAfter(&n.Key, before, after) + walkBeforeAfter(&n.Value, before, after) + case *ast.ChanType: + walkBeforeAfter(&n.Value, before, after) + + case *ast.BadStmt: + case *ast.DeclStmt: + walkBeforeAfter(&n.Decl, before, after) + case *ast.EmptyStmt: + case *ast.LabeledStmt: + walkBeforeAfter(&n.Stmt, before, after) + case *ast.ExprStmt: + walkBeforeAfter(&n.X, before, after) + case *ast.SendStmt: + walkBeforeAfter(&n.Chan, before, after) + walkBeforeAfter(&n.Value, before, after) + case *ast.IncDecStmt: + walkBeforeAfter(&n.X, before, after) + case *ast.AssignStmt: + walkBeforeAfter(&n.Lhs, before, after) + walkBeforeAfter(&n.Rhs, before, after) + case *ast.GoStmt: + walkBeforeAfter(&n.Call, before, after) + case *ast.DeferStmt: + walkBeforeAfter(&n.Call, before, after) + case *ast.ReturnStmt: + walkBeforeAfter(&n.Results, before, after) + case *ast.BranchStmt: + case *ast.BlockStmt: + walkBeforeAfter(&n.List, before, after) + case *ast.IfStmt: + walkBeforeAfter(&n.Init, before, after) + walkBeforeAfter(&n.Cond, before, after) + walkBeforeAfter(&n.Body, before, after) + walkBeforeAfter(&n.Else, before, after) + case *ast.CaseClause: + walkBeforeAfter(&n.List, before, after) + walkBeforeAfter(&n.Body, before, after) + case *ast.SwitchStmt: + walkBeforeAfter(&n.Init, before, after) + walkBeforeAfter(&n.Tag, before, after) + walkBeforeAfter(&n.Body, before, after) + case *ast.TypeSwitchStmt: + walkBeforeAfter(&n.Init, before, after) + walkBeforeAfter(&n.Assign, before, after) + walkBeforeAfter(&n.Body, before, after) + case *ast.CommClause: + walkBeforeAfter(&n.Comm, before, after) + walkBeforeAfter(&n.Body, before, after) + case *ast.SelectStmt: + walkBeforeAfter(&n.Body, before, after) + case *ast.ForStmt: + walkBeforeAfter(&n.Init, before, after) + walkBeforeAfter(&n.Cond, before, after) + walkBeforeAfter(&n.Post, before, after) + walkBeforeAfter(&n.Body, before, after) + case *ast.RangeStmt: + walkBeforeAfter(&n.Key, before, after) + walkBeforeAfter(&n.Value, before, after) + walkBeforeAfter(&n.X, before, after) + walkBeforeAfter(&n.Body, before, after) + + case *ast.ImportSpec: + case *ast.ValueSpec: + walkBeforeAfter(&n.Type, before, after) + walkBeforeAfter(&n.Values, before, after) + case *ast.TypeSpec: + walkBeforeAfter(&n.Type, before, after) + + case *ast.BadDecl: + case *ast.GenDecl: + walkBeforeAfter(&n.Specs, before, after) + case *ast.FuncDecl: + if n.Recv != nil { + walkBeforeAfter(&n.Recv, before, after) + } + walkBeforeAfter(&n.Type, before, after) + if n.Body != nil { + walkBeforeAfter(&n.Body, before, after) + } + + case *ast.File: + walkBeforeAfter(&n.Decls, before, after) + + case *ast.Package: + walkBeforeAfter(&n.Files, before, after) + + case []*ast.File: + for i := range n { + walkBeforeAfter(&n[i], before, after) + } + case []ast.Decl: + for i := range n { + walkBeforeAfter(&n[i], before, after) + } + case []ast.Expr: + for i := range n { + walkBeforeAfter(&n[i], before, after) + } + case []ast.Stmt: + for i := range n { + walkBeforeAfter(&n[i], before, after) + } + case []ast.Spec: + for i := range n { + walkBeforeAfter(&n[i], before, after) + } + } + after(x) +} + +// imports returns true if f imports path. +func imports(f *ast.File, path string) bool { + for _, s := range f.Imports { + t, err := strconv.Unquote(s.Path.Value) + if err == nil && t == path { + return true + } + } + return false +} + +// isPkgDot returns true if t is the expression "pkg.name" +// where pkg is an imported identifier. +func isPkgDot(t ast.Expr, pkg, name string) bool { + sel, ok := t.(*ast.SelectorExpr) + return ok && isTopName(sel.X, pkg) && sel.Sel.String() == name +} + +// isPtrPkgDot returns true if f is the expression "*pkg.name" +// where pkg is an imported identifier. +func isPtrPkgDot(t ast.Expr, pkg, name string) bool { + ptr, ok := t.(*ast.StarExpr) + return ok && isPkgDot(ptr.X, pkg, name) +} + +// isTopName returns true if n is a top-level unresolved identifier with the given name. +func isTopName(n ast.Expr, name string) bool { + id, ok := n.(*ast.Ident) + return ok && id.Name == name && id.Obj == nil +} + +// isName returns true if n is an identifier with the given name. +func isName(n ast.Expr, name string) bool { + id, ok := n.(*ast.Ident) + return ok && id.String() == name +} + +// isCall returns true if t is a call to pkg.name. +func isCall(t ast.Expr, pkg, name string) bool { + call, ok := t.(*ast.CallExpr) + return ok && isPkgDot(call.Fun, pkg, name) +} + +// If n is an *ast.Ident, isIdent returns it; otherwise isIdent returns nil. +func isIdent(n interface{}) *ast.Ident { + id, _ := n.(*ast.Ident) + return id +} + +// refersTo returns true if n is a reference to the same object as x. +func refersTo(n ast.Node, x *ast.Ident) bool { + id, ok := n.(*ast.Ident) + // The test of id.Name == x.Name handles top-level unresolved + // identifiers, which all have Obj == nil. + return ok && id.Obj == x.Obj && id.Name == x.Name +} + +// isBlank returns true if n is the blank identifier. +func isBlank(n ast.Expr) bool { + return isName(n, "_") +} + +// isEmptyString returns true if n is an empty string literal. +func isEmptyString(n ast.Expr) bool { + lit, ok := n.(*ast.BasicLit) + return ok && lit.Kind == token.STRING && len(lit.Value) == 2 +} + +func warn(pos token.Pos, msg string, args ...interface{}) { + if pos.IsValid() { + msg = "%s: " + msg + arg1 := []interface{}{fset.Position(pos).String()} + args = append(arg1, args...) + } + fmt.Fprintf(os.Stderr, msg+"\n", args...) +} + +// countUses returns the number of uses of the identifier x in scope. +func countUses(x *ast.Ident, scope []ast.Stmt) int { + count := 0 + ff := func(n interface{}) { + if n, ok := n.(ast.Node); ok && refersTo(n, x) { + count++ + } + } + for _, n := range scope { + walk(n, ff) + } + return count +} + +// rewriteUses replaces all uses of the identifier x and !x in scope +// with f(x.Pos()) and fnot(x.Pos()). +func rewriteUses(x *ast.Ident, f, fnot func(token.Pos) ast.Expr, scope []ast.Stmt) { + var lastF ast.Expr + ff := func(n interface{}) { + ptr, ok := n.(*ast.Expr) + if !ok { + return + } + nn := *ptr + + // The child node was just walked and possibly replaced. + // If it was replaced and this is a negation, replace with fnot(p). + not, ok := nn.(*ast.UnaryExpr) + if ok && not.Op == token.NOT && not.X == lastF { + *ptr = fnot(nn.Pos()) + return + } + if refersTo(nn, x) { + lastF = f(nn.Pos()) + *ptr = lastF + } + } + for _, n := range scope { + walk(n, ff) + } +} + +// assignsTo returns true if any of the code in scope assigns to or takes the address of x. +func assignsTo(x *ast.Ident, scope []ast.Stmt) bool { + assigned := false + ff := func(n interface{}) { + if assigned { + return + } + switch n := n.(type) { + case *ast.UnaryExpr: + // use of &x + if n.Op == token.AND && refersTo(n.X, x) { + assigned = true + return + } + case *ast.AssignStmt: + for _, l := range n.Lhs { + if refersTo(l, x) { + assigned = true + return + } + } + } + } + for _, n := range scope { + if assigned { + break + } + walk(n, ff) + } + return assigned +} + +// newPkgDot returns an ast.Expr referring to "pkg.name" at position pos. +func newPkgDot(pos token.Pos, pkg, name string) ast.Expr { + return &ast.SelectorExpr{ + X: &ast.Ident{ + NamePos: pos, + Name: pkg, + }, + Sel: &ast.Ident{ + NamePos: pos, + Name: name, + }, + } +} diff --git a/src/cmd/gofix/httpserver.go b/src/cmd/gofix/httpserver.go new file mode 100644 index 000000000..37866e88b --- /dev/null +++ b/src/cmd/gofix/httpserver.go @@ -0,0 +1,140 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "go/ast" + "go/token" +) + +var httpserverFix = fix{ + "httpserver", + httpserver, + `Adapt http server methods and functions to changes +made to the http ResponseWriter interface. + +http://codereview.appspot.com/4245064 Hijacker +http://codereview.appspot.com/4239076 Header +http://codereview.appspot.com/4239077 Flusher +http://codereview.appspot.com/4248075 RemoteAddr, UsingTLS +`, +} + +func init() { + register(httpserverFix) +} + +func httpserver(f *ast.File) bool { + if !imports(f, "http") { + return false + } + + fixed := false + for _, decl := range f.Decls { + fn, ok := decl.(*ast.FuncDecl) + if !ok { + continue + } + w, req, ok := isServeHTTP(fn) + if !ok { + continue + } + walk(fn.Body, func(n interface{}) { + // Want to replace expression sometimes, + // so record pointer to it for updating below. + ptr, ok := n.(*ast.Expr) + if ok { + n = *ptr + } + + // Look for w.UsingTLS() and w.Remoteaddr(). + call, ok := n.(*ast.CallExpr) + if !ok || (len(call.Args) != 0 && len(call.Args) != 2) { + return + } + sel, ok := call.Fun.(*ast.SelectorExpr) + if !ok { + return + } + if !refersTo(sel.X, w) { + return + } + switch sel.Sel.String() { + case "Hijack": + // replace w with w.(http.Hijacker) + sel.X = &ast.TypeAssertExpr{ + X: sel.X, + Type: ast.NewIdent("http.Hijacker"), + } + fixed = true + case "Flush": + // replace w with w.(http.Flusher) + sel.X = &ast.TypeAssertExpr{ + X: sel.X, + Type: ast.NewIdent("http.Flusher"), + } + fixed = true + case "UsingTLS": + if ptr == nil { + // can only replace expression if we have pointer to it + break + } + // replace with req.TLS != nil + *ptr = &ast.BinaryExpr{ + X: &ast.SelectorExpr{ + X: ast.NewIdent(req.String()), + Sel: ast.NewIdent("TLS"), + }, + Op: token.NEQ, + Y: ast.NewIdent("nil"), + } + fixed = true + case "RemoteAddr": + if ptr == nil { + // can only replace expression if we have pointer to it + break + } + // replace with req.RemoteAddr + *ptr = &ast.SelectorExpr{ + X: ast.NewIdent(req.String()), + Sel: ast.NewIdent("RemoteAddr"), + } + fixed = true + case "SetHeader": + // replace w.SetHeader with w.Header().Set + // or w.Header().Del if second argument is "" + sel.X = &ast.CallExpr{ + Fun: &ast.SelectorExpr{ + X: ast.NewIdent(w.String()), + Sel: ast.NewIdent("Header"), + }, + } + sel.Sel = ast.NewIdent("Set") + if len(call.Args) == 2 && isEmptyString(call.Args[1]) { + sel.Sel = ast.NewIdent("Del") + call.Args = call.Args[:1] + } + fixed = true + } + }) + } + return fixed +} + +func isServeHTTP(fn *ast.FuncDecl) (w, req *ast.Ident, ok bool) { + for _, field := range fn.Type.Params.List { + if isPkgDot(field.Type, "http", "ResponseWriter") { + w = field.Names[0] + continue + } + if isPtrPkgDot(field.Type, "http", "Request") { + req = field.Names[0] + continue + } + } + + ok = w != nil && req != nil + return +} diff --git a/src/cmd/gofix/httpserver_test.go b/src/cmd/gofix/httpserver_test.go new file mode 100644 index 000000000..89bb4fa71 --- /dev/null +++ b/src/cmd/gofix/httpserver_test.go @@ -0,0 +1,53 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func init() { + addTestCases(httpserverTests) +} + +var httpserverTests = []testCase{ + { + Name: "httpserver.0", + In: `package main + +import "http" + +func f(xyz http.ResponseWriter, abc *http.Request, b string) { + xyz.SetHeader("foo", "bar") + xyz.SetHeader("baz", "") + xyz.Hijack() + xyz.Flush() + go xyz.Hijack() + defer xyz.Flush() + _ = xyz.UsingTLS() + _ = true == xyz.UsingTLS() + _ = xyz.RemoteAddr() + _ = xyz.RemoteAddr() == "hello" + if xyz.UsingTLS() { + } +} +`, + Out: `package main + +import "http" + +func f(xyz http.ResponseWriter, abc *http.Request, b string) { + xyz.Header().Set("foo", "bar") + xyz.Header().Del("baz") + xyz.(http.Hijacker).Hijack() + xyz.(http.Flusher).Flush() + go xyz.(http.Hijacker).Hijack() + defer xyz.(http.Flusher).Flush() + _ = abc.TLS != nil + _ = true == (abc.TLS != nil) + _ = abc.RemoteAddr + _ = abc.RemoteAddr == "hello" + if abc.TLS != nil { + } +} +`, + }, +} diff --git a/src/cmd/gofix/main.go b/src/cmd/gofix/main.go new file mode 100644 index 000000000..4f7e923e3 --- /dev/null +++ b/src/cmd/gofix/main.go @@ -0,0 +1,264 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "bytes" + "exec" + "flag" + "fmt" + "go/parser" + "go/printer" + "go/scanner" + "go/token" + "io/ioutil" + "os" + "path/filepath" + "sort" + "strings" +) + +var ( + fset = token.NewFileSet() + exitCode = 0 +) + +var allowedRewrites = flag.String("r", "", + "restrict the rewrites to this comma-separated list") + +var allowed map[string]bool + +var doDiff = flag.Bool("diff", false, "display diffs instead of rewriting files") + +func usage() { + fmt.Fprintf(os.Stderr, "usage: gofix [-diff] [-r fixname,...] [path ...]\n") + flag.PrintDefaults() + fmt.Fprintf(os.Stderr, "\nAvailable rewrites are:\n") + for _, f := range fixes { + fmt.Fprintf(os.Stderr, "\n%s\n", f.name) + desc := strings.TrimSpace(f.desc) + desc = strings.Replace(desc, "\n", "\n\t", -1) + fmt.Fprintf(os.Stderr, "\t%s\n", desc) + } + os.Exit(2) +} + +func main() { + sort.Sort(fixes) + + flag.Usage = usage + flag.Parse() + + if *allowedRewrites != "" { + allowed = make(map[string]bool) + for _, f := range strings.Split(*allowedRewrites, ",", -1) { + allowed[f] = true + } + } + + if flag.NArg() == 0 { + if err := processFile("standard input", true); err != nil { + report(err) + } + os.Exit(exitCode) + } + + for i := 0; i < flag.NArg(); i++ { + path := flag.Arg(i) + switch dir, err := os.Stat(path); { + case err != nil: + report(err) + case dir.IsRegular(): + if err := processFile(path, false); err != nil { + report(err) + } + case dir.IsDirectory(): + walkDir(path) + } + } + + os.Exit(exitCode) +} + +const ( + tabWidth = 8 + parserMode = parser.ParseComments + printerMode = printer.TabIndent | printer.UseSpaces +) + +var printConfig = &printer.Config{ + printerMode, + tabWidth, +} + +func processFile(filename string, useStdin bool) os.Error { + var f *os.File + var err os.Error + var fixlog bytes.Buffer + var buf bytes.Buffer + + if useStdin { + f = os.Stdin + } else { + f, err = os.Open(filename) + if err != nil { + return err + } + defer f.Close() + } + + src, err := ioutil.ReadAll(f) + if err != nil { + return err + } + + file, err := parser.ParseFile(fset, filename, src, parserMode) + if err != nil { + return err + } + + // Apply all fixes to file. + newFile := file + fixed := false + for _, fix := range fixes { + if allowed != nil && !allowed[fix.desc] { + continue + } + if fix.f(newFile) { + fixed = true + fmt.Fprintf(&fixlog, " %s", fix.name) + + // AST changed. + // Print and parse, to update any missing scoping + // or position information for subsequent fixers. + buf.Reset() + _, err = printConfig.Fprint(&buf, fset, newFile) + if err != nil { + return err + } + newSrc := buf.Bytes() + newFile, err = parser.ParseFile(fset, filename, newSrc, parserMode) + if err != nil { + return err + } + } + } + if !fixed { + return nil + } + fmt.Fprintf(os.Stderr, "%s: fixed %s\n", filename, fixlog.String()[1:]) + + // Print AST. We did that after each fix, so this appears + // redundant, but it is necessary to generate gofmt-compatible + // source code in a few cases. The official gofmt style is the + // output of the printer run on a standard AST generated by the parser, + // but the source we generated inside the loop above is the + // output of the printer run on a mangled AST generated by a fixer. + buf.Reset() + _, err = printConfig.Fprint(&buf, fset, newFile) + if err != nil { + return err + } + newSrc := buf.Bytes() + + if *doDiff { + data, err := diff(src, newSrc) + if err != nil { + return fmt.Errorf("computing diff: %s", err) + } + fmt.Printf("diff %s fixed/%s\n", filename, filename) + os.Stdout.Write(data) + return nil + } + + if useStdin { + os.Stdout.Write(newSrc) + return nil + } + + return ioutil.WriteFile(f.Name(), newSrc, 0) +} + +var gofmtBuf bytes.Buffer + +func gofmt(n interface{}) string { + gofmtBuf.Reset() + _, err := printConfig.Fprint(&gofmtBuf, fset, n) + if err != nil { + return "<" + err.String() + ">" + } + return gofmtBuf.String() +} + +func report(err os.Error) { + scanner.PrintError(os.Stderr, err) + exitCode = 2 +} + +func walkDir(path string) { + v := make(fileVisitor) + go func() { + filepath.Walk(path, v, v) + close(v) + }() + for err := range v { + if err != nil { + report(err) + } + } +} + +type fileVisitor chan os.Error + +func (v fileVisitor) VisitDir(path string, f *os.FileInfo) bool { + return true +} + +func (v fileVisitor) VisitFile(path string, f *os.FileInfo) { + if isGoFile(f) { + v <- nil // synchronize error handler + if err := processFile(path, false); err != nil { + v <- err + } + } +} + +func isGoFile(f *os.FileInfo) bool { + // ignore non-Go files + return f.IsRegular() && !strings.HasPrefix(f.Name, ".") && strings.HasSuffix(f.Name, ".go") +} + +func diff(b1, b2 []byte) (data []byte, err os.Error) { + f1, err := ioutil.TempFile("", "gofix") + if err != nil { + return nil, err + } + defer os.Remove(f1.Name()) + defer f1.Close() + + f2, err := ioutil.TempFile("", "gofix") + if err != nil { + return nil, err + } + defer os.Remove(f2.Name()) + defer f2.Close() + + f1.Write(b1) + f2.Write(b2) + + diffcmd, err := exec.LookPath("diff") + if err != nil { + return nil, err + } + + c, err := exec.Run(diffcmd, []string{"diff", f1.Name(), f2.Name()}, nil, "", + exec.DevNull, exec.Pipe, exec.MergeWithStdout) + if err != nil { + return nil, err + } + defer c.Close() + + return ioutil.ReadAll(c.Stdout) +} diff --git a/src/cmd/gofix/main_test.go b/src/cmd/gofix/main_test.go new file mode 100644 index 000000000..275778e5b --- /dev/null +++ b/src/cmd/gofix/main_test.go @@ -0,0 +1,126 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "bytes" + "go/ast" + "go/parser" + "go/printer" + "strings" + "testing" +) + +type testCase struct { + Name string + Fn func(*ast.File) bool + In string + Out string +} + +var testCases []testCase + +func addTestCases(t []testCase) { + testCases = append(testCases, t...) +} + +func fnop(*ast.File) bool { return false } + +func parseFixPrint(t *testing.T, fn func(*ast.File) bool, desc, in string) (out string, fixed, ok bool) { + file, err := parser.ParseFile(fset, desc, in, parserMode) + if err != nil { + t.Errorf("%s: parsing: %v", desc, err) + return + } + + var buf bytes.Buffer + buf.Reset() + _, err = (&printer.Config{printerMode, tabWidth}).Fprint(&buf, fset, file) + if err != nil { + t.Errorf("%s: printing: %v", desc, err) + return + } + if s := buf.String(); in != s && fn != fnop { + t.Errorf("%s: not gofmt-formatted.\n--- %s\n%s\n--- %s | gofmt\n%s", + desc, desc, in, desc, s) + tdiff(t, in, s) + return + } + + if fn == nil { + for _, fix := range fixes { + if fix.f(file) { + fixed = true + } + } + } else { + fixed = fn(file) + } + + buf.Reset() + _, err = (&printer.Config{printerMode, tabWidth}).Fprint(&buf, fset, file) + if err != nil { + t.Errorf("%s: printing: %v", desc, err) + return + } + + return buf.String(), fixed, true +} + +func TestRewrite(t *testing.T) { + for _, tt := range testCases { + // Apply fix: should get tt.Out. + out, fixed, ok := parseFixPrint(t, tt.Fn, tt.Name, tt.In) + if !ok { + continue + } + + // reformat to get printing right + out, _, ok = parseFixPrint(t, fnop, tt.Name, out) + if !ok { + continue + } + + if out != tt.Out { + t.Errorf("%s: incorrect output.\n", tt.Name) + if !strings.HasPrefix(tt.Name, "testdata/") { + t.Errorf("--- have\n%s\n--- want\n%s", out, tt.Out) + } + tdiff(t, out, tt.Out) + continue + } + + if changed := out != tt.In; changed != fixed { + t.Errorf("%s: changed=%v != fixed=%v", tt.Name, changed, fixed) + continue + } + + // Should not change if run again. + out2, fixed2, ok := parseFixPrint(t, tt.Fn, tt.Name+" output", out) + if !ok { + continue + } + + if fixed2 { + t.Errorf("%s: applied fixes during second round", tt.Name) + continue + } + + if out2 != out { + t.Errorf("%s: changed output after second round of fixes.\n--- output after first round\n%s\n--- output after second round\n%s", + tt.Name, out, out2) + tdiff(t, out, out2) + } + } +} + +func tdiff(t *testing.T, a, b string) { + data, err := diff([]byte(a), []byte(b)) + if err != nil { + t.Error(err) + return + } + t.Error(string(data)) +} diff --git a/src/cmd/gofix/netdial.go b/src/cmd/gofix/netdial.go new file mode 100644 index 000000000..afa98953b --- /dev/null +++ b/src/cmd/gofix/netdial.go @@ -0,0 +1,114 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "go/ast" +) + +var netdialFix = fix{ + "netdial", + netdial, + `Adapt 3-argument calls of net.Dial to use 2-argument form. + +http://codereview.appspot.com/4244055 +`, +} + +var tlsdialFix = fix{ + "tlsdial", + tlsdial, + `Adapt 4-argument calls of tls.Dial to use 3-argument form. + +http://codereview.appspot.com/4244055 +`, +} + +var netlookupFix = fix{ + "netlookup", + netlookup, + `Adapt 3-result calls to net.LookupHost to use 2-result form. + +http://codereview.appspot.com/4244055 +`, +} + +func init() { + register(netdialFix) + register(tlsdialFix) + register(netlookupFix) +} + +func netdial(f *ast.File) bool { + if !imports(f, "net") { + return false + } + + fixed := false + walk(f, func(n interface{}) { + call, ok := n.(*ast.CallExpr) + if !ok || !isPkgDot(call.Fun, "net", "Dial") || len(call.Args) != 3 { + return + } + // net.Dial(a, "", b) -> net.Dial(a, b) + if !isEmptyString(call.Args[1]) { + warn(call.Pos(), "call to net.Dial with non-empty second argument") + return + } + call.Args[1] = call.Args[2] + call.Args = call.Args[:2] + fixed = true + }) + return fixed +} + +func tlsdial(f *ast.File) bool { + if !imports(f, "crypto/tls") { + return false + } + + fixed := false + walk(f, func(n interface{}) { + call, ok := n.(*ast.CallExpr) + if !ok || !isPkgDot(call.Fun, "tls", "Dial") || len(call.Args) != 4 { + return + } + // tls.Dial(a, "", b, c) -> tls.Dial(a, b, c) + if !isEmptyString(call.Args[1]) { + warn(call.Pos(), "call to tls.Dial with non-empty second argument") + return + } + call.Args[1] = call.Args[2] + call.Args[2] = call.Args[3] + call.Args = call.Args[:3] + fixed = true + }) + return fixed +} + +func netlookup(f *ast.File) bool { + if !imports(f, "net") { + return false + } + + fixed := false + walk(f, func(n interface{}) { + as, ok := n.(*ast.AssignStmt) + if !ok || len(as.Lhs) != 3 || len(as.Rhs) != 1 { + return + } + call, ok := as.Rhs[0].(*ast.CallExpr) + if !ok || !isPkgDot(call.Fun, "net", "LookupHost") { + return + } + if !isBlank(as.Lhs[2]) { + warn(as.Pos(), "call to net.LookupHost expecting cname; use net.LookupCNAME") + return + } + as.Lhs = as.Lhs[:2] + fixed = true + }) + return fixed +} diff --git a/src/cmd/gofix/netdial_test.go b/src/cmd/gofix/netdial_test.go new file mode 100644 index 000000000..272aa526a --- /dev/null +++ b/src/cmd/gofix/netdial_test.go @@ -0,0 +1,51 @@ +package main + +func init() { + addTestCases(netdialTests) +} + +var netdialTests = []testCase{ + { + Name: "netdial.0", + In: `package main + +import "net" + +func f() { + c, err := net.Dial(net, "", addr) + c, err = net.Dial(net, "", addr) +} +`, + Out: `package main + +import "net" + +func f() { + c, err := net.Dial(net, addr) + c, err = net.Dial(net, addr) +} +`, + }, + + { + Name: "netlookup.0", + In: `package main + +import "net" + +func f() { + foo, bar, _ := net.LookupHost(host) + foo, bar, _ = net.LookupHost(host) +} +`, + Out: `package main + +import "net" + +func f() { + foo, bar := net.LookupHost(host) + foo, bar = net.LookupHost(host) +} +`, + }, +} diff --git a/src/cmd/gofix/osopen.go b/src/cmd/gofix/osopen.go new file mode 100644 index 000000000..8eb5d0655 --- /dev/null +++ b/src/cmd/gofix/osopen.go @@ -0,0 +1,122 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "go/ast" +) + +var osopenFix = fix{ + "osopen", + osopen, + `Adapt os.Open calls to new, easier API and rename O_CREAT O_CREATE. + + http://codereview.appspot.com/4357052 +`, +} + +func init() { + register(osopenFix) +} + +func osopen(f *ast.File) bool { + if !imports(f, "os") { + return false + } + + fixed := false + walk(f, func(n interface{}) { + // Rename O_CREAT to O_CREATE. + if expr, ok := n.(ast.Expr); ok && isPkgDot(expr, "os", "O_CREAT") { + expr.(*ast.SelectorExpr).Sel.Name = "O_CREATE" + return + } + + // Fix up calls to Open. + call, ok := n.(*ast.CallExpr) + if !ok || len(call.Args) != 3 { + return + } + if !isPkgDot(call.Fun, "os", "Open") { + return + } + sel := call.Fun.(*ast.SelectorExpr) + args := call.Args + // os.Open(a, os.O_RDONLY, c) -> os.Open(a) + if isPkgDot(args[1], "os", "O_RDONLY") || isPkgDot(args[1], "syscall", "O_RDONLY") { + call.Args = call.Args[0:1] + fixed = true + return + } + // os.Open(a, createlike_flags, c) -> os.Create(a, c) + if isCreateFlag(args[1]) { + sel.Sel.Name = "Create" + if !isSimplePerm(args[2]) { + warn(sel.Pos(), "rewrote os.Open to os.Create with permission not 0666") + } + call.Args = args[0:1] + fixed = true + return + } + // Fallback: os.Open(a, b, c) -> os.OpenFile(a, b, c) + sel.Sel.Name = "OpenFile" + fixed = true + }) + return fixed +} + +func isCreateFlag(flag ast.Expr) bool { + foundCreate := false + foundTrunc := false + // OR'ing of flags: is O_CREATE on? + or | would be fine; we just look for os.O_CREATE + // and don't worry about the actual operator. + p := flag.Pos() + for { + lhs := flag + expr, isBinary := flag.(*ast.BinaryExpr) + if isBinary { + lhs = expr.Y + } + sel, ok := lhs.(*ast.SelectorExpr) + if !ok || !isTopName(sel.X, "os") { + return false + } + switch sel.Sel.Name { + case "O_CREATE": + foundCreate = true + case "O_TRUNC": + foundTrunc = true + case "O_RDONLY", "O_WRONLY", "O_RDWR": + // okay + default: + // Unexpected flag, like O_APPEND or O_EXCL. + // Be conservative and do not rewrite. + return false + } + if !isBinary { + break + } + flag = expr.X + } + if !foundCreate { + return false + } + if !foundTrunc { + warn(p, "rewrote os.Open with O_CREATE but not O_TRUNC to os.Create") + } + return foundCreate +} + +func isSimplePerm(perm ast.Expr) bool { + basicLit, ok := perm.(*ast.BasicLit) + if !ok { + return false + } + switch basicLit.Value { + case "0666": + return true + } + return false +} diff --git a/src/cmd/gofix/osopen_test.go b/src/cmd/gofix/osopen_test.go new file mode 100644 index 000000000..43ddd1a40 --- /dev/null +++ b/src/cmd/gofix/osopen_test.go @@ -0,0 +1,59 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func init() { + addTestCases(osopenTests) +} + +var osopenTests = []testCase{ + { + Name: "osopen.0", + In: `package main + +import ( + "os" +) + +func f() { + os.OpenFile(a, b, c) + os.Open(a, os.O_RDONLY, 0) + os.Open(a, os.O_RDONLY, 0666) + os.Open(a, os.O_RDWR, 0) + os.Open(a, os.O_CREAT, 0666) + os.Open(a, os.O_CREAT|os.O_TRUNC, 0664) + os.Open(a, os.O_CREATE, 0666) + os.Open(a, os.O_CREATE|os.O_TRUNC, 0664) + os.Open(a, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666) + os.Open(a, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666) + os.Open(a, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0666) + os.Open(a, os.O_SURPRISE|os.O_CREATE, 0666) + _ = os.O_CREAT +} +`, + Out: `package main + +import ( + "os" +) + +func f() { + os.OpenFile(a, b, c) + os.Open(a) + os.Open(a) + os.OpenFile(a, os.O_RDWR, 0) + os.Create(a) + os.Create(a) + os.Create(a) + os.Create(a) + os.Create(a) + os.OpenFile(a, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666) + os.OpenFile(a, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0666) + os.OpenFile(a, os.O_SURPRISE|os.O_CREATE, 0666) + _ = os.O_CREATE +} +`, + }, +} diff --git a/src/cmd/gofix/procattr.go b/src/cmd/gofix/procattr.go new file mode 100644 index 000000000..0e2190b1f --- /dev/null +++ b/src/cmd/gofix/procattr.go @@ -0,0 +1,61 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "go/ast" + "go/token" +) + +var procattrFix = fix{ + "procattr", + procattr, + `Adapt calls to os.StartProcess to use new ProcAttr type. + +http://codereview.appspot.com/4253052 +`, +} + +func init() { + register(procattrFix) +} + +func procattr(f *ast.File) bool { + if !imports(f, "os") && !imports(f, "syscall") { + return false + } + + fixed := false + walk(f, func(n interface{}) { + call, ok := n.(*ast.CallExpr) + if !ok || len(call.Args) != 5 { + return + } + var pkg string + if isPkgDot(call.Fun, "os", "StartProcess") { + pkg = "os" + } else if isPkgDot(call.Fun, "syscall", "StartProcess") { + pkg = "syscall" + } else { + return + } + // os.StartProcess(a, b, c, d, e) -> os.StartProcess(a, b, &os.ProcAttr{Env: c, Dir: d, Files: e}) + lit := &ast.CompositeLit{Type: ast.NewIdent(pkg + ".ProcAttr")} + env, dir, files := call.Args[2], call.Args[3], call.Args[4] + if !isName(env, "nil") && !isCall(env, "os", "Environ") { + lit.Elts = append(lit.Elts, &ast.KeyValueExpr{Key: ast.NewIdent("Env"), Value: env}) + } + if !isEmptyString(dir) { + lit.Elts = append(lit.Elts, &ast.KeyValueExpr{Key: ast.NewIdent("Dir"), Value: dir}) + } + if !isName(files, "nil") { + lit.Elts = append(lit.Elts, &ast.KeyValueExpr{Key: ast.NewIdent("Files"), Value: files}) + } + call.Args[2] = &ast.UnaryExpr{Op: token.AND, X: lit} + call.Args = call.Args[:3] + fixed = true + }) + return fixed +} diff --git a/src/cmd/gofix/procattr_test.go b/src/cmd/gofix/procattr_test.go new file mode 100644 index 000000000..b973b9684 --- /dev/null +++ b/src/cmd/gofix/procattr_test.go @@ -0,0 +1,74 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func init() { + addTestCases(procattrTests) +} + +var procattrTests = []testCase{ + { + Name: "procattr.0", + In: `package main + +import ( + "os" + "syscall" +) + +func f() { + os.StartProcess(a, b, c, d, e) + os.StartProcess(a, b, os.Environ(), d, e) + os.StartProcess(a, b, nil, d, e) + os.StartProcess(a, b, c, "", e) + os.StartProcess(a, b, c, d, nil) + os.StartProcess(a, b, nil, "", nil) + + os.StartProcess( + a, + b, + c, + d, + e, + ) + + syscall.StartProcess(a, b, c, d, e) + syscall.StartProcess(a, b, os.Environ(), d, e) + syscall.StartProcess(a, b, nil, d, e) + syscall.StartProcess(a, b, c, "", e) + syscall.StartProcess(a, b, c, d, nil) + syscall.StartProcess(a, b, nil, "", nil) +} +`, + Out: `package main + +import ( + "os" + "syscall" +) + +func f() { + os.StartProcess(a, b, &os.ProcAttr{Env: c, Dir: d, Files: e}) + os.StartProcess(a, b, &os.ProcAttr{Dir: d, Files: e}) + os.StartProcess(a, b, &os.ProcAttr{Dir: d, Files: e}) + os.StartProcess(a, b, &os.ProcAttr{Env: c, Files: e}) + os.StartProcess(a, b, &os.ProcAttr{Env: c, Dir: d}) + os.StartProcess(a, b, &os.ProcAttr{}) + + os.StartProcess( + a, + b, &os.ProcAttr{Env: c, Dir: d, Files: e}, + ) + + syscall.StartProcess(a, b, &syscall.ProcAttr{Env: c, Dir: d, Files: e}) + syscall.StartProcess(a, b, &syscall.ProcAttr{Dir: d, Files: e}) + syscall.StartProcess(a, b, &syscall.ProcAttr{Dir: d, Files: e}) + syscall.StartProcess(a, b, &syscall.ProcAttr{Env: c, Files: e}) + syscall.StartProcess(a, b, &syscall.ProcAttr{Env: c, Dir: d}) + syscall.StartProcess(a, b, &syscall.ProcAttr{}) +} +`, + }, +} diff --git a/src/cmd/gofix/reflect.go b/src/cmd/gofix/reflect.go new file mode 100644 index 000000000..74ddb398f --- /dev/null +++ b/src/cmd/gofix/reflect.go @@ -0,0 +1,843 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// TODO(rsc): Once there is better support for writing +// multi-package commands, this should really be in +// its own package, and then we can drop all the "reflect" +// prefixes on the global variables and functions. + +package main + +import ( + "go/ast" + "go/token" + "strings" +) + +var reflectFix = fix{ + "reflect", + reflectFn, + `Adapt code to new reflect API. + +http://codereview.appspot.com/4281055 +`, +} + +func init() { + register(reflectFix) +} + +// The reflect API change dropped the concrete types *reflect.ArrayType etc. +// Any type assertions prior to method calls can be deleted: +// x.(*reflect.ArrayType).Len() -> x.Len() +// +// Any type checks can be replaced by assignment and check of Kind: +// x, y := z.(*reflect.ArrayType) +// -> +// x := z +// y := x.Kind() == reflect.Array +// +// If z is an ordinary variable name and x is not subsequently assigned to, +// references to x can be replaced by z and the assignment deleted. +// We only bother if x and z are the same name. +// If y is not subsequently assigned to and neither is x, references to +// y can be replaced by its expression. We only bother when there is +// just one use or when the use appears in an if clause. +// +// Not all type checks result in a single Kind check. The rewrite of the type check for +// reflect.ArrayOrSliceType checks x.Kind() against reflect.Array and reflect.Slice. +// The rewrite for *reflect.IntType checks againt Int, Int8, Int16, Int32, Int64. +// The rewrite for *reflect.UintType adds Uintptr. +// +// A type switch turns into an assignment and a switch on Kind: +// switch x := y.(type) { +// case reflect.ArrayOrSliceType: +// ... +// case *reflect.ChanType: +// ... +// case *reflect.IntType: +// ... +// } +// -> +// switch x := y; x.Kind() { +// case reflect.Array, reflect.Slice: +// ... +// case reflect.Chan: +// ... +// case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: +// ... +// } +// +// The same simplification applies: we drop x := x if x is not assigned +// to in the switch cases. +// +// Because the type check assignment includes a type assertion in its +// syntax and the rewrite traversal is bottom up, we must do a pass to +// rewrite the type check assignments and then a separate pass to +// rewrite the type assertions. +// +// The same process applies to the API changes for reflect.Value. +// +// For both cases, but especially Value, the code needs to be aware +// of the type of a receiver when rewriting a method call. For example, +// x.(*reflect.ArrayValue).Elem(i) becomes x.Index(i) while +// x.(*reflect.MapValue).Elem(v) becomes x.MapIndex(v). +// In general, reflectFn needs to know the type of the receiver expression. +// In most cases (and in all the cases in the Go source tree), the toy +// type checker in typecheck.go provides enough information for gofix +// to make the rewrite. If gofix misses a rewrite, the code that is left over +// will not compile, so it will be noticed immediately. + +func reflectFn(f *ast.File) bool { + if !imports(f, "reflect") { + return false + } + + fixed := false + + // Rewrite names in method calls. + // Needs basic type information (see above). + typeof := typecheck(reflectTypeConfig, f) + walk(f, func(n interface{}) { + switch n := n.(type) { + case *ast.SelectorExpr: + typ := typeof[n.X] + if m := reflectRewriteMethod[typ]; m != nil { + if replace := m[n.Sel.Name]; replace != "" { + n.Sel.Name = replace + fixed = true + return + } + } + + // For all reflect Values, replace SetValue with Set. + if isReflectValue[typ] && n.Sel.Name == "SetValue" { + n.Sel.Name = "Set" + fixed = true + return + } + + // Replace reflect.MakeZero with reflect.Zero. + if isPkgDot(n, "reflect", "MakeZero") { + n.Sel.Name = "Zero" + fixed = true + return + } + } + }) + + // Replace PtrValue's PointTo(x) with Set(x.Addr()). + walk(f, func(n interface{}) { + call, ok := n.(*ast.CallExpr) + if !ok || len(call.Args) != 1 { + return + } + sel, ok := call.Fun.(*ast.SelectorExpr) + if !ok || sel.Sel.Name != "PointTo" { + return + } + typ := typeof[sel.X] + if typ != "*reflect.PtrValue" { + return + } + sel.Sel.Name = "Set" + if !isTopName(call.Args[0], "nil") { + call.Args[0] = &ast.SelectorExpr{ + X: call.Args[0], + Sel: ast.NewIdent("Addr()"), + } + } + fixed = true + }) + + // Fix type switches. + walk(f, func(n interface{}) { + if reflectFixSwitch(n) { + fixed = true + } + }) + + // Fix type assertion checks (multiple assignment statements). + // Have to work on the statement context (statement list or if statement) + // so that we can insert an extra statement occasionally. + // Ignoring for and switch because they don't come up in + // typical code. + walk(f, func(n interface{}) { + switch n := n.(type) { + case *[]ast.Stmt: + // v is the replacement statement list. + var v []ast.Stmt + insert := func(x ast.Stmt) { + v = append(v, x) + } + for i, x := range *n { + // Tentatively append to v; if we rewrite x + // we'll have to update the entry, so remember + // the index. + j := len(v) + v = append(v, x) + if reflectFixTypecheck(&x, insert, (*n)[i+1:]) { + // reflectFixTypecheck may have overwritten x. + // Update the entry we appended just before the call. + v[j] = x + fixed = true + } + } + *n = v + case *ast.IfStmt: + x := &ast.ExprStmt{n.Cond} + if reflectFixTypecheck(&n.Init, nil, []ast.Stmt{x, n.Body, n.Else}) { + n.Cond = x.X + fixed = true + } + } + }) + + // Warn about any typecheck statements that we missed. + walk(f, reflectWarnTypecheckStmt) + + // Now that those are gone, fix remaining type assertions. + // Delayed because the type checks have + // type assertions as part of their syntax. + walk(f, func(n interface{}) { + if reflectFixAssert(n) { + fixed = true + } + }) + + // Now that the type assertions are gone, rewrite remaining + // references to specific reflect types to use the general ones. + walk(f, func(n interface{}) { + ptr, ok := n.(*ast.Expr) + if !ok { + return + } + nn := *ptr + typ := reflectType(nn) + if typ == "" { + return + } + if strings.HasSuffix(typ, "Type") { + *ptr = newPkgDot(nn.Pos(), "reflect", "Type") + } else { + *ptr = newPkgDot(nn.Pos(), "reflect", "Value") + } + fixed = true + }) + + // Rewrite v.Set(nil) to v.Set(reflect.MakeZero(v.Type())). + walk(f, func(n interface{}) { + call, ok := n.(*ast.CallExpr) + if !ok || len(call.Args) != 1 || !isTopName(call.Args[0], "nil") { + return + } + sel, ok := call.Fun.(*ast.SelectorExpr) + if !ok || !isReflectValue[typeof[sel.X]] || sel.Sel.Name != "Set" { + return + } + call.Args[0] = &ast.CallExpr{ + Fun: newPkgDot(call.Args[0].Pos(), "reflect", "Zero"), + Args: []ast.Expr{ + &ast.CallExpr{ + Fun: &ast.SelectorExpr{ + X: sel.X, + Sel: &ast.Ident{Name: "Type"}, + }, + }, + }, + } + fixed = true + }) + + // Rewrite v != nil to v.IsValid(). + // Rewrite nil used as reflect.Value (in function argument or return) to reflect.Value{}. + walk(f, func(n interface{}) { + ptr, ok := n.(*ast.Expr) + if !ok { + return + } + if isTopName(*ptr, "nil") && isReflectValue[typeof[*ptr]] { + *ptr = ast.NewIdent("reflect.Value{}") + fixed = true + return + } + nn, ok := (*ptr).(*ast.BinaryExpr) + if !ok || (nn.Op != token.EQL && nn.Op != token.NEQ) || !isTopName(nn.Y, "nil") || !isReflectValue[typeof[nn.X]] { + return + } + var call ast.Expr = &ast.CallExpr{ + Fun: &ast.SelectorExpr{ + X: nn.X, + Sel: &ast.Ident{Name: "IsValid"}, + }, + } + if nn.Op == token.EQL { + call = &ast.UnaryExpr{Op: token.NOT, X: call} + } + *ptr = call + fixed = true + }) + + return fixed +} + +// reflectFixSwitch rewrites *n (if n is an *ast.Stmt) corresponding +// to a type switch. +func reflectFixSwitch(n interface{}) bool { + ptr, ok := n.(*ast.Stmt) + if !ok { + return false + } + n = *ptr + + ts, ok := n.(*ast.TypeSwitchStmt) + if !ok { + return false + } + + // Are any switch cases referring to reflect types? + // (That is, is this an old reflect type switch?) + for _, cas := range ts.Body.List { + for _, typ := range cas.(*ast.CaseClause).List { + if reflectType(typ) != "" { + goto haveReflect + } + } + } + return false + +haveReflect: + // Now we know it's an old reflect type switch. Prepare the new version, + // but don't replace or edit the original until we're sure of success. + + // Figure out the initializer statement, if any, and the receiver for the Kind call. + var init ast.Stmt + var rcvr ast.Expr + + init = ts.Init + switch n := ts.Assign.(type) { + default: + warn(ts.Pos(), "unexpected form in type switch") + return false + + case *ast.AssignStmt: + as := n + ta := as.Rhs[0].(*ast.TypeAssertExpr) + x := isIdent(as.Lhs[0]) + z := isIdent(ta.X) + + if isBlank(x) || x != nil && z != nil && x.Name == z.Name && !assignsTo(x, ts.Body.List) { + // Can drop the variable creation. + rcvr = ta.X + } else { + // Need to use initialization statement. + if init != nil { + warn(ts.Pos(), "cannot rewrite reflect type switch with initializing statement") + return false + } + init = &ast.AssignStmt{ + Lhs: []ast.Expr{as.Lhs[0]}, + TokPos: as.TokPos, + Tok: token.DEFINE, + Rhs: []ast.Expr{ta.X}, + } + rcvr = as.Lhs[0] + } + + case *ast.ExprStmt: + rcvr = n.X.(*ast.TypeAssertExpr).X + } + + // Prepare rewritten type switch (see large comment above for form). + sw := &ast.SwitchStmt{ + Switch: ts.Switch, + Init: init, + Tag: &ast.CallExpr{ + Fun: &ast.SelectorExpr{ + X: rcvr, + Sel: &ast.Ident{ + NamePos: rcvr.End(), + Name: "Kind", + Obj: nil, + }, + }, + Lparen: rcvr.End(), + Rparen: rcvr.End(), + }, + Body: &ast.BlockStmt{ + Lbrace: ts.Body.Lbrace, + List: nil, // to be filled in + Rbrace: ts.Body.Rbrace, + }, + } + + // Translate cases. + for _, tcas := range ts.Body.List { + tcas := tcas.(*ast.CaseClause) + cas := &ast.CaseClause{ + Case: tcas.Case, + Colon: tcas.Colon, + Body: tcas.Body, + } + for _, t := range tcas.List { + if isTopName(t, "nil") { + cas.List = append(cas.List, newPkgDot(t.Pos(), "reflect", "Invalid")) + continue + } + + typ := reflectType(t) + if typ == "" { + warn(t.Pos(), "cannot rewrite reflect type switch case with non-reflect type %s", gofmt(t)) + cas.List = append(cas.List, t) + continue + } + + for _, k := range reflectKind[typ] { + cas.List = append(cas.List, newPkgDot(t.Pos(), "reflect", k)) + } + } + sw.Body.List = append(sw.Body.List, cas) + } + + // Everything worked. Rewrite AST. + *ptr = sw + return true +} + +// Rewrite x, y = z.(T) into +// x = z +// y = x.Kind() == K +// as described in the long comment above. +// +// If insert != nil, it can be called to insert a statement after *ptr in its block. +// If insert == nil, insertion is not possible. +// At most one call to insert is allowed. +// +// Scope gives the statements for which a declaration +// in *ptr would be in scope. +// +// The result is true of the statement was rewritten. +// +func reflectFixTypecheck(ptr *ast.Stmt, insert func(ast.Stmt), scope []ast.Stmt) bool { + st := *ptr + as, ok := st.(*ast.AssignStmt) + if !ok || len(as.Lhs) != 2 || len(as.Rhs) != 1 { + return false + } + + ta, ok := as.Rhs[0].(*ast.TypeAssertExpr) + if !ok { + return false + } + typ := reflectType(ta.Type) + if typ == "" { + return false + } + + // Have x, y := z.(t). + x := isIdent(as.Lhs[0]) + y := isIdent(as.Lhs[1]) + z := isIdent(ta.X) + + // First step is x := z, unless it's x := x and the resulting x is never reassigned. + // rcvr is the x in x.Kind(). + var rcvr ast.Expr + if isBlank(x) || + as.Tok == token.DEFINE && x != nil && z != nil && x.Name == z.Name && !assignsTo(x, scope) { + // Can drop the statement. + // If we need to insert a statement later, now we have a slot. + *ptr = &ast.EmptyStmt{} + insert = func(x ast.Stmt) { *ptr = x } + rcvr = ta.X + } else { + *ptr = &ast.AssignStmt{ + Lhs: []ast.Expr{as.Lhs[0]}, + TokPos: as.TokPos, + Tok: as.Tok, + Rhs: []ast.Expr{ta.X}, + } + rcvr = as.Lhs[0] + } + + // Prepare x.Kind() == T expression appropriate to t. + // If x is not a simple identifier, warn that we might be + // reevaluating x. + if x == nil { + warn(as.Pos(), "rewrite reevaluates expr with possible side effects: %s", gofmt(as.Lhs[0])) + } + yExpr, yNotExpr := reflectKindEq(rcvr, reflectKind[typ]) + + // Second step is y := x.Kind() == T, unless it's only used once + // or we have no way to insert that statement. + var yStmt *ast.AssignStmt + if as.Tok == token.DEFINE && countUses(y, scope) <= 1 || insert == nil { + // Can drop the statement and use the expression directly. + rewriteUses(y, + func(token.Pos) ast.Expr { return yExpr }, + func(token.Pos) ast.Expr { return yNotExpr }, + scope) + } else { + yStmt = &ast.AssignStmt{ + Lhs: []ast.Expr{as.Lhs[1]}, + TokPos: as.End(), + Tok: as.Tok, + Rhs: []ast.Expr{yExpr}, + } + insert(yStmt) + } + return true +} + +// reflectKindEq returns the expression z.Kind() == kinds[0] || z.Kind() == kinds[1] || ... +// and its negation. +// The qualifier "reflect." is inserted before each kinds[i] expression. +func reflectKindEq(z ast.Expr, kinds []string) (ast.Expr, ast.Expr) { + n := len(kinds) + if n == 1 { + y := &ast.BinaryExpr{ + X: &ast.CallExpr{ + Fun: &ast.SelectorExpr{ + X: z, + Sel: ast.NewIdent("Kind"), + }, + }, + Op: token.EQL, + Y: newPkgDot(token.NoPos, "reflect", kinds[0]), + } + ynot := &ast.BinaryExpr{ + X: &ast.CallExpr{ + Fun: &ast.SelectorExpr{ + X: z, + Sel: ast.NewIdent("Kind"), + }, + }, + Op: token.NEQ, + Y: newPkgDot(token.NoPos, "reflect", kinds[0]), + } + return y, ynot + } + + x, xnot := reflectKindEq(z, kinds[0:n-1]) + y, ynot := reflectKindEq(z, kinds[n-1:]) + + or := &ast.BinaryExpr{ + X: x, + Op: token.LOR, + Y: y, + } + andnot := &ast.BinaryExpr{ + X: xnot, + Op: token.LAND, + Y: ynot, + } + return or, andnot +} + +// if x represents a known old reflect type/value like *reflect.PtrType or reflect.ArrayOrSliceValue, +// reflectType returns the string form of that type. +func reflectType(x ast.Expr) string { + ptr, ok := x.(*ast.StarExpr) + if ok { + x = ptr.X + } + + sel, ok := x.(*ast.SelectorExpr) + if !ok || !isName(sel.X, "reflect") { + return "" + } + + var s = "reflect." + if ptr != nil { + s = "*reflect." + } + s += sel.Sel.Name + + if reflectKind[s] != nil { + return s + } + return "" +} + +// reflectWarnTypecheckStmt warns about statements +// of the form x, y = z.(T) for any old reflect type T. +// The last pass should have gotten them all, and if it didn't, +// the next pass is going to turn them into x, y = z. +func reflectWarnTypecheckStmt(n interface{}) { + as, ok := n.(*ast.AssignStmt) + if !ok || len(as.Lhs) != 2 || len(as.Rhs) != 1 { + return + } + ta, ok := as.Rhs[0].(*ast.TypeAssertExpr) + if !ok || reflectType(ta.Type) == "" { + return + } + warn(n.(ast.Node).Pos(), "unfixed reflect type check") +} + +// reflectFixAssert rewrites x.(T) to x for any old reflect type T. +func reflectFixAssert(n interface{}) bool { + ptr, ok := n.(*ast.Expr) + if ok { + ta, ok := (*ptr).(*ast.TypeAssertExpr) + if ok && reflectType(ta.Type) != "" { + *ptr = ta.X + return true + } + } + return false +} + +// Tables describing the transformations. + +// Description of old reflect API for partial type checking. +// We pretend the Elem method is on Type and Value instead +// of enumerating all the types it is actually on. +// Also, we pretend that ArrayType etc embeds Type for the +// purposes of describing the API. (In fact they embed commonType, +// which implements Type.) +var reflectTypeConfig = &TypeConfig{ + Type: map[string]*Type{ + "reflect.ArrayOrSliceType": &Type{Embed: []string{"reflect.Type"}}, + "reflect.ArrayOrSliceValue": &Type{Embed: []string{"reflect.Value"}}, + "reflect.ArrayType": &Type{Embed: []string{"reflect.Type"}}, + "reflect.ArrayValue": &Type{Embed: []string{"reflect.Value"}}, + "reflect.BoolType": &Type{Embed: []string{"reflect.Type"}}, + "reflect.BoolValue": &Type{Embed: []string{"reflect.Value"}}, + "reflect.ChanType": &Type{Embed: []string{"reflect.Type"}}, + "reflect.ChanValue": &Type{ + Method: map[string]string{ + "Recv": "func() (reflect.Value, bool)", + "TryRecv": "func() (reflect.Value, bool)", + }, + Embed: []string{"reflect.Value"}, + }, + "reflect.ComplexType": &Type{Embed: []string{"reflect.Type"}}, + "reflect.ComplexValue": &Type{Embed: []string{"reflect.Value"}}, + "reflect.FloatType": &Type{Embed: []string{"reflect.Type"}}, + "reflect.FloatValue": &Type{Embed: []string{"reflect.Value"}}, + "reflect.FuncType": &Type{ + Method: map[string]string{ + "In": "func(int) reflect.Type", + "Out": "func(int) reflect.Type", + }, + Embed: []string{"reflect.Type"}, + }, + "reflect.FuncValue": &Type{ + Method: map[string]string{ + "Call": "func([]reflect.Value) []reflect.Value", + }, + }, + "reflect.IntType": &Type{Embed: []string{"reflect.Type"}}, + "reflect.IntValue": &Type{Embed: []string{"reflect.Value"}}, + "reflect.InterfaceType": &Type{Embed: []string{"reflect.Type"}}, + "reflect.InterfaceValue": &Type{Embed: []string{"reflect.Value"}}, + "reflect.MapType": &Type{ + Method: map[string]string{ + "Key": "func() reflect.Type", + }, + Embed: []string{"reflect.Type"}, + }, + "reflect.MapValue": &Type{ + Method: map[string]string{ + "Keys": "func() []reflect.Value", + }, + Embed: []string{"reflect.Value"}, + }, + "reflect.Method": &Type{ + Field: map[string]string{ + "Type": "*reflect.FuncType", + "Func": "*reflect.FuncValue", + }, + }, + "reflect.PtrType": &Type{Embed: []string{"reflect.Type"}}, + "reflect.PtrValue": &Type{Embed: []string{"reflect.Value"}}, + "reflect.SliceType": &Type{Embed: []string{"reflect.Type"}}, + "reflect.SliceValue": &Type{ + Method: map[string]string{ + "Slice": "func(int, int) *reflect.SliceValue", + }, + Embed: []string{"reflect.Value"}, + }, + "reflect.StringType": &Type{Embed: []string{"reflect.Type"}}, + "reflect.StringValue": &Type{Embed: []string{"reflect.Value"}}, + "reflect.StructField": &Type{ + Field: map[string]string{ + "Type": "reflect.Type", + }, + }, + "reflect.StructType": &Type{ + Method: map[string]string{ + "Field": "func() reflect.StructField", + "FieldByIndex": "func() reflect.StructField", + "FieldByName": "func() reflect.StructField,bool", + "FieldByNameFunc": "func() reflect.StructField,bool", + }, + Embed: []string{"reflect.Type"}, + }, + "reflect.StructValue": &Type{ + Method: map[string]string{ + "Field": "func() reflect.Value", + "FieldByIndex": "func() reflect.Value", + "FieldByName": "func() reflect.Value", + "FieldByNameFunc": "func() reflect.Value", + }, + Embed: []string{"reflect.Value"}, + }, + "reflect.Type": &Type{ + Method: map[string]string{ + "Elem": "func() reflect.Type", + "Method": "func() reflect.Method", + }, + }, + "reflect.UintType": &Type{Embed: []string{"reflect.Type"}}, + "reflect.UintValue": &Type{Embed: []string{"reflect.Value"}}, + "reflect.UnsafePointerType": &Type{Embed: []string{"reflect.Type"}}, + "reflect.UnsafePointerValue": &Type{Embed: []string{"reflect.Value"}}, + "reflect.Value": &Type{ + Method: map[string]string{ + "Addr": "func() *reflect.PtrValue", + "Elem": "func() reflect.Value", + "Method": "func() *reflect.FuncValue", + "SetValue": "func(reflect.Value)", + }, + }, + }, + Func: map[string]string{ + "reflect.Append": "*reflect.SliceValue", + "reflect.AppendSlice": "*reflect.SliceValue", + "reflect.Indirect": "reflect.Value", + "reflect.MakeSlice": "*reflect.SliceValue", + "reflect.MakeChan": "*reflect.ChanValue", + "reflect.MakeMap": "*reflect.MapValue", + "reflect.MakeZero": "reflect.Value", + "reflect.NewValue": "reflect.Value", + "reflect.PtrTo": "*reflect.PtrType", + "reflect.Typeof": "reflect.Type", + }, +} + +var reflectRewriteMethod = map[string]map[string]string{ + // The type API didn't change much. + "*reflect.ChanType": {"Dir": "ChanDir"}, + "*reflect.FuncType": {"DotDotDot": "IsVariadic"}, + + // The value API has longer names to disambiguate + // methods with different signatures. + "reflect.ArrayOrSliceValue": { // interface, not pointer + "Elem": "Index", + }, + "*reflect.ArrayValue": { + "Elem": "Index", + }, + "*reflect.BoolValue": { + "Get": "Bool", + "Set": "SetBool", + }, + "*reflect.ChanValue": { + "Get": "Pointer", + }, + "*reflect.ComplexValue": { + "Get": "Complex", + "Set": "SetComplex", + "Overflow": "OverflowComplex", + }, + "*reflect.FloatValue": { + "Get": "Float", + "Set": "SetFloat", + "Overflow": "OverflowFloat", + }, + "*reflect.FuncValue": { + "Get": "Pointer", + }, + "*reflect.IntValue": { + "Get": "Int", + "Set": "SetInt", + "Overflow": "OverflowInt", + }, + "*reflect.InterfaceValue": { + "Get": "InterfaceData", + }, + "*reflect.MapValue": { + "Elem": "MapIndex", + "Get": "Pointer", + "Keys": "MapKeys", + "SetElem": "SetMapIndex", + }, + "*reflect.PtrValue": { + "Get": "Pointer", + }, + "*reflect.SliceValue": { + "Elem": "Index", + "Get": "Pointer", + }, + "*reflect.StringValue": { + "Get": "String", + "Set": "SetString", + }, + "*reflect.UintValue": { + "Get": "Uint", + "Set": "SetUint", + "Overflow": "OverflowUint", + }, + "*reflect.UnsafePointerValue": { + "Get": "Pointer", + "Set": "SetPointer", + }, +} + +var reflectKind = map[string][]string{ + "reflect.ArrayOrSliceType": {"Array", "Slice"}, // interface, not pointer + "*reflect.ArrayType": {"Array"}, + "*reflect.BoolType": {"Bool"}, + "*reflect.ChanType": {"Chan"}, + "*reflect.ComplexType": {"Complex64", "Complex128"}, + "*reflect.FloatType": {"Float32", "Float64"}, + "*reflect.FuncType": {"Func"}, + "*reflect.IntType": {"Int", "Int8", "Int16", "Int32", "Int64"}, + "*reflect.InterfaceType": {"Interface"}, + "*reflect.MapType": {"Map"}, + "*reflect.PtrType": {"Ptr"}, + "*reflect.SliceType": {"Slice"}, + "*reflect.StringType": {"String"}, + "*reflect.StructType": {"Struct"}, + "*reflect.UintType": {"Uint", "Uint8", "Uint16", "Uint32", "Uint64", "Uintptr"}, + "*reflect.UnsafePointerType": {"UnsafePointer"}, + + "reflect.ArrayOrSliceValue": {"Array", "Slice"}, // interface, not pointer + "*reflect.ArrayValue": {"Array"}, + "*reflect.BoolValue": {"Bool"}, + "*reflect.ChanValue": {"Chan"}, + "*reflect.ComplexValue": {"Complex64", "Complex128"}, + "*reflect.FloatValue": {"Float32", "Float64"}, + "*reflect.FuncValue": {"Func"}, + "*reflect.IntValue": {"Int", "Int8", "Int16", "Int32", "Int64"}, + "*reflect.InterfaceValue": {"Interface"}, + "*reflect.MapValue": {"Map"}, + "*reflect.PtrValue": {"Ptr"}, + "*reflect.SliceValue": {"Slice"}, + "*reflect.StringValue": {"String"}, + "*reflect.StructValue": {"Struct"}, + "*reflect.UintValue": {"Uint", "Uint8", "Uint16", "Uint32", "Uint64", "Uintptr"}, + "*reflect.UnsafePointerValue": {"UnsafePointer"}, +} + +var isReflectValue = map[string]bool{ + "reflect.ArrayOrSliceValue": true, // interface, not pointer + "*reflect.ArrayValue": true, + "*reflect.BoolValue": true, + "*reflect.ChanValue": true, + "*reflect.ComplexValue": true, + "*reflect.FloatValue": true, + "*reflect.FuncValue": true, + "*reflect.IntValue": true, + "*reflect.InterfaceValue": true, + "*reflect.MapValue": true, + "*reflect.PtrValue": true, + "*reflect.SliceValue": true, + "*reflect.StringValue": true, + "*reflect.StructValue": true, + "*reflect.UintValue": true, + "*reflect.UnsafePointerValue": true, + "reflect.Value": true, // interface, not pointer +} diff --git a/src/cmd/gofix/reflect_test.go b/src/cmd/gofix/reflect_test.go new file mode 100644 index 000000000..00edf30e9 --- /dev/null +++ b/src/cmd/gofix/reflect_test.go @@ -0,0 +1,31 @@ +package main + +import ( + "io/ioutil" + "log" + "path/filepath" +) + +func init() { + addTestCases(reflectTests()) +} + +func reflectTests() []testCase { + var tests []testCase + + names, _ := filepath.Glob("testdata/reflect.*.in") + for _, in := range names { + out := in[:len(in)-len(".in")] + ".out" + inb, err := ioutil.ReadFile(in) + if err != nil { + log.Fatal(err) + } + outb, err := ioutil.ReadFile(out) + if err != nil { + log.Fatal(err) + } + tests = append(tests, testCase{Name: in, In: string(inb), Out: string(outb)}) + } + + return tests +} diff --git a/src/cmd/gofix/testdata/reflect.asn1.go.in b/src/cmd/gofix/testdata/reflect.asn1.go.in new file mode 100644 index 000000000..c5314517b --- /dev/null +++ b/src/cmd/gofix/testdata/reflect.asn1.go.in @@ -0,0 +1,815 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// The asn1 package implements parsing of DER-encoded ASN.1 data structures, +// as defined in ITU-T Rec X.690. +// +// See also ``A Layman's Guide to a Subset of ASN.1, BER, and DER,'' +// http://luca.ntop.org/Teaching/Appunti/asn1.html. +package asn1 + +// ASN.1 is a syntax for specifying abstract objects and BER, DER, PER, XER etc +// are different encoding formats for those objects. Here, we'll be dealing +// with DER, the Distinguished Encoding Rules. DER is used in X.509 because +// it's fast to parse and, unlike BER, has a unique encoding for every object. +// When calculating hashes over objects, it's important that the resulting +// bytes be the same at both ends and DER removes this margin of error. +// +// ASN.1 is very complex and this package doesn't attempt to implement +// everything by any means. + +import ( + "fmt" + "os" + "reflect" + "time" +) + +// A StructuralError suggests that the ASN.1 data is valid, but the Go type +// which is receiving it doesn't match. +type StructuralError struct { + Msg string +} + +func (e StructuralError) String() string { return "ASN.1 structure error: " + e.Msg } + +// A SyntaxError suggests that the ASN.1 data is invalid. +type SyntaxError struct { + Msg string +} + +func (e SyntaxError) String() string { return "ASN.1 syntax error: " + e.Msg } + +// We start by dealing with each of the primitive types in turn. + +// BOOLEAN + +func parseBool(bytes []byte) (ret bool, err os.Error) { + if len(bytes) != 1 { + err = SyntaxError{"invalid boolean"} + return + } + + return bytes[0] != 0, nil +} + +// INTEGER + +// parseInt64 treats the given bytes as a big-endian, signed integer and +// returns the result. +func parseInt64(bytes []byte) (ret int64, err os.Error) { + if len(bytes) > 8 { + // We'll overflow an int64 in this case. + err = StructuralError{"integer too large"} + return + } + for bytesRead := 0; bytesRead < len(bytes); bytesRead++ { + ret <<= 8 + ret |= int64(bytes[bytesRead]) + } + + // Shift up and down in order to sign extend the result. + ret <<= 64 - uint8(len(bytes))*8 + ret >>= 64 - uint8(len(bytes))*8 + return +} + +// parseInt treats the given bytes as a big-endian, signed integer and returns +// the result. +func parseInt(bytes []byte) (int, os.Error) { + ret64, err := parseInt64(bytes) + if err != nil { + return 0, err + } + if ret64 != int64(int(ret64)) { + return 0, StructuralError{"integer too large"} + } + return int(ret64), nil +} + +// BIT STRING + +// BitString is the structure to use when you want an ASN.1 BIT STRING type. A +// bit string is padded up to the nearest byte in memory and the number of +// valid bits is recorded. Padding bits will be zero. +type BitString struct { + Bytes []byte // bits packed into bytes. + BitLength int // length in bits. +} + +// At returns the bit at the given index. If the index is out of range it +// returns false. +func (b BitString) At(i int) int { + if i < 0 || i >= b.BitLength { + return 0 + } + x := i / 8 + y := 7 - uint(i%8) + return int(b.Bytes[x]>>y) & 1 +} + +// RightAlign returns a slice where the padding bits are at the beginning. The +// slice may share memory with the BitString. +func (b BitString) RightAlign() []byte { + shift := uint(8 - (b.BitLength % 8)) + if shift == 8 || len(b.Bytes) == 0 { + return b.Bytes + } + + a := make([]byte, len(b.Bytes)) + a[0] = b.Bytes[0] >> shift + for i := 1; i < len(b.Bytes); i++ { + a[i] = b.Bytes[i-1] << (8 - shift) + a[i] |= b.Bytes[i] >> shift + } + + return a +} + +// parseBitString parses an ASN.1 bit string from the given byte array and returns it. +func parseBitString(bytes []byte) (ret BitString, err os.Error) { + if len(bytes) == 0 { + err = SyntaxError{"zero length BIT STRING"} + return + } + paddingBits := int(bytes[0]) + if paddingBits > 7 || + len(bytes) == 1 && paddingBits > 0 || + bytes[len(bytes)-1]&((1< 4 { + err = StructuralError{"base 128 integer too large"} + return + } + ret <<= 7 + b := bytes[offset] + ret |= int(b & 0x7f) + offset++ + if b&0x80 == 0 { + return + } + } + err = SyntaxError{"truncated base 128 integer"} + return +} + +// UTCTime + +func parseUTCTime(bytes []byte) (ret *time.Time, err os.Error) { + s := string(bytes) + ret, err = time.Parse("0601021504Z0700", s) + if err == nil { + return + } + ret, err = time.Parse("060102150405Z0700", s) + return +} + +// parseGeneralizedTime parses the GeneralizedTime from the given byte array +// and returns the resulting time. +func parseGeneralizedTime(bytes []byte) (ret *time.Time, err os.Error) { + return time.Parse("20060102150405Z0700", string(bytes)) +} + +// PrintableString + +// parsePrintableString parses a ASN.1 PrintableString from the given byte +// array and returns it. +func parsePrintableString(bytes []byte) (ret string, err os.Error) { + for _, b := range bytes { + if !isPrintable(b) { + err = SyntaxError{"PrintableString contains invalid character"} + return + } + } + ret = string(bytes) + return +} + +// isPrintable returns true iff the given b is in the ASN.1 PrintableString set. +func isPrintable(b byte) bool { + return 'a' <= b && b <= 'z' || + 'A' <= b && b <= 'Z' || + '0' <= b && b <= '9' || + '\'' <= b && b <= ')' || + '+' <= b && b <= '/' || + b == ' ' || + b == ':' || + b == '=' || + b == '?' || + // This is techincally not allowed in a PrintableString. + // However, x509 certificates with wildcard strings don't + // always use the correct string type so we permit it. + b == '*' +} + +// IA5String + +// parseIA5String parses a ASN.1 IA5String (ASCII string) from the given +// byte array and returns it. +func parseIA5String(bytes []byte) (ret string, err os.Error) { + for _, b := range bytes { + if b >= 0x80 { + err = SyntaxError{"IA5String contains invalid character"} + return + } + } + ret = string(bytes) + return +} + +// T61String + +// parseT61String parses a ASN.1 T61String (8-bit clean string) from the given +// byte array and returns it. +func parseT61String(bytes []byte) (ret string, err os.Error) { + return string(bytes), nil +} + +// A RawValue represents an undecoded ASN.1 object. +type RawValue struct { + Class, Tag int + IsCompound bool + Bytes []byte + FullBytes []byte // includes the tag and length +} + +// RawContent is used to signal that the undecoded, DER data needs to be +// preserved for a struct. To use it, the first field of the struct must have +// this type. It's an error for any of the other fields to have this type. +type RawContent []byte + +// Tagging + +// parseTagAndLength parses an ASN.1 tag and length pair from the given offset +// into a byte array. It returns the parsed data and the new offset. SET and +// SET OF (tag 17) are mapped to SEQUENCE and SEQUENCE OF (tag 16) since we +// don't distinguish between ordered and unordered objects in this code. +func parseTagAndLength(bytes []byte, initOffset int) (ret tagAndLength, offset int, err os.Error) { + offset = initOffset + b := bytes[offset] + offset++ + ret.class = int(b >> 6) + ret.isCompound = b&0x20 == 0x20 + ret.tag = int(b & 0x1f) + + // If the bottom five bits are set, then the tag number is actually base 128 + // encoded afterwards + if ret.tag == 0x1f { + ret.tag, offset, err = parseBase128Int(bytes, offset) + if err != nil { + return + } + } + if offset >= len(bytes) { + err = SyntaxError{"truncated tag or length"} + return + } + b = bytes[offset] + offset++ + if b&0x80 == 0 { + // The length is encoded in the bottom 7 bits. + ret.length = int(b & 0x7f) + } else { + // Bottom 7 bits give the number of length bytes to follow. + numBytes := int(b & 0x7f) + // We risk overflowing a signed 32-bit number if we accept more than 3 bytes. + if numBytes > 3 { + err = StructuralError{"length too large"} + return + } + if numBytes == 0 { + err = SyntaxError{"indefinite length found (not DER)"} + return + } + ret.length = 0 + for i := 0; i < numBytes; i++ { + if offset >= len(bytes) { + err = SyntaxError{"truncated tag or length"} + return + } + b = bytes[offset] + offset++ + ret.length <<= 8 + ret.length |= int(b) + } + } + + return +} + +// parseSequenceOf is used for SEQUENCE OF and SET OF values. It tries to parse +// a number of ASN.1 values from the given byte array and returns them as a +// slice of Go values of the given type. +func parseSequenceOf(bytes []byte, sliceType *reflect.SliceType, elemType reflect.Type) (ret *reflect.SliceValue, err os.Error) { + expectedTag, compoundType, ok := getUniversalType(elemType) + if !ok { + err = StructuralError{"unknown Go type for slice"} + return + } + + // First we iterate over the input and count the number of elements, + // checking that the types are correct in each case. + numElements := 0 + for offset := 0; offset < len(bytes); { + var t tagAndLength + t, offset, err = parseTagAndLength(bytes, offset) + if err != nil { + return + } + // We pretend that GENERAL STRINGs are PRINTABLE STRINGs so + // that a sequence of them can be parsed into a []string. + if t.tag == tagGeneralString { + t.tag = tagPrintableString + } + if t.class != classUniversal || t.isCompound != compoundType || t.tag != expectedTag { + err = StructuralError{"sequence tag mismatch"} + return + } + if invalidLength(offset, t.length, len(bytes)) { + err = SyntaxError{"truncated sequence"} + return + } + offset += t.length + numElements++ + } + ret = reflect.MakeSlice(sliceType, numElements, numElements) + params := fieldParameters{} + offset := 0 + for i := 0; i < numElements; i++ { + offset, err = parseField(ret.Elem(i), bytes, offset, params) + if err != nil { + return + } + } + return +} + +var ( + bitStringType = reflect.Typeof(BitString{}) + objectIdentifierType = reflect.Typeof(ObjectIdentifier{}) + enumeratedType = reflect.Typeof(Enumerated(0)) + flagType = reflect.Typeof(Flag(false)) + timeType = reflect.Typeof(&time.Time{}) + rawValueType = reflect.Typeof(RawValue{}) + rawContentsType = reflect.Typeof(RawContent(nil)) +) + +// invalidLength returns true iff offset + length > sliceLength, or if the +// addition would overflow. +func invalidLength(offset, length, sliceLength int) bool { + return offset+length < offset || offset+length > sliceLength +} + +// parseField is the main parsing function. Given a byte array and an offset +// into the array, it will try to parse a suitable ASN.1 value out and store it +// in the given Value. +func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParameters) (offset int, err os.Error) { + offset = initOffset + fieldType := v.Type() + + // If we have run out of data, it may be that there are optional elements at the end. + if offset == len(bytes) { + if !setDefaultValue(v, params) { + err = SyntaxError{"sequence truncated"} + } + return + } + + // Deal with raw values. + if fieldType == rawValueType { + var t tagAndLength + t, offset, err = parseTagAndLength(bytes, offset) + if err != nil { + return + } + if invalidLength(offset, t.length, len(bytes)) { + err = SyntaxError{"data truncated"} + return + } + result := RawValue{t.class, t.tag, t.isCompound, bytes[offset : offset+t.length], bytes[initOffset : offset+t.length]} + offset += t.length + v.(*reflect.StructValue).Set(reflect.NewValue(result).(*reflect.StructValue)) + return + } + + // Deal with the ANY type. + if ifaceType, ok := fieldType.(*reflect.InterfaceType); ok && ifaceType.NumMethod() == 0 { + ifaceValue := v.(*reflect.InterfaceValue) + var t tagAndLength + t, offset, err = parseTagAndLength(bytes, offset) + if err != nil { + return + } + if invalidLength(offset, t.length, len(bytes)) { + err = SyntaxError{"data truncated"} + return + } + var result interface{} + if !t.isCompound && t.class == classUniversal { + innerBytes := bytes[offset : offset+t.length] + switch t.tag { + case tagPrintableString: + result, err = parsePrintableString(innerBytes) + case tagIA5String: + result, err = parseIA5String(innerBytes) + case tagT61String: + result, err = parseT61String(innerBytes) + case tagInteger: + result, err = parseInt64(innerBytes) + case tagBitString: + result, err = parseBitString(innerBytes) + case tagOID: + result, err = parseObjectIdentifier(innerBytes) + case tagUTCTime: + result, err = parseUTCTime(innerBytes) + case tagOctetString: + result = innerBytes + default: + // If we don't know how to handle the type, we just leave Value as nil. + } + } + offset += t.length + if err != nil { + return + } + if result != nil { + ifaceValue.Set(reflect.NewValue(result)) + } + return + } + universalTag, compoundType, ok1 := getUniversalType(fieldType) + if !ok1 { + err = StructuralError{fmt.Sprintf("unknown Go type: %v", fieldType)} + return + } + + t, offset, err := parseTagAndLength(bytes, offset) + if err != nil { + return + } + if params.explicit { + expectedClass := classContextSpecific + if params.application { + expectedClass = classApplication + } + if t.class == expectedClass && t.tag == *params.tag && (t.length == 0 || t.isCompound) { + if t.length > 0 { + t, offset, err = parseTagAndLength(bytes, offset) + if err != nil { + return + } + } else { + if fieldType != flagType { + err = StructuralError{"Zero length explicit tag was not an asn1.Flag"} + return + } + + flagValue := v.(*reflect.BoolValue) + flagValue.Set(true) + return + } + } else { + // The tags didn't match, it might be an optional element. + ok := setDefaultValue(v, params) + if ok { + offset = initOffset + } else { + err = StructuralError{"explicitly tagged member didn't match"} + } + return + } + } + + // Special case for strings: PrintableString and IA5String both map to + // the Go type string. getUniversalType returns the tag for + // PrintableString when it sees a string so, if we see an IA5String on + // the wire, we change the universal type to match. + if universalTag == tagPrintableString && t.tag == tagIA5String { + universalTag = tagIA5String + } + // Likewise for GeneralString + if universalTag == tagPrintableString && t.tag == tagGeneralString { + universalTag = tagGeneralString + } + + // Special case for time: UTCTime and GeneralizedTime both map to the + // Go type time.Time. + if universalTag == tagUTCTime && t.tag == tagGeneralizedTime { + universalTag = tagGeneralizedTime + } + + expectedClass := classUniversal + expectedTag := universalTag + + if !params.explicit && params.tag != nil { + expectedClass = classContextSpecific + expectedTag = *params.tag + } + + if !params.explicit && params.application && params.tag != nil { + expectedClass = classApplication + expectedTag = *params.tag + } + + // We have unwrapped any explicit tagging at this point. + if t.class != expectedClass || t.tag != expectedTag || t.isCompound != compoundType { + // Tags don't match. Again, it could be an optional element. + ok := setDefaultValue(v, params) + if ok { + offset = initOffset + } else { + err = StructuralError{fmt.Sprintf("tags don't match (%d vs %+v) %+v %s @%d", expectedTag, t, params, fieldType.Name(), offset)} + } + return + } + if invalidLength(offset, t.length, len(bytes)) { + err = SyntaxError{"data truncated"} + return + } + innerBytes := bytes[offset : offset+t.length] + offset += t.length + + // We deal with the structures defined in this package first. + switch fieldType { + case objectIdentifierType: + newSlice, err1 := parseObjectIdentifier(innerBytes) + sliceValue := v.(*reflect.SliceValue) + sliceValue.Set(reflect.MakeSlice(sliceValue.Type().(*reflect.SliceType), len(newSlice), len(newSlice))) + if err1 == nil { + reflect.Copy(sliceValue, reflect.NewValue(newSlice).(reflect.ArrayOrSliceValue)) + } + err = err1 + return + case bitStringType: + structValue := v.(*reflect.StructValue) + bs, err1 := parseBitString(innerBytes) + if err1 == nil { + structValue.Set(reflect.NewValue(bs).(*reflect.StructValue)) + } + err = err1 + return + case timeType: + ptrValue := v.(*reflect.PtrValue) + var time *time.Time + var err1 os.Error + if universalTag == tagUTCTime { + time, err1 = parseUTCTime(innerBytes) + } else { + time, err1 = parseGeneralizedTime(innerBytes) + } + if err1 == nil { + ptrValue.Set(reflect.NewValue(time).(*reflect.PtrValue)) + } + err = err1 + return + case enumeratedType: + parsedInt, err1 := parseInt(innerBytes) + enumValue := v.(*reflect.IntValue) + if err1 == nil { + enumValue.Set(int64(parsedInt)) + } + err = err1 + return + case flagType: + flagValue := v.(*reflect.BoolValue) + flagValue.Set(true) + return + } + switch val := v.(type) { + case *reflect.BoolValue: + parsedBool, err1 := parseBool(innerBytes) + if err1 == nil { + val.Set(parsedBool) + } + err = err1 + return + case *reflect.IntValue: + switch val.Type().Kind() { + case reflect.Int: + parsedInt, err1 := parseInt(innerBytes) + if err1 == nil { + val.Set(int64(parsedInt)) + } + err = err1 + return + case reflect.Int64: + parsedInt, err1 := parseInt64(innerBytes) + if err1 == nil { + val.Set(parsedInt) + } + err = err1 + return + } + case *reflect.StructValue: + structType := fieldType.(*reflect.StructType) + + if structType.NumField() > 0 && + structType.Field(0).Type == rawContentsType { + bytes := bytes[initOffset:offset] + val.Field(0).SetValue(reflect.NewValue(RawContent(bytes))) + } + + innerOffset := 0 + for i := 0; i < structType.NumField(); i++ { + field := structType.Field(i) + if i == 0 && field.Type == rawContentsType { + continue + } + innerOffset, err = parseField(val.Field(i), innerBytes, innerOffset, parseFieldParameters(field.Tag)) + if err != nil { + return + } + } + // We allow extra bytes at the end of the SEQUENCE because + // adding elements to the end has been used in X.509 as the + // version numbers have increased. + return + case *reflect.SliceValue: + sliceType := fieldType.(*reflect.SliceType) + if sliceType.Elem().Kind() == reflect.Uint8 { + val.Set(reflect.MakeSlice(sliceType, len(innerBytes), len(innerBytes))) + reflect.Copy(val, reflect.NewValue(innerBytes).(reflect.ArrayOrSliceValue)) + return + } + newSlice, err1 := parseSequenceOf(innerBytes, sliceType, sliceType.Elem()) + if err1 == nil { + val.Set(newSlice) + } + err = err1 + return + case *reflect.StringValue: + var v string + switch universalTag { + case tagPrintableString: + v, err = parsePrintableString(innerBytes) + case tagIA5String: + v, err = parseIA5String(innerBytes) + case tagT61String: + v, err = parseT61String(innerBytes) + case tagGeneralString: + // GeneralString is specified in ISO-2022/ECMA-35, + // A brief review suggests that it includes structures + // that allow the encoding to change midstring and + // such. We give up and pass it as an 8-bit string. + v, err = parseT61String(innerBytes) + default: + err = SyntaxError{fmt.Sprintf("internal error: unknown string type %d", universalTag)} + } + if err == nil { + val.Set(v) + } + return + } + err = StructuralError{"unknown Go type"} + return +} + +// setDefaultValue is used to install a default value, from a tag string, into +// a Value. It is successful is the field was optional, even if a default value +// wasn't provided or it failed to install it into the Value. +func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) { + if !params.optional { + return + } + ok = true + if params.defaultValue == nil { + return + } + switch val := v.(type) { + case *reflect.IntValue: + val.Set(*params.defaultValue) + } + return +} + +// Unmarshal parses the DER-encoded ASN.1 data structure b +// and uses the reflect package to fill in an arbitrary value pointed at by val. +// Because Unmarshal uses the reflect package, the structs +// being written to must use upper case field names. +// +// An ASN.1 INTEGER can be written to an int or int64. +// If the encoded value does not fit in the Go type, +// Unmarshal returns a parse error. +// +// An ASN.1 BIT STRING can be written to a BitString. +// +// An ASN.1 OCTET STRING can be written to a []byte. +// +// An ASN.1 OBJECT IDENTIFIER can be written to an +// ObjectIdentifier. +// +// An ASN.1 ENUMERATED can be written to an Enumerated. +// +// An ASN.1 UTCTIME or GENERALIZEDTIME can be written to a *time.Time. +// +// An ASN.1 PrintableString or IA5String can be written to a string. +// +// Any of the above ASN.1 values can be written to an interface{}. +// The value stored in the interface has the corresponding Go type. +// For integers, that type is int64. +// +// An ASN.1 SEQUENCE OF x or SET OF x can be written +// to a slice if an x can be written to the slice's element type. +// +// An ASN.1 SEQUENCE or SET can be written to a struct +// if each of the elements in the sequence can be +// written to the corresponding element in the struct. +// +// The following tags on struct fields have special meaning to Unmarshal: +// +// optional marks the field as ASN.1 OPTIONAL +// [explicit] tag:x specifies the ASN.1 tag number; implies ASN.1 CONTEXT SPECIFIC +// default:x sets the default value for optional integer fields +// +// If the type of the first field of a structure is RawContent then the raw +// ASN1 contents of the struct will be stored in it. +// +// Other ASN.1 types are not supported; if it encounters them, +// Unmarshal returns a parse error. +func Unmarshal(b []byte, val interface{}) (rest []byte, err os.Error) { + return UnmarshalWithParams(b, val, "") +} + +// UnmarshalWithParams allows field parameters to be specified for the +// top-level element. The form of the params is the same as the field tags. +func UnmarshalWithParams(b []byte, val interface{}, params string) (rest []byte, err os.Error) { + v := reflect.NewValue(val).(*reflect.PtrValue).Elem() + offset, err := parseField(v, b, 0, parseFieldParameters(params)) + if err != nil { + return nil, err + } + return b[offset:], nil +} diff --git a/src/cmd/gofix/testdata/reflect.asn1.go.out b/src/cmd/gofix/testdata/reflect.asn1.go.out new file mode 100644 index 000000000..902635939 --- /dev/null +++ b/src/cmd/gofix/testdata/reflect.asn1.go.out @@ -0,0 +1,815 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// The asn1 package implements parsing of DER-encoded ASN.1 data structures, +// as defined in ITU-T Rec X.690. +// +// See also ``A Layman's Guide to a Subset of ASN.1, BER, and DER,'' +// http://luca.ntop.org/Teaching/Appunti/asn1.html. +package asn1 + +// ASN.1 is a syntax for specifying abstract objects and BER, DER, PER, XER etc +// are different encoding formats for those objects. Here, we'll be dealing +// with DER, the Distinguished Encoding Rules. DER is used in X.509 because +// it's fast to parse and, unlike BER, has a unique encoding for every object. +// When calculating hashes over objects, it's important that the resulting +// bytes be the same at both ends and DER removes this margin of error. +// +// ASN.1 is very complex and this package doesn't attempt to implement +// everything by any means. + +import ( + "fmt" + "os" + "reflect" + "time" +) + +// A StructuralError suggests that the ASN.1 data is valid, but the Go type +// which is receiving it doesn't match. +type StructuralError struct { + Msg string +} + +func (e StructuralError) String() string { return "ASN.1 structure error: " + e.Msg } + +// A SyntaxError suggests that the ASN.1 data is invalid. +type SyntaxError struct { + Msg string +} + +func (e SyntaxError) String() string { return "ASN.1 syntax error: " + e.Msg } + +// We start by dealing with each of the primitive types in turn. + +// BOOLEAN + +func parseBool(bytes []byte) (ret bool, err os.Error) { + if len(bytes) != 1 { + err = SyntaxError{"invalid boolean"} + return + } + + return bytes[0] != 0, nil +} + +// INTEGER + +// parseInt64 treats the given bytes as a big-endian, signed integer and +// returns the result. +func parseInt64(bytes []byte) (ret int64, err os.Error) { + if len(bytes) > 8 { + // We'll overflow an int64 in this case. + err = StructuralError{"integer too large"} + return + } + for bytesRead := 0; bytesRead < len(bytes); bytesRead++ { + ret <<= 8 + ret |= int64(bytes[bytesRead]) + } + + // Shift up and down in order to sign extend the result. + ret <<= 64 - uint8(len(bytes))*8 + ret >>= 64 - uint8(len(bytes))*8 + return +} + +// parseInt treats the given bytes as a big-endian, signed integer and returns +// the result. +func parseInt(bytes []byte) (int, os.Error) { + ret64, err := parseInt64(bytes) + if err != nil { + return 0, err + } + if ret64 != int64(int(ret64)) { + return 0, StructuralError{"integer too large"} + } + return int(ret64), nil +} + +// BIT STRING + +// BitString is the structure to use when you want an ASN.1 BIT STRING type. A +// bit string is padded up to the nearest byte in memory and the number of +// valid bits is recorded. Padding bits will be zero. +type BitString struct { + Bytes []byte // bits packed into bytes. + BitLength int // length in bits. +} + +// At returns the bit at the given index. If the index is out of range it +// returns false. +func (b BitString) At(i int) int { + if i < 0 || i >= b.BitLength { + return 0 + } + x := i / 8 + y := 7 - uint(i%8) + return int(b.Bytes[x]>>y) & 1 +} + +// RightAlign returns a slice where the padding bits are at the beginning. The +// slice may share memory with the BitString. +func (b BitString) RightAlign() []byte { + shift := uint(8 - (b.BitLength % 8)) + if shift == 8 || len(b.Bytes) == 0 { + return b.Bytes + } + + a := make([]byte, len(b.Bytes)) + a[0] = b.Bytes[0] >> shift + for i := 1; i < len(b.Bytes); i++ { + a[i] = b.Bytes[i-1] << (8 - shift) + a[i] |= b.Bytes[i] >> shift + } + + return a +} + +// parseBitString parses an ASN.1 bit string from the given byte array and returns it. +func parseBitString(bytes []byte) (ret BitString, err os.Error) { + if len(bytes) == 0 { + err = SyntaxError{"zero length BIT STRING"} + return + } + paddingBits := int(bytes[0]) + if paddingBits > 7 || + len(bytes) == 1 && paddingBits > 0 || + bytes[len(bytes)-1]&((1< 4 { + err = StructuralError{"base 128 integer too large"} + return + } + ret <<= 7 + b := bytes[offset] + ret |= int(b & 0x7f) + offset++ + if b&0x80 == 0 { + return + } + } + err = SyntaxError{"truncated base 128 integer"} + return +} + +// UTCTime + +func parseUTCTime(bytes []byte) (ret *time.Time, err os.Error) { + s := string(bytes) + ret, err = time.Parse("0601021504Z0700", s) + if err == nil { + return + } + ret, err = time.Parse("060102150405Z0700", s) + return +} + +// parseGeneralizedTime parses the GeneralizedTime from the given byte array +// and returns the resulting time. +func parseGeneralizedTime(bytes []byte) (ret *time.Time, err os.Error) { + return time.Parse("20060102150405Z0700", string(bytes)) +} + +// PrintableString + +// parsePrintableString parses a ASN.1 PrintableString from the given byte +// array and returns it. +func parsePrintableString(bytes []byte) (ret string, err os.Error) { + for _, b := range bytes { + if !isPrintable(b) { + err = SyntaxError{"PrintableString contains invalid character"} + return + } + } + ret = string(bytes) + return +} + +// isPrintable returns true iff the given b is in the ASN.1 PrintableString set. +func isPrintable(b byte) bool { + return 'a' <= b && b <= 'z' || + 'A' <= b && b <= 'Z' || + '0' <= b && b <= '9' || + '\'' <= b && b <= ')' || + '+' <= b && b <= '/' || + b == ' ' || + b == ':' || + b == '=' || + b == '?' || + // This is techincally not allowed in a PrintableString. + // However, x509 certificates with wildcard strings don't + // always use the correct string type so we permit it. + b == '*' +} + +// IA5String + +// parseIA5String parses a ASN.1 IA5String (ASCII string) from the given +// byte array and returns it. +func parseIA5String(bytes []byte) (ret string, err os.Error) { + for _, b := range bytes { + if b >= 0x80 { + err = SyntaxError{"IA5String contains invalid character"} + return + } + } + ret = string(bytes) + return +} + +// T61String + +// parseT61String parses a ASN.1 T61String (8-bit clean string) from the given +// byte array and returns it. +func parseT61String(bytes []byte) (ret string, err os.Error) { + return string(bytes), nil +} + +// A RawValue represents an undecoded ASN.1 object. +type RawValue struct { + Class, Tag int + IsCompound bool + Bytes []byte + FullBytes []byte // includes the tag and length +} + +// RawContent is used to signal that the undecoded, DER data needs to be +// preserved for a struct. To use it, the first field of the struct must have +// this type. It's an error for any of the other fields to have this type. +type RawContent []byte + +// Tagging + +// parseTagAndLength parses an ASN.1 tag and length pair from the given offset +// into a byte array. It returns the parsed data and the new offset. SET and +// SET OF (tag 17) are mapped to SEQUENCE and SEQUENCE OF (tag 16) since we +// don't distinguish between ordered and unordered objects in this code. +func parseTagAndLength(bytes []byte, initOffset int) (ret tagAndLength, offset int, err os.Error) { + offset = initOffset + b := bytes[offset] + offset++ + ret.class = int(b >> 6) + ret.isCompound = b&0x20 == 0x20 + ret.tag = int(b & 0x1f) + + // If the bottom five bits are set, then the tag number is actually base 128 + // encoded afterwards + if ret.tag == 0x1f { + ret.tag, offset, err = parseBase128Int(bytes, offset) + if err != nil { + return + } + } + if offset >= len(bytes) { + err = SyntaxError{"truncated tag or length"} + return + } + b = bytes[offset] + offset++ + if b&0x80 == 0 { + // The length is encoded in the bottom 7 bits. + ret.length = int(b & 0x7f) + } else { + // Bottom 7 bits give the number of length bytes to follow. + numBytes := int(b & 0x7f) + // We risk overflowing a signed 32-bit number if we accept more than 3 bytes. + if numBytes > 3 { + err = StructuralError{"length too large"} + return + } + if numBytes == 0 { + err = SyntaxError{"indefinite length found (not DER)"} + return + } + ret.length = 0 + for i := 0; i < numBytes; i++ { + if offset >= len(bytes) { + err = SyntaxError{"truncated tag or length"} + return + } + b = bytes[offset] + offset++ + ret.length <<= 8 + ret.length |= int(b) + } + } + + return +} + +// parseSequenceOf is used for SEQUENCE OF and SET OF values. It tries to parse +// a number of ASN.1 values from the given byte array and returns them as a +// slice of Go values of the given type. +func parseSequenceOf(bytes []byte, sliceType reflect.Type, elemType reflect.Type) (ret reflect.Value, err os.Error) { + expectedTag, compoundType, ok := getUniversalType(elemType) + if !ok { + err = StructuralError{"unknown Go type for slice"} + return + } + + // First we iterate over the input and count the number of elements, + // checking that the types are correct in each case. + numElements := 0 + for offset := 0; offset < len(bytes); { + var t tagAndLength + t, offset, err = parseTagAndLength(bytes, offset) + if err != nil { + return + } + // We pretend that GENERAL STRINGs are PRINTABLE STRINGs so + // that a sequence of them can be parsed into a []string. + if t.tag == tagGeneralString { + t.tag = tagPrintableString + } + if t.class != classUniversal || t.isCompound != compoundType || t.tag != expectedTag { + err = StructuralError{"sequence tag mismatch"} + return + } + if invalidLength(offset, t.length, len(bytes)) { + err = SyntaxError{"truncated sequence"} + return + } + offset += t.length + numElements++ + } + ret = reflect.MakeSlice(sliceType, numElements, numElements) + params := fieldParameters{} + offset := 0 + for i := 0; i < numElements; i++ { + offset, err = parseField(ret.Index(i), bytes, offset, params) + if err != nil { + return + } + } + return +} + +var ( + bitStringType = reflect.Typeof(BitString{}) + objectIdentifierType = reflect.Typeof(ObjectIdentifier{}) + enumeratedType = reflect.Typeof(Enumerated(0)) + flagType = reflect.Typeof(Flag(false)) + timeType = reflect.Typeof(&time.Time{}) + rawValueType = reflect.Typeof(RawValue{}) + rawContentsType = reflect.Typeof(RawContent(nil)) +) + +// invalidLength returns true iff offset + length > sliceLength, or if the +// addition would overflow. +func invalidLength(offset, length, sliceLength int) bool { + return offset+length < offset || offset+length > sliceLength +} + +// parseField is the main parsing function. Given a byte array and an offset +// into the array, it will try to parse a suitable ASN.1 value out and store it +// in the given Value. +func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParameters) (offset int, err os.Error) { + offset = initOffset + fieldType := v.Type() + + // If we have run out of data, it may be that there are optional elements at the end. + if offset == len(bytes) { + if !setDefaultValue(v, params) { + err = SyntaxError{"sequence truncated"} + } + return + } + + // Deal with raw values. + if fieldType == rawValueType { + var t tagAndLength + t, offset, err = parseTagAndLength(bytes, offset) + if err != nil { + return + } + if invalidLength(offset, t.length, len(bytes)) { + err = SyntaxError{"data truncated"} + return + } + result := RawValue{t.class, t.tag, t.isCompound, bytes[offset : offset+t.length], bytes[initOffset : offset+t.length]} + offset += t.length + v.Set(reflect.NewValue(result)) + return + } + + // Deal with the ANY type. + if ifaceType := fieldType; ifaceType.Kind() == reflect.Interface && ifaceType.NumMethod() == 0 { + ifaceValue := v + var t tagAndLength + t, offset, err = parseTagAndLength(bytes, offset) + if err != nil { + return + } + if invalidLength(offset, t.length, len(bytes)) { + err = SyntaxError{"data truncated"} + return + } + var result interface{} + if !t.isCompound && t.class == classUniversal { + innerBytes := bytes[offset : offset+t.length] + switch t.tag { + case tagPrintableString: + result, err = parsePrintableString(innerBytes) + case tagIA5String: + result, err = parseIA5String(innerBytes) + case tagT61String: + result, err = parseT61String(innerBytes) + case tagInteger: + result, err = parseInt64(innerBytes) + case tagBitString: + result, err = parseBitString(innerBytes) + case tagOID: + result, err = parseObjectIdentifier(innerBytes) + case tagUTCTime: + result, err = parseUTCTime(innerBytes) + case tagOctetString: + result = innerBytes + default: + // If we don't know how to handle the type, we just leave Value as nil. + } + } + offset += t.length + if err != nil { + return + } + if result != nil { + ifaceValue.Set(reflect.NewValue(result)) + } + return + } + universalTag, compoundType, ok1 := getUniversalType(fieldType) + if !ok1 { + err = StructuralError{fmt.Sprintf("unknown Go type: %v", fieldType)} + return + } + + t, offset, err := parseTagAndLength(bytes, offset) + if err != nil { + return + } + if params.explicit { + expectedClass := classContextSpecific + if params.application { + expectedClass = classApplication + } + if t.class == expectedClass && t.tag == *params.tag && (t.length == 0 || t.isCompound) { + if t.length > 0 { + t, offset, err = parseTagAndLength(bytes, offset) + if err != nil { + return + } + } else { + if fieldType != flagType { + err = StructuralError{"Zero length explicit tag was not an asn1.Flag"} + return + } + + flagValue := v + flagValue.SetBool(true) + return + } + } else { + // The tags didn't match, it might be an optional element. + ok := setDefaultValue(v, params) + if ok { + offset = initOffset + } else { + err = StructuralError{"explicitly tagged member didn't match"} + } + return + } + } + + // Special case for strings: PrintableString and IA5String both map to + // the Go type string. getUniversalType returns the tag for + // PrintableString when it sees a string so, if we see an IA5String on + // the wire, we change the universal type to match. + if universalTag == tagPrintableString && t.tag == tagIA5String { + universalTag = tagIA5String + } + // Likewise for GeneralString + if universalTag == tagPrintableString && t.tag == tagGeneralString { + universalTag = tagGeneralString + } + + // Special case for time: UTCTime and GeneralizedTime both map to the + // Go type time.Time. + if universalTag == tagUTCTime && t.tag == tagGeneralizedTime { + universalTag = tagGeneralizedTime + } + + expectedClass := classUniversal + expectedTag := universalTag + + if !params.explicit && params.tag != nil { + expectedClass = classContextSpecific + expectedTag = *params.tag + } + + if !params.explicit && params.application && params.tag != nil { + expectedClass = classApplication + expectedTag = *params.tag + } + + // We have unwrapped any explicit tagging at this point. + if t.class != expectedClass || t.tag != expectedTag || t.isCompound != compoundType { + // Tags don't match. Again, it could be an optional element. + ok := setDefaultValue(v, params) + if ok { + offset = initOffset + } else { + err = StructuralError{fmt.Sprintf("tags don't match (%d vs %+v) %+v %s @%d", expectedTag, t, params, fieldType.Name(), offset)} + } + return + } + if invalidLength(offset, t.length, len(bytes)) { + err = SyntaxError{"data truncated"} + return + } + innerBytes := bytes[offset : offset+t.length] + offset += t.length + + // We deal with the structures defined in this package first. + switch fieldType { + case objectIdentifierType: + newSlice, err1 := parseObjectIdentifier(innerBytes) + sliceValue := v + sliceValue.Set(reflect.MakeSlice(sliceValue.Type(), len(newSlice), len(newSlice))) + if err1 == nil { + reflect.Copy(sliceValue, reflect.NewValue(newSlice)) + } + err = err1 + return + case bitStringType: + structValue := v + bs, err1 := parseBitString(innerBytes) + if err1 == nil { + structValue.Set(reflect.NewValue(bs)) + } + err = err1 + return + case timeType: + ptrValue := v + var time *time.Time + var err1 os.Error + if universalTag == tagUTCTime { + time, err1 = parseUTCTime(innerBytes) + } else { + time, err1 = parseGeneralizedTime(innerBytes) + } + if err1 == nil { + ptrValue.Set(reflect.NewValue(time)) + } + err = err1 + return + case enumeratedType: + parsedInt, err1 := parseInt(innerBytes) + enumValue := v + if err1 == nil { + enumValue.SetInt(int64(parsedInt)) + } + err = err1 + return + case flagType: + flagValue := v + flagValue.SetBool(true) + return + } + switch val := v; val.Kind() { + case reflect.Bool: + parsedBool, err1 := parseBool(innerBytes) + if err1 == nil { + val.SetBool(parsedBool) + } + err = err1 + return + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + switch val.Type().Kind() { + case reflect.Int: + parsedInt, err1 := parseInt(innerBytes) + if err1 == nil { + val.SetInt(int64(parsedInt)) + } + err = err1 + return + case reflect.Int64: + parsedInt, err1 := parseInt64(innerBytes) + if err1 == nil { + val.SetInt(parsedInt) + } + err = err1 + return + } + case reflect.Struct: + structType := fieldType + + if structType.NumField() > 0 && + structType.Field(0).Type == rawContentsType { + bytes := bytes[initOffset:offset] + val.Field(0).Set(reflect.NewValue(RawContent(bytes))) + } + + innerOffset := 0 + for i := 0; i < structType.NumField(); i++ { + field := structType.Field(i) + if i == 0 && field.Type == rawContentsType { + continue + } + innerOffset, err = parseField(val.Field(i), innerBytes, innerOffset, parseFieldParameters(field.Tag)) + if err != nil { + return + } + } + // We allow extra bytes at the end of the SEQUENCE because + // adding elements to the end has been used in X.509 as the + // version numbers have increased. + return + case reflect.Slice: + sliceType := fieldType + if sliceType.Elem().Kind() == reflect.Uint8 { + val.Set(reflect.MakeSlice(sliceType, len(innerBytes), len(innerBytes))) + reflect.Copy(val, reflect.NewValue(innerBytes)) + return + } + newSlice, err1 := parseSequenceOf(innerBytes, sliceType, sliceType.Elem()) + if err1 == nil { + val.Set(newSlice) + } + err = err1 + return + case reflect.String: + var v string + switch universalTag { + case tagPrintableString: + v, err = parsePrintableString(innerBytes) + case tagIA5String: + v, err = parseIA5String(innerBytes) + case tagT61String: + v, err = parseT61String(innerBytes) + case tagGeneralString: + // GeneralString is specified in ISO-2022/ECMA-35, + // A brief review suggests that it includes structures + // that allow the encoding to change midstring and + // such. We give up and pass it as an 8-bit string. + v, err = parseT61String(innerBytes) + default: + err = SyntaxError{fmt.Sprintf("internal error: unknown string type %d", universalTag)} + } + if err == nil { + val.SetString(v) + } + return + } + err = StructuralError{"unknown Go type"} + return +} + +// setDefaultValue is used to install a default value, from a tag string, into +// a Value. It is successful is the field was optional, even if a default value +// wasn't provided or it failed to install it into the Value. +func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) { + if !params.optional { + return + } + ok = true + if params.defaultValue == nil { + return + } + switch val := v; val.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + val.SetInt(*params.defaultValue) + } + return +} + +// Unmarshal parses the DER-encoded ASN.1 data structure b +// and uses the reflect package to fill in an arbitrary value pointed at by val. +// Because Unmarshal uses the reflect package, the structs +// being written to must use upper case field names. +// +// An ASN.1 INTEGER can be written to an int or int64. +// If the encoded value does not fit in the Go type, +// Unmarshal returns a parse error. +// +// An ASN.1 BIT STRING can be written to a BitString. +// +// An ASN.1 OCTET STRING can be written to a []byte. +// +// An ASN.1 OBJECT IDENTIFIER can be written to an +// ObjectIdentifier. +// +// An ASN.1 ENUMERATED can be written to an Enumerated. +// +// An ASN.1 UTCTIME or GENERALIZEDTIME can be written to a *time.Time. +// +// An ASN.1 PrintableString or IA5String can be written to a string. +// +// Any of the above ASN.1 values can be written to an interface{}. +// The value stored in the interface has the corresponding Go type. +// For integers, that type is int64. +// +// An ASN.1 SEQUENCE OF x or SET OF x can be written +// to a slice if an x can be written to the slice's element type. +// +// An ASN.1 SEQUENCE or SET can be written to a struct +// if each of the elements in the sequence can be +// written to the corresponding element in the struct. +// +// The following tags on struct fields have special meaning to Unmarshal: +// +// optional marks the field as ASN.1 OPTIONAL +// [explicit] tag:x specifies the ASN.1 tag number; implies ASN.1 CONTEXT SPECIFIC +// default:x sets the default value for optional integer fields +// +// If the type of the first field of a structure is RawContent then the raw +// ASN1 contents of the struct will be stored in it. +// +// Other ASN.1 types are not supported; if it encounters them, +// Unmarshal returns a parse error. +func Unmarshal(b []byte, val interface{}) (rest []byte, err os.Error) { + return UnmarshalWithParams(b, val, "") +} + +// UnmarshalWithParams allows field parameters to be specified for the +// top-level element. The form of the params is the same as the field tags. +func UnmarshalWithParams(b []byte, val interface{}, params string) (rest []byte, err os.Error) { + v := reflect.NewValue(val).Elem() + offset, err := parseField(v, b, 0, parseFieldParameters(params)) + if err != nil { + return nil, err + } + return b[offset:], nil +} diff --git a/src/cmd/gofix/testdata/reflect.datafmt.go.in b/src/cmd/gofix/testdata/reflect.datafmt.go.in new file mode 100644 index 000000000..46c412342 --- /dev/null +++ b/src/cmd/gofix/testdata/reflect.datafmt.go.in @@ -0,0 +1,731 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* The datafmt package implements syntax-directed, type-driven formatting + of arbitrary data structures. Formatting a data structure consists of + two phases: first, a parser reads a format specification and builds a + "compiled" format. Then, the format can be applied repeatedly to + arbitrary values. Applying a format to a value evaluates to a []byte + containing the formatted value bytes, or nil. + + A format specification is a set of package declarations and format rules: + + Format = [ Entry { ";" Entry } [ ";" ] ] . + Entry = PackageDecl | FormatRule . + + (The syntax of a format specification is presented in the same EBNF + notation as used in the Go language specification. The syntax of white + space, comments, identifiers, and string literals is the same as in Go.) + + A package declaration binds a package name (such as 'ast') to a + package import path (such as '"go/ast"'). Each package used (in + a type name, see below) must be declared once before use. + + PackageDecl = PackageName ImportPath . + PackageName = identifier . + ImportPath = string . + + A format rule binds a rule name to a format expression. A rule name + may be a type name or one of the special names 'default' or '/'. + A type name may be the name of a predeclared type (for example, 'int', + 'float32', etc.), the package-qualified name of a user-defined type + (for example, 'ast.MapType'), or an identifier indicating the structure + of unnamed composite types ('array', 'chan', 'func', 'interface', 'map', + or 'ptr'). Each rule must have a unique name; rules can be declared in + any order. + + FormatRule = RuleName "=" Expression . + RuleName = TypeName | "default" | "/" . + TypeName = [ PackageName "." ] identifier . + + To format a value, the value's type name is used to select the format rule + (there is an override mechanism, see below). The format expression of the + selected rule specifies how the value is formatted. Each format expression, + when applied to a value, evaluates to a byte sequence or nil. + + In its most general form, a format expression is a list of alternatives, + each of which is a sequence of operands: + + Expression = [ Sequence ] { "|" [ Sequence ] } . + Sequence = Operand { Operand } . + + The formatted result produced by an expression is the result of the first + alternative sequence that evaluates to a non-nil result; if there is no + such alternative, the expression evaluates to nil. The result produced by + an operand sequence is the concatenation of the results of its operands. + If any operand in the sequence evaluates to nil, the entire sequence + evaluates to nil. + + There are five kinds of operands: + + Operand = Literal | Field | Group | Option | Repetition . + + Literals evaluate to themselves, with two substitutions. First, + %-formats expand in the manner of fmt.Printf, with the current value + passed as the parameter. Second, the current indentation (see below) + is inserted after every newline or form feed character. + + Literal = string . + + This table shows string literals applied to the value 42 and the + corresponding formatted result: + + "foo" foo + "%x" 2a + "x = %d" x = 42 + "%#x = %d" 0x2a = 42 + + A field operand is a field name optionally followed by an alternate + rule name. The field name may be an identifier or one of the special + names @ or *. + + Field = FieldName [ ":" RuleName ] . + FieldName = identifier | "@" | "*" . + + If the field name is an identifier, the current value must be a struct, + and there must be a field with that name in the struct. The same lookup + rules apply as in the Go language (for instance, the name of an anonymous + field is the unqualified type name). The field name denotes the field + value in the struct. If the field is not found, formatting is aborted + and an error message is returned. (TODO consider changing the semantics + such that if a field is not found, it evaluates to nil). + + The special name '@' denotes the current value. + + The meaning of the special name '*' depends on the type of the current + value: + + array, slice types array, slice element (inside {} only, see below) + interfaces value stored in interface + pointers value pointed to by pointer + + (Implementation restriction: channel, function and map types are not + supported due to missing reflection support). + + Fields are evaluated as follows: If the field value is nil, or an array + or slice element does not exist, the result is nil (see below for details + on array/slice elements). If the value is not nil the field value is + formatted (recursively) using the rule corresponding to its type name, + or the alternate rule name, if given. + + The following example shows a complete format specification for a + struct 'myPackage.Point'. Assume the package + + package myPackage // in directory myDir/myPackage + type Point struct { + name string; + x, y int; + } + + Applying the format specification + + myPackage "myDir/myPackage"; + int = "%d"; + hexInt = "0x%x"; + string = "---%s---"; + myPackage.Point = name "{" x ", " y:hexInt "}"; + + to the value myPackage.Point{"foo", 3, 15} results in + + ---foo---{3, 0xf} + + Finally, an operand may be a grouped, optional, or repeated expression. + A grouped expression ("group") groups a more complex expression (body) + so that it can be used in place of a single operand: + + Group = "(" [ Indentation ">>" ] Body ")" . + Indentation = Expression . + Body = Expression . + + A group body may be prefixed by an indentation expression followed by '>>'. + The indentation expression is applied to the current value like any other + expression and the result, if not nil, is appended to the current indentation + during the evaluation of the body (see also formatting state, below). + + An optional expression ("option") is enclosed in '[]' brackets. + + Option = "[" Body "]" . + + An option evaluates to its body, except that if the body evaluates to nil, + the option expression evaluates to an empty []byte. Thus an option's purpose + is to protect the expression containing the option from a nil operand. + + A repeated expression ("repetition") is enclosed in '{}' braces. + + Repetition = "{" Body [ "/" Separator ] "}" . + Separator = Expression . + + A repeated expression is evaluated as follows: The body is evaluated + repeatedly and its results are concatenated until the body evaluates + to nil. The result of the repetition is the (possibly empty) concatenation, + but it is never nil. An implicit index is supplied for the evaluation of + the body: that index is used to address elements of arrays or slices. If + the corresponding elements do not exist, the field denoting the element + evaluates to nil (which in turn may terminate the repetition). + + The body of a repetition may be followed by a '/' and a "separator" + expression. If the separator is present, it is invoked between repetitions + of the body. + + The following example shows a complete format specification for formatting + a slice of unnamed type. Applying the specification + + int = "%b"; + array = { * / ", " }; // array is the type name for an unnamed slice + + to the value '[]int{2, 3, 5, 7}' results in + + 10, 11, 101, 111 + + Default rule: If a format rule named 'default' is present, it is used for + formatting a value if no other rule was found. A common default rule is + + default = "%v" + + to provide default formatting for basic types without having to specify + a specific rule for each basic type. + + Global separator rule: If a format rule named '/' is present, it is + invoked with the current value between literals. If the separator + expression evaluates to nil, it is ignored. + + For instance, a global separator rule may be used to punctuate a sequence + of values with commas. The rules: + + default = "%v"; + / = ", "; + + will format an argument list by printing each one in its default format, + separated by a comma and a space. +*/ +package datafmt + +import ( + "bytes" + "fmt" + "go/token" + "io" + "os" + "reflect" + "runtime" +) + + +// ---------------------------------------------------------------------------- +// Format representation + +// Custom formatters implement the Formatter function type. +// A formatter is invoked with the current formatting state, the +// value to format, and the rule name under which the formatter +// was installed (the same formatter function may be installed +// under different names). The formatter may access the current state +// to guide formatting and use State.Write to append to the state's +// output. +// +// A formatter must return a boolean value indicating if it evaluated +// to a non-nil value (true), or a nil value (false). +// +type Formatter func(state *State, value interface{}, ruleName string) bool + + +// A FormatterMap is a set of custom formatters. +// It maps a rule name to a formatter function. +// +type FormatterMap map[string]Formatter + + +// A parsed format expression is built from the following nodes. +// +type ( + expr interface{} + + alternatives []expr // x | y | z + + sequence []expr // x y z + + literal [][]byte // a list of string segments, possibly starting with '%' + + field struct { + fieldName string // including "@", "*" + ruleName string // "" if no rule name specified + } + + group struct { + indent, body expr // (indent >> body) + } + + option struct { + body expr // [body] + } + + repetition struct { + body, separator expr // {body / separator} + } + + custom struct { + ruleName string + fun Formatter + } +) + + +// A Format is the result of parsing a format specification. +// The format may be applied repeatedly to format values. +// +type Format map[string]expr + + +// ---------------------------------------------------------------------------- +// Formatting + +// An application-specific environment may be provided to Format.Apply; +// the environment is available inside custom formatters via State.Env(). +// Environments must implement copying; the Copy method must return an +// complete copy of the receiver. This is necessary so that the formatter +// can save and restore an environment (in case of an absent expression). +// +// If the Environment doesn't change during formatting (this is under +// control of the custom formatters), the Copy function can simply return +// the receiver, and thus can be very light-weight. +// +type Environment interface { + Copy() Environment +} + + +// State represents the current formatting state. +// It is provided as argument to custom formatters. +// +type State struct { + fmt Format // format in use + env Environment // user-supplied environment + errors chan os.Error // not chan *Error (errors <- nil would be wrong!) + hasOutput bool // true after the first literal has been written + indent bytes.Buffer // current indentation + output bytes.Buffer // format output + linePos token.Position // position of line beginning (Column == 0) + default_ expr // possibly nil + separator expr // possibly nil +} + + +func newState(fmt Format, env Environment, errors chan os.Error) *State { + s := new(State) + s.fmt = fmt + s.env = env + s.errors = errors + s.linePos = token.Position{Line: 1} + + // if we have a default rule, cache it's expression for fast access + if x, found := fmt["default"]; found { + s.default_ = x + } + + // if we have a global separator rule, cache it's expression for fast access + if x, found := fmt["/"]; found { + s.separator = x + } + + return s +} + + +// Env returns the environment passed to Format.Apply. +func (s *State) Env() interface{} { return s.env } + + +// LinePos returns the position of the current line beginning +// in the state's output buffer. Line numbers start at 1. +// +func (s *State) LinePos() token.Position { return s.linePos } + + +// Pos returns the position of the next byte to be written to the +// output buffer. Line numbers start at 1. +// +func (s *State) Pos() token.Position { + offs := s.output.Len() + return token.Position{Line: s.linePos.Line, Column: offs - s.linePos.Offset, Offset: offs} +} + + +// Write writes data to the output buffer, inserting the indentation +// string after each newline or form feed character. It cannot return an error. +// +func (s *State) Write(data []byte) (int, os.Error) { + n := 0 + i0 := 0 + for i, ch := range data { + if ch == '\n' || ch == '\f' { + // write text segment and indentation + n1, _ := s.output.Write(data[i0 : i+1]) + n2, _ := s.output.Write(s.indent.Bytes()) + n += n1 + n2 + i0 = i + 1 + s.linePos.Offset = s.output.Len() + s.linePos.Line++ + } + } + n3, _ := s.output.Write(data[i0:]) + return n + n3, nil +} + + +type checkpoint struct { + env Environment + hasOutput bool + outputLen int + linePos token.Position +} + + +func (s *State) save() checkpoint { + saved := checkpoint{nil, s.hasOutput, s.output.Len(), s.linePos} + if s.env != nil { + saved.env = s.env.Copy() + } + return saved +} + + +func (s *State) restore(m checkpoint) { + s.env = m.env + s.output.Truncate(m.outputLen) +} + + +func (s *State) error(msg string) { + s.errors <- os.NewError(msg) + runtime.Goexit() +} + + +// TODO At the moment, unnamed types are simply mapped to the default +// names below. For instance, all unnamed arrays are mapped to +// 'array' which is not really sufficient. Eventually one may want +// to be able to specify rules for say an unnamed slice of T. +// + +func typename(typ reflect.Type) string { + switch typ.(type) { + case *reflect.ArrayType: + return "array" + case *reflect.SliceType: + return "array" + case *reflect.ChanType: + return "chan" + case *reflect.FuncType: + return "func" + case *reflect.InterfaceType: + return "interface" + case *reflect.MapType: + return "map" + case *reflect.PtrType: + return "ptr" + } + return typ.String() +} + +func (s *State) getFormat(name string) expr { + if fexpr, found := s.fmt[name]; found { + return fexpr + } + + if s.default_ != nil { + return s.default_ + } + + s.error(fmt.Sprintf("no format rule for type: '%s'", name)) + return nil +} + + +// eval applies a format expression fexpr to a value. If the expression +// evaluates internally to a non-nil []byte, that slice is appended to +// the state's output buffer and eval returns true. Otherwise, eval +// returns false and the state remains unchanged. +// +func (s *State) eval(fexpr expr, value reflect.Value, index int) bool { + // an empty format expression always evaluates + // to a non-nil (but empty) []byte + if fexpr == nil { + return true + } + + switch t := fexpr.(type) { + case alternatives: + // append the result of the first alternative that evaluates to + // a non-nil []byte to the state's output + mark := s.save() + for _, x := range t { + if s.eval(x, value, index) { + return true + } + s.restore(mark) + } + return false + + case sequence: + // append the result of all operands to the state's output + // unless a nil result is encountered + mark := s.save() + for _, x := range t { + if !s.eval(x, value, index) { + s.restore(mark) + return false + } + } + return true + + case literal: + // write separator, if any + if s.hasOutput { + // not the first literal + if s.separator != nil { + sep := s.separator // save current separator + s.separator = nil // and disable it (avoid recursion) + mark := s.save() + if !s.eval(sep, value, index) { + s.restore(mark) + } + s.separator = sep // enable it again + } + } + s.hasOutput = true + // write literal segments + for _, lit := range t { + if len(lit) > 1 && lit[0] == '%' { + // segment contains a %-format at the beginning + if lit[1] == '%' { + // "%%" is printed as a single "%" + s.Write(lit[1:]) + } else { + // use s instead of s.output to get indentation right + fmt.Fprintf(s, string(lit), value.Interface()) + } + } else { + // segment contains no %-formats + s.Write(lit) + } + } + return true // a literal never evaluates to nil + + case *field: + // determine field value + switch t.fieldName { + case "@": + // field value is current value + + case "*": + // indirection: operation is type-specific + switch v := value.(type) { + case *reflect.ArrayValue: + if v.Len() <= index { + return false + } + value = v.Elem(index) + + case *reflect.SliceValue: + if v.IsNil() || v.Len() <= index { + return false + } + value = v.Elem(index) + + case *reflect.MapValue: + s.error("reflection support for maps incomplete") + + case *reflect.PtrValue: + if v.IsNil() { + return false + } + value = v.Elem() + + case *reflect.InterfaceValue: + if v.IsNil() { + return false + } + value = v.Elem() + + case *reflect.ChanValue: + s.error("reflection support for chans incomplete") + + case *reflect.FuncValue: + s.error("reflection support for funcs incomplete") + + default: + s.error(fmt.Sprintf("error: * does not apply to `%s`", value.Type())) + } + + default: + // value is value of named field + var field reflect.Value + if sval, ok := value.(*reflect.StructValue); ok { + field = sval.FieldByName(t.fieldName) + if field == nil { + // TODO consider just returning false in this case + s.error(fmt.Sprintf("error: no field `%s` in `%s`", t.fieldName, value.Type())) + } + } + value = field + } + + // determine rule + ruleName := t.ruleName + if ruleName == "" { + // no alternate rule name, value type determines rule + ruleName = typename(value.Type()) + } + fexpr = s.getFormat(ruleName) + + mark := s.save() + if !s.eval(fexpr, value, index) { + s.restore(mark) + return false + } + return true + + case *group: + // remember current indentation + indentLen := s.indent.Len() + + // update current indentation + mark := s.save() + s.eval(t.indent, value, index) + // if the indentation evaluates to nil, the state's output buffer + // didn't change - either way it's ok to append the difference to + // the current identation + s.indent.Write(s.output.Bytes()[mark.outputLen:s.output.Len()]) + s.restore(mark) + + // format group body + mark = s.save() + b := true + if !s.eval(t.body, value, index) { + s.restore(mark) + b = false + } + + // reset indentation + s.indent.Truncate(indentLen) + return b + + case *option: + // evaluate the body and append the result to the state's output + // buffer unless the result is nil + mark := s.save() + if !s.eval(t.body, value, 0) { // TODO is 0 index correct? + s.restore(mark) + } + return true // an option never evaluates to nil + + case *repetition: + // evaluate the body and append the result to the state's output + // buffer until a result is nil + for i := 0; ; i++ { + mark := s.save() + // write separator, if any + if i > 0 && t.separator != nil { + // nil result from separator is ignored + mark := s.save() + if !s.eval(t.separator, value, i) { + s.restore(mark) + } + } + if !s.eval(t.body, value, i) { + s.restore(mark) + break + } + } + return true // a repetition never evaluates to nil + + case *custom: + // invoke the custom formatter to obtain the result + mark := s.save() + if !t.fun(s, value.Interface(), t.ruleName) { + s.restore(mark) + return false + } + return true + } + + panic("unreachable") + return false +} + + +// Eval formats each argument according to the format +// f and returns the resulting []byte and os.Error. If +// an error occurred, the []byte contains the partially +// formatted result. An environment env may be passed +// in which is available in custom formatters through +// the state parameter. +// +func (f Format) Eval(env Environment, args ...interface{}) ([]byte, os.Error) { + if f == nil { + return nil, os.NewError("format is nil") + } + + errors := make(chan os.Error) + s := newState(f, env, errors) + + go func() { + for _, v := range args { + fld := reflect.NewValue(v) + if fld == nil { + errors <- os.NewError("nil argument") + return + } + mark := s.save() + if !s.eval(s.getFormat(typename(fld.Type())), fld, 0) { // TODO is 0 index correct? + s.restore(mark) + } + } + errors <- nil // no errors + }() + + err := <-errors + return s.output.Bytes(), err +} + + +// ---------------------------------------------------------------------------- +// Convenience functions + +// Fprint formats each argument according to the format f +// and writes to w. The result is the total number of bytes +// written and an os.Error, if any. +// +func (f Format) Fprint(w io.Writer, env Environment, args ...interface{}) (int, os.Error) { + data, err := f.Eval(env, args...) + if err != nil { + // TODO should we print partial result in case of error? + return 0, err + } + return w.Write(data) +} + + +// Print formats each argument according to the format f +// and writes to standard output. The result is the total +// number of bytes written and an os.Error, if any. +// +func (f Format) Print(args ...interface{}) (int, os.Error) { + return f.Fprint(os.Stdout, nil, args...) +} + + +// Sprint formats each argument according to the format f +// and returns the resulting string. If an error occurs +// during formatting, the result string contains the +// partially formatted result followed by an error message. +// +func (f Format) Sprint(args ...interface{}) string { + var buf bytes.Buffer + _, err := f.Fprint(&buf, nil, args...) + if err != nil { + var i interface{} = args + fmt.Fprintf(&buf, "--- Sprint(%s) failed: %v", fmt.Sprint(i), err) + } + return buf.String() +} diff --git a/src/cmd/gofix/testdata/reflect.datafmt.go.out b/src/cmd/gofix/testdata/reflect.datafmt.go.out new file mode 100644 index 000000000..6d816fc2d --- /dev/null +++ b/src/cmd/gofix/testdata/reflect.datafmt.go.out @@ -0,0 +1,731 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* The datafmt package implements syntax-directed, type-driven formatting + of arbitrary data structures. Formatting a data structure consists of + two phases: first, a parser reads a format specification and builds a + "compiled" format. Then, the format can be applied repeatedly to + arbitrary values. Applying a format to a value evaluates to a []byte + containing the formatted value bytes, or nil. + + A format specification is a set of package declarations and format rules: + + Format = [ Entry { ";" Entry } [ ";" ] ] . + Entry = PackageDecl | FormatRule . + + (The syntax of a format specification is presented in the same EBNF + notation as used in the Go language specification. The syntax of white + space, comments, identifiers, and string literals is the same as in Go.) + + A package declaration binds a package name (such as 'ast') to a + package import path (such as '"go/ast"'). Each package used (in + a type name, see below) must be declared once before use. + + PackageDecl = PackageName ImportPath . + PackageName = identifier . + ImportPath = string . + + A format rule binds a rule name to a format expression. A rule name + may be a type name or one of the special names 'default' or '/'. + A type name may be the name of a predeclared type (for example, 'int', + 'float32', etc.), the package-qualified name of a user-defined type + (for example, 'ast.MapType'), or an identifier indicating the structure + of unnamed composite types ('array', 'chan', 'func', 'interface', 'map', + or 'ptr'). Each rule must have a unique name; rules can be declared in + any order. + + FormatRule = RuleName "=" Expression . + RuleName = TypeName | "default" | "/" . + TypeName = [ PackageName "." ] identifier . + + To format a value, the value's type name is used to select the format rule + (there is an override mechanism, see below). The format expression of the + selected rule specifies how the value is formatted. Each format expression, + when applied to a value, evaluates to a byte sequence or nil. + + In its most general form, a format expression is a list of alternatives, + each of which is a sequence of operands: + + Expression = [ Sequence ] { "|" [ Sequence ] } . + Sequence = Operand { Operand } . + + The formatted result produced by an expression is the result of the first + alternative sequence that evaluates to a non-nil result; if there is no + such alternative, the expression evaluates to nil. The result produced by + an operand sequence is the concatenation of the results of its operands. + If any operand in the sequence evaluates to nil, the entire sequence + evaluates to nil. + + There are five kinds of operands: + + Operand = Literal | Field | Group | Option | Repetition . + + Literals evaluate to themselves, with two substitutions. First, + %-formats expand in the manner of fmt.Printf, with the current value + passed as the parameter. Second, the current indentation (see below) + is inserted after every newline or form feed character. + + Literal = string . + + This table shows string literals applied to the value 42 and the + corresponding formatted result: + + "foo" foo + "%x" 2a + "x = %d" x = 42 + "%#x = %d" 0x2a = 42 + + A field operand is a field name optionally followed by an alternate + rule name. The field name may be an identifier or one of the special + names @ or *. + + Field = FieldName [ ":" RuleName ] . + FieldName = identifier | "@" | "*" . + + If the field name is an identifier, the current value must be a struct, + and there must be a field with that name in the struct. The same lookup + rules apply as in the Go language (for instance, the name of an anonymous + field is the unqualified type name). The field name denotes the field + value in the struct. If the field is not found, formatting is aborted + and an error message is returned. (TODO consider changing the semantics + such that if a field is not found, it evaluates to nil). + + The special name '@' denotes the current value. + + The meaning of the special name '*' depends on the type of the current + value: + + array, slice types array, slice element (inside {} only, see below) + interfaces value stored in interface + pointers value pointed to by pointer + + (Implementation restriction: channel, function and map types are not + supported due to missing reflection support). + + Fields are evaluated as follows: If the field value is nil, or an array + or slice element does not exist, the result is nil (see below for details + on array/slice elements). If the value is not nil the field value is + formatted (recursively) using the rule corresponding to its type name, + or the alternate rule name, if given. + + The following example shows a complete format specification for a + struct 'myPackage.Point'. Assume the package + + package myPackage // in directory myDir/myPackage + type Point struct { + name string; + x, y int; + } + + Applying the format specification + + myPackage "myDir/myPackage"; + int = "%d"; + hexInt = "0x%x"; + string = "---%s---"; + myPackage.Point = name "{" x ", " y:hexInt "}"; + + to the value myPackage.Point{"foo", 3, 15} results in + + ---foo---{3, 0xf} + + Finally, an operand may be a grouped, optional, or repeated expression. + A grouped expression ("group") groups a more complex expression (body) + so that it can be used in place of a single operand: + + Group = "(" [ Indentation ">>" ] Body ")" . + Indentation = Expression . + Body = Expression . + + A group body may be prefixed by an indentation expression followed by '>>'. + The indentation expression is applied to the current value like any other + expression and the result, if not nil, is appended to the current indentation + during the evaluation of the body (see also formatting state, below). + + An optional expression ("option") is enclosed in '[]' brackets. + + Option = "[" Body "]" . + + An option evaluates to its body, except that if the body evaluates to nil, + the option expression evaluates to an empty []byte. Thus an option's purpose + is to protect the expression containing the option from a nil operand. + + A repeated expression ("repetition") is enclosed in '{}' braces. + + Repetition = "{" Body [ "/" Separator ] "}" . + Separator = Expression . + + A repeated expression is evaluated as follows: The body is evaluated + repeatedly and its results are concatenated until the body evaluates + to nil. The result of the repetition is the (possibly empty) concatenation, + but it is never nil. An implicit index is supplied for the evaluation of + the body: that index is used to address elements of arrays or slices. If + the corresponding elements do not exist, the field denoting the element + evaluates to nil (which in turn may terminate the repetition). + + The body of a repetition may be followed by a '/' and a "separator" + expression. If the separator is present, it is invoked between repetitions + of the body. + + The following example shows a complete format specification for formatting + a slice of unnamed type. Applying the specification + + int = "%b"; + array = { * / ", " }; // array is the type name for an unnamed slice + + to the value '[]int{2, 3, 5, 7}' results in + + 10, 11, 101, 111 + + Default rule: If a format rule named 'default' is present, it is used for + formatting a value if no other rule was found. A common default rule is + + default = "%v" + + to provide default formatting for basic types without having to specify + a specific rule for each basic type. + + Global separator rule: If a format rule named '/' is present, it is + invoked with the current value between literals. If the separator + expression evaluates to nil, it is ignored. + + For instance, a global separator rule may be used to punctuate a sequence + of values with commas. The rules: + + default = "%v"; + / = ", "; + + will format an argument list by printing each one in its default format, + separated by a comma and a space. +*/ +package datafmt + +import ( + "bytes" + "fmt" + "go/token" + "io" + "os" + "reflect" + "runtime" +) + + +// ---------------------------------------------------------------------------- +// Format representation + +// Custom formatters implement the Formatter function type. +// A formatter is invoked with the current formatting state, the +// value to format, and the rule name under which the formatter +// was installed (the same formatter function may be installed +// under different names). The formatter may access the current state +// to guide formatting and use State.Write to append to the state's +// output. +// +// A formatter must return a boolean value indicating if it evaluated +// to a non-nil value (true), or a nil value (false). +// +type Formatter func(state *State, value interface{}, ruleName string) bool + + +// A FormatterMap is a set of custom formatters. +// It maps a rule name to a formatter function. +// +type FormatterMap map[string]Formatter + + +// A parsed format expression is built from the following nodes. +// +type ( + expr interface{} + + alternatives []expr // x | y | z + + sequence []expr // x y z + + literal [][]byte // a list of string segments, possibly starting with '%' + + field struct { + fieldName string // including "@", "*" + ruleName string // "" if no rule name specified + } + + group struct { + indent, body expr // (indent >> body) + } + + option struct { + body expr // [body] + } + + repetition struct { + body, separator expr // {body / separator} + } + + custom struct { + ruleName string + fun Formatter + } +) + + +// A Format is the result of parsing a format specification. +// The format may be applied repeatedly to format values. +// +type Format map[string]expr + + +// ---------------------------------------------------------------------------- +// Formatting + +// An application-specific environment may be provided to Format.Apply; +// the environment is available inside custom formatters via State.Env(). +// Environments must implement copying; the Copy method must return an +// complete copy of the receiver. This is necessary so that the formatter +// can save and restore an environment (in case of an absent expression). +// +// If the Environment doesn't change during formatting (this is under +// control of the custom formatters), the Copy function can simply return +// the receiver, and thus can be very light-weight. +// +type Environment interface { + Copy() Environment +} + + +// State represents the current formatting state. +// It is provided as argument to custom formatters. +// +type State struct { + fmt Format // format in use + env Environment // user-supplied environment + errors chan os.Error // not chan *Error (errors <- nil would be wrong!) + hasOutput bool // true after the first literal has been written + indent bytes.Buffer // current indentation + output bytes.Buffer // format output + linePos token.Position // position of line beginning (Column == 0) + default_ expr // possibly nil + separator expr // possibly nil +} + + +func newState(fmt Format, env Environment, errors chan os.Error) *State { + s := new(State) + s.fmt = fmt + s.env = env + s.errors = errors + s.linePos = token.Position{Line: 1} + + // if we have a default rule, cache it's expression for fast access + if x, found := fmt["default"]; found { + s.default_ = x + } + + // if we have a global separator rule, cache it's expression for fast access + if x, found := fmt["/"]; found { + s.separator = x + } + + return s +} + + +// Env returns the environment passed to Format.Apply. +func (s *State) Env() interface{} { return s.env } + + +// LinePos returns the position of the current line beginning +// in the state's output buffer. Line numbers start at 1. +// +func (s *State) LinePos() token.Position { return s.linePos } + + +// Pos returns the position of the next byte to be written to the +// output buffer. Line numbers start at 1. +// +func (s *State) Pos() token.Position { + offs := s.output.Len() + return token.Position{Line: s.linePos.Line, Column: offs - s.linePos.Offset, Offset: offs} +} + + +// Write writes data to the output buffer, inserting the indentation +// string after each newline or form feed character. It cannot return an error. +// +func (s *State) Write(data []byte) (int, os.Error) { + n := 0 + i0 := 0 + for i, ch := range data { + if ch == '\n' || ch == '\f' { + // write text segment and indentation + n1, _ := s.output.Write(data[i0 : i+1]) + n2, _ := s.output.Write(s.indent.Bytes()) + n += n1 + n2 + i0 = i + 1 + s.linePos.Offset = s.output.Len() + s.linePos.Line++ + } + } + n3, _ := s.output.Write(data[i0:]) + return n + n3, nil +} + + +type checkpoint struct { + env Environment + hasOutput bool + outputLen int + linePos token.Position +} + + +func (s *State) save() checkpoint { + saved := checkpoint{nil, s.hasOutput, s.output.Len(), s.linePos} + if s.env != nil { + saved.env = s.env.Copy() + } + return saved +} + + +func (s *State) restore(m checkpoint) { + s.env = m.env + s.output.Truncate(m.outputLen) +} + + +func (s *State) error(msg string) { + s.errors <- os.NewError(msg) + runtime.Goexit() +} + + +// TODO At the moment, unnamed types are simply mapped to the default +// names below. For instance, all unnamed arrays are mapped to +// 'array' which is not really sufficient. Eventually one may want +// to be able to specify rules for say an unnamed slice of T. +// + +func typename(typ reflect.Type) string { + switch typ.Kind() { + case reflect.Array: + return "array" + case reflect.Slice: + return "array" + case reflect.Chan: + return "chan" + case reflect.Func: + return "func" + case reflect.Interface: + return "interface" + case reflect.Map: + return "map" + case reflect.Ptr: + return "ptr" + } + return typ.String() +} + +func (s *State) getFormat(name string) expr { + if fexpr, found := s.fmt[name]; found { + return fexpr + } + + if s.default_ != nil { + return s.default_ + } + + s.error(fmt.Sprintf("no format rule for type: '%s'", name)) + return nil +} + + +// eval applies a format expression fexpr to a value. If the expression +// evaluates internally to a non-nil []byte, that slice is appended to +// the state's output buffer and eval returns true. Otherwise, eval +// returns false and the state remains unchanged. +// +func (s *State) eval(fexpr expr, value reflect.Value, index int) bool { + // an empty format expression always evaluates + // to a non-nil (but empty) []byte + if fexpr == nil { + return true + } + + switch t := fexpr.(type) { + case alternatives: + // append the result of the first alternative that evaluates to + // a non-nil []byte to the state's output + mark := s.save() + for _, x := range t { + if s.eval(x, value, index) { + return true + } + s.restore(mark) + } + return false + + case sequence: + // append the result of all operands to the state's output + // unless a nil result is encountered + mark := s.save() + for _, x := range t { + if !s.eval(x, value, index) { + s.restore(mark) + return false + } + } + return true + + case literal: + // write separator, if any + if s.hasOutput { + // not the first literal + if s.separator != nil { + sep := s.separator // save current separator + s.separator = nil // and disable it (avoid recursion) + mark := s.save() + if !s.eval(sep, value, index) { + s.restore(mark) + } + s.separator = sep // enable it again + } + } + s.hasOutput = true + // write literal segments + for _, lit := range t { + if len(lit) > 1 && lit[0] == '%' { + // segment contains a %-format at the beginning + if lit[1] == '%' { + // "%%" is printed as a single "%" + s.Write(lit[1:]) + } else { + // use s instead of s.output to get indentation right + fmt.Fprintf(s, string(lit), value.Interface()) + } + } else { + // segment contains no %-formats + s.Write(lit) + } + } + return true // a literal never evaluates to nil + + case *field: + // determine field value + switch t.fieldName { + case "@": + // field value is current value + + case "*": + // indirection: operation is type-specific + switch v := value; v.Kind() { + case reflect.Array: + if v.Len() <= index { + return false + } + value = v.Index(index) + + case reflect.Slice: + if v.IsNil() || v.Len() <= index { + return false + } + value = v.Index(index) + + case reflect.Map: + s.error("reflection support for maps incomplete") + + case reflect.Ptr: + if v.IsNil() { + return false + } + value = v.Elem() + + case reflect.Interface: + if v.IsNil() { + return false + } + value = v.Elem() + + case reflect.Chan: + s.error("reflection support for chans incomplete") + + case reflect.Func: + s.error("reflection support for funcs incomplete") + + default: + s.error(fmt.Sprintf("error: * does not apply to `%s`", value.Type())) + } + + default: + // value is value of named field + var field reflect.Value + if sval := value; sval.Kind() == reflect.Struct { + field = sval.FieldByName(t.fieldName) + if !field.IsValid() { + // TODO consider just returning false in this case + s.error(fmt.Sprintf("error: no field `%s` in `%s`", t.fieldName, value.Type())) + } + } + value = field + } + + // determine rule + ruleName := t.ruleName + if ruleName == "" { + // no alternate rule name, value type determines rule + ruleName = typename(value.Type()) + } + fexpr = s.getFormat(ruleName) + + mark := s.save() + if !s.eval(fexpr, value, index) { + s.restore(mark) + return false + } + return true + + case *group: + // remember current indentation + indentLen := s.indent.Len() + + // update current indentation + mark := s.save() + s.eval(t.indent, value, index) + // if the indentation evaluates to nil, the state's output buffer + // didn't change - either way it's ok to append the difference to + // the current identation + s.indent.Write(s.output.Bytes()[mark.outputLen:s.output.Len()]) + s.restore(mark) + + // format group body + mark = s.save() + b := true + if !s.eval(t.body, value, index) { + s.restore(mark) + b = false + } + + // reset indentation + s.indent.Truncate(indentLen) + return b + + case *option: + // evaluate the body and append the result to the state's output + // buffer unless the result is nil + mark := s.save() + if !s.eval(t.body, value, 0) { // TODO is 0 index correct? + s.restore(mark) + } + return true // an option never evaluates to nil + + case *repetition: + // evaluate the body and append the result to the state's output + // buffer until a result is nil + for i := 0; ; i++ { + mark := s.save() + // write separator, if any + if i > 0 && t.separator != nil { + // nil result from separator is ignored + mark := s.save() + if !s.eval(t.separator, value, i) { + s.restore(mark) + } + } + if !s.eval(t.body, value, i) { + s.restore(mark) + break + } + } + return true // a repetition never evaluates to nil + + case *custom: + // invoke the custom formatter to obtain the result + mark := s.save() + if !t.fun(s, value.Interface(), t.ruleName) { + s.restore(mark) + return false + } + return true + } + + panic("unreachable") + return false +} + + +// Eval formats each argument according to the format +// f and returns the resulting []byte and os.Error. If +// an error occurred, the []byte contains the partially +// formatted result. An environment env may be passed +// in which is available in custom formatters through +// the state parameter. +// +func (f Format) Eval(env Environment, args ...interface{}) ([]byte, os.Error) { + if f == nil { + return nil, os.NewError("format is nil") + } + + errors := make(chan os.Error) + s := newState(f, env, errors) + + go func() { + for _, v := range args { + fld := reflect.NewValue(v) + if !fld.IsValid() { + errors <- os.NewError("nil argument") + return + } + mark := s.save() + if !s.eval(s.getFormat(typename(fld.Type())), fld, 0) { // TODO is 0 index correct? + s.restore(mark) + } + } + errors <- nil // no errors + }() + + err := <-errors + return s.output.Bytes(), err +} + + +// ---------------------------------------------------------------------------- +// Convenience functions + +// Fprint formats each argument according to the format f +// and writes to w. The result is the total number of bytes +// written and an os.Error, if any. +// +func (f Format) Fprint(w io.Writer, env Environment, args ...interface{}) (int, os.Error) { + data, err := f.Eval(env, args...) + if err != nil { + // TODO should we print partial result in case of error? + return 0, err + } + return w.Write(data) +} + + +// Print formats each argument according to the format f +// and writes to standard output. The result is the total +// number of bytes written and an os.Error, if any. +// +func (f Format) Print(args ...interface{}) (int, os.Error) { + return f.Fprint(os.Stdout, nil, args...) +} + + +// Sprint formats each argument according to the format f +// and returns the resulting string. If an error occurs +// during formatting, the result string contains the +// partially formatted result followed by an error message. +// +func (f Format) Sprint(args ...interface{}) string { + var buf bytes.Buffer + _, err := f.Fprint(&buf, nil, args...) + if err != nil { + var i interface{} = args + fmt.Fprintf(&buf, "--- Sprint(%s) failed: %v", fmt.Sprint(i), err) + } + return buf.String() +} diff --git a/src/cmd/gofix/testdata/reflect.decode.go.in b/src/cmd/gofix/testdata/reflect.decode.go.in new file mode 100644 index 000000000..501230c0c --- /dev/null +++ b/src/cmd/gofix/testdata/reflect.decode.go.in @@ -0,0 +1,907 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Represents JSON data structure using native Go types: booleans, floats, +// strings, arrays, and maps. + +package json + +import ( + "container/vector" + "encoding/base64" + "os" + "reflect" + "runtime" + "strconv" + "strings" + "unicode" + "utf16" + "utf8" +) + +// Unmarshal parses the JSON-encoded data and stores the result +// in the value pointed to by v. +// +// Unmarshal traverses the value v recursively. +// If an encountered value implements the Unmarshaler interface, +// Unmarshal calls its UnmarshalJSON method with a well-formed +// JSON encoding. +// +// Otherwise, Unmarshal uses the inverse of the encodings that +// Marshal uses, allocating maps, slices, and pointers as necessary, +// with the following additional rules: +// +// To unmarshal a JSON value into a nil interface value, the +// type stored in the interface value is one of: +// +// bool, for JSON booleans +// float64, for JSON numbers +// string, for JSON strings +// []interface{}, for JSON arrays +// map[string]interface{}, for JSON objects +// nil for JSON null +// +// If a JSON value is not appropriate for a given target type, +// or if a JSON number overflows the target type, Unmarshal +// skips that field and completes the unmarshalling as best it can. +// If no more serious errors are encountered, Unmarshal returns +// an UnmarshalTypeError describing the earliest such error. +// +func Unmarshal(data []byte, v interface{}) os.Error { + d := new(decodeState).init(data) + + // Quick check for well-formedness. + // Avoids filling out half a data structure + // before discovering a JSON syntax error. + err := checkValid(data, &d.scan) + if err != nil { + return err + } + + return d.unmarshal(v) +} + +// Unmarshaler is the interface implemented by objects +// that can unmarshal a JSON description of themselves. +// The input can be assumed to be a valid JSON object +// encoding. UnmarshalJSON must copy the JSON data +// if it wishes to retain the data after returning. +type Unmarshaler interface { + UnmarshalJSON([]byte) os.Error +} + + +// An UnmarshalTypeError describes a JSON value that was +// not appropriate for a value of a specific Go type. +type UnmarshalTypeError struct { + Value string // description of JSON value - "bool", "array", "number -5" + Type reflect.Type // type of Go value it could not be assigned to +} + +func (e *UnmarshalTypeError) String() string { + return "json: cannot unmarshal " + e.Value + " into Go value of type " + e.Type.String() +} + +// An UnmarshalFieldError describes a JSON object key that +// led to an unexported (and therefore unwritable) struct field. +type UnmarshalFieldError struct { + Key string + Type *reflect.StructType + Field reflect.StructField +} + +func (e *UnmarshalFieldError) String() string { + return "json: cannot unmarshal object key " + strconv.Quote(e.Key) + " into unexported field " + e.Field.Name + " of type " + e.Type.String() +} + +// An InvalidUnmarshalError describes an invalid argument passed to Unmarshal. +// (The argument to Unmarshal must be a non-nil pointer.) +type InvalidUnmarshalError struct { + Type reflect.Type +} + +func (e *InvalidUnmarshalError) String() string { + if e.Type == nil { + return "json: Unmarshal(nil)" + } + + if _, ok := e.Type.(*reflect.PtrType); !ok { + return "json: Unmarshal(non-pointer " + e.Type.String() + ")" + } + return "json: Unmarshal(nil " + e.Type.String() + ")" +} + +func (d *decodeState) unmarshal(v interface{}) (err os.Error) { + defer func() { + if r := recover(); r != nil { + if _, ok := r.(runtime.Error); ok { + panic(r) + } + err = r.(os.Error) + } + }() + + rv := reflect.NewValue(v) + pv, ok := rv.(*reflect.PtrValue) + if !ok || pv.IsNil() { + return &InvalidUnmarshalError{reflect.Typeof(v)} + } + + d.scan.reset() + // We decode rv not pv.Elem because the Unmarshaler interface + // test must be applied at the top level of the value. + d.value(rv) + return d.savedError +} + +// decodeState represents the state while decoding a JSON value. +type decodeState struct { + data []byte + off int // read offset in data + scan scanner + nextscan scanner // for calls to nextValue + savedError os.Error +} + +// errPhase is used for errors that should not happen unless +// there is a bug in the JSON decoder or something is editing +// the data slice while the decoder executes. +var errPhase = os.NewError("JSON decoder out of sync - data changing underfoot?") + +func (d *decodeState) init(data []byte) *decodeState { + d.data = data + d.off = 0 + d.savedError = nil + return d +} + +// error aborts the decoding by panicking with err. +func (d *decodeState) error(err os.Error) { + panic(err) +} + +// saveError saves the first err it is called with, +// for reporting at the end of the unmarshal. +func (d *decodeState) saveError(err os.Error) { + if d.savedError == nil { + d.savedError = err + } +} + +// next cuts off and returns the next full JSON value in d.data[d.off:]. +// The next value is known to be an object or array, not a literal. +func (d *decodeState) next() []byte { + c := d.data[d.off] + item, rest, err := nextValue(d.data[d.off:], &d.nextscan) + if err != nil { + d.error(err) + } + d.off = len(d.data) - len(rest) + + // Our scanner has seen the opening brace/bracket + // and thinks we're still in the middle of the object. + // invent a closing brace/bracket to get it out. + if c == '{' { + d.scan.step(&d.scan, '}') + } else { + d.scan.step(&d.scan, ']') + } + + return item +} + +// scanWhile processes bytes in d.data[d.off:] until it +// receives a scan code not equal to op. +// It updates d.off and returns the new scan code. +func (d *decodeState) scanWhile(op int) int { + var newOp int + for { + if d.off >= len(d.data) { + newOp = d.scan.eof() + d.off = len(d.data) + 1 // mark processed EOF with len+1 + } else { + c := int(d.data[d.off]) + d.off++ + newOp = d.scan.step(&d.scan, c) + } + if newOp != op { + break + } + } + return newOp +} + +// value decodes a JSON value from d.data[d.off:] into the value. +// it updates d.off to point past the decoded value. +func (d *decodeState) value(v reflect.Value) { + if v == nil { + _, rest, err := nextValue(d.data[d.off:], &d.nextscan) + if err != nil { + d.error(err) + } + d.off = len(d.data) - len(rest) + + // d.scan thinks we're still at the beginning of the item. + // Feed in an empty string - the shortest, simplest value - + // so that it knows we got to the end of the value. + if d.scan.step == stateRedo { + panic("redo") + } + d.scan.step(&d.scan, '"') + d.scan.step(&d.scan, '"') + return + } + + switch op := d.scanWhile(scanSkipSpace); op { + default: + d.error(errPhase) + + case scanBeginArray: + d.array(v) + + case scanBeginObject: + d.object(v) + + case scanBeginLiteral: + d.literal(v) + } +} + +// indirect walks down v allocating pointers as needed, +// until it gets to a non-pointer. +// if it encounters an Unmarshaler, indirect stops and returns that. +// if wantptr is true, indirect stops at the last pointer. +func (d *decodeState) indirect(v reflect.Value, wantptr bool) (Unmarshaler, reflect.Value) { + for { + var isUnmarshaler bool + if v.Type().NumMethod() > 0 { + // Remember that this is an unmarshaler, + // but wait to return it until after allocating + // the pointer (if necessary). + _, isUnmarshaler = v.Interface().(Unmarshaler) + } + + if iv, ok := v.(*reflect.InterfaceValue); ok && !iv.IsNil() { + v = iv.Elem() + continue + } + pv, ok := v.(*reflect.PtrValue) + if !ok { + break + } + _, isptrptr := pv.Elem().(*reflect.PtrValue) + if !isptrptr && wantptr && !isUnmarshaler { + return nil, pv + } + if pv.IsNil() { + pv.PointTo(reflect.MakeZero(pv.Type().(*reflect.PtrType).Elem())) + } + if isUnmarshaler { + // Using v.Interface().(Unmarshaler) + // here means that we have to use a pointer + // as the struct field. We cannot use a value inside + // a pointer to a struct, because in that case + // v.Interface() is the value (x.f) not the pointer (&x.f). + // This is an unfortunate consequence of reflect. + // An alternative would be to look up the + // UnmarshalJSON method and return a FuncValue. + return v.Interface().(Unmarshaler), nil + } + v = pv.Elem() + } + return nil, v +} + +// array consumes an array from d.data[d.off-1:], decoding into the value v. +// the first byte of the array ('[') has been read already. +func (d *decodeState) array(v reflect.Value) { + // Check for unmarshaler. + unmarshaler, pv := d.indirect(v, false) + if unmarshaler != nil { + d.off-- + err := unmarshaler.UnmarshalJSON(d.next()) + if err != nil { + d.error(err) + } + return + } + v = pv + + // Decoding into nil interface? Switch to non-reflect code. + iv, ok := v.(*reflect.InterfaceValue) + if ok { + iv.Set(reflect.NewValue(d.arrayInterface())) + return + } + + // Check type of target. + av, ok := v.(reflect.ArrayOrSliceValue) + if !ok { + d.saveError(&UnmarshalTypeError{"array", v.Type()}) + d.off-- + d.next() + return + } + + sv, _ := v.(*reflect.SliceValue) + + i := 0 + for { + // Look ahead for ] - can only happen on first iteration. + op := d.scanWhile(scanSkipSpace) + if op == scanEndArray { + break + } + + // Back up so d.value can have the byte we just read. + d.off-- + d.scan.undo(op) + + // Get element of array, growing if necessary. + if i >= av.Cap() && sv != nil { + newcap := sv.Cap() + sv.Cap()/2 + if newcap < 4 { + newcap = 4 + } + newv := reflect.MakeSlice(sv.Type().(*reflect.SliceType), sv.Len(), newcap) + reflect.Copy(newv, sv) + sv.Set(newv) + } + if i >= av.Len() && sv != nil { + // Must be slice; gave up on array during i >= av.Cap(). + sv.SetLen(i + 1) + } + + // Decode into element. + if i < av.Len() { + d.value(av.Elem(i)) + } else { + // Ran out of fixed array: skip. + d.value(nil) + } + i++ + + // Next token must be , or ]. + op = d.scanWhile(scanSkipSpace) + if op == scanEndArray { + break + } + if op != scanArrayValue { + d.error(errPhase) + } + } + if i < av.Len() { + if sv == nil { + // Array. Zero the rest. + z := reflect.MakeZero(av.Type().(*reflect.ArrayType).Elem()) + for ; i < av.Len(); i++ { + av.Elem(i).SetValue(z) + } + } else { + sv.SetLen(i) + } + } +} + +// matchName returns true if key should be written to a field named name. +func matchName(key, name string) bool { + return strings.ToLower(key) == strings.ToLower(name) +} + +// object consumes an object from d.data[d.off-1:], decoding into the value v. +// the first byte of the object ('{') has been read already. +func (d *decodeState) object(v reflect.Value) { + // Check for unmarshaler. + unmarshaler, pv := d.indirect(v, false) + if unmarshaler != nil { + d.off-- + err := unmarshaler.UnmarshalJSON(d.next()) + if err != nil { + d.error(err) + } + return + } + v = pv + + // Decoding into nil interface? Switch to non-reflect code. + iv, ok := v.(*reflect.InterfaceValue) + if ok { + iv.Set(reflect.NewValue(d.objectInterface())) + return + } + + // Check type of target: struct or map[string]T + var ( + mv *reflect.MapValue + sv *reflect.StructValue + ) + switch v := v.(type) { + case *reflect.MapValue: + // map must have string type + t := v.Type().(*reflect.MapType) + if t.Key() != reflect.Typeof("") { + d.saveError(&UnmarshalTypeError{"object", v.Type()}) + break + } + mv = v + if mv.IsNil() { + mv.SetValue(reflect.MakeMap(t)) + } + case *reflect.StructValue: + sv = v + default: + d.saveError(&UnmarshalTypeError{"object", v.Type()}) + } + + if mv == nil && sv == nil { + d.off-- + d.next() // skip over { } in input + return + } + + for { + // Read opening " of string key or closing }. + op := d.scanWhile(scanSkipSpace) + if op == scanEndObject { + // closing } - can only happen on first iteration. + break + } + if op != scanBeginLiteral { + d.error(errPhase) + } + + // Read string key. + start := d.off - 1 + op = d.scanWhile(scanContinue) + item := d.data[start : d.off-1] + key, ok := unquote(item) + if !ok { + d.error(errPhase) + } + + // Figure out field corresponding to key. + var subv reflect.Value + if mv != nil { + subv = reflect.MakeZero(mv.Type().(*reflect.MapType).Elem()) + } else { + var f reflect.StructField + var ok bool + st := sv.Type().(*reflect.StructType) + // First try for field with that tag. + if isValidTag(key) { + for i := 0; i < sv.NumField(); i++ { + f = st.Field(i) + if f.Tag == key { + ok = true + break + } + } + } + if !ok { + // Second, exact match. + f, ok = st.FieldByName(key) + } + if !ok { + // Third, case-insensitive match. + f, ok = st.FieldByNameFunc(func(s string) bool { return matchName(key, s) }) + } + + // Extract value; name must be exported. + if ok { + if f.PkgPath != "" { + d.saveError(&UnmarshalFieldError{key, st, f}) + } else { + subv = sv.FieldByIndex(f.Index) + } + } + } + + // Read : before value. + if op == scanSkipSpace { + op = d.scanWhile(scanSkipSpace) + } + if op != scanObjectKey { + d.error(errPhase) + } + + // Read value. + d.value(subv) + + // Write value back to map; + // if using struct, subv points into struct already. + if mv != nil { + mv.SetElem(reflect.NewValue(key), subv) + } + + // Next token must be , or }. + op = d.scanWhile(scanSkipSpace) + if op == scanEndObject { + break + } + if op != scanObjectValue { + d.error(errPhase) + } + } +} + +// literal consumes a literal from d.data[d.off-1:], decoding into the value v. +// The first byte of the literal has been read already +// (that's how the caller knows it's a literal). +func (d *decodeState) literal(v reflect.Value) { + // All bytes inside literal return scanContinue op code. + start := d.off - 1 + op := d.scanWhile(scanContinue) + + // Scan read one byte too far; back up. + d.off-- + d.scan.undo(op) + item := d.data[start:d.off] + + // Check for unmarshaler. + wantptr := item[0] == 'n' // null + unmarshaler, pv := d.indirect(v, wantptr) + if unmarshaler != nil { + err := unmarshaler.UnmarshalJSON(item) + if err != nil { + d.error(err) + } + return + } + v = pv + + switch c := item[0]; c { + case 'n': // null + switch v.(type) { + default: + d.saveError(&UnmarshalTypeError{"null", v.Type()}) + case *reflect.InterfaceValue, *reflect.PtrValue, *reflect.MapValue: + v.SetValue(nil) + } + + case 't', 'f': // true, false + value := c == 't' + switch v := v.(type) { + default: + d.saveError(&UnmarshalTypeError{"bool", v.Type()}) + case *reflect.BoolValue: + v.Set(value) + case *reflect.InterfaceValue: + v.Set(reflect.NewValue(value)) + } + + case '"': // string + s, ok := unquoteBytes(item) + if !ok { + d.error(errPhase) + } + switch v := v.(type) { + default: + d.saveError(&UnmarshalTypeError{"string", v.Type()}) + case *reflect.SliceValue: + if v.Type() != byteSliceType { + d.saveError(&UnmarshalTypeError{"string", v.Type()}) + break + } + b := make([]byte, base64.StdEncoding.DecodedLen(len(s))) + n, err := base64.StdEncoding.Decode(b, s) + if err != nil { + d.saveError(err) + break + } + v.Set(reflect.NewValue(b[0:n]).(*reflect.SliceValue)) + case *reflect.StringValue: + v.Set(string(s)) + case *reflect.InterfaceValue: + v.Set(reflect.NewValue(string(s))) + } + + default: // number + if c != '-' && (c < '0' || c > '9') { + d.error(errPhase) + } + s := string(item) + switch v := v.(type) { + default: + d.error(&UnmarshalTypeError{"number", v.Type()}) + case *reflect.InterfaceValue: + n, err := strconv.Atof64(s) + if err != nil { + d.saveError(&UnmarshalTypeError{"number " + s, v.Type()}) + break + } + v.Set(reflect.NewValue(n)) + + case *reflect.IntValue: + n, err := strconv.Atoi64(s) + if err != nil || v.Overflow(n) { + d.saveError(&UnmarshalTypeError{"number " + s, v.Type()}) + break + } + v.Set(n) + + case *reflect.UintValue: + n, err := strconv.Atoui64(s) + if err != nil || v.Overflow(n) { + d.saveError(&UnmarshalTypeError{"number " + s, v.Type()}) + break + } + v.Set(n) + + case *reflect.FloatValue: + n, err := strconv.AtofN(s, v.Type().Bits()) + if err != nil || v.Overflow(n) { + d.saveError(&UnmarshalTypeError{"number " + s, v.Type()}) + break + } + v.Set(n) + } + } +} + +// The xxxInterface routines build up a value to be stored +// in an empty interface. They are not strictly necessary, +// but they avoid the weight of reflection in this common case. + +// valueInterface is like value but returns interface{} +func (d *decodeState) valueInterface() interface{} { + switch d.scanWhile(scanSkipSpace) { + default: + d.error(errPhase) + case scanBeginArray: + return d.arrayInterface() + case scanBeginObject: + return d.objectInterface() + case scanBeginLiteral: + return d.literalInterface() + } + panic("unreachable") +} + +// arrayInterface is like array but returns []interface{}. +func (d *decodeState) arrayInterface() []interface{} { + var v vector.Vector + for { + // Look ahead for ] - can only happen on first iteration. + op := d.scanWhile(scanSkipSpace) + if op == scanEndArray { + break + } + + // Back up so d.value can have the byte we just read. + d.off-- + d.scan.undo(op) + + v.Push(d.valueInterface()) + + // Next token must be , or ]. + op = d.scanWhile(scanSkipSpace) + if op == scanEndArray { + break + } + if op != scanArrayValue { + d.error(errPhase) + } + } + return v +} + +// objectInterface is like object but returns map[string]interface{}. +func (d *decodeState) objectInterface() map[string]interface{} { + m := make(map[string]interface{}) + for { + // Read opening " of string key or closing }. + op := d.scanWhile(scanSkipSpace) + if op == scanEndObject { + // closing } - can only happen on first iteration. + break + } + if op != scanBeginLiteral { + d.error(errPhase) + } + + // Read string key. + start := d.off - 1 + op = d.scanWhile(scanContinue) + item := d.data[start : d.off-1] + key, ok := unquote(item) + if !ok { + d.error(errPhase) + } + + // Read : before value. + if op == scanSkipSpace { + op = d.scanWhile(scanSkipSpace) + } + if op != scanObjectKey { + d.error(errPhase) + } + + // Read value. + m[key] = d.valueInterface() + + // Next token must be , or }. + op = d.scanWhile(scanSkipSpace) + if op == scanEndObject { + break + } + if op != scanObjectValue { + d.error(errPhase) + } + } + return m +} + + +// literalInterface is like literal but returns an interface value. +func (d *decodeState) literalInterface() interface{} { + // All bytes inside literal return scanContinue op code. + start := d.off - 1 + op := d.scanWhile(scanContinue) + + // Scan read one byte too far; back up. + d.off-- + d.scan.undo(op) + item := d.data[start:d.off] + + switch c := item[0]; c { + case 'n': // null + return nil + + case 't', 'f': // true, false + return c == 't' + + case '"': // string + s, ok := unquote(item) + if !ok { + d.error(errPhase) + } + return s + + default: // number + if c != '-' && (c < '0' || c > '9') { + d.error(errPhase) + } + n, err := strconv.Atof64(string(item)) + if err != nil { + d.saveError(&UnmarshalTypeError{"number " + string(item), reflect.Typeof(0.0)}) + } + return n + } + panic("unreachable") +} + +// getu4 decodes \uXXXX from the beginning of s, returning the hex value, +// or it returns -1. +func getu4(s []byte) int { + if len(s) < 6 || s[0] != '\\' || s[1] != 'u' { + return -1 + } + rune, err := strconv.Btoui64(string(s[2:6]), 16) + if err != nil { + return -1 + } + return int(rune) +} + +// unquote converts a quoted JSON string literal s into an actual string t. +// The rules are different than for Go, so cannot use strconv.Unquote. +func unquote(s []byte) (t string, ok bool) { + s, ok = unquoteBytes(s) + t = string(s) + return +} + +func unquoteBytes(s []byte) (t []byte, ok bool) { + if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' { + return + } + s = s[1 : len(s)-1] + + // Check for unusual characters. If there are none, + // then no unquoting is needed, so return a slice of the + // original bytes. + r := 0 + for r < len(s) { + c := s[r] + if c == '\\' || c == '"' || c < ' ' { + break + } + if c < utf8.RuneSelf { + r++ + continue + } + rune, size := utf8.DecodeRune(s[r:]) + if rune == utf8.RuneError && size == 1 { + break + } + r += size + } + if r == len(s) { + return s, true + } + + b := make([]byte, len(s)+2*utf8.UTFMax) + w := copy(b, s[0:r]) + for r < len(s) { + // Out of room? Can only happen if s is full of + // malformed UTF-8 and we're replacing each + // byte with RuneError. + if w >= len(b)-2*utf8.UTFMax { + nb := make([]byte, (len(b)+utf8.UTFMax)*2) + copy(nb, b[0:w]) + b = nb + } + switch c := s[r]; { + case c == '\\': + r++ + if r >= len(s) { + return + } + switch s[r] { + default: + return + case '"', '\\', '/', '\'': + b[w] = s[r] + r++ + w++ + case 'b': + b[w] = '\b' + r++ + w++ + case 'f': + b[w] = '\f' + r++ + w++ + case 'n': + b[w] = '\n' + r++ + w++ + case 'r': + b[w] = '\r' + r++ + w++ + case 't': + b[w] = '\t' + r++ + w++ + case 'u': + r-- + rune := getu4(s[r:]) + if rune < 0 { + return + } + r += 6 + if utf16.IsSurrogate(rune) { + rune1 := getu4(s[r:]) + if dec := utf16.DecodeRune(rune, rune1); dec != unicode.ReplacementChar { + // A valid pair; consume. + r += 6 + w += utf8.EncodeRune(b[w:], dec) + break + } + // Invalid surrogate; fall back to replacement rune. + rune = unicode.ReplacementChar + } + w += utf8.EncodeRune(b[w:], rune) + } + + // Quote, control characters are invalid. + case c == '"', c < ' ': + return + + // ASCII + case c < utf8.RuneSelf: + b[w] = c + r++ + w++ + + // Coerce to well-formed UTF-8. + default: + rune, size := utf8.DecodeRune(s[r:]) + r += size + w += utf8.EncodeRune(b[w:], rune) + } + } + return b[0:w], true +} diff --git a/src/cmd/gofix/testdata/reflect.decode.go.out b/src/cmd/gofix/testdata/reflect.decode.go.out new file mode 100644 index 000000000..a5fd33912 --- /dev/null +++ b/src/cmd/gofix/testdata/reflect.decode.go.out @@ -0,0 +1,910 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Represents JSON data structure using native Go types: booleans, floats, +// strings, arrays, and maps. + +package json + +import ( + "container/vector" + "encoding/base64" + "os" + "reflect" + "runtime" + "strconv" + "strings" + "unicode" + "utf16" + "utf8" +) + +// Unmarshal parses the JSON-encoded data and stores the result +// in the value pointed to by v. +// +// Unmarshal traverses the value v recursively. +// If an encountered value implements the Unmarshaler interface, +// Unmarshal calls its UnmarshalJSON method with a well-formed +// JSON encoding. +// +// Otherwise, Unmarshal uses the inverse of the encodings that +// Marshal uses, allocating maps, slices, and pointers as necessary, +// with the following additional rules: +// +// To unmarshal a JSON value into a nil interface value, the +// type stored in the interface value is one of: +// +// bool, for JSON booleans +// float64, for JSON numbers +// string, for JSON strings +// []interface{}, for JSON arrays +// map[string]interface{}, for JSON objects +// nil for JSON null +// +// If a JSON value is not appropriate for a given target type, +// or if a JSON number overflows the target type, Unmarshal +// skips that field and completes the unmarshalling as best it can. +// If no more serious errors are encountered, Unmarshal returns +// an UnmarshalTypeError describing the earliest such error. +// +func Unmarshal(data []byte, v interface{}) os.Error { + d := new(decodeState).init(data) + + // Quick check for well-formedness. + // Avoids filling out half a data structure + // before discovering a JSON syntax error. + err := checkValid(data, &d.scan) + if err != nil { + return err + } + + return d.unmarshal(v) +} + +// Unmarshaler is the interface implemented by objects +// that can unmarshal a JSON description of themselves. +// The input can be assumed to be a valid JSON object +// encoding. UnmarshalJSON must copy the JSON data +// if it wishes to retain the data after returning. +type Unmarshaler interface { + UnmarshalJSON([]byte) os.Error +} + + +// An UnmarshalTypeError describes a JSON value that was +// not appropriate for a value of a specific Go type. +type UnmarshalTypeError struct { + Value string // description of JSON value - "bool", "array", "number -5" + Type reflect.Type // type of Go value it could not be assigned to +} + +func (e *UnmarshalTypeError) String() string { + return "json: cannot unmarshal " + e.Value + " into Go value of type " + e.Type.String() +} + +// An UnmarshalFieldError describes a JSON object key that +// led to an unexported (and therefore unwritable) struct field. +type UnmarshalFieldError struct { + Key string + Type reflect.Type + Field reflect.StructField +} + +func (e *UnmarshalFieldError) String() string { + return "json: cannot unmarshal object key " + strconv.Quote(e.Key) + " into unexported field " + e.Field.Name + " of type " + e.Type.String() +} + +// An InvalidUnmarshalError describes an invalid argument passed to Unmarshal. +// (The argument to Unmarshal must be a non-nil pointer.) +type InvalidUnmarshalError struct { + Type reflect.Type +} + +func (e *InvalidUnmarshalError) String() string { + if e.Type == nil { + return "json: Unmarshal(nil)" + } + + if e.Type.Kind() != reflect.Ptr { + return "json: Unmarshal(non-pointer " + e.Type.String() + ")" + } + return "json: Unmarshal(nil " + e.Type.String() + ")" +} + +func (d *decodeState) unmarshal(v interface{}) (err os.Error) { + defer func() { + if r := recover(); r != nil { + if _, ok := r.(runtime.Error); ok { + panic(r) + } + err = r.(os.Error) + } + }() + + rv := reflect.NewValue(v) + pv := rv + if pv.Kind() != reflect.Ptr || + pv.IsNil() { + return &InvalidUnmarshalError{reflect.Typeof(v)} + } + + d.scan.reset() + // We decode rv not pv.Elem because the Unmarshaler interface + // test must be applied at the top level of the value. + d.value(rv) + return d.savedError +} + +// decodeState represents the state while decoding a JSON value. +type decodeState struct { + data []byte + off int // read offset in data + scan scanner + nextscan scanner // for calls to nextValue + savedError os.Error +} + +// errPhase is used for errors that should not happen unless +// there is a bug in the JSON decoder or something is editing +// the data slice while the decoder executes. +var errPhase = os.NewError("JSON decoder out of sync - data changing underfoot?") + +func (d *decodeState) init(data []byte) *decodeState { + d.data = data + d.off = 0 + d.savedError = nil + return d +} + +// error aborts the decoding by panicking with err. +func (d *decodeState) error(err os.Error) { + panic(err) +} + +// saveError saves the first err it is called with, +// for reporting at the end of the unmarshal. +func (d *decodeState) saveError(err os.Error) { + if d.savedError == nil { + d.savedError = err + } +} + +// next cuts off and returns the next full JSON value in d.data[d.off:]. +// The next value is known to be an object or array, not a literal. +func (d *decodeState) next() []byte { + c := d.data[d.off] + item, rest, err := nextValue(d.data[d.off:], &d.nextscan) + if err != nil { + d.error(err) + } + d.off = len(d.data) - len(rest) + + // Our scanner has seen the opening brace/bracket + // and thinks we're still in the middle of the object. + // invent a closing brace/bracket to get it out. + if c == '{' { + d.scan.step(&d.scan, '}') + } else { + d.scan.step(&d.scan, ']') + } + + return item +} + +// scanWhile processes bytes in d.data[d.off:] until it +// receives a scan code not equal to op. +// It updates d.off and returns the new scan code. +func (d *decodeState) scanWhile(op int) int { + var newOp int + for { + if d.off >= len(d.data) { + newOp = d.scan.eof() + d.off = len(d.data) + 1 // mark processed EOF with len+1 + } else { + c := int(d.data[d.off]) + d.off++ + newOp = d.scan.step(&d.scan, c) + } + if newOp != op { + break + } + } + return newOp +} + +// value decodes a JSON value from d.data[d.off:] into the value. +// it updates d.off to point past the decoded value. +func (d *decodeState) value(v reflect.Value) { + if !v.IsValid() { + _, rest, err := nextValue(d.data[d.off:], &d.nextscan) + if err != nil { + d.error(err) + } + d.off = len(d.data) - len(rest) + + // d.scan thinks we're still at the beginning of the item. + // Feed in an empty string - the shortest, simplest value - + // so that it knows we got to the end of the value. + if d.scan.step == stateRedo { + panic("redo") + } + d.scan.step(&d.scan, '"') + d.scan.step(&d.scan, '"') + return + } + + switch op := d.scanWhile(scanSkipSpace); op { + default: + d.error(errPhase) + + case scanBeginArray: + d.array(v) + + case scanBeginObject: + d.object(v) + + case scanBeginLiteral: + d.literal(v) + } +} + +// indirect walks down v allocating pointers as needed, +// until it gets to a non-pointer. +// if it encounters an Unmarshaler, indirect stops and returns that. +// if wantptr is true, indirect stops at the last pointer. +func (d *decodeState) indirect(v reflect.Value, wantptr bool) (Unmarshaler, reflect.Value) { + for { + var isUnmarshaler bool + if v.Type().NumMethod() > 0 { + // Remember that this is an unmarshaler, + // but wait to return it until after allocating + // the pointer (if necessary). + _, isUnmarshaler = v.Interface().(Unmarshaler) + } + + if iv := v; iv.Kind() == reflect.Interface && !iv.IsNil() { + v = iv.Elem() + continue + } + pv := v + if pv.Kind() != reflect.Ptr { + break + } + + if pv.Elem().Kind() != reflect.Ptr && + wantptr && !isUnmarshaler { + return nil, pv + } + if pv.IsNil() { + pv.Set(reflect.Zero(pv.Type().Elem()).Addr()) + } + if isUnmarshaler { + // Using v.Interface().(Unmarshaler) + // here means that we have to use a pointer + // as the struct field. We cannot use a value inside + // a pointer to a struct, because in that case + // v.Interface() is the value (x.f) not the pointer (&x.f). + // This is an unfortunate consequence of reflect. + // An alternative would be to look up the + // UnmarshalJSON method and return a FuncValue. + return v.Interface().(Unmarshaler), reflect.Value{} + } + v = pv.Elem() + } + return nil, v +} + +// array consumes an array from d.data[d.off-1:], decoding into the value v. +// the first byte of the array ('[') has been read already. +func (d *decodeState) array(v reflect.Value) { + // Check for unmarshaler. + unmarshaler, pv := d.indirect(v, false) + if unmarshaler != nil { + d.off-- + err := unmarshaler.UnmarshalJSON(d.next()) + if err != nil { + d.error(err) + } + return + } + v = pv + + // Decoding into nil interface? Switch to non-reflect code. + iv := v + ok := iv.Kind() == reflect.Interface + if ok { + iv.Set(reflect.NewValue(d.arrayInterface())) + return + } + + // Check type of target. + av := v + if av.Kind() != reflect.Array && av.Kind() != reflect.Slice { + d.saveError(&UnmarshalTypeError{"array", v.Type()}) + d.off-- + d.next() + return + } + + sv := v + + i := 0 + for { + // Look ahead for ] - can only happen on first iteration. + op := d.scanWhile(scanSkipSpace) + if op == scanEndArray { + break + } + + // Back up so d.value can have the byte we just read. + d.off-- + d.scan.undo(op) + + // Get element of array, growing if necessary. + if i >= av.Cap() && sv.IsValid() { + newcap := sv.Cap() + sv.Cap()/2 + if newcap < 4 { + newcap = 4 + } + newv := reflect.MakeSlice(sv.Type(), sv.Len(), newcap) + reflect.Copy(newv, sv) + sv.Set(newv) + } + if i >= av.Len() && sv.IsValid() { + // Must be slice; gave up on array during i >= av.Cap(). + sv.SetLen(i + 1) + } + + // Decode into element. + if i < av.Len() { + d.value(av.Index(i)) + } else { + // Ran out of fixed array: skip. + d.value(reflect.Value{}) + } + i++ + + // Next token must be , or ]. + op = d.scanWhile(scanSkipSpace) + if op == scanEndArray { + break + } + if op != scanArrayValue { + d.error(errPhase) + } + } + if i < av.Len() { + if !sv.IsValid() { + // Array. Zero the rest. + z := reflect.Zero(av.Type().Elem()) + for ; i < av.Len(); i++ { + av.Index(i).Set(z) + } + } else { + sv.SetLen(i) + } + } +} + +// matchName returns true if key should be written to a field named name. +func matchName(key, name string) bool { + return strings.ToLower(key) == strings.ToLower(name) +} + +// object consumes an object from d.data[d.off-1:], decoding into the value v. +// the first byte of the object ('{') has been read already. +func (d *decodeState) object(v reflect.Value) { + // Check for unmarshaler. + unmarshaler, pv := d.indirect(v, false) + if unmarshaler != nil { + d.off-- + err := unmarshaler.UnmarshalJSON(d.next()) + if err != nil { + d.error(err) + } + return + } + v = pv + + // Decoding into nil interface? Switch to non-reflect code. + iv := v + if iv.Kind() == reflect.Interface { + iv.Set(reflect.NewValue(d.objectInterface())) + return + } + + // Check type of target: struct or map[string]T + var ( + mv reflect.Value + sv reflect.Value + ) + switch v.Kind() { + case reflect.Map: + // map must have string type + t := v.Type() + if t.Key() != reflect.Typeof("") { + d.saveError(&UnmarshalTypeError{"object", v.Type()}) + break + } + mv = v + if mv.IsNil() { + mv.Set(reflect.MakeMap(t)) + } + case reflect.Struct: + sv = v + default: + d.saveError(&UnmarshalTypeError{"object", v.Type()}) + } + + if !mv.IsValid() && !sv.IsValid() { + d.off-- + d.next() // skip over { } in input + return + } + + for { + // Read opening " of string key or closing }. + op := d.scanWhile(scanSkipSpace) + if op == scanEndObject { + // closing } - can only happen on first iteration. + break + } + if op != scanBeginLiteral { + d.error(errPhase) + } + + // Read string key. + start := d.off - 1 + op = d.scanWhile(scanContinue) + item := d.data[start : d.off-1] + key, ok := unquote(item) + if !ok { + d.error(errPhase) + } + + // Figure out field corresponding to key. + var subv reflect.Value + if mv.IsValid() { + subv = reflect.Zero(mv.Type().Elem()) + } else { + var f reflect.StructField + var ok bool + st := sv.Type() + // First try for field with that tag. + if isValidTag(key) { + for i := 0; i < sv.NumField(); i++ { + f = st.Field(i) + if f.Tag == key { + ok = true + break + } + } + } + if !ok { + // Second, exact match. + f, ok = st.FieldByName(key) + } + if !ok { + // Third, case-insensitive match. + f, ok = st.FieldByNameFunc(func(s string) bool { return matchName(key, s) }) + } + + // Extract value; name must be exported. + if ok { + if f.PkgPath != "" { + d.saveError(&UnmarshalFieldError{key, st, f}) + } else { + subv = sv.FieldByIndex(f.Index) + } + } + } + + // Read : before value. + if op == scanSkipSpace { + op = d.scanWhile(scanSkipSpace) + } + if op != scanObjectKey { + d.error(errPhase) + } + + // Read value. + d.value(subv) + + // Write value back to map; + // if using struct, subv points into struct already. + if mv.IsValid() { + mv.SetMapIndex(reflect.NewValue(key), subv) + } + + // Next token must be , or }. + op = d.scanWhile(scanSkipSpace) + if op == scanEndObject { + break + } + if op != scanObjectValue { + d.error(errPhase) + } + } +} + +// literal consumes a literal from d.data[d.off-1:], decoding into the value v. +// The first byte of the literal has been read already +// (that's how the caller knows it's a literal). +func (d *decodeState) literal(v reflect.Value) { + // All bytes inside literal return scanContinue op code. + start := d.off - 1 + op := d.scanWhile(scanContinue) + + // Scan read one byte too far; back up. + d.off-- + d.scan.undo(op) + item := d.data[start:d.off] + + // Check for unmarshaler. + wantptr := item[0] == 'n' // null + unmarshaler, pv := d.indirect(v, wantptr) + if unmarshaler != nil { + err := unmarshaler.UnmarshalJSON(item) + if err != nil { + d.error(err) + } + return + } + v = pv + + switch c := item[0]; c { + case 'n': // null + switch v.Kind() { + default: + d.saveError(&UnmarshalTypeError{"null", v.Type()}) + case reflect.Interface, reflect.Ptr, reflect.Map: + v.Set(reflect.Zero(v.Type())) + } + + case 't', 'f': // true, false + value := c == 't' + switch v.Kind() { + default: + d.saveError(&UnmarshalTypeError{"bool", v.Type()}) + case reflect.Bool: + v.SetBool(value) + case reflect.Interface: + v.Set(reflect.NewValue(value)) + } + + case '"': // string + s, ok := unquoteBytes(item) + if !ok { + d.error(errPhase) + } + switch v.Kind() { + default: + d.saveError(&UnmarshalTypeError{"string", v.Type()}) + case reflect.Slice: + if v.Type() != byteSliceType { + d.saveError(&UnmarshalTypeError{"string", v.Type()}) + break + } + b := make([]byte, base64.StdEncoding.DecodedLen(len(s))) + n, err := base64.StdEncoding.Decode(b, s) + if err != nil { + d.saveError(err) + break + } + v.Set(reflect.NewValue(b[0:n])) + case reflect.String: + v.SetString(string(s)) + case reflect.Interface: + v.Set(reflect.NewValue(string(s))) + } + + default: // number + if c != '-' && (c < '0' || c > '9') { + d.error(errPhase) + } + s := string(item) + switch v.Kind() { + default: + d.error(&UnmarshalTypeError{"number", v.Type()}) + case reflect.Interface: + n, err := strconv.Atof64(s) + if err != nil { + d.saveError(&UnmarshalTypeError{"number " + s, v.Type()}) + break + } + v.Set(reflect.NewValue(n)) + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + n, err := strconv.Atoi64(s) + if err != nil || v.OverflowInt(n) { + d.saveError(&UnmarshalTypeError{"number " + s, v.Type()}) + break + } + v.SetInt(n) + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + n, err := strconv.Atoui64(s) + if err != nil || v.OverflowUint(n) { + d.saveError(&UnmarshalTypeError{"number " + s, v.Type()}) + break + } + v.SetUint(n) + + case reflect.Float32, reflect.Float64: + n, err := strconv.AtofN(s, v.Type().Bits()) + if err != nil || v.OverflowFloat(n) { + d.saveError(&UnmarshalTypeError{"number " + s, v.Type()}) + break + } + v.SetFloat(n) + } + } +} + +// The xxxInterface routines build up a value to be stored +// in an empty interface. They are not strictly necessary, +// but they avoid the weight of reflection in this common case. + +// valueInterface is like value but returns interface{} +func (d *decodeState) valueInterface() interface{} { + switch d.scanWhile(scanSkipSpace) { + default: + d.error(errPhase) + case scanBeginArray: + return d.arrayInterface() + case scanBeginObject: + return d.objectInterface() + case scanBeginLiteral: + return d.literalInterface() + } + panic("unreachable") +} + +// arrayInterface is like array but returns []interface{}. +func (d *decodeState) arrayInterface() []interface{} { + var v vector.Vector + for { + // Look ahead for ] - can only happen on first iteration. + op := d.scanWhile(scanSkipSpace) + if op == scanEndArray { + break + } + + // Back up so d.value can have the byte we just read. + d.off-- + d.scan.undo(op) + + v.Push(d.valueInterface()) + + // Next token must be , or ]. + op = d.scanWhile(scanSkipSpace) + if op == scanEndArray { + break + } + if op != scanArrayValue { + d.error(errPhase) + } + } + return v +} + +// objectInterface is like object but returns map[string]interface{}. +func (d *decodeState) objectInterface() map[string]interface{} { + m := make(map[string]interface{}) + for { + // Read opening " of string key or closing }. + op := d.scanWhile(scanSkipSpace) + if op == scanEndObject { + // closing } - can only happen on first iteration. + break + } + if op != scanBeginLiteral { + d.error(errPhase) + } + + // Read string key. + start := d.off - 1 + op = d.scanWhile(scanContinue) + item := d.data[start : d.off-1] + key, ok := unquote(item) + if !ok { + d.error(errPhase) + } + + // Read : before value. + if op == scanSkipSpace { + op = d.scanWhile(scanSkipSpace) + } + if op != scanObjectKey { + d.error(errPhase) + } + + // Read value. + m[key] = d.valueInterface() + + // Next token must be , or }. + op = d.scanWhile(scanSkipSpace) + if op == scanEndObject { + break + } + if op != scanObjectValue { + d.error(errPhase) + } + } + return m +} + + +// literalInterface is like literal but returns an interface value. +func (d *decodeState) literalInterface() interface{} { + // All bytes inside literal return scanContinue op code. + start := d.off - 1 + op := d.scanWhile(scanContinue) + + // Scan read one byte too far; back up. + d.off-- + d.scan.undo(op) + item := d.data[start:d.off] + + switch c := item[0]; c { + case 'n': // null + return nil + + case 't', 'f': // true, false + return c == 't' + + case '"': // string + s, ok := unquote(item) + if !ok { + d.error(errPhase) + } + return s + + default: // number + if c != '-' && (c < '0' || c > '9') { + d.error(errPhase) + } + n, err := strconv.Atof64(string(item)) + if err != nil { + d.saveError(&UnmarshalTypeError{"number " + string(item), reflect.Typeof(0.0)}) + } + return n + } + panic("unreachable") +} + +// getu4 decodes \uXXXX from the beginning of s, returning the hex value, +// or it returns -1. +func getu4(s []byte) int { + if len(s) < 6 || s[0] != '\\' || s[1] != 'u' { + return -1 + } + rune, err := strconv.Btoui64(string(s[2:6]), 16) + if err != nil { + return -1 + } + return int(rune) +} + +// unquote converts a quoted JSON string literal s into an actual string t. +// The rules are different than for Go, so cannot use strconv.Unquote. +func unquote(s []byte) (t string, ok bool) { + s, ok = unquoteBytes(s) + t = string(s) + return +} + +func unquoteBytes(s []byte) (t []byte, ok bool) { + if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' { + return + } + s = s[1 : len(s)-1] + + // Check for unusual characters. If there are none, + // then no unquoting is needed, so return a slice of the + // original bytes. + r := 0 + for r < len(s) { + c := s[r] + if c == '\\' || c == '"' || c < ' ' { + break + } + if c < utf8.RuneSelf { + r++ + continue + } + rune, size := utf8.DecodeRune(s[r:]) + if rune == utf8.RuneError && size == 1 { + break + } + r += size + } + if r == len(s) { + return s, true + } + + b := make([]byte, len(s)+2*utf8.UTFMax) + w := copy(b, s[0:r]) + for r < len(s) { + // Out of room? Can only happen if s is full of + // malformed UTF-8 and we're replacing each + // byte with RuneError. + if w >= len(b)-2*utf8.UTFMax { + nb := make([]byte, (len(b)+utf8.UTFMax)*2) + copy(nb, b[0:w]) + b = nb + } + switch c := s[r]; { + case c == '\\': + r++ + if r >= len(s) { + return + } + switch s[r] { + default: + return + case '"', '\\', '/', '\'': + b[w] = s[r] + r++ + w++ + case 'b': + b[w] = '\b' + r++ + w++ + case 'f': + b[w] = '\f' + r++ + w++ + case 'n': + b[w] = '\n' + r++ + w++ + case 'r': + b[w] = '\r' + r++ + w++ + case 't': + b[w] = '\t' + r++ + w++ + case 'u': + r-- + rune := getu4(s[r:]) + if rune < 0 { + return + } + r += 6 + if utf16.IsSurrogate(rune) { + rune1 := getu4(s[r:]) + if dec := utf16.DecodeRune(rune, rune1); dec != unicode.ReplacementChar { + // A valid pair; consume. + r += 6 + w += utf8.EncodeRune(b[w:], dec) + break + } + // Invalid surrogate; fall back to replacement rune. + rune = unicode.ReplacementChar + } + w += utf8.EncodeRune(b[w:], rune) + } + + // Quote, control characters are invalid. + case c == '"', c < ' ': + return + + // ASCII + case c < utf8.RuneSelf: + b[w] = c + r++ + w++ + + // Coerce to well-formed UTF-8. + default: + rune, size := utf8.DecodeRune(s[r:]) + r += size + w += utf8.EncodeRune(b[w:], rune) + } + } + return b[0:w], true +} diff --git a/src/cmd/gofix/testdata/reflect.decoder.go.in b/src/cmd/gofix/testdata/reflect.decoder.go.in new file mode 100644 index 000000000..34364161a --- /dev/null +++ b/src/cmd/gofix/testdata/reflect.decoder.go.in @@ -0,0 +1,196 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gob + +import ( + "bufio" + "bytes" + "io" + "os" + "reflect" + "sync" +) + +// A Decoder manages the receipt of type and data information read from the +// remote side of a connection. +type Decoder struct { + mutex sync.Mutex // each item must be received atomically + r io.Reader // source of the data + buf bytes.Buffer // buffer for more efficient i/o from r + wireType map[typeId]*wireType // map from remote ID to local description + decoderCache map[reflect.Type]map[typeId]**decEngine // cache of compiled engines + ignorerCache map[typeId]**decEngine // ditto for ignored objects + freeList *decoderState // list of free decoderStates; avoids reallocation + countBuf []byte // used for decoding integers while parsing messages + tmp []byte // temporary storage for i/o; saves reallocating + err os.Error +} + +// NewDecoder returns a new decoder that reads from the io.Reader. +func NewDecoder(r io.Reader) *Decoder { + dec := new(Decoder) + dec.r = bufio.NewReader(r) + dec.wireType = make(map[typeId]*wireType) + dec.decoderCache = make(map[reflect.Type]map[typeId]**decEngine) + dec.ignorerCache = make(map[typeId]**decEngine) + dec.countBuf = make([]byte, 9) // counts may be uint64s (unlikely!), require 9 bytes + + return dec +} + +// recvType loads the definition of a type. +func (dec *Decoder) recvType(id typeId) { + // Have we already seen this type? That's an error + if id < firstUserId || dec.wireType[id] != nil { + dec.err = os.ErrorString("gob: duplicate type received") + return + } + + // Type: + wire := new(wireType) + dec.decodeValue(tWireType, reflect.NewValue(wire)) + if dec.err != nil { + return + } + // Remember we've seen this type. + dec.wireType[id] = wire +} + +// recvMessage reads the next count-delimited item from the input. It is the converse +// of Encoder.writeMessage. It returns false on EOF or other error reading the message. +func (dec *Decoder) recvMessage() bool { + // Read a count. + nbytes, _, err := decodeUintReader(dec.r, dec.countBuf) + if err != nil { + dec.err = err + return false + } + dec.readMessage(int(nbytes)) + return dec.err == nil +} + +// readMessage reads the next nbytes bytes from the input. +func (dec *Decoder) readMessage(nbytes int) { + // Allocate the buffer. + if cap(dec.tmp) < nbytes { + dec.tmp = make([]byte, nbytes+100) // room to grow + } + dec.tmp = dec.tmp[:nbytes] + + // Read the data + _, dec.err = io.ReadFull(dec.r, dec.tmp) + if dec.err != nil { + if dec.err == os.EOF { + dec.err = io.ErrUnexpectedEOF + } + return + } + dec.buf.Write(dec.tmp) +} + +// toInt turns an encoded uint64 into an int, according to the marshaling rules. +func toInt(x uint64) int64 { + i := int64(x >> 1) + if x&1 != 0 { + i = ^i + } + return i +} + +func (dec *Decoder) nextInt() int64 { + n, _, err := decodeUintReader(&dec.buf, dec.countBuf) + if err != nil { + dec.err = err + } + return toInt(n) +} + +func (dec *Decoder) nextUint() uint64 { + n, _, err := decodeUintReader(&dec.buf, dec.countBuf) + if err != nil { + dec.err = err + } + return n +} + +// decodeTypeSequence parses: +// TypeSequence +// (TypeDefinition DelimitedTypeDefinition*)? +// and returns the type id of the next value. It returns -1 at +// EOF. Upon return, the remainder of dec.buf is the value to be +// decoded. If this is an interface value, it can be ignored by +// simply resetting that buffer. +func (dec *Decoder) decodeTypeSequence(isInterface bool) typeId { + for dec.err == nil { + if dec.buf.Len() == 0 { + if !dec.recvMessage() { + break + } + } + // Receive a type id. + id := typeId(dec.nextInt()) + if id >= 0 { + // Value follows. + return id + } + // Type definition for (-id) follows. + dec.recvType(-id) + // When decoding an interface, after a type there may be a + // DelimitedValue still in the buffer. Skip its count. + // (Alternatively, the buffer is empty and the byte count + // will be absorbed by recvMessage.) + if dec.buf.Len() > 0 { + if !isInterface { + dec.err = os.ErrorString("extra data in buffer") + break + } + dec.nextUint() + } + } + return -1 +} + +// Decode reads the next value from the connection and stores +// it in the data represented by the empty interface value. +// If e is nil, the value will be discarded. Otherwise, +// the value underlying e must either be the correct type for the next +// data item received, and must be a pointer. +func (dec *Decoder) Decode(e interface{}) os.Error { + if e == nil { + return dec.DecodeValue(nil) + } + value := reflect.NewValue(e) + // If e represents a value as opposed to a pointer, the answer won't + // get back to the caller. Make sure it's a pointer. + if value.Type().Kind() != reflect.Ptr { + dec.err = os.ErrorString("gob: attempt to decode into a non-pointer") + return dec.err + } + return dec.DecodeValue(value) +} + +// DecodeValue reads the next value from the connection and stores +// it in the data represented by the reflection value. +// The value must be the correct type for the next +// data item received, or it may be nil, which means the +// value will be discarded. +func (dec *Decoder) DecodeValue(value reflect.Value) os.Error { + // Make sure we're single-threaded through here. + dec.mutex.Lock() + defer dec.mutex.Unlock() + + dec.buf.Reset() // In case data lingers from previous invocation. + dec.err = nil + id := dec.decodeTypeSequence(false) + if dec.err == nil { + dec.decodeValue(id, value) + } + return dec.err +} + +// If debug.go is compiled into the program , debugFunc prints a human-readable +// representation of the gob data read from r by calling that file's Debug function. +// Otherwise it is nil. +var debugFunc func(io.Reader) diff --git a/src/cmd/gofix/testdata/reflect.decoder.go.out b/src/cmd/gofix/testdata/reflect.decoder.go.out new file mode 100644 index 000000000..a631c27a2 --- /dev/null +++ b/src/cmd/gofix/testdata/reflect.decoder.go.out @@ -0,0 +1,196 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gob + +import ( + "bufio" + "bytes" + "io" + "os" + "reflect" + "sync" +) + +// A Decoder manages the receipt of type and data information read from the +// remote side of a connection. +type Decoder struct { + mutex sync.Mutex // each item must be received atomically + r io.Reader // source of the data + buf bytes.Buffer // buffer for more efficient i/o from r + wireType map[typeId]*wireType // map from remote ID to local description + decoderCache map[reflect.Type]map[typeId]**decEngine // cache of compiled engines + ignorerCache map[typeId]**decEngine // ditto for ignored objects + freeList *decoderState // list of free decoderStates; avoids reallocation + countBuf []byte // used for decoding integers while parsing messages + tmp []byte // temporary storage for i/o; saves reallocating + err os.Error +} + +// NewDecoder returns a new decoder that reads from the io.Reader. +func NewDecoder(r io.Reader) *Decoder { + dec := new(Decoder) + dec.r = bufio.NewReader(r) + dec.wireType = make(map[typeId]*wireType) + dec.decoderCache = make(map[reflect.Type]map[typeId]**decEngine) + dec.ignorerCache = make(map[typeId]**decEngine) + dec.countBuf = make([]byte, 9) // counts may be uint64s (unlikely!), require 9 bytes + + return dec +} + +// recvType loads the definition of a type. +func (dec *Decoder) recvType(id typeId) { + // Have we already seen this type? That's an error + if id < firstUserId || dec.wireType[id] != nil { + dec.err = os.ErrorString("gob: duplicate type received") + return + } + + // Type: + wire := new(wireType) + dec.decodeValue(tWireType, reflect.NewValue(wire)) + if dec.err != nil { + return + } + // Remember we've seen this type. + dec.wireType[id] = wire +} + +// recvMessage reads the next count-delimited item from the input. It is the converse +// of Encoder.writeMessage. It returns false on EOF or other error reading the message. +func (dec *Decoder) recvMessage() bool { + // Read a count. + nbytes, _, err := decodeUintReader(dec.r, dec.countBuf) + if err != nil { + dec.err = err + return false + } + dec.readMessage(int(nbytes)) + return dec.err == nil +} + +// readMessage reads the next nbytes bytes from the input. +func (dec *Decoder) readMessage(nbytes int) { + // Allocate the buffer. + if cap(dec.tmp) < nbytes { + dec.tmp = make([]byte, nbytes+100) // room to grow + } + dec.tmp = dec.tmp[:nbytes] + + // Read the data + _, dec.err = io.ReadFull(dec.r, dec.tmp) + if dec.err != nil { + if dec.err == os.EOF { + dec.err = io.ErrUnexpectedEOF + } + return + } + dec.buf.Write(dec.tmp) +} + +// toInt turns an encoded uint64 into an int, according to the marshaling rules. +func toInt(x uint64) int64 { + i := int64(x >> 1) + if x&1 != 0 { + i = ^i + } + return i +} + +func (dec *Decoder) nextInt() int64 { + n, _, err := decodeUintReader(&dec.buf, dec.countBuf) + if err != nil { + dec.err = err + } + return toInt(n) +} + +func (dec *Decoder) nextUint() uint64 { + n, _, err := decodeUintReader(&dec.buf, dec.countBuf) + if err != nil { + dec.err = err + } + return n +} + +// decodeTypeSequence parses: +// TypeSequence +// (TypeDefinition DelimitedTypeDefinition*)? +// and returns the type id of the next value. It returns -1 at +// EOF. Upon return, the remainder of dec.buf is the value to be +// decoded. If this is an interface value, it can be ignored by +// simply resetting that buffer. +func (dec *Decoder) decodeTypeSequence(isInterface bool) typeId { + for dec.err == nil { + if dec.buf.Len() == 0 { + if !dec.recvMessage() { + break + } + } + // Receive a type id. + id := typeId(dec.nextInt()) + if id >= 0 { + // Value follows. + return id + } + // Type definition for (-id) follows. + dec.recvType(-id) + // When decoding an interface, after a type there may be a + // DelimitedValue still in the buffer. Skip its count. + // (Alternatively, the buffer is empty and the byte count + // will be absorbed by recvMessage.) + if dec.buf.Len() > 0 { + if !isInterface { + dec.err = os.ErrorString("extra data in buffer") + break + } + dec.nextUint() + } + } + return -1 +} + +// Decode reads the next value from the connection and stores +// it in the data represented by the empty interface value. +// If e is nil, the value will be discarded. Otherwise, +// the value underlying e must either be the correct type for the next +// data item received, and must be a pointer. +func (dec *Decoder) Decode(e interface{}) os.Error { + if e == nil { + return dec.DecodeValue(reflect.Value{}) + } + value := reflect.NewValue(e) + // If e represents a value as opposed to a pointer, the answer won't + // get back to the caller. Make sure it's a pointer. + if value.Type().Kind() != reflect.Ptr { + dec.err = os.ErrorString("gob: attempt to decode into a non-pointer") + return dec.err + } + return dec.DecodeValue(value) +} + +// DecodeValue reads the next value from the connection and stores +// it in the data represented by the reflection value. +// The value must be the correct type for the next +// data item received, or it may be nil, which means the +// value will be discarded. +func (dec *Decoder) DecodeValue(value reflect.Value) os.Error { + // Make sure we're single-threaded through here. + dec.mutex.Lock() + defer dec.mutex.Unlock() + + dec.buf.Reset() // In case data lingers from previous invocation. + dec.err = nil + id := dec.decodeTypeSequence(false) + if dec.err == nil { + dec.decodeValue(id, value) + } + return dec.err +} + +// If debug.go is compiled into the program , debugFunc prints a human-readable +// representation of the gob data read from r by calling that file's Debug function. +// Otherwise it is nil. +var debugFunc func(io.Reader) diff --git a/src/cmd/gofix/testdata/reflect.dnsmsg.go.in b/src/cmd/gofix/testdata/reflect.dnsmsg.go.in new file mode 100644 index 000000000..5209c1a06 --- /dev/null +++ b/src/cmd/gofix/testdata/reflect.dnsmsg.go.in @@ -0,0 +1,779 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// DNS packet assembly. See RFC 1035. +// +// This is intended to support name resolution during net.Dial. +// It doesn't have to be blazing fast. +// +// Rather than write the usual handful of routines to pack and +// unpack every message that can appear on the wire, we use +// reflection to write a generic pack/unpack for structs and then +// use it. Thus, if in the future we need to define new message +// structs, no new pack/unpack/printing code needs to be written. +// +// The first half of this file defines the DNS message formats. +// The second half implements the conversion to and from wire format. +// A few of the structure elements have string tags to aid the +// generic pack/unpack routines. +// +// TODO(rsc): There are enough names defined in this file that they're all +// prefixed with dns. Perhaps put this in its own package later. + +package net + +import ( + "fmt" + "os" + "reflect" +) + +// Packet formats + +// Wire constants. +const ( + // valid dnsRR_Header.Rrtype and dnsQuestion.qtype + dnsTypeA = 1 + dnsTypeNS = 2 + dnsTypeMD = 3 + dnsTypeMF = 4 + dnsTypeCNAME = 5 + dnsTypeSOA = 6 + dnsTypeMB = 7 + dnsTypeMG = 8 + dnsTypeMR = 9 + dnsTypeNULL = 10 + dnsTypeWKS = 11 + dnsTypePTR = 12 + dnsTypeHINFO = 13 + dnsTypeMINFO = 14 + dnsTypeMX = 15 + dnsTypeTXT = 16 + dnsTypeAAAA = 28 + dnsTypeSRV = 33 + + // valid dnsQuestion.qtype only + dnsTypeAXFR = 252 + dnsTypeMAILB = 253 + dnsTypeMAILA = 254 + dnsTypeALL = 255 + + // valid dnsQuestion.qclass + dnsClassINET = 1 + dnsClassCSNET = 2 + dnsClassCHAOS = 3 + dnsClassHESIOD = 4 + dnsClassANY = 255 + + // dnsMsg.rcode + dnsRcodeSuccess = 0 + dnsRcodeFormatError = 1 + dnsRcodeServerFailure = 2 + dnsRcodeNameError = 3 + dnsRcodeNotImplemented = 4 + dnsRcodeRefused = 5 +) + +// The wire format for the DNS packet header. +type dnsHeader struct { + Id uint16 + Bits uint16 + Qdcount, Ancount, Nscount, Arcount uint16 +} + +const ( + // dnsHeader.Bits + _QR = 1 << 15 // query/response (response=1) + _AA = 1 << 10 // authoritative + _TC = 1 << 9 // truncated + _RD = 1 << 8 // recursion desired + _RA = 1 << 7 // recursion available +) + +// DNS queries. +type dnsQuestion struct { + Name string "domain-name" // "domain-name" specifies encoding; see packers below + Qtype uint16 + Qclass uint16 +} + +// DNS responses (resource records). +// There are many types of messages, +// but they all share the same header. +type dnsRR_Header struct { + Name string "domain-name" + Rrtype uint16 + Class uint16 + Ttl uint32 + Rdlength uint16 // length of data after header +} + +func (h *dnsRR_Header) Header() *dnsRR_Header { + return h +} + +type dnsRR interface { + Header() *dnsRR_Header +} + + +// Specific DNS RR formats for each query type. + +type dnsRR_CNAME struct { + Hdr dnsRR_Header + Cname string "domain-name" +} + +func (rr *dnsRR_CNAME) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_HINFO struct { + Hdr dnsRR_Header + Cpu string + Os string +} + +func (rr *dnsRR_HINFO) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_MB struct { + Hdr dnsRR_Header + Mb string "domain-name" +} + +func (rr *dnsRR_MB) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_MG struct { + Hdr dnsRR_Header + Mg string "domain-name" +} + +func (rr *dnsRR_MG) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_MINFO struct { + Hdr dnsRR_Header + Rmail string "domain-name" + Email string "domain-name" +} + +func (rr *dnsRR_MINFO) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_MR struct { + Hdr dnsRR_Header + Mr string "domain-name" +} + +func (rr *dnsRR_MR) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_MX struct { + Hdr dnsRR_Header + Pref uint16 + Mx string "domain-name" +} + +func (rr *dnsRR_MX) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_NS struct { + Hdr dnsRR_Header + Ns string "domain-name" +} + +func (rr *dnsRR_NS) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_PTR struct { + Hdr dnsRR_Header + Ptr string "domain-name" +} + +func (rr *dnsRR_PTR) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_SOA struct { + Hdr dnsRR_Header + Ns string "domain-name" + Mbox string "domain-name" + Serial uint32 + Refresh uint32 + Retry uint32 + Expire uint32 + Minttl uint32 +} + +func (rr *dnsRR_SOA) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_TXT struct { + Hdr dnsRR_Header + Txt string // not domain name +} + +func (rr *dnsRR_TXT) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_SRV struct { + Hdr dnsRR_Header + Priority uint16 + Weight uint16 + Port uint16 + Target string "domain-name" +} + +func (rr *dnsRR_SRV) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_A struct { + Hdr dnsRR_Header + A uint32 "ipv4" +} + +func (rr *dnsRR_A) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_AAAA struct { + Hdr dnsRR_Header + AAAA [16]byte "ipv6" +} + +func (rr *dnsRR_AAAA) Header() *dnsRR_Header { + return &rr.Hdr +} + +// Packing and unpacking. +// +// All the packers and unpackers take a (msg []byte, off int) +// and return (off1 int, ok bool). If they return ok==false, they +// also return off1==len(msg), so that the next unpacker will +// also fail. This lets us avoid checks of ok until the end of a +// packing sequence. + +// Map of constructors for each RR wire type. +var rr_mk = map[int]func() dnsRR{ + dnsTypeCNAME: func() dnsRR { return new(dnsRR_CNAME) }, + dnsTypeHINFO: func() dnsRR { return new(dnsRR_HINFO) }, + dnsTypeMB: func() dnsRR { return new(dnsRR_MB) }, + dnsTypeMG: func() dnsRR { return new(dnsRR_MG) }, + dnsTypeMINFO: func() dnsRR { return new(dnsRR_MINFO) }, + dnsTypeMR: func() dnsRR { return new(dnsRR_MR) }, + dnsTypeMX: func() dnsRR { return new(dnsRR_MX) }, + dnsTypeNS: func() dnsRR { return new(dnsRR_NS) }, + dnsTypePTR: func() dnsRR { return new(dnsRR_PTR) }, + dnsTypeSOA: func() dnsRR { return new(dnsRR_SOA) }, + dnsTypeTXT: func() dnsRR { return new(dnsRR_TXT) }, + dnsTypeSRV: func() dnsRR { return new(dnsRR_SRV) }, + dnsTypeA: func() dnsRR { return new(dnsRR_A) }, + dnsTypeAAAA: func() dnsRR { return new(dnsRR_AAAA) }, +} + +// Pack a domain name s into msg[off:]. +// Domain names are a sequence of counted strings +// split at the dots. They end with a zero-length string. +func packDomainName(s string, msg []byte, off int) (off1 int, ok bool) { + // Add trailing dot to canonicalize name. + if n := len(s); n == 0 || s[n-1] != '.' { + s += "." + } + + // Each dot ends a segment of the name. + // We trade each dot byte for a length byte. + // There is also a trailing zero. + // Check that we have all the space we need. + tot := len(s) + 1 + if off+tot > len(msg) { + return len(msg), false + } + + // Emit sequence of counted strings, chopping at dots. + begin := 0 + for i := 0; i < len(s); i++ { + if s[i] == '.' { + if i-begin >= 1<<6 { // top two bits of length must be clear + return len(msg), false + } + msg[off] = byte(i - begin) + off++ + for j := begin; j < i; j++ { + msg[off] = s[j] + off++ + } + begin = i + 1 + } + } + msg[off] = 0 + off++ + return off, true +} + +// Unpack a domain name. +// In addition to the simple sequences of counted strings above, +// domain names are allowed to refer to strings elsewhere in the +// packet, to avoid repeating common suffixes when returning +// many entries in a single domain. The pointers are marked +// by a length byte with the top two bits set. Ignoring those +// two bits, that byte and the next give a 14 bit offset from msg[0] +// where we should pick up the trail. +// Note that if we jump elsewhere in the packet, +// we return off1 == the offset after the first pointer we found, +// which is where the next record will start. +// In theory, the pointers are only allowed to jump backward. +// We let them jump anywhere and stop jumping after a while. +func unpackDomainName(msg []byte, off int) (s string, off1 int, ok bool) { + s = "" + ptr := 0 // number of pointers followed +Loop: + for { + if off >= len(msg) { + return "", len(msg), false + } + c := int(msg[off]) + off++ + switch c & 0xC0 { + case 0x00: + if c == 0x00 { + // end of name + break Loop + } + // literal string + if off+c > len(msg) { + return "", len(msg), false + } + s += string(msg[off:off+c]) + "." + off += c + case 0xC0: + // pointer to somewhere else in msg. + // remember location after first ptr, + // since that's how many bytes we consumed. + // also, don't follow too many pointers -- + // maybe there's a loop. + if off >= len(msg) { + return "", len(msg), false + } + c1 := msg[off] + off++ + if ptr == 0 { + off1 = off + } + if ptr++; ptr > 10 { + return "", len(msg), false + } + off = (c^0xC0)<<8 | int(c1) + default: + // 0x80 and 0x40 are reserved + return "", len(msg), false + } + } + if ptr == 0 { + off1 = off + } + return s, off1, true +} + +// TODO(rsc): Move into generic library? +// Pack a reflect.StructValue into msg. Struct members can only be uint16, uint32, string, +// [n]byte, and other (often anonymous) structs. +func packStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int, ok bool) { + for i := 0; i < val.NumField(); i++ { + f := val.Type().(*reflect.StructType).Field(i) + switch fv := val.Field(i).(type) { + default: + BadType: + fmt.Fprintf(os.Stderr, "net: dns: unknown packing type %v", f.Type) + return len(msg), false + case *reflect.StructValue: + off, ok = packStructValue(fv, msg, off) + case *reflect.UintValue: + i := fv.Get() + switch fv.Type().Kind() { + default: + goto BadType + case reflect.Uint16: + if off+2 > len(msg) { + return len(msg), false + } + msg[off] = byte(i >> 8) + msg[off+1] = byte(i) + off += 2 + case reflect.Uint32: + if off+4 > len(msg) { + return len(msg), false + } + msg[off] = byte(i >> 24) + msg[off+1] = byte(i >> 16) + msg[off+2] = byte(i >> 8) + msg[off+3] = byte(i) + off += 4 + } + case *reflect.ArrayValue: + if fv.Type().(*reflect.ArrayType).Elem().Kind() != reflect.Uint8 { + goto BadType + } + n := fv.Len() + if off+n > len(msg) { + return len(msg), false + } + reflect.Copy(reflect.NewValue(msg[off:off+n]).(*reflect.SliceValue), fv) + off += n + case *reflect.StringValue: + // There are multiple string encodings. + // The tag distinguishes ordinary strings from domain names. + s := fv.Get() + switch f.Tag { + default: + fmt.Fprintf(os.Stderr, "net: dns: unknown string tag %v", f.Tag) + return len(msg), false + case "domain-name": + off, ok = packDomainName(s, msg, off) + if !ok { + return len(msg), false + } + case "": + // Counted string: 1 byte length. + if len(s) > 255 || off+1+len(s) > len(msg) { + return len(msg), false + } + msg[off] = byte(len(s)) + off++ + off += copy(msg[off:], s) + } + } + } + return off, true +} + +func structValue(any interface{}) *reflect.StructValue { + return reflect.NewValue(any).(*reflect.PtrValue).Elem().(*reflect.StructValue) +} + +func packStruct(any interface{}, msg []byte, off int) (off1 int, ok bool) { + off, ok = packStructValue(structValue(any), msg, off) + return off, ok +} + +// TODO(rsc): Move into generic library? +// Unpack a reflect.StructValue from msg. +// Same restrictions as packStructValue. +func unpackStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int, ok bool) { + for i := 0; i < val.NumField(); i++ { + f := val.Type().(*reflect.StructType).Field(i) + switch fv := val.Field(i).(type) { + default: + BadType: + fmt.Fprintf(os.Stderr, "net: dns: unknown packing type %v", f.Type) + return len(msg), false + case *reflect.StructValue: + off, ok = unpackStructValue(fv, msg, off) + case *reflect.UintValue: + switch fv.Type().Kind() { + default: + goto BadType + case reflect.Uint16: + if off+2 > len(msg) { + return len(msg), false + } + i := uint16(msg[off])<<8 | uint16(msg[off+1]) + fv.Set(uint64(i)) + off += 2 + case reflect.Uint32: + if off+4 > len(msg) { + return len(msg), false + } + i := uint32(msg[off])<<24 | uint32(msg[off+1])<<16 | uint32(msg[off+2])<<8 | uint32(msg[off+3]) + fv.Set(uint64(i)) + off += 4 + } + case *reflect.ArrayValue: + if fv.Type().(*reflect.ArrayType).Elem().Kind() != reflect.Uint8 { + goto BadType + } + n := fv.Len() + if off+n > len(msg) { + return len(msg), false + } + reflect.Copy(fv, reflect.NewValue(msg[off:off+n]).(*reflect.SliceValue)) + off += n + case *reflect.StringValue: + var s string + switch f.Tag { + default: + fmt.Fprintf(os.Stderr, "net: dns: unknown string tag %v", f.Tag) + return len(msg), false + case "domain-name": + s, off, ok = unpackDomainName(msg, off) + if !ok { + return len(msg), false + } + case "": + if off >= len(msg) || off+1+int(msg[off]) > len(msg) { + return len(msg), false + } + n := int(msg[off]) + off++ + b := make([]byte, n) + for i := 0; i < n; i++ { + b[i] = msg[off+i] + } + off += n + s = string(b) + } + fv.Set(s) + } + } + return off, true +} + +func unpackStruct(any interface{}, msg []byte, off int) (off1 int, ok bool) { + off, ok = unpackStructValue(structValue(any), msg, off) + return off, ok +} + +// Generic struct printer. +// Doesn't care about the string tag "domain-name", +// but does look for an "ipv4" tag on uint32 variables +// and the "ipv6" tag on array variables, +// printing them as IP addresses. +func printStructValue(val *reflect.StructValue) string { + s := "{" + for i := 0; i < val.NumField(); i++ { + if i > 0 { + s += ", " + } + f := val.Type().(*reflect.StructType).Field(i) + if !f.Anonymous { + s += f.Name + "=" + } + fval := val.Field(i) + if fv, ok := fval.(*reflect.StructValue); ok { + s += printStructValue(fv) + } else if fv, ok := fval.(*reflect.UintValue); ok && f.Tag == "ipv4" { + i := fv.Get() + s += IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i)).String() + } else if fv, ok := fval.(*reflect.ArrayValue); ok && f.Tag == "ipv6" { + i := fv.Interface().([]byte) + s += IP(i).String() + } else { + s += fmt.Sprint(fval.Interface()) + } + } + s += "}" + return s +} + +func printStruct(any interface{}) string { return printStructValue(structValue(any)) } + +// Resource record packer. +func packRR(rr dnsRR, msg []byte, off int) (off2 int, ok bool) { + var off1 int + // pack twice, once to find end of header + // and again to find end of packet. + // a bit inefficient but this doesn't need to be fast. + // off1 is end of header + // off2 is end of rr + off1, ok = packStruct(rr.Header(), msg, off) + off2, ok = packStruct(rr, msg, off) + if !ok { + return len(msg), false + } + // pack a third time; redo header with correct data length + rr.Header().Rdlength = uint16(off2 - off1) + packStruct(rr.Header(), msg, off) + return off2, true +} + +// Resource record unpacker. +func unpackRR(msg []byte, off int) (rr dnsRR, off1 int, ok bool) { + // unpack just the header, to find the rr type and length + var h dnsRR_Header + off0 := off + if off, ok = unpackStruct(&h, msg, off); !ok { + return nil, len(msg), false + } + end := off + int(h.Rdlength) + + // make an rr of that type and re-unpack. + // again inefficient but doesn't need to be fast. + mk, known := rr_mk[int(h.Rrtype)] + if !known { + return &h, end, true + } + rr = mk() + off, ok = unpackStruct(rr, msg, off0) + if off != end { + return &h, end, true + } + return rr, off, ok +} + +// Usable representation of a DNS packet. + +// A manually-unpacked version of (id, bits). +// This is in its own struct for easy printing. +type dnsMsgHdr struct { + id uint16 + response bool + opcode int + authoritative bool + truncated bool + recursion_desired bool + recursion_available bool + rcode int +} + +type dnsMsg struct { + dnsMsgHdr + question []dnsQuestion + answer []dnsRR + ns []dnsRR + extra []dnsRR +} + + +func (dns *dnsMsg) Pack() (msg []byte, ok bool) { + var dh dnsHeader + + // Convert convenient dnsMsg into wire-like dnsHeader. + dh.Id = dns.id + dh.Bits = uint16(dns.opcode)<<11 | uint16(dns.rcode) + if dns.recursion_available { + dh.Bits |= _RA + } + if dns.recursion_desired { + dh.Bits |= _RD + } + if dns.truncated { + dh.Bits |= _TC + } + if dns.authoritative { + dh.Bits |= _AA + } + if dns.response { + dh.Bits |= _QR + } + + // Prepare variable sized arrays. + question := dns.question + answer := dns.answer + ns := dns.ns + extra := dns.extra + + dh.Qdcount = uint16(len(question)) + dh.Ancount = uint16(len(answer)) + dh.Nscount = uint16(len(ns)) + dh.Arcount = uint16(len(extra)) + + // Could work harder to calculate message size, + // but this is far more than we need and not + // big enough to hurt the allocator. + msg = make([]byte, 2000) + + // Pack it in: header and then the pieces. + off := 0 + off, ok = packStruct(&dh, msg, off) + for i := 0; i < len(question); i++ { + off, ok = packStruct(&question[i], msg, off) + } + for i := 0; i < len(answer); i++ { + off, ok = packRR(answer[i], msg, off) + } + for i := 0; i < len(ns); i++ { + off, ok = packRR(ns[i], msg, off) + } + for i := 0; i < len(extra); i++ { + off, ok = packRR(extra[i], msg, off) + } + if !ok { + return nil, false + } + return msg[0:off], true +} + +func (dns *dnsMsg) Unpack(msg []byte) bool { + // Header. + var dh dnsHeader + off := 0 + var ok bool + if off, ok = unpackStruct(&dh, msg, off); !ok { + return false + } + dns.id = dh.Id + dns.response = (dh.Bits & _QR) != 0 + dns.opcode = int(dh.Bits>>11) & 0xF + dns.authoritative = (dh.Bits & _AA) != 0 + dns.truncated = (dh.Bits & _TC) != 0 + dns.recursion_desired = (dh.Bits & _RD) != 0 + dns.recursion_available = (dh.Bits & _RA) != 0 + dns.rcode = int(dh.Bits & 0xF) + + // Arrays. + dns.question = make([]dnsQuestion, dh.Qdcount) + dns.answer = make([]dnsRR, dh.Ancount) + dns.ns = make([]dnsRR, dh.Nscount) + dns.extra = make([]dnsRR, dh.Arcount) + + for i := 0; i < len(dns.question); i++ { + off, ok = unpackStruct(&dns.question[i], msg, off) + } + for i := 0; i < len(dns.answer); i++ { + dns.answer[i], off, ok = unpackRR(msg, off) + } + for i := 0; i < len(dns.ns); i++ { + dns.ns[i], off, ok = unpackRR(msg, off) + } + for i := 0; i < len(dns.extra); i++ { + dns.extra[i], off, ok = unpackRR(msg, off) + } + if !ok { + return false + } + // if off != len(msg) { + // println("extra bytes in dns packet", off, "<", len(msg)); + // } + return true +} + +func (dns *dnsMsg) String() string { + s := "DNS: " + printStruct(&dns.dnsMsgHdr) + "\n" + if len(dns.question) > 0 { + s += "-- Questions\n" + for i := 0; i < len(dns.question); i++ { + s += printStruct(&dns.question[i]) + "\n" + } + } + if len(dns.answer) > 0 { + s += "-- Answers\n" + for i := 0; i < len(dns.answer); i++ { + s += printStruct(dns.answer[i]) + "\n" + } + } + if len(dns.ns) > 0 { + s += "-- Name servers\n" + for i := 0; i < len(dns.ns); i++ { + s += printStruct(dns.ns[i]) + "\n" + } + } + if len(dns.extra) > 0 { + s += "-- Extra\n" + for i := 0; i < len(dns.extra); i++ { + s += printStruct(dns.extra[i]) + "\n" + } + } + return s +} diff --git a/src/cmd/gofix/testdata/reflect.dnsmsg.go.out b/src/cmd/gofix/testdata/reflect.dnsmsg.go.out new file mode 100644 index 000000000..546e713a0 --- /dev/null +++ b/src/cmd/gofix/testdata/reflect.dnsmsg.go.out @@ -0,0 +1,779 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// DNS packet assembly. See RFC 1035. +// +// This is intended to support name resolution during net.Dial. +// It doesn't have to be blazing fast. +// +// Rather than write the usual handful of routines to pack and +// unpack every message that can appear on the wire, we use +// reflection to write a generic pack/unpack for structs and then +// use it. Thus, if in the future we need to define new message +// structs, no new pack/unpack/printing code needs to be written. +// +// The first half of this file defines the DNS message formats. +// The second half implements the conversion to and from wire format. +// A few of the structure elements have string tags to aid the +// generic pack/unpack routines. +// +// TODO(rsc): There are enough names defined in this file that they're all +// prefixed with dns. Perhaps put this in its own package later. + +package net + +import ( + "fmt" + "os" + "reflect" +) + +// Packet formats + +// Wire constants. +const ( + // valid dnsRR_Header.Rrtype and dnsQuestion.qtype + dnsTypeA = 1 + dnsTypeNS = 2 + dnsTypeMD = 3 + dnsTypeMF = 4 + dnsTypeCNAME = 5 + dnsTypeSOA = 6 + dnsTypeMB = 7 + dnsTypeMG = 8 + dnsTypeMR = 9 + dnsTypeNULL = 10 + dnsTypeWKS = 11 + dnsTypePTR = 12 + dnsTypeHINFO = 13 + dnsTypeMINFO = 14 + dnsTypeMX = 15 + dnsTypeTXT = 16 + dnsTypeAAAA = 28 + dnsTypeSRV = 33 + + // valid dnsQuestion.qtype only + dnsTypeAXFR = 252 + dnsTypeMAILB = 253 + dnsTypeMAILA = 254 + dnsTypeALL = 255 + + // valid dnsQuestion.qclass + dnsClassINET = 1 + dnsClassCSNET = 2 + dnsClassCHAOS = 3 + dnsClassHESIOD = 4 + dnsClassANY = 255 + + // dnsMsg.rcode + dnsRcodeSuccess = 0 + dnsRcodeFormatError = 1 + dnsRcodeServerFailure = 2 + dnsRcodeNameError = 3 + dnsRcodeNotImplemented = 4 + dnsRcodeRefused = 5 +) + +// The wire format for the DNS packet header. +type dnsHeader struct { + Id uint16 + Bits uint16 + Qdcount, Ancount, Nscount, Arcount uint16 +} + +const ( + // dnsHeader.Bits + _QR = 1 << 15 // query/response (response=1) + _AA = 1 << 10 // authoritative + _TC = 1 << 9 // truncated + _RD = 1 << 8 // recursion desired + _RA = 1 << 7 // recursion available +) + +// DNS queries. +type dnsQuestion struct { + Name string "domain-name" // "domain-name" specifies encoding; see packers below + Qtype uint16 + Qclass uint16 +} + +// DNS responses (resource records). +// There are many types of messages, +// but they all share the same header. +type dnsRR_Header struct { + Name string "domain-name" + Rrtype uint16 + Class uint16 + Ttl uint32 + Rdlength uint16 // length of data after header +} + +func (h *dnsRR_Header) Header() *dnsRR_Header { + return h +} + +type dnsRR interface { + Header() *dnsRR_Header +} + + +// Specific DNS RR formats for each query type. + +type dnsRR_CNAME struct { + Hdr dnsRR_Header + Cname string "domain-name" +} + +func (rr *dnsRR_CNAME) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_HINFO struct { + Hdr dnsRR_Header + Cpu string + Os string +} + +func (rr *dnsRR_HINFO) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_MB struct { + Hdr dnsRR_Header + Mb string "domain-name" +} + +func (rr *dnsRR_MB) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_MG struct { + Hdr dnsRR_Header + Mg string "domain-name" +} + +func (rr *dnsRR_MG) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_MINFO struct { + Hdr dnsRR_Header + Rmail string "domain-name" + Email string "domain-name" +} + +func (rr *dnsRR_MINFO) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_MR struct { + Hdr dnsRR_Header + Mr string "domain-name" +} + +func (rr *dnsRR_MR) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_MX struct { + Hdr dnsRR_Header + Pref uint16 + Mx string "domain-name" +} + +func (rr *dnsRR_MX) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_NS struct { + Hdr dnsRR_Header + Ns string "domain-name" +} + +func (rr *dnsRR_NS) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_PTR struct { + Hdr dnsRR_Header + Ptr string "domain-name" +} + +func (rr *dnsRR_PTR) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_SOA struct { + Hdr dnsRR_Header + Ns string "domain-name" + Mbox string "domain-name" + Serial uint32 + Refresh uint32 + Retry uint32 + Expire uint32 + Minttl uint32 +} + +func (rr *dnsRR_SOA) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_TXT struct { + Hdr dnsRR_Header + Txt string // not domain name +} + +func (rr *dnsRR_TXT) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_SRV struct { + Hdr dnsRR_Header + Priority uint16 + Weight uint16 + Port uint16 + Target string "domain-name" +} + +func (rr *dnsRR_SRV) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_A struct { + Hdr dnsRR_Header + A uint32 "ipv4" +} + +func (rr *dnsRR_A) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_AAAA struct { + Hdr dnsRR_Header + AAAA [16]byte "ipv6" +} + +func (rr *dnsRR_AAAA) Header() *dnsRR_Header { + return &rr.Hdr +} + +// Packing and unpacking. +// +// All the packers and unpackers take a (msg []byte, off int) +// and return (off1 int, ok bool). If they return ok==false, they +// also return off1==len(msg), so that the next unpacker will +// also fail. This lets us avoid checks of ok until the end of a +// packing sequence. + +// Map of constructors for each RR wire type. +var rr_mk = map[int]func() dnsRR{ + dnsTypeCNAME: func() dnsRR { return new(dnsRR_CNAME) }, + dnsTypeHINFO: func() dnsRR { return new(dnsRR_HINFO) }, + dnsTypeMB: func() dnsRR { return new(dnsRR_MB) }, + dnsTypeMG: func() dnsRR { return new(dnsRR_MG) }, + dnsTypeMINFO: func() dnsRR { return new(dnsRR_MINFO) }, + dnsTypeMR: func() dnsRR { return new(dnsRR_MR) }, + dnsTypeMX: func() dnsRR { return new(dnsRR_MX) }, + dnsTypeNS: func() dnsRR { return new(dnsRR_NS) }, + dnsTypePTR: func() dnsRR { return new(dnsRR_PTR) }, + dnsTypeSOA: func() dnsRR { return new(dnsRR_SOA) }, + dnsTypeTXT: func() dnsRR { return new(dnsRR_TXT) }, + dnsTypeSRV: func() dnsRR { return new(dnsRR_SRV) }, + dnsTypeA: func() dnsRR { return new(dnsRR_A) }, + dnsTypeAAAA: func() dnsRR { return new(dnsRR_AAAA) }, +} + +// Pack a domain name s into msg[off:]. +// Domain names are a sequence of counted strings +// split at the dots. They end with a zero-length string. +func packDomainName(s string, msg []byte, off int) (off1 int, ok bool) { + // Add trailing dot to canonicalize name. + if n := len(s); n == 0 || s[n-1] != '.' { + s += "." + } + + // Each dot ends a segment of the name. + // We trade each dot byte for a length byte. + // There is also a trailing zero. + // Check that we have all the space we need. + tot := len(s) + 1 + if off+tot > len(msg) { + return len(msg), false + } + + // Emit sequence of counted strings, chopping at dots. + begin := 0 + for i := 0; i < len(s); i++ { + if s[i] == '.' { + if i-begin >= 1<<6 { // top two bits of length must be clear + return len(msg), false + } + msg[off] = byte(i - begin) + off++ + for j := begin; j < i; j++ { + msg[off] = s[j] + off++ + } + begin = i + 1 + } + } + msg[off] = 0 + off++ + return off, true +} + +// Unpack a domain name. +// In addition to the simple sequences of counted strings above, +// domain names are allowed to refer to strings elsewhere in the +// packet, to avoid repeating common suffixes when returning +// many entries in a single domain. The pointers are marked +// by a length byte with the top two bits set. Ignoring those +// two bits, that byte and the next give a 14 bit offset from msg[0] +// where we should pick up the trail. +// Note that if we jump elsewhere in the packet, +// we return off1 == the offset after the first pointer we found, +// which is where the next record will start. +// In theory, the pointers are only allowed to jump backward. +// We let them jump anywhere and stop jumping after a while. +func unpackDomainName(msg []byte, off int) (s string, off1 int, ok bool) { + s = "" + ptr := 0 // number of pointers followed +Loop: + for { + if off >= len(msg) { + return "", len(msg), false + } + c := int(msg[off]) + off++ + switch c & 0xC0 { + case 0x00: + if c == 0x00 { + // end of name + break Loop + } + // literal string + if off+c > len(msg) { + return "", len(msg), false + } + s += string(msg[off:off+c]) + "." + off += c + case 0xC0: + // pointer to somewhere else in msg. + // remember location after first ptr, + // since that's how many bytes we consumed. + // also, don't follow too many pointers -- + // maybe there's a loop. + if off >= len(msg) { + return "", len(msg), false + } + c1 := msg[off] + off++ + if ptr == 0 { + off1 = off + } + if ptr++; ptr > 10 { + return "", len(msg), false + } + off = (c^0xC0)<<8 | int(c1) + default: + // 0x80 and 0x40 are reserved + return "", len(msg), false + } + } + if ptr == 0 { + off1 = off + } + return s, off1, true +} + +// TODO(rsc): Move into generic library? +// Pack a reflect.StructValue into msg. Struct members can only be uint16, uint32, string, +// [n]byte, and other (often anonymous) structs. +func packStructValue(val reflect.Value, msg []byte, off int) (off1 int, ok bool) { + for i := 0; i < val.NumField(); i++ { + f := val.Type().Field(i) + switch fv := val.Field(i); fv.Kind() { + default: + BadType: + fmt.Fprintf(os.Stderr, "net: dns: unknown packing type %v", f.Type) + return len(msg), false + case reflect.Struct: + off, ok = packStructValue(fv, msg, off) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + i := fv.Uint() + switch fv.Type().Kind() { + default: + goto BadType + case reflect.Uint16: + if off+2 > len(msg) { + return len(msg), false + } + msg[off] = byte(i >> 8) + msg[off+1] = byte(i) + off += 2 + case reflect.Uint32: + if off+4 > len(msg) { + return len(msg), false + } + msg[off] = byte(i >> 24) + msg[off+1] = byte(i >> 16) + msg[off+2] = byte(i >> 8) + msg[off+3] = byte(i) + off += 4 + } + case reflect.Array: + if fv.Type().Elem().Kind() != reflect.Uint8 { + goto BadType + } + n := fv.Len() + if off+n > len(msg) { + return len(msg), false + } + reflect.Copy(reflect.NewValue(msg[off:off+n]), fv) + off += n + case reflect.String: + // There are multiple string encodings. + // The tag distinguishes ordinary strings from domain names. + s := fv.String() + switch f.Tag { + default: + fmt.Fprintf(os.Stderr, "net: dns: unknown string tag %v", f.Tag) + return len(msg), false + case "domain-name": + off, ok = packDomainName(s, msg, off) + if !ok { + return len(msg), false + } + case "": + // Counted string: 1 byte length. + if len(s) > 255 || off+1+len(s) > len(msg) { + return len(msg), false + } + msg[off] = byte(len(s)) + off++ + off += copy(msg[off:], s) + } + } + } + return off, true +} + +func structValue(any interface{}) reflect.Value { + return reflect.NewValue(any).Elem() +} + +func packStruct(any interface{}, msg []byte, off int) (off1 int, ok bool) { + off, ok = packStructValue(structValue(any), msg, off) + return off, ok +} + +// TODO(rsc): Move into generic library? +// Unpack a reflect.StructValue from msg. +// Same restrictions as packStructValue. +func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, ok bool) { + for i := 0; i < val.NumField(); i++ { + f := val.Type().Field(i) + switch fv := val.Field(i); fv.Kind() { + default: + BadType: + fmt.Fprintf(os.Stderr, "net: dns: unknown packing type %v", f.Type) + return len(msg), false + case reflect.Struct: + off, ok = unpackStructValue(fv, msg, off) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + switch fv.Type().Kind() { + default: + goto BadType + case reflect.Uint16: + if off+2 > len(msg) { + return len(msg), false + } + i := uint16(msg[off])<<8 | uint16(msg[off+1]) + fv.SetUint(uint64(i)) + off += 2 + case reflect.Uint32: + if off+4 > len(msg) { + return len(msg), false + } + i := uint32(msg[off])<<24 | uint32(msg[off+1])<<16 | uint32(msg[off+2])<<8 | uint32(msg[off+3]) + fv.SetUint(uint64(i)) + off += 4 + } + case reflect.Array: + if fv.Type().Elem().Kind() != reflect.Uint8 { + goto BadType + } + n := fv.Len() + if off+n > len(msg) { + return len(msg), false + } + reflect.Copy(fv, reflect.NewValue(msg[off:off+n])) + off += n + case reflect.String: + var s string + switch f.Tag { + default: + fmt.Fprintf(os.Stderr, "net: dns: unknown string tag %v", f.Tag) + return len(msg), false + case "domain-name": + s, off, ok = unpackDomainName(msg, off) + if !ok { + return len(msg), false + } + case "": + if off >= len(msg) || off+1+int(msg[off]) > len(msg) { + return len(msg), false + } + n := int(msg[off]) + off++ + b := make([]byte, n) + for i := 0; i < n; i++ { + b[i] = msg[off+i] + } + off += n + s = string(b) + } + fv.SetString(s) + } + } + return off, true +} + +func unpackStruct(any interface{}, msg []byte, off int) (off1 int, ok bool) { + off, ok = unpackStructValue(structValue(any), msg, off) + return off, ok +} + +// Generic struct printer. +// Doesn't care about the string tag "domain-name", +// but does look for an "ipv4" tag on uint32 variables +// and the "ipv6" tag on array variables, +// printing them as IP addresses. +func printStructValue(val reflect.Value) string { + s := "{" + for i := 0; i < val.NumField(); i++ { + if i > 0 { + s += ", " + } + f := val.Type().Field(i) + if !f.Anonymous { + s += f.Name + "=" + } + fval := val.Field(i) + if fv := fval; fv.Kind() == reflect.Struct { + s += printStructValue(fv) + } else if fv := fval; (fv.Kind() == reflect.Uint || fv.Kind() == reflect.Uint8 || fv.Kind() == reflect.Uint16 || fv.Kind() == reflect.Uint32 || fv.Kind() == reflect.Uint64 || fv.Kind() == reflect.Uintptr) && f.Tag == "ipv4" { + i := fv.Uint() + s += IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i)).String() + } else if fv := fval; fv.Kind() == reflect.Array && f.Tag == "ipv6" { + i := fv.Interface().([]byte) + s += IP(i).String() + } else { + s += fmt.Sprint(fval.Interface()) + } + } + s += "}" + return s +} + +func printStruct(any interface{}) string { return printStructValue(structValue(any)) } + +// Resource record packer. +func packRR(rr dnsRR, msg []byte, off int) (off2 int, ok bool) { + var off1 int + // pack twice, once to find end of header + // and again to find end of packet. + // a bit inefficient but this doesn't need to be fast. + // off1 is end of header + // off2 is end of rr + off1, ok = packStruct(rr.Header(), msg, off) + off2, ok = packStruct(rr, msg, off) + if !ok { + return len(msg), false + } + // pack a third time; redo header with correct data length + rr.Header().Rdlength = uint16(off2 - off1) + packStruct(rr.Header(), msg, off) + return off2, true +} + +// Resource record unpacker. +func unpackRR(msg []byte, off int) (rr dnsRR, off1 int, ok bool) { + // unpack just the header, to find the rr type and length + var h dnsRR_Header + off0 := off + if off, ok = unpackStruct(&h, msg, off); !ok { + return nil, len(msg), false + } + end := off + int(h.Rdlength) + + // make an rr of that type and re-unpack. + // again inefficient but doesn't need to be fast. + mk, known := rr_mk[int(h.Rrtype)] + if !known { + return &h, end, true + } + rr = mk() + off, ok = unpackStruct(rr, msg, off0) + if off != end { + return &h, end, true + } + return rr, off, ok +} + +// Usable representation of a DNS packet. + +// A manually-unpacked version of (id, bits). +// This is in its own struct for easy printing. +type dnsMsgHdr struct { + id uint16 + response bool + opcode int + authoritative bool + truncated bool + recursion_desired bool + recursion_available bool + rcode int +} + +type dnsMsg struct { + dnsMsgHdr + question []dnsQuestion + answer []dnsRR + ns []dnsRR + extra []dnsRR +} + + +func (dns *dnsMsg) Pack() (msg []byte, ok bool) { + var dh dnsHeader + + // Convert convenient dnsMsg into wire-like dnsHeader. + dh.Id = dns.id + dh.Bits = uint16(dns.opcode)<<11 | uint16(dns.rcode) + if dns.recursion_available { + dh.Bits |= _RA + } + if dns.recursion_desired { + dh.Bits |= _RD + } + if dns.truncated { + dh.Bits |= _TC + } + if dns.authoritative { + dh.Bits |= _AA + } + if dns.response { + dh.Bits |= _QR + } + + // Prepare variable sized arrays. + question := dns.question + answer := dns.answer + ns := dns.ns + extra := dns.extra + + dh.Qdcount = uint16(len(question)) + dh.Ancount = uint16(len(answer)) + dh.Nscount = uint16(len(ns)) + dh.Arcount = uint16(len(extra)) + + // Could work harder to calculate message size, + // but this is far more than we need and not + // big enough to hurt the allocator. + msg = make([]byte, 2000) + + // Pack it in: header and then the pieces. + off := 0 + off, ok = packStruct(&dh, msg, off) + for i := 0; i < len(question); i++ { + off, ok = packStruct(&question[i], msg, off) + } + for i := 0; i < len(answer); i++ { + off, ok = packRR(answer[i], msg, off) + } + for i := 0; i < len(ns); i++ { + off, ok = packRR(ns[i], msg, off) + } + for i := 0; i < len(extra); i++ { + off, ok = packRR(extra[i], msg, off) + } + if !ok { + return nil, false + } + return msg[0:off], true +} + +func (dns *dnsMsg) Unpack(msg []byte) bool { + // Header. + var dh dnsHeader + off := 0 + var ok bool + if off, ok = unpackStruct(&dh, msg, off); !ok { + return false + } + dns.id = dh.Id + dns.response = (dh.Bits & _QR) != 0 + dns.opcode = int(dh.Bits>>11) & 0xF + dns.authoritative = (dh.Bits & _AA) != 0 + dns.truncated = (dh.Bits & _TC) != 0 + dns.recursion_desired = (dh.Bits & _RD) != 0 + dns.recursion_available = (dh.Bits & _RA) != 0 + dns.rcode = int(dh.Bits & 0xF) + + // Arrays. + dns.question = make([]dnsQuestion, dh.Qdcount) + dns.answer = make([]dnsRR, dh.Ancount) + dns.ns = make([]dnsRR, dh.Nscount) + dns.extra = make([]dnsRR, dh.Arcount) + + for i := 0; i < len(dns.question); i++ { + off, ok = unpackStruct(&dns.question[i], msg, off) + } + for i := 0; i < len(dns.answer); i++ { + dns.answer[i], off, ok = unpackRR(msg, off) + } + for i := 0; i < len(dns.ns); i++ { + dns.ns[i], off, ok = unpackRR(msg, off) + } + for i := 0; i < len(dns.extra); i++ { + dns.extra[i], off, ok = unpackRR(msg, off) + } + if !ok { + return false + } + // if off != len(msg) { + // println("extra bytes in dns packet", off, "<", len(msg)); + // } + return true +} + +func (dns *dnsMsg) String() string { + s := "DNS: " + printStruct(&dns.dnsMsgHdr) + "\n" + if len(dns.question) > 0 { + s += "-- Questions\n" + for i := 0; i < len(dns.question); i++ { + s += printStruct(&dns.question[i]) + "\n" + } + } + if len(dns.answer) > 0 { + s += "-- Answers\n" + for i := 0; i < len(dns.answer); i++ { + s += printStruct(dns.answer[i]) + "\n" + } + } + if len(dns.ns) > 0 { + s += "-- Name servers\n" + for i := 0; i < len(dns.ns); i++ { + s += printStruct(dns.ns[i]) + "\n" + } + } + if len(dns.extra) > 0 { + s += "-- Extra\n" + for i := 0; i < len(dns.extra); i++ { + s += printStruct(dns.extra[i]) + "\n" + } + } + return s +} diff --git a/src/cmd/gofix/testdata/reflect.encode.go.in b/src/cmd/gofix/testdata/reflect.encode.go.in new file mode 100644 index 000000000..26ce47039 --- /dev/null +++ b/src/cmd/gofix/testdata/reflect.encode.go.in @@ -0,0 +1,367 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// The json package implements encoding and decoding of JSON objects as +// defined in RFC 4627. +package json + +import ( + "bytes" + "encoding/base64" + "os" + "reflect" + "runtime" + "sort" + "strconv" + "unicode" + "utf8" +) + +// Marshal returns the JSON encoding of v. +// +// Marshal traverses the value v recursively. +// If an encountered value implements the Marshaler interface, +// Marshal calls its MarshalJSON method to produce JSON. +// +// Otherwise, Marshal uses the following type-dependent default encodings: +// +// Boolean values encode as JSON booleans. +// +// Floating point and integer values encode as JSON numbers. +// +// String values encode as JSON strings, with each invalid UTF-8 sequence +// replaced by the encoding of the Unicode replacement character U+FFFD. +// +// Array and slice values encode as JSON arrays, except that +// []byte encodes as a base64-encoded string. +// +// Struct values encode as JSON objects. Each struct field becomes +// a member of the object. By default the object's key name is the +// struct field name. If the struct field has a non-empty tag consisting +// of only Unicode letters, digits, and underscores, that tag will be used +// as the name instead. Only exported fields will be encoded. +// +// Map values encode as JSON objects. +// The map's key type must be string; the object keys are used directly +// as map keys. +// +// Pointer values encode as the value pointed to. +// A nil pointer encodes as the null JSON object. +// +// Interface values encode as the value contained in the interface. +// A nil interface value encodes as the null JSON object. +// +// Channel, complex, and function values cannot be encoded in JSON. +// Attempting to encode such a value causes Marshal to return +// an InvalidTypeError. +// +// JSON cannot represent cyclic data structures and Marshal does not +// handle them. Passing cyclic structures to Marshal will result in +// an infinite recursion. +// +func Marshal(v interface{}) ([]byte, os.Error) { + e := &encodeState{} + err := e.marshal(v) + if err != nil { + return nil, err + } + return e.Bytes(), nil +} + +// MarshalIndent is like Marshal but applies Indent to format the output. +func MarshalIndent(v interface{}, prefix, indent string) ([]byte, os.Error) { + b, err := Marshal(v) + if err != nil { + return nil, err + } + var buf bytes.Buffer + err = Indent(&buf, b, prefix, indent) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +// MarshalForHTML is like Marshal but applies HTMLEscape to the output. +func MarshalForHTML(v interface{}) ([]byte, os.Error) { + b, err := Marshal(v) + if err != nil { + return nil, err + } + var buf bytes.Buffer + HTMLEscape(&buf, b) + return buf.Bytes(), nil +} + +// HTMLEscape appends to dst the JSON-encoded src with <, >, and & +// characters inside string literals changed to \u003c, \u003e, \u0026 +// so that the JSON will be safe to embed inside HTML