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 --- 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 +- 641 files changed, 51056 insertions(+), 14056 deletions(-) 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 (limited to 'src') 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