summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOndřej Surý <ondrej@sury.org>2011-04-26 09:55:32 +0200
committerOndřej Surý <ondrej@sury.org>2011-04-26 09:55:32 +0200
commit7b15ed9ef455b6b66c6b376898a88aef5d6a9970 (patch)
tree3ef530baa80cdf29436ba981f5783be6b4d2202b
parent50104cc32a498f7517a51c8dc93106c51c7a54b4 (diff)
downloadgolang-7b15ed9ef455b6b66c6b376898a88aef5d6a9970.tar.gz
Imported Upstream version 2011.04.13upstream/2011.04.13
-rw-r--r--AUTHORS6
-rw-r--r--CONTRIBUTORS9
-rw-r--r--doc/codelab/wiki/Makefile2
-rw-r--r--doc/codelab/wiki/final-noclosure.go2
-rw-r--r--doc/codelab/wiki/final-noerror.go4
-rw-r--r--doc/codelab/wiki/final-parsetemplate.go2
-rw-r--r--doc/codelab/wiki/final-template.go2
-rw-r--r--doc/codelab/wiki/index.html8
-rw-r--r--doc/codewalk/functions.xml115
-rw-r--r--doc/codewalk/pig.go124
-rw-r--r--doc/contrib.html47
-rw-r--r--doc/devel/release.html367
-rw-r--r--doc/devel/roadmap.html32
-rw-r--r--doc/effective_go.html81
-rw-r--r--doc/go_faq.html5
-rw-r--r--doc/go_spec.html69
-rw-r--r--doc/install.html27
-rw-r--r--lib/codereview/codereview.py141
-rw-r--r--lib/godoc/search.txt4
-rwxr-xr-xmisc/bbedit/Go.plist3
-rw-r--r--misc/cgo/stdio/Makefile3
-rw-r--r--misc/cgo/stdio/hello.go20
-rw-r--r--misc/cgo/test/Makefile23
-rw-r--r--misc/cgo/test/align.go (renamed from misc/cgo/stdio/align.go)18
-rw-r--r--misc/cgo/test/basic.go (renamed from misc/cgo/stdio/test.go)36
-rw-r--r--misc/cgo/test/callback.go136
-rw-r--r--misc/cgo/test/callback_c.c12
-rw-r--r--misc/cgo/test/cgo_test.go5
-rw-r--r--misc/cgo/test/issue1222.go (renamed from misc/cgo/stdio/test1.go)2
-rw-r--r--misc/cgo/test/issue1328.go30
-rw-r--r--misc/cgo/test/issue1560.go46
-rw-r--r--misc/cgo/test/runtime.c21
-rw-r--r--misc/dashboard/README34
-rw-r--r--misc/dashboard/buildcontrol.py278
-rw-r--r--misc/dashboard/buildcron.sh58
-rw-r--r--misc/dashboard/builder.sh95
-rw-r--r--misc/dashboard/builder/doc.go6
-rw-r--r--misc/dashboard/builder/exec.go2
-rw-r--r--misc/dashboard/builder/main.go2
-rw-r--r--misc/dashboard/godashboard/gobuild.py4
-rw-r--r--misc/dashboard/godashboard/package.py21
-rw-r--r--misc/emacs/go-mode-load.el19
-rw-r--r--misc/emacs/go-mode.el58
-rw-r--r--misc/goplay/goplay.go2
-rw-r--r--misc/kate/go.xml18
-rwxr-xr-xmisc/notepadplus/README8
-rwxr-xr-xmisc/notepadplus/go.xml66
-rwxr-xr-xmisc/notepadplus/userDefineLang.xml36
-rw-r--r--misc/swig/callback/Makefile17
-rw-r--r--misc/swig/callback/callback.h24
-rw-r--r--misc/swig/callback/callback.swigcxx18
-rwxr-xr-xmisc/swig/callback/runbin0 -> 1179384 bytes
-rw-r--r--misc/swig/callback/run.go39
-rw-r--r--misc/swig/stdio/Makefile17
-rw-r--r--misc/swig/stdio/file.swig11
-rwxr-xr-xmisc/swig/stdio/hellobin0 -> 231270 bytes
-rw-r--r--misc/swig/stdio/hello.go11
-rw-r--r--misc/vim/ftplugin/go/fmt.vim30
-rw-r--r--misc/vim/indent/go.vim30
-rw-r--r--misc/vim/readme.txt13
-rw-r--r--misc/vim/syntax/go.vim4
-rw-r--r--src/Make.cmd4
-rw-r--r--src/Make.common9
-rw-r--r--src/Make.inc35
-rw-r--r--src/Make.pkg61
-rwxr-xr-xsrc/all-qemu.bash16
-rwxr-xr-xsrc/clean.bash4
-rw-r--r--src/cmd/5c/peep.c4
-rw-r--r--src/cmd/5c/txt.c4
-rw-r--r--src/cmd/5g/ggen.c2
-rw-r--r--src/cmd/5g/gobj.c186
-rw-r--r--src/cmd/5l/asm.c5
-rw-r--r--src/cmd/5l/l.h19
-rw-r--r--src/cmd/5l/noop.c18
-rw-r--r--src/cmd/5l/softfloat.c1
-rw-r--r--src/cmd/5l/thumb.c1
-rw-r--r--src/cmd/6c/peep.c4
-rw-r--r--src/cmd/6c/txt.c6
-rw-r--r--src/cmd/6g/ggen.c2
-rw-r--r--src/cmd/6g/gobj.c174
-rw-r--r--src/cmd/6l/asm.c44
-rw-r--r--src/cmd/6l/l.h25
-rw-r--r--src/cmd/6l/obj.c18
-rw-r--r--src/cmd/6l/pass.c9
-rw-r--r--src/cmd/8c/peep.c2
-rw-r--r--src/cmd/8c/txt.c4
-rw-r--r--src/cmd/8g/ggen.c2
-rw-r--r--src/cmd/8g/gobj.c170
-rw-r--r--src/cmd/8g/peep.c29
-rw-r--r--src/cmd/8l/asm.c16
-rw-r--r--src/cmd/8l/l.h27
-rw-r--r--src/cmd/8l/pass.c15
-rw-r--r--src/cmd/Makefile5
-rw-r--r--src/cmd/cgo/ast.go20
-rw-r--r--src/cmd/cgo/gcc.go60
-rw-r--r--src/cmd/cgo/main.go8
-rw-r--r--src/cmd/cgo/out.go6
-rw-r--r--src/cmd/cgo/util.go20
-rw-r--r--src/cmd/gc/align.c12
-rw-r--r--src/cmd/gc/builtin.c.boot6
-rw-r--r--src/cmd/gc/const.c2
-rw-r--r--src/cmd/gc/dcl.c41
-rw-r--r--src/cmd/gc/export.c15
-rw-r--r--src/cmd/gc/gen.c157
-rw-r--r--src/cmd/gc/go.h32
-rw-r--r--src/cmd/gc/go.y20
-rw-r--r--src/cmd/gc/lex.c4
-rw-r--r--src/cmd/gc/obj.c54
-rw-r--r--src/cmd/gc/print.c2
-rw-r--r--src/cmd/gc/range.c4
-rw-r--r--src/cmd/gc/reflect.c65
-rw-r--r--src/cmd/gc/runtime.go4
-rw-r--r--src/cmd/gc/select.c70
-rw-r--r--src/cmd/gc/sinit.c2
-rw-r--r--src/cmd/gc/subr.c41
-rw-r--r--src/cmd/gc/typecheck.c21
-rw-r--r--src/cmd/gc/unsafe.go2
-rw-r--r--src/cmd/gc/walk.c15
-rw-r--r--src/cmd/godefs/stabs.c3
-rw-r--r--src/cmd/godoc/codewalk.go2
-rw-r--r--src/cmd/godoc/dirtrees.go8
-rw-r--r--src/cmd/godoc/format.go4
-rw-r--r--src/cmd/godoc/godoc.go11
-rw-r--r--src/cmd/godoc/index.go2
-rw-r--r--src/cmd/godoc/main.go9
-rw-r--r--src/cmd/godoc/spec.go6
-rw-r--r--src/cmd/godoc/utils.go2
-rw-r--r--src/cmd/gofix/Makefile24
-rw-r--r--src/cmd/gofix/doc.go34
-rw-r--r--src/cmd/gofix/fix.go422
-rw-r--r--src/cmd/gofix/httpserver.go140
-rw-r--r--src/cmd/gofix/httpserver_test.go53
-rw-r--r--src/cmd/gofix/main.go264
-rw-r--r--src/cmd/gofix/main_test.go126
-rw-r--r--src/cmd/gofix/netdial.go114
-rw-r--r--src/cmd/gofix/netdial_test.go51
-rw-r--r--src/cmd/gofix/osopen.go122
-rw-r--r--src/cmd/gofix/osopen_test.go59
-rw-r--r--src/cmd/gofix/procattr.go61
-rw-r--r--src/cmd/gofix/procattr_test.go74
-rw-r--r--src/cmd/gofix/reflect.go843
-rw-r--r--src/cmd/gofix/reflect_test.go31
-rw-r--r--src/cmd/gofix/testdata/reflect.asn1.go.in815
-rw-r--r--src/cmd/gofix/testdata/reflect.asn1.go.out815
-rw-r--r--src/cmd/gofix/testdata/reflect.datafmt.go.in731
-rw-r--r--src/cmd/gofix/testdata/reflect.datafmt.go.out731
-rw-r--r--src/cmd/gofix/testdata/reflect.decode.go.in907
-rw-r--r--src/cmd/gofix/testdata/reflect.decode.go.out910
-rw-r--r--src/cmd/gofix/testdata/reflect.decoder.go.in196
-rw-r--r--src/cmd/gofix/testdata/reflect.decoder.go.out196
-rw-r--r--src/cmd/gofix/testdata/reflect.dnsmsg.go.in779
-rw-r--r--src/cmd/gofix/testdata/reflect.dnsmsg.go.out779
-rw-r--r--src/cmd/gofix/testdata/reflect.encode.go.in367
-rw-r--r--src/cmd/gofix/testdata/reflect.encode.go.out367
-rw-r--r--src/cmd/gofix/testdata/reflect.encoder.go.in240
-rw-r--r--src/cmd/gofix/testdata/reflect.encoder.go.out240
-rw-r--r--src/cmd/gofix/testdata/reflect.export.go.in400
-rw-r--r--src/cmd/gofix/testdata/reflect.export.go.out400
-rw-r--r--src/cmd/gofix/testdata/reflect.print.go.in945
-rw-r--r--src/cmd/gofix/testdata/reflect.print.go.out945
-rw-r--r--src/cmd/gofix/testdata/reflect.quick.go.in364
-rw-r--r--src/cmd/gofix/testdata/reflect.quick.go.out365
-rw-r--r--src/cmd/gofix/testdata/reflect.read.go.in620
-rw-r--r--src/cmd/gofix/testdata/reflect.read.go.out620
-rw-r--r--src/cmd/gofix/testdata/reflect.scan.go.in1084
-rw-r--r--src/cmd/gofix/testdata/reflect.scan.go.out1084
-rw-r--r--src/cmd/gofix/testdata/reflect.script.go.in359
-rw-r--r--src/cmd/gofix/testdata/reflect.script.go.out359
-rw-r--r--src/cmd/gofix/testdata/reflect.template.go.in1043
-rw-r--r--src/cmd/gofix/testdata/reflect.template.go.out1044
-rw-r--r--src/cmd/gofix/testdata/reflect.type.go.in789
-rw-r--r--src/cmd/gofix/testdata/reflect.type.go.out789
-rw-r--r--src/cmd/gofix/typecheck.go579
-rw-r--r--src/cmd/gofmt/Makefile4
-rw-r--r--src/cmd/gofmt/doc.go16
-rw-r--r--src/cmd/gofmt/gofmt.go103
-rw-r--r--src/cmd/gofmt/gofmt_test.go81
-rw-r--r--src/cmd/gofmt/rewrite.go118
-rw-r--r--src/cmd/gofmt/testdata/rewrite1.golden8
-rw-r--r--src/cmd/gofmt/testdata/rewrite1.input8
-rwxr-xr-xsrc/cmd/gofmt/testdata/test.sh65
-rw-r--r--src/cmd/goinstall/Makefile15
-rw-r--r--src/cmd/goinstall/doc.go1
-rw-r--r--src/cmd/goinstall/main.go2
-rw-r--r--src/cmd/goinstall/make.go56
-rw-r--r--src/cmd/goinstall/parse.go69
-rw-r--r--src/cmd/goinstall/syslist_test.go61
-rw-r--r--src/cmd/gopack/ar.c177
-rw-r--r--src/cmd/gopack/doc.go5
-rw-r--r--src/cmd/gotest/Makefile16
-rw-r--r--src/cmd/gotest/doc.go92
-rw-r--r--src/cmd/gotest/flag.go155
-rwxr-xr-xsrc/cmd/gotest/gotest197
-rw-r--r--src/cmd/gotest/gotest.go415
-rw-r--r--src/cmd/gotry/Makefile18
-rwxr-xr-xsrc/cmd/gotry/gotry (renamed from src/cmd/gotest/gotry)0
-rw-r--r--src/cmd/gotype/Makefile17
-rw-r--r--src/cmd/gotype/doc.go59
-rw-r--r--src/cmd/gotype/gotype.go198
-rw-r--r--src/cmd/gotype/gotype_test.go52
-rw-r--r--src/cmd/gotype/testdata/test1.go6
-rw-r--r--src/cmd/govet/govet.go116
-rw-r--r--src/cmd/goyacc/goyacc.go122
-rw-r--r--src/cmd/goyacc/units.y32
-rw-r--r--src/cmd/ld/data.c31
-rw-r--r--src/cmd/ld/dwarf.c51
-rw-r--r--src/cmd/ld/elf.c38
-rw-r--r--src/cmd/ld/elf.h4
-rw-r--r--src/cmd/ld/go.c13
-rw-r--r--src/cmd/ld/ldmacho.c32
-rw-r--r--src/cmd/ld/lib.c70
-rw-r--r--src/cmd/ld/lib.h38
-rw-r--r--src/cmd/ld/pe.c8
-rw-r--r--src/cmd/ld/symtab.c30
-rw-r--r--src/cmd/nm/doc.go3
-rwxr-xr-xsrc/cmd/prof/gopprof23
-rw-r--r--src/lib9/Makefile2
-rw-r--r--src/libmach/Makefile2
-rw-r--r--src/libmach/darwin.c2
-rw-r--r--src/libmach/freebsd.c5
-rw-r--r--src/libmach/windows.c8
-rwxr-xr-xsrc/make.bash11
-rw-r--r--src/pkg/Makefile33
-rw-r--r--src/pkg/archive/tar/reader.go10
-rw-r--r--src/pkg/archive/tar/reader_test.go8
-rw-r--r--src/pkg/archive/tar/writer_test.go3
-rw-r--r--src/pkg/archive/zip/reader.go61
-rw-r--r--src/pkg/archive/zip/reader_test.go6
-rw-r--r--src/pkg/asn1/asn1.go102
-rw-r--r--src/pkg/asn1/asn1_test.go9
-rw-r--r--src/pkg/asn1/common.go22
-rw-r--r--src/pkg/asn1/marshal.go62
-rw-r--r--src/pkg/asn1/marshal_test.go24
-rwxr-xr-xsrc/pkg/big/int.go93
-rwxr-xr-xsrc/pkg/big/int_test.go50
-rwxr-xr-xsrc/pkg/big/nat.go47
-rw-r--r--src/pkg/bufio/bufio.go95
-rw-r--r--src/pkg/bufio/bufio_test.go134
-rw-r--r--src/pkg/bytes/buffer_test.go12
-rw-r--r--src/pkg/bytes/bytes.go18
-rw-r--r--src/pkg/bytes/bytes_test.go5
-rw-r--r--src/pkg/compress/lzw/reader_test.go23
-rw-r--r--src/pkg/compress/lzw/writer_test.go31
-rw-r--r--src/pkg/compress/zlib/writer_test.go4
-rw-r--r--src/pkg/container/vector/numbers_test.go9
-rw-r--r--src/pkg/crypto/block/cbc.go71
-rw-r--r--src/pkg/crypto/block/cfb.go96
-rw-r--r--src/pkg/crypto/block/cfb_aes_test.go311
-rw-r--r--src/pkg/crypto/block/cipher.go57
-rw-r--r--src/pkg/crypto/block/cmac.go105
-rw-r--r--src/pkg/crypto/block/cmac_aes_test.go130
-rw-r--r--src/pkg/crypto/block/ctr.go67
-rw-r--r--src/pkg/crypto/block/eax.go253
-rw-r--r--src/pkg/crypto/block/eax_aes_test.go140
-rw-r--r--src/pkg/crypto/block/ecb.go270
-rw-r--r--src/pkg/crypto/block/ecb_aes_test.go127
-rw-r--r--src/pkg/crypto/block/ecb_test.go181
-rw-r--r--src/pkg/crypto/block/ofb.go60
-rw-r--r--src/pkg/crypto/block/ofb_aes_test.go108
-rw-r--r--src/pkg/crypto/block/xor.go124
-rw-r--r--src/pkg/crypto/block/xor_test.go168
-rw-r--r--src/pkg/crypto/cipher/ctr.go4
-rw-r--r--src/pkg/crypto/des/Makefile (renamed from src/pkg/crypto/block/Makefile)14
-rw-r--r--src/pkg/crypto/des/block.go98
-rw-r--r--src/pkg/crypto/des/cipher.go103
-rw-r--r--src/pkg/crypto/des/const.go139
-rw-r--r--src/pkg/crypto/des/des_test.go1497
-rw-r--r--src/pkg/crypto/ecdsa/Makefile11
-rw-r--r--src/pkg/crypto/ecdsa/ecdsa.go149
-rw-r--r--src/pkg/crypto/ecdsa/ecdsa_test.go227
-rw-r--r--src/pkg/crypto/elliptic/elliptic.go5
-rw-r--r--src/pkg/crypto/elliptic/elliptic_test.go3
-rw-r--r--src/pkg/crypto/openpgp/armor/armor.go6
-rw-r--r--src/pkg/crypto/openpgp/armor/encode.go4
-rw-r--r--src/pkg/crypto/openpgp/packet/packet.go30
-rw-r--r--src/pkg/crypto/openpgp/packet/packet_test.go20
-rw-r--r--src/pkg/crypto/openpgp/packet/private_key.go31
-rw-r--r--src/pkg/crypto/openpgp/packet/public_key.go46
-rw-r--r--src/pkg/crypto/openpgp/packet/public_key_test.go12
-rw-r--r--src/pkg/crypto/openpgp/packet/signature.go108
-rw-r--r--src/pkg/crypto/openpgp/read_test.go32
-rw-r--r--src/pkg/crypto/openpgp/s2k/s2k_test.go3
-rw-r--r--src/pkg/crypto/openpgp/write.go6
-rw-r--r--src/pkg/crypto/openpgp/write_test.go16
-rw-r--r--src/pkg/crypto/rand/rand_test.go6
-rw-r--r--src/pkg/crypto/rand/rand_unix.go2
-rw-r--r--src/pkg/crypto/rsa/pkcs1v15.go10
-rw-r--r--src/pkg/crypto/rsa/pkcs1v15_test.go6
-rw-r--r--src/pkg/crypto/rsa/rsa.go167
-rw-r--r--src/pkg/crypto/rsa/rsa_test.go94
-rw-r--r--src/pkg/crypto/tls/common.go14
-rw-r--r--src/pkg/crypto/tls/conn.go14
-rw-r--r--src/pkg/crypto/tls/generate_cert.go10
-rw-r--r--src/pkg/crypto/tls/handshake_client.go33
-rw-r--r--src/pkg/crypto/tls/handshake_client_test.go2
-rw-r--r--src/pkg/crypto/tls/handshake_messages_test.go8
-rw-r--r--src/pkg/crypto/tls/tls.go19
-rw-r--r--src/pkg/crypto/x509/x509.go104
-rw-r--r--src/pkg/crypto/x509/x509_test.go7
-rw-r--r--src/pkg/debug/elf/file.go8
-rw-r--r--src/pkg/debug/gosym/Makefile6
-rw-r--r--src/pkg/debug/gosym/pclntab_test.go3
-rw-r--r--src/pkg/debug/macho/file.go2
-rw-r--r--src/pkg/debug/pe/file.go10
-rw-r--r--src/pkg/debug/proc/proc_darwin.go2
-rw-r--r--src/pkg/debug/proc/proc_freebsd.go2
-rw-r--r--src/pkg/debug/proc/proc_linux.go20
-rw-r--r--src/pkg/debug/proc/proc_windows.go2
-rw-r--r--src/pkg/ebnf/ebnf_test.go45
-rw-r--r--src/pkg/ebnf/parser.go8
-rw-r--r--src/pkg/encoding/binary/binary.go164
-rw-r--r--src/pkg/encoding/binary/binary_test.go2
-rw-r--r--src/pkg/exec/exec.go16
-rw-r--r--src/pkg/exec/exec_test.go52
-rw-r--r--src/pkg/exp/datafmt/datafmt.go42
-rw-r--r--src/pkg/exp/datafmt/parser.go10
-rw-r--r--src/pkg/exp/draw/x11/auth.go2
-rw-r--r--src/pkg/exp/draw/x11/conn.go6
-rw-r--r--src/pkg/exp/eval/bridge.go91
-rwxr-xr-xsrc/pkg/exp/eval/evalbin0 -> 3500057 bytes
-rw-r--r--src/pkg/exp/eval/eval_test.go8
-rw-r--r--src/pkg/exp/eval/stmt.go13
-rw-r--r--src/pkg/exp/eval/stmt_test.go2
-rw-r--r--src/pkg/exp/eval/type.go2
-rw-r--r--src/pkg/exp/ogle/cmd.go6
-rw-r--r--src/pkg/exp/ogle/process.go6
-rw-r--r--src/pkg/exp/ogle/rruntime.go14
-rw-r--r--src/pkg/expvar/expvar.go2
-rw-r--r--src/pkg/flag/flag.go49
-rw-r--r--src/pkg/flag/flag_test.go7
-rw-r--r--src/pkg/fmt/fmt_test.go14
-rw-r--r--src/pkg/fmt/format.go22
-rw-r--r--src/pkg/fmt/print.go85
-rw-r--r--src/pkg/fmt/scan.go97
-rw-r--r--src/pkg/fmt/scan_test.go27
-rw-r--r--src/pkg/go/ast/Makefile1
-rw-r--r--src/pkg/go/ast/ast.go58
-rw-r--r--src/pkg/go/ast/filter.go6
-rw-r--r--src/pkg/go/ast/print.go94
-rw-r--r--src/pkg/go/ast/print_test.go80
-rw-r--r--src/pkg/go/ast/resolve.go188
-rw-r--r--src/pkg/go/ast/scope.go268
-rw-r--r--src/pkg/go/ast/walk.go8
-rw-r--r--src/pkg/go/doc/comment.go2
-rw-r--r--src/pkg/go/doc/doc.go8
-rw-r--r--src/pkg/go/parser/interface.go7
-rw-r--r--src/pkg/go/parser/parser.go696
-rw-r--r--src/pkg/go/parser/parser_test.go3
-rw-r--r--src/pkg/go/printer/nodes.go114
-rw-r--r--src/pkg/go/printer/printer.go196
-rw-r--r--src/pkg/go/printer/printer_test.go37
-rw-r--r--src/pkg/go/printer/testdata/declarations.golden77
-rw-r--r--src/pkg/go/printer/testdata/declarations.input29
-rw-r--r--src/pkg/go/printer/testdata/expressions.golden34
-rw-r--r--src/pkg/go/printer/testdata/expressions.input20
-rw-r--r--src/pkg/go/printer/testdata/expressions.raw37
-rw-r--r--src/pkg/go/printer/testdata/slow.golden85
-rw-r--r--src/pkg/go/printer/testdata/slow.input85
-rw-r--r--src/pkg/go/scanner/scanner.go26
-rw-r--r--src/pkg/go/scanner/scanner_test.go60
-rw-r--r--src/pkg/go/token/token.go15
-rw-r--r--src/pkg/go/typechecker/Makefile1
-rw-r--r--src/pkg/go/typechecker/scope.go72
-rw-r--r--src/pkg/go/typechecker/testdata/test0.src (renamed from src/pkg/go/typechecker/testdata/test0.go)0
-rw-r--r--src/pkg/go/typechecker/testdata/test1.src (renamed from src/pkg/go/typechecker/testdata/test1.go)2
-rw-r--r--src/pkg/go/typechecker/testdata/test3.src (renamed from src/pkg/go/typechecker/testdata/test3.go)7
-rw-r--r--src/pkg/go/typechecker/testdata/test4.src (renamed from src/pkg/go/typechecker/testdata/test4.go)2
-rw-r--r--src/pkg/go/typechecker/type.go125
-rw-r--r--src/pkg/go/typechecker/typechecker.go82
-rw-r--r--src/pkg/go/typechecker/typechecker_test.go4
-rw-r--r--src/pkg/go/typechecker/universe.go6
-rw-r--r--src/pkg/go/types/Makefile15
-rw-r--r--src/pkg/go/types/const.go347
-rw-r--r--src/pkg/go/types/exportdata.go135
-rw-r--r--src/pkg/go/types/gcimporter.go786
-rw-r--r--src/pkg/go/types/gcimporter_test.go111
-rw-r--r--src/pkg/go/types/testdata/exports.go89
-rw-r--r--src/pkg/go/types/types.go122
-rw-r--r--src/pkg/go/types/universe.go113
-rw-r--r--src/pkg/gob/codec_test.go57
-rw-r--r--src/pkg/gob/decode.go205
-rw-r--r--src/pkg/gob/decoder.go11
-rw-r--r--src/pkg/gob/dump.go2
-rw-r--r--src/pkg/gob/encode.go148
-rw-r--r--src/pkg/gob/encoder.go35
-rw-r--r--src/pkg/gob/encoder_test.go2
-rw-r--r--src/pkg/gob/gobencdec_test.go61
-rw-r--r--src/pkg/gob/timing_test.go90
-rw-r--r--src/pkg/gob/type.go179
-rw-r--r--src/pkg/hash/fnv/Makefile11
-rw-r--r--src/pkg/hash/fnv/fnv.go133
-rw-r--r--src/pkg/hash/fnv/fnv_test.go167
-rw-r--r--src/pkg/html/parse_test.go2
-rw-r--r--src/pkg/http/cgi/Makefile3
-rw-r--r--src/pkg/http/cgi/child.go192
-rw-r--r--src/pkg/http/cgi/child_test.go83
-rw-r--r--src/pkg/http/cgi/host.go (renamed from src/pkg/http/cgi/cgi.go)54
-rw-r--r--src/pkg/http/cgi/host_test.go (renamed from src/pkg/http/cgi/cgi_test.go)36
-rw-r--r--src/pkg/http/cgi/matryoshka_test.go74
-rwxr-xr-xsrc/pkg/http/cgi/testdata/test.cgi6
-rw-r--r--src/pkg/http/client.go79
-rw-r--r--src/pkg/http/client_test.go23
-rw-r--r--src/pkg/http/cookie.go230
-rw-r--r--src/pkg/http/cookie_test.go30
-rw-r--r--src/pkg/http/dump.go4
-rw-r--r--src/pkg/http/export_test.go34
-rw-r--r--src/pkg/http/fs.go49
-rw-r--r--src/pkg/http/fs_test.go24
-rw-r--r--src/pkg/http/httptest/recorder.go44
-rw-r--r--src/pkg/http/httptest/server.go106
-rw-r--r--src/pkg/http/persist.go24
-rw-r--r--src/pkg/http/pprof/pprof.go36
-rw-r--r--src/pkg/http/proxy_test.go30
-rw-r--r--src/pkg/http/request.go17
-rw-r--r--src/pkg/http/request_test.go37
-rw-r--r--src/pkg/http/requestwrite_test.go5
-rw-r--r--src/pkg/http/response.go8
-rw-r--r--src/pkg/http/response_test.go28
-rw-r--r--src/pkg/http/responsewrite_test.go32
-rw-r--r--src/pkg/http/serve_test.go273
-rw-r--r--src/pkg/http/server.go189
-rw-r--r--src/pkg/http/transfer.go11
-rw-r--r--src/pkg/http/transport.go601
-rw-r--r--src/pkg/http/transport_test.go450
-rw-r--r--src/pkg/http/triv.go7
-rw-r--r--src/pkg/http/url.go4
-rw-r--r--src/pkg/http/url_test.go4
-rw-r--r--src/pkg/image/decode_test.go2
-rw-r--r--src/pkg/image/format.go22
-rw-r--r--src/pkg/image/png/reader_test.go16
-rw-r--r--src/pkg/image/png/writer_test.go6
-rw-r--r--src/pkg/image/ycbcr/Makefile11
-rw-r--r--src/pkg/image/ycbcr/ycbcr.go174
-rw-r--r--src/pkg/image/ycbcr/ycbcr_test.go33
-rw-r--r--src/pkg/io/io.go24
-rw-r--r--src/pkg/io/io_test.go33
-rw-r--r--src/pkg/io/ioutil/ioutil.go44
-rw-r--r--src/pkg/io/ioutil/tempfile.go2
-rw-r--r--src/pkg/io/multi.go6
-rw-r--r--src/pkg/io/pipe.go307
-rw-r--r--src/pkg/json/decode.go129
-rw-r--r--src/pkg/json/decode_test.go7
-rw-r--r--src/pkg/json/encode.go46
-rw-r--r--src/pkg/json/scanner_test.go25
-rw-r--r--src/pkg/log/log.go101
-rw-r--r--src/pkg/log/log_test.go33
-rw-r--r--src/pkg/mime/type.go2
-rw-r--r--src/pkg/net/Makefile6
-rw-r--r--src/pkg/net/cgo_stub.go21
-rw-r--r--src/pkg/net/dial.go79
-rw-r--r--src/pkg/net/dialgoogle_test.go13
-rw-r--r--src/pkg/net/dnsclient.go109
-rw-r--r--src/pkg/net/dnsmsg.go145
-rw-r--r--src/pkg/net/fd.go112
-rw-r--r--src/pkg/net/fd_darwin.go34
-rw-r--r--src/pkg/net/fd_freebsd.go34
-rw-r--r--src/pkg/net/fd_linux.go88
-rw-r--r--src/pkg/net/fd_windows.go27
-rw-r--r--src/pkg/net/file.go119
-rw-r--r--src/pkg/net/file_test.go131
-rw-r--r--src/pkg/net/file_windows.go25
-rw-r--r--src/pkg/net/hosts.go4
-rw-r--r--src/pkg/net/hosts_test.go1
-rw-r--r--src/pkg/net/ip.go122
-rw-r--r--src/pkg/net/ip_test.go90
-rw-r--r--src/pkg/net/ipraw_test.go7
-rw-r--r--src/pkg/net/iprawsock.go2
-rw-r--r--src/pkg/net/ipsock.go17
-rw-r--r--src/pkg/net/lookup.go38
-rw-r--r--src/pkg/net/multicast_test.go11
-rw-r--r--src/pkg/net/net_test.go23
-rw-r--r--src/pkg/net/newpollserver.go2
-rw-r--r--src/pkg/net/parse.go2
-rw-r--r--src/pkg/net/parse_test.go2
-rw-r--r--src/pkg/net/port.go4
-rw-r--r--src/pkg/net/port_test.go2
-rw-r--r--src/pkg/net/resolv_windows.go41
-rw-r--r--src/pkg/net/server_test.go10
-rw-r--r--src/pkg/net/sock.go22
-rw-r--r--src/pkg/net/tcpsock.go7
-rw-r--r--src/pkg/net/textproto/textproto.go2
-rw-r--r--src/pkg/net/timeout_test.go2
-rw-r--r--src/pkg/net/udpsock.go2
-rw-r--r--src/pkg/netchan/common.go6
-rw-r--r--src/pkg/netchan/export.go24
-rw-r--r--src/pkg/netchan/import.go8
-rw-r--r--src/pkg/netchan/netchan_test.go18
-rw-r--r--src/pkg/os/Makefile25
-rw-r--r--src/pkg/os/dir_freebsd.go66
-rw-r--r--src/pkg/os/dir_linux.go69
-rw-r--r--src/pkg/os/dir_plan9.go284
-rw-r--r--src/pkg/os/dir_unix.go (renamed from src/pkg/os/dir_darwin.go)35
-rw-r--r--src/pkg/os/env_plan9.go91
-rw-r--r--src/pkg/os/error.go81
-rw-r--r--src/pkg/os/error_plan9.go60
-rw-r--r--src/pkg/os/error_posix.go90
-rw-r--r--src/pkg/os/exec.go133
-rw-r--r--src/pkg/os/exec_plan9.go114
-rw-r--r--src/pkg/os/exec_posix.go127
-rw-r--r--src/pkg/os/file.go277
-rw-r--r--src/pkg/os/file_plan9.go240
-rw-r--r--src/pkg/os/file_posix.go246
-rw-r--r--src/pkg/os/file_unix.go26
-rw-r--r--src/pkg/os/file_windows.go10
-rw-r--r--src/pkg/os/getwd.go2
-rw-r--r--src/pkg/os/inotify/inotify_linux.go2
-rw-r--r--src/pkg/os/inotify/inotify_linux_test.go26
-rw-r--r--src/pkg/os/os_test.go98
-rw-r--r--src/pkg/os/path.go4
-rw-r--r--src/pkg/os/path_test.go27
-rw-r--r--src/pkg/os/proc.go4
-rw-r--r--src/pkg/os/stat_darwin.go8
-rw-r--r--src/pkg/os/stat_freebsd.go8
-rw-r--r--src/pkg/os/stat_linux.go8
-rw-r--r--src/pkg/os/stat_plan9.go84
-rw-r--r--src/pkg/os/sys_linux.go2
-rw-r--r--src/pkg/os/sys_plan9.go27
-rw-r--r--src/pkg/os/time.go4
-rw-r--r--src/pkg/path/filepath/Makefile5
-rw-r--r--src/pkg/path/filepath/match.go55
-rw-r--r--src/pkg/path/filepath/match_test.go47
-rw-r--r--src/pkg/path/filepath/path.go128
-rw-r--r--src/pkg/path/filepath/path_plan9.go28
-rw-r--r--src/pkg/path/filepath/path_test.go147
-rw-r--r--src/pkg/path/filepath/path_unix.go18
-rw-r--r--src/pkg/path/filepath/path_windows.go37
-rw-r--r--src/pkg/reflect/all_test.go412
-rw-r--r--src/pkg/reflect/deepequal.go58
-rw-r--r--src/pkg/reflect/tostring_test.go54
-rw-r--r--src/pkg/reflect/type.go757
-rw-r--r--src/pkg/reflect/value.go2129
-rw-r--r--src/pkg/rpc/client.go45
-rw-r--r--src/pkg/rpc/jsonrpc/client.go2
-rw-r--r--src/pkg/rpc/server.go139
-rw-r--r--src/pkg/rpc/server_test.go62
-rw-r--r--src/pkg/runtime/386/asm.s253
-rw-r--r--src/pkg/runtime/Makefile2
-rw-r--r--src/pkg/runtime/amd64/asm.s194
-rw-r--r--src/pkg/runtime/amd64/traceback.c9
-rw-r--r--src/pkg/runtime/arm/asm.s65
-rw-r--r--src/pkg/runtime/arm/traceback.c9
-rw-r--r--src/pkg/runtime/cgocall.c219
-rw-r--r--src/pkg/runtime/cgocall.h2
-rw-r--r--src/pkg/runtime/chan.c346
-rw-r--r--src/pkg/runtime/cpuprof.c421
-rw-r--r--src/pkg/runtime/darwin/386/defs.h29
-rw-r--r--src/pkg/runtime/darwin/386/signal.c57
-rw-r--r--src/pkg/runtime/darwin/386/sys.s5
-rw-r--r--src/pkg/runtime/darwin/amd64/defs.h34
-rw-r--r--src/pkg/runtime/darwin/amd64/signal.c57
-rw-r--r--src/pkg/runtime/darwin/amd64/sys.s13
-rw-r--r--src/pkg/runtime/darwin/defs.c6
-rw-r--r--src/pkg/runtime/darwin/os.h4
-rw-r--r--src/pkg/runtime/debug.go84
-rw-r--r--src/pkg/runtime/freebsd/386/defs.h21
-rw-r--r--src/pkg/runtime/freebsd/386/signal.c61
-rw-r--r--src/pkg/runtime/freebsd/386/sys.s5
-rw-r--r--src/pkg/runtime/freebsd/amd64/defs.h23
-rw-r--r--src/pkg/runtime/freebsd/amd64/signal.c61
-rw-r--r--src/pkg/runtime/freebsd/amd64/sys.s8
-rw-r--r--src/pkg/runtime/freebsd/defs.c7
-rw-r--r--src/pkg/runtime/freebsd/mem.c2
-rw-r--r--src/pkg/runtime/freebsd/os.h5
-rw-r--r--src/pkg/runtime/linux/386/defs.h12
-rw-r--r--src/pkg/runtime/linux/386/signal.c58
-rw-r--r--src/pkg/runtime/linux/386/sys.s9
-rw-r--r--src/pkg/runtime/linux/amd64/defs.h13
-rw-r--r--src/pkg/runtime/linux/amd64/signal.c58
-rw-r--r--src/pkg/runtime/linux/amd64/sys.s8
-rw-r--r--src/pkg/runtime/linux/arm/defs.h42
-rw-r--r--src/pkg/runtime/linux/arm/signal.c59
-rw-r--r--src/pkg/runtime/linux/arm/sys.s9
-rw-r--r--src/pkg/runtime/linux/defs.c7
-rw-r--r--src/pkg/runtime/linux/defs2.c8
-rw-r--r--src/pkg/runtime/linux/defs_arm.c31
-rw-r--r--src/pkg/runtime/linux/mem.c2
-rw-r--r--src/pkg/runtime/linux/os.h4
-rw-r--r--src/pkg/runtime/linux/signals.h2
-rw-r--r--src/pkg/runtime/mem.go69
-rw-r--r--src/pkg/runtime/mgc0.c5
-rw-r--r--src/pkg/runtime/plan9/386/signal.c8
-rw-r--r--src/pkg/runtime/pprof/pprof.go69
-rw-r--r--src/pkg/runtime/pprof/pprof_test.go77
-rw-r--r--src/pkg/runtime/proc.c367
-rw-r--r--src/pkg/runtime/reflect.goc12
-rw-r--r--src/pkg/runtime/runtime-gdb.py14
-rw-r--r--src/pkg/runtime/runtime.h17
-rw-r--r--src/pkg/runtime/windows/386/signal.c8
-rw-r--r--src/pkg/runtime/windows/386/sys.s2
-rw-r--r--src/pkg/scanner/scanner.go45
-rw-r--r--src/pkg/scanner/scanner_test.go68
-rw-r--r--src/pkg/smtp/smtp.go2
-rw-r--r--src/pkg/sort/sort_test.go9
-rw-r--r--src/pkg/strconv/fp_test.go2
-rw-r--r--src/pkg/strings/strings.go51
-rw-r--r--src/pkg/strings/strings_test.go84
-rw-r--r--src/pkg/sync/atomic/asm_arm.s41
-rw-r--r--src/pkg/sync/atomic/atomic_test.go55
-rw-r--r--src/pkg/sync/atomic/doc.go5
-rw-r--r--src/pkg/sync/rwmutex_test.go24
-rw-r--r--src/pkg/sync/waitgroup.go2
-rw-r--r--src/pkg/syscall/Makefile3
-rw-r--r--src/pkg/syscall/asm_darwin_386.s54
-rw-r--r--src/pkg/syscall/asm_darwin_amd64.s21
-rw-r--r--src/pkg/syscall/asm_freebsd_386.s54
-rw-r--r--src/pkg/syscall/asm_freebsd_amd64.s20
-rw-r--r--src/pkg/syscall/asm_linux_386.s47
-rw-r--r--src/pkg/syscall/asm_linux_amd64.s22
-rw-r--r--src/pkg/syscall/asm_linux_arm.s28
-rw-r--r--src/pkg/syscall/asm_plan9_386.s151
-rw-r--r--src/pkg/syscall/exec_plan9.go521
-rw-r--r--src/pkg/syscall/exec_unix.go102
-rw-r--r--src/pkg/syscall/exec_windows.go338
-rwxr-xr-xsrc/pkg/syscall/mkall.sh50
-rwxr-xr-xsrc/pkg/syscall/mkerrors.sh12
-rwxr-xr-xsrc/pkg/syscall/mksyscall.pl (renamed from src/pkg/syscall/mksyscall.sh)45
-rwxr-xr-xsrc/pkg/syscall/mksyscall_windows.pl (renamed from src/pkg/syscall/mksyscall_windows.sh)16
-rwxr-xr-xsrc/pkg/syscall/mksysnum_darwin.pl (renamed from src/pkg/syscall/mksysnum_darwin.sh)2
-rwxr-xr-xsrc/pkg/syscall/mksysnum_freebsd.pl (renamed from src/pkg/syscall/mksysnum_freebsd.sh)2
-rwxr-xr-xsrc/pkg/syscall/mksysnum_linux.pl (renamed from src/pkg/syscall/mksysnum_linux.sh)2
-rwxr-xr-xsrc/pkg/syscall/mksysnum_plan9.sh25
-rw-r--r--src/pkg/syscall/str.go4
-rw-r--r--src/pkg/syscall/syscall.go65
-rw-r--r--src/pkg/syscall/syscall_bsd.go47
-rw-r--r--src/pkg/syscall/syscall_darwin.go76
-rw-r--r--src/pkg/syscall/syscall_darwin_386.go4
-rw-r--r--src/pkg/syscall/syscall_darwin_amd64.go2
-rw-r--r--src/pkg/syscall/syscall_freebsd.go78
-rw-r--r--src/pkg/syscall/syscall_freebsd_386.go2
-rw-r--r--src/pkg/syscall/syscall_linux.go123
-rw-r--r--src/pkg/syscall/syscall_linux_386.go50
-rw-r--r--src/pkg/syscall/syscall_linux_amd64.go32
-rw-r--r--src/pkg/syscall/syscall_linux_arm.go45
-rw-r--r--src/pkg/syscall/syscall_plan9.go343
-rw-r--r--src/pkg/syscall/syscall_plan9_386.go5
-rw-r--r--src/pkg/syscall/syscall_unix.go3
-rw-r--r--src/pkg/syscall/syscall_windows.go7
-rw-r--r--src/pkg/syscall/types_freebsd.c21
-rw-r--r--src/pkg/syscall/types_plan9.c115
-rw-r--r--src/pkg/syscall/zerrors_darwin_386.go29
-rw-r--r--src/pkg/syscall/zerrors_darwin_amd64.go29
-rw-r--r--src/pkg/syscall/zerrors_freebsd_386.go1806
-rw-r--r--src/pkg/syscall/zerrors_freebsd_amd64.go1806
-rw-r--r--src/pkg/syscall/zerrors_linux_386.go52
-rw-r--r--src/pkg/syscall/zerrors_linux_amd64.go52
-rw-r--r--src/pkg/syscall/zerrors_linux_arm.go32
-rw-r--r--src/pkg/syscall/zerrors_plan9_386.go25
-rw-r--r--src/pkg/syscall/zsyscall_darwin_386.go89
-rw-r--r--src/pkg/syscall/zsyscall_darwin_amd64.go89
-rw-r--r--src/pkg/syscall/zsyscall_freebsd_386.go89
-rw-r--r--src/pkg/syscall/zsyscall_freebsd_amd64.go89
-rw-r--r--src/pkg/syscall/zsyscall_linux_386.go121
-rw-r--r--src/pkg/syscall/zsyscall_linux_amd64.go133
-rw-r--r--src/pkg/syscall/zsyscall_linux_arm.go137
-rw-r--r--src/pkg/syscall/zsyscall_plan9_386.go267
-rw-r--r--src/pkg/syscall/zsyscall_windows_386.go20
-rw-r--r--src/pkg/syscall/zsysnum_darwin_amd64.go2
-rw-r--r--src/pkg/syscall/zsysnum_linux_386.go2
-rw-r--r--src/pkg/syscall/zsysnum_linux_amd64.go2
-rw-r--r--src/pkg/syscall/zsysnum_plan9_386.go47
-rw-r--r--src/pkg/syscall/ztypes_darwin_amd64.go2
-rw-r--r--src/pkg/syscall/ztypes_freebsd_386.go50
-rw-r--r--src/pkg/syscall/ztypes_freebsd_amd64.go51
-rw-r--r--src/pkg/syscall/ztypes_plan9_386.go74
-rw-r--r--src/pkg/syscall/ztypes_windows_386.go4
-rw-r--r--src/pkg/syslog/Makefile1
-rw-r--r--src/pkg/syslog/syslog.go54
-rw-r--r--src/pkg/syslog/syslog_unix.go31
-rw-r--r--src/pkg/template/template.go90
-rw-r--r--src/pkg/testing/quick/quick.go114
-rw-r--r--src/pkg/testing/script/script.go16
-rw-r--r--src/pkg/testing/testing.go99
-rw-r--r--src/pkg/time/sleep.go5
-rw-r--r--src/pkg/time/sleep_test.go23
-rw-r--r--src/pkg/time/sys.go14
-rw-r--r--src/pkg/time/time.go2
-rw-r--r--src/pkg/time/time_test.go12
-rw-r--r--src/pkg/try/try.go8
-rw-r--r--src/pkg/utf8/string_test.go11
-rw-r--r--src/pkg/websocket/client.go4
-rw-r--r--src/pkg/websocket/server.go4
-rw-r--r--src/pkg/websocket/websocket_test.go10
-rw-r--r--src/pkg/xml/read.go116
-rw-r--r--src/pkg/xml/read_test.go4
-rw-r--r--src/pkg/xml/xml.go1
-rwxr-xr-xsrc/quietgcc.bash7
-rwxr-xr-xsrc/run.bash23
-rwxr-xr-xsrc/version.bash6
-rw-r--r--test/bench/regex-dna-parallel.go2
-rw-r--r--test/bench/regex-dna.go2
-rw-r--r--test/bugs/bug322.dir/main.go10
-rw-r--r--test/bugs/bug324.dir/main.go2
-rw-r--r--test/bugs/bug324.go2
-rw-r--r--test/chan/perm.go15
-rw-r--r--test/chan/select3.go20
-rw-r--r--test/closedchan.go157
-rw-r--r--test/cmp6.go10
-rw-r--r--test/ddd1.go3
-rw-r--r--test/env.go9
-rw-r--r--test/fixedbugs/bug016.go2
-rw-r--r--test/fixedbugs/bug055.go29
-rw-r--r--test/fixedbugs/bug069.go21
-rw-r--r--test/fixedbugs/bug076.go10
-rw-r--r--test/fixedbugs/bug077.go5
-rw-r--r--test/fixedbugs/bug081.go2
-rw-r--r--test/fixedbugs/bug091.go11
-rw-r--r--test/fixedbugs/bug137.go19
-rw-r--r--test/fixedbugs/bug140.go13
-rw-r--r--test/fixedbugs/bug177.go25
-rw-r--r--test/fixedbugs/bug178.go18
-rw-r--r--test/fixedbugs/bug179.go14
-rw-r--r--test/fixedbugs/bug196.go7
-rw-r--r--test/fixedbugs/bug234.go26
-rw-r--r--test/fixedbugs/bug242.go8
-rw-r--r--test/fixedbugs/bug243.go37
-rw-r--r--test/fixedbugs/bug252.go4
-rw-r--r--test/fixedbugs/bug274.go3
-rw-r--r--test/fixedbugs/bug323.go6
-rw-r--r--test/fixedbugs/bug325.go1
-rw-r--r--test/fixedbugs/bug326.go41
-rw-r--r--test/fixedbugs/bug327.go24
-rw-r--r--test/func6.go14
-rw-r--r--test/gc2.go41
-rw-r--r--test/golden.out7
-rw-r--r--test/init.go4
-rw-r--r--test/interface/fake.go24
-rw-r--r--test/interface/private.go32
-rw-r--r--test/interface/private1.go18
-rw-r--r--test/ken/cplx3.go6
-rw-r--r--test/label.go60
-rw-r--r--test/label1.go85
-rw-r--r--test/named1.go9
-rwxr-xr-xtest/run3
-rw-r--r--test/syntax/chan.go4
-rw-r--r--test/syntax/if.go3
735 files changed, 51942 insertions, 13541 deletions
diff --git a/AUTHORS b/AUTHORS
index cbac729b2..33f30135d 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -12,6 +12,7 @@ Abhinav Gupta <abhinav.g90@gmail.com>
Adrian O'Grady <elpollouk@gmail.com>
Albert Strasheim <fullung@gmail.com>
Alex Brainman <alex.brainman@gmail.com>
+Alexey Borzenkov <snaury@gmail.com>
Amrut Joshi <amrut.joshi@gmail.com>
Andrei Vieru <euvieru@gmail.com>
Andrew Skiba <skibaa@gmail.com>
@@ -40,10 +41,12 @@ Dan Sinclair <dan.sinclair@gmail.com>
Daniel Fleischman <danielfleischman@gmail.com>
Daniel Theophanes <kardianos@gmail.com>
Dave Cheney <dave@cheney.net>
+David Forsythe <dforsythe@gmail.com>
David G. Andersen <dave.andersen@gmail.com>
David Titarenco <david.titarenco@gmail.com>
Dean Prichard <dean.prichard@gmail.com>
Devon H. O'Dell <devon.odell@gmail.com>
+Dmitry Chestnykh <dchest@gmail.com>
Eden Li <eden.li@gmail.com>
Eoghan Sherry <ejsherry@gmail.com>
Eric Clark <zerohp@gmail.com>
@@ -81,6 +84,8 @@ Kevin Ballard <kevin@sb.org>
Kyle Consalus <consalus@gmail.com>
Kyle Lemons <kyle@kylelemons.net>
Lorenzo Stoakes <lstoakes@gmail.com>
+Lucio De Re <lucio.dere@gmail.com>
+Luit van Drongelen <luitvd@gmail.com>
Markus Duft <markus.duft@salomon.at>
Martin Neubauer <m.ne@gmx.net>
Mathieu Lonjaret <mathieu.lonjaret@gmail.com>
@@ -108,6 +113,7 @@ Pieter Droogendijk <pieter@binky.org.uk>
Raif S. Naffah <go@naffah-raif.name>
Risto Jaakko Saarelma <rsaarelm@gmail.com>
Robert Hencke <robert.hencke@gmail.com>
+Roger Pau Monné <royger@gmail.com>
Roger Peppe <rogpeppe@gmail.com>
Ross Light <rlight2@gmail.com>
Ryan Hitchman <hitchmanr@gmail.com>
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 913116e1e..57a5f66c7 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -36,6 +36,7 @@ Adam Langley <agl@golang.org>
Adrian O'Grady <elpollouk@gmail.com>
Albert Strasheim <fullung@gmail.com>
Alex Brainman <alex.brainman@gmail.com>
+Alexey Borzenkov <snaury@gmail.com>
Amrut Joshi <amrut.joshi@gmail.com>
Andrei Vieru <euvieru@gmail.com>
Andrew Gerrand <adg@golang.org>
@@ -76,11 +77,13 @@ Daniel Nadasi <dnadasi@google.com>
Daniel Theophanes <kardianos@gmail.com>
Dave Cheney <dave@cheney.net>
David Anderson <danderson@google.com>
+David Forsythe <dforsythe@gmail.com>
David G. Andersen <dave.andersen@gmail.com>
David Symonds <dsymonds@golang.org>
David Titarenco <david.titarenco@gmail.com>
Dean Prichard <dean.prichard@gmail.com>
Devon H. O'Dell <devon.odell@gmail.com>
+Dmitry Chestnykh <dchest@gmail.com>
Eden Li <eden.li@gmail.com>
Eoghan Sherry <ejsherry@gmail.com>
Eric Clark <zerohp@gmail.com>
@@ -111,6 +114,7 @@ Jan Mercl <befelemepeseveze@gmail.com>
Jeff R. Allen <jra@nella.org> <jeff.allen@gmail.com>
Jim McGrath <jimmc2@gmail.com>
Joe Poirier <jdpoirier@gmail.com>
+John DeNero <denero@google.com>
Jonathan Wills <runningwild@gmail.com>
Jos Visser <josv@google.com>
Jose Luis Vázquez González <josvazg@gmail.com>
@@ -127,11 +131,14 @@ Kyle Consalus <consalus@gmail.com>
Kyle Lemons <kyle@kylelemons.net> <etherealflaim@gmail.com>
Larry Hosken <lahosken@golang.org>
Lorenzo Stoakes <lstoakes@gmail.com>
+Lucio De Re <lucio.dere@gmail.com>
+Luit van Drongelen <luitvd@gmail.com>
Luuk van Dijk <lvd@golang.org> <lvd@google.com>
Mark Zavislak <zavislak@google.com>
Markus Duft <markus.duft@salomon.at>
Martin Neubauer <m.ne@gmx.net>
Mathieu Lonjaret <mathieu.lonjaret@gmail.com>
+Matt Jones <mrjones@google.com>
Maxim Ushakov <ushakov@google.com>
Micah Stetson <micah.stetson@gmail.com>
Michael Elkins <michael.elkins@gmail.com>
@@ -163,11 +170,13 @@ Risto Jaakko Saarelma <rsaarelm@gmail.com>
Rob Pike <r@golang.org>
Robert Griesemer <gri@golang.org>
Robert Hencke <robert.hencke@gmail.com>
+Roger Pau Monné <royger@gmail.com>
Roger Peppe <rogpeppe@gmail.com>
Ross Light <rlight2@gmail.com>
Russ Cox <rsc@golang.org>
Ryan Hitchman <hitchmanr@gmail.com>
Sam Thorogood <thorogood@google.com> <sam.thorogood@gmail.com>
+Sameer Ajmani <ajmani@gmail.com>
Scott Lawrence <bytbox@gmail.com>
Scott Schwartz <scotts@golang.org>
Sebastien Binet <seb.binet@gmail.com>
diff --git a/doc/codelab/wiki/Makefile b/doc/codelab/wiki/Makefile
index 43f05b21d..09c3291a0 100644
--- a/doc/codelab/wiki/Makefile
+++ b/doc/codelab/wiki/Makefile
@@ -8,7 +8,7 @@ all: index.html
include ../../../src/Make.common
-CLEANFILES+=index.html srcextract.bin htmlify.bin
+CLEANFILES+=index.html srcextract.bin htmlify.bin get.bin
index.html: srcextract.bin htmlify.bin
PATH=.:$$PATH awk '/^!/{system(substr($$0,2)); next} {print}' < wiki.html | tr -d '\r' > index.html
diff --git a/doc/codelab/wiki/final-noclosure.go b/doc/codelab/wiki/final-noclosure.go
index 99121f298..d09a0d7ab 100644
--- a/doc/codelab/wiki/final-noclosure.go
+++ b/doc/codelab/wiki/final-noclosure.go
@@ -73,7 +73,7 @@ func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
http.Error(w, err.String(), http.StatusInternalServerError)
return
}
- err = t.Execute(p, w)
+ err = t.Execute(w, p)
if err != nil {
http.Error(w, err.String(), http.StatusInternalServerError)
}
diff --git a/doc/codelab/wiki/final-noerror.go b/doc/codelab/wiki/final-noerror.go
index 0f18912d2..5fcf1de76 100644
--- a/doc/codelab/wiki/final-noerror.go
+++ b/doc/codelab/wiki/final-noerror.go
@@ -35,14 +35,14 @@ func editHandler(w http.ResponseWriter, r *http.Request) {
p = &Page{Title: title}
}
t, _ := template.ParseFile("edit.html", nil)
- t.Execute(p, w)
+ t.Execute(w, p)
}
func viewHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[lenPath:]
p, _ := loadPage(title)
t, _ := template.ParseFile("view.html", nil)
- t.Execute(p, w)
+ t.Execute(w, p)
}
func main() {
diff --git a/doc/codelab/wiki/final-parsetemplate.go b/doc/codelab/wiki/final-parsetemplate.go
index ea8977601..f25012eed 100644
--- a/doc/codelab/wiki/final-parsetemplate.go
+++ b/doc/codelab/wiki/final-parsetemplate.go
@@ -61,7 +61,7 @@ func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
http.Error(w, err.String(), http.StatusInternalServerError)
return
}
- err = t.Execute(p, w)
+ err = t.Execute(w, p)
if err != nil {
http.Error(w, err.String(), http.StatusInternalServerError)
}
diff --git a/doc/codelab/wiki/final-template.go b/doc/codelab/wiki/final-template.go
index 4d6a2cfab..aab536ee1 100644
--- a/doc/codelab/wiki/final-template.go
+++ b/doc/codelab/wiki/final-template.go
@@ -53,7 +53,7 @@ func saveHandler(w http.ResponseWriter, r *http.Request) {
func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
t, _ := template.ParseFile(tmpl+".html", nil)
- t.Execute(p, w)
+ t.Execute(w, p)
}
func main() {
diff --git a/doc/codelab/wiki/index.html b/doc/codelab/wiki/index.html
index fc8c27bfa..d059fa027 100644
--- a/doc/codelab/wiki/index.html
+++ b/doc/codelab/wiki/index.html
@@ -475,7 +475,7 @@ func editHandler(w http.ResponseWriter, r *http.Request) {
p = &amp;Page{Title: title}
}
t, _ := template.ParseFile(&#34;edit.html&#34;, nil)
- t.Execute(p, w)
+ t.Execute(w, p)
}
</pre>
@@ -527,7 +527,7 @@ func viewHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[lenPath:]
p, _ := loadPage(title)
t, _ := template.ParseFile(&#34;view.html&#34;, nil)
- t.Execute(p, w)
+ t.Execute(w, p)
}
</pre>
@@ -555,7 +555,7 @@ func editHandler(w http.ResponseWriter, r *http.Request) {
func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
t, _ := template.ParseFile(tmpl+&#34;.html&#34;, nil)
- t.Execute(p, w)
+ t.Execute(w, p)
}
</pre>
@@ -644,7 +644,7 @@ func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
http.Error(w, err.String(), http.StatusInternalServerError)
return
}
- err = t.Execute(p, w)
+ err = t.Execute(w, p)
if err != nil {
http.Error(w, err.String(), http.StatusInternalServerError)
}
diff --git a/doc/codewalk/functions.xml b/doc/codewalk/functions.xml
new file mode 100644
index 000000000..986a017e1
--- /dev/null
+++ b/doc/codewalk/functions.xml
@@ -0,0 +1,115 @@
+<codewalk title="First-Class Functions in Go">
+
+<step title="Introduction" src="doc/codewalk/pig.go">
+ Go supports first class functions, higher-order functions, user-defined
+ function types, function literals, closures, and multiple return values.
+ <br/><br/>
+
+ This rich feature set supports a functional programming style in a strongly
+ typed language.
+ <br/><br/>
+
+ In this codewalk we will look at a simple program that simulates a dice game
+ called <a href="http://en.wikipedia.org/wiki/Pig_(dice)">Pig</a> and evaluates
+ basic strategies.
+</step>
+
+<step title="Game overview" src="doc/codewalk/pig.go:/\/\/ A score/,/thisTurn int\n}/">
+ Pig is a two-player game played with a 6-sided die. Each turn, you may roll or stay.
+ <ul>
+ <li> If you roll a 1, you lose all points for your turn and play passes to
+ your opponent. Any other roll adds its value to your turn score. </li>
+ <li> If you stay, your turn score is added to your total score, and play passes
+ to your opponent. </li>
+ </ul>
+
+ The first person to reach 100 total points wins.
+ <br/><br/>
+
+ The <code>score</code> type stores the scores of the current and opposing
+ players, in addition to the points accumulated during the current turn.
+</step>
+
+<step title="User-defined function types" src="doc/codewalk/pig.go:/\/\/ An action/,/bool\)/">
+ In Go, functions can be passed around just like any other value. A function's
+ type signature describes the types of its arguments and return values.
+ <br/><br/>
+
+ The <code>action</code> type is a function that takes a <code>score</code>
+ and returns the resulting <code>score</code> and whether the current turn is
+ over.
+ <br/><br/>
+
+ If the turn is over, the <code>player</code> and <code>opponent</code> fields
+ in the resulting <code>score</code> should be swapped, as it is now the other player's
+ turn.
+</step>
+
+<step title="Multiple return values" src="doc/codewalk/pig.go:/\/\/ roll returns/,/stay.*true\n}/">
+ Go functions can return multiple values.
+ <br/><br/>
+
+ The functions <code>roll</code> and <code>stay</code> each return a pair of
+ values. They also match the <code>action</code> type signature. These
+ <code>action</code> functions define the rules of Pig.
+</step>
+
+<step title="Higher-order functions" src="doc/codewalk/pig.go:/\/\/ A strategy/,/action\n/">
+ A function can use other functions as arguments and return values.
+ <br/><br/>
+
+ A <code>strategy</code> is a function that takes a <code>score</code> as input
+ and returns an <code>action</code> to perform. <br/>
+ (Remember, an <code>action</code> is itself a function.)
+</step>
+
+<step title="Function literals and closures" src="doc/codewalk/pig.go:/return func/,/return roll\n\t}/">
+ Anonymous functions can be declared in Go, as in this example. Function
+ literals are closures: they inherit the scope of the function in which they
+ are declared.
+ <br/><br/>
+
+ One basic strategy in Pig is to continue rolling until you have accumulated at
+ least k points in a turn, and then stay. The argument <code>k</code> is
+ enclosed by this function literal, which matches the <code>strategy</code> type
+ signature.
+</step>
+
+<step title="Simulating games" src="doc/codewalk/pig.go:/\/\/ play/,/currentPlayer\n}/">
+ We simulate a game of Pig by calling an <code>action</code> to update the
+ <code>score</code> until one player reaches 100 points. Each
+ <code>action</code> is selected by calling the <code>strategy</code> function
+ associated with the current player.
+</step>
+
+<step title="Comparing functions" src="doc/codewalk/pig.go:/if action/,/currentPlayer\)\)\n\t\t}/">
+ Functions can be compared for equality in Go. From the
+ <a href="http://golang.org/doc/go_spec.html#Comparison_operators">language specification</a>:
+ Function values are equal if they refer to the same function or if both are <code>nil</code>.
+ <br/><br/>
+
+ We enforce that a <code>strategy</code> function can only return a legal
+ <code>action</code>: either <code>roll</code> or <code>stay</code>.
+</step>
+
+<step title="Simulating a tournament" src="doc/codewalk/pig.go:/\/\/ roundRobin/,/gamesPerStrategy\n}/">
+ The <code>roundRobin</code> function simulates a tournament and tallies wins.
+ Each strategy plays each other strategy <code>gamesPerSeries</code> times.
+</step>
+
+<step title="Variadic function declarations" src="doc/codewalk/pig.go:/\/\/ ratioS/,/string {/">
+ Variadic functions like <code>ratioString</code> take a variable number of
+ arguments. These arguments are available as a slice inside the function.
+</step>
+
+<step title="Simulation results" src="doc/codewalk/pig.go:/func main/,/\n}/">
+ The <code>main</code> function defines 100 basic strategies, simulates a round
+ robin tournament, and then prints the win/loss record of each strategy.
+ <br/><br/>
+
+ Among these strategies, staying at 25 is best, but the <a
+ href="http://www.google.com/search?q=optimal+play+pig">optimal strategy for
+ Pig</a> is much more complex.
+</step>
+
+</codewalk>
diff --git a/doc/codewalk/pig.go b/doc/codewalk/pig.go
new file mode 100644
index 000000000..9e415f589
--- /dev/null
+++ b/doc/codewalk/pig.go
@@ -0,0 +1,124 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "fmt"
+ "rand"
+)
+
+const (
+ win = 100 // The winning score in a game of Pig
+ gamesPerSeries = 10 // The number of games per series to simulate
+)
+
+// A score includes scores accumulated in previous turns for each player,
+// as well as the points scored by the current player in this turn.
+type score struct {
+ player, opponent, thisTurn int
+}
+
+// An action transitions stochastically to a resulting score.
+type action func(current score) (result score, turnIsOver bool)
+
+// roll returns the (result, turnIsOver) outcome of simulating a die roll.
+// If the roll value is 1, then thisTurn score is abandoned, and the players'
+// roles swap. Otherwise, the roll value is added to thisTurn.
+func roll(s score) (score, bool) {
+ outcome := rand.Intn(6) + 1 // A random int in [1, 6]
+ if outcome == 1 {
+ return score{s.opponent, s.player, 0}, true
+ }
+ return score{s.player, s.opponent, outcome + s.thisTurn}, false
+}
+
+// stay returns the (result, turnIsOver) outcome of staying.
+// thisTurn score is added to the player's score, and the players' roles swap.
+func stay(s score) (score, bool) {
+ return score{s.opponent, s.player + s.thisTurn, 0}, true
+}
+
+// A strategy chooses an action for any given score.
+type strategy func(score) action
+
+// stayAtK returns a strategy that rolls until thisTurn is at least k, then stays.
+func stayAtK(k int) strategy {
+ return func(s score) action {
+ if s.thisTurn >= k {
+ return stay
+ }
+ return roll
+ }
+}
+
+// play simulates a Pig game and returns the winner (0 or 1).
+func play(strategy0, strategy1 strategy) int {
+ strategies := []strategy{strategy0, strategy1}
+ var s score
+ var turnIsOver bool
+ currentPlayer := rand.Intn(2) // Randomly decide who plays first
+ for s.player+s.thisTurn < win {
+ action := strategies[currentPlayer](s)
+ if action != roll && action != stay {
+ panic(fmt.Sprintf("Player %d is cheating", currentPlayer))
+ }
+ s, turnIsOver = action(s)
+ if turnIsOver {
+ currentPlayer = (currentPlayer + 1) % 2
+ }
+ }
+ return currentPlayer
+}
+
+// roundRobin simulates a series of games between every pair of strategies.
+func roundRobin(strategies []strategy) ([]int, int) {
+ wins := make([]int, len(strategies))
+ for i := 0; i < len(strategies); i++ {
+ for j := i + 1; j < len(strategies); j++ {
+ for k := 0; k < gamesPerSeries; k++ {
+ winner := play(strategies[i], strategies[j])
+ if winner == 0 {
+ wins[i]++
+ } else {
+ wins[j]++
+ }
+ }
+ }
+ }
+ gamesPerStrategy := gamesPerSeries * (len(strategies) - 1) // no self play
+ return wins, gamesPerStrategy
+}
+
+// ratioString takes a list of integer values and returns a string that lists
+// each value and its percentage of the sum of all values.
+// e.g., ratios(1, 2, 3) = "1/6 (16.7%), 2/6 (33.3%), 3/6 (50.0%)"
+func ratioString(vals ...int) string {
+ total := 0
+ for _, val := range vals {
+ total += val
+ }
+ s := ""
+ for _, val := range vals {
+ if s != "" {
+ s += ", "
+ }
+ pct := 100 * float64(val) / float64(total)
+ s += fmt.Sprintf("%d/%d (%0.1f%%)", val, total, pct)
+ }
+ return s
+}
+
+func main() {
+ strategies := make([]strategy, win)
+ for k := range strategies {
+ strategies[k] = stayAtK(k + 1)
+ }
+ wins, games := roundRobin(strategies)
+
+ for k := range strategies {
+ fmt.Printf("Wins, losses staying at k =% 4d: %s\n",
+ k+1, ratioString(wins[k], games-wins[k]))
+ }
+}
diff --git a/doc/contrib.html b/doc/contrib.html
index 121cc45dc..9d0c42726 100644
--- a/doc/contrib.html
+++ b/doc/contrib.html
@@ -2,21 +2,38 @@
<div class="left-column">
-<h2 id="developer_info">Resources for Developers</h2>
+<h2 id="howto">How you can help</h2>
-<h3 id="issuetracker"><a href="http://code.google.com/p/go/issues">Issue Tracker</a></h3>
-<p>Having an issue with Go? Check the tracker to see if it's a known issue.</p>
-<p>If your issue is not listed, please file a <a
-href="http://code.google.com/p/go/issues/entry">bug report</a>.</p>
+<h3>Reporting issues</h3>
-<h3 id="build_status"><a href="http://godashboard.appspot.com/">Build Status</a></h3>
-<p>View the status of Go builds across the supported operating
-systems and architectures.</p>
+<p>
+If you spot bugs, mistakes, or inconsistencies in the Go project's code or
+documentation, please let us know by
+<a href="http://code.google.com/p/go/issues/entry">filing a ticket</a>
+on our <a href="http://code.google.com/p/go/issues">issue tracker</a>.
+(Of course, you should check it's not an existing issue before creating
+a new one.)
+</p>
+
+<p>
+We pride ourselves on being meticulous; no issue is too small.
+</p>
+
+<h3>Contributing code</h3>
-<h3 id="contibute"><a href="contribute.html">Contribution Guidelines</a></h3>
-<p>So, you want to contribute code to the Go project? That's great!</p>
-<p>The first step is to read these contributions guidelines for information on
-design, testing, and our code review process.</p>
+<p>
+Go is an open source project and we welcome contributions from the community.
+</p>
+<p>
+To get started, read these <a href="contribute.html">contribution
+guidelines</a> for information on design, testing, and our code review process.
+</p>
+<p>
+Check <a href="http://code.google.com/p/go/issues">the tracker</a> for
+open issues that interest you. Those labeled
+<a href="http://code.google.com/p/go/issues/list?q=status=HelpWanted">HelpWanted</a>
+are particularly in need of outside help.
+</p>
</div>
@@ -24,11 +41,15 @@ design, testing, and our code review process.</p>
<h2 id="">The Go Project</h2>
+<h3 id="build_status"><a href="http://godashboard.appspot.com/">Build Status</a></h3>
+<p>View the status of Go builds across the supported operating
+systems and architectures.</p>
+
<h3 id="roadmap"><a href="devel/roadmap.html">Roadmap</a></h3>
<p>Features and ideas being developed or discussed by the Go team.</p>
<h3 id="release"><a href="devel/release.html">Release History</a></h3>
-<p>A summarization of the changes between tagged releases of Go.</p>
+<p>A summary of the changes between tagged releases of Go.</p>
<h3 id="golang-dev"><a href="http://groups.google.com/group/golang-dev">Developer Mailing List</a></h3>
<p>The <a href="http://groups.google.com/group/golang-dev">golang-dev</a>
diff --git a/doc/devel/release.html b/doc/devel/release.html
index c7691c766..f75cbf24f 100644
--- a/doc/devel/release.html
+++ b/doc/devel/release.html
@@ -5,7 +5,368 @@
<p>This page summarizes the changes between tagged releases of Go.
For full details, see the <a href="http://code.google.com/p/go/source/list">Mercurial change log</a>.</p>
-<h3 id="2011-03-07">2011-03-07</h3>
+<h3 id="2011-04-13">2011-04-13</h3>
+
+<pre>
+weekly.2011-04-13
+
+This weekly snapshot includes major changes to the reflect package and the
+os.Open function. Code that uses reflect or os.Open will require updating,
+which can be done mechanically using the gofix tool.
+
+The reflect package's Type and Value types have changed. Type is now an
+interface that implements all the possible type methods. Instead of a type
+switch on a reflect.Type t, switch on t.Kind(). Value is now a struct value
+that implements all the possible value methods. Instead of a type switch on a
+reflect.Value v, switch on v.Kind(). See the change for the full details:
+ http://code.google.com/p/go/source/detail?r=843855f3c026
+
+The os package's Open function has been replaced by three functions:
+ OpenFile(name, flag, perm) // same as old Open
+ Open(name) // same as old Open(name, O_RDONLY, 0)
+ Create(name) // same as old Open(name, O_RDWR|O_TRUNC|O_CREAT, 0666)
+
+To update your code to use the new APIs, run "gofix path/to/code". Gofix can’t
+handle all situations perfectly, so read and test the changes it makes before
+committing them.
+
+Other changes:
+* archive/zip: add func OpenReader, type ReadCloser (thanks Dmitry Chestnykh).
+* asn1: Implement correct marshaling of length octets (thanks Luit van Drongelen).
+* big: don't crash when printing nil ints.
+* bufio: add ReadLine, to replace encoding/line.
+* build: make the build faster, quieter.
+* codereview: automatically port old diffs forward,
+ drop Author: line on self-clpatch,
+ recognize code URL without trailing slash.
+* crypto/block: remove deprecated package.
+* crypto/des: new package implementating DES and TDEA (thanks Yasuhiro Matsumoto).
+* crypto/ecdsa, crypto/rsa: use io.ReadFull to read from random source (thanks Dmitry Chestnykh).
+* crypto/rsa: add 3-prime support,
+ add support for precomputing CRT values,
+ flip the CRT code over so that it matches PKCS#1.
+* crypto/x509: expose complete DER data (thanks Mikkel Krautz).
+* doc: new "Functions" codewalk (thanks John DeNero).
+* doc/roadmap: add sections on tools, packages.
+* fmt: allow %U for unsigned integers.
+* gc: fixes and optimizations.
+* go/printer, gofmt: use blank to separate import rename from import path.
+* go/scanner: better TokenString output.
+* go/types: new Go type hierarchy implementation for AST.
+* godashboard: show packages at launchpad.net (thanks Gustavo Niemeyer).
+* gofix: add -diff, various fixes and helpers.
+* gotest: fix a bug in error handling,
+ fixes for [^.]_test file pattern (thanks Peter Mundy),
+ handle \r\n returned by gomake on Windows (thanks Alex Brainman).
+* gotype: use go/types GcImporter.
+* govet: make name-matching for printf etc. case-insensitive.
+* http: allow override of Content-Type for ServeFile,
+ client gzip support,
+ do not listen on 0.0.0.0 during test,
+ flesh out server Expect handling + tests.
+* image/ycbcr: new package.
+* image: allow "?" wildcards when registering image formats.
+* io: fixes for Read with n > 0, os.EOF (thanks Robert Hencke).
+* ld: correct Plan 9 compiler warnings (thanks Lucio De Re),
+ ELF header function declarations (thanks Lucio De Re),
+ fix Mach-O X86_64_RELOC_SIGNED relocations (thanks Mikkel Krautz),
+ fix Mach-O bss bug (thanks Mikkel Krautz),
+ fix dwarf decoding of strings for struct's fieldnames (thanks Luuk van Dijk),
+ fixes and optimizations (25% faster).
+* log: generalize getting and setting flags and prefix.
+* misc/cgo/life: enable build and test on Windows (thanks Alex Brainman).
+* misc/vim: add plugin with Fmt command (thanks Dmitry Chestnykh),
+ update type highlighting for new reflect package.
+* net: disable multicast tests by default (thanks Dave Cheney),
+ sort records returned by LookupMX (thanks Corey Thomasson).
+* openpgp: Fix improper := shadowing (thanks Gustavo Niemeyer).
+* os: rename Open to OpenFile, add new Open, Create,
+ fix Readdir in Plan 9 (thanks Fazlul Shahriar).
+* os/inotify: use _test for test files, not _obj.
+* pkg/path: enable tests on Windows (thanks Alex Brainman).
+* reflect: new Type and Value API.
+* src/pkg/Makefile: trim per-directory make output except on failure.
+* syscall: Add DT_* and MADV_* constants on Linux (thanks Albert Strasheim),
+ add Mmap, Munmap on Linux, FreeBSD, OS X,
+ fix StartProcess in Plan 9 (thanks Fazlul Shahriar),
+ fix Windows Signaled (thanks Alex Brainman).
+* test/bench: enable build and test on Windows (thanks Alex Brainman).
+</pre>
+
+<h3 id="2011-04-04">2011-04-04</h3>
+
+<pre>
+This release includes changes to the net package. Your code will require
+changes if it uses the Dial or LookupHost functions.
+
+The laddr argument has been removed from net.Dial, and the cname return value
+has been removed from net.LookupHost. The new net.LookupCNAME function can be
+used to find the canonical host for a given name. You can update your
+networking code with gofix.
+
+The gotest shell script has been replaced by a Go program, making testing
+significantly faster.
+
+Other changes:
+* asn1: extensions needed for parsing Kerberos.
+* bufio: Write and WriteString cleanup (thanks Evan Shaw).
+* bytes, strings: simplify Join (thanks Evan Shaw).
+* crypto/cipher: bad CTR IV length now triggers panic.
+* crypto/tls: extend NPN support to the client,
+ added X509KeyPair function to parse a Certificate from memory.
+* crypto/x509: parse Extended Key Usage extension (thanks Mikkel Krautz).
+* debug/gosym: remove need for gotest to run preparatory commands.
+* fmt: implement precision (length of input) values for %q: %.20q.
+* go/parser: fix scoping for local type declarations (thanks Roger Peppe),
+ package name must not be the blank identifier.
+* go/printer, gofmt: remove special case for multi-line raw strings.
+* gopack: add P flag to remove prefix from filename information.
+* gotest: add -test.timeout option,
+ replace the shell script with the compiled program written in go,
+ execute gomake properly on Windows (thanks Alex Brainman).
+* gotry: move into its own directory, separate from gotest.
+* gotype: support for more tests, added one new test.
+* http: add Transport.MaxIdleConnsPerHost,
+ use upper case hex in URL escaping (thanks Matt Jones).
+* httptest: add NewTLSServer.
+* misc/kate: reorganize, remove closed() (thanks Evan Shaw).
+* misc/notepadplus: support for notepad++ (thanks Anthony Starks).
+* net: implement non-blocking connect (thanks Alexey Borzenkov).
+* os: fix MkdirAll("/thisdoesnotexist") (thanks Albert Strasheim),
+ Plan 9 support (thanks Yuval Pavel Zholkover),
+ add a few missing Plan 9 errors (thanks Andrey Mirtchovski),
+ fix FileInfo.Name returned by Stat (thanks David Forsythe).
+* path/filepath.Glob: add an error return,
+ don't drop known matches on error.
+* path/filepath: add support for Plan 9 (thanks Andrey Mirtchovski).
+* scanner: treat line comments like in Go.
+* syscall: Plan 9 support (thanks Yuval Pavel Zholkover),
+ StartProcess Chroot and Credential (thanks Albert Strasheim),
+ add BPF support for freebsd/386, freebsd/amd64 (thanks Mikio Hara),
+ make [Raw]Syscall6 pass 6th arg on linux/386 (thanks Evan Shaw).
+</pre>
+
+<h3 id="2011-03-28">2011-03-28</h3>
+
+<pre>
+This weekly release includes improved support for testing.
+
+Memory and CPU profiling is now available via the gotest tool. Gotest will
+produce memory and CPU profiling data when invoked with the -test.memprofile
+and -test.cpuprofile flags. Run "godoc gotest" for details.
+
+We have also introduced a way for tests to run quickly when an exhaustive test
+is unnecessary. Gotest’s new -test.short flag in combination with the testing
+package’s new Short function allows you to write tests that can be run in
+normal or "short" mode; short mode is now used by all.bash to reduce
+installation time.
+The Makefiles know about the flag - you can just run "make testshort".
+
+Other changes:
+* .hgignore: Ignore all goinstalled packages (thanks Evan Shaw).
+* build: add all-qemu.bash, handful of arm fixes,
+ add support for SWIG, and add two SWIG examples,
+ diagnose Ubuntu's buggy copy of gold,
+ handle broken awk in version.bash (thanks Dave Cheney),
+ reenable clean.bash without gomake (thanks Gustavo Niemeyer).
+* cgo: fix index-out-of-bounds bug.
+* codereview: permit CLs of the form weekly.DATE
+* crypto/ecdsa: truncate hash values.
+* crypto/openpgp: add DSA signature support.
+* dashboard: remove old python/bash builder, update README.
+* doc: explain release and weekly tags in install.html.
+* exec: document dir option for Run (thanks Gustavo Niemeyer).
+* flag: document Nflag function (thanks Fazlul Shahriar).
+* gc: remove interim ... error which rejects valid code.
+* go/ast: implemented NewPackage,
+ merge CaseClause and TypeCaseClause.
+* go/parser: fix memory leak by making a copy of token literals,
+ resolve identifiers properly.
+* go/printer, gofmt: avoid exponential layout algorithm,
+ gofmt: simplify struct formatting and respect line breaks.
+* go/scanner: to interpret line comments with Windows filenames (thanks Alex Brainman).
+* go/token: use array instead of map for token->string table.
+* gob: optimizations to reduce allocations,
+ use pointers in bootstrapType so interfaces behave properly.
+* gobuilder: recognize CLs of the form weekly.DATE.
+* godefs: handle volatile.
+* godoc: add -template flag to specify custom templates,
+ fix path problem for windows (thanks Yasuhiro Matsumoto).
+* gofix: httpserver - rewrite rw.SetHeader to rw.Header.Set.
+* gofmt: add profiling flag.
+* gopprof: fix bug: do not rotate 180 degrees for large scrolls,
+ update list of memory allocation functions.
+* gotest: fix gofmt issue in generated _testmain.go.
+* http: add NewProxyClientConn,
+ avoid crash when asked for multiple file ranges,
+ don't chunk 304 responses,
+ export Transport, add keep-alive support.
+* ld: return > 0 exit code on unsafe import.
+* misc/bbedit: remove closed keyword (thanks Anthony Starks).
+* misc/emacs: gofmt: don't clobber the current buffer on failure.
+* misc/vim: remove 'closed' as a builtin function.
+* net: add FileConn, FilePacketConn, FileListener (thanks Albert Strasheim),
+ don't force epoll/kqueue to wake up in order to add new events,
+ let OS-specific AddFD routine wake up polling thread,
+ use preallocated buffer for epoll and kqueue/kevent.
+* path/filepath: add EvalSymlinks function,
+ fix TestEvalSymlinks when run under symlinked GOROOT.
+* path: work for windows (thanks Yasuhiro Matsumoto).
+* rpc: increase server_test timeout (thanks Gustavo Niemeyer),
+ optimizations to reduce allocations.
+* runtime: fix darwin/amd64 thread VM footprint (thanks Alexey Borzenkov),
+ fix gdb support for goroutines,
+ more stack split fixes,
+ os-specific types and code for setitimer,
+ update defs.h for freebsd-386 (thanks Devon H. O'Dell).
+* strings: Map: avoid allocation when string is unchanged.
+* syscall: GetsockoptInt (thanks Albert Strasheim),
+ StartProcess fixes for windows (thanks Alex Brainman),
+ permit non-blocking syscalls,
+ rename from .sh to .pl, because these files are in Perl.
+* test: enable tests using v, ok := <-ch syntax (thanks Robert Hencke).
+* time: give a helpful message when we can't set the time zone for testing.
+ isolate syscall reference in sys.go.
+</pre>
+
+<h3 id="2011-03-15">2011-03-15</h3>
+
+<pre>
+This week's release introduces a new release tagging scheme. We intend to
+continue with our weekly releases, but have renamed the existing tags from
+"release" to "weekly". The "release" tag will now be applied to one hand-picked
+stable release each month or two.
+
+The revision formerly tagged "release.2011-03-07.1" (now "weekly.2011-03-07.1")
+has been nominated our first stable release, and has been given the tag
+"release.r56". As we tag each stable release we will post an announcement to
+the new golang-announce mailing list:
+ http://groups.google.com/group/golang-announce
+
+You can continue to keep your Go installation updated using "hg update
+release", but now you should only need to update once we tag a new stable
+release, which we will announce here. If you wish to stay at the leading edge,
+you should switch to the weekly tag with "hg update weekly".
+
+
+This weekly release includes significant changes to the language spec and the
+http, os, and syscall packages. Your code may need to be changed. It also
+introduces the new gofix tool.
+
+The closed function has been removed from the language. The syntax for channel
+receives has been changed to return an optional second value, a boolean value
+indicating whether the channel is closed. This code:
+ v := <-ch
+ if closed(ch) {
+ // channel is closed
+ }
+should now be written as:
+ v, ok := <-ch
+ if !ok {
+ // channel is closed
+ }
+
+It is now illegal to declare unused labels, just as it is illegal to declare
+unused local variables.
+
+The new gofix tool finds Go programs that use old APIs and rewrites them to use
+newer ones. After you update to a new Go release, gofix helps make the
+necessary changes to your programs. Gofix will handle the http, os, and syscall
+package changes described below, and we will update the program to keep up with
+future changes to the libraries.
+
+The Hijack and Flush methods have been removed from the http.ResponseWriter
+interface and are accessible via the new http.Hijacker and http.Flusher
+interfaces. The RemoteAddr and UsingTLS methods have been moved from
+http.ResponseWriter to http.Request.
+
+The http.ResponseWriter interface's SetHeader method has been replaced by a
+Header() method that returns the response's http.Header. Caller code needs to
+change. This code:
+ rw.SetHeader("Content-Type", "text/plain")
+should now be written as:
+ rw.Header().Set("Content-Type", "text/plain")
+The os and syscall packages' StartProcess functions now take their final three
+arguments as an *os.ProcAttr and *syscall.ProcAttr values, respectively. This
+code:
+ os.StartProcess(bin, args, env, dir, fds)
+should now be written as:
+ os.StartProcess(bin, args, &os.ProcAttr{Files: fds, Dir: dir, Env: env})
+
+The gob package will now encode and decode values of types that implement the
+gob.GobEncoder and gob.GobDecoder interfaces. This allows types with unexported
+fields to transmit self-consistent descriptions; one instance is big.Int and
+big.Rat.
+
+Other changes:
+* 5l, 6l, 8l: reduce binary size about 40% by omitting symbols for type, string, go.string.
+* 5l, 8l: output missing section symbols (thanks Anthony Martin).
+* 6l, 8l: fix gdb crash.
+* Make.cmd: also clean _test* (thanks Gustavo Niemeyer).
+* big: implemented custom Gob(En/De)coder for Int type.
+* build: remove duplicate dependency in Make.cmd (thanks Robert Hencke),
+ run gotest in misc/cgo/test.
+* codereview.py: don't suggest change -d if user is not CL author (thanks Robert Hencke).
+* compress/lzw: benchmark a range of input sizes.
+* crypto/ecdsa: add package.
+* crypto/elliptic: add the N value of each curve.
+* crypto/openpgp: bug fixes and fix misnamed function.
+* crypto/tls: fix compile error (thanks Dave Cheney).
+* doc: Effective Go: some small cleanups,
+ update FAQ. hello, world is now 1.1MB, down from 1.8MB,
+ update codelab wiki to fix template.Execute argument order.
+* flag: visit the flags in sorted order, for nicer messages.
+* fmt: do not export EOF = -1.
+* fmt: make ScanState.Token more general (thanks Roger Peppe).
+* gc: diagnose unused labels,
+ fix handling of return values named _,
+ include all dependencies in export metadata,
+ make unsafe.Pointer its own kind of type, instead of an equivalent to *any.
+* go/ast, go/parser: populate identifier scopes at parse time.
+* go/ast: add FileSet parameter to ast.Print and ast.Fprint.
+* go/parser: first constant in a constant declaration must have a value.
+* gob: efficiency and reliability fixes.
+* gofmt: remove -trace and -ast flags.
+* goinstall: handle $(GOOS) and $(GOARCH) in filenames,
+ handle .c files with gc when cgo isn't used, and
+ handle .s files with gc (thanks Gustavo Niemeyer).
+* gopack: omit time stamps, makes output deterministic.
+* gotype: commandline tool to typecheck go programs.
+* govet: handle '*' in print format strings.
+* hash: new FNV-1a implementation (thanks Pascal S. de Kloe).
+* http/cgi: child support (e.g. Go CGI under Apache).
+* http: adapt Cookie code to follow IETF draft (thanks Petar Maymounkov),
+ add test for fixed HTTP/1.0 keep-alive issue,
+ don't hit external network in client_test.go,
+ fix transport crash when request URL is nil,
+ rename interface Transport to RoundTripper,
+ run tests even with DISABLE_NET_TESTS=1.
+* httptest: default the Recorder status code to 200 on a Write.
+* io/ioutil: clean-up of ReadAll and ReadFile.
+* ioutil: add NopCloser.
+* ld: preserve symbol sizes during data layout.
+* lib9, libmach: Change GOOS references to GOHOSTOS (thanks Evan Shaw).
+* libmach: correct string comparison to revive 6cov on darwin (thanks Dave Cheney).
+* misc/vim: Add indent script for Vim (thanks Ross Light).
+* net, os, syslog: fixes for Solaris support.
+* net: don't loop to drain wakeup pipe.
+* nm: document -S flag.
+* openpgp: add PublicKey KeyId string accessors.
+* rpc: optimizations, add benchmarks and memory profiling,
+ use httptest.Server for tests (thanks Robert Hencke).
+* runtime: reduce lock contention via wakeup on scheduler unlock,
+ scheduler, cgo reorganization,
+ split non-debugging malloc interface out of debug.go into mem.go.
+* spec: clarify return statement rules.
+* strings: add IndexRune tests, ASCII fast path,
+ better benchmark names; add BenchmarkIndex.
+* syscall: implement Mount and Unmount for linux,
+ implement Reboot for linux.
+* time: fix Time.ZoneOffset documentation (thanks Peter Mundy).
+* tls: move PeerCertificates to ConnectionState.
+</pre>
+
+<h3 id="2011-03-07">2011-03-07 (r56)</h3>
<pre>
This release includes changes to the reflect and path packages.
@@ -135,7 +496,7 @@ Other changes:
* netchan: allow use of arbitrary connections (thanks Roger Peppe).
* os: add ENODATA and ENOTCONN (thanks Albert Strasheim).
* reflect: add a couple of sentences explaining how Methods operate,
- add a secret method to ArrayOrSliceType to ensure it’s only implemented by arrays and slices,
+ add a secret method to ArrayOrSliceType to ensure it's only implemented by arrays and slices,
add pointer word to CommonType (placeholder for future work).
* runtime-gdb.py: gdb pretty printer for go strings properly handles length.
* runtime: various bug fixes, more complete stack traces,
@@ -186,7 +547,7 @@ Other changes:
fix spaces in GOROOT (thanks Christopher Nielsen).
* bytes: fix bug in buffer.ReadBytes (thanks Evan Shaw).
* 5g: better int64 code,
- don’t use MVN instruction.
+ don't use MVN instruction.
* cgo: don't run cgo when not compiling (thanks Gustavo Niemeyer),
fix _cgo_run timestamp file order (thanks Gustavo Niemeyer),
fix handling of signed enumerations (thanks Gustavo Niemeyer),
diff --git a/doc/devel/roadmap.html b/doc/devel/roadmap.html
index 97d8a08b8..a73ec6353 100644
--- a/doc/devel/roadmap.html
+++ b/doc/devel/roadmap.html
@@ -30,6 +30,8 @@ Generics. An active topic of discussion.
<li>
Methods for operators, to allow a type to use arithmetic notation for
expressions.
+<li>
+Possibly allow top-level packages to be given names other than main.
</ul>
<h3 id="Implementation_roadmap">
@@ -37,8 +39,7 @@ Implementation roadmap</h3>
<ul>
<li>
-Improved garbage collector, most likely a reference counting collector
-with a cycle detector running in a separate core.
+Improved garbage collector.
<li>
Debugger.
<li>
@@ -48,7 +49,7 @@ Improved CGO including some mechanism for calling back from C to Go.
<li>
Improved implementation documentation.
<li>
-Comprehensive support for internationalization.
+Faster, allocation-light reflection.
</ul>
<h4 id="Gc_roadmap">
@@ -77,6 +78,31 @@ Separate gcc interface from frontend proper.
Use escape analysis to keep more data on stack.
</ul>
+<h4 id="Tools_roadmap">
+Tools roadmap</h4>
+
+<ul>
+<li>
+Strengthen goinstall until it can displace make for most builds.
+</ul>
+
+<h4 id="Packages_roadmap">
+Packages roadmap</h4>
+
+<ul>
+<li>
+Faster, allocation-light reflection.
+<li>
+Faster, RE2-like regular expressions.
+<li>
+Comprehensive support for international text.
+<li>
+Support for international dates, times, etc.
+<li>
+Support for multilingual messages.
+</ul>
+
+
<h3 id="done">Done</h3>
<ul>
diff --git a/doc/effective_go.html b/doc/effective_go.html
index a32179298..27bfd1bf5 100644
--- a/doc/effective_go.html
+++ b/doc/effective_go.html
@@ -59,13 +59,14 @@ prescriptive style guide.
With Go we take an unusual
approach and let the machine
take care of most formatting issues.
-A program, <code>gofmt</code>, reads a Go program
+The <code>gofmt</code> tool reads a Go program
and emits the source in a standard style of indentation
and vertical alignment, retaining and if necessary
reformatting comments.
If you want to know how to handle some new layout
situation, run <code>gofmt</code>; if the answer doesn't
-seem right, fix the program (or file a bug), don't work around it.
+seem right, rearrange your program (or file a bug about <code>gofmt</code>),
+don't work around it.
</p>
<p>
@@ -94,7 +95,7 @@ type T struct {
</pre>
<p>
-All code in the libraries has been formatted with <code>gofmt</code>.
+All Go code in the standard packages has been formatted with <code>gofmt</code>.
</p>
@@ -304,7 +305,8 @@ not <code>container_vector</code> and not <code>containerVector</code>.
<p>
The importer of a package will use the name to refer to its contents
(the <code>import .</code> notation is intended mostly for tests and other
-unusual situations), so exported names in the package can use that fact
+unusual situations and should be avoided unless necessary),
+so exported names in the package can use that fact
to avoid stutter.
For instance, the buffered reader type in the <code>bufio</code> package is called <code>Reader</code>,
not <code>BufReader</code>, because users see it as <code>bufio.Reader</code>,
@@ -316,8 +318,8 @@ Similarly, the function to make new instances of <code>ring.Ring</code>&mdash;wh
is the definition of a <em>constructor</em> in Go&mdash;would
normally be called <code>NewRing</code>, but since
<code>Ring</code> is the only type exported by the package, and since the
-package is called <code>ring</code>, it's called just <code>New</code>.
-Clients of the package see that as <code>ring.New</code>.
+package is called <code>ring</code>, it's called just <code>New</code>,
+which clients of the package see as <code>ring.New</code>.
Use the package structure to help you choose good names.
</p>
@@ -331,6 +333,27 @@ to write a helpful doc comment than to attempt to put all the information
into the name.
</p>
+<h3 id="Getters">Getters</h3>
+
+<p>
+Go doesn't provide automatic support for getters and setters.
+There's nothing wrong with providing getters and setters yourself,
+and it's often appropriate to do so, but it's neither idiomatic nor necessary
+to put <code>Get</code> into the getter's name. If you have a field called
+<code>owner</code> (lower case, unexported), the getter method should be
+called <code>Owner</code> (upper case, exported), not <code>GetOwner</code>.
+The use of upper-case names for export provides the hook to discriminate
+the field from the method.
+A setter function, if needed, will likely be called <code>SetOwner</code>.
+Both names read well in practice:
+</p>
+<pre>
+owner := obj.Owner()
+if owner != user {
+ obj.SetOwner(user)
+}
+</pre>
+
<h3 id="interface-names">Interface names</h3>
<p>
@@ -489,8 +512,8 @@ codeUsing(f)
</pre>
<p>
-This is a example of a common situation where code must analyze a
-sequence of error possibilities. The code reads well if the
+This is an example of a common situation where code must guard against a
+sequence of error conditions. The code reads well if the
successful flow of control runs down the page, eliminating error cases
as they arise. Since error cases tend to end in <code>return</code>
statements, the resulting code needs no <code>else</code> statements.
@@ -553,8 +576,9 @@ for _, value := range m { // key is unused
<p>
For strings, the <code>range</code> does more work for you, breaking out individual
-Unicode characters by parsing the UTF-8 (erroneous encodings consume one byte and produce the
-replacement rune U+FFFD). The loop
+Unicode characters by parsing the UTF-8.
+Erroneous encodings consume one byte and produce the
+replacement rune U+FFFD. The loop
</p>
<pre>
for pos, char := range "日本語" {
@@ -571,8 +595,9 @@ character 語 starts at byte position 6
</pre>
<p>
-Finally, since Go has no comma operator and <code>++</code> and <code>--</code>
-are statements not expressions, if you want to run multiple variables in a <code>for</code>
+Finally, Go has no comma operator and <code>++</code> and <code>--</code>
+are statements not expressions.
+Thus if you want to run multiple variables in a <code>for</code>
you should use parallel assignment.
</p>
<pre>
@@ -676,7 +701,7 @@ case *int:
<p>
One of Go's unusual features is that functions and methods
-can return multiple values. This can be used to
+can return multiple values. This form can be used to
improve on a couple of clumsy idioms in C programs: in-band
error returns (such as <code>-1</code> for <code>EOF</code>)
and modifying an argument.
@@ -811,7 +836,7 @@ func Contents(filename string) (string, os.Error) {
</pre>
<p>
-Deferring a function like this has two advantages. First, it
+Deferring a call to a function such as <code>Close</code> has two advantages. First, it
guarantees that you will never forget to close the file, a mistake
that's easy to make if you later edit the function to add a new return
path. Second, it means that the close sits near the open,
@@ -903,8 +928,9 @@ leaving: b
For programmers accustomed to block-level resource management from
other languages, <code>defer</code> may seem peculiar, but its most
interesting and powerful applications come precisely from the fact
-that it's not block-based but function based. In the section on
-<code>panic</code> and <code>recover</code> we'll see an example.
+that it's not block-based but function-based. In the section on
+<code>panic</code> and <code>recover</code> we'll see another
+example of its possibilities.
</p>
<h2 id="data">Data</h2>
@@ -949,7 +975,7 @@ type SyncedBuffer struct {
<p>
Values of type <code>SyncedBuffer</code> are also ready to use immediately upon allocation
-or just declaration. In this snippet, both <code>p</code> and <code>v</code> will work
+or just declaration. In the next snippet, both <code>p</code> and <code>v</code> will work
correctly without further arrangement.
</p>
@@ -987,7 +1013,6 @@ an expression that creates a
new instance each time it is evaluated.
</p>
-
<pre>
func NewFile(fd int, name string) *File {
if fd &lt; 0 {
@@ -999,7 +1024,7 @@ func NewFile(fd int, name string) *File {
</pre>
<p>
-Note that it's perfectly OK to return the address of a local variable;
+Note that, unlike in C, it's perfectly OK to return the address of a local variable;
the storage associated with the variable survives after the function
returns.
In fact, taking the address of a composite literal
@@ -1053,7 +1078,7 @@ is that these three types are, under the covers, references to data structures t
must be initialized before use.
A slice, for example, is a three-item descriptor
containing a pointer to the data (inside an array), the length, and the
-capacity; until those items are initialized, the slice is <code>nil</code>.
+capacity, and until those items are initialized, the slice is <code>nil</code>.
For slices, maps, and channels,
<code>make</code> initializes the internal data structure and prepares
the value for use.
@@ -1273,7 +1298,21 @@ is not present in the map will return the zero value for the type
of the entries
in the map. For instance, if the map contains integers, looking
up a non-existent key will return <code>0</code>.
+A set can be implemented as a map with value type <code>bool</code>.
+Set the map entry to <code>true</code> to put the value in the set, and then
+test it by simple indexing.
</p>
+<pre>
+attended := map[string] bool {
+ "Ann": true,
+ "Joe": true,
+ ...
+}
+
+if attended[person] { // will be false if person is not in the map
+ fmt.Println(person, "was at the meeting")
+}
+</pre>
<p>
Sometimes you need to distinguish a missing entry from
a zero value. Is there an entry for <code>"UTC"</code>
@@ -1298,7 +1337,7 @@ func offset(tz string) int {
if seconds, ok := timeZone[tz]; ok {
return seconds
}
- log.Println("unknown time zone", tz)
+ log.Println("unknown time zone:", tz)
return 0
}
</pre>
diff --git a/doc/go_faq.html b/doc/go_faq.html
index 4be811068..5f92b0528 100644
--- a/doc/go_faq.html
+++ b/doc/go_faq.html
@@ -558,7 +558,8 @@ type Fooer interface {
<p>
A type must then implement the <code>ImplementsFooer</code> method to be a
-<code>Fooer</code>, clearly documenting the fact.
+<code>Fooer</code>, clearly documenting the fact and announcing it in
+<a href="/cmd/godoc/">godoc</a>'s output.
</p>
<pre>
@@ -1032,7 +1033,7 @@ type checks, reflection, and even panic-time stack traces.
<p>
A trivial C "hello, world" program compiled and linked statically using gcc
-on Linux is around 750 kB. An equivalent Go program is around 1.8 MB, but
+on Linux is around 750 kB. An equivalent Go program is around 1.1 MB, but
that includes more powerful run-time support. We believe that with some effort
the size of Go binaries can be reduced.
diff --git a/doc/go_spec.html b/doc/go_spec.html
index 85dfc44bd..f8fe5974a 100644
--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -1,5 +1,5 @@
<!-- title The Go Programming Language Specification -->
-<!-- subtitle Version of March 3, 2011 -->
+<!-- subtitle Version of Apr 5, 2011 -->
<!--
TODO
@@ -1235,8 +1235,11 @@ receiver are ready.
</p>
<p>
-A channel may be closed and tested for closure with the built-in functions
-<a href="#Close_and_closed"><code>close</code> and <code>closed</code></a>.
+A channel may be closed with the built-in function
+<a href="#Close"><code>close</code></a>; the
+multi-valued assignment form of the
+<a href="#Receive_operator">receive operator</a>
+tests whether a channel has been closed.
</p>
<h2 id="Properties_of_types_and_values">Properties of types and values</h2>
@@ -1469,6 +1472,7 @@ declarations.
Labels are declared by <a href="#Labeled_statements">labeled statements</a> and are
used in the <code>break</code>, <code>continue</code>, and <code>goto</code>
statements (§<a href="#Break_statements">Break statements</a>, §<a href="#Continue_statements">Continue statements</a>, §<a href="#Goto_statements">Goto statements</a>).
+It is illegal to define a label that is never used.
In contrast to other identifiers, labels are not block scoped and do
not conflict with identifiers that are not labels. The scope of a label
is the body of the function in which it is declared and excludes
@@ -1496,7 +1500,7 @@ Zero value:
nil
Functions:
- append cap close closed complex copy imag len
+ append cap close complex copy imag len
make new panic print println real recover
</pre>
@@ -3029,9 +3033,6 @@ f(&lt;-ch)
&lt;-strobe // wait until clock pulse and discard received value
</pre>
-<!--
- TODO(rsc): Add after a release or two without any x,ok := <-c.
-
<p>
A receive expression used in an assignment or initialization of the form
</p>
@@ -3049,7 +3050,6 @@ the received value was sent on the channel (<code>true</code>)
or is a <a href="#The_zero_value">zero value</a> returned
because the channel is closed and empty (<code>false</code>).
</p>
--->
<p>
Receiving from a <code>nil</code> channel causes a
@@ -4009,9 +4009,8 @@ iteration values for each entry will be produced at most once.
<li>
For channels, the iteration values produced are the successive values sent on
-the channel until the channel is closed; it does not produce the zero value sent
-before the channel is closed
-(§<a href="#Close_and_closed"><code>close</code> and <code>closed</code></a>).
+the channel until the channel is closed
+(§<a href="#Close"><code>close</code>).
</li>
</ol>
@@ -4086,12 +4085,9 @@ cases all referring to communication operations.
SelectStmt = "select" "{" { CommClause } "}" .
CommClause = CommCase ":" { Statement ";" } .
CommCase = "case" ( SendStmt | RecvStmt ) | "default" .
-RecvStmt = [ Expression ( "=" | ":=" ) ] RecvExpr .
+RecvStmt = [ Expression [ "," Expression ] ( "=" | ":=" ) ] RecvExpr .
RecvExpr = Expression .
</pre>
-<!-- TODO(rsc):
-RecvStmt = [ Expression [ "," Expression ] ( "=" | ":=" ) ] RecvExpr .
--->
<p>
RecvExpr must be a <a href="#Receive_operator">receive operation</a>.
@@ -4122,27 +4118,24 @@ in the "select" statement.
If multiple cases can proceed, a pseudo-random fair choice is made to decide
which single communication will execute.
<p>
-<!-- TODO(rsc): s/variable/& or &s/ -->
-The receive case may declare a new variable using a
+The receive case may declare one or two new variables using a
<a href="#Short_variable_declarations">short variable declaration</a>.
</p>
<pre>
-var c, c1, c2 chan int
+var c, c1, c2, c3 chan int
var i1, i2 int
select {
case i1 = &lt;-c1:
print("received ", i1, " from c1\n")
case c2 &lt;- i2:
print("sent ", i2, " to c2\n")
-<!-- TODO(rsc): add , c3 to channel list above too
case i3, ok := &lt;-c3:
if ok {
print("received ", i3, " from c3\n")
} else {
print("c3 is closed\n")
}
--->
default:
print("no communication\n")
}
@@ -4212,7 +4205,7 @@ func complex_f2() (re float64, im float64) {
</pre>
</li>
<li>The expression list may be empty if the function's result
- type specifies names for its result parameters (§<a href="#Function_Types">Function Types</a>).
+ type specifies names for its result parameters (§<a href="#Function_types">Function Types</a>).
The result parameters act as ordinary local variables
and the function may assign values to them as necessary.
The "return" statement returns the values of these variables.
@@ -4222,6 +4215,11 @@ func complex_f3() (re float64, im float64) {
im = 4.0
return
}
+
+func (devnull) Write(p []byte) (n int, _ os.Error) {
+ n = len(p)
+ return
+}
</pre>
</li>
</ol>
@@ -4259,11 +4257,13 @@ terminates
</p>
<pre>
-L: for i &lt; n {
- switch i {
- case 5: break L
+L:
+ for i &lt; n {
+ switch i {
+ case 5:
+ break L
+ }
}
-}
</pre>
<h3 id="Continue_statements">Continue statements</h3>
@@ -4305,8 +4305,8 @@ instance, this example:
</p>
<pre>
-goto L // BAD
-v := 3
+ goto L // BAD
+ v := 3
L:
</pre>
@@ -4396,8 +4396,7 @@ BuiltinCall = identifier "(" [ BuiltinArgs [ "," ] ] ")" .
BuiltinArgs = Type [ "," ExpressionList ] | ExpressionList .
</pre>
-<!-- TODO(rsc): s/.and.closed//g -->
-<h3 id="Close_and_closed">Close and closed</h3>
+<h3 id="Close">Close</h3>
<p>
For a channel <code>c</code>, the built-in function <code>close(c)</code>
@@ -4407,12 +4406,8 @@ After calling <code>close</code>, and after any previously
sent values have been received, receive operations will return
the zero value for the channel's type without blocking.
-<!-- TODO(rsc): delete next sentence, replace with
- The multi-valued <a href="#Receive_operator">receive operation</a>
- returns a received value along with an indication of whether the channel is closed.
--->
-After at least one such zero value has been
-received, <code>closed(c)</code> returns true.
+The multi-valued <a href="#Receive_operator">receive operation</a>
+returns a received value along with an indication of whether the channel is closed.
</p>
@@ -4700,7 +4695,7 @@ func protect(g func()) {
if x := recover(); x != nil {
log.Printf("run time panic: %v", x)
}
- }
+ }()
log.Println("start")
g()
}
@@ -5152,6 +5147,4 @@ The following minimal alignment properties are guaranteed:
<h2 id="Implementation_differences"><span class="alert">Implementation differences - TODO</span></h2>
<ul>
<li><span class="alert">Implementation does not honor the restriction on goto statements and targets (no intervening declarations).</span></li>
- <li><span class="alert">Gccgo: Method expressions are partially implemented.</span></li>
- <li><span class="alert">Gccgo: allows only one init() function per source file.</span></li>
</ul>
diff --git a/doc/install.html b/doc/install.html
index 816e6e654..843e0645f 100644
--- a/doc/install.html
+++ b/doc/install.html
@@ -91,7 +91,9 @@ installed as part of
<a href="http://developer.apple.com/TOOLS/Xcode/">Xcode</a>.
</p>
-<p>On Ubuntu/Debian, use <code>sudo apt-get install bison ed gawk gcc libc6-dev make</code>.
+<p>On Ubuntu/Debian, use <code>sudo apt-get install bison ed gawk gcc libc6-dev
+make</code>. If you want to build 32-bit binaries on a 64-bit system you'll
+also need the <code>libc6-dev-i386</code> package.
</p>
<h2 id="mercurial">Install Mercurial, if needed</h2>
@@ -275,7 +277,24 @@ For the full story, consult Go's extensive
<h2 id="releases">Keeping up with releases</h2>
-<p>New releases are announced on the <a href="http://groups.google.com/group/golang-nuts">Go Nuts</a> mailing list.
+<p>
+The Go project maintains two stable tags in its Mercurial repository:
+<code>release</code> and <code>weekly</code>.
+The <code>weekly</code> tag is updated about once a week, and should be used by
+those who want to track the project's development.
+The <code>release</code> tag is given, less often, to those weekly releases
+that have proven themselves to be robust.
+</p>
+
+<p>
+Most Go users will want to keep their Go installation at the latest
+<code>release</code> tag.
+New releases are announced on the
+<a href="http://groups.google.com/group/golang-announce">golang-announce</a>
+mailing list.
+</p>
+
+<p>
To update an existing tree to the latest release, you can run:
</p>
@@ -286,6 +305,10 @@ $ hg update release
$ ./all.bash
</pre>
+<p>
+To use the <code>weekly</code> tag run <code>hg update weekly</code> instead.
+</p>
+
<h2 id="community">Community resources</h2>
<p>
diff --git a/lib/codereview/codereview.py b/lib/codereview/codereview.py
index fa703c711..766e827fc 100644
--- a/lib/codereview/codereview.py
+++ b/lib/codereview/codereview.py
@@ -779,7 +779,7 @@ def Incoming(ui, repo, opts):
_, incoming, _ = findcommonincoming(repo, getremote(ui, repo, opts))
return incoming
-desc_re = '^(.+: |tag release\.|release\.|fix build)'
+desc_re = '^(.+: |(tag )?(release|weekly)\.|fix build)'
desc_msg = '''Your CL description appears not to use the standard form.
@@ -1135,12 +1135,27 @@ def clpatch(ui, repo, clname, **opts):
if missing_codereview:
return missing_codereview
- cl, patch, err = DownloadCL(ui, repo, clname)
+ cl, vers, patch, err = DownloadCL(ui, repo, clname)
if err != "":
return err
if patch == emptydiff:
return "codereview issue %s has no diff" % clname
+ if not repo[vers]:
+ return "codereview issue %s is newer than the current repository; hg sync" % clname
+
+ # find current hg version (hg identify)
+ ctx = repo[None]
+ parents = ctx.parents()
+ id = '+'.join([short(p.node()) for p in parents])
+
+ # if version does not match the patch version,
+ # try to update the patch line numbers.
+ if id != vers:
+ patch, err = portPatch(repo, patch, vers, id)
+ if err != "":
+ return "codereview issue %s is out of date: %s" % (clname, err)
+
argv = ["hgpatch"]
if opts["no_incoming"]:
argv += ["--checksync=false"]
@@ -1163,6 +1178,67 @@ def clpatch(ui, repo, clname, **opts):
cl.Flush(ui, repo)
ui.write(cl.PendingText() + "\n")
+# portPatch rewrites patch from being a patch against
+# oldver to being a patch against newver.
+def portPatch(repo, patch, oldver, newver):
+ lines = patch.splitlines(True) # True = keep \n
+ delta = None
+ for i in range(len(lines)):
+ line = lines[i]
+ if line.startswith('--- a/'):
+ file = line[6:-1]
+ delta = fileDeltas(repo, file, oldver, newver)
+ if not delta or not line.startswith('@@ '):
+ continue
+ # @@ -x,y +z,w @@ means the patch chunk replaces
+ # the original file's line numbers x up to x+y with the
+ # line numbers z up to z+w in the new file.
+ # Find the delta from x in the original to the same
+ # line in the current version and add that delta to both
+ # x and z.
+ m = re.match('@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@', line)
+ if not m:
+ return None, "error parsing patch line numbers"
+ n1, len1, n2, len2 = int(m.group(1)), int(m.group(2)), int(m.group(3)), int(m.group(4))
+ d, err = lineDelta(delta, n1, len1)
+ if err != "":
+ return "", err
+ n1 += d
+ n2 += d
+ lines[i] = "@@ -%d,%d +%d,%d @@\n" % (n1, len1, n2, len2)
+
+ newpatch = ''.join(lines)
+ return newpatch, ""
+
+# fileDelta returns the line number deltas for the given file's
+# changes from oldver to newver.
+# The deltas are a list of (n, len, newdelta) triples that say
+# lines [n, n+len) were modified, and after that range the
+# line numbers are +newdelta from what they were before.
+def fileDeltas(repo, file, oldver, newver):
+ cmd = ["hg", "diff", "--git", "-r", oldver + ":" + newver, "path:" + file]
+ data = RunShell(cmd, silent_ok=True)
+ deltas = []
+ for line in data.splitlines():
+ m = re.match('@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@', line)
+ if not m:
+ continue
+ n1, len1, n2, len2 = int(m.group(1)), int(m.group(2)), int(m.group(3)), int(m.group(4))
+ deltas.append((n1, len1, n2+len2-(n1+len1)))
+ return deltas
+
+# lineDelta finds the appropriate line number delta to apply to the lines [n, n+len).
+# It returns an error if those lines were rewritten by the patch.
+def lineDelta(deltas, n, len):
+ d = 0
+ for (old, oldlen, newdelta) in deltas:
+ if old >= n+len:
+ break
+ if old+len > n:
+ return 0, "patch and recent changes conflict"
+ d = newdelta
+ return d, ""
+
def download(ui, repo, clname, **opts):
"""download a change from the code review server
@@ -1172,7 +1248,7 @@ def download(ui, repo, clname, **opts):
if missing_codereview:
return missing_codereview
- cl, patch, err = DownloadCL(ui, repo, clname)
+ cl, vers, patch, err = DownloadCL(ui, repo, clname)
if err != "":
return err
ui.write(cl.EditorText() + "\n")
@@ -1333,16 +1409,16 @@ def reposetup(ui, repo):
def CheckContributor(ui, repo, user=None):
set_status("checking CONTRIBUTORS file")
- if not user:
- user = ui.config("ui", "username")
- if not user:
- raise util.Abort("[ui] username is not configured in .hgrc")
_, userline = FindContributor(ui, repo, user, warn=False)
if not userline:
raise util.Abort("cannot find %s in CONTRIBUTORS" % (user,))
return userline
-def FindContributor(ui, repo, user, warn=True):
+def FindContributor(ui, repo, user=None, warn=True):
+ if not user:
+ user = ui.config("ui", "username")
+ if not user:
+ raise util.Abort("[ui] username is not configured in .hgrc")
user = user.lower()
m = re.match(r".*<(.*)>", user)
if m:
@@ -1463,7 +1539,7 @@ def submit(ui, repo, *pats, **opts):
# we're committed. upload final patch, close review, add commit message
changeURL = short(node)
url = other.url()
- m = re.match("^https?://([^@/]+@)?([^.]+)\.googlecode\.com/hg/", url)
+ m = re.match("^https?://([^@/]+@)?([^.]+)\.googlecode\.com/hg/?", url)
if m:
changeURL = "http://code.google.com/p/%s/source/detail?r=%s" % (m.group(2), changeURL)
else:
@@ -1558,7 +1634,10 @@ def sync_changes(ui, repo):
cl.files = Sub(cl.files, extra)
cl.Flush(ui, repo)
if not cl.files:
- ui.warn("CL %s has no files; suggest hg change -d %s\n" % (cl.name, cl.name))
+ if not cl.copied_from:
+ ui.warn("CL %s has no files; delete with hg change -d %s\n" % (cl.name, cl.name))
+ else:
+ ui.warn("CL %s has no files; delete locally with hg change -D %s\n" % (cl.name, cl.name))
return
def upload(ui, repo, name, **opts):
@@ -1738,25 +1817,35 @@ def DownloadCL(ui, repo, clname):
set_status("downloading CL " + clname)
cl, err = LoadCL(ui, repo, clname)
if err != "":
- return None, None, "error loading CL %s: %s" % (clname, ExceptionDetail())
+ return None, None, None, "error loading CL %s: %s" % (clname, err)
# Grab RSS feed to learn about CL
feed = XMLGet(ui, "/rss/issue/" + clname)
if feed is None:
- return None, None, "cannot download CL"
+ return None, None, None, "cannot download CL"
# Find most recent diff
diff = None
prefix = 'http://' + server + '/'
- for link in feed.findall("{http://www.w3.org/2005/Atom}entry/{http://www.w3.org/2005/Atom}link"):
- if link.get('rel') != 'alternate':
- continue
- text = link.get('href')
- if not text.startswith(prefix) or not text.endswith('.diff'):
+ vers = ""
+ for entry in feed.findall("{http://www.w3.org/2005/Atom}entry"):
+ thisVers = ""
+ for title in entry.findall("{http://www.w3.org/2005/Atom}title"):
+ m = re.search('diff -r ([0-9a-f]+) ', title.text)
+ if m:
+ thisVers = m.group(1)
+ if thisVers == "":
continue
- diff = text[len(prefix)-1:]
+ for link in entry.findall("{http://www.w3.org/2005/Atom}link"):
+ if link.get('rel') != 'alternate':
+ continue
+ text = link.get('href')
+ if not text.startswith(prefix) or not text.endswith('.diff'):
+ continue
+ diff = text[len(prefix)-1:]
+ vers = thisVers
if diff is None:
- return None, None, "CL has no diff"
+ return None, None, None, "CL has no diff"
diffdata = MySend(diff, force_auth=False)
# Find author - first entry will be author who created CL.
@@ -1765,7 +1854,7 @@ def DownloadCL(ui, repo, clname):
nick = author.text.strip()
break
if not nick:
- return None, None, "CL has no author"
+ return None, None, None, "CL has no author"
# The author is just a nickname: get the real email address.
try:
@@ -1775,7 +1864,7 @@ def DownloadCL(ui, repo, clname):
except:
ui.warn("error looking up %s: %s\n" % (nick, ExceptionDetail()))
cl.copied_from = nick+"@needtofix"
- return cl, diffdata, ""
+ return cl, vers, diffdata, ""
match = re.match(r"<b>(.*) \((.*)\)</b>", data)
if not match:
return None, None, "error looking up %s: cannot parse result %s" % (nick, repr(data))
@@ -1784,10 +1873,12 @@ def DownloadCL(ui, repo, clname):
email = match.group(1)
# Print warning if email is not in CONTRIBUTORS file.
- FindContributor(ui, repo, email)
- cl.copied_from = email
+ him = FindContributor(ui, repo, email)
+ me = FindContributor(ui, repo, None)
+ if him != me:
+ cl.copied_from = email
- return cl, diffdata, ""
+ return cl, vers, diffdata, ""
def MySend(request_path, payload=None,
content_type="application/octet-stream",
@@ -1797,7 +1888,7 @@ def MySend(request_path, payload=None,
try:
return MySend1(request_path, payload, content_type, timeout, force_auth, **kwargs)
except Exception, e:
- if type(e) == urllib2.HTTPError and e.code == 403: # forbidden, it happens
+ if type(e) != urllib2.HTTPError or e.code != 500: # only retry on HTTP 500 error
raise
print >>sys.stderr, "Loading "+request_path+": "+ExceptionDetail()+"; trying again in 2 seconds."
time.sleep(2)
diff --git a/lib/godoc/search.txt b/lib/godoc/search.txt
index eff4d36fc..967c1ac01 100644
--- a/lib/godoc/search.txt
+++ b/lib/godoc/search.txt
@@ -46,9 +46,9 @@ package {Pak.Name}
{.end}
{.section Textual}
{.section Complete}
-{Found} TEXTUAL OCCURENCES
+{Found} TEXTUAL OCCURRENCES
{.or}
-MORE THAN {Found} TEXTUAL OCCURENCES
+MORE THAN {Found} TEXTUAL OCCURRENCES
{.end}
{.repeated section @}
diff --git a/misc/bbedit/Go.plist b/misc/bbedit/Go.plist
index 9dc3bf6f1..45535350a 100755
--- a/misc/bbedit/Go.plist
+++ b/misc/bbedit/Go.plist
@@ -14,7 +14,6 @@
case,
chan,
close,
- closed,
complex,
complex128,
complex64,
@@ -95,7 +94,7 @@
"Open Strings 1" = "`";
"Open Strings 2" = "\"";
"Prefix for Functions" = "func";
- "Prefix for Procedures" = func;
+ "Prefix for Procedures" = "func";
"Terminator for Prototypes 1" = ";";
"Terminator for Prototypes 2" = "";
};
diff --git a/misc/cgo/stdio/Makefile b/misc/cgo/stdio/Makefile
index fc925e607..3f7a4c01c 100644
--- a/misc/cgo/stdio/Makefile
+++ b/misc/cgo/stdio/Makefile
@@ -6,10 +6,7 @@ include ../../../src/Make.inc
TARG=stdio
CGOFILES=\
- align.go\
file.go\
- test.go\
- test1.go\
CLEANFILES+=hello fib chain run.out
diff --git a/misc/cgo/stdio/hello.go b/misc/cgo/stdio/hello.go
index 9cb6e6884..58fc6d574 100644
--- a/misc/cgo/stdio/hello.go
+++ b/misc/cgo/stdio/hello.go
@@ -4,26 +4,8 @@
package main
-import (
- "os"
- "stdio"
-)
+import "stdio"
func main() {
stdio.Stdout.WriteString(stdio.Greeting + "\n")
-
- l := stdio.Atol("123")
- if l != 123 {
- println("Atol 123: ", l)
- panic("bad atol")
- }
-
- n, err := stdio.Strtol("asdf", 123)
- if n != 0 || err != os.EINVAL {
- println("Strtol: ", n, err)
- panic("bad atoi2")
- }
-
- stdio.TestAlign()
- stdio.TestEnum()
}
diff --git a/misc/cgo/test/Makefile b/misc/cgo/test/Makefile
new file mode 100644
index 000000000..893540d97
--- /dev/null
+++ b/misc/cgo/test/Makefile
@@ -0,0 +1,23 @@
+# Copyright 2011 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+include ../../../src/Make.inc
+
+TARG=runtime/cgotest
+
+CGOFILES=\
+ align.go\
+ basic.go\
+ callback.go\
+ issue1222.go\
+ issue1328.go\
+ issue1560.go\
+
+CGO_OFILES=\
+ callback_c.o\
+
+OFILES=\
+ runtime.$O\
+
+include ../../../src/Make.pkg
diff --git a/misc/cgo/stdio/align.go b/misc/cgo/test/align.go
index 6cdfd902f..2d2979595 100644
--- a/misc/cgo/stdio/align.go
+++ b/misc/cgo/test/align.go
@@ -1,4 +1,4 @@
-package stdio
+package cgotest
/*
#include <stdio.h>
@@ -55,24 +55,18 @@ void cTest(SDL_KeyboardEvent *event) {
import "C"
import (
- "fmt"
- "syscall"
+ "testing"
)
-func TestAlign() {
- if syscall.ARCH == "amd64" {
- // alignment is known to be broken on amd64.
- // http://code.google.com/p/go/issues/detail?id=609
- return
- }
+func TestAlign(t *testing.T) {
var evt C.SDL_KeyboardEvent
C.makeEvent(&evt)
if C.same(&evt, evt.typ, evt.which, evt.state, evt.keysym.scancode, evt.keysym.sym, evt.keysym.mod, evt.keysym.unicode) == 0 {
- fmt.Println("*** bad alignment")
+ t.Error("*** bad alignment")
C.cTest(&evt)
- fmt.Printf("Go: %#x %#x %#x %#x %#x %#x %#x\n",
+ t.Errorf("Go: %#x %#x %#x %#x %#x %#x %#x\n",
evt.typ, evt.which, evt.state, evt.keysym.scancode,
evt.keysym.sym, evt.keysym.mod, evt.keysym.unicode)
- fmt.Println(evt)
+ t.Error(evt)
}
}
diff --git a/misc/cgo/stdio/test.go b/misc/cgo/test/basic.go
index 8f21603ca..a94074c52 100644
--- a/misc/cgo/stdio/test.go
+++ b/misc/cgo/test/basic.go
@@ -2,9 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// This file contains test cases for cgo.
+// Basic test cases for cgo.
-package stdio
+package cgotest
/*
#include <stdio.h>
@@ -52,6 +52,7 @@ struct ibv_context {
import "C"
import (
"os"
+ "testing"
"unsafe"
)
@@ -89,38 +90,35 @@ func Atol(s string) int {
return int(n)
}
-func TestConst() {
+func TestConst(t *testing.T) {
C.myConstFunc(nil, 0, nil)
}
-func TestEnum() {
+func TestEnum(t *testing.T) {
if C.Enum1 != 1 || C.Enum2 != 2 {
- println("bad enum", C.Enum1, C.Enum2)
+ t.Error("bad enum", C.Enum1, C.Enum2)
}
}
-func TestAtol() {
+func TestAtol(t *testing.T) {
l := Atol("123")
if l != 123 {
- println("Atol 123: ", l)
- panic("bad atol")
+ t.Error("Atol 123: ", l)
}
}
-func TestErrno() {
+func TestErrno(t *testing.T) {
n, err := Strtol("asdf", 123)
if n != 0 || err != os.EINVAL {
- println("Strtol: ", n, err)
- panic("bad strtol")
+ t.Error("Strtol: ", n, err)
}
}
-func TestMultipleAssign() {
- p := C.CString("123")
+func TestMultipleAssign(t *testing.T) {
+ p := C.CString("234")
n, m := C.strtol(p, nil, 345), C.strtol(p, nil, 10)
if n != 0 || m != 234 {
- println("Strtol x2: ", n, m)
- panic("bad strtol x2")
+ t.Fatal("Strtol x2: ", n, m)
}
C.free(unsafe.Pointer(p))
}
@@ -134,11 +132,3 @@ var (
type Context struct {
ctx *C.struct_ibv_context
}
-
-func Test() {
- TestAlign()
- TestAtol()
- TestEnum()
- TestErrno()
- TestConst()
-}
diff --git a/misc/cgo/test/callback.go b/misc/cgo/test/callback.go
new file mode 100644
index 000000000..450a7cbf2
--- /dev/null
+++ b/misc/cgo/test/callback.go
@@ -0,0 +1,136 @@
+package cgotest
+
+/*
+void callback(void *f);
+void callGoFoo(void) {
+ extern void goFoo(void);
+ goFoo();
+}
+*/
+import "C"
+
+import (
+ "runtime"
+ "testing"
+ "unsafe"
+)
+
+// nestedCall calls into C, back into Go, and finally to f.
+func nestedCall(f func()) {
+ // NOTE: Depends on representation of f.
+ // callback(x) calls goCallback(x)
+ C.callback(*(*unsafe.Pointer)(unsafe.Pointer(&f)))
+}
+
+//export goCallback
+func goCallback(p unsafe.Pointer) {
+ (*(*func())(unsafe.Pointer(&p)))()
+}
+
+func TestCallback(t *testing.T) {
+ var x = false
+ nestedCall(func() { x = true })
+ if !x {
+ t.Fatal("nestedCall did not call func")
+ }
+}
+
+func TestCallbackGC(t *testing.T) {
+ nestedCall(runtime.GC)
+}
+
+func lockedOSThread() bool // in runtime.c
+
+func TestCallbackPanic(t *testing.T) {
+ // Make sure panic during callback unwinds properly.
+ if lockedOSThread() {
+ t.Fatal("locked OS thread on entry to TestCallbackPanic")
+ }
+ defer func() {
+ s := recover()
+ if s == nil {
+ t.Fatal("did not panic")
+ }
+ if s.(string) != "callback panic" {
+ t.Fatal("wrong panic:", s)
+ }
+ if lockedOSThread() {
+ t.Fatal("locked OS thread on exit from TestCallbackPanic")
+ }
+ }()
+ nestedCall(func() { panic("callback panic") })
+ panic("nestedCall returned")
+}
+
+func TestCallbackPanicLoop(t *testing.T) {
+ // Make sure we don't blow out m->g0 stack.
+ for i := 0; i < 100000; i++ {
+ TestCallbackPanic(t)
+ }
+}
+
+func TestCallbackPanicLocked(t *testing.T) {
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ if !lockedOSThread() {
+ t.Fatal("runtime.LockOSThread didn't")
+ }
+ defer func() {
+ s := recover()
+ if s == nil {
+ t.Fatal("did not panic")
+ }
+ if s.(string) != "callback panic" {
+ t.Fatal("wrong panic:", s)
+ }
+ if !lockedOSThread() {
+ t.Fatal("lost lock on OS thread after panic")
+ }
+ }()
+ nestedCall(func() { panic("callback panic") })
+ panic("nestedCall returned")
+}
+
+// Callback with zero arguments used to make the stack misaligned,
+// which broke the garbage collector and other things.
+func TestZeroArgCallback(t *testing.T) {
+ defer func() {
+ s := recover()
+ if s != nil {
+ t.Fatal("panic during callback:", s)
+ }
+ }()
+ C.callGoFoo()
+}
+
+//export goFoo
+func goFoo() {
+ x := 1
+ for i := 0; i < 10000; i++ {
+ // variadic call mallocs + writes to
+ variadic(x, x, x)
+ if x != 1 {
+ panic("bad x")
+ }
+ }
+}
+
+func variadic(x ...interface{}) {}
+
+func TestBlocking(t *testing.T) {
+ c := make(chan int)
+ go func() {
+ for i := 0; i < 10; i++ {
+ c <- <-c
+ }
+ }()
+ nestedCall(func() {
+ for i := 0; i < 10; i++ {
+ c <- i
+ if j := <-c; j != i {
+ t.Errorf("out of sync %d != %d", j, i)
+ }
+ }
+ })
+}
diff --git a/misc/cgo/test/callback_c.c b/misc/cgo/test/callback_c.c
new file mode 100644
index 000000000..5983a5e11
--- /dev/null
+++ b/misc/cgo/test/callback_c.c
@@ -0,0 +1,12 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include <sys/types.h>
+#include "_cgo_export.h"
+
+void
+callback(void *f)
+{
+ goCallback(f);
+}
diff --git a/misc/cgo/test/cgo_test.go b/misc/cgo/test/cgo_test.go
new file mode 100644
index 000000000..967dc0e92
--- /dev/null
+++ b/misc/cgo/test/cgo_test.go
@@ -0,0 +1,5 @@
+package cgotest
+
+// dummy file so gotest thinks there are tests.
+// the actual tests are in the main go files, next
+// to the code they test.
diff --git a/misc/cgo/stdio/test1.go b/misc/cgo/test/issue1222.go
index dce2ef83c..c396a0c41 100644
--- a/misc/cgo/stdio/test1.go
+++ b/misc/cgo/test/issue1222.go
@@ -4,7 +4,7 @@
// This file contains test cases for cgo.
-package stdio
+package cgotest
/*
// issue 1222
diff --git a/misc/cgo/test/issue1328.go b/misc/cgo/test/issue1328.go
new file mode 100644
index 000000000..f29d7057e
--- /dev/null
+++ b/misc/cgo/test/issue1328.go
@@ -0,0 +1,30 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cgotest
+
+import "testing"
+
+// extern void BackIntoGo(void);
+// void IntoC() { BackIntoGo(); }
+import "C"
+
+//export BackIntoGo
+func BackIntoGo() {
+ x := 1
+
+ for i := 0; i < 10000; i++ {
+ xvariadic(x)
+ if x != 1 {
+ panic("x is not 1?")
+ }
+ }
+}
+
+func xvariadic(x ...interface{}) {
+}
+
+func Test1328(t *testing.T) {
+ C.IntoC()
+}
diff --git a/misc/cgo/test/issue1560.go b/misc/cgo/test/issue1560.go
new file mode 100644
index 000000000..75d31c035
--- /dev/null
+++ b/misc/cgo/test/issue1560.go
@@ -0,0 +1,46 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cgotest
+
+/*
+#include <unistd.h>
+
+extern void BackgroundSleep(int);
+void twoSleep(int n) {
+ BackgroundSleep(n);
+ sleep(n);
+}
+*/
+import "C"
+
+import (
+ "testing"
+ "time"
+)
+
+var sleepDone = make(chan bool)
+
+func parallelSleep(n int) {
+ C.twoSleep(C.int(n))
+ <-sleepDone
+}
+
+//export BackgroundSleep
+func BackgroundSleep(n int) {
+ go func() {
+ C.sleep(C.uint(n))
+ sleepDone <- true
+ }()
+}
+
+func TestParallelSleep(t *testing.T) {
+ dt := -time.Nanoseconds()
+ parallelSleep(1)
+ dt += time.Nanoseconds()
+ // bug used to run sleeps in serial, producing a 2-second delay.
+ if dt >= 1.3e9 {
+ t.Fatalf("parallel 1-second sleeps slept for %f seconds", float64(dt)/1e9)
+ }
+}
diff --git a/misc/cgo/test/runtime.c b/misc/cgo/test/runtime.c
new file mode 100644
index 000000000..e087c7622
--- /dev/null
+++ b/misc/cgo/test/runtime.c
@@ -0,0 +1,21 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Expose some runtime functions for testing.
+
+typedef char bool;
+
+bool runtime·lockedOSThread(void);
+
+static void
+FLUSH(void*)
+{
+}
+
+void
+·lockedOSThread(bool b)
+{
+ b = runtime·lockedOSThread();
+ FLUSH(&b);
+}
diff --git a/misc/dashboard/README b/misc/dashboard/README
index 72d5546a4..c00311ef7 100644
--- a/misc/dashboard/README
+++ b/misc/dashboard/README
@@ -4,28 +4,12 @@
The files in this directory constitute the continuous builder:
-godashboard/: An AppEngine that acts as a server
-builder.sh, buildcontrol.sh: used by the build slaves
-buildcron.sh: a build loop that can be run regularly via cron
+godashboard/: an AppEngine server
+builder/: gobuilder, a Go continuous build client
If you wish to run a Go builder, please email golang-dev@googlegroups.com
-
-To set up a Go builder automatically, run buildcron.sh
-(you might want to read it first to see what it does).
-
-To set up a Go builder by hand:
-
-* (Optional) create a new user 'gobuild'
-* Edit ~gobuild/.bash_profile and add the following:
-
-export GOROOT=/gobuild/go
-export GOARCH=XXX
-export GOOS=XXX
-export GOBIN=/gobuild/bin
-export PATH=$PATH:/gobuild/bin
-export BUILDER=$GOOS-$GOARCH
-export BUILDHOST=godashboard.appspot.com
+To run a builder:
* Write the key ~gobuild/.gobuildkey
You need to get it from someone who knows the key.
@@ -38,13 +22,5 @@ export BUILDHOST=godashboard.appspot.com
(This is for uploading tarballs to the project downloads section,
and is an optional step.)
-* sudo apt-get install bison gcc libc6-dev ed make
-* cd ~gobuild
-* mkdir bin
-* hg clone https://go.googlecode.com/hg/ $GOROOT
-* copy builder.sh and buildcontrol.py to ~gobuild
-* chmod a+x ./builder.sh ./buildcontrol.py
-* cd go
-* ../buildcontrol.py next $BUILDER (just to check that things are ok)
-* cd ..
-* ./builder.sh (You probably want to run this in a screen long term.)
+* Build and run gobuilder (see its documentation for command-line options).
+
diff --git a/misc/dashboard/buildcontrol.py b/misc/dashboard/buildcontrol.py
deleted file mode 100644
index ec503e7ff..000000000
--- a/misc/dashboard/buildcontrol.py
+++ /dev/null
@@ -1,278 +0,0 @@
-#!/usr/bin/env python
-
-# Copyright 2009 The Go Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file.
-
-# This is a utility script for implementing a Go build slave.
-
-import binascii
-import httplib
-import os
-import struct
-import subprocess
-import sys
-import time
-
-buildhost = ''
-buildport = -1
-buildkey = ''
-
-upload_project = "go"
-
-def main(args):
- global buildport, buildhost, buildkey
-
- if len(args) < 2:
- return usage(args[0])
-
- if 'BUILDHOST' not in os.environ:
- print >>sys.stderr, "Please set $BUILDHOST"
- return
- buildhost = os.environ['BUILDHOST']
-
- if 'BUILDPORT' not in os.environ:
- buildport = 80
- else:
- buildport = int(os.environ['BUILDPORT'])
-
- try:
- buildkeyfile = file('%s/.gobuildkey-%s' % (os.environ['HOME'], os.environ['BUILDER']), 'r')
- buildkey = buildkeyfile.readline().strip()
- except IOError:
- try:
- buildkeyfile = file('%s/.gobuildkey' % os.environ['HOME'], 'r')
- buildkey = buildkeyfile.readline().strip()
- except IOError:
- print >>sys.stderr, "Need key in ~/.gobuildkey-%s or ~/.gobuildkey" % os.environ['BUILDER']
- return
-
- # get upload credentials
- try:
- username = buildkeyfile.readline().strip()
- password = buildkeyfile.readline().strip()
- except:
- username, password = None, None
-
- if args[1] == 'init':
- return doInit(args)
- elif args[1] == 'hwget':
- return doHWGet(args)
- elif args[1] == 'hwset':
- return doHWSet(args)
- elif args[1] == 'next':
- return doNext(args)
- elif args[1] == 'record':
- return doRecord(args)
- elif args[1] == 'benchmarks':
- return doBenchmarks(args)
- elif args[1] == 'upload':
- return doUpload(args, username, password)
- else:
- return usage(args[0])
-
-def usage(name):
- sys.stderr.write('''Usage: %s <command>
-
-Commands:
- init <rev>: init the build bot with the given commit as the first in history
- hwget <builder>: get the most recent revision built by the given builder
- hwset <builder> <rev>: get the most recent revision built by the given builder
- next <builder>: get the next revision number to by built by the given builder
- record <builder> <rev> <ok|log file>: record a build result
- benchmarks <builder> <rev> <log file>: record benchmark numbers
- upload <builder> <summary> <tar file>: upload tarball to googlecode
-''' % name)
- return 1
-
-def doInit(args):
- if len(args) != 3:
- return usage(args[0])
- c = getCommit(args[2])
- if c is None:
- fatal('Cannot get commit %s' % args[2])
-
- return command('init', {'node': c.node, 'date': c.date, 'user': c.user, 'desc': c.desc})
-
-def doHWGet(args, retries = 0):
- if len(args) != 3:
- return usage(args[0])
- conn = httplib.HTTPConnection(buildhost, buildport, True)
- conn.request('GET', '/hw-get?builder=%s' % args[2]);
- reply = conn.getresponse()
- if reply.status == 200:
- print reply.read()
- elif reply.status == 500 and retries < 3:
- time.sleep(3)
- return doHWGet(args, retries = retries + 1)
- else:
- raise Failed('get-hw returned %d' % reply.status)
- return 0
-
-def doHWSet(args):
- if len(args) != 4:
- return usage(args[0])
- c = getCommit(args[3])
- if c is None:
- fatal('Cannot get commit %s' % args[3])
-
- return command('hw-set', {'builder': args[2], 'hw': c.node})
-
-def doNext(args):
- if len(args) != 3:
- return usage(args[0])
- conn = httplib.HTTPConnection(buildhost, buildport, True)
- conn.request('GET', '/hw-get?builder=%s' % args[2]);
- reply = conn.getresponse()
- if reply.status == 200:
- rev = reply.read()
- else:
- raise Failed('get-hw returned %d' % reply.status)
-
- c = getCommit(rev)
- next = getCommit(str(c.num + 1))
- if next is not None and next.parent == c.node:
- print c.num + 1
- else:
- print "<none>"
- return 0
-
-def doRecord(args):
- if len(args) != 5:
- return usage(args[0])
- builder = args[2]
- rev = args[3]
- c = getCommit(rev)
- if c is None:
- print >>sys.stderr, "Bad revision:", rev
- return 1
- logfile = args[4]
- log = ''
- if logfile != 'ok':
- log = file(logfile, 'r').read()
- return command('build', {'node': c.node, 'parent': c.parent, 'date': c.date, 'user': c.user, 'desc': c.desc, 'log': log, 'builder': builder})
-
-def doBenchmarks(args):
- if len(args) != 5:
- return usage(args[0])
- builder = args[2]
- rev = args[3]
- c = getCommit(rev)
- if c is None:
- print >>sys.stderr, "Bad revision:", rev
- return 1
-
- benchmarks = {}
- for line in file(args[4], 'r').readlines():
- if 'Benchmark' in line and 'ns/op' in line:
- parts = line.split()
- if parts[3] == 'ns/op':
- benchmarks[parts[0]] = (parts[1], parts[2])
-
- e = []
- for (name, (a, b)) in benchmarks.items():
- e.append(struct.pack('>H', len(name)))
- e.append(name)
- e.append(struct.pack('>H', len(a)))
- e.append(a)
- e.append(struct.pack('>H', len(b)))
- e.append(b)
- return command('benchmarks', {'node': c.node, 'builder': builder, 'benchmarkdata': binascii.b2a_base64(''.join(e))})
-
-def doUpload(args, username, password):
- # fail gracefully if no username or password set
- if not username or not password:
- return
-
- if len(args) != 5:
- return usage(args[0])
- builder = args[2]
- summary = args[3]
- filename = args[4]
-
- from googlecode_upload import upload
- code, msg, url = upload(
- filename, # filename
- upload_project, # 'go'
- username,
- password,
- summary,
- builder.split('-'), # labels
- )
- if code != 201:
- raise Failed('Upload returned code %s msg "%s".' % (code, msg))
-
-def encodeMultipartFormdata(fields, files):
- """fields is a sequence of (name, value) elements for regular form fields.
- files is a sequence of (name, filename, value) elements for data to be uploaded as files"""
- BOUNDARY = '----------ThIs_Is_tHe_bouNdaRY_$'
- CRLF = '\r\n'
- L = []
- for (key, value) in fields.items():
- L.append('--' + BOUNDARY)
- L.append('Content-Disposition: form-data; name="%s"' % key)
- L.append('')
- L.append(value)
- for (key, filename, value) in files:
- L.append('--' + BOUNDARY)
- L.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename))
- L.append('Content-Type: %s' % get_content_type(filename))
- L.append('')
- L.append(value)
- L.append('--' + BOUNDARY + '--')
- L.append('')
- body = CRLF.join(L)
- content_type = 'multipart/form-data; boundary=%s' % BOUNDARY
- return content_type, body
-
-def unescapeXML(s):
- return s.replace('&lt;', '<').replace('&gt;', '>').replace('&amp;', '&')
-
-class Commit:
- pass
-
-def getCommit(rev):
- output, stderr = subprocess.Popen(['hg', 'log', '-r', rev, '-l', '1', '--template', '{rev}>{node|escape}>{author|escape}>{date}>{desc}'], stdout = subprocess.PIPE, stderr = subprocess.PIPE, close_fds = True).communicate()
- if len(stderr) > 0:
- return None
- [n, node, user, date, desc] = output.split('>', 4)
-
- c = Commit()
- c.num = int(n)
- c.node = unescapeXML(node)
- c.user = unescapeXML(user)
- c.date = unescapeXML(date)
- c.desc = desc
- c.parent = ''
-
- if c.num > 0:
- output, _ = subprocess.Popen(['hg', 'log', '-r', str(c.num - 1), '-l', '1', '--template', '{node}'], stdout = subprocess.PIPE, close_fds = True).communicate()
- c.parent = output
-
- return c
-
-class Failed(Exception):
- def __init__(self, msg):
- self.msg = msg
- def __str__(self):
- return self.msg
-
-def command(cmd, args, retries = 0):
- args['key'] = buildkey
- contentType, body = encodeMultipartFormdata(args, [])
- print body
- conn = httplib.HTTPConnection(buildhost, buildport, True)
- conn.request('POST', '/' + cmd, body, {'Content-Type': contentType})
- reply = conn.getresponse()
- if reply.status != 200:
- print "Command failed. Output:"
- print reply.read()
- if reply.status == 500 and retries < 3:
- print "Was a 500. Waiting two seconds and trying again."
- time.sleep(2)
- return command(cmd, args, retries = retries + 1)
- if reply.status != 200:
- raise Failed('Command "%s" returned %d' % (cmd, reply.status))
-
-if __name__ == '__main__':
- sys.exit(main(sys.argv))
diff --git a/misc/dashboard/buildcron.sh b/misc/dashboard/buildcron.sh
deleted file mode 100644
index 7aa70ce57..000000000
--- a/misc/dashboard/buildcron.sh
+++ /dev/null
@@ -1,58 +0,0 @@
-#!/usr/bin/env bash
-# Copyright 2010 The Go Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file.
-
-# This script can be run to create a new builder and then
-# to keep it running via cron. First, run it by hand until it
-# starts up without errors and can run the loop. Then, once
-# you're confident that it works, add this to your crontab:
-#
-# */5 * * * * cd $HOME; path/to/buildcron.sh darwin 386 >/dev/null 2>/dev/null
-
-if [ $# != 2 ]; then
- echo 'usage: buildcron.sh goos goarch' 1>&2
- exit 2
-fi
-
-export GOOS=$1
-export GOARCH=$2
-
-# Check if we are already running.
-# First command must not be pipeline, to avoid seeing extra processes in ps.
-all=$(ps axwwu)
-pid=$(echo "$all" | grep "buildcron.sh $1 $2" | grep -v "sh -c" | grep -v $$ | awk '{print $2}')
-if [ "$pid" != "" ]; then
- #echo already running buildcron.sh $1 $2
- #echo "$all" | grep "buildcron.sh $1 $2" | grep -v "sh -c" | grep -v $$
- exit 0
-fi
-
-export BUILDHOST=godashboard.appspot.com
-export BUILDER=${GOOS}-${GOARCH}
-export GOROOT=$HOME/go-$BUILDER/go
-export GOBIN=$HOME/go-$BUILDER/bin
-
-if [ ! -f ~/.gobuildkey-$BUILDER ]; then
- echo "need gobuildkey for $BUILDER in ~/.gobuildkey-$BUILDER" 1>&2
- exit 2
-fi
-
-if [ ! -d $GOROOT ]; then
- mkdir -p $GOROOT
- hg clone https://go.googlecode.com/hg/ $GOROOT
-else
- cd $GOROOT
- hg pull -u || exit 1
-fi
-mkdir -p $GOROOT/bin
-
-cd $GOROOT/..
-cp go/misc/dashboard/{builder.sh,buildcontrol.py,googlecode_upload.py} .
-chmod a+x builder.sh buildcontrol.py
-cd go
-../buildcontrol.py next $BUILDER
-cd ..
-./builder.sh
-
-
diff --git a/misc/dashboard/builder.sh b/misc/dashboard/builder.sh
deleted file mode 100644
index 4a8d117bf..000000000
--- a/misc/dashboard/builder.sh
+++ /dev/null
@@ -1,95 +0,0 @@
-#!/usr/bin/env bash
-
-# Copyright 2009 The Go Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file.
-
-fatal() {
- echo $0: $1 1>&2
- exit 1
-}
-
-if [ ! -d go ] ; then
- fatal "Please run in directory that contains a checked out repo in 'go'"
-fi
-
-if [ ! -f buildcontrol.py ] ; then
- fatal 'Please include buildcontrol.py in this directory'
-fi
-
-if [ "x$BUILDER" == "x" ] ; then
- fatal 'Please set $BUILDER to the name of this builder'
-fi
-
-if [ "x$BUILDHOST" == "x" ] ; then
- fatal 'Please set $BUILDHOST to the hostname of the gobuild server'
-fi
-
-if [ "x$GOARCH" == "x" -o "x$GOOS" == "x" ] ; then
- fatal 'Please set $GOARCH and $GOOS'
-fi
-
-export PATH=$PATH:`pwd`/candidate/bin
-export GOBIN=`pwd`/candidate/bin
-export GOROOT_FINAL=/usr/local/go
-
-while true ; do (
- cd go || fatal "Cannot cd into 'go'"
- hg pull -u || fatal "hg sync failed"
- rev=`python ../buildcontrol.py next $BUILDER`
- if [ $? -ne 0 ] ; then
- fatal "Cannot get next revision"
- fi
- cd .. || fatal "Cannot cd up"
- if [ "x$rev" == "x<none>" ] ; then
- sleep 10
- continue
- fi
-
- echo "Cloning for revision $rev"
- rm -Rf candidate
- hg clone -r $rev go candidate || fatal "hg clone failed"
- export GOROOT=`pwd`/candidate
- mkdir -p candidate/bin || fatal "Cannot create candidate/bin"
- cd candidate/src || fatal "Cannot cd into candidate/src"
- echo "Building revision $rev"
- ALL=all.bash
- if [ -f all-$GOOS.bash ]; then
- ALL=all-$GOOS.bash
- elif [ -f all-$GOARCH.bash ]; then
- ALL=all-$GOARCH.bash
- fi
- ./$ALL > ../log 2>&1
- if [ $? -ne 0 ] ; then
- echo "Recording failure for $rev"
- python ../../buildcontrol.py record $BUILDER $rev ../log || fatal "Cannot record result"
- else
- echo "Recording success for $rev"
- python ../../buildcontrol.py record $BUILDER $rev ok || fatal "Cannot record result"
- if [ "$ALL" = "all.bash" ]; then
- echo "Running benchmarks"
- cd pkg || fatal "failed to cd to pkg"
- make bench > ../../benchmarks 2>&1
- python ../../../buildcontrol.py benchmarks $BUILDER $rev ../../benchmarks || fatal "Cannot record benchmarks"
- cd .. || fatal "failed to cd out of pkg"
- fi
- # check if we're at a release (via the hg summary)
- # if so, package the tar.gz and upload to googlecode
- SUMMARY=$(hg log -l 1 | grep summary\: | awk '{print $2}')
- if [[ "x${SUMMARY:0:7}" == "xrelease" ]]; then
- echo "Uploading binary to googlecode"
- TARBALL="go.$SUMMARY.$BUILDER.tar.gz"
- ./clean.bash --nopkg
- # move contents of candidate/ to candidate/go/ for archival
- cd ../.. || fatal "Cannot cd up"
- mv candidate go-candidate || fatal "Cannot rename candidate"
- mkdir candidate || fatal "Cannot mkdir candidate"
- mv go-candidate candidate/go || fatal "Cannot mv directory"
- cd candidate || fatal "Cannot cd candidate"
- # build tarball
- tar czf ../$TARBALL go || fatal "Cannot create tarball"
- ../buildcontrol.py upload $BUILDER $SUMMARY ../$TARBALL || fatal "Cannot upload tarball"
- fi
- fi
- sleep 10
-) done
diff --git a/misc/dashboard/builder/doc.go b/misc/dashboard/builder/doc.go
index a28658a95..7bb7ccbe3 100644
--- a/misc/dashboard/builder/doc.go
+++ b/misc/dashboard/builder/doc.go
@@ -39,7 +39,7 @@ Optional flags:
-release: Build and deliver binary release archive
-rev=N: Build revision N and exit
-
+
-cmd="./all.bash": Build command (specify absolute or relative to go/src)
-v: Verbose logging
@@ -47,8 +47,8 @@ Optional flags:
-external: External package builder mode (will not report Go build
state to dashboard, issue releases, or run benchmarks)
-The key file should be located at $HOME/.gobuilder or, for a builder-specific
-key, $HOME/.gobuilder-$BUILDER (eg, $HOME/.gobuilder-linux-amd64).
+The key file should be located at $HOME/.gobuildkey or, for a builder-specific
+key, $HOME/.gobuildkey-$BUILDER (eg, $HOME/.gobuildkey-linux-amd64).
The build key file is a text file of the format:
diff --git a/misc/dashboard/builder/exec.go b/misc/dashboard/builder/exec.go
index 53ea93ac5..3c6fbdced 100644
--- a/misc/dashboard/builder/exec.go
+++ b/misc/dashboard/builder/exec.go
@@ -49,7 +49,7 @@ func runLog(envv []string, logfile, dir string, argv ...string) (output string,
b := new(bytes.Buffer)
var w io.Writer = b
if logfile != "" {
- f, err := os.Open(logfile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
+ f, err := os.OpenFile(logfile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
return
}
diff --git a/misc/dashboard/builder/main.go b/misc/dashboard/builder/main.go
index fc11d365e..735717e28 100644
--- a/misc/dashboard/builder/main.go
+++ b/misc/dashboard/builder/main.go
@@ -64,7 +64,7 @@ var (
var (
goroot string
- releaseRegexp = regexp.MustCompile(`^release\.[0-9\-.]+`)
+ releaseRegexp = regexp.MustCompile(`^(release|weekly)\.[0-9\-.]+`)
benchRequests vector.Vector
)
diff --git a/misc/dashboard/godashboard/gobuild.py b/misc/dashboard/godashboard/gobuild.py
index 08d70ec64..1eacdb38e 100644
--- a/misc/dashboard/godashboard/gobuild.py
+++ b/misc/dashboard/godashboard/gobuild.py
@@ -7,7 +7,6 @@
from google.appengine.api import mail
from google.appengine.api import memcache
-from google.appengine.runtime import DeadlineExceededError
from google.appengine.ext import db
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
@@ -219,7 +218,7 @@ class SetHighwater(webapp.RequestHandler):
q = Commit.all()
q.order('-__key__')
recent = q.fetch(N+1)
- for c in head:
+ for c in recent:
if c.node == newhw:
found = True
break
@@ -384,7 +383,6 @@ class Benchmarks(webapp.RequestHandler):
self.response.headers['Content-Type'] = 'text/plain; charset=utf-8'
self.response.out.write('{"benchmarks": [')
- first = True
sep = "\n\t"
for b in bs:
self.response.out.write('%s"%s"' % (sep, b.name))
diff --git a/misc/dashboard/godashboard/package.py b/misc/dashboard/godashboard/package.py
index 7570d2218..a1bca1908 100644
--- a/misc/dashboard/godashboard/package.py
+++ b/misc/dashboard/godashboard/package.py
@@ -6,7 +6,6 @@
# It must be run by App Engine.
from google.appengine.api import memcache
-from google.appengine.runtime import DeadlineExceededError
from google.appengine.ext import db
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
@@ -14,15 +13,10 @@ from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.api import users
from google.appengine.api import mail
from google.appengine.api import urlfetch
-import binascii
import datetime
-import hashlib
-import hmac
import logging
import os
import re
-import struct
-import time
import urllib2
import sets
@@ -52,6 +46,8 @@ class Project(db.Model):
re_bitbucket = re.compile(r'^bitbucket\.org/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+$')
re_googlecode = re.compile(r'^[a-z0-9\-]+\.googlecode\.com/(svn|hg)$')
re_github = re.compile(r'^github\.com/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+$')
+re_launchpad = re.compile(r'^launchpad\.net/([a-z0-9A-Z_.\-]+(/[a-z0-9A-Z_.\-]+)?|~[a-z0-9A-Z_.\-]+/(\+junk|[a-z0-9A-Z_.\-]+)/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]+)?$')
+
def vc_to_web(path):
if re_bitbucket.match(path):
@@ -65,6 +61,8 @@ def vc_to_web(path):
elif re_googlecode.match(path):
check_url = 'http://'+path
web = 'http://code.google.com/p/' + path[:path.index('.')]
+ elif re_launchpad.match(path):
+ check_url = web = 'https://'+path
else:
return False, False
return web, check_url
@@ -72,7 +70,8 @@ def vc_to_web(path):
re_bitbucket_web = re.compile(r'bitbucket\.org/([a-z0-9A-Z_.\-]+)/([a-z0-9A-Z_.\-]+)')
re_googlecode_web = re.compile(r'code.google.com/p/([a-z0-9\-]+)')
re_github_web = re.compile(r'github\.com/([a-z0-9A-Z_.\-]+)/([a-z0-9A-Z_.\-]+)')
-re_striphttp = re.compile(r'http://(www\.)?')
+re_launchpad_web = re.compile(r'launchpad\.net/([a-z0-9A-Z_.\-]+(/[a-z0-9A-Z_.\-]+)?|~[a-z0-9A-Z_.\-]+/(\+junk|[a-z0-9A-Z_.\-]+)/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]+)?')
+re_striphttp = re.compile(r'https?://(www\.)?')
def web_to_vc(url):
url = re_striphttp.sub('', url)
@@ -93,6 +92,9 @@ def web_to_vc(url):
vcs = 'hg'
except: pass
return path + vcs
+ m = re_launchpad_web.match(url)
+ if m:
+ return m.group(0)
return False
MaxPathLength = 100
@@ -136,7 +138,7 @@ class PackagePage(webapp.RequestHandler):
sep = ','
s += '\n]}\n'
json = s
- memcache.set('view-package-json', json, time=CacheTimeoout)
+ memcache.set('view-package-json', json, time=CacheTimeout)
self.response.out.write(json)
def can_get_url(self, url):
@@ -150,7 +152,8 @@ class PackagePage(webapp.RequestHandler):
def is_valid_package_path(self, path):
return (re_bitbucket.match(path) or
re_googlecode.match(path) or
- re_github.match(path))
+ re_github.match(path) or
+ re_launchpad.match(path))
def record_pkg(self, path):
# sanity check string
diff --git a/misc/emacs/go-mode-load.el b/misc/emacs/go-mode-load.el
index c73156317..0ace46dfa 100644
--- a/misc/emacs/go-mode-load.el
+++ b/misc/emacs/go-mode-load.el
@@ -18,10 +18,11 @@
;; (let ((generated-autoload-file buffer-file-name)) (update-file-autoloads "go-mode.el"))
-;;;### (autoloads (go-mode) "go-mode" "go-mode.el" (19168 32439))
+;;;### (autoloads (gofmt-before-save gofmt go-mode) "go-mode" "go-mode.el"
+;;;;;; (19847 61431))
;;; Generated autoloads from go-mode.el
-(autoload (quote go-mode) "go-mode" "\
+(autoload 'go-mode "go-mode" "\
Major mode for editing Go source text.
This provides basic syntax highlighting for keywords, built-ins,
@@ -30,7 +31,19 @@ functions, and some types. It also provides indentation that is
\(fn)" t nil)
-(add-to-list (quote auto-mode-alist) (cons "\\.go$" (function go-mode)))
+(add-to-list 'auto-mode-alist (cons "\\.go$" #'go-mode))
+
+(autoload 'gofmt "go-mode" "\
+Pipe the current buffer through the external tool `gofmt`.
+Replace the current buffer on success; display errors on failure.
+
+\(fn)" t nil)
+
+(autoload 'gofmt-before-save "go-mode" "\
+Add this to .emacs to run gofmt on the current buffer when saving:
+ (add-hook 'before-save-hook #'gofmt-before-save)
+
+\(fn)" t nil)
;;;***
diff --git a/misc/emacs/go-mode.el b/misc/emacs/go-mode.el
index 2624e87cb..692cabfe5 100644
--- a/misc/emacs/go-mode.el
+++ b/misc/emacs/go-mode.el
@@ -498,21 +498,45 @@ Useful for development work."
(require 'go-mode)
(go-mode))
-(provide 'go-mode)
-
+;;;###autoload
(defun gofmt ()
- "Pipe the current buffer through the external tool `gofmt`."
-
- (interactive)
- ;; for some reason save-excursion isn't working
- ;; probably because shell-command-on-region deletes the contents of the
- ;; region before filling in the new values
- ;; so we will save the point/mark by hand
- ;; similarly we can't use push-mark/pop-mark
- (let ((old-mark (mark t)) (old-point (point)))
- (save-restriction
- (let (deactivate-mark)
- (widen)
- (shell-command-on-region (point-min) (point-max) "gofmt" t t shell-command-default-error-buffer)))
- (goto-char (min old-point (point-max)))
- (if old-mark (set-mark (min old-mark (point-max))))))
+ "Pipe the current buffer through the external tool `gofmt`.
+Replace the current buffer on success; display errors on failure."
+
+ (interactive)
+ (let ((srcbuf (current-buffer)))
+ (with-temp-buffer
+ (let ((outbuf (current-buffer))
+ (errbuf (get-buffer-create "*Gofmt Errors*")))
+ (with-current-buffer errbuf (erase-buffer))
+ (with-current-buffer srcbuf
+ (save-restriction
+ (let (deactivate-mark)
+ (widen)
+ (if (= 0 (shell-command-on-region (point-min) (point-max) "gofmt"
+ outbuf nil errbuf))
+ ;; gofmt succeeded: replace the current buffer with outbuf,
+ ;; restore the mark and point, and discard errbuf.
+ (let ((old-mark (mark t)) (old-point (point)))
+ (erase-buffer)
+ (insert-buffer-substring outbuf)
+ (goto-char (min old-point (point-max)))
+ (if old-mark (set-mark (min old-mark (point-max))))
+ (kill-buffer errbuf))
+
+ ;; gofmt failed: display the errors
+ (display-buffer errbuf)))))
+
+ ;; Collapse any window opened on outbuf if shell-command-on-region
+ ;; displayed it.
+ (delete-windows-on outbuf)))))
+
+;;;###autoload
+(defun gofmt-before-save ()
+ "Add this to .emacs to run gofmt on the current buffer when saving:
+ (add-hook 'before-save-hook #'gofmt-before-save)"
+
+ (interactive)
+ (when (eq major-mode 'go-mode) (gofmt)))
+
+(provide 'go-mode)
diff --git a/misc/goplay/goplay.go b/misc/goplay/goplay.go
index 3ca5ed80c..f887fbbda 100644
--- a/misc/goplay/goplay.go
+++ b/misc/goplay/goplay.go
@@ -83,7 +83,7 @@ func Compile(w http.ResponseWriter, req *http.Request) {
}
// write request Body to x.go
- f, err := os.Open(src, os.O_CREAT|os.O_WRONLY|os.O_TRUNC, 0666)
+ f, err := os.Create(src)
if err != nil {
error(w, nil, err)
return
diff --git a/misc/kate/go.xml b/misc/kate/go.xml
index b8ff59267..14d88b26a 100644
--- a/misc/kate/go.xml
+++ b/misc/kate/go.xml
@@ -12,13 +12,13 @@
<list name="keywords">
<item> break </item>
<item> case </item>
+ <item> chan </item>
<item> const </item>
<item> continue </item>
<item> default </item>
<item> defer </item>
<item> else </item>
<item> fallthrough </item>
- <item> false </item>
<item> for </item>
<item> func </item>
<item> go </item>
@@ -26,22 +26,25 @@
<item> if </item>
<item> import </item>
<item> interface </item>
- <item> iota </item>
- <item> nil </item>
+ <item> map </item>
<item> package </item>
<item> range </item>
<item> return </item>
<item> select </item>
<item> struct </item>
<item> switch </item>
- <item> true </item>
<item> type </item>
<item> var </item>
</list>
+ <list name="predeclared">
+ <item> false </item>
+ <item> iota </item>
+ <item> nil </item>
+ <item> true </item>
+ </list>
<list name="types">
<item> bool </item>
<item> byte </item>
- <item> chan </item>
<item> complex64 </item>
<item> complex128 </item>
<item> float32 </item>
@@ -51,7 +54,6 @@
<item> int16 </item>
<item> int32 </item>
<item> int64 </item>
- <item> map </item>
<item> string </item>
<item> uint </item>
<item> uintptr </item>
@@ -64,7 +66,6 @@
<item> append </item>
<item> cap </item>
<item> close </item>
- <item> closed </item>
<item> complex </item>
<item> copy </item>
<item> imag </item>
@@ -81,6 +82,8 @@
<context attribute="Normal Text" lineEndContext="#stay" name="Normal">
<DetectSpaces />
<keyword attribute="Keyword" context="#stay" String="keywords"/>
+ <keyword attribute="Predeclared Identifier" context="#stay"
+ String="predeclared"/>
<keyword attribute="Data Type" context="#stay" String="types"/>
<keyword attribute="Builtin Function" context="#stay" String="functions"/>
<DetectIdentifier />
@@ -119,6 +122,7 @@
<itemDatas>
<itemData name="Normal Text" defStyleNum="dsNormal" spellChecking="false"/>
<itemData name="Keyword" defStyleNum="dsKeyword" spellChecking="false"/>
+ <itemData name="Predeclared Identifier" defStyleNum="dsOthers" spellChecking="false"/>
<itemData name="Builtin Function" defStyleNum="dsFunction" spellChecking="false"/>
<itemData name="Data Type" defStyleNum="dsDataType" spellChecking="false"/>
<itemData name="Decimal" defStyleNum="dsDecVal" spellChecking="false"/>
diff --git a/misc/notepadplus/README b/misc/notepadplus/README
new file mode 100755
index 000000000..000d31746
--- /dev/null
+++ b/misc/notepadplus/README
@@ -0,0 +1,8 @@
+Given a Notepad++ installation at <DIR>:
+
+1. Add the contents of userDefineLang.xml at <DIR>\userDefineLang.xml
+ between <NotepadPlus> ... </NotepadPlus>
+
+2. Copy go.xml to <DIR>\plugins\APIs
+
+3. Restart Notepad++
diff --git a/misc/notepadplus/go.xml b/misc/notepadplus/go.xml
new file mode 100755
index 000000000..7c5d8a173
--- /dev/null
+++ b/misc/notepadplus/go.xml
@@ -0,0 +1,66 @@
+<NotepadPlus>
+ <!-- Go Programming Language builtins and keywords -->
+ <AutoComplete>
+ <KeyWord name="append"/>
+ <KeyWord name="bool" />
+ <KeyWord name="break" />
+ <KeyWord name="byte" />
+ <KeyWord name="cap" />
+ <KeyWord name="case" />
+ <KeyWord name="chan" />
+ <KeyWord name="close" />
+ <KeyWord name="complex" />
+ <KeyWord name="complex128" />
+ <KeyWord name="complex64" />
+ <KeyWord name="const" />
+ <KeyWord name="continue" />
+ <KeyWord name="copy" />
+ <KeyWord name="default" />
+ <KeyWord name="defer" />
+ <KeyWord name="else" />
+ <KeyWord name="fallthrough" />
+ <KeyWord name="false" />
+ <KeyWord name="float32" />
+ <KeyWord name="float64" />
+ <KeyWord name="for" />
+ <KeyWord name="func" />
+ <KeyWord name="go" />
+ <KeyWord name="goto" />
+ <KeyWord name="if" />
+ <KeyWord name="iota" />
+ <KeyWord name="imag" />
+ <KeyWord name="import" />
+ <KeyWord name="int" />
+ <KeyWord name="int16" />
+ <KeyWord name="int32" />
+ <KeyWord name="int64" />
+ <KeyWord name="int8" />
+ <KeyWord name="interface" />
+ <KeyWord name="len" />
+ <KeyWord name="make" />
+ <KeyWord name="map" />
+ <KeyWord name="new" />
+ <KeyWord name="nil" />
+ <KeyWord name="package" />
+ <KeyWord name="panic" />
+ <KeyWord name="print" />
+ <KeyWord name="println" />
+ <KeyWord name="range" />
+ <KeyWord name="real" />
+ <KeyWord name="recover" />
+ <KeyWord name="return" />
+ <KeyWord name="select" />
+ <KeyWord name="string" />
+ <KeyWord name="struct" />
+ <KeyWord name="switch" />
+ <KeyWord name="true" />
+ <KeyWord name="type" />
+ <KeyWord name="uint" />
+ <KeyWord name="uint16" />
+ <KeyWord name="uint32" />
+ <KeyWord name="uint64" />
+ <KeyWord name="uint8" />
+ <KeyWord name="uintptr" />
+ <KeyWord name="var" />
+ </AutoComplete>
+</NotepadPlus>
diff --git a/misc/notepadplus/userDefineLang.xml b/misc/notepadplus/userDefineLang.xml
new file mode 100755
index 000000000..d1927a340
--- /dev/null
+++ b/misc/notepadplus/userDefineLang.xml
@@ -0,0 +1,36 @@
+<!-- <NotepadPlus> -->
+ <UserLang name="go" ext="go">
+ <Settings>
+ <Global caseIgnored="no" />
+ <TreatAsSymbol comment="no" commentLine="no" />
+ <Prefix words1="no" words2="no" words3="no" words4="no" />
+ </Settings>
+ <KeywordLists>
+ <Keywords name="Delimiters">&quot;`0&quot;`</Keywords>
+ <Keywords name="Folder+"></Keywords>
+ <Keywords name="Folder-"></Keywords>
+ <Keywords name="Operators">( ) [ ] { } ... . , _ &amp; ^ % &gt; &lt; ! = + - * | :</Keywords>
+ <Keywords name="Comment"> 1/* 2*/ 0//</Keywords>
+ <Keywords name="Words1">append bool break byte cap case chan close complex complex128 complex64 const continue copy default defer else fallthrough false float32 float64 for func go goto if iota imag import int int16 int32 int64 int8 interface len make map new nil package panic print println range real recover return select string struct switch true type uint uint16 uint32 uint64 uint8 uintptr var</Keywords>
+ <Keywords name="Words2"></Keywords>
+ <Keywords name="Words3"></Keywords>
+ <Keywords name="Words4"></Keywords>
+ </KeywordLists>
+ <Styles>
+ <WordsStyle name="DEFAULT" styleID="11" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" />
+ <WordsStyle name="FOLDEROPEN" styleID="12" fgColor="FFFF00" bgColor="FFFFFF" fontName="" fontStyle="0" />
+ <WordsStyle name="FOLDERCLOSE" styleID="13" fgColor="0B243B" bgColor="FFFFFF" fontName="" fontStyle="0" />
+ <WordsStyle name="KEYWORD1" styleID="5" fgColor="AA0000" bgColor="FFFFFF" fontName="" fontStyle="1" />
+ <WordsStyle name="KEYWORD2" styleID="6" fgColor="AA0000" bgColor="FFFFFF" fontName="" fontStyle="1" />
+ <WordsStyle name="KEYWORD3" styleID="7" fgColor="AA0000" bgColor="FFFFFF" fontName="" fontStyle="0" />
+ <WordsStyle name="KEYWORD4" styleID="8" fgColor="A00000" bgColor="FFFFFF" fontName="" fontStyle="0" />
+ <WordsStyle name="COMMENT" styleID="1" fgColor="AAAAAA" bgColor="FFFFFF" fontName="" fontStyle="0" />
+ <WordsStyle name="COMMENT LINE" styleID="2" fgColor="AAAAAA" bgColor="FFFFFF" fontName="" fontStyle="0" />
+ <WordsStyle name="NUMBER" styleID="4" fgColor="A52A2A" bgColor="FFFFFF" fontName="" fontStyle="0" />
+ <WordsStyle name="OPERATOR" styleID="10" fgColor="8000FF" bgColor="FFFFFF" fontName="" fontStyle="1" />
+ <WordsStyle name="DELIMINER1" styleID="14" fgColor="0000FF" bgColor="FFFFFF" fontName="" fontStyle="0" />
+ <WordsStyle name="DELIMINER2" styleID="15" fgColor="0000FF" bgColor="FFFFFF" fontName="" fontStyle="0" />
+ <WordsStyle name="DELIMINER3" styleID="16" fgColor="0000FF" bgColor="FFFFFF" fontName="" fontStyle="0" />
+ </Styles>
+ </UserLang>
+<!-- </NotepadPlus> -->
diff --git a/misc/swig/callback/Makefile b/misc/swig/callback/Makefile
new file mode 100644
index 000000000..fde0d107b
--- /dev/null
+++ b/misc/swig/callback/Makefile
@@ -0,0 +1,17 @@
+# Copyright 2011 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+include ../../../src/Make.inc
+
+TARG=swig/callback
+SWIGFILES=\
+ callback.swigcxx
+
+CLEANFILES+=run
+
+include ../../../src/Make.pkg
+
+%: install %.go
+ $(GC) $*.go
+ $(LD) $(SWIG_RPATH) -o $@ $*.$O
diff --git a/misc/swig/callback/callback.h b/misc/swig/callback/callback.h
new file mode 100644
index 000000000..80232a8b3
--- /dev/null
+++ b/misc/swig/callback/callback.h
@@ -0,0 +1,24 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+class Callback {
+public:
+ virtual ~Callback() { }
+ virtual std::string run() { return "Callback::run"; }
+};
+
+class Caller {
+private:
+ Callback *callback_;
+public:
+ Caller(): callback_(0) { }
+ ~Caller() { delCallback(); }
+ void delCallback() { delete callback_; callback_ = 0; }
+ void setCallback(Callback *cb) { delCallback(); callback_ = cb; }
+ std::string call() {
+ if (callback_ != 0)
+ return callback_->run();
+ return "";
+ }
+};
diff --git a/misc/swig/callback/callback.swigcxx b/misc/swig/callback/callback.swigcxx
new file mode 100644
index 000000000..0c97ef101
--- /dev/null
+++ b/misc/swig/callback/callback.swigcxx
@@ -0,0 +1,18 @@
+/* Copyright 2011 The Go Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style
+ license that can be found in the LICENSE file. */
+
+/* An example of writing a C++ virtual function in Go. */
+
+%module(directors="1") callback
+
+%{
+#include <string>
+#include "callback.h"
+%}
+
+%include "std_string.i"
+
+%feature("director");
+
+%include "callback.h"
diff --git a/misc/swig/callback/run b/misc/swig/callback/run
new file mode 100755
index 000000000..de150ed05
--- /dev/null
+++ b/misc/swig/callback/run
Binary files differ
diff --git a/misc/swig/callback/run.go b/misc/swig/callback/run.go
new file mode 100644
index 000000000..a76e636cb
--- /dev/null
+++ b/misc/swig/callback/run.go
@@ -0,0 +1,39 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "swig/callback"
+ "fmt"
+)
+
+type GoCallback struct{}
+
+func (p *GoCallback) Run() string {
+ return "GoCallback.Run"
+}
+
+func main() {
+ c := callback.NewCaller()
+ cb := callback.NewCallback()
+
+ c.SetCallback(cb)
+ s := c.Call()
+ fmt.Println(s)
+ if s != "Callback::run" {
+ panic(s)
+ }
+ c.DelCallback()
+
+ cb = callback.NewDirectorCallback(&GoCallback{})
+ c.SetCallback(cb)
+ s = c.Call()
+ fmt.Println(s)
+ if s != "GoCallback.Run" {
+ panic(s)
+ }
+ c.DelCallback()
+ callback.DeleteDirectorCallback(cb)
+}
diff --git a/misc/swig/stdio/Makefile b/misc/swig/stdio/Makefile
new file mode 100644
index 000000000..e7d330587
--- /dev/null
+++ b/misc/swig/stdio/Makefile
@@ -0,0 +1,17 @@
+# Copyright 2011 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+include ../../../src/Make.inc
+
+TARG=swig/file
+SWIGFILES=\
+ file.swig
+
+CLEANFILES+=hello
+
+include ../../../src/Make.pkg
+
+%: install %.go
+ $(GC) $*.go
+ $(LD) $(SWIG_RPATH) -o $@ $*.$O
diff --git a/misc/swig/stdio/file.swig b/misc/swig/stdio/file.swig
new file mode 100644
index 000000000..57c623f8f
--- /dev/null
+++ b/misc/swig/stdio/file.swig
@@ -0,0 +1,11 @@
+/* Copyright 2011 The Go Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style
+ license that can be found in the LICENSE file. */
+
+/* A trivial example of wrapping a C library using SWIG. */
+
+%{
+#include <stdio.h>
+%}
+
+int puts(const char *);
diff --git a/misc/swig/stdio/hello b/misc/swig/stdio/hello
new file mode 100755
index 000000000..10c55631f
--- /dev/null
+++ b/misc/swig/stdio/hello
Binary files differ
diff --git a/misc/swig/stdio/hello.go b/misc/swig/stdio/hello.go
new file mode 100644
index 000000000..eec294278
--- /dev/null
+++ b/misc/swig/stdio/hello.go
@@ -0,0 +1,11 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "swig/file"
+
+func main() {
+ file.Puts("Hello, world")
+}
diff --git a/misc/vim/ftplugin/go/fmt.vim b/misc/vim/ftplugin/go/fmt.vim
new file mode 100644
index 000000000..18a2156f5
--- /dev/null
+++ b/misc/vim/ftplugin/go/fmt.vim
@@ -0,0 +1,30 @@
+" Copyright 2011 The Go Authors. All rights reserved.
+" Use of this source code is governed by a BSD-style
+" license that can be found in the LICENSE file.
+"
+" fmt.vim: Vim command to format Go files with gofmt.
+"
+" This filetype plugin add a new commands for go buffers:
+"
+" :Fmt
+"
+" Filter the current Go buffer through gofmt.
+" It tries to preserve cursor position and avoids
+" replacing the buffer with stderr output.
+"
+
+command! Fmt call s:GoFormat()
+
+function! s:GoFormat()
+ let view = winsaveview()
+ %!gofmt
+ if v:shell_error
+ %| " output errors returned by gofmt
+ " TODO(dchest): perhaps, errors should go to quickfix
+ undo
+ echohl Error | echomsg "Gofmt returned error" | echohl None
+ endif
+ call winrestview(view)
+endfunction
+
+" vim:ts=4:sw=4:et
diff --git a/misc/vim/indent/go.vim b/misc/vim/indent/go.vim
new file mode 100644
index 000000000..2e9f191f5
--- /dev/null
+++ b/misc/vim/indent/go.vim
@@ -0,0 +1,30 @@
+" Copyright 2011 The Go Authors. All rights reserved.
+" Use of this source code is governed by a BSD-style
+" license that can be found in the LICENSE file.
+"
+" indent/go.vim: Vim indent file for Go.
+"
+
+if exists("b:did_indent")
+ finish
+endif
+let b:did_indent = 1
+
+" C indentation is mostly correct
+setlocal cindent
+
+" Options set:
+" +0 -- Don't indent continuation lines (because Go doesn't use semicolons
+" much)
+" L0 -- Don't move jump labels (NOTE: this isn't correct when working with
+" gofmt, but it does keep struct literals properly indented.)
+" :0 -- Align case labels with switch statement
+" l1 -- Always align case body relative to case labels
+" J1 -- Indent JSON-style objects (properly indents struct-literals)
+" (0, Ws -- Indent lines inside of unclosed parentheses by one shiftwidth
+" m1 -- Align closing parenthesis line with first non-blank of matching
+" parenthesis line
+"
+" Known issue: Trying to do a multi-line struct literal in a short variable
+" declaration will not indent properly.
+setlocal cinoptions+=+0,L0,:0,l1,J1,(0,Ws,m1
diff --git a/misc/vim/readme.txt b/misc/vim/readme.txt
index f836f58f3..3c3255113 100644
--- a/misc/vim/readme.txt
+++ b/misc/vim/readme.txt
@@ -35,3 +35,16 @@ To install one of the available filetype plugins for Go:
3. Add the following line to your .vimrc file (normally $HOME/.vimrc):
filetype plugin on
+
+
+Vim indentation plugin for Go
+=============================
+
+To install automatic indentation for Go:
+
+ 1. Same as 1 above.
+ 2. Copy or link indent/go.vim to the indent directory underneath your vim
+ runtime directory (normally $HOME/.vim/indent).
+ 3. Add the following line to your .vimrc file (normally $HOME/.vimrc):
+
+ filetype indent on
diff --git a/misc/vim/syntax/go.vim b/misc/vim/syntax/go.vim
index 7507cada2..26d7defe3 100644
--- a/misc/vim/syntax/go.vim
+++ b/misc/vim/syntax/go.vim
@@ -85,7 +85,7 @@ syn match goType /\<func\>/
syn match goDeclaration /^func\>/
" Predefined functions and values
-syn keyword goBuiltins append cap close closed complex copy imag len
+syn keyword goBuiltins append cap close complex copy imag len
syn keyword goBuiltins make new panic print println real recover
syn keyword goConstants iota true false nil
@@ -181,7 +181,7 @@ if go_highlight_extra_types != 0
syn match goExtraType /\<bytes\.\(Buffer\)\>/
syn match goExtraType /\<io\.\(Reader\|Writer\|ReadWriter\|ReadWriteCloser\)\>/
syn match goExtraType /\<\(os\.Error\)\>/
- syn match goExtraType /\<reflect\.\w*\(Type\|Value\)\>/
+ syn match goExtraType /\<reflect\.\(Kind\|Type\|Value\)\>/
syn match goExtraType /\<unsafe\.Pointer\>/
endif
diff --git a/src/Make.cmd b/src/Make.cmd
index 2b9aba4a5..6f88e5cc2 100644
--- a/src/Make.cmd
+++ b/src/Make.cmd
@@ -23,7 +23,7 @@ install: $(QUOTED_GOBIN)/$(TARG)
$(QUOTED_GOBIN)/$(TARG): $(TARG)
cp -f $(TARG) $(QUOTED_GOBIN)
-CLEANFILES+=$(TARG)
+CLEANFILES+=$(TARG) _test _testmain.go
nuke: clean
rm -f $(QUOTED_GOBIN)/$(TARG)
@@ -34,8 +34,6 @@ testpackage: _test/main.a
testpackage-clean:
rm -f _test/main.a _gotest_.$O
-testpackage: _test/main.a
-
_test/main.a: _gotest_.$O
@mkdir -p _test
rm -f $@
diff --git a/src/Make.common b/src/Make.common
index af6d04adc..34d7016f4 100644
--- a/src/Make.common
+++ b/src/Make.common
@@ -5,6 +5,15 @@
clean:
rm -rf *.o *.a *.[$(OS)] [$(OS)].out $(CLEANFILES)
+install.clean: install
+ rm -rf *.o *.a *.[$(OS)] [$(OS)].out $(CLEANFILES)
+
+test.clean: test
+ rm -rf *.o *.a *.[$(OS)] [$(OS)].out $(CLEANFILES)
+
+testshort.clean: testshort
+ rm -rf *.o *.a *.[$(OS)] [$(OS)].out $(CLEANFILES)
+
%.make:
$(MAKE) -C $* install
diff --git a/src/Make.inc b/src/Make.inc
index 2889c7edf..a6edb165a 100644
--- a/src/Make.inc
+++ b/src/Make.inc
@@ -29,14 +29,20 @@ ifeq ($(GOOS),)
GOOS:=$(GOHOSTOS)
endif
-ifeq ($(GOOS),darwin)
-else ifeq ($(GOOS),freebsd)
-else ifeq ($(GOOS),linux)
-else ifeq ($(GOOS),tiny)
-else ifeq ($(GOOS),plan9)
-else ifeq ($(GOOS),windows)
-else
-$(error Invalid $$GOOS '$(GOOS)'; must be darwin, freebsd, linux, plan9, tiny, or windows)
+GOOS_LIST=\
+ darwin\
+ freebsd\
+ linux\
+ plan9\
+ windows\
+
+GOARCH_LIST=\
+ 386\
+ amd64\
+ arm\
+
+ifeq ($(filter $(GOOS),$(GOOS_LIST)),)
+$(error Invalid $$GOOS '$(GOOS)'; must be one of: $(GOOS_LIST))
endif
ifeq ($(GOHOSTARCH),)
@@ -59,24 +65,25 @@ ifeq ($(GOOS),darwin)
GOHOSTARCH:=$(GOARCH)
endif
+ifeq ($(filter $(GOARCH),$(GOARCH_LIST)),)
+$(error Invalid $$GOARCH '$(GOARCH)'; must be one of: $(GOARCH_LIST))
+endif
+
ifeq ($(GOARCH),386)
O:=8
else ifeq ($(GOARCH),amd64)
O:=6
else ifeq ($(GOARCH),arm)
-
O:=5
-ifeq ($(GOOS),linux)
-else
+ifneq ($(GOOS),linux)
$(error Invalid $$GOOS '$(GOOS)' for GOARCH=arm; must be linux)
endif
-
else
-$(error Invalid $$GOARCH '$(GOARCH)'; must be 386, amd64, or arm)
+$(error Missing $$O for '$(GOARCH)')
endif
# Save for recursive make to avoid recomputing.
-export GOARCH GOOS GOHOSTARCH GOHOSTOS
+export GOARCH GOOS GOHOSTARCH GOHOSTOS GOARCH_LIST GOOS_LIST
# ugly hack to deal with whitespaces in $GOROOT
nullstring :=
diff --git a/src/Make.pkg b/src/Make.pkg
index 3d616ca99..59ce56ac0 100644
--- a/src/Make.pkg
+++ b/src/Make.pkg
@@ -41,17 +41,28 @@ CGO_OFILES+=$(patsubst %.go,%.cgo2.o,$(CGOFILES)) _cgo_export.o
OFILES+=_cgo_defun.$O _cgo_import.$O $(CGO_OFILES)
endif
+ifdef SWIGFILES
+GOFILES+=$(patsubst %.swig,_obj/%.go,$(patsubst %.swigcxx,%.swig,$(SWIGFILES)))
+OFILES+=$(patsubst %.swig,_obj/%_gc.$O,$(patsubst %.swigcxx,%.swig,$(SWIGFILES)))
+SWIG_PREFIX=$(subst /,-,$(TARG))
+SWIG_SOS+=$(patsubst %.swig,_obj/$(SWIG_PREFIX)-%.so,$(patsubst %.swigcxx,%.swig,$(SWIGFILES)))
+INSTALLFILES+=$(patsubst %.swig,$(pkgdir)/swig/$(SWIG_PREFIX)-%.so,$(patsubst %.swigcxx,%.swig,$(SWIGFILES)))
+endif
+
PREREQ+=$(patsubst %,%.make,$(DEPS))
coverage:
gotest
6cov -g $(shell pwd) $O.out | grep -v '_test\.go:'
-CLEANFILES+=*.so _obj _test _testmain.go *.exe _cgo* *.cgo[12].*
+CLEANFILES+=*.so _obj _test _testmain.go *.exe _cgo* *.cgo[12].* test.out build.out
test:
gotest
+testshort:
+ gotest -test.short -test.timeout=120
+
bench:
gotest -test.bench=. -test.run="Do not run tests"
@@ -173,6 +184,54 @@ RUNTIME_CFLAGS=-I$(pkgdir)
_cgo_defun.$O: _obj/_cgo_defun.c
$(CC) $(CFLAGS) $(RUNTIME_CFLAGS) -I . -o "$@" _obj/_cgo_defun.c
+# To use swig in a Go package, add a line
+#
+# SWIGFILES=x.swig
+#
+# to the main Makefile. This signals that SWIG should process the
+#.swig file when building the package.
+#
+# To wrap C code, use an extension of .swig. To wrap C++ code, use an
+# extension of .swigcxx.
+#
+# SWIGFILES=myclib.swig mycxxlib.swigcxx
+
+ifdef SWIGFILES
+_obj/%._swig_run _obj/%.go _obj/%_gc.c _obj/%_wrap.c: %.swig
+ @mkdir -p _obj
+ swig -go -module $* -soname $(SWIG_PREFIX)-$*.so -o _obj/$*_wrap.c -outdir _obj $<
+
+_obj/%._swig_run _obj/%.go _obj/%_gc.c _obj/%_wrap.cxx: %.swigcxx
+ @mkdir -p _obj
+ swig -go -c++ -module $* -soname $(SWIG_PREFIX)-$*.so -o _obj/$*_wrap.cxx -outdir _obj $<
+
+_obj/%_gc.$O: _obj/%_gc.c
+ $(CC) $(CFLAGS) -I . -I$(pkgdir) -o "$@" _obj/$*_gc.c
+
+_obj/%_wrap.o: _obj/%_wrap.c
+ $(HOST_CC) $(_CGO_CFLAGS_$(GOARCH)) -I . -g -fPIC -O2 -o $@ -c $^ $(SWIG_CFLAGS)
+
+HOST_CXX=g++
+
+_obj/%_wrapcxx.o: _obj/%_wrap.cxx
+ $(HOST_CXX) $(_CGO_CFLAGS_$(GOARCH)) -I . -g -fPIC -O2 -o $@ -c $^ $(SWIG_CXXFLAGS)
+
+_obj/$(SWIG_PREFIX)-%.so: _obj/%_wrap.o
+ $(HOST_CC) $(_CGO_CFLAGS_$(GOARCH)) -o $@ $^ $(SWIG_LDFLAGS) $(_CGO_LDFLAGS_$(GOOS)) $(_SWIG_LDFLAGS_$(GOOS))
+
+_obj/$(SWIG_PREFIX)-%.so: _obj/%_wrapcxx.o
+ $(HOST_CXX) $(_CGO_CFLAGS_$(GOARCH)) -o $@ $^ $(SWIG_LDFLAGS) $(_CGO_LDFLAGS_$(GOOS)) $(_SWIG_LDFLAGS_$(GOOS))
+
+$(pkgdir)/swig/$(SWIG_PREFIX)-%.so: _obj/$(SWIG_PREFIX)-%.so
+ @test -d $(QUOTED_GOROOT)/pkg && mkdir -p $(pkgdir)/swig
+ cp $< "$@"
+
+all: $(SWIG_SOS)
+
+SWIG_RPATH=-r $(pkgdir)/swig
+
+endif
+
# Generic build rules.
# These come last so that the rules above can override them
# for more specific file names.
diff --git a/src/all-qemu.bash b/src/all-qemu.bash
new file mode 100755
index 000000000..b2be15ac8
--- /dev/null
+++ b/src/all-qemu.bash
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+# Copyright 2011 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+# Run all.bash but exclude tests that depend on functionality
+# missing in QEMU's system call emulation.
+
+export DISABLE_NET_TESTS=1 # no external network
+export NOTEST=""
+
+NOTEST="$NOTEST big" # xxx
+NOTEST="$NOTEST http net rpc syslog websocket" # no localhost network
+NOTEST="$NOTEST os" # 64-bit seek fails
+
+./all.bash
diff --git a/src/clean.bash b/src/clean.bash
index 7969e2cd0..1955b583b 100755
--- a/src/clean.bash
+++ b/src/clean.bash
@@ -21,7 +21,9 @@ fi
rm -f "$GOROOT"/lib/*.a
for i in lib9 libbio libmach cmd pkg \
../misc/cgo/gmp ../misc/cgo/stdio \
+ ../misc/cgo/life ../misc/cgo/test \
../test/bench ../test/garbage
do
- gomake -C "$GOROOT/src/$i" clean
+ # Do not use gomake here. It may not be available.
+ $MAKE -C "$GOROOT/src/$i" clean
done
diff --git a/src/cmd/5c/peep.c b/src/cmd/5c/peep.c
index 8945ee732..c15bf0fc4 100644
--- a/src/cmd/5c/peep.c
+++ b/src/cmd/5c/peep.c
@@ -1100,7 +1100,7 @@ copyu(Prog *p, Adr *v, Adr *s)
if(v->type == D_REG) {
if(v->reg <= REGEXT && v->reg > exregoffset)
return 2;
- if(v->reg == REGARG)
+ if(v->reg == (uchar)REGARG)
return 2;
}
if(v->type == D_FREG)
@@ -1118,7 +1118,7 @@ copyu(Prog *p, Adr *v, Adr *s)
case ATEXT: /* funny */
if(v->type == D_REG)
- if(v->reg == REGARG)
+ if(v->reg == (uchar)REGARG)
return 3;
return 0;
}
diff --git a/src/cmd/5c/txt.c b/src/cmd/5c/txt.c
index f5619f800..4be1f6f62 100644
--- a/src/cmd/5c/txt.c
+++ b/src/cmd/5c/txt.c
@@ -400,6 +400,10 @@ regsalloc(Node *n, Node *nn)
void
regaalloc1(Node *n, Node *nn)
{
+ if(REGARG < 0) {
+ fatal(n, "regaalloc1 and REGARG<0");
+ return;
+ }
nodreg(n, nn, REGARG);
reg[REGARG]++;
curarg = align(curarg, nn->type, Aarg1, nil);
diff --git a/src/cmd/5g/ggen.c b/src/cmd/5g/ggen.c
index 182d7f147..7197709d4 100644
--- a/src/cmd/5g/ggen.c
+++ b/src/cmd/5g/ggen.c
@@ -32,7 +32,7 @@ compile(Node *fn)
return;
// set up domain for labels
- labellist = L;
+ clearlabels();
lno = setlineno(fn);
diff --git a/src/cmd/5g/gobj.c b/src/cmd/5g/gobj.c
index bf59534b9..acece6c0d 100644
--- a/src/cmd/5g/gobj.c
+++ b/src/cmd/5g/gobj.c
@@ -268,7 +268,7 @@ static Prog *estrdat;
static int gflag;
static Prog *savepc;
-static void
+void
data(void)
{
gflag = debug['g'];
@@ -285,7 +285,7 @@ data(void)
pc = estrdat;
}
-static void
+void
text(void)
{
if(!savepc)
@@ -310,6 +310,29 @@ dumpdata(void)
pc = estrdat;
}
+int
+dsname(Sym *sym, int off, char *t, int n)
+{
+ Prog *p;
+
+ p = gins(ADATA, N, N);
+ p->from.type = D_OREG;
+ p->from.name = D_EXTERN;
+ p->from.etype = TINT32;
+ p->from.offset = off;
+ p->from.reg = NREG;
+ p->from.sym = sym;
+
+ p->reg = n;
+
+ p->to.type = D_SCONST;
+ p->to.name = D_NONE;
+ p->to.reg = NREG;
+ p->to.offset = 0;
+ memmove(p->to.sval, t, n);
+ return off + n;
+}
+
/*
* make a refer to the data s, s+len
* emitting DATA if needed.
@@ -317,76 +340,15 @@ dumpdata(void)
void
datastring(char *s, int len, Addr *a)
{
- int w;
- Prog *p;
- Addr ac, ao;
- static int gen;
- struct {
- Strlit lit;
- char buf[100];
- } tmp;
-
- // string
- memset(&ao, 0, sizeof(ao));
- ao.type = D_OREG;
- ao.name = D_STATIC;
- ao.etype = TINT32;
- ao.offset = 0; // fill in
- ao.reg = NREG;
-
- // constant
- memset(&ac, 0, sizeof(ac));
- ac.type = D_CONST;
- ac.name = D_NONE;
- ac.offset = 0; // fill in
- ac.reg = NREG;
-
- // huge strings are made static to avoid long names.
- if(len > 100) {
- snprint(namebuf, sizeof(namebuf), ".string.%d", gen++);
- ao.sym = lookup(namebuf);
- ao.name = D_STATIC;
- } else {
- if(len > 0 && s[len-1] == '\0')
- len--;
- tmp.lit.len = len;
- memmove(tmp.lit.s, s, len);
- tmp.lit.s[len] = '\0';
- len++;
- snprint(namebuf, sizeof(namebuf), "\"%Z\"", &tmp.lit);
- ao.sym = pkglookup(namebuf, stringpkg);
- ao.name = D_EXTERN;
- }
- *a = ao;
-
- // only generate data the first time.
- if(ao.sym->flags & SymUniq)
- return;
- ao.sym->flags |= SymUniq;
-
- data();
- for(w=0; w<len; w+=8) {
- p = pc;
- gins(ADATA, N, N);
-
- // DATA s+w, [NSNAME], $"xxx"
- p->from = 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; h<NHASH; h++) {
for(s=hash[h]; s!=S; s=s->hash) {
+ 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; w<len; w+=8) {
- p = pc;
- gins(ADATA, N, N);
-
- // DATA s+w, [NSNAME], $"xxx"
- p->from = 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; h<NHASH; h++) {
- for(s=hash[h]; s!=S; s=s->hash) {
- 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; w<len; w+=8) {
- p = pc;
- gins(ADATA, N, N);
-
- // DATA s+w, [NSNAME], $"xxx"
- p->from = 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; h<NHASH; h++) {
for(s=hash[h]; s!=S; s=s->hash) {
+ 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; i<nelem(phash); i++)
+ for(p=phash[i]; p; p=p->link)
+ 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) {
+ m = 8;
+ if(m > 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; i<nelem(yymsg); i++) {
if(yymsg[i].yystate == yystate && yymsg[i].yychar == yychar) {
@@ -834,7 +845,6 @@ goopnames[] =
[OCALL] = "function call",
[OCAP] = "cap",
[OCASE] = "case",
- [OCLOSED] = "closed",
[OCLOSE] = "close",
[OCOMPLEX] = "complex",
[OCOM] = "^",
@@ -1144,7 +1154,7 @@ Tpretty(Fmt *fp, Type *t)
&& t->sym != 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)
@@ -1663,6 +1681,9 @@ isselect(Node *n)
s = pkglookup("selectrecv", runtimepkg);
if(s == n->sym)
return 1;
+ s = pkglookup("selectrecv2", runtimepkg);
+ if(s == n->sym)
+ return 1;
s = pkglookup("selectdefault", runtimepkg);
if(s == n->sym)
return 1;
@@ -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(`<span class ="comment">`),
+ /* 001 */ []byte(`<span class="comment">`),
/* 010 */ []byte(`<span class="highlight">`),
/* 011 */ []byte(`<span class="highlight-comment">`),
/* 100 */ []byte(`<span class="selection">`),
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, `<a id="%s">%s</a>`, 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<<bytes[0])-1) != 0 {
+ err = SyntaxError{"invalid padding bits in BIT STRING"}
+ return
+ }
+ ret.BitLength = (len(bytes)-1)*8 - paddingBits
+ ret.Bytes = bytes[1:]
+ return
+}
+
+// OBJECT IDENTIFIER
+
+// An ObjectIdentifier represents an ASN.1 OBJECT IDENTIFIER.
+type ObjectIdentifier []int
+
+// Equal returns true iff oi and other represent the same identifier.
+func (oi ObjectIdentifier) Equal(other ObjectIdentifier) bool {
+ if len(oi) != len(other) {
+ return false
+ }
+ for i := 0; i < len(oi); i++ {
+ if oi[i] != other[i] {
+ return false
+ }
+ }
+
+ return true
+}
+
+// parseObjectIdentifier parses an OBJECT IDENTIFER from the given bytes and
+// returns it. An object identifer is a sequence of variable length integers
+// that are assigned in a hierarachy.
+func parseObjectIdentifier(bytes []byte) (s []int, err os.Error) {
+ if len(bytes) == 0 {
+ err = SyntaxError{"zero length OBJECT IDENTIFIER"}
+ return
+ }
+
+ // In the worst case, we get two elements from the first byte (which is
+ // encoded differently) and then every varint is a single byte long.
+ s = make([]int, len(bytes)+1)
+
+ // The first byte is 40*value1 + value2:
+ s[0] = int(bytes[0]) / 40
+ s[1] = int(bytes[0]) % 40
+ i := 2
+ for offset := 1; offset < len(bytes); i++ {
+ var v int
+ v, offset, err = parseBase128Int(bytes, offset)
+ if err != nil {
+ return
+ }
+ s[i] = v
+ }
+ s = s[0:i]
+ return
+}
+
+// ENUMERATED
+
+// An Enumerated is represented as a plain int.
+type Enumerated int
+
+
+// FLAG
+
+// A Flag accepts any data and is set to true if present.
+type Flag bool
+
+// parseBase128Int parses a base-128 encoded int from the given offset in the
+// given byte array. It returns the value and the new offset.
+func parseBase128Int(bytes []byte, initOffset int) (ret, offset int, err os.Error) {
+ offset = initOffset
+ for shifted := 0; offset < len(bytes); shifted++ {
+ if shifted > 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<<bytes[0])-1) != 0 {
+ err = SyntaxError{"invalid padding bits in BIT STRING"}
+ return
+ }
+ ret.BitLength = (len(bytes)-1)*8 - paddingBits
+ ret.Bytes = bytes[1:]
+ return
+}
+
+// OBJECT IDENTIFIER
+
+// An ObjectIdentifier represents an ASN.1 OBJECT IDENTIFIER.
+type ObjectIdentifier []int
+
+// Equal returns true iff oi and other represent the same identifier.
+func (oi ObjectIdentifier) Equal(other ObjectIdentifier) bool {
+ if len(oi) != len(other) {
+ return false
+ }
+ for i := 0; i < len(oi); i++ {
+ if oi[i] != other[i] {
+ return false
+ }
+ }
+
+ return true
+}
+
+// parseObjectIdentifier parses an OBJECT IDENTIFER from the given bytes and
+// returns it. An object identifer is a sequence of variable length integers
+// that are assigned in a hierarachy.
+func parseObjectIdentifier(bytes []byte) (s []int, err os.Error) {
+ if len(bytes) == 0 {
+ err = SyntaxError{"zero length OBJECT IDENTIFIER"}
+ return
+ }
+
+ // In the worst case, we get two elements from the first byte (which is
+ // encoded differently) and then every varint is a single byte long.
+ s = make([]int, len(bytes)+1)
+
+ // The first byte is 40*value1 + value2:
+ s[0] = int(bytes[0]) / 40
+ s[1] = int(bytes[0]) % 40
+ i := 2
+ for offset := 1; offset < len(bytes); i++ {
+ var v int
+ v, offset, err = parseBase128Int(bytes, offset)
+ if err != nil {
+ return
+ }
+ s[i] = v
+ }
+ s = s[0:i]
+ return
+}
+
+// ENUMERATED
+
+// An Enumerated is represented as a plain int.
+type Enumerated int
+
+
+// FLAG
+
+// A Flag accepts any data and is set to true if present.
+type Flag bool
+
+// parseBase128Int parses a base-128 encoded int from the given offset in the
+// given byte array. It returns the value and the new offset.
+func parseBase128Int(bytes []byte, initOffset int) (ret, offset int, err os.Error) {
+ offset = initOffset
+ for shifted := 0; offset < len(bytes); shifted++ {
+ if shifted > 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 <script> tags.
+// For historical reasons, web browsers don't honor standard HTML
+// escaping within <script> tags, so an alternative JSON encoding must
+// be used.
+func HTMLEscape(dst *bytes.Buffer, src []byte) {
+ // < > & can only appear in string literals,
+ // so just scan the string one byte at a time.
+ start := 0
+ for i, c := range src {
+ if c == '<' || c == '>' || c == '&' {
+ if start < i {
+ dst.Write(src[start:i])
+ }
+ dst.WriteString(`\u00`)
+ dst.WriteByte(hex[c>>4])
+ dst.WriteByte(hex[c&0xF])
+ start = i + 1
+ }
+ }
+ if start < len(src) {
+ dst.Write(src[start:])
+ }
+}
+
+// Marshaler is the interface implemented by objects that
+// can marshal themselves into valid JSON.
+type Marshaler interface {
+ MarshalJSON() ([]byte, os.Error)
+}
+
+type UnsupportedTypeError struct {
+ Type reflect.Type
+}
+
+func (e *UnsupportedTypeError) String() string {
+ return "json: unsupported type: " + e.Type.String()
+}
+
+type InvalidUTF8Error struct {
+ S string
+}
+
+func (e *InvalidUTF8Error) String() string {
+ return "json: invalid UTF-8 in string: " + strconv.Quote(e.S)
+}
+
+type MarshalerError struct {
+ Type reflect.Type
+ Error os.Error
+}
+
+func (e *MarshalerError) String() string {
+ return "json: error calling MarshalJSON for type " + e.Type.String() + ": " + e.Error.String()
+}
+
+type interfaceOrPtrValue interface {
+ IsNil() bool
+ Elem() reflect.Value
+}
+
+var hex = "0123456789abcdef"
+
+// An encodeState encodes JSON into a bytes.Buffer.
+type encodeState struct {
+ bytes.Buffer // accumulated output
+}
+
+func (e *encodeState) marshal(v interface{}) (err os.Error) {
+ defer func() {
+ if r := recover(); r != nil {
+ if _, ok := r.(runtime.Error); ok {
+ panic(r)
+ }
+ err = r.(os.Error)
+ }
+ }()
+ e.reflectValue(reflect.NewValue(v))
+ return nil
+}
+
+func (e *encodeState) error(err os.Error) {
+ panic(err)
+}
+
+var byteSliceType = reflect.Typeof([]byte(nil))
+
+func (e *encodeState) reflectValue(v reflect.Value) {
+ if v == nil {
+ e.WriteString("null")
+ return
+ }
+
+ if j, ok := v.Interface().(Marshaler); ok {
+ b, err := j.MarshalJSON()
+ if err == nil {
+ // copy JSON into buffer, checking validity.
+ err = Compact(&e.Buffer, b)
+ }
+ if err != nil {
+ e.error(&MarshalerError{v.Type(), err})
+ }
+ return
+ }
+
+ switch v := v.(type) {
+ case *reflect.BoolValue:
+ x := v.Get()
+ if x {
+ e.WriteString("true")
+ } else {
+ e.WriteString("false")
+ }
+
+ case *reflect.IntValue:
+ e.WriteString(strconv.Itoa64(v.Get()))
+
+ case *reflect.UintValue:
+ e.WriteString(strconv.Uitoa64(v.Get()))
+
+ case *reflect.FloatValue:
+ e.WriteString(strconv.FtoaN(v.Get(), 'g', -1, v.Type().Bits()))
+
+ case *reflect.StringValue:
+ e.string(v.Get())
+
+ case *reflect.StructValue:
+ e.WriteByte('{')
+ t := v.Type().(*reflect.StructType)
+ n := v.NumField()
+ first := true
+ for i := 0; i < n; i++ {
+ f := t.Field(i)
+ if f.PkgPath != "" {
+ continue
+ }
+ if first {
+ first = false
+ } else {
+ e.WriteByte(',')
+ }
+ if isValidTag(f.Tag) {
+ e.string(f.Tag)
+ } else {
+ e.string(f.Name)
+ }
+ e.WriteByte(':')
+ e.reflectValue(v.Field(i))
+ }
+ e.WriteByte('}')
+
+ case *reflect.MapValue:
+ if _, ok := v.Type().(*reflect.MapType).Key().(*reflect.StringType); !ok {
+ e.error(&UnsupportedTypeError{v.Type()})
+ }
+ if v.IsNil() {
+ e.WriteString("null")
+ break
+ }
+ e.WriteByte('{')
+ var sv stringValues = v.Keys()
+ sort.Sort(sv)
+ for i, k := range sv {
+ if i > 0 {
+ e.WriteByte(',')
+ }
+ e.string(k.(*reflect.StringValue).Get())
+ e.WriteByte(':')
+ e.reflectValue(v.Elem(k))
+ }
+ e.WriteByte('}')
+
+ case reflect.ArrayOrSliceValue:
+ if v.Type() == byteSliceType {
+ e.WriteByte('"')
+ s := v.Interface().([]byte)
+ if len(s) < 1024 {
+ // for small buffers, using Encode directly is much faster.
+ dst := make([]byte, base64.StdEncoding.EncodedLen(len(s)))
+ base64.StdEncoding.Encode(dst, s)
+ e.Write(dst)
+ } else {
+ // for large buffers, avoid unnecessary extra temporary
+ // buffer space.
+ enc := base64.NewEncoder(base64.StdEncoding, e)
+ enc.Write(s)
+ enc.Close()
+ }
+ e.WriteByte('"')
+ break
+ }
+ e.WriteByte('[')
+ n := v.Len()
+ for i := 0; i < n; i++ {
+ if i > 0 {
+ e.WriteByte(',')
+ }
+ e.reflectValue(v.Elem(i))
+ }
+ e.WriteByte(']')
+
+ case interfaceOrPtrValue:
+ if v.IsNil() {
+ e.WriteString("null")
+ return
+ }
+ e.reflectValue(v.Elem())
+
+ default:
+ e.error(&UnsupportedTypeError{v.Type()})
+ }
+ return
+}
+
+func isValidTag(s string) bool {
+ if s == "" {
+ return false
+ }
+ for _, c := range s {
+ if c != '_' && !unicode.IsLetter(c) && !unicode.IsDigit(c) {
+ return false
+ }
+ }
+ return true
+}
+
+// stringValues is a slice of reflect.Value holding *reflect.StringValue.
+// It implements the methods to sort by string.
+type stringValues []reflect.Value
+
+func (sv stringValues) Len() int { return len(sv) }
+func (sv stringValues) Swap(i, j int) { sv[i], sv[j] = sv[j], sv[i] }
+func (sv stringValues) Less(i, j int) bool { return sv.get(i) < sv.get(j) }
+func (sv stringValues) get(i int) string { return sv[i].(*reflect.StringValue).Get() }
+
+func (e *encodeState) string(s string) {
+ e.WriteByte('"')
+ start := 0
+ for i := 0; i < len(s); {
+ if b := s[i]; b < utf8.RuneSelf {
+ if 0x20 <= b && b != '\\' && b != '"' {
+ i++
+ continue
+ }
+ if start < i {
+ e.WriteString(s[start:i])
+ }
+ if b == '\\' || b == '"' {
+ e.WriteByte('\\')
+ e.WriteByte(b)
+ } else {
+ e.WriteString(`\u00`)
+ e.WriteByte(hex[b>>4])
+ e.WriteByte(hex[b&0xF])
+ }
+ i++
+ start = i
+ continue
+ }
+ c, size := utf8.DecodeRuneInString(s[i:])
+ if c == utf8.RuneError && size == 1 {
+ e.error(&InvalidUTF8Error{s})
+ }
+ i += size
+ }
+ if start < len(s) {
+ e.WriteString(s[start:])
+ }
+ e.WriteByte('"')
+}
diff --git a/src/cmd/gofix/testdata/reflect.encode.go.out b/src/cmd/gofix/testdata/reflect.encode.go.out
new file mode 100644
index 000000000..8c79a27d4
--- /dev/null
+++ b/src/cmd/gofix/testdata/reflect.encode.go.out
@@ -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 <script> tags.
+// For historical reasons, web browsers don't honor standard HTML
+// escaping within <script> tags, so an alternative JSON encoding must
+// be used.
+func HTMLEscape(dst *bytes.Buffer, src []byte) {
+ // < > & can only appear in string literals,
+ // so just scan the string one byte at a time.
+ start := 0
+ for i, c := range src {
+ if c == '<' || c == '>' || c == '&' {
+ if start < i {
+ dst.Write(src[start:i])
+ }
+ dst.WriteString(`\u00`)
+ dst.WriteByte(hex[c>>4])
+ dst.WriteByte(hex[c&0xF])
+ start = i + 1
+ }
+ }
+ if start < len(src) {
+ dst.Write(src[start:])
+ }
+}
+
+// Marshaler is the interface implemented by objects that
+// can marshal themselves into valid JSON.
+type Marshaler interface {
+ MarshalJSON() ([]byte, os.Error)
+}
+
+type UnsupportedTypeError struct {
+ Type reflect.Type
+}
+
+func (e *UnsupportedTypeError) String() string {
+ return "json: unsupported type: " + e.Type.String()
+}
+
+type InvalidUTF8Error struct {
+ S string
+}
+
+func (e *InvalidUTF8Error) String() string {
+ return "json: invalid UTF-8 in string: " + strconv.Quote(e.S)
+}
+
+type MarshalerError struct {
+ Type reflect.Type
+ Error os.Error
+}
+
+func (e *MarshalerError) String() string {
+ return "json: error calling MarshalJSON for type " + e.Type.String() + ": " + e.Error.String()
+}
+
+type interfaceOrPtrValue interface {
+ IsNil() bool
+ Elem() reflect.Value
+}
+
+var hex = "0123456789abcdef"
+
+// An encodeState encodes JSON into a bytes.Buffer.
+type encodeState struct {
+ bytes.Buffer // accumulated output
+}
+
+func (e *encodeState) marshal(v interface{}) (err os.Error) {
+ defer func() {
+ if r := recover(); r != nil {
+ if _, ok := r.(runtime.Error); ok {
+ panic(r)
+ }
+ err = r.(os.Error)
+ }
+ }()
+ e.reflectValue(reflect.NewValue(v))
+ return nil
+}
+
+func (e *encodeState) error(err os.Error) {
+ panic(err)
+}
+
+var byteSliceType = reflect.Typeof([]byte(nil))
+
+func (e *encodeState) reflectValue(v reflect.Value) {
+ if !v.IsValid() {
+ e.WriteString("null")
+ return
+ }
+
+ if j, ok := v.Interface().(Marshaler); ok {
+ b, err := j.MarshalJSON()
+ if err == nil {
+ // copy JSON into buffer, checking validity.
+ err = Compact(&e.Buffer, b)
+ }
+ if err != nil {
+ e.error(&MarshalerError{v.Type(), err})
+ }
+ return
+ }
+
+ switch v.Kind() {
+ case reflect.Bool:
+ x := v.Bool()
+ if x {
+ e.WriteString("true")
+ } else {
+ e.WriteString("false")
+ }
+
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ e.WriteString(strconv.Itoa64(v.Int()))
+
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ e.WriteString(strconv.Uitoa64(v.Uint()))
+
+ case reflect.Float32, reflect.Float64:
+ e.WriteString(strconv.FtoaN(v.Float(), 'g', -1, v.Type().Bits()))
+
+ case reflect.String:
+ e.string(v.String())
+
+ case reflect.Struct:
+ e.WriteByte('{')
+ t := v.Type()
+ n := v.NumField()
+ first := true
+ for i := 0; i < n; i++ {
+ f := t.Field(i)
+ if f.PkgPath != "" {
+ continue
+ }
+ if first {
+ first = false
+ } else {
+ e.WriteByte(',')
+ }
+ if isValidTag(f.Tag) {
+ e.string(f.Tag)
+ } else {
+ e.string(f.Name)
+ }
+ e.WriteByte(':')
+ e.reflectValue(v.Field(i))
+ }
+ e.WriteByte('}')
+
+ case reflect.Map:
+ if v.Type().Key().Kind() != reflect.String {
+ e.error(&UnsupportedTypeError{v.Type()})
+ }
+ if v.IsNil() {
+ e.WriteString("null")
+ break
+ }
+ e.WriteByte('{')
+ var sv stringValues = v.MapKeys()
+ sort.Sort(sv)
+ for i, k := range sv {
+ if i > 0 {
+ e.WriteByte(',')
+ }
+ e.string(k.String())
+ e.WriteByte(':')
+ e.reflectValue(v.MapIndex(k))
+ }
+ e.WriteByte('}')
+
+ case reflect.Array, reflect.Slice:
+ if v.Type() == byteSliceType {
+ e.WriteByte('"')
+ s := v.Interface().([]byte)
+ if len(s) < 1024 {
+ // for small buffers, using Encode directly is much faster.
+ dst := make([]byte, base64.StdEncoding.EncodedLen(len(s)))
+ base64.StdEncoding.Encode(dst, s)
+ e.Write(dst)
+ } else {
+ // for large buffers, avoid unnecessary extra temporary
+ // buffer space.
+ enc := base64.NewEncoder(base64.StdEncoding, e)
+ enc.Write(s)
+ enc.Close()
+ }
+ e.WriteByte('"')
+ break
+ }
+ e.WriteByte('[')
+ n := v.Len()
+ for i := 0; i < n; i++ {
+ if i > 0 {
+ e.WriteByte(',')
+ }
+ e.reflectValue(v.Index(i))
+ }
+ e.WriteByte(']')
+
+ case interfaceOrPtrValue:
+ if v.IsNil() {
+ e.WriteString("null")
+ return
+ }
+ e.reflectValue(v.Elem())
+
+ default:
+ e.error(&UnsupportedTypeError{v.Type()})
+ }
+ return
+}
+
+func isValidTag(s string) bool {
+ if s == "" {
+ return false
+ }
+ for _, c := range s {
+ if c != '_' && !unicode.IsLetter(c) && !unicode.IsDigit(c) {
+ return false
+ }
+ }
+ return true
+}
+
+// stringValues is a slice of reflect.Value holding *reflect.StringValue.
+// It implements the methods to sort by string.
+type stringValues []reflect.Value
+
+func (sv stringValues) Len() int { return len(sv) }
+func (sv stringValues) Swap(i, j int) { sv[i], sv[j] = sv[j], sv[i] }
+func (sv stringValues) Less(i, j int) bool { return sv.get(i) < sv.get(j) }
+func (sv stringValues) get(i int) string { return sv[i].String() }
+
+func (e *encodeState) string(s string) {
+ e.WriteByte('"')
+ start := 0
+ for i := 0; i < len(s); {
+ if b := s[i]; b < utf8.RuneSelf {
+ if 0x20 <= b && b != '\\' && b != '"' {
+ i++
+ continue
+ }
+ if start < i {
+ e.WriteString(s[start:i])
+ }
+ if b == '\\' || b == '"' {
+ e.WriteByte('\\')
+ e.WriteByte(b)
+ } else {
+ e.WriteString(`\u00`)
+ e.WriteByte(hex[b>>4])
+ e.WriteByte(hex[b&0xF])
+ }
+ i++
+ start = i
+ continue
+ }
+ c, size := utf8.DecodeRuneInString(s[i:])
+ if c == utf8.RuneError && size == 1 {
+ e.error(&InvalidUTF8Error{s})
+ }
+ i += size
+ }
+ if start < len(s) {
+ e.WriteString(s[start:])
+ }
+ e.WriteByte('"')
+}
diff --git a/src/cmd/gofix/testdata/reflect.encoder.go.in b/src/cmd/gofix/testdata/reflect.encoder.go.in
new file mode 100644
index 000000000..e52a4de29
--- /dev/null
+++ b/src/cmd/gofix/testdata/reflect.encoder.go.in
@@ -0,0 +1,240 @@
+// 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 (
+ "bytes"
+ "io"
+ "os"
+ "reflect"
+ "sync"
+)
+
+// An Encoder manages the transmission of type and data information to the
+// other side of a connection.
+type Encoder struct {
+ mutex sync.Mutex // each item must be sent atomically
+ w []io.Writer // where to send the data
+ sent map[reflect.Type]typeId // which types we've already sent
+ countState *encoderState // stage for writing counts
+ freeList *encoderState // list of free encoderStates; avoids reallocation
+ buf []byte // for collecting the output.
+ byteBuf bytes.Buffer // buffer for top-level encoderState
+ err os.Error
+}
+
+// NewEncoder returns a new encoder that will transmit on the io.Writer.
+func NewEncoder(w io.Writer) *Encoder {
+ enc := new(Encoder)
+ enc.w = []io.Writer{w}
+ enc.sent = make(map[reflect.Type]typeId)
+ enc.countState = enc.newEncoderState(new(bytes.Buffer))
+ return enc
+}
+
+// writer() returns the innermost writer the encoder is using
+func (enc *Encoder) writer() io.Writer {
+ return enc.w[len(enc.w)-1]
+}
+
+// pushWriter adds a writer to the encoder.
+func (enc *Encoder) pushWriter(w io.Writer) {
+ enc.w = append(enc.w, w)
+}
+
+// popWriter pops the innermost writer.
+func (enc *Encoder) popWriter() {
+ enc.w = enc.w[0 : len(enc.w)-1]
+}
+
+func (enc *Encoder) badType(rt reflect.Type) {
+ enc.setError(os.ErrorString("gob: can't encode type " + rt.String()))
+}
+
+func (enc *Encoder) setError(err os.Error) {
+ if enc.err == nil { // remember the first.
+ enc.err = err
+ }
+}
+
+// writeMessage sends the data item preceded by a unsigned count of its length.
+func (enc *Encoder) writeMessage(w io.Writer, b *bytes.Buffer) {
+ enc.countState.encodeUint(uint64(b.Len()))
+ // Build the buffer.
+ countLen := enc.countState.b.Len()
+ total := countLen + b.Len()
+ if total > len(enc.buf) {
+ enc.buf = make([]byte, total+1000) // extra for growth
+ }
+ // Place the length before the data.
+ // TODO(r): avoid the extra copy here.
+ enc.countState.b.Read(enc.buf[0:countLen])
+ // Now the data.
+ b.Read(enc.buf[countLen:total])
+ // Write the data.
+ _, err := w.Write(enc.buf[0:total])
+ if err != nil {
+ enc.setError(err)
+ }
+}
+
+// sendActualType sends the requested type, without further investigation, unless
+// it's been sent before.
+func (enc *Encoder) sendActualType(w io.Writer, state *encoderState, ut *userTypeInfo, actual reflect.Type) (sent bool) {
+ if _, alreadySent := enc.sent[actual]; alreadySent {
+ return false
+ }
+ typeLock.Lock()
+ info, err := getTypeInfo(ut)
+ typeLock.Unlock()
+ if err != nil {
+ enc.setError(err)
+ return
+ }
+ // Send the pair (-id, type)
+ // Id:
+ state.encodeInt(-int64(info.id))
+ // Type:
+ enc.encode(state.b, reflect.NewValue(info.wire), wireTypeUserInfo)
+ enc.writeMessage(w, state.b)
+ if enc.err != nil {
+ return
+ }
+
+ // Remember we've sent this type, both what the user gave us and the base type.
+ enc.sent[ut.base] = info.id
+ if ut.user != ut.base {
+ enc.sent[ut.user] = info.id
+ }
+ // Now send the inner types
+ switch st := actual.(type) {
+ case *reflect.StructType:
+ for i := 0; i < st.NumField(); i++ {
+ enc.sendType(w, state, st.Field(i).Type)
+ }
+ case reflect.ArrayOrSliceType:
+ enc.sendType(w, state, st.Elem())
+ }
+ return true
+}
+
+// sendType sends the type info to the other side, if necessary.
+func (enc *Encoder) sendType(w io.Writer, state *encoderState, origt reflect.Type) (sent bool) {
+ ut := userType(origt)
+ if ut.isGobEncoder {
+ // The rules are different: regardless of the underlying type's representation,
+ // we need to tell the other side that this exact type is a GobEncoder.
+ return enc.sendActualType(w, state, ut, ut.user)
+ }
+
+ // It's a concrete value, so drill down to the base type.
+ switch rt := ut.base.(type) {
+ default:
+ // Basic types and interfaces do not need to be described.
+ return
+ case *reflect.SliceType:
+ // If it's []uint8, don't send; it's considered basic.
+ if rt.Elem().Kind() == reflect.Uint8 {
+ return
+ }
+ // Otherwise we do send.
+ break
+ case *reflect.ArrayType:
+ // arrays must be sent so we know their lengths and element types.
+ break
+ case *reflect.MapType:
+ // maps must be sent so we know their lengths and key/value types.
+ break
+ case *reflect.StructType:
+ // structs must be sent so we know their fields.
+ break
+ case *reflect.ChanType, *reflect.FuncType:
+ // Probably a bad field in a struct.
+ enc.badType(rt)
+ return
+ }
+
+ return enc.sendActualType(w, state, ut, ut.base)
+}
+
+// Encode transmits the data item represented by the empty interface value,
+// guaranteeing that all necessary type information has been transmitted first.
+func (enc *Encoder) Encode(e interface{}) os.Error {
+ return enc.EncodeValue(reflect.NewValue(e))
+}
+
+// sendTypeDescriptor makes sure the remote side knows about this type.
+// It will send a descriptor if this is the first time the type has been
+// sent.
+func (enc *Encoder) sendTypeDescriptor(w io.Writer, state *encoderState, ut *userTypeInfo) {
+ // Make sure the type is known to the other side.
+ // First, have we already sent this type?
+ rt := ut.base
+ if ut.isGobEncoder {
+ rt = ut.user
+ }
+ if _, alreadySent := enc.sent[rt]; !alreadySent {
+ // No, so send it.
+ sent := enc.sendType(w, state, rt)
+ if enc.err != nil {
+ return
+ }
+ // If the type info has still not been transmitted, it means we have
+ // a singleton basic type (int, []byte etc.) at top level. We don't
+ // need to send the type info but we do need to update enc.sent.
+ if !sent {
+ typeLock.Lock()
+ info, err := getTypeInfo(ut)
+ typeLock.Unlock()
+ if err != nil {
+ enc.setError(err)
+ return
+ }
+ enc.sent[rt] = info.id
+ }
+ }
+}
+
+// sendTypeId sends the id, which must have already been defined.
+func (enc *Encoder) sendTypeId(state *encoderState, ut *userTypeInfo) {
+ // Identify the type of this top-level value.
+ state.encodeInt(int64(enc.sent[ut.base]))
+}
+
+// EncodeValue transmits the data item represented by the reflection value,
+// guaranteeing that all necessary type information has been transmitted first.
+func (enc *Encoder) EncodeValue(value reflect.Value) os.Error {
+ // Make sure we're single-threaded through here, so multiple
+ // goroutines can share an encoder.
+ enc.mutex.Lock()
+ defer enc.mutex.Unlock()
+
+ // Remove any nested writers remaining due to previous errors.
+ enc.w = enc.w[0:1]
+
+ ut, err := validUserType(value.Type())
+ if err != nil {
+ return err
+ }
+
+ enc.err = nil
+ enc.byteBuf.Reset()
+ state := enc.newEncoderState(&enc.byteBuf)
+
+ enc.sendTypeDescriptor(enc.writer(), state, ut)
+ enc.sendTypeId(state, ut)
+ if enc.err != nil {
+ return enc.err
+ }
+
+ // Encode the object.
+ enc.encode(state.b, value, ut)
+ if enc.err == nil {
+ enc.writeMessage(enc.writer(), state.b)
+ }
+
+ enc.freeEncoderState(state)
+ return enc.err
+}
diff --git a/src/cmd/gofix/testdata/reflect.encoder.go.out b/src/cmd/gofix/testdata/reflect.encoder.go.out
new file mode 100644
index 000000000..928f3b244
--- /dev/null
+++ b/src/cmd/gofix/testdata/reflect.encoder.go.out
@@ -0,0 +1,240 @@
+// 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 (
+ "bytes"
+ "io"
+ "os"
+ "reflect"
+ "sync"
+)
+
+// An Encoder manages the transmission of type and data information to the
+// other side of a connection.
+type Encoder struct {
+ mutex sync.Mutex // each item must be sent atomically
+ w []io.Writer // where to send the data
+ sent map[reflect.Type]typeId // which types we've already sent
+ countState *encoderState // stage for writing counts
+ freeList *encoderState // list of free encoderStates; avoids reallocation
+ buf []byte // for collecting the output.
+ byteBuf bytes.Buffer // buffer for top-level encoderState
+ err os.Error
+}
+
+// NewEncoder returns a new encoder that will transmit on the io.Writer.
+func NewEncoder(w io.Writer) *Encoder {
+ enc := new(Encoder)
+ enc.w = []io.Writer{w}
+ enc.sent = make(map[reflect.Type]typeId)
+ enc.countState = enc.newEncoderState(new(bytes.Buffer))
+ return enc
+}
+
+// writer() returns the innermost writer the encoder is using
+func (enc *Encoder) writer() io.Writer {
+ return enc.w[len(enc.w)-1]
+}
+
+// pushWriter adds a writer to the encoder.
+func (enc *Encoder) pushWriter(w io.Writer) {
+ enc.w = append(enc.w, w)
+}
+
+// popWriter pops the innermost writer.
+func (enc *Encoder) popWriter() {
+ enc.w = enc.w[0 : len(enc.w)-1]
+}
+
+func (enc *Encoder) badType(rt reflect.Type) {
+ enc.setError(os.ErrorString("gob: can't encode type " + rt.String()))
+}
+
+func (enc *Encoder) setError(err os.Error) {
+ if enc.err == nil { // remember the first.
+ enc.err = err
+ }
+}
+
+// writeMessage sends the data item preceded by a unsigned count of its length.
+func (enc *Encoder) writeMessage(w io.Writer, b *bytes.Buffer) {
+ enc.countState.encodeUint(uint64(b.Len()))
+ // Build the buffer.
+ countLen := enc.countState.b.Len()
+ total := countLen + b.Len()
+ if total > len(enc.buf) {
+ enc.buf = make([]byte, total+1000) // extra for growth
+ }
+ // Place the length before the data.
+ // TODO(r): avoid the extra copy here.
+ enc.countState.b.Read(enc.buf[0:countLen])
+ // Now the data.
+ b.Read(enc.buf[countLen:total])
+ // Write the data.
+ _, err := w.Write(enc.buf[0:total])
+ if err != nil {
+ enc.setError(err)
+ }
+}
+
+// sendActualType sends the requested type, without further investigation, unless
+// it's been sent before.
+func (enc *Encoder) sendActualType(w io.Writer, state *encoderState, ut *userTypeInfo, actual reflect.Type) (sent bool) {
+ if _, alreadySent := enc.sent[actual]; alreadySent {
+ return false
+ }
+ typeLock.Lock()
+ info, err := getTypeInfo(ut)
+ typeLock.Unlock()
+ if err != nil {
+ enc.setError(err)
+ return
+ }
+ // Send the pair (-id, type)
+ // Id:
+ state.encodeInt(-int64(info.id))
+ // Type:
+ enc.encode(state.b, reflect.NewValue(info.wire), wireTypeUserInfo)
+ enc.writeMessage(w, state.b)
+ if enc.err != nil {
+ return
+ }
+
+ // Remember we've sent this type, both what the user gave us and the base type.
+ enc.sent[ut.base] = info.id
+ if ut.user != ut.base {
+ enc.sent[ut.user] = info.id
+ }
+ // Now send the inner types
+ switch st := actual; st.Kind() {
+ case reflect.Struct:
+ for i := 0; i < st.NumField(); i++ {
+ enc.sendType(w, state, st.Field(i).Type)
+ }
+ case reflect.Array, reflect.Slice:
+ enc.sendType(w, state, st.Elem())
+ }
+ return true
+}
+
+// sendType sends the type info to the other side, if necessary.
+func (enc *Encoder) sendType(w io.Writer, state *encoderState, origt reflect.Type) (sent bool) {
+ ut := userType(origt)
+ if ut.isGobEncoder {
+ // The rules are different: regardless of the underlying type's representation,
+ // we need to tell the other side that this exact type is a GobEncoder.
+ return enc.sendActualType(w, state, ut, ut.user)
+ }
+
+ // It's a concrete value, so drill down to the base type.
+ switch rt := ut.base; rt.Kind() {
+ default:
+ // Basic types and interfaces do not need to be described.
+ return
+ case reflect.Slice:
+ // If it's []uint8, don't send; it's considered basic.
+ if rt.Elem().Kind() == reflect.Uint8 {
+ return
+ }
+ // Otherwise we do send.
+ break
+ case reflect.Array:
+ // arrays must be sent so we know their lengths and element types.
+ break
+ case reflect.Map:
+ // maps must be sent so we know their lengths and key/value types.
+ break
+ case reflect.Struct:
+ // structs must be sent so we know their fields.
+ break
+ case reflect.Chan, reflect.Func:
+ // Probably a bad field in a struct.
+ enc.badType(rt)
+ return
+ }
+
+ return enc.sendActualType(w, state, ut, ut.base)
+}
+
+// Encode transmits the data item represented by the empty interface value,
+// guaranteeing that all necessary type information has been transmitted first.
+func (enc *Encoder) Encode(e interface{}) os.Error {
+ return enc.EncodeValue(reflect.NewValue(e))
+}
+
+// sendTypeDescriptor makes sure the remote side knows about this type.
+// It will send a descriptor if this is the first time the type has been
+// sent.
+func (enc *Encoder) sendTypeDescriptor(w io.Writer, state *encoderState, ut *userTypeInfo) {
+ // Make sure the type is known to the other side.
+ // First, have we already sent this type?
+ rt := ut.base
+ if ut.isGobEncoder {
+ rt = ut.user
+ }
+ if _, alreadySent := enc.sent[rt]; !alreadySent {
+ // No, so send it.
+ sent := enc.sendType(w, state, rt)
+ if enc.err != nil {
+ return
+ }
+ // If the type info has still not been transmitted, it means we have
+ // a singleton basic type (int, []byte etc.) at top level. We don't
+ // need to send the type info but we do need to update enc.sent.
+ if !sent {
+ typeLock.Lock()
+ info, err := getTypeInfo(ut)
+ typeLock.Unlock()
+ if err != nil {
+ enc.setError(err)
+ return
+ }
+ enc.sent[rt] = info.id
+ }
+ }
+}
+
+// sendTypeId sends the id, which must have already been defined.
+func (enc *Encoder) sendTypeId(state *encoderState, ut *userTypeInfo) {
+ // Identify the type of this top-level value.
+ state.encodeInt(int64(enc.sent[ut.base]))
+}
+
+// EncodeValue transmits the data item represented by the reflection value,
+// guaranteeing that all necessary type information has been transmitted first.
+func (enc *Encoder) EncodeValue(value reflect.Value) os.Error {
+ // Make sure we're single-threaded through here, so multiple
+ // goroutines can share an encoder.
+ enc.mutex.Lock()
+ defer enc.mutex.Unlock()
+
+ // Remove any nested writers remaining due to previous errors.
+ enc.w = enc.w[0:1]
+
+ ut, err := validUserType(value.Type())
+ if err != nil {
+ return err
+ }
+
+ enc.err = nil
+ enc.byteBuf.Reset()
+ state := enc.newEncoderState(&enc.byteBuf)
+
+ enc.sendTypeDescriptor(enc.writer(), state, ut)
+ enc.sendTypeId(state, ut)
+ if enc.err != nil {
+ return enc.err
+ }
+
+ // Encode the object.
+ enc.encode(state.b, value, ut)
+ if enc.err == nil {
+ enc.writeMessage(enc.writer(), state.b)
+ }
+
+ enc.freeEncoderState(state)
+ return enc.err
+}
diff --git a/src/cmd/gofix/testdata/reflect.export.go.in b/src/cmd/gofix/testdata/reflect.export.go.in
new file mode 100644
index 000000000..e91e777e3
--- /dev/null
+++ b/src/cmd/gofix/testdata/reflect.export.go.in
@@ -0,0 +1,400 @@
+// 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 netchan package implements type-safe networked channels:
+ it allows the two ends of a channel to appear on different
+ computers connected by a network. It does this by transporting
+ data sent to a channel on one machine so it can be recovered
+ by a receive of a channel of the same type on the other.
+
+ An exporter publishes a set of channels by name. An importer
+ connects to the exporting machine and imports the channels
+ by name. After importing the channels, the two machines can
+ use the channels in the usual way.
+
+ Networked channels are not synchronized; they always behave
+ as if they are buffered channels of at least one element.
+*/
+package netchan
+
+// BUG: can't use range clause to receive when using ImportNValues to limit the count.
+
+import (
+ "log"
+ "io"
+ "net"
+ "os"
+ "reflect"
+ "strconv"
+ "sync"
+)
+
+// Export
+
+// expLog is a logging convenience function. The first argument must be a string.
+func expLog(args ...interface{}) {
+ args[0] = "netchan export: " + args[0].(string)
+ log.Print(args...)
+}
+
+// An Exporter allows a set of channels to be published on a single
+// network port. A single machine may have multiple Exporters
+// but they must use different ports.
+type Exporter struct {
+ *clientSet
+}
+
+type expClient struct {
+ *encDec
+ exp *Exporter
+ chans map[int]*netChan // channels in use by client
+ mu sync.Mutex // protects remaining fields
+ errored bool // client has been sent an error
+ seqNum int64 // sequences messages sent to client; has value of highest sent
+ ackNum int64 // highest sequence number acknowledged
+ seqLock sync.Mutex // guarantees messages are in sequence, only locked under mu
+}
+
+func newClient(exp *Exporter, conn io.ReadWriter) *expClient {
+ client := new(expClient)
+ client.exp = exp
+ client.encDec = newEncDec(conn)
+ client.seqNum = 0
+ client.ackNum = 0
+ client.chans = make(map[int]*netChan)
+ return client
+}
+
+func (client *expClient) sendError(hdr *header, err string) {
+ error := &error{err}
+ expLog("sending error to client:", error.Error)
+ client.encode(hdr, payError, error) // ignore any encode error, hope client gets it
+ client.mu.Lock()
+ client.errored = true
+ client.mu.Unlock()
+}
+
+func (client *expClient) newChan(hdr *header, dir Dir, name string, size int, count int64) *netChan {
+ exp := client.exp
+ exp.mu.Lock()
+ ech, ok := exp.names[name]
+ exp.mu.Unlock()
+ if !ok {
+ client.sendError(hdr, "no such channel: "+name)
+ return nil
+ }
+ if ech.dir != dir {
+ client.sendError(hdr, "wrong direction for channel: "+name)
+ return nil
+ }
+ nch := newNetChan(name, hdr.Id, ech, client.encDec, size, count)
+ client.chans[hdr.Id] = nch
+ return nch
+}
+
+func (client *expClient) getChan(hdr *header, dir Dir) *netChan {
+ nch := client.chans[hdr.Id]
+ if nch == nil {
+ return nil
+ }
+ if nch.dir != dir {
+ client.sendError(hdr, "wrong direction for channel: "+nch.name)
+ }
+ return nch
+}
+
+// The function run manages sends and receives for a single client. For each
+// (client Recv) request, this will launch a serveRecv goroutine to deliver
+// the data for that channel, while (client Send) requests are handled as
+// data arrives from the client.
+func (client *expClient) run() {
+ hdr := new(header)
+ hdrValue := reflect.NewValue(hdr)
+ req := new(request)
+ reqValue := reflect.NewValue(req)
+ error := new(error)
+ for {
+ *hdr = header{}
+ if err := client.decode(hdrValue); err != nil {
+ if err != os.EOF {
+ expLog("error decoding client header:", err)
+ }
+ break
+ }
+ switch hdr.PayloadType {
+ case payRequest:
+ *req = request{}
+ if err := client.decode(reqValue); err != nil {
+ expLog("error decoding client request:", err)
+ break
+ }
+ if req.Size < 1 {
+ panic("netchan: remote requested " + strconv.Itoa(req.Size) + " values")
+ }
+ switch req.Dir {
+ case Recv:
+ // look up channel before calling serveRecv to
+ // avoid a lock around client.chans.
+ if nch := client.newChan(hdr, Send, req.Name, req.Size, req.Count); nch != nil {
+ go client.serveRecv(nch, *hdr, req.Count)
+ }
+ case Send:
+ client.newChan(hdr, Recv, req.Name, req.Size, req.Count)
+ // The actual sends will have payload type payData.
+ // TODO: manage the count?
+ default:
+ error.Error = "request: can't handle channel direction"
+ expLog(error.Error, req.Dir)
+ client.encode(hdr, payError, error)
+ }
+ case payData:
+ client.serveSend(*hdr)
+ case payClosed:
+ client.serveClosed(*hdr)
+ case payAck:
+ client.mu.Lock()
+ if client.ackNum != hdr.SeqNum-1 {
+ // Since the sequence number is incremented and the message is sent
+ // in a single instance of locking client.mu, the messages are guaranteed
+ // to be sent in order. Therefore receipt of acknowledgement N means
+ // all messages <=N have been seen by the recipient. We check anyway.
+ expLog("sequence out of order:", client.ackNum, hdr.SeqNum)
+ }
+ if client.ackNum < hdr.SeqNum { // If there has been an error, don't back up the count.
+ client.ackNum = hdr.SeqNum
+ }
+ client.mu.Unlock()
+ case payAckSend:
+ if nch := client.getChan(hdr, Send); nch != nil {
+ nch.acked()
+ }
+ default:
+ log.Fatal("netchan export: unknown payload type", hdr.PayloadType)
+ }
+ }
+ client.exp.delClient(client)
+}
+
+// Send all the data on a single channel to a client asking for a Recv.
+// The header is passed by value to avoid issues of overwriting.
+func (client *expClient) serveRecv(nch *netChan, hdr header, count int64) {
+ for {
+ val, ok := nch.recv()
+ if !ok {
+ if err := client.encode(&hdr, payClosed, nil); err != nil {
+ expLog("error encoding server closed message:", err)
+ }
+ break
+ }
+ // We hold the lock during transmission to guarantee messages are
+ // sent in sequence number order. Also, we increment first so the
+ // value of client.SeqNum is the value of the highest used sequence
+ // number, not one beyond.
+ client.mu.Lock()
+ client.seqNum++
+ hdr.SeqNum = client.seqNum
+ client.seqLock.Lock() // guarantee ordering of messages
+ client.mu.Unlock()
+ err := client.encode(&hdr, payData, val.Interface())
+ client.seqLock.Unlock()
+ if err != nil {
+ expLog("error encoding client response:", err)
+ client.sendError(&hdr, err.String())
+ break
+ }
+ // Negative count means run forever.
+ if count >= 0 {
+ if count--; count <= 0 {
+ break
+ }
+ }
+ }
+}
+
+// Receive and deliver locally one item from a client asking for a Send
+// The header is passed by value to avoid issues of overwriting.
+func (client *expClient) serveSend(hdr header) {
+ nch := client.getChan(&hdr, Recv)
+ if nch == nil {
+ return
+ }
+ // Create a new value for each received item.
+ val := reflect.MakeZero(nch.ch.Type().(*reflect.ChanType).Elem())
+ if err := client.decode(val); err != nil {
+ expLog("value decode:", err, "; type ", nch.ch.Type())
+ return
+ }
+ nch.send(val)
+}
+
+// Report that client has closed the channel that is sending to us.
+// The header is passed by value to avoid issues of overwriting.
+func (client *expClient) serveClosed(hdr header) {
+ nch := client.getChan(&hdr, Recv)
+ if nch == nil {
+ return
+ }
+ nch.close()
+}
+
+func (client *expClient) unackedCount() int64 {
+ client.mu.Lock()
+ n := client.seqNum - client.ackNum
+ client.mu.Unlock()
+ return n
+}
+
+func (client *expClient) seq() int64 {
+ client.mu.Lock()
+ n := client.seqNum
+ client.mu.Unlock()
+ return n
+}
+
+func (client *expClient) ack() int64 {
+ client.mu.Lock()
+ n := client.seqNum
+ client.mu.Unlock()
+ return n
+}
+
+// Serve waits for incoming connections on the listener
+// and serves the Exporter's channels on each.
+// It blocks until the listener is closed.
+func (exp *Exporter) Serve(listener net.Listener) {
+ for {
+ conn, err := listener.Accept()
+ if err != nil {
+ expLog("listen:", err)
+ break
+ }
+ go exp.ServeConn(conn)
+ }
+}
+
+// ServeConn exports the Exporter's channels on conn.
+// It blocks until the connection is terminated.
+func (exp *Exporter) ServeConn(conn io.ReadWriter) {
+ exp.addClient(conn).run()
+}
+
+// NewExporter creates a new Exporter that exports a set of channels.
+func NewExporter() *Exporter {
+ e := &Exporter{
+ clientSet: &clientSet{
+ names: make(map[string]*chanDir),
+ clients: make(map[unackedCounter]bool),
+ },
+ }
+ return e
+}
+
+// ListenAndServe exports the exporter's channels through the
+// given network and local address defined as in net.Listen.
+func (exp *Exporter) ListenAndServe(network, localaddr string) os.Error {
+ listener, err := net.Listen(network, localaddr)
+ if err != nil {
+ return err
+ }
+ go exp.Serve(listener)
+ return nil
+}
+
+// addClient creates a new expClient and records its existence
+func (exp *Exporter) addClient(conn io.ReadWriter) *expClient {
+ client := newClient(exp, conn)
+ exp.mu.Lock()
+ exp.clients[client] = true
+ exp.mu.Unlock()
+ return client
+}
+
+// delClient forgets the client existed
+func (exp *Exporter) delClient(client *expClient) {
+ exp.mu.Lock()
+ exp.clients[client] = false, false
+ exp.mu.Unlock()
+}
+
+// Drain waits until all messages sent from this exporter/importer, including
+// those not yet sent to any client and possibly including those sent while
+// Drain was executing, have been received by the importer. In short, it
+// waits until all the exporter's messages have been received by a client.
+// If the timeout (measured in nanoseconds) is positive and Drain takes
+// longer than that to complete, an error is returned.
+func (exp *Exporter) Drain(timeout int64) os.Error {
+ // This wrapper function is here so the method's comment will appear in godoc.
+ return exp.clientSet.drain(timeout)
+}
+
+// Sync waits until all clients of the exporter have received the messages
+// that were sent at the time Sync was invoked. Unlike Drain, it does not
+// wait for messages sent while it is running or messages that have not been
+// dispatched to any client. If the timeout (measured in nanoseconds) is
+// positive and Sync takes longer than that to complete, an error is
+// returned.
+func (exp *Exporter) Sync(timeout int64) os.Error {
+ // This wrapper function is here so the method's comment will appear in godoc.
+ return exp.clientSet.sync(timeout)
+}
+
+func checkChan(chT interface{}, dir Dir) (*reflect.ChanValue, os.Error) {
+ chanType, ok := reflect.Typeof(chT).(*reflect.ChanType)
+ if !ok {
+ return nil, os.ErrorString("not a channel")
+ }
+ if dir != Send && dir != Recv {
+ return nil, os.ErrorString("unknown channel direction")
+ }
+ switch chanType.Dir() {
+ case reflect.BothDir:
+ case reflect.SendDir:
+ if dir != Recv {
+ return nil, os.ErrorString("to import/export with Send, must provide <-chan")
+ }
+ case reflect.RecvDir:
+ if dir != Send {
+ return nil, os.ErrorString("to import/export with Recv, must provide chan<-")
+ }
+ }
+ return reflect.NewValue(chT).(*reflect.ChanValue), nil
+}
+
+// Export exports a channel of a given type and specified direction. The
+// channel to be exported is provided in the call and may be of arbitrary
+// channel type.
+// Despite the literal signature, the effective signature is
+// Export(name string, chT chan T, dir Dir)
+func (exp *Exporter) Export(name string, chT interface{}, dir Dir) os.Error {
+ ch, err := checkChan(chT, dir)
+ if err != nil {
+ return err
+ }
+ exp.mu.Lock()
+ defer exp.mu.Unlock()
+ _, present := exp.names[name]
+ if present {
+ return os.ErrorString("channel name already being exported:" + name)
+ }
+ exp.names[name] = &chanDir{ch, dir}
+ return nil
+}
+
+// Hangup disassociates the named channel from the Exporter and closes
+// the channel. Messages in flight for the channel may be dropped.
+func (exp *Exporter) Hangup(name string) os.Error {
+ exp.mu.Lock()
+ chDir, ok := exp.names[name]
+ if ok {
+ exp.names[name] = nil, false
+ }
+ // TODO drop all instances of channel from client sets
+ exp.mu.Unlock()
+ if !ok {
+ return os.ErrorString("netchan export: hangup: no such channel: " + name)
+ }
+ chDir.ch.Close()
+ return nil
+}
diff --git a/src/cmd/gofix/testdata/reflect.export.go.out b/src/cmd/gofix/testdata/reflect.export.go.out
new file mode 100644
index 000000000..2209f04e8
--- /dev/null
+++ b/src/cmd/gofix/testdata/reflect.export.go.out
@@ -0,0 +1,400 @@
+// 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 netchan package implements type-safe networked channels:
+ it allows the two ends of a channel to appear on different
+ computers connected by a network. It does this by transporting
+ data sent to a channel on one machine so it can be recovered
+ by a receive of a channel of the same type on the other.
+
+ An exporter publishes a set of channels by name. An importer
+ connects to the exporting machine and imports the channels
+ by name. After importing the channels, the two machines can
+ use the channels in the usual way.
+
+ Networked channels are not synchronized; they always behave
+ as if they are buffered channels of at least one element.
+*/
+package netchan
+
+// BUG: can't use range clause to receive when using ImportNValues to limit the count.
+
+import (
+ "log"
+ "io"
+ "net"
+ "os"
+ "reflect"
+ "strconv"
+ "sync"
+)
+
+// Export
+
+// expLog is a logging convenience function. The first argument must be a string.
+func expLog(args ...interface{}) {
+ args[0] = "netchan export: " + args[0].(string)
+ log.Print(args...)
+}
+
+// An Exporter allows a set of channels to be published on a single
+// network port. A single machine may have multiple Exporters
+// but they must use different ports.
+type Exporter struct {
+ *clientSet
+}
+
+type expClient struct {
+ *encDec
+ exp *Exporter
+ chans map[int]*netChan // channels in use by client
+ mu sync.Mutex // protects remaining fields
+ errored bool // client has been sent an error
+ seqNum int64 // sequences messages sent to client; has value of highest sent
+ ackNum int64 // highest sequence number acknowledged
+ seqLock sync.Mutex // guarantees messages are in sequence, only locked under mu
+}
+
+func newClient(exp *Exporter, conn io.ReadWriter) *expClient {
+ client := new(expClient)
+ client.exp = exp
+ client.encDec = newEncDec(conn)
+ client.seqNum = 0
+ client.ackNum = 0
+ client.chans = make(map[int]*netChan)
+ return client
+}
+
+func (client *expClient) sendError(hdr *header, err string) {
+ error := &error{err}
+ expLog("sending error to client:", error.Error)
+ client.encode(hdr, payError, error) // ignore any encode error, hope client gets it
+ client.mu.Lock()
+ client.errored = true
+ client.mu.Unlock()
+}
+
+func (client *expClient) newChan(hdr *header, dir Dir, name string, size int, count int64) *netChan {
+ exp := client.exp
+ exp.mu.Lock()
+ ech, ok := exp.names[name]
+ exp.mu.Unlock()
+ if !ok {
+ client.sendError(hdr, "no such channel: "+name)
+ return nil
+ }
+ if ech.dir != dir {
+ client.sendError(hdr, "wrong direction for channel: "+name)
+ return nil
+ }
+ nch := newNetChan(name, hdr.Id, ech, client.encDec, size, count)
+ client.chans[hdr.Id] = nch
+ return nch
+}
+
+func (client *expClient) getChan(hdr *header, dir Dir) *netChan {
+ nch := client.chans[hdr.Id]
+ if nch == nil {
+ return nil
+ }
+ if nch.dir != dir {
+ client.sendError(hdr, "wrong direction for channel: "+nch.name)
+ }
+ return nch
+}
+
+// The function run manages sends and receives for a single client. For each
+// (client Recv) request, this will launch a serveRecv goroutine to deliver
+// the data for that channel, while (client Send) requests are handled as
+// data arrives from the client.
+func (client *expClient) run() {
+ hdr := new(header)
+ hdrValue := reflect.NewValue(hdr)
+ req := new(request)
+ reqValue := reflect.NewValue(req)
+ error := new(error)
+ for {
+ *hdr = header{}
+ if err := client.decode(hdrValue); err != nil {
+ if err != os.EOF {
+ expLog("error decoding client header:", err)
+ }
+ break
+ }
+ switch hdr.PayloadType {
+ case payRequest:
+ *req = request{}
+ if err := client.decode(reqValue); err != nil {
+ expLog("error decoding client request:", err)
+ break
+ }
+ if req.Size < 1 {
+ panic("netchan: remote requested " + strconv.Itoa(req.Size) + " values")
+ }
+ switch req.Dir {
+ case Recv:
+ // look up channel before calling serveRecv to
+ // avoid a lock around client.chans.
+ if nch := client.newChan(hdr, Send, req.Name, req.Size, req.Count); nch != nil {
+ go client.serveRecv(nch, *hdr, req.Count)
+ }
+ case Send:
+ client.newChan(hdr, Recv, req.Name, req.Size, req.Count)
+ // The actual sends will have payload type payData.
+ // TODO: manage the count?
+ default:
+ error.Error = "request: can't handle channel direction"
+ expLog(error.Error, req.Dir)
+ client.encode(hdr, payError, error)
+ }
+ case payData:
+ client.serveSend(*hdr)
+ case payClosed:
+ client.serveClosed(*hdr)
+ case payAck:
+ client.mu.Lock()
+ if client.ackNum != hdr.SeqNum-1 {
+ // Since the sequence number is incremented and the message is sent
+ // in a single instance of locking client.mu, the messages are guaranteed
+ // to be sent in order. Therefore receipt of acknowledgement N means
+ // all messages <=N have been seen by the recipient. We check anyway.
+ expLog("sequence out of order:", client.ackNum, hdr.SeqNum)
+ }
+ if client.ackNum < hdr.SeqNum { // If there has been an error, don't back up the count.
+ client.ackNum = hdr.SeqNum
+ }
+ client.mu.Unlock()
+ case payAckSend:
+ if nch := client.getChan(hdr, Send); nch != nil {
+ nch.acked()
+ }
+ default:
+ log.Fatal("netchan export: unknown payload type", hdr.PayloadType)
+ }
+ }
+ client.exp.delClient(client)
+}
+
+// Send all the data on a single channel to a client asking for a Recv.
+// The header is passed by value to avoid issues of overwriting.
+func (client *expClient) serveRecv(nch *netChan, hdr header, count int64) {
+ for {
+ val, ok := nch.recv()
+ if !ok {
+ if err := client.encode(&hdr, payClosed, nil); err != nil {
+ expLog("error encoding server closed message:", err)
+ }
+ break
+ }
+ // We hold the lock during transmission to guarantee messages are
+ // sent in sequence number order. Also, we increment first so the
+ // value of client.SeqNum is the value of the highest used sequence
+ // number, not one beyond.
+ client.mu.Lock()
+ client.seqNum++
+ hdr.SeqNum = client.seqNum
+ client.seqLock.Lock() // guarantee ordering of messages
+ client.mu.Unlock()
+ err := client.encode(&hdr, payData, val.Interface())
+ client.seqLock.Unlock()
+ if err != nil {
+ expLog("error encoding client response:", err)
+ client.sendError(&hdr, err.String())
+ break
+ }
+ // Negative count means run forever.
+ if count >= 0 {
+ if count--; count <= 0 {
+ break
+ }
+ }
+ }
+}
+
+// Receive and deliver locally one item from a client asking for a Send
+// The header is passed by value to avoid issues of overwriting.
+func (client *expClient) serveSend(hdr header) {
+ nch := client.getChan(&hdr, Recv)
+ if nch == nil {
+ return
+ }
+ // Create a new value for each received item.
+ val := reflect.Zero(nch.ch.Type().Elem())
+ if err := client.decode(val); err != nil {
+ expLog("value decode:", err, "; type ", nch.ch.Type())
+ return
+ }
+ nch.send(val)
+}
+
+// Report that client has closed the channel that is sending to us.
+// The header is passed by value to avoid issues of overwriting.
+func (client *expClient) serveClosed(hdr header) {
+ nch := client.getChan(&hdr, Recv)
+ if nch == nil {
+ return
+ }
+ nch.close()
+}
+
+func (client *expClient) unackedCount() int64 {
+ client.mu.Lock()
+ n := client.seqNum - client.ackNum
+ client.mu.Unlock()
+ return n
+}
+
+func (client *expClient) seq() int64 {
+ client.mu.Lock()
+ n := client.seqNum
+ client.mu.Unlock()
+ return n
+}
+
+func (client *expClient) ack() int64 {
+ client.mu.Lock()
+ n := client.seqNum
+ client.mu.Unlock()
+ return n
+}
+
+// Serve waits for incoming connections on the listener
+// and serves the Exporter's channels on each.
+// It blocks until the listener is closed.
+func (exp *Exporter) Serve(listener net.Listener) {
+ for {
+ conn, err := listener.Accept()
+ if err != nil {
+ expLog("listen:", err)
+ break
+ }
+ go exp.ServeConn(conn)
+ }
+}
+
+// ServeConn exports the Exporter's channels on conn.
+// It blocks until the connection is terminated.
+func (exp *Exporter) ServeConn(conn io.ReadWriter) {
+ exp.addClient(conn).run()
+}
+
+// NewExporter creates a new Exporter that exports a set of channels.
+func NewExporter() *Exporter {
+ e := &Exporter{
+ clientSet: &clientSet{
+ names: make(map[string]*chanDir),
+ clients: make(map[unackedCounter]bool),
+ },
+ }
+ return e
+}
+
+// ListenAndServe exports the exporter's channels through the
+// given network and local address defined as in net.Listen.
+func (exp *Exporter) ListenAndServe(network, localaddr string) os.Error {
+ listener, err := net.Listen(network, localaddr)
+ if err != nil {
+ return err
+ }
+ go exp.Serve(listener)
+ return nil
+}
+
+// addClient creates a new expClient and records its existence
+func (exp *Exporter) addClient(conn io.ReadWriter) *expClient {
+ client := newClient(exp, conn)
+ exp.mu.Lock()
+ exp.clients[client] = true
+ exp.mu.Unlock()
+ return client
+}
+
+// delClient forgets the client existed
+func (exp *Exporter) delClient(client *expClient) {
+ exp.mu.Lock()
+ exp.clients[client] = false, false
+ exp.mu.Unlock()
+}
+
+// Drain waits until all messages sent from this exporter/importer, including
+// those not yet sent to any client and possibly including those sent while
+// Drain was executing, have been received by the importer. In short, it
+// waits until all the exporter's messages have been received by a client.
+// If the timeout (measured in nanoseconds) is positive and Drain takes
+// longer than that to complete, an error is returned.
+func (exp *Exporter) Drain(timeout int64) os.Error {
+ // This wrapper function is here so the method's comment will appear in godoc.
+ return exp.clientSet.drain(timeout)
+}
+
+// Sync waits until all clients of the exporter have received the messages
+// that were sent at the time Sync was invoked. Unlike Drain, it does not
+// wait for messages sent while it is running or messages that have not been
+// dispatched to any client. If the timeout (measured in nanoseconds) is
+// positive and Sync takes longer than that to complete, an error is
+// returned.
+func (exp *Exporter) Sync(timeout int64) os.Error {
+ // This wrapper function is here so the method's comment will appear in godoc.
+ return exp.clientSet.sync(timeout)
+}
+
+func checkChan(chT interface{}, dir Dir) (reflect.Value, os.Error) {
+ chanType := reflect.Typeof(chT)
+ if chanType.Kind() != reflect.Chan {
+ return reflect.Value{}, os.ErrorString("not a channel")
+ }
+ if dir != Send && dir != Recv {
+ return reflect.Value{}, os.ErrorString("unknown channel direction")
+ }
+ switch chanType.ChanDir() {
+ case reflect.BothDir:
+ case reflect.SendDir:
+ if dir != Recv {
+ return reflect.Value{}, os.ErrorString("to import/export with Send, must provide <-chan")
+ }
+ case reflect.RecvDir:
+ if dir != Send {
+ return reflect.Value{}, os.ErrorString("to import/export with Recv, must provide chan<-")
+ }
+ }
+ return reflect.NewValue(chT), nil
+}
+
+// Export exports a channel of a given type and specified direction. The
+// channel to be exported is provided in the call and may be of arbitrary
+// channel type.
+// Despite the literal signature, the effective signature is
+// Export(name string, chT chan T, dir Dir)
+func (exp *Exporter) Export(name string, chT interface{}, dir Dir) os.Error {
+ ch, err := checkChan(chT, dir)
+ if err != nil {
+ return err
+ }
+ exp.mu.Lock()
+ defer exp.mu.Unlock()
+ _, present := exp.names[name]
+ if present {
+ return os.ErrorString("channel name already being exported:" + name)
+ }
+ exp.names[name] = &chanDir{ch, dir}
+ return nil
+}
+
+// Hangup disassociates the named channel from the Exporter and closes
+// the channel. Messages in flight for the channel may be dropped.
+func (exp *Exporter) Hangup(name string) os.Error {
+ exp.mu.Lock()
+ chDir, ok := exp.names[name]
+ if ok {
+ exp.names[name] = nil, false
+ }
+ // TODO drop all instances of channel from client sets
+ exp.mu.Unlock()
+ if !ok {
+ return os.ErrorString("netchan export: hangup: no such channel: " + name)
+ }
+ chDir.ch.Close()
+ return nil
+}
diff --git a/src/cmd/gofix/testdata/reflect.print.go.in b/src/cmd/gofix/testdata/reflect.print.go.in
new file mode 100644
index 000000000..194bcdb3b
--- /dev/null
+++ b/src/cmd/gofix/testdata/reflect.print.go.in
@@ -0,0 +1,945 @@
+// 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 fmt
+
+import (
+ "bytes"
+ "io"
+ "os"
+ "reflect"
+ "utf8"
+)
+
+// Some constants in the form of bytes, to avoid string overhead.
+// Needlessly fastidious, I suppose.
+var (
+ commaSpaceBytes = []byte(", ")
+ nilAngleBytes = []byte("<nil>")
+ nilParenBytes = []byte("(nil)")
+ nilBytes = []byte("nil")
+ mapBytes = []byte("map[")
+ missingBytes = []byte("(MISSING)")
+ extraBytes = []byte("%!(EXTRA ")
+ irparenBytes = []byte("i)")
+ bytesBytes = []byte("[]byte{")
+ widthBytes = []byte("%!(BADWIDTH)")
+ precBytes = []byte("%!(BADPREC)")
+ noVerbBytes = []byte("%!(NOVERB)")
+)
+
+// State represents the printer state passed to custom formatters.
+// It provides access to the io.Writer interface plus information about
+// the flags and options for the operand's format specifier.
+type State interface {
+ // Write is the function to call to emit formatted output to be printed.
+ Write(b []byte) (ret int, err os.Error)
+ // Width returns the value of the width option and whether it has been set.
+ Width() (wid int, ok bool)
+ // Precision returns the value of the precision option and whether it has been set.
+ Precision() (prec int, ok bool)
+
+ // Flag returns whether the flag c, a character, has been set.
+ Flag(int) bool
+}
+
+// Formatter is the interface implemented by values with a custom formatter.
+// The implementation of Format may call Sprintf or Fprintf(f) etc.
+// to generate its output.
+type Formatter interface {
+ Format(f State, c int)
+}
+
+// Stringer is implemented by any value that has a String method(),
+// which defines the ``native'' format for that value.
+// The String method is used to print values passed as an operand
+// to a %s or %v format or to an unformatted printer such as Print.
+type Stringer interface {
+ String() string
+}
+
+// GoStringer is implemented by any value that has a GoString() method,
+// which defines the Go syntax for that value.
+// The GoString method is used to print values passed as an operand
+// to a %#v format.
+type GoStringer interface {
+ GoString() string
+}
+
+type pp struct {
+ n int
+ buf bytes.Buffer
+ runeBuf [utf8.UTFMax]byte
+ fmt fmt
+}
+
+// A cache holds a set of reusable objects.
+// The buffered channel holds the currently available objects.
+// If more are needed, the cache creates them by calling new.
+type cache struct {
+ saved chan interface{}
+ new func() interface{}
+}
+
+func (c *cache) put(x interface{}) {
+ select {
+ case c.saved <- x:
+ // saved in cache
+ default:
+ // discard
+ }
+}
+
+func (c *cache) get() interface{} {
+ select {
+ case x := <-c.saved:
+ return x // reused from cache
+ default:
+ return c.new()
+ }
+ panic("not reached")
+}
+
+func newCache(f func() interface{}) *cache {
+ return &cache{make(chan interface{}, 100), f}
+}
+
+var ppFree = newCache(func() interface{} { return new(pp) })
+
+// Allocate a new pp struct or grab a cached one.
+func newPrinter() *pp {
+ p := ppFree.get().(*pp)
+ p.fmt.init(&p.buf)
+ return p
+}
+
+// Save used pp structs in ppFree; avoids an allocation per invocation.
+func (p *pp) free() {
+ // Don't hold on to pp structs with large buffers.
+ if cap(p.buf.Bytes()) > 1024 {
+ return
+ }
+ p.buf.Reset()
+ ppFree.put(p)
+}
+
+func (p *pp) Width() (wid int, ok bool) { return p.fmt.wid, p.fmt.widPresent }
+
+func (p *pp) Precision() (prec int, ok bool) { return p.fmt.prec, p.fmt.precPresent }
+
+func (p *pp) Flag(b int) bool {
+ switch b {
+ case '-':
+ return p.fmt.minus
+ case '+':
+ return p.fmt.plus
+ case '#':
+ return p.fmt.sharp
+ case ' ':
+ return p.fmt.space
+ case '0':
+ return p.fmt.zero
+ }
+ return false
+}
+
+func (p *pp) add(c int) {
+ p.buf.WriteRune(c)
+}
+
+// Implement Write so we can call Fprintf on a pp (through State), for
+// recursive use in custom verbs.
+func (p *pp) Write(b []byte) (ret int, err os.Error) {
+ return p.buf.Write(b)
+}
+
+// These routines end in 'f' and take a format string.
+
+// Fprintf formats according to a format specifier and writes to w.
+// It returns the number of bytes written and any write error encountered.
+func Fprintf(w io.Writer, format string, a ...interface{}) (n int, error os.Error) {
+ p := newPrinter()
+ p.doPrintf(format, a)
+ n64, error := p.buf.WriteTo(w)
+ p.free()
+ return int(n64), error
+}
+
+// Printf formats according to a format specifier and writes to standard output.
+// It returns the number of bytes written and any write error encountered.
+func Printf(format string, a ...interface{}) (n int, errno os.Error) {
+ n, errno = Fprintf(os.Stdout, format, a...)
+ return n, errno
+}
+
+// Sprintf formats according to a format specifier and returns the resulting string.
+func Sprintf(format string, a ...interface{}) string {
+ p := newPrinter()
+ p.doPrintf(format, a)
+ s := p.buf.String()
+ p.free()
+ return s
+}
+
+// Errorf formats according to a format specifier and returns the string
+// converted to an os.ErrorString, which satisfies the os.Error interface.
+func Errorf(format string, a ...interface{}) os.Error {
+ return os.ErrorString(Sprintf(format, a...))
+}
+
+// These routines do not take a format string
+
+// Fprint formats using the default formats for its operands and writes to w.
+// Spaces are added between operands when neither is a string.
+// It returns the number of bytes written and any write error encountered.
+func Fprint(w io.Writer, a ...interface{}) (n int, error os.Error) {
+ p := newPrinter()
+ p.doPrint(a, false, false)
+ n64, error := p.buf.WriteTo(w)
+ p.free()
+ return int(n64), error
+}
+
+// Print formats using the default formats for its operands and writes to standard output.
+// Spaces are added between operands when neither is a string.
+// It returns the number of bytes written and any write error encountered.
+func Print(a ...interface{}) (n int, errno os.Error) {
+ n, errno = Fprint(os.Stdout, a...)
+ return n, errno
+}
+
+// Sprint formats using the default formats for its operands and returns the resulting string.
+// Spaces are added between operands when neither is a string.
+func Sprint(a ...interface{}) string {
+ p := newPrinter()
+ p.doPrint(a, false, false)
+ s := p.buf.String()
+ p.free()
+ return s
+}
+
+// These routines end in 'ln', do not take a format string,
+// always add spaces between operands, and add a newline
+// after the last operand.
+
+// Fprintln formats using the default formats for its operands and writes to w.
+// Spaces are always added between operands and a newline is appended.
+// It returns the number of bytes written and any write error encountered.
+func Fprintln(w io.Writer, a ...interface{}) (n int, error os.Error) {
+ p := newPrinter()
+ p.doPrint(a, true, true)
+ n64, error := p.buf.WriteTo(w)
+ p.free()
+ return int(n64), error
+}
+
+// Println formats using the default formats for its operands and writes to standard output.
+// Spaces are always added between operands and a newline is appended.
+// It returns the number of bytes written and any write error encountered.
+func Println(a ...interface{}) (n int, errno os.Error) {
+ n, errno = Fprintln(os.Stdout, a...)
+ return n, errno
+}
+
+// Sprintln formats using the default formats for its operands and returns the resulting string.
+// Spaces are always added between operands and a newline is appended.
+func Sprintln(a ...interface{}) string {
+ p := newPrinter()
+ p.doPrint(a, true, true)
+ s := p.buf.String()
+ p.free()
+ return s
+}
+
+
+// Get the i'th arg of the struct value.
+// If the arg itself is an interface, return a value for
+// the thing inside the interface, not the interface itself.
+func getField(v *reflect.StructValue, i int) reflect.Value {
+ val := v.Field(i)
+ if i, ok := val.(*reflect.InterfaceValue); ok {
+ if inter := i.Interface(); inter != nil {
+ return reflect.NewValue(inter)
+ }
+ }
+ return val
+}
+
+// Convert ASCII to integer. n is 0 (and got is false) if no number present.
+func parsenum(s string, start, end int) (num int, isnum bool, newi int) {
+ if start >= end {
+ return 0, false, end
+ }
+ for newi = start; newi < end && '0' <= s[newi] && s[newi] <= '9'; newi++ {
+ num = num*10 + int(s[newi]-'0')
+ isnum = true
+ }
+ return
+}
+
+func (p *pp) unknownType(v interface{}) {
+ if v == nil {
+ p.buf.Write(nilAngleBytes)
+ return
+ }
+ p.buf.WriteByte('?')
+ p.buf.WriteString(reflect.Typeof(v).String())
+ p.buf.WriteByte('?')
+}
+
+func (p *pp) badVerb(verb int, val interface{}) {
+ p.add('%')
+ p.add('!')
+ p.add(verb)
+ p.add('(')
+ if val == nil {
+ p.buf.Write(nilAngleBytes)
+ } else {
+ p.buf.WriteString(reflect.Typeof(val).String())
+ p.add('=')
+ p.printField(val, 'v', false, false, 0)
+ }
+ p.add(')')
+}
+
+func (p *pp) fmtBool(v bool, verb int, value interface{}) {
+ switch verb {
+ case 't', 'v':
+ p.fmt.fmt_boolean(v)
+ default:
+ p.badVerb(verb, value)
+ }
+}
+
+// fmtC formats a rune for the 'c' format.
+func (p *pp) fmtC(c int64) {
+ rune := int(c) // Check for overflow.
+ if int64(rune) != c {
+ rune = utf8.RuneError
+ }
+ w := utf8.EncodeRune(p.runeBuf[0:utf8.UTFMax], rune)
+ p.fmt.pad(p.runeBuf[0:w])
+}
+
+func (p *pp) fmtInt64(v int64, verb int, value interface{}) {
+ switch verb {
+ case 'b':
+ p.fmt.integer(v, 2, signed, ldigits)
+ case 'c':
+ p.fmtC(v)
+ case 'd', 'v':
+ p.fmt.integer(v, 10, signed, ldigits)
+ case 'o':
+ p.fmt.integer(v, 8, signed, ldigits)
+ case 'x':
+ p.fmt.integer(v, 16, signed, ldigits)
+ case 'U':
+ p.fmtUnicode(v)
+ case 'X':
+ p.fmt.integer(v, 16, signed, udigits)
+ default:
+ p.badVerb(verb, value)
+ }
+}
+
+// fmt0x64 formats a uint64 in hexadecimal and prefixes it with 0x or
+// not, as requested, by temporarily setting the sharp flag.
+func (p *pp) fmt0x64(v uint64, leading0x bool) {
+ sharp := p.fmt.sharp
+ p.fmt.sharp = leading0x
+ p.fmt.integer(int64(v), 16, unsigned, ldigits)
+ p.fmt.sharp = sharp
+}
+
+// fmtUnicode formats a uint64 in U+1234 form by
+// temporarily turning on the unicode flag and tweaking the precision.
+func (p *pp) fmtUnicode(v int64) {
+ precPresent := p.fmt.precPresent
+ prec := p.fmt.prec
+ if !precPresent {
+ // If prec is already set, leave it alone; otherwise 4 is minimum.
+ p.fmt.prec = 4
+ p.fmt.precPresent = true
+ }
+ p.fmt.unicode = true // turn on U+
+ p.fmt.integer(int64(v), 16, unsigned, udigits)
+ p.fmt.unicode = false
+ p.fmt.prec = prec
+ p.fmt.precPresent = precPresent
+}
+
+func (p *pp) fmtUint64(v uint64, verb int, goSyntax bool, value interface{}) {
+ switch verb {
+ case 'b':
+ p.fmt.integer(int64(v), 2, unsigned, ldigits)
+ case 'c':
+ p.fmtC(int64(v))
+ case 'd':
+ p.fmt.integer(int64(v), 10, unsigned, ldigits)
+ case 'v':
+ if goSyntax {
+ p.fmt0x64(v, true)
+ } else {
+ p.fmt.integer(int64(v), 10, unsigned, ldigits)
+ }
+ case 'o':
+ p.fmt.integer(int64(v), 8, unsigned, ldigits)
+ case 'x':
+ p.fmt.integer(int64(v), 16, unsigned, ldigits)
+ case 'X':
+ p.fmt.integer(int64(v), 16, unsigned, udigits)
+ default:
+ p.badVerb(verb, value)
+ }
+}
+
+func (p *pp) fmtFloat32(v float32, verb int, value interface{}) {
+ switch verb {
+ case 'b':
+ p.fmt.fmt_fb32(v)
+ case 'e':
+ p.fmt.fmt_e32(v)
+ case 'E':
+ p.fmt.fmt_E32(v)
+ case 'f':
+ p.fmt.fmt_f32(v)
+ case 'g', 'v':
+ p.fmt.fmt_g32(v)
+ case 'G':
+ p.fmt.fmt_G32(v)
+ default:
+ p.badVerb(verb, value)
+ }
+}
+
+func (p *pp) fmtFloat64(v float64, verb int, value interface{}) {
+ switch verb {
+ case 'b':
+ p.fmt.fmt_fb64(v)
+ case 'e':
+ p.fmt.fmt_e64(v)
+ case 'E':
+ p.fmt.fmt_E64(v)
+ case 'f':
+ p.fmt.fmt_f64(v)
+ case 'g', 'v':
+ p.fmt.fmt_g64(v)
+ case 'G':
+ p.fmt.fmt_G64(v)
+ default:
+ p.badVerb(verb, value)
+ }
+}
+
+func (p *pp) fmtComplex64(v complex64, verb int, value interface{}) {
+ switch verb {
+ case 'e', 'E', 'f', 'F', 'g', 'G':
+ p.fmt.fmt_c64(v, verb)
+ case 'v':
+ p.fmt.fmt_c64(v, 'g')
+ default:
+ p.badVerb(verb, value)
+ }
+}
+
+func (p *pp) fmtComplex128(v complex128, verb int, value interface{}) {
+ switch verb {
+ case 'e', 'E', 'f', 'F', 'g', 'G':
+ p.fmt.fmt_c128(v, verb)
+ case 'v':
+ p.fmt.fmt_c128(v, 'g')
+ default:
+ p.badVerb(verb, value)
+ }
+}
+
+func (p *pp) fmtString(v string, verb int, goSyntax bool, value interface{}) {
+ switch verb {
+ case 'v':
+ if goSyntax {
+ p.fmt.fmt_q(v)
+ } else {
+ p.fmt.fmt_s(v)
+ }
+ case 's':
+ p.fmt.fmt_s(v)
+ case 'x':
+ p.fmt.fmt_sx(v)
+ case 'X':
+ p.fmt.fmt_sX(v)
+ case 'q':
+ p.fmt.fmt_q(v)
+ default:
+ p.badVerb(verb, value)
+ }
+}
+
+func (p *pp) fmtBytes(v []byte, verb int, goSyntax bool, depth int, value interface{}) {
+ if verb == 'v' || verb == 'd' {
+ if goSyntax {
+ p.buf.Write(bytesBytes)
+ } else {
+ p.buf.WriteByte('[')
+ }
+ for i, c := range v {
+ if i > 0 {
+ if goSyntax {
+ p.buf.Write(commaSpaceBytes)
+ } else {
+ p.buf.WriteByte(' ')
+ }
+ }
+ p.printField(c, 'v', p.fmt.plus, goSyntax, depth+1)
+ }
+ if goSyntax {
+ p.buf.WriteByte('}')
+ } else {
+ p.buf.WriteByte(']')
+ }
+ return
+ }
+ s := string(v)
+ switch verb {
+ case 's':
+ p.fmt.fmt_s(s)
+ case 'x':
+ p.fmt.fmt_sx(s)
+ case 'X':
+ p.fmt.fmt_sX(s)
+ case 'q':
+ p.fmt.fmt_q(s)
+ default:
+ p.badVerb(verb, value)
+ }
+}
+
+func (p *pp) fmtPointer(field interface{}, value reflect.Value, verb int, goSyntax bool) {
+ var u uintptr
+ switch value.(type) {
+ case *reflect.ChanValue, *reflect.FuncValue, *reflect.MapValue, *reflect.PtrValue, *reflect.SliceValue, *reflect.UnsafePointerValue:
+ u = value.Pointer()
+ default:
+ p.badVerb(verb, field)
+ return
+ }
+ if goSyntax {
+ p.add('(')
+ p.buf.WriteString(reflect.Typeof(field).String())
+ p.add(')')
+ p.add('(')
+ if u == 0 {
+ p.buf.Write(nilBytes)
+ } else {
+ p.fmt0x64(uint64(u), true)
+ }
+ p.add(')')
+ } else {
+ p.fmt0x64(uint64(u), !p.fmt.sharp)
+ }
+}
+
+var (
+ intBits = reflect.Typeof(0).Bits()
+ floatBits = reflect.Typeof(0.0).Bits()
+ complexBits = reflect.Typeof(1i).Bits()
+ uintptrBits = reflect.Typeof(uintptr(0)).Bits()
+)
+
+func (p *pp) printField(field interface{}, verb int, plus, goSyntax bool, depth int) (wasString bool) {
+ if field == nil {
+ if verb == 'T' || verb == 'v' {
+ p.buf.Write(nilAngleBytes)
+ } else {
+ p.badVerb(verb, field)
+ }
+ return false
+ }
+
+ // Special processing considerations.
+ // %T (the value's type) and %p (its address) are special; we always do them first.
+ switch verb {
+ case 'T':
+ p.printField(reflect.Typeof(field).String(), 's', false, false, 0)
+ return false
+ case 'p':
+ p.fmtPointer(field, reflect.NewValue(field), verb, goSyntax)
+ return false
+ }
+ // Is it a Formatter?
+ if formatter, ok := field.(Formatter); ok {
+ formatter.Format(p, verb)
+ return false // this value is not a string
+
+ }
+ // Must not touch flags before Formatter looks at them.
+ if plus {
+ p.fmt.plus = false
+ }
+ // If we're doing Go syntax and the field knows how to supply it, take care of it now.
+ if goSyntax {
+ p.fmt.sharp = false
+ if stringer, ok := field.(GoStringer); ok {
+ // Print the result of GoString unadorned.
+ p.fmtString(stringer.GoString(), 's', false, field)
+ return false // this value is not a string
+ }
+ } else {
+ // Is it a Stringer?
+ if stringer, ok := field.(Stringer); ok {
+ p.printField(stringer.String(), verb, plus, false, depth)
+ return false // this value is not a string
+ }
+ }
+
+ // Some types can be done without reflection.
+ switch f := field.(type) {
+ case bool:
+ p.fmtBool(f, verb, field)
+ return false
+ case float32:
+ p.fmtFloat32(f, verb, field)
+ return false
+ case float64:
+ p.fmtFloat64(f, verb, field)
+ return false
+ case complex64:
+ p.fmtComplex64(complex64(f), verb, field)
+ return false
+ case complex128:
+ p.fmtComplex128(f, verb, field)
+ return false
+ case int:
+ p.fmtInt64(int64(f), verb, field)
+ return false
+ case int8:
+ p.fmtInt64(int64(f), verb, field)
+ return false
+ case int16:
+ p.fmtInt64(int64(f), verb, field)
+ return false
+ case int32:
+ p.fmtInt64(int64(f), verb, field)
+ return false
+ case int64:
+ p.fmtInt64(f, verb, field)
+ return false
+ case uint:
+ p.fmtUint64(uint64(f), verb, goSyntax, field)
+ return false
+ case uint8:
+ p.fmtUint64(uint64(f), verb, goSyntax, field)
+ return false
+ case uint16:
+ p.fmtUint64(uint64(f), verb, goSyntax, field)
+ return false
+ case uint32:
+ p.fmtUint64(uint64(f), verb, goSyntax, field)
+ return false
+ case uint64:
+ p.fmtUint64(f, verb, goSyntax, field)
+ return false
+ case uintptr:
+ p.fmtUint64(uint64(f), verb, goSyntax, field)
+ return false
+ case string:
+ p.fmtString(f, verb, goSyntax, field)
+ return verb == 's' || verb == 'v'
+ case []byte:
+ p.fmtBytes(f, verb, goSyntax, depth, field)
+ return verb == 's'
+ }
+
+ // Need to use reflection
+ value := reflect.NewValue(field)
+
+BigSwitch:
+ switch f := value.(type) {
+ case *reflect.BoolValue:
+ p.fmtBool(f.Get(), verb, field)
+ case *reflect.IntValue:
+ p.fmtInt64(f.Get(), verb, field)
+ case *reflect.UintValue:
+ p.fmtUint64(uint64(f.Get()), verb, goSyntax, field)
+ case *reflect.FloatValue:
+ if f.Type().Size() == 4 {
+ p.fmtFloat32(float32(f.Get()), verb, field)
+ } else {
+ p.fmtFloat64(float64(f.Get()), verb, field)
+ }
+ case *reflect.ComplexValue:
+ if f.Type().Size() == 8 {
+ p.fmtComplex64(complex64(f.Get()), verb, field)
+ } else {
+ p.fmtComplex128(complex128(f.Get()), verb, field)
+ }
+ case *reflect.StringValue:
+ p.fmtString(f.Get(), verb, goSyntax, field)
+ case *reflect.MapValue:
+ if goSyntax {
+ p.buf.WriteString(f.Type().String())
+ p.buf.WriteByte('{')
+ } else {
+ p.buf.Write(mapBytes)
+ }
+ keys := f.Keys()
+ for i, key := range keys {
+ if i > 0 {
+ if goSyntax {
+ p.buf.Write(commaSpaceBytes)
+ } else {
+ p.buf.WriteByte(' ')
+ }
+ }
+ p.printField(key.Interface(), verb, plus, goSyntax, depth+1)
+ p.buf.WriteByte(':')
+ p.printField(f.Elem(key).Interface(), verb, plus, goSyntax, depth+1)
+ }
+ if goSyntax {
+ p.buf.WriteByte('}')
+ } else {
+ p.buf.WriteByte(']')
+ }
+ case *reflect.StructValue:
+ if goSyntax {
+ p.buf.WriteString(reflect.Typeof(field).String())
+ }
+ p.add('{')
+ v := f
+ t := v.Type().(*reflect.StructType)
+ for i := 0; i < v.NumField(); i++ {
+ if i > 0 {
+ if goSyntax {
+ p.buf.Write(commaSpaceBytes)
+ } else {
+ p.buf.WriteByte(' ')
+ }
+ }
+ if plus || goSyntax {
+ if f := t.Field(i); f.Name != "" {
+ p.buf.WriteString(f.Name)
+ p.buf.WriteByte(':')
+ }
+ }
+ p.printField(getField(v, i).Interface(), verb, plus, goSyntax, depth+1)
+ }
+ p.buf.WriteByte('}')
+ case *reflect.InterfaceValue:
+ value := f.Elem()
+ if value == nil {
+ if goSyntax {
+ p.buf.WriteString(reflect.Typeof(field).String())
+ p.buf.Write(nilParenBytes)
+ } else {
+ p.buf.Write(nilAngleBytes)
+ }
+ } else {
+ return p.printField(value.Interface(), verb, plus, goSyntax, depth+1)
+ }
+ case reflect.ArrayOrSliceValue:
+ // Byte slices are special.
+ if f.Type().(reflect.ArrayOrSliceType).Elem().Kind() == reflect.Uint8 {
+ // We know it's a slice of bytes, but we also know it does not have static type
+ // []byte, or it would have been caught above. Therefore we cannot convert
+ // it directly in the (slightly) obvious way: f.Interface().([]byte); it doesn't have
+ // that type, and we can't write an expression of the right type and do a
+ // conversion because we don't have a static way to write the right type.
+ // So we build a slice by hand. This is a rare case but it would be nice
+ // if reflection could help a little more.
+ bytes := make([]byte, f.Len())
+ for i := range bytes {
+ bytes[i] = byte(f.Elem(i).(*reflect.UintValue).Get())
+ }
+ p.fmtBytes(bytes, verb, goSyntax, depth, field)
+ return verb == 's'
+ }
+ if goSyntax {
+ p.buf.WriteString(reflect.Typeof(field).String())
+ p.buf.WriteByte('{')
+ } else {
+ p.buf.WriteByte('[')
+ }
+ for i := 0; i < f.Len(); i++ {
+ if i > 0 {
+ if goSyntax {
+ p.buf.Write(commaSpaceBytes)
+ } else {
+ p.buf.WriteByte(' ')
+ }
+ }
+ p.printField(f.Elem(i).Interface(), verb, plus, goSyntax, depth+1)
+ }
+ if goSyntax {
+ p.buf.WriteByte('}')
+ } else {
+ p.buf.WriteByte(']')
+ }
+ case *reflect.PtrValue:
+ v := f.Get()
+ // pointer to array or slice or struct? ok at top level
+ // but not embedded (avoid loops)
+ if v != 0 && depth == 0 {
+ switch a := f.Elem().(type) {
+ case reflect.ArrayOrSliceValue:
+ p.buf.WriteByte('&')
+ p.printField(a.Interface(), verb, plus, goSyntax, depth+1)
+ break BigSwitch
+ case *reflect.StructValue:
+ p.buf.WriteByte('&')
+ p.printField(a.Interface(), verb, plus, goSyntax, depth+1)
+ break BigSwitch
+ }
+ }
+ if goSyntax {
+ p.buf.WriteByte('(')
+ p.buf.WriteString(reflect.Typeof(field).String())
+ p.buf.WriteByte(')')
+ p.buf.WriteByte('(')
+ if v == 0 {
+ p.buf.Write(nilBytes)
+ } else {
+ p.fmt0x64(uint64(v), true)
+ }
+ p.buf.WriteByte(')')
+ break
+ }
+ if v == 0 {
+ p.buf.Write(nilAngleBytes)
+ break
+ }
+ p.fmt0x64(uint64(v), true)
+ case *reflect.ChanValue, *reflect.FuncValue, *reflect.UnsafePointerValue:
+ p.fmtPointer(field, value, verb, goSyntax)
+ default:
+ p.unknownType(f)
+ }
+ return false
+}
+
+// intFromArg gets the fieldnumth element of a. On return, isInt reports whether the argument has type int.
+func intFromArg(a []interface{}, end, i, fieldnum int) (num int, isInt bool, newi, newfieldnum int) {
+ newi, newfieldnum = end, fieldnum
+ if i < end && fieldnum < len(a) {
+ num, isInt = a[fieldnum].(int)
+ newi, newfieldnum = i+1, fieldnum+1
+ }
+ return
+}
+
+func (p *pp) doPrintf(format string, a []interface{}) {
+ end := len(format)
+ fieldnum := 0 // we process one field per non-trivial format
+ for i := 0; i < end; {
+ lasti := i
+ for i < end && format[i] != '%' {
+ i++
+ }
+ if i > lasti {
+ p.buf.WriteString(format[lasti:i])
+ }
+ if i >= end {
+ // done processing format string
+ break
+ }
+
+ // Process one verb
+ i++
+ // flags and widths
+ p.fmt.clearflags()
+ F:
+ for ; i < end; i++ {
+ switch format[i] {
+ case '#':
+ p.fmt.sharp = true
+ case '0':
+ p.fmt.zero = true
+ case '+':
+ p.fmt.plus = true
+ case '-':
+ p.fmt.minus = true
+ case ' ':
+ p.fmt.space = true
+ default:
+ break F
+ }
+ }
+ // do we have width?
+ if i < end && format[i] == '*' {
+ p.fmt.wid, p.fmt.widPresent, i, fieldnum = intFromArg(a, end, i, fieldnum)
+ if !p.fmt.widPresent {
+ p.buf.Write(widthBytes)
+ }
+ } else {
+ p.fmt.wid, p.fmt.widPresent, i = parsenum(format, i, end)
+ }
+ // do we have precision?
+ if i < end && format[i] == '.' {
+ if format[i+1] == '*' {
+ p.fmt.prec, p.fmt.precPresent, i, fieldnum = intFromArg(a, end, i+1, fieldnum)
+ if !p.fmt.precPresent {
+ p.buf.Write(precBytes)
+ }
+ } else {
+ p.fmt.prec, p.fmt.precPresent, i = parsenum(format, i+1, end)
+ }
+ }
+ if i >= end {
+ p.buf.Write(noVerbBytes)
+ continue
+ }
+ c, w := utf8.DecodeRuneInString(format[i:])
+ i += w
+ // percent is special - absorbs no operand
+ if c == '%' {
+ p.buf.WriteByte('%') // We ignore width and prec.
+ continue
+ }
+ if fieldnum >= len(a) { // out of operands
+ p.buf.WriteByte('%')
+ p.add(c)
+ p.buf.Write(missingBytes)
+ continue
+ }
+ field := a[fieldnum]
+ fieldnum++
+
+ goSyntax := c == 'v' && p.fmt.sharp
+ plus := c == 'v' && p.fmt.plus
+ p.printField(field, c, plus, goSyntax, 0)
+ }
+
+ if fieldnum < len(a) {
+ p.buf.Write(extraBytes)
+ for ; fieldnum < len(a); fieldnum++ {
+ field := a[fieldnum]
+ if field != nil {
+ p.buf.WriteString(reflect.Typeof(field).String())
+ p.buf.WriteByte('=')
+ }
+ p.printField(field, 'v', false, false, 0)
+ if fieldnum+1 < len(a) {
+ p.buf.Write(commaSpaceBytes)
+ }
+ }
+ p.buf.WriteByte(')')
+ }
+}
+
+func (p *pp) doPrint(a []interface{}, addspace, addnewline bool) {
+ prevString := false
+ for fieldnum := 0; fieldnum < len(a); fieldnum++ {
+ p.fmt.clearflags()
+ // always add spaces if we're doing println
+ field := a[fieldnum]
+ if fieldnum > 0 {
+ isString := field != nil && reflect.Typeof(field).Kind() == reflect.String
+ if addspace || !isString && !prevString {
+ p.buf.WriteByte(' ')
+ }
+ }
+ prevString = p.printField(field, 'v', false, false, 0)
+ }
+ if addnewline {
+ p.buf.WriteByte('\n')
+ }
+}
diff --git a/src/cmd/gofix/testdata/reflect.print.go.out b/src/cmd/gofix/testdata/reflect.print.go.out
new file mode 100644
index 000000000..e3dc775cf
--- /dev/null
+++ b/src/cmd/gofix/testdata/reflect.print.go.out
@@ -0,0 +1,945 @@
+// 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 fmt
+
+import (
+ "bytes"
+ "io"
+ "os"
+ "reflect"
+ "utf8"
+)
+
+// Some constants in the form of bytes, to avoid string overhead.
+// Needlessly fastidious, I suppose.
+var (
+ commaSpaceBytes = []byte(", ")
+ nilAngleBytes = []byte("<nil>")
+ nilParenBytes = []byte("(nil)")
+ nilBytes = []byte("nil")
+ mapBytes = []byte("map[")
+ missingBytes = []byte("(MISSING)")
+ extraBytes = []byte("%!(EXTRA ")
+ irparenBytes = []byte("i)")
+ bytesBytes = []byte("[]byte{")
+ widthBytes = []byte("%!(BADWIDTH)")
+ precBytes = []byte("%!(BADPREC)")
+ noVerbBytes = []byte("%!(NOVERB)")
+)
+
+// State represents the printer state passed to custom formatters.
+// It provides access to the io.Writer interface plus information about
+// the flags and options for the operand's format specifier.
+type State interface {
+ // Write is the function to call to emit formatted output to be printed.
+ Write(b []byte) (ret int, err os.Error)
+ // Width returns the value of the width option and whether it has been set.
+ Width() (wid int, ok bool)
+ // Precision returns the value of the precision option and whether it has been set.
+ Precision() (prec int, ok bool)
+
+ // Flag returns whether the flag c, a character, has been set.
+ Flag(int) bool
+}
+
+// Formatter is the interface implemented by values with a custom formatter.
+// The implementation of Format may call Sprintf or Fprintf(f) etc.
+// to generate its output.
+type Formatter interface {
+ Format(f State, c int)
+}
+
+// Stringer is implemented by any value that has a String method(),
+// which defines the ``native'' format for that value.
+// The String method is used to print values passed as an operand
+// to a %s or %v format or to an unformatted printer such as Print.
+type Stringer interface {
+ String() string
+}
+
+// GoStringer is implemented by any value that has a GoString() method,
+// which defines the Go syntax for that value.
+// The GoString method is used to print values passed as an operand
+// to a %#v format.
+type GoStringer interface {
+ GoString() string
+}
+
+type pp struct {
+ n int
+ buf bytes.Buffer
+ runeBuf [utf8.UTFMax]byte
+ fmt fmt
+}
+
+// A cache holds a set of reusable objects.
+// The buffered channel holds the currently available objects.
+// If more are needed, the cache creates them by calling new.
+type cache struct {
+ saved chan interface{}
+ new func() interface{}
+}
+
+func (c *cache) put(x interface{}) {
+ select {
+ case c.saved <- x:
+ // saved in cache
+ default:
+ // discard
+ }
+}
+
+func (c *cache) get() interface{} {
+ select {
+ case x := <-c.saved:
+ return x // reused from cache
+ default:
+ return c.new()
+ }
+ panic("not reached")
+}
+
+func newCache(f func() interface{}) *cache {
+ return &cache{make(chan interface{}, 100), f}
+}
+
+var ppFree = newCache(func() interface{} { return new(pp) })
+
+// Allocate a new pp struct or grab a cached one.
+func newPrinter() *pp {
+ p := ppFree.get().(*pp)
+ p.fmt.init(&p.buf)
+ return p
+}
+
+// Save used pp structs in ppFree; avoids an allocation per invocation.
+func (p *pp) free() {
+ // Don't hold on to pp structs with large buffers.
+ if cap(p.buf.Bytes()) > 1024 {
+ return
+ }
+ p.buf.Reset()
+ ppFree.put(p)
+}
+
+func (p *pp) Width() (wid int, ok bool) { return p.fmt.wid, p.fmt.widPresent }
+
+func (p *pp) Precision() (prec int, ok bool) { return p.fmt.prec, p.fmt.precPresent }
+
+func (p *pp) Flag(b int) bool {
+ switch b {
+ case '-':
+ return p.fmt.minus
+ case '+':
+ return p.fmt.plus
+ case '#':
+ return p.fmt.sharp
+ case ' ':
+ return p.fmt.space
+ case '0':
+ return p.fmt.zero
+ }
+ return false
+}
+
+func (p *pp) add(c int) {
+ p.buf.WriteRune(c)
+}
+
+// Implement Write so we can call Fprintf on a pp (through State), for
+// recursive use in custom verbs.
+func (p *pp) Write(b []byte) (ret int, err os.Error) {
+ return p.buf.Write(b)
+}
+
+// These routines end in 'f' and take a format string.
+
+// Fprintf formats according to a format specifier and writes to w.
+// It returns the number of bytes written and any write error encountered.
+func Fprintf(w io.Writer, format string, a ...interface{}) (n int, error os.Error) {
+ p := newPrinter()
+ p.doPrintf(format, a)
+ n64, error := p.buf.WriteTo(w)
+ p.free()
+ return int(n64), error
+}
+
+// Printf formats according to a format specifier and writes to standard output.
+// It returns the number of bytes written and any write error encountered.
+func Printf(format string, a ...interface{}) (n int, errno os.Error) {
+ n, errno = Fprintf(os.Stdout, format, a...)
+ return n, errno
+}
+
+// Sprintf formats according to a format specifier and returns the resulting string.
+func Sprintf(format string, a ...interface{}) string {
+ p := newPrinter()
+ p.doPrintf(format, a)
+ s := p.buf.String()
+ p.free()
+ return s
+}
+
+// Errorf formats according to a format specifier and returns the string
+// converted to an os.ErrorString, which satisfies the os.Error interface.
+func Errorf(format string, a ...interface{}) os.Error {
+ return os.ErrorString(Sprintf(format, a...))
+}
+
+// These routines do not take a format string
+
+// Fprint formats using the default formats for its operands and writes to w.
+// Spaces are added between operands when neither is a string.
+// It returns the number of bytes written and any write error encountered.
+func Fprint(w io.Writer, a ...interface{}) (n int, error os.Error) {
+ p := newPrinter()
+ p.doPrint(a, false, false)
+ n64, error := p.buf.WriteTo(w)
+ p.free()
+ return int(n64), error
+}
+
+// Print formats using the default formats for its operands and writes to standard output.
+// Spaces are added between operands when neither is a string.
+// It returns the number of bytes written and any write error encountered.
+func Print(a ...interface{}) (n int, errno os.Error) {
+ n, errno = Fprint(os.Stdout, a...)
+ return n, errno
+}
+
+// Sprint formats using the default formats for its operands and returns the resulting string.
+// Spaces are added between operands when neither is a string.
+func Sprint(a ...interface{}) string {
+ p := newPrinter()
+ p.doPrint(a, false, false)
+ s := p.buf.String()
+ p.free()
+ return s
+}
+
+// These routines end in 'ln', do not take a format string,
+// always add spaces between operands, and add a newline
+// after the last operand.
+
+// Fprintln formats using the default formats for its operands and writes to w.
+// Spaces are always added between operands and a newline is appended.
+// It returns the number of bytes written and any write error encountered.
+func Fprintln(w io.Writer, a ...interface{}) (n int, error os.Error) {
+ p := newPrinter()
+ p.doPrint(a, true, true)
+ n64, error := p.buf.WriteTo(w)
+ p.free()
+ return int(n64), error
+}
+
+// Println formats using the default formats for its operands and writes to standard output.
+// Spaces are always added between operands and a newline is appended.
+// It returns the number of bytes written and any write error encountered.
+func Println(a ...interface{}) (n int, errno os.Error) {
+ n, errno = Fprintln(os.Stdout, a...)
+ return n, errno
+}
+
+// Sprintln formats using the default formats for its operands and returns the resulting string.
+// Spaces are always added between operands and a newline is appended.
+func Sprintln(a ...interface{}) string {
+ p := newPrinter()
+ p.doPrint(a, true, true)
+ s := p.buf.String()
+ p.free()
+ return s
+}
+
+
+// Get the i'th arg of the struct value.
+// If the arg itself is an interface, return a value for
+// the thing inside the interface, not the interface itself.
+func getField(v reflect.Value, i int) reflect.Value {
+ val := v.Field(i)
+ if i := val; i.Kind() == reflect.Interface {
+ if inter := i.Interface(); inter != nil {
+ return reflect.NewValue(inter)
+ }
+ }
+ return val
+}
+
+// Convert ASCII to integer. n is 0 (and got is false) if no number present.
+func parsenum(s string, start, end int) (num int, isnum bool, newi int) {
+ if start >= end {
+ return 0, false, end
+ }
+ for newi = start; newi < end && '0' <= s[newi] && s[newi] <= '9'; newi++ {
+ num = num*10 + int(s[newi]-'0')
+ isnum = true
+ }
+ return
+}
+
+func (p *pp) unknownType(v interface{}) {
+ if v == nil {
+ p.buf.Write(nilAngleBytes)
+ return
+ }
+ p.buf.WriteByte('?')
+ p.buf.WriteString(reflect.Typeof(v).String())
+ p.buf.WriteByte('?')
+}
+
+func (p *pp) badVerb(verb int, val interface{}) {
+ p.add('%')
+ p.add('!')
+ p.add(verb)
+ p.add('(')
+ if val == nil {
+ p.buf.Write(nilAngleBytes)
+ } else {
+ p.buf.WriteString(reflect.Typeof(val).String())
+ p.add('=')
+ p.printField(val, 'v', false, false, 0)
+ }
+ p.add(')')
+}
+
+func (p *pp) fmtBool(v bool, verb int, value interface{}) {
+ switch verb {
+ case 't', 'v':
+ p.fmt.fmt_boolean(v)
+ default:
+ p.badVerb(verb, value)
+ }
+}
+
+// fmtC formats a rune for the 'c' format.
+func (p *pp) fmtC(c int64) {
+ rune := int(c) // Check for overflow.
+ if int64(rune) != c {
+ rune = utf8.RuneError
+ }
+ w := utf8.EncodeRune(p.runeBuf[0:utf8.UTFMax], rune)
+ p.fmt.pad(p.runeBuf[0:w])
+}
+
+func (p *pp) fmtInt64(v int64, verb int, value interface{}) {
+ switch verb {
+ case 'b':
+ p.fmt.integer(v, 2, signed, ldigits)
+ case 'c':
+ p.fmtC(v)
+ case 'd', 'v':
+ p.fmt.integer(v, 10, signed, ldigits)
+ case 'o':
+ p.fmt.integer(v, 8, signed, ldigits)
+ case 'x':
+ p.fmt.integer(v, 16, signed, ldigits)
+ case 'U':
+ p.fmtUnicode(v)
+ case 'X':
+ p.fmt.integer(v, 16, signed, udigits)
+ default:
+ p.badVerb(verb, value)
+ }
+}
+
+// fmt0x64 formats a uint64 in hexadecimal and prefixes it with 0x or
+// not, as requested, by temporarily setting the sharp flag.
+func (p *pp) fmt0x64(v uint64, leading0x bool) {
+ sharp := p.fmt.sharp
+ p.fmt.sharp = leading0x
+ p.fmt.integer(int64(v), 16, unsigned, ldigits)
+ p.fmt.sharp = sharp
+}
+
+// fmtUnicode formats a uint64 in U+1234 form by
+// temporarily turning on the unicode flag and tweaking the precision.
+func (p *pp) fmtUnicode(v int64) {
+ precPresent := p.fmt.precPresent
+ prec := p.fmt.prec
+ if !precPresent {
+ // If prec is already set, leave it alone; otherwise 4 is minimum.
+ p.fmt.prec = 4
+ p.fmt.precPresent = true
+ }
+ p.fmt.unicode = true // turn on U+
+ p.fmt.integer(int64(v), 16, unsigned, udigits)
+ p.fmt.unicode = false
+ p.fmt.prec = prec
+ p.fmt.precPresent = precPresent
+}
+
+func (p *pp) fmtUint64(v uint64, verb int, goSyntax bool, value interface{}) {
+ switch verb {
+ case 'b':
+ p.fmt.integer(int64(v), 2, unsigned, ldigits)
+ case 'c':
+ p.fmtC(int64(v))
+ case 'd':
+ p.fmt.integer(int64(v), 10, unsigned, ldigits)
+ case 'v':
+ if goSyntax {
+ p.fmt0x64(v, true)
+ } else {
+ p.fmt.integer(int64(v), 10, unsigned, ldigits)
+ }
+ case 'o':
+ p.fmt.integer(int64(v), 8, unsigned, ldigits)
+ case 'x':
+ p.fmt.integer(int64(v), 16, unsigned, ldigits)
+ case 'X':
+ p.fmt.integer(int64(v), 16, unsigned, udigits)
+ default:
+ p.badVerb(verb, value)
+ }
+}
+
+func (p *pp) fmtFloat32(v float32, verb int, value interface{}) {
+ switch verb {
+ case 'b':
+ p.fmt.fmt_fb32(v)
+ case 'e':
+ p.fmt.fmt_e32(v)
+ case 'E':
+ p.fmt.fmt_E32(v)
+ case 'f':
+ p.fmt.fmt_f32(v)
+ case 'g', 'v':
+ p.fmt.fmt_g32(v)
+ case 'G':
+ p.fmt.fmt_G32(v)
+ default:
+ p.badVerb(verb, value)
+ }
+}
+
+func (p *pp) fmtFloat64(v float64, verb int, value interface{}) {
+ switch verb {
+ case 'b':
+ p.fmt.fmt_fb64(v)
+ case 'e':
+ p.fmt.fmt_e64(v)
+ case 'E':
+ p.fmt.fmt_E64(v)
+ case 'f':
+ p.fmt.fmt_f64(v)
+ case 'g', 'v':
+ p.fmt.fmt_g64(v)
+ case 'G':
+ p.fmt.fmt_G64(v)
+ default:
+ p.badVerb(verb, value)
+ }
+}
+
+func (p *pp) fmtComplex64(v complex64, verb int, value interface{}) {
+ switch verb {
+ case 'e', 'E', 'f', 'F', 'g', 'G':
+ p.fmt.fmt_c64(v, verb)
+ case 'v':
+ p.fmt.fmt_c64(v, 'g')
+ default:
+ p.badVerb(verb, value)
+ }
+}
+
+func (p *pp) fmtComplex128(v complex128, verb int, value interface{}) {
+ switch verb {
+ case 'e', 'E', 'f', 'F', 'g', 'G':
+ p.fmt.fmt_c128(v, verb)
+ case 'v':
+ p.fmt.fmt_c128(v, 'g')
+ default:
+ p.badVerb(verb, value)
+ }
+}
+
+func (p *pp) fmtString(v string, verb int, goSyntax bool, value interface{}) {
+ switch verb {
+ case 'v':
+ if goSyntax {
+ p.fmt.fmt_q(v)
+ } else {
+ p.fmt.fmt_s(v)
+ }
+ case 's':
+ p.fmt.fmt_s(v)
+ case 'x':
+ p.fmt.fmt_sx(v)
+ case 'X':
+ p.fmt.fmt_sX(v)
+ case 'q':
+ p.fmt.fmt_q(v)
+ default:
+ p.badVerb(verb, value)
+ }
+}
+
+func (p *pp) fmtBytes(v []byte, verb int, goSyntax bool, depth int, value interface{}) {
+ if verb == 'v' || verb == 'd' {
+ if goSyntax {
+ p.buf.Write(bytesBytes)
+ } else {
+ p.buf.WriteByte('[')
+ }
+ for i, c := range v {
+ if i > 0 {
+ if goSyntax {
+ p.buf.Write(commaSpaceBytes)
+ } else {
+ p.buf.WriteByte(' ')
+ }
+ }
+ p.printField(c, 'v', p.fmt.plus, goSyntax, depth+1)
+ }
+ if goSyntax {
+ p.buf.WriteByte('}')
+ } else {
+ p.buf.WriteByte(']')
+ }
+ return
+ }
+ s := string(v)
+ switch verb {
+ case 's':
+ p.fmt.fmt_s(s)
+ case 'x':
+ p.fmt.fmt_sx(s)
+ case 'X':
+ p.fmt.fmt_sX(s)
+ case 'q':
+ p.fmt.fmt_q(s)
+ default:
+ p.badVerb(verb, value)
+ }
+}
+
+func (p *pp) fmtPointer(field interface{}, value reflect.Value, verb int, goSyntax bool) {
+ var u uintptr
+ switch value.Kind() {
+ case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Slice, reflect.UnsafePointer:
+ u = value.Pointer()
+ default:
+ p.badVerb(verb, field)
+ return
+ }
+ if goSyntax {
+ p.add('(')
+ p.buf.WriteString(reflect.Typeof(field).String())
+ p.add(')')
+ p.add('(')
+ if u == 0 {
+ p.buf.Write(nilBytes)
+ } else {
+ p.fmt0x64(uint64(u), true)
+ }
+ p.add(')')
+ } else {
+ p.fmt0x64(uint64(u), !p.fmt.sharp)
+ }
+}
+
+var (
+ intBits = reflect.Typeof(0).Bits()
+ floatBits = reflect.Typeof(0.0).Bits()
+ complexBits = reflect.Typeof(1i).Bits()
+ uintptrBits = reflect.Typeof(uintptr(0)).Bits()
+)
+
+func (p *pp) printField(field interface{}, verb int, plus, goSyntax bool, depth int) (wasString bool) {
+ if field == nil {
+ if verb == 'T' || verb == 'v' {
+ p.buf.Write(nilAngleBytes)
+ } else {
+ p.badVerb(verb, field)
+ }
+ return false
+ }
+
+ // Special processing considerations.
+ // %T (the value's type) and %p (its address) are special; we always do them first.
+ switch verb {
+ case 'T':
+ p.printField(reflect.Typeof(field).String(), 's', false, false, 0)
+ return false
+ case 'p':
+ p.fmtPointer(field, reflect.NewValue(field), verb, goSyntax)
+ return false
+ }
+ // Is it a Formatter?
+ if formatter, ok := field.(Formatter); ok {
+ formatter.Format(p, verb)
+ return false // this value is not a string
+
+ }
+ // Must not touch flags before Formatter looks at them.
+ if plus {
+ p.fmt.plus = false
+ }
+ // If we're doing Go syntax and the field knows how to supply it, take care of it now.
+ if goSyntax {
+ p.fmt.sharp = false
+ if stringer, ok := field.(GoStringer); ok {
+ // Print the result of GoString unadorned.
+ p.fmtString(stringer.GoString(), 's', false, field)
+ return false // this value is not a string
+ }
+ } else {
+ // Is it a Stringer?
+ if stringer, ok := field.(Stringer); ok {
+ p.printField(stringer.String(), verb, plus, false, depth)
+ return false // this value is not a string
+ }
+ }
+
+ // Some types can be done without reflection.
+ switch f := field.(type) {
+ case bool:
+ p.fmtBool(f, verb, field)
+ return false
+ case float32:
+ p.fmtFloat32(f, verb, field)
+ return false
+ case float64:
+ p.fmtFloat64(f, verb, field)
+ return false
+ case complex64:
+ p.fmtComplex64(complex64(f), verb, field)
+ return false
+ case complex128:
+ p.fmtComplex128(f, verb, field)
+ return false
+ case int:
+ p.fmtInt64(int64(f), verb, field)
+ return false
+ case int8:
+ p.fmtInt64(int64(f), verb, field)
+ return false
+ case int16:
+ p.fmtInt64(int64(f), verb, field)
+ return false
+ case int32:
+ p.fmtInt64(int64(f), verb, field)
+ return false
+ case int64:
+ p.fmtInt64(f, verb, field)
+ return false
+ case uint:
+ p.fmtUint64(uint64(f), verb, goSyntax, field)
+ return false
+ case uint8:
+ p.fmtUint64(uint64(f), verb, goSyntax, field)
+ return false
+ case uint16:
+ p.fmtUint64(uint64(f), verb, goSyntax, field)
+ return false
+ case uint32:
+ p.fmtUint64(uint64(f), verb, goSyntax, field)
+ return false
+ case uint64:
+ p.fmtUint64(f, verb, goSyntax, field)
+ return false
+ case uintptr:
+ p.fmtUint64(uint64(f), verb, goSyntax, field)
+ return false
+ case string:
+ p.fmtString(f, verb, goSyntax, field)
+ return verb == 's' || verb == 'v'
+ case []byte:
+ p.fmtBytes(f, verb, goSyntax, depth, field)
+ return verb == 's'
+ }
+
+ // Need to use reflection
+ value := reflect.NewValue(field)
+
+BigSwitch:
+ switch f := value; f.Kind() {
+ case reflect.Bool:
+ p.fmtBool(f.Bool(), verb, field)
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ p.fmtInt64(f.Int(), verb, field)
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ p.fmtUint64(uint64(f.Uint()), verb, goSyntax, field)
+ case reflect.Float32, reflect.Float64:
+ if f.Type().Size() == 4 {
+ p.fmtFloat32(float32(f.Float()), verb, field)
+ } else {
+ p.fmtFloat64(float64(f.Float()), verb, field)
+ }
+ case reflect.Complex64, reflect.Complex128:
+ if f.Type().Size() == 8 {
+ p.fmtComplex64(complex64(f.Complex()), verb, field)
+ } else {
+ p.fmtComplex128(complex128(f.Complex()), verb, field)
+ }
+ case reflect.String:
+ p.fmtString(f.String(), verb, goSyntax, field)
+ case reflect.Map:
+ if goSyntax {
+ p.buf.WriteString(f.Type().String())
+ p.buf.WriteByte('{')
+ } else {
+ p.buf.Write(mapBytes)
+ }
+ keys := f.MapKeys()
+ for i, key := range keys {
+ if i > 0 {
+ if goSyntax {
+ p.buf.Write(commaSpaceBytes)
+ } else {
+ p.buf.WriteByte(' ')
+ }
+ }
+ p.printField(key.Interface(), verb, plus, goSyntax, depth+1)
+ p.buf.WriteByte(':')
+ p.printField(f.MapIndex(key).Interface(), verb, plus, goSyntax, depth+1)
+ }
+ if goSyntax {
+ p.buf.WriteByte('}')
+ } else {
+ p.buf.WriteByte(']')
+ }
+ case reflect.Struct:
+ if goSyntax {
+ p.buf.WriteString(reflect.Typeof(field).String())
+ }
+ p.add('{')
+ v := f
+ t := v.Type()
+ for i := 0; i < v.NumField(); i++ {
+ if i > 0 {
+ if goSyntax {
+ p.buf.Write(commaSpaceBytes)
+ } else {
+ p.buf.WriteByte(' ')
+ }
+ }
+ if plus || goSyntax {
+ if f := t.Field(i); f.Name != "" {
+ p.buf.WriteString(f.Name)
+ p.buf.WriteByte(':')
+ }
+ }
+ p.printField(getField(v, i).Interface(), verb, plus, goSyntax, depth+1)
+ }
+ p.buf.WriteByte('}')
+ case reflect.Interface:
+ value := f.Elem()
+ if !value.IsValid() {
+ if goSyntax {
+ p.buf.WriteString(reflect.Typeof(field).String())
+ p.buf.Write(nilParenBytes)
+ } else {
+ p.buf.Write(nilAngleBytes)
+ }
+ } else {
+ return p.printField(value.Interface(), verb, plus, goSyntax, depth+1)
+ }
+ case reflect.Array, reflect.Slice:
+ // Byte slices are special.
+ if f.Type().Elem().Kind() == reflect.Uint8 {
+ // We know it's a slice of bytes, but we also know it does not have static type
+ // []byte, or it would have been caught above. Therefore we cannot convert
+ // it directly in the (slightly) obvious way: f.Interface().([]byte); it doesn't have
+ // that type, and we can't write an expression of the right type and do a
+ // conversion because we don't have a static way to write the right type.
+ // So we build a slice by hand. This is a rare case but it would be nice
+ // if reflection could help a little more.
+ bytes := make([]byte, f.Len())
+ for i := range bytes {
+ bytes[i] = byte(f.Index(i).Uint())
+ }
+ p.fmtBytes(bytes, verb, goSyntax, depth, field)
+ return verb == 's'
+ }
+ if goSyntax {
+ p.buf.WriteString(reflect.Typeof(field).String())
+ p.buf.WriteByte('{')
+ } else {
+ p.buf.WriteByte('[')
+ }
+ for i := 0; i < f.Len(); i++ {
+ if i > 0 {
+ if goSyntax {
+ p.buf.Write(commaSpaceBytes)
+ } else {
+ p.buf.WriteByte(' ')
+ }
+ }
+ p.printField(f.Index(i).Interface(), verb, plus, goSyntax, depth+1)
+ }
+ if goSyntax {
+ p.buf.WriteByte('}')
+ } else {
+ p.buf.WriteByte(']')
+ }
+ case reflect.Ptr:
+ v := f.Pointer()
+ // pointer to array or slice or struct? ok at top level
+ // but not embedded (avoid loops)
+ if v != 0 && depth == 0 {
+ switch a := f.Elem(); a.Kind() {
+ case reflect.Array, reflect.Slice:
+ p.buf.WriteByte('&')
+ p.printField(a.Interface(), verb, plus, goSyntax, depth+1)
+ break BigSwitch
+ case reflect.Struct:
+ p.buf.WriteByte('&')
+ p.printField(a.Interface(), verb, plus, goSyntax, depth+1)
+ break BigSwitch
+ }
+ }
+ if goSyntax {
+ p.buf.WriteByte('(')
+ p.buf.WriteString(reflect.Typeof(field).String())
+ p.buf.WriteByte(')')
+ p.buf.WriteByte('(')
+ if v == 0 {
+ p.buf.Write(nilBytes)
+ } else {
+ p.fmt0x64(uint64(v), true)
+ }
+ p.buf.WriteByte(')')
+ break
+ }
+ if v == 0 {
+ p.buf.Write(nilAngleBytes)
+ break
+ }
+ p.fmt0x64(uint64(v), true)
+ case reflect.Chan, reflect.Func, reflect.UnsafePointer:
+ p.fmtPointer(field, value, verb, goSyntax)
+ default:
+ p.unknownType(f)
+ }
+ return false
+}
+
+// intFromArg gets the fieldnumth element of a. On return, isInt reports whether the argument has type int.
+func intFromArg(a []interface{}, end, i, fieldnum int) (num int, isInt bool, newi, newfieldnum int) {
+ newi, newfieldnum = end, fieldnum
+ if i < end && fieldnum < len(a) {
+ num, isInt = a[fieldnum].(int)
+ newi, newfieldnum = i+1, fieldnum+1
+ }
+ return
+}
+
+func (p *pp) doPrintf(format string, a []interface{}) {
+ end := len(format)
+ fieldnum := 0 // we process one field per non-trivial format
+ for i := 0; i < end; {
+ lasti := i
+ for i < end && format[i] != '%' {
+ i++
+ }
+ if i > lasti {
+ p.buf.WriteString(format[lasti:i])
+ }
+ if i >= end {
+ // done processing format string
+ break
+ }
+
+ // Process one verb
+ i++
+ // flags and widths
+ p.fmt.clearflags()
+ F:
+ for ; i < end; i++ {
+ switch format[i] {
+ case '#':
+ p.fmt.sharp = true
+ case '0':
+ p.fmt.zero = true
+ case '+':
+ p.fmt.plus = true
+ case '-':
+ p.fmt.minus = true
+ case ' ':
+ p.fmt.space = true
+ default:
+ break F
+ }
+ }
+ // do we have width?
+ if i < end && format[i] == '*' {
+ p.fmt.wid, p.fmt.widPresent, i, fieldnum = intFromArg(a, end, i, fieldnum)
+ if !p.fmt.widPresent {
+ p.buf.Write(widthBytes)
+ }
+ } else {
+ p.fmt.wid, p.fmt.widPresent, i = parsenum(format, i, end)
+ }
+ // do we have precision?
+ if i < end && format[i] == '.' {
+ if format[i+1] == '*' {
+ p.fmt.prec, p.fmt.precPresent, i, fieldnum = intFromArg(a, end, i+1, fieldnum)
+ if !p.fmt.precPresent {
+ p.buf.Write(precBytes)
+ }
+ } else {
+ p.fmt.prec, p.fmt.precPresent, i = parsenum(format, i+1, end)
+ }
+ }
+ if i >= end {
+ p.buf.Write(noVerbBytes)
+ continue
+ }
+ c, w := utf8.DecodeRuneInString(format[i:])
+ i += w
+ // percent is special - absorbs no operand
+ if c == '%' {
+ p.buf.WriteByte('%') // We ignore width and prec.
+ continue
+ }
+ if fieldnum >= len(a) { // out of operands
+ p.buf.WriteByte('%')
+ p.add(c)
+ p.buf.Write(missingBytes)
+ continue
+ }
+ field := a[fieldnum]
+ fieldnum++
+
+ goSyntax := c == 'v' && p.fmt.sharp
+ plus := c == 'v' && p.fmt.plus
+ p.printField(field, c, plus, goSyntax, 0)
+ }
+
+ if fieldnum < len(a) {
+ p.buf.Write(extraBytes)
+ for ; fieldnum < len(a); fieldnum++ {
+ field := a[fieldnum]
+ if field != nil {
+ p.buf.WriteString(reflect.Typeof(field).String())
+ p.buf.WriteByte('=')
+ }
+ p.printField(field, 'v', false, false, 0)
+ if fieldnum+1 < len(a) {
+ p.buf.Write(commaSpaceBytes)
+ }
+ }
+ p.buf.WriteByte(')')
+ }
+}
+
+func (p *pp) doPrint(a []interface{}, addspace, addnewline bool) {
+ prevString := false
+ for fieldnum := 0; fieldnum < len(a); fieldnum++ {
+ p.fmt.clearflags()
+ // always add spaces if we're doing println
+ field := a[fieldnum]
+ if fieldnum > 0 {
+ isString := field != nil && reflect.Typeof(field).Kind() == reflect.String
+ if addspace || !isString && !prevString {
+ p.buf.WriteByte(' ')
+ }
+ }
+ prevString = p.printField(field, 'v', false, false, 0)
+ }
+ if addnewline {
+ p.buf.WriteByte('\n')
+ }
+}
diff --git a/src/cmd/gofix/testdata/reflect.quick.go.in b/src/cmd/gofix/testdata/reflect.quick.go.in
new file mode 100644
index 000000000..a5568b048
--- /dev/null
+++ b/src/cmd/gofix/testdata/reflect.quick.go.in
@@ -0,0 +1,364 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This package implements utility functions to help with black box testing.
+package quick
+
+import (
+ "flag"
+ "fmt"
+ "math"
+ "os"
+ "rand"
+ "reflect"
+ "strings"
+)
+
+var defaultMaxCount *int = flag.Int("quickchecks", 100, "The default number of iterations for each check")
+
+// A Generator can generate random values of its own type.
+type Generator interface {
+ // Generate returns a random instance of the type on which it is a
+ // method using the size as a size hint.
+ Generate(rand *rand.Rand, size int) reflect.Value
+}
+
+// randFloat32 generates a random float taking the full range of a float32.
+func randFloat32(rand *rand.Rand) float32 {
+ f := rand.Float64() * math.MaxFloat32
+ if rand.Int()&1 == 1 {
+ f = -f
+ }
+ return float32(f)
+}
+
+// randFloat64 generates a random float taking the full range of a float64.
+func randFloat64(rand *rand.Rand) float64 {
+ f := rand.Float64()
+ if rand.Int()&1 == 1 {
+ f = -f
+ }
+ return f
+}
+
+// randInt64 returns a random integer taking half the range of an int64.
+func randInt64(rand *rand.Rand) int64 { return rand.Int63() - 1<<62 }
+
+// complexSize is the maximum length of arbitrary values that contain other
+// values.
+const complexSize = 50
+
+// Value returns an arbitrary value of the given type.
+// If the type implements the Generator interface, that will be used.
+// Note: in order to create arbitrary values for structs, all the members must be public.
+func Value(t reflect.Type, rand *rand.Rand) (value reflect.Value, ok bool) {
+ if m, ok := reflect.MakeZero(t).Interface().(Generator); ok {
+ return m.Generate(rand, complexSize), true
+ }
+
+ switch concrete := t.(type) {
+ case *reflect.BoolType:
+ return reflect.NewValue(rand.Int()&1 == 0), true
+ case *reflect.FloatType, *reflect.IntType, *reflect.UintType, *reflect.ComplexType:
+ switch t.Kind() {
+ case reflect.Float32:
+ return reflect.NewValue(randFloat32(rand)), true
+ case reflect.Float64:
+ return reflect.NewValue(randFloat64(rand)), true
+ case reflect.Complex64:
+ return reflect.NewValue(complex(randFloat32(rand), randFloat32(rand))), true
+ case reflect.Complex128:
+ return reflect.NewValue(complex(randFloat64(rand), randFloat64(rand))), true
+ case reflect.Int16:
+ return reflect.NewValue(int16(randInt64(rand))), true
+ case reflect.Int32:
+ return reflect.NewValue(int32(randInt64(rand))), true
+ case reflect.Int64:
+ return reflect.NewValue(randInt64(rand)), true
+ case reflect.Int8:
+ return reflect.NewValue(int8(randInt64(rand))), true
+ case reflect.Int:
+ return reflect.NewValue(int(randInt64(rand))), true
+ case reflect.Uint16:
+ return reflect.NewValue(uint16(randInt64(rand))), true
+ case reflect.Uint32:
+ return reflect.NewValue(uint32(randInt64(rand))), true
+ case reflect.Uint64:
+ return reflect.NewValue(uint64(randInt64(rand))), true
+ case reflect.Uint8:
+ return reflect.NewValue(uint8(randInt64(rand))), true
+ case reflect.Uint:
+ return reflect.NewValue(uint(randInt64(rand))), true
+ case reflect.Uintptr:
+ return reflect.NewValue(uintptr(randInt64(rand))), true
+ }
+ case *reflect.MapType:
+ numElems := rand.Intn(complexSize)
+ m := reflect.MakeMap(concrete)
+ for i := 0; i < numElems; i++ {
+ key, ok1 := Value(concrete.Key(), rand)
+ value, ok2 := Value(concrete.Elem(), rand)
+ if !ok1 || !ok2 {
+ return nil, false
+ }
+ m.SetElem(key, value)
+ }
+ return m, true
+ case *reflect.PtrType:
+ v, ok := Value(concrete.Elem(), rand)
+ if !ok {
+ return nil, false
+ }
+ p := reflect.MakeZero(concrete)
+ p.(*reflect.PtrValue).PointTo(v)
+ return p, true
+ case *reflect.SliceType:
+ numElems := rand.Intn(complexSize)
+ s := reflect.MakeSlice(concrete, numElems, numElems)
+ for i := 0; i < numElems; i++ {
+ v, ok := Value(concrete.Elem(), rand)
+ if !ok {
+ return nil, false
+ }
+ s.Elem(i).SetValue(v)
+ }
+ return s, true
+ case *reflect.StringType:
+ numChars := rand.Intn(complexSize)
+ codePoints := make([]int, numChars)
+ for i := 0; i < numChars; i++ {
+ codePoints[i] = rand.Intn(0x10ffff)
+ }
+ return reflect.NewValue(string(codePoints)), true
+ case *reflect.StructType:
+ s := reflect.MakeZero(t).(*reflect.StructValue)
+ for i := 0; i < s.NumField(); i++ {
+ v, ok := Value(concrete.Field(i).Type, rand)
+ if !ok {
+ return nil, false
+ }
+ s.Field(i).SetValue(v)
+ }
+ return s, true
+ default:
+ return nil, false
+ }
+
+ return
+}
+
+// A Config structure contains options for running a test.
+type Config struct {
+ // MaxCount sets the maximum number of iterations. If zero,
+ // MaxCountScale is used.
+ MaxCount int
+ // MaxCountScale is a non-negative scale factor applied to the default
+ // maximum. If zero, the default is unchanged.
+ MaxCountScale float64
+ // If non-nil, rand is a source of random numbers. Otherwise a default
+ // pseudo-random source will be used.
+ Rand *rand.Rand
+ // If non-nil, Values is a function which generates a slice of arbitrary
+ // Values that are congruent with the arguments to the function being
+ // tested. Otherwise, Values is used to generate the values.
+ Values func([]reflect.Value, *rand.Rand)
+}
+
+var defaultConfig Config
+
+// getRand returns the *rand.Rand to use for a given Config.
+func (c *Config) getRand() *rand.Rand {
+ if c.Rand == nil {
+ return rand.New(rand.NewSource(0))
+ }
+ return c.Rand
+}
+
+// getMaxCount returns the maximum number of iterations to run for a given
+// Config.
+func (c *Config) getMaxCount() (maxCount int) {
+ maxCount = c.MaxCount
+ if maxCount == 0 {
+ if c.MaxCountScale != 0 {
+ maxCount = int(c.MaxCountScale * float64(*defaultMaxCount))
+ } else {
+ maxCount = *defaultMaxCount
+ }
+ }
+
+ return
+}
+
+// A SetupError is the result of an error in the way that check is being
+// used, independent of the functions being tested.
+type SetupError string
+
+func (s SetupError) String() string { return string(s) }
+
+// A CheckError is the result of Check finding an error.
+type CheckError struct {
+ Count int
+ In []interface{}
+}
+
+func (s *CheckError) String() string {
+ return fmt.Sprintf("#%d: failed on input %s", s.Count, toString(s.In))
+}
+
+// A CheckEqualError is the result CheckEqual finding an error.
+type CheckEqualError struct {
+ CheckError
+ Out1 []interface{}
+ Out2 []interface{}
+}
+
+func (s *CheckEqualError) String() string {
+ return fmt.Sprintf("#%d: failed on input %s. Output 1: %s. Output 2: %s", s.Count, toString(s.In), toString(s.Out1), toString(s.Out2))
+}
+
+// Check looks for an input to f, any function that returns bool,
+// such that f returns false. It calls f repeatedly, with arbitrary
+// values for each argument. If f returns false on a given input,
+// Check returns that input as a *CheckError.
+// For example:
+//
+// func TestOddMultipleOfThree(t *testing.T) {
+// f := func(x int) bool {
+// y := OddMultipleOfThree(x)
+// return y%2 == 1 && y%3 == 0
+// }
+// if err := quick.Check(f, nil); err != nil {
+// t.Error(err)
+// }
+// }
+func Check(function interface{}, config *Config) (err os.Error) {
+ if config == nil {
+ config = &defaultConfig
+ }
+
+ f, fType, ok := functionAndType(function)
+ if !ok {
+ err = SetupError("argument is not a function")
+ return
+ }
+
+ if fType.NumOut() != 1 {
+ err = SetupError("function returns more than one value.")
+ return
+ }
+ if _, ok := fType.Out(0).(*reflect.BoolType); !ok {
+ err = SetupError("function does not return a bool")
+ return
+ }
+
+ arguments := make([]reflect.Value, fType.NumIn())
+ rand := config.getRand()
+ maxCount := config.getMaxCount()
+
+ for i := 0; i < maxCount; i++ {
+ err = arbitraryValues(arguments, fType, config, rand)
+ if err != nil {
+ return
+ }
+
+ if !f.Call(arguments)[0].(*reflect.BoolValue).Get() {
+ err = &CheckError{i + 1, toInterfaces(arguments)}
+ return
+ }
+ }
+
+ return
+}
+
+// CheckEqual looks for an input on which f and g return different results.
+// It calls f and g repeatedly with arbitrary values for each argument.
+// If f and g return different answers, CheckEqual returns a *CheckEqualError
+// describing the input and the outputs.
+func CheckEqual(f, g interface{}, config *Config) (err os.Error) {
+ if config == nil {
+ config = &defaultConfig
+ }
+
+ x, xType, ok := functionAndType(f)
+ if !ok {
+ err = SetupError("f is not a function")
+ return
+ }
+ y, yType, ok := functionAndType(g)
+ if !ok {
+ err = SetupError("g is not a function")
+ return
+ }
+
+ if xType != yType {
+ err = SetupError("functions have different types")
+ return
+ }
+
+ arguments := make([]reflect.Value, xType.NumIn())
+ rand := config.getRand()
+ maxCount := config.getMaxCount()
+
+ for i := 0; i < maxCount; i++ {
+ err = arbitraryValues(arguments, xType, config, rand)
+ if err != nil {
+ return
+ }
+
+ xOut := toInterfaces(x.Call(arguments))
+ yOut := toInterfaces(y.Call(arguments))
+
+ if !reflect.DeepEqual(xOut, yOut) {
+ err = &CheckEqualError{CheckError{i + 1, toInterfaces(arguments)}, xOut, yOut}
+ return
+ }
+ }
+
+ return
+}
+
+// arbitraryValues writes Values to args such that args contains Values
+// suitable for calling f.
+func arbitraryValues(args []reflect.Value, f *reflect.FuncType, config *Config, rand *rand.Rand) (err os.Error) {
+ if config.Values != nil {
+ config.Values(args, rand)
+ return
+ }
+
+ for j := 0; j < len(args); j++ {
+ var ok bool
+ args[j], ok = Value(f.In(j), rand)
+ if !ok {
+ err = SetupError(fmt.Sprintf("cannot create arbitrary value of type %s for argument %d", f.In(j), j))
+ return
+ }
+ }
+
+ return
+}
+
+func functionAndType(f interface{}) (v *reflect.FuncValue, t *reflect.FuncType, ok bool) {
+ v, ok = reflect.NewValue(f).(*reflect.FuncValue)
+ if !ok {
+ return
+ }
+ t = v.Type().(*reflect.FuncType)
+ return
+}
+
+func toInterfaces(values []reflect.Value) []interface{} {
+ ret := make([]interface{}, len(values))
+ for i, v := range values {
+ ret[i] = v.Interface()
+ }
+ return ret
+}
+
+func toString(interfaces []interface{}) string {
+ s := make([]string, len(interfaces))
+ for i, v := range interfaces {
+ s[i] = fmt.Sprintf("%#v", v)
+ }
+ return strings.Join(s, ", ")
+}
diff --git a/src/cmd/gofix/testdata/reflect.quick.go.out b/src/cmd/gofix/testdata/reflect.quick.go.out
new file mode 100644
index 000000000..152dbad32
--- /dev/null
+++ b/src/cmd/gofix/testdata/reflect.quick.go.out
@@ -0,0 +1,365 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This package implements utility functions to help with black box testing.
+package quick
+
+import (
+ "flag"
+ "fmt"
+ "math"
+ "os"
+ "rand"
+ "reflect"
+ "strings"
+)
+
+var defaultMaxCount *int = flag.Int("quickchecks", 100, "The default number of iterations for each check")
+
+// A Generator can generate random values of its own type.
+type Generator interface {
+ // Generate returns a random instance of the type on which it is a
+ // method using the size as a size hint.
+ Generate(rand *rand.Rand, size int) reflect.Value
+}
+
+// randFloat32 generates a random float taking the full range of a float32.
+func randFloat32(rand *rand.Rand) float32 {
+ f := rand.Float64() * math.MaxFloat32
+ if rand.Int()&1 == 1 {
+ f = -f
+ }
+ return float32(f)
+}
+
+// randFloat64 generates a random float taking the full range of a float64.
+func randFloat64(rand *rand.Rand) float64 {
+ f := rand.Float64()
+ if rand.Int()&1 == 1 {
+ f = -f
+ }
+ return f
+}
+
+// randInt64 returns a random integer taking half the range of an int64.
+func randInt64(rand *rand.Rand) int64 { return rand.Int63() - 1<<62 }
+
+// complexSize is the maximum length of arbitrary values that contain other
+// values.
+const complexSize = 50
+
+// Value returns an arbitrary value of the given type.
+// If the type implements the Generator interface, that will be used.
+// Note: in order to create arbitrary values for structs, all the members must be public.
+func Value(t reflect.Type, rand *rand.Rand) (value reflect.Value, ok bool) {
+ if m, ok := reflect.Zero(t).Interface().(Generator); ok {
+ return m.Generate(rand, complexSize), true
+ }
+
+ switch concrete := t; concrete.Kind() {
+ case reflect.Bool:
+ return reflect.NewValue(rand.Int()&1 == 0), true
+ case reflect.Float32, reflect.Float64, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Complex64, reflect.Complex128:
+ switch t.Kind() {
+ case reflect.Float32:
+ return reflect.NewValue(randFloat32(rand)), true
+ case reflect.Float64:
+ return reflect.NewValue(randFloat64(rand)), true
+ case reflect.Complex64:
+ return reflect.NewValue(complex(randFloat32(rand), randFloat32(rand))), true
+ case reflect.Complex128:
+ return reflect.NewValue(complex(randFloat64(rand), randFloat64(rand))), true
+ case reflect.Int16:
+ return reflect.NewValue(int16(randInt64(rand))), true
+ case reflect.Int32:
+ return reflect.NewValue(int32(randInt64(rand))), true
+ case reflect.Int64:
+ return reflect.NewValue(randInt64(rand)), true
+ case reflect.Int8:
+ return reflect.NewValue(int8(randInt64(rand))), true
+ case reflect.Int:
+ return reflect.NewValue(int(randInt64(rand))), true
+ case reflect.Uint16:
+ return reflect.NewValue(uint16(randInt64(rand))), true
+ case reflect.Uint32:
+ return reflect.NewValue(uint32(randInt64(rand))), true
+ case reflect.Uint64:
+ return reflect.NewValue(uint64(randInt64(rand))), true
+ case reflect.Uint8:
+ return reflect.NewValue(uint8(randInt64(rand))), true
+ case reflect.Uint:
+ return reflect.NewValue(uint(randInt64(rand))), true
+ case reflect.Uintptr:
+ return reflect.NewValue(uintptr(randInt64(rand))), true
+ }
+ case reflect.Map:
+ numElems := rand.Intn(complexSize)
+ m := reflect.MakeMap(concrete)
+ for i := 0; i < numElems; i++ {
+ key, ok1 := Value(concrete.Key(), rand)
+ value, ok2 := Value(concrete.Elem(), rand)
+ if !ok1 || !ok2 {
+ return reflect.Value{}, false
+ }
+ m.SetMapIndex(key, value)
+ }
+ return m, true
+ case reflect.Ptr:
+ v, ok := Value(concrete.Elem(), rand)
+ if !ok {
+ return reflect.Value{}, false
+ }
+ p := reflect.Zero(concrete)
+ p.Set(v.Addr())
+ return p, true
+ case reflect.Slice:
+ numElems := rand.Intn(complexSize)
+ s := reflect.MakeSlice(concrete, numElems, numElems)
+ for i := 0; i < numElems; i++ {
+ v, ok := Value(concrete.Elem(), rand)
+ if !ok {
+ return reflect.Value{}, false
+ }
+ s.Index(i).Set(v)
+ }
+ return s, true
+ case reflect.String:
+ numChars := rand.Intn(complexSize)
+ codePoints := make([]int, numChars)
+ for i := 0; i < numChars; i++ {
+ codePoints[i] = rand.Intn(0x10ffff)
+ }
+ return reflect.NewValue(string(codePoints)), true
+ case reflect.Struct:
+ s := reflect.Zero(t)
+ for i := 0; i < s.NumField(); i++ {
+ v, ok := Value(concrete.Field(i).Type, rand)
+ if !ok {
+ return reflect.Value{}, false
+ }
+ s.Field(i).Set(v)
+ }
+ return s, true
+ default:
+ return reflect.Value{}, false
+ }
+
+ return
+}
+
+// A Config structure contains options for running a test.
+type Config struct {
+ // MaxCount sets the maximum number of iterations. If zero,
+ // MaxCountScale is used.
+ MaxCount int
+ // MaxCountScale is a non-negative scale factor applied to the default
+ // maximum. If zero, the default is unchanged.
+ MaxCountScale float64
+ // If non-nil, rand is a source of random numbers. Otherwise a default
+ // pseudo-random source will be used.
+ Rand *rand.Rand
+ // If non-nil, Values is a function which generates a slice of arbitrary
+ // Values that are congruent with the arguments to the function being
+ // tested. Otherwise, Values is used to generate the values.
+ Values func([]reflect.Value, *rand.Rand)
+}
+
+var defaultConfig Config
+
+// getRand returns the *rand.Rand to use for a given Config.
+func (c *Config) getRand() *rand.Rand {
+ if c.Rand == nil {
+ return rand.New(rand.NewSource(0))
+ }
+ return c.Rand
+}
+
+// getMaxCount returns the maximum number of iterations to run for a given
+// Config.
+func (c *Config) getMaxCount() (maxCount int) {
+ maxCount = c.MaxCount
+ if maxCount == 0 {
+ if c.MaxCountScale != 0 {
+ maxCount = int(c.MaxCountScale * float64(*defaultMaxCount))
+ } else {
+ maxCount = *defaultMaxCount
+ }
+ }
+
+ return
+}
+
+// A SetupError is the result of an error in the way that check is being
+// used, independent of the functions being tested.
+type SetupError string
+
+func (s SetupError) String() string { return string(s) }
+
+// A CheckError is the result of Check finding an error.
+type CheckError struct {
+ Count int
+ In []interface{}
+}
+
+func (s *CheckError) String() string {
+ return fmt.Sprintf("#%d: failed on input %s", s.Count, toString(s.In))
+}
+
+// A CheckEqualError is the result CheckEqual finding an error.
+type CheckEqualError struct {
+ CheckError
+ Out1 []interface{}
+ Out2 []interface{}
+}
+
+func (s *CheckEqualError) String() string {
+ return fmt.Sprintf("#%d: failed on input %s. Output 1: %s. Output 2: %s", s.Count, toString(s.In), toString(s.Out1), toString(s.Out2))
+}
+
+// Check looks for an input to f, any function that returns bool,
+// such that f returns false. It calls f repeatedly, with arbitrary
+// values for each argument. If f returns false on a given input,
+// Check returns that input as a *CheckError.
+// For example:
+//
+// func TestOddMultipleOfThree(t *testing.T) {
+// f := func(x int) bool {
+// y := OddMultipleOfThree(x)
+// return y%2 == 1 && y%3 == 0
+// }
+// if err := quick.Check(f, nil); err != nil {
+// t.Error(err)
+// }
+// }
+func Check(function interface{}, config *Config) (err os.Error) {
+ if config == nil {
+ config = &defaultConfig
+ }
+
+ f, fType, ok := functionAndType(function)
+ if !ok {
+ err = SetupError("argument is not a function")
+ return
+ }
+
+ if fType.NumOut() != 1 {
+ err = SetupError("function returns more than one value.")
+ return
+ }
+ if fType.Out(0).Kind() != reflect.Bool {
+ err = SetupError("function does not return a bool")
+ return
+ }
+
+ arguments := make([]reflect.Value, fType.NumIn())
+ rand := config.getRand()
+ maxCount := config.getMaxCount()
+
+ for i := 0; i < maxCount; i++ {
+ err = arbitraryValues(arguments, fType, config, rand)
+ if err != nil {
+ return
+ }
+
+ if !f.Call(arguments)[0].Bool() {
+ err = &CheckError{i + 1, toInterfaces(arguments)}
+ return
+ }
+ }
+
+ return
+}
+
+// CheckEqual looks for an input on which f and g return different results.
+// It calls f and g repeatedly with arbitrary values for each argument.
+// If f and g return different answers, CheckEqual returns a *CheckEqualError
+// describing the input and the outputs.
+func CheckEqual(f, g interface{}, config *Config) (err os.Error) {
+ if config == nil {
+ config = &defaultConfig
+ }
+
+ x, xType, ok := functionAndType(f)
+ if !ok {
+ err = SetupError("f is not a function")
+ return
+ }
+ y, yType, ok := functionAndType(g)
+ if !ok {
+ err = SetupError("g is not a function")
+ return
+ }
+
+ if xType != yType {
+ err = SetupError("functions have different types")
+ return
+ }
+
+ arguments := make([]reflect.Value, xType.NumIn())
+ rand := config.getRand()
+ maxCount := config.getMaxCount()
+
+ for i := 0; i < maxCount; i++ {
+ err = arbitraryValues(arguments, xType, config, rand)
+ if err != nil {
+ return
+ }
+
+ xOut := toInterfaces(x.Call(arguments))
+ yOut := toInterfaces(y.Call(arguments))
+
+ if !reflect.DeepEqual(xOut, yOut) {
+ err = &CheckEqualError{CheckError{i + 1, toInterfaces(arguments)}, xOut, yOut}
+ return
+ }
+ }
+
+ return
+}
+
+// arbitraryValues writes Values to args such that args contains Values
+// suitable for calling f.
+func arbitraryValues(args []reflect.Value, f reflect.Type, config *Config, rand *rand.Rand) (err os.Error) {
+ if config.Values != nil {
+ config.Values(args, rand)
+ return
+ }
+
+ for j := 0; j < len(args); j++ {
+ var ok bool
+ args[j], ok = Value(f.In(j), rand)
+ if !ok {
+ err = SetupError(fmt.Sprintf("cannot create arbitrary value of type %s for argument %d", f.In(j), j))
+ return
+ }
+ }
+
+ return
+}
+
+func functionAndType(f interface{}) (v reflect.Value, t reflect.Type, ok bool) {
+ v = reflect.NewValue(f)
+ ok = v.Kind() == reflect.Func
+ if !ok {
+ return
+ }
+ t = v.Type()
+ return
+}
+
+func toInterfaces(values []reflect.Value) []interface{} {
+ ret := make([]interface{}, len(values))
+ for i, v := range values {
+ ret[i] = v.Interface()
+ }
+ return ret
+}
+
+func toString(interfaces []interface{}) string {
+ s := make([]string, len(interfaces))
+ for i, v := range interfaces {
+ s[i] = fmt.Sprintf("%#v", v)
+ }
+ return strings.Join(s, ", ")
+}
diff --git a/src/cmd/gofix/testdata/reflect.read.go.in b/src/cmd/gofix/testdata/reflect.read.go.in
new file mode 100644
index 000000000..9ae3bb8ee
--- /dev/null
+++ b/src/cmd/gofix/testdata/reflect.read.go.in
@@ -0,0 +1,620 @@
+// 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 xml
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "os"
+ "reflect"
+ "strconv"
+ "strings"
+ "unicode"
+ "utf8"
+)
+
+// BUG(rsc): Mapping between XML elements and data structures is inherently flawed:
+// an XML element is an order-dependent collection of anonymous
+// values, while a data structure is an order-independent collection
+// of named values.
+// See package json for a textual representation more suitable
+// to data structures.
+
+// Unmarshal parses an XML element from r and uses the
+// reflect library to fill in an arbitrary struct, slice, or string
+// pointed at by val. Well-formed data that does not fit
+// into val is discarded.
+//
+// For example, given these definitions:
+//
+// type Email struct {
+// Where string "attr"
+// Addr string
+// }
+//
+// type Result struct {
+// XMLName xml.Name "result"
+// Name string
+// Phone string
+// Email []Email
+// Groups []string "group>value"
+// }
+//
+// result := Result{Name: "name", Phone: "phone", Email: nil}
+//
+// unmarshalling the XML input
+//
+// <result>
+// <email where="home">
+// <addr>gre@example.com</addr>
+// </email>
+// <email where='work'>
+// <addr>gre@work.com</addr>
+// </email>
+// <name>Grace R. Emlin</name>
+// <group>
+// <value>Friends</value>
+// <value>Squash</value>
+// </group>
+// <address>123 Main Street</address>
+// </result>
+//
+// via Unmarshal(r, &result) is equivalent to assigning
+//
+// r = Result{xml.Name{"", "result"},
+// "Grace R. Emlin", // name
+// "phone", // no phone given
+// []Email{
+// Email{"home", "gre@example.com"},
+// Email{"work", "gre@work.com"},
+// },
+// []string{"Friends", "Squash"},
+// }
+//
+// Note that the field r.Phone has not been modified and
+// that the XML <address> element was discarded. Also, the field
+// Groups was assigned considering the element path provided in the
+// field tag.
+//
+// Because Unmarshal uses the reflect package, it can only
+// assign to upper case fields. Unmarshal uses a case-insensitive
+// comparison to match XML element names to struct field names.
+//
+// Unmarshal maps an XML element to a struct using the following rules:
+//
+// * If the struct has a field of type []byte or string with tag "innerxml",
+// Unmarshal accumulates the raw XML nested inside the element
+// in that field. The rest of the rules still apply.
+//
+// * If the struct has a field named XMLName of type xml.Name,
+// Unmarshal records the element name in that field.
+//
+// * If the XMLName field has an associated tag string of the form
+// "tag" or "namespace-URL tag", the XML element must have
+// the given tag (and, optionally, name space) or else Unmarshal
+// returns an error.
+//
+// * If the XML element has an attribute whose name matches a
+// struct field of type string with tag "attr", Unmarshal records
+// the attribute value in that field.
+//
+// * If the XML element contains character data, that data is
+// accumulated in the first struct field that has tag "chardata".
+// The struct field may have type []byte or string.
+// If there is no such field, the character data is discarded.
+//
+// * If the XML element contains a sub-element whose name matches
+// the prefix of a struct field tag formatted as "a>b>c", unmarshal
+// will descend into the XML structure looking for elements with the
+// given names, and will map the innermost elements to that struct field.
+// A struct field tag starting with ">" is equivalent to one starting
+// with the field name followed by ">".
+//
+// * If the XML element contains a sub-element whose name
+// matches a struct field whose tag is neither "attr" nor "chardata",
+// Unmarshal maps the sub-element to that struct field.
+// Otherwise, if the struct has a field named Any, unmarshal
+// maps the sub-element to that struct field.
+//
+// Unmarshal maps an XML element to a string or []byte by saving the
+// concatenation of that element's character data in the string or []byte.
+//
+// Unmarshal maps an XML element to a slice by extending the length
+// of the slice and mapping the element to the newly created value.
+//
+// Unmarshal maps an XML element to a bool by setting it to the boolean
+// value represented by the string.
+//
+// Unmarshal maps an XML element to an integer or floating-point
+// field by setting the field to the result of interpreting the string
+// value in decimal. There is no check for overflow.
+//
+// Unmarshal maps an XML element to an xml.Name by recording the
+// element name.
+//
+// Unmarshal maps an XML element to a pointer by setting the pointer
+// to a freshly allocated value and then mapping the element to that value.
+//
+func Unmarshal(r io.Reader, val interface{}) os.Error {
+ v, ok := reflect.NewValue(val).(*reflect.PtrValue)
+ if !ok {
+ return os.NewError("non-pointer passed to Unmarshal")
+ }
+ p := NewParser(r)
+ elem := v.Elem()
+ err := p.unmarshal(elem, nil)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// An UnmarshalError represents an error in the unmarshalling process.
+type UnmarshalError string
+
+func (e UnmarshalError) String() string { return string(e) }
+
+// A TagPathError represents an error in the unmarshalling process
+// caused by the use of field tags with conflicting paths.
+type TagPathError struct {
+ Struct reflect.Type
+ Field1, Tag1 string
+ Field2, Tag2 string
+}
+
+func (e *TagPathError) String() string {
+ return fmt.Sprintf("%s field %q with tag %q conflicts with field %q with tag %q", e.Struct, e.Field1, e.Tag1, e.Field2, e.Tag2)
+}
+
+// The Parser's Unmarshal method is like xml.Unmarshal
+// except that it can be passed a pointer to the initial start element,
+// useful when a client reads some raw XML tokens itself
+// but also defers to Unmarshal for some elements.
+// Passing a nil start element indicates that Unmarshal should
+// read the token stream to find the start element.
+func (p *Parser) Unmarshal(val interface{}, start *StartElement) os.Error {
+ v, ok := reflect.NewValue(val).(*reflect.PtrValue)
+ if !ok {
+ return os.NewError("non-pointer passed to Unmarshal")
+ }
+ return p.unmarshal(v.Elem(), start)
+}
+
+// fieldName strips invalid characters from an XML name
+// to create a valid Go struct name. It also converts the
+// name to lower case letters.
+func fieldName(original string) string {
+
+ var i int
+ //remove leading underscores
+ for i = 0; i < len(original) && original[i] == '_'; i++ {
+ }
+
+ return strings.Map(
+ func(x int) int {
+ if x == '_' || unicode.IsDigit(x) || unicode.IsLetter(x) {
+ return unicode.ToLower(x)
+ }
+ return -1
+ },
+ original[i:])
+}
+
+// Unmarshal a single XML element into val.
+func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error {
+ // Find start element if we need it.
+ if start == nil {
+ for {
+ tok, err := p.Token()
+ if err != nil {
+ return err
+ }
+ if t, ok := tok.(StartElement); ok {
+ start = &t
+ break
+ }
+ }
+ }
+
+ if pv, ok := val.(*reflect.PtrValue); ok {
+ if pv.Get() == 0 {
+ zv := reflect.MakeZero(pv.Type().(*reflect.PtrType).Elem())
+ pv.PointTo(zv)
+ val = zv
+ } else {
+ val = pv.Elem()
+ }
+ }
+
+ var (
+ data []byte
+ saveData reflect.Value
+ comment []byte
+ saveComment reflect.Value
+ saveXML reflect.Value
+ saveXMLIndex int
+ saveXMLData []byte
+ sv *reflect.StructValue
+ styp *reflect.StructType
+ fieldPaths map[string]pathInfo
+ )
+
+ switch v := val.(type) {
+ default:
+ return os.ErrorString("unknown type " + v.Type().String())
+
+ case *reflect.SliceValue:
+ typ := v.Type().(*reflect.SliceType)
+ if typ.Elem().Kind() == reflect.Uint8 {
+ // []byte
+ saveData = v
+ break
+ }
+
+ // Slice of element values.
+ // Grow slice.
+ n := v.Len()
+ if n >= v.Cap() {
+ ncap := 2 * n
+ if ncap < 4 {
+ ncap = 4
+ }
+ new := reflect.MakeSlice(typ, n, ncap)
+ reflect.Copy(new, v)
+ v.Set(new)
+ }
+ v.SetLen(n + 1)
+
+ // Recur to read element into slice.
+ if err := p.unmarshal(v.Elem(n), start); err != nil {
+ v.SetLen(n)
+ return err
+ }
+ return nil
+
+ case *reflect.BoolValue, *reflect.FloatValue, *reflect.IntValue, *reflect.UintValue, *reflect.StringValue:
+ saveData = v
+
+ case *reflect.StructValue:
+ if _, ok := v.Interface().(Name); ok {
+ v.Set(reflect.NewValue(start.Name).(*reflect.StructValue))
+ break
+ }
+
+ sv = v
+ typ := sv.Type().(*reflect.StructType)
+ styp = typ
+ // Assign name.
+ if f, ok := typ.FieldByName("XMLName"); ok {
+ // Validate element name.
+ if f.Tag != "" {
+ tag := f.Tag
+ ns := ""
+ i := strings.LastIndex(tag, " ")
+ if i >= 0 {
+ ns, tag = tag[0:i], tag[i+1:]
+ }
+ if tag != start.Name.Local {
+ return UnmarshalError("expected element type <" + tag + "> but have <" + start.Name.Local + ">")
+ }
+ if ns != "" && ns != start.Name.Space {
+ e := "expected element <" + tag + "> in name space " + ns + " but have "
+ if start.Name.Space == "" {
+ e += "no name space"
+ } else {
+ e += start.Name.Space
+ }
+ return UnmarshalError(e)
+ }
+ }
+
+ // Save
+ v := sv.FieldByIndex(f.Index)
+ if _, ok := v.Interface().(Name); !ok {
+ return UnmarshalError(sv.Type().String() + " field XMLName does not have type xml.Name")
+ }
+ v.(*reflect.StructValue).Set(reflect.NewValue(start.Name).(*reflect.StructValue))
+ }
+
+ // Assign attributes.
+ // Also, determine whether we need to save character data or comments.
+ for i, n := 0, typ.NumField(); i < n; i++ {
+ f := typ.Field(i)
+ switch f.Tag {
+ case "attr":
+ strv, ok := sv.FieldByIndex(f.Index).(*reflect.StringValue)
+ if !ok {
+ return UnmarshalError(sv.Type().String() + " field " + f.Name + " has attr tag but is not type string")
+ }
+ // Look for attribute.
+ val := ""
+ k := strings.ToLower(f.Name)
+ for _, a := range start.Attr {
+ if fieldName(a.Name.Local) == k {
+ val = a.Value
+ break
+ }
+ }
+ strv.Set(val)
+
+ case "comment":
+ if saveComment == nil {
+ saveComment = sv.FieldByIndex(f.Index)
+ }
+
+ case "chardata":
+ if saveData == nil {
+ saveData = sv.FieldByIndex(f.Index)
+ }
+
+ case "innerxml":
+ if saveXML == nil {
+ saveXML = sv.FieldByIndex(f.Index)
+ if p.saved == nil {
+ saveXMLIndex = 0
+ p.saved = new(bytes.Buffer)
+ } else {
+ saveXMLIndex = p.savedOffset()
+ }
+ }
+
+ default:
+ if strings.Contains(f.Tag, ">") {
+ if fieldPaths == nil {
+ fieldPaths = make(map[string]pathInfo)
+ }
+ path := strings.ToLower(f.Tag)
+ if strings.HasPrefix(f.Tag, ">") {
+ path = strings.ToLower(f.Name) + path
+ }
+ if strings.HasSuffix(f.Tag, ">") {
+ path = path[:len(path)-1]
+ }
+ err := addFieldPath(sv, fieldPaths, path, f.Index)
+ if err != nil {
+ return err
+ }
+ }
+ }
+ }
+ }
+
+ // Find end element.
+ // Process sub-elements along the way.
+Loop:
+ for {
+ var savedOffset int
+ if saveXML != nil {
+ savedOffset = p.savedOffset()
+ }
+ tok, err := p.Token()
+ if err != nil {
+ return err
+ }
+ switch t := tok.(type) {
+ case StartElement:
+ // Sub-element.
+ // Look up by tag name.
+ if sv != nil {
+ k := fieldName(t.Name.Local)
+
+ if fieldPaths != nil {
+ if _, found := fieldPaths[k]; found {
+ if err := p.unmarshalPaths(sv, fieldPaths, k, &t); err != nil {
+ return err
+ }
+ continue Loop
+ }
+ }
+
+ match := func(s string) bool {
+ // check if the name matches ignoring case
+ if strings.ToLower(s) != k {
+ return false
+ }
+ // now check that it's public
+ c, _ := utf8.DecodeRuneInString(s)
+ return unicode.IsUpper(c)
+ }
+
+ f, found := styp.FieldByNameFunc(match)
+ if !found { // fall back to mop-up field named "Any"
+ f, found = styp.FieldByName("Any")
+ }
+ if found {
+ if err := p.unmarshal(sv.FieldByIndex(f.Index), &t); err != nil {
+ return err
+ }
+ continue Loop
+ }
+ }
+ // Not saving sub-element but still have to skip over it.
+ if err := p.Skip(); err != nil {
+ return err
+ }
+
+ case EndElement:
+ if saveXML != nil {
+ saveXMLData = p.saved.Bytes()[saveXMLIndex:savedOffset]
+ if saveXMLIndex == 0 {
+ p.saved = nil
+ }
+ }
+ break Loop
+
+ case CharData:
+ if saveData != nil {
+ data = append(data, t...)
+ }
+
+ case Comment:
+ if saveComment != nil {
+ comment = append(comment, t...)
+ }
+ }
+ }
+
+ var err os.Error
+ // Helper functions for integer and unsigned integer conversions
+ var itmp int64
+ getInt64 := func() bool {
+ itmp, err = strconv.Atoi64(string(data))
+ // TODO: should check sizes
+ return err == nil
+ }
+ var utmp uint64
+ getUint64 := func() bool {
+ utmp, err = strconv.Atoui64(string(data))
+ // TODO: check for overflow?
+ return err == nil
+ }
+ var ftmp float64
+ getFloat64 := func() bool {
+ ftmp, err = strconv.Atof64(string(data))
+ // TODO: check for overflow?
+ return err == nil
+ }
+
+ // Save accumulated data and comments
+ switch t := saveData.(type) {
+ case nil:
+ // Probably a comment, handled below
+ default:
+ return os.ErrorString("cannot happen: unknown type " + t.Type().String())
+ case *reflect.IntValue:
+ if !getInt64() {
+ return err
+ }
+ t.Set(itmp)
+ case *reflect.UintValue:
+ if !getUint64() {
+ return err
+ }
+ t.Set(utmp)
+ case *reflect.FloatValue:
+ if !getFloat64() {
+ return err
+ }
+ t.Set(ftmp)
+ case *reflect.BoolValue:
+ value, err := strconv.Atob(strings.TrimSpace(string(data)))
+ if err != nil {
+ return err
+ }
+ t.Set(value)
+ case *reflect.StringValue:
+ t.Set(string(data))
+ case *reflect.SliceValue:
+ t.Set(reflect.NewValue(data).(*reflect.SliceValue))
+ }
+
+ switch t := saveComment.(type) {
+ case *reflect.StringValue:
+ t.Set(string(comment))
+ case *reflect.SliceValue:
+ t.Set(reflect.NewValue(comment).(*reflect.SliceValue))
+ }
+
+ switch t := saveXML.(type) {
+ case *reflect.StringValue:
+ t.Set(string(saveXMLData))
+ case *reflect.SliceValue:
+ t.Set(reflect.NewValue(saveXMLData).(*reflect.SliceValue))
+ }
+
+ return nil
+}
+
+type pathInfo struct {
+ fieldIdx []int
+ complete bool
+}
+
+// addFieldPath takes an element path such as "a>b>c" and fills the
+// paths map with all paths leading to it ("a", "a>b", and "a>b>c").
+// It is okay for paths to share a common, shorter prefix but not ok
+// for one path to itself be a prefix of another.
+func addFieldPath(sv *reflect.StructValue, paths map[string]pathInfo, path string, fieldIdx []int) os.Error {
+ if info, found := paths[path]; found {
+ return tagError(sv, info.fieldIdx, fieldIdx)
+ }
+ paths[path] = pathInfo{fieldIdx, true}
+ for {
+ i := strings.LastIndex(path, ">")
+ if i < 0 {
+ break
+ }
+ path = path[:i]
+ if info, found := paths[path]; found {
+ if info.complete {
+ return tagError(sv, info.fieldIdx, fieldIdx)
+ }
+ } else {
+ paths[path] = pathInfo{fieldIdx, false}
+ }
+ }
+ return nil
+
+}
+
+func tagError(sv *reflect.StructValue, idx1 []int, idx2 []int) os.Error {
+ t := sv.Type().(*reflect.StructType)
+ f1 := t.FieldByIndex(idx1)
+ f2 := t.FieldByIndex(idx2)
+ return &TagPathError{t, f1.Name, f1.Tag, f2.Name, f2.Tag}
+}
+
+// unmarshalPaths walks down an XML structure looking for
+// wanted paths, and calls unmarshal on them.
+func (p *Parser) unmarshalPaths(sv *reflect.StructValue, paths map[string]pathInfo, path string, start *StartElement) os.Error {
+ if info, _ := paths[path]; info.complete {
+ return p.unmarshal(sv.FieldByIndex(info.fieldIdx), start)
+ }
+ for {
+ tok, err := p.Token()
+ if err != nil {
+ return err
+ }
+ switch t := tok.(type) {
+ case StartElement:
+ k := path + ">" + fieldName(t.Name.Local)
+ if _, found := paths[k]; found {
+ if err := p.unmarshalPaths(sv, paths, k, &t); err != nil {
+ return err
+ }
+ continue
+ }
+ if err := p.Skip(); err != nil {
+ return err
+ }
+ case EndElement:
+ return nil
+ }
+ }
+ panic("unreachable")
+}
+
+// Have already read a start element.
+// Read tokens until we find the end element.
+// Token is taking care of making sure the
+// end element matches the start element we saw.
+func (p *Parser) Skip() os.Error {
+ for {
+ tok, err := p.Token()
+ if err != nil {
+ return err
+ }
+ switch t := tok.(type) {
+ case StartElement:
+ if err := p.Skip(); err != nil {
+ return err
+ }
+ case EndElement:
+ return nil
+ }
+ }
+ panic("unreachable")
+}
diff --git a/src/cmd/gofix/testdata/reflect.read.go.out b/src/cmd/gofix/testdata/reflect.read.go.out
new file mode 100644
index 000000000..a3ddb9d4c
--- /dev/null
+++ b/src/cmd/gofix/testdata/reflect.read.go.out
@@ -0,0 +1,620 @@
+// 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 xml
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "os"
+ "reflect"
+ "strconv"
+ "strings"
+ "unicode"
+ "utf8"
+)
+
+// BUG(rsc): Mapping between XML elements and data structures is inherently flawed:
+// an XML element is an order-dependent collection of anonymous
+// values, while a data structure is an order-independent collection
+// of named values.
+// See package json for a textual representation more suitable
+// to data structures.
+
+// Unmarshal parses an XML element from r and uses the
+// reflect library to fill in an arbitrary struct, slice, or string
+// pointed at by val. Well-formed data that does not fit
+// into val is discarded.
+//
+// For example, given these definitions:
+//
+// type Email struct {
+// Where string "attr"
+// Addr string
+// }
+//
+// type Result struct {
+// XMLName xml.Name "result"
+// Name string
+// Phone string
+// Email []Email
+// Groups []string "group>value"
+// }
+//
+// result := Result{Name: "name", Phone: "phone", Email: nil}
+//
+// unmarshalling the XML input
+//
+// <result>
+// <email where="home">
+// <addr>gre@example.com</addr>
+// </email>
+// <email where='work'>
+// <addr>gre@work.com</addr>
+// </email>
+// <name>Grace R. Emlin</name>
+// <group>
+// <value>Friends</value>
+// <value>Squash</value>
+// </group>
+// <address>123 Main Street</address>
+// </result>
+//
+// via Unmarshal(r, &result) is equivalent to assigning
+//
+// r = Result{xml.Name{"", "result"},
+// "Grace R. Emlin", // name
+// "phone", // no phone given
+// []Email{
+// Email{"home", "gre@example.com"},
+// Email{"work", "gre@work.com"},
+// },
+// []string{"Friends", "Squash"},
+// }
+//
+// Note that the field r.Phone has not been modified and
+// that the XML <address> element was discarded. Also, the field
+// Groups was assigned considering the element path provided in the
+// field tag.
+//
+// Because Unmarshal uses the reflect package, it can only
+// assign to upper case fields. Unmarshal uses a case-insensitive
+// comparison to match XML element names to struct field names.
+//
+// Unmarshal maps an XML element to a struct using the following rules:
+//
+// * If the struct has a field of type []byte or string with tag "innerxml",
+// Unmarshal accumulates the raw XML nested inside the element
+// in that field. The rest of the rules still apply.
+//
+// * If the struct has a field named XMLName of type xml.Name,
+// Unmarshal records the element name in that field.
+//
+// * If the XMLName field has an associated tag string of the form
+// "tag" or "namespace-URL tag", the XML element must have
+// the given tag (and, optionally, name space) or else Unmarshal
+// returns an error.
+//
+// * If the XML element has an attribute whose name matches a
+// struct field of type string with tag "attr", Unmarshal records
+// the attribute value in that field.
+//
+// * If the XML element contains character data, that data is
+// accumulated in the first struct field that has tag "chardata".
+// The struct field may have type []byte or string.
+// If there is no such field, the character data is discarded.
+//
+// * If the XML element contains a sub-element whose name matches
+// the prefix of a struct field tag formatted as "a>b>c", unmarshal
+// will descend into the XML structure looking for elements with the
+// given names, and will map the innermost elements to that struct field.
+// A struct field tag starting with ">" is equivalent to one starting
+// with the field name followed by ">".
+//
+// * If the XML element contains a sub-element whose name
+// matches a struct field whose tag is neither "attr" nor "chardata",
+// Unmarshal maps the sub-element to that struct field.
+// Otherwise, if the struct has a field named Any, unmarshal
+// maps the sub-element to that struct field.
+//
+// Unmarshal maps an XML element to a string or []byte by saving the
+// concatenation of that element's character data in the string or []byte.
+//
+// Unmarshal maps an XML element to a slice by extending the length
+// of the slice and mapping the element to the newly created value.
+//
+// Unmarshal maps an XML element to a bool by setting it to the boolean
+// value represented by the string.
+//
+// Unmarshal maps an XML element to an integer or floating-point
+// field by setting the field to the result of interpreting the string
+// value in decimal. There is no check for overflow.
+//
+// Unmarshal maps an XML element to an xml.Name by recording the
+// element name.
+//
+// Unmarshal maps an XML element to a pointer by setting the pointer
+// to a freshly allocated value and then mapping the element to that value.
+//
+func Unmarshal(r io.Reader, val interface{}) os.Error {
+ v := reflect.NewValue(val)
+ if v.Kind() != reflect.Ptr {
+ return os.NewError("non-pointer passed to Unmarshal")
+ }
+ p := NewParser(r)
+ elem := v.Elem()
+ err := p.unmarshal(elem, nil)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// An UnmarshalError represents an error in the unmarshalling process.
+type UnmarshalError string
+
+func (e UnmarshalError) String() string { return string(e) }
+
+// A TagPathError represents an error in the unmarshalling process
+// caused by the use of field tags with conflicting paths.
+type TagPathError struct {
+ Struct reflect.Type
+ Field1, Tag1 string
+ Field2, Tag2 string
+}
+
+func (e *TagPathError) String() string {
+ return fmt.Sprintf("%s field %q with tag %q conflicts with field %q with tag %q", e.Struct, e.Field1, e.Tag1, e.Field2, e.Tag2)
+}
+
+// The Parser's Unmarshal method is like xml.Unmarshal
+// except that it can be passed a pointer to the initial start element,
+// useful when a client reads some raw XML tokens itself
+// but also defers to Unmarshal for some elements.
+// Passing a nil start element indicates that Unmarshal should
+// read the token stream to find the start element.
+func (p *Parser) Unmarshal(val interface{}, start *StartElement) os.Error {
+ v := reflect.NewValue(val)
+ if v.Kind() != reflect.Ptr {
+ return os.NewError("non-pointer passed to Unmarshal")
+ }
+ return p.unmarshal(v.Elem(), start)
+}
+
+// fieldName strips invalid characters from an XML name
+// to create a valid Go struct name. It also converts the
+// name to lower case letters.
+func fieldName(original string) string {
+
+ var i int
+ //remove leading underscores
+ for i = 0; i < len(original) && original[i] == '_'; i++ {
+ }
+
+ return strings.Map(
+ func(x int) int {
+ if x == '_' || unicode.IsDigit(x) || unicode.IsLetter(x) {
+ return unicode.ToLower(x)
+ }
+ return -1
+ },
+ original[i:])
+}
+
+// Unmarshal a single XML element into val.
+func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error {
+ // Find start element if we need it.
+ if start == nil {
+ for {
+ tok, err := p.Token()
+ if err != nil {
+ return err
+ }
+ if t, ok := tok.(StartElement); ok {
+ start = &t
+ break
+ }
+ }
+ }
+
+ if pv := val; pv.Kind() == reflect.Ptr {
+ if pv.Pointer() == 0 {
+ zv := reflect.Zero(pv.Type().Elem())
+ pv.Set(zv.Addr())
+ val = zv
+ } else {
+ val = pv.Elem()
+ }
+ }
+
+ var (
+ data []byte
+ saveData reflect.Value
+ comment []byte
+ saveComment reflect.Value
+ saveXML reflect.Value
+ saveXMLIndex int
+ saveXMLData []byte
+ sv reflect.Value
+ styp reflect.Type
+ fieldPaths map[string]pathInfo
+ )
+
+ switch v := val; v.Kind() {
+ default:
+ return os.ErrorString("unknown type " + v.Type().String())
+
+ case reflect.Slice:
+ typ := v.Type()
+ if typ.Elem().Kind() == reflect.Uint8 {
+ // []byte
+ saveData = v
+ break
+ }
+
+ // Slice of element values.
+ // Grow slice.
+ n := v.Len()
+ if n >= v.Cap() {
+ ncap := 2 * n
+ if ncap < 4 {
+ ncap = 4
+ }
+ new := reflect.MakeSlice(typ, n, ncap)
+ reflect.Copy(new, v)
+ v.Set(new)
+ }
+ v.SetLen(n + 1)
+
+ // Recur to read element into slice.
+ if err := p.unmarshal(v.Index(n), start); err != nil {
+ v.SetLen(n)
+ return err
+ }
+ return nil
+
+ case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.String:
+ saveData = v
+
+ case reflect.Struct:
+ if _, ok := v.Interface().(Name); ok {
+ v.Set(reflect.NewValue(start.Name))
+ break
+ }
+
+ sv = v
+ typ := sv.Type()
+ styp = typ
+ // Assign name.
+ if f, ok := typ.FieldByName("XMLName"); ok {
+ // Validate element name.
+ if f.Tag != "" {
+ tag := f.Tag
+ ns := ""
+ i := strings.LastIndex(tag, " ")
+ if i >= 0 {
+ ns, tag = tag[0:i], tag[i+1:]
+ }
+ if tag != start.Name.Local {
+ return UnmarshalError("expected element type <" + tag + "> but have <" + start.Name.Local + ">")
+ }
+ if ns != "" && ns != start.Name.Space {
+ e := "expected element <" + tag + "> in name space " + ns + " but have "
+ if start.Name.Space == "" {
+ e += "no name space"
+ } else {
+ e += start.Name.Space
+ }
+ return UnmarshalError(e)
+ }
+ }
+
+ // Save
+ v := sv.FieldByIndex(f.Index)
+ if _, ok := v.Interface().(Name); !ok {
+ return UnmarshalError(sv.Type().String() + " field XMLName does not have type xml.Name")
+ }
+ v.Set(reflect.NewValue(start.Name))
+ }
+
+ // Assign attributes.
+ // Also, determine whether we need to save character data or comments.
+ for i, n := 0, typ.NumField(); i < n; i++ {
+ f := typ.Field(i)
+ switch f.Tag {
+ case "attr":
+ strv := sv.FieldByIndex(f.Index)
+ if strv.Kind() != reflect.String {
+ return UnmarshalError(sv.Type().String() + " field " + f.Name + " has attr tag but is not type string")
+ }
+ // Look for attribute.
+ val := ""
+ k := strings.ToLower(f.Name)
+ for _, a := range start.Attr {
+ if fieldName(a.Name.Local) == k {
+ val = a.Value
+ break
+ }
+ }
+ strv.SetString(val)
+
+ case "comment":
+ if !saveComment.IsValid() {
+ saveComment = sv.FieldByIndex(f.Index)
+ }
+
+ case "chardata":
+ if !saveData.IsValid() {
+ saveData = sv.FieldByIndex(f.Index)
+ }
+
+ case "innerxml":
+ if !saveXML.IsValid() {
+ saveXML = sv.FieldByIndex(f.Index)
+ if p.saved == nil {
+ saveXMLIndex = 0
+ p.saved = new(bytes.Buffer)
+ } else {
+ saveXMLIndex = p.savedOffset()
+ }
+ }
+
+ default:
+ if strings.Contains(f.Tag, ">") {
+ if fieldPaths == nil {
+ fieldPaths = make(map[string]pathInfo)
+ }
+ path := strings.ToLower(f.Tag)
+ if strings.HasPrefix(f.Tag, ">") {
+ path = strings.ToLower(f.Name) + path
+ }
+ if strings.HasSuffix(f.Tag, ">") {
+ path = path[:len(path)-1]
+ }
+ err := addFieldPath(sv, fieldPaths, path, f.Index)
+ if err != nil {
+ return err
+ }
+ }
+ }
+ }
+ }
+
+ // Find end element.
+ // Process sub-elements along the way.
+Loop:
+ for {
+ var savedOffset int
+ if saveXML.IsValid() {
+ savedOffset = p.savedOffset()
+ }
+ tok, err := p.Token()
+ if err != nil {
+ return err
+ }
+ switch t := tok.(type) {
+ case StartElement:
+ // Sub-element.
+ // Look up by tag name.
+ if sv.IsValid() {
+ k := fieldName(t.Name.Local)
+
+ if fieldPaths != nil {
+ if _, found := fieldPaths[k]; found {
+ if err := p.unmarshalPaths(sv, fieldPaths, k, &t); err != nil {
+ return err
+ }
+ continue Loop
+ }
+ }
+
+ match := func(s string) bool {
+ // check if the name matches ignoring case
+ if strings.ToLower(s) != k {
+ return false
+ }
+ // now check that it's public
+ c, _ := utf8.DecodeRuneInString(s)
+ return unicode.IsUpper(c)
+ }
+
+ f, found := styp.FieldByNameFunc(match)
+ if !found { // fall back to mop-up field named "Any"
+ f, found = styp.FieldByName("Any")
+ }
+ if found {
+ if err := p.unmarshal(sv.FieldByIndex(f.Index), &t); err != nil {
+ return err
+ }
+ continue Loop
+ }
+ }
+ // Not saving sub-element but still have to skip over it.
+ if err := p.Skip(); err != nil {
+ return err
+ }
+
+ case EndElement:
+ if saveXML.IsValid() {
+ saveXMLData = p.saved.Bytes()[saveXMLIndex:savedOffset]
+ if saveXMLIndex == 0 {
+ p.saved = nil
+ }
+ }
+ break Loop
+
+ case CharData:
+ if saveData.IsValid() {
+ data = append(data, t...)
+ }
+
+ case Comment:
+ if saveComment.IsValid() {
+ comment = append(comment, t...)
+ }
+ }
+ }
+
+ var err os.Error
+ // Helper functions for integer and unsigned integer conversions
+ var itmp int64
+ getInt64 := func() bool {
+ itmp, err = strconv.Atoi64(string(data))
+ // TODO: should check sizes
+ return err == nil
+ }
+ var utmp uint64
+ getUint64 := func() bool {
+ utmp, err = strconv.Atoui64(string(data))
+ // TODO: check for overflow?
+ return err == nil
+ }
+ var ftmp float64
+ getFloat64 := func() bool {
+ ftmp, err = strconv.Atof64(string(data))
+ // TODO: check for overflow?
+ return err == nil
+ }
+
+ // Save accumulated data and comments
+ switch t := saveData; t.Kind() {
+ case reflect.Invalid:
+ // Probably a comment, handled below
+ default:
+ return os.ErrorString("cannot happen: unknown type " + t.Type().String())
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ if !getInt64() {
+ return err
+ }
+ t.SetInt(itmp)
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ if !getUint64() {
+ return err
+ }
+ t.SetUint(utmp)
+ case reflect.Float32, reflect.Float64:
+ if !getFloat64() {
+ return err
+ }
+ t.SetFloat(ftmp)
+ case reflect.Bool:
+ value, err := strconv.Atob(strings.TrimSpace(string(data)))
+ if err != nil {
+ return err
+ }
+ t.SetBool(value)
+ case reflect.String:
+ t.SetString(string(data))
+ case reflect.Slice:
+ t.Set(reflect.NewValue(data))
+ }
+
+ switch t := saveComment; t.Kind() {
+ case reflect.String:
+ t.SetString(string(comment))
+ case reflect.Slice:
+ t.Set(reflect.NewValue(comment))
+ }
+
+ switch t := saveXML; t.Kind() {
+ case reflect.String:
+ t.SetString(string(saveXMLData))
+ case reflect.Slice:
+ t.Set(reflect.NewValue(saveXMLData))
+ }
+
+ return nil
+}
+
+type pathInfo struct {
+ fieldIdx []int
+ complete bool
+}
+
+// addFieldPath takes an element path such as "a>b>c" and fills the
+// paths map with all paths leading to it ("a", "a>b", and "a>b>c").
+// It is okay for paths to share a common, shorter prefix but not ok
+// for one path to itself be a prefix of another.
+func addFieldPath(sv reflect.Value, paths map[string]pathInfo, path string, fieldIdx []int) os.Error {
+ if info, found := paths[path]; found {
+ return tagError(sv, info.fieldIdx, fieldIdx)
+ }
+ paths[path] = pathInfo{fieldIdx, true}
+ for {
+ i := strings.LastIndex(path, ">")
+ if i < 0 {
+ break
+ }
+ path = path[:i]
+ if info, found := paths[path]; found {
+ if info.complete {
+ return tagError(sv, info.fieldIdx, fieldIdx)
+ }
+ } else {
+ paths[path] = pathInfo{fieldIdx, false}
+ }
+ }
+ return nil
+
+}
+
+func tagError(sv reflect.Value, idx1 []int, idx2 []int) os.Error {
+ t := sv.Type()
+ f1 := t.FieldByIndex(idx1)
+ f2 := t.FieldByIndex(idx2)
+ return &TagPathError{t, f1.Name, f1.Tag, f2.Name, f2.Tag}
+}
+
+// unmarshalPaths walks down an XML structure looking for
+// wanted paths, and calls unmarshal on them.
+func (p *Parser) unmarshalPaths(sv reflect.Value, paths map[string]pathInfo, path string, start *StartElement) os.Error {
+ if info, _ := paths[path]; info.complete {
+ return p.unmarshal(sv.FieldByIndex(info.fieldIdx), start)
+ }
+ for {
+ tok, err := p.Token()
+ if err != nil {
+ return err
+ }
+ switch t := tok.(type) {
+ case StartElement:
+ k := path + ">" + fieldName(t.Name.Local)
+ if _, found := paths[k]; found {
+ if err := p.unmarshalPaths(sv, paths, k, &t); err != nil {
+ return err
+ }
+ continue
+ }
+ if err := p.Skip(); err != nil {
+ return err
+ }
+ case EndElement:
+ return nil
+ }
+ }
+ panic("unreachable")
+}
+
+// Have already read a start element.
+// Read tokens until we find the end element.
+// Token is taking care of making sure the
+// end element matches the start element we saw.
+func (p *Parser) Skip() os.Error {
+ for {
+ tok, err := p.Token()
+ if err != nil {
+ return err
+ }
+ switch t := tok.(type) {
+ case StartElement:
+ if err := p.Skip(); err != nil {
+ return err
+ }
+ case EndElement:
+ return nil
+ }
+ }
+ panic("unreachable")
+}
diff --git a/src/cmd/gofix/testdata/reflect.scan.go.in b/src/cmd/gofix/testdata/reflect.scan.go.in
new file mode 100644
index 000000000..36271a8d4
--- /dev/null
+++ b/src/cmd/gofix/testdata/reflect.scan.go.in
@@ -0,0 +1,1084 @@
+// 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.
+
+package fmt
+
+import (
+ "bytes"
+ "io"
+ "math"
+ "os"
+ "reflect"
+ "strconv"
+ "strings"
+ "unicode"
+ "utf8"
+)
+
+// runeUnreader is the interface to something that can unread runes.
+// If the object provided to Scan does not satisfy this interface,
+// a local buffer will be used to back up the input, but its contents
+// will be lost when Scan returns.
+type runeUnreader interface {
+ UnreadRune() os.Error
+}
+
+// ScanState represents the scanner state passed to custom scanners.
+// Scanners may do rune-at-a-time scanning or ask the ScanState
+// to discover the next space-delimited token.
+type ScanState interface {
+ // ReadRune reads the next rune (Unicode code point) from the input.
+ // If invoked during Scanln, Fscanln, or Sscanln, ReadRune() will
+ // return EOF after returning the first '\n' or when reading beyond
+ // the specified width.
+ ReadRune() (rune int, size int, err os.Error)
+ // UnreadRune causes the next call to ReadRune to return the same rune.
+ UnreadRune() os.Error
+ // Token skips space in the input if skipSpace is true, then returns the
+ // run of Unicode code points c satisfying f(c). If f is nil,
+ // !unicode.IsSpace(c) is used; that is, the token will hold non-space
+ // characters. Newlines are treated as space unless the scan operation
+ // is Scanln, Fscanln or Sscanln, in which case a newline is treated as
+ // EOF. The returned slice points to shared data that may be overwritten
+ // by the next call to Token, a call to a Scan function using the ScanState
+ // as input, or when the calling Scan method returns.
+ Token(skipSpace bool, f func(int) bool) (token []byte, err os.Error)
+ // Width returns the value of the width option and whether it has been set.
+ // The unit is Unicode code points.
+ Width() (wid int, ok bool)
+ // Because ReadRune is implemented by the interface, Read should never be
+ // called by the scanning routines and a valid implementation of
+ // ScanState may choose always to return an error from Read.
+ Read(buf []byte) (n int, err os.Error)
+}
+
+// Scanner is implemented by any value that has a Scan method, which scans
+// the input for the representation of a value and stores the result in the
+// receiver, which must be a pointer to be useful. The Scan method is called
+// for any argument to Scan, Scanf, or Scanln that implements it.
+type Scanner interface {
+ Scan(state ScanState, verb int) os.Error
+}
+
+// Scan scans text read from standard input, storing successive
+// space-separated values into successive arguments. Newlines count
+// as space. It returns the number of items successfully scanned.
+// If that is less than the number of arguments, err will report why.
+func Scan(a ...interface{}) (n int, err os.Error) {
+ return Fscan(os.Stdin, a...)
+}
+
+// Scanln is similar to Scan, but stops scanning at a newline and
+// after the final item there must be a newline or EOF.
+func Scanln(a ...interface{}) (n int, err os.Error) {
+ return Fscanln(os.Stdin, a...)
+}
+
+// Scanf scans text read from standard input, storing successive
+// space-separated values into successive arguments as determined by
+// the format. It returns the number of items successfully scanned.
+func Scanf(format string, a ...interface{}) (n int, err os.Error) {
+ return Fscanf(os.Stdin, format, a...)
+}
+
+// Sscan scans the argument string, storing successive space-separated
+// values into successive arguments. Newlines count as space. It
+// returns the number of items successfully scanned. If that is less
+// than the number of arguments, err will report why.
+func Sscan(str string, a ...interface{}) (n int, err os.Error) {
+ return Fscan(strings.NewReader(str), a...)
+}
+
+// Sscanln is similar to Sscan, but stops scanning at a newline and
+// after the final item there must be a newline or EOF.
+func Sscanln(str string, a ...interface{}) (n int, err os.Error) {
+ return Fscanln(strings.NewReader(str), a...)
+}
+
+// Sscanf scans the argument string, storing successive space-separated
+// values into successive arguments as determined by the format. It
+// returns the number of items successfully parsed.
+func Sscanf(str string, format string, a ...interface{}) (n int, err os.Error) {
+ return Fscanf(strings.NewReader(str), format, a...)
+}
+
+// Fscan scans text read from r, storing successive space-separated
+// values into successive arguments. Newlines count as space. It
+// returns the number of items successfully scanned. If that is less
+// than the number of arguments, err will report why.
+func Fscan(r io.Reader, a ...interface{}) (n int, err os.Error) {
+ s, old := newScanState(r, true, false)
+ n, err = s.doScan(a)
+ s.free(old)
+ return
+}
+
+// Fscanln is similar to Fscan, but stops scanning at a newline and
+// after the final item there must be a newline or EOF.
+func Fscanln(r io.Reader, a ...interface{}) (n int, err os.Error) {
+ s, old := newScanState(r, false, true)
+ n, err = s.doScan(a)
+ s.free(old)
+ return
+}
+
+// Fscanf scans text read from r, storing successive space-separated
+// values into successive arguments as determined by the format. It
+// returns the number of items successfully parsed.
+func Fscanf(r io.Reader, format string, a ...interface{}) (n int, err os.Error) {
+ s, old := newScanState(r, false, false)
+ n, err = s.doScanf(format, a)
+ s.free(old)
+ return
+}
+
+// scanError represents an error generated by the scanning software.
+// It's used as a unique signature to identify such errors when recovering.
+type scanError struct {
+ err os.Error
+}
+
+const eof = -1
+
+// ss is the internal implementation of ScanState.
+type ss struct {
+ rr io.RuneReader // where to read input
+ buf bytes.Buffer // token accumulator
+ peekRune int // one-rune lookahead
+ prevRune int // last rune returned by ReadRune
+ count int // runes consumed so far.
+ atEOF bool // already read EOF
+ ssave
+}
+
+// ssave holds the parts of ss that need to be
+// saved and restored on recursive scans.
+type ssave struct {
+ validSave bool // is or was a part of an actual ss.
+ nlIsEnd bool // whether newline terminates scan
+ nlIsSpace bool // whether newline counts as white space
+ fieldLimit int // max value of ss.count for this field; fieldLimit <= limit
+ limit int // max value of ss.count.
+ maxWid int // width of this field.
+}
+
+// The Read method is only in ScanState so that ScanState
+// satisfies io.Reader. It will never be called when used as
+// intended, so there is no need to make it actually work.
+func (s *ss) Read(buf []byte) (n int, err os.Error) {
+ return 0, os.ErrorString("ScanState's Read should not be called. Use ReadRune")
+}
+
+func (s *ss) ReadRune() (rune int, size int, err os.Error) {
+ if s.peekRune >= 0 {
+ s.count++
+ rune = s.peekRune
+ size = utf8.RuneLen(rune)
+ s.prevRune = rune
+ s.peekRune = -1
+ return
+ }
+ if s.atEOF || s.nlIsEnd && s.prevRune == '\n' || s.count >= s.fieldLimit {
+ err = os.EOF
+ return
+ }
+
+ rune, size, err = s.rr.ReadRune()
+ if err == nil {
+ s.count++
+ s.prevRune = rune
+ } else if err == os.EOF {
+ s.atEOF = true
+ }
+ return
+}
+
+func (s *ss) Width() (wid int, ok bool) {
+ if s.maxWid == hugeWid {
+ return 0, false
+ }
+ return s.maxWid, true
+}
+
+// The public method returns an error; this private one panics.
+// If getRune reaches EOF, the return value is EOF (-1).
+func (s *ss) getRune() (rune int) {
+ rune, _, err := s.ReadRune()
+ if err != nil {
+ if err == os.EOF {
+ return eof
+ }
+ s.error(err)
+ }
+ return
+}
+
+// mustReadRune turns os.EOF into a panic(io.ErrUnexpectedEOF).
+// It is called in cases such as string scanning where an EOF is a
+// syntax error.
+func (s *ss) mustReadRune() (rune int) {
+ rune = s.getRune()
+ if rune == eof {
+ s.error(io.ErrUnexpectedEOF)
+ }
+ return
+}
+
+func (s *ss) UnreadRune() os.Error {
+ if u, ok := s.rr.(runeUnreader); ok {
+ u.UnreadRune()
+ } else {
+ s.peekRune = s.prevRune
+ }
+ s.count--
+ return nil
+}
+
+func (s *ss) error(err os.Error) {
+ panic(scanError{err})
+}
+
+func (s *ss) errorString(err string) {
+ panic(scanError{os.ErrorString(err)})
+}
+
+func (s *ss) Token(skipSpace bool, f func(int) bool) (tok []byte, err os.Error) {
+ defer func() {
+ if e := recover(); e != nil {
+ if se, ok := e.(scanError); ok {
+ err = se.err
+ } else {
+ panic(e)
+ }
+ }
+ }()
+ if f == nil {
+ f = notSpace
+ }
+ s.buf.Reset()
+ tok = s.token(skipSpace, f)
+ return
+}
+
+// notSpace is the default scanning function used in Token.
+func notSpace(r int) bool {
+ return !unicode.IsSpace(r)
+}
+
+// readRune is a structure to enable reading UTF-8 encoded code points
+// from an io.Reader. It is used if the Reader given to the scanner does
+// not already implement io.RuneReader.
+type readRune struct {
+ reader io.Reader
+ buf [utf8.UTFMax]byte // used only inside ReadRune
+ pending int // number of bytes in pendBuf; only >0 for bad UTF-8
+ pendBuf [utf8.UTFMax]byte // bytes left over
+}
+
+// readByte returns the next byte from the input, which may be
+// left over from a previous read if the UTF-8 was ill-formed.
+func (r *readRune) readByte() (b byte, err os.Error) {
+ if r.pending > 0 {
+ b = r.pendBuf[0]
+ copy(r.pendBuf[0:], r.pendBuf[1:])
+ r.pending--
+ return
+ }
+ _, err = r.reader.Read(r.pendBuf[0:1])
+ return r.pendBuf[0], err
+}
+
+// unread saves the bytes for the next read.
+func (r *readRune) unread(buf []byte) {
+ copy(r.pendBuf[r.pending:], buf)
+ r.pending += len(buf)
+}
+
+// ReadRune returns the next UTF-8 encoded code point from the
+// io.Reader inside r.
+func (r *readRune) ReadRune() (rune int, size int, err os.Error) {
+ r.buf[0], err = r.readByte()
+ if err != nil {
+ return 0, 0, err
+ }
+ if r.buf[0] < utf8.RuneSelf { // fast check for common ASCII case
+ rune = int(r.buf[0])
+ return
+ }
+ var n int
+ for n = 1; !utf8.FullRune(r.buf[0:n]); n++ {
+ r.buf[n], err = r.readByte()
+ if err != nil {
+ if err == os.EOF {
+ err = nil
+ break
+ }
+ return
+ }
+ }
+ rune, size = utf8.DecodeRune(r.buf[0:n])
+ if size < n { // an error
+ r.unread(r.buf[size:n])
+ }
+ return
+}
+
+
+var ssFree = newCache(func() interface{} { return new(ss) })
+
+// Allocate a new ss struct or grab a cached one.
+func newScanState(r io.Reader, nlIsSpace, nlIsEnd bool) (s *ss, old ssave) {
+ // If the reader is a *ss, then we've got a recursive
+ // call to Scan, so re-use the scan state.
+ s, ok := r.(*ss)
+ if ok {
+ old = s.ssave
+ s.limit = s.fieldLimit
+ s.nlIsEnd = nlIsEnd || s.nlIsEnd
+ s.nlIsSpace = nlIsSpace
+ return
+ }
+
+ s = ssFree.get().(*ss)
+ if rr, ok := r.(io.RuneReader); ok {
+ s.rr = rr
+ } else {
+ s.rr = &readRune{reader: r}
+ }
+ s.nlIsSpace = nlIsSpace
+ s.nlIsEnd = nlIsEnd
+ s.prevRune = -1
+ s.peekRune = -1
+ s.atEOF = false
+ s.limit = hugeWid
+ s.fieldLimit = hugeWid
+ s.maxWid = hugeWid
+ s.validSave = true
+ return
+}
+
+// Save used ss structs in ssFree; avoid an allocation per invocation.
+func (s *ss) free(old ssave) {
+ // If it was used recursively, just restore the old state.
+ if old.validSave {
+ s.ssave = old
+ return
+ }
+ // Don't hold on to ss structs with large buffers.
+ if cap(s.buf.Bytes()) > 1024 {
+ return
+ }
+ s.buf.Reset()
+ s.rr = nil
+ ssFree.put(s)
+}
+
+// skipSpace skips spaces and maybe newlines.
+func (s *ss) skipSpace(stopAtNewline bool) {
+ for {
+ rune := s.getRune()
+ if rune == eof {
+ return
+ }
+ if rune == '\n' {
+ if stopAtNewline {
+ break
+ }
+ if s.nlIsSpace {
+ continue
+ }
+ s.errorString("unexpected newline")
+ return
+ }
+ if !unicode.IsSpace(rune) {
+ s.UnreadRune()
+ break
+ }
+ }
+}
+
+
+// token returns the next space-delimited string from the input. It
+// skips white space. For Scanln, it stops at newlines. For Scan,
+// newlines are treated as spaces.
+func (s *ss) token(skipSpace bool, f func(int) bool) []byte {
+ if skipSpace {
+ s.skipSpace(false)
+ }
+ // read until white space or newline
+ for {
+ rune := s.getRune()
+ if rune == eof {
+ break
+ }
+ if !f(rune) {
+ s.UnreadRune()
+ break
+ }
+ s.buf.WriteRune(rune)
+ }
+ return s.buf.Bytes()
+}
+
+// typeError indicates that the type of the operand did not match the format
+func (s *ss) typeError(field interface{}, expected string) {
+ s.errorString("expected field of type pointer to " + expected + "; found " + reflect.Typeof(field).String())
+}
+
+var complexError = os.ErrorString("syntax error scanning complex number")
+var boolError = os.ErrorString("syntax error scanning boolean")
+
+// consume reads the next rune in the input and reports whether it is in the ok string.
+// If accept is true, it puts the character into the input token.
+func (s *ss) consume(ok string, accept bool) bool {
+ rune := s.getRune()
+ if rune == eof {
+ return false
+ }
+ if strings.IndexRune(ok, rune) >= 0 {
+ if accept {
+ s.buf.WriteRune(rune)
+ }
+ return true
+ }
+ if rune != eof && accept {
+ s.UnreadRune()
+ }
+ return false
+}
+
+// peek reports whether the next character is in the ok string, without consuming it.
+func (s *ss) peek(ok string) bool {
+ rune := s.getRune()
+ if rune != eof {
+ s.UnreadRune()
+ }
+ return strings.IndexRune(ok, rune) >= 0
+}
+
+// accept checks the next rune in the input. If it's a byte (sic) in the string, it puts it in the
+// buffer and returns true. Otherwise it return false.
+func (s *ss) accept(ok string) bool {
+ return s.consume(ok, true)
+}
+
+// okVerb verifies that the verb is present in the list, setting s.err appropriately if not.
+func (s *ss) okVerb(verb int, okVerbs, typ string) bool {
+ for _, v := range okVerbs {
+ if v == verb {
+ return true
+ }
+ }
+ s.errorString("bad verb %" + string(verb) + " for " + typ)
+ return false
+}
+
+// scanBool returns the value of the boolean represented by the next token.
+func (s *ss) scanBool(verb int) bool {
+ if !s.okVerb(verb, "tv", "boolean") {
+ return false
+ }
+ // Syntax-checking a boolean is annoying. We're not fastidious about case.
+ switch s.mustReadRune() {
+ case '0':
+ return false
+ case '1':
+ return true
+ case 't', 'T':
+ if s.accept("rR") && (!s.accept("uU") || !s.accept("eE")) {
+ s.error(boolError)
+ }
+ return true
+ case 'f', 'F':
+ if s.accept("aL") && (!s.accept("lL") || !s.accept("sS") || !s.accept("eE")) {
+ s.error(boolError)
+ }
+ return false
+ }
+ return false
+}
+
+// Numerical elements
+const (
+ binaryDigits = "01"
+ octalDigits = "01234567"
+ decimalDigits = "0123456789"
+ hexadecimalDigits = "0123456789aAbBcCdDeEfF"
+ sign = "+-"
+ period = "."
+ exponent = "eEp"
+)
+
+// getBase returns the numeric base represented by the verb and its digit string.
+func (s *ss) getBase(verb int) (base int, digits string) {
+ s.okVerb(verb, "bdoUxXv", "integer") // sets s.err
+ base = 10
+ digits = decimalDigits
+ switch verb {
+ case 'b':
+ base = 2
+ digits = binaryDigits
+ case 'o':
+ base = 8
+ digits = octalDigits
+ case 'x', 'X', 'U':
+ base = 16
+ digits = hexadecimalDigits
+ }
+ return
+}
+
+// scanNumber returns the numerical string with specified digits starting here.
+func (s *ss) scanNumber(digits string, haveDigits bool) string {
+ if !haveDigits && !s.accept(digits) {
+ s.errorString("expected integer")
+ }
+ for s.accept(digits) {
+ }
+ return s.buf.String()
+}
+
+// scanRune returns the next rune value in the input.
+func (s *ss) scanRune(bitSize int) int64 {
+ rune := int64(s.mustReadRune())
+ n := uint(bitSize)
+ x := (rune << (64 - n)) >> (64 - n)
+ if x != rune {
+ s.errorString("overflow on character value " + string(rune))
+ }
+ return rune
+}
+
+// scanBasePrefix reports whether the integer begins with a 0 or 0x,
+// and returns the base, digit string, and whether a zero was found.
+// It is called only if the verb is %v.
+func (s *ss) scanBasePrefix() (base int, digits string, found bool) {
+ if !s.peek("0") {
+ return 10, decimalDigits, false
+ }
+ s.accept("0")
+ found = true // We've put a digit into the token buffer.
+ // Special cases for '0' && '0x'
+ base, digits = 8, octalDigits
+ if s.peek("xX") {
+ s.consume("xX", false)
+ base, digits = 16, hexadecimalDigits
+ }
+ return
+}
+
+// scanInt returns the value of the integer represented by the next
+// token, checking for overflow. Any error is stored in s.err.
+func (s *ss) scanInt(verb int, bitSize int) int64 {
+ if verb == 'c' {
+ return s.scanRune(bitSize)
+ }
+ s.skipSpace(false)
+ base, digits := s.getBase(verb)
+ haveDigits := false
+ if verb == 'U' {
+ if !s.consume("U", false) || !s.consume("+", false) {
+ s.errorString("bad unicode format ")
+ }
+ } else {
+ s.accept(sign) // If there's a sign, it will be left in the token buffer.
+ if verb == 'v' {
+ base, digits, haveDigits = s.scanBasePrefix()
+ }
+ }
+ tok := s.scanNumber(digits, haveDigits)
+ i, err := strconv.Btoi64(tok, base)
+ if err != nil {
+ s.error(err)
+ }
+ n := uint(bitSize)
+ x := (i << (64 - n)) >> (64 - n)
+ if x != i {
+ s.errorString("integer overflow on token " + tok)
+ }
+ return i
+}
+
+// scanUint returns the value of the unsigned integer represented
+// by the next token, checking for overflow. Any error is stored in s.err.
+func (s *ss) scanUint(verb int, bitSize int) uint64 {
+ if verb == 'c' {
+ return uint64(s.scanRune(bitSize))
+ }
+ s.skipSpace(false)
+ base, digits := s.getBase(verb)
+ haveDigits := false
+ if verb == 'U' {
+ if !s.consume("U", false) || !s.consume("+", false) {
+ s.errorString("bad unicode format ")
+ }
+ } else if verb == 'v' {
+ base, digits, haveDigits = s.scanBasePrefix()
+ }
+ tok := s.scanNumber(digits, haveDigits)
+ i, err := strconv.Btoui64(tok, base)
+ if err != nil {
+ s.error(err)
+ }
+ n := uint(bitSize)
+ x := (i << (64 - n)) >> (64 - n)
+ if x != i {
+ s.errorString("unsigned integer overflow on token " + tok)
+ }
+ return i
+}
+
+// floatToken returns the floating-point number starting here, no longer than swid
+// if the width is specified. It's not rigorous about syntax because it doesn't check that
+// we have at least some digits, but Atof will do that.
+func (s *ss) floatToken() string {
+ s.buf.Reset()
+ // NaN?
+ if s.accept("nN") && s.accept("aA") && s.accept("nN") {
+ return s.buf.String()
+ }
+ // leading sign?
+ s.accept(sign)
+ // Inf?
+ if s.accept("iI") && s.accept("nN") && s.accept("fF") {
+ return s.buf.String()
+ }
+ // digits?
+ for s.accept(decimalDigits) {
+ }
+ // decimal point?
+ if s.accept(period) {
+ // fraction?
+ for s.accept(decimalDigits) {
+ }
+ }
+ // exponent?
+ if s.accept(exponent) {
+ // leading sign?
+ s.accept(sign)
+ // digits?
+ for s.accept(decimalDigits) {
+ }
+ }
+ return s.buf.String()
+}
+
+// complexTokens returns the real and imaginary parts of the complex number starting here.
+// The number might be parenthesized and has the format (N+Ni) where N is a floating-point
+// number and there are no spaces within.
+func (s *ss) complexTokens() (real, imag string) {
+ // TODO: accept N and Ni independently?
+ parens := s.accept("(")
+ real = s.floatToken()
+ s.buf.Reset()
+ // Must now have a sign.
+ if !s.accept("+-") {
+ s.error(complexError)
+ }
+ // Sign is now in buffer
+ imagSign := s.buf.String()
+ imag = s.floatToken()
+ if !s.accept("i") {
+ s.error(complexError)
+ }
+ if parens && !s.accept(")") {
+ s.error(complexError)
+ }
+ return real, imagSign + imag
+}
+
+// convertFloat converts the string to a float64value.
+func (s *ss) convertFloat(str string, n int) float64 {
+ if p := strings.Index(str, "p"); p >= 0 {
+ // Atof doesn't handle power-of-2 exponents,
+ // but they're easy to evaluate.
+ f, err := strconv.AtofN(str[:p], n)
+ if err != nil {
+ // Put full string into error.
+ if e, ok := err.(*strconv.NumError); ok {
+ e.Num = str
+ }
+ s.error(err)
+ }
+ n, err := strconv.Atoi(str[p+1:])
+ if err != nil {
+ // Put full string into error.
+ if e, ok := err.(*strconv.NumError); ok {
+ e.Num = str
+ }
+ s.error(err)
+ }
+ return math.Ldexp(f, n)
+ }
+ f, err := strconv.AtofN(str, n)
+ if err != nil {
+ s.error(err)
+ }
+ return f
+}
+
+// convertComplex converts the next token to a complex128 value.
+// The atof argument is a type-specific reader for the underlying type.
+// If we're reading complex64, atof will parse float32s and convert them
+// to float64's to avoid reproducing this code for each complex type.
+func (s *ss) scanComplex(verb int, n int) complex128 {
+ if !s.okVerb(verb, floatVerbs, "complex") {
+ return 0
+ }
+ s.skipSpace(false)
+ sreal, simag := s.complexTokens()
+ real := s.convertFloat(sreal, n/2)
+ imag := s.convertFloat(simag, n/2)
+ return complex(real, imag)
+}
+
+// convertString returns the string represented by the next input characters.
+// The format of the input is determined by the verb.
+func (s *ss) convertString(verb int) (str string) {
+ if !s.okVerb(verb, "svqx", "string") {
+ return ""
+ }
+ s.skipSpace(false)
+ switch verb {
+ case 'q':
+ str = s.quotedString()
+ case 'x':
+ str = s.hexString()
+ default:
+ str = string(s.token(true, notSpace)) // %s and %v just return the next word
+ }
+ // Empty strings other than with %q are not OK.
+ if len(str) == 0 && verb != 'q' && s.maxWid > 0 {
+ s.errorString("Scan: no data for string")
+ }
+ return
+}
+
+// quotedString returns the double- or back-quoted string represented by the next input characters.
+func (s *ss) quotedString() string {
+ quote := s.mustReadRune()
+ switch quote {
+ case '`':
+ // Back-quoted: Anything goes until EOF or back quote.
+ for {
+ rune := s.mustReadRune()
+ if rune == quote {
+ break
+ }
+ s.buf.WriteRune(rune)
+ }
+ return s.buf.String()
+ case '"':
+ // Double-quoted: Include the quotes and let strconv.Unquote do the backslash escapes.
+ s.buf.WriteRune(quote)
+ for {
+ rune := s.mustReadRune()
+ s.buf.WriteRune(rune)
+ if rune == '\\' {
+ // In a legal backslash escape, no matter how long, only the character
+ // immediately after the escape can itself be a backslash or quote.
+ // Thus we only need to protect the first character after the backslash.
+ rune := s.mustReadRune()
+ s.buf.WriteRune(rune)
+ } else if rune == '"' {
+ break
+ }
+ }
+ result, err := strconv.Unquote(s.buf.String())
+ if err != nil {
+ s.error(err)
+ }
+ return result
+ default:
+ s.errorString("expected quoted string")
+ }
+ return ""
+}
+
+// hexDigit returns the value of the hexadecimal digit
+func (s *ss) hexDigit(digit int) int {
+ switch digit {
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ return digit - '0'
+ case 'a', 'b', 'c', 'd', 'e', 'f':
+ return 10 + digit - 'a'
+ case 'A', 'B', 'C', 'D', 'E', 'F':
+ return 10 + digit - 'A'
+ }
+ s.errorString("Scan: illegal hex digit")
+ return 0
+}
+
+// hexByte returns the next hex-encoded (two-character) byte from the input.
+// There must be either two hexadecimal digits or a space character in the input.
+func (s *ss) hexByte() (b byte, ok bool) {
+ rune1 := s.getRune()
+ if rune1 == eof {
+ return
+ }
+ if unicode.IsSpace(rune1) {
+ s.UnreadRune()
+ return
+ }
+ rune2 := s.mustReadRune()
+ return byte(s.hexDigit(rune1)<<4 | s.hexDigit(rune2)), true
+}
+
+// hexString returns the space-delimited hexpair-encoded string.
+func (s *ss) hexString() string {
+ for {
+ b, ok := s.hexByte()
+ if !ok {
+ break
+ }
+ s.buf.WriteByte(b)
+ }
+ if s.buf.Len() == 0 {
+ s.errorString("Scan: no hex data for %x string")
+ return ""
+ }
+ return s.buf.String()
+}
+
+const floatVerbs = "beEfFgGv"
+
+const hugeWid = 1 << 30
+
+// scanOne scans a single value, deriving the scanner from the type of the argument.
+func (s *ss) scanOne(verb int, field interface{}) {
+ s.buf.Reset()
+ var err os.Error
+ // If the parameter has its own Scan method, use that.
+ if v, ok := field.(Scanner); ok {
+ err = v.Scan(s, verb)
+ if err != nil {
+ if err == os.EOF {
+ err = io.ErrUnexpectedEOF
+ }
+ s.error(err)
+ }
+ return
+ }
+ switch v := field.(type) {
+ case *bool:
+ *v = s.scanBool(verb)
+ case *complex64:
+ *v = complex64(s.scanComplex(verb, 64))
+ case *complex128:
+ *v = s.scanComplex(verb, 128)
+ case *int:
+ *v = int(s.scanInt(verb, intBits))
+ case *int8:
+ *v = int8(s.scanInt(verb, 8))
+ case *int16:
+ *v = int16(s.scanInt(verb, 16))
+ case *int32:
+ *v = int32(s.scanInt(verb, 32))
+ case *int64:
+ *v = s.scanInt(verb, 64)
+ case *uint:
+ *v = uint(s.scanUint(verb, intBits))
+ case *uint8:
+ *v = uint8(s.scanUint(verb, 8))
+ case *uint16:
+ *v = uint16(s.scanUint(verb, 16))
+ case *uint32:
+ *v = uint32(s.scanUint(verb, 32))
+ case *uint64:
+ *v = s.scanUint(verb, 64)
+ case *uintptr:
+ *v = uintptr(s.scanUint(verb, uintptrBits))
+ // Floats are tricky because you want to scan in the precision of the result, not
+ // scan in high precision and convert, in order to preserve the correct error condition.
+ case *float32:
+ if s.okVerb(verb, floatVerbs, "float32") {
+ s.skipSpace(false)
+ *v = float32(s.convertFloat(s.floatToken(), 32))
+ }
+ case *float64:
+ if s.okVerb(verb, floatVerbs, "float64") {
+ s.skipSpace(false)
+ *v = s.convertFloat(s.floatToken(), 64)
+ }
+ case *string:
+ *v = s.convertString(verb)
+ case *[]byte:
+ // We scan to string and convert so we get a copy of the data.
+ // If we scanned to bytes, the slice would point at the buffer.
+ *v = []byte(s.convertString(verb))
+ default:
+ val := reflect.NewValue(v)
+ ptr, ok := val.(*reflect.PtrValue)
+ if !ok {
+ s.errorString("Scan: type not a pointer: " + val.Type().String())
+ return
+ }
+ switch v := ptr.Elem().(type) {
+ case *reflect.BoolValue:
+ v.Set(s.scanBool(verb))
+ case *reflect.IntValue:
+ v.Set(s.scanInt(verb, v.Type().Bits()))
+ case *reflect.UintValue:
+ v.Set(s.scanUint(verb, v.Type().Bits()))
+ case *reflect.StringValue:
+ v.Set(s.convertString(verb))
+ case *reflect.SliceValue:
+ // For now, can only handle (renamed) []byte.
+ typ := v.Type().(*reflect.SliceType)
+ if typ.Elem().Kind() != reflect.Uint8 {
+ goto CantHandle
+ }
+ str := s.convertString(verb)
+ v.Set(reflect.MakeSlice(typ, len(str), len(str)))
+ for i := 0; i < len(str); i++ {
+ v.Elem(i).(*reflect.UintValue).Set(uint64(str[i]))
+ }
+ case *reflect.FloatValue:
+ s.skipSpace(false)
+ v.Set(s.convertFloat(s.floatToken(), v.Type().Bits()))
+ case *reflect.ComplexValue:
+ v.Set(s.scanComplex(verb, v.Type().Bits()))
+ default:
+ CantHandle:
+ s.errorString("Scan: can't handle type: " + val.Type().String())
+ }
+ }
+}
+
+// errorHandler turns local panics into error returns. EOFs are benign.
+func errorHandler(errp *os.Error) {
+ if e := recover(); e != nil {
+ if se, ok := e.(scanError); ok { // catch local error
+ if se.err != os.EOF {
+ *errp = se.err
+ }
+ } else {
+ panic(e)
+ }
+ }
+}
+
+// doScan does the real work for scanning without a format string.
+func (s *ss) doScan(a []interface{}) (numProcessed int, err os.Error) {
+ defer errorHandler(&err)
+ for _, field := range a {
+ s.scanOne('v', field)
+ numProcessed++
+ }
+ // Check for newline if required.
+ if !s.nlIsSpace {
+ for {
+ rune := s.getRune()
+ if rune == '\n' || rune == eof {
+ break
+ }
+ if !unicode.IsSpace(rune) {
+ s.errorString("Scan: expected newline")
+ break
+ }
+ }
+ }
+ return
+}
+
+// advance determines whether the next characters in the input match
+// those of the format. It returns the number of bytes (sic) consumed
+// in the format. Newlines included, all runs of space characters in
+// either input or format behave as a single space. This routine also
+// handles the %% case. If the return value is zero, either format
+// starts with a % (with no following %) or the input is empty.
+// If it is negative, the input did not match the string.
+func (s *ss) advance(format string) (i int) {
+ for i < len(format) {
+ fmtc, w := utf8.DecodeRuneInString(format[i:])
+ if fmtc == '%' {
+ // %% acts like a real percent
+ nextc, _ := utf8.DecodeRuneInString(format[i+w:]) // will not match % if string is empty
+ if nextc != '%' {
+ return
+ }
+ i += w // skip the first %
+ }
+ sawSpace := false
+ for unicode.IsSpace(fmtc) && i < len(format) {
+ sawSpace = true
+ i += w
+ fmtc, w = utf8.DecodeRuneInString(format[i:])
+ }
+ if sawSpace {
+ // There was space in the format, so there should be space (EOF)
+ // in the input.
+ inputc := s.getRune()
+ if inputc == eof {
+ return
+ }
+ if !unicode.IsSpace(inputc) {
+ // Space in format but not in input: error
+ s.errorString("expected space in input to match format")
+ }
+ s.skipSpace(true)
+ continue
+ }
+ inputc := s.mustReadRune()
+ if fmtc != inputc {
+ s.UnreadRune()
+ return -1
+ }
+ i += w
+ }
+ return
+}
+
+// doScanf does the real work when scanning with a format string.
+// At the moment, it handles only pointers to basic types.
+func (s *ss) doScanf(format string, a []interface{}) (numProcessed int, err os.Error) {
+ defer errorHandler(&err)
+ end := len(format) - 1
+ // We process one item per non-trivial format
+ for i := 0; i <= end; {
+ w := s.advance(format[i:])
+ if w > 0 {
+ i += w
+ continue
+ }
+ // Either we failed to advance, we have a percent character, or we ran out of input.
+ if format[i] != '%' {
+ // Can't advance format. Why not?
+ if w < 0 {
+ s.errorString("input does not match format")
+ }
+ // Otherwise at EOF; "too many operands" error handled below
+ break
+ }
+ i++ // % is one byte
+
+ // do we have 20 (width)?
+ var widPresent bool
+ s.maxWid, widPresent, i = parsenum(format, i, end)
+ if !widPresent {
+ s.maxWid = hugeWid
+ }
+ s.fieldLimit = s.limit
+ if f := s.count + s.maxWid; f < s.fieldLimit {
+ s.fieldLimit = f
+ }
+
+ c, w := utf8.DecodeRuneInString(format[i:])
+ i += w
+
+ if numProcessed >= len(a) { // out of operands
+ s.errorString("too few operands for format %" + format[i-w:])
+ break
+ }
+ field := a[numProcessed]
+
+ s.scanOne(c, field)
+ numProcessed++
+ s.fieldLimit = s.limit
+ }
+ if numProcessed < len(a) {
+ s.errorString("too many operands")
+ }
+ return
+}
diff --git a/src/cmd/gofix/testdata/reflect.scan.go.out b/src/cmd/gofix/testdata/reflect.scan.go.out
new file mode 100644
index 000000000..b1b3975e2
--- /dev/null
+++ b/src/cmd/gofix/testdata/reflect.scan.go.out
@@ -0,0 +1,1084 @@
+// 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.
+
+package fmt
+
+import (
+ "bytes"
+ "io"
+ "math"
+ "os"
+ "reflect"
+ "strconv"
+ "strings"
+ "unicode"
+ "utf8"
+)
+
+// runeUnreader is the interface to something that can unread runes.
+// If the object provided to Scan does not satisfy this interface,
+// a local buffer will be used to back up the input, but its contents
+// will be lost when Scan returns.
+type runeUnreader interface {
+ UnreadRune() os.Error
+}
+
+// ScanState represents the scanner state passed to custom scanners.
+// Scanners may do rune-at-a-time scanning or ask the ScanState
+// to discover the next space-delimited token.
+type ScanState interface {
+ // ReadRune reads the next rune (Unicode code point) from the input.
+ // If invoked during Scanln, Fscanln, or Sscanln, ReadRune() will
+ // return EOF after returning the first '\n' or when reading beyond
+ // the specified width.
+ ReadRune() (rune int, size int, err os.Error)
+ // UnreadRune causes the next call to ReadRune to return the same rune.
+ UnreadRune() os.Error
+ // Token skips space in the input if skipSpace is true, then returns the
+ // run of Unicode code points c satisfying f(c). If f is nil,
+ // !unicode.IsSpace(c) is used; that is, the token will hold non-space
+ // characters. Newlines are treated as space unless the scan operation
+ // is Scanln, Fscanln or Sscanln, in which case a newline is treated as
+ // EOF. The returned slice points to shared data that may be overwritten
+ // by the next call to Token, a call to a Scan function using the ScanState
+ // as input, or when the calling Scan method returns.
+ Token(skipSpace bool, f func(int) bool) (token []byte, err os.Error)
+ // Width returns the value of the width option and whether it has been set.
+ // The unit is Unicode code points.
+ Width() (wid int, ok bool)
+ // Because ReadRune is implemented by the interface, Read should never be
+ // called by the scanning routines and a valid implementation of
+ // ScanState may choose always to return an error from Read.
+ Read(buf []byte) (n int, err os.Error)
+}
+
+// Scanner is implemented by any value that has a Scan method, which scans
+// the input for the representation of a value and stores the result in the
+// receiver, which must be a pointer to be useful. The Scan method is called
+// for any argument to Scan, Scanf, or Scanln that implements it.
+type Scanner interface {
+ Scan(state ScanState, verb int) os.Error
+}
+
+// Scan scans text read from standard input, storing successive
+// space-separated values into successive arguments. Newlines count
+// as space. It returns the number of items successfully scanned.
+// If that is less than the number of arguments, err will report why.
+func Scan(a ...interface{}) (n int, err os.Error) {
+ return Fscan(os.Stdin, a...)
+}
+
+// Scanln is similar to Scan, but stops scanning at a newline and
+// after the final item there must be a newline or EOF.
+func Scanln(a ...interface{}) (n int, err os.Error) {
+ return Fscanln(os.Stdin, a...)
+}
+
+// Scanf scans text read from standard input, storing successive
+// space-separated values into successive arguments as determined by
+// the format. It returns the number of items successfully scanned.
+func Scanf(format string, a ...interface{}) (n int, err os.Error) {
+ return Fscanf(os.Stdin, format, a...)
+}
+
+// Sscan scans the argument string, storing successive space-separated
+// values into successive arguments. Newlines count as space. It
+// returns the number of items successfully scanned. If that is less
+// than the number of arguments, err will report why.
+func Sscan(str string, a ...interface{}) (n int, err os.Error) {
+ return Fscan(strings.NewReader(str), a...)
+}
+
+// Sscanln is similar to Sscan, but stops scanning at a newline and
+// after the final item there must be a newline or EOF.
+func Sscanln(str string, a ...interface{}) (n int, err os.Error) {
+ return Fscanln(strings.NewReader(str), a...)
+}
+
+// Sscanf scans the argument string, storing successive space-separated
+// values into successive arguments as determined by the format. It
+// returns the number of items successfully parsed.
+func Sscanf(str string, format string, a ...interface{}) (n int, err os.Error) {
+ return Fscanf(strings.NewReader(str), format, a...)
+}
+
+// Fscan scans text read from r, storing successive space-separated
+// values into successive arguments. Newlines count as space. It
+// returns the number of items successfully scanned. If that is less
+// than the number of arguments, err will report why.
+func Fscan(r io.Reader, a ...interface{}) (n int, err os.Error) {
+ s, old := newScanState(r, true, false)
+ n, err = s.doScan(a)
+ s.free(old)
+ return
+}
+
+// Fscanln is similar to Fscan, but stops scanning at a newline and
+// after the final item there must be a newline or EOF.
+func Fscanln(r io.Reader, a ...interface{}) (n int, err os.Error) {
+ s, old := newScanState(r, false, true)
+ n, err = s.doScan(a)
+ s.free(old)
+ return
+}
+
+// Fscanf scans text read from r, storing successive space-separated
+// values into successive arguments as determined by the format. It
+// returns the number of items successfully parsed.
+func Fscanf(r io.Reader, format string, a ...interface{}) (n int, err os.Error) {
+ s, old := newScanState(r, false, false)
+ n, err = s.doScanf(format, a)
+ s.free(old)
+ return
+}
+
+// scanError represents an error generated by the scanning software.
+// It's used as a unique signature to identify such errors when recovering.
+type scanError struct {
+ err os.Error
+}
+
+const eof = -1
+
+// ss is the internal implementation of ScanState.
+type ss struct {
+ rr io.RuneReader // where to read input
+ buf bytes.Buffer // token accumulator
+ peekRune int // one-rune lookahead
+ prevRune int // last rune returned by ReadRune
+ count int // runes consumed so far.
+ atEOF bool // already read EOF
+ ssave
+}
+
+// ssave holds the parts of ss that need to be
+// saved and restored on recursive scans.
+type ssave struct {
+ validSave bool // is or was a part of an actual ss.
+ nlIsEnd bool // whether newline terminates scan
+ nlIsSpace bool // whether newline counts as white space
+ fieldLimit int // max value of ss.count for this field; fieldLimit <= limit
+ limit int // max value of ss.count.
+ maxWid int // width of this field.
+}
+
+// The Read method is only in ScanState so that ScanState
+// satisfies io.Reader. It will never be called when used as
+// intended, so there is no need to make it actually work.
+func (s *ss) Read(buf []byte) (n int, err os.Error) {
+ return 0, os.ErrorString("ScanState's Read should not be called. Use ReadRune")
+}
+
+func (s *ss) ReadRune() (rune int, size int, err os.Error) {
+ if s.peekRune >= 0 {
+ s.count++
+ rune = s.peekRune
+ size = utf8.RuneLen(rune)
+ s.prevRune = rune
+ s.peekRune = -1
+ return
+ }
+ if s.atEOF || s.nlIsEnd && s.prevRune == '\n' || s.count >= s.fieldLimit {
+ err = os.EOF
+ return
+ }
+
+ rune, size, err = s.rr.ReadRune()
+ if err == nil {
+ s.count++
+ s.prevRune = rune
+ } else if err == os.EOF {
+ s.atEOF = true
+ }
+ return
+}
+
+func (s *ss) Width() (wid int, ok bool) {
+ if s.maxWid == hugeWid {
+ return 0, false
+ }
+ return s.maxWid, true
+}
+
+// The public method returns an error; this private one panics.
+// If getRune reaches EOF, the return value is EOF (-1).
+func (s *ss) getRune() (rune int) {
+ rune, _, err := s.ReadRune()
+ if err != nil {
+ if err == os.EOF {
+ return eof
+ }
+ s.error(err)
+ }
+ return
+}
+
+// mustReadRune turns os.EOF into a panic(io.ErrUnexpectedEOF).
+// It is called in cases such as string scanning where an EOF is a
+// syntax error.
+func (s *ss) mustReadRune() (rune int) {
+ rune = s.getRune()
+ if rune == eof {
+ s.error(io.ErrUnexpectedEOF)
+ }
+ return
+}
+
+func (s *ss) UnreadRune() os.Error {
+ if u, ok := s.rr.(runeUnreader); ok {
+ u.UnreadRune()
+ } else {
+ s.peekRune = s.prevRune
+ }
+ s.count--
+ return nil
+}
+
+func (s *ss) error(err os.Error) {
+ panic(scanError{err})
+}
+
+func (s *ss) errorString(err string) {
+ panic(scanError{os.ErrorString(err)})
+}
+
+func (s *ss) Token(skipSpace bool, f func(int) bool) (tok []byte, err os.Error) {
+ defer func() {
+ if e := recover(); e != nil {
+ if se, ok := e.(scanError); ok {
+ err = se.err
+ } else {
+ panic(e)
+ }
+ }
+ }()
+ if f == nil {
+ f = notSpace
+ }
+ s.buf.Reset()
+ tok = s.token(skipSpace, f)
+ return
+}
+
+// notSpace is the default scanning function used in Token.
+func notSpace(r int) bool {
+ return !unicode.IsSpace(r)
+}
+
+// readRune is a structure to enable reading UTF-8 encoded code points
+// from an io.Reader. It is used if the Reader given to the scanner does
+// not already implement io.RuneReader.
+type readRune struct {
+ reader io.Reader
+ buf [utf8.UTFMax]byte // used only inside ReadRune
+ pending int // number of bytes in pendBuf; only >0 for bad UTF-8
+ pendBuf [utf8.UTFMax]byte // bytes left over
+}
+
+// readByte returns the next byte from the input, which may be
+// left over from a previous read if the UTF-8 was ill-formed.
+func (r *readRune) readByte() (b byte, err os.Error) {
+ if r.pending > 0 {
+ b = r.pendBuf[0]
+ copy(r.pendBuf[0:], r.pendBuf[1:])
+ r.pending--
+ return
+ }
+ _, err = r.reader.Read(r.pendBuf[0:1])
+ return r.pendBuf[0], err
+}
+
+// unread saves the bytes for the next read.
+func (r *readRune) unread(buf []byte) {
+ copy(r.pendBuf[r.pending:], buf)
+ r.pending += len(buf)
+}
+
+// ReadRune returns the next UTF-8 encoded code point from the
+// io.Reader inside r.
+func (r *readRune) ReadRune() (rune int, size int, err os.Error) {
+ r.buf[0], err = r.readByte()
+ if err != nil {
+ return 0, 0, err
+ }
+ if r.buf[0] < utf8.RuneSelf { // fast check for common ASCII case
+ rune = int(r.buf[0])
+ return
+ }
+ var n int
+ for n = 1; !utf8.FullRune(r.buf[0:n]); n++ {
+ r.buf[n], err = r.readByte()
+ if err != nil {
+ if err == os.EOF {
+ err = nil
+ break
+ }
+ return
+ }
+ }
+ rune, size = utf8.DecodeRune(r.buf[0:n])
+ if size < n { // an error
+ r.unread(r.buf[size:n])
+ }
+ return
+}
+
+
+var ssFree = newCache(func() interface{} { return new(ss) })
+
+// Allocate a new ss struct or grab a cached one.
+func newScanState(r io.Reader, nlIsSpace, nlIsEnd bool) (s *ss, old ssave) {
+ // If the reader is a *ss, then we've got a recursive
+ // call to Scan, so re-use the scan state.
+ s, ok := r.(*ss)
+ if ok {
+ old = s.ssave
+ s.limit = s.fieldLimit
+ s.nlIsEnd = nlIsEnd || s.nlIsEnd
+ s.nlIsSpace = nlIsSpace
+ return
+ }
+
+ s = ssFree.get().(*ss)
+ if rr, ok := r.(io.RuneReader); ok {
+ s.rr = rr
+ } else {
+ s.rr = &readRune{reader: r}
+ }
+ s.nlIsSpace = nlIsSpace
+ s.nlIsEnd = nlIsEnd
+ s.prevRune = -1
+ s.peekRune = -1
+ s.atEOF = false
+ s.limit = hugeWid
+ s.fieldLimit = hugeWid
+ s.maxWid = hugeWid
+ s.validSave = true
+ return
+}
+
+// Save used ss structs in ssFree; avoid an allocation per invocation.
+func (s *ss) free(old ssave) {
+ // If it was used recursively, just restore the old state.
+ if old.validSave {
+ s.ssave = old
+ return
+ }
+ // Don't hold on to ss structs with large buffers.
+ if cap(s.buf.Bytes()) > 1024 {
+ return
+ }
+ s.buf.Reset()
+ s.rr = nil
+ ssFree.put(s)
+}
+
+// skipSpace skips spaces and maybe newlines.
+func (s *ss) skipSpace(stopAtNewline bool) {
+ for {
+ rune := s.getRune()
+ if rune == eof {
+ return
+ }
+ if rune == '\n' {
+ if stopAtNewline {
+ break
+ }
+ if s.nlIsSpace {
+ continue
+ }
+ s.errorString("unexpected newline")
+ return
+ }
+ if !unicode.IsSpace(rune) {
+ s.UnreadRune()
+ break
+ }
+ }
+}
+
+
+// token returns the next space-delimited string from the input. It
+// skips white space. For Scanln, it stops at newlines. For Scan,
+// newlines are treated as spaces.
+func (s *ss) token(skipSpace bool, f func(int) bool) []byte {
+ if skipSpace {
+ s.skipSpace(false)
+ }
+ // read until white space or newline
+ for {
+ rune := s.getRune()
+ if rune == eof {
+ break
+ }
+ if !f(rune) {
+ s.UnreadRune()
+ break
+ }
+ s.buf.WriteRune(rune)
+ }
+ return s.buf.Bytes()
+}
+
+// typeError indicates that the type of the operand did not match the format
+func (s *ss) typeError(field interface{}, expected string) {
+ s.errorString("expected field of type pointer to " + expected + "; found " + reflect.Typeof(field).String())
+}
+
+var complexError = os.ErrorString("syntax error scanning complex number")
+var boolError = os.ErrorString("syntax error scanning boolean")
+
+// consume reads the next rune in the input and reports whether it is in the ok string.
+// If accept is true, it puts the character into the input token.
+func (s *ss) consume(ok string, accept bool) bool {
+ rune := s.getRune()
+ if rune == eof {
+ return false
+ }
+ if strings.IndexRune(ok, rune) >= 0 {
+ if accept {
+ s.buf.WriteRune(rune)
+ }
+ return true
+ }
+ if rune != eof && accept {
+ s.UnreadRune()
+ }
+ return false
+}
+
+// peek reports whether the next character is in the ok string, without consuming it.
+func (s *ss) peek(ok string) bool {
+ rune := s.getRune()
+ if rune != eof {
+ s.UnreadRune()
+ }
+ return strings.IndexRune(ok, rune) >= 0
+}
+
+// accept checks the next rune in the input. If it's a byte (sic) in the string, it puts it in the
+// buffer and returns true. Otherwise it return false.
+func (s *ss) accept(ok string) bool {
+ return s.consume(ok, true)
+}
+
+// okVerb verifies that the verb is present in the list, setting s.err appropriately if not.
+func (s *ss) okVerb(verb int, okVerbs, typ string) bool {
+ for _, v := range okVerbs {
+ if v == verb {
+ return true
+ }
+ }
+ s.errorString("bad verb %" + string(verb) + " for " + typ)
+ return false
+}
+
+// scanBool returns the value of the boolean represented by the next token.
+func (s *ss) scanBool(verb int) bool {
+ if !s.okVerb(verb, "tv", "boolean") {
+ return false
+ }
+ // Syntax-checking a boolean is annoying. We're not fastidious about case.
+ switch s.mustReadRune() {
+ case '0':
+ return false
+ case '1':
+ return true
+ case 't', 'T':
+ if s.accept("rR") && (!s.accept("uU") || !s.accept("eE")) {
+ s.error(boolError)
+ }
+ return true
+ case 'f', 'F':
+ if s.accept("aL") && (!s.accept("lL") || !s.accept("sS") || !s.accept("eE")) {
+ s.error(boolError)
+ }
+ return false
+ }
+ return false
+}
+
+// Numerical elements
+const (
+ binaryDigits = "01"
+ octalDigits = "01234567"
+ decimalDigits = "0123456789"
+ hexadecimalDigits = "0123456789aAbBcCdDeEfF"
+ sign = "+-"
+ period = "."
+ exponent = "eEp"
+)
+
+// getBase returns the numeric base represented by the verb and its digit string.
+func (s *ss) getBase(verb int) (base int, digits string) {
+ s.okVerb(verb, "bdoUxXv", "integer") // sets s.err
+ base = 10
+ digits = decimalDigits
+ switch verb {
+ case 'b':
+ base = 2
+ digits = binaryDigits
+ case 'o':
+ base = 8
+ digits = octalDigits
+ case 'x', 'X', 'U':
+ base = 16
+ digits = hexadecimalDigits
+ }
+ return
+}
+
+// scanNumber returns the numerical string with specified digits starting here.
+func (s *ss) scanNumber(digits string, haveDigits bool) string {
+ if !haveDigits && !s.accept(digits) {
+ s.errorString("expected integer")
+ }
+ for s.accept(digits) {
+ }
+ return s.buf.String()
+}
+
+// scanRune returns the next rune value in the input.
+func (s *ss) scanRune(bitSize int) int64 {
+ rune := int64(s.mustReadRune())
+ n := uint(bitSize)
+ x := (rune << (64 - n)) >> (64 - n)
+ if x != rune {
+ s.errorString("overflow on character value " + string(rune))
+ }
+ return rune
+}
+
+// scanBasePrefix reports whether the integer begins with a 0 or 0x,
+// and returns the base, digit string, and whether a zero was found.
+// It is called only if the verb is %v.
+func (s *ss) scanBasePrefix() (base int, digits string, found bool) {
+ if !s.peek("0") {
+ return 10, decimalDigits, false
+ }
+ s.accept("0")
+ found = true // We've put a digit into the token buffer.
+ // Special cases for '0' && '0x'
+ base, digits = 8, octalDigits
+ if s.peek("xX") {
+ s.consume("xX", false)
+ base, digits = 16, hexadecimalDigits
+ }
+ return
+}
+
+// scanInt returns the value of the integer represented by the next
+// token, checking for overflow. Any error is stored in s.err.
+func (s *ss) scanInt(verb int, bitSize int) int64 {
+ if verb == 'c' {
+ return s.scanRune(bitSize)
+ }
+ s.skipSpace(false)
+ base, digits := s.getBase(verb)
+ haveDigits := false
+ if verb == 'U' {
+ if !s.consume("U", false) || !s.consume("+", false) {
+ s.errorString("bad unicode format ")
+ }
+ } else {
+ s.accept(sign) // If there's a sign, it will be left in the token buffer.
+ if verb == 'v' {
+ base, digits, haveDigits = s.scanBasePrefix()
+ }
+ }
+ tok := s.scanNumber(digits, haveDigits)
+ i, err := strconv.Btoi64(tok, base)
+ if err != nil {
+ s.error(err)
+ }
+ n := uint(bitSize)
+ x := (i << (64 - n)) >> (64 - n)
+ if x != i {
+ s.errorString("integer overflow on token " + tok)
+ }
+ return i
+}
+
+// scanUint returns the value of the unsigned integer represented
+// by the next token, checking for overflow. Any error is stored in s.err.
+func (s *ss) scanUint(verb int, bitSize int) uint64 {
+ if verb == 'c' {
+ return uint64(s.scanRune(bitSize))
+ }
+ s.skipSpace(false)
+ base, digits := s.getBase(verb)
+ haveDigits := false
+ if verb == 'U' {
+ if !s.consume("U", false) || !s.consume("+", false) {
+ s.errorString("bad unicode format ")
+ }
+ } else if verb == 'v' {
+ base, digits, haveDigits = s.scanBasePrefix()
+ }
+ tok := s.scanNumber(digits, haveDigits)
+ i, err := strconv.Btoui64(tok, base)
+ if err != nil {
+ s.error(err)
+ }
+ n := uint(bitSize)
+ x := (i << (64 - n)) >> (64 - n)
+ if x != i {
+ s.errorString("unsigned integer overflow on token " + tok)
+ }
+ return i
+}
+
+// floatToken returns the floating-point number starting here, no longer than swid
+// if the width is specified. It's not rigorous about syntax because it doesn't check that
+// we have at least some digits, but Atof will do that.
+func (s *ss) floatToken() string {
+ s.buf.Reset()
+ // NaN?
+ if s.accept("nN") && s.accept("aA") && s.accept("nN") {
+ return s.buf.String()
+ }
+ // leading sign?
+ s.accept(sign)
+ // Inf?
+ if s.accept("iI") && s.accept("nN") && s.accept("fF") {
+ return s.buf.String()
+ }
+ // digits?
+ for s.accept(decimalDigits) {
+ }
+ // decimal point?
+ if s.accept(period) {
+ // fraction?
+ for s.accept(decimalDigits) {
+ }
+ }
+ // exponent?
+ if s.accept(exponent) {
+ // leading sign?
+ s.accept(sign)
+ // digits?
+ for s.accept(decimalDigits) {
+ }
+ }
+ return s.buf.String()
+}
+
+// complexTokens returns the real and imaginary parts of the complex number starting here.
+// The number might be parenthesized and has the format (N+Ni) where N is a floating-point
+// number and there are no spaces within.
+func (s *ss) complexTokens() (real, imag string) {
+ // TODO: accept N and Ni independently?
+ parens := s.accept("(")
+ real = s.floatToken()
+ s.buf.Reset()
+ // Must now have a sign.
+ if !s.accept("+-") {
+ s.error(complexError)
+ }
+ // Sign is now in buffer
+ imagSign := s.buf.String()
+ imag = s.floatToken()
+ if !s.accept("i") {
+ s.error(complexError)
+ }
+ if parens && !s.accept(")") {
+ s.error(complexError)
+ }
+ return real, imagSign + imag
+}
+
+// convertFloat converts the string to a float64value.
+func (s *ss) convertFloat(str string, n int) float64 {
+ if p := strings.Index(str, "p"); p >= 0 {
+ // Atof doesn't handle power-of-2 exponents,
+ // but they're easy to evaluate.
+ f, err := strconv.AtofN(str[:p], n)
+ if err != nil {
+ // Put full string into error.
+ if e, ok := err.(*strconv.NumError); ok {
+ e.Num = str
+ }
+ s.error(err)
+ }
+ n, err := strconv.Atoi(str[p+1:])
+ if err != nil {
+ // Put full string into error.
+ if e, ok := err.(*strconv.NumError); ok {
+ e.Num = str
+ }
+ s.error(err)
+ }
+ return math.Ldexp(f, n)
+ }
+ f, err := strconv.AtofN(str, n)
+ if err != nil {
+ s.error(err)
+ }
+ return f
+}
+
+// convertComplex converts the next token to a complex128 value.
+// The atof argument is a type-specific reader for the underlying type.
+// If we're reading complex64, atof will parse float32s and convert them
+// to float64's to avoid reproducing this code for each complex type.
+func (s *ss) scanComplex(verb int, n int) complex128 {
+ if !s.okVerb(verb, floatVerbs, "complex") {
+ return 0
+ }
+ s.skipSpace(false)
+ sreal, simag := s.complexTokens()
+ real := s.convertFloat(sreal, n/2)
+ imag := s.convertFloat(simag, n/2)
+ return complex(real, imag)
+}
+
+// convertString returns the string represented by the next input characters.
+// The format of the input is determined by the verb.
+func (s *ss) convertString(verb int) (str string) {
+ if !s.okVerb(verb, "svqx", "string") {
+ return ""
+ }
+ s.skipSpace(false)
+ switch verb {
+ case 'q':
+ str = s.quotedString()
+ case 'x':
+ str = s.hexString()
+ default:
+ str = string(s.token(true, notSpace)) // %s and %v just return the next word
+ }
+ // Empty strings other than with %q are not OK.
+ if len(str) == 0 && verb != 'q' && s.maxWid > 0 {
+ s.errorString("Scan: no data for string")
+ }
+ return
+}
+
+// quotedString returns the double- or back-quoted string represented by the next input characters.
+func (s *ss) quotedString() string {
+ quote := s.mustReadRune()
+ switch quote {
+ case '`':
+ // Back-quoted: Anything goes until EOF or back quote.
+ for {
+ rune := s.mustReadRune()
+ if rune == quote {
+ break
+ }
+ s.buf.WriteRune(rune)
+ }
+ return s.buf.String()
+ case '"':
+ // Double-quoted: Include the quotes and let strconv.Unquote do the backslash escapes.
+ s.buf.WriteRune(quote)
+ for {
+ rune := s.mustReadRune()
+ s.buf.WriteRune(rune)
+ if rune == '\\' {
+ // In a legal backslash escape, no matter how long, only the character
+ // immediately after the escape can itself be a backslash or quote.
+ // Thus we only need to protect the first character after the backslash.
+ rune := s.mustReadRune()
+ s.buf.WriteRune(rune)
+ } else if rune == '"' {
+ break
+ }
+ }
+ result, err := strconv.Unquote(s.buf.String())
+ if err != nil {
+ s.error(err)
+ }
+ return result
+ default:
+ s.errorString("expected quoted string")
+ }
+ return ""
+}
+
+// hexDigit returns the value of the hexadecimal digit
+func (s *ss) hexDigit(digit int) int {
+ switch digit {
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ return digit - '0'
+ case 'a', 'b', 'c', 'd', 'e', 'f':
+ return 10 + digit - 'a'
+ case 'A', 'B', 'C', 'D', 'E', 'F':
+ return 10 + digit - 'A'
+ }
+ s.errorString("Scan: illegal hex digit")
+ return 0
+}
+
+// hexByte returns the next hex-encoded (two-character) byte from the input.
+// There must be either two hexadecimal digits or a space character in the input.
+func (s *ss) hexByte() (b byte, ok bool) {
+ rune1 := s.getRune()
+ if rune1 == eof {
+ return
+ }
+ if unicode.IsSpace(rune1) {
+ s.UnreadRune()
+ return
+ }
+ rune2 := s.mustReadRune()
+ return byte(s.hexDigit(rune1)<<4 | s.hexDigit(rune2)), true
+}
+
+// hexString returns the space-delimited hexpair-encoded string.
+func (s *ss) hexString() string {
+ for {
+ b, ok := s.hexByte()
+ if !ok {
+ break
+ }
+ s.buf.WriteByte(b)
+ }
+ if s.buf.Len() == 0 {
+ s.errorString("Scan: no hex data for %x string")
+ return ""
+ }
+ return s.buf.String()
+}
+
+const floatVerbs = "beEfFgGv"
+
+const hugeWid = 1 << 30
+
+// scanOne scans a single value, deriving the scanner from the type of the argument.
+func (s *ss) scanOne(verb int, field interface{}) {
+ s.buf.Reset()
+ var err os.Error
+ // If the parameter has its own Scan method, use that.
+ if v, ok := field.(Scanner); ok {
+ err = v.Scan(s, verb)
+ if err != nil {
+ if err == os.EOF {
+ err = io.ErrUnexpectedEOF
+ }
+ s.error(err)
+ }
+ return
+ }
+ switch v := field.(type) {
+ case *bool:
+ *v = s.scanBool(verb)
+ case *complex64:
+ *v = complex64(s.scanComplex(verb, 64))
+ case *complex128:
+ *v = s.scanComplex(verb, 128)
+ case *int:
+ *v = int(s.scanInt(verb, intBits))
+ case *int8:
+ *v = int8(s.scanInt(verb, 8))
+ case *int16:
+ *v = int16(s.scanInt(verb, 16))
+ case *int32:
+ *v = int32(s.scanInt(verb, 32))
+ case *int64:
+ *v = s.scanInt(verb, 64)
+ case *uint:
+ *v = uint(s.scanUint(verb, intBits))
+ case *uint8:
+ *v = uint8(s.scanUint(verb, 8))
+ case *uint16:
+ *v = uint16(s.scanUint(verb, 16))
+ case *uint32:
+ *v = uint32(s.scanUint(verb, 32))
+ case *uint64:
+ *v = s.scanUint(verb, 64)
+ case *uintptr:
+ *v = uintptr(s.scanUint(verb, uintptrBits))
+ // Floats are tricky because you want to scan in the precision of the result, not
+ // scan in high precision and convert, in order to preserve the correct error condition.
+ case *float32:
+ if s.okVerb(verb, floatVerbs, "float32") {
+ s.skipSpace(false)
+ *v = float32(s.convertFloat(s.floatToken(), 32))
+ }
+ case *float64:
+ if s.okVerb(verb, floatVerbs, "float64") {
+ s.skipSpace(false)
+ *v = s.convertFloat(s.floatToken(), 64)
+ }
+ case *string:
+ *v = s.convertString(verb)
+ case *[]byte:
+ // We scan to string and convert so we get a copy of the data.
+ // If we scanned to bytes, the slice would point at the buffer.
+ *v = []byte(s.convertString(verb))
+ default:
+ val := reflect.NewValue(v)
+ ptr := val
+ if ptr.Kind() != reflect.Ptr {
+ s.errorString("Scan: type not a pointer: " + val.Type().String())
+ return
+ }
+ switch v := ptr.Elem(); v.Kind() {
+ case reflect.Bool:
+ v.SetBool(s.scanBool(verb))
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ v.SetInt(s.scanInt(verb, v.Type().Bits()))
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ v.SetUint(s.scanUint(verb, v.Type().Bits()))
+ case reflect.String:
+ v.SetString(s.convertString(verb))
+ case reflect.Slice:
+ // For now, can only handle (renamed) []byte.
+ typ := v.Type()
+ if typ.Elem().Kind() != reflect.Uint8 {
+ goto CantHandle
+ }
+ str := s.convertString(verb)
+ v.Set(reflect.MakeSlice(typ, len(str), len(str)))
+ for i := 0; i < len(str); i++ {
+ v.Index(i).SetUint(uint64(str[i]))
+ }
+ case reflect.Float32, reflect.Float64:
+ s.skipSpace(false)
+ v.SetFloat(s.convertFloat(s.floatToken(), v.Type().Bits()))
+ case reflect.Complex64, reflect.Complex128:
+ v.SetComplex(s.scanComplex(verb, v.Type().Bits()))
+ default:
+ CantHandle:
+ s.errorString("Scan: can't handle type: " + val.Type().String())
+ }
+ }
+}
+
+// errorHandler turns local panics into error returns. EOFs are benign.
+func errorHandler(errp *os.Error) {
+ if e := recover(); e != nil {
+ if se, ok := e.(scanError); ok { // catch local error
+ if se.err != os.EOF {
+ *errp = se.err
+ }
+ } else {
+ panic(e)
+ }
+ }
+}
+
+// doScan does the real work for scanning without a format string.
+func (s *ss) doScan(a []interface{}) (numProcessed int, err os.Error) {
+ defer errorHandler(&err)
+ for _, field := range a {
+ s.scanOne('v', field)
+ numProcessed++
+ }
+ // Check for newline if required.
+ if !s.nlIsSpace {
+ for {
+ rune := s.getRune()
+ if rune == '\n' || rune == eof {
+ break
+ }
+ if !unicode.IsSpace(rune) {
+ s.errorString("Scan: expected newline")
+ break
+ }
+ }
+ }
+ return
+}
+
+// advance determines whether the next characters in the input match
+// those of the format. It returns the number of bytes (sic) consumed
+// in the format. Newlines included, all runs of space characters in
+// either input or format behave as a single space. This routine also
+// handles the %% case. If the return value is zero, either format
+// starts with a % (with no following %) or the input is empty.
+// If it is negative, the input did not match the string.
+func (s *ss) advance(format string) (i int) {
+ for i < len(format) {
+ fmtc, w := utf8.DecodeRuneInString(format[i:])
+ if fmtc == '%' {
+ // %% acts like a real percent
+ nextc, _ := utf8.DecodeRuneInString(format[i+w:]) // will not match % if string is empty
+ if nextc != '%' {
+ return
+ }
+ i += w // skip the first %
+ }
+ sawSpace := false
+ for unicode.IsSpace(fmtc) && i < len(format) {
+ sawSpace = true
+ i += w
+ fmtc, w = utf8.DecodeRuneInString(format[i:])
+ }
+ if sawSpace {
+ // There was space in the format, so there should be space (EOF)
+ // in the input.
+ inputc := s.getRune()
+ if inputc == eof {
+ return
+ }
+ if !unicode.IsSpace(inputc) {
+ // Space in format but not in input: error
+ s.errorString("expected space in input to match format")
+ }
+ s.skipSpace(true)
+ continue
+ }
+ inputc := s.mustReadRune()
+ if fmtc != inputc {
+ s.UnreadRune()
+ return -1
+ }
+ i += w
+ }
+ return
+}
+
+// doScanf does the real work when scanning with a format string.
+// At the moment, it handles only pointers to basic types.
+func (s *ss) doScanf(format string, a []interface{}) (numProcessed int, err os.Error) {
+ defer errorHandler(&err)
+ end := len(format) - 1
+ // We process one item per non-trivial format
+ for i := 0; i <= end; {
+ w := s.advance(format[i:])
+ if w > 0 {
+ i += w
+ continue
+ }
+ // Either we failed to advance, we have a percent character, or we ran out of input.
+ if format[i] != '%' {
+ // Can't advance format. Why not?
+ if w < 0 {
+ s.errorString("input does not match format")
+ }
+ // Otherwise at EOF; "too many operands" error handled below
+ break
+ }
+ i++ // % is one byte
+
+ // do we have 20 (width)?
+ var widPresent bool
+ s.maxWid, widPresent, i = parsenum(format, i, end)
+ if !widPresent {
+ s.maxWid = hugeWid
+ }
+ s.fieldLimit = s.limit
+ if f := s.count + s.maxWid; f < s.fieldLimit {
+ s.fieldLimit = f
+ }
+
+ c, w := utf8.DecodeRuneInString(format[i:])
+ i += w
+
+ if numProcessed >= len(a) { // out of operands
+ s.errorString("too few operands for format %" + format[i-w:])
+ break
+ }
+ field := a[numProcessed]
+
+ s.scanOne(c, field)
+ numProcessed++
+ s.fieldLimit = s.limit
+ }
+ if numProcessed < len(a) {
+ s.errorString("too many operands")
+ }
+ return
+}
diff --git a/src/cmd/gofix/testdata/reflect.script.go.in b/src/cmd/gofix/testdata/reflect.script.go.in
new file mode 100644
index 000000000..b341b1f89
--- /dev/null
+++ b/src/cmd/gofix/testdata/reflect.script.go.in
@@ -0,0 +1,359 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This package aids in the testing of code that uses channels.
+package script
+
+import (
+ "fmt"
+ "os"
+ "rand"
+ "reflect"
+ "strings"
+)
+
+// An Event is an element in a partially ordered set that either sends a value
+// to a channel or expects a value from a channel.
+type Event struct {
+ name string
+ occurred bool
+ predecessors []*Event
+ action action
+}
+
+type action interface {
+ // getSend returns nil if the action is not a send action.
+ getSend() sendAction
+ // getRecv returns nil if the action is not a receive action.
+ getRecv() recvAction
+ // getChannel returns the channel that the action operates on.
+ getChannel() interface{}
+}
+
+type recvAction interface {
+ recvMatch(interface{}) bool
+}
+
+type sendAction interface {
+ send()
+}
+
+// isReady returns true if all the predecessors of an Event have occurred.
+func (e Event) isReady() bool {
+ for _, predecessor := range e.predecessors {
+ if !predecessor.occurred {
+ return false
+ }
+ }
+
+ return true
+}
+
+// A Recv action reads a value from a channel and uses reflect.DeepMatch to
+// compare it with an expected value.
+type Recv struct {
+ Channel interface{}
+ Expected interface{}
+}
+
+func (r Recv) getRecv() recvAction { return r }
+
+func (Recv) getSend() sendAction { return nil }
+
+func (r Recv) getChannel() interface{} { return r.Channel }
+
+func (r Recv) recvMatch(chanEvent interface{}) bool {
+ c, ok := chanEvent.(channelRecv)
+ if !ok || c.channel != r.Channel {
+ return false
+ }
+
+ return reflect.DeepEqual(c.value, r.Expected)
+}
+
+// A RecvMatch action reads a value from a channel and calls a function to
+// determine if the value matches.
+type RecvMatch struct {
+ Channel interface{}
+ Match func(interface{}) bool
+}
+
+func (r RecvMatch) getRecv() recvAction { return r }
+
+func (RecvMatch) getSend() sendAction { return nil }
+
+func (r RecvMatch) getChannel() interface{} { return r.Channel }
+
+func (r RecvMatch) recvMatch(chanEvent interface{}) bool {
+ c, ok := chanEvent.(channelRecv)
+ if !ok || c.channel != r.Channel {
+ return false
+ }
+
+ return r.Match(c.value)
+}
+
+// A Closed action matches if the given channel is closed. The closing is
+// treated as an event, not a state, thus Closed will only match once for a
+// given channel.
+type Closed struct {
+ Channel interface{}
+}
+
+func (r Closed) getRecv() recvAction { return r }
+
+func (Closed) getSend() sendAction { return nil }
+
+func (r Closed) getChannel() interface{} { return r.Channel }
+
+func (r Closed) recvMatch(chanEvent interface{}) bool {
+ c, ok := chanEvent.(channelClosed)
+ if !ok || c.channel != r.Channel {
+ return false
+ }
+
+ return true
+}
+
+// A Send action sends a value to a channel. The value must match the
+// type of the channel exactly unless the channel if of type chan interface{}.
+type Send struct {
+ Channel interface{}
+ Value interface{}
+}
+
+func (Send) getRecv() recvAction { return nil }
+
+func (s Send) getSend() sendAction { return s }
+
+func (s Send) getChannel() interface{} { return s.Channel }
+
+type empty struct {
+ x interface{}
+}
+
+func newEmptyInterface(e empty) reflect.Value {
+ return reflect.NewValue(e).(*reflect.StructValue).Field(0)
+}
+
+func (s Send) send() {
+ // With reflect.ChanValue.Send, we must match the types exactly. So, if
+ // s.Channel is a chan interface{} we convert s.Value to an interface{}
+ // first.
+ c := reflect.NewValue(s.Channel).(*reflect.ChanValue)
+ var v reflect.Value
+ if iface, ok := c.Type().(*reflect.ChanType).Elem().(*reflect.InterfaceType); ok && iface.NumMethod() == 0 {
+ v = newEmptyInterface(empty{s.Value})
+ } else {
+ v = reflect.NewValue(s.Value)
+ }
+ c.Send(v)
+}
+
+// A Close action closes the given channel.
+type Close struct {
+ Channel interface{}
+}
+
+func (Close) getRecv() recvAction { return nil }
+
+func (s Close) getSend() sendAction { return s }
+
+func (s Close) getChannel() interface{} { return s.Channel }
+
+func (s Close) send() { reflect.NewValue(s.Channel).(*reflect.ChanValue).Close() }
+
+// A ReceivedUnexpected error results if no active Events match a value
+// received from a channel.
+type ReceivedUnexpected struct {
+ Value interface{}
+ ready []*Event
+}
+
+func (r ReceivedUnexpected) String() string {
+ names := make([]string, len(r.ready))
+ for i, v := range r.ready {
+ names[i] = v.name
+ }
+ return fmt.Sprintf("received unexpected value on one of the channels: %#v. Runnable events: %s", r.Value, strings.Join(names, ", "))
+}
+
+// A SetupError results if there is a error with the configuration of a set of
+// Events.
+type SetupError string
+
+func (s SetupError) String() string { return string(s) }
+
+func NewEvent(name string, predecessors []*Event, action action) *Event {
+ e := &Event{name, false, predecessors, action}
+ return e
+}
+
+// Given a set of Events, Perform repeatedly iterates over the set and finds the
+// subset of ready Events (that is, all of their predecessors have
+// occurred). From that subset, it pseudo-randomly selects an Event to perform.
+// If the Event is a send event, the send occurs and Perform recalculates the ready
+// set. If the event is a receive event, Perform waits for a value from any of the
+// channels that are contained in any of the events. That value is then matched
+// against the ready events. The first event that matches is considered to
+// have occurred and Perform recalculates the ready set.
+//
+// Perform continues this until all Events have occurred.
+//
+// Note that uncollected goroutines may still be reading from any of the
+// channels read from after Perform returns.
+//
+// For example, consider the problem of testing a function that reads values on
+// one channel and echos them to two output channels. To test this we would
+// create three events: a send event and two receive events. Each of the
+// receive events must list the send event as a predecessor but there is no
+// ordering between the receive events.
+//
+// send := NewEvent("send", nil, Send{c, 1})
+// recv1 := NewEvent("recv 1", []*Event{send}, Recv{c, 1})
+// recv2 := NewEvent("recv 2", []*Event{send}, Recv{c, 1})
+// Perform(0, []*Event{send, recv1, recv2})
+//
+// At first, only the send event would be in the ready set and thus Perform will
+// send a value to the input channel. Now the two receive events are ready and
+// Perform will match each of them against the values read from the output channels.
+//
+// It would be invalid to list one of the receive events as a predecessor of
+// the other. At each receive step, all the receive channels are considered,
+// thus Perform may see a value from a channel that is not in the current ready
+// set and fail.
+func Perform(seed int64, events []*Event) (err os.Error) {
+ r := rand.New(rand.NewSource(seed))
+
+ channels, err := getChannels(events)
+ if err != nil {
+ return
+ }
+ multiplex := make(chan interface{})
+ for _, channel := range channels {
+ go recvValues(multiplex, channel)
+ }
+
+Outer:
+ for {
+ ready, err := readyEvents(events)
+ if err != nil {
+ return err
+ }
+
+ if len(ready) == 0 {
+ // All events occurred.
+ break
+ }
+
+ event := ready[r.Intn(len(ready))]
+ if send := event.action.getSend(); send != nil {
+ send.send()
+ event.occurred = true
+ continue
+ }
+
+ v := <-multiplex
+ for _, event := range ready {
+ if recv := event.action.getRecv(); recv != nil && recv.recvMatch(v) {
+ event.occurred = true
+ continue Outer
+ }
+ }
+
+ return ReceivedUnexpected{v, ready}
+ }
+
+ return nil
+}
+
+// getChannels returns all the channels listed in any receive events.
+func getChannels(events []*Event) ([]interface{}, os.Error) {
+ channels := make([]interface{}, len(events))
+
+ j := 0
+ for _, event := range events {
+ if recv := event.action.getRecv(); recv == nil {
+ continue
+ }
+ c := event.action.getChannel()
+ if _, ok := reflect.NewValue(c).(*reflect.ChanValue); !ok {
+ return nil, SetupError("one of the channel values is not a channel")
+ }
+
+ duplicate := false
+ for _, other := range channels[0:j] {
+ if c == other {
+ duplicate = true
+ break
+ }
+ }
+
+ if !duplicate {
+ channels[j] = c
+ j++
+ }
+ }
+
+ return channels[0:j], nil
+}
+
+// recvValues is a multiplexing helper function. It reads values from the given
+// channel repeatedly, wrapping them up as either a channelRecv or
+// channelClosed structure, and forwards them to the multiplex channel.
+func recvValues(multiplex chan<- interface{}, channel interface{}) {
+ c := reflect.NewValue(channel).(*reflect.ChanValue)
+
+ for {
+ v, ok := c.Recv()
+ if !ok {
+ multiplex <- channelClosed{channel}
+ return
+ }
+
+ multiplex <- channelRecv{channel, v.Interface()}
+ }
+}
+
+type channelClosed struct {
+ channel interface{}
+}
+
+type channelRecv struct {
+ channel interface{}
+ value interface{}
+}
+
+// readyEvents returns the subset of events that are ready.
+func readyEvents(events []*Event) ([]*Event, os.Error) {
+ ready := make([]*Event, len(events))
+
+ j := 0
+ eventsWaiting := false
+ for _, event := range events {
+ if event.occurred {
+ continue
+ }
+
+ eventsWaiting = true
+ if event.isReady() {
+ ready[j] = event
+ j++
+ }
+ }
+
+ if j == 0 && eventsWaiting {
+ names := make([]string, len(events))
+ for _, event := range events {
+ if event.occurred {
+ continue
+ }
+ names[j] = event.name
+ }
+
+ return nil, SetupError("dependency cycle in events. These events are waiting to run but cannot: " + strings.Join(names, ", "))
+ }
+
+ return ready[0:j], nil
+}
diff --git a/src/cmd/gofix/testdata/reflect.script.go.out b/src/cmd/gofix/testdata/reflect.script.go.out
new file mode 100644
index 000000000..b18018497
--- /dev/null
+++ b/src/cmd/gofix/testdata/reflect.script.go.out
@@ -0,0 +1,359 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This package aids in the testing of code that uses channels.
+package script
+
+import (
+ "fmt"
+ "os"
+ "rand"
+ "reflect"
+ "strings"
+)
+
+// An Event is an element in a partially ordered set that either sends a value
+// to a channel or expects a value from a channel.
+type Event struct {
+ name string
+ occurred bool
+ predecessors []*Event
+ action action
+}
+
+type action interface {
+ // getSend returns nil if the action is not a send action.
+ getSend() sendAction
+ // getRecv returns nil if the action is not a receive action.
+ getRecv() recvAction
+ // getChannel returns the channel that the action operates on.
+ getChannel() interface{}
+}
+
+type recvAction interface {
+ recvMatch(interface{}) bool
+}
+
+type sendAction interface {
+ send()
+}
+
+// isReady returns true if all the predecessors of an Event have occurred.
+func (e Event) isReady() bool {
+ for _, predecessor := range e.predecessors {
+ if !predecessor.occurred {
+ return false
+ }
+ }
+
+ return true
+}
+
+// A Recv action reads a value from a channel and uses reflect.DeepMatch to
+// compare it with an expected value.
+type Recv struct {
+ Channel interface{}
+ Expected interface{}
+}
+
+func (r Recv) getRecv() recvAction { return r }
+
+func (Recv) getSend() sendAction { return nil }
+
+func (r Recv) getChannel() interface{} { return r.Channel }
+
+func (r Recv) recvMatch(chanEvent interface{}) bool {
+ c, ok := chanEvent.(channelRecv)
+ if !ok || c.channel != r.Channel {
+ return false
+ }
+
+ return reflect.DeepEqual(c.value, r.Expected)
+}
+
+// A RecvMatch action reads a value from a channel and calls a function to
+// determine if the value matches.
+type RecvMatch struct {
+ Channel interface{}
+ Match func(interface{}) bool
+}
+
+func (r RecvMatch) getRecv() recvAction { return r }
+
+func (RecvMatch) getSend() sendAction { return nil }
+
+func (r RecvMatch) getChannel() interface{} { return r.Channel }
+
+func (r RecvMatch) recvMatch(chanEvent interface{}) bool {
+ c, ok := chanEvent.(channelRecv)
+ if !ok || c.channel != r.Channel {
+ return false
+ }
+
+ return r.Match(c.value)
+}
+
+// A Closed action matches if the given channel is closed. The closing is
+// treated as an event, not a state, thus Closed will only match once for a
+// given channel.
+type Closed struct {
+ Channel interface{}
+}
+
+func (r Closed) getRecv() recvAction { return r }
+
+func (Closed) getSend() sendAction { return nil }
+
+func (r Closed) getChannel() interface{} { return r.Channel }
+
+func (r Closed) recvMatch(chanEvent interface{}) bool {
+ c, ok := chanEvent.(channelClosed)
+ if !ok || c.channel != r.Channel {
+ return false
+ }
+
+ return true
+}
+
+// A Send action sends a value to a channel. The value must match the
+// type of the channel exactly unless the channel if of type chan interface{}.
+type Send struct {
+ Channel interface{}
+ Value interface{}
+}
+
+func (Send) getRecv() recvAction { return nil }
+
+func (s Send) getSend() sendAction { return s }
+
+func (s Send) getChannel() interface{} { return s.Channel }
+
+type empty struct {
+ x interface{}
+}
+
+func newEmptyInterface(e empty) reflect.Value {
+ return reflect.NewValue(e).Field(0)
+}
+
+func (s Send) send() {
+ // With reflect.ChanValue.Send, we must match the types exactly. So, if
+ // s.Channel is a chan interface{} we convert s.Value to an interface{}
+ // first.
+ c := reflect.NewValue(s.Channel)
+ var v reflect.Value
+ if iface := c.Type().Elem(); iface.Kind() == reflect.Interface && iface.NumMethod() == 0 {
+ v = newEmptyInterface(empty{s.Value})
+ } else {
+ v = reflect.NewValue(s.Value)
+ }
+ c.Send(v)
+}
+
+// A Close action closes the given channel.
+type Close struct {
+ Channel interface{}
+}
+
+func (Close) getRecv() recvAction { return nil }
+
+func (s Close) getSend() sendAction { return s }
+
+func (s Close) getChannel() interface{} { return s.Channel }
+
+func (s Close) send() { reflect.NewValue(s.Channel).Close() }
+
+// A ReceivedUnexpected error results if no active Events match a value
+// received from a channel.
+type ReceivedUnexpected struct {
+ Value interface{}
+ ready []*Event
+}
+
+func (r ReceivedUnexpected) String() string {
+ names := make([]string, len(r.ready))
+ for i, v := range r.ready {
+ names[i] = v.name
+ }
+ return fmt.Sprintf("received unexpected value on one of the channels: %#v. Runnable events: %s", r.Value, strings.Join(names, ", "))
+}
+
+// A SetupError results if there is a error with the configuration of a set of
+// Events.
+type SetupError string
+
+func (s SetupError) String() string { return string(s) }
+
+func NewEvent(name string, predecessors []*Event, action action) *Event {
+ e := &Event{name, false, predecessors, action}
+ return e
+}
+
+// Given a set of Events, Perform repeatedly iterates over the set and finds the
+// subset of ready Events (that is, all of their predecessors have
+// occurred). From that subset, it pseudo-randomly selects an Event to perform.
+// If the Event is a send event, the send occurs and Perform recalculates the ready
+// set. If the event is a receive event, Perform waits for a value from any of the
+// channels that are contained in any of the events. That value is then matched
+// against the ready events. The first event that matches is considered to
+// have occurred and Perform recalculates the ready set.
+//
+// Perform continues this until all Events have occurred.
+//
+// Note that uncollected goroutines may still be reading from any of the
+// channels read from after Perform returns.
+//
+// For example, consider the problem of testing a function that reads values on
+// one channel and echos them to two output channels. To test this we would
+// create three events: a send event and two receive events. Each of the
+// receive events must list the send event as a predecessor but there is no
+// ordering between the receive events.
+//
+// send := NewEvent("send", nil, Send{c, 1})
+// recv1 := NewEvent("recv 1", []*Event{send}, Recv{c, 1})
+// recv2 := NewEvent("recv 2", []*Event{send}, Recv{c, 1})
+// Perform(0, []*Event{send, recv1, recv2})
+//
+// At first, only the send event would be in the ready set and thus Perform will
+// send a value to the input channel. Now the two receive events are ready and
+// Perform will match each of them against the values read from the output channels.
+//
+// It would be invalid to list one of the receive events as a predecessor of
+// the other. At each receive step, all the receive channels are considered,
+// thus Perform may see a value from a channel that is not in the current ready
+// set and fail.
+func Perform(seed int64, events []*Event) (err os.Error) {
+ r := rand.New(rand.NewSource(seed))
+
+ channels, err := getChannels(events)
+ if err != nil {
+ return
+ }
+ multiplex := make(chan interface{})
+ for _, channel := range channels {
+ go recvValues(multiplex, channel)
+ }
+
+Outer:
+ for {
+ ready, err := readyEvents(events)
+ if err != nil {
+ return err
+ }
+
+ if len(ready) == 0 {
+ // All events occurred.
+ break
+ }
+
+ event := ready[r.Intn(len(ready))]
+ if send := event.action.getSend(); send != nil {
+ send.send()
+ event.occurred = true
+ continue
+ }
+
+ v := <-multiplex
+ for _, event := range ready {
+ if recv := event.action.getRecv(); recv != nil && recv.recvMatch(v) {
+ event.occurred = true
+ continue Outer
+ }
+ }
+
+ return ReceivedUnexpected{v, ready}
+ }
+
+ return nil
+}
+
+// getChannels returns all the channels listed in any receive events.
+func getChannels(events []*Event) ([]interface{}, os.Error) {
+ channels := make([]interface{}, len(events))
+
+ j := 0
+ for _, event := range events {
+ if recv := event.action.getRecv(); recv == nil {
+ continue
+ }
+ c := event.action.getChannel()
+ if reflect.NewValue(c).Kind() != reflect.Chan {
+ return nil, SetupError("one of the channel values is not a channel")
+ }
+
+ duplicate := false
+ for _, other := range channels[0:j] {
+ if c == other {
+ duplicate = true
+ break
+ }
+ }
+
+ if !duplicate {
+ channels[j] = c
+ j++
+ }
+ }
+
+ return channels[0:j], nil
+}
+
+// recvValues is a multiplexing helper function. It reads values from the given
+// channel repeatedly, wrapping them up as either a channelRecv or
+// channelClosed structure, and forwards them to the multiplex channel.
+func recvValues(multiplex chan<- interface{}, channel interface{}) {
+ c := reflect.NewValue(channel)
+
+ for {
+ v, ok := c.Recv()
+ if !ok {
+ multiplex <- channelClosed{channel}
+ return
+ }
+
+ multiplex <- channelRecv{channel, v.Interface()}
+ }
+}
+
+type channelClosed struct {
+ channel interface{}
+}
+
+type channelRecv struct {
+ channel interface{}
+ value interface{}
+}
+
+// readyEvents returns the subset of events that are ready.
+func readyEvents(events []*Event) ([]*Event, os.Error) {
+ ready := make([]*Event, len(events))
+
+ j := 0
+ eventsWaiting := false
+ for _, event := range events {
+ if event.occurred {
+ continue
+ }
+
+ eventsWaiting = true
+ if event.isReady() {
+ ready[j] = event
+ j++
+ }
+ }
+
+ if j == 0 && eventsWaiting {
+ names := make([]string, len(events))
+ for _, event := range events {
+ if event.occurred {
+ continue
+ }
+ names[j] = event.name
+ }
+
+ return nil, SetupError("dependency cycle in events. These events are waiting to run but cannot: " + strings.Join(names, ", "))
+ }
+
+ return ready[0:j], nil
+}
diff --git a/src/cmd/gofix/testdata/reflect.template.go.in b/src/cmd/gofix/testdata/reflect.template.go.in
new file mode 100644
index 000000000..ba06de4e3
--- /dev/null
+++ b/src/cmd/gofix/testdata/reflect.template.go.in
@@ -0,0 +1,1043 @@
+// 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.
+
+/*
+ Data-driven templates for generating textual output such as
+ HTML.
+
+ Templates are executed by applying them to a data structure.
+ Annotations in the template refer to elements of the data
+ structure (typically a field of a struct or a key in a map)
+ to control execution and derive values to be displayed.
+ The template walks the structure as it executes and the
+ "cursor" @ represents the value at the current location
+ in the structure.
+
+ Data items may be values or pointers; the interface hides the
+ indirection.
+
+ In the following, 'field' is one of several things, according to the data.
+
+ - The name of a field of a struct (result = data.field),
+ - The value stored in a map under that key (result = data[field]), or
+ - The result of invoking a niladic single-valued method with that name
+ (result = data.field())
+
+ Major constructs ({} are the default delimiters for template actions;
+ [] are the notation in this comment for optional elements):
+
+ {# comment }
+
+ A one-line comment.
+
+ {.section field} XXX [ {.or} YYY ] {.end}
+
+ Set @ to the value of the field. It may be an explicit @
+ to stay at the same point in the data. If the field is nil
+ or empty, execute YYY; otherwise execute XXX.
+
+ {.repeated section field} XXX [ {.alternates with} ZZZ ] [ {.or} YYY ] {.end}
+
+ Like .section, but field must be an array or slice. XXX
+ is executed for each element. If the array is nil or empty,
+ YYY is executed instead. If the {.alternates with} marker
+ is present, ZZZ is executed between iterations of XXX.
+
+ {field}
+ {field1 field2 ...}
+ {field|formatter}
+ {field1 field2...|formatter}
+ {field|formatter1|formatter2}
+
+ Insert the value of the fields into the output. Each field is
+ first looked for in the cursor, as in .section and .repeated.
+ If it is not found, the search continues in outer sections
+ until the top level is reached.
+
+ If the field value is a pointer, leading asterisks indicate
+ that the value to be inserted should be evaluated through the
+ pointer. For example, if x.p is of type *int, {x.p} will
+ insert the value of the pointer but {*x.p} will insert the
+ value of the underlying integer. If the value is nil or not a
+ pointer, asterisks have no effect.
+
+ If a formatter is specified, it must be named in the formatter
+ map passed to the template set up routines or in the default
+ set ("html","str","") and is used to process the data for
+ output. The formatter function has signature
+ func(wr io.Writer, formatter string, data ...interface{})
+ where wr is the destination for output, data holds the field
+ values at the instantiation, and formatter is its name at
+ the invocation site. The default formatter just concatenates
+ the string representations of the fields.
+
+ Multiple formatters separated by the pipeline character | are
+ executed sequentially, with each formatter receiving the bytes
+ emitted by the one to its left.
+
+ The delimiter strings get their default value, "{" and "}", from
+ JSON-template. They may be set to any non-empty, space-free
+ string using the SetDelims method. Their value can be printed
+ in the output using {.meta-left} and {.meta-right}.
+*/
+package template
+
+import (
+ "bytes"
+ "container/vector"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "reflect"
+ "strings"
+ "unicode"
+ "utf8"
+)
+
+// Errors returned during parsing and execution. Users may extract the information and reformat
+// if they desire.
+type Error struct {
+ Line int
+ Msg string
+}
+
+func (e *Error) String() string { return fmt.Sprintf("line %d: %s", e.Line, e.Msg) }
+
+// Most of the literals are aces.
+var lbrace = []byte{'{'}
+var rbrace = []byte{'}'}
+var space = []byte{' '}
+var tab = []byte{'\t'}
+
+// The various types of "tokens", which are plain text or (usually) brace-delimited descriptors
+const (
+ tokAlternates = iota
+ tokComment
+ tokEnd
+ tokLiteral
+ tokOr
+ tokRepeated
+ tokSection
+ tokText
+ tokVariable
+)
+
+// FormatterMap is the type describing the mapping from formatter
+// names to the functions that implement them.
+type FormatterMap map[string]func(io.Writer, string, ...interface{})
+
+// Built-in formatters.
+var builtins = FormatterMap{
+ "html": HTMLFormatter,
+ "str": StringFormatter,
+ "": StringFormatter,
+}
+
+// The parsed state of a template is a vector of xxxElement structs.
+// Sections have line numbers so errors can be reported better during execution.
+
+// Plain text.
+type textElement struct {
+ text []byte
+}
+
+// A literal such as .meta-left or .meta-right
+type literalElement struct {
+ text []byte
+}
+
+// A variable invocation to be evaluated
+type variableElement struct {
+ linenum int
+ word []string // The fields in the invocation.
+ fmts []string // Names of formatters to apply. len(fmts) > 0
+}
+
+// A .section block, possibly with a .or
+type sectionElement struct {
+ linenum int // of .section itself
+ field string // cursor field for this block
+ start int // first element
+ or int // first element of .or block
+ end int // one beyond last element
+}
+
+// A .repeated block, possibly with a .or and a .alternates
+type repeatedElement struct {
+ sectionElement // It has the same structure...
+ altstart int // ... except for alternates
+ altend int
+}
+
+// Template is the type that represents a template definition.
+// It is unchanged after parsing.
+type Template struct {
+ fmap FormatterMap // formatters for variables
+ // Used during parsing:
+ ldelim, rdelim []byte // delimiters; default {}
+ buf []byte // input text to process
+ p int // position in buf
+ linenum int // position in input
+ // Parsed results:
+ elems *vector.Vector
+}
+
+// Internal state for executing a Template. As we evaluate the struct,
+// the data item descends into the fields associated with sections, etc.
+// Parent is used to walk upwards to find variables higher in the tree.
+type state struct {
+ parent *state // parent in hierarchy
+ data reflect.Value // the driver data for this section etc.
+ wr io.Writer // where to send output
+ buf [2]bytes.Buffer // alternating buffers used when chaining formatters
+}
+
+func (parent *state) clone(data reflect.Value) *state {
+ return &state{parent: parent, data: data, wr: parent.wr}
+}
+
+// New creates a new template with the specified formatter map (which
+// may be nil) to define auxiliary functions for formatting variables.
+func New(fmap FormatterMap) *Template {
+ t := new(Template)
+ t.fmap = fmap
+ t.ldelim = lbrace
+ t.rdelim = rbrace
+ t.elems = new(vector.Vector)
+ return t
+}
+
+// Report error and stop executing. The line number must be provided explicitly.
+func (t *Template) execError(st *state, line int, err string, args ...interface{}) {
+ panic(&Error{line, fmt.Sprintf(err, args...)})
+}
+
+// Report error, panic to terminate parsing.
+// The line number comes from the template state.
+func (t *Template) parseError(err string, args ...interface{}) {
+ panic(&Error{t.linenum, fmt.Sprintf(err, args...)})
+}
+
+// Is this an exported - upper case - name?
+func isExported(name string) bool {
+ rune, _ := utf8.DecodeRuneInString(name)
+ return unicode.IsUpper(rune)
+}
+
+// -- Lexical analysis
+
+// Is c a white space character?
+func white(c uint8) bool { return c == ' ' || c == '\t' || c == '\r' || c == '\n' }
+
+// Safely, does s[n:n+len(t)] == t?
+func equal(s []byte, n int, t []byte) bool {
+ b := s[n:]
+ if len(t) > len(b) { // not enough space left for a match.
+ return false
+ }
+ for i, c := range t {
+ if c != b[i] {
+ return false
+ }
+ }
+ return true
+}
+
+// nextItem returns the next item from the input buffer. If the returned
+// item is empty, we are at EOF. The item will be either a
+// delimited string or a non-empty string between delimited
+// strings. Tokens stop at (but include, if plain text) a newline.
+// Action tokens on a line by themselves drop any space on
+// either side, up to and including the newline.
+func (t *Template) nextItem() []byte {
+ startOfLine := t.p == 0 || t.buf[t.p-1] == '\n'
+ start := t.p
+ var i int
+ newline := func() {
+ t.linenum++
+ i++
+ }
+ // Leading white space up to but not including newline
+ for i = start; i < len(t.buf); i++ {
+ if t.buf[i] == '\n' || !white(t.buf[i]) {
+ break
+ }
+ }
+ leadingSpace := i > start
+ // What's left is nothing, newline, delimited string, or plain text
+ switch {
+ case i == len(t.buf):
+ // EOF; nothing to do
+ case t.buf[i] == '\n':
+ newline()
+ case equal(t.buf, i, t.ldelim):
+ left := i // Start of left delimiter.
+ right := -1 // Will be (immediately after) right delimiter.
+ haveText := false // Delimiters contain text.
+ i += len(t.ldelim)
+ // Find the end of the action.
+ for ; i < len(t.buf); i++ {
+ if t.buf[i] == '\n' {
+ break
+ }
+ if equal(t.buf, i, t.rdelim) {
+ i += len(t.rdelim)
+ right = i
+ break
+ }
+ haveText = true
+ }
+ if right < 0 {
+ t.parseError("unmatched opening delimiter")
+ return nil
+ }
+ // Is this a special action (starts with '.' or '#') and the only thing on the line?
+ if startOfLine && haveText {
+ firstChar := t.buf[left+len(t.ldelim)]
+ if firstChar == '.' || firstChar == '#' {
+ // It's special and the first thing on the line. Is it the last?
+ for j := right; j < len(t.buf) && white(t.buf[j]); j++ {
+ if t.buf[j] == '\n' {
+ // Yes it is. Drop the surrounding space and return the {.foo}
+ t.linenum++
+ t.p = j + 1
+ return t.buf[left:right]
+ }
+ }
+ }
+ }
+ // No it's not. If there's leading space, return that.
+ if leadingSpace {
+ // not trimming space: return leading white space if there is some.
+ t.p = left
+ return t.buf[start:left]
+ }
+ // Return the word, leave the trailing space.
+ start = left
+ break
+ default:
+ for ; i < len(t.buf); i++ {
+ if t.buf[i] == '\n' {
+ newline()
+ break
+ }
+ if equal(t.buf, i, t.ldelim) {
+ break
+ }
+ }
+ }
+ item := t.buf[start:i]
+ t.p = i
+ return item
+}
+
+// Turn a byte array into a white-space-split array of strings.
+func words(buf []byte) []string {
+ s := make([]string, 0, 5)
+ p := 0 // position in buf
+ // one word per loop
+ for i := 0; ; i++ {
+ // skip white space
+ for ; p < len(buf) && white(buf[p]); p++ {
+ }
+ // grab word
+ start := p
+ for ; p < len(buf) && !white(buf[p]); p++ {
+ }
+ if start == p { // no text left
+ break
+ }
+ s = append(s, string(buf[start:p]))
+ }
+ return s
+}
+
+// Analyze an item and return its token type and, if it's an action item, an array of
+// its constituent words.
+func (t *Template) analyze(item []byte) (tok int, w []string) {
+ // item is known to be non-empty
+ if !equal(item, 0, t.ldelim) { // doesn't start with left delimiter
+ tok = tokText
+ return
+ }
+ if !equal(item, len(item)-len(t.rdelim), t.rdelim) { // doesn't end with right delimiter
+ t.parseError("internal error: unmatched opening delimiter") // lexing should prevent this
+ return
+ }
+ if len(item) <= len(t.ldelim)+len(t.rdelim) { // no contents
+ t.parseError("empty directive")
+ return
+ }
+ // Comment
+ if item[len(t.ldelim)] == '#' {
+ tok = tokComment
+ return
+ }
+ // Split into words
+ w = words(item[len(t.ldelim) : len(item)-len(t.rdelim)]) // drop final delimiter
+ if len(w) == 0 {
+ t.parseError("empty directive")
+ return
+ }
+ if len(w) > 0 && w[0][0] != '.' {
+ tok = tokVariable
+ return
+ }
+ switch w[0] {
+ case ".meta-left", ".meta-right", ".space", ".tab":
+ tok = tokLiteral
+ return
+ case ".or":
+ tok = tokOr
+ return
+ case ".end":
+ tok = tokEnd
+ return
+ case ".section":
+ if len(w) != 2 {
+ t.parseError("incorrect fields for .section: %s", item)
+ return
+ }
+ tok = tokSection
+ return
+ case ".repeated":
+ if len(w) != 3 || w[1] != "section" {
+ t.parseError("incorrect fields for .repeated: %s", item)
+ return
+ }
+ tok = tokRepeated
+ return
+ case ".alternates":
+ if len(w) != 2 || w[1] != "with" {
+ t.parseError("incorrect fields for .alternates: %s", item)
+ return
+ }
+ tok = tokAlternates
+ return
+ }
+ t.parseError("bad directive: %s", item)
+ return
+}
+
+// formatter returns the Formatter with the given name in the Template, or nil if none exists.
+func (t *Template) formatter(name string) func(io.Writer, string, ...interface{}) {
+ if t.fmap != nil {
+ if fn := t.fmap[name]; fn != nil {
+ return fn
+ }
+ }
+ return builtins[name]
+}
+
+// -- Parsing
+
+// Allocate a new variable-evaluation element.
+func (t *Template) newVariable(words []string) *variableElement {
+ // After the final space-separated argument, formatters may be specified separated
+ // by pipe symbols, for example: {a b c|d|e}
+
+ // Until we learn otherwise, formatters contains a single name: "", the default formatter.
+ formatters := []string{""}
+ lastWord := words[len(words)-1]
+ bar := strings.IndexRune(lastWord, '|')
+ if bar >= 0 {
+ words[len(words)-1] = lastWord[0:bar]
+ formatters = strings.Split(lastWord[bar+1:], "|", -1)
+ }
+
+ // We could remember the function address here and avoid the lookup later,
+ // but it's more dynamic to let the user change the map contents underfoot.
+ // We do require the name to be present, though.
+
+ // Is it in user-supplied map?
+ for _, f := range formatters {
+ if t.formatter(f) == nil {
+ t.parseError("unknown formatter: %q", f)
+ }
+ }
+ return &variableElement{t.linenum, words, formatters}
+}
+
+// Grab the next item. If it's simple, just append it to the template.
+// Otherwise return its details.
+func (t *Template) parseSimple(item []byte) (done bool, tok int, w []string) {
+ tok, w = t.analyze(item)
+ done = true // assume for simplicity
+ switch tok {
+ case tokComment:
+ return
+ case tokText:
+ t.elems.Push(&textElement{item})
+ return
+ case tokLiteral:
+ switch w[0] {
+ case ".meta-left":
+ t.elems.Push(&literalElement{t.ldelim})
+ case ".meta-right":
+ t.elems.Push(&literalElement{t.rdelim})
+ case ".space":
+ t.elems.Push(&literalElement{space})
+ case ".tab":
+ t.elems.Push(&literalElement{tab})
+ default:
+ t.parseError("internal error: unknown literal: %s", w[0])
+ }
+ return
+ case tokVariable:
+ t.elems.Push(t.newVariable(w))
+ return
+ }
+ return false, tok, w
+}
+
+// parseRepeated and parseSection are mutually recursive
+
+func (t *Template) parseRepeated(words []string) *repeatedElement {
+ r := new(repeatedElement)
+ t.elems.Push(r)
+ r.linenum = t.linenum
+ r.field = words[2]
+ // Scan section, collecting true and false (.or) blocks.
+ r.start = t.elems.Len()
+ r.or = -1
+ r.altstart = -1
+ r.altend = -1
+Loop:
+ for {
+ item := t.nextItem()
+ if len(item) == 0 {
+ t.parseError("missing .end for .repeated section")
+ break
+ }
+ done, tok, w := t.parseSimple(item)
+ if done {
+ continue
+ }
+ switch tok {
+ case tokEnd:
+ break Loop
+ case tokOr:
+ if r.or >= 0 {
+ t.parseError("extra .or in .repeated section")
+ break Loop
+ }
+ r.altend = t.elems.Len()
+ r.or = t.elems.Len()
+ case tokSection:
+ t.parseSection(w)
+ case tokRepeated:
+ t.parseRepeated(w)
+ case tokAlternates:
+ if r.altstart >= 0 {
+ t.parseError("extra .alternates in .repeated section")
+ break Loop
+ }
+ if r.or >= 0 {
+ t.parseError(".alternates inside .or block in .repeated section")
+ break Loop
+ }
+ r.altstart = t.elems.Len()
+ default:
+ t.parseError("internal error: unknown repeated section item: %s", item)
+ break Loop
+ }
+ }
+ if r.altend < 0 {
+ r.altend = t.elems.Len()
+ }
+ r.end = t.elems.Len()
+ return r
+}
+
+func (t *Template) parseSection(words []string) *sectionElement {
+ s := new(sectionElement)
+ t.elems.Push(s)
+ s.linenum = t.linenum
+ s.field = words[1]
+ // Scan section, collecting true and false (.or) blocks.
+ s.start = t.elems.Len()
+ s.or = -1
+Loop:
+ for {
+ item := t.nextItem()
+ if len(item) == 0 {
+ t.parseError("missing .end for .section")
+ break
+ }
+ done, tok, w := t.parseSimple(item)
+ if done {
+ continue
+ }
+ switch tok {
+ case tokEnd:
+ break Loop
+ case tokOr:
+ if s.or >= 0 {
+ t.parseError("extra .or in .section")
+ break Loop
+ }
+ s.or = t.elems.Len()
+ case tokSection:
+ t.parseSection(w)
+ case tokRepeated:
+ t.parseRepeated(w)
+ case tokAlternates:
+ t.parseError(".alternates not in .repeated")
+ default:
+ t.parseError("internal error: unknown section item: %s", item)
+ }
+ }
+ s.end = t.elems.Len()
+ return s
+}
+
+func (t *Template) parse() {
+ for {
+ item := t.nextItem()
+ if len(item) == 0 {
+ break
+ }
+ done, tok, w := t.parseSimple(item)
+ if done {
+ continue
+ }
+ switch tok {
+ case tokOr, tokEnd, tokAlternates:
+ t.parseError("unexpected %s", w[0])
+ case tokSection:
+ t.parseSection(w)
+ case tokRepeated:
+ t.parseRepeated(w)
+ default:
+ t.parseError("internal error: bad directive in parse: %s", item)
+ }
+ }
+}
+
+// -- Execution
+
+// Evaluate interfaces and pointers looking for a value that can look up the name, via a
+// struct field, method, or map key, and return the result of the lookup.
+func (t *Template) lookup(st *state, v reflect.Value, name string) reflect.Value {
+ for v != nil {
+ typ := v.Type()
+ if n := v.Type().NumMethod(); n > 0 {
+ for i := 0; i < n; i++ {
+ m := typ.Method(i)
+ mtyp := m.Type
+ if m.Name == name && mtyp.NumIn() == 1 && mtyp.NumOut() == 1 {
+ if !isExported(name) {
+ t.execError(st, t.linenum, "name not exported: %s in type %s", name, st.data.Type())
+ }
+ return v.Method(i).Call(nil)[0]
+ }
+ }
+ }
+ switch av := v.(type) {
+ case *reflect.PtrValue:
+ v = av.Elem()
+ case *reflect.InterfaceValue:
+ v = av.Elem()
+ case *reflect.StructValue:
+ if !isExported(name) {
+ t.execError(st, t.linenum, "name not exported: %s in type %s", name, st.data.Type())
+ }
+ return av.FieldByName(name)
+ case *reflect.MapValue:
+ if v := av.Elem(reflect.NewValue(name)); v != nil {
+ return v
+ }
+ return reflect.MakeZero(typ.(*reflect.MapType).Elem())
+ default:
+ return nil
+ }
+ }
+ return v
+}
+
+// indirectPtr returns the item numLevels levels of indirection below the value.
+// It is forgiving: if the value is not a pointer, it returns it rather than giving
+// an error. If the pointer is nil, it is returned as is.
+func indirectPtr(v reflect.Value, numLevels int) reflect.Value {
+ for i := numLevels; v != nil && i > 0; i++ {
+ if p, ok := v.(*reflect.PtrValue); ok {
+ if p.IsNil() {
+ return v
+ }
+ v = p.Elem()
+ } else {
+ break
+ }
+ }
+ return v
+}
+
+// Walk v through pointers and interfaces, extracting the elements within.
+func indirect(v reflect.Value) reflect.Value {
+loop:
+ for v != nil {
+ switch av := v.(type) {
+ case *reflect.PtrValue:
+ v = av.Elem()
+ case *reflect.InterfaceValue:
+ v = av.Elem()
+ default:
+ break loop
+ }
+ }
+ return v
+}
+
+// If the data for this template is a struct, find the named variable.
+// Names of the form a.b.c are walked down the data tree.
+// The special name "@" (the "cursor") denotes the current data.
+// The value coming in (st.data) might need indirecting to reach
+// a struct while the return value is not indirected - that is,
+// it represents the actual named field. Leading stars indicate
+// levels of indirection to be applied to the value.
+func (t *Template) findVar(st *state, s string) reflect.Value {
+ data := st.data
+ flattenedName := strings.TrimLeft(s, "*")
+ numStars := len(s) - len(flattenedName)
+ s = flattenedName
+ if s == "@" {
+ return indirectPtr(data, numStars)
+ }
+ for _, elem := range strings.Split(s, ".", -1) {
+ // Look up field; data must be a struct or map.
+ data = t.lookup(st, data, elem)
+ if data == nil {
+ return nil
+ }
+ }
+ return indirectPtr(data, numStars)
+}
+
+// Is there no data to look at?
+func empty(v reflect.Value) bool {
+ v = indirect(v)
+ if v == nil {
+ return true
+ }
+ switch v := v.(type) {
+ case *reflect.BoolValue:
+ return v.Get() == false
+ case *reflect.StringValue:
+ return v.Get() == ""
+ case *reflect.StructValue:
+ return false
+ case *reflect.MapValue:
+ return false
+ case *reflect.ArrayValue:
+ return v.Len() == 0
+ case *reflect.SliceValue:
+ return v.Len() == 0
+ }
+ return false
+}
+
+// Look up a variable or method, up through the parent if necessary.
+func (t *Template) varValue(name string, st *state) reflect.Value {
+ field := t.findVar(st, name)
+ if field == nil {
+ if st.parent == nil {
+ t.execError(st, t.linenum, "name not found: %s in type %s", name, st.data.Type())
+ }
+ return t.varValue(name, st.parent)
+ }
+ return field
+}
+
+func (t *Template) format(wr io.Writer, fmt string, val []interface{}, v *variableElement, st *state) {
+ fn := t.formatter(fmt)
+ if fn == nil {
+ t.execError(st, v.linenum, "missing formatter %s for variable %s", fmt, v.word[0])
+ }
+ fn(wr, fmt, val...)
+}
+
+// Evaluate a variable, looking up through the parent if necessary.
+// If it has a formatter attached ({var|formatter}) run that too.
+func (t *Template) writeVariable(v *variableElement, st *state) {
+ // Turn the words of the invocation into values.
+ val := make([]interface{}, len(v.word))
+ for i, word := range v.word {
+ val[i] = t.varValue(word, st).Interface()
+ }
+
+ for i, fmt := range v.fmts[:len(v.fmts)-1] {
+ b := &st.buf[i&1]
+ b.Reset()
+ t.format(b, fmt, val, v, st)
+ val = val[0:1]
+ val[0] = b.Bytes()
+ }
+ t.format(st.wr, v.fmts[len(v.fmts)-1], val, v, st)
+}
+
+// Execute element i. Return next index to execute.
+func (t *Template) executeElement(i int, st *state) int {
+ switch elem := t.elems.At(i).(type) {
+ case *textElement:
+ st.wr.Write(elem.text)
+ return i + 1
+ case *literalElement:
+ st.wr.Write(elem.text)
+ return i + 1
+ case *variableElement:
+ t.writeVariable(elem, st)
+ return i + 1
+ case *sectionElement:
+ t.executeSection(elem, st)
+ return elem.end
+ case *repeatedElement:
+ t.executeRepeated(elem, st)
+ return elem.end
+ }
+ e := t.elems.At(i)
+ t.execError(st, 0, "internal error: bad directive in execute: %v %T\n", reflect.NewValue(e).Interface(), e)
+ return 0
+}
+
+// Execute the template.
+func (t *Template) execute(start, end int, st *state) {
+ for i := start; i < end; {
+ i = t.executeElement(i, st)
+ }
+}
+
+// Execute a .section
+func (t *Template) executeSection(s *sectionElement, st *state) {
+ // Find driver data for this section. It must be in the current struct.
+ field := t.varValue(s.field, st)
+ if field == nil {
+ t.execError(st, s.linenum, ".section: cannot find field %s in %s", s.field, st.data.Type())
+ }
+ st = st.clone(field)
+ start, end := s.start, s.or
+ if !empty(field) {
+ // Execute the normal block.
+ if end < 0 {
+ end = s.end
+ }
+ } else {
+ // Execute the .or block. If it's missing, do nothing.
+ start, end = s.or, s.end
+ if start < 0 {
+ return
+ }
+ }
+ for i := start; i < end; {
+ i = t.executeElement(i, st)
+ }
+}
+
+// Return the result of calling the Iter method on v, or nil.
+func iter(v reflect.Value) *reflect.ChanValue {
+ for j := 0; j < v.Type().NumMethod(); j++ {
+ mth := v.Type().Method(j)
+ fv := v.Method(j)
+ ft := fv.Type().(*reflect.FuncType)
+ // TODO(rsc): NumIn() should return 0 here, because ft is from a curried FuncValue.
+ if mth.Name != "Iter" || ft.NumIn() != 1 || ft.NumOut() != 1 {
+ continue
+ }
+ ct, ok := ft.Out(0).(*reflect.ChanType)
+ if !ok || ct.Dir()&reflect.RecvDir == 0 {
+ continue
+ }
+ return fv.Call(nil)[0].(*reflect.ChanValue)
+ }
+ return nil
+}
+
+// Execute a .repeated section
+func (t *Template) executeRepeated(r *repeatedElement, st *state) {
+ // Find driver data for this section. It must be in the current struct.
+ field := t.varValue(r.field, st)
+ if field == nil {
+ t.execError(st, r.linenum, ".repeated: cannot find field %s in %s", r.field, st.data.Type())
+ }
+ field = indirect(field)
+
+ start, end := r.start, r.or
+ if end < 0 {
+ end = r.end
+ }
+ if r.altstart >= 0 {
+ end = r.altstart
+ }
+ first := true
+
+ // Code common to all the loops.
+ loopBody := func(newst *state) {
+ // .alternates between elements
+ if !first && r.altstart >= 0 {
+ for i := r.altstart; i < r.altend; {
+ i = t.executeElement(i, newst)
+ }
+ }
+ first = false
+ for i := start; i < end; {
+ i = t.executeElement(i, newst)
+ }
+ }
+
+ if array, ok := field.(reflect.ArrayOrSliceValue); ok {
+ for j := 0; j < array.Len(); j++ {
+ loopBody(st.clone(array.Elem(j)))
+ }
+ } else if m, ok := field.(*reflect.MapValue); ok {
+ for _, key := range m.Keys() {
+ loopBody(st.clone(m.Elem(key)))
+ }
+ } else if ch := iter(field); ch != nil {
+ for {
+ e, ok := ch.Recv()
+ if !ok {
+ break
+ }
+ loopBody(st.clone(e))
+ }
+ } else {
+ t.execError(st, r.linenum, ".repeated: cannot repeat %s (type %s)",
+ r.field, field.Type())
+ }
+
+ if first {
+ // Empty. Execute the .or block, once. If it's missing, do nothing.
+ start, end := r.or, r.end
+ if start >= 0 {
+ newst := st.clone(field)
+ for i := start; i < end; {
+ i = t.executeElement(i, newst)
+ }
+ }
+ return
+ }
+}
+
+// A valid delimiter must contain no white space and be non-empty.
+func validDelim(d []byte) bool {
+ if len(d) == 0 {
+ return false
+ }
+ for _, c := range d {
+ if white(c) {
+ return false
+ }
+ }
+ return true
+}
+
+// checkError is a deferred function to turn a panic with type *Error into a plain error return.
+// Other panics are unexpected and so are re-enabled.
+func checkError(error *os.Error) {
+ if v := recover(); v != nil {
+ if e, ok := v.(*Error); ok {
+ *error = e
+ } else {
+ // runtime errors should crash
+ panic(v)
+ }
+ }
+}
+
+// -- Public interface
+
+// Parse initializes a Template by parsing its definition. The string
+// s contains the template text. If any errors occur, Parse returns
+// the error.
+func (t *Template) Parse(s string) (err os.Error) {
+ if t.elems == nil {
+ return &Error{1, "template not allocated with New"}
+ }
+ if !validDelim(t.ldelim) || !validDelim(t.rdelim) {
+ return &Error{1, fmt.Sprintf("bad delimiter strings %q %q", t.ldelim, t.rdelim)}
+ }
+ defer checkError(&err)
+ t.buf = []byte(s)
+ t.p = 0
+ t.linenum = 1
+ t.parse()
+ return nil
+}
+
+// ParseFile is like Parse but reads the template definition from the
+// named file.
+func (t *Template) ParseFile(filename string) (err os.Error) {
+ b, err := ioutil.ReadFile(filename)
+ if err != nil {
+ return err
+ }
+ return t.Parse(string(b))
+}
+
+// Execute applies a parsed template to the specified data object,
+// generating output to wr.
+func (t *Template) Execute(wr io.Writer, data interface{}) (err os.Error) {
+ // Extract the driver data.
+ val := reflect.NewValue(data)
+ defer checkError(&err)
+ t.p = 0
+ t.execute(0, t.elems.Len(), &state{parent: nil, data: val, wr: wr})
+ return nil
+}
+
+// SetDelims sets the left and right delimiters for operations in the
+// template. They are validated during parsing. They could be
+// validated here but it's better to keep the routine simple. The
+// delimiters are very rarely invalid and Parse has the necessary
+// error-handling interface already.
+func (t *Template) SetDelims(left, right string) {
+ t.ldelim = []byte(left)
+ t.rdelim = []byte(right)
+}
+
+// Parse creates a Template with default parameters (such as {} for
+// metacharacters). The string s contains the template text while
+// the formatter map fmap, which may be nil, defines auxiliary functions
+// for formatting variables. The template is returned. If any errors
+// occur, err will be non-nil.
+func Parse(s string, fmap FormatterMap) (t *Template, err os.Error) {
+ t = New(fmap)
+ err = t.Parse(s)
+ if err != nil {
+ t = nil
+ }
+ return
+}
+
+// ParseFile is a wrapper function that creates a Template with default
+// parameters (such as {} for metacharacters). The filename identifies
+// a file containing the template text, while the formatter map fmap, which
+// may be nil, defines auxiliary functions for formatting variables.
+// The template is returned. If any errors occur, err will be non-nil.
+func ParseFile(filename string, fmap FormatterMap) (t *Template, err os.Error) {
+ b, err := ioutil.ReadFile(filename)
+ if err != nil {
+ return nil, err
+ }
+ return Parse(string(b), fmap)
+}
+
+// MustParse is like Parse but panics if the template cannot be parsed.
+func MustParse(s string, fmap FormatterMap) *Template {
+ t, err := Parse(s, fmap)
+ if err != nil {
+ panic("template.MustParse error: " + err.String())
+ }
+ return t
+}
+
+// MustParseFile is like ParseFile but panics if the file cannot be read
+// or the template cannot be parsed.
+func MustParseFile(filename string, fmap FormatterMap) *Template {
+ b, err := ioutil.ReadFile(filename)
+ if err != nil {
+ panic("template.MustParseFile error: " + err.String())
+ }
+ return MustParse(string(b), fmap)
+}
diff --git a/src/cmd/gofix/testdata/reflect.template.go.out b/src/cmd/gofix/testdata/reflect.template.go.out
new file mode 100644
index 000000000..28872dbee
--- /dev/null
+++ b/src/cmd/gofix/testdata/reflect.template.go.out
@@ -0,0 +1,1044 @@
+// 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.
+
+/*
+ Data-driven templates for generating textual output such as
+ HTML.
+
+ Templates are executed by applying them to a data structure.
+ Annotations in the template refer to elements of the data
+ structure (typically a field of a struct or a key in a map)
+ to control execution and derive values to be displayed.
+ The template walks the structure as it executes and the
+ "cursor" @ represents the value at the current location
+ in the structure.
+
+ Data items may be values or pointers; the interface hides the
+ indirection.
+
+ In the following, 'field' is one of several things, according to the data.
+
+ - The name of a field of a struct (result = data.field),
+ - The value stored in a map under that key (result = data[field]), or
+ - The result of invoking a niladic single-valued method with that name
+ (result = data.field())
+
+ Major constructs ({} are the default delimiters for template actions;
+ [] are the notation in this comment for optional elements):
+
+ {# comment }
+
+ A one-line comment.
+
+ {.section field} XXX [ {.or} YYY ] {.end}
+
+ Set @ to the value of the field. It may be an explicit @
+ to stay at the same point in the data. If the field is nil
+ or empty, execute YYY; otherwise execute XXX.
+
+ {.repeated section field} XXX [ {.alternates with} ZZZ ] [ {.or} YYY ] {.end}
+
+ Like .section, but field must be an array or slice. XXX
+ is executed for each element. If the array is nil or empty,
+ YYY is executed instead. If the {.alternates with} marker
+ is present, ZZZ is executed between iterations of XXX.
+
+ {field}
+ {field1 field2 ...}
+ {field|formatter}
+ {field1 field2...|formatter}
+ {field|formatter1|formatter2}
+
+ Insert the value of the fields into the output. Each field is
+ first looked for in the cursor, as in .section and .repeated.
+ If it is not found, the search continues in outer sections
+ until the top level is reached.
+
+ If the field value is a pointer, leading asterisks indicate
+ that the value to be inserted should be evaluated through the
+ pointer. For example, if x.p is of type *int, {x.p} will
+ insert the value of the pointer but {*x.p} will insert the
+ value of the underlying integer. If the value is nil or not a
+ pointer, asterisks have no effect.
+
+ If a formatter is specified, it must be named in the formatter
+ map passed to the template set up routines or in the default
+ set ("html","str","") and is used to process the data for
+ output. The formatter function has signature
+ func(wr io.Writer, formatter string, data ...interface{})
+ where wr is the destination for output, data holds the field
+ values at the instantiation, and formatter is its name at
+ the invocation site. The default formatter just concatenates
+ the string representations of the fields.
+
+ Multiple formatters separated by the pipeline character | are
+ executed sequentially, with each formatter receiving the bytes
+ emitted by the one to its left.
+
+ The delimiter strings get their default value, "{" and "}", from
+ JSON-template. They may be set to any non-empty, space-free
+ string using the SetDelims method. Their value can be printed
+ in the output using {.meta-left} and {.meta-right}.
+*/
+package template
+
+import (
+ "bytes"
+ "container/vector"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "reflect"
+ "strings"
+ "unicode"
+ "utf8"
+)
+
+// Errors returned during parsing and execution. Users may extract the information and reformat
+// if they desire.
+type Error struct {
+ Line int
+ Msg string
+}
+
+func (e *Error) String() string { return fmt.Sprintf("line %d: %s", e.Line, e.Msg) }
+
+// Most of the literals are aces.
+var lbrace = []byte{'{'}
+var rbrace = []byte{'}'}
+var space = []byte{' '}
+var tab = []byte{'\t'}
+
+// The various types of "tokens", which are plain text or (usually) brace-delimited descriptors
+const (
+ tokAlternates = iota
+ tokComment
+ tokEnd
+ tokLiteral
+ tokOr
+ tokRepeated
+ tokSection
+ tokText
+ tokVariable
+)
+
+// FormatterMap is the type describing the mapping from formatter
+// names to the functions that implement them.
+type FormatterMap map[string]func(io.Writer, string, ...interface{})
+
+// Built-in formatters.
+var builtins = FormatterMap{
+ "html": HTMLFormatter,
+ "str": StringFormatter,
+ "": StringFormatter,
+}
+
+// The parsed state of a template is a vector of xxxElement structs.
+// Sections have line numbers so errors can be reported better during execution.
+
+// Plain text.
+type textElement struct {
+ text []byte
+}
+
+// A literal such as .meta-left or .meta-right
+type literalElement struct {
+ text []byte
+}
+
+// A variable invocation to be evaluated
+type variableElement struct {
+ linenum int
+ word []string // The fields in the invocation.
+ fmts []string // Names of formatters to apply. len(fmts) > 0
+}
+
+// A .section block, possibly with a .or
+type sectionElement struct {
+ linenum int // of .section itself
+ field string // cursor field for this block
+ start int // first element
+ or int // first element of .or block
+ end int // one beyond last element
+}
+
+// A .repeated block, possibly with a .or and a .alternates
+type repeatedElement struct {
+ sectionElement // It has the same structure...
+ altstart int // ... except for alternates
+ altend int
+}
+
+// Template is the type that represents a template definition.
+// It is unchanged after parsing.
+type Template struct {
+ fmap FormatterMap // formatters for variables
+ // Used during parsing:
+ ldelim, rdelim []byte // delimiters; default {}
+ buf []byte // input text to process
+ p int // position in buf
+ linenum int // position in input
+ // Parsed results:
+ elems *vector.Vector
+}
+
+// Internal state for executing a Template. As we evaluate the struct,
+// the data item descends into the fields associated with sections, etc.
+// Parent is used to walk upwards to find variables higher in the tree.
+type state struct {
+ parent *state // parent in hierarchy
+ data reflect.Value // the driver data for this section etc.
+ wr io.Writer // where to send output
+ buf [2]bytes.Buffer // alternating buffers used when chaining formatters
+}
+
+func (parent *state) clone(data reflect.Value) *state {
+ return &state{parent: parent, data: data, wr: parent.wr}
+}
+
+// New creates a new template with the specified formatter map (which
+// may be nil) to define auxiliary functions for formatting variables.
+func New(fmap FormatterMap) *Template {
+ t := new(Template)
+ t.fmap = fmap
+ t.ldelim = lbrace
+ t.rdelim = rbrace
+ t.elems = new(vector.Vector)
+ return t
+}
+
+// Report error and stop executing. The line number must be provided explicitly.
+func (t *Template) execError(st *state, line int, err string, args ...interface{}) {
+ panic(&Error{line, fmt.Sprintf(err, args...)})
+}
+
+// Report error, panic to terminate parsing.
+// The line number comes from the template state.
+func (t *Template) parseError(err string, args ...interface{}) {
+ panic(&Error{t.linenum, fmt.Sprintf(err, args...)})
+}
+
+// Is this an exported - upper case - name?
+func isExported(name string) bool {
+ rune, _ := utf8.DecodeRuneInString(name)
+ return unicode.IsUpper(rune)
+}
+
+// -- Lexical analysis
+
+// Is c a white space character?
+func white(c uint8) bool { return c == ' ' || c == '\t' || c == '\r' || c == '\n' }
+
+// Safely, does s[n:n+len(t)] == t?
+func equal(s []byte, n int, t []byte) bool {
+ b := s[n:]
+ if len(t) > len(b) { // not enough space left for a match.
+ return false
+ }
+ for i, c := range t {
+ if c != b[i] {
+ return false
+ }
+ }
+ return true
+}
+
+// nextItem returns the next item from the input buffer. If the returned
+// item is empty, we are at EOF. The item will be either a
+// delimited string or a non-empty string between delimited
+// strings. Tokens stop at (but include, if plain text) a newline.
+// Action tokens on a line by themselves drop any space on
+// either side, up to and including the newline.
+func (t *Template) nextItem() []byte {
+ startOfLine := t.p == 0 || t.buf[t.p-1] == '\n'
+ start := t.p
+ var i int
+ newline := func() {
+ t.linenum++
+ i++
+ }
+ // Leading white space up to but not including newline
+ for i = start; i < len(t.buf); i++ {
+ if t.buf[i] == '\n' || !white(t.buf[i]) {
+ break
+ }
+ }
+ leadingSpace := i > start
+ // What's left is nothing, newline, delimited string, or plain text
+ switch {
+ case i == len(t.buf):
+ // EOF; nothing to do
+ case t.buf[i] == '\n':
+ newline()
+ case equal(t.buf, i, t.ldelim):
+ left := i // Start of left delimiter.
+ right := -1 // Will be (immediately after) right delimiter.
+ haveText := false // Delimiters contain text.
+ i += len(t.ldelim)
+ // Find the end of the action.
+ for ; i < len(t.buf); i++ {
+ if t.buf[i] == '\n' {
+ break
+ }
+ if equal(t.buf, i, t.rdelim) {
+ i += len(t.rdelim)
+ right = i
+ break
+ }
+ haveText = true
+ }
+ if right < 0 {
+ t.parseError("unmatched opening delimiter")
+ return nil
+ }
+ // Is this a special action (starts with '.' or '#') and the only thing on the line?
+ if startOfLine && haveText {
+ firstChar := t.buf[left+len(t.ldelim)]
+ if firstChar == '.' || firstChar == '#' {
+ // It's special and the first thing on the line. Is it the last?
+ for j := right; j < len(t.buf) && white(t.buf[j]); j++ {
+ if t.buf[j] == '\n' {
+ // Yes it is. Drop the surrounding space and return the {.foo}
+ t.linenum++
+ t.p = j + 1
+ return t.buf[left:right]
+ }
+ }
+ }
+ }
+ // No it's not. If there's leading space, return that.
+ if leadingSpace {
+ // not trimming space: return leading white space if there is some.
+ t.p = left
+ return t.buf[start:left]
+ }
+ // Return the word, leave the trailing space.
+ start = left
+ break
+ default:
+ for ; i < len(t.buf); i++ {
+ if t.buf[i] == '\n' {
+ newline()
+ break
+ }
+ if equal(t.buf, i, t.ldelim) {
+ break
+ }
+ }
+ }
+ item := t.buf[start:i]
+ t.p = i
+ return item
+}
+
+// Turn a byte array into a white-space-split array of strings.
+func words(buf []byte) []string {
+ s := make([]string, 0, 5)
+ p := 0 // position in buf
+ // one word per loop
+ for i := 0; ; i++ {
+ // skip white space
+ for ; p < len(buf) && white(buf[p]); p++ {
+ }
+ // grab word
+ start := p
+ for ; p < len(buf) && !white(buf[p]); p++ {
+ }
+ if start == p { // no text left
+ break
+ }
+ s = append(s, string(buf[start:p]))
+ }
+ return s
+}
+
+// Analyze an item and return its token type and, if it's an action item, an array of
+// its constituent words.
+func (t *Template) analyze(item []byte) (tok int, w []string) {
+ // item is known to be non-empty
+ if !equal(item, 0, t.ldelim) { // doesn't start with left delimiter
+ tok = tokText
+ return
+ }
+ if !equal(item, len(item)-len(t.rdelim), t.rdelim) { // doesn't end with right delimiter
+ t.parseError("internal error: unmatched opening delimiter") // lexing should prevent this
+ return
+ }
+ if len(item) <= len(t.ldelim)+len(t.rdelim) { // no contents
+ t.parseError("empty directive")
+ return
+ }
+ // Comment
+ if item[len(t.ldelim)] == '#' {
+ tok = tokComment
+ return
+ }
+ // Split into words
+ w = words(item[len(t.ldelim) : len(item)-len(t.rdelim)]) // drop final delimiter
+ if len(w) == 0 {
+ t.parseError("empty directive")
+ return
+ }
+ if len(w) > 0 && w[0][0] != '.' {
+ tok = tokVariable
+ return
+ }
+ switch w[0] {
+ case ".meta-left", ".meta-right", ".space", ".tab":
+ tok = tokLiteral
+ return
+ case ".or":
+ tok = tokOr
+ return
+ case ".end":
+ tok = tokEnd
+ return
+ case ".section":
+ if len(w) != 2 {
+ t.parseError("incorrect fields for .section: %s", item)
+ return
+ }
+ tok = tokSection
+ return
+ case ".repeated":
+ if len(w) != 3 || w[1] != "section" {
+ t.parseError("incorrect fields for .repeated: %s", item)
+ return
+ }
+ tok = tokRepeated
+ return
+ case ".alternates":
+ if len(w) != 2 || w[1] != "with" {
+ t.parseError("incorrect fields for .alternates: %s", item)
+ return
+ }
+ tok = tokAlternates
+ return
+ }
+ t.parseError("bad directive: %s", item)
+ return
+}
+
+// formatter returns the Formatter with the given name in the Template, or nil if none exists.
+func (t *Template) formatter(name string) func(io.Writer, string, ...interface{}) {
+ if t.fmap != nil {
+ if fn := t.fmap[name]; fn != nil {
+ return fn
+ }
+ }
+ return builtins[name]
+}
+
+// -- Parsing
+
+// Allocate a new variable-evaluation element.
+func (t *Template) newVariable(words []string) *variableElement {
+ // After the final space-separated argument, formatters may be specified separated
+ // by pipe symbols, for example: {a b c|d|e}
+
+ // Until we learn otherwise, formatters contains a single name: "", the default formatter.
+ formatters := []string{""}
+ lastWord := words[len(words)-1]
+ bar := strings.IndexRune(lastWord, '|')
+ if bar >= 0 {
+ words[len(words)-1] = lastWord[0:bar]
+ formatters = strings.Split(lastWord[bar+1:], "|", -1)
+ }
+
+ // We could remember the function address here and avoid the lookup later,
+ // but it's more dynamic to let the user change the map contents underfoot.
+ // We do require the name to be present, though.
+
+ // Is it in user-supplied map?
+ for _, f := range formatters {
+ if t.formatter(f) == nil {
+ t.parseError("unknown formatter: %q", f)
+ }
+ }
+ return &variableElement{t.linenum, words, formatters}
+}
+
+// Grab the next item. If it's simple, just append it to the template.
+// Otherwise return its details.
+func (t *Template) parseSimple(item []byte) (done bool, tok int, w []string) {
+ tok, w = t.analyze(item)
+ done = true // assume for simplicity
+ switch tok {
+ case tokComment:
+ return
+ case tokText:
+ t.elems.Push(&textElement{item})
+ return
+ case tokLiteral:
+ switch w[0] {
+ case ".meta-left":
+ t.elems.Push(&literalElement{t.ldelim})
+ case ".meta-right":
+ t.elems.Push(&literalElement{t.rdelim})
+ case ".space":
+ t.elems.Push(&literalElement{space})
+ case ".tab":
+ t.elems.Push(&literalElement{tab})
+ default:
+ t.parseError("internal error: unknown literal: %s", w[0])
+ }
+ return
+ case tokVariable:
+ t.elems.Push(t.newVariable(w))
+ return
+ }
+ return false, tok, w
+}
+
+// parseRepeated and parseSection are mutually recursive
+
+func (t *Template) parseRepeated(words []string) *repeatedElement {
+ r := new(repeatedElement)
+ t.elems.Push(r)
+ r.linenum = t.linenum
+ r.field = words[2]
+ // Scan section, collecting true and false (.or) blocks.
+ r.start = t.elems.Len()
+ r.or = -1
+ r.altstart = -1
+ r.altend = -1
+Loop:
+ for {
+ item := t.nextItem()
+ if len(item) == 0 {
+ t.parseError("missing .end for .repeated section")
+ break
+ }
+ done, tok, w := t.parseSimple(item)
+ if done {
+ continue
+ }
+ switch tok {
+ case tokEnd:
+ break Loop
+ case tokOr:
+ if r.or >= 0 {
+ t.parseError("extra .or in .repeated section")
+ break Loop
+ }
+ r.altend = t.elems.Len()
+ r.or = t.elems.Len()
+ case tokSection:
+ t.parseSection(w)
+ case tokRepeated:
+ t.parseRepeated(w)
+ case tokAlternates:
+ if r.altstart >= 0 {
+ t.parseError("extra .alternates in .repeated section")
+ break Loop
+ }
+ if r.or >= 0 {
+ t.parseError(".alternates inside .or block in .repeated section")
+ break Loop
+ }
+ r.altstart = t.elems.Len()
+ default:
+ t.parseError("internal error: unknown repeated section item: %s", item)
+ break Loop
+ }
+ }
+ if r.altend < 0 {
+ r.altend = t.elems.Len()
+ }
+ r.end = t.elems.Len()
+ return r
+}
+
+func (t *Template) parseSection(words []string) *sectionElement {
+ s := new(sectionElement)
+ t.elems.Push(s)
+ s.linenum = t.linenum
+ s.field = words[1]
+ // Scan section, collecting true and false (.or) blocks.
+ s.start = t.elems.Len()
+ s.or = -1
+Loop:
+ for {
+ item := t.nextItem()
+ if len(item) == 0 {
+ t.parseError("missing .end for .section")
+ break
+ }
+ done, tok, w := t.parseSimple(item)
+ if done {
+ continue
+ }
+ switch tok {
+ case tokEnd:
+ break Loop
+ case tokOr:
+ if s.or >= 0 {
+ t.parseError("extra .or in .section")
+ break Loop
+ }
+ s.or = t.elems.Len()
+ case tokSection:
+ t.parseSection(w)
+ case tokRepeated:
+ t.parseRepeated(w)
+ case tokAlternates:
+ t.parseError(".alternates not in .repeated")
+ default:
+ t.parseError("internal error: unknown section item: %s", item)
+ }
+ }
+ s.end = t.elems.Len()
+ return s
+}
+
+func (t *Template) parse() {
+ for {
+ item := t.nextItem()
+ if len(item) == 0 {
+ break
+ }
+ done, tok, w := t.parseSimple(item)
+ if done {
+ continue
+ }
+ switch tok {
+ case tokOr, tokEnd, tokAlternates:
+ t.parseError("unexpected %s", w[0])
+ case tokSection:
+ t.parseSection(w)
+ case tokRepeated:
+ t.parseRepeated(w)
+ default:
+ t.parseError("internal error: bad directive in parse: %s", item)
+ }
+ }
+}
+
+// -- Execution
+
+// Evaluate interfaces and pointers looking for a value that can look up the name, via a
+// struct field, method, or map key, and return the result of the lookup.
+func (t *Template) lookup(st *state, v reflect.Value, name string) reflect.Value {
+ for v.IsValid() {
+ typ := v.Type()
+ if n := v.Type().NumMethod(); n > 0 {
+ for i := 0; i < n; i++ {
+ m := typ.Method(i)
+ mtyp := m.Type
+ if m.Name == name && mtyp.NumIn() == 1 && mtyp.NumOut() == 1 {
+ if !isExported(name) {
+ t.execError(st, t.linenum, "name not exported: %s in type %s", name, st.data.Type())
+ }
+ return v.Method(i).Call(nil)[0]
+ }
+ }
+ }
+ switch av := v; av.Kind() {
+ case reflect.Ptr:
+ v = av.Elem()
+ case reflect.Interface:
+ v = av.Elem()
+ case reflect.Struct:
+ if !isExported(name) {
+ t.execError(st, t.linenum, "name not exported: %s in type %s", name, st.data.Type())
+ }
+ return av.FieldByName(name)
+ case reflect.Map:
+ if v := av.MapIndex(reflect.NewValue(name)); v.IsValid() {
+ return v
+ }
+ return reflect.Zero(typ.Elem())
+ default:
+ return reflect.Value{}
+ }
+ }
+ return v
+}
+
+// indirectPtr returns the item numLevels levels of indirection below the value.
+// It is forgiving: if the value is not a pointer, it returns it rather than giving
+// an error. If the pointer is nil, it is returned as is.
+func indirectPtr(v reflect.Value, numLevels int) reflect.Value {
+ for i := numLevels; v.IsValid() && i > 0; i++ {
+ if p := v; p.Kind() == reflect.Ptr {
+ if p.IsNil() {
+ return v
+ }
+ v = p.Elem()
+ } else {
+ break
+ }
+ }
+ return v
+}
+
+// Walk v through pointers and interfaces, extracting the elements within.
+func indirect(v reflect.Value) reflect.Value {
+loop:
+ for v.IsValid() {
+ switch av := v; av.Kind() {
+ case reflect.Ptr:
+ v = av.Elem()
+ case reflect.Interface:
+ v = av.Elem()
+ default:
+ break loop
+ }
+ }
+ return v
+}
+
+// If the data for this template is a struct, find the named variable.
+// Names of the form a.b.c are walked down the data tree.
+// The special name "@" (the "cursor") denotes the current data.
+// The value coming in (st.data) might need indirecting to reach
+// a struct while the return value is not indirected - that is,
+// it represents the actual named field. Leading stars indicate
+// levels of indirection to be applied to the value.
+func (t *Template) findVar(st *state, s string) reflect.Value {
+ data := st.data
+ flattenedName := strings.TrimLeft(s, "*")
+ numStars := len(s) - len(flattenedName)
+ s = flattenedName
+ if s == "@" {
+ return indirectPtr(data, numStars)
+ }
+ for _, elem := range strings.Split(s, ".", -1) {
+ // Look up field; data must be a struct or map.
+ data = t.lookup(st, data, elem)
+ if !data.IsValid() {
+ return reflect.Value{}
+ }
+ }
+ return indirectPtr(data, numStars)
+}
+
+// Is there no data to look at?
+func empty(v reflect.Value) bool {
+ v = indirect(v)
+ if !v.IsValid() {
+ return true
+ }
+ switch v.Kind() {
+ case reflect.Bool:
+ return v.Bool() == false
+ case reflect.String:
+ return v.String() == ""
+ case reflect.Struct:
+ return false
+ case reflect.Map:
+ return false
+ case reflect.Array:
+ return v.Len() == 0
+ case reflect.Slice:
+ return v.Len() == 0
+ }
+ return false
+}
+
+// Look up a variable or method, up through the parent if necessary.
+func (t *Template) varValue(name string, st *state) reflect.Value {
+ field := t.findVar(st, name)
+ if !field.IsValid() {
+ if st.parent == nil {
+ t.execError(st, t.linenum, "name not found: %s in type %s", name, st.data.Type())
+ }
+ return t.varValue(name, st.parent)
+ }
+ return field
+}
+
+func (t *Template) format(wr io.Writer, fmt string, val []interface{}, v *variableElement, st *state) {
+ fn := t.formatter(fmt)
+ if fn == nil {
+ t.execError(st, v.linenum, "missing formatter %s for variable %s", fmt, v.word[0])
+ }
+ fn(wr, fmt, val...)
+}
+
+// Evaluate a variable, looking up through the parent if necessary.
+// If it has a formatter attached ({var|formatter}) run that too.
+func (t *Template) writeVariable(v *variableElement, st *state) {
+ // Turn the words of the invocation into values.
+ val := make([]interface{}, len(v.word))
+ for i, word := range v.word {
+ val[i] = t.varValue(word, st).Interface()
+ }
+
+ for i, fmt := range v.fmts[:len(v.fmts)-1] {
+ b := &st.buf[i&1]
+ b.Reset()
+ t.format(b, fmt, val, v, st)
+ val = val[0:1]
+ val[0] = b.Bytes()
+ }
+ t.format(st.wr, v.fmts[len(v.fmts)-1], val, v, st)
+}
+
+// Execute element i. Return next index to execute.
+func (t *Template) executeElement(i int, st *state) int {
+ switch elem := t.elems.At(i).(type) {
+ case *textElement:
+ st.wr.Write(elem.text)
+ return i + 1
+ case *literalElement:
+ st.wr.Write(elem.text)
+ return i + 1
+ case *variableElement:
+ t.writeVariable(elem, st)
+ return i + 1
+ case *sectionElement:
+ t.executeSection(elem, st)
+ return elem.end
+ case *repeatedElement:
+ t.executeRepeated(elem, st)
+ return elem.end
+ }
+ e := t.elems.At(i)
+ t.execError(st, 0, "internal error: bad directive in execute: %v %T\n", reflect.NewValue(e).Interface(), e)
+ return 0
+}
+
+// Execute the template.
+func (t *Template) execute(start, end int, st *state) {
+ for i := start; i < end; {
+ i = t.executeElement(i, st)
+ }
+}
+
+// Execute a .section
+func (t *Template) executeSection(s *sectionElement, st *state) {
+ // Find driver data for this section. It must be in the current struct.
+ field := t.varValue(s.field, st)
+ if !field.IsValid() {
+ t.execError(st, s.linenum, ".section: cannot find field %s in %s", s.field, st.data.Type())
+ }
+ st = st.clone(field)
+ start, end := s.start, s.or
+ if !empty(field) {
+ // Execute the normal block.
+ if end < 0 {
+ end = s.end
+ }
+ } else {
+ // Execute the .or block. If it's missing, do nothing.
+ start, end = s.or, s.end
+ if start < 0 {
+ return
+ }
+ }
+ for i := start; i < end; {
+ i = t.executeElement(i, st)
+ }
+}
+
+// Return the result of calling the Iter method on v, or nil.
+func iter(v reflect.Value) reflect.Value {
+ for j := 0; j < v.Type().NumMethod(); j++ {
+ mth := v.Type().Method(j)
+ fv := v.Method(j)
+ ft := fv.Type()
+ // TODO(rsc): NumIn() should return 0 here, because ft is from a curried FuncValue.
+ if mth.Name != "Iter" || ft.NumIn() != 1 || ft.NumOut() != 1 {
+ continue
+ }
+ ct := ft.Out(0)
+ if ct.Kind() != reflect.Chan ||
+ ct.ChanDir()&reflect.RecvDir == 0 {
+ continue
+ }
+ return fv.Call(nil)[0]
+ }
+ return reflect.Value{}
+}
+
+// Execute a .repeated section
+func (t *Template) executeRepeated(r *repeatedElement, st *state) {
+ // Find driver data for this section. It must be in the current struct.
+ field := t.varValue(r.field, st)
+ if !field.IsValid() {
+ t.execError(st, r.linenum, ".repeated: cannot find field %s in %s", r.field, st.data.Type())
+ }
+ field = indirect(field)
+
+ start, end := r.start, r.or
+ if end < 0 {
+ end = r.end
+ }
+ if r.altstart >= 0 {
+ end = r.altstart
+ }
+ first := true
+
+ // Code common to all the loops.
+ loopBody := func(newst *state) {
+ // .alternates between elements
+ if !first && r.altstart >= 0 {
+ for i := r.altstart; i < r.altend; {
+ i = t.executeElement(i, newst)
+ }
+ }
+ first = false
+ for i := start; i < end; {
+ i = t.executeElement(i, newst)
+ }
+ }
+
+ if array := field; array.Kind() == reflect.Array || array.Kind() == reflect.Slice {
+ for j := 0; j < array.Len(); j++ {
+ loopBody(st.clone(array.Index(j)))
+ }
+ } else if m := field; m.Kind() == reflect.Map {
+ for _, key := range m.MapKeys() {
+ loopBody(st.clone(m.MapIndex(key)))
+ }
+ } else if ch := iter(field); ch.IsValid() {
+ for {
+ e, ok := ch.Recv()
+ if !ok {
+ break
+ }
+ loopBody(st.clone(e))
+ }
+ } else {
+ t.execError(st, r.linenum, ".repeated: cannot repeat %s (type %s)",
+ r.field, field.Type())
+ }
+
+ if first {
+ // Empty. Execute the .or block, once. If it's missing, do nothing.
+ start, end := r.or, r.end
+ if start >= 0 {
+ newst := st.clone(field)
+ for i := start; i < end; {
+ i = t.executeElement(i, newst)
+ }
+ }
+ return
+ }
+}
+
+// A valid delimiter must contain no white space and be non-empty.
+func validDelim(d []byte) bool {
+ if len(d) == 0 {
+ return false
+ }
+ for _, c := range d {
+ if white(c) {
+ return false
+ }
+ }
+ return true
+}
+
+// checkError is a deferred function to turn a panic with type *Error into a plain error return.
+// Other panics are unexpected and so are re-enabled.
+func checkError(error *os.Error) {
+ if v := recover(); v != nil {
+ if e, ok := v.(*Error); ok {
+ *error = e
+ } else {
+ // runtime errors should crash
+ panic(v)
+ }
+ }
+}
+
+// -- Public interface
+
+// Parse initializes a Template by parsing its definition. The string
+// s contains the template text. If any errors occur, Parse returns
+// the error.
+func (t *Template) Parse(s string) (err os.Error) {
+ if t.elems == nil {
+ return &Error{1, "template not allocated with New"}
+ }
+ if !validDelim(t.ldelim) || !validDelim(t.rdelim) {
+ return &Error{1, fmt.Sprintf("bad delimiter strings %q %q", t.ldelim, t.rdelim)}
+ }
+ defer checkError(&err)
+ t.buf = []byte(s)
+ t.p = 0
+ t.linenum = 1
+ t.parse()
+ return nil
+}
+
+// ParseFile is like Parse but reads the template definition from the
+// named file.
+func (t *Template) ParseFile(filename string) (err os.Error) {
+ b, err := ioutil.ReadFile(filename)
+ if err != nil {
+ return err
+ }
+ return t.Parse(string(b))
+}
+
+// Execute applies a parsed template to the specified data object,
+// generating output to wr.
+func (t *Template) Execute(wr io.Writer, data interface{}) (err os.Error) {
+ // Extract the driver data.
+ val := reflect.NewValue(data)
+ defer checkError(&err)
+ t.p = 0
+ t.execute(0, t.elems.Len(), &state{parent: nil, data: val, wr: wr})
+ return nil
+}
+
+// SetDelims sets the left and right delimiters for operations in the
+// template. They are validated during parsing. They could be
+// validated here but it's better to keep the routine simple. The
+// delimiters are very rarely invalid and Parse has the necessary
+// error-handling interface already.
+func (t *Template) SetDelims(left, right string) {
+ t.ldelim = []byte(left)
+ t.rdelim = []byte(right)
+}
+
+// Parse creates a Template with default parameters (such as {} for
+// metacharacters). The string s contains the template text while
+// the formatter map fmap, which may be nil, defines auxiliary functions
+// for formatting variables. The template is returned. If any errors
+// occur, err will be non-nil.
+func Parse(s string, fmap FormatterMap) (t *Template, err os.Error) {
+ t = New(fmap)
+ err = t.Parse(s)
+ if err != nil {
+ t = nil
+ }
+ return
+}
+
+// ParseFile is a wrapper function that creates a Template with default
+// parameters (such as {} for metacharacters). The filename identifies
+// a file containing the template text, while the formatter map fmap, which
+// may be nil, defines auxiliary functions for formatting variables.
+// The template is returned. If any errors occur, err will be non-nil.
+func ParseFile(filename string, fmap FormatterMap) (t *Template, err os.Error) {
+ b, err := ioutil.ReadFile(filename)
+ if err != nil {
+ return nil, err
+ }
+ return Parse(string(b), fmap)
+}
+
+// MustParse is like Parse but panics if the template cannot be parsed.
+func MustParse(s string, fmap FormatterMap) *Template {
+ t, err := Parse(s, fmap)
+ if err != nil {
+ panic("template.MustParse error: " + err.String())
+ }
+ return t
+}
+
+// MustParseFile is like ParseFile but panics if the file cannot be read
+// or the template cannot be parsed.
+func MustParseFile(filename string, fmap FormatterMap) *Template {
+ b, err := ioutil.ReadFile(filename)
+ if err != nil {
+ panic("template.MustParseFile error: " + err.String())
+ }
+ return MustParse(string(b), fmap)
+}
diff --git a/src/cmd/gofix/testdata/reflect.type.go.in b/src/cmd/gofix/testdata/reflect.type.go.in
new file mode 100644
index 000000000..305d41980
--- /dev/null
+++ b/src/cmd/gofix/testdata/reflect.type.go.in
@@ -0,0 +1,789 @@
+// 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 (
+ "fmt"
+ "os"
+ "reflect"
+ "sync"
+ "unicode"
+ "utf8"
+)
+
+// userTypeInfo stores the information associated with a type the user has handed
+// to the package. It's computed once and stored in a map keyed by reflection
+// type.
+type userTypeInfo struct {
+ user reflect.Type // the type the user handed us
+ base reflect.Type // the base type after all indirections
+ indir int // number of indirections to reach the base type
+ isGobEncoder bool // does the type implement GobEncoder?
+ isGobDecoder bool // does the type implement GobDecoder?
+ encIndir int8 // number of indirections to reach the receiver type; may be negative
+ decIndir int8 // number of indirections to reach the receiver type; may be negative
+}
+
+var (
+ // Protected by an RWMutex because we read it a lot and write
+ // it only when we see a new type, typically when compiling.
+ userTypeLock sync.RWMutex
+ userTypeCache = make(map[reflect.Type]*userTypeInfo)
+)
+
+// validType returns, and saves, the information associated with user-provided type rt.
+// If the user type is not valid, err will be non-nil. To be used when the error handler
+// is not set up.
+func validUserType(rt reflect.Type) (ut *userTypeInfo, err os.Error) {
+ userTypeLock.RLock()
+ ut = userTypeCache[rt]
+ userTypeLock.RUnlock()
+ if ut != nil {
+ return
+ }
+ // Now set the value under the write lock.
+ userTypeLock.Lock()
+ defer userTypeLock.Unlock()
+ if ut = userTypeCache[rt]; ut != nil {
+ // Lost the race; not a problem.
+ return
+ }
+ ut = new(userTypeInfo)
+ ut.base = rt
+ ut.user = rt
+ // A type that is just a cycle of pointers (such as type T *T) cannot
+ // be represented in gobs, which need some concrete data. We use a
+ // cycle detection algorithm from Knuth, Vol 2, Section 3.1, Ex 6,
+ // pp 539-540. As we step through indirections, run another type at
+ // half speed. If they meet up, there's a cycle.
+ slowpoke := ut.base // walks half as fast as ut.base
+ for {
+ pt, ok := ut.base.(*reflect.PtrType)
+ if !ok {
+ break
+ }
+ ut.base = pt.Elem()
+ if ut.base == slowpoke { // ut.base lapped slowpoke
+ // recursive pointer type.
+ return nil, os.ErrorString("can't represent recursive pointer type " + ut.base.String())
+ }
+ if ut.indir%2 == 0 {
+ slowpoke = slowpoke.(*reflect.PtrType).Elem()
+ }
+ ut.indir++
+ }
+ ut.isGobEncoder, ut.encIndir = implementsInterface(ut.user, gobEncoderCheck)
+ ut.isGobDecoder, ut.decIndir = implementsInterface(ut.user, gobDecoderCheck)
+ userTypeCache[rt] = ut
+ return
+}
+
+const (
+ gobEncodeMethodName = "GobEncode"
+ gobDecodeMethodName = "GobDecode"
+)
+
+// implements returns whether the type implements the interface, as encoded
+// in the check function.
+func implements(typ reflect.Type, check func(typ reflect.Type) bool) bool {
+ if typ.NumMethod() == 0 { // avoid allocations etc. unless there's some chance
+ return false
+ }
+ return check(typ)
+}
+
+// gobEncoderCheck makes the type assertion a boolean function.
+func gobEncoderCheck(typ reflect.Type) bool {
+ _, ok := reflect.MakeZero(typ).Interface().(GobEncoder)
+ return ok
+}
+
+// gobDecoderCheck makes the type assertion a boolean function.
+func gobDecoderCheck(typ reflect.Type) bool {
+ _, ok := reflect.MakeZero(typ).Interface().(GobDecoder)
+ return ok
+}
+
+// implementsInterface reports whether the type implements the
+// interface. (The actual check is done through the provided function.)
+// It also returns the number of indirections required to get to the
+// implementation.
+func implementsInterface(typ reflect.Type, check func(typ reflect.Type) bool) (success bool, indir int8) {
+ if typ == nil {
+ return
+ }
+ rt := typ
+ // The type might be a pointer and we need to keep
+ // dereferencing to the base type until we find an implementation.
+ for {
+ if implements(rt, check) {
+ return true, indir
+ }
+ if p, ok := rt.(*reflect.PtrType); ok {
+ indir++
+ if indir > 100 { // insane number of indirections
+ return false, 0
+ }
+ rt = p.Elem()
+ continue
+ }
+ break
+ }
+ // No luck yet, but if this is a base type (non-pointer), the pointer might satisfy.
+ if _, ok := typ.(*reflect.PtrType); !ok {
+ // Not a pointer, but does the pointer work?
+ if implements(reflect.PtrTo(typ), check) {
+ return true, -1
+ }
+ }
+ return false, 0
+}
+
+// userType returns, and saves, the information associated with user-provided type rt.
+// If the user type is not valid, it calls error.
+func userType(rt reflect.Type) *userTypeInfo {
+ ut, err := validUserType(rt)
+ if err != nil {
+ error(err)
+ }
+ return ut
+}
+// A typeId represents a gob Type as an integer that can be passed on the wire.
+// Internally, typeIds are used as keys to a map to recover the underlying type info.
+type typeId int32
+
+var nextId typeId // incremented for each new type we build
+var typeLock sync.Mutex // set while building a type
+const firstUserId = 64 // lowest id number granted to user
+
+type gobType interface {
+ id() typeId
+ setId(id typeId)
+ name() string
+ string() string // not public; only for debugging
+ safeString(seen map[typeId]bool) string
+}
+
+var types = make(map[reflect.Type]gobType)
+var idToType = make(map[typeId]gobType)
+var builtinIdToType map[typeId]gobType // set in init() after builtins are established
+
+func setTypeId(typ gobType) {
+ nextId++
+ typ.setId(nextId)
+ idToType[nextId] = typ
+}
+
+func (t typeId) gobType() gobType {
+ if t == 0 {
+ return nil
+ }
+ return idToType[t]
+}
+
+// string returns the string representation of the type associated with the typeId.
+func (t typeId) string() string {
+ if t.gobType() == nil {
+ return "<nil>"
+ }
+ return t.gobType().string()
+}
+
+// Name returns the name of the type associated with the typeId.
+func (t typeId) name() string {
+ if t.gobType() == nil {
+ return "<nil>"
+ }
+ return t.gobType().name()
+}
+
+// Common elements of all types.
+type CommonType struct {
+ Name string
+ Id typeId
+}
+
+func (t *CommonType) id() typeId { return t.Id }
+
+func (t *CommonType) setId(id typeId) { t.Id = id }
+
+func (t *CommonType) string() string { return t.Name }
+
+func (t *CommonType) safeString(seen map[typeId]bool) string {
+ return t.Name
+}
+
+func (t *CommonType) name() string { return t.Name }
+
+// Create and check predefined types
+// The string for tBytes is "bytes" not "[]byte" to signify its specialness.
+
+var (
+ // Primordial types, needed during initialization.
+ // Always passed as pointers so the interface{} type
+ // goes through without losing its interfaceness.
+ tBool = bootstrapType("bool", (*bool)(nil), 1)
+ tInt = bootstrapType("int", (*int)(nil), 2)
+ tUint = bootstrapType("uint", (*uint)(nil), 3)
+ tFloat = bootstrapType("float", (*float64)(nil), 4)
+ tBytes = bootstrapType("bytes", (*[]byte)(nil), 5)
+ tString = bootstrapType("string", (*string)(nil), 6)
+ tComplex = bootstrapType("complex", (*complex128)(nil), 7)
+ tInterface = bootstrapType("interface", (*interface{})(nil), 8)
+ // Reserve some Ids for compatible expansion
+ tReserved7 = bootstrapType("_reserved1", (*struct{ r7 int })(nil), 9)
+ tReserved6 = bootstrapType("_reserved1", (*struct{ r6 int })(nil), 10)
+ tReserved5 = bootstrapType("_reserved1", (*struct{ r5 int })(nil), 11)
+ tReserved4 = bootstrapType("_reserved1", (*struct{ r4 int })(nil), 12)
+ tReserved3 = bootstrapType("_reserved1", (*struct{ r3 int })(nil), 13)
+ tReserved2 = bootstrapType("_reserved1", (*struct{ r2 int })(nil), 14)
+ tReserved1 = bootstrapType("_reserved1", (*struct{ r1 int })(nil), 15)
+)
+
+// Predefined because it's needed by the Decoder
+var tWireType = mustGetTypeInfo(reflect.Typeof(wireType{})).id
+var wireTypeUserInfo *userTypeInfo // userTypeInfo of (*wireType)
+
+func init() {
+ // Some magic numbers to make sure there are no surprises.
+ checkId(16, tWireType)
+ checkId(17, mustGetTypeInfo(reflect.Typeof(arrayType{})).id)
+ checkId(18, mustGetTypeInfo(reflect.Typeof(CommonType{})).id)
+ checkId(19, mustGetTypeInfo(reflect.Typeof(sliceType{})).id)
+ checkId(20, mustGetTypeInfo(reflect.Typeof(structType{})).id)
+ checkId(21, mustGetTypeInfo(reflect.Typeof(fieldType{})).id)
+ checkId(23, mustGetTypeInfo(reflect.Typeof(mapType{})).id)
+
+ builtinIdToType = make(map[typeId]gobType)
+ for k, v := range idToType {
+ builtinIdToType[k] = v
+ }
+
+ // Move the id space upwards to allow for growth in the predefined world
+ // without breaking existing files.
+ if nextId > firstUserId {
+ panic(fmt.Sprintln("nextId too large:", nextId))
+ }
+ nextId = firstUserId
+ registerBasics()
+ wireTypeUserInfo = userType(reflect.Typeof((*wireType)(nil)))
+}
+
+// Array type
+type arrayType struct {
+ CommonType
+ Elem typeId
+ Len int
+}
+
+func newArrayType(name string) *arrayType {
+ a := &arrayType{CommonType{Name: name}, 0, 0}
+ return a
+}
+
+func (a *arrayType) init(elem gobType, len int) {
+ // Set our type id before evaluating the element's, in case it's our own.
+ setTypeId(a)
+ a.Elem = elem.id()
+ a.Len = len
+}
+
+func (a *arrayType) safeString(seen map[typeId]bool) string {
+ if seen[a.Id] {
+ return a.Name
+ }
+ seen[a.Id] = true
+ return fmt.Sprintf("[%d]%s", a.Len, a.Elem.gobType().safeString(seen))
+}
+
+func (a *arrayType) string() string { return a.safeString(make(map[typeId]bool)) }
+
+// GobEncoder type (something that implements the GobEncoder interface)
+type gobEncoderType struct {
+ CommonType
+}
+
+func newGobEncoderType(name string) *gobEncoderType {
+ g := &gobEncoderType{CommonType{Name: name}}
+ setTypeId(g)
+ return g
+}
+
+func (g *gobEncoderType) safeString(seen map[typeId]bool) string {
+ return g.Name
+}
+
+func (g *gobEncoderType) string() string { return g.Name }
+
+// Map type
+type mapType struct {
+ CommonType
+ Key typeId
+ Elem typeId
+}
+
+func newMapType(name string) *mapType {
+ m := &mapType{CommonType{Name: name}, 0, 0}
+ return m
+}
+
+func (m *mapType) init(key, elem gobType) {
+ // Set our type id before evaluating the element's, in case it's our own.
+ setTypeId(m)
+ m.Key = key.id()
+ m.Elem = elem.id()
+}
+
+func (m *mapType) safeString(seen map[typeId]bool) string {
+ if seen[m.Id] {
+ return m.Name
+ }
+ seen[m.Id] = true
+ key := m.Key.gobType().safeString(seen)
+ elem := m.Elem.gobType().safeString(seen)
+ return fmt.Sprintf("map[%s]%s", key, elem)
+}
+
+func (m *mapType) string() string { return m.safeString(make(map[typeId]bool)) }
+
+// Slice type
+type sliceType struct {
+ CommonType
+ Elem typeId
+}
+
+func newSliceType(name string) *sliceType {
+ s := &sliceType{CommonType{Name: name}, 0}
+ return s
+}
+
+func (s *sliceType) init(elem gobType) {
+ // Set our type id before evaluating the element's, in case it's our own.
+ setTypeId(s)
+ s.Elem = elem.id()
+}
+
+func (s *sliceType) safeString(seen map[typeId]bool) string {
+ if seen[s.Id] {
+ return s.Name
+ }
+ seen[s.Id] = true
+ return fmt.Sprintf("[]%s", s.Elem.gobType().safeString(seen))
+}
+
+func (s *sliceType) string() string { return s.safeString(make(map[typeId]bool)) }
+
+// Struct type
+type fieldType struct {
+ Name string
+ Id typeId
+}
+
+type structType struct {
+ CommonType
+ Field []*fieldType
+}
+
+func (s *structType) safeString(seen map[typeId]bool) string {
+ if s == nil {
+ return "<nil>"
+ }
+ if _, ok := seen[s.Id]; ok {
+ return s.Name
+ }
+ seen[s.Id] = true
+ str := s.Name + " = struct { "
+ for _, f := range s.Field {
+ str += fmt.Sprintf("%s %s; ", f.Name, f.Id.gobType().safeString(seen))
+ }
+ str += "}"
+ return str
+}
+
+func (s *structType) string() string { return s.safeString(make(map[typeId]bool)) }
+
+func newStructType(name string) *structType {
+ s := &structType{CommonType{Name: name}, nil}
+ // For historical reasons we set the id here rather than init.
+ // See the comment in newTypeObject for details.
+ setTypeId(s)
+ return s
+}
+
+// newTypeObject allocates a gobType for the reflection type rt.
+// Unless ut represents a GobEncoder, rt should be the base type
+// of ut.
+// This is only called from the encoding side. The decoding side
+// works through typeIds and userTypeInfos alone.
+func newTypeObject(name string, ut *userTypeInfo, rt reflect.Type) (gobType, os.Error) {
+ // Does this type implement GobEncoder?
+ if ut.isGobEncoder {
+ return newGobEncoderType(name), nil
+ }
+ var err os.Error
+ var type0, type1 gobType
+ defer func() {
+ if err != nil {
+ types[rt] = nil, false
+ }
+ }()
+ // Install the top-level type before the subtypes (e.g. struct before
+ // fields) so recursive types can be constructed safely.
+ switch t := rt.(type) {
+ // All basic types are easy: they are predefined.
+ case *reflect.BoolType:
+ return tBool.gobType(), nil
+
+ case *reflect.IntType:
+ return tInt.gobType(), nil
+
+ case *reflect.UintType:
+ return tUint.gobType(), nil
+
+ case *reflect.FloatType:
+ return tFloat.gobType(), nil
+
+ case *reflect.ComplexType:
+ return tComplex.gobType(), nil
+
+ case *reflect.StringType:
+ return tString.gobType(), nil
+
+ case *reflect.InterfaceType:
+ return tInterface.gobType(), nil
+
+ case *reflect.ArrayType:
+ at := newArrayType(name)
+ types[rt] = at
+ type0, err = getBaseType("", t.Elem())
+ if err != nil {
+ return nil, err
+ }
+ // Historical aside:
+ // For arrays, maps, and slices, we set the type id after the elements
+ // are constructed. This is to retain the order of type id allocation after
+ // a fix made to handle recursive types, which changed the order in
+ // which types are built. Delaying the setting in this way preserves
+ // type ids while allowing recursive types to be described. Structs,
+ // done below, were already handling recursion correctly so they
+ // assign the top-level id before those of the field.
+ at.init(type0, t.Len())
+ return at, nil
+
+ case *reflect.MapType:
+ mt := newMapType(name)
+ types[rt] = mt
+ type0, err = getBaseType("", t.Key())
+ if err != nil {
+ return nil, err
+ }
+ type1, err = getBaseType("", t.Elem())
+ if err != nil {
+ return nil, err
+ }
+ mt.init(type0, type1)
+ return mt, nil
+
+ case *reflect.SliceType:
+ // []byte == []uint8 is a special case
+ if t.Elem().Kind() == reflect.Uint8 {
+ return tBytes.gobType(), nil
+ }
+ st := newSliceType(name)
+ types[rt] = st
+ type0, err = getBaseType(t.Elem().Name(), t.Elem())
+ if err != nil {
+ return nil, err
+ }
+ st.init(type0)
+ return st, nil
+
+ case *reflect.StructType:
+ st := newStructType(name)
+ types[rt] = st
+ idToType[st.id()] = st
+ for i := 0; i < t.NumField(); i++ {
+ f := t.Field(i)
+ if !isExported(f.Name) {
+ continue
+ }
+ typ := userType(f.Type).base
+ tname := typ.Name()
+ if tname == "" {
+ t := userType(f.Type).base
+ tname = t.String()
+ }
+ gt, err := getBaseType(tname, f.Type)
+ if err != nil {
+ return nil, err
+ }
+ st.Field = append(st.Field, &fieldType{f.Name, gt.id()})
+ }
+ return st, nil
+
+ default:
+ return nil, os.ErrorString("gob NewTypeObject can't handle type: " + rt.String())
+ }
+ return nil, nil
+}
+
+// isExported reports whether this is an exported - upper case - name.
+func isExported(name string) bool {
+ rune, _ := utf8.DecodeRuneInString(name)
+ return unicode.IsUpper(rune)
+}
+
+// getBaseType returns the Gob type describing the given reflect.Type's base type.
+// typeLock must be held.
+func getBaseType(name string, rt reflect.Type) (gobType, os.Error) {
+ ut := userType(rt)
+ return getType(name, ut, ut.base)
+}
+
+// getType returns the Gob type describing the given reflect.Type.
+// Should be called only when handling GobEncoders/Decoders,
+// which may be pointers. All other types are handled through the
+// base type, never a pointer.
+// typeLock must be held.
+func getType(name string, ut *userTypeInfo, rt reflect.Type) (gobType, os.Error) {
+ typ, present := types[rt]
+ if present {
+ return typ, nil
+ }
+ typ, err := newTypeObject(name, ut, rt)
+ if err == nil {
+ types[rt] = typ
+ }
+ return typ, err
+}
+
+func checkId(want, got typeId) {
+ if want != got {
+ fmt.Fprintf(os.Stderr, "checkId: %d should be %d\n", int(got), int(want))
+ panic("bootstrap type wrong id: " + got.name() + " " + got.string() + " not " + want.string())
+ }
+}
+
+// used for building the basic types; called only from init(). the incoming
+// interface always refers to a pointer.
+func bootstrapType(name string, e interface{}, expect typeId) typeId {
+ rt := reflect.Typeof(e).(*reflect.PtrType).Elem()
+ _, present := types[rt]
+ if present {
+ panic("bootstrap type already present: " + name + ", " + rt.String())
+ }
+ typ := &CommonType{Name: name}
+ types[rt] = typ
+ setTypeId(typ)
+ checkId(expect, nextId)
+ userType(rt) // might as well cache it now
+ return nextId
+}
+
+// Representation of the information we send and receive about this type.
+// Each value we send is preceded by its type definition: an encoded int.
+// However, the very first time we send the value, we first send the pair
+// (-id, wireType).
+// For bootstrapping purposes, we assume that the recipient knows how
+// to decode a wireType; it is exactly the wireType struct here, interpreted
+// using the gob rules for sending a structure, except that we assume the
+// ids for wireType and structType etc. are known. The relevant pieces
+// are built in encode.go's init() function.
+// To maintain binary compatibility, if you extend this type, always put
+// the new fields last.
+type wireType struct {
+ ArrayT *arrayType
+ SliceT *sliceType
+ StructT *structType
+ MapT *mapType
+ GobEncoderT *gobEncoderType
+}
+
+func (w *wireType) string() string {
+ const unknown = "unknown type"
+ if w == nil {
+ return unknown
+ }
+ switch {
+ case w.ArrayT != nil:
+ return w.ArrayT.Name
+ case w.SliceT != nil:
+ return w.SliceT.Name
+ case w.StructT != nil:
+ return w.StructT.Name
+ case w.MapT != nil:
+ return w.MapT.Name
+ case w.GobEncoderT != nil:
+ return w.GobEncoderT.Name
+ }
+ return unknown
+}
+
+type typeInfo struct {
+ id typeId
+ encoder *encEngine
+ wire *wireType
+}
+
+var typeInfoMap = make(map[reflect.Type]*typeInfo) // protected by typeLock
+
+// typeLock must be held.
+func getTypeInfo(ut *userTypeInfo) (*typeInfo, os.Error) {
+ rt := ut.base
+ if ut.isGobEncoder {
+ // We want the user type, not the base type.
+ rt = ut.user
+ }
+ info, ok := typeInfoMap[rt]
+ if ok {
+ return info, nil
+ }
+ info = new(typeInfo)
+ gt, err := getBaseType(rt.Name(), rt)
+ if err != nil {
+ return nil, err
+ }
+ info.id = gt.id()
+
+ if ut.isGobEncoder {
+ userType, err := getType(rt.Name(), ut, rt)
+ if err != nil {
+ return nil, err
+ }
+ info.wire = &wireType{GobEncoderT: userType.id().gobType().(*gobEncoderType)}
+ typeInfoMap[ut.user] = info
+ return info, nil
+ }
+
+ t := info.id.gobType()
+ switch typ := rt.(type) {
+ case *reflect.ArrayType:
+ info.wire = &wireType{ArrayT: t.(*arrayType)}
+ case *reflect.MapType:
+ info.wire = &wireType{MapT: t.(*mapType)}
+ case *reflect.SliceType:
+ // []byte == []uint8 is a special case handled separately
+ if typ.Elem().Kind() != reflect.Uint8 {
+ info.wire = &wireType{SliceT: t.(*sliceType)}
+ }
+ case *reflect.StructType:
+ info.wire = &wireType{StructT: t.(*structType)}
+ }
+ typeInfoMap[rt] = info
+ return info, nil
+}
+
+// Called only when a panic is acceptable and unexpected.
+func mustGetTypeInfo(rt reflect.Type) *typeInfo {
+ t, err := getTypeInfo(userType(rt))
+ if err != nil {
+ panic("getTypeInfo: " + err.String())
+ }
+ return t
+}
+
+// GobEncoder is the interface describing data that provides its own
+// representation for encoding values for transmission to a GobDecoder.
+// A type that implements GobEncoder and GobDecoder has complete
+// control over the representation of its data and may therefore
+// contain things such as private fields, channels, and functions,
+// which are not usually transmissable in gob streams.
+//
+// Note: Since gobs can be stored permanently, It is good design
+// to guarantee the encoding used by a GobEncoder is stable as the
+// software evolves. For instance, it might make sense for GobEncode
+// to include a version number in the encoding.
+type GobEncoder interface {
+ // GobEncode returns a byte slice representing the encoding of the
+ // receiver for transmission to a GobDecoder, usually of the same
+ // concrete type.
+ GobEncode() ([]byte, os.Error)
+}
+
+// GobDecoder is the interface describing data that provides its own
+// routine for decoding transmitted values sent by a GobEncoder.
+type GobDecoder interface {
+ // GobDecode overwrites the receiver, which must be a pointer,
+ // with the value represented by the byte slice, which was written
+ // by GobEncode, usually for the same concrete type.
+ GobDecode([]byte) os.Error
+}
+
+var (
+ nameToConcreteType = make(map[string]reflect.Type)
+ concreteTypeToName = make(map[reflect.Type]string)
+)
+
+// RegisterName is like Register but uses the provided name rather than the
+// type's default.
+func RegisterName(name string, value interface{}) {
+ if name == "" {
+ // reserved for nil
+ panic("attempt to register empty name")
+ }
+ base := userType(reflect.Typeof(value)).base
+ // Check for incompatible duplicates.
+ if t, ok := nameToConcreteType[name]; ok && t != base {
+ panic("gob: registering duplicate types for " + name)
+ }
+ if n, ok := concreteTypeToName[base]; ok && n != name {
+ panic("gob: registering duplicate names for " + base.String())
+ }
+ // Store the name and type provided by the user....
+ nameToConcreteType[name] = reflect.Typeof(value)
+ // but the flattened type in the type table, since that's what decode needs.
+ concreteTypeToName[base] = name
+}
+
+// Register records a type, identified by a value for that type, under its
+// internal type name. That name will identify the concrete type of a value
+// sent or received as an interface variable. Only types that will be
+// transferred as implementations of interface values need to be registered.
+// Expecting to be used only during initialization, it panics if the mapping
+// between types and names is not a bijection.
+func Register(value interface{}) {
+ // Default to printed representation for unnamed types
+ rt := reflect.Typeof(value)
+ name := rt.String()
+
+ // But for named types (or pointers to them), qualify with import path.
+ // Dereference one pointer looking for a named type.
+ star := ""
+ if rt.Name() == "" {
+ if pt, ok := rt.(*reflect.PtrType); ok {
+ star = "*"
+ rt = pt
+ }
+ }
+ if rt.Name() != "" {
+ if rt.PkgPath() == "" {
+ name = star + rt.Name()
+ } else {
+ name = star + rt.PkgPath() + "." + rt.Name()
+ }
+ }
+
+ RegisterName(name, value)
+}
+
+func registerBasics() {
+ Register(int(0))
+ Register(int8(0))
+ Register(int16(0))
+ Register(int32(0))
+ Register(int64(0))
+ Register(uint(0))
+ Register(uint8(0))
+ Register(uint16(0))
+ Register(uint32(0))
+ Register(uint64(0))
+ Register(float32(0))
+ Register(float64(0))
+ Register(complex64(0i))
+ Register(complex128(0i))
+ Register(false)
+ Register("")
+ Register([]byte(nil))
+}
diff --git a/src/cmd/gofix/testdata/reflect.type.go.out b/src/cmd/gofix/testdata/reflect.type.go.out
new file mode 100644
index 000000000..8fd174841
--- /dev/null
+++ b/src/cmd/gofix/testdata/reflect.type.go.out
@@ -0,0 +1,789 @@
+// 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 (
+ "fmt"
+ "os"
+ "reflect"
+ "sync"
+ "unicode"
+ "utf8"
+)
+
+// userTypeInfo stores the information associated with a type the user has handed
+// to the package. It's computed once and stored in a map keyed by reflection
+// type.
+type userTypeInfo struct {
+ user reflect.Type // the type the user handed us
+ base reflect.Type // the base type after all indirections
+ indir int // number of indirections to reach the base type
+ isGobEncoder bool // does the type implement GobEncoder?
+ isGobDecoder bool // does the type implement GobDecoder?
+ encIndir int8 // number of indirections to reach the receiver type; may be negative
+ decIndir int8 // number of indirections to reach the receiver type; may be negative
+}
+
+var (
+ // Protected by an RWMutex because we read it a lot and write
+ // it only when we see a new type, typically when compiling.
+ userTypeLock sync.RWMutex
+ userTypeCache = make(map[reflect.Type]*userTypeInfo)
+)
+
+// validType returns, and saves, the information associated with user-provided type rt.
+// If the user type is not valid, err will be non-nil. To be used when the error handler
+// is not set up.
+func validUserType(rt reflect.Type) (ut *userTypeInfo, err os.Error) {
+ userTypeLock.RLock()
+ ut = userTypeCache[rt]
+ userTypeLock.RUnlock()
+ if ut != nil {
+ return
+ }
+ // Now set the value under the write lock.
+ userTypeLock.Lock()
+ defer userTypeLock.Unlock()
+ if ut = userTypeCache[rt]; ut != nil {
+ // Lost the race; not a problem.
+ return
+ }
+ ut = new(userTypeInfo)
+ ut.base = rt
+ ut.user = rt
+ // A type that is just a cycle of pointers (such as type T *T) cannot
+ // be represented in gobs, which need some concrete data. We use a
+ // cycle detection algorithm from Knuth, Vol 2, Section 3.1, Ex 6,
+ // pp 539-540. As we step through indirections, run another type at
+ // half speed. If they meet up, there's a cycle.
+ slowpoke := ut.base // walks half as fast as ut.base
+ for {
+ pt := ut.base
+ if pt.Kind() != reflect.Ptr {
+ break
+ }
+ ut.base = pt.Elem()
+ if ut.base == slowpoke { // ut.base lapped slowpoke
+ // recursive pointer type.
+ return nil, os.ErrorString("can't represent recursive pointer type " + ut.base.String())
+ }
+ if ut.indir%2 == 0 {
+ slowpoke = slowpoke.Elem()
+ }
+ ut.indir++
+ }
+ ut.isGobEncoder, ut.encIndir = implementsInterface(ut.user, gobEncoderCheck)
+ ut.isGobDecoder, ut.decIndir = implementsInterface(ut.user, gobDecoderCheck)
+ userTypeCache[rt] = ut
+ return
+}
+
+const (
+ gobEncodeMethodName = "GobEncode"
+ gobDecodeMethodName = "GobDecode"
+)
+
+// implements returns whether the type implements the interface, as encoded
+// in the check function.
+func implements(typ reflect.Type, check func(typ reflect.Type) bool) bool {
+ if typ.NumMethod() == 0 { // avoid allocations etc. unless there's some chance
+ return false
+ }
+ return check(typ)
+}
+
+// gobEncoderCheck makes the type assertion a boolean function.
+func gobEncoderCheck(typ reflect.Type) bool {
+ _, ok := reflect.Zero(typ).Interface().(GobEncoder)
+ return ok
+}
+
+// gobDecoderCheck makes the type assertion a boolean function.
+func gobDecoderCheck(typ reflect.Type) bool {
+ _, ok := reflect.Zero(typ).Interface().(GobDecoder)
+ return ok
+}
+
+// implementsInterface reports whether the type implements the
+// interface. (The actual check is done through the provided function.)
+// It also returns the number of indirections required to get to the
+// implementation.
+func implementsInterface(typ reflect.Type, check func(typ reflect.Type) bool) (success bool, indir int8) {
+ if typ == nil {
+ return
+ }
+ rt := typ
+ // The type might be a pointer and we need to keep
+ // dereferencing to the base type until we find an implementation.
+ for {
+ if implements(rt, check) {
+ return true, indir
+ }
+ if p := rt; p.Kind() == reflect.Ptr {
+ indir++
+ if indir > 100 { // insane number of indirections
+ return false, 0
+ }
+ rt = p.Elem()
+ continue
+ }
+ break
+ }
+ // No luck yet, but if this is a base type (non-pointer), the pointer might satisfy.
+ if typ.Kind() != reflect.Ptr {
+ // Not a pointer, but does the pointer work?
+ if implements(reflect.PtrTo(typ), check) {
+ return true, -1
+ }
+ }
+ return false, 0
+}
+
+// userType returns, and saves, the information associated with user-provided type rt.
+// If the user type is not valid, it calls error.
+func userType(rt reflect.Type) *userTypeInfo {
+ ut, err := validUserType(rt)
+ if err != nil {
+ error(err)
+ }
+ return ut
+}
+// A typeId represents a gob Type as an integer that can be passed on the wire.
+// Internally, typeIds are used as keys to a map to recover the underlying type info.
+type typeId int32
+
+var nextId typeId // incremented for each new type we build
+var typeLock sync.Mutex // set while building a type
+const firstUserId = 64 // lowest id number granted to user
+
+type gobType interface {
+ id() typeId
+ setId(id typeId)
+ name() string
+ string() string // not public; only for debugging
+ safeString(seen map[typeId]bool) string
+}
+
+var types = make(map[reflect.Type]gobType)
+var idToType = make(map[typeId]gobType)
+var builtinIdToType map[typeId]gobType // set in init() after builtins are established
+
+func setTypeId(typ gobType) {
+ nextId++
+ typ.setId(nextId)
+ idToType[nextId] = typ
+}
+
+func (t typeId) gobType() gobType {
+ if t == 0 {
+ return nil
+ }
+ return idToType[t]
+}
+
+// string returns the string representation of the type associated with the typeId.
+func (t typeId) string() string {
+ if t.gobType() == nil {
+ return "<nil>"
+ }
+ return t.gobType().string()
+}
+
+// Name returns the name of the type associated with the typeId.
+func (t typeId) name() string {
+ if t.gobType() == nil {
+ return "<nil>"
+ }
+ return t.gobType().name()
+}
+
+// Common elements of all types.
+type CommonType struct {
+ Name string
+ Id typeId
+}
+
+func (t *CommonType) id() typeId { return t.Id }
+
+func (t *CommonType) setId(id typeId) { t.Id = id }
+
+func (t *CommonType) string() string { return t.Name }
+
+func (t *CommonType) safeString(seen map[typeId]bool) string {
+ return t.Name
+}
+
+func (t *CommonType) name() string { return t.Name }
+
+// Create and check predefined types
+// The string for tBytes is "bytes" not "[]byte" to signify its specialness.
+
+var (
+ // Primordial types, needed during initialization.
+ // Always passed as pointers so the interface{} type
+ // goes through without losing its interfaceness.
+ tBool = bootstrapType("bool", (*bool)(nil), 1)
+ tInt = bootstrapType("int", (*int)(nil), 2)
+ tUint = bootstrapType("uint", (*uint)(nil), 3)
+ tFloat = bootstrapType("float", (*float64)(nil), 4)
+ tBytes = bootstrapType("bytes", (*[]byte)(nil), 5)
+ tString = bootstrapType("string", (*string)(nil), 6)
+ tComplex = bootstrapType("complex", (*complex128)(nil), 7)
+ tInterface = bootstrapType("interface", (*interface{})(nil), 8)
+ // Reserve some Ids for compatible expansion
+ tReserved7 = bootstrapType("_reserved1", (*struct{ r7 int })(nil), 9)
+ tReserved6 = bootstrapType("_reserved1", (*struct{ r6 int })(nil), 10)
+ tReserved5 = bootstrapType("_reserved1", (*struct{ r5 int })(nil), 11)
+ tReserved4 = bootstrapType("_reserved1", (*struct{ r4 int })(nil), 12)
+ tReserved3 = bootstrapType("_reserved1", (*struct{ r3 int })(nil), 13)
+ tReserved2 = bootstrapType("_reserved1", (*struct{ r2 int })(nil), 14)
+ tReserved1 = bootstrapType("_reserved1", (*struct{ r1 int })(nil), 15)
+)
+
+// Predefined because it's needed by the Decoder
+var tWireType = mustGetTypeInfo(reflect.Typeof(wireType{})).id
+var wireTypeUserInfo *userTypeInfo // userTypeInfo of (*wireType)
+
+func init() {
+ // Some magic numbers to make sure there are no surprises.
+ checkId(16, tWireType)
+ checkId(17, mustGetTypeInfo(reflect.Typeof(arrayType{})).id)
+ checkId(18, mustGetTypeInfo(reflect.Typeof(CommonType{})).id)
+ checkId(19, mustGetTypeInfo(reflect.Typeof(sliceType{})).id)
+ checkId(20, mustGetTypeInfo(reflect.Typeof(structType{})).id)
+ checkId(21, mustGetTypeInfo(reflect.Typeof(fieldType{})).id)
+ checkId(23, mustGetTypeInfo(reflect.Typeof(mapType{})).id)
+
+ builtinIdToType = make(map[typeId]gobType)
+ for k, v := range idToType {
+ builtinIdToType[k] = v
+ }
+
+ // Move the id space upwards to allow for growth in the predefined world
+ // without breaking existing files.
+ if nextId > firstUserId {
+ panic(fmt.Sprintln("nextId too large:", nextId))
+ }
+ nextId = firstUserId
+ registerBasics()
+ wireTypeUserInfo = userType(reflect.Typeof((*wireType)(nil)))
+}
+
+// Array type
+type arrayType struct {
+ CommonType
+ Elem typeId
+ Len int
+}
+
+func newArrayType(name string) *arrayType {
+ a := &arrayType{CommonType{Name: name}, 0, 0}
+ return a
+}
+
+func (a *arrayType) init(elem gobType, len int) {
+ // Set our type id before evaluating the element's, in case it's our own.
+ setTypeId(a)
+ a.Elem = elem.id()
+ a.Len = len
+}
+
+func (a *arrayType) safeString(seen map[typeId]bool) string {
+ if seen[a.Id] {
+ return a.Name
+ }
+ seen[a.Id] = true
+ return fmt.Sprintf("[%d]%s", a.Len, a.Elem.gobType().safeString(seen))
+}
+
+func (a *arrayType) string() string { return a.safeString(make(map[typeId]bool)) }
+
+// GobEncoder type (something that implements the GobEncoder interface)
+type gobEncoderType struct {
+ CommonType
+}
+
+func newGobEncoderType(name string) *gobEncoderType {
+ g := &gobEncoderType{CommonType{Name: name}}
+ setTypeId(g)
+ return g
+}
+
+func (g *gobEncoderType) safeString(seen map[typeId]bool) string {
+ return g.Name
+}
+
+func (g *gobEncoderType) string() string { return g.Name }
+
+// Map type
+type mapType struct {
+ CommonType
+ Key typeId
+ Elem typeId
+}
+
+func newMapType(name string) *mapType {
+ m := &mapType{CommonType{Name: name}, 0, 0}
+ return m
+}
+
+func (m *mapType) init(key, elem gobType) {
+ // Set our type id before evaluating the element's, in case it's our own.
+ setTypeId(m)
+ m.Key = key.id()
+ m.Elem = elem.id()
+}
+
+func (m *mapType) safeString(seen map[typeId]bool) string {
+ if seen[m.Id] {
+ return m.Name
+ }
+ seen[m.Id] = true
+ key := m.Key.gobType().safeString(seen)
+ elem := m.Elem.gobType().safeString(seen)
+ return fmt.Sprintf("map[%s]%s", key, elem)
+}
+
+func (m *mapType) string() string { return m.safeString(make(map[typeId]bool)) }
+
+// Slice type
+type sliceType struct {
+ CommonType
+ Elem typeId
+}
+
+func newSliceType(name string) *sliceType {
+ s := &sliceType{CommonType{Name: name}, 0}
+ return s
+}
+
+func (s *sliceType) init(elem gobType) {
+ // Set our type id before evaluating the element's, in case it's our own.
+ setTypeId(s)
+ s.Elem = elem.id()
+}
+
+func (s *sliceType) safeString(seen map[typeId]bool) string {
+ if seen[s.Id] {
+ return s.Name
+ }
+ seen[s.Id] = true
+ return fmt.Sprintf("[]%s", s.Elem.gobType().safeString(seen))
+}
+
+func (s *sliceType) string() string { return s.safeString(make(map[typeId]bool)) }
+
+// Struct type
+type fieldType struct {
+ Name string
+ Id typeId
+}
+
+type structType struct {
+ CommonType
+ Field []*fieldType
+}
+
+func (s *structType) safeString(seen map[typeId]bool) string {
+ if s == nil {
+ return "<nil>"
+ }
+ if _, ok := seen[s.Id]; ok {
+ return s.Name
+ }
+ seen[s.Id] = true
+ str := s.Name + " = struct { "
+ for _, f := range s.Field {
+ str += fmt.Sprintf("%s %s; ", f.Name, f.Id.gobType().safeString(seen))
+ }
+ str += "}"
+ return str
+}
+
+func (s *structType) string() string { return s.safeString(make(map[typeId]bool)) }
+
+func newStructType(name string) *structType {
+ s := &structType{CommonType{Name: name}, nil}
+ // For historical reasons we set the id here rather than init.
+ // See the comment in newTypeObject for details.
+ setTypeId(s)
+ return s
+}
+
+// newTypeObject allocates a gobType for the reflection type rt.
+// Unless ut represents a GobEncoder, rt should be the base type
+// of ut.
+// This is only called from the encoding side. The decoding side
+// works through typeIds and userTypeInfos alone.
+func newTypeObject(name string, ut *userTypeInfo, rt reflect.Type) (gobType, os.Error) {
+ // Does this type implement GobEncoder?
+ if ut.isGobEncoder {
+ return newGobEncoderType(name), nil
+ }
+ var err os.Error
+ var type0, type1 gobType
+ defer func() {
+ if err != nil {
+ types[rt] = nil, false
+ }
+ }()
+ // Install the top-level type before the subtypes (e.g. struct before
+ // fields) so recursive types can be constructed safely.
+ switch t := rt; t.Kind() {
+ // All basic types are easy: they are predefined.
+ case reflect.Bool:
+ return tBool.gobType(), nil
+
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return tInt.gobType(), nil
+
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ return tUint.gobType(), nil
+
+ case reflect.Float32, reflect.Float64:
+ return tFloat.gobType(), nil
+
+ case reflect.Complex64, reflect.Complex128:
+ return tComplex.gobType(), nil
+
+ case reflect.String:
+ return tString.gobType(), nil
+
+ case reflect.Interface:
+ return tInterface.gobType(), nil
+
+ case reflect.Array:
+ at := newArrayType(name)
+ types[rt] = at
+ type0, err = getBaseType("", t.Elem())
+ if err != nil {
+ return nil, err
+ }
+ // Historical aside:
+ // For arrays, maps, and slices, we set the type id after the elements
+ // are constructed. This is to retain the order of type id allocation after
+ // a fix made to handle recursive types, which changed the order in
+ // which types are built. Delaying the setting in this way preserves
+ // type ids while allowing recursive types to be described. Structs,
+ // done below, were already handling recursion correctly so they
+ // assign the top-level id before those of the field.
+ at.init(type0, t.Len())
+ return at, nil
+
+ case reflect.Map:
+ mt := newMapType(name)
+ types[rt] = mt
+ type0, err = getBaseType("", t.Key())
+ if err != nil {
+ return nil, err
+ }
+ type1, err = getBaseType("", t.Elem())
+ if err != nil {
+ return nil, err
+ }
+ mt.init(type0, type1)
+ return mt, nil
+
+ case reflect.Slice:
+ // []byte == []uint8 is a special case
+ if t.Elem().Kind() == reflect.Uint8 {
+ return tBytes.gobType(), nil
+ }
+ st := newSliceType(name)
+ types[rt] = st
+ type0, err = getBaseType(t.Elem().Name(), t.Elem())
+ if err != nil {
+ return nil, err
+ }
+ st.init(type0)
+ return st, nil
+
+ case reflect.Struct:
+ st := newStructType(name)
+ types[rt] = st
+ idToType[st.id()] = st
+ for i := 0; i < t.NumField(); i++ {
+ f := t.Field(i)
+ if !isExported(f.Name) {
+ continue
+ }
+ typ := userType(f.Type).base
+ tname := typ.Name()
+ if tname == "" {
+ t := userType(f.Type).base
+ tname = t.String()
+ }
+ gt, err := getBaseType(tname, f.Type)
+ if err != nil {
+ return nil, err
+ }
+ st.Field = append(st.Field, &fieldType{f.Name, gt.id()})
+ }
+ return st, nil
+
+ default:
+ return nil, os.ErrorString("gob NewTypeObject can't handle type: " + rt.String())
+ }
+ return nil, nil
+}
+
+// isExported reports whether this is an exported - upper case - name.
+func isExported(name string) bool {
+ rune, _ := utf8.DecodeRuneInString(name)
+ return unicode.IsUpper(rune)
+}
+
+// getBaseType returns the Gob type describing the given reflect.Type's base type.
+// typeLock must be held.
+func getBaseType(name string, rt reflect.Type) (gobType, os.Error) {
+ ut := userType(rt)
+ return getType(name, ut, ut.base)
+}
+
+// getType returns the Gob type describing the given reflect.Type.
+// Should be called only when handling GobEncoders/Decoders,
+// which may be pointers. All other types are handled through the
+// base type, never a pointer.
+// typeLock must be held.
+func getType(name string, ut *userTypeInfo, rt reflect.Type) (gobType, os.Error) {
+ typ, present := types[rt]
+ if present {
+ return typ, nil
+ }
+ typ, err := newTypeObject(name, ut, rt)
+ if err == nil {
+ types[rt] = typ
+ }
+ return typ, err
+}
+
+func checkId(want, got typeId) {
+ if want != got {
+ fmt.Fprintf(os.Stderr, "checkId: %d should be %d\n", int(got), int(want))
+ panic("bootstrap type wrong id: " + got.name() + " " + got.string() + " not " + want.string())
+ }
+}
+
+// used for building the basic types; called only from init(). the incoming
+// interface always refers to a pointer.
+func bootstrapType(name string, e interface{}, expect typeId) typeId {
+ rt := reflect.Typeof(e).Elem()
+ _, present := types[rt]
+ if present {
+ panic("bootstrap type already present: " + name + ", " + rt.String())
+ }
+ typ := &CommonType{Name: name}
+ types[rt] = typ
+ setTypeId(typ)
+ checkId(expect, nextId)
+ userType(rt) // might as well cache it now
+ return nextId
+}
+
+// Representation of the information we send and receive about this type.
+// Each value we send is preceded by its type definition: an encoded int.
+// However, the very first time we send the value, we first send the pair
+// (-id, wireType).
+// For bootstrapping purposes, we assume that the recipient knows how
+// to decode a wireType; it is exactly the wireType struct here, interpreted
+// using the gob rules for sending a structure, except that we assume the
+// ids for wireType and structType etc. are known. The relevant pieces
+// are built in encode.go's init() function.
+// To maintain binary compatibility, if you extend this type, always put
+// the new fields last.
+type wireType struct {
+ ArrayT *arrayType
+ SliceT *sliceType
+ StructT *structType
+ MapT *mapType
+ GobEncoderT *gobEncoderType
+}
+
+func (w *wireType) string() string {
+ const unknown = "unknown type"
+ if w == nil {
+ return unknown
+ }
+ switch {
+ case w.ArrayT != nil:
+ return w.ArrayT.Name
+ case w.SliceT != nil:
+ return w.SliceT.Name
+ case w.StructT != nil:
+ return w.StructT.Name
+ case w.MapT != nil:
+ return w.MapT.Name
+ case w.GobEncoderT != nil:
+ return w.GobEncoderT.Name
+ }
+ return unknown
+}
+
+type typeInfo struct {
+ id typeId
+ encoder *encEngine
+ wire *wireType
+}
+
+var typeInfoMap = make(map[reflect.Type]*typeInfo) // protected by typeLock
+
+// typeLock must be held.
+func getTypeInfo(ut *userTypeInfo) (*typeInfo, os.Error) {
+ rt := ut.base
+ if ut.isGobEncoder {
+ // We want the user type, not the base type.
+ rt = ut.user
+ }
+ info, ok := typeInfoMap[rt]
+ if ok {
+ return info, nil
+ }
+ info = new(typeInfo)
+ gt, err := getBaseType(rt.Name(), rt)
+ if err != nil {
+ return nil, err
+ }
+ info.id = gt.id()
+
+ if ut.isGobEncoder {
+ userType, err := getType(rt.Name(), ut, rt)
+ if err != nil {
+ return nil, err
+ }
+ info.wire = &wireType{GobEncoderT: userType.id().gobType().(*gobEncoderType)}
+ typeInfoMap[ut.user] = info
+ return info, nil
+ }
+
+ t := info.id.gobType()
+ switch typ := rt; typ.Kind() {
+ case reflect.Array:
+ info.wire = &wireType{ArrayT: t.(*arrayType)}
+ case reflect.Map:
+ info.wire = &wireType{MapT: t.(*mapType)}
+ case reflect.Slice:
+ // []byte == []uint8 is a special case handled separately
+ if typ.Elem().Kind() != reflect.Uint8 {
+ info.wire = &wireType{SliceT: t.(*sliceType)}
+ }
+ case reflect.Struct:
+ info.wire = &wireType{StructT: t.(*structType)}
+ }
+ typeInfoMap[rt] = info
+ return info, nil
+}
+
+// Called only when a panic is acceptable and unexpected.
+func mustGetTypeInfo(rt reflect.Type) *typeInfo {
+ t, err := getTypeInfo(userType(rt))
+ if err != nil {
+ panic("getTypeInfo: " + err.String())
+ }
+ return t
+}
+
+// GobEncoder is the interface describing data that provides its own
+// representation for encoding values for transmission to a GobDecoder.
+// A type that implements GobEncoder and GobDecoder has complete
+// control over the representation of its data and may therefore
+// contain things such as private fields, channels, and functions,
+// which are not usually transmissable in gob streams.
+//
+// Note: Since gobs can be stored permanently, It is good design
+// to guarantee the encoding used by a GobEncoder is stable as the
+// software evolves. For instance, it might make sense for GobEncode
+// to include a version number in the encoding.
+type GobEncoder interface {
+ // GobEncode returns a byte slice representing the encoding of the
+ // receiver for transmission to a GobDecoder, usually of the same
+ // concrete type.
+ GobEncode() ([]byte, os.Error)
+}
+
+// GobDecoder is the interface describing data that provides its own
+// routine for decoding transmitted values sent by a GobEncoder.
+type GobDecoder interface {
+ // GobDecode overwrites the receiver, which must be a pointer,
+ // with the value represented by the byte slice, which was written
+ // by GobEncode, usually for the same concrete type.
+ GobDecode([]byte) os.Error
+}
+
+var (
+ nameToConcreteType = make(map[string]reflect.Type)
+ concreteTypeToName = make(map[reflect.Type]string)
+)
+
+// RegisterName is like Register but uses the provided name rather than the
+// type's default.
+func RegisterName(name string, value interface{}) {
+ if name == "" {
+ // reserved for nil
+ panic("attempt to register empty name")
+ }
+ base := userType(reflect.Typeof(value)).base
+ // Check for incompatible duplicates.
+ if t, ok := nameToConcreteType[name]; ok && t != base {
+ panic("gob: registering duplicate types for " + name)
+ }
+ if n, ok := concreteTypeToName[base]; ok && n != name {
+ panic("gob: registering duplicate names for " + base.String())
+ }
+ // Store the name and type provided by the user....
+ nameToConcreteType[name] = reflect.Typeof(value)
+ // but the flattened type in the type table, since that's what decode needs.
+ concreteTypeToName[base] = name
+}
+
+// Register records a type, identified by a value for that type, under its
+// internal type name. That name will identify the concrete type of a value
+// sent or received as an interface variable. Only types that will be
+// transferred as implementations of interface values need to be registered.
+// Expecting to be used only during initialization, it panics if the mapping
+// between types and names is not a bijection.
+func Register(value interface{}) {
+ // Default to printed representation for unnamed types
+ rt := reflect.Typeof(value)
+ name := rt.String()
+
+ // But for named types (or pointers to them), qualify with import path.
+ // Dereference one pointer looking for a named type.
+ star := ""
+ if rt.Name() == "" {
+ if pt := rt; pt.Kind() == reflect.Ptr {
+ star = "*"
+ rt = pt
+ }
+ }
+ if rt.Name() != "" {
+ if rt.PkgPath() == "" {
+ name = star + rt.Name()
+ } else {
+ name = star + rt.PkgPath() + "." + rt.Name()
+ }
+ }
+
+ RegisterName(name, value)
+}
+
+func registerBasics() {
+ Register(int(0))
+ Register(int8(0))
+ Register(int16(0))
+ Register(int32(0))
+ Register(int64(0))
+ Register(uint(0))
+ Register(uint8(0))
+ Register(uint16(0))
+ Register(uint32(0))
+ Register(uint64(0))
+ Register(float32(0))
+ Register(float64(0))
+ Register(complex64(0i))
+ Register(complex128(0i))
+ Register(false)
+ Register("")
+ Register([]byte(nil))
+}
diff --git a/src/cmd/gofix/typecheck.go b/src/cmd/gofix/typecheck.go
new file mode 100644
index 000000000..d565e7b4b
--- /dev/null
+++ b/src/cmd/gofix/typecheck.go
@@ -0,0 +1,579 @@
+// 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"
+ "reflect"
+ "strings"
+)
+
+// Partial type checker.
+//
+// The fact that it is partial is very important: the input is
+// an AST and a description of some type information to
+// assume about one or more packages, but not all the
+// packages that the program imports. The checker is
+// expected to do as much as it can with what it has been
+// given. There is not enough information supplied to do
+// a full type check, but the type checker is expected to
+// apply information that can be derived from variable
+// declarations, function and method returns, and type switches
+// as far as it can, so that the caller can still tell the types
+// of expression relevant to a particular fix.
+//
+// TODO(rsc,gri): Replace with go/typechecker.
+// Doing that could be an interesting test case for go/typechecker:
+// the constraints about working with partial information will
+// likely exercise it in interesting ways. The ideal interface would
+// be to pass typecheck a map from importpath to package API text
+// (Go source code), but for now we use data structures (TypeConfig, Type).
+//
+// The strings mostly use gofmt form.
+//
+// A Field or FieldList has as its type a comma-separated list
+// of the types of the fields. For example, the field list
+// x, y, z int
+// has type "int, int, int".
+
+// The prefix "type " is the type of a type.
+// For example, given
+// var x int
+// type T int
+// x's type is "int" but T's type is "type int".
+// mkType inserts the "type " prefix.
+// getType removes it.
+// isType tests for it.
+
+func mkType(t string) string {
+ return "type " + t
+}
+
+func getType(t string) string {
+ if !isType(t) {
+ return ""
+ }
+ return t[len("type "):]
+}
+
+func isType(t string) bool {
+ return strings.HasPrefix(t, "type ")
+}
+
+// TypeConfig describes the universe of relevant types.
+// For ease of creation, the types are all referred to by string
+// name (e.g., "reflect.Value"). TypeByName is the only place
+// where the strings are resolved.
+
+type TypeConfig struct {
+ Type map[string]*Type
+ Var map[string]string
+ Func map[string]string
+}
+
+// typeof returns the type of the given name, which may be of
+// the form "x" or "p.X".
+func (cfg *TypeConfig) typeof(name string) string {
+ if cfg.Var != nil {
+ if t := cfg.Var[name]; t != "" {
+ return t
+ }
+ }
+ if cfg.Func != nil {
+ if t := cfg.Func[name]; t != "" {
+ return "func()" + t
+ }
+ }
+ return ""
+}
+
+// Type describes the Fields and Methods of a type.
+// If the field or method cannot be found there, it is next
+// looked for in the Embed list.
+type Type struct {
+ Field map[string]string // map field name to type
+ Method map[string]string // map method name to comma-separated return types
+ Embed []string // list of types this type embeds (for extra methods)
+}
+
+// dot returns the type of "typ.name", making its decision
+// using the type information in cfg.
+func (typ *Type) dot(cfg *TypeConfig, name string) string {
+ if typ.Field != nil {
+ if t := typ.Field[name]; t != "" {
+ return t
+ }
+ }
+ if typ.Method != nil {
+ if t := typ.Method[name]; t != "" {
+ return t
+ }
+ }
+
+ for _, e := range typ.Embed {
+ etyp := cfg.Type[e]
+ if etyp != nil {
+ if t := etyp.dot(cfg, name); t != "" {
+ return t
+ }
+ }
+ }
+
+ return ""
+}
+
+// typecheck type checks the AST f assuming the information in cfg.
+// It returns a map from AST nodes to type information in gofmt string form.
+func typecheck(cfg *TypeConfig, f *ast.File) map[interface{}]string {
+ typeof := make(map[interface{}]string)
+
+ // gather function declarations
+ for _, decl := range f.Decls {
+ fn, ok := decl.(*ast.FuncDecl)
+ if !ok {
+ continue
+ }
+ typecheck1(cfg, fn.Type, typeof)
+ t := typeof[fn.Type]
+ if fn.Recv != nil {
+ // The receiver must be a type.
+ rcvr := typeof[fn.Recv]
+ if !isType(rcvr) {
+ if len(fn.Recv.List) != 1 {
+ continue
+ }
+ rcvr = mkType(gofmt(fn.Recv.List[0].Type))
+ typeof[fn.Recv.List[0].Type] = rcvr
+ }
+ rcvr = getType(rcvr)
+ if rcvr != "" && rcvr[0] == '*' {
+ rcvr = rcvr[1:]
+ }
+ typeof[rcvr+"."+fn.Name.Name] = t
+ } else {
+ if isType(t) {
+ t = getType(t)
+ } else {
+ t = gofmt(fn.Type)
+ }
+ typeof[fn.Name] = t
+
+ // Record typeof[fn.Name.Obj] for future references to fn.Name.
+ typeof[fn.Name.Obj] = t
+ }
+ }
+
+ typecheck1(cfg, f, typeof)
+ return typeof
+}
+
+func makeExprList(a []*ast.Ident) []ast.Expr {
+ var b []ast.Expr
+ for _, x := range a {
+ b = append(b, x)
+ }
+ return b
+}
+
+// Typecheck1 is the recursive form of typecheck.
+// It is like typecheck but adds to the information in typeof
+// instead of allocating a new map.
+func typecheck1(cfg *TypeConfig, f interface{}, typeof map[interface{}]string) {
+ // set sets the type of n to typ.
+ // If isDecl is true, n is being declared.
+ set := func(n ast.Expr, typ string, isDecl bool) {
+ if typeof[n] != "" || typ == "" {
+ return
+ }
+ typeof[n] = typ
+
+ // If we obtained typ from the declaration of x
+ // propagate the type to all the uses.
+ // The !isDecl case is a cheat here, but it makes
+ // up in some cases for not paying attention to
+ // struct fields. The real type checker will be
+ // more accurate so we won't need the cheat.
+ if id, ok := n.(*ast.Ident); ok && id.Obj != nil && (isDecl || typeof[id.Obj] == "") {
+ typeof[id.Obj] = typ
+ }
+ }
+
+ // Type-check an assignment lhs = rhs.
+ // If isDecl is true, this is := so we can update
+ // the types of the objects that lhs refers to.
+ typecheckAssign := func(lhs, rhs []ast.Expr, isDecl bool) {
+ if len(lhs) > 1 && len(rhs) == 1 {
+ if _, ok := rhs[0].(*ast.CallExpr); ok {
+ t := split(typeof[rhs[0]])
+ // Lists should have same length but may not; pair what can be paired.
+ for i := 0; i < len(lhs) && i < len(t); i++ {
+ set(lhs[i], t[i], isDecl)
+ }
+ return
+ }
+ }
+ if len(lhs) == 1 && len(rhs) == 2 {
+ // x = y, ok
+ rhs = rhs[:1]
+ } else if len(lhs) == 2 && len(rhs) == 1 {
+ // x, ok = y
+ lhs = lhs[:1]
+ }
+
+ // Match as much as we can.
+ for i := 0; i < len(lhs) && i < len(rhs); i++ {
+ x, y := lhs[i], rhs[i]
+ if typeof[y] != "" {
+ set(x, typeof[y], isDecl)
+ } else {
+ set(y, typeof[x], false)
+ }
+ }
+ }
+
+ // The main type check is a recursive algorithm implemented
+ // by walkBeforeAfter(n, before, after).
+ // Most of it is bottom-up, but in a few places we need
+ // to know the type of the function we are checking.
+ // The before function records that information on
+ // the curfn stack.
+ var curfn []*ast.FuncType
+
+ before := func(n interface{}) {
+ // push function type on stack
+ switch n := n.(type) {
+ case *ast.FuncDecl:
+ curfn = append(curfn, n.Type)
+ case *ast.FuncLit:
+ curfn = append(curfn, n.Type)
+ }
+ }
+
+ // After is the real type checker.
+ after := func(n interface{}) {
+ if n == nil {
+ return
+ }
+ if false && reflect.Typeof(n).Kind() == reflect.Ptr { // debugging trace
+ defer func() {
+ if t := typeof[n]; t != "" {
+ pos := fset.Position(n.(ast.Node).Pos())
+ fmt.Fprintf(os.Stderr, "%s: typeof[%s] = %s\n", pos.String(), gofmt(n), t)
+ }
+ }()
+ }
+
+ switch n := n.(type) {
+ case *ast.FuncDecl, *ast.FuncLit:
+ // pop function type off stack
+ curfn = curfn[:len(curfn)-1]
+
+ case *ast.FuncType:
+ typeof[n] = mkType(joinFunc(split(typeof[n.Params]), split(typeof[n.Results])))
+
+ case *ast.FieldList:
+ // Field list is concatenation of sub-lists.
+ t := ""
+ for _, field := range n.List {
+ if t != "" {
+ t += ", "
+ }
+ t += typeof[field]
+ }
+ typeof[n] = t
+
+ case *ast.Field:
+ // Field is one instance of the type per name.
+ all := ""
+ t := typeof[n.Type]
+ if !isType(t) {
+ // Create a type, because it is typically *T or *p.T
+ // and we might care about that type.
+ t = mkType(gofmt(n.Type))
+ typeof[n.Type] = t
+ }
+ t = getType(t)
+ if len(n.Names) == 0 {
+ all = t
+ } else {
+ for _, id := range n.Names {
+ if all != "" {
+ all += ", "
+ }
+ all += t
+ typeof[id.Obj] = t
+ typeof[id] = t
+ }
+ }
+ typeof[n] = all
+
+ case *ast.ValueSpec:
+ // var declaration. Use type if present.
+ if n.Type != nil {
+ t := typeof[n.Type]
+ if !isType(t) {
+ t = mkType(gofmt(n.Type))
+ typeof[n.Type] = t
+ }
+ t = getType(t)
+ for _, id := range n.Names {
+ set(id, t, true)
+ }
+ }
+ // Now treat same as assignment.
+ typecheckAssign(makeExprList(n.Names), n.Values, true)
+
+ case *ast.AssignStmt:
+ typecheckAssign(n.Lhs, n.Rhs, n.Tok == token.DEFINE)
+
+ case *ast.Ident:
+ // Identifier can take its type from underlying object.
+ if t := typeof[n.Obj]; t != "" {
+ typeof[n] = t
+ }
+
+ case *ast.SelectorExpr:
+ // Field or method.
+ name := n.Sel.Name
+ if t := typeof[n.X]; t != "" {
+ if strings.HasPrefix(t, "*") {
+ t = t[1:] // implicit *
+ }
+ if typ := cfg.Type[t]; typ != nil {
+ if t := typ.dot(cfg, name); t != "" {
+ typeof[n] = t
+ return
+ }
+ }
+ tt := typeof[t+"."+name]
+ if isType(tt) {
+ typeof[n] = getType(tt)
+ return
+ }
+ }
+ // Package selector.
+ if x, ok := n.X.(*ast.Ident); ok && x.Obj == nil {
+ str := x.Name + "." + name
+ if cfg.Type[str] != nil {
+ typeof[n] = mkType(str)
+ return
+ }
+ if t := cfg.typeof(x.Name + "." + name); t != "" {
+ typeof[n] = t
+ return
+ }
+ }
+
+ case *ast.CallExpr:
+ // make(T) has type T.
+ if isTopName(n.Fun, "make") && len(n.Args) >= 1 {
+ typeof[n] = gofmt(n.Args[0])
+ return
+ }
+ // Otherwise, use type of function to determine arguments.
+ t := typeof[n.Fun]
+ in, out := splitFunc(t)
+ if in == nil && out == nil {
+ return
+ }
+ typeof[n] = join(out)
+ for i, arg := range n.Args {
+ if i >= len(in) {
+ break
+ }
+ if typeof[arg] == "" {
+ typeof[arg] = in[i]
+ }
+ }
+
+ case *ast.TypeAssertExpr:
+ // x.(type) has type of x.
+ if n.Type == nil {
+ typeof[n] = typeof[n.X]
+ return
+ }
+ // x.(T) has type T.
+ if t := typeof[n.Type]; isType(t) {
+ typeof[n] = getType(t)
+ }
+
+ case *ast.SliceExpr:
+ // x[i:j] has type of x.
+ typeof[n] = typeof[n.X]
+
+ case *ast.IndexExpr:
+ // x[i] has key type of x's type.
+ t := typeof[n.X]
+ if strings.HasPrefix(t, "[") || strings.HasPrefix(t, "map[") {
+ // Lazy: assume there are no nested [] in the array
+ // length or map key type.
+ if i := strings.Index(t, "]"); i >= 0 {
+ typeof[n] = t[i+1:]
+ }
+ }
+
+ case *ast.StarExpr:
+ // *x for x of type *T has type T when x is an expr.
+ // We don't use the result when *x is a type, but
+ // compute it anyway.
+ t := typeof[n.X]
+ if isType(t) {
+ typeof[n] = "type *" + getType(t)
+ } else if strings.HasPrefix(t, "*") {
+ typeof[n] = t[len("*"):]
+ }
+
+ case *ast.UnaryExpr:
+ // &x for x of type T has type *T.
+ t := typeof[n.X]
+ if t != "" && n.Op == token.AND {
+ typeof[n] = "&" + t
+ }
+
+ case *ast.CompositeLit:
+ // T{...} has type T.
+ typeof[n] = gofmt(n.Type)
+
+ case *ast.ParenExpr:
+ // (x) has type of x.
+ typeof[n] = typeof[n.X]
+
+ case *ast.TypeSwitchStmt:
+ // Type of variable changes for each case in type switch,
+ // but go/parser generates just one variable.
+ // Repeat type check for each case with more precise
+ // type information.
+ as, ok := n.Assign.(*ast.AssignStmt)
+ if !ok {
+ return
+ }
+ varx, ok := as.Lhs[0].(*ast.Ident)
+ if !ok {
+ return
+ }
+ t := typeof[varx]
+ for _, cas := range n.Body.List {
+ cas := cas.(*ast.CaseClause)
+ if len(cas.List) == 1 {
+ // Variable has specific type only when there is
+ // exactly one type in the case list.
+ if tt := typeof[cas.List[0]]; isType(tt) {
+ tt = getType(tt)
+ typeof[varx] = tt
+ typeof[varx.Obj] = tt
+ typecheck1(cfg, cas.Body, typeof)
+ }
+ }
+ }
+ // Restore t.
+ typeof[varx] = t
+ typeof[varx.Obj] = t
+
+ case *ast.ReturnStmt:
+ if len(curfn) == 0 {
+ // Probably can't happen.
+ return
+ }
+ f := curfn[len(curfn)-1]
+ res := n.Results
+ if f.Results != nil {
+ t := split(typeof[f.Results])
+ for i := 0; i < len(res) && i < len(t); i++ {
+ set(res[i], t[i], false)
+ }
+ }
+ }
+ }
+ walkBeforeAfter(f, before, after)
+}
+
+// Convert between function type strings and lists of types.
+// Using strings makes this a little harder, but it makes
+// a lot of the rest of the code easier. This will all go away
+// when we can use go/typechecker directly.
+
+// splitFunc splits "func(x,y,z) (a,b,c)" into ["x", "y", "z"] and ["a", "b", "c"].
+func splitFunc(s string) (in, out []string) {
+ if !strings.HasPrefix(s, "func(") {
+ return nil, nil
+ }
+
+ i := len("func(") // index of beginning of 'in' arguments
+ nparen := 0
+ for j := i; j < len(s); j++ {
+ switch s[j] {
+ case '(':
+ nparen++
+ case ')':
+ nparen--
+ if nparen < 0 {
+ // found end of parameter list
+ out := strings.TrimSpace(s[j+1:])
+ if len(out) >= 2 && out[0] == '(' && out[len(out)-1] == ')' {
+ out = out[1 : len(out)-1]
+ }
+ return split(s[i:j]), split(out)
+ }
+ }
+ }
+ return nil, nil
+}
+
+// joinFunc is the inverse of splitFunc.
+func joinFunc(in, out []string) string {
+ outs := ""
+ if len(out) == 1 {
+ outs = " " + out[0]
+ } else if len(out) > 1 {
+ outs = " (" + join(out) + ")"
+ }
+ return "func(" + join(in) + ")" + outs
+}
+
+// split splits "int, float" into ["int", "float"] and splits "" into [].
+func split(s string) []string {
+ out := []string{}
+ i := 0 // current type being scanned is s[i:j].
+ nparen := 0
+ for j := 0; j < len(s); j++ {
+ switch s[j] {
+ case ' ':
+ if i == j {
+ i++
+ }
+ case '(':
+ nparen++
+ case ')':
+ nparen--
+ if nparen < 0 {
+ // probably can't happen
+ return nil
+ }
+ case ',':
+ if nparen == 0 {
+ if i < j {
+ out = append(out, s[i:j])
+ }
+ i = j + 1
+ }
+ }
+ }
+ if nparen != 0 {
+ // probably can't happen
+ return nil
+ }
+ if i < len(s) {
+ out = append(out, s[i:])
+ }
+ return out
+}
+
+// join is the inverse of split.
+func join(x []string) string {
+ return strings.Join(x, ", ")
+}
diff --git a/src/cmd/gofmt/Makefile b/src/cmd/gofmt/Makefile
index 5f2f454e8..dc5b060e6 100644
--- a/src/cmd/gofmt/Makefile
+++ b/src/cmd/gofmt/Makefile
@@ -15,5 +15,5 @@ include ../../Make.cmd
test: $(TARG)
./test.sh
-smoketest: $(TARG)
- (cd testdata; ./test.sh)
+testshort:
+ gotest -test.short
diff --git a/src/cmd/gofmt/doc.go b/src/cmd/gofmt/doc.go
index 2d2c9ae61..e44030eee 100644
--- a/src/cmd/gofmt/doc.go
+++ b/src/cmd/gofmt/doc.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
/*
-
Gofmt formats Go programs.
Without an explicit path, it processes the standard input. Given a file,
@@ -16,14 +15,16 @@ Usage:
The flags are:
-l
- just list files whose formatting differs from gofmt's; generate no other output
- unless -w is also set.
+ just list files whose formatting differs from gofmt's;
+ generate no other output unless -w is also set.
-r rule
apply the rewrite rule to the source before reformatting.
-s
try to simplify code (after applying the rewrite rule, if any).
-w
if set, overwrite each input file with its output.
+ -comments=true
+ print comments; if false, all comments are elided from the output.
-spaces
align with spaces instead of tabs.
-tabindent
@@ -31,15 +32,6 @@ The flags are:
-tabwidth=8
tab width in spaces.
-Debugging flags:
-
- -trace
- print parse trace.
- -ast
- print AST (before rewrites).
- -comments=true
- print comments; if false, all comments are elided from the output.
-
The rewrite rule specified with the -r flag must be a string of the form:
pattern -> replacement
diff --git a/src/cmd/gofmt/gofmt.go b/src/cmd/gofmt/gofmt.go
index 224aee717..ce274aa21 100644
--- a/src/cmd/gofmt/gofmt.go
+++ b/src/cmd/gofmt/gofmt.go
@@ -13,9 +13,11 @@ import (
"go/printer"
"go/scanner"
"go/token"
+ "io"
"io/ioutil"
"os"
"path/filepath"
+ "runtime/pprof"
"strings"
)
@@ -27,15 +29,14 @@ var (
rewriteRule = flag.String("r", "", "rewrite rule (e.g., 'α[β:len(α)] -> α[β:]')")
simplifyAST = flag.Bool("s", false, "simplify code")
- // debugging support
- comments = flag.Bool("comments", true, "print comments")
- trace = flag.Bool("trace", false, "print parse trace")
- printAST = flag.Bool("ast", false, "print AST (before rewrites)")
-
// layout control
+ comments = flag.Bool("comments", true, "print comments")
tabWidth = flag.Int("tabwidth", 8, "tab width")
tabIndent = flag.Bool("tabindent", true, "indent with tabs independent of -spaces")
useSpaces = flag.Bool("spaces", true, "align with spaces instead of tabs")
+
+ // debugging
+ cpuprofile = flag.String("cpuprofile", "", "write cpu profile to this file")
)
@@ -66,9 +67,6 @@ func initParserMode() {
if *comments {
parserMode |= parser.ParseComments
}
- if *trace {
- parserMode |= parser.Trace
- }
}
@@ -89,20 +87,25 @@ func isGoFile(f *os.FileInfo) bool {
}
-func processFile(f *os.File) os.Error {
- src, err := ioutil.ReadAll(f)
- if err != nil {
- return err
+// If in == nil, the source is the contents of the file with the given filename.
+func processFile(filename string, in io.Reader, out io.Writer) os.Error {
+ if in == nil {
+ f, err := os.Open(filename)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ in = f
}
- file, err := parser.ParseFile(fset, f.Name(), src, parserMode)
-
+ src, err := ioutil.ReadAll(in)
if err != nil {
return err
}
- if *printAST {
- ast.Print(file)
+ file, err := parser.ParseFile(fset, filename, src, parserMode)
+ if err != nil {
+ return err
}
if rewrite != nil {
@@ -123,10 +126,10 @@ func processFile(f *os.File) os.Error {
if !bytes.Equal(src, res) {
// formatting has changed
if *list {
- fmt.Fprintln(os.Stdout, f.Name())
+ fmt.Fprintln(out, filename)
}
if *write {
- err = ioutil.WriteFile(f.Name(), res, 0)
+ err = ioutil.WriteFile(filename, res, 0)
if err != nil {
return err
}
@@ -134,23 +137,13 @@ func processFile(f *os.File) os.Error {
}
if !*list && !*write {
- _, err = os.Stdout.Write(res)
+ _, err = out.Write(res)
}
return err
}
-func processFileByName(filename string) os.Error {
- file, err := os.Open(filename, os.O_RDONLY, 0)
- if err != nil {
- return err
- }
- defer file.Close()
- return processFile(file)
-}
-
-
type fileVisitor chan os.Error
func (v fileVisitor) VisitDir(path string, f *os.FileInfo) bool {
@@ -161,7 +154,7 @@ func (v fileVisitor) VisitDir(path string, f *os.FileInfo) bool {
func (v fileVisitor) VisitFile(path string, f *os.FileInfo) {
if isGoFile(f) {
v <- nil // synchronize error handler
- if err := processFileByName(path); err != nil {
+ if err := processFile(path, nil, os.Stdout); err != nil {
v <- err
}
}
@@ -169,30 +162,47 @@ func (v fileVisitor) VisitFile(path string, f *os.FileInfo) {
func walkDir(path string) {
- // start an error handler
- done := make(chan bool)
v := make(fileVisitor)
go func() {
- for err := range v {
- if err != nil {
- report(err)
- }
- }
- done <- true
+ filepath.Walk(path, v, v)
+ close(v)
}()
- // walk the tree
- filepath.Walk(path, v, v)
- close(v) // terminate error handler loop
- <-done // wait for all errors to be reported
+ for err := range v {
+ if err != nil {
+ report(err)
+ }
+ }
}
func main() {
+ // call gofmtMain in a separate function
+ // so that it can use defer and have them
+ // run before the exit.
+ gofmtMain()
+ os.Exit(exitCode)
+}
+
+
+func gofmtMain() {
flag.Usage = usage
flag.Parse()
if *tabWidth < 0 {
fmt.Fprintf(os.Stderr, "negative tabwidth %d\n", *tabWidth)
- os.Exit(2)
+ exitCode = 2
+ return
+ }
+
+ if *cpuprofile != "" {
+ f, err := os.Create(*cpuprofile)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "creating cpu profile: %s\n", err)
+ exitCode = 2
+ return
+ }
+ defer f.Close()
+ pprof.StartCPUProfile(f)
+ defer pprof.StopCPUProfile()
}
initParserMode()
@@ -200,9 +210,10 @@ func main() {
initRewrite()
if flag.NArg() == 0 {
- if err := processFile(os.Stdin); err != nil {
+ if err := processFile("<standard input>", os.Stdin, os.Stdout); err != nil {
report(err)
}
+ return
}
for i := 0; i < flag.NArg(); i++ {
@@ -211,13 +222,11 @@ func main() {
case err != nil:
report(err)
case dir.IsRegular():
- if err := processFileByName(path); err != nil {
+ if err := processFile(path, nil, os.Stdout); err != nil {
report(err)
}
case dir.IsDirectory():
walkDir(path)
}
}
-
- os.Exit(exitCode)
}
diff --git a/src/cmd/gofmt/gofmt_test.go b/src/cmd/gofmt/gofmt_test.go
new file mode 100644
index 000000000..4ec94e293
--- /dev/null
+++ b/src/cmd/gofmt/gofmt_test.go
@@ -0,0 +1,81 @@
+// 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"
+ "io/ioutil"
+ "path/filepath"
+ "strings"
+ "testing"
+)
+
+
+func runTest(t *testing.T, dirname, in, out, flags string) {
+ in = filepath.Join(dirname, in)
+ out = filepath.Join(dirname, out)
+
+ // process flags
+ *simplifyAST = false
+ *rewriteRule = ""
+ for _, flag := range strings.Split(flags, " ", -1) {
+ elts := strings.Split(flag, "=", 2)
+ name := elts[0]
+ value := ""
+ if len(elts) == 2 {
+ value = elts[1]
+ }
+ switch name {
+ case "":
+ // no flags
+ case "-r":
+ *rewriteRule = value
+ case "-s":
+ *simplifyAST = true
+ default:
+ t.Errorf("unrecognized flag name: %s", name)
+ }
+ }
+
+ initParserMode()
+ initPrinterMode()
+ initRewrite()
+
+ var buf bytes.Buffer
+ err := processFile(in, nil, &buf)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ expected, err := ioutil.ReadFile(out)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ if got := buf.Bytes(); bytes.Compare(got, expected) != 0 {
+ t.Errorf("(gofmt %s) != %s (see %s.gofmt)", in, out, in)
+ ioutil.WriteFile(in+".gofmt", got, 0666)
+ }
+}
+
+
+// TODO(gri) Add more test cases!
+var tests = []struct {
+ dirname, in, out, flags string
+}{
+ {".", "gofmt.go", "gofmt.go", ""},
+ {".", "gofmt_test.go", "gofmt_test.go", ""},
+ {"testdata", "composites.input", "composites.golden", "-s"},
+ {"testdata", "rewrite1.input", "rewrite1.golden", "-r=Foo->Bar"},
+}
+
+
+func TestRewrite(t *testing.T) {
+ for _, test := range tests {
+ runTest(t, test.dirname, test.in, test.out, test.flags)
+ }
+}
diff --git a/src/cmd/gofmt/rewrite.go b/src/cmd/gofmt/rewrite.go
index fbcd46aa2..93643dced 100644
--- a/src/cmd/gofmt/rewrite.go
+++ b/src/cmd/gofmt/rewrite.go
@@ -46,6 +46,16 @@ func parseExpr(s string, what string) ast.Expr {
}
+// Keep this function for debugging.
+/*
+func dump(msg string, val reflect.Value) {
+ fmt.Printf("%s:\n", msg)
+ ast.Print(fset, val.Interface())
+ fmt.Println()
+}
+*/
+
+
// rewriteFile applies the rewrite rule 'pattern -> replace' to an entire file.
func rewriteFile(pattern, replace ast.Expr, p *ast.File) *ast.File {
m := make(map[string]reflect.Value)
@@ -54,7 +64,7 @@ func rewriteFile(pattern, replace ast.Expr, p *ast.File) *ast.File {
var f func(val reflect.Value) reflect.Value // f is recursive
f = func(val reflect.Value) reflect.Value {
for k := range m {
- m[k] = nil, false
+ m[k] = reflect.Value{}, false
}
val = apply(f, val)
if match(m, pat, val) {
@@ -78,28 +88,45 @@ func setValue(x, y reflect.Value) {
panic(x)
}
}()
- x.SetValue(y)
+ x.Set(y)
}
+// Values/types for special cases.
+var (
+ objectPtrNil = reflect.NewValue((*ast.Object)(nil))
+
+ identType = reflect.Typeof((*ast.Ident)(nil))
+ objectPtrType = reflect.Typeof((*ast.Object)(nil))
+ positionType = reflect.Typeof(token.NoPos)
+)
+
+
// apply replaces each AST field x in val with f(x), returning val.
// To avoid extra conversions, f operates on the reflect.Value form.
func apply(f func(reflect.Value) reflect.Value, val reflect.Value) reflect.Value {
- if val == nil {
- return nil
+ if !val.IsValid() {
+ return reflect.Value{}
+ }
+
+ // *ast.Objects introduce cycles and are likely incorrect after
+ // rewrite; don't follow them but replace with nil instead
+ if val.Type() == objectPtrType {
+ return objectPtrNil
}
- switch v := reflect.Indirect(val).(type) {
- case *reflect.SliceValue:
+
+ switch v := reflect.Indirect(val); v.Kind() {
+ case reflect.Slice:
for i := 0; i < v.Len(); i++ {
- e := v.Elem(i)
+ e := v.Index(i)
setValue(e, f(e))
}
- case *reflect.StructValue:
+ case reflect.Struct:
for i := 0; i < v.NumField(); i++ {
e := v.Field(i)
setValue(e, f(e))
}
- case *reflect.InterfaceValue:
+ case reflect.Interface:
e := v.Elem()
setValue(v, f(e))
}
@@ -107,10 +134,6 @@ func apply(f func(reflect.Value) reflect.Value, val reflect.Value) reflect.Value
}
-var positionType = reflect.Typeof(token.NoPos)
-var identType = reflect.Typeof((*ast.Ident)(nil))
-
-
func isWildcard(s string) bool {
rune, size := utf8.DecodeRuneInString(s)
return size == len(s) && unicode.IsLower(rune)
@@ -124,9 +147,9 @@ func match(m map[string]reflect.Value, pattern, val reflect.Value) bool {
// Wildcard matches any expression. If it appears multiple
// times in the pattern, it must match the same expression
// each time.
- if m != nil && pattern != nil && pattern.Type() == identType {
+ if m != nil && pattern.IsValid() && pattern.Type() == identType {
name := pattern.Interface().(*ast.Ident).Name
- if isWildcard(name) && val != nil {
+ if isWildcard(name) && val.IsValid() {
// wildcards only match expressions
if _, ok := val.Interface().(ast.Expr); ok {
if old, ok := m[name]; ok {
@@ -139,8 +162,8 @@ func match(m map[string]reflect.Value, pattern, val reflect.Value) bool {
}
// Otherwise, pattern and val must match recursively.
- if pattern == nil || val == nil {
- return pattern == nil && val == nil
+ if !pattern.IsValid() || !val.IsValid() {
+ return !pattern.IsValid() && !val.IsValid()
}
if pattern.Type() != val.Type() {
return false
@@ -148,9 +171,6 @@ func match(m map[string]reflect.Value, pattern, val reflect.Value) bool {
// Special cases.
switch pattern.Type() {
- case positionType:
- // token positions don't need to match
- return true
case identType:
// For identifiers, only the names need to match
// (and none of the other *ast.Object information).
@@ -159,29 +179,30 @@ func match(m map[string]reflect.Value, pattern, val reflect.Value) bool {
p := pattern.Interface().(*ast.Ident)
v := val.Interface().(*ast.Ident)
return p == nil && v == nil || p != nil && v != nil && p.Name == v.Name
+ case objectPtrType, positionType:
+ // object pointers and token positions don't need to match
+ return true
}
p := reflect.Indirect(pattern)
v := reflect.Indirect(val)
- if p == nil || v == nil {
- return p == nil && v == nil
+ if !p.IsValid() || !v.IsValid() {
+ return !p.IsValid() && !v.IsValid()
}
- switch p := p.(type) {
- case *reflect.SliceValue:
- v := v.(*reflect.SliceValue)
+ switch p.Kind() {
+ case reflect.Slice:
if p.Len() != v.Len() {
return false
}
for i := 0; i < p.Len(); i++ {
- if !match(m, p.Elem(i), v.Elem(i)) {
+ if !match(m, p.Index(i), v.Index(i)) {
return false
}
}
return true
- case *reflect.StructValue:
- v := v.(*reflect.StructValue)
+ case reflect.Struct:
if p.NumField() != v.NumField() {
return false
}
@@ -192,8 +213,7 @@ func match(m map[string]reflect.Value, pattern, val reflect.Value) bool {
}
return true
- case *reflect.InterfaceValue:
- v := v.(*reflect.InterfaceValue)
+ case reflect.Interface:
return match(m, p.Elem(), v.Elem())
}
@@ -207,8 +227,8 @@ func match(m map[string]reflect.Value, pattern, val reflect.Value) bool {
// if m == nil, subst returns a copy of pattern and doesn't change the line
// number information.
func subst(m map[string]reflect.Value, pattern reflect.Value, pos reflect.Value) reflect.Value {
- if pattern == nil {
- return nil
+ if !pattern.IsValid() {
+ return reflect.Value{}
}
// Wildcard gets replaced with map value.
@@ -216,12 +236,12 @@ func subst(m map[string]reflect.Value, pattern reflect.Value, pos reflect.Value)
name := pattern.Interface().(*ast.Ident).Name
if isWildcard(name) {
if old, ok := m[name]; ok {
- return subst(nil, old, nil)
+ return subst(nil, old, reflect.Value{})
}
}
}
- if pos != nil && pattern.Type() == positionType {
+ if pos.IsValid() && pattern.Type() == positionType {
// use new position only if old position was valid in the first place
if old := pattern.Interface().(token.Pos); !old.IsValid() {
return pattern
@@ -230,29 +250,33 @@ func subst(m map[string]reflect.Value, pattern reflect.Value, pos reflect.Value)
}
// Otherwise copy.
- switch p := pattern.(type) {
- case *reflect.SliceValue:
- v := reflect.MakeSlice(p.Type().(*reflect.SliceType), p.Len(), p.Len())
+ switch p := pattern; p.Kind() {
+ case reflect.Slice:
+ v := reflect.MakeSlice(p.Type(), p.Len(), p.Len())
for i := 0; i < p.Len(); i++ {
- v.Elem(i).SetValue(subst(m, p.Elem(i), pos))
+ v.Index(i).Set(subst(m, p.Index(i), pos))
}
return v
- case *reflect.StructValue:
- v := reflect.MakeZero(p.Type()).(*reflect.StructValue)
+ case reflect.Struct:
+ v := reflect.Zero(p.Type())
for i := 0; i < p.NumField(); i++ {
- v.Field(i).SetValue(subst(m, p.Field(i), pos))
+ v.Field(i).Set(subst(m, p.Field(i), pos))
}
return v
- case *reflect.PtrValue:
- v := reflect.MakeZero(p.Type()).(*reflect.PtrValue)
- v.PointTo(subst(m, p.Elem(), pos))
+ case reflect.Ptr:
+ v := reflect.Zero(p.Type())
+ if elem := p.Elem(); elem.IsValid() {
+ v.Set(subst(m, elem, pos).Addr())
+ }
return v
- case *reflect.InterfaceValue:
- v := reflect.MakeZero(p.Type()).(*reflect.InterfaceValue)
- v.SetValue(subst(m, p.Elem(), pos))
+ case reflect.Interface:
+ v := reflect.Zero(p.Type())
+ if elem := p.Elem(); elem.IsValid() {
+ v.Set(subst(m, elem, pos))
+ }
return v
}
diff --git a/src/cmd/gofmt/testdata/rewrite1.golden b/src/cmd/gofmt/testdata/rewrite1.golden
new file mode 100644
index 000000000..3f909ff4a
--- /dev/null
+++ b/src/cmd/gofmt/testdata/rewrite1.golden
@@ -0,0 +1,8 @@
+package main
+
+type Bar int
+
+func main() {
+ var a Bar
+ println(a)
+}
diff --git a/src/cmd/gofmt/testdata/rewrite1.input b/src/cmd/gofmt/testdata/rewrite1.input
new file mode 100644
index 000000000..1f10e3601
--- /dev/null
+++ b/src/cmd/gofmt/testdata/rewrite1.input
@@ -0,0 +1,8 @@
+package main
+
+type Foo int
+
+func main() {
+ var a Foo
+ println(a)
+}
diff --git a/src/cmd/gofmt/testdata/test.sh b/src/cmd/gofmt/testdata/test.sh
deleted file mode 100755
index a1d5d823e..000000000
--- a/src/cmd/gofmt/testdata/test.sh
+++ /dev/null
@@ -1,65 +0,0 @@
-#!/usr/bin/env bash
-# Copyright 2010 The Go Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file.
-
-CMD="../gofmt"
-TMP=test_tmp.go
-COUNT=0
-
-
-cleanup() {
- rm -f $TMP
-}
-
-
-error() {
- echo $1
- exit 1
-}
-
-
-count() {
- #echo $1
- let COUNT=$COUNT+1
- let M=$COUNT%10
- if [ $M == 0 ]; then
- echo -n "."
- fi
-}
-
-
-test() {
- count $1
-
- # compare against .golden file
- cleanup
- $CMD -s $1 > $TMP
- cmp -s $TMP $2
- if [ $? != 0 ]; then
- diff $TMP $2
- error "Error: simplified $1 does not match $2"
- fi
-
- # make sure .golden is idempotent
- cleanup
- $CMD -s $2 > $TMP
- cmp -s $TMP $2
- if [ $? != 0 ]; then
- diff $TMP $2
- error "Error: $2 is not idempotent"
- fi
-}
-
-
-runtests() {
- smoketest=../../../pkg/go/parser/parser.go
- test $smoketest $smoketest
- test composites.input composites.golden
- # add more test cases here
-}
-
-
-runtests
-cleanup
-echo "PASSED ($COUNT tests)"
diff --git a/src/cmd/goinstall/Makefile b/src/cmd/goinstall/Makefile
index 6ddb32be7..aaf202ee7 100644
--- a/src/cmd/goinstall/Makefile
+++ b/src/cmd/goinstall/Makefile
@@ -10,5 +10,20 @@ GOFILES=\
main.go\
make.go\
parse.go\
+ syslist.go\
+
+CLEANFILES+=syslist.go
include ../../Make.cmd
+
+syslist.go:
+ echo '// Generated automatically by make.' >$@
+ echo 'package main' >>$@
+ echo 'const goosList = "$(GOOS_LIST)"' >>$@
+ echo 'const goarchList = "$(GOARCH_LIST)"' >>$@
+
+test:
+ gotest
+
+testshort:
+ gotest -test.short
diff --git a/src/cmd/goinstall/doc.go b/src/cmd/goinstall/doc.go
index 17cc06969..15845b574 100644
--- a/src/cmd/goinstall/doc.go
+++ b/src/cmd/goinstall/doc.go
@@ -14,6 +14,7 @@ Usage:
Flags and default settings:
-a=false install all previously installed packages
+ -clean=false clean the package directory before installing
-dashboard=true tally public packages on godashboard.appspot.com
-log=true log installed packages to $GOROOT/goinstall.log for use by -a
-u=false update already-downloaded packages
diff --git a/src/cmd/goinstall/main.go b/src/cmd/goinstall/main.go
index 34441be45..8fec8e312 100644
--- a/src/cmd/goinstall/main.go
+++ b/src/cmd/goinstall/main.go
@@ -120,7 +120,7 @@ func logPackage(pkg string) {
if installedPkgs[pkg] {
return
}
- fout, err := os.Open(logfile, os.O_WRONLY|os.O_APPEND|os.O_CREAT, 0644)
+ fout, err := os.OpenFile(logfile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
fmt.Fprintf(os.Stderr, "%s: %s\n", argv0, err)
return
diff --git a/src/cmd/goinstall/make.go b/src/cmd/goinstall/make.go
index e2d99bb47..ceb119e5a 100644
--- a/src/cmd/goinstall/make.go
+++ b/src/cmd/goinstall/make.go
@@ -52,12 +52,6 @@ func makeMakefile(dir, pkg string) ([]byte, os.Error) {
return nil, err
}
- if len(dirInfo.cgoFiles) == 0 && len(dirInfo.cFiles) > 0 {
- // When using cgo, .c files are compiled with gcc. Without cgo,
- // they may be intended for 6c. Just error out for now.
- return nil, os.ErrorString("C files found in non-cgo package")
- }
-
cgoFiles := dirInfo.cgoFiles
isCgo := make(map[string]bool, len(cgoFiles))
for _, file := range cgoFiles {
@@ -67,26 +61,40 @@ func makeMakefile(dir, pkg string) ([]byte, os.Error) {
isCgo[file] = true
}
- oFiles := make([]string, 0, len(dirInfo.cFiles))
- for _, file := range dirInfo.cFiles {
+ goFiles := make([]string, 0, len(dirInfo.goFiles))
+ for _, file := range dirInfo.goFiles {
if !safeName(file) {
return nil, os.ErrorString("unsafe name: " + file)
}
- oFiles = append(oFiles, file[:len(file)-2]+".o")
+ if !isCgo[file] {
+ goFiles = append(goFiles, file)
+ }
}
- goFiles := make([]string, 0, len(dirInfo.goFiles))
- for _, file := range dirInfo.goFiles {
+ oFiles := make([]string, 0, len(dirInfo.cFiles)+len(dirInfo.sFiles))
+ cgoOFiles := make([]string, 0, len(dirInfo.cFiles))
+ for _, file := range dirInfo.cFiles {
if !safeName(file) {
return nil, os.ErrorString("unsafe name: " + file)
}
- if !isCgo[file] {
- goFiles = append(goFiles, file)
+ // When cgo is in use, C files are compiled with gcc,
+ // otherwise they're compiled with gc.
+ if len(cgoFiles) > 0 {
+ cgoOFiles = append(cgoOFiles, file[:len(file)-2]+".o")
+ } else {
+ oFiles = append(oFiles, file[:len(file)-2]+".$O")
}
}
+ for _, file := range dirInfo.sFiles {
+ if !safeName(file) {
+ return nil, os.ErrorString("unsafe name: " + file)
+ }
+ oFiles = append(oFiles, file[:len(file)-2]+".$O")
+ }
+
var buf bytes.Buffer
- md := makedata{pkg, goFiles, cgoFiles, oFiles}
+ md := makedata{pkg, goFiles, oFiles, cgoFiles, cgoOFiles}
if err := makefileTemplate.Execute(&buf, &md); err != nil {
return nil, err
}
@@ -106,10 +114,11 @@ func safeName(s string) bool {
// makedata is the data type for the makefileTemplate.
type makedata struct {
- Pkg string // package import path
- GoFiles []string // list of non-cgo .go files
- CgoFiles []string // list of cgo .go files
- OFiles []string // list of ofiles for cgo
+ Pkg string // package import path
+ GoFiles []string // list of non-cgo .go files
+ OFiles []string // list of .$O files
+ CgoFiles []string // list of cgo .go files
+ CgoOFiles []string // list of cgo .o files, without extension
}
var makefileTemplate = template.MustParse(`
@@ -124,6 +133,13 @@ GOFILES=\
{.end}
{.end}
+{.section OFiles}
+OFILES=\
+{.repeated section OFiles}
+ {@}\
+{.end}
+
+{.end}
{.section CgoFiles}
CGOFILES=\
{.repeated section CgoFiles}
@@ -131,9 +147,9 @@ CGOFILES=\
{.end}
{.end}
-{.section OFiles}
+{.section CgoOFiles}
CGO_OFILES=\
-{.repeated section OFiles}
+{.repeated section CgoOFiles}
{@}\
{.end}
diff --git a/src/cmd/goinstall/parse.go b/src/cmd/goinstall/parse.go
index 014b8fcb2..0e617903c 100644
--- a/src/cmd/goinstall/parse.go
+++ b/src/cmd/goinstall/parse.go
@@ -14,6 +14,7 @@ import (
"path/filepath"
"strconv"
"strings"
+ "runtime"
)
@@ -21,6 +22,7 @@ type dirInfo struct {
goFiles []string // .go files within dir (including cgoFiles)
cgoFiles []string // .go files that import "C"
cFiles []string // .c files within dir
+ sFiles []string // .s files within dir
imports []string // All packages imported by goFiles
pkgName string // Name of package within dir
}
@@ -37,7 +39,7 @@ type dirInfo struct {
// The imports map keys are package paths imported by listed Go files,
// and the values are the Go files importing the respective package paths.
func scanDir(dir string, allowMain bool) (info *dirInfo, err os.Error) {
- f, err := os.Open(dir, os.O_RDONLY, 0)
+ f, err := os.Open(dir)
if err != nil {
return nil, err
}
@@ -50,6 +52,7 @@ func scanDir(dir string, allowMain bool) (info *dirInfo, err os.Error) {
goFiles := make([]string, 0, len(dirs))
cgoFiles := make([]string, 0, len(dirs))
cFiles := make([]string, 0, len(dirs))
+ sFiles := make([]string, 0, len(dirs))
importsm := make(map[string]bool)
pkgName := ""
for i := range dirs {
@@ -57,13 +60,25 @@ func scanDir(dir string, allowMain bool) (info *dirInfo, err os.Error) {
if strings.HasPrefix(d.Name, "_") || strings.Index(d.Name, ".cgo") != -1 {
continue
}
- if strings.HasSuffix(d.Name, ".c") {
- cFiles = append(cFiles, d.Name)
+ if !goodOSArch(d.Name) {
continue
}
- if !strings.HasSuffix(d.Name, ".go") || strings.HasSuffix(d.Name, "_test.go") {
+
+ switch filepath.Ext(d.Name) {
+ case ".go":
+ if strings.HasSuffix(d.Name, "_test.go") {
+ continue
+ }
+ case ".c":
+ cFiles = append(cFiles, d.Name)
+ continue
+ case ".s":
+ sFiles = append(sFiles, d.Name)
+ continue
+ default:
continue
}
+
filename := filepath.Join(dir, d.Name)
pf, err := parser.ParseFile(fset, filename, nil, parser.ImportsOnly)
if err != nil {
@@ -106,5 +121,49 @@ func scanDir(dir string, allowMain bool) (info *dirInfo, err os.Error) {
imports[i] = p
i++
}
- return &dirInfo{goFiles, cgoFiles, cFiles, imports, pkgName}, nil
+ return &dirInfo{goFiles, cgoFiles, cFiles, sFiles, imports, pkgName}, nil
+}
+
+// goodOSArch returns false if the filename contains a $GOOS or $GOARCH
+// suffix which does not match the current system.
+// The recognized filename formats are:
+//
+// name_$(GOOS).*
+// name_$(GOARCH).*
+// name_$(GOOS)_$(GOARCH).*
+//
+func goodOSArch(filename string) bool {
+ if dot := strings.Index(filename, "."); dot != -1 {
+ filename = filename[:dot]
+ }
+ l := strings.Split(filename, "_", -1)
+ n := len(l)
+ if n == 0 {
+ return true
+ }
+ if good, known := goodOS[l[n-1]]; known {
+ return good
+ }
+ if good, known := goodArch[l[n-1]]; known {
+ if !good || n < 2 {
+ return false
+ }
+ good, known = goodOS[l[n-2]]
+ return good || !known
+ }
+ return true
+}
+
+var goodOS = make(map[string]bool)
+var goodArch = make(map[string]bool)
+
+func init() {
+ goodOS = make(map[string]bool)
+ goodArch = make(map[string]bool)
+ for _, v := range strings.Fields(goosList) {
+ goodOS[v] = v == runtime.GOOS
+ }
+ for _, v := range strings.Fields(goarchList) {
+ goodArch[v] = v == runtime.GOARCH
+ }
}
diff --git a/src/cmd/goinstall/syslist_test.go b/src/cmd/goinstall/syslist_test.go
new file mode 100644
index 000000000..795cd293a
--- /dev/null
+++ b/src/cmd/goinstall/syslist_test.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 (
+ "runtime"
+ "testing"
+)
+
+var (
+ thisOS = runtime.GOOS
+ thisArch = runtime.GOARCH
+ otherOS = anotherOS()
+ otherArch = anotherArch()
+)
+
+func anotherOS() string {
+ if thisOS != "darwin" {
+ return "darwin"
+ }
+ return "linux"
+}
+
+func anotherArch() string {
+ if thisArch != "amd64" {
+ return "amd64"
+ }
+ return "386"
+}
+
+type GoodFileTest struct {
+ name string
+ result bool
+}
+
+var tests = []GoodFileTest{
+ {"file.go", true},
+ {"file.c", true},
+ {"file_foo.go", true},
+ {"file_" + thisArch + ".go", true},
+ {"file_" + otherArch + ".go", false},
+ {"file_" + thisOS + ".go", true},
+ {"file_" + otherOS + ".go", false},
+ {"file_" + thisOS + "_" + thisArch + ".go", true},
+ {"file_" + otherOS + "_" + thisArch + ".go", false},
+ {"file_" + thisOS + "_" + otherArch + ".go", false},
+ {"file_" + otherOS + "_" + otherArch + ".go", false},
+ {"file_foo_" + thisArch + ".go", true},
+ {"file_foo_" + otherArch + ".go", false},
+ {"file_" + thisOS + ".c", true},
+ {"file_" + otherOS + ".c", false},
+}
+
+func TestGoodOSArch(t *testing.T) {
+ for _, test := range tests {
+ if goodOSArch(test.name) != test.result {
+ t.Fatalf("goodOSArch(%q) != %v", test.name, test.result)
+ }
+ }
+}
diff --git a/src/cmd/gopack/ar.c b/src/cmd/gopack/ar.c
index a7e2c41af..dc3899f37 100644
--- a/src/cmd/gopack/ar.c
+++ b/src/cmd/gopack/ar.c
@@ -41,6 +41,7 @@
#include <libc.h>
#include <bio.h>
#include <mach.h>
+#include "../../libmach/obj.h"
#include <ar.h>
#undef select
@@ -123,6 +124,7 @@ int gflag;
int oflag;
int uflag;
int vflag;
+int Pflag; /* remove leading file prefix */
int Sflag; /* force mark Go package as safe */
int errors;
@@ -141,6 +143,7 @@ char poname[ARNAMESIZE+1]; /* name of pivot member */
char *file; /* current file or member being worked on */
Biobuf bout;
Biobuf bar;
+char *prefix;
void arcopy(Biobuf*, Arfile*, Armember*);
int arcreate(char*);
@@ -149,7 +152,7 @@ void arinsert(Arfile*, Armember*);
void *armalloc(int);
char *arstrdup(char*);
void armove(Biobuf*, Arfile*, Armember*);
-void arread(Biobuf*, Armember*, int);
+void arread(Biobuf*, Armember*);
void arstream(int, Arfile*);
int arwrite(int, Armember*);
int bamatch(char*, char*);
@@ -179,6 +182,7 @@ void trim(char*, char*, int);
void usage(void);
void wrerr(void);
void wrsym(Biobuf*, long, Arsymref*);
+int arread_cutprefix(Biobuf*, Armember*);
void rcmd(char*, int, char**); /* command processing */
void dcmd(char*, int, char**);
@@ -220,6 +224,7 @@ main(int argc, char *argv[])
case 'v': vflag = 1; break;
case 'x': setcom(xcmd); break;
case 'S': Sflag = 1; break;
+ case 'P': Pflag = 1; break;
default:
fprint(2, "gopack: bad option `%c'\n", *cp);
exits("error");
@@ -236,6 +241,15 @@ main(int argc, char *argv[])
if(argc < 3)
usage();
}
+ if(Pflag) {
+ if(argc < 4) {
+ fprint(2, "gopack: P flag requires prefix argument\n");
+ usage();
+ }
+ prefix = argv[2];
+ argv++;
+ argc--;
+ }
if(comfun == 0) {
if(uflag == 0) {
fprint(2, "gopack: one of [%s] must be specified\n", man);
@@ -313,7 +327,16 @@ rcmd(char *arname, int count, char **files)
skip(&bar, bp->size);
continue;
}
- if (count && !match(count, files)) {
+ /*
+ * the plan 9 ar treats count == 0 as equivalent
+ * to listing all the archive's files on the command line:
+ * it will try to open every file name in the archive
+ * and copy that file into the archive if it exists.
+ * for go we disable that behavior, because we use
+ * r with no files to make changes to the archive itself,
+ * using the S or P flags.
+ */
+ if (!match(count, files)) {
scanobj(&bar, ap, bp->size);
arcopy(&bar, ap, bp);
continue;
@@ -430,7 +453,7 @@ xcmd(char *arname, int count, char **files)
arcopy(&bar, 0, bp);
if (write(f, bp->member, bp->size) < 0)
wrerr();
- if(oflag) {
+ if(oflag && bp->date != 0) {
nulldir(&dx);
dx.atime = bp->date;
dx.mtime = bp->date;
@@ -972,7 +995,7 @@ phaseerr(int offset)
void
usage(void)
{
- fprint(2, "usage: gopack [%s][%s] archive files ...\n", opt, man);
+ fprint(2, "usage: gopack [%s][%s][P prefix] archive files ...\n", opt, man);
exits("error");
}
@@ -1012,29 +1035,32 @@ armove(Biobuf *b, Arfile *ap, Armember *bp)
{
char *cp;
Dir *d;
+ vlong n;
d = dirfstat(Bfildes(b));
if (d == nil) {
fprint(2, "gopack: cannot stat %s\n", file);
return;
}
+
trim(file, bp->hdr.name, sizeof(bp->hdr.name));
for (cp = strchr(bp->hdr.name, 0); /* blank pad on right */
cp < bp->hdr.name+sizeof(bp->hdr.name); cp++)
*cp = ' ';
- sprint(bp->hdr.date, "%-12ld", d->mtime);
+ sprint(bp->hdr.date, "%-12ld", 0); // was d->mtime but removed for idempotent builds
sprint(bp->hdr.uid, "%-6d", 0);
sprint(bp->hdr.gid, "%-6d", 0);
sprint(bp->hdr.mode, "%-8lo", d->mode);
sprint(bp->hdr.size, "%-10lld", d->length);
strncpy(bp->hdr.fmag, ARFMAG, 2);
bp->size = d->length;
- arread(b, bp, bp->size);
- if (d->length&0x01)
- d->length++;
+ arread(b, bp);
+ n = bp->size;
+ if (n&1)
+ n++;
if (ap) {
arinsert(ap, bp);
- ap->size += d->length+SAR_HDR;
+ ap->size += n+SAR_HDR;
}
free(d);
}
@@ -1047,10 +1073,10 @@ arcopy(Biobuf *b, Arfile *ap, Armember *bp)
{
long n;
+ arread(b, bp);
n = bp->size;
if (n & 01)
n++;
- arread(b, bp, n);
if (ap) {
arinsert(ap, bp);
ap->size += n+SAR_HDR;
@@ -1125,7 +1151,7 @@ rl(int fd)
len = symdefsize;
if(len&01)
len++;
- sprint(a.date, "%-12ld", time(0));
+ sprint(a.date, "%-12ld", 0); // time(0)
sprint(a.uid, "%-6d", 0);
sprint(a.gid, "%-6d", 0);
sprint(a.mode, "%-8lo", 0644L);
@@ -1162,7 +1188,7 @@ rl(int fd)
if (gflag) {
len = pkgdefsize;
- sprint(a.date, "%-12ld", time(0));
+ sprint(a.date, "%-12ld", 0); // time(0)
sprint(a.uid, "%-6d", 0);
sprint(a.gid, "%-6d", 0);
sprint(a.mode, "%-8lo", 0644L);
@@ -1316,7 +1342,8 @@ longt(Armember *bp)
Bprint(&bout, "%7ld", bp->size);
date = bp->date;
cp = ctime(&date);
- Bprint(&bout, " %-12.12s %-4.4s ", cp+4, cp+24);
+ /* using unix ctime, not plan 9 time, so cp+20 for year, not cp+24 */
+ Bprint(&bout, " %-12.12s %-4.4s ", cp+4, cp+20);
}
int m1[] = { 1, ROWN, 'r', '-' };
@@ -1378,17 +1405,29 @@ newmember(void) /* allocate a member buffer */
}
void
-arread(Biobuf *b, Armember *bp, int n) /* read an image into a member buffer */
+arread(Biobuf *b, Armember *bp) /* read an image into a member buffer */
{
int i;
+ vlong off;
- bp->member = armalloc(n);
- i = Bread(b, bp->member, n);
+ bp->member = armalloc(bp->size);
+
+ // If P flag is set, let arread_cutprefix try.
+ // If it succeeds, we're done. If not, fall back
+ // to a direct copy.
+ off = Boffset(b);
+ if(Pflag && arread_cutprefix(b, bp))
+ return;
+ Bseek(b, off, 0);
+
+ i = Bread(b, bp->member, bp->size);
if (i < 0) {
free(bp->member);
bp->member = 0;
rderr();
}
+ if(bp->size&1)
+ Bgetc(b);
}
/*
@@ -1551,3 +1590,109 @@ arstrdup(char *s)
}
+/*
+ * Parts of libmach we're not supposed
+ * to look at but need for arread_cutprefix.
+ */
+extern int _read5(Biobuf*, Prog*);
+extern int _read6(Biobuf*, Prog*);
+extern int _read8(Biobuf*, Prog*);
+int (*reader[256])(Biobuf*, Prog*) = {
+ [ObjArm] = _read5,
+ [ObjAmd64] = _read6,
+ [Obj386] = _read8,
+};
+
+/*
+ * copy b into bp->member but rewrite object
+ * during copy to drop prefix from all file names.
+ * return 1 if b was recognized as an object file
+ * and copied successfully, 0 otherwise.
+ */
+int
+arread_cutprefix(Biobuf *b, Armember *bp)
+{
+ vlong offset, o, end;
+ int n, t;
+ int (*rd)(Biobuf*, Prog*);
+ char *w, *inprefix;
+ Prog p;
+
+ offset = Boffset(b);
+ end = offset + bp->size;
+ t = objtype(b, nil);
+ if(t < 0)
+ return 0;
+ if((rd = reader[t]) == nil)
+ return 0;
+
+ // copy header
+ w = bp->member;
+ n = Boffset(b) - offset;
+ Bseek(b, -n, 1);
+ if(Bread(b, w, n) != n)
+ return 0;
+ offset += n;
+ w += n;
+
+ // read object file one pseudo-instruction at a time,
+ // eliding the file name instructions that refer to
+ // the prefix.
+ memset(&p, 0, sizeof p);
+ inprefix = nil;
+ while(Boffset(b) < end && rd(b, &p)) {
+ if(p.kind == aName && p.type == UNKNOWN && p.sym == 1 && p.id[0] == '<') {
+ // part of a file path.
+ // we'll keep continuing (skipping the copy)
+ // around the loop until either we get to a
+ // name piece that should be kept or we see
+ // the whole prefix.
+
+ if(inprefix == nil && prefix[0] == '/' && p.id[1] == '/' && p.id[2] == '\0') {
+ // leading /
+ inprefix = prefix+1;
+ } else if(inprefix != nil) {
+ // handle subsequent elements
+ n = strlen(p.id+1);
+ if(strncmp(p.id+1, inprefix, n) == 0 && (inprefix[n] == '/' || inprefix[n] == '\0')) {
+ inprefix += n;
+ if(inprefix[0] == '/')
+ inprefix++;
+ }
+ }
+
+ if(inprefix && inprefix[0] == '\0') {
+ // reached end of prefix.
+ // if we another path element follows,
+ // nudge the offset to skip over the prefix we saw.
+ // if not, leave offset alone, to emit the whole name.
+ // additional name elements will not be skipped
+ // because inprefix is now nil and we won't see another
+ // leading / in this name.
+ inprefix = nil;
+ o = Boffset(b);
+ if(o < end && rd(b, &p) && p.kind == aName && p.type == UNKNOWN && p.sym == 1 && p.id[0] == '<') {
+ // print("skip %lld-%lld\n", offset, o);
+ offset = o;
+ }
+ }
+ }
+
+ // copy instructions
+ if(!inprefix) {
+ n = Boffset(b) - offset;
+ Bseek(b, -n, 1);
+ if(Bread(b, w, n) != n)
+ return 0;
+ offset += n;
+ w += n;
+ }
+ }
+ bp->size = w - (char*)bp->member;
+ sprint(bp->hdr.size, "%-10lld", (vlong)bp->size);
+ strncpy(bp->hdr.fmag, ARFMAG, 2);
+ Bseek(b, end, 0);
+ if(Boffset(b)&1)
+ Bgetc(b);
+ return 1;
+}
diff --git a/src/cmd/gopack/doc.go b/src/cmd/gopack/doc.go
index 08711e72e..1551a275f 100644
--- a/src/cmd/gopack/doc.go
+++ b/src/cmd/gopack/doc.go
@@ -12,12 +12,15 @@ It adds a special Go-specific section __.PKGDEF that collects all the
Go type information from the files in the archive; that section is
used by the compiler when importing the package during compilation.
-Usage: gopack [uvnbailogS][mrxtdpq] archive files ...
+Usage: gopack [uvnbailogS][mrxtdpq][P prefix] archive files ...
The new option 'g' causes gopack to maintain the __.PKGDEF section
as files are added to the archive.
The new option 'S' forces gopack to mark the archive as safe.
+The new option 'P' causes gopack to remove the given prefix
+from file names in the line number information in object files
+that are already stored in or added to the archive.
*/
package documentation
diff --git a/src/cmd/gotest/Makefile b/src/cmd/gotest/Makefile
index 74054e974..5c1154537 100644
--- a/src/cmd/gotest/Makefile
+++ b/src/cmd/gotest/Makefile
@@ -4,15 +4,9 @@
include ../../Make.inc
-TARG=install
-
-clean:
- @true
-
-install: install-gotest install-gotry
-
-install-%: %
- ! test -f "$(GOBIN)"/$* || chmod u+w "$(GOBIN)"/$*
- sed 's`@@GOROOT@@`$(GOROOT_FINAL)`' $* >"$(GOBIN)"/$*
- chmod +x "$(GOBIN)"/$*
+TARG=gotest
+GOFILES=\
+ flag.go\
+ gotest.go\
+include ../../Make.cmd
diff --git a/src/cmd/gotest/doc.go b/src/cmd/gotest/doc.go
index 581eaaab9..9dba390c1 100644
--- a/src/cmd/gotest/doc.go
+++ b/src/cmd/gotest/doc.go
@@ -6,22 +6,23 @@
Gotest is an automated testing tool for Go packages.
-Normally a Go package is compiled without its test files. Gotest
-is a simple script that recompiles the package along with any files
-named *_test.go. Functions in the test sources named TestXXX
-(where XXX is any alphanumeric string starting with an upper case
-letter) will be run when the binary is executed. Gotest requires
-that the package have a standard package Makefile, one that
-includes go/src/Make.pkg.
+Normally a Go package is compiled without its test files. Gotest is a
+tool that recompiles the package whose source is in the current
+directory, along with any files whose names match the pattern
+"[^.]*_test.go". Functions in the test source named TestXXX (where
+XXX is any alphanumeric string not starting with a lower case letter)
+will be run when the binary is executed. Gotest requires that the
+package have a standard package Makefile, one that includes
+go/src/Make.pkg.
The test functions are run in the order they appear in the source.
-They should have signature
+They should have the signature,
func TestXXX(t *testing.T) { ... }
-Benchmark functions can be written as well; they will be run only
-when the -test.bench flag is provided. Benchmarks should have
-signature
+Benchmark functions can be written as well; they will be run only when
+the -test.bench flag is provided. Benchmarks should have the
+signature,
func BenchmarkXXX(b *testing.B) { ... }
@@ -29,28 +30,75 @@ See the documentation of the testing package for more information.
By default, gotest needs no arguments. It compiles all the .go files
in the directory, including tests, and runs the tests. If file names
-are given, only those test files are added to the package.
-(The non-test files are always compiled.)
+are given (with flag -file=test.go, one per extra test source file),
+only those test files are added to the package. (The non-test files
+are always compiled.)
The package is built in a special subdirectory so it does not
interfere with the non-test installation.
Usage:
- gotest [pkg_test.go ...]
+ gotest [-file a.go -file b.go ...] [-c] [-x] [args for test binary]
-The resulting binary, called (for amd64) 6.out, has a couple of
-arguments.
+The flags specific to gotest are:
+ -c Compile the test binary but do not run it.
+ -file a.go Use only the tests in the source file a.go.
+ Multiple -file flags may be provided.
+ -x Print each subcommand gotest executes.
+
+Everything else on the command line is passed to the test binary.
+
+The resulting test binary, called (for amd64) 6.out, has several flags.
Usage:
- 6.out [-test.v] [-test.run pattern] [-test.bench pattern]
+ 6.out [-test.v] [-test.run pattern] [-test.bench pattern] \
+ [-test.cpuprofile=cpu.out] \
+ [-test.memprofile=mem.out] [-test.memprofilerate=1]
The -test.v flag causes the tests to be logged as they run. The
-test.run flag causes only those tests whose names match the regular
-expression pattern to be run. By default all tests are run silently.
-If all the specified test pass, 6.out prints PASS and exits with a 0
-exit code. If any tests fail, it prints FAIL and exits with a
-non-zero code. The -test.bench flag is analogous to the -test.run
-flag, but applies to benchmarks. No benchmarks run by default.
+expression pattern to be run. By default all tests are run silently.
+
+If all specified tests pass, 6.out prints the word PASS and exits with
+a 0 exit code. If any tests fail, it prints error details, the word
+FAIL, and exits with a non-zero code. The -test.bench flag is
+analogous to the -test.run flag, but applies to benchmarks. No
+benchmarks run by default.
+
+The -test.cpuprofile flag causes the testing software to write a CPU
+profile to the specified file before exiting.
+
+The -test.memprofile flag causes the testing software to write a
+memory profile to the specified file when all tests are complete. The
+-test.memprofilerate flag enables more precise (and expensive)
+profiles by setting runtime.MemProfileRate; run
+ godoc runtime MemProfileRate
+for details. The defaults are no memory profile and the standard
+setting of MemProfileRate. The memory profile records a sampling of
+the memory in use at the end of the test. To profile all memory
+allocations, use -test.memprofilerate=1 to sample every byte and set
+the environment variable GOGC=off to disable the garbage collector,
+provided the test can run in the available memory without garbage
+collection.
+
+Use -test.run or -test.bench to limit profiling to a particular test
+or benchmark.
+
+The -test.short flag tells long-running tests to shorten their run
+time. It is off by default but set by all.bash so installations of
+the Go tree can do a sanity check but not spend time running
+exhaustive tests.
+
+The -test.timeout flag sets a timeout for the test in seconds. If the
+test runs for longer than that, it will panic, dumping a stack trace
+of all existing goroutines.
+
+For convenience, each of these -test.X flags of the test binary is
+also available as the flag -X in gotest itself. Flags not listed here
+are unaffected. For instance, the command
+ gotest -x -v -cpuprofile=prof.out -dir=testdata -update -file x_test.go
+will compile the test binary using x_test.go and then run it as
+ 6.out -test.v -test.cpuprofile=prof.out -dir=testdata -update
*/
package documentation
diff --git a/src/cmd/gotest/flag.go b/src/cmd/gotest/flag.go
new file mode 100644
index 000000000..780c78b9c
--- /dev/null
+++ b/src/cmd/gotest/flag.go
@@ -0,0 +1,155 @@
+// 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"
+ "os"
+ "strconv"
+ "strings"
+)
+
+// The flag handling part of gotest is large and distracting.
+// We can't use the flag package because some of the flags from
+// our command line are for us, and some are for 6.out, and
+// some are for both.
+
+var usageMessage = `Usage of %s:
+ -c=false: compile but do not run the test binary
+ -file=file:
+ -x=false: print command lines as they are executed
+
+ // These flags can be passed with or without a "test." prefix: -v or -test.v.
+ -bench="": passes -test.bench to test
+ -cpuprofile="": passes -test.cpuprofile to test
+ -memprofile="": passes -test.memprofile to test
+ -memprofilerate=0: passes -test.memprofilerate to test
+ -run="": passes -test.run to test
+ -short=false: passes -test.short to test
+ -timeout=0: passes -test.timeout to test
+ -v=false: passes -test.v to test
+`
+
+// usage prints a usage message and exits.
+func usage() {
+ fmt.Fprintf(os.Stdout, usageMessage, os.Args[0])
+ os.Exit(2)
+}
+
+// flagSpec defines a flag we know about.
+type flagSpec struct {
+ name string
+ isBool bool
+ passToTest bool // pass to Test
+ multiOK bool // OK to have multiple instances
+ present bool // flag has been seen
+}
+
+// flagDefn is the set of flags we process.
+var flagDefn = []*flagSpec{
+ // gotest-local.
+ &flagSpec{name: "c", isBool: true},
+ &flagSpec{name: "file", multiOK: true},
+ &flagSpec{name: "x", isBool: true},
+
+ // passed to 6.out, adding a "test." prefix to the name if necessary: -v becomes -test.v.
+ &flagSpec{name: "bench", passToTest: true},
+ &flagSpec{name: "cpuprofile", passToTest: true},
+ &flagSpec{name: "memprofile", passToTest: true},
+ &flagSpec{name: "memprofilerate", passToTest: true},
+ &flagSpec{name: "run", passToTest: true},
+ &flagSpec{name: "short", isBool: true, passToTest: true},
+ &flagSpec{name: "timeout", passToTest: true},
+ &flagSpec{name: "v", isBool: true, passToTest: true},
+}
+
+// flags processes the command line, grabbing -x and -c, rewriting known flags
+// to have "test" before them, and reading the command line for the 6.out.
+// Unfortunately for us, we need to do our own flag processing because gotest
+// grabs some flags but otherwise its command line is just a holding place for
+// 6.out's arguments.
+func flags() {
+ for i := 1; i < len(os.Args); i++ {
+ arg := os.Args[i]
+ f, value, extraWord := flag(i)
+ if f == nil {
+ args = append(args, arg)
+ continue
+ }
+ switch f.name {
+ case "c":
+ setBoolFlag(&cFlag, value)
+ case "x":
+ setBoolFlag(&xFlag, value)
+ case "file":
+ fileNames = append(fileNames, value)
+ }
+ if extraWord {
+ i++
+ }
+ if f.passToTest {
+ args = append(args, "-test."+f.name+"="+value)
+ }
+ }
+}
+
+// flag sees if argument i is a known flag and returns its definition, value, and whether it consumed an extra word.
+func flag(i int) (f *flagSpec, value string, extra bool) {
+ arg := os.Args[i]
+ if strings.HasPrefix(arg, "--") { // reduce two minuses to one
+ arg = arg[1:]
+ }
+ if arg == "" || arg[0] != '-' {
+ return
+ }
+ name := arg[1:]
+ // If there's already "test.", drop it for now.
+ if strings.HasPrefix(name, "test.") {
+ name = name[5:]
+ }
+ equals := strings.Index(name, "=")
+ if equals >= 0 {
+ value = name[equals+1:]
+ name = name[:equals]
+ }
+ for _, f = range flagDefn {
+ if name == f.name {
+ // Booleans are special because they have modes -x, -x=true, -x=false.
+ if f.isBool {
+ if equals < 0 { // otherwise, it's been set and will be verified in setBoolFlag
+ value = "true"
+ } else {
+ // verify it parses
+ setBoolFlag(new(bool), value)
+ }
+ } else { // Non-booleans must have a value.
+ extra = equals < 0
+ if extra {
+ if i+1 >= len(os.Args) {
+ usage()
+ }
+ value = os.Args[i+1]
+ }
+ }
+ if f.present && !f.multiOK {
+ usage()
+ }
+ f.present = true
+ return
+ }
+ }
+ f = nil
+ return
+}
+
+// setBoolFlag sets the addressed boolean to the value.
+func setBoolFlag(flag *bool, value string) {
+ x, err := strconv.Atob(value)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "gotest: illegal bool flag value %s\n", value)
+ usage()
+ }
+ *flag = x
+}
diff --git a/src/cmd/gotest/gotest b/src/cmd/gotest/gotest
deleted file mode 100755
index 69eaae730..000000000
--- a/src/cmd/gotest/gotest
+++ /dev/null
@@ -1,197 +0,0 @@
-#!/usr/bin/env bash
-# Copyright 2009 The Go Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file.
-
-# Using all the *_test.go files in the current directory, write out a file
-# _testmain.go that runs all its tests. Compile everything and run the
-# tests.
-# If files are named on the command line, use them instead of *_test.go.
-
-# Makes egrep,grep work better in general if we put them
-# in ordinary C mode instead of what the current language is.
-unset LANG
-export LC_ALL=C
-export LC_CTYPE=C
-
-_GC=$GC # Make.inc will overwrite this
-
-if [ ! -f [Mm]akefile ]; then
- echo 'please create a Makefile for gotest; see http://golang.org/doc/code.html for details' 1>&2
- exit 2
-fi
-
-export GOROOT=${GOROOT:-"@@GOROOT@@"}
-eval $(gomake -j1 --no-print-directory -f "$GOROOT"/src/Make.inc go-env)
-if [ -z "$O" ]; then
- echo 'missing $O - maybe no Make.$GOARCH?' 1>&2
- exit 2
-fi
-
-E="$GORUN"
-
-# Allow overrides
-GC="${_GC:-$GC} -I _test"
-GL="${GL:-$LD} -L _test"
-AS="$AS"
-CC="$CC"
-LD="$LD"
-export GC GL O AS CC LD
-
-gofiles=""
-loop=true
-while $loop; do
- case "x$1" in
- x-*)
- loop=false
- ;;
- x)
- loop=false
- ;;
- *)
- gofiles="$gofiles $1"
- shift
- ;;
- esac
-done
-
-case "x$gofiles" in
-x)
- gofiles=$(echo -n $(ls *_test.go 2>/dev/null))
-esac
-
-case "x$gofiles" in
-x)
- echo 'no test files found (*_test.go)' 1>&2
- exit 2
-esac
-
-# Run any commands given in sources, like
-# // gotest: $GC foo.go
-# to build any test-only dependencies.
-sed -n 's/^\/\/ gotest: //p' $gofiles | sh -e || exit 1
-
-# Split $gofiles into external gofiles (those in *_test packages)
-# and internal ones (those in the main package).
-xgofiles=$(echo $(grep '^package[ ]' $gofiles /dev/null | grep ':.*_test' | sed 's/:.*//'))
-gofiles=$(echo $(grep '^package[ ]' $gofiles /dev/null | grep -v ':.*_test' | sed 's/:.*//'))
-
-# External $O file
-xofile=""
-havex=false
-if [ "x$xgofiles" != "x" ]; then
- xofile="_xtest_.$O"
- havex=true
-fi
-
-set -e
-
-gomake testpackage-clean
-gomake testpackage "GOTESTFILES=$gofiles"
-if $havex; then
- $GC -o $xofile $xgofiles
-fi
-
-# They all compile; now generate the code to call them.
-
-# Suppress output to stdout on Linux
-MAKEFLAGS=
-MAKELEVEL=
-
-# usage: nmgrep pattern file...
-nmgrep() {
- pat="$1"
- shift
- for i
- do
- # Package symbol "".Foo is pkg.Foo when imported in Go.
- # Figure out pkg.
- case "$i" in
- *.a)
- pkg=$(gopack p $i __.PKGDEF | sed -n 's/^package //p' | sed 's/ .*//' | sed 1q)
- ;;
- *)
- pkg=$(sed -n 's/^ .* in package "\(.*\)".*/\1/p' $i | sed 1q)
- ;;
- esac
- 6nm -s "$i" | egrep ' T .*\.'"$pat"'$' |
- sed 's/.* //; /\..*\./d; s/""\./'"$pkg"'./g'
- done
-}
-
-localname() {
- # The package main has been renamed to __main__ when imported.
- # Adjust its uses.
- echo $1 | sed 's/^main\./__main__./'
-}
-
-importpath=$(gomake -s importpath)
-{
- # test functions are named TestFoo
- # the grep -v eliminates methods and other special names
- # that have multiple dots.
- pattern='Test([^a-z].*)?'
- tests=$(nmgrep $pattern _test/$importpath.a $xofile)
- if [ "x$tests" = x ]; then
- echo 'gotest: error: no tests matching '$pattern in _test/$importpath.a $xofile 1>&2
- exit 2
- fi
- # benchmarks are named BenchmarkFoo.
- pattern='Benchmark([^a-z].*)?'
- benchmarks=$(nmgrep $pattern _test/$importpath.a $xofile)
-
- # package spec
- echo 'package main'
- echo
- # imports
- if echo "$tests" | egrep -v '_test\.' >/dev/null; then
- case "$importpath" in
- testing)
- ;;
- main)
- # Import path main is reserved, so import with
- # explicit reference to ./_test/main instead.
- # Also, the file we are writing defines a function named main,
- # so rename this import to __main__ to avoid name conflict.
- echo 'import __main__ "./_test/main"'
- ;;
- *)
- echo 'import "'$importpath'"'
- ;;
- esac
- fi
- if $havex; then
- echo 'import "./_xtest_"'
- fi
- echo 'import "testing"'
- echo 'import __regexp__ "regexp"' # rename in case tested package is called regexp
- # test array
- echo
- echo 'var tests = []testing.InternalTest{'
- for i in $tests
- do
- j=$(localname $i)
- echo ' {"'$i'", '$j'},'
- done
- echo '}'
- # benchmark array
- # The comment makes the multiline declaration
- # gofmt-safe even when there are no benchmarks.
- echo 'var benchmarks = []testing.InternalBenchmark{ //'
- for i in $benchmarks
- do
- j=$(localname $i)
- echo ' {"'$i'", '$j'},'
- done
- echo '}'
- # body
- echo
- echo 'func main() {'
- echo ' testing.Main(__regexp__.MatchString, tests)'
- echo ' testing.RunBenchmarks(__regexp__.MatchString, benchmarks)'
- echo '}'
-}>_testmain.go
-
-$GC _testmain.go
-$GL _testmain.$O
-$E ./$O.out "$@"
diff --git a/src/cmd/gotest/gotest.go b/src/cmd/gotest/gotest.go
new file mode 100644
index 000000000..138216e68
--- /dev/null
+++ b/src/cmd/gotest/gotest.go
@@ -0,0 +1,415 @@
+// 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 (
+ "bufio"
+ "exec"
+ "fmt"
+ "go/ast"
+ "go/parser"
+ "go/token"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "unicode"
+ "utf8"
+)
+
+// Environment for commands.
+var (
+ XGC []string // 6g -I _test -o _xtest_.6
+ GC []string // 6g -I _test _testmain.go
+ GL []string // 6l -L _test _testmain.6
+ GOARCH string
+ GOROOT string
+ GORUN string
+ O string
+ args []string // arguments passed to gotest; also passed to the binary
+ fileNames []string
+ env = os.Environ()
+)
+
+// These strings are created by getTestNames.
+var (
+ insideFileNames []string // list of *.go files inside the package.
+ outsideFileNames []string // list of *.go files outside the package (in package foo_test).
+)
+
+var (
+ files []*File
+ importPath string
+)
+
+// Flags for our own purposes. We do our own flag processing.
+var (
+ cFlag bool
+ xFlag bool
+)
+
+// File represents a file that contains tests.
+type File struct {
+ name string
+ pkg string
+ file *os.File
+ astFile *ast.File
+ tests []string // The names of the TestXXXs.
+ benchmarks []string // The names of the BenchmarkXXXs.
+}
+
+func main() {
+ flags()
+ needMakefile()
+ setEnvironment()
+ getTestFileNames()
+ parseFiles()
+ getTestNames()
+ run("gomake", "testpackage-clean")
+ run("gomake", "testpackage", fmt.Sprintf("GOTESTFILES=%s", strings.Join(insideFileNames, " ")))
+ if len(outsideFileNames) > 0 {
+ run(append(XGC, outsideFileNames...)...)
+ }
+ importPath = runWithStdout("gomake", "-s", "importpath")
+ writeTestmainGo()
+ run(GC...)
+ run(GL...)
+ if !cFlag {
+ runTestWithArgs("./" + O + ".out")
+ }
+}
+
+// needMakefile tests that we have a Makefile in this directory.
+func needMakefile() {
+ if _, err := os.Stat("Makefile"); err != nil {
+ Fatalf("please create a Makefile for gotest; see http://golang.org/doc/code.html for details")
+ }
+}
+
+// Fatalf formats its arguments, prints the message with a final newline, and exits.
+func Fatalf(s string, args ...interface{}) {
+ fmt.Fprintf(os.Stderr, "gotest: "+s+"\n", args...)
+ os.Exit(2)
+}
+
+// theChar is the map from architecture to object character.
+var theChar = map[string]string{
+ "arm": "5",
+ "amd64": "6",
+ "386": "8",
+}
+
+// addEnv adds a name=value pair to the environment passed to subcommands.
+// If the item is already in the environment, addEnv replaces the value.
+func addEnv(name, value string) {
+ for i := 0; i < len(env); i++ {
+ if strings.HasPrefix(env[i], name+"=") {
+ env[i] = name + "=" + value
+ return
+ }
+ }
+ env = append(env, name+"="+value)
+}
+
+// setEnvironment assembles the configuration for gotest and its subcommands.
+func setEnvironment() {
+ // Basic environment.
+ GOROOT = runtime.GOROOT()
+ addEnv("GOROOT", GOROOT)
+ GOARCH = runtime.GOARCH
+ addEnv("GOARCH", GOARCH)
+ O = theChar[GOARCH]
+ if O == "" {
+ Fatalf("unknown architecture %s", GOARCH)
+ }
+
+ // Commands and their flags.
+ gc := os.Getenv("GC")
+ if gc == "" {
+ gc = O + "g"
+ }
+ XGC = []string{gc, "-I", "_test", "-o", "_xtest_." + O}
+ GC = []string{gc, "-I", "_test", "_testmain.go"}
+ gl := os.Getenv("GL")
+ if gl == "" {
+ gl = O + "l"
+ }
+ GL = []string{gl, "-L", "_test", "_testmain." + O}
+
+ // Silence make on Linux
+ addEnv("MAKEFLAGS", "")
+ addEnv("MAKELEVEL", "")
+}
+
+// getTestFileNames gets the set of files we're looking at.
+// If gotest has no arguments, it scans for file names matching "[^.]*_test.go".
+func getTestFileNames() {
+ names := fileNames
+ if len(names) == 0 {
+ var err os.Error
+ names, err = filepath.Glob("[^.]*_test.go")
+ if err != nil {
+ Fatalf("Glob pattern error: %s", err)
+ }
+ if len(names) == 0 {
+ Fatalf(`no test files found: no match for "[^.]*_test.go"`)
+ }
+ }
+ for _, n := range names {
+ fd, err := os.Open(n)
+ if err != nil {
+ Fatalf("%s: %s", n, err)
+ }
+ f := &File{name: n, file: fd}
+ files = append(files, f)
+ }
+}
+
+// parseFiles parses the files and remembers the packages we find.
+func parseFiles() {
+ fileSet := token.NewFileSet()
+ for _, f := range files {
+ // Report declaration errors so we can abort if the files are incorrect Go.
+ file, err := parser.ParseFile(fileSet, f.name, nil, parser.DeclarationErrors)
+ if err != nil {
+ Fatalf("parse error: %s", err)
+ }
+ f.astFile = file
+ f.pkg = file.Name.String()
+ if f.pkg == "" {
+ Fatalf("cannot happen: no package name in %s", f.name)
+ }
+ }
+}
+
+// getTestNames extracts the names of tests and benchmarks. They are all
+// top-level functions that are not methods.
+func getTestNames() {
+ for _, f := range files {
+ for _, d := range f.astFile.Decls {
+ n, ok := d.(*ast.FuncDecl)
+ if !ok {
+ continue
+ }
+ if n.Recv != nil { // a method, not a function.
+ continue
+ }
+ name := n.Name.String()
+ if isTest(name, "Test") {
+ f.tests = append(f.tests, name)
+ } else if isTest(name, "Benchmark") {
+ f.benchmarks = append(f.benchmarks, name)
+ }
+ // TODO: worth checking the signature? Probably not.
+ }
+ if strings.HasSuffix(f.pkg, "_test") {
+ outsideFileNames = append(outsideFileNames, f.name)
+ } else {
+ insideFileNames = append(insideFileNames, f.name)
+ }
+ }
+}
+
+// isTest tells whether name looks like a test (or benchmark, according to prefix).
+// It is a Test (say) if there is a character after Test that is not a lower-case letter.
+// We don't want TesticularCancer.
+func isTest(name, prefix string) bool {
+ if !strings.HasPrefix(name, prefix) {
+ return false
+ }
+ if len(name) == len(prefix) { // "Test" is ok
+ return true
+ }
+ rune, _ := utf8.DecodeRuneInString(name[len(prefix):])
+ return !unicode.IsLower(rune)
+}
+
+func run(args ...string) {
+ doRun(args, false)
+}
+
+// runWithStdout is like run, but returns the text of standard output with the last newline dropped.
+func runWithStdout(argv ...string) string {
+ s := doRun(argv, true)
+ if strings.HasSuffix(s, "\r\n") {
+ s = s[:len(s)-2]
+ } else if strings.HasSuffix(s, "\n") {
+ s = s[:len(s)-1]
+ }
+ if len(s) == 0 {
+ Fatalf("no output from command %s", strings.Join(argv, " "))
+ }
+ return s
+}
+
+// runTestWithArgs appends gotest's runs the provided binary with the args passed on the command line.
+func runTestWithArgs(binary string) {
+ doRun(append([]string{binary}, args...), false)
+}
+
+// doRun is the general command runner. The flag says whether we want to
+// retrieve standard output.
+func doRun(argv []string, returnStdout bool) string {
+ if xFlag {
+ fmt.Printf("gotest: %s\n", strings.Join(argv, " "))
+ }
+ command := argv[0]
+ if runtime.GOOS == "windows" && command == "gomake" {
+ // gomake is a shell script and it cannot be executed directly on Windows.
+ cmd := ""
+ for i, v := range argv {
+ if i > 0 {
+ cmd += " "
+ }
+ cmd += `"` + v + `"`
+ }
+ argv = []string{"cmd", "/c", "sh", "-c", cmd}
+ }
+ var err os.Error
+ argv[0], err = exec.LookPath(argv[0])
+ if err != nil {
+ Fatalf("can't find %s: %s", command, err)
+ }
+ procAttr := &os.ProcAttr{
+ Env: env,
+ Files: []*os.File{
+ os.Stdin,
+ os.Stdout,
+ os.Stderr,
+ },
+ }
+ var r, w *os.File
+ if returnStdout {
+ r, w, err = os.Pipe()
+ if err != nil {
+ Fatalf("can't create pipe: %s", err)
+ }
+ procAttr.Files[1] = w
+ }
+ proc, err := os.StartProcess(argv[0], argv, procAttr)
+ if err != nil {
+ Fatalf("%s failed to start: %s", command, err)
+ }
+ if returnStdout {
+ defer r.Close()
+ w.Close()
+ }
+ waitMsg, err := proc.Wait(0)
+ if err != nil || waitMsg == nil {
+ Fatalf("%s failed: %s", command, err)
+ }
+ if !waitMsg.Exited() || waitMsg.ExitStatus() != 0 {
+ Fatalf("%q failed: %s", strings.Join(argv, " "), waitMsg)
+ }
+ if returnStdout {
+ b, err := ioutil.ReadAll(r)
+ if err != nil {
+ Fatalf("can't read output from command: %s", err)
+ }
+ return string(b)
+ }
+ return ""
+}
+
+// writeTestmainGo generates the test program to be compiled, "./_testmain.go".
+func writeTestmainGo() {
+ f, err := os.Create("_testmain.go")
+ if err != nil {
+ Fatalf("can't create _testmain.go: %s", err)
+ }
+ defer f.Close()
+ b := bufio.NewWriter(f)
+ defer b.Flush()
+
+ // Package and imports.
+ fmt.Fprint(b, "package main\n\n")
+ // Are there tests from a package other than the one we're testing?
+ // We can't just use file names because some of the things we compiled
+ // contain no tests.
+ outsideTests := false
+ insideTests := false
+ for _, f := range files {
+ //println(f.name, f.pkg)
+ if len(f.tests) == 0 && len(f.benchmarks) == 0 {
+ continue
+ }
+ if strings.HasSuffix(f.pkg, "_test") {
+ outsideTests = true
+ } else {
+ insideTests = true
+ }
+ }
+ if insideTests {
+ switch importPath {
+ case "testing":
+ case "main":
+ // Import path main is reserved, so import with
+ // explicit reference to ./_test/main instead.
+ // Also, the file we are writing defines a function named main,
+ // so rename this import to __main__ to avoid name conflict.
+ fmt.Fprintf(b, "import __main__ %q\n", "./_test/main")
+ default:
+ fmt.Fprintf(b, "import %q\n", importPath)
+ }
+ }
+ if outsideTests {
+ fmt.Fprintf(b, "import %q\n", "./_xtest_")
+ }
+ fmt.Fprintf(b, "import %q\n", "testing")
+ fmt.Fprintf(b, "import __os__ %q\n", "os") // rename in case tested package is called os
+ fmt.Fprintf(b, "import __regexp__ %q\n", "regexp") // rename in case tested package is called regexp
+ fmt.Fprintln(b) // for gofmt
+
+ // Tests.
+ fmt.Fprintln(b, "var tests = []testing.InternalTest{")
+ for _, f := range files {
+ for _, t := range f.tests {
+ fmt.Fprintf(b, "\t{\"%s.%s\", %s.%s},\n", f.pkg, t, notMain(f.pkg), t)
+ }
+ }
+ fmt.Fprintln(b, "}")
+ fmt.Fprintln(b)
+
+ // Benchmarks.
+ fmt.Fprintln(b, "var benchmarks = []testing.InternalBenchmark{")
+ for _, f := range files {
+ for _, bm := range f.benchmarks {
+ fmt.Fprintf(b, "\t{\"%s.%s\", %s.%s},\n", f.pkg, bm, notMain(f.pkg), bm)
+ }
+ }
+ fmt.Fprintln(b, "}")
+
+ // Body.
+ fmt.Fprintln(b, testBody)
+}
+
+// notMain returns the package, renaming as appropriate if it's "main".
+func notMain(pkg string) string {
+ if pkg == "main" {
+ return "__main__"
+ }
+ return pkg
+}
+
+// testBody is just copied to the output. It's the code that runs the tests.
+var testBody = `
+var matchPat string
+var matchRe *__regexp__.Regexp
+
+func matchString(pat, str string) (result bool, err __os__.Error) {
+ if matchRe == nil || matchPat != pat {
+ matchPat = pat
+ matchRe, err = __regexp__.Compile(matchPat)
+ if err != nil {
+ return
+ }
+ }
+ return matchRe.MatchString(str), nil
+}
+
+func main() {
+ testing.Main(matchString, tests, benchmarks)
+}`
diff --git a/src/cmd/gotry/Makefile b/src/cmd/gotry/Makefile
new file mode 100644
index 000000000..6a32bbf2d
--- /dev/null
+++ b/src/cmd/gotry/Makefile
@@ -0,0 +1,18 @@
+# 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.
+
+include ../../Make.inc
+
+TARG=install
+
+clean:
+ @true
+
+install: install-gotry
+
+install-%: %
+ ! test -f "$(GOBIN)"/$* || chmod u+w "$(GOBIN)"/$*
+ sed 's`@@GOROOT@@`$(GOROOT_FINAL)`' $* >"$(GOBIN)"/$*
+ chmod +x "$(GOBIN)"/$*
+
diff --git a/src/cmd/gotest/gotry b/src/cmd/gotry/gotry
index 52c5d2d58..52c5d2d58 100755
--- a/src/cmd/gotest/gotry
+++ b/src/cmd/gotry/gotry
diff --git a/src/cmd/gotype/Makefile b/src/cmd/gotype/Makefile
new file mode 100644
index 000000000..18171945d
--- /dev/null
+++ b/src/cmd/gotype/Makefile
@@ -0,0 +1,17 @@
+# Copyright 2011 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+include ../../Make.inc
+
+TARG=gotype
+GOFILES=\
+ gotype.go\
+
+include ../../Make.cmd
+
+test:
+ gotest
+
+testshort:
+ gotest -test.short
diff --git a/src/cmd/gotype/doc.go b/src/cmd/gotype/doc.go
new file mode 100644
index 000000000..ec4eb7c24
--- /dev/null
+++ b/src/cmd/gotype/doc.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.
+
+/*
+The gotype command does syntactic and semantic analysis of Go files
+and packages similar to the analysis performed by the front-end of
+a Go compiler. Errors are reported if the analysis fails; otherwise
+gotype is quiet (unless -v is set).
+
+Without a list of paths, gotype processes the standard input, which must
+be the source of a single package file.
+
+Given a list of file names, each file must be a source file belonging to
+the same package unless the package name is explicitly specified with the
+-p flag.
+
+Given a directory name, gotype collects all .go files in the directory
+and processes them as if they were provided as an explicit list of file
+names. Each directory is processed independently. Files starting with .
+or not ending in .go are ignored.
+
+Usage:
+ gotype [flags] [path ...]
+
+The flags are:
+ -p pkgName
+ process only those files in package pkgName.
+ -r
+ recursively process subdirectories.
+ -v
+ verbose mode.
+
+Debugging flags:
+ -trace
+ print parse trace (disables concurrent parsing).
+ -ast
+ print AST (disables concurrent parsing).
+
+
+Examples
+
+To check the files file.go, old.saved, and .ignored:
+
+ gotype file.go old.saved .ignored
+
+To check all .go files belonging to package main in the current directory
+and recursively in all subdirectories:
+
+ gotype -p main -r .
+
+To verify the output of a pipe:
+
+ echo "package foo" | gotype
+
+*/
+package documentation
+
+// BUG(gri): At the moment, only single-file scope analysis is performed.
diff --git a/src/cmd/gotype/gotype.go b/src/cmd/gotype/gotype.go
new file mode 100644
index 000000000..568467322
--- /dev/null
+++ b/src/cmd/gotype/gotype.go
@@ -0,0 +1,198 @@
+// 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 (
+ "flag"
+ "fmt"
+ "go/ast"
+ "go/parser"
+ "go/scanner"
+ "go/token"
+ "go/types"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+
+var (
+ // main operation modes
+ pkgName = flag.String("p", "", "process only those files in package pkgName")
+ recursive = flag.Bool("r", false, "recursively process subdirectories")
+ verbose = flag.Bool("v", false, "verbose mode")
+
+ // debugging support
+ printTrace = flag.Bool("trace", false, "print parse trace")
+ printAST = flag.Bool("ast", false, "print AST")
+)
+
+
+var exitCode = 0
+
+
+func usage() {
+ fmt.Fprintf(os.Stderr, "usage: gotype [flags] [path ...]\n")
+ flag.PrintDefaults()
+ os.Exit(2)
+}
+
+
+func report(err os.Error) {
+ scanner.PrintError(os.Stderr, err)
+ exitCode = 2
+}
+
+
+// parse returns the AST for the Go source src.
+// The filename is for error reporting only.
+// The result is nil if there were errors or if
+// the file does not belong to the -p package.
+func parse(fset *token.FileSet, filename string, src []byte) *ast.File {
+ if *verbose {
+ fmt.Println(filename)
+ }
+
+ // ignore files with different package name
+ if *pkgName != "" {
+ file, err := parser.ParseFile(fset, filename, src, parser.PackageClauseOnly)
+ if err != nil {
+ report(err)
+ return nil
+ }
+ if file.Name.Name != *pkgName {
+ if *verbose {
+ fmt.Printf("\tignored (package %s)\n", file.Name.Name)
+ }
+ return nil
+ }
+ }
+
+ // parse entire file
+ mode := parser.DeclarationErrors
+ if *printTrace {
+ mode |= parser.Trace
+ }
+ file, err := parser.ParseFile(fset, filename, src, mode)
+ if err != nil {
+ report(err)
+ return nil
+ }
+ if *printAST {
+ ast.Print(fset, file)
+ }
+
+ return file
+}
+
+
+func parseStdin(fset *token.FileSet) (files map[string]*ast.File) {
+ files = make(map[string]*ast.File)
+ src, err := ioutil.ReadAll(os.Stdin)
+ if err != nil {
+ report(err)
+ return
+ }
+ const filename = "<standard input>"
+ if file := parse(fset, filename, src); file != nil {
+ files[filename] = file
+ }
+ return
+}
+
+
+func parseFiles(fset *token.FileSet, filenames []string) (files map[string]*ast.File) {
+ files = make(map[string]*ast.File)
+ for _, filename := range filenames {
+ src, err := ioutil.ReadFile(filename)
+ if err != nil {
+ report(err)
+ continue
+ }
+ if file := parse(fset, filename, src); file != nil {
+ if files[filename] != nil {
+ report(os.ErrorString(fmt.Sprintf("%q: duplicate file", filename)))
+ continue
+ }
+ files[filename] = file
+ }
+ }
+ return
+}
+
+
+func isGoFilename(filename string) bool {
+ // ignore non-Go files
+ return !strings.HasPrefix(filename, ".") && strings.HasSuffix(filename, ".go")
+}
+
+
+func processDirectory(dirname string) {
+ f, err := os.Open(dirname)
+ if err != nil {
+ report(err)
+ return
+ }
+ filenames, err := f.Readdirnames(-1)
+ f.Close()
+ if err != nil {
+ report(err)
+ // continue since filenames may not be empty
+ }
+ for i, filename := range filenames {
+ filenames[i] = filepath.Join(dirname, filename)
+ }
+ processFiles(filenames, false)
+}
+
+
+func processFiles(filenames []string, allFiles bool) {
+ i := 0
+ for _, filename := range filenames {
+ switch info, err := os.Stat(filename); {
+ case err != nil:
+ report(err)
+ case info.IsRegular():
+ if allFiles || isGoFilename(info.Name) {
+ filenames[i] = filename
+ i++
+ }
+ case info.IsDirectory():
+ if allFiles || *recursive {
+ processDirectory(filename)
+ }
+ }
+ }
+ fset := token.NewFileSet()
+ processPackage(fset, parseFiles(fset, filenames[0:i]))
+}
+
+
+func processPackage(fset *token.FileSet, files map[string]*ast.File) {
+ // make a package (resolve all identifiers)
+ pkg, err := ast.NewPackage(fset, files, types.GcImporter, types.Universe)
+ if err != nil {
+ report(err)
+ return
+ }
+ // TODO(gri): typecheck package
+ _ = pkg
+}
+
+
+func main() {
+ flag.Usage = usage
+ flag.Parse()
+
+ if flag.NArg() == 0 {
+ fset := token.NewFileSet()
+ processPackage(fset, parseStdin(fset))
+ } else {
+ processFiles(flag.Args(), true)
+ }
+
+ os.Exit(exitCode)
+}
diff --git a/src/cmd/gotype/gotype_test.go b/src/cmd/gotype/gotype_test.go
new file mode 100644
index 000000000..9c8f8f2a7
--- /dev/null
+++ b/src/cmd/gotype/gotype_test.go
@@ -0,0 +1,52 @@
+// 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 (
+ "path/filepath"
+ "runtime"
+ "testing"
+)
+
+
+func runTest(t *testing.T, path, pkg string) {
+ exitCode = 0
+ *pkgName = pkg
+ *recursive = false
+
+ if pkg == "" {
+ processFiles([]string{path}, true)
+ } else {
+ processDirectory(path)
+ }
+
+ if exitCode != 0 {
+ t.Errorf("processing %s failed: exitCode = %d", path, exitCode)
+ }
+}
+
+
+var tests = []struct {
+ path string
+ pkg string
+}{
+ // individual files
+ {"testdata/test1.go", ""},
+
+ // directories
+ {filepath.Join(runtime.GOROOT(), "src/pkg/go/ast"), "ast"},
+ {filepath.Join(runtime.GOROOT(), "src/pkg/go/doc"), "doc"},
+ {filepath.Join(runtime.GOROOT(), "src/pkg/go/token"), "scanner"},
+ {filepath.Join(runtime.GOROOT(), "src/pkg/go/scanner"), "scanner"},
+ {filepath.Join(runtime.GOROOT(), "src/pkg/go/parser"), "parser"},
+ {filepath.Join(runtime.GOROOT(), "src/pkg/go/types"), "types"},
+}
+
+
+func Test(t *testing.T) {
+ for _, test := range tests {
+ runTest(t, test.path, test.pkg)
+ }
+}
diff --git a/src/cmd/gotype/testdata/test1.go b/src/cmd/gotype/testdata/test1.go
new file mode 100644
index 000000000..0bd46568d
--- /dev/null
+++ b/src/cmd/gotype/testdata/test1.go
@@ -0,0 +1,6 @@
+package p
+
+func _() {
+ // the scope of a local type declaration starts immediately after the type name
+ type T struct{ _ *T }
+}
diff --git a/src/cmd/govet/govet.go b/src/cmd/govet/govet.go
index ff6421de8..b811c61a2 100644
--- a/src/cmd/govet/govet.go
+++ b/src/cmd/govet/govet.go
@@ -7,7 +7,6 @@
package main
import (
- "bytes"
"flag"
"fmt"
"io"
@@ -18,6 +17,7 @@ import (
"path/filepath"
"strconv"
"strings"
+ "utf8"
)
var verbose = flag.Bool("v", false, "verbose")
@@ -63,6 +63,7 @@ func main() {
}
name = name[:colon]
}
+ name = strings.ToLower(name)
if name[len(name)-1] == 'f' {
printfList[name] = skip
} else {
@@ -205,35 +206,38 @@ func (f *File) checkCallExpr(call *ast.CallExpr) {
}
// printfList records the formatted-print functions. The value is the location
-// of the format parameter.
+// of the format parameter. Names are lower-cased so the lookup is
+// case insensitive.
var printfList = map[string]int{
- "Errorf": 0,
- "Fatalf": 0,
- "Fprintf": 1,
- "Panicf": 0,
- "Printf": 0,
- "Sprintf": 0,
+ "errorf": 0,
+ "fatalf": 0,
+ "fprintf": 1,
+ "panicf": 0,
+ "printf": 0,
+ "sprintf": 0,
}
// printList records the unformatted-print functions. The value is the location
-// of the first parameter to be printed.
+// of the first parameter to be printed. Names are lower-cased so the lookup is
+// case insensitive.
var printList = map[string]int{
- "Error": 0,
- "Fatal": 0,
- "Fprint": 1, "Fprintln": 1,
- "Panic": 0, "Panicln": 0,
- "Print": 0, "Println": 0,
- "Sprint": 0, "Sprintln": 0,
+ "error": 0,
+ "fatal": 0,
+ "fprint": 1, "fprintln": 1,
+ "panic": 0, "panicln": 0,
+ "print": 0, "println": 0,
+ "sprint": 0, "sprintln": 0,
}
// checkCall triggers the print-specific checks if the call invokes a print function.
-func (f *File) checkCall(call *ast.CallExpr, name string) {
+func (f *File) checkCall(call *ast.CallExpr, Name string) {
+ name := strings.ToLower(Name)
if skip, ok := printfList[name]; ok {
- f.checkPrintf(call, name, skip)
+ f.checkPrintf(call, Name, skip)
return
}
if skip, ok := printList[name]; ok {
- f.checkPrint(call, name, skip)
+ f.checkPrint(call, Name, skip)
return
}
}
@@ -256,7 +260,7 @@ func (f *File) checkPrintf(call *ast.CallExpr, name string, skip int) {
return
}
if lit.Kind == token.STRING {
- if bytes.IndexByte(lit.Value, '%') < 0 {
+ if !strings.Contains(lit.Value, "%") {
if len(call.Args) > skip+1 {
f.Badf(call.Pos(), "no formatting directive in %s call", name)
}
@@ -265,24 +269,64 @@ func (f *File) checkPrintf(call *ast.CallExpr, name string, skip int) {
}
// Hard part: check formats against args.
// Trivial but useful test: count.
- numPercent := 0
- for i := 0; i < len(lit.Value); i++ {
+ numArgs := 0
+ for i, w := 0, 0; i < len(lit.Value); i += w {
+ w = 1
if lit.Value[i] == '%' {
- if i+1 < len(lit.Value) && lit.Value[i+1] == '%' {
- // %% doesn't count.
- i++
- } else {
- numPercent++
- }
+ nbytes, nargs := parsePrintfVerb(lit.Value[i:])
+ w = nbytes
+ numArgs += nargs
}
}
expect := len(call.Args) - (skip + 1)
- if numPercent != expect {
- f.Badf(call.Pos(), "wrong number of formatting directives in %s call: %d percent(s) for %d args", name, numPercent, expect)
+ if numArgs != expect {
+ f.Badf(call.Pos(), "wrong number of args in %s call: %d needed but %d args", name, numArgs, expect)
}
}
-var terminalNewline = []byte(`\n"`) // \n at end of interpreted string
+// parsePrintfVerb returns the number of bytes and number of arguments
+// consumed by the Printf directive that begins s, including its percent sign
+// and verb.
+func parsePrintfVerb(s string) (nbytes, nargs int) {
+ // There's guaranteed a percent sign.
+ nbytes = 1
+ end := len(s)
+ // There may be flags.
+FlagLoop:
+ for nbytes < end {
+ switch s[nbytes] {
+ case '#', '0', '+', '-', ' ':
+ nbytes++
+ default:
+ break FlagLoop
+ }
+ }
+ getNum := func() {
+ if nbytes < end && s[nbytes] == '*' {
+ nbytes++
+ nargs++
+ } else {
+ for nbytes < end && '0' <= s[nbytes] && s[nbytes] <= '9' {
+ nbytes++
+ }
+ }
+ }
+ // There may be a width.
+ getNum()
+ // If there's a period, there may be a precision.
+ if nbytes < end && s[nbytes] == '.' {
+ nbytes++
+ getNum()
+ }
+ // Now a verb.
+ c, w := utf8.DecodeRuneInString(s[nbytes:])
+ nbytes += w
+ if c != '%' {
+ nargs++
+ }
+ return
+}
+
// checkPrint checks a call to an unformatted print routine such as Println.
// The skip argument records how many arguments to ignore; that is,
@@ -298,7 +342,7 @@ func (f *File) checkPrint(call *ast.CallExpr, name string, skip int) {
}
arg := args[skip]
if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING {
- if bytes.IndexByte(lit.Value, '%') >= 0 {
+ if strings.Contains(lit.Value, "%") {
f.Badf(call.Pos(), "possible formatting directive in %s call", name)
}
}
@@ -306,7 +350,7 @@ func (f *File) checkPrint(call *ast.CallExpr, name string, skip int) {
// The last item, if a string, should not have a newline.
arg = args[len(call.Args)-1]
if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING {
- if bytes.HasSuffix(lit.Value, terminalNewline) {
+ if strings.HasSuffix(lit.Value, `\n"`) {
f.Badf(call.Pos(), "%s call ends with newline", name)
}
}
@@ -320,8 +364,16 @@ func BadFunctionUsedInTests() {
fmt.Println("%s", "hi") // % in call to Println
fmt.Printf("%s", "hi", 3) // wrong # percents
fmt.Printf("%s%%%d", "hi", 3) // right # percents
+ fmt.Printf("%.*d", 3, 3) // right # percents, with a *
+ fmt.Printf("%.*d", 3, 3, 3) // wrong # percents, with a *
+ printf("now is the time", "buddy") // no %s
Printf("now is the time", "buddy") // no %s
f := new(File)
f.Warn(0, "%s", "hello", 3) // % in call to added function
f.Warnf(0, "%s", "hello", 3) // wrong # %s in call to added function
}
+
+// printf is used by the test.
+func printf(format string, args ...interface{}) {
+ panic("don't call - testing only")
+}
diff --git a/src/cmd/goyacc/goyacc.go b/src/cmd/goyacc/goyacc.go
index 32816b700..220c99492 100644
--- a/src/cmd/goyacc/goyacc.go
+++ b/src/cmd/goyacc/goyacc.go
@@ -382,7 +382,7 @@ outer:
for {
switch t {
default:
- error("syntax error tok=%v", t-PRIVATE)
+ errorf("syntax error tok=%v", t-PRIVATE)
case MARK, ENDFILE:
break outer
@@ -392,14 +392,14 @@ outer:
case START:
t = gettok()
if t != IDENTIFIER {
- error("bad %%start construction")
+ errorf("bad %%start construction")
}
start = chfind(1, tokname)
case TYPEDEF:
t = gettok()
if t != TYPENAME {
- error("bad syntax in %%type")
+ errorf("bad syntax in %%type")
}
ty = numbval
for {
@@ -410,7 +410,7 @@ outer:
if t < NTBASE {
j = TYPE(toklev[t])
if j != 0 && j != ty {
- error("type redeclaration of token ",
+ errorf("type redeclaration of token ",
tokset[t].name)
} else {
toklev[t] = SETTYPE(toklev[t], ty)
@@ -418,7 +418,7 @@ outer:
} else {
j = nontrst[t-NTBASE].value
if j != 0 && j != ty {
- error("type redeclaration of nonterminal %v",
+ errorf("type redeclaration of nonterminal %v",
nontrst[t-NTBASE].name)
} else {
nontrst[t-NTBASE].value = ty
@@ -464,18 +464,18 @@ outer:
case IDENTIFIER:
j = chfind(0, tokname)
if j >= NTBASE {
- error("%v defined earlier as nonterminal", tokname)
+ errorf("%v defined earlier as nonterminal", tokname)
}
if lev != 0 {
if ASSOC(toklev[j]) != 0 {
- error("redeclaration of precedence of %v", tokname)
+ errorf("redeclaration of precedence of %v", tokname)
}
toklev[j] = SETASC(toklev[j], lev)
toklev[j] = SETPLEV(toklev[j], i)
}
if ty != 0 {
if TYPE(toklev[j]) != 0 {
- error("redeclaration of type of %v", tokname)
+ errorf("redeclaration of type of %v", tokname)
}
toklev[j] = SETTYPE(toklev[j], ty)
}
@@ -498,7 +498,7 @@ outer:
}
if t == ENDFILE {
- error("unexpected EOF before %%")
+ errorf("unexpected EOF before %%")
}
// put out non-literal terminals
@@ -533,7 +533,7 @@ outer:
curprod := make([]int, RULEINC)
t = gettok()
if t != IDENTCOLON {
- error("bad syntax on first rule")
+ errorf("bad syntax on first rule")
}
if start == 0 {
@@ -557,11 +557,11 @@ outer:
} else if t == IDENTCOLON {
curprod[mem] = chfind(1, tokname)
if curprod[mem] < NTBASE {
- error("token illegal on LHS of grammar rule")
+ errorf("token illegal on LHS of grammar rule")
}
mem++
} else {
- error("illegal rule: missing semicolon or | ?")
+ errorf("illegal rule: missing semicolon or | ?")
}
// read rule body
@@ -582,11 +582,11 @@ outer:
}
if t == PREC {
if gettok() != IDENTIFIER {
- error("illegal %%prec syntax")
+ errorf("illegal %%prec syntax")
}
j = chfind(2, tokname)
if j >= NTBASE {
- error("nonterminal " + nontrst[j-NTBASE].name + " illegal after %%prec")
+ errorf("nonterminal " + nontrst[j-NTBASE].name + " illegal after %%prec")
}
levprd[nprod] = toklev[j]
t = gettok()
@@ -642,7 +642,7 @@ outer:
// no explicit action, LHS has value
tempty := curprod[1]
if tempty < 0 {
- error("must return a value, since LHS has a type")
+ errorf("must return a value, since LHS has a type")
}
if tempty >= NTBASE {
tempty = nontrst[tempty-NTBASE].value
@@ -650,7 +650,7 @@ outer:
tempty = TYPE(toklev[tempty])
}
if tempty != nontrst[curprod[0]-NTBASE].value {
- error("default action causes potential type clash")
+ errorf("default action causes potential type clash")
}
fmt.Fprintf(fcode, "\ncase %v:", nprod)
fmt.Fprintf(fcode, "\n\t%sVAL.%v = %sS[%spt-0].%v;",
@@ -773,7 +773,7 @@ func defin(nt int, s string) int {
case 'v':
val = '\v'
default:
- error("invalid escape %v", s[1:3])
+ errorf("invalid escape %v", s[1:3])
}
} else if s[2] == 'u' && len(s) == 2+1+4 { // \unnnn sequence
val = 0
@@ -788,16 +788,16 @@ func defin(nt int, s string) int {
case c >= 'A' && c <= 'F':
c -= 'A' - 10
default:
- error("illegal \\unnnn construction")
+ errorf("illegal \\unnnn construction")
}
val = val*16 + c
s = s[1:]
}
if val == 0 {
- error("'\\u0000' is illegal")
+ errorf("'\\u0000' is illegal")
}
} else {
- error("unknown escape")
+ errorf("unknown escape")
}
} else {
val = extval
@@ -855,7 +855,7 @@ func gettok() int {
}
if c != '>' {
- error("unterminated < ... > clause")
+ errorf("unterminated < ... > clause")
}
for i = 1; i <= ntypes; i++ {
@@ -881,7 +881,7 @@ func gettok() int {
for {
c = getrune(finput)
if c == '\n' || c == EOF {
- error("illegal or missing ' or \"")
+ errorf("illegal or missing ' or \"")
}
if c == '\\' {
tokname += string('\\')
@@ -926,7 +926,7 @@ func gettok() int {
return resrv[c].value
}
}
- error("invalid escape, or illegal reserved word: %v", tokname)
+ errorf("invalid escape, or illegal reserved word: %v", tokname)
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
numbval = c - '0'
@@ -1004,7 +1004,7 @@ func fdtype(t int) int {
s = tokset[t].name
}
if v <= 0 {
- error("must specify type for %v", s)
+ errorf("must specify type for %v", s)
}
return v
}
@@ -1026,7 +1026,7 @@ func chfind(t int, s string) int {
// cannot find name
if t > 1 {
- error("%v should have been defined earlier", s)
+ errorf("%v should have been defined earlier", s)
}
return defin(t, s)
}
@@ -1047,7 +1047,7 @@ out:
for {
c := getrune(finput)
if c == EOF {
- error("EOF encountered while processing %%union")
+ errorf("EOF encountered while processing %%union")
}
ftable.WriteRune(c)
switch c {
@@ -1097,7 +1097,7 @@ func cpycode() {
c = getrune(finput)
}
lineno = lno
- error("eof before %%}")
+ errorf("eof before %%}")
}
//
@@ -1115,11 +1115,11 @@ func skipcom() int {
}
c = getrune(finput)
}
- error("EOF inside comment")
+ errorf("EOF inside comment")
return 0
}
if c != '*' {
- error("illegal comment")
+ errorf("illegal comment")
}
nl := 0 // lines skipped
@@ -1196,7 +1196,7 @@ loop:
if c == '<' {
ungetrune(finput, c)
if gettok() != TYPENAME {
- error("bad syntax on $<ident> clause")
+ errorf("bad syntax on $<ident> clause")
}
tok = numbval
c = getrune(finput)
@@ -1226,13 +1226,13 @@ loop:
ungetrune(finput, c)
j = j * s
if j >= max {
- error("Illegal use of $%v", j)
+ errorf("Illegal use of $%v", j)
}
} else if isword(c) || c == '_' || c == '.' {
// look for $name
ungetrune(finput, c)
if gettok() != IDENTIFIER {
- error("$ must be followed by an identifier")
+ errorf("$ must be followed by an identifier")
}
tokn := chfind(2, tokname)
fnd := -1
@@ -1240,7 +1240,7 @@ loop:
if c != '@' {
ungetrune(finput, c)
} else if gettok() != NUMBER {
- error("@ must be followed by number")
+ errorf("@ must be followed by number")
} else {
fnd = numbval
}
@@ -1253,7 +1253,7 @@ loop:
}
}
if j >= max {
- error("$name or $name@number not found")
+ errorf("$name or $name@number not found")
}
} else {
fcode.WriteRune('$')
@@ -1268,7 +1268,7 @@ loop:
// put out the proper tag
if ntypes != 0 {
if j <= 0 && tok < 0 {
- error("must specify type of $%v", j)
+ errorf("must specify type of $%v", j)
}
if tok < 0 {
tok = fdtype(curprod[j])
@@ -1315,7 +1315,7 @@ loop:
fcode.WriteRune(c)
c = getrune(finput)
}
- error("EOF inside comment")
+ errorf("EOF inside comment")
case '\'', '"':
// character string or constant
@@ -1333,16 +1333,16 @@ loop:
break swt
}
if c == '\n' {
- error("newline in string or char const")
+ errorf("newline in string or char const")
}
fcode.WriteRune(c)
c = getrune(finput)
}
- error("EOF in string or character constant")
+ errorf("EOF in string or character constant")
case EOF:
lineno = lno
- error("action does not terminate")
+ errorf("action does not terminate")
case '\n':
lineno++
@@ -1356,14 +1356,14 @@ func openup() {
infile = flag.Arg(0)
finput = open(infile)
if finput == nil {
- error("cannot open %v", infile)
+ errorf("cannot open %v", infile)
}
foutput = nil
if vflag != "" {
- foutput = create(vflag, 0666)
+ foutput = create(vflag)
if foutput == nil {
- error("can't create file %v", vflag)
+ errorf("can't create file %v", vflag)
}
}
@@ -1371,9 +1371,9 @@ func openup() {
if oflag == "" {
oflag = "y.go"
}
- ftable = create(oflag, 0666)
+ ftable = create(oflag)
if ftable == nil {
- error("can't create file %v", oflag)
+ errorf("can't create file %v", oflag)
}
}
@@ -1433,7 +1433,7 @@ func cpres() {
}
}
if n == 0 {
- error("nonterminal %v not defined", nontrst[i].name)
+ errorf("nonterminal %v not defined", nontrst[i].name)
continue
}
pres[i] = make([][]int, n)
@@ -1506,7 +1506,7 @@ more:
}
if pempty[i] != OK {
fatfl = 0
- error("nonterminal " + nontrst[i].name + " never derives any token string")
+ errorf("nonterminal " + nontrst[i].name + " never derives any token string")
}
}
@@ -1921,11 +1921,11 @@ look:
// state is new
zznewstate++
if nolook != 0 {
- error("yacc state/nolook error")
+ errorf("yacc state/nolook error")
}
pstate[nstate+2] = p2
if nstate+1 >= NSTATES {
- error("too many states")
+ errorf("too many states")
}
if c >= NTBASE {
mstates[nstate] = ntstates[c-NTBASE]
@@ -2061,7 +2061,7 @@ nextk:
}
return off + rr
}
- error("no space in action table")
+ errorf("no space in action table")
return 0
}
@@ -2623,7 +2623,7 @@ nextgp:
if s > maxa {
maxa = s
if maxa >= ACTSIZE {
- error("a array overflow")
+ errorf("a array overflow")
}
}
if amem[s] != 0 {
@@ -2646,7 +2646,7 @@ nextgp:
}
return
}
- error("cannot place goto %v\n", i)
+ errorf("cannot place goto %v\n", i)
}
func stin(i int) {
@@ -2705,7 +2705,7 @@ nextn:
maxa = s
}
if amem[s] != 0 && amem[s] != q[r+1] {
- error("clobber of a array, pos'n %v, by %v", s, q[r+1])
+ errorf("clobber of a array, pos'n %v, by %v", s, q[r+1])
}
amem[s] = q[r+1]
}
@@ -2715,7 +2715,7 @@ nextn:
}
return
}
- error("Error; failure to place state %v", i)
+ errorf("Error; failure to place state %v", i)
}
//
@@ -3014,7 +3014,7 @@ func getrune(f *bufio.Reader) int {
return EOF
}
if err != nil {
- error("read error: %v", err)
+ errorf("read error: %v", err)
}
//fmt.Printf("rune = %v n=%v\n", string(c), n);
return c
@@ -3036,27 +3036,27 @@ func write(f *bufio.Writer, b []byte, n int) int {
}
func open(s string) *bufio.Reader {
- fi, err := os.Open(s, os.O_RDONLY, 0)
+ fi, err := os.Open(s)
if err != nil {
- error("error opening %v: %v", s, err)
+ errorf("error opening %v: %v", s, err)
}
//fmt.Printf("open %v\n", s);
return bufio.NewReader(fi)
}
-func create(s string, m uint32) *bufio.Writer {
- fo, err := os.Open(s, os.O_WRONLY|os.O_CREAT|os.O_TRUNC, m)
+func create(s string) *bufio.Writer {
+ fo, err := os.Create(s)
if err != nil {
- error("error opening %v: %v", s, err)
+ errorf("error creating %v: %v", s, err)
}
- //fmt.Printf("create %v mode %v\n", s, m);
+ //fmt.Printf("create %v mode %v\n", s);
return bufio.NewWriter(fo)
}
//
// write out error comment
//
-func error(s string, v ...interface{}) {
+func errorf(s string, v ...interface{}) {
nerrors++
fmt.Fprintf(stderr, s, v...)
fmt.Fprintf(stderr, ": %v:%v\n", infile, lineno)
diff --git a/src/cmd/goyacc/units.y b/src/cmd/goyacc/units.y
index 5d3f9aca2..06ce11693 100644
--- a/src/cmd/goyacc/units.y
+++ b/src/cmd/goyacc/units.y
@@ -90,7 +90,7 @@ prog:
$2.node = $3;
$2.node.dim[0] = 1;
if f != 0 {
- Error("redefinition of %v", $2.name);
+ Errorf("redefinition of %v", $2.name);
} else
if vflag {
fmt.Printf("%v\t%v\n", $2.name, &$2.node);
@@ -106,7 +106,7 @@ prog:
}
}
if i >= Ndim {
- Error("too many dimensions");
+ Errorf("too many dimensions");
i = Ndim-1;
}
fund[i] = $2;
@@ -116,7 +116,7 @@ prog:
$2.node.dim[0] = 1;
$2.node.dim[i] = 1;
if f != 0 {
- Error("redefinition of %v", $2.name);
+ Errorf("redefinition of %v", $2.name);
} else
if vflag {
fmt.Printf("%v\t#\n", $2.name);
@@ -175,7 +175,7 @@ expr2:
for i=1; i<Ndim; i++ {
if $3.dim[i] != 0 {
- Error("exponent has units");
+ Errorf("exponent has units");
$$ = $1;
break;
}
@@ -183,7 +183,7 @@ expr2:
if i >= Ndim {
i = int($3.vval);
if float64(i) != $3.vval {
- Error("exponent not integral");
+ Errorf("exponent not integral");
}
xpn(&$$, &$1, i);
}
@@ -200,7 +200,7 @@ expr0:
VAR
{
if $1.node.dim[0] == 0 {
- Error("undefined %v", $1.name);
+ Errorf("undefined %v", $1.name);
$$ = one;
} else
$$ = $1.node;
@@ -284,7 +284,7 @@ numb:
}
func (UnitsLex) Error(s string) {
- Error("syntax error, last name: %v", sym)
+ Errorf("syntax error, last name: %v", sym)
}
func main() {
@@ -299,7 +299,7 @@ func main() {
file = flag.Arg(0)
}
- f, err := os.Open(file, os.O_RDONLY, 0)
+ f, err := os.Open(file)
if err != nil {
fmt.Fprintf(os.Stderr, "error opening %v: %v\n", file, err)
os.Exit(1)
@@ -391,7 +391,7 @@ func rdigit(c int) bool {
return false
}
-func Error(s string, v ...interface{}) {
+func Errorf(s string, v ...interface{}) {
fmt.Printf("%v: %v\n\t", lineno, line)
fmt.Printf(s, v...)
fmt.Printf("\n")
@@ -411,7 +411,7 @@ func add(c, a, b *Node) {
d = a.dim[i]
c.dim[i] = d
if d != b.dim[i] {
- Error("add must be like units")
+ Errorf("add must be like units")
}
}
c.vval = fadd(a.vval, b.vval)
@@ -425,7 +425,7 @@ func sub(c, a, b *Node) {
d = a.dim[i]
c.dim[i] = d
if d != b.dim[i] {
- Error("sub must be like units")
+ Errorf("sub must be like units")
}
}
c.vval = fadd(a.vval, -b.vval)
@@ -711,11 +711,11 @@ func fmul(a, b float64) float64 {
}
if l > Maxe {
- Error("overflow in multiply")
+ Errorf("overflow in multiply")
return 1
}
if l < -Maxe {
- Error("underflow in multiply")
+ Errorf("underflow in multiply")
return 0
}
return a * b
@@ -728,7 +728,7 @@ func fdiv(a, b float64) float64 {
if b <= 0 {
if b == 0 {
- Error("division by zero: %v %v", a, b)
+ Errorf("division by zero: %v %v", a, b)
return 1
}
l = math.Log(-b)
@@ -746,11 +746,11 @@ func fdiv(a, b float64) float64 {
}
if l < -Maxe {
- Error("overflow in divide")
+ Errorf("overflow in divide")
return 1
}
if l > Maxe {
- Error("underflow in divide")
+ Errorf("underflow in divide")
return 0
}
return a / b
diff --git a/src/cmd/ld/data.c b/src/cmd/ld/data.c
index a20b057ce..d27416dac 100644
--- a/src/cmd/ld/data.c
+++ b/src/cmd/ld/data.c
@@ -722,7 +722,7 @@ addsize(Sym *s, Sym *t)
void
dodata(void)
{
- int32 h, t, datsize;
+ int32 t, datsize;
Section *sect;
Sym *s, *last, **l;
@@ -732,23 +732,22 @@ dodata(void)
last = nil;
datap = nil;
- for(h=0; h<NHASH; h++) {
- for(s=hash[h]; s!=S; s=s->hash){
- if(!s->reachable || s->special)
- continue;
- if(STEXT < s->type && s->type < SXREF) {
- if(last == nil)
- datap = s;
- else
- last->next = s;
- s->next = nil;
- last = s;
- }
+
+ for(s=allsym; s!=S; s=s->allsym) {
+ if(!s->reachable || s->special)
+ continue;
+ if(STEXT < s->type && s->type < SXREF) {
+ if(last == nil)
+ datap = s;
+ else
+ last->next = s;
+ s->next = nil;
+ last = s;
}
}
for(s = datap; s != nil; s = s->next) {
- if(s->np > 0 && s->type == SBSS) // TODO: necessary?
+ if(s->np > 0 && s->type == SBSS)
s->type = SDATA;
if(s->np > s->size)
diag("%s: initialize bounds (%lld < %d)",
@@ -786,8 +785,7 @@ dodata(void)
s = datap;
for(; s != nil && s->type < SDATA; s = s->next) {
s->type = SRODATA;
- t = rnd(s->size, 4);
- s->size = t;
+ t = rnd(s->size, PtrSize);
s->value = datsize;
datsize += t;
}
@@ -834,7 +832,6 @@ dodata(void)
datsize = rnd(datsize, 4);
else
datsize = rnd(datsize, 8);
- s->size = t;
s->value = datsize;
datsize += t;
}
diff --git a/src/cmd/ld/dwarf.c b/src/cmd/ld/dwarf.c
index 5ba4b7c64..fa55fcbb4 100644
--- a/src/cmd/ld/dwarf.c
+++ b/src/cmd/ld/dwarf.c
@@ -6,7 +6,6 @@
// - eliminate DW_CLS_ if not used
// - package info in compilation units
// - assign global variables and types to their packages
-// - (upstream) type info for C parts of runtime
// - gdb uses c syntax, meaning clumsy quoting is needed for go identifiers. eg
// ptype struct '[]uint8' and qualifiers need to be quoted away
// - lexical scoping is lost, so gdb gets confused as to which 'main.i' you mean.
@@ -943,14 +942,16 @@ enum {
static char*
decodetype_structfieldname(Sym *s, int i)
{
+ Reloc *r;
+
// go.string."foo" 0x28 / 0x40
s = decode_reloc_sym(s, CommonSize + PtrSize + 2*4 + i*StructFieldSize);
if (s == nil) // embedded structs have a nil name.
return nil;
- s = decode_reloc_sym(s, 0); // string."foo"
- if (s == nil) // shouldn't happen.
+ r = decode_reloc(s, 0); // s has a pointer to the string data at offset 0
+ if (r == nil) // shouldn't happen.
return nil;
- return (char*)s->p; // the c-string
+ return (char*) r->sym->p + r->add; // the c-string
}
static Sym*
@@ -989,8 +990,8 @@ lookup_or_diag(char *n)
{
Sym *s;
- s = lookup(n, 0);
- if (s->size == 0) {
+ s = rlookup(n, 0);
+ if (s == nil || s->size == 0) {
diag("dwarf: missing type: %s", n);
errorexit();
}
@@ -1021,22 +1022,8 @@ defgotype(Sym *gotype)
if (die != nil)
return die;
- if (0 && debug['v'] > 2) {
- print("new type: %s @0x%08x [%d]", gotype->name, gotype->value, gotype->size);
- for (i = 0; i < gotype->size; i++) {
- if (!(i%8)) print("\n\t%04x ", i);
- print("%02x ", gotype->p[i]);
- }
- print("\n");
- for (i = 0; i < gotype->nr; i++) {
- print("\t0x%02x[%x] %d %s[%llx]\n",
- gotype->r[i].off,
- gotype->r[i].siz,
- gotype->r[i].type,
- gotype->r[i].sym->name,
- (vlong)gotype->r[i].add);
- }
- }
+ if (0 && debug['v'] > 2)
+ print("new type: %Y\n", gotype);
kind = decodetype_kind(gotype);
bytesize = decodetype_size(gotype);
@@ -1117,7 +1104,6 @@ defgotype(Sym *gotype)
fld = newdie(die, DW_ABRV_FUNCTYPEPARAM, s->name+5);
newrefattr(fld, DW_AT_type, defptrto(defgotype(s)));
}
- die = defptrto(die);
break;
case KindInterface:
@@ -1391,7 +1377,7 @@ static void
synthesizechantypes(DWDie *die)
{
DWDie *sudog, *waitq, *link, *hchan,
- *dws, *dww, *dwl, *dwh, *elemtype;
+ *dws, *dww, *dwh, *elemtype;
DWAttr *a;
int elemsize, linksize, sudogsize;
@@ -1430,21 +1416,10 @@ synthesizechantypes(DWDie *die)
newattr(dww, DW_AT_byte_size, DW_CLS_CONSTANT,
getattr(waitq, DW_AT_byte_size)->value, NULL);
- // link<T>
- dwl = newdie(&dwtypes, DW_ABRV_STRUCTTYPE,
- mkinternaltypename("link", getattr(elemtype, DW_AT_name)->data, NULL));
- copychildren(dwl, link);
- substitutetype(dwl, "link", defptrto(dwl));
- substitutetype(dwl, "elem", elemtype);
- newattr(dwl, DW_AT_byte_size, DW_CLS_CONSTANT,
- linksize + (elemsize > 8 ? elemsize - 8 : 0), NULL);
-
// hchan<T>
dwh = newdie(&dwtypes, DW_ABRV_STRUCTTYPE,
mkinternaltypename("hchan", getattr(elemtype, DW_AT_name)->data, NULL));
copychildren(dwh, hchan);
- substitutetype(dwh, "senddataq", defptrto(dwl));
- substitutetype(dwh, "recvdataq", defptrto(dwl));
substitutetype(dwh, "recvq", dww);
substitutetype(dwh, "sendq", dww);
substitutetype(dwh, "free", dws);
@@ -1463,12 +1438,8 @@ defdwsymb(Sym* sym, char *s, int t, vlong v, vlong size, int ver, Sym *gotype)
if (strncmp(s, "go.string.", 10) == 0)
return;
- if (strncmp(s, "string.", 7) == 0)
- return;
- if (strncmp(s, "type._.", 7) == 0)
- return;
- if (strncmp(s, "type.", 5) == 0) {
+ if (strncmp(s, "type.", 5) == 0 && strcmp(s, "type.*") != 0) {
defgotype(sym);
return;
}
diff --git a/src/cmd/ld/elf.c b/src/cmd/ld/elf.c
index d5b0b0311..b0cce4985 100644
--- a/src/cmd/ld/elf.c
+++ b/src/cmd/ld/elf.c
@@ -336,7 +336,7 @@ void
elfdynhash(void)
{
Sym *s, *sy;
- int i, h, nbucket, b;
+ int i, nbucket, b;
uchar *pc;
uint32 hc, g;
uint32 *chain, *buckets;
@@ -367,26 +367,24 @@ elfdynhash(void)
}
memset(chain, 0, nsym * sizeof(uint32));
memset(buckets, 0, nbucket * sizeof(uint32));
- for(h = 0; h<NHASH; h++) {
- for(sy=hash[h]; sy!=S; sy=sy->hash) {
- if (sy->dynid <= 0)
- continue;
-
- hc = 0;
- name = sy->dynimpname;
- if(name == nil)
- name = sy->name;
- for(pc = (uchar*)name; *pc; pc++) {
- hc = (hc<<4) + *pc;
- g = hc & 0xf0000000;
- hc ^= g >> 24;
- hc &= ~g;
- }
-
- b = hc % nbucket;
- chain[sy->dynid] = buckets[b];
- buckets[b] = sy->dynid;
+ for(sy=allsym; sy!=S; sy=sy->allsym) {
+ if (sy->dynid <= 0)
+ continue;
+
+ hc = 0;
+ name = sy->dynimpname;
+ if(name == nil)
+ name = sy->name;
+ for(pc = (uchar*)name; *pc; pc++) {
+ hc = (hc<<4) + *pc;
+ g = hc & 0xf0000000;
+ hc ^= g >> 24;
+ hc &= ~g;
}
+
+ b = hc % nbucket;
+ chain[sy->dynid] = buckets[b];
+ buckets[b] = sy->dynid;
}
adduint32(s, nbucket);
diff --git a/src/cmd/ld/elf.h b/src/cmd/ld/elf.h
index df15cb115..b27ae679b 100644
--- a/src/cmd/ld/elf.h
+++ b/src/cmd/ld/elf.h
@@ -946,10 +946,10 @@ typedef Elf64_Shdr ElfShdr;
typedef Elf64_Phdr ElfPhdr;
void elfinit(void);
-ElfEhdr *getElfEhdr();
+ElfEhdr *getElfEhdr(void);
ElfShdr *newElfShstrtab(vlong);
ElfShdr *newElfShdr(vlong);
-ElfPhdr *newElfPhdr();
+ElfPhdr *newElfPhdr(void);
uint32 elfwritehdr(void);
uint32 elfwritephdrs(void);
uint32 elfwriteshdrs(void);
diff --git a/src/cmd/ld/go.c b/src/cmd/ld/go.c
index 3c1e230b4..055163d08 100644
--- a/src/cmd/ld/go.c
+++ b/src/cmd/ld/go.c
@@ -135,6 +135,7 @@ ldpkg(Biobuf *f, char *pkg, int64 len, char *filename, int whence)
if(debug['u'] && whence != ArchiveObj &&
(p0+6 > p1 || memcmp(p0, " safe\n", 6) != 0)) {
fprint(2, "%s: load of unsafe package %s\n", argv0, filename);
+ nerrors++;
errorexit();
}
if(p0 < p1) {
@@ -657,27 +658,25 @@ deadcode(void)
else
last->next = nil;
- for(i=0; i<NHASH; i++)
- for(s = hash[i]; s != S; s = s->hash)
+ for(s = allsym; s != S; s = s->allsym)
if(strncmp(s->name, "weak.", 5) == 0) {
s->special = 1; // do not lay out in data segment
s->reachable = 1;
+ s->hide = 1;
}
}
void
doweak(void)
{
- int i;
Sym *s, *t;
// resolve weak references only if
// target symbol will be in binary anyway.
- for(i=0; i<NHASH; i++)
- for(s = hash[i]; s != S; s = s->hash) {
+ for(s = allsym; s != S; s = s->allsym) {
if(strncmp(s->name, "weak.", 5) == 0) {
- t = lookup(s->name+5, s->version);
- if(t->type != 0 && t->reachable) {
+ t = rlookup(s->name+5, s->version);
+ if(t && t->type != 0 && t->reachable) {
s->value = t->value;
s->type = t->type;
} else {
diff --git a/src/cmd/ld/ldmacho.c b/src/cmd/ld/ldmacho.c
index 7e38db0e4..bbb21d51a 100644
--- a/src/cmd/ld/ldmacho.c
+++ b/src/cmd/ld/ldmacho.c
@@ -422,6 +422,7 @@ void
ldmacho(Biobuf *f, char *pkg, int64 len, char *pn)
{
int i, j, is64;
+ uint64 secaddr;
uchar hdr[7*4], *cmdp;
uchar tmp[4];
uchar *dat;
@@ -581,7 +582,11 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn)
else
s->type = SRODATA;
} else {
- s->type = SDATA;
+ if (strcmp(sect->name, "__bss") == 0) {
+ s->type = SBSS;
+ s->np = 0;
+ } else
+ s->type = SDATA;
}
if(s->type == STEXT) {
if(etextp)
@@ -751,8 +756,29 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn)
rp->siz = rel->length;
rp->type = 512 + (rel->type<<1) + rel->pcrel;
rp->off = rel->addr;
-
- rp->add = e->e32(s->p+rp->off);
+
+ // Handle X86_64_RELOC_SIGNED referencing a section (rel->extrn == 0).
+ if (thechar == '6' && rel->extrn == 0 && rel->type == 1) {
+ // Calculate the addend as the offset into the section.
+ //
+ // The rip-relative offset stored in the object file is encoded
+ // as follows:
+ //
+ // movsd 0x00000360(%rip),%xmm0
+ //
+ // To get the absolute address of the value this rip-relative address is pointing
+ // to, we must add the address of the next instruction to it. This is done by
+ // taking the address of the relocation and adding 4 to it (since the rip-relative
+ // offset can at most be 32 bits long). To calculate the offset into the section the
+ // relocation is referencing, we subtract the vaddr of the start of the referenced
+ // section found in the original object file.
+ //
+ // [For future reference, see Darwin's /usr/include/mach-o/x86_64/reloc.h]
+ secaddr = c->seg.sect[rel->symnum-1].addr;
+ rp->add = e->e32(s->p+rp->off) + rp->off + 4 - secaddr;
+ } else
+ rp->add = e->e32(s->p+rp->off);
+
// For i386 Mach-O PC-relative, the addend is written such that
// it *is* the PC being subtracted. Use that to make
// it match our version of PC-relative.
diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c
index e645502b3..8cd570463 100644
--- a/src/cmd/ld/lib.c
+++ b/src/cmd/ld/lib.c
@@ -61,6 +61,7 @@ void
libinit(void)
{
fmtinstall('i', iconv);
+ fmtinstall('Y', Yconv);
mywhatsys(); // get goroot, goarch, goos
if(strcmp(goarch, thestring) != 0)
print("goarch is not known: %s\n", goarch);
@@ -470,8 +471,8 @@ eof:
diag("truncated object file: %s", pn);
}
-Sym*
-lookup(char *symb, int v)
+static Sym*
+_lookup(char *symb, int v, int creat)
{
Sym *s;
char *p;
@@ -485,10 +486,12 @@ lookup(char *symb, int v)
// not if(h < 0) h = ~h, because gcc 4.3 -O2 miscompiles it.
h &= 0xffffff;
h %= NHASH;
+ c = symb[0];
for(s = hash[h]; s != S; s = s->hash)
- if(s->version == v)
if(memcmp(s->name, symb, l) == 0)
return s;
+ if(!creat)
+ return nil;
s = mal(sizeof(*s));
if(debug['v'] > 1)
@@ -508,9 +511,25 @@ lookup(char *symb, int v)
s->size = 0;
hash[h] = s;
nsymbol++;
+
+ s->allsym = allsym;
+ allsym = s;
return s;
}
+Sym*
+lookup(char *name, int v)
+{
+ return _lookup(name, v, 1);
+}
+
+// read-only lookup
+Sym*
+rlookup(char *name, int v)
+{
+ return _lookup(name, v, 0);
+}
+
void
copyhistfrog(char *buf, int nbuf)
{
@@ -829,7 +848,7 @@ unmal(void *v, uint32 n)
// Copied from ../gc/subr.c:/^pathtoprefix; must stay in sync.
/*
* Convert raw string to the prefix that will be used in the symbol table.
- * Invalid bytes turn into %xx. Right now the only bytes that need
+ * Invalid bytes turn into %xx. Right now the only bytes that need
* escaping are %, ., and ", but we escape all control characters too.
*/
static char*
@@ -1104,7 +1123,7 @@ static Sym *newstack;
enum
{
HasLinkRegister = (thechar == '5'),
- CallSize = (!HasLinkRegister)*PtrSize, // bytes of stack required for a call
+ CallSize = (!HasLinkRegister)*PtrSize, // bytes of stack required for a call
};
void
@@ -1130,7 +1149,7 @@ dostkcheck(void)
// Check calling contexts.
// Some nosplits get called a little further down,
- // like newproc and deferproc. We could hard-code
+ // like newproc and deferproc. We could hard-code
// that knowledge but it's more robust to look at
// the actual call sites.
for(s = textp; s != nil; s = s->next) {
@@ -1283,11 +1302,44 @@ headtype(char *name)
void
undef(void)
{
- int i;
Sym *s;
- for(i=0; i<NHASH; i++)
- for(s = hash[i]; s != S; s = s->hash)
+ for(s = allsym; s != S; s = s->allsym)
if(s->type == SXREF)
diag("%s(%d): not defined", s->name, s->version);
}
+
+int
+Yconv(Fmt *fp)
+{
+ Sym *s;
+ Fmt fmt;
+ int i;
+ char *str;
+
+ s = va_arg(fp->args, Sym*);
+ if (s == S) {
+ fmtprint(fp, "<nil>");
+ } else {
+ fmtstrinit(&fmt);
+ fmtprint(&fmt, "%s @0x%08x [%d]", s->name, s->value, s->size);
+ for (i = 0; i < s->size; i++) {
+ if (!(i%8)) fmtprint(&fmt, "\n\t0x%04x ", i);
+ fmtprint(&fmt, "%02x ", s->p[i]);
+ }
+ fmtprint(&fmt, "\n");
+ for (i = 0; i < s->nr; i++) {
+ fmtprint(&fmt, "\t0x%04x[%x] %d %s[%llx]\n",
+ s->r[i].off,
+ s->r[i].siz,
+ s->r[i].type,
+ s->r[i].sym->name,
+ (vlong)s->r[i].add);
+ }
+ str = fmtstrflush(&fmt);
+ fmtstrcpy(fp, str);
+ free(str);
+ }
+
+ return 0;
+}
diff --git a/src/cmd/ld/lib.h b/src/cmd/ld/lib.h
index adde2c9ff..646aeb535 100644
--- a/src/cmd/ld/lib.h
+++ b/src/cmd/ld/lib.h
@@ -28,8 +28,37 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-// Where symbol table data gets mapped into memory.
-#define SYMDATVA 0x99LL<<24
+enum
+{
+ Sxxx,
+
+ /* order here is order in output file */
+ STEXT,
+ SELFDATA,
+ SMACHOPLT,
+ STYPE,
+ SSTRING,
+ SGOSTRING,
+ 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 = 100003,
+};
typedef struct Library Library;
struct Library
@@ -79,6 +108,7 @@ EXTERN Library* library;
EXTERN int libraryp;
EXTERN int nlibrary;
EXTERN Sym* hash[NHASH];
+EXTERN Sym* allsym;
EXTERN Sym* histfrog[MAXHIST];
EXTERN uchar fnuxi8[8];
EXTERN uchar fnuxi4[4];
@@ -106,6 +136,7 @@ void asmlc(void);
void histtoauto(void);
void collapsefrog(Sym *s);
Sym* lookup(char *symb, int v);
+Sym* rlookup(char *symb, int v);
void nuxiinit(void);
int find1(int32 l, int c);
int find2(int32 l, int c);
@@ -243,3 +274,6 @@ EXTERN char* headstring;
extern Header headers[];
int headtype(char*);
+
+int Yconv(Fmt*);
+#pragma varargck type "Y" Sym*
diff --git a/src/cmd/ld/pe.c b/src/cmd/ld/pe.c
index e72b0b2a0..0d4240e36 100644
--- a/src/cmd/ld/pe.c
+++ b/src/cmd/ld/pe.c
@@ -171,12 +171,10 @@ initdynimport(void)
Imp *m;
Dll *d;
Sym *s, *dynamic;
- int i;
dr = nil;
- for(i=0; i<NHASH; i++)
- for(s = hash[i]; s != S; s = s->hash) {
+ for(s = allsym; s != S; s = s->allsym) {
if(!s->reachable || !s->dynimpname || s->dynexport)
continue;
for(d = dr; d != nil; d = d->next) {
@@ -312,12 +310,10 @@ scmp(const void *p1, const void *p2)
static void
initdynexport(void)
{
- int i;
Sym *s;
nexport = 0;
- for(i=0; i<NHASH; i++)
- for(s = hash[i]; s != S; s = s->hash) {
+ for(s = allsym; s != S; s = s->allsym) {
if(!s->reachable || !s->dynimpname || !s->dynexport)
continue;
if(nexport+1 > sizeof(dexport)/sizeof(dexport[0])) {
diff --git a/src/cmd/ld/symtab.c b/src/cmd/ld/symtab.c
index 22777b6b5..aefe0b1af 100644
--- a/src/cmd/ld/symtab.c
+++ b/src/cmd/ld/symtab.c
@@ -340,6 +340,8 @@ putsymb(Sym *s, char *name, int t, vlong v, vlong size, int ver, Sym *typ)
void
symtab(void)
{
+ Sym *s;
+
// Define these so that they'll get put into the symbol table.
// data.c:/^address will provide the actual values.
xdefine("text", STEXT, 0);
@@ -351,11 +353,39 @@ symtab(void)
xdefine("end", SBSS, 0);
xdefine("epclntab", SRODATA, 0);
xdefine("esymtab", SRODATA, 0);
+
+ // pseudo-symbols to mark locations of type, string, and go string data.
+ s = lookup("type.*", 0);
+ s->type = STYPE;
+ s->size = 0;
+ s->reachable = 1;
+
+ s = lookup("go.string.*", 0);
+ s->type = SGOSTRING;
+ s->size = 0;
+ s->reachable = 1;
symt = lookup("symtab", 0);
symt->type = SRODATA;
symt->size = 0;
symt->reachable = 1;
+
+ // assign specific types so that they sort together.
+ // within a type they sort by size, so the .* symbols
+ // just defined above will be first.
+ // hide the specific symbols.
+ for(s = allsym; s != S; s = s->allsym) {
+ if(!s->reachable || s->special || s->type != SRODATA)
+ continue;
+ if(strncmp(s->name, "type.", 5) == 0) {
+ s->type = STYPE;
+ s->hide = 1;
+ }
+ if(strncmp(s->name, "go.string.", 10) == 0) {
+ s->type = SGOSTRING;
+ s->hide = 1;
+ }
+ }
genasmsym(putsymb);
}
diff --git a/src/cmd/nm/doc.go b/src/cmd/nm/doc.go
index 84a91792f..2a37dd835 100644
--- a/src/cmd/nm/doc.go
+++ b/src/cmd/nm/doc.go
@@ -11,6 +11,9 @@ Nm is a version of the Plan 9 nm command. The original is documented at
It prints the name list (symbol table) for programs compiled by gc as well as the
Plan 9 C compiler.
+This implementation adds the flag -S, which prints each symbol's size
+in decimal after its address.
+
For reasons of disambiguation it is installed as 6nm although it also serves
as an 8nm and a 5nm.
diff --git a/src/cmd/prof/gopprof b/src/cmd/prof/gopprof
index 4bcfa5800..8fa00cbe8 100755
--- a/src/cmd/prof/gopprof
+++ b/src/cmd/prof/gopprof
@@ -1896,6 +1896,7 @@ sub SvgJavascript {
// SVGPan
// http://www.cyberz.org/blog/2009/12/08/svgpan-a-javascript-svg-panzoomdrag-library/
// Local modification: if(true || ...) below to force panning, never moving.
+// Local modification: add clamping to fix bug in handleMouseWheel.
/**
* SVGPan library 1.2
@@ -2038,6 +2039,15 @@ function handleMouseWheel(evt) {
var z = 1 + delta; // Zoom factor: 0.9/1.1
+ // Clamp to reasonable values.
+ // The 0.1 check is important because
+ // a very large scroll can turn into a
+ // negative z, which rotates the image 180 degrees.
+ if(z < 0.1)
+ z = 0.1;
+ if(z > 10.0)
+ z = 10.0;
+
var g = svgDoc.getElementById("viewport");
var p = getEventPoint(evt);
@@ -2416,11 +2426,17 @@ sub RemoveUninterestingFrames {
'makechan',
'makemap',
'mal',
- 'mallocgc',
+ 'runtime.new',
+ 'makeslice1',
+ 'runtime.gostringsize',
+ 'runtime.malloc',
+ 'unsafe.New',
+ 'runtime.mallocgc',
'runtime.catstring',
'runtime.ifaceT2E',
'runtime.ifaceT2I',
'runtime.makechan',
+ 'runtime.makechan_c',
'runtime.makemap',
'runtime.makeslice',
'runtime.mal',
@@ -2460,7 +2476,8 @@ sub RemoveUninterestingFrames {
# Nothing skipped for unknown types
}
- if ($main::profile_type eq 'cpu') {
+ # Go doesn't have the problem that this heuristic tries to fix. Disable.
+ if (0 && $main::profile_type eq 'cpu') {
# If all the second-youngest program counters are the same,
# this STRONGLY suggests that it is an artifact of measurement,
# i.e., stack frames pushed by the CPU profiler signal handler.
@@ -2960,7 +2977,7 @@ sub FetchDynamicProfile {
$url = sprintf("http://$profile_name" . "seconds=%d",
$main::opt_seconds);
}
- $curl_timeout = sprintf("--max-time=%d",
+ $curl_timeout = sprintf("--max-time %d",
int($main::opt_seconds * 1.01 + 60));
} else {
# For non-CPU profiles, we add a type-extension to
diff --git a/src/lib9/Makefile b/src/lib9/Makefile
index a10d7730a..d222e2f53 100644
--- a/src/lib9/Makefile
+++ b/src/lib9/Makefile
@@ -82,7 +82,7 @@ LIB9OFILES=\
time.$O\
tokenize.$O\
-ifeq ($(GOOS),windows)
+ifeq ($(GOHOSTOS),windows)
LIB9OFILES+=\
win32.$O\
diff --git a/src/libmach/Makefile b/src/libmach/Makefile
index 5d7e87d86..2a53749c8 100644
--- a/src/libmach/Makefile
+++ b/src/libmach/Makefile
@@ -49,7 +49,7 @@ OFILES=\
6obj.$O\
8obj.$O\
-ifneq ($(GOOS),windows)
+ifneq ($(GOHOSTOS),windows)
OFILES+=\
$(shell uname | tr A-Z a-z).$O\
diff --git a/src/libmach/darwin.c b/src/libmach/darwin.c
index 7ee6f7ace..d44fd5612 100644
--- a/src/libmach/darwin.c
+++ b/src/libmach/darwin.c
@@ -579,7 +579,7 @@ machregrw(Map *map, Seg *seg, uvlong addr, void *v, uint n, int isr)
if(!isr)
thread_resume(thread);
rerrstr(buf, sizeof buf);
- if(strcmp(buf, "send invalid dest") == 0)
+ if(strstr(buf, "send invalid dest") != nil)
werrstr("process exited");
else
werrstr("thread_get_state: %r");
diff --git a/src/libmach/freebsd.c b/src/libmach/freebsd.c
index 531861e94..45de966ec 100644
--- a/src/libmach/freebsd.c
+++ b/src/libmach/freebsd.c
@@ -8,24 +8,28 @@ int
ctlproc(int pid, char *msg)
{
sysfatal("ctlproc unimplemented in FreeBSD");
+ return -1;
}
char*
proctextfile(int pid)
{
sysfatal("proctextfile unimplemented in FreeBSD");
+ return nil;
}
char*
procstatus(int pid)
{
sysfatal("procstatus unimplemented in FreeBSD");
+ return nil;
}
Map*
attachproc(int pid, Fhdr *fp)
{
sysfatal("attachproc unimplemented in FreeBSD");
+ return nil;
}
void
@@ -38,4 +42,5 @@ int
procthreadpids(int pid, int *p, int np)
{
sysfatal("procthreadpids unimplemented in FreeBSD");
+ return -1;
}
diff --git a/src/libmach/windows.c b/src/libmach/windows.c
index 391761c18..81fa6b6d2 100644
--- a/src/libmach/windows.c
+++ b/src/libmach/windows.c
@@ -8,24 +8,28 @@ int
ctlproc(int pid, char *msg)
{
sysfatal("ctlproc unimplemented in Windows");
+ return -1;
}
char*
proctextfile(int pid)
{
sysfatal("proctextfile unimplemented in Windows");
+ return nil;
}
char*
procstatus(int pid)
{
sysfatal("procstatus unimplemented in Windows");
+ return nil;
}
Map*
attachproc(int pid, Fhdr *fp)
{
sysfatal("attachproc unimplemented in Windows");
+ return nil;
}
void
@@ -38,22 +42,26 @@ int
procthreadpids(int pid, int *p, int np)
{
sysfatal("procthreadpids unimplemented in Windows");
+ return -1;
}
int
pread(int fd, void *buf, int count, int offset)
{
sysfatal("pread unimplemented in Windows");
+ return -1;
}
int
pwrite(int fd, void *buf, int count, int offset)
{
sysfatal("pwrite unimplemented in Windows");
+ return -1;
}
int
nanosleep(const struct timespec *rqtp, struct timespec *rmtp)
{
sysfatal("nanosleep unimplemented in Windows");
+ return -1;
}
diff --git a/src/make.bash b/src/make.bash
index d9ca40d42..79e368cb5 100755
--- a/src/make.bash
+++ b/src/make.bash
@@ -10,6 +10,17 @@ if [ ! -f env.bash ]; then
fi
. ./env.bash
+if ld --version 2>&1 | grep 'gold.*2\.20' >/dev/null; then
+ echo 'ERROR: Your system has gold 2.20 installed.'
+ echo 'This version is shipped by Ubuntu even though'
+ echo 'it is known not to work on Ubuntu.'
+ echo 'Binaries built with this linker are likely to fail in mysterious ways.'
+ echo
+ echo 'Run sudo apt-get remove binutils-gold.'
+ echo
+ exit 1
+fi
+
# Create target directories
if [ "$GOBIN" = "$GOROOT/bin" ]; then
mkdir -p "$GOROOT/bin"
diff --git a/src/pkg/Makefile b/src/pkg/Makefile
index 6e70690d1..e45b39e86 100644
--- a/src/pkg/Makefile
+++ b/src/pkg/Makefile
@@ -32,11 +32,12 @@ DIRS=\
container/vector\
crypto\
crypto/aes\
- crypto/block\
crypto/blowfish\
crypto/cast5\
crypto/cipher\
+ crypto/des\
crypto/dsa\
+ crypto/ecdsa\
crypto/elliptic\
crypto/hmac\
crypto/md4\
@@ -89,11 +90,13 @@ DIRS=\
go/scanner\
go/token\
go/typechecker\
+ go/types\
gob\
hash\
hash/adler32\
hash/crc32\
hash/crc64\
+ hash/fnv\
html\
http\
http/cgi\
@@ -102,6 +105,7 @@ DIRS=\
image\
image/jpeg\
image/png\
+ image/ycbcr\
index/suffixarray\
io\
io/ioutil\
@@ -153,8 +157,11 @@ DIRS=\
../cmd/cgo\
../cmd/ebnflint\
../cmd/godoc\
+ ../cmd/gofix\
../cmd/gofmt\
../cmd/goinstall\
+ ../cmd/gotest\
+ ../cmd/gotype\
../cmd/govet\
../cmd/goyacc\
../cmd/hgpatch\
@@ -165,7 +172,7 @@ DIRS+=\
endif
-NOTEST=\
+NOTEST+=\
crypto\
crypto/openpgp/error\
debug/proc\
@@ -180,7 +187,6 @@ NOTEST=\
net/dict\
rand\
runtime/cgo\
- runtime/pprof\
syscall\
testing\
testing/iotest\
@@ -188,24 +194,22 @@ NOTEST=\
../cmd/cgo\
../cmd/ebnflint\
../cmd/godoc\
- ../cmd/gofmt\
- ../cmd/goinstall\
+ ../cmd/gotest\
../cmd/govet\
../cmd/goyacc\
../cmd/hgpatch\
-NOBENCH=\
+NOBENCH+=\
container/vector\
# Disable tests that depend on an external network.
ifeq ($(DISABLE_NET_TESTS),1)
-NOTEST+=http net syslog
+NOTEST+=net syslog
endif
# Disable tests that windows cannot run yet.
ifeq ($(GOOS),windows)
NOTEST+=os/signal # no signals
-NOTEST+=path # tree walking does not work
NOTEST+=syslog # no network
NOTEST+=time # no syscall.Kill, syscall.SIGCHLD for sleep tests
endif
@@ -220,19 +224,26 @@ clean.dirs: $(addsuffix .clean, $(DIRS))
install.dirs: $(addsuffix .install, $(DIRS))
nuke.dirs: $(addsuffix .nuke, $(DIRS))
test.dirs: $(addsuffix .test, $(TEST))
+testshort.dirs: $(addsuffix .testshort, $(TEST))
bench.dirs: $(addsuffix .bench, $(BENCH))
%.clean:
+$(MAKE) -C $* clean
%.install:
- +$(MAKE) -C $* install
+ +@echo install $*
+ +@$(MAKE) -C $* install.clean >$*/build.out 2>&1 || (echo INSTALL FAIL $*; cat $*/build.out; exit 1)
%.nuke:
+$(MAKE) -C $* nuke
%.test:
- +$(MAKE) -C $* test
+ +@echo test $*
+ +@$(MAKE) -C $* test.clean >$*/test.out 2>&1 || (echo TEST FAIL $*; cat $*/test.out; exit 1)
+
+%.testshort:
+ +@echo test $*
+ +@$(MAKE) -C $* testshort.clean >$*/test.out 2>&1 || (echo TEST FAIL $*; cat $*/test.out; exit 1)
%.bench:
+$(MAKE) -C $* bench
@@ -243,6 +254,8 @@ install: install.dirs
test: test.dirs
+testshort: testshort.dirs
+
bench: bench.dirs ../../test/garbage.bench
nuke: nuke.dirs
diff --git a/src/pkg/archive/tar/reader.go b/src/pkg/archive/tar/reader.go
index 35a15f74b..0cfdf355d 100644
--- a/src/pkg/archive/tar/reader.go
+++ b/src/pkg/archive/tar/reader.go
@@ -27,13 +27,13 @@ var (
// tr := tar.NewReader(r)
// for {
// hdr, err := tr.Next()
-// if err != nil {
-// // handle error
-// }
-// if hdr == nil {
+// if err == os.EOF {
// // end of tar archive
// break
// }
+// if err != nil {
+// // handle error
+// }
// io.Copy(data, tr)
// }
type Reader struct {
@@ -95,7 +95,7 @@ func (tr *Reader) skipUnread() {
nr := tr.nb + tr.pad // number of bytes to skip
tr.nb, tr.pad = 0, 0
if sr, ok := tr.r.(io.Seeker); ok {
- if _, err := sr.Seek(nr, 1); err == nil {
+ if _, err := sr.Seek(nr, os.SEEK_CUR); err == nil {
return
}
}
diff --git a/src/pkg/archive/tar/reader_test.go b/src/pkg/archive/tar/reader_test.go
index aa4c797fb..32fc8f915 100644
--- a/src/pkg/archive/tar/reader_test.go
+++ b/src/pkg/archive/tar/reader_test.go
@@ -113,7 +113,7 @@ var untarTests = []*untarTest{
func TestReader(t *testing.T) {
testLoop:
for i, test := range untarTests {
- f, err := os.Open(test.file, os.O_RDONLY, 0444)
+ f, err := os.Open(test.file)
if err != nil {
t.Errorf("test %d: Unexpected error: %v", i, err)
continue
@@ -143,7 +143,7 @@ testLoop:
}
func TestPartialRead(t *testing.T) {
- f, err := os.Open("testdata/gnu.tar", os.O_RDONLY, 0444)
+ f, err := os.Open("testdata/gnu.tar")
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
@@ -181,7 +181,7 @@ func TestPartialRead(t *testing.T) {
func TestIncrementalRead(t *testing.T) {
test := gnuTarTest
- f, err := os.Open(test.file, os.O_RDONLY, 0444)
+ f, err := os.Open(test.file)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
@@ -235,7 +235,7 @@ func TestIncrementalRead(t *testing.T) {
func TestNonSeekable(t *testing.T) {
test := gnuTarTest
- f, err := os.Open(test.file, os.O_RDONLY, 0444)
+ f, err := os.Open(test.file)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
diff --git a/src/pkg/archive/tar/writer_test.go b/src/pkg/archive/tar/writer_test.go
index 48b891140..838cb7e1f 100644
--- a/src/pkg/archive/tar/writer_test.go
+++ b/src/pkg/archive/tar/writer_test.go
@@ -150,5 +150,8 @@ testLoop:
t.Errorf("test %d: Incorrect result: (-=expected, +=actual)\n%v",
i, bytediff(expected, actual))
}
+ if testing.Short() { // The second test is expensive.
+ break
+ }
}
}
diff --git a/src/pkg/archive/zip/reader.go b/src/pkg/archive/zip/reader.go
index d8d9bba60..0391d6441 100644
--- a/src/pkg/archive/zip/reader.go
+++ b/src/pkg/archive/zip/reader.go
@@ -19,6 +19,7 @@ import (
"hash/crc32"
"encoding/binary"
"io"
+ "io/ioutil"
"os"
)
@@ -34,6 +35,11 @@ type Reader struct {
Comment string
}
+type ReadCloser struct {
+ f *os.File
+ Reader
+}
+
type File struct {
FileHeader
zipr io.ReaderAt
@@ -46,43 +52,60 @@ func (f *File) hasDataDescriptor() bool {
return f.Flags&0x8 != 0
}
-// OpenReader will open the Zip file specified by name and return a Reader.
-func OpenReader(name string) (*Reader, os.Error) {
- f, err := os.Open(name, os.O_RDONLY, 0644)
+// OpenReader will open the Zip file specified by name and return a ReaderCloser.
+func OpenReader(name string) (*ReadCloser, os.Error) {
+ f, err := os.Open(name)
if err != nil {
return nil, err
}
fi, err := f.Stat()
if err != nil {
+ f.Close()
return nil, err
}
- return NewReader(f, fi.Size)
+ r := new(ReadCloser)
+ if err := r.init(f, fi.Size); err != nil {
+ f.Close()
+ return nil, err
+ }
+ return r, nil
}
// NewReader returns a new Reader reading from r, which is assumed to
// have the given size in bytes.
func NewReader(r io.ReaderAt, size int64) (*Reader, os.Error) {
- end, err := readDirectoryEnd(r, size)
- if err != nil {
+ zr := new(Reader)
+ if err := zr.init(r, size); err != nil {
return nil, err
}
- z := &Reader{
- r: r,
- File: make([]*File, end.directoryRecords),
- Comment: end.comment,
+ return zr, nil
+}
+
+func (z *Reader) init(r io.ReaderAt, size int64) os.Error {
+ end, err := readDirectoryEnd(r, size)
+ if err != nil {
+ return err
}
+ z.r = r
+ z.File = make([]*File, end.directoryRecords)
+ z.Comment = end.comment
rs := io.NewSectionReader(r, 0, size)
- if _, err = rs.Seek(int64(end.directoryOffset), 0); err != nil {
- return nil, err
+ if _, err = rs.Seek(int64(end.directoryOffset), os.SEEK_SET); err != nil {
+ return err
}
buf := bufio.NewReader(rs)
for i := range z.File {
z.File[i] = &File{zipr: r, zipsize: size}
if err := readDirectoryHeader(z.File[i], buf); err != nil {
- return nil, err
+ return err
}
}
- return z, nil
+ return nil
+}
+
+// Close closes the Zip file, rendering it unusable for I/O.
+func (rc *ReadCloser) Close() os.Error {
+ return rc.f.Close()
}
// Open returns a ReadCloser that provides access to the File's contents.
@@ -93,7 +116,7 @@ func (f *File) Open() (rc io.ReadCloser, err os.Error) {
if err = readFileHeader(f, r); err != nil {
return
}
- if f.bodyOffset, err = r.Seek(0, 1); err != nil {
+ if f.bodyOffset, err = r.Seek(0, os.SEEK_CUR); err != nil {
return
}
}
@@ -109,7 +132,7 @@ func (f *File) Open() (rc io.ReadCloser, err os.Error) {
r := io.NewSectionReader(f.zipr, off+f.bodyOffset, size)
switch f.Method {
case 0: // store (no compression)
- rc = nopCloser{r}
+ rc = ioutil.NopCloser(r)
case 8: // DEFLATE
rc = flate.NewReader(r)
default:
@@ -147,12 +170,6 @@ func (r *checksumReader) Read(b []byte) (n int, err os.Error) {
func (r *checksumReader) Close() os.Error { return r.rc.Close() }
-type nopCloser struct {
- io.Reader
-}
-
-func (f nopCloser) Close() os.Error { return nil }
-
func readFileHeader(f *File, r io.Reader) (err os.Error) {
defer func() {
if rerr, ok := recover().(os.Error); ok {
diff --git a/src/pkg/archive/zip/reader_test.go b/src/pkg/archive/zip/reader_test.go
index 72e8cccfd..c72cd9a23 100644
--- a/src/pkg/archive/zip/reader_test.go
+++ b/src/pkg/archive/zip/reader_test.go
@@ -76,6 +76,12 @@ func readTestZip(t *testing.T, zt ZipTest) {
return
}
+ // bail if file is not zip
+ if err == FormatError {
+ return
+ }
+ defer z.Close()
+
// bail here if no Files expected to be tested
// (there may actually be files in the zip, but we don't care)
if zt.File == nil {
diff --git a/src/pkg/asn1/asn1.go b/src/pkg/asn1/asn1.go
index d06b1d4d7..8c99bd7a0 100644
--- a/src/pkg/asn1/asn1.go
+++ b/src/pkg/asn1/asn1.go
@@ -373,7 +373,7 @@ func parseTagAndLength(bytes []byte, initOffset int) (ret tagAndLength, offset i
// 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) {
+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"}
@@ -389,6 +389,11 @@ func parseSequenceOf(bytes []byte, sliceType *reflect.SliceType, elemType reflec
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
@@ -404,7 +409,7 @@ func parseSequenceOf(bytes []byte, sliceType *reflect.SliceType, elemType reflec
params := fieldParameters{}
offset := 0
for i := 0; i < numElements; i++ {
- offset, err = parseField(ret.Elem(i), bytes, offset, params)
+ offset, err = parseField(ret.Index(i), bytes, offset, params)
if err != nil {
return
}
@@ -456,13 +461,12 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
}
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))
+ v.Set(reflect.NewValue(result))
return
}
// Deal with the ANY type.
- if ifaceType, ok := fieldType.(*reflect.InterfaceType); ok && ifaceType.NumMethod() == 0 {
- ifaceValue := v.(*reflect.InterfaceValue)
+ if ifaceType := fieldType; ifaceType.Kind() == reflect.Interface && ifaceType.NumMethod() == 0 {
var t tagAndLength
t, offset, err = parseTagAndLength(bytes, offset)
if err != nil {
@@ -501,7 +505,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
return
}
if result != nil {
- ifaceValue.Set(reflect.NewValue(result))
+ v.Set(reflect.NewValue(result))
}
return
}
@@ -516,7 +520,11 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
return
}
if params.explicit {
- if t.class == classContextSpecific && t.tag == *params.tag && (t.length == 0 || t.isCompound) {
+ 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 {
@@ -527,9 +535,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
err = StructuralError{"Zero length explicit tag was not an asn1.Flag"}
return
}
-
- flagValue := v.(*reflect.BoolValue)
- flagValue.Set(true)
+ v.SetBool(true)
return
}
} else {
@@ -551,6 +557,10 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
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.
@@ -566,6 +576,11 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
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.
@@ -588,23 +603,20 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
switch fieldType {
case objectIdentifierType:
newSlice, err1 := parseObjectIdentifier(innerBytes)
- sliceValue := v.(*reflect.SliceValue)
- sliceValue.Set(reflect.MakeSlice(sliceValue.Type().(*reflect.SliceType), len(newSlice), len(newSlice)))
+ v.Set(reflect.MakeSlice(v.Type(), len(newSlice), len(newSlice)))
if err1 == nil {
- reflect.Copy(sliceValue, reflect.NewValue(newSlice).(reflect.ArrayOrSliceValue))
+ reflect.Copy(v, reflect.NewValue(newSlice))
}
err = err1
return
case bitStringType:
- structValue := v.(*reflect.StructValue)
bs, err1 := parseBitString(innerBytes)
if err1 == nil {
- structValue.Set(reflect.NewValue(bs).(*reflect.StructValue))
+ v.Set(reflect.NewValue(bs))
}
err = err1
return
case timeType:
- ptrValue := v.(*reflect.PtrValue)
var time *time.Time
var err1 os.Error
if universalTag == tagUTCTime {
@@ -613,55 +625,53 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
time, err1 = parseGeneralizedTime(innerBytes)
}
if err1 == nil {
- ptrValue.Set(reflect.NewValue(time).(*reflect.PtrValue))
+ v.Set(reflect.NewValue(time))
}
err = err1
return
case enumeratedType:
parsedInt, err1 := parseInt(innerBytes)
- enumValue := v.(*reflect.IntValue)
if err1 == nil {
- enumValue.Set(int64(parsedInt))
+ v.SetInt(int64(parsedInt))
}
err = err1
return
case flagType:
- flagValue := v.(*reflect.BoolValue)
- flagValue.Set(true)
+ v.SetBool(true)
return
}
- switch val := v.(type) {
- case *reflect.BoolValue:
+ switch val := v; val.Kind() {
+ case reflect.Bool:
parsedBool, err1 := parseBool(innerBytes)
if err1 == nil {
- val.Set(parsedBool)
+ val.SetBool(parsedBool)
}
err = err1
return
- case *reflect.IntValue:
+ 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.Set(int64(parsedInt))
+ val.SetInt(int64(parsedInt))
}
err = err1
return
case reflect.Int64:
parsedInt, err1 := parseInt64(innerBytes)
if err1 == nil {
- val.Set(parsedInt)
+ val.SetInt(parsedInt)
}
err = err1
return
}
- case *reflect.StructValue:
- structType := fieldType.(*reflect.StructType)
+ case reflect.Struct:
+ structType := fieldType
if structType.NumField() > 0 &&
structType.Field(0).Type == rawContentsType {
bytes := bytes[initOffset:offset]
- val.Field(0).SetValue(reflect.NewValue(RawContent(bytes)))
+ val.Field(0).Set(reflect.NewValue(RawContent(bytes)))
}
innerOffset := 0
@@ -679,11 +689,11 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
// 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)
+ 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).(reflect.ArrayOrSliceValue))
+ reflect.Copy(val, reflect.NewValue(innerBytes))
return
}
newSlice, err1 := parseSequenceOf(innerBytes, sliceType, sliceType.Elem())
@@ -692,7 +702,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
}
err = err1
return
- case *reflect.StringValue:
+ case reflect.String:
var v string
switch universalTag {
case tagPrintableString:
@@ -701,11 +711,17 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
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)
+ val.SetString(v)
}
return
}
@@ -724,9 +740,9 @@ func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) {
if params.defaultValue == nil {
return
}
- switch val := v.(type) {
- case *reflect.IntValue:
- val.Set(*params.defaultValue)
+ switch val := v; val.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ val.SetInt(*params.defaultValue)
}
return
}
@@ -776,8 +792,14 @@ func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) {
// 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) {
- v := reflect.NewValue(val).(*reflect.PtrValue).Elem()
- offset, err := parseField(v, b, 0, fieldParameters{})
+ 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
}
diff --git a/src/pkg/asn1/asn1_test.go b/src/pkg/asn1/asn1_test.go
index 34b5f1ecd..018c534eb 100644
--- a/src/pkg/asn1/asn1_test.go
+++ b/src/pkg/asn1/asn1_test.go
@@ -249,11 +249,12 @@ var parseFieldParametersTestData []parseFieldParametersTest = []parseFieldParame
{"printable", fieldParameters{stringType: tagPrintableString}},
{"optional", fieldParameters{optional: true}},
{"explicit", fieldParameters{explicit: true, tag: new(int)}},
+ {"application", fieldParameters{application: true, tag: new(int)}},
{"optional,explicit", fieldParameters{optional: true, explicit: true, tag: new(int)}},
{"default:42", fieldParameters{defaultValue: newInt64(42)}},
{"tag:17", fieldParameters{tag: newInt(17)}},
{"optional,explicit,default:42,tag:17", fieldParameters{optional: true, explicit: true, defaultValue: newInt64(42), tag: newInt(17)}},
- {"optional,explicit,default:42,tag:17,rubbish1", fieldParameters{true, true, newInt64(42), newInt(17), 0, false}},
+ {"optional,explicit,default:42,tag:17,rubbish1", fieldParameters{true, true, false, newInt64(42), newInt(17), 0, false}},
{"set", fieldParameters{set: true}},
}
@@ -308,9 +309,9 @@ var unmarshalTestData []unmarshalTest = []unmarshalTest{
func TestUnmarshal(t *testing.T) {
for i, test := range unmarshalTestData {
- pv := reflect.MakeZero(reflect.NewValue(test.out).Type())
- zv := reflect.MakeZero(pv.Type().(*reflect.PtrType).Elem())
- pv.(*reflect.PtrValue).PointTo(zv)
+ pv := reflect.Zero(reflect.NewValue(test.out).Type())
+ zv := reflect.Zero(pv.Type().Elem())
+ pv.Set(zv.Addr())
val := pv.Interface()
_, err := Unmarshal(test.in, val)
if err != nil {
diff --git a/src/pkg/asn1/common.go b/src/pkg/asn1/common.go
index 4a5eca145..158987747 100644
--- a/src/pkg/asn1/common.go
+++ b/src/pkg/asn1/common.go
@@ -32,6 +32,7 @@ const (
tagIA5String = 22
tagUTCTime = 23
tagGeneralizedTime = 24
+ tagGeneralString = 27
)
const (
@@ -67,7 +68,8 @@ type tagAndLength struct {
// fieldParameters is the parsed representation of tag string from a structure field.
type fieldParameters struct {
optional bool // true iff the field is OPTIONAL
- explicit bool // true iff and EXPLICIT tag is in use.
+ explicit bool // true iff an EXPLICIT tag is in use.
+ application bool // true iff an APPLICATION tag is in use.
defaultValue *int64 // a default value for INTEGER typed fields (maybe nil).
tag *int // the EXPLICIT or IMPLICIT tag (maybe nil).
stringType int // the string tag to use when marshaling.
@@ -89,7 +91,6 @@ func parseFieldParameters(str string) (ret fieldParameters) {
ret.explicit = true
if ret.tag == nil {
ret.tag = new(int)
- *ret.tag = 0
}
case part == "ia5":
ret.stringType = tagIA5String
@@ -109,6 +110,11 @@ func parseFieldParameters(str string) (ret fieldParameters) {
}
case part == "set":
ret.set = true
+ case part == "application":
+ ret.application = true
+ if ret.tag == nil {
+ ret.tag = new(int)
+ }
}
}
return
@@ -127,14 +133,14 @@ func getUniversalType(t reflect.Type) (tagNumber int, isCompound, ok bool) {
case enumeratedType:
return tagEnum, false, true
}
- switch t := t.(type) {
- case *reflect.BoolType:
+ switch t.Kind() {
+ case reflect.Bool:
return tagBoolean, false, true
- case *reflect.IntType:
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return tagInteger, false, true
- case *reflect.StructType:
+ case reflect.Struct:
return tagSequence, true, true
- case *reflect.SliceType:
+ case reflect.Slice:
if t.Elem().Kind() == reflect.Uint8 {
return tagOctetString, false, true
}
@@ -142,7 +148,7 @@ func getUniversalType(t reflect.Type) (tagNumber int, isCompound, ok bool) {
return tagSet, true, true
}
return tagSequence, true, true
- case *reflect.StringType:
+ case reflect.String:
return tagPrintableString, false, true
}
return 0, false, false
diff --git a/src/pkg/asn1/marshal.go b/src/pkg/asn1/marshal.go
index 57b8f20ba..64cb0f2bb 100644
--- a/src/pkg/asn1/marshal.go
+++ b/src/pkg/asn1/marshal.go
@@ -125,6 +125,28 @@ func int64Length(i int64) (numBytes int) {
return
}
+func marshalLength(out *forkableWriter, i int) (err os.Error) {
+ n := lengthLength(i)
+
+ for ; n > 0; n-- {
+ err = out.WriteByte(byte(i >> uint((n-1)*8)))
+ if err != nil {
+ return
+ }
+ }
+
+ return nil
+}
+
+func lengthLength(i int) (numBytes int) {
+ numBytes = 1
+ for i > 255 {
+ numBytes++
+ i >>= 8
+ }
+ return
+}
+
func marshalTagAndLength(out *forkableWriter, t tagAndLength) (err os.Error) {
b := uint8(t.class) << 6
if t.isCompound {
@@ -149,12 +171,12 @@ func marshalTagAndLength(out *forkableWriter, t tagAndLength) (err os.Error) {
}
if t.length >= 128 {
- l := int64Length(int64(t.length))
+ l := lengthLength(t.length)
err = out.WriteByte(0x80 | byte(l))
if err != nil {
return
}
- err = marshalInt64(out, int64(t.length))
+ err = marshalLength(out, t.length)
if err != nil {
return
}
@@ -314,28 +336,28 @@ func marshalBody(out *forkableWriter, value reflect.Value, params fieldParameter
return marshalObjectIdentifier(out, value.Interface().(ObjectIdentifier))
}
- switch v := value.(type) {
- case *reflect.BoolValue:
- if v.Get() {
+ switch v := value; v.Kind() {
+ case reflect.Bool:
+ if v.Bool() {
return out.WriteByte(255)
} else {
return out.WriteByte(0)
}
- case *reflect.IntValue:
- return marshalInt64(out, int64(v.Get()))
- case *reflect.StructValue:
- t := v.Type().(*reflect.StructType)
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return marshalInt64(out, int64(v.Int()))
+ case reflect.Struct:
+ t := v.Type()
startingField := 0
// If the first element of the structure is a non-empty
// RawContents, then we don't bother serialising the rest.
if t.NumField() > 0 && t.Field(0).Type == rawContentsType {
- s := v.Field(0).(*reflect.SliceValue)
+ s := v.Field(0)
if s.Len() > 0 {
bytes := make([]byte, s.Len())
for i := 0; i < s.Len(); i++ {
- bytes[i] = uint8(s.Elem(i).(*reflect.UintValue).Get())
+ bytes[i] = uint8(s.Index(i).Uint())
}
/* The RawContents will contain the tag and
* length fields but we'll also be writing
@@ -357,12 +379,12 @@ func marshalBody(out *forkableWriter, value reflect.Value, params fieldParameter
}
}
return
- case *reflect.SliceValue:
- sliceType := v.Type().(*reflect.SliceType)
+ case reflect.Slice:
+ sliceType := v.Type()
if sliceType.Elem().Kind() == reflect.Uint8 {
bytes := make([]byte, v.Len())
for i := 0; i < v.Len(); i++ {
- bytes[i] = uint8(v.Elem(i).(*reflect.UintValue).Get())
+ bytes[i] = uint8(v.Index(i).Uint())
}
_, err = out.Write(bytes)
return
@@ -372,17 +394,17 @@ func marshalBody(out *forkableWriter, value reflect.Value, params fieldParameter
for i := 0; i < v.Len(); i++ {
var pre *forkableWriter
pre, out = out.fork()
- err = marshalField(pre, v.Elem(i), params)
+ err = marshalField(pre, v.Index(i), params)
if err != nil {
return
}
}
return
- case *reflect.StringValue:
+ case reflect.String:
if params.stringType == tagIA5String {
- return marshalIA5String(out, v.Get())
+ return marshalIA5String(out, v.String())
} else {
- return marshalPrintableString(out, v.Get())
+ return marshalPrintableString(out, v.String())
}
return
}
@@ -392,7 +414,7 @@ func marshalBody(out *forkableWriter, value reflect.Value, params fieldParameter
func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters) (err os.Error) {
// If the field is an interface{} then recurse into it.
- if v, ok := v.(*reflect.InterfaceValue); ok && v.Type().(*reflect.InterfaceType).NumMethod() == 0 {
+ if v.Kind() == reflect.Interface && v.Type().NumMethod() == 0 {
return marshalField(out, v.Elem(), params)
}
@@ -406,7 +428,7 @@ func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters)
return
}
- if params.optional && reflect.DeepEqual(v.Interface(), reflect.MakeZero(v.Type()).Interface()) {
+ if params.optional && reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface()) {
return
}
diff --git a/src/pkg/asn1/marshal_test.go b/src/pkg/asn1/marshal_test.go
index 85eafc9e4..cd165d203 100644
--- a/src/pkg/asn1/marshal_test.go
+++ b/src/pkg/asn1/marshal_test.go
@@ -77,6 +77,30 @@ var marshalTests = []marshalTest{
{ObjectIdentifier([]int{1, 2, 3, 4}), "06032a0304"},
{ObjectIdentifier([]int{1, 2, 840, 133549, 1, 1, 5}), "06092a864888932d010105"},
{"test", "130474657374"},
+ {
+ "" +
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", // This is 127 times 'x'
+ "137f" +
+ "7878787878787878787878787878787878787878787878787878787878787878" +
+ "7878787878787878787878787878787878787878787878787878787878787878" +
+ "7878787878787878787878787878787878787878787878787878787878787878" +
+ "78787878787878787878787878787878787878787878787878787878787878",
+ },
+ {
+ "" +
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", // This is 128 times 'x'
+ "138180" +
+ "7878787878787878787878787878787878787878787878787878787878787878" +
+ "7878787878787878787878787878787878787878787878787878787878787878" +
+ "7878787878787878787878787878787878787878787878787878787878787878" +
+ "7878787878787878787878787878787878787878787878787878787878787878",
+ },
{ia5StringTest{"test"}, "3006160474657374"},
{printableStringTest{"test"}, "3006130474657374"},
{printableStringTest{"test*"}, "30071305746573742a"},
diff --git a/src/pkg/big/int.go b/src/pkg/big/int.go
index 46e008734..f1ea7b1c2 100755
--- a/src/pkg/big/int.go
+++ b/src/pkg/big/int.go
@@ -8,6 +8,7 @@ package big
import (
"fmt"
+ "os"
"rand"
)
@@ -336,6 +337,10 @@ func fmtbase(ch int) int {
// 'x' (hexadecimal).
//
func (x *Int) Format(s fmt.State, ch int) {
+ if x == nil {
+ fmt.Fprint(s, "<nil>")
+ return
+ }
if x.neg {
fmt.Fprint(s, "-")
}
@@ -393,62 +398,19 @@ func (z *Int) SetString(s string, base int) (*Int, bool) {
}
-// SetBytes interprets b as the bytes of a big-endian, unsigned integer and
-// sets z to that value.
-func (z *Int) SetBytes(b []byte) *Int {
- const s = _S
- z.abs = z.abs.make((len(b) + s - 1) / s)
-
- j := 0
- for len(b) >= s {
- var w Word
-
- for i := s; i > 0; i-- {
- w <<= 8
- w |= Word(b[len(b)-i])
- }
-
- z.abs[j] = w
- j++
- b = b[0 : len(b)-s]
- }
-
- if len(b) > 0 {
- var w Word
-
- for i := len(b); i > 0; i-- {
- w <<= 8
- w |= Word(b[len(b)-i])
- }
-
- z.abs[j] = w
- }
-
- z.abs = z.abs.norm()
+// SetBytes interprets buf as the bytes of a big-endian unsigned
+// integer, sets z to that value, and returns z.
+func (z *Int) SetBytes(buf []byte) *Int {
+ z.abs = z.abs.setBytes(buf)
z.neg = false
return z
}
-// Bytes returns the absolute value of x as a big-endian byte array.
+// Bytes returns the absolute value of z as a big-endian byte slice.
func (z *Int) Bytes() []byte {
- const s = _S
- b := make([]byte, len(z.abs)*s)
-
- for i, w := range z.abs {
- wordBytes := b[(len(z.abs)-i-1)*s : (len(z.abs)-i)*s]
- for j := s - 1; j >= 0; j-- {
- wordBytes[j] = byte(w)
- w >>= 8
- }
- }
-
- i := 0
- for i < len(b) && b[i] == 0 {
- i++
- }
-
- return b[i:]
+ buf := make([]byte, len(z.abs)*_S)
+ return buf[z.abs.bytes(buf):]
}
@@ -739,3 +701,34 @@ func (z *Int) Not(x *Int) *Int {
z.neg = true // z cannot be zero if x is positive
return z
}
+
+
+// Gob codec version. Permits backward-compatible changes to the encoding.
+const version byte = 1
+
+// GobEncode implements the gob.GobEncoder interface.
+func (z *Int) GobEncode() ([]byte, os.Error) {
+ buf := make([]byte, len(z.abs)*_S+1) // extra byte for version and sign bit
+ i := z.abs.bytes(buf) - 1 // i >= 0
+ b := version << 1 // make space for sign bit
+ if z.neg {
+ b |= 1
+ }
+ buf[i] = b
+ return buf[i:], nil
+}
+
+
+// GobDecode implements the gob.GobDecoder interface.
+func (z *Int) GobDecode(buf []byte) os.Error {
+ if len(buf) == 0 {
+ return os.NewError("Int.GobDecode: no data")
+ }
+ b := buf[0]
+ if b>>1 != version {
+ return os.NewError(fmt.Sprintf("Int.GobDecode: encoding version %d not supported", b>>1))
+ }
+ z.neg = b&1 != 0
+ z.abs = z.abs.setBytes(buf[1:])
+ return nil
+}
diff --git a/src/pkg/big/int_test.go b/src/pkg/big/int_test.go
index fc981e1da..9c19dd5da 100755
--- a/src/pkg/big/int_test.go
+++ b/src/pkg/big/int_test.go
@@ -8,6 +8,7 @@ import (
"bytes"
"encoding/hex"
"fmt"
+ "gob"
"testing"
"testing/quick"
)
@@ -715,18 +716,25 @@ var composites = []string{
func TestProbablyPrime(t *testing.T) {
+ nreps := 20
+ if testing.Short() {
+ nreps = 1
+ }
for i, s := range primes {
p, _ := new(Int).SetString(s, 10)
- if !ProbablyPrime(p, 20) {
+ if !ProbablyPrime(p, nreps) {
t.Errorf("#%d prime found to be non-prime (%s)", i, s)
}
}
for i, s := range composites {
c, _ := new(Int).SetString(s, 10)
- if ProbablyPrime(c, 20) {
+ if ProbablyPrime(c, nreps) {
t.Errorf("#%d composite found to be prime (%s)", i, s)
}
+ if testing.Short() {
+ break
+ }
}
}
@@ -1053,3 +1061,41 @@ func TestModInverse(t *testing.T) {
}
}
}
+
+
+var gobEncodingTests = []string{
+ "0",
+ "1",
+ "2",
+ "10",
+ "42",
+ "1234567890",
+ "298472983472983471903246121093472394872319615612417471234712061",
+}
+
+func TestGobEncoding(t *testing.T) {
+ var medium bytes.Buffer
+ enc := gob.NewEncoder(&medium)
+ dec := gob.NewDecoder(&medium)
+ for i, test := range gobEncodingTests {
+ for j := 0; j < 2; j++ {
+ medium.Reset() // empty buffer for each test case (in case of failures)
+ stest := test
+ if j == 0 {
+ stest = "-" + test
+ }
+ var tx Int
+ tx.SetString(stest, 10)
+ if err := enc.Encode(&tx); err != nil {
+ t.Errorf("#%d%c: encoding failed: %s", i, 'a'+j, err)
+ }
+ var rx Int
+ if err := dec.Decode(&rx); err != nil {
+ t.Errorf("#%d%c: decoding failed: %s", i, 'a'+j, err)
+ }
+ if rx.Cmp(&tx) != 0 {
+ t.Errorf("#%d%c: transmission failed: got %s want %s", i, 'a'+j, &rx, &tx)
+ }
+ }
+ }
+}
diff --git a/src/pkg/big/nat.go b/src/pkg/big/nat.go
index a308f69e8..a04d3b1d9 100755
--- a/src/pkg/big/nat.go
+++ b/src/pkg/big/nat.go
@@ -1065,3 +1065,50 @@ NextRandom:
return true
}
+
+
+// bytes writes the value of z into buf using big-endian encoding.
+// len(buf) must be >= len(z)*_S. The value of z is encoded in the
+// slice buf[i:]. The number i of unused bytes at the beginning of
+// buf is returned as result.
+func (z nat) bytes(buf []byte) (i int) {
+ i = len(buf)
+ for _, d := range z {
+ for j := 0; j < _S; j++ {
+ i--
+ buf[i] = byte(d)
+ d >>= 8
+ }
+ }
+
+ for i < len(buf) && buf[i] == 0 {
+ i++
+ }
+
+ return
+}
+
+
+// setBytes interprets buf as the bytes of a big-endian unsigned
+// integer, sets z to that value, and returns z.
+func (z nat) setBytes(buf []byte) nat {
+ z = z.make((len(buf) + _S - 1) / _S)
+
+ k := 0
+ s := uint(0)
+ var d Word
+ for i := len(buf); i > 0; i-- {
+ d |= Word(buf[i-1]) << s
+ if s += 8; s == _S*8 {
+ z[k] = d
+ k++
+ s = 0
+ d = 0
+ }
+ }
+ if k < len(z) {
+ z[k] = d
+ }
+
+ return z.norm()
+}
diff --git a/src/pkg/bufio/bufio.go b/src/pkg/bufio/bufio.go
index eae5c5ce9..32a25afae 100644
--- a/src/pkg/bufio/bufio.go
+++ b/src/pkg/bufio/bufio.go
@@ -282,6 +282,33 @@ func (b *Reader) ReadSlice(delim byte) (line []byte, err os.Error) {
panic("not reached")
}
+// ReadLine tries to return a single line, not including the end-of-line bytes.
+// If the line was too long for the buffer then isPrefix is set and the
+// beginning of the line is returned. The rest of the line will be returned
+// from future calls. isPrefix will be false when returning the last fragment
+// of the line. The returned buffer is only valid until the next call to
+// ReadLine. ReadLine either returns a non-nil line or it returns an error,
+// never both.
+func (b *Reader) ReadLine() (line []byte, isPrefix bool, err os.Error) {
+ line, err = b.ReadSlice('\n')
+ if err == ErrBufferFull {
+ return line, true, nil
+ }
+
+ if len(line) == 0 {
+ return
+ }
+ err = nil
+
+ if line[len(line)-1] == '\n' {
+ line = line[:len(line)-1]
+ }
+ if len(line) > 0 && line[len(line)-1] == '\r' {
+ line = line[:len(line)-1]
+ }
+ return
+}
+
// ReadBytes reads until the first occurrence of delim in the input,
// returning a slice containing the data up to and including the delimiter.
// If ReadBytes encounters an error before finding a delimiter,
@@ -415,38 +442,27 @@ func (b *Writer) Buffered() int { return b.n }
// If nn < len(p), it also returns an error explaining
// why the write is short.
func (b *Writer) Write(p []byte) (nn int, err os.Error) {
- if b.err != nil {
- return 0, b.err
- }
- nn = 0
- for len(p) > 0 {
- n := b.Available()
- if n <= 0 {
- if b.Flush(); b.err != nil {
- break
- }
- n = b.Available()
- }
- if b.Buffered() == 0 && len(p) >= len(b.buf) {
+ for len(p) > b.Available() && b.err == nil {
+ var n int
+ if b.Buffered() == 0 {
// Large write, empty buffer.
// Write directly from p to avoid copy.
n, b.err = b.wr.Write(p)
- nn += n
- p = p[n:]
- if b.err != nil {
- break
- }
- continue
+ } else {
+ n = copy(b.buf[b.n:], p)
+ b.n += n
+ b.Flush()
}
- if n > len(p) {
- n = len(p)
- }
- copy(b.buf[b.n:b.n+n], p[0:n])
- b.n += n
nn += n
p = p[n:]
}
- return nn, b.err
+ if b.err != nil {
+ return nn, b.err
+ }
+ n := copy(b.buf[b.n:], p)
+ b.n += n
+ nn += n
+ return nn, nil
}
// WriteByte writes a single byte.
@@ -496,24 +512,21 @@ func (b *Writer) WriteRune(rune int) (size int, err os.Error) {
// If the count is less than len(s), it also returns an error explaining
// why the write is short.
func (b *Writer) WriteString(s string) (int, os.Error) {
- if b.err != nil {
- return 0, b.err
- }
- // Common case, worth making fast.
- if b.Available() >= len(s) || len(b.buf) >= len(s) && b.Flush() == nil {
- for i := 0; i < len(s); i++ { // loop over bytes, not runes.
- b.buf[b.n] = s[i]
- b.n++
- }
- return len(s), nil
+ nn := 0
+ for len(s) > b.Available() && b.err == nil {
+ n := copy(b.buf[b.n:], s)
+ b.n += n
+ nn += n
+ s = s[n:]
+ b.Flush()
}
- for i := 0; i < len(s); i++ { // loop over bytes, not runes.
- b.WriteByte(s[i])
- if b.err != nil {
- return i, b.err
- }
+ if b.err != nil {
+ return nn, b.err
}
- return len(s), nil
+ n := copy(b.buf[b.n:], s)
+ b.n += n
+ nn += n
+ return nn, nil
}
// buffered input and output
diff --git a/src/pkg/bufio/bufio_test.go b/src/pkg/bufio/bufio_test.go
index 059ca6dd2..123adac29 100644
--- a/src/pkg/bufio/bufio_test.go
+++ b/src/pkg/bufio/bufio_test.go
@@ -2,12 +2,14 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package bufio
+package bufio_test
import (
+ . "bufio"
"bytes"
"fmt"
"io"
+ "io/ioutil"
"os"
"strings"
"testing"
@@ -502,9 +504,8 @@ func TestWriteString(t *testing.T) {
b.WriteString("7890") // easy after flush
b.WriteString("abcdefghijklmnopqrstuvwxy") // hard
b.WriteString("z")
- b.Flush()
- if b.err != nil {
- t.Error("WriteString", b.err)
+ if err := b.Flush(); err != nil {
+ t.Error("WriteString", err)
}
s := "01234567890abcdefghijklmnopqrstuvwxyz"
if string(buf.Bytes()) != s {
@@ -570,3 +571,128 @@ func TestPeekThenUnreadRune(t *testing.T) {
r.UnreadRune()
r.ReadRune() // Used to panic here
}
+
+var testOutput = []byte("0123456789abcdefghijklmnopqrstuvwxy")
+var testInput = []byte("012\n345\n678\n9ab\ncde\nfgh\nijk\nlmn\nopq\nrst\nuvw\nxy")
+var testInputrn = []byte("012\r\n345\r\n678\r\n9ab\r\ncde\r\nfgh\r\nijk\r\nlmn\r\nopq\r\nrst\r\nuvw\r\nxy\r\n\n\r\n")
+
+// TestReader wraps a []byte and returns reads of a specific length.
+type testReader struct {
+ data []byte
+ stride int
+}
+
+func (t *testReader) Read(buf []byte) (n int, err os.Error) {
+ n = t.stride
+ if n > len(t.data) {
+ n = len(t.data)
+ }
+ if n > len(buf) {
+ n = len(buf)
+ }
+ copy(buf, t.data)
+ t.data = t.data[n:]
+ if len(t.data) == 0 {
+ err = os.EOF
+ }
+ return
+}
+
+func testReadLine(t *testing.T, input []byte) {
+ //for stride := 1; stride < len(input); stride++ {
+ for stride := 1; stride < 2; stride++ {
+ done := 0
+ reader := testReader{input, stride}
+ l, _ := NewReaderSize(&reader, len(input)+1)
+ for {
+ line, isPrefix, err := l.ReadLine()
+ if len(line) > 0 && err != nil {
+ t.Errorf("ReadLine returned both data and error: %s", err)
+ }
+ if isPrefix {
+ t.Errorf("ReadLine returned prefix")
+ }
+ if err != nil {
+ if err != os.EOF {
+ t.Fatalf("Got unknown error: %s", err)
+ }
+ break
+ }
+ if want := testOutput[done : done+len(line)]; !bytes.Equal(want, line) {
+ t.Errorf("Bad line at stride %d: want: %x got: %x", stride, want, line)
+ }
+ done += len(line)
+ }
+ if done != len(testOutput) {
+ t.Errorf("ReadLine didn't return everything: got: %d, want: %d (stride: %d)", done, len(testOutput), stride)
+ }
+ }
+}
+
+func TestReadLine(t *testing.T) {
+ testReadLine(t, testInput)
+ testReadLine(t, testInputrn)
+}
+
+func TestLineTooLong(t *testing.T) {
+ buf := bytes.NewBuffer([]byte("aaabbbcc\n"))
+ l, _ := NewReaderSize(buf, 3)
+ line, isPrefix, err := l.ReadLine()
+ if !isPrefix || !bytes.Equal(line, []byte("aaa")) || err != nil {
+ t.Errorf("bad result for first line: %x %s", line, err)
+ }
+ line, isPrefix, err = l.ReadLine()
+ if !isPrefix || !bytes.Equal(line, []byte("bbb")) || err != nil {
+ t.Errorf("bad result for second line: %x", line)
+ }
+ line, isPrefix, err = l.ReadLine()
+ if isPrefix || !bytes.Equal(line, []byte("cc")) || err != nil {
+ t.Errorf("bad result for third line: %x", line)
+ }
+ line, isPrefix, err = l.ReadLine()
+ if isPrefix || err == nil {
+ t.Errorf("expected no more lines: %x %s", line, err)
+ }
+}
+
+func TestReadAfterLines(t *testing.T) {
+ line1 := "line1"
+ restData := "line2\nline 3\n"
+ inbuf := bytes.NewBuffer([]byte(line1 + "\n" + restData))
+ outbuf := new(bytes.Buffer)
+ maxLineLength := len(line1) + len(restData)/2
+ l, _ := NewReaderSize(inbuf, maxLineLength)
+ line, isPrefix, err := l.ReadLine()
+ if isPrefix || err != nil || string(line) != line1 {
+ t.Errorf("bad result for first line: isPrefix=%v err=%v line=%q", isPrefix, err, string(line))
+ }
+ n, err := io.Copy(outbuf, l)
+ if int(n) != len(restData) || err != nil {
+ t.Errorf("bad result for Read: n=%d err=%v", n, err)
+ }
+ if outbuf.String() != restData {
+ t.Errorf("bad result for Read: got %q; expected %q", outbuf.String(), restData)
+ }
+}
+
+func TestReadEmptyBuffer(t *testing.T) {
+ l, _ := NewReaderSize(bytes.NewBuffer(nil), 10)
+ line, isPrefix, err := l.ReadLine()
+ if err != os.EOF {
+ t.Errorf("expected EOF from ReadLine, got '%s' %t %s", line, isPrefix, err)
+ }
+}
+
+func TestLinesAfterRead(t *testing.T) {
+ l, _ := NewReaderSize(bytes.NewBuffer([]byte("foo")), 10)
+ _, err := ioutil.ReadAll(l)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ line, isPrefix, err := l.ReadLine()
+ if err != os.EOF {
+ t.Errorf("expected EOF from ReadLine, got '%s' %t %s", line, isPrefix, err)
+ }
+}
diff --git a/src/pkg/bytes/buffer_test.go b/src/pkg/bytes/buffer_test.go
index 56a2d9275..14f950141 100644
--- a/src/pkg/bytes/buffer_test.go
+++ b/src/pkg/bytes/buffer_test.go
@@ -178,7 +178,11 @@ func TestBasicOperations(t *testing.T) {
func TestLargeStringWrites(t *testing.T) {
var buf Buffer
- for i := 3; i < 30; i += 3 {
+ limit := 30
+ if testing.Short() {
+ limit = 9
+ }
+ for i := 3; i < limit; i += 3 {
s := fillString(t, "TestLargeWrites (1)", &buf, "", 5, data)
empty(t, "TestLargeStringWrites (2)", &buf, s, make([]byte, len(data)/i))
}
@@ -188,7 +192,11 @@ func TestLargeStringWrites(t *testing.T) {
func TestLargeByteWrites(t *testing.T) {
var buf Buffer
- for i := 3; i < 30; i += 3 {
+ limit := 30
+ if testing.Short() {
+ limit = 9
+ }
+ for i := 3; i < limit; i += 3 {
s := fillBytes(t, "TestLargeWrites (1)", &buf, "", 5, bytes)
empty(t, "TestLargeByteWrites (2)", &buf, s, make([]byte, len(data)/i))
}
diff --git a/src/pkg/bytes/bytes.go b/src/pkg/bytes/bytes.go
index bfe2ef39d..c12a13573 100644
--- a/src/pkg/bytes/bytes.go
+++ b/src/pkg/bytes/bytes.go
@@ -293,20 +293,10 @@ func Join(a [][]byte, sep []byte) []byte {
}
b := make([]byte, n)
- bp := 0
- for i := 0; i < len(a); i++ {
- s := a[i]
- for j := 0; j < len(s); j++ {
- b[bp] = s[j]
- bp++
- }
- if i+1 < len(a) {
- s = sep
- for j := 0; j < len(s); j++ {
- b[bp] = s[j]
- bp++
- }
- }
+ bp := copy(b, a[0])
+ for _, s := range a[1:] {
+ bp += copy(b[bp:], sep)
+ bp += copy(b[bp:], s)
}
return b
}
diff --git a/src/pkg/bytes/bytes_test.go b/src/pkg/bytes/bytes_test.go
index 063686ec5..4ce291a4f 100644
--- a/src/pkg/bytes/bytes_test.go
+++ b/src/pkg/bytes/bytes_test.go
@@ -201,7 +201,10 @@ func TestIndexByte(t *testing.T) {
// test a larger buffer with different sizes and alignments
func TestIndexByteBig(t *testing.T) {
- const n = 1024
+ var n = 1024
+ if testing.Short() {
+ n = 128
+ }
b := make([]byte, n)
for i := 0; i < n; i++ {
// different start alignments
diff --git a/src/pkg/compress/lzw/reader_test.go b/src/pkg/compress/lzw/reader_test.go
index 7795a4c14..4b5dfaade 100644
--- a/src/pkg/compress/lzw/reader_test.go
+++ b/src/pkg/compress/lzw/reader_test.go
@@ -9,6 +9,7 @@ import (
"io"
"io/ioutil"
"os"
+ "runtime"
"strconv"
"strings"
"testing"
@@ -117,16 +118,34 @@ func (devNull) Write(p []byte) (int, os.Error) {
return len(p), nil
}
-func BenchmarkDecoder(b *testing.B) {
+func benchmarkDecoder(b *testing.B, n int) {
b.StopTimer()
+ b.SetBytes(int64(n))
buf0, _ := ioutil.ReadFile("../testdata/e.txt")
+ buf0 = buf0[:10000]
compressed := bytes.NewBuffer(nil)
w := NewWriter(compressed, LSB, 8)
- io.Copy(w, bytes.NewBuffer(buf0))
+ for i := 0; i < n; i += len(buf0) {
+ io.Copy(w, bytes.NewBuffer(buf0))
+ }
w.Close()
buf1 := compressed.Bytes()
+ buf0, compressed, w = nil, nil, nil
+ runtime.GC()
b.StartTimer()
for i := 0; i < b.N; i++ {
io.Copy(devNull{}, NewReader(bytes.NewBuffer(buf1), LSB, 8))
}
}
+
+func BenchmarkDecoder1e4(b *testing.B) {
+ benchmarkDecoder(b, 1e4)
+}
+
+func BenchmarkDecoder1e5(b *testing.B) {
+ benchmarkDecoder(b, 1e5)
+}
+
+func BenchmarkDecoder1e6(b *testing.B) {
+ benchmarkDecoder(b, 1e6)
+}
diff --git a/src/pkg/compress/lzw/writer_test.go b/src/pkg/compress/lzw/writer_test.go
index 715b974aa..e5815a03d 100644
--- a/src/pkg/compress/lzw/writer_test.go
+++ b/src/pkg/compress/lzw/writer_test.go
@@ -8,6 +8,7 @@ import (
"io"
"io/ioutil"
"os"
+ "runtime"
"testing"
)
@@ -20,7 +21,7 @@ var filenames = []string{
// the given options yields equivalent bytes to the original file.
func testFile(t *testing.T, fn string, order Order, litWidth int) {
// Read the file, as golden output.
- golden, err := os.Open(fn, os.O_RDONLY, 0400)
+ golden, err := os.Open(fn)
if err != nil {
t.Errorf("%s (order=%d litWidth=%d): %v", fn, order, litWidth, err)
return
@@ -28,7 +29,7 @@ func testFile(t *testing.T, fn string, order Order, litWidth int) {
defer golden.Close()
// Read the file again, and push it through a pipe that compresses at the write end, and decompresses at the read end.
- raw, err := os.Open(fn, os.O_RDONLY, 0400)
+ raw, err := os.Open(fn)
if err != nil {
t.Errorf("%s (order=%d litWidth=%d): %v", fn, order, litWidth, err)
return
@@ -99,13 +100,33 @@ func TestWriter(t *testing.T) {
}
}
-func BenchmarkEncoder(b *testing.B) {
+func benchmarkEncoder(b *testing.B, n int) {
b.StopTimer()
- buf, _ := ioutil.ReadFile("../testdata/e.txt")
+ b.SetBytes(int64(n))
+ buf0, _ := ioutil.ReadFile("../testdata/e.txt")
+ buf0 = buf0[:10000]
+ buf1 := make([]byte, n)
+ for i := 0; i < n; i += len(buf0) {
+ copy(buf1[i:], buf0)
+ }
+ buf0 = nil
+ runtime.GC()
b.StartTimer()
for i := 0; i < b.N; i++ {
w := NewWriter(devNull{}, LSB, 8)
- w.Write(buf)
+ w.Write(buf1)
w.Close()
}
}
+
+func BenchmarkEncoder1e4(b *testing.B) {
+ benchmarkEncoder(b, 1e4)
+}
+
+func BenchmarkEncoder1e5(b *testing.B) {
+ benchmarkEncoder(b, 1e5)
+}
+
+func BenchmarkEncoder1e6(b *testing.B) {
+ benchmarkEncoder(b, 1e6)
+}
diff --git a/src/pkg/compress/zlib/writer_test.go b/src/pkg/compress/zlib/writer_test.go
index 5a13ba825..7eb1cd494 100644
--- a/src/pkg/compress/zlib/writer_test.go
+++ b/src/pkg/compress/zlib/writer_test.go
@@ -20,7 +20,7 @@ var filenames = []string{
// yields equivalent bytes to the original file.
func testFileLevel(t *testing.T, fn string, level int) {
// Read the file, as golden output.
- golden, err := os.Open(fn, os.O_RDONLY, 0444)
+ golden, err := os.Open(fn)
if err != nil {
t.Errorf("%s (level=%d): %v", fn, level, err)
return
@@ -28,7 +28,7 @@ func testFileLevel(t *testing.T, fn string, level int) {
defer golden.Close()
// Read the file again, and push it through a pipe that compresses at the write end, and decompresses at the read end.
- raw, err := os.Open(fn, os.O_RDONLY, 0444)
+ raw, err := os.Open(fn)
if err != nil {
t.Errorf("%s (level=%d): %v", fn, level, err)
return
diff --git a/src/pkg/container/vector/numbers_test.go b/src/pkg/container/vector/numbers_test.go
index d540ace05..b83b0bfee 100644
--- a/src/pkg/container/vector/numbers_test.go
+++ b/src/pkg/container/vector/numbers_test.go
@@ -33,6 +33,9 @@ func s(n uint64) string {
func TestVectorNums(t *testing.T) {
+ if testing.Short() {
+ return
+ }
var v Vector
c := int(0)
runtime.GC()
@@ -51,6 +54,9 @@ func TestVectorNums(t *testing.T) {
func TestIntVectorNums(t *testing.T) {
+ if testing.Short() {
+ return
+ }
var v IntVector
c := int(0)
runtime.GC()
@@ -69,6 +75,9 @@ func TestIntVectorNums(t *testing.T) {
func TestStringVectorNums(t *testing.T) {
+ if testing.Short() {
+ return
+ }
var v StringVector
c := ""
runtime.GC()
diff --git a/src/pkg/crypto/block/cbc.go b/src/pkg/crypto/block/cbc.go
deleted file mode 100644
index 23229c09f..000000000
--- a/src/pkg/crypto/block/cbc.go
+++ /dev/null
@@ -1,71 +0,0 @@
-// 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.
-
-// Cipher block chaining (CBC) mode.
-
-// CBC provides confidentiality by xoring (chaining) each plaintext block
-// with the previous ciphertext block before applying the block cipher.
-
-// See NIST SP 800-38A, pp 10-11
-
-package block
-
-import (
- "io"
-)
-
-type cbcCipher struct {
- c Cipher
- blockSize int
- iv []byte
- tmp []byte
-}
-
-func newCBC(c Cipher, iv []byte) *cbcCipher {
- n := c.BlockSize()
- x := new(cbcCipher)
- x.c = c
- x.blockSize = n
- x.iv = dup(iv)
- x.tmp = make([]byte, n)
- return x
-}
-
-func (x *cbcCipher) BlockSize() int { return x.blockSize }
-
-func (x *cbcCipher) Encrypt(dst, src []byte) {
- for i := 0; i < x.blockSize; i++ {
- x.iv[i] ^= src[i]
- }
- x.c.Encrypt(x.iv, x.iv)
- for i := 0; i < x.blockSize; i++ {
- dst[i] = x.iv[i]
- }
-}
-
-func (x *cbcCipher) Decrypt(dst, src []byte) {
- x.c.Decrypt(x.tmp, src)
- for i := 0; i < x.blockSize; i++ {
- x.tmp[i] ^= x.iv[i]
- x.iv[i] = src[i]
- dst[i] = x.tmp[i]
- }
-}
-
-// NewCBCDecrypter returns a reader that reads data from r and decrypts it using c
-// in cipher block chaining (CBC) mode with the initialization vector iv.
-// The returned Reader does not buffer or read ahead except
-// as required by the cipher's block size.
-func NewCBCDecrypter(c Cipher, iv []byte, r io.Reader) io.Reader {
- return NewECBDecrypter(newCBC(c, iv), r)
-}
-
-// NewCBCEncrypter returns a writer that encrypts data using c
-// in cipher block chaining (CBC) mode with the initialization vector iv
-// and writes the encrypted data to w.
-// The returned Writer does no buffering except as required
-// by the cipher's block size, so there is no need for a Flush method.
-func NewCBCEncrypter(c Cipher, iv []byte, w io.Writer) io.Writer {
- return NewECBEncrypter(newCBC(c, iv), w)
-}
diff --git a/src/pkg/crypto/block/cfb.go b/src/pkg/crypto/block/cfb.go
deleted file mode 100644
index f20c0a04f..000000000
--- a/src/pkg/crypto/block/cfb.go
+++ /dev/null
@@ -1,96 +0,0 @@
-// 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.
-
-// Cipher feedback (CFB) mode.
-
-// CFB provides confidentiality by feeding a fraction of
-// the previous ciphertext in as the plaintext for the next
-// block operation.
-
-// See NIST SP 800-38A, pp 11-13
-
-package block
-
-import (
- "io"
-)
-
-type cfbCipher struct {
- c Cipher
- blockSize int // our block size (s/8)
- cipherSize int // underlying cipher block size
- iv []byte
- tmp []byte
-}
-
-func newCFB(c Cipher, s int, iv []byte) *cfbCipher {
- if s == 0 || s%8 != 0 {
- panic("crypto/block: invalid CFB mode")
- }
- b := c.BlockSize()
- x := new(cfbCipher)
- x.c = c
- x.blockSize = s / 8
- x.cipherSize = b
- x.iv = dup(iv)
- x.tmp = make([]byte, b)
- return x
-}
-
-func (x *cfbCipher) BlockSize() int { return x.blockSize }
-
-func (x *cfbCipher) Encrypt(dst, src []byte) {
- // Encrypt old IV and xor prefix with src to make dst.
- x.c.Encrypt(x.tmp, x.iv)
- for i := 0; i < x.blockSize; i++ {
- dst[i] = src[i] ^ x.tmp[i]
- }
-
- // Slide unused IV pieces down and insert dst at end.
- for i := 0; i < x.cipherSize-x.blockSize; i++ {
- x.iv[i] = x.iv[i+x.blockSize]
- }
- off := x.cipherSize - x.blockSize
- for i := off; i < x.cipherSize; i++ {
- x.iv[i] = dst[i-off]
- }
-}
-
-func (x *cfbCipher) Decrypt(dst, src []byte) {
- // Encrypt [sic] old IV and xor prefix with src to make dst.
- x.c.Encrypt(x.tmp, x.iv)
- for i := 0; i < x.blockSize; i++ {
- dst[i] = src[i] ^ x.tmp[i]
- }
-
- // Slide unused IV pieces down and insert src at top.
- for i := 0; i < x.cipherSize-x.blockSize; i++ {
- x.iv[i] = x.iv[i+x.blockSize]
- }
- off := x.cipherSize - x.blockSize
- for i := off; i < x.cipherSize; i++ {
- // Reconstruct src = dst ^ x.tmp
- // in case we overwrote src (src == dst).
- x.iv[i] = dst[i-off] ^ x.tmp[i-off]
- }
-}
-
-// NewCFBDecrypter returns a reader that reads data from r and decrypts it using c
-// in s-bit cipher feedback (CFB) mode with the initialization vector iv.
-// The returned Reader does not buffer or read ahead except
-// as required by the cipher's block size.
-// Modes for s not a multiple of 8 are unimplemented.
-func NewCFBDecrypter(c Cipher, s int, iv []byte, r io.Reader) io.Reader {
- return NewECBDecrypter(newCFB(c, s, iv), r)
-}
-
-// NewCFBEncrypter returns a writer that encrypts data using c
-// in s-bit cipher feedback (CFB) mode with the initialization vector iv
-// and writes the encrypted data to w.
-// The returned Writer does no buffering except as required
-// by the cipher's block size, so there is no need for a Flush method.
-// Modes for s not a multiple of 8 are unimplemented.
-func NewCFBEncrypter(c Cipher, s int, iv []byte, w io.Writer) io.Writer {
- return NewECBEncrypter(newCFB(c, s, iv), w)
-}
diff --git a/src/pkg/crypto/block/cfb_aes_test.go b/src/pkg/crypto/block/cfb_aes_test.go
deleted file mode 100644
index e400c182a..000000000
--- a/src/pkg/crypto/block/cfb_aes_test.go
+++ /dev/null
@@ -1,311 +0,0 @@
-// 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.
-
-// CFB AES test vectors.
-
-// See U.S. National Institute of Standards and Technology (NIST)
-// Special Publication 800-38A, ``Recommendation for Block Cipher
-// Modes of Operation,'' 2001 Edition, pp. 29-52.
-
-package block
-
-import (
- "bytes"
- "crypto/aes"
- "io"
- "testing"
-)
-
-type cfbTest struct {
- name string
- s int
- key []byte
- iv []byte
- in []byte
- out []byte
-}
-
-var cfbAESTests = []cfbTest{
- {
- "CFB1-AES128",
- 1,
- commonKey128,
- commonIV,
- []byte{
- 0<<7 | 1<<6 | 1<<5 | 0<<4 | 1<<3 | 0<<2 | 1<<1,
- 1<<7 | 1<<6 | 0<<5 | 0<<4 | 0<<3 | 0<<2 | 0<<1,
- },
- []byte{
- 0<<7 | 1<<6 | 1<<5 | 0<<4 | 1<<3 | 0<<2 | 0<<1,
- 1<<7 | 0<<6 | 1<<5 | 1<<4 | 0<<3 | 0<<2 | 1<<1,
- },
- },
- {
- "CFB1-AES192",
- 1,
- commonKey192,
- commonIV,
- []byte{
- 0<<7 | 1<<6 | 1<<5 | 0<<4 | 1<<3 | 0<<2 | 1<<1,
- 1<<7 | 1<<6 | 0<<5 | 0<<4 | 0<<3 | 0<<2 | 0<<1,
- },
- []byte{
- 1<<7 | 0<<6 | 0<<5 | 1<<4 | 0<<3 | 0<<2 | 1<<1,
- 0<<7 | 1<<6 | 0<<5 | 1<<4 | 1<<3 | 0<<2 | 0<<1,
- },
- },
- {
- "CFB1-AES256",
- 1,
- commonKey256,
- commonIV,
- []byte{
- 0<<7 | 1<<6 | 1<<5 | 0<<4 | 1<<3 | 0<<2 | 1<<1,
- 1<<7 | 1<<6 | 0<<5 | 0<<4 | 0<<3 | 0<<2 | 0<<1,
- },
- []byte{
- 1<<7 | 0<<6 | 0<<5 | 1<<4 | 0<<3 | 0<<2 | 0<<1,
- 0<<7 | 0<<6 | 1<<5 | 0<<4 | 1<<3 | 0<<2 | 0<<1,
- },
- },
-
- {
- "CFB8-AES128",
- 8,
- commonKey128,
- commonIV,
- []byte{
- 0x6b,
- 0xc1,
- 0xbe,
- 0xe2,
- 0x2e,
- 0x40,
- 0x9f,
- 0x96,
- 0xe9,
- 0x3d,
- 0x7e,
- 0x11,
- 0x73,
- 0x93,
- 0x17,
- 0x2a,
- 0xae,
- 0x2d,
- },
- []byte{
- 0x3b,
- 0x79,
- 0x42,
- 0x4c,
- 0x9c,
- 0x0d,
- 0xd4,
- 0x36,
- 0xba,
- 0xce,
- 0x9e,
- 0x0e,
- 0xd4,
- 0x58,
- 0x6a,
- 0x4f,
- 0x32,
- 0xb9,
- },
- },
-
- {
- "CFB8-AES192",
- 8,
- commonKey192,
- commonIV,
- []byte{
- 0x6b,
- 0xc1,
- 0xbe,
- 0xe2,
- 0x2e,
- 0x40,
- 0x9f,
- 0x96,
- 0xe9,
- 0x3d,
- 0x7e,
- 0x11,
- 0x73,
- 0x93,
- 0x17,
- 0x2a,
- 0xae,
- 0x2d,
- },
- []byte{
- 0xcd,
- 0xa2,
- 0x52,
- 0x1e,
- 0xf0,
- 0xa9,
- 0x05,
- 0xca,
- 0x44,
- 0xcd,
- 0x05,
- 0x7c,
- 0xbf,
- 0x0d,
- 0x47,
- 0xa0,
- 0x67,
- 0x8a,
- },
- },
-
- {
- "CFB8-AES256",
- 8,
- commonKey256,
- commonIV,
- []byte{
- 0x6b,
- 0xc1,
- 0xbe,
- 0xe2,
- 0x2e,
- 0x40,
- 0x9f,
- 0x96,
- 0xe9,
- 0x3d,
- 0x7e,
- 0x11,
- 0x73,
- 0x93,
- 0x17,
- 0x2a,
- 0xae,
- 0x2d,
- },
- []byte{
- 0xdc,
- 0x1f,
- 0x1a,
- 0x85,
- 0x20,
- 0xa6,
- 0x4d,
- 0xb5,
- 0x5f,
- 0xcc,
- 0x8a,
- 0xc5,
- 0x54,
- 0x84,
- 0x4e,
- 0x88,
- 0x97,
- 0x00,
- },
- },
-
- {
- "CFB128-AES128",
- 128,
- commonKey128,
- commonIV,
- []byte{
- 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
- 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
- 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
- 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10,
- },
- []byte{
- 0x3b, 0x3f, 0xd9, 0x2e, 0xb7, 0x2d, 0xad, 0x20, 0x33, 0x34, 0x49, 0xf8, 0xe8, 0x3c, 0xfb, 0x4a,
- 0xc8, 0xa6, 0x45, 0x37, 0xa0, 0xb3, 0xa9, 0x3f, 0xcd, 0xe3, 0xcd, 0xad, 0x9f, 0x1c, 0xe5, 0x8b,
- 0x26, 0x75, 0x1f, 0x67, 0xa3, 0xcb, 0xb1, 0x40, 0xb1, 0x80, 0x8c, 0xf1, 0x87, 0xa4, 0xf4, 0xdf,
- 0xc0, 0x4b, 0x05, 0x35, 0x7c, 0x5d, 0x1c, 0x0e, 0xea, 0xc4, 0xc6, 0x6f, 0x9f, 0xf7, 0xf2, 0xe6,
- },
- },
-
- {
- "CFB128-AES192",
- 128,
- commonKey192,
- commonIV,
- []byte{
- 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
- 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
- 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
- 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10,
- },
- []byte{
- 0xcd, 0xc8, 0x0d, 0x6f, 0xdd, 0xf1, 0x8c, 0xab, 0x34, 0xc2, 0x59, 0x09, 0xc9, 0x9a, 0x41, 0x74,
- 0x67, 0xce, 0x7f, 0x7f, 0x81, 0x17, 0x36, 0x21, 0x96, 0x1a, 0x2b, 0x70, 0x17, 0x1d, 0x3d, 0x7a,
- 0x2e, 0x1e, 0x8a, 0x1d, 0xd5, 0x9b, 0x88, 0xb1, 0xc8, 0xe6, 0x0f, 0xed, 0x1e, 0xfa, 0xc4, 0xc9,
- 0xc0, 0x5f, 0x9f, 0x9c, 0xa9, 0x83, 0x4f, 0xa0, 0x42, 0xae, 0x8f, 0xba, 0x58, 0x4b, 0x09, 0xff,
- },
- },
-
- {
- "CFB128-AES256",
- 128,
- commonKey256,
- commonIV,
- []byte{
- 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
- 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
- 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
- 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10,
- },
- []byte{
- 0xdc, 0x7e, 0x84, 0xbf, 0xda, 0x79, 0x16, 0x4b, 0x7e, 0xcd, 0x84, 0x86, 0x98, 0x5d, 0x38, 0x60,
- 0x39, 0xff, 0xed, 0x14, 0x3b, 0x28, 0xb1, 0xc8, 0x32, 0x11, 0x3c, 0x63, 0x31, 0xe5, 0x40, 0x7b,
- 0xdf, 0x10, 0x13, 0x24, 0x15, 0xe5, 0x4b, 0x92, 0xa1, 0x3e, 0xd0, 0xa8, 0x26, 0x7a, 0xe2, 0xf9,
- 0x75, 0xa3, 0x85, 0x74, 0x1a, 0xb9, 0xce, 0xf8, 0x20, 0x31, 0x62, 0x3d, 0x55, 0xb1, 0xe4, 0x71,
- },
- },
-}
-
-func TestCFB_AES(t *testing.T) {
- for _, tt := range cfbAESTests {
- test := tt.name
-
- if tt.s == 1 {
- // 1-bit CFB not implemented
- continue
- }
-
- c, err := aes.NewCipher(tt.key)
- if err != nil {
- t.Errorf("%s: NewCipher(%d bytes) = %s", test, len(tt.key), err)
- continue
- }
-
- var crypt bytes.Buffer
- w := NewCFBEncrypter(c, tt.s, tt.iv, &crypt)
- var r io.Reader = bytes.NewBuffer(tt.in)
- n, err := io.Copy(w, r)
- if n != int64(len(tt.in)) || err != nil {
- t.Errorf("%s: CFBEncrypter io.Copy = %d, %v want %d, nil", test, n, err, len(tt.in))
- } else if d := crypt.Bytes(); !same(tt.out, d) {
- t.Errorf("%s: CFBEncrypter\nhave %x\nwant %x", test, d, tt.out)
- }
-
- var plain bytes.Buffer
- r = NewCFBDecrypter(c, tt.s, tt.iv, bytes.NewBuffer(tt.out))
- w = &plain
- n, err = io.Copy(w, r)
- if n != int64(len(tt.out)) || err != nil {
- t.Errorf("%s: CFBDecrypter io.Copy = %d, %v want %d, nil", test, n, err, len(tt.out))
- } else if d := plain.Bytes(); !same(tt.in, d) {
- t.Errorf("%s: CFBDecrypter\nhave %x\nwant %x", test, d, tt.in)
- }
-
- if t.Failed() {
- break
- }
- }
-}
diff --git a/src/pkg/crypto/block/cipher.go b/src/pkg/crypto/block/cipher.go
deleted file mode 100644
index e1099e9a1..000000000
--- a/src/pkg/crypto/block/cipher.go
+++ /dev/null
@@ -1,57 +0,0 @@
-// 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 block package is deprecated, use cipher instead.
-// The block package implements standard block cipher modes
-// that can be wrapped around low-level block cipher implementations.
-// See http://csrc.nist.gov/groups/ST/toolkit/BCM/current_modes.html
-// and NIST Special Publication 800-38A.
-package block
-
-// A Cipher represents an implementation of block cipher
-// using a given key. It provides the capability to encrypt
-// or decrypt individual blocks. The mode implementations
-// extend that capability to streams of blocks.
-type Cipher interface {
- // BlockSize returns the cipher's block size.
- BlockSize() int
-
- // Encrypt encrypts the first block in src into dst.
- // Src and dst may point at the same memory.
- Encrypt(dst, src []byte)
-
- // Decrypt decrypts the first block in src into dst.
- // Src and dst may point at the same memory.
- Decrypt(dst, src []byte)
-}
-
-// Utility routines
-
-func shift1(dst, src []byte) byte {
- var b byte
- for i := len(src) - 1; i >= 0; i-- {
- bb := src[i] >> 7
- dst[i] = src[i]<<1 | b
- b = bb
- }
- return b
-}
-
-func same(p, q []byte) bool {
- if len(p) != len(q) {
- return false
- }
- for i := 0; i < len(p); i++ {
- if p[i] != q[i] {
- return false
- }
- }
- return true
-}
-
-func dup(p []byte) []byte {
- q := make([]byte, len(p))
- copy(q, p)
- return q
-}
diff --git a/src/pkg/crypto/block/cmac.go b/src/pkg/crypto/block/cmac.go
deleted file mode 100644
index b85cde72e..000000000
--- a/src/pkg/crypto/block/cmac.go
+++ /dev/null
@@ -1,105 +0,0 @@
-// 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.
-
-// CMAC message authentication code, defined in
-// NIST Special Publication SP 800-38B.
-
-package block
-
-import (
- "hash"
- "os"
-)
-
-const (
- // minimal irreducible polynomial of degree b
- r64 = 0x1b
- r128 = 0x87
-)
-
-type cmac struct {
- k1, k2, ci, digest []byte
- p int // position in ci
- c Cipher
-}
-
-// TODO(rsc): Should this return an error instead of panic?
-
-// NewCMAC returns a new instance of a CMAC message authentication code
-// digest using the given Cipher.
-func NewCMAC(c Cipher) hash.Hash {
- var r byte
- n := c.BlockSize()
- switch n {
- case 64 / 8:
- r = r64
- case 128 / 8:
- r = r128
- default:
- panic("crypto/block: NewCMAC: invalid cipher block size")
- }
-
- d := new(cmac)
- d.c = c
- d.k1 = make([]byte, n)
- d.k2 = make([]byte, n)
- d.ci = make([]byte, n)
- d.digest = make([]byte, n)
-
- // Subkey generation, p. 7
- c.Encrypt(d.k1, d.k1)
- if shift1(d.k1, d.k1) != 0 {
- d.k1[n-1] ^= r
- }
- if shift1(d.k2, d.k1) != 0 {
- d.k2[n-1] ^= r
- }
-
- return d
-}
-
-// Reset clears the digest state, starting a new digest.
-func (d *cmac) Reset() {
- for i := range d.ci {
- d.ci[i] = 0
- }
- d.p = 0
-}
-
-// Write adds the given data to the digest state.
-func (d *cmac) Write(p []byte) (n int, err os.Error) {
- // Xor input into ci.
- for _, c := range p {
- // If ci is full, encrypt and start over.
- if d.p >= len(d.ci) {
- d.c.Encrypt(d.ci, d.ci)
- d.p = 0
- }
- d.ci[d.p] ^= c
- d.p++
- }
- return len(p), nil
-}
-
-// Sum returns the CMAC digest, one cipher block in length,
-// of the data written with Write.
-func (d *cmac) Sum() []byte {
- // Finish last block, mix in key, encrypt.
- // Don't edit ci, in case caller wants
- // to keep digesting after call to Sum.
- k := d.k1
- if d.p < len(d.digest) {
- k = d.k2
- }
- for i := 0; i < len(d.ci); i++ {
- d.digest[i] = d.ci[i] ^ k[i]
- }
- if d.p < len(d.digest) {
- d.digest[d.p] ^= 0x80
- }
- d.c.Encrypt(d.digest, d.digest)
- return d.digest
-}
-
-func (d *cmac) Size() int { return len(d.digest) }
diff --git a/src/pkg/crypto/block/cmac_aes_test.go b/src/pkg/crypto/block/cmac_aes_test.go
deleted file mode 100644
index 0a4a1a418..000000000
--- a/src/pkg/crypto/block/cmac_aes_test.go
+++ /dev/null
@@ -1,130 +0,0 @@
-// 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.
-
-// CMAC test vectors. See NIST SP 800-38B, Appendix D.
-
-package block
-
-import (
- "crypto/aes"
- "testing"
-)
-
-type cmacAESTest struct {
- key []byte
- in []byte
- digest []byte
-}
-
-var cmacAESTests = []cmacAESTest{
- {
- commonKey128,
- nil,
- []byte{0xbb, 0x1d, 0x69, 0x29, 0xe9, 0x59, 0x37, 0x28, 0x7f, 0xa3, 0x7d, 0x12, 0x9b, 0x75, 0x67, 0x46},
- },
- {
- commonKey128,
- []byte{0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a},
- []byte{0x07, 0x0a, 0x16, 0xb4, 0x6b, 0x4d, 0x41, 0x44, 0xf7, 0x9b, 0xdd, 0x9d, 0xd0, 0x4a, 0x28, 0x7c},
- },
- {
- commonKey128,
- []byte{
- 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
- 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
- 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
- },
- []byte{0xdf, 0xa6, 0x67, 0x47, 0xde, 0x9a, 0xe6, 0x30, 0x30, 0xca, 0x32, 0x61, 0x14, 0x97, 0xc8, 0x27},
- },
- {
- commonKey128,
- []byte{
- 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
- 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
- 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
- 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10,
- },
- []byte{0x51, 0xf0, 0xbe, 0xbf, 0x7e, 0x3b, 0x9d, 0x92, 0xfc, 0x49, 0x74, 0x17, 0x79, 0x36, 0x3c, 0xfe},
- },
- {
- commonKey192,
- nil,
- []byte{0xd1, 0x7d, 0xdf, 0x46, 0xad, 0xaa, 0xcd, 0xe5, 0x31, 0xca, 0xc4, 0x83, 0xde, 0x7a, 0x93, 0x67},
- },
- {
- commonKey192,
- []byte{0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a},
- []byte{0x9e, 0x99, 0xa7, 0xbf, 0x31, 0xe7, 0x10, 0x90, 0x06, 0x62, 0xf6, 0x5e, 0x61, 0x7c, 0x51, 0x84},
- },
- {
- commonKey192,
- []byte{
- 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
- 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
- 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
- },
- []byte{0x8a, 0x1d, 0xe5, 0xbe, 0x2e, 0xb3, 0x1a, 0xad, 0x08, 0x9a, 0x82, 0xe6, 0xee, 0x90, 0x8b, 0x0e},
- },
- {
- commonKey192,
- []byte{
- 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
- 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
- 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
- 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10,
- },
- []byte{0xa1, 0xd5, 0xdf, 0x0e, 0xed, 0x79, 0x0f, 0x79, 0x4d, 0x77, 0x58, 0x96, 0x59, 0xf3, 0x9a, 0x11},
- },
- {
- commonKey256,
- nil,
- []byte{0x02, 0x89, 0x62, 0xf6, 0x1b, 0x7b, 0xf8, 0x9e, 0xfc, 0x6b, 0x55, 0x1f, 0x46, 0x67, 0xd9, 0x83},
- },
- {
- commonKey256,
- []byte{0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a},
- []byte{0x28, 0xa7, 0x02, 0x3f, 0x45, 0x2e, 0x8f, 0x82, 0xbd, 0x4b, 0xf2, 0x8d, 0x8c, 0x37, 0xc3, 0x5c},
- },
- {
- commonKey256,
- []byte{
- 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
- 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
- 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
- },
- []byte{0xaa, 0xf3, 0xd8, 0xf1, 0xde, 0x56, 0x40, 0xc2, 0x32, 0xf5, 0xb1, 0x69, 0xb9, 0xc9, 0x11, 0xe6},
- },
- {
- commonKey256,
- []byte{
- 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
- 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
- 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
- 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10,
- },
- []byte{0xe1, 0x99, 0x21, 0x90, 0x54, 0x9f, 0x6e, 0xd5, 0x69, 0x6a, 0x2c, 0x05, 0x6c, 0x31, 0x54, 0x10},
- },
-}
-
-func TestCMAC_AES(t *testing.T) {
- for i, tt := range cmacAESTests {
- c, err := aes.NewCipher(tt.key)
- if err != nil {
- t.Errorf("test %d: NewCipher: %s", i, err)
- continue
- }
- d := NewCMAC(c)
- n, err := d.Write(tt.in)
- if err != nil || n != len(tt.in) {
- t.Errorf("test %d: Write %d: %d, %s", i, len(tt.in), n, err)
- continue
- }
- sum := d.Sum()
- if !same(sum, tt.digest) {
- x := d.(*cmac)
- t.Errorf("test %d: digest mismatch\n\twant %x\n\thave %x\n\tk1 %x\n\tk2 %x", i, tt.digest, sum, x.k1, x.k2)
- continue
- }
- }
-}
diff --git a/src/pkg/crypto/block/ctr.go b/src/pkg/crypto/block/ctr.go
deleted file mode 100644
index 5d65c0c9a..000000000
--- a/src/pkg/crypto/block/ctr.go
+++ /dev/null
@@ -1,67 +0,0 @@
-// 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.
-
-// Counter (CTR) mode.
-
-// CTR converts a block cipher into a stream cipher by
-// repeatedly encrypting an incrementing counter and
-// xoring the resulting stream of data with the input.
-
-// See NIST SP 800-38A, pp 13-15
-
-package block
-
-import (
- "io"
-)
-
-type ctrStream struct {
- c Cipher
- ctr []byte
- out []byte
-}
-
-func newCTRStream(c Cipher, ctr []byte) *ctrStream {
- x := new(ctrStream)
- x.c = c
- x.ctr = dup(ctr)
- x.out = make([]byte, len(ctr))
- return x
-}
-
-func (x *ctrStream) Next() []byte {
- // Next block is encryption of counter.
- x.c.Encrypt(x.out, x.ctr)
-
- // Increment counter
- for i := len(x.ctr) - 1; i >= 0; i-- {
- x.ctr[i]++
- if x.ctr[i] != 0 {
- break
- }
- }
-
- return x.out
-}
-
-// NewCTRReader returns a reader that reads data from r, decrypts (or encrypts)
-// it using c in counter (CTR) mode with the initialization vector iv.
-// The returned Reader does not buffer and has no block size.
-// In CTR mode, encryption and decryption are the same operation:
-// a CTR reader applied to an encrypted stream produces a decrypted
-// stream and vice versa.
-func NewCTRReader(c Cipher, iv []byte, r io.Reader) io.Reader {
- return newXorReader(newCTRStream(c, iv), r)
-}
-
-// NewCTRWriter returns a writer that encrypts (or decrypts) data using c
-// in counter (CTR) mode with the initialization vector iv
-// and writes the encrypted data to w.
-// The returned Writer does not buffer and has no block size.
-// In CTR mode, encryption and decryption are the same operation:
-// a CTR writer applied to an decrypted stream produces an encrypted
-// stream and vice versa.
-func NewCTRWriter(c Cipher, iv []byte, w io.Writer) io.Writer {
- return newXorWriter(newCTRStream(c, iv), w)
-}
diff --git a/src/pkg/crypto/block/eax.go b/src/pkg/crypto/block/eax.go
deleted file mode 100644
index 3f3b96431..000000000
--- a/src/pkg/crypto/block/eax.go
+++ /dev/null
@@ -1,253 +0,0 @@
-// 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.
-
-// EAX mode, not a NIST standard (yet).
-// EAX provides encryption and authentication.
-// EAX targets the same uses as NIST's CCM mode,
-// but EAX adds the ability to run in streaming mode.
-
-// See
-// http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/eax/eax-spec.pdf
-// http://www.cs.ucdavis.edu/~rogaway/papers/eax.pdf
-// What those papers call OMAC is now called CMAC.
-
-package block
-
-import (
- "fmt"
- "hash"
- "io"
- "os"
-)
-
-// An EAXTagError is returned when the message has failed to authenticate,
-// because the tag at the end of the message stream (Read) does not match
-// the tag computed from the message itself (Computed).
-type EAXTagError struct {
- Read []byte
- Computed []byte
-}
-
-func (e *EAXTagError) String() string {
- return fmt.Sprintf("crypto/block: EAX tag mismatch: read %x but computed %x", e.Read, e.Computed)
-}
-
-func setupEAX(c Cipher, iv, hdr []byte, tagBytes int) (ctrIV, tag []byte, cmac hash.Hash) {
- n := len(iv)
- if n != c.BlockSize() {
- panic(fmt.Sprintln("crypto/block: EAX: iv length", n, "!=", c.BlockSize()))
- }
- buf := make([]byte, n) // zeroed
-
- // tag = CMAC(0 + iv) ^ CMAC(1 + hdr) ^ CMAC(2 + data)
- cmac = NewCMAC(c)
- cmac.Write(buf) // 0
- cmac.Write(iv)
- sum := cmac.Sum()
- ctrIV = dup(sum)
- tag = dup(sum[0:tagBytes])
-
- cmac.Reset()
- buf[n-1] = 1
- cmac.Write(buf) // 1
- cmac.Write(hdr)
- sum = cmac.Sum()
- for i := 0; i < tagBytes; i++ {
- tag[i] ^= sum[i]
- }
-
- cmac.Reset()
- buf[n-1] = 2 // 2
- cmac.Write(buf)
-
- return
-}
-
-func finishEAX(tag []byte, cmac hash.Hash) {
- // Finish CMAC #2 and xor into tag.
- sum := cmac.Sum()
- for i := range tag {
- tag[i] ^= sum[i]
- }
-}
-
-// Writer adapter. Tees writes into both w and cmac.
-// Knows that cmac never returns write errors.
-type cmacWriter struct {
- w io.Writer
- cmac hash.Hash
-}
-
-func (cw *cmacWriter) Write(p []byte) (n int, err os.Error) {
- n, err = cw.w.Write(p)
- cw.cmac.Write(p[0:n])
- return
-}
-
-// An eaxEncrypter implements the EAX encryption mode.
-type eaxEncrypter struct {
- ctr io.Writer // CTR encrypter
- cw cmacWriter // CTR's output stream
- tag []byte
-}
-
-// NewEAXEncrypter creates and returns a new EAX encrypter
-// using the given cipher c, initialization vector iv, associated data hdr,
-// and tag length tagBytes. The encrypter's Write method encrypts
-// the data it receives and writes that data to w.
-// The encrypter's Close method writes a final authenticating tag to w.
-func NewEAXEncrypter(c Cipher, iv []byte, hdr []byte, tagBytes int, w io.Writer) io.WriteCloser {
- x := new(eaxEncrypter)
-
- // Create new CTR instance writing to both
- // w for encrypted output and cmac for digesting.
- x.cw.w = w
- var ctrIV []byte
- ctrIV, x.tag, x.cw.cmac = setupEAX(c, iv, hdr, tagBytes)
- x.ctr = NewCTRWriter(c, ctrIV, &x.cw)
- return x
-}
-
-func (x *eaxEncrypter) Write(p []byte) (n int, err os.Error) {
- return x.ctr.Write(p)
-}
-
-func (x *eaxEncrypter) Close() os.Error {
- x.ctr = nil // crash if Write is called again
-
- // Write tag.
- finishEAX(x.tag, x.cw.cmac)
- n, err := x.cw.w.Write(x.tag)
- if n != len(x.tag) && err == nil {
- err = io.ErrShortWrite
- }
-
- return err
-}
-
-// Reader adapter. Returns data read from r but hangs
-// on to the last len(tag) bytes for itself (returns EOF len(tag)
-// bytes early). Also tees all data returned from Read into
-// the cmac digest. The "don't return the last t bytes"
-// and the "tee into digest" functionality could be separated,
-// but the latter half is trivial.
-type cmacReader struct {
- r io.Reader
- cmac hash.Hash
- tag []byte
- tmp []byte
-}
-
-func (cr *cmacReader) Read(p []byte) (n int, err os.Error) {
- // TODO(rsc): Maybe fall back to simpler code if
- // we recognize the underlying r as a ByteBuffer
- // or ByteReader. Then we can just take the last piece
- // off at the start.
-
- // First, read a tag-sized chunk.
- // It's probably not the tag (unless there's no data).
- tag := cr.tag
- if len(tag) < cap(tag) {
- nt := len(tag)
- nn, err1 := io.ReadFull(cr.r, tag[nt:cap(tag)])
- tag = tag[0 : nt+nn]
- cr.tag = tag
- if err1 != nil {
- return 0, err1
- }
- }
-
- tagBytes := len(tag)
- if len(p) > 4*tagBytes {
- // If p is big, try to read directly into p to avoid a copy.
- n, err = cr.r.Read(p[tagBytes:])
- if n == 0 {
- goto out
- }
- // copy old tag into p
- for i := 0; i < tagBytes; i++ {
- p[i] = tag[i]
- }
- // copy new tag out of p
- for i := 0; i < tagBytes; i++ {
- tag[i] = p[n+i]
- }
- goto out
- }
-
- // Otherwise, read into p and then slide data
- n, err = cr.r.Read(p)
- if n == 0 {
- goto out
- }
-
- // copy tag+p into p+tmp and then swap tmp, tag
- tmp := cr.tmp
- for i := n + tagBytes - 1; i >= 0; i-- {
- var c byte
- if i < tagBytes {
- c = tag[i]
- } else {
- c = p[i-tagBytes]
- }
- if i < n {
- p[i] = c
- } else {
- tmp[i] = c
- }
- }
- cr.tmp, cr.tag = tag, tmp
-
-out:
- cr.cmac.Write(p[0:n])
- return
-}
-
-type eaxDecrypter struct {
- ctr io.Reader
- cr cmacReader
- tag []byte
-}
-
-// NewEAXDecrypter creates and returns a new EAX decrypter
-// using the given cipher c, initialization vector iv, associated data hdr,
-// and tag length tagBytes. The encrypter's Read method decrypts and
-// returns data read from r. At r's EOF, the encrypter checks the final
-// authenticating tag and returns an EAXTagError if the tag is invalid.
-// In that case, the message should be discarded.
-// Note that the data stream returned from Read cannot be
-// assumed to be valid, authenticated data until Read returns
-// 0, nil to signal the end of the data.
-func NewEAXDecrypter(c Cipher, iv []byte, hdr []byte, tagBytes int, r io.Reader) io.Reader {
- x := new(eaxDecrypter)
-
- x.cr.r = r
- x.cr.tag = make([]byte, 0, tagBytes)
- x.cr.tmp = make([]byte, 0, tagBytes)
- var ctrIV []byte
- ctrIV, x.tag, x.cr.cmac = setupEAX(c, iv, hdr, tagBytes)
- x.ctr = NewCTRReader(c, ctrIV, &x.cr)
- return x
-}
-
-func (x *eaxDecrypter) checkTag() os.Error {
- x.ctr = nil // crash if Read is called again
-
- finishEAX(x.tag, x.cr.cmac)
- if !same(x.tag, x.cr.tag) {
- e := new(EAXTagError)
- e.Computed = dup(x.tag)
- e.Read = dup(x.cr.tag)
- return e
- }
- return nil
-}
-
-func (x *eaxDecrypter) Read(p []byte) (n int, err os.Error) {
- n, err = x.ctr.Read(p)
- if n == 0 && err == nil {
- err = x.checkTag()
- }
- return n, err
-}
diff --git a/src/pkg/crypto/block/eax_aes_test.go b/src/pkg/crypto/block/eax_aes_test.go
deleted file mode 100644
index 93aa771be..000000000
--- a/src/pkg/crypto/block/eax_aes_test.go
+++ /dev/null
@@ -1,140 +0,0 @@
-// 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 block
-
-import (
- "bytes"
- "crypto/aes"
- "fmt"
- "io"
- "testing"
-)
-
-// Test vectors from http://www.cs.ucdavis.edu/~rogaway/papers/eax.pdf
-
-type eaxAESTest struct {
- msg []byte
- key []byte
- nonce []byte
- header []byte
- cipher []byte
-}
-
-var eaxAESTests = []eaxAESTest{
- {
- []byte{},
- []byte{0x23, 0x39, 0x52, 0xDE, 0xE4, 0xD5, 0xED, 0x5F, 0x9B, 0x9C, 0x6D, 0x6F, 0xF8, 0x0F, 0xF4, 0x78},
- []byte{0x62, 0xEC, 0x67, 0xF9, 0xC3, 0xA4, 0xA4, 0x07, 0xFC, 0xB2, 0xA8, 0xC4, 0x90, 0x31, 0xA8, 0xB3},
- []byte{0x6B, 0xFB, 0x91, 0x4F, 0xD0, 0x7E, 0xAE, 0x6B},
- []byte{0xE0, 0x37, 0x83, 0x0E, 0x83, 0x89, 0xF2, 0x7B, 0x02, 0x5A, 0x2D, 0x65, 0x27, 0xE7, 0x9D, 0x01},
- },
- {
- []byte{0xF7, 0xFB},
- []byte{0x91, 0x94, 0x5D, 0x3F, 0x4D, 0xCB, 0xEE, 0x0B, 0xF4, 0x5E, 0xF5, 0x22, 0x55, 0xF0, 0x95, 0xA4},
- []byte{0xBE, 0xCA, 0xF0, 0x43, 0xB0, 0xA2, 0x3D, 0x84, 0x31, 0x94, 0xBA, 0x97, 0x2C, 0x66, 0xDE, 0xBD},
- []byte{0xFA, 0x3B, 0xFD, 0x48, 0x06, 0xEB, 0x53, 0xFA},
- []byte{0x19, 0xDD, 0x5C, 0x4C, 0x93, 0x31, 0x04, 0x9D, 0x0B, 0xDA, 0xB0, 0x27, 0x74, 0x08, 0xF6, 0x79, 0x67, 0xE5},
- },
- {
- []byte{0x1A, 0x47, 0xCB, 0x49, 0x33},
- []byte{0x01, 0xF7, 0x4A, 0xD6, 0x40, 0x77, 0xF2, 0xE7, 0x04, 0xC0, 0xF6, 0x0A, 0xDA, 0x3D, 0xD5, 0x23},
- []byte{0x70, 0xC3, 0xDB, 0x4F, 0x0D, 0x26, 0x36, 0x84, 0x00, 0xA1, 0x0E, 0xD0, 0x5D, 0x2B, 0xFF, 0x5E},
- []byte{0x23, 0x4A, 0x34, 0x63, 0xC1, 0x26, 0x4A, 0xC6},
- []byte{0xD8, 0x51, 0xD5, 0xBA, 0xE0, 0x3A, 0x59, 0xF2, 0x38, 0xA2, 0x3E, 0x39, 0x19, 0x9D, 0xC9, 0x26, 0x66, 0x26, 0xC4, 0x0F, 0x80},
- },
- {
- []byte{0x48, 0x1C, 0x9E, 0x39, 0xB1},
- []byte{0xD0, 0x7C, 0xF6, 0xCB, 0xB7, 0xF3, 0x13, 0xBD, 0xDE, 0x66, 0xB7, 0x27, 0xAF, 0xD3, 0xC5, 0xE8},
- []byte{0x84, 0x08, 0xDF, 0xFF, 0x3C, 0x1A, 0x2B, 0x12, 0x92, 0xDC, 0x19, 0x9E, 0x46, 0xB7, 0xD6, 0x17},
- []byte{0x33, 0xCC, 0xE2, 0xEA, 0xBF, 0xF5, 0xA7, 0x9D},
- []byte{0x63, 0x2A, 0x9D, 0x13, 0x1A, 0xD4, 0xC1, 0x68, 0xA4, 0x22, 0x5D, 0x8E, 0x1F, 0xF7, 0x55, 0x93, 0x99, 0x74, 0xA7, 0xBE, 0xDE},
- },
- {
- []byte{0x40, 0xD0, 0xC0, 0x7D, 0xA5, 0xE4},
- []byte{0x35, 0xB6, 0xD0, 0x58, 0x00, 0x05, 0xBB, 0xC1, 0x2B, 0x05, 0x87, 0x12, 0x45, 0x57, 0xD2, 0xC2},
- []byte{0xFD, 0xB6, 0xB0, 0x66, 0x76, 0xEE, 0xDC, 0x5C, 0x61, 0xD7, 0x42, 0x76, 0xE1, 0xF8, 0xE8, 0x16},
- []byte{0xAE, 0xB9, 0x6E, 0xAE, 0xBE, 0x29, 0x70, 0xE9},
- []byte{0x07, 0x1D, 0xFE, 0x16, 0xC6, 0x75, 0xCB, 0x06, 0x77, 0xE5, 0x36, 0xF7, 0x3A, 0xFE, 0x6A, 0x14, 0xB7, 0x4E, 0xE4, 0x98, 0x44, 0xDD},
- },
- {
- []byte{0x4D, 0xE3, 0xB3, 0x5C, 0x3F, 0xC0, 0x39, 0x24, 0x5B, 0xD1, 0xFB, 0x7D},
- []byte{0xBD, 0x8E, 0x6E, 0x11, 0x47, 0x5E, 0x60, 0xB2, 0x68, 0x78, 0x4C, 0x38, 0xC6, 0x2F, 0xEB, 0x22},
- []byte{0x6E, 0xAC, 0x5C, 0x93, 0x07, 0x2D, 0x8E, 0x85, 0x13, 0xF7, 0x50, 0x93, 0x5E, 0x46, 0xDA, 0x1B},
- []byte{0xD4, 0x48, 0x2D, 0x1C, 0xA7, 0x8D, 0xCE, 0x0F},
- []byte{0x83, 0x5B, 0xB4, 0xF1, 0x5D, 0x74, 0x3E, 0x35, 0x0E, 0x72, 0x84, 0x14, 0xAB, 0xB8, 0x64, 0x4F, 0xD6, 0xCC, 0xB8, 0x69, 0x47, 0xC5, 0xE1, 0x05, 0x90, 0x21, 0x0A, 0x4F},
- },
- {
- []byte{0x8B, 0x0A, 0x79, 0x30, 0x6C, 0x9C, 0xE7, 0xED, 0x99, 0xDA, 0xE4, 0xF8, 0x7F, 0x8D, 0xD6, 0x16, 0x36},
- []byte{0x7C, 0x77, 0xD6, 0xE8, 0x13, 0xBE, 0xD5, 0xAC, 0x98, 0xBA, 0xA4, 0x17, 0x47, 0x7A, 0x2E, 0x7D},
- []byte{0x1A, 0x8C, 0x98, 0xDC, 0xD7, 0x3D, 0x38, 0x39, 0x3B, 0x2B, 0xF1, 0x56, 0x9D, 0xEE, 0xFC, 0x19},
- []byte{0x65, 0xD2, 0x01, 0x79, 0x90, 0xD6, 0x25, 0x28},
- []byte{0x02, 0x08, 0x3E, 0x39, 0x79, 0xDA, 0x01, 0x48, 0x12, 0xF5, 0x9F, 0x11, 0xD5, 0x26, 0x30, 0xDA, 0x30, 0x13, 0x73, 0x27, 0xD1, 0x06, 0x49, 0xB0, 0xAA, 0x6E, 0x1C, 0x18, 0x1D, 0xB6, 0x17, 0xD7, 0xF2},
- },
- {
- []byte{0x1B, 0xDA, 0x12, 0x2B, 0xCE, 0x8A, 0x8D, 0xBA, 0xF1, 0x87, 0x7D, 0x96, 0x2B, 0x85, 0x92, 0xDD, 0x2D, 0x56},
- []byte{0x5F, 0xFF, 0x20, 0xCA, 0xFA, 0xB1, 0x19, 0xCA, 0x2F, 0xC7, 0x35, 0x49, 0xE2, 0x0F, 0x5B, 0x0D},
- []byte{0xDD, 0xE5, 0x9B, 0x97, 0xD7, 0x22, 0x15, 0x6D, 0x4D, 0x9A, 0xFF, 0x2B, 0xC7, 0x55, 0x98, 0x26},
- []byte{0x54, 0xB9, 0xF0, 0x4E, 0x6A, 0x09, 0x18, 0x9A},
- []byte{0x2E, 0xC4, 0x7B, 0x2C, 0x49, 0x54, 0xA4, 0x89, 0xAF, 0xC7, 0xBA, 0x48, 0x97, 0xED, 0xCD, 0xAE, 0x8C, 0xC3, 0x3B, 0x60, 0x45, 0x05, 0x99, 0xBD, 0x02, 0xC9, 0x63, 0x82, 0x90, 0x2A, 0xEF, 0x7F, 0x83, 0x2A},
- },
- {
- []byte{0x6C, 0xF3, 0x67, 0x20, 0x87, 0x2B, 0x85, 0x13, 0xF6, 0xEA, 0xB1, 0xA8, 0xA4, 0x44, 0x38, 0xD5, 0xEF, 0x11},
- []byte{0xA4, 0xA4, 0x78, 0x2B, 0xCF, 0xFD, 0x3E, 0xC5, 0xE7, 0xEF, 0x6D, 0x8C, 0x34, 0xA5, 0x61, 0x23},
- []byte{0xB7, 0x81, 0xFC, 0xF2, 0xF7, 0x5F, 0xA5, 0xA8, 0xDE, 0x97, 0xA9, 0xCA, 0x48, 0xE5, 0x22, 0xEC},
- []byte{0x89, 0x9A, 0x17, 0x58, 0x97, 0x56, 0x1D, 0x7E},
- []byte{0x0D, 0xE1, 0x8F, 0xD0, 0xFD, 0xD9, 0x1E, 0x7A, 0xF1, 0x9F, 0x1D, 0x8E, 0xE8, 0x73, 0x39, 0x38, 0xB1, 0xE8, 0xE7, 0xF6, 0xD2, 0x23, 0x16, 0x18, 0x10, 0x2F, 0xDB, 0x7F, 0xE5, 0x5F, 0xF1, 0x99, 0x17, 0x00},
- },
- {
- []byte{0xCA, 0x40, 0xD7, 0x44, 0x6E, 0x54, 0x5F, 0xFA, 0xED, 0x3B, 0xD1, 0x2A, 0x74, 0x0A, 0x65, 0x9F, 0xFB, 0xBB, 0x3C, 0xEA, 0xB7},
- []byte{0x83, 0x95, 0xFC, 0xF1, 0xE9, 0x5B, 0xEB, 0xD6, 0x97, 0xBD, 0x01, 0x0B, 0xC7, 0x66, 0xAA, 0xC3},
- []byte{0x22, 0xE7, 0xAD, 0xD9, 0x3C, 0xFC, 0x63, 0x93, 0xC5, 0x7E, 0xC0, 0xB3, 0xC1, 0x7D, 0x6B, 0x44},
- []byte{0x12, 0x67, 0x35, 0xFC, 0xC3, 0x20, 0xD2, 0x5A},
- []byte{0xCB, 0x89, 0x20, 0xF8, 0x7A, 0x6C, 0x75, 0xCF, 0xF3, 0x96, 0x27, 0xB5, 0x6E, 0x3E, 0xD1, 0x97, 0xC5, 0x52, 0xD2, 0x95, 0xA7, 0xCF, 0xC4, 0x6A, 0xFC, 0x25, 0x3B, 0x46, 0x52, 0xB1, 0xAF, 0x37, 0x95, 0xB1, 0x24, 0xAB, 0x6E},
- },
-}
-
-func TestEAXEncrypt_AES(t *testing.T) {
- b := new(bytes.Buffer)
- for i, tt := range eaxAESTests {
- test := fmt.Sprintf("test %d", i)
- c, err := aes.NewCipher(tt.key)
- if err != nil {
- t.Fatalf("%s: NewCipher(%d bytes) = %s", test, len(tt.key), err)
- }
- b.Reset()
- enc := NewEAXEncrypter(c, tt.nonce, tt.header, 16, b)
- n, err := io.Copy(enc, bytes.NewBuffer(tt.msg))
- if n != int64(len(tt.msg)) || err != nil {
- t.Fatalf("%s: io.Copy into encrypter: %d, %s", test, n, err)
- }
- err = enc.Close()
- if err != nil {
- t.Fatalf("%s: enc.Close: %s", test, err)
- }
- if d := b.Bytes(); !same(d, tt.cipher) {
- t.Fatalf("%s: got %x want %x", test, d, tt.cipher)
- }
- }
-}
-
-func TestEAXDecrypt_AES(t *testing.T) {
- b := new(bytes.Buffer)
- for i, tt := range eaxAESTests {
- test := fmt.Sprintf("test %d", i)
- c, err := aes.NewCipher(tt.key)
- if err != nil {
- t.Fatalf("%s: NewCipher(%d bytes) = %s", test, len(tt.key), err)
- }
- b.Reset()
- dec := NewEAXDecrypter(c, tt.nonce, tt.header, 16, bytes.NewBuffer(tt.cipher))
- n, err := io.Copy(b, dec)
- if n != int64(len(tt.msg)) || err != nil {
- t.Fatalf("%s: io.Copy into decrypter: %d, %s", test, n, err)
- }
- if d := b.Bytes(); !same(d, tt.msg) {
- t.Fatalf("%s: got %x want %x", test, d, tt.msg)
- }
- }
-}
diff --git a/src/pkg/crypto/block/ecb.go b/src/pkg/crypto/block/ecb.go
deleted file mode 100644
index cf09f7cb3..000000000
--- a/src/pkg/crypto/block/ecb.go
+++ /dev/null
@@ -1,270 +0,0 @@
-// 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.
-
-// Electronic codebook (ECB) mode.
-// ECB is a fancy name for ``encrypt and decrypt each block separately.''
-// It's a pretty bad thing to do for any large amount of data (more than one block),
-// because the individual blocks can still be identified, duplicated, and reordered.
-// The ECB implementation exists mainly to provide buffering for
-// the other modes, which wrap it by providing modified Ciphers.
-
-// See NIST SP 800-38A, pp 9-10
-
-package block
-
-import (
- "io"
- "os"
- "strconv"
-)
-
-type ecbDecrypter struct {
- c Cipher
- r io.Reader
- blockSize int // block size
-
- // Buffered data.
- // The buffer buf is used as storage for both
- // plain or crypt; at least one of those is nil at any given time.
- buf []byte
- plain []byte // plain text waiting to be read
- crypt []byte // ciphertext waiting to be decrypted
-}
-
-// Read into x.crypt until it has a full block or EOF or an error happens.
-func (x *ecbDecrypter) fillCrypt() os.Error {
- var err os.Error
- for len(x.crypt) < x.blockSize {
- off := len(x.crypt)
- var m int
- m, err = x.r.Read(x.crypt[off:x.blockSize])
- x.crypt = x.crypt[0 : off+m]
- if m == 0 {
- break
- }
-
- // If an error happened but we got enough
- // data to do some decryption, we can decrypt
- // first and report the error (with some data) later.
- // But if we don't have enough to decrypt,
- // have to stop now.
- if err != nil && len(x.crypt) < x.blockSize {
- break
- }
- }
- return err
-}
-
-// Read from plain text buffer into p.
-func (x *ecbDecrypter) readPlain(p []byte) int {
- n := len(x.plain)
- if n > len(p) {
- n = len(p)
- }
- for i := 0; i < n; i++ {
- p[i] = x.plain[i]
- }
- if n < len(x.plain) {
- x.plain = x.plain[n:]
- } else {
- x.plain = nil
- }
- return n
-}
-
-type ecbFragmentError int
-
-func (n ecbFragmentError) String() string {
- return "crypto/block: " + strconv.Itoa(int(n)) + "-byte fragment at EOF"
-}
-
-func (x *ecbDecrypter) Read(p []byte) (n int, err os.Error) {
- if len(p) == 0 {
- return
- }
-
- // If there's no plaintext waiting and p is not big enough
- // to hold a whole cipher block, we'll have to work in the
- // cipher text buffer. Set it to non-nil so that the
- // code below will fill it.
- if x.plain == nil && len(p) < x.blockSize && x.crypt == nil {
- x.crypt = x.buf[0:0]
- }
-
- // If there is a leftover cipher text buffer,
- // try to accumulate a full block.
- if x.crypt != nil {
- err = x.fillCrypt()
- if err != nil || len(x.crypt) == 0 {
- return
- }
- x.c.Decrypt(x.crypt, x.crypt)
- x.plain = x.crypt
- x.crypt = nil
- }
-
- // If there is a leftover plain text buffer, read from it.
- if x.plain != nil {
- n = x.readPlain(p)
- return
- }
-
- // Read and decrypt directly in caller's buffer.
- n, err = io.ReadAtLeast(x.r, p, x.blockSize)
- if err == os.EOF && n > 0 {
- // EOF is only okay on block boundary
- err = os.ErrorString("block fragment at EOF during decryption")
- return
- }
- var i int
- for i = 0; i+x.blockSize <= n; i += x.blockSize {
- a := p[i : i+x.blockSize]
- x.c.Decrypt(a, a)
- }
-
- // There might be an encrypted fringe remaining.
- // Save it for next time.
- if i < n {
- p = p[i:n]
- copy(x.buf, p)
- x.crypt = x.buf[0:len(p)]
- n = i
- }
-
- return
-}
-
-// NewECBDecrypter returns a reader that reads data from r and decrypts it using c.
-// It decrypts by calling c.Decrypt on each block in sequence;
-// this mode is known as electronic codebook mode, or ECB.
-// The returned Reader does not buffer or read ahead except
-// as required by the cipher's block size.
-func NewECBDecrypter(c Cipher, r io.Reader) io.Reader {
- x := new(ecbDecrypter)
- x.c = c
- x.r = r
- x.blockSize = c.BlockSize()
- x.buf = make([]byte, x.blockSize)
- return x
-}
-
-type ecbEncrypter struct {
- c Cipher
- w io.Writer
- blockSize int
-
- // Buffered data.
- // The buffer buf is used as storage for both
- // plain or crypt. If both are non-nil, plain
- // follows crypt in buf.
- buf []byte
- plain []byte // plain text waiting to be encrypted
- crypt []byte // encrypted text waiting to be written
-}
-
-// Flush the x.crypt buffer to x.w.
-func (x *ecbEncrypter) flushCrypt() os.Error {
- if len(x.crypt) == 0 {
- return nil
- }
- n, err := x.w.Write(x.crypt)
- if n < len(x.crypt) {
- x.crypt = x.crypt[n:]
- if err == nil {
- err = io.ErrShortWrite
- }
- }
- if err != nil {
- return err
- }
- x.crypt = nil
- return nil
-}
-
-// Slide x.plain down to the beginning of x.buf.
-// Plain is known to have less than one block of data,
-// so this is cheap enough.
-func (x *ecbEncrypter) slidePlain() {
- if len(x.plain) == 0 {
- x.plain = x.buf[0:0]
- } else if cap(x.plain) < cap(x.buf) {
- copy(x.buf, x.plain)
- x.plain = x.buf[0:len(x.plain)]
- }
-}
-
-// Fill x.plain from the data in p.
-// Return the number of bytes copied.
-func (x *ecbEncrypter) fillPlain(p []byte) int {
- off := len(x.plain)
- n := len(p)
- if max := cap(x.plain) - off; n > max {
- n = max
- }
- x.plain = x.plain[0 : off+n]
- for i := 0; i < n; i++ {
- x.plain[off+i] = p[i]
- }
- return n
-}
-
-// Encrypt x.plain; record encrypted range as x.crypt.
-func (x *ecbEncrypter) encrypt() {
- var i int
- n := len(x.plain)
- for i = 0; i+x.blockSize <= n; i += x.blockSize {
- a := x.plain[i : i+x.blockSize]
- x.c.Encrypt(a, a)
- }
- x.crypt = x.plain[0:i]
- x.plain = x.plain[i:n]
-}
-
-func (x *ecbEncrypter) Write(p []byte) (n int, err os.Error) {
- for {
- // If there is data waiting to be written, write it.
- // This can happen on the first iteration
- // if a write failed in an earlier call.
- if err = x.flushCrypt(); err != nil {
- return
- }
-
- // Now that encrypted data is gone (flush ran),
- // perhaps we need to slide the plaintext down.
- x.slidePlain()
-
- // Fill plaintext buffer from p.
- m := x.fillPlain(p)
- if m == 0 {
- break
- }
- n += m
- p = p[m:]
-
- // Encrypt, adjusting crypt and plain.
- x.encrypt()
-
- // Write x.crypt.
- if err = x.flushCrypt(); err != nil {
- break
- }
- }
- return
-}
-
-// NewECBEncrypter returns a writer that encrypts data using c and writes it to w.
-// It encrypts by calling c.Encrypt on each block in sequence;
-// this mode is known as electronic codebook mode, or ECB.
-// The returned Writer does no buffering except as required
-// by the cipher's block size, so there is no need for a Flush method.
-func NewECBEncrypter(c Cipher, w io.Writer) io.Writer {
- x := new(ecbEncrypter)
- x.c = c
- x.w = w
- x.blockSize = c.BlockSize()
-
- // Create a buffer that is an integral number of blocks.
- x.buf = make([]byte, 8192/x.blockSize*x.blockSize)
- return x
-}
diff --git a/src/pkg/crypto/block/ecb_aes_test.go b/src/pkg/crypto/block/ecb_aes_test.go
deleted file mode 100644
index 14481d096..000000000
--- a/src/pkg/crypto/block/ecb_aes_test.go
+++ /dev/null
@@ -1,127 +0,0 @@
-// 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.
-
-// ECB AES test vectors.
-
-// See U.S. National Institute of Standards and Technology (NIST)
-// Special Publication 800-38A, ``Recommendation for Block Cipher
-// Modes of Operation,'' 2001 Edition, pp. 24-27.
-
-package block
-
-import (
- "bytes"
- "crypto/aes"
- "io"
- "testing"
-)
-
-type ecbTest struct {
- name string
- key []byte
- in []byte
- out []byte
-}
-
-var commonInput = []byte{
- 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
- 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
- 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
- 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10,
-}
-
-var commonKey128 = []byte{0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c}
-
-var commonKey192 = []byte{
- 0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52, 0xc8, 0x10, 0xf3, 0x2b, 0x80, 0x90, 0x79, 0xe5,
- 0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b,
-}
-
-var commonKey256 = []byte{
- 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81,
- 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4,
-}
-
-var commonIV = []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}
-
-var ecbAESTests = []ecbTest{
- // FIPS 197, Appendix B, C
- {
- "FIPS-197 Appendix B",
- commonKey128,
- []byte{0x32, 0x43, 0xf6, 0xa8, 0x88, 0x5a, 0x30, 0x8d, 0x31, 0x31, 0x98, 0xa2, 0xe0, 0x37, 0x07, 0x34},
- []byte{0x39, 0x25, 0x84, 0x1d, 0x02, 0xdc, 0x09, 0xfb, 0xdc, 0x11, 0x85, 0x97, 0x19, 0x6a, 0x0b, 0x32},
- },
-
- // NIST SP 800-38A pp 24-27
- {
- "ECB-AES128",
- commonKey128,
- commonInput,
- []byte{
- 0x3a, 0xd7, 0x7b, 0xb4, 0x0d, 0x7a, 0x36, 0x60, 0xa8, 0x9e, 0xca, 0xf3, 0x24, 0x66, 0xef, 0x97,
- 0xf5, 0xd3, 0xd5, 0x85, 0x03, 0xb9, 0x69, 0x9d, 0xe7, 0x85, 0x89, 0x5a, 0x96, 0xfd, 0xba, 0xaf,
- 0x43, 0xb1, 0xcd, 0x7f, 0x59, 0x8e, 0xce, 0x23, 0x88, 0x1b, 0x00, 0xe3, 0xed, 0x03, 0x06, 0x88,
- 0x7b, 0x0c, 0x78, 0x5e, 0x27, 0xe8, 0xad, 0x3f, 0x82, 0x23, 0x20, 0x71, 0x04, 0x72, 0x5d, 0xd4,
- },
- },
- {
- "ECB-AES192",
- commonKey192,
- commonInput,
- []byte{
- 0xbd, 0x33, 0x4f, 0x1d, 0x6e, 0x45, 0xf2, 0x5f, 0xf7, 0x12, 0xa2, 0x14, 0x57, 0x1f, 0xa5, 0xcc,
- 0x97, 0x41, 0x04, 0x84, 0x6d, 0x0a, 0xd3, 0xad, 0x77, 0x34, 0xec, 0xb3, 0xec, 0xee, 0x4e, 0xef,
- 0xef, 0x7a, 0xfd, 0x22, 0x70, 0xe2, 0xe6, 0x0a, 0xdc, 0xe0, 0xba, 0x2f, 0xac, 0xe6, 0x44, 0x4e,
- 0x9a, 0x4b, 0x41, 0xba, 0x73, 0x8d, 0x6c, 0x72, 0xfb, 0x16, 0x69, 0x16, 0x03, 0xc1, 0x8e, 0x0e,
- },
- },
- {
- "ECB-AES256",
- commonKey256,
- commonInput,
- []byte{
- 0xf3, 0xee, 0xd1, 0xbd, 0xb5, 0xd2, 0xa0, 0x3c, 0x06, 0x4b, 0x5a, 0x7e, 0x3d, 0xb1, 0x81, 0xf8,
- 0x59, 0x1c, 0xcb, 0x10, 0xd4, 0x10, 0xed, 0x26, 0xdc, 0x5b, 0xa7, 0x4a, 0x31, 0x36, 0x28, 0x70,
- 0xb6, 0xed, 0x21, 0xb9, 0x9c, 0xa6, 0xf4, 0xf9, 0xf1, 0x53, 0xe7, 0xb1, 0xbe, 0xaf, 0xed, 0x1d,
- 0x23, 0x30, 0x4b, 0x7a, 0x39, 0xf9, 0xf3, 0xff, 0x06, 0x7d, 0x8d, 0x8f, 0x9e, 0x24, 0xec, 0xc7,
- },
- },
-}
-
-func TestECB_AES(t *testing.T) {
- for _, tt := range ecbAESTests {
- test := tt.name
-
- c, err := aes.NewCipher(tt.key)
- if err != nil {
- t.Errorf("%s: NewCipher(%d bytes) = %s", test, len(tt.key), err)
- continue
- }
-
- var crypt bytes.Buffer
- w := NewECBEncrypter(c, &crypt)
- var r io.Reader = bytes.NewBuffer(tt.in)
- n, err := io.Copy(w, r)
- if n != int64(len(tt.in)) || err != nil {
- t.Errorf("%s: ECBReader io.Copy = %d, %v want %d, nil", test, n, err, len(tt.in))
- } else if d := crypt.Bytes(); !same(tt.out, d) {
- t.Errorf("%s: ECBReader\nhave %x\nwant %x", test, d, tt.out)
- }
-
- var plain bytes.Buffer
- r = NewECBDecrypter(c, bytes.NewBuffer(tt.out))
- w = &plain
- n, err = io.Copy(w, r)
- if n != int64(len(tt.out)) || err != nil {
- t.Errorf("%s: ECBWriter io.Copy = %d, %v want %d, nil", test, n, err, len(tt.out))
- } else if d := plain.Bytes(); !same(tt.in, d) {
- t.Errorf("%s: ECBWriter\nhave %x\nwant %x", test, d, tt.in)
- }
-
- if t.Failed() {
- break
- }
- }
-}
diff --git a/src/pkg/crypto/block/ecb_test.go b/src/pkg/crypto/block/ecb_test.go
deleted file mode 100644
index 6f79d929a..000000000
--- a/src/pkg/crypto/block/ecb_test.go
+++ /dev/null
@@ -1,181 +0,0 @@
-// 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 block
-
-import (
- "bytes"
- "fmt"
- "io"
- "testing"
- "testing/iotest"
-)
-
-// Simple Cipher for testing: adds an incrementing amount
-// to each byte in each
-type IncCipher struct {
- blockSize int
- delta byte
- encrypting bool
-}
-
-func (c *IncCipher) BlockSize() int { return c.blockSize }
-
-func (c *IncCipher) Encrypt(dst, src []byte) {
- if !c.encrypting {
- panic("encrypt: not encrypting")
- }
- if len(src) != c.blockSize || len(dst) != c.blockSize {
- panic(fmt.Sprintln("encrypt: wrong block size", c.blockSize, len(src), len(dst)))
- }
- c.delta++
- for i, b := range src {
- dst[i] = b + c.delta
- }
-}
-
-func (c *IncCipher) Decrypt(dst, src []byte) {
- if c.encrypting {
- panic("decrypt: not decrypting")
- }
- if len(src) != c.blockSize || len(dst) != c.blockSize {
- panic(fmt.Sprintln("decrypt: wrong block size ", c.blockSize, " ", len(src), " ", len(dst)))
- }
- c.delta--
- for i, b := range src {
- dst[i] = b + c.delta
- }
-}
-
-func TestECBEncrypter(t *testing.T) {
- var plain, crypt [256]byte
- for i := 0; i < len(plain); i++ {
- plain[i] = byte(i)
- }
- b := new(bytes.Buffer)
- for block := 1; block <= 64; block *= 2 {
- // compute encrypted version
- delta := byte(0)
- for i := 0; i < len(crypt); i++ {
- if i%block == 0 {
- delta++
- }
- crypt[i] = plain[i] + delta
- }
-
- for frag := 0; frag < 2; frag++ {
- c := &IncCipher{block, 0, true}
- b.Reset()
- r := bytes.NewBuffer(plain[0:])
- w := NewECBEncrypter(c, b)
-
- // copy plain into w in increasingly large chunks: 1, 1, 2, 4, 8, ...
- // if frag != 0, move the 1 to the end to cause fragmentation.
- if frag == 0 {
- _, err := io.Copyn(w, r, 1)
- if err != nil {
- t.Errorf("block=%d frag=0: first Copyn: %s", block, err)
- continue
- }
- }
- for n := 1; n <= len(plain)/2; n *= 2 {
- _, err := io.Copyn(w, r, int64(n))
- if err != nil {
- t.Errorf("block=%d frag=%d: Copyn %d: %s", block, frag, n, err)
- }
- }
- if frag != 0 {
- _, err := io.Copyn(w, r, 1)
- if err != nil {
- t.Errorf("block=%d frag=1: last Copyn: %s", block, err)
- continue
- }
- }
-
- // check output
- data := b.Bytes()
- if len(data) != len(crypt) {
- t.Errorf("block=%d frag=%d: want %d bytes, got %d", block, frag, len(crypt), len(data))
- continue
- }
-
- if string(data) != string(crypt[0:]) {
- t.Errorf("block=%d frag=%d: want %x got %x", block, frag, data, crypt)
- }
- }
- }
-}
-
-func testECBDecrypter(t *testing.T, maxio int) {
- var readers = []func(io.Reader) io.Reader{
- func(r io.Reader) io.Reader { return r },
- iotest.OneByteReader,
- iotest.HalfReader,
- }
- var plain, crypt [256]byte
- for i := 0; i < len(plain); i++ {
- plain[i] = byte(255 - i)
- }
- b := new(bytes.Buffer)
- for block := 1; block <= 64 && block <= maxio; block *= 2 {
- // compute encrypted version
- delta := byte(0)
- for i := 0; i < len(crypt); i++ {
- if i%block == 0 {
- delta++
- }
- crypt[i] = plain[i] + delta
- }
-
- for mode := 0; mode < len(readers); mode++ {
- for frag := 0; frag < 2; frag++ {
- test := fmt.Sprintf("block=%d mode=%d frag=%d maxio=%d", block, mode, frag, maxio)
- c := &IncCipher{block, 0, false}
- b.Reset()
- r := NewECBDecrypter(c, readers[mode](bytes.NewBuffer(crypt[0:maxio])))
-
- // read from crypt in increasingly large chunks: 1, 1, 2, 4, 8, ...
- // if frag == 1, move the 1 to the end to cause fragmentation.
- if frag == 0 {
- _, err := io.Copyn(b, r, 1)
- if err != nil {
- t.Errorf("%s: first Copyn: %s", test, err)
- continue
- }
- }
- for n := 1; n <= maxio/2; n *= 2 {
- _, err := io.Copyn(b, r, int64(n))
- if err != nil {
- t.Errorf("%s: Copyn %d: %s", test, n, err)
- }
- }
- if frag != 0 {
- _, err := io.Copyn(b, r, 1)
- if err != nil {
- t.Errorf("%s: last Copyn: %s", test, err)
- continue
- }
- }
-
- // check output
- data := b.Bytes()
- if len(data) != maxio {
- t.Errorf("%s: want %d bytes, got %d", test, maxio, len(data))
- continue
- }
-
- if string(data) != string(plain[0:maxio]) {
- t.Errorf("%s: input=%x want %x got %x", test, crypt[0:maxio], plain[0:maxio], data)
- }
- }
- }
- }
-}
-
-func TestECBDecrypter(t *testing.T) {
- // Do shorter I/O sizes first; they're easier to debug.
- for n := 1; n <= 256 && !t.Failed(); n *= 2 {
- testECBDecrypter(t, n)
- }
-}
diff --git a/src/pkg/crypto/block/ofb.go b/src/pkg/crypto/block/ofb.go
deleted file mode 100644
index 11aaaa4d7..000000000
--- a/src/pkg/crypto/block/ofb.go
+++ /dev/null
@@ -1,60 +0,0 @@
-// 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.
-
-// Output feedback (OFB) mode.
-
-// OFB converts a block cipher into a stream cipher by
-// repeatedly encrypting an initialization vector and
-// xoring the resulting stream of data with the input.
-
-// See NIST SP 800-38A, pp 13-15
-
-package block
-
-import (
- "fmt"
- "io"
-)
-
-type ofbStream struct {
- c Cipher
- iv []byte
-}
-
-func newOFBStream(c Cipher, iv []byte) *ofbStream {
- x := new(ofbStream)
- x.c = c
- n := len(iv)
- if n != c.BlockSize() {
- panic(fmt.Sprintln("crypto/block: newOFBStream: invalid iv size", n, "!=", c.BlockSize()))
- }
- x.iv = dup(iv)
- return x
-}
-
-func (x *ofbStream) Next() []byte {
- x.c.Encrypt(x.iv, x.iv)
- return x.iv
-}
-
-// NewOFBReader returns a reader that reads data from r, decrypts (or encrypts)
-// it using c in output feedback (OFB) mode with the initialization vector iv.
-// The returned Reader does not buffer and has no block size.
-// In OFB mode, encryption and decryption are the same operation:
-// an OFB reader applied to an encrypted stream produces a decrypted
-// stream and vice versa.
-func NewOFBReader(c Cipher, iv []byte, r io.Reader) io.Reader {
- return newXorReader(newOFBStream(c, iv), r)
-}
-
-// NewOFBWriter returns a writer that encrypts (or decrypts) data using c
-// in cipher feedback (OFB) mode with the initialization vector iv
-// and writes the encrypted data to w.
-// The returned Writer does not buffer and has no block size.
-// In OFB mode, encryption and decryption are the same operation:
-// an OFB writer applied to an decrypted stream produces an encrypted
-// stream and vice versa.
-func NewOFBWriter(c Cipher, iv []byte, w io.Writer) io.Writer {
- return newXorWriter(newOFBStream(c, iv), w)
-}
diff --git a/src/pkg/crypto/block/ofb_aes_test.go b/src/pkg/crypto/block/ofb_aes_test.go
deleted file mode 100644
index 9c527a6b3..000000000
--- a/src/pkg/crypto/block/ofb_aes_test.go
+++ /dev/null
@@ -1,108 +0,0 @@
-// 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.
-
-// OFB AES test vectors.
-
-// See U.S. National Institute of Standards and Technology (NIST)
-// Special Publication 800-38A, ``Recommendation for Block Cipher
-// Modes of Operation,'' 2001 Edition, pp. 52-55.
-
-package block
-
-import (
- "bytes"
- "crypto/aes"
- "io"
- "testing"
-)
-
-type ofbTest struct {
- name string
- key []byte
- iv []byte
- in []byte
- out []byte
-}
-
-var ofbAESTests = []ofbTest{
- // NIST SP 800-38A pp 52-55
- {
- "OFB-AES128",
- commonKey128,
- commonIV,
- commonInput,
- []byte{
- 0x3b, 0x3f, 0xd9, 0x2e, 0xb7, 0x2d, 0xad, 0x20, 0x33, 0x34, 0x49, 0xf8, 0xe8, 0x3c, 0xfb, 0x4a,
- 0x77, 0x89, 0x50, 0x8d, 0x16, 0x91, 0x8f, 0x03, 0xf5, 0x3c, 0x52, 0xda, 0xc5, 0x4e, 0xd8, 0x25,
- 0x97, 0x40, 0x05, 0x1e, 0x9c, 0x5f, 0xec, 0xf6, 0x43, 0x44, 0xf7, 0xa8, 0x22, 0x60, 0xed, 0xcc,
- 0x30, 0x4c, 0x65, 0x28, 0xf6, 0x59, 0xc7, 0x78, 0x66, 0xa5, 0x10, 0xd9, 0xc1, 0xd6, 0xae, 0x5e,
- },
- },
- {
- "OFB-AES192",
- commonKey192,
- commonIV,
- commonInput,
- []byte{
- 0xcd, 0xc8, 0x0d, 0x6f, 0xdd, 0xf1, 0x8c, 0xab, 0x34, 0xc2, 0x59, 0x09, 0xc9, 0x9a, 0x41, 0x74,
- 0xfc, 0xc2, 0x8b, 0x8d, 0x4c, 0x63, 0x83, 0x7c, 0x09, 0xe8, 0x17, 0x00, 0xc1, 0x10, 0x04, 0x01,
- 0x8d, 0x9a, 0x9a, 0xea, 0xc0, 0xf6, 0x59, 0x6f, 0x55, 0x9c, 0x6d, 0x4d, 0xaf, 0x59, 0xa5, 0xf2,
- 0x6d, 0x9f, 0x20, 0x08, 0x57, 0xca, 0x6c, 0x3e, 0x9c, 0xac, 0x52, 0x4b, 0xd9, 0xac, 0xc9, 0x2a,
- },
- },
- {
- "OFB-AES256",
- commonKey256,
- commonIV,
- commonInput,
- []byte{
- 0xdc, 0x7e, 0x84, 0xbf, 0xda, 0x79, 0x16, 0x4b, 0x7e, 0xcd, 0x84, 0x86, 0x98, 0x5d, 0x38, 0x60,
- 0x4f, 0xeb, 0xdc, 0x67, 0x40, 0xd2, 0x0b, 0x3a, 0xc8, 0x8f, 0x6a, 0xd8, 0x2a, 0x4f, 0xb0, 0x8d,
- 0x71, 0xab, 0x47, 0xa0, 0x86, 0xe8, 0x6e, 0xed, 0xf3, 0x9d, 0x1c, 0x5b, 0xba, 0x97, 0xc4, 0x08,
- 0x01, 0x26, 0x14, 0x1d, 0x67, 0xf3, 0x7b, 0xe8, 0x53, 0x8f, 0x5a, 0x8b, 0xe7, 0x40, 0xe4, 0x84,
- },
- },
-}
-
-func TestOFB_AES(t *testing.T) {
- for _, tt := range ofbAESTests {
- test := tt.name
-
- c, err := aes.NewCipher(tt.key)
- if err != nil {
- t.Errorf("%s: NewCipher(%d bytes) = %s", test, len(tt.key), err)
- continue
- }
-
- for j := 0; j <= 5; j += 5 {
- var crypt bytes.Buffer
- in := tt.in[0 : len(tt.in)-j]
- w := NewOFBWriter(c, tt.iv, &crypt)
- var r io.Reader = bytes.NewBuffer(in)
- n, err := io.Copy(w, r)
- if n != int64(len(in)) || err != nil {
- t.Errorf("%s/%d: OFBWriter io.Copy = %d, %v want %d, nil", test, len(in), n, err, len(in))
- } else if d, out := crypt.Bytes(), tt.out[0:len(in)]; !same(out, d) {
- t.Errorf("%s/%d: OFBWriter\ninpt %x\nhave %x\nwant %x", test, len(in), in, d, out)
- }
- }
-
- for j := 0; j <= 7; j += 7 {
- var plain bytes.Buffer
- out := tt.out[0 : len(tt.out)-j]
- r := NewOFBReader(c, tt.iv, bytes.NewBuffer(out))
- w := &plain
- n, err := io.Copy(w, r)
- if n != int64(len(out)) || err != nil {
- t.Errorf("%s/%d: OFBReader io.Copy = %d, %v want %d, nil", test, len(out), n, err, len(out))
- } else if d, in := plain.Bytes(), tt.in[0:len(out)]; !same(in, d) {
- t.Errorf("%s/%d: OFBReader\nhave %x\nwant %x", test, len(out), d, in)
- }
- }
-
- if t.Failed() {
- break
- }
- }
-}
diff --git a/src/pkg/crypto/block/xor.go b/src/pkg/crypto/block/xor.go
deleted file mode 100644
index 9d8b17224..000000000
--- a/src/pkg/crypto/block/xor.go
+++ /dev/null
@@ -1,124 +0,0 @@
-// 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.
-
-// Encrypt/decrypt data by xor with a pseudo-random data stream.
-
-package block
-
-import (
- "io"
- "os"
-)
-
-// A dataStream is an interface to an unending stream of data,
-// used by XorReader and XorWriter to model a pseudo-random generator.
-// Calls to Next() return sequential blocks of data from the stream.
-// Each call must return at least one byte: there is no EOF.
-type dataStream interface {
- Next() []byte
-}
-
-type xorReader struct {
- r io.Reader
- rand dataStream // pseudo-random
- buf []byte // data available from last call to rand
-}
-
-func newXorReader(rand dataStream, r io.Reader) io.Reader {
- x := new(xorReader)
- x.r = r
- x.rand = rand
- return x
-}
-
-func (x *xorReader) Read(p []byte) (n int, err os.Error) {
- n, err = x.r.Read(p)
-
- // xor input with stream.
- bp := 0
- buf := x.buf
- for i := 0; i < n; i++ {
- if bp >= len(buf) {
- buf = x.rand.Next()
- bp = 0
- }
- p[i] ^= buf[bp]
- bp++
- }
- x.buf = buf[bp:]
- return n, err
-}
-
-type xorWriter struct {
- w io.Writer
- rand dataStream // pseudo-random
- buf []byte // last buffer returned by rand
- extra []byte // extra random data (use before buf)
- work []byte // work space
-}
-
-func newXorWriter(rand dataStream, w io.Writer) io.Writer {
- x := new(xorWriter)
- x.w = w
- x.rand = rand
- x.work = make([]byte, 4096)
- return x
-}
-
-func (x *xorWriter) Write(p []byte) (n int, err os.Error) {
- for len(p) > 0 {
- // Determine next chunk of random data
- // and xor with p into x.work.
- var chunk []byte
- m := len(p)
- if nn := len(x.extra); nn > 0 {
- // extra points into work, so edit directly
- if m > nn {
- m = nn
- }
- for i := 0; i < m; i++ {
- x.extra[i] ^= p[i]
- }
- chunk = x.extra[0:m]
- } else {
- // xor p ^ buf into work, refreshing buf as needed
- if nn := len(x.work); m > nn {
- m = nn
- }
- bp := 0
- buf := x.buf
- for i := 0; i < m; i++ {
- if bp >= len(buf) {
- buf = x.rand.Next()
- bp = 0
- }
- x.work[i] = buf[bp] ^ p[i]
- bp++
- }
- x.buf = buf[bp:]
- chunk = x.work[0:m]
- }
-
- // Write chunk.
- var nn int
- nn, err = x.w.Write(chunk)
- if nn != len(chunk) && err == nil {
- err = io.ErrShortWrite
- }
- if nn < len(chunk) {
- // Reconstruct the random bits from the unwritten
- // data and save them for next time.
- for i := nn; i < m; i++ {
- chunk[i] ^= p[i]
- }
- x.extra = chunk[nn:]
- }
- n += nn
- if err != nil {
- return
- }
- p = p[m:]
- }
- return
-}
diff --git a/src/pkg/crypto/block/xor_test.go b/src/pkg/crypto/block/xor_test.go
deleted file mode 100644
index 50f6bb08d..000000000
--- a/src/pkg/crypto/block/xor_test.go
+++ /dev/null
@@ -1,168 +0,0 @@
-// 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 block
-
-import (
- "bytes"
- "fmt"
- "io"
- "testing"
- "testing/iotest"
-)
-
-// Simple "pseudo-random" stream for testing.
-type incStream struct {
- buf []byte
- n byte
-}
-
-func newIncStream(blockSize int) *incStream {
- x := new(incStream)
- x.buf = make([]byte, blockSize)
- return x
-}
-
-func (x *incStream) Next() []byte {
- x.n++
- for i := range x.buf {
- x.buf[i] = x.n
- x.n++
- }
- return x.buf
-}
-
-func testXorWriter(t *testing.T, maxio int) {
- var plain, crypt [256]byte
- for i := 0; i < len(plain); i++ {
- plain[i] = byte(i)
- }
- b := new(bytes.Buffer)
- for block := 1; block <= 64 && block <= maxio; block *= 2 {
- // compute encrypted version
- n := byte(0)
- for i := 0; i < len(crypt); i++ {
- if i%block == 0 {
- n++
- }
- crypt[i] = plain[i] ^ n
- n++
- }
-
- for frag := 0; frag < 2; frag++ {
- test := fmt.Sprintf("block=%d frag=%d maxio=%d", block, frag, maxio)
- b.Reset()
- r := bytes.NewBuffer(plain[0:])
- s := newIncStream(block)
- w := newXorWriter(s, b)
-
- // copy plain into w in increasingly large chunks: 1, 1, 2, 4, 8, ...
- // if frag != 0, move the 1 to the end to cause fragmentation.
- if frag == 0 {
- _, err := io.Copyn(w, r, 1)
- if err != nil {
- t.Errorf("%s: first Copyn: %s", test, err)
- continue
- }
- }
- for n := 1; n <= len(plain)/2; n *= 2 {
- _, err := io.Copyn(w, r, int64(n))
- if err != nil {
- t.Errorf("%s: Copyn %d: %s", test, n, err)
- }
- }
-
- // check output
- crypt := crypt[0 : len(crypt)-frag]
- data := b.Bytes()
- if len(data) != len(crypt) {
- t.Errorf("%s: want %d bytes, got %d", test, len(crypt), len(data))
- continue
- }
-
- if string(data) != string(crypt) {
- t.Errorf("%s: want %x got %x", test, data, crypt)
- }
- }
- }
-}
-
-
-func TestXorWriter(t *testing.T) {
- // Do shorter I/O sizes first; they're easier to debug.
- for n := 1; n <= 256 && !t.Failed(); n *= 2 {
- testXorWriter(t, n)
- }
-}
-
-func testXorReader(t *testing.T, maxio int) {
- var readers = []func(io.Reader) io.Reader{
- func(r io.Reader) io.Reader { return r },
- iotest.OneByteReader,
- iotest.HalfReader,
- }
- var plain, crypt [256]byte
- for i := 0; i < len(plain); i++ {
- plain[i] = byte(255 - i)
- }
- b := new(bytes.Buffer)
- for block := 1; block <= 64 && block <= maxio; block *= 2 {
- // compute encrypted version
- n := byte(0)
- for i := 0; i < len(crypt); i++ {
- if i%block == 0 {
- n++
- }
- crypt[i] = plain[i] ^ n
- n++
- }
-
- for mode := 0; mode < len(readers); mode++ {
- for frag := 0; frag < 2; frag++ {
- test := fmt.Sprintf("block=%d mode=%d frag=%d maxio=%d", block, mode, frag, maxio)
- s := newIncStream(block)
- b.Reset()
- r := newXorReader(s, readers[mode](bytes.NewBuffer(crypt[0:maxio])))
-
- // read from crypt in increasingly large chunks: 1, 1, 2, 4, 8, ...
- // if frag == 1, move the 1 to the end to cause fragmentation.
- if frag == 0 {
- _, err := io.Copyn(b, r, 1)
- if err != nil {
- t.Errorf("%s: first Copyn: %s", test, err)
- continue
- }
- }
- for n := 1; n <= maxio/2; n *= 2 {
- _, err := io.Copyn(b, r, int64(n))
- if err != nil {
- t.Errorf("%s: Copyn %d: %s", test, n, err)
- }
- }
-
- // check output
- data := b.Bytes()
- crypt := crypt[0 : maxio-frag]
- plain := plain[0 : maxio-frag]
- if len(data) != len(plain) {
- t.Errorf("%s: want %d bytes, got %d", test, len(plain), len(data))
- continue
- }
-
- if string(data) != string(plain) {
- t.Errorf("%s: input=%x want %x got %x", test, crypt, plain, data)
- }
- }
- }
- }
-}
-
-func TestXorReader(t *testing.T) {
- // Do shorter I/O sizes first; they're easier to debug.
- for n := 1; n <= 256 && !t.Failed(); n *= 2 {
- testXorReader(t, n)
- }
-}
-
-// TODO(rsc): Test handling of writes after write errors.
diff --git a/src/pkg/crypto/cipher/ctr.go b/src/pkg/crypto/cipher/ctr.go
index 04436ec23..147b74fc2 100644
--- a/src/pkg/crypto/cipher/ctr.go
+++ b/src/pkg/crypto/cipher/ctr.go
@@ -22,6 +22,10 @@ type ctr struct {
// NewCTR returns a Stream which encrypts/decrypts using the given Block in
// counter mode. The length of iv must be the same as the Block's block size.
func NewCTR(block Block, iv []byte) Stream {
+ if len(iv) != block.BlockSize() {
+ panic("cipher.NewCTR: iv length must equal block size")
+ }
+
return &ctr{
b: block,
ctr: dup(iv),
diff --git a/src/pkg/crypto/block/Makefile b/src/pkg/crypto/des/Makefile
index 71c7aff64..94b0fc0fa 100644
--- a/src/pkg/crypto/block/Makefile
+++ b/src/pkg/crypto/des/Makefile
@@ -1,19 +1,13 @@
-# Copyright 2009 The Go Authors. All rights reserved.
+# 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.
include ../../../Make.inc
-TARG=crypto/block
+TARG=crypto/des
GOFILES=\
- cbc.go\
- cfb.go\
+ block.go\
cipher.go\
- cmac.go\
- ctr.go\
- eax.go\
- ecb.go\
- ofb.go\
- xor.go\
+ const.go\
include ../../../Make.pkg
diff --git a/src/pkg/crypto/des/block.go b/src/pkg/crypto/des/block.go
new file mode 100644
index 000000000..e18eaedf5
--- /dev/null
+++ b/src/pkg/crypto/des/block.go
@@ -0,0 +1,98 @@
+// 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 des
+
+import (
+ "encoding/binary"
+)
+
+func cryptBlock(subkeys []uint64, dst, src []byte, decrypt bool) {
+ b := binary.BigEndian.Uint64(src)
+ b = permuteBlock(b, initialPermutation[:])
+ left, right := uint32(b>>32), uint32(b)
+
+ var subkey uint64
+ for i := 0; i < 16; i++ {
+ if decrypt {
+ subkey = subkeys[15-i]
+ } else {
+ subkey = subkeys[i]
+ }
+
+ left, right = right, left^feistel(right, subkey)
+ }
+ // switch left & right and perform final permutation
+ preOutput := (uint64(right) << 32) | uint64(left)
+ binary.BigEndian.PutUint64(dst, permuteBlock(preOutput, finalPermutation[:]))
+}
+
+// Encrypt one block from src into dst, using the subkeys.
+func encryptBlock(subkeys []uint64, dst, src []byte) {
+ cryptBlock(subkeys, dst, src, false)
+}
+
+// Decrypt one block from src into dst, using the subkeys.
+func decryptBlock(subkeys []uint64, dst, src []byte) {
+ cryptBlock(subkeys, dst, src, true)
+}
+
+// DES Feistel function
+func feistel(right uint32, key uint64) (result uint32) {
+ sBoxLocations := key ^ permuteBlock(uint64(right), expansionFunction[:])
+ var sBoxResult uint32
+ for i := uint8(0); i < 8; i++ {
+ sBoxLocation := uint8(sBoxLocations>>42) & 0x3f
+ sBoxLocations <<= 6
+ // row determined by 1st and 6th bit
+ row := (sBoxLocation & 0x1) | ((sBoxLocation & 0x20) >> 4)
+ // column is middle four bits
+ column := (sBoxLocation >> 1) & 0xf
+ sBoxResult |= uint32(sBoxes[i][row][column]) << (4 * (7 - i))
+ }
+ return uint32(permuteBlock(uint64(sBoxResult), permutationFunction[:]))
+}
+
+// general purpose function to perform DES block permutations
+func permuteBlock(src uint64, permutation []uint8) (block uint64) {
+ for position, n := range permutation {
+ bit := (src >> n) & 1
+ block |= bit << uint((len(permutation)-1)-position)
+ }
+ return
+}
+
+// creates 16 28-bit blocks rotated according
+// to the rotation schedule
+func ksRotate(in uint32) (out []uint32) {
+ out = make([]uint32, 16)
+ last := in
+ for i := 0; i < 16; i++ {
+ // 28-bit circular left shift
+ left := (last << (4 + ksRotations[i])) >> 4
+ right := (last << 4) >> (32 - ksRotations[i])
+ out[i] = left | right
+ last = out[i]
+ }
+ return
+}
+
+// creates 16 56-bit subkeys from the original key
+func (c *Cipher) generateSubkeys(keyBytes []byte) {
+ // apply PC1 permutation to key
+ key := binary.BigEndian.Uint64(keyBytes)
+ permutedKey := permuteBlock(key, permutedChoice1[:])
+
+ // rotate halves of permuted key according to the rotation schedule
+ leftRotations := ksRotate(uint32(permutedKey >> 28))
+ rightRotations := ksRotate(uint32(permutedKey<<4) >> 4)
+
+ // generate subkeys
+ for i := 0; i < 16; i++ {
+ // combine halves to form 56-bit input to PC2
+ pc2Input := uint64(leftRotations[i])<<28 | uint64(rightRotations[i])
+ // apply PC2 permutation to 7 byte input
+ c.subkeys[i] = permuteBlock(pc2Input, permutedChoice2[:])
+ }
+}
diff --git a/src/pkg/crypto/des/cipher.go b/src/pkg/crypto/des/cipher.go
new file mode 100644
index 000000000..d17a1a783
--- /dev/null
+++ b/src/pkg/crypto/des/cipher.go
@@ -0,0 +1,103 @@
+// 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 des
+
+import (
+ "os"
+ "strconv"
+)
+
+// The DES block size in bytes.
+const BlockSize = 8
+
+type KeySizeError int
+
+func (k KeySizeError) String() string {
+ return "crypto/des: invalid key size " + strconv.Itoa(int(k))
+}
+
+// Cipher is an instance of DES encryption.
+type Cipher struct {
+ subkeys [16]uint64
+}
+
+// NewCipher creates and returns a new Cipher.
+func NewCipher(key []byte) (*Cipher, os.Error) {
+ if len(key) != 8 {
+ return nil, KeySizeError(len(key))
+ }
+
+ c := new(Cipher)
+ c.generateSubkeys(key)
+ return c, nil
+}
+
+// BlockSize returns the DES block size, 8 bytes.
+func (c *Cipher) BlockSize() int { return BlockSize }
+
+// Encrypts the 8-byte buffer src and stores the result in dst.
+// Note that for amounts of data larger than a block,
+// it is not safe to just call Encrypt on successive blocks;
+// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go).
+func (c *Cipher) Encrypt(dst, src []byte) { encryptBlock(c.subkeys[:], dst, src) }
+
+// Decrypts the 8-byte buffer src and stores the result in dst.
+func (c *Cipher) Decrypt(dst, src []byte) { decryptBlock(c.subkeys[:], dst, src) }
+
+// Reset zeros the key data, so that it will no longer
+// appear in the process's memory.
+func (c *Cipher) Reset() {
+ for i := 0; i < len(c.subkeys); i++ {
+ c.subkeys[i] = 0
+ }
+}
+
+// A TripleDESCipher is an instance of TripleDES encryption.
+type TripleDESCipher struct {
+ cipher1, cipher2, cipher3 Cipher
+}
+
+// NewCipher creates and returns a new Cipher.
+func NewTripleDESCipher(key []byte) (*TripleDESCipher, os.Error) {
+ if len(key) != 24 {
+ return nil, KeySizeError(len(key))
+ }
+
+ c := new(TripleDESCipher)
+ c.cipher1.generateSubkeys(key[:8])
+ c.cipher2.generateSubkeys(key[8:16])
+ c.cipher3.generateSubkeys(key[16:])
+ return c, nil
+}
+
+// BlockSize returns the TripleDES block size, 8 bytes.
+// It is necessary to satisfy the Block interface in the
+// package "crypto/cipher".
+func (c *TripleDESCipher) BlockSize() int { return BlockSize }
+
+// Encrypts the 8-byte buffer src and stores the result in dst.
+// Note that for amounts of data larger than a block,
+// it is not safe to just call Encrypt on successive blocks;
+// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go).
+func (c *TripleDESCipher) Encrypt(dst, src []byte) {
+ c.cipher1.Encrypt(dst, src)
+ c.cipher2.Decrypt(dst, dst)
+ c.cipher3.Encrypt(dst, dst)
+}
+
+// Decrypts the 8-byte buffer src and stores the result in dst.
+func (c *TripleDESCipher) Decrypt(dst, src []byte) {
+ c.cipher3.Decrypt(dst, src)
+ c.cipher2.Encrypt(dst, dst)
+ c.cipher1.Decrypt(dst, dst)
+}
+
+// Reset zeros the key data, so that it will no longer
+// appear in the process's memory.
+func (c *TripleDESCipher) Reset() {
+ c.cipher1.Reset()
+ c.cipher2.Reset()
+ c.cipher3.Reset()
+}
diff --git a/src/pkg/crypto/des/const.go b/src/pkg/crypto/des/const.go
new file mode 100644
index 000000000..2bd485ee8
--- /dev/null
+++ b/src/pkg/crypto/des/const.go
@@ -0,0 +1,139 @@
+// 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.
+
+// Package des implements the Data Encryption Standard (DES) and the
+// Triple Data Encryption Algorithm (TDEA) as defined
+// in U.S. Federal Information Processing Standards Publication 46-3.
+package des
+
+// Used to perform an initial permutation of a 64-bit input block.
+var initialPermutation = [64]byte{
+ 6, 14, 22, 30, 38, 46, 54, 62,
+ 4, 12, 20, 28, 36, 44, 52, 60,
+ 2, 10, 18, 26, 34, 42, 50, 58,
+ 0, 8, 16, 24, 32, 40, 48, 56,
+ 7, 15, 23, 31, 39, 47, 55, 63,
+ 5, 13, 21, 29, 37, 45, 53, 61,
+ 3, 11, 19, 27, 35, 43, 51, 59,
+ 1, 9, 17, 25, 33, 41, 49, 57,
+}
+
+// Used to perform a final permutation of a 4-bit preoutput block. This is the
+// inverse of initialPermutation
+var finalPermutation = [64]byte{
+ 24, 56, 16, 48, 8, 40, 0, 32,
+ 25, 57, 17, 49, 9, 41, 1, 33,
+ 26, 58, 18, 50, 10, 42, 2, 34,
+ 27, 59, 19, 51, 11, 43, 3, 35,
+ 28, 60, 20, 52, 12, 44, 4, 36,
+ 29, 61, 21, 53, 13, 45, 5, 37,
+ 30, 62, 22, 54, 14, 46, 6, 38,
+ 31, 63, 23, 55, 15, 47, 7, 39,
+}
+
+// Used to expand an input block of 32 bits, producing an output block of 48
+// bits.
+var expansionFunction = [48]byte{
+ 0, 31, 30, 29, 28, 27, 28, 27,
+ 26, 25, 24, 23, 24, 23, 22, 21,
+ 20, 19, 20, 19, 18, 17, 16, 15,
+ 16, 15, 14, 13, 12, 11, 12, 11,
+ 10, 9, 8, 7, 8, 7, 6, 5,
+ 4, 3, 4, 3, 2, 1, 0, 31,
+}
+
+// Yields a 32-bit output from a 32-bit input
+var permutationFunction = [32]byte{
+ 16, 25, 12, 11, 3, 20, 4, 15,
+ 31, 17, 9, 6, 27, 14, 1, 22,
+ 30, 24, 8, 18, 0, 5, 29, 23,
+ 13, 19, 2, 26, 10, 21, 28, 7,
+}
+
+// Used in the key schedule to select 56 bits
+// from a 64-bit input.
+var permutedChoice1 = [56]byte{
+ 7, 15, 23, 31, 39, 47, 55, 63,
+ 6, 14, 22, 30, 38, 46, 54, 62,
+ 5, 13, 21, 29, 37, 45, 53, 61,
+ 4, 12, 20, 28, 1, 9, 17, 25,
+ 33, 41, 49, 57, 2, 10, 18, 26,
+ 34, 42, 50, 58, 3, 11, 19, 27,
+ 35, 43, 51, 59, 36, 44, 52, 60,
+}
+
+// Used in the key schedule to produce each subkey by selecting 48 bits from
+// the 56-bit input
+var permutedChoice2 = [48]byte{
+ 42, 39, 45, 32, 55, 51, 53, 28,
+ 41, 50, 35, 46, 33, 37, 44, 52,
+ 30, 48, 40, 49, 29, 36, 43, 54,
+ 15, 4, 25, 19, 9, 1, 26, 16,
+ 5, 11, 23, 8, 12, 7, 17, 0,
+ 22, 3, 10, 14, 6, 20, 27, 24,
+}
+
+// 8 S-boxes composed of 4 rows and 16 columns
+// Used in the DES cipher function
+var sBoxes = [8][4][16]uint8{
+ // S-box 1
+ {
+ {14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7},
+ {0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8},
+ {4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0},
+ {15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13},
+ },
+ // S-box 2
+ {
+ {15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10},
+ {3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5},
+ {0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15},
+ {13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9},
+ },
+ // S-box 3
+ {
+ {10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8},
+ {13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1},
+ {13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7},
+ {1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12},
+ },
+ // S-box 4
+ {
+ {7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15},
+ {13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9},
+ {10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4},
+ {3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14},
+ },
+ // S-box 5
+ {
+ {2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9},
+ {14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6},
+ {4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14},
+ {11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3},
+ },
+ // S-box 6
+ {
+ {12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11},
+ {10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8},
+ {9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6},
+ {4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13},
+ },
+ // S-box 7
+ {
+ {4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1},
+ {13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6},
+ {1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2},
+ {6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12},
+ },
+ // S-box 8
+ {
+ {13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7},
+ {1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2},
+ {7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8},
+ {2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11},
+ },
+}
+
+// Size of left rotation per round in each half of the key schedule
+var ksRotations = [16]uint8{1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1}
diff --git a/src/pkg/crypto/des/des_test.go b/src/pkg/crypto/des/des_test.go
new file mode 100644
index 000000000..d1f3aa71a
--- /dev/null
+++ b/src/pkg/crypto/des/des_test.go
@@ -0,0 +1,1497 @@
+// 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 des
+
+import (
+ "bytes"
+ "testing"
+)
+
+type CryptTest struct {
+ key []byte
+ in []byte
+ out []byte
+}
+
+// some custom tests for DES
+var encryptDESTests = []CryptTest{
+ {
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0x8c, 0xa6, 0x4d, 0xe9, 0xc1, 0xb1, 0x23, 0xa7}},
+ {
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ []byte{0x35, 0x55, 0x50, 0xb2, 0x15, 0x0e, 0x24, 0x51}},
+ {
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef},
+ []byte{0x61, 0x7b, 0x3a, 0x0c, 0xe8, 0xf0, 0x71, 0x00}},
+ {
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
+ []byte{0x92, 0x31, 0xf2, 0x36, 0xff, 0x9a, 0xa9, 0x5c}},
+ {
+ []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0xca, 0xaa, 0xaf, 0x4d, 0xea, 0xf1, 0xdb, 0xae}},
+ {
+ []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ []byte{0x73, 0x59, 0xb2, 0x16, 0x3e, 0x4e, 0xdc, 0x58}},
+ {
+ []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef},
+ []byte{0x6d, 0xce, 0x0d, 0xc9, 0x00, 0x65, 0x56, 0xa3}},
+ {
+ []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ []byte{0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
+ []byte{0x9e, 0x84, 0xc5, 0xf3, 0x17, 0x0f, 0x8e, 0xff}},
+ {
+ []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef},
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0xd5, 0xd4, 0x4f, 0xf7, 0x20, 0x68, 0x3d, 0x0d}},
+ {
+ []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef},
+ []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ []byte{0x59, 0x73, 0x23, 0x56, 0xf3, 0x6f, 0xde, 0x06}},
+ {
+ []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef},
+ []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef},
+ []byte{0x56, 0xcc, 0x09, 0xe7, 0xcf, 0xdc, 0x4c, 0xef}},
+ {
+ []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef},
+ []byte{0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
+ []byte{0x12, 0xc6, 0x26, 0xaf, 0x05, 0x8b, 0x43, 0x3b}},
+ {
+ []byte{0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0xa6, 0x8c, 0xdc, 0xa9, 0x0c, 0x90, 0x21, 0xf9}},
+ {
+ []byte{0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
+ []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ []byte{0x2a, 0x2b, 0xb0, 0x08, 0xdf, 0x97, 0xc2, 0xf2}},
+ {
+ []byte{0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
+ []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef},
+ []byte{0xed, 0x39, 0xd9, 0x50, 0xfa, 0x74, 0xbc, 0xc4}},
+ {
+ []byte{0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
+ []byte{0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
+ []byte{0xa9, 0x33, 0xf6, 0x18, 0x30, 0x23, 0xb3, 0x10}},
+ {
+ []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef},
+ []byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11},
+ []byte{0x17, 0x66, 0x8d, 0xfc, 0x72, 0x92, 0x53, 0x2d}},
+ {
+ []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef},
+ []byte{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01},
+ []byte{0xb4, 0xfd, 0x23, 0x16, 0x47, 0xa5, 0xbe, 0xc0}},
+ {
+ []byte{0x0e, 0x32, 0x92, 0x32, 0xea, 0x6d, 0x0d, 0x73},
+ []byte{0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87},
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+ {
+ []byte{0x73, 0x65, 0x63, 0x52, 0x33, 0x74, 0x24, 0x3b}, // "secR3t$;"
+ []byte{0x61, 0x20, 0x74, 0x65, 0x73, 0x74, 0x31, 0x32}, // "a test12"
+ []byte{0x37, 0x0d, 0xee, 0x2c, 0x1f, 0xb4, 0xf7, 0xa5}},
+ {
+ []byte{0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68}, // "abcdefgh"
+ []byte{0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68}, // "abcdefgh"
+ []byte{0x2a, 0x8d, 0x69, 0xde, 0x9d, 0x5f, 0xdf, 0xf9}},
+ {
+ []byte{0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68}, // "abcdefgh"
+ []byte{0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38}, // "12345678"
+ []byte{0x21, 0xc6, 0x0d, 0xa5, 0x34, 0x24, 0x8b, 0xce}},
+ {
+ []byte{0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38}, // "12345678"
+ []byte{0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68}, // "abcdefgh"
+ []byte{0x94, 0xd4, 0x43, 0x6b, 0xc3, 0xb5, 0xb6, 0x93}},
+ {
+ []byte{0x1f, 0x79, 0x90, 0x5f, 0x88, 0x01, 0xc8, 0x88}, // random
+ []byte{0xc7, 0x46, 0x18, 0x73, 0xaf, 0x48, 0x5f, 0xb3}, // random
+ []byte{0xb0, 0x93, 0x50, 0x88, 0xf9, 0x92, 0x44, 0x6a}},
+ {
+ []byte{0xe6, 0xf4, 0xf2, 0xdb, 0x31, 0x42, 0x53, 0x01}, // random
+ []byte{0xff, 0x3d, 0x25, 0x50, 0x12, 0xe3, 0x4a, 0xc5}, // random
+ []byte{0x86, 0x08, 0xd3, 0xd1, 0x6c, 0x2f, 0xd2, 0x55}},
+ {
+ []byte{0x69, 0xc1, 0x9d, 0xc1, 0x15, 0xc5, 0xfb, 0x2b}, // random
+ []byte{0x1a, 0x22, 0x5c, 0xaf, 0x1f, 0x1d, 0xa3, 0xf9}, // random
+ []byte{0x64, 0xba, 0x31, 0x67, 0x56, 0x91, 0x1e, 0xa7}},
+ {
+ []byte{0x6e, 0x5e, 0xe2, 0x47, 0xc4, 0xbf, 0xf6, 0x51}, // random
+ []byte{0x11, 0xc9, 0x57, 0xff, 0x66, 0x89, 0x0e, 0xf0}, // random
+ []byte{0x94, 0xc5, 0x35, 0xb2, 0xc5, 0x8b, 0x39, 0x72}},
+}
+
+var weakKeyTests = []CryptTest{
+ {
+ []byte{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01},
+ []byte{0x55, 0x74, 0xc0, 0xbd, 0x7c, 0xdf, 0xf7, 0x39}, // random
+ nil},
+ {
+ []byte{0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe},
+ []byte{0xe8, 0xe1, 0xa7, 0xc1, 0xde, 0x11, 0x89, 0xaa}, // random
+ nil},
+ {
+ []byte{0xe0, 0xe0, 0xe0, 0xe0, 0xf1, 0xf1, 0xf1, 0xf1},
+ []byte{0x50, 0x6a, 0x4b, 0x94, 0x3b, 0xed, 0x7d, 0xdc}, // random
+ nil},
+ {
+ []byte{0x1f, 0x1f, 0x1f, 0x1f, 0x0e, 0x0e, 0x0e, 0x0e},
+ []byte{0x88, 0x81, 0x56, 0x38, 0xec, 0x3b, 0x1c, 0x97}, // random
+ nil},
+ {
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0x17, 0xa0, 0x83, 0x62, 0x32, 0xfe, 0x9a, 0x0b}, // random
+ nil},
+ {
+ []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ []byte{0xca, 0x8f, 0xca, 0x1f, 0x50, 0xc5, 0x7b, 0x49}, // random
+ nil},
+ {
+ []byte{0xe1, 0xe1, 0xe1, 0xe1, 0xf0, 0xf0, 0xf0, 0xf0},
+ []byte{0xb1, 0xea, 0xad, 0x7d, 0xe7, 0xc3, 0x7a, 0x43}, // random
+ nil},
+ {
+ []byte{0x1e, 0x1e, 0x1e, 0x1e, 0x0f, 0x0f, 0x0f, 0x0f},
+ []byte{0xae, 0x74, 0x7d, 0x6f, 0xef, 0x16, 0xbb, 0x81}, // random
+ nil},
+}
+
+var semiWeakKeyTests = []CryptTest{
+ // key and out contain the semi-weak key pair
+ {
+ []byte{0x01, 0x1f, 0x01, 0x1f, 0x01, 0x0e, 0x01, 0x0e},
+ []byte{0x12, 0xfa, 0x31, 0x16, 0xf9, 0xc5, 0x0a, 0xe4}, // random
+ []byte{0x1f, 0x01, 0x1f, 0x01, 0x0e, 0x01, 0x0e, 0x01}},
+ {
+ []byte{0x01, 0xe0, 0x01, 0xe0, 0x01, 0xf1, 0x01, 0xf1},
+ []byte{0xb0, 0x4c, 0x7a, 0xee, 0xd2, 0xe5, 0x4d, 0xb7}, // random
+ []byte{0xe0, 0x01, 0xe0, 0x01, 0xf1, 0x01, 0xf1, 0x01}},
+ {
+ []byte{0x01, 0xfe, 0x01, 0xfe, 0x01, 0xfe, 0x01, 0xfe},
+ []byte{0xa4, 0x81, 0xcd, 0xb1, 0x64, 0x6f, 0xd3, 0xbc}, // random
+ []byte{0xfe, 0x01, 0xfe, 0x01, 0xfe, 0x01, 0xfe, 0x01}},
+ {
+ []byte{0x1f, 0xe0, 0x1f, 0xe0, 0x0e, 0xf1, 0x0e, 0xf1},
+ []byte{0xee, 0x27, 0xdd, 0x88, 0x4c, 0x22, 0xcd, 0xce}, // random
+ []byte{0xe0, 0x1f, 0xe0, 0x1f, 0xf1, 0x0e, 0xf1, 0x0e}},
+ {
+ []byte{0x1f, 0xfe, 0x1f, 0xfe, 0x0e, 0xfe, 0x0e, 0xfe},
+ []byte{0x19, 0x3d, 0xcf, 0x97, 0x70, 0xfb, 0xab, 0xe1}, // random
+ []byte{0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x0e, 0xfe, 0x0e}},
+ {
+ []byte{0xe0, 0xfe, 0xe0, 0xfe, 0xf1, 0xfe, 0xf1, 0xfe},
+ []byte{0x7c, 0x82, 0x69, 0xe4, 0x1e, 0x86, 0x99, 0xd7}, // random
+ []byte{0xfe, 0xe0, 0xfe, 0xe0, 0xfe, 0xf1, 0xfe, 0xf1}},
+}
+
+// some custom tests for TripleDES
+var encryptTripleDESTests = []CryptTest{
+ {
+ []byte{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0x92, 0x95, 0xb5, 0x9b, 0xb3, 0x84, 0x73, 0x6e}},
+ {
+ []byte{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ []byte{0xc1, 0x97, 0xf5, 0x58, 0x74, 0x8a, 0x20, 0xe7}},
+ {
+ []byte{
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0x3e, 0x68, 0x0a, 0xa7, 0x8b, 0x75, 0xdf, 0x18}},
+ {
+ []byte{
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ []byte{0x6d, 0x6a, 0x4a, 0x64, 0x4c, 0x7b, 0x8c, 0x91}},
+ {
+ []byte{ // "abcdefgh12345678ABCDEFGH"
+ 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
+ 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
+ 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48},
+ []byte{0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30}, // "00000000"
+ []byte{0xe4, 0x61, 0xb7, 0x59, 0x68, 0x8b, 0xff, 0x66}},
+ {
+ []byte{ // "abcdefgh12345678ABCDEFGH"
+ 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
+ 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
+ 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48},
+ []byte{0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38}, // "12345678"
+ []byte{0xdb, 0xd0, 0x92, 0xde, 0xf8, 0x34, 0xff, 0x58}},
+ {
+ []byte{ // "abcdefgh12345678ABCDEFGH"
+ 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
+ 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
+ 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48},
+ []byte{0xf0, 0xc5, 0x82, 0x22, 0xd3, 0xe6, 0x12, 0xd2}, // random
+ []byte{0xba, 0xe4, 0x41, 0xb1, 0x3c, 0x37, 0x4d, 0xf4}},
+ {
+ []byte{ // random
+ 0xd3, 0x7d, 0x45, 0xee, 0x22, 0xe9, 0xcf, 0x52,
+ 0xf4, 0x65, 0xa2, 0x4f, 0x70, 0xd1, 0x81, 0x8a,
+ 0x3d, 0xbe, 0x2f, 0x39, 0xc7, 0x71, 0xd2, 0xe9},
+ []byte{0x49, 0x53, 0xc3, 0xe9, 0x78, 0xdf, 0x9f, 0xaf}, // random
+ []byte{0x53, 0x40, 0x51, 0x24, 0xd8, 0x3c, 0xf9, 0x88}},
+ {
+ []byte{ // random
+ 0xcb, 0x10, 0x7d, 0xda, 0x7e, 0x96, 0x57, 0x0a,
+ 0xe8, 0xeb, 0xe8, 0x07, 0x8e, 0x87, 0xd3, 0x57,
+ 0xb2, 0x61, 0x12, 0xb8, 0x2a, 0x90, 0xb7, 0x2f},
+ []byte{0xa3, 0xc2, 0x60, 0xb1, 0x0b, 0xb7, 0x28, 0x6e}, // random
+ []byte{0x56, 0x73, 0x7d, 0xfb, 0xb5, 0xa1, 0xc3, 0xde}},
+}
+
+// NIST Special Publication 800-20, Appendix A
+// Key for use with Table A.1 tests
+var tableA1Key = []byte{
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+}
+
+// Table A.1 Resulting Ciphertext from the Variable Plaintext Known Answer Test
+var tableA1Tests = []CryptTest{
+ {nil, // 0
+ []byte{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0x95, 0xf8, 0xa5, 0xe5, 0xdd, 0x31, 0xd9, 0x00}},
+ {nil, // 1
+ []byte{0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0xdd, 0x7f, 0x12, 0x1c, 0xa5, 0x01, 0x56, 0x19}},
+ {nil, // 2
+ []byte{0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0x2e, 0x86, 0x53, 0x10, 0x4f, 0x38, 0x34, 0xea}},
+ {nil, // 3
+ []byte{0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0x4b, 0xd3, 0x88, 0xff, 0x6c, 0xd8, 0x1d, 0x4f}},
+ {nil, // 4
+ []byte{0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0x20, 0xb9, 0xe7, 0x67, 0xb2, 0xfb, 0x14, 0x56}},
+ {nil, // 5
+ []byte{0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0x55, 0x57, 0x93, 0x80, 0xd7, 0x71, 0x38, 0xef}},
+ {nil, // 6
+ []byte{0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0x6c, 0xc5, 0xde, 0xfa, 0xaf, 0x04, 0x51, 0x2f}},
+ {nil, // 7
+ []byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0x0d, 0x9f, 0x27, 0x9b, 0xa5, 0xd8, 0x72, 0x60}},
+ {nil, // 8
+ []byte{0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0xd9, 0x03, 0x1b, 0x02, 0x71, 0xbd, 0x5a, 0x0a}},
+ {nil, // 9
+ []byte{0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0x42, 0x42, 0x50, 0xb3, 0x7c, 0x3d, 0xd9, 0x51}},
+ {nil, // 10
+ []byte{0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0xb8, 0x06, 0x1b, 0x7e, 0xcd, 0x9a, 0x21, 0xe5}},
+ {nil, // 11
+ []byte{0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0xf1, 0x5d, 0x0f, 0x28, 0x6b, 0x65, 0xbd, 0x28}},
+ {nil, // 12
+ []byte{0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0xad, 0xd0, 0xcc, 0x8d, 0x6e, 0x5d, 0xeb, 0xa1}},
+ {nil, // 13
+ []byte{0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0xe6, 0xd5, 0xf8, 0x27, 0x52, 0xad, 0x63, 0xd1}},
+ {nil, // 14
+ []byte{0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0xec, 0xbf, 0xe3, 0xbd, 0x3f, 0x59, 0x1a, 0x5e}},
+ {nil, // 15
+ []byte{0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0xf3, 0x56, 0x83, 0x43, 0x79, 0xd1, 0x65, 0xcd}},
+ {nil, // 16
+ []byte{0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0x2b, 0x9f, 0x98, 0x2f, 0x20, 0x03, 0x7f, 0xa9}},
+ {nil, // 17
+ []byte{0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0x88, 0x9d, 0xe0, 0x68, 0xa1, 0x6f, 0x0b, 0xe6}},
+ {nil, // 18
+ []byte{0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0xe1, 0x9e, 0x27, 0x5d, 0x84, 0x6a, 0x12, 0x98}},
+ {nil, // 19
+ []byte{0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0x32, 0x9a, 0x8e, 0xd5, 0x23, 0xd7, 0x1a, 0xec}},
+ {nil, // 20
+ []byte{0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0xe7, 0xfc, 0xe2, 0x25, 0x57, 0xd2, 0x3c, 0x97}},
+ {nil, // 21
+ []byte{0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0x12, 0xa9, 0xf5, 0x81, 0x7f, 0xf2, 0xd6, 0x5d}},
+ {nil, // 22
+ []byte{0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0xa4, 0x84, 0xc3, 0xad, 0x38, 0xdc, 0x9c, 0x19}},
+ {nil, // 23
+ []byte{0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0xfb, 0xe0, 0x0a, 0x8a, 0x1e, 0xf8, 0xad, 0x72}},
+ {nil, // 24
+ []byte{0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00},
+ []byte{0x75, 0x0d, 0x07, 0x94, 0x07, 0x52, 0x13, 0x63}},
+ {nil, // 25
+ []byte{0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00},
+ []byte{0x64, 0xfe, 0xed, 0x9c, 0x72, 0x4c, 0x2f, 0xaf}},
+ {nil, // 26
+ []byte{0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00},
+ []byte{0xf0, 0x2b, 0x26, 0x3b, 0x32, 0x8e, 0x2b, 0x60}},
+ {nil, // 27
+ []byte{0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00},
+ []byte{0x9d, 0x64, 0x55, 0x5a, 0x9a, 0x10, 0xb8, 0x52}},
+ {nil, // 28
+ []byte{0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00},
+ []byte{0xd1, 0x06, 0xff, 0x0b, 0xed, 0x52, 0x55, 0xd7}},
+ {nil, // 29
+ []byte{0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00},
+ []byte{0xe1, 0x65, 0x2c, 0x6b, 0x13, 0x8c, 0x64, 0xa5}},
+ {nil, // 30
+ []byte{0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00},
+ []byte{0xe4, 0x28, 0x58, 0x11, 0x86, 0xec, 0x8f, 0x46}},
+ {nil, // 31
+ []byte{0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00},
+ []byte{0xae, 0xb5, 0xf5, 0xed, 0xe2, 0x2d, 0x1a, 0x36}},
+ {nil, // 32
+ []byte{0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00},
+ []byte{0xe9, 0x43, 0xd7, 0x56, 0x8a, 0xec, 0x0c, 0x5c}},
+ {nil, // 33
+ []byte{0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00},
+ []byte{0xdf, 0x98, 0xc8, 0x27, 0x6f, 0x54, 0xb0, 0x4b}},
+ {nil, // 34
+ []byte{0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00},
+ []byte{0xb1, 0x60, 0xe4, 0x68, 0x0f, 0x6c, 0x69, 0x6f}},
+ {nil, // 35
+ []byte{0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00},
+ []byte{0xfa, 0x07, 0x52, 0xb0, 0x7d, 0x9c, 0x4a, 0xb8}},
+ {nil, // 36
+ []byte{0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00},
+ []byte{0xca, 0x3a, 0x2b, 0x03, 0x6d, 0xbc, 0x85, 0x02}},
+ {nil, // 37
+ []byte{0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00},
+ []byte{0x5e, 0x09, 0x05, 0x51, 0x7b, 0xb5, 0x9b, 0xcf}},
+ {nil, // 38
+ []byte{0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00},
+ []byte{0x81, 0x4e, 0xeb, 0x3b, 0x91, 0xd9, 0x07, 0x26}},
+ {nil, // 39
+ []byte{0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00},
+ []byte{0x4d, 0x49, 0xdb, 0x15, 0x32, 0x91, 0x9c, 0x9f}},
+ {nil, // 40
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00},
+ []byte{0x25, 0xeb, 0x5f, 0xc3, 0xf8, 0xcf, 0x06, 0x21}},
+ {nil, // 41
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00},
+ []byte{0xab, 0x6a, 0x20, 0xc0, 0x62, 0x0d, 0x1c, 0x6f}},
+ {nil, // 42
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00},
+ []byte{0x79, 0xe9, 0x0d, 0xbc, 0x98, 0xf9, 0x2c, 0xca}},
+ {nil, // 43
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00},
+ []byte{0x86, 0x6e, 0xce, 0xdd, 0x80, 0x72, 0xbb, 0x0e}},
+ {nil, // 44
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00},
+ []byte{0x8b, 0x54, 0x53, 0x6f, 0x2f, 0x3e, 0x64, 0xa8}},
+ {nil, // 45
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00},
+ []byte{0xea, 0x51, 0xd3, 0x97, 0x55, 0x95, 0xb8, 0x6b}},
+ {nil, // 46
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00},
+ []byte{0xca, 0xff, 0xc6, 0xac, 0x45, 0x42, 0xde, 0x31}},
+ {nil, // 47
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00},
+ []byte{0x8d, 0xd4, 0x5a, 0x2d, 0xdf, 0x90, 0x79, 0x6c}},
+ {nil, // 48
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00},
+ []byte{0x10, 0x29, 0xd5, 0x5e, 0x88, 0x0e, 0xc2, 0xd0}},
+ {nil, // 49
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00},
+ []byte{0x5d, 0x86, 0xcb, 0x23, 0x63, 0x9d, 0xbe, 0xa9}},
+ {nil, // 50
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00},
+ []byte{0x1d, 0x1c, 0xa8, 0x53, 0xae, 0x7c, 0x0c, 0x5f}},
+ {nil, // 51
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00},
+ []byte{0xce, 0x33, 0x23, 0x29, 0x24, 0x8f, 0x32, 0x28}},
+ {nil, // 52
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00},
+ []byte{0x84, 0x05, 0xd1, 0xab, 0xe2, 0x4f, 0xb9, 0x42}},
+ {nil, // 53
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00},
+ []byte{0xe6, 0x43, 0xd7, 0x80, 0x90, 0xca, 0x42, 0x07}},
+ {nil, // 54
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00},
+ []byte{0x48, 0x22, 0x1b, 0x99, 0x37, 0x74, 0x8a, 0x23}},
+ {nil, // 55
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00},
+ []byte{0xdd, 0x7c, 0x0b, 0xbd, 0x61, 0xfa, 0xfd, 0x54}},
+ {nil, // 56
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80},
+ []byte{0x2f, 0xbc, 0x29, 0x1a, 0x57, 0x0d, 0xb5, 0xc4}},
+ {nil, // 57
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40},
+ []byte{0xe0, 0x7c, 0x30, 0xd7, 0xe4, 0xe2, 0x6e, 0x12}},
+ {nil, // 58
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20},
+ []byte{0x09, 0x53, 0xe2, 0x25, 0x8e, 0x8e, 0x90, 0xa1}},
+ {nil, // 59
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10},
+ []byte{0x5b, 0x71, 0x1b, 0xc4, 0xce, 0xeb, 0xf2, 0xee}},
+ {nil, // 60
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08},
+ []byte{0xcc, 0x08, 0x3f, 0x1e, 0x6d, 0x9e, 0x85, 0xf6}},
+ {nil, // 61
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04},
+ []byte{0xd2, 0xfd, 0x88, 0x67, 0xd5, 0x0d, 0x2d, 0xfe}},
+ {nil, // 62
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02},
+ []byte{0x06, 0xe7, 0xea, 0x22, 0xce, 0x92, 0x70, 0x8f}},
+ {nil, // 63
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
+ []byte{0x16, 0x6b, 0x40, 0xb4, 0x4a, 0xba, 0x4b, 0xd6}},
+}
+
+// Plaintext for use with Table A.2 tests
+var tableA2Plaintext = []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
+
+// Table A.2 Resulting Ciphertext from the Variable Key Known Answer Test
+var tableA2Tests = []CryptTest{
+ { // 0
+ []byte{
+ 0x80, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x80, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x80, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01},
+ nil,
+ []byte{0x95, 0xa8, 0xd7, 0x28, 0x13, 0xda, 0xa9, 0x4d}},
+ { // 1
+ []byte{
+ 0x40, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x40, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x40, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01},
+ nil,
+ []byte{0x0e, 0xec, 0x14, 0x87, 0xdd, 0x8c, 0x26, 0xd5}},
+ { // 2
+ []byte{
+ 0x20, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x20, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x20, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01},
+ nil,
+ []byte{0x7a, 0xd1, 0x6f, 0xfb, 0x79, 0xc4, 0x59, 0x26}},
+ { // 3
+ []byte{
+ 0x10, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x10, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x10, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01},
+ nil,
+ []byte{0xd3, 0x74, 0x62, 0x94, 0xca, 0x6a, 0x6c, 0xf3}},
+ { // 4
+ []byte{
+ 0x08, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x08, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x08, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01},
+ nil,
+ []byte{0x80, 0x9f, 0x5f, 0x87, 0x3c, 0x1f, 0xd7, 0x61}},
+ { // 5
+ []byte{
+ 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01},
+ nil,
+ []byte{0xc0, 0x2f, 0xaf, 0xfe, 0xc9, 0x89, 0xd1, 0xfc}},
+ { // 6
+ []byte{
+ 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01},
+ nil,
+ []byte{0x46, 0x15, 0xaa, 0x1d, 0x33, 0xe7, 0x2f, 0x10}},
+ { // 7
+ []byte{
+ 0x01, 0x80, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x80, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x80, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01},
+ nil,
+ []byte{0x20, 0x55, 0x12, 0x33, 0x50, 0xc0, 0x08, 0x58}},
+ { // 8
+ []byte{
+ 0x01, 0x40, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x40, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x40, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01},
+ nil,
+ []byte{0xdf, 0x3b, 0x99, 0xd6, 0x57, 0x73, 0x97, 0xc8}},
+ { // 9
+ []byte{
+ 0x01, 0x20, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x20, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x20, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01},
+ nil,
+ []byte{0x31, 0xfe, 0x17, 0x36, 0x9b, 0x52, 0x88, 0xc9}},
+ { // 10
+ []byte{
+ 0x01, 0x10, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x10, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x10, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01},
+ nil,
+ []byte{0xdf, 0xdd, 0x3c, 0xc6, 0x4d, 0xae, 0x16, 0x42}},
+ { // 11
+ []byte{
+ 0x01, 0x08, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x08, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x08, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01},
+ nil,
+ []byte{0x17, 0x8c, 0x83, 0xce, 0x2b, 0x39, 0x9d, 0x94}},
+ { // 12
+ []byte{
+ 0x01, 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01},
+ nil,
+ []byte{0x50, 0xf6, 0x36, 0x32, 0x4a, 0x9b, 0x7f, 0x80}},
+ { // 13
+ []byte{
+ 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01},
+ nil,
+ []byte{0xa8, 0x46, 0x8e, 0xe3, 0xbc, 0x18, 0xf0, 0x6d}},
+ { // 14
+ []byte{
+ 0x01, 0x01, 0x80, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x80, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x80, 0x01, 0x01, 0x01, 0x01, 0x01},
+ nil,
+ []byte{0xa2, 0xdc, 0x9e, 0x92, 0xfd, 0x3c, 0xde, 0x92}},
+ { // 15
+ []byte{
+ 0x01, 0x01, 0x40, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x40, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x40, 0x01, 0x01, 0x01, 0x01, 0x01},
+ nil,
+ []byte{0xca, 0xc0, 0x9f, 0x79, 0x7d, 0x03, 0x12, 0x87}},
+ { // 16
+ []byte{
+ 0x01, 0x01, 0x20, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x20, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x20, 0x01, 0x01, 0x01, 0x01, 0x01},
+ nil,
+ []byte{0x90, 0xba, 0x68, 0x0b, 0x22, 0xae, 0xb5, 0x25}},
+ { // 17
+ []byte{
+ 0x01, 0x01, 0x10, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x10, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x10, 0x01, 0x01, 0x01, 0x01, 0x01},
+ nil,
+ []byte{0xce, 0x7a, 0x24, 0xf3, 0x50, 0xe2, 0x80, 0xb6}},
+ { // 18
+ []byte{
+ 0x01, 0x01, 0x08, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x08, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x08, 0x01, 0x01, 0x01, 0x01, 0x01},
+ nil,
+ []byte{0x88, 0x2b, 0xff, 0x0a, 0xa0, 0x1a, 0x0b, 0x87}},
+ { // 19
+ []byte{
+ 0x01, 0x01, 0x04, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x04, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x04, 0x01, 0x01, 0x01, 0x01, 0x01},
+ nil,
+ []byte{0x25, 0x61, 0x02, 0x88, 0x92, 0x45, 0x11, 0xc2}},
+ { // 20
+ []byte{
+ 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01},
+ nil,
+ []byte{0xc7, 0x15, 0x16, 0xc2, 0x9c, 0x75, 0xd1, 0x70}},
+ { // 21
+ []byte{
+ 0x01, 0x01, 0x01, 0x80, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x80, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x80, 0x01, 0x01, 0x01, 0x01},
+ nil,
+ []byte{0x51, 0x99, 0xc2, 0x9a, 0x52, 0xc9, 0xf0, 0x59}},
+ { // 22
+ []byte{
+ 0x01, 0x01, 0x01, 0x40, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x40, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x40, 0x01, 0x01, 0x01, 0x01},
+ nil,
+ []byte{0xc2, 0x2f, 0x0a, 0x29, 0x4a, 0x71, 0xf2, 0x9f}},
+ { // 23
+ []byte{
+ 0x01, 0x01, 0x01, 0x20, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x20, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x20, 0x01, 0x01, 0x01, 0x01},
+ nil,
+ []byte{0xee, 0x37, 0x14, 0x83, 0x71, 0x4c, 0x02, 0xea}},
+ { // 24
+ []byte{
+ 0x01, 0x01, 0x01, 0x10, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x10, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x10, 0x01, 0x01, 0x01, 0x01},
+ nil,
+ []byte{0xa8, 0x1f, 0xbd, 0x44, 0x8f, 0x9e, 0x52, 0x2f}},
+ { // 25
+ []byte{
+ 0x01, 0x01, 0x01, 0x08, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x08, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x08, 0x01, 0x01, 0x01, 0x01},
+ nil,
+ []byte{0x4f, 0x64, 0x4c, 0x92, 0xe1, 0x92, 0xdf, 0xed}},
+ { // 26
+ []byte{
+ 0x01, 0x01, 0x01, 0x04, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x04, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x04, 0x01, 0x01, 0x01, 0x01},
+ nil,
+ []byte{0x1a, 0xfa, 0x9a, 0x66, 0xa6, 0xdf, 0x92, 0xae}},
+ { // 27
+ []byte{
+ 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01},
+ nil,
+ []byte{0xb3, 0xc1, 0xcc, 0x71, 0x5c, 0xb8, 0x79, 0xd8}},
+ { // 28
+ []byte{
+ 0x01, 0x01, 0x01, 0x01, 0x80, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x80, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x80, 0x01, 0x01, 0x01},
+ nil,
+ []byte{0x19, 0xd0, 0x32, 0xe6, 0x4a, 0xb0, 0xbd, 0x8b}},
+ { // 29
+ []byte{
+ 0x01, 0x01, 0x01, 0x01, 0x40, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x40, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x40, 0x01, 0x01, 0x01},
+ nil,
+ []byte{0x3c, 0xfa, 0xa7, 0xa7, 0xdc, 0x87, 0x20, 0xdc}},
+ { // 30
+ []byte{
+ 0x01, 0x01, 0x01, 0x01, 0x20, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x20, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x20, 0x01, 0x01, 0x01},
+ nil,
+ []byte{0xb7, 0x26, 0x5f, 0x7f, 0x44, 0x7a, 0xc6, 0xf3}},
+ { // 31
+ []byte{
+ 0x01, 0x01, 0x01, 0x01, 0x10, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x10, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x10, 0x01, 0x01, 0x01},
+ nil,
+ []byte{0x9d, 0xb7, 0x3b, 0x3c, 0x0d, 0x16, 0x3f, 0x54}},
+ { // 32
+ []byte{
+ 0x01, 0x01, 0x01, 0x01, 0x08, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x08, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x08, 0x01, 0x01, 0x01},
+ nil,
+ []byte{0x81, 0x81, 0xb6, 0x5b, 0xab, 0xf4, 0xa9, 0x75}},
+ { // 33
+ []byte{
+ 0x01, 0x01, 0x01, 0x01, 0x04, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x04, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x04, 0x01, 0x01, 0x01},
+ nil,
+ []byte{0x93, 0xc9, 0xb6, 0x40, 0x42, 0xea, 0xa2, 0x40}},
+ { // 34
+ []byte{
+ 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01},
+ nil,
+ []byte{0x55, 0x70, 0x53, 0x08, 0x29, 0x70, 0x55, 0x92}},
+ { // 35
+ []byte{
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x80, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x80, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x80, 0x01, 0x01},
+ nil,
+ []byte{0x86, 0x38, 0x80, 0x9e, 0x87, 0x87, 0x87, 0xa0}},
+ { // 36
+ []byte{
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x40, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x40, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x40, 0x01, 0x01},
+ nil,
+ []byte{0x41, 0xb9, 0xa7, 0x9a, 0xf7, 0x9a, 0xc2, 0x08}},
+ { // 37
+ []byte{
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x20, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x20, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x20, 0x01, 0x01},
+ nil,
+ []byte{0x7a, 0x9b, 0xe4, 0x2f, 0x20, 0x09, 0xa8, 0x92}},
+ { // 38
+ []byte{
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x10, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x10, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x10, 0x01, 0x01},
+ nil,
+ []byte{0x29, 0x03, 0x8d, 0x56, 0xba, 0x6d, 0x27, 0x45}},
+ { // 39
+ []byte{
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x08, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x08, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x08, 0x01, 0x01},
+ nil,
+ []byte{0x54, 0x95, 0xc6, 0xab, 0xf1, 0xe5, 0xdf, 0x51}},
+ { // 40
+ []byte{
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x04, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x04, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x04, 0x01, 0x01},
+ nil,
+ []byte{0xae, 0x13, 0xdb, 0xd5, 0x61, 0x48, 0x89, 0x33}},
+ { // 41
+ []byte{
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01},
+ nil,
+ []byte{0x02, 0x4d, 0x1f, 0xfa, 0x89, 0x04, 0xe3, 0x89}},
+ { // 42
+ []byte{
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x80, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x80, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x80, 0x01},
+ nil,
+ []byte{0xd1, 0x39, 0x97, 0x12, 0xf9, 0x9b, 0xf0, 0x2e}},
+ { // 43
+ []byte{
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x40, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x40, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x40, 0x01},
+ nil,
+ []byte{0x14, 0xc1, 0xd7, 0xc1, 0xcf, 0xfe, 0xc7, 0x9e}},
+ { // 44
+ []byte{
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x20, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x20, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x20, 0x01},
+ nil,
+ []byte{0x1d, 0xe5, 0x27, 0x9d, 0xae, 0x3b, 0xed, 0x6f}},
+ { // 45
+ []byte{
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x10, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x10, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x10, 0x01},
+ nil,
+ []byte{0xe9, 0x41, 0xa3, 0x3f, 0x85, 0x50, 0x13, 0x03}},
+ { // 46
+ []byte{
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x08, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x08, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x08, 0x01},
+ nil,
+ []byte{0xda, 0x99, 0xdb, 0xbc, 0x9a, 0x03, 0xf3, 0x79}},
+ { // 47
+ []byte{
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x04, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x04, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x04, 0x01},
+ nil,
+ []byte{0xb7, 0xfc, 0x92, 0xf9, 0x1d, 0x8e, 0x92, 0xe9}},
+ { // 48
+ []byte{
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01},
+ nil,
+ []byte{0xae, 0x8e, 0x5c, 0xaa, 0x3c, 0xa0, 0x4e, 0x85}},
+ { // 49
+ []byte{
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x80,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x80,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x80},
+ nil,
+ []byte{0x9c, 0xc6, 0x2d, 0xf4, 0x3b, 0x6e, 0xed, 0x74}},
+ { // 50
+ []byte{
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x40,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x40,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x40},
+ nil,
+ []byte{0xd8, 0x63, 0xdb, 0xb5, 0xc5, 0x9a, 0x91, 0xa0}},
+ { // 50
+ []byte{
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x20,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x20,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x20},
+ nil,
+ []byte{0xa1, 0xab, 0x21, 0x90, 0x54, 0x5b, 0x91, 0xd7}},
+ { // 52
+ []byte{
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x10,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x10,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x10},
+ nil,
+ []byte{0x08, 0x75, 0x04, 0x1e, 0x64, 0xc5, 0x70, 0xf7}},
+ { // 53
+ []byte{
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x08,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x08,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x08},
+ nil,
+ []byte{0x5a, 0x59, 0x45, 0x28, 0xbe, 0xbe, 0xf1, 0xcc}},
+ { // 54
+ []byte{
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x04,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x04,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x04},
+ nil,
+ []byte{0xfc, 0xdb, 0x32, 0x91, 0xde, 0x21, 0xf0, 0xc0}},
+ { // 55
+ []byte{
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02},
+ nil,
+ []byte{0x86, 0x9e, 0xfd, 0x7f, 0x9f, 0x26, 0x5a, 0x09}},
+}
+
+// Plaintext for use with Table A.3 tests
+var tableA3Plaintext = []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
+
+// Table A.3 Values To Be Used for the Permutation Operation Known Answer Test
+var tableA3Tests = []CryptTest{
+ { // 0
+ []byte{
+ 0x10, 0x46, 0x91, 0x34, 0x89, 0x98, 0x01, 0x31,
+ 0x10, 0x46, 0x91, 0x34, 0x89, 0x98, 0x01, 0x31,
+ 0x10, 0x46, 0x91, 0x34, 0x89, 0x98, 0x01, 0x31,
+ },
+ nil,
+ []byte{0x88, 0xd5, 0x5e, 0x54, 0xf5, 0x4c, 0x97, 0xb4}},
+ { // 1
+ []byte{
+ 0x10, 0x07, 0x10, 0x34, 0x89, 0x98, 0x80, 0x20,
+ 0x10, 0x07, 0x10, 0x34, 0x89, 0x98, 0x80, 0x20,
+ 0x10, 0x07, 0x10, 0x34, 0x89, 0x98, 0x80, 0x20,
+ },
+ nil,
+ []byte{0x0c, 0x0c, 0xc0, 0x0c, 0x83, 0xea, 0x48, 0xfd}},
+ { // 2
+ []byte{
+ 0x10, 0x07, 0x10, 0x34, 0xc8, 0x98, 0x01, 0x20,
+ 0x10, 0x07, 0x10, 0x34, 0xc8, 0x98, 0x01, 0x20,
+ 0x10, 0x07, 0x10, 0x34, 0xc8, 0x98, 0x01, 0x20,
+ },
+ nil,
+ []byte{0x83, 0xbc, 0x8e, 0xf3, 0xa6, 0x57, 0x01, 0x83}},
+ { // 3
+ []byte{
+ 0x10, 0x46, 0x10, 0x34, 0x89, 0x98, 0x80, 0x20,
+ 0x10, 0x46, 0x10, 0x34, 0x89, 0x98, 0x80, 0x20,
+ 0x10, 0x46, 0x10, 0x34, 0x89, 0x98, 0x80, 0x20,
+ },
+ nil,
+ []byte{0xdf, 0x72, 0x5d, 0xca, 0xd9, 0x4e, 0xa2, 0xe9}},
+ { // 4
+ []byte{
+ 0x10, 0x86, 0x91, 0x15, 0x19, 0x19, 0x01, 0x01,
+ 0x10, 0x86, 0x91, 0x15, 0x19, 0x19, 0x01, 0x01,
+ 0x10, 0x86, 0x91, 0x15, 0x19, 0x19, 0x01, 0x01,
+ },
+ nil,
+ []byte{0xe6, 0x52, 0xb5, 0x3b, 0x55, 0x0b, 0xe8, 0xb0}},
+ { // 5
+ []byte{
+ 0x10, 0x86, 0x91, 0x15, 0x19, 0x58, 0x01, 0x01,
+ 0x10, 0x86, 0x91, 0x15, 0x19, 0x58, 0x01, 0x01,
+ 0x10, 0x86, 0x91, 0x15, 0x19, 0x58, 0x01, 0x01,
+ },
+ nil,
+ []byte{0xaf, 0x52, 0x71, 0x20, 0xc4, 0x85, 0xcb, 0xb0}},
+ { // 6
+ []byte{
+ 0x51, 0x07, 0xb0, 0x15, 0x19, 0x58, 0x01, 0x01,
+ 0x51, 0x07, 0xb0, 0x15, 0x19, 0x58, 0x01, 0x01,
+ 0x51, 0x07, 0xb0, 0x15, 0x19, 0x58, 0x01, 0x01,
+ },
+ nil,
+ []byte{0x0f, 0x04, 0xce, 0x39, 0x3d, 0xb9, 0x26, 0xd5}},
+ { // 7
+ []byte{
+ 0x10, 0x07, 0xb0, 0x15, 0x19, 0x19, 0x01, 0x01,
+ 0x10, 0x07, 0xb0, 0x15, 0x19, 0x19, 0x01, 0x01,
+ 0x10, 0x07, 0xb0, 0x15, 0x19, 0x19, 0x01, 0x01,
+ },
+ nil,
+ []byte{0xc9, 0xf0, 0x0f, 0xfc, 0x74, 0x07, 0x90, 0x67}},
+ { // 8
+ []byte{
+ 0x31, 0x07, 0x91, 0x54, 0x98, 0x08, 0x01, 0x01,
+ 0x31, 0x07, 0x91, 0x54, 0x98, 0x08, 0x01, 0x01,
+ 0x31, 0x07, 0x91, 0x54, 0x98, 0x08, 0x01, 0x01,
+ },
+ nil,
+ []byte{0x7c, 0xfd, 0x82, 0xa5, 0x93, 0x25, 0x2b, 0x4e}},
+ { // 9
+ []byte{
+ 0x31, 0x07, 0x91, 0x94, 0x98, 0x08, 0x01, 0x01,
+ 0x31, 0x07, 0x91, 0x94, 0x98, 0x08, 0x01, 0x01,
+ 0x31, 0x07, 0x91, 0x94, 0x98, 0x08, 0x01, 0x01,
+ },
+ nil,
+ []byte{0xcb, 0x49, 0xa2, 0xf9, 0xe9, 0x13, 0x63, 0xe3}},
+ { // 10
+ []byte{
+ 0x10, 0x07, 0x91, 0x15, 0xb9, 0x08, 0x01, 0x40,
+ 0x10, 0x07, 0x91, 0x15, 0xb9, 0x08, 0x01, 0x40,
+ 0x10, 0x07, 0x91, 0x15, 0xb9, 0x08, 0x01, 0x40,
+ },
+ nil,
+ []byte{0x00, 0xb5, 0x88, 0xbe, 0x70, 0xd2, 0x3f, 0x56}},
+ { // 11
+ []byte{
+ 0x31, 0x07, 0x91, 0x15, 0x98, 0x08, 0x01, 0x40,
+ 0x31, 0x07, 0x91, 0x15, 0x98, 0x08, 0x01, 0x40,
+ 0x31, 0x07, 0x91, 0x15, 0x98, 0x08, 0x01, 0x40,
+ },
+ nil,
+ []byte{0x40, 0x6a, 0x9a, 0x6a, 0xb4, 0x33, 0x99, 0xae}},
+ { // 12
+ []byte{
+ 0x10, 0x07, 0xd0, 0x15, 0x89, 0x98, 0x01, 0x01,
+ 0x10, 0x07, 0xd0, 0x15, 0x89, 0x98, 0x01, 0x01,
+ 0x10, 0x07, 0xd0, 0x15, 0x89, 0x98, 0x01, 0x01,
+ },
+ nil,
+ []byte{0x6c, 0xb7, 0x73, 0x61, 0x1d, 0xca, 0x9a, 0xda}},
+ { // 13
+ []byte{
+ 0x91, 0x07, 0x91, 0x15, 0x89, 0x98, 0x01, 0x01,
+ 0x91, 0x07, 0x91, 0x15, 0x89, 0x98, 0x01, 0x01,
+ 0x91, 0x07, 0x91, 0x15, 0x89, 0x98, 0x01, 0x01,
+ },
+ nil,
+ []byte{0x67, 0xfd, 0x21, 0xc1, 0x7d, 0xbb, 0x5d, 0x70}},
+ { // 14
+ []byte{
+ 0x91, 0x07, 0xd0, 0x15, 0x89, 0x19, 0x01, 0x01,
+ 0x91, 0x07, 0xd0, 0x15, 0x89, 0x19, 0x01, 0x01,
+ 0x91, 0x07, 0xd0, 0x15, 0x89, 0x19, 0x01, 0x01,
+ },
+ nil,
+ []byte{0x95, 0x92, 0xcb, 0x41, 0x10, 0x43, 0x07, 0x87}},
+ { // 15
+ []byte{
+ 0x10, 0x07, 0xd0, 0x15, 0x98, 0x98, 0x01, 0x20,
+ 0x10, 0x07, 0xd0, 0x15, 0x98, 0x98, 0x01, 0x20,
+ 0x10, 0x07, 0xd0, 0x15, 0x98, 0x98, 0x01, 0x20,
+ },
+ nil,
+ []byte{0xa6, 0xb7, 0xff, 0x68, 0xa3, 0x18, 0xdd, 0xd3}},
+ { // 16
+ []byte{
+ 0x10, 0x07, 0x94, 0x04, 0x98, 0x19, 0x01, 0x01,
+ 0x10, 0x07, 0x94, 0x04, 0x98, 0x19, 0x01, 0x01,
+ 0x10, 0x07, 0x94, 0x04, 0x98, 0x19, 0x01, 0x01,
+ },
+ nil,
+ []byte{0x4d, 0x10, 0x21, 0x96, 0xc9, 0x14, 0xca, 0x16}},
+ { // 17
+ []byte{
+ 0x01, 0x07, 0x91, 0x04, 0x91, 0x19, 0x04, 0x01,
+ 0x01, 0x07, 0x91, 0x04, 0x91, 0x19, 0x04, 0x01,
+ 0x01, 0x07, 0x91, 0x04, 0x91, 0x19, 0x04, 0x01,
+ },
+ nil,
+ []byte{0x2d, 0xfa, 0x9f, 0x45, 0x73, 0x59, 0x49, 0x65}},
+ { // 18
+ []byte{
+ 0x01, 0x07, 0x91, 0x04, 0x91, 0x19, 0x01, 0x01,
+ 0x01, 0x07, 0x91, 0x04, 0x91, 0x19, 0x01, 0x01,
+ 0x01, 0x07, 0x91, 0x04, 0x91, 0x19, 0x01, 0x01,
+ },
+ nil,
+ []byte{0xb4, 0x66, 0x04, 0x81, 0x6c, 0x0e, 0x07, 0x74}},
+ { // 19
+ []byte{
+ 0x01, 0x07, 0x94, 0x04, 0x91, 0x19, 0x04, 0x01,
+ 0x01, 0x07, 0x94, 0x04, 0x91, 0x19, 0x04, 0x01,
+ 0x01, 0x07, 0x94, 0x04, 0x91, 0x19, 0x04, 0x01,
+ },
+ nil,
+ []byte{0x6e, 0x7e, 0x62, 0x21, 0xa4, 0xf3, 0x4e, 0x87}},
+ { // 20
+ []byte{
+ 0x19, 0x07, 0x92, 0x10, 0x98, 0x1a, 0x01, 0x01,
+ 0x19, 0x07, 0x92, 0x10, 0x98, 0x1a, 0x01, 0x01,
+ 0x19, 0x07, 0x92, 0x10, 0x98, 0x1a, 0x01, 0x01,
+ },
+ nil,
+ []byte{0xaa, 0x85, 0xe7, 0x46, 0x43, 0x23, 0x31, 0x99}},
+ { // 21
+ []byte{
+ 0x10, 0x07, 0x91, 0x19, 0x98, 0x19, 0x08, 0x01,
+ 0x10, 0x07, 0x91, 0x19, 0x98, 0x19, 0x08, 0x01,
+ 0x10, 0x07, 0x91, 0x19, 0x98, 0x19, 0x08, 0x01,
+ },
+ nil,
+ []byte{0x2e, 0x5a, 0x19, 0xdb, 0x4d, 0x19, 0x62, 0xd6}},
+ { // 22
+ []byte{
+ 0x10, 0x07, 0x91, 0x19, 0x98, 0x1a, 0x08, 0x01,
+ 0x10, 0x07, 0x91, 0x19, 0x98, 0x1a, 0x08, 0x01,
+ 0x10, 0x07, 0x91, 0x19, 0x98, 0x1a, 0x08, 0x01,
+ },
+ nil,
+ []byte{0x23, 0xa8, 0x66, 0xa8, 0x09, 0xd3, 0x08, 0x94}},
+ { // 23
+ []byte{
+ 0x10, 0x07, 0x92, 0x10, 0x98, 0x19, 0x01, 0x01,
+ 0x10, 0x07, 0x92, 0x10, 0x98, 0x19, 0x01, 0x01,
+ 0x10, 0x07, 0x92, 0x10, 0x98, 0x19, 0x01, 0x01,
+ },
+ nil,
+ []byte{0xd8, 0x12, 0xd9, 0x61, 0xf0, 0x17, 0xd3, 0x20}},
+ { // 24
+ []byte{
+ 0x10, 0x07, 0x91, 0x15, 0x98, 0x19, 0x01, 0x0b,
+ 0x10, 0x07, 0x91, 0x15, 0x98, 0x19, 0x01, 0x0b,
+ 0x10, 0x07, 0x91, 0x15, 0x98, 0x19, 0x01, 0x0b,
+ },
+ nil,
+ []byte{0x05, 0x56, 0x05, 0x81, 0x6e, 0x58, 0x60, 0x8f}},
+ { // 25
+ []byte{
+ 0x10, 0x04, 0x80, 0x15, 0x98, 0x19, 0x01, 0x01,
+ 0x10, 0x04, 0x80, 0x15, 0x98, 0x19, 0x01, 0x01,
+ 0x10, 0x04, 0x80, 0x15, 0x98, 0x19, 0x01, 0x01,
+ },
+ nil,
+ []byte{0xab, 0xd8, 0x8e, 0x8b, 0x1b, 0x77, 0x16, 0xf1}},
+ { // 26
+ []byte{
+ 0x10, 0x04, 0x80, 0x15, 0x98, 0x19, 0x01, 0x02,
+ 0x10, 0x04, 0x80, 0x15, 0x98, 0x19, 0x01, 0x02,
+ 0x10, 0x04, 0x80, 0x15, 0x98, 0x19, 0x01, 0x02,
+ },
+ nil,
+ []byte{0x53, 0x7a, 0xc9, 0x5b, 0xe6, 0x9d, 0xa1, 0xe1}},
+ { // 27
+ []byte{
+ 0x10, 0x04, 0x80, 0x15, 0x98, 0x19, 0x01, 0x08,
+ 0x10, 0x04, 0x80, 0x15, 0x98, 0x19, 0x01, 0x08,
+ 0x10, 0x04, 0x80, 0x15, 0x98, 0x19, 0x01, 0x08,
+ },
+ nil,
+ []byte{0xae, 0xd0, 0xf6, 0xae, 0x3c, 0x25, 0xcd, 0xd8}},
+ { // 28
+ []byte{
+ 0x10, 0x02, 0x91, 0x15, 0x98, 0x10, 0x01, 0x04,
+ 0x10, 0x02, 0x91, 0x15, 0x98, 0x10, 0x01, 0x04,
+ 0x10, 0x02, 0x91, 0x15, 0x98, 0x10, 0x01, 0x04,
+ },
+ nil,
+ []byte{0xb3, 0xe3, 0x5a, 0x5e, 0xe5, 0x3e, 0x7b, 0x8d}},
+ { // 29
+ []byte{
+ 0x10, 0x02, 0x91, 0x15, 0x98, 0x19, 0x01, 0x04,
+ 0x10, 0x02, 0x91, 0x15, 0x98, 0x19, 0x01, 0x04,
+ 0x10, 0x02, 0x91, 0x15, 0x98, 0x19, 0x01, 0x04,
+ },
+ nil,
+ []byte{0x61, 0xc7, 0x9c, 0x71, 0x92, 0x1a, 0x2e, 0xf8}},
+ { // 30
+ []byte{
+ 0x10, 0x02, 0x91, 0x15, 0x98, 0x10, 0x02, 0x01,
+ 0x10, 0x02, 0x91, 0x15, 0x98, 0x10, 0x02, 0x01,
+ 0x10, 0x02, 0x91, 0x15, 0x98, 0x10, 0x02, 0x01,
+ },
+ nil,
+ []byte{0xe2, 0xf5, 0x72, 0x8f, 0x09, 0x95, 0x01, 0x3c}},
+ { // 31
+ []byte{
+ 0x10, 0x02, 0x91, 0x16, 0x98, 0x10, 0x01, 0x01,
+ 0x10, 0x02, 0x91, 0x16, 0x98, 0x10, 0x01, 0x01,
+ 0x10, 0x02, 0x91, 0x16, 0x98, 0x10, 0x01, 0x01,
+ },
+ nil,
+ []byte{0x1a, 0xea, 0xc3, 0x9a, 0x61, 0xf0, 0xa4, 0x64}},
+}
+
+// Table A.4 Values To Be Used for the Substitution Table Known Answer Test
+var tableA4Tests = []CryptTest{
+ { // 0
+ []byte{
+ 0x7c, 0xa1, 0x10, 0x45, 0x4a, 0x1a, 0x6e, 0x57,
+ 0x7c, 0xa1, 0x10, 0x45, 0x4a, 0x1a, 0x6e, 0x57,
+ 0x7c, 0xa1, 0x10, 0x45, 0x4a, 0x1a, 0x6e, 0x57},
+ []byte{0x01, 0xa1, 0xd6, 0xd0, 0x39, 0x77, 0x67, 0x42},
+ []byte{0x69, 0x0f, 0x5b, 0x0d, 0x9a, 0x26, 0x93, 0x9b}},
+ { // 1
+ []byte{
+ 0x01, 0x31, 0xd9, 0x61, 0x9d, 0xc1, 0x37, 0x6e,
+ 0x01, 0x31, 0xd9, 0x61, 0x9d, 0xc1, 0x37, 0x6e,
+ 0x01, 0x31, 0xd9, 0x61, 0x9d, 0xc1, 0x37, 0x6e},
+ []byte{0x5c, 0xd5, 0x4c, 0xa8, 0x3d, 0xef, 0x57, 0xda},
+ []byte{0x7a, 0x38, 0x9d, 0x10, 0x35, 0x4b, 0xd2, 0x71}},
+ { // 2
+ []byte{
+ 0x07, 0xa1, 0x13, 0x3e, 0x4a, 0x0b, 0x26, 0x86,
+ 0x07, 0xa1, 0x13, 0x3e, 0x4a, 0x0b, 0x26, 0x86,
+ 0x07, 0xa1, 0x13, 0x3e, 0x4a, 0x0b, 0x26, 0x86},
+ []byte{0x02, 0x48, 0xd4, 0x38, 0x06, 0xf6, 0x71, 0x72},
+ []byte{0x86, 0x8e, 0xbb, 0x51, 0xca, 0xb4, 0x59, 0x9a}},
+ { // 3
+ []byte{
+ 0x38, 0x49, 0x67, 0x4c, 0x26, 0x02, 0x31, 0x9e,
+ 0x38, 0x49, 0x67, 0x4c, 0x26, 0x02, 0x31, 0x9e,
+ 0x38, 0x49, 0x67, 0x4c, 0x26, 0x02, 0x31, 0x9e},
+ []byte{0x51, 0x45, 0x4b, 0x58, 0x2d, 0xdf, 0x44, 0x0a},
+ []byte{0x71, 0x78, 0x87, 0x6e, 0x01, 0xf1, 0x9b, 0x2a}},
+ { // 4
+ []byte{
+ 0x04, 0xb9, 0x15, 0xba, 0x43, 0xfe, 0xb5, 0xb6,
+ 0x04, 0xb9, 0x15, 0xba, 0x43, 0xfe, 0xb5, 0xb6,
+ 0x04, 0xb9, 0x15, 0xba, 0x43, 0xfe, 0xb5, 0xb6},
+ []byte{0x42, 0xfd, 0x44, 0x30, 0x59, 0x57, 0x7f, 0xa2},
+ []byte{0xaf, 0x37, 0xfb, 0x42, 0x1f, 0x8c, 0x40, 0x95}},
+ { // 5
+ []byte{
+ 0x01, 0x13, 0xb9, 0x70, 0xfd, 0x34, 0xf2, 0xce,
+ 0x01, 0x13, 0xb9, 0x70, 0xfd, 0x34, 0xf2, 0xce,
+ 0x01, 0x13, 0xb9, 0x70, 0xfd, 0x34, 0xf2, 0xce},
+ []byte{0x05, 0x9b, 0x5e, 0x08, 0x51, 0xcf, 0x14, 0x3a},
+ []byte{0x86, 0xa5, 0x60, 0xf1, 0x0e, 0xc6, 0xd8, 0x5b}},
+ { // 6
+ []byte{
+ 0x01, 0x70, 0xf1, 0x75, 0x46, 0x8f, 0xb5, 0xe6,
+ 0x01, 0x70, 0xf1, 0x75, 0x46, 0x8f, 0xb5, 0xe6,
+ 0x01, 0x70, 0xf1, 0x75, 0x46, 0x8f, 0xb5, 0xe6},
+ []byte{0x07, 0x56, 0xd8, 0xe0, 0x77, 0x47, 0x61, 0xd2},
+ []byte{0x0c, 0xd3, 0xda, 0x02, 0x00, 0x21, 0xdc, 0x09}},
+ { // 7
+ []byte{
+ 0x43, 0x29, 0x7f, 0xad, 0x38, 0xe3, 0x73, 0xfe,
+ 0x43, 0x29, 0x7f, 0xad, 0x38, 0xe3, 0x73, 0xfe,
+ 0x43, 0x29, 0x7f, 0xad, 0x38, 0xe3, 0x73, 0xfe},
+ []byte{0x76, 0x25, 0x14, 0xb8, 0x29, 0xbf, 0x48, 0x6a},
+ []byte{0xea, 0x67, 0x6b, 0x2c, 0xb7, 0xdb, 0x2b, 0x7a}},
+ { // 8
+ []byte{
+ 0x07, 0xa7, 0x13, 0x70, 0x45, 0xda, 0x2a, 0x16,
+ 0x07, 0xa7, 0x13, 0x70, 0x45, 0xda, 0x2a, 0x16,
+ 0x07, 0xa7, 0x13, 0x70, 0x45, 0xda, 0x2a, 0x16},
+ []byte{0x3b, 0xdd, 0x11, 0x90, 0x49, 0x37, 0x28, 0x02},
+ []byte{0xdf, 0xd6, 0x4a, 0x81, 0x5c, 0xaf, 0x1a, 0x0f}},
+ { // 9
+ []byte{
+ 0x04, 0x68, 0x91, 0x04, 0xc2, 0xfd, 0x3b, 0x2f,
+ 0x04, 0x68, 0x91, 0x04, 0xc2, 0xfd, 0x3b, 0x2f,
+ 0x04, 0x68, 0x91, 0x04, 0xc2, 0xfd, 0x3b, 0x2f},
+ []byte{0x26, 0x95, 0x5f, 0x68, 0x35, 0xaf, 0x60, 0x9a},
+ []byte{0x5c, 0x51, 0x3c, 0x9c, 0x48, 0x86, 0xc0, 0x88}},
+ { // 10
+ []byte{
+ 0x37, 0xd0, 0x6b, 0xb5, 0x16, 0xcb, 0x75, 0x46,
+ 0x37, 0xd0, 0x6b, 0xb5, 0x16, 0xcb, 0x75, 0x46,
+ 0x37, 0xd0, 0x6b, 0xb5, 0x16, 0xcb, 0x75, 0x46},
+ []byte{0x16, 0x4d, 0x5e, 0x40, 0x4f, 0x27, 0x52, 0x32},
+ []byte{0x0a, 0x2a, 0xee, 0xae, 0x3f, 0xf4, 0xab, 0x77}},
+ { // 11
+ []byte{
+ 0x1f, 0x08, 0x26, 0x0d, 0x1a, 0xc2, 0x46, 0x5e,
+ 0x1f, 0x08, 0x26, 0x0d, 0x1a, 0xc2, 0x46, 0x5e,
+ 0x1f, 0x08, 0x26, 0x0d, 0x1a, 0xc2, 0x46, 0x5e},
+ []byte{0x6b, 0x05, 0x6e, 0x18, 0x75, 0x9f, 0x5c, 0xca},
+ []byte{0xef, 0x1b, 0xf0, 0x3e, 0x5d, 0xfa, 0x57, 0x5a}},
+ { // 12
+ []byte{
+ 0x58, 0x40, 0x23, 0x64, 0x1a, 0xba, 0x61, 0x76,
+ 0x58, 0x40, 0x23, 0x64, 0x1a, 0xba, 0x61, 0x76,
+ 0x58, 0x40, 0x23, 0x64, 0x1a, 0xba, 0x61, 0x76},
+ []byte{0x00, 0x4b, 0xd6, 0xef, 0x09, 0x17, 0x60, 0x62},
+ []byte{0x88, 0xbf, 0x0d, 0xb6, 0xd7, 0x0d, 0xee, 0x56}},
+ { // 13
+ []byte{
+ 0x02, 0x58, 0x16, 0x16, 0x46, 0x29, 0xb0, 0x07,
+ 0x02, 0x58, 0x16, 0x16, 0x46, 0x29, 0xb0, 0x07,
+ 0x02, 0x58, 0x16, 0x16, 0x46, 0x29, 0xb0, 0x07},
+ []byte{0x48, 0x0d, 0x39, 0x00, 0x6e, 0xe7, 0x62, 0xf2},
+ []byte{0xa1, 0xf9, 0x91, 0x55, 0x41, 0x02, 0x0b, 0x56}},
+ { // 14
+ []byte{
+ 0x49, 0x79, 0x3e, 0xbc, 0x79, 0xb3, 0x25, 0x8f,
+ 0x49, 0x79, 0x3e, 0xbc, 0x79, 0xb3, 0x25, 0x8f,
+ 0x49, 0x79, 0x3e, 0xbc, 0x79, 0xb3, 0x25, 0x8f},
+ []byte{0x43, 0x75, 0x40, 0xc8, 0x69, 0x8f, 0x3c, 0xfa},
+ []byte{0x6f, 0xbf, 0x1c, 0xaf, 0xcf, 0xfd, 0x05, 0x56}},
+ { // 15
+ []byte{
+ 0x4f, 0xb0, 0x5e, 0x15, 0x15, 0xab, 0x73, 0xa7,
+ 0x4f, 0xb0, 0x5e, 0x15, 0x15, 0xab, 0x73, 0xa7,
+ 0x4f, 0xb0, 0x5e, 0x15, 0x15, 0xab, 0x73, 0xa7},
+ []byte{0x07, 0x2d, 0x43, 0xa0, 0x77, 0x07, 0x52, 0x92},
+ []byte{0x2f, 0x22, 0xe4, 0x9b, 0xab, 0x7c, 0xa1, 0xac}},
+ { // 16
+ []byte{
+ 0x49, 0xe9, 0x5d, 0x6d, 0x4c, 0xa2, 0x29, 0xbf,
+ 0x49, 0xe9, 0x5d, 0x6d, 0x4c, 0xa2, 0x29, 0xbf,
+ 0x49, 0xe9, 0x5d, 0x6d, 0x4c, 0xa2, 0x29, 0xbf},
+ []byte{0x02, 0xfe, 0x55, 0x77, 0x81, 0x17, 0xf1, 0x2a},
+ []byte{0x5a, 0x6b, 0x61, 0x2c, 0xc2, 0x6c, 0xce, 0x4a}},
+ { // 17
+ []byte{
+ 0x01, 0x83, 0x10, 0xdc, 0x40, 0x9b, 0x26, 0xd6,
+ 0x01, 0x83, 0x10, 0xdc, 0x40, 0x9b, 0x26, 0xd6,
+ 0x01, 0x83, 0x10, 0xdc, 0x40, 0x9b, 0x26, 0xd6},
+ []byte{0x1d, 0x9d, 0x5c, 0x50, 0x18, 0xf7, 0x28, 0xc2},
+ []byte{0x5f, 0x4c, 0x03, 0x8e, 0xd1, 0x2b, 0x2e, 0x41}},
+ { // 18
+ []byte{
+ 0x1c, 0x58, 0x7f, 0x1c, 0x13, 0x92, 0x4f, 0xef,
+ 0x1c, 0x58, 0x7f, 0x1c, 0x13, 0x92, 0x4f, 0xef,
+ 0x1c, 0x58, 0x7f, 0x1c, 0x13, 0x92, 0x4f, 0xef},
+ []byte{0x30, 0x55, 0x32, 0x28, 0x6d, 0x6f, 0x29, 0x5a},
+ []byte{0x63, 0xfa, 0xc0, 0xd0, 0x34, 0xd9, 0xf7, 0x93}},
+}
+
+// Use the known weak keys to test DES implementation
+func TestWeakKeys(t *testing.T) {
+ for i, tt := range weakKeyTests {
+ var encrypt = func(in []byte) (out []byte) {
+ c, _ := NewCipher(tt.key)
+ out = make([]byte, len(in))
+ encryptBlock(c.subkeys[:], out, in)
+ return
+ }
+
+ // Encrypting twice with a DES weak
+ // key should reproduce the original input
+ result := encrypt(tt.in)
+ result = encrypt(result)
+
+ if !bytes.Equal(result, tt.in) {
+ t.Errorf("#%d: result: %x want: %x", i, result, tt.in)
+ }
+ }
+}
+
+// Use the known semi-weak key pairs to test DES implementation
+func TestSemiWeakKeyPairs(t *testing.T) {
+ for i, tt := range semiWeakKeyTests {
+ var encrypt = func(key, in []byte) (out []byte) {
+ c, _ := NewCipher(key)
+ out = make([]byte, len(in))
+ encryptBlock(c.subkeys[:], out, in)
+ return
+ }
+
+ // Encrypting with one member of the semi-weak pair
+ // and then encrypting the result with the other member
+ // should reproduce the original input.
+ result := encrypt(tt.key, tt.in)
+ result = encrypt(tt.out, result)
+
+ if !bytes.Equal(result, tt.in) {
+ t.Errorf("#%d: result: %x want: %x", i, result, tt.in)
+ }
+ }
+}
+
+func TestDESEncryptBlock(t *testing.T) {
+ for i, tt := range encryptDESTests {
+ c, _ := NewCipher(tt.key)
+ out := make([]byte, len(tt.in))
+ encryptBlock(c.subkeys[:], out, tt.in)
+
+ if !bytes.Equal(out, tt.out) {
+ t.Errorf("#%d: result: %x want: %x", i, out, tt.out)
+ }
+ }
+}
+
+func TestDESDecryptBlock(t *testing.T) {
+ for i, tt := range encryptDESTests {
+ c, _ := NewCipher(tt.key)
+ plain := make([]byte, len(tt.in))
+ decryptBlock(c.subkeys[:], plain, tt.out)
+
+ if !bytes.Equal(plain, tt.in) {
+ t.Errorf("#%d: result: %x want: %x", i, plain, tt.in)
+ }
+ }
+}
+
+func TestEncryptTripleDES(t *testing.T) {
+ for i, tt := range encryptTripleDESTests {
+ c, _ := NewTripleDESCipher(tt.key)
+ out := make([]byte, len(tt.in))
+ c.Encrypt(out, tt.in)
+
+ if !bytes.Equal(out, tt.out) {
+ t.Errorf("#%d: result: %x want: %x", i, out, tt.out)
+ }
+ }
+}
+
+func TestDecryptTripleDES(t *testing.T) {
+ for i, tt := range encryptTripleDESTests {
+ c, _ := NewTripleDESCipher(tt.key)
+
+ plain := make([]byte, len(tt.in))
+ c.Decrypt(plain, tt.out)
+
+ if !bytes.Equal(plain, tt.in) {
+ t.Errorf("#%d: result: %x want: %x", i, plain, tt.in)
+ }
+ }
+}
+
+// Defined in Pub 800-20
+func TestVariablePlaintextKnownAnswer(t *testing.T) {
+ for i, tt := range tableA1Tests {
+ c, _ := NewTripleDESCipher(tableA1Key)
+
+ out := make([]byte, len(tt.in))
+ c.Encrypt(out, tt.in)
+
+ if !bytes.Equal(out, tt.out) {
+ t.Errorf("#%d: result: %x want: %x", i, out, tt.out)
+ }
+ }
+}
+
+// Defined in Pub 800-20
+func TestVariableCiphertextKnownAnswer(t *testing.T) {
+ for i, tt := range tableA1Tests {
+ c, _ := NewTripleDESCipher(tableA1Key)
+
+ plain := make([]byte, len(tt.out))
+ c.Decrypt(plain, tt.out)
+
+ if !bytes.Equal(plain, tt.in) {
+ t.Errorf("#%d: result: %x want: %x", i, plain, tt.in)
+ }
+ }
+}
+
+// Defined in Pub 800-20
+// Encrypting the Table A.1 ciphertext with the
+// 0x01... key produces the original plaintext
+func TestInversePermutationKnownAnswer(t *testing.T) {
+ for i, tt := range tableA1Tests {
+ c, _ := NewTripleDESCipher(tableA1Key)
+
+ plain := make([]byte, len(tt.in))
+ c.Encrypt(plain, tt.out)
+
+ if !bytes.Equal(plain, tt.in) {
+ t.Errorf("#%d: result: %x want: %x", i, plain, tt.in)
+ }
+ }
+}
+
+// Defined in Pub 800-20
+// Decrypting the Table A.1 plaintext with the
+// 0x01... key produces the corresponding ciphertext
+func TestInitialPermutationKnownAnswer(t *testing.T) {
+ for i, tt := range tableA1Tests {
+ c, _ := NewTripleDESCipher(tableA1Key)
+
+ out := make([]byte, len(tt.in))
+ c.Decrypt(out, tt.in)
+
+ if !bytes.Equal(out, tt.out) {
+ t.Errorf("#%d: result: %x want: %x", i, out, tt.out)
+ }
+ }
+}
+
+// Defined in Pub 800-20
+func TestVariableKeyKnownAnswerEncrypt(t *testing.T) {
+ for i, tt := range tableA2Tests {
+ c, _ := NewTripleDESCipher(tt.key)
+
+ out := make([]byte, len(tableA2Plaintext))
+ c.Encrypt(out, tableA2Plaintext)
+
+ if !bytes.Equal(out, tt.out) {
+ t.Errorf("#%d: result: %x want: %x", i, out, tt.out)
+ }
+ }
+}
+
+// Defined in Pub 800-20
+func TestVariableKeyKnownAnswerDecrypt(t *testing.T) {
+ for i, tt := range tableA2Tests {
+ c, _ := NewTripleDESCipher(tt.key)
+
+ out := make([]byte, len(tt.out))
+ c.Decrypt(out, tt.out)
+
+ if !bytes.Equal(out, tableA2Plaintext) {
+ t.Errorf("#%d: result: %x want: %x", i, out, tableA2Plaintext)
+ }
+ }
+}
+
+// Defined in Pub 800-20
+func TestPermutationOperationKnownAnswerEncrypt(t *testing.T) {
+ for i, tt := range tableA3Tests {
+ c, _ := NewTripleDESCipher(tt.key)
+
+ out := make([]byte, len(tableA3Plaintext))
+ c.Encrypt(out, tableA3Plaintext)
+
+ if !bytes.Equal(out, tt.out) {
+ t.Errorf("#%d: result: %x want: %x", i, out, tt.out)
+ }
+ }
+}
+
+// Defined in Pub 800-20
+func TestPermutationOperationKnownAnswerDecrypt(t *testing.T) {
+ for i, tt := range tableA3Tests {
+ c, _ := NewTripleDESCipher(tt.key)
+
+ out := make([]byte, len(tt.out))
+ c.Decrypt(out, tt.out)
+
+ if !bytes.Equal(out, tableA3Plaintext) {
+ t.Errorf("#%d: result: %x want: %x", i, out, tableA3Plaintext)
+ }
+ }
+}
+
+// Defined in Pub 800-20
+func TestSubstitutionTableKnownAnswerEncrypt(t *testing.T) {
+ for i, tt := range tableA4Tests {
+ c, _ := NewTripleDESCipher(tt.key)
+
+ out := make([]byte, len(tt.in))
+ c.Encrypt(out, tt.in)
+
+ if !bytes.Equal(out, tt.out) {
+ t.Errorf("#%d: result: %x want: %x", i, out, tt.out)
+ }
+ }
+}
+
+// Defined in Pub 800-20
+func TestSubstitutionTableKnownAnswerDecrypt(t *testing.T) {
+ for i, tt := range tableA4Tests {
+ c, _ := NewTripleDESCipher(tt.key)
+
+ out := make([]byte, len(tt.out))
+ c.Decrypt(out, tt.out)
+
+ if !bytes.Equal(out, tt.in) {
+ t.Errorf("#%d: result: %x want: %x", i, out, tt.in)
+ }
+ }
+}
diff --git a/src/pkg/crypto/ecdsa/Makefile b/src/pkg/crypto/ecdsa/Makefile
new file mode 100644
index 000000000..0af24c2f2
--- /dev/null
+++ b/src/pkg/crypto/ecdsa/Makefile
@@ -0,0 +1,11 @@
+# Copyright 2011 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+include ../../../Make.inc
+
+TARG=crypto/ecdsa
+GOFILES=\
+ ecdsa.go\
+
+include ../../../Make.pkg
diff --git a/src/pkg/crypto/ecdsa/ecdsa.go b/src/pkg/crypto/ecdsa/ecdsa.go
new file mode 100644
index 000000000..7bce1bc96
--- /dev/null
+++ b/src/pkg/crypto/ecdsa/ecdsa.go
@@ -0,0 +1,149 @@
+// 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 ecdsa implements the Elliptic Curve Digital Signature Algorithm, as
+// defined in FIPS 186-3.
+package ecdsa
+
+// References:
+// [NSA]: Suite B implementor's guide to FIPS 186-3,
+// http://www.nsa.gov/ia/_files/ecdsa.pdf
+// [SECG]: SECG, SEC1
+// http://www.secg.org/download/aid-780/sec1-v2.pdf
+
+import (
+ "big"
+ "crypto/elliptic"
+ "io"
+ "os"
+)
+
+// PublicKey represents an ECDSA public key.
+type PublicKey struct {
+ *elliptic.Curve
+ X, Y *big.Int
+}
+
+// PrivateKey represents a ECDSA private key.
+type PrivateKey struct {
+ PublicKey
+ D *big.Int
+}
+
+var one = new(big.Int).SetInt64(1)
+
+// randFieldElement returns a random element of the field underlying the given
+// curve using the procedure given in [NSA] A.2.1.
+func randFieldElement(c *elliptic.Curve, rand io.Reader) (k *big.Int, err os.Error) {
+ b := make([]byte, c.BitSize/8+8)
+ _, err = io.ReadFull(rand, b)
+ if err != nil {
+ return
+ }
+
+ k = new(big.Int).SetBytes(b)
+ n := new(big.Int).Sub(c.N, one)
+ k.Mod(k, n)
+ k.Add(k, one)
+ return
+}
+
+// GenerateKey generates a public&private key pair.
+func GenerateKey(c *elliptic.Curve, rand io.Reader) (priv *PrivateKey, err os.Error) {
+ k, err := randFieldElement(c, rand)
+ if err != nil {
+ return
+ }
+
+ priv = new(PrivateKey)
+ priv.PublicKey.Curve = c
+ priv.D = k
+ priv.PublicKey.X, priv.PublicKey.Y = c.ScalarBaseMult(k.Bytes())
+ return
+}
+
+// hashToInt converts a hash value to an integer. There is some disagreement
+// about how this is done. [NSA] suggests that this is done in the obvious
+// manner, but [SECG] truncates the hash to the bit-length of the curve order
+// first. We follow [SECG] because that's what OpenSSL does.
+func hashToInt(hash []byte, c *elliptic.Curve) *big.Int {
+ orderBits := c.N.BitLen()
+ orderBytes := (orderBits + 7) / 8
+ if len(hash) > orderBytes {
+ hash = hash[:orderBytes]
+ }
+
+ ret := new(big.Int).SetBytes(hash)
+ excess := orderBytes*8 - orderBits
+ if excess > 0 {
+ ret.Rsh(ret, uint(excess))
+ }
+ return ret
+}
+
+// Sign signs an arbitrary length hash (which should be the result of hashing a
+// larger message) using the private key, priv. It returns the signature as a
+// pair of integers. The security of the private key depends on the entropy of
+// rand.
+func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err os.Error) {
+ // See [NSA] 3.4.1
+ c := priv.PublicKey.Curve
+
+ var k, kInv *big.Int
+ for {
+ for {
+ k, err = randFieldElement(c, rand)
+ if err != nil {
+ r = nil
+ return
+ }
+
+ kInv = new(big.Int).ModInverse(k, c.N)
+ r, _ = priv.Curve.ScalarBaseMult(k.Bytes())
+ r.Mod(r, priv.Curve.N)
+ if r.Sign() != 0 {
+ break
+ }
+ }
+
+ e := hashToInt(hash, c)
+ s = new(big.Int).Mul(priv.D, r)
+ s.Add(s, e)
+ s.Mul(s, kInv)
+ s.Mod(s, priv.PublicKey.Curve.N)
+ if s.Sign() != 0 {
+ break
+ }
+ }
+
+ return
+}
+
+// Verify verifies the signature in r, s of hash using the public key, pub. It
+// returns true iff the signature is valid.
+func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool {
+ // See [NSA] 3.4.2
+ c := pub.Curve
+
+ if r.Sign() == 0 || s.Sign() == 0 {
+ return false
+ }
+ if r.Cmp(c.N) >= 0 || s.Cmp(c.N) >= 0 {
+ return false
+ }
+ e := hashToInt(hash, c)
+ w := new(big.Int).ModInverse(s, c.N)
+
+ u1 := e.Mul(e, w)
+ u2 := w.Mul(r, w)
+
+ x1, y1 := c.ScalarBaseMult(u1.Bytes())
+ x2, y2 := c.ScalarMult(pub.X, pub.Y, u2.Bytes())
+ if x1.Cmp(x2) == 0 {
+ return false
+ }
+ x, _ := c.Add(x1, y1, x2, y2)
+ x.Mod(x, c.N)
+ return x.Cmp(r) == 0
+}
diff --git a/src/pkg/crypto/ecdsa/ecdsa_test.go b/src/pkg/crypto/ecdsa/ecdsa_test.go
new file mode 100644
index 000000000..d6b403914
--- /dev/null
+++ b/src/pkg/crypto/ecdsa/ecdsa_test.go
@@ -0,0 +1,227 @@
+// 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 ecdsa
+
+import (
+ "big"
+ "crypto/elliptic"
+ "crypto/sha1"
+ "crypto/rand"
+ "encoding/hex"
+ "testing"
+)
+
+func testKeyGeneration(t *testing.T, c *elliptic.Curve, tag string) {
+ priv, err := GenerateKey(c, rand.Reader)
+ if err != nil {
+ t.Errorf("%s: error: %s", tag, err)
+ return
+ }
+ if !c.IsOnCurve(priv.PublicKey.X, priv.PublicKey.Y) {
+ t.Errorf("%s: public key invalid: %s", tag, err)
+ }
+}
+
+func TestKeyGeneration(t *testing.T) {
+ testKeyGeneration(t, elliptic.P224(), "p224")
+ if testing.Short() {
+ return
+ }
+ testKeyGeneration(t, elliptic.P256(), "p256")
+ testKeyGeneration(t, elliptic.P384(), "p384")
+ testKeyGeneration(t, elliptic.P521(), "p521")
+}
+
+func testSignAndVerify(t *testing.T, c *elliptic.Curve, tag string) {
+ priv, _ := GenerateKey(c, rand.Reader)
+
+ hashed := []byte("testing")
+ r, s, err := Sign(rand.Reader, priv, hashed)
+ if err != nil {
+ t.Errorf("%s: error signing: %s", tag, err)
+ return
+ }
+
+ if !Verify(&priv.PublicKey, hashed, r, s) {
+ t.Errorf("%s: Verify failed", tag)
+ }
+
+ hashed[0] ^= 0xff
+ if Verify(&priv.PublicKey, hashed, r, s) {
+ t.Errorf("%s: Verify always works!", tag)
+ }
+}
+
+func TestSignAndVerify(t *testing.T) {
+ testSignAndVerify(t, elliptic.P224(), "p224")
+ if testing.Short() {
+ return
+ }
+ testSignAndVerify(t, elliptic.P256(), "p256")
+ testSignAndVerify(t, elliptic.P384(), "p384")
+ testSignAndVerify(t, elliptic.P521(), "p521")
+}
+
+func fromHex(s string) *big.Int {
+ r, ok := new(big.Int).SetString(s, 16)
+ if !ok {
+ panic("bad hex")
+ }
+ return r
+}
+
+// These test vectors were taken from
+// http://csrc.nist.gov/groups/STM/cavp/documents/dss/ecdsatestvectors.zip
+var testVectors = []struct {
+ msg string
+ Qx, Qy string
+ r, s string
+ ok bool
+}{
+ {
+ "09626b45493672e48f3d1226a3aff3201960e577d33a7f72c7eb055302db8fe8ed61685dd036b554942a5737cd1512cdf811ee0c00e6dd2f08c69f08643be396e85dafda664801e772cdb7396868ac47b172245b41986aa2648cb77fbbfa562581be06651355a0c4b090f9d17d8f0ab6cced4e0c9d386cf465a516630f0231bd",
+ "9504b5b82d97a264d8b3735e0568decabc4b6ca275bc53cbadfc1c40",
+ "03426f80e477603b10dee670939623e3da91a94267fc4e51726009ed",
+ "81d3ac609f9575d742028dd496450a58a60eea2dcf8b9842994916e1",
+ "96a8c5f382c992e8f30ccce9af120b067ec1d74678fa8445232f75a5",
+ false,
+ },
+ {
+ "96b2b6536f6df29be8567a72528aceeaccbaa66c66c534f3868ca9778b02faadb182e4ed34662e73b9d52ecbe9dc8e875fc05033c493108b380689ebf47e5b062e6a0cdb3dd34ce5fe347d92768d72f7b9b377c20aea927043b509c078ed2467d7113405d2ddd458811e6faf41c403a2a239240180f1430a6f4330df5d77de37",
+ "851e3100368a22478a0029353045ae40d1d8202ef4d6533cfdddafd8",
+ "205302ac69457dd345e86465afa72ee8c74ca97e2b0b999aec1f10c2",
+ "4450c2d38b697e990721aa2dbb56578d32b4f5aeb3b9072baa955ee0",
+ "e26d4b589166f7b4ba4b1c8fce823fa47aad22f8c9c396b8c6526e12",
+ false,
+ },
+ {
+ "86778dbb4a068a01047a8d245d632f636c11d2ad350740b36fad90428b454ad0f120cb558d12ea5c8a23db595d87543d06d1ef489263d01ee529871eb68737efdb8ff85bc7787b61514bed85b7e01d6be209e0a4eb0db5c8df58a5c5bf706d76cb2bdf7800208639e05b89517155d11688236e6a47ed37d8e5a2b1e0adea338e",
+ "ad5bda09d319a717c1721acd6688d17020b31b47eef1edea57ceeffc",
+ "c8ce98e181770a7c9418c73c63d01494b8b80a41098c5ea50692c984",
+ "de5558c257ab4134e52c19d8db3b224a1899cbd08cc508ce8721d5e9",
+ "745db7af5a477e5046705c0a5eff1f52cb94a79d481f0c5a5e108ecd",
+ true,
+ },
+ {
+ "4bc6ef1958556686dab1e39c3700054a304cbd8f5928603dcd97fafd1f29e69394679b638f71c9344ce6a535d104803d22119f57b5f9477e253817a52afa9bfbc9811d6cc8c8be6b6566c6ef48b439bbb532abe30627548c598867f3861ba0b154dc1c3deca06eb28df8efd28258554b5179883a36fbb1eecf4f93ee19d41e3d",
+ "cc5eea2edf964018bdc0504a3793e4d2145142caa09a72ac5fb8d3e8",
+ "a48d78ae5d08aa725342773975a00d4219cf7a8029bb8cf3c17c374a",
+ "67b861344b4e416d4094472faf4272f6d54a497177fbc5f9ef292836",
+ "1d54f3fcdad795bf3b23408ecbac3e1321d1d66f2e4e3d05f41f7020",
+ false,
+ },
+ {
+ "bb658732acbf3147729959eb7318a2058308b2739ec58907dd5b11cfa3ecf69a1752b7b7d806fe00ec402d18f96039f0b78dbb90a59c4414fb33f1f4e02e4089de4122cd93df5263a95be4d7084e2126493892816e6a5b4ed123cb705bf930c8f67af0fb4514d5769232a9b008a803af225160ce63f675bd4872c4c97b146e5e",
+ "6234c936e27bf141fc7534bfc0a7eedc657f91308203f1dcbd642855",
+ "27983d87ca785ef4892c3591ef4a944b1deb125dd58bd351034a6f84",
+ "e94e05b42d01d0b965ffdd6c3a97a36a771e8ea71003de76c4ecb13f",
+ "1dc6464ffeefbd7872a081a5926e9fc3e66d123f1784340ba17737e9",
+ false,
+ },
+ {
+ "7c00be9123bfa2c4290be1d8bc2942c7f897d9a5b7917e3aabd97ef1aab890f148400a89abd554d19bec9d8ed911ce57b22fbcf6d30ca2115f13ce0a3f569a23bad39ee645f624c49c60dcfc11e7d2be24de9c905596d8f23624d63dc46591d1f740e46f982bfae453f107e80db23545782be23ce43708245896fc54e1ee5c43",
+ "9f3f037282aaf14d4772edffff331bbdda845c3f65780498cde334f1",
+ "8308ee5a16e3bcb721b6bc30000a0419bc1aaedd761be7f658334066",
+ "6381d7804a8808e3c17901e4d283b89449096a8fba993388fa11dc54",
+ "8e858f6b5b253686a86b757bad23658cda53115ac565abca4e3d9f57",
+ false,
+ },
+ {
+ "cffc122a44840dc705bb37130069921be313d8bde0b66201aebc48add028ca131914ef2e705d6bedd19dc6cf9459bbb0f27cdfe3c50483808ffcdaffbeaa5f062e097180f07a40ef4ab6ed03fe07ed6bcfb8afeb42c97eafa2e8a8df469de07317c5e1494c41547478eff4d8c7d9f0f484ad90fedf6e1c35ee68fa73f1691601",
+ "a03b88a10d930002c7b17ca6af2fd3e88fa000edf787dc594f8d4fd4",
+ "e0cf7acd6ddc758e64847fe4df9915ebda2f67cdd5ec979aa57421f5",
+ "387b84dcf37dc343c7d2c5beb82f0bf8bd894b395a7b894565d296c1",
+ "4adc12ce7d20a89ce3925e10491c731b15ddb3f339610857a21b53b4",
+ false,
+ },
+ {
+ "26e0e0cafd85b43d16255908ccfd1f061c680df75aba3081246b337495783052ba06c60f4a486c1591a4048bae11b4d7fec4f161d80bdc9a7b79d23e44433ed625eab280521a37f23dd3e1bdc5c6a6cfaa026f3c45cf703e76dab57add93fe844dd4cda67dc3bddd01f9152579e49df60969b10f09ce9372fdd806b0c7301866",
+ "9a8983c42f2b5a87c37a00458b5970320d247f0c8a88536440173f7d",
+ "15e489ec6355351361900299088cfe8359f04fe0cab78dde952be80c",
+ "929a21baa173d438ec9f28d6a585a2f9abcfc0a4300898668e476dc0",
+ "59a853f046da8318de77ff43f26fe95a92ee296fa3f7e56ce086c872",
+ true,
+ },
+ {
+ "1078eac124f48ae4f807e946971d0de3db3748dd349b14cca5c942560fb25401b2252744f18ad5e455d2d97ed5ae745f55ff509c6c8e64606afe17809affa855c4c4cdcaf6b69ab4846aa5624ed0687541aee6f2224d929685736c6a23906d974d3c257abce1a3fb8db5951b89ecb0cda92b5207d93f6618fd0f893c32cf6a6e",
+ "d6e55820bb62c2be97650302d59d667a411956138306bd566e5c3c2b",
+ "631ab0d64eaf28a71b9cbd27a7a88682a2167cee6251c44e3810894f",
+ "65af72bc7721eb71c2298a0eb4eed3cec96a737cc49125706308b129",
+ "bd5a987c78e2d51598dbd9c34a9035b0069c580edefdacee17ad892a",
+ false,
+ },
+ {
+ "919deb1fdd831c23481dfdb2475dcbe325b04c34f82561ced3d2df0b3d749b36e255c4928973769d46de8b95f162b53cd666cad9ae145e7fcfba97919f703d864efc11eac5f260a5d920d780c52899e5d76f8fe66936ff82130761231f536e6a3d59792f784902c469aa897aabf9a0678f93446610d56d5e0981e4c8a563556b",
+ "269b455b1024eb92d860a420f143ac1286b8cce43031562ae7664574",
+ "baeb6ca274a77c44a0247e5eb12ca72bdd9a698b3f3ae69c9f1aaa57",
+ "cb4ec2160f04613eb0dfe4608486091a25eb12aa4dec1afe91cfb008",
+ "40b01d8cd06589481574f958b98ca08ade9d2a8fe31024375c01bb40",
+ false,
+ },
+ {
+ "6e012361250dacf6166d2dd1aa7be544c3206a9d43464b3fcd90f3f8cf48d08ec099b59ba6fe7d9bdcfaf244120aed1695d8be32d1b1cd6f143982ab945d635fb48a7c76831c0460851a3d62b7209c30cd9c2abdbe3d2a5282a9fcde1a6f418dd23c409bc351896b9b34d7d3a1a63bbaf3d677e612d4a80fa14829386a64b33f",
+ "6d2d695efc6b43b13c14111f2109608f1020e3e03b5e21cfdbc82fcd",
+ "26a4859296b7e360b69cf40be7bd97ceaffa3d07743c8489fc47ca1b",
+ "9a8cb5f2fdc288b7183c5b32d8e546fc2ed1ca4285eeae00c8b572ad",
+ "8c623f357b5d0057b10cdb1a1593dab57cda7bdec9cf868157a79b97",
+ true,
+ },
+ {
+ "bf6bd7356a52b234fe24d25557200971fc803836f6fec3cade9642b13a8e7af10ab48b749de76aada9d8927f9b12f75a2c383ca7358e2566c4bb4f156fce1fd4e87ef8c8d2b6b1bdd351460feb22cdca0437ac10ca5e0abbbce9834483af20e4835386f8b1c96daaa41554ceee56730aac04f23a5c765812efa746051f396566",
+ "14250131b2599939cf2d6bc491be80ddfe7ad9de644387ee67de2d40",
+ "b5dc473b5d014cd504022043c475d3f93c319a8bdcb7262d9e741803",
+ "4f21642f2201278a95339a80f75cc91f8321fcb3c9462562f6cbf145",
+ "452a5f816ea1f75dee4fd514fa91a0d6a43622981966c59a1b371ff8",
+ false,
+ },
+ {
+ "0eb7f4032f90f0bd3cf9473d6d9525d264d14c031a10acd31a053443ed5fe919d5ac35e0be77813071b4062f0b5fdf58ad5f637b76b0b305aec18f82441b6e607b44cdf6e0e3c7c57f24e6fd565e39430af4a6b1d979821ed0175fa03e3125506847654d7e1ae904ce1190ae38dc5919e257bdac2db142a6e7cd4da6c2e83770",
+ "d1f342b7790a1667370a1840255ac5bbbdc66f0bc00ae977d99260ac",
+ "76416cabae2de9a1000b4646338b774baabfa3db4673790771220cdb",
+ "bc85e3fc143d19a7271b2f9e1c04b86146073f3fab4dda1c3b1f35ca",
+ "9a5c70ede3c48d5f43307a0c2a4871934424a3303b815df4bb0f128e",
+ false,
+ },
+ {
+ "5cc25348a05d85e56d4b03cec450128727bc537c66ec3a9fb613c151033b5e86878632249cba83adcefc6c1e35dcd31702929c3b57871cda5c18d1cf8f9650a25b917efaed56032e43b6fc398509f0d2997306d8f26675f3a8683b79ce17128e006aa0903b39eeb2f1001be65de0520115e6f919de902b32c38d691a69c58c92",
+ "7e49a7abf16a792e4c7bbc4d251820a2abd22d9f2fc252a7bf59c9a6",
+ "44236a8fb4791c228c26637c28ae59503a2f450d4cfb0dc42aa843b9",
+ "084461b4050285a1a85b2113be76a17878d849e6bc489f4d84f15cd8",
+ "079b5bddcc4d45de8dbdfd39f69817c7e5afa454a894d03ee1eaaac3",
+ false,
+ },
+ {
+ "1951533ce33afb58935e39e363d8497a8dd0442018fd96dff167b3b23d7206a3ee182a3194765df4768a3284e23b8696c199b4686e670d60c9d782f08794a4bccc05cffffbd1a12acd9eb1cfa01f7ebe124da66ecff4599ea7720c3be4bb7285daa1a86ebf53b042bd23208d468c1b3aa87381f8e1ad63e2b4c2ba5efcf05845",
+ "31945d12ebaf4d81f02be2b1768ed80784bf35cf5e2ff53438c11493",
+ "a62bebffac987e3b9d3ec451eb64c462cdf7b4aa0b1bbb131ceaa0a4",
+ "bc3c32b19e42b710bca5c6aaa128564da3ddb2726b25f33603d2af3c",
+ "ed1a719cc0c507edc5239d76fe50e2306c145ad252bd481da04180c0",
+ false,
+ },
+}
+
+func TestVectors(t *testing.T) {
+ sha := sha1.New()
+
+ for i, test := range testVectors {
+ pub := PublicKey{
+ Curve: elliptic.P224(),
+ X: fromHex(test.Qx),
+ Y: fromHex(test.Qy),
+ }
+ msg, _ := hex.DecodeString(test.msg)
+ sha.Reset()
+ sha.Write(msg)
+ hashed := sha.Sum()
+ r := fromHex(test.r)
+ s := fromHex(test.s)
+ if Verify(&pub, hashed, r, s) != test.ok {
+ t.Errorf("%d: bad result", i)
+ }
+ if testing.Short() {
+ break
+ }
+ }
+}
diff --git a/src/pkg/crypto/elliptic/elliptic.go b/src/pkg/crypto/elliptic/elliptic.go
index beac45ca0..2296e9607 100644
--- a/src/pkg/crypto/elliptic/elliptic.go
+++ b/src/pkg/crypto/elliptic/elliptic.go
@@ -24,6 +24,7 @@ import (
// See http://www.hyperelliptic.org/EFD/g1p/auto-shortw.html
type Curve struct {
P *big.Int // the order of the underlying field
+ N *big.Int // the order of the base point
B *big.Int // the constant of the curve equation
Gx, Gy *big.Int // (x,y) of the base point
BitSize int // the size of the underlying field
@@ -315,6 +316,7 @@ func initP224() {
// See FIPS 186-3, section D.2.2
p224 = new(Curve)
p224.P, _ = new(big.Int).SetString("26959946667150639794667015087019630673557916260026308143510066298881", 10)
+ p224.N, _ = new(big.Int).SetString("26959946667150639794667015087019625940457807714424391721682722368061", 10)
p224.B, _ = new(big.Int).SetString("b4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4", 16)
p224.Gx, _ = new(big.Int).SetString("b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21", 16)
p224.Gy, _ = new(big.Int).SetString("bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34", 16)
@@ -325,6 +327,7 @@ func initP256() {
// See FIPS 186-3, section D.2.3
p256 = new(Curve)
p256.P, _ = new(big.Int).SetString("115792089210356248762697446949407573530086143415290314195533631308867097853951", 10)
+ p256.N, _ = new(big.Int).SetString("115792089210356248762697446949407573529996955224135760342422259061068512044369", 10)
p256.B, _ = new(big.Int).SetString("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16)
p256.Gx, _ = new(big.Int).SetString("6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", 16)
p256.Gy, _ = new(big.Int).SetString("4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5", 16)
@@ -335,6 +338,7 @@ func initP384() {
// See FIPS 186-3, section D.2.4
p384 = new(Curve)
p384.P, _ = new(big.Int).SetString("39402006196394479212279040100143613805079739270465446667948293404245721771496870329047266088258938001861606973112319", 10)
+ p384.N, _ = new(big.Int).SetString("39402006196394479212279040100143613805079739270465446667946905279627659399113263569398956308152294913554433653942643", 10)
p384.B, _ = new(big.Int).SetString("b3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef", 16)
p384.Gx, _ = new(big.Int).SetString("aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7", 16)
p384.Gy, _ = new(big.Int).SetString("3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f", 16)
@@ -345,6 +349,7 @@ func initP521() {
// See FIPS 186-3, section D.2.5
p521 = new(Curve)
p521.P, _ = new(big.Int).SetString("6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151", 10)
+ p521.N, _ = new(big.Int).SetString("6864797660130609714981900799081393217269435300143305409394463459185543183397655394245057746333217197532963996371363321113864768612440380340372808892707005449", 10)
p521.B, _ = new(big.Int).SetString("051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00", 16)
p521.Gx, _ = new(big.Int).SetString("c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66", 16)
p521.Gy, _ = new(big.Int).SetString("11839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650", 16)
diff --git a/src/pkg/crypto/elliptic/elliptic_test.go b/src/pkg/crypto/elliptic/elliptic_test.go
index 6ae6fb96d..02083a986 100644
--- a/src/pkg/crypto/elliptic/elliptic_test.go
+++ b/src/pkg/crypto/elliptic/elliptic_test.go
@@ -297,6 +297,9 @@ func TestBaseMult(t *testing.T) {
if fmt.Sprintf("%x", x) != e.x || fmt.Sprintf("%x", y) != e.y {
t.Errorf("%d: bad output for k=%s: got (%x, %s), want (%s, %s)", i, e.k, x, y, e.x, e.y)
}
+ if testing.Short() && i > 5 {
+ break
+ }
}
}
diff --git a/src/pkg/crypto/openpgp/armor/armor.go b/src/pkg/crypto/openpgp/armor/armor.go
index 0c5ae9d71..d695a8c33 100644
--- a/src/pkg/crypto/openpgp/armor/armor.go
+++ b/src/pkg/crypto/openpgp/armor/armor.go
@@ -7,10 +7,10 @@
package armor
import (
+ "bufio"
"bytes"
"crypto/openpgp/error"
"encoding/base64"
- "encoding/line"
"io"
"os"
)
@@ -63,7 +63,7 @@ var armorEndOfLine = []byte("-----")
// lineReader wraps a line based reader. It watches for the end of an armor
// block and records the expected CRC value.
type lineReader struct {
- in *line.Reader
+ in *bufio.Reader
buf []byte
eof bool
crc uint32
@@ -156,7 +156,7 @@ func (r *openpgpReader) Read(p []byte) (n int, err os.Error) {
// given Reader is not usable after calling this function: an arbitary amount
// of data may have been read past the end of the block.
func Decode(in io.Reader) (p *Block, err os.Error) {
- r := line.NewReader(in, 100)
+ r, _ := bufio.NewReaderSize(in, 100)
var line []byte
ignoreNext := false
diff --git a/src/pkg/crypto/openpgp/armor/encode.go b/src/pkg/crypto/openpgp/armor/encode.go
index 0f7de0241..99dee375e 100644
--- a/src/pkg/crypto/openpgp/armor/encode.go
+++ b/src/pkg/crypto/openpgp/armor/encode.go
@@ -18,9 +18,9 @@ var armorEndOfLineOut = []byte("-----\n")
// writeSlices writes its arguments to the given Writer.
func writeSlices(out io.Writer, slices ...[]byte) (err os.Error) {
for _, s := range slices {
- _, err := out.Write(s)
+ _, err = out.Write(s)
if err != nil {
- return
+ return err
}
}
return
diff --git a/src/pkg/crypto/openpgp/packet/packet.go b/src/pkg/crypto/openpgp/packet/packet.go
index 269603ba4..57ff3afbf 100644
--- a/src/pkg/crypto/openpgp/packet/packet.go
+++ b/src/pkg/crypto/openpgp/packet/packet.go
@@ -7,6 +7,7 @@
package packet
import (
+ "big"
"crypto/aes"
"crypto/cast5"
"crypto/cipher"
@@ -166,10 +167,10 @@ func readHeader(r io.Reader) (tag packetType, length int64, contents io.Reader,
return
}
-// serialiseHeader writes an OpenPGP packet header to w. See RFC 4880, section
+// serializeHeader writes an OpenPGP packet header to w. See RFC 4880, section
// 4.2.
-func serialiseHeader(w io.Writer, ptype packetType, length int) (err os.Error) {
- var buf [5]byte
+func serializeHeader(w io.Writer, ptype packetType, length int) (err os.Error) {
+ var buf [6]byte
var n int
buf[0] = 0x80 | 0x40 | byte(ptype)
@@ -178,16 +179,16 @@ func serialiseHeader(w io.Writer, ptype packetType, length int) (err os.Error) {
n = 2
} else if length < 8384 {
length -= 192
- buf[1] = byte(length >> 8)
+ buf[1] = 192 + byte(length>>8)
buf[2] = byte(length)
n = 3
} else {
- buf[0] = 255
- buf[1] = byte(length >> 24)
- buf[2] = byte(length >> 16)
- buf[3] = byte(length >> 8)
- buf[4] = byte(length)
- n = 5
+ buf[1] = 255
+ buf[2] = byte(length >> 24)
+ buf[3] = byte(length >> 16)
+ buf[4] = byte(length >> 8)
+ buf[5] = byte(length)
+ n = 6
}
_, err = w.Write(buf[:n])
@@ -371,7 +372,7 @@ func (cipher CipherFunction) new(key []byte) (block cipher.Block) {
// readMPI reads a big integer from r. The bit length returned is the bit
// length that was specified in r. This is preserved so that the integer can be
-// reserialised exactly.
+// reserialized exactly.
func readMPI(r io.Reader) (mpi []byte, bitLength uint16, err os.Error) {
var buf [2]byte
_, err = readFull(r, buf[0:])
@@ -385,7 +386,7 @@ func readMPI(r io.Reader) (mpi []byte, bitLength uint16, err os.Error) {
return
}
-// writeMPI serialises a big integer to r.
+// writeMPI serializes a big integer to w.
func writeMPI(w io.Writer, bitLength uint16, mpiBytes []byte) (err os.Error) {
_, err = w.Write([]byte{byte(bitLength >> 8), byte(bitLength)})
if err == nil {
@@ -393,3 +394,8 @@ func writeMPI(w io.Writer, bitLength uint16, mpiBytes []byte) (err os.Error) {
}
return
}
+
+// writeBig serializes a *big.Int to w.
+func writeBig(w io.Writer, i *big.Int) os.Error {
+ return writeMPI(w, uint16(i.BitLen()), i.Bytes())
+}
diff --git a/src/pkg/crypto/openpgp/packet/packet_test.go b/src/pkg/crypto/openpgp/packet/packet_test.go
index 6789d2abc..1a4692cd4 100644
--- a/src/pkg/crypto/openpgp/packet/packet_test.go
+++ b/src/pkg/crypto/openpgp/packet/packet_test.go
@@ -190,3 +190,23 @@ func TestReadHeader(t *testing.T) {
}
}
}
+
+func TestSerializeHeader(t *testing.T) {
+ tag := packetTypePublicKey
+ lengths := []int{0, 1, 2, 64, 192, 193, 8000, 8384, 8385, 10000}
+
+ for _, length := range lengths {
+ buf := bytes.NewBuffer(nil)
+ serializeHeader(buf, tag, length)
+ tag2, length2, _, err := readHeader(buf)
+ if err != nil {
+ t.Errorf("length %d, err: %s", length, err)
+ }
+ if tag2 != tag {
+ t.Errorf("length %d, tag incorrect (got %d, want %d)", length, tag2, tag)
+ }
+ if int(length2) != length {
+ t.Errorf("length %d, length incorrect (got %d)", length, length2)
+ }
+ }
+}
diff --git a/src/pkg/crypto/openpgp/packet/private_key.go b/src/pkg/crypto/openpgp/packet/private_key.go
index b22891755..694482390 100644
--- a/src/pkg/crypto/openpgp/packet/private_key.go
+++ b/src/pkg/crypto/openpgp/packet/private_key.go
@@ -8,6 +8,7 @@ import (
"big"
"bytes"
"crypto/cipher"
+ "crypto/dsa"
"crypto/openpgp/error"
"crypto/openpgp/s2k"
"crypto/rsa"
@@ -134,7 +135,16 @@ func (pk *PrivateKey) Decrypt(passphrase []byte) os.Error {
}
func (pk *PrivateKey) parsePrivateKey(data []byte) (err os.Error) {
- // TODO(agl): support DSA and ECDSA private keys.
+ switch pk.PublicKey.PubKeyAlgo {
+ case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoRSAEncryptOnly:
+ return pk.parseRSAPrivateKey(data)
+ case PubKeyAlgoDSA:
+ return pk.parseDSAPrivateKey(data)
+ }
+ panic("impossible")
+}
+
+func (pk *PrivateKey) parseRSAPrivateKey(data []byte) (err os.Error) {
rsaPub := pk.PublicKey.PublicKey.(*rsa.PublicKey)
rsaPriv := new(rsa.PrivateKey)
rsaPriv.PublicKey = *rsaPub
@@ -162,3 +172,22 @@ func (pk *PrivateKey) parsePrivateKey(data []byte) (err os.Error) {
return nil
}
+
+func (pk *PrivateKey) parseDSAPrivateKey(data []byte) (err os.Error) {
+ dsaPub := pk.PublicKey.PublicKey.(*dsa.PublicKey)
+ dsaPriv := new(dsa.PrivateKey)
+ dsaPriv.PublicKey = *dsaPub
+
+ buf := bytes.NewBuffer(data)
+ x, _, err := readMPI(buf)
+ if err != nil {
+ return
+ }
+
+ dsaPriv.X = new(big.Int).SetBytes(x)
+ pk.PrivateKey = dsaPriv
+ pk.Encrypted = false
+ pk.encryptedData = nil
+
+ return nil
+}
diff --git a/src/pkg/crypto/openpgp/packet/public_key.go b/src/pkg/crypto/openpgp/packet/public_key.go
index 8866bdaaa..ebef481fb 100644
--- a/src/pkg/crypto/openpgp/packet/public_key.go
+++ b/src/pkg/crypto/openpgp/packet/public_key.go
@@ -11,6 +11,7 @@ import (
"crypto/rsa"
"crypto/sha1"
"encoding/binary"
+ "fmt"
"hash"
"io"
"os"
@@ -178,12 +179,6 @@ func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err os.E
return error.InvalidArgumentError("public key cannot generate signatures")
}
- rsaPublicKey, ok := pk.PublicKey.(*rsa.PublicKey)
- if !ok {
- // TODO(agl): support DSA and ECDSA keys.
- return error.UnsupportedError("non-RSA public key")
- }
-
signed.Write(sig.HashSuffix)
hashBytes := signed.Sum()
@@ -191,11 +186,28 @@ func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err os.E
return error.SignatureError("hash tag doesn't match")
}
- err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, sig.Signature)
- if err != nil {
- return error.SignatureError("RSA verification failure")
+ if pk.PubKeyAlgo != sig.PubKeyAlgo {
+ return error.InvalidArgumentError("public key and signature use different algorithms")
}
- return nil
+
+ switch pk.PubKeyAlgo {
+ case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
+ rsaPublicKey, _ := pk.PublicKey.(*rsa.PublicKey)
+ err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, sig.RSASignature)
+ if err != nil {
+ return error.SignatureError("RSA verification failure")
+ }
+ return nil
+ case PubKeyAlgoDSA:
+ dsaPublicKey, _ := pk.PublicKey.(*dsa.PublicKey)
+ if !dsa.Verify(dsaPublicKey, hashBytes, sig.DSASigR, sig.DSASigS) {
+ return error.SignatureError("DSA verification failure")
+ }
+ return nil
+ default:
+ panic("shouldn't happen")
+ }
+ panic("unreachable")
}
// VerifyKeySignature returns nil iff sig is a valid signature, make by this
@@ -239,9 +251,21 @@ func (pk *PublicKey) VerifyUserIdSignature(id string, sig *Signature) (err os.Er
return pk.VerifySignature(h, sig)
}
+// KeyIdString returns the public key's fingerprint in capital hex
+// (e.g. "6C7EE1B8621CC013").
+func (pk *PublicKey) KeyIdString() string {
+ return fmt.Sprintf("%X", pk.Fingerprint[12:20])
+}
+
+// KeyIdShortString returns the short form of public key's fingerprint
+// in capital hex, as shown by gpg --list-keys (e.g. "621CC013").
+func (pk *PublicKey) KeyIdShortString() string {
+ return fmt.Sprintf("%X", pk.Fingerprint[16:20])
+}
+
// A parsedMPI is used to store the contents of a big integer, along with the
// bit length that was specified in the original input. This allows the MPI to
-// be reserialised exactly.
+// be reserialized exactly.
type parsedMPI struct {
bytes []byte
bitLength uint16
diff --git a/src/pkg/crypto/openpgp/packet/public_key_test.go b/src/pkg/crypto/openpgp/packet/public_key_test.go
index c015f64ae..069388c14 100644
--- a/src/pkg/crypto/openpgp/packet/public_key_test.go
+++ b/src/pkg/crypto/openpgp/packet/public_key_test.go
@@ -16,9 +16,11 @@ var pubKeyTests = []struct {
creationTime uint32
pubKeyAlgo PublicKeyAlgorithm
keyId uint64
+ keyIdString string
+ keyIdShort string
}{
- {rsaPkDataHex, rsaFingerprintHex, 0x4d3c5c10, PubKeyAlgoRSA, 0xa34d7e18c20c31bb},
- {dsaPkDataHex, dsaFingerprintHex, 0x4d432f89, PubKeyAlgoDSA, 0x8e8fbe54062f19ed},
+ {rsaPkDataHex, rsaFingerprintHex, 0x4d3c5c10, PubKeyAlgoRSA, 0xa34d7e18c20c31bb, "A34D7E18C20C31BB", "C20C31BB"},
+ {dsaPkDataHex, dsaFingerprintHex, 0x4d432f89, PubKeyAlgoDSA, 0x8e8fbe54062f19ed, "8E8FBE54062F19ED", "062F19ED"},
}
func TestPublicKeyRead(t *testing.T) {
@@ -46,6 +48,12 @@ func TestPublicKeyRead(t *testing.T) {
if pk.KeyId != test.keyId {
t.Errorf("#%d: bad keyid got:%x want:%x", i, pk.KeyId, test.keyId)
}
+ if g, e := pk.KeyIdString(), test.keyIdString; g != e {
+ t.Errorf("#%d: bad KeyIdString got:%q want:%q", i, g, e)
+ }
+ if g, e := pk.KeyIdShortString(), test.keyIdShort; g != e {
+ t.Errorf("#%d: bad KeyIdShortString got:%q want:%q", i, g, e)
+ }
}
}
diff --git a/src/pkg/crypto/openpgp/packet/signature.go b/src/pkg/crypto/openpgp/packet/signature.go
index fd2518ab4..719657e76 100644
--- a/src/pkg/crypto/openpgp/packet/signature.go
+++ b/src/pkg/crypto/openpgp/packet/signature.go
@@ -5,7 +5,9 @@
package packet
import (
+ "big"
"crypto"
+ "crypto/dsa"
"crypto/openpgp/error"
"crypto/openpgp/s2k"
"crypto/rand"
@@ -29,7 +31,9 @@ type Signature struct {
// of bad signed data.
HashTag [2]byte
CreationTime uint32 // Unix epoch time
- Signature []byte
+
+ RSASignature []byte
+ DSASigR, DSASigS *big.Int
// The following are optional so are nil when not included in the
// signature.
@@ -66,7 +70,7 @@ func (sig *Signature) parse(r io.Reader) (err os.Error) {
sig.SigType = SignatureType(buf[0])
sig.PubKeyAlgo = PublicKeyAlgorithm(buf[1])
switch sig.PubKeyAlgo {
- case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
+ case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA:
default:
err = error.UnsupportedError("public key algorithm " + strconv.Itoa(int(sig.PubKeyAlgo)))
return
@@ -122,8 +126,20 @@ func (sig *Signature) parse(r io.Reader) (err os.Error) {
return
}
- // We have already checked that the public key algorithm is RSA.
- sig.Signature, _, err = readMPI(r)
+ switch sig.PubKeyAlgo {
+ case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
+ sig.RSASignature, _, err = readMPI(r)
+ case PubKeyAlgoDSA:
+ var rBytes, sBytes []byte
+ rBytes, _, err = readMPI(r)
+ sig.DSASigR = new(big.Int).SetBytes(rBytes)
+ if err == nil {
+ sBytes, _, err = readMPI(r)
+ sig.DSASigS = new(big.Int).SetBytes(sBytes)
+ }
+ default:
+ panic("unreachable")
+ }
return
}
@@ -316,8 +332,8 @@ func subpacketLengthLength(length int) int {
return 5
}
-// serialiseSubpacketLength marshals the given length into to.
-func serialiseSubpacketLength(to []byte, length int) int {
+// serializeSubpacketLength marshals the given length into to.
+func serializeSubpacketLength(to []byte, length int) int {
if length < 192 {
to[0] = byte(length)
return 1
@@ -336,7 +352,7 @@ func serialiseSubpacketLength(to []byte, length int) int {
return 5
}
-// subpacketsLength returns the serialised length, in bytes, of the given
+// subpacketsLength returns the serialized length, in bytes, of the given
// subpackets.
func subpacketsLength(subpackets []outputSubpacket, hashed bool) (length int) {
for _, subpacket := range subpackets {
@@ -349,11 +365,11 @@ func subpacketsLength(subpackets []outputSubpacket, hashed bool) (length int) {
return
}
-// serialiseSubpackets marshals the given subpackets into to.
-func serialiseSubpackets(to []byte, subpackets []outputSubpacket, hashed bool) {
+// serializeSubpackets marshals the given subpackets into to.
+func serializeSubpackets(to []byte, subpackets []outputSubpacket, hashed bool) {
for _, subpacket := range subpackets {
if subpacket.hashed == hashed {
- n := serialiseSubpacketLength(to, len(subpacket.contents)+1)
+ n := serializeSubpacketLength(to, len(subpacket.contents)+1)
to[n] = byte(subpacket.subpacketType)
to = to[1+n:]
n = copy(to, subpacket.contents)
@@ -381,7 +397,7 @@ func (sig *Signature) buildHashSuffix() (err os.Error) {
}
sig.HashSuffix[4] = byte(hashedSubpacketsLen >> 8)
sig.HashSuffix[5] = byte(hashedSubpacketsLen)
- serialiseSubpackets(sig.HashSuffix[6:l], sig.outSubpackets, true)
+ serializeSubpackets(sig.HashSuffix[6:l], sig.outSubpackets, true)
trailer := sig.HashSuffix[l:]
trailer[0] = 4
trailer[1] = 0xff
@@ -392,32 +408,66 @@ func (sig *Signature) buildHashSuffix() (err os.Error) {
return
}
-// SignRSA signs a message with an RSA private key. The hash, h, must contain
-// the hash of message to be signed and will be mutated by this function.
-func (sig *Signature) SignRSA(h hash.Hash, priv *rsa.PrivateKey) (err os.Error) {
+func (sig *Signature) signPrepareHash(h hash.Hash) (digest []byte, err os.Error) {
err = sig.buildHashSuffix()
if err != nil {
return
}
h.Write(sig.HashSuffix)
- digest := h.Sum()
+ digest = h.Sum()
copy(sig.HashTag[:], digest)
- sig.Signature, err = rsa.SignPKCS1v15(rand.Reader, priv, sig.Hash, digest)
return
}
-// Serialize marshals sig to w. SignRSA must have been called first.
+// SignRSA signs a message with an RSA private key. The hash, h, must contain
+// the hash of the message to be signed and will be mutated by this function.
+// On success, the signature is stored in sig. Call Serialize to write it out.
+func (sig *Signature) SignRSA(h hash.Hash, priv *rsa.PrivateKey) (err os.Error) {
+ digest, err := sig.signPrepareHash(h)
+ if err != nil {
+ return
+ }
+ sig.RSASignature, err = rsa.SignPKCS1v15(rand.Reader, priv, sig.Hash, digest)
+ return
+}
+
+// SignDSA signs a message with a DSA private key. The hash, h, must contain
+// the hash of the message to be signed and will be mutated by this function.
+// On success, the signature is stored in sig. Call Serialize to write it out.
+func (sig *Signature) SignDSA(h hash.Hash, priv *dsa.PrivateKey) (err os.Error) {
+ digest, err := sig.signPrepareHash(h)
+ if err != nil {
+ return
+ }
+ sig.DSASigR, sig.DSASigS, err = dsa.Sign(rand.Reader, priv, digest)
+ return
+}
+
+// Serialize marshals sig to w. SignRSA or SignDSA must have been called first.
func (sig *Signature) Serialize(w io.Writer) (err os.Error) {
- if sig.Signature == nil {
- return error.InvalidArgumentError("Signature: need to call SignRSA before Serialize")
+ if sig.RSASignature == nil && sig.DSASigR == nil {
+ return error.InvalidArgumentError("Signature: need to call SignRSA or SignDSA before Serialize")
+ }
+
+ sigLength := 0
+ switch sig.PubKeyAlgo {
+ case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
+ sigLength = len(sig.RSASignature)
+ case PubKeyAlgoDSA:
+ sigLength = 2 /* MPI length */
+ sigLength += (sig.DSASigR.BitLen() + 7) / 8
+ sigLength += 2 /* MPI length */
+ sigLength += (sig.DSASigS.BitLen() + 7) / 8
+ default:
+ panic("impossible")
}
unhashedSubpacketsLen := subpacketsLength(sig.outSubpackets, false)
length := len(sig.HashSuffix) - 6 /* trailer not included */ +
2 /* length of unhashed subpackets */ + unhashedSubpacketsLen +
- 2 /* hash tag */ + 2 /* length of signature MPI */ + len(sig.Signature)
- err = serialiseHeader(w, packetTypeSignature, length)
+ 2 /* hash tag */ + 2 /* length of signature MPI */ + sigLength
+ err = serializeHeader(w, packetTypeSignature, length)
if err != nil {
return
}
@@ -430,7 +480,7 @@ func (sig *Signature) Serialize(w io.Writer) (err os.Error) {
unhashedSubpackets := make([]byte, 2+unhashedSubpacketsLen)
unhashedSubpackets[0] = byte(unhashedSubpacketsLen >> 8)
unhashedSubpackets[1] = byte(unhashedSubpacketsLen)
- serialiseSubpackets(unhashedSubpackets[2:], sig.outSubpackets, false)
+ serializeSubpackets(unhashedSubpackets[2:], sig.outSubpackets, false)
_, err = w.Write(unhashedSubpackets)
if err != nil {
@@ -440,7 +490,19 @@ func (sig *Signature) Serialize(w io.Writer) (err os.Error) {
if err != nil {
return
}
- return writeMPI(w, 8*uint16(len(sig.Signature)), sig.Signature)
+
+ switch sig.PubKeyAlgo {
+ case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
+ err = writeMPI(w, 8*uint16(len(sig.RSASignature)), sig.RSASignature)
+ case PubKeyAlgoDSA:
+ err = writeBig(w, sig.DSASigR)
+ if err == nil {
+ err = writeBig(w, sig.DSASigS)
+ }
+ default:
+ panic("impossible")
+ }
+ return
}
// outputSubpacket represents a subpacket to be marshaled.
diff --git a/src/pkg/crypto/openpgp/read_test.go b/src/pkg/crypto/openpgp/read_test.go
index 58199e132..6218d9990 100644
--- a/src/pkg/crypto/openpgp/read_test.go
+++ b/src/pkg/crypto/openpgp/read_test.go
@@ -44,6 +44,17 @@ func TestReadPrivateKeyRing(t *testing.T) {
}
}
+func TestReadDSAKey(t *testing.T) {
+ kring, err := ReadKeyRing(readerFromHex(dsaTestKeyHex))
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ if len(kring) != 1 || uint32(kring[0].PrimaryKey.KeyId) != 0x0CCC0360 {
+ t.Errorf("bad parse: %#v", kring)
+ }
+}
+
func TestGetKeyById(t *testing.T) {
kring, _ := ReadKeyRing(readerFromHex(testKeys1And2Hex))
@@ -192,7 +203,7 @@ func TestSymmetricallyEncrypted(t *testing.T) {
}
}
-func testDetachedSignature(t *testing.T, kring KeyRing, signature io.Reader, sigInput, tag string) {
+func testDetachedSignature(t *testing.T, kring KeyRing, signature io.Reader, sigInput, tag string, expectedSignerKeyId uint64) {
signed := bytes.NewBufferString(sigInput)
signer, err := CheckDetachedSignature(kring, signed, signature)
if err != nil {
@@ -203,7 +214,6 @@ func testDetachedSignature(t *testing.T, kring KeyRing, signature io.Reader, sig
t.Errorf("%s: signer is nil", tag)
return
}
- expectedSignerKeyId := uint64(0xa34d7e18c20c31bb)
if signer.PrimaryKey.KeyId != expectedSignerKeyId {
t.Errorf("%s: wrong signer got:%x want:%x", tag, signer.PrimaryKey.KeyId, expectedSignerKeyId)
}
@@ -211,10 +221,18 @@ func testDetachedSignature(t *testing.T, kring KeyRing, signature io.Reader, sig
func TestDetachedSignature(t *testing.T) {
kring, _ := ReadKeyRing(readerFromHex(testKeys1And2Hex))
- testDetachedSignature(t, kring, readerFromHex(detachedSignatureHex), signedInput, "binary")
- testDetachedSignature(t, kring, readerFromHex(detachedSignatureTextHex), signedInput, "text")
+ testDetachedSignature(t, kring, readerFromHex(detachedSignatureHex), signedInput, "binary", testKey1KeyId)
+ testDetachedSignature(t, kring, readerFromHex(detachedSignatureTextHex), signedInput, "text", testKey1KeyId)
}
+func TestDetachedSignatureDSA(t *testing.T) {
+ kring, _ := ReadKeyRing(readerFromHex(dsaTestKeyHex))
+ testDetachedSignature(t, kring, readerFromHex(detachedSignatureDSAHex), signedInput, "binary", testKey3KeyId)
+}
+
+const testKey1KeyId = 0xA34D7E18C20C31BB
+const testKey3KeyId = 0x338934250CCC0360
+
const signedInput = "Signed message\nline 2\nline 3\n"
const signedTextInput = "Signed message\r\nline 2\r\nline 3\r\n"
@@ -224,6 +242,8 @@ const detachedSignatureHex = "889c04000102000605024d449cd1000a0910a34d7e18c20c31
const detachedSignatureTextHex = "889c04010102000605024d449d21000a0910a34d7e18c20c31bbc8c60400a24fbef7342603a41cb1165767bd18985d015fb72fe05db42db36cfb2f1d455967f1e491194fbf6cf88146222b23bf6ffbd50d17598d976a0417d3192ff9cc0034fd00f287b02e90418bbefe609484b09231e4e7a5f3562e199bf39909ab5276c4d37382fe088f6b5c3426fc1052865da8b3ab158672d58b6264b10823dc4b39"
+const detachedSignatureDSAHex = "884604001102000605024d6c4eac000a0910338934250ccc0360f18d00a087d743d6405ed7b87755476629600b8b694a39e900a0abff8126f46faf1547c1743c37b21b4ea15b8f83"
+
const testKeys1And2Hex = "988d044d3c5c10010400b1d13382944bd5aba23a4312968b5095d14f947f600eb478e14a6fcb16b0e0cac764884909c020bc495cfcc39a935387c661507bdb236a0612fb582cac3af9b29cc2c8c70090616c41b662f4da4c1201e195472eb7f4ae1ccbcbf9940fe21d985e379a5563dde5b9a23d35f1cfaa5790da3b79db26f23695107bfaca8e7b5bcd0011010001b41054657374204b6579203120285253412988b804130102002205024d3c5c10021b03060b090807030206150802090a0b0416020301021e01021780000a0910a34d7e18c20c31bbb5b304009cc45fe610b641a2c146331be94dade0a396e73ca725e1b25c21708d9cab46ecca5ccebc23055879df8f99eea39b377962a400f2ebdc36a7c99c333d74aeba346315137c3ff9d0a09b0273299090343048afb8107cf94cbd1400e3026f0ccac7ecebbc4d78588eb3e478fe2754d3ca664bcf3eac96ca4a6b0c8d7df5102f60f6b0020003b88d044d3c5c10010400b201df61d67487301f11879d514f4248ade90c8f68c7af1284c161098de4c28c2850f1ec7b8e30f959793e571542ffc6532189409cb51c3d30dad78c4ad5165eda18b20d9826d8707d0f742e2ab492103a85bbd9ddf4f5720f6de7064feb0d39ee002219765bb07bcfb8b877f47abe270ddeda4f676108cecb6b9bb2ad484a4f0011010001889f04180102000905024d3c5c10021b0c000a0910a34d7e18c20c31bb1a03040085c8d62e16d05dc4e9dad64953c8a2eed8b6c12f92b1575eeaa6dcf7be9473dd5b24b37b6dffbb4e7c99ed1bd3cb11634be19b3e6e207bed7505c7ca111ccf47cb323bf1f8851eb6360e8034cbff8dd149993c959de89f8f77f38e7e98b8e3076323aa719328e2b408db5ec0d03936efd57422ba04f925cdc7b4c1af7590e40ab0020003988d044d3c5c33010400b488c3e5f83f4d561f317817538d9d0397981e9aef1321ca68ebfae1cf8b7d388e19f4b5a24a82e2fbbf1c6c26557a6c5845307a03d815756f564ac7325b02bc83e87d5480a8fae848f07cb891f2d51ce7df83dcafdc12324517c86d472cc0ee10d47a68fd1d9ae49a6c19bbd36d82af597a0d88cc9c49de9df4e696fc1f0b5d0011010001b42754657374204b6579203220285253412c20656e637279707465642070726976617465206b65792988b804130102002205024d3c5c33021b03060b090807030206150802090a0b0416020301021e01021780000a0910d4984f961e35246b98940400908a73b6a6169f700434f076c6c79015a49bee37130eaf23aaa3cfa9ce60bfe4acaa7bc95f1146ada5867e0079babb38804891f4f0b8ebca57a86b249dee786161a755b7a342e68ccf3f78ed6440a93a6626beb9a37aa66afcd4f888790cb4bb46d94a4ae3eb3d7d3e6b00f6bfec940303e89ec5b32a1eaaacce66497d539328b0020003b88d044d3c5c33010400a4e913f9442abcc7f1804ccab27d2f787ffa592077ca935a8bb23165bd8d57576acac647cc596b2c3f814518cc8c82953c7a4478f32e0cf645630a5ba38d9618ef2bc3add69d459ae3dece5cab778938d988239f8c5ae437807075e06c828019959c644ff05ef6a5a1dab72227c98e3a040b0cf219026640698d7a13d8538a570011010001889f04180102000905024d3c5c33021b0c000a0910d4984f961e35246b26c703ff7ee29ef53bc1ae1ead533c408fa136db508434e233d6e62be621e031e5940bbd4c08142aed0f82217e7c3e1ec8de574bc06ccf3c36633be41ad78a9eacd209f861cae7b064100758545cc9dd83db71806dc1cfd5fb9ae5c7474bba0c19c44034ae61bae5eca379383339dece94ff56ff7aa44a582f3e5c38f45763af577c0934b0020003"
const testKeys1And2PrivateHex = "9501d8044d3c5c10010400b1d13382944bd5aba23a4312968b5095d14f947f600eb478e14a6fcb16b0e0cac764884909c020bc495cfcc39a935387c661507bdb236a0612fb582cac3af9b29cc2c8c70090616c41b662f4da4c1201e195472eb7f4ae1ccbcbf9940fe21d985e379a5563dde5b9a23d35f1cfaa5790da3b79db26f23695107bfaca8e7b5bcd00110100010003ff4d91393b9a8e3430b14d6209df42f98dc927425b881f1209f319220841273a802a97c7bdb8b3a7740b3ab5866c4d1d308ad0d3a79bd1e883aacf1ac92dfe720285d10d08752a7efe3c609b1d00f17f2805b217be53999a7da7e493bfc3e9618fd17018991b8128aea70a05dbce30e4fbe626aa45775fa255dd9177aabf4df7cf0200c1ded12566e4bc2bb590455e5becfb2e2c9796482270a943343a7835de41080582c2be3caf5981aa838140e97afa40ad652a0b544f83eb1833b0957dce26e47b0200eacd6046741e9ce2ec5beb6fb5e6335457844fb09477f83b050a96be7da043e17f3a9523567ed40e7a521f818813a8b8a72209f1442844843ccc7eb9805442570200bdafe0438d97ac36e773c7162028d65844c4d463e2420aa2228c6e50dc2743c3d6c72d0d782a5173fe7be2169c8a9f4ef8a7cf3e37165e8c61b89c346cdc6c1799d2b41054657374204b6579203120285253412988b804130102002205024d3c5c10021b03060b090807030206150802090a0b0416020301021e01021780000a0910a34d7e18c20c31bbb5b304009cc45fe610b641a2c146331be94dade0a396e73ca725e1b25c21708d9cab46ecca5ccebc23055879df8f99eea39b377962a400f2ebdc36a7c99c333d74aeba346315137c3ff9d0a09b0273299090343048afb8107cf94cbd1400e3026f0ccac7ecebbc4d78588eb3e478fe2754d3ca664bcf3eac96ca4a6b0c8d7df5102f60f6b00200009d01d8044d3c5c10010400b201df61d67487301f11879d514f4248ade90c8f68c7af1284c161098de4c28c2850f1ec7b8e30f959793e571542ffc6532189409cb51c3d30dad78c4ad5165eda18b20d9826d8707d0f742e2ab492103a85bbd9ddf4f5720f6de7064feb0d39ee002219765bb07bcfb8b877f47abe270ddeda4f676108cecb6b9bb2ad484a4f00110100010003fd17a7490c22a79c59281fb7b20f5e6553ec0c1637ae382e8adaea295f50241037f8997cf42c1ce26417e015091451b15424b2c59eb8d4161b0975630408e394d3b00f88d4b4e18e2cc85e8251d4753a27c639c83f5ad4a571c4f19d7cd460b9b73c25ade730c99df09637bd173d8e3e981ac64432078263bb6dc30d3e974150dd0200d0ee05be3d4604d2146fb0457f31ba17c057560785aa804e8ca5530a7cd81d3440d0f4ba6851efcfd3954b7e68908fc0ba47f7ac37bf559c6c168b70d3a7c8cd0200da1c677c4bce06a068070f2b3733b0a714e88d62aa3f9a26c6f5216d48d5c2b5624144f3807c0df30be66b3268eeeca4df1fbded58faf49fc95dc3c35f134f8b01fd1396b6c0fc1b6c4f0eb8f5e44b8eace1e6073e20d0b8bc5385f86f1cf3f050f66af789f3ef1fc107b7f4421e19e0349c730c68f0a226981f4e889054fdb4dc149e8e889f04180102000905024d3c5c10021b0c000a0910a34d7e18c20c31bb1a03040085c8d62e16d05dc4e9dad64953c8a2eed8b6c12f92b1575eeaa6dcf7be9473dd5b24b37b6dffbb4e7c99ed1bd3cb11634be19b3e6e207bed7505c7ca111ccf47cb323bf1f8851eb6360e8034cbff8dd149993c959de89f8f77f38e7e98b8e3076323aa719328e2b408db5ec0d03936efd57422ba04f925cdc7b4c1af7590e40ab00200009501fe044d3c5c33010400b488c3e5f83f4d561f317817538d9d0397981e9aef1321ca68ebfae1cf8b7d388e19f4b5a24a82e2fbbf1c6c26557a6c5845307a03d815756f564ac7325b02bc83e87d5480a8fae848f07cb891f2d51ce7df83dcafdc12324517c86d472cc0ee10d47a68fd1d9ae49a6c19bbd36d82af597a0d88cc9c49de9df4e696fc1f0b5d0011010001fe030302e9030f3c783e14856063f16938530e148bc57a7aa3f3e4f90df9dceccdc779bc0835e1ad3d006e4a8d7b36d08b8e0de5a0d947254ecfbd22037e6572b426bcfdc517796b224b0036ff90bc574b5509bede85512f2eefb520fb4b02aa523ba739bff424a6fe81c5041f253f8d757e69a503d3563a104d0d49e9e890b9d0c26f96b55b743883b472caa7050c4acfd4a21f875bdf1258d88bd61224d303dc9df77f743137d51e6d5246b88c406780528fd9a3e15bab5452e5b93970d9dcc79f48b38651b9f15bfbcf6da452837e9cc70683d1bdca94507870f743e4ad902005812488dd342f836e72869afd00ce1850eea4cfa53ce10e3608e13d3c149394ee3cbd0e23d018fcbcb6e2ec5a1a22972d1d462ca05355d0d290dd2751e550d5efb38c6c89686344df64852bf4ff86638708f644e8ec6bd4af9b50d8541cb91891a431326ab2e332faa7ae86cfb6e0540aa63160c1e5cdd5a4add518b303fff0a20117c6bc77f7cfbaf36b04c865c6c2b42754657374204b6579203220285253412c20656e637279707465642070726976617465206b65792988b804130102002205024d3c5c33021b03060b090807030206150802090a0b0416020301021e01021780000a0910d4984f961e35246b98940400908a73b6a6169f700434f076c6c79015a49bee37130eaf23aaa3cfa9ce60bfe4acaa7bc95f1146ada5867e0079babb38804891f4f0b8ebca57a86b249dee786161a755b7a342e68ccf3f78ed6440a93a6626beb9a37aa66afcd4f888790cb4bb46d94a4ae3eb3d7d3e6b00f6bfec940303e89ec5b32a1eaaacce66497d539328b00200009d01fe044d3c5c33010400a4e913f9442abcc7f1804ccab27d2f787ffa592077ca935a8bb23165bd8d57576acac647cc596b2c3f814518cc8c82953c7a4478f32e0cf645630a5ba38d9618ef2bc3add69d459ae3dece5cab778938d988239f8c5ae437807075e06c828019959c644ff05ef6a5a1dab72227c98e3a040b0cf219026640698d7a13d8538a570011010001fe030302e9030f3c783e148560f936097339ae381d63116efcf802ff8b1c9360767db5219cc987375702a4123fd8657d3e22700f23f95020d1b261eda5257e9a72f9a918e8ef22dd5b3323ae03bbc1923dd224db988cadc16acc04b120a9f8b7e84da9716c53e0334d7b66586ddb9014df604b41be1e960dcfcbc96f4ed150a1a0dd070b9eb14276b9b6be413a769a75b519a53d3ecc0c220e85cd91ca354d57e7344517e64b43b6e29823cbd87eae26e2b2e78e6dedfbb76e3e9f77bcb844f9a8932eb3db2c3f9e44316e6f5d60e9e2a56e46b72abe6b06dc9a31cc63f10023d1f5e12d2a3ee93b675c96f504af0001220991c88db759e231b3320dcedf814dcf723fd9857e3d72d66a0f2af26950b915abdf56c1596f46a325bf17ad4810d3535fb02a259b247ac3dbd4cc3ecf9c51b6c07cebb009c1506fba0a89321ec8683e3fd009a6e551d50243e2d5092fefb3321083a4bad91320dc624bd6b5dddf93553e3d53924c05bfebec1fb4bd47e89a1a889f04180102000905024d3c5c33021b0c000a0910d4984f961e35246b26c703ff7ee29ef53bc1ae1ead533c408fa136db508434e233d6e62be621e031e5940bbd4c08142aed0f82217e7c3e1ec8de574bc06ccf3c36633be41ad78a9eacd209f861cae7b064100758545cc9dd83db71806dc1cfd5fb9ae5c7474bba0c19c44034ae61bae5eca379383339dece94ff56ff7aa44a582f3e5c38f45763af577c0934b0020000"
@@ -235,3 +255,7 @@ const signedTextMessageHex = "a3019bc0cbccc8c4b8d8b74ee2108fe16ec6d36a250cbece0c
const signedEncryptedMessageHex = "848c032a67d68660df41c70103ff5789d0de26b6a50c985a02a13131ca829c413a35d0e6fa8d6842599252162808ac7439c72151c8c6183e76923fe3299301414d0c25a2f06a2257db3839e7df0ec964773f6e4c4ac7ff3b48c444237166dd46ba8ff443a5410dc670cb486672fdbe7c9dfafb75b4fea83af3a204fe2a7dfa86bd20122b4f3d2646cbeecb8f7be8d2c03b018bd210b1d3791e1aba74b0f1034e122ab72e760492c192383cf5e20b5628bd043272d63df9b923f147eb6091cd897553204832aba48fec54aa447547bb16305a1024713b90e77fd0065f1918271947549205af3c74891af22ee0b56cd29bfec6d6e351901cd4ab3ece7c486f1e32a792d4e474aed98ee84b3f591c7dff37b64e0ecd68fd036d517e412dcadf85840ce184ad7921ad446c4ee28db80447aea1ca8d4f574db4d4e37688158ddd19e14ee2eab4873d46947d65d14a23e788d912cf9a19624ca7352469b72a83866b7c23cb5ace3deab3c7018061b0ba0f39ed2befe27163e5083cf9b8271e3e3d52cc7ad6e2a3bd81d4c3d7022f8d"
const symmetricallyEncryptedCompressedHex = "8c0d04030302eb4a03808145d0d260c92f714339e13de5a79881216431925bf67ee2898ea61815f07894cd0703c50d0a76ef64d482196f47a8bc729af9b80bb6"
+
+const dsaTestKeyHex = "9901a2044d6c49de110400cb5ce438cf9250907ac2ba5bf6547931270b89f7c4b53d9d09f4d0213a5ef2ec1f26806d3d259960f872a4a102ef1581ea3f6d6882d15134f21ef6a84de933cc34c47cc9106efe3bd84c6aec12e78523661e29bc1a61f0aab17fa58a627fd5fd33f5149153fbe8cd70edf3d963bc287ef875270ff14b5bfdd1bca4483793923b00a0fe46d76cb6e4cbdc568435cd5480af3266d610d303fe33ae8273f30a96d4d34f42fa28ce1112d425b2e3bf7ea553d526e2db6b9255e9dc7419045ce817214d1a0056dbc8d5289956a4b1b69f20f1105124096e6a438f41f2e2495923b0f34b70642607d45559595c7fe94d7fa85fc41bf7d68c1fd509ebeaa5f315f6059a446b9369c277597e4f474a9591535354c7e7f4fd98a08aa60400b130c24ff20bdfbf683313f5daebf1c9b34b3bdadfc77f2ddd72ee1fb17e56c473664bc21d66467655dd74b9005e3a2bacce446f1920cd7017231ae447b67036c9b431b8179deacd5120262d894c26bc015bffe3d827ba7087ad9b700d2ca1f6d16cc1786581e5dd065f293c31209300f9b0afcc3f7c08dd26d0a22d87580b4db41054657374204b65792033202844534129886204131102002205024d6c49de021b03060b090807030206150802090a0b0416020301021e01021780000a0910338934250ccc03607e0400a0bdb9193e8a6b96fc2dfc108ae848914b504481f100a09c4dc148cb693293a67af24dd40d2b13a9e36794"
+
+const dsaTestKeyPrivateHex = "9501bb044d6c49de110400cb5ce438cf9250907ac2ba5bf6547931270b89f7c4b53d9d09f4d0213a5ef2ec1f26806d3d259960f872a4a102ef1581ea3f6d6882d15134f21ef6a84de933cc34c47cc9106efe3bd84c6aec12e78523661e29bc1a61f0aab17fa58a627fd5fd33f5149153fbe8cd70edf3d963bc287ef875270ff14b5bfdd1bca4483793923b00a0fe46d76cb6e4cbdc568435cd5480af3266d610d303fe33ae8273f30a96d4d34f42fa28ce1112d425b2e3bf7ea553d526e2db6b9255e9dc7419045ce817214d1a0056dbc8d5289956a4b1b69f20f1105124096e6a438f41f2e2495923b0f34b70642607d45559595c7fe94d7fa85fc41bf7d68c1fd509ebeaa5f315f6059a446b9369c277597e4f474a9591535354c7e7f4fd98a08aa60400b130c24ff20bdfbf683313f5daebf1c9b34b3bdadfc77f2ddd72ee1fb17e56c473664bc21d66467655dd74b9005e3a2bacce446f1920cd7017231ae447b67036c9b431b8179deacd5120262d894c26bc015bffe3d827ba7087ad9b700d2ca1f6d16cc1786581e5dd065f293c31209300f9b0afcc3f7c08dd26d0a22d87580b4d00009f592e0619d823953577d4503061706843317e4fee083db41054657374204b65792033202844534129886204131102002205024d6c49de021b03060b090807030206150802090a0b0416020301021e01021780000a0910338934250ccc03607e0400a0bdb9193e8a6b96fc2dfc108ae848914b504481f100a09c4dc148cb693293a67af24dd40d2b13a9e36794"
diff --git a/src/pkg/crypto/openpgp/s2k/s2k_test.go b/src/pkg/crypto/openpgp/s2k/s2k_test.go
index 814b78627..75bc47ec1 100644
--- a/src/pkg/crypto/openpgp/s2k/s2k_test.go
+++ b/src/pkg/crypto/openpgp/s2k/s2k_test.go
@@ -90,5 +90,8 @@ func TestParse(t *testing.T) {
if !bytes.Equal(out, expected) {
t.Errorf("%d: output got: %x want: %x", i, out, expected)
}
+ if testing.Short() {
+ break
+ }
}
}
diff --git a/src/pkg/crypto/openpgp/write.go b/src/pkg/crypto/openpgp/write.go
index 1a2e2bf04..ef7b11230 100644
--- a/src/pkg/crypto/openpgp/write.go
+++ b/src/pkg/crypto/openpgp/write.go
@@ -6,6 +6,7 @@ package openpgp
import (
"crypto"
+ "crypto/dsa"
"crypto/openpgp/armor"
"crypto/openpgp/error"
"crypto/openpgp/packet"
@@ -39,7 +40,7 @@ func DetachSignText(w io.Writer, signer *Entity, message io.Reader) os.Error {
// ArmoredDetachSignText signs message (after canonicalising the line endings)
// with the private key from signer (which must already have been decrypted)
// and writes an armored signature to w.
-func SignTextDetachedArmored(w io.Writer, signer *Entity, message io.Reader) os.Error {
+func ArmoredDetachSignText(w io.Writer, signer *Entity, message io.Reader) os.Error {
return armoredDetachSign(w, signer, message, packet.SigTypeText)
}
@@ -80,6 +81,9 @@ func detachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.S
case packet.PubKeyAlgoRSA, packet.PubKeyAlgoRSASignOnly:
priv := signer.PrivateKey.PrivateKey.(*rsa.PrivateKey)
err = sig.SignRSA(h, priv)
+ case packet.PubKeyAlgoDSA:
+ priv := signer.PrivateKey.PrivateKey.(*dsa.PrivateKey)
+ err = sig.SignDSA(h, priv)
default:
err = error.UnsupportedError("public key algorithm: " + strconv.Itoa(int(sig.PubKeyAlgo)))
}
diff --git a/src/pkg/crypto/openpgp/write_test.go b/src/pkg/crypto/openpgp/write_test.go
index 33e8809f2..42cd0d27f 100644
--- a/src/pkg/crypto/openpgp/write_test.go
+++ b/src/pkg/crypto/openpgp/write_test.go
@@ -18,7 +18,7 @@ func TestSignDetached(t *testing.T) {
t.Error(err)
}
- testDetachedSignature(t, kring, out, signedInput, "check")
+ testDetachedSignature(t, kring, out, signedInput, "check", testKey1KeyId)
}
func TestSignTextDetached(t *testing.T) {
@@ -30,5 +30,17 @@ func TestSignTextDetached(t *testing.T) {
t.Error(err)
}
- testDetachedSignature(t, kring, out, signedInput, "check")
+ testDetachedSignature(t, kring, out, signedInput, "check", testKey1KeyId)
+}
+
+func TestSignDetachedDSA(t *testing.T) {
+ kring, _ := ReadKeyRing(readerFromHex(dsaTestKeyPrivateHex))
+ out := bytes.NewBuffer(nil)
+ message := bytes.NewBufferString(signedInput)
+ err := DetachSign(out, kring[0], message)
+ if err != nil {
+ t.Error(err)
+ }
+
+ testDetachedSignature(t, kring, out, signedInput, "check", testKey3KeyId)
}
diff --git a/src/pkg/crypto/rand/rand_test.go b/src/pkg/crypto/rand/rand_test.go
index f64ead4ca..bfae7ce4f 100644
--- a/src/pkg/crypto/rand/rand_test.go
+++ b/src/pkg/crypto/rand/rand_test.go
@@ -11,7 +11,11 @@ import (
)
func TestRead(t *testing.T) {
- b := make([]byte, 4e6)
+ var n int = 4e6
+ if testing.Short() {
+ n = 1e5
+ }
+ b := make([]byte, n)
n, err := Read(b)
if n != len(b) || err != nil {
t.Fatalf("Read(buf) = %d, %s", n, err)
diff --git a/src/pkg/crypto/rand/rand_unix.go b/src/pkg/crypto/rand/rand_unix.go
index 66b72c076..3a06aa8b1 100644
--- a/src/pkg/crypto/rand/rand_unix.go
+++ b/src/pkg/crypto/rand/rand_unix.go
@@ -32,7 +32,7 @@ func (r *devReader) Read(b []byte) (n int, err os.Error) {
r.mu.Lock()
defer r.mu.Unlock()
if r.f == nil {
- f, err := os.Open(r.name, os.O_RDONLY, 0)
+ f, err := os.Open(r.name)
if f == nil {
return 0, err
}
diff --git a/src/pkg/crypto/rsa/pkcs1v15.go b/src/pkg/crypto/rsa/pkcs1v15.go
index 2eaadee24..3defa62ea 100644
--- a/src/pkg/crypto/rsa/pkcs1v15.go
+++ b/src/pkg/crypto/rsa/pkcs1v15.go
@@ -127,7 +127,7 @@ func nonZeroRandomBytes(s []byte, rand io.Reader) (err os.Error) {
for i := 0; i < len(s); i++ {
for s[i] == 0 {
- _, err = rand.Read(s[i : i+1])
+ _, err = io.ReadFull(rand, s[i:i+1])
if err != nil {
return
}
@@ -149,10 +149,10 @@ func nonZeroRandomBytes(s []byte, rand io.Reader) (err os.Error) {
// precompute a prefix of the digest value that makes a valid ASN1 DER string
// with the correct contents.
var hashPrefixes = map[crypto.Hash][]byte{
- crypto.MD5: []byte{0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10},
- crypto.SHA1: []byte{0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14},
- crypto.SHA256: []byte{0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20},
- crypto.SHA384: []byte{0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30},
+ crypto.MD5: {0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10},
+ crypto.SHA1: {0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14},
+ crypto.SHA256: {0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20},
+ crypto.SHA384: {0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30},
crypto.SHA512: {0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40},
crypto.MD5SHA1: {}, // A special TLS case which doesn't use an ASN1 prefix.
crypto.RIPEMD160: {0x30, 0x20, 0x30, 0x08, 0x06, 0x06, 0x28, 0xcf, 0x06, 0x03, 0x00, 0x31, 0x04, 0x14},
diff --git a/src/pkg/crypto/rsa/pkcs1v15_test.go b/src/pkg/crypto/rsa/pkcs1v15_test.go
index 7b2ce08cb..30a4824a6 100644
--- a/src/pkg/crypto/rsa/pkcs1v15_test.go
+++ b/src/pkg/crypto/rsa/pkcs1v15_test.go
@@ -97,7 +97,11 @@ func TestEncryptPKCS1v15(t *testing.T) {
return true
}
- quick.Check(tryEncryptDecrypt, nil)
+ config := new(quick.Config)
+ if testing.Short() {
+ config.MaxCount = 10
+ }
+ quick.Check(tryEncryptDecrypt, config)
}
// These test vectors were generated with `openssl rsautl -pkcs -encrypt`
diff --git a/src/pkg/crypto/rsa/rsa.go b/src/pkg/crypto/rsa/rsa.go
index faf914991..b3b212c20 100644
--- a/src/pkg/crypto/rsa/rsa.go
+++ b/src/pkg/crypto/rsa/rsa.go
@@ -13,6 +13,7 @@ import (
"hash"
"io"
"os"
+ "sync"
)
var bigZero = big.NewInt(0)
@@ -91,15 +92,21 @@ type PublicKey struct {
type PrivateKey struct {
PublicKey // public part.
D *big.Int // private exponent
- P, Q *big.Int // prime factors of N
+ P, Q, R *big.Int // prime factors of N (R may be nil)
+
+ rwMutex sync.RWMutex // protects the following
+ dP, dQ, dR *big.Int // D mod (P-1) (or mod Q-1 etc)
+ qInv *big.Int // q^-1 mod p
+ pq *big.Int // P*Q
+ tr *big.Int // pq·tr ≡ 1 mod r
}
// Validate performs basic sanity checks on the key.
// It returns nil if the key is valid, or else an os.Error describing a problem.
-func (priv PrivateKey) Validate() os.Error {
- // Check that p and q are prime. Note that this is just a sanity
- // check. Since the random witnesses chosen by ProbablyPrime are
+func (priv *PrivateKey) Validate() os.Error {
+ // Check that p, q and, maybe, r are prime. Note that this is just a
+ // sanity check. Since the random witnesses chosen by ProbablyPrime are
// deterministic, given the candidate number, it's easy for an attack
// to generate composites that pass this test.
if !big.ProbablyPrime(priv.P, 20) {
@@ -108,16 +115,26 @@ func (priv PrivateKey) Validate() os.Error {
if !big.ProbablyPrime(priv.Q, 20) {
return os.ErrorString("Q is composite")
}
+ if priv.R != nil && !big.ProbablyPrime(priv.R, 20) {
+ return os.ErrorString("R is composite")
+ }
- // Check that p*q == n.
+ // Check that p*q*r == n.
modulus := new(big.Int).Mul(priv.P, priv.Q)
+ if priv.R != nil {
+ modulus.Mul(modulus, priv.R)
+ }
if modulus.Cmp(priv.N) != 0 {
return os.ErrorString("invalid modulus")
}
- // Check that e and totient(p, q) are coprime.
+ // Check that e and totient(p, q, r) are coprime.
pminus1 := new(big.Int).Sub(priv.P, bigOne)
qminus1 := new(big.Int).Sub(priv.Q, bigOne)
totient := new(big.Int).Mul(pminus1, qminus1)
+ if priv.R != nil {
+ rminus1 := new(big.Int).Sub(priv.R, bigOne)
+ totient.Mul(totient, rminus1)
+ }
e := big.NewInt(int64(priv.E))
gcd := new(big.Int)
x := new(big.Int)
@@ -126,7 +143,7 @@ func (priv PrivateKey) Validate() os.Error {
if gcd.Cmp(bigOne) != 0 {
return os.ErrorString("invalid public exponent E")
}
- // Check that de ≡ 1 (mod totient(p, q))
+ // Check that de ≡ 1 (mod totient(p, q, r))
de := new(big.Int).Mul(priv.D, e)
de.Mod(de, totient)
if de.Cmp(bigOne) != 0 {
@@ -135,7 +152,7 @@ func (priv PrivateKey) Validate() os.Error {
return nil
}
-// GenerateKeyPair generates an RSA keypair of the given bit size.
+// GenerateKey generates an RSA keypair of the given bit size.
func GenerateKey(rand io.Reader, bits int) (priv *PrivateKey, err os.Error) {
priv = new(PrivateKey)
// Smaller public exponents lead to faster public key
@@ -191,6 +208,77 @@ func GenerateKey(rand io.Reader, bits int) (priv *PrivateKey, err os.Error) {
return
}
+// Generate3PrimeKey generates a 3-prime RSA keypair of the given bit size, as
+// suggested in [1]. Although the public keys are compatible (actually,
+// indistinguishable) from the 2-prime case, the private keys are not. Thus it
+// may not be possible to export 3-prime private keys in certain formats or to
+// subsequently import them into other code.
+//
+// Table 1 in [2] suggests that size should be >= 1024 when using 3 primes.
+//
+// [1] US patent 4405829 (1972, expired)
+// [2] http://www.cacr.math.uwaterloo.ca/techreports/2006/cacr2006-16.pdf
+func Generate3PrimeKey(rand io.Reader, bits int) (priv *PrivateKey, err os.Error) {
+ priv = new(PrivateKey)
+ priv.E = 3
+
+ pminus1 := new(big.Int)
+ qminus1 := new(big.Int)
+ rminus1 := new(big.Int)
+ totient := new(big.Int)
+
+ for {
+ p, err := randomPrime(rand, bits/3)
+ if err != nil {
+ return nil, err
+ }
+
+ todo := bits - p.BitLen()
+ q, err := randomPrime(rand, todo/2)
+ if err != nil {
+ return nil, err
+ }
+
+ todo -= q.BitLen()
+ r, err := randomPrime(rand, todo)
+ if err != nil {
+ return nil, err
+ }
+
+ if p.Cmp(q) == 0 ||
+ q.Cmp(r) == 0 ||
+ r.Cmp(p) == 0 {
+ continue
+ }
+
+ n := new(big.Int).Mul(p, q)
+ n.Mul(n, r)
+ pminus1.Sub(p, bigOne)
+ qminus1.Sub(q, bigOne)
+ rminus1.Sub(r, bigOne)
+ totient.Mul(pminus1, qminus1)
+ totient.Mul(totient, rminus1)
+
+ g := new(big.Int)
+ priv.D = new(big.Int)
+ y := new(big.Int)
+ e := big.NewInt(int64(priv.E))
+ big.GcdInt(g, priv.D, y, e, totient)
+
+ if g.Cmp(bigOne) == 0 {
+ priv.D.Add(priv.D, totient)
+ priv.P = p
+ priv.Q = q
+ priv.R = r
+ priv.N = n
+
+ break
+ }
+ }
+
+ return
+}
+
// incCounter increments a four byte, big-endian counter.
func incCounter(c *[4]byte) {
if c[3]++; c[3] != 0 {
@@ -321,6 +409,26 @@ func modInverse(a, n *big.Int) (ia *big.Int, ok bool) {
return x, true
}
+// precompute performs some calculations that speed up private key operations
+// in the future.
+func (priv *PrivateKey) precompute() {
+ priv.dP = new(big.Int).Sub(priv.P, bigOne)
+ priv.dP.Mod(priv.D, priv.dP)
+
+ priv.dQ = new(big.Int).Sub(priv.Q, bigOne)
+ priv.dQ.Mod(priv.D, priv.dQ)
+
+ priv.qInv = new(big.Int).ModInverse(priv.Q, priv.P)
+
+ if priv.R != nil {
+ priv.dR = new(big.Int).Sub(priv.R, bigOne)
+ priv.dR.Mod(priv.D, priv.dR)
+
+ priv.pq = new(big.Int).Mul(priv.P, priv.Q)
+ priv.tr = new(big.Int).ModInverse(priv.pq, priv.R)
+ }
+}
+
// decrypt performs an RSA decryption, resulting in a plaintext integer. If a
// random source is given, RSA blinding is used.
func decrypt(rand io.Reader, priv *PrivateKey, c *big.Int) (m *big.Int, err os.Error) {
@@ -359,7 +467,48 @@ func decrypt(rand io.Reader, priv *PrivateKey, c *big.Int) (m *big.Int, err os.E
c.Mod(c, priv.N)
}
- m = new(big.Int).Exp(c, priv.D, priv.N)
+ priv.rwMutex.RLock()
+
+ if priv.dP == nil && priv.P != nil {
+ priv.rwMutex.RUnlock()
+ priv.rwMutex.Lock()
+ if priv.dP == nil && priv.P != nil {
+ priv.precompute()
+ }
+ priv.rwMutex.Unlock()
+ priv.rwMutex.RLock()
+ }
+
+ if priv.dP == nil {
+ m = new(big.Int).Exp(c, priv.D, priv.N)
+ } else {
+ // We have the precalculated values needed for the CRT.
+ m = new(big.Int).Exp(c, priv.dP, priv.P)
+ m2 := new(big.Int).Exp(c, priv.dQ, priv.Q)
+ m.Sub(m, m2)
+ if m.Sign() < 0 {
+ m.Add(m, priv.P)
+ }
+ m.Mul(m, priv.qInv)
+ m.Mod(m, priv.P)
+ m.Mul(m, priv.Q)
+ m.Add(m, m2)
+
+ if priv.dR != nil {
+ // 3-prime CRT.
+ m2.Exp(c, priv.dR, priv.R)
+ m2.Sub(m2, m)
+ m2.Mul(m2, priv.tr)
+ m2.Mod(m2, priv.R)
+ if m2.Sign() < 0 {
+ m2.Add(m2, priv.R)
+ }
+ m2.Mul(m2, priv.pq)
+ m.Add(m, m2)
+ }
+ }
+
+ priv.rwMutex.RUnlock()
if ir != nil {
// Unblind.
diff --git a/src/pkg/crypto/rsa/rsa_test.go b/src/pkg/crypto/rsa/rsa_test.go
index 22d4576e8..d8a936eb6 100644
--- a/src/pkg/crypto/rsa/rsa_test.go
+++ b/src/pkg/crypto/rsa/rsa_test.go
@@ -13,24 +13,48 @@ import (
)
func TestKeyGeneration(t *testing.T) {
- random := rand.Reader
+ size := 1024
+ if testing.Short() {
+ size = 128
+ }
+ priv, err := GenerateKey(rand.Reader, size)
+ if err != nil {
+ t.Errorf("failed to generate key")
+ }
+ testKeyBasics(t, priv)
+}
+
+func Test3PrimeKeyGeneration(t *testing.T) {
+ if testing.Short() {
+ return
+ }
- priv, err := GenerateKey(random, 1024)
+ size := 768
+ priv, err := Generate3PrimeKey(rand.Reader, size)
if err != nil {
t.Errorf("failed to generate key")
}
+ testKeyBasics(t, priv)
+}
+
+func testKeyBasics(t *testing.T, priv *PrivateKey) {
+ if err := priv.Validate(); err != nil {
+ t.Errorf("Validate() failed: %s", err)
+ }
+
pub := &priv.PublicKey
m := big.NewInt(42)
c := encrypt(new(big.Int), pub, m)
m2, err := decrypt(nil, priv, c)
if err != nil {
t.Errorf("error while decrypting: %s", err)
+ return
}
if m.Cmp(m2) != 0 {
- t.Errorf("got:%v, want:%v (%s)", m2, m, priv)
+ t.Errorf("got:%v, want:%v (%+v)", m2, m, priv)
}
- m3, err := decrypt(random, priv, c)
+ m3, err := decrypt(rand.Reader, priv, c)
if err != nil {
t.Errorf("error while decrypting (blind): %s", err)
}
@@ -39,6 +63,57 @@ func TestKeyGeneration(t *testing.T) {
}
}
+func fromBase10(base10 string) *big.Int {
+ i := new(big.Int)
+ i.SetString(base10, 10)
+ return i
+}
+
+func BenchmarkRSA2048Decrypt(b *testing.B) {
+ b.StopTimer()
+ priv := &PrivateKey{
+ PublicKey: PublicKey{
+ N: fromBase10("14314132931241006650998084889274020608918049032671858325988396851334124245188214251956198731333464217832226406088020736932173064754214329009979944037640912127943488972644697423190955557435910767690712778463524983667852819010259499695177313115447116110358524558307947613422897787329221478860907963827160223559690523660574329011927531289655711860504630573766609239332569210831325633840174683944553667352219670930408593321661375473885147973879086994006440025257225431977751512374815915392249179976902953721486040787792801849818254465486633791826766873076617116727073077821584676715609985777563958286637185868165868520557"),
+ E: 3,
+ },
+ D: fromBase10("9542755287494004433998723259516013739278699355114572217325597900889416163458809501304132487555642811888150937392013824621448709836142886006653296025093941418628992648429798282127303704957273845127141852309016655778568546006839666463451542076964744073572349705538631742281931858219480985907271975884773482372966847639853897890615456605598071088189838676728836833012254065983259638538107719766738032720239892094196108713378822882383694456030043492571063441943847195939549773271694647657549658603365629458610273821292232646334717612674519997533901052790334279661754176490593041941863932308687197618671528035670452762731"),
+ P: fromBase10("130903255182996722426771613606077755295583329135067340152947172868415809027537376306193179624298874215608270802054347609836776473930072411958753044562214537013874103802006369634761074377213995983876788718033850153719421695468704276694983032644416930879093914927146648402139231293035971427838068945045019075433"),
+ Q: fromBase10("109348945610485453577574767652527472924289229538286649661240938988020367005475727988253438647560958573506159449538793540472829815903949343191091817779240101054552748665267574271163617694640513549693841337820602726596756351006149518830932261246698766355347898158548465400674856021497190430791824869615170301029"),
+ }
+ priv.precompute()
+
+ c := fromBase10("1000")
+
+ b.StartTimer()
+
+ for i := 0; i < b.N; i++ {
+ decrypt(nil, priv, c)
+ }
+}
+
+func Benchmark3PrimeRSA2048Decrypt(b *testing.B) {
+ b.StopTimer()
+ priv := &PrivateKey{
+ PublicKey: PublicKey{
+ N: fromBase10("16346378922382193400538269749936049106320265317511766357599732575277382844051791096569333808598921852351577762718529818072849191122419410612033592401403764925096136759934497687765453905884149505175426053037420486697072448609022753683683718057795566811401938833367954642951433473337066311978821180526439641496973296037000052546108507805269279414789035461158073156772151892452251106173507240488993608650881929629163465099476849643165682709047462010581308719577053905787496296934240246311806555924593059995202856826239801816771116902778517096212527979497399966526283516447337775509777558018145573127308919204297111496233"),
+ E: 3,
+ },
+ D: fromBase10("10897585948254795600358846499957366070880176878341177571733155050184921896034527397712889205732614568234385175145686545381899460748279607074689061600935843283397424506622998458510302603922766336783617368686090042765718290914099334449154829375179958369993407724946186243249568928237086215759259909861748642124071874879861299389874230489928271621259294894142840428407196932444474088857746123104978617098858619445675532587787023228852383149557470077802718705420275739737958953794088728369933811184572620857678792001136676902250566845618813972833750098806496641114644760255910789397593428910198080271317419213080834885003"),
+ P: fromBase10("1025363189502892836833747188838978207017355117492483312747347695538428729137306368764177201532277413433182799108299960196606011786562992097313508180436744488171474690412562218914213688661311117337381958560443"),
+ Q: fromBase10("3467903426626310123395340254094941045497208049900750380025518552334536945536837294961497712862519984786362199788654739924501424784631315081391467293694361474867825728031147665777546570788493758372218019373"),
+ R: fromBase10("4597024781409332673052708605078359346966325141767460991205742124888960305710298765592730135879076084498363772408626791576005136245060321874472727132746643162385746062759369754202494417496879741537284589047"),
+ }
+ priv.precompute()
+
+ c := fromBase10("1000")
+
+ b.StartTimer()
+
+ for i := 0; i < b.N; i++ {
+ decrypt(nil, priv, c)
+ }
+}
+
type testEncryptOAEPMessage struct {
in []byte
seed []byte
@@ -81,10 +156,12 @@ func TestDecryptOAEP(t *testing.T) {
for i, test := range testEncryptOAEPData {
n.SetString(test.modulus, 16)
d.SetString(test.d, 16)
- private := PrivateKey{PublicKey{n, test.e}, d, nil, nil}
+ private := new(PrivateKey)
+ private.PublicKey = PublicKey{n, test.e}
+ private.D = d
for j, message := range test.msgs {
- out, err := DecryptOAEP(sha1, nil, &private, message.out, nil)
+ out, err := DecryptOAEP(sha1, nil, private, message.out, nil)
if err != nil {
t.Errorf("#%d,%d error: %s", i, j, err)
} else if bytes.Compare(out, message.in) != 0 {
@@ -92,13 +169,16 @@ func TestDecryptOAEP(t *testing.T) {
}
// Decrypt with blinding.
- out, err = DecryptOAEP(sha1, random, &private, message.out, nil)
+ out, err = DecryptOAEP(sha1, random, private, message.out, nil)
if err != nil {
t.Errorf("#%d,%d (blind) error: %s", i, j, err)
} else if bytes.Compare(out, message.in) != 0 {
t.Errorf("#%d,%d (blind) bad result: %#v (want %#v)", i, j, out, message.in)
}
}
+ if testing.Short() {
+ break
+ }
}
}
diff --git a/src/pkg/crypto/tls/common.go b/src/pkg/crypto/tls/common.go
index 7135f3d0f..fb2916ae0 100644
--- a/src/pkg/crypto/tls/common.go
+++ b/src/pkg/crypto/tls/common.go
@@ -7,6 +7,7 @@ package tls
import (
"crypto/rand"
"crypto/rsa"
+ "crypto/x509"
"io"
"io/ioutil"
"sync"
@@ -92,9 +93,13 @@ const (
// ConnectionState records basic TLS details about the connection.
type ConnectionState struct {
- HandshakeComplete bool
- CipherSuite uint16
- NegotiatedProtocol string
+ HandshakeComplete bool
+ CipherSuite uint16
+ NegotiatedProtocol string
+ NegotiatedProtocolIsMutual bool
+
+ // the certificate chain that was presented by the other side
+ PeerCertificates []*x509.Certificate
}
// A Config structure is used to configure a TLS client or server. After one
@@ -120,7 +125,6 @@ type Config struct {
RootCAs *CASet
// NextProtos is a list of supported, application level protocols.
- // Currently only server-side handling is supported.
NextProtos []string
// ServerName is included in the client's handshake to support virtual
@@ -251,7 +255,7 @@ var varDefaultCipherSuites []uint16
func initDefaultCipherSuites() {
varDefaultCipherSuites = make([]uint16, len(cipherSuites))
i := 0
- for id, _ := range cipherSuites {
+ for id := range cipherSuites {
varDefaultCipherSuites[i] = id
i++
}
diff --git a/src/pkg/crypto/tls/conn.go b/src/pkg/crypto/tls/conn.go
index d203e8d51..b94e235c8 100644
--- a/src/pkg/crypto/tls/conn.go
+++ b/src/pkg/crypto/tls/conn.go
@@ -35,7 +35,8 @@ type Conn struct {
ocspResponse []byte // stapled OCSP response
peerCertificates []*x509.Certificate
- clientProtocol string
+ clientProtocol string
+ clientProtocolFallback bool
// first permanent error
errMutex sync.Mutex
@@ -761,7 +762,9 @@ func (c *Conn) ConnectionState() ConnectionState {
state.HandshakeComplete = c.handshakeComplete
if c.handshakeComplete {
state.NegotiatedProtocol = c.clientProtocol
+ state.NegotiatedProtocolIsMutual = !c.clientProtocolFallback
state.CipherSuite = c.cipherSuite
+ state.PeerCertificates = c.peerCertificates
}
return state
@@ -776,15 +779,6 @@ func (c *Conn) OCSPResponse() []byte {
return c.ocspResponse
}
-// PeerCertificates returns the certificate chain that was presented by the
-// other side.
-func (c *Conn) PeerCertificates() []*x509.Certificate {
- c.handshakeMutex.Lock()
- defer c.handshakeMutex.Unlock()
-
- return c.peerCertificates
-}
-
// VerifyHostname checks that the peer certificate chain is valid for
// connecting to host. If so, it returns nil; if not, it returns an os.Error
// describing the problem.
diff --git a/src/pkg/crypto/tls/generate_cert.go b/src/pkg/crypto/tls/generate_cert.go
index 3e0c63938..5b8c700e5 100644
--- a/src/pkg/crypto/tls/generate_cert.go
+++ b/src/pkg/crypto/tls/generate_cert.go
@@ -25,7 +25,7 @@ func main() {
priv, err := rsa.GenerateKey(rand.Reader, 1024)
if err != nil {
- log.Exitf("failed to generate private key: %s", err)
+ log.Fatalf("failed to generate private key: %s", err)
return
}
@@ -46,20 +46,20 @@ func main() {
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
if err != nil {
- log.Exitf("Failed to create certificate: %s", err)
+ log.Fatalf("Failed to create certificate: %s", err)
return
}
- certOut, err := os.Open("cert.pem", os.O_WRONLY|os.O_CREAT, 0644)
+ certOut, err := os.Create("cert.pem")
if err != nil {
- log.Exitf("failed to open cert.pem for writing: %s", err)
+ log.Fatalf("failed to open cert.pem for writing: %s", err)
return
}
pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
certOut.Close()
log.Print("written cert.pem\n")
- keyOut, err := os.Open("key.pem", os.O_WRONLY|os.O_CREAT, 0600)
+ keyOut, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREAT|os.O_TRUNC, 0600)
if err != nil {
log.Print("failed to open key.pem for writing:", err)
return
diff --git a/src/pkg/crypto/tls/handshake_client.go b/src/pkg/crypto/tls/handshake_client.go
index a325a9b95..540b25c87 100644
--- a/src/pkg/crypto/tls/handshake_client.go
+++ b/src/pkg/crypto/tls/handshake_client.go
@@ -29,6 +29,7 @@ func (c *Conn) clientHandshake() os.Error {
serverName: c.config.ServerName,
supportedCurves: []uint16{curveP256, curveP384, curveP521},
supportedPoints: []uint8{pointFormatUncompressed},
+ nextProtoNeg: len(c.config.NextProtos) > 0,
}
t := uint32(c.config.time())
@@ -66,6 +67,11 @@ func (c *Conn) clientHandshake() os.Error {
return c.sendAlert(alertUnexpectedMessage)
}
+ if !hello.nextProtoNeg && serverHello.nextProtoNeg {
+ c.sendAlert(alertHandshakeFailure)
+ return os.ErrorString("server advertised unrequested NPN")
+ }
+
suite, suiteId := mutualCipherSuite(c.config.cipherSuites(), serverHello.cipherSuite)
if suite == nil {
return c.sendAlert(alertHandshakeFailure)
@@ -267,6 +273,17 @@ func (c *Conn) clientHandshake() os.Error {
c.out.prepareCipherSpec(clientCipher, clientHash)
c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
+ if serverHello.nextProtoNeg {
+ nextProto := new(nextProtoMsg)
+ proto, fallback := mutualProtocol(c.config.NextProtos, serverHello.nextProtos)
+ nextProto.proto = proto
+ c.clientProtocol = proto
+ c.clientProtocolFallback = fallback
+
+ finishedHash.Write(nextProto.marshal())
+ c.writeRecord(recordTypeHandshake, nextProto.marshal())
+ }
+
finished := new(finishedMsg)
finished.verifyData = finishedHash.clientSum(masterSecret)
finishedHash.Write(finished.marshal())
@@ -299,3 +316,19 @@ func (c *Conn) clientHandshake() os.Error {
c.cipherSuite = suiteId
return nil
}
+
+// mutualProtocol finds the mutual Next Protocol Negotiation protocol given the
+// set of client and server supported protocols. The set of client supported
+// protocols must not be empty. It returns the resulting protocol and flag
+// indicating if the fallback case was reached.
+func mutualProtocol(clientProtos, serverProtos []string) (string, bool) {
+ for _, s := range serverProtos {
+ for _, c := range clientProtos {
+ if s == c {
+ return s, false
+ }
+ }
+ }
+
+ return clientProtos[0], true
+}
diff --git a/src/pkg/crypto/tls/handshake_client_test.go b/src/pkg/crypto/tls/handshake_client_test.go
index fd1f145cf..3f91c7acf 100644
--- a/src/pkg/crypto/tls/handshake_client_test.go
+++ b/src/pkg/crypto/tls/handshake_client_test.go
@@ -50,7 +50,7 @@ func TestRunClient(t *testing.T) {
testConfig.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_RC4_128_SHA}
- conn, err := Dial("tcp", "", "127.0.0.1:10443", testConfig)
+ conn, err := Dial("tcp", "127.0.0.1:10443", testConfig)
if err != nil {
t.Fatal(err)
}
diff --git a/src/pkg/crypto/tls/handshake_messages_test.go b/src/pkg/crypto/tls/handshake_messages_test.go
index 21577dd0b..f5e94e269 100644
--- a/src/pkg/crypto/tls/handshake_messages_test.go
+++ b/src/pkg/crypto/tls/handshake_messages_test.go
@@ -34,7 +34,11 @@ func TestMarshalUnmarshal(t *testing.T) {
for i, iface := range tests {
ty := reflect.NewValue(iface).Type()
- for j := 0; j < 100; j++ {
+ n := 100
+ if testing.Short() {
+ n = 5
+ }
+ for j := 0; j < n; j++ {
v, ok := quick.Value(ty, rand)
if !ok {
t.Errorf("#%d: failed to create value", i)
@@ -117,7 +121,7 @@ func (*clientHelloMsg) Generate(rand *rand.Rand, size int) reflect.Value {
m.ocspStapling = rand.Intn(10) > 5
m.supportedPoints = randomBytes(rand.Intn(5)+1, rand)
m.supportedCurves = make([]uint16, rand.Intn(5)+1)
- for i, _ := range m.supportedCurves {
+ for i := range m.supportedCurves {
m.supportedCurves[i] = uint16(rand.Intn(30000))
}
diff --git a/src/pkg/crypto/tls/tls.go b/src/pkg/crypto/tls/tls.go
index e8290d728..7de44bbd2 100644
--- a/src/pkg/crypto/tls/tls.go
+++ b/src/pkg/crypto/tls/tls.go
@@ -87,8 +87,9 @@ func Listen(network, laddr string, config *Config) (*Listener, os.Error) {
// Dial interprets a nil configuration as equivalent to
// the zero configuration; see the documentation of Config
// for the defaults.
-func Dial(network, laddr, raddr string, config *Config) (*Conn, os.Error) {
- c, err := net.Dial(network, laddr, raddr)
+func Dial(network, addr string, config *Config) (*Conn, os.Error) {
+ raddr := addr
+ c, err := net.Dial(network, raddr)
if err != nil {
return nil, err
}
@@ -123,7 +124,16 @@ func LoadX509KeyPair(certFile string, keyFile string) (cert Certificate, err os.
if err != nil {
return
}
+ keyPEMBlock, err := ioutil.ReadFile(keyFile)
+ if err != nil {
+ return
+ }
+ return X509KeyPair(certPEMBlock, keyPEMBlock)
+}
+// X509KeyPair parses a public/private key pair from a pair of
+// PEM encoded data.
+func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (cert Certificate, err os.Error) {
var certDERBlock *pem.Block
for {
certDERBlock, certPEMBlock = pem.Decode(certPEMBlock)
@@ -140,11 +150,6 @@ func LoadX509KeyPair(certFile string, keyFile string) (cert Certificate, err os.
return
}
- keyPEMBlock, err := ioutil.ReadFile(keyFile)
- if err != nil {
- return
- }
-
keyDERBlock, _ := pem.Decode(keyPEMBlock)
if keyDERBlock == nil {
err = os.ErrorString("crypto/tls: failed to parse key PEM data")
diff --git a/src/pkg/crypto/x509/x509.go b/src/pkg/crypto/x509/x509.go
index 3af8ba8ca..2a57f8758 100644
--- a/src/pkg/crypto/x509/x509.go
+++ b/src/pkg/crypto/x509/x509.go
@@ -54,20 +54,21 @@ func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err os.Error) {
return
}
- key = &rsa.PrivateKey{
- PublicKey: rsa.PublicKey{
- E: priv.E,
- N: new(big.Int).SetBytes(priv.N.Bytes),
- },
- D: new(big.Int).SetBytes(priv.D.Bytes),
- P: new(big.Int).SetBytes(priv.P.Bytes),
- Q: new(big.Int).SetBytes(priv.Q.Bytes),
+ key = new(rsa.PrivateKey)
+ key.PublicKey = rsa.PublicKey{
+ E: priv.E,
+ N: new(big.Int).SetBytes(priv.N.Bytes),
}
+ key.D = new(big.Int).SetBytes(priv.D.Bytes)
+ key.P = new(big.Int).SetBytes(priv.P.Bytes)
+ key.Q = new(big.Int).SetBytes(priv.Q.Bytes)
+
err = key.Validate()
if err != nil {
return nil, err
}
+
return
}
@@ -89,6 +90,7 @@ func MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte {
// These structures reflect the ASN.1 structure of X.509 certificates.:
type certificate struct {
+ Raw asn1.RawContent
TBSCertificate tbsCertificate
SignatureAlgorithm algorithmIdentifier
SignatureValue asn1.BitString
@@ -304,9 +306,46 @@ const (
KeyUsageDecipherOnly
)
+// RFC 5280, 4.2.1.12 Extended Key Usage
+//
+// anyExtendedKeyUsage OBJECT IDENTIFIER ::= { id-ce-extKeyUsage 0 }
+//
+// id-kp OBJECT IDENTIFIER ::= { id-pkix 3 }
+//
+// id-kp-serverAuth OBJECT IDENTIFIER ::= { id-kp 1 }
+// id-kp-clientAuth OBJECT IDENTIFIER ::= { id-kp 2 }
+// id-kp-codeSigning OBJECT IDENTIFIER ::= { id-kp 3 }
+// id-kp-emailProtection OBJECT IDENTIFIER ::= { id-kp 4 }
+// id-kp-timeStamping OBJECT IDENTIFIER ::= { id-kp 8 }
+// id-kp-OCSPSigning OBJECT IDENTIFIER ::= { id-kp 9 }
+var (
+ oidExtKeyUsageAny = asn1.ObjectIdentifier{2, 5, 29, 37, 0}
+ oidExtKeyUsageServerAuth = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 1}
+ oidExtKeyUsageClientAuth = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 2}
+ oidExtKeyUsageCodeSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 3}
+ oidExtKeyUsageEmailProtection = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 4}
+ oidExtKeyUsageTimeStamping = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 8}
+ oidExtKeyUsageOCSPSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 9}
+)
+
+// ExtKeyUsage represents an extended set of actions that are valid for a given key.
+// Each of the ExtKeyUsage* constants define a unique action.
+type ExtKeyUsage int
+
+const (
+ ExtKeyUsageAny ExtKeyUsage = iota
+ ExtKeyUsageServerAuth
+ ExtKeyUsageClientAuth
+ ExtKeyUsageCodeSigning
+ ExtKeyUsageEmailProtection
+ ExtKeyUsageTimeStamping
+ ExtKeyUsageOCSPSigning
+)
+
// A Certificate represents an X.509 certificate.
type Certificate struct {
- Raw []byte // Raw ASN.1 DER contents.
+ Raw []byte // Complete ASN.1 DER content (certificate, signature algorithm and signature).
+ RawTBSCertificate []byte // Certificate part of raw ASN.1 DER content.
Signature []byte
SignatureAlgorithm SignatureAlgorithm
@@ -320,6 +359,9 @@ type Certificate struct {
NotBefore, NotAfter *time.Time // Validity bounds.
KeyUsage KeyUsage
+ ExtKeyUsage []ExtKeyUsage // Sequence of extended key usages.
+ UnknownExtKeyUsage []asn1.ObjectIdentifier // Encountered extended key usages unknown to this package.
+
BasicConstraintsValid bool // if true then the next two fields are valid.
IsCA bool
MaxPathLen int
@@ -394,7 +436,7 @@ func (c *Certificate) CheckSignatureFrom(parent *Certificate) (err os.Error) {
return UnsupportedAlgorithmError{}
}
- h.Write(c.Raw)
+ h.Write(c.RawTBSCertificate)
digest := h.Sum()
return rsa.VerifyPKCS1v15(pub, hashType, digest, c.Signature)
@@ -518,7 +560,8 @@ func parsePublicKey(algo PublicKeyAlgorithm, asn1Data []byte) (interface{}, os.E
func parseCertificate(in *certificate) (*Certificate, os.Error) {
out := new(Certificate)
- out.Raw = in.TBSCertificate.Raw
+ out.Raw = in.Raw
+ out.RawTBSCertificate = in.TBSCertificate.Raw
out.Signature = in.SignatureValue.RightAlign()
out.SignatureAlgorithm =
@@ -666,6 +709,44 @@ func parseCertificate(in *certificate) (*Certificate, os.Error) {
out.AuthorityKeyId = a.Id
continue
+ case 37:
+ // RFC 5280, 4.2.1.12. Extended Key Usage
+
+ // id-ce-extKeyUsage OBJECT IDENTIFIER ::= { id-ce 37 }
+ //
+ // ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId
+ //
+ // KeyPurposeId ::= OBJECT IDENTIFIER
+
+ var keyUsage []asn1.ObjectIdentifier
+ _, err = asn1.Unmarshal(e.Value, &keyUsage)
+ if err != nil {
+ return nil, err
+ }
+
+ for _, u := range keyUsage {
+ switch {
+ case u.Equal(oidExtKeyUsageAny):
+ out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageAny)
+ case u.Equal(oidExtKeyUsageServerAuth):
+ out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageServerAuth)
+ case u.Equal(oidExtKeyUsageClientAuth):
+ out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageClientAuth)
+ case u.Equal(oidExtKeyUsageCodeSigning):
+ out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageCodeSigning)
+ case u.Equal(oidExtKeyUsageEmailProtection):
+ out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageEmailProtection)
+ case u.Equal(oidExtKeyUsageTimeStamping):
+ out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageTimeStamping)
+ case u.Equal(oidExtKeyUsageOCSPSigning):
+ out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageOCSPSigning)
+ default:
+ out.UnknownExtKeyUsage = append(out.UnknownExtKeyUsage, u)
+ }
+ }
+
+ continue
+
case 14:
// RFC 5280, 4.2.1.2
var keyid []byte
@@ -918,6 +999,7 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub *rsa.P
}
cert, err = asn1.Marshal(certificate{
+ nil,
c,
algorithmIdentifier{oidSHA1WithRSA},
asn1.BitString{Bytes: signature, BitLength: len(signature) * 8},
diff --git a/src/pkg/crypto/x509/x509_test.go b/src/pkg/crypto/x509/x509_test.go
index 57889e7e1..d9511b863 100644
--- a/src/pkg/crypto/x509/x509_test.go
+++ b/src/pkg/crypto/x509/x509_test.go
@@ -11,7 +11,6 @@ import (
"crypto/rsa"
"encoding/hex"
"encoding/pem"
- "reflect"
"testing"
"time"
)
@@ -22,7 +21,11 @@ func TestParsePKCS1PrivateKey(t *testing.T) {
if err != nil {
t.Errorf("Failed to parse private key: %s", err)
}
- if !reflect.DeepEqual(priv, rsaPrivateKey) {
+ if priv.PublicKey.N.Cmp(rsaPrivateKey.PublicKey.N) != 0 ||
+ priv.PublicKey.E != rsaPrivateKey.PublicKey.E ||
+ priv.D.Cmp(rsaPrivateKey.D) != 0 ||
+ priv.P.Cmp(rsaPrivateKey.P) != 0 ||
+ priv.Q.Cmp(rsaPrivateKey.Q) != 0 {
t.Errorf("got:%+v want:%+v", priv, rsaPrivateKey)
}
}
diff --git a/src/pkg/debug/elf/file.go b/src/pkg/debug/elf/file.go
index e69317a75..6fdcda6d4 100644
--- a/src/pkg/debug/elf/file.go
+++ b/src/pkg/debug/elf/file.go
@@ -144,7 +144,7 @@ func (e *FormatError) String() string {
// Open opens the named file using os.Open and prepares it for use as an ELF binary.
func Open(name string) (*File, os.Error) {
- f, err := os.Open(name, os.O_RDONLY, 0)
+ f, err := os.Open(name)
if err != nil {
return nil, err
}
@@ -228,7 +228,7 @@ func NewFile(r io.ReaderAt) (*File, os.Error) {
switch f.Class {
case ELFCLASS32:
hdr := new(Header32)
- sr.Seek(0, 0)
+ sr.Seek(0, os.SEEK_SET)
if err := binary.Read(sr, f.ByteOrder, hdr); err != nil {
return nil, err
}
@@ -243,7 +243,7 @@ func NewFile(r io.ReaderAt) (*File, os.Error) {
shstrndx = int(hdr.Shstrndx)
case ELFCLASS64:
hdr := new(Header64)
- sr.Seek(0, 0)
+ sr.Seek(0, os.SEEK_SET)
if err := binary.Read(sr, f.ByteOrder, hdr); err != nil {
return nil, err
}
@@ -269,7 +269,7 @@ func NewFile(r io.ReaderAt) (*File, os.Error) {
names := make([]uint32, shnum)
for i := 0; i < shnum; i++ {
off := shoff + int64(i)*int64(shentsize)
- sr.Seek(off, 0)
+ sr.Seek(off, os.SEEK_SET)
s := new(Section)
switch f.Class {
case ELFCLASS32:
diff --git a/src/pkg/debug/gosym/Makefile b/src/pkg/debug/gosym/Makefile
index 3c0d8c440..4f420e729 100644
--- a/src/pkg/debug/gosym/Makefile
+++ b/src/pkg/debug/gosym/Makefile
@@ -11,3 +11,9 @@ GOFILES=\
include ../../../Make.pkg
+test: make-pclinetest
+
+testshort: make-pclinetest
+
+make-pclinetest:
+ @if [ "`uname`-`uname -m`" = Linux-x86_64 -a $(GOARCH) = amd64 ]; then mkdir -p _test && $(AS) pclinetest.s && $(LD) -E main -o _test/pclinetest pclinetest.$O; fi
diff --git a/src/pkg/debug/gosym/pclntab_test.go b/src/pkg/debug/gosym/pclntab_test.go
index 908702173..c83e64eab 100644
--- a/src/pkg/debug/gosym/pclntab_test.go
+++ b/src/pkg/debug/gosym/pclntab_test.go
@@ -143,9 +143,6 @@ func TestLineAline(t *testing.T) {
}
}
-// gotest: if [ "$(uname)-$(uname -m)" = Linux-x86_64 -a "$GOARCH" = amd64 ]; then
-// gotest: mkdir -p _test && $AS pclinetest.s && $LD -E main -o _test/pclinetest pclinetest.$O
-// gotest: fi
func TestPCLine(t *testing.T) {
if !dotest() {
return
diff --git a/src/pkg/debug/macho/file.go b/src/pkg/debug/macho/file.go
index fd8da9449..a777d873c 100644
--- a/src/pkg/debug/macho/file.go
+++ b/src/pkg/debug/macho/file.go
@@ -159,7 +159,7 @@ func (e *FormatError) String() string {
// Open opens the named file using os.Open and prepares it for use as a Mach-O binary.
func Open(name string) (*File, os.Error) {
- f, err := os.Open(name, os.O_RDONLY, 0)
+ f, err := os.Open(name)
if err != nil {
return nil, err
}
diff --git a/src/pkg/debug/pe/file.go b/src/pkg/debug/pe/file.go
index 1bcbdc5e9..6a14e50f9 100644
--- a/src/pkg/debug/pe/file.go
+++ b/src/pkg/debug/pe/file.go
@@ -87,7 +87,7 @@ func (e *FormatError) String() string {
// Open opens the named file using os.Open and prepares it for use as a PE binary.
func Open(name string) (*File, os.Error) {
- f, err := os.Open(name, os.O_RDONLY, 0)
+ f, err := os.Open(name)
if err != nil {
return nil, err
}
@@ -132,7 +132,7 @@ func NewFile(r io.ReaderAt) (*File, os.Error) {
} else {
base = int64(0)
}
- sr.Seek(base, 0)
+ sr.Seek(base, os.SEEK_SET)
if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil {
return nil, err
}
@@ -140,7 +140,7 @@ func NewFile(r io.ReaderAt) (*File, os.Error) {
return nil, os.NewError("Invalid PE File Format.")
}
// get symbol string table
- sr.Seek(int64(f.FileHeader.PointerToSymbolTable+18*f.FileHeader.NumberOfSymbols), 0)
+ sr.Seek(int64(f.FileHeader.PointerToSymbolTable+18*f.FileHeader.NumberOfSymbols), os.SEEK_SET)
var l uint32
if err := binary.Read(sr, binary.LittleEndian, &l); err != nil {
return nil, err
@@ -149,9 +149,9 @@ func NewFile(r io.ReaderAt) (*File, os.Error) {
if _, err := r.ReadAt(ss, int64(f.FileHeader.PointerToSymbolTable+18*f.FileHeader.NumberOfSymbols)); err != nil {
return nil, err
}
- sr.Seek(base, 0)
+ sr.Seek(base, os.SEEK_SET)
binary.Read(sr, binary.LittleEndian, &f.FileHeader)
- sr.Seek(int64(f.FileHeader.SizeOfOptionalHeader), 1) //Skip OptionalHeader
+ sr.Seek(int64(f.FileHeader.SizeOfOptionalHeader), os.SEEK_CUR) //Skip OptionalHeader
f.Sections = make([]*Section, f.FileHeader.NumberOfSections)
for i := 0; i < int(f.FileHeader.NumberOfSections); i++ {
sh := new(SectionHeader32)
diff --git a/src/pkg/debug/proc/proc_darwin.go b/src/pkg/debug/proc/proc_darwin.go
index 7caf3a21a..49f0a5361 100644
--- a/src/pkg/debug/proc/proc_darwin.go
+++ b/src/pkg/debug/proc/proc_darwin.go
@@ -12,6 +12,6 @@ func Attach(pid int) (Process, os.Error) {
return nil, os.NewError("debug/proc not implemented on OS X")
}
-func ForkExec(argv0 string, argv []string, envv []string, dir string, fd []*os.File) (Process, os.Error) {
+func StartProcess(argv0 string, argv []string, attr *os.ProcAttr) (Process, os.Error) {
return Attach(0)
}
diff --git a/src/pkg/debug/proc/proc_freebsd.go b/src/pkg/debug/proc/proc_freebsd.go
index f6474ce80..4df07c365 100644
--- a/src/pkg/debug/proc/proc_freebsd.go
+++ b/src/pkg/debug/proc/proc_freebsd.go
@@ -12,6 +12,6 @@ func Attach(pid int) (Process, os.Error) {
return nil, os.NewError("debug/proc not implemented on FreeBSD")
}
-func ForkExec(argv0 string, argv []string, envv []string, dir string, fd []*os.File) (Process, os.Error) {
+func StartProcess(argv0 string, argv []string, attr *os.ProcAttr) (Process, os.Error) {
return Attach(0)
}
diff --git a/src/pkg/debug/proc/proc_linux.go b/src/pkg/debug/proc/proc_linux.go
index f0cc43a10..17c8fa529 100644
--- a/src/pkg/debug/proc/proc_linux.go
+++ b/src/pkg/debug/proc/proc_linux.go
@@ -1184,7 +1184,7 @@ func (p *process) attachThread(tid int) (*thread, os.Error) {
// attachAllThreads attaches to all threads in a process.
func (p *process) attachAllThreads() os.Error {
taskPath := "/proc/" + strconv.Itoa(p.pid) + "/task"
- taskDir, err := os.Open(taskPath, os.O_RDONLY, 0)
+ taskDir, err := os.Open(taskPath)
if err != nil {
return err
}
@@ -1279,25 +1279,31 @@ func Attach(pid int) (Process, os.Error) {
return p, nil
}
-// ForkExec forks the current process and execs argv0, stopping the
-// new process after the exec syscall. See os.ForkExec for additional
+// StartProcess forks the current process and execs argv0, stopping the
+// new process after the exec syscall. See os.StartProcess for additional
// details.
-func ForkExec(argv0 string, argv []string, envv []string, dir string, fd []*os.File) (Process, os.Error) {
+func StartProcess(argv0 string, argv []string, attr *os.ProcAttr) (Process, os.Error) {
+ sysattr := &syscall.ProcAttr{
+ Dir: attr.Dir,
+ Env: attr.Env,
+ Ptrace: true,
+ }
p := newProcess(-1)
// Create array of integer (system) fds.
- intfd := make([]int, len(fd))
- for i, f := range fd {
+ intfd := make([]int, len(attr.Files))
+ for i, f := range attr.Files {
if f == nil {
intfd[i] = -1
} else {
intfd[i] = f.Fd()
}
}
+ sysattr.Files = intfd
// Fork from the monitor thread so we get the right tracer pid.
err := p.do(func() os.Error {
- pid, errno := syscall.PtraceForkExec(argv0, argv, envv, dir, intfd)
+ pid, _, errno := syscall.StartProcess(argv0, argv, sysattr)
if errno != 0 {
return &os.PathError{"fork/exec", argv0, os.Errno(errno)}
}
diff --git a/src/pkg/debug/proc/proc_windows.go b/src/pkg/debug/proc/proc_windows.go
index dc22faef8..661474b67 100644
--- a/src/pkg/debug/proc/proc_windows.go
+++ b/src/pkg/debug/proc/proc_windows.go
@@ -12,6 +12,6 @@ func Attach(pid int) (Process, os.Error) {
return nil, os.NewError("debug/proc not implemented on windows")
}
-func ForkExec(argv0 string, argv []string, envv []string, dir string, fd []*os.File) (Process, os.Error) {
+func StartProcess(argv0 string, argv []string, attr *os.ProcAttr) (Process, os.Error) {
return Attach(0)
}
diff --git a/src/pkg/ebnf/ebnf_test.go b/src/pkg/ebnf/ebnf_test.go
index 69ad5fed1..e77cf64ad 100644
--- a/src/pkg/ebnf/ebnf_test.go
+++ b/src/pkg/ebnf/ebnf_test.go
@@ -15,31 +15,26 @@ var fset = token.NewFileSet()
var grammars = []string{
-`Program = .
-`,
-
-`Program = foo .
-foo = "foo" .
-`,
-
-`Program = "a" | "b" "c" .
-`,
-
-`Program = "a" ... "z" .
-`,
-
-`Program = Song .
- Song = { Note } .
- Note = Do | (Re | Mi | Fa | So | La) | Ti .
- Do = "c" .
- Re = "d" .
- Mi = "e" .
- Fa = "f" .
- So = "g" .
- La = "a" .
- Ti = ti .
- ti = "b" .
-`,
+ `Program = .`,
+
+ `Program = foo .
+ foo = "foo" .`,
+
+ `Program = "a" | "b" "c" .`,
+
+ `Program = "a" ... "z" .`,
+
+ `Program = Song .
+ Song = { Note } .
+ Note = Do | (Re | Mi | Fa | So | La) | Ti .
+ Do = "c" .
+ Re = "d" .
+ Mi = "e" .
+ Fa = "f" .
+ So = "g" .
+ La = "a" .
+ Ti = ti .
+ ti = "b" .`,
}
diff --git a/src/pkg/ebnf/parser.go b/src/pkg/ebnf/parser.go
index c38530177..818168e11 100644
--- a/src/pkg/ebnf/parser.go
+++ b/src/pkg/ebnf/parser.go
@@ -18,7 +18,7 @@ type parser struct {
scanner scanner.Scanner
pos token.Pos // token position
tok token.Token // one token look-ahead
- lit []byte // token literal
+ lit string // token literal
}
@@ -44,7 +44,7 @@ func (p *parser) 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(pos, msg)
@@ -63,7 +63,7 @@ func (p *parser) expect(tok token.Token) token.Pos {
func (p *parser) parseIdentifier() *Name {
pos := p.pos
- name := string(p.lit)
+ name := p.lit
p.expect(token.IDENT)
return &Name{pos, name}
}
@@ -73,7 +73,7 @@ func (p *parser) parseToken() *Token {
pos := p.pos
value := ""
if p.tok == token.STRING {
- value, _ = strconv.Unquote(string(p.lit))
+ value, _ = strconv.Unquote(p.lit)
// Unquote may fail with an error, but only if the scanner found
// an illegal string in the first place. In this case the error
// has already been reported.
diff --git a/src/pkg/encoding/binary/binary.go b/src/pkg/encoding/binary/binary.go
index ee2f23dbb..a4b390701 100644
--- a/src/pkg/encoding/binary/binary.go
+++ b/src/pkg/encoding/binary/binary.go
@@ -126,10 +126,10 @@ func (bigEndian) GoString() string { return "binary.BigEndian" }
// and written to successive fields of the data.
func Read(r io.Reader, order ByteOrder, data interface{}) os.Error {
var v reflect.Value
- switch d := reflect.NewValue(data).(type) {
- case *reflect.PtrValue:
+ switch d := reflect.NewValue(data); d.Kind() {
+ case reflect.Ptr:
v = d.Elem()
- case *reflect.SliceValue:
+ case reflect.Slice:
v = d
default:
return os.NewError("binary.Read: invalid type " + d.Type().String())
@@ -168,26 +168,26 @@ func Write(w io.Writer, order ByteOrder, data interface{}) os.Error {
}
func TotalSize(v reflect.Value) int {
- if sv, ok := v.(*reflect.SliceValue); ok {
- elem := sizeof(v.Type().(*reflect.SliceType).Elem())
+ if v.Kind() == reflect.Slice {
+ elem := sizeof(v.Type().Elem())
if elem < 0 {
return -1
}
- return sv.Len() * elem
+ return v.Len() * elem
}
return sizeof(v.Type())
}
-func sizeof(v reflect.Type) int {
- switch t := v.(type) {
- case *reflect.ArrayType:
+func sizeof(t reflect.Type) int {
+ switch t.Kind() {
+ case reflect.Array:
n := sizeof(t.Elem())
if n < 0 {
return -1
}
return t.Len() * n
- case *reflect.StructType:
+ case reflect.Struct:
sum := 0
for i, n := 0, t.NumField(); i < n; i++ {
s := sizeof(t.Field(i).Type)
@@ -198,12 +198,10 @@ func sizeof(v reflect.Type) int {
}
return sum
- case *reflect.UintType, *reflect.IntType, *reflect.FloatType, *reflect.ComplexType:
- switch t := t.Kind(); t {
- case reflect.Int, reflect.Uint, reflect.Uintptr:
- return -1
- }
- return int(v.Size())
+ case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
+ reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
+ reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128:
+ return int(t.Size())
}
return -1
}
@@ -279,130 +277,118 @@ func (d *decoder) int64() int64 { return int64(d.uint64()) }
func (e *encoder) int64(x int64) { e.uint64(uint64(x)) }
func (d *decoder) value(v reflect.Value) {
- switch v := v.(type) {
- case *reflect.ArrayValue:
+ switch v.Kind() {
+ case reflect.Array:
l := v.Len()
for i := 0; i < l; i++ {
- d.value(v.Elem(i))
+ d.value(v.Index(i))
}
- case *reflect.StructValue:
+ case reflect.Struct:
l := v.NumField()
for i := 0; i < l; i++ {
d.value(v.Field(i))
}
- case *reflect.SliceValue:
+ case reflect.Slice:
l := v.Len()
for i := 0; i < l; i++ {
- d.value(v.Elem(i))
+ d.value(v.Index(i))
}
- case *reflect.IntValue:
- switch v.Type().Kind() {
- case reflect.Int8:
- v.Set(int64(d.int8()))
- case reflect.Int16:
- v.Set(int64(d.int16()))
- case reflect.Int32:
- v.Set(int64(d.int32()))
- case reflect.Int64:
- v.Set(d.int64())
- }
-
- case *reflect.UintValue:
- switch v.Type().Kind() {
- case reflect.Uint8:
- v.Set(uint64(d.uint8()))
- case reflect.Uint16:
- v.Set(uint64(d.uint16()))
- case reflect.Uint32:
- v.Set(uint64(d.uint32()))
- case reflect.Uint64:
- v.Set(d.uint64())
- }
-
- case *reflect.FloatValue:
- switch v.Type().Kind() {
- case reflect.Float32:
- v.Set(float64(math.Float32frombits(d.uint32())))
- case reflect.Float64:
- v.Set(math.Float64frombits(d.uint64()))
- }
-
- case *reflect.ComplexValue:
- switch v.Type().Kind() {
- case reflect.Complex64:
- v.Set(complex(
- float64(math.Float32frombits(d.uint32())),
- float64(math.Float32frombits(d.uint32())),
- ))
- case reflect.Complex128:
- v.Set(complex(
- math.Float64frombits(d.uint64()),
- math.Float64frombits(d.uint64()),
- ))
- }
+ case reflect.Int8:
+ v.SetInt(int64(d.int8()))
+ case reflect.Int16:
+ v.SetInt(int64(d.int16()))
+ case reflect.Int32:
+ v.SetInt(int64(d.int32()))
+ case reflect.Int64:
+ v.SetInt(d.int64())
+
+ case reflect.Uint8:
+ v.SetUint(uint64(d.uint8()))
+ case reflect.Uint16:
+ v.SetUint(uint64(d.uint16()))
+ case reflect.Uint32:
+ v.SetUint(uint64(d.uint32()))
+ case reflect.Uint64:
+ v.SetUint(d.uint64())
+
+ case reflect.Float32:
+ v.SetFloat(float64(math.Float32frombits(d.uint32())))
+ case reflect.Float64:
+ v.SetFloat(math.Float64frombits(d.uint64()))
+
+ case reflect.Complex64:
+ v.SetComplex(complex(
+ float64(math.Float32frombits(d.uint32())),
+ float64(math.Float32frombits(d.uint32())),
+ ))
+ case reflect.Complex128:
+ v.SetComplex(complex(
+ math.Float64frombits(d.uint64()),
+ math.Float64frombits(d.uint64()),
+ ))
}
}
func (e *encoder) value(v reflect.Value) {
- switch v := v.(type) {
- case *reflect.ArrayValue:
+ switch v.Kind() {
+ case reflect.Array:
l := v.Len()
for i := 0; i < l; i++ {
- e.value(v.Elem(i))
+ e.value(v.Index(i))
}
- case *reflect.StructValue:
+ case reflect.Struct:
l := v.NumField()
for i := 0; i < l; i++ {
e.value(v.Field(i))
}
- case *reflect.SliceValue:
+ case reflect.Slice:
l := v.Len()
for i := 0; i < l; i++ {
- e.value(v.Elem(i))
+ e.value(v.Index(i))
}
- case *reflect.IntValue:
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
switch v.Type().Kind() {
case reflect.Int8:
- e.int8(int8(v.Get()))
+ e.int8(int8(v.Int()))
case reflect.Int16:
- e.int16(int16(v.Get()))
+ e.int16(int16(v.Int()))
case reflect.Int32:
- e.int32(int32(v.Get()))
+ e.int32(int32(v.Int()))
case reflect.Int64:
- e.int64(v.Get())
+ e.int64(v.Int())
}
- case *reflect.UintValue:
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
switch v.Type().Kind() {
case reflect.Uint8:
- e.uint8(uint8(v.Get()))
+ e.uint8(uint8(v.Uint()))
case reflect.Uint16:
- e.uint16(uint16(v.Get()))
+ e.uint16(uint16(v.Uint()))
case reflect.Uint32:
- e.uint32(uint32(v.Get()))
+ e.uint32(uint32(v.Uint()))
case reflect.Uint64:
- e.uint64(v.Get())
+ e.uint64(v.Uint())
}
- case *reflect.FloatValue:
+ case reflect.Float32, reflect.Float64:
switch v.Type().Kind() {
case reflect.Float32:
- e.uint32(math.Float32bits(float32(v.Get())))
+ e.uint32(math.Float32bits(float32(v.Float())))
case reflect.Float64:
- e.uint64(math.Float64bits(v.Get()))
+ e.uint64(math.Float64bits(v.Float()))
}
- case *reflect.ComplexValue:
+ case reflect.Complex64, reflect.Complex128:
switch v.Type().Kind() {
case reflect.Complex64:
- x := v.Get()
+ x := v.Complex()
e.uint32(math.Float32bits(float32(real(x))))
e.uint32(math.Float32bits(float32(imag(x))))
case reflect.Complex128:
- x := v.Get()
+ x := v.Complex()
e.uint64(math.Float64bits(real(x)))
e.uint64(math.Float64bits(imag(x)))
}
diff --git a/src/pkg/encoding/binary/binary_test.go b/src/pkg/encoding/binary/binary_test.go
index e09ec489f..d1fc1bfd3 100644
--- a/src/pkg/encoding/binary/binary_test.go
+++ b/src/pkg/encoding/binary/binary_test.go
@@ -152,7 +152,7 @@ func TestWriteT(t *testing.T) {
t.Errorf("WriteT: have nil, want non-nil")
}
- tv := reflect.Indirect(reflect.NewValue(ts)).(*reflect.StructValue)
+ tv := reflect.Indirect(reflect.NewValue(ts))
for i, n := 0, tv.NumField(); i < n; i++ {
err = Write(buf, BigEndian, tv.Field(i).Interface())
if err == nil {
diff --git a/src/pkg/exec/exec.go b/src/pkg/exec/exec.go
index 80f6f3c7d..5398eb8e0 100644
--- a/src/pkg/exec/exec.go
+++ b/src/pkg/exec/exec.go
@@ -2,9 +2,13 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// The exec package runs external commands.
+// The exec package runs external commands. It wraps os.StartProcess
+// to make it easier to remap stdin and stdout, connect I/O with pipes,
+// and do other adjustments.
package exec
+// BUG(r): This package should be made even easier to use or merged into os.
+
import (
"os"
"strconv"
@@ -49,7 +53,7 @@ func modeToFiles(mode, fd int) (*os.File, *os.File, os.Error) {
if fd == 0 {
rw = os.O_RDONLY
}
- f, err := os.Open(os.DevNull, rw, 0)
+ f, err := os.OpenFile(os.DevNull, rw, 0)
return f, nil, err
case PassThrough:
switch fd {
@@ -75,17 +79,19 @@ func modeToFiles(mode, fd int) (*os.File, *os.File, os.Error) {
// Run starts the named binary running with
// arguments argv and environment envv.
+// If the dir argument is not empty, the child changes
+// into the directory before executing the binary.
// It returns a pointer to a new Cmd representing
// the command or an error.
//
-// The parameters stdin, stdout, and stderr
+// The arguments stdin, stdout, and stderr
// specify how to handle standard input, output, and error.
// The choices are DevNull (connect to /dev/null),
// PassThrough (connect to the current process's standard stream),
// Pipe (connect to an operating system pipe), and
// MergeWithStdout (only for standard error; use the same
// file descriptor as was used for standard output).
-// If a parameter is Pipe, then the corresponding field (Stdin, Stdout, Stderr)
+// If an argument is Pipe, then the corresponding field (Stdin, Stdout, Stderr)
// of the returned Cmd is the other end of the pipe.
// Otherwise the field in Cmd is nil.
func Run(name string, argv, envv []string, dir string, stdin, stdout, stderr int) (c *Cmd, err os.Error) {
@@ -105,7 +111,7 @@ func Run(name string, argv, envv []string, dir string, stdin, stdout, stderr int
}
// Run command.
- c.Process, err = os.StartProcess(name, argv, envv, dir, fd[0:])
+ c.Process, err = os.StartProcess(name, argv, &os.ProcAttr{Dir: dir, Files: fd[:], Env: envv})
if err != nil {
goto Error
}
diff --git a/src/pkg/exec/exec_test.go b/src/pkg/exec/exec_test.go
index 3a3d3b1a5..5e37b99ee 100644
--- a/src/pkg/exec/exec_test.go
+++ b/src/pkg/exec/exec_test.go
@@ -118,3 +118,55 @@ func TestAddEnvVar(t *testing.T) {
t.Fatal("close:", err)
}
}
+
+var tryargs = []string{
+ `2`,
+ `2 `,
+ "2 \t",
+ `2" "`,
+ `2 ab `,
+ `2 "ab" `,
+ `2 \ `,
+ `2 \\ `,
+ `2 \" `,
+ `2 \`,
+ `2\`,
+ `2"`,
+ `2\"`,
+ `2 "`,
+ `2 \"`,
+ ``,
+ `2 ^ `,
+ `2 \^`,
+}
+
+func TestArgs(t *testing.T) {
+ for _, a := range tryargs {
+ argv := []string{
+ "awk",
+ `BEGIN{printf("%s|%s|%s",ARGV[1],ARGV[2],ARGV[3])}`,
+ "/dev/null",
+ a,
+ "EOF",
+ }
+ exe, err := LookPath(argv[0])
+ if err != nil {
+ t.Fatal("run:", err)
+ }
+ cmd, err := Run(exe, argv, nil, "", DevNull, Pipe, DevNull)
+ if err != nil {
+ t.Fatal("run:", err)
+ }
+ buf, err := ioutil.ReadAll(cmd.Stdout)
+ if err != nil {
+ t.Fatal("read:", err)
+ }
+ expect := "/dev/null|" + a + "|EOF"
+ if string(buf) != expect {
+ t.Errorf("read: got %q expect %q", buf, expect)
+ }
+ if err = cmd.Close(); err != nil {
+ t.Fatal("close:", err)
+ }
+ }
+}
diff --git a/src/pkg/exp/datafmt/datafmt.go b/src/pkg/exp/datafmt/datafmt.go
index 46c412342..6d816fc2d 100644
--- a/src/pkg/exp/datafmt/datafmt.go
+++ b/src/pkg/exp/datafmt/datafmt.go
@@ -408,20 +408,20 @@ func (s *State) error(msg string) {
//
func typename(typ reflect.Type) string {
- switch typ.(type) {
- case *reflect.ArrayType:
+ switch typ.Kind() {
+ case reflect.Array:
return "array"
- case *reflect.SliceType:
+ case reflect.Slice:
return "array"
- case *reflect.ChanType:
+ case reflect.Chan:
return "chan"
- case *reflect.FuncType:
+ case reflect.Func:
return "func"
- case *reflect.InterfaceType:
+ case reflect.Interface:
return "interface"
- case *reflect.MapType:
+ case reflect.Map:
return "map"
- case *reflect.PtrType:
+ case reflect.Ptr:
return "ptr"
}
return typ.String()
@@ -519,38 +519,38 @@ func (s *State) eval(fexpr expr, value reflect.Value, index int) bool {
case "*":
// indirection: operation is type-specific
- switch v := value.(type) {
- case *reflect.ArrayValue:
+ switch v := value; v.Kind() {
+ case reflect.Array:
if v.Len() <= index {
return false
}
- value = v.Elem(index)
+ value = v.Index(index)
- case *reflect.SliceValue:
+ case reflect.Slice:
if v.IsNil() || v.Len() <= index {
return false
}
- value = v.Elem(index)
+ value = v.Index(index)
- case *reflect.MapValue:
+ case reflect.Map:
s.error("reflection support for maps incomplete")
- case *reflect.PtrValue:
+ case reflect.Ptr:
if v.IsNil() {
return false
}
value = v.Elem()
- case *reflect.InterfaceValue:
+ case reflect.Interface:
if v.IsNil() {
return false
}
value = v.Elem()
- case *reflect.ChanValue:
+ case reflect.Chan:
s.error("reflection support for chans incomplete")
- case *reflect.FuncValue:
+ case reflect.Func:
s.error("reflection support for funcs incomplete")
default:
@@ -560,9 +560,9 @@ func (s *State) eval(fexpr expr, value reflect.Value, index int) bool {
default:
// value is value of named field
var field reflect.Value
- if sval, ok := value.(*reflect.StructValue); ok {
+ if sval := value; sval.Kind() == reflect.Struct {
field = sval.FieldByName(t.fieldName)
- if field == nil {
+ 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()))
}
@@ -672,7 +672,7 @@ func (f Format) Eval(env Environment, args ...interface{}) ([]byte, os.Error) {
go func() {
for _, v := range args {
fld := reflect.NewValue(v)
- if fld == nil {
+ if !fld.IsValid() {
errors <- os.NewError("nil argument")
return
}
diff --git a/src/pkg/exp/datafmt/parser.go b/src/pkg/exp/datafmt/parser.go
index c6d140264..7dedb531a 100644
--- a/src/pkg/exp/datafmt/parser.go
+++ b/src/pkg/exp/datafmt/parser.go
@@ -22,7 +22,7 @@ type parser struct {
file *token.File
pos token.Pos // token position
tok token.Token // one token look-ahead
- lit []byte // token literal
+ lit string // token literal
packs map[string]string // PackageName -> ImportPath
rules map[string]expr // RuleName -> Expression
@@ -62,7 +62,7 @@ func (p *parser) 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(pos, msg)
@@ -80,7 +80,7 @@ func (p *parser) expect(tok token.Token) token.Pos {
func (p *parser) parseIdentifier() string {
- name := string(p.lit)
+ name := p.lit
p.expect(token.IDENT)
return name
}
@@ -130,7 +130,7 @@ func (p *parser) parseRuleName() (string, bool) {
func (p *parser) parseString() string {
s := ""
if p.tok == token.STRING {
- s, _ = strconv.Unquote(string(p.lit))
+ s, _ = strconv.Unquote(p.lit)
// Unquote may fail with an error, but only if the scanner found
// an illegal string in the first place. In this case the error
// has already been reported.
@@ -181,7 +181,7 @@ func (p *parser) parseField() expr {
var fname string
switch p.tok {
case token.ILLEGAL:
- if string(p.lit) != "@" {
+ if p.lit != "@" {
return nil
}
fname = "@"
diff --git a/src/pkg/exp/draw/x11/auth.go b/src/pkg/exp/draw/x11/auth.go
index 896dedf05..d48936ac1 100644
--- a/src/pkg/exp/draw/x11/auth.go
+++ b/src/pkg/exp/draw/x11/auth.go
@@ -53,7 +53,7 @@ func readAuth(displayStr string) (name, data string, err os.Error) {
}
fn = home + "/.Xauthority"
}
- r, err := os.Open(fn, os.O_RDONLY, 0444)
+ r, err := os.Open(fn)
if err != nil {
return
}
diff --git a/src/pkg/exp/draw/x11/conn.go b/src/pkg/exp/draw/x11/conn.go
index e28fb2170..53294af15 100644
--- a/src/pkg/exp/draw/x11/conn.go
+++ b/src/pkg/exp/draw/x11/conn.go
@@ -286,11 +286,11 @@ func connect(display string) (conn net.Conn, displayStr string, err os.Error) {
}
// Make the connection.
if socket != "" {
- conn, err = net.Dial("unix", "", socket+":"+displayStr)
+ conn, err = net.Dial("unix", socket+":"+displayStr)
} else if host != "" {
- conn, err = net.Dial(protocol, "", host+":"+strconv.Itoa(6000+displayInt))
+ conn, err = net.Dial(protocol, host+":"+strconv.Itoa(6000+displayInt))
} else {
- conn, err = net.Dial("unix", "", "/tmp/.X11-unix/X"+displayStr)
+ conn, err = net.Dial("unix", "/tmp/.X11-unix/X"+displayStr)
}
if err != nil {
return nil, "", os.NewError("cannot connect to " + display + ": " + err.String())
diff --git a/src/pkg/exp/eval/bridge.go b/src/pkg/exp/eval/bridge.go
index 12835c4c0..d1efa2eb6 100644
--- a/src/pkg/exp/eval/bridge.go
+++ b/src/pkg/exp/eval/bridge.go
@@ -34,54 +34,49 @@ func TypeFromNative(t reflect.Type) Type {
}
var et Type
- switch t := t.(type) {
- case *reflect.BoolType:
+ switch t.Kind() {
+ case reflect.Bool:
et = BoolType
- case *reflect.FloatType:
- switch t.Kind() {
- case reflect.Float32:
- et = Float32Type
- case reflect.Float64:
- et = Float64Type
- }
- case *reflect.IntType:
- switch t.Kind() {
- case reflect.Int16:
- et = Int16Type
- case reflect.Int32:
- et = Int32Type
- case reflect.Int64:
- et = Int64Type
- case reflect.Int8:
- et = Int8Type
- case reflect.Int:
- et = IntType
- }
- case *reflect.UintType:
- switch t.Kind() {
- case reflect.Uint16:
- et = Uint16Type
- case reflect.Uint32:
- et = Uint32Type
- case reflect.Uint64:
- et = Uint64Type
- case reflect.Uint8:
- et = Uint8Type
- case reflect.Uint:
- et = UintType
- case reflect.Uintptr:
- et = UintptrType
- }
- case *reflect.StringType:
+
+ case reflect.Float32:
+ et = Float32Type
+ case reflect.Float64:
+ et = Float64Type
+
+ case reflect.Int16:
+ et = Int16Type
+ case reflect.Int32:
+ et = Int32Type
+ case reflect.Int64:
+ et = Int64Type
+ case reflect.Int8:
+ et = Int8Type
+ case reflect.Int:
+ et = IntType
+
+ case reflect.Uint16:
+ et = Uint16Type
+ case reflect.Uint32:
+ et = Uint32Type
+ case reflect.Uint64:
+ et = Uint64Type
+ case reflect.Uint8:
+ et = Uint8Type
+ case reflect.Uint:
+ et = UintType
+ case reflect.Uintptr:
+ et = UintptrType
+
+ case reflect.String:
et = StringType
- case *reflect.ArrayType:
+ case reflect.Array:
et = NewArrayType(int64(t.Len()), TypeFromNative(t.Elem()))
- case *reflect.ChanType:
+ case reflect.Chan:
log.Panicf("%T not implemented", t)
- case *reflect.FuncType:
+ case reflect.Func:
nin := t.NumIn()
// Variadic functions have DotDotDotType at the end
- variadic := t.DotDotDot()
+ variadic := t.IsVariadic()
if variadic {
nin--
}
@@ -94,15 +89,15 @@ func TypeFromNative(t reflect.Type) Type {
out[i] = TypeFromNative(t.Out(i))
}
et = NewFuncType(in, variadic, out)
- case *reflect.InterfaceType:
+ case reflect.Interface:
log.Panicf("%T not implemented", t)
- case *reflect.MapType:
+ case reflect.Map:
log.Panicf("%T not implemented", t)
- case *reflect.PtrType:
+ case reflect.Ptr:
et = NewPtrType(TypeFromNative(t.Elem()))
- case *reflect.SliceType:
+ case reflect.Slice:
et = NewSliceType(TypeFromNative(t.Elem()))
- case *reflect.StructType:
+ case reflect.Struct:
n := t.NumField()
fields := make([]StructField, n)
for i := 0; i < n; i++ {
@@ -113,7 +108,7 @@ func TypeFromNative(t reflect.Type) Type {
fields[i].Anonymous = sf.Anonymous
}
et = NewStructType(fields)
- case *reflect.UnsafePointerType:
+ case reflect.UnsafePointer:
log.Panicf("%T not implemented", t)
default:
log.Panicf("unexpected reflect.Type: %T", t)
diff --git a/src/pkg/exp/eval/eval b/src/pkg/exp/eval/eval
new file mode 100755
index 000000000..20231f2e2
--- /dev/null
+++ b/src/pkg/exp/eval/eval
Binary files differ
diff --git a/src/pkg/exp/eval/eval_test.go b/src/pkg/exp/eval/eval_test.go
index ff28cf1a9..541d3feb7 100644
--- a/src/pkg/exp/eval/eval_test.go
+++ b/src/pkg/exp/eval/eval_test.go
@@ -39,9 +39,13 @@ type job struct {
}
func runTests(t *testing.T, baseName string, tests []test) {
- for i, test := range tests {
+ delta := 1
+ if testing.Short() {
+ delta = 16
+ }
+ for i := 0; i < len(tests); i += delta {
name := fmt.Sprintf("%s[%d]", baseName, i)
- test.run(t, name)
+ tests[i].run(t, name)
}
}
diff --git a/src/pkg/exp/eval/stmt.go b/src/pkg/exp/eval/stmt.go
index 5c5d4338a..f6b7c1cda 100644
--- a/src/pkg/exp/eval/stmt.go
+++ b/src/pkg/exp/eval/stmt.go
@@ -287,9 +287,6 @@ func (a *stmtCompiler) compile(s ast.Stmt) {
case *ast.SwitchStmt:
a.compileSwitchStmt(s)
- case *ast.TypeCaseClause:
- notimpl = true
-
case *ast.TypeSwitchStmt:
notimpl = true
@@ -1012,13 +1009,13 @@ func (a *stmtCompiler) compileSwitchStmt(s *ast.SwitchStmt) {
a.diagAt(clause.Pos(), "switch statement must contain case clauses")
continue
}
- if clause.Values == nil {
+ if clause.List == nil {
if hasDefault {
a.diagAt(clause.Pos(), "switch statement contains more than one default case")
}
hasDefault = true
} else {
- ncases += len(clause.Values)
+ ncases += len(clause.List)
}
}
@@ -1030,7 +1027,7 @@ func (a *stmtCompiler) compileSwitchStmt(s *ast.SwitchStmt) {
if !ok {
continue
}
- for _, v := range clause.Values {
+ for _, v := range clause.List {
e := condbc.compileExpr(condbc.block, false, v)
switch {
case e == nil:
@@ -1077,8 +1074,8 @@ func (a *stmtCompiler) compileSwitchStmt(s *ast.SwitchStmt) {
// Save jump PC's
pc := a.nextPC()
- if clause.Values != nil {
- for _ = range clause.Values {
+ if clause.List != nil {
+ for _ = range clause.List {
casePCs[i] = &pc
i++
}
diff --git a/src/pkg/exp/eval/stmt_test.go b/src/pkg/exp/eval/stmt_test.go
index 4a883ef5e..a8a3e1620 100644
--- a/src/pkg/exp/eval/stmt_test.go
+++ b/src/pkg/exp/eval/stmt_test.go
@@ -27,7 +27,7 @@ var stmtTests = []test{
CErr("i, u := 1, 2", atLeastOneDecl),
Val2("i, x := 2, f", "i", 2, "x", 1.0),
// Various errors
- CErr("1 := 2", "left side of := must be a name"),
+ CErr("1 := 2", "expected identifier"),
CErr("c, a := 1, 1", "cannot assign"),
// Unpacking
Val2("x, y := oneTwo()", "x", 1, "y", 2),
diff --git a/src/pkg/exp/eval/type.go b/src/pkg/exp/eval/type.go
index 3f272ce4b..0d6dfe923 100644
--- a/src/pkg/exp/eval/type.go
+++ b/src/pkg/exp/eval/type.go
@@ -86,7 +86,7 @@ func hashTypeArray(key []Type) uintptr {
if t == nil {
continue
}
- addr := reflect.NewValue(t).(*reflect.PtrValue).Get()
+ addr := reflect.NewValue(t).Pointer()
hash ^= addr
}
return hash
diff --git a/src/pkg/exp/ogle/cmd.go b/src/pkg/exp/ogle/cmd.go
index 4f67032d0..813d3a875 100644
--- a/src/pkg/exp/ogle/cmd.go
+++ b/src/pkg/exp/ogle/cmd.go
@@ -160,7 +160,7 @@ func cmdLoad(args []byte) os.Error {
} else {
fname = parts[0]
}
- tproc, err = proc.ForkExec(fname, parts, os.Environ(), "", []*os.File{os.Stdin, os.Stdout, os.Stderr})
+ tproc, err = proc.StartProcess(fname, parts, &os.ProcAttr{Files: []*os.File{os.Stdin, os.Stdout, os.Stderr}})
if err != nil {
return err
}
@@ -170,7 +170,7 @@ func cmdLoad(args []byte) os.Error {
}
// Get symbols
- f, err := os.Open(fname, os.O_RDONLY, 0)
+ f, err := os.Open(fname)
if err != nil {
tproc.Detach()
return err
@@ -205,7 +205,7 @@ func parseLoad(args []byte) (ident string, path string, err os.Error) {
sc, ev := newScanner(args)
var toks [4]token.Token
- var lits [4][]byte
+ var lits [4]string
for i := range toks {
_, toks[i], lits[i] = sc.Scan()
}
diff --git a/src/pkg/exp/ogle/process.go b/src/pkg/exp/ogle/process.go
index 58e830aa6..e4f44b6fc 100644
--- a/src/pkg/exp/ogle/process.go
+++ b/src/pkg/exp/ogle/process.go
@@ -226,8 +226,8 @@ func (p *Process) bootstrap() {
p.runtime.G = newManualType(eval.TypeOfNative(rt1G{}), p.Arch)
// Get addresses of type.*runtime.XType for discrimination.
- rtv := reflect.Indirect(reflect.NewValue(&p.runtime)).(*reflect.StructValue)
- rtvt := rtv.Type().(*reflect.StructType)
+ rtv := reflect.Indirect(reflect.NewValue(&p.runtime))
+ rtvt := rtv.Type()
for i := 0; i < rtv.NumField(); i++ {
n := rtvt.Field(i).Name
if n[0] != 'P' || n[1] < 'A' || n[1] > 'Z' {
@@ -237,7 +237,7 @@ func (p *Process) bootstrap() {
if sym == nil {
continue
}
- rtv.Field(i).(*reflect.UintValue).Set(sym.Value)
+ rtv.Field(i).SetUint(sym.Value)
}
// Get runtime field indexes
diff --git a/src/pkg/exp/ogle/rruntime.go b/src/pkg/exp/ogle/rruntime.go
index 33f1935b8..e234f3186 100644
--- a/src/pkg/exp/ogle/rruntime.go
+++ b/src/pkg/exp/ogle/rruntime.go
@@ -236,9 +236,9 @@ type runtimeValues struct {
// indexes gathered from the remoteTypes recorded in a runtimeValues
// structure.
func fillRuntimeIndexes(runtime *runtimeValues, out *runtimeIndexes) {
- outv := reflect.Indirect(reflect.NewValue(out)).(*reflect.StructValue)
- outt := outv.Type().(*reflect.StructType)
- runtimev := reflect.Indirect(reflect.NewValue(runtime)).(*reflect.StructValue)
+ outv := reflect.Indirect(reflect.NewValue(out))
+ outt := outv.Type()
+ runtimev := reflect.Indirect(reflect.NewValue(runtime))
// out contains fields corresponding to each runtime type
for i := 0; i < outt.NumField(); i++ {
@@ -260,12 +260,12 @@ func fillRuntimeIndexes(runtime *runtimeValues, out *runtimeIndexes) {
}
// Fill this field of out
- outStructv := outv.Field(i).(*reflect.StructValue)
- outStructt := outStructv.Type().(*reflect.StructType)
+ outStructv := outv.Field(i)
+ outStructt := outStructv.Type()
for j := 0; j < outStructt.NumField(); j++ {
- f := outStructv.Field(j).(*reflect.IntValue)
+ f := outStructv.Field(j)
name := outStructt.Field(j).Name
- f.Set(int64(indexes[name]))
+ f.SetInt(int64(indexes[name]))
}
}
}
diff --git a/src/pkg/expvar/expvar.go b/src/pkg/expvar/expvar.go
index b1f0f6c1b..ed6cff78d 100644
--- a/src/pkg/expvar/expvar.go
+++ b/src/pkg/expvar/expvar.go
@@ -269,7 +269,7 @@ func Iter() <-chan KeyValue {
}
func expvarHandler(w http.ResponseWriter, r *http.Request) {
- w.SetHeader("content-type", "application/json; charset=utf-8")
+ w.Header().Set("Content-Type", "application/json; charset=utf-8")
fmt.Fprintf(w, "{\n")
first := true
for name, value := range vars {
diff --git a/src/pkg/flag/flag.go b/src/pkg/flag/flag.go
index be972057e..19a310455 100644
--- a/src/pkg/flag/flag.go
+++ b/src/pkg/flag/flag.go
@@ -56,7 +56,7 @@
flag.Bool(...) // global options
flag.Parse() // parse leading command
- subcmd := flag.Args(0)
+ subcmd := flag.Arg(0)
switch subcmd {
// add per-subcommand options
}
@@ -68,6 +68,7 @@ package flag
import (
"fmt"
"os"
+ "sort"
"strconv"
)
@@ -205,16 +206,34 @@ type allFlags struct {
var flags *allFlags
-// VisitAll visits the flags, calling fn for each. It visits all flags, even those not set.
+// sortFlags returns the flags as a slice in lexicographical sorted order.
+func sortFlags(flags map[string]*Flag) []*Flag {
+ list := make(sort.StringArray, len(flags))
+ i := 0
+ for _, f := range flags {
+ list[i] = f.Name
+ i++
+ }
+ list.Sort()
+ result := make([]*Flag, len(list))
+ for i, name := range list {
+ result[i] = flags[name]
+ }
+ return result
+}
+
+// VisitAll visits the flags in lexicographical order, calling fn for each.
+// It visits all flags, even those not set.
func VisitAll(fn func(*Flag)) {
- for _, f := range flags.formal {
+ for _, f := range sortFlags(flags.formal) {
fn(f)
}
}
-// Visit visits the flags, calling fn for each. It visits only those flags that have been set.
+// Visit visits the flags in lexicographical order, calling fn for each.
+// It visits only those flags that have been set.
func Visit(fn func(*Flag)) {
- for _, f := range flags.actual {
+ for _, f := range sortFlags(flags.actual) {
fn(f)
}
}
@@ -260,7 +279,9 @@ var Usage = func() {
var panicOnError = false
-func fail() {
+// failf prints to standard error a formatted error and Usage, and then exits the program.
+func failf(format string, a ...interface{}) {
+ fmt.Fprintf(os.Stderr, format, a...)
Usage()
if panicOnError {
panic("flag parse error")
@@ -268,6 +289,7 @@ func fail() {
os.Exit(2)
}
+// NFlag returns the number of flags that have been set.
func NFlag() int { return len(flags.actual) }
// Arg returns the i'th command-line argument. Arg(0) is the first remaining argument
@@ -415,8 +437,7 @@ func (f *allFlags) parseOne() (ok bool) {
}
name := s[num_minuses:]
if len(name) == 0 || name[0] == '-' || name[0] == '=' {
- fmt.Fprintln(os.Stderr, "bad flag syntax:", s)
- fail()
+ failf("bad flag syntax: %s\n", s)
}
// it's a flag. does it have an argument?
@@ -434,14 +455,12 @@ func (f *allFlags) parseOne() (ok bool) {
m := flags.formal
flag, alreadythere := m[name] // BUG
if !alreadythere {
- fmt.Fprintf(os.Stderr, "flag provided but not defined: -%s\n", name)
- fail()
+ failf("flag provided but not defined: -%s\n", name)
}
if fv, ok := flag.Value.(*boolValue); ok { // special case: doesn't need an arg
if has_value {
if !fv.Set(value) {
- fmt.Fprintf(os.Stderr, "invalid boolean value %q for flag: -%s\n", value, name)
- fail()
+ failf("invalid boolean value %q for flag: -%s\n", value, name)
}
} else {
fv.Set("true")
@@ -454,13 +473,11 @@ func (f *allFlags) parseOne() (ok bool) {
value, f.args = f.args[0], f.args[1:]
}
if !has_value {
- fmt.Fprintf(os.Stderr, "flag needs an argument: -%s\n", name)
- fail()
+ failf("flag needs an argument: -%s\n", name)
}
ok = flag.Value.Set(value)
if !ok {
- fmt.Fprintf(os.Stderr, "invalid value %q for flag: -%s\n", value, name)
- fail()
+ failf("invalid value %q for flag: -%s\n", value, name)
}
}
flags.actual[name] = flag
diff --git a/src/pkg/flag/flag_test.go b/src/pkg/flag/flag_test.go
index 30a21e61a..1e47d12e4 100644
--- a/src/pkg/flag/flag_test.go
+++ b/src/pkg/flag/flag_test.go
@@ -8,6 +8,7 @@ import (
. "flag"
"fmt"
"os"
+ "sort"
"testing"
)
@@ -77,6 +78,12 @@ func TestEverything(t *testing.T) {
t.Log(k, *v)
}
}
+ // Now test they're visited in sort order.
+ var flagNames []string
+ Visit(func(f *Flag) { flagNames = append(flagNames, f.Name) })
+ if !sort.StringsAreSorted(flagNames) {
+ t.Errorf("flag names not sorted: %v", flagNames)
+ }
}
func TestUsage(t *testing.T) {
diff --git a/src/pkg/fmt/fmt_test.go b/src/pkg/fmt/fmt_test.go
index c8aa6090b..b3c0c5abe 100644
--- a/src/pkg/fmt/fmt_test.go
+++ b/src/pkg/fmt/fmt_test.go
@@ -139,7 +139,17 @@ var fmttests = []struct {
{"%5s", "abc", " abc"},
{"%2s", "\u263a", " \u263a"},
{"%-5s", "abc", "abc "},
+ {"%-8q", "abc", `"abc" `},
{"%05s", "abc", "00abc"},
+ {"%08q", "abc", `000"abc"`},
+ {"%5s", "abcdefghijklmnopqrstuvwxyz", "abcdefghijklmnopqrstuvwxyz"},
+ {"%.5s", "abcdefghijklmnopqrstuvwxyz", "abcde"},
+ {"%.5s", "日本語日本語", "日本語日本"},
+ {"%.5s", []byte("日本語日本語"), "日本語日本"},
+ {"%.5q", "abcdefghijklmnopqrstuvwxyz", `"abcde"`},
+ {"%.3q", "日本語日本語", `"\u65e5\u672c\u8a9e"`},
+ {"%.3q", []byte("日本語日本語"), `"\u65e5\u672c\u8a9e"`},
+ {"%10.1q", "日本語日本語", ` "\u65e5"`},
// integers
{"%d", 12345, "12345"},
@@ -160,6 +170,7 @@ var fmttests = []struct {
// unicode format
{"%U", 0x1, "U+0001"},
+ {"%U", uint(0x1), "U+0001"},
{"%.8U", 0x2, "U+00000002"},
{"%U", 0x1234, "U+1234"},
{"%U", 0x12345, "U+12345"},
@@ -442,6 +453,9 @@ func BenchmarkSprintfPrefixedInt(b *testing.B) {
}
func TestCountMallocs(t *testing.T) {
+ if testing.Short() {
+ return
+ }
mallocs := 0 - runtime.MemStats.Mallocs
for i := 0; i < 100; i++ {
Sprintf("")
diff --git a/src/pkg/fmt/format.go b/src/pkg/fmt/format.go
index caaa7ac1a..f9d2b4fca 100644
--- a/src/pkg/fmt/format.go
+++ b/src/pkg/fmt/format.go
@@ -235,13 +235,24 @@ func (f *fmt) integer(a int64, base uint64, signedness bool, digits string) {
f.pad(buf[i:])
}
-// fmt_s formats a string.
-func (f *fmt) fmt_s(s string) {
- if f.precPresent {
- if f.prec < len(s) {
- s = s[0:f.prec]
+// truncate truncates the string to the specified precision, if present.
+func (f *fmt) truncate(s string) string {
+ if f.precPresent && f.prec < utf8.RuneCountInString(s) {
+ n := f.prec
+ for i := range s {
+ if n == 0 {
+ s = s[:i]
+ break
+ }
+ n--
}
}
+ return s
+}
+
+// fmt_s formats a string.
+func (f *fmt) fmt_s(s string) {
+ s = f.truncate(s)
f.padString(s)
}
@@ -275,6 +286,7 @@ func (f *fmt) fmt_sX(s string) {
// fmt_q formats a string as a double-quoted, escaped Go string constant.
func (f *fmt) fmt_q(s string) {
+ s = f.truncate(s)
var quoted string
if f.sharp && strconv.CanBackquote(s) {
quoted = "`" + s + "`"
diff --git a/src/pkg/fmt/print.go b/src/pkg/fmt/print.go
index 4e14fdaa4..7fca6afe4 100644
--- a/src/pkg/fmt/print.go
+++ b/src/pkg/fmt/print.go
@@ -256,9 +256,9 @@ func Sprintln(a ...interface{}) string {
// Get the i'th arg of the struct value.
// If the arg itself is an interface, return a value for
// the thing inside the interface, not the interface itself.
-func getField(v *reflect.StructValue, i int) reflect.Value {
+func getField(v reflect.Value, i int) reflect.Value {
val := v.Field(i)
- if i, ok := val.(*reflect.InterfaceValue); ok {
+ if i := val; i.Kind() == reflect.Interface {
if inter := i.Interface(); inter != nil {
return reflect.NewValue(inter)
}
@@ -278,11 +278,6 @@ func parsenum(s string, start, end int) (num int, isnum bool, newi int) {
return
}
-// Reflection values like reflect.FuncValue implement this method. We use it for %p.
-type uintptrGetter interface {
- Get() uintptr
-}
-
func (p *pp) unknownType(v interface{}) {
if v == nil {
p.buf.Write(nilAngleBytes)
@@ -394,6 +389,8 @@ func (p *pp) fmtUint64(v uint64, verb int, goSyntax bool, value interface{}) {
p.fmt.integer(int64(v), 16, unsigned, ldigits)
case 'X':
p.fmt.integer(int64(v), 16, unsigned, udigits)
+ case 'U':
+ p.fmtUnicode(int64(v))
default:
p.badVerb(verb, value)
}
@@ -520,12 +517,14 @@ func (p *pp) fmtBytes(v []byte, verb int, goSyntax bool, depth int, value interf
}
func (p *pp) fmtPointer(field interface{}, value reflect.Value, verb int, goSyntax bool) {
- v, ok := value.(uintptrGetter)
- if !ok { // reflect.PtrValue is a uintptrGetter, so failure means it's not a pointer at all.
+ var u uintptr
+ switch value.Kind() {
+ case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Slice, reflect.UnsafePointer:
+ u = value.Pointer()
+ default:
p.badVerb(verb, field)
return
}
- u := v.Get()
if goSyntax {
p.add('(')
p.buf.WriteString(reflect.Typeof(field).String())
@@ -534,7 +533,7 @@ func (p *pp) fmtPointer(field interface{}, value reflect.Value, verb int, goSynt
if u == 0 {
p.buf.Write(nilBytes)
} else {
- p.fmt0x64(uint64(v.Get()), true)
+ p.fmt0x64(uint64(u), true)
}
p.add(')')
} else {
@@ -657,35 +656,35 @@ func (p *pp) printField(field interface{}, verb int, plus, goSyntax bool, depth
value := reflect.NewValue(field)
BigSwitch:
- switch f := value.(type) {
- case *reflect.BoolValue:
- p.fmtBool(f.Get(), verb, field)
- case *reflect.IntValue:
- p.fmtInt64(f.Get(), verb, field)
- case *reflect.UintValue:
- p.fmtUint64(uint64(f.Get()), verb, goSyntax, field)
- case *reflect.FloatValue:
+ switch f := value; f.Kind() {
+ case reflect.Bool:
+ p.fmtBool(f.Bool(), verb, field)
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ p.fmtInt64(f.Int(), verb, field)
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ p.fmtUint64(uint64(f.Uint()), verb, goSyntax, field)
+ case reflect.Float32, reflect.Float64:
if f.Type().Size() == 4 {
- p.fmtFloat32(float32(f.Get()), verb, field)
+ p.fmtFloat32(float32(f.Float()), verb, field)
} else {
- p.fmtFloat64(float64(f.Get()), verb, field)
+ p.fmtFloat64(float64(f.Float()), verb, field)
}
- case *reflect.ComplexValue:
+ case reflect.Complex64, reflect.Complex128:
if f.Type().Size() == 8 {
- p.fmtComplex64(complex64(f.Get()), verb, field)
+ p.fmtComplex64(complex64(f.Complex()), verb, field)
} else {
- p.fmtComplex128(complex128(f.Get()), verb, field)
+ p.fmtComplex128(complex128(f.Complex()), verb, field)
}
- case *reflect.StringValue:
- p.fmtString(f.Get(), verb, goSyntax, field)
- case *reflect.MapValue:
+ case reflect.String:
+ p.fmtString(f.String(), verb, goSyntax, field)
+ case reflect.Map:
if goSyntax {
p.buf.WriteString(f.Type().String())
p.buf.WriteByte('{')
} else {
p.buf.Write(mapBytes)
}
- keys := f.Keys()
+ keys := f.MapKeys()
for i, key := range keys {
if i > 0 {
if goSyntax {
@@ -696,20 +695,20 @@ BigSwitch:
}
p.printField(key.Interface(), verb, plus, goSyntax, depth+1)
p.buf.WriteByte(':')
- p.printField(f.Elem(key).Interface(), verb, plus, goSyntax, depth+1)
+ p.printField(f.MapIndex(key).Interface(), verb, plus, goSyntax, depth+1)
}
if goSyntax {
p.buf.WriteByte('}')
} else {
p.buf.WriteByte(']')
}
- case *reflect.StructValue:
+ case reflect.Struct:
if goSyntax {
p.buf.WriteString(reflect.Typeof(field).String())
}
p.add('{')
v := f
- t := v.Type().(*reflect.StructType)
+ t := v.Type()
for i := 0; i < v.NumField(); i++ {
if i > 0 {
if goSyntax {
@@ -727,9 +726,9 @@ BigSwitch:
p.printField(getField(v, i).Interface(), verb, plus, goSyntax, depth+1)
}
p.buf.WriteByte('}')
- case *reflect.InterfaceValue:
+ case reflect.Interface:
value := f.Elem()
- if value == nil {
+ if !value.IsValid() {
if goSyntax {
p.buf.WriteString(reflect.Typeof(field).String())
p.buf.Write(nilParenBytes)
@@ -739,9 +738,9 @@ BigSwitch:
} else {
return p.printField(value.Interface(), verb, plus, goSyntax, depth+1)
}
- case reflect.ArrayOrSliceValue:
+ case reflect.Array, reflect.Slice:
// Byte slices are special.
- if f.Type().(reflect.ArrayOrSliceType).Elem().Kind() == reflect.Uint8 {
+ if f.Type().Elem().Kind() == reflect.Uint8 {
// We know it's a slice of bytes, but we also know it does not have static type
// []byte, or it would have been caught above. Therefore we cannot convert
// it directly in the (slightly) obvious way: f.Interface().([]byte); it doesn't have
@@ -751,7 +750,7 @@ BigSwitch:
// if reflection could help a little more.
bytes := make([]byte, f.Len())
for i := range bytes {
- bytes[i] = byte(f.Elem(i).(*reflect.UintValue).Get())
+ bytes[i] = byte(f.Index(i).Uint())
}
p.fmtBytes(bytes, verb, goSyntax, depth, field)
return verb == 's'
@@ -770,24 +769,24 @@ BigSwitch:
p.buf.WriteByte(' ')
}
}
- p.printField(f.Elem(i).Interface(), verb, plus, goSyntax, depth+1)
+ p.printField(f.Index(i).Interface(), verb, plus, goSyntax, depth+1)
}
if goSyntax {
p.buf.WriteByte('}')
} else {
p.buf.WriteByte(']')
}
- case *reflect.PtrValue:
- v := f.Get()
+ case reflect.Ptr:
+ v := f.Pointer()
// pointer to array or slice or struct? ok at top level
// but not embedded (avoid loops)
if v != 0 && depth == 0 {
- switch a := f.Elem().(type) {
- case reflect.ArrayOrSliceValue:
+ switch a := f.Elem(); a.Kind() {
+ case reflect.Array, reflect.Slice:
p.buf.WriteByte('&')
p.printField(a.Interface(), verb, plus, goSyntax, depth+1)
break BigSwitch
- case *reflect.StructValue:
+ case reflect.Struct:
p.buf.WriteByte('&')
p.printField(a.Interface(), verb, plus, goSyntax, depth+1)
break BigSwitch
@@ -811,7 +810,7 @@ BigSwitch:
break
}
p.fmt0x64(uint64(v), true)
- case uintptrGetter:
+ case reflect.Chan, reflect.Func, reflect.UnsafePointer:
p.fmtPointer(field, value, verb, goSyntax)
default:
p.unknownType(f)
diff --git a/src/pkg/fmt/scan.go b/src/pkg/fmt/scan.go
index c0f2bacb6..b1b3975e2 100644
--- a/src/pkg/fmt/scan.go
+++ b/src/pkg/fmt/scan.go
@@ -35,10 +35,15 @@ type ScanState interface {
ReadRune() (rune int, size int, err os.Error)
// UnreadRune causes the next call to ReadRune to return the same rune.
UnreadRune() os.Error
- // Token returns the next space-delimited token from the input. If
- // a width has been specified, the returned token will be no longer
- // than the width.
- Token() (token string, err os.Error)
+ // Token skips space in the input if skipSpace is true, then returns the
+ // run of Unicode code points c satisfying f(c). If f is nil,
+ // !unicode.IsSpace(c) is used; that is, the token will hold non-space
+ // characters. Newlines are treated as space unless the scan operation
+ // is Scanln, Fscanln or Sscanln, in which case a newline is treated as
+ // EOF. The returned slice points to shared data that may be overwritten
+ // by the next call to Token, a call to a Scan function using the ScanState
+ // as input, or when the calling Scan method returns.
+ Token(skipSpace bool, f func(int) bool) (token []byte, err os.Error)
// Width returns the value of the width option and whether it has been set.
// The unit is Unicode code points.
Width() (wid int, ok bool)
@@ -134,7 +139,7 @@ type scanError struct {
err os.Error
}
-const EOF = -1
+const eof = -1
// ss is the internal implementation of ScanState.
type ss struct {
@@ -202,7 +207,7 @@ func (s *ss) getRune() (rune int) {
rune, _, err := s.ReadRune()
if err != nil {
if err == os.EOF {
- return EOF
+ return eof
}
s.error(err)
}
@@ -214,7 +219,7 @@ func (s *ss) getRune() (rune int) {
// syntax error.
func (s *ss) mustReadRune() (rune int) {
rune = s.getRune()
- if rune == EOF {
+ if rune == eof {
s.error(io.ErrUnexpectedEOF)
}
return
@@ -238,7 +243,7 @@ func (s *ss) errorString(err string) {
panic(scanError{os.ErrorString(err)})
}
-func (s *ss) Token() (tok string, err os.Error) {
+func (s *ss) Token(skipSpace bool, f func(int) bool) (tok []byte, err os.Error) {
defer func() {
if e := recover(); e != nil {
if se, ok := e.(scanError); ok {
@@ -248,10 +253,19 @@ func (s *ss) Token() (tok string, err os.Error) {
}
}
}()
- tok = s.token()
+ if f == nil {
+ f = notSpace
+ }
+ s.buf.Reset()
+ tok = s.token(skipSpace, f)
return
}
+// notSpace is the default scanning function used in Token.
+func notSpace(r int) bool {
+ return !unicode.IsSpace(r)
+}
+
// readRune is a structure to enable reading UTF-8 encoded code points
// from an io.Reader. It is used if the Reader given to the scanner does
// not already implement io.RuneReader.
@@ -364,7 +378,7 @@ func (s *ss) free(old ssave) {
func (s *ss) skipSpace(stopAtNewline bool) {
for {
rune := s.getRune()
- if rune == EOF {
+ if rune == eof {
return
}
if rune == '\n' {
@@ -384,24 +398,27 @@ func (s *ss) skipSpace(stopAtNewline bool) {
}
}
+
// token returns the next space-delimited string from the input. It
// skips white space. For Scanln, it stops at newlines. For Scan,
// newlines are treated as spaces.
-func (s *ss) token() string {
- s.skipSpace(false)
+func (s *ss) token(skipSpace bool, f func(int) bool) []byte {
+ if skipSpace {
+ s.skipSpace(false)
+ }
// read until white space or newline
for {
rune := s.getRune()
- if rune == EOF {
+ if rune == eof {
break
}
- if unicode.IsSpace(rune) {
+ if !f(rune) {
s.UnreadRune()
break
}
s.buf.WriteRune(rune)
}
- return s.buf.String()
+ return s.buf.Bytes()
}
// typeError indicates that the type of the operand did not match the format
@@ -416,7 +433,7 @@ var boolError = os.ErrorString("syntax error scanning boolean")
// If accept is true, it puts the character into the input token.
func (s *ss) consume(ok string, accept bool) bool {
rune := s.getRune()
- if rune == EOF {
+ if rune == eof {
return false
}
if strings.IndexRune(ok, rune) >= 0 {
@@ -425,7 +442,7 @@ func (s *ss) consume(ok string, accept bool) bool {
}
return true
}
- if rune != EOF && accept {
+ if rune != eof && accept {
s.UnreadRune()
}
return false
@@ -434,7 +451,7 @@ func (s *ss) consume(ok string, accept bool) bool {
// peek reports whether the next character is in the ok string, without consuming it.
func (s *ss) peek(ok string) bool {
rune := s.getRune()
- if rune != EOF {
+ if rune != eof {
s.UnreadRune()
}
return strings.IndexRune(ok, rune) >= 0
@@ -729,7 +746,7 @@ func (s *ss) convertString(verb int) (str string) {
case 'x':
str = s.hexString()
default:
- str = s.token() // %s and %v just return the next word
+ str = string(s.token(true, notSpace)) // %s and %v just return the next word
}
// Empty strings other than with %q are not OK.
if len(str) == 0 && verb != 'q' && s.maxWid > 0 {
@@ -797,7 +814,7 @@ func (s *ss) hexDigit(digit int) int {
// There must be either two hexadecimal digits or a space character in the input.
func (s *ss) hexByte() (b byte, ok bool) {
rune1 := s.getRune()
- if rune1 == EOF {
+ if rune1 == eof {
return
}
if unicode.IsSpace(rune1) {
@@ -892,36 +909,36 @@ func (s *ss) scanOne(verb int, field interface{}) {
*v = []byte(s.convertString(verb))
default:
val := reflect.NewValue(v)
- ptr, ok := val.(*reflect.PtrValue)
- if !ok {
+ ptr := val
+ if ptr.Kind() != reflect.Ptr {
s.errorString("Scan: type not a pointer: " + val.Type().String())
return
}
- switch v := ptr.Elem().(type) {
- case *reflect.BoolValue:
- v.Set(s.scanBool(verb))
- case *reflect.IntValue:
- v.Set(s.scanInt(verb, v.Type().Bits()))
- case *reflect.UintValue:
- v.Set(s.scanUint(verb, v.Type().Bits()))
- case *reflect.StringValue:
- v.Set(s.convertString(verb))
- case *reflect.SliceValue:
+ switch v := ptr.Elem(); v.Kind() {
+ case reflect.Bool:
+ v.SetBool(s.scanBool(verb))
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ v.SetInt(s.scanInt(verb, v.Type().Bits()))
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ v.SetUint(s.scanUint(verb, v.Type().Bits()))
+ case reflect.String:
+ v.SetString(s.convertString(verb))
+ case reflect.Slice:
// For now, can only handle (renamed) []byte.
- typ := v.Type().(*reflect.SliceType)
+ typ := v.Type()
if typ.Elem().Kind() != reflect.Uint8 {
goto CantHandle
}
str := s.convertString(verb)
v.Set(reflect.MakeSlice(typ, len(str), len(str)))
for i := 0; i < len(str); i++ {
- v.Elem(i).(*reflect.UintValue).Set(uint64(str[i]))
+ v.Index(i).SetUint(uint64(str[i]))
}
- case *reflect.FloatValue:
+ case reflect.Float32, reflect.Float64:
s.skipSpace(false)
- v.Set(s.convertFloat(s.floatToken(), v.Type().Bits()))
- case *reflect.ComplexValue:
- v.Set(s.scanComplex(verb, v.Type().Bits()))
+ v.SetFloat(s.convertFloat(s.floatToken(), v.Type().Bits()))
+ case reflect.Complex64, reflect.Complex128:
+ v.SetComplex(s.scanComplex(verb, v.Type().Bits()))
default:
CantHandle:
s.errorString("Scan: can't handle type: " + val.Type().String())
@@ -953,7 +970,7 @@ func (s *ss) doScan(a []interface{}) (numProcessed int, err os.Error) {
if !s.nlIsSpace {
for {
rune := s.getRune()
- if rune == '\n' || rune == EOF {
+ if rune == '\n' || rune == eof {
break
}
if !unicode.IsSpace(rune) {
@@ -993,7 +1010,7 @@ func (s *ss) advance(format string) (i int) {
// There was space in the format, so there should be space (EOF)
// in the input.
inputc := s.getRune()
- if inputc == EOF {
+ if inputc == eof {
return
}
if !unicode.IsSpace(inputc) {
diff --git a/src/pkg/fmt/scan_test.go b/src/pkg/fmt/scan_test.go
index cab86dd98..b8b3ac975 100644
--- a/src/pkg/fmt/scan_test.go
+++ b/src/pkg/fmt/scan_test.go
@@ -88,14 +88,15 @@ type FloatTest struct {
type Xs string
func (x *Xs) Scan(state ScanState, verb int) os.Error {
- tok, err := state.Token()
+ tok, err := state.Token(true, func(r int) bool { return r == verb })
if err != nil {
return err
}
- if !regexp.MustCompile("^" + string(verb) + "+$").MatchString(tok) {
+ s := string(tok)
+ if !regexp.MustCompile("^" + string(verb) + "+$").MatchString(s) {
return os.ErrorString("syntax error for xs")
}
- *x = Xs(tok)
+ *x = Xs(s)
return nil
}
@@ -113,9 +114,11 @@ func (s *IntString) Scan(state ScanState, verb int) os.Error {
return err
}
- if _, err := Fscan(state, &s.s); err != nil {
+ tok, err := state.Token(true, nil)
+ if err != nil {
return err
}
+ s.s = string(tok)
return nil
}
@@ -331,7 +334,7 @@ var multiTests = []ScanfMultiTest{
{"%c%c%c", "2\u50c2X", args(&i, &j, &k), args('2', '\u50c2', 'X'), ""},
// Custom scanners.
- {"%2e%f", "eefffff", args(&x, &y), args(Xs("ee"), Xs("fffff")), ""},
+ {"%e%f", "eefffff", args(&x, &y), args(Xs("ee"), Xs("fffff")), ""},
{"%4v%s", "12abcd", args(&z, &s), args(IntString{12, "ab"}, "cd"), ""},
// Errors
@@ -368,7 +371,7 @@ func testScan(name string, t *testing.T, scan func(r io.Reader, a ...interface{}
}
// The incoming value may be a pointer
v := reflect.NewValue(test.in)
- if p, ok := v.(*reflect.PtrValue); ok {
+ if p := v; p.Kind() == reflect.Ptr {
v = p.Elem()
}
val := v.Interface()
@@ -407,7 +410,7 @@ func TestScanf(t *testing.T) {
}
// The incoming value may be a pointer
v := reflect.NewValue(test.in)
- if p, ok := v.(*reflect.PtrValue); ok {
+ if p := v; p.Kind() == reflect.Ptr {
v = p.Elem()
}
val := v.Interface()
@@ -483,7 +486,7 @@ func TestInf(t *testing.T) {
}
func testScanfMulti(name string, t *testing.T) {
- sliceType := reflect.Typeof(make([]interface{}, 1)).(*reflect.SliceType)
+ sliceType := reflect.Typeof(make([]interface{}, 1))
for _, test := range multiTests {
var r io.Reader
if name == "StringReader" {
@@ -510,8 +513,8 @@ func testScanfMulti(name string, t *testing.T) {
// Convert the slice of pointers into a slice of values
resultVal := reflect.MakeSlice(sliceType, n, n)
for i := 0; i < n; i++ {
- v := reflect.NewValue(test.in[i]).(*reflect.PtrValue).Elem()
- resultVal.Elem(i).(*reflect.InterfaceValue).Set(v)
+ v := reflect.NewValue(test.in[i]).Elem()
+ resultVal.Index(i).Set(v)
}
result := resultVal.Interface()
if !reflect.DeepEqual(result, test.out) {
@@ -820,12 +823,12 @@ func testScanInts(t *testing.T, scan func(*RecursiveInt, *bytes.Buffer) os.Error
i := 1
for ; r != nil; r = r.next {
if r.i != i {
- t.Fatal("bad scan: expected %d got %d", i, r.i)
+ t.Fatalf("bad scan: expected %d got %d", i, r.i)
}
i++
}
if i-1 != intCount {
- t.Fatal("bad scan count: expected %d got %d", intCount, i-1)
+ t.Fatalf("bad scan count: expected %d got %d", intCount, i-1)
}
}
diff --git a/src/pkg/go/ast/Makefile b/src/pkg/go/ast/Makefile
index e9b885c70..40be10208 100644
--- a/src/pkg/go/ast/Makefile
+++ b/src/pkg/go/ast/Makefile
@@ -9,6 +9,7 @@ GOFILES=\
ast.go\
filter.go\
print.go\
+ resolve.go\
scope.go\
walk.go\
diff --git a/src/pkg/go/ast/ast.go b/src/pkg/go/ast/ast.go
index abafb5663..ed3e2cdd9 100644
--- a/src/pkg/go/ast/ast.go
+++ b/src/pkg/go/ast/ast.go
@@ -66,7 +66,7 @@ type Decl interface {
// A Comment node represents a single //-style or /*-style comment.
type Comment struct {
Slash token.Pos // position of "/" starting the comment
- Text []byte // comment text (excluding '\n' for //-style comments)
+ Text string // comment text (excluding '\n' for //-style comments)
}
@@ -199,7 +199,7 @@ type (
BasicLit struct {
ValuePos token.Pos // literal position
Kind token.Token // token.INT, token.FLOAT, token.IMAG, token.CHAR, or token.STRING
- Value []byte // literal string; e.g. 42, 0x7f, 3.14, 1e-9, 2.4i, 'a', '\x7f', "foo" or `\m\n\o`
+ Value string // literal string; e.g. 42, 0x7f, 3.14, 1e-9, 2.4i, 'a', '\x7f', "foo" or `\m\n\o`
}
// A FuncLit node represents a function literal.
@@ -602,12 +602,12 @@ type (
Else Stmt // else branch; or nil
}
- // A CaseClause represents a case of an expression switch statement.
+ // A CaseClause represents a case of an expression or type switch statement.
CaseClause struct {
- Case token.Pos // position of "case" or "default" keyword
- Values []Expr // nil means default case
- Colon token.Pos // position of ":"
- Body []Stmt // statement list; or nil
+ Case token.Pos // position of "case" or "default" keyword
+ List []Expr // list of expressions or types; nil means default case
+ Colon token.Pos // position of ":"
+ Body []Stmt // statement list; or nil
}
// A SwitchStmt node represents an expression switch statement.
@@ -618,20 +618,12 @@ type (
Body *BlockStmt // CaseClauses only
}
- // A TypeCaseClause represents a case of a type switch statement.
- TypeCaseClause struct {
- Case token.Pos // position of "case" or "default" keyword
- Types []Expr // nil means default case
- Colon token.Pos // position of ":"
- Body []Stmt // statement list; or nil
- }
-
// An TypeSwitchStmt node represents a type switch statement.
TypeSwitchStmt struct {
Switch token.Pos // position of "switch" keyword
Init Stmt // initalization statement; or nil
- Assign Stmt // x := y.(type)
- Body *BlockStmt // TypeCaseClauses only
+ Assign Stmt // x := y.(type) or y.(type)
+ Body *BlockStmt // CaseClauses only
}
// A CommClause node represents a case of a select statement.
@@ -687,7 +679,6 @@ func (s *BlockStmt) Pos() token.Pos { return s.Lbrace }
func (s *IfStmt) Pos() token.Pos { return s.If }
func (s *CaseClause) Pos() token.Pos { return s.Case }
func (s *SwitchStmt) Pos() token.Pos { return s.Switch }
-func (s *TypeCaseClause) Pos() token.Pos { return s.Case }
func (s *TypeSwitchStmt) Pos() token.Pos { return s.Switch }
func (s *CommClause) Pos() token.Pos { return s.Case }
func (s *SelectStmt) Pos() token.Pos { return s.Select }
@@ -734,13 +725,7 @@ func (s *CaseClause) End() token.Pos {
}
return s.Colon + 1
}
-func (s *SwitchStmt) End() token.Pos { return s.Body.End() }
-func (s *TypeCaseClause) End() token.Pos {
- if n := len(s.Body); n > 0 {
- return s.Body[n-1].End()
- }
- return s.Colon + 1
-}
+func (s *SwitchStmt) End() token.Pos { return s.Body.End() }
func (s *TypeSwitchStmt) End() token.Pos { return s.Body.End() }
func (s *CommClause) End() token.Pos {
if n := len(s.Body); n > 0 {
@@ -772,7 +757,6 @@ func (s *BlockStmt) stmtNode() {}
func (s *IfStmt) stmtNode() {}
func (s *CaseClause) stmtNode() {}
func (s *SwitchStmt) stmtNode() {}
-func (s *TypeCaseClause) stmtNode() {}
func (s *TypeSwitchStmt) stmtNode() {}
func (s *CommClause) stmtNode() {}
func (s *SelectStmt) stmtNode() {}
@@ -797,7 +781,7 @@ type (
ImportSpec struct {
Doc *CommentGroup // associated documentation; or nil
Name *Ident // local package name (including "."); or nil
- Path *BasicLit // package path
+ Path *BasicLit // import path
Comment *CommentGroup // line comments; or nil
}
@@ -937,11 +921,14 @@ func (d *FuncDecl) declNode() {}
// via Doc and Comment fields.
//
type File struct {
- Doc *CommentGroup // associated documentation; or nil
- Package token.Pos // position of "package" keyword
- Name *Ident // package name
- Decls []Decl // top-level declarations; or nil
- Comments []*CommentGroup // list of all comments in the source file
+ Doc *CommentGroup // associated documentation; or nil
+ Package token.Pos // position of "package" keyword
+ Name *Ident // package name
+ Decls []Decl // top-level declarations; or nil
+ Scope *Scope // package scope (this file only)
+ Imports []*ImportSpec // imports in this file
+ Unresolved []*Ident // unresolved identifiers in this file
+ Comments []*CommentGroup // list of all comments in the source file
}
@@ -958,9 +945,10 @@ func (f *File) End() token.Pos {
// collectively building a Go package.
//
type Package struct {
- Name string // package name
- Scope *Scope // package scope; or nil
- Files map[string]*File // Go source files by filename
+ Name string // package name
+ Scope *Scope // package scope
+ Imports map[string]*Scope // map of import path -> package scope across all files
+ Files map[string]*File // Go source files by filename
}
diff --git a/src/pkg/go/ast/filter.go b/src/pkg/go/ast/filter.go
index 0c3cef4b2..090d08d34 100644
--- a/src/pkg/go/ast/filter.go
+++ b/src/pkg/go/ast/filter.go
@@ -304,7 +304,7 @@ const (
// separator is an empty //-style comment that is interspersed between
// different comment groups when they are concatenated into a single group
//
-var separator = &Comment{noPos, []byte("//")}
+var separator = &Comment{noPos, "//"}
// MergePackageFiles creates a file AST by merging the ASTs of the
@@ -425,5 +425,7 @@ func MergePackageFiles(pkg *Package, mode MergeMode) *File {
}
}
- return &File{doc, pos, NewIdent(pkg.Name), decls, comments}
+ // TODO(gri) need to compute pkgScope and unresolved identifiers!
+ // TODO(gri) need to compute imports!
+ return &File{doc, pos, NewIdent(pkg.Name), decls, nil, nil, nil, comments}
}
diff --git a/src/pkg/go/ast/print.go b/src/pkg/go/ast/print.go
index d71490d4a..e6d4e838d 100644
--- a/src/pkg/go/ast/print.go
+++ b/src/pkg/go/ast/print.go
@@ -21,24 +21,29 @@ type FieldFilter func(name string, value reflect.Value) bool
// NotNilFilter returns true for field values that are not nil;
// it returns false otherwise.
-func NotNilFilter(_ string, value reflect.Value) bool {
- v, ok := value.(interface {
- IsNil() bool
- })
- return !ok || !v.IsNil()
+func NotNilFilter(_ string, v reflect.Value) bool {
+ switch v.Kind() {
+ case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
+ return !v.IsNil()
+ }
+ return true
}
// Fprint prints the (sub-)tree starting at AST node x to w.
+// If fset != nil, position information is interpreted relative
+// to that file set. Otherwise positions are printed as integer
+// values (file set specific offsets).
//
// A non-nil FieldFilter f may be provided to control the output:
// struct fields for which f(fieldname, fieldvalue) is true are
// are printed; all others are filtered from the output.
//
-func Fprint(w io.Writer, x interface{}, f FieldFilter) (n int, err os.Error) {
+func Fprint(w io.Writer, fset *token.FileSet, x interface{}, f FieldFilter) (n int, err os.Error) {
// setup printer
p := printer{
output: w,
+ fset: fset,
filter: f,
ptrmap: make(map[interface{}]int),
last: '\n', // force printing of line number on first line
@@ -65,16 +70,17 @@ func Fprint(w io.Writer, x interface{}, f FieldFilter) (n int, err os.Error) {
// Print prints x to standard output, skipping nil fields.
-// Print(x) is the same as Fprint(os.Stdout, x, NotNilFilter).
-func Print(x interface{}) (int, os.Error) {
- return Fprint(os.Stdout, x, NotNilFilter)
+// Print(fset, x) is the same as Fprint(os.Stdout, fset, x, NotNilFilter).
+func Print(fset *token.FileSet, x interface{}) (int, os.Error) {
+ return Fprint(os.Stdout, fset, x, NotNilFilter)
}
type printer struct {
output io.Writer
+ fset *token.FileSet
filter FieldFilter
- ptrmap map[interface{}]int // *reflect.PtrValue -> line number
+ ptrmap map[interface{}]int // *T -> line number
written int // number of bytes written to output
indent int // current indentation level
last byte // the last byte processed by Write
@@ -135,73 +141,69 @@ func (p *printer) printf(format string, args ...interface{}) {
// Implementation note: Print is written for AST nodes but could be
// used to print arbitrary data structures; such a version should
// probably be in a different package.
+//
+// Note: This code detects (some) cycles created via pointers but
+// not cycles that are created via slices or maps containing the
+// same slice or map. Code for general data structures probably
+// should catch those as well.
func (p *printer) print(x reflect.Value) {
- // Note: This test is only needed because AST nodes
- // embed a token.Position, and thus all of them
- // understand the String() method (but it only
- // applies to the Position field).
- // TODO: Should reconsider this AST design decision.
- if pos, ok := x.Interface().(token.Position); ok {
- p.printf("%s", pos)
- return
- }
-
if !NotNilFilter("", x) {
p.printf("nil")
return
}
- switch v := x.(type) {
- case *reflect.InterfaceValue:
- p.print(v.Elem())
+ switch x.Kind() {
+ case reflect.Interface:
+ p.print(x.Elem())
- case *reflect.MapValue:
- p.printf("%s (len = %d) {\n", x.Type().String(), v.Len())
+ case reflect.Map:
+ p.printf("%s (len = %d) {\n", x.Type().String(), x.Len())
p.indent++
- for _, key := range v.Keys() {
+ for _, key := range x.MapKeys() {
p.print(key)
p.printf(": ")
- p.print(v.Elem(key))
+ p.print(x.MapIndex(key))
+ p.printf("\n")
}
p.indent--
p.printf("}")
- case *reflect.PtrValue:
+ case reflect.Ptr:
p.printf("*")
// type-checked ASTs may contain cycles - use ptrmap
// to keep track of objects that have been printed
// already and print the respective line number instead
- ptr := v.Interface()
+ ptr := x.Interface()
if line, exists := p.ptrmap[ptr]; exists {
p.printf("(obj @ %d)", line)
} else {
p.ptrmap[ptr] = p.line
- p.print(v.Elem())
+ p.print(x.Elem())
}
- case *reflect.SliceValue:
- if s, ok := v.Interface().([]byte); ok {
+ case reflect.Slice:
+ if s, ok := x.Interface().([]byte); ok {
p.printf("%#q", s)
return
}
- p.printf("%s (len = %d) {\n", x.Type().String(), v.Len())
+ p.printf("%s (len = %d) {\n", x.Type().String(), x.Len())
p.indent++
- for i, n := 0, v.Len(); i < n; i++ {
+ for i, n := 0, x.Len(); i < n; i++ {
p.printf("%d: ", i)
- p.print(v.Elem(i))
+ p.print(x.Index(i))
p.printf("\n")
}
p.indent--
p.printf("}")
- case *reflect.StructValue:
+ case reflect.Struct:
p.printf("%s {\n", x.Type().String())
p.indent++
- t := v.Type().(*reflect.StructType)
+ t := x.Type()
for i, n := 0, t.NumField(); i < n; i++ {
name := t.Field(i).Name
- value := v.Field(i)
+ value := x.Field(i)
if p.filter == nil || p.filter(name, value) {
p.printf("%s: ", name)
p.print(value)
@@ -212,6 +214,20 @@ func (p *printer) print(x reflect.Value) {
p.printf("}")
default:
- p.printf("%v", x.Interface())
+ v := x.Interface()
+ switch v := v.(type) {
+ case string:
+ // print strings in quotes
+ p.printf("%q", v)
+ return
+ case token.Pos:
+ // position values can be printed nicely if we have a file set
+ if p.fset != nil {
+ p.printf("%s", p.fset.Position(v))
+ return
+ }
+ }
+ // default
+ p.printf("%v", v)
}
}
diff --git a/src/pkg/go/ast/print_test.go b/src/pkg/go/ast/print_test.go
new file mode 100644
index 000000000..0820dcfce
--- /dev/null
+++ b/src/pkg/go/ast/print_test.go
@@ -0,0 +1,80 @@
+// 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 ast
+
+import (
+ "bytes"
+ "strings"
+ "testing"
+)
+
+
+var tests = []struct {
+ x interface{} // x is printed as s
+ s string
+}{
+ // basic types
+ {nil, "0 nil"},
+ {true, "0 true"},
+ {42, "0 42"},
+ {3.14, "0 3.14"},
+ {1 + 2.718i, "0 (1+2.718i)"},
+ {"foobar", "0 \"foobar\""},
+
+ // maps
+ {map[string]int{"a": 1, "b": 2},
+ `0 map[string] int (len = 2) {
+ 1 . "a": 1
+ 2 . "b": 2
+ 3 }`},
+
+ // pointers
+ {new(int), "0 *0"},
+
+ // slices
+ {[]int{1, 2, 3},
+ `0 []int (len = 3) {
+ 1 . 0: 1
+ 2 . 1: 2
+ 3 . 2: 3
+ 4 }`},
+
+ // structs
+ {struct{ x, y int }{42, 991},
+ `0 struct { x int; y int } {
+ 1 . x: 42
+ 2 . y: 991
+ 3 }`},
+}
+
+
+// Split s into lines, trim whitespace from all lines, and return
+// the concatenated non-empty lines.
+func trim(s string) string {
+ lines := strings.Split(s, "\n", -1)
+ i := 0
+ for _, line := range lines {
+ line = strings.TrimSpace(line)
+ if line != "" {
+ lines[i] = line
+ i++
+ }
+ }
+ return strings.Join(lines[0:i], "\n")
+}
+
+
+func TestPrint(t *testing.T) {
+ var buf bytes.Buffer
+ for _, test := range tests {
+ buf.Reset()
+ if _, err := Fprint(&buf, nil, test.x, nil); err != nil {
+ t.Errorf("Fprint failed: %s", err)
+ }
+ if s, ts := trim(buf.String()), trim(test.s); s != ts {
+ t.Errorf("got:\n%s\nexpected:\n%s\n", s, ts)
+ }
+ }
+}
diff --git a/src/pkg/go/ast/resolve.go b/src/pkg/go/ast/resolve.go
new file mode 100644
index 000000000..fddc3baab
--- /dev/null
+++ b/src/pkg/go/ast/resolve.go
@@ -0,0 +1,188 @@
+// 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.
+
+// This file implements NewPackage.
+
+package ast
+
+import (
+ "fmt"
+ "go/scanner"
+ "go/token"
+ "os"
+)
+
+
+type pkgBuilder struct {
+ scanner.ErrorVector
+ fset *token.FileSet
+}
+
+
+func (p *pkgBuilder) error(pos token.Pos, msg string) {
+ p.Error(p.fset.Position(pos), msg)
+}
+
+
+func (p *pkgBuilder) errorf(pos token.Pos, format string, args ...interface{}) {
+ p.error(pos, fmt.Sprintf(format, args...))
+}
+
+
+func (p *pkgBuilder) declare(scope, altScope *Scope, obj *Object) {
+ alt := scope.Insert(obj)
+ if alt == nil && altScope != nil {
+ // see if there is a conflicting declaration in altScope
+ alt = altScope.Lookup(obj.Name)
+ }
+ if alt != nil {
+ prevDecl := ""
+ if pos := alt.Pos(); pos.IsValid() {
+ prevDecl = fmt.Sprintf("\n\tprevious declaration at %s", p.fset.Position(pos))
+ }
+ p.error(obj.Pos(), fmt.Sprintf("%s redeclared in this block%s", obj.Name, prevDecl))
+ }
+}
+
+
+func resolve(scope *Scope, ident *Ident) bool {
+ for ; scope != nil; scope = scope.Outer {
+ if obj := scope.Lookup(ident.Name); obj != nil {
+ ident.Obj = obj
+ return true
+ }
+ }
+ return false
+}
+
+
+// NewPackage uses an Importer to resolve imports. Given an importPath,
+// an importer returns the imported package's name, its scope of exported
+// objects, and an error, if any.
+//
+type Importer func(path string) (name string, scope *Scope, err os.Error)
+
+
+// NewPackage creates a new Package node from a set of File nodes. It resolves
+// unresolved identifiers across files and updates each file's Unresolved list
+// accordingly. If a non-nil importer and universe scope are provided, they are
+// used to resolve identifiers not declared in any of the package files. Any
+// remaining unresolved identifiers are reported as undeclared. If the files
+// belong to different packages, one package name is selected and files with
+// different package name are reported and then ignored.
+// The result is a package node and a scanner.ErrorList if there were errors.
+//
+func NewPackage(fset *token.FileSet, files map[string]*File, importer Importer, universe *Scope) (*Package, os.Error) {
+ var p pkgBuilder
+ p.fset = fset
+
+ // complete package scope
+ pkgName := ""
+ pkgScope := NewScope(universe)
+ for _, file := range files {
+ // package names must match
+ switch name := file.Name.Name; {
+ case pkgName == "":
+ pkgName = name
+ case name != pkgName:
+ p.errorf(file.Package, "package %s; expected %s", name, pkgName)
+ continue // ignore this file
+ }
+
+ // collect top-level file objects in package scope
+ for _, obj := range file.Scope.Objects {
+ p.declare(pkgScope, nil, obj)
+ }
+ }
+
+ // imports maps import paths to package names and scopes
+ // TODO(gri): Eventually we like to get to the import scope from
+ // a package object. Then we can have a map path -> Obj.
+ type importedPkg struct {
+ name string
+ scope *Scope
+ }
+ imports := make(map[string]*importedPkg)
+
+ // complete file scopes with imports and resolve identifiers
+ for _, file := range files {
+ // ignore file if it belongs to a different package
+ // (error has already been reported)
+ if file.Name.Name != pkgName {
+ continue
+ }
+
+ // build file scope by processing all imports
+ importErrors := false
+ fileScope := NewScope(pkgScope)
+ for _, spec := range file.Imports {
+ // add import to global map of imports
+ path := string(spec.Path.Value)
+ path = path[1 : len(path)-1] // strip ""'s
+ pkg := imports[path]
+ if pkg == nil {
+ if importer == nil {
+ importErrors = true
+ continue
+ }
+ name, scope, err := importer(path)
+ if err != nil {
+ p.errorf(spec.Path.Pos(), "could not import %s (%s)", path, err)
+ importErrors = true
+ continue
+ }
+ pkg = &importedPkg{name, scope}
+ imports[path] = pkg
+ // TODO(gri) If a local package name != "." is provided,
+ // global identifier resolution could proceed even if the
+ // import failed. Consider adjusting the logic here a bit.
+ }
+ // local name overrides imported package name
+ name := pkg.name
+ if spec.Name != nil {
+ name = spec.Name.Name
+ }
+ // add import to file scope
+ if name == "." {
+ // merge imported scope with file scope
+ for _, obj := range pkg.scope.Objects {
+ p.declare(fileScope, pkgScope, obj)
+ }
+ } else {
+ // declare imported package object in file scope
+ obj := NewObj(Pkg, name)
+ obj.Decl = spec
+ p.declare(fileScope, pkgScope, obj)
+ }
+ }
+
+ // resolve identifiers
+ if importErrors {
+ // don't use the universe scope without correct imports
+ // (objects in the universe may be shadowed by imports;
+ // with missing imports identifiers might get resolved
+ // wrongly)
+ pkgScope.Outer = nil
+ }
+ i := 0
+ for _, ident := range file.Unresolved {
+ if !resolve(fileScope, ident) {
+ p.errorf(ident.Pos(), "undeclared name: %s", ident.Name)
+ file.Unresolved[i] = ident
+ i++
+ }
+
+ }
+ file.Unresolved = file.Unresolved[0:i]
+ pkgScope.Outer = universe // reset universe scope
+ }
+
+ // collect all import paths and respective package scopes
+ importedScopes := make(map[string]*Scope)
+ for path, pkg := range imports {
+ importedScopes[path] = pkg.scope
+ }
+
+ return &Package{pkgName, pkgScope, importedScopes, files}, p.GetError(scanner.Sorted)
+}
diff --git a/src/pkg/go/ast/scope.go b/src/pkg/go/ast/scope.go
index 956a208ae..830d88aef 100644
--- a/src/pkg/go/ast/scope.go
+++ b/src/pkg/go/ast/scope.go
@@ -2,31 +2,31 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// This file implements scopes, the objects they contain,
-// and object types.
+// This file implements scopes and the objects they contain.
package ast
+import (
+ "bytes"
+ "fmt"
+ "go/token"
+)
+
+
// A Scope maintains the set of named language entities declared
// in the scope and a link to the immediately surrounding (outer)
// scope.
//
type Scope struct {
Outer *Scope
- Objects []*Object // in declaration order
- // Implementation note: In some cases (struct fields,
- // function parameters) we need the source order of
- // variables. Thus for now, we store scope entries
- // in a linear list. If scopes become very large
- // (say, for packages), we may need to change this
- // to avoid slow lookups.
+ Objects map[string]*Object
}
// NewScope creates a new scope nested in the outer scope.
func NewScope(outer *Scope) *Scope {
- const n = 4 // initial scope capacity, must be > 0
- return &Scope{outer, make([]*Object, 0, n)}
+ const n = 4 // initial scope capacity
+ return &Scope{outer, make(map[string]*Object, n)}
}
@@ -34,73 +34,111 @@ func NewScope(outer *Scope) *Scope {
// found in scope s, otherwise it returns nil. Outer scopes
// are ignored.
//
-// Lookup always returns nil if name is "_", even if the scope
-// contains objects with that name.
-//
func (s *Scope) Lookup(name string) *Object {
- if name != "_" {
- for _, obj := range s.Objects {
- if obj.Name == name {
- return obj
- }
- }
- }
- return nil
+ return s.Objects[name]
}
-// Insert attempts to insert a named object into the scope s.
-// If the scope does not contain an object with that name yet
-// or if the object is named "_", Insert inserts the object
-// and returns it. Otherwise, Insert leaves the scope unchanged
-// and returns the object found in the scope instead.
+// Insert attempts to insert a named object obj into the scope s.
+// If the scope already contains an object alt with the same name,
+// Insert leaves the scope unchanged and returns alt. Otherwise
+// it inserts obj and returns nil."
//
-func (s *Scope) Insert(obj *Object) *Object {
- alt := s.Lookup(obj.Name)
- if alt == nil {
- s.append(obj)
- alt = obj
+func (s *Scope) Insert(obj *Object) (alt *Object) {
+ if alt = s.Objects[obj.Name]; alt == nil {
+ s.Objects[obj.Name] = obj
}
- return alt
+ return
}
-func (s *Scope) append(obj *Object) {
- s.Objects = append(s.Objects, obj)
+// Debugging support
+func (s *Scope) String() string {
+ var buf bytes.Buffer
+ fmt.Fprintf(&buf, "scope %p {", s)
+ if s != nil && len(s.Objects) > 0 {
+ fmt.Fprintln(&buf)
+ for _, obj := range s.Objects {
+ fmt.Fprintf(&buf, "\t%s %s\n", obj.Kind, obj.Name)
+ }
+ }
+ fmt.Fprintf(&buf, "}\n")
+ return buf.String()
}
+
// ----------------------------------------------------------------------------
// Objects
-// An Object describes a language entity such as a package,
-// constant, type, variable, or function (incl. methods).
+// An Object describes a named language entity such as a package,
+// constant, type, variable, function (incl. methods), or label.
//
type Object struct {
- Kind Kind
- Name string // declared name
- Type *Type
- Decl interface{} // corresponding Field, XxxSpec or FuncDecl
- N int // value of iota for this declaration
+ Kind ObjKind
+ Name string // declared name
+ Decl interface{} // corresponding Field, XxxSpec, FuncDecl, or LabeledStmt; or nil
+ Type interface{} // place holder for type information; may be nil
}
// NewObj creates a new object of a given kind and name.
-func NewObj(kind Kind, name string) *Object {
+func NewObj(kind ObjKind, name string) *Object {
return &Object{Kind: kind, Name: name}
}
-// Kind describes what an object represents.
-type Kind int
+// Pos computes the source position of the declaration of an object name.
+// The result may be an invalid position if it cannot be computed
+// (obj.Decl may be nil or not correct).
+func (obj *Object) Pos() token.Pos {
+ name := obj.Name
+ switch d := obj.Decl.(type) {
+ case *Field:
+ for _, n := range d.Names {
+ if n.Name == name {
+ return n.Pos()
+ }
+ }
+ case *ImportSpec:
+ if d.Name != nil && d.Name.Name == name {
+ return d.Name.Pos()
+ }
+ return d.Path.Pos()
+ case *ValueSpec:
+ for _, n := range d.Names {
+ if n.Name == name {
+ return n.Pos()
+ }
+ }
+ case *TypeSpec:
+ if d.Name.Name == name {
+ return d.Name.Pos()
+ }
+ case *FuncDecl:
+ if d.Name.Name == name {
+ return d.Name.Pos()
+ }
+ case *LabeledStmt:
+ if d.Label.Name == name {
+ return d.Label.Pos()
+ }
+ }
+ return token.NoPos
+}
+
+
+// ObKind describes what an object represents.
+type ObjKind int
// The list of possible Object kinds.
const (
- Bad Kind = iota // for error handling
- Pkg // package
- Con // constant
- Typ // type
- Var // variable
- Fun // function or method
+ Bad ObjKind = iota // for error handling
+ Pkg // package
+ Con // constant
+ Typ // type
+ Var // variable
+ Fun // function or method
+ Lbl // label
)
@@ -111,132 +149,8 @@ var objKindStrings = [...]string{
Typ: "type",
Var: "var",
Fun: "func",
+ Lbl: "label",
}
-func (kind Kind) String() string { return objKindStrings[kind] }
-
-
-// IsExported returns whether obj is exported.
-func (obj *Object) IsExported() bool { return IsExported(obj.Name) }
-
-
-// ----------------------------------------------------------------------------
-// Types
-
-// A Type represents a Go type.
-type Type struct {
- Form Form
- Obj *Object // corresponding type name, or nil
- Scope *Scope // fields and methods, always present
- N uint // basic type id, array length, number of function results, or channel direction
- Key, Elt *Type // map key and array, pointer, slice, map or channel element
- Params *Scope // function (receiver, input and result) parameters, tuple expressions (results of function calls), or nil
- Expr Expr // corresponding AST expression
-}
-
-
-// NewType creates a new type of a given form.
-func NewType(form Form) *Type {
- return &Type{Form: form, Scope: NewScope(nil)}
-}
-
-
-// Form describes the form of a type.
-type Form int
-
-// The list of possible type forms.
-const (
- BadType Form = iota // for error handling
- Unresolved // type not fully setup
- Basic
- Array
- Struct
- Pointer
- Function
- Method
- Interface
- Slice
- Map
- Channel
- Tuple
-)
-
-
-var formStrings = [...]string{
- BadType: "badType",
- Unresolved: "unresolved",
- Basic: "basic",
- Array: "array",
- Struct: "struct",
- Pointer: "pointer",
- Function: "function",
- Method: "method",
- Interface: "interface",
- Slice: "slice",
- Map: "map",
- Channel: "channel",
- Tuple: "tuple",
-}
-
-
-func (form Form) String() string { return formStrings[form] }
-
-
-// The list of basic type id's.
-const (
- Bool = iota
- Byte
- Uint
- Int
- Float
- Complex
- Uintptr
- String
-
- Uint8
- Uint16
- Uint32
- Uint64
-
- Int8
- Int16
- Int32
- Int64
-
- Float32
- Float64
-
- Complex64
- Complex128
-
- // TODO(gri) ideal types are missing
-)
-
-
-var BasicTypes = map[uint]string{
- Bool: "bool",
- Byte: "byte",
- Uint: "uint",
- Int: "int",
- Float: "float",
- Complex: "complex",
- Uintptr: "uintptr",
- String: "string",
-
- Uint8: "uint8",
- Uint16: "uint16",
- Uint32: "uint32",
- Uint64: "uint64",
-
- Int8: "int8",
- Int16: "int16",
- Int32: "int32",
- Int64: "int64",
-
- Float32: "float32",
- Float64: "float64",
-
- Complex64: "complex64",
- Complex128: "complex128",
-}
+func (kind ObjKind) String() string { return objKindStrings[kind] }
diff --git a/src/pkg/go/ast/walk.go b/src/pkg/go/ast/walk.go
index 20c337c3b..95c4b3a35 100644
--- a/src/pkg/go/ast/walk.go
+++ b/src/pkg/go/ast/walk.go
@@ -234,7 +234,7 @@ func Walk(v Visitor, node Node) {
}
case *CaseClause:
- walkExprList(v, n.Values)
+ walkExprList(v, n.List)
walkStmtList(v, n.Body)
case *SwitchStmt:
@@ -246,12 +246,6 @@ func Walk(v Visitor, node Node) {
}
Walk(v, n.Body)
- case *TypeCaseClause:
- for _, x := range n.Types {
- Walk(v, x)
- }
- walkStmtList(v, n.Body)
-
case *TypeSwitchStmt:
if n.Init != nil {
Walk(v, n.Init)
diff --git a/src/pkg/go/doc/comment.go b/src/pkg/go/doc/comment.go
index 9ff0bd536..f1ebfa97b 100644
--- a/src/pkg/go/doc/comment.go
+++ b/src/pkg/go/doc/comment.go
@@ -286,7 +286,7 @@ func unindent(block [][]byte) {
// nor to have trailing spaces at the end of lines.
// The comment markers have already been removed.
//
-// Turn each run of multiple \n into </p><p>
+// Turn each run of multiple \n into </p><p>.
// Turn each run of indented lines into a <pre> block without indent.
//
// URLs in the comment text are converted into links; if the URL also appears
diff --git a/src/pkg/go/doc/doc.go b/src/pkg/go/doc/doc.go
index e46857cb8..e7a8d3f63 100644
--- a/src/pkg/go/doc/doc.go
+++ b/src/pkg/go/doc/doc.go
@@ -66,7 +66,7 @@ func (doc *docReader) addDoc(comments *ast.CommentGroup) {
n2 := len(comments.List)
list := make([]*ast.Comment, n1+1+n2) // + 1 for separator line
copy(list, doc.doc.List)
- list[n1] = &ast.Comment{token.NoPos, []byte("//")} // separator line
+ list[n1] = &ast.Comment{token.NoPos, "//"} // separator line
copy(list[n1+1:], comments.List)
doc.doc = &ast.CommentGroup{list}
}
@@ -105,7 +105,7 @@ func baseTypeName(typ ast.Expr) string {
// if the type is not exported, the effect to
// a client is as if there were no type name
if t.IsExported() {
- return string(t.Name)
+ return t.Name
}
case *ast.StarExpr:
return baseTypeName(t.X)
@@ -300,9 +300,9 @@ func (doc *docReader) addFile(src *ast.File) {
// collect BUG(...) comments
for _, c := range src.Comments {
text := c.List[0].Text
- if m := bug_markers.FindIndex(text); m != nil {
+ if m := bug_markers.FindStringIndex(text); m != nil {
// found a BUG comment; maybe empty
- if btxt := text[m[1]:]; bug_content.Match(btxt) {
+ if btxt := text[m[1]:]; bug_content.MatchString(btxt) {
// non-empty BUG comment; collect comment without BUG prefix
list := copyCommentList(c.List)
list[0].Text = text[m[1]:]
diff --git a/src/pkg/go/parser/interface.go b/src/pkg/go/parser/interface.go
index 6f35b495e..b4780e057 100644
--- a/src/pkg/go/parser/interface.go
+++ b/src/pkg/go/parser/interface.go
@@ -69,7 +69,7 @@ func ParseExpr(fset *token.FileSet, filename string, src interface{}) (ast.Expr,
var p parser
p.init(fset, filename, data, 0)
- x := p.parseExpr()
+ x := p.parseRhs()
if p.tok == token.SEMICOLON {
p.next() // consume automatically inserted semicolon, if any
}
@@ -159,7 +159,8 @@ func ParseFiles(fset *token.FileSet, filenames []string, mode uint) (pkgs map[st
name := src.Name.Name
pkg, found := pkgs[name]
if !found {
- pkg = &ast.Package{name, nil, make(map[string]*ast.File)}
+ // TODO(gri) Use NewPackage here; reconsider ParseFiles API.
+ pkg = &ast.Package{name, nil, nil, make(map[string]*ast.File)}
pkgs[name] = pkg
}
pkg.Files[filename] = src
@@ -182,7 +183,7 @@ func ParseFiles(fset *token.FileSet, filenames []string, mode uint) (pkgs map[st
// error are returned.
//
func ParseDir(fset *token.FileSet, path string, filter func(*os.FileInfo) bool, mode uint) (map[string]*ast.Package, os.Error) {
- fd, err := os.Open(path, os.O_RDONLY, 0)
+ fd, err := os.Open(path)
if err != nil {
return nil, err
}
diff --git a/src/pkg/go/parser/parser.go b/src/pkg/go/parser/parser.go
index 7c5843f36..84a0da6ae 100644
--- a/src/pkg/go/parser/parser.go
+++ b/src/pkg/go/parser/parser.go
@@ -17,10 +17,6 @@ import (
)
-// noPos is used when there is no corresponding source position for a token.
-var noPos token.Position
-
-
// The mode parameter to the Parse* functions is a set of flags (or 0).
// They control the amount of source code parsed and other optional
// parser functionality.
@@ -30,6 +26,7 @@ const (
ImportsOnly // parsing stops after import declarations
ParseComments // parse comments and add them to AST
Trace // print a trace of parsed productions
+ DeclarationErrors // report declaration errors
)
@@ -46,16 +43,27 @@ type parser struct {
// Comments
comments []*ast.CommentGroup
- leadComment *ast.CommentGroup // the last lead comment
- lineComment *ast.CommentGroup // the last line comment
+ leadComment *ast.CommentGroup // last lead comment
+ lineComment *ast.CommentGroup // last line comment
// Next token
pos token.Pos // token position
tok token.Token // one token look-ahead
- lit []byte // token literal
+ lit string // token literal
// Non-syntactic parser control
exprLev int // < 0: in control clause, >= 0: in expression
+
+ // Ordinary identifer scopes
+ pkgScope *ast.Scope // pkgScope.Outer == nil
+ topScope *ast.Scope // top-most scope; may be pkgScope
+ unresolved []*ast.Ident // unresolved identifiers
+ imports []*ast.ImportSpec // list of imports
+
+ // Label scope
+ // (maintained by open/close LabelScope)
+ labelScope *ast.Scope // label scope for current function
+ targetStack [][]*ast.Ident // stack of unresolved labels
}
@@ -72,9 +80,133 @@ func scannerMode(mode uint) uint {
func (p *parser) init(fset *token.FileSet, filename string, src []byte, mode uint) {
p.file = fset.AddFile(filename, fset.Base(), len(src))
p.scanner.Init(p.file, src, p, scannerMode(mode))
+
p.mode = mode
p.trace = mode&Trace != 0 // for convenience (p.trace is used frequently)
+
p.next()
+
+ // set up the pkgScope here (as opposed to in parseFile) because
+ // there are other parser entry points (ParseExpr, etc.)
+ p.openScope()
+ p.pkgScope = p.topScope
+
+ // for the same reason, set up a label scope
+ p.openLabelScope()
+}
+
+
+// ----------------------------------------------------------------------------
+// Scoping support
+
+func (p *parser) openScope() {
+ p.topScope = ast.NewScope(p.topScope)
+}
+
+
+func (p *parser) closeScope() {
+ p.topScope = p.topScope.Outer
+}
+
+
+func (p *parser) openLabelScope() {
+ p.labelScope = ast.NewScope(p.labelScope)
+ p.targetStack = append(p.targetStack, nil)
+}
+
+
+func (p *parser) closeLabelScope() {
+ // resolve labels
+ n := len(p.targetStack) - 1
+ scope := p.labelScope
+ for _, ident := range p.targetStack[n] {
+ ident.Obj = scope.Lookup(ident.Name)
+ if ident.Obj == nil && p.mode&DeclarationErrors != 0 {
+ p.error(ident.Pos(), fmt.Sprintf("label %s undefined", ident.Name))
+ }
+ }
+ // pop label scope
+ p.targetStack = p.targetStack[0:n]
+ p.labelScope = p.labelScope.Outer
+}
+
+
+func (p *parser) declare(decl interface{}, scope *ast.Scope, kind ast.ObjKind, idents ...*ast.Ident) {
+ for _, ident := range idents {
+ assert(ident.Obj == nil, "identifier already declared or resolved")
+ if ident.Name != "_" {
+ obj := ast.NewObj(kind, ident.Name)
+ // remember the corresponding declaration for redeclaration
+ // errors and global variable resolution/typechecking phase
+ obj.Decl = decl
+ if alt := scope.Insert(obj); alt != nil && p.mode&DeclarationErrors != 0 {
+ prevDecl := ""
+ if pos := alt.Pos(); pos.IsValid() {
+ prevDecl = fmt.Sprintf("\n\tprevious declaration at %s", p.file.Position(pos))
+ }
+ p.error(ident.Pos(), fmt.Sprintf("%s redeclared in this block%s", ident.Name, prevDecl))
+ }
+ ident.Obj = obj
+ }
+ }
+}
+
+
+func (p *parser) shortVarDecl(idents []*ast.Ident) {
+ // Go spec: A short variable declaration may redeclare variables
+ // provided they were originally declared in the same block with
+ // the same type, and at least one of the non-blank variables is new.
+ n := 0 // number of new variables
+ for _, ident := range idents {
+ assert(ident.Obj == nil, "identifier already declared or resolved")
+ if ident.Name != "_" {
+ obj := ast.NewObj(ast.Var, ident.Name)
+ // short var declarations cannot have redeclaration errors
+ // and are not global => no need to remember the respective
+ // declaration
+ alt := p.topScope.Insert(obj)
+ if alt == nil {
+ n++ // new declaration
+ alt = obj
+ }
+ ident.Obj = alt
+ }
+ }
+ if n == 0 && p.mode&DeclarationErrors != 0 {
+ p.error(idents[0].Pos(), "no new variables on left side of :=")
+ }
+}
+
+
+// The unresolved object is a sentinel to mark identifiers that have been added
+// to the list of unresolved identifiers. The sentinel is only used for verifying
+// internal consistency.
+var unresolved = new(ast.Object)
+
+
+func (p *parser) resolve(x ast.Expr) {
+ // nothing to do if x is not an identifier or the blank identifier
+ ident, _ := x.(*ast.Ident)
+ if ident == nil {
+ return
+ }
+ assert(ident.Obj == nil, "identifier already declared or resolved")
+ if ident.Name == "_" {
+ return
+ }
+ // try to resolve the identifier
+ for s := p.topScope; s != nil; s = s.Outer {
+ if obj := s.Lookup(ident.Name); obj != nil {
+ ident.Obj = obj
+ return
+ }
+ }
+ // all local scopes are known, so any unresolved identifier
+ // must be found either in the file scope, package scope
+ // (perhaps in another file), or universe scope --- collect
+ // them so that they can be resolved later
+ ident.Obj = unresolved
+ p.unresolved = append(p.unresolved, ident)
}
@@ -120,7 +252,7 @@ func (p *parser) next0() {
s := p.tok.String()
switch {
case p.tok.IsLiteral():
- p.printTrace(s, string(p.lit))
+ p.printTrace(s, p.lit)
case p.tok.IsOperator(), p.tok.IsKeyword():
p.printTrace("\"" + s + "\"")
default:
@@ -137,8 +269,9 @@ func (p *parser) consumeComment() (comment *ast.Comment, endline int) {
// Scan the comment for '\n' chars and adjust endline accordingly.
endline = p.file.Line(p.pos)
if p.lit[1] == '*' {
- for _, b := range p.lit {
- if b == '\n' {
+ // don't use range here - no need to decode Unicode code points
+ for i := 0; i < len(p.lit); i++ {
+ if p.lit[i] == '\n' {
endline++
}
}
@@ -199,7 +332,7 @@ func (p *parser) next() {
var endline int
if p.file.Line(p.pos) == line {
- // The comment is on same line as previous token; it
+ // The comment is on same line as the previous token; it
// cannot be a lead comment but may be a line comment.
comment, endline = p.consumeCommentGroup()
if p.file.Line(p.pos) != endline {
@@ -239,7 +372,7 @@ func (p *parser) errorExpected(pos token.Pos, msg string) {
} else {
msg += ", found '" + p.tok.String() + "'"
if p.tok.IsLiteral() {
- msg += " " + string(p.lit)
+ msg += " " + p.lit
}
}
}
@@ -264,6 +397,13 @@ func (p *parser) expectSemi() {
}
+func assert(cond bool, msg string) {
+ if !cond {
+ panic("go/parser internal error: " + msg)
+ }
+}
+
+
// ----------------------------------------------------------------------------
// Identifiers
@@ -271,7 +411,7 @@ func (p *parser) parseIdent() *ast.Ident {
pos := p.pos
name := "_"
if p.tok == token.IDENT {
- name = string(p.lit)
+ name = p.lit
p.next()
} else {
p.expect(token.IDENT) // use expect() error handling
@@ -298,21 +438,51 @@ func (p *parser) parseIdentList() (list []*ast.Ident) {
// ----------------------------------------------------------------------------
// Common productions
-func (p *parser) parseExprList() (list []ast.Expr) {
+// If lhs is set, result list elements which are identifiers are not resolved.
+func (p *parser) parseExprList(lhs bool) (list []ast.Expr) {
if p.trace {
defer un(trace(p, "ExpressionList"))
}
- list = append(list, p.parseExpr())
+ list = append(list, p.parseExpr(lhs))
for p.tok == token.COMMA {
p.next()
- list = append(list, p.parseExpr())
+ list = append(list, p.parseExpr(lhs))
}
return
}
+func (p *parser) parseLhsList() []ast.Expr {
+ list := p.parseExprList(true)
+ switch p.tok {
+ case token.DEFINE:
+ // lhs of a short variable declaration
+ p.shortVarDecl(p.makeIdentList(list))
+ case token.COLON:
+ // lhs of a label declaration or a communication clause of a select
+ // statement (parseLhsList is not called when parsing the case clause
+ // of a switch statement):
+ // - labels are declared by the caller of parseLhsList
+ // - for communication clauses, if there is a stand-alone identifier
+ // followed by a colon, we have a syntax error; there is no need
+ // to resolve the identifier in that case
+ default:
+ // identifiers must be declared elsewhere
+ for _, x := range list {
+ p.resolve(x)
+ }
+ }
+ return list
+}
+
+
+func (p *parser) parseRhsList() []ast.Expr {
+ return p.parseExprList(false)
+}
+
+
// ----------------------------------------------------------------------------
// Types
@@ -334,28 +504,24 @@ func (p *parser) parseType() ast.Expr {
}
-func (p *parser) parseQualifiedIdent() ast.Expr {
+// If the result is an identifier, it is not resolved.
+func (p *parser) parseTypeName() ast.Expr {
if p.trace {
- defer un(trace(p, "QualifiedIdent"))
+ defer un(trace(p, "TypeName"))
}
- var x ast.Expr = p.parseIdent()
+ ident := p.parseIdent()
+ // don't resolve ident yet - it may be a parameter or field name
+
if p.tok == token.PERIOD {
- // first identifier is a package identifier
+ // ident is a package name
p.next()
+ p.resolve(ident)
sel := p.parseIdent()
- x = &ast.SelectorExpr{x, sel}
- }
- return x
-}
-
-
-func (p *parser) parseTypeName() ast.Expr {
- if p.trace {
- defer un(trace(p, "TypeName"))
+ return &ast.SelectorExpr{ident, sel}
}
- return p.parseQualifiedIdent()
+ return ident
}
@@ -370,7 +536,7 @@ func (p *parser) parseArrayType(ellipsisOk bool) ast.Expr {
len = &ast.Ellipsis{p.pos, nil}
p.next()
} else if p.tok != token.RBRACK {
- len = p.parseExpr()
+ len = p.parseRhs()
}
p.expect(token.RBRACK)
elt := p.parseType()
@@ -394,7 +560,7 @@ func (p *parser) makeIdentList(list []ast.Expr) []*ast.Ident {
}
-func (p *parser) parseFieldDecl() *ast.Field {
+func (p *parser) parseFieldDecl(scope *ast.Scope) *ast.Field {
if p.trace {
defer un(trace(p, "FieldDecl"))
}
@@ -419,6 +585,7 @@ func (p *parser) parseFieldDecl() *ast.Field {
} else {
// ["*"] TypeName (AnonymousField)
typ = list[0] // we always have at least one element
+ p.resolve(typ)
if n := len(list); n > 1 || !isTypeName(deref(typ)) {
pos := typ.Pos()
p.errorExpected(pos, "anonymous field")
@@ -426,9 +593,12 @@ func (p *parser) parseFieldDecl() *ast.Field {
}
}
- p.expectSemi()
+ p.expectSemi() // call before accessing p.linecomment
+
+ field := &ast.Field{doc, idents, typ, tag, p.lineComment}
+ p.declare(field, scope, ast.Var, idents...)
- return &ast.Field{doc, idents, typ, tag, p.lineComment}
+ return field
}
@@ -439,15 +609,17 @@ func (p *parser) parseStructType() *ast.StructType {
pos := p.expect(token.STRUCT)
lbrace := p.expect(token.LBRACE)
+ scope := ast.NewScope(nil) // struct scope
var list []*ast.Field
for p.tok == token.IDENT || p.tok == token.MUL || p.tok == token.LPAREN {
// a field declaration cannot start with a '(' but we accept
// it here for more robust parsing and better error messages
// (parseFieldDecl will check and complain if necessary)
- list = append(list, p.parseFieldDecl())
+ list = append(list, p.parseFieldDecl(scope))
}
rbrace := p.expect(token.RBRACE)
+ // TODO(gri): store struct scope in AST
return &ast.StructType{pos, &ast.FieldList{lbrace, list, rbrace}, false}
}
@@ -468,7 +640,7 @@ func (p *parser) tryVarType(isParam bool) ast.Expr {
if isParam && p.tok == token.ELLIPSIS {
pos := p.pos
p.next()
- typ := p.tryType() // don't use parseType so we can provide better error message
+ typ := p.tryIdentOrType(isParam) // don't use parseType so we can provide better error message
if typ == nil {
p.error(pos, "'...' parameter is missing type")
typ = &ast.BadExpr{pos, p.pos}
@@ -478,7 +650,7 @@ func (p *parser) tryVarType(isParam bool) ast.Expr {
}
return &ast.Ellipsis{pos, typ}
}
- return p.tryType()
+ return p.tryIdentOrType(false)
}
@@ -514,12 +686,15 @@ func (p *parser) parseVarList(isParam bool) (list []ast.Expr, typ ast.Expr) {
// if we had a list of identifiers, it must be followed by a type
typ = p.tryVarType(isParam)
+ if typ != nil {
+ p.resolve(typ)
+ }
return
}
-func (p *parser) parseParameterList(ellipsisOk bool) (params []*ast.Field) {
+func (p *parser) parseParameterList(scope *ast.Scope, ellipsisOk bool) (params []*ast.Field) {
if p.trace {
defer un(trace(p, "ParameterList"))
}
@@ -528,7 +703,11 @@ func (p *parser) parseParameterList(ellipsisOk bool) (params []*ast.Field) {
if typ != nil {
// IdentifierList Type
idents := p.makeIdentList(list)
- params = append(params, &ast.Field{nil, idents, typ, nil, nil})
+ field := &ast.Field{nil, idents, typ, nil, nil}
+ params = append(params, field)
+ // Go spec: The scope of an identifier denoting a function
+ // parameter or result variable is the function body.
+ p.declare(field, scope, ast.Var, idents...)
if p.tok == token.COMMA {
p.next()
}
@@ -536,7 +715,11 @@ func (p *parser) parseParameterList(ellipsisOk bool) (params []*ast.Field) {
for p.tok != token.RPAREN && p.tok != token.EOF {
idents := p.parseIdentList()
typ := p.parseVarType(ellipsisOk)
- params = append(params, &ast.Field{nil, idents, typ, nil, nil})
+ field := &ast.Field{nil, idents, typ, nil, nil}
+ params = append(params, field)
+ // Go spec: The scope of an identifier denoting a function
+ // parameter or result variable is the function body.
+ p.declare(field, scope, ast.Var, idents...)
if p.tok != token.COMMA {
break
}
@@ -547,6 +730,7 @@ func (p *parser) parseParameterList(ellipsisOk bool) (params []*ast.Field) {
// Type { "," Type } (anonymous parameters)
params = make([]*ast.Field, len(list))
for i, x := range list {
+ p.resolve(x)
params[i] = &ast.Field{Type: x}
}
}
@@ -555,7 +739,7 @@ func (p *parser) parseParameterList(ellipsisOk bool) (params []*ast.Field) {
}
-func (p *parser) parseParameters(ellipsisOk bool) *ast.FieldList {
+func (p *parser) parseParameters(scope *ast.Scope, ellipsisOk bool) *ast.FieldList {
if p.trace {
defer un(trace(p, "Parameters"))
}
@@ -563,7 +747,7 @@ func (p *parser) parseParameters(ellipsisOk bool) *ast.FieldList {
var params []*ast.Field
lparen := p.expect(token.LPAREN)
if p.tok != token.RPAREN {
- params = p.parseParameterList(ellipsisOk)
+ params = p.parseParameterList(scope, ellipsisOk)
}
rparen := p.expect(token.RPAREN)
@@ -571,13 +755,13 @@ func (p *parser) parseParameters(ellipsisOk bool) *ast.FieldList {
}
-func (p *parser) parseResult() *ast.FieldList {
+func (p *parser) parseResult(scope *ast.Scope) *ast.FieldList {
if p.trace {
defer un(trace(p, "Result"))
}
if p.tok == token.LPAREN {
- return p.parseParameters(false)
+ return p.parseParameters(scope, false)
}
typ := p.tryType()
@@ -591,31 +775,32 @@ func (p *parser) parseResult() *ast.FieldList {
}
-func (p *parser) parseSignature() (params, results *ast.FieldList) {
+func (p *parser) parseSignature(scope *ast.Scope) (params, results *ast.FieldList) {
if p.trace {
defer un(trace(p, "Signature"))
}
- params = p.parseParameters(true)
- results = p.parseResult()
+ params = p.parseParameters(scope, true)
+ results = p.parseResult(scope)
return
}
-func (p *parser) parseFuncType() *ast.FuncType {
+func (p *parser) parseFuncType() (*ast.FuncType, *ast.Scope) {
if p.trace {
defer un(trace(p, "FuncType"))
}
pos := p.expect(token.FUNC)
- params, results := p.parseSignature()
+ scope := ast.NewScope(p.topScope) // function scope
+ params, results := p.parseSignature(scope)
- return &ast.FuncType{pos, params, results}
+ return &ast.FuncType{pos, params, results}, scope
}
-func (p *parser) parseMethodSpec() *ast.Field {
+func (p *parser) parseMethodSpec(scope *ast.Scope) *ast.Field {
if p.trace {
defer un(trace(p, "MethodSpec"))
}
@@ -623,19 +808,23 @@ func (p *parser) parseMethodSpec() *ast.Field {
doc := p.leadComment
var idents []*ast.Ident
var typ ast.Expr
- x := p.parseQualifiedIdent()
+ x := p.parseTypeName()
if ident, isIdent := x.(*ast.Ident); isIdent && p.tok == token.LPAREN {
// method
idents = []*ast.Ident{ident}
- params, results := p.parseSignature()
+ scope := ast.NewScope(nil) // method scope
+ params, results := p.parseSignature(scope)
typ = &ast.FuncType{token.NoPos, params, results}
} else {
// embedded interface
typ = x
}
- p.expectSemi()
+ p.expectSemi() // call before accessing p.linecomment
- return &ast.Field{doc, idents, typ, nil, p.lineComment}
+ spec := &ast.Field{doc, idents, typ, nil, p.lineComment}
+ p.declare(spec, scope, ast.Fun, idents...)
+
+ return spec
}
@@ -646,12 +835,14 @@ func (p *parser) parseInterfaceType() *ast.InterfaceType {
pos := p.expect(token.INTERFACE)
lbrace := p.expect(token.LBRACE)
+ scope := ast.NewScope(nil) // interface scope
var list []*ast.Field
for p.tok == token.IDENT {
- list = append(list, p.parseMethodSpec())
+ list = append(list, p.parseMethodSpec(scope))
}
rbrace := p.expect(token.RBRACE)
+ // TODO(gri): store interface scope in AST
return &ast.InterfaceType{pos, &ast.FieldList{lbrace, list, rbrace}, false}
}
@@ -695,7 +886,8 @@ func (p *parser) parseChanType() *ast.ChanType {
}
-func (p *parser) tryRawType(ellipsisOk bool) ast.Expr {
+// If the result is an identifier, it is not resolved.
+func (p *parser) tryIdentOrType(ellipsisOk bool) ast.Expr {
switch p.tok {
case token.IDENT:
return p.parseTypeName()
@@ -706,7 +898,8 @@ func (p *parser) tryRawType(ellipsisOk bool) ast.Expr {
case token.MUL:
return p.parsePointerType()
case token.FUNC:
- return p.parseFuncType()
+ typ, _ := p.parseFuncType()
+ return typ
case token.INTERFACE:
return p.parseInterfaceType()
case token.MAP:
@@ -726,7 +919,13 @@ func (p *parser) tryRawType(ellipsisOk bool) ast.Expr {
}
-func (p *parser) tryType() ast.Expr { return p.tryRawType(false) }
+func (p *parser) tryType() ast.Expr {
+ typ := p.tryIdentOrType(false)
+ if typ != nil {
+ p.resolve(typ)
+ }
+ return typ
+}
// ----------------------------------------------------------------------------
@@ -745,13 +944,17 @@ func (p *parser) parseStmtList() (list []ast.Stmt) {
}
-func (p *parser) parseBody() *ast.BlockStmt {
+func (p *parser) parseBody(scope *ast.Scope) *ast.BlockStmt {
if p.trace {
defer un(trace(p, "Body"))
}
lbrace := p.expect(token.LBRACE)
+ p.topScope = scope // open function scope
+ p.openLabelScope()
list := p.parseStmtList()
+ p.closeLabelScope()
+ p.closeScope()
rbrace := p.expect(token.RBRACE)
return &ast.BlockStmt{lbrace, list, rbrace}
@@ -764,7 +967,9 @@ func (p *parser) parseBlockStmt() *ast.BlockStmt {
}
lbrace := p.expect(token.LBRACE)
+ p.openScope()
list := p.parseStmtList()
+ p.closeScope()
rbrace := p.expect(token.RBRACE)
return &ast.BlockStmt{lbrace, list, rbrace}
@@ -779,14 +984,14 @@ func (p *parser) parseFuncTypeOrLit() ast.Expr {
defer un(trace(p, "FuncTypeOrLit"))
}
- typ := p.parseFuncType()
+ typ, scope := p.parseFuncType()
if p.tok != token.LBRACE {
// function type only
return typ
}
p.exprLev++
- body := p.parseBody()
+ body := p.parseBody(scope)
p.exprLev--
return &ast.FuncLit{typ, body}
@@ -795,15 +1000,20 @@ func (p *parser) parseFuncTypeOrLit() ast.Expr {
// parseOperand may return an expression or a raw type (incl. array
// types of the form [...]T. Callers must verify the result.
+// If lhs is set and the result is an identifier, it is not resolved.
//
-func (p *parser) parseOperand() ast.Expr {
+func (p *parser) parseOperand(lhs bool) ast.Expr {
if p.trace {
defer un(trace(p, "Operand"))
}
switch p.tok {
case token.IDENT:
- return p.parseIdent()
+ x := p.parseIdent()
+ if !lhs {
+ p.resolve(x)
+ }
+ return x
case token.INT, token.FLOAT, token.IMAG, token.CHAR, token.STRING:
x := &ast.BasicLit{p.pos, p.tok, p.lit}
@@ -814,7 +1024,7 @@ func (p *parser) parseOperand() ast.Expr {
lparen := p.pos
p.next()
p.exprLev++
- x := p.parseExpr()
+ x := p.parseRhs()
p.exprLev--
rparen := p.expect(token.RPAREN)
return &ast.ParenExpr{lparen, x, rparen}
@@ -823,9 +1033,11 @@ func (p *parser) parseOperand() ast.Expr {
return p.parseFuncTypeOrLit()
default:
- t := p.tryRawType(true) // could be type for composite literal or conversion
- if t != nil {
- return t
+ if typ := p.tryIdentOrType(true); typ != nil {
+ // could be type for composite literal or conversion
+ _, isIdent := typ.(*ast.Ident)
+ assert(!isIdent, "type cannot be identifier")
+ return typ
}
}
@@ -836,19 +1048,22 @@ func (p *parser) parseOperand() ast.Expr {
}
-func (p *parser) parseSelectorOrTypeAssertion(x ast.Expr) ast.Expr {
+func (p *parser) parseSelector(x ast.Expr) ast.Expr {
if p.trace {
- defer un(trace(p, "SelectorOrTypeAssertion"))
+ defer un(trace(p, "Selector"))
}
- p.expect(token.PERIOD)
- if p.tok == token.IDENT {
- // selector
- sel := p.parseIdent()
- return &ast.SelectorExpr{x, sel}
+ sel := p.parseIdent()
+
+ return &ast.SelectorExpr{x, sel}
+}
+
+
+func (p *parser) parseTypeAssertion(x ast.Expr) ast.Expr {
+ if p.trace {
+ defer un(trace(p, "TypeAssertion"))
}
- // type assertion
p.expect(token.LPAREN)
var typ ast.Expr
if p.tok == token.TYPE {
@@ -873,13 +1088,13 @@ func (p *parser) parseIndexOrSlice(x ast.Expr) ast.Expr {
var low, high ast.Expr
isSlice := false
if p.tok != token.COLON {
- low = p.parseExpr()
+ low = p.parseRhs()
}
if p.tok == token.COLON {
isSlice = true
p.next()
if p.tok != token.RBRACK {
- high = p.parseExpr()
+ high = p.parseRhs()
}
}
p.exprLev--
@@ -902,7 +1117,7 @@ func (p *parser) parseCallOrConversion(fun ast.Expr) *ast.CallExpr {
var list []ast.Expr
var ellipsis token.Pos
for p.tok != token.RPAREN && p.tok != token.EOF && !ellipsis.IsValid() {
- list = append(list, p.parseExpr())
+ list = append(list, p.parseRhs())
if p.tok == token.ELLIPSIS {
ellipsis = p.pos
p.next()
@@ -928,12 +1143,16 @@ func (p *parser) parseElement(keyOk bool) ast.Expr {
return p.parseLiteralValue(nil)
}
- x := p.parseExpr()
- if keyOk && p.tok == token.COLON {
- colon := p.pos
- p.next()
- x = &ast.KeyValueExpr{x, colon, p.parseElement(false)}
+ x := p.parseExpr(keyOk) // don't resolve if map key
+ if keyOk {
+ if p.tok == token.COLON {
+ colon := p.pos
+ p.next()
+ return &ast.KeyValueExpr{x, colon, p.parseElement(false)}
+ }
+ p.resolve(x) // not a map key
}
+
return x
}
@@ -1085,23 +1304,47 @@ func (p *parser) checkExprOrType(x ast.Expr) ast.Expr {
}
-func (p *parser) parsePrimaryExpr() ast.Expr {
+// If lhs is set and the result is an identifier, it is not resolved.
+func (p *parser) parsePrimaryExpr(lhs bool) ast.Expr {
if p.trace {
defer un(trace(p, "PrimaryExpr"))
}
- x := p.parseOperand()
+ x := p.parseOperand(lhs)
L:
for {
switch p.tok {
case token.PERIOD:
- x = p.parseSelectorOrTypeAssertion(p.checkExpr(x))
+ p.next()
+ if lhs {
+ p.resolve(x)
+ }
+ switch p.tok {
+ case token.IDENT:
+ x = p.parseSelector(p.checkExpr(x))
+ case token.LPAREN:
+ x = p.parseTypeAssertion(p.checkExpr(x))
+ default:
+ pos := p.pos
+ p.next() // make progress
+ p.errorExpected(pos, "selector or type assertion")
+ x = &ast.BadExpr{pos, p.pos}
+ }
case token.LBRACK:
+ if lhs {
+ p.resolve(x)
+ }
x = p.parseIndexOrSlice(p.checkExpr(x))
case token.LPAREN:
+ if lhs {
+ p.resolve(x)
+ }
x = p.parseCallOrConversion(p.checkExprOrType(x))
case token.LBRACE:
if isLiteralType(x) && (p.exprLev >= 0 || !isTypeName(x)) {
+ if lhs {
+ p.resolve(x)
+ }
x = p.parseLiteralValue(x)
} else {
break L
@@ -1109,13 +1352,15 @@ L:
default:
break L
}
+ lhs = false // no need to try to resolve again
}
return x
}
-func (p *parser) parseUnaryExpr() ast.Expr {
+// If lhs is set and the result is an identifier, it is not resolved.
+func (p *parser) parseUnaryExpr(lhs bool) ast.Expr {
if p.trace {
defer un(trace(p, "UnaryExpr"))
}
@@ -1124,7 +1369,7 @@ func (p *parser) parseUnaryExpr() ast.Expr {
case token.ADD, token.SUB, token.NOT, token.XOR, token.AND, token.RANGE:
pos, op := p.pos, p.tok
p.next()
- x := p.parseUnaryExpr()
+ x := p.parseUnaryExpr(false)
return &ast.UnaryExpr{pos, op, p.checkExpr(x)}
case token.ARROW:
@@ -1137,32 +1382,37 @@ func (p *parser) parseUnaryExpr() ast.Expr {
return &ast.ChanType{pos, ast.RECV, value}
}
- x := p.parseUnaryExpr()
+ x := p.parseUnaryExpr(false)
return &ast.UnaryExpr{pos, token.ARROW, p.checkExpr(x)}
case token.MUL:
// pointer type or unary "*" expression
pos := p.pos
p.next()
- x := p.parseUnaryExpr()
+ x := p.parseUnaryExpr(false)
return &ast.StarExpr{pos, p.checkExprOrType(x)}
}
- return p.parsePrimaryExpr()
+ return p.parsePrimaryExpr(lhs)
}
-func (p *parser) parseBinaryExpr(prec1 int) ast.Expr {
+// If lhs is set and the result is an identifier, it is not resolved.
+func (p *parser) parseBinaryExpr(lhs bool, prec1 int) ast.Expr {
if p.trace {
defer un(trace(p, "BinaryExpr"))
}
- x := p.parseUnaryExpr()
+ x := p.parseUnaryExpr(lhs)
for prec := p.tok.Precedence(); prec >= prec1; prec-- {
for p.tok.Precedence() == prec {
pos, op := p.pos, p.tok
p.next()
- y := p.parseBinaryExpr(prec + 1)
+ if lhs {
+ p.resolve(x)
+ lhs = false
+ }
+ y := p.parseBinaryExpr(false, prec+1)
x = &ast.BinaryExpr{p.checkExpr(x), pos, op, p.checkExpr(y)}
}
}
@@ -1171,14 +1421,20 @@ func (p *parser) parseBinaryExpr(prec1 int) ast.Expr {
}
+// If lhs is set and the result is an identifier, it is not resolved.
// TODO(gri): parseExpr may return a type or even a raw type ([..]int) -
// should reject when a type/raw type is obviously not allowed
-func (p *parser) parseExpr() ast.Expr {
+func (p *parser) parseExpr(lhs bool) ast.Expr {
if p.trace {
defer un(trace(p, "Expression"))
}
- return p.parseBinaryExpr(token.LowestPrec + 1)
+ return p.parseBinaryExpr(lhs, token.LowestPrec+1)
+}
+
+
+func (p *parser) parseRhs() ast.Expr {
+ return p.parseExpr(false)
}
@@ -1190,7 +1446,7 @@ func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt {
defer un(trace(p, "SimpleStmt"))
}
- x := p.parseExprList()
+ x := p.parseLhsList()
switch p.tok {
case
@@ -1201,7 +1457,7 @@ func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt {
// assignment statement
pos, tok := p.pos, p.tok
p.next()
- y := p.parseExprList()
+ y := p.parseRhsList()
return &ast.AssignStmt{x, pos, tok, y}
}
@@ -1216,7 +1472,12 @@ func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt {
colon := p.pos
p.next()
if label, isIdent := x[0].(*ast.Ident); labelOk && isIdent {
- return &ast.LabeledStmt{label, colon, p.parseStmt()}
+ // Go spec: The scope of a label is the body of the function
+ // in which it is declared and excludes the body of any nested
+ // function.
+ stmt := &ast.LabeledStmt{label, colon, p.parseStmt()}
+ p.declare(stmt, p.labelScope, ast.Lbl, label)
+ return stmt
}
p.error(x[0].Pos(), "illegal label declaration")
return &ast.BadStmt{x[0].Pos(), colon + 1}
@@ -1225,7 +1486,7 @@ func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt {
// send statement
arrow := p.pos
p.next() // consume "<-"
- y := p.parseExpr()
+ y := p.parseRhs()
return &ast.SendStmt{x[0], arrow, y}
case token.INC, token.DEC:
@@ -1241,7 +1502,7 @@ func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt {
func (p *parser) parseCallExpr() *ast.CallExpr {
- x := p.parseExpr()
+ x := p.parseRhs()
if call, isCall := x.(*ast.CallExpr); isCall {
return call
}
@@ -1291,7 +1552,7 @@ func (p *parser) parseReturnStmt() *ast.ReturnStmt {
p.expect(token.RETURN)
var x []ast.Expr
if p.tok != token.SEMICOLON && p.tok != token.RBRACE {
- x = p.parseExprList()
+ x = p.parseRhsList()
}
p.expectSemi()
@@ -1304,14 +1565,17 @@ func (p *parser) parseBranchStmt(tok token.Token) *ast.BranchStmt {
defer un(trace(p, "BranchStmt"))
}
- s := &ast.BranchStmt{p.pos, tok, nil}
- p.expect(tok)
+ pos := p.expect(tok)
+ var label *ast.Ident
if tok != token.FALLTHROUGH && p.tok == token.IDENT {
- s.Label = p.parseIdent()
+ label = p.parseIdent()
+ // add to list of unresolved targets
+ n := len(p.targetStack) - 1
+ p.targetStack[n] = append(p.targetStack[n], label)
}
p.expectSemi()
- return s
+ return &ast.BranchStmt{pos, tok, label}
}
@@ -1333,6 +1597,8 @@ func (p *parser) parseIfStmt() *ast.IfStmt {
}
pos := p.expect(token.IF)
+ p.openScope()
+ defer p.closeScope()
var s ast.Stmt
var x ast.Expr
@@ -1341,12 +1607,12 @@ func (p *parser) parseIfStmt() *ast.IfStmt {
p.exprLev = -1
if p.tok == token.SEMICOLON {
p.next()
- x = p.parseExpr()
+ x = p.parseRhs()
} else {
s = p.parseSimpleStmt(false)
if p.tok == token.SEMICOLON {
p.next()
- x = p.parseExpr()
+ x = p.parseRhs()
} else {
x = p.makeExpr(s)
s = nil
@@ -1368,28 +1634,6 @@ func (p *parser) parseIfStmt() *ast.IfStmt {
}
-func (p *parser) parseCaseClause() *ast.CaseClause {
- if p.trace {
- defer un(trace(p, "CaseClause"))
- }
-
- // SwitchCase
- pos := p.pos
- var x []ast.Expr
- if p.tok == token.CASE {
- p.next()
- x = p.parseExprList()
- } else {
- p.expect(token.DEFAULT)
- }
-
- colon := p.expect(token.COLON)
- body := p.parseStmtList()
-
- return &ast.CaseClause{pos, x, colon, body}
-}
-
-
func (p *parser) parseTypeList() (list []ast.Expr) {
if p.trace {
defer un(trace(p, "TypeList"))
@@ -1405,25 +1649,30 @@ func (p *parser) parseTypeList() (list []ast.Expr) {
}
-func (p *parser) parseTypeCaseClause() *ast.TypeCaseClause {
+func (p *parser) parseCaseClause(exprSwitch bool) *ast.CaseClause {
if p.trace {
- defer un(trace(p, "TypeCaseClause"))
+ defer un(trace(p, "CaseClause"))
}
- // TypeSwitchCase
pos := p.pos
- var types []ast.Expr
+ var list []ast.Expr
if p.tok == token.CASE {
p.next()
- types = p.parseTypeList()
+ if exprSwitch {
+ list = p.parseRhsList()
+ } else {
+ list = p.parseTypeList()
+ }
} else {
p.expect(token.DEFAULT)
}
colon := p.expect(token.COLON)
+ p.openScope()
body := p.parseStmtList()
+ p.closeScope()
- return &ast.TypeCaseClause{pos, types, colon, body}
+ return &ast.CaseClause{pos, list, colon, body}
}
@@ -1447,6 +1696,8 @@ func (p *parser) parseSwitchStmt() ast.Stmt {
}
pos := p.expect(token.SWITCH)
+ p.openScope()
+ defer p.closeScope()
var s1, s2 ast.Stmt
if p.tok != token.LBRACE {
@@ -1466,28 +1717,21 @@ func (p *parser) parseSwitchStmt() ast.Stmt {
p.exprLev = prevLev
}
- if isExprSwitch(s2) {
- lbrace := p.expect(token.LBRACE)
- var list []ast.Stmt
- for p.tok == token.CASE || p.tok == token.DEFAULT {
- list = append(list, p.parseCaseClause())
- }
- rbrace := p.expect(token.RBRACE)
- body := &ast.BlockStmt{lbrace, list, rbrace}
- p.expectSemi()
- return &ast.SwitchStmt{pos, s1, p.makeExpr(s2), body}
- }
-
- // type switch
- // TODO(gri): do all the checks!
+ exprSwitch := isExprSwitch(s2)
lbrace := p.expect(token.LBRACE)
var list []ast.Stmt
for p.tok == token.CASE || p.tok == token.DEFAULT {
- list = append(list, p.parseTypeCaseClause())
+ list = append(list, p.parseCaseClause(exprSwitch))
}
rbrace := p.expect(token.RBRACE)
p.expectSemi()
body := &ast.BlockStmt{lbrace, list, rbrace}
+
+ if exprSwitch {
+ return &ast.SwitchStmt{pos, s1, p.makeExpr(s2), body}
+ }
+ // type switch
+ // TODO(gri): do all the checks!
return &ast.TypeSwitchStmt{pos, s1, s2, body}
}
@@ -1497,12 +1741,12 @@ func (p *parser) parseCommClause() *ast.CommClause {
defer un(trace(p, "CommClause"))
}
- // CommCase
+ p.openScope()
pos := p.pos
var comm ast.Stmt
if p.tok == token.CASE {
p.next()
- lhs := p.parseExprList()
+ lhs := p.parseLhsList()
if p.tok == token.ARROW {
// SendStmt
if len(lhs) > 1 {
@@ -1511,14 +1755,14 @@ func (p *parser) parseCommClause() *ast.CommClause {
}
arrow := p.pos
p.next()
- rhs := p.parseExpr()
+ rhs := p.parseRhs()
comm = &ast.SendStmt{lhs[0], arrow, rhs}
} else {
// RecvStmt
pos := p.pos
tok := p.tok
var rhs ast.Expr
- if p.tok == token.ASSIGN || p.tok == token.DEFINE {
+ if tok == token.ASSIGN || tok == token.DEFINE {
// RecvStmt with assignment
if len(lhs) > 2 {
p.errorExpected(lhs[0].Pos(), "1 or 2 expressions")
@@ -1526,7 +1770,7 @@ func (p *parser) parseCommClause() *ast.CommClause {
lhs = lhs[0:2]
}
p.next()
- rhs = p.parseExpr()
+ rhs = p.parseRhs()
} else {
// rhs must be single receive operation
if len(lhs) > 1 {
@@ -1552,6 +1796,7 @@ func (p *parser) parseCommClause() *ast.CommClause {
colon := p.expect(token.COLON)
body := p.parseStmtList()
+ p.closeScope()
return &ast.CommClause{pos, comm, colon, body}
}
@@ -1582,6 +1827,8 @@ func (p *parser) parseForStmt() ast.Stmt {
}
pos := p.expect(token.FOR)
+ p.openScope()
+ defer p.closeScope()
var s1, s2, s3 ast.Stmt
if p.tok != token.LBRACE {
@@ -1631,18 +1878,16 @@ func (p *parser) parseForStmt() ast.Stmt {
return &ast.BadStmt{pos, body.End()}
}
if rhs, isUnary := as.Rhs[0].(*ast.UnaryExpr); isUnary && rhs.Op == token.RANGE {
- // rhs is range expression; check lhs
+ // rhs is range expression
+ // (any short variable declaration was handled by parseSimpleStat above)
return &ast.RangeStmt{pos, key, value, as.TokPos, as.Tok, rhs.X, body}
- } else {
- p.errorExpected(s2.Pos(), "range clause")
- return &ast.BadStmt{pos, body.End()}
}
- } else {
- // regular for statement
- return &ast.ForStmt{pos, s1, p.makeExpr(s2), s3, body}
+ p.errorExpected(s2.Pos(), "range clause")
+ return &ast.BadStmt{pos, body.End()}
}
- panic("unreachable")
+ // regular for statement
+ return &ast.ForStmt{pos, s1, p.makeExpr(s2), s3, body}
}
@@ -1706,19 +1951,20 @@ func (p *parser) parseStmt() (s ast.Stmt) {
// ----------------------------------------------------------------------------
// Declarations
-type parseSpecFunction func(p *parser, doc *ast.CommentGroup) ast.Spec
+type parseSpecFunction func(p *parser, doc *ast.CommentGroup, iota int) ast.Spec
-func parseImportSpec(p *parser, doc *ast.CommentGroup) ast.Spec {
+func parseImportSpec(p *parser, doc *ast.CommentGroup, _ int) ast.Spec {
if p.trace {
defer un(trace(p, "ImportSpec"))
}
var ident *ast.Ident
- if p.tok == token.PERIOD {
+ switch p.tok {
+ case token.PERIOD:
ident = &ast.Ident{p.pos, ".", nil}
p.next()
- } else if p.tok == token.IDENT {
+ case token.IDENT:
ident = p.parseIdent()
}
@@ -1729,13 +1975,17 @@ func parseImportSpec(p *parser, doc *ast.CommentGroup) ast.Spec {
} else {
p.expect(token.STRING) // use expect() error handling
}
- p.expectSemi()
+ p.expectSemi() // call before accessing p.linecomment
- return &ast.ImportSpec{doc, ident, path, p.lineComment}
+ // collect imports
+ spec := &ast.ImportSpec{doc, ident, path, p.lineComment}
+ p.imports = append(p.imports, spec)
+
+ return spec
}
-func parseConstSpec(p *parser, doc *ast.CommentGroup) ast.Spec {
+func parseConstSpec(p *parser, doc *ast.CommentGroup, iota int) ast.Spec {
if p.trace {
defer un(trace(p, "ConstSpec"))
}
@@ -1743,30 +1993,46 @@ func parseConstSpec(p *parser, doc *ast.CommentGroup) ast.Spec {
idents := p.parseIdentList()
typ := p.tryType()
var values []ast.Expr
- if typ != nil || p.tok == token.ASSIGN {
+ if typ != nil || p.tok == token.ASSIGN || iota == 0 {
p.expect(token.ASSIGN)
- values = p.parseExprList()
+ values = p.parseRhsList()
}
- p.expectSemi()
+ p.expectSemi() // call before accessing p.linecomment
+
+ // Go spec: The scope of a constant or variable identifier declared inside
+ // a function begins at the end of the ConstSpec or VarSpec and ends at
+ // the end of the innermost containing block.
+ // (Global identifiers are resolved in a separate phase after parsing.)
+ spec := &ast.ValueSpec{doc, idents, typ, values, p.lineComment}
+ p.declare(spec, p.topScope, ast.Con, idents...)
- return &ast.ValueSpec{doc, idents, typ, values, p.lineComment}
+ return spec
}
-func parseTypeSpec(p *parser, doc *ast.CommentGroup) ast.Spec {
+func parseTypeSpec(p *parser, doc *ast.CommentGroup, _ int) ast.Spec {
if p.trace {
defer un(trace(p, "TypeSpec"))
}
ident := p.parseIdent()
- typ := p.parseType()
- p.expectSemi()
- return &ast.TypeSpec{doc, ident, typ, p.lineComment}
+ // Go spec: The scope of a type identifier declared inside a function begins
+ // at the identifier in the TypeSpec and ends at the end of the innermost
+ // containing block.
+ // (Global identifiers are resolved in a separate phase after parsing.)
+ spec := &ast.TypeSpec{doc, ident, nil, nil}
+ p.declare(spec, p.topScope, ast.Typ, ident)
+
+ spec.Type = p.parseType()
+ p.expectSemi() // call before accessing p.linecomment
+ spec.Comment = p.lineComment
+
+ return spec
}
-func parseVarSpec(p *parser, doc *ast.CommentGroup) ast.Spec {
+func parseVarSpec(p *parser, doc *ast.CommentGroup, _ int) ast.Spec {
if p.trace {
defer un(trace(p, "VarSpec"))
}
@@ -1776,11 +2042,18 @@ func parseVarSpec(p *parser, doc *ast.CommentGroup) ast.Spec {
var values []ast.Expr
if typ == nil || p.tok == token.ASSIGN {
p.expect(token.ASSIGN)
- values = p.parseExprList()
+ values = p.parseRhsList()
}
- p.expectSemi()
+ p.expectSemi() // call before accessing p.linecomment
+
+ // Go spec: The scope of a constant or variable identifier declared inside
+ // a function begins at the end of the ConstSpec or VarSpec and ends at
+ // the end of the innermost containing block.
+ // (Global identifiers are resolved in a separate phase after parsing.)
+ spec := &ast.ValueSpec{doc, idents, typ, values, p.lineComment}
+ p.declare(spec, p.topScope, ast.Var, idents...)
- return &ast.ValueSpec{doc, idents, typ, values, p.lineComment}
+ return spec
}
@@ -1796,26 +2069,26 @@ func (p *parser) parseGenDecl(keyword token.Token, f parseSpecFunction) *ast.Gen
if p.tok == token.LPAREN {
lparen = p.pos
p.next()
- for p.tok != token.RPAREN && p.tok != token.EOF {
- list = append(list, f(p, p.leadComment))
+ for iota := 0; p.tok != token.RPAREN && p.tok != token.EOF; iota++ {
+ list = append(list, f(p, p.leadComment, iota))
}
rparen = p.expect(token.RPAREN)
p.expectSemi()
} else {
- list = append(list, f(p, nil))
+ list = append(list, f(p, nil, 0))
}
return &ast.GenDecl{doc, pos, keyword, lparen, list, rparen}
}
-func (p *parser) parseReceiver() *ast.FieldList {
+func (p *parser) parseReceiver(scope *ast.Scope) *ast.FieldList {
if p.trace {
defer un(trace(p, "Receiver"))
}
pos := p.pos
- par := p.parseParameters(false)
+ par := p.parseParameters(scope, false)
// must have exactly one receiver
if par.NumFields() != 1 {
@@ -1844,22 +2117,37 @@ func (p *parser) parseFuncDecl() *ast.FuncDecl {
doc := p.leadComment
pos := p.expect(token.FUNC)
+ scope := ast.NewScope(p.topScope) // function scope
var recv *ast.FieldList
if p.tok == token.LPAREN {
- recv = p.parseReceiver()
+ recv = p.parseReceiver(scope)
}
ident := p.parseIdent()
- params, results := p.parseSignature()
+
+ params, results := p.parseSignature(scope)
var body *ast.BlockStmt
if p.tok == token.LBRACE {
- body = p.parseBody()
+ body = p.parseBody(scope)
}
p.expectSemi()
- return &ast.FuncDecl{doc, recv, ident, &ast.FuncType{pos, params, results}, body}
+ decl := &ast.FuncDecl{doc, recv, ident, &ast.FuncType{pos, params, results}, body}
+ if recv == nil {
+ // Go spec: The scope of an identifier denoting a constant, type,
+ // variable, or function (but not method) declared at top level
+ // (outside any function) is the package block.
+ //
+ // init() functions cannot be referred to and there may
+ // be more than one - don't put them in the pkgScope
+ if ident.Name != "init" {
+ p.declare(decl, p.pkgScope, ast.Fun, ident)
+ }
+ }
+
+ return decl
}
@@ -1918,7 +2206,12 @@ func (p *parser) parseFile() *ast.File {
// package clause
doc := p.leadComment
pos := p.expect(token.PACKAGE)
+ // Go spec: The package clause is not a declaration;
+ // the package name does not appear in any scope.
ident := p.parseIdent()
+ if ident.Name == "_" {
+ p.error(p.pos, "invalid package name _")
+ }
p.expectSemi()
var decls []ast.Decl
@@ -1940,5 +2233,20 @@ func (p *parser) parseFile() *ast.File {
}
}
- return &ast.File{doc, pos, ident, decls, p.comments}
+ assert(p.topScope == p.pkgScope, "imbalanced scopes")
+
+ // resolve global identifiers within the same file
+ i := 0
+ for _, ident := range p.unresolved {
+ // i <= index for current ident
+ assert(ident.Obj == unresolved, "object already resolved")
+ ident.Obj = p.pkgScope.Lookup(ident.Name) // also removes unresolved sentinel
+ if ident.Obj == nil {
+ p.unresolved[i] = ident
+ i++
+ }
+ }
+
+ // TODO(gri): store p.imports in AST
+ return &ast.File{doc, pos, ident, decls, p.pkgScope, p.imports, p.unresolved[0:i], p.comments}
}
diff --git a/src/pkg/go/parser/parser_test.go b/src/pkg/go/parser/parser_test.go
index 38535627a..2f1ee6bfc 100644
--- a/src/pkg/go/parser/parser_test.go
+++ b/src/pkg/go/parser/parser_test.go
@@ -21,6 +21,7 @@ var illegalInputs = []interface{}{
`package p; func f() { if /* should have condition */ {} };`,
`package p; func f() { if ; /* should have condition */ {} };`,
`package p; func f() { if f(); /* should have condition */ {} };`,
+ `package p; const c; /* should have constant value */`,
}
@@ -73,7 +74,7 @@ var validFiles = []string{
func TestParse3(t *testing.T) {
for _, filename := range validFiles {
- _, err := ParseFile(fset, filename, nil, 0)
+ _, err := ParseFile(fset, filename, nil, DeclarationErrors)
if err != nil {
t.Errorf("ParseFile(%s): %v", filename, err)
}
diff --git a/src/pkg/go/printer/nodes.go b/src/pkg/go/printer/nodes.go
index 7933c2f18..86c327930 100644
--- a/src/pkg/go/printer/nodes.go
+++ b/src/pkg/go/printer/nodes.go
@@ -108,17 +108,6 @@ func (p *printer) identList(list []*ast.Ident, indent bool, multiLine *bool) {
}
-// Compute the key size of a key:value expression.
-// Returns 0 if the expression doesn't fit onto a single line.
-func (p *printer) keySize(pair *ast.KeyValueExpr) int {
- if p.nodeSize(pair, infinity) <= infinity {
- // entire expression fits on one line - return key size
- return p.nodeSize(pair.Key, infinity)
- }
- return 0
-}
-
-
// Print a list of expressions. If the list spans multiple
// source lines, the original line breaks are respected between
// expressions. Sets multiLine to true if the list spans multiple
@@ -171,19 +160,7 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp
// the first linebreak is always a formfeed since this section must not
// depend on any previous formatting
prevBreak := -1 // index of last expression that was followed by a linebreak
- linebreakMin := 1
- if mode&periodSep != 0 {
- // Make fragments like
- //
- // a.Bar(1,
- // 2).Foo
- //
- // format correctly (a linebreak shouldn't be added before Foo) when
- // doing period-separated expr lists by setting minimum linebreak to 0
- // lines for them.
- linebreakMin = 0
- }
- if prev.IsValid() && prev.Line < line && p.linebreak(line, linebreakMin, ws, true) {
+ if prev.IsValid() && prev.Line < line && p.linebreak(line, 0, ws, true) {
ws = ignore
*multiLine = true
prevBreak = 0
@@ -204,17 +181,21 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp
// the key and the node size into the decision process
useFF := true
- // determine size
+ // determine element size: all bets are off if we don't have
+ // position information for the previous and next token (likely
+ // generated code - simply ignore the size in this case by setting
+ // it to 0)
prevSize := size
const infinity = 1e6 // larger than any source line
size = p.nodeSize(x, infinity)
pair, isPair := x.(*ast.KeyValueExpr)
- if size <= infinity {
+ if size <= infinity && prev.IsValid() && next.IsValid() {
// x fits on a single line
if isPair {
size = p.nodeSize(pair.Key, infinity) // size <= infinity
}
} else {
+ // size too large or we don't have good layout information
size = 0
}
@@ -244,8 +225,7 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp
// lines are broken using newlines so comments remain aligned
// unless forceFF is set or there are multiple expressions on
// the same line in which case formfeed is used
- // broken with a formfeed
- if p.linebreak(line, linebreakMin, ws, useFF || prevBreak+1 < i) {
+ if p.linebreak(line, 0, ws, useFF || prevBreak+1 < i) {
ws = ignore
*multiLine = true
prevBreak = i
@@ -371,11 +351,11 @@ func (p *printer) isOneLineFieldList(list []*ast.Field) bool {
func (p *printer) setLineComment(text string) {
- p.setComment(&ast.CommentGroup{[]*ast.Comment{&ast.Comment{token.NoPos, []byte(text)}}})
+ p.setComment(&ast.CommentGroup{[]*ast.Comment{&ast.Comment{token.NoPos, text}}})
}
-func (p *printer) fieldList(fields *ast.FieldList, isIncomplete bool, ctxt exprContext) {
+func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool) {
p.nesting++
defer func() {
p.nesting--
@@ -384,15 +364,15 @@ func (p *printer) fieldList(fields *ast.FieldList, isIncomplete bool, ctxt exprC
lbrace := fields.Opening
list := fields.List
rbrace := fields.Closing
+ srcIsOneLine := lbrace.IsValid() && rbrace.IsValid() && p.fset.Position(lbrace).Line == p.fset.Position(rbrace).Line
- if !isIncomplete && !p.commentBefore(p.fset.Position(rbrace)) {
+ if !isIncomplete && !p.commentBefore(p.fset.Position(rbrace)) && srcIsOneLine {
// possibly a one-line struct/interface
if len(list) == 0 {
// no blank between keyword and {} in this case
p.print(lbrace, token.LBRACE, rbrace, token.RBRACE)
return
- } else if ctxt&(compositeLit|structType) == compositeLit|structType &&
- p.isOneLineFieldList(list) { // for now ignore interfaces
+ } else if isStruct && p.isOneLineFieldList(list) { // for now ignore interfaces
// small enough - print on one line
// (don't use identList and ignore source line breaks)
p.print(lbrace, token.LBRACE, blank)
@@ -414,7 +394,7 @@ func (p *printer) fieldList(fields *ast.FieldList, isIncomplete bool, ctxt exprC
// at least one entry or incomplete
p.print(blank, lbrace, token.LBRACE, indent, formfeed)
- if ctxt&structType != 0 {
+ if isStruct {
sep := vtab
if len(list) == 1 {
@@ -497,15 +477,6 @@ func (p *printer) fieldList(fields *ast.FieldList, isIncomplete bool, ctxt exprC
// ----------------------------------------------------------------------------
// Expressions
-// exprContext describes the syntactic environment in which an expression node is printed.
-type exprContext uint
-
-const (
- compositeLit exprContext = 1 << iota
- structType
-)
-
-
func walkBinary(e *ast.BinaryExpr) (has4, has5 bool, maxProblem int) {
switch e.Op.Precedence() {
case 4:
@@ -544,7 +515,7 @@ func walkBinary(e *ast.BinaryExpr) (has4, has5 bool, maxProblem int) {
}
case *ast.StarExpr:
- if e.Op.String() == "/" {
+ if e.Op == token.QUO { // `*/`
maxProblem = 5
}
@@ -650,7 +621,7 @@ func (p *printer) binaryExpr(x *ast.BinaryExpr, prec1, cutoff, depth int, multiL
printBlank := prec < cutoff
ws := indent
- p.expr1(x.X, prec, depth+diffPrec(x.X, prec), 0, multiLine)
+ p.expr1(x.X, prec, depth+diffPrec(x.X, prec), multiLine)
if printBlank {
p.print(blank)
}
@@ -669,7 +640,7 @@ func (p *printer) binaryExpr(x *ast.BinaryExpr, prec1, cutoff, depth int, multiL
if printBlank {
p.print(blank)
}
- p.expr1(x.Y, prec+1, depth+1, 0, multiLine)
+ p.expr1(x.Y, prec+1, depth+1, multiLine)
if ws == ignore {
p.print(unindent)
}
@@ -742,7 +713,7 @@ func selectorExprList(expr ast.Expr) (list []ast.Expr) {
// Sets multiLine to true if the expression spans multiple lines.
-func (p *printer) expr1(expr ast.Expr, prec1, depth int, ctxt exprContext, multiLine *bool) {
+func (p *printer) expr1(expr ast.Expr, prec1, depth int, multiLine *bool) {
p.print(expr.Pos())
switch x := expr.(type) {
@@ -792,7 +763,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int, ctxt exprContext, multi
// TODO(gri) Remove this code if it cannot be reached.
p.print(blank)
}
- p.expr1(x.X, prec, depth, 0, multiLine)
+ p.expr1(x.X, prec, depth, multiLine)
}
case *ast.BasicLit:
@@ -818,7 +789,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int, ctxt exprContext, multi
p.exprList(token.NoPos, parts, depth, periodSep, multiLine, token.NoPos)
case *ast.TypeAssertExpr:
- p.expr1(x.X, token.HighestPrec, depth, 0, multiLine)
+ p.expr1(x.X, token.HighestPrec, depth, multiLine)
p.print(token.PERIOD, token.LPAREN)
if x.Type != nil {
p.expr(x.Type, multiLine)
@@ -829,14 +800,14 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int, ctxt exprContext, multi
case *ast.IndexExpr:
// TODO(gri): should treat[] like parentheses and undo one level of depth
- p.expr1(x.X, token.HighestPrec, 1, 0, multiLine)
+ p.expr1(x.X, token.HighestPrec, 1, multiLine)
p.print(x.Lbrack, token.LBRACK)
p.expr0(x.Index, depth+1, multiLine)
p.print(x.Rbrack, token.RBRACK)
case *ast.SliceExpr:
// TODO(gri): should treat[] like parentheses and undo one level of depth
- p.expr1(x.X, token.HighestPrec, 1, 0, multiLine)
+ p.expr1(x.X, token.HighestPrec, 1, multiLine)
p.print(x.Lbrack, token.LBRACK)
if x.Low != nil {
p.expr0(x.Low, depth+1, multiLine)
@@ -856,7 +827,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int, ctxt exprContext, multi
if len(x.Args) > 1 {
depth++
}
- p.expr1(x.Fun, token.HighestPrec, depth, 0, multiLine)
+ p.expr1(x.Fun, token.HighestPrec, depth, multiLine)
p.print(x.Lparen, token.LPAREN)
p.exprList(x.Lparen, x.Args, depth, commaSep|commaTerm, multiLine, x.Rparen)
if x.Ellipsis.IsValid() {
@@ -867,7 +838,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int, ctxt exprContext, multi
case *ast.CompositeLit:
// composite literal elements that are composite literals themselves may have the type omitted
if x.Type != nil {
- p.expr1(x.Type, token.HighestPrec, depth, compositeLit, multiLine)
+ p.expr1(x.Type, token.HighestPrec, depth, multiLine)
}
p.print(x.Lbrace, token.LBRACE)
p.exprList(x.Lbrace, x.Elts, 1, commaSep|commaTerm, multiLine, x.Rbrace)
@@ -892,7 +863,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int, ctxt exprContext, multi
case *ast.StructType:
p.print(token.STRUCT)
- p.fieldList(x.Fields, x.Incomplete, ctxt|structType)
+ p.fieldList(x.Fields, true, x.Incomplete)
case *ast.FuncType:
p.print(token.FUNC)
@@ -900,7 +871,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int, ctxt exprContext, multi
case *ast.InterfaceType:
p.print(token.INTERFACE)
- p.fieldList(x.Methods, x.Incomplete, ctxt)
+ p.fieldList(x.Methods, false, x.Incomplete)
case *ast.MapType:
p.print(token.MAP, token.LBRACK)
@@ -929,14 +900,14 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int, ctxt exprContext, multi
func (p *printer) expr0(x ast.Expr, depth int, multiLine *bool) {
- p.expr1(x, token.LowestPrec, depth, 0, multiLine)
+ p.expr1(x, token.LowestPrec, depth, multiLine)
}
// Sets multiLine to true if the expression spans multiple lines.
func (p *printer) expr(x ast.Expr, multiLine *bool) {
const depth = 1
- p.expr1(x, token.LowestPrec, depth, 0, multiLine)
+ p.expr1(x, token.LowestPrec, depth, multiLine)
}
@@ -1145,9 +1116,9 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool, multiLine *bool) {
}
case *ast.CaseClause:
- if s.Values != nil {
+ if s.List != nil {
p.print(token.CASE)
- p.exprList(s.Pos(), s.Values, 1, blankStart|commaSep, multiLine, s.Colon)
+ p.exprList(s.Pos(), s.List, 1, blankStart|commaSep, multiLine, s.Colon)
} else {
p.print(token.DEFAULT)
}
@@ -1160,16 +1131,6 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool, multiLine *bool) {
p.block(s.Body, 0)
*multiLine = true
- case *ast.TypeCaseClause:
- if s.Types != nil {
- p.print(token.CASE)
- p.exprList(s.Pos(), s.Types, 1, blankStart|commaSep, multiLine, s.Colon)
- } else {
- p.print(token.DEFAULT)
- }
- p.print(s.Colon, token.COLON)
- p.stmtList(s.Body, 1, nextIsRBrace)
-
case *ast.TypeSwitchStmt:
p.print(token.SWITCH)
if s.Init != nil {
@@ -1239,7 +1200,7 @@ func (p *printer) spec(spec ast.Spec, n int, doIndent bool, multiLine *bool) {
p.setComment(s.Doc)
if s.Name != nil {
p.expr(s.Name, multiLine)
- p.print(vtab)
+ p.print(blank)
}
p.expr(s.Path, multiLine)
p.setComment(s.Comment)
@@ -1331,13 +1292,23 @@ func (p *printer) genDecl(d *ast.GenDecl, multiLine *bool) {
// any control chars. Otherwise, the result is > maxSize.
//
func (p *printer) nodeSize(n ast.Node, maxSize int) (size int) {
+ // nodeSize invokes the printer, which may invoke nodeSize
+ // recursively. For deep composite literal nests, this can
+ // lead to an exponential algorithm. Remember previous
+ // results to prune the recursion (was issue 1628).
+ if size, found := p.nodeSizes[n]; found {
+ return size
+ }
+
size = maxSize + 1 // assume n doesn't fit
+ p.nodeSizes[n] = size
+
// nodeSize computation must be indendent of particular
// style so that we always get the same decision; print
// in RawFormat
cfg := Config{Mode: RawFormat}
var buf bytes.Buffer
- if _, err := cfg.Fprint(&buf, p.fset, n); err != nil {
+ if _, err := cfg.fprint(&buf, p.fset, n, p.nodeSizes); err != nil {
return
}
if buf.Len() <= maxSize {
@@ -1347,6 +1318,7 @@ func (p *printer) nodeSize(n ast.Node, maxSize int) (size int) {
}
}
size = buf.Len() // n fits
+ p.nodeSizes[n] = size
}
return
}
diff --git a/src/pkg/go/printer/printer.go b/src/pkg/go/printer/printer.go
index 90d9784ac..697a83fa8 100644
--- a/src/pkg/go/printer/printer.go
+++ b/src/pkg/go/printer/printer.go
@@ -34,12 +34,6 @@ const (
)
-const (
- esc2 = '\xfe' // an escape byte that cannot occur in regular UTF-8
- _ = 1 / (esc2 - tabwriter.Escape) // cause compiler error if esc2 == tabwriter.Escape
-)
-
-
var (
esc = []byte{tabwriter.Escape}
htab = []byte{'\t'}
@@ -81,8 +75,9 @@ type printer struct {
mode pmode // current printer mode
lastTok token.Token // the last token printed (token.ILLEGAL if it's whitespace)
- // Buffered whitespace
- buffer []whiteSpace
+ // Reused buffers
+ wsbuf []whiteSpace // delayed white space
+ litbuf bytes.Buffer // for creation of escaped literals and comments
// The (possibly estimated) position in the generated output;
// in AST space (i.e., pos is set whenever a token position is
@@ -94,22 +89,23 @@ type printer struct {
// written using writeItem.
last token.Position
- // HTML support
- lastTaggedLine int // last line for which a line tag was written
-
// The list of all source comments, in order of appearance.
comments []*ast.CommentGroup // may be nil
cindex int // current comment index
useNodeComments bool // if not set, ignore lead and line comments of nodes
+
+ // Cache of already computed node sizes.
+ nodeSizes map[ast.Node]int
}
-func (p *printer) init(output io.Writer, cfg *Config, fset *token.FileSet) {
+func (p *printer) init(output io.Writer, cfg *Config, fset *token.FileSet, nodeSizes map[ast.Node]int) {
p.output = output
p.Config = *cfg
p.fset = fset
p.errors = make(chan os.Error)
- p.buffer = make([]whiteSpace, 0, 16) // whitespace sequences are short
+ p.wsbuf = make([]whiteSpace, 0, 16) // whitespace sequences are short
+ p.nodeSizes = nodeSizes
}
@@ -122,6 +118,20 @@ func (p *printer) internalError(msg ...interface{}) {
}
+// escape escapes string s by bracketing it with tabwriter.Escape.
+// Escaped strings pass through tabwriter unchanged. (Note that
+// valid Go programs cannot contain tabwriter.Escape bytes since
+// they do not appear in legal UTF-8 sequences).
+//
+func (p *printer) escape(s string) string {
+ p.litbuf.Reset()
+ p.litbuf.WriteByte(tabwriter.Escape)
+ p.litbuf.WriteString(s)
+ p.litbuf.WriteByte(tabwriter.Escape)
+ return p.litbuf.String()
+}
+
+
// nlines returns the adjusted number of linebreaks given the desired number
// of breaks n such that min <= result <= max where max depends on the current
// nesting level.
@@ -229,7 +239,7 @@ func (p *printer) writeNewlines(n int, useFF bool) {
// source text. writeItem updates p.last to the position immediately following
// the data.
//
-func (p *printer) writeItem(pos token.Position, data []byte) {
+func (p *printer) writeItem(pos token.Position, data string) {
if pos.IsValid() {
// continue with previous position if we don't have a valid pos
if p.last.IsValid() && p.last.Filename != pos.Filename {
@@ -238,7 +248,7 @@ func (p *printer) writeItem(pos token.Position, data []byte) {
// e.g., the result of ast.MergePackageFiles)
p.indent = 0
p.mode = 0
- p.buffer = p.buffer[0:0]
+ p.wsbuf = p.wsbuf[0:0]
}
p.pos = pos
}
@@ -247,7 +257,7 @@ func (p *printer) writeItem(pos token.Position, data []byte) {
_, filename := filepath.Split(pos.Filename)
p.write0([]byte(fmt.Sprintf("[%s:%d:%d]", filename, pos.Line, pos.Column)))
}
- p.write(data)
+ p.write([]byte(data))
p.last = p.pos
}
@@ -279,11 +289,11 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment
if prev == nil {
// first comment of a comment group
j := 0
- for i, ch := range p.buffer {
+ for i, ch := range p.wsbuf {
switch ch {
case blank:
// ignore any blanks before a comment
- p.buffer[i] = ignore
+ p.wsbuf[i] = ignore
continue
case vtab:
// respect existing tabs - important
@@ -317,11 +327,11 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment
if prev == nil {
// first comment of a comment group
j := 0
- for i, ch := range p.buffer {
+ for i, ch := range p.wsbuf {
switch ch {
case blank, vtab:
// ignore any horizontal whitespace before line breaks
- p.buffer[i] = ignore
+ p.wsbuf[i] = ignore
continue
case indent:
// apply pending indentation
@@ -338,7 +348,7 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment
}
case newline, formfeed:
// TODO(gri): may want to keep formfeed info in some cases
- p.buffer[i] = ignore
+ p.wsbuf[i] = ignore
}
j = i
break
@@ -359,12 +369,8 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment
}
-func (p *printer) writeCommentLine(comment *ast.Comment, pos token.Position, line []byte) {
- // line must pass through unchanged, bracket it with tabwriter.Escape
- line = bytes.Join([][]byte{esc, line, esc}, nil)
- p.writeItem(pos, line)
-}
-
+// TODO(gri): It should be possible to convert the code below from using
+// []byte to string and in the process eliminate some conversions.
// Split comment text into lines
func split(text []byte) [][]byte {
@@ -545,13 +551,13 @@ func (p *printer) writeComment(comment *ast.Comment) {
// shortcut common case of //-style comments
if text[1] == '/' {
- p.writeCommentLine(comment, p.fset.Position(comment.Pos()), text)
+ p.writeItem(p.fset.Position(comment.Pos()), p.escape(text))
return
}
// for /*-style comments, print line by line and let the
// write function take care of the proper indentation
- lines := split(text)
+ lines := split([]byte(text))
stripCommonPrefix(lines)
// write comment lines, separated by formfeed,
@@ -564,7 +570,7 @@ func (p *printer) writeComment(comment *ast.Comment) {
pos = p.pos
}
if len(line) > 0 {
- p.writeCommentLine(comment, pos, line)
+ p.writeItem(pos, p.escape(string(line)))
}
}
}
@@ -577,11 +583,11 @@ func (p *printer) writeComment(comment *ast.Comment) {
// formfeed was dropped from the whitespace buffer.
//
func (p *printer) writeCommentSuffix(needsLinebreak bool) (droppedFF bool) {
- for i, ch := range p.buffer {
+ for i, ch := range p.wsbuf {
switch ch {
case blank, vtab:
// ignore trailing whitespace
- p.buffer[i] = ignore
+ p.wsbuf[i] = ignore
case indent, unindent:
// don't loose indentation information
case newline, formfeed:
@@ -593,11 +599,11 @@ func (p *printer) writeCommentSuffix(needsLinebreak bool) (droppedFF bool) {
if ch == formfeed {
droppedFF = true
}
- p.buffer[i] = ignore
+ p.wsbuf[i] = ignore
}
}
}
- p.writeWhitespace(len(p.buffer))
+ p.writeWhitespace(len(p.wsbuf))
// make sure we have a line break
if needsLinebreak {
@@ -651,7 +657,7 @@ func (p *printer) writeWhitespace(n int) {
// write entries
var data [1]byte
for i := 0; i < n; i++ {
- switch ch := p.buffer[i]; ch {
+ switch ch := p.wsbuf[i]; ch {
case ignore:
// ignore!
case indent:
@@ -669,13 +675,13 @@ func (p *printer) writeWhitespace(n int) {
// the line break and the label, the unindent is not
// part of the comment whitespace prefix and the comment
// will be positioned correctly indented.
- if i+1 < n && p.buffer[i+1] == unindent {
+ if i+1 < n && p.wsbuf[i+1] == unindent {
// Use a formfeed to terminate the current section.
// Otherwise, a long label name on the next line leading
// to a wide column may increase the indentation column
// of lines before the label; effectively leading to wrong
// indentation.
- p.buffer[i], p.buffer[i+1] = unindent, formfeed
+ p.wsbuf[i], p.wsbuf[i+1] = unindent, formfeed
i-- // do it again
continue
}
@@ -688,11 +694,11 @@ func (p *printer) writeWhitespace(n int) {
// shift remaining entries down
i := 0
- for ; n < len(p.buffer); n++ {
- p.buffer[i] = p.buffer[n]
+ for ; n < len(p.wsbuf); n++ {
+ p.wsbuf[i] = p.wsbuf[n]
i++
}
- p.buffer = p.buffer[0:i]
+ p.wsbuf = p.wsbuf[0:i]
}
@@ -733,7 +739,7 @@ func mayCombine(prev token.Token, next byte) (b bool) {
func (p *printer) print(args ...interface{}) {
for _, f := range args {
next := p.pos // estimated position of next item
- var data []byte
+ var data string
var tok token.Token
switch x := f.(type) {
@@ -747,42 +753,22 @@ func (p *printer) print(args ...interface{}) {
// LabeledStmt)
break
}
- i := len(p.buffer)
- if i == cap(p.buffer) {
+ i := len(p.wsbuf)
+ if i == cap(p.wsbuf) {
// Whitespace sequences are very short so this should
// never happen. Handle gracefully (but possibly with
// bad comment placement) if it does happen.
p.writeWhitespace(i)
i = 0
}
- p.buffer = p.buffer[0 : i+1]
- p.buffer[i] = x
+ p.wsbuf = p.wsbuf[0 : i+1]
+ p.wsbuf[i] = x
case *ast.Ident:
- data = []byte(x.Name)
+ data = x.Name
tok = token.IDENT
case *ast.BasicLit:
- // escape all literals so they pass through unchanged
- // (note that valid Go programs cannot contain
- // tabwriter.Escape bytes since they do not appear in
- // legal UTF-8 sequences)
- data = make([]byte, 0, len(x.Value)+2)
- data = append(data, tabwriter.Escape)
- data = append(data, x.Value...)
- data = append(data, tabwriter.Escape)
+ data = p.escape(x.Value)
tok = x.Kind
- // If we have a raw string that spans multiple lines and
- // the opening quote (`) is on a line preceded only by
- // indentation, we don't want to write that indentation
- // because the following lines of the raw string are not
- // indented. It's easiest to correct the output at the end
- // via the trimmer (because of the complex handling of
- // white space).
- // Mark multi-line raw strings by replacing the opening
- // quote with esc2 and have the trimmer take care of fixing
- // it up. (Do this _after_ making a copy of data!)
- if data[1] == '`' && bytes.IndexByte(data, '\n') > 0 {
- data[1] = esc2
- }
case token.Token:
s := x.String()
if mayCombine(p.lastTok, s[0]) {
@@ -792,13 +778,13 @@ func (p *printer) print(args ...interface{}) {
// (except for token.INT followed by a '.' this
// should never happen because it is taken care
// of via binary expression formatting)
- if len(p.buffer) != 0 {
+ if len(p.wsbuf) != 0 {
p.internalError("whitespace buffer not empty")
}
- p.buffer = p.buffer[0:1]
- p.buffer[0] = ' '
+ p.wsbuf = p.wsbuf[0:1]
+ p.wsbuf[0] = ' '
}
- data = []byte(s)
+ data = s
tok = x
case token.Pos:
if x.IsValid() {
@@ -812,7 +798,7 @@ func (p *printer) print(args ...interface{}) {
p.lastTok = tok
p.pos = next
- if data != nil {
+ if data != "" {
droppedFF := p.flush(next, tok)
// intersperse extra newlines if present in the source
@@ -847,7 +833,7 @@ func (p *printer) flush(next token.Position, tok token.Token) (droppedFF bool) {
droppedFF = p.intersperseComments(next, tok)
} else {
// otherwise, write any leftover whitespace
- p.writeWhitespace(len(p.buffer))
+ p.writeWhitespace(len(p.wsbuf))
}
return
}
@@ -863,10 +849,9 @@ func (p *printer) flush(next token.Position, tok token.Token) (droppedFF bool) {
// through unchanged.
//
type trimmer struct {
- output io.Writer
- state int
- space bytes.Buffer
- hasText bool
+ output io.Writer
+ state int
+ space bytes.Buffer
}
@@ -874,15 +859,11 @@ type trimmer struct {
// It can be in one of the following states:
const (
inSpace = iota // inside space
- atEscape // inside space and the last char was an opening tabwriter.Escape
inEscape // inside text bracketed by tabwriter.Escapes
inText // inside text
)
-var backquote = []byte{'`'}
-
-
// Design note: It is tempting to eliminate extra blanks occurring in
// whitespace in this function as it could simplify some
// of the blanks logic in the node printing functions.
@@ -891,9 +872,8 @@ var backquote = []byte{'`'}
func (p *trimmer) Write(data []byte) (n int, err os.Error) {
// invariants:
- // p.state == inSpace, atEscape:
+ // p.state == inSpace:
// p.space is unwritten
- // p.hasText indicates if there is any text on this line
// p.state == inEscape, inText:
// data[m:n] is unwritten
m := 0
@@ -910,32 +890,20 @@ func (p *trimmer) Write(data []byte) (n int, err os.Error) {
case '\n', '\f':
p.space.Reset() // discard trailing space
_, err = p.output.Write(newlines[0:1]) // write newline
- p.hasText = false
case tabwriter.Escape:
- p.state = atEscape
+ _, err = p.output.Write(p.space.Bytes())
+ p.state = inEscape
+ m = n + 1 // +1: skip tabwriter.Escape
default:
_, err = p.output.Write(p.space.Bytes())
p.state = inText
m = n
}
- case atEscape:
- // discard indentation if we have a multi-line raw string
- // (see printer.print for details)
- if b != esc2 || p.hasText {
- _, err = p.output.Write(p.space.Bytes())
- }
- p.state = inEscape
- m = n
- if b == esc2 {
- _, err = p.output.Write(backquote) // convert back
- m++
- }
case inEscape:
if b == tabwriter.Escape {
_, err = p.output.Write(data[m:n])
p.state = inSpace
p.space.Reset()
- p.hasText = true
}
case inText:
switch b {
@@ -944,19 +912,18 @@ func (p *trimmer) Write(data []byte) (n int, err os.Error) {
p.state = inSpace
p.space.Reset()
p.space.WriteByte(b) // WriteByte returns no errors
- p.hasText = true
case '\n', '\f':
_, err = p.output.Write(data[m:n])
p.state = inSpace
p.space.Reset()
_, err = p.output.Write(newlines[0:1]) // write newline
- p.hasText = false
case tabwriter.Escape:
_, err = p.output.Write(data[m:n])
- p.state = atEscape
- p.space.Reset()
- p.hasText = true
+ p.state = inEscape
+ m = n + 1 // +1: skip tabwriter.Escape
}
+ default:
+ panic("unreachable")
}
if err != nil {
return
@@ -969,7 +936,6 @@ func (p *trimmer) Write(data []byte) (n int, err os.Error) {
_, err = p.output.Write(data[m:n])
p.state = inSpace
p.space.Reset()
- p.hasText = true
}
return
@@ -994,13 +960,8 @@ type Config struct {
}
-// Fprint "pretty-prints" an AST node to output and returns the number
-// of bytes written and an error (if any) for a given configuration cfg.
-// Position information is interpreted relative to the file set fset.
-// The node type must be *ast.File, or assignment-compatible to ast.Expr,
-// ast.Decl, ast.Spec, or ast.Stmt.
-//
-func (cfg *Config) Fprint(output io.Writer, fset *token.FileSet, node interface{}) (int, os.Error) {
+// fprint implements Fprint and takes a nodesSizes map for setting up the printer state.
+func (cfg *Config) fprint(output io.Writer, fset *token.FileSet, node interface{}, nodeSizes map[ast.Node]int) (int, os.Error) {
// redirect output through a trimmer to eliminate trailing whitespace
// (Input to a tabwriter must be untrimmed since trailing tabs provide
// formatting information. The tabwriter could provide trimming
@@ -1029,7 +990,7 @@ func (cfg *Config) Fprint(output io.Writer, fset *token.FileSet, node interface{
// setup printer and print node
var p printer
- p.init(output, cfg, fset)
+ p.init(output, cfg, fset, nodeSizes)
go func() {
switch n := node.(type) {
case ast.Expr:
@@ -1076,6 +1037,17 @@ func (cfg *Config) Fprint(output io.Writer, fset *token.FileSet, node interface{
}
+// Fprint "pretty-prints" an AST node to output and returns the number
+// of bytes written and an error (if any) for a given configuration cfg.
+// Position information is interpreted relative to the file set fset.
+// The node type must be *ast.File, or assignment-compatible to ast.Expr,
+// ast.Decl, ast.Spec, or ast.Stmt.
+//
+func (cfg *Config) Fprint(output io.Writer, fset *token.FileSet, node interface{}) (int, os.Error) {
+ return cfg.fprint(output, fset, node, make(map[ast.Node]int))
+}
+
+
// Fprint "pretty-prints" an AST node to output.
// It calls Config.Fprint with default settings.
//
diff --git a/src/pkg/go/printer/printer_test.go b/src/pkg/go/printer/printer_test.go
index 62b726913..090f92af1 100644
--- a/src/pkg/go/printer/printer_test.go
+++ b/src/pkg/go/printer/printer_test.go
@@ -13,6 +13,7 @@ import (
"go/token"
"path/filepath"
"testing"
+ "time"
)
@@ -45,7 +46,7 @@ const (
)
-func check(t *testing.T, source, golden string, mode checkMode) {
+func runcheck(t *testing.T, source, golden string, mode checkMode) {
// parse source
prog, err := parser.ParseFile(fset, source, nil, parser.ParseComments)
if err != nil {
@@ -109,6 +110,32 @@ func check(t *testing.T, source, golden string, mode checkMode) {
}
+func check(t *testing.T, source, golden string, mode checkMode) {
+ // start a timer to produce a time-out signal
+ tc := make(chan int)
+ go func() {
+ time.Sleep(10e9) // plenty of a safety margin, even for very slow machines
+ tc <- 0
+ }()
+
+ // run the test
+ cc := make(chan int)
+ go func() {
+ runcheck(t, source, golden, mode)
+ cc <- 0
+ }()
+
+ // wait for the first finisher
+ select {
+ case <-tc:
+ // test running past time out
+ t.Errorf("%s: running too slowly", source)
+ case <-cc:
+ // test finished within alloted time margin
+ }
+}
+
+
type entry struct {
source, golden string
mode checkMode
@@ -124,16 +151,20 @@ var data = []entry{
{"expressions.input", "expressions.raw", rawFormat},
{"declarations.input", "declarations.golden", 0},
{"statements.input", "statements.golden", 0},
+ {"slow.input", "slow.golden", 0},
}
func TestFiles(t *testing.T) {
- for _, e := range data {
+ for i, e := range data {
source := filepath.Join(dataDir, e.source)
golden := filepath.Join(dataDir, e.golden)
check(t, source, golden, e.mode)
// TODO(gri) check that golden is idempotent
- //check(t, golden, golden, e.mode);
+ //check(t, golden, golden, e.mode)
+ if testing.Short() && i >= 3 {
+ break
+ }
}
}
diff --git a/src/pkg/go/printer/testdata/declarations.golden b/src/pkg/go/printer/testdata/declarations.golden
index 1c091b929..c1b255842 100644
--- a/src/pkg/go/printer/testdata/declarations.golden
+++ b/src/pkg/go/printer/testdata/declarations.golden
@@ -7,10 +7,10 @@ package imports
import "io"
import (
- _ "io"
+ _ "io"
)
-import _ "io"
+import _ "io"
import (
"io"
@@ -20,40 +20,40 @@ import (
import (
"io"
- aLongRename "io"
+ aLongRename "io"
- b "io"
+ b "io"
)
import (
"unrenamed"
- renamed "renameMe"
- . "io"
- _ "io"
+ renamed "renameMe"
+ . "io"
+ _ "io"
"io"
- . "os"
+ . "os"
)
// no newlines between consecutive single imports, but
// respect extra line breaks in the source (at most one empty line)
-import _ "io"
-import _ "io"
-import _ "io"
+import _ "io"
+import _ "io"
+import _ "io"
-import _ "os"
-import _ "os"
-import _ "os"
+import _ "os"
+import _ "os"
+import _ "os"
-import _ "fmt"
-import _ "fmt"
-import _ "fmt"
+import _ "fmt"
+import _ "fmt"
+import _ "fmt"
import "foo" // a comment
import "bar" // a comment
import (
- _ "foo"
+ _ "foo"
// a comment
"bar"
"foo" // a comment
@@ -63,17 +63,17 @@ import (
// comments + renames
import (
"unrenamed" // a comment
- renamed "renameMe"
- . "io" /* a comment */
- _ "io/ioutil" // a comment
+ renamed "renameMe"
+ . "io" /* a comment */
+ _ "io/ioutil" // a comment
"io" // testing alignment
- . "os"
+ . "os"
// a comment
)
// a case that caused problems in the past (comment placement)
import (
- . "fmt"
+ . "fmt"
"io"
"malloc" // for the malloc count test only
"math"
@@ -81,9 +81,38 @@ import (
"testing"
)
+// more import examples
+import (
+ "xxx"
+ "much longer name" // comment
+ "short name" // comment
+)
+
+import (
+ _ "xxx"
+ "much longer name" // comment
+)
+
+import (
+ mymath "math"
+ "/foo/bar/long_package_path" // a comment
+)
+
+import (
+ "package_a" // comment
+ "package_b"
+ my_better_c "package_c" // comment
+ "package_d" // comment
+ my_e "package_e" // comment
+
+ "package_a" // comment
+ "package_bb"
+ "package_ccc" // comment
+ "package_dddd" // comment
+)
// at least one empty line between declarations of different kind
-import _ "io"
+import _ "io"
var _ int
diff --git a/src/pkg/go/printer/testdata/declarations.input b/src/pkg/go/printer/testdata/declarations.input
index c826462f9..c8b37e12b 100644
--- a/src/pkg/go/printer/testdata/declarations.input
+++ b/src/pkg/go/printer/testdata/declarations.input
@@ -81,6 +81,35 @@ import (
"testing"
)
+// more import examples
+import (
+ "xxx"
+ "much longer name" // comment
+ "short name" // comment
+)
+
+import (
+ _ "xxx"
+ "much longer name" // comment
+)
+
+import (
+ mymath "math"
+ "/foo/bar/long_package_path" // a comment
+)
+
+import (
+ "package_a" // comment
+ "package_b"
+ my_better_c "package_c" // comment
+ "package_d" // comment
+ my_e "package_e" // comment
+
+ "package_a" // comment
+ "package_bb"
+ "package_ccc" // comment
+ "package_dddd" // comment
+)
// at least one empty line between declarations of different kind
import _ "io"
diff --git a/src/pkg/go/printer/testdata/expressions.golden b/src/pkg/go/printer/testdata/expressions.golden
index 7f18f338a..c1a7e970b 100644
--- a/src/pkg/go/printer/testdata/expressions.golden
+++ b/src/pkg/go/printer/testdata/expressions.golden
@@ -224,11 +224,7 @@ func _() {
_ = struct{ x int }{0}
_ = struct{ x, y, z int }{0, 1, 2}
_ = struct{ int }{0}
- _ = struct {
- s struct {
- int
- }
- }{struct{ int }{0}} // compositeLit context not propagated => multiLine result
+ _ = struct{ s struct{ int } }{struct{ int }{0}}
}
@@ -257,8 +253,8 @@ bar`
var _ = ``
var _ = `foo`
var _ =
- // the next line should not be indented
-`foo
+ // the next line should remain indented
+ `foo
bar`
var _ = // comment
@@ -266,8 +262,8 @@ bar`
var _ = // comment
`foo`
var _ = // comment
- // the next line should not be indented
-`foo
+ // the next line should remain indented
+ `foo
bar`
var _ = /* comment */ ``
@@ -280,12 +276,12 @@ bar`
var _ = /* comment */
`foo`
var _ = /* comment */
- // the next line should not be indented
-`foo
+ // the next line should remain indented
+ `foo
bar`
var board = []int(
-`...........
+ `...........
...........
....●●●....
....●●●....
@@ -300,8 +296,8 @@ bar`
var state = S{
"foo",
- // the next line should not be indented
-`...........
+ // the next line should remain indented
+ `...........
...........
....●●●....
....●●●....
@@ -623,3 +619,13 @@ func _() {
b.(T).
c
}
+
+
+// Don't introduce extra newlines in strangely formatted expression lists.
+func f() {
+ // os.Open parameters should remain on two lines
+ if writer, err = os.Open(outfile, s.O_WRONLY|os.O_CREATE|
+ os.O_TRUNC,0666); err != nil {
+ log.Fatal(err)
+ }
+}
diff --git a/src/pkg/go/printer/testdata/expressions.input b/src/pkg/go/printer/testdata/expressions.input
index 6bcd9b5f8..b87381198 100644
--- a/src/pkg/go/printer/testdata/expressions.input
+++ b/src/pkg/go/printer/testdata/expressions.input
@@ -224,7 +224,7 @@ func _() {
_ = struct{ x int }{0}
_ = struct{ x, y, z int }{0, 1, 2}
_ = struct{ int }{0}
- _ = struct{ s struct { int } }{struct{ int}{0}} // compositeLit context not propagated => multiLine result
+ _ = struct{ s struct { int } }{struct{ int}{0} }
}
@@ -256,7 +256,7 @@ var _ =
var _ =
`foo`
var _ =
- // the next line should not be indented
+ // the next line should remain indented
`foo
bar`
@@ -266,7 +266,7 @@ bar`
var _ = // comment
`foo`
var _ = // comment
- // the next line should not be indented
+ // the next line should remain indented
`foo
bar`
@@ -282,7 +282,7 @@ bar`
var _ = /* comment */
`foo`
var _ = /* comment */
- // the next line should not be indented
+ // the next line should remain indented
`foo
bar`
@@ -304,7 +304,7 @@ var board = []int(
var state = S{
"foo",
- // the next line should not be indented
+ // the next line should remain indented
`...........
...........
....●●●....
@@ -625,3 +625,13 @@ baz()
(T).
c
}
+
+
+// Don't introduce extra newlines in strangely formatted expression lists.
+func f() {
+ // os.Open parameters should remain on two lines
+ if writer, err = os.Open(outfile, s.O_WRONLY|os.O_CREATE|
+ os.O_TRUNC, 0666); err != nil {
+ log.Fatal(err)
+ }
+}
diff --git a/src/pkg/go/printer/testdata/expressions.raw b/src/pkg/go/printer/testdata/expressions.raw
index f1944c94b..735cd943e 100644
--- a/src/pkg/go/printer/testdata/expressions.raw
+++ b/src/pkg/go/printer/testdata/expressions.raw
@@ -224,11 +224,7 @@ func _() {
_ = struct{ x int }{0}
_ = struct{ x, y, z int }{0, 1, 2}
_ = struct{ int }{0}
- _ = struct {
- s struct {
- int
- }
- }{struct{ int }{0}} // compositeLit context not propagated => multiLine result
+ _ = struct{ s struct{ int } }{struct{ int }{0}}
}
@@ -243,7 +239,8 @@ func _() {
_ = `foo
bar`
_ = `three spaces before the end of the line starting here:
-they must not be removed` }
+they must not be removed`
+}
func _() {
@@ -256,8 +253,8 @@ bar`
var _ = ``
var _ = `foo`
var _ =
- // the next line should not be indented
-`foo
+ // the next line should remain indented
+ `foo
bar`
var _ = // comment
@@ -265,8 +262,8 @@ bar`
var _ = // comment
`foo`
var _ = // comment
- // the next line should not be indented
-`foo
+ // the next line should remain indented
+ `foo
bar`
var _ = /* comment */ ``
@@ -279,12 +276,12 @@ bar`
var _ = /* comment */
`foo`
var _ = /* comment */
- // the next line should not be indented
-`foo
+ // the next line should remain indented
+ `foo
bar`
var board = []int(
-`...........
+ `...........
...........
....●●●....
....●●●....
@@ -299,8 +296,8 @@ bar`
var state = S{
"foo",
- // the next line should not be indented
-`...........
+ // the next line should remain indented
+ `...........
...........
....●●●....
....●●●....
@@ -622,3 +619,13 @@ func _() {
b.(T).
c
}
+
+
+// Don't introduce extra newlines in strangely formatted expression lists.
+func f() {
+ // os.Open parameters should remain on two lines
+ if writer, err = os.Open(outfile, s.O_WRONLY|os.O_CREATE|
+ os.O_TRUNC,0666); err != nil {
+ log.Fatal(err)
+ }
+}
diff --git a/src/pkg/go/printer/testdata/slow.golden b/src/pkg/go/printer/testdata/slow.golden
new file mode 100644
index 000000000..43a15cb1d
--- /dev/null
+++ b/src/pkg/go/printer/testdata/slow.golden
@@ -0,0 +1,85 @@
+// 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 deepequal_test
+
+import (
+ "testing"
+ "google3/spam/archer/frontend/deepequal"
+)
+
+func TestTwoNilValues(t *testing.T) {
+ if err := deepequal.Check(nil, nil); err != nil {
+ t.Errorf("expected nil, saw %v", err)
+ }
+}
+
+type Foo struct {
+ bar *Bar
+ bang *Bar
+}
+
+type Bar struct {
+ baz *Baz
+ foo []*Foo
+}
+
+type Baz struct {
+ entries map[int]interface{}
+ whatever string
+}
+
+func newFoo() *Foo {
+ return &Foo{bar: &Bar{baz: &Baz{
+ entries: map[int]interface{}{
+ 42: &Foo{},
+ 21: &Bar{},
+ 11: &Baz{whatever: "it's just a test"}}}},
+ bang: &Bar{foo: []*Foo{
+ &Foo{bar: &Bar{baz: &Baz{
+ entries: map[int]interface{}{
+ 43: &Foo{},
+ 22: &Bar{},
+ 13: &Baz{whatever: "this is nuts"}}}},
+ bang: &Bar{foo: []*Foo{
+ &Foo{bar: &Bar{baz: &Baz{
+ entries: map[int]interface{}{
+ 61: &Foo{},
+ 71: &Bar{},
+ 11: &Baz{whatever: "no, it's Go"}}}},
+ bang: &Bar{foo: []*Foo{
+ &Foo{bar: &Bar{baz: &Baz{
+ entries: map[int]interface{}{
+ 0: &Foo{},
+ -2: &Bar{},
+ -11: &Baz{whatever: "we need to go deeper"}}}},
+ bang: &Bar{foo: []*Foo{
+ &Foo{bar: &Bar{baz: &Baz{
+ entries: map[int]interface{}{
+ -2: &Foo{},
+ -5: &Bar{},
+ -7: &Baz{whatever: "are you serious?"}}}},
+ bang: &Bar{foo: []*Foo{}}},
+ &Foo{bar: &Bar{baz: &Baz{
+ entries: map[int]interface{}{
+ -100: &Foo{},
+ 50: &Bar{},
+ 20: &Baz{whatever: "na, not really ..."}}}},
+ bang: &Bar{foo: []*Foo{}}}}}}}}},
+ &Foo{bar: &Bar{baz: &Baz{
+ entries: map[int]interface{}{
+ 2: &Foo{},
+ 1: &Bar{},
+ -1: &Baz{whatever: "... it's just a test."}}}},
+ bang: &Bar{foo: []*Foo{}}}}}}}}}
+}
+
+func TestElaborate(t *testing.T) {
+ a := newFoo()
+ b := newFoo()
+
+ if err := deepequal.Check(a, b); err != nil {
+ t.Errorf("expected nil, saw %v", err)
+ }
+}
diff --git a/src/pkg/go/printer/testdata/slow.input b/src/pkg/go/printer/testdata/slow.input
new file mode 100644
index 000000000..0e5a23d88
--- /dev/null
+++ b/src/pkg/go/printer/testdata/slow.input
@@ -0,0 +1,85 @@
+// 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 deepequal_test
+
+import (
+ "testing"
+ "google3/spam/archer/frontend/deepequal"
+)
+
+func TestTwoNilValues(t *testing.T) {
+ if err := deepequal.Check(nil, nil); err != nil {
+ t.Errorf("expected nil, saw %v", err)
+ }
+}
+
+type Foo struct {
+ bar *Bar
+ bang *Bar
+}
+
+type Bar struct {
+ baz *Baz
+ foo []*Foo
+}
+
+type Baz struct {
+ entries map[int]interface{}
+ whatever string
+}
+
+func newFoo() (*Foo) {
+return &Foo{bar: &Bar{ baz: &Baz{
+entries: map[int]interface{}{
+42: &Foo{},
+21: &Bar{},
+11: &Baz{ whatever: "it's just a test" }}}},
+ bang: &Bar{foo: []*Foo{
+&Foo{bar: &Bar{ baz: &Baz{
+entries: map[int]interface{}{
+43: &Foo{},
+22: &Bar{},
+13: &Baz{ whatever: "this is nuts" }}}},
+ bang: &Bar{foo: []*Foo{
+&Foo{bar: &Bar{ baz: &Baz{
+entries: map[int]interface{}{
+61: &Foo{},
+71: &Bar{},
+11: &Baz{ whatever: "no, it's Go" }}}},
+ bang: &Bar{foo: []*Foo{
+&Foo{bar: &Bar{ baz: &Baz{
+entries: map[int]interface{}{
+0: &Foo{},
+-2: &Bar{},
+-11: &Baz{ whatever: "we need to go deeper" }}}},
+ bang: &Bar{foo: []*Foo{
+&Foo{bar: &Bar{ baz: &Baz{
+entries: map[int]interface{}{
+-2: &Foo{},
+-5: &Bar{},
+-7: &Baz{ whatever: "are you serious?" }}}},
+ bang: &Bar{foo: []*Foo{}}},
+&Foo{bar: &Bar{ baz: &Baz{
+entries: map[int]interface{}{
+-100: &Foo{},
+50: &Bar{},
+20: &Baz{ whatever: "na, not really ..." }}}},
+ bang: &Bar{foo: []*Foo{}}}}}}}}},
+&Foo{bar: &Bar{ baz: &Baz{
+entries: map[int]interface{}{
+2: &Foo{},
+1: &Bar{},
+-1: &Baz{ whatever: "... it's just a test." }}}},
+ bang: &Bar{foo: []*Foo{}}}}}}}}}
+}
+
+func TestElaborate(t *testing.T) {
+ a := newFoo()
+ b := newFoo()
+
+ if err := deepequal.Check(a, b); err != nil {
+ t.Errorf("expected nil, saw %v", err)
+ }
+}
diff --git a/src/pkg/go/scanner/scanner.go b/src/pkg/go/scanner/scanner.go
index 153707f59..2f949ad25 100644
--- a/src/pkg/go/scanner/scanner.go
+++ b/src/pkg/go/scanner/scanner.go
@@ -177,11 +177,11 @@ var prefix = []byte("//line ")
func (S *Scanner) interpretLineComment(text []byte) {
if bytes.HasPrefix(text, prefix) {
// get filename and line number, if any
- if i := bytes.Index(text, []byte{':'}); i > 0 {
+ if i := bytes.LastIndex(text, []byte{':'}); i > 0 {
if line, err := strconv.Atoi(string(text[i+1:])); err == nil && line > 0 {
// valid //line filename:line comment;
filename := filepath.Clean(string(text[len(prefix):i]))
- if filename[0] != '/' {
+ if !filepath.IsAbs(filename) {
// make filename relative to current directory
filename = filepath.Join(S.dir, filename)
}
@@ -538,14 +538,12 @@ func (S *Scanner) switch4(tok0, tok1 token.Token, ch2 int, tok2, tok3 token.Toke
}
-var newline = []byte{'\n'}
-
-// Scan scans the next token and returns the token position pos,
-// the token tok, and the literal text lit corresponding to the
+// Scan scans the next token and returns the token position,
+// the token, and the literal string corresponding to the
// token. The source end is indicated by token.EOF.
//
// If the returned token is token.SEMICOLON, the corresponding
-// literal value is ";" if the semicolon was present in the source,
+// literal string is ";" if the semicolon was present in the source,
// and "\n" if the semicolon was inserted because of a newline or
// at EOF.
//
@@ -560,7 +558,7 @@ var newline = []byte{'\n'}
// set with Init. Token positions are relative to that file
// and thus relative to the file set.
//
-func (S *Scanner) Scan() (token.Pos, token.Token, []byte) {
+func (S *Scanner) Scan() (token.Pos, token.Token, string) {
scanAgain:
S.skipWhitespace()
@@ -586,7 +584,7 @@ scanAgain:
case -1:
if S.insertSemi {
S.insertSemi = false // EOF consumed
- return S.file.Pos(offs), token.SEMICOLON, newline
+ return S.file.Pos(offs), token.SEMICOLON, "\n"
}
tok = token.EOF
case '\n':
@@ -594,7 +592,7 @@ scanAgain:
// set in the first place and exited early
// from S.skipWhitespace()
S.insertSemi = false // newline consumed
- return S.file.Pos(offs), token.SEMICOLON, newline
+ return S.file.Pos(offs), token.SEMICOLON, "\n"
case '"':
insertSemi = true
tok = token.STRING
@@ -662,7 +660,7 @@ scanAgain:
S.offset = offs
S.rdOffset = offs + 1
S.insertSemi = false // newline consumed
- return S.file.Pos(offs), token.SEMICOLON, newline
+ return S.file.Pos(offs), token.SEMICOLON, "\n"
}
S.scanComment()
if S.mode&ScanComments == 0 {
@@ -711,5 +709,9 @@ scanAgain:
if S.mode&InsertSemis != 0 {
S.insertSemi = insertSemi
}
- return S.file.Pos(offs), tok, S.src[offs:S.offset]
+
+ // TODO(gri): The scanner API should change such that the literal string
+ // is only valid if an actual literal was scanned. This will
+ // permit a more efficient implementation.
+ return S.file.Pos(offs), tok, string(S.src[offs:S.offset])
}
diff --git a/src/pkg/go/scanner/scanner_test.go b/src/pkg/go/scanner/scanner_test.go
index c622ff482..8afb00ee5 100644
--- a/src/pkg/go/scanner/scanner_test.go
+++ b/src/pkg/go/scanner/scanner_test.go
@@ -7,6 +7,8 @@ package scanner
import (
"go/token"
"os"
+ "path/filepath"
+ "runtime"
"testing"
)
@@ -232,12 +234,11 @@ func TestScan(t *testing.T) {
index := 0
epos := token.Position{"", 0, 1, 1} // expected position
for {
- pos, tok, litb := s.Scan()
+ pos, tok, lit := s.Scan()
e := elt{token.EOF, "", special}
if index < len(tokens) {
e = tokens[index]
}
- lit := string(litb)
if tok == token.EOF {
lit = "<EOF>"
epos.Line = src_linecount
@@ -255,7 +256,7 @@ func TestScan(t *testing.T) {
}
epos.Offset += len(lit) + len(whitespace)
epos.Line += newlineCount(lit) + whitespace_linecount
- if tok == token.COMMENT && litb[1] == '/' {
+ if tok == token.COMMENT && lit[1] == '/' {
// correct for unaccounted '/n' in //-style comment
epos.Offset++
epos.Line++
@@ -290,7 +291,7 @@ func checkSemi(t *testing.T, line string, mode uint) {
semiPos.Column++
pos, tok, lit = S.Scan()
if tok == token.SEMICOLON {
- if string(lit) != semiLit {
+ if lit != semiLit {
t.Errorf(`bad literal for %q: got %q, expected %q`, line, lit, semiLit)
}
checkPos(t, line, pos, semiPos)
@@ -443,32 +444,41 @@ func TestSemis(t *testing.T) {
}
}
-
-var segments = []struct {
+type segment struct {
srcline string // a line of source text
filename string // filename for current token
line int // line number for current token
-}{
+}
+
+var segments = []segment{
// exactly one token per line since the test consumes one token per segment
- {" line1", "dir/TestLineComments", 1},
- {"\nline2", "dir/TestLineComments", 2},
- {"\nline3 //line File1.go:100", "dir/TestLineComments", 3}, // bad line comment, ignored
- {"\nline4", "dir/TestLineComments", 4},
- {"\n//line File1.go:100\n line100", "dir/File1.go", 100},
- {"\n//line File2.go:200\n line200", "dir/File2.go", 200},
+ {" line1", filepath.Join("dir", "TestLineComments"), 1},
+ {"\nline2", filepath.Join("dir", "TestLineComments"), 2},
+ {"\nline3 //line File1.go:100", filepath.Join("dir", "TestLineComments"), 3}, // bad line comment, ignored
+ {"\nline4", filepath.Join("dir", "TestLineComments"), 4},
+ {"\n//line File1.go:100\n line100", filepath.Join("dir", "File1.go"), 100},
+ {"\n//line File2.go:200\n line200", filepath.Join("dir", "File2.go"), 200},
{"\n//line :1\n line1", "dir", 1},
- {"\n//line foo:42\n line42", "dir/foo", 42},
- {"\n //line foo:42\n line44", "dir/foo", 44}, // bad line comment, ignored
- {"\n//line foo 42\n line46", "dir/foo", 46}, // bad line comment, ignored
- {"\n//line foo:42 extra text\n line48", "dir/foo", 48}, // bad line comment, ignored
- {"\n//line /bar:42\n line42", "/bar", 42},
- {"\n//line ./foo:42\n line42", "dir/foo", 42},
- {"\n//line a/b/c/File1.go:100\n line100", "dir/a/b/c/File1.go", 100},
+ {"\n//line foo:42\n line42", filepath.Join("dir", "foo"), 42},
+ {"\n //line foo:42\n line44", filepath.Join("dir", "foo"), 44}, // bad line comment, ignored
+ {"\n//line foo 42\n line46", filepath.Join("dir", "foo"), 46}, // bad line comment, ignored
+ {"\n//line foo:42 extra text\n line48", filepath.Join("dir", "foo"), 48}, // bad line comment, ignored
+ {"\n//line /bar:42\n line42", string(filepath.Separator) + "bar", 42},
+ {"\n//line ./foo:42\n line42", filepath.Join("dir", "foo"), 42},
+ {"\n//line a/b/c/File1.go:100\n line100", filepath.Join("dir", "a", "b", "c", "File1.go"), 100},
+}
+
+var winsegments = []segment{
+ {"\n//line c:\\dir\\File1.go:100\n line100", "c:\\dir\\File1.go", 100},
}
// Verify that comments of the form "//line filename:line" are interpreted correctly.
func TestLineComments(t *testing.T) {
+ if runtime.GOOS == "windows" {
+ segments = append(segments, winsegments...)
+ }
+
// make source
var src string
for _, e := range segments {
@@ -477,12 +487,12 @@ func TestLineComments(t *testing.T) {
// verify scan
var S Scanner
- file := fset.AddFile("dir/TestLineComments", fset.Base(), len(src))
+ file := fset.AddFile(filepath.Join("dir", "TestLineComments"), fset.Base(), len(src))
S.Init(file, []byte(src), nil, 0)
for _, s := range segments {
p, _, lit := S.Scan()
pos := file.Position(p)
- checkPos(t, string(lit), p, token.Position{s.filename, pos.Offset, s.line, pos.Column})
+ checkPos(t, lit, p, token.Position{s.filename, pos.Offset, s.line, pos.Column})
}
if S.ErrorCount != 0 {
@@ -536,10 +546,10 @@ func TestIllegalChars(t *testing.T) {
for offs, ch := range src {
pos, tok, lit := s.Scan()
if poffs := file.Offset(pos); poffs != offs {
- t.Errorf("bad position for %s: got %d, expected %d", string(lit), poffs, offs)
+ t.Errorf("bad position for %s: got %d, expected %d", lit, poffs, offs)
}
- if tok == token.ILLEGAL && string(lit) != string(ch) {
- t.Errorf("bad token: got %s, expected %s", string(lit), string(ch))
+ if tok == token.ILLEGAL && lit != string(ch) {
+ t.Errorf("bad token: got %s, expected %s", lit, string(ch))
}
}
diff --git a/src/pkg/go/token/token.go b/src/pkg/go/token/token.go
index 2a2d3ecc4..a5f21df16 100644
--- a/src/pkg/go/token/token.go
+++ b/src/pkg/go/token/token.go
@@ -126,10 +126,7 @@ const (
)
-// At the moment we have no array literal syntax that lets us describe
-// the index for each element - use a map for now to make sure they are
-// in sync.
-var tokens = map[Token]string{
+var tokens = [...]string{
ILLEGAL: "ILLEGAL",
EOF: "EOF",
@@ -237,10 +234,14 @@ var tokens = map[Token]string{
// constant name (e.g. for the token IDENT, the string is "IDENT").
//
func (tok Token) String() string {
- if str, exists := tokens[tok]; exists {
- return str
+ s := ""
+ if 0 <= tok && tok < Token(len(tokens)) {
+ s = tokens[tok]
}
- return "token(" + strconv.Itoa(int(tok)) + ")"
+ if s == "" {
+ s = "token(" + strconv.Itoa(int(tok)) + ")"
+ }
+ return s
}
diff --git a/src/pkg/go/typechecker/Makefile b/src/pkg/go/typechecker/Makefile
index 62b2aa7fe..83af3ef4e 100644
--- a/src/pkg/go/typechecker/Makefile
+++ b/src/pkg/go/typechecker/Makefile
@@ -7,6 +7,7 @@ include ../../../Make.inc
TARG=go/typechecker
GOFILES=\
scope.go\
+ type.go\
typechecker.go\
universe.go\
diff --git a/src/pkg/go/typechecker/scope.go b/src/pkg/go/typechecker/scope.go
index 114c93ea8..a4bee6e69 100644
--- a/src/pkg/go/typechecker/scope.go
+++ b/src/pkg/go/typechecker/scope.go
@@ -2,15 +2,15 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// This file implements scope support functions.
+// DEPRECATED FILE - WILL GO AWAY EVENTUALLY.
+//
+// Scope handling is now done in go/parser.
+// The functionality here is only present to
+// keep the typechecker running for now.
package typechecker
-import (
- "fmt"
- "go/ast"
- "go/token"
-)
+import "go/ast"
func (tc *typechecker) openScope() *ast.Scope {
@@ -24,52 +24,25 @@ func (tc *typechecker) closeScope() {
}
-// objPos computes the source position of the declaration of an object name.
-// Only required for error reporting, so doesn't have to be fast.
-func objPos(obj *ast.Object) (pos token.Pos) {
- switch d := obj.Decl.(type) {
- case *ast.Field:
- for _, n := range d.Names {
- if n.Name == obj.Name {
- return n.Pos()
- }
- }
- case *ast.ValueSpec:
- for _, n := range d.Names {
- if n.Name == obj.Name {
- return n.Pos()
- }
- }
- case *ast.TypeSpec:
- return d.Name.Pos()
- case *ast.FuncDecl:
- return d.Name.Pos()
- }
- if debug {
- fmt.Printf("decl = %T\n", obj.Decl)
- }
- panic("unreachable")
-}
-
-
// declInScope declares an object of a given kind and name in scope and sets the object's Decl and N fields.
// It returns the newly allocated object. If an object with the same name already exists in scope, an error
// is reported and the object is not inserted.
-// (Objects with _ name are always inserted into a scope without errors, but they cannot be found.)
-func (tc *typechecker) declInScope(scope *ast.Scope, kind ast.Kind, name *ast.Ident, decl interface{}, n int) *ast.Object {
+func (tc *typechecker) declInScope(scope *ast.Scope, kind ast.ObjKind, name *ast.Ident, decl interface{}, n int) *ast.Object {
obj := ast.NewObj(kind, name.Name)
obj.Decl = decl
- obj.N = n
+ //obj.N = n
name.Obj = obj
- if alt := scope.Insert(obj); alt != obj {
- tc.Errorf(name.Pos(), "%s already declared at %s", name.Name, objPos(alt))
+ if name.Name != "_" {
+ if alt := scope.Insert(obj); alt != nil {
+ tc.Errorf(name.Pos(), "%s already declared at %s", name.Name, tc.fset.Position(alt.Pos()).String())
+ }
}
return obj
}
// decl is the same as declInScope(tc.topScope, ...)
-func (tc *typechecker) decl(kind ast.Kind, name *ast.Ident, decl interface{}, n int) *ast.Object {
+func (tc *typechecker) decl(kind ast.ObjKind, name *ast.Ident, decl interface{}, n int) *ast.Object {
return tc.declInScope(tc.topScope, kind, name, decl, n)
}
@@ -91,7 +64,7 @@ func (tc *typechecker) find(name *ast.Ident) (obj *ast.Object) {
// findField returns the object with the given name if visible in the type's scope.
// If no such object is found, an error is reported and a bad object is returned instead.
-func (tc *typechecker) findField(typ *ast.Type, name *ast.Ident) (obj *ast.Object) {
+func (tc *typechecker) findField(typ *Type, name *ast.Ident) (obj *ast.Object) {
// TODO(gri) This is simplistic at the moment and ignores anonymous fields.
obj = typ.Scope.Lookup(name.Name)
if obj == nil {
@@ -100,20 +73,3 @@ func (tc *typechecker) findField(typ *ast.Type, name *ast.Ident) (obj *ast.Objec
}
return
}
-
-
-// printScope prints the objects in a scope.
-func printScope(scope *ast.Scope) {
- fmt.Printf("scope %p {", scope)
- if scope != nil && len(scope.Objects) > 0 {
- fmt.Println()
- for _, obj := range scope.Objects {
- form := "void"
- if obj.Type != nil {
- form = obj.Type.Form.String()
- }
- fmt.Printf("\t%s\t%s\n", obj.Name, form)
- }
- }
- fmt.Printf("}\n")
-}
diff --git a/src/pkg/go/typechecker/testdata/test0.go b/src/pkg/go/typechecker/testdata/test0.src
index 4e317f214..4e317f214 100644
--- a/src/pkg/go/typechecker/testdata/test0.go
+++ b/src/pkg/go/typechecker/testdata/test0.src
diff --git a/src/pkg/go/typechecker/testdata/test1.go b/src/pkg/go/typechecker/testdata/test1.src
index b0808ee7a..b5531fb9f 100644
--- a/src/pkg/go/typechecker/testdata/test1.go
+++ b/src/pkg/go/typechecker/testdata/test1.src
@@ -7,7 +7,7 @@
package P1
const (
- c1 /* ERROR "missing initializer" */
+ c1 = 0
c2 int = 0
c3, c4 = 0
)
diff --git a/src/pkg/go/typechecker/testdata/test3.go b/src/pkg/go/typechecker/testdata/test3.src
index ea35808a0..2e1a9fa8f 100644
--- a/src/pkg/go/typechecker/testdata/test3.go
+++ b/src/pkg/go/typechecker/testdata/test3.src
@@ -27,8 +27,11 @@ func (T) m1 /* ERROR "already declared" */ () {}
func (x *T) m2(u, x /* ERROR "already declared" */ int) {}
func (x *T) m3(a, b, c int) (u, x /* ERROR "already declared" */ int) {}
-func (T) _(x, x /* ERROR "already declared" */ int) {}
-func (T) _() (x, x /* ERROR "already declared" */ int) {}
+// The following are disabled for now because the typechecker
+// in in the process of being rewritten and cannot handle them
+// at the moment
+//func (T) _(x, x /* "already declared" */ int) {}
+//func (T) _() (x, x /* "already declared" */ int) {}
//func (PT) _() {}
diff --git a/src/pkg/go/typechecker/testdata/test4.go b/src/pkg/go/typechecker/testdata/test4.src
index bb9aee3ad..94d3558f9 100644
--- a/src/pkg/go/typechecker/testdata/test4.go
+++ b/src/pkg/go/typechecker/testdata/test4.src
@@ -7,5 +7,5 @@
package P4
const (
- c0 /* ERROR "missing initializer" */
+ c0 = 0
)
diff --git a/src/pkg/go/typechecker/type.go b/src/pkg/go/typechecker/type.go
new file mode 100644
index 000000000..62b4e9d3e
--- /dev/null
+++ b/src/pkg/go/typechecker/type.go
@@ -0,0 +1,125 @@
+// 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.
+
+package typechecker
+
+import "go/ast"
+
+
+// A Type represents a Go type.
+type Type struct {
+ Form Form
+ Obj *ast.Object // corresponding type name, or nil
+ Scope *ast.Scope // fields and methods, always present
+ N uint // basic type id, array length, number of function results, or channel direction
+ Key, Elt *Type // map key and array, pointer, slice, map or channel element
+ Params *ast.Scope // function (receiver, input and result) parameters, tuple expressions (results of function calls), or nil
+ Expr ast.Expr // corresponding AST expression
+}
+
+
+// NewType creates a new type of a given form.
+func NewType(form Form) *Type {
+ return &Type{Form: form, Scope: ast.NewScope(nil)}
+}
+
+
+// Form describes the form of a type.
+type Form int
+
+// The list of possible type forms.
+const (
+ BadType Form = iota // for error handling
+ Unresolved // type not fully setup
+ Basic
+ Array
+ Struct
+ Pointer
+ Function
+ Method
+ Interface
+ Slice
+ Map
+ Channel
+ Tuple
+)
+
+
+var formStrings = [...]string{
+ BadType: "badType",
+ Unresolved: "unresolved",
+ Basic: "basic",
+ Array: "array",
+ Struct: "struct",
+ Pointer: "pointer",
+ Function: "function",
+ Method: "method",
+ Interface: "interface",
+ Slice: "slice",
+ Map: "map",
+ Channel: "channel",
+ Tuple: "tuple",
+}
+
+
+func (form Form) String() string { return formStrings[form] }
+
+
+// The list of basic type id's.
+const (
+ Bool = iota
+ Byte
+ Uint
+ Int
+ Float
+ Complex
+ Uintptr
+ String
+
+ Uint8
+ Uint16
+ Uint32
+ Uint64
+
+ Int8
+ Int16
+ Int32
+ Int64
+
+ Float32
+ Float64
+
+ Complex64
+ Complex128
+
+ // TODO(gri) ideal types are missing
+)
+
+
+var BasicTypes = map[uint]string{
+ Bool: "bool",
+ Byte: "byte",
+ Uint: "uint",
+ Int: "int",
+ Float: "float",
+ Complex: "complex",
+ Uintptr: "uintptr",
+ String: "string",
+
+ Uint8: "uint8",
+ Uint16: "uint16",
+ Uint32: "uint32",
+ Uint64: "uint64",
+
+ Int8: "int8",
+ Int16: "int16",
+ Int32: "int32",
+ Int64: "int64",
+
+ Float32: "float32",
+ Float64: "float64",
+
+ Complex64: "complex64",
+ Complex128: "complex128",
+}
diff --git a/src/pkg/go/typechecker/typechecker.go b/src/pkg/go/typechecker/typechecker.go
index e9aefa240..b151f5834 100644
--- a/src/pkg/go/typechecker/typechecker.go
+++ b/src/pkg/go/typechecker/typechecker.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// INCOMPLETE PACKAGE.
+// DEPRECATED PACKAGE - SEE go/types INSTEAD.
// This package implements typechecking of a Go AST.
// The result of the typecheck is an augmented AST
// with object and type information for each identifier.
@@ -53,7 +53,7 @@ func CheckPackage(fset *token.FileSet, pkg *ast.Package, importer Importer) os.E
//
func CheckFile(fset *token.FileSet, file *ast.File, importer Importer) os.Error {
// create a single-file dummy package
- pkg := &ast.Package{file.Name.Name, nil, map[string]*ast.File{fset.Position(file.Name.NamePos).Filename: file}}
+ pkg := &ast.Package{file.Name.Name, nil, nil, map[string]*ast.File{fset.Position(file.Name.NamePos).Filename: file}}
return CheckPackage(fset, pkg, importer)
}
@@ -65,6 +65,7 @@ type typechecker struct {
fset *token.FileSet
scanner.ErrorVector
importer Importer
+ globals []*ast.Object // list of global objects
topScope *ast.Scope // current top-most scope
cyclemap map[*ast.Object]bool // for cycle detection
iota int // current value of iota
@@ -94,7 +95,7 @@ phase 1: declare all global objects; also collect all function and method declar
- report global double declarations
phase 2: bind methods to their receiver base types
- - received base types must be declared in the package, thus for
+ - receiver base types must be declared in the package, thus for
each method a corresponding (unresolved) type must exist
- report method double declarations and errors with base types
@@ -142,16 +143,16 @@ func (tc *typechecker) checkPackage(pkg *ast.Package) {
}
// phase 3: resolve all global objects
- // (note that objects with _ name are also in the scope)
tc.cyclemap = make(map[*ast.Object]bool)
- for _, obj := range tc.topScope.Objects {
+ for _, obj := range tc.globals {
tc.resolve(obj)
}
assert(len(tc.cyclemap) == 0)
// 4: sequentially typecheck function and method bodies
for _, f := range funcs {
- tc.checkBlock(f.Body.List, f.Name.Obj.Type)
+ ftype, _ := f.Name.Obj.Type.(*Type)
+ tc.checkBlock(f.Body.List, ftype)
}
pkg.Scope = tc.topScope
@@ -183,11 +184,11 @@ func (tc *typechecker) declGlobal(global ast.Decl) {
}
}
for _, name := range s.Names {
- tc.decl(ast.Con, name, s, iota)
+ tc.globals = append(tc.globals, tc.decl(ast.Con, name, s, iota))
}
case token.VAR:
for _, name := range s.Names {
- tc.decl(ast.Var, name, s, 0)
+ tc.globals = append(tc.globals, tc.decl(ast.Var, name, s, 0))
}
default:
panic("unreachable")
@@ -196,9 +197,10 @@ func (tc *typechecker) declGlobal(global ast.Decl) {
iota++
case *ast.TypeSpec:
obj := tc.decl(ast.Typ, s.Name, s, 0)
+ tc.globals = append(tc.globals, obj)
// give all type objects an unresolved type so
// that we can collect methods in the type scope
- typ := ast.NewType(ast.Unresolved)
+ typ := NewType(Unresolved)
obj.Type = typ
typ.Obj = obj
default:
@@ -208,7 +210,7 @@ func (tc *typechecker) declGlobal(global ast.Decl) {
case *ast.FuncDecl:
if d.Recv == nil {
- tc.decl(ast.Fun, d.Name, d, 0)
+ tc.globals = append(tc.globals, tc.decl(ast.Fun, d.Name, d, 0))
}
default:
@@ -239,8 +241,8 @@ func (tc *typechecker) bindMethod(method *ast.FuncDecl) {
} else if obj.Kind != ast.Typ {
tc.Errorf(name.Pos(), "invalid receiver: %s is not a type", name.Name)
} else {
- typ := obj.Type
- assert(typ.Form == ast.Unresolved)
+ typ := obj.Type.(*Type)
+ assert(typ.Form == Unresolved)
scope = typ.Scope
}
}
@@ -261,7 +263,7 @@ func (tc *typechecker) bindMethod(method *ast.FuncDecl) {
func (tc *typechecker) resolve(obj *ast.Object) {
// check for declaration cycles
if tc.cyclemap[obj] {
- tc.Errorf(objPos(obj), "illegal cycle in declaration of %s", obj.Name)
+ tc.Errorf(obj.Pos(), "illegal cycle in declaration of %s", obj.Name)
obj.Kind = ast.Bad
return
}
@@ -271,7 +273,7 @@ func (tc *typechecker) resolve(obj *ast.Object) {
}()
// resolve non-type objects
- typ := obj.Type
+ typ, _ := obj.Type.(*Type)
if typ == nil {
switch obj.Kind {
case ast.Bad:
@@ -282,12 +284,12 @@ func (tc *typechecker) resolve(obj *ast.Object) {
case ast.Var:
tc.declVar(obj)
- //obj.Type = tc.typeFor(nil, obj.Decl.(*ast.ValueSpec).Type, false)
+ obj.Type = tc.typeFor(nil, obj.Decl.(*ast.ValueSpec).Type, false)
case ast.Fun:
- obj.Type = ast.NewType(ast.Function)
+ obj.Type = NewType(Function)
t := obj.Decl.(*ast.FuncDecl).Type
- tc.declSignature(obj.Type, nil, t.Params, t.Results)
+ tc.declSignature(obj.Type.(*Type), nil, t.Params, t.Results)
default:
// type objects have non-nil types when resolve is called
@@ -300,32 +302,34 @@ func (tc *typechecker) resolve(obj *ast.Object) {
}
// resolve type objects
- if typ.Form == ast.Unresolved {
+ if typ.Form == Unresolved {
tc.typeFor(typ, typ.Obj.Decl.(*ast.TypeSpec).Type, false)
// provide types for all methods
for _, obj := range typ.Scope.Objects {
if obj.Kind == ast.Fun {
assert(obj.Type == nil)
- obj.Type = ast.NewType(ast.Method)
+ obj.Type = NewType(Method)
f := obj.Decl.(*ast.FuncDecl)
t := f.Type
- tc.declSignature(obj.Type, f.Recv, t.Params, t.Results)
+ tc.declSignature(obj.Type.(*Type), f.Recv, t.Params, t.Results)
}
}
}
}
-func (tc *typechecker) checkBlock(body []ast.Stmt, ftype *ast.Type) {
+func (tc *typechecker) checkBlock(body []ast.Stmt, ftype *Type) {
tc.openScope()
defer tc.closeScope()
// inject function/method parameters into block scope, if any
if ftype != nil {
for _, par := range ftype.Params.Objects {
- obj := tc.topScope.Insert(par)
- assert(obj == par) // ftype has no double declarations
+ if par.Name != "_" {
+ alt := tc.topScope.Insert(par)
+ assert(alt == nil) // ftype has no double declarations
+ }
}
}
@@ -362,8 +366,8 @@ func (tc *typechecker) declFields(scope *ast.Scope, fields *ast.FieldList, ref b
}
-func (tc *typechecker) declSignature(typ *ast.Type, recv, params, results *ast.FieldList) {
- assert((typ.Form == ast.Method) == (recv != nil))
+func (tc *typechecker) declSignature(typ *Type, recv, params, results *ast.FieldList) {
+ assert((typ.Form == Method) == (recv != nil))
typ.Params = ast.NewScope(nil)
tc.declFields(typ.Params, recv, true)
tc.declFields(typ.Params, params, true)
@@ -371,7 +375,7 @@ func (tc *typechecker) declSignature(typ *ast.Type, recv, params, results *ast.F
}
-func (tc *typechecker) typeFor(def *ast.Type, x ast.Expr, ref bool) (typ *ast.Type) {
+func (tc *typechecker) typeFor(def *Type, x ast.Expr, ref bool) (typ *Type) {
x = unparen(x)
// type name
@@ -381,10 +385,10 @@ func (tc *typechecker) typeFor(def *ast.Type, x ast.Expr, ref bool) (typ *ast.Ty
if obj.Kind != ast.Typ {
tc.Errorf(t.Pos(), "%s is not a type", t.Name)
if def == nil {
- typ = ast.NewType(ast.BadType)
+ typ = NewType(BadType)
} else {
typ = def
- typ.Form = ast.BadType
+ typ.Form = BadType
}
typ.Expr = x
return
@@ -393,7 +397,7 @@ func (tc *typechecker) typeFor(def *ast.Type, x ast.Expr, ref bool) (typ *ast.Ty
if !ref {
tc.resolve(obj) // check for cycles even if type resolved
}
- typ = obj.Type
+ typ = obj.Type.(*Type)
if def != nil {
// new type declaration: copy type structure
@@ -410,7 +414,7 @@ func (tc *typechecker) typeFor(def *ast.Type, x ast.Expr, ref bool) (typ *ast.Ty
// type literal
typ = def
if typ == nil {
- typ = ast.NewType(ast.BadType)
+ typ = NewType(BadType)
}
typ.Expr = x
@@ -419,42 +423,42 @@ func (tc *typechecker) typeFor(def *ast.Type, x ast.Expr, ref bool) (typ *ast.Ty
if debug {
fmt.Println("qualified identifier unimplemented")
}
- typ.Form = ast.BadType
+ typ.Form = BadType
case *ast.StarExpr:
- typ.Form = ast.Pointer
+ typ.Form = Pointer
typ.Elt = tc.typeFor(nil, t.X, true)
case *ast.ArrayType:
if t.Len != nil {
- typ.Form = ast.Array
+ typ.Form = Array
// TODO(gri) compute the real length
// (this may call resolve recursively)
(*typ).N = 42
} else {
- typ.Form = ast.Slice
+ typ.Form = Slice
}
typ.Elt = tc.typeFor(nil, t.Elt, t.Len == nil)
case *ast.StructType:
- typ.Form = ast.Struct
+ typ.Form = Struct
tc.declFields(typ.Scope, t.Fields, false)
case *ast.FuncType:
- typ.Form = ast.Function
+ typ.Form = Function
tc.declSignature(typ, nil, t.Params, t.Results)
case *ast.InterfaceType:
- typ.Form = ast.Interface
+ typ.Form = Interface
tc.declFields(typ.Scope, t.Methods, true)
case *ast.MapType:
- typ.Form = ast.Map
+ typ.Form = Map
typ.Key = tc.typeFor(nil, t.Key, true)
typ.Elt = tc.typeFor(nil, t.Value, true)
case *ast.ChanType:
- typ.Form = ast.Channel
+ typ.Form = Channel
typ.N = uint(t.Dir)
typ.Elt = tc.typeFor(nil, t.Value, true)
diff --git a/src/pkg/go/typechecker/typechecker_test.go b/src/pkg/go/typechecker/typechecker_test.go
index 33f4a6223..d16e06921 100644
--- a/src/pkg/go/typechecker/typechecker_test.go
+++ b/src/pkg/go/typechecker/typechecker_test.go
@@ -78,7 +78,7 @@ func expectedErrors(t *testing.T, pkg *ast.Package) (list scanner.ErrorList) {
case token.EOF:
break loop
case token.COMMENT:
- s := errRx.FindSubmatch(lit)
+ s := errRx.FindStringSubmatch(lit)
if len(s) == 2 {
list = append(list, &scanner.Error{fset.Position(prev), string(s[1])})
}
@@ -93,7 +93,7 @@ func expectedErrors(t *testing.T, pkg *ast.Package) (list scanner.ErrorList) {
func testFilter(f *os.FileInfo) bool {
- return strings.HasSuffix(f.Name, ".go") && f.Name[0] != '.'
+ return strings.HasSuffix(f.Name, ".src") && f.Name[0] != '.'
}
diff --git a/src/pkg/go/typechecker/universe.go b/src/pkg/go/typechecker/universe.go
index db950737f..abc8bbbd4 100644
--- a/src/pkg/go/typechecker/universe.go
+++ b/src/pkg/go/typechecker/universe.go
@@ -14,7 +14,7 @@ var Universe *ast.Scope
func def(obj *ast.Object) {
alt := Universe.Insert(obj)
- if alt != obj {
+ if alt != nil {
panic("object declared twice")
}
}
@@ -24,8 +24,8 @@ func init() {
Universe = ast.NewScope(nil)
// basic types
- for n, name := range ast.BasicTypes {
- typ := ast.NewType(ast.Basic)
+ for n, name := range BasicTypes {
+ typ := NewType(Basic)
typ.N = n
obj := ast.NewObj(ast.Typ, name)
obj.Type = typ
diff --git a/src/pkg/go/types/Makefile b/src/pkg/go/types/Makefile
new file mode 100644
index 000000000..54e762b36
--- /dev/null
+++ b/src/pkg/go/types/Makefile
@@ -0,0 +1,15 @@
+# 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.
+
+include ../../../Make.inc
+
+TARG=go/types
+GOFILES=\
+ const.go\
+ exportdata.go\
+ gcimporter.go\
+ types.go\
+ universe.go\
+
+include ../../../Make.pkg
diff --git a/src/pkg/go/types/const.go b/src/pkg/go/types/const.go
new file mode 100644
index 000000000..6fdc22f6b
--- /dev/null
+++ b/src/pkg/go/types/const.go
@@ -0,0 +1,347 @@
+// 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.
+
+// This file implements operations on ideal constants.
+
+package types
+
+import (
+ "big"
+ "go/token"
+ "strconv"
+)
+
+
+// TODO(gri) Consider changing the API so Const is an interface
+// and operations on consts don't have to type switch.
+
+// A Const implements an ideal constant Value.
+// The zero value z for a Const is not a valid constant value.
+type Const struct {
+ // representation of constant values:
+ // ideal bool -> bool
+ // ideal int -> *big.Int
+ // ideal float -> *big.Rat
+ // ideal complex -> cmplx
+ // ideal string -> string
+ val interface{}
+}
+
+
+// Representation of complex values.
+type cmplx struct {
+ re, im *big.Rat
+}
+
+
+func assert(cond bool) {
+ if !cond {
+ panic("go/types internal error: assertion failed")
+ }
+}
+
+
+// MakeConst makes an ideal constant from a literal
+// token and the corresponding literal string.
+func MakeConst(tok token.Token, lit string) Const {
+ switch tok {
+ case token.INT:
+ var x big.Int
+ _, ok := x.SetString(lit, 0)
+ assert(ok)
+ return Const{&x}
+ case token.FLOAT:
+ var y big.Rat
+ _, ok := y.SetString(lit)
+ assert(ok)
+ return Const{&y}
+ case token.IMAG:
+ assert(lit[len(lit)-1] == 'i')
+ var im big.Rat
+ _, ok := im.SetString(lit[0 : len(lit)-1])
+ assert(ok)
+ return Const{cmplx{big.NewRat(0, 1), &im}}
+ case token.CHAR:
+ assert(lit[0] == '\'' && lit[len(lit)-1] == '\'')
+ code, _, _, err := strconv.UnquoteChar(lit[1:len(lit)-1], '\'')
+ assert(err == nil)
+ return Const{big.NewInt(int64(code))}
+ case token.STRING:
+ s, err := strconv.Unquote(lit)
+ assert(err == nil)
+ return Const{s}
+ }
+ panic("unreachable")
+}
+
+
+// MakeZero returns the zero constant for the given type.
+func MakeZero(typ *Type) Const {
+ // TODO(gri) fix this
+ return Const{0}
+}
+
+
+// Match attempts to match the internal constant representations of x and y.
+// If the attempt is successful, the result is the values of x and y,
+// if necessary converted to have the same internal representation; otherwise
+// the results are invalid.
+func (x Const) Match(y Const) (u, v Const) {
+ switch a := x.val.(type) {
+ case bool:
+ if _, ok := y.val.(bool); ok {
+ u, v = x, y
+ }
+ case *big.Int:
+ switch y.val.(type) {
+ case *big.Int:
+ u, v = x, y
+ case *big.Rat:
+ var z big.Rat
+ z.SetInt(a)
+ u, v = Const{&z}, y
+ case cmplx:
+ var z big.Rat
+ z.SetInt(a)
+ u, v = Const{cmplx{&z, big.NewRat(0, 1)}}, y
+ }
+ case *big.Rat:
+ switch y.val.(type) {
+ case *big.Int:
+ v, u = y.Match(x)
+ case *big.Rat:
+ u, v = x, y
+ case cmplx:
+ u, v = Const{cmplx{a, big.NewRat(0, 0)}}, y
+ }
+ case cmplx:
+ switch y.val.(type) {
+ case *big.Int, *big.Rat:
+ v, u = y.Match(x)
+ case cmplx:
+ u, v = x, y
+ }
+ case string:
+ if _, ok := y.val.(string); ok {
+ u, v = x, y
+ }
+ default:
+ panic("unreachable")
+ }
+ return
+}
+
+
+// Convert attempts to convert the constant x to a given type.
+// If the attempt is successful, the result is the new constant;
+// otherwise the result is invalid.
+func (x Const) Convert(typ *Type) Const {
+ // TODO(gri) implement this
+ switch x := x.val.(type) {
+ case bool:
+ case *big.Int:
+ case *big.Rat:
+ case cmplx:
+ case string:
+ }
+ return x
+}
+
+
+func (x Const) String() string {
+ switch x := x.val.(type) {
+ case bool:
+ if x {
+ return "true"
+ }
+ return "false"
+ case *big.Int:
+ return x.String()
+ case *big.Rat:
+ return x.FloatString(10) // 10 digits of precision after decimal point seems fine
+ case cmplx:
+ // TODO(gri) don't print 0 components
+ return x.re.FloatString(10) + " + " + x.im.FloatString(10) + "i"
+ case string:
+ return x
+ }
+ panic("unreachable")
+}
+
+
+func (x Const) UnaryOp(op token.Token) Const {
+ panic("unimplemented")
+}
+
+
+func (x Const) BinaryOp(op token.Token, y Const) Const {
+ var z interface{}
+ switch x := x.val.(type) {
+ case bool:
+ z = binaryBoolOp(x, op, y.val.(bool))
+ case *big.Int:
+ z = binaryIntOp(x, op, y.val.(*big.Int))
+ case *big.Rat:
+ z = binaryFloatOp(x, op, y.val.(*big.Rat))
+ case cmplx:
+ z = binaryCmplxOp(x, op, y.val.(cmplx))
+ case string:
+ z = binaryStringOp(x, op, y.val.(string))
+ default:
+ panic("unreachable")
+ }
+ return Const{z}
+}
+
+
+func binaryBoolOp(x bool, op token.Token, y bool) interface{} {
+ switch op {
+ case token.EQL:
+ return x == y
+ case token.NEQ:
+ return x != y
+ }
+ panic("unreachable")
+}
+
+
+func binaryIntOp(x *big.Int, op token.Token, y *big.Int) interface{} {
+ var z big.Int
+ switch op {
+ case token.ADD:
+ return z.Add(x, y)
+ case token.SUB:
+ return z.Sub(x, y)
+ case token.MUL:
+ return z.Mul(x, y)
+ case token.QUO:
+ return z.Quo(x, y)
+ case token.REM:
+ return z.Rem(x, y)
+ case token.AND:
+ return z.And(x, y)
+ case token.OR:
+ return z.Or(x, y)
+ case token.XOR:
+ return z.Xor(x, y)
+ case token.AND_NOT:
+ return z.AndNot(x, y)
+ case token.SHL:
+ panic("unimplemented")
+ case token.SHR:
+ panic("unimplemented")
+ case token.EQL:
+ return x.Cmp(y) == 0
+ case token.NEQ:
+ return x.Cmp(y) != 0
+ case token.LSS:
+ return x.Cmp(y) < 0
+ case token.LEQ:
+ return x.Cmp(y) <= 0
+ case token.GTR:
+ return x.Cmp(y) > 0
+ case token.GEQ:
+ return x.Cmp(y) >= 0
+ }
+ panic("unreachable")
+}
+
+
+func binaryFloatOp(x *big.Rat, op token.Token, y *big.Rat) interface{} {
+ var z big.Rat
+ switch op {
+ case token.ADD:
+ return z.Add(x, y)
+ case token.SUB:
+ return z.Sub(x, y)
+ case token.MUL:
+ return z.Mul(x, y)
+ case token.QUO:
+ return z.Quo(x, y)
+ case token.EQL:
+ return x.Cmp(y) == 0
+ case token.NEQ:
+ return x.Cmp(y) != 0
+ case token.LSS:
+ return x.Cmp(y) < 0
+ case token.LEQ:
+ return x.Cmp(y) <= 0
+ case token.GTR:
+ return x.Cmp(y) > 0
+ case token.GEQ:
+ return x.Cmp(y) >= 0
+ }
+ panic("unreachable")
+}
+
+
+func binaryCmplxOp(x cmplx, op token.Token, y cmplx) interface{} {
+ a, b := x.re, x.im
+ c, d := y.re, y.im
+ switch op {
+ case token.ADD:
+ // (a+c) + i(b+d)
+ var re, im big.Rat
+ re.Add(a, c)
+ im.Add(b, d)
+ return cmplx{&re, &im}
+ case token.SUB:
+ // (a-c) + i(b-d)
+ var re, im big.Rat
+ re.Sub(a, c)
+ im.Sub(b, d)
+ return cmplx{&re, &im}
+ case token.MUL:
+ // (ac-bd) + i(bc+ad)
+ var ac, bd, bc, ad big.Rat
+ ac.Mul(a, c)
+ bd.Mul(b, d)
+ bc.Mul(b, c)
+ ad.Mul(a, d)
+ var re, im big.Rat
+ re.Sub(&ac, &bd)
+ im.Add(&bc, &ad)
+ return cmplx{&re, &im}
+ case token.QUO:
+ // (ac+bd)/s + i(bc-ad)/s, with s = cc + dd
+ var ac, bd, bc, ad, s big.Rat
+ ac.Mul(a, c)
+ bd.Mul(b, d)
+ bc.Mul(b, c)
+ ad.Mul(a, d)
+ s.Add(c.Mul(c, c), d.Mul(d, d))
+ var re, im big.Rat
+ re.Add(&ac, &bd)
+ re.Quo(&re, &s)
+ im.Sub(&bc, &ad)
+ im.Quo(&im, &s)
+ return cmplx{&re, &im}
+ case token.EQL:
+ return a.Cmp(c) == 0 && b.Cmp(d) == 0
+ case token.NEQ:
+ return a.Cmp(c) != 0 || b.Cmp(d) != 0
+ }
+ panic("unreachable")
+}
+
+
+func binaryStringOp(x string, op token.Token, y string) interface{} {
+ switch op {
+ case token.ADD:
+ return x + y
+ case token.EQL:
+ return x == y
+ case token.NEQ:
+ return x != y
+ case token.LSS:
+ return x < y
+ case token.LEQ:
+ return x <= y
+ case token.GTR:
+ return x > y
+ case token.GEQ:
+ return x >= y
+ }
+ panic("unreachable")
+}
diff --git a/src/pkg/go/types/exportdata.go b/src/pkg/go/types/exportdata.go
new file mode 100644
index 000000000..cb08ffe18
--- /dev/null
+++ b/src/pkg/go/types/exportdata.go
@@ -0,0 +1,135 @@
+// 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.
+
+// This file implements ExportData.
+
+package types
+
+import (
+ "bufio"
+ "fmt"
+ "io"
+ "os"
+ "strconv"
+ "strings"
+)
+
+
+func readGopackHeader(buf *bufio.Reader) (name string, size int, err os.Error) {
+ // See $GOROOT/include/ar.h.
+ hdr := make([]byte, 64+12+6+6+8+10+2)
+ _, err = io.ReadFull(buf, hdr)
+ if err != nil {
+ return
+ }
+ if trace {
+ fmt.Printf("header: %s", hdr)
+ }
+ s := strings.TrimSpace(string(hdr[64+12+6+6+8:][:10]))
+ size, err = strconv.Atoi(s)
+ if err != nil || hdr[len(hdr)-2] != '`' || hdr[len(hdr)-1] != '\n' {
+ err = os.ErrorString("invalid archive header")
+ return
+ }
+ name = strings.TrimSpace(string(hdr[:64]))
+ return
+}
+
+
+type dataReader struct {
+ *bufio.Reader
+ io.Closer
+}
+
+
+// ExportData returns a readCloser positioned at the beginning of the
+// export data section of the given object/archive file, or an error.
+// It is the caller's responsibility to close the readCloser.
+//
+func ExportData(filename string) (rc io.ReadCloser, err os.Error) {
+ file, err := os.Open(filename)
+ if err != nil {
+ return
+ }
+
+ defer func() {
+ if err != nil {
+ file.Close()
+ // Add file name to error.
+ err = fmt.Errorf("reading export data: %s: %v", filename, err)
+ }
+ }()
+
+ buf := bufio.NewReader(file)
+
+ // Read first line to make sure this is an object file.
+ line, err := buf.ReadSlice('\n')
+ if err != nil {
+ return
+ }
+ if string(line) == "!<arch>\n" {
+ // Archive file. Scan to __.PKGDEF, which should
+ // be second archive entry.
+ var name string
+ var size int
+
+ // First entry should be __.SYMDEF.
+ // Read and discard.
+ if name, size, err = readGopackHeader(buf); err != nil {
+ return
+ }
+ if name != "__.SYMDEF" {
+ err = os.ErrorString("go archive does not begin with __.SYMDEF")
+ return
+ }
+ const block = 4096
+ tmp := make([]byte, block)
+ for size > 0 {
+ n := size
+ if n > block {
+ n = block
+ }
+ _, err = io.ReadFull(buf, tmp[:n])
+ if err != nil {
+ return
+ }
+ size -= n
+ }
+
+ // Second entry should be __.PKGDEF.
+ if name, size, err = readGopackHeader(buf); err != nil {
+ return
+ }
+ if name != "__.PKGDEF" {
+ err = os.ErrorString("go archive is missing __.PKGDEF")
+ return
+ }
+
+ // Read first line of __.PKGDEF data, so that line
+ // is once again the first line of the input.
+ line, err = buf.ReadSlice('\n')
+ if err != nil {
+ return
+ }
+ }
+
+ // Now at __.PKGDEF in archive or still at beginning of file.
+ // Either way, line should begin with "go object ".
+ if !strings.HasPrefix(string(line), "go object ") {
+ err = os.ErrorString("not a go object file")
+ return
+ }
+
+ // Skip over object header to export data.
+ // Begins after first line with $$.
+ for line[0] != '$' {
+ line, err = buf.ReadSlice('\n')
+ if err != nil {
+ return
+ }
+ }
+
+ rc = &dataReader{buf, file}
+ return
+}
diff --git a/src/pkg/go/types/gcimporter.go b/src/pkg/go/types/gcimporter.go
new file mode 100644
index 000000000..9e0ae6285
--- /dev/null
+++ b/src/pkg/go/types/gcimporter.go
@@ -0,0 +1,786 @@
+// 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.
+
+// This file implements an ast.Importer for gc generated object files.
+// TODO(gri) Eventually move this into a separate package outside types.
+
+package types
+
+import (
+ "big"
+ "fmt"
+ "go/ast"
+ "go/token"
+ "io"
+ "os"
+ "path/filepath"
+ "runtime"
+ "scanner"
+ "strconv"
+)
+
+
+const trace = false // set to true for debugging
+
+var (
+ pkgRoot = filepath.Join(runtime.GOROOT(), "pkg", runtime.GOOS+"_"+runtime.GOARCH)
+ pkgExts = [...]string{".a", ".5", ".6", ".8"}
+)
+
+
+// findPkg returns the filename and package id for an import path.
+// If no file was found, an empty filename is returned.
+func findPkg(path string) (filename, id string) {
+ if len(path) == 0 {
+ return
+ }
+
+ id = path
+ var noext string
+ switch path[0] {
+ default:
+ // "x" -> "$GOROOT/pkg/$GOOS_$GOARCH/x.ext", "x"
+ noext = filepath.Join(pkgRoot, path)
+
+ case '.':
+ // "./x" -> "/this/directory/x.ext", "/this/directory/x"
+ cwd, err := os.Getwd()
+ if err != nil {
+ return
+ }
+ noext = filepath.Join(cwd, path)
+ id = noext
+
+ case '/':
+ // "/x" -> "/x.ext", "/x"
+ noext = path
+ }
+
+ // try extensions
+ for _, ext := range pkgExts {
+ filename = noext + ext
+ if f, err := os.Stat(filename); err == nil && f.IsRegular() {
+ return
+ }
+ }
+
+ filename = "" // not found
+ return
+}
+
+
+// gcParser parses the exports inside a gc compiler-produced
+// object/archive file and populates its scope with the results.
+type gcParser struct {
+ scanner scanner.Scanner
+ tok int // current token
+ lit string // literal string; only valid for Ident, Int, String tokens
+ id string // package id of imported package
+ scope *ast.Scope // scope of imported package; alias for deps[id]
+ deps map[string]*ast.Scope // package id -> package scope
+}
+
+
+func (p *gcParser) init(filename, id string, src io.Reader) {
+ p.scanner.Init(src)
+ p.scanner.Error = func(_ *scanner.Scanner, msg string) { p.error(msg) }
+ p.scanner.Mode = scanner.ScanIdents | scanner.ScanInts | scanner.ScanStrings | scanner.ScanComments | scanner.SkipComments
+ p.scanner.Whitespace = 1<<'\t' | 1<<' '
+ p.scanner.Filename = filename // for good error messages
+ p.next()
+ p.id = id
+ p.scope = ast.NewScope(nil)
+ p.deps = map[string]*ast.Scope{"unsafe": Unsafe, id: p.scope}
+}
+
+
+func (p *gcParser) next() {
+ p.tok = p.scanner.Scan()
+ switch p.tok {
+ case scanner.Ident, scanner.Int, scanner.String:
+ p.lit = p.scanner.TokenText()
+ default:
+ p.lit = ""
+ }
+ if trace {
+ fmt.Printf("%s: %q -> %q\n", scanner.TokenString(p.tok), p.scanner.TokenText(), p.lit)
+ }
+}
+
+
+// GcImporter implements the ast.Importer signature.
+func GcImporter(path string) (name string, scope *ast.Scope, err os.Error) {
+ if path == "unsafe" {
+ return path, Unsafe, nil
+ }
+
+ defer func() {
+ if r := recover(); r != nil {
+ err = r.(importError) // will re-panic if r is not an importError
+ if trace {
+ panic(err) // force a stack trace
+ }
+ }
+ }()
+
+ filename, id := findPkg(path)
+ if filename == "" {
+ err = os.ErrorString("can't find import: " + id)
+ return
+ }
+
+ buf, err := ExportData(filename)
+ if err != nil {
+ return
+ }
+ defer buf.Close()
+
+ if trace {
+ fmt.Printf("importing %s\n", filename)
+ }
+
+ var p gcParser
+ p.init(filename, id, buf)
+ name, scope = p.parseExport()
+
+ return
+}
+
+
+// ----------------------------------------------------------------------------
+// Error handling
+
+// Internal errors are boxed as importErrors.
+type importError struct {
+ pos scanner.Position
+ err os.Error
+}
+
+
+func (e importError) String() string {
+ return fmt.Sprintf("import error %s (byte offset = %d): %s", e.pos, e.pos.Offset, e.err)
+}
+
+
+func (p *gcParser) error(err interface{}) {
+ if s, ok := err.(string); ok {
+ err = os.ErrorString(s)
+ }
+ // panic with a runtime.Error if err is not an os.Error
+ panic(importError{p.scanner.Pos(), err.(os.Error)})
+}
+
+
+func (p *gcParser) errorf(format string, args ...interface{}) {
+ p.error(fmt.Sprintf(format, args...))
+}
+
+
+func (p *gcParser) expect(tok int) string {
+ lit := p.lit
+ if p.tok != tok {
+ p.errorf("expected %q, got %q (%q)", scanner.TokenString(tok), scanner.TokenString(p.tok), lit)
+ }
+ p.next()
+ return lit
+}
+
+
+func (p *gcParser) expectSpecial(tok string) {
+ sep := 'x' // not white space
+ i := 0
+ for i < len(tok) && p.tok == int(tok[i]) && sep > ' ' {
+ sep = p.scanner.Peek() // if sep <= ' ', there is white space before the next token
+ p.next()
+ i++
+ }
+ if i < len(tok) {
+ p.errorf("expected %q, got %q", tok, tok[0:i])
+ }
+}
+
+
+func (p *gcParser) expectKeyword(keyword string) {
+ lit := p.expect(scanner.Ident)
+ if lit != keyword {
+ p.errorf("expected keyword %s, got %q", keyword, lit)
+ }
+}
+
+
+// ----------------------------------------------------------------------------
+// Import declarations
+
+// ImportPath = string_lit .
+//
+func (p *gcParser) parsePkgId() *ast.Scope {
+ id, err := strconv.Unquote(p.expect(scanner.String))
+ if err != nil {
+ p.error(err)
+ }
+
+ scope := p.scope // id == "" stands for the imported package id
+ if id != "" {
+ if scope = p.deps[id]; scope == nil {
+ scope = ast.NewScope(nil)
+ p.deps[id] = scope
+ }
+ }
+
+ return scope
+}
+
+
+// dotIdentifier = ( ident | '·' ) { ident | int | '·' } .
+func (p *gcParser) parseDotIdent() string {
+ ident := ""
+ if p.tok != scanner.Int {
+ sep := 'x' // not white space
+ for (p.tok == scanner.Ident || p.tok == scanner.Int || p.tok == '·') && sep > ' ' {
+ ident += p.lit
+ sep = p.scanner.Peek() // if sep <= ' ', there is white space before the next token
+ p.next()
+ }
+ }
+ if ident == "" {
+ p.expect(scanner.Ident) // use expect() for error handling
+ }
+ return ident
+}
+
+
+// ExportedName = ImportPath "." dotIdentifier .
+//
+func (p *gcParser) parseExportedName(kind ast.ObjKind) *ast.Object {
+ scope := p.parsePkgId()
+ p.expect('.')
+ name := p.parseDotIdent()
+
+ // a type may have been declared before - if it exists
+ // already in the respective package scope, return that
+ // type
+ if kind == ast.Typ {
+ if obj := scope.Lookup(name); obj != nil {
+ assert(obj.Kind == ast.Typ)
+ return obj
+ }
+ }
+
+ // any other object must be a newly declared object -
+ // create it and insert it into the package scope
+ obj := ast.NewObj(kind, name)
+ if scope.Insert(obj) != nil {
+ p.errorf("already declared: %s", obj.Name)
+ }
+
+ // a new type object is a named type and may be referred
+ // to before the underlying type is known - set it up
+ if kind == ast.Typ {
+ obj.Type = &Name{Obj: obj}
+ }
+
+ return obj
+}
+
+
+// ----------------------------------------------------------------------------
+// Types
+
+// BasicType = identifier .
+//
+func (p *gcParser) parseBasicType() Type {
+ obj := Universe.Lookup(p.expect(scanner.Ident))
+ if obj == nil || obj.Kind != ast.Typ {
+ p.errorf("not a basic type: %s", obj.Name)
+ }
+ return obj.Type.(Type)
+}
+
+
+// ArrayType = "[" int_lit "]" Type .
+//
+func (p *gcParser) parseArrayType() Type {
+ // "[" already consumed and lookahead known not to be "]"
+ lit := p.expect(scanner.Int)
+ p.expect(']')
+ elt := p.parseType()
+ n, err := strconv.Atoui64(lit)
+ if err != nil {
+ p.error(err)
+ }
+ return &Array{Len: n, Elt: elt}
+}
+
+
+// MapType = "map" "[" Type "]" Type .
+//
+func (p *gcParser) parseMapType() Type {
+ p.expectKeyword("map")
+ p.expect('[')
+ key := p.parseType()
+ p.expect(']')
+ elt := p.parseType()
+ return &Map{Key: key, Elt: elt}
+}
+
+
+// Name = identifier | "?" .
+//
+func (p *gcParser) parseName() (name string) {
+ switch p.tok {
+ case scanner.Ident:
+ name = p.lit
+ p.next()
+ case '?':
+ // anonymous
+ p.next()
+ default:
+ p.error("name expected")
+ }
+ return
+}
+
+
+// Field = Name Type [ ":" string_lit ] .
+//
+func (p *gcParser) parseField(scope *ast.Scope) {
+ // TODO(gri) The code below is not correct for anonymous fields:
+ // The name is the type name; it should not be empty.
+ name := p.parseName()
+ ftyp := p.parseType()
+ if name == "" {
+ // anonymous field - ftyp must be T or *T and T must be a type name
+ ftyp = Deref(ftyp)
+ if ftyp, ok := ftyp.(*Name); ok {
+ name = ftyp.Obj.Name
+ } else {
+ p.errorf("anonymous field expected")
+ }
+ }
+ if p.tok == ':' {
+ p.next()
+ tag := p.expect(scanner.String)
+ _ = tag // TODO(gri) store tag somewhere
+ }
+ fld := ast.NewObj(ast.Var, name)
+ fld.Type = ftyp
+ scope.Insert(fld)
+}
+
+
+// StructType = "struct" "{" [ FieldList ] "}" .
+// FieldList = Field { ";" Field } .
+//
+func (p *gcParser) parseStructType() Type {
+ p.expectKeyword("struct")
+ p.expect('{')
+ scope := ast.NewScope(nil)
+ if p.tok != '}' {
+ p.parseField(scope)
+ for p.tok == ';' {
+ p.next()
+ p.parseField(scope)
+ }
+ }
+ p.expect('}')
+ return &Struct{}
+}
+
+
+// Parameter = ( identifier | "?" ) [ "..." ] Type .
+//
+func (p *gcParser) parseParameter(scope *ast.Scope, isVariadic *bool) {
+ name := p.parseName()
+ if name == "" {
+ name = "_" // cannot access unnamed identifiers
+ }
+ if isVariadic != nil {
+ if *isVariadic {
+ p.error("... not on final argument")
+ }
+ if p.tok == '.' {
+ p.expectSpecial("...")
+ *isVariadic = true
+ }
+ }
+ ptyp := p.parseType()
+ par := ast.NewObj(ast.Var, name)
+ par.Type = ptyp
+ scope.Insert(par)
+}
+
+
+// Parameters = "(" [ ParameterList ] ")" .
+// ParameterList = { Parameter "," } Parameter .
+//
+func (p *gcParser) parseParameters(scope *ast.Scope, isVariadic *bool) {
+ p.expect('(')
+ if p.tok != ')' {
+ p.parseParameter(scope, isVariadic)
+ for p.tok == ',' {
+ p.next()
+ p.parseParameter(scope, isVariadic)
+ }
+ }
+ p.expect(')')
+}
+
+
+// Signature = Parameters [ Result ] .
+// Result = Type | Parameters .
+//
+func (p *gcParser) parseSignature(scope *ast.Scope, isVariadic *bool) {
+ p.parseParameters(scope, isVariadic)
+
+ // optional result type
+ switch p.tok {
+ case scanner.Ident, scanner.String, '[', '*', '<':
+ // single, unnamed result
+ result := ast.NewObj(ast.Var, "_")
+ result.Type = p.parseType()
+ scope.Insert(result)
+ case '(':
+ // named or multiple result(s)
+ p.parseParameters(scope, nil)
+ }
+}
+
+
+// FuncType = "func" Signature .
+//
+func (p *gcParser) parseFuncType() Type {
+ // "func" already consumed
+ scope := ast.NewScope(nil)
+ isVariadic := false
+ p.parseSignature(scope, &isVariadic)
+ return &Func{IsVariadic: isVariadic}
+}
+
+
+// MethodSpec = identifier Signature .
+//
+func (p *gcParser) parseMethodSpec(scope *ast.Scope) {
+ p.expect(scanner.Ident)
+ isVariadic := false
+ p.parseSignature(scope, &isVariadic)
+}
+
+
+// InterfaceType = "interface" "{" [ MethodList ] "}" .
+// MethodList = MethodSpec { ";" MethodSpec } .
+//
+func (p *gcParser) parseInterfaceType() Type {
+ p.expectKeyword("interface")
+ p.expect('{')
+ scope := ast.NewScope(nil)
+ if p.tok != '}' {
+ p.parseMethodSpec(scope)
+ for p.tok == ';' {
+ p.next()
+ p.parseMethodSpec(scope)
+ }
+ }
+ p.expect('}')
+ return &Interface{}
+}
+
+
+// ChanType = ( "chan" [ "<-" ] | "<-" "chan" ) Type .
+//
+func (p *gcParser) parseChanType() Type {
+ dir := ast.SEND | ast.RECV
+ if p.tok == scanner.Ident {
+ p.expectKeyword("chan")
+ if p.tok == '<' {
+ p.expectSpecial("<-")
+ dir = ast.SEND
+ }
+ } else {
+ p.expectSpecial("<-")
+ p.expectKeyword("chan")
+ dir = ast.RECV
+ }
+ elt := p.parseType()
+ return &Chan{Dir: dir, Elt: elt}
+}
+
+
+// Type =
+// BasicType | TypeName | ArrayType | SliceType | StructType |
+// PointerType | FuncType | InterfaceType | MapType | ChanType |
+// "(" Type ")" .
+// BasicType = ident .
+// TypeName = ExportedName .
+// SliceType = "[" "]" Type .
+// PointerType = "*" Type .
+//
+func (p *gcParser) parseType() Type {
+ switch p.tok {
+ case scanner.Ident:
+ switch p.lit {
+ default:
+ return p.parseBasicType()
+ case "struct":
+ return p.parseStructType()
+ case "func":
+ p.next() // parseFuncType assumes "func" is already consumed
+ return p.parseFuncType()
+ case "interface":
+ return p.parseInterfaceType()
+ case "map":
+ return p.parseMapType()
+ case "chan":
+ return p.parseChanType()
+ }
+ case scanner.String:
+ // TypeName
+ return p.parseExportedName(ast.Typ).Type.(Type)
+ case '[':
+ p.next() // look ahead
+ if p.tok == ']' {
+ // SliceType
+ p.next()
+ return &Slice{Elt: p.parseType()}
+ }
+ return p.parseArrayType()
+ case '*':
+ // PointerType
+ p.next()
+ return &Pointer{Base: p.parseType()}
+ case '<':
+ return p.parseChanType()
+ case '(':
+ // "(" Type ")"
+ p.next()
+ typ := p.parseType()
+ p.expect(')')
+ return typ
+ }
+ p.errorf("expected type, got %s (%q)", scanner.TokenString(p.tok), p.lit)
+ return nil
+}
+
+
+// ----------------------------------------------------------------------------
+// Declarations
+
+// ImportDecl = "import" identifier string_lit .
+//
+func (p *gcParser) parseImportDecl() {
+ p.expectKeyword("import")
+ // The identifier has no semantic meaning in the import data.
+ // It exists so that error messages can print the real package
+ // name: binary.ByteOrder instead of "encoding/binary".ByteOrder.
+ // TODO(gri): Save package id -> package name mapping.
+ p.expect(scanner.Ident)
+ p.parsePkgId()
+}
+
+
+// int_lit = [ "+" | "-" ] { "0" ... "9" } .
+//
+func (p *gcParser) parseInt() (sign, val string) {
+ switch p.tok {
+ case '-':
+ p.next()
+ sign = "-"
+ case '+':
+ p.next()
+ }
+ val = p.expect(scanner.Int)
+ return
+}
+
+
+// number = int_lit [ "p" int_lit ] .
+//
+func (p *gcParser) parseNumber() Const {
+ // mantissa
+ sign, val := p.parseInt()
+ mant, ok := new(big.Int).SetString(sign+val, 10)
+ assert(ok)
+
+ if p.lit == "p" {
+ // exponent (base 2)
+ p.next()
+ sign, val = p.parseInt()
+ exp, err := strconv.Atoui(val)
+ if err != nil {
+ p.error(err)
+ }
+ if sign == "-" {
+ denom := big.NewInt(1)
+ denom.Lsh(denom, exp)
+ return Const{new(big.Rat).SetFrac(mant, denom)}
+ }
+ if exp > 0 {
+ mant.Lsh(mant, exp)
+ }
+ return Const{new(big.Rat).SetInt(mant)}
+ }
+
+ return Const{mant}
+}
+
+
+// ConstDecl = "const" ExportedName [ Type ] "=" Literal .
+// Literal = bool_lit | int_lit | float_lit | complex_lit | string_lit .
+// bool_lit = "true" | "false" .
+// complex_lit = "(" float_lit "+" float_lit ")" .
+// string_lit = `"` { unicode_char } `"` .
+//
+func (p *gcParser) parseConstDecl() {
+ p.expectKeyword("const")
+ obj := p.parseExportedName(ast.Con)
+ var x Const
+ var typ Type
+ if p.tok != '=' {
+ obj.Type = p.parseType()
+ }
+ p.expect('=')
+ switch p.tok {
+ case scanner.Ident:
+ // bool_lit
+ if p.lit != "true" && p.lit != "false" {
+ p.error("expected true or false")
+ }
+ x = Const{p.lit == "true"}
+ typ = Bool.Underlying
+ p.next()
+ case '-', scanner.Int:
+ // int_lit
+ x = p.parseNumber()
+ typ = Int.Underlying
+ if _, ok := x.val.(*big.Rat); ok {
+ typ = Float64.Underlying
+ }
+ case '(':
+ // complex_lit
+ p.next()
+ re := p.parseNumber()
+ p.expect('+')
+ im := p.parseNumber()
+ p.expect(')')
+ x = Const{cmplx{re.val.(*big.Rat), im.val.(*big.Rat)}}
+ typ = Complex128.Underlying
+ case scanner.String:
+ // string_lit
+ x = MakeConst(token.STRING, p.lit)
+ p.next()
+ typ = String.Underlying
+ default:
+ p.error("expected literal")
+ }
+ if obj.Type == nil {
+ obj.Type = typ
+ }
+ _ = x // TODO(gri) store x somewhere
+}
+
+
+// TypeDecl = "type" ExportedName Type .
+//
+func (p *gcParser) parseTypeDecl() {
+ p.expectKeyword("type")
+ obj := p.parseExportedName(ast.Typ)
+ typ := p.parseType()
+
+ name := obj.Type.(*Name)
+ assert(name.Underlying == nil)
+ assert(Underlying(typ) == typ)
+ name.Underlying = typ
+}
+
+
+// VarDecl = "var" ExportedName Type .
+//
+func (p *gcParser) parseVarDecl() {
+ p.expectKeyword("var")
+ obj := p.parseExportedName(ast.Var)
+ obj.Type = p.parseType()
+}
+
+
+// FuncDecl = "func" ExportedName Signature .
+//
+func (p *gcParser) parseFuncDecl() {
+ // "func" already consumed
+ obj := p.parseExportedName(ast.Fun)
+ obj.Type = p.parseFuncType()
+}
+
+
+// MethodDecl = "func" Receiver identifier Signature .
+// Receiver = "(" ( identifier | "?" ) [ "*" ] ExportedName ")" .
+//
+func (p *gcParser) parseMethodDecl() {
+ // "func" already consumed
+ scope := ast.NewScope(nil) // method scope
+ p.expect('(')
+ p.parseParameter(scope, nil) // receiver
+ p.expect(')')
+ p.expect(scanner.Ident)
+ isVariadic := false
+ p.parseSignature(scope, &isVariadic)
+
+}
+
+
+// Decl = [ ImportDecl | ConstDecl | TypeDecl | VarDecl | FuncDecl | MethodDecl ] "\n" .
+//
+func (p *gcParser) parseDecl() {
+ switch p.lit {
+ case "import":
+ p.parseImportDecl()
+ case "const":
+ p.parseConstDecl()
+ case "type":
+ p.parseTypeDecl()
+ case "var":
+ p.parseVarDecl()
+ case "func":
+ p.next() // look ahead
+ if p.tok == '(' {
+ p.parseMethodDecl()
+ } else {
+ p.parseFuncDecl()
+ }
+ }
+ p.expect('\n')
+}
+
+
+// ----------------------------------------------------------------------------
+// Export
+
+// Export = "PackageClause { Decl } "$$" .
+// PackageClause = "package" identifier [ "safe" ] "\n" .
+//
+func (p *gcParser) parseExport() (string, *ast.Scope) {
+ p.expectKeyword("package")
+ name := p.expect(scanner.Ident)
+ if p.tok != '\n' {
+ // A package is safe if it was compiled with the -u flag,
+ // which disables the unsafe package.
+ // TODO(gri) remember "safe" package
+ p.expectKeyword("safe")
+ }
+ p.expect('\n')
+
+ for p.tok != '$' && p.tok != scanner.EOF {
+ p.parseDecl()
+ }
+
+ if ch := p.scanner.Peek(); p.tok != '$' || ch != '$' {
+ // don't call next()/expect() since reading past the
+ // export data may cause scanner errors (e.g. NUL chars)
+ p.errorf("expected '$$', got %s %c", scanner.TokenString(p.tok), ch)
+ }
+
+ if n := p.scanner.ErrorCount; n != 0 {
+ p.errorf("expected no scanner errors, got %d", n)
+ }
+
+ return name, p.scope
+}
diff --git a/src/pkg/go/types/gcimporter_test.go b/src/pkg/go/types/gcimporter_test.go
new file mode 100644
index 000000000..556e761df
--- /dev/null
+++ b/src/pkg/go/types/gcimporter_test.go
@@ -0,0 +1,111 @@
+// 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 types
+
+import (
+ "exec"
+ "io/ioutil"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "testing"
+ "time"
+)
+
+
+var gcName, gcPath string // compiler name and path
+
+func init() {
+ // determine compiler
+ switch runtime.GOARCH {
+ case "386":
+ gcName = "8g"
+ case "amd64":
+ gcName = "6g"
+ case "arm":
+ gcName = "5g"
+ default:
+ gcName = "unknown-GOARCH-compiler"
+ gcPath = gcName
+ return
+ }
+ gcPath, _ = exec.LookPath(gcName)
+}
+
+
+func compile(t *testing.T, dirname, filename string) {
+ cmd, err := exec.Run(gcPath, []string{gcPath, filename}, nil, dirname, exec.DevNull, exec.Pipe, exec.MergeWithStdout)
+ if err != nil {
+ t.Errorf("%s %s failed: %s", gcName, filename, err)
+ return
+ }
+ defer cmd.Close()
+
+ msg, err := cmd.Wait(0)
+ if err != nil {
+ t.Errorf("%s %s failed: %s", gcName, filename, err)
+ return
+ }
+
+ if !msg.Exited() || msg.ExitStatus() != 0 {
+ t.Errorf("%s %s failed: exit status = %d", gcName, filename, msg.ExitStatus())
+ output, _ := ioutil.ReadAll(cmd.Stdout)
+ t.Log(string(output))
+ }
+}
+
+
+func testPath(t *testing.T, path string) bool {
+ _, _, err := GcImporter(path)
+ if err != nil {
+ t.Errorf("testPath(%s): %s", path, err)
+ return false
+ }
+ return true
+}
+
+
+const maxTime = 3e9 // maximum allotted testing time in ns
+
+func testDir(t *testing.T, dir string, endTime int64) (nimports int) {
+ dirname := filepath.Join(pkgRoot, dir)
+ list, err := ioutil.ReadDir(dirname)
+ if err != nil {
+ t.Errorf("testDir(%s): %s", dirname, err)
+ }
+ for _, f := range list {
+ if time.Nanoseconds() >= endTime {
+ t.Log("testing time used up")
+ return
+ }
+ switch {
+ case f.IsRegular():
+ // try extensions
+ for _, ext := range pkgExts {
+ if strings.HasSuffix(f.Name, ext) {
+ name := f.Name[0 : len(f.Name)-len(ext)] // remove extension
+ if testPath(t, filepath.Join(dir, name)) {
+ nimports++
+ }
+ }
+ }
+ case f.IsDirectory():
+ nimports += testDir(t, filepath.Join(dir, f.Name), endTime)
+ }
+ }
+ return
+}
+
+
+func TestGcImport(t *testing.T) {
+ compile(t, "testdata", "exports.go")
+
+ nimports := 0
+ if testPath(t, "./testdata/exports") {
+ nimports++
+ }
+ nimports += testDir(t, "", time.Nanoseconds()+maxTime) // installed packages
+ t.Logf("tested %d imports", nimports)
+}
diff --git a/src/pkg/go/types/testdata/exports.go b/src/pkg/go/types/testdata/exports.go
new file mode 100644
index 000000000..13efe012a
--- /dev/null
+++ b/src/pkg/go/types/testdata/exports.go
@@ -0,0 +1,89 @@
+// 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.
+
+// This file is used to generate a .6 object file which
+// serves as test file for gcimporter_test.go.
+
+package exports
+
+import (
+ "go/ast"
+)
+
+
+const (
+ C0 int = 0
+ C1 = 3.14159265
+ C2 = 2.718281828i
+ C3 = -123.456e-789
+ C4 = +123.456E+789
+ C5 = 1234i
+ C6 = "foo\n"
+ C7 = `bar\n`
+)
+
+
+type (
+ T1 int
+ T2 [10]int
+ T3 []int
+ T4 *int
+ T5 chan int
+ T6a chan<- int
+ T6b chan (<-chan int)
+ T6c chan<- (chan int)
+ T7 <-chan *ast.File
+ T8 struct{}
+ T9 struct {
+ a int
+ b, c float32
+ d []string "tag"
+ }
+ T10 struct {
+ T8
+ T9
+ _ *T10
+ }
+ T11 map[int]string
+ T12 interface{}
+ T13 interface {
+ m1()
+ m2(int) float32
+ }
+ T14 interface {
+ T12
+ T13
+ m3(x ...struct{}) []T9
+ }
+ T15 func()
+ T16 func(int)
+ T17 func(x int)
+ T18 func() float32
+ T19 func() (x float32)
+ T20 func(...interface{})
+ T21 struct{ next *T21 }
+ T22 struct{ link *T23 }
+ T23 struct{ link *T22 }
+ T24 *T24
+ T25 *T26
+ T26 *T27
+ T27 *T25
+ T28 func(T28) T28
+)
+
+
+var (
+ V0 int
+ V1 = -991.0
+)
+
+
+func F1() {}
+func F2(x int) {}
+func F3() int { return 0 }
+func F4() float32 { return 0 }
+func F5(a, b, c int, u, v, w struct{ x, y T1 }, more ...interface{}) (p, q, r chan<- T10)
+
+
+func (p *T1) M1()
diff --git a/src/pkg/go/types/types.go b/src/pkg/go/types/types.go
new file mode 100644
index 000000000..72384e121
--- /dev/null
+++ b/src/pkg/go/types/types.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 UNDER CONSTRUCTION. ANY AND ALL PARTS MAY CHANGE.
+// The types package declares the types used to represent Go types.
+//
+package types
+
+import "go/ast"
+
+
+// All types implement the Type interface.
+type Type interface {
+ isType()
+}
+
+
+// All concrete types embed ImplementsType which
+// ensures that all types implement the Type interface.
+type ImplementsType struct{}
+
+func (t *ImplementsType) isType() {}
+
+
+// A Basic represents a (unnamed) basic type.
+type Basic struct {
+ ImplementsType
+ // TODO(gri) need a field specifying the exact basic type
+}
+
+
+// An Array represents an array type [Len]Elt.
+type Array struct {
+ ImplementsType
+ Len uint64
+ Elt Type
+}
+
+
+// A Slice represents a slice type []Elt.
+type Slice struct {
+ ImplementsType
+ Elt Type
+}
+
+
+// A Struct represents a struct type struct{...}.
+type Struct struct {
+ ImplementsType
+ // TODO(gri) need to remember fields.
+}
+
+
+// A Pointer represents a pointer type *Base.
+type Pointer struct {
+ ImplementsType
+ Base Type
+}
+
+
+// A Func represents a function type func(...) (...).
+type Func struct {
+ ImplementsType
+ IsVariadic bool
+ // TODO(gri) need to remember parameters.
+}
+
+
+// An Interface represents an interface type interface{...}.
+type Interface struct {
+ ImplementsType
+ // TODO(gri) need to remember methods.
+}
+
+
+// A Map represents a map type map[Key]Elt.
+type Map struct {
+ ImplementsType
+ Key, Elt Type
+}
+
+
+// A Chan represents a channel type chan Elt, <-chan Elt, or chan<-Elt.
+type Chan struct {
+ ImplementsType
+ Dir ast.ChanDir
+ Elt Type
+}
+
+
+// A Name represents a named type as declared in a type declaration.
+type Name struct {
+ ImplementsType
+ Underlying Type // nil if not fully declared
+ Obj *ast.Object // corresponding declared object
+ // TODO(gri) need to remember fields and methods.
+}
+
+
+// If typ is a pointer type, Deref returns the pointer's base type;
+// otherwise it returns typ.
+func Deref(typ Type) Type {
+ if typ, ok := typ.(*Pointer); ok {
+ return typ.Base
+ }
+ return typ
+}
+
+
+// Underlying returns the underlying type of a type.
+func Underlying(typ Type) Type {
+ if typ, ok := typ.(*Name); ok {
+ utyp := typ.Underlying
+ if _, ok := utyp.(*Basic); ok {
+ return typ
+ }
+ return utyp
+
+ }
+ return typ
+}
diff --git a/src/pkg/go/types/universe.go b/src/pkg/go/types/universe.go
new file mode 100644
index 000000000..2a54a8ac1
--- /dev/null
+++ b/src/pkg/go/types/universe.go
@@ -0,0 +1,113 @@
+// 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.
+
+// FILE UNDER CONSTRUCTION. ANY AND ALL PARTS MAY CHANGE.
+// This file implements the universe and unsafe package scopes.
+
+package types
+
+import "go/ast"
+
+
+var (
+ scope, // current scope to use for initialization
+ Universe,
+ Unsafe *ast.Scope
+)
+
+
+func define(kind ast.ObjKind, name string) *ast.Object {
+ obj := ast.NewObj(kind, name)
+ if scope.Insert(obj) != nil {
+ panic("types internal error: double declaration")
+ }
+ return obj
+}
+
+
+func defType(name string) *Name {
+ obj := define(ast.Typ, name)
+ typ := &Name{Underlying: &Basic{}, Obj: obj}
+ obj.Type = typ
+ return typ
+}
+
+
+func defConst(name string) {
+ obj := define(ast.Con, name)
+ _ = obj // TODO(gri) fill in other properties
+}
+
+
+func defFun(name string) {
+ obj := define(ast.Fun, name)
+ _ = obj // TODO(gri) fill in other properties
+}
+
+
+var (
+ Bool,
+ Int,
+ Float64,
+ Complex128,
+ String *Name
+)
+
+
+func init() {
+ Universe = ast.NewScope(nil)
+ scope = Universe
+
+ Bool = defType("bool")
+ defType("byte") // TODO(gri) should be an alias for uint8
+ defType("complex64")
+ Complex128 = defType("complex128")
+ defType("float32")
+ Float64 = defType("float64")
+ defType("int8")
+ defType("int16")
+ defType("int32")
+ defType("int64")
+ String = defType("string")
+ defType("uint8")
+ defType("uint16")
+ defType("uint32")
+ defType("uint64")
+ Int = defType("int")
+ defType("uint")
+ defType("uintptr")
+
+ defConst("true")
+ defConst("false")
+ defConst("iota")
+ defConst("nil")
+
+ defFun("append")
+ defFun("cap")
+ defFun("close")
+ defFun("complex")
+ defFun("copy")
+ defFun("imag")
+ defFun("len")
+ defFun("make")
+ defFun("new")
+ defFun("panic")
+ defFun("print")
+ defFun("println")
+ defFun("real")
+ defFun("recover")
+
+ Unsafe = ast.NewScope(nil)
+ scope = Unsafe
+ defType("Pointer")
+
+ defFun("Alignof")
+ defFun("New")
+ defFun("NewArray")
+ defFun("Offsetof")
+ defFun("Reflect")
+ defFun("Sizeof")
+ defFun("Typeof")
+ defFun("Unreflect")
+}
diff --git a/src/pkg/gob/codec_test.go b/src/pkg/gob/codec_test.go
index 4562e1930..28042ccaa 100644
--- a/src/pkg/gob/codec_test.go
+++ b/src/pkg/gob/codec_test.go
@@ -50,7 +50,7 @@ func testError(t *testing.T) {
func TestUintCodec(t *testing.T) {
defer testError(t)
b := new(bytes.Buffer)
- encState := newEncoderState(nil, b)
+ encState := newEncoderState(b)
for _, tt := range encodeT {
b.Reset()
encState.encodeUint(tt.x)
@@ -58,7 +58,7 @@ func TestUintCodec(t *testing.T) {
t.Errorf("encodeUint: %#x encode: expected % x got % x", tt.x, tt.b, b.Bytes())
}
}
- decState := newDecodeState(nil, b)
+ decState := newDecodeState(b)
for u := uint64(0); ; u = (u + 1) * 7 {
b.Reset()
encState.encodeUint(u)
@@ -75,9 +75,9 @@ func TestUintCodec(t *testing.T) {
func verifyInt(i int64, t *testing.T) {
defer testError(t)
var b = new(bytes.Buffer)
- encState := newEncoderState(nil, b)
+ encState := newEncoderState(b)
encState.encodeInt(i)
- decState := newDecodeState(nil, b)
+ decState := newDecodeState(b)
decState.buf = make([]byte, 8)
j := decState.decodeInt()
if i != j {
@@ -111,9 +111,16 @@ var complexResult = []byte{0x07, 0xFE, 0x31, 0x40, 0xFE, 0x33, 0x40}
// The result of encoding "hello" with field number 7
var bytesResult = []byte{0x07, 0x05, 'h', 'e', 'l', 'l', 'o'}
-func newencoderState(b *bytes.Buffer) *encoderState {
+func newDecodeState(buf *bytes.Buffer) *decoderState {
+ d := new(decoderState)
+ d.b = buf
+ d.buf = make([]byte, uint64Size)
+ return d
+}
+
+func newEncoderState(b *bytes.Buffer) *encoderState {
b.Reset()
- state := newEncoderState(nil, b)
+ state := &encoderState{enc: nil, b: b}
state.fieldnum = -1
return state
}
@@ -127,7 +134,7 @@ func TestScalarEncInstructions(t *testing.T) {
{
data := struct{ a bool }{true}
instr := &encInstr{encBool, 6, 0, 0}
- state := newencoderState(b)
+ state := newEncoderState(b)
instr.op(instr, state, unsafe.Pointer(&data))
if !bytes.Equal(boolResult, b.Bytes()) {
t.Errorf("bool enc instructions: expected % x got % x", boolResult, b.Bytes())
@@ -139,7 +146,7 @@ func TestScalarEncInstructions(t *testing.T) {
b.Reset()
data := struct{ a int }{17}
instr := &encInstr{encInt, 6, 0, 0}
- state := newencoderState(b)
+ state := newEncoderState(b)
instr.op(instr, state, unsafe.Pointer(&data))
if !bytes.Equal(signedResult, b.Bytes()) {
t.Errorf("int enc instructions: expected % x got % x", signedResult, b.Bytes())
@@ -151,7 +158,7 @@ func TestScalarEncInstructions(t *testing.T) {
b.Reset()
data := struct{ a uint }{17}
instr := &encInstr{encUint, 6, 0, 0}
- state := newencoderState(b)
+ state := newEncoderState(b)
instr.op(instr, state, unsafe.Pointer(&data))
if !bytes.Equal(unsignedResult, b.Bytes()) {
t.Errorf("uint enc instructions: expected % x got % x", unsignedResult, b.Bytes())
@@ -163,7 +170,7 @@ func TestScalarEncInstructions(t *testing.T) {
b.Reset()
data := struct{ a int8 }{17}
instr := &encInstr{encInt8, 6, 0, 0}
- state := newencoderState(b)
+ state := newEncoderState(b)
instr.op(instr, state, unsafe.Pointer(&data))
if !bytes.Equal(signedResult, b.Bytes()) {
t.Errorf("int8 enc instructions: expected % x got % x", signedResult, b.Bytes())
@@ -175,7 +182,7 @@ func TestScalarEncInstructions(t *testing.T) {
b.Reset()
data := struct{ a uint8 }{17}
instr := &encInstr{encUint8, 6, 0, 0}
- state := newencoderState(b)
+ state := newEncoderState(b)
instr.op(instr, state, unsafe.Pointer(&data))
if !bytes.Equal(unsignedResult, b.Bytes()) {
t.Errorf("uint8 enc instructions: expected % x got % x", unsignedResult, b.Bytes())
@@ -187,7 +194,7 @@ func TestScalarEncInstructions(t *testing.T) {
b.Reset()
data := struct{ a int16 }{17}
instr := &encInstr{encInt16, 6, 0, 0}
- state := newencoderState(b)
+ state := newEncoderState(b)
instr.op(instr, state, unsafe.Pointer(&data))
if !bytes.Equal(signedResult, b.Bytes()) {
t.Errorf("int16 enc instructions: expected % x got % x", signedResult, b.Bytes())
@@ -199,7 +206,7 @@ func TestScalarEncInstructions(t *testing.T) {
b.Reset()
data := struct{ a uint16 }{17}
instr := &encInstr{encUint16, 6, 0, 0}
- state := newencoderState(b)
+ state := newEncoderState(b)
instr.op(instr, state, unsafe.Pointer(&data))
if !bytes.Equal(unsignedResult, b.Bytes()) {
t.Errorf("uint16 enc instructions: expected % x got % x", unsignedResult, b.Bytes())
@@ -211,7 +218,7 @@ func TestScalarEncInstructions(t *testing.T) {
b.Reset()
data := struct{ a int32 }{17}
instr := &encInstr{encInt32, 6, 0, 0}
- state := newencoderState(b)
+ state := newEncoderState(b)
instr.op(instr, state, unsafe.Pointer(&data))
if !bytes.Equal(signedResult, b.Bytes()) {
t.Errorf("int32 enc instructions: expected % x got % x", signedResult, b.Bytes())
@@ -223,7 +230,7 @@ func TestScalarEncInstructions(t *testing.T) {
b.Reset()
data := struct{ a uint32 }{17}
instr := &encInstr{encUint32, 6, 0, 0}
- state := newencoderState(b)
+ state := newEncoderState(b)
instr.op(instr, state, unsafe.Pointer(&data))
if !bytes.Equal(unsignedResult, b.Bytes()) {
t.Errorf("uint32 enc instructions: expected % x got % x", unsignedResult, b.Bytes())
@@ -235,7 +242,7 @@ func TestScalarEncInstructions(t *testing.T) {
b.Reset()
data := struct{ a int64 }{17}
instr := &encInstr{encInt64, 6, 0, 0}
- state := newencoderState(b)
+ state := newEncoderState(b)
instr.op(instr, state, unsafe.Pointer(&data))
if !bytes.Equal(signedResult, b.Bytes()) {
t.Errorf("int64 enc instructions: expected % x got % x", signedResult, b.Bytes())
@@ -247,7 +254,7 @@ func TestScalarEncInstructions(t *testing.T) {
b.Reset()
data := struct{ a uint64 }{17}
instr := &encInstr{encUint64, 6, 0, 0}
- state := newencoderState(b)
+ state := newEncoderState(b)
instr.op(instr, state, unsafe.Pointer(&data))
if !bytes.Equal(unsignedResult, b.Bytes()) {
t.Errorf("uint64 enc instructions: expected % x got % x", unsignedResult, b.Bytes())
@@ -259,7 +266,7 @@ func TestScalarEncInstructions(t *testing.T) {
b.Reset()
data := struct{ a float32 }{17}
instr := &encInstr{encFloat32, 6, 0, 0}
- state := newencoderState(b)
+ state := newEncoderState(b)
instr.op(instr, state, unsafe.Pointer(&data))
if !bytes.Equal(floatResult, b.Bytes()) {
t.Errorf("float32 enc instructions: expected % x got % x", floatResult, b.Bytes())
@@ -271,7 +278,7 @@ func TestScalarEncInstructions(t *testing.T) {
b.Reset()
data := struct{ a float64 }{17}
instr := &encInstr{encFloat64, 6, 0, 0}
- state := newencoderState(b)
+ state := newEncoderState(b)
instr.op(instr, state, unsafe.Pointer(&data))
if !bytes.Equal(floatResult, b.Bytes()) {
t.Errorf("float64 enc instructions: expected % x got % x", floatResult, b.Bytes())
@@ -283,7 +290,7 @@ func TestScalarEncInstructions(t *testing.T) {
b.Reset()
data := struct{ a []byte }{[]byte("hello")}
instr := &encInstr{encUint8Array, 6, 0, 0}
- state := newencoderState(b)
+ state := newEncoderState(b)
instr.op(instr, state, unsafe.Pointer(&data))
if !bytes.Equal(bytesResult, b.Bytes()) {
t.Errorf("bytes enc instructions: expected % x got % x", bytesResult, b.Bytes())
@@ -295,7 +302,7 @@ func TestScalarEncInstructions(t *testing.T) {
b.Reset()
data := struct{ a string }{"hello"}
instr := &encInstr{encString, 6, 0, 0}
- state := newencoderState(b)
+ state := newEncoderState(b)
instr.op(instr, state, unsafe.Pointer(&data))
if !bytes.Equal(bytesResult, b.Bytes()) {
t.Errorf("string enc instructions: expected % x got % x", bytesResult, b.Bytes())
@@ -315,7 +322,7 @@ func execDec(typ string, instr *decInstr, state *decoderState, t *testing.T, p u
func newDecodeStateFromData(data []byte) *decoderState {
b := bytes.NewBuffer(data)
- state := newDecodeState(nil, b)
+ state := newDecodeState(b)
state.fieldnum = -1
return state
}
@@ -997,9 +1004,9 @@ func TestInvalidField(t *testing.T) {
var bad0 Bad0
bad0.CH = make(chan int)
b := new(bytes.Buffer)
- var nilEncoder *Encoder
- err := nilEncoder.encode(b, reflect.NewValue(&bad0), userType(reflect.Typeof(&bad0)))
- if err == nil {
+ dummyEncoder := new(Encoder) // sufficient for this purpose.
+ dummyEncoder.encode(b, reflect.NewValue(&bad0), userType(reflect.Typeof(&bad0)))
+ if err := dummyEncoder.err; err == nil {
t.Error("expected error; got none")
} else if strings.Index(err.String(), "type") < 0 {
t.Error("expected type error; got", err)
diff --git a/src/pkg/gob/decode.go b/src/pkg/gob/decode.go
index b7ae78200..51fac798d 100644
--- a/src/pkg/gob/decode.go
+++ b/src/pkg/gob/decode.go
@@ -19,7 +19,7 @@ import (
var (
errBadUint = os.ErrorString("gob: encoded unsigned integer out of range")
errBadType = os.ErrorString("gob: unknown type id or corrupted data")
- errRange = os.ErrorString("gob: internal error: field numbers out of bounds")
+ errRange = os.ErrorString("gob: bad data: field numbers out of bounds")
)
// decoderState is the execution state of an instance of the decoder. A new state
@@ -31,18 +31,29 @@ type decoderState struct {
b *bytes.Buffer
fieldnum int // the last field number read.
buf []byte
+ next *decoderState // for free list
}
// We pass the bytes.Buffer separately for easier testing of the infrastructure
// without requiring a full Decoder.
-func newDecodeState(dec *Decoder, buf *bytes.Buffer) *decoderState {
- d := new(decoderState)
- d.dec = dec
+func (dec *Decoder) newDecoderState(buf *bytes.Buffer) *decoderState {
+ d := dec.freeList
+ if d == nil {
+ d = new(decoderState)
+ d.dec = dec
+ d.buf = make([]byte, uint64Size)
+ } else {
+ dec.freeList = d.next
+ }
d.b = buf
- d.buf = make([]byte, uint64Size)
return d
}
+func (dec *Decoder) freeDecoderState(d *decoderState) {
+ d.next = dec.freeList
+ dec.freeList = d
+}
+
func overflow(name string) os.ErrorString {
return os.ErrorString(`value for "` + name + `" out of range`)
}
@@ -401,7 +412,14 @@ func decString(i *decInstr, state *decoderState, p unsafe.Pointer) {
}
b := make([]byte, state.decodeUint())
state.b.Read(b)
- *(*string)(p) = string(b)
+ // It would be a shame to do the obvious thing here,
+ // *(*string)(p) = string(b)
+ // because we've already allocated the storage and this would
+ // allocate again and copy. So we do this ugly hack, which is even
+ // even more unsafe than it looks as it depends the memory
+ // representation of a string matching the beginning of the memory
+ // representation of a byte slice (a byte slice is longer).
+ *(*string)(p) = *(*string)(unsafe.Pointer(&b))
}
// ignoreUint8Array skips over the data for a byte slice value with no destination.
@@ -445,7 +463,7 @@ func (dec *Decoder) decodeSingle(engine *decEngine, ut *userTypeInfo, p uintptr)
indir = int(ut.decIndir)
}
p = allocate(ut.base, p, indir)
- state := newDecodeState(dec, &dec.buf)
+ state := dec.newDecoderState(&dec.buf)
state.fieldnum = singletonField
basep := p
delta := int(state.decodeUint())
@@ -458,6 +476,7 @@ func (dec *Decoder) decodeSingle(engine *decEngine, ut *userTypeInfo, p uintptr)
ptr = decIndirect(ptr, instr.indir)
}
instr.op(instr, state, ptr)
+ dec.freeDecoderState(state)
return nil
}
@@ -466,9 +485,9 @@ func (dec *Decoder) decodeSingle(engine *decEngine, ut *userTypeInfo, p uintptr)
// differ from ut.indir, which was computed when the engine was built.
// This state cannot arise for decodeSingle, which is called directly
// from the user's value, not from the innards of an engine.
-func (dec *Decoder) decodeStruct(engine *decEngine, ut *userTypeInfo, p uintptr, indir int) (err os.Error) {
- p = allocate(ut.base.(*reflect.StructType), p, indir)
- state := newDecodeState(dec, &dec.buf)
+func (dec *Decoder) decodeStruct(engine *decEngine, ut *userTypeInfo, p uintptr, indir int) {
+ p = allocate(ut.base, p, indir)
+ state := dec.newDecoderState(&dec.buf)
state.fieldnum = -1
basep := p
for state.b.Len() > 0 {
@@ -492,12 +511,12 @@ func (dec *Decoder) decodeStruct(engine *decEngine, ut *userTypeInfo, p uintptr,
instr.op(instr, state, p)
state.fieldnum = fieldnum
}
- return nil
+ dec.freeDecoderState(state)
}
// ignoreStruct discards the data for a struct with no destination.
-func (dec *Decoder) ignoreStruct(engine *decEngine) (err os.Error) {
- state := newDecodeState(dec, &dec.buf)
+func (dec *Decoder) ignoreStruct(engine *decEngine) {
+ state := dec.newDecoderState(&dec.buf)
state.fieldnum = -1
for state.b.Len() > 0 {
delta := int(state.decodeUint())
@@ -515,13 +534,13 @@ func (dec *Decoder) ignoreStruct(engine *decEngine) (err os.Error) {
instr.op(instr, state, unsafe.Pointer(nil))
state.fieldnum = fieldnum
}
- return nil
+ dec.freeDecoderState(state)
}
// ignoreSingle discards the data for a top-level non-struct value with no
// destination. It's used when calling Decode with a nil value.
-func (dec *Decoder) ignoreSingle(engine *decEngine) (err os.Error) {
- state := newDecodeState(dec, &dec.buf)
+func (dec *Decoder) ignoreSingle(engine *decEngine) {
+ state := dec.newDecoderState(&dec.buf)
state.fieldnum = singletonField
delta := int(state.decodeUint())
if delta != 0 {
@@ -529,7 +548,7 @@ func (dec *Decoder) ignoreSingle(engine *decEngine) (err os.Error) {
}
instr := &engine.instr[singletonField]
instr.op(instr, state, unsafe.Pointer(nil))
- return nil
+ dec.freeDecoderState(state)
}
// decodeArrayHelper does the work for decoding arrays and slices.
@@ -548,7 +567,7 @@ func (dec *Decoder) decodeArrayHelper(state *decoderState, p uintptr, elemOp dec
// decodeArray decodes an array and stores it through p, that is, p points to the zeroth element.
// The length is an unsigned integer preceding the elements. Even though the length is redundant
// (it's part of the type), it's a useful check and is included in the encoding.
-func (dec *Decoder) decodeArray(atyp *reflect.ArrayType, state *decoderState, p uintptr, elemOp decOp, elemWid uintptr, length, indir, elemIndir int, ovfl os.ErrorString) {
+func (dec *Decoder) decodeArray(atyp reflect.Type, state *decoderState, p uintptr, elemOp decOp, elemWid uintptr, length, indir, elemIndir int, ovfl os.ErrorString) {
if indir > 0 {
p = allocate(atyp, p, 1) // All but the last level has been allocated by dec.Indirect
}
@@ -574,24 +593,24 @@ func decodeIntoValue(state *decoderState, op decOp, indir int, v reflect.Value,
// Maps are encoded as a length followed by key:value pairs.
// Because the internals of maps are not visible to us, we must
// use reflection rather than pointer magic.
-func (dec *Decoder) decodeMap(mtyp *reflect.MapType, state *decoderState, p uintptr, keyOp, elemOp decOp, indir, keyIndir, elemIndir int, ovfl os.ErrorString) {
+func (dec *Decoder) decodeMap(mtyp reflect.Type, state *decoderState, p uintptr, keyOp, elemOp decOp, indir, keyIndir, elemIndir int, ovfl os.ErrorString) {
if indir > 0 {
p = allocate(mtyp, p, 1) // All but the last level has been allocated by dec.Indirect
}
up := unsafe.Pointer(p)
if *(*unsafe.Pointer)(up) == nil { // maps are represented as a pointer in the runtime
// Allocate map.
- *(*unsafe.Pointer)(up) = unsafe.Pointer(reflect.MakeMap(mtyp).Get())
+ *(*unsafe.Pointer)(up) = unsafe.Pointer(reflect.MakeMap(mtyp).Pointer())
}
// Maps cannot be accessed by moving addresses around the way
// that slices etc. can. We must recover a full reflection value for
// the iteration.
- v := reflect.NewValue(unsafe.Unreflect(mtyp, unsafe.Pointer(p))).(*reflect.MapValue)
+ v := reflect.NewValue(unsafe.Unreflect(mtyp, unsafe.Pointer(p)))
n := int(state.decodeUint())
for i := 0; i < n; i++ {
- key := decodeIntoValue(state, keyOp, keyIndir, reflect.MakeZero(mtyp.Key()), ovfl)
- elem := decodeIntoValue(state, elemOp, elemIndir, reflect.MakeZero(mtyp.Elem()), ovfl)
- v.SetElem(key, elem)
+ key := decodeIntoValue(state, keyOp, keyIndir, reflect.Zero(mtyp.Key()), ovfl)
+ elem := decodeIntoValue(state, elemOp, elemIndir, reflect.Zero(mtyp.Elem()), ovfl)
+ v.SetMapIndex(key, elem)
}
}
@@ -624,7 +643,7 @@ func (dec *Decoder) ignoreMap(state *decoderState, keyOp, elemOp decOp) {
// decodeSlice decodes a slice and stores the slice header through p.
// Slices are encoded as an unsigned length followed by the elements.
-func (dec *Decoder) decodeSlice(atyp *reflect.SliceType, state *decoderState, p uintptr, elemOp decOp, elemWid uintptr, indir, elemIndir int, ovfl os.ErrorString) {
+func (dec *Decoder) decodeSlice(atyp reflect.Type, state *decoderState, p uintptr, elemOp decOp, elemWid uintptr, indir, elemIndir int, ovfl os.ErrorString) {
n := int(uintptr(state.decodeUint()))
if indir > 0 {
up := unsafe.Pointer(p)
@@ -654,7 +673,7 @@ func (dec *Decoder) ignoreSlice(state *decoderState, elemOp decOp) {
// This dance avoids manually checking that the value satisfies the
// interface.
// TODO(rsc): avoid panic+recover after fixing issue 327.
-func setInterfaceValue(ivalue *reflect.InterfaceValue, value reflect.Value) {
+func setInterfaceValue(ivalue reflect.Value, value reflect.Value) {
defer func() {
if e := recover(); e != nil {
error(e.(os.Error))
@@ -666,9 +685,9 @@ func setInterfaceValue(ivalue *reflect.InterfaceValue, value reflect.Value) {
// decodeInterface decodes an interface value and stores it through p.
// Interfaces are encoded as the name of a concrete type followed by a value.
// If the name is empty, the value is nil and no value is sent.
-func (dec *Decoder) decodeInterface(ityp *reflect.InterfaceType, state *decoderState, p uintptr, indir int) {
+func (dec *Decoder) decodeInterface(ityp reflect.Type, state *decoderState, p uintptr, indir int) {
// Create an interface reflect.Value. We need one even for the nil case.
- ivalue := reflect.MakeZero(ityp).(*reflect.InterfaceValue)
+ ivalue := reflect.Zero(ityp)
// Read the name of the concrete type.
b := make([]byte, state.decodeUint())
state.b.Read(b)
@@ -676,7 +695,7 @@ func (dec *Decoder) decodeInterface(ityp *reflect.InterfaceType, state *decoderS
if name == "" {
// Copy the representation of the nil interface value to the target.
// This is horribly unsafe and special.
- *(*[2]uintptr)(unsafe.Pointer(p)) = ivalue.Get()
+ *(*[2]uintptr)(unsafe.Pointer(p)) = ivalue.InterfaceData()
return
}
// The concrete type must be registered.
@@ -693,7 +712,7 @@ func (dec *Decoder) decodeInterface(ityp *reflect.InterfaceType, state *decoderS
// in case we want to ignore the value by skipping it completely).
state.decodeUint()
// Read the concrete value.
- value := reflect.MakeZero(typ)
+ value := reflect.Zero(typ)
dec.decodeValue(concreteId, value)
if dec.err != nil {
error(dec.err)
@@ -707,7 +726,7 @@ func (dec *Decoder) decodeInterface(ityp *reflect.InterfaceType, state *decoderS
setInterfaceValue(ivalue, value)
// Copy the representation of the interface value to the target.
// This is horribly unsafe and special.
- *(*[2]uintptr)(unsafe.Pointer(p)) = ivalue.Get()
+ *(*[2]uintptr)(unsafe.Pointer(p)) = ivalue.InterfaceData()
}
// ignoreInterface discards the data for an interface value with no destination.
@@ -804,8 +823,8 @@ func (dec *Decoder) decOpFor(wireId typeId, rt reflect.Type, name string, inProg
if op == nil {
inProgress[rt] = &op
// Special cases
- switch t := typ.(type) {
- case *reflect.ArrayType:
+ switch t := typ; t.Kind() {
+ case reflect.Array:
name = "element of " + name
elemId := dec.wireType[wireId].ArrayT.Elem
elemOp, elemIndir := dec.decOpFor(elemId, t.Elem(), name, inProgress)
@@ -814,7 +833,7 @@ func (dec *Decoder) decOpFor(wireId typeId, rt reflect.Type, name string, inProg
state.dec.decodeArray(t, state, uintptr(p), *elemOp, t.Elem().Size(), t.Len(), i.indir, elemIndir, ovfl)
}
- case *reflect.MapType:
+ case reflect.Map:
name = "element of " + name
keyId := dec.wireType[wireId].MapT.Key
elemId := dec.wireType[wireId].MapT.Elem
@@ -826,7 +845,7 @@ func (dec *Decoder) decOpFor(wireId typeId, rt reflect.Type, name string, inProg
state.dec.decodeMap(t, state, uintptr(up), *keyOp, *elemOp, i.indir, keyIndir, elemIndir, ovfl)
}
- case *reflect.SliceType:
+ case reflect.Slice:
name = "element of " + name
if t.Elem().Kind() == reflect.Uint8 {
op = decUint8Array
@@ -844,7 +863,7 @@ func (dec *Decoder) decOpFor(wireId typeId, rt reflect.Type, name string, inProg
state.dec.decodeSlice(t, state, uintptr(p), *elemOp, t.Elem().Size(), i.indir, elemIndir, ovfl)
}
- case *reflect.StructType:
+ case reflect.Struct:
// Generate a closure that calls out to the engine for the nested type.
enginePtr, err := dec.getDecEnginePtr(wireId, userType(typ))
if err != nil {
@@ -852,12 +871,9 @@ func (dec *Decoder) decOpFor(wireId typeId, rt reflect.Type, name string, inProg
}
op = func(i *decInstr, state *decoderState, p unsafe.Pointer) {
// indirect through enginePtr to delay evaluation for recursive structs.
- err = dec.decodeStruct(*enginePtr, userType(typ), uintptr(p), i.indir)
- if err != nil {
- error(err)
- }
+ dec.decodeStruct(*enginePtr, userType(typ), uintptr(p), i.indir)
}
- case *reflect.InterfaceType:
+ case reflect.Interface:
op = func(i *decInstr, state *decoderState, p unsafe.Pointer) {
state.dec.decodeInterface(t, state, uintptr(p), i.indir)
}
@@ -885,7 +901,7 @@ func (dec *Decoder) decIgnoreOpFor(wireId typeId) decOp {
wire := dec.wireType[wireId]
switch {
case wire == nil:
- panic("internal error: can't find ignore op for type " + wireId.string())
+ errorf("gob: bad data: undefined type %s", wireId.string())
case wire.ArrayT != nil:
elemId := wire.ArrayT.Elem
elemOp := dec.decIgnoreOpFor(elemId)
@@ -927,7 +943,7 @@ func (dec *Decoder) decIgnoreOpFor(wireId typeId) decOp {
}
}
if op == nil {
- errorf("ignore can't handle type %s", wireId.string())
+ errorf("gob: bad data: ignore can't handle type %s", wireId.string())
}
return op
}
@@ -936,27 +952,29 @@ func (dec *Decoder) decIgnoreOpFor(wireId typeId) decOp {
// GobDecoder.
func (dec *Decoder) gobDecodeOpFor(ut *userTypeInfo) (*decOp, int) {
rt := ut.user
- if ut.decIndir != 0 {
- errorf("gob: TODO: can't handle indirection to reach GobDecoder")
- }
- index := -1
- for i := 0; i < rt.NumMethod(); i++ {
- if rt.Method(i).Name == gobDecodeMethodName {
- index = i
- break
+ if ut.decIndir == -1 {
+ rt = reflect.PtrTo(rt)
+ } else if ut.decIndir > 0 {
+ for i := int8(0); i < ut.decIndir; i++ {
+ rt = rt.Elem()
}
}
- if index < 0 {
- panic("can't find GobDecode method")
- }
var op decOp
op = func(i *decInstr, state *decoderState, p unsafe.Pointer) {
// Allocate the underlying data, but hold on to the address we have,
- // since it's known to be the receiver's address.
- // TODO: fix this up when decIndir can be non-zero.
+ // since we need it to get to the receiver's address.
allocate(ut.base, uintptr(p), ut.indir)
- v := reflect.NewValue(unsafe.Unreflect(rt, p))
- state.dec.decodeGobDecoder(state, v, index)
+ var v reflect.Value
+ if ut.decIndir == -1 {
+ // Need to climb up one level to turn value into pointer.
+ v = reflect.NewValue(unsafe.Unreflect(rt, unsafe.Pointer(&p)))
+ } else {
+ if ut.decIndir > 0 {
+ p = decIndirect(p, int(ut.decIndir))
+ }
+ v = reflect.NewValue(unsafe.Unreflect(rt, p))
+ }
+ state.dec.decodeGobDecoder(state, v, methodIndex(rt, gobDecodeMethodName))
}
return &op, int(ut.decIndir)
@@ -981,37 +999,37 @@ func (dec *Decoder) compatibleType(fr reflect.Type, fw typeId, inProgress map[re
if ut.isGobDecoder { // This test trumps all others.
return true
}
- switch t := ut.base.(type) {
+ switch t := ut.base; t.Kind() {
default:
// chan, etc: cannot handle.
return false
- case *reflect.BoolType:
+ case reflect.Bool:
return fw == tBool
- case *reflect.IntType:
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return fw == tInt
- case *reflect.UintType:
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return fw == tUint
- case *reflect.FloatType:
+ case reflect.Float32, reflect.Float64:
return fw == tFloat
- case *reflect.ComplexType:
+ case reflect.Complex64, reflect.Complex128:
return fw == tComplex
- case *reflect.StringType:
+ case reflect.String:
return fw == tString
- case *reflect.InterfaceType:
+ case reflect.Interface:
return fw == tInterface
- case *reflect.ArrayType:
+ case reflect.Array:
if !ok || wire.ArrayT == nil {
return false
}
array := wire.ArrayT
return t.Len() == array.Len && dec.compatibleType(t.Elem(), array.Elem, inProgress)
- case *reflect.MapType:
+ case reflect.Map:
if !ok || wire.MapT == nil {
return false
}
MapType := wire.MapT
return dec.compatibleType(t.Key(), MapType.Key, inProgress) && dec.compatibleType(t.Elem(), MapType.Elem, inProgress)
- case *reflect.SliceType:
+ case reflect.Slice:
// Is it an array of bytes?
if t.Elem().Kind() == reflect.Uint8 {
return fw == tBytes
@@ -1025,7 +1043,7 @@ func (dec *Decoder) compatibleType(fr reflect.Type, fw typeId, inProgress map[re
}
elem := userType(t.Elem()).base
return sw != nil && dec.compatibleType(elem, sw.Elem, inProgress)
- case *reflect.StructType:
+ case reflect.Struct:
return true
}
return true
@@ -1075,8 +1093,9 @@ func (dec *Decoder) compileIgnoreSingle(remoteId typeId) (engine *decEngine, err
// it calls out to compileSingle.
func (dec *Decoder) compileDec(remoteId typeId, ut *userTypeInfo) (engine *decEngine, err os.Error) {
rt := ut.base
- srt, ok := rt.(*reflect.StructType)
- if !ok || ut.isGobDecoder {
+ srt := rt
+ if srt.Kind() != reflect.Struct ||
+ ut.isGobDecoder {
return dec.compileSingle(remoteId, ut)
}
var wireStruct *structType
@@ -1168,11 +1187,12 @@ func (dec *Decoder) getIgnoreEnginePtr(wireId typeId) (enginePtr **decEngine, er
}
// decodeValue decodes the data stream representing a value and stores it in val.
-func (dec *Decoder) decodeValue(wireId typeId, val reflect.Value) (err os.Error) {
- defer catchError(&err)
+func (dec *Decoder) decodeValue(wireId typeId, val reflect.Value) {
+ defer catchError(&dec.err)
// If the value is nil, it means we should just ignore this item.
- if val == nil {
- return dec.decodeIgnoredValue(wireId)
+ if !val.IsValid() {
+ dec.decodeIgnoredValue(wireId)
+ return
}
// Dereference down to the underlying struct type.
ut := userType(val.Type())
@@ -1180,36 +1200,37 @@ func (dec *Decoder) decodeValue(wireId typeId, val reflect.Value) (err os.Error)
indir := ut.indir
if ut.isGobDecoder {
indir = int(ut.decIndir)
- if indir != 0 {
- errorf("TODO: can't handle indirection in GobDecoder value")
- }
}
- enginePtr, err := dec.getDecEnginePtr(wireId, ut)
- if err != nil {
- return err
+ var enginePtr **decEngine
+ enginePtr, dec.err = dec.getDecEnginePtr(wireId, ut)
+ if dec.err != nil {
+ return
}
engine := *enginePtr
- if st, ok := base.(*reflect.StructType); ok && !ut.isGobDecoder {
+ if st := base; st.Kind() == reflect.Struct && !ut.isGobDecoder {
if engine.numInstr == 0 && st.NumField() > 0 && len(dec.wireType[wireId].StructT.Field) > 0 {
name := base.Name()
- return os.ErrorString("gob: type mismatch: no fields matched compiling decoder for " + name)
+ errorf("gob: type mismatch: no fields matched compiling decoder for %s", name)
}
- return dec.decodeStruct(engine, ut, uintptr(val.UnsafeAddr()), indir)
+ dec.decodeStruct(engine, ut, uintptr(val.UnsafeAddr()), indir)
+ } else {
+ dec.decodeSingle(engine, ut, uintptr(val.UnsafeAddr()))
}
- return dec.decodeSingle(engine, ut, uintptr(val.UnsafeAddr()))
}
// decodeIgnoredValue decodes the data stream representing a value of the specified type and discards it.
-func (dec *Decoder) decodeIgnoredValue(wireId typeId) os.Error {
- enginePtr, err := dec.getIgnoreEnginePtr(wireId)
- if err != nil {
- return err
+func (dec *Decoder) decodeIgnoredValue(wireId typeId) {
+ var enginePtr **decEngine
+ enginePtr, dec.err = dec.getIgnoreEnginePtr(wireId)
+ if dec.err != nil {
+ return
}
wire := dec.wireType[wireId]
if wire != nil && wire.StructT != nil {
- return dec.ignoreStruct(*enginePtr)
+ dec.ignoreStruct(*enginePtr)
+ } else {
+ dec.ignoreSingle(*enginePtr)
}
- return dec.ignoreSingle(*enginePtr)
}
func init() {
diff --git a/src/pkg/gob/decoder.go b/src/pkg/gob/decoder.go
index 719274583..a631c27a2 100644
--- a/src/pkg/gob/decoder.go
+++ b/src/pkg/gob/decoder.go
@@ -5,6 +5,7 @@
package gob
import (
+ "bufio"
"bytes"
"io"
"os"
@@ -21,7 +22,7 @@ type Decoder struct {
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
- countState *decoderState // reads counts from wire
+ 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
@@ -30,7 +31,7 @@ type Decoder struct {
// NewDecoder returns a new decoder that reads from the io.Reader.
func NewDecoder(r io.Reader) *Decoder {
dec := new(Decoder)
- dec.r = r
+ 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)
@@ -49,7 +50,7 @@ func (dec *Decoder) recvType(id typeId) {
// Type:
wire := new(wireType)
- dec.err = dec.decodeValue(tWireType, reflect.NewValue(wire))
+ dec.decodeValue(tWireType, reflect.NewValue(wire))
if dec.err != nil {
return
}
@@ -158,7 +159,7 @@ func (dec *Decoder) decodeTypeSequence(isInterface bool) typeId {
// data item received, and must be a pointer.
func (dec *Decoder) Decode(e interface{}) os.Error {
if e == nil {
- return dec.DecodeValue(nil)
+ return dec.DecodeValue(reflect.Value{})
}
value := reflect.NewValue(e)
// If e represents a value as opposed to a pointer, the answer won't
@@ -184,7 +185,7 @@ func (dec *Decoder) DecodeValue(value reflect.Value) os.Error {
dec.err = nil
id := dec.decodeTypeSequence(false)
if dec.err == nil {
- dec.err = dec.decodeValue(id, value)
+ dec.decodeValue(id, value)
}
return dec.err
}
diff --git a/src/pkg/gob/dump.go b/src/pkg/gob/dump.go
index a05510566..1555f0fbb 100644
--- a/src/pkg/gob/dump.go
+++ b/src/pkg/gob/dump.go
@@ -12,7 +12,7 @@ func main() {
var err os.Error
file := os.Stdin
if len(os.Args) > 1 {
- file, err = os.Open(os.Args[1], os.O_RDONLY, 0)
+ file, err = os.Open(os.Args[1])
if err != nil {
fmt.Fprintf(os.Stderr, "dump: %s\n", err)
os.Exit(1)
diff --git a/src/pkg/gob/encode.go b/src/pkg/gob/encode.go
index 9190d9203..36bde08aa 100644
--- a/src/pkg/gob/encode.go
+++ b/src/pkg/gob/encode.go
@@ -6,9 +6,7 @@ package gob
import (
"bytes"
- "io"
"math"
- "os"
"reflect"
"unsafe"
)
@@ -25,10 +23,26 @@ type encoderState struct {
sendZero bool // encoding an array element or map key/value pair; send zero values
fieldnum int // the last field number written.
buf [1 + uint64Size]byte // buffer used by the encoder; here to avoid allocation.
+ next *encoderState // for free list
}
-func newEncoderState(enc *Encoder, b *bytes.Buffer) *encoderState {
- return &encoderState{enc: enc, b: b}
+func (enc *Encoder) newEncoderState(b *bytes.Buffer) *encoderState {
+ e := enc.freeList
+ if e == nil {
+ e = new(encoderState)
+ e.enc = enc
+ } else {
+ enc.freeList = e.next
+ }
+ e.sendZero = false
+ e.fieldnum = 0
+ e.b = b
+ return e
+}
+
+func (enc *Encoder) freeEncoderState(e *encoderState) {
+ e.next = enc.freeList
+ enc.freeList = e
}
// Unsigned integers have a two-state encoding. If the number is less
@@ -92,11 +106,14 @@ func (state *encoderState) update(instr *encInstr) {
}
}
-// Each encoder is responsible for handling any indirections associated
-// with the data structure. If any pointer so reached is nil, no bytes are written.
-// If the data item is zero, no bytes are written.
-// Otherwise, the output (for a scalar) is the field number, as an encoded integer,
-// followed by the field data in its appropriate format.
+// Each encoder for a composite is responsible for handling any
+// indirections associated with the elements of the data structure.
+// If any pointer so reached is nil, no bytes are written. If the
+// data item is zero, no bytes are written. Single values - ints,
+// strings etc. - are indirected before calling their encoders.
+// Otherwise, the output (for a scalar) is the field number, as an
+// encoded integer, followed by the field data in its appropriate
+// format.
// encIndirect dereferences p indir times and returns the result.
func encIndirect(p unsafe.Pointer, indir int) unsafe.Pointer {
@@ -301,7 +318,7 @@ func encString(i *encInstr, state *encoderState, p unsafe.Pointer) {
if len(s) > 0 || state.sendZero {
state.update(i)
state.encodeUint(uint64(len(s)))
- io.WriteString(state.b, s)
+ state.b.WriteString(s)
}
}
@@ -323,7 +340,7 @@ const singletonField = 0
// encodeSingle encodes a single top-level non-struct value.
func (enc *Encoder) encodeSingle(b *bytes.Buffer, engine *encEngine, basep uintptr) {
- state := newEncoderState(enc, b)
+ state := enc.newEncoderState(b)
state.fieldnum = singletonField
// There is no surrounding struct to frame the transmission, so we must
// generate data even if the item is zero. To do this, set sendZero.
@@ -336,11 +353,12 @@ func (enc *Encoder) encodeSingle(b *bytes.Buffer, engine *encEngine, basep uintp
}
}
instr.op(instr, state, p)
+ enc.freeEncoderState(state)
}
// encodeStruct encodes a single struct value.
func (enc *Encoder) encodeStruct(b *bytes.Buffer, engine *encEngine, basep uintptr) {
- state := newEncoderState(enc, b)
+ state := enc.newEncoderState(b)
state.fieldnum = -1
for i := 0; i < len(engine.instr); i++ {
instr := &engine.instr[i]
@@ -352,11 +370,12 @@ func (enc *Encoder) encodeStruct(b *bytes.Buffer, engine *encEngine, basep uintp
}
instr.op(instr, state, p)
}
+ enc.freeEncoderState(state)
}
// encodeArray encodes the array whose 0th element is at p.
func (enc *Encoder) encodeArray(b *bytes.Buffer, p uintptr, op encOp, elemWid uintptr, elemIndir int, length int) {
- state := newEncoderState(enc, b)
+ state := enc.newEncoderState(b)
state.fieldnum = -1
state.sendZero = true
state.encodeUint(uint64(length))
@@ -372,14 +391,15 @@ func (enc *Encoder) encodeArray(b *bytes.Buffer, p uintptr, op encOp, elemWid ui
op(nil, state, unsafe.Pointer(elemp))
p += uintptr(elemWid)
}
+ enc.freeEncoderState(state)
}
// encodeReflectValue is a helper for maps. It encodes the value v.
func encodeReflectValue(state *encoderState, v reflect.Value, op encOp, indir int) {
- for i := 0; i < indir && v != nil; i++ {
+ for i := 0; i < indir && v.IsValid(); i++ {
v = reflect.Indirect(v)
}
- if v == nil {
+ if !v.IsValid() {
errorf("gob: encodeReflectValue: nil element")
}
op(nil, state, unsafe.Pointer(v.UnsafeAddr()))
@@ -388,16 +408,17 @@ func encodeReflectValue(state *encoderState, v reflect.Value, op encOp, indir in
// encodeMap encodes a map as unsigned count followed by key:value pairs.
// Because map internals are not exposed, we must use reflection rather than
// addresses.
-func (enc *Encoder) encodeMap(b *bytes.Buffer, mv *reflect.MapValue, keyOp, elemOp encOp, keyIndir, elemIndir int) {
- state := newEncoderState(enc, b)
+func (enc *Encoder) encodeMap(b *bytes.Buffer, mv reflect.Value, keyOp, elemOp encOp, keyIndir, elemIndir int) {
+ state := enc.newEncoderState(b)
state.fieldnum = -1
state.sendZero = true
- keys := mv.Keys()
+ keys := mv.MapKeys()
state.encodeUint(uint64(len(keys)))
for _, key := range keys {
encodeReflectValue(state, key, keyOp, keyIndir)
- encodeReflectValue(state, mv.Elem(key), elemOp, elemIndir)
+ encodeReflectValue(state, mv.MapIndex(key), elemOp, elemIndir)
}
+ enc.freeEncoderState(state)
}
// encodeInterface encodes the interface value iv.
@@ -405,8 +426,8 @@ func (enc *Encoder) encodeMap(b *bytes.Buffer, mv *reflect.MapValue, keyOp, elem
// by the type identifier (which might require defining that type right now), followed
// by the concrete value. A nil value gets sent as the empty string for the name,
// followed by no value.
-func (enc *Encoder) encodeInterface(b *bytes.Buffer, iv *reflect.InterfaceValue) {
- state := newEncoderState(enc, b)
+func (enc *Encoder) encodeInterface(b *bytes.Buffer, iv reflect.Value) {
+ state := enc.newEncoderState(b)
state.fieldnum = -1
state.sendZero = true
if iv.IsNil() {
@@ -421,7 +442,7 @@ func (enc *Encoder) encodeInterface(b *bytes.Buffer, iv *reflect.InterfaceValue)
}
// Send the name.
state.encodeUint(uint64(len(name)))
- _, err := io.WriteString(state.b, name)
+ _, err := state.b.WriteString(name)
if err != nil {
error(err)
}
@@ -433,15 +454,16 @@ func (enc *Encoder) encodeInterface(b *bytes.Buffer, iv *reflect.InterfaceValue)
// should be written to b, before the encoded value.
enc.pushWriter(b)
data := new(bytes.Buffer)
- err = enc.encode(data, iv.Elem(), ut)
- if err != nil {
- error(err)
+ enc.encode(data, iv.Elem(), ut)
+ if enc.err != nil {
+ error(enc.err)
}
enc.popWriter()
enc.writeMessage(b, data)
if enc.err != nil {
error(err)
}
+ enc.freeEncoderState(state)
}
// encGobEncoder encodes a value that implements the GobEncoder interface.
@@ -453,10 +475,11 @@ func (enc *Encoder) encodeGobEncoder(b *bytes.Buffer, v reflect.Value, index int
if err != nil {
error(err)
}
- state := newEncoderState(enc, b)
+ state := enc.newEncoderState(b)
state.fieldnum = -1
state.encodeUint(uint64(len(data)))
state.b.Write(data)
+ enc.freeEncoderState(state)
}
var encOpTable = [...]encOp{
@@ -502,8 +525,8 @@ func (enc *Encoder) encOpFor(rt reflect.Type, inProgress map[reflect.Type]*encOp
if op == nil {
inProgress[rt] = &op
// Special cases
- switch t := typ.(type) {
- case *reflect.SliceType:
+ switch t := typ; t.Kind() {
+ case reflect.Slice:
if t.Elem().Kind() == reflect.Uint8 {
op = encUint8Array
break
@@ -518,14 +541,14 @@ func (enc *Encoder) encOpFor(rt reflect.Type, inProgress map[reflect.Type]*encOp
state.update(i)
state.enc.encodeArray(state.b, slice.Data, *elemOp, t.Elem().Size(), indir, int(slice.Len))
}
- case *reflect.ArrayType:
+ case reflect.Array:
// True arrays have size in the type.
elemOp, indir := enc.encOpFor(t.Elem(), inProgress)
op = func(i *encInstr, state *encoderState, p unsafe.Pointer) {
state.update(i)
state.enc.encodeArray(state.b, uintptr(p), *elemOp, t.Elem().Size(), indir, t.Len())
}
- case *reflect.MapType:
+ case reflect.Map:
keyOp, keyIndir := enc.encOpFor(t.Key(), inProgress)
elemOp, elemIndir := enc.encOpFor(t.Elem(), inProgress)
op = func(i *encInstr, state *encoderState, p unsafe.Pointer) {
@@ -533,14 +556,14 @@ func (enc *Encoder) encOpFor(rt reflect.Type, inProgress map[reflect.Type]*encOp
// that slices etc. can. We must recover a full reflection value for
// the iteration.
v := reflect.NewValue(unsafe.Unreflect(t, unsafe.Pointer(p)))
- mv := reflect.Indirect(v).(*reflect.MapValue)
+ mv := reflect.Indirect(v)
if !state.sendZero && mv.Len() == 0 {
return
}
state.update(i)
state.enc.encodeMap(state.b, mv, *keyOp, *elemOp, keyIndir, elemIndir)
}
- case *reflect.StructType:
+ case reflect.Struct:
// Generate a closure that calls out to the engine for the nested type.
enc.getEncEngine(userType(typ))
info := mustGetTypeInfo(typ)
@@ -549,13 +572,13 @@ func (enc *Encoder) encOpFor(rt reflect.Type, inProgress map[reflect.Type]*encOp
// indirect through info to delay evaluation for recursive structs
state.enc.encodeStruct(state.b, info.encoder, uintptr(p))
}
- case *reflect.InterfaceType:
+ case reflect.Interface:
op = func(i *encInstr, state *encoderState, p unsafe.Pointer) {
// Interfaces transmit the name and contents of the concrete
// value they contain.
v := reflect.NewValue(unsafe.Unreflect(t, unsafe.Pointer(p)))
- iv := reflect.Indirect(v).(*reflect.InterfaceValue)
- if !state.sendZero && (iv == nil || iv.IsNil()) {
+ iv := reflect.Indirect(v)
+ if !state.sendZero && (!iv.IsValid() || iv.IsNil()) {
return
}
state.update(i)
@@ -569,43 +592,54 @@ func (enc *Encoder) encOpFor(rt reflect.Type, inProgress map[reflect.Type]*encOp
return &op, indir
}
+// methodIndex returns which method of rt implements the method.
+func methodIndex(rt reflect.Type, method string) int {
+ for i := 0; i < rt.NumMethod(); i++ {
+ if rt.Method(i).Name == method {
+ return i
+ }
+ }
+ errorf("gob: internal error: can't find method %s", method)
+ return 0
+}
+
// gobEncodeOpFor returns the op for a type that is known to implement
// GobEncoder.
func (enc *Encoder) gobEncodeOpFor(ut *userTypeInfo) (*encOp, int) {
rt := ut.user
- if ut.encIndir != 0 {
- errorf("gob: TODO: can't handle indirection to reach GobEncoder")
- }
- index := -1
- for i := 0; i < rt.NumMethod(); i++ {
- if rt.Method(i).Name == gobEncodeMethodName {
- index = i
- break
+ if ut.encIndir == -1 {
+ rt = reflect.PtrTo(rt)
+ } else if ut.encIndir > 0 {
+ for i := int8(0); i < ut.encIndir; i++ {
+ rt = rt.Elem()
}
}
- if index < 0 {
- panic("can't find GobEncode method")
- }
var op encOp
op = func(i *encInstr, state *encoderState, p unsafe.Pointer) {
- // TODO: this will need fixing when ut.encIndr != 0.
- v := reflect.NewValue(unsafe.Unreflect(rt, p))
+ var v reflect.Value
+ if ut.encIndir == -1 {
+ // Need to climb up one level to turn value into pointer.
+ v = reflect.NewValue(unsafe.Unreflect(rt, unsafe.Pointer(&p)))
+ } else {
+ v = reflect.NewValue(unsafe.Unreflect(rt, p))
+ }
state.update(i)
- state.enc.encodeGobEncoder(state.b, v, index)
+ state.enc.encodeGobEncoder(state.b, v, methodIndex(rt, gobEncodeMethodName))
}
- return &op, int(ut.encIndir)
+ return &op, int(ut.encIndir) // encIndir: op will get called with p == address of receiver.
}
// compileEnc returns the engine to compile the type.
func (enc *Encoder) compileEnc(ut *userTypeInfo) *encEngine {
- srt, isStruct := ut.base.(*reflect.StructType)
+ srt := ut.base
engine := new(encEngine)
seen := make(map[reflect.Type]*encOp)
rt := ut.base
if ut.isGobEncoder {
rt = ut.user
}
- if !ut.isGobEncoder && isStruct {
+ if !ut.isGobEncoder &&
+ srt.Kind() == reflect.Struct {
for fieldNum, wireFieldNum := 0, 0; fieldNum < srt.NumField(); fieldNum++ {
f := srt.Field(fieldNum)
if !isExported(f.Name) {
@@ -616,7 +650,7 @@ func (enc *Encoder) compileEnc(ut *userTypeInfo) *encEngine {
wireFieldNum++
}
if srt.NumField() > 0 && len(engine.instr) == 0 {
- errorf("type %s has no exported fields", rt)
+ errorf("gob: type %s has no exported fields", rt)
}
engine.instr = append(engine.instr, encInstr{encStructTerminator, 0, 0, 0})
} else {
@@ -650,15 +684,12 @@ func (enc *Encoder) lockAndGetEncEngine(ut *userTypeInfo) *encEngine {
return enc.getEncEngine(ut)
}
-func (enc *Encoder) encode(b *bytes.Buffer, value reflect.Value, ut *userTypeInfo) (err os.Error) {
- defer catchError(&err)
+func (enc *Encoder) encode(b *bytes.Buffer, value reflect.Value, ut *userTypeInfo) {
+ defer catchError(&enc.err)
engine := enc.lockAndGetEncEngine(ut)
indir := ut.indir
if ut.isGobEncoder {
indir = int(ut.encIndir)
- if indir != 0 {
- errorf("TODO: can't handle indirection in GobEncoder value")
- }
}
for i := 0; i < indir; i++ {
value = reflect.Indirect(value)
@@ -668,5 +699,4 @@ func (enc *Encoder) encode(b *bytes.Buffer, value reflect.Value, ut *userTypeInf
} else {
enc.encodeSingle(b, engine, value.UnsafeAddr())
}
- return nil
}
diff --git a/src/pkg/gob/encoder.go b/src/pkg/gob/encoder.go
index 4bfcf15c7..928f3b244 100644
--- a/src/pkg/gob/encoder.go
+++ b/src/pkg/gob/encoder.go
@@ -19,7 +19,9 @@ type Encoder struct {
w []io.Writer // where to send the data
sent map[reflect.Type]typeId // which types we've already sent
countState *encoderState // stage for writing counts
+ freeList *encoderState // list of free encoderStates; avoids reallocation
buf []byte // for collecting the output.
+ byteBuf bytes.Buffer // buffer for top-level encoderState
err os.Error
}
@@ -28,7 +30,7 @@ func NewEncoder(w io.Writer) *Encoder {
enc := new(Encoder)
enc.w = []io.Writer{w}
enc.sent = make(map[reflect.Type]typeId)
- enc.countState = newEncoderState(enc, new(bytes.Buffer))
+ enc.countState = enc.newEncoderState(new(bytes.Buffer))
return enc
}
@@ -107,12 +109,12 @@ func (enc *Encoder) sendActualType(w io.Writer, state *encoderState, ut *userTyp
enc.sent[ut.user] = info.id
}
// Now send the inner types
- switch st := actual.(type) {
- case *reflect.StructType:
+ switch st := actual; st.Kind() {
+ case reflect.Struct:
for i := 0; i < st.NumField(); i++ {
enc.sendType(w, state, st.Field(i).Type)
}
- case reflect.ArrayOrSliceType:
+ case reflect.Array, reflect.Slice:
enc.sendType(w, state, st.Elem())
}
return true
@@ -128,27 +130,27 @@ func (enc *Encoder) sendType(w io.Writer, state *encoderState, origt reflect.Typ
}
// It's a concrete value, so drill down to the base type.
- switch rt := ut.base.(type) {
+ switch rt := ut.base; rt.Kind() {
default:
// Basic types and interfaces do not need to be described.
return
- case *reflect.SliceType:
+ case reflect.Slice:
// If it's []uint8, don't send; it's considered basic.
if rt.Elem().Kind() == reflect.Uint8 {
return
}
// Otherwise we do send.
break
- case *reflect.ArrayType:
+ case reflect.Array:
// arrays must be sent so we know their lengths and element types.
break
- case *reflect.MapType:
+ case reflect.Map:
// maps must be sent so we know their lengths and key/value types.
break
- case *reflect.StructType:
+ case reflect.Struct:
// structs must be sent so we know their fields.
break
- case *reflect.ChanType, *reflect.FuncType:
+ case reflect.Chan, reflect.Func:
// Probably a bad field in a struct.
enc.badType(rt)
return
@@ -172,9 +174,6 @@ func (enc *Encoder) sendTypeDescriptor(w io.Writer, state *encoderState, ut *use
rt := ut.base
if ut.isGobEncoder {
rt = ut.user
- if ut.encIndir != 0 {
- panic("TODO: can't handle non-zero encIndir")
- }
}
if _, alreadySent := enc.sent[rt]; !alreadySent {
// No, so send it.
@@ -221,7 +220,8 @@ func (enc *Encoder) EncodeValue(value reflect.Value) os.Error {
}
enc.err = nil
- state := newEncoderState(enc, new(bytes.Buffer))
+ enc.byteBuf.Reset()
+ state := enc.newEncoderState(&enc.byteBuf)
enc.sendTypeDescriptor(enc.writer(), state, ut)
enc.sendTypeId(state, ut)
@@ -230,12 +230,11 @@ func (enc *Encoder) EncodeValue(value reflect.Value) os.Error {
}
// Encode the object.
- err = enc.encode(state.b, value, ut)
- if err != nil {
- enc.setError(err)
- } else {
+ enc.encode(state.b, value, ut)
+ if enc.err == nil {
enc.writeMessage(enc.writer(), state.b)
}
+ enc.freeEncoderState(state)
return enc.err
}
diff --git a/src/pkg/gob/encoder_test.go b/src/pkg/gob/encoder_test.go
index a0c713b81..3d5dfdb86 100644
--- a/src/pkg/gob/encoder_test.go
+++ b/src/pkg/gob/encoder_test.go
@@ -339,7 +339,7 @@ func TestSingletons(t *testing.T) {
continue
}
// Get rid of the pointer in the rhs
- val := reflect.NewValue(test.out).(*reflect.PtrValue).Elem().Interface()
+ val := reflect.NewValue(test.out).Elem().Interface()
if !reflect.DeepEqual(test.in, val) {
t.Errorf("decoding singleton: expected %v got %v", test.in, val)
}
diff --git a/src/pkg/gob/gobencdec_test.go b/src/pkg/gob/gobencdec_test.go
index 82ca68170..012b09956 100644
--- a/src/pkg/gob/gobencdec_test.go
+++ b/src/pkg/gob/gobencdec_test.go
@@ -110,8 +110,8 @@ type GobTest2 struct {
}
type GobTest3 struct {
- X int // guarantee we have something in common with GobTest*
- G *Gobber // TODO: should be able to satisfy interface without a pointer
+ X int // guarantee we have something in common with GobTest*
+ G *Gobber
}
type GobTest4 struct {
@@ -128,6 +128,16 @@ type GobTestIgnoreEncoder struct {
X int // guarantee we have something in common with GobTest*
}
+type GobTestValueEncDec struct {
+ X int // guarantee we have something in common with GobTest*
+ G StringStruct // not a pointer.
+}
+
+type GobTestIndirectEncDec struct {
+ X int // guarantee we have something in common with GobTest*
+ G ***StringStruct // indirections to the receiver.
+}
+
func TestGobEncoderField(t *testing.T) {
b := new(bytes.Buffer)
// First a field that's a structure.
@@ -162,6 +172,50 @@ func TestGobEncoderField(t *testing.T) {
}
}
+// Even though the field is a value, we can still take its address
+// and should be able to call the methods.
+func TestGobEncoderValueField(t *testing.T) {
+ b := new(bytes.Buffer)
+ // First a field that's a structure.
+ enc := NewEncoder(b)
+ err := enc.Encode(GobTestValueEncDec{17, StringStruct{"HIJKL"}})
+ if err != nil {
+ t.Fatal("encode error:", err)
+ }
+ dec := NewDecoder(b)
+ x := new(GobTestValueEncDec)
+ err = dec.Decode(x)
+ if err != nil {
+ t.Fatal("decode error:", err)
+ }
+ if x.G.s != "HIJKL" {
+ t.Errorf("expected `HIJKL` got %s", x.G.s)
+ }
+}
+
+// GobEncode/Decode should work even if the value is
+// more indirect than the receiver.
+func TestGobEncoderIndirectField(t *testing.T) {
+ b := new(bytes.Buffer)
+ // First a field that's a structure.
+ enc := NewEncoder(b)
+ s := &StringStruct{"HIJKL"}
+ sp := &s
+ err := enc.Encode(GobTestIndirectEncDec{17, &sp})
+ if err != nil {
+ t.Fatal("encode error:", err)
+ }
+ dec := NewDecoder(b)
+ x := new(GobTestIndirectEncDec)
+ err = dec.Decode(x)
+ if err != nil {
+ t.Fatal("decode error:", err)
+ }
+ if (***x.G).s != "HIJKL" {
+ t.Errorf("expected `HIJKL` got %s", (***x.G).s)
+ }
+}
+
// As long as the fields have the same name and implement the
// interface, we can cross-connect them. Not sure it's useful
// and may even be bad but it works and it's hard to prevent
@@ -275,8 +329,7 @@ func TestGobEncoderStructSingleton(t *testing.T) {
func TestGobEncoderNonStructSingleton(t *testing.T) {
b := new(bytes.Buffer)
enc := NewEncoder(b)
- g := Gobber(1234) // TODO: shouldn't need to take the address here.
- err := enc.Encode(&g)
+ err := enc.Encode(Gobber(1234))
if err != nil {
t.Fatal("encode error:", err)
}
diff --git a/src/pkg/gob/timing_test.go b/src/pkg/gob/timing_test.go
new file mode 100644
index 000000000..645f4fe51
--- /dev/null
+++ b/src/pkg/gob/timing_test.go
@@ -0,0 +1,90 @@
+// 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 gob
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "os"
+ "runtime"
+ "testing"
+)
+
+type Bench struct {
+ A int
+ B float64
+ C string
+ D []byte
+}
+
+func benchmarkEndToEnd(r io.Reader, w io.Writer, b *testing.B) {
+ b.StopTimer()
+ enc := NewEncoder(w)
+ dec := NewDecoder(r)
+ bench := &Bench{7, 3.2, "now is the time", []byte("for all good men")}
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ if enc.Encode(bench) != nil {
+ panic("encode error")
+ }
+ if dec.Decode(bench) != nil {
+ panic("decode error")
+ }
+ }
+}
+
+func BenchmarkEndToEndPipe(b *testing.B) {
+ r, w, err := os.Pipe()
+ if err != nil {
+ panic("can't get pipe:" + err.String())
+ }
+ benchmarkEndToEnd(r, w, b)
+}
+
+func BenchmarkEndToEndByteBuffer(b *testing.B) {
+ var buf bytes.Buffer
+ benchmarkEndToEnd(&buf, &buf, b)
+}
+
+func TestCountEncodeMallocs(t *testing.T) {
+ var buf bytes.Buffer
+ enc := NewEncoder(&buf)
+ bench := &Bench{7, 3.2, "now is the time", []byte("for all good men")}
+ mallocs := 0 - runtime.MemStats.Mallocs
+ const count = 1000
+ for i := 0; i < count; i++ {
+ err := enc.Encode(bench)
+ if err != nil {
+ t.Fatal("encode:", err)
+ }
+ }
+ mallocs += runtime.MemStats.Mallocs
+ fmt.Printf("mallocs per encode of type Bench: %d\n", mallocs/count)
+}
+
+func TestCountDecodeMallocs(t *testing.T) {
+ var buf bytes.Buffer
+ enc := NewEncoder(&buf)
+ bench := &Bench{7, 3.2, "now is the time", []byte("for all good men")}
+ const count = 1000
+ for i := 0; i < count; i++ {
+ err := enc.Encode(bench)
+ if err != nil {
+ t.Fatal("encode:", err)
+ }
+ }
+ dec := NewDecoder(&buf)
+ mallocs := 0 - runtime.MemStats.Mallocs
+ for i := 0; i < count; i++ {
+ *bench = Bench{}
+ err := dec.Decode(&bench)
+ if err != nil {
+ t.Fatal("decode:", err)
+ }
+ }
+ mallocs += runtime.MemStats.Mallocs
+ fmt.Printf("mallocs per decode of type Bench: %d\n", mallocs/count)
+}
diff --git a/src/pkg/gob/type.go b/src/pkg/gob/type.go
index a43813941..8fd174841 100644
--- a/src/pkg/gob/type.go
+++ b/src/pkg/gob/type.go
@@ -60,8 +60,8 @@ func validUserType(rt reflect.Type) (ut *userTypeInfo, err os.Error) {
// half speed. If they meet up, there's a cycle.
slowpoke := ut.base // walks half as fast as ut.base
for {
- pt, ok := ut.base.(*reflect.PtrType)
- if !ok {
+ pt := ut.base
+ if pt.Kind() != reflect.Ptr {
break
}
ut.base = pt.Elem()
@@ -70,20 +70,13 @@ func validUserType(rt reflect.Type) (ut *userTypeInfo, err os.Error) {
return nil, os.ErrorString("can't represent recursive pointer type " + ut.base.String())
}
if ut.indir%2 == 0 {
- slowpoke = slowpoke.(*reflect.PtrType).Elem()
+ slowpoke = slowpoke.Elem()
}
ut.indir++
}
- ut.isGobEncoder, ut.encIndir = implementsGobEncoder(ut.user)
- ut.isGobDecoder, ut.decIndir = implementsGobDecoder(ut.user)
+ ut.isGobEncoder, ut.encIndir = implementsInterface(ut.user, gobEncoderCheck)
+ ut.isGobDecoder, ut.decIndir = implementsInterface(ut.user, gobDecoderCheck)
userTypeCache[rt] = ut
- if ut.encIndir != 0 || ut.decIndir != 0 {
- // There are checks in lots of other places, but putting this here means we won't even
- // attempt to encode/decode this type.
- // TODO: make it possible to handle types that are indirect to the implementation,
- // such as a structure field of type T when *T implements GobDecoder.
- return nil, os.ErrorString("TODO: gob can't handle indirections to GobEncoder/Decoder")
- }
return
}
@@ -92,53 +85,43 @@ const (
gobDecodeMethodName = "GobDecode"
)
-// implementsGobEncoder reports whether the type implements the interface. It also
-// returns the number of indirections required to get to the implementation.
-// TODO: when reflection makes it possible, should also be prepared to climb up
-// one level if we're not on a pointer (implementation could be on *T for our T).
-// That will mean that indir could be < 0, which is sure to cause problems, but
-// we ignore them now as indir is always >= 0 now.
-func implementsGobEncoder(rt reflect.Type) (implements bool, indir int8) {
- if rt == nil {
- return
- }
- // The type might be a pointer, or it might not, and we need to keep
- // dereferencing to the base type until we find an implementation.
- for {
- if rt.NumMethod() > 0 { // avoid allocations etc. unless there's some chance
- if _, ok := reflect.MakeZero(rt).Interface().(GobEncoder); ok {
- return true, indir
- }
- }
- if p, ok := rt.(*reflect.PtrType); ok {
- indir++
- if indir > 100 { // insane number of indirections
- return false, 0
- }
- rt = p.Elem()
- continue
- }
- break
+// implements returns whether the type implements the interface, as encoded
+// in the check function.
+func implements(typ reflect.Type, check func(typ reflect.Type) bool) bool {
+ if typ.NumMethod() == 0 { // avoid allocations etc. unless there's some chance
+ return false
}
- return false, 0
+ return check(typ)
+}
+
+// gobEncoderCheck makes the type assertion a boolean function.
+func gobEncoderCheck(typ reflect.Type) bool {
+ _, ok := reflect.Zero(typ).Interface().(GobEncoder)
+ return ok
}
-// implementsGobDecoder reports whether the type implements the interface. It also
-// returns the number of indirections required to get to the implementation.
-// TODO: see comment on implementsGobEncoder.
-func implementsGobDecoder(rt reflect.Type) (implements bool, indir int8) {
- if rt == nil {
+// gobDecoderCheck makes the type assertion a boolean function.
+func gobDecoderCheck(typ reflect.Type) bool {
+ _, ok := reflect.Zero(typ).Interface().(GobDecoder)
+ return ok
+}
+
+// implementsInterface reports whether the type implements the
+// interface. (The actual check is done through the provided function.)
+// It also returns the number of indirections required to get to the
+// implementation.
+func implementsInterface(typ reflect.Type, check func(typ reflect.Type) bool) (success bool, indir int8) {
+ if typ == nil {
return
}
- // The type might be a pointer, or it might not, and we need to keep
+ rt := typ
+ // The type might be a pointer and we need to keep
// dereferencing to the base type until we find an implementation.
for {
- if rt.NumMethod() > 0 { // avoid allocations etc. unless there's some chance
- if _, ok := reflect.MakeZero(rt).Interface().(GobDecoder); ok {
- return true, indir
- }
+ if implements(rt, check) {
+ return true, indir
}
- if p, ok := rt.(*reflect.PtrType); ok {
+ if p := rt; p.Kind() == reflect.Ptr {
indir++
if indir > 100 { // insane number of indirections
return false, 0
@@ -148,6 +131,13 @@ func implementsGobDecoder(rt reflect.Type) (implements bool, indir int8) {
}
break
}
+ // No luck yet, but if this is a base type (non-pointer), the pointer might satisfy.
+ if typ.Kind() != reflect.Ptr {
+ // Not a pointer, but does the pointer work?
+ if implements(reflect.PtrTo(typ), check) {
+ return true, -1
+ }
+ }
return false, 0
}
@@ -232,22 +222,24 @@ func (t *CommonType) name() string { return t.Name }
var (
// Primordial types, needed during initialization.
- tBool = bootstrapType("bool", false, 1)
- tInt = bootstrapType("int", int(0), 2)
- tUint = bootstrapType("uint", uint(0), 3)
- tFloat = bootstrapType("float", float64(0), 4)
- tBytes = bootstrapType("bytes", make([]byte, 0), 5)
- tString = bootstrapType("string", "", 6)
- tComplex = bootstrapType("complex", 0+0i, 7)
- tInterface = bootstrapType("interface", interface{}(nil), 8)
+ // Always passed as pointers so the interface{} type
+ // goes through without losing its interfaceness.
+ tBool = bootstrapType("bool", (*bool)(nil), 1)
+ tInt = bootstrapType("int", (*int)(nil), 2)
+ tUint = bootstrapType("uint", (*uint)(nil), 3)
+ tFloat = bootstrapType("float", (*float64)(nil), 4)
+ tBytes = bootstrapType("bytes", (*[]byte)(nil), 5)
+ tString = bootstrapType("string", (*string)(nil), 6)
+ tComplex = bootstrapType("complex", (*complex128)(nil), 7)
+ tInterface = bootstrapType("interface", (*interface{})(nil), 8)
// Reserve some Ids for compatible expansion
- tReserved7 = bootstrapType("_reserved1", struct{ r7 int }{}, 9)
- tReserved6 = bootstrapType("_reserved1", struct{ r6 int }{}, 10)
- tReserved5 = bootstrapType("_reserved1", struct{ r5 int }{}, 11)
- tReserved4 = bootstrapType("_reserved1", struct{ r4 int }{}, 12)
- tReserved3 = bootstrapType("_reserved1", struct{ r3 int }{}, 13)
- tReserved2 = bootstrapType("_reserved1", struct{ r2 int }{}, 14)
- tReserved1 = bootstrapType("_reserved1", struct{ r1 int }{}, 15)
+ tReserved7 = bootstrapType("_reserved1", (*struct{ r7 int })(nil), 9)
+ tReserved6 = bootstrapType("_reserved1", (*struct{ r6 int })(nil), 10)
+ tReserved5 = bootstrapType("_reserved1", (*struct{ r5 int })(nil), 11)
+ tReserved4 = bootstrapType("_reserved1", (*struct{ r4 int })(nil), 12)
+ tReserved3 = bootstrapType("_reserved1", (*struct{ r3 int })(nil), 13)
+ tReserved2 = bootstrapType("_reserved1", (*struct{ r2 int })(nil), 14)
+ tReserved1 = bootstrapType("_reserved1", (*struct{ r1 int })(nil), 15)
)
// Predefined because it's needed by the Decoder
@@ -415,7 +407,7 @@ func (s *structType) string() string { return s.safeString(make(map[typeId]bool)
func newStructType(name string) *structType {
s := &structType{CommonType{Name: name}, nil}
// For historical reasons we set the id here rather than init.
- // Se the comment in newTypeObject for details.
+ // See the comment in newTypeObject for details.
setTypeId(s)
return s
}
@@ -439,30 +431,30 @@ func newTypeObject(name string, ut *userTypeInfo, rt reflect.Type) (gobType, os.
}()
// Install the top-level type before the subtypes (e.g. struct before
// fields) so recursive types can be constructed safely.
- switch t := rt.(type) {
+ switch t := rt; t.Kind() {
// All basic types are easy: they are predefined.
- case *reflect.BoolType:
+ case reflect.Bool:
return tBool.gobType(), nil
- case *reflect.IntType:
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return tInt.gobType(), nil
- case *reflect.UintType:
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return tUint.gobType(), nil
- case *reflect.FloatType:
+ case reflect.Float32, reflect.Float64:
return tFloat.gobType(), nil
- case *reflect.ComplexType:
+ case reflect.Complex64, reflect.Complex128:
return tComplex.gobType(), nil
- case *reflect.StringType:
+ case reflect.String:
return tString.gobType(), nil
- case *reflect.InterfaceType:
+ case reflect.Interface:
return tInterface.gobType(), nil
- case *reflect.ArrayType:
+ case reflect.Array:
at := newArrayType(name)
types[rt] = at
type0, err = getBaseType("", t.Elem())
@@ -480,7 +472,7 @@ func newTypeObject(name string, ut *userTypeInfo, rt reflect.Type) (gobType, os.
at.init(type0, t.Len())
return at, nil
- case *reflect.MapType:
+ case reflect.Map:
mt := newMapType(name)
types[rt] = mt
type0, err = getBaseType("", t.Key())
@@ -494,7 +486,7 @@ func newTypeObject(name string, ut *userTypeInfo, rt reflect.Type) (gobType, os.
mt.init(type0, type1)
return mt, nil
- case *reflect.SliceType:
+ case reflect.Slice:
// []byte == []uint8 is a special case
if t.Elem().Kind() == reflect.Uint8 {
return tBytes.gobType(), nil
@@ -508,7 +500,7 @@ func newTypeObject(name string, ut *userTypeInfo, rt reflect.Type) (gobType, os.
st.init(type0)
return st, nil
- case *reflect.StructType:
+ case reflect.Struct:
st := newStructType(name)
types[rt] = st
idToType[st.id()] = st
@@ -553,7 +545,7 @@ func getBaseType(name string, rt reflect.Type) (gobType, os.Error) {
// getType returns the Gob type describing the given reflect.Type.
// Should be called only when handling GobEncoders/Decoders,
// which may be pointers. All other types are handled through the
-// base type, never a pointer.
+// base type, never a pointer.
// typeLock must be held.
func getType(name string, ut *userTypeInfo, rt reflect.Type) (gobType, os.Error) {
typ, present := types[rt]
@@ -569,14 +561,15 @@ func getType(name string, ut *userTypeInfo, rt reflect.Type) (gobType, os.Error)
func checkId(want, got typeId) {
if want != got {
- fmt.Fprintf(os.Stderr, "checkId: %d should be %d\n", int(want), int(got))
+ fmt.Fprintf(os.Stderr, "checkId: %d should be %d\n", int(got), int(want))
panic("bootstrap type wrong id: " + got.name() + " " + got.string() + " not " + want.string())
}
}
-// used for building the basic types; called only from init()
+// used for building the basic types; called only from init(). the incoming
+// interface always refers to a pointer.
func bootstrapType(name string, e interface{}, expect typeId) typeId {
- rt := reflect.Typeof(e)
+ rt := reflect.Typeof(e).Elem()
_, present := types[rt]
if present {
panic("bootstrap type already present: " + name + ", " + rt.String())
@@ -665,17 +658,17 @@ func getTypeInfo(ut *userTypeInfo) (*typeInfo, os.Error) {
}
t := info.id.gobType()
- switch typ := rt.(type) {
- case *reflect.ArrayType:
+ switch typ := rt; typ.Kind() {
+ case reflect.Array:
info.wire = &wireType{ArrayT: t.(*arrayType)}
- case *reflect.MapType:
+ case reflect.Map:
info.wire = &wireType{MapT: t.(*mapType)}
- case *reflect.SliceType:
+ case reflect.Slice:
// []byte == []uint8 is a special case handled separately
if typ.Elem().Kind() != reflect.Uint8 {
info.wire = &wireType{SliceT: t.(*sliceType)}
}
- case *reflect.StructType:
+ case reflect.Struct:
info.wire = &wireType{StructT: t.(*structType)}
}
typeInfoMap[rt] = info
@@ -702,10 +695,6 @@ func mustGetTypeInfo(rt reflect.Type) *typeInfo {
// to guarantee the encoding used by a GobEncoder is stable as the
// software evolves. For instance, it might make sense for GobEncode
// to include a version number in the encoding.
-//
-// Note: At the moment, the type implementing GobEncoder must
-// be exactly the type passed to Encode. For example, if *T implements
-// GobEncoder, the data item must be of type *T, not T or **T.
type GobEncoder interface {
// GobEncode returns a byte slice representing the encoding of the
// receiver for transmission to a GobDecoder, usually of the same
@@ -715,10 +704,6 @@ type GobEncoder interface {
// GobDecoder is the interface describing data that provides its own
// routine for decoding transmitted values sent by a GobEncoder.
-//
-// Note: At the moment, the type implementing GobDecoder must
-// be exactly the type passed to Decode. For example, if *T implements
-// GobDecoder, the data item must be of type *T, not T or **T.
type GobDecoder interface {
// GobDecode overwrites the receiver, which must be a pointer,
// with the value represented by the byte slice, which was written
@@ -767,7 +752,7 @@ func Register(value interface{}) {
// Dereference one pointer looking for a named type.
star := ""
if rt.Name() == "" {
- if pt, ok := rt.(*reflect.PtrType); ok {
+ if pt := rt; pt.Kind() == reflect.Ptr {
star = "*"
rt = pt
}
diff --git a/src/pkg/hash/fnv/Makefile b/src/pkg/hash/fnv/Makefile
new file mode 100644
index 000000000..4c8a4ecf0
--- /dev/null
+++ b/src/pkg/hash/fnv/Makefile
@@ -0,0 +1,11 @@
+# Copyright 2011 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+include ../../../Make.inc
+
+TARG=hash/fnv
+GOFILES=\
+ fnv.go\
+
+include ../../../Make.pkg
diff --git a/src/pkg/hash/fnv/fnv.go b/src/pkg/hash/fnv/fnv.go
new file mode 100644
index 000000000..66ab5a635
--- /dev/null
+++ b/src/pkg/hash/fnv/fnv.go
@@ -0,0 +1,133 @@
+// 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.
+
+// The fnv package implements FNV-1 and FNV-1a,
+// non-cryptographic hash functions created by
+// Glenn Fowler, Landon Curt Noll, and Phong Vo.
+// See http://isthe.com/chongo/tech/comp/fnv/.
+package fnv
+
+import (
+ "encoding/binary"
+ "hash"
+ "os"
+ "unsafe"
+)
+
+type (
+ sum32 uint32
+ sum32a uint32
+ sum64 uint64
+ sum64a uint64
+)
+
+const (
+ offset32 = 2166136261
+ offset64 = 14695981039346656037
+ prime32 = 16777619
+ prime64 = 1099511628211
+)
+
+// New32 returns a new 32-bit FNV-1 hash.Hash.
+func New32() hash.Hash32 {
+ var s sum32 = offset32
+ return &s
+}
+
+// New32a returns a new 32-bit FNV-1a hash.Hash.
+func New32a() hash.Hash32 {
+ var s sum32a = offset32
+ return &s
+}
+
+// New64 returns a new 64-bit FNV-1 hash.Hash.
+func New64() hash.Hash64 {
+ var s sum64 = offset64
+ return &s
+}
+
+// New64a returns a new 64-bit FNV-1a hash.Hash.
+func New64a() hash.Hash64 {
+ var s sum64a = offset64
+ return &s
+}
+
+func (s *sum32) Reset() { *s = offset32 }
+func (s *sum32a) Reset() { *s = offset32 }
+func (s *sum64) Reset() { *s = offset64 }
+func (s *sum64a) Reset() { *s = offset64 }
+
+func (s *sum32) Sum32() uint32 { return uint32(*s) }
+func (s *sum32a) Sum32() uint32 { return uint32(*s) }
+func (s *sum64) Sum64() uint64 { return uint64(*s) }
+func (s *sum64a) Sum64() uint64 { return uint64(*s) }
+
+func (s *sum32) Write(data []byte) (int, os.Error) {
+ hash := *s
+ for _, c := range data {
+ hash *= prime32
+ hash ^= sum32(c)
+ }
+ *s = hash
+ return len(data), nil
+}
+
+func (s *sum32a) Write(data []byte) (int, os.Error) {
+ hash := *s
+ for _, c := range data {
+ hash ^= sum32a(c)
+ hash *= prime32
+ }
+ *s = hash
+ return len(data), nil
+}
+
+func (s *sum64) Write(data []byte) (int, os.Error) {
+ hash := *s
+ for _, c := range data {
+ hash *= prime64
+ hash ^= sum64(c)
+ }
+ *s = hash
+ return len(data), nil
+}
+
+func (s *sum64a) Write(data []byte) (int, os.Error) {
+ hash := *s
+ for _, c := range data {
+ hash ^= sum64a(c)
+ hash *= prime64
+ }
+ *s = hash
+ return len(data), nil
+}
+
+func (s *sum32) Size() int { return unsafe.Sizeof(*s) }
+func (s *sum32a) Size() int { return unsafe.Sizeof(*s) }
+func (s *sum64) Size() int { return unsafe.Sizeof(*s) }
+func (s *sum64a) Size() int { return unsafe.Sizeof(*s) }
+
+func (s *sum32) Sum() []byte {
+ a := make([]byte, unsafe.Sizeof(*s))
+ binary.BigEndian.PutUint32(a, uint32(*s))
+ return a
+}
+
+func (s *sum32a) Sum() []byte {
+ a := make([]byte, unsafe.Sizeof(*s))
+ binary.BigEndian.PutUint32(a, uint32(*s))
+ return a
+}
+
+func (s *sum64) Sum() []byte {
+ a := make([]byte, unsafe.Sizeof(*s))
+ binary.BigEndian.PutUint64(a, uint64(*s))
+ return a
+}
+
+func (s *sum64a) Sum() []byte {
+ a := make([]byte, unsafe.Sizeof(*s))
+ binary.BigEndian.PutUint64(a, uint64(*s))
+ return a
+}
diff --git a/src/pkg/hash/fnv/fnv_test.go b/src/pkg/hash/fnv/fnv_test.go
new file mode 100644
index 000000000..429230c80
--- /dev/null
+++ b/src/pkg/hash/fnv/fnv_test.go
@@ -0,0 +1,167 @@
+// 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 fnv
+
+import (
+ "bytes"
+ "encoding/binary"
+ "hash"
+ "testing"
+)
+
+const testDataSize = 40
+
+type golden struct {
+ sum []byte
+ text string
+}
+
+var golden32 = []golden{
+ {[]byte{0x81, 0x1c, 0x9d, 0xc5}, ""},
+ {[]byte{0x05, 0x0c, 0x5d, 0x7e}, "a"},
+ {[]byte{0x70, 0x77, 0x2d, 0x38}, "ab"},
+ {[]byte{0x43, 0x9c, 0x2f, 0x4b}, "abc"},
+}
+
+var golden32a = []golden{
+ {[]byte{0x81, 0x1c, 0x9d, 0xc5}, ""},
+ {[]byte{0xe4, 0x0c, 0x29, 0x2c}, "a"},
+ {[]byte{0x4d, 0x25, 0x05, 0xca}, "ab"},
+ {[]byte{0x1a, 0x47, 0xe9, 0x0b}, "abc"},
+}
+
+var golden64 = []golden{
+ {[]byte{0xcb, 0xf2, 0x9c, 0xe4, 0x84, 0x22, 0x23, 0x25}, ""},
+ {[]byte{0xaf, 0x63, 0xbd, 0x4c, 0x86, 0x01, 0xb7, 0xbe}, "a"},
+ {[]byte{0x08, 0x32, 0x67, 0x07, 0xb4, 0xeb, 0x37, 0xb8}, "ab"},
+ {[]byte{0xd8, 0xdc, 0xca, 0x18, 0x6b, 0xaf, 0xad, 0xcb}, "abc"},
+}
+
+var golden64a = []golden{
+ {[]byte{0xcb, 0xf2, 0x9c, 0xe4, 0x84, 0x22, 0x23, 0x25}, ""},
+ {[]byte{0xaf, 0x63, 0xdc, 0x4c, 0x86, 0x01, 0xec, 0x8c}, "a"},
+ {[]byte{0x08, 0x9c, 0x44, 0x07, 0xb5, 0x45, 0x98, 0x6a}, "ab"},
+ {[]byte{0xe7, 0x1f, 0xa2, 0x19, 0x05, 0x41, 0x57, 0x4b}, "abc"},
+}
+
+func TestGolden32(t *testing.T) {
+ testGolden(t, New32(), golden32)
+}
+
+func TestGolden32a(t *testing.T) {
+ testGolden(t, New32a(), golden32a)
+}
+
+func TestGolden64(t *testing.T) {
+ testGolden(t, New64(), golden64)
+}
+
+func TestGolden64a(t *testing.T) {
+ testGolden(t, New64a(), golden64a)
+}
+
+func testGolden(t *testing.T, hash hash.Hash, gold []golden) {
+ for _, g := range gold {
+ hash.Reset()
+ done, error := hash.Write([]byte(g.text))
+ if error != nil {
+ t.Fatalf("write error: %s", error)
+ }
+ if done != len(g.text) {
+ t.Fatalf("wrote only %d out of %d bytes", done, len(g.text))
+ }
+ if actual := hash.Sum(); !bytes.Equal(g.sum, actual) {
+ t.Errorf("hash(%q) = 0x%x want 0x%x", g.text, actual, g.sum)
+ }
+ }
+}
+
+func TestIntegrity32(t *testing.T) {
+ testIntegrity(t, New32())
+}
+
+func TestIntegrity32a(t *testing.T) {
+ testIntegrity(t, New32a())
+}
+
+func TestIntegrity64(t *testing.T) {
+ testIntegrity(t, New64())
+}
+
+func TestIntegrity64a(t *testing.T) {
+ testIntegrity(t, New64a())
+}
+
+func testIntegrity(t *testing.T, h hash.Hash) {
+ data := []byte{'1', '2', 3, 4, 5}
+ h.Write(data)
+ sum := h.Sum()
+
+ if size := h.Size(); size != len(sum) {
+ t.Fatalf("Size()=%d but len(Sum())=%d", size, len(sum))
+ }
+
+ if a := h.Sum(); !bytes.Equal(sum, a) {
+ t.Fatalf("first Sum()=0x%x, second Sum()=0x%x", sum, a)
+ }
+
+ h.Reset()
+ h.Write(data)
+ if a := h.Sum(); !bytes.Equal(sum, a) {
+ t.Fatalf("Sum()=0x%x, but after Reset() Sum()=0x%x", sum, a)
+ }
+
+ h.Reset()
+ h.Write(data[:2])
+ h.Write(data[2:])
+ if a := h.Sum(); !bytes.Equal(sum, a) {
+ t.Fatalf("Sum()=0x%x, but with partial writes, Sum()=0x%x", sum, a)
+ }
+
+ switch h.Size() {
+ case 4:
+ sum32 := h.(hash.Hash32).Sum32()
+ if sum32 != binary.BigEndian.Uint32(sum) {
+ t.Fatalf("Sum()=0x%x, but Sum32()=0x%x", sum, sum32)
+ }
+ case 8:
+ sum64 := h.(hash.Hash64).Sum64()
+ if sum64 != binary.BigEndian.Uint64(sum) {
+ t.Fatalf("Sum()=0x%x, but Sum64()=0x%x", sum, sum64)
+ }
+ }
+}
+
+func Benchmark32(b *testing.B) {
+ benchmark(b, New32())
+}
+
+func Benchmark32a(b *testing.B) {
+ benchmark(b, New32a())
+}
+
+func Benchmark64(b *testing.B) {
+ benchmark(b, New64())
+}
+
+func Benchmark64a(b *testing.B) {
+ benchmark(b, New64a())
+}
+
+func benchmark(b *testing.B, h hash.Hash) {
+ b.ResetTimer()
+ b.SetBytes(testDataSize)
+ data := make([]byte, testDataSize)
+ for i := range data {
+ data[i] = byte(i + 'a')
+ }
+
+ b.StartTimer()
+ for todo := b.N; todo != 0; todo-- {
+ h.Reset()
+ h.Write(data)
+ h.Sum()
+ }
+}
diff --git a/src/pkg/html/parse_test.go b/src/pkg/html/parse_test.go
index d153533b5..fe955436c 100644
--- a/src/pkg/html/parse_test.go
+++ b/src/pkg/html/parse_test.go
@@ -28,7 +28,7 @@ func pipeErr(err os.Error) io.Reader {
}
func readDat(filename string, c chan io.Reader) {
- f, err := os.Open("testdata/webkit/"+filename, os.O_RDONLY, 0600)
+ f, err := os.Open("testdata/webkit/" + filename)
if err != nil {
c <- pipeErr(err)
return
diff --git a/src/pkg/http/cgi/Makefile b/src/pkg/http/cgi/Makefile
index 02f6cfc9e..19b1039c2 100644
--- a/src/pkg/http/cgi/Makefile
+++ b/src/pkg/http/cgi/Makefile
@@ -6,6 +6,7 @@ include ../../../Make.inc
TARG=http/cgi
GOFILES=\
- cgi.go\
+ child.go\
+ host.go\
include ../../../Make.pkg
diff --git a/src/pkg/http/cgi/child.go b/src/pkg/http/cgi/child.go
new file mode 100644
index 000000000..c7d48b9eb
--- /dev/null
+++ b/src/pkg/http/cgi/child.go
@@ -0,0 +1,192 @@
+// 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.
+
+// This file implements CGI from the perspective of a child
+// process.
+
+package cgi
+
+import (
+ "bufio"
+ "fmt"
+ "http"
+ "io"
+ "io/ioutil"
+ "os"
+ "strconv"
+ "strings"
+)
+
+// Request returns the HTTP request as represented in the current
+// environment. This assumes the current program is being run
+// by a web server in a CGI environment.
+func Request() (*http.Request, os.Error) {
+ return requestFromEnvironment(envMap(os.Environ()))
+}
+
+func envMap(env []string) map[string]string {
+ m := make(map[string]string)
+ for _, kv := range env {
+ if idx := strings.Index(kv, "="); idx != -1 {
+ m[kv[:idx]] = kv[idx+1:]
+ }
+ }
+ return m
+}
+
+// These environment variables are manually copied into Request
+var skipHeader = map[string]bool{
+ "HTTP_HOST": true,
+ "HTTP_REFERER": true,
+ "HTTP_USER_AGENT": true,
+}
+
+func requestFromEnvironment(env map[string]string) (*http.Request, os.Error) {
+ r := new(http.Request)
+ r.Method = env["REQUEST_METHOD"]
+ if r.Method == "" {
+ return nil, os.NewError("cgi: no REQUEST_METHOD in environment")
+ }
+ r.Close = true
+ r.Trailer = http.Header{}
+ r.Header = http.Header{}
+
+ r.Host = env["HTTP_HOST"]
+ r.Referer = env["HTTP_REFERER"]
+ r.UserAgent = env["HTTP_USER_AGENT"]
+
+ // CGI doesn't allow chunked requests, so these should all be accurate:
+ r.Proto = "HTTP/1.0"
+ r.ProtoMajor = 1
+ r.ProtoMinor = 0
+ r.TransferEncoding = nil
+
+ if lenstr := env["CONTENT_LENGTH"]; lenstr != "" {
+ clen, err := strconv.Atoi64(lenstr)
+ if err != nil {
+ return nil, os.NewError("cgi: bad CONTENT_LENGTH in environment: " + lenstr)
+ }
+ r.ContentLength = clen
+ r.Body = ioutil.NopCloser(io.LimitReader(os.Stdin, clen))
+ }
+
+ // Copy "HTTP_FOO_BAR" variables to "Foo-Bar" Headers
+ for k, v := range env {
+ if !strings.HasPrefix(k, "HTTP_") || skipHeader[k] {
+ continue
+ }
+ r.Header.Add(strings.Replace(k[5:], "_", "-", -1), v)
+ }
+
+ // TODO: cookies. parsing them isn't exported, though.
+
+ if r.Host != "" {
+ // Hostname is provided, so we can reasonably construct a URL,
+ // even if we have to assume 'http' for the scheme.
+ r.RawURL = "http://" + r.Host + env["REQUEST_URI"]
+ url, err := http.ParseURL(r.RawURL)
+ if err != nil {
+ return nil, os.NewError("cgi: failed to parse host and REQUEST_URI into a URL: " + r.RawURL)
+ }
+ r.URL = url
+ }
+ // Fallback logic if we don't have a Host header or the URL
+ // failed to parse
+ if r.URL == nil {
+ r.RawURL = env["REQUEST_URI"]
+ url, err := http.ParseURL(r.RawURL)
+ if err != nil {
+ return nil, os.NewError("cgi: failed to parse REQUEST_URI into a URL: " + r.RawURL)
+ }
+ r.URL = url
+ }
+ return r, nil
+}
+
+// Serve executes the provided Handler on the currently active CGI
+// request, if any. If there's no current CGI environment
+// an error is returned. The provided handler may be nil to use
+// http.DefaultServeMux.
+func Serve(handler http.Handler) os.Error {
+ req, err := Request()
+ if err != nil {
+ return err
+ }
+ if handler == nil {
+ handler = http.DefaultServeMux
+ }
+ rw := &response{
+ req: req,
+ header: make(http.Header),
+ bufw: bufio.NewWriter(os.Stdout),
+ }
+ handler.ServeHTTP(rw, req)
+ if err = rw.bufw.Flush(); err != nil {
+ return err
+ }
+ return nil
+}
+
+type response struct {
+ req *http.Request
+ header http.Header
+ bufw *bufio.Writer
+ headerSent bool
+}
+
+func (r *response) Flush() {
+ r.bufw.Flush()
+}
+
+func (r *response) RemoteAddr() string {
+ return os.Getenv("REMOTE_ADDR")
+}
+
+func (r *response) Header() http.Header {
+ return r.header
+}
+
+func (r *response) Write(p []byte) (n int, err os.Error) {
+ if !r.headerSent {
+ r.WriteHeader(http.StatusOK)
+ }
+ return r.bufw.Write(p)
+}
+
+func (r *response) WriteHeader(code int) {
+ if r.headerSent {
+ // Note: explicitly using Stderr, as Stdout is our HTTP output.
+ fmt.Fprintf(os.Stderr, "CGI attempted to write header twice on request for %s", r.req.URL)
+ return
+ }
+ r.headerSent = true
+ fmt.Fprintf(r.bufw, "Status: %d %s\r\n", code, http.StatusText(code))
+
+ // Set a default Content-Type
+ if _, hasType := r.header["Content-Type"]; !hasType {
+ r.header.Add("Content-Type", "text/html; charset=utf-8")
+ }
+
+ // TODO: add a method on http.Header to write itself to an io.Writer?
+ // This is duplicated code.
+ for k, vv := range r.header {
+ for _, v := range vv {
+ v = strings.Replace(v, "\n", "", -1)
+ v = strings.Replace(v, "\r", "", -1)
+ v = strings.TrimSpace(v)
+ fmt.Fprintf(r.bufw, "%s: %s\r\n", k, v)
+ }
+ }
+ r.bufw.Write([]byte("\r\n"))
+ r.bufw.Flush()
+}
+
+func (r *response) UsingTLS() bool {
+ // There's apparently a de-facto standard for this.
+ // http://docstore.mik.ua/orelly/linux/cgi/ch03_02.htm#ch03-35636
+ if s := os.Getenv("HTTPS"); s == "on" || s == "ON" || s == "1" {
+ return true
+ }
+ return false
+}
diff --git a/src/pkg/http/cgi/child_test.go b/src/pkg/http/cgi/child_test.go
new file mode 100644
index 000000000..db0e09cf6
--- /dev/null
+++ b/src/pkg/http/cgi/child_test.go
@@ -0,0 +1,83 @@
+// 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.
+
+// Tests for CGI (the child process perspective)
+
+package cgi
+
+import (
+ "testing"
+)
+
+func TestRequest(t *testing.T) {
+ env := map[string]string{
+ "REQUEST_METHOD": "GET",
+ "HTTP_HOST": "example.com",
+ "HTTP_REFERER": "elsewhere",
+ "HTTP_USER_AGENT": "goclient",
+ "HTTP_FOO_BAR": "baz",
+ "REQUEST_URI": "/path?a=b",
+ "CONTENT_LENGTH": "123",
+ }
+ req, err := requestFromEnvironment(env)
+ if err != nil {
+ t.Fatalf("requestFromEnvironment: %v", err)
+ }
+ if g, e := req.UserAgent, "goclient"; e != g {
+ t.Errorf("expected UserAgent %q; got %q", e, g)
+ }
+ if g, e := req.Method, "GET"; e != g {
+ t.Errorf("expected Method %q; got %q", e, g)
+ }
+ if g, e := req.Header.Get("User-Agent"), ""; e != g {
+ // Tests that we don't put recognized headers in the map
+ t.Errorf("expected User-Agent %q; got %q", e, g)
+ }
+ if g, e := req.ContentLength, int64(123); e != g {
+ t.Errorf("expected ContentLength %d; got %d", e, g)
+ }
+ if g, e := req.Referer, "elsewhere"; e != g {
+ t.Errorf("expected Referer %q; got %q", e, g)
+ }
+ if req.Header == nil {
+ t.Fatalf("unexpected nil Header")
+ }
+ if g, e := req.Header.Get("Foo-Bar"), "baz"; e != g {
+ t.Errorf("expected Foo-Bar %q; got %q", e, g)
+ }
+ if g, e := req.RawURL, "http://example.com/path?a=b"; e != g {
+ t.Errorf("expected RawURL %q; got %q", e, g)
+ }
+ if g, e := req.URL.String(), "http://example.com/path?a=b"; e != g {
+ t.Errorf("expected URL %q; got %q", e, g)
+ }
+ if g, e := req.FormValue("a"), "b"; e != g {
+ t.Errorf("expected FormValue(a) %q; got %q", e, g)
+ }
+ if req.Trailer == nil {
+ t.Errorf("unexpected nil Trailer")
+ }
+}
+
+func TestRequestWithoutHost(t *testing.T) {
+ env := map[string]string{
+ "HTTP_HOST": "",
+ "REQUEST_METHOD": "GET",
+ "REQUEST_URI": "/path?a=b",
+ "CONTENT_LENGTH": "123",
+ }
+ req, err := requestFromEnvironment(env)
+ if err != nil {
+ t.Fatalf("requestFromEnvironment: %v", err)
+ }
+ if g, e := req.RawURL, "/path?a=b"; e != g {
+ t.Errorf("expected RawURL %q; got %q", e, g)
+ }
+ if req.URL == nil {
+ t.Fatalf("unexpected nil URL")
+ }
+ if g, e := req.URL.String(), "/path?a=b"; e != g {
+ t.Errorf("expected URL %q; got %q", e, g)
+ }
+}
diff --git a/src/pkg/http/cgi/cgi.go b/src/pkg/http/cgi/host.go
index dba59efa2..a713d7c3c 100644
--- a/src/pkg/http/cgi/cgi.go
+++ b/src/pkg/http/cgi/host.go
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// This file implements the host side of CGI (being the webserver
+// parent process).
+
// Package cgi implements CGI (Common Gateway Interface) as specified
// in RFC 3875.
//
@@ -12,14 +15,15 @@
package cgi
import (
- "encoding/line"
+ "bufio"
+ "bytes"
"exec"
"fmt"
"http"
"io"
"log"
"os"
- "path"
+ "path/filepath"
"regexp"
"strconv"
"strings"
@@ -29,10 +33,12 @@ var trailingPort = regexp.MustCompile(`:([0-9]+)$`)
// Handler runs an executable in a subprocess with a CGI environment.
type Handler struct {
- Path string // path to the CGI executable
- Root string // root URI prefix of handler or empty for "/"
+ Path string // path to the CGI executable
+ Root string // root URI prefix of handler or empty for "/"
+
Env []string // extra environment variables to set, if any
Logger *log.Logger // optional log for errors or nil to use log.Print
+ Args []string // optional arguments to pass to child process
}
func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
@@ -68,14 +74,29 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
"PATH_INFO=" + pathInfo,
"SCRIPT_NAME=" + root,
"SCRIPT_FILENAME=" + h.Path,
- "REMOTE_ADDR=" + rw.RemoteAddr(),
- "REMOTE_HOST=" + rw.RemoteAddr(),
+ "REMOTE_ADDR=" + req.RemoteAddr,
+ "REMOTE_HOST=" + req.RemoteAddr,
"SERVER_PORT=" + port,
}
- for k, _ := range req.Header {
+ if req.TLS != nil {
+ env = append(env, "HTTPS=on")
+ }
+
+ if len(req.Cookie) > 0 {
+ b := new(bytes.Buffer)
+ for idx, c := range req.Cookie {
+ if idx > 0 {
+ b.Write([]byte("; "))
+ }
+ fmt.Fprintf(b, "%s=%s", c.Name, c.Value)
+ }
+ env = append(env, "HTTP_COOKIE="+b.String())
+ }
+
+ for k, v := range req.Header {
k = strings.Map(upperCaseAndUnderscore, k)
- env = append(env, "HTTP_"+k+"="+req.Header.Get(k))
+ env = append(env, "HTTP_"+k+"="+strings.Join(v, ", "))
}
if req.ContentLength > 0 {
@@ -89,15 +110,17 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
env = append(env, h.Env...)
}
- // TODO: use filepath instead of path when available
- cwd, pathBase := path.Split(h.Path)
+ cwd, pathBase := filepath.Split(h.Path)
if cwd == "" {
cwd = "."
}
+ args := []string{h.Path}
+ args = append(args, h.Args...)
+
cmd, err := exec.Run(
pathBase,
- []string{h.Path},
+ args,
env,
cwd,
exec.Pipe, // stdin
@@ -119,8 +142,8 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
go io.Copy(cmd.Stdin, req.Body)
}
- linebody := line.NewReader(cmd.Stdout, 1024)
- headers := make(map[string]string)
+ linebody, _ := bufio.NewReaderSize(cmd.Stdout, 1024)
+ headers := rw.Header()
statusCode := http.StatusOK
for {
line, isPrefix, err := linebody.ReadLine()
@@ -162,12 +185,9 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
}
statusCode = code
default:
- headers[header] = val
+ headers.Add(header, val)
}
}
- for h, v := range headers {
- rw.SetHeader(h, v)
- }
rw.WriteHeader(statusCode)
_, err = io.Copy(rw, linebody)
diff --git a/src/pkg/http/cgi/cgi_test.go b/src/pkg/http/cgi/host_test.go
index daf9a2cb3..e8084b113 100644
--- a/src/pkg/http/cgi/cgi_test.go
+++ b/src/pkg/http/cgi/host_test.go
@@ -37,6 +37,7 @@ func newRequest(httpreq string) *http.Request {
if err != nil {
panic("cgi: bogus http request in test: " + httpreq)
}
+ req.RemoteAddr = "1.2.3.4"
return req
}
@@ -47,6 +48,7 @@ func runCgiTest(t *testing.T, h *Handler, httpreq string, expectedMap map[string
// Make a map to hold the test map that the CGI returns.
m := make(map[string]string)
+ linesRead := 0
readlines:
for {
line, err := rw.Body.ReadString('\n')
@@ -56,10 +58,12 @@ readlines:
case err != nil:
t.Fatalf("unexpected error reading from CGI: %v", err)
}
- line = strings.TrimRight(line, "\r\n")
- split := strings.Split(line, "=", 2)
+ linesRead++
+ trimmedLine := strings.TrimRight(line, "\r\n")
+ split := strings.Split(trimmedLine, "=", 2)
if len(split) != 2 {
- t.Fatalf("Unexpected %d parts from invalid line: %q", len(split), line)
+ t.Fatalf("Unexpected %d parts from invalid line number %v: %q; existing map=%v",
+ len(split), linesRead, line, m)
}
m[split[0]] = split[1]
}
@@ -111,10 +115,10 @@ func TestCGIBasicGet(t *testing.T) {
}
replay := runCgiTest(t, h, "GET /test.cgi?foo=bar&a=b HTTP/1.0\nHost: example.com\n\n", expectedMap)
- if expected, got := "text/html", replay.Header.Get("Content-Type"); got != expected {
+ if expected, got := "text/html", replay.Header().Get("Content-Type"); got != expected {
t.Errorf("got a Content-Type of %q; expected %q", got, expected)
}
- if expected, got := "X-Test-Value", replay.Header.Get("X-Test-Header"); got != expected {
+ if expected, got := "X-Test-Value", replay.Header().Get("X-Test-Header"); got != expected {
t.Errorf("got a X-Test-Header of %q; expected %q", got, expected)
}
}
@@ -176,6 +180,28 @@ func TestPathInfoDirRoot(t *testing.T) {
runCgiTest(t, h, "GET /myscript/bar?a=b HTTP/1.0\nHost: example.com\n\n", expectedMap)
}
+func TestDupHeaders(t *testing.T) {
+ if skipTest(t) {
+ return
+ }
+ h := &Handler{
+ Path: "testdata/test.cgi",
+ }
+ expectedMap := map[string]string{
+ "env-REQUEST_URI": "/myscript/bar?a=b",
+ "env-SCRIPT_FILENAME": "testdata/test.cgi",
+ "env-HTTP_COOKIE": "nom=NOM; yum=YUM",
+ "env-HTTP_X_FOO": "val1, val2",
+ }
+ runCgiTest(t, h, "GET /myscript/bar?a=b HTTP/1.0\n"+
+ "Cookie: nom=NOM\n"+
+ "Cookie: yum=YUM\n"+
+ "X-Foo: val1\n"+
+ "X-Foo: val2\n"+
+ "Host: example.com\n\n",
+ expectedMap)
+}
+
func TestPathInfoNoRoot(t *testing.T) {
if skipTest(t) {
return
diff --git a/src/pkg/http/cgi/matryoshka_test.go b/src/pkg/http/cgi/matryoshka_test.go
new file mode 100644
index 000000000..3e4a6addf
--- /dev/null
+++ b/src/pkg/http/cgi/matryoshka_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.
+
+// Tests a Go CGI program running under a Go CGI host process.
+// Further, the two programs are the same binary, just checking
+// their environment to figure out what mode to run in.
+
+package cgi
+
+import (
+ "fmt"
+ "http"
+ "os"
+ "testing"
+)
+
+// This test is a CGI host (testing host.go) that runs its own binary
+// as a child process testing the other half of CGI (child.go).
+func TestHostingOurselves(t *testing.T) {
+ h := &Handler{
+ Path: os.Args[0],
+ Root: "/test.go",
+ Args: []string{"-test.run=TestBeChildCGIProcess"},
+ }
+ expectedMap := map[string]string{
+ "test": "Hello CGI-in-CGI",
+ "param-a": "b",
+ "param-foo": "bar",
+ "env-GATEWAY_INTERFACE": "CGI/1.1",
+ "env-HTTP_HOST": "example.com",
+ "env-PATH_INFO": "",
+ "env-QUERY_STRING": "foo=bar&a=b",
+ "env-REMOTE_ADDR": "1.2.3.4",
+ "env-REMOTE_HOST": "1.2.3.4",
+ "env-REQUEST_METHOD": "GET",
+ "env-REQUEST_URI": "/test.go?foo=bar&a=b",
+ "env-SCRIPT_FILENAME": os.Args[0],
+ "env-SCRIPT_NAME": "/test.go",
+ "env-SERVER_NAME": "example.com",
+ "env-SERVER_PORT": "80",
+ "env-SERVER_SOFTWARE": "go",
+ }
+ replay := runCgiTest(t, h, "GET /test.go?foo=bar&a=b HTTP/1.0\nHost: example.com\n\n", expectedMap)
+
+ if expected, got := "text/html; charset=utf-8", replay.Header().Get("Content-Type"); got != expected {
+ t.Errorf("got a Content-Type of %q; expected %q", got, expected)
+ }
+ if expected, got := "X-Test-Value", replay.Header().Get("X-Test-Header"); got != expected {
+ t.Errorf("got a X-Test-Header of %q; expected %q", got, expected)
+ }
+}
+
+// Note: not actually a test.
+func TestBeChildCGIProcess(t *testing.T) {
+ if os.Getenv("REQUEST_METHOD") == "" {
+ // Not in a CGI environment; skipping test.
+ return
+ }
+ Serve(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
+ rw.Header().Set("X-Test-Header", "X-Test-Value")
+ fmt.Fprintf(rw, "test=Hello CGI-in-CGI\n")
+ req.ParseForm()
+ for k, vv := range req.Form {
+ for _, v := range vv {
+ fmt.Fprintf(rw, "param-%s=%s\n", k, v)
+ }
+ }
+ for _, kv := range os.Environ() {
+ fmt.Fprintf(rw, "env-%s\n", kv)
+ }
+ }))
+ os.Exit(0)
+}
diff --git a/src/pkg/http/cgi/testdata/test.cgi b/src/pkg/http/cgi/testdata/test.cgi
index b931b04c5..253589eed 100755
--- a/src/pkg/http/cgi/testdata/test.cgi
+++ b/src/pkg/http/cgi/testdata/test.cgi
@@ -12,7 +12,7 @@ my $q = CGI->new;
my $params = $q->Vars;
my $NL = "\r\n";
-$NL = "\n" if 1 || $params->{mode} eq "NL";
+$NL = "\n" if $params->{mode} eq "NL";
my $p = sub {
print "$_[0]$NL";
@@ -30,5 +30,7 @@ foreach my $k (sort keys %$params) {
}
foreach my $k (sort keys %ENV) {
- print "env-$k=$ENV{$k}\n";
+ my $clean_env = $ENV{$k};
+ $clean_env =~ s/[\n\r]//g;
+ print "env-$k=$clean_env\n";
}
diff --git a/src/pkg/http/client.go b/src/pkg/http/client.go
index c24eea581..daba3a89b 100644
--- a/src/pkg/http/client.go
+++ b/src/pkg/http/client.go
@@ -11,6 +11,7 @@ import (
"encoding/base64"
"fmt"
"io"
+ "io/ioutil"
"os"
"strconv"
"strings"
@@ -20,26 +21,28 @@ import (
// that uses DefaultTransport.
// Client is not yet very configurable.
type Client struct {
- Transport Transport // if nil, DefaultTransport is used
+ Transport RoundTripper // if nil, DefaultTransport is used
}
// DefaultClient is the default Client and is used by Get, Head, and Post.
var DefaultClient = &Client{}
-// Transport is an interface representing the ability to execute a
+// RoundTripper is an interface representing the ability to execute a
// single HTTP transaction, obtaining the Response for a given Request.
-type Transport interface {
- // Do executes a single HTTP transaction, returning the Response for the
- // request req. Do should not attempt to interpret the response.
- // In particular, Do must return err == nil if it obtained a response,
- // regardless of the response's HTTP status code. A non-nil err should
- // be reserved for failure to obtain a response. Similarly, Do should
- // not attempt to handle higher-level protocol details such as redirects,
+type RoundTripper interface {
+ // RoundTrip executes a single HTTP transaction, returning
+ // the Response for the request req. RoundTrip should not
+ // attempt to interpret the response. In particular,
+ // RoundTrip must return err == nil if it obtained a response,
+ // regardless of the response's HTTP status code. A non-nil
+ // err should be reserved for failure to obtain a response.
+ // Similarly, RoundTrip should not attempt to handle
+ // higher-level protocol details such as redirects,
// authentication, or cookies.
//
- // Transports may modify the request. The request Headers field is
- // guaranteed to be initalized.
- Do(req *Request) (resp *Response, err os.Error)
+ // RoundTrip may modify the request. The request Headers field is
+ // guaranteed to be initialized.
+ RoundTrip(req *Request) (resp *Response, err os.Error)
}
// Given a string of the form "host", "host:port", or "[ipv6::address]:port",
@@ -54,40 +57,6 @@ type readClose struct {
io.Closer
}
-// matchNoProxy returns true if requests to addr should not use a proxy,
-// according to the NO_PROXY or no_proxy environment variable.
-func matchNoProxy(addr string) bool {
- if len(addr) == 0 {
- return false
- }
- no_proxy := os.Getenv("NO_PROXY")
- if len(no_proxy) == 0 {
- no_proxy = os.Getenv("no_proxy")
- }
- if no_proxy == "*" {
- return true
- }
-
- addr = strings.ToLower(strings.TrimSpace(addr))
- if hasPort(addr) {
- addr = addr[:strings.LastIndex(addr, ":")]
- }
-
- for _, p := range strings.Split(no_proxy, ",", -1) {
- p = strings.ToLower(strings.TrimSpace(p))
- if len(p) == 0 {
- continue
- }
- if hasPort(p) {
- p = p[:strings.LastIndex(p, ":")]
- }
- if addr == p || (p[0] == '.' && (strings.HasSuffix(addr, p) || addr == p[1:])) {
- return true
- }
- }
- return false
-}
-
// Do sends an HTTP request and returns an HTTP response, following
// policy (e.g. redirects, cookies, auth) as configured on the client.
//
@@ -100,11 +69,7 @@ func (c *Client) Do(req *Request) (resp *Response, err os.Error) {
// send issues an HTTP request. Caller should close resp.Body when done reading from it.
-//
-// TODO: support persistent connections (multiple requests on a single connection).
-// send() method is nonpublic because, when we refactor the code for persistent
-// connections, it may no longer make sense to have a method with this signature.
-func send(req *Request, t Transport) (resp *Response, err os.Error) {
+func send(req *Request, t RoundTripper) (resp *Response, err os.Error) {
if t == nil {
t = DefaultTransport
if t == nil {
@@ -130,7 +95,7 @@ func send(req *Request, t Transport) (resp *Response, err os.Error) {
}
req.Header.Set("Authorization", "Basic "+string(encoded))
}
- return t.Do(req)
+ return t.RoundTrip(req)
}
// True if the specified HTTP status code is one for which the Get utility should
@@ -237,7 +202,7 @@ func (c *Client) Post(url string, bodyType string, body io.Reader) (r *Response,
req.ProtoMajor = 1
req.ProtoMinor = 1
req.Close = true
- req.Body = nopCloser{body}
+ req.Body = ioutil.NopCloser(body)
req.Header = Header{
"Content-Type": {bodyType},
}
@@ -272,7 +237,7 @@ func (c *Client) PostForm(url string, data map[string]string) (r *Response, err
req.ProtoMinor = 1
req.Close = true
body := urlencode(data)
- req.Body = nopCloser{body}
+ req.Body = ioutil.NopCloser(body)
req.Header = Header{
"Content-Type": {"application/x-www-form-urlencoded"},
"Content-Length": {strconv.Itoa(body.Len())},
@@ -312,9 +277,3 @@ func (c *Client) Head(url string) (r *Response, err os.Error) {
}
return send(&req, c.Transport)
}
-
-type nopCloser struct {
- io.Reader
-}
-
-func (nopCloser) Close() os.Error { return nil }
diff --git a/src/pkg/http/client_test.go b/src/pkg/http/client_test.go
index c89ecbce2..3a6f83425 100644
--- a/src/pkg/http/client_test.go
+++ b/src/pkg/http/client_test.go
@@ -4,20 +4,28 @@
// Tests for client.go
-package http
+package http_test
import (
+ "fmt"
+ . "http"
+ "http/httptest"
"io/ioutil"
"os"
"strings"
"testing"
)
+var robotsTxtHandler = HandlerFunc(func(w ResponseWriter, r *Request) {
+ w.Header().Set("Last-Modified", "sometime")
+ fmt.Fprintf(w, "User-agent: go\nDisallow: /something/")
+})
+
func TestClient(t *testing.T) {
- // TODO: add a proper test suite. Current test merely verifies that
- // we can retrieve the Google robots.txt file.
+ ts := httptest.NewServer(robotsTxtHandler)
+ defer ts.Close()
- r, _, err := Get("http://www.google.com/robots.txt")
+ r, _, err := Get(ts.URL)
var b []byte
if err == nil {
b, err = ioutil.ReadAll(r.Body)
@@ -31,7 +39,10 @@ func TestClient(t *testing.T) {
}
func TestClientHead(t *testing.T) {
- r, err := Head("http://www.google.com/robots.txt")
+ ts := httptest.NewServer(robotsTxtHandler)
+ defer ts.Close()
+
+ r, err := Head(ts.URL)
if err != nil {
t.Fatal(err)
}
@@ -44,7 +55,7 @@ type recordingTransport struct {
req *Request
}
-func (t *recordingTransport) Do(req *Request) (resp *Response, err os.Error) {
+func (t *recordingTransport) RoundTrip(req *Request) (resp *Response, err os.Error) {
t.req = req
return nil, os.NewError("dummy impl")
}
diff --git a/src/pkg/http/cookie.go b/src/pkg/http/cookie.go
index ff75c47c9..2bb66e58e 100644
--- a/src/pkg/http/cookie.go
+++ b/src/pkg/http/cookie.go
@@ -15,65 +15,28 @@ import (
"time"
)
-// A note on Version=0 vs. Version=1 cookies
+// This implementation is done according to IETF draft-ietf-httpstate-cookie-23, found at
//
-// The difference between Set-Cookie and Set-Cookie2 is hard to discern from the
-// RFCs as it is not stated explicitly. There seem to be three standards
-// lingering on the web: Netscape, RFC 2109 (aka Version=0) and RFC 2965 (aka
-// Version=1). It seems that Netscape and RFC 2109 are the same thing, hereafter
-// Version=0 cookies.
-//
-// In general, Set-Cookie2 is a superset of Set-Cookie. It has a few new
-// attributes like HttpOnly and Secure. To be meticulous, if a server intends
-// to use these, it needs to send a Set-Cookie2. However, it is most likely
-// most modern browsers will not complain seeing an HttpOnly attribute in a
-// Set-Cookie header.
-//
-// Both RFC 2109 and RFC 2965 use Cookie in the same way - two send cookie
-// values from clients to servers - and the allowable attributes seem to be the
-// same.
-//
-// The Cookie2 header is used for a different purpose. If a client suspects that
-// the server speaks Version=1 (RFC 2965) then along with the Cookie header
-// lines, you can also send:
-//
-// Cookie2: $Version="1"
-//
-// in order to suggest to the server that you understand Version=1 cookies. At
-// which point the server may continue responding with Set-Cookie2 headers.
-// When a client sends the (above) Cookie2 header line, it must be prepated to
-// understand incoming Set-Cookie2.
-//
-// This implementation of cookies supports neither Set-Cookie2 nor Cookie2
-// headers. However, it parses Version=1 Cookies (along with Version=0) as well
-// as Set-Cookie headers which utilize the full Set-Cookie2 syntax.
-
-// TODO(petar): Explicitly forbid parsing of Set-Cookie attributes
-// starting with '$', which have been used to hack into broken
-// servers using the eventual Request headers containing those
-// invalid attributes that may overwrite intended $Version, $Path,
-// etc. attributes.
-// TODO(petar): Read 'Set-Cookie2' headers and prioritize them over equivalent
-// 'Set-Cookie' headers. 'Set-Cookie2' headers are still extremely rare.
+// http://tools.ietf.org/html/draft-ietf-httpstate-cookie-23
-// A Cookie represents an RFC 2965 HTTP cookie as sent in
-// the Set-Cookie header of an HTTP response or the Cookie header
-// of an HTTP request.
-// The Set-Cookie2 and Cookie2 headers are unimplemented.
+// A Cookie represents an HTTP cookie as sent in the Set-Cookie header of an
+// HTTP response or the Cookie header of an HTTP request.
type Cookie struct {
Name string
Value string
Path string
Domain string
- Comment string
- Version int
Expires time.Time
RawExpires string
- MaxAge int // Max age in seconds
- Secure bool
- HttpOnly bool
- Raw string
- Unparsed []string // Raw text of unparsed attribute-value pairs
+
+ // MaxAge=0 means no 'Max-Age' attribute specified.
+ // MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'
+ // MaxAge>0 means Max-Age attribute present and given in seconds
+ MaxAge int
+ Secure bool
+ HttpOnly bool
+ Raw string
+ Unparsed []string // Raw text of unparsed attribute-value pairs
}
// readSetCookies parses all "Set-Cookie" values from
@@ -94,16 +57,19 @@ func readSetCookies(h Header) []*Cookie {
continue
}
name, value := parts[0][:j], parts[0][j+1:]
- value, err := URLUnescape(value)
- if err != nil {
+ if !isCookieNameValid(name) {
+ unparsedLines = append(unparsedLines, line)
+ continue
+ }
+ value, success := parseCookieValue(value)
+ if !success {
unparsedLines = append(unparsedLines, line)
continue
}
c := &Cookie{
- Name: name,
- Value: value,
- MaxAge: -1, // Not specified
- Raw: line,
+ Name: name,
+ Value: value,
+ Raw: line,
}
for i := 1; i < len(parts); i++ {
parts[i] = strings.TrimSpace(parts[i])
@@ -114,11 +80,11 @@ func readSetCookies(h Header) []*Cookie {
attr, val := parts[i], ""
if j := strings.Index(attr, "="); j >= 0 {
attr, val = attr[:j], attr[j+1:]
- val, err = URLUnescape(val)
- if err != nil {
- c.Unparsed = append(c.Unparsed, parts[i])
- continue
- }
+ }
+ val, success = parseCookieValue(val)
+ if !success {
+ c.Unparsed = append(c.Unparsed, parts[i])
+ continue
}
switch strings.ToLower(attr) {
case "secure":
@@ -127,19 +93,20 @@ func readSetCookies(h Header) []*Cookie {
case "httponly":
c.HttpOnly = true
continue
- case "comment":
- c.Comment = val
- continue
case "domain":
c.Domain = val
// TODO: Add domain parsing
continue
case "max-age":
secs, err := strconv.Atoi(val)
- if err != nil || secs < 0 {
+ if err != nil || secs < 0 || secs != 0 && val[0] == '0' {
break
}
- c.MaxAge = secs
+ if secs <= 0 {
+ c.MaxAge = -1
+ } else {
+ c.MaxAge = secs
+ }
continue
case "expires":
c.RawExpires = val
@@ -154,13 +121,6 @@ func readSetCookies(h Header) []*Cookie {
c.Path = val
// TODO: Add path parsing
continue
- case "version":
- c.Version, err = strconv.Atoi(val)
- if err != nil {
- c.Version = 0
- break
- }
- continue
}
c.Unparsed = append(c.Unparsed, parts[i])
}
@@ -182,11 +142,7 @@ func writeSetCookies(w io.Writer, kk []*Cookie) os.Error {
var b bytes.Buffer
for _, c := range kk {
b.Reset()
- // TODO(petar): c.Value (below) should be unquoted if it is recognized as quoted
- fmt.Fprintf(&b, "%s=%s", CanonicalHeaderKey(c.Name), c.Value)
- if c.Version > 0 {
- fmt.Fprintf(&b, "Version=%d; ", c.Version)
- }
+ fmt.Fprintf(&b, "%s=%s", c.Name, c.Value)
if len(c.Path) > 0 {
fmt.Fprintf(&b, "; Path=%s", URLEscape(c.Path))
}
@@ -196,8 +152,10 @@ func writeSetCookies(w io.Writer, kk []*Cookie) os.Error {
if len(c.Expires.Zone) > 0 {
fmt.Fprintf(&b, "; Expires=%s", c.Expires.Format(time.RFC1123))
}
- if c.MaxAge >= 0 {
+ if c.MaxAge > 0 {
fmt.Fprintf(&b, "; Max-Age=%d", c.MaxAge)
+ } else if c.MaxAge < 0 {
+ fmt.Fprintf(&b, "; Max-Age=0")
}
if c.HttpOnly {
fmt.Fprintf(&b, "; HttpOnly")
@@ -205,9 +163,6 @@ func writeSetCookies(w io.Writer, kk []*Cookie) os.Error {
if c.Secure {
fmt.Fprintf(&b, "; Secure")
}
- if len(c.Comment) > 0 {
- fmt.Fprintf(&b, "; Comment=%s", URLEscape(c.Comment))
- }
lines = append(lines, "Set-Cookie: "+b.String()+"\r\n")
}
sort.SortStrings(lines)
@@ -235,63 +190,29 @@ func readCookies(h Header) []*Cookie {
continue
}
// Per-line attributes
- var lineCookies = make(map[string]string)
- var version int
- var path string
- var domain string
- var comment string
- var httponly bool
+ parsedPairs := 0
for i := 0; i < len(parts); i++ {
parts[i] = strings.TrimSpace(parts[i])
if len(parts[i]) == 0 {
continue
}
attr, val := parts[i], ""
- var err os.Error
if j := strings.Index(attr, "="); j >= 0 {
attr, val = attr[:j], attr[j+1:]
- val, err = URLUnescape(val)
- if err != nil {
- continue
- }
}
- switch strings.ToLower(attr) {
- case "$httponly":
- httponly = true
- case "$version":
- version, err = strconv.Atoi(val)
- if err != nil {
- version = 0
- continue
- }
- case "$domain":
- domain = val
- // TODO: Add domain parsing
- case "$path":
- path = val
- // TODO: Add path parsing
- case "$comment":
- comment = val
- default:
- lineCookies[attr] = val
+ if !isCookieNameValid(attr) {
+ continue
+ }
+ val, success := parseCookieValue(val)
+ if !success {
+ continue
}
+ cookies = append(cookies, &Cookie{Name: attr, Value: val})
+ parsedPairs++
}
- if len(lineCookies) == 0 {
+ if parsedPairs == 0 {
unparsedLines = append(unparsedLines, line)
}
- for n, v := range lineCookies {
- cookies = append(cookies, &Cookie{
- Name: n,
- Value: v,
- Path: path,
- Domain: domain,
- Comment: comment,
- Version: version,
- HttpOnly: httponly,
- MaxAge: -1,
- Raw: line,
- })
- }
}
h["Cookie"] = unparsedLines, len(unparsedLines) > 0
return cookies
@@ -303,28 +224,8 @@ func readCookies(h Header) []*Cookie {
// line-length, so it seems safer to place cookies on separate lines.
func writeCookies(w io.Writer, kk []*Cookie) os.Error {
lines := make([]string, 0, len(kk))
- var b bytes.Buffer
for _, c := range kk {
- b.Reset()
- n := c.Name
- if c.Version > 0 {
- fmt.Fprintf(&b, "$Version=%d; ", c.Version)
- }
- // TODO(petar): c.Value (below) should be unquoted if it is recognized as quoted
- fmt.Fprintf(&b, "%s=%s", CanonicalHeaderKey(n), c.Value)
- if len(c.Path) > 0 {
- fmt.Fprintf(&b, "; $Path=%s", URLEscape(c.Path))
- }
- if len(c.Domain) > 0 {
- fmt.Fprintf(&b, "; $Domain=%s", URLEscape(c.Domain))
- }
- if c.HttpOnly {
- fmt.Fprintf(&b, "; $HttpOnly")
- }
- if len(c.Comment) > 0 {
- fmt.Fprintf(&b, "; $Comment=%s", URLEscape(c.Comment))
- }
- lines = append(lines, "Cookie: "+b.String()+"\r\n")
+ lines = append(lines, fmt.Sprintf("Cookie: %s=%s\r\n", c.Name, c.Value))
}
sort.SortStrings(lines)
for _, l := range lines {
@@ -334,3 +235,38 @@ func writeCookies(w io.Writer, kk []*Cookie) os.Error {
}
return nil
}
+
+func unquoteCookieValue(v string) string {
+ if len(v) > 1 && v[0] == '"' && v[len(v)-1] == '"' {
+ return v[1 : len(v)-1]
+ }
+ return v
+}
+
+func isCookieByte(c byte) bool {
+ switch true {
+ case c == 0x21, 0x23 <= c && c <= 0x2b, 0x2d <= c && c <= 0x3a,
+ 0x3c <= c && c <= 0x5b, 0x5d <= c && c <= 0x7e:
+ return true
+ }
+ return false
+}
+
+func parseCookieValue(raw string) (string, bool) {
+ raw = unquoteCookieValue(raw)
+ for i := 0; i < len(raw); i++ {
+ if !isCookieByte(raw[i]) {
+ return "", false
+ }
+ }
+ return raw, true
+}
+
+func isCookieNameValid(raw string) bool {
+ for _, c := range raw {
+ if !isToken(byte(c)) {
+ return false
+ }
+ }
+ return true
+}
diff --git a/src/pkg/http/cookie_test.go b/src/pkg/http/cookie_test.go
index 363c841bb..db0997040 100644
--- a/src/pkg/http/cookie_test.go
+++ b/src/pkg/http/cookie_test.go
@@ -6,6 +6,8 @@ package http
import (
"bytes"
+ "fmt"
+ "json"
"reflect"
"testing"
)
@@ -16,8 +18,12 @@ var writeSetCookiesTests = []struct {
Raw string
}{
{
- []*Cookie{&Cookie{Name: "cookie-1", Value: "v$1", MaxAge: -1}},
- "Set-Cookie: Cookie-1=v$1\r\n",
+ []*Cookie{
+ &Cookie{Name: "cookie-1", Value: "v$1"},
+ &Cookie{Name: "cookie-2", Value: "two", MaxAge: 3600},
+ },
+ "Set-Cookie: cookie-1=v$1\r\n" +
+ "Set-Cookie: cookie-2=two; Max-Age=3600\r\n",
},
}
@@ -38,8 +44,8 @@ var writeCookiesTests = []struct {
Raw string
}{
{
- []*Cookie{&Cookie{Name: "cookie-1", Value: "v$1", MaxAge: -1}},
- "Cookie: Cookie-1=v$1\r\n",
+ []*Cookie{&Cookie{Name: "cookie-1", Value: "v$1"}},
+ "Cookie: cookie-1=v$1\r\n",
},
}
@@ -61,15 +67,23 @@ var readSetCookiesTests = []struct {
}{
{
Header{"Set-Cookie": {"Cookie-1=v$1"}},
- []*Cookie{&Cookie{Name: "Cookie-1", Value: "v$1", MaxAge: -1, Raw: "Cookie-1=v$1"}},
+ []*Cookie{&Cookie{Name: "Cookie-1", Value: "v$1", Raw: "Cookie-1=v$1"}},
},
}
+func toJSON(v interface{}) string {
+ b, err := json.Marshal(v)
+ if err != nil {
+ return fmt.Sprintf("%#v", v)
+ }
+ return string(b)
+}
+
func TestReadSetCookies(t *testing.T) {
for i, tt := range readSetCookiesTests {
c := readSetCookies(tt.Header)
if !reflect.DeepEqual(c, tt.Cookies) {
- t.Errorf("#%d readSetCookies: have\n%#v\nwant\n%#v\n", i, c, tt.Cookies)
+ t.Errorf("#%d readSetCookies: have\n%s\nwant\n%s\n", i, toJSON(c), toJSON(tt.Cookies))
continue
}
}
@@ -81,7 +95,7 @@ var readCookiesTests = []struct {
}{
{
Header{"Cookie": {"Cookie-1=v$1"}},
- []*Cookie{&Cookie{Name: "Cookie-1", Value: "v$1", MaxAge: -1, Raw: "Cookie-1=v$1"}},
+ []*Cookie{&Cookie{Name: "Cookie-1", Value: "v$1"}},
},
}
@@ -89,7 +103,7 @@ func TestReadCookies(t *testing.T) {
for i, tt := range readCookiesTests {
c := readCookies(tt.Header)
if !reflect.DeepEqual(c, tt.Cookies) {
- t.Errorf("#%d readCookies: have\n%#v\nwant\n%#v\n", i, c, tt.Cookies)
+ t.Errorf("#%d readCookies: have\n%s\nwant\n%s\n", i, toJSON(c), toJSON(tt.Cookies))
continue
}
}
diff --git a/src/pkg/http/dump.go b/src/pkg/http/dump.go
index 73ac97973..306c45bc2 100644
--- a/src/pkg/http/dump.go
+++ b/src/pkg/http/dump.go
@@ -7,10 +7,10 @@ package http
import (
"bytes"
"io"
+ "io/ioutil"
"os"
)
-
// One of the copies, say from b to r2, could be avoided by using a more
// elaborate trick where the other copy is made during Request/Response.Write.
// This would complicate things too much, given that these functions are for
@@ -23,7 +23,7 @@ func drainBody(b io.ReadCloser) (r1, r2 io.ReadCloser, err os.Error) {
if err = b.Close(); err != nil {
return nil, nil, err
}
- return nopCloser{&buf}, nopCloser{bytes.NewBuffer(buf.Bytes())}, nil
+ return ioutil.NopCloser(&buf), ioutil.NopCloser(bytes.NewBuffer(buf.Bytes())), nil
}
// DumpRequest returns the wire representation of req,
diff --git a/src/pkg/http/export_test.go b/src/pkg/http/export_test.go
new file mode 100644
index 000000000..47c687760
--- /dev/null
+++ b/src/pkg/http/export_test.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.
+
+// Bridge package to expose http internals to tests in the http_test
+// package.
+
+package http
+
+func (t *Transport) IdleConnKeysForTesting() (keys []string) {
+ keys = make([]string, 0)
+ t.lk.Lock()
+ defer t.lk.Unlock()
+ if t.idleConn == nil {
+ return
+ }
+ for key := range t.idleConn {
+ keys = append(keys, key)
+ }
+ return
+}
+
+func (t *Transport) IdleConnCountForTesting(cacheKey string) int {
+ t.lk.Lock()
+ defer t.lk.Unlock()
+ if t.idleConn == nil {
+ return 0
+ }
+ conns, ok := t.idleConn[cacheKey]
+ if !ok {
+ return 0
+ }
+ return len(conns)
+}
diff --git a/src/pkg/http/fs.go b/src/pkg/http/fs.go
index a4cd7072e..c5efffca9 100644
--- a/src/pkg/http/fs.go
+++ b/src/pkg/http/fs.go
@@ -72,7 +72,7 @@ func serveFile(w ResponseWriter, r *Request, name string, redirect bool) {
return
}
- f, err := os.Open(name, os.O_RDONLY, 0)
+ f, err := os.Open(name)
if err != nil {
// TODO expose actual error?
NotFound(w, r)
@@ -108,12 +108,12 @@ func serveFile(w ResponseWriter, r *Request, name string, redirect bool) {
w.WriteHeader(StatusNotModified)
return
}
- w.SetHeader("Last-Modified", time.SecondsToUTC(d.Mtime_ns/1e9).Format(TimeFormat))
+ w.Header().Set("Last-Modified", time.SecondsToUTC(d.Mtime_ns/1e9).Format(TimeFormat))
// use contents of index.html for directory, if present
if d.IsDirectory() {
index := name + filepath.FromSlash(indexPage)
- ff, err := os.Open(index, os.O_RDONLY, 0)
+ ff, err := os.Open(index)
if err == nil {
defer ff.Close()
dd, err := ff.Stat()
@@ -134,43 +134,48 @@ func serveFile(w ResponseWriter, r *Request, name string, redirect bool) {
size := d.Size
code := StatusOK
- // use extension to find content type.
- ext := filepath.Ext(name)
- if ctype := mime.TypeByExtension(ext); ctype != "" {
- w.SetHeader("Content-Type", ctype)
- } else {
- // read first chunk to decide between utf-8 text and binary
- var buf [1024]byte
- n, _ := io.ReadFull(f, buf[:])
- b := buf[:n]
- if isText(b) {
- w.SetHeader("Content-Type", "text-plain; charset=utf-8")
- } else {
- w.SetHeader("Content-Type", "application/octet-stream") // generic binary
+ // If Content-Type isn't set, use the file's extension to find it.
+ if w.Header().Get("Content-Type") == "" {
+ ctype := mime.TypeByExtension(filepath.Ext(name))
+ if ctype == "" {
+ // read a chunk to decide between utf-8 text and binary
+ var buf [1024]byte
+ n, _ := io.ReadFull(f, buf[:])
+ b := buf[:n]
+ if isText(b) {
+ ctype = "text-plain; charset=utf-8"
+ } else {
+ // generic binary
+ ctype = "application/octet-stream"
+ }
+ f.Seek(0, os.SEEK_SET) // rewind to output whole file
}
- f.Seek(0, 0) // rewind to output whole file
+ w.Header().Set("Content-Type", ctype)
}
// handle Content-Range header.
// TODO(adg): handle multiple ranges
ranges, err := parseRange(r.Header.Get("Range"), size)
- if err != nil || len(ranges) > 1 {
+ if err == nil && len(ranges) > 1 {
+ err = os.ErrorString("multiple ranges not supported")
+ }
+ if err != nil {
Error(w, err.String(), StatusRequestedRangeNotSatisfiable)
return
}
if len(ranges) == 1 {
ra := ranges[0]
- if _, err := f.Seek(ra.start, 0); err != nil {
+ if _, err := f.Seek(ra.start, os.SEEK_SET); err != nil {
Error(w, err.String(), StatusRequestedRangeNotSatisfiable)
return
}
size = ra.length
code = StatusPartialContent
- w.SetHeader("Content-Range", fmt.Sprintf("bytes %d-%d/%d", ra.start, ra.start+ra.length-1, d.Size))
+ w.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", ra.start, ra.start+ra.length-1, d.Size))
}
- w.SetHeader("Accept-Ranges", "bytes")
- w.SetHeader("Content-Length", strconv.Itoa64(size))
+ w.Header().Set("Accept-Ranges", "bytes")
+ w.Header().Set("Content-Length", strconv.Itoa64(size))
w.WriteHeader(code)
diff --git a/src/pkg/http/fs_test.go b/src/pkg/http/fs_test.go
index a89c76d0b..692b9863e 100644
--- a/src/pkg/http/fs_test.go
+++ b/src/pkg/http/fs_test.go
@@ -85,6 +85,30 @@ func TestServeFile(t *testing.T) {
}
}
+func TestServeFileContentType(t *testing.T) {
+ const ctype = "icecream/chocolate"
+ override := false
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ if override {
+ w.Header().Set("Content-Type", ctype)
+ }
+ ServeFile(w, r, "testdata/file")
+ }))
+ defer ts.Close()
+ get := func(want string) {
+ resp, _, err := Get(ts.URL)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if h := resp.Header.Get("Content-Type"); h != want {
+ t.Errorf("Content-Type mismatch: got %q, want %q", h, want)
+ }
+ }
+ get("text-plain; charset=utf-8")
+ override = true
+ get(ctype)
+}
+
func getBody(t *testing.T, req Request) (*Response, []byte) {
r, err := DefaultClient.Do(&req)
if err != nil {
diff --git a/src/pkg/http/httptest/recorder.go b/src/pkg/http/httptest/recorder.go
index ec7bde8aa..0dd19a617 100644
--- a/src/pkg/http/httptest/recorder.go
+++ b/src/pkg/http/httptest/recorder.go
@@ -14,20 +14,17 @@ import (
// ResponseRecorder is an implementation of http.ResponseWriter that
// records its mutations for later inspection in tests.
type ResponseRecorder struct {
- Code int // the HTTP response code from WriteHeader
- Header http.Header // if non-nil, the headers to populate
- Body *bytes.Buffer // if non-nil, the bytes.Buffer to append written data to
- Flushed bool
-
- FakeRemoteAddr string // the fake RemoteAddr to return, or "" for DefaultRemoteAddr
- FakeUsingTLS bool // whether to return true from the UsingTLS method
+ Code int // the HTTP response code from WriteHeader
+ HeaderMap http.Header // the HTTP response headers
+ Body *bytes.Buffer // if non-nil, the bytes.Buffer to append written data to
+ Flushed bool
}
// NewRecorder returns an initialized ResponseRecorder.
func NewRecorder() *ResponseRecorder {
return &ResponseRecorder{
- Header: http.Header(make(map[string][]string)),
- Body: new(bytes.Buffer),
+ HeaderMap: make(http.Header),
+ Body: new(bytes.Buffer),
}
}
@@ -35,29 +32,9 @@ func NewRecorder() *ResponseRecorder {
// an explicit DefaultRemoteAddr isn't set on ResponseRecorder.
const DefaultRemoteAddr = "1.2.3.4"
-// RemoteAddr returns the value of rw.FakeRemoteAddr, if set, else
-// returns DefaultRemoteAddr.
-func (rw *ResponseRecorder) RemoteAddr() string {
- if rw.FakeRemoteAddr != "" {
- return rw.FakeRemoteAddr
- }
- return DefaultRemoteAddr
-}
-
-// UsingTLS returns the fake value in rw.FakeUsingTLS
-func (rw *ResponseRecorder) UsingTLS() bool {
- return rw.FakeUsingTLS
-}
-
-// SetHeader populates rw.Header, if non-nil.
-func (rw *ResponseRecorder) SetHeader(k, v string) {
- if rw.Header != nil {
- if v == "" {
- rw.Header.Del(k)
- } else {
- rw.Header.Set(k, v)
- }
- }
+// Header returns the response headers.
+func (rw *ResponseRecorder) Header() http.Header {
+ return rw.HeaderMap
}
// Write always succeeds and writes to rw.Body, if not nil.
@@ -65,6 +42,9 @@ func (rw *ResponseRecorder) Write(buf []byte) (int, os.Error) {
if rw.Body != nil {
rw.Body.Write(buf)
}
+ if rw.Code == 0 {
+ rw.Code = http.StatusOK
+ }
return len(buf), nil
}
diff --git a/src/pkg/http/httptest/server.go b/src/pkg/http/httptest/server.go
index 86c9eb435..8e385d045 100644
--- a/src/pkg/http/httptest/server.go
+++ b/src/pkg/http/httptest/server.go
@@ -7,9 +7,13 @@
package httptest
import (
+ "crypto/rand"
+ "crypto/tls"
"fmt"
"http"
"net"
+ "os"
+ "time"
)
// A Server is an HTTP server listening on a system-chosen port on the
@@ -17,22 +21,69 @@ import (
type Server struct {
URL string // base URL of form http://ipaddr:port with no trailing slash
Listener net.Listener
+ TLS *tls.Config // nil if not using using TLS
}
-// NewServer starts and returns a new Server.
-// The caller should call Close when finished, to shut it down.
-func NewServer(handler http.Handler) *Server {
- ts := new(Server)
+// historyListener keeps track of all connections that it's ever
+// accepted.
+type historyListener struct {
+ net.Listener
+ history []net.Conn
+}
+
+func (hs *historyListener) Accept() (c net.Conn, err os.Error) {
+ c, err = hs.Listener.Accept()
+ if err == nil {
+ hs.history = append(hs.history, c)
+ }
+ return
+}
+
+func newLocalListener() net.Listener {
l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
if l, err = net.Listen("tcp6", "[::1]:0"); err != nil {
panic(fmt.Sprintf("httptest: failed to listen on a port: %v", err))
}
}
- ts.Listener = l
+ return l
+}
+
+// NewServer starts and returns a new Server.
+// The caller should call Close when finished, to shut it down.
+func NewServer(handler http.Handler) *Server {
+ ts := new(Server)
+ l := newLocalListener()
+ ts.Listener = &historyListener{l, make([]net.Conn, 0)}
ts.URL = "http://" + l.Addr().String()
server := &http.Server{Handler: handler}
- go server.Serve(l)
+ go server.Serve(ts.Listener)
+ return ts
+}
+
+// NewTLSServer starts and returns a new Server using TLS.
+// The caller should call Close when finished, to shut it down.
+func NewTLSServer(handler http.Handler) *Server {
+ l := newLocalListener()
+ ts := new(Server)
+
+ cert, err := tls.X509KeyPair(localhostCert, localhostKey)
+ if err != nil {
+ panic(fmt.Sprintf("httptest: NewTLSServer: %v", err))
+ }
+
+ ts.TLS = &tls.Config{
+ Rand: rand.Reader,
+ Time: time.Seconds,
+ NextProtos: []string{"http/1.1"},
+ Certificates: []tls.Certificate{cert},
+ }
+ tlsListener := tls.NewListener(l, ts.TLS)
+
+ ts.Listener = &historyListener{tlsListener, make([]net.Conn, 0)}
+ ts.URL = "https://" + l.Addr().String()
+ server := &http.Server{Handler: handler}
+ go server.Serve(ts.Listener)
return ts
}
@@ -40,3 +91,46 @@ func NewServer(handler http.Handler) *Server {
func (s *Server) Close() {
s.Listener.Close()
}
+
+// CloseClientConnections closes any currently open HTTP connections
+// to the test Server.
+func (s *Server) CloseClientConnections() {
+ hl, ok := s.Listener.(*historyListener)
+ if !ok {
+ return
+ }
+ for _, conn := range hl.history {
+ conn.Close()
+ }
+}
+
+// localhostCert is a PEM-encoded TLS cert with SAN DNS names
+// "127.0.0.1" and "[::1]", expiring at the last second of 2049 (the end
+// of ASN.1 time).
+var localhostCert = []byte(`-----BEGIN CERTIFICATE-----
+MIIBwTCCASugAwIBAgIBADALBgkqhkiG9w0BAQUwADAeFw0xMTAzMzEyMDI1MDda
+Fw00OTEyMzEyMzU5NTlaMAAwggCdMAsGCSqGSIb3DQEBAQOCAIwAMIIAhwKCAIB6
+oy4iT42G6qk+GGn5VL5JlnJT6ZG5cqaMNFaNGlIxNb6CPUZLKq2sM3gRaimsktIw
+nNAcNwQGHpe1tZo+J/Pl04JTt71Y/TTAxy7OX27aZf1Rpt0SjdZ7vTPnFDPNsHGe
+KBKvPt55l2+YKjkZmV7eRevsVbpkNvNGB+T5d4Ge/wIBA6NPME0wDgYDVR0PAQH/
+BAQDAgCgMA0GA1UdDgQGBAQBAgMEMA8GA1UdIwQIMAaABAECAwQwGwYDVR0RBBQw
+EoIJMTI3LjAuMC4xggVbOjoxXTALBgkqhkiG9w0BAQUDggCBAHC3gbdvc44vs+wD
+g2kONiENnx8WKc0UTGg/TOXS3gaRb+CUIQtHWja65l8rAfclEovjHgZ7gx8brO0W
+JuC6p3MUAKsgOssIrrRIx2rpnfcmFVMzguCmrMNVmKUAalw18Yp0F72xYAIitVQl
+kJrLdIhBajcJRYu/YGltHQRaXuVt
+-----END CERTIFICATE-----
+`)
+
+// localhostKey is the private key for localhostCert.
+var localhostKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
+MIIBkgIBAQKCAIB6oy4iT42G6qk+GGn5VL5JlnJT6ZG5cqaMNFaNGlIxNb6CPUZL
+Kq2sM3gRaimsktIwnNAcNwQGHpe1tZo+J/Pl04JTt71Y/TTAxy7OX27aZf1Rpt0S
+jdZ7vTPnFDPNsHGeKBKvPt55l2+YKjkZmV7eRevsVbpkNvNGB+T5d4Ge/wIBAwKC
+AIBRwh7Bil5Z8cYpZZv7jdQxDvbim7Z7ocRdeDmzZuF2I9RW04QyHHPIIlALnBvI
+YeF1veASz1gEFGUjzmbUGqKYSbCoTzXoev+F4bmbRxcX9sOmtslqvhMSHRSzA5NH
+aDVI3Hn4wvBVD8gePu8ACWqvPGbCiql11OKCMfjlPn2uuwJAx/24/F5DjXZ6hQQ7
+HxScOxKrpx5WnA9r1wZTltOTZkhRRzuLc21WJeE3M15QUdWi3zZxCKRFoth65HEs
+jy9YHQJAnPueRI44tz79b5QqVbeaOMUr7ZCb1Kp0uo6G+ANPLdlfliAupwij2eIz
+mHRJOWk0jBtXfRft1McH2H51CpXAyw==
+-----END RSA PRIVATE KEY-----
+`)
diff --git a/src/pkg/http/persist.go b/src/pkg/http/persist.go
index 53efd7c8c..b93c5fe48 100644
--- a/src/pkg/http/persist.go
+++ b/src/pkg/http/persist.go
@@ -211,7 +211,9 @@ type ClientConn struct {
nread, nwritten int
pipereq map[*Request]uint
- pipe textproto.Pipeline
+ pipe textproto.Pipeline
+ writeReq func(*Request, io.Writer) os.Error
+ readRes func(buf *bufio.Reader, method string) (*Response, os.Error)
}
// NewClientConn returns a new ClientConn reading and writing c. If r is not
@@ -220,7 +222,21 @@ func NewClientConn(c net.Conn, r *bufio.Reader) *ClientConn {
if r == nil {
r = bufio.NewReader(c)
}
- return &ClientConn{c: c, r: r, pipereq: make(map[*Request]uint)}
+ return &ClientConn{
+ c: c,
+ r: r,
+ pipereq: make(map[*Request]uint),
+ writeReq: (*Request).Write,
+ readRes: ReadResponse,
+ }
+}
+
+// NewProxyClientConn works like NewClientConn but writes Requests
+// using Request's WriteProxy method.
+func NewProxyClientConn(c net.Conn, r *bufio.Reader) *ClientConn {
+ cc := NewClientConn(c, r)
+ cc.writeReq = (*Request).WriteProxy
+ return cc
}
// Close detaches the ClientConn and returns the underlying connection as well
@@ -281,7 +297,7 @@ func (cc *ClientConn) Write(req *Request) (err os.Error) {
}
cc.lk.Unlock()
- err = req.Write(c)
+ err = cc.writeReq(req, c)
cc.lk.Lock()
defer cc.lk.Unlock()
if err != nil {
@@ -349,7 +365,7 @@ func (cc *ClientConn) Read(req *Request) (resp *Response, err os.Error) {
}
}
- resp, err = ReadResponse(r, req.Method)
+ resp, err = cc.readRes(r, req.Method)
cc.lk.Lock()
defer cc.lk.Unlock()
if err != nil {
diff --git a/src/pkg/http/pprof/pprof.go b/src/pkg/http/pprof/pprof.go
index f7db9aab9..bc79e2183 100644
--- a/src/pkg/http/pprof/pprof.go
+++ b/src/pkg/http/pprof/pprof.go
@@ -18,6 +18,10 @@
//
// pprof http://localhost:6060/debug/pprof/heap
//
+// Or to look at a 30-second CPU profile:
+//
+// pprof http://localhost:6060/debug/pprof/profile
+//
package pprof
import (
@@ -29,10 +33,12 @@ import (
"runtime/pprof"
"strconv"
"strings"
+ "time"
)
func init() {
http.Handle("/debug/pprof/cmdline", http.HandlerFunc(Cmdline))
+ http.Handle("/debug/pprof/profile", http.HandlerFunc(Profile))
http.Handle("/debug/pprof/heap", http.HandlerFunc(Heap))
http.Handle("/debug/pprof/symbol", http.HandlerFunc(Symbol))
}
@@ -41,22 +47,46 @@ func init() {
// command line, with arguments separated by NUL bytes.
// The package initialization registers it as /debug/pprof/cmdline.
func Cmdline(w http.ResponseWriter, r *http.Request) {
- w.SetHeader("content-type", "text/plain; charset=utf-8")
+ w.Header().Set("Content-Type", "text/plain; charset=utf-8")
fmt.Fprintf(w, strings.Join(os.Args, "\x00"))
}
// Heap responds with the pprof-formatted heap profile.
// The package initialization registers it as /debug/pprof/heap.
func Heap(w http.ResponseWriter, r *http.Request) {
- w.SetHeader("content-type", "text/plain; charset=utf-8")
+ w.Header().Set("Content-Type", "text/plain; charset=utf-8")
pprof.WriteHeapProfile(w)
}
+// Profile responds with the pprof-formatted cpu profile.
+// The package initialization registers it as /debug/pprof/profile.
+func Profile(w http.ResponseWriter, r *http.Request) {
+ sec, _ := strconv.Atoi64(r.FormValue("seconds"))
+ if sec == 0 {
+ sec = 30
+ }
+
+ // Set Content Type assuming StartCPUProfile will work,
+ // because if it does it starts writing.
+ w.Header().Set("Content-Type", "application/octet-stream")
+ if err := pprof.StartCPUProfile(w); err != nil {
+ // StartCPUProfile failed, so no writes yet.
+ // Can change header back to text content
+ // and send error code.
+ w.Header().Set("Content-Type", "text/plain; charset=utf-8")
+ w.WriteHeader(http.StatusInternalServerError)
+ fmt.Fprintf(w, "Could not enable CPU profiling: %s\n", err)
+ return
+ }
+ time.Sleep(sec * 1e9)
+ pprof.StopCPUProfile()
+}
+
// Symbol looks up the program counters listed in the request,
// responding with a table mapping program counters to function names.
// The package initialization registers it as /debug/pprof/symbol.
func Symbol(w http.ResponseWriter, r *http.Request) {
- w.SetHeader("content-type", "text/plain; charset=utf-8")
+ w.Header().Set("Content-Type", "text/plain; charset=utf-8")
// We don't know how many symbols we have, but we
// do have symbol information. Pprof only cares whether
diff --git a/src/pkg/http/proxy_test.go b/src/pkg/http/proxy_test.go
index 0f2ca458f..7050ef5ed 100644
--- a/src/pkg/http/proxy_test.go
+++ b/src/pkg/http/proxy_test.go
@@ -12,31 +12,33 @@ import (
// TODO(mattn):
// test ProxyAuth
-var MatchNoProxyTests = []struct {
+var UseProxyTests = []struct {
host string
match bool
}{
- {"localhost", true}, // match completely
- {"barbaz.net", true}, // match as .barbaz.net
- {"foobar.com:443", true}, // have a port but match
- {"foofoobar.com", false}, // not match as a part of foobar.com
- {"baz.com", false}, // not match as a part of barbaz.com
- {"localhost.net", false}, // not match as suffix of address
- {"local.localhost", false}, // not match as prefix as address
- {"barbarbaz.net", false}, // not match because NO_PROXY have a '.'
- {"www.foobar.com", false}, // not match because NO_PROXY is not .foobar.com
+ {"localhost", false}, // match completely
+ {"barbaz.net", false}, // match as .barbaz.net
+ {"foobar.com:443", false}, // have a port but match
+ {"foofoobar.com", true}, // not match as a part of foobar.com
+ {"baz.com", true}, // not match as a part of barbaz.com
+ {"localhost.net", true}, // not match as suffix of address
+ {"local.localhost", true}, // not match as prefix as address
+ {"barbarbaz.net", true}, // not match because NO_PROXY have a '.'
+ {"www.foobar.com", true}, // not match because NO_PROXY is not .foobar.com
}
-func TestMatchNoProxy(t *testing.T) {
+func TestUseProxy(t *testing.T) {
oldenv := os.Getenv("NO_PROXY")
no_proxy := "foobar.com, .barbaz.net , localhost"
os.Setenv("NO_PROXY", no_proxy)
defer os.Setenv("NO_PROXY", oldenv)
- for _, test := range MatchNoProxyTests {
- if matchNoProxy(test.host) != test.match {
+ tr := &Transport{}
+
+ for _, test := range UseProxyTests {
+ if tr.useProxy(test.host) != test.match {
if test.match {
- t.Errorf("matchNoProxy(%v) = %v, want %v", test.host, !test.match, test.match)
+ t.Errorf("useProxy(%v) = %v, want %v", test.host, !test.match, test.match)
} else {
t.Errorf("not expected: '%s' shouldn't match as '%s'", test.host, no_proxy)
}
diff --git a/src/pkg/http/request.go b/src/pkg/http/request.go
index d8456bab3..d82894fab 100644
--- a/src/pkg/http/request.go
+++ b/src/pkg/http/request.go
@@ -11,6 +11,7 @@ package http
import (
"bufio"
+ "crypto/tls"
"container/vector"
"fmt"
"io"
@@ -137,6 +138,22 @@ type Request struct {
// response has multiple trailer lines with the same key, they will be
// concatenated, delimited by commas.
Trailer Header
+
+ // RemoteAddr allows HTTP servers and other software to record
+ // the network address that sent the request, usually for
+ // logging. This field is not filled in by ReadRequest and
+ // has no defined format. The HTTP server in this package
+ // sets RemoteAddr to an "IP:port" address before invoking a
+ // handler.
+ RemoteAddr string
+
+ // TLS allows HTTP servers and other software to record
+ // information about the TLS connection on which the request
+ // was received. This field is not filled in by ReadRequest.
+ // The HTTP server in this package sets the field for
+ // TLS-enabled connections before invoking a handler;
+ // otherwise it leaves the field nil.
+ TLS *tls.ConnectionState
}
// ProtoAtLeast returns whether the HTTP protocol used
diff --git a/src/pkg/http/request_test.go b/src/pkg/http/request_test.go
index ae1c4e982..19083adf6 100644
--- a/src/pkg/http/request_test.go
+++ b/src/pkg/http/request_test.go
@@ -2,10 +2,15 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package http
+package http_test
import (
"bytes"
+ "fmt"
+ . "http"
+ "http/httptest"
+ "io"
+ "os"
"reflect"
"regexp"
"strings"
@@ -141,17 +146,33 @@ func TestMultipartReader(t *testing.T) {
}
func TestRedirect(t *testing.T) {
- const (
- start = "http://google.com/"
- endRe = "^http://www\\.google\\.[a-z.]+/$"
- )
- var end = regexp.MustCompile(endRe)
- r, url, err := Get(start)
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ switch r.URL.Path {
+ case "/":
+ w.Header().Set("Location", "/foo/")
+ w.WriteHeader(StatusSeeOther)
+ case "/foo/":
+ fmt.Fprintf(w, "foo")
+ default:
+ w.WriteHeader(StatusBadRequest)
+ }
+ }))
+ defer ts.Close()
+
+ var end = regexp.MustCompile("/foo/$")
+ r, url, err := Get(ts.URL)
if err != nil {
t.Fatal(err)
}
r.Body.Close()
if r.StatusCode != 200 || !end.MatchString(url) {
- t.Fatalf("Get(%s) got status %d at %q, want 200 matching %q", start, r.StatusCode, url, endRe)
+ t.Fatalf("Get got status %d at %q, want 200 matching /foo/$", r.StatusCode, url)
}
}
+
+// TODO: stop copy/pasting this around. move to io/ioutil?
+type nopCloser struct {
+ io.Reader
+}
+
+func (nopCloser) Close() os.Error { return nil }
diff --git a/src/pkg/http/requestwrite_test.go b/src/pkg/http/requestwrite_test.go
index 03a766efd..726baa266 100644
--- a/src/pkg/http/requestwrite_test.go
+++ b/src/pkg/http/requestwrite_test.go
@@ -6,6 +6,7 @@ package http
import (
"bytes"
+ "io/ioutil"
"testing"
)
@@ -158,7 +159,7 @@ func TestRequestWrite(t *testing.T) {
for i := range reqWriteTests {
tt := &reqWriteTests[i]
if tt.Body != nil {
- tt.Req.Body = nopCloser{bytes.NewBuffer(tt.Body)}
+ tt.Req.Body = ioutil.NopCloser(bytes.NewBuffer(tt.Body))
}
var braw bytes.Buffer
err := tt.Req.Write(&braw)
@@ -173,7 +174,7 @@ func TestRequestWrite(t *testing.T) {
}
if tt.Body != nil {
- tt.Req.Body = nopCloser{bytes.NewBuffer(tt.Body)}
+ tt.Req.Body = ioutil.NopCloser(bytes.NewBuffer(tt.Body))
}
var praw bytes.Buffer
err = tt.Req.WriteProxy(&praw)
diff --git a/src/pkg/http/response.go b/src/pkg/http/response.go
index 3d77c5555..1f725ecdd 100644
--- a/src/pkg/http/response.go
+++ b/src/pkg/http/response.go
@@ -217,13 +217,19 @@ func (resp *Response) Write(w io.Writer) os.Error {
func writeSortedHeader(w io.Writer, h Header, exclude map[string]bool) os.Error {
keys := make([]string, 0, len(h))
for k := range h {
- if !exclude[k] {
+ if exclude == nil || !exclude[k] {
keys = append(keys, k)
}
}
sort.SortStrings(keys)
for _, k := range keys {
for _, v := range h[k] {
+ v = strings.Replace(v, "\n", " ", -1)
+ v = strings.Replace(v, "\r", " ", -1)
+ v = strings.TrimSpace(v)
+ if v == "" {
+ continue
+ }
if _, err := fmt.Fprintf(w, "%s: %s\r\n", k, v); err != nil {
return err
}
diff --git a/src/pkg/http/response_test.go b/src/pkg/http/response_test.go
index bf63ccb9e..314f05b36 100644
--- a/src/pkg/http/response_test.go
+++ b/src/pkg/http/response_test.go
@@ -164,6 +164,28 @@ var respTests = []respTest{
"Body here\n",
},
+ // Chunked response in response to a HEAD request (the "chunked" should
+ // be ignored, as HEAD responses never have bodies)
+ {
+ "HTTP/1.0 200 OK\r\n" +
+ "Transfer-Encoding: chunked\r\n" +
+ "\r\n",
+
+ Response{
+ Status: "200 OK",
+ StatusCode: 200,
+ Proto: "HTTP/1.0",
+ ProtoMajor: 1,
+ ProtoMinor: 0,
+ RequestMethod: "HEAD",
+ Header: Header{},
+ Close: true,
+ ContentLength: 0,
+ },
+
+ "",
+ },
+
// Status line without a Reason-Phrase, but trailing space.
// (permitted by RFC 2616)
{
@@ -229,8 +251,8 @@ func TestReadResponse(t *testing.T) {
}
func diff(t *testing.T, prefix string, have, want interface{}) {
- hv := reflect.NewValue(have).(*reflect.PtrValue).Elem().(*reflect.StructValue)
- wv := reflect.NewValue(want).(*reflect.PtrValue).Elem().(*reflect.StructValue)
+ hv := reflect.NewValue(have).Elem()
+ wv := reflect.NewValue(want).Elem()
if hv.Type() != wv.Type() {
t.Errorf("%s: type mismatch %v vs %v", prefix, hv.Type(), wv.Type())
}
@@ -238,7 +260,7 @@ func diff(t *testing.T, prefix string, have, want interface{}) {
hf := hv.Field(i).Interface()
wf := wv.Field(i).Interface()
if !reflect.DeepEqual(hf, wf) {
- t.Errorf("%s: %s = %v want %v", prefix, hv.Type().(*reflect.StructType).Field(i).Name, hf, wf)
+ t.Errorf("%s: %s = %v want %v", prefix, hv.Type().Field(i).Name, hf, wf)
}
}
}
diff --git a/src/pkg/http/responsewrite_test.go b/src/pkg/http/responsewrite_test.go
index 228ed5f7d..de0635da5 100644
--- a/src/pkg/http/responsewrite_test.go
+++ b/src/pkg/http/responsewrite_test.go
@@ -6,6 +6,7 @@ package http
import (
"bytes"
+ "io/ioutil"
"testing"
)
@@ -23,7 +24,7 @@ var respWriteTests = []respWriteTest{
ProtoMinor: 0,
RequestMethod: "GET",
Header: Header{},
- Body: nopCloser{bytes.NewBufferString("abcdef")},
+ Body: ioutil.NopCloser(bytes.NewBufferString("abcdef")),
ContentLength: 6,
},
@@ -39,7 +40,7 @@ var respWriteTests = []respWriteTest{
ProtoMinor: 0,
RequestMethod: "GET",
Header: Header{},
- Body: nopCloser{bytes.NewBufferString("abcdef")},
+ Body: ioutil.NopCloser(bytes.NewBufferString("abcdef")),
ContentLength: -1,
},
"HTTP/1.0 200 OK\r\n" +
@@ -54,7 +55,7 @@ var respWriteTests = []respWriteTest{
ProtoMinor: 1,
RequestMethod: "GET",
Header: Header{},
- Body: nopCloser{bytes.NewBufferString("abcdef")},
+ Body: ioutil.NopCloser(bytes.NewBufferString("abcdef")),
ContentLength: 6,
TransferEncoding: []string{"chunked"},
Close: true,
@@ -65,6 +66,29 @@ var respWriteTests = []respWriteTest{
"Transfer-Encoding: chunked\r\n\r\n" +
"6\r\nabcdef\r\n0\r\n\r\n",
},
+
+ // Header value with a newline character (Issue 914).
+ // Also tests removal of leading and trailing whitespace.
+ {
+ Response{
+ StatusCode: 204,
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ RequestMethod: "GET",
+ Header: Header{
+ "Foo": []string{" Bar\nBaz "},
+ },
+ Body: nil,
+ ContentLength: 0,
+ TransferEncoding: []string{"chunked"},
+ Close: true,
+ },
+
+ "HTTP/1.1 204 No Content\r\n" +
+ "Connection: close\r\n" +
+ "Foo: Bar Baz\r\n" +
+ "\r\n",
+ },
}
func TestResponseWrite(t *testing.T) {
@@ -78,7 +102,7 @@ func TestResponseWrite(t *testing.T) {
}
sraw := braw.String()
if sraw != tt.Raw {
- t.Errorf("Test %d, expecting:\n%s\nGot:\n%s\n", i, tt.Raw, sraw)
+ t.Errorf("Test %d, expecting:\n%q\nGot:\n%q\n", i, tt.Raw, sraw)
continue
}
}
diff --git a/src/pkg/http/serve_test.go b/src/pkg/http/serve_test.go
index 86d64bdbb..0142dead9 100644
--- a/src/pkg/http/serve_test.go
+++ b/src/pkg/http/serve_test.go
@@ -15,6 +15,7 @@ import (
"io/ioutil"
"os"
"net"
+ "reflect"
"strings"
"testing"
"time"
@@ -144,7 +145,7 @@ func TestConsumingBodyOnNextConn(t *testing.T) {
type stringHandler string
func (s stringHandler) ServeHTTP(w ResponseWriter, r *Request) {
- w.SetHeader("Result", string(s))
+ w.Header().Set("Result", string(s))
}
var handlers = []struct {
@@ -174,7 +175,7 @@ func TestHostHandlers(t *testing.T) {
ts := httptest.NewServer(nil)
defer ts.Close()
- conn, err := net.Dial("tcp", "", ts.Listener.Addr().String())
+ conn, err := net.Dial("tcp", ts.Listener.Addr().String())
if err != nil {
t.Fatal(err)
}
@@ -216,7 +217,7 @@ func TestMuxRedirectLeadingSlashes(t *testing.T) {
mux.ServeHTTP(resp, req)
- if loc, expected := resp.Header.Get("Location"), "/foo.txt"; loc != expected {
+ if loc, expected := resp.Header().Get("Location"), "/foo.txt"; loc != expected {
t.Errorf("Expected Location header set to %q; got %q", expected, loc)
return
}
@@ -229,7 +230,8 @@ func TestMuxRedirectLeadingSlashes(t *testing.T) {
}
func TestServerTimeouts(t *testing.T) {
- l, err := net.ListenTCP("tcp", &net.TCPAddr{Port: 0})
+ // TODO(bradfitz): convert this to use httptest.Server
+ l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("listen error: %v", err)
}
@@ -248,7 +250,9 @@ func TestServerTimeouts(t *testing.T) {
url := fmt.Sprintf("http://localhost:%d/", addr.Port)
// Hit the HTTP server successfully.
- r, _, err := Get(url)
+ tr := &Transport{DisableKeepAlives: true} // they interfere with this test
+ c := &Client{Transport: tr}
+ r, _, err := c.Get(url)
if err != nil {
t.Fatalf("http Get #1: %v", err)
}
@@ -261,7 +265,7 @@ func TestServerTimeouts(t *testing.T) {
// Slow client that should timeout.
t1 := time.Nanoseconds()
- conn, err := net.Dial("tcp", "", fmt.Sprintf("localhost:%d", addr.Port))
+ conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", addr.Port))
if err != nil {
t.Fatalf("Dial: %v", err)
}
@@ -294,8 +298,8 @@ func TestServerTimeouts(t *testing.T) {
// TestIdentityResponse verifies that a handler can unset
func TestIdentityResponse(t *testing.T) {
handler := HandlerFunc(func(rw ResponseWriter, req *Request) {
- rw.SetHeader("Content-Length", "3")
- rw.SetHeader("Transfer-Encoding", req.FormValue("te"))
+ rw.Header().Set("Content-Length", "3")
+ rw.Header().Set("Transfer-Encoding", req.FormValue("te"))
switch {
case req.FormValue("overwrite") == "1":
_, err := rw.Write([]byte("foo TOO LONG"))
@@ -303,7 +307,7 @@ func TestIdentityResponse(t *testing.T) {
t.Errorf("expected ErrContentLength; got %v", err)
}
case req.FormValue("underwrite") == "1":
- rw.SetHeader("Content-Length", "500")
+ rw.Header().Set("Content-Length", "500")
rw.Write([]byte("too short"))
default:
rw.Write([]byte("foo"))
@@ -333,6 +337,7 @@ func TestIdentityResponse(t *testing.T) {
t.Errorf("for %s expected len(res.TransferEncoding) of %d; got %d (%v)",
url, expected, tl, res.TransferEncoding)
}
+ res.Body.Close()
}
// Verify that ErrContentLength is returned
@@ -341,10 +346,9 @@ func TestIdentityResponse(t *testing.T) {
if err != nil {
t.Fatalf("error with Get of %s: %v", url, err)
}
-
// Verify that the connection is closed when the declared Content-Length
// is larger than what the handler wrote.
- conn, err := net.Dial("tcp", "", ts.Listener.Addr().String())
+ conn, err := net.Dial("tcp", ts.Listener.Addr().String())
if err != nil {
t.Fatalf("error dialing: %v", err)
}
@@ -365,3 +369,250 @@ func TestIdentityResponse(t *testing.T) {
expectedSuffix, string(got))
}
}
+
+// TestServeHTTP10Close verifies that HTTP/1.0 requests won't be kept alive.
+func TestServeHTTP10Close(t *testing.T) {
+ s := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ ServeFile(w, r, "testdata/file")
+ }))
+ defer s.Close()
+
+ conn, err := net.Dial("tcp", s.Listener.Addr().String())
+ if err != nil {
+ t.Fatal("dial error:", err)
+ }
+ defer conn.Close()
+
+ _, err = fmt.Fprint(conn, "GET / HTTP/1.0\r\n\r\n")
+ if err != nil {
+ t.Fatal("print error:", err)
+ }
+
+ r := bufio.NewReader(conn)
+ _, err = ReadResponse(r, "GET")
+ if err != nil {
+ t.Fatal("ReadResponse error:", err)
+ }
+
+ success := make(chan bool)
+ go func() {
+ select {
+ case <-time.After(5e9):
+ t.Fatal("body not closed after 5s")
+ case <-success:
+ }
+ }()
+
+ _, err = ioutil.ReadAll(r)
+ if err != nil {
+ t.Fatal("read error:", err)
+ }
+
+ success <- true
+}
+
+func TestSetsRemoteAddr(t *testing.T) {
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ fmt.Fprintf(w, "%s", r.RemoteAddr)
+ }))
+ defer ts.Close()
+
+ res, _, err := Get(ts.URL)
+ if err != nil {
+ t.Fatalf("Get error: %v", err)
+ }
+ body, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ t.Fatalf("ReadAll error: %v", err)
+ }
+ ip := string(body)
+ if !strings.HasPrefix(ip, "127.0.0.1:") && !strings.HasPrefix(ip, "[::1]:") {
+ t.Fatalf("Expected local addr; got %q", ip)
+ }
+}
+
+func TestChunkedResponseHeaders(t *testing.T) {
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ w.Header().Set("Content-Length", "intentional gibberish") // we check that this is deleted
+ fmt.Fprintf(w, "I am a chunked response.")
+ }))
+ defer ts.Close()
+
+ res, _, err := Get(ts.URL)
+ if err != nil {
+ t.Fatalf("Get error: %v", err)
+ }
+ if g, e := res.ContentLength, int64(-1); g != e {
+ t.Errorf("expected ContentLength of %d; got %d", e, g)
+ }
+ if g, e := res.TransferEncoding, []string{"chunked"}; !reflect.DeepEqual(g, e) {
+ t.Errorf("expected TransferEncoding of %v; got %v", e, g)
+ }
+ if _, haveCL := res.Header["Content-Length"]; haveCL {
+ t.Errorf("Unexpected Content-Length")
+ }
+}
+
+// Test304Responses verifies that 304s don't declare that they're
+// chunking in their response headers and aren't allowed to produce
+// output.
+func Test304Responses(t *testing.T) {
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ w.WriteHeader(StatusNotModified)
+ _, err := w.Write([]byte("illegal body"))
+ if err != ErrBodyNotAllowed {
+ t.Errorf("on Write, expected ErrBodyNotAllowed, got %v", err)
+ }
+ }))
+ defer ts.Close()
+ res, _, err := Get(ts.URL)
+ if err != nil {
+ t.Error(err)
+ }
+ if len(res.TransferEncoding) > 0 {
+ t.Errorf("expected no TransferEncoding; got %v", res.TransferEncoding)
+ }
+ body, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ t.Error(err)
+ }
+ if len(body) > 0 {
+ t.Errorf("got unexpected body %q", string(body))
+ }
+}
+
+// TestHeadResponses verifies that responses to HEAD requests don't
+// declare that they're chunking in their response headers and aren't
+// allowed to produce output.
+func TestHeadResponses(t *testing.T) {
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ _, err := w.Write([]byte("Ignored body"))
+ if err != ErrBodyNotAllowed {
+ t.Errorf("on Write, expected ErrBodyNotAllowed, got %v", err)
+ }
+ }))
+ defer ts.Close()
+ res, err := Head(ts.URL)
+ if err != nil {
+ t.Error(err)
+ }
+ if len(res.TransferEncoding) > 0 {
+ t.Errorf("expected no TransferEncoding; got %v", res.TransferEncoding)
+ }
+ body, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ t.Error(err)
+ }
+ if len(body) > 0 {
+ t.Errorf("got unexpected body %q", string(body))
+ }
+}
+
+func TestTLSServer(t *testing.T) {
+ ts := httptest.NewTLSServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ fmt.Fprintf(w, "tls=%v", r.TLS != nil)
+ }))
+ defer ts.Close()
+ if !strings.HasPrefix(ts.URL, "https://") {
+ t.Fatalf("expected test TLS server to start with https://, got %q", ts.URL)
+ }
+ res, _, err := Get(ts.URL)
+ if err != nil {
+ t.Error(err)
+ }
+ if res == nil {
+ t.Fatalf("got nil Response")
+ }
+ if res.Body == nil {
+ t.Fatalf("got nil Response.Body")
+ }
+ body, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ t.Error(err)
+ }
+ if e, g := "tls=true", string(body); e != g {
+ t.Errorf("expected body %q; got %q", e, g)
+ }
+}
+
+type serverExpectTest struct {
+ contentLength int // of request body
+ expectation string // e.g. "100-continue"
+ readBody bool // whether handler should read the body (if false, sends StatusUnauthorized)
+ expectedResponse string // expected substring in first line of http response
+}
+
+var serverExpectTests = []serverExpectTest{
+ // Normal 100-continues, case-insensitive.
+ {100, "100-continue", true, "100 Continue"},
+ {100, "100-cOntInUE", true, "100 Continue"},
+
+ // No 100-continue.
+ {100, "", true, "200 OK"},
+
+ // 100-continue but requesting client to deny us,
+ // so it never eads the body.
+ {100, "100-continue", false, "401 Unauthorized"},
+ // Likewise without 100-continue:
+ {100, "", false, "401 Unauthorized"},
+
+ // Non-standard expectations are failures
+ {0, "a-pony", false, "417 Expectation Failed"},
+
+ // Expect-100 requested but no body
+ {0, "100-continue", true, "400 Bad Request"},
+}
+
+// Tests that the server responds to the "Expect" request header
+// correctly.
+func TestServerExpect(t *testing.T) {
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ // Note using r.FormValue("readbody") because for POST
+ // requests that would read from r.Body, which we only
+ // conditionally want to do.
+ if strings.Contains(r.URL.RawPath, "readbody=true") {
+ ioutil.ReadAll(r.Body)
+ w.Write([]byte("Hi"))
+ } else {
+ w.WriteHeader(StatusUnauthorized)
+ }
+ }))
+ defer ts.Close()
+
+ runTest := func(test serverExpectTest) {
+ conn, err := net.Dial("tcp", ts.Listener.Addr().String())
+ if err != nil {
+ t.Fatalf("Dial: %v", err)
+ }
+ defer conn.Close()
+ sendf := func(format string, args ...interface{}) {
+ _, err := fmt.Fprintf(conn, format, args...)
+ if err != nil {
+ t.Fatalf("Error writing %q: %v", format, err)
+ }
+ }
+ go func() {
+ sendf("POST /?readbody=%v HTTP/1.1\r\n"+
+ "Connection: close\r\n"+
+ "Content-Length: %d\r\n"+
+ "Expect: %s\r\nHost: foo\r\n\r\n",
+ test.readBody, test.contentLength, test.expectation)
+ if test.contentLength > 0 && strings.ToLower(test.expectation) != "100-continue" {
+ body := strings.Repeat("A", test.contentLength)
+ sendf(body)
+ }
+ }()
+ bufr := bufio.NewReader(conn)
+ line, err := bufr.ReadString('\n')
+ if err != nil {
+ t.Fatalf("ReadString: %v", err)
+ }
+ if !strings.Contains(line, test.expectedResponse) {
+ t.Errorf("for test %#v got first line=%q", test, line)
+ }
+ }
+
+ for _, test := range serverExpectTests {
+ runTest(test)
+ }
+}
diff --git a/src/pkg/http/server.go b/src/pkg/http/server.go
index 5d623e696..3291de101 100644
--- a/src/pkg/http/server.go
+++ b/src/pkg/http/server.go
@@ -6,7 +6,6 @@
// TODO(rsc):
// logging
-// cgi support
// post support
package http
@@ -49,23 +48,10 @@ type Handler interface {
// A ResponseWriter interface is used by an HTTP handler to
// construct an HTTP response.
type ResponseWriter interface {
- // RemoteAddr returns the address of the client that sent the current request
- RemoteAddr() string
-
- // UsingTLS returns true if the client is connected using TLS
- UsingTLS() bool
-
- // SetHeader sets a header line in the eventual response.
- // For example, SetHeader("Content-Type", "text/html; charset=utf-8")
- // will result in the header line
- //
- // Content-Type: text/html; charset=utf-8
- //
- // being sent. UTF-8 encoded HTML is the default setting for
- // Content-Type in this library, so users need not make that
- // particular call. Calls to SetHeader after WriteHeader (or Write)
- // are ignored. An empty value removes the header if previously set.
- SetHeader(string, string)
+ // Header returns the header map that will be sent by WriteHeader.
+ // Changing the header after a call to WriteHeader (or Write) has
+ // no effect.
+ Header() Header
// Write writes the data to the connection as part of an HTTP reply.
// If WriteHeader has not yet been called, Write calls WriteHeader(http.StatusOK)
@@ -78,42 +64,52 @@ type ResponseWriter interface {
// Thus explicit calls to WriteHeader are mainly used to
// send error codes.
WriteHeader(int)
+}
+// The Flusher interface is implemented by ResponseWriters that allow
+// an HTTP handler to flush buffered data to the client.
+//
+// Note that even for ResponseWriters that support Flush,
+// if the client is connected through an HTTP proxy,
+// the buffered data may not reach the client until the response
+// completes.
+type Flusher interface {
// Flush sends any buffered data to the client.
Flush()
}
-// A Hijacker is an HTTP request which be taken over by an HTTP handler.
+// The Hijacker interface is implemented by ResponseWriters that allow
+// an HTTP handler to take over the connection.
type Hijacker interface {
// Hijack lets the caller take over the connection.
// After a call to Hijack(), the HTTP server library
// will not do anything else with the connection.
// It becomes the caller's responsibility to manage
// and close the connection.
- Hijack() (io.ReadWriteCloser, *bufio.ReadWriter, os.Error)
+ Hijack() (net.Conn, *bufio.ReadWriter, os.Error)
}
// A conn represents the server side of an HTTP connection.
type conn struct {
- remoteAddr string // network address of remote side
- handler Handler // request handler
- rwc io.ReadWriteCloser // i/o connection
- buf *bufio.ReadWriter // buffered rwc
- hijacked bool // connection has been hijacked by handler
- usingTLS bool // a flag indicating connection over TLS
+ remoteAddr string // network address of remote side
+ handler Handler // request handler
+ rwc net.Conn // i/o connection
+ buf *bufio.ReadWriter // buffered rwc
+ hijacked bool // connection has been hijacked by handler
+ tlsState *tls.ConnectionState // or nil when not using TLS
}
// A response represents the server side of an HTTP response.
type response struct {
conn *conn
- req *Request // request for this response
- chunking bool // using chunked transfer encoding for reply body
- wroteHeader bool // reply header has been written
- wroteContinue bool // 100 Continue response was written
- header map[string]string // reply header parameters
- written int64 // number of bytes written in body
- contentLength int64 // explicitly-declared Content-Length; or -1
- status int // status code passed to WriteHeader
+ req *Request // request for this response
+ chunking bool // using chunked transfer encoding for reply body
+ wroteHeader bool // reply header has been written
+ wroteContinue bool // 100 Continue response was written
+ header Header // reply header parameters
+ written int64 // number of bytes written in body
+ contentLength int64 // explicitly-declared Content-Length; or -1
+ status int // status code passed to WriteHeader
// close connection after this reply. set on request and
// updated after response from handler if there's a
@@ -128,10 +124,15 @@ func newConn(rwc net.Conn, handler Handler) (c *conn, err os.Error) {
c.remoteAddr = rwc.RemoteAddr().String()
c.handler = handler
c.rwc = rwc
- _, c.usingTLS = rwc.(*tls.Conn)
br := bufio.NewReader(rwc)
bw := bufio.NewWriter(rwc)
c.buf = bufio.NewReadWriter(br, bw)
+
+ if tlsConn, ok := rwc.(*tls.Conn); ok {
+ c.tlsState = new(tls.ConnectionState)
+ *c.tlsState = tlsConn.ConnectionState()
+ }
+
return c, nil
}
@@ -171,35 +172,21 @@ func (c *conn) readRequest() (w *response, err os.Error) {
return nil, err
}
+ req.RemoteAddr = c.remoteAddr
+ req.TLS = c.tlsState
+
w = new(response)
w.conn = c
w.req = req
- w.header = make(map[string]string)
+ w.header = make(Header)
w.contentLength = -1
-
- // Expect 100 Continue support
- if req.expectsContinue() && req.ProtoAtLeast(1, 1) {
- // Wrap the Body reader with one that replies on the connection
- req.Body = &expectContinueReader{readCloser: req.Body, resp: w}
- }
return w, nil
}
-// UsingTLS implements the ResponseWriter.UsingTLS
-func (w *response) UsingTLS() bool {
- return w.conn.usingTLS
-}
-
-// RemoteAddr implements the ResponseWriter.RemoteAddr method
-func (w *response) RemoteAddr() string { return w.conn.remoteAddr }
-
-// SetHeader implements the ResponseWriter.SetHeader method
-// An empty value removes the header from the map.
-func (w *response) SetHeader(hdr, val string) {
- w.header[CanonicalHeaderKey(hdr)] = val, val != ""
+func (w *response) Header() Header {
+ return w.header
}
-// WriteHeader implements the ResponseWriter.WriteHeader method
func (w *response) WriteHeader(code int) {
if w.conn.hijacked {
log.Print("http: response.WriteHeader on hijacked connection")
@@ -214,55 +201,55 @@ func (w *response) WriteHeader(code int) {
if code == StatusNotModified {
// Must not have body.
for _, header := range []string{"Content-Type", "Content-Length", "Transfer-Encoding"} {
- if w.header[header] != "" {
+ if w.header.Get(header) != "" {
// TODO: return an error if WriteHeader gets a return parameter
// or set a flag on w to make future Writes() write an error page?
// for now just log and drop the header.
log.Printf("http: StatusNotModified response with header %q defined", header)
- w.header[header] = "", false
+ w.header.Del(header)
}
}
} else {
// Default output is HTML encoded in UTF-8.
- if w.header["Content-Type"] == "" {
- w.SetHeader("Content-Type", "text/html; charset=utf-8")
+ if w.header.Get("Content-Type") == "" {
+ w.header.Set("Content-Type", "text/html; charset=utf-8")
}
}
- if w.header["Date"] == "" {
- w.SetHeader("Date", time.UTC().Format(TimeFormat))
+ if w.header.Get("Date") == "" {
+ w.Header().Set("Date", time.UTC().Format(TimeFormat))
}
// Check for a explicit (and valid) Content-Length header.
var hasCL bool
var contentLength int64
- if clenStr, ok := w.header["Content-Length"]; ok {
+ if clenStr := w.header.Get("Content-Length"); clenStr != "" {
var err os.Error
contentLength, err = strconv.Atoi64(clenStr)
if err == nil {
hasCL = true
} else {
log.Printf("http: invalid Content-Length of %q sent", clenStr)
- w.SetHeader("Content-Length", "")
+ w.header.Del("Content-Length")
}
}
- te, hasTE := w.header["Transfer-Encoding"]
+ te := w.header.Get("Transfer-Encoding")
+ hasTE := te != ""
if hasCL && hasTE && te != "identity" {
// TODO: return an error if WriteHeader gets a return parameter
// For now just ignore the Content-Length.
log.Printf("http: WriteHeader called with both Transfer-Encoding of %q and a Content-Length of %d",
te, contentLength)
- w.SetHeader("Content-Length", "")
+ w.header.Del("Content-Length")
hasCL = false
}
- if w.req.Method == "HEAD" {
+ if w.req.Method == "HEAD" || code == StatusNotModified {
// do nothing
} else if hasCL {
- w.chunking = false
w.contentLength = contentLength
- w.SetHeader("Transfer-Encoding", "")
+ w.header.Del("Transfer-Encoding")
} else if w.req.ProtoAtLeast(1, 1) {
// HTTP/1.1 or greater: use chunked transfer encoding
// to avoid closing the connection at EOF.
@@ -270,20 +257,19 @@ func (w *response) WriteHeader(code int) {
// might have set. Deal with that as need arises once we have a valid
// use case.
w.chunking = true
- w.SetHeader("Transfer-Encoding", "chunked")
+ w.header.Set("Transfer-Encoding", "chunked")
} else {
// HTTP version < 1.1: cannot do chunked transfer
// encoding and we don't know the Content-Length so
// signal EOF by closing connection.
w.closeAfterReply = true
- w.chunking = false // redundant
- w.SetHeader("Transfer-Encoding", "") // in case already set
+ w.header.Del("Transfer-Encoding") // in case already set
}
if w.req.wantsHttp10KeepAlive() && (w.req.Method == "HEAD" || hasCL) {
_, connectionHeaderSet := w.header["Connection"]
if !connectionHeaderSet {
- w.SetHeader("Connection", "keep-alive")
+ w.header.Set("Connection", "keep-alive")
}
} else if !w.req.ProtoAtLeast(1, 1) {
// Client did not ask to keep connection alive.
@@ -292,7 +278,7 @@ func (w *response) WriteHeader(code int) {
// Cannot use Content-Length with non-identity Transfer-Encoding.
if w.chunking {
- w.SetHeader("Content-Length", "")
+ w.header.Del("Content-Length")
}
if !w.req.ProtoAtLeast(1, 0) {
return
@@ -307,13 +293,10 @@ func (w *response) WriteHeader(code int) {
text = "status code " + codestring
}
io.WriteString(w.conn.buf, proto+" "+codestring+" "+text+"\r\n")
- for k, v := range w.header {
- io.WriteString(w.conn.buf, k+": "+v+"\r\n")
- }
+ writeSortedHeader(w.conn.buf, w.header, nil)
io.WriteString(w.conn.buf, "\r\n")
}
-// Write implements the ResponseWriter.Write method
func (w *response) Write(data []byte) (n int, err os.Error) {
if w.conn.hijacked {
log.Print("http: response.Write on hijacked connection")
@@ -388,7 +371,7 @@ func errorKludge(w *response) {
msg += " would ignore this error page if this text weren't here.\n"
// Is it text? ("Content-Type" is always in the map)
- baseType := strings.Split(w.header["Content-Type"], ";", 2)[0]
+ baseType := strings.Split(w.header.Get("Content-Type"), ";", 2)[0]
switch baseType {
case "text/html":
io.WriteString(w, "<!-- ")
@@ -408,8 +391,8 @@ func (w *response) finishRequest() {
// If this was an HTTP/1.0 request with keep-alive and we sent a Content-Length
// back, we can make this a keep-alive response ...
if w.req.wantsHttp10KeepAlive() {
- _, sentLength := w.header["Content-Length"]
- if sentLength && w.header["Connection"] == "keep-alive" {
+ sentLength := w.header.Get("Content-Length") != ""
+ if sentLength && w.header.Get("Connection") == "keep-alive" {
w.closeAfterReply = false
}
}
@@ -431,7 +414,6 @@ func (w *response) finishRequest() {
}
}
-// Flush implements the ResponseWriter.Flush method.
func (w *response) Flush() {
if !w.wroteHeader {
w.WriteHeader(StatusOK)
@@ -458,6 +440,38 @@ func (c *conn) serve() {
if err != nil {
break
}
+
+ // Expect 100 Continue support
+ req := w.req
+ if req.expectsContinue() {
+ if req.ProtoAtLeast(1, 1) {
+ // Wrap the Body reader with one that replies on the connection
+ req.Body = &expectContinueReader{readCloser: req.Body, resp: w}
+ }
+ if req.ContentLength == 0 {
+ w.Header().Set("Connection", "close")
+ w.WriteHeader(StatusBadRequest)
+ break
+ }
+ req.Header.Del("Expect")
+ } else if req.Header.Get("Expect") != "" {
+ // TODO(bradfitz): let ServeHTTP handlers handle
+ // requests with non-standard expectation[s]? Seems
+ // theoretical at best, and doesn't fit into the
+ // current ServeHTTP model anyway. We'd need to
+ // make the ResponseWriter an optional
+ // "ExpectReplier" interface or something.
+ //
+ // For now we'll just obey RFC 2616 14.20 which says
+ // "If a server receives a request containing an
+ // Expect field that includes an expectation-
+ // extension that it does not support, it MUST
+ // respond with a 417 (Expectation Failed) status."
+ w.Header().Set("Connection", "close")
+ w.WriteHeader(StatusExpectationFailed)
+ break
+ }
+
// HTTP cannot have multiple simultaneous active requests.[*]
// Until the server replies to this request, it can't read another,
// so we might as well run the handler in this goroutine.
@@ -475,8 +489,9 @@ func (c *conn) serve() {
c.close()
}
-// Hijack impements the ResponseWriter.Hijack method.
-func (w *response) Hijack() (rwc io.ReadWriteCloser, buf *bufio.ReadWriter, err os.Error) {
+// Hijack implements the Hijacker.Hijack method. Our response is both a ResponseWriter
+// and a Hijacker.
+func (w *response) Hijack() (rwc net.Conn, buf *bufio.ReadWriter, err os.Error) {
if w.conn.hijacked {
return nil, nil, ErrHijacked
}
@@ -503,7 +518,7 @@ func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
// Error replies to the request with the specified error message and HTTP code.
func Error(w ResponseWriter, error string, code int) {
- w.SetHeader("Content-Type", "text/plain; charset=utf-8")
+ w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.WriteHeader(code)
fmt.Fprintln(w, error)
}
@@ -556,7 +571,7 @@ func Redirect(w ResponseWriter, r *Request, url string, code int) {
}
}
- w.SetHeader("Location", url)
+ w.Header().Set("Location", url)
w.WriteHeader(code)
// RFC2616 recommends that a short note "SHOULD" be included in the
@@ -679,7 +694,7 @@ func (mux *ServeMux) match(path string) Handler {
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
// Clean path to canonical form and redirect.
if p := cleanPath(r.URL.Path); p != r.URL.Path {
- w.SetHeader("Location", p)
+ w.Header().Set("Location", p)
w.WriteHeader(StatusMovedPermanently)
return
}
@@ -832,7 +847,7 @@ func ListenAndServe(addr string, handler Handler) os.Error {
// )
//
// func handler(w http.ResponseWriter, req *http.Request) {
-// w.SetHeader("Content-Type", "text/plain")
+// w.Header().Set("Content-Type", "text/plain")
// w.Write([]byte("This is an example server.\n"))
// }
//
diff --git a/src/pkg/http/transfer.go b/src/pkg/http/transfer.go
index 996e28973..41614f144 100644
--- a/src/pkg/http/transfer.go
+++ b/src/pkg/http/transfer.go
@@ -215,7 +215,7 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err os.Error) {
}
// Transfer encoding, content length
- t.TransferEncoding, err = fixTransferEncoding(t.Header)
+ t.TransferEncoding, err = fixTransferEncoding(t.RequestMethod, t.Header)
if err != nil {
return err
}
@@ -289,13 +289,20 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err os.Error) {
func chunked(te []string) bool { return len(te) > 0 && te[0] == "chunked" }
// Sanitize transfer encoding
-func fixTransferEncoding(header Header) ([]string, os.Error) {
+func fixTransferEncoding(requestMethod string, header Header) ([]string, os.Error) {
raw, present := header["Transfer-Encoding"]
if !present {
return nil, nil
}
header["Transfer-Encoding"] = nil, false
+
+ // Head responses have no bodies, so the transfer encoding
+ // should be ignored.
+ if requestMethod == "HEAD" {
+ return nil, nil
+ }
+
encodings := strings.Split(raw[0], ",", -1)
te := make([]string, 0, len(encodings))
// TODO: Even though we only support "identity" and "chunked"
diff --git a/src/pkg/http/transport.go b/src/pkg/http/transport.go
index 78d316a55..7fa37af3b 100644
--- a/src/pkg/http/transport.go
+++ b/src/pkg/http/transport.go
@@ -6,9 +6,12 @@ package http
import (
"bufio"
+ "compress/gzip"
"crypto/tls"
"encoding/base64"
"fmt"
+ "io"
+ "log"
"net"
"os"
"strings"
@@ -20,46 +23,109 @@ import (
// each call to Do and uses HTTP proxies as directed by the
// $HTTP_PROXY and $NO_PROXY (or $http_proxy and $no_proxy)
// environment variables.
-var DefaultTransport Transport = &transport{}
-
-// transport implements Tranport for the default case, using TCP
-// connections to either the host or a proxy, serving http or https
-// schemes. In the future this may become public and support options
-// on keep-alive connection duration, pipelining controls, etc. For
-// now this is simply a port of the old Go code client code to the
-// Transport interface.
-type transport struct {
- // TODO: keep-alives, pipelining, etc using a map from
- // scheme/host to a connection. Something like:
- l sync.Mutex
- hostConn map[string]*ClientConn
-}
-
-func (ct *transport) Do(req *Request) (resp *Response, err os.Error) {
+var DefaultTransport RoundTripper = &Transport{}
+
+// DefaultMaxIdleConnsPerHost is the default value of Transport's
+// MaxIdleConnsPerHost.
+const DefaultMaxIdleConnsPerHost = 2
+
+// Transport is an implementation of RoundTripper that supports http,
+// https, and http proxies (for either http or https with CONNECT).
+// Transport can also cache connections for future re-use.
+type Transport struct {
+ lk sync.Mutex
+ idleConn map[string][]*persistConn
+
+ // TODO: tunable on global max cached connections
+ // TODO: tunable on timeout on cached connections
+ // TODO: optional pipelining
+
+ IgnoreEnvironment bool // don't look at environment variables for proxy configuration
+ DisableKeepAlives bool
+ DisableCompression bool
+
+ // MaxIdleConnsPerHost, if non-zero, controls the maximum idle
+ // (keep-alive) to keep to keep per-host. If zero,
+ // DefaultMaxIdleConnsPerHost is used.
+ MaxIdleConnsPerHost int
+}
+
+// RoundTrip implements the RoundTripper interface.
+func (t *Transport) RoundTrip(req *Request) (resp *Response, err os.Error) {
+ if req.URL == nil {
+ if req.URL, err = ParseURL(req.RawURL); err != nil {
+ return
+ }
+ }
if req.URL.Scheme != "http" && req.URL.Scheme != "https" {
return nil, &badStringError{"unsupported protocol scheme", req.URL.Scheme}
}
- addr := req.URL.Host
- if !hasPort(addr) {
- addr += ":" + req.URL.Scheme
+ cm, err := t.connectMethodForRequest(req)
+ if err != nil {
+ return nil, err
+ }
+
+ // Get the cached or newly-created connection to either the
+ // host (for http or https), the http proxy, or the http proxy
+ // pre-CONNECTed to https server. In any case, we'll be ready
+ // to send it requests.
+ pconn, err := t.getConn(cm)
+ if err != nil {
+ return nil, err
}
- var proxyURL *URL
- proxyAuth := ""
- proxy := ""
- if !matchNoProxy(addr) {
- proxy = os.Getenv("HTTP_PROXY")
- if proxy == "" {
- proxy = os.Getenv("http_proxy")
+ return pconn.roundTrip(req)
+}
+
+// CloseIdleConnections closes any connections which were previously
+// connected from previous requests but are now sitting idle in
+// a "keep-alive" state. It does not interrupt any connections currently
+// in use.
+func (t *Transport) CloseIdleConnections() {
+ t.lk.Lock()
+ defer t.lk.Unlock()
+ if t.idleConn == nil {
+ return
+ }
+ for _, conns := range t.idleConn {
+ for _, pconn := range conns {
+ pconn.close()
}
}
+ t.idleConn = nil
+}
- var write = (*Request).Write
+//
+// Private implementation past this point.
+//
- if proxy != "" {
- write = (*Request).WriteProxy
- proxyURL, err = ParseRequestURL(proxy)
+func (t *Transport) getenvEitherCase(k string) string {
+ if t.IgnoreEnvironment {
+ return ""
+ }
+ if v := t.getenv(strings.ToUpper(k)); v != "" {
+ return v
+ }
+ return t.getenv(strings.ToLower(k))
+}
+
+func (t *Transport) getenv(k string) string {
+ if t.IgnoreEnvironment {
+ return ""
+ }
+ return os.Getenv(k)
+}
+
+func (t *Transport) connectMethodForRequest(req *Request) (*connectMethod, os.Error) {
+ cm := &connectMethod{
+ targetScheme: req.URL.Scheme,
+ targetAddr: canonicalAddr(req.URL),
+ }
+
+ proxy := t.getenvEitherCase("HTTP_PROXY")
+ if proxy != "" && t.useProxy(cm.targetAddr) {
+ proxyURL, err := ParseRequestURL(proxy)
if err != nil {
return nil, os.ErrorString("invalid proxy address")
}
@@ -69,83 +135,452 @@ func (ct *transport) Do(req *Request) (resp *Response, err os.Error) {
return nil, os.ErrorString("invalid proxy address")
}
}
- addr = proxyURL.Host
- proxyInfo := proxyURL.RawUserinfo
- if proxyInfo != "" {
- enc := base64.URLEncoding
- encoded := make([]byte, enc.EncodedLen(len(proxyInfo)))
- enc.Encode(encoded, []byte(proxyInfo))
- proxyAuth = "Basic " + string(encoded)
+ cm.proxyURL = proxyURL
+ }
+ return cm, nil
+}
+
+// proxyAuth returns the Proxy-Authorization header to set
+// on requests, if applicable.
+func (cm *connectMethod) proxyAuth() string {
+ if cm.proxyURL == nil {
+ return ""
+ }
+ proxyInfo := cm.proxyURL.RawUserinfo
+ if proxyInfo != "" {
+ enc := base64.URLEncoding
+ encoded := make([]byte, enc.EncodedLen(len(proxyInfo)))
+ enc.Encode(encoded, []byte(proxyInfo))
+ return "Basic " + string(encoded)
+ }
+ return ""
+}
+
+func (t *Transport) putIdleConn(pconn *persistConn) {
+ t.lk.Lock()
+ defer t.lk.Unlock()
+ if t.DisableKeepAlives || t.MaxIdleConnsPerHost < 0 {
+ pconn.close()
+ return
+ }
+ if pconn.isBroken() {
+ return
+ }
+ key := pconn.cacheKey
+ max := t.MaxIdleConnsPerHost
+ if max == 0 {
+ max = DefaultMaxIdleConnsPerHost
+ }
+ if len(t.idleConn[key]) >= max {
+ pconn.close()
+ return
+ }
+ t.idleConn[key] = append(t.idleConn[key], pconn)
+}
+
+func (t *Transport) getIdleConn(cm *connectMethod) (pconn *persistConn) {
+ t.lk.Lock()
+ defer t.lk.Unlock()
+ if t.idleConn == nil {
+ t.idleConn = make(map[string][]*persistConn)
+ }
+ key := cm.String()
+ for {
+ pconns, ok := t.idleConn[key]
+ if !ok {
+ return nil
+ }
+ if len(pconns) == 1 {
+ pconn = pconns[0]
+ t.idleConn[key] = nil, false
+ } else {
+ // 2 or more cached connections; pop last
+ // TODO: queue?
+ pconn = pconns[len(pconns)-1]
+ t.idleConn[key] = pconns[0 : len(pconns)-1]
}
+ if !pconn.isBroken() {
+ return
+ }
+ }
+ return
+}
+
+// getConn dials and creates a new persistConn to the target as
+// specified in the connectMethod. This includes doing a proxy CONNECT
+// and/or setting up TLS. If this doesn't return an error, the persistConn
+// is ready to write requests to.
+func (t *Transport) getConn(cm *connectMethod) (*persistConn, os.Error) {
+ if pc := t.getIdleConn(cm); pc != nil {
+ return pc, nil
}
- // Connect to server or proxy
- conn, err := net.Dial("tcp", "", addr)
+ conn, err := net.Dial("tcp", cm.addr())
if err != nil {
return nil, err
}
- if req.URL.Scheme == "http" {
- // Include proxy http header if needed.
- if proxyAuth != "" {
- req.Header.Set("Proxy-Authorization", proxyAuth)
- }
- } else { // https
- if proxyURL != nil {
- // Ask proxy for direct connection to server.
- // addr defaults above to ":https" but we need to use numbers
- addr = req.URL.Host
- if !hasPort(addr) {
- addr += ":443"
- }
- fmt.Fprintf(conn, "CONNECT %s HTTP/1.1\r\n", addr)
- fmt.Fprintf(conn, "Host: %s\r\n", addr)
- if proxyAuth != "" {
- fmt.Fprintf(conn, "Proxy-Authorization: %s\r\n", proxyAuth)
- }
- fmt.Fprintf(conn, "\r\n")
+ pa := cm.proxyAuth()
- // Read response.
- // Okay to use and discard buffered reader here, because
- // TLS server will not speak until spoken to.
- br := bufio.NewReader(conn)
- resp, err := ReadResponse(br, "CONNECT")
- if err != nil {
- return nil, err
- }
- if resp.StatusCode != 200 {
- f := strings.Split(resp.Status, " ", 2)
- return nil, os.ErrorString(f[1])
+ pconn := &persistConn{
+ t: t,
+ cacheKey: cm.String(),
+ conn: conn,
+ reqch: make(chan requestAndChan, 50),
+ }
+ newClientConnFunc := NewClientConn
+
+ switch {
+ case cm.proxyURL == nil:
+ // Do nothing.
+ case cm.targetScheme == "http":
+ newClientConnFunc = NewProxyClientConn
+ if pa != "" {
+ pconn.mutateRequestFunc = func(req *Request) {
+ if req.Header == nil {
+ req.Header = make(Header)
+ }
+ req.Header.Set("Proxy-Authorization", pa)
}
}
+ case cm.targetScheme == "https":
+ fmt.Fprintf(conn, "CONNECT %s HTTP/1.1\r\n", cm.targetAddr)
+ fmt.Fprintf(conn, "Host: %s\r\n", cm.targetAddr)
+ if pa != "" {
+ fmt.Fprintf(conn, "Proxy-Authorization: %s\r\n", pa)
+ }
+ fmt.Fprintf(conn, "\r\n")
+ // Read response.
+ // Okay to use and discard buffered reader here, because
+ // TLS server will not speak until spoken to.
+ br := bufio.NewReader(conn)
+ resp, err := ReadResponse(br, "CONNECT")
+ if err != nil {
+ conn.Close()
+ return nil, err
+ }
+ if resp.StatusCode != 200 {
+ f := strings.Split(resp.Status, " ", 2)
+ conn.Close()
+ return nil, os.ErrorString(f[1])
+ }
+ }
+
+ if cm.targetScheme == "https" {
// Initiate TLS and check remote host name against certificate.
conn = tls.Client(conn, nil)
if err = conn.(*tls.Conn).Handshake(); err != nil {
return nil, err
}
- h := req.URL.Host
- if hasPort(h) {
- h = h[:strings.LastIndex(h, ":")]
- }
- if err = conn.(*tls.Conn).VerifyHostname(h); err != nil {
+ if err = conn.(*tls.Conn).VerifyHostname(cm.tlsHost()); err != nil {
return nil, err
}
+ pconn.conn = conn
}
- err = write(req, conn)
- if err != nil {
- conn.Close()
- return nil, err
+ pconn.br = bufio.NewReader(pconn.conn)
+ pconn.cc = newClientConnFunc(conn, pconn.br)
+ pconn.cc.readRes = readResponseWithEOFSignal
+ go pconn.readLoop()
+ return pconn, nil
+}
+
+// useProxy returns true if requests to addr should use a proxy,
+// according to the NO_PROXY or no_proxy environment variable.
+func (t *Transport) useProxy(addr string) bool {
+ if len(addr) == 0 {
+ return true
+ }
+ no_proxy := t.getenvEitherCase("NO_PROXY")
+ if no_proxy == "*" {
+ return false
}
- reader := bufio.NewReader(conn)
- resp, err = ReadResponse(reader, req.Method)
+ addr = strings.ToLower(strings.TrimSpace(addr))
+ if hasPort(addr) {
+ addr = addr[:strings.LastIndex(addr, ":")]
+ }
+
+ for _, p := range strings.Split(no_proxy, ",", -1) {
+ p = strings.ToLower(strings.TrimSpace(p))
+ if len(p) == 0 {
+ continue
+ }
+ if hasPort(p) {
+ p = p[:strings.LastIndex(p, ":")]
+ }
+ if addr == p || (p[0] == '.' && (strings.HasSuffix(addr, p) || addr == p[1:])) {
+ return false
+ }
+ }
+ return true
+}
+
+// connectMethod is the map key (in its String form) for keeping persistent
+// TCP connections alive for subsequent HTTP requests.
+//
+// A connect method may be of the following types:
+//
+// Cache key form Description
+// ----------------- -------------------------
+// ||http|foo.com http directly to server, no proxy
+// ||https|foo.com https directly to server, no proxy
+// http://proxy.com|https|foo.com http to proxy, then CONNECT to foo.com
+// http://proxy.com|http http to proxy, http to anywhere after that
+//
+// Note: no support to https to the proxy yet.
+//
+type connectMethod struct {
+ proxyURL *URL // "" for no proxy, else full proxy URL
+ targetScheme string // "http" or "https"
+ targetAddr string // Not used if proxy + http targetScheme (4th example in table)
+}
+
+func (ck *connectMethod) String() string {
+ proxyStr := ""
+ if ck.proxyURL != nil {
+ proxyStr = ck.proxyURL.String()
+ }
+ return strings.Join([]string{proxyStr, ck.targetScheme, ck.targetAddr}, "|")
+}
+
+// addr returns the first hop "host:port" to which we need to TCP connect.
+func (cm *connectMethod) addr() string {
+ if cm.proxyURL != nil {
+ return canonicalAddr(cm.proxyURL)
+ }
+ return cm.targetAddr
+}
+
+// tlsHost returns the host name to match against the peer's
+// TLS certificate.
+func (cm *connectMethod) tlsHost() string {
+ h := cm.targetAddr
+ if hasPort(h) {
+ h = h[:strings.LastIndex(h, ":")]
+ }
+ return h
+}
+
+type readResult struct {
+ res *Response // either res or err will be set
+ err os.Error
+}
+
+type writeRequest struct {
+ // Set by client (in pc.roundTrip)
+ req *Request
+ resch chan *readResult
+
+ // Set by writeLoop if an error writing headers.
+ writeErr os.Error
+}
+
+// persistConn wraps a connection, usually a persistent one
+// (but may be used for non-keep-alive requests as well)
+type persistConn struct {
+ t *Transport
+ cacheKey string // its connectMethod.String()
+ conn net.Conn
+ cc *ClientConn
+ br *bufio.Reader
+ reqch chan requestAndChan // written by roundTrip(); read by readLoop()
+ mutateRequestFunc func(*Request) // nil or func to modify each outbound request
+
+ lk sync.Mutex // guards numExpectedResponses and broken
+ numExpectedResponses int
+ broken bool // an error has happened on this connection; marked broken so it's not reused.
+}
+
+func (pc *persistConn) isBroken() bool {
+ pc.lk.Lock()
+ defer pc.lk.Unlock()
+ return pc.broken
+}
+
+func (pc *persistConn) expectingResponse() bool {
+ pc.lk.Lock()
+ defer pc.lk.Unlock()
+ return pc.numExpectedResponses > 0
+}
+
+func (pc *persistConn) readLoop() {
+ alive := true
+ for alive {
+ pb, err := pc.br.Peek(1)
+ if err != nil {
+ if (err == os.EOF || err == os.EINVAL) && !pc.expectingResponse() {
+ // Remote side closed on us. (We probably hit their
+ // max idle timeout)
+ pc.close()
+ return
+ }
+ }
+ if !pc.expectingResponse() {
+ log.Printf("Unsolicited response received on idle HTTP channel starting with %q; err=%v",
+ string(pb), err)
+ pc.close()
+ return
+ }
+
+ rc := <-pc.reqch
+ resp, err := pc.cc.Read(rc.req)
+
+ if err == ErrPersistEOF {
+ // Succeeded, but we can't send any more
+ // persistent connections on this again. We
+ // hide this error to upstream callers.
+ alive = false
+ err = nil
+ } else if err != nil || rc.req.Close {
+ alive = false
+ }
+
+ hasBody := resp != nil && resp.ContentLength != 0
+ var waitForBodyRead chan bool
+ if alive {
+ if hasBody {
+ waitForBodyRead = make(chan bool)
+ resp.Body.(*bodyEOFSignal).fn = func() {
+ pc.t.putIdleConn(pc)
+ waitForBodyRead <- true
+ }
+ } else {
+ pc.t.putIdleConn(pc)
+ }
+ }
+
+ rc.ch <- responseAndError{resp, err}
+
+ // Wait for the just-returned response body to be fully consumed
+ // before we race and peek on the underlying bufio reader.
+ if waitForBodyRead != nil {
+ <-waitForBodyRead
+ }
+ }
+}
+
+type responseAndError struct {
+ res *Response
+ err os.Error
+}
+
+type requestAndChan struct {
+ req *Request
+ ch chan responseAndError
+}
+
+func (pc *persistConn) roundTrip(req *Request) (resp *Response, err os.Error) {
+ if pc.mutateRequestFunc != nil {
+ pc.mutateRequestFunc(req)
+ }
+
+ // Ask for a compressed version if the caller didn't set their
+ // own value for Accept-Encoding. We only attempted to
+ // uncompress the gzip stream if we were the layer that
+ // requested it.
+ requestedGzip := false
+ if !pc.t.DisableCompression && req.Header.Get("Accept-Encoding") == "" {
+ // Request gzip only, not deflate. Deflate is ambiguous and
+ // as universally supported anyway.
+ // See: http://www.gzip.org/zlib/zlib_faq.html#faq38
+ requestedGzip = true
+ req.Header.Set("Accept-Encoding", "gzip")
+ }
+
+ pc.lk.Lock()
+ pc.numExpectedResponses++
+ pc.lk.Unlock()
+
+ err = pc.cc.Write(req)
if err != nil {
- conn.Close()
- return nil, err
+ pc.close()
+ return
+ }
+
+ ch := make(chan responseAndError, 1)
+ pc.reqch <- requestAndChan{req, ch}
+ re := <-ch
+ pc.lk.Lock()
+ pc.numExpectedResponses--
+ pc.lk.Unlock()
+
+ if re.err == nil && requestedGzip && re.res.Header.Get("Content-Encoding") == "gzip" {
+ re.res.Header.Del("Content-Encoding")
+ re.res.Header.Del("Content-Length")
+ re.res.ContentLength = -1
+ var err os.Error
+ re.res.Body, err = gzip.NewReader(re.res.Body)
+ if err != nil {
+ pc.close()
+ return nil, err
+ }
+ }
+
+ return re.res, re.err
+}
+
+func (pc *persistConn) close() {
+ pc.lk.Lock()
+ defer pc.lk.Unlock()
+ pc.broken = true
+ pc.cc.Close()
+ pc.conn.Close()
+ pc.mutateRequestFunc = nil
+}
+
+var portMap = map[string]string{
+ "http": "80",
+ "https": "443",
+}
+
+// canonicalAddr returns url.Host but always with a ":port" suffix
+func canonicalAddr(url *URL) string {
+ addr := url.Host
+ if !hasPort(addr) {
+ return addr + ":" + portMap[url.Scheme]
}
+ return addr
+}
+
+func responseIsKeepAlive(res *Response) bool {
+ // TODO: implement. for now just always shutting down the connection.
+ return false
+}
- resp.Body = readClose{resp.Body, conn}
+// readResponseWithEOFSignal is a wrapper around ReadResponse that replaces
+// the response body with a bodyEOFSignal-wrapped version.
+func readResponseWithEOFSignal(r *bufio.Reader, requestMethod string) (resp *Response, err os.Error) {
+ resp, err = ReadResponse(r, requestMethod)
+ if err == nil && resp.ContentLength != 0 {
+ resp.Body = &bodyEOFSignal{resp.Body, nil}
+ }
+ return
+}
+
+// bodyEOFSignal wraps a ReadCloser but runs fn (if non-nil) at most
+// once, right before the final Read() or Close() call returns, but after
+// EOF has been seen.
+type bodyEOFSignal struct {
+ body io.ReadCloser
+ fn func()
+}
+
+func (es *bodyEOFSignal) Read(p []byte) (n int, err os.Error) {
+ n, err = es.body.Read(p)
+ if err == os.EOF && es.fn != nil {
+ es.fn()
+ es.fn = nil
+ }
+ return
+}
+
+func (es *bodyEOFSignal) Close() (err os.Error) {
+ err = es.body.Close()
+ if err == nil && es.fn != nil {
+ es.fn()
+ es.fn = nil
+ }
return
}
diff --git a/src/pkg/http/transport_test.go b/src/pkg/http/transport_test.go
new file mode 100644
index 000000000..f83deedfc
--- /dev/null
+++ b/src/pkg/http/transport_test.go
@@ -0,0 +1,450 @@
+// 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.
+
+// Tests for transport.go
+
+package http_test
+
+import (
+ "bytes"
+ "compress/gzip"
+ "fmt"
+ . "http"
+ "http/httptest"
+ "io/ioutil"
+ "os"
+ "testing"
+ "time"
+)
+
+// TODO: test 5 pipelined requests with responses: 1) OK, 2) OK, Connection: Close
+// and then verify that the final 2 responses get errors back.
+
+// hostPortHandler writes back the client's "host:port".
+var hostPortHandler = HandlerFunc(func(w ResponseWriter, r *Request) {
+ if r.FormValue("close") == "true" {
+ w.Header().Set("Connection", "close")
+ }
+ w.Write([]byte(r.RemoteAddr))
+})
+
+// Two subsequent requests and verify their response is the same.
+// The response from the server is our own IP:port
+func TestTransportKeepAlives(t *testing.T) {
+ ts := httptest.NewServer(hostPortHandler)
+ defer ts.Close()
+
+ for _, disableKeepAlive := range []bool{false, true} {
+ tr := &Transport{DisableKeepAlives: disableKeepAlive}
+ c := &Client{Transport: tr}
+
+ fetch := func(n int) string {
+ res, _, err := c.Get(ts.URL)
+ if err != nil {
+ t.Fatalf("error in disableKeepAlive=%v, req #%d, GET: %v", disableKeepAlive, n, err)
+ }
+ body, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ t.Fatalf("error in disableKeepAlive=%v, req #%d, ReadAll: %v", disableKeepAlive, n, err)
+ }
+ return string(body)
+ }
+
+ body1 := fetch(1)
+ body2 := fetch(2)
+
+ bodiesDiffer := body1 != body2
+ if bodiesDiffer != disableKeepAlive {
+ t.Errorf("error in disableKeepAlive=%v. unexpected bodiesDiffer=%v; body1=%q; body2=%q",
+ disableKeepAlive, bodiesDiffer, body1, body2)
+ }
+ }
+}
+
+func TestTransportConnectionCloseOnResponse(t *testing.T) {
+ ts := httptest.NewServer(hostPortHandler)
+ defer ts.Close()
+
+ for _, connectionClose := range []bool{false, true} {
+ tr := &Transport{}
+ c := &Client{Transport: tr}
+
+ fetch := func(n int) string {
+ req := new(Request)
+ var err os.Error
+ req.URL, err = ParseURL(ts.URL + fmt.Sprintf("?close=%v", connectionClose))
+ if err != nil {
+ t.Fatalf("URL parse error: %v", err)
+ }
+ req.Method = "GET"
+ req.Proto = "HTTP/1.1"
+ req.ProtoMajor = 1
+ req.ProtoMinor = 1
+
+ res, err := c.Do(req)
+ if err != nil {
+ t.Fatalf("error in connectionClose=%v, req #%d, Do: %v", connectionClose, n, err)
+ }
+ body, err := ioutil.ReadAll(res.Body)
+ defer res.Body.Close()
+ if err != nil {
+ t.Fatalf("error in connectionClose=%v, req #%d, ReadAll: %v", connectionClose, n, err)
+ }
+ return string(body)
+ }
+
+ body1 := fetch(1)
+ body2 := fetch(2)
+ bodiesDiffer := body1 != body2
+ if bodiesDiffer != connectionClose {
+ t.Errorf("error in connectionClose=%v. unexpected bodiesDiffer=%v; body1=%q; body2=%q",
+ connectionClose, bodiesDiffer, body1, body2)
+ }
+ }
+}
+
+func TestTransportConnectionCloseOnRequest(t *testing.T) {
+ ts := httptest.NewServer(hostPortHandler)
+ defer ts.Close()
+
+ for _, connectionClose := range []bool{false, true} {
+ tr := &Transport{}
+ c := &Client{Transport: tr}
+
+ fetch := func(n int) string {
+ req := new(Request)
+ var err os.Error
+ req.URL, err = ParseURL(ts.URL)
+ if err != nil {
+ t.Fatalf("URL parse error: %v", err)
+ }
+ req.Method = "GET"
+ req.Proto = "HTTP/1.1"
+ req.ProtoMajor = 1
+ req.ProtoMinor = 1
+ req.Close = connectionClose
+
+ res, err := c.Do(req)
+ if err != nil {
+ t.Fatalf("error in connectionClose=%v, req #%d, Do: %v", connectionClose, n, err)
+ }
+ body, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ t.Fatalf("error in connectionClose=%v, req #%d, ReadAll: %v", connectionClose, n, err)
+ }
+ return string(body)
+ }
+
+ body1 := fetch(1)
+ body2 := fetch(2)
+ bodiesDiffer := body1 != body2
+ if bodiesDiffer != connectionClose {
+ t.Errorf("error in connectionClose=%v. unexpected bodiesDiffer=%v; body1=%q; body2=%q",
+ connectionClose, bodiesDiffer, body1, body2)
+ }
+ }
+}
+
+func TestTransportIdleCacheKeys(t *testing.T) {
+ ts := httptest.NewServer(hostPortHandler)
+ defer ts.Close()
+
+ tr := &Transport{DisableKeepAlives: false}
+ c := &Client{Transport: tr}
+
+ if e, g := 0, len(tr.IdleConnKeysForTesting()); e != g {
+ t.Errorf("After CloseIdleConnections expected %d idle conn cache keys; got %d", e, g)
+ }
+
+ resp, _, err := c.Get(ts.URL)
+ if err != nil {
+ t.Error(err)
+ }
+ ioutil.ReadAll(resp.Body)
+
+ keys := tr.IdleConnKeysForTesting()
+ if e, g := 1, len(keys); e != g {
+ t.Fatalf("After Get expected %d idle conn cache keys; got %d", e, g)
+ }
+
+ if e := "|http|" + ts.Listener.Addr().String(); keys[0] != e {
+ t.Errorf("Expected idle cache key %q; got %q", e, keys[0])
+ }
+
+ tr.CloseIdleConnections()
+ if e, g := 0, len(tr.IdleConnKeysForTesting()); e != g {
+ t.Errorf("After CloseIdleConnections expected %d idle conn cache keys; got %d", e, g)
+ }
+}
+
+func TestTransportMaxPerHostIdleConns(t *testing.T) {
+ ch := make(chan string)
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ w.Write([]byte(<-ch))
+ }))
+ defer ts.Close()
+ maxIdleConns := 2
+ tr := &Transport{DisableKeepAlives: false, MaxIdleConnsPerHost: maxIdleConns}
+ c := &Client{Transport: tr}
+
+ // Start 3 outstanding requests (will hang until we write to
+ // ch)
+ donech := make(chan bool)
+ doReq := func() {
+ resp, _, err := c.Get(ts.URL)
+ if err != nil {
+ t.Error(err)
+ }
+ ioutil.ReadAll(resp.Body)
+ donech <- true
+ }
+ go doReq()
+ go doReq()
+ go doReq()
+
+ if e, g := 0, len(tr.IdleConnKeysForTesting()); e != g {
+ t.Fatalf("Before writes, expected %d idle conn cache keys; got %d", e, g)
+ }
+
+ ch <- "res1"
+ <-donech
+ keys := tr.IdleConnKeysForTesting()
+ if e, g := 1, len(keys); e != g {
+ t.Fatalf("after first response, expected %d idle conn cache keys; got %d", e, g)
+ }
+ cacheKey := "|http|" + ts.Listener.Addr().String()
+ if keys[0] != cacheKey {
+ t.Fatalf("Expected idle cache key %q; got %q", cacheKey, keys[0])
+ }
+ if e, g := 1, tr.IdleConnCountForTesting(cacheKey); e != g {
+ t.Errorf("after first response, expected %d idle conns; got %d", e, g)
+ }
+
+ ch <- "res2"
+ <-donech
+ if e, g := 2, tr.IdleConnCountForTesting(cacheKey); e != g {
+ t.Errorf("after second response, expected %d idle conns; got %d", e, g)
+ }
+
+ ch <- "res3"
+ <-donech
+ if e, g := maxIdleConns, tr.IdleConnCountForTesting(cacheKey); e != g {
+ t.Errorf("after third response, still expected %d idle conns; got %d", e, g)
+ }
+}
+
+func TestTransportServerClosingUnexpectedly(t *testing.T) {
+ ts := httptest.NewServer(hostPortHandler)
+ defer ts.Close()
+
+ tr := &Transport{}
+ c := &Client{Transport: tr}
+
+ fetch := func(n int) string {
+ res, _, err := c.Get(ts.URL)
+ if err != nil {
+ t.Fatalf("error in req #%d, GET: %v", n, err)
+ }
+ body, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ t.Fatalf("error in req #%d, ReadAll: %v", n, err)
+ }
+ res.Body.Close()
+ return string(body)
+ }
+
+ body1 := fetch(1)
+ body2 := fetch(2)
+
+ ts.CloseClientConnections() // surprise!
+ time.Sleep(25e6) // idle for a bit (test is inherently racey, but expectedly)
+
+ body3 := fetch(3)
+
+ if body1 != body2 {
+ t.Errorf("expected body1 and body2 to be equal")
+ }
+ if body2 == body3 {
+ t.Errorf("expected body2 and body3 to be different")
+ }
+}
+
+// TestTransportHeadResponses verifies that we deal with Content-Lengths
+// with no bodies properly
+func TestTransportHeadResponses(t *testing.T) {
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ if r.Method != "HEAD" {
+ panic("expected HEAD; got " + r.Method)
+ }
+ w.Header().Set("Content-Length", "123")
+ w.WriteHeader(200)
+ }))
+ defer ts.Close()
+
+ tr := &Transport{DisableKeepAlives: false}
+ c := &Client{Transport: tr}
+ for i := 0; i < 2; i++ {
+ res, err := c.Head(ts.URL)
+ if err != nil {
+ t.Errorf("error on loop %d: %v", i, err)
+ }
+ if e, g := "123", res.Header.Get("Content-Length"); e != g {
+ t.Errorf("loop %d: expected Content-Length header of %q, got %q", i, e, g)
+ }
+ if e, g := int64(0), res.ContentLength; e != g {
+ t.Errorf("loop %d: expected res.ContentLength of %v, got %v", i, e, g)
+ }
+ }
+}
+
+// TestTransportHeadChunkedResponse verifies that we ignore chunked transfer-encoding
+// on responses to HEAD requests.
+func TestTransportHeadChunkedResponse(t *testing.T) {
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ if r.Method != "HEAD" {
+ panic("expected HEAD; got " + r.Method)
+ }
+ w.Header().Set("Transfer-Encoding", "chunked") // client should ignore
+ w.Header().Set("x-client-ipport", r.RemoteAddr)
+ w.WriteHeader(200)
+ }))
+ defer ts.Close()
+
+ tr := &Transport{DisableKeepAlives: false}
+ c := &Client{Transport: tr}
+
+ res1, err := c.Head(ts.URL)
+ if err != nil {
+ t.Fatalf("request 1 error: %v", err)
+ }
+ res2, err := c.Head(ts.URL)
+ if err != nil {
+ t.Fatalf("request 2 error: %v", err)
+ }
+ if v1, v2 := res1.Header.Get("x-client-ipport"), res2.Header.Get("x-client-ipport"); v1 != v2 {
+ t.Errorf("ip/ports differed between head requests: %q vs %q", v1, v2)
+ }
+}
+
+func TestTransportNilURL(t *testing.T) {
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ fmt.Fprintf(w, "Hi")
+ }))
+ defer ts.Close()
+
+ req := new(Request)
+ req.URL = nil // what we're actually testing
+ req.Method = "GET"
+ req.RawURL = ts.URL
+ req.Proto = "HTTP/1.1"
+ req.ProtoMajor = 1
+ req.ProtoMinor = 1
+ req.Header = make(Header)
+
+ tr := &Transport{}
+ res, err := tr.RoundTrip(req)
+ if err != nil {
+ t.Fatalf("unexpected RoundTrip error: %v", err)
+ }
+ body, err := ioutil.ReadAll(res.Body)
+ if g, e := string(body), "Hi"; g != e {
+ t.Fatalf("Expected response body of %q; got %q", e, g)
+ }
+}
+
+func TestTransportGzip(t *testing.T) {
+ const testString = "The test string aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ if g, e := r.Header.Get("Accept-Encoding"), "gzip"; g != e {
+ t.Errorf("Accept-Encoding = %q, want %q", g, e)
+ }
+ w.Header().Set("Content-Encoding", "gzip")
+ gz, _ := gzip.NewWriter(w)
+ defer gz.Close()
+ gz.Write([]byte(testString))
+
+ }))
+ defer ts.Close()
+
+ c := &Client{Transport: &Transport{}}
+ res, _, err := c.Get(ts.URL)
+ if err != nil {
+ t.Fatal(err)
+ }
+ body, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if g, e := string(body), testString; g != e {
+ t.Fatalf("body = %q; want %q", g, e)
+ }
+ if g, e := res.Header.Get("Content-Encoding"), ""; g != e {
+ t.Fatalf("Content-Encoding = %q; want %q", g, e)
+ }
+}
+
+// TestTransportGzipRecursive sends a gzip quine and checks that the
+// client gets the same value back. This is more cute than anything,
+// but checks that we don't recurse forever, and checks that
+// Content-Encoding is removed.
+func TestTransportGzipRecursive(t *testing.T) {
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ w.Header().Set("Content-Encoding", "gzip")
+ w.Write(rgz)
+ }))
+ defer ts.Close()
+
+ c := &Client{Transport: &Transport{}}
+ res, _, err := c.Get(ts.URL)
+ if err != nil {
+ t.Fatal(err)
+ }
+ body, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.Equal(body, rgz) {
+ t.Fatalf("Incorrect result from recursive gz:\nhave=%x\nwant=%x",
+ body, rgz)
+ }
+ if g, e := res.Header.Get("Content-Encoding"), ""; g != e {
+ t.Fatalf("Content-Encoding = %q; want %q", g, e)
+ }
+}
+
+// rgz is a gzip quine that uncompresses to itself.
+var rgz = []byte{
+ 0x1f, 0x8b, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73,
+ 0x69, 0x76, 0x65, 0x00, 0x92, 0xef, 0xe6, 0xe0,
+ 0x60, 0x00, 0x83, 0xa2, 0xd4, 0xe4, 0xd2, 0xa2,
+ 0xe2, 0xcc, 0xb2, 0x54, 0x06, 0x00, 0x00, 0x17,
+ 0x00, 0xe8, 0xff, 0x92, 0xef, 0xe6, 0xe0, 0x60,
+ 0x00, 0x83, 0xa2, 0xd4, 0xe4, 0xd2, 0xa2, 0xe2,
+ 0xcc, 0xb2, 0x54, 0x06, 0x00, 0x00, 0x17, 0x00,
+ 0xe8, 0xff, 0x42, 0x12, 0x46, 0x16, 0x06, 0x00,
+ 0x05, 0x00, 0xfa, 0xff, 0x42, 0x12, 0x46, 0x16,
+ 0x06, 0x00, 0x05, 0x00, 0xfa, 0xff, 0x00, 0x05,
+ 0x00, 0xfa, 0xff, 0x00, 0x14, 0x00, 0xeb, 0xff,
+ 0x42, 0x12, 0x46, 0x16, 0x06, 0x00, 0x05, 0x00,
+ 0xfa, 0xff, 0x00, 0x05, 0x00, 0xfa, 0xff, 0x00,
+ 0x14, 0x00, 0xeb, 0xff, 0x42, 0x88, 0x21, 0xc4,
+ 0x00, 0x00, 0x14, 0x00, 0xeb, 0xff, 0x42, 0x88,
+ 0x21, 0xc4, 0x00, 0x00, 0x14, 0x00, 0xeb, 0xff,
+ 0x42, 0x88, 0x21, 0xc4, 0x00, 0x00, 0x14, 0x00,
+ 0xeb, 0xff, 0x42, 0x88, 0x21, 0xc4, 0x00, 0x00,
+ 0x14, 0x00, 0xeb, 0xff, 0x42, 0x88, 0x21, 0xc4,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00,
+ 0x00, 0xff, 0xff, 0x00, 0x17, 0x00, 0xe8, 0xff,
+ 0x42, 0x88, 0x21, 0xc4, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00,
+ 0x17, 0x00, 0xe8, 0xff, 0x42, 0x12, 0x46, 0x16,
+ 0x06, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x08,
+ 0x00, 0xf7, 0xff, 0x3d, 0xb1, 0x20, 0x85, 0xfa,
+ 0x00, 0x00, 0x00, 0x42, 0x12, 0x46, 0x16, 0x06,
+ 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x08, 0x00,
+ 0xf7, 0xff, 0x3d, 0xb1, 0x20, 0x85, 0xfa, 0x00,
+ 0x00, 0x00, 0x3d, 0xb1, 0x20, 0x85, 0xfa, 0x00,
+ 0x00, 0x00,
+}
diff --git a/src/pkg/http/triv.go b/src/pkg/http/triv.go
index 52d521d3d..bff6a106d 100644
--- a/src/pkg/http/triv.go
+++ b/src/pkg/http/triv.go
@@ -56,7 +56,7 @@ func (ctr *Counter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
var booleanflag = flag.Bool("boolean", true, "another flag for testing")
func FlagServer(w http.ResponseWriter, req *http.Request) {
- w.SetHeader("content-type", "text/plain; charset=utf-8")
+ w.Header().Set("Content-Type", "text/plain; charset=utf-8")
fmt.Fprint(w, "Flags:\n")
flag.VisitAll(func(f *flag.Flag) {
if f.Value.String() != f.DefValue {
@@ -93,13 +93,14 @@ func (ch Chan) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// exec a program, redirecting output
func DateServer(rw http.ResponseWriter, req *http.Request) {
- rw.SetHeader("content-type", "text/plain; charset=utf-8")
+ rw.Header().Set("Content-Type", "text/plain; charset=utf-8")
r, w, err := os.Pipe()
if err != nil {
fmt.Fprintf(rw, "pipe: %s\n", err)
return
}
- p, err := os.StartProcess("/bin/date", []string{"date"}, os.Environ(), "", []*os.File{nil, w, w})
+
+ p, err := os.StartProcess("/bin/date", []string{"date"}, &os.ProcAttr{Files: []*os.File{nil, w, w}})
defer r.Close()
w.Close()
if err != nil {
diff --git a/src/pkg/http/url.go b/src/pkg/http/url.go
index efd90d81e..0fc0cb2d7 100644
--- a/src/pkg/http/url.go
+++ b/src/pkg/http/url.go
@@ -213,8 +213,8 @@ func urlEscape(s string, mode encoding) string {
j++
case shouldEscape(c, mode):
t[j] = '%'
- t[j+1] = "0123456789abcdef"[c>>4]
- t[j+2] = "0123456789abcdef"[c&15]
+ t[j+1] = "0123456789ABCDEF"[c>>4]
+ t[j+2] = "0123456789ABCDEF"[c&15]
j += 3
default:
t[j] = s[i]
diff --git a/src/pkg/http/url_test.go b/src/pkg/http/url_test.go
index 0801f7ff3..d8863f3d3 100644
--- a/src/pkg/http/url_test.go
+++ b/src/pkg/http/url_test.go
@@ -490,7 +490,7 @@ var escapeTests = []URLEscapeTest{
},
{
" ?&=#+%!<>#\"{}|\\^[]`☺\t",
- "+%3f%26%3d%23%2b%25!%3c%3e%23%22%7b%7d%7c%5c%5e%5b%5d%60%e2%98%ba%09",
+ "+%3F%26%3D%23%2B%25!%3C%3E%23%22%7B%7D%7C%5C%5E%5B%5D%60%E2%98%BA%09",
nil,
},
}
@@ -519,7 +519,7 @@ type UserinfoTest struct {
var userinfoTests = []UserinfoTest{
{"user", "password", "user:password"},
{"foo:bar", "~!@#$%^&*()_+{}|[]\\-=`:;'\"<>?,./",
- "foo%3abar:~!%40%23$%25%5e&*()_+%7b%7d%7c%5b%5d%5c-=%60%3a;'%22%3c%3e?,.%2f"},
+ "foo%3Abar:~!%40%23$%25%5E&*()_+%7B%7D%7C%5B%5D%5C-=%60%3A;'%22%3C%3E?,.%2F"},
}
func TestEscapeUserinfo(t *testing.T) {
diff --git a/src/pkg/image/decode_test.go b/src/pkg/image/decode_test.go
index 5b87344c5..0716ad905 100644
--- a/src/pkg/image/decode_test.go
+++ b/src/pkg/image/decode_test.go
@@ -34,7 +34,7 @@ var imageTests = []imageTest{
}
func decode(filename string) (image.Image, string, os.Error) {
- f, err := os.Open(filename, os.O_RDONLY, 0400)
+ f, err := os.Open(filename)
if err != nil {
return nil, "", err
}
diff --git a/src/pkg/image/format.go b/src/pkg/image/format.go
index 1d541b094..b4859325e 100644
--- a/src/pkg/image/format.go
+++ b/src/pkg/image/format.go
@@ -25,7 +25,8 @@ var formats []format
// RegisterFormat registers an image format for use by Decode.
// Name is the name of the format, like "jpeg" or "png".
-// Magic is the magic prefix that identifies the format's encoding.
+// Magic is the magic prefix that identifies the format's encoding. The magic
+// string can contain "?" wildcards that each match any one byte.
// Decode is the function that decodes the encoded image.
// DecodeConfig is the function that decodes just its configuration.
func RegisterFormat(name, magic string, decode func(io.Reader) (Image, os.Error), decodeConfig func(io.Reader) (Config, os.Error)) {
@@ -46,11 +47,24 @@ func asReader(r io.Reader) reader {
return bufio.NewReader(r)
}
-// sniff determines the format of r's data.
+// Match returns whether magic matches b. Magic may contain "?" wildcards.
+func match(magic string, b []byte) bool {
+ if len(magic) != len(b) {
+ return false
+ }
+ for i, c := range b {
+ if magic[i] != c && magic[i] != '?' {
+ return false
+ }
+ }
+ return true
+}
+
+// Sniff determines the format of r's data.
func sniff(r reader) format {
for _, f := range formats {
- s, err := r.Peek(len(f.magic))
- if err == nil && string(s) == f.magic {
+ b, err := r.Peek(len(f.magic))
+ if err == nil && match(f.magic, b) {
return f
}
}
diff --git a/src/pkg/image/png/reader_test.go b/src/pkg/image/png/reader_test.go
index 8314a8338..efa6336d7 100644
--- a/src/pkg/image/png/reader_test.go
+++ b/src/pkg/image/png/reader_test.go
@@ -34,8 +34,14 @@ var filenames = []string{
"basn6a16",
}
+var filenamesShort = []string{
+ "basn0g01",
+ "basn0g04-31",
+ "basn6a16",
+}
+
func readPng(filename string) (image.Image, os.Error) {
- f, err := os.Open(filename, os.O_RDONLY, 0444)
+ f, err := os.Open(filename)
if err != nil {
return nil, err
}
@@ -157,7 +163,11 @@ func sng(w io.WriteCloser, filename string, png image.Image) {
}
func TestReader(t *testing.T) {
- for _, fn := range filenames {
+ names := filenames
+ if testing.Short() {
+ names = filenamesShort
+ }
+ for _, fn := range names {
// Read the .png file.
img, err := readPng("testdata/pngsuite/" + fn + ".png")
if err != nil {
@@ -181,7 +191,7 @@ func TestReader(t *testing.T) {
defer piper.Close()
// Read the .sng file.
- sf, err := os.Open("testdata/pngsuite/"+fn+".sng", os.O_RDONLY, 0444)
+ sf, err := os.Open("testdata/pngsuite/" + fn + ".sng")
if err != nil {
t.Error(fn, err)
continue
diff --git a/src/pkg/image/png/writer_test.go b/src/pkg/image/png/writer_test.go
index f218a5564..4d9929f31 100644
--- a/src/pkg/image/png/writer_test.go
+++ b/src/pkg/image/png/writer_test.go
@@ -32,7 +32,11 @@ func diff(m0, m1 image.Image) os.Error {
func TestWriter(t *testing.T) {
// The filenames variable is declared in reader_test.go.
- for _, fn := range filenames {
+ names := filenames
+ if testing.Short() {
+ names = filenamesShort
+ }
+ for _, fn := range names {
qfn := "testdata/pngsuite/" + fn + ".png"
// Read the image.
m0, err := readPng(qfn)
diff --git a/src/pkg/image/ycbcr/Makefile b/src/pkg/image/ycbcr/Makefile
new file mode 100644
index 000000000..a9c4c1367
--- /dev/null
+++ b/src/pkg/image/ycbcr/Makefile
@@ -0,0 +1,11 @@
+# Copyright 2011 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+include ../../../Make.inc
+
+TARG=image/ycbcr
+GOFILES=\
+ ycbcr.go\
+
+include ../../../Make.pkg
diff --git a/src/pkg/image/ycbcr/ycbcr.go b/src/pkg/image/ycbcr/ycbcr.go
new file mode 100644
index 000000000..b2e033b82
--- /dev/null
+++ b/src/pkg/image/ycbcr/ycbcr.go
@@ -0,0 +1,174 @@
+// 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.
+
+// The ycbcr package provides images from the Y'CbCr color model.
+//
+// JPEG, VP8, the MPEG family and other codecs use this color model. Such
+// codecs often use the terms YUV and Y'CbCr interchangeably, but strictly
+// speaking, the term YUV applies only to analog video signals.
+//
+// Conversion between RGB and Y'CbCr is lossy and there are multiple, slightly
+// different formulae for converting between the two. This package follows
+// the JFIF specification at http://www.w3.org/Graphics/JPEG/jfif3.pdf.
+package ycbcr
+
+import (
+ "image"
+)
+
+// RGBToYCbCr converts an RGB triple to a YCbCr triple. All components lie
+// within the range [0, 255].
+func RGBToYCbCr(r, g, b uint8) (uint8, uint8, uint8) {
+ // The JFIF specification says:
+ // Y' = 0.2990*R + 0.5870*G + 0.1140*B
+ // Cb = -0.1687*R - 0.3313*G + 0.5000*B + 128
+ // Cr = 0.5000*R - 0.4187*G - 0.0813*B + 128
+ // http://www.w3.org/Graphics/JPEG/jfif3.pdf says Y but means Y'.
+ r1 := int(r)
+ g1 := int(g)
+ b1 := int(b)
+ yy := (19595*r1 + 38470*g1 + 7471*b1 + 1<<15) >> 16
+ cb := (-11056*r1 - 21712*g1 + 32768*b1 + 257<<15) >> 16
+ cr := (32768*r1 - 27440*g1 - 5328*b1 + 257<<15) >> 16
+ if yy < 0 {
+ yy = 0
+ } else if yy > 255 {
+ yy = 255
+ }
+ if cb < 0 {
+ cb = 0
+ } else if cb > 255 {
+ cb = 255
+ }
+ if cr < 0 {
+ cr = 0
+ } else if cr > 255 {
+ cr = 255
+ }
+ return uint8(yy), uint8(cb), uint8(cr)
+}
+
+// YCbCrToRGB converts a YCbCr triple to an RGB triple. All components lie
+// within the range [0, 255].
+func YCbCrToRGB(y, cb, cr uint8) (uint8, uint8, uint8) {
+ // The JFIF specification says:
+ // R = Y' + 1.40200*(Cr-128)
+ // G = Y' - 0.34414*(Cb-128) - 0.71414*(Cr-128)
+ // B = Y' + 1.77200*(Cb-128)
+ // http://www.w3.org/Graphics/JPEG/jfif3.pdf says Y but means Y'.
+ yy1 := int(y)<<16 + 1<<15
+ cb1 := int(cb) - 128
+ cr1 := int(cr) - 128
+ r := (yy1 + 91881*cr1) >> 16
+ g := (yy1 - 22554*cb1 - 46802*cr1) >> 16
+ b := (yy1 + 116130*cb1) >> 16
+ if r < 0 {
+ r = 0
+ } else if r > 255 {
+ r = 255
+ }
+ if g < 0 {
+ g = 0
+ } else if g > 255 {
+ g = 255
+ }
+ if b < 0 {
+ b = 0
+ } else if b > 255 {
+ b = 255
+ }
+ return uint8(r), uint8(g), uint8(b)
+}
+
+// YCbCrColor represents a fully opaque 24-bit Y'CbCr color, having 8 bits for
+// each of one luma and two chroma components.
+type YCbCrColor struct {
+ Y, Cb, Cr uint8
+}
+
+func (c YCbCrColor) RGBA() (uint32, uint32, uint32, uint32) {
+ r, g, b := YCbCrToRGB(c.Y, c.Cb, c.Cr)
+ return uint32(r) * 0x101, uint32(g) * 0x101, uint32(b) * 0x101, 0xffff
+}
+
+func toYCbCrColor(c image.Color) image.Color {
+ if _, ok := c.(YCbCrColor); ok {
+ return c
+ }
+ r, g, b, _ := c.RGBA()
+ y, u, v := RGBToYCbCr(uint8(r>>8), uint8(g>>8), uint8(b>>8))
+ return YCbCrColor{y, u, v}
+}
+
+// YCbCrColorModel is the color model for YCbCrColor.
+var YCbCrColorModel image.ColorModel = image.ColorModelFunc(toYCbCrColor)
+
+// SubsampleRatio is the chroma subsample ratio used in a YCbCr image.
+type SubsampleRatio int
+
+const (
+ SubsampleRatio444 SubsampleRatio = iota
+ SubsampleRatio422
+ SubsampleRatio420
+)
+
+// YCbCr is an in-memory image of YCbCr colors. There is one Y sample per pixel,
+// but each Cb and Cr sample can span one or more pixels.
+// YStride is the Y slice index delta between vertically adjacent pixels.
+// CStride is the Cb and Cr slice index delta between vertically adjacent pixels
+// that map to separate chroma samples.
+// It is not an absolute requirement, but YStride and len(Y) are typically
+// multiples of 8, and:
+// For 4:4:4, CStride == YStride/1 && len(Cb) == len(Cr) == len(Y)/1.
+// For 4:2:2, CStride == YStride/2 && len(Cb) == len(Cr) == len(Y)/2.
+// For 4:2:0, CStride == YStride/2 && len(Cb) == len(Cr) == len(Y)/4.
+type YCbCr struct {
+ Y []uint8
+ Cb []uint8
+ Cr []uint8
+ YStride int
+ CStride int
+ SubsampleRatio SubsampleRatio
+ Rect image.Rectangle
+}
+
+func (p *YCbCr) ColorModel() image.ColorModel {
+ return YCbCrColorModel
+}
+
+func (p *YCbCr) Bounds() image.Rectangle {
+ return p.Rect
+}
+
+func (p *YCbCr) At(x, y int) image.Color {
+ if !p.Rect.Contains(image.Point{x, y}) {
+ return YCbCrColor{}
+ }
+ switch p.SubsampleRatio {
+ case SubsampleRatio422:
+ i := x / 2
+ return YCbCrColor{
+ p.Y[y*p.YStride+x],
+ p.Cb[y*p.CStride+i],
+ p.Cr[y*p.CStride+i],
+ }
+ case SubsampleRatio420:
+ i, j := x/2, y/2
+ return YCbCrColor{
+ p.Y[y*p.YStride+x],
+ p.Cb[j*p.CStride+i],
+ p.Cr[j*p.CStride+i],
+ }
+ }
+ // Default to 4:4:4 subsampling.
+ return YCbCrColor{
+ p.Y[y*p.YStride+x],
+ p.Cb[y*p.CStride+x],
+ p.Cr[y*p.CStride+x],
+ }
+}
+
+func (p *YCbCr) Opaque() bool {
+ return true
+}
diff --git a/src/pkg/image/ycbcr/ycbcr_test.go b/src/pkg/image/ycbcr/ycbcr_test.go
new file mode 100644
index 000000000..2e60a6f61
--- /dev/null
+++ b/src/pkg/image/ycbcr/ycbcr_test.go
@@ -0,0 +1,33 @@
+// 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 ycbcr
+
+import (
+ "testing"
+)
+
+func delta(x, y uint8) uint8 {
+ if x >= y {
+ return x - y
+ }
+ return y - x
+}
+
+// Test that a subset of RGB space can be converted to YCbCr and back to within
+// 1/256 tolerance.
+func TestRoundtrip(t *testing.T) {
+ for r := 0; r < 255; r += 7 {
+ for g := 0; g < 255; g += 5 {
+ for b := 0; b < 255; b += 3 {
+ r0, g0, b0 := uint8(r), uint8(g), uint8(b)
+ y, cb, cr := RGBToYCbCr(r0, g0, b0)
+ r1, g1, b1 := YCbCrToRGB(y, cb, cr)
+ if delta(r0, r1) > 1 || delta(g0, g1) > 1 || delta(b0, b1) > 1 {
+ t.Fatalf("r0, g0, b0 = %d, %d, %d r1, g1, b1 = %d, %d, %d", r0, g0, b0, r1, g1, b1)
+ }
+ }
+ }
+ }
+}
diff --git a/src/pkg/io/io.go b/src/pkg/io/io.go
index 3b8791897..d3707eb1d 100644
--- a/src/pkg/io/io.go
+++ b/src/pkg/io/io.go
@@ -136,6 +136,10 @@ type WriterTo interface {
// At the end of the input stream, ReadAt returns 0, os.EOF.
// ReadAt may return a non-zero number of bytes with a non-nil err.
// In particular, a ReadAt that exhausts the input may return n > 0, os.EOF.
+//
+// If ReadAt is reading from an data stream with a seek offset,
+// ReadAt should not affect nor be affected by the underlying
+// seek offset.
type ReaderAt interface {
ReadAt(p []byte, off int64) (n int, err os.Error)
}
@@ -182,16 +186,16 @@ func ReadAtLeast(r Reader, buf []byte, min int) (n int, err os.Error) {
if len(buf) < min {
return 0, ErrShortBuffer
}
- for n < min {
- nn, e := r.Read(buf[n:])
- if nn > 0 {
- n += nn
- }
- if e != nil {
- if e == os.EOF && n > 0 {
- e = ErrUnexpectedEOF
- }
- return n, e
+ for n < min && err == nil {
+ var nn int
+ nn, err = r.Read(buf[n:])
+ n += nn
+ }
+ if err == os.EOF {
+ if n >= min {
+ err = nil
+ } else if n > 0 {
+ err = ErrUnexpectedEOF
}
}
return
diff --git a/src/pkg/io/io_test.go b/src/pkg/io/io_test.go
index 4fcd85e69..bc4f354af 100644
--- a/src/pkg/io/io_test.go
+++ b/src/pkg/io/io_test.go
@@ -118,27 +118,50 @@ func TestCopynEOF(t *testing.T) {
func TestReadAtLeast(t *testing.T) {
var rb bytes.Buffer
+ testReadAtLeast(t, &rb)
+}
+
+// A version of bytes.Buffer that returns n > 0, os.EOF on Read
+// when the input is exhausted.
+type dataAndEOFBuffer struct {
+ bytes.Buffer
+}
+
+func (r *dataAndEOFBuffer) Read(p []byte) (n int, err os.Error) {
+ n, err = r.Buffer.Read(p)
+ if n > 0 && r.Buffer.Len() == 0 && err == nil {
+ err = os.EOF
+ }
+ return
+}
+
+func TestReadAtLeastWithDataAndEOF(t *testing.T) {
+ var rb dataAndEOFBuffer
+ testReadAtLeast(t, &rb)
+}
+
+func testReadAtLeast(t *testing.T, rb ReadWriter) {
rb.Write([]byte("0123"))
buf := make([]byte, 2)
- n, err := ReadAtLeast(&rb, buf, 2)
+ n, err := ReadAtLeast(rb, buf, 2)
if err != nil {
t.Error(err)
}
- n, err = ReadAtLeast(&rb, buf, 4)
+ n, err = ReadAtLeast(rb, buf, 4)
if err != ErrShortBuffer {
t.Errorf("expected ErrShortBuffer got %v", err)
}
if n != 0 {
t.Errorf("expected to have read 0 bytes, got %v", n)
}
- n, err = ReadAtLeast(&rb, buf, 1)
+ n, err = ReadAtLeast(rb, buf, 1)
if err != nil {
t.Error(err)
}
if n != 2 {
t.Errorf("expected to have read 2 bytes, got %v", n)
}
- n, err = ReadAtLeast(&rb, buf, 2)
+ n, err = ReadAtLeast(rb, buf, 2)
if err != os.EOF {
t.Errorf("expected EOF, got %v", err)
}
@@ -146,7 +169,7 @@ func TestReadAtLeast(t *testing.T) {
t.Errorf("expected to have read 0 bytes, got %v", n)
}
rb.Write([]byte("4"))
- n, err = ReadAtLeast(&rb, buf, 2)
+ n, err = ReadAtLeast(rb, buf, 2)
if err != ErrUnexpectedEOF {
t.Errorf("expected ErrUnexpectedEOF, got %v", err)
}
diff --git a/src/pkg/io/ioutil/ioutil.go b/src/pkg/io/ioutil/ioutil.go
index fb3fdcda1..57d797e85 100644
--- a/src/pkg/io/ioutil/ioutil.go
+++ b/src/pkg/io/ioutil/ioutil.go
@@ -13,16 +13,22 @@ import (
"sort"
)
+// readAll reads from r until an error or EOF and returns the data it read
+// from the internal buffer allocated with a specified capacity.
+func readAll(r io.Reader, capacity int64) ([]byte, os.Error) {
+ buf := bytes.NewBuffer(make([]byte, 0, capacity))
+ _, err := buf.ReadFrom(r)
+ return buf.Bytes(), err
+}
+
// ReadAll reads from r until an error or EOF and returns the data it read.
func ReadAll(r io.Reader) ([]byte, os.Error) {
- var buf bytes.Buffer
- _, err := io.Copy(&buf, r)
- return buf.Bytes(), err
+ return readAll(r, bytes.MinRead)
}
// ReadFile reads the file named by filename and returns the contents.
func ReadFile(filename string) ([]byte, os.Error) {
- f, err := os.Open(filename, os.O_RDONLY, 0)
+ f, err := os.Open(filename)
if err != nil {
return nil, err
}
@@ -34,23 +40,19 @@ func ReadFile(filename string) ([]byte, os.Error) {
if err == nil && fi.Size < 2e9 { // Don't preallocate a huge buffer, just in case.
n = fi.Size
}
- // Add a little extra in case Size is zero, and to avoid another allocation after
- // Read has filled the buffer.
- n += bytes.MinRead
- // Pre-allocate the correct size of buffer, then set its size to zero. The
- // Buffer will read into the allocated space cheaply. If the size was wrong,
- // we'll either waste some space off the end or reallocate as needed, but
+ // As initial capacity for readAll, use n + a little extra in case Size is zero,
+ // and to avoid another allocation after Read has filled the buffer. The readAll
+ // call will read into its allocated internal buffer cheaply. If the size was
+ // wrong, we'll either waste some space off the end or reallocate as needed, but
// in the overwhelmingly common case we'll get it just right.
- buf := bytes.NewBuffer(make([]byte, 0, n))
- _, err = buf.ReadFrom(f)
- return buf.Bytes(), err
+ return readAll(f, n+bytes.MinRead)
}
// WriteFile writes data to a file named by filename.
// If the file does not exist, WriteFile creates it with permissions perm;
// otherwise WriteFile truncates it before writing.
func WriteFile(filename string, data []byte, perm uint32) os.Error {
- f, err := os.Open(filename, os.O_WRONLY|os.O_CREAT|os.O_TRUNC, perm)
+ f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
if err != nil {
return err
}
@@ -72,7 +74,7 @@ func (f fileInfoList) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
// ReadDir reads the directory named by dirname and returns
// a list of sorted directory entries.
func ReadDir(dirname string) ([]*os.FileInfo, os.Error) {
- f, err := os.Open(dirname, os.O_RDONLY, 0)
+ f, err := os.Open(dirname)
if err != nil {
return nil, err
}
@@ -88,3 +90,15 @@ func ReadDir(dirname string) ([]*os.FileInfo, os.Error) {
sort.Sort(fi)
return fi, nil
}
+
+type nopCloser struct {
+ io.Reader
+}
+
+func (nopCloser) Close() os.Error { return nil }
+
+// NopCloser returns a ReadCloser with a no-op Close method wrapping
+// the provided Reader r.
+func NopCloser(r io.Reader) io.ReadCloser {
+ return nopCloser{r}
+}
diff --git a/src/pkg/io/ioutil/tempfile.go b/src/pkg/io/ioutil/tempfile.go
index 62f8849c0..8e681bdc3 100644
--- a/src/pkg/io/ioutil/tempfile.go
+++ b/src/pkg/io/ioutil/tempfile.go
@@ -48,7 +48,7 @@ func TempFile(dir, prefix string) (f *os.File, err os.Error) {
nconflict := 0
for i := 0; i < 10000; i++ {
name := filepath.Join(dir, prefix+nextSuffix())
- f, err = os.Open(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
+ f, err = os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
if pe, ok := err.(*os.PathError); ok && pe.Error == os.EEXIST {
if nconflict++; nconflict > 10 {
rand = reseed()
diff --git a/src/pkg/io/multi.go b/src/pkg/io/multi.go
index 88e4f1b76..d702d46c7 100644
--- a/src/pkg/io/multi.go
+++ b/src/pkg/io/multi.go
@@ -15,10 +15,8 @@ func (mr *multiReader) Read(p []byte) (n int, err os.Error) {
n, err = mr.readers[0].Read(p)
if n > 0 || err != os.EOF {
if err == os.EOF {
- // This shouldn't happen.
- // Well-behaved Readers should never
- // return non-zero bytes read with an
- // EOF. But if so, we clean it.
+ // Don't return EOF yet. There may be more bytes
+ // in the remaining readers.
err = nil
}
return
diff --git a/src/pkg/io/pipe.go b/src/pkg/io/pipe.go
index df76418b9..00be8efa2 100644
--- a/src/pkg/io/pipe.go
+++ b/src/pkg/io/pipe.go
@@ -9,7 +9,6 @@ package io
import (
"os"
- "runtime"
"sync"
)
@@ -18,208 +17,114 @@ type pipeResult struct {
err os.Error
}
-// Shared pipe structure.
+// A pipe is the shared pipe structure underlying PipeReader and PipeWriter.
type pipe struct {
- // Reader sends on cr1, receives on cr2.
- // Writer does the same on cw1, cw2.
- r1, w1 chan []byte
- r2, w2 chan pipeResult
-
- rclose chan os.Error // read close; error to return to writers
- wclose chan os.Error // write close; error to return to readers
-
- done chan int // read or write half is done
-}
-
-func (p *pipe) run() {
- var (
- rb []byte // pending Read
- wb []byte // pending Write
- wn int // amount written so far from wb
- rerr os.Error // if read end is closed, error to send to writers
- werr os.Error // if write end is closed, error to send to readers
- r1 chan []byte // p.cr1 or nil depending on whether Read is ok
- w1 chan []byte // p.cw1 or nil depending on whether Write is ok
- ndone int
- )
-
- // Read and Write are enabled at the start.
- r1 = p.r1
- w1 = p.w1
-
+ rl sync.Mutex // gates readers one at a time
+ wl sync.Mutex // gates writers one at a time
+ l sync.Mutex // protects remaining fields
+ data []byte // data remaining in pending write
+ rwait sync.Cond // waiting reader
+ wwait sync.Cond // waiting writer
+ rerr os.Error // if reader closed, error to give writes
+ werr os.Error // if writer closed, error to give reads
+}
+
+func (p *pipe) read(b []byte) (n int, err os.Error) {
+ // One reader at a time.
+ p.rl.Lock()
+ defer p.rl.Unlock()
+
+ p.l.Lock()
+ defer p.l.Unlock()
for {
- select {
- case <-p.done:
- if ndone++; ndone == 2 {
- // both reader and writer are gone
- // close out any existing i/o
- if r1 == nil {
- p.r2 <- pipeResult{0, os.EINVAL}
- }
- if w1 == nil {
- p.w2 <- pipeResult{0, os.EINVAL}
- }
- return
- }
- continue
- case rerr = <-p.rclose:
- if w1 == nil {
- // finish pending Write
- p.w2 <- pipeResult{wn, rerr}
- wn = 0
- w1 = p.w1 // allow another Write
- }
- if r1 == nil {
- // Close of read side during Read.
- // finish pending Read with os.EINVAL.
- p.r2 <- pipeResult{0, os.EINVAL}
- r1 = p.r1 // allow another Read
- }
- continue
- case werr = <-p.wclose:
- if r1 == nil {
- // finish pending Read
- p.r2 <- pipeResult{0, werr}
- r1 = p.r1 // allow another Read
- }
- if w1 == nil {
- // Close of write side during Write.
- // finish pending Write with os.EINVAL.
- p.w2 <- pipeResult{wn, os.EINVAL}
- wn = 0
- w1 = p.w1 // allow another Write
- }
- continue
- case rb = <-r1:
- if werr != nil {
- // write end is closed
- p.r2 <- pipeResult{0, werr}
- continue
- }
- if rerr != nil {
- // read end is closed
- p.r2 <- pipeResult{0, os.EINVAL}
- continue
- }
- r1 = nil // disable Read until this one is done
- case wb = <-w1:
- if rerr != nil {
- // read end is closed
- p.w2 <- pipeResult{0, rerr}
- continue
- }
- if werr != nil {
- // write end is closed
- p.w2 <- pipeResult{0, os.EINVAL}
- continue
- }
- w1 = nil // disable Write until this one is done
+ if p.rerr != nil {
+ return 0, os.EINVAL
}
-
- if r1 == nil && w1 == nil {
- // Have rb and wb. Execute.
- n := copy(rb, wb)
- wn += n
- wb = wb[n:]
-
- // Finish Read.
- p.r2 <- pipeResult{n, nil}
- r1 = p.r1 // allow another Read
-
- // Maybe finish Write.
- if len(wb) == 0 {
- p.w2 <- pipeResult{wn, nil}
- wn = 0
- w1 = p.w1 // allow another Write
- }
+ if p.data != nil {
+ break
}
+ if p.werr != nil {
+ return 0, p.werr
+ }
+ p.rwait.Wait()
+ }
+ n = copy(b, p.data)
+ p.data = p.data[n:]
+ if len(p.data) == 0 {
+ p.data = nil
+ p.wwait.Signal()
}
+ return
}
-// Read/write halves of the pipe.
-// They are separate structures for two reasons:
-// 1. If one end becomes garbage without being Closed,
-// its finalizer can Close so that the other end
-// does not hang indefinitely.
-// 2. Clients cannot use interface conversions on the
-// read end to find the Write method, and vice versa.
+var zero [0]byte
-type pipeHalf struct {
- c1 chan []byte
- c2 chan pipeResult
- cclose chan os.Error
- done chan int
-
- lock sync.Mutex
- closed bool
+func (p *pipe) write(b []byte) (n int, err os.Error) {
+ // pipe uses nil to mean not available
+ if b == nil {
+ b = zero[:]
+ }
- io sync.Mutex
- ioclosed bool
-}
+ // One writer at a time.
+ p.wl.Lock()
+ defer p.wl.Unlock()
-func (p *pipeHalf) rw(data []byte) (n int, err os.Error) {
- // Run i/o operation.
- // Check ioclosed flag under lock to make sure we're still allowed to do i/o.
- p.io.Lock()
- if p.ioclosed {
- p.io.Unlock()
- return 0, os.EINVAL
+ p.l.Lock()
+ defer p.l.Unlock()
+ p.data = b
+ p.rwait.Signal()
+ for {
+ if p.data == nil {
+ break
+ }
+ if p.rerr != nil {
+ err = p.rerr
+ break
+ }
+ if p.werr != nil {
+ err = os.EINVAL
+ }
+ p.wwait.Wait()
}
- p.io.Unlock()
- p.c1 <- data
- res := <-p.c2
- return res.n, res.err
+ n = len(b) - len(p.data)
+ p.data = nil // in case of rerr or werr
+ return
}
-func (p *pipeHalf) close(err os.Error) os.Error {
- // Close pipe half.
- // Only first call to close does anything.
- p.lock.Lock()
- if p.closed {
- p.lock.Unlock()
- return os.EINVAL
+func (p *pipe) rclose(err os.Error) {
+ if err == nil {
+ err = os.EPIPE
}
- p.closed = true
- p.lock.Unlock()
-
- // First, send the close notification.
- p.cclose <- err
-
- // Runner is now responding to rw operations
- // with os.EINVAL. Cut off future rw operations
- // by setting ioclosed flag.
- p.io.Lock()
- p.ioclosed = true
- p.io.Unlock()
-
- // With ioclosed set, there will be no more rw operations
- // working on the channels.
- // Tell the runner we won't be bothering it anymore.
- p.done <- 1
-
- // Successfully torn down; can disable finalizer.
- runtime.SetFinalizer(p, nil)
-
- return nil
+ p.l.Lock()
+ defer p.l.Unlock()
+ p.rerr = err
+ p.rwait.Signal()
+ p.wwait.Signal()
}
-func (p *pipeHalf) finalizer() {
- p.close(os.EINVAL)
+func (p *pipe) wclose(err os.Error) {
+ if err == nil {
+ err = os.EOF
+ }
+ p.l.Lock()
+ defer p.l.Unlock()
+ p.werr = err
+ p.rwait.Signal()
+ p.wwait.Signal()
}
-
// A PipeReader is the read half of a pipe.
type PipeReader struct {
- pipeHalf
+ p *pipe
}
// Read implements the standard Read interface:
// it reads data from the pipe, blocking until a writer
// arrives or the write end is closed.
// If the write end is closed with an error, that error is
-// returned as err; otherwise err is nil.
+// returned as err; otherwise err is os.EOF.
func (r *PipeReader) Read(data []byte) (n int, err os.Error) {
- return r.rw(data)
+ return r.p.read(data)
}
// Close closes the reader; subsequent writes to the
@@ -231,15 +136,13 @@ func (r *PipeReader) Close() os.Error {
// CloseWithError closes the reader; subsequent writes
// to the write half of the pipe will return the error err.
func (r *PipeReader) CloseWithError(err os.Error) os.Error {
- if err == nil {
- err = os.EPIPE
- }
- return r.close(err)
+ r.p.rclose(err)
+ return nil
}
// A PipeWriter is the write half of a pipe.
type PipeWriter struct {
- pipeHalf
+ p *pipe
}
// Write implements the standard Write interface:
@@ -248,7 +151,7 @@ type PipeWriter struct {
// If the read end is closed with an error, that err is
// returned as err; otherwise err is os.EPIPE.
func (w *PipeWriter) Write(data []byte) (n int, err os.Error) {
- return w.rw(data)
+ return w.p.write(data)
}
// Close closes the writer; subsequent reads from the
@@ -260,10 +163,8 @@ func (w *PipeWriter) Close() os.Error {
// CloseWithError closes the writer; subsequent reads from the
// read half of the pipe will return no bytes and the error err.
func (w *PipeWriter) CloseWithError(err os.Error) os.Error {
- if err == nil {
- err = os.EOF
- }
- return w.close(err)
+ w.p.wclose(err)
+ return nil
}
// Pipe creates a synchronous in-memory pipe.
@@ -272,34 +173,10 @@ func (w *PipeWriter) CloseWithError(err os.Error) os.Error {
// Reads on one end are matched with writes on the other,
// copying data directly between the two; there is no internal buffering.
func Pipe() (*PipeReader, *PipeWriter) {
- p := &pipe{
- r1: make(chan []byte),
- r2: make(chan pipeResult),
- w1: make(chan []byte),
- w2: make(chan pipeResult),
- rclose: make(chan os.Error),
- wclose: make(chan os.Error),
- done: make(chan int),
- }
- go p.run()
-
- // NOTE: Cannot use composite literal here:
- // pipeHalf{c1: p.cr1, c2: p.cr2, cclose: p.crclose, cdone: p.cdone}
- // because this implicitly copies the pipeHalf, which copies the inner mutex.
-
- r := new(PipeReader)
- r.c1 = p.r1
- r.c2 = p.r2
- r.cclose = p.rclose
- r.done = p.done
- runtime.SetFinalizer(r, (*PipeReader).finalizer)
-
- w := new(PipeWriter)
- w.c1 = p.w1
- w.c2 = p.w2
- w.cclose = p.wclose
- w.done = p.done
- runtime.SetFinalizer(w, (*PipeWriter).finalizer)
-
+ p := new(pipe)
+ p.rwait.L = &p.l
+ p.wwait.L = &p.l
+ r := &PipeReader{p}
+ w := &PipeWriter{p}
return r, w
}
diff --git a/src/pkg/json/decode.go b/src/pkg/json/decode.go
index 501230c0c..a5fd33912 100644
--- a/src/pkg/json/decode.go
+++ b/src/pkg/json/decode.go
@@ -87,7 +87,7 @@ func (e *UnmarshalTypeError) String() string {
// led to an unexported (and therefore unwritable) struct field.
type UnmarshalFieldError struct {
Key string
- Type *reflect.StructType
+ Type reflect.Type
Field reflect.StructField
}
@@ -106,7 +106,7 @@ func (e *InvalidUnmarshalError) String() string {
return "json: Unmarshal(nil)"
}
- if _, ok := e.Type.(*reflect.PtrType); !ok {
+ if e.Type.Kind() != reflect.Ptr {
return "json: Unmarshal(non-pointer " + e.Type.String() + ")"
}
return "json: Unmarshal(nil " + e.Type.String() + ")"
@@ -123,8 +123,9 @@ func (d *decodeState) unmarshal(v interface{}) (err os.Error) {
}()
rv := reflect.NewValue(v)
- pv, ok := rv.(*reflect.PtrValue)
- if !ok || pv.IsNil() {
+ pv := rv
+ if pv.Kind() != reflect.Ptr ||
+ pv.IsNil() {
return &InvalidUnmarshalError{reflect.Typeof(v)}
}
@@ -215,7 +216,7 @@ func (d *decodeState) scanWhile(op int) int {
// 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 {
+ if !v.IsValid() {
_, rest, err := nextValue(d.data[d.off:], &d.nextscan)
if err != nil {
d.error(err)
@@ -262,20 +263,21 @@ func (d *decodeState) indirect(v reflect.Value, wantptr bool) (Unmarshaler, refl
_, isUnmarshaler = v.Interface().(Unmarshaler)
}
- if iv, ok := v.(*reflect.InterfaceValue); ok && !iv.IsNil() {
+ if iv := v; iv.Kind() == reflect.Interface && !iv.IsNil() {
v = iv.Elem()
continue
}
- pv, ok := v.(*reflect.PtrValue)
- if !ok {
+ pv := v
+ if pv.Kind() != reflect.Ptr {
break
}
- _, isptrptr := pv.Elem().(*reflect.PtrValue)
- if !isptrptr && wantptr && !isUnmarshaler {
+
+ if pv.Elem().Kind() != reflect.Ptr &&
+ wantptr && !isUnmarshaler {
return nil, pv
}
if pv.IsNil() {
- pv.PointTo(reflect.MakeZero(pv.Type().(*reflect.PtrType).Elem()))
+ pv.Set(reflect.Zero(pv.Type().Elem()).Addr())
}
if isUnmarshaler {
// Using v.Interface().(Unmarshaler)
@@ -286,7 +288,7 @@ func (d *decodeState) indirect(v reflect.Value, wantptr bool) (Unmarshaler, refl
// 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
+ return v.Interface().(Unmarshaler), reflect.Value{}
}
v = pv.Elem()
}
@@ -309,22 +311,23 @@ func (d *decodeState) array(v reflect.Value) {
v = pv
// Decoding into nil interface? Switch to non-reflect code.
- iv, ok := v.(*reflect.InterfaceValue)
+ iv := v
+ ok := iv.Kind() == reflect.Interface
if ok {
iv.Set(reflect.NewValue(d.arrayInterface()))
return
}
// Check type of target.
- av, ok := v.(reflect.ArrayOrSliceValue)
- if !ok {
+ av := v
+ if av.Kind() != reflect.Array && av.Kind() != reflect.Slice {
d.saveError(&UnmarshalTypeError{"array", v.Type()})
d.off--
d.next()
return
}
- sv, _ := v.(*reflect.SliceValue)
+ sv := v
i := 0
for {
@@ -339,26 +342,26 @@ func (d *decodeState) array(v reflect.Value) {
d.scan.undo(op)
// Get element of array, growing if necessary.
- if i >= av.Cap() && sv != nil {
+ if i >= av.Cap() && sv.IsValid() {
newcap := sv.Cap() + sv.Cap()/2
if newcap < 4 {
newcap = 4
}
- newv := reflect.MakeSlice(sv.Type().(*reflect.SliceType), sv.Len(), newcap)
+ newv := reflect.MakeSlice(sv.Type(), sv.Len(), newcap)
reflect.Copy(newv, sv)
sv.Set(newv)
}
- if i >= av.Len() && sv != nil {
+ 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.Elem(i))
+ d.value(av.Index(i))
} else {
// Ran out of fixed array: skip.
- d.value(nil)
+ d.value(reflect.Value{})
}
i++
@@ -372,11 +375,11 @@ func (d *decodeState) array(v reflect.Value) {
}
}
if i < av.Len() {
- if sv == nil {
+ if !sv.IsValid() {
// Array. Zero the rest.
- z := reflect.MakeZero(av.Type().(*reflect.ArrayType).Elem())
+ z := reflect.Zero(av.Type().Elem())
for ; i < av.Len(); i++ {
- av.Elem(i).SetValue(z)
+ av.Index(i).Set(z)
}
} else {
sv.SetLen(i)
@@ -405,36 +408,36 @@ func (d *decodeState) object(v reflect.Value) {
v = pv
// Decoding into nil interface? Switch to non-reflect code.
- iv, ok := v.(*reflect.InterfaceValue)
- if ok {
+ 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.MapValue
- sv *reflect.StructValue
+ mv reflect.Value
+ sv reflect.Value
)
- switch v := v.(type) {
- case *reflect.MapValue:
+ switch v.Kind() {
+ case reflect.Map:
// map must have string type
- t := v.Type().(*reflect.MapType)
+ t := v.Type()
if t.Key() != reflect.Typeof("") {
d.saveError(&UnmarshalTypeError{"object", v.Type()})
break
}
mv = v
if mv.IsNil() {
- mv.SetValue(reflect.MakeMap(t))
+ mv.Set(reflect.MakeMap(t))
}
- case *reflect.StructValue:
+ case reflect.Struct:
sv = v
default:
d.saveError(&UnmarshalTypeError{"object", v.Type()})
}
- if mv == nil && sv == nil {
+ if !mv.IsValid() && !sv.IsValid() {
d.off--
d.next() // skip over { } in input
return
@@ -462,12 +465,12 @@ func (d *decodeState) object(v reflect.Value) {
// Figure out field corresponding to key.
var subv reflect.Value
- if mv != nil {
- subv = reflect.MakeZero(mv.Type().(*reflect.MapType).Elem())
+ if mv.IsValid() {
+ subv = reflect.Zero(mv.Type().Elem())
} else {
var f reflect.StructField
var ok bool
- st := sv.Type().(*reflect.StructType)
+ st := sv.Type()
// First try for field with that tag.
if isValidTag(key) {
for i := 0; i < sv.NumField(); i++ {
@@ -510,8 +513,8 @@ func (d *decodeState) object(v reflect.Value) {
// Write value back to map;
// if using struct, subv points into struct already.
- if mv != nil {
- mv.SetElem(reflect.NewValue(key), subv)
+ if mv.IsValid() {
+ mv.SetMapIndex(reflect.NewValue(key), subv)
}
// Next token must be , or }.
@@ -552,21 +555,21 @@ func (d *decodeState) literal(v reflect.Value) {
switch c := item[0]; c {
case 'n': // null
- switch v.(type) {
+ switch v.Kind() {
default:
d.saveError(&UnmarshalTypeError{"null", v.Type()})
- case *reflect.InterfaceValue, *reflect.PtrValue, *reflect.MapValue:
- v.SetValue(nil)
+ case reflect.Interface, reflect.Ptr, reflect.Map:
+ v.Set(reflect.Zero(v.Type()))
}
case 't', 'f': // true, false
value := c == 't'
- switch v := v.(type) {
+ switch v.Kind() {
default:
d.saveError(&UnmarshalTypeError{"bool", v.Type()})
- case *reflect.BoolValue:
- v.Set(value)
- case *reflect.InterfaceValue:
+ case reflect.Bool:
+ v.SetBool(value)
+ case reflect.Interface:
v.Set(reflect.NewValue(value))
}
@@ -575,10 +578,10 @@ func (d *decodeState) literal(v reflect.Value) {
if !ok {
d.error(errPhase)
}
- switch v := v.(type) {
+ switch v.Kind() {
default:
d.saveError(&UnmarshalTypeError{"string", v.Type()})
- case *reflect.SliceValue:
+ case reflect.Slice:
if v.Type() != byteSliceType {
d.saveError(&UnmarshalTypeError{"string", v.Type()})
break
@@ -589,10 +592,10 @@ func (d *decodeState) literal(v reflect.Value) {
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(b[0:n]))
+ case reflect.String:
+ v.SetString(string(s))
+ case reflect.Interface:
v.Set(reflect.NewValue(string(s)))
}
@@ -601,10 +604,10 @@ func (d *decodeState) literal(v reflect.Value) {
d.error(errPhase)
}
s := string(item)
- switch v := v.(type) {
+ switch v.Kind() {
default:
d.error(&UnmarshalTypeError{"number", v.Type()})
- case *reflect.InterfaceValue:
+ case reflect.Interface:
n, err := strconv.Atof64(s)
if err != nil {
d.saveError(&UnmarshalTypeError{"number " + s, v.Type()})
@@ -612,29 +615,29 @@ func (d *decodeState) literal(v reflect.Value) {
}
v.Set(reflect.NewValue(n))
- case *reflect.IntValue:
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
n, err := strconv.Atoi64(s)
- if err != nil || v.Overflow(n) {
+ if err != nil || v.OverflowInt(n) {
d.saveError(&UnmarshalTypeError{"number " + s, v.Type()})
break
}
- v.Set(n)
+ v.SetInt(n)
- case *reflect.UintValue:
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
n, err := strconv.Atoui64(s)
- if err != nil || v.Overflow(n) {
+ if err != nil || v.OverflowUint(n) {
d.saveError(&UnmarshalTypeError{"number " + s, v.Type()})
break
}
- v.Set(n)
+ v.SetUint(n)
- case *reflect.FloatValue:
+ case reflect.Float32, reflect.Float64:
n, err := strconv.AtofN(s, v.Type().Bits())
- if err != nil || v.Overflow(n) {
+ if err != nil || v.OverflowFloat(n) {
d.saveError(&UnmarshalTypeError{"number " + s, v.Type()})
break
}
- v.Set(n)
+ v.SetFloat(n)
}
}
}
diff --git a/src/pkg/json/decode_test.go b/src/pkg/json/decode_test.go
index ad6026363..49135c4bf 100644
--- a/src/pkg/json/decode_test.go
+++ b/src/pkg/json/decode_test.go
@@ -21,7 +21,7 @@ type tx struct {
x int
}
-var txType = reflect.Typeof((*tx)(nil)).(*reflect.PtrType).Elem().(*reflect.StructType)
+var txType = reflect.Typeof((*tx)(nil)).Elem()
// A type that can unmarshal itself.
@@ -138,8 +138,8 @@ func TestUnmarshal(t *testing.T) {
continue
}
// v = new(right-type)
- v := reflect.NewValue(tt.ptr).(*reflect.PtrValue)
- v.PointTo(reflect.MakeZero(v.Type().(*reflect.PtrType).Elem()))
+ v := reflect.NewValue(tt.ptr)
+ v.Set(reflect.Zero(v.Type().Elem()).Addr())
if err := Unmarshal([]byte(in), v.Interface()); !reflect.DeepEqual(err, tt.err) {
t.Errorf("#%d: %v want %v", i, err, tt.err)
continue
@@ -157,6 +157,7 @@ func TestUnmarshal(t *testing.T) {
}
func TestUnmarshalMarshal(t *testing.T) {
+ initBig()
var v interface{}
if err := Unmarshal(jsonBig, &v); err != nil {
t.Fatalf("Unmarshal: %v", err)
diff --git a/src/pkg/json/encode.go b/src/pkg/json/encode.go
index 26ce47039..dfa3c59da 100644
--- a/src/pkg/json/encode.go
+++ b/src/pkg/json/encode.go
@@ -183,7 +183,7 @@ func (e *encodeState) error(err os.Error) {
var byteSliceType = reflect.Typeof([]byte(nil))
func (e *encodeState) reflectValue(v reflect.Value) {
- if v == nil {
+ if !v.IsValid() {
e.WriteString("null")
return
}
@@ -200,30 +200,30 @@ func (e *encodeState) reflectValue(v reflect.Value) {
return
}
- switch v := v.(type) {
- case *reflect.BoolValue:
- x := v.Get()
+ switch v.Kind() {
+ case reflect.Bool:
+ x := v.Bool()
if x {
e.WriteString("true")
} else {
e.WriteString("false")
}
- case *reflect.IntValue:
- e.WriteString(strconv.Itoa64(v.Get()))
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ e.WriteString(strconv.Itoa64(v.Int()))
- case *reflect.UintValue:
- e.WriteString(strconv.Uitoa64(v.Get()))
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ e.WriteString(strconv.Uitoa64(v.Uint()))
- case *reflect.FloatValue:
- e.WriteString(strconv.FtoaN(v.Get(), 'g', -1, v.Type().Bits()))
+ case reflect.Float32, reflect.Float64:
+ e.WriteString(strconv.FtoaN(v.Float(), 'g', -1, v.Type().Bits()))
- case *reflect.StringValue:
- e.string(v.Get())
+ case reflect.String:
+ e.string(v.String())
- case *reflect.StructValue:
+ case reflect.Struct:
e.WriteByte('{')
- t := v.Type().(*reflect.StructType)
+ t := v.Type()
n := v.NumField()
first := true
for i := 0; i < n; i++ {
@@ -246,8 +246,8 @@ func (e *encodeState) reflectValue(v reflect.Value) {
}
e.WriteByte('}')
- case *reflect.MapValue:
- if _, ok := v.Type().(*reflect.MapType).Key().(*reflect.StringType); !ok {
+ case reflect.Map:
+ if v.Type().Key().Kind() != reflect.String {
e.error(&UnsupportedTypeError{v.Type()})
}
if v.IsNil() {
@@ -255,19 +255,19 @@ func (e *encodeState) reflectValue(v reflect.Value) {
break
}
e.WriteByte('{')
- var sv stringValues = v.Keys()
+ var sv stringValues = v.MapKeys()
sort.Sort(sv)
for i, k := range sv {
if i > 0 {
e.WriteByte(',')
}
- e.string(k.(*reflect.StringValue).Get())
+ e.string(k.String())
e.WriteByte(':')
- e.reflectValue(v.Elem(k))
+ e.reflectValue(v.MapIndex(k))
}
e.WriteByte('}')
- case reflect.ArrayOrSliceValue:
+ case reflect.Array, reflect.Slice:
if v.Type() == byteSliceType {
e.WriteByte('"')
s := v.Interface().([]byte)
@@ -292,11 +292,11 @@ func (e *encodeState) reflectValue(v reflect.Value) {
if i > 0 {
e.WriteByte(',')
}
- e.reflectValue(v.Elem(i))
+ e.reflectValue(v.Index(i))
}
e.WriteByte(']')
- case interfaceOrPtrValue:
+ case reflect.Interface, reflect.Ptr:
if v.IsNil() {
e.WriteString("null")
return
@@ -328,7 +328,7 @@ type stringValues []reflect.Value
func (sv stringValues) Len() int { return len(sv) }
func (sv stringValues) Swap(i, j int) { sv[i], sv[j] = sv[j], sv[i] }
func (sv stringValues) Less(i, j int) bool { return sv.get(i) < sv.get(j) }
-func (sv stringValues) get(i int) string { return sv[i].(*reflect.StringValue).Get() }
+func (sv stringValues) get(i int) string { return sv[i].String() }
func (e *encodeState) string(s string) {
e.WriteByte('"')
diff --git a/src/pkg/json/scanner_test.go b/src/pkg/json/scanner_test.go
index 2dc8ff87f..0d4de3246 100644
--- a/src/pkg/json/scanner_test.go
+++ b/src/pkg/json/scanner_test.go
@@ -85,6 +85,7 @@ func TestIndent(t *testing.T) {
// Tests of a large random structure.
func TestCompactBig(t *testing.T) {
+ initBig()
var buf bytes.Buffer
if err := Compact(&buf, jsonBig); err != nil {
t.Fatalf("Compact: %v", err)
@@ -98,6 +99,7 @@ func TestCompactBig(t *testing.T) {
}
func TestIndentBig(t *testing.T) {
+ initBig()
var buf bytes.Buffer
if err := Indent(&buf, jsonBig, "", "\t"); err != nil {
t.Fatalf("Indent1: %v", err)
@@ -135,6 +137,7 @@ func TestIndentBig(t *testing.T) {
}
func TestNextValueBig(t *testing.T) {
+ initBig()
var scan scanner
item, rest, err := nextValue(jsonBig, &scan)
if err != nil {
@@ -160,6 +163,7 @@ func TestNextValueBig(t *testing.T) {
}
func BenchmarkSkipValue(b *testing.B) {
+ initBig()
var scan scanner
for i := 0; i < b.N; i++ {
nextValue(jsonBig, &scan)
@@ -191,12 +195,23 @@ func trim(b []byte) []byte {
var jsonBig []byte
-func init() {
- b, err := Marshal(genValue(10000))
- if err != nil {
- panic(err)
+const (
+ big = 10000
+ small = 100
+)
+
+func initBig() {
+ n := big
+ if testing.Short() {
+ n = small
+ }
+ if len(jsonBig) != n {
+ b, err := Marshal(genValue(n))
+ if err != nil {
+ panic(err)
+ }
+ jsonBig = b
}
- jsonBig = b
}
func genValue(n int) interface{} {
diff --git a/src/pkg/log/log.go b/src/pkg/log/log.go
index 658e3bd94..33140ee08 100644
--- a/src/pkg/log/log.go
+++ b/src/pkg/log/log.go
@@ -28,11 +28,12 @@ const (
// order they appear (the order listed here) or the format they present (as
// described in the comments). A colon appears after these items:
// 2009/0123 01:23:23.123123 /a/b/c/d.go:23: message
- Ldate = 1 << iota // the date: 2009/0123
- Ltime // the time: 01:23:23
- Lmicroseconds // microsecond resolution: 01:23:23.123123. assumes Ltime.
- Llongfile // full file name and line number: /a/b/c/d.go:23
- Lshortfile // final file name element and line number: d.go:23. overrides Llongfile
+ Ldate = 1 << iota // the date: 2009/0123
+ Ltime // the time: 01:23:23
+ Lmicroseconds // microsecond resolution: 01:23:23.123123. assumes Ltime.
+ Llongfile // full file name and line number: /a/b/c/d.go:23
+ Lshortfile // final file name element and line number: d.go:23. overrides Llongfile
+ LstdFlags = Ldate | Ltime // initial values for the standard logger
)
// A Logger represents an active logging object that generates lines of
@@ -40,10 +41,11 @@ const (
// the Writer's Write method. A Logger can be used simultaneously from
// multiple goroutines; it guarantees to serialize access to the Writer.
type Logger struct {
- mu sync.Mutex // ensures atomic writes
- out io.Writer // destination for output
- prefix string // prefix to write at beginning of each line
- flag int // properties
+ prefix string // prefix to write at beginning of each line
+ flag int // properties
+ mu sync.Mutex // ensures atomic writes; protects the following fields
+ out io.Writer // destination for output
+ buf bytes.Buffer // for accumulating text to write
}
// New creates a new Logger. The out variable sets the
@@ -54,7 +56,7 @@ func New(out io.Writer, prefix string, flag int) *Logger {
return &Logger{out: out, prefix: prefix, flag: flag}
}
-var std = New(os.Stderr, "", Ldate|Ltime)
+var std = New(os.Stderr, "", LstdFlags)
// Cheap integer to fixed-width decimal ASCII. Give a negative width to avoid zero-padding.
// Knows the buffer has capacity.
@@ -81,7 +83,7 @@ func itoa(buf *bytes.Buffer, i int, wid int) {
}
}
-func (l *Logger) formatHeader(buf *bytes.Buffer, ns int64, calldepth int) {
+func (l *Logger) formatHeader(buf *bytes.Buffer, ns int64, file string, line int) {
buf.WriteString(l.prefix)
if l.flag&(Ldate|Ltime|Lmicroseconds) != 0 {
t := time.SecondsToLocalTime(ns / 1e9)
@@ -107,21 +109,15 @@ func (l *Logger) formatHeader(buf *bytes.Buffer, ns int64, calldepth int) {
}
}
if l.flag&(Lshortfile|Llongfile) != 0 {
- _, file, line, ok := runtime.Caller(calldepth)
- if ok {
- if l.flag&Lshortfile != 0 {
- short := file
- for i := len(file) - 1; i > 0; i-- {
- if file[i] == '/' {
- short = file[i+1:]
- break
- }
+ if l.flag&Lshortfile != 0 {
+ short := file
+ for i := len(file) - 1; i > 0; i-- {
+ if file[i] == '/' {
+ short = file[i+1:]
+ break
}
- file = short
}
- } else {
- file = "???"
- line = 0
+ file = short
}
buf.WriteString(file)
buf.WriteByte(':')
@@ -138,15 +134,26 @@ func (l *Logger) formatHeader(buf *bytes.Buffer, ns int64, calldepth int) {
// paths it will be 2.
func (l *Logger) Output(calldepth int, s string) os.Error {
now := time.Nanoseconds() // get this early.
- buf := new(bytes.Buffer)
- l.formatHeader(buf, now, calldepth+1)
- buf.WriteString(s)
- if len(s) > 0 && s[len(s)-1] != '\n' {
- buf.WriteByte('\n')
+ // get caller info (if required) before locking - it's expensive.
+ var file string
+ var line int
+ if l.flag&(Lshortfile|Llongfile) != 0 {
+ var ok bool
+ _, file, line, ok = runtime.Caller(calldepth)
+ if !ok {
+ file = "???"
+ line = 0
+ }
}
l.mu.Lock()
defer l.mu.Unlock()
- _, err := l.out.Write(buf.Bytes())
+ l.buf.Reset()
+ l.formatHeader(&l.buf, now, file, line)
+ l.buf.WriteString(s)
+ if len(s) > 0 && s[len(s)-1] != '\n' {
+ l.buf.WriteByte('\n')
+ }
+ _, err := l.out.Write(l.buf.Bytes())
return err
}
@@ -203,19 +210,49 @@ func (l *Logger) Panicln(v ...interface{}) {
panic(s)
}
+// Flags returns the output flags for the logger.
+func (l *Logger) Flags() int {
+ return l.flag
+}
+
+// SetFlags sets the output flags for the logger.
+func (l *Logger) SetFlags(flag int) {
+ l.flag = flag
+}
+
+// Prefix returns the output prefix for the logger.
+func (l *Logger) Prefix() string {
+ return l.prefix
+}
+
+// SetPrefix sets the output prefix for the logger.
+func (l *Logger) SetPrefix(prefix string) {
+ l.prefix = prefix
+}
+
// SetOutput sets the output destination for the standard logger.
func SetOutput(w io.Writer) {
std.out = w
}
+// Flags returns the output flags for the standard logger.
+func Flags() int {
+ return std.Flags()
+}
+
// SetFlags sets the output flags for the standard logger.
func SetFlags(flag int) {
- std.flag = flag
+ std.SetFlags(flag)
+}
+
+// Prefix returns the output prefix for the standard logger.
+func Prefix() string {
+ return std.Prefix()
}
// SetPrefix sets the output prefix for the standard logger.
func SetPrefix(prefix string) {
- std.prefix = prefix
+ std.SetPrefix(prefix)
}
// These functions write to the standard logger.
diff --git a/src/pkg/log/log_test.go b/src/pkg/log/log_test.go
index f99070afb..158c3d93c 100644
--- a/src/pkg/log/log_test.go
+++ b/src/pkg/log/log_test.go
@@ -84,3 +84,36 @@ func TestOutput(t *testing.T) {
t.Errorf("log output should match %q is %q", expect, b.String())
}
}
+
+func TestFlagAndPrefixSetting(t *testing.T) {
+ var b bytes.Buffer
+ l := New(&b, "Test:", LstdFlags)
+ f := l.Flags()
+ if f != LstdFlags {
+ t.Errorf("Flags 1: expected %x got %x", LstdFlags, f)
+ }
+ l.SetFlags(f | Lmicroseconds)
+ f = l.Flags()
+ if f != LstdFlags|Lmicroseconds {
+ t.Errorf("Flags 2: expected %x got %x", LstdFlags|Lmicroseconds, f)
+ }
+ p := l.Prefix()
+ if p != "Test:" {
+ t.Errorf(`Prefix: expected "Test:" got %q`, p)
+ }
+ l.SetPrefix("Reality:")
+ p = l.Prefix()
+ if p != "Reality:" {
+ t.Errorf(`Prefix: expected "Reality:" got %q`, p)
+ }
+ // Verify a log message looks right, with our prefix and microseconds present.
+ l.Print("hello")
+ pattern := "^Reality:" + Rdate + " " + Rtime + Rmicroseconds + " hello\n"
+ matched, err := regexp.Match(pattern, b.Bytes())
+ if err != nil {
+ t.Fatalf("pattern %q did not compile: %s", pattern, err)
+ }
+ if !matched {
+ t.Error("message did not match pattern")
+ }
+}
diff --git a/src/pkg/mime/type.go b/src/pkg/mime/type.go
index a10b780ae..6fe0ed5fd 100644
--- a/src/pkg/mime/type.go
+++ b/src/pkg/mime/type.go
@@ -33,7 +33,7 @@ var mimeTypes = map[string]string{
var mimeLock sync.RWMutex
func loadMimeFile(filename string) {
- f, err := os.Open(filename, os.O_RDONLY, 0666)
+ f, err := os.Open(filename)
if err != nil {
return
}
diff --git a/src/pkg/net/Makefile b/src/pkg/net/Makefile
index 6b6d7c0e3..7ce650279 100644
--- a/src/pkg/net/Makefile
+++ b/src/pkg/net/Makefile
@@ -6,6 +6,7 @@ include ../../Make.inc
TARG=net
GOFILES=\
+ cgo_stub.go\
dial.go\
dnsmsg.go\
fd_$(GOOS).go\
@@ -13,6 +14,7 @@ GOFILES=\
ip.go\
ipsock.go\
iprawsock.go\
+ lookup.go\
net.go\
parse.go\
pipe.go\
@@ -24,6 +26,7 @@ GOFILES=\
GOFILES_freebsd=\
newpollserver.go\
fd.go\
+ file.go\
dnsconfig.go\
dnsclient.go\
port.go\
@@ -31,6 +34,7 @@ GOFILES_freebsd=\
GOFILES_darwin=\
newpollserver.go\
fd.go\
+ file.go\
dnsconfig.go\
dnsclient.go\
port.go\
@@ -38,12 +42,14 @@ GOFILES_darwin=\
GOFILES_linux=\
newpollserver.go\
fd.go\
+ file.go\
dnsconfig.go\
dnsclient.go\
port.go\
GOFILES_windows=\
resolv_windows.go\
+ file_windows.go\
GOFILES+=$(GOFILES_$(GOOS))
diff --git a/src/pkg/net/cgo_stub.go b/src/pkg/net/cgo_stub.go
new file mode 100644
index 000000000..e28f6622e
--- /dev/null
+++ b/src/pkg/net/cgo_stub.go
@@ -0,0 +1,21 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Stub cgo routines for systems that do not use cgo to do network lookups.
+
+package net
+
+import "os"
+
+func cgoLookupHost(name string) (addrs []string, err os.Error, completed bool) {
+ return nil, nil, false
+}
+
+func cgoLookupPort(network, service string) (port int, err os.Error, completed bool) {
+ return 0, nil, false
+}
+
+func cgoLookupIP(name string) (addrs []IP, err os.Error, completed bool) {
+ return nil, nil, false
+}
diff --git a/src/pkg/net/dial.go b/src/pkg/net/dial.go
index 1cf8e7915..66cb09b19 100644
--- a/src/pkg/net/dial.go
+++ b/src/pkg/net/dial.go
@@ -6,9 +6,7 @@ package net
import "os"
-// Dial connects to the remote address raddr on the network net.
-// If the string laddr is not empty, it is used as the local address
-// for the connection.
+// Dial connects to the address addr on the network net.
//
// Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only),
// "udp", "udp4" (IPv4-only), "udp6" (IPv6-only), "ip", "ip4"
@@ -16,79 +14,56 @@ import "os"
//
// For IP networks, addresses have the form host:port. If host is
// a literal IPv6 address, it must be enclosed in square brackets.
+// The functions JoinHostPort and SplitHostPort manipulate
+// addresses in this form.
//
// Examples:
-// Dial("tcp", "", "12.34.56.78:80")
-// Dial("tcp", "", "google.com:80")
-// Dial("tcp", "", "[de:ad:be:ef::ca:fe]:80")
-// Dial("tcp", "127.0.0.1:123", "127.0.0.1:88")
+// Dial("tcp", "12.34.56.78:80")
+// Dial("tcp", "google.com:80")
+// Dial("tcp", "[de:ad:be:ef::ca:fe]:80")
//
-func Dial(net, laddr, raddr string) (c Conn, err os.Error) {
+func Dial(net, addr string) (c Conn, err os.Error) {
+ raddr := addr
+ if raddr == "" {
+ return nil, &OpError{"dial", net, nil, errMissingAddress}
+ }
switch net {
case "tcp", "tcp4", "tcp6":
- var la, ra *TCPAddr
- if laddr != "" {
- if la, err = ResolveTCPAddr(laddr); err != nil {
- goto Error
- }
- }
- if raddr != "" {
- if ra, err = ResolveTCPAddr(raddr); err != nil {
- goto Error
- }
+ var ra *TCPAddr
+ if ra, err = ResolveTCPAddr(raddr); err != nil {
+ goto Error
}
- c, err := DialTCP(net, la, ra)
+ c, err := DialTCP(net, nil, ra)
if err != nil {
return nil, err
}
return c, nil
case "udp", "udp4", "udp6":
- var la, ra *UDPAddr
- if laddr != "" {
- if la, err = ResolveUDPAddr(laddr); err != nil {
- goto Error
- }
- }
- if raddr != "" {
- if ra, err = ResolveUDPAddr(raddr); err != nil {
- goto Error
- }
+ var ra *UDPAddr
+ if ra, err = ResolveUDPAddr(raddr); err != nil {
+ goto Error
}
- c, err := DialUDP(net, la, ra)
+ c, err := DialUDP(net, nil, ra)
if err != nil {
return nil, err
}
return c, nil
case "unix", "unixgram", "unixpacket":
- var la, ra *UnixAddr
- if raddr != "" {
- if ra, err = ResolveUnixAddr(net, raddr); err != nil {
- goto Error
- }
- }
- if laddr != "" {
- if la, err = ResolveUnixAddr(net, laddr); err != nil {
- goto Error
- }
+ var ra *UnixAddr
+ if ra, err = ResolveUnixAddr(net, raddr); err != nil {
+ goto Error
}
- c, err = DialUnix(net, la, ra)
+ c, err = DialUnix(net, nil, ra)
if err != nil {
return nil, err
}
return c, nil
case "ip", "ip4", "ip6":
- var la, ra *IPAddr
- if laddr != "" {
- if la, err = ResolveIPAddr(laddr); err != nil {
- goto Error
- }
- }
- if raddr != "" {
- if ra, err = ResolveIPAddr(raddr); err != nil {
- goto Error
- }
+ var ra *IPAddr
+ if ra, err = ResolveIPAddr(raddr); err != nil {
+ goto Error
}
- c, err := DialIP(net, la, ra)
+ c, err := DialIP(net, nil, ra)
if err != nil {
return nil, err
}
diff --git a/src/pkg/net/dialgoogle_test.go b/src/pkg/net/dialgoogle_test.go
index a432800cf..9a9c02ebd 100644
--- a/src/pkg/net/dialgoogle_test.go
+++ b/src/pkg/net/dialgoogle_test.go
@@ -32,7 +32,7 @@ func fetchGoogle(t *testing.T, fd Conn, network, addr string) {
}
func doDial(t *testing.T, network, addr string) {
- fd, err := Dial(network, "", addr)
+ fd, err := Dial(network, addr)
if err != nil {
t.Errorf("Dial(%q, %q, %q) = _, %v", network, "", addr, err)
return
@@ -55,6 +55,13 @@ var googleaddrs = []string{
"[2001:4860:0:2001::68]:80", // ipv6.google.com; removed if ipv6 flag not set
}
+func TestLookupCNAME(t *testing.T) {
+ cname, err := LookupCNAME("www.google.com")
+ if cname != "www.l.google.com." || err != nil {
+ t.Errorf(`LookupCNAME("www.google.com.") = %q, %v, want "www.l.google.com.", nil`, cname, err)
+ }
+}
+
func TestDialGoogle(t *testing.T) {
// If no ipv6 tunnel, don't try the last address.
if !*ipv6 {
@@ -64,14 +71,14 @@ func TestDialGoogle(t *testing.T) {
// Insert an actual IP address for google.com
// into the table.
- _, addrs, err := LookupHost("www.google.com")
+ addrs, err := LookupIP("www.google.com")
if err != nil {
t.Fatalf("lookup www.google.com: %v", err)
}
if len(addrs) == 0 {
t.Fatalf("no addresses for www.google.com")
}
- ip := ParseIP(addrs[0]).To4()
+ ip := addrs[0].To4()
for i, s := range googleaddrs {
if strings.Contains(s, "%") {
diff --git a/src/pkg/net/dnsclient.go b/src/pkg/net/dnsclient.go
index 3252dd454..c3e727bce 100644
--- a/src/pkg/net/dnsclient.go
+++ b/src/pkg/net/dnsclient.go
@@ -21,6 +21,7 @@ import (
"rand"
"sync"
"time"
+ "sort"
)
// DNSError represents a DNS lookup error.
@@ -159,7 +160,7 @@ func tryOneName(cfg *dnsConfig, name string, qtype uint16) (cname string, addrs
// all the cfg.servers[i] are IP addresses, which
// Dial will use without a DNS lookup.
server := cfg.servers[i] + ":53"
- c, cerr := Dial("udp", "", server)
+ c, cerr := Dial("udp", server)
if cerr != nil {
err = cerr
continue
@@ -178,12 +179,23 @@ func tryOneName(cfg *dnsConfig, name string, qtype uint16) (cname string, addrs
return
}
-func convertRR_A(records []dnsRR) []string {
- addrs := make([]string, len(records))
+func convertRR_A(records []dnsRR) []IP {
+ addrs := make([]IP, len(records))
for i := 0; i < len(records); i++ {
rr := records[i]
a := rr.(*dnsRR_A).A
- addrs[i] = IPv4(byte(a>>24), byte(a>>16), byte(a>>8), byte(a)).String()
+ addrs[i] = IPv4(byte(a>>24), byte(a>>16), byte(a>>8), byte(a))
+ }
+ return addrs
+}
+
+func convertRR_AAAA(records []dnsRR) []IP {
+ addrs := make([]IP, len(records))
+ for i := 0; i < len(records); i++ {
+ rr := records[i]
+ a := make(IP, 16)
+ copy(a, rr.(*dnsRR_AAAA).AAAA[:])
+ addrs[i] = a
}
return addrs
}
@@ -294,10 +306,8 @@ func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err os.Erro
return
}
-// LookupHost looks for name using the local hosts file and DNS resolver.
-// It returns the canonical name for the host and an array of that
-// host's addresses.
-func LookupHost(name string) (cname string, addrs []string, err os.Error) {
+// goLookupHost is the native Go implementation of LookupHost.
+func goLookupHost(name string) (addrs []string, err os.Error) {
onceLoadConfig.Do(loadConfig)
if dnserr != nil || cfg == nil {
err = dnserr
@@ -306,18 +316,69 @@ func LookupHost(name string) (cname string, addrs []string, err os.Error) {
// Use entries from /etc/hosts if they match.
addrs = lookupStaticHost(name)
if len(addrs) > 0 {
- cname = name
+ return
+ }
+ ips, err := goLookupIP(name)
+ if err != nil {
+ return
+ }
+ addrs = make([]string, 0, len(ips))
+ for _, ip := range ips {
+ addrs = append(addrs, ip.String())
+ }
+ return
+}
+
+// goLookupIP is the native Go implementation of LookupIP.
+func goLookupIP(name string) (addrs []IP, err os.Error) {
+ onceLoadConfig.Do(loadConfig)
+ if dnserr != nil || cfg == nil {
+ err = dnserr
return
}
var records []dnsRR
+ var cname string
cname, records, err = lookup(name, dnsTypeA)
if err != nil {
return
}
addrs = convertRR_A(records)
+ if cname != "" {
+ name = cname
+ }
+ _, records, err = lookup(name, dnsTypeAAAA)
+ if err != nil && len(addrs) > 0 {
+ // Ignore error because A lookup succeeded.
+ err = nil
+ }
+ if err != nil {
+ return
+ }
+ addrs = append(addrs, convertRR_AAAA(records)...)
return
}
+// LookupCNAME returns the canonical DNS host for the given name.
+// Callers that do not care about the canonical name can call
+// LookupHost or LookupIP directly; both take care of resolving
+// the canonical name as part of the lookup.
+func LookupCNAME(name string) (cname string, err os.Error) {
+ onceLoadConfig.Do(loadConfig)
+ if dnserr != nil || cfg == nil {
+ err = dnserr
+ return
+ }
+ _, rr, err := lookup(name, dnsTypeCNAME)
+ if err != nil {
+ return
+ }
+ if len(rr) >= 0 {
+ cname = rr[0].(*dnsRR_CNAME).Cname
+ }
+ return
+}
+
+// An SRV represents a single DNS SRV record.
type SRV struct {
Target string
Port uint16
@@ -344,22 +405,38 @@ func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err os.
return
}
+// An MX represents a single DNS MX record.
type MX struct {
Host string
Pref uint16
}
-func LookupMX(name string) (entries []*MX, err os.Error) {
- var records []dnsRR
- _, records, err = lookup(name, dnsTypeMX)
+// byPref implements sort.Interface to sort MX records by preference
+type byPref []*MX
+
+func (s byPref) Len() int { return len(s) }
+
+func (s byPref) Less(i, j int) bool { return s[i].Pref < s[j].Pref }
+
+func (s byPref) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+
+// LookupMX returns the DNS MX records for the given domain name sorted by preference.
+func LookupMX(name string) (mx []*MX, err os.Error) {
+ _, rr, err := lookup(name, dnsTypeMX)
if err != nil {
return
}
- entries = make([]*MX, len(records))
- for i := range records {
- r := records[i].(*dnsRR_MX)
- entries[i] = &MX{r.Mx, r.Pref}
+ mx = make([]*MX, len(rr))
+ for i := range rr {
+ r := rr[i].(*dnsRR_MX)
+ mx[i] = &MX{r.Mx, r.Pref}
+ }
+ // Shuffle the records to match RFC 5321 when sorted
+ for i := range mx {
+ j := rand.Intn(i + 1)
+ mx[i], mx[j] = mx[j], mx[i]
}
+ sort.Sort(byPref(mx))
return
}
diff --git a/src/pkg/net/dnsmsg.go b/src/pkg/net/dnsmsg.go
index dc195caf8..e8eb8d958 100644
--- a/src/pkg/net/dnsmsg.go
+++ b/src/pkg/net/dnsmsg.go
@@ -50,6 +50,7 @@ const (
dnsTypeMINFO = 14
dnsTypeMX = 15
dnsTypeTXT = 16
+ dnsTypeAAAA = 28
dnsTypeSRV = 33
// valid dnsQuestion.qtype only
@@ -244,8 +245,18 @@ type dnsRR_A struct {
A uint32 "ipv4"
}
-func (rr *dnsRR_A) Header() *dnsRR_Header { return &rr.Hdr }
+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.
//
@@ -270,6 +281,7 @@ var rr_mk = map[int]func() dnsRR{
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:].
@@ -377,43 +389,49 @@ Loop:
// TODO(rsc): Move into generic library?
// Pack a reflect.StructValue into msg. Struct members can only be uint16, uint32, string,
-// and other (often anonymous) structs.
-func packStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int, ok bool) {
+// [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().(*reflect.StructType).Field(i)
- switch fv := val.Field(i).(type) {
+ 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.StructValue:
+ case reflect.Struct:
off, ok = packStructValue(fv, msg, off)
- case *reflect.UintValue:
- i := fv.Get()
- switch fv.Type().Kind() {
- default:
+ case reflect.Uint16:
+ if off+2 > len(msg) {
+ return len(msg), false
+ }
+ i := fv.Uint()
+ msg[off] = byte(i >> 8)
+ msg[off+1] = byte(i)
+ off += 2
+ case reflect.Uint32:
+ if off+4 > len(msg) {
+ return len(msg), false
+ }
+ i := fv.Uint()
+ 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
- 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.StringValue:
+ 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.Get()
+ s := fv.String()
switch f.Tag {
default:
fmt.Fprintf(os.Stderr, "net: dns: unknown string tag %v", f.Tag)
@@ -437,8 +455,8 @@ func packStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int, o
return off, true
}
-func structValue(any interface{}) *reflect.StructValue {
- return reflect.NewValue(any).(*reflect.PtrValue).Elem().(*reflect.StructValue)
+func structValue(any interface{}) reflect.Value {
+ return reflect.NewValue(any).Elem()
}
func packStruct(any interface{}, msg []byte, off int) (off1 int, ok bool) {
@@ -449,36 +467,41 @@ func packStruct(any interface{}, msg []byte, off int) (off1 int, ok bool) {
// 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) {
+func unpackStructValue(val reflect.Value, 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) {
+ 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.StructValue:
+ case reflect.Struct:
off, ok = unpackStructValue(fv, msg, off)
- case *reflect.UintValue:
- switch fv.Type().Kind() {
- default:
+ 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
- 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.StringValue:
+ 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:
@@ -502,7 +525,7 @@ func unpackStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int,
off += n
s = string(b)
}
- fv.Set(s)
+ fv.SetString(s)
}
}
return off, true
@@ -515,24 +538,28 @@ func unpackStruct(any interface{}, msg []byte, off int) (off1 int, ok bool) {
// Generic struct printer.
// Doesn't care about the string tag "domain-name",
-// but does look for an "ipv4" tag on uint32 variables,
+// 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 {
+func printStructValue(val reflect.Value) string {
s := "{"
for i := 0; i < val.NumField(); i++ {
if i > 0 {
s += ", "
}
- f := val.Type().(*reflect.StructType).Field(i)
+ f := val.Type().Field(i)
if !f.Anonymous {
s += f.Name + "="
}
fval := val.Field(i)
- if fv, ok := fval.(*reflect.StructValue); ok {
+ if fv := fval; fv.Kind() == reflect.Struct {
s += printStructValue(fv)
- } else if fv, ok := fval.(*reflect.UintValue); ok && f.Tag == "ipv4" {
- i := fv.Get()
+ } 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())
}
diff --git a/src/pkg/net/fd.go b/src/pkg/net/fd.go
index 2ba9296f3..cd1a21dc3 100644
--- a/src/pkg/net/fd.go
+++ b/src/pkg/net/fd.go
@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// TODO(rsc): All the prints in this file should go to standard error.
-
package net
import (
@@ -85,11 +83,12 @@ func (e *InvalidConnError) Timeout() bool { return false }
// will the fd be closed.
type pollServer struct {
- cr, cw chan *netFD // buffered >= 1
- pr, pw *os.File
- pending map[int]*netFD
- poll *pollster // low-level OS hooks
- deadline int64 // next deadline (nsec since 1970)
+ cr, cw chan *netFD // buffered >= 1
+ pr, pw *os.File
+ poll *pollster // low-level OS hooks
+ sync.Mutex // controls pending and deadline
+ pending map[int]*netFD
+ deadline int64 // next deadline (nsec since 1970)
}
func (s *pollServer) AddFD(fd *netFD, mode int) {
@@ -103,10 +102,8 @@ func (s *pollServer) AddFD(fd *netFD, mode int) {
}
return
}
- if err := s.poll.AddFD(intfd, mode, false); err != nil {
- panic("pollServer AddFD " + err.String())
- return
- }
+
+ s.Lock()
var t int64
key := intfd << 1
@@ -119,11 +116,31 @@ func (s *pollServer) AddFD(fd *netFD, mode int) {
t = fd.wdeadline
}
s.pending[key] = fd
+ doWakeup := false
if t > 0 && (s.deadline == 0 || t < s.deadline) {
s.deadline = t
+ doWakeup = true
+ }
+
+ wake, err := s.poll.AddFD(intfd, mode, false)
+ if err != nil {
+ panic("pollServer AddFD " + err.String())
+ }
+ if wake {
+ doWakeup = true
+ }
+
+ s.Unlock()
+
+ if doWakeup {
+ s.Wakeup()
}
}
+var wakeupbuf [1]byte
+
+func (s *pollServer) Wakeup() { s.pw.Write(wakeupbuf[0:]) }
+
func (s *pollServer) LookupFD(fd int, mode int) *netFD {
key := fd << 1
if mode == 'w' {
@@ -195,6 +212,8 @@ func (s *pollServer) CheckDeadlines() {
func (s *pollServer) Run() {
var scratch [100]byte
+ s.Lock()
+ defer s.Unlock()
for {
var t = s.deadline
if t > 0 {
@@ -204,7 +223,7 @@ func (s *pollServer) Run() {
continue
}
}
- fd, mode, err := s.poll.WaitFD(t)
+ fd, mode, err := s.poll.WaitFD(s, t)
if err != nil {
print("pollServer WaitFD: ", err.String(), "\n")
return
@@ -215,22 +234,11 @@ func (s *pollServer) Run() {
continue
}
if fd == s.pr.Fd() {
- // Drain our wakeup pipe.
- for nn, _ := s.pr.Read(scratch[0:]); nn > 0; {
- nn, _ = s.pr.Read(scratch[0:])
- }
- // Read from channels
- Update:
- for {
- select {
- case fd := <-s.cr:
- s.AddFD(fd, 'r')
- case fd := <-s.cw:
- s.AddFD(fd, 'w')
- default:
- break Update
- }
- }
+ // Drain our wakeup pipe (we could loop here,
+ // but it's unlikely that there are more than
+ // len(scratch) wakeup calls).
+ s.pr.Read(scratch[0:])
+ s.CheckDeadlines()
} else {
netfd := s.LookupFD(fd, mode)
if netfd == nil {
@@ -242,19 +250,13 @@ func (s *pollServer) Run() {
}
}
-var wakeupbuf [1]byte
-
-func (s *pollServer) Wakeup() { s.pw.Write(wakeupbuf[0:]) }
-
func (s *pollServer) WaitRead(fd *netFD) {
- s.cr <- fd
- s.Wakeup()
+ s.AddFD(fd, 'r')
<-fd.cr
}
func (s *pollServer) WaitWrite(fd *netFD) {
- s.cw <- fd
- s.Wakeup()
+ s.AddFD(fd, 'w')
<-fd.cw
}
@@ -272,19 +274,25 @@ func startServer() {
pollserver = p
}
-func newFD(fd, family, proto int, net string, laddr, raddr Addr) (f *netFD, err os.Error) {
+func newFD(fd, family, proto int, net string) (f *netFD, err os.Error) {
onceStartServer.Do(startServer)
if e := syscall.SetNonblock(fd, true); e != 0 {
- return nil, &OpError{"setnonblock", net, laddr, os.Errno(e)}
+ return nil, os.Errno(e)
}
f = &netFD{
sysfd: fd,
family: family,
proto: proto,
net: net,
- laddr: laddr,
- raddr: raddr,
}
+ f.cr = make(chan bool, 1)
+ f.cw = make(chan bool, 1)
+ return f, nil
+}
+
+func (fd *netFD) setAddr(laddr, raddr Addr) {
+ fd.laddr = laddr
+ fd.raddr = raddr
var ls, rs string
if laddr != nil {
ls = laddr.String()
@@ -292,10 +300,23 @@ func newFD(fd, family, proto int, net string, laddr, raddr Addr) (f *netFD, err
if raddr != nil {
rs = raddr.String()
}
- f.sysfile = os.NewFile(fd, net+":"+ls+"->"+rs)
- f.cr = make(chan bool, 1)
- f.cw = make(chan bool, 1)
- return f, nil
+ fd.sysfile = os.NewFile(fd.sysfd, fd.net+":"+ls+"->"+rs)
+}
+
+func (fd *netFD) connect(ra syscall.Sockaddr) (err os.Error) {
+ e := syscall.Connect(fd.sysfd, ra)
+ if e == syscall.EINPROGRESS {
+ var errno int
+ pollserver.WaitWrite(fd)
+ e, errno = syscall.GetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_ERROR)
+ if errno != 0 {
+ return os.NewSyscallError("getsockopt", errno)
+ }
+ }
+ if e != 0 {
+ return os.Errno(e)
+ }
+ return nil
}
// Add a reference to this fd.
@@ -591,10 +612,11 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err os.
syscall.CloseOnExec(s)
syscall.ForkLock.RUnlock()
- if nfd, err = newFD(s, fd.family, fd.proto, fd.net, fd.laddr, toAddr(sa)); err != nil {
+ if nfd, err = newFD(s, fd.family, fd.proto, fd.net); err != nil {
syscall.Close(s)
return nil, err
}
+ nfd.setAddr(fd.laddr, toAddr(sa))
return nfd, nil
}
diff --git a/src/pkg/net/fd_darwin.go b/src/pkg/net/fd_darwin.go
index cd0738753..00a049bfd 100644
--- a/src/pkg/net/fd_darwin.go
+++ b/src/pkg/net/fd_darwin.go
@@ -15,6 +15,10 @@ type pollster struct {
kq int
eventbuf [10]syscall.Kevent_t
events []syscall.Kevent_t
+
+ // An event buffer for AddFD/DelFD.
+ // Must hold pollServer lock.
+ kbuf [1]syscall.Kevent_t
}
func newpollster() (p *pollster, err os.Error) {
@@ -27,15 +31,16 @@ func newpollster() (p *pollster, err os.Error) {
return p, nil
}
-func (p *pollster) AddFD(fd int, mode int, repeat bool) os.Error {
+func (p *pollster) AddFD(fd int, mode int, repeat bool) (bool, os.Error) {
+ // pollServer is locked.
+
var kmode int
if mode == 'r' {
kmode = syscall.EVFILT_READ
} else {
kmode = syscall.EVFILT_WRITE
}
- var events [1]syscall.Kevent_t
- ev := &events[0]
+ ev := &p.kbuf[0]
// EV_ADD - add event to kqueue list
// EV_RECEIPT - generate fake EV_ERROR as result of add,
// rather than waiting for real event
@@ -46,36 +51,37 @@ func (p *pollster) AddFD(fd int, mode int, repeat bool) os.Error {
}
syscall.SetKevent(ev, fd, kmode, flags)
- n, e := syscall.Kevent(p.kq, events[0:], events[0:], nil)
+ n, e := syscall.Kevent(p.kq, p.kbuf[0:], p.kbuf[0:], nil)
if e != 0 {
- return os.NewSyscallError("kevent", e)
+ return false, os.NewSyscallError("kevent", e)
}
if n != 1 || (ev.Flags&syscall.EV_ERROR) == 0 || int(ev.Ident) != fd || int(ev.Filter) != kmode {
- return os.ErrorString("kqueue phase error")
+ return false, os.ErrorString("kqueue phase error")
}
if ev.Data != 0 {
- return os.Errno(int(ev.Data))
+ return false, os.Errno(int(ev.Data))
}
- return nil
+ return false, nil
}
func (p *pollster) DelFD(fd int, mode int) {
+ // pollServer is locked.
+
var kmode int
if mode == 'r' {
kmode = syscall.EVFILT_READ
} else {
kmode = syscall.EVFILT_WRITE
}
- var events [1]syscall.Kevent_t
- ev := &events[0]
+ ev := &p.kbuf[0]
// EV_DELETE - delete event from kqueue list
// EV_RECEIPT - generate fake EV_ERROR as result of add,
// rather than waiting for real event
syscall.SetKevent(ev, fd, kmode, syscall.EV_DELETE|syscall.EV_RECEIPT)
- syscall.Kevent(p.kq, events[0:], events[0:], nil)
+ syscall.Kevent(p.kq, p.kbuf[0:], p.kbuf[0:], nil)
}
-func (p *pollster) WaitFD(nsec int64) (fd int, mode int, err os.Error) {
+func (p *pollster) WaitFD(s *pollServer, nsec int64) (fd int, mode int, err os.Error) {
var t *syscall.Timespec
for len(p.events) == 0 {
if nsec > 0 {
@@ -84,7 +90,11 @@ func (p *pollster) WaitFD(nsec int64) (fd int, mode int, err os.Error) {
}
*t = syscall.NsecToTimespec(nsec)
}
+
+ s.Unlock()
nn, e := syscall.Kevent(p.kq, nil, p.eventbuf[0:], t)
+ s.Lock()
+
if e != 0 {
if e == syscall.EINTR {
continue
diff --git a/src/pkg/net/fd_freebsd.go b/src/pkg/net/fd_freebsd.go
index 4c5e93424..e50883e94 100644
--- a/src/pkg/net/fd_freebsd.go
+++ b/src/pkg/net/fd_freebsd.go
@@ -15,6 +15,10 @@ type pollster struct {
kq int
eventbuf [10]syscall.Kevent_t
events []syscall.Kevent_t
+
+ // An event buffer for AddFD/DelFD.
+ // Must hold pollServer lock.
+ kbuf [1]syscall.Kevent_t
}
func newpollster() (p *pollster, err os.Error) {
@@ -27,15 +31,16 @@ func newpollster() (p *pollster, err os.Error) {
return p, nil
}
-func (p *pollster) AddFD(fd int, mode int, repeat bool) os.Error {
+func (p *pollster) AddFD(fd int, mode int, repeat bool) (bool, os.Error) {
+ // pollServer is locked.
+
var kmode int
if mode == 'r' {
kmode = syscall.EVFILT_READ
} else {
kmode = syscall.EVFILT_WRITE
}
- var events [1]syscall.Kevent_t
- ev := &events[0]
+ ev := &p.kbuf[0]
// EV_ADD - add event to kqueue list
// EV_ONESHOT - delete the event the first time it triggers
flags := syscall.EV_ADD
@@ -44,34 +49,35 @@ func (p *pollster) AddFD(fd int, mode int, repeat bool) os.Error {
}
syscall.SetKevent(ev, fd, kmode, flags)
- n, e := syscall.Kevent(p.kq, events[:], nil, nil)
+ n, e := syscall.Kevent(p.kq, p.kbuf[:], nil, nil)
if e != 0 {
- return os.NewSyscallError("kevent", e)
+ return false, os.NewSyscallError("kevent", e)
}
if n != 1 || (ev.Flags&syscall.EV_ERROR) == 0 || int(ev.Ident) != fd || int(ev.Filter) != kmode {
- return os.NewSyscallError("kqueue phase error", e)
+ return false, os.NewSyscallError("kqueue phase error", e)
}
if ev.Data != 0 {
- return os.Errno(int(ev.Data))
+ return false, os.Errno(int(ev.Data))
}
- return nil
+ return false, nil
}
func (p *pollster) DelFD(fd int, mode int) {
+ // pollServer is locked.
+
var kmode int
if mode == 'r' {
kmode = syscall.EVFILT_READ
} else {
kmode = syscall.EVFILT_WRITE
}
- var events [1]syscall.Kevent_t
- ev := &events[0]
+ ev := &p.kbuf[0]
// EV_DELETE - delete event from kqueue list
syscall.SetKevent(ev, fd, kmode, syscall.EV_DELETE)
- syscall.Kevent(p.kq, events[:], nil, nil)
+ syscall.Kevent(p.kq, p.kbuf[:], nil, nil)
}
-func (p *pollster) WaitFD(nsec int64) (fd int, mode int, err os.Error) {
+func (p *pollster) WaitFD(s *pollServer, nsec int64) (fd int, mode int, err os.Error) {
var t *syscall.Timespec
for len(p.events) == 0 {
if nsec > 0 {
@@ -80,7 +86,11 @@ func (p *pollster) WaitFD(nsec int64) (fd int, mode int, err os.Error) {
}
*t = syscall.NsecToTimespec(nsec)
}
+
+ s.Unlock()
nn, e := syscall.Kevent(p.kq, nil, p.eventbuf[:], t)
+ s.Lock()
+
if e != 0 {
if e == syscall.EINTR {
continue
diff --git a/src/pkg/net/fd_linux.go b/src/pkg/net/fd_linux.go
index ef86cb17f..dcf65c014 100644
--- a/src/pkg/net/fd_linux.go
+++ b/src/pkg/net/fd_linux.go
@@ -20,7 +20,17 @@ type pollster struct {
epfd int
// Events we're already waiting for
+ // Must hold pollServer lock
events map[int]uint32
+
+ // An event buffer for EpollWait.
+ // Used without a lock, may only be used by WaitFD.
+ waitEventBuf [10]syscall.EpollEvent
+ waitEvents []syscall.EpollEvent
+
+ // An event buffer for EpollCtl, to avoid a malloc.
+ // Must hold pollServer lock.
+ ctlEvent syscall.EpollEvent
}
func newpollster() (p *pollster, err os.Error) {
@@ -29,7 +39,7 @@ func newpollster() (p *pollster, err os.Error) {
// The arg to epoll_create is a hint to the kernel
// about the number of FDs we will care about.
- // We don't know.
+ // We don't know, and since 2.6.8 the kernel ignores it anyhow.
if p.epfd, e = syscall.EpollCreate(16); e != 0 {
return nil, os.NewSyscallError("epoll_create", e)
}
@@ -37,18 +47,19 @@ func newpollster() (p *pollster, err os.Error) {
return p, nil
}
-func (p *pollster) AddFD(fd int, mode int, repeat bool) os.Error {
- var ev syscall.EpollEvent
+func (p *pollster) AddFD(fd int, mode int, repeat bool) (bool, os.Error) {
+ // pollServer is locked.
+
var already bool
- ev.Fd = int32(fd)
- ev.Events, already = p.events[fd]
+ p.ctlEvent.Fd = int32(fd)
+ p.ctlEvent.Events, already = p.events[fd]
if !repeat {
- ev.Events |= syscall.EPOLLONESHOT
+ p.ctlEvent.Events |= syscall.EPOLLONESHOT
}
if mode == 'r' {
- ev.Events |= readFlags
+ p.ctlEvent.Events |= readFlags
} else {
- ev.Events |= writeFlags
+ p.ctlEvent.Events |= writeFlags
}
var op int
@@ -57,14 +68,16 @@ func (p *pollster) AddFD(fd int, mode int, repeat bool) os.Error {
} else {
op = syscall.EPOLL_CTL_ADD
}
- if e := syscall.EpollCtl(p.epfd, op, fd, &ev); e != 0 {
- return os.NewSyscallError("epoll_ctl", e)
+ if e := syscall.EpollCtl(p.epfd, op, fd, &p.ctlEvent); e != 0 {
+ return false, os.NewSyscallError("epoll_ctl", e)
}
- p.events[fd] = ev.Events
- return nil
+ p.events[fd] = p.ctlEvent.Events
+ return false, nil
}
func (p *pollster) StopWaiting(fd int, bits uint) {
+ // pollServer is locked.
+
events, already := p.events[fd]
if !already {
print("Epoll unexpected fd=", fd, "\n")
@@ -82,10 +95,9 @@ func (p *pollster) StopWaiting(fd int, bits uint) {
// event in the kernel. Otherwise, delete it.
events &= ^uint32(bits)
if int32(events)&^syscall.EPOLLONESHOT != 0 {
- var ev syscall.EpollEvent
- ev.Fd = int32(fd)
- ev.Events = events
- if e := syscall.EpollCtl(p.epfd, syscall.EPOLL_CTL_MOD, fd, &ev); e != 0 {
+ p.ctlEvent.Fd = int32(fd)
+ p.ctlEvent.Events = events
+ if e := syscall.EpollCtl(p.epfd, syscall.EPOLL_CTL_MOD, fd, &p.ctlEvent); e != 0 {
print("Epoll modify fd=", fd, ": ", os.Errno(e).String(), "\n")
}
p.events[fd] = events
@@ -98,6 +110,8 @@ func (p *pollster) StopWaiting(fd int, bits uint) {
}
func (p *pollster) DelFD(fd int, mode int) {
+ // pollServer is locked.
+
if mode == 'r' {
p.StopWaiting(fd, readFlags)
} else {
@@ -105,24 +119,32 @@ func (p *pollster) DelFD(fd int, mode int) {
}
}
-func (p *pollster) WaitFD(nsec int64) (fd int, mode int, err os.Error) {
- // Get an event.
- var evarray [1]syscall.EpollEvent
- ev := &evarray[0]
- var msec int = -1
- if nsec > 0 {
- msec = int((nsec + 1e6 - 1) / 1e6)
- }
- n, e := syscall.EpollWait(p.epfd, evarray[0:], msec)
- for e == syscall.EAGAIN || e == syscall.EINTR {
- n, e = syscall.EpollWait(p.epfd, evarray[0:], msec)
- }
- if e != 0 {
- return -1, 0, os.NewSyscallError("epoll_wait", e)
- }
- if n == 0 {
- return -1, 0, nil
+func (p *pollster) WaitFD(s *pollServer, nsec int64) (fd int, mode int, err os.Error) {
+ for len(p.waitEvents) == 0 {
+ var msec int = -1
+ if nsec > 0 {
+ msec = int((nsec + 1e6 - 1) / 1e6)
+ }
+
+ s.Unlock()
+ n, e := syscall.EpollWait(p.epfd, p.waitEventBuf[0:], msec)
+ s.Lock()
+
+ if e != 0 {
+ if e == syscall.EAGAIN || e == syscall.EINTR {
+ continue
+ }
+ return -1, 0, os.NewSyscallError("epoll_wait", e)
+ }
+ if n == 0 {
+ return -1, 0, nil
+ }
+ p.waitEvents = p.waitEventBuf[0:n]
}
+
+ ev := &p.waitEvents[0]
+ p.waitEvents = p.waitEvents[1:]
+
fd = int(ev.Fd)
if ev.Events&writeFlags != 0 {
diff --git a/src/pkg/net/fd_windows.go b/src/pkg/net/fd_windows.go
index 63a8fbc44..c2f736cc1 100644
--- a/src/pkg/net/fd_windows.go
+++ b/src/pkg/net/fd_windows.go
@@ -225,29 +225,40 @@ type netFD struct {
wio sync.Mutex
}
-func allocFD(fd, family, proto int, net string, laddr, raddr Addr) (f *netFD) {
+func allocFD(fd, family, proto int, net string) (f *netFD) {
f = &netFD{
sysfd: fd,
family: family,
proto: proto,
net: net,
- laddr: laddr,
- raddr: raddr,
}
runtime.SetFinalizer(f, (*netFD).Close)
return f
}
-func newFD(fd, family, proto int, net string, laddr, raddr Addr) (f *netFD, err os.Error) {
+func newFD(fd, family, proto int, net string) (f *netFD, err os.Error) {
if initErr != nil {
return nil, initErr
}
onceStartServer.Do(startServer)
// Associate our socket with resultsrv.iocp.
if _, e := syscall.CreateIoCompletionPort(int32(fd), resultsrv.iocp, 0, 0); e != 0 {
- return nil, &OpError{"CreateIoCompletionPort", net, laddr, os.Errno(e)}
+ return nil, os.Errno(e)
+ }
+ return allocFD(fd, family, proto, net), nil
+}
+
+func (fd *netFD) setAddr(laddr, raddr Addr) {
+ fd.laddr = laddr
+ fd.raddr = raddr
+}
+
+func (fd *netFD) connect(ra syscall.Sockaddr) (err os.Error) {
+ e := syscall.Connect(fd.sysfd, ra)
+ if e != 0 {
+ return os.Errno(e)
}
- return allocFD(fd, family, proto, net, laddr, raddr), nil
+ return nil
}
// Add a reference to this fd.
@@ -497,7 +508,9 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err os.
lsa, _ := lrsa.Sockaddr()
rsa, _ := rrsa.Sockaddr()
- return allocFD(s, fd.family, fd.proto, fd.net, toAddr(lsa), toAddr(rsa)), nil
+ nfd = allocFD(s, fd.family, fd.proto, fd.net)
+ nfd.setAddr(toAddr(lsa), toAddr(rsa))
+ return nfd, nil
}
// Not implemeted functions.
diff --git a/src/pkg/net/file.go b/src/pkg/net/file.go
new file mode 100644
index 000000000..0e411a192
--- /dev/null
+++ b/src/pkg/net/file.go
@@ -0,0 +1,119 @@
+// 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 net
+
+import (
+ "os"
+ "syscall"
+)
+
+func newFileFD(f *os.File) (nfd *netFD, err os.Error) {
+ fd, errno := syscall.Dup(f.Fd())
+ if errno != 0 {
+ return nil, os.NewSyscallError("dup", errno)
+ }
+
+ proto, errno := syscall.GetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_TYPE)
+ if errno != 0 {
+ return nil, os.NewSyscallError("getsockopt", errno)
+ }
+
+ toAddr := sockaddrToTCP
+ sa, _ := syscall.Getsockname(fd)
+ switch sa.(type) {
+ default:
+ closesocket(fd)
+ return nil, os.EINVAL
+ case *syscall.SockaddrInet4:
+ if proto == syscall.SOCK_DGRAM {
+ toAddr = sockaddrToUDP
+ } else if proto == syscall.SOCK_RAW {
+ toAddr = sockaddrToIP
+ }
+ case *syscall.SockaddrInet6:
+ if proto == syscall.SOCK_DGRAM {
+ toAddr = sockaddrToUDP
+ } else if proto == syscall.SOCK_RAW {
+ toAddr = sockaddrToIP
+ }
+ case *syscall.SockaddrUnix:
+ toAddr = sockaddrToUnix
+ if proto == syscall.SOCK_DGRAM {
+ toAddr = sockaddrToUnixgram
+ } else if proto == syscall.SOCK_SEQPACKET {
+ toAddr = sockaddrToUnixpacket
+ }
+ }
+ laddr := toAddr(sa)
+ sa, _ = syscall.Getpeername(fd)
+ raddr := toAddr(sa)
+
+ if nfd, err = newFD(fd, 0, proto, laddr.Network()); err != nil {
+ return nil, err
+ }
+ nfd.setAddr(laddr, raddr)
+ return nfd, nil
+}
+
+// FileConn returns a copy of the network connection corresponding to
+// the open file f. It is the caller's responsibility to close f when
+// finished. Closing c does not affect f, and closing f does not
+// affect c.
+func FileConn(f *os.File) (c Conn, err os.Error) {
+ fd, err := newFileFD(f)
+ if err != nil {
+ return nil, err
+ }
+ switch fd.laddr.(type) {
+ case *TCPAddr:
+ return newTCPConn(fd), nil
+ case *UDPAddr:
+ return newUDPConn(fd), nil
+ case *UnixAddr:
+ return newUnixConn(fd), nil
+ case *IPAddr:
+ return newIPConn(fd), nil
+ }
+ fd.Close()
+ return nil, os.EINVAL
+}
+
+// FileListener returns a copy of the network listener corresponding
+// to the open file f. It is the caller's responsibility to close l
+// when finished. Closing c does not affect l, and closing l does not
+// affect c.
+func FileListener(f *os.File) (l Listener, err os.Error) {
+ fd, err := newFileFD(f)
+ if err != nil {
+ return nil, err
+ }
+ switch laddr := fd.laddr.(type) {
+ case *TCPAddr:
+ return &TCPListener{fd}, nil
+ case *UnixAddr:
+ return &UnixListener{fd, laddr.Name}, nil
+ }
+ fd.Close()
+ return nil, os.EINVAL
+}
+
+// FilePacketConn returns a copy of the packet network connection
+// corresponding to the open file f. It is the caller's
+// responsibility to close f when finished. Closing c does not affect
+// f, and closing f does not affect c.
+func FilePacketConn(f *os.File) (c PacketConn, err os.Error) {
+ fd, err := newFileFD(f)
+ if err != nil {
+ return nil, err
+ }
+ switch fd.laddr.(type) {
+ case *UDPAddr:
+ return newUDPConn(fd), nil
+ case *UnixAddr:
+ return newUnixConn(fd), nil
+ }
+ fd.Close()
+ return nil, os.EINVAL
+}
diff --git a/src/pkg/net/file_test.go b/src/pkg/net/file_test.go
new file mode 100644
index 000000000..1ec05fdee
--- /dev/null
+++ b/src/pkg/net/file_test.go
@@ -0,0 +1,131 @@
+// 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 net
+
+import (
+ "os"
+ "reflect"
+ "runtime"
+ "syscall"
+ "testing"
+)
+
+type listenerFile interface {
+ Listener
+ File() (f *os.File, err os.Error)
+}
+
+type packetConnFile interface {
+ PacketConn
+ File() (f *os.File, err os.Error)
+}
+
+type connFile interface {
+ Conn
+ File() (f *os.File, err os.Error)
+}
+
+func testFileListener(t *testing.T, net, laddr string) {
+ if net == "tcp" {
+ laddr += ":0" // any available port
+ }
+ l, err := Listen(net, laddr)
+ if err != nil {
+ t.Fatalf("Listen failed: %v", err)
+ }
+ defer l.Close()
+ lf := l.(listenerFile)
+ f, err := lf.File()
+ if err != nil {
+ t.Fatalf("File failed: %v", err)
+ }
+ c, err := FileListener(f)
+ if err != nil {
+ t.Fatalf("FileListener failed: %v", err)
+ }
+ if !reflect.DeepEqual(l.Addr(), c.Addr()) {
+ t.Fatalf("Addrs not equal: %#v != %#v", l.Addr(), c.Addr())
+ }
+ if err := c.Close(); err != nil {
+ t.Fatalf("Close failed: %v", err)
+ }
+ if err := f.Close(); err != nil {
+ t.Fatalf("Close failed: %v", err)
+ }
+}
+
+func TestFileListener(t *testing.T) {
+ if runtime.GOOS == "windows" {
+ return
+ }
+ testFileListener(t, "tcp", "127.0.0.1")
+ testFileListener(t, "tcp", "127.0.0.1")
+ if kernelSupportsIPv6() {
+ testFileListener(t, "tcp", "[::ffff:127.0.0.1]")
+ testFileListener(t, "tcp", "127.0.0.1")
+ testFileListener(t, "tcp", "[::ffff:127.0.0.1]")
+ }
+ if syscall.OS == "linux" {
+ testFileListener(t, "unix", "@gotest/net")
+ testFileListener(t, "unixpacket", "@gotest/net")
+ }
+}
+
+func testFilePacketConn(t *testing.T, pcf packetConnFile) {
+ f, err := pcf.File()
+ if err != nil {
+ t.Fatalf("File failed: %v", err)
+ }
+ c, err := FilePacketConn(f)
+ if err != nil {
+ t.Fatalf("FilePacketConn failed: %v", err)
+ }
+ if !reflect.DeepEqual(pcf.LocalAddr(), c.LocalAddr()) {
+ t.Fatalf("LocalAddrs not equal: %#v != %#v", pcf.LocalAddr(), c.LocalAddr())
+ }
+ if err := c.Close(); err != nil {
+ t.Fatalf("Close failed: %v", err)
+ }
+ if err := f.Close(); err != nil {
+ t.Fatalf("Close failed: %v", err)
+ }
+}
+
+func testFilePacketConnListen(t *testing.T, net, laddr string) {
+ l, err := ListenPacket(net, laddr)
+ if err != nil {
+ t.Fatalf("Listen failed: %v", err)
+ }
+ testFilePacketConn(t, l.(packetConnFile))
+ if err := l.Close(); err != nil {
+ t.Fatalf("Close failed: %v", err)
+ }
+}
+
+func testFilePacketConnDial(t *testing.T, net, raddr string) {
+ c, err := Dial(net, raddr)
+ if err != nil {
+ t.Fatalf("Dial failed: %v", err)
+ }
+ testFilePacketConn(t, c.(packetConnFile))
+ if err := c.Close(); err != nil {
+ t.Fatalf("Close failed: %v", err)
+ }
+}
+
+func TestFilePacketConn(t *testing.T) {
+ if runtime.GOOS == "windows" {
+ return
+ }
+ testFilePacketConnListen(t, "udp", "127.0.0.1:0")
+ testFilePacketConnDial(t, "udp", "127.0.0.1:12345")
+ if kernelSupportsIPv6() {
+ testFilePacketConnListen(t, "udp", "[::1]:0")
+ testFilePacketConnDial(t, "udp", "[::ffff:127.0.0.1]:12345")
+ }
+ if syscall.OS == "linux" {
+ testFilePacketConnListen(t, "unixgram", "@gotest1/net")
+ }
+}
diff --git a/src/pkg/net/file_windows.go b/src/pkg/net/file_windows.go
new file mode 100644
index 000000000..94aa58375
--- /dev/null
+++ b/src/pkg/net/file_windows.go
@@ -0,0 +1,25 @@
+// 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 net
+
+import (
+ "os"
+ "syscall"
+)
+
+func FileConn(f *os.File) (c Conn, err os.Error) {
+ // TODO: Implement this
+ return nil, os.NewSyscallError("FileConn", syscall.EWINDOWS)
+}
+
+func FileListener(f *os.File) (l Listener, err os.Error) {
+ // TODO: Implement this
+ return nil, os.NewSyscallError("FileListener", syscall.EWINDOWS)
+}
+
+func FilePacketConn(f *os.File) (c PacketConn, err os.Error) {
+ // TODO: Implement this
+ return nil, os.NewSyscallError("FilePacketConn", syscall.EWINDOWS)
+}
diff --git a/src/pkg/net/hosts.go b/src/pkg/net/hosts.go
index 8525f578d..d75e9e038 100644
--- a/src/pkg/net/hosts.go
+++ b/src/pkg/net/hosts.go
@@ -59,7 +59,7 @@ func readHosts() {
}
}
-// lookupStaticHosts looks up the addresses for the given host from /etc/hosts.
+// lookupStaticHost looks up the addresses for the given host from /etc/hosts.
func lookupStaticHost(host string) []string {
hosts.Lock()
defer hosts.Unlock()
@@ -72,7 +72,7 @@ func lookupStaticHost(host string) []string {
return nil
}
-// rlookupStaticHosts looks up the hosts for the given address from /etc/hosts.
+// lookupStaticAddr looks up the hosts for the given address from /etc/hosts.
func lookupStaticAddr(addr string) []string {
hosts.Lock()
defer hosts.Unlock()
diff --git a/src/pkg/net/hosts_test.go b/src/pkg/net/hosts_test.go
index 84cd92e37..470e35f78 100644
--- a/src/pkg/net/hosts_test.go
+++ b/src/pkg/net/hosts_test.go
@@ -13,7 +13,6 @@ type hostTest struct {
ips []IP
}
-
var hosttests = []hostTest{
{"odin", []IP{
IPv4(127, 0, 0, 2),
diff --git a/src/pkg/net/ip.go b/src/pkg/net/ip.go
index e82224a28..12bb6f351 100644
--- a/src/pkg/net/ip.go
+++ b/src/pkg/net/ip.go
@@ -12,6 +12,8 @@
package net
+import "os"
+
// IP address lengths (bytes).
const (
IPv4len = 4
@@ -39,11 +41,7 @@ type IPMask []byte
// IPv4 address a.b.c.d.
func IPv4(a, b, c, d byte) IP {
p := make(IP, IPv6len)
- for i := 0; i < 10; i++ {
- p[i] = 0
- }
- p[10] = 0xff
- p[11] = 0xff
+ copy(p, v4InV6Prefix)
p[12] = a
p[13] = b
p[14] = c
@@ -51,6 +49,8 @@ func IPv4(a, b, c, d byte) IP {
return p
}
+var v4InV6Prefix = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}
+
// IPv4Mask returns the IP mask (in 16-byte form) of the
// IPv4 mask a.b.c.d.
func IPv4Mask(a, b, c, d byte) IPMask {
@@ -140,9 +140,24 @@ func (ip IP) DefaultMask() IPMask {
return nil // not reached
}
+func allFF(b []byte) bool {
+ for _, c := range b {
+ if c != 0xff {
+ return false
+ }
+ }
+ return true
+}
+
// Mask returns the result of masking the IP address ip with mask.
func (ip IP) Mask(mask IPMask) IP {
n := len(ip)
+ if len(mask) == 16 && len(ip) == 4 && allFF(mask[:12]) {
+ mask = mask[12:]
+ }
+ if len(mask) == 4 && len(ip) == 16 && bytesEqual(ip[:12], v4InV6Prefix) {
+ ip = ip[12:]
+ }
if n != len(mask) {
return nil
}
@@ -245,6 +260,34 @@ func (ip IP) String() string {
return s
}
+// Equal returns true if ip and x are the same IP address.
+// An IPv4 address and that same address in IPv6 form are
+// considered to be equal.
+func (ip IP) Equal(x IP) bool {
+ if len(ip) == len(x) {
+ return bytesEqual(ip, x)
+ }
+ if len(ip) == 4 && len(x) == 16 {
+ return bytesEqual(x[0:12], v4InV6Prefix) && bytesEqual(ip, x[12:])
+ }
+ if len(ip) == 16 && len(x) == 4 {
+ return bytesEqual(ip[0:12], v4InV6Prefix) && bytesEqual(ip[12:], x)
+ }
+ return false
+}
+
+func bytesEqual(x, y []byte) bool {
+ if len(x) != len(y) {
+ return false
+ }
+ for i, b := range x {
+ if y[i] != b {
+ return false
+ }
+ }
+ return true
+}
+
// If mask is a sequence of 1 bits followed by 0 bits,
// return the number of 1 bits.
func simpleMaskLength(mask IPMask) int {
@@ -351,7 +394,6 @@ func parseIPv6(s string) IP {
// Loop, parsing hex numbers followed by colon.
j := 0
-L:
for j < IPv6len {
// Hex number.
n, i1, ok := xtoi(s, i)
@@ -432,15 +474,79 @@ L:
return p
}
+// A ParseError represents a malformed text string and the type of string that was expected.
+type ParseError struct {
+ Type string
+ Text string
+}
+
+func (e *ParseError) String() string {
+ return "invalid " + e.Type + ": " + e.Text
+}
+
+func parseIP(s string) IP {
+ if p := parseIPv4(s); p != nil {
+ return p
+ }
+ if p := parseIPv6(s); p != nil {
+ return p
+ }
+ return nil
+}
+
// ParseIP parses s as an IP address, returning the result.
// The string s can be in dotted decimal ("74.125.19.99")
// or IPv6 ("2001:4860:0:2001::68") form.
// If s is not a valid textual representation of an IP address,
// ParseIP returns nil.
func ParseIP(s string) IP {
- p := parseIPv4(s)
- if p != nil {
+ if p := parseIPv4(s); p != nil {
return p
}
return parseIPv6(s)
}
+
+// ParseCIDR parses s as a CIDR notation IP address and mask,
+// like "192.168.100.1/24", "2001:DB8::/48", as defined in
+// RFC 4632 and RFC 4291.
+func ParseCIDR(s string) (ip IP, mask IPMask, err os.Error) {
+ i := byteIndex(s, '/')
+ if i < 0 {
+ return nil, nil, &ParseError{"CIDR address", s}
+ }
+ ipstr, maskstr := s[:i], s[i+1:]
+ iplen := 4
+ ip = parseIPv4(ipstr)
+ if ip == nil {
+ iplen = 16
+ ip = parseIPv6(ipstr)
+ }
+ nn, i, ok := dtoi(maskstr, 0)
+ if ip == nil || !ok || i != len(maskstr) || nn < 0 || nn > 8*iplen {
+ return nil, nil, &ParseError{"CIDR address", s}
+ }
+ n := uint(nn)
+ if iplen == 4 {
+ v4mask := ^uint32(0xffffffff >> n)
+ mask = IPv4Mask(byte(v4mask>>24), byte(v4mask>>16), byte(v4mask>>8), byte(v4mask))
+ } else {
+ mask = make(IPMask, 16)
+ for i := 0; i < 16; i++ {
+ if n >= 8 {
+ mask[i] = 0xff
+ n -= 8
+ continue
+ }
+ mask[i] = ^byte(0xff >> n)
+ n = 0
+
+ }
+ }
+ // address must not have any bits not in mask
+ for i := range ip {
+ if ip[i]&^mask[i] != 0 {
+ return nil, nil, &ParseError{"CIDR address", s}
+ }
+ }
+ return ip, mask, nil
+}
diff --git a/src/pkg/net/ip_test.go b/src/pkg/net/ip_test.go
index e29c3021d..f1a4716d2 100644
--- a/src/pkg/net/ip_test.go
+++ b/src/pkg/net/ip_test.go
@@ -5,30 +5,26 @@
package net
import (
+ "bytes"
+ "reflect"
"testing"
+ "os"
)
-func isEqual(a, b IP) bool {
+func isEqual(a, b []byte) bool {
if a == nil && b == nil {
return true
}
- if a == nil || b == nil || len(a) != len(b) {
+ if a == nil || b == nil {
return false
}
- for i := 0; i < len(a); i++ {
- if a[i] != b[i] {
- return false
- }
- }
- return true
+ return bytes.Equal(a, b)
}
-type parseIPTest struct {
+var parseiptests = []struct {
in string
out IP
-}
-
-var parseiptests = []parseIPTest{
+}{
{"127.0.1.2", IPv4(127, 0, 1, 2)},
{"127.0.0.1", IPv4(127, 0, 0, 1)},
{"127.0.0.256", nil},
@@ -43,20 +39,17 @@ var parseiptests = []parseIPTest{
}
func TestParseIP(t *testing.T) {
- for i := 0; i < len(parseiptests); i++ {
- tt := parseiptests[i]
+ for _, tt := range parseiptests {
if out := ParseIP(tt.in); !isEqual(out, tt.out) {
t.Errorf("ParseIP(%#q) = %v, want %v", tt.in, out, tt.out)
}
}
}
-type ipStringTest struct {
+var ipstringtests = []struct {
in IP
out string
-}
-
-var ipstringtests = []ipStringTest{
+}{
// cf. RFC 5952 (A Recommendation for IPv6 Address Text Representation)
{IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0,
0, 0, 0x1, 0x23, 0, 0x12, 0, 0x1},
@@ -85,10 +78,67 @@ var ipstringtests = []ipStringTest{
}
func TestIPString(t *testing.T) {
- for i := 0; i < len(ipstringtests); i++ {
- tt := ipstringtests[i]
+ for _, tt := range ipstringtests {
if out := tt.in.String(); out != tt.out {
t.Errorf("IP.String(%v) = %#q, want %#q", tt.in, out, tt.out)
}
}
}
+
+var parsecidrtests = []struct {
+ in string
+ ip IP
+ mask IPMask
+ err os.Error
+}{
+ {"135.104.0.0/32", IPv4(135, 104, 0, 0), IPv4Mask(255, 255, 255, 255), nil},
+ {"0.0.0.0/24", IPv4(0, 0, 0, 0), IPv4Mask(255, 255, 255, 0), nil},
+ {"135.104.0.0/24", IPv4(135, 104, 0, 0), IPv4Mask(255, 255, 255, 0), nil},
+ {"135.104.0.1/32", IPv4(135, 104, 0, 1), IPv4Mask(255, 255, 255, 255), nil},
+ {"135.104.0.1/24", nil, nil, &ParseError{"CIDR address", "135.104.0.1/24"}},
+ {"::1/128", ParseIP("::1"), IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")), nil},
+ {"abcd:2345::/127", ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe")), nil},
+ {"abcd:2345::/65", ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:ffff:8000::")), nil},
+ {"abcd:2345::/64", ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:ffff::")), nil},
+ {"abcd:2345::/63", ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:fffe::")), nil},
+ {"abcd:2345::/33", ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:8000::")), nil},
+ {"abcd:2345::/32", ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff::")), nil},
+ {"abcd:2344::/31", ParseIP("abcd:2344::"), IPMask(ParseIP("ffff:fffe::")), nil},
+ {"abcd:2300::/24", ParseIP("abcd:2300::"), IPMask(ParseIP("ffff:ff00::")), nil},
+ {"abcd:2345::/24", nil, nil, &ParseError{"CIDR address", "abcd:2345::/24"}},
+ {"2001:DB8::/48", ParseIP("2001:DB8::"), IPMask(ParseIP("ffff:ffff:ffff::")), nil},
+}
+
+func TestParseCIDR(t *testing.T) {
+ for _, tt := range parsecidrtests {
+ if ip, mask, err := ParseCIDR(tt.in); !isEqual(ip, tt.ip) || !isEqual(mask, tt.mask) || !reflect.DeepEqual(err, tt.err) {
+ t.Errorf("ParseCIDR(%q) = %v, %v, %v; want %v, %v, %v", tt.in, ip, mask, err, tt.ip, tt.mask, tt.err)
+ }
+ }
+}
+
+var splitjointests = []struct {
+ Host string
+ Port string
+ Join string
+}{
+ {"www.google.com", "80", "www.google.com:80"},
+ {"127.0.0.1", "1234", "127.0.0.1:1234"},
+ {"::1", "80", "[::1]:80"},
+}
+
+func TestSplitHostPort(t *testing.T) {
+ for _, tt := range splitjointests {
+ if host, port, err := SplitHostPort(tt.Join); host != tt.Host || port != tt.Port || err != nil {
+ t.Errorf("SplitHostPort(%q) = %q, %q, %v; want %q, %q, nil", tt.Join, host, port, err, tt.Host, tt.Port)
+ }
+ }
+}
+
+func TestJoinHostPort(t *testing.T) {
+ for _, tt := range splitjointests {
+ if join := JoinHostPort(tt.Host, tt.Port); join != tt.Join {
+ t.Errorf("JoinHostPort(%q, %q) = %q; want %q", tt.Host, tt.Port, join, tt.Join)
+ }
+ }
+}
diff --git a/src/pkg/net/ipraw_test.go b/src/pkg/net/ipraw_test.go
index 562298bdf..ee8c71fc1 100644
--- a/src/pkg/net/ipraw_test.go
+++ b/src/pkg/net/ipraw_test.go
@@ -69,9 +69,12 @@ func TestICMP(t *testing.T) {
return
}
- var laddr *IPAddr
+ var (
+ laddr *IPAddr
+ err os.Error
+ )
if *srchost != "" {
- laddr, err := ResolveIPAddr(*srchost)
+ laddr, err = ResolveIPAddr(*srchost)
if err != nil {
t.Fatalf(`net.ResolveIPAddr("%v") = %v, %v`, *srchost, laddr, err)
}
diff --git a/src/pkg/net/iprawsock.go b/src/pkg/net/iprawsock.go
index 81a918ce5..60433303a 100644
--- a/src/pkg/net/iprawsock.go
+++ b/src/pkg/net/iprawsock.go
@@ -240,7 +240,7 @@ func hostToIP(host string) (ip IP, err os.Error) {
addr = ParseIP(host)
if addr == nil {
// Not an IP address. Try as a DNS name.
- _, addrs, err1 := LookupHost(host)
+ addrs, err1 := LookupHost(host)
if err1 != nil {
err = err1
goto Error
diff --git a/src/pkg/net/ipsock.go b/src/pkg/net/ipsock.go
index ae4204b48..80bc3eea5 100644
--- a/src/pkg/net/ipsock.go
+++ b/src/pkg/net/ipsock.go
@@ -170,9 +170,10 @@ func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, os.Error) {
return nil, InvalidAddrError("unexpected socket family")
}
-// Split "host:port" into "host" and "port".
-// Host cannot contain colons unless it is bracketed.
-func splitHostPort(hostport string) (host, port string, err os.Error) {
+// SplitHostPort splits a network address of the form
+// "host:port" or "[host]:port" into host and port.
+// The latter form must be used when host contains a colon.
+func SplitHostPort(hostport string) (host, port string, err os.Error) {
// The port starts after the last colon.
i := last(hostport, ':')
if i < 0 {
@@ -195,9 +196,9 @@ func splitHostPort(hostport string) (host, port string, err os.Error) {
return
}
-// Join "host" and "port" into "host:port".
-// If host contains colons, will join into "[host]:port".
-func joinHostPort(host, port string) string {
+// JoinHostPort combines host and port into a network address
+// of the form "host:port" or, if host contains a colon, "[host]:port".
+func JoinHostPort(host, port string) string {
// If host has colons, have to bracket it.
if byteIndex(host, ':') >= 0 {
return "[" + host + "]:" + port
@@ -207,7 +208,7 @@ func joinHostPort(host, port string) string {
// Convert "host:port" into IP address and port.
func hostPortToIP(net, hostport string) (ip IP, iport int, err os.Error) {
- host, port, err := splitHostPort(hostport)
+ host, port, err := SplitHostPort(hostport)
if err != nil {
goto Error
}
@@ -218,7 +219,7 @@ func hostPortToIP(net, hostport string) (ip IP, iport int, err os.Error) {
addr = ParseIP(host)
if addr == nil {
// Not an IP address. Try as a DNS name.
- _, addrs, err1 := LookupHost(host)
+ addrs, err1 := LookupHost(host)
if err1 != nil {
err = err1
goto Error
diff --git a/src/pkg/net/lookup.go b/src/pkg/net/lookup.go
new file mode 100644
index 000000000..7b2185ed4
--- /dev/null
+++ b/src/pkg/net/lookup.go
@@ -0,0 +1,38 @@
+// 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 net
+
+import (
+ "os"
+)
+
+// LookupHost looks up the given host using the local resolver.
+// It returns an array of that host's addresses.
+func LookupHost(host string) (addrs []string, err os.Error) {
+ addrs, err, ok := cgoLookupHost(host)
+ if !ok {
+ addrs, err = goLookupHost(host)
+ }
+ return
+}
+
+// LookupIP looks up host using the local resolver.
+// It returns an array of that host's IPv4 and IPv6 addresses.
+func LookupIP(host string) (addrs []IP, err os.Error) {
+ addrs, err, ok := cgoLookupIP(host)
+ if !ok {
+ addrs, err = goLookupIP(host)
+ }
+ return
+}
+
+// LookupPort looks up the port for the given network and service.
+func LookupPort(network, service string) (port int, err os.Error) {
+ port, err, ok := cgoLookupPort(network, service)
+ if !ok {
+ port, err = goLookupPort(network, service)
+ }
+ return
+}
diff --git a/src/pkg/net/multicast_test.go b/src/pkg/net/multicast_test.go
index 32fdec85b..be6dbf2dc 100644
--- a/src/pkg/net/multicast_test.go
+++ b/src/pkg/net/multicast_test.go
@@ -5,14 +5,21 @@
package net
import (
+ "flag"
"runtime"
"testing"
)
+var multicast = flag.Bool("multicast", false, "enable multicast tests")
+
func TestMulticastJoinAndLeave(t *testing.T) {
if runtime.GOOS == "windows" {
return
}
+ if !*multicast {
+ t.Logf("test disabled; use --multicast to enable")
+ return
+ }
addr := &UDPAddr{
IP: IPv4zero,
@@ -40,6 +47,10 @@ func TestMulticastJoinAndLeave(t *testing.T) {
}
func TestJoinFailureWithIPv6Address(t *testing.T) {
+ if !*multicast {
+ t.Logf("test disabled; use --multicast to enable")
+ return
+ }
addr := &UDPAddr{
IP: IPv4zero,
Port: 0,
diff --git a/src/pkg/net/net_test.go b/src/pkg/net/net_test.go
index 1e6e99eec..f7eae56fe 100644
--- a/src/pkg/net/net_test.go
+++ b/src/pkg/net/net_test.go
@@ -15,50 +15,49 @@ var runErrorTest = flag.Bool("run_error_test", false, "let TestDialError check f
type DialErrorTest struct {
Net string
- Laddr string
Raddr string
Pattern string
}
var dialErrorTests = []DialErrorTest{
{
- "datakit", "", "mh/astro/r70",
+ "datakit", "mh/astro/r70",
"dial datakit mh/astro/r70: unknown network datakit",
},
{
- "tcp", "", "127.0.0.1:☺",
+ "tcp", "127.0.0.1:☺",
"dial tcp 127.0.0.1:☺: unknown port tcp/☺",
},
{
- "tcp", "", "no-such-name.google.com.:80",
+ "tcp", "no-such-name.google.com.:80",
"dial tcp no-such-name.google.com.:80: lookup no-such-name.google.com.( on .*)?: no (.*)",
},
{
- "tcp", "", "no-such-name.no-such-top-level-domain.:80",
+ "tcp", "no-such-name.no-such-top-level-domain.:80",
"dial tcp no-such-name.no-such-top-level-domain.:80: lookup no-such-name.no-such-top-level-domain.( on .*)?: no (.*)",
},
{
- "tcp", "", "no-such-name:80",
+ "tcp", "no-such-name:80",
`dial tcp no-such-name:80: lookup no-such-name\.(.*\.)?( on .*)?: no (.*)`,
},
{
- "tcp", "", "mh/astro/r70:http",
+ "tcp", "mh/astro/r70:http",
"dial tcp mh/astro/r70:http: lookup mh/astro/r70: invalid domain name",
},
{
- "unix", "", "/etc/file-not-found",
+ "unix", "/etc/file-not-found",
"dial unix /etc/file-not-found: no such file or directory",
},
{
- "unix", "", "/etc/",
+ "unix", "/etc/",
"dial unix /etc/: (permission denied|socket operation on non-socket|connection refused)",
},
{
- "unixpacket", "", "/etc/file-not-found",
+ "unixpacket", "/etc/file-not-found",
"dial unixpacket /etc/file-not-found: no such file or directory",
},
{
- "unixpacket", "", "/etc/",
+ "unixpacket", "/etc/",
"dial unixpacket /etc/: (permission denied|socket operation on non-socket|connection refused)",
},
}
@@ -69,7 +68,7 @@ func TestDialError(t *testing.T) {
return
}
for i, tt := range dialErrorTests {
- c, e := Dial(tt.Net, tt.Laddr, tt.Raddr)
+ c, e := Dial(tt.Net, tt.Raddr)
if c != nil {
c.Close()
}
diff --git a/src/pkg/net/newpollserver.go b/src/pkg/net/newpollserver.go
index 820e70b46..fff54dba7 100644
--- a/src/pkg/net/newpollserver.go
+++ b/src/pkg/net/newpollserver.go
@@ -31,7 +31,7 @@ func newPollServer() (s *pollServer, err os.Error) {
if s.poll, err = newpollster(); err != nil {
goto Error
}
- if err = s.poll.AddFD(s.pr.Fd(), 'r', true); err != nil {
+ if _, err = s.poll.AddFD(s.pr.Fd(), 'r', true); err != nil {
s.poll.Close()
goto Error
}
diff --git a/src/pkg/net/parse.go b/src/pkg/net/parse.go
index 2bc0db465..de46830d2 100644
--- a/src/pkg/net/parse.go
+++ b/src/pkg/net/parse.go
@@ -63,7 +63,7 @@ func (f *file) readLine() (s string, ok bool) {
}
func open(name string) (*file, os.Error) {
- fd, err := os.Open(name, os.O_RDONLY, 0)
+ fd, err := os.Open(name)
if err != nil {
return nil, err
}
diff --git a/src/pkg/net/parse_test.go b/src/pkg/net/parse_test.go
index 2b7784eee..226f354d3 100644
--- a/src/pkg/net/parse_test.go
+++ b/src/pkg/net/parse_test.go
@@ -18,7 +18,7 @@ func TestReadLine(t *testing.T) {
}
filename := "/etc/services" // a nice big file
- fd, err := os.Open(filename, os.O_RDONLY, 0)
+ fd, err := os.Open(filename)
if err != nil {
t.Fatalf("open %s: %v", filename, err)
}
diff --git a/src/pkg/net/port.go b/src/pkg/net/port.go
index 7d25058b2..8f8327a37 100644
--- a/src/pkg/net/port.go
+++ b/src/pkg/net/port.go
@@ -50,8 +50,8 @@ func readServices() {
file.close()
}
-// LookupPort looks up the port for the given network and service.
-func LookupPort(network, service string) (port int, err os.Error) {
+// goLookupPort is the native Go implementation of LookupPort.
+func goLookupPort(network, service string) (port int, err os.Error) {
onceReadServices.Do(readServices)
switch network {
diff --git a/src/pkg/net/port_test.go b/src/pkg/net/port_test.go
index 1b7eaf231..329b169f3 100644
--- a/src/pkg/net/port_test.go
+++ b/src/pkg/net/port_test.go
@@ -27,9 +27,7 @@ var porttests = []portTest{
{"tcp", "smtp", 25, true},
{"tcp", "time", 37, true},
{"tcp", "domain", 53, true},
- {"tcp", "gopher", 70, true},
{"tcp", "finger", 79, true},
- {"tcp", "http", 80, true},
{"udp", "echo", 7, true},
{"udp", "tftp", 69, true},
diff --git a/src/pkg/net/resolv_windows.go b/src/pkg/net/resolv_windows.go
index f3d854ff2..000c30659 100644
--- a/src/pkg/net/resolv_windows.go
+++ b/src/pkg/net/resolv_windows.go
@@ -14,26 +14,51 @@ import (
var hostentLock sync.Mutex
var serventLock sync.Mutex
-func LookupHost(name string) (cname string, addrs []string, err os.Error) {
+func goLookupHost(name string) (addrs []string, err os.Error) {
+ ips, err := goLookupIP(name)
+ if err != nil {
+ return
+ }
+ addrs = make([]string, 0, len(ips))
+ for _, ip := range ips {
+ addrs = append(addrs, ip.String())
+ }
+ return
+}
+
+func goLookupIP(name string) (addrs []IP, err os.Error) {
hostentLock.Lock()
defer hostentLock.Unlock()
h, e := syscall.GetHostByName(name)
if e != 0 {
- return "", nil, os.NewSyscallError("GetHostByName", e)
+ return nil, os.NewSyscallError("GetHostByName", e)
}
- cname = name
switch h.AddrType {
case syscall.AF_INET:
i := 0
- addrs = make([]string, 100) // plenty of room to grow
+ addrs = make([]IP, 100) // plenty of room to grow
for p := (*[100](*[4]byte))(unsafe.Pointer(h.AddrList)); i < cap(addrs) && p[i] != nil; i++ {
- addrs[i] = IPv4(p[i][0], p[i][1], p[i][2], p[i][3]).String()
+ addrs[i] = IPv4(p[i][0], p[i][1], p[i][2], p[i][3])
}
addrs = addrs[0:i]
default: // TODO(vcc): Implement non IPv4 address lookups.
- return "", nil, os.NewSyscallError("LookupHost", syscall.EWINDOWS)
+ return nil, os.NewSyscallError("LookupHost", syscall.EWINDOWS)
+ }
+ return addrs, nil
+}
+
+func LookupCNAME(name string) (cname string, err os.Error) {
+ var r *syscall.DNSRecord
+ e := syscall.DnsQuery(name, syscall.DNS_TYPE_CNAME, 0, nil, &r, nil)
+ if int(e) != 0 {
+ return "", os.NewSyscallError("LookupCNAME", int(e))
+ }
+ defer syscall.DnsRecordListFree(r, 1)
+ if r != nil && r.Type == syscall.DNS_TYPE_CNAME {
+ v := (*syscall.DNSPTRData)(unsafe.Pointer(&r.Data[0]))
+ cname = syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Host))[:]) + "."
}
- return cname, addrs, nil
+ return
}
type SRV struct {
@@ -62,7 +87,7 @@ func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err os.
return name, addrs, nil
}
-func LookupPort(network, service string) (port int, err os.Error) {
+func goLookupPort(network, service string) (port int, err os.Error) {
switch network {
case "tcp4", "tcp6":
network = "tcp"
diff --git a/src/pkg/net/server_test.go b/src/pkg/net/server_test.go
index 3dda500e5..37695a068 100644
--- a/src/pkg/net/server_test.go
+++ b/src/pkg/net/server_test.go
@@ -54,13 +54,15 @@ func runServe(t *testing.T, network, addr string, listening chan<- string, done
}
func connect(t *testing.T, network, addr string, isEmpty bool) {
- var laddr string
+ var fd Conn
+ var err os.Error
if network == "unixgram" {
- laddr = addr + ".local"
+ fd, err = DialUnix(network, &UnixAddr{addr + ".local", network}, &UnixAddr{addr, network})
+ } else {
+ fd, err = Dial(network, addr)
}
- fd, err := Dial(network, laddr, addr)
if err != nil {
- t.Fatalf("net.Dial(%q, %q, %q) = _, %v", network, laddr, addr, err)
+ t.Fatalf("net.Dial(%q, %q) = _, %v", network, addr, err)
}
fd.SetReadTimeout(1e9) // 1s
diff --git a/src/pkg/net/sock.go b/src/pkg/net/sock.go
index 8ad3548ad..933700af1 100644
--- a/src/pkg/net/sock.go
+++ b/src/pkg/net/sock.go
@@ -52,11 +52,16 @@ func socket(net string, f, p, t int, la, ra syscall.Sockaddr, toAddr func(syscal
}
}
+ if fd, err = newFD(s, f, p, net); err != nil {
+ closesocket(s)
+ return nil, err
+ }
+
if ra != nil {
- e = syscall.Connect(s, ra)
- if e != 0 {
+ if err = fd.connect(ra); err != nil {
+ fd.sysfd = -1
closesocket(s)
- return nil, os.Errno(e)
+ return nil, err
}
}
@@ -65,12 +70,7 @@ func socket(net string, f, p, t int, la, ra syscall.Sockaddr, toAddr func(syscal
sa, _ = syscall.Getpeername(s)
raddr := toAddr(sa)
- fd, err = newFD(s, f, p, net, laddr, raddr)
- if err != nil {
- closesocket(s)
- return nil, err
- }
-
+ fd.setAddr(laddr, raddr)
return fd, nil
}
@@ -167,9 +167,9 @@ func (e *UnknownSocketError) String() string {
func sockaddrToString(sa syscall.Sockaddr) (name string, err os.Error) {
switch a := sa.(type) {
case *syscall.SockaddrInet4:
- return joinHostPort(IP(a.Addr[0:]).String(), itoa(a.Port)), nil
+ return JoinHostPort(IP(a.Addr[0:]).String(), itoa(a.Port)), nil
case *syscall.SockaddrInet6:
- return joinHostPort(IP(a.Addr[0:]).String(), itoa(a.Port)), nil
+ return JoinHostPort(IP(a.Addr[0:]).String(), itoa(a.Port)), nil
case *syscall.SockaddrUnix:
return a.Name, nil
}
diff --git a/src/pkg/net/tcpsock.go b/src/pkg/net/tcpsock.go
index a4bca11bb..b484be20b 100644
--- a/src/pkg/net/tcpsock.go
+++ b/src/pkg/net/tcpsock.go
@@ -34,7 +34,7 @@ func (a *TCPAddr) String() string {
if a == nil {
return "<nil>"
}
- return joinHostPort(a.IP.String(), itoa(a.Port))
+ return JoinHostPort(a.IP.String(), itoa(a.Port))
}
func (a *TCPAddr) family() int {
@@ -213,8 +213,9 @@ func (c *TCPConn) SetNoDelay(noDelay bool) os.Error {
// Closing c does not affect f, and closing f does not affect c.
func (c *TCPConn) File() (f *os.File, err os.Error) { return c.fd.dup() }
-// DialTCP is like Dial but can only connect to TCP networks
-// and returns a TCPConn structure.
+// DialTCP connects to the remote address raddr on the network net,
+// which must be "tcp", "tcp4", or "tcp6". If laddr is not nil, it is used
+// as the local address for the connection.
func DialTCP(net string, laddr, raddr *TCPAddr) (c *TCPConn, err os.Error) {
if raddr == nil {
return nil, &OpError{"dial", "tcp", nil, errMissingAddress}
diff --git a/src/pkg/net/textproto/textproto.go b/src/pkg/net/textproto/textproto.go
index f62009c52..fbfad9d61 100644
--- a/src/pkg/net/textproto/textproto.go
+++ b/src/pkg/net/textproto/textproto.go
@@ -78,7 +78,7 @@ func (c *Conn) Close() os.Error {
// Dial connects to the given address on the given network using net.Dial
// and then returns a new Conn for the connection.
func Dial(network, addr string) (*Conn, os.Error) {
- c, err := net.Dial(network, "", addr)
+ c, err := net.Dial(network, addr)
if err != nil {
return nil, err
}
diff --git a/src/pkg/net/timeout_test.go b/src/pkg/net/timeout_test.go
index 09a257dc8..0dbab5846 100644
--- a/src/pkg/net/timeout_test.go
+++ b/src/pkg/net/timeout_test.go
@@ -11,7 +11,7 @@ import (
)
func testTimeout(t *testing.T, network, addr string, readFrom bool) {
- fd, err := Dial(network, "", addr)
+ fd, err := Dial(network, addr)
if err != nil {
t.Errorf("dial %s %s failed: %v", network, addr, err)
return
diff --git a/src/pkg/net/udpsock.go b/src/pkg/net/udpsock.go
index f9274493e..44d618dab 100644
--- a/src/pkg/net/udpsock.go
+++ b/src/pkg/net/udpsock.go
@@ -34,7 +34,7 @@ func (a *UDPAddr) String() string {
if a == nil {
return "<nil>"
}
- return joinHostPort(a.IP.String(), itoa(a.Port))
+ return JoinHostPort(a.IP.String(), itoa(a.Port))
}
func (a *UDPAddr) family() int {
diff --git a/src/pkg/netchan/common.go b/src/pkg/netchan/common.go
index dd06050ee..a319391bf 100644
--- a/src/pkg/netchan/common.go
+++ b/src/pkg/netchan/common.go
@@ -73,7 +73,7 @@ type unackedCounter interface {
// A channel and its direction.
type chanDir struct {
- ch *reflect.ChanValue
+ ch reflect.Value
dir Dir
}
@@ -306,7 +306,7 @@ func (nch *netChan) sender() {
}
// Receive value from local side for sending to remote side.
-func (nch *netChan) recv() (val reflect.Value, closed bool) {
+func (nch *netChan) recv() (val reflect.Value, ok bool) {
if nch.dir != Send {
panic("recv on wrong direction of channel")
}
@@ -317,7 +317,7 @@ func (nch *netChan) recv() (val reflect.Value, closed bool) {
nch.space++
}
nch.space--
- return nch.ch.Recv(), nch.ch.Closed()
+ return nch.ch.Recv()
}
// acked is called when the remote side indicates that
diff --git a/src/pkg/netchan/export.go b/src/pkg/netchan/export.go
index 55eba0e2e..2209f04e8 100644
--- a/src/pkg/netchan/export.go
+++ b/src/pkg/netchan/export.go
@@ -181,8 +181,8 @@ func (client *expClient) run() {
// The header is passed by value to avoid issues of overwriting.
func (client *expClient) serveRecv(nch *netChan, hdr header, count int64) {
for {
- val, closed := nch.recv()
- if closed {
+ val, ok := nch.recv()
+ if !ok {
if err := client.encode(&hdr, payClosed, nil); err != nil {
expLog("error encoding server closed message:", err)
}
@@ -221,7 +221,7 @@ func (client *expClient) serveSend(hdr header) {
return
}
// Create a new value for each received item.
- val := reflect.MakeZero(nch.ch.Type().(*reflect.ChanType).Elem())
+ val := reflect.Zero(nch.ch.Type().Elem())
if err := client.decode(val); err != nil {
expLog("value decode:", err, "; type ", nch.ch.Type())
return
@@ -340,26 +340,26 @@ func (exp *Exporter) Sync(timeout int64) os.Error {
return exp.clientSet.sync(timeout)
}
-func checkChan(chT interface{}, dir Dir) (*reflect.ChanValue, os.Error) {
- chanType, ok := reflect.Typeof(chT).(*reflect.ChanType)
- if !ok {
- return nil, os.ErrorString("not a channel")
+func checkChan(chT interface{}, dir Dir) (reflect.Value, os.Error) {
+ chanType := reflect.Typeof(chT)
+ if chanType.Kind() != reflect.Chan {
+ return reflect.Value{}, os.ErrorString("not a channel")
}
if dir != Send && dir != Recv {
- return nil, os.ErrorString("unknown channel direction")
+ return reflect.Value{}, os.ErrorString("unknown channel direction")
}
- switch chanType.Dir() {
+ switch chanType.ChanDir() {
case reflect.BothDir:
case reflect.SendDir:
if dir != Recv {
- return nil, os.ErrorString("to import/export with Send, must provide <-chan")
+ return reflect.Value{}, os.ErrorString("to import/export with Send, must provide <-chan")
}
case reflect.RecvDir:
if dir != Send {
- return nil, os.ErrorString("to import/export with Recv, must provide chan<-")
+ return reflect.Value{}, os.ErrorString("to import/export with Recv, must provide chan<-")
}
}
- return reflect.NewValue(chT).(*reflect.ChanValue), nil
+ return reflect.NewValue(chT), nil
}
// Export exports a channel of a given type and specified direction. The
diff --git a/src/pkg/netchan/import.go b/src/pkg/netchan/import.go
index 30edcd812..9921486bd 100644
--- a/src/pkg/netchan/import.go
+++ b/src/pkg/netchan/import.go
@@ -48,7 +48,7 @@ func NewImporter(conn io.ReadWriter) *Importer {
// Import imports a set of channels from the given network and address.
func Import(network, remoteaddr string) (*Importer, os.Error) {
- conn, err := net.Dial(network, "", remoteaddr)
+ conn, err := net.Dial(network, remoteaddr)
if err != nil {
return nil, err
}
@@ -133,7 +133,7 @@ func (imp *Importer) run() {
ackHdr.SeqNum = hdr.SeqNum
imp.encode(ackHdr, payAck, nil)
// Create a new value for each received item.
- value := reflect.MakeZero(nch.ch.Type().(*reflect.ChanType).Elem())
+ value := reflect.Zero(nch.ch.Type().Elem())
if e := imp.decode(value); e != nil {
impLog("importer value decode:", e)
return
@@ -213,8 +213,8 @@ func (imp *Importer) ImportNValues(name string, chT interface{}, dir Dir, size,
if dir == Send {
go func() {
for i := 0; n == -1 || i < n; i++ {
- val, closed := nch.recv()
- if closed {
+ val, ok := nch.recv()
+ if !ok {
if err = imp.encode(hdr, payClosed, nil); err != nil {
impLog("error encoding client closed message:", err)
}
diff --git a/src/pkg/netchan/netchan_test.go b/src/pkg/netchan/netchan_test.go
index 1c84a9d14..fd4d8f780 100644
--- a/src/pkg/netchan/netchan_test.go
+++ b/src/pkg/netchan/netchan_test.go
@@ -41,8 +41,8 @@ func exportReceive(exp *Exporter, t *testing.T, expDone chan bool) {
t.Fatal("exportReceive:", err)
}
for i := 0; i < count; i++ {
- v := <-ch
- if closed(ch) {
+ v, ok := <-ch
+ if !ok {
if i != closeCount {
t.Errorf("exportReceive expected close at %d; got one at %d", closeCount, i)
}
@@ -78,8 +78,8 @@ func importReceive(imp *Importer, t *testing.T, done chan bool) {
t.Fatal("importReceive:", err)
}
for i := 0; i < count; i++ {
- v := <-ch
- if closed(ch) {
+ v, ok := <-ch
+ if !ok {
if i != closeCount {
t.Errorf("importReceive expected close at %d; got one at %d", closeCount, i)
}
@@ -212,8 +212,8 @@ func TestExportHangup(t *testing.T) {
}
// Now hang up the channel. Importer should see it close.
exp.Hangup("exportedSend")
- v = <-ich
- if !closed(ich) {
+ v, ok := <-ich
+ if ok {
t.Fatal("expected channel to be closed; got value", v)
}
}
@@ -242,8 +242,8 @@ func TestImportHangup(t *testing.T) {
}
// Now hang up the channel. Exporter should see it close.
imp.Hangup("exportedRecv")
- v = <-ech
- if !closed(ech) {
+ v, ok := <-ech
+ if ok {
t.Fatal("expected channel to be closed; got value", v)
}
}
@@ -399,7 +399,7 @@ func TestImportFlowControl(t *testing.T) {
func testFlow(sendDone chan bool, ch <-chan int, N int, t *testing.T) {
go func() {
- time.Sleep(1e9)
+ time.Sleep(0.5e9)
sendDone <- false
}()
diff --git a/src/pkg/os/Makefile b/src/pkg/os/Makefile
index 3a81afe39..cd9284079 100644
--- a/src/pkg/os/Makefile
+++ b/src/pkg/os/Makefile
@@ -6,7 +6,6 @@ include ../../Make.inc
TARG=os
GOFILES=\
- dir_$(GOOS).go\
error.go\
env.go\
exec.go\
@@ -19,29 +18,53 @@ GOFILES=\
types.go\
GOFILES_freebsd=\
+ dir_unix.go\
+ error_posix.go\
env_unix.go\
+ file_posix.go\
file_unix.go\
sys_bsd.go\
+ exec_posix.go\
exec_unix.go\
GOFILES_darwin=\
+ dir_unix.go\
+ error_posix.go\
env_unix.go\
+ file_posix.go\
file_unix.go\
sys_bsd.go\
+ exec_posix.go\
exec_unix.go\
GOFILES_linux=\
+ dir_unix.go\
+ error_posix.go\
env_unix.go\
+ file_posix.go\
file_unix.go\
sys_linux.go\
+ exec_posix.go\
exec_unix.go\
GOFILES_windows=\
+ dir_windows.go\
+ error_posix.go\
env_windows.go\
+ file_posix.go\
file_windows.go\
sys_windows.go\
+ exec_posix.go\
exec_windows.go\
+GOFILES_plan9=\
+ dir_plan9.go\
+ error_plan9.go\
+ env_plan9.go\
+ file_plan9.go\
+ sys_plan9.go\
+ exec_plan9.go\
+
GOFILES+=$(GOFILES_$(GOOS))
include ../../Make.pkg
diff --git a/src/pkg/os/dir_freebsd.go b/src/pkg/os/dir_freebsd.go
deleted file mode 100644
index 2ebe368a6..000000000
--- a/src/pkg/os/dir_freebsd.go
+++ /dev/null
@@ -1,66 +0,0 @@
-// 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 os
-
-import (
- "syscall"
- "unsafe"
-)
-
-const (
- blockSize = 4096 // TODO(r): use statfs
-)
-
-func (file *File) Readdirnames(count int) (names []string, err Error) {
- // If this file has no dirinfo, create one.
- if file.dirinfo == nil {
- file.dirinfo = new(dirInfo)
- // The buffer must be at least a block long.
- // TODO(r): use fstatfs to find fs block size.
- file.dirinfo.buf = make([]byte, blockSize)
- }
- d := file.dirinfo
- size := count
- if size < 0 {
- size = 100
- }
- names = make([]string, 0, size) // Empty with room to grow.
- for count != 0 {
- // Refill the buffer if necessary
- if d.bufp >= d.nbuf {
- var errno int
- d.bufp = 0
- // Final argument is (basep *uintptr) and the syscall doesn't take nil.
- d.nbuf, errno = syscall.Getdirentries(file.fd, d.buf, new(uintptr))
- if errno != 0 {
- d.nbuf = 0
- return names, NewSyscallError("getdirentries", errno)
- }
- if d.nbuf <= 0 {
- break // EOF
- }
- }
- // Drain the buffer
- for count != 0 && d.bufp < d.nbuf {
- dirent := (*syscall.Dirent)(unsafe.Pointer(&d.buf[d.bufp]))
- if dirent.Reclen == 0 {
- d.bufp = d.nbuf
- break
- }
- d.bufp += int(dirent.Reclen)
- if dirent.Fileno == 0 { // File absent in directory.
- continue
- }
- bytes := (*[10000]byte)(unsafe.Pointer(&dirent.Name[0]))
- var name = string(bytes[0:dirent.Namlen])
- if name == "." || name == ".." { // Useless names
- continue
- }
- count--
- names = append(names, name)
- }
- }
- return names, nil
-}
diff --git a/src/pkg/os/dir_linux.go b/src/pkg/os/dir_linux.go
deleted file mode 100644
index 09aad6367..000000000
--- a/src/pkg/os/dir_linux.go
+++ /dev/null
@@ -1,69 +0,0 @@
-// 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 os
-
-import (
- "syscall"
- "unsafe"
-)
-
-const (
- blockSize = 4096 // TODO(r): use statfs
-)
-
-func clen(n []byte) int {
- for i := 0; i < len(n); i++ {
- if n[i] == 0 {
- return i
- }
- }
- return len(n)
-}
-
-func (file *File) Readdirnames(count int) (names []string, err Error) {
- // If this file has no dirinfo, create one.
- if file.dirinfo == nil {
- file.dirinfo = new(dirInfo)
- // The buffer must be at least a block long.
- // TODO(r): use fstatfs to find fs block size.
- file.dirinfo.buf = make([]byte, blockSize)
- }
- d := file.dirinfo
- size := count
- if size < 0 {
- size = 100
- }
- names = make([]string, 0, size) // Empty with room to grow.
- for count != 0 {
- // Refill the buffer if necessary
- if d.bufp >= d.nbuf {
- var errno int
- d.nbuf, errno = syscall.Getdents(file.fd, d.buf)
- if errno != 0 {
- return names, NewSyscallError("getdents", errno)
- }
- if d.nbuf <= 0 {
- break // EOF
- }
- d.bufp = 0
- }
- // Drain the buffer
- for count != 0 && d.bufp < d.nbuf {
- dirent := (*syscall.Dirent)(unsafe.Pointer(&d.buf[d.bufp]))
- d.bufp += int(dirent.Reclen)
- if dirent.Ino == 0 { // File absent in directory.
- continue
- }
- bytes := (*[10000]byte)(unsafe.Pointer(&dirent.Name[0]))
- var name = string(bytes[0:clen(bytes[0:])])
- if name == "." || name == ".." { // Useless names
- continue
- }
- count--
- names = append(names, name)
- }
- }
- return names, nil
-}
diff --git a/src/pkg/os/dir_plan9.go b/src/pkg/os/dir_plan9.go
new file mode 100644
index 000000000..d9514191d
--- /dev/null
+++ b/src/pkg/os/dir_plan9.go
@@ -0,0 +1,284 @@
+// 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 os
+
+import (
+ "syscall"
+)
+
+// Readdir reads the contents of the directory associated with file and
+// returns an array of up to count FileInfo structures, in directory order.
+// Subsequent calls on the same file will yield further FileInfos.
+// A negative count means to read until EOF.
+// Readdir returns the array and an Error, if any.
+func (file *File) Readdir(count int) (fi []FileInfo, err Error) {
+ // If this file has no dirinfo, create one.
+ if file.dirinfo == nil {
+ file.dirinfo = new(dirInfo)
+ }
+ d := file.dirinfo
+ size := count
+ if size < 0 {
+ size = 100
+ }
+ result := make([]FileInfo, 0, size) // Empty with room to grow.
+ for count != 0 {
+ // Refill the buffer if necessary
+ if d.bufp >= d.nbuf {
+ d.bufp = 0
+ var e Error
+ d.nbuf, e = file.Read(d.buf[:])
+ if e != nil && e != EOF {
+ return nil, &PathError{"readdir", file.name, e}
+ }
+ if e == EOF {
+ break
+ }
+ if d.nbuf < syscall.STATFIXLEN {
+ return nil, &PathError{"readdir", file.name, Eshortstat}
+ }
+ }
+
+ // Get a record from buffer
+ m, _ := gbit16(d.buf[d.bufp:])
+ m += 2
+ if m < syscall.STATFIXLEN {
+ return nil, &PathError{"readdir", file.name, Eshortstat}
+ }
+ dir, e := UnmarshalDir(d.buf[d.bufp : d.bufp+int(m)])
+ if e != nil {
+ return nil, &PathError{"readdir", file.name, e}
+ }
+ var f FileInfo
+ fileInfoFromStat(&f, dir)
+ result = append(result, f)
+
+ d.bufp += int(m)
+ count--
+ }
+ return result, nil
+}
+
+// Readdirnames returns an array of up to count file names residing in the
+// directory associated with file. A negative count will return all of them.
+// Readdir returns the array and an Error, if any.
+func (file *File) Readdirnames(count int) (names []string, err Error) {
+ fi, e := file.Readdir(count)
+
+ if e != nil {
+ return []string{}, e
+ }
+
+ names = make([]string, len(fi))
+ err = nil
+
+ for i := range fi {
+ names[i] = fi[i].Name
+ }
+
+ return
+}
+
+type Dir struct {
+ // system-modified data
+ Type uint16 // server type
+ Dev uint32 // server subtype
+ // file data
+ Qid Qid // unique id from server
+ Mode uint32 // permissions
+ Atime uint32 // last read time
+ Mtime uint32 // last write time
+ Length uint64 // file length
+ Name string // last element of path
+ Uid string // owner name
+ Gid string // group name
+ Muid string // last modifier name
+}
+
+type Qid struct {
+ Path uint64 // the file server's unique identification for the file
+ Vers uint32 // version number for given Path
+ Type uint8 // the type of the file (syscall.QTDIR for example)
+}
+
+var nullDir = Dir{
+ ^uint16(0),
+ ^uint32(0),
+ Qid{^uint64(0), ^uint32(0), ^uint8(0)},
+ ^uint32(0),
+ ^uint32(0),
+ ^uint32(0),
+ ^uint64(0),
+ "",
+ "",
+ "",
+ "",
+}
+
+// Null assigns members of d with special "don't care" values indicating
+// they should not be written by syscall.Wstat.
+func (d *Dir) Null() {
+ *d = nullDir
+}
+
+// pdir appends a 9P Stat message based on the contents of Dir d to a byte slice b.
+func pdir(b []byte, d *Dir) []byte {
+ n := len(b)
+ b = pbit16(b, 0) // length, filled in later
+ b = pbit16(b, d.Type)
+ b = pbit32(b, d.Dev)
+ b = pqid(b, d.Qid)
+ b = pbit32(b, d.Mode)
+ b = pbit32(b, d.Atime)
+ b = pbit32(b, d.Mtime)
+ b = pbit64(b, d.Length)
+ b = pstring(b, d.Name)
+ b = pstring(b, d.Uid)
+ b = pstring(b, d.Gid)
+ b = pstring(b, d.Muid)
+ pbit16(b[0:n], uint16(len(b)-(n+2)))
+ return b
+}
+
+// UnmarshalDir reads a 9P Stat message from a 9P protocol message strored in b,
+// returning the corresponding Dir struct.
+func UnmarshalDir(b []byte) (d *Dir, err Error) {
+ n := uint16(0)
+ n, b = gbit16(b)
+
+ if int(n) != len(b) {
+ return nil, Ebadstat
+ }
+
+ d = new(Dir)
+ d.Type, b = gbit16(b)
+ d.Dev, b = gbit32(b)
+ d.Qid, b = gqid(b)
+ d.Mode, b = gbit32(b)
+ d.Atime, b = gbit32(b)
+ d.Mtime, b = gbit32(b)
+ d.Length, b = gbit64(b)
+ d.Name, b = gstring(b)
+ d.Uid, b = gstring(b)
+ d.Gid, b = gstring(b)
+ d.Muid, b = gstring(b)
+
+ if len(b) != 0 {
+ return nil, Ebadstat
+ }
+
+ return d, nil
+}
+
+// gqid reads the qid part of a 9P Stat message from a 9P protocol message strored in b,
+// returning the corresponding Qid struct and the remaining slice of b.
+func gqid(b []byte) (Qid, []byte) {
+ var q Qid
+ q.Path, b = gbit64(b)
+ q.Vers, b = gbit32(b)
+ q.Type, b = gbit8(b)
+ return q, b
+}
+
+// pqid appends a Qid struct q to a 9P message b.
+func pqid(b []byte, q Qid) []byte {
+ b = pbit64(b, q.Path)
+ b = pbit32(b, q.Vers)
+ b = pbit8(b, q.Type)
+ return b
+}
+
+// gbit8 reads a byte-sized numeric value from a 9P protocol message strored in b,
+// returning the value and the remaining slice of b.
+func gbit8(b []byte) (uint8, []byte) {
+ return uint8(b[0]), b[1:]
+}
+
+// gbit16 reads a 16-bit numeric value from a 9P protocol message strored in b,
+// returning the value and the remaining slice of b.
+func gbit16(b []byte) (uint16, []byte) {
+ return uint16(b[0]) | uint16(b[1])<<8, b[2:]
+}
+
+// gbit32 reads a 32-bit numeric value from a 9P protocol message strored in b,
+// returning the value and the remaining slice of b.
+func gbit32(b []byte) (uint32, []byte) {
+ return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24, b[4:]
+}
+
+// gbit64 reads a 64-bit numeric value from a 9P protocol message strored in b,
+// returning the value and the remaining slice of b.
+func gbit64(b []byte) (uint64, []byte) {
+ lo, b := gbit32(b)
+ hi, b := gbit32(b)
+ return uint64(hi)<<32 | uint64(lo), b
+}
+
+// gstring reads a string from a 9P protocol message strored in b,
+// returning the value as a Go string and the remaining slice of b.
+func gstring(b []byte) (string, []byte) {
+ n, b := gbit16(b)
+ return string(b[0:n]), b[n:]
+}
+
+// pbit8 appends a byte-sized numeric value x to a 9P message b.
+func pbit8(b []byte, x uint8) []byte {
+ n := len(b)
+ if n+1 > cap(b) {
+ nb := make([]byte, n, 100+2*cap(b))
+ copy(nb, b)
+ b = nb
+ }
+ b = b[0 : n+1]
+ b[n] = x
+ return b
+}
+
+// pbit16 appends a 16-bit numeric value x to a 9P message b.
+func pbit16(b []byte, x uint16) []byte {
+ n := len(b)
+ if n+2 > cap(b) {
+ nb := make([]byte, n, 100+2*cap(b))
+ copy(nb, b)
+ b = nb
+ }
+ b = b[0 : n+2]
+ b[n] = byte(x)
+ b[n+1] = byte(x >> 8)
+ return b
+}
+
+// pbit32 appends a 32-bit numeric value x to a 9P message b.
+func pbit32(b []byte, x uint32) []byte {
+ n := len(b)
+ if n+4 > cap(b) {
+ nb := make([]byte, n, 100+2*cap(b))
+ copy(nb, b)
+ b = nb
+ }
+ b = b[0 : n+4]
+ b[n] = byte(x)
+ b[n+1] = byte(x >> 8)
+ b[n+2] = byte(x >> 16)
+ b[n+3] = byte(x >> 24)
+ return b
+}
+
+// pbit64 appends a 64-bit numeric value x to a 9P message b.
+func pbit64(b []byte, x uint64) []byte {
+ b = pbit32(b, uint32(x))
+ b = pbit32(b, uint32(x>>32))
+ return b
+}
+
+// pstring appends a Go string s to a 9P message b.
+func pstring(b []byte, s string) []byte {
+ if len(s) >= 1<<16 {
+ panic(NewError("string too long"))
+ }
+ b = pbit16(b, uint16(len(s)))
+ b = append(b, []byte(s)...)
+ return b
+}
diff --git a/src/pkg/os/dir_darwin.go b/src/pkg/os/dir_unix.go
index 861bcef27..f5b82230d 100644
--- a/src/pkg/os/dir_darwin.go
+++ b/src/pkg/os/dir_unix.go
@@ -6,11 +6,10 @@ package os
import (
"syscall"
- "unsafe"
)
const (
- blockSize = 4096 // TODO(r): use statfs
+ blockSize = 4096
)
// Readdirnames reads the contents of the directory associated with file and
@@ -23,7 +22,6 @@ func (file *File) Readdirnames(count int) (names []string, err Error) {
if file.dirinfo == nil {
file.dirinfo = new(dirInfo)
// The buffer must be at least a block long.
- // TODO(r): use fstatfs to find fs block size.
file.dirinfo.buf = make([]byte, blockSize)
}
d := file.dirinfo
@@ -35,37 +33,22 @@ func (file *File) Readdirnames(count int) (names []string, err Error) {
for count != 0 {
// Refill the buffer if necessary
if d.bufp >= d.nbuf {
- var errno int
d.bufp = 0
- // Final argument is (basep *uintptr) and the syscall doesn't take nil.
- d.nbuf, errno = syscall.Getdirentries(file.fd, d.buf, new(uintptr))
+ var errno int
+ d.nbuf, errno = syscall.ReadDirent(file.fd, d.buf)
if errno != 0 {
- d.nbuf = 0
- return names, NewSyscallError("getdirentries", errno)
+ return names, NewSyscallError("readdirent", errno)
}
if d.nbuf <= 0 {
break // EOF
}
}
+
// Drain the buffer
- for count != 0 && d.bufp < d.nbuf {
- dirent := (*syscall.Dirent)(unsafe.Pointer(&d.buf[d.bufp]))
- if dirent.Reclen == 0 {
- d.bufp = d.nbuf
- break
- }
- d.bufp += int(dirent.Reclen)
- if dirent.Ino == 0 { // File absent in directory.
- continue
- }
- bytes := (*[10000]byte)(unsafe.Pointer(&dirent.Name[0]))
- var name = string(bytes[0:dirent.Namlen])
- if name == "." || name == ".." { // Useless names
- continue
- }
- count--
- names = append(names, name)
- }
+ var nb, nc int
+ nb, nc, names = syscall.ParseDirent(d.buf[d.bufp:d.nbuf], count, names)
+ d.bufp += nb
+ count -= nc
}
return names, nil
}
diff --git a/src/pkg/os/env_plan9.go b/src/pkg/os/env_plan9.go
new file mode 100644
index 000000000..14df55ed0
--- /dev/null
+++ b/src/pkg/os/env_plan9.go
@@ -0,0 +1,91 @@
+// 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.
+
+// Plan 9 environment variables.
+
+package os
+
+import "syscall"
+
+// ENOENV is the Error indicating that an environment variable does not exist.
+var ENOENV = NewError("no such environment variable")
+
+// Getenverror retrieves the value of the environment variable named by the key.
+// It returns the value and an error, if any.
+func Getenverror(key string) (value string, err Error) {
+ if len(key) == 0 {
+ return "", EINVAL
+ }
+ f, e := Open("/env/" + key)
+ if iserror(e) {
+ return "", ENOENV
+ }
+ defer f.Close()
+
+ var buf [4096]byte
+ n, e := f.Read(buf[:len(buf)-1])
+ if iserror(e) {
+ return "", ENOENV
+ }
+ buf[n] = 0
+ return string(buf[0:n]), nil
+}
+
+// Getenv retrieves the value of the environment variable named by the key.
+// It returns the value, which will be empty if the variable is not present.
+func Getenv(key string) string {
+ v, _ := Getenverror(key)
+ return v
+}
+
+// Setenv sets the value of the environment variable named by the key.
+// It returns an Error, if any.
+func Setenv(key, value string) Error {
+ if len(key) == 0 {
+ return EINVAL
+ }
+
+ f, e := Create("/env/" + key)
+ if iserror(e) {
+ return e
+ }
+ defer f.Close()
+
+ _, e = f.Write(syscall.StringByteSlice(value))
+ return nil
+}
+
+// Clearenv deletes all environment variables.
+func Clearenv() {
+ syscall.RawSyscall(syscall.SYS_RFORK, syscall.RFCENVG, 0, 0)
+}
+
+// Environ returns an array of strings representing the environment,
+// in the form "key=value".
+func Environ() []string {
+ env := make([]string, 0, 100)
+
+ f, e := Open("/env")
+ if iserror(e) {
+ panic(e)
+ }
+ defer f.Close()
+
+ names, e := f.Readdirnames(-1)
+ if iserror(e) {
+ panic(e)
+ }
+
+ for _, k := range names {
+ if v, e := Getenverror(k); !iserror(e) {
+ env = append(env, k+"="+v)
+ }
+ }
+ return env[0:len(env)]
+}
+
+// TempDir returns the default directory to use for temporary files.
+func TempDir() string {
+ return "/tmp"
+}
diff --git a/src/pkg/os/error.go b/src/pkg/os/error.go
index 635a3fe50..2c4516ca7 100644
--- a/src/pkg/os/error.go
+++ b/src/pkg/os/error.go
@@ -4,8 +4,6 @@
package os
-import syscall "syscall"
-
// An Error can represent any printable error condition.
type Error interface {
String() string
@@ -26,63 +24,6 @@ func (e ErrorString) Timeout() bool { return false }
// NewError converts s to an ErrorString, which satisfies the Error interface.
func NewError(s string) Error { return ErrorString(s) }
-// Errno is the Unix error number. Names such as EINVAL are simple
-// wrappers to convert the error number into an Error.
-type Errno int64
-
-func (e Errno) String() string { return syscall.Errstr(int(e)) }
-
-func (e Errno) Temporary() bool {
- return e == Errno(syscall.EINTR) || e.Timeout()
-}
-
-func (e Errno) Timeout() bool {
- return e == Errno(syscall.EAGAIN) || e == Errno(syscall.EWOULDBLOCK) || e == Errno(syscall.ETIMEDOUT)
-}
-
-// Commonly known Unix errors.
-var (
- EPERM Error = Errno(syscall.EPERM)
- ENOENT Error = Errno(syscall.ENOENT)
- ESRCH Error = Errno(syscall.ESRCH)
- EINTR Error = Errno(syscall.EINTR)
- EIO Error = Errno(syscall.EIO)
- ENXIO Error = Errno(syscall.ENXIO)
- E2BIG Error = Errno(syscall.E2BIG)
- ENOEXEC Error = Errno(syscall.ENOEXEC)
- EBADF Error = Errno(syscall.EBADF)
- ECHILD Error = Errno(syscall.ECHILD)
- EDEADLK Error = Errno(syscall.EDEADLK)
- ENOMEM Error = Errno(syscall.ENOMEM)
- EACCES Error = Errno(syscall.EACCES)
- EFAULT Error = Errno(syscall.EFAULT)
- EBUSY Error = Errno(syscall.EBUSY)
- EEXIST Error = Errno(syscall.EEXIST)
- EXDEV Error = Errno(syscall.EXDEV)
- ENODEV Error = Errno(syscall.ENODEV)
- ENOTDIR Error = Errno(syscall.ENOTDIR)
- EISDIR Error = Errno(syscall.EISDIR)
- EINVAL Error = Errno(syscall.EINVAL)
- ENFILE Error = Errno(syscall.ENFILE)
- EMFILE Error = Errno(syscall.EMFILE)
- ENOTTY Error = Errno(syscall.ENOTTY)
- EFBIG Error = Errno(syscall.EFBIG)
- ENOSPC Error = Errno(syscall.ENOSPC)
- ESPIPE Error = Errno(syscall.ESPIPE)
- EROFS Error = Errno(syscall.EROFS)
- EMLINK Error = Errno(syscall.EMLINK)
- EPIPE Error = Errno(syscall.EPIPE)
- EAGAIN Error = Errno(syscall.EAGAIN)
- EDOM Error = Errno(syscall.EDOM)
- ERANGE Error = Errno(syscall.ERANGE)
- EADDRINUSE Error = Errno(syscall.EADDRINUSE)
- ECONNREFUSED Error = Errno(syscall.ECONNREFUSED)
- ENAMETOOLONG Error = Errno(syscall.ENAMETOOLONG)
- EAFNOSUPPORT Error = Errno(syscall.EAFNOSUPPORT)
- ETIMEDOUT Error = Errno(syscall.ETIMEDOUT)
- ENOTCONN Error = Errno(syscall.ENOTCONN)
-)
-
// PathError records an error and the operation and file path that caused it.
type PathError struct {
Op string
@@ -91,25 +32,3 @@ type PathError struct {
}
func (e *PathError) String() string { return e.Op + " " + e.Path + ": " + e.Error.String() }
-
-// SyscallError records an error from a specific system call.
-type SyscallError struct {
- Syscall string
- Errno Errno
-}
-
-func (e *SyscallError) String() string { return e.Syscall + ": " + e.Errno.String() }
-
-// Note: If the name of the function NewSyscallError changes,
-// pkg/go/doc/doc.go should be adjusted since it hardwires
-// this name in a heuristic.
-
-// NewSyscallError returns, as an Error, a new SyscallError
-// with the given system call name and error number.
-// As a convenience, if errno is 0, NewSyscallError returns nil.
-func NewSyscallError(syscall string, errno int) Error {
- if errno == 0 {
- return nil
- }
- return &SyscallError{syscall, Errno(errno)}
-}
diff --git a/src/pkg/os/error_plan9.go b/src/pkg/os/error_plan9.go
new file mode 100644
index 000000000..3374775b8
--- /dev/null
+++ b/src/pkg/os/error_plan9.go
@@ -0,0 +1,60 @@
+// 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 os
+
+import syscall "syscall"
+
+// SyscallError records an error from a specific system call.
+type SyscallError struct {
+ Syscall string
+ Err string
+}
+
+func (e *SyscallError) String() string { return e.Syscall + ": " + e.Err }
+
+// Note: If the name of the function NewSyscallError changes,
+// pkg/go/doc/doc.go should be adjusted since it hardwires
+// this name in a heuristic.
+
+// NewSyscallError returns, as an Error, a new SyscallError
+// with the given system call name and error details.
+// As a convenience, if err is nil, NewSyscallError returns nil.
+func NewSyscallError(syscall string, err syscall.Error) Error {
+ if err == nil {
+ return nil
+ }
+ return &SyscallError{syscall, err.String()}
+}
+
+var (
+ Eshortstat = NewError("stat buffer too small")
+ Ebadstat = NewError("malformed stat buffer")
+ Ebadfd = NewError("fd out of range or not open")
+ Ebadarg = NewError("bad arg in system call")
+ Enotdir = NewError("not a directory")
+ Enonexist = NewError("file does not exist")
+ Eexist = NewError("file already exists")
+ Eio = NewError("i/o error")
+ Eperm = NewError("permission denied")
+
+ EINVAL = Ebadarg
+ ENOTDIR = Enotdir
+ ENOENT = Enonexist
+ EEXIST = Eexist
+ EIO = Eio
+ EACCES = Eperm
+ EISDIR = syscall.EISDIR
+
+ ENAMETOOLONG = NewError("file name too long")
+ ERANGE = NewError("math result not representable")
+ EPIPE = NewError("Broken Pipe")
+ EPLAN9 = NewError("not supported by plan 9")
+)
+
+func iserror(err syscall.Error) bool {
+ return err != nil
+}
+
+func Errno(e syscall.Error) syscall.Error { return e }
diff --git a/src/pkg/os/error_posix.go b/src/pkg/os/error_posix.go
new file mode 100644
index 000000000..0ee34e4b0
--- /dev/null
+++ b/src/pkg/os/error_posix.go
@@ -0,0 +1,90 @@
+// 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 os
+
+import syscall "syscall"
+
+// Errno is the Unix error number. Names such as EINVAL are simple
+// wrappers to convert the error number into an Error.
+type Errno int64
+
+func (e Errno) String() string { return syscall.Errstr(int(e)) }
+
+func (e Errno) Temporary() bool {
+ return e == Errno(syscall.EINTR) || e.Timeout()
+}
+
+func (e Errno) Timeout() bool {
+ return e == Errno(syscall.EAGAIN) || e == Errno(syscall.EWOULDBLOCK) || e == Errno(syscall.ETIMEDOUT)
+}
+
+// Commonly known Unix errors.
+var (
+ EPERM Error = Errno(syscall.EPERM)
+ ENOENT Error = Errno(syscall.ENOENT)
+ ESRCH Error = Errno(syscall.ESRCH)
+ EINTR Error = Errno(syscall.EINTR)
+ EIO Error = Errno(syscall.EIO)
+ ENXIO Error = Errno(syscall.ENXIO)
+ E2BIG Error = Errno(syscall.E2BIG)
+ ENOEXEC Error = Errno(syscall.ENOEXEC)
+ EBADF Error = Errno(syscall.EBADF)
+ ECHILD Error = Errno(syscall.ECHILD)
+ EDEADLK Error = Errno(syscall.EDEADLK)
+ ENOMEM Error = Errno(syscall.ENOMEM)
+ EACCES Error = Errno(syscall.EACCES)
+ EFAULT Error = Errno(syscall.EFAULT)
+ EBUSY Error = Errno(syscall.EBUSY)
+ EEXIST Error = Errno(syscall.EEXIST)
+ EXDEV Error = Errno(syscall.EXDEV)
+ ENODEV Error = Errno(syscall.ENODEV)
+ ENOTDIR Error = Errno(syscall.ENOTDIR)
+ EISDIR Error = Errno(syscall.EISDIR)
+ EINVAL Error = Errno(syscall.EINVAL)
+ ENFILE Error = Errno(syscall.ENFILE)
+ EMFILE Error = Errno(syscall.EMFILE)
+ ENOTTY Error = Errno(syscall.ENOTTY)
+ EFBIG Error = Errno(syscall.EFBIG)
+ ENOSPC Error = Errno(syscall.ENOSPC)
+ ESPIPE Error = Errno(syscall.ESPIPE)
+ EROFS Error = Errno(syscall.EROFS)
+ EMLINK Error = Errno(syscall.EMLINK)
+ EPIPE Error = Errno(syscall.EPIPE)
+ EAGAIN Error = Errno(syscall.EAGAIN)
+ EDOM Error = Errno(syscall.EDOM)
+ ERANGE Error = Errno(syscall.ERANGE)
+ EADDRINUSE Error = Errno(syscall.EADDRINUSE)
+ ECONNREFUSED Error = Errno(syscall.ECONNREFUSED)
+ ENAMETOOLONG Error = Errno(syscall.ENAMETOOLONG)
+ EAFNOSUPPORT Error = Errno(syscall.EAFNOSUPPORT)
+ ETIMEDOUT Error = Errno(syscall.ETIMEDOUT)
+ ENOTCONN Error = Errno(syscall.ENOTCONN)
+)
+
+// SyscallError records an error from a specific system call.
+type SyscallError struct {
+ Syscall string
+ Errno Errno
+}
+
+func (e *SyscallError) String() string { return e.Syscall + ": " + e.Errno.String() }
+
+// Note: If the name of the function NewSyscallError changes,
+// pkg/go/doc/doc.go should be adjusted since it hardwires
+// this name in a heuristic.
+
+// NewSyscallError returns, as an Error, a new SyscallError
+// with the given system call name and error details.
+// As a convenience, if errno is 0, NewSyscallError returns nil.
+func NewSyscallError(syscall string, errno int) Error {
+ if errno == 0 {
+ return nil
+ }
+ return &SyscallError{syscall, Errno(errno)}
+}
+
+func iserror(errno int) bool {
+ return errno != 0
+}
diff --git a/src/pkg/os/exec.go b/src/pkg/os/exec.go
index dbdfacc58..f62caf9a0 100644
--- a/src/pkg/os/exec.go
+++ b/src/pkg/os/exec.go
@@ -21,123 +21,22 @@ func newProcess(pid, handle int) *Process {
return p
}
-// StartProcess starts a new process with the program, arguments,
-// and environment specified by name, argv, and envv. The fd array specifies the
-// file descriptors to be set up in the new process: fd[0] will be Unix file
-// descriptor 0 (standard input), fd[1] descriptor 1, and so on. A nil entry
-// will cause the child to have no open file descriptor with that index.
-// If dir is not empty, the child chdirs into the directory before execing the program.
-func StartProcess(name string, argv []string, envv []string, dir string, fd []*File) (p *Process, err Error) {
- if envv == nil {
- envv = Environ()
- }
- // Create array of integer (system) fds.
- intfd := make([]int, len(fd))
- for i, f := range fd {
- if f == nil {
- intfd[i] = -1
- } else {
- intfd[i] = f.Fd()
- }
- }
-
- pid, h, e := syscall.StartProcess(name, argv, envv, dir, intfd)
- if e != 0 {
- return nil, &PathError{"fork/exec", name, Errno(e)}
- }
- return newProcess(pid, h), nil
-}
-
-// Exec replaces the current process with an execution of the
-// named binary, with arguments argv and environment envv.
-// If successful, Exec never returns. If it fails, it returns an Error.
-// StartProcess is almost always a better way to execute a program.
-func Exec(name string, argv []string, envv []string) Error {
- if envv == nil {
- envv = Environ()
- }
- e := syscall.Exec(name, argv, envv)
- if e != 0 {
- return &PathError{"exec", name, Errno(e)}
- }
- return nil
-}
-
-// TODO(rsc): Should os implement its own syscall.WaitStatus
-// wrapper with the methods, or is exposing the underlying one enough?
-//
-// TODO(rsc): Certainly need to have Rusage struct,
-// since syscall one might have different field types across
-// different OS.
-
-// Waitmsg stores the information about an exited process as reported by Wait.
-type Waitmsg struct {
- Pid int // The process's id.
- syscall.WaitStatus // System-dependent status info.
- Rusage *syscall.Rusage // System-dependent resource usage info.
-}
-
-// Wait waits for process pid to exit or stop, and then returns a
-// Waitmsg describing its status and an Error, if any. The options
-// (WNOHANG etc.) affect the behavior of the Wait call.
-// Wait is equivalent to calling FindProcess and then Wait
-// and Release on the result.
-func Wait(pid int, options int) (w *Waitmsg, err Error) {
- p, e := FindProcess(pid)
- if e != nil {
- return nil, e
- }
- defer p.Release()
- return p.Wait(options)
-}
-
-// Convert i to decimal string.
-func itod(i int) string {
- if i == 0 {
- return "0"
- }
-
- u := uint64(i)
- if i < 0 {
- u = -u
- }
-
- // Assemble decimal in reverse order.
- var b [32]byte
- bp := len(b)
- for ; u > 0; u /= 10 {
- bp--
- b[bp] = byte(u%10) + '0'
- }
-
- if i < 0 {
- bp--
- b[bp] = '-'
- }
-
- return string(b[bp:])
-}
-
-func (w Waitmsg) String() string {
- // TODO(austin) Use signal names when possible?
- res := ""
- switch {
- case w.Exited():
- res = "exit status " + itod(w.ExitStatus())
- case w.Signaled():
- res = "signal " + itod(w.Signal())
- case w.Stopped():
- res = "stop signal " + itod(w.StopSignal())
- if w.StopSignal() == syscall.SIGTRAP && w.TrapCause() != 0 {
- res += " (trap " + itod(w.TrapCause()) + ")"
- }
- case w.Continued():
- res = "continued"
- }
- if w.CoreDump() {
- res += " (core dumped)"
- }
- return res
+// ProcAttr holds the attributes that will be applied to a new process
+// started by StartProcess.
+type ProcAttr struct {
+ // If Dir is non-empty, the child changes into the directory before
+ // creating the process.
+ Dir string
+ // If Env is non-nil, it gives the environment variables for the
+ // new process in the form returned by Environ.
+ // If it is nil, the result of Environ will be used.
+ Env []string
+ // Files specifies the open files inherited by the new process. The
+ // first three entries correspond to standard input, standard output, and
+ // standard error. An implementation may support additional entries,
+ // depending on the underlying operating system. A nil entry corresponds
+ // to that file being closed when the process starts.
+ Files []*File
}
// Getpid returns the process id of the caller.
diff --git a/src/pkg/os/exec_plan9.go b/src/pkg/os/exec_plan9.go
new file mode 100644
index 000000000..11874aba6
--- /dev/null
+++ b/src/pkg/os/exec_plan9.go
@@ -0,0 +1,114 @@
+// 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 os
+
+import (
+ "runtime"
+ "syscall"
+)
+
+// StartProcess starts a new process with the program, arguments and attributes
+// specified by name, argv and attr.
+func StartProcess(name string, argv []string, attr *ProcAttr) (p *Process, err Error) {
+ sysattr := &syscall.ProcAttr{
+ Dir: attr.Dir,
+ Env: attr.Env,
+ }
+
+ // Create array of integer (system) fds.
+ intfd := make([]int, len(attr.Files))
+ for i, f := range attr.Files {
+ if f == nil {
+ intfd[i] = -1
+ } else {
+ intfd[i] = f.Fd()
+ }
+ }
+
+ sysattr.Files = intfd
+
+ pid, h, e := syscall.StartProcess(name, argv, sysattr)
+ if iserror(e) {
+ return nil, &PathError{"fork/exec", name, e}
+ }
+
+ return newProcess(pid, h), nil
+}
+
+// Exec replaces the current process with an execution of the
+// named binary, with arguments argv and environment envv.
+// If successful, Exec never returns. If it fails, it returns an Error.
+// ForkExec is almost always a better way to execute a program.
+func Exec(name string, argv []string, envv []string) Error {
+ e := syscall.Exec(name, argv, envv)
+ if iserror(e) {
+ return &PathError{"exec", name, e}
+ }
+
+ return nil
+}
+
+// Waitmsg stores the information about an exited process as reported by Wait.
+type Waitmsg syscall.Waitmsg
+
+// Wait waits for the Process to exit or stop, and then returns a
+// Waitmsg describing its status and an Error, if any. The options
+// (WNOHANG etc.) affect the behavior of the Wait call.
+func (p *Process) Wait(options int) (w *Waitmsg, err Error) {
+ var waitmsg syscall.Waitmsg
+
+ if p.Pid == -1 {
+ return nil, EINVAL
+ }
+
+ for true {
+ err = syscall.Await(&waitmsg)
+
+ if iserror(err) {
+ return nil, NewSyscallError("wait", err)
+ }
+
+ if waitmsg.Pid == p.Pid {
+ break
+ }
+ }
+
+ return (*Waitmsg)(&waitmsg), nil
+}
+
+// Wait waits for process pid to exit or stop, and then returns a
+// Waitmsg describing its status and an Error, if any. The options
+// (WNOHANG etc.) affect the behavior of the Wait call.
+// Wait is equivalent to calling FindProcess and then Wait
+// and Release on the result.
+func Wait(pid int, options int) (w *Waitmsg, err Error) {
+ p, e := FindProcess(pid)
+ if e != nil {
+ return nil, e
+ }
+ defer p.Release()
+ return p.Wait(options)
+}
+
+// Release releases any resources associated with the Process.
+func (p *Process) Release() Error {
+ // NOOP for Plan 9.
+ p.Pid = -1
+ // no need for a finalizer anymore
+ runtime.SetFinalizer(p, nil)
+ return nil
+}
+
+// FindProcess looks for a running process by its pid.
+// The Process it returns can be used to obtain information
+// about the underlying operating system process.
+func FindProcess(pid int) (p *Process, err Error) {
+ // NOOP for Plan 9.
+ return newProcess(pid, 0), nil
+}
+
+func (w Waitmsg) String() string {
+ return "exit status: " + w.Msg
+}
diff --git a/src/pkg/os/exec_posix.go b/src/pkg/os/exec_posix.go
new file mode 100644
index 000000000..9102dc0a4
--- /dev/null
+++ b/src/pkg/os/exec_posix.go
@@ -0,0 +1,127 @@
+// 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 os
+
+import "syscall"
+
+// StartProcess starts a new process with the program, arguments and attributes
+// specified by name, argv and attr.
+func StartProcess(name string, argv []string, attr *ProcAttr) (p *Process, err Error) {
+ sysattr := &syscall.ProcAttr{
+ Dir: attr.Dir,
+ Env: attr.Env,
+ }
+ if sysattr.Env == nil {
+ sysattr.Env = Environ()
+ }
+ // Create array of integer (system) fds.
+ intfd := make([]int, len(attr.Files))
+ for i, f := range attr.Files {
+ if f == nil {
+ intfd[i] = -1
+ } else {
+ intfd[i] = f.Fd()
+ }
+ }
+ sysattr.Files = intfd
+
+ pid, h, e := syscall.StartProcess(name, argv, sysattr)
+ if iserror(e) {
+ return nil, &PathError{"fork/exec", name, Errno(e)}
+ }
+ return newProcess(pid, h), nil
+}
+
+// Exec replaces the current process with an execution of the
+// named binary, with arguments argv and environment envv.
+// If successful, Exec never returns. If it fails, it returns an Error.
+// StartProcess is almost always a better way to execute a program.
+func Exec(name string, argv []string, envv []string) Error {
+ if envv == nil {
+ envv = Environ()
+ }
+ e := syscall.Exec(name, argv, envv)
+ if iserror(e) {
+ return &PathError{"exec", name, Errno(e)}
+ }
+ return nil
+}
+
+// TODO(rsc): Should os implement its own syscall.WaitStatus
+// wrapper with the methods, or is exposing the underlying one enough?
+//
+// TODO(rsc): Certainly need to have Rusage struct,
+// since syscall one might have different field types across
+// different OS.
+
+// Waitmsg stores the information about an exited process as reported by Wait.
+type Waitmsg struct {
+ Pid int // The process's id.
+ syscall.WaitStatus // System-dependent status info.
+ Rusage *syscall.Rusage // System-dependent resource usage info.
+}
+
+// Wait waits for process pid to exit or stop, and then returns a
+// Waitmsg describing its status and an Error, if any. The options
+// (WNOHANG etc.) affect the behavior of the Wait call.
+// Wait is equivalent to calling FindProcess and then Wait
+// and Release on the result.
+func Wait(pid int, options int) (w *Waitmsg, err Error) {
+ p, e := FindProcess(pid)
+ if e != nil {
+ return nil, e
+ }
+ defer p.Release()
+ return p.Wait(options)
+}
+
+// Convert i to decimal string.
+func itod(i int) string {
+ if i == 0 {
+ return "0"
+ }
+
+ u := uint64(i)
+ if i < 0 {
+ u = -u
+ }
+
+ // Assemble decimal in reverse order.
+ var b [32]byte
+ bp := len(b)
+ for ; u > 0; u /= 10 {
+ bp--
+ b[bp] = byte(u%10) + '0'
+ }
+
+ if i < 0 {
+ bp--
+ b[bp] = '-'
+ }
+
+ return string(b[bp:])
+}
+
+func (w Waitmsg) String() string {
+ // TODO(austin) Use signal names when possible?
+ res := ""
+ switch {
+ case w.Exited():
+ res = "exit status " + itod(w.ExitStatus())
+ case w.Signaled():
+ res = "signal " + itod(w.Signal())
+ case w.Stopped():
+ res = "stop signal " + itod(w.StopSignal())
+ if w.StopSignal() == syscall.SIGTRAP && w.TrapCause() != 0 {
+ res += " (trap " + itod(w.TrapCause()) + ")"
+ }
+ case w.Continued():
+ res = "continued"
+ }
+ if w.CoreDump() {
+ res += " (core dumped)"
+ }
+ return res
+}
diff --git a/src/pkg/os/file.go b/src/pkg/os/file.go
index 3f73f1dff..3aad80234 100644
--- a/src/pkg/os/file.go
+++ b/src/pkg/os/file.go
@@ -51,14 +51,20 @@ const (
O_RDWR int = syscall.O_RDWR // open the file read-write.
O_APPEND int = syscall.O_APPEND // append data to the file when writing.
O_ASYNC int = syscall.O_ASYNC // generate a signal when I/O is available.
- O_CREAT int = syscall.O_CREAT // create a new file if none exists.
- O_EXCL int = syscall.O_EXCL // used with O_CREAT, file must not exist
+ O_CREATE int = syscall.O_CREAT // create a new file if none exists.
+ O_EXCL int = syscall.O_EXCL // used with O_CREATE, file must not exist
O_NOCTTY int = syscall.O_NOCTTY // do not make file the controlling tty.
O_NONBLOCK int = syscall.O_NONBLOCK // open in non-blocking mode.
O_NDELAY int = O_NONBLOCK // synonym for O_NONBLOCK
O_SYNC int = syscall.O_SYNC // open for synchronous I/O.
O_TRUNC int = syscall.O_TRUNC // if possible, truncate file when opened.
- O_CREATE int = O_CREAT // create a new file if none exists.
+)
+
+// Seek whence values.
+const (
+ SEEK_SET int = 0 // seek relative to the origin of the file
+ SEEK_CUR int = 1 // seek relative to the current offset
+ SEEK_END int = 2 // seek relative to the end
)
type eofError int
@@ -83,10 +89,10 @@ func (file *File) Read(b []byte) (n int, err Error) {
if n < 0 {
n = 0
}
- if n == 0 && e == 0 {
+ if n == 0 && !iserror(e) {
return 0, EOF
}
- if e != 0 {
+ if iserror(e) {
err = &PathError{"read", file.name, Errno(e)}
}
return n, err
@@ -102,10 +108,10 @@ func (file *File) ReadAt(b []byte, off int64) (n int, err Error) {
}
for len(b) > 0 {
m, e := syscall.Pread(file.fd, b, off)
- if m == 0 && e == 0 {
+ if m == 0 && !iserror(e) {
return n, EOF
}
- if e != 0 {
+ if iserror(e) {
err = &PathError{"read", file.name, Errno(e)}
break
}
@@ -127,15 +133,10 @@ func (file *File) Write(b []byte) (n int, err Error) {
if n < 0 {
n = 0
}
- if e == syscall.EPIPE {
- file.nepipe++
- if file.nepipe >= 10 {
- Exit(syscall.EPIPE)
- }
- } else {
- file.nepipe = 0
- }
- if e != 0 {
+
+ epipecheck(file, e)
+
+ if iserror(e) {
err = &PathError{"write", file.name, Errno(e)}
}
return n, err
@@ -150,7 +151,7 @@ func (file *File) WriteAt(b []byte, off int64) (n int, err Error) {
}
for len(b) > 0 {
m, e := syscall.Pwrite(file.fd, b, off)
- if e != 0 {
+ if iserror(e) {
err = &PathError{"write", file.name, Errno(e)}
break
}
@@ -167,10 +168,10 @@ func (file *File) WriteAt(b []byte, off int64) (n int, err Error) {
// It returns the new offset and an Error, if any.
func (file *File) Seek(offset int64, whence int) (ret int64, err Error) {
r, e := syscall.Seek(file.fd, offset, whence)
- if e == 0 && file.dirinfo != nil && r != 0 {
+ if !iserror(e) && file.dirinfo != nil && r != 0 {
e = syscall.EISDIR
}
- if e != 0 {
+ if iserror(e) {
return 0, &PathError{"seek", file.name, Errno(e)}
}
return r, nil
@@ -187,71 +188,19 @@ func (file *File) WriteString(s string) (ret int, err Error) {
return file.Write(b)
}
-// Pipe returns a connected pair of Files; reads from r return bytes written to w.
-// It returns the files and an Error, if any.
-func Pipe() (r *File, w *File, err Error) {
- var p [2]int
-
- // See ../syscall/exec.go for description of lock.
- syscall.ForkLock.RLock()
- e := syscall.Pipe(p[0:])
- if e != 0 {
- syscall.ForkLock.RUnlock()
- return nil, nil, NewSyscallError("pipe", e)
- }
- syscall.CloseOnExec(p[0])
- syscall.CloseOnExec(p[1])
- syscall.ForkLock.RUnlock()
-
- return NewFile(p[0], "|0"), NewFile(p[1], "|1"), nil
-}
-
// Mkdir creates a new directory with the specified name and permission bits.
// It returns an error, if any.
func Mkdir(name string, perm uint32) Error {
e := syscall.Mkdir(name, perm)
- if e != 0 {
+ if iserror(e) {
return &PathError{"mkdir", name, Errno(e)}
}
return nil
}
-// Stat returns a FileInfo structure describing the named file and an error, if any.
-// If name names a valid symbolic link, the returned FileInfo describes
-// the file pointed at by the link and has fi.FollowedSymlink set to true.
-// If name names an invalid symbolic link, the returned FileInfo describes
-// the link itself and has fi.FollowedSymlink set to false.
-func Stat(name string) (fi *FileInfo, err Error) {
- var lstat, stat syscall.Stat_t
- e := syscall.Lstat(name, &lstat)
- if e != 0 {
- return nil, &PathError{"stat", name, Errno(e)}
- }
- statp := &lstat
- if lstat.Mode&syscall.S_IFMT == syscall.S_IFLNK {
- e := syscall.Stat(name, &stat)
- if e == 0 {
- statp = &stat
- }
- }
- return fileInfoFromStat(name, new(FileInfo), &lstat, statp), nil
-}
-
-// Lstat returns the FileInfo structure describing the named file and an
-// error, if any. If the file is a symbolic link, the returned FileInfo
-// describes the symbolic link. Lstat makes no attempt to follow the link.
-func Lstat(name string) (fi *FileInfo, err Error) {
- var stat syscall.Stat_t
- e := syscall.Lstat(name, &stat)
- if e != 0 {
- return nil, &PathError{"lstat", name, Errno(e)}
- }
- return fileInfoFromStat(name, new(FileInfo), &stat, &stat), nil
-}
-
// Chdir changes the current working directory to the named directory.
func Chdir(dir string) Error {
- if e := syscall.Chdir(dir); e != 0 {
+ if e := syscall.Chdir(dir); iserror(e) {
return &PathError{"chdir", dir, Errno(e)}
}
return nil
@@ -260,179 +209,25 @@ func Chdir(dir string) Error {
// Chdir changes the current working directory to the file,
// which must be a directory.
func (f *File) Chdir() Error {
- if e := syscall.Fchdir(f.fd); e != 0 {
+ if e := syscall.Fchdir(f.fd); iserror(e) {
return &PathError{"chdir", f.name, Errno(e)}
}
return nil
}
-// Remove removes the named file or directory.
-func Remove(name string) Error {
- // System call interface forces us to know
- // whether name is a file or directory.
- // Try both: it is cheaper on average than
- // doing a Stat plus the right one.
- e := syscall.Unlink(name)
- if e == 0 {
- return nil
- }
- e1 := syscall.Rmdir(name)
- if e1 == 0 {
- return nil
- }
-
- // Both failed: figure out which error to return.
- // OS X and Linux differ on whether unlink(dir)
- // returns EISDIR, so can't use that. However,
- // both agree that rmdir(file) returns ENOTDIR,
- // so we can use that to decide which error is real.
- // Rmdir might also return ENOTDIR if given a bad
- // file path, like /etc/passwd/foo, but in that case,
- // both errors will be ENOTDIR, so it's okay to
- // use the error from unlink.
- // For windows syscall.ENOTDIR is set
- // to syscall.ERROR_DIRECTORY, hopefully it should
- // do the trick.
- if e1 != syscall.ENOTDIR {
- e = e1
- }
- return &PathError{"remove", name, Errno(e)}
-}
-
-// LinkError records an error during a link or symlink or rename
-// system call and the paths that caused it.
-type LinkError struct {
- Op string
- Old string
- New string
- Error Error
-}
-
-func (e *LinkError) String() string {
- return e.Op + " " + e.Old + " " + e.New + ": " + e.Error.String()
+// Open opens the named file for reading. If successful, methods on
+// the returned file can be used for reading; the associated file
+// descriptor has mode O_RDONLY.
+// It returns the File and an Error, if any.
+func Open(name string) (file *File, err Error) {
+ return OpenFile(name, O_RDONLY, 0)
}
-// Link creates a hard link.
-func Link(oldname, newname string) Error {
- e := syscall.Link(oldname, newname)
- if e != 0 {
- return &LinkError{"link", oldname, newname, Errno(e)}
- }
- return nil
-}
-
-// Symlink creates a symbolic link.
-func Symlink(oldname, newname string) Error {
- e := syscall.Symlink(oldname, newname)
- if e != 0 {
- return &LinkError{"symlink", oldname, newname, Errno(e)}
- }
- return nil
-}
-
-// Readlink reads the contents of a symbolic link: the destination of
-// the link. It returns the contents and an Error, if any.
-func Readlink(name string) (string, Error) {
- for len := 128; ; len *= 2 {
- b := make([]byte, len)
- n, e := syscall.Readlink(name, b)
- if e != 0 {
- return "", &PathError{"readlink", name, Errno(e)}
- }
- if n < len {
- return string(b[0:n]), nil
- }
- }
- // Silence 6g.
- return "", nil
-}
-
-// Rename renames a file.
-func Rename(oldname, newname string) Error {
- e := syscall.Rename(oldname, newname)
- if e != 0 {
- return &LinkError{"rename", oldname, newname, Errno(e)}
- }
- return nil
-}
-
-// Chmod changes the mode of the named file to mode.
-// If the file is a symbolic link, it changes the mode of the link's target.
-func Chmod(name string, mode uint32) Error {
- if e := syscall.Chmod(name, mode); e != 0 {
- return &PathError{"chmod", name, Errno(e)}
- }
- return nil
-}
-
-// Chmod changes the mode of the file to mode.
-func (f *File) Chmod(mode uint32) Error {
- if e := syscall.Fchmod(f.fd, mode); e != 0 {
- return &PathError{"chmod", f.name, Errno(e)}
- }
- return nil
-}
-
-// Chown changes the numeric uid and gid of the named file.
-// If the file is a symbolic link, it changes the uid and gid of the link's target.
-func Chown(name string, uid, gid int) Error {
- if e := syscall.Chown(name, uid, gid); e != 0 {
- return &PathError{"chown", name, Errno(e)}
- }
- return nil
-}
-
-// Lchown changes the numeric uid and gid of the named file.
-// If the file is a symbolic link, it changes the uid and gid of the link itself.
-func Lchown(name string, uid, gid int) Error {
- if e := syscall.Lchown(name, uid, gid); e != 0 {
- return &PathError{"lchown", name, Errno(e)}
- }
- return nil
-}
-
-// Chown changes the numeric uid and gid of the named file.
-func (f *File) Chown(uid, gid int) Error {
- if e := syscall.Fchown(f.fd, uid, gid); e != 0 {
- return &PathError{"chown", f.name, Errno(e)}
- }
- return nil
-}
-
-// Truncate changes the size of the file.
-// It does not change the I/O offset.
-func (f *File) Truncate(size int64) Error {
- if e := syscall.Ftruncate(f.fd, size); e != 0 {
- return &PathError{"truncate", f.name, Errno(e)}
- }
- return nil
-}
-
-// Sync commits the current contents of the file to stable storage.
-// Typically, this means flushing the file system's in-memory copy
-// of recently written data to disk.
-func (file *File) Sync() (err Error) {
- if file == nil {
- return EINVAL
- }
- if e := syscall.Fsync(file.fd); e != 0 {
- return NewSyscallError("fsync", e)
- }
- return nil
-}
-
-// Chtimes changes the access and modification times of the named
-// file, similar to the Unix utime() or utimes() functions.
-//
-// The argument times are in nanoseconds, although the underlying
-// filesystem may truncate or round the values to a more
-// coarse time unit.
-func Chtimes(name string, atime_ns int64, mtime_ns int64) Error {
- var utimes [2]syscall.Timeval
- utimes[0] = syscall.NsecToTimeval(atime_ns)
- utimes[1] = syscall.NsecToTimeval(mtime_ns)
- if e := syscall.Utimes(name, utimes[0:]); e != 0 {
- return &PathError{"chtimes", name, Errno(e)}
- }
- return nil
+// Create creates the named file mode 0666 (before umask), truncating
+// it if it already exists. If successful, methods on the returned
+// File can be used for I/O; the associated file descriptor has mode
+// O_RDWR.
+// It returns the File and an Error, if any.
+func Create(name string) (file *File, err Error) {
+ return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
}
diff --git a/src/pkg/os/file_plan9.go b/src/pkg/os/file_plan9.go
new file mode 100644
index 000000000..c8d0efba4
--- /dev/null
+++ b/src/pkg/os/file_plan9.go
@@ -0,0 +1,240 @@
+// 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 os
+
+import (
+ "runtime"
+ "syscall"
+)
+
+// Auxiliary information if the File describes a directory
+type dirInfo struct {
+ buf [syscall.STATMAX]byte // buffer for directory I/O
+ nbuf int // length of buf; return value from Read
+ bufp int // location of next record in buf.
+}
+
+func epipecheck(file *File, e syscall.Error) {
+}
+
+
+// DevNull is the name of the operating system's ``null device.''
+// On Unix-like systems, it is "/dev/null"; on Windows, "NUL".
+const DevNull = "/dev/null"
+
+// OpenFile is the generalized open call; most users will use Open
+// or Create instead. It opens the named file with specified flag
+// (O_RDONLY etc.) and perm, (0666 etc.) if applicable. If successful,
+// methods on the returned File can be used for I/O.
+// It returns the File and an Error, if any.
+func OpenFile(name string, flag int, perm uint32) (file *File, err Error) {
+ var fd int
+ var e syscall.Error
+
+ syscall.ForkLock.RLock()
+ if flag&O_CREATE == O_CREATE {
+ fd, e = syscall.Create(name, flag & ^O_CREATE, perm)
+ } else {
+ fd, e = syscall.Open(name, flag)
+ }
+ syscall.ForkLock.RUnlock()
+
+ if e != nil {
+ return nil, &PathError{"open", name, e}
+ }
+
+ return NewFile(fd, name), nil
+}
+
+// Close closes the File, rendering it unusable for I/O.
+// It returns an Error, if any.
+func (file *File) Close() Error {
+ if file == nil || file.fd < 0 {
+ return Ebadfd
+ }
+ var err Error
+ syscall.ForkLock.RLock()
+ if e := syscall.Close(file.fd); e != nil {
+ err = &PathError{"close", file.name, e}
+ }
+ syscall.ForkLock.RUnlock()
+ file.fd = -1 // so it can't be closed again
+
+ // no need for a finalizer anymore
+ runtime.SetFinalizer(file, nil)
+ return err
+}
+
+// Stat returns the FileInfo structure describing file.
+// It returns the FileInfo and an error, if any.
+func (file *File) Stat() (fi *FileInfo, err Error) {
+ return dirstat(file)
+}
+
+// Truncate changes the size of the file.
+// It does not change the I/O offset.
+func (f *File) Truncate(size int64) Error {
+ var d Dir
+ d.Null()
+
+ d.Length = uint64(size)
+
+ if e := syscall.Fwstat(f.fd, pdir(nil, &d)); iserror(e) {
+ return &PathError{"truncate", f.name, e}
+ }
+ return nil
+}
+
+// Chmod changes the mode of the file to mode.
+func (f *File) Chmod(mode uint32) Error {
+ var d Dir
+ d.Null()
+
+ d.Mode = mode & 0777
+
+ if e := syscall.Fwstat(f.fd, pdir(nil, &d)); iserror(e) {
+ return &PathError{"chmod", f.name, e}
+ }
+ return nil
+}
+
+// Sync commits the current contents of the file to stable storage.
+// Typically, this means flushing the file system's in-memory copy
+// of recently written data to disk.
+func (f *File) Sync() (err Error) {
+ if f == nil {
+ return EINVAL
+ }
+
+ var d Dir
+ d.Null()
+
+ if e := syscall.Fwstat(f.fd, pdir(nil, &d)); iserror(e) {
+ return NewSyscallError("fsync", e)
+ }
+ return nil
+}
+
+// Truncate changes the size of the named file.
+// If the file is a symbolic link, it changes the size of the link's target.
+func Truncate(name string, size int64) Error {
+ var d Dir
+ d.Null()
+
+ d.Length = uint64(size)
+
+ if e := syscall.Wstat(name, pdir(nil, &d)); iserror(e) {
+ return &PathError{"truncate", name, e}
+ }
+ return nil
+}
+
+// Remove removes the named file or directory.
+func Remove(name string) Error {
+ if e := syscall.Remove(name); iserror(e) {
+ return &PathError{"remove", name, e}
+ }
+ return nil
+}
+
+// Rename renames a file.
+func Rename(oldname, newname string) Error {
+ var d Dir
+ d.Null()
+
+ d.Name = newname
+
+ if e := syscall.Wstat(oldname, pdir(nil, &d)); iserror(e) {
+ return &PathError{"rename", oldname, e}
+ }
+ return nil
+}
+
+// Chmod changes the mode of the named file to mode.
+func Chmod(name string, mode uint32) Error {
+ var d Dir
+ d.Null()
+
+ d.Mode = mode & 0777
+
+ if e := syscall.Wstat(name, pdir(nil, &d)); iserror(e) {
+ return &PathError{"chmod", name, e}
+ }
+ return nil
+}
+
+// ChownPlan9 changes the uid and gid strings of the named file.
+func ChownPlan9(name, uid, gid string) Error {
+ var d Dir
+ d.Null()
+
+ d.Uid = uid
+ d.Gid = gid
+
+ if e := syscall.Wstat(name, pdir(nil, &d)); iserror(e) {
+ return &PathError{"chown_plan9", name, e}
+ }
+ return nil
+}
+
+// Chtimes changes the access and modification times of the named
+// file, similar to the Unix utime() or utimes() functions.
+//
+// The argument times are in nanoseconds, although the underlying
+// filesystem may truncate or round the values to a more
+// coarse time unit.
+func Chtimes(name string, atimeNs int64, mtimeNs int64) Error {
+ var d Dir
+ d.Null()
+
+ d.Atime = uint32(atimeNs / 1e9)
+ d.Mtime = uint32(mtimeNs / 1e9)
+
+ if e := syscall.Wstat(name, pdir(nil, &d)); iserror(e) {
+ return &PathError{"chtimes", name, e}
+ }
+ return nil
+}
+
+func Pipe() (r *File, w *File, err Error) {
+ var p [2]int
+
+ syscall.ForkLock.RLock()
+ if e := syscall.Pipe(p[0:]); iserror(e) {
+ syscall.ForkLock.RUnlock()
+ return nil, nil, NewSyscallError("pipe", e)
+ }
+ syscall.ForkLock.RUnlock()
+
+ return NewFile(p[0], "|0"), NewFile(p[1], "|1"), nil
+}
+
+
+// not supported on Plan 9
+
+// Link creates a hard link.
+func Link(oldname, newname string) Error {
+ return EPLAN9
+}
+
+func Symlink(oldname, newname string) Error {
+ return EPLAN9
+}
+
+func Readlink(name string) (string, Error) {
+ return "", EPLAN9
+}
+
+func Chown(name string, uid, gid int) Error {
+ return EPLAN9
+}
+
+func Lchown(name string, uid, gid int) Error {
+ return EPLAN9
+}
+
+func (f *File) Chown(uid, gid int) Error {
+ return EPLAN9
+}
diff --git a/src/pkg/os/file_posix.go b/src/pkg/os/file_posix.go
new file mode 100644
index 000000000..5151df498
--- /dev/null
+++ b/src/pkg/os/file_posix.go
@@ -0,0 +1,246 @@
+// 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 os package provides a platform-independent interface to operating
+// system functionality. The design is Unix-like.
+package os
+
+import (
+ "syscall"
+)
+
+func epipecheck(file *File, e int) {
+ if e == syscall.EPIPE {
+ file.nepipe++
+ if file.nepipe >= 10 {
+ Exit(syscall.EPIPE)
+ }
+ } else {
+ file.nepipe = 0
+ }
+}
+
+
+// Pipe returns a connected pair of Files; reads from r return bytes written to w.
+// It returns the files and an Error, if any.
+func Pipe() (r *File, w *File, err Error) {
+ var p [2]int
+
+ // See ../syscall/exec.go for description of lock.
+ syscall.ForkLock.RLock()
+ e := syscall.Pipe(p[0:])
+ if iserror(e) {
+ syscall.ForkLock.RUnlock()
+ return nil, nil, NewSyscallError("pipe", e)
+ }
+ syscall.CloseOnExec(p[0])
+ syscall.CloseOnExec(p[1])
+ syscall.ForkLock.RUnlock()
+
+ return NewFile(p[0], "|0"), NewFile(p[1], "|1"), nil
+}
+
+// Stat returns a FileInfo structure describing the named file and an error, if any.
+// If name names a valid symbolic link, the returned FileInfo describes
+// the file pointed at by the link and has fi.FollowedSymlink set to true.
+// If name names an invalid symbolic link, the returned FileInfo describes
+// the link itself and has fi.FollowedSymlink set to false.
+func Stat(name string) (fi *FileInfo, err Error) {
+ var lstat, stat syscall.Stat_t
+ e := syscall.Lstat(name, &lstat)
+ if iserror(e) {
+ return nil, &PathError{"stat", name, Errno(e)}
+ }
+ statp := &lstat
+ if lstat.Mode&syscall.S_IFMT == syscall.S_IFLNK {
+ e := syscall.Stat(name, &stat)
+ if !iserror(e) {
+ statp = &stat
+ }
+ }
+ return fileInfoFromStat(name, new(FileInfo), &lstat, statp), nil
+}
+
+// Lstat returns the FileInfo structure describing the named file and an
+// error, if any. If the file is a symbolic link, the returned FileInfo
+// describes the symbolic link. Lstat makes no attempt to follow the link.
+func Lstat(name string) (fi *FileInfo, err Error) {
+ var stat syscall.Stat_t
+ e := syscall.Lstat(name, &stat)
+ if iserror(e) {
+ return nil, &PathError{"lstat", name, Errno(e)}
+ }
+ return fileInfoFromStat(name, new(FileInfo), &stat, &stat), nil
+}
+
+// Remove removes the named file or directory.
+func Remove(name string) Error {
+ // System call interface forces us to know
+ // whether name is a file or directory.
+ // Try both: it is cheaper on average than
+ // doing a Stat plus the right one.
+ e := syscall.Unlink(name)
+ if !iserror(e) {
+ return nil
+ }
+ e1 := syscall.Rmdir(name)
+ if !iserror(e1) {
+ return nil
+ }
+
+ // Both failed: figure out which error to return.
+ // OS X and Linux differ on whether unlink(dir)
+ // returns EISDIR, so can't use that. However,
+ // both agree that rmdir(file) returns ENOTDIR,
+ // so we can use that to decide which error is real.
+ // Rmdir might also return ENOTDIR if given a bad
+ // file path, like /etc/passwd/foo, but in that case,
+ // both errors will be ENOTDIR, so it's okay to
+ // use the error from unlink.
+ // For windows syscall.ENOTDIR is set
+ // to syscall.ERROR_DIRECTORY, hopefully it should
+ // do the trick.
+ if e1 != syscall.ENOTDIR {
+ e = e1
+ }
+ return &PathError{"remove", name, Errno(e)}
+}
+
+// LinkError records an error during a link or symlink or rename
+// system call and the paths that caused it.
+type LinkError struct {
+ Op string
+ Old string
+ New string
+ Error Error
+}
+
+func (e *LinkError) String() string {
+ return e.Op + " " + e.Old + " " + e.New + ": " + e.Error.String()
+}
+
+// Link creates a hard link.
+func Link(oldname, newname string) Error {
+ e := syscall.Link(oldname, newname)
+ if iserror(e) {
+ return &LinkError{"link", oldname, newname, Errno(e)}
+ }
+ return nil
+}
+
+// Symlink creates a symbolic link.
+func Symlink(oldname, newname string) Error {
+ e := syscall.Symlink(oldname, newname)
+ if iserror(e) {
+ return &LinkError{"symlink", oldname, newname, Errno(e)}
+ }
+ return nil
+}
+
+// Readlink reads the contents of a symbolic link: the destination of
+// the link. It returns the contents and an Error, if any.
+func Readlink(name string) (string, Error) {
+ for len := 128; ; len *= 2 {
+ b := make([]byte, len)
+ n, e := syscall.Readlink(name, b)
+ if iserror(e) {
+ return "", &PathError{"readlink", name, Errno(e)}
+ }
+ if n < len {
+ return string(b[0:n]), nil
+ }
+ }
+ // Silence 6g.
+ return "", nil
+}
+
+// Rename renames a file.
+func Rename(oldname, newname string) Error {
+ e := syscall.Rename(oldname, newname)
+ if iserror(e) {
+ return &LinkError{"rename", oldname, newname, Errno(e)}
+ }
+ return nil
+}
+
+// Chmod changes the mode of the named file to mode.
+// If the file is a symbolic link, it changes the mode of the link's target.
+func Chmod(name string, mode uint32) Error {
+ if e := syscall.Chmod(name, mode); iserror(e) {
+ return &PathError{"chmod", name, Errno(e)}
+ }
+ return nil
+}
+
+// Chmod changes the mode of the file to mode.
+func (f *File) Chmod(mode uint32) Error {
+ if e := syscall.Fchmod(f.fd, mode); iserror(e) {
+ return &PathError{"chmod", f.name, Errno(e)}
+ }
+ return nil
+}
+
+// Chown changes the numeric uid and gid of the named file.
+// If the file is a symbolic link, it changes the uid and gid of the link's target.
+func Chown(name string, uid, gid int) Error {
+ if e := syscall.Chown(name, uid, gid); iserror(e) {
+ return &PathError{"chown", name, Errno(e)}
+ }
+ return nil
+}
+
+// Lchown changes the numeric uid and gid of the named file.
+// If the file is a symbolic link, it changes the uid and gid of the link itself.
+func Lchown(name string, uid, gid int) Error {
+ if e := syscall.Lchown(name, uid, gid); iserror(e) {
+ return &PathError{"lchown", name, Errno(e)}
+ }
+ return nil
+}
+
+// Chown changes the numeric uid and gid of the named file.
+func (f *File) Chown(uid, gid int) Error {
+ if e := syscall.Fchown(f.fd, uid, gid); iserror(e) {
+ return &PathError{"chown", f.name, Errno(e)}
+ }
+ return nil
+}
+
+// Truncate changes the size of the file.
+// It does not change the I/O offset.
+func (f *File) Truncate(size int64) Error {
+ if e := syscall.Ftruncate(f.fd, size); iserror(e) {
+ return &PathError{"truncate", f.name, Errno(e)}
+ }
+ return nil
+}
+
+// Sync commits the current contents of the file to stable storage.
+// Typically, this means flushing the file system's in-memory copy
+// of recently written data to disk.
+func (file *File) Sync() (err Error) {
+ if file == nil {
+ return EINVAL
+ }
+ if e := syscall.Fsync(file.fd); iserror(e) {
+ return NewSyscallError("fsync", e)
+ }
+ return nil
+}
+
+// Chtimes changes the access and modification times of the named
+// file, similar to the Unix utime() or utimes() functions.
+//
+// The argument times are in nanoseconds, although the underlying
+// filesystem may truncate or round the values to a more
+// coarse time unit.
+func Chtimes(name string, atime_ns int64, mtime_ns int64) Error {
+ var utimes [2]syscall.Timeval
+ utimes[0] = syscall.NsecToTimeval(atime_ns)
+ utimes[1] = syscall.NsecToTimeval(mtime_ns)
+ if e := syscall.Utimes(name, utimes[0:]); iserror(e) {
+ return &PathError{"chtimes", name, Errno(e)}
+ }
+ return nil
+}
diff --git a/src/pkg/os/file_unix.go b/src/pkg/os/file_unix.go
index df5894459..f2b94f4c2 100644
--- a/src/pkg/os/file_unix.go
+++ b/src/pkg/os/file_unix.go
@@ -20,10 +20,12 @@ type dirInfo struct {
// On Unix-like systems, it is "/dev/null"; on Windows, "NUL".
const DevNull = "/dev/null"
-// Open opens the named file with specified flag (O_RDONLY etc.) and perm, (0666 etc.)
-// if applicable. If successful, methods on the returned File can be used for I/O.
+// OpenFile is the generalized open call; most users will use Open
+// or Create instead. It opens the named file with specified flag
+// (O_RDONLY etc.) and perm, (0666 etc.) if applicable. If successful,
+// methods on the returned File can be used for I/O.
// It returns the File and an Error, if any.
-func Open(name string, flag int, perm uint32) (file *File, err Error) {
+func OpenFile(name string, flag int, perm uint32) (file *File, err Error) {
r, e := syscall.Open(name, flag|syscall.O_CLOEXEC, perm)
if e != 0 {
return nil, &PathError{"open", name, Errno(e)}
@@ -102,3 +104,21 @@ func Truncate(name string, size int64) Error {
}
return nil
}
+
+// basename removes trailing slashes and the leading directory name from path name
+func basename(name string) string {
+ i := len(name) - 1
+ // Remove trailing slashes
+ for ; i > 0 && name[i] == '/'; i-- {
+ name = name[:i]
+ }
+ // Remove leading directory name
+ for i--; i >= 0; i-- {
+ if name[i] == '/' {
+ name = name[i+1:]
+ break
+ }
+ }
+
+ return name
+}
diff --git a/src/pkg/os/file_windows.go b/src/pkg/os/file_windows.go
index d14c38e17..862baf6b9 100644
--- a/src/pkg/os/file_windows.go
+++ b/src/pkg/os/file_windows.go
@@ -46,10 +46,12 @@ func openDir(name string) (file *File, err Error) {
return f, nil
}
-// Open opens the named file with specified flag (O_RDONLY etc.) and perm, (0666 etc.)
-// if applicable. If successful, methods on the returned File can be used for I/O.
+// OpenFile is the generalized open call; most users will use Open
+// or Create instead. It opens the named file with specified flag
+// (O_RDONLY etc.) and perm, (0666 etc.) if applicable. If successful,
+// methods on the returned File can be used for I/O.
// It returns the File and an Error, if any.
-func Open(name string, flag int, perm uint32) (file *File, err Error) {
+func OpenFile(name string, flag int, perm uint32) (file *File, err Error) {
// TODO(brainman): not sure about my logic of assuming it is dir first, then fall back to file
r, e := openDir(name)
if e == nil {
@@ -166,7 +168,7 @@ func (file *File) Readdir(count int) (fi []FileInfo, err Error) {
// Truncate changes the size of the named file.
// If the file is a symbolic link, it changes the size of the link's target.
func Truncate(name string, size int64) Error {
- f, e := Open(name, O_WRONLY|O_CREAT, 0666)
+ f, e := OpenFile(name, O_WRONLY|O_CREATE, 0666)
if e != nil {
return e
}
diff --git a/src/pkg/os/getwd.go b/src/pkg/os/getwd.go
index 49aaea865..4c142ad3a 100644
--- a/src/pkg/os/getwd.go
+++ b/src/pkg/os/getwd.go
@@ -54,7 +54,7 @@ func Getwd() (string, Error) {
if len(parent) >= 1024 { // Sanity check
return "", ENAMETOOLONG
}
- fd, err := Open(parent, O_RDONLY, 0)
+ fd, err := Open(parent)
if err != nil {
return "", err
}
diff --git a/src/pkg/os/inotify/inotify_linux.go b/src/pkg/os/inotify/inotify_linux.go
index 96c229e7b..8b5c30e0d 100644
--- a/src/pkg/os/inotify/inotify_linux.go
+++ b/src/pkg/os/inotify/inotify_linux.go
@@ -109,7 +109,7 @@ func (w *Watcher) AddWatch(path string, flags uint32) os.Error {
}
wd, errno := syscall.InotifyAddWatch(w.fd, path, flags)
if wd == -1 {
- return os.NewSyscallError("inotify_add_watch", errno)
+ return &os.PathError{"inotify_add_watch", path, os.Errno(errno)}
}
if !found {
diff --git a/src/pkg/os/inotify/inotify_linux_test.go b/src/pkg/os/inotify/inotify_linux_test.go
index 332edcb64..e29a46d6c 100644
--- a/src/pkg/os/inotify/inotify_linux_test.go
+++ b/src/pkg/os/inotify/inotify_linux_test.go
@@ -17,8 +17,8 @@ func TestInotifyEvents(t *testing.T) {
t.Fatalf("NewWatcher() failed: %s", err)
}
- // Add a watch for "_obj"
- err = watcher.Watch("_obj")
+ // Add a watch for "_test"
+ err = watcher.Watch("_test")
if err != nil {
t.Fatalf("Watcher.Watch() failed: %s", err)
}
@@ -30,11 +30,12 @@ func TestInotifyEvents(t *testing.T) {
}
}()
- const testFile string = "_obj/TestInotifyEvents.testfile"
+ const testFile string = "_test/TestInotifyEvents.testfile"
// Receive events on the event channel on a separate goroutine
eventstream := watcher.Event
var eventsReceived = 0
+ done := make(chan bool)
go func() {
for event := range eventstream {
// Only count relevant events
@@ -45,11 +46,12 @@ func TestInotifyEvents(t *testing.T) {
t.Logf("unexpected event received: %s", event)
}
}
+ done <- true
}()
// Create a file
// This should add at least one event to the inotify event queue
- _, err = os.Open(testFile, os.O_WRONLY|os.O_CREAT, 0666)
+ _, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
t.Fatalf("creating test file failed: %s", err)
}
@@ -64,16 +66,12 @@ func TestInotifyEvents(t *testing.T) {
t.Log("calling Close()")
watcher.Close()
t.Log("waiting for the event channel to become closed...")
- var i = 0
- for !closed(eventstream) {
- if i >= 20 {
- t.Fatal("event stream was not closed after 1 second, as expected")
- }
- t.Log("waiting for 50 ms...")
- time.Sleep(50e6) // 50 ms
- i++
+ select {
+ case <-done:
+ t.Log("event channel closed")
+ case <-time.After(1e9):
+ t.Fatal("event stream was not closed after 1 second")
}
- t.Log("event channel closed")
}
@@ -92,7 +90,7 @@ func TestInotifyClose(t *testing.T) {
t.Fatal("double Close() test failed: second Close() call didn't return")
}
- err := watcher.Watch("_obj")
+ err := watcher.Watch("_test")
if err == nil {
t.Fatal("expected error on Watch() after Close(), got nil")
}
diff --git a/src/pkg/os/os_test.go b/src/pkg/os/os_test.go
index 2ea8acdc4..551b86508 100644
--- a/src/pkg/os/os_test.go
+++ b/src/pkg/os/os_test.go
@@ -10,14 +10,14 @@ import (
"io"
"io/ioutil"
. "os"
+ "path/filepath"
"strings"
"syscall"
"testing"
)
var dot = []string{
- "dir_darwin.go",
- "dir_linux.go",
+ "dir_unix.go",
"env_unix.go",
"error.go",
"file.go",
@@ -45,6 +45,14 @@ var sysdir = func() (sd *sysDir) {
"services",
},
}
+ case "plan9":
+ sd = &sysDir{
+ "/lib/ndb",
+ []string{
+ "common",
+ "local",
+ },
+ }
default:
sd = &sysDir{
"/etc",
@@ -59,7 +67,7 @@ var sysdir = func() (sd *sysDir) {
}()
func size(name string, t *testing.T) int64 {
- file, err := Open(name, O_RDONLY, 0)
+ file, err := Open(name)
defer file.Close()
if err != nil {
t.Fatal("open failed:", err)
@@ -124,7 +132,7 @@ func TestStat(t *testing.T) {
func TestFstat(t *testing.T) {
path := sfdir + "/" + sfname
- file, err1 := Open(path, O_RDONLY, 0)
+ file, err1 := Open(path)
defer file.Close()
if err1 != nil {
t.Fatal("open failed:", err1)
@@ -158,7 +166,7 @@ func TestLstat(t *testing.T) {
}
func testReaddirnames(dir string, contents []string, t *testing.T) {
- file, err := Open(dir, O_RDONLY, 0)
+ file, err := Open(dir)
defer file.Close()
if err != nil {
t.Fatalf("open %q failed: %v", dir, err)
@@ -187,7 +195,7 @@ func testReaddirnames(dir string, contents []string, t *testing.T) {
}
func testReaddir(dir string, contents []string, t *testing.T) {
- file, err := Open(dir, O_RDONLY, 0)
+ file, err := Open(dir)
defer file.Close()
if err != nil {
t.Fatalf("open %q failed: %v", dir, err)
@@ -245,10 +253,13 @@ func smallReaddirnames(file *File, length int, t *testing.T) []string {
func TestReaddirnamesOneAtATime(t *testing.T) {
// big directory that doesn't change often.
dir := "/usr/bin"
- if syscall.OS == "windows" {
+ switch syscall.OS {
+ case "windows":
dir = Getenv("SystemRoot") + "\\system32"
+ case "plan9":
+ dir = "/bin"
}
- file, err := Open(dir, O_RDONLY, 0)
+ file, err := Open(dir)
defer file.Close()
if err != nil {
t.Fatalf("open %q failed: %v", dir, err)
@@ -257,11 +268,14 @@ func TestReaddirnamesOneAtATime(t *testing.T) {
if err1 != nil {
t.Fatalf("readdirnames %q failed: %v", dir, err1)
}
- file1, err2 := Open(dir, O_RDONLY, 0)
+ file1, err2 := Open(dir)
if err2 != nil {
t.Fatalf("open %q failed: %v", dir, err2)
}
small := smallReaddirnames(file1, len(all)+100, t) // +100 in case we screw up
+ if len(small) < len(all) {
+ t.Fatalf("len(small) is %d, less than %d", len(small), len(all))
+ }
for i, n := range all {
if small[i] != n {
t.Errorf("small read %q mismatch: %v", small[i], n)
@@ -276,7 +290,7 @@ func TestHardLink(t *testing.T) {
}
from, to := "hardlinktestfrom", "hardlinktestto"
Remove(from) // Just in case.
- file, err := Open(to, O_CREAT|O_WRONLY, 0666)
+ file, err := Create(to)
if err != nil {
t.Fatalf("open %q failed: %v", to, err)
}
@@ -309,7 +323,7 @@ func TestSymLink(t *testing.T) {
}
from, to := "symlinktestfrom", "symlinktestto"
Remove(from) // Just in case.
- file, err := Open(to, O_CREAT|O_WRONLY, 0666)
+ file, err := Create(to)
if err != nil {
t.Fatalf("open %q failed: %v", to, err)
}
@@ -357,7 +371,7 @@ func TestSymLink(t *testing.T) {
if s != to {
t.Fatalf("after symlink %q != %q", s, to)
}
- file, err = Open(from, O_RDONLY, 0)
+ file, err = Open(from)
if err != nil {
t.Fatalf("open %q failed: %v", from, err)
}
@@ -391,7 +405,7 @@ func TestLongSymlink(t *testing.T) {
func TestRename(t *testing.T) {
from, to := "renamefrom", "renameto"
Remove(to) // Just in case.
- file, err := Open(from, O_CREAT|O_WRONLY, 0666)
+ file, err := Create(from)
if err != nil {
t.Fatalf("open %q failed: %v", to, err)
}
@@ -409,25 +423,13 @@ func TestRename(t *testing.T) {
}
}
-func TestForkExec(t *testing.T) {
- var cmd, adir, expect string
- var args []string
+func exec(t *testing.T, dir, cmd string, args []string, expect string) {
r, w, err := Pipe()
if err != nil {
t.Fatalf("Pipe: %v", err)
}
- if syscall.OS == "windows" {
- cmd = Getenv("COMSPEC")
- args = []string{Getenv("COMSPEC"), "/c cd"}
- adir = Getenv("SystemRoot")
- expect = Getenv("SystemRoot") + "\r\n"
- } else {
- cmd = "/bin/pwd"
- args = []string{"pwd"}
- adir = "/"
- expect = "/\n"
- }
- p, err := StartProcess(cmd, args, nil, adir, []*File{nil, w, Stderr})
+ attr := &ProcAttr{Dir: dir, Files: []*File{nil, w, Stderr}}
+ p, err := StartProcess(cmd, args, attr)
if err != nil {
t.Fatalf("StartProcess: %v", err)
}
@@ -438,12 +440,34 @@ func TestForkExec(t *testing.T) {
io.Copy(&b, r)
output := b.String()
if output != expect {
- args[0] = cmd
- t.Errorf("exec %q returned %q wanted %q", strings.Join(args, " "), output, expect)
+ t.Errorf("exec %q returned %q wanted %q",
+ strings.Join(append([]string{cmd}, args...), " "), output, expect)
}
p.Wait(0)
}
+func TestStartProcess(t *testing.T) {
+ var dir, cmd, le string
+ var args []string
+ if syscall.OS == "windows" {
+ le = "\r\n"
+ cmd = Getenv("COMSPEC")
+ dir = Getenv("SystemRoot")
+ args = []string{"/c", "cd"}
+ } else {
+ le = "\n"
+ cmd = "/bin/pwd"
+ dir = "/"
+ args = []string{}
+ }
+ cmddir, cmdbase := filepath.Split(cmd)
+ args = append([]string{cmdbase}, args...)
+ // Test absolute executable path.
+ exec(t, dir, cmd, args, dir+le)
+ // Test relative executable path.
+ exec(t, cmddir, cmdbase, args, filepath.Clean(cmddir)+le)
+}
+
func checkMode(t *testing.T, path string, mode uint32) {
dir, err := Stat(path)
if err != nil {
@@ -608,19 +632,19 @@ func TestChdirAndGetwd(t *testing.T) {
if syscall.OS == "windows" {
return
}
- fd, err := Open(".", O_RDONLY, 0)
+ fd, err := Open(".")
if err != nil {
t.Fatalf("Open .: %s", err)
}
// These are chosen carefully not to be symlinks on a Mac
// (unlike, say, /var, /etc, and /tmp).
- dirs := []string{"/bin", "/", "/usr/bin"}
+ dirs := []string{"/", "/usr/bin"}
for mode := 0; mode < 2; mode++ {
for _, d := range dirs {
if mode == 0 {
err = Chdir(d)
} else {
- fd1, err := Open(d, O_RDONLY, 0)
+ fd1, err := Open(d)
if err != nil {
t.Errorf("Open %s: %s", d, err)
continue
@@ -729,7 +753,7 @@ var openErrorTests = []openErrorTest{
func TestOpenError(t *testing.T) {
for _, tt := range openErrorTests {
- f, err := Open(tt.path, tt.mode, 0)
+ f, err := OpenFile(tt.path, tt.mode, 0)
if err == nil {
t.Errorf("Open(%q, %d) succeeded", tt.path, tt.mode)
f.Close()
@@ -751,7 +775,7 @@ func run(t *testing.T, cmd []string) string {
if err != nil {
t.Fatal(err)
}
- p, err := StartProcess("/bin/hostname", []string{"hostname"}, nil, "/", []*File{nil, w, Stderr})
+ p, err := StartProcess("/bin/hostname", []string{"hostname"}, &ProcAttr{Files: []*File{nil, w, Stderr}})
if err != nil {
t.Fatal(err)
}
@@ -835,7 +859,7 @@ func TestWriteAt(t *testing.T) {
}
func writeFile(t *testing.T, fname string, flag int, text string) string {
- f, err := Open(fname, flag, 0666)
+ f, err := OpenFile(fname, flag, 0666)
if err != nil {
t.Fatalf("Open: %v", err)
}
@@ -854,7 +878,7 @@ func writeFile(t *testing.T, fname string, flag int, text string) string {
func TestAppend(t *testing.T) {
const f = "append.txt"
defer Remove(f)
- s := writeFile(t, f, O_CREAT|O_TRUNC|O_RDWR, "new")
+ s := writeFile(t, f, O_CREATE|O_TRUNC|O_RDWR, "new")
if s != "new" {
t.Fatalf("writeFile: have %q want %q", s, "new")
}
diff --git a/src/pkg/os/path.go b/src/pkg/os/path.go
index b762971d9..0eb3ee503 100644
--- a/src/pkg/os/path.go
+++ b/src/pkg/os/path.go
@@ -33,7 +33,7 @@ func MkdirAll(path string, perm uint32) Error {
j--
}
- if j > 0 {
+ if j > 1 {
// Create parent
err = MkdirAll(path[0:j-1], perm)
if err != nil {
@@ -80,7 +80,7 @@ func RemoveAll(path string) Error {
}
// Directory.
- fd, err := Open(path, O_RDONLY, 0)
+ fd, err := Open(path)
if err != nil {
return err
}
diff --git a/src/pkg/os/path_test.go b/src/pkg/os/path_test.go
index 799e3ec2f..483bb6395 100644
--- a/src/pkg/os/path_test.go
+++ b/src/pkg/os/path_test.go
@@ -29,7 +29,7 @@ func TestMkdirAll(t *testing.T) {
// Make file.
fpath := path + "/file"
- _, err = Open(fpath, O_WRONLY|O_CREAT, 0666)
+ _, err = Create(fpath)
if err != nil {
t.Fatalf("create %q: %s", fpath, err)
}
@@ -72,7 +72,7 @@ func TestRemoveAll(t *testing.T) {
if err := MkdirAll(path, 0777); err != nil {
t.Fatalf("MkdirAll %q: %s", path, err)
}
- fd, err := Open(fpath, O_WRONLY|O_CREAT, 0666)
+ fd, err := Create(fpath)
if err != nil {
t.Fatalf("create %q: %s", fpath, err)
}
@@ -88,12 +88,12 @@ func TestRemoveAll(t *testing.T) {
if err = MkdirAll(dpath, 0777); err != nil {
t.Fatalf("MkdirAll %q: %s", dpath, err)
}
- fd, err = Open(fpath, O_WRONLY|O_CREAT, 0666)
+ fd, err = Create(fpath)
if err != nil {
t.Fatalf("create %q: %s", fpath, err)
}
fd.Close()
- fd, err = Open(dpath+"/file", O_WRONLY|O_CREAT, 0666)
+ fd, err = Create(dpath + "/file")
if err != nil {
t.Fatalf("create %q: %s", fpath, err)
}
@@ -121,7 +121,7 @@ func TestRemoveAll(t *testing.T) {
}
for _, s := range []string{fpath, dpath + "/file1", path + "/zzz"} {
- fd, err = Open(s, O_WRONLY|O_CREAT, 0666)
+ fd, err = Create(s)
if err != nil {
t.Fatalf("create %q: %s", s, err)
}
@@ -179,3 +179,20 @@ func TestMkdirAllWithSymlink(t *testing.T) {
t.Errorf("MkdirAll %q: %s", path, err)
}
}
+
+func TestMkdirAllAtSlash(t *testing.T) {
+ if runtime.GOOS == "windows" {
+ return
+ }
+ RemoveAll("/_go_os_test")
+ err := MkdirAll("/_go_os_test/dir", 0777)
+ if err != nil {
+ pathErr, ok := err.(*PathError)
+ // common for users not to be able to write to /
+ if ok && pathErr.Error == EACCES {
+ return
+ }
+ t.Fatalf(`MkdirAll "/_go_os_test/dir": %v`, err)
+ }
+ RemoveAll("/_go_os_test")
+}
diff --git a/src/pkg/os/proc.go b/src/pkg/os/proc.go
index dfddab6cb..481ef6033 100644
--- a/src/pkg/os/proc.go
+++ b/src/pkg/os/proc.go
@@ -26,8 +26,8 @@ func Getegid() int { return syscall.Getegid() }
// Getgroups returns a list of the numeric ids of groups that the caller belongs to.
func Getgroups() ([]int, Error) {
- gids, errno := syscall.Getgroups()
- return gids, NewSyscallError("getgroups", errno)
+ gids, e := syscall.Getgroups()
+ return gids, NewSyscallError("getgroups", e)
}
// Exit causes the current program to exit with the given status code.
diff --git a/src/pkg/os/stat_darwin.go b/src/pkg/os/stat_darwin.go
index 8f4e6bafa..0661a6d59 100644
--- a/src/pkg/os/stat_darwin.go
+++ b/src/pkg/os/stat_darwin.go
@@ -24,13 +24,7 @@ func fileInfoFromStat(name string, fi *FileInfo, lstat, stat *syscall.Stat_t) *F
fi.Atime_ns = syscall.TimespecToNsec(stat.Atimespec)
fi.Mtime_ns = syscall.TimespecToNsec(stat.Mtimespec)
fi.Ctime_ns = syscall.TimespecToNsec(stat.Ctimespec)
- for i := len(name) - 1; i >= 0; i-- {
- if name[i] == '/' {
- name = name[i+1:]
- break
- }
- }
- fi.Name = name
+ fi.Name = basename(name)
if isSymlink(lstat) && !isSymlink(stat) {
fi.FollowedSymlink = true
}
diff --git a/src/pkg/os/stat_freebsd.go b/src/pkg/os/stat_freebsd.go
index aa15d4b63..454165d4e 100644
--- a/src/pkg/os/stat_freebsd.go
+++ b/src/pkg/os/stat_freebsd.go
@@ -24,13 +24,7 @@ func fileInfoFromStat(name string, fi *FileInfo, lstat, stat *syscall.Stat_t) *F
fi.Atime_ns = syscall.TimespecToNsec(stat.Atimespec)
fi.Mtime_ns = syscall.TimespecToNsec(stat.Mtimespec)
fi.Ctime_ns = syscall.TimespecToNsec(stat.Ctimespec)
- for i := len(name) - 1; i >= 0; i-- {
- if name[i] == '/' {
- name = name[i+1:]
- break
- }
- }
- fi.Name = name
+ fi.Name = basename(name)
if isSymlink(lstat) && !isSymlink(stat) {
fi.FollowedSymlink = true
}
diff --git a/src/pkg/os/stat_linux.go b/src/pkg/os/stat_linux.go
index ebfa1721c..7a3cf794d 100644
--- a/src/pkg/os/stat_linux.go
+++ b/src/pkg/os/stat_linux.go
@@ -24,13 +24,7 @@ func fileInfoFromStat(name string, fi *FileInfo, lstat, stat *syscall.Stat_t) *F
fi.Atime_ns = syscall.TimespecToNsec(stat.Atim)
fi.Mtime_ns = syscall.TimespecToNsec(stat.Mtim)
fi.Ctime_ns = syscall.TimespecToNsec(stat.Ctim)
- for i := len(name) - 1; i >= 0; i-- {
- if name[i] == '/' {
- name = name[i+1:]
- break
- }
- }
- fi.Name = name
+ fi.Name = basename(name)
if isSymlink(lstat) && !isSymlink(stat) {
fi.FollowedSymlink = true
}
diff --git a/src/pkg/os/stat_plan9.go b/src/pkg/os/stat_plan9.go
new file mode 100644
index 000000000..e96749d33
--- /dev/null
+++ b/src/pkg/os/stat_plan9.go
@@ -0,0 +1,84 @@
+// 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 os
+
+import "syscall"
+
+func fileInfoFromStat(fi *FileInfo, d *Dir) *FileInfo {
+ fi.Dev = uint64(d.Qid.Vers) | uint64(d.Qid.Type<<32)
+ fi.Ino = d.Qid.Path
+
+ fi.Mode = uint32(d.Mode) & 0777
+ if (d.Mode & syscall.DMDIR) == syscall.DMDIR {
+ fi.Mode |= syscall.S_IFDIR
+ } else {
+ fi.Mode |= syscall.S_IFREG
+ }
+
+ fi.Size = int64(d.Length)
+ fi.Atime_ns = 1e9 * int64(d.Atime)
+ fi.Mtime_ns = 1e9 * int64(d.Mtime)
+ fi.Name = d.Name
+ fi.FollowedSymlink = false
+ return fi
+}
+
+// arg is an open *File or a path string.
+func dirstat(arg interface{}) (fi *FileInfo, err Error) {
+ var name string
+ nd := syscall.STATFIXLEN + 16*4
+
+ for i := 0; i < 2; i++ { /* should work by the second try */
+ buf := make([]byte, nd)
+
+ var n int
+ var e syscall.Error
+
+ switch syscallArg := arg.(type) {
+ case *File:
+ name = syscallArg.name
+ n, e = syscall.Fstat(syscallArg.fd, buf)
+ case string:
+ name = syscallArg
+ n, e = syscall.Stat(name, buf)
+ }
+
+ if e != nil {
+ return nil, &PathError{"stat", name, e}
+ }
+
+ if n < syscall.STATFIXLEN {
+ return nil, &PathError{"stat", name, Eshortstat}
+ }
+
+ ntmp, _ := gbit16(buf)
+ nd = int(ntmp)
+
+ if nd <= n {
+ d, e := UnmarshalDir(buf[:n])
+
+ if e != nil {
+ return nil, &PathError{"stat", name, e}
+ }
+
+ return fileInfoFromStat(new(FileInfo), d), nil
+ }
+ }
+
+ return nil, &PathError{"stat", name, Ebadstat}
+}
+
+
+// Stat returns a FileInfo structure describing the named file and an error, if any.
+func Stat(name string) (fi *FileInfo, err Error) {
+ return dirstat(name)
+}
+
+// Lstat returns the FileInfo structure describing the named file and an
+// error, if any. If the file is a symbolic link (though Plan 9 does not have symbolic links),
+// the returned FileInfo describes the symbolic link. Lstat makes no attempt to follow the link.
+func Lstat(name string) (fi *FileInfo, err Error) {
+ return dirstat(name)
+}
diff --git a/src/pkg/os/sys_linux.go b/src/pkg/os/sys_linux.go
index b82d295d3..408d667c7 100644
--- a/src/pkg/os/sys_linux.go
+++ b/src/pkg/os/sys_linux.go
@@ -9,7 +9,7 @@ package os
// Hostname returns the host name reported by the kernel.
func Hostname() (name string, err Error) {
- f, err := Open("/proc/sys/kernel/hostname", O_RDONLY, 0)
+ f, err := Open("/proc/sys/kernel/hostname")
if err != nil {
return "", err
}
diff --git a/src/pkg/os/sys_plan9.go b/src/pkg/os/sys_plan9.go
new file mode 100644
index 000000000..f6af28b61
--- /dev/null
+++ b/src/pkg/os/sys_plan9.go
@@ -0,0 +1,27 @@
+// 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.
+
+// Plan 9-specific
+
+package os
+
+
+func Hostname() (name string, err Error) {
+ f, err := Open("#c/sysname")
+ if err != nil {
+ return "", err
+ }
+ defer f.Close()
+
+ var buf [128]byte
+ n, err := f.Read(buf[:len(buf)-1])
+
+ if err != nil {
+ return "", err
+ }
+ if n > 0 {
+ buf[n] = 0
+ }
+ return string(buf[0:n]), nil
+}
diff --git a/src/pkg/os/time.go b/src/pkg/os/time.go
index 380345f1b..8e87a49e1 100644
--- a/src/pkg/os/time.go
+++ b/src/pkg/os/time.go
@@ -13,8 +13,8 @@ import "syscall"
// time is the Unix epoch.
func Time() (sec int64, nsec int64, err Error) {
var tv syscall.Timeval
- if errno := syscall.Gettimeofday(&tv); errno != 0 {
- return 0, 0, NewSyscallError("gettimeofday", errno)
+ if e := syscall.Gettimeofday(&tv); iserror(e) {
+ return 0, 0, NewSyscallError("gettimeofday", e)
}
return int64(tv.Sec), int64(tv.Usec) * 1000, err
}
diff --git a/src/pkg/path/filepath/Makefile b/src/pkg/path/filepath/Makefile
index 2330fc09d..bc26a7d6a 100644
--- a/src/pkg/path/filepath/Makefile
+++ b/src/pkg/path/filepath/Makefile
@@ -18,8 +18,11 @@ GOFILES_darwin=\
GOFILES_linux=\
path_unix.go
+GOFILES_plan9=\
+ path_plan9.go
+
GOFILES_windows=\
- path_unix.go
+ path_windows.go
GOFILES+=$(GOFILES_$(GOOS))
diff --git a/src/pkg/path/filepath/match.go b/src/pkg/path/filepath/match.go
index ad4053fa2..a05bb5f7e 100644
--- a/src/pkg/path/filepath/match.go
+++ b/src/pkg/path/filepath/match.go
@@ -32,7 +32,7 @@ var ErrBadPattern = os.NewError("syntax error in pattern")
// lo '-' hi matches character c for lo <= c <= hi
//
// Match requires pattern to match all of name, not just a substring.
-// The only possible error return is when pattern is malformed.
+// The only possible error return occurs when the pattern is malformed.
//
func Match(pattern, name string) (matched bool, err os.Error) {
Pattern:
@@ -211,13 +211,14 @@ func getEsc(chunk string) (r int, nchunk string, err os.Error) {
// if there is no matching file. The syntax of patterns is the same
// as in Match. The pattern may describe hierarchical names such as
// /usr/*/bin/ed (assuming the Separator is '/').
+// The only possible error return occurs when the pattern is malformed.
//
-func Glob(pattern string) (matches []string) {
+func Glob(pattern string) (matches []string, err os.Error) {
if !hasMeta(pattern) {
- if _, err := os.Stat(pattern); err == nil {
- return []string{pattern}
+ if _, err = os.Stat(pattern); err != nil {
+ return
}
- return nil
+ return []string{pattern}, nil
}
dir, file := Split(pattern)
@@ -230,48 +231,60 @@ func Glob(pattern string) (matches []string) {
dir = dir[0 : len(dir)-1] // chop off trailing separator
}
- if hasMeta(dir) {
- for _, d := range Glob(dir) {
- matches = glob(d, file, matches)
- }
- } else {
+ if !hasMeta(dir) {
return glob(dir, file, nil)
}
- return matches
+
+ var m []string
+ m, err = Glob(dir)
+ if err != nil {
+ return
+ }
+ for _, d := range m {
+ matches, err = glob(d, file, matches)
+ if err != nil {
+ return
+ }
+ }
+ return
}
// glob searches for files matching pattern in the directory dir
-// and appends them to matches.
-func glob(dir, pattern string, matches []string) []string {
+// and appends them to matches. If the directory cannot be
+// opened, it returns the existing matches. New matches are
+// added in lexicographical order.
+// The only possible error return occurs when the pattern is malformed.
+func glob(dir, pattern string, matches []string) (m []string, e os.Error) {
+ m = matches
fi, err := os.Stat(dir)
if err != nil {
- return nil
+ return
}
if !fi.IsDirectory() {
- return matches
+ return
}
- d, err := os.Open(dir, os.O_RDONLY, 0666)
+ d, err := os.Open(dir)
if err != nil {
- return nil
+ return
}
defer d.Close()
names, err := d.Readdirnames(-1)
if err != nil {
- return nil
+ return
}
sort.SortStrings(names)
for _, n := range names {
matched, err := Match(pattern, n)
if err != nil {
- return matches
+ return m, err
}
if matched {
- matches = append(matches, Join(dir, n))
+ m = append(m, Join(dir, n))
}
}
- return matches
+ return
}
// hasMeta returns true if path contains any of the magic characters
diff --git a/src/pkg/path/filepath/match_test.go b/src/pkg/path/filepath/match_test.go
index ad0c90b75..43e1c1cc2 100644
--- a/src/pkg/path/filepath/match_test.go
+++ b/src/pkg/path/filepath/match_test.go
@@ -6,8 +6,9 @@ package filepath_test
import (
"os"
- "path/filepath"
+ . "path/filepath"
"testing"
+ "runtime"
)
type MatchTest struct {
@@ -55,22 +56,26 @@ var matchTests = []MatchTest{
{"[\\-x]", "x", true, nil},
{"[\\-x]", "-", true, nil},
{"[\\-x]", "a", false, nil},
- {"[]a]", "]", false, filepath.ErrBadPattern},
- {"[-]", "-", false, filepath.ErrBadPattern},
- {"[x-]", "x", false, filepath.ErrBadPattern},
- {"[x-]", "-", false, filepath.ErrBadPattern},
- {"[x-]", "z", false, filepath.ErrBadPattern},
- {"[-x]", "x", false, filepath.ErrBadPattern},
- {"[-x]", "-", false, filepath.ErrBadPattern},
- {"[-x]", "a", false, filepath.ErrBadPattern},
- {"\\", "a", false, filepath.ErrBadPattern},
- {"[a-b-c]", "a", false, filepath.ErrBadPattern},
+ {"[]a]", "]", false, ErrBadPattern},
+ {"[-]", "-", false, ErrBadPattern},
+ {"[x-]", "x", false, ErrBadPattern},
+ {"[x-]", "-", false, ErrBadPattern},
+ {"[x-]", "z", false, ErrBadPattern},
+ {"[-x]", "x", false, ErrBadPattern},
+ {"[-x]", "-", false, ErrBadPattern},
+ {"[-x]", "a", false, ErrBadPattern},
+ {"\\", "a", false, ErrBadPattern},
+ {"[a-b-c]", "a", false, ErrBadPattern},
{"*x", "xxx", true, nil},
}
func TestMatch(t *testing.T) {
+ if runtime.GOOS == "windows" {
+ // XXX: Don't pass for windows.
+ return
+ }
for _, tt := range matchTests {
- ok, err := filepath.Match(tt.pattern, tt.s)
+ ok, err := Match(tt.pattern, tt.s)
if ok != tt.match || err != tt.err {
t.Errorf("Match(%#q, %#q) = %v, %v want %v, nil", tt.pattern, tt.s, ok, err, tt.match)
}
@@ -79,6 +84,7 @@ func TestMatch(t *testing.T) {
// contains returns true if vector contains the string s.
func contains(vector []string, s string) bool {
+ s = ToSlash(s)
for _, elem := range vector {
if elem == s {
return true
@@ -97,10 +103,25 @@ var globTests = []struct {
}
func TestGlob(t *testing.T) {
+ if runtime.GOOS == "windows" {
+ // XXX: Don't pass for windows.
+ return
+ }
for _, tt := range globTests {
- matches := filepath.Glob(tt.pattern)
+ matches, err := Glob(tt.pattern)
+ if err != nil {
+ t.Errorf("Glob error for %q: %s", tt.pattern, err)
+ continue
+ }
if !contains(matches, tt.result) {
t.Errorf("Glob(%#q) = %#v want %v", tt.pattern, matches, tt.result)
}
}
}
+
+func TestGlobError(t *testing.T) {
+ _, err := Glob("[7]")
+ if err != nil {
+ t.Error("expected error for bad pattern; got none")
+ }
+}
diff --git a/src/pkg/path/filepath/path.go b/src/pkg/path/filepath/path.go
index 414df7d20..de673a725 100644
--- a/src/pkg/path/filepath/path.go
+++ b/src/pkg/path/filepath/path.go
@@ -8,12 +8,16 @@
package filepath
import (
+ "bytes"
"os"
"sort"
"strings"
)
-// BUG(niemeyer): Package filepath does not yet work on Windows.
+const (
+ SeparatorString = string(Separator)
+ ListSeparatorString = string(ListSeparator)
+)
// Clean returns the shortest path name equivalent to path
// by purely lexical processing. It applies the following rules
@@ -38,36 +42,39 @@ func Clean(path string) string {
return "."
}
- rooted := path[0] == Separator
- n := len(path)
+ rooted := IsAbs(path)
// Invariants:
// reading from path; r is index of next byte to process.
// writing to buf; w is index of next byte to write.
// dotdot is index in buf where .. must stop, either because
// it is the leading slash or it is a leading ../../.. prefix.
+ prefix := volumeName(path)
+ path = path[len(prefix):]
+ n := len(path)
buf := []byte(path)
r, w, dotdot := 0, 0, 0
if rooted {
+ buf[0] = Separator
r, w, dotdot = 1, 1, 1
}
for r < n {
switch {
- case path[r] == Separator:
+ case isSeparator(path[r]):
// empty path element
r++
- case path[r] == '.' && (r+1 == n || path[r+1] == Separator):
+ case path[r] == '.' && (r+1 == n || isSeparator(path[r+1])):
// . element
r++
- case path[r] == '.' && path[r+1] == '.' && (r+2 == n || path[r+2] == Separator):
+ case path[r] == '.' && path[r+1] == '.' && (r+2 == n || isSeparator(path[r+2])):
// .. element: remove to last separator
r += 2
switch {
case w > dotdot:
// can backtrack
w--
- for w > dotdot && buf[w] != Separator {
+ for w > dotdot && !isSeparator(buf[w]) {
w--
}
case !rooted:
@@ -90,7 +97,7 @@ func Clean(path string) string {
w++
}
// copy element
- for ; r < n && path[r] != Separator; r++ {
+ for ; r < n && !isSeparator(path[r]); r++ {
buf[w] = path[r]
w++
}
@@ -103,7 +110,7 @@ func Clean(path string) string {
w++
}
- return string(buf[0:w])
+ return prefix + string(buf[0:w])
}
// ToSlash returns the result of replacing each separator character
@@ -112,7 +119,7 @@ func ToSlash(path string) string {
if Separator == '/' {
return path
}
- return strings.Replace(path, string(Separator), "/", -1)
+ return strings.Replace(path, SeparatorString, "/", -1)
}
// FromSlash returns the result of replacing each slash ('/') character
@@ -121,7 +128,7 @@ func FromSlash(path string) string {
if Separator == '/' {
return path
}
- return strings.Replace(path, "/", string(Separator), -1)
+ return strings.Replace(path, "/", SeparatorString, -1)
}
// SplitList splits a list of paths joined by the OS-specific ListSeparator.
@@ -129,7 +136,7 @@ func SplitList(path string) []string {
if path == "" {
return []string{}
}
- return strings.Split(path, string(ListSeparator), -1)
+ return strings.Split(path, ListSeparatorString, -1)
}
// Split splits path immediately following the final Separator,
@@ -137,7 +144,10 @@ func SplitList(path string) []string {
// If there are no separators in path, Split returns an empty base
// and file set to path.
func Split(path string) (dir, file string) {
- i := strings.LastIndex(path, string(Separator))
+ i := len(path) - 1
+ for i >= 0 && !isSeparator(path[i]) {
+ i--
+ }
return path[:i+1], path[i+1:]
}
@@ -146,7 +156,7 @@ func Split(path string) (dir, file string) {
func Join(elem ...string) string {
for i, e := range elem {
if e != "" {
- return Clean(strings.Join(elem[i:], string(Separator)))
+ return Clean(strings.Join(elem[i:], SeparatorString))
}
}
return ""
@@ -157,7 +167,7 @@ func Join(elem ...string) string {
// in the final element of path; it is empty if there is
// no dot.
func Ext(path string) string {
- for i := len(path) - 1; i >= 0 && path[i] != Separator; i-- {
+ for i := len(path) - 1; i >= 0 && !isSeparator(path[i]); i-- {
if path[i] == '.' {
return path[i:]
}
@@ -165,6 +175,77 @@ func Ext(path string) string {
return ""
}
+// EvalSymlinks returns the path name after the evaluation of any symbolic
+// links.
+// If path is relative it will be evaluated relative to the current directory.
+func EvalSymlinks(path string) (string, os.Error) {
+ const maxIter = 255
+ originalPath := path
+ // consume path by taking each frontmost path element,
+ // expanding it if it's a symlink, and appending it to b
+ var b bytes.Buffer
+ for n := 0; path != ""; n++ {
+ if n > maxIter {
+ return "", os.NewError("EvalSymlinks: too many links in " + originalPath)
+ }
+
+ // find next path component, p
+ i := strings.IndexRune(path, Separator)
+ var p string
+ if i == -1 {
+ p, path = path, ""
+ } else {
+ p, path = path[:i], path[i+1:]
+ }
+
+ if p == "" {
+ if b.Len() == 0 {
+ // must be absolute path
+ b.WriteRune(Separator)
+ }
+ continue
+ }
+
+ fi, err := os.Lstat(b.String() + p)
+ if err != nil {
+ return "", err
+ }
+ if !fi.IsSymlink() {
+ b.WriteString(p)
+ if path != "" {
+ b.WriteRune(Separator)
+ }
+ continue
+ }
+
+ // it's a symlink, put it at the front of path
+ dest, err := os.Readlink(b.String() + p)
+ if err != nil {
+ return "", err
+ }
+ if IsAbs(dest) {
+ b.Reset()
+ }
+ path = dest + SeparatorString + path
+ }
+ return Clean(b.String()), nil
+}
+
+// Abs returns an absolute representation of path.
+// If the path is not absolute it will be joined with the current
+// working directory to turn it into an absolute path. The absolute
+// path name for a given file is not guaranteed to be unique.
+func Abs(path string) (string, os.Error) {
+ if IsAbs(path) {
+ return path, nil
+ }
+ wd, err := os.Getwd()
+ if err != nil {
+ return "", err
+ }
+ return Join(wd, path), nil
+}
+
// Visitor methods are invoked for corresponding file tree entries
// visited by Walk. The parameter path is the full path of f relative
// to root.
@@ -199,7 +280,7 @@ func walk(path string, f *os.FileInfo, v Visitor, errors chan<- os.Error) {
// a list of sorted directory entries.
// Copied from io/ioutil to avoid the circular import.
func readDir(dirname string) ([]*os.FileInfo, os.Error) {
- f, err := os.Open(dirname, os.O_RDONLY, 0)
+ f, err := os.Open(dirname)
if err != nil {
return nil, err
}
@@ -250,21 +331,20 @@ func Base(path string) string {
return "."
}
// Strip trailing slashes.
- for len(path) > 0 && path[len(path)-1] == Separator {
+ for len(path) > 0 && isSeparator(path[len(path)-1]) {
path = path[0 : len(path)-1]
}
// Find the last element
- if i := strings.LastIndex(path, string(Separator)); i >= 0 {
+ i := len(path) - 1
+ for i >= 0 && !isSeparator(path[i]) {
+ i--
+ }
+ if i >= 0 {
path = path[i+1:]
}
// If empty now, it had only slashes.
if path == "" {
- return string(Separator)
+ return SeparatorString
}
return path
}
-
-// IsAbs returns true if the path is absolute.
-func IsAbs(path string) bool {
- return len(path) > 0 && path[0] == Separator
-}
diff --git a/src/pkg/path/filepath/path_plan9.go b/src/pkg/path/filepath/path_plan9.go
new file mode 100644
index 000000000..e40008364
--- /dev/null
+++ b/src/pkg/path/filepath/path_plan9.go
@@ -0,0 +1,28 @@
+// 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.
+
+package filepath
+
+import "strings"
+
+const (
+ Separator = '/' // OS-specific path separator
+ ListSeparator = 0 // OS-specific path list separator
+)
+
+// isSeparator returns true if c is a directory separator character.
+func isSeparator(c uint8) bool {
+ return Separator == c
+}
+
+// IsAbs returns true if the path is absolute.
+func IsAbs(path string) bool {
+ return strings.HasPrefix(path, "/") || strings.HasPrefix(path, "#")
+}
+
+// volumeName returns the leading volume name on Windows.
+// It returns "" elsewhere
+func volumeName(path string) string {
+ return ""
+}
diff --git a/src/pkg/path/filepath/path_test.go b/src/pkg/path/filepath/path_test.go
index 8f887f00b..b3b6eb5ab 100644
--- a/src/pkg/path/filepath/path_test.go
+++ b/src/pkg/path/filepath/path_test.go
@@ -9,6 +9,7 @@ import (
"path/filepath"
"reflect"
"runtime"
+ "strings"
"testing"
)
@@ -68,7 +69,7 @@ var cleantests = []PathTest{
func TestClean(t *testing.T) {
for _, test := range cleantests {
- if s := filepath.Clean(test.path); s != test.result {
+ if s := filepath.ToSlash(filepath.Clean(test.path)); s != test.result {
t.Errorf("Clean(%q) = %q, want %q", test.path, s, test.result)
}
}
@@ -161,6 +162,14 @@ var jointests = []JoinTest{
{[]string{"", ""}, ""},
}
+var winjointests = []JoinTest{
+ {[]string{`directory`, `file`}, `directory\file`},
+ {[]string{`C:\Windows\`, `System32`}, `C:\Windows\System32`},
+ {[]string{`C:\Windows\`, ``}, `C:\Windows`},
+ {[]string{`C:\`, `Windows`}, `C:\Windows`},
+ {[]string{`C:`, `Windows`}, `C:\Windows`},
+}
+
// join takes a []string and passes it to Join.
func join(elem []string, args ...string) string {
args = elem
@@ -168,8 +177,11 @@ func join(elem []string, args ...string) string {
}
func TestJoin(t *testing.T) {
+ if runtime.GOOS == "windows" {
+ jointests = append(jointests, winjointests...)
+ }
for _, test := range jointests {
- if p := join(test.elem); p != test.path {
+ if p := join(test.elem); p != filepath.FromSlash(test.path) {
t.Errorf("join(%q) = %q, want %q", test.elem, p, test.path)
}
}
@@ -237,7 +249,7 @@ func walkTree(n *Node, path string, f func(path string, n *Node)) {
func makeTree(t *testing.T) {
walkTree(tree, tree.name, func(path string, n *Node) {
if n.entries == nil {
- fd, err := os.Open(path, os.O_CREAT, 0660)
+ fd, err := os.Create(path)
if err != nil {
t.Errorf("makeTree: %v", err)
}
@@ -261,6 +273,7 @@ func checkMarks(t *testing.T) {
// Assumes that each node name is unique. Good enough for a test.
func mark(name string) {
+ name = filepath.ToSlash(name)
walkTree(tree, tree.name, func(path string, n *Node) {
if n.name == name {
n.mark++
@@ -302,7 +315,7 @@ func TestWalk(t *testing.T) {
}
checkMarks(t)
- if os.Getuid() != 0 {
+ if os.Getuid() > 0 {
// introduce 2 errors: chmod top-level directories to 0
os.Chmod(filepath.Join(tree.name, tree.entries[1].name), 0)
os.Chmod(filepath.Join(tree.name, tree.entries[3].name), 0)
@@ -361,7 +374,7 @@ var basetests = []PathTest{
func TestBase(t *testing.T) {
for _, test := range basetests {
- if s := filepath.Base(test.path); s != test.result {
+ if s := filepath.ToSlash(filepath.Base(test.path)); s != test.result {
t.Errorf("Base(%q) = %q, want %q", test.path, s, test.result)
}
}
@@ -372,7 +385,7 @@ type IsAbsTest struct {
isAbs bool
}
-var isAbsTests = []IsAbsTest{
+var isabstests = []IsAbsTest{
{"", false},
{"/", true},
{"/usr/bin/gcc", true},
@@ -383,10 +396,130 @@ var isAbsTests = []IsAbsTest{
{"lala", false},
}
+var winisabstests = []IsAbsTest{
+ {`C:\`, true},
+ {`c\`, false},
+ {`c::`, false},
+ {`/`, true},
+ {`\`, true},
+ {`\Windows`, true},
+}
+
func TestIsAbs(t *testing.T) {
- for _, test := range isAbsTests {
+ if runtime.GOOS == "windows" {
+ isabstests = append(isabstests, winisabstests...)
+ }
+ for _, test := range isabstests {
if r := filepath.IsAbs(test.path); r != test.isAbs {
t.Errorf("IsAbs(%q) = %v, want %v", test.path, r, test.isAbs)
}
}
}
+
+type EvalSymlinksTest struct {
+ path, dest string
+}
+
+var EvalSymlinksTestDirs = []EvalSymlinksTest{
+ {"test", ""},
+ {"test/dir", ""},
+ {"test/dir/link3", "../../"},
+ {"test/link1", "../test"},
+ {"test/link2", "dir"},
+}
+
+var EvalSymlinksTests = []EvalSymlinksTest{
+ {"test", "test"},
+ {"test/dir", "test/dir"},
+ {"test/dir/../..", "."},
+ {"test/link1", "test"},
+ {"test/link2", "test/dir"},
+ {"test/link1/dir", "test/dir"},
+ {"test/link2/..", "test"},
+ {"test/dir/link3", "."},
+ {"test/link2/link3/test", "test"},
+}
+
+func TestEvalSymlinks(t *testing.T) {
+ // Symlinks are not supported under windows.
+ if runtime.GOOS == "windows" {
+ return
+ }
+ defer os.RemoveAll("test")
+ for _, d := range EvalSymlinksTestDirs {
+ var err os.Error
+ if d.dest == "" {
+ err = os.Mkdir(d.path, 0755)
+ } else {
+ err = os.Symlink(d.dest, d.path)
+ }
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+ // relative
+ for _, d := range EvalSymlinksTests {
+ if p, err := filepath.EvalSymlinks(d.path); err != nil {
+ t.Errorf("EvalSymlinks(%q) error: %v", d.path, err)
+ } else if p != d.dest {
+ t.Errorf("EvalSymlinks(%q)=%q, want %q", d.path, p, d.dest)
+ }
+ }
+ // absolute
+ goroot, err := filepath.EvalSymlinks(os.Getenv("GOROOT"))
+ if err != nil {
+ t.Fatalf("EvalSymlinks(%q) error: %v", os.Getenv("GOROOT"), err)
+ }
+ testroot := filepath.Join(goroot, "src", "pkg", "path", "filepath")
+ for _, d := range EvalSymlinksTests {
+ a := EvalSymlinksTest{
+ filepath.Join(testroot, d.path),
+ filepath.Join(testroot, d.dest),
+ }
+ if p, err := filepath.EvalSymlinks(a.path); err != nil {
+ t.Errorf("EvalSymlinks(%q) error: %v", a.path, err)
+ } else if p != a.dest {
+ t.Errorf("EvalSymlinks(%q)=%q, want %q", a.path, p, a.dest)
+ }
+ }
+}
+
+// Test paths relative to $GOROOT/src
+var abstests = []string{
+ "../AUTHORS",
+ "pkg/../../AUTHORS",
+ "Make.pkg",
+ "pkg/Makefile",
+
+ // Already absolute
+ "$GOROOT/src/Make.pkg",
+}
+
+func TestAbs(t *testing.T) {
+ oldwd, err := os.Getwd()
+ if err != nil {
+ t.Fatal("Getwd failed: " + err.String())
+ }
+ defer os.Chdir(oldwd)
+ goroot := os.Getenv("GOROOT")
+ cwd := filepath.Join(goroot, "src")
+ os.Chdir(cwd)
+ for _, path := range abstests {
+ path = strings.Replace(path, "$GOROOT", goroot, -1)
+ abspath, err := filepath.Abs(path)
+ if err != nil {
+ t.Errorf("Abs(%q) error: %v", path, err)
+ }
+ info, err := os.Stat(path)
+ if err != nil {
+ t.Errorf("%s: %s", path, err)
+ }
+ absinfo, err := os.Stat(abspath)
+ if err != nil || absinfo.Ino != info.Ino {
+ t.Errorf("Abs(%q)=%q, not the same file", path, abspath)
+ }
+ if !filepath.IsAbs(abspath) {
+ t.Errorf("Abs(%q)=%q, not an absolute path", path, abspath)
+ }
+ }
+}
diff --git a/src/pkg/path/filepath/path_unix.go b/src/pkg/path/filepath/path_unix.go
index 7d07794e3..f8ac248fb 100644
--- a/src/pkg/path/filepath/path_unix.go
+++ b/src/pkg/path/filepath/path_unix.go
@@ -4,7 +4,25 @@
package filepath
+import "strings"
+
const (
Separator = '/' // OS-specific path separator
ListSeparator = ':' // OS-specific path list separator
)
+
+// isSeparator returns true if c is a directory separator character.
+func isSeparator(c uint8) bool {
+ return Separator == c
+}
+
+// IsAbs returns true if the path is absolute.
+func IsAbs(path string) bool {
+ return strings.HasPrefix(path, "/")
+}
+
+// volumeName returns the leading volume name on Windows.
+// It returns "" elsewhere.
+func volumeName(path string) string {
+ return ""
+}
diff --git a/src/pkg/path/filepath/path_windows.go b/src/pkg/path/filepath/path_windows.go
new file mode 100644
index 000000000..dbd1c1e40
--- /dev/null
+++ b/src/pkg/path/filepath/path_windows.go
@@ -0,0 +1,37 @@
+// 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.
+
+package filepath
+
+const (
+ Separator = '\\' // OS-specific path separator
+ ListSeparator = ':' // OS-specific path list separator
+)
+
+// isSeparator returns true if c is a directory separator character.
+func isSeparator(c uint8) bool {
+ // NOTE: Windows accept / as path separator.
+ return c == '\\' || c == '/'
+}
+
+// IsAbs returns true if the path is absolute.
+func IsAbs(path string) bool {
+ return path != "" && (volumeName(path) != "" || isSeparator(path[0]))
+}
+
+// volumeName return leading volume name.
+// If given "C:\foo\bar", return "C:" on windows.
+func volumeName(path string) string {
+ if path == "" {
+ return ""
+ }
+ // with drive letter
+ c := path[0]
+ if len(path) > 2 && path[1] == ':' && isSeparator(path[2]) &&
+ ('0' <= c && c <= '9' || 'a' <= c && c <= 'z' ||
+ 'A' <= c && c <= 'Z') {
+ return path[0:2]
+ }
+ return ""
+}
diff --git a/src/pkg/reflect/all_test.go b/src/pkg/reflect/all_test.go
index 7a97ea173..bc9157672 100644
--- a/src/pkg/reflect/all_test.go
+++ b/src/pkg/reflect/all_test.go
@@ -207,58 +207,46 @@ func testType(t *testing.T, i int, typ Type, want string) {
func TestTypes(t *testing.T) {
for i, tt := range typeTests {
- testType(t, i, NewValue(tt.i).(*StructValue).Field(0).Type(), tt.s)
+ testType(t, i, NewValue(tt.i).Field(0).Type(), tt.s)
}
}
func TestSet(t *testing.T) {
for i, tt := range valueTests {
v := NewValue(tt.i)
- switch v := v.(type) {
- case *IntValue:
- switch v.Type().Kind() {
- case Int:
- v.Set(132)
- case Int8:
- v.Set(8)
- case Int16:
- v.Set(16)
- case Int32:
- v.Set(32)
- case Int64:
- v.Set(64)
- }
- case *UintValue:
- switch v.Type().Kind() {
- case Uint:
- v.Set(132)
- case Uint8:
- v.Set(8)
- case Uint16:
- v.Set(16)
- case Uint32:
- v.Set(32)
- case Uint64:
- v.Set(64)
- }
- case *FloatValue:
- switch v.Type().Kind() {
- case Float32:
- v.Set(256.25)
- case Float64:
- v.Set(512.125)
- }
- case *ComplexValue:
- switch v.Type().Kind() {
- case Complex64:
- v.Set(532.125 + 10i)
- case Complex128:
- v.Set(564.25 + 1i)
- }
- case *StringValue:
- v.Set("stringy cheese")
- case *BoolValue:
- v.Set(true)
+ switch v.Kind() {
+ case Int:
+ v.SetInt(132)
+ case Int8:
+ v.SetInt(8)
+ case Int16:
+ v.SetInt(16)
+ case Int32:
+ v.SetInt(32)
+ case Int64:
+ v.SetInt(64)
+ case Uint:
+ v.SetUint(132)
+ case Uint8:
+ v.SetUint(8)
+ case Uint16:
+ v.SetUint(16)
+ case Uint32:
+ v.SetUint(32)
+ case Uint64:
+ v.SetUint(64)
+ case Float32:
+ v.SetFloat(256.25)
+ case Float64:
+ v.SetFloat(512.125)
+ case Complex64:
+ v.SetComplex(532.125 + 10i)
+ case Complex128:
+ v.SetComplex(564.25 + 1i)
+ case String:
+ v.SetString("stringy cheese")
+ case Bool:
+ v.SetBool(true)
}
s := valueToString(v)
if s != tt.s {
@@ -270,52 +258,39 @@ func TestSet(t *testing.T) {
func TestSetValue(t *testing.T) {
for i, tt := range valueTests {
v := NewValue(tt.i)
- switch v := v.(type) {
- case *IntValue:
- switch v.Type().Kind() {
- case Int:
- v.SetValue(NewValue(int(132)))
- case Int8:
- v.SetValue(NewValue(int8(8)))
- case Int16:
- v.SetValue(NewValue(int16(16)))
- case Int32:
- v.SetValue(NewValue(int32(32)))
- case Int64:
- v.SetValue(NewValue(int64(64)))
- }
- case *UintValue:
- switch v.Type().Kind() {
- case Uint:
- v.SetValue(NewValue(uint(132)))
- case Uint8:
- v.SetValue(NewValue(uint8(8)))
- case Uint16:
- v.SetValue(NewValue(uint16(16)))
- case Uint32:
- v.SetValue(NewValue(uint32(32)))
- case Uint64:
- v.SetValue(NewValue(uint64(64)))
- }
- case *FloatValue:
- switch v.Type().Kind() {
- case Float32:
- v.SetValue(NewValue(float32(256.25)))
- case Float64:
- v.SetValue(NewValue(512.125))
- }
- case *ComplexValue:
- switch v.Type().Kind() {
- case Complex64:
- v.SetValue(NewValue(complex64(532.125 + 10i)))
- case Complex128:
- v.SetValue(NewValue(complex128(564.25 + 1i)))
- }
-
- case *StringValue:
- v.SetValue(NewValue("stringy cheese"))
- case *BoolValue:
- v.SetValue(NewValue(true))
+ switch v.Kind() {
+ case Int:
+ v.Set(NewValue(int(132)))
+ case Int8:
+ v.Set(NewValue(int8(8)))
+ case Int16:
+ v.Set(NewValue(int16(16)))
+ case Int32:
+ v.Set(NewValue(int32(32)))
+ case Int64:
+ v.Set(NewValue(int64(64)))
+ case Uint:
+ v.Set(NewValue(uint(132)))
+ case Uint8:
+ v.Set(NewValue(uint8(8)))
+ case Uint16:
+ v.Set(NewValue(uint16(16)))
+ case Uint32:
+ v.Set(NewValue(uint32(32)))
+ case Uint64:
+ v.Set(NewValue(uint64(64)))
+ case Float32:
+ v.Set(NewValue(float32(256.25)))
+ case Float64:
+ v.Set(NewValue(512.125))
+ case Complex64:
+ v.Set(NewValue(complex64(532.125 + 10i)))
+ case Complex128:
+ v.Set(NewValue(complex128(564.25 + 1i)))
+ case String:
+ v.Set(NewValue("stringy cheese"))
+ case Bool:
+ v.Set(NewValue(true))
}
s := valueToString(v)
if s != tt.s {
@@ -350,7 +325,7 @@ func TestValueToString(t *testing.T) {
func TestArrayElemSet(t *testing.T) {
v := NewValue([10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})
- v.(*ArrayValue).Elem(4).(*IntValue).Set(123)
+ v.Index(4).SetInt(123)
s := valueToString(v)
const want = "[10]int{1, 2, 3, 4, 123, 6, 7, 8, 9, 10}"
if s != want {
@@ -358,7 +333,7 @@ func TestArrayElemSet(t *testing.T) {
}
v = NewValue([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})
- v.(*SliceValue).Elem(4).(*IntValue).Set(123)
+ v.Index(4).SetInt(123)
s = valueToString(v)
const want1 = "[]int{1, 2, 3, 4, 123, 6, 7, 8, 9, 10}"
if s != want1 {
@@ -371,14 +346,14 @@ func TestPtrPointTo(t *testing.T) {
var i int32 = 1234
vip := NewValue(&ip)
vi := NewValue(i)
- vip.(*PtrValue).Elem().(*PtrValue).PointTo(vi)
+ vip.Elem().Set(vi.Addr())
if *ip != 1234 {
t.Errorf("got %d, want 1234", *ip)
}
ip = nil
- vp := NewValue(ip).(*PtrValue)
- vp.PointTo(vp.Elem())
+ vp := NewValue(ip)
+ vp.Set(Zero(vp.Type()))
if ip != nil {
t.Errorf("got non-nil (%p), want nil", ip)
}
@@ -388,7 +363,7 @@ func TestPtrSetNil(t *testing.T) {
var i int32 = 1234
ip := &i
vip := NewValue(&ip)
- vip.(*PtrValue).Elem().(*PtrValue).Set(nil)
+ vip.Elem().Set(Zero(vip.Elem().Type()))
if ip != nil {
t.Errorf("got non-nil (%d), want nil", *ip)
}
@@ -397,7 +372,7 @@ func TestPtrSetNil(t *testing.T) {
func TestMapSetNil(t *testing.T) {
m := make(map[string]int)
vm := NewValue(&m)
- vm.(*PtrValue).Elem().(*MapValue).Set(nil)
+ vm.Elem().Set(Zero(vm.Elem().Type()))
if m != nil {
t.Errorf("got non-nil (%p), want nil", m)
}
@@ -406,16 +381,16 @@ func TestMapSetNil(t *testing.T) {
func TestAll(t *testing.T) {
testType(t, 1, Typeof((int8)(0)), "int8")
- testType(t, 2, Typeof((*int8)(nil)).(*PtrType).Elem(), "int8")
+ testType(t, 2, Typeof((*int8)(nil)).Elem(), "int8")
typ := Typeof((*struct {
c chan *int32
d float32
})(nil))
testType(t, 3, typ, "*struct { c chan *int32; d float32 }")
- etyp := typ.(*PtrType).Elem()
+ etyp := typ.Elem()
testType(t, 4, etyp, "struct { c chan *int32; d float32 }")
- styp := etyp.(*StructType)
+ styp := etyp
f := styp.Field(0)
testType(t, 5, f.Type, "chan *int32")
@@ -432,22 +407,22 @@ func TestAll(t *testing.T) {
typ = Typeof([32]int32{})
testType(t, 7, typ, "[32]int32")
- testType(t, 8, typ.(*ArrayType).Elem(), "int32")
+ testType(t, 8, typ.Elem(), "int32")
typ = Typeof((map[string]*int32)(nil))
testType(t, 9, typ, "map[string] *int32")
- mtyp := typ.(*MapType)
+ mtyp := typ
testType(t, 10, mtyp.Key(), "string")
testType(t, 11, mtyp.Elem(), "*int32")
typ = Typeof((chan<- string)(nil))
testType(t, 12, typ, "chan<- string")
- testType(t, 13, typ.(*ChanType).Elem(), "string")
+ testType(t, 13, typ.Elem(), "string")
// make sure tag strings are not part of element type
typ = Typeof(struct {
d []uint32 "TAG"
- }{}).(*StructType).Field(0).Type
+ }{}).Field(0).Type
testType(t, 14, typ, "[]uint32")
}
@@ -457,9 +432,9 @@ func TestInterfaceGet(t *testing.T) {
}
inter.e = 123.456
v1 := NewValue(&inter)
- v2 := v1.(*PtrValue).Elem().(*StructValue).Field(0)
+ v2 := v1.Elem().Field(0)
assert(t, v2.Type().String(), "interface { }")
- i2 := v2.(*InterfaceValue).Interface()
+ i2 := v2.Interface()
v3 := NewValue(i2)
assert(t, v3.Type().String(), "float64")
}
@@ -470,9 +445,9 @@ func TestInterfaceValue(t *testing.T) {
}
inter.e = 123.456
v1 := NewValue(&inter)
- v2 := v1.(*PtrValue).Elem().(*StructValue).Field(0)
+ v2 := v1.Elem().Field(0)
assert(t, v2.Type().String(), "interface { }")
- v3 := v2.(*InterfaceValue).Elem()
+ v3 := v2.Elem()
assert(t, v3.Type().String(), "float64")
i3 := v2.Interface()
@@ -506,9 +481,9 @@ func TestAppend(t *testing.T) {
e0[j] = NewValue(e)
}
// Convert extra from []int to *SliceValue.
- e1 := NewValue(test.extra).(*SliceValue)
+ e1 := NewValue(test.extra)
// Test Append.
- a0 := NewValue(test.orig).(*SliceValue)
+ a0 := NewValue(test.orig)
have0 := Append(a0, e0...).Interface().([]int)
if !DeepEqual(have0, want) {
t.Errorf("Append #%d: have %v, want %v", i, have0, want)
@@ -521,7 +496,7 @@ func TestAppend(t *testing.T) {
t.Errorf("Append #%d extraLen: have %v, want %v", i, len(test.extra), extraLen)
}
// Test AppendSlice.
- a1 := NewValue(test.orig).(*SliceValue)
+ a1 := NewValue(test.orig)
have1 := AppendSlice(a1, e1).Interface().([]int)
if !DeepEqual(have1, want) {
t.Errorf("AppendSlice #%d: have %v, want %v", i, have1, want)
@@ -545,8 +520,8 @@ func TestCopy(t *testing.T) {
t.Fatalf("b != c before test")
}
}
- aa := NewValue(a).(*SliceValue)
- ab := NewValue(b).(*SliceValue)
+ aa := NewValue(a)
+ ab := NewValue(b)
for tocopy := 1; tocopy <= 7; tocopy++ {
aa.SetLen(tocopy)
Copy(ab, aa)
@@ -660,7 +635,7 @@ func TestDeepEqual(t *testing.T) {
func TestTypeof(t *testing.T) {
for _, test := range deepEqualTests {
v := NewValue(test.a)
- if v == nil {
+ if !v.IsValid() {
continue
}
typ := Typeof(test.a)
@@ -715,8 +690,8 @@ func TestDeepEqualComplexStructInequality(t *testing.T) {
func check2ndField(x interface{}, offs uintptr, t *testing.T) {
- s := NewValue(x).(*StructValue)
- f := s.Type().(*StructType).Field(1)
+ s := NewValue(x)
+ f := s.Type().Field(1)
if f.Offset != offs {
t.Error("mismatched offsets in structure alignment:", f.Offset, offs)
}
@@ -747,36 +722,22 @@ func TestAlignment(t *testing.T) {
check2ndField(x1, uintptr(unsafe.Pointer(&x1.f))-uintptr(unsafe.Pointer(&x1)), t)
}
-type IsNiller interface {
- IsNil() bool
-}
-
func Nil(a interface{}, t *testing.T) {
- n := NewValue(a).(*StructValue).Field(0).(IsNiller)
+ n := NewValue(a).Field(0)
if !n.IsNil() {
t.Errorf("%v should be nil", a)
}
}
func NotNil(a interface{}, t *testing.T) {
- n := NewValue(a).(*StructValue).Field(0).(IsNiller)
+ n := NewValue(a).Field(0)
if n.IsNil() {
t.Errorf("value of type %v should not be nil", NewValue(a).Type().String())
}
}
func TestIsNil(t *testing.T) {
- // These do not implement IsNil
- doNotNil := []interface{}{int(0), float32(0), struct{ a int }{}}
- for _, ts := range doNotNil {
- ty := Typeof(ts)
- v := MakeZero(ty)
- if _, ok := v.(IsNiller); ok {
- t.Errorf("%s is nilable; should not be", ts)
- }
- }
-
- // These do implement IsNil.
+ // These implement IsNil.
// Wrap in extra struct to hide interface type.
doNil := []interface{}{
struct{ x *int }{},
@@ -787,11 +748,9 @@ func TestIsNil(t *testing.T) {
struct{ x []string }{},
}
for _, ts := range doNil {
- ty := Typeof(ts).(*StructType).Field(0).Type
- v := MakeZero(ty)
- if _, ok := v.(IsNiller); !ok {
- t.Errorf("%s %T is not nilable; should be", ts, v)
- }
+ ty := Typeof(ts).Field(0).Type
+ v := Zero(ty)
+ v.IsNil() // panics if not okay to call
}
// Check the implementations
@@ -844,7 +803,7 @@ func TestInterfaceExtraction(t *testing.T) {
}
s.w = os.Stdout
- v := Indirect(NewValue(&s)).(*StructValue).Field(0).Interface()
+ v := Indirect(NewValue(&s)).Field(0).Interface()
if v != s.w.(interface{}) {
t.Error("Interface() on interface: ", v, s.w)
}
@@ -864,7 +823,7 @@ func TestInterfaceEditing(t *testing.T) {
// and setting that copy to "bye" should
// not change the value stored in i.
- v.(*StringValue).Set("bye")
+ v.SetString("bye")
if i.(string) != "hello" {
t.Errorf(`Set("bye") changed i to %s`, i.(string))
}
@@ -872,7 +831,7 @@ func TestInterfaceEditing(t *testing.T) {
// the same should be true of smaller items.
i = 123
v = NewValue(i)
- v.(*IntValue).Set(234)
+ v.SetInt(234)
if i.(int) != 123 {
t.Errorf("Set(234) changed i to %d", i.(int))
}
@@ -880,20 +839,20 @@ func TestInterfaceEditing(t *testing.T) {
func TestNilPtrValueSub(t *testing.T) {
var pi *int
- if pv := NewValue(pi).(*PtrValue); pv.Elem() != nil {
- t.Error("NewValue((*int)(nil)).(*PtrValue).Elem() != nil")
+ if pv := NewValue(pi); pv.Elem().IsValid() {
+ t.Error("NewValue((*int)(nil)).Elem().IsValid()")
}
}
func TestMap(t *testing.T) {
m := map[string]int{"a": 1, "b": 2}
- mv := NewValue(m).(*MapValue)
+ mv := NewValue(m)
if n := mv.Len(); n != len(m) {
t.Errorf("Len = %d, want %d", n, len(m))
}
- keys := mv.Keys()
+ keys := mv.MapKeys()
i := 0
- newmap := MakeMap(mv.Type().(*MapType))
+ newmap := MakeMap(mv.Type())
for k, v := range m {
// Check that returned Keys match keys in range.
// These aren't required to be in the same order,
@@ -901,22 +860,22 @@ func TestMap(t *testing.T) {
// the test easier.
if i >= len(keys) {
t.Errorf("Missing key #%d %q", i, k)
- } else if kv := keys[i].(*StringValue); kv.Get() != k {
- t.Errorf("Keys[%d] = %q, want %q", i, kv.Get(), k)
+ } else if kv := keys[i]; kv.String() != k {
+ t.Errorf("Keys[%q] = %d, want %d", i, kv.Int(), k)
}
i++
// Check that value lookup is correct.
- vv := mv.Elem(NewValue(k))
- if vi := vv.(*IntValue).Get(); vi != int64(v) {
+ vv := mv.MapIndex(NewValue(k))
+ if vi := vv.Int(); vi != int64(v) {
t.Errorf("Key %q: have value %d, want %d", k, vi, v)
}
// Copy into new map.
- newmap.SetElem(NewValue(k), NewValue(v))
+ newmap.SetMapIndex(NewValue(k), NewValue(v))
}
- vv := mv.Elem(NewValue("not-present"))
- if vv != nil {
+ vv := mv.MapIndex(NewValue("not-present"))
+ if vv.IsValid() {
t.Errorf("Invalid key: got non-nil value %s", valueToString(vv))
}
@@ -932,14 +891,14 @@ func TestMap(t *testing.T) {
}
}
- newmap.SetElem(NewValue("a"), nil)
+ newmap.SetMapIndex(NewValue("a"), Value{})
v, ok := newm["a"]
if ok {
t.Errorf("newm[\"a\"] = %d after delete", v)
}
- mv = NewValue(&m).(*PtrValue).Elem().(*MapValue)
- mv.Set(nil)
+ mv = NewValue(&m).Elem()
+ mv.Set(Zero(mv.Type()))
if m != nil {
t.Errorf("mv.Set(nil) failed")
}
@@ -948,15 +907,15 @@ func TestMap(t *testing.T) {
func TestChan(t *testing.T) {
for loop := 0; loop < 2; loop++ {
var c chan int
- var cv *ChanValue
+ var cv Value
// check both ways to allocate channels
switch loop {
case 1:
c = make(chan int, 1)
- cv = NewValue(c).(*ChanValue)
+ cv = NewValue(c)
case 0:
- cv = MakeChan(Typeof(c).(*ChanType), 1)
+ cv = MakeChan(Typeof(c), 1)
c = cv.Interface().(chan int)
}
@@ -968,28 +927,28 @@ func TestChan(t *testing.T) {
// Recv
c <- 3
- if i := cv.Recv().(*IntValue).Get(); i != 3 {
- t.Errorf("native send 3, reflect Recv %d", i)
+ if i, ok := cv.Recv(); i.Int() != 3 || !ok {
+ t.Errorf("native send 3, reflect Recv %d, %t", i.Int(), ok)
}
// TryRecv fail
- val := cv.TryRecv()
- if val != nil {
- t.Errorf("TryRecv on empty chan: %s", valueToString(val))
+ val, ok := cv.TryRecv()
+ if val.IsValid() || ok {
+ t.Errorf("TryRecv on empty chan: %s, %t", valueToString(val), ok)
}
// TryRecv success
c <- 4
- val = cv.TryRecv()
- if val == nil {
+ val, ok = cv.TryRecv()
+ if !val.IsValid() {
t.Errorf("TryRecv on ready chan got nil")
- } else if i := val.(*IntValue).Get(); i != 4 {
- t.Errorf("native send 4, TryRecv %d", i)
+ } else if i := val.Int(); i != 4 || !ok {
+ t.Errorf("native send 4, TryRecv %d, %t", i, ok)
}
// TrySend fail
c <- 100
- ok := cv.TrySend(NewValue(5))
+ ok = cv.TrySend(NewValue(5))
i := <-c
if ok {
t.Errorf("TrySend on full chan succeeded: value %d", i)
@@ -1008,36 +967,27 @@ func TestChan(t *testing.T) {
// Close
c <- 123
cv.Close()
- if cv.Closed() {
- t.Errorf("closed too soon - 1")
+ if i, ok := cv.Recv(); i.Int() != 123 || !ok {
+ t.Errorf("send 123 then close; Recv %d, %t", i.Int(), ok)
}
- if i := cv.Recv().(*IntValue).Get(); i != 123 {
- t.Errorf("send 123 then close; Recv %d", i)
- }
- if cv.Closed() {
- t.Errorf("closed too soon - 2")
- }
- if i := cv.Recv().(*IntValue).Get(); i != 0 {
- t.Errorf("after close Recv %d", i)
- }
- if !cv.Closed() {
- t.Errorf("not closed")
+ if i, ok := cv.Recv(); i.Int() != 0 || ok {
+ t.Errorf("after close Recv %d, %t", i.Int(), ok)
}
}
// check creation of unbuffered channel
var c chan int
- cv := MakeChan(Typeof(c).(*ChanType), 0)
+ cv := MakeChan(Typeof(c), 0)
c = cv.Interface().(chan int)
if cv.TrySend(NewValue(7)) {
t.Errorf("TrySend on sync chan succeeded")
}
- if cv.TryRecv() != nil {
+ if v, ok := cv.TryRecv(); v.IsValid() || ok {
t.Errorf("TryRecv on sync chan succeeded")
}
// len/cap
- cv = MakeChan(Typeof(c).(*ChanType), 10)
+ cv = MakeChan(Typeof(c), 10)
c = cv.Interface().(chan int)
for i := 0; i < 3; i++ {
c <- i
@@ -1055,14 +1005,14 @@ func dummy(b byte, c int, d byte) (i byte, j int, k byte) {
}
func TestFunc(t *testing.T) {
- ret := NewValue(dummy).(*FuncValue).Call([]Value{NewValue(byte(10)), NewValue(20), NewValue(byte(30))})
+ ret := NewValue(dummy).Call([]Value{NewValue(byte(10)), NewValue(20), NewValue(byte(30))})
if len(ret) != 3 {
t.Fatalf("Call returned %d values, want 3", len(ret))
}
- i := ret[0].(*UintValue).Get()
- j := ret[1].(*IntValue).Get()
- k := ret[2].(*UintValue).Get()
+ i := byte(ret[0].Uint())
+ j := int(ret[1].Int())
+ k := byte(ret[2].Uint())
if i != 10 || j != 20 || k != 30 {
t.Errorf("Call returned %d, %d, %d; want 10, 20, 30", i, j, k)
}
@@ -1077,30 +1027,30 @@ func (p Point) Dist(scale int) int { return p.x*p.x*scale + p.y*p.y*scale }
func TestMethod(t *testing.T) {
// Non-curried method of type.
p := Point{3, 4}
- i := Typeof(p).Method(0).Func.Call([]Value{NewValue(p), NewValue(10)})[0].(*IntValue).Get()
+ i := Typeof(p).Method(0).Func.Call([]Value{NewValue(p), NewValue(10)})[0].Int()
if i != 250 {
t.Errorf("Type Method returned %d; want 250", i)
}
- i = Typeof(&p).Method(0).Func.Call([]Value{NewValue(&p), NewValue(10)})[0].(*IntValue).Get()
+ i = Typeof(&p).Method(0).Func.Call([]Value{NewValue(&p), NewValue(10)})[0].Int()
if i != 250 {
t.Errorf("Pointer Type Method returned %d; want 250", i)
}
// Curried method of value.
- i = NewValue(p).Method(0).Call([]Value{NewValue(10)})[0].(*IntValue).Get()
+ i = NewValue(p).Method(0).Call([]Value{NewValue(10)})[0].Int()
if i != 250 {
t.Errorf("Value Method returned %d; want 250", i)
}
// Curried method of pointer.
- i = NewValue(&p).Method(0).Call([]Value{NewValue(10)})[0].(*IntValue).Get()
+ i = NewValue(&p).Method(0).Call([]Value{NewValue(10)})[0].Int()
if i != 250 {
t.Errorf("Value Method returned %d; want 250", i)
}
// Curried method of pointer to value.
- i = NewValue(p).Addr().Method(0).Call([]Value{NewValue(10)})[0].(*IntValue).Get()
+ i = NewValue(p).Addr().Method(0).Call([]Value{NewValue(10)})[0].Int()
if i != 250 {
t.Errorf("Value Method returned %d; want 250", i)
}
@@ -1114,8 +1064,8 @@ func TestMethod(t *testing.T) {
Dist(int) int
}
}{p}
- pv := NewValue(s).(*StructValue).Field(0)
- i = pv.Method(0).Call([]Value{NewValue(10)})[0].(*IntValue).Get()
+ pv := NewValue(s).Field(0)
+ i = pv.Method(0).Call([]Value{NewValue(10)})[0].Int()
if i != 250 {
t.Errorf("Interface Method returned %d; want 250", i)
}
@@ -1130,19 +1080,19 @@ func TestInterfaceSet(t *testing.T) {
Dist(int) int
}
}
- sv := NewValue(&s).(*PtrValue).Elem().(*StructValue)
- sv.Field(0).(*InterfaceValue).Set(NewValue(p))
+ sv := NewValue(&s).Elem()
+ sv.Field(0).Set(NewValue(p))
if q := s.I.(*Point); q != p {
t.Errorf("i: have %p want %p", q, p)
}
- pv := sv.Field(1).(*InterfaceValue)
+ pv := sv.Field(1)
pv.Set(NewValue(p))
if q := s.P.(*Point); q != p {
t.Errorf("i: have %p want %p", q, p)
}
- i := pv.Method(0).Call([]Value{NewValue(10)})[0].(*IntValue).Get()
+ i := pv.Method(0).Call([]Value{NewValue(10)})[0].Int()
if i != 250 {
t.Errorf("Interface Method returned %d; want 250", i)
}
@@ -1157,7 +1107,7 @@ func TestAnonymousFields(t *testing.T) {
var field StructField
var ok bool
var t1 T1
- type1 := Typeof(t1).(*StructType)
+ type1 := Typeof(t1)
if field, ok = type1.FieldByName("int"); !ok {
t.Error("no field 'int'")
}
@@ -1241,7 +1191,7 @@ var fieldTests = []FTest{
func TestFieldByIndex(t *testing.T) {
for _, test := range fieldTests {
- s := Typeof(test.s).(*StructType)
+ s := Typeof(test.s)
f := s.FieldByIndex(test.index)
if f.Name != "" {
if test.index != nil {
@@ -1256,8 +1206,8 @@ func TestFieldByIndex(t *testing.T) {
}
if test.value != 0 {
- v := NewValue(test.s).(*StructValue).FieldByIndex(test.index)
- if v != nil {
+ v := NewValue(test.s).FieldByIndex(test.index)
+ if v.IsValid() {
if x, ok := v.Interface().(int); ok {
if x != test.value {
t.Errorf("%s%v is %d; want %d", s.Name(), test.index, x, test.value)
@@ -1274,7 +1224,7 @@ func TestFieldByIndex(t *testing.T) {
func TestFieldByName(t *testing.T) {
for _, test := range fieldTests {
- s := Typeof(test.s).(*StructType)
+ s := Typeof(test.s)
f, found := s.FieldByName(test.name)
if found {
if test.index != nil {
@@ -1296,8 +1246,8 @@ func TestFieldByName(t *testing.T) {
}
if test.value != 0 {
- v := NewValue(test.s).(*StructValue).FieldByName(test.name)
- if v != nil {
+ v := NewValue(test.s).FieldByName(test.name)
+ if v.IsValid() {
if x, ok := v.Interface().(int); ok {
if x != test.value {
t.Errorf("%s.%s is %d; want %d", s.Name(), test.name, x, test.value)
@@ -1321,10 +1271,10 @@ func TestImportPath(t *testing.T) {
func TestDotDotDot(t *testing.T) {
// Test example from FuncType.DotDotDot documentation.
var f func(x int, y ...float64)
- typ := Typeof(f).(*FuncType)
+ typ := Typeof(f)
if typ.NumIn() == 2 && typ.In(0) == Typeof(int(0)) {
- sl, ok := typ.In(1).(*SliceType)
- if ok {
+ sl := typ.In(1)
+ if sl.Kind() == Slice {
if sl.Elem() == Typeof(0.0) {
// ok
return
@@ -1355,11 +1305,11 @@ func (*outer) m() {}
func TestNestedMethods(t *testing.T) {
typ := Typeof((*outer)(nil))
- if typ.NumMethod() != 1 || typ.Method(0).Func.Get() != NewValue((*outer).m).(*FuncValue).Get() {
+ if typ.NumMethod() != 1 || typ.Method(0).Func.Pointer() != NewValue((*outer).m).Pointer() {
t.Errorf("Wrong method table for outer: (m=%p)", (*outer).m)
for i := 0; i < typ.NumMethod(); i++ {
m := typ.Method(i)
- t.Errorf("\t%d: %s %#x\n", i, m.Name, m.Func.Get())
+ t.Errorf("\t%d: %s %#x\n", i, m.Name, m.Func.Pointer())
}
}
}
@@ -1379,21 +1329,21 @@ func (i *innerInt) m() int {
func TestEmbeddedMethods(t *testing.T) {
typ := Typeof((*outerInt)(nil))
- if typ.NumMethod() != 1 || typ.Method(0).Func.Get() != NewValue((*outerInt).m).(*FuncValue).Get() {
+ if typ.NumMethod() != 1 || typ.Method(0).Func.Pointer() != NewValue((*outerInt).m).Pointer() {
t.Errorf("Wrong method table for outerInt: (m=%p)", (*outerInt).m)
for i := 0; i < typ.NumMethod(); i++ {
m := typ.Method(i)
- t.Errorf("\t%d: %s %#x\n", i, m.Name, m.Func.Get())
+ t.Errorf("\t%d: %s %#x\n", i, m.Name, m.Func.Pointer())
}
}
i := &innerInt{3}
- if v := NewValue(i).Method(0).Call(nil)[0].(*IntValue).Get(); v != 3 {
+ if v := NewValue(i).Method(0).Call(nil)[0].Int(); v != 3 {
t.Errorf("i.m() = %d, want 3", v)
}
o := &outerInt{1, innerInt{2}}
- if v := NewValue(o).Method(0).Call(nil)[0].(*IntValue).Get(); v != 2 {
+ if v := NewValue(o).Method(0).Call(nil)[0].Int(); v != 2 {
t.Errorf("i.m() = %d, want 2", v)
}
@@ -1411,7 +1361,7 @@ func TestPtrTo(t *testing.T) {
typ = PtrTo(typ)
}
for i = 0; i < 100; i++ {
- typ = typ.(*PtrType).Elem()
+ typ = typ.Elem()
}
if typ != Typeof(i) {
t.Errorf("after 100 PtrTo and Elem, have %s, want %s", typ, Typeof(i))
@@ -1424,11 +1374,11 @@ func TestAddr(t *testing.T) {
}
v := NewValue(&p)
- v = v.(*PtrValue).Elem()
+ v = v.Elem()
v = v.Addr()
- v = v.(*PtrValue).Elem()
- v = v.(*StructValue).Field(0)
- v.(*IntValue).Set(2)
+ v = v.Elem()
+ v = v.Field(0)
+ v.SetInt(2)
if p.X != 2 {
t.Errorf("Addr.Elem.Set failed to set value")
}
@@ -1437,12 +1387,12 @@ func TestAddr(t *testing.T) {
// Exercises generation of PtrTypes not present in the binary.
v = NewValue(&p)
v = v.Addr()
- v = v.(*PtrValue).Elem()
- v = v.(*PtrValue).Elem()
+ v = v.Elem()
+ v = v.Elem()
v = v.Addr()
- v = v.(*PtrValue).Elem()
- v = v.(*StructValue).Field(0)
- v.(*IntValue).Set(3)
+ v = v.Elem()
+ v = v.Field(0)
+ v.SetInt(3)
if p.X != 3 {
t.Errorf("Addr.Elem.Set failed to set value")
}
@@ -1452,9 +1402,9 @@ func TestAddr(t *testing.T) {
v = NewValue(p)
v0 := v
v = v.Addr()
- v = v.(*PtrValue).Elem()
- v = v.(*StructValue).Field(0)
- v.(*IntValue).Set(4)
+ v = v.Elem()
+ v = v.Field(0)
+ v.SetInt(4)
if p.X != 3 { // should be unchanged from last time
t.Errorf("somehow value Set changed original p")
}
diff --git a/src/pkg/reflect/deepequal.go b/src/pkg/reflect/deepequal.go
index c9beec506..f5a781460 100644
--- a/src/pkg/reflect/deepequal.go
+++ b/src/pkg/reflect/deepequal.go
@@ -22,8 +22,8 @@ type visit struct {
// comparisons that have already been seen, which allows short circuiting on
// recursive types.
func deepValueEqual(v1, v2 Value, visited map[uintptr]*visit, depth int) bool {
- if v1 == nil || v2 == nil {
- return v1 == v2
+ if !v1.IsValid() || !v2.IsValid() {
+ return v1.IsValid() == v2.IsValid()
}
if v1.Type() != v2.Type() {
return false
@@ -56,57 +56,47 @@ func deepValueEqual(v1, v2 Value, visited map[uintptr]*visit, depth int) bool {
// Remember for later.
visited[h] = &visit{addr1, addr2, typ, seen}
- switch v := v1.(type) {
- case *ArrayValue:
- arr1 := v
- arr2 := v2.(*ArrayValue)
- if arr1.Len() != arr2.Len() {
+ switch v1.Kind() {
+ case Array:
+ if v1.Len() != v2.Len() {
return false
}
- for i := 0; i < arr1.Len(); i++ {
- if !deepValueEqual(arr1.Elem(i), arr2.Elem(i), visited, depth+1) {
+ for i := 0; i < v1.Len(); i++ {
+ if !deepValueEqual(v1.Index(i), v2.Index(i), visited, depth+1) {
return false
}
}
return true
- case *SliceValue:
- arr1 := v
- arr2 := v2.(*SliceValue)
- if arr1.Len() != arr2.Len() {
+ case Slice:
+ if v1.Len() != v2.Len() {
return false
}
- for i := 0; i < arr1.Len(); i++ {
- if !deepValueEqual(arr1.Elem(i), arr2.Elem(i), visited, depth+1) {
+ for i := 0; i < v1.Len(); i++ {
+ if !deepValueEqual(v1.Index(i), v2.Index(i), visited, depth+1) {
return false
}
}
return true
- case *InterfaceValue:
- i1 := v.Interface()
- i2 := v2.Interface()
- if i1 == nil || i2 == nil {
- return i1 == i2
+ case Interface:
+ if v1.IsNil() || v2.IsNil() {
+ return v1.IsNil() == v2.IsNil()
}
- return deepValueEqual(NewValue(i1), NewValue(i2), visited, depth+1)
- case *PtrValue:
- return deepValueEqual(v.Elem(), v2.(*PtrValue).Elem(), visited, depth+1)
- case *StructValue:
- struct1 := v
- struct2 := v2.(*StructValue)
- for i, n := 0, v.NumField(); i < n; i++ {
- if !deepValueEqual(struct1.Field(i), struct2.Field(i), visited, depth+1) {
+ return deepValueEqual(v1.Elem(), v2.Elem(), visited, depth+1)
+ case Ptr:
+ return deepValueEqual(v1.Elem(), v2.Elem(), visited, depth+1)
+ case Struct:
+ for i, n := 0, v1.NumField(); i < n; i++ {
+ if !deepValueEqual(v1.Field(i), v2.Field(i), visited, depth+1) {
return false
}
}
return true
- case *MapValue:
- map1 := v
- map2 := v2.(*MapValue)
- if map1.Len() != map2.Len() {
+ case Map:
+ if v1.Len() != v2.Len() {
return false
}
- for _, k := range map1.Keys() {
- if !deepValueEqual(map1.Elem(k), map2.Elem(k), visited, depth+1) {
+ for _, k := range v1.MapKeys() {
+ if !deepValueEqual(v1.MapIndex(k), v2.MapIndex(k), visited, depth+1) {
return false
}
}
diff --git a/src/pkg/reflect/tostring_test.go b/src/pkg/reflect/tostring_test.go
index a1487fdd2..5f5c52b77 100644
--- a/src/pkg/reflect/tostring_test.go
+++ b/src/pkg/reflect/tostring_test.go
@@ -17,29 +17,29 @@ import (
// For debugging only.
func valueToString(val Value) string {
var str string
- if val == nil {
- return "<nil>"
+ if !val.IsValid() {
+ return "<zero Value>"
}
typ := val.Type()
- switch val := val.(type) {
- case *IntValue:
- return strconv.Itoa64(val.Get())
- case *UintValue:
- return strconv.Uitoa64(val.Get())
- case *FloatValue:
- return strconv.Ftoa64(float64(val.Get()), 'g', -1)
- case *ComplexValue:
- c := val.Get()
- return strconv.Ftoa64(float64(real(c)), 'g', -1) + "+" + strconv.Ftoa64(float64(imag(c)), 'g', -1) + "i"
- case *StringValue:
- return val.Get()
- case *BoolValue:
- if val.Get() {
+ switch val.Kind() {
+ case Int, Int8, Int16, Int32, Int64:
+ return strconv.Itoa64(val.Int())
+ case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
+ return strconv.Uitoa64(val.Uint())
+ case Float32, Float64:
+ return strconv.Ftoa64(val.Float(), 'g', -1)
+ case Complex64, Complex128:
+ c := val.Complex()
+ return strconv.Ftoa64(real(c), 'g', -1) + "+" + strconv.Ftoa64(imag(c), 'g', -1) + "i"
+ case String:
+ return val.String()
+ case Bool:
+ if val.Bool() {
return "true"
} else {
return "false"
}
- case *PtrValue:
+ case Ptr:
v := val
str = typ.String() + "("
if v.IsNil() {
@@ -49,7 +49,7 @@ func valueToString(val Value) string {
}
str += ")"
return str
- case ArrayOrSliceValue:
+ case Array, Slice:
v := val
str += typ.String()
str += "{"
@@ -57,22 +57,22 @@ func valueToString(val Value) string {
if i > 0 {
str += ", "
}
- str += valueToString(v.Elem(i))
+ str += valueToString(v.Index(i))
}
str += "}"
return str
- case *MapValue:
- t := typ.(*MapType)
+ case Map:
+ t := typ
str = t.String()
str += "{"
str += "<can't iterate on maps>"
str += "}"
return str
- case *ChanValue:
+ case Chan:
str = typ.String()
return str
- case *StructValue:
- t := typ.(*StructType)
+ case Struct:
+ t := typ
v := val
str += t.String()
str += "{"
@@ -84,11 +84,11 @@ func valueToString(val Value) string {
}
str += "}"
return str
- case *InterfaceValue:
+ case Interface:
return typ.String() + "(" + valueToString(val.Elem()) + ")"
- case *FuncValue:
+ case Func:
v := val
- return typ.String() + "(" + strconv.Itoa64(int64(v.Get())) + ")"
+ return typ.String() + "(" + strconv.Uitoa64(uint64(v.Pointer())) + ")"
default:
panic("valueToString: can't print type " + typ.String())
}
diff --git a/src/pkg/reflect/type.go b/src/pkg/reflect/type.go
index 2cc1f576a..9f3e0bf68 100644
--- a/src/pkg/reflect/type.go
+++ b/src/pkg/reflect/type.go
@@ -5,14 +5,11 @@
// The reflect package implements run-time reflection, allowing a program to
// manipulate objects with arbitrary types. The typical use is to take a
// value with static type interface{} and extract its dynamic type
-// information by calling Typeof, which returns an object with interface
-// type Type. That contains a pointer to a struct of type *StructType,
-// *IntType, etc. representing the details of the underlying type. A type
-// switch or type assertion can reveal which.
+// information by calling Typeof, which returns a Type.
//
-// A call to NewValue creates a Value representing the run-time data; it
-// contains a *StructValue, *IntValue, etc. MakeZero takes a Type and
-// returns a Value representing a zero value for that type.
+// A call to NewValue returns a Value representing the run-time data.
+// Zero takes a Type and returns a Value representing a zero value
+// for that type.
package reflect
import (
@@ -22,6 +19,186 @@ import (
"unsafe"
)
+// Type is the representation of a Go type.
+//
+// Not all methods apply to all kinds of types. Restrictions,
+// if any, are noted in the documentation for each method.
+// Use the Kind method to find out the kind of type before
+// calling kind-specific methods. Calling a method
+// inappropriate to the kind of type causes a run-time panic.
+type Type interface {
+ // Methods applicable to all types.
+
+ // Align returns the alignment in bytes of a value of
+ // this type when allocated in memory.
+ Align() int
+
+ // FieldAlign returns the alignment in bytes of a value of
+ // this type when used as a field in a struct.
+ FieldAlign() int
+
+ // Method returns the i'th method in the type's method set.
+ // It panics if i is not in the range [0, NumMethod()).
+ //
+ // For a non-interface type T or *T, the returned Method's Type and Func
+ // fields describe a function whose first argument is the receiver.
+ //
+ // For an interface type, the returned Method's Type field gives the
+ // method signature, without a receiver, and the Func field is nil.
+ Method(int) Method
+
+ // NumMethods returns the number of methods in the type's method set.
+ NumMethod() int
+
+ // Name returns the type's name within its package.
+ // It returns an empty string for unnamed types.
+ Name() string
+
+ // PkgPath returns the type's package path.
+ // The package path is a full package import path like "container/vector".
+ // PkgPath returns an empty string for unnamed types.
+ PkgPath() string
+
+ // Size returns the number of bytes needed to store
+ // a value of the given type; it is analogous to unsafe.Sizeof.
+ Size() uintptr
+
+ // String returns a string representation of the type.
+ // The string representation may use shortened package names
+ // (e.g., vector instead of "container/vector") and is not
+ // guaranteed to be unique among types. To test for equality,
+ // compare the Types directly.
+ String() string
+
+ // Kind returns the specific kind of this type.
+ Kind() Kind
+
+ // Methods applicable only to some types, depending on Kind.
+ // The methods allowed for each kind are:
+ //
+ // Int*, Uint*, Float*, Complex*: Bits
+ // Array: Elem, Len
+ // Chan: ChanDir, Elem
+ // Func: In, NumIn, Out, NumOut, IsVariadic.
+ // Map: Key, Elem
+ // Ptr: Elem
+ // Slice: Elem
+ // Struct: Field, FieldByIndex, FieldByName, FieldByNameFunc, NumField
+
+ // Bits returns the size of the type in bits.
+ // It panics if the type's Kind is not one of the
+ // sized or unsized Int, Uint, Float, or Complex kinds.
+ Bits() int
+
+ // ChanDir returns a channel type's direction.
+ // It panics if the type's Kind is not Chan.
+ ChanDir() ChanDir
+
+ // IsVariadic returns true if a function type's final input parameter
+ // is a "..." parameter. If so, t.In(t.NumIn() - 1) returns the parameter's
+ // implicit actual type []T.
+ //
+ // For concreteness, if t represents func(x int, y ... float), then
+ //
+ // t.NumIn() == 2
+ // t.In(0) is the reflect.Type for "int"
+ // t.In(1) is the reflect.Type for "[]float"
+ // t.IsVariadic() == true
+ //
+ // IsVariadic panics if the type's Kind is not Func.
+ IsVariadic() bool
+
+ // Elem returns a type's element type.
+ // It panics if the type's Kind is not Array, Chan, Map, Ptr, or Slice.
+ Elem() Type
+
+ // Field returns a struct type's i'th field.
+ // It panics if the type's Kind is not Struct.
+ // It panics if i is not in the range [0, NumField()).
+ Field(i int) StructField
+
+ // FieldByIndex returns the nested field corresponding
+ // to the index sequence. It is equivalent to calling Field
+ // successively for each index i.
+ // It panics if the type's Kind is not Struct.
+ FieldByIndex(index []int) StructField
+
+ // FieldByName returns the struct field with the given name
+ // and a boolean indicating if the field was found.
+ FieldByName(name string) (StructField, bool)
+
+ // FieldByNameFunc returns the first struct field with a name
+ // that satisfies the match function and a boolean indicating if
+ // the field was found.
+ FieldByNameFunc(match func(string) bool) (StructField, bool)
+
+ // In returns the type of a function type's i'th input parameter.
+ // It panics if the type's Kind is not Func.
+ // It panics if i is not in the range [0, NumIn()).
+ In(i int) Type
+
+ // Key returns a map type's key type.
+ // It panics if the type's Kind is not Map.
+ Key() Type
+
+ // Len returns an array type's length.
+ // It panics if the type's Kind is not Array.
+ Len() int
+
+ // NumField returns a struct type's field count.
+ // It panics if the type's Kind is not Struct.
+ NumField() int
+
+ // NumIn returns a function type's input parameter count.
+ // It panics if the type's Kind is not Func.
+ NumIn() int
+
+ // NumOut returns a function type's output parameter count.
+ // It panics if the type's Kind is not Func.
+ NumOut() int
+
+ // Out returns the type of a function type's i'th output parameter.
+ // It panics if the type's Kind is not Func.
+ // It panics if i is not in the range [0, NumOut()).
+ Out(i int) Type
+
+ uncommon() *uncommonType
+}
+
+// A Kind represents the specific kind of type that a Type represents.
+// The zero Kind is not a valid kind.
+type Kind uint8
+
+const (
+ Invalid Kind = iota
+ Bool
+ Int
+ Int8
+ Int16
+ Int32
+ Int64
+ Uint
+ Uint8
+ Uint16
+ Uint32
+ Uint64
+ Uintptr
+ Float32
+ Float64
+ Complex64
+ Complex128
+ Array
+ Chan
+ Func
+ Interface
+ Map
+ Ptr
+ Slice
+ String
+ Struct
+ UnsafePointer
+)
+
/*
* Copy of data structures from ../runtime/type.go.
* For comments, see the ones in that file.
@@ -67,48 +244,6 @@ type uncommonType struct {
methods []method
}
-// BoolType represents a boolean type.
-type BoolType struct {
- commonType "bool"
-}
-
-// FloatType represents a float type.
-type FloatType struct {
- commonType "float"
-}
-
-// ComplexType represents a complex type.
-type ComplexType struct {
- commonType "complex"
-}
-
-// IntType represents a signed integer type.
-type IntType struct {
- commonType "int"
-}
-
-// UintType represents a uint type.
-type UintType struct {
- commonType "uint"
-}
-
-// StringType represents a string type.
-type StringType struct {
- commonType "string"
-}
-
-// UnsafePointerType represents an unsafe.Pointer type.
-type UnsafePointerType struct {
- commonType "unsafe.Pointer"
-}
-
-// ArrayType represents a fixed array type.
-type ArrayType struct {
- commonType "array"
- elem *runtime.Type
- len uintptr
-}
-
// ChanDir represents a channel type's direction.
type ChanDir int
@@ -118,57 +253,61 @@ const (
BothDir = RecvDir | SendDir
)
-// ChanType represents a channel type.
-type ChanType struct {
+
+// arrayType represents a fixed array type.
+type arrayType struct {
+ commonType "array"
+ elem *runtime.Type
+ len uintptr
+}
+
+// chanType represents a channel type.
+type chanType struct {
commonType "chan"
elem *runtime.Type
dir uintptr
}
-// FuncType represents a function type.
-type FuncType struct {
+// funcType represents a function type.
+type funcType struct {
commonType "func"
dotdotdot bool
in []*runtime.Type
out []*runtime.Type
}
-// Method on interface type
+// imethod represents a method on an interface type
type imethod struct {
name *string
pkgPath *string
typ *runtime.Type
}
-// InterfaceType represents an interface type.
-type InterfaceType struct {
+// interfaceType represents an interface type.
+type interfaceType struct {
commonType "interface"
methods []imethod
}
-// MapType represents a map type.
-type MapType struct {
+// mapType represents a map type.
+type mapType struct {
commonType "map"
key *runtime.Type
elem *runtime.Type
}
-// PtrType represents a pointer type.
-type PtrType struct {
+// ptrType represents a pointer type.
+type ptrType struct {
commonType "ptr"
elem *runtime.Type
}
-// SliceType represents a slice type.
-type SliceType struct {
+// sliceType represents a slice type.
+type sliceType struct {
commonType "slice"
elem *runtime.Type
}
-// arrayOrSliceType is an unexported method that guarantees only
-// arrays and slices implement ArrayOrSliceType.
-func (*SliceType) arrayOrSliceType() {}
-
// Struct field
type structField struct {
name *string
@@ -178,8 +317,8 @@ type structField struct {
offset uintptr
}
-// StructType represents a struct type.
-type StructType struct {
+// structType represents a struct type.
+type structType struct {
commonType "struct"
fields []structField
}
@@ -194,106 +333,10 @@ type StructType struct {
type Method struct {
PkgPath string // empty for uppercase Name
Name string
- Type *FuncType
- Func *FuncValue
+ Type Type
+ Func Value
}
-// Type is the runtime representation of a Go type.
-// Every type implements the methods listed here.
-// Some types implement additional interfaces;
-// use a type switch to find out what kind of type a Type is.
-// Each type in a program has a unique Type, so == on Types
-// corresponds to Go's type equality.
-type Type interface {
- // PkgPath returns the type's package path.
- // The package path is a full package import path like "container/vector".
- // PkgPath returns an empty string for unnamed types.
- PkgPath() string
-
- // Name returns the type's name within its package.
- // Name returns an empty string for unnamed types.
- Name() string
-
- // String returns a string representation of the type.
- // The string representation may use shortened package names
- // (e.g., vector instead of "container/vector") and is not
- // guaranteed to be unique among types. To test for equality,
- // compare the Types directly.
- String() string
-
- // Size returns the number of bytes needed to store
- // a value of the given type; it is analogous to unsafe.Sizeof.
- Size() uintptr
-
- // Bits returns the size of the type in bits.
- // It is intended for use with numeric types and may overflow
- // when used for composite types.
- Bits() int
-
- // Align returns the alignment of a value of this type
- // when allocated in memory.
- Align() int
-
- // FieldAlign returns the alignment of a value of this type
- // when used as a field in a struct.
- FieldAlign() int
-
- // Kind returns the specific kind of this type.
- Kind() Kind
-
- // Method returns the i'th method in the type's method set.
- //
- // For a non-interface type T or *T, the returned Method's Type and Func
- // fields describe a function whose first argument is the receiver.
- //
- // For an interface type, the returned Method's Type field gives the
- // method signature, without a receiver, and the Func field is nil.
- Method(int) Method
-
- // NumMethods returns the number of methods in the type's method set.
- NumMethod() int
-
- common() *commonType
- uncommon() *uncommonType
-}
-
-// A Kind represents the specific kind of type that a Type represents.
-// For numeric types, the Kind gives more information than the Type's
-// dynamic type. For example, the Type of a float32 is FloatType, but
-// the Kind is Float32.
-//
-// The zero Kind is not a valid kind.
-type Kind uint8
-
-const (
- Bool Kind = 1 + iota
- Int
- Int8
- Int16
- Int32
- Int64
- Uint
- Uint8
- Uint16
- Uint32
- Uint64
- Uintptr
- Float32
- Float64
- Complex64
- Complex128
- Array
- Chan
- Func
- Interface
- Map
- Ptr
- Slice
- String
- Struct
- UnsafePointer
-)
-
// High bit says whether type has
// embedded pointers,to help garbage collector.
const kindMask = 0x7f
@@ -306,6 +349,7 @@ func (k Kind) String() string {
}
var kindNames = []string{
+ Invalid: "invalid",
Bool: "bool",
Int: "int",
Int8: "int8",
@@ -352,11 +396,24 @@ func (t *uncommonType) Name() string {
return *t.name
}
+func (t *commonType) toType() Type {
+ if t == nil {
+ return nil
+ }
+ return t
+}
+
func (t *commonType) String() string { return *t.string }
func (t *commonType) Size() uintptr { return t.size }
-func (t *commonType) Bits() int { return int(t.size * 8) }
+func (t *commonType) Bits() int {
+ k := t.Kind()
+ if k < Int || k > Complex128 {
+ panic("reflect: Bits of non-arithmetic Type")
+ }
+ return int(t.size) * 8
+}
func (t *commonType) Align() int { return int(t.align) }
@@ -377,9 +434,9 @@ func (t *uncommonType) Method(i int) (m Method) {
if p.pkgPath != nil {
m.PkgPath = *p.pkgPath
}
- m.Type = toType(*p.typ).(*FuncType)
+ m.Type = toType(p.typ)
fn := p.tfn
- m.Func = &FuncValue{value: value{m.Type, addr(&fn), canSet}}
+ m.Func = Value{&funcValue{value: value{m.Type, addr(&fn), canSet}}}
return
}
@@ -393,79 +450,169 @@ func (t *uncommonType) NumMethod() int {
// TODO(rsc): 6g supplies these, but they are not
// as efficient as they could be: they have commonType
// as the receiver instead of *commonType.
-func (t *commonType) NumMethod() int { return t.uncommonType.NumMethod() }
+func (t *commonType) NumMethod() int {
+ if t.Kind() == Interface {
+ tt := (*interfaceType)(unsafe.Pointer(t))
+ return tt.NumMethod()
+ }
+ return t.uncommonType.NumMethod()
+}
-func (t *commonType) Method(i int) (m Method) { return t.uncommonType.Method(i) }
+func (t *commonType) Method(i int) (m Method) {
+ if t.Kind() == Interface {
+ tt := (*interfaceType)(unsafe.Pointer(t))
+ return tt.Method(i)
+ }
+ return t.uncommonType.Method(i)
+}
-func (t *commonType) PkgPath() string { return t.uncommonType.PkgPath() }
+func (t *commonType) PkgPath() string {
+ return t.uncommonType.PkgPath()
+}
-func (t *commonType) Name() string { return t.uncommonType.Name() }
+func (t *commonType) Name() string {
+ return t.uncommonType.Name()
+}
-// Len returns the number of elements in the array.
-func (t *ArrayType) Len() int { return int(t.len) }
+func (t *commonType) ChanDir() ChanDir {
+ if t.Kind() != Chan {
+ panic("reflect: ChanDir of non-chan type")
+ }
+ tt := (*chanType)(unsafe.Pointer(t))
+ return ChanDir(tt.dir)
+}
-// Elem returns the type of the array's elements.
-func (t *ArrayType) Elem() Type { return toType(*t.elem) }
+func (t *commonType) IsVariadic() bool {
+ if t.Kind() != Func {
+ panic("reflect: IsVariadic of non-func type")
+ }
+ tt := (*funcType)(unsafe.Pointer(t))
+ return tt.dotdotdot
+}
+
+func (t *commonType) Elem() Type {
+ switch t.Kind() {
+ case Array:
+ tt := (*arrayType)(unsafe.Pointer(t))
+ return toType(tt.elem)
+ case Chan:
+ tt := (*chanType)(unsafe.Pointer(t))
+ return toType(tt.elem)
+ case Map:
+ tt := (*mapType)(unsafe.Pointer(t))
+ return toType(tt.elem)
+ case Ptr:
+ tt := (*ptrType)(unsafe.Pointer(t))
+ return toType(tt.elem)
+ case Slice:
+ tt := (*sliceType)(unsafe.Pointer(t))
+ return toType(tt.elem)
+ }
+ panic("reflect; Elem of invalid type")
+}
-// arrayOrSliceType is an unexported method that guarantees only
-// arrays and slices implement ArrayOrSliceType.
-func (*ArrayType) arrayOrSliceType() {}
+func (t *commonType) Field(i int) StructField {
+ if t.Kind() != Struct {
+ panic("reflect: Field of non-struct type")
+ }
+ tt := (*structType)(unsafe.Pointer(t))
+ return tt.Field(i)
+}
-// Dir returns the channel direction.
-func (t *ChanType) Dir() ChanDir { return ChanDir(t.dir) }
+func (t *commonType) FieldByIndex(index []int) StructField {
+ if t.Kind() != Struct {
+ panic("reflect: FieldByIndex of non-struct type")
+ }
+ tt := (*structType)(unsafe.Pointer(t))
+ return tt.FieldByIndex(index)
+}
-// Elem returns the channel's element type.
-func (t *ChanType) Elem() Type { return toType(*t.elem) }
+func (t *commonType) FieldByName(name string) (StructField, bool) {
+ if t.Kind() != Struct {
+ panic("reflect: FieldByName of non-struct type")
+ }
+ tt := (*structType)(unsafe.Pointer(t))
+ return tt.FieldByName(name)
+}
-func (d ChanDir) String() string {
- switch d {
- case SendDir:
- return "chan<-"
- case RecvDir:
- return "<-chan"
- case BothDir:
- return "chan"
+func (t *commonType) FieldByNameFunc(match func(string) bool) (StructField, bool) {
+ if t.Kind() != Struct {
+ panic("reflect: FieldByNameFunc of non-struct type")
}
- return "ChanDir" + strconv.Itoa(int(d))
+ tt := (*structType)(unsafe.Pointer(t))
+ return tt.FieldByNameFunc(match)
}
-// In returns the type of the i'th function input parameter.
-func (t *FuncType) In(i int) Type {
- if i < 0 || i >= len(t.in) {
- return nil
+func (t *commonType) In(i int) Type {
+ if t.Kind() != Func {
+ panic("reflect: In of non-func type")
}
- return toType(*t.in[i])
+ tt := (*funcType)(unsafe.Pointer(t))
+ return toType(tt.in[i])
}
-// DotDotDot returns true if the final function input parameter
-// is a "..." parameter. If so, t.In(t.NumIn() - 1) returns the
-// parameter's underlying static type []T.
-//
-// For concreteness, if t is func(x int, y ... float), then
-//
-// t.NumIn() == 2
-// t.In(0) is the reflect.Type for "int"
-// t.In(1) is the reflect.Type for "[]float"
-// t.DotDotDot() == true
-//
-func (t *FuncType) DotDotDot() bool { return t.dotdotdot }
+func (t *commonType) Key() Type {
+ if t.Kind() != Map {
+ panic("reflect: Key of non-map type")
+ }
+ tt := (*mapType)(unsafe.Pointer(t))
+ return toType(tt.key)
+}
-// NumIn returns the number of input parameters.
-func (t *FuncType) NumIn() int { return len(t.in) }
+func (t *commonType) Len() int {
+ if t.Kind() != Array {
+ panic("reflect: Len of non-array type")
+ }
+ tt := (*arrayType)(unsafe.Pointer(t))
+ return int(tt.len)
+}
-// Out returns the type of the i'th function output parameter.
-func (t *FuncType) Out(i int) Type {
- if i < 0 || i >= len(t.out) {
- return nil
+func (t *commonType) NumField() int {
+ if t.Kind() != Struct {
+ panic("reflect: NumField of non-struct type")
}
- return toType(*t.out[i])
+ tt := (*structType)(unsafe.Pointer(t))
+ return len(tt.fields)
}
-// NumOut returns the number of function output parameters.
-func (t *FuncType) NumOut() int { return len(t.out) }
+func (t *commonType) NumIn() int {
+ if t.Kind() != Func {
+ panic("reflect; NumIn of non-func type")
+ }
+ tt := (*funcType)(unsafe.Pointer(t))
+ return len(tt.in)
+}
+
+func (t *commonType) NumOut() int {
+ if t.Kind() != Func {
+ panic("reflect; NumOut of non-func type")
+ }
+ tt := (*funcType)(unsafe.Pointer(t))
+ return len(tt.out)
+}
+
+func (t *commonType) Out(i int) Type {
+ if t.Kind() != Func {
+ panic("reflect: Out of non-func type")
+ }
+ tt := (*funcType)(unsafe.Pointer(t))
+ return toType(tt.out[i])
+}
+
+func (d ChanDir) String() string {
+ switch d {
+ case SendDir:
+ return "chan<-"
+ case RecvDir:
+ return "<-chan"
+ case BothDir:
+ return "chan"
+ }
+ return "ChanDir" + strconv.Itoa(int(d))
+}
// Method returns the i'th method in the type's method set.
-func (t *InterfaceType) Method(i int) (m Method) {
+func (t *interfaceType) Method(i int) (m Method) {
if i < 0 || i >= len(t.methods) {
return
}
@@ -474,24 +621,12 @@ func (t *InterfaceType) Method(i int) (m Method) {
if p.pkgPath != nil {
m.PkgPath = *p.pkgPath
}
- m.Type = toType(*p.typ).(*FuncType)
+ m.Type = toType(p.typ)
return
}
// NumMethod returns the number of interface methods in the type's method set.
-func (t *InterfaceType) NumMethod() int { return len(t.methods) }
-
-// Key returns the map key type.
-func (t *MapType) Key() Type { return toType(*t.key) }
-
-// Elem returns the map element type.
-func (t *MapType) Elem() Type { return toType(*t.elem) }
-
-// Elem returns the pointer element type.
-func (t *PtrType) Elem() Type { return toType(*t.elem) }
-
-// Elem returns the type of the slice's elements.
-func (t *SliceType) Elem() Type { return toType(*t.elem) }
+func (t *interfaceType) NumMethod() int { return len(t.methods) }
type StructField struct {
PkgPath string // empty for uppercase Name
@@ -504,18 +639,18 @@ type StructField struct {
}
// Field returns the i'th struct field.
-func (t *StructType) Field(i int) (f StructField) {
+func (t *structType) Field(i int) (f StructField) {
if i < 0 || i >= len(t.fields) {
return
}
p := t.fields[i]
- f.Type = toType(*p.typ)
+ f.Type = toType(p.typ)
if p.name != nil {
f.Name = *p.name
} else {
t := f.Type
- if pt, ok := t.(*PtrType); ok {
- t = pt.Elem()
+ if t.Kind() == Ptr {
+ t = t.Elem()
}
f.Name = t.Name()
f.Anonymous = true
@@ -535,29 +670,24 @@ func (t *StructType) Field(i int) (f StructField) {
// is wrong for FieldByIndex?
// FieldByIndex returns the nested field corresponding to index.
-func (t *StructType) FieldByIndex(index []int) (f StructField) {
+func (t *structType) FieldByIndex(index []int) (f StructField) {
+ f.Type = Type(t.toType())
for i, x := range index {
if i > 0 {
ft := f.Type
- if pt, ok := ft.(*PtrType); ok {
- ft = pt.Elem()
- }
- if st, ok := ft.(*StructType); ok {
- t = st
- } else {
- var f0 StructField
- f = f0
- return
+ if ft.Kind() == Ptr && ft.Elem().Kind() == Struct {
+ ft = ft.Elem()
}
+ f.Type = ft
}
- f = t.Field(x)
+ f = f.Type.Field(x)
}
return
}
const inf = 1 << 30 // infinity - no struct has that many nesting levels
-func (t *StructType) fieldByNameFunc(match func(string) bool, mark map[*StructType]bool, depth int) (ff StructField, fd int) {
+func (t *structType) fieldByNameFunc(match func(string) bool, mark map[*structType]bool, depth int) (ff StructField, fd int) {
fd = inf // field depth
if mark[t] {
@@ -578,8 +708,8 @@ L:
d = depth
case f.Anonymous:
ft := f.Type
- if pt, ok := ft.(*PtrType); ok {
- ft = pt.Elem()
+ if ft.Kind() == Ptr {
+ ft = ft.Elem()
}
switch {
case match(ft.Name()):
@@ -587,7 +717,8 @@ L:
d = depth
case fd > depth:
// No top-level field yet; look inside nested structs.
- if st, ok := ft.(*StructType); ok {
+ if ft.Kind() == Struct {
+ st := (*structType)(unsafe.Pointer(ft.(*commonType)))
f, d = st.fieldByNameFunc(match, mark, depth+1)
}
}
@@ -626,97 +757,54 @@ L:
// FieldByName returns the struct field with the given name
// and a boolean to indicate if the field was found.
-func (t *StructType) FieldByName(name string) (f StructField, present bool) {
+func (t *structType) FieldByName(name string) (f StructField, present bool) {
return t.FieldByNameFunc(func(s string) bool { return s == name })
}
// FieldByNameFunc returns the struct field with a name that satisfies the
// match function and a boolean to indicate if the field was found.
-func (t *StructType) FieldByNameFunc(match func(string) bool) (f StructField, present bool) {
- if ff, fd := t.fieldByNameFunc(match, make(map[*StructType]bool), 0); fd < inf {
+func (t *structType) FieldByNameFunc(match func(string) bool) (f StructField, present bool) {
+ if ff, fd := t.fieldByNameFunc(match, make(map[*structType]bool), 0); fd < inf {
ff.Index = ff.Index[0 : fd+1]
f, present = ff, true
}
return
}
-// NumField returns the number of struct fields.
-func (t *StructType) NumField() int { return len(t.fields) }
-
// Convert runtime type to reflect type.
-// Same memory layouts, different method sets.
-func toType(i interface{}) Type {
- switch v := i.(type) {
- case nil:
- return nil
- case *runtime.BoolType:
- return (*BoolType)(unsafe.Pointer(v))
- case *runtime.FloatType:
- return (*FloatType)(unsafe.Pointer(v))
- case *runtime.ComplexType:
- return (*ComplexType)(unsafe.Pointer(v))
- case *runtime.IntType:
- return (*IntType)(unsafe.Pointer(v))
- case *runtime.StringType:
- return (*StringType)(unsafe.Pointer(v))
- case *runtime.UintType:
- return (*UintType)(unsafe.Pointer(v))
- case *runtime.UnsafePointerType:
- return (*UnsafePointerType)(unsafe.Pointer(v))
- case *runtime.ArrayType:
- return (*ArrayType)(unsafe.Pointer(v))
- case *runtime.ChanType:
- return (*ChanType)(unsafe.Pointer(v))
- case *runtime.FuncType:
- return (*FuncType)(unsafe.Pointer(v))
- case *runtime.InterfaceType:
- return (*InterfaceType)(unsafe.Pointer(v))
- case *runtime.MapType:
- return (*MapType)(unsafe.Pointer(v))
- case *runtime.PtrType:
- return (*PtrType)(unsafe.Pointer(v))
- case *runtime.SliceType:
- return (*SliceType)(unsafe.Pointer(v))
- case *runtime.StructType:
- return (*StructType)(unsafe.Pointer(v))
- }
- println(i)
- panic("toType")
-}
-
-// ArrayOrSliceType is the common interface implemented
-// by both ArrayType and SliceType.
-type ArrayOrSliceType interface {
- Type
- Elem() Type
- arrayOrSliceType() // Guarantees only Array and Slice implement this interface.
+func toType(p *runtime.Type) Type {
+ type hdr struct {
+ x interface{}
+ t commonType
+ }
+ t := &(*hdr)(unsafe.Pointer(p)).t
+ return t.toType()
}
// Typeof returns the reflection Type of the value in the interface{}.
-func Typeof(i interface{}) Type { return toType(unsafe.Typeof(i)) }
+func Typeof(i interface{}) Type {
+ type hdr struct {
+ typ *byte
+ val *commonType
+ }
+ rt := unsafe.Typeof(i)
+ t := (*(*hdr)(unsafe.Pointer(&rt))).val
+ return t.toType()
+}
// ptrMap is the cache for PtrTo.
var ptrMap struct {
sync.RWMutex
- m map[Type]*PtrType
-}
-
-// runtimePtrType is the runtime layout for a *PtrType.
-// The memory immediately before the *PtrType is always
-// the canonical runtime.Type to be used for a *runtime.Type
-// describing this PtrType.
-type runtimePtrType struct {
- runtime.Type
- runtime.PtrType
+ m map[*commonType]*ptrType
}
// PtrTo returns the pointer type with element t.
// For example, if t represents type Foo, PtrTo(t) represents *Foo.
-func PtrTo(t Type) *PtrType {
+func PtrTo(t Type) Type {
// If t records its pointer-to type, use it.
- ct := t.common()
+ ct := t.(*commonType)
if p := ct.ptrToThis; p != nil {
- return toType(*p).(*PtrType)
+ return toType(p)
}
// Otherwise, synthesize one.
@@ -726,35 +814,34 @@ func PtrTo(t Type) *PtrType {
// the type structures in read-only memory.
ptrMap.RLock()
if m := ptrMap.m; m != nil {
- if p := m[t]; p != nil {
+ if p := m[ct]; p != nil {
ptrMap.RUnlock()
- return p
+ return p.commonType.toType()
}
}
ptrMap.RUnlock()
ptrMap.Lock()
if ptrMap.m == nil {
- ptrMap.m = make(map[Type]*PtrType)
+ ptrMap.m = make(map[*commonType]*ptrType)
}
- p := ptrMap.m[t]
+ p := ptrMap.m[ct]
if p != nil {
// some other goroutine won the race and created it
ptrMap.Unlock()
return p
}
- // runtime.Type value is always right before type structure.
- // 2*ptrSize is size of interface header
- rt := (*runtime.Type)(unsafe.Pointer(uintptr(unsafe.Pointer(ct)) - uintptr(unsafe.Sizeof(runtime.Type(nil)))))
-
- rp := new(runtimePtrType)
- rp.Type = &rp.PtrType
+ var rt struct {
+ i runtime.Type
+ ptrType
+ }
+ rt.i = (*runtime.PtrType)(unsafe.Pointer(&rt.ptrType))
- // initialize rp.PtrType using *byte's PtrType as a prototype.
+ // initialize p using *byte's PtrType as a prototype.
// have to do assignment as PtrType, not runtime.PtrType,
// in order to write to unexported fields.
- p = (*PtrType)(unsafe.Pointer(&rp.PtrType))
- bp := (*PtrType)(unsafe.Pointer(unsafe.Typeof((*byte)(nil)).(*runtime.PtrType)))
+ p = &rt.ptrType
+ bp := (*ptrType)(unsafe.Pointer(unsafe.Typeof((*byte)(nil)).(*runtime.PtrType)))
*p = *bp
s := "*" + *ct.string
@@ -769,9 +856,9 @@ func PtrTo(t Type) *PtrType {
p.uncommonType = nil
p.ptrToThis = nil
- p.elem = rt
+ p.elem = (*runtime.Type)(unsafe.Pointer(uintptr(unsafe.Pointer(ct)) - uintptr(unsafe.Offsetof(rt.ptrType))))
- ptrMap.m[t] = (*PtrType)(unsafe.Pointer(&rp.PtrType))
+ ptrMap.m[ct] = p
ptrMap.Unlock()
- return p
+ return p.commonType.toType()
}
diff --git a/src/pkg/reflect/value.go b/src/pkg/reflect/value.go
index 0b70b17f8..ddc31100f 100644
--- a/src/pkg/reflect/value.go
+++ b/src/pkg/reflect/value.go
@@ -41,171 +41,581 @@ func memmove(adst, asrc addr, n uintptr) {
}
}
-// Value is the common interface to reflection values.
-// The implementations of Value (e.g., ArrayValue, StructValue)
-// have additional type-specific methods.
-type Value interface {
- // Type returns the value's type.
- Type() Type
+// Value is the reflection interface to a Go value.
+//
+// Not all methods apply to all kinds of values. Restrictions,
+// if any, are noted in the documentation for each method.
+// Use the Kind method to find out the kind of value before
+// calling kind-specific methods. Calling a method
+// inappropriate to the kind of type causes a run time panic.
+//
+// The zero Value represents no value.
+// Its IsValid method returns false, its Kind method returns Invalid,
+// its String method returns "<invalid Value>", and all other methods panic.
+// Most functions and methods never return an invalid value.
+// If one does, its documentation states the conditions explicitly.
+type Value struct {
+ Internal valueInterface
+}
- // Interface returns the value as an interface{}.
- Interface() interface{}
+// TODO(rsc): This implementation of Value is a just a façade
+// in front of the old implementation, now called valueInterface.
+// A future CL will change it to a real implementation.
+// Changing the API is already a big enough step for one CL.
- // CanSet returns true if the value can be changed.
- // Values obtained by the use of non-exported struct fields
- // can be used in Get but not Set.
- // If CanSet returns false, calling the type-specific Set will panic.
- CanSet() bool
+// A ValueError occurs when a Value method is invoked on
+// a Value that does not support it. Such cases are documented
+// in the description of each method.
+type ValueError struct {
+ Method string
+ Kind Kind
+}
- // SetValue assigns v to the value; v must have the same type as the value.
- SetValue(v Value)
+func (e *ValueError) String() string {
+ if e.Kind == 0 {
+ return "reflect: call of " + e.Method + " on zero Value"
+ }
+ return "reflect: call of " + e.Method + " on " + e.Kind.String() + " Value"
+}
- // CanAddr returns true if the value's address can be obtained with Addr.
- // Such values are called addressable. A value is addressable if it is
- // an element of a slice, an element of an addressable array,
- // a field of an addressable struct, the result of dereferencing a pointer,
- // or the result of a call to NewValue, MakeChan, MakeMap, or MakeZero.
- // If CanAddr returns false, calling Addr will panic.
- CanAddr() bool
+// methodName returns the name of the calling method,
+// assumed to be two stack frames above.
+func methodName() string {
+ pc, _, _, _ := runtime.Caller(2)
+ f := runtime.FuncForPC(pc)
+ if f == nil {
+ return "unknown method"
+ }
+ return f.Name()
+}
- // Addr returns the address of the value.
- // If the value is not addressable, Addr panics.
- // Addr is typically used to obtain a pointer to a struct field or slice element
- // in order to call a method that requires a pointer receiver.
- Addr() *PtrValue
+func (v Value) internal() valueInterface {
+ vi := v.Internal
+ if vi == nil {
+ panic(&ValueError{methodName(), 0})
+ }
+ return vi
+}
- // UnsafeAddr returns a pointer to the underlying data.
- // It is for advanced clients that also import the "unsafe" package.
- UnsafeAddr() uintptr
+func (v Value) panicIfNot(want Kind) valueInterface {
+ vi := v.Internal
+ if vi == nil {
+ panic(&ValueError{methodName(), 0})
+ }
+ if k := vi.Kind(); k != want {
+ panic(&ValueError{methodName(), k})
+ }
+ return vi
+}
- // Method returns a FuncValue corresponding to the value's i'th method.
- // The arguments to a Call on the returned FuncValue
- // should not include a receiver; the FuncValue will use
- // the value as the receiver.
- Method(i int) *FuncValue
+func (v Value) panicIfNots(wants []Kind) valueInterface {
+ vi := v.Internal
+ if vi == nil {
+ panic(&ValueError{methodName(), 0})
+ }
+ k := vi.Kind()
+ for _, want := range wants {
+ if k == want {
+ return vi
+ }
+ }
+ panic(&ValueError{methodName(), k})
+}
- getAddr() addr
+// Addr returns a pointer value representing the address of v.
+// It panics if CanAddr() returns false.
+// Addr is typically used to obtain a pointer to a struct field
+// or slice element in order to call a method that requires a
+// pointer receiver.
+func (v Value) Addr() Value {
+ return v.internal().Addr()
}
-// flags for value
-const (
- canSet uint32 = 1 << iota // can set value (write to *v.addr)
- canAddr // can take address of value
- canStore // can store through value (write to **v.addr)
-)
+// Bool returns v's underlying value.
+// It panics if v's kind is not Bool.
+func (v Value) Bool() bool {
+ u := v.panicIfNot(Bool).(*boolValue)
+ return *(*bool)(u.addr)
+}
-// value is the common implementation of most values.
-// It is embedded in other, public struct types, but always
-// with a unique tag like "uint" or "float" so that the client cannot
-// convert from, say, *UintValue to *FloatValue.
-type value struct {
- typ Type
- addr addr
- flag uint32
+// CanAddr returns true if the value's address can be obtained with Addr.
+// Such values are called addressable. A value is addressable if it is
+// an element of a slice, an element of an addressable array,
+// a field of an addressable struct, the result of dereferencing a pointer,
+// or the result of a call to NewValue, MakeChan, MakeMap, or Zero.
+// If CanAddr returns false, calling Addr will panic.
+func (v Value) CanAddr() bool {
+ return v.internal().CanAddr()
}
-func (v *value) Type() Type { return v.typ }
+// CanSet returns true if the value of v can be changed.
+// Values obtained by the use of unexported struct fields
+// can be read but not set.
+// If CanSet returns false, calling Set or any type-specific
+// setter (e.g., SetBool, SetInt64) will panic.
+func (v Value) CanSet() bool {
+ return v.internal().CanSet()
+}
-func (v *value) Addr() *PtrValue {
- if !v.CanAddr() {
- panic("reflect: cannot take address of value")
+// Call calls the function v with the input parameters in.
+// It panics if v's Kind is not Func.
+// It returns the output parameters as Values.
+func (v Value) Call(in []Value) []Value {
+ fv := v.panicIfNot(Func).(*funcValue)
+ t := fv.Type()
+ nin := len(in)
+ if fv.first != nil && !fv.isInterface {
+ nin++
}
- a := v.addr
- flag := canSet
- if v.CanSet() {
- flag |= canStore
+ if nin != t.NumIn() {
+ panic("funcValue: wrong argument count")
}
- // We could safely set canAddr here too -
- // the caller would get the address of a -
- // but it doesn't match the Go model.
- // The language doesn't let you say &&v.
- return newValue(PtrTo(v.typ), addr(&a), flag).(*PtrValue)
-}
+ nout := t.NumOut()
-func (v *value) UnsafeAddr() uintptr { return uintptr(v.addr) }
+ // Compute arg size & allocate.
+ // This computation is 6g/8g-dependent
+ // and probably wrong for gccgo, but so
+ // is most of this function.
+ size := uintptr(0)
+ if fv.isInterface {
+ // extra word for interface value
+ size += ptrSize
+ }
+ for i := 0; i < nin; i++ {
+ tv := t.In(i)
+ a := uintptr(tv.Align())
+ size = (size + a - 1) &^ (a - 1)
+ size += tv.Size()
+ }
+ size = (size + ptrSize - 1) &^ (ptrSize - 1)
+ for i := 0; i < nout; i++ {
+ tv := t.Out(i)
+ a := uintptr(tv.Align())
+ size = (size + a - 1) &^ (a - 1)
+ size += tv.Size()
+ }
-func (v *value) getAddr() addr { return v.addr }
+ // size must be > 0 in order for &args[0] to be valid.
+ // the argument copying is going to round it up to
+ // a multiple of ptrSize anyway, so make it ptrSize to begin with.
+ if size < ptrSize {
+ size = ptrSize
+ }
-func (v *value) Interface() interface{} {
- if typ, ok := v.typ.(*InterfaceType); ok {
- // There are two different representations of interface values,
- // one if the interface type has methods and one if it doesn't.
- // These two representations require different expressions
- // to extract correctly.
- if typ.NumMethod() == 0 {
- // Extract as interface value without methods.
- return *(*interface{})(v.addr)
+ // round to pointer size
+ size = (size + ptrSize - 1) &^ (ptrSize - 1)
+
+ // Copy into args.
+ //
+ // TODO(rsc): revisit when reference counting happens.
+ // The values are holding up the in references for us,
+ // but something must be done for the out references.
+ // For now make everything look like a pointer by pretending
+ // to allocate a []*int.
+ args := make([]*int, size/ptrSize)
+ ptr := uintptr(unsafe.Pointer(&args[0]))
+ off := uintptr(0)
+ delta := 0
+ if v := fv.first; v != nil {
+ // Hard-wired first argument.
+ if fv.isInterface {
+ // v is a single uninterpreted word
+ memmove(addr(ptr), v.getAddr(), ptrSize)
+ off = ptrSize
+ } else {
+ // v is a real value
+ tv := v.Type()
+ typesMustMatch(t.In(0), tv)
+ n := tv.Size()
+ memmove(addr(ptr), v.getAddr(), n)
+ off = n
+ delta = 1
}
- // Extract from v.addr as interface value with methods.
- return *(*interface {
- m()
- })(v.addr)
}
- return unsafe.Unreflect(v.typ, unsafe.Pointer(v.addr))
+ for i, v := range in {
+ tv := v.Type()
+ typesMustMatch(t.In(i+delta), tv)
+ a := uintptr(tv.Align())
+ off = (off + a - 1) &^ (a - 1)
+ n := tv.Size()
+ memmove(addr(ptr+off), v.internal().getAddr(), n)
+ off += n
+ }
+ off = (off + ptrSize - 1) &^ (ptrSize - 1)
+
+ // Call
+ call(*(**byte)(fv.addr), (*byte)(addr(ptr)), uint32(size))
+
+ // Copy return values out of args.
+ //
+ // TODO(rsc): revisit like above.
+ ret := make([]Value, nout)
+ for i := 0; i < nout; i++ {
+ tv := t.Out(i)
+ a := uintptr(tv.Align())
+ off = (off + a - 1) &^ (a - 1)
+ v := Zero(tv)
+ n := tv.Size()
+ memmove(v.internal().getAddr(), addr(ptr+off), n)
+ ret[i] = v
+ off += n
+ }
+
+ return ret
}
-func (v *value) CanSet() bool { return v.flag&canSet != 0 }
+var capKinds = []Kind{Array, Chan, Slice}
-func (v *value) CanAddr() bool { return v.flag&canAddr != 0 }
+// Cap returns v's capacity.
+// It panics if v's Kind is not Array, Chan, or Slice.
+func (v Value) Cap() int {
+ switch vv := v.panicIfNots(capKinds).(type) {
+ case *arrayValue:
+ return vv.typ.Len()
+ case *chanValue:
+ ch := *(**byte)(vv.addr)
+ return int(chancap(ch))
+ case *sliceValue:
+ return int(vv.slice().Cap)
+ }
+ panic("not reached")
+}
+// Close closes the channel v.
+// It panics if v's Kind is not Chan.
+func (v Value) Close() {
+ vv := v.panicIfNot(Chan).(*chanValue)
-/*
- * basic types
- */
+ ch := *(**byte)(vv.addr)
+ chanclose(ch)
+}
-// BoolValue represents a bool value.
-type BoolValue struct {
- value "bool"
+var complexKinds = []Kind{Complex64, Complex128}
+
+// Complex returns v's underlying value, as a complex128.
+// It panics if v's Kind is not Complex64 or Complex128
+func (v Value) Complex() complex128 {
+ vv := v.panicIfNots(complexKinds).(*complexValue)
+
+ switch vv.typ.Kind() {
+ case Complex64:
+ return complex128(*(*complex64)(vv.addr))
+ case Complex128:
+ return *(*complex128)(vv.addr)
+ }
+ panic("reflect: invalid complex kind")
}
-// Get returns the underlying bool value.
-func (v *BoolValue) Get() bool { return *(*bool)(v.addr) }
+var interfaceOrPtr = []Kind{Interface, Ptr}
-// Set sets v to the value x.
-func (v *BoolValue) Set(x bool) {
- if !v.CanSet() {
- panic(cannotSet)
+// Elem returns the value that the interface v contains
+// or that the pointer v points to.
+// It panics if v's Kind is not Interface or Ptr.
+// It returns the zero Value if v is nil.
+func (v Value) Elem() Value {
+ switch vv := v.panicIfNots(interfaceOrPtr).(type) {
+ case *interfaceValue:
+ return NewValue(vv.Interface())
+ case *ptrValue:
+ if v.IsNil() {
+ return Value{}
+ }
+ flag := canAddr
+ if vv.flag&canStore != 0 {
+ flag |= canSet | canStore
+ }
+ return newValue(vv.typ.Elem(), *(*addr)(vv.addr), flag)
}
- *(*bool)(v.addr) = x
+ panic("not reached")
}
-// Set sets v to the value x.
-func (v *BoolValue) SetValue(x Value) { v.Set(x.(*BoolValue).Get()) }
+// Field returns the i'th field of the struct v.
+// It panics if v's Kind is not Struct.
+func (v Value) Field(i int) Value {
+ vv := v.panicIfNot(Struct).(*structValue)
-// FloatValue represents a float value.
-type FloatValue struct {
- value "float"
+ t := vv.typ
+ if i < 0 || i >= t.NumField() {
+ panic("reflect: Field index out of range")
+ }
+ f := t.Field(i)
+ flag := vv.flag
+ if f.PkgPath != "" {
+ // unexported field
+ flag &^= canSet | canStore
+ }
+ return newValue(f.Type, addr(uintptr(vv.addr)+f.Offset), flag)
+}
+
+// FieldByIndex returns the nested field corresponding to index.
+// It panics if v's Kind is not struct.
+func (v Value) FieldByIndex(index []int) Value {
+ v.panicIfNot(Struct)
+ for i, x := range index {
+ if i > 0 {
+ if v.Kind() == Ptr {
+ v = v.Elem()
+ }
+ if v.Kind() != Struct {
+ return Value{}
+ }
+ }
+ v = v.Field(x)
+ }
+ return v
}
-// Get returns the underlying int value.
-func (v *FloatValue) Get() float64 {
- switch v.typ.Kind() {
+// FieldByName returns the struct field with the given name.
+// It returns the zero Value if no field was found.
+// It panics if v's Kind is not struct.
+func (v Value) FieldByName(name string) Value {
+ if f, ok := v.Type().FieldByName(name); ok {
+ return v.FieldByIndex(f.Index)
+ }
+ return Value{}
+}
+
+// FieldByNameFunc returns the struct field with a name
+// that satisfies the match function.
+// It panics if v's Kind is not struct.
+// It returns the zero Value if no field was found.
+func (v Value) FieldByNameFunc(match func(string) bool) Value {
+ if f, ok := v.Type().FieldByNameFunc(match); ok {
+ return v.FieldByIndex(f.Index)
+ }
+ return Value{}
+}
+
+var floatKinds = []Kind{Float32, Float64}
+
+// Float returns v's underlying value, as an float64.
+// It panics if v's Kind is not Float32 or Float64
+func (v Value) Float() float64 {
+ vv := v.panicIfNots(floatKinds).(*floatValue)
+
+ switch vv.typ.Kind() {
case Float32:
- return float64(*(*float32)(v.addr))
+ return float64(*(*float32)(vv.addr))
case Float64:
- return *(*float64)(v.addr)
+ return *(*float64)(vv.addr)
}
panic("reflect: invalid float kind")
+
}
-// Set sets v to the value x.
-func (v *FloatValue) Set(x float64) {
- if !v.CanSet() {
- panic(cannotSet)
+var arrayOrSlice = []Kind{Array, Slice}
+
+// Index returns v's i'th element.
+// It panics if v's Kind is not Array or Slice.
+func (v Value) Index(i int) Value {
+ switch vv := v.panicIfNots(arrayOrSlice).(type) {
+ case *arrayValue:
+ typ := vv.typ.Elem()
+ n := v.Len()
+ if i < 0 || i >= n {
+ panic("array index out of bounds")
+ }
+ p := addr(uintptr(vv.addr()) + uintptr(i)*typ.Size())
+ return newValue(typ, p, vv.flag)
+ case *sliceValue:
+ typ := vv.typ.Elem()
+ n := v.Len()
+ if i < 0 || i >= n {
+ panic("reflect: slice index out of range")
+ }
+ p := addr(uintptr(vv.addr()) + uintptr(i)*typ.Size())
+ flag := canAddr
+ if vv.flag&canStore != 0 {
+ flag |= canSet | canStore
+ }
+ return newValue(typ, p, flag)
}
- switch v.typ.Kind() {
- default:
- panic("reflect: invalid float kind")
- case Float32:
- *(*float32)(v.addr) = float32(x)
- case Float64:
- *(*float64)(v.addr) = x
+ panic("not reached")
+}
+
+var intKinds = []Kind{Int, Int8, Int16, Int32, Int64}
+
+// Int returns v's underlying value, as an int64.
+// It panics if v's Kind is not a sized or unsized Int kind.
+func (v Value) Int() int64 {
+ vv := v.panicIfNots(intKinds).(*intValue)
+
+ switch vv.typ.Kind() {
+ case Int:
+ return int64(*(*int)(vv.addr))
+ case Int8:
+ return int64(*(*int8)(vv.addr))
+ case Int16:
+ return int64(*(*int16)(vv.addr))
+ case Int32:
+ return int64(*(*int32)(vv.addr))
+ case Int64:
+ return *(*int64)(vv.addr)
+ }
+ panic("reflect: invalid int kind")
+}
+
+// Interface returns v's value as an interface{}.
+// If v is a method obtained by invoking Value.Method
+// (as opposed to Type.Method), Interface cannot return an
+// interface value, so it panics.
+func (v Value) Interface() interface{} {
+ return v.internal().Interface()
+}
+
+// InterfaceData returns the interface v's value as a uintptr pair.
+// It panics if v's Kind is not Interface.
+func (v Value) InterfaceData() [2]uintptr {
+ vv := v.panicIfNot(Interface).(*interfaceValue)
+
+ return *(*[2]uintptr)(vv.addr)
+}
+
+var nilKinds = []Kind{Chan, Func, Interface, Map, Ptr, Slice}
+
+// IsNil returns true if v is a nil value.
+// It panics if v's Kind is not Chan, Func, Interface, Map, Ptr, or Slice.
+func (v Value) IsNil() bool {
+ switch vv := v.panicIfNots(nilKinds).(type) {
+ case *chanValue:
+ return *(*uintptr)(vv.addr) == 0
+ case *funcValue:
+ return *(*uintptr)(vv.addr) == 0
+ case *interfaceValue:
+ return vv.Interface() == nil
+ case *mapValue:
+ return *(*uintptr)(vv.addr) == 0
+ case *ptrValue:
+ return *(*uintptr)(vv.addr) == 0
+ case *sliceValue:
+ return vv.slice().Data == 0
+ }
+ panic("not reached")
+}
+
+// IsValid returns true if v represents a value.
+// It returns false if v is the zero Value.
+// If IsValid returns false, all other methods except String panic.
+// Most functions and methods never return an invalid value.
+// If one does, its documentation states the conditions explicitly.
+func (v Value) IsValid() bool {
+ return v.Internal != nil
+}
+
+// Kind returns v's Kind.
+// If v is the zero Value (IsValid returns false), Kind returns Invalid.
+func (v Value) Kind() Kind {
+ if v.Internal == nil {
+ return Invalid
+ }
+ return v.internal().Kind()
+}
+
+var lenKinds = []Kind{Array, Chan, Map, Slice}
+
+// Len returns v's length.
+// It panics if v's Kind is not Array, Chan, Map, or Slice.
+func (v Value) Len() int {
+ switch vv := v.panicIfNots(lenKinds).(type) {
+ case *arrayValue:
+ return vv.typ.Len()
+ case *chanValue:
+ ch := *(**byte)(vv.addr)
+ return int(chanlen(ch))
+ case *mapValue:
+ m := *(**byte)(vv.addr)
+ if m == nil {
+ return 0
+ }
+ return int(maplen(m))
+ case *sliceValue:
+ return int(vv.slice().Len)
+ }
+ panic("not reached")
+}
+
+// MapIndex returns the value associated with key in the map v.
+// It panics if v's Kind is not Map.
+// It returns the zero Value if key is not found in the map.
+func (v Value) MapIndex(key Value) Value {
+ vv := v.panicIfNot(Map).(*mapValue)
+ t := vv.Type()
+ typesMustMatch(t.Key(), key.Type())
+ m := *(**byte)(vv.addr)
+ if m == nil {
+ return Value{}
+ }
+ newval := Zero(t.Elem())
+ if !mapaccess(m, (*byte)(key.internal().getAddr()), (*byte)(newval.internal().getAddr())) {
+ return Value{}
+ }
+ return newval
+}
+
+// MapKeys returns a slice containing all the keys present in the map,
+// in unspecified order.
+// It panics if v's Kind is not Map.
+func (v Value) MapKeys() []Value {
+ vv := v.panicIfNot(Map).(*mapValue)
+ tk := vv.Type().Key()
+ m := *(**byte)(vv.addr)
+ mlen := int32(0)
+ if m != nil {
+ mlen = maplen(m)
+ }
+ it := mapiterinit(m)
+ a := make([]Value, mlen)
+ var i int
+ for i = 0; i < len(a); i++ {
+ k := Zero(tk)
+ if !mapiterkey(it, (*byte)(k.internal().getAddr())) {
+ break
+ }
+ a[i] = k
+ mapiternext(it)
+ }
+ return a[0:i]
+}
+
+// Method returns a function value corresponding to v's i'th method.
+// The arguments to a Call on the returned function should not include
+// a receiver; the returned function will always use v as the receiver.
+func (v Value) Method(i int) Value {
+ return v.internal().Method(i)
+}
+
+// NumField returns the number of fields in the struct v.
+// It panics if v's Kind is not Struct.
+func (v Value) NumField() int {
+ return v.panicIfNot(Struct).(*structValue).typ.NumField()
+}
+
+// OverflowComplex returns true if the complex128 x cannot be represented by v's type.
+// It panics if v's Kind is not Complex64 or Complex128.
+func (v Value) OverflowComplex(x complex128) bool {
+ vv := v.panicIfNots(complexKinds).(*complexValue)
+
+ if vv.typ.Size() == 16 {
+ return false
+ }
+ r := real(x)
+ i := imag(x)
+ if r < 0 {
+ r = -r
+ }
+ if i < 0 {
+ i = -i
}
+ return math.MaxFloat32 <= r && r <= math.MaxFloat64 ||
+ math.MaxFloat32 <= i && i <= math.MaxFloat64
}
-// Overflow returns true if x cannot be represented by the type of v.
-func (v *FloatValue) Overflow(x float64) bool {
- if v.typ.Size() == 8 {
+// OverflowFloat returns true if the float64 x cannot be represented by v's type.
+// It panics if v's Kind is not Float32 or Float64.
+func (v Value) OverflowFloat(x float64) bool {
+ vv := v.panicIfNots(floatKinds).(*floatValue)
+
+ if vv.typ.Size() == 8 {
return false
}
if x < 0 {
@@ -214,200 +624,612 @@ func (v *FloatValue) Overflow(x float64) bool {
return math.MaxFloat32 < x && x <= math.MaxFloat64
}
-// Set sets v to the value x.
-func (v *FloatValue) SetValue(x Value) { v.Set(x.(*FloatValue).Get()) }
+// OverflowInt returns true if the int64 x cannot be represented by v's type.
+// It panics if v's Kind is not a sized or unsized Int kind.
+func (v Value) OverflowInt(x int64) bool {
+ vv := v.panicIfNots(intKinds).(*intValue)
-// ComplexValue represents a complex value.
-type ComplexValue struct {
- value "complex"
+ bitSize := uint(vv.typ.Bits())
+ trunc := (x << (64 - bitSize)) >> (64 - bitSize)
+ return x != trunc
}
-// Get returns the underlying complex value.
-func (v *ComplexValue) Get() complex128 {
- switch v.typ.Kind() {
- case Complex64:
- return complex128(*(*complex64)(v.addr))
- case Complex128:
- return *(*complex128)(v.addr)
+// OverflowUint returns true if the uint64 x cannot be represented by v's type.
+// It panics if v's Kind is not a sized or unsized Uint kind.
+func (v Value) OverflowUint(x uint64) bool {
+ vv := v.panicIfNots(uintKinds).(*uintValue)
+
+ bitSize := uint(vv.typ.Bits())
+ trunc := (x << (64 - bitSize)) >> (64 - bitSize)
+ return x != trunc
+}
+
+var pointerKinds = []Kind{Chan, Func, Map, Ptr, Slice, UnsafePointer}
+
+// Pointer returns v's value as a uintptr.
+// It returns uintptr instead of unsafe.Pointer so that
+// code using reflect cannot obtain unsafe.Pointers
+// without importing the unsafe package explicitly.
+// It panics if v's Kind is not Chan, Func, Map, Ptr, Slice, or UnsafePointer.
+func (v Value) Pointer() uintptr {
+ switch vv := v.panicIfNots(pointerKinds).(type) {
+ case *chanValue:
+ return *(*uintptr)(vv.addr)
+ case *funcValue:
+ return *(*uintptr)(vv.addr)
+ case *mapValue:
+ return *(*uintptr)(vv.addr)
+ case *ptrValue:
+ return *(*uintptr)(vv.addr)
+ case *sliceValue:
+ typ := vv.typ
+ return uintptr(vv.addr()) + uintptr(v.Cap())*typ.Elem().Size()
+ case *unsafePointerValue:
+ return uintptr(*(*unsafe.Pointer)(vv.addr))
+ }
+ panic("not reached")
+}
+
+// Recv receives and returns a value from the channel v.
+// It panics if v's Kind is not Chan.
+// The receive blocks until a value is ready.
+// The boolean value ok is true if the value x corresponds to a send
+// on the channel, false if it is a zero value received because the channel is closed.
+func (v Value) Recv() (x Value, ok bool) {
+ return v.panicIfNot(Chan).(*chanValue).recv(nil)
+}
+
+// internal recv; non-blocking if selected != nil
+func (v *chanValue) recv(selected *bool) (Value, bool) {
+ t := v.Type()
+ if t.ChanDir()&RecvDir == 0 {
+ panic("recv on send-only channel")
+ }
+ ch := *(**byte)(v.addr)
+ x := Zero(t.Elem())
+ var ok bool
+ chanrecv(ch, (*byte)(x.internal().getAddr()), selected, &ok)
+ return x, ok
+}
+
+// Send sends x on the channel v.
+// It panics if v's kind is not Chan or if x's type is not the same type as v's element type.
+func (v Value) Send(x Value) {
+ v.panicIfNot(Chan).(*chanValue).send(x, nil)
+}
+
+// internal send; non-blocking if selected != nil
+func (v *chanValue) send(x Value, selected *bool) {
+ t := v.Type()
+ if t.ChanDir()&SendDir == 0 {
+ panic("send on recv-only channel")
+ }
+ typesMustMatch(t.Elem(), x.Type())
+ ch := *(**byte)(v.addr)
+ chansend(ch, (*byte)(x.internal().getAddr()), selected)
+}
+
+// Set assigns x to the value v; x must have the same type as v.
+// It panics if CanSet() returns false or if x is the zero Value.
+func (v Value) Set(x Value) {
+ x.internal()
+ switch vv := v.internal().(type) {
+ case *arrayValue:
+ xx := x.panicIfNot(Array).(*arrayValue)
+ if !vv.CanSet() {
+ panic(cannotSet)
+ }
+ typesMustMatch(vv.typ, xx.typ)
+ Copy(v, x)
+
+ case *boolValue:
+ v.SetBool(x.Bool())
+
+ case *chanValue:
+ x := x.panicIfNot(Chan).(*chanValue)
+ if !vv.CanSet() {
+ panic(cannotSet)
+ }
+ typesMustMatch(vv.typ, x.typ)
+ *(*uintptr)(vv.addr) = *(*uintptr)(x.addr)
+
+ case *floatValue:
+ v.SetFloat(x.Float())
+
+ case *funcValue:
+ x := x.panicIfNot(Func).(*funcValue)
+ if !vv.CanSet() {
+ panic(cannotSet)
+ }
+ typesMustMatch(vv.typ, x.typ)
+ *(*uintptr)(vv.addr) = *(*uintptr)(x.addr)
+
+ case *intValue:
+ v.SetInt(x.Int())
+
+ case *interfaceValue:
+ i := x.Interface()
+ if !vv.CanSet() {
+ panic(cannotSet)
+ }
+ // Two different representations; see comment in Get.
+ // Empty interface is easy.
+ t := (*interfaceType)(unsafe.Pointer(vv.typ.(*commonType)))
+ if t.NumMethod() == 0 {
+ *(*interface{})(vv.addr) = i
+ return
+ }
+
+ // Non-empty interface requires a runtime check.
+ setiface(t, &i, vv.addr)
+
+ case *mapValue:
+ x := x.panicIfNot(Map).(*mapValue)
+ if !vv.CanSet() {
+ panic(cannotSet)
+ }
+ if x == nil {
+ *(**uintptr)(vv.addr) = nil
+ return
+ }
+ typesMustMatch(vv.typ, x.typ)
+ *(*uintptr)(vv.addr) = *(*uintptr)(x.addr)
+
+ case *ptrValue:
+ x := x.panicIfNot(Ptr).(*ptrValue)
+ if x == nil {
+ *(**uintptr)(vv.addr) = nil
+ return
+ }
+ if !vv.CanSet() {
+ panic(cannotSet)
+ }
+ if x.flag&canStore == 0 {
+ panic("cannot copy pointer obtained from unexported struct field")
+ }
+ typesMustMatch(vv.typ, x.typ)
+ // TODO: This will have to move into the runtime
+ // once the new gc goes in
+ *(*uintptr)(vv.addr) = *(*uintptr)(x.addr)
+
+ case *sliceValue:
+ x := x.panicIfNot(Slice).(*sliceValue)
+ if !vv.CanSet() {
+ panic(cannotSet)
+ }
+ typesMustMatch(vv.typ, x.typ)
+ *vv.slice() = *x.slice()
+
+ case *stringValue:
+ // Do the kind check explicitly, because x.String() does not.
+ x.panicIfNot(String)
+ v.SetString(x.String())
+
+ case *structValue:
+ x := x.panicIfNot(Struct).(*structValue)
+ // TODO: This will have to move into the runtime
+ // once the gc goes in.
+ if !vv.CanSet() {
+ panic(cannotSet)
+ }
+ typesMustMatch(vv.typ, x.typ)
+ memmove(vv.addr, x.addr, vv.typ.Size())
+
+ case *uintValue:
+ v.SetUint(x.Uint())
+
+ case *unsafePointerValue:
+ // Do the kind check explicitly, because x.UnsafePointer
+ // applies to more than just the UnsafePointer Kind.
+ x.panicIfNot(UnsafePointer)
+ v.SetPointer(unsafe.Pointer(x.Pointer()))
}
- panic("reflect: invalid complex kind")
}
-// Set sets v to the value x.
-func (v *ComplexValue) Set(x complex128) {
- if !v.CanSet() {
+// SetBool sets v's underlying value.
+// It panics if v's Kind is not Bool or if CanSet() is false.
+func (v Value) SetBool(x bool) {
+ vv := v.panicIfNot(Bool).(*boolValue)
+
+ if !vv.CanSet() {
+ panic(cannotSet)
+ }
+ *(*bool)(vv.addr) = x
+}
+
+// SetComplex sets v's underlying value to x.
+// It panics if v's Kind is not Complex64 or Complex128, or if CanSet() is false.
+func (v Value) SetComplex(x complex128) {
+ vv := v.panicIfNots(complexKinds).(*complexValue)
+
+ if !vv.CanSet() {
panic(cannotSet)
}
- switch v.typ.Kind() {
+ switch vv.typ.Kind() {
default:
panic("reflect: invalid complex kind")
case Complex64:
- *(*complex64)(v.addr) = complex64(x)
+ *(*complex64)(vv.addr) = complex64(x)
case Complex128:
- *(*complex128)(v.addr) = x
+ *(*complex128)(vv.addr) = x
}
}
-// Set sets v to the value x.
-func (v *ComplexValue) SetValue(x Value) { v.Set(x.(*ComplexValue).Get()) }
+// SetFloat sets v's underlying value to x.
+// It panics if v's Kind is not Float32 or Float64, or if CanSet() is false.
+func (v Value) SetFloat(x float64) {
+ vv := v.panicIfNots(floatKinds).(*floatValue)
-// IntValue represents an int value.
-type IntValue struct {
- value "int"
-}
-
-// Get returns the underlying int value.
-func (v *IntValue) Get() int64 {
- switch v.typ.Kind() {
- case Int:
- return int64(*(*int)(v.addr))
- case Int8:
- return int64(*(*int8)(v.addr))
- case Int16:
- return int64(*(*int16)(v.addr))
- case Int32:
- return int64(*(*int32)(v.addr))
- case Int64:
- return *(*int64)(v.addr)
+ if !vv.CanSet() {
+ panic(cannotSet)
+ }
+ switch vv.typ.Kind() {
+ default:
+ panic("reflect: invalid float kind")
+ case Float32:
+ *(*float32)(vv.addr) = float32(x)
+ case Float64:
+ *(*float64)(vv.addr) = x
}
- panic("reflect: invalid int kind")
}
-// Set sets v to the value x.
-func (v *IntValue) Set(x int64) {
- if !v.CanSet() {
+// SetInt sets v's underlying value to x.
+// It panics if v's Kind is not a sized or unsized Int kind, or if CanSet() is false.
+func (v Value) SetInt(x int64) {
+ vv := v.panicIfNots(intKinds).(*intValue)
+
+ if !vv.CanSet() {
panic(cannotSet)
}
- switch v.typ.Kind() {
+ switch vv.typ.Kind() {
default:
panic("reflect: invalid int kind")
case Int:
- *(*int)(v.addr) = int(x)
+ *(*int)(vv.addr) = int(x)
case Int8:
- *(*int8)(v.addr) = int8(x)
+ *(*int8)(vv.addr) = int8(x)
case Int16:
- *(*int16)(v.addr) = int16(x)
+ *(*int16)(vv.addr) = int16(x)
case Int32:
- *(*int32)(v.addr) = int32(x)
+ *(*int32)(vv.addr) = int32(x)
case Int64:
- *(*int64)(v.addr) = x
+ *(*int64)(vv.addr) = x
}
}
-// Set sets v to the value x.
-func (v *IntValue) SetValue(x Value) { v.Set(x.(*IntValue).Get()) }
-
-// Overflow returns true if x cannot be represented by the type of v.
-func (v *IntValue) Overflow(x int64) bool {
- bitSize := uint(v.typ.Bits())
- trunc := (x << (64 - bitSize)) >> (64 - bitSize)
- return x != trunc
-}
+// SetLen sets v's length to n.
+// It panics if v's Kind is not Slice.
+func (v Value) SetLen(n int) {
+ vv := v.panicIfNot(Slice).(*sliceValue)
-// StringHeader is the runtime representation of a string.
-type StringHeader struct {
- Data uintptr
- Len int
+ s := vv.slice()
+ if n < 0 || n > int(s.Cap) {
+ panic("reflect: slice length out of range in SetLen")
+ }
+ s.Len = n
}
-// StringValue represents a string value.
-type StringValue struct {
- value "string"
+// SetMapIndex sets the value associated with key in the map v to val.
+// It panics if v's Kind is not Map.
+// If val is the zero Value, SetMapIndex deletes the key from the map.
+func (v Value) SetMapIndex(key, val Value) {
+ vv := v.panicIfNot(Map).(*mapValue)
+ t := vv.Type()
+ typesMustMatch(t.Key(), key.Type())
+ var vaddr *byte
+ if val.IsValid() {
+ typesMustMatch(t.Elem(), val.Type())
+ vaddr = (*byte)(val.internal().getAddr())
+ }
+ m := *(**byte)(vv.addr)
+ mapassign(m, (*byte)(key.internal().getAddr()), vaddr)
}
-// Get returns the underlying string value.
-func (v *StringValue) Get() string { return *(*string)(v.addr) }
+// SetUint sets v's underlying value to x.
+// It panics if v's Kind is not a sized or unsized Uint kind, or if CanSet() is false.
+func (v Value) SetUint(x uint64) {
+ vv := v.panicIfNots(uintKinds).(*uintValue)
-// Set sets v to the value x.
-func (v *StringValue) Set(x string) {
- if !v.CanSet() {
+ if !vv.CanSet() {
panic(cannotSet)
}
- *(*string)(v.addr) = x
-}
-
-// Set sets v to the value x.
-func (v *StringValue) SetValue(x Value) { v.Set(x.(*StringValue).Get()) }
-
-// UintValue represents a uint value.
-type UintValue struct {
- value "uint"
-}
-
-// Get returns the underlying uuint value.
-func (v *UintValue) Get() uint64 {
- switch v.typ.Kind() {
+ switch vv.typ.Kind() {
+ default:
+ panic("reflect: invalid uint kind")
case Uint:
- return uint64(*(*uint)(v.addr))
+ *(*uint)(vv.addr) = uint(x)
case Uint8:
- return uint64(*(*uint8)(v.addr))
+ *(*uint8)(vv.addr) = uint8(x)
case Uint16:
- return uint64(*(*uint16)(v.addr))
+ *(*uint16)(vv.addr) = uint16(x)
case Uint32:
- return uint64(*(*uint32)(v.addr))
+ *(*uint32)(vv.addr) = uint32(x)
case Uint64:
- return *(*uint64)(v.addr)
+ *(*uint64)(vv.addr) = x
case Uintptr:
- return uint64(*(*uintptr)(v.addr))
+ *(*uintptr)(vv.addr) = uintptr(x)
}
- panic("reflect: invalid uint kind")
}
-// Set sets v to the value x.
-func (v *UintValue) Set(x uint64) {
- if !v.CanSet() {
+// SetPointer sets the unsafe.Pointer value v to x.
+// It panics if v's Kind is not UnsafePointer.
+func (v Value) SetPointer(x unsafe.Pointer) {
+ vv := v.panicIfNot(UnsafePointer).(*unsafePointerValue)
+
+ if !vv.CanSet() {
panic(cannotSet)
}
- switch v.typ.Kind() {
- default:
- panic("reflect: invalid uint kind")
+ *(*unsafe.Pointer)(vv.addr) = x
+}
+
+// SetString sets v's underlying value to x.
+// It panics if v's Kind is not String or if CanSet() is false.
+func (v Value) SetString(x string) {
+ vv := v.panicIfNot(String).(*stringValue)
+
+ if !vv.CanSet() {
+ panic(cannotSet)
+ }
+ *(*string)(vv.addr) = x
+}
+
+// BUG(rsc): Value.Slice should allow slicing arrays.
+
+// Slice returns a slice of v.
+// It panics if v's Kind is not Slice.
+func (v Value) Slice(beg, end int) Value {
+ vv := v.panicIfNot(Slice).(*sliceValue)
+
+ cap := v.Cap()
+ if beg < 0 || end < beg || end > cap {
+ panic("slice index out of bounds")
+ }
+ typ := vv.typ
+ s := new(SliceHeader)
+ s.Data = uintptr(vv.addr()) + uintptr(beg)*typ.Elem().Size()
+ s.Len = end - beg
+ s.Cap = cap - beg
+
+ // Like the result of Addr, we treat Slice as an
+ // unaddressable temporary, so don't set canAddr.
+ flag := canSet
+ if vv.flag&canStore != 0 {
+ flag |= canStore
+ }
+ return newValue(typ, addr(s), flag)
+}
+
+// String returns the string v's underlying value, as a string.
+// String is a special case because of Go's String method convention.
+// Unlike the other getters, it does not panic if v's Kind is not String.
+// Instead, it returns a string of the form "<T value>" where T is v's type.
+func (v Value) String() string {
+ vi := v.Internal
+ if vi == nil {
+ return "<invalid Value>"
+ }
+ if vi.Kind() == String {
+ vv := vi.(*stringValue)
+ return *(*string)(vv.addr)
+ }
+ return "<" + vi.Type().String() + " Value>"
+}
+
+// TryRecv attempts to receive a value from the channel v but will not block.
+// It panics if v's Kind is not Chan.
+// If the receive cannot finish without blocking, x is the zero Value.
+// The boolean ok is true if the value x corresponds to a send
+// on the channel, false if it is a zero value received because the channel is closed.
+func (v Value) TryRecv() (x Value, ok bool) {
+ vv := v.panicIfNot(Chan).(*chanValue)
+
+ var selected bool
+ x, ok = vv.recv(&selected)
+ if !selected {
+ return Value{}, false
+ }
+ return x, ok
+}
+
+// TrySend attempts to send x on the channel v but will not block.
+// It panics if v's Kind is not Chan.
+// It returns true if the value was sent, false otherwise.
+func (v Value) TrySend(x Value) bool {
+ vv := v.panicIfNot(Chan).(*chanValue)
+
+ var selected bool
+ vv.send(x, &selected)
+ return selected
+}
+
+// Type returns v's type.
+func (v Value) Type() Type {
+ return v.internal().Type()
+}
+
+var uintKinds = []Kind{Uint, Uint8, Uint16, Uint32, Uint64, Uintptr}
+
+// Uint returns v's underlying value, as a uint64.
+// It panics if v's Kind is not a sized or unsized Uint kind.
+func (v Value) Uint() uint64 {
+ vv := v.panicIfNots(uintKinds).(*uintValue)
+
+ switch vv.typ.Kind() {
case Uint:
- *(*uint)(v.addr) = uint(x)
+ return uint64(*(*uint)(vv.addr))
case Uint8:
- *(*uint8)(v.addr) = uint8(x)
+ return uint64(*(*uint8)(vv.addr))
case Uint16:
- *(*uint16)(v.addr) = uint16(x)
+ return uint64(*(*uint16)(vv.addr))
case Uint32:
- *(*uint32)(v.addr) = uint32(x)
+ return uint64(*(*uint32)(vv.addr))
case Uint64:
- *(*uint64)(v.addr) = x
+ return *(*uint64)(vv.addr)
case Uintptr:
- *(*uintptr)(v.addr) = uintptr(x)
+ return uint64(*(*uintptr)(vv.addr))
}
+ panic("reflect: invalid uint kind")
}
-// Overflow returns true if x cannot be represented by the type of v.
-func (v *UintValue) Overflow(x uint64) bool {
- bitSize := uint(v.typ.Bits())
- trunc := (x << (64 - bitSize)) >> (64 - bitSize)
- return x != trunc
+// UnsafeAddr returns a pointer to v's data.
+// It is for advanced clients that also import the "unsafe" package.
+func (v Value) UnsafeAddr() uintptr {
+ return v.internal().UnsafeAddr()
+}
+
+// valueInterface is the common interface to reflection values.
+// The implementations of Value (e.g., arrayValue, structValue)
+// have additional type-specific methods.
+type valueInterface interface {
+ // Type returns the value's type.
+ Type() Type
+
+ // Interface returns the value as an interface{}.
+ Interface() interface{}
+
+ // CanSet returns true if the value can be changed.
+ // Values obtained by the use of non-exported struct fields
+ // can be used in Get but not Set.
+ // If CanSet returns false, calling the type-specific Set will panic.
+ CanSet() bool
+
+ // CanAddr returns true if the value's address can be obtained with Addr.
+ // Such values are called addressable. A value is addressable if it is
+ // an element of a slice, an element of an addressable array,
+ // a field of an addressable struct, the result of dereferencing a pointer,
+ // or the result of a call to NewValue, MakeChan, MakeMap, or Zero.
+ // If CanAddr returns false, calling Addr will panic.
+ CanAddr() bool
+
+ // Addr returns the address of the value.
+ // If the value is not addressable, Addr panics.
+ // Addr is typically used to obtain a pointer to a struct field or slice element
+ // in order to call a method that requires a pointer receiver.
+ Addr() Value
+
+ // UnsafeAddr returns a pointer to the underlying data.
+ // It is for advanced clients that also import the "unsafe" package.
+ UnsafeAddr() uintptr
+
+ // Method returns a funcValue corresponding to the value's i'th method.
+ // The arguments to a Call on the returned funcValue
+ // should not include a receiver; the funcValue will use
+ // the value as the receiver.
+ Method(i int) Value
+
+ Kind() Kind
+
+ getAddr() addr
}
-// Set sets v to the value x.
-func (v *UintValue) SetValue(x Value) { v.Set(x.(*UintValue).Get()) }
+// flags for value
+const (
+ canSet uint32 = 1 << iota // can set value (write to *v.addr)
+ canAddr // can take address of value
+ canStore // can store through value (write to **v.addr)
+)
-// UnsafePointerValue represents an unsafe.Pointer value.
-type UnsafePointerValue struct {
- value "unsafe.Pointer"
+// value is the common implementation of most values.
+// It is embedded in other, public struct types, but always
+// with a unique tag like "uint" or "float" so that the client cannot
+// convert from, say, *uintValue to *floatValue.
+type value struct {
+ typ Type
+ addr addr
+ flag uint32
}
-// Get returns the underlying uintptr value.
-// Get returns uintptr, not unsafe.Pointer, so that
-// programs that do not import "unsafe" cannot
-// obtain a value of unsafe.Pointer type from "reflect".
-func (v *UnsafePointerValue) Get() uintptr { return uintptr(*(*unsafe.Pointer)(v.addr)) }
+func (v *value) Type() Type { return v.typ }
-// Set sets v to the value x.
-func (v *UnsafePointerValue) Set(x unsafe.Pointer) {
- if !v.CanSet() {
- panic(cannotSet)
+func (v *value) Kind() Kind { return v.typ.Kind() }
+
+func (v *value) Addr() Value {
+ if !v.CanAddr() {
+ panic("reflect: cannot take address of value")
}
- *(*unsafe.Pointer)(v.addr) = x
+ a := v.addr
+ flag := canSet
+ if v.CanSet() {
+ flag |= canStore
+ }
+ // We could safely set canAddr here too -
+ // the caller would get the address of a -
+ // but it doesn't match the Go model.
+ // The language doesn't let you say &&v.
+ return newValue(PtrTo(v.typ), addr(&a), flag)
}
-// Set sets v to the value x.
-func (v *UnsafePointerValue) SetValue(x Value) {
- v.Set(unsafe.Pointer(x.(*UnsafePointerValue).Get()))
+func (v *value) UnsafeAddr() uintptr { return uintptr(v.addr) }
+
+func (v *value) getAddr() addr { return v.addr }
+
+func (v *value) Interface() interface{} {
+ typ := v.typ
+ if typ.Kind() == Interface {
+ // There are two different representations of interface values,
+ // one if the interface type has methods and one if it doesn't.
+ // These two representations require different expressions
+ // to extract correctly.
+ if typ.NumMethod() == 0 {
+ // Extract as interface value without methods.
+ return *(*interface{})(v.addr)
+ }
+ // Extract from v.addr as interface value with methods.
+ return *(*interface {
+ m()
+ })(v.addr)
+ }
+ return unsafe.Unreflect(v.typ, unsafe.Pointer(v.addr))
+}
+
+func (v *value) CanSet() bool { return v.flag&canSet != 0 }
+
+func (v *value) CanAddr() bool { return v.flag&canAddr != 0 }
+
+
+/*
+ * basic types
+ */
+
+// boolValue represents a bool value.
+type boolValue struct {
+ value "bool"
+}
+
+// floatValue represents a float value.
+type floatValue struct {
+ value "float"
+}
+
+// complexValue represents a complex value.
+type complexValue struct {
+ value "complex"
+}
+
+// intValue represents an int value.
+type intValue struct {
+ value "int"
+}
+
+// StringHeader is the runtime representation of a string.
+type StringHeader struct {
+ Data uintptr
+ Len int
+}
+
+// stringValue represents a string value.
+type stringValue struct {
+ value "string"
+}
+
+// uintValue represents a uint value.
+type uintValue struct {
+ value "uint"
+}
+
+// unsafePointerValue represents an unsafe.Pointer value.
+type unsafePointerValue struct {
+ value "unsafe.Pointer"
}
func typesMustMatch(t1, t2 Type) {
@@ -421,18 +1243,15 @@ func typesMustMatch(t1, t2 Type) {
*/
// ArrayOrSliceValue is the common interface
-// implemented by both ArrayValue and SliceValue.
-type ArrayOrSliceValue interface {
- Value
- Len() int
- Cap() int
- Elem(i int) Value
+// implemented by both arrayValue and sliceValue.
+type arrayOrSliceValue interface {
+ valueInterface
addr() addr
}
// grow grows the slice s so that it can hold extra more values, allocating
// more capacity if needed. It also returns the old and new slice lengths.
-func grow(s *SliceValue, extra int) (*SliceValue, int, int) {
+func grow(s Value, extra int) (Value, int, int) {
i0 := s.Len()
i1 := i0 + extra
if i1 < i0 {
@@ -453,24 +1272,25 @@ func grow(s *SliceValue, extra int) (*SliceValue, int, int) {
}
}
}
- t := MakeSlice(s.Type().(*SliceType), i1, m)
+ t := MakeSlice(s.Type(), i1, m)
Copy(t, s)
return t, i0, i1
}
// Append appends the values x to a slice s and returns the resulting slice.
// Each x must have the same type as s' element type.
-func Append(s *SliceValue, x ...Value) *SliceValue {
+func Append(s Value, x ...Value) Value {
s, i0, i1 := grow(s, len(x))
+ s.panicIfNot(Slice)
for i, j := i0, 0; i < i1; i, j = i+1, j+1 {
- s.Elem(i).SetValue(x[j])
+ s.Index(i).Set(x[j])
}
return s
}
// AppendSlice appends a slice t to a slice s and returns the resulting slice.
// The slices s and t must have the same element type.
-func AppendSlice(s, t *SliceValue) *SliceValue {
+func AppendSlice(s, t Value) Value {
s, i0, i1 := grow(s, t.Len())
Copy(s.Slice(i0, i1), t)
return s
@@ -479,58 +1299,31 @@ func AppendSlice(s, t *SliceValue) *SliceValue {
// Copy copies the contents of src into dst until either
// dst has been filled or src has been exhausted.
// It returns the number of elements copied.
-// The arrays dst and src must have the same element type.
-func Copy(dst, src ArrayOrSliceValue) int {
+// Dst and src each must be a slice or array, and they
+// must have the same element type.
+func Copy(dst, src Value) int {
// TODO: This will have to move into the runtime
// once the real gc goes in.
- de := dst.Type().(ArrayOrSliceType).Elem()
- se := src.Type().(ArrayOrSliceType).Elem()
+ de := dst.Type().Elem()
+ se := src.Type().Elem()
typesMustMatch(de, se)
n := dst.Len()
if xn := src.Len(); n > xn {
n = xn
}
- memmove(dst.addr(), src.addr(), uintptr(n)*de.Size())
+ memmove(dst.panicIfNots(arrayOrSlice).(arrayOrSliceValue).addr(),
+ src.panicIfNots(arrayOrSlice).(arrayOrSliceValue).addr(),
+ uintptr(n)*de.Size())
return n
}
-// An ArrayValue represents an array.
-type ArrayValue struct {
+// An arrayValue represents an array.
+type arrayValue struct {
value "array"
}
-// Len returns the length of the array.
-func (v *ArrayValue) Len() int { return v.typ.(*ArrayType).Len() }
-
-// Cap returns the capacity of the array (equal to Len()).
-func (v *ArrayValue) Cap() int { return v.typ.(*ArrayType).Len() }
-
// addr returns the base address of the data in the array.
-func (v *ArrayValue) addr() addr { return v.value.addr }
-
-// Set assigns x to v.
-// The new value x must have the same type as v.
-func (v *ArrayValue) Set(x *ArrayValue) {
- if !v.CanSet() {
- panic(cannotSet)
- }
- typesMustMatch(v.typ, x.typ)
- Copy(v, x)
-}
-
-// Set sets v to the value x.
-func (v *ArrayValue) SetValue(x Value) { v.Set(x.(*ArrayValue)) }
-
-// Elem returns the i'th element of v.
-func (v *ArrayValue) Elem(i int) Value {
- typ := v.typ.(*ArrayType).Elem()
- n := v.Len()
- if i < 0 || i >= n {
- panic("array index out of bounds")
- }
- p := addr(uintptr(v.addr()) + uintptr(i)*typ.Size())
- return newValue(typ, p, v.flag)
-}
+func (v *arrayValue) addr() addr { return v.value.addr }
/*
* slice
@@ -543,221 +1336,61 @@ type SliceHeader struct {
Cap int
}
-// A SliceValue represents a slice.
-type SliceValue struct {
+// A sliceValue represents a slice.
+type sliceValue struct {
value "slice"
}
-func (v *SliceValue) slice() *SliceHeader { return (*SliceHeader)(v.value.addr) }
-
-// IsNil returns whether v is a nil slice.
-func (v *SliceValue) IsNil() bool { return v.slice().Data == 0 }
-
-// Len returns the length of the slice.
-func (v *SliceValue) Len() int { return int(v.slice().Len) }
-
-// Cap returns the capacity of the slice.
-func (v *SliceValue) Cap() int { return int(v.slice().Cap) }
+func (v *sliceValue) slice() *SliceHeader { return (*SliceHeader)(v.value.addr) }
// addr returns the base address of the data in the slice.
-func (v *SliceValue) addr() addr { return addr(v.slice().Data) }
-
-// SetLen changes the length of v.
-// The new length n must be between 0 and the capacity, inclusive.
-func (v *SliceValue) SetLen(n int) {
- s := v.slice()
- if n < 0 || n > int(s.Cap) {
- panic("reflect: slice length out of range in SetLen")
- }
- s.Len = n
-}
-
-// Set assigns x to v.
-// The new value x must have the same type as v.
-func (v *SliceValue) Set(x *SliceValue) {
- if !v.CanSet() {
- panic(cannotSet)
- }
- typesMustMatch(v.typ, x.typ)
- *v.slice() = *x.slice()
-}
-
-// Set sets v to the value x.
-func (v *SliceValue) SetValue(x Value) { v.Set(x.(*SliceValue)) }
-
-// Get returns the uintptr address of the v.Cap()'th element. This gives
-// the same result for all slices of the same array.
-// It is mainly useful for printing.
-func (v *SliceValue) Get() uintptr {
- typ := v.typ.(*SliceType)
- return uintptr(v.addr()) + uintptr(v.Cap())*typ.Elem().Size()
-}
-
-// Slice returns a sub-slice of the slice v.
-func (v *SliceValue) Slice(beg, end int) *SliceValue {
- cap := v.Cap()
- if beg < 0 || end < beg || end > cap {
- panic("slice index out of bounds")
- }
- typ := v.typ.(*SliceType)
- s := new(SliceHeader)
- s.Data = uintptr(v.addr()) + uintptr(beg)*typ.Elem().Size()
- s.Len = end - beg
- s.Cap = cap - beg
-
- // Like the result of Addr, we treat Slice as an
- // unaddressable temporary, so don't set canAddr.
- flag := canSet
- if v.flag&canStore != 0 {
- flag |= canStore
- }
- return newValue(typ, addr(s), flag).(*SliceValue)
-}
-
-// Elem returns the i'th element of v.
-func (v *SliceValue) Elem(i int) Value {
- typ := v.typ.(*SliceType).Elem()
- n := v.Len()
- if i < 0 || i >= n {
- panic("reflect: slice index out of range")
- }
- p := addr(uintptr(v.addr()) + uintptr(i)*typ.Size())
- flag := canAddr
- if v.flag&canStore != 0 {
- flag |= canSet | canStore
- }
- return newValue(typ, p, flag)
-}
+func (v *sliceValue) addr() addr { return addr(v.slice().Data) }
// MakeSlice creates a new zero-initialized slice value
// for the specified slice type, length, and capacity.
-func MakeSlice(typ *SliceType, len, cap int) *SliceValue {
+func MakeSlice(typ Type, len, cap int) Value {
+ if typ.Kind() != Slice {
+ panic("reflect: MakeSlice of non-slice type")
+ }
s := &SliceHeader{
Data: uintptr(unsafe.NewArray(typ.Elem(), cap)),
Len: len,
Cap: cap,
}
- return newValue(typ, addr(s), canAddr|canSet|canStore).(*SliceValue)
+ return newValue(typ, addr(s), canAddr|canSet|canStore)
}
/*
* chan
*/
-// A ChanValue represents a chan.
-type ChanValue struct {
+// A chanValue represents a chan.
+type chanValue struct {
value "chan"
}
-// IsNil returns whether v is a nil channel.
-func (v *ChanValue) IsNil() bool { return *(*uintptr)(v.addr) == 0 }
-
-// Set assigns x to v.
-// The new value x must have the same type as v.
-func (v *ChanValue) Set(x *ChanValue) {
- if !v.CanSet() {
- panic(cannotSet)
- }
- typesMustMatch(v.typ, x.typ)
- *(*uintptr)(v.addr) = *(*uintptr)(x.addr)
-}
-
-// Set sets v to the value x.
-func (v *ChanValue) SetValue(x Value) { v.Set(x.(*ChanValue)) }
-
-// Get returns the uintptr value of v.
-// It is mainly useful for printing.
-func (v *ChanValue) Get() uintptr { return *(*uintptr)(v.addr) }
-
// implemented in ../pkg/runtime/reflect.cgo
func makechan(typ *runtime.ChanType, size uint32) (ch *byte)
-func chansend(ch, val *byte, pres *bool)
-func chanrecv(ch, val *byte, pres *bool)
-func chanclosed(ch *byte) bool
+func chansend(ch, val *byte, selected *bool)
+func chanrecv(ch, val *byte, selected *bool, ok *bool)
func chanclose(ch *byte)
func chanlen(ch *byte) int32
func chancap(ch *byte) int32
-// Closed returns the result of closed(c) on the underlying channel.
-func (v *ChanValue) Closed() bool {
- ch := *(**byte)(v.addr)
- return chanclosed(ch)
-}
-
-// Close closes the channel.
-func (v *ChanValue) Close() {
- ch := *(**byte)(v.addr)
- chanclose(ch)
-}
-
-func (v *ChanValue) Len() int {
- ch := *(**byte)(v.addr)
- return int(chanlen(ch))
-}
-
-func (v *ChanValue) Cap() int {
- ch := *(**byte)(v.addr)
- return int(chancap(ch))
-}
-
-// internal send; non-blocking if b != nil
-func (v *ChanValue) send(x Value, b *bool) {
- t := v.Type().(*ChanType)
- if t.Dir()&SendDir == 0 {
- panic("send on recv-only channel")
- }
- typesMustMatch(t.Elem(), x.Type())
- ch := *(**byte)(v.addr)
- chansend(ch, (*byte)(x.getAddr()), b)
-}
-
-// internal recv; non-blocking if b != nil
-func (v *ChanValue) recv(b *bool) Value {
- t := v.Type().(*ChanType)
- if t.Dir()&RecvDir == 0 {
- panic("recv on send-only channel")
- }
- ch := *(**byte)(v.addr)
- x := MakeZero(t.Elem())
- chanrecv(ch, (*byte)(x.getAddr()), b)
- return x
-}
-
-// Send sends x on the channel v.
-func (v *ChanValue) Send(x Value) { v.send(x, nil) }
-
-// Recv receives and returns a value from the channel v.
-func (v *ChanValue) Recv() Value { return v.recv(nil) }
-
-// TrySend attempts to sends x on the channel v but will not block.
-// It returns true if the value was sent, false otherwise.
-func (v *ChanValue) TrySend(x Value) bool {
- var ok bool
- v.send(x, &ok)
- return ok
-}
-
-// TryRecv attempts to receive a value from the channel v but will not block.
-// It returns the value if one is received, nil otherwise.
-func (v *ChanValue) TryRecv() Value {
- var ok bool
- x := v.recv(&ok)
- if !ok {
- return nil
- }
- return x
-}
-
// MakeChan creates a new channel with the specified type and buffer size.
-func MakeChan(typ *ChanType, buffer int) *ChanValue {
+func MakeChan(typ Type, buffer int) Value {
+ if typ.Kind() != Chan {
+ panic("reflect: MakeChan of non-chan type")
+ }
if buffer < 0 {
panic("MakeChan: negative buffer size")
}
- if typ.Dir() != BothDir {
+ if typ.ChanDir() != BothDir {
panic("MakeChan: unidirectional channel type")
}
- v := MakeZero(typ).(*ChanValue)
- *(**byte)(v.addr) = makechan((*runtime.ChanType)(unsafe.Pointer(typ)), uint32(buffer))
+ v := Zero(typ)
+ ch := v.panicIfNot(Chan).(*chanValue)
+ *(**byte)(ch.addr) = makechan((*runtime.ChanType)(unsafe.Pointer(typ.(*commonType))), uint32(buffer))
return v
}
@@ -765,229 +1398,62 @@ func MakeChan(typ *ChanType, buffer int) *ChanValue {
* func
*/
-// A FuncValue represents a function value.
-type FuncValue struct {
+// A funcValue represents a function value.
+type funcValue struct {
value "func"
first *value
isInterface bool
}
-// IsNil returns whether v is a nil function.
-func (v *FuncValue) IsNil() bool { return *(*uintptr)(v.addr) == 0 }
-
-// Get returns the uintptr value of v.
-// It is mainly useful for printing.
-func (v *FuncValue) Get() uintptr { return *(*uintptr)(v.addr) }
-
-// Set assigns x to v.
-// The new value x must have the same type as v.
-func (v *FuncValue) Set(x *FuncValue) {
- if !v.CanSet() {
- panic(cannotSet)
- }
- typesMustMatch(v.typ, x.typ)
- *(*uintptr)(v.addr) = *(*uintptr)(x.addr)
-}
-
-// Set sets v to the value x.
-func (v *FuncValue) SetValue(x Value) { v.Set(x.(*FuncValue)) }
-
-// Method returns a FuncValue corresponding to v's i'th method.
-// The arguments to a Call on the returned FuncValue
-// should not include a receiver; the FuncValue will use v
+// Method returns a funcValue corresponding to v's i'th method.
+// The arguments to a Call on the returned funcValue
+// should not include a receiver; the funcValue will use v
// as the receiver.
-func (v *value) Method(i int) *FuncValue {
+func (v *value) Method(i int) Value {
t := v.Type().uncommon()
if t == nil || i < 0 || i >= len(t.methods) {
- return nil
+ panic("reflect: Method index out of range")
}
p := &t.methods[i]
fn := p.tfn
- fv := &FuncValue{value: value{toType(*p.typ), addr(&fn), 0}, first: v, isInterface: false}
- return fv
+ fv := &funcValue{value: value{toType(p.typ), addr(&fn), 0}, first: v, isInterface: false}
+ return Value{fv}
}
// implemented in ../pkg/runtime/*/asm.s
func call(fn, arg *byte, n uint32)
-type tiny struct {
- b byte
-}
-
// Interface returns the fv as an interface value.
// If fv is a method obtained by invoking Value.Method
// (as opposed to Type.Method), Interface cannot return an
// interface value, so it panics.
-func (fv *FuncValue) Interface() interface{} {
+func (fv *funcValue) Interface() interface{} {
if fv.first != nil {
- panic("FuncValue: cannot create interface value for method with bound receiver")
+ panic("funcValue: cannot create interface value for method with bound receiver")
}
return fv.value.Interface()
}
-// Call calls the function fv with input parameters in.
-// It returns the function's output parameters as Values.
-func (fv *FuncValue) Call(in []Value) []Value {
- t := fv.Type().(*FuncType)
- nin := len(in)
- if fv.first != nil && !fv.isInterface {
- nin++
- }
- if nin != t.NumIn() {
- panic("FuncValue: wrong argument count")
- }
- nout := t.NumOut()
-
- // Compute arg size & allocate.
- // This computation is 6g/8g-dependent
- // and probably wrong for gccgo, but so
- // is most of this function.
- size := uintptr(0)
- if fv.isInterface {
- // extra word for interface value
- size += ptrSize
- }
- for i := 0; i < nin; i++ {
- tv := t.In(i)
- a := uintptr(tv.Align())
- size = (size + a - 1) &^ (a - 1)
- size += tv.Size()
- }
- size = (size + ptrSize - 1) &^ (ptrSize - 1)
- for i := 0; i < nout; i++ {
- tv := t.Out(i)
- a := uintptr(tv.Align())
- size = (size + a - 1) &^ (a - 1)
- size += tv.Size()
- }
-
- // size must be > 0 in order for &args[0] to be valid.
- // the argument copying is going to round it up to
- // a multiple of ptrSize anyway, so make it ptrSize to begin with.
- if size < ptrSize {
- size = ptrSize
- }
-
- // round to pointer size
- size = (size + ptrSize - 1) &^ (ptrSize - 1)
-
- // Copy into args.
- //
- // TODO(rsc): revisit when reference counting happens.
- // The values are holding up the in references for us,
- // but something must be done for the out references.
- // For now make everything look like a pointer by pretending
- // to allocate a []*int.
- args := make([]*int, size/ptrSize)
- ptr := uintptr(unsafe.Pointer(&args[0]))
- off := uintptr(0)
- delta := 0
- if v := fv.first; v != nil {
- // Hard-wired first argument.
- if fv.isInterface {
- // v is a single uninterpreted word
- memmove(addr(ptr), v.getAddr(), ptrSize)
- off = ptrSize
- } else {
- // v is a real value
- tv := v.Type()
- typesMustMatch(t.In(0), tv)
- n := tv.Size()
- memmove(addr(ptr), v.getAddr(), n)
- off = n
- delta = 1
- }
- }
- for i, v := range in {
- tv := v.Type()
- typesMustMatch(t.In(i+delta), tv)
- a := uintptr(tv.Align())
- off = (off + a - 1) &^ (a - 1)
- n := tv.Size()
- memmove(addr(ptr+off), v.getAddr(), n)
- off += n
- }
- off = (off + ptrSize - 1) &^ (ptrSize - 1)
-
- // Call
- call(*(**byte)(fv.addr), (*byte)(addr(ptr)), uint32(size))
-
- // Copy return values out of args.
- //
- // TODO(rsc): revisit like above.
- ret := make([]Value, nout)
- for i := 0; i < nout; i++ {
- tv := t.Out(i)
- a := uintptr(tv.Align())
- off = (off + a - 1) &^ (a - 1)
- v := MakeZero(tv)
- n := tv.Size()
- memmove(v.getAddr(), addr(ptr+off), n)
- ret[i] = v
- off += n
- }
-
- return ret
-}
-
/*
* interface
*/
-// An InterfaceValue represents an interface value.
-type InterfaceValue struct {
+// An interfaceValue represents an interface value.
+type interfaceValue struct {
value "interface"
}
-// IsNil returns whether v is a nil interface value.
-func (v *InterfaceValue) IsNil() bool { return v.Interface() == nil }
-
-// No single uinptr Get because v.Interface() is available.
-
-// Get returns the two words that represent an interface in the runtime.
-// Those words are useful only when playing unsafe games.
-func (v *InterfaceValue) Get() [2]uintptr {
- return *(*[2]uintptr)(v.addr)
-}
-
-// Elem returns the concrete value stored in the interface value v.
-func (v *InterfaceValue) Elem() Value { return NewValue(v.Interface()) }
-
// ../runtime/reflect.cgo
-func setiface(typ *InterfaceType, x *interface{}, addr addr)
-
-// Set assigns x to v.
-func (v *InterfaceValue) Set(x Value) {
- var i interface{}
- if x != nil {
- i = x.Interface()
- }
- if !v.CanSet() {
- panic(cannotSet)
- }
- // Two different representations; see comment in Get.
- // Empty interface is easy.
- t := v.typ.(*InterfaceType)
- if t.NumMethod() == 0 {
- *(*interface{})(v.addr) = i
- return
- }
+func setiface(typ *interfaceType, x *interface{}, addr addr)
- // Non-empty interface requires a runtime check.
- setiface(t, &i, v.addr)
-}
-
-// Set sets v to the value x.
-func (v *InterfaceValue) SetValue(x Value) { v.Set(x) }
-
-// Method returns a FuncValue corresponding to v's i'th method.
-// The arguments to a Call on the returned FuncValue
-// should not include a receiver; the FuncValue will use v
+// Method returns a funcValue corresponding to v's i'th method.
+// The arguments to a Call on the returned funcValue
+// should not include a receiver; the funcValue will use v
// as the receiver.
-func (v *InterfaceValue) Method(i int) *FuncValue {
- t := v.Type().(*InterfaceType)
+func (v *interfaceValue) Method(i int) Value {
+ t := (*interfaceType)(unsafe.Pointer(v.Type().(*commonType)))
if t == nil || i < 0 || i >= len(t.methods) {
- return nil
+ panic("reflect: Method index out of range")
}
p := &t.methods[i]
@@ -997,49 +1463,19 @@ func (v *InterfaceValue) Method(i int) *FuncValue {
// Function pointer is at p.perm in the table.
fn := tab.Fn[i]
- fv := &FuncValue{value: value{toType(*p.typ), addr(&fn), 0}, first: data, isInterface: true}
- return fv
+ fv := &funcValue{value: value{toType(p.typ), addr(&fn), 0}, first: data, isInterface: true}
+ return Value{fv}
}
/*
* map
*/
-// A MapValue represents a map value.
-type MapValue struct {
+// A mapValue represents a map value.
+type mapValue struct {
value "map"
}
-// IsNil returns whether v is a nil map value.
-func (v *MapValue) IsNil() bool { return *(*uintptr)(v.addr) == 0 }
-
-// Set assigns x to v.
-// The new value x must have the same type as v.
-func (v *MapValue) Set(x *MapValue) {
- if !v.CanSet() {
- panic(cannotSet)
- }
- if x == nil {
- *(**uintptr)(v.addr) = nil
- return
- }
- typesMustMatch(v.typ, x.typ)
- *(*uintptr)(v.addr) = *(*uintptr)(x.addr)
-}
-
-// Set sets v to the value x.
-func (v *MapValue) SetValue(x Value) {
- if x == nil {
- v.Set(nil)
- return
- }
- v.Set(x.(*MapValue))
-}
-
-// Get returns the uintptr value of v.
-// It is mainly useful for printing.
-func (v *MapValue) Get() uintptr { return *(*uintptr)(v.addr) }
-
// implemented in ../pkg/runtime/reflect.cgo
func mapaccess(m, key, val *byte) bool
func mapassign(m, key, val *byte)
@@ -1049,72 +1485,14 @@ func mapiternext(it *byte)
func mapiterkey(it *byte, key *byte) bool
func makemap(t *runtime.MapType) *byte
-// Elem returns the value associated with key in the map v.
-// It returns nil if key is not found in the map.
-func (v *MapValue) Elem(key Value) Value {
- t := v.Type().(*MapType)
- typesMustMatch(t.Key(), key.Type())
- m := *(**byte)(v.addr)
- if m == nil {
- return nil
- }
- newval := MakeZero(t.Elem())
- if !mapaccess(m, (*byte)(key.getAddr()), (*byte)(newval.getAddr())) {
- return nil
- }
- return newval
-}
-
-// SetElem sets the value associated with key in the map v to val.
-// If val is nil, Put deletes the key from map.
-func (v *MapValue) SetElem(key, val Value) {
- t := v.Type().(*MapType)
- typesMustMatch(t.Key(), key.Type())
- var vaddr *byte
- if val != nil {
- typesMustMatch(t.Elem(), val.Type())
- vaddr = (*byte)(val.getAddr())
- }
- m := *(**byte)(v.addr)
- mapassign(m, (*byte)(key.getAddr()), vaddr)
-}
-
-// Len returns the number of keys in the map v.
-func (v *MapValue) Len() int {
- m := *(**byte)(v.addr)
- if m == nil {
- return 0
- }
- return int(maplen(m))
-}
-
-// Keys returns a slice containing all the keys present in the map,
-// in unspecified order.
-func (v *MapValue) Keys() []Value {
- tk := v.Type().(*MapType).Key()
- m := *(**byte)(v.addr)
- mlen := int32(0)
- if m != nil {
- mlen = maplen(m)
- }
- it := mapiterinit(m)
- a := make([]Value, mlen)
- var i int
- for i = 0; i < len(a); i++ {
- k := MakeZero(tk)
- if !mapiterkey(it, (*byte)(k.getAddr())) {
- break
- }
- a[i] = k
- mapiternext(it)
- }
- return a[0:i]
-}
-
// MakeMap creates a new map of the specified type.
-func MakeMap(typ *MapType) *MapValue {
- v := MakeZero(typ).(*MapValue)
- *(**byte)(v.addr) = makemap((*runtime.MapType)(unsafe.Pointer(typ)))
+func MakeMap(typ Type) Value {
+ if typ.Kind() != Map {
+ panic("reflect: MakeMap of non-map type")
+ }
+ v := Zero(typ)
+ m := v.panicIfNot(Map).(*mapValue)
+ *(**byte)(m.addr) = makemap((*runtime.MapType)(unsafe.Pointer(typ.(*commonType))))
return v
}
@@ -1122,221 +1500,88 @@ func MakeMap(typ *MapType) *MapValue {
* ptr
*/
-// A PtrValue represents a pointer.
-type PtrValue struct {
+// A ptrValue represents a pointer.
+type ptrValue struct {
value "ptr"
}
-// IsNil returns whether v is a nil pointer.
-func (v *PtrValue) IsNil() bool { return *(*uintptr)(v.addr) == 0 }
-
-// Get returns the uintptr value of v.
-// It is mainly useful for printing.
-func (v *PtrValue) Get() uintptr { return *(*uintptr)(v.addr) }
-
-// Set assigns x to v.
-// The new value x must have the same type as v, and x.Elem().CanSet() must be true.
-func (v *PtrValue) Set(x *PtrValue) {
- if x == nil {
- *(**uintptr)(v.addr) = nil
- return
- }
- if !v.CanSet() {
- panic(cannotSet)
- }
- if x.flag&canStore == 0 {
- panic("cannot copy pointer obtained from unexported struct field")
- }
- typesMustMatch(v.typ, x.typ)
- // TODO: This will have to move into the runtime
- // once the new gc goes in
- *(*uintptr)(v.addr) = *(*uintptr)(x.addr)
-}
-
-// Set sets v to the value x.
-func (v *PtrValue) SetValue(x Value) {
- if x == nil {
- v.Set(nil)
- return
- }
- v.Set(x.(*PtrValue))
-}
-
-// PointTo changes v to point to x.
-// If x is a nil Value, PointTo sets v to nil.
-func (v *PtrValue) PointTo(x Value) {
- if x == nil {
- *(**uintptr)(v.addr) = nil
- return
- }
- if !x.CanSet() {
- panic("cannot set x; cannot point to x")
- }
- typesMustMatch(v.typ.(*PtrType).Elem(), x.Type())
- // TODO: This will have to move into the runtime
- // once the new gc goes in.
- *(*uintptr)(v.addr) = x.UnsafeAddr()
-}
-
-// Elem returns the value that v points to.
-// If v is a nil pointer, Elem returns a nil Value.
-func (v *PtrValue) Elem() Value {
- if v.IsNil() {
- return nil
- }
- flag := canAddr
- if v.flag&canStore != 0 {
- flag |= canSet | canStore
- }
- return newValue(v.typ.(*PtrType).Elem(), *(*addr)(v.addr), flag)
-}
-
// Indirect returns the value that v points to.
// If v is a nil pointer, Indirect returns a nil Value.
// If v is not a pointer, Indirect returns v.
func Indirect(v Value) Value {
- if pv, ok := v.(*PtrValue); ok {
- return pv.Elem()
+ if v.Kind() != Ptr {
+ return v
}
- return v
+ return v.Elem()
}
/*
* struct
*/
-// A StructValue represents a struct value.
-type StructValue struct {
+// A structValue represents a struct value.
+type structValue struct {
value "struct"
}
-// Set assigns x to v.
-// The new value x must have the same type as v.
-func (v *StructValue) Set(x *StructValue) {
- // TODO: This will have to move into the runtime
- // once the gc goes in.
- if !v.CanSet() {
- panic(cannotSet)
- }
- typesMustMatch(v.typ, x.typ)
- memmove(v.addr, x.addr, v.typ.Size())
-}
-
-// Set sets v to the value x.
-func (v *StructValue) SetValue(x Value) { v.Set(x.(*StructValue)) }
-
-// Field returns the i'th field of the struct.
-func (v *StructValue) Field(i int) Value {
- t := v.typ.(*StructType)
- if i < 0 || i >= t.NumField() {
- return nil
- }
- f := t.Field(i)
- flag := v.flag
- if f.PkgPath != "" {
- // unexported field
- flag &^= canSet | canStore
- }
- return newValue(f.Type, addr(uintptr(v.addr)+f.Offset), flag)
-}
-
-// FieldByIndex returns the nested field corresponding to index.
-func (t *StructValue) FieldByIndex(index []int) (v Value) {
- v = t
- for i, x := range index {
- if i > 0 {
- if p, ok := v.(*PtrValue); ok {
- v = p.Elem()
- }
- if s, ok := v.(*StructValue); ok {
- t = s
- } else {
- v = nil
- return
- }
- }
- v = t.Field(x)
- }
- return
-}
-
-// FieldByName returns the struct field with the given name.
-// The result is nil if no field was found.
-func (t *StructValue) FieldByName(name string) Value {
- if f, ok := t.Type().(*StructType).FieldByName(name); ok {
- return t.FieldByIndex(f.Index)
- }
- return nil
-}
-
-// FieldByNameFunc returns the struct field with a name that satisfies the
-// match function.
-// The result is nil if no field was found.
-func (t *StructValue) FieldByNameFunc(match func(string) bool) Value {
- if f, ok := t.Type().(*StructType).FieldByNameFunc(match); ok {
- return t.FieldByIndex(f.Index)
- }
- return nil
-}
-
-// NumField returns the number of fields in the struct.
-func (v *StructValue) NumField() int { return v.typ.(*StructType).NumField() }
-
/*
* constructors
*/
// NewValue returns a new Value initialized to the concrete value
-// stored in the interface i. NewValue(nil) returns nil.
+// stored in the interface i. NewValue(nil) returns the zero Value.
func NewValue(i interface{}) Value {
if i == nil {
- return nil
+ return Value{}
}
- t, a := unsafe.Reflect(i)
- return newValue(toType(t), addr(a), canSet|canAddr|canStore)
+ _, a := unsafe.Reflect(i)
+ return newValue(Typeof(i), addr(a), canSet|canAddr|canStore)
}
func newValue(typ Type, addr addr, flag uint32) Value {
v := value{typ, addr, flag}
- switch typ.(type) {
- case *ArrayType:
- return &ArrayValue{v}
- case *BoolType:
- return &BoolValue{v}
- case *ChanType:
- return &ChanValue{v}
- case *FloatType:
- return &FloatValue{v}
- case *FuncType:
- return &FuncValue{value: v}
- case *ComplexType:
- return &ComplexValue{v}
- case *IntType:
- return &IntValue{v}
- case *InterfaceType:
- return &InterfaceValue{v}
- case *MapType:
- return &MapValue{v}
- case *PtrType:
- return &PtrValue{v}
- case *SliceType:
- return &SliceValue{v}
- case *StringType:
- return &StringValue{v}
- case *StructType:
- return &StructValue{v}
- case *UintType:
- return &UintValue{v}
- case *UnsafePointerType:
- return &UnsafePointerValue{v}
+ switch typ.Kind() {
+ case Array:
+ return Value{&arrayValue{v}}
+ case Bool:
+ return Value{&boolValue{v}}
+ case Chan:
+ return Value{&chanValue{v}}
+ case Float32, Float64:
+ return Value{&floatValue{v}}
+ case Func:
+ return Value{&funcValue{value: v}}
+ case Complex64, Complex128:
+ return Value{&complexValue{v}}
+ case Int, Int8, Int16, Int32, Int64:
+ return Value{&intValue{v}}
+ case Interface:
+ return Value{&interfaceValue{v}}
+ case Map:
+ return Value{&mapValue{v}}
+ case Ptr:
+ return Value{&ptrValue{v}}
+ case Slice:
+ return Value{&sliceValue{v}}
+ case String:
+ return Value{&stringValue{v}}
+ case Struct:
+ return Value{&structValue{v}}
+ case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
+ return Value{&uintValue{v}}
+ case UnsafePointer:
+ return Value{&unsafePointerValue{v}}
}
panic("newValue" + typ.String())
}
-// MakeZero returns a zero Value for the specified Type.
-func MakeZero(typ Type) Value {
+// Zero returns a Value representing a zero value for the specified type.
+// The result is different from the zero value of the Value struct,
+// which represents no value at all.
+// For example, Zero(Typeof(42)) returns a Value with Kind Int and value 0.
+func Zero(typ Type) Value {
if typ == nil {
- return nil
+ panic("reflect: Zero(nil)")
}
return newValue(typ, addr(unsafe.New(typ)), canSet|canAddr|canStore)
}
diff --git a/src/pkg/rpc/client.go b/src/pkg/rpc/client.go
index 6de6d1325..8af4afcf6 100644
--- a/src/pkg/rpc/client.go
+++ b/src/pkg/rpc/client.go
@@ -39,8 +39,9 @@ type Call struct {
// There may be multiple outstanding Calls associated
// with a single Client.
type Client struct {
- mutex sync.Mutex // protects pending, seq
+ mutex sync.Mutex // protects pending, seq, request
sending sync.Mutex
+ request Request
seq uint64
codec ClientCodec
pending map[uint64]*Call
@@ -79,21 +80,21 @@ func (client *Client) send(c *Call) {
client.mutex.Unlock()
// Encode and send the request.
- request := new(Request)
client.sending.Lock()
defer client.sending.Unlock()
- request.Seq = c.seq
- request.ServiceMethod = c.ServiceMethod
- if err := client.codec.WriteRequest(request, c.Args); err != nil {
+ client.request.Seq = c.seq
+ client.request.ServiceMethod = c.ServiceMethod
+ if err := client.codec.WriteRequest(&client.request, c.Args); err != nil {
panic("rpc: client encode error: " + err.String())
}
}
func (client *Client) input() {
var err os.Error
+ var response Response
for err == nil {
- response := new(Response)
- err = client.codec.ReadResponseHeader(response)
+ response = Response{}
+ err = client.codec.ReadResponseHeader(&response)
if err != nil {
if err == os.EOF && !client.closing {
err = io.ErrUnexpectedEOF
@@ -148,8 +149,12 @@ func (call *Call) done() {
// NewClient returns a new Client to handle requests to the
// set of services at the other end of the connection.
+// It adds a buffer to the write side of the connection so
+// the header and payload are sent as a unit.
func NewClient(conn io.ReadWriteCloser) *Client {
- return NewClientWithCodec(&gobClientCodec{conn, gob.NewDecoder(conn), gob.NewEncoder(conn)})
+ encBuf := bufio.NewWriter(conn)
+ client := &gobClientCodec{conn, gob.NewDecoder(conn), gob.NewEncoder(encBuf), encBuf}
+ return NewClientWithCodec(client)
}
// NewClientWithCodec is like NewClient but uses the specified
@@ -164,16 +169,20 @@ func NewClientWithCodec(codec ClientCodec) *Client {
}
type gobClientCodec struct {
- rwc io.ReadWriteCloser
- dec *gob.Decoder
- enc *gob.Encoder
+ rwc io.ReadWriteCloser
+ dec *gob.Decoder
+ enc *gob.Encoder
+ encBuf *bufio.Writer
}
-func (c *gobClientCodec) WriteRequest(r *Request, body interface{}) os.Error {
- if err := c.enc.Encode(r); err != nil {
- return err
+func (c *gobClientCodec) WriteRequest(r *Request, body interface{}) (err os.Error) {
+ if err = c.enc.Encode(r); err != nil {
+ return
+ }
+ if err = c.enc.Encode(body); err != nil {
+ return
}
- return c.enc.Encode(body)
+ return c.encBuf.Flush()
}
func (c *gobClientCodec) ReadResponseHeader(r *Response) os.Error {
@@ -199,7 +208,7 @@ func DialHTTP(network, address string) (*Client, os.Error) {
// at the specified network address and path.
func DialHTTPPath(network, address, path string) (*Client, os.Error) {
var err os.Error
- conn, err := net.Dial(network, "", address)
+ conn, err := net.Dial(network, address)
if err != nil {
return nil, err
}
@@ -220,7 +229,7 @@ func DialHTTPPath(network, address, path string) (*Client, os.Error) {
// Dial connects to an RPC server at the specified network address.
func Dial(network, address string) (*Client, os.Error) {
- conn, err := net.Dial(network, "", address)
+ conn, err := net.Dial(network, address)
if err != nil {
return nil, err
}
@@ -273,6 +282,6 @@ func (client *Client) Call(serviceMethod string, args interface{}, reply interfa
if client.shutdown {
return ErrShutdown
}
- call := <-client.Go(serviceMethod, args, reply, nil).Done
+ call := <-client.Go(serviceMethod, args, reply, make(chan *Call, 1)).Done
return call.Error
}
diff --git a/src/pkg/rpc/jsonrpc/client.go b/src/pkg/rpc/jsonrpc/client.go
index 5b806bd6e..57e977d32 100644
--- a/src/pkg/rpc/jsonrpc/client.go
+++ b/src/pkg/rpc/jsonrpc/client.go
@@ -116,7 +116,7 @@ func NewClient(conn io.ReadWriteCloser) *rpc.Client {
// Dial connects to a JSON-RPC server at the specified network address.
func Dial(network, address string) (*rpc.Client, os.Error) {
- conn, err := net.Dial(network, "", address)
+ conn, err := net.Dial(network, address)
if err != nil {
return nil, err
}
diff --git a/src/pkg/rpc/server.go b/src/pkg/rpc/server.go
index f185cd16e..af31a65cc 100644
--- a/src/pkg/rpc/server.go
+++ b/src/pkg/rpc/server.go
@@ -110,6 +110,7 @@
package rpc
import (
+ "bufio"
"gob"
"http"
"log"
@@ -132,13 +133,13 @@ const (
// Precompute the reflect type for os.Error. Can't use os.Error directly
// because Typeof takes an empty interface value. This is annoying.
var unusedError *os.Error
-var typeOfOsError = reflect.Typeof(unusedError).(*reflect.PtrType).Elem()
+var typeOfOsError = reflect.Typeof(unusedError).Elem()
type methodType struct {
sync.Mutex // protects counters
method reflect.Method
- ArgType *reflect.PtrType
- ReplyType *reflect.PtrType
+ ArgType reflect.Type
+ ReplyType reflect.Type
numCalls uint
}
@@ -153,29 +154,29 @@ type service struct {
// but documented here as an aid to debugging, such as when analyzing
// network traffic.
type Request struct {
- ServiceMethod string // format: "Service.Method"
- Seq uint64 // sequence number chosen by client
+ ServiceMethod string // format: "Service.Method"
+ Seq uint64 // sequence number chosen by client
+ next *Request // for free list in Server
}
// Response is a header written before every RPC return. It is used internally
// but documented here as an aid to debugging, such as when analyzing
// network traffic.
type Response struct {
- ServiceMethod string // echoes that of the Request
- Seq uint64 // echoes that of the request
- Error string // error, if any.
-}
-
-// ClientInfo records information about an RPC client connection.
-type ClientInfo struct {
- LocalAddr string
- RemoteAddr string
+ ServiceMethod string // echoes that of the Request
+ Seq uint64 // echoes that of the request
+ Error string // error, if any.
+ next *Response // for free list in Server
}
// Server represents an RPC Server.
type Server struct {
sync.Mutex // protects the serviceMap
serviceMap map[string]*service
+ reqLock sync.Mutex // protects freeReq
+ freeReq *Request
+ respLock sync.Mutex // protects freeResp
+ freeResp *Response
}
// NewServer returns a new Server.
@@ -251,13 +252,14 @@ func (server *Server) register(rcvr interface{}, name string, useName bool) os.E
log.Println("method", mname, "has wrong number of ins:", mtype.NumIn())
continue
}
- argType, ok := mtype.In(1).(*reflect.PtrType)
+ argType := mtype.In(1)
+ ok := argType.Kind() == reflect.Ptr
if !ok {
log.Println(mname, "arg type not a pointer:", mtype.In(1))
continue
}
- replyType, ok := mtype.In(2).(*reflect.PtrType)
- if !ok {
+ replyType := mtype.In(2)
+ if replyType.Kind() != reflect.Ptr {
log.Println(mname, "reply type not a pointer:", mtype.In(2))
continue
}
@@ -269,13 +271,6 @@ func (server *Server) register(rcvr interface{}, name string, useName bool) os.E
log.Println(mname, "reply type not exported:", replyType)
continue
}
- if mtype.NumIn() == 4 {
- t := mtype.In(3)
- if t != reflect.Typeof((*ClientInfo)(nil)) {
- log.Println(mname, "last argument not *ClientInfo")
- continue
- }
- }
// Method needs one out: os.Error.
if mtype.NumOut() != 1 {
log.Println("method", mname, "has wrong number of outs:", mtype.NumOut())
@@ -298,20 +293,18 @@ func (server *Server) register(rcvr interface{}, name string, useName bool) os.E
}
// A value sent as a placeholder for the response when the server receives an invalid request.
-type InvalidRequest struct {
- Marker int
-}
+type InvalidRequest struct{}
var invalidRequest = InvalidRequest{}
-func _new(t *reflect.PtrType) *reflect.PtrValue {
- v := reflect.MakeZero(t).(*reflect.PtrValue)
- v.PointTo(reflect.MakeZero(t.Elem()))
+func _new(t reflect.Type) reflect.Value {
+ v := reflect.Zero(t)
+ v.Set(reflect.Zero(t.Elem()).Addr())
return v
}
-func sendResponse(sending *sync.Mutex, req *Request, reply interface{}, codec ServerCodec, errmsg string) {
- resp := new(Response)
+func (server *Server) sendResponse(sending *sync.Mutex, req *Request, reply interface{}, codec ServerCodec, errmsg string) {
+ resp := server.getResponse()
// Encode the response header
resp.ServiceMethod = req.ServiceMethod
if errmsg != "" {
@@ -325,6 +318,7 @@ func sendResponse(sending *sync.Mutex, req *Request, reply interface{}, codec Se
log.Println("rpc: writing response:", err)
}
sending.Unlock()
+ server.freeResponse(resp)
}
func (m *methodType) NumCalls() (n uint) {
@@ -334,7 +328,7 @@ func (m *methodType) NumCalls() (n uint) {
return n
}
-func (s *service) call(sending *sync.Mutex, mtype *methodType, req *Request, argv, replyv reflect.Value, codec ServerCodec) {
+func (s *service) call(server *Server, sending *sync.Mutex, mtype *methodType, req *Request, argv, replyv reflect.Value, codec ServerCodec) {
mtype.Lock()
mtype.numCalls++
mtype.Unlock()
@@ -347,13 +341,15 @@ func (s *service) call(sending *sync.Mutex, mtype *methodType, req *Request, arg
if errInter != nil {
errmsg = errInter.(os.Error).String()
}
- sendResponse(sending, req, replyv.Interface(), codec, errmsg)
+ server.sendResponse(sending, req, replyv.Interface(), codec, errmsg)
+ server.freeRequest(req)
}
type gobServerCodec struct {
- rwc io.ReadWriteCloser
- dec *gob.Decoder
- enc *gob.Encoder
+ rwc io.ReadWriteCloser
+ dec *gob.Decoder
+ enc *gob.Encoder
+ encBuf *bufio.Writer
}
func (c *gobServerCodec) ReadRequestHeader(r *Request) os.Error {
@@ -364,11 +360,14 @@ func (c *gobServerCodec) ReadRequestBody(body interface{}) os.Error {
return c.dec.Decode(body)
}
-func (c *gobServerCodec) WriteResponse(r *Response, body interface{}) os.Error {
- if err := c.enc.Encode(r); err != nil {
- return err
+func (c *gobServerCodec) WriteResponse(r *Response, body interface{}) (err os.Error) {
+ if err = c.enc.Encode(r); err != nil {
+ return
}
- return c.enc.Encode(body)
+ if err = c.enc.Encode(body); err != nil {
+ return
+ }
+ return c.encBuf.Flush()
}
func (c *gobServerCodec) Close() os.Error {
@@ -382,7 +381,9 @@ func (c *gobServerCodec) Close() os.Error {
// ServeConn uses the gob wire format (see package gob) on the
// connection. To use an alternate codec, use ServeCodec.
func (server *Server) ServeConn(conn io.ReadWriteCloser) {
- server.ServeCodec(&gobServerCodec{conn, gob.NewDecoder(conn), gob.NewEncoder(conn)})
+ buf := bufio.NewWriter(conn)
+ srv := &gobServerCodec{conn, gob.NewDecoder(conn), gob.NewEncoder(buf), buf}
+ server.ServeCodec(srv)
}
// ServeCodec is like ServeConn but uses the specified codec to
@@ -403,7 +404,8 @@ func (server *Server) ServeCodec(codec ServerCodec) {
// send a response if we actually managed to read a header.
if req != nil {
- sendResponse(sending, req, invalidRequest, codec, err.String())
+ server.sendResponse(sending, req, invalidRequest, codec, err.String())
+ server.freeRequest(req)
}
continue
}
@@ -419,16 +421,57 @@ func (server *Server) ServeCodec(codec ServerCodec) {
}
break
}
- sendResponse(sending, req, replyv.Interface(), codec, err.String())
+ server.sendResponse(sending, req, replyv.Interface(), codec, err.String())
continue
}
- go service.call(sending, mtype, req, argv, replyv, codec)
+ go service.call(server, sending, mtype, req, argv, replyv, codec)
}
codec.Close()
}
+
+func (server *Server) getRequest() *Request {
+ server.reqLock.Lock()
+ req := server.freeReq
+ if req == nil {
+ req = new(Request)
+ } else {
+ server.freeReq = req.next
+ *req = Request{}
+ }
+ server.reqLock.Unlock()
+ return req
+}
+
+func (server *Server) freeRequest(req *Request) {
+ server.reqLock.Lock()
+ req.next = server.freeReq
+ server.freeReq = req
+ server.reqLock.Unlock()
+}
+
+func (server *Server) getResponse() *Response {
+ server.respLock.Lock()
+ resp := server.freeResp
+ if resp == nil {
+ resp = new(Response)
+ } else {
+ server.freeResp = resp.next
+ *resp = Response{}
+ }
+ server.respLock.Unlock()
+ return resp
+}
+
+func (server *Server) freeResponse(resp *Response) {
+ server.respLock.Lock()
+ resp.next = server.freeResp
+ server.freeResp = resp
+ server.respLock.Unlock()
+}
+
func (server *Server) readRequest(codec ServerCodec) (req *Request, service *service, mtype *methodType, err os.Error) {
// Grab the request header.
- req = new(Request)
+ req = server.getRequest()
err = codec.ReadRequestHeader(req)
if err != nil {
req = nil
@@ -522,14 +565,14 @@ var connected = "200 Connected to Go RPC"
// ServeHTTP implements an http.Handler that answers RPC requests.
func (server *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if req.Method != "CONNECT" {
- w.SetHeader("Content-Type", "text/plain; charset=utf-8")
+ w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.WriteHeader(http.StatusMethodNotAllowed)
io.WriteString(w, "405 must CONNECT\n")
return
}
conn, _, err := w.(http.Hijacker).Hijack()
if err != nil {
- log.Print("rpc hijacking ", w.RemoteAddr(), ": ", err.String())
+ log.Print("rpc hijacking ", req.RemoteAddr, ": ", err.String())
return
}
io.WriteString(conn, "HTTP/1.0 "+connected+"\n\n")
diff --git a/src/pkg/rpc/server_test.go b/src/pkg/rpc/server_test.go
index 05aaebceb..d4041ae70 100644
--- a/src/pkg/rpc/server_test.go
+++ b/src/pkg/rpc/server_test.go
@@ -6,10 +6,11 @@ package rpc
import (
"fmt"
- "http"
+ "http/httptest"
"log"
"net"
"os"
+ "runtime"
"strings"
"sync"
"testing"
@@ -103,11 +104,9 @@ func startNewServer() {
}
func startHttpServer() {
- var l net.Listener
- l, httpServerAddr = listenTCP()
- httpServerAddr = l.Addr().String()
+ server := httptest.NewServer(nil)
+ httpServerAddr = server.Listener.Addr().String()
log.Println("Test HTTP RPC server listening on", httpServerAddr)
- go http.Serve(l, nil)
}
func TestRPC(t *testing.T) {
@@ -313,12 +312,12 @@ func (WriteFailCodec) WriteRequest(*Request, interface{}) os.Error {
}
func (WriteFailCodec) ReadResponseHeader(*Response) os.Error {
- time.Sleep(60e9)
+ time.Sleep(120e9)
panic("unreachable")
}
func (WriteFailCodec) ReadResponseBody(interface{}) os.Error {
- time.Sleep(60e9)
+ time.Sleep(120e9)
panic("unreachable")
}
@@ -351,3 +350,52 @@ func testSendDeadlock(client *Client) {
reply := new(Reply)
client.Call("Arith.Add", args, reply)
}
+
+func TestCountMallocs(t *testing.T) {
+ once.Do(startServer)
+ client, err := Dial("tcp", serverAddr)
+ if err != nil {
+ t.Error("error dialing", err)
+ }
+ args := &Args{7, 8}
+ reply := new(Reply)
+ mallocs := 0 - runtime.MemStats.Mallocs
+ const count = 100
+ for i := 0; i < count; i++ {
+ err = client.Call("Arith.Add", args, reply)
+ if err != nil {
+ t.Errorf("Add: expected no error but got string %q", err.String())
+ }
+ if reply.C != args.A+args.B {
+ t.Errorf("Add: expected %d got %d", reply.C, args.A+args.B)
+ }
+ }
+ mallocs += runtime.MemStats.Mallocs
+ fmt.Printf("mallocs per rpc round trip: %d\n", mallocs/count)
+}
+
+func BenchmarkEndToEnd(b *testing.B) {
+ b.StopTimer()
+ once.Do(startServer)
+ client, err := Dial("tcp", serverAddr)
+ if err != nil {
+ fmt.Println("error dialing", err)
+ return
+ }
+
+ // Synchronous calls
+ args := &Args{7, 8}
+ reply := new(Reply)
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ err = client.Call("Arith.Add", args, reply)
+ if err != nil {
+ fmt.Printf("Add: expected no error but got string %q", err.String())
+ break
+ }
+ if reply.C != args.A+args.B {
+ fmt.Printf("Add: expected %d got %d", reply.C, args.A+args.B)
+ break
+ }
+ }
+}
diff --git a/src/pkg/runtime/386/asm.s b/src/pkg/runtime/386/asm.s
index 74e1df0da..598fc6846 100644
--- a/src/pkg/runtime/386/asm.s
+++ b/src/pkg/runtime/386/asm.s
@@ -105,7 +105,7 @@ TEXT runtime·breakpoint(SB),7,$0
* go-routine
*/
-// uintptr gosave(Gobuf*)
+// void gosave(Gobuf*)
// save state in Gobuf; setjmp
TEXT runtime·gosave(SB), 7, $0
MOVL 4(SP), AX // gobuf
@@ -116,7 +116,6 @@ TEXT runtime·gosave(SB), 7, $0
get_tls(CX)
MOVL g(CX), BX
MOVL BX, gobuf_g(AX)
- MOVL $0, AX // return 0
RET
// void gogo(Gobuf*, uintptr)
@@ -148,6 +147,35 @@ TEXT runtime·gogocall(SB), 7, $0
JMP AX
POPL BX // not reached
+// void mcall(void (*fn)(G*))
+// Switch to m->g0's stack, call fn(g).
+// Fn must never return. It should gogo(&g->gobuf)
+// to keep running g.
+TEXT runtime·mcall(SB), 7, $0
+ MOVL fn+0(FP), DI
+
+ get_tls(CX)
+ MOVL g(CX), AX // save state in g->gobuf
+ MOVL 0(SP), BX // caller's PC
+ MOVL BX, (g_sched+gobuf_pc)(AX)
+ LEAL 4(SP), BX // caller's SP
+ MOVL BX, (g_sched+gobuf_sp)(AX)
+ MOVL AX, (g_sched+gobuf_g)(AX)
+
+ // switch to m->g0 & its stack, call fn
+ MOVL m(CX), BX
+ MOVL m_g0(BX), SI
+ CMPL SI, AX // if g == m->g0 call badmcall
+ JNE 2(PC)
+ CALL runtime·badmcall(SB)
+ MOVL SI, g(CX) // g = m->g0
+ MOVL (g_sched+gobuf_sp)(SI), SP // sp = m->g0->gobuf.sp
+ PUSHL AX
+ CALL DI
+ POPL AX
+ CALL runtime·badmcall2(SB)
+ RET
+
/*
* support for morestack
*/
@@ -183,10 +211,10 @@ TEXT runtime·morestack(SB),7,$0
MOVL 0(SP), AX
MOVL AX, m_morepc(BX)
- // Call newstack on m's scheduling stack.
+ // Call newstack on m->g0's stack.
MOVL m_g0(BX), BP
MOVL BP, g(CX)
- MOVL (m_sched+gobuf_sp)(BX), AX
+ MOVL (g_sched+gobuf_sp)(BP), AX
MOVL -4(AX), BX // fault if CALL would, before smashing SP
MOVL AX, SP
CALL runtime·newstack(SB)
@@ -226,11 +254,11 @@ TEXT reflect·call(SB), 7, $0
MOVL CX, m_moreargsize(BX) // f's argument size
MOVL $1, m_moreframesize(BX) // f's frame size
- // Call newstack on m's scheduling stack.
+ // Call newstack on m->g0's stack.
MOVL m_g0(BX), BP
get_tls(CX)
MOVL BP, g(CX)
- MOVL (m_sched+gobuf_sp)(BX), SP
+ MOVL (g_sched+gobuf_sp)(BP), SP
CALL runtime·newstack(SB)
MOVL $0, 0x1103 // crash if newstack returns
RET
@@ -243,10 +271,10 @@ TEXT runtime·lessstack(SB), 7, $0
MOVL m(CX), BX
MOVL AX, m_cret(BX)
- // Call oldstack on m's scheduling stack.
- MOVL m_g0(BX), DX
- MOVL DX, g(CX)
- MOVL (m_sched+gobuf_sp)(BX), SP
+ // Call oldstack on m->g0's stack.
+ MOVL m_g0(BX), BP
+ MOVL BP, g(CX)
+ MOVL (g_sched+gobuf_sp)(BP), SP
CALL runtime·oldstack(SB)
MOVL $0, 0x1004 // crash if oldstack returns
RET
@@ -302,6 +330,133 @@ TEXT runtime·jmpdefer(SB), 7, $0
SUBL $5, (SP) // return to CALL again
JMP AX // but first run the deferred function
+// Dummy function to use in saved gobuf.PC,
+// to match SP pointing at a return address.
+// The gobuf.PC is unused by the contortions here
+// but setting it to return will make the traceback code work.
+TEXT return<>(SB),7,$0
+ RET
+
+// asmcgocall(void(*fn)(void*), void *arg)
+// Call fn(arg) on the scheduler stack,
+// aligned appropriately for the gcc ABI.
+// See cgocall.c for more details.
+TEXT runtime·asmcgocall(SB),7,$0
+ MOVL fn+0(FP), AX
+ MOVL arg+4(FP), BX
+ MOVL SP, DX
+
+ // Figure out if we need to switch to m->g0 stack.
+ // We get called to create new OS threads too, and those
+ // come in on the m->g0 stack already.
+ get_tls(CX)
+ MOVL m(CX), BP
+ MOVL m_g0(BP), SI
+ MOVL g(CX), DI
+ CMPL SI, DI
+ JEQ 6(PC)
+ MOVL SP, (g_sched+gobuf_sp)(DI)
+ MOVL $return<>(SB), (g_sched+gobuf_pc)(DI)
+ MOVL DI, (g_sched+gobuf_g)(DI)
+ MOVL SI, g(CX)
+ MOVL (g_sched+gobuf_sp)(SI), SP
+
+ // Now on a scheduling stack (a pthread-created stack).
+ SUBL $32, SP
+ ANDL $~15, SP // alignment, perhaps unnecessary
+ MOVL DI, 8(SP) // save g
+ MOVL DX, 4(SP) // save SP
+ MOVL BX, 0(SP) // first argument in x86-32 ABI
+ CALL AX
+
+ // Restore registers, g, stack pointer.
+ get_tls(CX)
+ MOVL 8(SP), DI
+ MOVL DI, g(CX)
+ MOVL 4(SP), SP
+ RET
+
+// cgocallback(void (*fn)(void*), void *frame, uintptr framesize)
+// See cgocall.c for more details.
+TEXT runtime·cgocallback(SB),7,$12
+ MOVL fn+0(FP), AX
+ MOVL frame+4(FP), BX
+ MOVL framesize+8(FP), DX
+
+ // Save current m->g0->sched.sp on stack and then set it to SP.
+ get_tls(CX)
+ MOVL m(CX), BP
+ MOVL m_g0(BP), SI
+ PUSHL (g_sched+gobuf_sp)(SI)
+ MOVL SP, (g_sched+gobuf_sp)(SI)
+
+ // Switch to m->curg stack and call runtime.cgocallback
+ // with the three arguments. Because we are taking over
+ // the execution of m->curg but *not* resuming what had
+ // been running, we need to save that information (m->curg->gobuf)
+ // so that we can restore it when we're done.
+ // We can restore m->curg->gobuf.sp easily, because calling
+ // runtime.cgocallback leaves SP unchanged upon return.
+ // To save m->curg->gobuf.pc, we push it onto the stack.
+ // This has the added benefit that it looks to the traceback
+ // routine like cgocallback is going to return to that
+ // PC (because we defined cgocallback to have
+ // a frame size of 12, the same amount that we use below),
+ // so that the traceback will seamlessly trace back into
+ // the earlier calls.
+ MOVL m_curg(BP), SI
+ MOVL SI, g(CX)
+ MOVL (g_sched+gobuf_sp)(SI), DI // prepare stack as DI
+
+ // Push gobuf.pc
+ MOVL (g_sched+gobuf_pc)(SI), BP
+ SUBL $4, DI
+ MOVL BP, 0(DI)
+
+ // Push arguments to cgocallbackg.
+ // Frame size here must match the frame size above
+ // to trick traceback routines into doing the right thing.
+ SUBL $12, DI
+ MOVL AX, 0(DI)
+ MOVL BX, 4(DI)
+ MOVL DX, 8(DI)
+
+ // Switch stack and make the call.
+ MOVL DI, SP
+ CALL runtime·cgocallbackg(SB)
+
+ // Restore g->gobuf (== m->curg->gobuf) from saved values.
+ get_tls(CX)
+ MOVL g(CX), SI
+ MOVL 12(SP), BP
+ MOVL BP, (g_sched+gobuf_pc)(SI)
+ LEAL (12+4)(SP), DI
+ MOVL DI, (g_sched+gobuf_sp)(SI)
+
+ // Switch back to m->g0's stack and restore m->g0->sched.sp.
+ // (Unlike m->curg, the g0 goroutine never uses sched.pc,
+ // so we do not have to restore it.)
+ MOVL m(CX), BP
+ MOVL m_g0(BP), SI
+ MOVL SI, g(CX)
+ MOVL (g_sched+gobuf_sp)(SI), SP
+ POPL (g_sched+gobuf_sp)(SI)
+
+ // Done!
+ RET
+
+// check that SP is in range [g->stackbase, g->stackguard)
+TEXT runtime·stackcheck(SB), 7, $0
+ get_tls(CX)
+ MOVL g(CX), AX
+ CMPL g_stackbase(AX), SP
+ JHI 2(PC)
+ INT $3
+ CMPL SP, g_stackguard(AX)
+ JHI 2(PC)
+ INT $3
+ RET
+
TEXT runtime·memclr(SB),7,$0
MOVL 4(SP), DI // arg 1 addr
MOVL 8(SP), CX // arg 2 count
@@ -345,82 +500,4 @@ TEXT runtime·emptyfunc(SB),0,$0
TEXT runtime·abort(SB),7,$0
INT $0x3
-// runcgo(void(*fn)(void*), void *arg)
-// Call fn(arg) on the scheduler stack,
-// aligned appropriately for the gcc ABI.
-TEXT runtime·runcgo(SB),7,$16
- MOVL fn+0(FP), AX
- MOVL arg+4(FP), BX
- MOVL SP, CX
-
- // Figure out if we need to switch to m->g0 stack.
- get_tls(DI)
- MOVL m(DI), DX
- MOVL m_g0(DX), SI
- CMPL g(DI), SI
- JEQ 2(PC)
- MOVL (m_sched+gobuf_sp)(DX), SP
-
- // Now on a scheduling stack (a pthread-created stack).
- SUBL $16, SP
- ANDL $~15, SP // alignment for gcc ABI
- MOVL g(DI), BP
- MOVL BP, 8(SP)
- MOVL SI, g(DI)
- MOVL CX, 4(SP)
- MOVL BX, 0(SP)
- CALL AX
-
- // Back; switch to original g and stack, re-establish
- // "DF is clear" invariant.
- CLD
- get_tls(DI)
- MOVL 8(SP), SI
- MOVL SI, g(DI)
- MOVL 4(SP), SP
- RET
-
-// runcgocallback(G *g1, void* sp, void (*fn)(void))
-// Switch to g1 and sp, call fn, switch back. fn's arguments are on
-// the new stack.
-TEXT runtime·runcgocallback(SB),7,$32
- MOVL g1+0(FP), DX
- MOVL sp+4(FP), AX
- MOVL fn+8(FP), BX
-
- // We are running on m's scheduler stack. Save current SP
- // into m->sched.sp so that a recursive call to runcgo doesn't
- // clobber our stack, and also so that we can restore
- // the SP when the call finishes. Reusing m->sched.sp
- // for this purpose depends on the fact that there is only
- // one possible gosave of m->sched.
- get_tls(CX)
- MOVL DX, g(CX)
- MOVL m(CX), CX
- MOVL SP, (m_sched+gobuf_sp)(CX)
-
- // Set new SP, call fn
- MOVL AX, SP
- CALL BX
-
- // Restore old g and SP, return
- get_tls(CX)
- MOVL m(CX), DX
- MOVL m_g0(DX), BX
- MOVL BX, g(CX)
- MOVL (m_sched+gobuf_sp)(DX), SP
- RET
-
-// check that SP is in range [g->stackbase, g->stackguard)
-TEXT runtime·stackcheck(SB), 7, $0
- get_tls(CX)
- MOVL g(CX), AX
- CMPL g_stackbase(AX), SP
- JHI 2(PC)
- INT $3
- CMPL SP, g_stackguard(AX)
- JHI 2(PC)
- INT $3
- RET
-
GLOBL runtime·tls0(SB), $32
diff --git a/src/pkg/runtime/Makefile b/src/pkg/runtime/Makefile
index e4cc08175..4da78c5f0 100644
--- a/src/pkg/runtime/Makefile
+++ b/src/pkg/runtime/Makefile
@@ -22,6 +22,7 @@ GOFILES=\
debug.go\
error.go\
extern.go\
+ mem.go\
sig.go\
softfloat64.go\
type.go\
@@ -52,6 +53,7 @@ OFILES=\
cgocall.$O\
chan.$O\
closure.$O\
+ cpuprof.$O\
float.$O\
complex.$O\
hashmap.$O\
diff --git a/src/pkg/runtime/amd64/asm.s b/src/pkg/runtime/amd64/asm.s
index cc05435f7..a611985c5 100644
--- a/src/pkg/runtime/amd64/asm.s
+++ b/src/pkg/runtime/amd64/asm.s
@@ -89,7 +89,7 @@ TEXT runtime·breakpoint(SB),7,$0
* go-routine
*/
-// uintptr gosave(Gobuf*)
+// void gosave(Gobuf*)
// save state in Gobuf; setjmp
TEXT runtime·gosave(SB), 7, $0
MOVQ 8(SP), AX // gobuf
@@ -100,7 +100,6 @@ TEXT runtime·gosave(SB), 7, $0
get_tls(CX)
MOVQ g(CX), BX
MOVQ BX, gobuf_g(AX)
- MOVL $0, AX // return 0
RET
// void gogo(Gobuf*, uintptr)
@@ -132,6 +131,35 @@ TEXT runtime·gogocall(SB), 7, $0
JMP AX
POPQ BX // not reached
+// void mcall(void (*fn)(G*))
+// Switch to m->g0's stack, call fn(g).
+// Fn must never return. It should gogo(&g->gobuf)
+// to keep running g.
+TEXT runtime·mcall(SB), 7, $0
+ MOVQ fn+0(FP), DI
+
+ get_tls(CX)
+ MOVQ g(CX), AX // save state in g->gobuf
+ MOVQ 0(SP), BX // caller's PC
+ MOVQ BX, (g_sched+gobuf_pc)(AX)
+ LEAQ 8(SP), BX // caller's SP
+ MOVQ BX, (g_sched+gobuf_sp)(AX)
+ MOVQ AX, (g_sched+gobuf_g)(AX)
+
+ // switch to m->g0 & its stack, call fn
+ MOVQ m(CX), BX
+ MOVQ m_g0(BX), SI
+ CMPQ SI, AX // if g == m->g0 call badmcall
+ JNE 2(PC)
+ CALL runtime·badmcall(SB)
+ MOVQ SI, g(CX) // g = m->g0
+ MOVQ (g_sched+gobuf_sp)(SI), SP // sp = m->g0->gobuf.sp
+ PUSHQ AX
+ CALL DI
+ POPQ AX
+ CALL runtime·badmcall2(SB)
+ RET
+
/*
* support for morestack
*/
@@ -160,10 +188,10 @@ TEXT runtime·morestack(SB),7,$0
MOVQ 0(SP), AX
MOVQ AX, m_morepc(BX)
- // Call newstack on m's scheduling stack.
+ // Call newstack on m->g0's stack.
MOVQ m_g0(BX), BP
MOVQ BP, g(CX)
- MOVQ (m_sched+gobuf_sp)(BX), SP
+ MOVQ (g_sched+gobuf_sp)(BP), SP
CALL runtime·newstack(SB)
MOVQ $0, 0x1003 // crash if newstack returns
RET
@@ -201,11 +229,11 @@ TEXT reflect·call(SB), 7, $0
MOVL CX, m_moreargsize(BX) // f's argument size
MOVL $1, m_moreframesize(BX) // f's frame size
- // Call newstack on m's scheduling stack.
+ // Call newstack on m->g0's stack.
MOVQ m_g0(BX), BP
get_tls(CX)
MOVQ BP, g(CX)
- MOVQ (m_sched+gobuf_sp)(BX), SP
+ MOVQ (g_sched+gobuf_sp)(BP), SP
CALL runtime·newstack(SB)
MOVQ $0, 0x1103 // crash if newstack returns
RET
@@ -217,10 +245,10 @@ TEXT runtime·lessstack(SB), 7, $0
MOVQ m(CX), BX
MOVQ AX, m_cret(BX)
- // Call oldstack on m's scheduling stack.
- MOVQ m_g0(BX), DX
- MOVQ DX, g(CX)
- MOVQ (m_sched+gobuf_sp)(BX), SP
+ // Call oldstack on m->g0's stack.
+ MOVQ m_g0(BX), BP
+ MOVQ BP, g(CX)
+ MOVQ (g_sched+gobuf_sp)(BP), SP
CALL runtime·oldstack(SB)
MOVQ $0, 0x1004 // crash if oldstack returns
RET
@@ -336,7 +364,6 @@ TEXT runtime·casp(SB), 7, $0
MOVL $1, AX
RET
-
// void jmpdefer(fn, sp);
// called from deferreturn.
// 1. pop the caller
@@ -349,68 +376,119 @@ TEXT runtime·jmpdefer(SB), 7, $0
SUBQ $5, (SP) // return to CALL again
JMP AX // but first run the deferred function
-// runcgo(void(*fn)(void*), void *arg)
+// Dummy function to use in saved gobuf.PC,
+// to match SP pointing at a return address.
+// The gobuf.PC is unused by the contortions here
+// but setting it to return will make the traceback code work.
+TEXT return<>(SB),7,$0
+ RET
+
+// asmcgocall(void(*fn)(void*), void *arg)
// Call fn(arg) on the scheduler stack,
// aligned appropriately for the gcc ABI.
-TEXT runtime·runcgo(SB),7,$32
- MOVQ fn+0(FP), R12
- MOVQ arg+8(FP), R13
- MOVQ SP, CX
+// See cgocall.c for more details.
+TEXT runtime·asmcgocall(SB),7,$0
+ MOVQ fn+0(FP), AX
+ MOVQ arg+8(FP), BX
+ MOVQ SP, DX
// Figure out if we need to switch to m->g0 stack.
- get_tls(DI)
- MOVQ m(DI), DX
- MOVQ m_g0(DX), SI
- CMPQ g(DI), SI
- JEQ 2(PC)
- MOVQ (m_sched+gobuf_sp)(DX), SP
+ // We get called to create new OS threads too, and those
+ // come in on the m->g0 stack already.
+ get_tls(CX)
+ MOVQ m(CX), BP
+ MOVQ m_g0(BP), SI
+ MOVQ g(CX), DI
+ CMPQ SI, DI
+ JEQ 6(PC)
+ MOVQ SP, (g_sched+gobuf_sp)(DI)
+ MOVQ $return<>(SB), (g_sched+gobuf_pc)(DI)
+ MOVQ DI, (g_sched+gobuf_g)(DI)
+ MOVQ SI, g(CX)
+ MOVQ (g_sched+gobuf_sp)(SI), SP
// Now on a scheduling stack (a pthread-created stack).
SUBQ $32, SP
ANDQ $~15, SP // alignment for gcc ABI
- MOVQ g(DI), BP
- MOVQ BP, 16(SP)
- MOVQ SI, g(DI)
- MOVQ CX, 8(SP)
- MOVQ R13, DI // DI = first argument in AMD64 ABI
- CALL R12
+ MOVQ DI, 16(SP) // save g
+ MOVQ DX, 8(SP) // save SP
+ MOVQ BX, DI // DI = first argument in AMD64 ABI
+ CALL AX
// Restore registers, g, stack pointer.
- get_tls(DI)
- MOVQ 16(SP), SI
- MOVQ SI, g(DI)
+ get_tls(CX)
+ MOVQ 16(SP), DI
+ MOVQ DI, g(CX)
MOVQ 8(SP), SP
RET
-// runcgocallback(G *g1, void* sp, void (*fn)(void))
-// Switch to g1 and sp, call fn, switch back. fn's arguments are on
-// the new stack.
-TEXT runtime·runcgocallback(SB),7,$48
- MOVQ g1+0(FP), DX
- MOVQ sp+8(FP), AX
- MOVQ fp+16(FP), BX
-
- // We are running on m's scheduler stack. Save current SP
- // into m->sched.sp so that a recursive call to runcgo doesn't
- // clobber our stack, and also so that we can restore
- // the SP when the call finishes. Reusing m->sched.sp
- // for this purpose depends on the fact that there is only
- // one possible gosave of m->sched.
- get_tls(CX)
- MOVQ DX, g(CX)
- MOVQ m(CX), CX
- MOVQ SP, (m_sched+gobuf_sp)(CX)
-
- // Set new SP, call fn
- MOVQ AX, SP
- CALL BX
+// cgocallback(void (*fn)(void*), void *frame, uintptr framesize)
+// See cgocall.c for more details.
+TEXT runtime·cgocallback(SB),7,$24
+ MOVQ fn+0(FP), AX
+ MOVQ frame+8(FP), BX
+ MOVQ framesize+16(FP), DX
- // Restore old g and SP, return
+ // Save current m->g0->sched.sp on stack and then set it to SP.
get_tls(CX)
- MOVQ m(CX), DX
- MOVQ m_g0(DX), BX
- MOVQ BX, g(CX)
- MOVQ (m_sched+gobuf_sp)(DX), SP
+ MOVQ m(CX), BP
+ MOVQ m_g0(BP), SI
+ PUSHQ (g_sched+gobuf_sp)(SI)
+ MOVQ SP, (g_sched+gobuf_sp)(SI)
+
+ // Switch to m->curg stack and call runtime.cgocallback
+ // with the three arguments. Because we are taking over
+ // the execution of m->curg but *not* resuming what had
+ // been running, we need to save that information (m->curg->gobuf)
+ // so that we can restore it when we're done.
+ // We can restore m->curg->gobuf.sp easily, because calling
+ // runtime.cgocallback leaves SP unchanged upon return.
+ // To save m->curg->gobuf.pc, we push it onto the stack.
+ // This has the added benefit that it looks to the traceback
+ // routine like cgocallback is going to return to that
+ // PC (because we defined cgocallback to have
+ // a frame size of 24, the same amount that we use below),
+ // so that the traceback will seamlessly trace back into
+ // the earlier calls.
+ MOVQ m_curg(BP), SI
+ MOVQ SI, g(CX)
+ MOVQ (g_sched+gobuf_sp)(SI), DI // prepare stack as DI
+
+ // Push gobuf.pc
+ MOVQ (g_sched+gobuf_pc)(SI), BP
+ SUBQ $8, DI
+ MOVQ BP, 0(DI)
+
+ // Push arguments to cgocallbackg.
+ // Frame size here must match the frame size above
+ // to trick traceback routines into doing the right thing.
+ SUBQ $24, DI
+ MOVQ AX, 0(DI)
+ MOVQ BX, 8(DI)
+ MOVQ DX, 16(DI)
+
+ // Switch stack and make the call.
+ MOVQ DI, SP
+ CALL runtime·cgocallbackg(SB)
+
+ // Restore g->gobuf (== m->curg->gobuf) from saved values.
+ get_tls(CX)
+ MOVQ g(CX), SI
+ MOVQ 24(SP), BP
+ MOVQ BP, (g_sched+gobuf_pc)(SI)
+ LEAQ (24+8)(SP), DI
+ MOVQ DI, (g_sched+gobuf_sp)(SI)
+
+ // Switch back to m->g0's stack and restore m->g0->sched.sp.
+ // (Unlike m->curg, the g0 goroutine never uses sched.pc,
+ // so we do not have to restore it.)
+ MOVQ m(CX), BP
+ MOVQ m_g0(BP), SI
+ MOVQ SI, g(CX)
+ MOVQ (g_sched+gobuf_sp)(SI), SP
+ POPQ (g_sched+gobuf_sp)(SI)
+
+ // Done!
RET
// check that SP is in range [g->stackbase, g->stackguard)
diff --git a/src/pkg/runtime/amd64/traceback.c b/src/pkg/runtime/amd64/traceback.c
index 0f6733c36..d422cb692 100644
--- a/src/pkg/runtime/amd64/traceback.c
+++ b/src/pkg/runtime/amd64/traceback.c
@@ -18,8 +18,8 @@ void runtime·morestack(void);
// as well as the runtime.Callers function (pcbuf != nil).
// A little clunky to merge the two but avoids duplicating
// the code and all its subtlety.
-static int32
-gentraceback(byte *pc0, byte *sp, G *g, int32 skip, uintptr *pcbuf, int32 max)
+int32
+runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr *pcbuf, int32 max)
{
byte *p;
int32 i, n, iter, sawnewstack;
@@ -28,6 +28,7 @@ gentraceback(byte *pc0, byte *sp, G *g, int32 skip, uintptr *pcbuf, int32 max)
Stktop *stk;
Func *f;
+ USED(lr0);
pc = (uintptr)pc0;
lr = 0;
fp = nil;
@@ -199,7 +200,7 @@ gentraceback(byte *pc0, byte *sp, G *g, int32 skip, uintptr *pcbuf, int32 max)
void
runtime·traceback(byte *pc0, byte *sp, byte*, G *g)
{
- gentraceback(pc0, sp, g, 0, nil, 100);
+ runtime·gentraceback(pc0, sp, nil, g, 0, nil, 100);
}
int32
@@ -211,7 +212,7 @@ runtime·callers(int32 skip, uintptr *pcbuf, int32 m)
sp = (byte*)&skip;
pc = runtime·getcallerpc(&skip);
- return gentraceback(pc, sp, g, skip, pcbuf, m);
+ return runtime·gentraceback(pc, sp, nil, g, skip, pcbuf, m);
}
static uintptr
diff --git a/src/pkg/runtime/arm/asm.s b/src/pkg/runtime/arm/asm.s
index f9fe7e628..4d36606a7 100644
--- a/src/pkg/runtime/arm/asm.s
+++ b/src/pkg/runtime/arm/asm.s
@@ -93,14 +93,13 @@ TEXT runtime·breakpoint(SB),7,$0
* go-routine
*/
-// uintptr gosave(Gobuf*)
+// void gosave(Gobuf*)
// save state in Gobuf; setjmp
TEXT runtime·gosave(SB), 7, $-4
MOVW 0(FP), R0 // gobuf
MOVW SP, gobuf_sp(R0)
MOVW LR, gobuf_pc(R0)
MOVW g, gobuf_g(R0)
- MOVW $0, R0 // return 0
RET
// void gogo(Gobuf*, uintptr)
@@ -127,6 +126,30 @@ TEXT runtime·gogocall(SB), 7, $-4
MOVW gobuf_pc(R0), LR
MOVW R1, PC
+// void mcall(void (*fn)(G*))
+// Switch to m->g0's stack, call fn(g).
+// Fn must never return. It should gogo(&g->gobuf)
+// to keep running g.
+TEXT runtime·mcall(SB), 7, $-4
+ MOVW fn+0(FP), R0
+
+ // Save caller state in g->gobuf.
+ MOVW SP, (g_sched+gobuf_sp)(g)
+ MOVW LR, (g_sched+gobuf_pc)(g)
+ MOVW g, (g_sched+gobuf_g)(g)
+
+ // Switch to m->g0 & its stack, call fn.
+ MOVW g, R1
+ MOVW m_g0(m), g
+ CMP g, R1
+ BL.EQ runtime·badmcall(SB)
+ MOVW (g_sched+gobuf_sp)(g), SP
+ SUB $8, SP
+ MOVW R1, 4(SP)
+ BL (R0)
+ BL runtime·badmcall2(SB)
+ RET
+
/*
* support for morestack
*/
@@ -159,9 +182,9 @@ TEXT runtime·morestack(SB),7,$-4
// Set m->morepc to f's PC.
MOVW LR, m_morepc(m)
- // Call newstack on m's scheduling stack.
+ // Call newstack on m->g0's stack.
MOVW m_g0(m), g
- MOVW (m_sched+gobuf_sp)(m), SP
+ MOVW (g_sched+gobuf_sp)(g), SP
B runtime·newstack(SB)
// Called from reflection library. Mimics morestack,
@@ -192,9 +215,9 @@ TEXT reflect·call(SB), 7, $-4
MOVW $1, R3
MOVW R3, m_moreframesize(m) // f's frame size
- // Call newstack on m's scheduling stack.
+ // Call newstack on m->g0's stack.
MOVW m_g0(m), g
- MOVW (m_sched+gobuf_sp)(m), SP
+ MOVW (g_sched+gobuf_sp)(g), SP
B runtime·newstack(SB)
// Return point when leaving stack.
@@ -203,9 +226,9 @@ TEXT runtime·lessstack(SB), 7, $-4
// Save return value in m->cret
MOVW R0, m_cret(m)
- // Call oldstack on m's scheduling stack.
+ // Call oldstack on m->g0's stack.
MOVW m_g0(m), g
- MOVW (m_sched+gobuf_sp)(m), SP
+ MOVW (g_sched+gobuf_sp)(g), SP
B runtime·oldstack(SB)
// void jmpdefer(fn, sp);
@@ -221,6 +244,12 @@ TEXT runtime·jmpdefer(SB), 7, $0
MOVW $-4(SP), SP // SP is 4 below argp, due to saved LR
B (R0)
+TEXT runtime·asmcgocall(SB),7,$0
+ B runtime·cgounimpl(SB)
+
+TEXT runtime·cgocallback(SB),7,$0
+ B runtime·cgounimpl(SB)
+
TEXT runtime·memclr(SB),7,$20
MOVW 0(FP), R0
MOVW $0, R1 // c = 0
@@ -248,22 +277,6 @@ TEXT runtime·getcallersp(SB),7,$-4
MOVW $-4(R0), R0
RET
-// runcgo(void(*fn)(void*), void *arg)
-// Just call fn(arg), but first align the stack
-// appropriately for the gcc ABI.
-// TODO(kaib): figure out the arm-gcc ABI
-TEXT runtime·runcgo(SB),7,$16
- BL runtime·abort(SB)
-// MOVL fn+0(FP), AX
-// MOVL arg+4(FP), BX
-// MOVL SP, CX
-// ANDL $~15, SP // alignment for gcc ABI
-// MOVL CX, 4(SP)
-// MOVL BX, 0(SP)
-// CALL AX
-// MOVL 4(SP), SP
-// RET
-
TEXT runtime·emptyfunc(SB),0,$0
RET
@@ -271,10 +284,6 @@ TEXT runtime·abort(SB),7,$-4
MOVW $0, R0
MOVW (R0), R1
-TEXT runtime·runcgocallback(SB),7,$0
- MOVW $0, R0
- MOVW (R0), R1
-
// bool armcas(int32 *val, int32 old, int32 new)
// Atomically:
// if(*val == old){
diff --git a/src/pkg/runtime/arm/traceback.c b/src/pkg/runtime/arm/traceback.c
index ad3096823..c3934c37c 100644
--- a/src/pkg/runtime/arm/traceback.c
+++ b/src/pkg/runtime/arm/traceback.c
@@ -14,8 +14,8 @@ void _mod(void);
void _divu(void);
void _modu(void);
-static int32
-gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr *pcbuf, int32 max)
+int32
+runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr *pcbuf, int32 max)
{
int32 i, n, iter;
uintptr pc, lr, tracepc, x;
@@ -189,11 +189,10 @@ gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr *pcbuf, i
return n;
}
-
void
runtime·traceback(byte *pc0, byte *sp, byte *lr, G *g)
{
- gentraceback(pc0, sp, lr, g, 0, nil, 100);
+ runtime·gentraceback(pc0, sp, lr, g, 0, nil, 100);
}
// func caller(n int) (pc uintptr, file string, line int, ok bool)
@@ -205,5 +204,5 @@ runtime·callers(int32 skip, uintptr *pcbuf, int32 m)
sp = runtime·getcallersp(&skip);
pc = runtime·getcallerpc(&skip);
- return gentraceback(pc, sp, 0, g, skip, pcbuf, m);
+ return runtime·gentraceback(pc, sp, 0, g, skip, pcbuf, m);
}
diff --git a/src/pkg/runtime/cgocall.c b/src/pkg/runtime/cgocall.c
index 741e8f0b8..58f287e90 100644
--- a/src/pkg/runtime/cgocall.c
+++ b/src/pkg/runtime/cgocall.c
@@ -3,18 +3,97 @@
// license that can be found in the LICENSE file.
#include "runtime.h"
+#include "arch.h"
#include "stack.h"
#include "cgocall.h"
+// Cgo call and callback support.
+//
+// To call into the C function f from Go, the cgo-generated code calls
+// runtime.cgocall(_cgo_Cfunc_f, frame), where _cgo_Cfunc_f is a
+// gcc-compiled function written by cgo.
+//
+// runtime.cgocall (below) locks g to m, calls entersyscall
+// so as not to block other goroutines or the garbage collector,
+// and then calls runtime.asmcgocall(_cgo_Cfunc_f, frame).
+//
+// runtime.asmcgocall (in $GOARCH/asm.s) switches to the m->g0 stack
+// (assumed to be an operating system-allocated stack, so safe to run
+// gcc-compiled code on) and calls _cgo_Cfunc_f(frame).
+//
+// _cgo_Cfunc_f invokes the actual C function f with arguments
+// taken from the frame structure, records the results in the frame,
+// and returns to runtime.asmcgocall.
+//
+// After it regains control, runtime.asmcgocall switches back to the
+// original g (m->curg)'s stack and returns to runtime.cgocall.
+//
+// After it regains control, runtime.cgocall calls exitsyscall, which blocks
+// until this m can run Go code without violating the $GOMAXPROCS limit,
+// and then unlocks g from m.
+//
+// The above description skipped over the possibility of the gcc-compiled
+// function f calling back into Go. If that happens, we continue down
+// the rabbit hole during the execution of f.
+//
+// To make it possible for gcc-compiled C code to call a Go function p.GoF,
+// cgo writes a gcc-compiled function named GoF (not p.GoF, since gcc doesn't
+// know about packages). The gcc-compiled C function f calls GoF.
+//
+// GoF calls crosscall2(_cgoexp_GoF, frame, framesize). Crosscall2
+// (in cgo/$GOOS.S, a gcc-compiled assembly file) is a two-argument
+// adapter from the gcc function call ABI to the 6c function call ABI.
+// It is called from gcc to call 6c functions. In this case it calls
+// _cgoexp_GoF(frame, framesize), still running on m->g0's stack
+// and outside the $GOMAXPROCS limit. Thus, this code cannot yet
+// call arbitrary Go code directly and must be careful not to allocate
+// memory or use up m->g0's stack.
+//
+// _cgoexp_GoF calls runtime.cgocallback(p.GoF, frame, framesize).
+// (The reason for having _cgoexp_GoF instead of writing a crosscall3
+// to make this call directly is that _cgoexp_GoF, because it is compiled
+// with 6c instead of gcc, can refer to dotted names like
+// runtime.cgocallback and p.GoF.)
+//
+// runtime.cgocallback (in $GOOS/asm.s) switches from m->g0's
+// stack to the original g (m->curg)'s stack, on which it calls
+// runtime.cgocallbackg(p.GoF, frame, framesize).
+// As part of the stack switch, runtime.cgocallback saves the current
+// SP as m->g0->sched.sp, so that any use of m->g0's stack during the
+// execution of the callback will be done below the existing stack frames.
+// Before overwriting m->g0->sched.sp, it pushes the old value on the
+// m->g0 stack, so that it can be restored later.
+//
+// runtime.cgocallbackg (below) is now running on a real goroutine
+// stack (not an m->g0 stack). First it calls runtime.exitsyscall, which will
+// block until the $GOMAXPROCS limit allows running this goroutine.
+// Once exitsyscall has returned, it is safe to do things like call the memory
+// allocator or invoke the Go callback function p.GoF. runtime.cgocallback
+// first defers a function to unwind m->g0.sched.sp, so that if p.GoF
+// panics, m->g0.sched.sp will be restored to its old value: the m->g0 stack
+// and the m->curg stack will be unwound in lock step.
+// Then it calls p.GoF. Finally it pops but does not execute the deferred
+// function, calls runtime.entersyscall, and returns to runtime.cgocallback.
+//
+// After it regains control, runtime.cgocallback switches back to
+// m->g0's stack (the pointer is still in m->g0.sched.sp), restores the old
+// m->g0.sched.sp value from the stack, and returns to _cgoexp_GoF.
+//
+// _cgoexp_GoF immediately returns to crosscall2, which restores the
+// callee-save registers for gcc and returns to GoF, which returns to f.
+
void *initcgo; /* filled in by dynamic linker when Cgo is available */
int64 ncgocall;
-void runtime·entersyscall(void);
-void runtime·exitsyscall(void);
+
+static void unlockm(void);
+static void unwindm(void);
+
+// Call from Go to C.
void
runtime·cgocall(void (*fn)(void*), void *arg)
{
- G *oldlock;
+ Defer *d;
if(!runtime·iscgo)
runtime·throw("cgocall unavailable");
@@ -28,61 +107,49 @@ runtime·cgocall(void (*fn)(void*), void *arg)
* Lock g to m to ensure we stay on the same stack if we do a
* cgo callback.
*/
- oldlock = m->lockedg;
- m->lockedg = g;
- g->lockedm = m;
+ d = nil;
+ if(m->lockedg == nil) {
+ m->lockedg = g;
+ g->lockedm = m;
+
+ // Add entry to defer stack in case of panic.
+ d = runtime·malloc(sizeof(*d));
+ d->fn = (byte*)unlockm;
+ d->siz = 0;
+ d->link = g->defer;
+ d->argp = (void*)-1; // unused because unwindm never recovers
+ g->defer = d;
+ }
/*
* Announce we are entering a system call
* so that the scheduler knows to create another
* M to run goroutines while we are in the
* foreign code.
+ *
+ * The call to asmcgocall is guaranteed not to
+ * split the stack and does not allocate memory,
+ * so it is safe to call while "in a system call", outside
+ * the $GOMAXPROCS accounting.
*/
runtime·entersyscall();
- runtime·runcgo(fn, arg);
+ runtime·asmcgocall(fn, arg);
runtime·exitsyscall();
- m->lockedg = oldlock;
- if(oldlock == nil)
- g->lockedm = nil;
-
- return;
+ if(d != nil) {
+ if(g->defer != d || d->fn != (byte*)unlockm)
+ runtime·throw("runtime: bad defer entry in cgocallback");
+ g->defer = d->link;
+ runtime·free(d);
+ unlockm();
+ }
}
-// When a C function calls back into Go, the wrapper function will
-// call this. This switches to a Go stack, copies the arguments
-// (arg/argsize) on to the stack, calls the function, copies the
-// arguments back where they came from, and finally returns to the old
-// stack.
-void
-runtime·cgocallback(void (*fn)(void), void *arg, int32 argsize)
+static void
+unlockm(void)
{
- Gobuf oldsched, oldg1sched;
- G *g1;
- void *sp;
-
- if(g != m->g0)
- runtime·throw("bad g in cgocallback");
-
- g1 = m->curg;
- oldsched = m->sched;
- oldg1sched = g1->sched;
-
- runtime·startcgocallback(g1);
-
- sp = g1->sched.sp - argsize;
- if(sp < g1->stackguard - StackGuard - StackSystem + 8) // +8 for return address
- runtime·throw("g stack overflow in cgocallback");
- runtime·mcpy(sp, arg, argsize);
-
- runtime·runcgocallback(g1, sp, fn);
-
- runtime·mcpy(arg, sp, argsize);
-
- runtime·endcgocallback(g1);
-
- m->sched = oldsched;
- g1->sched = oldg1sched;
+ m->lockedg = nil;
+ g->lockedm = nil;
}
void
@@ -92,6 +159,8 @@ runtime·Cgocalls(int64 ret)
FLUSH(&ret);
}
+// Helper functions for cgo code.
+
void (*_cgo_malloc)(void*);
void (*_cgo_free)(void*);
@@ -115,3 +184,63 @@ runtime·cfree(void *p)
runtime·cgocall(_cgo_free, p);
}
+// Call from C back to Go.
+
+void
+runtime·cgocallbackg(void (*fn)(void), void *arg, uintptr argsize)
+{
+ Defer *d;
+
+ if(g != m->curg)
+ runtime·throw("runtime: bad g in cgocallback");
+
+ runtime·exitsyscall(); // coming out of cgo call
+
+ // Add entry to defer stack in case of panic.
+ d = runtime·malloc(sizeof(*d));
+ d->fn = (byte*)unwindm;
+ d->siz = 0;
+ d->link = g->defer;
+ d->argp = (void*)-1; // unused because unwindm never recovers
+ g->defer = d;
+
+ // Invoke callback.
+ reflect·call((byte*)fn, arg, argsize);
+
+ // Pop defer.
+ // Do not unwind m->g0->sched.sp.
+ // Our caller, cgocallback, will do that.
+ if(g->defer != d || d->fn != (byte*)unwindm)
+ runtime·throw("runtime: bad defer entry in cgocallback");
+ g->defer = d->link;
+ runtime·free(d);
+
+ runtime·entersyscall(); // going back to cgo call
+}
+
+static void
+unwindm(void)
+{
+ // Restore sp saved by cgocallback during
+ // unwind of g's stack (see comment at top of file).
+ switch(thechar){
+ default:
+ runtime·throw("runtime: unwindm not implemented");
+ case '8':
+ case '6':
+ m->g0->sched.sp = *(void**)m->g0->sched.sp;
+ break;
+ }
+}
+
+void
+runtime·badcgocallback(void) // called from assembly
+{
+ runtime·throw("runtime: misaligned stack in cgocallback");
+}
+
+void
+runtime·cgounimpl(void) // called from (incomplete) assembly
+{
+ runtime·throw("runtime: cgo not implemented");
+}
diff --git a/src/pkg/runtime/cgocall.h b/src/pkg/runtime/cgocall.h
index 1ad954eb1..253661a7e 100644
--- a/src/pkg/runtime/cgocall.h
+++ b/src/pkg/runtime/cgocall.h
@@ -7,6 +7,6 @@
*/
void runtime·cgocall(void (*fn)(void*), void*);
-void runtime·cgocallback(void (*fn)(void), void*, int32);
+void runtime·cgocallback(void (*fn)(void), void*, uintptr);
void *runtime·cmalloc(uintptr);
void runtime·cfree(void*);
diff --git a/src/pkg/runtime/chan.c b/src/pkg/runtime/chan.c
index 3177c2295..8c45b076d 100644
--- a/src/pkg/runtime/chan.c
+++ b/src/pkg/runtime/chan.c
@@ -5,13 +5,9 @@
#include "runtime.h"
#include "type.h"
-static int32 debug = 0;
+#define MAXALIGN 7
-enum
-{
- Wclosed = 0x0001, // writer has closed
- Rclosed = 0x0002, // reader has seen close
-};
+static int32 debug = 0;
typedef struct Link Link;
typedef struct WaitQ WaitQ;
@@ -40,32 +36,47 @@ struct Hchan
uint32 qcount; // total data in the q
uint32 dataqsiz; // size of the circular q
uint16 elemsize;
- uint16 closed; // Wclosed Rclosed errorcount
+ bool closed;
uint8 elemalign;
Alg* elemalg; // interface for element type
- Link* senddataq; // pointer for sender
- Link* recvdataq; // pointer for receiver
+ uint32 sendx; // send index
+ uint32 recvx; // receive index
WaitQ recvq; // list of recv waiters
WaitQ sendq; // list of send waiters
SudoG* free; // freelist
Lock;
};
+// Buffer follows Hchan immediately in memory.
+// chanbuf(c, i) is pointer to the i'th slot in the buffer.
+#define chanbuf(c, i) ((byte*)((c)+1)+(uintptr)(c)->elemsize*(i))
+
struct Link
{
Link* link; // asynch queue circular linked list
byte elem[8]; // asynch queue data element (+ more)
};
+enum
+{
+ // Scase.kind
+ CaseRecv,
+ CaseSend,
+ CaseDefault,
+};
+
struct Scase
{
Hchan* chan; // chan
byte* pc; // return pc
- uint16 send; // 0-recv 1-send 2-default
+ uint16 kind;
uint16 so; // vararg of selected bool
union {
- byte elem[8]; // element (send)
- byte* elemp; // pointer to element (recv)
+ byte elem[2*sizeof(void*)]; // element (send)
+ struct {
+ byte* elemp; // pointer to element (recv)
+ bool* receivedp; // pointer to received bool (recv2)
+ } recv;
} u;
};
@@ -90,7 +101,8 @@ Hchan*
runtime·makechan_c(Type *elem, int64 hint)
{
Hchan *c;
- int32 i;
+ int32 n;
+ byte *by;
if(hint < 0 || (int32)hint != hint || hint > ((uintptr)-1) / elem->size)
runtime·panicstring("makechan: size out of range");
@@ -100,32 +112,22 @@ runtime·makechan_c(Type *elem, int64 hint)
runtime·throw("runtime.makechan: unsupported elem type");
}
- c = runtime·mal(sizeof(*c));
+ // calculate rounded size of Hchan
+ n = sizeof(*c);
+ while(n & MAXALIGN)
+ n++;
+
+ // allocate memory in one call
+ by = runtime·mal(n + hint*elem->size);
+
+ c = (Hchan*)by;
+ by += n;
runtime·addfinalizer(c, destroychan, 0);
c->elemsize = elem->size;
c->elemalg = &runtime·algarray[elem->alg];
c->elemalign = elem->align;
-
- if(hint > 0) {
- Link *d, *b, *e;
-
- // make a circular q
- b = nil;
- e = nil;
- for(i=0; i<hint; i++) {
- d = runtime·mal(sizeof(*d) + c->elemsize - sizeof(d->elem));
- if(e == nil)
- e = d;
- d->link = b;
- b = d;
- }
- e->link = b;
- c->recvdataq = b;
- c->senddataq = b;
- c->qcount = 0;
- c->dataqsiz = hint;
- }
+ c->dataqsiz = hint;
if(debug)
runtime·printf("makechan: chan=%p; elemsize=%D; elemalg=%d; elemalign=%d; dataqsiz=%d\n",
@@ -183,7 +185,7 @@ runtime·chansend(Hchan *c, byte *ep, bool *pres)
runtime·lock(c);
loop:
- if(c->closed & Wclosed)
+ if(c->closed)
goto closed;
if(c->dataqsiz > 0)
@@ -228,7 +230,7 @@ loop:
return;
asynch:
- if(c->closed & Wclosed)
+ if(c->closed)
goto closed;
if(c->qcount >= c->dataqsiz) {
@@ -247,8 +249,9 @@ asynch:
goto asynch;
}
if(ep != nil)
- c->elemalg->copy(c->elemsize, c->senddataq->elem, ep);
- c->senddataq = c->senddataq->link;
+ c->elemalg->copy(c->elemsize, chanbuf(c, c->sendx), ep);
+ if(++c->sendx == c->dataqsiz)
+ c->sendx = 0;
c->qcount++;
sg = dequeue(&c->recvq, c);
@@ -269,7 +272,7 @@ closed:
}
void
-runtime·chanrecv(Hchan* c, byte *ep, bool *pres, bool *closed)
+runtime·chanrecv(Hchan* c, byte *ep, bool *selected, bool *received)
{
SudoG *sg;
G *gp;
@@ -284,14 +287,12 @@ runtime·chanrecv(Hchan* c, byte *ep, bool *pres, bool *closed)
runtime·printf("chanrecv: chan=%p\n", c);
runtime·lock(c);
- if(closed != nil)
- *closed = false;
loop:
if(c->dataqsiz > 0)
goto asynch;
- if(c->closed & Wclosed)
+ if(c->closed)
goto closed;
sg = dequeue(&c->sendq, c);
@@ -305,14 +306,16 @@ loop:
runtime·unlock(c);
runtime·ready(gp);
- if(pres != nil)
- *pres = true;
+ if(selected != nil)
+ *selected = true;
+ if(received != nil)
+ *received = true;
return;
}
- if(pres != nil) {
+ if(selected != nil) {
runtime·unlock(c);
- *pres = false;
+ *selected = false;
return;
}
@@ -331,18 +334,22 @@ loop:
if(ep != nil)
c->elemalg->copy(c->elemsize, ep, sg->elem);
c->elemalg->copy(c->elemsize, sg->elem, nil);
+ if(received != nil)
+ *received = true;
freesg(c, sg);
runtime·unlock(c);
return;
asynch:
if(c->qcount <= 0) {
- if(c->closed & Wclosed)
+ if(c->closed)
goto closed;
- if(pres != nil) {
+ if(selected != nil) {
runtime·unlock(c);
- *pres = false;
+ *selected = false;
+ if(received != nil)
+ *received = false;
return;
}
sg = allocsg(c);
@@ -355,9 +362,10 @@ asynch:
goto asynch;
}
if(ep != nil)
- c->elemalg->copy(c->elemsize, ep, c->recvdataq->elem);
- c->elemalg->copy(c->elemsize, c->recvdataq->elem, nil);
- c->recvdataq = c->recvdataq->link;
+ c->elemalg->copy(c->elemsize, ep, chanbuf(c, c->recvx));
+ c->elemalg->copy(c->elemsize, chanbuf(c, c->recvx), nil);
+ if(++c->recvx == c->dataqsiz)
+ c->recvx = 0;
c->qcount--;
sg = dequeue(&c->sendq, c);
if(sg != nil) {
@@ -365,24 +373,22 @@ asynch:
freesg(c, sg);
runtime·unlock(c);
runtime·ready(gp);
- if(pres != nil)
- *pres = true;
- return;
- }
+ } else
+ runtime·unlock(c);
- runtime·unlock(c);
- if(pres != nil)
- *pres = true;
+ if(selected != nil)
+ *selected = true;
+ if(received != nil)
+ *received = true;
return;
closed:
- if(closed != nil)
- *closed = true;
if(ep != nil)
c->elemalg->copy(c->elemsize, ep, nil);
- c->closed |= Rclosed;
- if(pres != nil)
- *pres = true;
+ if(selected != nil)
+ *selected = true;
+ if(received != nil)
+ *received = false;
runtime·unlock(c);
}
@@ -416,16 +422,16 @@ runtime·chanrecv1(Hchan* c, ...)
runtime·chanrecv(c, ae, nil, nil);
}
-// chanrecv3(hchan *chan any) (elem any, closed bool);
+// chanrecv2(hchan *chan any) (elem any, received bool);
#pragma textflag 7
void
-runtime·chanrecv3(Hchan* c, ...)
+runtime·chanrecv2(Hchan* c, ...)
{
int32 o;
byte *ae, *ac;
if(c == nil)
- runtime·panicstring("range over nil channel");
+ runtime·panicstring("receive from nil channel");
o = runtime·rnd(sizeof(c), Structrnd);
ae = (byte*)&c + o;
@@ -490,9 +496,35 @@ runtime·selectnbsend(Hchan *c, ...)
//
#pragma textflag 7
void
-runtime·selectnbrecv(byte *v, Hchan *c, bool ok)
+runtime·selectnbrecv(byte *v, Hchan *c, bool selected)
{
- runtime·chanrecv(c, v, &ok, nil);
+ runtime·chanrecv(c, v, &selected, nil);
+}
+
+// func selectnbrecv2(elem *any, ok *bool, c chan any) bool
+//
+// compiler implements
+//
+// select {
+// case v, ok = <-c:
+// ... foo
+// default:
+// ... bar
+// }
+//
+// as
+//
+// if c != nil && selectnbrecv2(&v, &ok, c) {
+// ... foo
+// } else {
+// ... bar
+// }
+//
+#pragma textflag 7
+void
+runtime·selectnbrecv2(byte *v, bool *received, Hchan *c, bool selected)
+{
+ runtime·chanrecv(c, v, &selected, received);
}
static void newselect(int32, Select**);
@@ -530,19 +562,30 @@ newselect(int32 size, Select **selp)
runtime·printf("newselect s=%p size=%d\n", sel, size);
}
+// cut in half to give stack a chance to split
+static void selectsend(Select **selp, Hchan *c, void *pc);
+
// selectsend(sel *byte, hchan *chan any, elem any) (selected bool);
#pragma textflag 7
void
runtime·selectsend(Select *sel, Hchan *c, ...)
{
- int32 i, eo;
- Scase *cas;
- byte *ae;
-
// nil cases do not compete
if(c == nil)
return;
+
+ selectsend(&sel, c, runtime·getcallerpc(&sel));
+}
+static void
+selectsend(Select **selp, Hchan *c, void *pc)
+{
+ int32 i, eo;
+ Scase *cas;
+ byte *ae;
+ Select *sel;
+
+ sel = *selp;
i = sel->ncase;
if(i >= sel->tcase)
runtime·throw("selectsend: too many cases");
@@ -550,67 +593,88 @@ runtime·selectsend(Select *sel, Hchan *c, ...)
cas = runtime·mal(sizeof *cas + c->elemsize - sizeof(cas->u.elem));
sel->scase[i] = cas;
- cas->pc = runtime·getcallerpc(&sel);
+ cas->pc = pc;
cas->chan = c;
eo = runtime·rnd(sizeof(sel), sizeof(c));
eo = runtime·rnd(eo+sizeof(c), c->elemsize);
cas->so = runtime·rnd(eo+c->elemsize, Structrnd);
- cas->send = 1;
+ cas->kind = CaseSend;
- ae = (byte*)&sel + eo;
+ ae = (byte*)selp + eo;
c->elemalg->copy(c->elemsize, cas->u.elem, ae);
if(debug)
- runtime·printf("selectsend s=%p pc=%p chan=%p so=%d send=%d\n",
- sel, cas->pc, cas->chan, cas->so, cas->send);
+ runtime·printf("selectsend s=%p pc=%p chan=%p so=%d\n",
+ sel, cas->pc, cas->chan, cas->so);
}
+// cut in half to give stack a chance to split
+static void selectrecv(Select *sel, Hchan *c, void *pc, void *elem, bool*, int32 so);
+
// selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool);
#pragma textflag 7
void
-runtime·selectrecv(Select *sel, Hchan *c, ...)
+runtime·selectrecv(Select *sel, Hchan *c, void *elem, bool selected)
{
- int32 i, eo;
- Scase *cas;
+ // nil cases do not compete
+ if(c == nil)
+ return;
+
+ selectrecv(sel, c, runtime·getcallerpc(&sel), elem, nil, (byte*)&selected - (byte*)&sel);
+}
+// selectrecv2(sel *byte, hchan *chan any, elem *any, received *bool) (selected bool);
+#pragma textflag 7
+void
+runtime·selectrecv2(Select *sel, Hchan *c, void *elem, bool *received, bool selected)
+{
// nil cases do not compete
if(c == nil)
return;
+ selectrecv(sel, c, runtime·getcallerpc(&sel), elem, received, (byte*)&selected - (byte*)&sel);
+}
+
+static void
+selectrecv(Select *sel, Hchan *c, void *pc, void *elem, bool *received, int32 so)
+{
+ int32 i;
+ Scase *cas;
+
i = sel->ncase;
if(i >= sel->tcase)
runtime·throw("selectrecv: too many cases");
sel->ncase = i+1;
cas = runtime·mal(sizeof *cas);
sel->scase[i] = cas;
- cas->pc = runtime·getcallerpc(&sel);
+ cas->pc = pc;
cas->chan = c;
- eo = runtime·rnd(sizeof(sel), sizeof(c));
- eo = runtime·rnd(eo+sizeof(c), sizeof(byte*));
- cas->so = runtime·rnd(eo+sizeof(byte*), Structrnd);
- cas->send = 0;
- cas->u.elemp = *(byte**)((byte*)&sel + eo);
+ cas->so = so;
+ cas->kind = CaseRecv;
+ cas->u.recv.elemp = elem;
+ cas->u.recv.receivedp = nil;
+ cas->u.recv.receivedp = received;
if(debug)
- runtime·printf("selectrecv s=%p pc=%p chan=%p so=%d send=%d\n",
- sel, cas->pc, cas->chan, cas->so, cas->send);
+ runtime·printf("selectrecv s=%p pc=%p chan=%p so=%d\n",
+ sel, cas->pc, cas->chan, cas->so);
}
-
-static void selectdefault(Select*, void*);
+// cut in half to give stack a chance to split
+static void selectdefault(Select*, void*, int32);
// selectdefault(sel *byte) (selected bool);
#pragma textflag 7
void
-runtime·selectdefault(Select *sel, ...)
+runtime·selectdefault(Select *sel, bool selected)
{
- selectdefault(sel, runtime·getcallerpc(&sel));
+ selectdefault(sel, runtime·getcallerpc(&sel), (byte*)&selected - (byte*)&sel);
}
static void
-selectdefault(Select *sel, void *callerpc)
+selectdefault(Select *sel, void *callerpc, int32 so)
{
int32 i;
Scase *cas;
@@ -624,13 +688,12 @@ selectdefault(Select *sel, void *callerpc)
cas->pc = callerpc;
cas->chan = nil;
- cas->so = runtime·rnd(sizeof(sel), Structrnd);
- cas->send = 2;
- cas->u.elemp = nil;
+ cas->so = so;
+ cas->kind = CaseDefault;
if(debug)
- runtime·printf("selectdefault s=%p pc=%p so=%d send=%d\n",
- sel, cas->pc, cas->so, cas->send);
+ runtime·printf("selectdefault s=%p pc=%p so=%d\n",
+ sel, cas->pc, cas->so);
}
static void
@@ -747,8 +810,8 @@ loop:
cas = sel->scase[o];
c = cas->chan;
- switch(cas->send) {
- case 0: // recv
+ switch(cas->kind) {
+ case CaseRecv:
if(c->dataqsiz > 0) {
if(c->qcount > 0)
goto asyncrecv;
@@ -757,12 +820,12 @@ loop:
if(sg != nil)
goto syncrecv;
}
- if(c->closed & Wclosed)
+ if(c->closed)
goto rclose;
break;
- case 1: // send
- if(c->closed & Wclosed)
+ case CaseSend:
+ if(c->closed)
goto sclose;
if(c->dataqsiz > 0) {
if(c->qcount < c->dataqsiz)
@@ -774,7 +837,7 @@ loop:
}
break;
- case 2: // default
+ case CaseDefault:
dfl = cas;
break;
}
@@ -794,12 +857,12 @@ loop:
sg = allocsg(c);
sg->offset = o;
- switch(cas->send) {
- case 0: // recv
+ switch(cas->kind) {
+ case CaseRecv:
enqueue(&c->recvq, sg);
break;
- case 1: // send
+ case CaseSend:
if(c->dataqsiz == 0)
c->elemalg->copy(c->elemsize, sg->elem, cas->u.elem);
enqueue(&c->sendq, sg);
@@ -821,7 +884,7 @@ loop:
if(sg == nil || i != sg->offset) {
cas = sel->scase[i];
c = cas->chan;
- if(cas->send)
+ if(cas->kind == CaseSend)
dequeueg(&c->sendq, c);
else
dequeueg(&c->recvq, c);
@@ -841,12 +904,14 @@ loop:
}
if(debug)
- runtime·printf("wait-return: sel=%p c=%p cas=%p send=%d o=%d\n",
- sel, c, cas, cas->send, o);
-
- if(!cas->send) {
- if(cas->u.elemp != nil)
- c->elemalg->copy(c->elemsize, cas->u.elemp, sg->elem);
+ runtime·printf("wait-return: sel=%p c=%p cas=%p kind=%d o=%d\n",
+ sel, c, cas, cas->kind, o);
+
+ if(cas->kind == CaseRecv) {
+ if(cas->u.recv.receivedp != nil)
+ *cas->u.recv.receivedp = true;
+ if(cas->u.recv.elemp != nil)
+ c->elemalg->copy(c->elemsize, cas->u.recv.elemp, sg->elem);
c->elemalg->copy(c->elemsize, sg->elem, nil);
}
@@ -855,10 +920,13 @@ loop:
asyncrecv:
// can receive from buffer
- if(cas->u.elemp != nil)
- c->elemalg->copy(c->elemsize, cas->u.elemp, c->recvdataq->elem);
- c->elemalg->copy(c->elemsize, c->recvdataq->elem, nil);
- c->recvdataq = c->recvdataq->link;
+ if(cas->u.recv.receivedp != nil)
+ *cas->u.recv.receivedp = true;
+ if(cas->u.recv.elemp != nil)
+ c->elemalg->copy(c->elemsize, cas->u.recv.elemp, chanbuf(c, c->recvx));
+ c->elemalg->copy(c->elemsize, chanbuf(c, c->recvx), nil);
+ if(++c->recvx == c->dataqsiz)
+ c->recvx = 0;
c->qcount--;
sg = dequeue(&c->sendq, c);
if(sg != nil) {
@@ -871,8 +939,9 @@ asyncrecv:
asyncsend:
// can send to buffer
if(cas->u.elem != nil)
- c->elemalg->copy(c->elemsize, c->senddataq->elem, cas->u.elem);
- c->senddataq = c->senddataq->link;
+ c->elemalg->copy(c->elemsize, chanbuf(c, c->sendx), cas->u.elem);
+ if(++c->sendx == c->dataqsiz)
+ c->sendx = 0;
c->qcount++;
sg = dequeue(&c->recvq, c);
if(sg != nil) {
@@ -886,8 +955,10 @@ syncrecv:
// can receive from sleeping sender (sg)
if(debug)
runtime·printf("syncrecv: sel=%p c=%p o=%d\n", sel, c, o);
- if(cas->u.elemp != nil)
- c->elemalg->copy(c->elemsize, cas->u.elemp, sg->elem);
+ if(cas->u.recv.receivedp != nil)
+ *cas->u.recv.receivedp = true;
+ if(cas->u.recv.elemp != nil)
+ c->elemalg->copy(c->elemsize, cas->u.recv.elemp, sg->elem);
c->elemalg->copy(c->elemsize, sg->elem, nil);
gp = sg->g;
gp->param = sg;
@@ -896,16 +967,17 @@ syncrecv:
rclose:
// read at end of closed channel
- if(cas->u.elemp != nil)
- c->elemalg->copy(c->elemsize, cas->u.elemp, nil);
- c->closed |= Rclosed;
+ if(cas->u.recv.receivedp != nil)
+ *cas->u.recv.receivedp = false;
+ if(cas->u.recv.elemp != nil)
+ c->elemalg->copy(c->elemsize, cas->u.recv.elemp, nil);
goto retc;
syncsend:
// can send to sleeping receiver (sg)
if(debug)
runtime·printf("syncsend: sel=%p c=%p o=%d\n", sel, c, o);
- if(c->closed & Wclosed)
+ if(c->closed)
goto sclose;
c->elemalg->copy(c->elemsize, sg->elem, cas->u.elem);
gp = sg->g;
@@ -916,7 +988,6 @@ retc:
selunlock(sel);
// return to pc corresponding to chosen case
-
pc = cas->pc;
as = (byte*)selp + cas->so;
freesel(sel);
@@ -941,12 +1012,12 @@ runtime·closechan(Hchan *c)
runtime·gosched();
runtime·lock(c);
- if(c->closed & Wclosed) {
+ if(c->closed) {
runtime·unlock(c);
runtime·panicstring("close of closed channel");
}
- c->closed |= Wclosed;
+ c->closed = true;
// release all readers
for(;;) {
@@ -979,12 +1050,6 @@ runtime·chanclose(Hchan *c)
runtime·closechan(c);
}
-bool
-runtime·chanclosed(Hchan *c)
-{
- return (c->closed & Rclosed) != 0;
-}
-
int32
runtime·chanlen(Hchan *c)
{
@@ -997,15 +1062,6 @@ runtime·chancap(Hchan *c)
return c->dataqsiz;
}
-
-// closedchan(sel *byte) bool;
-void
-runtime·closedchan(Hchan *c, bool closed)
-{
- closed = runtime·chanclosed(c);
- FLUSH(&closed);
-}
-
static SudoG*
dequeue(WaitQ *q, Hchan *c)
{
diff --git a/src/pkg/runtime/cpuprof.c b/src/pkg/runtime/cpuprof.c
new file mode 100644
index 000000000..6233bcb45
--- /dev/null
+++ b/src/pkg/runtime/cpuprof.c
@@ -0,0 +1,421 @@
+// 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.
+
+// CPU profiling.
+// Based on algorithms and data structures used in
+// http://code.google.com/p/google-perftools/.
+//
+// The main difference between this code and the google-perftools
+// code is that this code is written to allow copying the profile data
+// to an arbitrary io.Writer, while the google-perftools code always
+// writes to an operating system file.
+//
+// The signal handler for the profiling clock tick adds a new stack trace
+// to a hash table tracking counts for recent traces. Most clock ticks
+// hit in the cache. In the event of a cache miss, an entry must be
+// evicted from the hash table, copied to a log that will eventually be
+// written as profile data. The google-perftools code flushed the
+// log itself during the signal handler. This code cannot do that, because
+// the io.Writer might block or need system calls or locks that are not
+// safe to use from within the signal handler. Instead, we split the log
+// into two halves and let the signal handler fill one half while a goroutine
+// is writing out the other half. When the signal handler fills its half, it
+// offers to swap with the goroutine. If the writer is not done with its half,
+// we lose the stack trace for this clock tick (and record that loss).
+// The goroutine interacts with the signal handler by calling getprofile() to
+// get the next log piece to write, implicitly handing back the last log
+// piece it obtained.
+//
+// The state of this dance between the signal handler and the goroutine
+// is encoded in the Profile.handoff field. If handoff == 0, then the goroutine
+// is not using either log half and is waiting (or will soon be waiting) for
+// a new piece by calling notesleep(&p->wait). If the signal handler
+// changes handoff from 0 to non-zero, it must call notewakeup(&p->wait)
+// to wake the goroutine. The value indicates the number of entries in the
+// log half being handed off. The goroutine leaves the non-zero value in
+// place until it has finished processing the log half and then flips the number
+// back to zero. Setting the high bit in handoff means that the profiling is over,
+// and the goroutine is now in charge of flushing the data left in the hash table
+// to the log and returning that data.
+//
+// The handoff field is manipulated using atomic operations.
+// For the most part, the manipulation of handoff is orderly: if handoff == 0
+// then the signal handler owns it and can change it to non-zero.
+// If handoff != 0 then the goroutine owns it and can change it to zero.
+// If that were the end of the story then we would not need to manipulate
+// handoff using atomic operations. The operations are needed, however,
+// in order to let the log closer set the high bit to indicate "EOF" safely
+// in the situation when normally the goroutine "owns" handoff.
+
+#include "runtime.h"
+#include "malloc.h"
+
+enum
+{
+ HashSize = 1<<10,
+ LogSize = 1<<17,
+ Assoc = 4,
+ MaxStack = 64,
+};
+
+typedef struct Profile Profile;
+typedef struct Bucket Bucket;
+typedef struct Entry Entry;
+
+struct Entry {
+ uintptr count;
+ uintptr depth;
+ uintptr stack[MaxStack];
+};
+
+struct Bucket {
+ Entry entry[Assoc];
+};
+
+struct Profile {
+ bool on; // profiling is on
+ Note wait; // goroutine waits here
+ uintptr count; // tick count
+ uintptr evicts; // eviction count
+ uintptr lost; // lost ticks that need to be logged
+ uintptr totallost; // total lost ticks
+
+ // Active recent stack traces.
+ Bucket hash[HashSize];
+
+ // Log of traces evicted from hash.
+ // Signal handler has filled log[toggle][:nlog].
+ // Goroutine is writing log[1-toggle][:handoff].
+ uintptr log[2][LogSize/2];
+ uintptr nlog;
+ int32 toggle;
+ uint32 handoff;
+
+ // Writer state.
+ // Writer maintains its own toggle to avoid races
+ // looking at signal handler's toggle.
+ uint32 wtoggle;
+ bool wholding; // holding & need to release a log half
+ bool flushing; // flushing hash table - profile is over
+};
+
+static Lock lk;
+static Profile *prof;
+
+static void tick(uintptr*, int32);
+static void add(Profile*, uintptr*, int32);
+static bool evict(Profile*, Entry*);
+static bool flushlog(Profile*);
+
+// LostProfileData is a no-op function used in profiles
+// to mark the number of profiling stack traces that were
+// discarded due to slow data writers.
+static void LostProfileData(void) {
+}
+
+// SetCPUProfileRate sets the CPU profiling rate.
+// The user documentation is in debug.go.
+void
+runtime·SetCPUProfileRate(int32 hz)
+{
+ uintptr *p;
+ uintptr n;
+
+ // Clamp hz to something reasonable.
+ if(hz < 0)
+ hz = 0;
+ if(hz > 1000000)
+ hz = 1000000;
+
+ runtime·lock(&lk);
+ if(hz > 0) {
+ if(prof == nil) {
+ prof = runtime·SysAlloc(sizeof *prof);
+ if(prof == nil) {
+ runtime·printf("runtime: cpu profiling cannot allocate memory\n");
+ runtime·unlock(&lk);
+ return;
+ }
+ }
+ if(prof->on || prof->handoff != 0) {
+ runtime·printf("runtime: cannot set cpu profile rate until previous profile has finished.\n");
+ runtime·unlock(&lk);
+ return;
+ }
+
+ prof->on = true;
+ p = prof->log[0];
+ // pprof binary header format.
+ // http://code.google.com/p/google-perftools/source/browse/trunk/src/profiledata.cc#117
+ *p++ = 0; // count for header
+ *p++ = 3; // depth for header
+ *p++ = 0; // version number
+ *p++ = 1000000 / hz; // period (microseconds)
+ *p++ = 0;
+ prof->nlog = p - prof->log[0];
+ prof->toggle = 0;
+ prof->wholding = false;
+ prof->wtoggle = 0;
+ prof->flushing = false;
+ runtime·noteclear(&prof->wait);
+
+ runtime·setcpuprofilerate(tick, hz);
+ } else if(prof->on) {
+ runtime·setcpuprofilerate(nil, 0);
+ prof->on = false;
+
+ // Now add is not running anymore, and getprofile owns the entire log.
+ // Set the high bit in prof->handoff to tell getprofile.
+ for(;;) {
+ n = prof->handoff;
+ if(n&0x80000000)
+ runtime·printf("runtime: setcpuprofile(off) twice");
+ if(runtime·cas(&prof->handoff, n, n|0x80000000))
+ break;
+ }
+ if(n == 0) {
+ // we did the transition from 0 -> nonzero so we wake getprofile
+ runtime·notewakeup(&prof->wait);
+ }
+ }
+ runtime·unlock(&lk);
+}
+
+static void
+tick(uintptr *pc, int32 n)
+{
+ add(prof, pc, n);
+}
+
+// add adds the stack trace to the profile.
+// It is called from signal handlers and other limited environments
+// and cannot allocate memory or acquire locks that might be
+// held at the time of the signal, nor can it use substantial amounts
+// of stack. It is allowed to call evict.
+static void
+add(Profile *p, uintptr *pc, int32 n)
+{
+ int32 i, j;
+ uintptr h, x;
+ Bucket *b;
+ Entry *e;
+
+ if(n > MaxStack)
+ n = MaxStack;
+
+ // Compute hash.
+ h = 0;
+ for(i=0; i<n; i++) {
+ h = h<<8 | (h>>(8*(sizeof(h)-1)));
+ x = pc[i];
+ h += x*31 + x*7 + x*3;
+ }
+ p->count++;
+
+ // Add to entry count if already present in table.
+ b = &p->hash[h%HashSize];
+ for(i=0; i<Assoc; i++) {
+ e = &b->entry[i];
+ if(e->depth != n)
+ continue;
+ for(j=0; j<n; j++)
+ if(e->stack[j] != pc[j])
+ goto ContinueAssoc;
+ e->count++;
+ return;
+ ContinueAssoc:;
+ }
+
+ // Evict entry with smallest count.
+ e = &b->entry[0];
+ for(i=1; i<Assoc; i++)
+ if(b->entry[i].count < e->count)
+ e = &b->entry[i];
+ if(e->count > 0) {
+ if(!evict(p, e)) {
+ // Could not evict entry. Record lost stack.
+ p->lost++;
+ p->totallost++;
+ return;
+ }
+ p->evicts++;
+ }
+
+ // Reuse the newly evicted entry.
+ e->depth = n;
+ e->count = 1;
+ for(i=0; i<n; i++)
+ e->stack[i] = pc[i];
+}
+
+// evict copies the given entry's data into the log, so that
+// the entry can be reused. evict is called from add, which
+// is called from the profiling signal handler, so it must not
+// allocate memory or block. It is safe to call flushLog.
+// evict returns true if the entry was copied to the log,
+// false if there was no room available.
+static bool
+evict(Profile *p, Entry *e)
+{
+ int32 i, d, nslot;
+ uintptr *log, *q;
+
+ d = e->depth;
+ nslot = d+2;
+ log = p->log[p->toggle];
+ if(p->nlog+nslot > nelem(p->log[0])) {
+ if(!flushlog(p))
+ return false;
+ log = p->log[p->toggle];
+ }
+
+ q = log+p->nlog;
+ *q++ = e->count;
+ *q++ = d;
+ for(i=0; i<d; i++)
+ *q++ = e->stack[i];
+ p->nlog = q - log;
+ e->count = 0;
+ return true;
+}
+
+// flushlog tries to flush the current log and switch to the other one.
+// flushlog is called from evict, called from add, called from the signal handler,
+// so it cannot allocate memory or block. It can try to swap logs with
+// the writing goroutine, as explained in the comment at the top of this file.
+static bool
+flushlog(Profile *p)
+{
+ uintptr *log, *q;
+
+ if(!runtime·cas(&p->handoff, 0, p->nlog))
+ return false;
+ runtime·notewakeup(&p->wait);
+
+ p->toggle = 1 - p->toggle;
+ log = p->log[p->toggle];
+ q = log;
+ if(p->lost > 0) {
+ *q++ = p->lost;
+ *q++ = 1;
+ *q++ = (uintptr)LostProfileData;
+ }
+ p->nlog = q - log;
+ return true;
+}
+
+// getprofile blocks until the next block of profiling data is available
+// and returns it as a []byte. It is called from the writing goroutine.
+Slice
+getprofile(Profile *p)
+{
+ uint32 i, j, n;
+ Slice ret;
+ Bucket *b;
+ Entry *e;
+
+ ret.array = nil;
+ ret.len = 0;
+ ret.cap = 0;
+
+ if(p == nil)
+ return ret;
+
+ if(p->wholding) {
+ // Release previous log to signal handling side.
+ // Loop because we are racing against setprofile(off).
+ for(;;) {
+ n = p->handoff;
+ if(n == 0) {
+ runtime·printf("runtime: phase error during cpu profile handoff\n");
+ return ret;
+ }
+ if(n & 0x80000000) {
+ p->wtoggle = 1 - p->wtoggle;
+ p->wholding = false;
+ p->flushing = true;
+ goto flush;
+ }
+ if(runtime·cas(&p->handoff, n, 0))
+ break;
+ }
+ p->wtoggle = 1 - p->wtoggle;
+ p->wholding = false;
+ }
+
+ if(p->flushing)
+ goto flush;
+
+ if(!p->on && p->handoff == 0)
+ return ret;
+
+ // Wait for new log.
+ runtime·entersyscall();
+ runtime·notesleep(&p->wait);
+ runtime·exitsyscall();
+ runtime·noteclear(&p->wait);
+
+ n = p->handoff;
+ if(n == 0) {
+ runtime·printf("runtime: phase error during cpu profile wait\n");
+ return ret;
+ }
+ if(n == 0x80000000) {
+ p->flushing = true;
+ goto flush;
+ }
+ n &= ~0x80000000;
+
+ // Return new log to caller.
+ p->wholding = true;
+
+ ret.array = (byte*)p->log[p->wtoggle];
+ ret.len = n*sizeof(uintptr);
+ ret.cap = ret.len;
+ return ret;
+
+flush:
+ // In flush mode.
+ // Add is no longer being called. We own the log.
+ // Also, p->handoff is non-zero, so flushlog will return false.
+ // Evict the hash table into the log and return it.
+ for(i=0; i<HashSize; i++) {
+ b = &p->hash[i];
+ for(j=0; j<Assoc; j++) {
+ e = &b->entry[j];
+ if(e->count > 0 && !evict(p, e)) {
+ // Filled the log. Stop the loop and return what we've got.
+ goto breakflush;
+ }
+ }
+ }
+breakflush:
+
+ // Return pending log data.
+ if(p->nlog > 0) {
+ // Note that we're using toggle now, not wtoggle,
+ // because we're working on the log directly.
+ ret.array = (byte*)p->log[p->toggle];
+ ret.len = p->nlog*sizeof(uintptr);
+ ret.cap = ret.len;
+ p->nlog = 0;
+ return ret;
+ }
+
+ // Made it through the table without finding anything to log.
+ // Finally done. Clean up and return nil.
+ p->flushing = false;
+ if(!runtime·cas(&p->handoff, p->handoff, 0))
+ runtime·printf("runtime: profile flush racing with something\n");
+ return ret; // set to nil at top of function
+}
+
+// CPUProfile returns the next cpu profile block as a []byte.
+// The user documentation is in debug.go.
+void
+runtime·CPUProfile(Slice ret)
+{
+ ret = getprofile(prof);
+ FLUSH(&ret);
+}
diff --git a/src/pkg/runtime/darwin/386/defs.h b/src/pkg/runtime/darwin/386/defs.h
index f9d874d85..bb70207fd 100644
--- a/src/pkg/runtime/darwin/386/defs.h
+++ b/src/pkg/runtime/darwin/386/defs.h
@@ -89,6 +89,9 @@ enum {
BUS_OBJERR = 0x3,
SEGV_MAPERR = 0x1,
SEGV_ACCERR = 0x2,
+ ITIMER_REAL = 0,
+ ITIMER_VIRTUAL = 0x1,
+ ITIMER_PROF = 0x2,
};
// Types
@@ -139,14 +142,14 @@ struct StackT {
typedef union Sighandler Sighandler;
union Sighandler {
- void *__sa_handler;
- void *__sa_sigaction;
+ uint32 __sa_handler;
+ uint32 __sa_sigaction;
};
typedef struct Sigaction Sigaction;
struct Sigaction {
Sighandler __sigaction_u;
- void *sa_tramp;
+ uint32 sa_tramp;
uint32 sa_mask;
int32 sa_flags;
};
@@ -171,14 +174,26 @@ struct Siginfo {
uint32 __pad[7];
};
+typedef struct Timeval Timeval;
+struct Timeval {
+ int32 tv_sec;
+ int32 tv_usec;
+};
+
+typedef struct Itimerval Itimerval;
+struct Itimerval {
+ Timeval it_interval;
+ Timeval it_value;
+};
+
typedef struct FPControl FPControl;
struct FPControl {
- byte pad0[2];
+ byte pad_godefs_0[2];
};
typedef struct FPStatus FPStatus;
struct FPStatus {
- byte pad0[2];
+ byte pad_godefs_0[2];
};
typedef struct RegMMST RegMMST;
@@ -214,7 +229,7 @@ struct Regs {
typedef struct FloatState FloatState;
struct FloatState {
- int32 fpu_reserved[2];
+ uint64 fpu_reserved;
FPControl fpu_fcw;
FPStatus fpu_fsw;
uint8 fpu_ftw;
@@ -267,7 +282,7 @@ struct Ucontext {
int32 uc_onstack;
uint32 uc_sigmask;
StackT uc_stack;
- Ucontext *uc_link;
+ uint32 uc_link;
uint32 uc_mcsize;
Mcontext *uc_mcontext;
};
diff --git a/src/pkg/runtime/darwin/386/signal.c b/src/pkg/runtime/darwin/386/signal.c
index aeef5de3f..35bbb178b 100644
--- a/src/pkg/runtime/darwin/386/signal.c
+++ b/src/pkg/runtime/darwin/386/signal.c
@@ -46,6 +46,11 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp)
mc = uc->uc_mcontext;
r = &mc->ss;
+ if(sig == SIGPROF) {
+ runtime·sigprof((uint8*)r->eip, (uint8*)r->esp, nil, gp);
+ return;
+ }
+
if(gp != nil && (runtime·sigtab[sig].flags & SigPanic)) {
// Work around Leopard bug that doesn't set FPE_INTDIV.
// Look at instruction to see if it is a divide.
@@ -126,31 +131,57 @@ runtime·signalstack(byte *p, int32 n)
runtime·sigaltstack(&st, nil);
}
+static void
+sigaction(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart)
+{
+ Sigaction sa;
+
+ runtime·memclr((byte*)&sa, sizeof sa);
+ sa.sa_flags = SA_SIGINFO|SA_ONSTACK;
+ if(restart)
+ sa.sa_flags |= SA_RESTART;
+ sa.sa_mask = ~0U;
+ sa.sa_tramp = (uintptr)runtime·sigtramp; // runtime·sigtramp's job is to call into real handler
+ sa.__sigaction_u.__sa_sigaction = (uintptr)fn;
+ runtime·sigaction(i, &sa, nil);
+}
+
void
runtime·initsig(int32 queue)
{
int32 i;
- static Sigaction sa;
+ void *fn;
runtime·siginit();
- sa.sa_flags |= SA_SIGINFO|SA_ONSTACK;
- sa.sa_mask = 0xFFFFFFFFU;
- sa.sa_tramp = runtime·sigtramp; // runtime·sigtramp's job is to call into real handler
for(i = 0; i<NSIG; i++) {
if(runtime·sigtab[i].flags) {
if((runtime·sigtab[i].flags & SigQueue) != queue)
continue;
- if(runtime·sigtab[i].flags & (SigCatch | SigQueue)) {
- sa.__sigaction_u.__sa_sigaction = runtime·sighandler;
- } else {
- sa.__sigaction_u.__sa_sigaction = runtime·sigignore;
- }
- if(runtime·sigtab[i].flags & SigRestart)
- sa.sa_flags |= SA_RESTART;
+ if(runtime·sigtab[i].flags & (SigCatch | SigQueue))
+ fn = runtime·sighandler;
else
- sa.sa_flags &= ~SA_RESTART;
- runtime·sigaction(i, &sa, nil);
+ fn = runtime·sigignore;
+ sigaction(i, fn, (runtime·sigtab[i].flags & SigRestart) != 0);
}
}
}
+
+void
+runtime·resetcpuprofiler(int32 hz)
+{
+ Itimerval it;
+
+ runtime·memclr((byte*)&it, sizeof it);
+ if(hz == 0) {
+ runtime·setitimer(ITIMER_PROF, &it, nil);
+ sigaction(SIGPROF, SIG_IGN, true);
+ } else {
+ sigaction(SIGPROF, runtime·sighandler, true);
+ it.it_interval.tv_sec = 0;
+ it.it_interval.tv_usec = 1000000 / hz;
+ it.it_value = it.it_interval;
+ runtime·setitimer(ITIMER_PROF, &it, nil);
+ }
+ m->profilehz = hz;
+}
diff --git a/src/pkg/runtime/darwin/386/sys.s b/src/pkg/runtime/darwin/386/sys.s
index 9d2caca0a..08eca9d5a 100644
--- a/src/pkg/runtime/darwin/386/sys.s
+++ b/src/pkg/runtime/darwin/386/sys.s
@@ -45,6 +45,11 @@ TEXT runtime·munmap(SB),7,$0
CALL runtime·notok(SB)
RET
+TEXT runtime·setitimer(SB),7,$0
+ MOVL $83, AX
+ INT $0x80
+ RET
+
// void gettime(int64 *sec, int32 *usec)
TEXT runtime·gettime(SB), 7, $32
LEAL 12(SP), AX // must be non-nil, unused
diff --git a/src/pkg/runtime/darwin/amd64/defs.h b/src/pkg/runtime/darwin/amd64/defs.h
index 09e595988..90f798e8a 100644
--- a/src/pkg/runtime/darwin/amd64/defs.h
+++ b/src/pkg/runtime/darwin/amd64/defs.h
@@ -89,6 +89,9 @@ enum {
BUS_OBJERR = 0x3,
SEGV_MAPERR = 0x1,
SEGV_ACCERR = 0x2,
+ ITIMER_REAL = 0,
+ ITIMER_VIRTUAL = 0x1,
+ ITIMER_PROF = 0x2,
};
// Types
@@ -135,19 +138,19 @@ struct StackT {
void *ss_sp;
uint64 ss_size;
int32 ss_flags;
- byte pad0[4];
+ byte pad_godefs_0[4];
};
typedef union Sighandler Sighandler;
union Sighandler {
- void *__sa_handler;
- void *__sa_sigaction;
+ uint64 __sa_handler;
+ uint64 __sa_sigaction;
};
typedef struct Sigaction Sigaction;
struct Sigaction {
Sighandler __sigaction_u;
- void *sa_tramp;
+ uint64 sa_tramp;
uint32 sa_mask;
int32 sa_flags;
};
@@ -172,14 +175,27 @@ struct Siginfo {
uint64 __pad[7];
};
+typedef struct Timeval Timeval;
+struct Timeval {
+ int64 tv_sec;
+ int32 tv_usec;
+ byte pad_godefs_0[4];
+};
+
+typedef struct Itimerval Itimerval;
+struct Itimerval {
+ Timeval it_interval;
+ Timeval it_value;
+};
+
typedef struct FPControl FPControl;
struct FPControl {
- byte pad0[2];
+ byte pad_godefs_0[2];
};
typedef struct FPStatus FPStatus;
struct FPStatus {
- byte pad0[2];
+ byte pad_godefs_0[2];
};
typedef struct RegMMST RegMMST;
@@ -220,7 +236,7 @@ struct Regs {
typedef struct FloatState FloatState;
struct FloatState {
- int32 fpu_reserved[2];
+ uint64 fpu_reserved;
FPControl fpu_fcw;
FPStatus fpu_fsw;
uint8 fpu_ftw;
@@ -274,7 +290,7 @@ struct Mcontext {
ExceptionState es;
Regs ss;
FloatState fs;
- byte pad0[4];
+ byte pad_godefs_0[4];
};
typedef struct Ucontext Ucontext;
@@ -282,7 +298,7 @@ struct Ucontext {
int32 uc_onstack;
uint32 uc_sigmask;
StackT uc_stack;
- Ucontext *uc_link;
+ uint64 uc_link;
uint64 uc_mcsize;
Mcontext *uc_mcontext;
};
diff --git a/src/pkg/runtime/darwin/amd64/signal.c b/src/pkg/runtime/darwin/amd64/signal.c
index 402ab33ca..3a99d2308 100644
--- a/src/pkg/runtime/darwin/amd64/signal.c
+++ b/src/pkg/runtime/darwin/amd64/signal.c
@@ -54,6 +54,11 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp)
mc = uc->uc_mcontext;
r = &mc->ss;
+ if(sig == SIGPROF) {
+ runtime·sigprof((uint8*)r->rip, (uint8*)r->rsp, nil, gp);
+ return;
+ }
+
if(gp != nil && (runtime·sigtab[sig].flags & SigPanic)) {
// Work around Leopard bug that doesn't set FPE_INTDIV.
// Look at instruction to see if it is a divide.
@@ -136,31 +141,57 @@ runtime·signalstack(byte *p, int32 n)
runtime·sigaltstack(&st, nil);
}
+static void
+sigaction(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart)
+{
+ Sigaction sa;
+
+ runtime·memclr((byte*)&sa, sizeof sa);
+ sa.sa_flags = SA_SIGINFO|SA_ONSTACK;
+ if(restart)
+ sa.sa_flags |= SA_RESTART;
+ sa.sa_mask = ~0ULL;
+ sa.sa_tramp = (uintptr)runtime·sigtramp; // runtime·sigtramp's job is to call into real handler
+ sa.__sigaction_u.__sa_sigaction = (uintptr)fn;
+ runtime·sigaction(i, &sa, nil);
+}
+
void
runtime·initsig(int32 queue)
{
int32 i;
- static Sigaction sa;
+ void *fn;
runtime·siginit();
- sa.sa_flags |= SA_SIGINFO|SA_ONSTACK;
- sa.sa_mask = 0xFFFFFFFFU;
- sa.sa_tramp = runtime·sigtramp; // runtime·sigtramp's job is to call into real handler
for(i = 0; i<NSIG; i++) {
if(runtime·sigtab[i].flags) {
if((runtime·sigtab[i].flags & SigQueue) != queue)
continue;
- if(runtime·sigtab[i].flags & (SigCatch | SigQueue)) {
- sa.__sigaction_u.__sa_sigaction = runtime·sighandler;
- } else {
- sa.__sigaction_u.__sa_sigaction = runtime·sigignore;
- }
- if(runtime·sigtab[i].flags & SigRestart)
- sa.sa_flags |= SA_RESTART;
+ if(runtime·sigtab[i].flags & (SigCatch | SigQueue))
+ fn = runtime·sighandler;
else
- sa.sa_flags &= ~SA_RESTART;
- runtime·sigaction(i, &sa, nil);
+ fn = runtime·sigignore;
+ sigaction(i, fn, (runtime·sigtab[i].flags & SigRestart) != 0);
}
}
}
+
+void
+runtime·resetcpuprofiler(int32 hz)
+{
+ Itimerval it;
+
+ runtime·memclr((byte*)&it, sizeof it);
+ if(hz == 0) {
+ runtime·setitimer(ITIMER_PROF, &it, nil);
+ sigaction(SIGPROF, SIG_IGN, true);
+ } else {
+ sigaction(SIGPROF, runtime·sighandler, true);
+ it.it_interval.tv_sec = 0;
+ it.it_interval.tv_usec = 1000000 / hz;
+ it.it_value = it.it_interval;
+ runtime·setitimer(ITIMER_PROF, &it, nil);
+ }
+ m->profilehz = hz;
+}
diff --git a/src/pkg/runtime/darwin/amd64/sys.s b/src/pkg/runtime/darwin/amd64/sys.s
index 4f9e0d77a..39398e065 100644
--- a/src/pkg/runtime/darwin/amd64/sys.s
+++ b/src/pkg/runtime/darwin/amd64/sys.s
@@ -38,11 +38,19 @@ TEXT runtime·write(SB),7,$0
SYSCALL
RET
+TEXT runtime·setitimer(SB), 7, $0
+ MOVL 8(SP), DI
+ MOVQ 16(SP), SI
+ MOVQ 24(SP), DX
+ MOVL $(0x2000000+83), AX // syscall entry
+ SYSCALL
+ RET
+
// void gettime(int64 *sec, int32 *usec)
TEXT runtime·gettime(SB), 7, $32
MOVQ SP, DI // must be non-nil, unused
MOVQ $0, SI
- MOVQ $(0x2000000+116), AX
+ MOVL $(0x2000000+116), AX
SYSCALL
MOVQ sec+0(FP), DI
MOVQ AX, (DI)
@@ -138,8 +146,7 @@ TEXT runtime·bsdthread_create(SB),7,$0
MOVQ mm+16(SP), SI // "arg"
MOVQ stk+8(SP), DX // stack
MOVQ gg+24(SP), R10 // "pthread"
-// TODO(rsc): why do we get away with 0 flags here but not on 386?
- MOVQ $0, R8 // flags
+ MOVQ $0x01000000, R8 // flags = PTHREAD_START_CUSTOM
MOVQ $0, R9 // paranoia
MOVQ $(0x2000000+360), AX // bsdthread_create
SYSCALL
diff --git a/src/pkg/runtime/darwin/defs.c b/src/pkg/runtime/darwin/defs.c
index 1a1cdf880..032a6bcbb 100644
--- a/src/pkg/runtime/darwin/defs.c
+++ b/src/pkg/runtime/darwin/defs.c
@@ -116,6 +116,10 @@ enum {
$SEGV_MAPERR = SEGV_MAPERR,
$SEGV_ACCERR = SEGV_ACCERR,
+
+ $ITIMER_REAL = ITIMER_REAL,
+ $ITIMER_VIRTUAL = ITIMER_VIRTUAL,
+ $ITIMER_PROF = ITIMER_PROF,
};
typedef mach_msg_body_t $MachBody;
@@ -130,6 +134,8 @@ typedef struct __sigaction $Sigaction; // used in syscalls
// typedef struct sigaction $Sigaction; // used by the C library
typedef union sigval $Sigval;
typedef siginfo_t $Siginfo;
+typedef struct timeval $Timeval;
+typedef struct itimerval $Itimerval;
typedef struct fp_control $FPControl;
typedef struct fp_status $FPStatus;
diff --git a/src/pkg/runtime/darwin/os.h b/src/pkg/runtime/darwin/os.h
index 35ef4e6d9..339768e51 100644
--- a/src/pkg/runtime/darwin/os.h
+++ b/src/pkg/runtime/darwin/os.h
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#define SIG_DFL ((void*)0)
+#define SIG_IGN ((void*)1)
+
int32 runtime·bsdthread_create(void*, M*, G*, void(*)(void));
void runtime·bsdthread_register(void);
int32 runtime·mach_msg_trap(MachHeader*, int32, uint32, uint32, uint32, uint32, uint32);
@@ -23,3 +26,4 @@ struct StackT;
void runtime·sigaltstack(struct StackT*, struct StackT*);
void runtime·sigtramp(void);
void runtime·sigpanic(void);
+void runtime·setitimer(int32, Itimerval*, Itimerval*);
diff --git a/src/pkg/runtime/debug.go b/src/pkg/runtime/debug.go
index 5117e1a55..6370a57d8 100644
--- a/src/pkg/runtime/debug.go
+++ b/src/pkg/runtime/debug.go
@@ -4,8 +4,6 @@
package runtime
-import "unsafe"
-
// Breakpoint() executes a breakpoint trap.
func Breakpoint()
@@ -31,65 +29,6 @@ func Cgocalls() int64
// Goroutines returns the number of goroutines that currently exist.
func Goroutines() int32
-type MemStatsType struct {
- // General statistics.
- // Not locked during update; approximate.
- Alloc uint64 // bytes allocated and still in use
- TotalAlloc uint64 // bytes allocated (even if freed)
- Sys uint64 // bytes obtained from system (should be sum of XxxSys below)
- Lookups uint64 // number of pointer lookups
- Mallocs uint64 // number of mallocs
- Frees uint64 // number of frees
-
- // Main allocation heap statistics.
- HeapAlloc uint64 // bytes allocated and still in use
- HeapSys uint64 // bytes obtained from system
- HeapIdle uint64 // bytes in idle spans
- HeapInuse uint64 // bytes in non-idle span
- HeapObjects uint64 // total number of allocated objects
-
- // Low-level fixed-size structure allocator statistics.
- // Inuse is bytes used now.
- // Sys is bytes obtained from system.
- StackInuse uint64 // bootstrap stacks
- StackSys uint64
- MSpanInuse uint64 // mspan structures
- MSpanSys uint64
- MCacheInuse uint64 // mcache structures
- MCacheSys uint64
- BuckHashSys uint64 // profiling bucket hash table
-
- // Garbage collector statistics.
- NextGC uint64
- PauseTotalNs uint64
- PauseNs [256]uint64 // most recent GC pause times
- NumGC uint32
- EnableGC bool
- DebugGC bool
-
- // Per-size allocation statistics.
- // Not locked during update; approximate.
- // 61 is NumSizeClasses in the C code.
- BySize [61]struct {
- Size uint32
- Mallocs uint64
- Frees uint64
- }
-}
-
-var sizeof_C_MStats int // filled in by malloc.goc
-
-func init() {
- if sizeof_C_MStats != unsafe.Sizeof(MemStats) {
- println(sizeof_C_MStats, unsafe.Sizeof(MemStats))
- panic("MStats vs MemStatsType size mismatch")
- }
-}
-
-// MemStats holds statistics about the memory system.
-// The statistics are only approximate, as they are not interlocked on update.
-var MemStats MemStatsType
-
// Alloc allocates a block of the given size.
// FOR TESTING AND DEBUGGING ONLY.
func Alloc(uintptr) *byte
@@ -102,9 +41,6 @@ func Free(*byte)
// FOR TESTING AND DEBUGGING ONLY.
func Lookup(*byte) (*byte, uintptr)
-// GC runs a garbage collection.
-func GC()
-
// MemProfileRate controls the fraction of memory allocations
// that are recorded and reported in the memory profile.
// The profiler aims to sample an average of
@@ -156,4 +92,24 @@ func (r *MemProfileRecord) Stack() []uintptr {
// where r.AllocBytes > 0 but r.AllocBytes == r.FreeBytes.
// These are sites where memory was allocated, but it has all
// been released back to the runtime.
+// Most clients should use the runtime/pprof package or
+// the testing package's -test.memprofile flag instead
+// of calling MemProfile directly.
func MemProfile(p []MemProfileRecord, inuseZero bool) (n int, ok bool)
+
+// CPUProfile returns the next chunk of binary CPU profiling stack trace data,
+// blocking until data is available. If profiling is turned off and all the profile
+// data accumulated while it was on has been returned, CPUProfile returns nil.
+// The caller must save the returned data before calling CPUProfile again.
+// Most clients should use the runtime/pprof package or
+// the testing package's -test.cpuprofile flag instead of calling
+// CPUProfile directly.
+func CPUProfile() []byte
+
+// SetCPUProfileRate sets the CPU profiling rate to hz samples per second.
+// If hz <= 0, SetCPUProfileRate turns off profiling.
+// If the profiler is on, the rate cannot be changed without first turning it off.
+// Most clients should use the runtime/pprof package or
+// the testing package's -test.cpuprofile flag instead of calling
+// SetCPUProfileRate directly.
+func SetCPUProfileRate(hz int)
diff --git a/src/pkg/runtime/freebsd/386/defs.h b/src/pkg/runtime/freebsd/386/defs.h
index 128be9cc9..ae12b2019 100644
--- a/src/pkg/runtime/freebsd/386/defs.h
+++ b/src/pkg/runtime/freebsd/386/defs.h
@@ -61,6 +61,9 @@ enum {
BUS_OBJERR = 0x3,
SEGV_MAPERR = 0x1,
SEGV_ACCERR = 0x2,
+ ITIMER_REAL = 0,
+ ITIMER_VIRTUAL = 0x1,
+ ITIMER_PROF = 0x2,
};
// Types
@@ -154,7 +157,9 @@ struct Mcontext {
int32 mc_ownedfp;
int32 mc_spare1[1];
int32 mc_fpstate[128];
- int32 mc_spare2[8];
+ int32 mc_fsbase;
+ int32 mc_gsbase;
+ int32 mc_spare2[6];
};
typedef struct Ucontext Ucontext;
@@ -165,6 +170,18 @@ struct Ucontext {
StackT uc_stack;
int32 uc_flags;
int32 __spare__[4];
- byte pad0[12];
+ byte pad_godefs_0[12];
+};
+
+typedef struct Timeval Timeval;
+struct Timeval {
+ int32 tv_sec;
+ int32 tv_usec;
+};
+
+typedef struct Itimerval Itimerval;
+struct Itimerval {
+ Timeval it_interval;
+ Timeval it_value;
};
#pragma pack off
diff --git a/src/pkg/runtime/freebsd/386/signal.c b/src/pkg/runtime/freebsd/386/signal.c
index 8e9d74256..1ae2554eb 100644
--- a/src/pkg/runtime/freebsd/386/signal.c
+++ b/src/pkg/runtime/freebsd/386/signal.c
@@ -54,6 +54,11 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp)
uc = context;
r = &uc->uc_mcontext;
+ if(sig == SIGPROF) {
+ runtime·sigprof((uint8*)r->mc_eip, (uint8*)r->mc_esp, nil, gp);
+ return;
+ }
+
if(gp != nil && (runtime·sigtab[sig].flags & SigPanic)) {
// Make it look like a call to the signal func.
// Have to pass arguments out of band since
@@ -122,32 +127,58 @@ runtime·signalstack(byte *p, int32 n)
runtime·sigaltstack(&st, nil);
}
+static void
+sigaction(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart)
+{
+ Sigaction sa;
+
+ runtime·memclr((byte*)&sa, sizeof sa);
+ sa.sa_flags = SA_SIGINFO|SA_ONSTACK;
+ if(restart)
+ sa.sa_flags |= SA_RESTART;
+ sa.sa_mask = ~0ULL;
+ if (fn == runtime·sighandler)
+ fn = (void*)runtime·sigtramp;
+ sa.__sigaction_u.__sa_sigaction = (void*)fn;
+ runtime·sigaction(i, &sa, nil);
+}
+
void
runtime·initsig(int32 queue)
{
- static Sigaction sa;
+ int32 i;
+ void *fn;
runtime·siginit();
- int32 i;
- sa.sa_flags |= SA_ONSTACK | SA_SIGINFO;
- sa.sa_mask = ~0x0ull;
-
- for(i = 0; i < NSIG; i++) {
+ for(i = 0; i<NSIG; i++) {
if(runtime·sigtab[i].flags) {
if((runtime·sigtab[i].flags & SigQueue) != queue)
continue;
if(runtime·sigtab[i].flags & (SigCatch | SigQueue))
- sa.__sigaction_u.__sa_sigaction = (void*) runtime·sigtramp;
+ fn = runtime·sighandler;
else
- sa.__sigaction_u.__sa_sigaction = (void*) runtime·sigignore;
-
- if(runtime·sigtab[i].flags & SigRestart)
- sa.sa_flags |= SA_RESTART;
- else
- sa.sa_flags &= ~SA_RESTART;
-
- runtime·sigaction(i, &sa, nil);
+ fn = runtime·sigignore;
+ sigaction(i, fn, (runtime·sigtab[i].flags & SigRestart) != 0);
}
}
}
+
+void
+runtime·resetcpuprofiler(int32 hz)
+{
+ Itimerval it;
+
+ runtime·memclr((byte*)&it, sizeof it);
+ if(hz == 0) {
+ runtime·setitimer(ITIMER_PROF, &it, nil);
+ sigaction(SIGPROF, SIG_IGN, true);
+ } else {
+ sigaction(SIGPROF, runtime·sighandler, true);
+ it.it_interval.tv_sec = 0;
+ it.it_interval.tv_usec = 1000000 / hz;
+ it.it_value = it.it_interval;
+ runtime·setitimer(ITIMER_PROF, &it, nil);
+ }
+ m->profilehz = hz;
+}
diff --git a/src/pkg/runtime/freebsd/386/sys.s b/src/pkg/runtime/freebsd/386/sys.s
index 60c189bf8..c4715b668 100644
--- a/src/pkg/runtime/freebsd/386/sys.s
+++ b/src/pkg/runtime/freebsd/386/sys.s
@@ -87,6 +87,11 @@ TEXT runtime·munmap(SB),7,$-4
CALL runtime·notok(SB)
RET
+TEXT runtime·setitimer(SB), 7, $-4
+ MOVL $83, AX
+ INT $0x80
+ RET
+
TEXT runtime·gettime(SB), 7, $32
MOVL $116, AX
LEAL 12(SP), BX
diff --git a/src/pkg/runtime/freebsd/amd64/defs.h b/src/pkg/runtime/freebsd/amd64/defs.h
index 2a295a479..b101b1932 100644
--- a/src/pkg/runtime/freebsd/amd64/defs.h
+++ b/src/pkg/runtime/freebsd/amd64/defs.h
@@ -61,6 +61,9 @@ enum {
BUS_OBJERR = 0x3,
SEGV_MAPERR = 0x1,
SEGV_ACCERR = 0x2,
+ ITIMER_REAL = 0,
+ ITIMER_VIRTUAL = 0x1,
+ ITIMER_PROF = 0x2,
};
// Types
@@ -83,7 +86,7 @@ struct ThrParam {
int64 *child_tid;
int64 *parent_tid;
int32 flags;
- byte pad0[4];
+ byte pad_godefs_0[4];
Rtprio *rtp;
void* spare[3];
};
@@ -93,7 +96,7 @@ struct Sigaltstack {
int8 *ss_sp;
uint64 ss_size;
int32 ss_flags;
- byte pad0[4];
+ byte pad_godefs_0[4];
};
typedef struct Sigset Sigset;
@@ -114,7 +117,7 @@ struct StackT {
int8 *ss_sp;
uint64 ss_size;
int32 ss_flags;
- byte pad0[4];
+ byte pad_godefs_0[4];
};
typedef struct Siginfo Siginfo;
@@ -178,6 +181,18 @@ struct Ucontext {
StackT uc_stack;
int32 uc_flags;
int32 __spare__[4];
- byte pad0[12];
+ byte pad_godefs_0[12];
+};
+
+typedef struct Timeval Timeval;
+struct Timeval {
+ int64 tv_sec;
+ int64 tv_usec;
+};
+
+typedef struct Itimerval Itimerval;
+struct Itimerval {
+ Timeval it_interval;
+ Timeval it_value;
};
#pragma pack off
diff --git a/src/pkg/runtime/freebsd/amd64/signal.c b/src/pkg/runtime/freebsd/amd64/signal.c
index f145371b4..9d8e5e692 100644
--- a/src/pkg/runtime/freebsd/amd64/signal.c
+++ b/src/pkg/runtime/freebsd/amd64/signal.c
@@ -62,6 +62,11 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp)
uc = context;
r = &uc->uc_mcontext;
+ if(sig == SIGPROF) {
+ runtime·sigprof((uint8*)r->mc_rip, (uint8*)r->mc_rsp, nil, gp);
+ return;
+ }
+
if(gp != nil && (runtime·sigtab[sig].flags & SigPanic)) {
// Make it look like a call to the signal func.
// Have to pass arguments out of band since
@@ -130,32 +135,58 @@ runtime·signalstack(byte *p, int32 n)
runtime·sigaltstack(&st, nil);
}
+static void
+sigaction(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart)
+{
+ Sigaction sa;
+
+ runtime·memclr((byte*)&sa, sizeof sa);
+ sa.sa_flags = SA_SIGINFO|SA_ONSTACK;
+ if(restart)
+ sa.sa_flags |= SA_RESTART;
+ sa.sa_mask = ~0ULL;
+ if (fn == runtime·sighandler)
+ fn = (void*)runtime·sigtramp;
+ sa.__sigaction_u.__sa_sigaction = (void*)fn;
+ runtime·sigaction(i, &sa, nil);
+}
+
void
runtime·initsig(int32 queue)
{
- static Sigaction sa;
+ int32 i;
+ void *fn;
runtime·siginit();
- int32 i;
- sa.sa_flags |= SA_ONSTACK | SA_SIGINFO;
- sa.sa_mask = ~0x0ull;
-
- for(i = 0; i < NSIG; i++) {
+ for(i = 0; i<NSIG; i++) {
if(runtime·sigtab[i].flags) {
if((runtime·sigtab[i].flags & SigQueue) != queue)
continue;
if(runtime·sigtab[i].flags & (SigCatch | SigQueue))
- sa.__sigaction_u.__sa_sigaction = (void*) runtime·sigtramp;
+ fn = runtime·sighandler;
else
- sa.__sigaction_u.__sa_sigaction = (void*) runtime·sigignore;
-
- if(runtime·sigtab[i].flags & SigRestart)
- sa.sa_flags |= SA_RESTART;
- else
- sa.sa_flags &= ~SA_RESTART;
-
- runtime·sigaction(i, &sa, nil);
+ fn = runtime·sigignore;
+ sigaction(i, fn, (runtime·sigtab[i].flags & SigRestart) != 0);
}
}
}
+
+void
+runtime·resetcpuprofiler(int32 hz)
+{
+ Itimerval it;
+
+ runtime·memclr((byte*)&it, sizeof it);
+ if(hz == 0) {
+ runtime·setitimer(ITIMER_PROF, &it, nil);
+ sigaction(SIGPROF, SIG_IGN, true);
+ } else {
+ sigaction(SIGPROF, runtime·sighandler, true);
+ it.it_interval.tv_sec = 0;
+ it.it_interval.tv_usec = 1000000 / hz;
+ it.it_value = it.it_interval;
+ runtime·setitimer(ITIMER_PROF, &it, nil);
+ }
+ m->profilehz = hz;
+}
diff --git a/src/pkg/runtime/freebsd/amd64/sys.s b/src/pkg/runtime/freebsd/amd64/sys.s
index d986e9ac0..9a6fdf1ac 100644
--- a/src/pkg/runtime/freebsd/amd64/sys.s
+++ b/src/pkg/runtime/freebsd/amd64/sys.s
@@ -65,6 +65,14 @@ TEXT runtime·write(SB),7,$-8
SYSCALL
RET
+TEXT runtime·setitimer(SB), 7, $-8
+ MOVL 8(SP), DI
+ MOVQ 16(SP), SI
+ MOVQ 24(SP), DX
+ MOVL $83, AX
+ SYSCALL
+ RET
+
TEXT runtime·gettime(SB), 7, $32
MOVL $116, AX
LEAQ 8(SP), DI
diff --git a/src/pkg/runtime/freebsd/defs.c b/src/pkg/runtime/freebsd/defs.c
index 32a80f475..2ce4fdc51 100644
--- a/src/pkg/runtime/freebsd/defs.c
+++ b/src/pkg/runtime/freebsd/defs.c
@@ -19,6 +19,7 @@
#include <sys/rtprio.h>
#include <sys/thr.h>
#include <sys/_sigset.h>
+#include <sys/unistd.h>
enum {
$PROT_NONE = PROT_NONE,
@@ -86,6 +87,10 @@ enum {
$SEGV_MAPERR = SEGV_MAPERR,
$SEGV_ACCERR = SEGV_ACCERR,
+
+ $ITIMER_REAL = ITIMER_REAL,
+ $ITIMER_VIRTUAL = ITIMER_VIRTUAL,
+ $ITIMER_PROF = ITIMER_PROF,
};
typedef struct rtprio $Rtprio;
@@ -99,3 +104,5 @@ typedef siginfo_t $Siginfo;
typedef mcontext_t $Mcontext;
typedef ucontext_t $Ucontext;
+typedef struct timeval $Timeval;
+typedef struct itimerval $Itimerval;
diff --git a/src/pkg/runtime/freebsd/mem.c b/src/pkg/runtime/freebsd/mem.c
index f5bbfa6fa..f80439e38 100644
--- a/src/pkg/runtime/freebsd/mem.c
+++ b/src/pkg/runtime/freebsd/mem.c
@@ -53,7 +53,7 @@ runtime·SysMap(void *v, uintptr n)
if(sizeof(void*) == 8) {
p = runtime·mmap(v, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, -1, 0);
if(p != v) {
- runtime·printf("runtime: address space conflict: map(%v) = %v\n", v, p);
+ runtime·printf("runtime: address space conflict: map(%p) = %p\n", v, p);
runtime·throw("runtime: address space conflict");
}
return;
diff --git a/src/pkg/runtime/freebsd/os.h b/src/pkg/runtime/freebsd/os.h
index 455355bc7..13754688b 100644
--- a/src/pkg/runtime/freebsd/os.h
+++ b/src/pkg/runtime/freebsd/os.h
@@ -1,5 +1,10 @@
+#define SIG_DFL ((void*)0)
+#define SIG_IGN ((void*)1)
+
int32 runtime·thr_new(ThrParam*, int32);
void runtime·sigpanic(void);
void runtime·sigaltstack(Sigaltstack*, Sigaltstack*);
struct sigaction;
void runtime·sigaction(int32, struct sigaction*, struct sigaction*);
+void runtiem·setitimerval(int32, Itimerval*, Itimerval*);
+void runtime·setitimer(int32, Itimerval*, Itimerval*);
diff --git a/src/pkg/runtime/linux/386/defs.h b/src/pkg/runtime/linux/386/defs.h
index c1f58b2a0..6ae1c4e13 100644
--- a/src/pkg/runtime/linux/386/defs.h
+++ b/src/pkg/runtime/linux/386/defs.h
@@ -58,6 +58,9 @@ enum {
BUS_OBJERR = 0x3,
SEGV_MAPERR = 0x1,
SEGV_ACCERR = 0x2,
+ ITIMER_REAL = 0,
+ ITIMER_VIRTUAL = 0x1,
+ ITIMER_PROF = 0x2,
};
// Types
@@ -98,7 +101,8 @@ struct Fpstate {
uint32 reserved;
Fpxreg _fxsr_st[8];
Xmmreg _xmm[8];
- uint32 padding[56];
+ uint32 padding1[44];
+ byte Pad_godefs_0[48];
};
typedef struct Timespec Timespec;
@@ -176,4 +180,10 @@ struct Ucontext {
Sigcontext uc_mcontext;
uint32 uc_sigmask;
};
+
+typedef struct Itimerval Itimerval;
+struct Itimerval {
+ Timeval it_interval;
+ Timeval it_value;
+};
#pragma pack off
diff --git a/src/pkg/runtime/linux/386/signal.c b/src/pkg/runtime/linux/386/signal.c
index bd918c7ea..9b72ecbae 100644
--- a/src/pkg/runtime/linux/386/signal.c
+++ b/src/pkg/runtime/linux/386/signal.c
@@ -51,6 +51,11 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp)
uc = context;
r = &uc->uc_mcontext;
+ if(sig == SIGPROF) {
+ runtime·sigprof((uint8*)r->eip, (uint8*)r->esp, nil, gp);
+ return;
+ }
+
if(gp != nil && (runtime·sigtab[sig].flags & SigPanic)) {
// Make it look like a call to the signal func.
// Have to pass arguments out of band since
@@ -114,30 +119,59 @@ runtime·signalstack(byte *p, int32 n)
runtime·sigaltstack(&st, nil);
}
+static void
+sigaction(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart)
+{
+ Sigaction sa;
+
+ runtime·memclr((byte*)&sa, sizeof sa);
+ sa.sa_flags = SA_ONSTACK | SA_SIGINFO | SA_RESTORER;
+ if(restart)
+ sa.sa_flags |= SA_RESTART;
+ sa.sa_mask = ~0ULL;
+ sa.sa_restorer = (void*)runtime·sigreturn;
+ if(fn == runtime·sighandler)
+ fn = (void*)runtime·sigtramp;
+ sa.k_sa_handler = fn;
+ runtime·rt_sigaction(i, &sa, nil, 8);
+}
+
void
runtime·initsig(int32 queue)
{
- static Sigaction sa;
+ int32 i;
+ void *fn;
runtime·siginit();
- int32 i;
- sa.sa_flags = SA_ONSTACK | SA_SIGINFO | SA_RESTORER;
- sa.sa_mask = 0xFFFFFFFFFFFFFFFFULL;
- sa.sa_restorer = (void*)runtime·sigreturn;
for(i = 0; i<NSIG; i++) {
if(runtime·sigtab[i].flags) {
if((runtime·sigtab[i].flags & SigQueue) != queue)
continue;
if(runtime·sigtab[i].flags & (SigCatch | SigQueue))
- sa.k_sa_handler = (void*)runtime·sigtramp;
+ fn = runtime·sighandler;
else
- sa.k_sa_handler = (void*)runtime·sigignore;
- if(runtime·sigtab[i].flags & SigRestart)
- sa.sa_flags |= SA_RESTART;
- else
- sa.sa_flags &= ~SA_RESTART;
- runtime·rt_sigaction(i, &sa, nil, 8);
+ fn = runtime·sigignore;
+ sigaction(i, fn, (runtime·sigtab[i].flags & SigRestart) != 0);
}
}
}
+
+void
+runtime·resetcpuprofiler(int32 hz)
+{
+ Itimerval it;
+
+ runtime·memclr((byte*)&it, sizeof it);
+ if(hz == 0) {
+ runtime·setitimer(ITIMER_PROF, &it, nil);
+ sigaction(SIGPROF, SIG_IGN, true);
+ } else {
+ sigaction(SIGPROF, runtime·sighandler, true);
+ it.it_interval.tv_sec = 0;
+ it.it_interval.tv_usec = 1000000 / hz;
+ it.it_value = it.it_interval;
+ runtime·setitimer(ITIMER_PROF, &it, nil);
+ }
+ m->profilehz = hz;
+}
diff --git a/src/pkg/runtime/linux/386/sys.s b/src/pkg/runtime/linux/386/sys.s
index a684371be..c39ce253f 100644
--- a/src/pkg/runtime/linux/386/sys.s
+++ b/src/pkg/runtime/linux/386/sys.s
@@ -30,6 +30,15 @@ TEXT runtime·write(SB),7,$0
INT $0x80
RET
+
+TEXT runtime·setitimer(SB),7,$0-24
+ MOVL $104, AX // syscall - setitimer
+ MOVL 4(SP), BX
+ MOVL 8(SP), CX
+ MOVL 12(SP), DX
+ INT $0x80
+ RET
+
TEXT runtime·gettime(SB), 7, $32
MOVL $78, AX // syscall - gettimeofday
LEAL 8(SP), BX
diff --git a/src/pkg/runtime/linux/amd64/defs.h b/src/pkg/runtime/linux/amd64/defs.h
index 3e3d32f0d..70d63145c 100644
--- a/src/pkg/runtime/linux/amd64/defs.h
+++ b/src/pkg/runtime/linux/amd64/defs.h
@@ -58,6 +58,9 @@ enum {
BUS_OBJERR = 0x3,
SEGV_MAPERR = 0x1,
SEGV_ACCERR = 0x2,
+ ITIMER_REAL = 0,
+ ITIMER_VIRTUAL = 0x1,
+ ITIMER_PROF = 0x2,
};
// Types
@@ -88,9 +91,15 @@ struct Siginfo {
int32 si_signo;
int32 si_errno;
int32 si_code;
- byte pad0[4];
+ byte pad_godefs_0[4];
byte _sifields[112];
};
+
+typedef struct Itimerval Itimerval;
+struct Itimerval {
+ Timeval it_interval;
+ Timeval it_value;
+};
#pragma pack off
// godefs -f -m64 defs1.c
@@ -170,7 +179,7 @@ typedef struct Sigaltstack Sigaltstack;
struct Sigaltstack {
void *ss_sp;
int32 ss_flags;
- byte pad0[4];
+ byte pad_godefs_0[4];
uint64 ss_size;
};
diff --git a/src/pkg/runtime/linux/amd64/signal.c b/src/pkg/runtime/linux/amd64/signal.c
index ea0932523..1db9c95e5 100644
--- a/src/pkg/runtime/linux/amd64/signal.c
+++ b/src/pkg/runtime/linux/amd64/signal.c
@@ -61,6 +61,11 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp)
mc = &uc->uc_mcontext;
r = (Sigcontext*)mc; // same layout, more conveient names
+ if(sig == SIGPROF) {
+ runtime·sigprof((uint8*)r->rip, (uint8*)r->rsp, nil, gp);
+ return;
+ }
+
if(gp != nil && (runtime·sigtab[sig].flags & SigPanic)) {
// Make it look like a call to the signal func.
// Have to pass arguments out of band since
@@ -124,30 +129,59 @@ runtime·signalstack(byte *p, int32 n)
runtime·sigaltstack(&st, nil);
}
+static void
+sigaction(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart)
+{
+ Sigaction sa;
+
+ runtime·memclr((byte*)&sa, sizeof sa);
+ sa.sa_flags = SA_ONSTACK | SA_SIGINFO | SA_RESTORER;
+ if(restart)
+ sa.sa_flags |= SA_RESTART;
+ sa.sa_mask = ~0ULL;
+ sa.sa_restorer = (void*)runtime·sigreturn;
+ if(fn == runtime·sighandler)
+ fn = (void*)runtime·sigtramp;
+ sa.sa_handler = fn;
+ runtime·rt_sigaction(i, &sa, nil, 8);
+}
+
void
runtime·initsig(int32 queue)
{
- static Sigaction sa;
+ int32 i;
+ void *fn;
runtime·siginit();
- int32 i;
- sa.sa_flags = SA_ONSTACK | SA_SIGINFO | SA_RESTORER;
- sa.sa_mask = 0xFFFFFFFFFFFFFFFFULL;
- sa.sa_restorer = (void*)runtime·sigreturn;
for(i = 0; i<NSIG; i++) {
if(runtime·sigtab[i].flags) {
if((runtime·sigtab[i].flags & SigQueue) != queue)
continue;
if(runtime·sigtab[i].flags & (SigCatch | SigQueue))
- sa.sa_handler = (void*)runtime·sigtramp;
+ fn = runtime·sighandler;
else
- sa.sa_handler = (void*)runtime·sigignore;
- if(runtime·sigtab[i].flags & SigRestart)
- sa.sa_flags |= SA_RESTART;
- else
- sa.sa_flags &= ~SA_RESTART;
- runtime·rt_sigaction(i, &sa, nil, 8);
+ fn = runtime·sigignore;
+ sigaction(i, fn, (runtime·sigtab[i].flags & SigRestart) != 0);
}
}
}
+
+void
+runtime·resetcpuprofiler(int32 hz)
+{
+ Itimerval it;
+
+ runtime·memclr((byte*)&it, sizeof it);
+ if(hz == 0) {
+ runtime·setitimer(ITIMER_PROF, &it, nil);
+ sigaction(SIGPROF, SIG_IGN, true);
+ } else {
+ sigaction(SIGPROF, runtime·sighandler, true);
+ it.it_interval.tv_sec = 0;
+ it.it_interval.tv_usec = 1000000 / hz;
+ it.it_value = it.it_interval;
+ runtime·setitimer(ITIMER_PROF, &it, nil);
+ }
+ m->profilehz = hz;
+}
diff --git a/src/pkg/runtime/linux/amd64/sys.s b/src/pkg/runtime/linux/amd64/sys.s
index 1bf734dc0..11df1f894 100644
--- a/src/pkg/runtime/linux/amd64/sys.s
+++ b/src/pkg/runtime/linux/amd64/sys.s
@@ -36,6 +36,14 @@ TEXT runtime·write(SB),7,$0-24
SYSCALL
RET
+TEXT runtime·setitimer(SB),7,$0-24
+ MOVL 8(SP), DI
+ MOVQ 16(SP), SI
+ MOVQ 24(SP), DX
+ MOVL $38, AX // syscall entry
+ SYSCALL
+ RET
+
TEXT runtime·gettime(SB), 7, $32
LEAQ 8(SP), DI
MOVQ $0, SI
diff --git a/src/pkg/runtime/linux/arm/defs.h b/src/pkg/runtime/linux/arm/defs.h
index ff43d689a..6b2f22c66 100644
--- a/src/pkg/runtime/linux/arm/defs.h
+++ b/src/pkg/runtime/linux/arm/defs.h
@@ -1,4 +1,4 @@
-// godefs -carm-gcc -f -I/usr/local/google/src/linux-2.6.28/arch/arm/include -f -I/usr/local/google/src/linux-2.6.28/include -f-D__KERNEL__ -f-D__ARCH_SI_UID_T=int defs_arm.c
+// godefs -f-I/usr/src/linux-headers-2.6.26-2-versatile/include defs_arm.c
// MACHINE GENERATED - DO NOT EDIT.
@@ -58,23 +58,15 @@ enum {
BUS_OBJERR = 0x3,
SEGV_MAPERR = 0x1,
SEGV_ACCERR = 0x2,
+ ITIMER_REAL = 0,
+ ITIMER_PROF = 0x2,
+ ITIMER_VIRTUAL = 0x1,
};
// Types
#pragma pack on
-typedef struct Sigset Sigset;
-struct Sigset {
- uint32 sig[2];
-};
-
-typedef struct Sigaction Sigaction;
-struct Sigaction {
- void *sa_handler;
- uint32 sa_flags;
- void *sa_restorer;
- Sigset sa_mask;
-};
+typedef uint32 Sigset;
typedef struct Timespec Timespec;
struct Timespec {
@@ -120,11 +112,23 @@ struct Ucontext {
Ucontext *uc_link;
Sigaltstack uc_stack;
Sigcontext uc_mcontext;
- Sigset uc_sigmask;
- int32 __unused[30];
+ uint32 uc_sigmask;
+ int32 __unused[31];
uint32 uc_regspace[128];
};
+typedef struct Timeval Timeval;
+struct Timeval {
+ int32 tv_sec;
+ int32 tv_usec;
+};
+
+typedef struct Itimerval Itimerval;
+struct Itimerval {
+ Timeval it_interval;
+ Timeval it_value;
+};
+
typedef struct Siginfo Siginfo;
struct Siginfo {
int32 si_signo;
@@ -132,4 +136,12 @@ struct Siginfo {
int32 si_code;
uint8 _sifields[4];
};
+
+typedef struct Sigaction Sigaction;
+struct Sigaction {
+ void *sa_handler;
+ uint32 sa_flags;
+ void *sa_restorer;
+ uint32 sa_mask;
+};
#pragma pack off
diff --git a/src/pkg/runtime/linux/arm/signal.c b/src/pkg/runtime/linux/arm/signal.c
index 843c40b68..05c6b0261 100644
--- a/src/pkg/runtime/linux/arm/signal.c
+++ b/src/pkg/runtime/linux/arm/signal.c
@@ -58,6 +58,11 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp)
uc = context;
r = &uc->uc_mcontext;
+ if(sig == SIGPROF) {
+ runtime·sigprof((uint8*)r->arm_pc, (uint8*)r->arm_sp, (uint8*)r->arm_lr, gp);
+ return;
+ }
+
if(gp != nil && (runtime·sigtab[sig].flags & SigPanic)) {
// Make it look like a call to the signal func.
// Have to pass arguments out of band since
@@ -119,31 +124,59 @@ runtime·signalstack(byte *p, int32 n)
runtime·sigaltstack(&st, nil);
}
+static void
+sigaction(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart)
+{
+ Sigaction sa;
+
+ runtime·memclr((byte*)&sa, sizeof sa);
+ sa.sa_flags = SA_ONSTACK | SA_SIGINFO | SA_RESTORER;
+ if(restart)
+ sa.sa_flags |= SA_RESTART;
+ sa.sa_mask = ~0ULL;
+ sa.sa_restorer = (void*)runtime·sigreturn;
+ if(fn == runtime·sighandler)
+ fn = (void*)runtime·sigtramp;
+ sa.sa_handler = fn;
+ runtime·rt_sigaction(i, &sa, nil, 8);
+}
+
void
runtime·initsig(int32 queue)
{
- static Sigaction sa;
+ int32 i;
+ void *fn;
runtime·siginit();
- int32 i;
- sa.sa_flags = SA_ONSTACK | SA_SIGINFO | SA_RESTORER;
- sa.sa_mask.sig[0] = 0xFFFFFFFF;
- sa.sa_mask.sig[1] = 0xFFFFFFFF;
- sa.sa_restorer = (void*)runtime·sigreturn;
for(i = 0; i<NSIG; i++) {
if(runtime·sigtab[i].flags) {
if((runtime·sigtab[i].flags & SigQueue) != queue)
continue;
if(runtime·sigtab[i].flags & (SigCatch | SigQueue))
- sa.sa_handler = (void*)runtime·sigtramp;
+ fn = runtime·sighandler;
else
- sa.sa_handler = (void*)runtime·sigignore;
- if(runtime·sigtab[i].flags & SigRestart)
- sa.sa_flags |= SA_RESTART;
- else
- sa.sa_flags &= ~SA_RESTART;
- runtime·rt_sigaction(i, &sa, nil, 8);
+ fn = runtime·sigignore;
+ sigaction(i, fn, (runtime·sigtab[i].flags & SigRestart) != 0);
}
}
}
+
+void
+runtime·resetcpuprofiler(int32 hz)
+{
+ Itimerval it;
+
+ runtime·memclr((byte*)&it, sizeof it);
+ if(hz == 0) {
+ runtime·setitimer(ITIMER_PROF, &it, nil);
+ sigaction(SIGPROF, SIG_IGN, true);
+ } else {
+ sigaction(SIGPROF, runtime·sighandler, true);
+ it.it_interval.tv_sec = 0;
+ it.it_interval.tv_usec = 1000000 / hz;
+ it.it_value = it.it_interval;
+ runtime·setitimer(ITIMER_PROF, &it, nil);
+ }
+ m->profilehz = hz;
+}
diff --git a/src/pkg/runtime/linux/arm/sys.s b/src/pkg/runtime/linux/arm/sys.s
index 9daf9c2e4..b9767a028 100644
--- a/src/pkg/runtime/linux/arm/sys.s
+++ b/src/pkg/runtime/linux/arm/sys.s
@@ -26,6 +26,7 @@
#define SYS_futex (SYS_BASE + 240)
#define SYS_exit_group (SYS_BASE + 248)
#define SYS_munmap (SYS_BASE + 91)
+#define SYS_setitimer (SYS_BASE + 104)
#define ARM_BASE (SYS_BASE + 0x0f0000)
#define SYS_ARM_cacheflush (ARM_BASE + 2)
@@ -72,6 +73,14 @@ TEXT runtime·munmap(SB),7,$0
SWI $0
RET
+TEXT runtime·setitimer(SB),7,$0
+ MOVW 0(FP), R0
+ MOVW 4(FP), R1
+ MOVW 8(FP), R2
+ MOVW $SYS_setitimer, R7
+ SWI $0
+ RET
+
TEXT runtime·gettime(SB),7,$32
/* dummy version - return 0,0 */
MOVW $0, R1
diff --git a/src/pkg/runtime/linux/defs.c b/src/pkg/runtime/linux/defs.c
index 2044fd60c..5dda78789 100644
--- a/src/pkg/runtime/linux/defs.c
+++ b/src/pkg/runtime/linux/defs.c
@@ -15,6 +15,8 @@
// headers for things like ucontext_t, so that happens in
// a separate file, defs1.c.
+#include <asm/posix_types.h>
+#define size_t __kernel_size_t
#include <asm/signal.h>
#include <asm/siginfo.h>
#include <asm/mman.h>
@@ -80,9 +82,14 @@ enum {
$SEGV_MAPERR = SEGV_MAPERR,
$SEGV_ACCERR = SEGV_ACCERR,
+
+ $ITIMER_REAL = ITIMER_REAL,
+ $ITIMER_VIRTUAL = ITIMER_VIRTUAL,
+ $ITIMER_PROF = ITIMER_PROF,
};
typedef struct timespec $Timespec;
typedef struct timeval $Timeval;
typedef struct sigaction $Sigaction;
typedef siginfo_t $Siginfo;
+typedef struct itimerval $Itimerval;
diff --git a/src/pkg/runtime/linux/defs2.c b/src/pkg/runtime/linux/defs2.c
index 3c0b110fc..ff641fff2 100644
--- a/src/pkg/runtime/linux/defs2.c
+++ b/src/pkg/runtime/linux/defs2.c
@@ -8,7 +8,7 @@
-f -I/home/rsc/pub/linux-2.6/arch/x86/include \
-f -I/home/rsc/pub/linux-2.6/include \
-f -D_LOOSE_KERNEL_NAMES \
- -f -D__ARCH_SI_UID_T=__kernel_uid32_t \
+ -f -D__ARCH_SI_UID_T'='__kernel_uid32_t \
defs2.c >386/defs.h
* The asm header tricks we have to use for Linux on amd64
@@ -100,6 +100,10 @@ enum {
$SEGV_MAPERR = SEGV_MAPERR,
$SEGV_ACCERR = SEGV_ACCERR,
+
+ $ITIMER_REAL = ITIMER_REAL,
+ $ITIMER_VIRTUAL = ITIMER_VIRTUAL,
+ $ITIMER_PROF = ITIMER_PROF,
};
typedef struct _fpreg $Fpreg;
@@ -113,4 +117,4 @@ typedef siginfo_t $Siginfo;
typedef struct sigaltstack $Sigaltstack;
typedef struct sigcontext $Sigcontext;
typedef struct ucontext $Ucontext;
-
+typedef struct itimerval $Itimerval;
diff --git a/src/pkg/runtime/linux/defs_arm.c b/src/pkg/runtime/linux/defs_arm.c
index a5897d6d0..1f935046e 100644
--- a/src/pkg/runtime/linux/defs_arm.c
+++ b/src/pkg/runtime/linux/defs_arm.c
@@ -4,16 +4,18 @@
/*
* Input to godefs
- godefs -carm-gcc -f -I/usr/local/google/src/linux-2.6.28/arch/arm/include -f -I/usr/local/google/src/linux-2.6.28/include -f-D__KERNEL__ -f-D__ARCH_SI_UID_T=int defs_arm.c >arm/defs.h
-
- * Another input file for ARM defs.h
+ * On a Debian Lenny arm linux distribution:
+ godefs -f-I/usr/src/linux-headers-2.6.26-2-versatile/include defs_arm.c
*/
+#define __ARCH_SI_UID_T int
+
#include <asm/signal.h>
#include <asm/mman.h>
#include <asm/sigcontext.h>
#include <asm/ucontext.h>
#include <asm/siginfo.h>
+#include <linux/time.h>
/*
#include <sys/signal.h>
@@ -21,8 +23,6 @@
#include <ucontext.h>
*/
-#include <time.h>
-
enum {
$PROT_NONE = PROT_NONE,
$PROT_READ = PROT_READ,
@@ -84,14 +84,19 @@ enum {
$SEGV_MAPERR = SEGV_MAPERR & 0xFFFF,
$SEGV_ACCERR = SEGV_ACCERR & 0xFFFF,
+
+ $ITIMER_REAL = ITIMER_REAL,
+ $ITIMER_PROF = ITIMER_PROF,
+ $ITIMER_VIRTUAL = ITIMER_VIRTUAL,
};
typedef sigset_t $Sigset;
-typedef struct sigaction $Sigaction;
typedef struct timespec $Timespec;
typedef struct sigaltstack $Sigaltstack;
typedef struct sigcontext $Sigcontext;
typedef struct ucontext $Ucontext;
+typedef struct timeval $Timeval;
+typedef struct itimerval $Itimerval;
struct xsiginfo {
int si_signo;
@@ -101,3 +106,17 @@ struct xsiginfo {
};
typedef struct xsiginfo $Siginfo;
+
+#undef sa_handler
+#undef sa_flags
+#undef sa_restorer
+#undef sa_mask
+
+struct xsigaction {
+ void (*sa_handler)(void);
+ unsigned long sa_flags;
+ void (*sa_restorer)(void);
+ unsigned int sa_mask; /* mask last for extensibility */
+};
+
+typedef struct xsigaction $Sigaction;
diff --git a/src/pkg/runtime/linux/mem.c b/src/pkg/runtime/linux/mem.c
index 633ad0c62..d2f6f8204 100644
--- a/src/pkg/runtime/linux/mem.c
+++ b/src/pkg/runtime/linux/mem.c
@@ -59,7 +59,7 @@ runtime·SysMap(void *v, uintptr n)
if(sizeof(void*) == 8) {
p = runtime·mmap(v, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, -1, 0);
if(p != v) {
- runtime·printf("runtime: address space conflict: map(%v) = %v\n", v, p);
+ runtime·printf("runtime: address space conflict: map(%p) = %p\n", v, p);
runtime·throw("runtime: address space conflict");
}
return;
diff --git a/src/pkg/runtime/linux/os.h b/src/pkg/runtime/linux/os.h
index 772ade7da..6ae088977 100644
--- a/src/pkg/runtime/linux/os.h
+++ b/src/pkg/runtime/linux/os.h
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#define SIG_DFL ((void*)0)
+#define SIG_IGN ((void*)1)
+
// Linux-specific system calls
int32 runtime·futex(uint32*, int32, uint32, Timespec*, uint32*, uint32);
int32 runtime·clone(int32, void*, M*, G*, void(*)(void));
@@ -11,3 +14,4 @@ void runtime·rt_sigaction(uintptr, struct Sigaction*, void*, uintptr);
void runtime·sigaltstack(Sigaltstack*, Sigaltstack*);
void runtime·sigpanic(void);
+void runtime·setitimer(int32, Itimerval*, Itimerval*);
diff --git a/src/pkg/runtime/linux/signals.h b/src/pkg/runtime/linux/signals.h
index 1fc5f8c87..919b80ea2 100644
--- a/src/pkg/runtime/linux/signals.h
+++ b/src/pkg/runtime/linux/signals.h
@@ -13,7 +13,7 @@ SigTab runtime·sigtab[] = {
/* 1 */ Q+R, "SIGHUP: terminal line hangup",
/* 2 */ Q+R, "SIGINT: interrupt",
/* 3 */ C, "SIGQUIT: quit",
- /* 4 */ C, "SIGILL: illegal instruction",
+ /* 4 */ C+P, "SIGILL: illegal instruction",
/* 5 */ C, "SIGTRAP: trace trap",
/* 6 */ C, "SIGABRT: abort",
/* 7 */ C+P, "SIGBUS: bus error",
diff --git a/src/pkg/runtime/mem.go b/src/pkg/runtime/mem.go
new file mode 100644
index 000000000..fe505a329
--- /dev/null
+++ b/src/pkg/runtime/mem.go
@@ -0,0 +1,69 @@
+// 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 runtime
+
+import "unsafe"
+
+type MemStatsType struct {
+ // General statistics.
+ // Not locked during update; approximate.
+ Alloc uint64 // bytes allocated and still in use
+ TotalAlloc uint64 // bytes allocated (even if freed)
+ Sys uint64 // bytes obtained from system (should be sum of XxxSys below)
+ Lookups uint64 // number of pointer lookups
+ Mallocs uint64 // number of mallocs
+ Frees uint64 // number of frees
+
+ // Main allocation heap statistics.
+ HeapAlloc uint64 // bytes allocated and still in use
+ HeapSys uint64 // bytes obtained from system
+ HeapIdle uint64 // bytes in idle spans
+ HeapInuse uint64 // bytes in non-idle span
+ HeapObjects uint64 // total number of allocated objects
+
+ // Low-level fixed-size structure allocator statistics.
+ // Inuse is bytes used now.
+ // Sys is bytes obtained from system.
+ StackInuse uint64 // bootstrap stacks
+ StackSys uint64
+ MSpanInuse uint64 // mspan structures
+ MSpanSys uint64
+ MCacheInuse uint64 // mcache structures
+ MCacheSys uint64
+ BuckHashSys uint64 // profiling bucket hash table
+
+ // Garbage collector statistics.
+ NextGC uint64
+ PauseTotalNs uint64
+ PauseNs [256]uint64 // most recent GC pause times
+ NumGC uint32
+ EnableGC bool
+ DebugGC bool
+
+ // Per-size allocation statistics.
+ // Not locked during update; approximate.
+ // 61 is NumSizeClasses in the C code.
+ BySize [61]struct {
+ Size uint32
+ Mallocs uint64
+ Frees uint64
+ }
+}
+
+var sizeof_C_MStats int // filled in by malloc.goc
+
+func init() {
+ if sizeof_C_MStats != unsafe.Sizeof(MemStats) {
+ println(sizeof_C_MStats, unsafe.Sizeof(MemStats))
+ panic("MStats vs MemStatsType size mismatch")
+ }
+}
+
+// MemStats holds statistics about the memory system.
+// The statistics are only approximate, as they are not interlocked on update.
+var MemStats MemStatsType
+
+// GC runs a garbage collection.
+func GC()
diff --git a/src/pkg/runtime/mgc0.c b/src/pkg/runtime/mgc0.c
index 7c175b308..14d485b71 100644
--- a/src/pkg/runtime/mgc0.c
+++ b/src/pkg/runtime/mgc0.c
@@ -53,7 +53,6 @@ enum {
static uint64 nlookup;
static uint64 nsizelookup;
static uint64 naddrlookup;
-static uint64 nhandoff;
static int32 gctrace;
typedef struct Workbuf Workbuf;
@@ -71,10 +70,8 @@ extern byte end[];
static G *fing;
static Finalizer *finq;
static int32 fingwait;
-static uint32 nfullwait;
static void runfinq(void);
-static bool bitlookup(void*, uintptr**, uintptr*, int32*);
static Workbuf* getempty(Workbuf*);
static Workbuf* getfull(Workbuf*);
@@ -379,8 +376,6 @@ mark(void)
case Gdead:
break;
case Grunning:
- case Grecovery:
- case Gstackalloc:
if(gp != g)
runtime·throw("mark - world not stopped");
scanstack(gp);
diff --git a/src/pkg/runtime/plan9/386/signal.c b/src/pkg/runtime/plan9/386/signal.c
index 6bde09846..364fd1c41 100644
--- a/src/pkg/runtime/plan9/386/signal.c
+++ b/src/pkg/runtime/plan9/386/signal.c
@@ -14,3 +14,11 @@ runtime·signame(int32)
{
return runtime·emptystring;
}
+
+void
+runtime·resetcpuprofiler(int32 hz)
+{
+ // TODO: Enable profiling interrupts.
+
+ m->profilehz = hz;
+}
diff --git a/src/pkg/runtime/pprof/pprof.go b/src/pkg/runtime/pprof/pprof.go
index 9bee51128..fdeceb4e8 100644
--- a/src/pkg/runtime/pprof/pprof.go
+++ b/src/pkg/runtime/pprof/pprof.go
@@ -14,6 +14,7 @@ import (
"io"
"os"
"runtime"
+ "sync"
)
// WriteHeapProfile writes a pprof-formatted heap profile to w.
@@ -105,3 +106,71 @@ func WriteHeapProfile(w io.Writer) os.Error {
}
return b.Flush()
}
+
+var cpu struct {
+ sync.Mutex
+ profiling bool
+ done chan bool
+}
+
+// StartCPUProfile enables CPU profiling for the current process.
+// While profiling, the profile will be buffered and written to w.
+// StartCPUProfile returns an error if profiling is already enabled.
+func StartCPUProfile(w io.Writer) os.Error {
+ // The runtime routines allow a variable profiling rate,
+ // but in practice operating systems cannot trigger signals
+ // at more than about 500 Hz, and our processing of the
+ // signal is not cheap (mostly getting the stack trace).
+ // 100 Hz is a reasonable choice: it is frequent enough to
+ // produce useful data, rare enough not to bog down the
+ // system, and a nice round number to make it easy to
+ // convert sample counts to seconds. Instead of requiring
+ // each client to specify the frequency, we hard code it.
+ const hz = 100
+
+ // Avoid queueing behind StopCPUProfile.
+ // Could use TryLock instead if we had it.
+ if cpu.profiling {
+ return fmt.Errorf("cpu profiling already in use")
+ }
+
+ cpu.Lock()
+ defer cpu.Unlock()
+ if cpu.done == nil {
+ cpu.done = make(chan bool)
+ }
+ // Double-check.
+ if cpu.profiling {
+ return fmt.Errorf("cpu profiling already in use")
+ }
+ cpu.profiling = true
+ runtime.SetCPUProfileRate(hz)
+ go profileWriter(w)
+ return nil
+}
+
+func profileWriter(w io.Writer) {
+ for {
+ data := runtime.CPUProfile()
+ if data == nil {
+ break
+ }
+ w.Write(data)
+ }
+ cpu.done <- true
+}
+
+// StopCPUProfile stops the current CPU profile, if any.
+// StopCPUProfile only returns after all the writes for the
+// profile have completed.
+func StopCPUProfile() {
+ cpu.Lock()
+ defer cpu.Unlock()
+
+ if !cpu.profiling {
+ return
+ }
+ cpu.profiling = false
+ runtime.SetCPUProfileRate(0)
+ <-cpu.done
+}
diff --git a/src/pkg/runtime/pprof/pprof_test.go b/src/pkg/runtime/pprof/pprof_test.go
new file mode 100644
index 000000000..a060917a2
--- /dev/null
+++ b/src/pkg/runtime/pprof/pprof_test.go
@@ -0,0 +1,77 @@
+// 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 pprof_test
+
+import (
+ "bytes"
+ "hash/crc32"
+ "runtime"
+ . "runtime/pprof"
+ "strings"
+ "testing"
+ "unsafe"
+)
+
+func TestCPUProfile(t *testing.T) {
+ switch runtime.GOOS {
+ case "darwin":
+ // see Apple Bug Report #9177434 (copied into change description)
+ return
+ case "plan9":
+ // unimplemented
+ return
+ case "windows":
+ // unimplemented
+ return
+ }
+
+ buf := make([]byte, 100000)
+ var prof bytes.Buffer
+ if err := StartCPUProfile(&prof); err != nil {
+ t.Fatal(err)
+ }
+ // This loop takes about a quarter second on a 2 GHz laptop.
+ // We only need to get one 100 Hz clock tick, so we've got
+ // a 25x safety buffer.
+ for i := 0; i < 1000; i++ {
+ crc32.ChecksumIEEE(buf)
+ }
+ StopCPUProfile()
+
+ // Convert []byte to []uintptr.
+ bytes := prof.Bytes()
+ val := *(*[]uintptr)(unsafe.Pointer(&bytes))
+ val = val[:len(bytes)/unsafe.Sizeof(uintptr(0))]
+
+ if len(val) < 10 {
+ t.Fatalf("profile too short: %#x", val)
+ }
+ if val[0] != 0 || val[1] != 3 || val[2] != 0 || val[3] != 1e6/100 || val[4] != 0 {
+ t.Fatalf("unexpected header %#x", val[:5])
+ }
+
+ // Check that profile is well formed and contains ChecksumIEEE.
+ found := false
+ val = val[5:]
+ for len(val) > 0 {
+ if len(val) < 2 || val[0] < 1 || val[1] < 1 || uintptr(len(val)) < 2+val[1] {
+ t.Fatalf("malformed profile. leftover: %#x", val)
+ }
+ for _, pc := range val[2 : 2+val[1]] {
+ f := runtime.FuncForPC(pc)
+ if f == nil {
+ continue
+ }
+ if strings.Contains(f.Name(), "ChecksumIEEE") {
+ found = true
+ }
+ }
+ val = val[2+val[1]:]
+ }
+
+ if !found {
+ t.Fatal("did not find ChecksumIEEE in the profile")
+ }
+}
diff --git a/src/pkg/runtime/proc.c b/src/pkg/runtime/proc.c
index db6072b5c..e212c7820 100644
--- a/src/pkg/runtime/proc.c
+++ b/src/pkg/runtime/proc.c
@@ -12,6 +12,9 @@
bool runtime·iscgo;
static void unwindstack(G*, byte*);
+static void schedule(G*);
+static void acquireproc(void);
+static void releaseproc(void);
typedef struct Sched Sched;
@@ -67,6 +70,7 @@ struct Sched {
int32 msyscall; // number of ms in system calls
int32 predawn; // running initialization, don't run new gs.
+ int32 profilehz; // cpu profiling rate
Note stopped; // one g can wait here for ms to stop
int32 waitstop; // after setting this flag
@@ -75,6 +79,13 @@ struct Sched {
Sched runtime·sched;
int32 gomaxprocs;
+// An m that is waiting for notewakeup(&m->havenextg). This may be
+// only be accessed while the scheduler lock is held. This is used to
+// minimize the number of times we call notewakeup while the scheduler
+// lock is held, since the m will normally move quickly to lock the
+// scheduler itself, producing lock contention.
+static M* mwakeup;
+
// Scheduling helpers. Sched must be locked.
static void gput(G*); // put/get on ghead/gtail
static G* gget(void);
@@ -86,9 +97,6 @@ static void matchmg(void); // match ms to gs
static void readylocked(G*); // ready, but sched is locked
static void mnextg(M*, G*);
-// Scheduler loop.
-static void scheduler(void);
-
// The bootstrap sequence is:
//
// call osinit
@@ -130,6 +138,26 @@ runtime·schedinit(void)
m->nomemprof--;
}
+// Lock the scheduler.
+static void
+schedlock(void)
+{
+ runtime·lock(&runtime·sched);
+}
+
+// Unlock the scheduler.
+static void
+schedunlock(void)
+{
+ M *m;
+
+ m = mwakeup;
+ mwakeup = nil;
+ runtime·unlock(&runtime·sched);
+ if(m != nil)
+ runtime·notewakeup(&m->havenextg);
+}
+
// Called after main·init_function; main·main will be called on return.
void
runtime·initdone(void)
@@ -141,9 +169,9 @@ runtime·initdone(void)
// If main·init_function started other goroutines,
// kick off new ms to handle them, like ready
// would have, had it not been pre-dawn.
- runtime·lock(&runtime·sched);
+ schedlock();
matchmg();
- runtime·unlock(&runtime·sched);
+ schedunlock();
}
void
@@ -261,9 +289,9 @@ mget(G *g)
void
runtime·ready(G *g)
{
- runtime·lock(&runtime·sched);
+ schedlock();
readylocked(g);
- runtime·unlock(&runtime·sched);
+ schedunlock();
}
// Mark g ready to run. Sched is already locked.
@@ -280,7 +308,7 @@ readylocked(G *g)
}
// Mark runnable.
- if(g->status == Grunnable || g->status == Grunning || g->status == Grecovery || g->status == Gstackalloc) {
+ if(g->status == Grunnable || g->status == Grunning) {
runtime·printf("goroutine %d has status %d\n", g->goid, g->status);
runtime·throw("bad g->status in ready");
}
@@ -314,7 +342,9 @@ mnextg(M *m, G *g)
m->nextg = g;
if(m->waitnextg) {
m->waitnextg = 0;
- runtime·notewakeup(&m->havenextg);
+ if(mwakeup != nil)
+ runtime·notewakeup(&mwakeup->havenextg);
+ mwakeup = m;
}
}
@@ -335,7 +365,7 @@ nextgandunlock(void)
if(m->nextg != nil) {
gp = m->nextg;
m->nextg = nil;
- runtime·unlock(&runtime·sched);
+ schedunlock();
return gp;
}
@@ -353,7 +383,7 @@ nextgandunlock(void)
continue;
}
runtime·sched.mcpu++; // this m will run gp
- runtime·unlock(&runtime·sched);
+ schedunlock();
return gp;
}
// Otherwise, wait on global m queue.
@@ -368,7 +398,7 @@ nextgandunlock(void)
runtime·sched.waitstop = 0;
runtime·notewakeup(&runtime·sched.stopped);
}
- runtime·unlock(&runtime·sched);
+ schedunlock();
runtime·notesleep(&m->havenextg);
if((gp = m->nextg) == nil)
@@ -382,7 +412,7 @@ nextgandunlock(void)
void
runtime·stoptheworld(void)
{
- runtime·lock(&runtime·sched);
+ schedlock();
runtime·gcwaiting = 1;
runtime·sched.mcpumax = 1;
while(runtime·sched.mcpu > 1) {
@@ -392,11 +422,11 @@ runtime·stoptheworld(void)
// so this is okay.
runtime·noteclear(&runtime·sched.stopped);
runtime·sched.waitstop = 1;
- runtime·unlock(&runtime·sched);
+ schedunlock();
runtime·notesleep(&runtime·sched.stopped);
- runtime·lock(&runtime·sched);
+ schedlock();
}
- runtime·unlock(&runtime·sched);
+ schedunlock();
}
// TODO(rsc): Remove. This is only temporary,
@@ -404,11 +434,11 @@ runtime·stoptheworld(void)
void
runtime·starttheworld(void)
{
- runtime·lock(&runtime·sched);
+ schedlock();
runtime·gcwaiting = 0;
runtime·sched.mcpumax = runtime·gomaxprocs;
matchmg();
- runtime·unlock(&runtime·sched);
+ schedunlock();
}
// Called to start an M.
@@ -419,8 +449,15 @@ runtime·mstart(void)
runtime·throw("bad runtime·mstart");
if(m->mcache == nil)
m->mcache = runtime·allocmcache();
+
+ // Record top of stack for use by mcall.
+ // Once we call schedule we're never coming back,
+ // so other calls can reuse this stack space.
+ runtime·gosave(&m->g0->sched);
+ m->g0->sched.pc = (void*)-1; // make sure it is never used
+
runtime·minit();
- scheduler();
+ schedule(nil);
}
// When running with cgo, we call libcgo_thread_start
@@ -454,7 +491,7 @@ matchmg(void)
if((m = mget(g)) == nil){
m = runtime·malloc(sizeof(M));
// Add to runtime·allm so garbage collector doesn't free m
- // when it is just in a register (R14 on amd64).
+ // when it is just in a register or thread-local storage.
m->alllink = runtime·allm;
runtime·allm = m;
m->id = runtime·sched.mcount++;
@@ -469,7 +506,7 @@ matchmg(void)
ts.m = m;
ts.g = m->g0;
ts.fn = runtime·mstart;
- runtime·runcgo(libcgo_thread_start, &ts);
+ runtime·asmcgocall(libcgo_thread_start, &ts);
} else {
if(Windows)
// windows will layout sched stack on os stack
@@ -483,58 +520,19 @@ matchmg(void)
}
}
-// Scheduler loop: find g to run, run it, repeat.
+// One round of scheduler: find a goroutine and run it.
+// The argument is the goroutine that was running before
+// schedule was called, or nil if this is the first call.
+// Never returns.
static void
-scheduler(void)
+schedule(G *gp)
{
- G* gp;
-
- runtime·lock(&runtime·sched);
- if(runtime·gosave(&m->sched) != 0){
- gp = m->curg;
- if(gp->status == Grecovery) {
- // switched to scheduler to get stack unwound.
- // don't go through the full scheduling logic.
- Defer *d;
-
- d = gp->defer;
- gp->defer = d->link;
-
- // unwind to the stack frame with d's arguments in it.
- unwindstack(gp, d->argp);
-
- // make the deferproc for this d return again,
- // this time returning 1. function will jump to
- // standard return epilogue.
- // the -2*sizeof(uintptr) makes up for the
- // two extra words that are on the stack at
- // each call to deferproc.
- // (the pc we're returning to does pop pop
- // before it tests the return value.)
- // on the arm there are 2 saved LRs mixed in too.
- if(thechar == '5')
- gp->sched.sp = (byte*)d->argp - 4*sizeof(uintptr);
- else
- gp->sched.sp = (byte*)d->argp - 2*sizeof(uintptr);
- gp->sched.pc = d->pc;
- gp->status = Grunning;
- runtime·free(d);
- runtime·gogo(&gp->sched, 1);
- }
-
- if(gp->status == Gstackalloc) {
- // switched to scheduler stack to call stackalloc.
- gp->param = runtime·stackalloc((uintptr)gp->param);
- gp->status = Grunning;
- runtime·gogo(&gp->sched, 1);
- }
-
- // Jumped here via runtime·gosave/gogo, so didn't
- // execute lock(&runtime·sched) above.
- runtime·lock(&runtime·sched);
+ int32 hz;
+ schedlock();
+ if(gp != nil) {
if(runtime·sched.predawn)
- runtime·throw("init sleeping");
+ runtime·throw("init rescheduling");
// Just finished running gp.
gp->m = nil;
@@ -545,8 +543,6 @@ scheduler(void)
switch(gp->status){
case Grunnable:
case Gdead:
- case Grecovery:
- case Gstackalloc:
// Shouldn't have been running!
runtime·throw("bad gp->status in sched");
case Grunning:
@@ -578,10 +574,16 @@ scheduler(void)
gp->status = Grunning;
m->curg = gp;
gp->m = m;
+
+ // Check whether the profiler needs to be turned on or off.
+ hz = runtime·sched.profilehz;
+ if(m->profilehz != hz)
+ runtime·resetcpuprofiler(hz);
+
if(gp->sched.pc == (byte*)runtime·goexit) { // kickoff
runtime·gogocall(&gp->sched, (void(*)(void))gp->entry);
}
- runtime·gogo(&gp->sched, 1);
+ runtime·gogo(&gp->sched, 0);
}
// Enter scheduler. If g->status is Grunning,
@@ -595,8 +597,7 @@ runtime·gosched(void)
runtime·throw("gosched holding locks");
if(g == m->g0)
runtime·throw("gosched of g0");
- if(runtime·gosave(&g->sched) == 0)
- runtime·gogo(&m->sched, 1);
+ runtime·mcall(schedule);
}
// The goroutine g is about to enter a system call.
@@ -605,19 +606,20 @@ runtime·gosched(void)
// not from the low-level system calls used by the runtime.
// Entersyscall cannot split the stack: the runtime·gosave must
// make g->sched refer to the caller's stack pointer.
+// It's okay to call matchmg and notewakeup even after
+// decrementing mcpu, because we haven't released the
+// sched lock yet.
#pragma textflag 7
void
runtime·entersyscall(void)
{
- runtime·lock(&runtime·sched);
// Leave SP around for gc and traceback.
// Do before notewakeup so that gc
// never sees Gsyscall with wrong stack.
runtime·gosave(&g->sched);
- if(runtime·sched.predawn) {
- runtime·unlock(&runtime·sched);
+ if(runtime·sched.predawn)
return;
- }
+ schedlock();
g->status = Gsyscall;
runtime·sched.mcpu--;
runtime·sched.msyscall++;
@@ -627,7 +629,7 @@ runtime·entersyscall(void)
runtime·sched.waitstop = 0;
runtime·notewakeup(&runtime·sched.stopped);
}
- runtime·unlock(&runtime·sched);
+ schedunlock();
}
// The goroutine g exited its system call.
@@ -637,17 +639,16 @@ runtime·entersyscall(void)
void
runtime·exitsyscall(void)
{
- runtime·lock(&runtime·sched);
- if(runtime·sched.predawn) {
- runtime·unlock(&runtime·sched);
+ if(runtime·sched.predawn)
return;
- }
+
+ schedlock();
runtime·sched.msyscall--;
runtime·sched.mcpu++;
// Fast path - if there's room for this m, we're done.
- if(runtime·sched.mcpu <= runtime·sched.mcpumax) {
+ if(m->profilehz == runtime·sched.profilehz && runtime·sched.mcpu <= runtime·sched.mcpumax) {
g->status = Grunning;
- runtime·unlock(&runtime·sched);
+ schedunlock();
return;
}
// Tell scheduler to put g back on the run queue:
@@ -655,7 +656,7 @@ runtime·exitsyscall(void)
// but keeps the garbage collector from thinking
// that g is running right now, which it's not.
g->readyonstop = 1;
- runtime·unlock(&runtime·sched);
+ schedunlock();
// Slow path - all the cpus are taken.
// The scheduler will ready g and put this m to sleep.
@@ -664,60 +665,6 @@ runtime·exitsyscall(void)
runtime·gosched();
}
-// Restore the position of m's scheduler stack if we unwind the stack
-// through a cgo callback.
-static void
-runtime·unwindcgocallback(void **spaddr, void *sp)
-{
- *spaddr = sp;
-}
-
-// Start scheduling g1 again for a cgo callback.
-void
-runtime·startcgocallback(G* g1)
-{
- Defer *d;
-
- runtime·lock(&runtime·sched);
- g1->status = Grunning;
- runtime·sched.msyscall--;
- runtime·sched.mcpu++;
- runtime·unlock(&runtime·sched);
-
- // Add an entry to the defer stack which restores the old
- // position of m's scheduler stack. This is so that if the
- // code we are calling panics, we won't lose the space on the
- // scheduler stack. Note that we are locked to this m here.
- d = runtime·malloc(sizeof(*d) + 2*sizeof(void*) - sizeof(d->args));
- d->fn = (byte*)runtime·unwindcgocallback;
- d->siz = 2 * sizeof(uintptr);
- ((void**)d->args)[0] = &m->sched.sp;
- ((void**)d->args)[1] = m->sched.sp;
- d->link = g1->defer;
- g1->defer = d;
-}
-
-// Stop scheduling g1 after a cgo callback.
-void
-runtime·endcgocallback(G* g1)
-{
- Defer *d;
-
- runtime·lock(&runtime·sched);
- g1->status = Gsyscall;
- runtime·sched.mcpu--;
- runtime·sched.msyscall++;
- runtime·unlock(&runtime·sched);
-
- // Remove the entry on the defer stack added by
- // startcgocallback.
- d = g1->defer;
- if (d == nil || d->fn != (byte*)runtime·unwindcgocallback)
- runtime·throw("bad defer entry in endcgocallback");
- g1->defer = d->link;
- runtime·free(d);
-}
-
void
runtime·oldstack(void)
{
@@ -767,6 +714,10 @@ runtime·newstack(void)
runtime·printf("runtime: split stack overflow: %p < %p\n", m->morebuf.sp, g1->stackguard - StackGuard);
runtime·throw("runtime: split stack overflow");
}
+ if(argsize % sizeof(uintptr) != 0) {
+ runtime·printf("runtime: stack split with misaligned argsize %d\n", argsize);
+ runtime·throw("runtime: stack split argsize");
+ }
reflectcall = framesize==1;
if(reflectcall)
@@ -831,12 +782,18 @@ runtime·newstack(void)
*(int32*)345 = 123; // never return
}
+static void
+mstackalloc(G *gp)
+{
+ gp->param = runtime·stackalloc((uintptr)gp->param);
+ runtime·gogo(&gp->sched, 0);
+}
+
G*
runtime·malg(int32 stacksize)
{
G *newg;
byte *stk;
- int32 oldstatus;
newg = runtime·malloc(sizeof(G));
if(stacksize >= 0) {
@@ -845,17 +802,10 @@ runtime·malg(int32 stacksize)
stk = runtime·stackalloc(StackSystem + stacksize);
} else {
// have to call stackalloc on scheduler stack.
- oldstatus = g->status;
g->param = (void*)(StackSystem + stacksize);
- g->status = Gstackalloc;
- // next two lines are runtime·gosched without the check
- // of m->locks. we're almost certainly holding a lock,
- // but this is not a real rescheduling so it's okay.
- if(runtime·gosave(&g->sched) == 0)
- runtime·gogo(&m->sched, 1);
+ runtime·mcall(mstackalloc);
stk = g->param;
g->param = nil;
- g->status = oldstatus;
}
newg->stack0 = stk;
newg->stackguard = stk + StackSystem + StackGuard;
@@ -900,7 +850,7 @@ runtime·newproc1(byte *fn, byte *argp, int32 narg, int32 nret, void *callerpc)
if(siz > 1024)
runtime·throw("runtime.newproc: too many args");
- runtime·lock(&runtime·sched);
+ schedlock();
if((newg = gfget()) != nil){
newg->status = Gwaiting;
@@ -933,7 +883,7 @@ runtime·newproc1(byte *fn, byte *argp, int32 narg, int32 nret, void *callerpc)
newg->goid = runtime·goidgen;
newprocreadylocked(newg);
- runtime·unlock(&runtime·sched);
+ schedunlock();
return newg;
//printf(" goid=%d\n", newg->goid);
@@ -1040,6 +990,8 @@ printpanics(Panic *p)
runtime·printf(" [recovered]");
runtime·printf("\n");
}
+
+static void recovery(G*);
void
runtime·panic(Eface e)
@@ -1070,9 +1022,8 @@ runtime·panic(Eface e)
// for scheduler to find.
d->link = g->defer;
g->defer = d;
- g->status = Grecovery;
- runtime·gosched();
- runtime·throw("recovery failed"); // gosched should not return
+ runtime·mcall(recovery);
+ runtime·throw("recovery failed"); // mcall should not return
}
runtime·free(d);
}
@@ -1083,6 +1034,36 @@ runtime·panic(Eface e)
runtime·dopanic(0);
}
+static void
+recovery(G *gp)
+{
+ Defer *d;
+
+ // Rewind gp's stack; we're running on m->g0's stack.
+ d = gp->defer;
+ gp->defer = d->link;
+
+ // Unwind to the stack frame with d's arguments in it.
+ unwindstack(gp, d->argp);
+
+ // Make the deferproc for this d return again,
+ // this time returning 1. The calling function will
+ // jump to the standard return epilogue.
+ // The -2*sizeof(uintptr) makes up for the
+ // two extra words that are on the stack at
+ // each call to deferproc.
+ // (The pc we're returning to does pop pop
+ // before it tests the return value.)
+ // On the arm there are 2 saved LRs mixed in too.
+ if(thechar == '5')
+ gp->sched.sp = (byte*)d->argp - 4*sizeof(uintptr);
+ else
+ gp->sched.sp = (byte*)d->argp - 2*sizeof(uintptr);
+ gp->sched.pc = d->pc;
+ runtime·free(d);
+ runtime·gogo(&gp->sched, 1);
+}
+
#pragma textflag 7 /* no split, or else g->stackguard is not the stack for fp */
void
runtime·recover(byte *argp, Eface ret)
@@ -1210,7 +1191,7 @@ runtime·gomaxprocsfunc(int32 n)
{
int32 ret;
- runtime·lock(&runtime·sched);
+ schedlock();
ret = runtime·gomaxprocs;
if (n <= 0)
n = ret;
@@ -1218,7 +1199,7 @@ runtime·gomaxprocsfunc(int32 n)
runtime·sched.mcpumax = n;
// handle fewer procs?
if(runtime·sched.mcpu > runtime·sched.mcpumax) {
- runtime·unlock(&runtime·sched);
+ schedunlock();
// just give up the cpu.
// we'll only get rescheduled once the
// number has come down.
@@ -1227,7 +1208,7 @@ runtime·gomaxprocsfunc(int32 n)
}
// handle more procs
matchmg();
- runtime·unlock(&runtime·sched);
+ schedunlock();
return ret;
}
@@ -1238,6 +1219,12 @@ runtime·UnlockOSThread(void)
g->lockedm = nil;
}
+bool
+runtime·lockedOSThread(void)
+{
+ return g->lockedm != nil && m->lockedg != nil;
+}
+
// for testing of wire, unwire
void
runtime·mid(uint32 ret)
@@ -1258,3 +1245,69 @@ runtime·mcount(void)
{
return runtime·sched.mcount;
}
+
+void
+runtime·badmcall(void) // called from assembly
+{
+ runtime·throw("runtime: mcall called on m->g0 stack");
+}
+
+void
+runtime·badmcall2(void) // called from assembly
+{
+ runtime·throw("runtime: mcall function returned");
+}
+
+static struct {
+ Lock;
+ void (*fn)(uintptr*, int32);
+ int32 hz;
+ uintptr pcbuf[100];
+} prof;
+
+void
+runtime·sigprof(uint8 *pc, uint8 *sp, uint8 *lr, G *gp)
+{
+ int32 n;
+
+ if(prof.fn == nil || prof.hz == 0)
+ return;
+
+ runtime·lock(&prof);
+ if(prof.fn == nil) {
+ runtime·unlock(&prof);
+ return;
+ }
+ n = runtime·gentraceback(pc, sp, lr, gp, 0, prof.pcbuf, nelem(prof.pcbuf));
+ if(n > 0)
+ prof.fn(prof.pcbuf, n);
+ runtime·unlock(&prof);
+}
+
+void
+runtime·setcpuprofilerate(void (*fn)(uintptr*, int32), int32 hz)
+{
+ // Force sane arguments.
+ if(hz < 0)
+ hz = 0;
+ if(hz == 0)
+ fn = nil;
+ if(fn == nil)
+ hz = 0;
+
+ // Stop profiler on this cpu so that it is safe to lock prof.
+ // if a profiling signal came in while we had prof locked,
+ // it would deadlock.
+ runtime·resetcpuprofiler(0);
+
+ runtime·lock(&prof);
+ prof.fn = fn;
+ prof.hz = hz;
+ runtime·unlock(&prof);
+ runtime·lock(&runtime·sched);
+ runtime·sched.profilehz = hz;
+ runtime·unlock(&runtime·sched);
+
+ if(hz != 0)
+ runtime·resetcpuprofiler(hz);
+}
diff --git a/src/pkg/runtime/reflect.goc b/src/pkg/runtime/reflect.goc
index 71d648266..9bdc48afb 100644
--- a/src/pkg/runtime/reflect.goc
+++ b/src/pkg/runtime/reflect.goc
@@ -70,22 +70,18 @@ func makechan(typ *byte, size uint32) (ch *byte) {
ch = (byte*)runtime·makechan_c(t->elem, size);
}
-func chansend(ch *byte, val *byte, pres *bool) {
- runtime·chansend((Hchan*)ch, val, pres);
+func chansend(ch *byte, val *byte, selected *bool) {
+ runtime·chansend((Hchan*)ch, val, selected);
}
-func chanrecv(ch *byte, val *byte, pres *bool) {
- runtime·chanrecv((Hchan*)ch, val, pres, nil);
+func chanrecv(ch *byte, val *byte, selected *bool, received *bool) {
+ runtime·chanrecv((Hchan*)ch, val, selected, received);
}
func chanclose(ch *byte) {
runtime·chanclose((Hchan*)ch);
}
-func chanclosed(ch *byte) (r bool) {
- r = runtime·chanclosed((Hchan*)ch);
-}
-
func chanlen(ch *byte) (r int32) {
r = runtime·chanlen((Hchan*)ch);
}
diff --git a/src/pkg/runtime/runtime-gdb.py b/src/pkg/runtime/runtime-gdb.py
index 68dd8abdc..08772a431 100644
--- a/src/pkg/runtime/runtime-gdb.py
+++ b/src/pkg/runtime/runtime-gdb.py
@@ -215,6 +215,8 @@ class IfacePrinter:
return 'string'
def to_string(self):
+ if self.val['data'] == 0:
+ return 0x0
try:
dtype = iface_dtype(self.val)
except:
@@ -308,15 +310,11 @@ class GoroutinesCmd(gdb.Command):
for ptr in linked_list(gdb.parse_and_eval("'runtime.allg'"), 'alllink'):
if ptr['status'] == 6: # 'gdead'
continue
- m = ptr['m']
s = ' '
- if m:
- pc = m['sched']['pc'].cast(vp)
- sp = m['sched']['sp'].cast(vp)
+ if ptr['m']:
s = '*'
- else:
- pc = ptr['sched']['pc'].cast(vp)
- sp = ptr['sched']['sp'].cast(vp)
+ pc = ptr['sched']['pc'].cast(vp)
+ sp = ptr['sched']['sp'].cast(vp)
blk = gdb.block_for_pc(long((pc)))
print s, ptr['goid'], "%8s" % sts[long((ptr['status']))], blk.function
@@ -326,7 +324,7 @@ def find_goroutine(goid):
if ptr['status'] == 6: # 'gdead'
continue
if ptr['goid'] == goid:
- return [(ptr['m'] or ptr)['sched'][x].cast(vp) for x in 'pc', 'sp']
+ return [ptr['sched'][x].cast(vp) for x in 'pc', 'sp']
return None, None
diff --git a/src/pkg/runtime/runtime.h b/src/pkg/runtime/runtime.h
index 85dca54f7..6cf2685fd 100644
--- a/src/pkg/runtime/runtime.h
+++ b/src/pkg/runtime/runtime.h
@@ -103,8 +103,6 @@ enum
Gwaiting,
Gmoribund,
Gdead,
- Grecovery,
- Gstackalloc,
};
enum
{
@@ -219,7 +217,6 @@ struct M
uint64 procid; // for debuggers, but offset not hard-coded
G* gsignal; // signal-handling G
uint32 tls[8]; // thread-local storage (for 386 extern register)
- Gobuf sched; // scheduling stack
G* curg; // current running goroutine
int32 id;
int32 mallocing;
@@ -228,6 +225,7 @@ struct M
int32 nomemprof;
int32 waitnextg;
int32 dying;
+ int32 profilehz;
Note havenextg;
G* nextg;
M* alllink; // on allm
@@ -385,7 +383,7 @@ int32 runtime·charntorune(int32*, uint8*, int32);
void runtime·gogo(Gobuf*, uintptr);
void runtime·gogocall(Gobuf*, void(*)(void));
-uintptr runtime·gosave(Gobuf*);
+void runtime·gosave(Gobuf*);
void runtime·lessstack(void);
void runtime·goargs(void);
void runtime·goenvs(void);
@@ -442,25 +440,27 @@ void runtime·walkfintab(void (*fn)(void*));
void runtime·runpanic(Panic*);
void* runtime·getcallersp(void*);
int32 runtime·mcount(void);
+void runtime·mcall(void(*)(G*));
void runtime·exit(int32);
void runtime·breakpoint(void);
void runtime·gosched(void);
void runtime·goexit(void);
-void runtime·runcgo(void (*fn)(void*), void*);
-void runtime·runcgocallback(G*, void*, void (*fn)());
+void runtime·asmcgocall(void (*fn)(void*), void*);
void runtime·entersyscall(void);
void runtime·exitsyscall(void);
-void runtime·startcgocallback(G*);
-void runtime·endcgocallback(G*);
G* runtime·newproc1(byte*, byte*, int32, int32, void*);
void runtime·siginit(void);
bool runtime·sigsend(int32 sig);
void runtime·gettime(int64*, int32*);
int32 runtime·callers(int32, uintptr*, int32);
+int32 runtime·gentraceback(byte*, byte*, byte*, G*, int32, uintptr*, int32);
int64 runtime·nanotime(void);
void runtime·dopanic(int32);
void runtime·startpanic(void);
+void runtime·sigprof(uint8 *pc, uint8 *sp, uint8 *lr, G *gp);
+void runtime·resetcpuprofiler(int32);
+void runtime·setcpuprofilerate(void(*)(uintptr*, int32), int32);
#pragma varargck argpos runtime·printf 1
#pragma varargck type "d" int32
@@ -590,7 +590,6 @@ Hchan* runtime·makechan_c(Type*, int64);
void runtime·chansend(Hchan*, void*, bool*);
void runtime·chanrecv(Hchan*, void*, bool*, bool*);
void runtime·chanclose(Hchan*);
-bool runtime·chanclosed(Hchan*);
int32 runtime·chanlen(Hchan*);
int32 runtime·chancap(Hchan*);
diff --git a/src/pkg/runtime/windows/386/signal.c b/src/pkg/runtime/windows/386/signal.c
index 08b32a137..cc6a2302f 100644
--- a/src/pkg/runtime/windows/386/signal.c
+++ b/src/pkg/runtime/windows/386/signal.c
@@ -88,3 +88,11 @@ runtime·sighandler(ExceptionRecord *info, void *frame, Context *r)
runtime·exit(2);
return 0;
}
+
+void
+runtime·resetcpuprofiler(int32 hz)
+{
+ // TODO: Enable profiling interrupts.
+
+ m->profilehz = hz;
+}
diff --git a/src/pkg/runtime/windows/386/sys.s b/src/pkg/runtime/windows/386/sys.s
index bca48febe..15f7f95b8 100644
--- a/src/pkg/runtime/windows/386/sys.s
+++ b/src/pkg/runtime/windows/386/sys.s
@@ -20,7 +20,7 @@ TEXT runtime·stdcall_raw(SB),7,$0
CMPL g(DI), SI
MOVL SP, BX
JEQ 2(PC)
- MOVL (m_sched+gobuf_sp)(DX), SP
+ MOVL (g_sched+gobuf_sp)(SI), SP
PUSHL BX
PUSHL g(DI)
MOVL SI, g(DI)
diff --git a/src/pkg/scanner/scanner.go b/src/pkg/scanner/scanner.go
index 2396cdd9a..ec2266477 100644
--- a/src/pkg/scanner/scanner.go
+++ b/src/pkg/scanner/scanner.go
@@ -115,7 +115,7 @@ func TokenString(tok int) string {
if s, found := tokenString[tok]; found {
return s
}
- return fmt.Sprintf("U+%04X", tok)
+ return fmt.Sprintf("%q", string(tok))
}
@@ -331,7 +331,7 @@ func (s *Scanner) error(msg string) {
s.Error(s, msg)
return
}
- fmt.Fprintf(os.Stderr, "%s: %s", s.Position, msg)
+ fmt.Fprintf(os.Stderr, "%s: %s\n", s.Position, msg)
}
@@ -503,41 +503,32 @@ func (s *Scanner) scanChar() {
}
-func (s *Scanner) scanLineComment() {
- ch := s.next() // read character after "//"
- for ch != '\n' {
- if ch < 0 {
- s.error("comment not terminated")
- return
+func (s *Scanner) scanComment(ch int) int {
+ // ch == '/' || ch == '*'
+ if ch == '/' {
+ // line comment
+ ch = s.next() // read character after "//"
+ for ch != '\n' && ch >= 0 {
+ ch = s.next()
}
- ch = s.next()
+ return ch
}
-}
-
-func (s *Scanner) scanGeneralComment() {
- ch := s.next() // read character after "/*"
+ // general comment
+ ch = s.next() // read character after "/*"
for {
if ch < 0 {
s.error("comment not terminated")
- return
+ break
}
ch0 := ch
ch = s.next()
if ch0 == '*' && ch == '/' {
+ ch = s.next()
break
}
}
-}
-
-
-func (s *Scanner) scanComment(ch int) {
- // ch == '/' || ch == '*'
- if ch == '/' {
- s.scanLineComment()
- return
- }
- s.scanGeneralComment()
+ return ch
}
@@ -619,13 +610,11 @@ redo:
if (ch == '/' || ch == '*') && s.Mode&ScanComments != 0 {
if s.Mode&SkipComments != 0 {
s.tokPos = -1 // don't collect token text
- s.scanComment(ch)
- ch = s.next()
+ ch = s.scanComment(ch)
goto redo
}
- s.scanComment(ch)
+ ch = s.scanComment(ch)
tok = Comment
- ch = s.next()
}
case '`':
if s.Mode&ScanRawStrings != 0 {
diff --git a/src/pkg/scanner/scanner_test.go b/src/pkg/scanner/scanner_test.go
index 002252de8..cf9ad0111 100644
--- a/src/pkg/scanner/scanner_test.go
+++ b/src/pkg/scanner/scanner_test.go
@@ -77,15 +77,15 @@ type token struct {
var f100 = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
var tokenList = []token{
- {Comment, "// line comments\n"},
- {Comment, "//\n"},
- {Comment, "////\n"},
- {Comment, "// comment\n"},
- {Comment, "// /* comment */\n"},
- {Comment, "// // comment //\n"},
- {Comment, "//" + f100 + "\n"},
-
- {Comment, "// general comments\n"},
+ {Comment, "// line comments"},
+ {Comment, "//"},
+ {Comment, "////"},
+ {Comment, "// comment"},
+ {Comment, "// /* comment */"},
+ {Comment, "// // comment //"},
+ {Comment, "//" + f100},
+
+ {Comment, "// general comments"},
{Comment, "/**/"},
{Comment, "/***/"},
{Comment, "/* comment */"},
@@ -94,7 +94,7 @@ var tokenList = []token{
{Comment, "/*\n comment\n*/"},
{Comment, "/*" + f100 + "*/"},
- {Comment, "// identifiers\n"},
+ {Comment, "// identifiers"},
{Ident, "a"},
{Ident, "a0"},
{Ident, "foobar"},
@@ -116,21 +116,21 @@ var tokenList = []token{
{Ident, "bar9876"},
{Ident, f100},
- {Comment, "// decimal ints\n"},
+ {Comment, "// decimal ints"},
{Int, "0"},
{Int, "1"},
{Int, "9"},
{Int, "42"},
{Int, "1234567890"},
- {Comment, "// octal ints\n"},
+ {Comment, "// octal ints"},
{Int, "00"},
{Int, "01"},
{Int, "07"},
{Int, "042"},
{Int, "01234567"},
- {Comment, "// hexadecimal ints\n"},
+ {Comment, "// hexadecimal ints"},
{Int, "0x0"},
{Int, "0x1"},
{Int, "0xf"},
@@ -144,7 +144,7 @@ var tokenList = []token{
{Int, "0X123456789abcDEF"},
{Int, "0X" + f100},
- {Comment, "// floats\n"},
+ {Comment, "// floats"},
{Float, "0."},
{Float, "1."},
{Float, "42."},
@@ -174,7 +174,7 @@ var tokenList = []token{
{Float, "42E+10"},
{Float, "01234567890E-10"},
- {Comment, "// chars\n"},
+ {Comment, "// chars"},
{Char, `' '`},
{Char, `'a'`},
{Char, `'本'`},
@@ -195,7 +195,7 @@ var tokenList = []token{
{Char, `'\U00000000'`},
{Char, `'\U0000ffAB'`},
- {Comment, "// strings\n"},
+ {Comment, "// strings"},
{String, `" "`},
{String, `"a"`},
{String, `"本"`},
@@ -217,13 +217,13 @@ var tokenList = []token{
{String, `"\U0000ffAB"`},
{String, `"` + f100 + `"`},
- {Comment, "// raw strings\n"},
+ {Comment, "// raw strings"},
{String, "``"},
{String, "`\\`"},
{String, "`" + "\n\n/* foobar */\n\n" + "`"},
{String, "`" + f100 + "`"},
- {Comment, "// individual characters\n"},
+ {Comment, "// individual characters"},
// NUL character is not allowed
{'\x01', "\x01"},
{' ' - 1, string(' ' - 1)},
@@ -276,7 +276,7 @@ func countNewlines(s string) int {
func testScan(t *testing.T, mode uint) {
- s := new(Scanner).Init(makeSource(" \t%s\t\n\r"))
+ s := new(Scanner).Init(makeSource(" \t%s\n"))
s.Mode = mode
tok := s.Scan()
line := 1
@@ -287,7 +287,7 @@ func testScan(t *testing.T, mode uint) {
}
line += countNewlines(k.text) + 1 // each token is on a new line
}
- checkTok(t, s, line, tok, -1, "")
+ checkTok(t, s, line, tok, EOF, "")
}
@@ -317,6 +317,10 @@ func TestPosition(t *testing.T) {
pos.Line += countNewlines(k.text) + 1 // each token is on a new line
s.Scan()
}
+ // make sure there were no token-internal errors reported by scanner
+ if s.ErrorCount != 0 {
+ t.Errorf("%d errors", s.ErrorCount)
+ }
}
@@ -336,6 +340,9 @@ func TestScanZeroMode(t *testing.T) {
if tok != EOF {
t.Fatalf("tok = %s, want EOF", TokenString(tok))
}
+ if s.ErrorCount != 0 {
+ t.Errorf("%d errors", s.ErrorCount)
+ }
}
@@ -350,6 +357,9 @@ func testScanSelectedMode(t *testing.T, mode uint, class int) {
}
tok = s.Scan()
}
+ if s.ErrorCount != 0 {
+ t.Errorf("%d errors", s.ErrorCount)
+ }
}
@@ -367,7 +377,7 @@ func TestScanSelectedMask(t *testing.T) {
func TestScanNext(t *testing.T) {
- s := new(Scanner).Init(bytes.NewBufferString("if a == bcd /* comment */ {\n\ta += c\n}"))
+ s := new(Scanner).Init(bytes.NewBufferString("if a == bcd /* comment */ {\n\ta += c\n} // line comment ending in eof"))
checkTok(t, s, 1, s.Scan(), Ident, "if")
checkTok(t, s, 1, s.Scan(), Ident, "a")
checkTok(t, s, 1, s.Scan(), '=', "=")
@@ -382,6 +392,9 @@ func TestScanNext(t *testing.T) {
checkTok(t, s, 2, s.Scan(), Ident, "c")
checkTok(t, s, 3, s.Scan(), '}', "}")
checkTok(t, s, 3, s.Scan(), -1, "")
+ if s.ErrorCount != 0 {
+ t.Errorf("%d errors", s.ErrorCount)
+ }
}
@@ -441,7 +454,6 @@ func TestError(t *testing.T) {
testError(t, `"\'"`, "illegal char escape", String)
testError(t, `"abc`, "literal not terminated", String)
testError(t, "`abc", "literal not terminated", String)
- testError(t, `//`, "comment not terminated", EOF)
testError(t, `/*/`, "comment not terminated", EOF)
testError(t, `"abc`+"\x00"+`def"`, "illegal character NUL", String)
testError(t, `"abc`+"\xff"+`def"`, "illegal UTF-8 encoding", String)
@@ -493,6 +505,9 @@ func TestPos(t *testing.T) {
for i := 10; i > 0; i-- {
checkScanPos(t, s, 1, 2, 1, EOF)
}
+ if s.ErrorCount != 0 {
+ t.Errorf("%d errors", s.ErrorCount)
+ }
// corner case: source with only a single character
s = new(Scanner).Init(bytes.NewBufferString("本"))
@@ -502,6 +517,9 @@ func TestPos(t *testing.T) {
for i := 10; i > 0; i-- {
checkScanPos(t, s, 3, 1, 2, EOF)
}
+ if s.ErrorCount != 0 {
+ t.Errorf("%d errors", s.ErrorCount)
+ }
// positions after calling Next
s = new(Scanner).Init(bytes.NewBufferString(" foo६४ \n\n本語\n"))
@@ -524,6 +542,9 @@ func TestPos(t *testing.T) {
for i := 10; i > 0; i-- {
checkScanPos(t, s, 22, 4, 1, EOF)
}
+ if s.ErrorCount != 0 {
+ t.Errorf("%d errors", s.ErrorCount)
+ }
// positions after calling Scan
s = new(Scanner).Init(bytes.NewBufferString("abc\n本語\n\nx"))
@@ -543,4 +564,7 @@ func TestPos(t *testing.T) {
for i := 10; i > 0; i-- {
checkScanPos(t, s, 13, 4, 2, EOF)
}
+ if s.ErrorCount != 0 {
+ t.Errorf("%d errors", s.ErrorCount)
+ }
}
diff --git a/src/pkg/smtp/smtp.go b/src/pkg/smtp/smtp.go
index 2f6d2f31a..3f89af147 100644
--- a/src/pkg/smtp/smtp.go
+++ b/src/pkg/smtp/smtp.go
@@ -39,7 +39,7 @@ type Client struct {
// Dial returns a new Client connected to an SMTP server at addr.
func Dial(addr string) (*Client, os.Error) {
- conn, err := net.Dial("tcp", "", addr)
+ conn, err := net.Dial("tcp", addr)
if err != nil {
return nil, err
}
diff --git a/src/pkg/sort/sort_test.go b/src/pkg/sort/sort_test.go
index 1bea8f032..3d7337fd0 100644
--- a/src/pkg/sort/sort_test.go
+++ b/src/pkg/sort/sort_test.go
@@ -74,7 +74,11 @@ func TestSortStrings(t *testing.T) {
}
func TestSortLarge_Random(t *testing.T) {
- data := make([]int, 1000000)
+ n := 1000000
+ if testing.Short() {
+ n /= 100
+ }
+ data := make([]int, n)
for i := 0; i < len(data); i++ {
data[i] = rand.Intn(100)
}
@@ -174,6 +178,9 @@ func lg(n int) int {
func TestBentleyMcIlroy(t *testing.T) {
sizes := []int{100, 1023, 1024, 1025}
+ if testing.Short() {
+ sizes = []int{100, 127, 128, 129}
+ }
dists := []string{"sawtooth", "rand", "stagger", "plateau", "shuffle"}
modes := []string{"copy", "reverse", "reverse1", "reverse2", "sort", "dither"}
var tmp1, tmp2 [1025]int
diff --git a/src/pkg/strconv/fp_test.go b/src/pkg/strconv/fp_test.go
index 305adcc0c..34baeee39 100644
--- a/src/pkg/strconv/fp_test.go
+++ b/src/pkg/strconv/fp_test.go
@@ -94,7 +94,7 @@ func myatof32(s string) (f float32, ok bool) {
}
func TestFp(t *testing.T) {
- f, err := os.Open("testfp.txt", os.O_RDONLY, 0)
+ f, err := os.Open("testfp.txt")
if err != nil {
t.Fatal("testfp: open testfp.txt:", err.String())
}
diff --git a/src/pkg/strings/strings.go b/src/pkg/strings/strings.go
index 98a0d5731..93c7c4647 100644
--- a/src/pkg/strings/strings.go
+++ b/src/pkg/strings/strings.go
@@ -119,9 +119,19 @@ func LastIndex(s, sep string) int {
// IndexRune returns the index of the first instance of the Unicode code point
// rune, or -1 if rune is not present in s.
func IndexRune(s string, rune int) int {
- for i, c := range s {
- if c == rune {
- return i
+ switch {
+ case rune < 0x80:
+ b := byte(rune)
+ for i := 0; i < len(s); i++ {
+ if s[i] == b {
+ return i
+ }
+ }
+ default:
+ for i, c := range s {
+ if c == rune {
+ return i
+ }
}
}
return -1
@@ -265,20 +275,10 @@ func Join(a []string, sep string) string {
}
b := make([]byte, n)
- bp := 0
- for i := 0; i < len(a); i++ {
- s := a[i]
- for j := 0; j < len(s); j++ {
- b[bp] = s[j]
- bp++
- }
- if i+1 < len(a) {
- s = sep
- for j := 0; j < len(s); j++ {
- b[bp] = s[j]
- bp++
- }
- }
+ bp := copy(b, a[0])
+ for _, s := range a[1:] {
+ bp += copy(b[bp:], sep)
+ bp += copy(b[bp:], s)
}
return string(b)
}
@@ -302,9 +302,19 @@ func Map(mapping func(rune int) int, s string) string {
// fine. It could also shrink but that falls out naturally.
maxbytes := len(s) // length of b
nbytes := 0 // number of bytes encoded in b
- b := make([]byte, maxbytes)
- for _, c := range s {
+ // The output buffer b is initialized on demand, the first
+ // time a character differs.
+ var b []byte
+
+ for i, c := range s {
rune := mapping(c)
+ if b == nil {
+ if rune == c {
+ continue
+ }
+ b = make([]byte, maxbytes)
+ nbytes = copy(b, s[:i])
+ }
if rune >= 0 {
wid := 1
if rune >= utf8.RuneSelf {
@@ -320,6 +330,9 @@ func Map(mapping func(rune int) int, s string) string {
nbytes += utf8.EncodeRune(b[nbytes:maxbytes], rune)
}
}
+ if b == nil {
+ return s
+ }
return string(b[0:nbytes])
}
diff --git a/src/pkg/strings/strings_test.go b/src/pkg/strings/strings_test.go
index 734fdd33d..c45b1485d 100644
--- a/src/pkg/strings/strings_test.go
+++ b/src/pkg/strings/strings_test.go
@@ -6,9 +6,12 @@ package strings_test
import (
"os"
+ "reflect"
+ "strconv"
. "strings"
"testing"
"unicode"
+ "unsafe"
"utf8"
)
@@ -116,6 +119,57 @@ func TestLastIndex(t *testing.T) { runIndexTests(t, LastIndex, "LastIndex", l
func TestIndexAny(t *testing.T) { runIndexTests(t, IndexAny, "IndexAny", indexAnyTests) }
func TestLastIndexAny(t *testing.T) { runIndexTests(t, LastIndexAny, "LastIndexAny", lastIndexAnyTests) }
+type IndexRuneTest struct {
+ s string
+ rune int
+ out int
+}
+
+var indexRuneTests = []IndexRuneTest{
+ {"a A x", 'A', 2},
+ {"some_text=some_value", '=', 9},
+ {"☺a", 'a', 3},
+ {"a☻☺b", '☺', 4},
+}
+
+func TestIndexRune(t *testing.T) {
+ for _, test := range indexRuneTests {
+ if actual := IndexRune(test.s, test.rune); actual != test.out {
+ t.Errorf("IndexRune(%q,%d)= %v; want %v", test.s, test.rune, actual, test.out)
+ }
+ }
+}
+
+const benchmarkString = "some_text=some☺value"
+
+func BenchmarkIndexRune(b *testing.B) {
+ if got := IndexRune(benchmarkString, '☺'); got != 14 {
+ panic("wrong index: got=" + strconv.Itoa(got))
+ }
+ for i := 0; i < b.N; i++ {
+ IndexRune(benchmarkString, '☺')
+ }
+}
+
+func BenchmarkIndexRuneFastPath(b *testing.B) {
+ if got := IndexRune(benchmarkString, 'v'); got != 17 {
+ panic("wrong index: got=" + strconv.Itoa(got))
+ }
+ for i := 0; i < b.N; i++ {
+ IndexRune(benchmarkString, 'v')
+ }
+}
+
+func BenchmarkIndex(b *testing.B) {
+ if got := Index(benchmarkString, "v"); got != 17 {
+ panic("wrong index: got=" + strconv.Itoa(got))
+ }
+ for i := 0; i < b.N; i++ {
+ Index(benchmarkString, "v")
+ }
+}
+
+
type ExplodeTest struct {
s string
n int
@@ -377,12 +431,32 @@ func TestMap(t *testing.T) {
if m != expect {
t.Errorf("drop: expected %q got %q", expect, m)
}
+
+ // 6. Identity
+ identity := func(rune int) int {
+ return rune
+ }
+ orig := "Input string that we expect not to be copied."
+ m = Map(identity, orig)
+ if (*reflect.StringHeader)(unsafe.Pointer(&orig)).Data !=
+ (*reflect.StringHeader)(unsafe.Pointer(&m)).Data {
+ t.Error("unexpected copy during identity map")
+ }
}
func TestToUpper(t *testing.T) { runStringTests(t, ToUpper, "ToUpper", upperTests) }
func TestToLower(t *testing.T) { runStringTests(t, ToLower, "ToLower", lowerTests) }
+func BenchmarkMapNoChanges(b *testing.B) {
+ identity := func(rune int) int {
+ return rune
+ }
+ for i := 0; i < b.N; i++ {
+ Map(identity, "Some string that won't be modified.")
+ }
+}
+
func TestSpecialCase(t *testing.T) {
lower := "abcçdefgğhıijklmnoöprsştuüvyz"
upper := "ABCÇDEFGĞHIİJKLMNOÖPRSŞTUÜVYZ"
@@ -565,7 +639,11 @@ func equal(m string, s1, s2 string, t *testing.T) bool {
func TestCaseConsistency(t *testing.T) {
// Make a string of all the runes.
- a := make([]int, unicode.MaxRune+1)
+ numRunes := unicode.MaxRune + 1
+ if testing.Short() {
+ numRunes = 1000
+ }
+ a := make([]int, numRunes)
for i := range a {
a[i] = i
}
@@ -575,10 +653,10 @@ func TestCaseConsistency(t *testing.T) {
lower := ToLower(s)
// Consistency checks
- if n := utf8.RuneCountInString(upper); n != unicode.MaxRune+1 {
+ if n := utf8.RuneCountInString(upper); n != numRunes {
t.Error("rune count wrong in upper:", n)
}
- if n := utf8.RuneCountInString(lower); n != unicode.MaxRune+1 {
+ if n := utf8.RuneCountInString(lower); n != numRunes {
t.Error("rune count wrong in lower:", n)
}
if !equal("ToUpper(upper)", ToUpper(upper), upper, t) {
diff --git a/src/pkg/sync/atomic/asm_arm.s b/src/pkg/sync/atomic/asm_arm.s
index 1ae0a995e..3363bbcf1 100644
--- a/src/pkg/sync/atomic/asm_arm.s
+++ b/src/pkg/sync/atomic/asm_arm.s
@@ -25,6 +25,7 @@ casfail:
RET
TEXT ·armCompareAndSwapUint64(SB),7,$0
+ BL fastCheck64<>(SB)
MOVW valptr+0(FP), R1
MOVW oldlo+4(FP), R2
MOVW oldhi+8(FP), R3
@@ -62,6 +63,7 @@ addloop:
RET
TEXT ·armAddUint64(SB),7,$0
+ BL fastCheck64<>(SB)
MOVW valptr+0(FP), R1
MOVW deltalo+4(FP), R2
MOVW deltahi+8(FP), R3
@@ -76,3 +78,42 @@ add64loop:
MOVW R4, retlo+12(FP)
MOVW R5, rethi+16(FP)
RET
+
+// Check for broken 64-bit LDREXD as found in QEMU.
+// LDREXD followed by immediate STREXD should succeed.
+// If it fails, try a few times just to be sure (maybe our thread got
+// rescheduled between the two instructions) and then panic.
+// A bug in some copies of QEMU makes STREXD never succeed,
+// which will make uses of the 64-bit atomic operations loop forever.
+// If things are working, set okLDREXD to avoid future checks.
+// https://bugs.launchpad.net/qemu/+bug/670883.
+TEXT check64<>(SB),7,$8
+ MOVW $10, R1
+loop:
+ LDREXD (SP), R2
+ STREXD R2, (SP), R0
+ CMP $0, R0
+ BEQ ok
+ SUB $1, R1
+ CMP $0, R1
+ BNE loop
+ // Must be buggy QEMU.
+ BL ·panic64(SB)
+ok:
+ RET
+
+// Fast, cached version of check. No frame, just MOVW CMP RET after first time.
+TEXT fastCheck64<>(SB),7,$-4
+ MOVW ok64<>(SB), R0
+ CMP $0, R0 // have we been here before?
+ RET.NE
+ B slowCheck64<>(SB)
+
+TEXT slowCheck64<>(SB),7,$0
+ BL check64<>(SB)
+ // Still here, must be okay.
+ MOVW $1, R0
+ MOVW R0, ok64<>(SB)
+ RET
+
+GLOBL ok64<>(SB), $4
diff --git a/src/pkg/sync/atomic/atomic_test.go b/src/pkg/sync/atomic/atomic_test.go
index 7b204b1d9..119ad0036 100644
--- a/src/pkg/sync/atomic/atomic_test.go
+++ b/src/pkg/sync/atomic/atomic_test.go
@@ -2,10 +2,11 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package atomic
+package atomic_test
import (
"runtime"
+ . "sync/atomic"
"testing"
"unsafe"
)
@@ -27,6 +28,16 @@ const (
magic64 = 0xdeddeadbeefbeef
)
+// Do the 64-bit functions panic? If so, don't bother testing.
+var test64err = func() (err interface{}) {
+ defer func() {
+ err = recover()
+ }()
+ var x int64
+ AddInt64(&x, 1)
+ return nil
+}()
+
func TestAddInt32(t *testing.T) {
var x struct {
before int32
@@ -70,6 +81,10 @@ func TestAddUint32(t *testing.T) {
}
func TestAddInt64(t *testing.T) {
+ if test64err != nil {
+ t.Logf("Skipping 64-bit tests: %v", test64err)
+ return
+ }
var x struct {
before int64
i int64
@@ -91,6 +106,10 @@ func TestAddInt64(t *testing.T) {
}
func TestAddUint64(t *testing.T) {
+ if test64err != nil {
+ t.Logf("Skipping 64-bit tests: %v", test64err)
+ return
+ }
var x struct {
before uint64
i uint64
@@ -193,6 +212,10 @@ func TestCompareAndSwapUint32(t *testing.T) {
}
func TestCompareAndSwapInt64(t *testing.T) {
+ if test64err != nil {
+ t.Logf("Skipping 64-bit tests: %v", test64err)
+ return
+ }
var x struct {
before int64
i int64
@@ -222,6 +245,10 @@ func TestCompareAndSwapInt64(t *testing.T) {
}
func TestCompareAndSwapUint64(t *testing.T) {
+ if test64err != nil {
+ t.Logf("Skipping 64-bit tests: %v", test64err)
+ return
+ }
var x struct {
before uint64
i uint64
@@ -370,10 +397,11 @@ func hammerCompareAndSwapUintptr32(uval *uint32, count int) {
}
func TestHammer32(t *testing.T) {
- const (
- n = 100000
- p = 4
- )
+ const p = 4
+ n := 100000
+ if testing.Short() {
+ n = 1000
+ }
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(p))
for _, tt := range hammer32 {
@@ -391,7 +419,7 @@ func TestHammer32(t *testing.T) {
for i := 0; i < p; i++ {
<-c
}
- if val != n*p {
+ if val != uint32(n)*p {
t.Errorf("%s: val=%d want %d", tt.name, val, n*p)
}
}
@@ -478,10 +506,15 @@ func hammerCompareAndSwapUintptr64(uval *uint64, count int) {
}
func TestHammer64(t *testing.T) {
- const (
- n = 100000
- p = 4
- )
+ if test64err != nil {
+ t.Logf("Skipping 64-bit tests: %v", test64err)
+ return
+ }
+ const p = 4
+ n := 100000
+ if testing.Short() {
+ n = 1000
+ }
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(p))
for _, tt := range hammer64 {
@@ -499,7 +532,7 @@ func TestHammer64(t *testing.T) {
for i := 0; i < p; i++ {
<-c
}
- if val != n*p {
+ if val != uint64(n)*p {
t.Errorf("%s: val=%d want %d", tt.name, val, n*p)
}
}
diff --git a/src/pkg/sync/atomic/doc.go b/src/pkg/sync/atomic/doc.go
index 1335def59..ec5a0d33a 100644
--- a/src/pkg/sync/atomic/doc.go
+++ b/src/pkg/sync/atomic/doc.go
@@ -55,3 +55,8 @@ func AddUint64(val *uint64, delta uint64) (new uint64)
// AddUintptr atomically adds delta to *val and returns the new value.
func AddUintptr(val *uintptr, delta uintptr) (new uintptr)
+
+// Helper for ARM. Linker will discard on other systems
+func panic64() {
+ panic("sync/atomic: broken 64-bit atomic operations (buggy QEMU)")
+}
diff --git a/src/pkg/sync/rwmutex_test.go b/src/pkg/sync/rwmutex_test.go
index 405079270..9fb89f8e8 100644
--- a/src/pkg/sync/rwmutex_test.go
+++ b/src/pkg/sync/rwmutex_test.go
@@ -102,16 +102,20 @@ func HammerRWMutex(gomaxprocs, numReaders, num_iterations int) {
}
func TestRWMutex(t *testing.T) {
- HammerRWMutex(1, 1, 1000)
- HammerRWMutex(1, 3, 1000)
- HammerRWMutex(1, 10, 1000)
- HammerRWMutex(4, 1, 1000)
- HammerRWMutex(4, 3, 1000)
- HammerRWMutex(4, 10, 1000)
- HammerRWMutex(10, 1, 1000)
- HammerRWMutex(10, 3, 1000)
- HammerRWMutex(10, 10, 1000)
- HammerRWMutex(10, 5, 10000)
+ n := 1000
+ if testing.Short() {
+ n = 5
+ }
+ HammerRWMutex(1, 1, n)
+ HammerRWMutex(1, 3, n)
+ HammerRWMutex(1, 10, n)
+ HammerRWMutex(4, 1, n)
+ HammerRWMutex(4, 3, n)
+ HammerRWMutex(4, 10, n)
+ HammerRWMutex(10, 1, n)
+ HammerRWMutex(10, 3, n)
+ HammerRWMutex(10, 10, n)
+ HammerRWMutex(10, 5, n)
}
func TestRLocker(t *testing.T) {
diff --git a/src/pkg/sync/waitgroup.go b/src/pkg/sync/waitgroup.go
index 68e1d509f..05478c630 100644
--- a/src/pkg/sync/waitgroup.go
+++ b/src/pkg/sync/waitgroup.go
@@ -22,7 +22,7 @@ import "runtime"
// go func() {
// // Do something.
// wg.Done()
-// }
+// }()
// }
// wg.Wait()
//
diff --git a/src/pkg/syscall/Makefile b/src/pkg/syscall/Makefile
index 061b0056c..978bc94f8 100644
--- a/src/pkg/syscall/Makefile
+++ b/src/pkg/syscall/Makefile
@@ -32,6 +32,9 @@ GOFILES_linux=\
GOFILES_windows=\
exec_windows.go
+
+GOFILES_plan9=\
+ exec_plan9.go
OFILES=\
asm_$(GOOS)_$(GOARCH).$O\
diff --git a/src/pkg/syscall/asm_darwin_386.s b/src/pkg/syscall/asm_darwin_386.s
index fffda6e20..20cd809c7 100644
--- a/src/pkg/syscall/asm_darwin_386.s
+++ b/src/pkg/syscall/asm_darwin_386.s
@@ -61,6 +61,36 @@ ok6:
CALL runtime·exitsyscall(SB)
RET
+TEXT ·Syscall9(SB),7,$0
+ CALL runtime·entersyscall(SB)
+ MOVL 4(SP), AX // syscall entry
+ // slide args down on top of system call number
+ LEAL 8(SP), SI
+ LEAL 4(SP), DI
+ CLD
+ MOVSL
+ MOVSL
+ MOVSL
+ MOVSL
+ MOVSL
+ MOVSL
+ MOVSL
+ MOVSL
+ MOVSL
+ INT $0x80
+ JAE ok9
+ MOVL $-1, 44(SP) // r1
+ MOVL $-1, 48(SP) // r2
+ MOVL AX, 52(SP) // errno
+ CALL runtime·exitsyscall(SB)
+ RET
+ok9:
+ MOVL AX, 44(SP) // r1
+ MOVL DX, 48(SP) // r2
+ MOVL $0, 52(SP) // errno
+ CALL runtime·exitsyscall(SB)
+ RET
+
TEXT ·RawSyscall(SB),7,$0
MOVL 4(SP), AX // syscall entry
// slide args down on top of system call number
@@ -81,3 +111,27 @@ ok1:
MOVL DX, 24(SP) // r2
MOVL $0, 28(SP) // errno
RET
+
+TEXT ·RawSyscall6(SB),7,$0
+ MOVL 4(SP), AX // syscall entry
+ // slide args down on top of system call number
+ LEAL 8(SP), SI
+ LEAL 4(SP), DI
+ CLD
+ MOVSL
+ MOVSL
+ MOVSL
+ MOVSL
+ MOVSL
+ MOVSL
+ INT $0x80
+ JAE ok2
+ MOVL $-1, 32(SP) // r1
+ MOVL $-1, 36(SP) // r2
+ MOVL AX, 40(SP) // errno
+ RET
+ok2:
+ MOVL AX, 32(SP) // r1
+ MOVL DX, 36(SP) // r2
+ MOVL $0, 40(SP) // errno
+ RET
diff --git a/src/pkg/syscall/asm_darwin_amd64.s b/src/pkg/syscall/asm_darwin_amd64.s
index a9e2dfcca..1613622aa 100644
--- a/src/pkg/syscall/asm_darwin_amd64.s
+++ b/src/pkg/syscall/asm_darwin_amd64.s
@@ -78,3 +78,24 @@ ok1:
MOVQ DX, 48(SP) // r2
MOVQ $0, 56(SP) // errno
RET
+
+TEXT ·RawSyscall6(SB),7,$0
+ MOVQ 16(SP), DI
+ MOVQ 24(SP), SI
+ MOVQ 32(SP), DX
+ MOVQ 40(SP), R10
+ MOVQ 48(SP), R8
+ MOVQ 56(SP), R9
+ MOVQ 8(SP), AX // syscall entry
+ ADDQ $0x2000000, AX
+ SYSCALL
+ JCC ok2
+ MOVQ $-1, 64(SP) // r1
+ MOVQ $0, 72(SP) // r2
+ MOVQ AX, 80(SP) // errno
+ RET
+ok2:
+ MOVQ AX, 64(SP) // r1
+ MOVQ DX, 72(SP) // r2
+ MOVQ $0, 80(SP) // errno
+ RET
diff --git a/src/pkg/syscall/asm_freebsd_386.s b/src/pkg/syscall/asm_freebsd_386.s
index c652dcbd0..f2d4438a1 100644
--- a/src/pkg/syscall/asm_freebsd_386.s
+++ b/src/pkg/syscall/asm_freebsd_386.s
@@ -61,6 +61,36 @@ ok6:
CALL runtime·exitsyscall(SB)
RET
+TEXT ·Syscall9(SB),7,$0
+ CALL runtime·entersyscall(SB)
+ MOVL 4(SP), AX // syscall entry
+ // slide args down on top of system call number
+ LEAL 8(SP), SI
+ LEAL 4(SP), DI
+ CLD
+ MOVSL
+ MOVSL
+ MOVSL
+ MOVSL
+ MOVSL
+ MOVSL
+ MOVSL
+ MOVSL
+ MOVSL
+ INT $0x80
+ JAE ok9
+ MOVL $-1, 44(SP) // r1
+ MOVL $-1, 48(SP) // r2
+ MOVL AX, 52(SP) // errno
+ CALL runtime·exitsyscall(SB)
+ RET
+ok9:
+ MOVL AX, 44(SP) // r1
+ MOVL DX, 48(SP) // r2
+ MOVL $0, 52(SP) // errno
+ CALL runtime·exitsyscall(SB)
+ RET
+
TEXT ·RawSyscall(SB),7,$0
MOVL 4(SP), AX // syscall entry
// slide args down on top of system call number
@@ -81,3 +111,27 @@ ok1:
MOVL DX, 24(SP) // r2
MOVL $0, 28(SP) // errno
RET
+
+TEXT ·RawSyscall6(SB),7,$0
+ MOVL 4(SP), AX // syscall entry
+ // slide args down on top of system call number
+ LEAL 8(SP), SI
+ LEAL 4(SP), DI
+ CLD
+ MOVSL
+ MOVSL
+ MOVSL
+ MOVSL
+ MOVSL
+ MOVSL
+ INT $0x80
+ JAE ok2
+ MOVL $-1, 32(SP) // r1
+ MOVL $-1, 36(SP) // r2
+ MOVL AX, 40(SP) // errno
+ RET
+ok2:
+ MOVL AX, 32(SP) // r1
+ MOVL DX, 36(SP) // r2
+ MOVL $0, 40(SP) // errno
+ RET
diff --git a/src/pkg/syscall/asm_freebsd_amd64.s b/src/pkg/syscall/asm_freebsd_amd64.s
index d70620a4e..022db697a 100644
--- a/src/pkg/syscall/asm_freebsd_amd64.s
+++ b/src/pkg/syscall/asm_freebsd_amd64.s
@@ -75,3 +75,23 @@ ok1:
MOVQ DX, 48(SP) // r2
MOVQ $0, 56(SP) // errno
RET
+
+TEXT ·RawSyscall6(SB),7,$0
+ MOVQ 16(SP), DI
+ MOVQ 24(SP), SI
+ MOVQ 32(SP), DX
+ MOVQ 40(SP), R10
+ MOVQ 48(SP), R8
+ MOVQ 56(SP), R9
+ MOVQ 8(SP), AX // syscall entry
+ SYSCALL
+ JCC ok2
+ MOVQ $-1, 64(SP) // r1
+ MOVQ $0, 72(SP) // r2
+ MOVQ AX, 80(SP) // errno
+ RET
+ok2:
+ MOVQ AX, 64(SP) // r1
+ MOVQ DX, 72(SP) // r2
+ MOVQ $0, 80(SP) // errno
+ RET
diff --git a/src/pkg/syscall/asm_linux_386.s b/src/pkg/syscall/asm_linux_386.s
index 68b5baa65..82f170b5b 100644
--- a/src/pkg/syscall/asm_linux_386.s
+++ b/src/pkg/syscall/asm_linux_386.s
@@ -34,7 +34,6 @@ ok:
RET
// func Syscall6(trap uintptr, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr);
-// Actually Syscall5 but the rest of the code expects it to be named Syscall6.
TEXT ·Syscall6(SB),7,$0
CALL runtime·entersyscall(SB)
MOVL 4(SP), AX // syscall entry
@@ -43,7 +42,7 @@ TEXT ·Syscall6(SB),7,$0
MOVL 16(SP), DX
MOVL 20(SP), SI
MOVL 24(SP), DI
- // 28(SP) is ignored
+ MOVL 28(SP), BP
INT $0x80
CMPL AX, $0xfffff001
JLS ok6
@@ -82,6 +81,29 @@ ok1:
MOVL $0, 28(SP) // errno
RET
+// func RawSyscall6(trap uintptr, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr);
+TEXT ·RawSyscall6(SB),7,$0
+ MOVL 4(SP), AX // syscall entry
+ MOVL 8(SP), BX
+ MOVL 12(SP), CX
+ MOVL 16(SP), DX
+ MOVL 20(SP), SI
+ MOVL 24(SP), DI
+ MOVL 28(SP), BP
+ INT $0x80
+ CMPL AX, $0xfffff001
+ JLS ok2
+ MOVL $-1, 32(SP) // r1
+ MOVL $0, 36(SP) // r2
+ NEGL AX
+ MOVL AX, 40(SP) // errno
+ RET
+ok2:
+ MOVL AX, 32(SP) // r1
+ MOVL DX, 36(SP) // r2
+ MOVL $0, 40(SP) // errno
+ RET
+
#define SYS_SOCKETCALL 102 /* from zsysnum_linux_386.go */
// func socketcall(call int, a0, a1, a2, a3, a4, a5 uintptr) (n int, errno int)
@@ -108,6 +130,27 @@ oksock:
CALL runtime·exitsyscall(SB)
RET
+// func rawsocketcall(call int, a0, a1, a2, a3, a4, a5 uintptr) (n int, errno int)
+// Kernel interface gets call sub-number and pointer to a0.
+TEXT ·rawsocketcall(SB),7,$0
+ MOVL $SYS_SOCKETCALL, AX // syscall entry
+ MOVL 4(SP), BX // socket call number
+ LEAL 8(SP), CX // pointer to call arguments
+ MOVL $0, DX
+ MOVL $0, SI
+ MOVL $0, DI
+ INT $0x80
+ CMPL AX, $0xfffff001
+ JLS oksock1
+ MOVL $-1, 32(SP) // n
+ NEGL AX
+ MOVL AX, 36(SP) // errno
+ RET
+oksock1:
+ MOVL AX, 32(SP) // n
+ MOVL $0, 36(SP) // errno
+ RET
+
#define SYS__LLSEEK 140 /* from zsysnum_linux_386.go */
// func Seek(fd int, offset int64, whence int) (newoffset int64, errno int)
// Implemented in assembly to avoid allocation when
diff --git a/src/pkg/syscall/asm_linux_amd64.s b/src/pkg/syscall/asm_linux_amd64.s
index 20a5a4fb7..fdc233ca5 100644
--- a/src/pkg/syscall/asm_linux_amd64.s
+++ b/src/pkg/syscall/asm_linux_amd64.s
@@ -83,6 +83,28 @@ ok1:
MOVQ $0, 56(SP) // errno
RET
+TEXT ·RawSyscall6(SB),7,$0
+ MOVQ 16(SP), DI
+ MOVQ 24(SP), SI
+ MOVQ 32(SP), DX
+ MOVQ 40(SP), R10
+ MOVQ 48(SP), R8
+ MOVQ 56(SP), R9
+ MOVQ 8(SP), AX // syscall entry
+ SYSCALL
+ CMPQ AX, $0xfffffffffffff001
+ JLS ok2
+ MOVQ $-1, 64(SP) // r1
+ MOVQ $0, 72(SP) // r2
+ NEGQ AX
+ MOVQ AX, 80(SP) // errno
+ RET
+ok2:
+ MOVQ AX, 64(SP) // r1
+ MOVQ DX, 72(SP) // r2
+ MOVQ $0, 80(SP) // errno
+ RET
+
TEXT ·Gettimeofday(SB),7,$0
MOVQ 8(SP), DI
MOVQ $0, SI
diff --git a/src/pkg/syscall/asm_linux_arm.s b/src/pkg/syscall/asm_linux_arm.s
index 04dbdb624..2651b7284 100644
--- a/src/pkg/syscall/asm_linux_arm.s
+++ b/src/pkg/syscall/asm_linux_arm.s
@@ -67,6 +67,34 @@ ok6:
BL runtime·exitsyscall(SB)
RET
+// func RawSyscall6(trap uintptr, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr);
+// Actually RawSyscall5 but the rest of the code expects it to be named RawSyscall6.
+TEXT ·RawSyscall6(SB),7,$0
+ MOVW 4(SP), R7 // syscall entry
+ MOVW 8(SP), R0
+ MOVW 12(SP), R1
+ MOVW 16(SP), R2
+ MOVW 20(SP), R3
+ MOVW 24(SP), R4
+ MOVW 28(SP), R5
+ SWI $0
+ MOVW $0xfffff001, R6
+ CMP R6, R0
+ BLS ok2
+ MOVW $-1, R1
+ MOVW R1, 32(SP) // r1
+ MOVW $0, R2
+ MOVW R2, 36(SP) // r2
+ RSB $0, R0, R0
+ MOVW R0, 40(SP) // errno
+ RET
+ok2:
+ MOVW R0, 32(SP) // r1
+ MOVW R1, 36(SP) // r2
+ MOVW $0, R0
+ MOVW R0, 40(SP) // errno
+ RET
+
#define SYS__LLSEEK 140 /* from zsysnum_linux_arm.go */
// func Seek(fd int, offset int64, whence int) (newoffset int64, errno int)
// Implemented in assembly to avoid allocation when
diff --git a/src/pkg/syscall/asm_plan9_386.s b/src/pkg/syscall/asm_plan9_386.s
new file mode 100644
index 000000000..86ebedccc
--- /dev/null
+++ b/src/pkg/syscall/asm_plan9_386.s
@@ -0,0 +1,151 @@
+// 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.
+
+//
+// System call support for 386, Plan 9
+//
+
+//func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err string)
+//func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err string)
+//func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr)
+//func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
+
+// Trap # in AX, args on stack above caller pc.
+TEXT ·Syscall(SB),7,$0
+ CALL runtime·entersyscall(SB)
+ MOVL 4(SP), AX // syscall entry
+ // slide args down on top of system call number
+ LEAL 8(SP), SI
+ LEAL 4(SP), DI
+ CLD
+ MOVSL
+ MOVSL
+ MOVSL
+ INT $64
+ MOVL AX, r1+20(SP)
+ MOVL $0, r2+24(SP)
+ CMPL AX, $-1
+ JNE ok3
+
+ SUBL $8, SP
+ CALL syscall·errstr(SB)
+ MOVL SP, SI
+ ADDL $8, SP
+ JMP copyresult3
+
+ok3:
+ LEAL runtime·emptystring(SB), SI
+
+copyresult3:
+ LEAL err+28(SP), DI
+
+ CLD
+ MOVSL
+ MOVSL
+
+ CALL runtime·exitsyscall(SB)
+ RET
+
+TEXT ·Syscall6(SB),7,$0
+ CALL runtime·entersyscall(SB)
+ MOVL 4(SP), AX // syscall entry
+ // slide args down on top of system call number
+ LEAL 8(SP), SI
+ LEAL 4(SP), DI
+ CLD
+ MOVSL
+ MOVSL
+ MOVSL
+ MOVSL
+ MOVSL
+ MOVSL
+ INT $64
+ MOVL AX, r1+32(SP)
+ MOVL $0, r2+36(SP)
+ CMPL AX, $-1
+ JNE ok4
+
+ SUBL $8, SP
+ CALL syscall·errstr(SB)
+ MOVL SP, SI
+ ADDL $8, SP
+ JMP copyresult4
+
+ok4:
+ LEAL runtime·emptystring(SB), SI
+
+copyresult4:
+ LEAL err+40(SP), DI
+
+ CLD
+ MOVSL
+ MOVSL
+
+ CALL runtime·exitsyscall(SB)
+ RET
+
+TEXT ·RawSyscall(SB),7,$0
+ MOVL 4(SP), AX // syscall entry
+ // slide args down on top of system call number
+ LEAL 8(SP), SI
+ LEAL 4(SP), DI
+ CLD
+ MOVSL
+ MOVSL
+ MOVSL
+ INT $64
+ MOVL AX, r1+20(SP)
+ MOVL AX, r2+24(SP)
+ MOVL AX, err+28(SP)
+ RET
+
+TEXT ·RawSyscall6(SB),7,$0
+ MOVL 4(SP), AX // syscall entry
+ // slide args down on top of system call number
+ LEAL 8(SP), SI
+ LEAL 4(SP), DI
+ CLD
+ MOVSL
+ MOVSL
+ MOVSL
+ MOVSL
+ MOVSL
+ MOVSL
+ INT $64
+ MOVL AX, r1+32(SP)
+ MOVL AX, r2+36(SP)
+ MOVL AX, err+40(SP)
+ RET
+
+#define SYS_SEEK 39 /* from zsysnum_plan9_386.go */
+
+//func seek(placeholder uintptr, fd int, offset int64, whence int) (newoffset int64, err string)
+TEXT ·seek(SB),7,$0
+ LEAL newoffset+24(SP), AX
+ MOVL AX, placeholder+4(SP)
+
+ MOVL $SYS_SEEK, AX // syscall entry
+ INT $64
+
+ CMPL AX, $-1
+ JNE ok6
+ MOVL AX, 24(SP) // newoffset low
+ MOVL AX, 28(SP) // newoffset high
+
+ SUBL $8, SP
+ CALL syscall·errstr(SB)
+ MOVL SP, SI
+ ADDL $8, SP
+ JMP copyresult6
+
+ok6:
+ LEAL runtime·emptystring(SB), SI
+
+copyresult6:
+ LEAL err+32(SP), DI
+
+ CLD
+ MOVSL
+ MOVSL
+ RET
diff --git a/src/pkg/syscall/exec_plan9.go b/src/pkg/syscall/exec_plan9.go
new file mode 100644
index 000000000..962b39b78
--- /dev/null
+++ b/src/pkg/syscall/exec_plan9.go
@@ -0,0 +1,521 @@
+// 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.
+
+// Fork, exec, wait, etc.
+
+package syscall
+
+import (
+ "sync"
+ "unsafe"
+)
+
+// Lock synchronizing creation of new file descriptors with fork.
+//
+// We want the child in a fork/exec sequence to inherit only the
+// file descriptors we intend. To do that, we mark all file
+// descriptors close-on-exec and then, in the child, explicitly
+// unmark the ones we want the exec'ed program to keep.
+// Unix doesn't make this easy: there is, in general, no way to
+// allocate a new file descriptor close-on-exec. Instead you
+// have to allocate the descriptor and then mark it close-on-exec.
+// If a fork happens between those two events, the child's exec
+// will inherit an unwanted file descriptor.
+//
+// This lock solves that race: the create new fd/mark close-on-exec
+// operation is done holding ForkLock for reading, and the fork itself
+// is done holding ForkLock for writing. At least, that's the idea.
+// There are some complications.
+//
+// Some system calls that create new file descriptors can block
+// for arbitrarily long times: open on a hung NFS server or named
+// pipe, accept on a socket, and so on. We can't reasonably grab
+// the lock across those operations.
+//
+// It is worse to inherit some file descriptors than others.
+// If a non-malicious child accidentally inherits an open ordinary file,
+// that's not a big deal. On the other hand, if a long-lived child
+// accidentally inherits the write end of a pipe, then the reader
+// of that pipe will not see EOF until that child exits, potentially
+// causing the parent program to hang. This is a common problem
+// in threaded C programs that use popen.
+//
+// Luckily, the file descriptors that are most important not to
+// inherit are not the ones that can take an arbitrarily long time
+// to create: pipe returns instantly, and the net package uses
+// non-blocking I/O to accept on a listening socket.
+// The rules for which file descriptor-creating operations use the
+// ForkLock are as follows:
+//
+// 1) Pipe. Does not block. Use the ForkLock.
+// 2) Socket. Does not block. Use the ForkLock.
+// 3) Accept. If using non-blocking mode, use the ForkLock.
+// Otherwise, live with the race.
+// 4) Open. Can block. Use O_CLOEXEC if available (Linux).
+// Otherwise, live with the race.
+// 5) Dup. Does not block. Use the ForkLock.
+// On Linux, could use fcntl F_DUPFD_CLOEXEC
+// instead of the ForkLock, but only for dup(fd, -1).
+
+var ForkLock sync.RWMutex
+
+// Convert array of string to array
+// of NUL-terminated byte pointer.
+func StringArrayPtr(ss []string) []*byte {
+ bb := make([]*byte, len(ss)+1)
+ for i := 0; i < len(ss); i++ {
+ bb[i] = StringBytePtr(ss[i])
+ }
+ bb[len(ss)] = nil
+ return bb
+}
+
+// gbit16 reads a 16-bit numeric value from a 9P protocol message strored in b,
+// returning the value and the remaining slice of b.
+func gbit16(b []byte) (uint16, []byte) {
+ return uint16(b[0]) | uint16(b[1])<<8, b[2:]
+}
+
+// gstring reads a string from a 9P protocol message strored in b,
+// returning the value as a Go string and the remaining slice of b.
+func gstring(b []byte) (string, []byte) {
+ n, b := gbit16(b)
+ return string(b[0:n]), b[n:]
+}
+
+// readdirnames returns the names of files inside the directory represented by dirfd.
+func readdirnames(dirfd int) (names []string, err Error) {
+ result := make([]string, 0, 100)
+ var buf [STATMAX]byte
+
+ for {
+ n, e := Read(dirfd, buf[:])
+ if e != nil {
+ return []string{}, e
+ }
+ if n == 0 {
+ break
+ }
+
+ for i := 0; i < n; {
+ m, _ := gbit16(buf[i:])
+ m += 2
+
+ if m < STATFIXLEN {
+ return []string{}, NewError("malformed stat buffer")
+ }
+
+ name, _ := gstring(buf[i+41:])
+ result = append(result, name)
+
+ i += int(m)
+ }
+ }
+ return []string{}, nil
+}
+
+// readdupdevice returns a list of currently opened fds (excluding stdin, stdout, stderr) from the dup device #d.
+// ForkLock should be write locked before calling, so that no new fds would be created while the fd list is being read.
+func readdupdevice() (fds []int, err Error) {
+ dupdevfd, err := Open("#d", O_RDONLY)
+
+ if err != nil {
+ return
+ }
+ defer Close(dupdevfd)
+
+ fileNames, err := readdirnames(dupdevfd)
+ if err != nil {
+ return
+ }
+
+ fds = make([]int, 0, len(fileNames)>>1)
+ for _, fdstr := range fileNames {
+ if l := len(fdstr); l > 2 && fdstr[l-3] == 'c' && fdstr[l-2] == 't' && fdstr[l-1] == 'l' {
+ continue
+ }
+
+ fd := int(atoi([]byte(fdstr)))
+
+ if fd == 0 || fd == 1 || fd == 2 || fd == dupdevfd {
+ continue
+ }
+
+ fds = append(fds, fd)
+ }
+
+ return fds[0:len(fds)], nil
+}
+
+var startupFds []int
+
+// Plan 9 does not allow clearing the OCEXEC flag
+// from the underlying channel backing an open file descriptor,
+// therefore we store a list of already opened file descriptors
+// inside startupFds and skip them when manually closing descriptors
+// not meant to be passed to a child exec.
+func init() {
+ startupFds, _ = readdupdevice()
+}
+
+// forkAndExecInChild forks the process, calling dup onto 0..len(fd)
+// and finally invoking exec(argv0, argvv, envv) in the child.
+// If a dup or exec fails, it writes the error string to pipe.
+// (The pipe write end is close-on-exec so if exec succeeds, it will be closed.)
+//
+// In the child, this function must not acquire any locks, because
+// they might have been locked at the time of the fork. This means
+// no rescheduling, no malloc calls, and no new stack segments.
+// The calls to RawSyscall are okay because they are assembly
+// functions that do not grow the stack.
+func forkAndExecInChild(argv0 *byte, argv []*byte, envv []envItem, chroot, dir *byte, attr *ProcAttr, fdsToClose []int, pipe int) (pid int, err Error) {
+ // Declare all variables at top in case any
+ // declarations require heap allocation (e.g., errbuf).
+ var (
+ r1 uintptr
+ nextfd int
+ i int
+ clearenv int
+ envfd int
+ errbuf [ERRMAX]byte
+ )
+
+ // guard against side effects of shuffling fds below.
+ fd := append([]int(nil), attr.Files...)
+
+ if envv != nil {
+ clearenv = RFCENVG
+ }
+
+ // About to call fork.
+ // No more allocation or calls of non-assembly functions.
+ r1, _, _ = RawSyscall(SYS_RFORK, uintptr(RFPROC|RFFDG|RFREND|clearenv), 0, 0)
+
+ if r1 != 0 {
+ if int(r1) == -1 {
+ return 0, NewError(errstr())
+ }
+ // parent; return PID
+ return int(r1), nil
+ }
+
+ // Fork succeeded, now in child.
+
+ // Close fds we don't need.
+ for i = 0; i < len(fdsToClose); i++ {
+ r1, _, _ = RawSyscall(SYS_CLOSE, uintptr(fdsToClose[i]), 0, 0)
+ if int(r1) == -1 {
+ goto childerror
+ }
+ }
+
+ if envv != nil {
+ // Write new environment variables.
+ for i = 0; i < len(envv); i++ {
+ r1, _, _ = RawSyscall(SYS_CREATE, uintptr(unsafe.Pointer(envv[i].name)), uintptr(O_WRONLY), uintptr(0666))
+
+ if int(r1) == -1 {
+ goto childerror
+ }
+
+ envfd = int(r1)
+
+ r1, _, _ = RawSyscall6(SYS_PWRITE, uintptr(envfd), uintptr(unsafe.Pointer(envv[i].value)), uintptr(envv[i].nvalue),
+ ^uintptr(0), ^uintptr(0), 0)
+
+ if int(r1) == -1 || int(r1) != envv[i].nvalue {
+ goto childerror
+ }
+
+ r1, _, _ = RawSyscall(SYS_CLOSE, uintptr(envfd), 0, 0)
+
+ if int(r1) == -1 {
+ goto childerror
+ }
+ }
+ }
+
+ // Chdir
+ if dir != nil {
+ r1, _, _ = RawSyscall(SYS_CHDIR, uintptr(unsafe.Pointer(dir)), 0, 0)
+ if int(r1) == -1 {
+ goto childerror
+ }
+ }
+
+ // Pass 1: look for fd[i] < i and move those up above len(fd)
+ // so that pass 2 won't stomp on an fd it needs later.
+ nextfd = int(len(fd))
+ if pipe < nextfd {
+ r1, _, _ = RawSyscall(SYS_DUP, uintptr(pipe), uintptr(nextfd), 0)
+ if int(r1) == -1 {
+ goto childerror
+ }
+ pipe = nextfd
+ nextfd++
+ }
+ for i = 0; i < len(fd); i++ {
+ if fd[i] >= 0 && fd[i] < int(i) {
+ r1, _, _ = RawSyscall(SYS_DUP, uintptr(fd[i]), uintptr(nextfd), 0)
+ if int(r1) == -1 {
+ goto childerror
+ }
+
+ fd[i] = nextfd
+ nextfd++
+ if nextfd == pipe { // don't stomp on pipe
+ nextfd++
+ }
+ }
+ }
+
+ // Pass 2: dup fd[i] down onto i.
+ for i = 0; i < len(fd); i++ {
+ if fd[i] == -1 {
+ RawSyscall(SYS_CLOSE, uintptr(i), 0, 0)
+ continue
+ }
+ if fd[i] == int(i) {
+ continue
+ }
+
+ r1, _, _ = RawSyscall(SYS_DUP, uintptr(fd[i]), uintptr(i), 0)
+ if int(r1) == -1 {
+ goto childerror
+ }
+ RawSyscall(SYS_CLOSE, uintptr(fd[i]), 0, 0)
+ }
+
+ // Time to exec.
+ r1, _, _ = RawSyscall(SYS_EXEC,
+ uintptr(unsafe.Pointer(argv0)),
+ uintptr(unsafe.Pointer(&argv[0])), 0)
+
+childerror:
+ // send error string on pipe
+ RawSyscall(SYS_ERRSTR, uintptr(unsafe.Pointer(&errbuf[0])), uintptr(len(errbuf)), 0)
+ errbuf[len(errbuf)-1] = 0
+ i = 0
+ for i < len(errbuf) && errbuf[i] != 0 {
+ i++
+ }
+
+ RawSyscall6(SYS_PWRITE, uintptr(pipe), uintptr(unsafe.Pointer(&errbuf[0])), uintptr(i),
+ ^uintptr(0), ^uintptr(0), 0)
+
+ for {
+ RawSyscall(SYS_EXITS, 0, 0, 0)
+ }
+
+ // Calling panic is not actually safe,
+ // but the for loop above won't break
+ // and this shuts up the compiler.
+ panic("unreached")
+}
+
+func cexecPipe(p []int) Error {
+ e := Pipe(p)
+ if e != nil {
+ return e
+ }
+
+ fd, e := Open("#d/"+itoa(p[1]), O_CLOEXEC)
+ if e != nil {
+ Close(p[0])
+ Close(p[1])
+ return e
+ }
+
+ Close(fd)
+ return nil
+}
+
+type envItem struct {
+ name *byte
+ value *byte
+ nvalue int
+}
+
+type ProcAttr struct {
+ Dir string // Current working directory.
+ Env []string // Environment.
+ Files []int // File descriptors.
+ Chroot string // Chroot.
+}
+
+var zeroAttributes ProcAttr
+
+
+func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err Error) {
+ var (
+ p [2]int
+ n int
+ errbuf [ERRMAX]byte
+ wmsg Waitmsg
+ )
+
+ if attr == nil {
+ attr = &zeroAttributes
+ }
+
+ p[0] = -1
+ p[1] = -1
+
+ // Convert args to C form.
+ argv0p := StringBytePtr(argv0)
+ argvp := StringArrayPtr(argv)
+
+ var chroot *byte
+ if attr.Chroot != "" {
+ chroot = StringBytePtr(attr.Chroot)
+ }
+ var dir *byte
+ if attr.Dir != "" {
+ dir = StringBytePtr(attr.Dir)
+ }
+ var envvParsed []envItem
+ if attr.Env != nil {
+ envvParsed = make([]envItem, 0, len(attr.Env))
+ for _, v := range attr.Env {
+ i := 0
+ for i < len(v) && v[i] != '=' {
+ i++
+ }
+
+ envvParsed = append(envvParsed, envItem{StringBytePtr("/env/" + v[:i]), StringBytePtr(v[i+1:]), len(v) - i})
+ }
+ }
+
+ // Acquire the fork lock to prevent other threads from creating new fds before we fork.
+ ForkLock.Lock()
+
+ // get a list of open fds, excluding stdin,stdout and stderr that need to be closed in the child.
+ // no new fds can be created while we hold the ForkLock for writing.
+ openFds, e := readdupdevice()
+
+ if e != nil {
+ ForkLock.Unlock()
+ return 0, e
+ }
+
+ fdsToClose := make([]int, 0, len(openFds))
+ // exclude fds opened from startup from the list of fds to be closed.
+ for _, fd := range openFds {
+ isReserved := false
+ for _, reservedFd := range startupFds {
+ if fd == reservedFd {
+ isReserved = true
+ break
+ }
+ }
+
+ if !isReserved {
+ fdsToClose = append(fdsToClose, fd)
+ }
+ }
+
+ // exclude fds requested by the caller from the list of fds to be closed.
+ for _, fd := range openFds {
+ isReserved := false
+ for _, reservedFd := range attr.Files {
+ if fd == reservedFd {
+ isReserved = true
+ break
+ }
+ }
+
+ if !isReserved {
+ fdsToClose = append(fdsToClose, fd)
+ }
+ }
+
+ // Allocate child status pipe close on exec.
+ e = cexecPipe(p[:])
+
+ if e != nil {
+ return 0, e
+ }
+ fdsToClose = append(fdsToClose, p[0])
+
+ // Kick off child.
+ pid, err = forkAndExecInChild(argv0p, argvp, envvParsed, chroot, dir, attr, fdsToClose, p[1])
+
+ if err != nil {
+ if p[0] >= 0 {
+ Close(p[0])
+ Close(p[1])
+ }
+ ForkLock.Unlock()
+ return 0, err
+ }
+ ForkLock.Unlock()
+
+ // Read child error status from pipe.
+ Close(p[1])
+ n, err = Read(p[0], errbuf[:])
+ Close(p[0])
+
+ if err != nil || n != 0 {
+ if n != 0 {
+ err = NewError(string(errbuf[:]))
+ }
+
+ // Child failed; wait for it to exit, to make sure
+ // the zombies don't accumulate.
+ for wmsg.Pid != pid {
+ Await(&wmsg)
+ }
+ return 0, err
+ }
+
+ // Read got EOF, so pipe closed on exec, so exec succeeded.
+ return pid, nil
+}
+
+// Combination of fork and exec, careful to be thread safe.
+func ForkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err Error) {
+ return forkExec(argv0, argv, attr)
+}
+
+// StartProcess wraps ForkExec for package os.
+func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid, handle int, err Error) {
+ pid, err = forkExec(argv0, argv, attr)
+ return pid, 0, err
+}
+
+// Ordinary exec.
+func Exec(argv0 string, argv []string, envv []string) (err Error) {
+ if envv != nil {
+ r1, _, _ := RawSyscall(SYS_RFORK, RFCENVG, 0, 0)
+ if int(r1) == -1 {
+ return NewError(errstr())
+ }
+
+ for _, v := range envv {
+ i := 0
+ for i < len(v) && v[i] != '=' {
+ i++
+ }
+
+ fd, e := Create("/env/"+v[:i], O_WRONLY, 0666)
+ if e != nil {
+ return e
+ }
+
+ _, e = Write(fd, []byte(v[i+1:]))
+ if e != nil {
+ Close(fd)
+ return e
+ }
+ Close(fd)
+ }
+ }
+
+ _, _, e := Syscall(SYS_EXEC,
+ uintptr(unsafe.Pointer(StringBytePtr(argv0))),
+ uintptr(unsafe.Pointer(&StringArrayPtr(argv)[0])),
+ 0)
+
+ return NewError(e)
+}
diff --git a/src/pkg/syscall/exec_unix.go b/src/pkg/syscall/exec_unix.go
index 2e09539ee..b6cb1baa2 100644
--- a/src/pkg/syscall/exec_unix.go
+++ b/src/pkg/syscall/exec_unix.go
@@ -96,13 +96,16 @@ func SetNonblock(fd int, nonblocking bool) (errno int) {
// no rescheduling, no malloc calls, and no new stack segments.
// The calls to RawSyscall are okay because they are assembly
// functions that do not grow the stack.
-func forkAndExecInChild(argv0 *byte, argv []*byte, envv []*byte, traceme bool, dir *byte, fd []int, pipe int) (pid int, err int) {
+func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, pipe int) (pid int, err int) {
// Declare all variables at top in case any
// declarations require heap allocation (e.g., err1).
var r1, r2, err1 uintptr
var nextfd int
var i int
+ // guard against side effects of shuffling fds below.
+ fd := append([]int(nil), attr.Files...)
+
darwin := OS == "darwin"
// About to call fork.
@@ -128,13 +131,50 @@ func forkAndExecInChild(argv0 *byte, argv []*byte, envv []*byte, traceme bool, d
// Fork succeeded, now in child.
// Enable tracing if requested.
- if traceme {
+ if attr.Ptrace {
_, _, err1 = RawSyscall(SYS_PTRACE, uintptr(PTRACE_TRACEME), 0, 0)
if err1 != 0 {
goto childerror
}
}
+ // Session ID
+ if attr.Setsid {
+ _, _, err1 = RawSyscall(SYS_SETSID, 0, 0, 0)
+ if err1 != 0 {
+ goto childerror
+ }
+ }
+
+ // Chroot
+ if chroot != nil {
+ _, _, err1 = RawSyscall(SYS_CHROOT, uintptr(unsafe.Pointer(chroot)), 0, 0)
+ if err1 != 0 {
+ goto childerror
+ }
+ }
+
+ // User and groups
+ if attr.Credential != nil {
+ ngroups := uintptr(len(attr.Credential.Groups))
+ groups := uintptr(0)
+ if ngroups > 0 {
+ groups = uintptr(unsafe.Pointer(&attr.Credential.Groups[0]))
+ }
+ _, _, err1 = RawSyscall(SYS_SETGROUPS, ngroups, groups, 0)
+ if err1 != 0 {
+ goto childerror
+ }
+ _, _, err1 = RawSyscall(SYS_SETGID, uintptr(attr.Credential.Gid), 0, 0)
+ if err1 != 0 {
+ goto childerror
+ }
+ _, _, err1 = RawSyscall(SYS_SETUID, uintptr(attr.Credential.Uid), 0, 0)
+ if err1 != 0 {
+ goto childerror
+ }
+ }
+
// Chdir
if dir != nil {
_, _, err1 = RawSyscall(SYS_CHDIR, uintptr(unsafe.Pointer(dir)), 0, 0)
@@ -220,28 +260,55 @@ childerror:
panic("unreached")
}
-func forkExec(argv0 string, argv []string, envv []string, traceme bool, dir string, fd []int) (pid int, err int) {
+type Credential struct {
+ Uid uint32 // User ID.
+ Gid uint32 // Group ID.
+ Groups []uint32 // Supplementary group IDs.
+}
+
+type ProcAttr struct {
+ Setsid bool // Create session.
+ Ptrace bool // Enable tracing.
+ Dir string // Current working directory.
+ Env []string // Environment.
+ Files []int // File descriptors.
+ Chroot string // Chroot.
+ Credential *Credential // Credential.
+}
+
+var zeroAttributes ProcAttr
+
+func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err int) {
var p [2]int
var n int
var err1 uintptr
var wstatus WaitStatus
+ if attr == nil {
+ attr = &zeroAttributes
+ }
+
p[0] = -1
p[1] = -1
// Convert args to C form.
argv0p := StringBytePtr(argv0)
argvp := StringArrayPtr(argv)
- envvp := StringArrayPtr(envv)
- var dirp *byte
- if len(dir) > 0 {
- dirp = StringBytePtr(dir)
- }
+ envvp := StringArrayPtr(attr.Env)
if OS == "freebsd" && len(argv[0]) > len(argv0) {
argvp[0] = argv0p
}
+ var chroot *byte
+ if attr.Chroot != "" {
+ chroot = StringBytePtr(attr.Chroot)
+ }
+ var dir *byte
+ if attr.Dir != "" {
+ dir = StringBytePtr(attr.Dir)
+ }
+
// Acquire the fork lock so that no other threads
// create new fds that are not yet close-on-exec
// before we fork.
@@ -259,7 +326,7 @@ func forkExec(argv0 string, argv []string, envv []string, traceme bool, dir stri
}
// Kick off child.
- pid, err = forkAndExecInChild(argv0p, argvp, envvp, traceme, dirp, fd, p[1])
+ pid, err = forkAndExecInChild(argv0p, argvp, envvp, chroot, dir, attr, p[1])
if err != 0 {
error:
if p[0] >= 0 {
@@ -297,13 +364,14 @@ func forkExec(argv0 string, argv []string, envv []string, traceme bool, dir stri
}
// Combination of fork and exec, careful to be thread safe.
-func ForkExec(argv0 string, argv []string, envv []string, dir string, fd []int) (pid int, err int) {
- return forkExec(argv0, argv, envv, false, dir, fd)
+func ForkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err int) {
+ return forkExec(argv0, argv, attr)
}
-// PtraceForkExec is like ForkExec, but starts the child in a traced state.
-func PtraceForkExec(argv0 string, argv []string, envv []string, dir string, fd []int) (pid int, err int) {
- return forkExec(argv0, argv, envv, true, dir, fd)
+// StartProcess wraps ForkExec for package os.
+func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid, handle int, err int) {
+ pid, err = forkExec(argv0, argv, attr)
+ return pid, 0, err
}
// Ordinary exec.
@@ -314,9 +382,3 @@ func Exec(argv0 string, argv []string, envv []string) (err int) {
uintptr(unsafe.Pointer(&StringArrayPtr(envv)[0])))
return int(err1)
}
-
-// StartProcess wraps ForkExec for package os.
-func StartProcess(argv0 string, argv []string, envv []string, dir string, fd []int) (pid, handle int, err int) {
- pid, err = forkExec(argv0, argv, envv, false, dir, fd)
- return pid, 0, err
-}
diff --git a/src/pkg/syscall/exec_windows.go b/src/pkg/syscall/exec_windows.go
index 73c3c8624..aeee191dd 100644
--- a/src/pkg/syscall/exec_windows.go
+++ b/src/pkg/syscall/exec_windows.go
@@ -11,48 +11,92 @@ import (
"utf16"
)
-// Windows doesn't have a good concept of just Exec in the documented API.
-// However, the kernel32 CreateProcess does a good job with
-// ForkExec.
-
var ForkLock sync.RWMutex
-// Joins an array of string with sep
-// From the "strings" package. Modified.
-func stringJoin(a []string, sep string, escape escapeFunc) string {
- if len(a) == 0 {
- return ""
+// escape rewrites command line argument s as prescribed
+// in http://msdn.microsoft.com/en-us/library/ms880421.
+// This function returns "" (2 double quotes) if s is empty.
+// Alternatively, these transformations are done:
+// - every back slash (\) is doubled, but only if immediately
+// followed by double quote (");
+// - every double quote (") is escaped by back slash (\);
+// - finally, s is wrapped with double quotes (arg -> "arg"),
+// but only if there is space or tab inside s.
+func escape(s string) string {
+ if len(s) == 0 {
+ return "\"\""
}
- if len(a) == 1 {
- return a[0]
+ n := len(s)
+ hasSpace := false
+ for i := 0; i < len(s); i++ {
+ switch s[i] {
+ case '"', '\\':
+ n++
+ case ' ', '\t':
+ hasSpace = true
+ }
}
- n := len(sep) * (len(a) - 1)
- for i := 0; i < len(a); i++ {
- a[i] = escape(a[i])
- n += len(a[i])
+ if hasSpace {
+ n += 2
+ }
+ if n == len(s) {
+ return s
}
- b := make([]byte, n)
- bp := 0
- for i := 0; i < len(a); i++ {
- s := a[i]
- for j := 0; j < len(s); j++ {
- b[bp] = s[j]
- bp++
- }
- if i+1 < len(a) {
- s = sep
- for j := 0; j < len(s); j++ {
- b[bp] = s[j]
- bp++
+ qs := make([]byte, n)
+ j := 0
+ if hasSpace {
+ qs[j] = '"'
+ j++
+ }
+ slashes := 0
+ for i := 0; i < len(s); i++ {
+ switch s[i] {
+ default:
+ slashes = 0
+ qs[j] = s[i]
+ case '\\':
+ slashes++
+ qs[j] = s[i]
+ case '"':
+ for ; slashes > 0; slashes-- {
+ qs[j] = '\\'
+ j++
}
+ qs[j] = '\\'
+ j++
+ qs[j] = s[i]
}
+ j++
}
- return string(b)
+ if hasSpace {
+ for ; slashes > 0; slashes-- {
+ qs[j] = '\\'
+ j++
+ }
+ qs[j] = '"'
+ j++
+ }
+ return string(qs[:j])
+}
+
+// makeCmdLine builds a command line out of args by escaping "special"
+// characters and joining the arguments with spaces.
+func makeCmdLine(args []string) string {
+ var s string
+ for _, v := range args {
+ if s != "" {
+ s += " "
+ }
+ s += escape(v)
+ }
+ return s
}
-//Env block is a sequence of null terminated strings followed by a null.
-//Last bytes are two unicode nulls, or four null bytes.
+// createEnvBlock converts an array of environment strings into
+// the representation required by CreateProcess: a sequence of NUL
+// terminated strings followed by a nil.
+// Last bytes are two UCS-2 NULs, or four NUL bytes.
func createEnvBlock(envv []string) *uint16 {
if len(envv) == 0 {
return &utf16.Encode([]int("\x00\x00"))[0]
@@ -76,69 +120,146 @@ func createEnvBlock(envv []string) *uint16 {
return &utf16.Encode([]int(string(b)))[0]
}
-type escapeFunc func(s string) string
-
-//escapes quotes by " -> ""
-//Also string -> "string"
-func escapeAddQuotes(s string) string {
- //normal ascii char, one byte wide
- rune := byte('"')
- l := len(s)
- n := 0
- for i := 0; i < l; i++ {
- if s[i] == rune {
- n++
+func CloseOnExec(fd int) {
+ SetHandleInformation(int32(fd), HANDLE_FLAG_INHERIT, 0)
+}
+
+func SetNonblock(fd int, nonblocking bool) (errno int) {
+ return 0
+}
+
+// getFullPath retrieves the full path of the specified file.
+// Just a wrapper for Windows GetFullPathName api.
+func getFullPath(name string) (path string, err int) {
+ p := StringToUTF16Ptr(name)
+ buf := make([]uint16, 100)
+ n, err := GetFullPathName(p, uint32(len(buf)), &buf[0], nil)
+ if err != 0 {
+ return "", err
+ }
+ if n > uint32(len(buf)) {
+ // Windows is asking for bigger buffer.
+ buf = make([]uint16, n)
+ n, err = GetFullPathName(p, uint32(len(buf)), &buf[0], nil)
+ if err != 0 {
+ return "", err
+ }
+ if n > uint32(len(buf)) {
+ return "", EINVAL
}
}
- qs := make([]byte, l+n+2)
+ return UTF16ToString(buf[:n]), 0
+}
- qs[0] = rune
- j := 1
- for i := 0; i < l; i++ {
- qs[i+j] = s[i]
- if s[i] == rune {
- j++
- qs[i+j] = rune
- }
+func isSlash(c uint8) bool {
+ return c == '\\' || c == '/'
+}
+
+func normalizeDir(dir string) (name string, err int) {
+ ndir, err := getFullPath(dir)
+ if err != 0 {
+ return "", err
+ }
+ if len(ndir) > 2 && isSlash(ndir[0]) && isSlash(ndir[1]) {
+ // dir cannot have \\server\share\path form
+ return "", EINVAL
}
- qs[len(qs)-1] = rune
- return string(qs)
+ return ndir, 0
}
+func volToUpper(ch int) int {
+ if 'a' <= ch && ch <= 'z' {
+ ch += 'A' - 'a'
+ }
+ return ch
+}
-func CloseOnExec(fd int) {
- SetHandleInformation(int32(fd), HANDLE_FLAG_INHERIT, 0)
+func joinExeDirAndFName(dir, p string) (name string, err int) {
+ if len(p) == 0 {
+ return "", EINVAL
+ }
+ if len(p) > 2 && isSlash(p[0]) && isSlash(p[1]) {
+ // \\server\share\path form
+ return p, 0
+ }
+ if len(p) > 1 && p[1] == ':' {
+ // has drive letter
+ if len(p) == 2 {
+ return "", EINVAL
+ }
+ if isSlash(p[2]) {
+ return p, 0
+ } else {
+ d, err := normalizeDir(dir)
+ if err != 0 {
+ return "", err
+ }
+ if volToUpper(int(p[0])) == volToUpper(int(d[0])) {
+ return getFullPath(d + "\\" + p[2:])
+ } else {
+ return getFullPath(p)
+ }
+ }
+ } else {
+ // no drive letter
+ d, err := normalizeDir(dir)
+ if err != 0 {
+ return "", err
+ }
+ if isSlash(p[0]) {
+ return getFullPath(d[:2] + p)
+ } else {
+ return getFullPath(d + "\\" + p)
+ }
+ }
+ // we shouldn't be here
+ return "", EINVAL
}
-func SetNonblock(fd int, nonblocking bool) (errno int) {
- return 0
+type ProcAttr struct {
+ Dir string
+ Env []string
+ Files []int
}
+var zeroAttributes ProcAttr
-// TODO(kardia): Add trace
-//The command and arguments are passed via the Command line parameter.
-func StartProcess(argv0 string, argv []string, envv []string, dir string, fd []int) (pid, handle int, err int) {
- if len(fd) > 3 {
+func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid, handle int, err int) {
+ if len(argv0) == 0 {
+ return 0, 0, EWINDOWS
+ }
+ if attr == nil {
+ attr = &zeroAttributes
+ }
+ if len(attr.Files) > 3 {
return 0, 0, EWINDOWS
}
- //CreateProcess will throw an error if the dir is not set to a valid dir
- // thus get the working dir if dir is empty.
- if len(dir) == 0 {
- if wd, ok := Getwd(); ok == 0 {
- dir = wd
+ if len(attr.Dir) != 0 {
+ // StartProcess assumes that argv0 is relative to attr.Dir,
+ // because it implies Chdir(attr.Dir) before executing argv0.
+ // Windows CreateProcess assumes the opposite: it looks for
+ // argv0 relative to the current directory, and, only once the new
+ // process is started, it does Chdir(attr.Dir). We are adjusting
+ // for that difference here by making argv0 absolute.
+ var err int
+ argv0, err = joinExeDirAndFName(attr.Dir, argv0)
+ if err != 0 {
+ return 0, 0, err
}
}
+ argv0p := StringToUTF16Ptr(argv0)
- startupInfo := new(StartupInfo)
- processInfo := new(ProcessInformation)
-
- GetStartupInfo(startupInfo)
+ var argvp *uint16
+ s := makeCmdLine(argv)
+ if len(s) != 0 {
+ argvp = StringToUTF16Ptr(s)
+ }
- startupInfo.Flags = STARTF_USESTDHANDLES
- startupInfo.StdInput = 0
- startupInfo.StdOutput = 0
- startupInfo.StdErr = 0
+ var dirp *uint16
+ if len(attr.Dir) != 0 {
+ dirp = StringToUTF16Ptr(attr.Dir)
+ }
// Acquire the fork lock so that no other threads
// create new fds that are not yet close-on-exec
@@ -146,54 +267,35 @@ func StartProcess(argv0 string, argv []string, envv []string, dir string, fd []i
ForkLock.Lock()
defer ForkLock.Unlock()
- var currentProc, _ = GetCurrentProcess()
- if len(fd) > 0 && fd[0] > 0 {
- err := DuplicateHandle(currentProc, int32(fd[0]), currentProc, &startupInfo.StdInput, 0, true, DUPLICATE_SAME_ACCESS)
- if err != 0 {
- return 0, 0, err
+ p, _ := GetCurrentProcess()
+ fd := make([]int32, len(attr.Files))
+ for i := range attr.Files {
+ if attr.Files[i] > 0 {
+ err := DuplicateHandle(p, int32(attr.Files[i]), p, &fd[i], 0, true, DUPLICATE_SAME_ACCESS)
+ if err != 0 {
+ return 0, 0, err
+ }
+ defer CloseHandle(int32(fd[i]))
}
- defer CloseHandle(int32(startupInfo.StdInput))
}
- if len(fd) > 1 && fd[1] > 0 {
- err := DuplicateHandle(currentProc, int32(fd[1]), currentProc, &startupInfo.StdOutput, 0, true, DUPLICATE_SAME_ACCESS)
- if err != 0 {
- return 0, 0, err
- }
- defer CloseHandle(int32(startupInfo.StdOutput))
+ si := new(StartupInfo)
+ GetStartupInfo(si)
+ si.Flags = STARTF_USESTDHANDLES
+ si.StdInput = fd[0]
+ si.StdOutput = fd[1]
+ si.StdErr = fd[2]
+
+ pi := new(ProcessInformation)
+
+ err = CreateProcess(argv0p, argvp, nil, nil, true, CREATE_UNICODE_ENVIRONMENT, createEnvBlock(attr.Env), dirp, si, pi)
+ if err != 0 {
+ return 0, 0, err
}
- if len(fd) > 2 && fd[2] > 0 {
- err := DuplicateHandle(currentProc, int32(fd[2]), currentProc, &startupInfo.StdErr, 0, true, DUPLICATE_SAME_ACCESS)
- if err != 0 {
- return 0, 0, err
- }
- defer CloseHandle(int32(startupInfo.StdErr))
- }
- if len(argv) == 0 {
- argv = []string{""}
- }
- // argv0 must not be longer then 256 chars
- // but the entire cmd line can have up to 32k chars (msdn)
- err = CreateProcess(
- nil,
- StringToUTF16Ptr(escapeAddQuotes(argv0)+" "+stringJoin(argv[1:], " ", escapeAddQuotes)),
- nil, //ptr to struct lpProcessAttributes
- nil, //ptr to struct lpThreadAttributes
- true, //bInheritHandles
- CREATE_UNICODE_ENVIRONMENT, //Flags
- createEnvBlock(envv), //env block, NULL uses parent env
- StringToUTF16Ptr(dir),
- startupInfo,
- processInfo)
-
- if err == 0 {
- pid = int(processInfo.ProcessId)
- handle = int(processInfo.Process)
- CloseHandle(processInfo.Thread)
- }
- return
+ defer CloseHandle(pi.Thread)
+
+ return int(pi.ProcessId), int(pi.Process), 0
}
-// Ordinary exec.
func Exec(argv0 string, argv []string, envv []string) (err int) {
return EWINDOWS
}
diff --git a/src/pkg/syscall/mkall.sh b/src/pkg/syscall/mkall.sh
index a1f8ae2ff..a2e6c5d71 100755
--- a/src/pkg/syscall/mkall.sh
+++ b/src/pkg/syscall/mkall.sh
@@ -63,7 +63,7 @@
#
# * zsyscall_${GOOS}_${GOARCH}.go
#
-# Generated by mksyscall.sh; see syscall_${GOOS}.go above.
+# Generated by mksyscall.pl; see syscall_${GOOS}.go above.
#
# * zsysnum_${GOOS}_${GOARCH}.go
#
@@ -76,11 +76,18 @@
GOOSARCH="${GOOS}_${GOARCH}"
# defaults
-mksyscall="./mksyscall.sh"
+mksyscall="./mksyscall.pl"
mkerrors="./mkerrors.sh"
run="sh"
case "$1" in
+-syscalls)
+ for i in zsyscall*go
+ do
+ sed 1q $i | sed 's;^// ;;' | sh | gofmt >_$i && mv _$i $i
+ done
+ exit 0
+ ;;
-n)
run="cat"
shift
@@ -101,59 +108,56 @@ _* | *_ | _)
;;
freebsd_386)
mkerrors="$mkerrors -f -m32"
- mksyscall="./mksyscall.sh -l32"
- mksysnum="curl -s 'http://svn.freebsd.org/viewvc/base/head/sys/kern/syscalls.master?view=markup' | ./mksysnum_freebsd.sh"
+ mksyscall="./mksyscall.pl -l32"
+ mksysnum="curl -s 'http://svn.freebsd.org/viewvc/base/head/sys/kern/syscalls.master?view=markup' | ./mksysnum_freebsd.pl"
mktypes="godefs -gsyscall -f-m32"
;;
freebsd_amd64)
mkerrors="$mkerrors -f -m64"
- mksysnum="curl -s 'http://svn.freebsd.org/viewvc/base/head/sys/kern/syscalls.master?view=markup' | ./mksysnum_freebsd.sh"
+ mksysnum="curl -s 'http://svn.freebsd.org/viewvc/base/head/sys/kern/syscalls.master?view=markup' | ./mksysnum_freebsd.pl"
mktypes="godefs -gsyscall -f-m64"
;;
darwin_386)
mkerrors="$mkerrors -f -m32"
- mksyscall="./mksyscall.sh -l32"
- mksysnum="./mksysnum_darwin.sh /home/rsc/pub/xnu-1228/bsd/kern/syscalls.master"
+ mksyscall="./mksyscall.pl -l32"
+ mksysnum="./mksysnum_darwin.pl /home/rsc/pub/xnu-1228/bsd/kern/syscalls.master"
mktypes="godefs -gsyscall -f-m32"
;;
darwin_amd64)
mkerrors="$mkerrors -f -m64"
- mksysnum="./mksysnum_darwin.sh /home/rsc/pub/xnu-1228/bsd/kern/syscalls.master"
+ mksysnum="./mksysnum_darwin.pl /home/rsc/pub/xnu-1228/bsd/kern/syscalls.master"
mktypes="godefs -gsyscall -f-m64"
mkerrors="./mkerrors.sh"
;;
linux_386)
mkerrors="$mkerrors -f -m32"
- mksyscall="./mksyscall.sh -l32"
- mksysnum="./mksysnum_linux.sh /usr/include/asm/unistd_32.h"
+ mksyscall="./mksyscall.pl -l32"
+ mksysnum="./mksysnum_linux.pl /usr/include/asm/unistd_32.h"
mktypes="godefs -gsyscall -f-m32"
;;
linux_amd64)
mkerrors="$mkerrors -f -m64"
- mksysnum="./mksysnum_linux.sh /usr/include/asm/unistd_64.h"
+ mksysnum="./mksysnum_linux.pl /usr/include/asm/unistd_64.h"
mktypes="godefs -gsyscall -f-m64"
;;
-nacl_386)
- NACL="/home/rsc/pub/nacl/native_client"
- NACLRUN="$NACL/src/trusted/service_runtime"
- NACLSDK="$NACL/src/third_party/nacl_sdk/linux/sdk/nacl-sdk/nacl"
- mksyscall="./mksyscall.sh -l32 -nacl"
- mksysnum="./mksysnum_nacl.sh $NACLRUN/include/bits/nacl_syscalls.h"
- mktypes="godefs -gsyscall -f-m32 -f-I$NACLSDK/include -f-I$NACL"
- mkerrors="./mkerrors_nacl.sh $NACLRUN/include/sys/errno.h"
- ;;
linux_arm)
mkerrors="$mkerrors"
- mksyscall="./mksyscall.sh -b32"
- mksysnum="./mksysnum_linux.sh /usr/include/asm/unistd.h"
+ mksyscall="./mksyscall.pl -b32"
+ mksysnum="./mksysnum_linux.pl /usr/include/asm/unistd.h"
mktypes="godefs -gsyscall"
;;
windows_386)
- mksyscall="./mksyscall_windows.sh -l32"
+ mksyscall="./mksyscall_windows.pl -l32"
mksysnum=
mktypes=
mkerrors="./mkerrors_windows.sh -f -m32"
;;
+plan9_386)
+ mkerrors=
+ mksyscall="./mksyscall.pl -l32 -plan9"
+ mksysnum="./mksysnum_plan9.sh /n/sources/plan9/sys/src/libc/9syscall/sys.h"
+ mktypes="godefs -gsyscall -f -m32"
+ ;;
*)
echo 'unrecognized $GOOS_$GOARCH: ' "$GOOSARCH" 1>&2
exit 1
diff --git a/src/pkg/syscall/mkerrors.sh b/src/pkg/syscall/mkerrors.sh
index d40d1f6b3..68a16842a 100755
--- a/src/pkg/syscall/mkerrors.sh
+++ b/src/pkg/syscall/mkerrors.sh
@@ -26,10 +26,12 @@ includes_Linux='
#include <sys/inotify.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
+#include <sys/mount.h>
#include <sys/stat.h>
#include <linux/ptrace.h>
#include <linux/wait.h>
#include <linux/if_tun.h>
+#include <linux/reboot.h>
#include <net/if.h>
#include <netpacket/packet.h>
'
@@ -43,6 +45,7 @@ includes_Darwin='
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/sysctl.h>
+#include <sys/mman.h>
#include <sys/wait.h>
#include <net/if.h>
#include <net/route.h>
@@ -58,6 +61,7 @@ includes_FreeBSD='
#include <sys/sockio.h>
#include <sys/sysctl.h>
#include <sys/wait.h>
+#include <net/bpf.h>
#include <net/if.h>
#include <net/route.h>
#include <netinet/in.h>
@@ -116,15 +120,21 @@ done
$2 ~ /^E[A-Z0-9_]+$/ ||
$2 ~ /^SIG[^_]/ ||
$2 ~ /^IN_/ ||
- $2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|TCP|EVFILT|EV|SHUT|PROT|MAP|PACKET|MSG|SCM|IFF|NET_RT|RTM|RTF|RTV|RTA|RTAX|MCL)_/ ||
+ $2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|TCP|EVFILT|EV|SHUT|PROT|MAP|PACKET|MSG|SCM|MCL|DT|MADV)_/ ||
$2 == "SOMAXCONN" ||
$2 == "NAME_MAX" ||
$2 == "IFNAMSIZ" ||
$2 == "CTL_NET" ||
$2 == "CTL_MAXNAME" ||
+ $2 ~ /^(MS|MNT)_/ ||
$2 ~ /^TUN(SET|GET|ATTACH|DETACH)/ ||
$2 ~ /^(O|F|FD|NAME|S|PTRACE)_/ ||
+ $2 ~ /^LINUX_REBOOT_CMD_/ ||
+ $2 ~ /^LINUX_REBOOT_MAGIC[12]$/ ||
$2 ~ /^SIOC/ ||
+ $2 ~ /^(IFF|NET_RT|RTM|RTF|RTV|RTA|RTAX)_/ ||
+ $2 ~ /^BIOC/ ||
+ $2 ~ /^(BPF|DLT)_/ ||
$2 !~ "WMESGLEN" &&
$2 ~ /^W[A-Z0-9]+$/ {printf("\t$%s = %s,\n", $2, $2)}
$2 ~ /^__WCOREFLAG$/ {next}
diff --git a/src/pkg/syscall/mksyscall.sh b/src/pkg/syscall/mksyscall.pl
index 0f8098098..ecf4abdd4 100755
--- a/src/pkg/syscall/mksyscall.sh
+++ b/src/pkg/syscall/mksyscall.pl
@@ -13,10 +13,17 @@
# the (x, y, z int) shorthand is not allowed.
# * If the return parameter is an error number, it must be named errno.
-$cmdline = "mksyscall.sh " . join(' ', @ARGV);
+# A line beginning with //sysnb is like //sys, except that the
+# goroutine will not be suspended during the execution of the system
+# call. This must only be used for system calls which can never
+# block, as otherwise the system call could cause all goroutines to
+# hang.
+
+$cmdline = "mksyscall.pl " . join(' ', @ARGV);
$errors = 0;
$_32bit = "";
$nacl = 0;
+$plan9 = 0;
if($ARGV[0] eq "-b32") {
$_32bit = "big-endian";
@@ -29,9 +36,13 @@ if($ARGV[0] eq "-nacl") {
$nacl = 1;
shift;
}
+if($ARGV[0] eq "-plan9") {
+ $plan9 = 1;
+ shift;
+}
if($ARGV[0] =~ /^-/) {
- print STDERR "usage: mksyscall.sh [-b32 | -l32] [file ...]\n";
+ print STDERR "usage: mksyscall.pl [-b32 | -l32] [file ...]\n";
exit 1;
}
@@ -61,17 +72,18 @@ while(<>) {
s/\s+/ /g;
s/^\s+//;
s/\s+$//;
- next if !/^\/\/sys /;
+ my $nonblock = /^\/\/sysnb /;
+ next if !/^\/\/sys / && !$nonblock;
# Line must be of the form
# func Open(path string, mode int, perm int) (fd int, errno int)
# Split into name, in params, out params.
- if(!/^\/\/sys (\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*(SYS_[A-Z0-9_]+))?$/) {
+ if(!/^\/\/sys(nb)? (\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*(SYS_[A-Z0-9_]+))?$/) {
print STDERR "$ARGV:$.: malformed //sys declaration\n";
$errors = 1;
next;
}
- my ($func, $in, $out, $sysname) = ($1, $2, $3, $4);
+ my ($func, $in, $out, $sysname) = ($2, $3, $4, $5);
# Split argument lists on comma.
my @in = parseparamlist($in);
@@ -119,15 +131,23 @@ while(<>) {
# Determine which form to use; pad args with zeros.
my $asm = "Syscall";
+ if ($nonblock) {
+ $asm = "RawSyscall";
+ }
if(@args <= 3) {
while(@args < 3) {
push @args, "0";
}
} elsif(@args <= 6) {
- $asm = "Syscall6";
+ $asm .= "6";
while(@args < 6) {
push @args, "0";
}
+ } elsif(@args <= 9) {
+ $asm .= "9";
+ while(@args < 9) {
+ push @args, "0";
+ }
} else {
print STDERR "$ARGV:$.: too many arguments to system call\n";
}
@@ -150,9 +170,13 @@ while(<>) {
my $p = $out[$i];
my ($name, $type) = parseparam($p);
my $reg = "";
- if($name eq "errno") {
+ if($name eq "errno" && !$plan9) {
$reg = "e1";
$ret[2] = $reg;
+ } elsif ($name eq "err" && $plan9) {
+ $ret[0] = "r0";
+ $ret[2] = "e1";
+ next;
} else {
$reg = sprintf("r%d", $i);
$ret[$i] = $reg;
@@ -181,6 +205,13 @@ while(<>) {
$text .= "\t$ret[0], $ret[1], $ret[2] := $call\n";
}
$text .= $body;
+
+ if ($plan9 && $ret[2] eq "e1") {
+ $text .= "\terr = nil\n";
+ $text .= "\tif int(r0) == -1 {\n";
+ $text .= "\t\terr = NewError(e1)\n";
+ $text .= "\t}\n";
+ }
$text .= "\treturn\n";
$text .= "}\n\n";
diff --git a/src/pkg/syscall/mksyscall_windows.sh b/src/pkg/syscall/mksyscall_windows.pl
index 3b1c9df85..d92ac3d28 100755
--- a/src/pkg/syscall/mksyscall_windows.sh
+++ b/src/pkg/syscall/mksyscall_windows.pl
@@ -23,7 +23,7 @@
# //sys LoadLibrary(libname string) (handle uint32, errno int) [failretval==-1] = LoadLibraryA
# and is [failretval==0] by default.
-$cmdline = "mksyscall_windows.sh " . join(' ', @ARGV);
+$cmdline = "mksyscall_windows.pl " . join(' ', @ARGV);
$errors = 0;
$_32bit = "";
@@ -36,7 +36,7 @@ if($ARGV[0] eq "-b32") {
}
if($ARGV[0] =~ /^-/) {
- print STDERR "usage: mksyscall_windows.sh [-b32 | -l32] [file ...]\n";
+ print STDERR "usage: mksyscall_windows.pl [-b32 | -l32] [file ...]\n";
exit 1;
}
@@ -119,7 +119,14 @@ while(<>) {
$vars .= sprintf "\t%s = getSysProcAddr(%s, \"%s\")\n", $sysvarname, $modvname, $sysname;
# Go function header.
- $text .= sprintf "func %s(%s) (%s) {\n", $func, join(', ', @in), join(', ', @out);
+ my $out = join(', ', @out);
+ if($out ne "") {
+ $out = " ($out)";
+ }
+ if($text ne "") {
+ $text .= "\n"
+ }
+ $text .= sprintf "func %s(%s)%s {\n", $func, join(', ', @in), $out;
# Prepare arguments to Syscall.
my @args = ();
@@ -232,6 +239,7 @@ while(<>) {
$failexpr = "$name $failcond";
}
}
+ $failexpr =~ s/(=)([0-9A-Za-z\-+])/\1 \2/; # gofmt compatible
if($name eq "errno") {
# Set errno to "last error" only if returned value indicate failure
$body .= "\tif $failexpr {\n";
@@ -259,7 +267,7 @@ while(<>) {
}
$text .= "\treturn\n";
- $text .= "}\n\n";
+ $text .= "}\n";
}
if($errors) {
diff --git a/src/pkg/syscall/mksysnum_darwin.sh b/src/pkg/syscall/mksysnum_darwin.pl
index 192e61a73..d078a1836 100755
--- a/src/pkg/syscall/mksysnum_darwin.sh
+++ b/src/pkg/syscall/mksysnum_darwin.pl
@@ -6,7 +6,7 @@
# Generate system call table for Darwin from master list
# (for example, xnu-1228/bsd/kern/syscalls.master).
-my $command = "mksysnum_darwin.sh " . join(' ', @ARGV);
+my $command = "mksysnum_darwin.pl " . join(' ', @ARGV);
print <<EOF;
// $command
diff --git a/src/pkg/syscall/mksysnum_freebsd.sh b/src/pkg/syscall/mksysnum_freebsd.pl
index e52835d49..03f7d9e25 100755
--- a/src/pkg/syscall/mksysnum_freebsd.sh
+++ b/src/pkg/syscall/mksysnum_freebsd.pl
@@ -6,7 +6,7 @@
# Generate system call table for FreeBSD from master list
# (for example, /usr/src/sys/kern/syscalls.master).
-my $command = "mksysnum_freebsd.sh " . join(' ', @ARGV);
+my $command = "mksysnum_freebsd.pl " . join(' ', @ARGV);
print <<EOF;
// $command
diff --git a/src/pkg/syscall/mksysnum_linux.sh b/src/pkg/syscall/mksysnum_linux.pl
index 89ece8a91..e97c87f44 100755
--- a/src/pkg/syscall/mksysnum_linux.sh
+++ b/src/pkg/syscall/mksysnum_linux.pl
@@ -3,7 +3,7 @@
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
-my $command = "mksysnum_linux.sh ". join(' ', @ARGV);
+my $command = "mksysnum_linux.pl ". join(' ', @ARGV);
print <<EOF;
// $command
diff --git a/src/pkg/syscall/mksysnum_plan9.sh b/src/pkg/syscall/mksysnum_plan9.sh
new file mode 100755
index 000000000..fc619f090
--- /dev/null
+++ b/src/pkg/syscall/mksysnum_plan9.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+# 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.# 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.
+
+COMMAND="mksysnum_plan9.sh $@"
+
+cat <<EOF
+// $COMMAND
+// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
+
+package syscall
+
+const(
+EOF
+
+SP='[ ]' # space or tab
+sed "s/^#define${SP}\\([A-Z0-9_][A-Z0-9_]*\\)${SP}${SP}*\\([0-9][0-9]*\\)/SYS_\\1=\\2/g" \
+ < $1 | grep -v SYS__
+
+cat <<EOF
+)
+EOF
diff --git a/src/pkg/syscall/str.go b/src/pkg/syscall/str.go
index 12f0c7d60..0fce842e8 100644
--- a/src/pkg/syscall/str.go
+++ b/src/pkg/syscall/str.go
@@ -4,9 +4,9 @@
package syscall
-func str(val int) string { // do it here rather than with fmt to avoid dependency
+func itoa(val int) string { // do it here rather than with fmt to avoid dependency
if val < 0 {
- return "-" + str(-val)
+ return "-" + itoa(-val)
}
var buf [32]byte // big enough for int64
i := len(buf) - 1
diff --git a/src/pkg/syscall/syscall.go b/src/pkg/syscall/syscall.go
index 96975376f..2a9ffd4af 100644
--- a/src/pkg/syscall/syscall.go
+++ b/src/pkg/syscall/syscall.go
@@ -13,6 +13,11 @@
// errno is an operating system error number describing the failure.
package syscall
+import (
+ "sync"
+ "unsafe"
+)
+
// StringByteSlice returns a NUL-terminated slice of bytes
// containing the text of s.
func StringByteSlice(s string) []byte {
@@ -28,3 +33,63 @@ func StringBytePtr(s string) *byte { return &StringByteSlice(s)[0] }
// Single-word zero for use when we need a valid pointer to 0 bytes.
// See mksyscall.sh.
var _zero uintptr
+
+// Mmap manager, for use by operating system-specific implementations.
+
+type mmapper struct {
+ sync.Mutex
+ active map[*byte][]byte // active mappings; key is last byte in mapping
+ mmap func(addr, length uintptr, prot, flags, fd int, offset int64) (uintptr, int)
+ munmap func(addr uintptr, length uintptr) int
+}
+
+func (m *mmapper) Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, errno int) {
+ if length <= 0 {
+ return nil, EINVAL
+ }
+
+ // Map the requested memory.
+ addr, errno := m.mmap(0, uintptr(length), prot, flags, fd, offset)
+ if errno != 0 {
+ return nil, errno
+ }
+
+ // Slice memory layout
+ var sl = struct {
+ addr uintptr
+ len int
+ cap int
+ }{addr, length, length}
+
+ // Use unsafe to turn sl into a []byte.
+ b := *(*[]byte)(unsafe.Pointer(&sl))
+
+ // Register mapping in m and return it.
+ p := &b[cap(b)-1]
+ m.Lock()
+ defer m.Unlock()
+ m.active[p] = b
+ return b, 0
+}
+
+func (m *mmapper) Munmap(data []byte) (errno int) {
+ if len(data) == 0 || len(data) != cap(data) {
+ return EINVAL
+ }
+
+ // Find the base of the mapping.
+ p := &data[cap(data)-1]
+ m.Lock()
+ defer m.Unlock()
+ b := m.active[p]
+ if b == nil || &b[0] != &data[0] {
+ return EINVAL
+ }
+
+ // Unmap the memory and update m.
+ if errno := m.munmap(uintptr(unsafe.Pointer(&b[0])), uintptr(len(b))); errno != 0 {
+ return errno
+ }
+ m.active[p] = nil, false
+ return 0
+}
diff --git a/src/pkg/syscall/syscall_bsd.go b/src/pkg/syscall/syscall_bsd.go
index 1f5b2ba9a..9f1244f13 100644
--- a/src/pkg/syscall/syscall_bsd.go
+++ b/src/pkg/syscall/syscall_bsd.go
@@ -27,8 +27,8 @@ func Getwd() (string, int) { return "", ENOTSUP }
* Wrapped
*/
-//sys getgroups(ngid int, gid *_Gid_t) (n int, errno int)
-//sys setgroups(ngid int, gid *_Gid_t) (errno int)
+//sysnb getgroups(ngid int, gid *_Gid_t) (n int, errno int)
+//sysnb setgroups(ngid int, gid *_Gid_t) (errno int)
func Getgroups() (gids []int, errno int) {
n, err := getgroups(0, nil)
@@ -68,6 +68,12 @@ func Setgroups(gids []int) (errno int) {
return setgroups(len(a), &a[0])
}
+func ReadDirent(fd int, buf []byte) (n int, errno int) {
+ // Final argument is (basep *uintptr) and the syscall doesn't take nil.
+ // TODO(rsc): Can we use a single global basep for all calls?
+ return Getdirentries(fd, buf, new(uintptr))
+}
+
// Wait status is 7 bits at bottom, either 0 (exited),
// 0x7F (stopped), or a signal number that caused an exit.
// The 0x80 bit is whether there was a core dump.
@@ -130,7 +136,7 @@ func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int,
return
}
-//sys pipe() (r int, w int, errno int)
+//sysnb pipe() (r int, w int, errno int)
func Pipe(p []int) (errno int) {
if len(p) != 2 {
@@ -148,10 +154,11 @@ func Sleep(ns int64) (errno int) {
//sys accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, errno int)
//sys bind(s int, addr uintptr, addrlen _Socklen) (errno int)
//sys connect(s int, addr uintptr, addrlen _Socklen) (errno int)
-//sys socket(domain int, typ int, proto int) (fd int, errno int)
+//sysnb socket(domain int, typ int, proto int) (fd int, errno int)
+//sys getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (errno int)
//sys setsockopt(s int, level int, name int, val uintptr, vallen int) (errno int)
-//sys getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int)
-//sys getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int)
+//sysnb getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int)
+//sysnb getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int)
//sys Shutdown(s int, how int) (errno int)
// For testing: clients can set this flag to force
@@ -355,13 +362,20 @@ func Socket(domain, typ, proto int) (fd, errno int) {
return
}
-//sys socketpair(domain int, typ int, proto int, fd *[2]int) (errno int)
+//sysnb socketpair(domain int, typ int, proto int, fd *[2]int) (errno int)
func Socketpair(domain, typ, proto int) (fd [2]int, errno int) {
errno = socketpair(domain, typ, proto, &fd)
return
}
+func GetsockoptInt(fd, level, opt int) (value, errno int) {
+ var n int32
+ vallen := _Socklen(4)
+ errno = getsockopt(fd, level, opt, uintptr(unsafe.Pointer(&n)), &vallen)
+ return int(n), errno
+}
+
func SetsockoptInt(fd, level, opt int, value int) (errno int) {
var n = int32(value)
return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(&n)), 4)
@@ -552,9 +566,24 @@ func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (errno int) {
// TODO: wrap
// Acct(name nil-string) (errno int)
// Gethostuuid(uuid *byte, timeout *Timespec) (errno int)
-// Getsockopt(s int, level int, name int, val *byte, vallen *int) (errno int)
// Madvise(addr *byte, len int, behav int) (errno int)
// Mprotect(addr *byte, len int, prot int) (errno int)
// Msync(addr *byte, len int, flags int) (errno int)
-// Munmap(addr *byte, len int) (errno int)
// Ptrace(req int, pid int, addr uintptr, data int) (ret uintptr, errno int)
+
+//sys mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, errno int)
+//sys munmap(addr uintptr, length uintptr) (errno int)
+
+var mapper = &mmapper{
+ active: make(map[*byte][]byte),
+ mmap: mmap,
+ munmap: munmap,
+}
+
+func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, errno int) {
+ return mapper.Mmap(fd, offset, length, prot, flags)
+}
+
+func Munmap(b []byte) (errno int) {
+ return mapper.Munmap(b)
+}
diff --git a/src/pkg/syscall/syscall_darwin.go b/src/pkg/syscall/syscall_darwin.go
index 552c9c154..30b57cf55 100644
--- a/src/pkg/syscall/syscall_darwin.go
+++ b/src/pkg/syscall/syscall_darwin.go
@@ -12,6 +12,8 @@
package syscall
+import "unsafe"
+
const OS = "darwin"
type SockaddrDatalink struct {
@@ -26,6 +28,34 @@ type SockaddrDatalink struct {
raw RawSockaddrDatalink
}
+// ParseDirent parses up to max directory entries in buf,
+// appending the names to names. It returns the number
+// bytes consumed from buf, the number of entries added
+// to names, and the new names slice.
+func ParseDirent(buf []byte, max int, names []string) (consumed int, count int, newnames []string) {
+ origlen := len(buf)
+ for max != 0 && len(buf) > 0 {
+ dirent := (*Dirent)(unsafe.Pointer(&buf[0]))
+ if dirent.Reclen == 0 {
+ buf = nil
+ break
+ }
+ buf = buf[dirent.Reclen:]
+ if dirent.Ino == 0 { // File absent in directory.
+ continue
+ }
+ bytes := (*[10000]byte)(unsafe.Pointer(&dirent.Name[0]))
+ var name = string(bytes[0:dirent.Namlen])
+ if name == "." || name == ".." { // Useless names
+ continue
+ }
+ max--
+ count++
+ names = append(names, name)
+ }
+ return origlen - len(buf), count, names
+}
+
/*
* Wrapped
*/
@@ -45,8 +75,8 @@ func Kill(pid int, signum int) (errno int) { return kill(pid, signum, 1) }
//sys Chown(path string, uid int, gid int) (errno int)
//sys Chroot(path string) (errno int)
//sys Close(fd int) (errno int)
-//sys Dup(fd int) (nfd int, errno int)
-//sys Dup2(from int, to int) (errno int)
+//sysnb Dup(fd int) (nfd int, errno int)
+//sysnb Dup2(from int, to int) (errno int)
//sys Exchangedata(path1 string, path2 string, options int) (errno int)
//sys Exit(code int)
//sys Fchdir(fd int) (errno int)
@@ -61,20 +91,20 @@ func Kill(pid int, signum int) (errno int) { return kill(pid, signum, 1) }
//sys Ftruncate(fd int, length int64) (errno int)
//sys Getdirentries(fd int, buf []byte, basep *uintptr) (n int, errno int) = SYS_GETDIRENTRIES64
//sys Getdtablesize() (size int)
-//sys Getegid() (egid int)
-//sys Geteuid() (uid int)
+//sysnb Getegid() (egid int)
+//sysnb Geteuid() (uid int)
//sys Getfsstat(buf []Statfs_t, flags int) (n int, errno int) = SYS_GETFSSTAT64
-//sys Getgid() (gid int)
-//sys Getpgid(pid int) (pgid int, errno int)
-//sys Getpgrp() (pgrp int)
-//sys Getpid() (pid int)
-//sys Getppid() (ppid int)
+//sysnb Getgid() (gid int)
+//sysnb Getpgid(pid int) (pgid int, errno int)
+//sysnb Getpgrp() (pgrp int)
+//sysnb Getpid() (pid int)
+//sysnb Getppid() (ppid int)
//sys Getpriority(which int, who int) (prio int, errno int)
-//sys Getrlimit(which int, lim *Rlimit) (errno int)
-//sys Getrusage(who int, rusage *Rusage) (errno int)
-//sys Getsid(pid int) (sid int, errno int)
-//sys Getuid() (uid int)
-//sys Issetugid() (tainted bool)
+//sysnb Getrlimit(which int, lim *Rlimit) (errno int)
+//sysnb Getrusage(who int, rusage *Rusage) (errno int)
+//sysnb Getsid(pid int) (sid int, errno int)
+//sysnb Getuid() (uid int)
+//sysnb Issetugid() (tainted bool)
//sys Kqueue() (fd int, errno int)
//sys Lchown(path string, uid int, gid int) (errno int)
//sys Link(path string, link string) (errno int)
@@ -95,18 +125,18 @@ func Kill(pid int, signum int) (errno int) { return kill(pid, signum, 1) }
//sys Seek(fd int, offset int64, whence int) (newoffset int64, errno int) = SYS_LSEEK
//sys Select(n int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (errno int)
//sys Setegid(egid int) (errno int)
-//sys Seteuid(euid int) (errno int)
-//sys Setgid(gid int) (errno int)
+//sysnb Seteuid(euid int) (errno int)
+//sysnb Setgid(gid int) (errno int)
//sys Setlogin(name string) (errno int)
-//sys Setpgid(pid int, pgid int) (errno int)
+//sysnb Setpgid(pid int, pgid int) (errno int)
//sys Setpriority(which int, who int, prio int) (errno int)
//sys Setprivexec(flag int) (errno int)
-//sys Setregid(rgid int, egid int) (errno int)
-//sys Setreuid(ruid int, euid int) (errno int)
-//sys Setrlimit(which int, lim *Rlimit) (errno int)
-//sys Setsid() (pid int, errno int)
-//sys Settimeofday(tp *Timeval) (errno int)
-//sys Setuid(uid int) (errno int)
+//sysnb Setregid(rgid int, egid int) (errno int)
+//sysnb Setreuid(ruid int, euid int) (errno int)
+//sysnb Setrlimit(which int, lim *Rlimit) (errno int)
+//sysnb Setsid() (pid int, errno int)
+//sysnb Settimeofday(tp *Timeval) (errno int)
+//sysnb Setuid(uid int) (errno int)
//sys Stat(path string, stat *Stat_t) (errno int) = SYS_STAT64
//sys Statfs(path string, stat *Statfs_t) (errno int) = SYS_STATFS64
//sys Symlink(path string, link string) (errno int)
diff --git a/src/pkg/syscall/syscall_darwin_386.go b/src/pkg/syscall/syscall_darwin_386.go
index 3fd72efe3..5101ba6c7 100644
--- a/src/pkg/syscall/syscall_darwin_386.go
+++ b/src/pkg/syscall/syscall_darwin_386.go
@@ -23,7 +23,7 @@ func NsecToTimeval(nsec int64) (tv Timeval) {
return
}
-//sys gettimeofday(tp *Timeval) (sec int32, usec int32, errno int)
+//sysnb gettimeofday(tp *Timeval) (sec int32, usec int32, errno int)
func Gettimeofday(tv *Timeval) (errno int) {
// The tv passed to gettimeofday must be non-nil
// but is otherwise unused. The answers come back
@@ -39,3 +39,5 @@ func SetKevent(k *Kevent_t, fd, mode, flags int) {
k.Filter = int16(mode)
k.Flags = uint16(flags)
}
+
+func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr) // sic
diff --git a/src/pkg/syscall/syscall_darwin_amd64.go b/src/pkg/syscall/syscall_darwin_amd64.go
index df8d37588..acf7a5554 100644
--- a/src/pkg/syscall/syscall_darwin_amd64.go
+++ b/src/pkg/syscall/syscall_darwin_amd64.go
@@ -23,7 +23,7 @@ func NsecToTimeval(nsec int64) (tv Timeval) {
return
}
-//sys gettimeofday(tp *Timeval) (sec int64, usec int32, errno int)
+//sysnb gettimeofday(tp *Timeval) (sec int64, usec int32, errno int)
func Gettimeofday(tv *Timeval) (errno int) {
// The tv passed to gettimeofday must be non-nil
// but is otherwise unused. The answers come back
diff --git a/src/pkg/syscall/syscall_freebsd.go b/src/pkg/syscall/syscall_freebsd.go
index ed310663a..242503dd7 100644
--- a/src/pkg/syscall/syscall_freebsd.go
+++ b/src/pkg/syscall/syscall_freebsd.go
@@ -12,6 +12,8 @@
package syscall
+import "unsafe"
+
const OS = "freebsd"
type SockaddrDatalink struct {
@@ -26,6 +28,34 @@ type SockaddrDatalink struct {
raw RawSockaddrDatalink
}
+// ParseDirent parses up to max directory entries in buf,
+// appending the names to names. It returns the number
+// bytes consumed from buf, the number of entries added
+// to names, and the new names slice.
+func ParseDirent(buf []byte, max int, names []string) (consumed int, count int, newnames []string) {
+ origlen := len(buf)
+ for max != 0 && len(buf) > 0 {
+ dirent := (*Dirent)(unsafe.Pointer(&buf[0]))
+ if dirent.Reclen == 0 {
+ buf = nil
+ break
+ }
+ buf = buf[dirent.Reclen:]
+ if dirent.Fileno == 0 { // File absent in directory.
+ continue
+ }
+ bytes := (*[10000]byte)(unsafe.Pointer(&dirent.Name[0]))
+ var name = string(bytes[0:dirent.Namlen])
+ if name == "." || name == ".." { // Useless names
+ continue
+ }
+ max--
+ count++
+ names = append(names, name)
+ }
+ return origlen - len(buf), count, names
+}
+
/*
* Exposed directly
*/
@@ -37,8 +67,8 @@ type SockaddrDatalink struct {
//sys Chown(path string, uid int, gid int) (errno int)
//sys Chroot(path string) (errno int)
//sys Close(fd int) (errno int)
-//sys Dup(fd int) (nfd int, errno int)
-//sys Dup2(from int, to int) (errno int)
+//sysnb Dup(fd int) (nfd int, errno int)
+//sysnb Dup2(from int, to int) (errno int)
//sys Exit(code int)
//sys Fchdir(fd int) (errno int)
//sys Fchflags(path string, flags int) (errno int)
@@ -52,20 +82,20 @@ type SockaddrDatalink struct {
//sys Ftruncate(fd int, length int64) (errno int)
//sys Getdirentries(fd int, buf []byte, basep *uintptr) (n int, errno int)
//sys Getdtablesize() (size int)
-//sys Getegid() (egid int)
-//sys Geteuid() (uid int)
+//sysnb Getegid() (egid int)
+//sysnb Geteuid() (uid int)
//sys Getfsstat(buf []Statfs_t, flags int) (n int, errno int)
-//sys Getgid() (gid int)
-//sys Getpgid(pid int) (pgid int, errno int)
-//sys Getpgrp() (pgrp int)
-//sys Getpid() (pid int)
-//sys Getppid() (ppid int)
+//sysnb Getgid() (gid int)
+//sysnb Getpgid(pid int) (pgid int, errno int)
+//sysnb Getpgrp() (pgrp int)
+//sysnb Getpid() (pid int)
+//sysnb Getppid() (ppid int)
//sys Getpriority(which int, who int) (prio int, errno int)
-//sys Getrlimit(which int, lim *Rlimit) (errno int)
-//sys Getrusage(who int, rusage *Rusage) (errno int)
-//sys Getsid(pid int) (sid int, errno int)
-//sys Gettimeofday(tv *Timeval) (errno int)
-//sys Getuid() (uid int)
+//sysnb Getrlimit(which int, lim *Rlimit) (errno int)
+//sysnb Getrusage(who int, rusage *Rusage) (errno int)
+//sysnb Getsid(pid int) (sid int, errno int)
+//sysnb Gettimeofday(tv *Timeval) (errno int)
+//sysnb Getuid() (uid int)
//sys Issetugid() (tainted bool)
//sys Kill(pid int, signum int) (errno int)
//sys Kqueue() (fd int, errno int)
@@ -88,18 +118,18 @@ type SockaddrDatalink struct {
//sys Rmdir(path string) (errno int)
//sys Seek(fd int, offset int64, whence int) (newoffset int64, errno int) = SYS_LSEEK
//sys Select(n int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (errno int)
-//sys Setegid(egid int) (errno int)
-//sys Seteuid(euid int) (errno int)
-//sys Setgid(gid int) (errno int)
+//sysnb Setegid(egid int) (errno int)
+//sysnb Seteuid(euid int) (errno int)
+//sysnb Setgid(gid int) (errno int)
//sys Setlogin(name string) (errno int)
-//sys Setpgid(pid int, pgid int) (errno int)
+//sysnb Setpgid(pid int, pgid int) (errno int)
//sys Setpriority(which int, who int, prio int) (errno int)
-//sys Setregid(rgid int, egid int) (errno int)
-//sys Setreuid(ruid int, euid int) (errno int)
-//sys Setrlimit(which int, lim *Rlimit) (errno int)
-//sys Setsid() (pid int, errno int)
-//sys Settimeofday(tp *Timeval) (errno int)
-//sys Setuid(uid int) (errno int)
+//sysnb Setregid(rgid int, egid int) (errno int)
+//sysnb Setreuid(ruid int, euid int) (errno int)
+//sysnb Setrlimit(which int, lim *Rlimit) (errno int)
+//sysnb Setsid() (pid int, errno int)
+//sysnb Settimeofday(tp *Timeval) (errno int)
+//sysnb Setuid(uid int) (errno int)
//sys Stat(path string, stat *Stat_t) (errno int)
//sys Statfs(path string, stat *Statfs_t) (errno int)
//sys Symlink(path string, link string) (errno int)
diff --git a/src/pkg/syscall/syscall_freebsd_386.go b/src/pkg/syscall/syscall_freebsd_386.go
index 0aa577ee6..d0fa506c7 100644
--- a/src/pkg/syscall/syscall_freebsd_386.go
+++ b/src/pkg/syscall/syscall_freebsd_386.go
@@ -28,3 +28,5 @@ func SetKevent(k *Kevent_t, fd, mode, flags int) {
k.Filter = int16(mode)
k.Flags = uint16(flags)
}
+
+func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr) // sic
diff --git a/src/pkg/syscall/syscall_linux.go b/src/pkg/syscall/syscall_linux.go
index 30ad89646..2b221bd60 100644
--- a/src/pkg/syscall/syscall_linux.go
+++ b/src/pkg/syscall/syscall_linux.go
@@ -29,7 +29,7 @@ func Openat(dirfd int, path string, flags int, mode uint32) (fd int, errno int)
return openat(dirfd, path, flags|O_LARGEFILE, mode)
}
-//sys pipe(p *[2]_C_int) (errno int)
+//sysnb pipe(p *[2]_C_int) (errno int)
func Pipe(p []int) (errno int) {
if len(p) != 2 {
return EINVAL
@@ -60,7 +60,7 @@ func Futimesat(dirfd int, path string, tv []Timeval) (errno int) {
func Futimes(fd int, tv []Timeval) (errno int) {
// Believe it or not, this is the best we can do on Linux
// (and is what glibc does).
- return Utimes("/proc/self/fd/"+str(fd), tv)
+ return Utimes("/proc/self/fd/"+itoa(fd), tv)
}
const ImplementsGetwd = true
@@ -415,6 +415,13 @@ func Socketpair(domain, typ, proto int) (fd [2]int, errno int) {
return
}
+func GetsockoptInt(fd, level, opt int) (value, errno int) {
+ var n int32
+ vallen := _Socklen(4)
+ errno = getsockopt(fd, level, opt, uintptr(unsafe.Pointer(&n)), &vallen)
+ return int(n), errno
+}
+
func SetsockoptInt(fd, level, opt int, value int) (errno int) {
var n = int32(value)
return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(&n)), 4)
@@ -667,10 +674,48 @@ func PtraceAttach(pid int) (errno int) { return ptrace(PTRACE_ATTACH, pid, 0, 0)
func PtraceDetach(pid int) (errno int) { return ptrace(PTRACE_DETACH, pid, 0, 0) }
+//sys reboot(magic1 uint, magic2 uint, cmd int, arg string) (errno int)
+func Reboot(cmd int) (errno int) {
+ return reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, cmd, "")
+}
+
+func clen(n []byte) int {
+ for i := 0; i < len(n); i++ {
+ if n[i] == 0 {
+ return i
+ }
+ }
+ return len(n)
+}
+
+func ReadDirent(fd int, buf []byte) (n int, errno int) {
+ return Getdents(fd, buf)
+}
+
+func ParseDirent(buf []byte, max int, names []string) (consumed int, count int, newnames []string) {
+ origlen := len(buf)
+ count = 0
+ for max != 0 && len(buf) > 0 {
+ dirent := (*Dirent)(unsafe.Pointer(&buf[0]))
+ buf = buf[dirent.Reclen:]
+ if dirent.Ino == 0 { // File absent in directory.
+ continue
+ }
+ bytes := (*[10000]byte)(unsafe.Pointer(&dirent.Name[0]))
+ var name = string(bytes[0:clen(bytes[:])])
+ if name == "." || name == ".." { // Useless names
+ continue
+ }
+ max--
+ count++
+ names = append(names, name)
+ }
+ return origlen - len(buf), count, names
+}
+
// Sendto
// Recvfrom
// Socketpair
-// Getsockopt
/*
* Direct access
@@ -683,10 +728,10 @@ func PtraceDetach(pid int) (errno int) { return ptrace(PTRACE_DETACH, pid, 0, 0)
//sys Chroot(path string) (errno int)
//sys Close(fd int) (errno int)
//sys Creat(path string, mode uint32) (fd int, errno int)
-//sys Dup(oldfd int) (fd int, errno int)
-//sys Dup2(oldfd int, newfd int) (fd int, errno int)
-//sys EpollCreate(size int) (fd int, errno int)
-//sys EpollCtl(epfd int, op int, fd int, event *EpollEvent) (errno int)
+//sysnb Dup(oldfd int) (fd int, errno int)
+//sysnb Dup2(oldfd int, newfd int) (fd int, errno int)
+//sysnb EpollCreate(size int) (fd int, errno int)
+//sysnb EpollCtl(epfd int, op int, fd int, event *EpollEvent) (errno int)
//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, errno int)
//sys Exit(code int) = SYS_EXIT_GROUP
//sys Faccessat(dirfd int, path string, mode uint32, flags int) (errno int)
@@ -699,24 +744,25 @@ func PtraceDetach(pid int) (errno int) { return ptrace(PTRACE_DETACH, pid, 0, 0)
//sys Fdatasync(fd int) (errno int)
//sys Fsync(fd int) (errno int)
//sys Getdents(fd int, buf []byte) (n int, errno int) = SYS_GETDENTS64
-//sys Getpgid(pid int) (pgid int, errno int)
-//sys Getpgrp() (pid int)
-//sys Getpid() (pid int)
-//sys Getppid() (ppid int)
-//sys Getrlimit(resource int, rlim *Rlimit) (errno int)
-//sys Getrusage(who int, rusage *Rusage) (errno int)
-//sys Gettid() (tid int)
-//sys InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, errno int)
-//sys InotifyInit() (fd int, errno int)
-//sys InotifyInit1(flags int) (fd int, errno int)
-//sys InotifyRmWatch(fd int, watchdesc uint32) (success int, errno int)
-//sys Kill(pid int, sig int) (errno int)
+//sysnb Getpgid(pid int) (pgid int, errno int)
+//sysnb Getpgrp() (pid int)
+//sysnb Getpid() (pid int)
+//sysnb Getppid() (ppid int)
+//sysnb Getrlimit(resource int, rlim *Rlimit) (errno int)
+//sysnb Getrusage(who int, rusage *Rusage) (errno int)
+//sysnb Gettid() (tid int)
+//sys InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, errno int)
+//sysnb InotifyInit() (fd int, errno int)
+//sysnb InotifyInit1(flags int) (fd int, errno int)
+//sysnb InotifyRmWatch(fd int, watchdesc uint32) (success int, errno int)
+//sysnb Kill(pid int, sig int) (errno int)
//sys Klogctl(typ int, buf []byte) (n int, errno int) = SYS_SYSLOG
//sys Link(oldpath string, newpath string) (errno int)
//sys Mkdir(path string, mode uint32) (errno int)
//sys Mkdirat(dirfd int, path string, mode uint32) (errno int)
//sys Mknod(path string, mode uint32, dev int) (errno int)
//sys Mknodat(dirfd int, path string, mode uint32, dev int) (errno int)
+//sys Mount(source string, target string, fstype string, flags int, data string) (errno int)
//sys Nanosleep(time *Timespec, leftover *Timespec) (errno int)
//sys Pause() (errno int)
//sys PivotRoot(newroot string, putold string) (errno int) = SYS_PIVOT_ROOT
@@ -727,21 +773,22 @@ func PtraceDetach(pid int) (errno int) { return ptrace(PTRACE_DETACH, pid, 0, 0)
//sys Rmdir(path string) (errno int)
//sys Setdomainname(p []byte) (errno int)
//sys Sethostname(p []byte) (errno int)
-//sys Setpgid(pid int, pgid int) (errno int)
-//sys Setrlimit(resource int, rlim *Rlimit) (errno int)
-//sys Setsid() (pid int, errno int)
-//sys Settimeofday(tv *Timeval) (errno int)
-//sys Setuid(uid int) (errno int)
+//sysnb Setpgid(pid int, pgid int) (errno int)
+//sysnb Setrlimit(resource int, rlim *Rlimit) (errno int)
+//sysnb Setsid() (pid int, errno int)
+//sysnb Settimeofday(tv *Timeval) (errno int)
+//sysnb Setuid(uid int) (errno int)
//sys Symlink(oldpath string, newpath string) (errno int)
//sys Sync()
-//sys Sysinfo(info *Sysinfo_t) (errno int)
+//sysnb Sysinfo(info *Sysinfo_t) (errno int)
//sys Tee(rfd int, wfd int, len int, flags int) (n int64, errno int)
-//sys Tgkill(tgid int, tid int, sig int) (errno int)
-//sys Times(tms *Tms) (ticks uintptr, errno int)
-//sys Umask(mask int) (oldmask int)
-//sys Uname(buf *Utsname) (errno int)
+//sysnb Tgkill(tgid int, tid int, sig int) (errno int)
+//sysnb Times(tms *Tms) (ticks uintptr, errno int)
+//sysnb Umask(mask int) (oldmask int)
+//sysnb Uname(buf *Utsname) (errno int)
//sys Unlink(path string) (errno int)
//sys Unlinkat(dirfd int, path string) (errno int)
+//sys Unmount(target string, flags int) (errno int) = SYS_UMOUNT2
//sys Unshare(flags int) (errno int)
//sys Ustat(dev int, ubuf *Ustat_t) (errno int)
//sys Utime(path string, buf *Utimbuf) (errno int)
@@ -750,6 +797,23 @@ func PtraceDetach(pid int) (errno int) { return ptrace(PTRACE_DETACH, pid, 0, 0)
//sys read(fd int, p *byte, np int) (n int, errno int)
//sys write(fd int, p *byte, np int) (n int, errno int)
+// mmap varies by architecture; see syscall_linux_*.go.
+//sys munmap(addr uintptr, length uintptr) (errno int)
+
+var mapper = &mmapper{
+ active: make(map[*byte][]byte),
+ mmap: mmap,
+ munmap: munmap,
+}
+
+func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, errno int) {
+ return mapper.Mmap(fd, offset, length, prot, flags)
+}
+
+func Munmap(b []byte) (errno int) {
+ return mapper.Munmap(b)
+}
+
/*
* Unimplemented
*/
@@ -842,7 +906,6 @@ func PtraceDetach(pid int) (errno int) { return ptrace(PTRACE_DETACH, pid, 0, 0)
// Quotactl
// Readahead
// Readv
-// Reboot
// RemapFilePages
// Removexattr
// RequestKey
diff --git a/src/pkg/syscall/syscall_linux_386.go b/src/pkg/syscall/syscall_linux_386.go
index 5bd3406de..2b6bdebf8 100644
--- a/src/pkg/syscall/syscall_linux_386.go
+++ b/src/pkg/syscall/syscall_linux_386.go
@@ -31,10 +31,10 @@ func NsecToTimeval(nsec int64) (tv Timeval) {
//sys Fchown(fd int, uid int, gid int) (errno int) = SYS_FCHOWN32
//sys Fstat(fd int, stat *Stat_t) (errno int) = SYS_FSTAT64
//sys Ftruncate(fd int, length int64) (errno int) = SYS_FTRUNCATE64
-//sys Getegid() (egid int) = SYS_GETEGID32
-//sys Geteuid() (euid int) = SYS_GETEUID32
-//sys Getgid() (gid int) = SYS_GETGID32
-//sys Getuid() (uid int) = SYS_GETUID32
+//sysnb Getegid() (egid int) = SYS_GETEGID32
+//sysnb Geteuid() (euid int) = SYS_GETEUID32
+//sysnb Getgid() (gid int) = SYS_GETGID32
+//sysnb Getuid() (uid int) = SYS_GETUID32
//sys Ioperm(from int, num int, on int) (errno int)
//sys Iopl(level int) (errno int)
//sys Lchown(path string, uid int, gid int) (errno int) = SYS_LCHOWN32
@@ -43,26 +43,36 @@ func NsecToTimeval(nsec int64) (tv Timeval) {
//sys Pwrite(fd int, p []byte, offset int64) (n int, errno int) = SYS_PWRITE64
//sys Setfsgid(gid int) (errno int) = SYS_SETFSGID32
//sys Setfsuid(uid int) (errno int) = SYS_SETFSUID32
-//sys Setgid(gid int) (errno int) = SYS_SETGID32
-//sys Setregid(rgid int, egid int) (errno int) = SYS_SETREGID32
-//sys Setresgid(rgid int, egid int, sgid int) (errno int) = SYS_SETRESGID32
-//sys Setresuid(ruid int, euid int, suid int) (errno int) = SYS_SETRESUID32
-//sys Setreuid(ruid int, euid int) (errno int) = SYS_SETREUID32
+//sysnb Setgid(gid int) (errno int) = SYS_SETGID32
+//sysnb Setregid(rgid int, egid int) (errno int) = SYS_SETREGID32
+//sysnb Setresgid(rgid int, egid int, sgid int) (errno int) = SYS_SETRESGID32
+//sysnb Setresuid(ruid int, euid int, suid int) (errno int) = SYS_SETRESUID32
+//sysnb Setreuid(ruid int, euid int) (errno int) = SYS_SETREUID32
//sys Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int, errno int)
//sys Stat(path string, stat *Stat_t) (errno int) = SYS_STAT64
//sys SyncFileRange(fd int, off int64, n int64, flags int) (errno int)
//sys Truncate(path string, length int64) (errno int) = SYS_TRUNCATE64
-//sys getgroups(n int, list *_Gid_t) (nn int, errno int) = SYS_GETGROUPS32
-//sys setgroups(n int, list *_Gid_t) (errno int) = SYS_SETGROUPS32
+//sysnb getgroups(n int, list *_Gid_t) (nn int, errno int) = SYS_GETGROUPS32
+//sysnb setgroups(n int, list *_Gid_t) (errno int) = SYS_SETGROUPS32
//sys Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, errno int) = SYS__NEWSELECT
+//sys mmap2(addr uintptr, length uintptr, prot int, flags int, fd int, pageOffset uintptr) (xaddr uintptr, errno int)
+
+func mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, errno int) {
+ page := uintptr(offset / 4096)
+ if offset != int64(page)*4096 {
+ return 0, EINVAL
+ }
+ return mmap2(addr, length, prot, flags, fd, page)
+}
+
// Underlying system call writes to newoffset via pointer.
// Implemented in assembly to avoid allocation.
func Seek(fd int, offset int64, whence int) (newoffset int64, errno int)
// Vsyscalls on amd64.
-//sys Gettimeofday(tv *Timeval) (errno int)
-//sys Time(t *Time_t) (tt Time_t, errno int)
+//sysnb Gettimeofday(tv *Timeval) (errno int)
+//sysnb Time(t *Time_t) (tt Time_t, errno int)
// On x86 Linux, all the socket calls go through an extra indirection,
// I think because the 5-register system call interface can't handle
@@ -93,6 +103,7 @@ const (
)
func socketcall(call int, a0, a1, a2, a3, a4, a5 uintptr) (n int, errno int)
+func rawsocketcall(call int, a0, a1, a2, a3, a4, a5 uintptr) (n int, errno int)
func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, errno int) {
fd, errno = socketcall(_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), 0, 0, 0)
@@ -100,17 +111,17 @@ func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, errno int) {
}
func getsockname(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int) {
- _, errno = socketcall(_GETSOCKNAME, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), 0, 0, 0)
+ _, errno = rawsocketcall(_GETSOCKNAME, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), 0, 0, 0)
return
}
func getpeername(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int) {
- _, errno = socketcall(_GETPEERNAME, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), 0, 0, 0)
+ _, errno = rawsocketcall(_GETPEERNAME, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), 0, 0, 0)
return
}
func socketpair(domain int, typ int, flags int, fd *[2]int) (errno int) {
- _, errno = socketcall(_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(flags), uintptr(unsafe.Pointer(fd)), 0, 0)
+ _, errno = rawsocketcall(_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(flags), uintptr(unsafe.Pointer(fd)), 0, 0)
return
}
@@ -125,7 +136,12 @@ func connect(s int, addr uintptr, addrlen _Socklen) (errno int) {
}
func socket(domain int, typ int, proto int) (fd int, errno int) {
- fd, errno = socketcall(_SOCKET, uintptr(domain), uintptr(typ), uintptr(proto), 0, 0, 0)
+ fd, errno = rawsocketcall(_SOCKET, uintptr(domain), uintptr(typ), uintptr(proto), 0, 0, 0)
+ return
+}
+
+func getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (errno int) {
+ _, errno = socketcall(_GETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(unsafe.Pointer(vallen)), 0)
return
}
diff --git a/src/pkg/syscall/syscall_linux_amd64.go b/src/pkg/syscall/syscall_linux_amd64.go
index ae108bd18..f2a4acfe9 100644
--- a/src/pkg/syscall/syscall_linux_amd64.go
+++ b/src/pkg/syscall/syscall_linux_amd64.go
@@ -9,10 +9,10 @@ package syscall
//sys Fstat(fd int, stat *Stat_t) (errno int)
//sys Fstatfs(fd int, buf *Statfs_t) (errno int)
//sys Ftruncate(fd int, length int64) (errno int)
-//sys Getegid() (egid int)
-//sys Geteuid() (euid int)
-//sys Getgid() (gid int)
-//sys Getuid() (uid int)
+//sysnb Getegid() (egid int)
+//sysnb Geteuid() (euid int)
+//sysnb Getgid() (gid int)
+//sysnb Getuid() (uid int)
//sys Ioperm(from int, num int, on int) (errno int)
//sys Iopl(level int) (errno int)
//sys Lchown(path string, uid int, gid int) (errno int)
@@ -24,11 +24,11 @@ package syscall
//sys Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, errno int)
//sys Setfsgid(gid int) (errno int)
//sys Setfsuid(uid int) (errno int)
-//sys Setgid(gid int) (errno int)
-//sys Setregid(rgid int, egid int) (errno int)
-//sys Setresgid(rgid int, egid int, sgid int) (errno int)
-//sys Setresuid(ruid int, euid int, suid int) (errno int)
-//sys Setreuid(ruid int, euid int) (errno int)
+//sysnb Setgid(gid int) (errno int)
+//sysnb Setregid(rgid int, egid int) (errno int)
+//sysnb Setresgid(rgid int, egid int, sgid int) (errno int)
+//sysnb Setresuid(ruid int, euid int, suid int) (errno int)
+//sysnb Setreuid(ruid int, euid int) (errno int)
//sys Shutdown(fd int, how int) (errno int)
//sys Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, errno int)
//sys Stat(path string, stat *Stat_t) (errno int)
@@ -38,17 +38,19 @@ package syscall
//sys accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, errno int)
//sys bind(s int, addr uintptr, addrlen _Socklen) (errno int)
//sys connect(s int, addr uintptr, addrlen _Socklen) (errno int)
-//sys getgroups(n int, list *_Gid_t) (nn int, errno int)
-//sys setgroups(n int, list *_Gid_t) (errno int)
+//sysnb getgroups(n int, list *_Gid_t) (nn int, errno int)
+//sysnb setgroups(n int, list *_Gid_t) (errno int)
+//sys getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (errno int)
//sys setsockopt(s int, level int, name int, val uintptr, vallen int) (errno int)
-//sys socket(domain int, typ int, proto int) (fd int, errno int)
-//sys socketpair(domain int, typ int, proto int, fd *[2]int) (errno int)
-//sys getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int)
-//sys getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int)
+//sysnb socket(domain int, typ int, proto int) (fd int, errno int)
+//sysnb socketpair(domain int, typ int, proto int, fd *[2]int) (errno int)
+//sysnb getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int)
+//sysnb getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int)
//sys recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (n int, errno int)
//sys sendto(s int, buf []byte, flags int, to uintptr, addrlen _Socklen) (errno int)
//sys recvmsg(s int, msg *Msghdr, flags int) (n int, errno int)
//sys sendmsg(s int, msg *Msghdr, flags int) (errno int)
+//sys mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, errno int)
func Getpagesize() int { return 4096 }
diff --git a/src/pkg/syscall/syscall_linux_arm.go b/src/pkg/syscall/syscall_linux_arm.go
index 1fc7a7b18..6472c4db5 100644
--- a/src/pkg/syscall/syscall_linux_arm.go
+++ b/src/pkg/syscall/syscall_linux_arm.go
@@ -55,15 +55,16 @@ func Seek(fd int, offset int64, whence int) (newoffset int64, errno int)
//sys accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, errno int)
//sys bind(s int, addr uintptr, addrlen _Socklen) (errno int)
//sys connect(s int, addr uintptr, addrlen _Socklen) (errno int)
-//sys getgroups(n int, list *_Gid_t) (nn int, errno int) = SYS_GETGROUPS32
-//sys setgroups(n int, list *_Gid_t) (errno int) = SYS_SETGROUPS32
+//sysnb getgroups(n int, list *_Gid_t) (nn int, errno int) = SYS_GETGROUPS32
+//sysnb setgroups(n int, list *_Gid_t) (errno int) = SYS_SETGROUPS32
+//sys getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (errno int)
//sys setsockopt(s int, level int, name int, val uintptr, vallen int) (errno int)
-//sys socket(domain int, typ int, proto int) (fd int, errno int)
-//sys getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int)
-//sys getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int)
+//sysnb socket(domain int, typ int, proto int) (fd int, errno int)
+//sysnb getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int)
+//sysnb getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int)
//sys recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (n int, errno int)
//sys sendto(s int, buf []byte, flags int, to uintptr, addrlen _Socklen) (errno int)
-//sys socketpair(domain int, typ int, flags int, fd *[2]int) (errno int)
+//sysnb socketpair(domain int, typ int, flags int, fd *[2]int) (errno int)
//sys recvmsg(s int, msg *Msghdr, flags int) (n int, errno int)
//sys sendmsg(s int, msg *Msghdr, flags int) (errno int)
@@ -72,21 +73,21 @@ func Seek(fd int, offset int64, whence int) (newoffset int64, errno int)
//sys Fstat(fd int, stat *Stat_t) (errno int) = SYS_FSTAT64
//sys Fstatfs(fd int, buf *Statfs_t) (errno int) = SYS_FSTATFS64
//sys Ftruncate(fd int, length int64) (errno int) = SYS_FTRUNCATE64
-//sys Getegid() (egid int)
-//sys Geteuid() (euid int)
-//sys Getgid() (gid int)
-//sys Getuid() (uid int)
+//sysnb Getegid() (egid int)
+//sysnb Geteuid() (euid int)
+//sysnb Getgid() (gid int)
+//sysnb Getuid() (uid int)
//sys Lchown(path string, uid int, gid int) (errno int)
//sys Listen(s int, n int) (errno int)
//sys Lstat(path string, stat *Stat_t) (errno int) = SYS_LSTAT64
//sys Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, errno int) = SYS__NEWSELECT
//sys Setfsgid(gid int) (errno int)
//sys Setfsuid(uid int) (errno int)
-//sys Setgid(gid int) (errno int)
-//sys Setregid(rgid int, egid int) (errno int)
-//sys Setresgid(rgid int, egid int, sgid int) (errno int)
-//sys Setresuid(ruid int, euid int, suid int) (errno int)
-//sys Setreuid(ruid int, euid int) (errno int)
+//sysnb Setgid(gid int) (errno int)
+//sysnb Setregid(rgid int, egid int) (errno int)
+//sysnb Setresgid(rgid int, egid int, sgid int) (errno int)
+//sysnb Setresuid(ruid int, euid int, suid int) (errno int)
+//sysnb Setreuid(ruid int, euid int) (errno int)
//sys Shutdown(fd int, how int) (errno int)
//sys Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int, errno int)
//sys Stat(path string, stat *Stat_t) (errno int) = SYS_STAT64
@@ -94,8 +95,18 @@ func Seek(fd int, offset int64, whence int) (newoffset int64, errno int)
//sys Truncate(path string, length int64) (errno int) = SYS_TRUNCATE64
// Vsyscalls on amd64.
-//sys Gettimeofday(tv *Timeval) (errno int)
-//sys Time(t *Time_t) (tt Time_t, errno int)
+//sysnb Gettimeofday(tv *Timeval) (errno int)
+//sysnb Time(t *Time_t) (tt Time_t, errno int)
+
+//sys mmap2(addr uintptr, length uintptr, prot int, flags int, fd int, pageOffset uintptr) (xaddr uintptr, errno int)
+
+func mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, errno int) {
+ page := uintptr(offset / 4096)
+ if offset != int64(page)*4096 {
+ return 0, EINVAL
+ }
+ return mmap2(addr, length, prot, flags, fd, page)
+}
// TODO(kaib): add support for tracing
func (r *PtraceRegs) PC() uint64 { return 0 }
diff --git a/src/pkg/syscall/syscall_plan9.go b/src/pkg/syscall/syscall_plan9.go
new file mode 100644
index 000000000..831cbddb2
--- /dev/null
+++ b/src/pkg/syscall/syscall_plan9.go
@@ -0,0 +1,343 @@
+// 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.
+
+// Plan 9 system calls.
+// This file is compiled as ordinary Go code,
+// but it is also input to mksyscall,
+// which parses the //sys lines and generates system call stubs.
+// Note that sometimes we use a lowercase //sys name and
+// wrap it in our own nicer implementation.
+
+package syscall
+
+import "unsafe"
+
+const OS = "plan9"
+
+const ImplementsGetwd = true
+
+// An Error can represent any printable error condition.
+type Error interface {
+ String() string
+}
+
+// ErrorString implements Error's String method by returning itself.
+type ErrorString string
+
+func (e ErrorString) String() string { return string(e) }
+
+// NewError converts s to an ErrorString, which satisfies the Error interface.
+func NewError(s string) Error { return ErrorString(s) }
+
+var (
+ Stdin = 0
+ Stdout = 1
+ Stderr = 2
+
+ EISDIR Error = NewError("file is a directory")
+)
+
+func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err string)
+func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err string)
+func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr)
+func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
+
+func atoi(b []byte) (n uint) {
+ n = 0
+ for i := 0; i < len(b); i++ {
+ n = n*10 + uint(b[i]-'0')
+ }
+ return
+}
+
+func cstring(s []byte) string {
+ for i := range s {
+ if s[i] == 0 {
+ return string(s[0:i])
+ }
+ }
+ return string(s)
+}
+
+func errstr() string {
+ var buf [ERRMAX]byte
+
+ RawSyscall(SYS_ERRSTR, uintptr(unsafe.Pointer(&buf[0])), uintptr(len(buf)), 0)
+
+ buf[len(buf)-1] = 0
+ return cstring(buf[:])
+}
+
+func Getpagesize() int { return 4096 }
+
+//sys exits(msg *byte)
+func Exits(msg *string) {
+ if msg == nil {
+ exits(nil)
+ }
+
+ exits(StringBytePtr(*msg))
+}
+
+func Exit(code int) {
+ if code == 0 {
+ Exits(nil)
+ }
+
+ msg := itoa(code)
+ Exits(&msg)
+}
+
+func readnum(path string) (uint, Error) {
+ var b [12]byte
+
+ fd, e := Open(path, O_RDONLY)
+ if e != nil {
+ return 0, e
+ }
+ defer Close(fd)
+
+ n, e := Pread(fd, b[:], 0)
+
+ if e != nil {
+ return 0, e
+ }
+
+ m := 0
+ for ; m < n && b[m] == ' '; m++ {
+ }
+
+ return atoi(b[m : n-1]), nil
+}
+
+func Getpid() (pid int) {
+ n, _ := readnum("#c/pid")
+ return int(n)
+}
+
+func Getppid() (ppid int) {
+ n, _ := readnum("#c/ppid")
+ return int(n)
+}
+
+
+func Read(fd int, p []byte) (n int, err Error) {
+ return Pread(fd, p, -1)
+}
+
+func Write(fd int, p []byte) (n int, err Error) {
+ return Pwrite(fd, p, -1)
+}
+
+func Getwd() (wd string, err Error) {
+ fd, e := Open(".", O_RDONLY)
+
+ if e != nil {
+ return "", e
+ }
+ defer Close(fd)
+
+ return Fd2path(fd)
+}
+
+//sys fd2path(fd int, buf []byte) (err Error)
+func Fd2path(fd int) (path string, err Error) {
+ var buf [512]byte
+
+ e := fd2path(fd, buf[:])
+ if e != nil {
+ return "", e
+ }
+ return cstring(buf[:]), nil
+}
+
+//sys pipe(p *[2]_C_int) (err Error)
+func Pipe(p []int) (err Error) {
+ if len(p) != 2 {
+ return NewError("bad arg in system call")
+ }
+ var pp [2]_C_int
+ err = pipe(&pp)
+ p[0] = int(pp[0])
+ p[1] = int(pp[1])
+ return
+}
+
+
+//sys sleep(millisecs int32) (err Error)
+func Sleep(nsec int64) (err Error) {
+ return sleep(int32((nsec + 999) / 1e6)) // round up to microsecond
+}
+
+// Underlying system call writes to newoffset via pointer.
+// Implemented in assembly to avoid allocation.
+func seek(placeholder uintptr, fd int, offset int64, whence int) (newoffset int64, err string)
+
+func Seek(fd int, offset int64, whence int) (newoffset int64, err Error) {
+ newoffset, e := seek(0, fd, offset, whence)
+
+ err = nil
+ if newoffset == -1 {
+ err = NewError(e)
+ }
+ return
+}
+
+func Mkdir(path string, mode uint32) (err Error) {
+ fd, err := Create(path, O_RDONLY, DMDIR|mode)
+
+ if fd != -1 {
+ Close(fd)
+ }
+
+ return
+}
+
+type Waitmsg struct {
+ Pid int
+ Time [3]uint32
+ Msg string
+}
+
+//sys await(s []byte) (n int, err Error)
+func Await(w *Waitmsg) (err Error) {
+ var buf [512]byte
+ var f [5][]byte
+
+ n, err := await(buf[:])
+
+ if err != nil || w == nil {
+ return
+ }
+
+ nf := 0
+ p := 0
+ for i := 0; i < n && nf < len(f)-1; i++ {
+ if buf[i] == ' ' {
+ f[nf] = buf[p:i]
+ p = i + 1
+ nf++
+ }
+ }
+ f[nf] = buf[p:]
+ nf++
+
+ if nf != len(f) {
+ return NewError("invalid wait message")
+ }
+ w.Pid = int(atoi(f[0]))
+ w.Time[0] = uint32(atoi(f[1]))
+ w.Time[1] = uint32(atoi(f[2]))
+ w.Time[2] = uint32(atoi(f[3]))
+ w.Msg = string(f[4])
+ return
+}
+
+func Unmount(name, old string) (err Error) {
+ oldp := uintptr(unsafe.Pointer(StringBytePtr(old)))
+
+ var r0 uintptr
+ var e string
+
+ // bind(2) man page: If name is zero, everything bound or mounted upon old is unbound or unmounted.
+ if name == "" {
+ r0, _, e = Syscall(SYS_UNMOUNT, _zero, oldp, 0)
+ } else {
+ r0, _, e = Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(StringBytePtr(name))), oldp, 0)
+ }
+
+ err = nil
+ if int(r0) == -1 {
+ err = NewError(e)
+ }
+ return
+}
+
+func Fchdir(fd int) (err Error) {
+ path, err := Fd2path(fd)
+
+ if err != nil {
+ return
+ }
+
+ return Chdir(path)
+}
+
+type Timeval struct {
+ Sec int32
+ Usec int32
+}
+
+func NsecToTimeval(nsec int64) (tv Timeval) {
+ nsec += 999 // round up to microsecond
+ tv.Usec = int32(nsec % 1e9 / 1e3)
+ tv.Sec = int32(nsec / 1e9)
+ return
+}
+
+func DecodeBintime(b []byte) (nsec int64, err Error) {
+ if len(b) != 8 {
+ return -1, NewError("bad /dev/bintime format")
+ }
+ err = nil
+ nsec = int64(b[0])<<56 |
+ int64(b[1])<<48 |
+ int64(b[2])<<40 |
+ int64(b[3])<<32 |
+ int64(b[4])<<24 |
+ int64(b[5])<<16 |
+ int64(b[6])<<8 |
+ int64(b[7])
+ return
+}
+
+func Gettimeofday(tv *Timeval) (err Error) {
+ // TODO(paulzhol):
+ // avoid reopening a file descriptor for /dev/bintime on each call,
+ // use lower-level calls to avoid allocation.
+
+ var b [8]byte
+ var nsec int64
+
+ fd, e := Open("/dev/bintime", O_RDONLY)
+ if e != nil {
+ return e
+ }
+ defer Close(fd)
+
+ if _, e = Pread(fd, b[:], 0); e != nil {
+ return e
+ }
+
+ if nsec, e = DecodeBintime(b[:]); e != nil {
+ return e
+ }
+ *tv = NsecToTimeval(nsec)
+
+ return e
+}
+
+func Getegid() (egid int) { return -1 }
+func Geteuid() (euid int) { return -1 }
+func Getgid() (gid int) { return -1 }
+func Getuid() (uid int) { return -1 }
+
+func Getgroups() (gids []int, err Error) {
+ return make([]int, 0), nil
+}
+
+//sys Dup(oldfd int, newfd int) (fd int, err Error)
+//sys Open(path string, mode int) (fd int, err Error)
+//sys Create(path string, mode int, perm uint32) (fd int, err Error)
+//sys Remove(path string) (err Error)
+//sys Pread(fd int, p []byte, offset int64) (n int, err Error)
+//sys Pwrite(fd int, p []byte, offset int64) (n int, err Error)
+//sys Close(fd int) (err Error)
+//sys Chdir(path string) (err Error)
+//sys Bind(name string, old string, flag int) (err Error)
+//sys Mount(fd int, afd int, old string, flag int, aname string) (err Error)
+//sys Stat(path string, edir []byte) (n int, err Error)
+//sys Fstat(fd int, edir []byte) (n int, err Error)
+//sys Wstat(path string, edir []byte) (err Error)
+//sys Fwstat(fd int, edir []byte) (err Error)
diff --git a/src/pkg/syscall/syscall_plan9_386.go b/src/pkg/syscall/syscall_plan9_386.go
new file mode 100644
index 000000000..e82b540b4
--- /dev/null
+++ b/src/pkg/syscall/syscall_plan9_386.go
@@ -0,0 +1,5 @@
+// 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 syscall
diff --git a/src/pkg/syscall/syscall_unix.go b/src/pkg/syscall/syscall_unix.go
index c01eca17a..a77e40bc6 100644
--- a/src/pkg/syscall/syscall_unix.go
+++ b/src/pkg/syscall/syscall_unix.go
@@ -13,10 +13,11 @@ var (
func Syscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr)
func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr)
+func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
func Errstr(errno int) string {
if errno < 0 || errno >= int(len(errors)) {
- return "error " + str(errno)
+ return "error " + itoa(errno)
}
return errors[errno]
}
diff --git a/src/pkg/syscall/syscall_windows.go b/src/pkg/syscall/syscall_windows.go
index 394e06442..4ac2154c8 100644
--- a/src/pkg/syscall/syscall_windows.go
+++ b/src/pkg/syscall/syscall_windows.go
@@ -135,7 +135,7 @@ func NewCallback(fn interface{}) uintptr
//sys CreateIoCompletionPort(filehandle int32, cphandle int32, key uint32, threadcnt uint32) (handle int32, errno int)
//sys GetQueuedCompletionStatus(cphandle int32, qty *uint32, key *uint32, overlapped **Overlapped, timeout uint32) (errno int)
//sys CancelIo(s uint32) (errno int)
-//sys CreateProcess(appName *int16, commandLine *uint16, procSecurity *int16, threadSecurity *int16, inheritHandles bool, creationFlags uint32, env *uint16, currentDir *uint16, startupInfo *StartupInfo, outProcInfo *ProcessInformation) (errno int) = CreateProcessW
+//sys CreateProcess(appName *uint16, commandLine *uint16, procSecurity *SecurityAttributes, threadSecurity *SecurityAttributes, inheritHandles bool, creationFlags uint32, env *uint16, currentDir *uint16, startupInfo *StartupInfo, outProcInfo *ProcessInformation) (errno int) = CreateProcessW
//sys OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle uint32, errno int)
//sys GetExitCodeProcess(handle uint32, exitcode *uint32) (errno int)
//sys GetStartupInfo(startupInfo *StartupInfo) (errno int) = GetStartupInfoW
@@ -160,6 +160,7 @@ func NewCallback(fn interface{}) uintptr
//sys LocalFree(hmem uint32) (handle uint32, errno int) [failretval!=0]
//sys SetHandleInformation(handle int32, mask uint32, flags uint32) (errno int)
//sys FlushFileBuffers(handle int32) (errno int)
+//sys GetFullPathName(path *uint16, buflen uint32, buf *uint16, fname **uint16) (n uint32, errno int) = kernel32.GetFullPathNameW
// syscall interface implementation for other packages
@@ -174,7 +175,7 @@ func Errstr(errno int) string {
b := make([]uint16, 300)
n, err := FormatMessage(flags, 0, uint32(errno), 0, b, nil)
if err != 0 {
- return "error " + str(errno) + " (FormatMessage failed with err=" + str(err) + ")"
+ return "error " + itoa(errno) + " (FormatMessage failed with err=" + itoa(err) + ")"
}
// trim terminating \r and \n
for ; n > 0 && (b[n-1] == '\n' || b[n-1] == '\r'); n-- {
@@ -684,7 +685,7 @@ func (w WaitStatus) Continued() bool { return false }
func (w WaitStatus) StopSignal() int { return -1 }
-func (w WaitStatus) Signaled() bool { return true }
+func (w WaitStatus) Signaled() bool { return false }
func (w WaitStatus) TrapCause() int { return -1 }
diff --git a/src/pkg/syscall/types_freebsd.c b/src/pkg/syscall/types_freebsd.c
index 6fc814134..9d65683ef 100644
--- a/src/pkg/syscall/types_freebsd.c
+++ b/src/pkg/syscall/types_freebsd.c
@@ -26,6 +26,7 @@ Input to godefs. See also mkerrors.sh and mkall.sh
#include <sys/types.h>
#include <sys/un.h>
#include <sys/wait.h>
+#include <net/bpf.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/route.h>
@@ -167,3 +168,23 @@ typedef struct if_data $IfData;
typedef struct ifa_msghdr $IfaMsghdr;
typedef struct rt_msghdr $RtMsghdr;
typedef struct rt_metrics $RtMetrics;
+
+// Berkeley packet filter
+
+enum {
+ $SizeofBpfVersion = sizeof(struct bpf_version),
+ $SizeofBpfStat = sizeof(struct bpf_stat),
+ $SizeofBpfZbuf = sizeof(struct bpf_zbuf),
+ $SizeofBpfProgram = sizeof(struct bpf_program),
+ $SizeofBpfInsn = sizeof(struct bpf_insn),
+ $SizeofBpfHdr = sizeof(struct bpf_hdr),
+ $SizeofBpfZbufHeader = sizeof(struct bpf_zbuf_header),
+};
+
+typedef struct bpf_version $BpfVersion;
+typedef struct bpf_stat $BpfStat;
+typedef struct bpf_zbuf $BpfZbuf;
+typedef struct bpf_program $BpfProgram;
+typedef struct bpf_insn $BpfInsn;
+typedef struct bpf_hdr $BpfHdr;
+typedef struct bpf_zbuf_header $BpfZbufHeader;
diff --git a/src/pkg/syscall/types_plan9.c b/src/pkg/syscall/types_plan9.c
new file mode 100644
index 000000000..6308ce08b
--- /dev/null
+++ b/src/pkg/syscall/types_plan9.c
@@ -0,0 +1,115 @@
+// 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.
+
+/*
+Input to godefs. See also mkerrors.sh and mkall.sh
+*/
+
+typedef unsigned short ushort;
+typedef unsigned char uchar;
+typedef unsigned long ulong;
+typedef unsigned int uint;
+typedef long long vlong;
+typedef unsigned long long uvlong;
+
+typedef int $_C_int;
+
+enum {
+ OREAD = 0, // open for read
+ OWRITE = 1, // write
+ ORDWR = 2, // read and write
+
+ $O_RDONLY = OREAD,
+ $O_WRONLY = OWRITE,
+ $O_RDWR = ORDWR,
+
+ OEXEC = 3, // execute, == read but check execute permission
+ OTRUNC = 16, // or'ed in (except for exec), truncate file first
+ OCEXEC = 32, // or'ed in, close on exec
+
+ $O_CLOEXEC = OCEXEC,
+
+ ORCLOSE = 64, // or'ed in, remove on close
+ OEXCL = 0x1000, // or'ed in, exclusive use (create only)
+ $O_EXCL = OEXCL,
+
+ $STATMAX = 65535U,
+ $ERRMAX = 128,
+
+ $MORDER = 0x0003, // mask for bits defining order of mounting
+ $MREPL = 0x0000, // mount replaces object
+ $MBEFORE = 0x0001, // mount goes before others in union directory
+ $MAFTER = 0x0002, // mount goes after others in union directory
+ $MCREATE = 0x0004, // permit creation in mounted directory
+ $MCACHE = 0x0010, // cache some data
+ $MMASK = 0x0017, // all bits on
+
+ $RFNAMEG = (1<<0),
+ $RFENVG = (1<<1),
+ $RFFDG = (1<<2),
+ $RFNOTEG = (1<<3),
+ $RFPROC = (1<<4),
+ $RFMEM = (1<<5),
+ $RFNOWAIT = (1<<6),
+ $RFCNAMEG = (1<<10),
+ $RFCENVG = (1<<11),
+ $RFCFDG = (1<<12),
+ $RFREND = (1<<13),
+ $RFNOMNT = (1<<14),
+
+ // bits in Qid.type
+ $QTDIR = 0x80, // type bit for directories
+ $QTAPPEND = 0x40, // type bit for append only files
+ $QTEXCL = 0x20, // type bit for exclusive use files
+ $QTMOUNT = 0x10, // type bit for mounted channel
+ $QTAUTH = 0x08, // type bit for authentication file
+ $QTTMP = 0x04, // type bit for not-backed-up file
+ $QTFILE = 0x00, // plain file
+
+
+ // bits in Dir.mode
+ $DMDIR = 0x80000000, // mode bit for directories
+ $DMAPPEND = 0x40000000, // mode bit for append only files
+ $DMEXCL = 0x20000000, // mode bit for exclusive use files
+ $DMMOUNT = 0x10000000, // mode bit for mounted channel
+ $DMAUTH = 0x08000000, // mode bit for authentication file
+ $DMTMP = 0x04000000, // mode bit for non-backed-up files
+ $DMREAD = 0x4, // mode bit for read permission
+ $DMWRITE = 0x2, // mode bit for write permission
+ $DMEXEC = 0x1, // mode bit for execute permission
+
+ BIT8SZ = 1,
+ BIT16SZ = 2,
+ BIT32SZ = 4,
+ BIT64SZ = 8,
+ QIDSZ = BIT8SZ+BIT32SZ+BIT64SZ,
+
+ // STATFIXLEN includes leading 16-bit count
+ // The count, however, excludes itself; total size is BIT16SZ+count
+ $STATFIXLEN = BIT16SZ+QIDSZ+5*BIT16SZ+4*BIT32SZ+1*BIT64SZ, // amount of fixed length data in a stat buffer
+};
+
+
+struct Prof // Per process profiling
+{
+ struct Plink *pp; // known to be 0(ptr)
+ struct Plink *next; // known to be 4(ptr)
+ struct Plink *last;
+ struct Plink *first;
+ ulong pid;
+ ulong what;
+};
+
+struct Tos {
+ struct Prof prof;
+ uvlong cyclefreq; // cycle clock frequency if there is one, 0 otherwise
+ vlong kcycles; // cycles spent in kernel
+ vlong pcycles; // cycles spent in process (kernel + user)
+ ulong pid; // might as well put the pid here
+ ulong clock;
+ // top of stack is here
+};
+
+typedef struct Prof $Prof;
+typedef struct Tos $Tos;
diff --git a/src/pkg/syscall/zerrors_darwin_386.go b/src/pkg/syscall/zerrors_darwin_386.go
index 52b986228..48f563f44 100644
--- a/src/pkg/syscall/zerrors_darwin_386.go
+++ b/src/pkg/syscall/zerrors_darwin_386.go
@@ -196,7 +196,6 @@ const (
F_GETLK = 0x7
F_GETOWN = 0x5
F_GETPATH = 0x32
- F_GETPROTECTIONCLASS = 0x3e
F_GLOBAL_NOCACHE = 0x37
F_LOG2PHYS = 0x31
F_MARKDEPENDENCY = 0x3c
@@ -213,7 +212,6 @@ const (
F_SETLK = 0x8
F_SETLKW = 0x9
F_SETOWN = 0x6
- F_SETPROTECTIONCLASS = 0x3f
F_SETSIZE = 0x2b
F_THAW_FS = 0x36
F_UNLCK = 0x2
@@ -461,6 +459,20 @@ const (
IP_TOS = 0x3
IP_TRAFFIC_MGT_BACKGROUND = 0x41
IP_TTL = 0x4
+ MAP_ANON = 0x1000
+ MAP_COPY = 0x2
+ MAP_FILE = 0
+ MAP_FIXED = 0x10
+ MAP_HASSEMAPHORE = 0x200
+ MAP_NOCACHE = 0x400
+ MAP_NOEXTEND = 0x100
+ MAP_NORESERVE = 0x40
+ MAP_PRIVATE = 0x2
+ MAP_RENAME = 0x20
+ MAP_RESERVED0080 = 0x80
+ MAP_SHARED = 0x1
+ MCL_CURRENT = 0x1
+ MCL_FUTURE = 0x2
MSG_CTRUNC = 0x20
MSG_DONTROUTE = 0x4
MSG_DONTWAIT = 0x80
@@ -477,6 +489,11 @@ const (
MSG_TRUNC = 0x10
MSG_WAITALL = 0x40
MSG_WAITSTREAM = 0x200
+ MS_ASYNC = 0x1
+ MS_DEACTIVATE = 0x8
+ MS_INVALIDATE = 0x2
+ MS_KILLPAGES = 0x4
+ MS_SYNC = 0x10
NAME_MAX = 0xff
NET_RT_DUMP = 0x1
NET_RT_DUMP2 = 0x7
@@ -509,6 +526,10 @@ const (
O_SYNC = 0x80
O_TRUNC = 0x400
O_WRONLY = 0x1
+ PROT_EXEC = 0x4
+ PROT_NONE = 0
+ PROT_READ = 0x1
+ PROT_WRITE = 0x2
RTAX_AUTHOR = 0x6
RTAX_BRD = 0x7
RTAX_DST = 0
@@ -535,7 +556,6 @@ const (
RTF_DYNAMIC = 0x10
RTF_GATEWAY = 0x2
RTF_HOST = 0x4
- RTF_IFREF = 0x4000000
RTF_IFSCOPE = 0x1000000
RTF_LLINFO = 0x400
RTF_LOCAL = 0x200000
@@ -629,7 +649,6 @@ const (
SIOCDIFADDR = 0x80206919
SIOCDIFPHYADDR = 0x80206941
SIOCDLIFADDR = 0x8118691f
- SIOCGDRVSPEC = 0xc01c697b
SIOCGETSGCNT = 0xc014721c
SIOCGETVIFCNT = 0xc014721b
SIOCGETVLAN = 0xc020697f
@@ -661,10 +680,8 @@ const (
SIOCGLOWAT = 0x40047303
SIOCGPGRP = 0x40047309
SIOCIFCREATE = 0xc0206978
- SIOCIFCREATE2 = 0xc020697a
SIOCIFDESTROY = 0x80206979
SIOCRSLVMULTI = 0xc008693b
- SIOCSDRVSPEC = 0x801c697b
SIOCSETVLAN = 0x8020697e
SIOCSHIWAT = 0x80047300
SIOCSIFADDR = 0x8020690c
diff --git a/src/pkg/syscall/zerrors_darwin_amd64.go b/src/pkg/syscall/zerrors_darwin_amd64.go
index 4e7a174ea..840ea13ce 100644
--- a/src/pkg/syscall/zerrors_darwin_amd64.go
+++ b/src/pkg/syscall/zerrors_darwin_amd64.go
@@ -196,7 +196,6 @@ const (
F_GETLK = 0x7
F_GETOWN = 0x5
F_GETPATH = 0x32
- F_GETPROTECTIONCLASS = 0x3e
F_GLOBAL_NOCACHE = 0x37
F_LOG2PHYS = 0x31
F_MARKDEPENDENCY = 0x3c
@@ -213,7 +212,6 @@ const (
F_SETLK = 0x8
F_SETLKW = 0x9
F_SETOWN = 0x6
- F_SETPROTECTIONCLASS = 0x3f
F_SETSIZE = 0x2b
F_THAW_FS = 0x36
F_UNLCK = 0x2
@@ -461,6 +459,20 @@ const (
IP_TOS = 0x3
IP_TRAFFIC_MGT_BACKGROUND = 0x41
IP_TTL = 0x4
+ MAP_ANON = 0x1000
+ MAP_COPY = 0x2
+ MAP_FILE = 0
+ MAP_FIXED = 0x10
+ MAP_HASSEMAPHORE = 0x200
+ MAP_NOCACHE = 0x400
+ MAP_NOEXTEND = 0x100
+ MAP_NORESERVE = 0x40
+ MAP_PRIVATE = 0x2
+ MAP_RENAME = 0x20
+ MAP_RESERVED0080 = 0x80
+ MAP_SHARED = 0x1
+ MCL_CURRENT = 0x1
+ MCL_FUTURE = 0x2
MSG_CTRUNC = 0x20
MSG_DONTROUTE = 0x4
MSG_DONTWAIT = 0x80
@@ -477,6 +489,11 @@ const (
MSG_TRUNC = 0x10
MSG_WAITALL = 0x40
MSG_WAITSTREAM = 0x200
+ MS_ASYNC = 0x1
+ MS_DEACTIVATE = 0x8
+ MS_INVALIDATE = 0x2
+ MS_KILLPAGES = 0x4
+ MS_SYNC = 0x10
NAME_MAX = 0xff
NET_RT_DUMP = 0x1
NET_RT_DUMP2 = 0x7
@@ -509,6 +526,10 @@ const (
O_SYNC = 0x80
O_TRUNC = 0x400
O_WRONLY = 0x1
+ PROT_EXEC = 0x4
+ PROT_NONE = 0
+ PROT_READ = 0x1
+ PROT_WRITE = 0x2
RTAX_AUTHOR = 0x6
RTAX_BRD = 0x7
RTAX_DST = 0
@@ -535,7 +556,6 @@ const (
RTF_DYNAMIC = 0x10
RTF_GATEWAY = 0x2
RTF_HOST = 0x4
- RTF_IFREF = 0x4000000
RTF_IFSCOPE = 0x1000000
RTF_LLINFO = 0x400
RTF_LOCAL = 0x200000
@@ -629,7 +649,6 @@ const (
SIOCDIFADDR = 0x80206919
SIOCDIFPHYADDR = 0x80206941
SIOCDLIFADDR = 0x8118691f
- SIOCGDRVSPEC = 0xc028697b
SIOCGETSGCNT = 0xc014721c
SIOCGETVIFCNT = 0xc014721b
SIOCGETVLAN = 0xc020697f
@@ -661,10 +680,8 @@ const (
SIOCGLOWAT = 0x40047303
SIOCGPGRP = 0x40047309
SIOCIFCREATE = 0xc0206978
- SIOCIFCREATE2 = 0xc020697a
SIOCIFDESTROY = 0x80206979
SIOCRSLVMULTI = 0xc010693b
- SIOCSDRVSPEC = 0x8028697b
SIOCSETVLAN = 0x8020697e
SIOCSHIWAT = 0x80047300
SIOCSIFADDR = 0x8020690c
diff --git a/src/pkg/syscall/zerrors_freebsd_386.go b/src/pkg/syscall/zerrors_freebsd_386.go
index d3d46ce03..9c21c71eb 100644
--- a/src/pkg/syscall/zerrors_freebsd_386.go
+++ b/src/pkg/syscall/zerrors_freebsd_386.go
@@ -9,805 +9,1013 @@ package syscall
// Constants
const (
- AF_APPLETALK = 0x10
- AF_ARP = 0x23
- AF_ATM = 0x1e
- AF_BLUETOOTH = 0x24
- AF_CCITT = 0xa
- AF_CHAOS = 0x5
- AF_CNT = 0x15
- AF_COIP = 0x14
- AF_DATAKIT = 0x9
- AF_DECnet = 0xc
- AF_DLI = 0xd
- AF_E164 = 0x1a
- AF_ECMA = 0x8
- AF_HYLINK = 0xf
- AF_IEEE80211 = 0x25
- AF_IMPLINK = 0x3
- AF_INET = 0x2
- AF_INET6 = 0x1c
- AF_IPX = 0x17
- AF_ISDN = 0x1a
- AF_ISO = 0x7
- AF_LAT = 0xe
- AF_LINK = 0x12
- AF_LOCAL = 0x1
- AF_MAX = 0x26
- AF_NATM = 0x1d
- AF_NETBIOS = 0x6
- AF_NETGRAPH = 0x20
- AF_OSI = 0x7
- AF_PUP = 0x4
- AF_ROUTE = 0x11
- AF_SCLUSTER = 0x22
- AF_SIP = 0x18
- AF_SLOW = 0x21
- AF_SNA = 0xb
- AF_UNIX = 0x1
- AF_UNSPEC = 0
- AF_VENDOR00 = 0x27
- AF_VENDOR01 = 0x29
- AF_VENDOR02 = 0x2b
- AF_VENDOR03 = 0x2d
- AF_VENDOR04 = 0x2f
- AF_VENDOR05 = 0x31
- AF_VENDOR06 = 0x33
- AF_VENDOR07 = 0x35
- AF_VENDOR08 = 0x37
- AF_VENDOR09 = 0x39
- AF_VENDOR10 = 0x3b
- AF_VENDOR11 = 0x3d
- AF_VENDOR12 = 0x3f
- AF_VENDOR13 = 0x41
- AF_VENDOR14 = 0x43
- AF_VENDOR15 = 0x45
- AF_VENDOR16 = 0x47
- AF_VENDOR17 = 0x49
- AF_VENDOR18 = 0x4b
- AF_VENDOR19 = 0x4d
- AF_VENDOR20 = 0x4f
- AF_VENDOR21 = 0x51
- AF_VENDOR22 = 0x53
- AF_VENDOR23 = 0x55
- AF_VENDOR24 = 0x57
- AF_VENDOR25 = 0x59
- AF_VENDOR26 = 0x5b
- AF_VENDOR27 = 0x5d
- AF_VENDOR28 = 0x5f
- AF_VENDOR29 = 0x61
- AF_VENDOR30 = 0x63
- AF_VENDOR31 = 0x65
- AF_VENDOR32 = 0x67
- AF_VENDOR33 = 0x69
- AF_VENDOR34 = 0x6b
- AF_VENDOR35 = 0x6d
- AF_VENDOR36 = 0x6f
- AF_VENDOR37 = 0x71
- AF_VENDOR38 = 0x73
- AF_VENDOR39 = 0x75
- AF_VENDOR40 = 0x77
- AF_VENDOR41 = 0x79
- AF_VENDOR42 = 0x7b
- AF_VENDOR43 = 0x7d
- AF_VENDOR44 = 0x7f
- AF_VENDOR45 = 0x81
- AF_VENDOR46 = 0x83
- AF_VENDOR47 = 0x85
- CTL_MAXNAME = 0x18
- CTL_NET = 0x4
- E2BIG = 0x7
- EACCES = 0xd
- EADDRINUSE = 0x30
- EADDRNOTAVAIL = 0x31
- EAFNOSUPPORT = 0x2f
- EAGAIN = 0x23
- EALREADY = 0x25
- EAUTH = 0x50
- EBADF = 0x9
- EBADMSG = 0x59
- EBADRPC = 0x48
- EBUSY = 0x10
- ECANCELED = 0x55
- ECHILD = 0xa
- ECONNABORTED = 0x35
- ECONNREFUSED = 0x3d
- ECONNRESET = 0x36
- EDEADLK = 0xb
- EDESTADDRREQ = 0x27
- EDOM = 0x21
- EDOOFUS = 0x58
- EDQUOT = 0x45
- EEXIST = 0x11
- EFAULT = 0xe
- EFBIG = 0x1b
- EFTYPE = 0x4f
- EHOSTDOWN = 0x40
- EHOSTUNREACH = 0x41
- EIDRM = 0x52
- EILSEQ = 0x56
- EINPROGRESS = 0x24
- EINTR = 0x4
- EINVAL = 0x16
- EIO = 0x5
- EISCONN = 0x38
- EISDIR = 0x15
- ELAST = 0x5d
- ELOOP = 0x3e
- EMFILE = 0x18
- EMLINK = 0x1f
- EMSGSIZE = 0x28
- EMULTIHOP = 0x5a
- ENAMETOOLONG = 0x3f
- ENEEDAUTH = 0x51
- ENETDOWN = 0x32
- ENETRESET = 0x34
- ENETUNREACH = 0x33
- ENFILE = 0x17
- ENOATTR = 0x57
- ENOBUFS = 0x37
- ENODEV = 0x13
- ENOENT = 0x2
- ENOEXEC = 0x8
- ENOLCK = 0x4d
- ENOLINK = 0x5b
- ENOMEM = 0xc
- ENOMSG = 0x53
- ENOPROTOOPT = 0x2a
- ENOSPC = 0x1c
- ENOSYS = 0x4e
- ENOTBLK = 0xf
- ENOTCAPABLE = 0x5d
- ENOTCONN = 0x39
- ENOTDIR = 0x14
- ENOTEMPTY = 0x42
- ENOTSOCK = 0x26
- ENOTSUP = 0x2d
- ENOTTY = 0x19
- ENXIO = 0x6
- EOPNOTSUPP = 0x2d
- EOVERFLOW = 0x54
- EPERM = 0x1
- EPFNOSUPPORT = 0x2e
- EPIPE = 0x20
- EPROCLIM = 0x43
- EPROCUNAVAIL = 0x4c
- EPROGMISMATCH = 0x4b
- EPROGUNAVAIL = 0x4a
- EPROTO = 0x5c
- EPROTONOSUPPORT = 0x2b
- EPROTOTYPE = 0x29
- ERANGE = 0x22
- EREMOTE = 0x47
- EROFS = 0x1e
- ERPCMISMATCH = 0x49
- ESHUTDOWN = 0x3a
- ESOCKTNOSUPPORT = 0x2c
- ESPIPE = 0x1d
- ESRCH = 0x3
- ESTALE = 0x46
- ETIMEDOUT = 0x3c
- ETOOMANYREFS = 0x3b
- ETXTBSY = 0x1a
- EUSERS = 0x44
- EVFILT_AIO = -0x3
- EVFILT_FS = -0x9
- EVFILT_LIO = -0xa
- EVFILT_PROC = -0x5
- EVFILT_READ = -0x1
- EVFILT_SIGNAL = -0x6
- EVFILT_SYSCOUNT = 0xb
- EVFILT_TIMER = -0x7
- EVFILT_USER = -0xb
- EVFILT_VNODE = -0x4
- EVFILT_WRITE = -0x2
- EV_ADD = 0x1
- EV_CLEAR = 0x20
- EV_DELETE = 0x2
- EV_DISABLE = 0x8
- EV_DISPATCH = 0x80
- EV_ENABLE = 0x4
- EV_EOF = 0x8000
- EV_ERROR = 0x4000
- EV_FLAG1 = 0x2000
- EV_ONESHOT = 0x10
- EV_RECEIPT = 0x40
- EV_SYSFLAGS = 0xf000
- EWOULDBLOCK = 0x23
- EXDEV = 0x12
- FD_CLOEXEC = 0x1
- FD_SETSIZE = 0x400
- F_CANCEL = 0x5
- F_DUP2FD = 0xa
- F_DUPFD = 0
- F_GETFD = 0x1
- F_GETFL = 0x3
- F_GETLK = 0xb
- F_GETOWN = 0x5
- F_OGETLK = 0x7
- F_OSETLK = 0x8
- F_OSETLKW = 0x9
- F_RDAHEAD = 0x10
- F_RDLCK = 0x1
- F_READAHEAD = 0xf
- F_SETFD = 0x2
- F_SETFL = 0x4
- F_SETLK = 0xc
- F_SETLKW = 0xd
- F_SETLK_REMOTE = 0xe
- F_SETOWN = 0x6
- F_UNLCK = 0x2
- F_UNLCKSYS = 0x4
- F_WRLCK = 0x3
- IFF_ALLMULTI = 0x200
- IFF_ALTPHYS = 0x4000
- IFF_BROADCAST = 0x2
- IFF_CANTCHANGE = 0x208f72
- IFF_DEBUG = 0x4
- IFF_DRV_OACTIVE = 0x400
- IFF_DRV_RUNNING = 0x40
- IFF_DYING = 0x200000
- IFF_LINK0 = 0x1000
- IFF_LINK1 = 0x2000
- IFF_LINK2 = 0x4000
- IFF_LOOPBACK = 0x8
- IFF_MONITOR = 0x40000
- IFF_MULTICAST = 0x8000
- IFF_NOARP = 0x80
- IFF_OACTIVE = 0x400
- IFF_POINTOPOINT = 0x10
- IFF_PPROMISC = 0x20000
- IFF_PROMISC = 0x100
- IFF_RENAMING = 0x400000
- IFF_RUNNING = 0x40
- IFF_SIMPLEX = 0x800
- IFF_SMART = 0x20
- IFF_STATICARP = 0x80000
- IFF_UP = 0x1
- IFNAMSIZ = 0x10
- IN_CLASSA_HOST = 0xffffff
- IN_CLASSA_MAX = 0x80
- IN_CLASSA_NET = 0xff000000
- IN_CLASSA_NSHIFT = 0x18
- IN_CLASSB_HOST = 0xffff
- IN_CLASSB_MAX = 0x10000
- IN_CLASSB_NET = 0xffff0000
- IN_CLASSB_NSHIFT = 0x10
- IN_CLASSC_HOST = 0xff
- IN_CLASSC_NET = 0xffffff00
- IN_CLASSC_NSHIFT = 0x8
- IN_CLASSD_HOST = 0xfffffff
- IN_CLASSD_NET = 0xf0000000
- IN_CLASSD_NSHIFT = 0x1c
- IN_LOOPBACKNET = 0x7f
- IPPROTO_3PC = 0x22
- IPPROTO_ADFS = 0x44
- IPPROTO_AH = 0x33
- IPPROTO_AHIP = 0x3d
- IPPROTO_APES = 0x63
- IPPROTO_ARGUS = 0xd
- IPPROTO_AX25 = 0x5d
- IPPROTO_BHA = 0x31
- IPPROTO_BLT = 0x1e
- IPPROTO_BRSATMON = 0x4c
- IPPROTO_CARP = 0x70
- IPPROTO_CFTP = 0x3e
- IPPROTO_CHAOS = 0x10
- IPPROTO_CMTP = 0x26
- IPPROTO_CPHB = 0x49
- IPPROTO_CPNX = 0x48
- IPPROTO_DDP = 0x25
- IPPROTO_DGP = 0x56
- IPPROTO_DIVERT = 0x102
- IPPROTO_DONE = 0x101
- IPPROTO_DSTOPTS = 0x3c
- IPPROTO_EGP = 0x8
- IPPROTO_EMCON = 0xe
- IPPROTO_ENCAP = 0x62
- IPPROTO_EON = 0x50
- IPPROTO_ESP = 0x32
- IPPROTO_ETHERIP = 0x61
- IPPROTO_FRAGMENT = 0x2c
- IPPROTO_GGP = 0x3
- IPPROTO_GMTP = 0x64
- IPPROTO_GRE = 0x2f
- IPPROTO_HELLO = 0x3f
- IPPROTO_HMP = 0x14
- IPPROTO_HOPOPTS = 0
- IPPROTO_ICMP = 0x1
- IPPROTO_ICMPV6 = 0x3a
- IPPROTO_IDP = 0x16
- IPPROTO_IDPR = 0x23
- IPPROTO_IDRP = 0x2d
- IPPROTO_IGMP = 0x2
- IPPROTO_IGP = 0x55
- IPPROTO_IGRP = 0x58
- IPPROTO_IL = 0x28
- IPPROTO_INLSP = 0x34
- IPPROTO_INP = 0x20
- IPPROTO_IP = 0
- IPPROTO_IPCOMP = 0x6c
- IPPROTO_IPCV = 0x47
- IPPROTO_IPEIP = 0x5e
- IPPROTO_IPIP = 0x4
- IPPROTO_IPPC = 0x43
- IPPROTO_IPV4 = 0x4
- IPPROTO_IPV6 = 0x29
- IPPROTO_IRTP = 0x1c
- IPPROTO_KRYPTOLAN = 0x41
- IPPROTO_LARP = 0x5b
- IPPROTO_LEAF1 = 0x19
- IPPROTO_LEAF2 = 0x1a
- IPPROTO_MAX = 0x100
- IPPROTO_MAXID = 0x34
- IPPROTO_MEAS = 0x13
- IPPROTO_MHRP = 0x30
- IPPROTO_MICP = 0x5f
- IPPROTO_MOBILE = 0x37
- IPPROTO_MTP = 0x5c
- IPPROTO_MUX = 0x12
- IPPROTO_ND = 0x4d
- IPPROTO_NHRP = 0x36
- IPPROTO_NONE = 0x3b
- IPPROTO_NSP = 0x1f
- IPPROTO_NVPII = 0xb
- IPPROTO_OLD_DIVERT = 0xfe
- IPPROTO_OSPFIGP = 0x59
- IPPROTO_PFSYNC = 0xf0
- IPPROTO_PGM = 0x71
- IPPROTO_PIGP = 0x9
- IPPROTO_PIM = 0x67
- IPPROTO_PRM = 0x15
- IPPROTO_PUP = 0xc
- IPPROTO_PVP = 0x4b
- IPPROTO_RAW = 0xff
- IPPROTO_RCCMON = 0xa
- IPPROTO_RDP = 0x1b
- IPPROTO_ROUTING = 0x2b
- IPPROTO_RSVP = 0x2e
- IPPROTO_RVD = 0x42
- IPPROTO_SATEXPAK = 0x40
- IPPROTO_SATMON = 0x45
- IPPROTO_SCCSP = 0x60
- IPPROTO_SCTP = 0x84
- IPPROTO_SDRP = 0x2a
- IPPROTO_SEP = 0x21
- IPPROTO_SKIP = 0x39
- IPPROTO_SPACER = 0x7fff
- IPPROTO_SRPC = 0x5a
- IPPROTO_ST = 0x7
- IPPROTO_SVMTP = 0x52
- IPPROTO_SWIPE = 0x35
- IPPROTO_TCF = 0x57
- IPPROTO_TCP = 0x6
- IPPROTO_TLSP = 0x38
- IPPROTO_TP = 0x1d
- IPPROTO_TPXX = 0x27
- IPPROTO_TRUNK1 = 0x17
- IPPROTO_TRUNK2 = 0x18
- IPPROTO_TTP = 0x54
- IPPROTO_UDP = 0x11
- IPPROTO_VINES = 0x53
- IPPROTO_VISA = 0x46
- IPPROTO_VMTP = 0x51
- IPPROTO_WBEXPAK = 0x4f
- IPPROTO_WBMON = 0x4e
- IPPROTO_WSN = 0x4a
- IPPROTO_XNET = 0xf
- IPPROTO_XTP = 0x24
- IPV6_AUTOFLOWLABEL = 0x3b
- IPV6_BINDANY = 0x40
- IPV6_BINDV6ONLY = 0x1b
- IPV6_CHECKSUM = 0x1a
- IPV6_DEFAULT_MULTICAST_HOPS = 0x1
- IPV6_DEFAULT_MULTICAST_LOOP = 0x1
- IPV6_DEFHLIM = 0x40
- IPV6_DONTFRAG = 0x3e
- IPV6_DSTOPTS = 0x32
- IPV6_FAITH = 0x1d
- IPV6_FLOWINFO_MASK = 0xffffff0f
- IPV6_FLOWLABEL_MASK = 0xffff0f00
- IPV6_FRAGTTL = 0x78
- IPV6_FW_ADD = 0x1e
- IPV6_FW_DEL = 0x1f
- IPV6_FW_FLUSH = 0x20
- IPV6_FW_GET = 0x22
- IPV6_FW_ZERO = 0x21
- IPV6_HLIMDEC = 0x1
- IPV6_HOPLIMIT = 0x2f
- IPV6_HOPOPTS = 0x31
- IPV6_IPSEC_POLICY = 0x1c
- IPV6_JOIN_GROUP = 0xc
- IPV6_LEAVE_GROUP = 0xd
- IPV6_MAXHLIM = 0xff
- IPV6_MAXOPTHDR = 0x800
- IPV6_MAXPACKET = 0xffff
- IPV6_MAX_GROUP_SRC_FILTER = 0x200
- IPV6_MAX_MEMBERSHIPS = 0xfff
- IPV6_MAX_SOCK_SRC_FILTER = 0x80
- IPV6_MIN_MEMBERSHIPS = 0x1f
- IPV6_MMTU = 0x500
- IPV6_MSFILTER = 0x4a
- IPV6_MULTICAST_HOPS = 0xa
- IPV6_MULTICAST_IF = 0x9
- IPV6_MULTICAST_LOOP = 0xb
- IPV6_NEXTHOP = 0x30
- IPV6_PATHMTU = 0x2c
- IPV6_PKTINFO = 0x2e
- IPV6_PORTRANGE = 0xe
- IPV6_PORTRANGE_DEFAULT = 0
- IPV6_PORTRANGE_HIGH = 0x1
- IPV6_PORTRANGE_LOW = 0x2
- IPV6_PREFER_TEMPADDR = 0x3f
- IPV6_RECVDSTOPTS = 0x28
- IPV6_RECVHOPLIMIT = 0x25
- IPV6_RECVHOPOPTS = 0x27
- IPV6_RECVPATHMTU = 0x2b
- IPV6_RECVPKTINFO = 0x24
- IPV6_RECVRTHDR = 0x26
- IPV6_RECVTCLASS = 0x39
- IPV6_RTHDR = 0x33
- IPV6_RTHDRDSTOPTS = 0x23
- IPV6_RTHDR_LOOSE = 0
- IPV6_RTHDR_STRICT = 0x1
- IPV6_RTHDR_TYPE_0 = 0
- IPV6_SOCKOPT_RESERVED1 = 0x3
- IPV6_TCLASS = 0x3d
- IPV6_UNICAST_HOPS = 0x4
- IPV6_USE_MIN_MTU = 0x2a
- IPV6_V6ONLY = 0x1b
- IPV6_VERSION = 0x60
- IPV6_VERSION_MASK = 0xf0
- IP_ADD_MEMBERSHIP = 0xc
- IP_ADD_SOURCE_MEMBERSHIP = 0x46
- IP_BINDANY = 0x18
- IP_BLOCK_SOURCE = 0x48
- IP_DEFAULT_MULTICAST_LOOP = 0x1
- IP_DEFAULT_MULTICAST_TTL = 0x1
- IP_DF = 0x4000
- IP_DONTFRAG = 0x43
- IP_DROP_MEMBERSHIP = 0xd
- IP_DROP_SOURCE_MEMBERSHIP = 0x47
- IP_DUMMYNET3 = 0x31
- IP_DUMMYNET_CONFIGURE = 0x3c
- IP_DUMMYNET_DEL = 0x3d
- IP_DUMMYNET_FLUSH = 0x3e
- IP_DUMMYNET_GET = 0x40
- IP_FAITH = 0x16
- IP_FW3 = 0x30
- IP_FW_ADD = 0x32
- IP_FW_DEL = 0x33
- IP_FW_FLUSH = 0x34
- IP_FW_GET = 0x36
- IP_FW_NAT_CFG = 0x38
- IP_FW_NAT_DEL = 0x39
- IP_FW_NAT_GET_CONFIG = 0x3a
- IP_FW_NAT_GET_LOG = 0x3b
- IP_FW_RESETLOG = 0x37
- IP_FW_TABLE_ADD = 0x28
- IP_FW_TABLE_DEL = 0x29
- IP_FW_TABLE_FLUSH = 0x2a
- IP_FW_TABLE_GETSIZE = 0x2b
- IP_FW_TABLE_LIST = 0x2c
- IP_FW_ZERO = 0x35
- IP_HDRINCL = 0x2
- IP_IPSEC_POLICY = 0x15
- IP_MAXPACKET = 0xffff
- IP_MAX_GROUP_SRC_FILTER = 0x200
- IP_MAX_MEMBERSHIPS = 0xfff
- IP_MAX_SOCK_MUTE_FILTER = 0x80
- IP_MAX_SOCK_SRC_FILTER = 0x80
- IP_MAX_SOURCE_FILTER = 0x400
- IP_MF = 0x2000
- IP_MINTTL = 0x42
- IP_MIN_MEMBERSHIPS = 0x1f
- IP_MSFILTER = 0x4a
- IP_MSS = 0x240
- IP_MULTICAST_IF = 0x9
- IP_MULTICAST_LOOP = 0xb
- IP_MULTICAST_TTL = 0xa
- IP_MULTICAST_VIF = 0xe
- IP_OFFMASK = 0x1fff
- IP_ONESBCAST = 0x17
- IP_OPTIONS = 0x1
- IP_PORTRANGE = 0x13
- IP_PORTRANGE_DEFAULT = 0
- IP_PORTRANGE_HIGH = 0x1
- IP_PORTRANGE_LOW = 0x2
- IP_RECVDSTADDR = 0x7
- IP_RECVIF = 0x14
- IP_RECVOPTS = 0x5
- IP_RECVRETOPTS = 0x6
- IP_RECVTTL = 0x41
- IP_RETOPTS = 0x8
- IP_RF = 0x8000
- IP_RSVP_OFF = 0x10
- IP_RSVP_ON = 0xf
- IP_RSVP_VIF_OFF = 0x12
- IP_RSVP_VIF_ON = 0x11
- IP_SENDSRCADDR = 0x7
- IP_TOS = 0x3
- IP_TTL = 0x4
- IP_UNBLOCK_SOURCE = 0x49
- MSG_COMPAT = 0x8000
- MSG_CTRUNC = 0x20
- MSG_DONTROUTE = 0x4
- MSG_DONTWAIT = 0x80
- MSG_EOF = 0x100
- MSG_EOR = 0x8
- MSG_NBIO = 0x4000
- MSG_NOSIGNAL = 0x20000
- MSG_NOTIFICATION = 0x2000
- MSG_OOB = 0x1
- MSG_PEEK = 0x2
- MSG_TRUNC = 0x10
- MSG_WAITALL = 0x40
- NET_RT_DUMP = 0x1
- NET_RT_FLAGS = 0x2
- NET_RT_IFLIST = 0x3
- NET_RT_IFMALIST = 0x4
- NET_RT_MAXID = 0x5
- O_ACCMODE = 0x3
- O_APPEND = 0x8
- O_ASYNC = 0x40
- O_CREAT = 0x200
- O_DIRECT = 0x10000
- O_DIRECTORY = 0x20000
- O_EXCL = 0x800
- O_EXEC = 0x40000
- O_EXLOCK = 0x20
- O_FSYNC = 0x80
- O_NDELAY = 0x4
- O_NOCTTY = 0x8000
- O_NOFOLLOW = 0x100
- O_NONBLOCK = 0x4
- O_RDONLY = 0
- O_RDWR = 0x2
- O_SHLOCK = 0x10
- O_SYNC = 0x80
- O_TRUNC = 0x400
- O_TTY_INIT = 0x80000
- O_WRONLY = 0x1
- RTAX_AUTHOR = 0x6
- RTAX_BRD = 0x7
- RTAX_DST = 0
- RTAX_GATEWAY = 0x1
- RTAX_GENMASK = 0x3
- RTAX_IFA = 0x5
- RTAX_IFP = 0x4
- RTAX_MAX = 0x8
- RTAX_NETMASK = 0x2
- RTA_AUTHOR = 0x40
- RTA_BRD = 0x80
- RTA_DST = 0x1
- RTA_GATEWAY = 0x2
- RTA_GENMASK = 0x8
- RTA_IFA = 0x20
- RTA_IFP = 0x10
- RTA_NETMASK = 0x4
- RTF_BLACKHOLE = 0x1000
- RTF_BROADCAST = 0x400000
- RTF_DONE = 0x40
- RTF_DYNAMIC = 0x10
- RTF_FMASK = 0x1004d808
- RTF_GATEWAY = 0x2
- RTF_HOST = 0x4
- RTF_LLDATA = 0x400
- RTF_LLINFO = 0x400
- RTF_LOCAL = 0x200000
- RTF_MODIFIED = 0x20
- RTF_MULTICAST = 0x800000
- RTF_PINNED = 0x100000
- RTF_PRCLONING = 0x10000
- RTF_PROTO1 = 0x8000
- RTF_PROTO2 = 0x4000
- RTF_PROTO3 = 0x40000
- RTF_REJECT = 0x8
- RTF_RNH_LOCKED = 0x40000000
- RTF_STATIC = 0x800
- RTF_STICKY = 0x10000000
- RTF_UP = 0x1
- RTF_XRESOLVE = 0x200
- RTM_ADD = 0x1
- RTM_CHANGE = 0x3
- RTM_DELADDR = 0xd
- RTM_DELETE = 0x2
- RTM_DELMADDR = 0x10
- RTM_GET = 0x4
- RTM_IEEE80211 = 0x12
- RTM_IFANNOUNCE = 0x11
- RTM_IFINFO = 0xe
- RTM_LOCK = 0x8
- RTM_LOSING = 0x5
- RTM_MISS = 0x7
- RTM_NEWADDR = 0xc
- RTM_NEWMADDR = 0xf
- RTM_OLDADD = 0x9
- RTM_OLDDEL = 0xa
- RTM_REDIRECT = 0x6
- RTM_RESOLVE = 0xb
- RTM_RTTUNIT = 0xf4240
- RTM_VERSION = 0x5
- RTV_EXPIRE = 0x4
- RTV_HOPCOUNT = 0x2
- RTV_MTU = 0x1
- RTV_RPIPE = 0x8
- RTV_RTT = 0x40
- RTV_RTTVAR = 0x80
- RTV_SPIPE = 0x10
- RTV_SSTHRESH = 0x20
- RTV_WEIGHT = 0x100
- SCM_BINTIME = 0x4
- SCM_CREDS = 0x3
- SCM_RIGHTS = 0x1
- SCM_TIMESTAMP = 0x2
- SHUT_RD = 0
- SHUT_RDWR = 0x2
- SHUT_WR = 0x1
- SIGABRT = 0x6
- SIGALRM = 0xe
- SIGBUS = 0xa
- SIGCHLD = 0x14
- SIGCONT = 0x13
- SIGEMT = 0x7
- SIGFPE = 0x8
- SIGHUP = 0x1
- SIGILL = 0x4
- SIGINFO = 0x1d
- SIGINT = 0x2
- SIGIO = 0x17
- SIGIOT = 0x6
- SIGKILL = 0x9
- SIGLWP = 0x20
- SIGPIPE = 0xd
- SIGPROF = 0x1b
- SIGQUIT = 0x3
- SIGSEGV = 0xb
- SIGSTOP = 0x11
- SIGSYS = 0xc
- SIGTERM = 0xf
- SIGTHR = 0x20
- SIGTRAP = 0x5
- SIGTSTP = 0x12
- SIGTTIN = 0x15
- SIGTTOU = 0x16
- SIGURG = 0x10
- SIGUSR1 = 0x1e
- SIGUSR2 = 0x1f
- SIGVTALRM = 0x1a
- SIGWINCH = 0x1c
- SIGXCPU = 0x18
- SIGXFSZ = 0x19
- SIOCADDMULTI = 0x80206931
- SIOCADDRT = 0x8030720a
- SIOCAIFADDR = 0x8040691a
- SIOCAIFGROUP = 0x80246987
- SIOCALIFADDR = 0x8118691b
- SIOCATMARK = 0x40047307
- SIOCDELMULTI = 0x80206932
- SIOCDELRT = 0x8030720b
- SIOCDIFADDR = 0x80206919
- SIOCDIFGROUP = 0x80246989
- SIOCDIFPHYADDR = 0x80206949
- SIOCDLIFADDR = 0x8118691d
- SIOCGDRVSPEC = 0xc01c697b
- SIOCGETSGCNT = 0xc0147210
- SIOCGETVIFCNT = 0xc014720f
- SIOCGHIWAT = 0x40047301
- SIOCGIFADDR = 0xc0206921
- SIOCGIFBRDADDR = 0xc0206923
- SIOCGIFCAP = 0xc020691f
- SIOCGIFCONF = 0xc0086924
- SIOCGIFDESCR = 0xc020692a
- SIOCGIFDSTADDR = 0xc0206922
- SIOCGIFFLAGS = 0xc0206911
- SIOCGIFGENERIC = 0xc020693a
- SIOCGIFGMEMB = 0xc024698a
- SIOCGIFGROUP = 0xc0246988
- SIOCGIFINDEX = 0xc0206920
- SIOCGIFMAC = 0xc0206926
- SIOCGIFMEDIA = 0xc0286938
- SIOCGIFMETRIC = 0xc0206917
- SIOCGIFMTU = 0xc0206933
- SIOCGIFNETMASK = 0xc0206925
- SIOCGIFPDSTADDR = 0xc0206948
- SIOCGIFPHYS = 0xc0206935
- SIOCGIFPSRCADDR = 0xc0206947
- SIOCGIFSTATUS = 0xc331693b
- SIOCGLIFADDR = 0xc118691c
- SIOCGLIFPHYADDR = 0xc118694b
- SIOCGLOWAT = 0x40047303
- SIOCGPGRP = 0x40047309
- SIOCGPRIVATE_0 = 0xc0206950
- SIOCGPRIVATE_1 = 0xc0206951
- SIOCIFCREATE = 0xc020697a
- SIOCIFCREATE2 = 0xc020697c
- SIOCIFDESTROY = 0x80206979
- SIOCIFGCLONERS = 0xc00c6978
- SIOCSDRVSPEC = 0x801c697b
- SIOCSHIWAT = 0x80047300
- SIOCSIFADDR = 0x8020690c
- SIOCSIFBRDADDR = 0x80206913
- SIOCSIFCAP = 0x8020691e
- SIOCSIFDESCR = 0x80206929
- SIOCSIFDSTADDR = 0x8020690e
- SIOCSIFFLAGS = 0x80206910
- SIOCSIFGENERIC = 0x80206939
- SIOCSIFLLADDR = 0x8020693c
- SIOCSIFMAC = 0x80206927
- SIOCSIFMEDIA = 0xc0206937
- SIOCSIFMETRIC = 0x80206918
- SIOCSIFMTU = 0x80206934
- SIOCSIFNAME = 0x80206928
- SIOCSIFNETMASK = 0x80206916
- SIOCSIFPHYADDR = 0x80406946
- SIOCSIFPHYS = 0x80206936
- SIOCSIFRVNET = 0xc020695b
- SIOCSIFVNET = 0xc020695a
- SIOCSLIFPHYADDR = 0x8118694a
- SIOCSLOWAT = 0x80047302
- SIOCSPGRP = 0x80047308
- SOCK_DGRAM = 0x2
- SOCK_MAXADDRLEN = 0xff
- SOCK_RAW = 0x3
- SOCK_RDM = 0x4
- SOCK_SEQPACKET = 0x5
- SOCK_STREAM = 0x1
- SOL_SOCKET = 0xffff
- SOMAXCONN = 0x80
- SO_ACCEPTCONN = 0x2
- SO_ACCEPTFILTER = 0x1000
- SO_BINTIME = 0x2000
- SO_BROADCAST = 0x20
- SO_DEBUG = 0x1
- SO_DONTROUTE = 0x10
- SO_ERROR = 0x1007
- SO_KEEPALIVE = 0x8
- SO_LABEL = 0x1009
- SO_LINGER = 0x80
- SO_LISTENINCQLEN = 0x1013
- SO_LISTENQLEN = 0x1012
- SO_LISTENQLIMIT = 0x1011
- SO_NOSIGPIPE = 0x800
- SO_NO_DDP = 0x8000
- SO_NO_OFFLOAD = 0x4000
- SO_OOBINLINE = 0x100
- SO_PEERLABEL = 0x1010
- SO_RCVBUF = 0x1002
- SO_RCVLOWAT = 0x1004
- SO_RCVTIMEO = 0x1006
- SO_REUSEADDR = 0x4
- SO_REUSEPORT = 0x200
- SO_SETFIB = 0x1014
- SO_SNDBUF = 0x1001
- SO_SNDLOWAT = 0x1003
- SO_SNDTIMEO = 0x1005
- SO_TIMESTAMP = 0x400
- SO_TYPE = 0x1008
- SO_USELOOPBACK = 0x40
- TCP_CA_NAME_MAX = 0x10
- TCP_CONGESTION = 0x40
- TCP_INFO = 0x20
- TCP_MAXBURST = 0x4
- TCP_MAXHLEN = 0x3c
- TCP_MAXOLEN = 0x28
- TCP_MAXSEG = 0x2
- TCP_MAXWIN = 0xffff
- TCP_MAX_SACK = 0x4
- TCP_MAX_WINSHIFT = 0xe
- TCP_MD5SIG = 0x10
- TCP_MINMSS = 0xd8
- TCP_MSS = 0x200
- TCP_NODELAY = 0x1
- TCP_NOOPT = 0x8
- TCP_NOPUSH = 0x4
- WCONTINUED = 0x4
- WCOREFLAG = 0x80
- WLINUXCLONE = 0x80000000
- WNOHANG = 0x1
- WNOWAIT = 0x8
- WSTOPPED = 0x2
- WUNTRACED = 0x2
+ AF_APPLETALK = 0x10
+ AF_ARP = 0x23
+ AF_ATM = 0x1e
+ AF_BLUETOOTH = 0x24
+ AF_CCITT = 0xa
+ AF_CHAOS = 0x5
+ AF_CNT = 0x15
+ AF_COIP = 0x14
+ AF_DATAKIT = 0x9
+ AF_DECnet = 0xc
+ AF_DLI = 0xd
+ AF_E164 = 0x1a
+ AF_ECMA = 0x8
+ AF_HYLINK = 0xf
+ AF_IEEE80211 = 0x25
+ AF_IMPLINK = 0x3
+ AF_INET = 0x2
+ AF_INET6 = 0x1c
+ AF_IPX = 0x17
+ AF_ISDN = 0x1a
+ AF_ISO = 0x7
+ AF_LAT = 0xe
+ AF_LINK = 0x12
+ AF_LOCAL = 0x1
+ AF_MAX = 0x26
+ AF_NATM = 0x1d
+ AF_NETBIOS = 0x6
+ AF_NETGRAPH = 0x20
+ AF_OSI = 0x7
+ AF_PUP = 0x4
+ AF_ROUTE = 0x11
+ AF_SCLUSTER = 0x22
+ AF_SIP = 0x18
+ AF_SLOW = 0x21
+ AF_SNA = 0xb
+ AF_UNIX = 0x1
+ AF_UNSPEC = 0
+ AF_VENDOR00 = 0x27
+ AF_VENDOR01 = 0x29
+ AF_VENDOR02 = 0x2b
+ AF_VENDOR03 = 0x2d
+ AF_VENDOR04 = 0x2f
+ AF_VENDOR05 = 0x31
+ AF_VENDOR06 = 0x33
+ AF_VENDOR07 = 0x35
+ AF_VENDOR08 = 0x37
+ AF_VENDOR09 = 0x39
+ AF_VENDOR10 = 0x3b
+ AF_VENDOR11 = 0x3d
+ AF_VENDOR12 = 0x3f
+ AF_VENDOR13 = 0x41
+ AF_VENDOR14 = 0x43
+ AF_VENDOR15 = 0x45
+ AF_VENDOR16 = 0x47
+ AF_VENDOR17 = 0x49
+ AF_VENDOR18 = 0x4b
+ AF_VENDOR19 = 0x4d
+ AF_VENDOR20 = 0x4f
+ AF_VENDOR21 = 0x51
+ AF_VENDOR22 = 0x53
+ AF_VENDOR23 = 0x55
+ AF_VENDOR24 = 0x57
+ AF_VENDOR25 = 0x59
+ AF_VENDOR26 = 0x5b
+ AF_VENDOR27 = 0x5d
+ AF_VENDOR28 = 0x5f
+ AF_VENDOR29 = 0x61
+ AF_VENDOR30 = 0x63
+ AF_VENDOR31 = 0x65
+ AF_VENDOR32 = 0x67
+ AF_VENDOR33 = 0x69
+ AF_VENDOR34 = 0x6b
+ AF_VENDOR35 = 0x6d
+ AF_VENDOR36 = 0x6f
+ AF_VENDOR37 = 0x71
+ AF_VENDOR38 = 0x73
+ AF_VENDOR39 = 0x75
+ AF_VENDOR40 = 0x77
+ AF_VENDOR41 = 0x79
+ AF_VENDOR42 = 0x7b
+ AF_VENDOR43 = 0x7d
+ AF_VENDOR44 = 0x7f
+ AF_VENDOR45 = 0x81
+ AF_VENDOR46 = 0x83
+ AF_VENDOR47 = 0x85
+ BIOCFEEDBACK = 0x8004427c
+ BIOCFLUSH = 0x20004268
+ BIOCGBLEN = 0x40044266
+ BIOCGDIRECTION = 0x40044276
+ BIOCGDLT = 0x4004426a
+ BIOCGDLTLIST = 0xc0084279
+ BIOCGETBUFMODE = 0x4004427d
+ BIOCGETIF = 0x4020426b
+ BIOCGETZMAX = 0x4004427f
+ BIOCGHDRCMPLT = 0x40044274
+ BIOCGRSIG = 0x40044272
+ BIOCGRTIMEOUT = 0x4008426e
+ BIOCGSEESENT = 0x40044276
+ BIOCGSTATS = 0x4008426f
+ BIOCIMMEDIATE = 0x80044270
+ BIOCLOCK = 0x2000427a
+ BIOCPROMISC = 0x20004269
+ BIOCROTZBUF = 0x400c4280
+ BIOCSBLEN = 0xc0044266
+ BIOCSDIRECTION = 0x80044277
+ BIOCSDLT = 0x80044278
+ BIOCSETBUFMODE = 0x8004427e
+ BIOCSETF = 0x80084267
+ BIOCSETFNR = 0x80084282
+ BIOCSETIF = 0x8020426c
+ BIOCSETWF = 0x8008427b
+ BIOCSETZBUF = 0x800c4281
+ BIOCSHDRCMPLT = 0x80044275
+ BIOCSRSIG = 0x80044273
+ BIOCSRTIMEOUT = 0x8008426d
+ BIOCSSEESENT = 0x80044277
+ BIOCVERSION = 0x40044271
+ BPF_A = 0x10
+ BPF_ABS = 0x20
+ BPF_ADD = 0
+ BPF_ALIGNMENT = 0x4
+ BPF_ALU = 0x4
+ BPF_AND = 0x50
+ BPF_B = 0x10
+ BPF_BUFMODE_BUFFER = 0x1
+ BPF_BUFMODE_ZBUF = 0x2
+ BPF_DIV = 0x30
+ BPF_H = 0x8
+ BPF_IMM = 0
+ BPF_IND = 0x40
+ BPF_JA = 0
+ BPF_JEQ = 0x10
+ BPF_JGE = 0x30
+ BPF_JGT = 0x20
+ BPF_JMP = 0x5
+ BPF_JSET = 0x40
+ BPF_K = 0
+ BPF_LD = 0
+ BPF_LDX = 0x1
+ BPF_LEN = 0x80
+ BPF_LSH = 0x60
+ BPF_MAJOR_VERSION = 0x1
+ BPF_MAXBUFSIZE = 0x80000
+ BPF_MAXINSNS = 0x200
+ BPF_MEM = 0x60
+ BPF_MEMWORDS = 0x10
+ BPF_MINBUFSIZE = 0x20
+ BPF_MINOR_VERSION = 0x1
+ BPF_MISC = 0x7
+ BPF_MSH = 0xa0
+ BPF_MUL = 0x20
+ BPF_NEG = 0x80
+ BPF_OR = 0x40
+ BPF_RELEASE = 0x30bb6
+ BPF_RET = 0x6
+ BPF_RSH = 0x70
+ BPF_ST = 0x2
+ BPF_STX = 0x3
+ BPF_SUB = 0x10
+ BPF_TAX = 0
+ BPF_TXA = 0x80
+ BPF_W = 0
+ BPF_X = 0x8
+ CTL_MAXNAME = 0x18
+ CTL_NET = 0x4
+ DLT_A429 = 0xb8
+ DLT_A653_ICM = 0xb9
+ DLT_AIRONET_HEADER = 0x78
+ DLT_APPLE_IP_OVER_IEEE1394 = 0x8a
+ DLT_ARCNET = 0x7
+ DLT_ARCNET_LINUX = 0x81
+ DLT_ATM_CLIP = 0x13
+ DLT_ATM_RFC1483 = 0xb
+ DLT_AURORA = 0x7e
+ DLT_AX25 = 0x3
+ DLT_AX25_KISS = 0xca
+ DLT_BACNET_MS_TP = 0xa5
+ DLT_BLUETOOTH_HCI_H4 = 0xbb
+ DLT_BLUETOOTH_HCI_H4_WITH_PHDR = 0xc9
+ DLT_CAN20B = 0xbe
+ DLT_CHAOS = 0x5
+ DLT_CHDLC = 0x68
+ DLT_CISCO_IOS = 0x76
+ DLT_C_HDLC = 0x68
+ DLT_C_HDLC_WITH_DIR = 0xcd
+ DLT_DOCSIS = 0x8f
+ DLT_ECONET = 0x73
+ DLT_EN10MB = 0x1
+ DLT_EN3MB = 0x2
+ DLT_ENC = 0x6d
+ DLT_ERF = 0xc5
+ DLT_ERF_ETH = 0xaf
+ DLT_ERF_POS = 0xb0
+ DLT_FDDI = 0xa
+ DLT_FLEXRAY = 0xd2
+ DLT_FRELAY = 0x6b
+ DLT_FRELAY_WITH_DIR = 0xce
+ DLT_GCOM_SERIAL = 0xad
+ DLT_GCOM_T1E1 = 0xac
+ DLT_GPF_F = 0xab
+ DLT_GPF_T = 0xaa
+ DLT_GPRS_LLC = 0xa9
+ DLT_HHDLC = 0x79
+ DLT_IBM_SN = 0x92
+ DLT_IBM_SP = 0x91
+ DLT_IEEE802 = 0x6
+ DLT_IEEE802_11 = 0x69
+ DLT_IEEE802_11_RADIO = 0x7f
+ DLT_IEEE802_11_RADIO_AVS = 0xa3
+ DLT_IEEE802_15_4 = 0xc3
+ DLT_IEEE802_15_4_LINUX = 0xbf
+ DLT_IEEE802_15_4_NONASK_PHY = 0xd7
+ DLT_IEEE802_16_MAC_CPS = 0xbc
+ DLT_IEEE802_16_MAC_CPS_RADIO = 0xc1
+ DLT_IPFILTER = 0x74
+ DLT_IPMB = 0xc7
+ DLT_IPMB_LINUX = 0xd1
+ DLT_IP_OVER_FC = 0x7a
+ DLT_JUNIPER_ATM1 = 0x89
+ DLT_JUNIPER_ATM2 = 0x87
+ DLT_JUNIPER_CHDLC = 0xb5
+ DLT_JUNIPER_ES = 0x84
+ DLT_JUNIPER_ETHER = 0xb2
+ DLT_JUNIPER_FRELAY = 0xb4
+ DLT_JUNIPER_GGSN = 0x85
+ DLT_JUNIPER_ISM = 0xc2
+ DLT_JUNIPER_MFR = 0x86
+ DLT_JUNIPER_MLFR = 0x83
+ DLT_JUNIPER_MLPPP = 0x82
+ DLT_JUNIPER_MONITOR = 0xa4
+ DLT_JUNIPER_PIC_PEER = 0xae
+ DLT_JUNIPER_PPP = 0xb3
+ DLT_JUNIPER_PPPOE = 0xa7
+ DLT_JUNIPER_PPPOE_ATM = 0xa8
+ DLT_JUNIPER_SERVICES = 0x88
+ DLT_JUNIPER_ST = 0xc8
+ DLT_JUNIPER_VP = 0xb7
+ DLT_LAPB_WITH_DIR = 0xcf
+ DLT_LAPD = 0xcb
+ DLT_LIN = 0xd4
+ DLT_LINUX_IRDA = 0x90
+ DLT_LINUX_LAPD = 0xb1
+ DLT_LINUX_PPP_WITHDIRECTION = 0xa6
+ DLT_LINUX_SLL = 0x71
+ DLT_LOOP = 0x6c
+ DLT_LTALK = 0x72
+ DLT_MFR = 0xb6
+ DLT_MOST = 0xd3
+ DLT_MTP2 = 0x8c
+ DLT_MTP2_WITH_PHDR = 0x8b
+ DLT_MTP3 = 0x8d
+ DLT_NULL = 0
+ DLT_PCI_EXP = 0x7d
+ DLT_PFLOG = 0x75
+ DLT_PFSYNC = 0x79
+ DLT_PPI = 0xc0
+ DLT_PPP = 0x9
+ DLT_PPP_BSDOS = 0x10
+ DLT_PPP_ETHER = 0x33
+ DLT_PPP_PPPD = 0xa6
+ DLT_PPP_SERIAL = 0x32
+ DLT_PPP_WITH_DIR = 0xcc
+ DLT_PPP_WITH_DIRECTION = 0xa6
+ DLT_PRISM_HEADER = 0x77
+ DLT_PRONET = 0x4
+ DLT_RAIF1 = 0xc6
+ DLT_RAW = 0xc
+ DLT_RIO = 0x7c
+ DLT_SCCP = 0x8e
+ DLT_SITA = 0xc4
+ DLT_SLIP = 0x8
+ DLT_SLIP_BSDOS = 0xf
+ DLT_SUNATM = 0x7b
+ DLT_SYMANTEC_FIREWALL = 0x63
+ DLT_TZSP = 0x80
+ DLT_USB = 0xba
+ DLT_USB_LINUX = 0xbd
+ DLT_USER0 = 0x93
+ DLT_USER1 = 0x94
+ DLT_USER10 = 0x9d
+ DLT_USER11 = 0x9e
+ DLT_USER12 = 0x9f
+ DLT_USER13 = 0xa0
+ DLT_USER14 = 0xa1
+ DLT_USER15 = 0xa2
+ DLT_USER2 = 0x95
+ DLT_USER3 = 0x96
+ DLT_USER4 = 0x97
+ DLT_USER5 = 0x98
+ DLT_USER6 = 0x99
+ DLT_USER7 = 0x9a
+ DLT_USER8 = 0x9b
+ DLT_USER9 = 0x9c
+ DLT_X2E_SERIAL = 0xd5
+ DLT_X2E_XORAYA = 0xd6
+ E2BIG = 0x7
+ EACCES = 0xd
+ EADDRINUSE = 0x30
+ EADDRNOTAVAIL = 0x31
+ EAFNOSUPPORT = 0x2f
+ EAGAIN = 0x23
+ EALREADY = 0x25
+ EAUTH = 0x50
+ EBADF = 0x9
+ EBADMSG = 0x59
+ EBADRPC = 0x48
+ EBUSY = 0x10
+ ECANCELED = 0x55
+ ECHILD = 0xa
+ ECONNABORTED = 0x35
+ ECONNREFUSED = 0x3d
+ ECONNRESET = 0x36
+ EDEADLK = 0xb
+ EDESTADDRREQ = 0x27
+ EDOM = 0x21
+ EDOOFUS = 0x58
+ EDQUOT = 0x45
+ EEXIST = 0x11
+ EFAULT = 0xe
+ EFBIG = 0x1b
+ EFTYPE = 0x4f
+ EHOSTDOWN = 0x40
+ EHOSTUNREACH = 0x41
+ EIDRM = 0x52
+ EILSEQ = 0x56
+ EINPROGRESS = 0x24
+ EINTR = 0x4
+ EINVAL = 0x16
+ EIO = 0x5
+ EISCONN = 0x38
+ EISDIR = 0x15
+ ELAST = 0x5d
+ ELOOP = 0x3e
+ EMFILE = 0x18
+ EMLINK = 0x1f
+ EMSGSIZE = 0x28
+ EMULTIHOP = 0x5a
+ ENAMETOOLONG = 0x3f
+ ENEEDAUTH = 0x51
+ ENETDOWN = 0x32
+ ENETRESET = 0x34
+ ENETUNREACH = 0x33
+ ENFILE = 0x17
+ ENOATTR = 0x57
+ ENOBUFS = 0x37
+ ENODEV = 0x13
+ ENOENT = 0x2
+ ENOEXEC = 0x8
+ ENOLCK = 0x4d
+ ENOLINK = 0x5b
+ ENOMEM = 0xc
+ ENOMSG = 0x53
+ ENOPROTOOPT = 0x2a
+ ENOSPC = 0x1c
+ ENOSYS = 0x4e
+ ENOTBLK = 0xf
+ ENOTCAPABLE = 0x5d
+ ENOTCONN = 0x39
+ ENOTDIR = 0x14
+ ENOTEMPTY = 0x42
+ ENOTSOCK = 0x26
+ ENOTSUP = 0x2d
+ ENOTTY = 0x19
+ ENXIO = 0x6
+ EOPNOTSUPP = 0x2d
+ EOVERFLOW = 0x54
+ EPERM = 0x1
+ EPFNOSUPPORT = 0x2e
+ EPIPE = 0x20
+ EPROCLIM = 0x43
+ EPROCUNAVAIL = 0x4c
+ EPROGMISMATCH = 0x4b
+ EPROGUNAVAIL = 0x4a
+ EPROTO = 0x5c
+ EPROTONOSUPPORT = 0x2b
+ EPROTOTYPE = 0x29
+ ERANGE = 0x22
+ EREMOTE = 0x47
+ EROFS = 0x1e
+ ERPCMISMATCH = 0x49
+ ESHUTDOWN = 0x3a
+ ESOCKTNOSUPPORT = 0x2c
+ ESPIPE = 0x1d
+ ESRCH = 0x3
+ ESTALE = 0x46
+ ETIMEDOUT = 0x3c
+ ETOOMANYREFS = 0x3b
+ ETXTBSY = 0x1a
+ EUSERS = 0x44
+ EVFILT_AIO = -0x3
+ EVFILT_FS = -0x9
+ EVFILT_LIO = -0xa
+ EVFILT_PROC = -0x5
+ EVFILT_READ = -0x1
+ EVFILT_SIGNAL = -0x6
+ EVFILT_SYSCOUNT = 0xb
+ EVFILT_TIMER = -0x7
+ EVFILT_USER = -0xb
+ EVFILT_VNODE = -0x4
+ EVFILT_WRITE = -0x2
+ EV_ADD = 0x1
+ EV_CLEAR = 0x20
+ EV_DELETE = 0x2
+ EV_DISABLE = 0x8
+ EV_DISPATCH = 0x80
+ EV_ENABLE = 0x4
+ EV_EOF = 0x8000
+ EV_ERROR = 0x4000
+ EV_FLAG1 = 0x2000
+ EV_ONESHOT = 0x10
+ EV_RECEIPT = 0x40
+ EV_SYSFLAGS = 0xf000
+ EWOULDBLOCK = 0x23
+ EXDEV = 0x12
+ FD_CLOEXEC = 0x1
+ FD_SETSIZE = 0x400
+ F_CANCEL = 0x5
+ F_DUP2FD = 0xa
+ F_DUPFD = 0
+ F_GETFD = 0x1
+ F_GETFL = 0x3
+ F_GETLK = 0xb
+ F_GETOWN = 0x5
+ F_OGETLK = 0x7
+ F_OSETLK = 0x8
+ F_OSETLKW = 0x9
+ F_RDAHEAD = 0x10
+ F_RDLCK = 0x1
+ F_READAHEAD = 0xf
+ F_SETFD = 0x2
+ F_SETFL = 0x4
+ F_SETLK = 0xc
+ F_SETLKW = 0xd
+ F_SETLK_REMOTE = 0xe
+ F_SETOWN = 0x6
+ F_UNLCK = 0x2
+ F_UNLCKSYS = 0x4
+ F_WRLCK = 0x3
+ IFF_ALLMULTI = 0x200
+ IFF_ALTPHYS = 0x4000
+ IFF_BROADCAST = 0x2
+ IFF_CANTCHANGE = 0x208f72
+ IFF_DEBUG = 0x4
+ IFF_DRV_OACTIVE = 0x400
+ IFF_DRV_RUNNING = 0x40
+ IFF_DYING = 0x200000
+ IFF_LINK0 = 0x1000
+ IFF_LINK1 = 0x2000
+ IFF_LINK2 = 0x4000
+ IFF_LOOPBACK = 0x8
+ IFF_MONITOR = 0x40000
+ IFF_MULTICAST = 0x8000
+ IFF_NOARP = 0x80
+ IFF_OACTIVE = 0x400
+ IFF_POINTOPOINT = 0x10
+ IFF_PPROMISC = 0x20000
+ IFF_PROMISC = 0x100
+ IFF_RENAMING = 0x400000
+ IFF_RUNNING = 0x40
+ IFF_SIMPLEX = 0x800
+ IFF_SMART = 0x20
+ IFF_STATICARP = 0x80000
+ IFF_UP = 0x1
+ IFNAMSIZ = 0x10
+ IN_CLASSA_HOST = 0xffffff
+ IN_CLASSA_MAX = 0x80
+ IN_CLASSA_NET = 0xff000000
+ IN_CLASSA_NSHIFT = 0x18
+ IN_CLASSB_HOST = 0xffff
+ IN_CLASSB_MAX = 0x10000
+ IN_CLASSB_NET = 0xffff0000
+ IN_CLASSB_NSHIFT = 0x10
+ IN_CLASSC_HOST = 0xff
+ IN_CLASSC_NET = 0xffffff00
+ IN_CLASSC_NSHIFT = 0x8
+ IN_CLASSD_HOST = 0xfffffff
+ IN_CLASSD_NET = 0xf0000000
+ IN_CLASSD_NSHIFT = 0x1c
+ IN_LOOPBACKNET = 0x7f
+ IPPROTO_3PC = 0x22
+ IPPROTO_ADFS = 0x44
+ IPPROTO_AH = 0x33
+ IPPROTO_AHIP = 0x3d
+ IPPROTO_APES = 0x63
+ IPPROTO_ARGUS = 0xd
+ IPPROTO_AX25 = 0x5d
+ IPPROTO_BHA = 0x31
+ IPPROTO_BLT = 0x1e
+ IPPROTO_BRSATMON = 0x4c
+ IPPROTO_CARP = 0x70
+ IPPROTO_CFTP = 0x3e
+ IPPROTO_CHAOS = 0x10
+ IPPROTO_CMTP = 0x26
+ IPPROTO_CPHB = 0x49
+ IPPROTO_CPNX = 0x48
+ IPPROTO_DDP = 0x25
+ IPPROTO_DGP = 0x56
+ IPPROTO_DIVERT = 0x102
+ IPPROTO_DONE = 0x101
+ IPPROTO_DSTOPTS = 0x3c
+ IPPROTO_EGP = 0x8
+ IPPROTO_EMCON = 0xe
+ IPPROTO_ENCAP = 0x62
+ IPPROTO_EON = 0x50
+ IPPROTO_ESP = 0x32
+ IPPROTO_ETHERIP = 0x61
+ IPPROTO_FRAGMENT = 0x2c
+ IPPROTO_GGP = 0x3
+ IPPROTO_GMTP = 0x64
+ IPPROTO_GRE = 0x2f
+ IPPROTO_HELLO = 0x3f
+ IPPROTO_HMP = 0x14
+ IPPROTO_HOPOPTS = 0
+ IPPROTO_ICMP = 0x1
+ IPPROTO_ICMPV6 = 0x3a
+ IPPROTO_IDP = 0x16
+ IPPROTO_IDPR = 0x23
+ IPPROTO_IDRP = 0x2d
+ IPPROTO_IGMP = 0x2
+ IPPROTO_IGP = 0x55
+ IPPROTO_IGRP = 0x58
+ IPPROTO_IL = 0x28
+ IPPROTO_INLSP = 0x34
+ IPPROTO_INP = 0x20
+ IPPROTO_IP = 0
+ IPPROTO_IPCOMP = 0x6c
+ IPPROTO_IPCV = 0x47
+ IPPROTO_IPEIP = 0x5e
+ IPPROTO_IPIP = 0x4
+ IPPROTO_IPPC = 0x43
+ IPPROTO_IPV4 = 0x4
+ IPPROTO_IPV6 = 0x29
+ IPPROTO_IRTP = 0x1c
+ IPPROTO_KRYPTOLAN = 0x41
+ IPPROTO_LARP = 0x5b
+ IPPROTO_LEAF1 = 0x19
+ IPPROTO_LEAF2 = 0x1a
+ IPPROTO_MAX = 0x100
+ IPPROTO_MAXID = 0x34
+ IPPROTO_MEAS = 0x13
+ IPPROTO_MHRP = 0x30
+ IPPROTO_MICP = 0x5f
+ IPPROTO_MOBILE = 0x37
+ IPPROTO_MTP = 0x5c
+ IPPROTO_MUX = 0x12
+ IPPROTO_ND = 0x4d
+ IPPROTO_NHRP = 0x36
+ IPPROTO_NONE = 0x3b
+ IPPROTO_NSP = 0x1f
+ IPPROTO_NVPII = 0xb
+ IPPROTO_OLD_DIVERT = 0xfe
+ IPPROTO_OSPFIGP = 0x59
+ IPPROTO_PFSYNC = 0xf0
+ IPPROTO_PGM = 0x71
+ IPPROTO_PIGP = 0x9
+ IPPROTO_PIM = 0x67
+ IPPROTO_PRM = 0x15
+ IPPROTO_PUP = 0xc
+ IPPROTO_PVP = 0x4b
+ IPPROTO_RAW = 0xff
+ IPPROTO_RCCMON = 0xa
+ IPPROTO_RDP = 0x1b
+ IPPROTO_ROUTING = 0x2b
+ IPPROTO_RSVP = 0x2e
+ IPPROTO_RVD = 0x42
+ IPPROTO_SATEXPAK = 0x40
+ IPPROTO_SATMON = 0x45
+ IPPROTO_SCCSP = 0x60
+ IPPROTO_SCTP = 0x84
+ IPPROTO_SDRP = 0x2a
+ IPPROTO_SEP = 0x21
+ IPPROTO_SKIP = 0x39
+ IPPROTO_SPACER = 0x7fff
+ IPPROTO_SRPC = 0x5a
+ IPPROTO_ST = 0x7
+ IPPROTO_SVMTP = 0x52
+ IPPROTO_SWIPE = 0x35
+ IPPROTO_TCF = 0x57
+ IPPROTO_TCP = 0x6
+ IPPROTO_TLSP = 0x38
+ IPPROTO_TP = 0x1d
+ IPPROTO_TPXX = 0x27
+ IPPROTO_TRUNK1 = 0x17
+ IPPROTO_TRUNK2 = 0x18
+ IPPROTO_TTP = 0x54
+ IPPROTO_UDP = 0x11
+ IPPROTO_VINES = 0x53
+ IPPROTO_VISA = 0x46
+ IPPROTO_VMTP = 0x51
+ IPPROTO_WBEXPAK = 0x4f
+ IPPROTO_WBMON = 0x4e
+ IPPROTO_WSN = 0x4a
+ IPPROTO_XNET = 0xf
+ IPPROTO_XTP = 0x24
+ IPV6_AUTOFLOWLABEL = 0x3b
+ IPV6_BINDANY = 0x40
+ IPV6_BINDV6ONLY = 0x1b
+ IPV6_CHECKSUM = 0x1a
+ IPV6_DEFAULT_MULTICAST_HOPS = 0x1
+ IPV6_DEFAULT_MULTICAST_LOOP = 0x1
+ IPV6_DEFHLIM = 0x40
+ IPV6_DONTFRAG = 0x3e
+ IPV6_DSTOPTS = 0x32
+ IPV6_FAITH = 0x1d
+ IPV6_FLOWINFO_MASK = 0xffffff0f
+ IPV6_FLOWLABEL_MASK = 0xffff0f00
+ IPV6_FRAGTTL = 0x78
+ IPV6_FW_ADD = 0x1e
+ IPV6_FW_DEL = 0x1f
+ IPV6_FW_FLUSH = 0x20
+ IPV6_FW_GET = 0x22
+ IPV6_FW_ZERO = 0x21
+ IPV6_HLIMDEC = 0x1
+ IPV6_HOPLIMIT = 0x2f
+ IPV6_HOPOPTS = 0x31
+ IPV6_IPSEC_POLICY = 0x1c
+ IPV6_JOIN_GROUP = 0xc
+ IPV6_LEAVE_GROUP = 0xd
+ IPV6_MAXHLIM = 0xff
+ IPV6_MAXOPTHDR = 0x800
+ IPV6_MAXPACKET = 0xffff
+ IPV6_MAX_GROUP_SRC_FILTER = 0x200
+ IPV6_MAX_MEMBERSHIPS = 0xfff
+ IPV6_MAX_SOCK_SRC_FILTER = 0x80
+ IPV6_MIN_MEMBERSHIPS = 0x1f
+ IPV6_MMTU = 0x500
+ IPV6_MSFILTER = 0x4a
+ IPV6_MULTICAST_HOPS = 0xa
+ IPV6_MULTICAST_IF = 0x9
+ IPV6_MULTICAST_LOOP = 0xb
+ IPV6_NEXTHOP = 0x30
+ IPV6_PATHMTU = 0x2c
+ IPV6_PKTINFO = 0x2e
+ IPV6_PORTRANGE = 0xe
+ IPV6_PORTRANGE_DEFAULT = 0
+ IPV6_PORTRANGE_HIGH = 0x1
+ IPV6_PORTRANGE_LOW = 0x2
+ IPV6_PREFER_TEMPADDR = 0x3f
+ IPV6_RECVDSTOPTS = 0x28
+ IPV6_RECVHOPLIMIT = 0x25
+ IPV6_RECVHOPOPTS = 0x27
+ IPV6_RECVPATHMTU = 0x2b
+ IPV6_RECVPKTINFO = 0x24
+ IPV6_RECVRTHDR = 0x26
+ IPV6_RECVTCLASS = 0x39
+ IPV6_RTHDR = 0x33
+ IPV6_RTHDRDSTOPTS = 0x23
+ IPV6_RTHDR_LOOSE = 0
+ IPV6_RTHDR_STRICT = 0x1
+ IPV6_RTHDR_TYPE_0 = 0
+ IPV6_SOCKOPT_RESERVED1 = 0x3
+ IPV6_TCLASS = 0x3d
+ IPV6_UNICAST_HOPS = 0x4
+ IPV6_USE_MIN_MTU = 0x2a
+ IPV6_V6ONLY = 0x1b
+ IPV6_VERSION = 0x60
+ IPV6_VERSION_MASK = 0xf0
+ IP_ADD_MEMBERSHIP = 0xc
+ IP_ADD_SOURCE_MEMBERSHIP = 0x46
+ IP_BINDANY = 0x18
+ IP_BLOCK_SOURCE = 0x48
+ IP_DEFAULT_MULTICAST_LOOP = 0x1
+ IP_DEFAULT_MULTICAST_TTL = 0x1
+ IP_DF = 0x4000
+ IP_DONTFRAG = 0x43
+ IP_DROP_MEMBERSHIP = 0xd
+ IP_DROP_SOURCE_MEMBERSHIP = 0x47
+ IP_DUMMYNET3 = 0x31
+ IP_DUMMYNET_CONFIGURE = 0x3c
+ IP_DUMMYNET_DEL = 0x3d
+ IP_DUMMYNET_FLUSH = 0x3e
+ IP_DUMMYNET_GET = 0x40
+ IP_FAITH = 0x16
+ IP_FW3 = 0x30
+ IP_FW_ADD = 0x32
+ IP_FW_DEL = 0x33
+ IP_FW_FLUSH = 0x34
+ IP_FW_GET = 0x36
+ IP_FW_NAT_CFG = 0x38
+ IP_FW_NAT_DEL = 0x39
+ IP_FW_NAT_GET_CONFIG = 0x3a
+ IP_FW_NAT_GET_LOG = 0x3b
+ IP_FW_RESETLOG = 0x37
+ IP_FW_TABLE_ADD = 0x28
+ IP_FW_TABLE_DEL = 0x29
+ IP_FW_TABLE_FLUSH = 0x2a
+ IP_FW_TABLE_GETSIZE = 0x2b
+ IP_FW_TABLE_LIST = 0x2c
+ IP_FW_ZERO = 0x35
+ IP_HDRINCL = 0x2
+ IP_IPSEC_POLICY = 0x15
+ IP_MAXPACKET = 0xffff
+ IP_MAX_GROUP_SRC_FILTER = 0x200
+ IP_MAX_MEMBERSHIPS = 0xfff
+ IP_MAX_SOCK_MUTE_FILTER = 0x80
+ IP_MAX_SOCK_SRC_FILTER = 0x80
+ IP_MAX_SOURCE_FILTER = 0x400
+ IP_MF = 0x2000
+ IP_MINTTL = 0x42
+ IP_MIN_MEMBERSHIPS = 0x1f
+ IP_MSFILTER = 0x4a
+ IP_MSS = 0x240
+ IP_MULTICAST_IF = 0x9
+ IP_MULTICAST_LOOP = 0xb
+ IP_MULTICAST_TTL = 0xa
+ IP_MULTICAST_VIF = 0xe
+ IP_OFFMASK = 0x1fff
+ IP_ONESBCAST = 0x17
+ IP_OPTIONS = 0x1
+ IP_PORTRANGE = 0x13
+ IP_PORTRANGE_DEFAULT = 0
+ IP_PORTRANGE_HIGH = 0x1
+ IP_PORTRANGE_LOW = 0x2
+ IP_RECVDSTADDR = 0x7
+ IP_RECVIF = 0x14
+ IP_RECVOPTS = 0x5
+ IP_RECVRETOPTS = 0x6
+ IP_RECVTTL = 0x41
+ IP_RETOPTS = 0x8
+ IP_RF = 0x8000
+ IP_RSVP_OFF = 0x10
+ IP_RSVP_ON = 0xf
+ IP_RSVP_VIF_OFF = 0x12
+ IP_RSVP_VIF_ON = 0x11
+ IP_SENDSRCADDR = 0x7
+ IP_TOS = 0x3
+ IP_TTL = 0x4
+ IP_UNBLOCK_SOURCE = 0x49
+ MSG_COMPAT = 0x8000
+ MSG_CTRUNC = 0x20
+ MSG_DONTROUTE = 0x4
+ MSG_DONTWAIT = 0x80
+ MSG_EOF = 0x100
+ MSG_EOR = 0x8
+ MSG_NBIO = 0x4000
+ MSG_NOSIGNAL = 0x20000
+ MSG_NOTIFICATION = 0x2000
+ MSG_OOB = 0x1
+ MSG_PEEK = 0x2
+ MSG_TRUNC = 0x10
+ MSG_WAITALL = 0x40
+ NET_RT_DUMP = 0x1
+ NET_RT_FLAGS = 0x2
+ NET_RT_IFLIST = 0x3
+ NET_RT_IFMALIST = 0x4
+ NET_RT_MAXID = 0x5
+ O_ACCMODE = 0x3
+ O_APPEND = 0x8
+ O_ASYNC = 0x40
+ O_CREAT = 0x200
+ O_DIRECT = 0x10000
+ O_DIRECTORY = 0x20000
+ O_EXCL = 0x800
+ O_EXEC = 0x40000
+ O_EXLOCK = 0x20
+ O_FSYNC = 0x80
+ O_NDELAY = 0x4
+ O_NOCTTY = 0x8000
+ O_NOFOLLOW = 0x100
+ O_NONBLOCK = 0x4
+ O_RDONLY = 0
+ O_RDWR = 0x2
+ O_SHLOCK = 0x10
+ O_SYNC = 0x80
+ O_TRUNC = 0x400
+ O_TTY_INIT = 0x80000
+ O_WRONLY = 0x1
+ RTAX_AUTHOR = 0x6
+ RTAX_BRD = 0x7
+ RTAX_DST = 0
+ RTAX_GATEWAY = 0x1
+ RTAX_GENMASK = 0x3
+ RTAX_IFA = 0x5
+ RTAX_IFP = 0x4
+ RTAX_MAX = 0x8
+ RTAX_NETMASK = 0x2
+ RTA_AUTHOR = 0x40
+ RTA_BRD = 0x80
+ RTA_DST = 0x1
+ RTA_GATEWAY = 0x2
+ RTA_GENMASK = 0x8
+ RTA_IFA = 0x20
+ RTA_IFP = 0x10
+ RTA_NETMASK = 0x4
+ RTF_BLACKHOLE = 0x1000
+ RTF_BROADCAST = 0x400000
+ RTF_DONE = 0x40
+ RTF_DYNAMIC = 0x10
+ RTF_FMASK = 0x1004d808
+ RTF_GATEWAY = 0x2
+ RTF_HOST = 0x4
+ RTF_LLDATA = 0x400
+ RTF_LLINFO = 0x400
+ RTF_LOCAL = 0x200000
+ RTF_MODIFIED = 0x20
+ RTF_MULTICAST = 0x800000
+ RTF_PINNED = 0x100000
+ RTF_PRCLONING = 0x10000
+ RTF_PROTO1 = 0x8000
+ RTF_PROTO2 = 0x4000
+ RTF_PROTO3 = 0x40000
+ RTF_REJECT = 0x8
+ RTF_RNH_LOCKED = 0x40000000
+ RTF_STATIC = 0x800
+ RTF_STICKY = 0x10000000
+ RTF_UP = 0x1
+ RTF_XRESOLVE = 0x200
+ RTM_ADD = 0x1
+ RTM_CHANGE = 0x3
+ RTM_DELADDR = 0xd
+ RTM_DELETE = 0x2
+ RTM_DELMADDR = 0x10
+ RTM_GET = 0x4
+ RTM_IEEE80211 = 0x12
+ RTM_IFANNOUNCE = 0x11
+ RTM_IFINFO = 0xe
+ RTM_LOCK = 0x8
+ RTM_LOSING = 0x5
+ RTM_MISS = 0x7
+ RTM_NEWADDR = 0xc
+ RTM_NEWMADDR = 0xf
+ RTM_OLDADD = 0x9
+ RTM_OLDDEL = 0xa
+ RTM_REDIRECT = 0x6
+ RTM_RESOLVE = 0xb
+ RTM_RTTUNIT = 0xf4240
+ RTM_VERSION = 0x5
+ RTV_EXPIRE = 0x4
+ RTV_HOPCOUNT = 0x2
+ RTV_MTU = 0x1
+ RTV_RPIPE = 0x8
+ RTV_RTT = 0x40
+ RTV_RTTVAR = 0x80
+ RTV_SPIPE = 0x10
+ RTV_SSTHRESH = 0x20
+ RTV_WEIGHT = 0x100
+ SCM_BINTIME = 0x4
+ SCM_CREDS = 0x3
+ SCM_RIGHTS = 0x1
+ SCM_TIMESTAMP = 0x2
+ SHUT_RD = 0
+ SHUT_RDWR = 0x2
+ SHUT_WR = 0x1
+ SIGABRT = 0x6
+ SIGALRM = 0xe
+ SIGBUS = 0xa
+ SIGCHLD = 0x14
+ SIGCONT = 0x13
+ SIGEMT = 0x7
+ SIGFPE = 0x8
+ SIGHUP = 0x1
+ SIGILL = 0x4
+ SIGINFO = 0x1d
+ SIGINT = 0x2
+ SIGIO = 0x17
+ SIGIOT = 0x6
+ SIGKILL = 0x9
+ SIGLWP = 0x20
+ SIGPIPE = 0xd
+ SIGPROF = 0x1b
+ SIGQUIT = 0x3
+ SIGSEGV = 0xb
+ SIGSTOP = 0x11
+ SIGSYS = 0xc
+ SIGTERM = 0xf
+ SIGTHR = 0x20
+ SIGTRAP = 0x5
+ SIGTSTP = 0x12
+ SIGTTIN = 0x15
+ SIGTTOU = 0x16
+ SIGURG = 0x10
+ SIGUSR1 = 0x1e
+ SIGUSR2 = 0x1f
+ SIGVTALRM = 0x1a
+ SIGWINCH = 0x1c
+ SIGXCPU = 0x18
+ SIGXFSZ = 0x19
+ SIOCADDMULTI = 0x80206931
+ SIOCADDRT = 0x8030720a
+ SIOCAIFADDR = 0x8040691a
+ SIOCAIFGROUP = 0x80246987
+ SIOCALIFADDR = 0x8118691b
+ SIOCATMARK = 0x40047307
+ SIOCDELMULTI = 0x80206932
+ SIOCDELRT = 0x8030720b
+ SIOCDIFADDR = 0x80206919
+ SIOCDIFGROUP = 0x80246989
+ SIOCDIFPHYADDR = 0x80206949
+ SIOCDLIFADDR = 0x8118691d
+ SIOCGDRVSPEC = 0xc01c697b
+ SIOCGETSGCNT = 0xc0147210
+ SIOCGETVIFCNT = 0xc014720f
+ SIOCGHIWAT = 0x40047301
+ SIOCGIFADDR = 0xc0206921
+ SIOCGIFBRDADDR = 0xc0206923
+ SIOCGIFCAP = 0xc020691f
+ SIOCGIFCONF = 0xc0086924
+ SIOCGIFDESCR = 0xc020692a
+ SIOCGIFDSTADDR = 0xc0206922
+ SIOCGIFFLAGS = 0xc0206911
+ SIOCGIFGENERIC = 0xc020693a
+ SIOCGIFGMEMB = 0xc024698a
+ SIOCGIFGROUP = 0xc0246988
+ SIOCGIFINDEX = 0xc0206920
+ SIOCGIFMAC = 0xc0206926
+ SIOCGIFMEDIA = 0xc0286938
+ SIOCGIFMETRIC = 0xc0206917
+ SIOCGIFMTU = 0xc0206933
+ SIOCGIFNETMASK = 0xc0206925
+ SIOCGIFPDSTADDR = 0xc0206948
+ SIOCGIFPHYS = 0xc0206935
+ SIOCGIFPSRCADDR = 0xc0206947
+ SIOCGIFSTATUS = 0xc331693b
+ SIOCGLIFADDR = 0xc118691c
+ SIOCGLIFPHYADDR = 0xc118694b
+ SIOCGLOWAT = 0x40047303
+ SIOCGPGRP = 0x40047309
+ SIOCGPRIVATE_0 = 0xc0206950
+ SIOCGPRIVATE_1 = 0xc0206951
+ SIOCIFCREATE = 0xc020697a
+ SIOCIFCREATE2 = 0xc020697c
+ SIOCIFDESTROY = 0x80206979
+ SIOCIFGCLONERS = 0xc00c6978
+ SIOCSDRVSPEC = 0x801c697b
+ SIOCSHIWAT = 0x80047300
+ SIOCSIFADDR = 0x8020690c
+ SIOCSIFBRDADDR = 0x80206913
+ SIOCSIFCAP = 0x8020691e
+ SIOCSIFDESCR = 0x80206929
+ SIOCSIFDSTADDR = 0x8020690e
+ SIOCSIFFLAGS = 0x80206910
+ SIOCSIFGENERIC = 0x80206939
+ SIOCSIFLLADDR = 0x8020693c
+ SIOCSIFMAC = 0x80206927
+ SIOCSIFMEDIA = 0xc0206937
+ SIOCSIFMETRIC = 0x80206918
+ SIOCSIFMTU = 0x80206934
+ SIOCSIFNAME = 0x80206928
+ SIOCSIFNETMASK = 0x80206916
+ SIOCSIFPHYADDR = 0x80406946
+ SIOCSIFPHYS = 0x80206936
+ SIOCSIFRVNET = 0xc020695b
+ SIOCSIFVNET = 0xc020695a
+ SIOCSLIFPHYADDR = 0x8118694a
+ SIOCSLOWAT = 0x80047302
+ SIOCSPGRP = 0x80047308
+ SOCK_DGRAM = 0x2
+ SOCK_MAXADDRLEN = 0xff
+ SOCK_RAW = 0x3
+ SOCK_RDM = 0x4
+ SOCK_SEQPACKET = 0x5
+ SOCK_STREAM = 0x1
+ SOL_SOCKET = 0xffff
+ SOMAXCONN = 0x80
+ SO_ACCEPTCONN = 0x2
+ SO_ACCEPTFILTER = 0x1000
+ SO_BINTIME = 0x2000
+ SO_BROADCAST = 0x20
+ SO_DEBUG = 0x1
+ SO_DONTROUTE = 0x10
+ SO_ERROR = 0x1007
+ SO_KEEPALIVE = 0x8
+ SO_LABEL = 0x1009
+ SO_LINGER = 0x80
+ SO_LISTENINCQLEN = 0x1013
+ SO_LISTENQLEN = 0x1012
+ SO_LISTENQLIMIT = 0x1011
+ SO_NOSIGPIPE = 0x800
+ SO_NO_DDP = 0x8000
+ SO_NO_OFFLOAD = 0x4000
+ SO_OOBINLINE = 0x100
+ SO_PEERLABEL = 0x1010
+ SO_RCVBUF = 0x1002
+ SO_RCVLOWAT = 0x1004
+ SO_RCVTIMEO = 0x1006
+ SO_REUSEADDR = 0x4
+ SO_REUSEPORT = 0x200
+ SO_SETFIB = 0x1014
+ SO_SNDBUF = 0x1001
+ SO_SNDLOWAT = 0x1003
+ SO_SNDTIMEO = 0x1005
+ SO_TIMESTAMP = 0x400
+ SO_TYPE = 0x1008
+ SO_USELOOPBACK = 0x40
+ TCP_CA_NAME_MAX = 0x10
+ TCP_CONGESTION = 0x40
+ TCP_INFO = 0x20
+ TCP_MAXBURST = 0x4
+ TCP_MAXHLEN = 0x3c
+ TCP_MAXOLEN = 0x28
+ TCP_MAXSEG = 0x2
+ TCP_MAXWIN = 0xffff
+ TCP_MAX_SACK = 0x4
+ TCP_MAX_WINSHIFT = 0xe
+ TCP_MD5SIG = 0x10
+ TCP_MINMSS = 0xd8
+ TCP_MSS = 0x200
+ TCP_NODELAY = 0x1
+ TCP_NOOPT = 0x8
+ TCP_NOPUSH = 0x4
+ WCONTINUED = 0x4
+ WCOREFLAG = 0x80
+ WLINUXCLONE = 0x80000000
+ WNOHANG = 0x1
+ WNOWAIT = 0x8
+ WSTOPPED = 0x2
+ WUNTRACED = 0x2
)
// Types
diff --git a/src/pkg/syscall/zerrors_freebsd_amd64.go b/src/pkg/syscall/zerrors_freebsd_amd64.go
index 770d293a2..33fd3de1f 100644
--- a/src/pkg/syscall/zerrors_freebsd_amd64.go
+++ b/src/pkg/syscall/zerrors_freebsd_amd64.go
@@ -9,805 +9,1013 @@ package syscall
// Constants
const (
- AF_APPLETALK = 0x10
- AF_ARP = 0x23
- AF_ATM = 0x1e
- AF_BLUETOOTH = 0x24
- AF_CCITT = 0xa
- AF_CHAOS = 0x5
- AF_CNT = 0x15
- AF_COIP = 0x14
- AF_DATAKIT = 0x9
- AF_DECnet = 0xc
- AF_DLI = 0xd
- AF_E164 = 0x1a
- AF_ECMA = 0x8
- AF_HYLINK = 0xf
- AF_IEEE80211 = 0x25
- AF_IMPLINK = 0x3
- AF_INET = 0x2
- AF_INET6 = 0x1c
- AF_IPX = 0x17
- AF_ISDN = 0x1a
- AF_ISO = 0x7
- AF_LAT = 0xe
- AF_LINK = 0x12
- AF_LOCAL = 0x1
- AF_MAX = 0x26
- AF_NATM = 0x1d
- AF_NETBIOS = 0x6
- AF_NETGRAPH = 0x20
- AF_OSI = 0x7
- AF_PUP = 0x4
- AF_ROUTE = 0x11
- AF_SCLUSTER = 0x22
- AF_SIP = 0x18
- AF_SLOW = 0x21
- AF_SNA = 0xb
- AF_UNIX = 0x1
- AF_UNSPEC = 0
- AF_VENDOR00 = 0x27
- AF_VENDOR01 = 0x29
- AF_VENDOR02 = 0x2b
- AF_VENDOR03 = 0x2d
- AF_VENDOR04 = 0x2f
- AF_VENDOR05 = 0x31
- AF_VENDOR06 = 0x33
- AF_VENDOR07 = 0x35
- AF_VENDOR08 = 0x37
- AF_VENDOR09 = 0x39
- AF_VENDOR10 = 0x3b
- AF_VENDOR11 = 0x3d
- AF_VENDOR12 = 0x3f
- AF_VENDOR13 = 0x41
- AF_VENDOR14 = 0x43
- AF_VENDOR15 = 0x45
- AF_VENDOR16 = 0x47
- AF_VENDOR17 = 0x49
- AF_VENDOR18 = 0x4b
- AF_VENDOR19 = 0x4d
- AF_VENDOR20 = 0x4f
- AF_VENDOR21 = 0x51
- AF_VENDOR22 = 0x53
- AF_VENDOR23 = 0x55
- AF_VENDOR24 = 0x57
- AF_VENDOR25 = 0x59
- AF_VENDOR26 = 0x5b
- AF_VENDOR27 = 0x5d
- AF_VENDOR28 = 0x5f
- AF_VENDOR29 = 0x61
- AF_VENDOR30 = 0x63
- AF_VENDOR31 = 0x65
- AF_VENDOR32 = 0x67
- AF_VENDOR33 = 0x69
- AF_VENDOR34 = 0x6b
- AF_VENDOR35 = 0x6d
- AF_VENDOR36 = 0x6f
- AF_VENDOR37 = 0x71
- AF_VENDOR38 = 0x73
- AF_VENDOR39 = 0x75
- AF_VENDOR40 = 0x77
- AF_VENDOR41 = 0x79
- AF_VENDOR42 = 0x7b
- AF_VENDOR43 = 0x7d
- AF_VENDOR44 = 0x7f
- AF_VENDOR45 = 0x81
- AF_VENDOR46 = 0x83
- AF_VENDOR47 = 0x85
- CTL_MAXNAME = 0x18
- CTL_NET = 0x4
- E2BIG = 0x7
- EACCES = 0xd
- EADDRINUSE = 0x30
- EADDRNOTAVAIL = 0x31
- EAFNOSUPPORT = 0x2f
- EAGAIN = 0x23
- EALREADY = 0x25
- EAUTH = 0x50
- EBADF = 0x9
- EBADMSG = 0x59
- EBADRPC = 0x48
- EBUSY = 0x10
- ECANCELED = 0x55
- ECHILD = 0xa
- ECONNABORTED = 0x35
- ECONNREFUSED = 0x3d
- ECONNRESET = 0x36
- EDEADLK = 0xb
- EDESTADDRREQ = 0x27
- EDOM = 0x21
- EDOOFUS = 0x58
- EDQUOT = 0x45
- EEXIST = 0x11
- EFAULT = 0xe
- EFBIG = 0x1b
- EFTYPE = 0x4f
- EHOSTDOWN = 0x40
- EHOSTUNREACH = 0x41
- EIDRM = 0x52
- EILSEQ = 0x56
- EINPROGRESS = 0x24
- EINTR = 0x4
- EINVAL = 0x16
- EIO = 0x5
- EISCONN = 0x38
- EISDIR = 0x15
- ELAST = 0x5d
- ELOOP = 0x3e
- EMFILE = 0x18
- EMLINK = 0x1f
- EMSGSIZE = 0x28
- EMULTIHOP = 0x5a
- ENAMETOOLONG = 0x3f
- ENEEDAUTH = 0x51
- ENETDOWN = 0x32
- ENETRESET = 0x34
- ENETUNREACH = 0x33
- ENFILE = 0x17
- ENOATTR = 0x57
- ENOBUFS = 0x37
- ENODEV = 0x13
- ENOENT = 0x2
- ENOEXEC = 0x8
- ENOLCK = 0x4d
- ENOLINK = 0x5b
- ENOMEM = 0xc
- ENOMSG = 0x53
- ENOPROTOOPT = 0x2a
- ENOSPC = 0x1c
- ENOSYS = 0x4e
- ENOTBLK = 0xf
- ENOTCAPABLE = 0x5d
- ENOTCONN = 0x39
- ENOTDIR = 0x14
- ENOTEMPTY = 0x42
- ENOTSOCK = 0x26
- ENOTSUP = 0x2d
- ENOTTY = 0x19
- ENXIO = 0x6
- EOPNOTSUPP = 0x2d
- EOVERFLOW = 0x54
- EPERM = 0x1
- EPFNOSUPPORT = 0x2e
- EPIPE = 0x20
- EPROCLIM = 0x43
- EPROCUNAVAIL = 0x4c
- EPROGMISMATCH = 0x4b
- EPROGUNAVAIL = 0x4a
- EPROTO = 0x5c
- EPROTONOSUPPORT = 0x2b
- EPROTOTYPE = 0x29
- ERANGE = 0x22
- EREMOTE = 0x47
- EROFS = 0x1e
- ERPCMISMATCH = 0x49
- ESHUTDOWN = 0x3a
- ESOCKTNOSUPPORT = 0x2c
- ESPIPE = 0x1d
- ESRCH = 0x3
- ESTALE = 0x46
- ETIMEDOUT = 0x3c
- ETOOMANYREFS = 0x3b
- ETXTBSY = 0x1a
- EUSERS = 0x44
- EVFILT_AIO = -0x3
- EVFILT_FS = -0x9
- EVFILT_LIO = -0xa
- EVFILT_PROC = -0x5
- EVFILT_READ = -0x1
- EVFILT_SIGNAL = -0x6
- EVFILT_SYSCOUNT = 0xb
- EVFILT_TIMER = -0x7
- EVFILT_USER = -0xb
- EVFILT_VNODE = -0x4
- EVFILT_WRITE = -0x2
- EV_ADD = 0x1
- EV_CLEAR = 0x20
- EV_DELETE = 0x2
- EV_DISABLE = 0x8
- EV_DISPATCH = 0x80
- EV_ENABLE = 0x4
- EV_EOF = 0x8000
- EV_ERROR = 0x4000
- EV_FLAG1 = 0x2000
- EV_ONESHOT = 0x10
- EV_RECEIPT = 0x40
- EV_SYSFLAGS = 0xf000
- EWOULDBLOCK = 0x23
- EXDEV = 0x12
- FD_CLOEXEC = 0x1
- FD_SETSIZE = 0x400
- F_CANCEL = 0x5
- F_DUP2FD = 0xa
- F_DUPFD = 0
- F_GETFD = 0x1
- F_GETFL = 0x3
- F_GETLK = 0xb
- F_GETOWN = 0x5
- F_OGETLK = 0x7
- F_OSETLK = 0x8
- F_OSETLKW = 0x9
- F_RDAHEAD = 0x10
- F_RDLCK = 0x1
- F_READAHEAD = 0xf
- F_SETFD = 0x2
- F_SETFL = 0x4
- F_SETLK = 0xc
- F_SETLKW = 0xd
- F_SETLK_REMOTE = 0xe
- F_SETOWN = 0x6
- F_UNLCK = 0x2
- F_UNLCKSYS = 0x4
- F_WRLCK = 0x3
- IFF_ALLMULTI = 0x200
- IFF_ALTPHYS = 0x4000
- IFF_BROADCAST = 0x2
- IFF_CANTCHANGE = 0x208f72
- IFF_DEBUG = 0x4
- IFF_DRV_OACTIVE = 0x400
- IFF_DRV_RUNNING = 0x40
- IFF_DYING = 0x200000
- IFF_LINK0 = 0x1000
- IFF_LINK1 = 0x2000
- IFF_LINK2 = 0x4000
- IFF_LOOPBACK = 0x8
- IFF_MONITOR = 0x40000
- IFF_MULTICAST = 0x8000
- IFF_NOARP = 0x80
- IFF_OACTIVE = 0x400
- IFF_POINTOPOINT = 0x10
- IFF_PPROMISC = 0x20000
- IFF_PROMISC = 0x100
- IFF_RENAMING = 0x400000
- IFF_RUNNING = 0x40
- IFF_SIMPLEX = 0x800
- IFF_SMART = 0x20
- IFF_STATICARP = 0x80000
- IFF_UP = 0x1
- IFNAMSIZ = 0x10
- IN_CLASSA_HOST = 0xffffff
- IN_CLASSA_MAX = 0x80
- IN_CLASSA_NET = 0xff000000
- IN_CLASSA_NSHIFT = 0x18
- IN_CLASSB_HOST = 0xffff
- IN_CLASSB_MAX = 0x10000
- IN_CLASSB_NET = 0xffff0000
- IN_CLASSB_NSHIFT = 0x10
- IN_CLASSC_HOST = 0xff
- IN_CLASSC_NET = 0xffffff00
- IN_CLASSC_NSHIFT = 0x8
- IN_CLASSD_HOST = 0xfffffff
- IN_CLASSD_NET = 0xf0000000
- IN_CLASSD_NSHIFT = 0x1c
- IN_LOOPBACKNET = 0x7f
- IPPROTO_3PC = 0x22
- IPPROTO_ADFS = 0x44
- IPPROTO_AH = 0x33
- IPPROTO_AHIP = 0x3d
- IPPROTO_APES = 0x63
- IPPROTO_ARGUS = 0xd
- IPPROTO_AX25 = 0x5d
- IPPROTO_BHA = 0x31
- IPPROTO_BLT = 0x1e
- IPPROTO_BRSATMON = 0x4c
- IPPROTO_CARP = 0x70
- IPPROTO_CFTP = 0x3e
- IPPROTO_CHAOS = 0x10
- IPPROTO_CMTP = 0x26
- IPPROTO_CPHB = 0x49
- IPPROTO_CPNX = 0x48
- IPPROTO_DDP = 0x25
- IPPROTO_DGP = 0x56
- IPPROTO_DIVERT = 0x102
- IPPROTO_DONE = 0x101
- IPPROTO_DSTOPTS = 0x3c
- IPPROTO_EGP = 0x8
- IPPROTO_EMCON = 0xe
- IPPROTO_ENCAP = 0x62
- IPPROTO_EON = 0x50
- IPPROTO_ESP = 0x32
- IPPROTO_ETHERIP = 0x61
- IPPROTO_FRAGMENT = 0x2c
- IPPROTO_GGP = 0x3
- IPPROTO_GMTP = 0x64
- IPPROTO_GRE = 0x2f
- IPPROTO_HELLO = 0x3f
- IPPROTO_HMP = 0x14
- IPPROTO_HOPOPTS = 0
- IPPROTO_ICMP = 0x1
- IPPROTO_ICMPV6 = 0x3a
- IPPROTO_IDP = 0x16
- IPPROTO_IDPR = 0x23
- IPPROTO_IDRP = 0x2d
- IPPROTO_IGMP = 0x2
- IPPROTO_IGP = 0x55
- IPPROTO_IGRP = 0x58
- IPPROTO_IL = 0x28
- IPPROTO_INLSP = 0x34
- IPPROTO_INP = 0x20
- IPPROTO_IP = 0
- IPPROTO_IPCOMP = 0x6c
- IPPROTO_IPCV = 0x47
- IPPROTO_IPEIP = 0x5e
- IPPROTO_IPIP = 0x4
- IPPROTO_IPPC = 0x43
- IPPROTO_IPV4 = 0x4
- IPPROTO_IPV6 = 0x29
- IPPROTO_IRTP = 0x1c
- IPPROTO_KRYPTOLAN = 0x41
- IPPROTO_LARP = 0x5b
- IPPROTO_LEAF1 = 0x19
- IPPROTO_LEAF2 = 0x1a
- IPPROTO_MAX = 0x100
- IPPROTO_MAXID = 0x34
- IPPROTO_MEAS = 0x13
- IPPROTO_MHRP = 0x30
- IPPROTO_MICP = 0x5f
- IPPROTO_MOBILE = 0x37
- IPPROTO_MTP = 0x5c
- IPPROTO_MUX = 0x12
- IPPROTO_ND = 0x4d
- IPPROTO_NHRP = 0x36
- IPPROTO_NONE = 0x3b
- IPPROTO_NSP = 0x1f
- IPPROTO_NVPII = 0xb
- IPPROTO_OLD_DIVERT = 0xfe
- IPPROTO_OSPFIGP = 0x59
- IPPROTO_PFSYNC = 0xf0
- IPPROTO_PGM = 0x71
- IPPROTO_PIGP = 0x9
- IPPROTO_PIM = 0x67
- IPPROTO_PRM = 0x15
- IPPROTO_PUP = 0xc
- IPPROTO_PVP = 0x4b
- IPPROTO_RAW = 0xff
- IPPROTO_RCCMON = 0xa
- IPPROTO_RDP = 0x1b
- IPPROTO_ROUTING = 0x2b
- IPPROTO_RSVP = 0x2e
- IPPROTO_RVD = 0x42
- IPPROTO_SATEXPAK = 0x40
- IPPROTO_SATMON = 0x45
- IPPROTO_SCCSP = 0x60
- IPPROTO_SCTP = 0x84
- IPPROTO_SDRP = 0x2a
- IPPROTO_SEP = 0x21
- IPPROTO_SKIP = 0x39
- IPPROTO_SPACER = 0x7fff
- IPPROTO_SRPC = 0x5a
- IPPROTO_ST = 0x7
- IPPROTO_SVMTP = 0x52
- IPPROTO_SWIPE = 0x35
- IPPROTO_TCF = 0x57
- IPPROTO_TCP = 0x6
- IPPROTO_TLSP = 0x38
- IPPROTO_TP = 0x1d
- IPPROTO_TPXX = 0x27
- IPPROTO_TRUNK1 = 0x17
- IPPROTO_TRUNK2 = 0x18
- IPPROTO_TTP = 0x54
- IPPROTO_UDP = 0x11
- IPPROTO_VINES = 0x53
- IPPROTO_VISA = 0x46
- IPPROTO_VMTP = 0x51
- IPPROTO_WBEXPAK = 0x4f
- IPPROTO_WBMON = 0x4e
- IPPROTO_WSN = 0x4a
- IPPROTO_XNET = 0xf
- IPPROTO_XTP = 0x24
- IPV6_AUTOFLOWLABEL = 0x3b
- IPV6_BINDANY = 0x40
- IPV6_BINDV6ONLY = 0x1b
- IPV6_CHECKSUM = 0x1a
- IPV6_DEFAULT_MULTICAST_HOPS = 0x1
- IPV6_DEFAULT_MULTICAST_LOOP = 0x1
- IPV6_DEFHLIM = 0x40
- IPV6_DONTFRAG = 0x3e
- IPV6_DSTOPTS = 0x32
- IPV6_FAITH = 0x1d
- IPV6_FLOWINFO_MASK = 0xffffff0f
- IPV6_FLOWLABEL_MASK = 0xffff0f00
- IPV6_FRAGTTL = 0x78
- IPV6_FW_ADD = 0x1e
- IPV6_FW_DEL = 0x1f
- IPV6_FW_FLUSH = 0x20
- IPV6_FW_GET = 0x22
- IPV6_FW_ZERO = 0x21
- IPV6_HLIMDEC = 0x1
- IPV6_HOPLIMIT = 0x2f
- IPV6_HOPOPTS = 0x31
- IPV6_IPSEC_POLICY = 0x1c
- IPV6_JOIN_GROUP = 0xc
- IPV6_LEAVE_GROUP = 0xd
- IPV6_MAXHLIM = 0xff
- IPV6_MAXOPTHDR = 0x800
- IPV6_MAXPACKET = 0xffff
- IPV6_MAX_GROUP_SRC_FILTER = 0x200
- IPV6_MAX_MEMBERSHIPS = 0xfff
- IPV6_MAX_SOCK_SRC_FILTER = 0x80
- IPV6_MIN_MEMBERSHIPS = 0x1f
- IPV6_MMTU = 0x500
- IPV6_MSFILTER = 0x4a
- IPV6_MULTICAST_HOPS = 0xa
- IPV6_MULTICAST_IF = 0x9
- IPV6_MULTICAST_LOOP = 0xb
- IPV6_NEXTHOP = 0x30
- IPV6_PATHMTU = 0x2c
- IPV6_PKTINFO = 0x2e
- IPV6_PORTRANGE = 0xe
- IPV6_PORTRANGE_DEFAULT = 0
- IPV6_PORTRANGE_HIGH = 0x1
- IPV6_PORTRANGE_LOW = 0x2
- IPV6_PREFER_TEMPADDR = 0x3f
- IPV6_RECVDSTOPTS = 0x28
- IPV6_RECVHOPLIMIT = 0x25
- IPV6_RECVHOPOPTS = 0x27
- IPV6_RECVPATHMTU = 0x2b
- IPV6_RECVPKTINFO = 0x24
- IPV6_RECVRTHDR = 0x26
- IPV6_RECVTCLASS = 0x39
- IPV6_RTHDR = 0x33
- IPV6_RTHDRDSTOPTS = 0x23
- IPV6_RTHDR_LOOSE = 0
- IPV6_RTHDR_STRICT = 0x1
- IPV6_RTHDR_TYPE_0 = 0
- IPV6_SOCKOPT_RESERVED1 = 0x3
- IPV6_TCLASS = 0x3d
- IPV6_UNICAST_HOPS = 0x4
- IPV6_USE_MIN_MTU = 0x2a
- IPV6_V6ONLY = 0x1b
- IPV6_VERSION = 0x60
- IPV6_VERSION_MASK = 0xf0
- IP_ADD_MEMBERSHIP = 0xc
- IP_ADD_SOURCE_MEMBERSHIP = 0x46
- IP_BINDANY = 0x18
- IP_BLOCK_SOURCE = 0x48
- IP_DEFAULT_MULTICAST_LOOP = 0x1
- IP_DEFAULT_MULTICAST_TTL = 0x1
- IP_DF = 0x4000
- IP_DONTFRAG = 0x43
- IP_DROP_MEMBERSHIP = 0xd
- IP_DROP_SOURCE_MEMBERSHIP = 0x47
- IP_DUMMYNET3 = 0x31
- IP_DUMMYNET_CONFIGURE = 0x3c
- IP_DUMMYNET_DEL = 0x3d
- IP_DUMMYNET_FLUSH = 0x3e
- IP_DUMMYNET_GET = 0x40
- IP_FAITH = 0x16
- IP_FW3 = 0x30
- IP_FW_ADD = 0x32
- IP_FW_DEL = 0x33
- IP_FW_FLUSH = 0x34
- IP_FW_GET = 0x36
- IP_FW_NAT_CFG = 0x38
- IP_FW_NAT_DEL = 0x39
- IP_FW_NAT_GET_CONFIG = 0x3a
- IP_FW_NAT_GET_LOG = 0x3b
- IP_FW_RESETLOG = 0x37
- IP_FW_TABLE_ADD = 0x28
- IP_FW_TABLE_DEL = 0x29
- IP_FW_TABLE_FLUSH = 0x2a
- IP_FW_TABLE_GETSIZE = 0x2b
- IP_FW_TABLE_LIST = 0x2c
- IP_FW_ZERO = 0x35
- IP_HDRINCL = 0x2
- IP_IPSEC_POLICY = 0x15
- IP_MAXPACKET = 0xffff
- IP_MAX_GROUP_SRC_FILTER = 0x200
- IP_MAX_MEMBERSHIPS = 0xfff
- IP_MAX_SOCK_MUTE_FILTER = 0x80
- IP_MAX_SOCK_SRC_FILTER = 0x80
- IP_MAX_SOURCE_FILTER = 0x400
- IP_MF = 0x2000
- IP_MINTTL = 0x42
- IP_MIN_MEMBERSHIPS = 0x1f
- IP_MSFILTER = 0x4a
- IP_MSS = 0x240
- IP_MULTICAST_IF = 0x9
- IP_MULTICAST_LOOP = 0xb
- IP_MULTICAST_TTL = 0xa
- IP_MULTICAST_VIF = 0xe
- IP_OFFMASK = 0x1fff
- IP_ONESBCAST = 0x17
- IP_OPTIONS = 0x1
- IP_PORTRANGE = 0x13
- IP_PORTRANGE_DEFAULT = 0
- IP_PORTRANGE_HIGH = 0x1
- IP_PORTRANGE_LOW = 0x2
- IP_RECVDSTADDR = 0x7
- IP_RECVIF = 0x14
- IP_RECVOPTS = 0x5
- IP_RECVRETOPTS = 0x6
- IP_RECVTTL = 0x41
- IP_RETOPTS = 0x8
- IP_RF = 0x8000
- IP_RSVP_OFF = 0x10
- IP_RSVP_ON = 0xf
- IP_RSVP_VIF_OFF = 0x12
- IP_RSVP_VIF_ON = 0x11
- IP_SENDSRCADDR = 0x7
- IP_TOS = 0x3
- IP_TTL = 0x4
- IP_UNBLOCK_SOURCE = 0x49
- MSG_COMPAT = 0x8000
- MSG_CTRUNC = 0x20
- MSG_DONTROUTE = 0x4
- MSG_DONTWAIT = 0x80
- MSG_EOF = 0x100
- MSG_EOR = 0x8
- MSG_NBIO = 0x4000
- MSG_NOSIGNAL = 0x20000
- MSG_NOTIFICATION = 0x2000
- MSG_OOB = 0x1
- MSG_PEEK = 0x2
- MSG_TRUNC = 0x10
- MSG_WAITALL = 0x40
- NET_RT_DUMP = 0x1
- NET_RT_FLAGS = 0x2
- NET_RT_IFLIST = 0x3
- NET_RT_IFMALIST = 0x4
- NET_RT_MAXID = 0x5
- O_ACCMODE = 0x3
- O_APPEND = 0x8
- O_ASYNC = 0x40
- O_CREAT = 0x200
- O_DIRECT = 0x10000
- O_DIRECTORY = 0x20000
- O_EXCL = 0x800
- O_EXEC = 0x40000
- O_EXLOCK = 0x20
- O_FSYNC = 0x80
- O_NDELAY = 0x4
- O_NOCTTY = 0x8000
- O_NOFOLLOW = 0x100
- O_NONBLOCK = 0x4
- O_RDONLY = 0
- O_RDWR = 0x2
- O_SHLOCK = 0x10
- O_SYNC = 0x80
- O_TRUNC = 0x400
- O_TTY_INIT = 0x80000
- O_WRONLY = 0x1
- RTAX_AUTHOR = 0x6
- RTAX_BRD = 0x7
- RTAX_DST = 0
- RTAX_GATEWAY = 0x1
- RTAX_GENMASK = 0x3
- RTAX_IFA = 0x5
- RTAX_IFP = 0x4
- RTAX_MAX = 0x8
- RTAX_NETMASK = 0x2
- RTA_AUTHOR = 0x40
- RTA_BRD = 0x80
- RTA_DST = 0x1
- RTA_GATEWAY = 0x2
- RTA_GENMASK = 0x8
- RTA_IFA = 0x20
- RTA_IFP = 0x10
- RTA_NETMASK = 0x4
- RTF_BLACKHOLE = 0x1000
- RTF_BROADCAST = 0x400000
- RTF_DONE = 0x40
- RTF_DYNAMIC = 0x10
- RTF_FMASK = 0x1004d808
- RTF_GATEWAY = 0x2
- RTF_HOST = 0x4
- RTF_LLDATA = 0x400
- RTF_LLINFO = 0x400
- RTF_LOCAL = 0x200000
- RTF_MODIFIED = 0x20
- RTF_MULTICAST = 0x800000
- RTF_PINNED = 0x100000
- RTF_PRCLONING = 0x10000
- RTF_PROTO1 = 0x8000
- RTF_PROTO2 = 0x4000
- RTF_PROTO3 = 0x40000
- RTF_REJECT = 0x8
- RTF_RNH_LOCKED = 0x40000000
- RTF_STATIC = 0x800
- RTF_STICKY = 0x10000000
- RTF_UP = 0x1
- RTF_XRESOLVE = 0x200
- RTM_ADD = 0x1
- RTM_CHANGE = 0x3
- RTM_DELADDR = 0xd
- RTM_DELETE = 0x2
- RTM_DELMADDR = 0x10
- RTM_GET = 0x4
- RTM_IEEE80211 = 0x12
- RTM_IFANNOUNCE = 0x11
- RTM_IFINFO = 0xe
- RTM_LOCK = 0x8
- RTM_LOSING = 0x5
- RTM_MISS = 0x7
- RTM_NEWADDR = 0xc
- RTM_NEWMADDR = 0xf
- RTM_OLDADD = 0x9
- RTM_OLDDEL = 0xa
- RTM_REDIRECT = 0x6
- RTM_RESOLVE = 0xb
- RTM_RTTUNIT = 0xf4240
- RTM_VERSION = 0x5
- RTV_EXPIRE = 0x4
- RTV_HOPCOUNT = 0x2
- RTV_MTU = 0x1
- RTV_RPIPE = 0x8
- RTV_RTT = 0x40
- RTV_RTTVAR = 0x80
- RTV_SPIPE = 0x10
- RTV_SSTHRESH = 0x20
- RTV_WEIGHT = 0x100
- SCM_BINTIME = 0x4
- SCM_CREDS = 0x3
- SCM_RIGHTS = 0x1
- SCM_TIMESTAMP = 0x2
- SHUT_RD = 0
- SHUT_RDWR = 0x2
- SHUT_WR = 0x1
- SIGABRT = 0x6
- SIGALRM = 0xe
- SIGBUS = 0xa
- SIGCHLD = 0x14
- SIGCONT = 0x13
- SIGEMT = 0x7
- SIGFPE = 0x8
- SIGHUP = 0x1
- SIGILL = 0x4
- SIGINFO = 0x1d
- SIGINT = 0x2
- SIGIO = 0x17
- SIGIOT = 0x6
- SIGKILL = 0x9
- SIGLWP = 0x20
- SIGPIPE = 0xd
- SIGPROF = 0x1b
- SIGQUIT = 0x3
- SIGSEGV = 0xb
- SIGSTOP = 0x11
- SIGSYS = 0xc
- SIGTERM = 0xf
- SIGTHR = 0x20
- SIGTRAP = 0x5
- SIGTSTP = 0x12
- SIGTTIN = 0x15
- SIGTTOU = 0x16
- SIGURG = 0x10
- SIGUSR1 = 0x1e
- SIGUSR2 = 0x1f
- SIGVTALRM = 0x1a
- SIGWINCH = 0x1c
- SIGXCPU = 0x18
- SIGXFSZ = 0x19
- SIOCADDMULTI = 0x80206931
- SIOCADDRT = 0x8040720a
- SIOCAIFADDR = 0x8040691a
- SIOCAIFGROUP = 0x80286987
- SIOCALIFADDR = 0x8118691b
- SIOCATMARK = 0x40047307
- SIOCDELMULTI = 0x80206932
- SIOCDELRT = 0x8040720b
- SIOCDIFADDR = 0x80206919
- SIOCDIFGROUP = 0x80286989
- SIOCDIFPHYADDR = 0x80206949
- SIOCDLIFADDR = 0x8118691d
- SIOCGDRVSPEC = 0xc028697b
- SIOCGETSGCNT = 0xc0207210
- SIOCGETVIFCNT = 0xc028720f
- SIOCGHIWAT = 0x40047301
- SIOCGIFADDR = 0xc0206921
- SIOCGIFBRDADDR = 0xc0206923
- SIOCGIFCAP = 0xc020691f
- SIOCGIFCONF = 0xc0106924
- SIOCGIFDESCR = 0xc020692a
- SIOCGIFDSTADDR = 0xc0206922
- SIOCGIFFLAGS = 0xc0206911
- SIOCGIFGENERIC = 0xc020693a
- SIOCGIFGMEMB = 0xc028698a
- SIOCGIFGROUP = 0xc0286988
- SIOCGIFINDEX = 0xc0206920
- SIOCGIFMAC = 0xc0206926
- SIOCGIFMEDIA = 0xc0306938
- SIOCGIFMETRIC = 0xc0206917
- SIOCGIFMTU = 0xc0206933
- SIOCGIFNETMASK = 0xc0206925
- SIOCGIFPDSTADDR = 0xc0206948
- SIOCGIFPHYS = 0xc0206935
- SIOCGIFPSRCADDR = 0xc0206947
- SIOCGIFSTATUS = 0xc331693b
- SIOCGLIFADDR = 0xc118691c
- SIOCGLIFPHYADDR = 0xc118694b
- SIOCGLOWAT = 0x40047303
- SIOCGPGRP = 0x40047309
- SIOCGPRIVATE_0 = 0xc0206950
- SIOCGPRIVATE_1 = 0xc0206951
- SIOCIFCREATE = 0xc020697a
- SIOCIFCREATE2 = 0xc020697c
- SIOCIFDESTROY = 0x80206979
- SIOCIFGCLONERS = 0xc0106978
- SIOCSDRVSPEC = 0x8028697b
- SIOCSHIWAT = 0x80047300
- SIOCSIFADDR = 0x8020690c
- SIOCSIFBRDADDR = 0x80206913
- SIOCSIFCAP = 0x8020691e
- SIOCSIFDESCR = 0x80206929
- SIOCSIFDSTADDR = 0x8020690e
- SIOCSIFFLAGS = 0x80206910
- SIOCSIFGENERIC = 0x80206939
- SIOCSIFLLADDR = 0x8020693c
- SIOCSIFMAC = 0x80206927
- SIOCSIFMEDIA = 0xc0206937
- SIOCSIFMETRIC = 0x80206918
- SIOCSIFMTU = 0x80206934
- SIOCSIFNAME = 0x80206928
- SIOCSIFNETMASK = 0x80206916
- SIOCSIFPHYADDR = 0x80406946
- SIOCSIFPHYS = 0x80206936
- SIOCSIFRVNET = 0xc020695b
- SIOCSIFVNET = 0xc020695a
- SIOCSLIFPHYADDR = 0x8118694a
- SIOCSLOWAT = 0x80047302
- SIOCSPGRP = 0x80047308
- SOCK_DGRAM = 0x2
- SOCK_MAXADDRLEN = 0xff
- SOCK_RAW = 0x3
- SOCK_RDM = 0x4
- SOCK_SEQPACKET = 0x5
- SOCK_STREAM = 0x1
- SOL_SOCKET = 0xffff
- SOMAXCONN = 0x80
- SO_ACCEPTCONN = 0x2
- SO_ACCEPTFILTER = 0x1000
- SO_BINTIME = 0x2000
- SO_BROADCAST = 0x20
- SO_DEBUG = 0x1
- SO_DONTROUTE = 0x10
- SO_ERROR = 0x1007
- SO_KEEPALIVE = 0x8
- SO_LABEL = 0x1009
- SO_LINGER = 0x80
- SO_LISTENINCQLEN = 0x1013
- SO_LISTENQLEN = 0x1012
- SO_LISTENQLIMIT = 0x1011
- SO_NOSIGPIPE = 0x800
- SO_NO_DDP = 0x8000
- SO_NO_OFFLOAD = 0x4000
- SO_OOBINLINE = 0x100
- SO_PEERLABEL = 0x1010
- SO_RCVBUF = 0x1002
- SO_RCVLOWAT = 0x1004
- SO_RCVTIMEO = 0x1006
- SO_REUSEADDR = 0x4
- SO_REUSEPORT = 0x200
- SO_SETFIB = 0x1014
- SO_SNDBUF = 0x1001
- SO_SNDLOWAT = 0x1003
- SO_SNDTIMEO = 0x1005
- SO_TIMESTAMP = 0x400
- SO_TYPE = 0x1008
- SO_USELOOPBACK = 0x40
- TCP_CA_NAME_MAX = 0x10
- TCP_CONGESTION = 0x40
- TCP_INFO = 0x20
- TCP_MAXBURST = 0x4
- TCP_MAXHLEN = 0x3c
- TCP_MAXOLEN = 0x28
- TCP_MAXSEG = 0x2
- TCP_MAXWIN = 0xffff
- TCP_MAX_SACK = 0x4
- TCP_MAX_WINSHIFT = 0xe
- TCP_MD5SIG = 0x10
- TCP_MINMSS = 0xd8
- TCP_MSS = 0x200
- TCP_NODELAY = 0x1
- TCP_NOOPT = 0x8
- TCP_NOPUSH = 0x4
- WCONTINUED = 0x4
- WCOREFLAG = 0x80
- WLINUXCLONE = 0x80000000
- WNOHANG = 0x1
- WNOWAIT = 0x8
- WSTOPPED = 0x2
- WUNTRACED = 0x2
+ AF_APPLETALK = 0x10
+ AF_ARP = 0x23
+ AF_ATM = 0x1e
+ AF_BLUETOOTH = 0x24
+ AF_CCITT = 0xa
+ AF_CHAOS = 0x5
+ AF_CNT = 0x15
+ AF_COIP = 0x14
+ AF_DATAKIT = 0x9
+ AF_DECnet = 0xc
+ AF_DLI = 0xd
+ AF_E164 = 0x1a
+ AF_ECMA = 0x8
+ AF_HYLINK = 0xf
+ AF_IEEE80211 = 0x25
+ AF_IMPLINK = 0x3
+ AF_INET = 0x2
+ AF_INET6 = 0x1c
+ AF_IPX = 0x17
+ AF_ISDN = 0x1a
+ AF_ISO = 0x7
+ AF_LAT = 0xe
+ AF_LINK = 0x12
+ AF_LOCAL = 0x1
+ AF_MAX = 0x26
+ AF_NATM = 0x1d
+ AF_NETBIOS = 0x6
+ AF_NETGRAPH = 0x20
+ AF_OSI = 0x7
+ AF_PUP = 0x4
+ AF_ROUTE = 0x11
+ AF_SCLUSTER = 0x22
+ AF_SIP = 0x18
+ AF_SLOW = 0x21
+ AF_SNA = 0xb
+ AF_UNIX = 0x1
+ AF_UNSPEC = 0
+ AF_VENDOR00 = 0x27
+ AF_VENDOR01 = 0x29
+ AF_VENDOR02 = 0x2b
+ AF_VENDOR03 = 0x2d
+ AF_VENDOR04 = 0x2f
+ AF_VENDOR05 = 0x31
+ AF_VENDOR06 = 0x33
+ AF_VENDOR07 = 0x35
+ AF_VENDOR08 = 0x37
+ AF_VENDOR09 = 0x39
+ AF_VENDOR10 = 0x3b
+ AF_VENDOR11 = 0x3d
+ AF_VENDOR12 = 0x3f
+ AF_VENDOR13 = 0x41
+ AF_VENDOR14 = 0x43
+ AF_VENDOR15 = 0x45
+ AF_VENDOR16 = 0x47
+ AF_VENDOR17 = 0x49
+ AF_VENDOR18 = 0x4b
+ AF_VENDOR19 = 0x4d
+ AF_VENDOR20 = 0x4f
+ AF_VENDOR21 = 0x51
+ AF_VENDOR22 = 0x53
+ AF_VENDOR23 = 0x55
+ AF_VENDOR24 = 0x57
+ AF_VENDOR25 = 0x59
+ AF_VENDOR26 = 0x5b
+ AF_VENDOR27 = 0x5d
+ AF_VENDOR28 = 0x5f
+ AF_VENDOR29 = 0x61
+ AF_VENDOR30 = 0x63
+ AF_VENDOR31 = 0x65
+ AF_VENDOR32 = 0x67
+ AF_VENDOR33 = 0x69
+ AF_VENDOR34 = 0x6b
+ AF_VENDOR35 = 0x6d
+ AF_VENDOR36 = 0x6f
+ AF_VENDOR37 = 0x71
+ AF_VENDOR38 = 0x73
+ AF_VENDOR39 = 0x75
+ AF_VENDOR40 = 0x77
+ AF_VENDOR41 = 0x79
+ AF_VENDOR42 = 0x7b
+ AF_VENDOR43 = 0x7d
+ AF_VENDOR44 = 0x7f
+ AF_VENDOR45 = 0x81
+ AF_VENDOR46 = 0x83
+ AF_VENDOR47 = 0x85
+ BIOCFEEDBACK = 0x8004427c
+ BIOCFLUSH = 0x20004268
+ BIOCGBLEN = 0x40044266
+ BIOCGDIRECTION = 0x40044276
+ BIOCGDLT = 0x4004426a
+ BIOCGDLTLIST = 0xc0104279
+ BIOCGETBUFMODE = 0x4004427d
+ BIOCGETIF = 0x4020426b
+ BIOCGETZMAX = 0x4008427f
+ BIOCGHDRCMPLT = 0x40044274
+ BIOCGRSIG = 0x40044272
+ BIOCGRTIMEOUT = 0x4010426e
+ BIOCGSEESENT = 0x40044276
+ BIOCGSTATS = 0x4008426f
+ BIOCIMMEDIATE = 0x80044270
+ BIOCLOCK = 0x2000427a
+ BIOCPROMISC = 0x20004269
+ BIOCROTZBUF = 0x40184280
+ BIOCSBLEN = 0xc0044266
+ BIOCSDIRECTION = 0x80044277
+ BIOCSDLT = 0x80044278
+ BIOCSETBUFMODE = 0x8004427e
+ BIOCSETF = 0x80104267
+ BIOCSETFNR = 0x80104282
+ BIOCSETIF = 0x8020426c
+ BIOCSETWF = 0x8010427b
+ BIOCSETZBUF = 0x80184281
+ BIOCSHDRCMPLT = 0x80044275
+ BIOCSRSIG = 0x80044273
+ BIOCSRTIMEOUT = 0x8010426d
+ BIOCSSEESENT = 0x80044277
+ BIOCVERSION = 0x40044271
+ BPF_A = 0x10
+ BPF_ABS = 0x20
+ BPF_ADD = 0
+ BPF_ALIGNMENT = 0x8
+ BPF_ALU = 0x4
+ BPF_AND = 0x50
+ BPF_B = 0x10
+ BPF_BUFMODE_BUFFER = 0x1
+ BPF_BUFMODE_ZBUF = 0x2
+ BPF_DIV = 0x30
+ BPF_H = 0x8
+ BPF_IMM = 0
+ BPF_IND = 0x40
+ BPF_JA = 0
+ BPF_JEQ = 0x10
+ BPF_JGE = 0x30
+ BPF_JGT = 0x20
+ BPF_JMP = 0x5
+ BPF_JSET = 0x40
+ BPF_K = 0
+ BPF_LD = 0
+ BPF_LDX = 0x1
+ BPF_LEN = 0x80
+ BPF_LSH = 0x60
+ BPF_MAJOR_VERSION = 0x1
+ BPF_MAXBUFSIZE = 0x80000
+ BPF_MAXINSNS = 0x200
+ BPF_MEM = 0x60
+ BPF_MEMWORDS = 0x10
+ BPF_MINBUFSIZE = 0x20
+ BPF_MINOR_VERSION = 0x1
+ BPF_MISC = 0x7
+ BPF_MSH = 0xa0
+ BPF_MUL = 0x20
+ BPF_NEG = 0x80
+ BPF_OR = 0x40
+ BPF_RELEASE = 0x30bb6
+ BPF_RET = 0x6
+ BPF_RSH = 0x70
+ BPF_ST = 0x2
+ BPF_STX = 0x3
+ BPF_SUB = 0x10
+ BPF_TAX = 0
+ BPF_TXA = 0x80
+ BPF_W = 0
+ BPF_X = 0x8
+ CTL_MAXNAME = 0x18
+ CTL_NET = 0x4
+ DLT_A429 = 0xb8
+ DLT_A653_ICM = 0xb9
+ DLT_AIRONET_HEADER = 0x78
+ DLT_APPLE_IP_OVER_IEEE1394 = 0x8a
+ DLT_ARCNET = 0x7
+ DLT_ARCNET_LINUX = 0x81
+ DLT_ATM_CLIP = 0x13
+ DLT_ATM_RFC1483 = 0xb
+ DLT_AURORA = 0x7e
+ DLT_AX25 = 0x3
+ DLT_AX25_KISS = 0xca
+ DLT_BACNET_MS_TP = 0xa5
+ DLT_BLUETOOTH_HCI_H4 = 0xbb
+ DLT_BLUETOOTH_HCI_H4_WITH_PHDR = 0xc9
+ DLT_CAN20B = 0xbe
+ DLT_CHAOS = 0x5
+ DLT_CHDLC = 0x68
+ DLT_CISCO_IOS = 0x76
+ DLT_C_HDLC = 0x68
+ DLT_C_HDLC_WITH_DIR = 0xcd
+ DLT_DOCSIS = 0x8f
+ DLT_ECONET = 0x73
+ DLT_EN10MB = 0x1
+ DLT_EN3MB = 0x2
+ DLT_ENC = 0x6d
+ DLT_ERF = 0xc5
+ DLT_ERF_ETH = 0xaf
+ DLT_ERF_POS = 0xb0
+ DLT_FDDI = 0xa
+ DLT_FLEXRAY = 0xd2
+ DLT_FRELAY = 0x6b
+ DLT_FRELAY_WITH_DIR = 0xce
+ DLT_GCOM_SERIAL = 0xad
+ DLT_GCOM_T1E1 = 0xac
+ DLT_GPF_F = 0xab
+ DLT_GPF_T = 0xaa
+ DLT_GPRS_LLC = 0xa9
+ DLT_HHDLC = 0x79
+ DLT_IBM_SN = 0x92
+ DLT_IBM_SP = 0x91
+ DLT_IEEE802 = 0x6
+ DLT_IEEE802_11 = 0x69
+ DLT_IEEE802_11_RADIO = 0x7f
+ DLT_IEEE802_11_RADIO_AVS = 0xa3
+ DLT_IEEE802_15_4 = 0xc3
+ DLT_IEEE802_15_4_LINUX = 0xbf
+ DLT_IEEE802_15_4_NONASK_PHY = 0xd7
+ DLT_IEEE802_16_MAC_CPS = 0xbc
+ DLT_IEEE802_16_MAC_CPS_RADIO = 0xc1
+ DLT_IPFILTER = 0x74
+ DLT_IPMB = 0xc7
+ DLT_IPMB_LINUX = 0xd1
+ DLT_IP_OVER_FC = 0x7a
+ DLT_JUNIPER_ATM1 = 0x89
+ DLT_JUNIPER_ATM2 = 0x87
+ DLT_JUNIPER_CHDLC = 0xb5
+ DLT_JUNIPER_ES = 0x84
+ DLT_JUNIPER_ETHER = 0xb2
+ DLT_JUNIPER_FRELAY = 0xb4
+ DLT_JUNIPER_GGSN = 0x85
+ DLT_JUNIPER_ISM = 0xc2
+ DLT_JUNIPER_MFR = 0x86
+ DLT_JUNIPER_MLFR = 0x83
+ DLT_JUNIPER_MLPPP = 0x82
+ DLT_JUNIPER_MONITOR = 0xa4
+ DLT_JUNIPER_PIC_PEER = 0xae
+ DLT_JUNIPER_PPP = 0xb3
+ DLT_JUNIPER_PPPOE = 0xa7
+ DLT_JUNIPER_PPPOE_ATM = 0xa8
+ DLT_JUNIPER_SERVICES = 0x88
+ DLT_JUNIPER_ST = 0xc8
+ DLT_JUNIPER_VP = 0xb7
+ DLT_LAPB_WITH_DIR = 0xcf
+ DLT_LAPD = 0xcb
+ DLT_LIN = 0xd4
+ DLT_LINUX_IRDA = 0x90
+ DLT_LINUX_LAPD = 0xb1
+ DLT_LINUX_PPP_WITHDIRECTION = 0xa6
+ DLT_LINUX_SLL = 0x71
+ DLT_LOOP = 0x6c
+ DLT_LTALK = 0x72
+ DLT_MFR = 0xb6
+ DLT_MOST = 0xd3
+ DLT_MTP2 = 0x8c
+ DLT_MTP2_WITH_PHDR = 0x8b
+ DLT_MTP3 = 0x8d
+ DLT_NULL = 0
+ DLT_PCI_EXP = 0x7d
+ DLT_PFLOG = 0x75
+ DLT_PFSYNC = 0x79
+ DLT_PPI = 0xc0
+ DLT_PPP = 0x9
+ DLT_PPP_BSDOS = 0x10
+ DLT_PPP_ETHER = 0x33
+ DLT_PPP_PPPD = 0xa6
+ DLT_PPP_SERIAL = 0x32
+ DLT_PPP_WITH_DIR = 0xcc
+ DLT_PPP_WITH_DIRECTION = 0xa6
+ DLT_PRISM_HEADER = 0x77
+ DLT_PRONET = 0x4
+ DLT_RAIF1 = 0xc6
+ DLT_RAW = 0xc
+ DLT_RIO = 0x7c
+ DLT_SCCP = 0x8e
+ DLT_SITA = 0xc4
+ DLT_SLIP = 0x8
+ DLT_SLIP_BSDOS = 0xf
+ DLT_SUNATM = 0x7b
+ DLT_SYMANTEC_FIREWALL = 0x63
+ DLT_TZSP = 0x80
+ DLT_USB = 0xba
+ DLT_USB_LINUX = 0xbd
+ DLT_USER0 = 0x93
+ DLT_USER1 = 0x94
+ DLT_USER10 = 0x9d
+ DLT_USER11 = 0x9e
+ DLT_USER12 = 0x9f
+ DLT_USER13 = 0xa0
+ DLT_USER14 = 0xa1
+ DLT_USER15 = 0xa2
+ DLT_USER2 = 0x95
+ DLT_USER3 = 0x96
+ DLT_USER4 = 0x97
+ DLT_USER5 = 0x98
+ DLT_USER6 = 0x99
+ DLT_USER7 = 0x9a
+ DLT_USER8 = 0x9b
+ DLT_USER9 = 0x9c
+ DLT_X2E_SERIAL = 0xd5
+ DLT_X2E_XORAYA = 0xd6
+ E2BIG = 0x7
+ EACCES = 0xd
+ EADDRINUSE = 0x30
+ EADDRNOTAVAIL = 0x31
+ EAFNOSUPPORT = 0x2f
+ EAGAIN = 0x23
+ EALREADY = 0x25
+ EAUTH = 0x50
+ EBADF = 0x9
+ EBADMSG = 0x59
+ EBADRPC = 0x48
+ EBUSY = 0x10
+ ECANCELED = 0x55
+ ECHILD = 0xa
+ ECONNABORTED = 0x35
+ ECONNREFUSED = 0x3d
+ ECONNRESET = 0x36
+ EDEADLK = 0xb
+ EDESTADDRREQ = 0x27
+ EDOM = 0x21
+ EDOOFUS = 0x58
+ EDQUOT = 0x45
+ EEXIST = 0x11
+ EFAULT = 0xe
+ EFBIG = 0x1b
+ EFTYPE = 0x4f
+ EHOSTDOWN = 0x40
+ EHOSTUNREACH = 0x41
+ EIDRM = 0x52
+ EILSEQ = 0x56
+ EINPROGRESS = 0x24
+ EINTR = 0x4
+ EINVAL = 0x16
+ EIO = 0x5
+ EISCONN = 0x38
+ EISDIR = 0x15
+ ELAST = 0x5d
+ ELOOP = 0x3e
+ EMFILE = 0x18
+ EMLINK = 0x1f
+ EMSGSIZE = 0x28
+ EMULTIHOP = 0x5a
+ ENAMETOOLONG = 0x3f
+ ENEEDAUTH = 0x51
+ ENETDOWN = 0x32
+ ENETRESET = 0x34
+ ENETUNREACH = 0x33
+ ENFILE = 0x17
+ ENOATTR = 0x57
+ ENOBUFS = 0x37
+ ENODEV = 0x13
+ ENOENT = 0x2
+ ENOEXEC = 0x8
+ ENOLCK = 0x4d
+ ENOLINK = 0x5b
+ ENOMEM = 0xc
+ ENOMSG = 0x53
+ ENOPROTOOPT = 0x2a
+ ENOSPC = 0x1c
+ ENOSYS = 0x4e
+ ENOTBLK = 0xf
+ ENOTCAPABLE = 0x5d
+ ENOTCONN = 0x39
+ ENOTDIR = 0x14
+ ENOTEMPTY = 0x42
+ ENOTSOCK = 0x26
+ ENOTSUP = 0x2d
+ ENOTTY = 0x19
+ ENXIO = 0x6
+ EOPNOTSUPP = 0x2d
+ EOVERFLOW = 0x54
+ EPERM = 0x1
+ EPFNOSUPPORT = 0x2e
+ EPIPE = 0x20
+ EPROCLIM = 0x43
+ EPROCUNAVAIL = 0x4c
+ EPROGMISMATCH = 0x4b
+ EPROGUNAVAIL = 0x4a
+ EPROTO = 0x5c
+ EPROTONOSUPPORT = 0x2b
+ EPROTOTYPE = 0x29
+ ERANGE = 0x22
+ EREMOTE = 0x47
+ EROFS = 0x1e
+ ERPCMISMATCH = 0x49
+ ESHUTDOWN = 0x3a
+ ESOCKTNOSUPPORT = 0x2c
+ ESPIPE = 0x1d
+ ESRCH = 0x3
+ ESTALE = 0x46
+ ETIMEDOUT = 0x3c
+ ETOOMANYREFS = 0x3b
+ ETXTBSY = 0x1a
+ EUSERS = 0x44
+ EVFILT_AIO = -0x3
+ EVFILT_FS = -0x9
+ EVFILT_LIO = -0xa
+ EVFILT_PROC = -0x5
+ EVFILT_READ = -0x1
+ EVFILT_SIGNAL = -0x6
+ EVFILT_SYSCOUNT = 0xb
+ EVFILT_TIMER = -0x7
+ EVFILT_USER = -0xb
+ EVFILT_VNODE = -0x4
+ EVFILT_WRITE = -0x2
+ EV_ADD = 0x1
+ EV_CLEAR = 0x20
+ EV_DELETE = 0x2
+ EV_DISABLE = 0x8
+ EV_DISPATCH = 0x80
+ EV_ENABLE = 0x4
+ EV_EOF = 0x8000
+ EV_ERROR = 0x4000
+ EV_FLAG1 = 0x2000
+ EV_ONESHOT = 0x10
+ EV_RECEIPT = 0x40
+ EV_SYSFLAGS = 0xf000
+ EWOULDBLOCK = 0x23
+ EXDEV = 0x12
+ FD_CLOEXEC = 0x1
+ FD_SETSIZE = 0x400
+ F_CANCEL = 0x5
+ F_DUP2FD = 0xa
+ F_DUPFD = 0
+ F_GETFD = 0x1
+ F_GETFL = 0x3
+ F_GETLK = 0xb
+ F_GETOWN = 0x5
+ F_OGETLK = 0x7
+ F_OSETLK = 0x8
+ F_OSETLKW = 0x9
+ F_RDAHEAD = 0x10
+ F_RDLCK = 0x1
+ F_READAHEAD = 0xf
+ F_SETFD = 0x2
+ F_SETFL = 0x4
+ F_SETLK = 0xc
+ F_SETLKW = 0xd
+ F_SETLK_REMOTE = 0xe
+ F_SETOWN = 0x6
+ F_UNLCK = 0x2
+ F_UNLCKSYS = 0x4
+ F_WRLCK = 0x3
+ IFF_ALLMULTI = 0x200
+ IFF_ALTPHYS = 0x4000
+ IFF_BROADCAST = 0x2
+ IFF_CANTCHANGE = 0x208f72
+ IFF_DEBUG = 0x4
+ IFF_DRV_OACTIVE = 0x400
+ IFF_DRV_RUNNING = 0x40
+ IFF_DYING = 0x200000
+ IFF_LINK0 = 0x1000
+ IFF_LINK1 = 0x2000
+ IFF_LINK2 = 0x4000
+ IFF_LOOPBACK = 0x8
+ IFF_MONITOR = 0x40000
+ IFF_MULTICAST = 0x8000
+ IFF_NOARP = 0x80
+ IFF_OACTIVE = 0x400
+ IFF_POINTOPOINT = 0x10
+ IFF_PPROMISC = 0x20000
+ IFF_PROMISC = 0x100
+ IFF_RENAMING = 0x400000
+ IFF_RUNNING = 0x40
+ IFF_SIMPLEX = 0x800
+ IFF_SMART = 0x20
+ IFF_STATICARP = 0x80000
+ IFF_UP = 0x1
+ IFNAMSIZ = 0x10
+ IN_CLASSA_HOST = 0xffffff
+ IN_CLASSA_MAX = 0x80
+ IN_CLASSA_NET = 0xff000000
+ IN_CLASSA_NSHIFT = 0x18
+ IN_CLASSB_HOST = 0xffff
+ IN_CLASSB_MAX = 0x10000
+ IN_CLASSB_NET = 0xffff0000
+ IN_CLASSB_NSHIFT = 0x10
+ IN_CLASSC_HOST = 0xff
+ IN_CLASSC_NET = 0xffffff00
+ IN_CLASSC_NSHIFT = 0x8
+ IN_CLASSD_HOST = 0xfffffff
+ IN_CLASSD_NET = 0xf0000000
+ IN_CLASSD_NSHIFT = 0x1c
+ IN_LOOPBACKNET = 0x7f
+ IPPROTO_3PC = 0x22
+ IPPROTO_ADFS = 0x44
+ IPPROTO_AH = 0x33
+ IPPROTO_AHIP = 0x3d
+ IPPROTO_APES = 0x63
+ IPPROTO_ARGUS = 0xd
+ IPPROTO_AX25 = 0x5d
+ IPPROTO_BHA = 0x31
+ IPPROTO_BLT = 0x1e
+ IPPROTO_BRSATMON = 0x4c
+ IPPROTO_CARP = 0x70
+ IPPROTO_CFTP = 0x3e
+ IPPROTO_CHAOS = 0x10
+ IPPROTO_CMTP = 0x26
+ IPPROTO_CPHB = 0x49
+ IPPROTO_CPNX = 0x48
+ IPPROTO_DDP = 0x25
+ IPPROTO_DGP = 0x56
+ IPPROTO_DIVERT = 0x102
+ IPPROTO_DONE = 0x101
+ IPPROTO_DSTOPTS = 0x3c
+ IPPROTO_EGP = 0x8
+ IPPROTO_EMCON = 0xe
+ IPPROTO_ENCAP = 0x62
+ IPPROTO_EON = 0x50
+ IPPROTO_ESP = 0x32
+ IPPROTO_ETHERIP = 0x61
+ IPPROTO_FRAGMENT = 0x2c
+ IPPROTO_GGP = 0x3
+ IPPROTO_GMTP = 0x64
+ IPPROTO_GRE = 0x2f
+ IPPROTO_HELLO = 0x3f
+ IPPROTO_HMP = 0x14
+ IPPROTO_HOPOPTS = 0
+ IPPROTO_ICMP = 0x1
+ IPPROTO_ICMPV6 = 0x3a
+ IPPROTO_IDP = 0x16
+ IPPROTO_IDPR = 0x23
+ IPPROTO_IDRP = 0x2d
+ IPPROTO_IGMP = 0x2
+ IPPROTO_IGP = 0x55
+ IPPROTO_IGRP = 0x58
+ IPPROTO_IL = 0x28
+ IPPROTO_INLSP = 0x34
+ IPPROTO_INP = 0x20
+ IPPROTO_IP = 0
+ IPPROTO_IPCOMP = 0x6c
+ IPPROTO_IPCV = 0x47
+ IPPROTO_IPEIP = 0x5e
+ IPPROTO_IPIP = 0x4
+ IPPROTO_IPPC = 0x43
+ IPPROTO_IPV4 = 0x4
+ IPPROTO_IPV6 = 0x29
+ IPPROTO_IRTP = 0x1c
+ IPPROTO_KRYPTOLAN = 0x41
+ IPPROTO_LARP = 0x5b
+ IPPROTO_LEAF1 = 0x19
+ IPPROTO_LEAF2 = 0x1a
+ IPPROTO_MAX = 0x100
+ IPPROTO_MAXID = 0x34
+ IPPROTO_MEAS = 0x13
+ IPPROTO_MHRP = 0x30
+ IPPROTO_MICP = 0x5f
+ IPPROTO_MOBILE = 0x37
+ IPPROTO_MTP = 0x5c
+ IPPROTO_MUX = 0x12
+ IPPROTO_ND = 0x4d
+ IPPROTO_NHRP = 0x36
+ IPPROTO_NONE = 0x3b
+ IPPROTO_NSP = 0x1f
+ IPPROTO_NVPII = 0xb
+ IPPROTO_OLD_DIVERT = 0xfe
+ IPPROTO_OSPFIGP = 0x59
+ IPPROTO_PFSYNC = 0xf0
+ IPPROTO_PGM = 0x71
+ IPPROTO_PIGP = 0x9
+ IPPROTO_PIM = 0x67
+ IPPROTO_PRM = 0x15
+ IPPROTO_PUP = 0xc
+ IPPROTO_PVP = 0x4b
+ IPPROTO_RAW = 0xff
+ IPPROTO_RCCMON = 0xa
+ IPPROTO_RDP = 0x1b
+ IPPROTO_ROUTING = 0x2b
+ IPPROTO_RSVP = 0x2e
+ IPPROTO_RVD = 0x42
+ IPPROTO_SATEXPAK = 0x40
+ IPPROTO_SATMON = 0x45
+ IPPROTO_SCCSP = 0x60
+ IPPROTO_SCTP = 0x84
+ IPPROTO_SDRP = 0x2a
+ IPPROTO_SEP = 0x21
+ IPPROTO_SKIP = 0x39
+ IPPROTO_SPACER = 0x7fff
+ IPPROTO_SRPC = 0x5a
+ IPPROTO_ST = 0x7
+ IPPROTO_SVMTP = 0x52
+ IPPROTO_SWIPE = 0x35
+ IPPROTO_TCF = 0x57
+ IPPROTO_TCP = 0x6
+ IPPROTO_TLSP = 0x38
+ IPPROTO_TP = 0x1d
+ IPPROTO_TPXX = 0x27
+ IPPROTO_TRUNK1 = 0x17
+ IPPROTO_TRUNK2 = 0x18
+ IPPROTO_TTP = 0x54
+ IPPROTO_UDP = 0x11
+ IPPROTO_VINES = 0x53
+ IPPROTO_VISA = 0x46
+ IPPROTO_VMTP = 0x51
+ IPPROTO_WBEXPAK = 0x4f
+ IPPROTO_WBMON = 0x4e
+ IPPROTO_WSN = 0x4a
+ IPPROTO_XNET = 0xf
+ IPPROTO_XTP = 0x24
+ IPV6_AUTOFLOWLABEL = 0x3b
+ IPV6_BINDANY = 0x40
+ IPV6_BINDV6ONLY = 0x1b
+ IPV6_CHECKSUM = 0x1a
+ IPV6_DEFAULT_MULTICAST_HOPS = 0x1
+ IPV6_DEFAULT_MULTICAST_LOOP = 0x1
+ IPV6_DEFHLIM = 0x40
+ IPV6_DONTFRAG = 0x3e
+ IPV6_DSTOPTS = 0x32
+ IPV6_FAITH = 0x1d
+ IPV6_FLOWINFO_MASK = 0xffffff0f
+ IPV6_FLOWLABEL_MASK = 0xffff0f00
+ IPV6_FRAGTTL = 0x78
+ IPV6_FW_ADD = 0x1e
+ IPV6_FW_DEL = 0x1f
+ IPV6_FW_FLUSH = 0x20
+ IPV6_FW_GET = 0x22
+ IPV6_FW_ZERO = 0x21
+ IPV6_HLIMDEC = 0x1
+ IPV6_HOPLIMIT = 0x2f
+ IPV6_HOPOPTS = 0x31
+ IPV6_IPSEC_POLICY = 0x1c
+ IPV6_JOIN_GROUP = 0xc
+ IPV6_LEAVE_GROUP = 0xd
+ IPV6_MAXHLIM = 0xff
+ IPV6_MAXOPTHDR = 0x800
+ IPV6_MAXPACKET = 0xffff
+ IPV6_MAX_GROUP_SRC_FILTER = 0x200
+ IPV6_MAX_MEMBERSHIPS = 0xfff
+ IPV6_MAX_SOCK_SRC_FILTER = 0x80
+ IPV6_MIN_MEMBERSHIPS = 0x1f
+ IPV6_MMTU = 0x500
+ IPV6_MSFILTER = 0x4a
+ IPV6_MULTICAST_HOPS = 0xa
+ IPV6_MULTICAST_IF = 0x9
+ IPV6_MULTICAST_LOOP = 0xb
+ IPV6_NEXTHOP = 0x30
+ IPV6_PATHMTU = 0x2c
+ IPV6_PKTINFO = 0x2e
+ IPV6_PORTRANGE = 0xe
+ IPV6_PORTRANGE_DEFAULT = 0
+ IPV6_PORTRANGE_HIGH = 0x1
+ IPV6_PORTRANGE_LOW = 0x2
+ IPV6_PREFER_TEMPADDR = 0x3f
+ IPV6_RECVDSTOPTS = 0x28
+ IPV6_RECVHOPLIMIT = 0x25
+ IPV6_RECVHOPOPTS = 0x27
+ IPV6_RECVPATHMTU = 0x2b
+ IPV6_RECVPKTINFO = 0x24
+ IPV6_RECVRTHDR = 0x26
+ IPV6_RECVTCLASS = 0x39
+ IPV6_RTHDR = 0x33
+ IPV6_RTHDRDSTOPTS = 0x23
+ IPV6_RTHDR_LOOSE = 0
+ IPV6_RTHDR_STRICT = 0x1
+ IPV6_RTHDR_TYPE_0 = 0
+ IPV6_SOCKOPT_RESERVED1 = 0x3
+ IPV6_TCLASS = 0x3d
+ IPV6_UNICAST_HOPS = 0x4
+ IPV6_USE_MIN_MTU = 0x2a
+ IPV6_V6ONLY = 0x1b
+ IPV6_VERSION = 0x60
+ IPV6_VERSION_MASK = 0xf0
+ IP_ADD_MEMBERSHIP = 0xc
+ IP_ADD_SOURCE_MEMBERSHIP = 0x46
+ IP_BINDANY = 0x18
+ IP_BLOCK_SOURCE = 0x48
+ IP_DEFAULT_MULTICAST_LOOP = 0x1
+ IP_DEFAULT_MULTICAST_TTL = 0x1
+ IP_DF = 0x4000
+ IP_DONTFRAG = 0x43
+ IP_DROP_MEMBERSHIP = 0xd
+ IP_DROP_SOURCE_MEMBERSHIP = 0x47
+ IP_DUMMYNET3 = 0x31
+ IP_DUMMYNET_CONFIGURE = 0x3c
+ IP_DUMMYNET_DEL = 0x3d
+ IP_DUMMYNET_FLUSH = 0x3e
+ IP_DUMMYNET_GET = 0x40
+ IP_FAITH = 0x16
+ IP_FW3 = 0x30
+ IP_FW_ADD = 0x32
+ IP_FW_DEL = 0x33
+ IP_FW_FLUSH = 0x34
+ IP_FW_GET = 0x36
+ IP_FW_NAT_CFG = 0x38
+ IP_FW_NAT_DEL = 0x39
+ IP_FW_NAT_GET_CONFIG = 0x3a
+ IP_FW_NAT_GET_LOG = 0x3b
+ IP_FW_RESETLOG = 0x37
+ IP_FW_TABLE_ADD = 0x28
+ IP_FW_TABLE_DEL = 0x29
+ IP_FW_TABLE_FLUSH = 0x2a
+ IP_FW_TABLE_GETSIZE = 0x2b
+ IP_FW_TABLE_LIST = 0x2c
+ IP_FW_ZERO = 0x35
+ IP_HDRINCL = 0x2
+ IP_IPSEC_POLICY = 0x15
+ IP_MAXPACKET = 0xffff
+ IP_MAX_GROUP_SRC_FILTER = 0x200
+ IP_MAX_MEMBERSHIPS = 0xfff
+ IP_MAX_SOCK_MUTE_FILTER = 0x80
+ IP_MAX_SOCK_SRC_FILTER = 0x80
+ IP_MAX_SOURCE_FILTER = 0x400
+ IP_MF = 0x2000
+ IP_MINTTL = 0x42
+ IP_MIN_MEMBERSHIPS = 0x1f
+ IP_MSFILTER = 0x4a
+ IP_MSS = 0x240
+ IP_MULTICAST_IF = 0x9
+ IP_MULTICAST_LOOP = 0xb
+ IP_MULTICAST_TTL = 0xa
+ IP_MULTICAST_VIF = 0xe
+ IP_OFFMASK = 0x1fff
+ IP_ONESBCAST = 0x17
+ IP_OPTIONS = 0x1
+ IP_PORTRANGE = 0x13
+ IP_PORTRANGE_DEFAULT = 0
+ IP_PORTRANGE_HIGH = 0x1
+ IP_PORTRANGE_LOW = 0x2
+ IP_RECVDSTADDR = 0x7
+ IP_RECVIF = 0x14
+ IP_RECVOPTS = 0x5
+ IP_RECVRETOPTS = 0x6
+ IP_RECVTTL = 0x41
+ IP_RETOPTS = 0x8
+ IP_RF = 0x8000
+ IP_RSVP_OFF = 0x10
+ IP_RSVP_ON = 0xf
+ IP_RSVP_VIF_OFF = 0x12
+ IP_RSVP_VIF_ON = 0x11
+ IP_SENDSRCADDR = 0x7
+ IP_TOS = 0x3
+ IP_TTL = 0x4
+ IP_UNBLOCK_SOURCE = 0x49
+ MSG_COMPAT = 0x8000
+ MSG_CTRUNC = 0x20
+ MSG_DONTROUTE = 0x4
+ MSG_DONTWAIT = 0x80
+ MSG_EOF = 0x100
+ MSG_EOR = 0x8
+ MSG_NBIO = 0x4000
+ MSG_NOSIGNAL = 0x20000
+ MSG_NOTIFICATION = 0x2000
+ MSG_OOB = 0x1
+ MSG_PEEK = 0x2
+ MSG_TRUNC = 0x10
+ MSG_WAITALL = 0x40
+ NET_RT_DUMP = 0x1
+ NET_RT_FLAGS = 0x2
+ NET_RT_IFLIST = 0x3
+ NET_RT_IFMALIST = 0x4
+ NET_RT_MAXID = 0x5
+ O_ACCMODE = 0x3
+ O_APPEND = 0x8
+ O_ASYNC = 0x40
+ O_CREAT = 0x200
+ O_DIRECT = 0x10000
+ O_DIRECTORY = 0x20000
+ O_EXCL = 0x800
+ O_EXEC = 0x40000
+ O_EXLOCK = 0x20
+ O_FSYNC = 0x80
+ O_NDELAY = 0x4
+ O_NOCTTY = 0x8000
+ O_NOFOLLOW = 0x100
+ O_NONBLOCK = 0x4
+ O_RDONLY = 0
+ O_RDWR = 0x2
+ O_SHLOCK = 0x10
+ O_SYNC = 0x80
+ O_TRUNC = 0x400
+ O_TTY_INIT = 0x80000
+ O_WRONLY = 0x1
+ RTAX_AUTHOR = 0x6
+ RTAX_BRD = 0x7
+ RTAX_DST = 0
+ RTAX_GATEWAY = 0x1
+ RTAX_GENMASK = 0x3
+ RTAX_IFA = 0x5
+ RTAX_IFP = 0x4
+ RTAX_MAX = 0x8
+ RTAX_NETMASK = 0x2
+ RTA_AUTHOR = 0x40
+ RTA_BRD = 0x80
+ RTA_DST = 0x1
+ RTA_GATEWAY = 0x2
+ RTA_GENMASK = 0x8
+ RTA_IFA = 0x20
+ RTA_IFP = 0x10
+ RTA_NETMASK = 0x4
+ RTF_BLACKHOLE = 0x1000
+ RTF_BROADCAST = 0x400000
+ RTF_DONE = 0x40
+ RTF_DYNAMIC = 0x10
+ RTF_FMASK = 0x1004d808
+ RTF_GATEWAY = 0x2
+ RTF_HOST = 0x4
+ RTF_LLDATA = 0x400
+ RTF_LLINFO = 0x400
+ RTF_LOCAL = 0x200000
+ RTF_MODIFIED = 0x20
+ RTF_MULTICAST = 0x800000
+ RTF_PINNED = 0x100000
+ RTF_PRCLONING = 0x10000
+ RTF_PROTO1 = 0x8000
+ RTF_PROTO2 = 0x4000
+ RTF_PROTO3 = 0x40000
+ RTF_REJECT = 0x8
+ RTF_RNH_LOCKED = 0x40000000
+ RTF_STATIC = 0x800
+ RTF_STICKY = 0x10000000
+ RTF_UP = 0x1
+ RTF_XRESOLVE = 0x200
+ RTM_ADD = 0x1
+ RTM_CHANGE = 0x3
+ RTM_DELADDR = 0xd
+ RTM_DELETE = 0x2
+ RTM_DELMADDR = 0x10
+ RTM_GET = 0x4
+ RTM_IEEE80211 = 0x12
+ RTM_IFANNOUNCE = 0x11
+ RTM_IFINFO = 0xe
+ RTM_LOCK = 0x8
+ RTM_LOSING = 0x5
+ RTM_MISS = 0x7
+ RTM_NEWADDR = 0xc
+ RTM_NEWMADDR = 0xf
+ RTM_OLDADD = 0x9
+ RTM_OLDDEL = 0xa
+ RTM_REDIRECT = 0x6
+ RTM_RESOLVE = 0xb
+ RTM_RTTUNIT = 0xf4240
+ RTM_VERSION = 0x5
+ RTV_EXPIRE = 0x4
+ RTV_HOPCOUNT = 0x2
+ RTV_MTU = 0x1
+ RTV_RPIPE = 0x8
+ RTV_RTT = 0x40
+ RTV_RTTVAR = 0x80
+ RTV_SPIPE = 0x10
+ RTV_SSTHRESH = 0x20
+ RTV_WEIGHT = 0x100
+ SCM_BINTIME = 0x4
+ SCM_CREDS = 0x3
+ SCM_RIGHTS = 0x1
+ SCM_TIMESTAMP = 0x2
+ SHUT_RD = 0
+ SHUT_RDWR = 0x2
+ SHUT_WR = 0x1
+ SIGABRT = 0x6
+ SIGALRM = 0xe
+ SIGBUS = 0xa
+ SIGCHLD = 0x14
+ SIGCONT = 0x13
+ SIGEMT = 0x7
+ SIGFPE = 0x8
+ SIGHUP = 0x1
+ SIGILL = 0x4
+ SIGINFO = 0x1d
+ SIGINT = 0x2
+ SIGIO = 0x17
+ SIGIOT = 0x6
+ SIGKILL = 0x9
+ SIGLWP = 0x20
+ SIGPIPE = 0xd
+ SIGPROF = 0x1b
+ SIGQUIT = 0x3
+ SIGSEGV = 0xb
+ SIGSTOP = 0x11
+ SIGSYS = 0xc
+ SIGTERM = 0xf
+ SIGTHR = 0x20
+ SIGTRAP = 0x5
+ SIGTSTP = 0x12
+ SIGTTIN = 0x15
+ SIGTTOU = 0x16
+ SIGURG = 0x10
+ SIGUSR1 = 0x1e
+ SIGUSR2 = 0x1f
+ SIGVTALRM = 0x1a
+ SIGWINCH = 0x1c
+ SIGXCPU = 0x18
+ SIGXFSZ = 0x19
+ SIOCADDMULTI = 0x80206931
+ SIOCADDRT = 0x8040720a
+ SIOCAIFADDR = 0x8040691a
+ SIOCAIFGROUP = 0x80286987
+ SIOCALIFADDR = 0x8118691b
+ SIOCATMARK = 0x40047307
+ SIOCDELMULTI = 0x80206932
+ SIOCDELRT = 0x8040720b
+ SIOCDIFADDR = 0x80206919
+ SIOCDIFGROUP = 0x80286989
+ SIOCDIFPHYADDR = 0x80206949
+ SIOCDLIFADDR = 0x8118691d
+ SIOCGDRVSPEC = 0xc028697b
+ SIOCGETSGCNT = 0xc0207210
+ SIOCGETVIFCNT = 0xc028720f
+ SIOCGHIWAT = 0x40047301
+ SIOCGIFADDR = 0xc0206921
+ SIOCGIFBRDADDR = 0xc0206923
+ SIOCGIFCAP = 0xc020691f
+ SIOCGIFCONF = 0xc0106924
+ SIOCGIFDESCR = 0xc020692a
+ SIOCGIFDSTADDR = 0xc0206922
+ SIOCGIFFLAGS = 0xc0206911
+ SIOCGIFGENERIC = 0xc020693a
+ SIOCGIFGMEMB = 0xc028698a
+ SIOCGIFGROUP = 0xc0286988
+ SIOCGIFINDEX = 0xc0206920
+ SIOCGIFMAC = 0xc0206926
+ SIOCGIFMEDIA = 0xc0306938
+ SIOCGIFMETRIC = 0xc0206917
+ SIOCGIFMTU = 0xc0206933
+ SIOCGIFNETMASK = 0xc0206925
+ SIOCGIFPDSTADDR = 0xc0206948
+ SIOCGIFPHYS = 0xc0206935
+ SIOCGIFPSRCADDR = 0xc0206947
+ SIOCGIFSTATUS = 0xc331693b
+ SIOCGLIFADDR = 0xc118691c
+ SIOCGLIFPHYADDR = 0xc118694b
+ SIOCGLOWAT = 0x40047303
+ SIOCGPGRP = 0x40047309
+ SIOCGPRIVATE_0 = 0xc0206950
+ SIOCGPRIVATE_1 = 0xc0206951
+ SIOCIFCREATE = 0xc020697a
+ SIOCIFCREATE2 = 0xc020697c
+ SIOCIFDESTROY = 0x80206979
+ SIOCIFGCLONERS = 0xc0106978
+ SIOCSDRVSPEC = 0x8028697b
+ SIOCSHIWAT = 0x80047300
+ SIOCSIFADDR = 0x8020690c
+ SIOCSIFBRDADDR = 0x80206913
+ SIOCSIFCAP = 0x8020691e
+ SIOCSIFDESCR = 0x80206929
+ SIOCSIFDSTADDR = 0x8020690e
+ SIOCSIFFLAGS = 0x80206910
+ SIOCSIFGENERIC = 0x80206939
+ SIOCSIFLLADDR = 0x8020693c
+ SIOCSIFMAC = 0x80206927
+ SIOCSIFMEDIA = 0xc0206937
+ SIOCSIFMETRIC = 0x80206918
+ SIOCSIFMTU = 0x80206934
+ SIOCSIFNAME = 0x80206928
+ SIOCSIFNETMASK = 0x80206916
+ SIOCSIFPHYADDR = 0x80406946
+ SIOCSIFPHYS = 0x80206936
+ SIOCSIFRVNET = 0xc020695b
+ SIOCSIFVNET = 0xc020695a
+ SIOCSLIFPHYADDR = 0x8118694a
+ SIOCSLOWAT = 0x80047302
+ SIOCSPGRP = 0x80047308
+ SOCK_DGRAM = 0x2
+ SOCK_MAXADDRLEN = 0xff
+ SOCK_RAW = 0x3
+ SOCK_RDM = 0x4
+ SOCK_SEQPACKET = 0x5
+ SOCK_STREAM = 0x1
+ SOL_SOCKET = 0xffff
+ SOMAXCONN = 0x80
+ SO_ACCEPTCONN = 0x2
+ SO_ACCEPTFILTER = 0x1000
+ SO_BINTIME = 0x2000
+ SO_BROADCAST = 0x20
+ SO_DEBUG = 0x1
+ SO_DONTROUTE = 0x10
+ SO_ERROR = 0x1007
+ SO_KEEPALIVE = 0x8
+ SO_LABEL = 0x1009
+ SO_LINGER = 0x80
+ SO_LISTENINCQLEN = 0x1013
+ SO_LISTENQLEN = 0x1012
+ SO_LISTENQLIMIT = 0x1011
+ SO_NOSIGPIPE = 0x800
+ SO_NO_DDP = 0x8000
+ SO_NO_OFFLOAD = 0x4000
+ SO_OOBINLINE = 0x100
+ SO_PEERLABEL = 0x1010
+ SO_RCVBUF = 0x1002
+ SO_RCVLOWAT = 0x1004
+ SO_RCVTIMEO = 0x1006
+ SO_REUSEADDR = 0x4
+ SO_REUSEPORT = 0x200
+ SO_SETFIB = 0x1014
+ SO_SNDBUF = 0x1001
+ SO_SNDLOWAT = 0x1003
+ SO_SNDTIMEO = 0x1005
+ SO_TIMESTAMP = 0x400
+ SO_TYPE = 0x1008
+ SO_USELOOPBACK = 0x40
+ TCP_CA_NAME_MAX = 0x10
+ TCP_CONGESTION = 0x40
+ TCP_INFO = 0x20
+ TCP_MAXBURST = 0x4
+ TCP_MAXHLEN = 0x3c
+ TCP_MAXOLEN = 0x28
+ TCP_MAXSEG = 0x2
+ TCP_MAXWIN = 0xffff
+ TCP_MAX_SACK = 0x4
+ TCP_MAX_WINSHIFT = 0xe
+ TCP_MD5SIG = 0x10
+ TCP_MINMSS = 0xd8
+ TCP_MSS = 0x200
+ TCP_NODELAY = 0x1
+ TCP_NOOPT = 0x8
+ TCP_NOPUSH = 0x4
+ WCONTINUED = 0x4
+ WCOREFLAG = 0x80
+ WLINUXCLONE = 0x80000000
+ WNOHANG = 0x1
+ WNOWAIT = 0x8
+ WSTOPPED = 0x2
+ WUNTRACED = 0x2
)
// Types
diff --git a/src/pkg/syscall/zerrors_linux_386.go b/src/pkg/syscall/zerrors_linux_386.go
index a73a94961..3fc7cc738 100644
--- a/src/pkg/syscall/zerrors_linux_386.go
+++ b/src/pkg/syscall/zerrors_linux_386.go
@@ -48,6 +48,15 @@ const (
AF_UNSPEC = 0
AF_WANPIPE = 0x19
AF_X25 = 0x9
+ DT_BLK = 0x6
+ DT_CHR = 0x2
+ DT_DIR = 0x4
+ DT_FIFO = 0x1
+ DT_LNK = 0xa
+ DT_REG = 0x8
+ DT_SOCK = 0xc
+ DT_UNKNOWN = 0
+ DT_WHT = 0xe
E2BIG = 0x7
EACCES = 0xd
EADDRINUSE = 0x62
@@ -408,6 +417,27 @@ const (
IP_TOS = 0x1
IP_TTL = 0x2
IP_UNBLOCK_SOURCE = 0x25
+ LINUX_REBOOT_CMD_CAD_OFF = 0
+ LINUX_REBOOT_CMD_CAD_ON = 0x89abcdef
+ LINUX_REBOOT_CMD_HALT = 0xcdef0123
+ LINUX_REBOOT_CMD_KEXEC = 0x45584543
+ LINUX_REBOOT_CMD_POWER_OFF = 0x4321fedc
+ LINUX_REBOOT_CMD_RESTART = 0x1234567
+ LINUX_REBOOT_CMD_RESTART2 = 0xa1b2c3d4
+ LINUX_REBOOT_CMD_SW_SUSPEND = 0xd000fce2
+ LINUX_REBOOT_MAGIC1 = 0xfee1dead
+ LINUX_REBOOT_MAGIC2 = 0x28121969
+ MADV_DOFORK = 0xb
+ MADV_DONTFORK = 0xa
+ MADV_DONTNEED = 0x4
+ MADV_HWPOISON = 0x64
+ MADV_MERGEABLE = 0xc
+ MADV_NORMAL = 0
+ MADV_RANDOM = 0x1
+ MADV_REMOVE = 0x9
+ MADV_SEQUENTIAL = 0x2
+ MADV_UNMERGEABLE = 0xd
+ MADV_WILLNEED = 0x3
MAP_32BIT = 0x40
MAP_ANON = 0x20
MAP_ANONYMOUS = 0x20
@@ -426,6 +456,9 @@ const (
MAP_TYPE = 0xf
MCL_CURRENT = 0x1
MCL_FUTURE = 0x2
+ MNT_DETACH = 0x2
+ MNT_EXPIRE = 0x4
+ MNT_FORCE = 0x1
MSG_CMSG_CLOEXEC = 0x40000000
MSG_CONFIRM = 0x800
MSG_CTRUNC = 0x8
@@ -444,6 +477,22 @@ const (
MSG_TRUNC = 0x20
MSG_TRYHARD = 0x4
MSG_WAITALL = 0x100
+ MS_ASYNC = 0x1
+ MS_BIND = 0x1000
+ MS_INVALIDATE = 0x2
+ MS_MANDLOCK = 0x40
+ MS_MGC_MSK = 0xffff0000
+ MS_MGC_VAL = 0xc0ed0000
+ MS_NOATIME = 0x400
+ MS_NODEV = 0x4
+ MS_NODIRATIME = 0x800
+ MS_NOEXEC = 0x8
+ MS_NOSUID = 0x2
+ MS_RDONLY = 0x1
+ MS_REMOUNT = 0x20
+ MS_RMT_MASK = 0xc51
+ MS_SYNC = 0x4
+ MS_SYNCHRONOUS = 0x10
NAME_MAX = 0xff
O_ACCMODE = 0x3
O_APPEND = 0x400
@@ -702,6 +751,7 @@ const (
SO_TIMESTAMPING = 0x25
SO_TIMESTAMPNS = 0x23
SO_TYPE = 0x3
+ S_APPEND = 0x100
S_BLKSIZE = 0x200
S_IEXEC = 0x40
S_IFBLK = 0x6000
@@ -712,6 +762,7 @@ const (
S_IFMT = 0xf000
S_IFREG = 0x8000
S_IFSOCK = 0xc000
+ S_IMMUTABLE = 0x200
S_IREAD = 0x100
S_IRGRP = 0x20
S_IROTH = 0x4
@@ -729,6 +780,7 @@ const (
S_IXGRP = 0x8
S_IXOTH = 0x1
S_IXUSR = 0x40
+ S_WRITE = 0x80
TCP_CONGESTION = 0xd
TCP_CORK = 0x3
TCP_DEFER_ACCEPT = 0x9
diff --git a/src/pkg/syscall/zerrors_linux_amd64.go b/src/pkg/syscall/zerrors_linux_amd64.go
index eea55a275..d5efdf55d 100644
--- a/src/pkg/syscall/zerrors_linux_amd64.go
+++ b/src/pkg/syscall/zerrors_linux_amd64.go
@@ -48,6 +48,15 @@ const (
AF_UNSPEC = 0
AF_WANPIPE = 0x19
AF_X25 = 0x9
+ DT_BLK = 0x6
+ DT_CHR = 0x2
+ DT_DIR = 0x4
+ DT_FIFO = 0x1
+ DT_LNK = 0xa
+ DT_REG = 0x8
+ DT_SOCK = 0xc
+ DT_UNKNOWN = 0
+ DT_WHT = 0xe
E2BIG = 0x7
EACCES = 0xd
EADDRINUSE = 0x62
@@ -408,6 +417,27 @@ const (
IP_TOS = 0x1
IP_TTL = 0x2
IP_UNBLOCK_SOURCE = 0x25
+ LINUX_REBOOT_CMD_CAD_OFF = 0
+ LINUX_REBOOT_CMD_CAD_ON = 0x89abcdef
+ LINUX_REBOOT_CMD_HALT = 0xcdef0123
+ LINUX_REBOOT_CMD_KEXEC = 0x45584543
+ LINUX_REBOOT_CMD_POWER_OFF = 0x4321fedc
+ LINUX_REBOOT_CMD_RESTART = 0x1234567
+ LINUX_REBOOT_CMD_RESTART2 = 0xa1b2c3d4
+ LINUX_REBOOT_CMD_SW_SUSPEND = 0xd000fce2
+ LINUX_REBOOT_MAGIC1 = 0xfee1dead
+ LINUX_REBOOT_MAGIC2 = 0x28121969
+ MADV_DOFORK = 0xb
+ MADV_DONTFORK = 0xa
+ MADV_DONTNEED = 0x4
+ MADV_HWPOISON = 0x64
+ MADV_MERGEABLE = 0xc
+ MADV_NORMAL = 0
+ MADV_RANDOM = 0x1
+ MADV_REMOVE = 0x9
+ MADV_SEQUENTIAL = 0x2
+ MADV_UNMERGEABLE = 0xd
+ MADV_WILLNEED = 0x3
MAP_32BIT = 0x40
MAP_ANON = 0x20
MAP_ANONYMOUS = 0x20
@@ -426,6 +456,9 @@ const (
MAP_TYPE = 0xf
MCL_CURRENT = 0x1
MCL_FUTURE = 0x2
+ MNT_DETACH = 0x2
+ MNT_EXPIRE = 0x4
+ MNT_FORCE = 0x1
MSG_CMSG_CLOEXEC = 0x40000000
MSG_CONFIRM = 0x800
MSG_CTRUNC = 0x8
@@ -444,6 +477,22 @@ const (
MSG_TRUNC = 0x20
MSG_TRYHARD = 0x4
MSG_WAITALL = 0x100
+ MS_ASYNC = 0x1
+ MS_BIND = 0x1000
+ MS_INVALIDATE = 0x2
+ MS_MANDLOCK = 0x40
+ MS_MGC_MSK = 0xffff0000
+ MS_MGC_VAL = 0xc0ed0000
+ MS_NOATIME = 0x400
+ MS_NODEV = 0x4
+ MS_NODIRATIME = 0x800
+ MS_NOEXEC = 0x8
+ MS_NOSUID = 0x2
+ MS_RDONLY = 0x1
+ MS_REMOUNT = 0x20
+ MS_RMT_MASK = 0xc51
+ MS_SYNC = 0x4
+ MS_SYNCHRONOUS = 0x10
NAME_MAX = 0xff
O_ACCMODE = 0x3
O_APPEND = 0x400
@@ -703,6 +752,7 @@ const (
SO_TIMESTAMPING = 0x25
SO_TIMESTAMPNS = 0x23
SO_TYPE = 0x3
+ S_APPEND = 0x100
S_BLKSIZE = 0x200
S_IEXEC = 0x40
S_IFBLK = 0x6000
@@ -713,6 +763,7 @@ const (
S_IFMT = 0xf000
S_IFREG = 0x8000
S_IFSOCK = 0xc000
+ S_IMMUTABLE = 0x200
S_IREAD = 0x100
S_IRGRP = 0x20
S_IROTH = 0x4
@@ -730,6 +781,7 @@ const (
S_IXGRP = 0x8
S_IXOTH = 0x1
S_IXUSR = 0x40
+ S_WRITE = 0x80
TCP_CONGESTION = 0xd
TCP_CORK = 0x3
TCP_DEFER_ACCEPT = 0x9
diff --git a/src/pkg/syscall/zerrors_linux_arm.go b/src/pkg/syscall/zerrors_linux_arm.go
index 1f8b1830b..356d51ca5 100644
--- a/src/pkg/syscall/zerrors_linux_arm.go
+++ b/src/pkg/syscall/zerrors_linux_arm.go
@@ -379,6 +379,35 @@ const (
IP_TOS = 0x1
IP_TTL = 0x2
IP_UNBLOCK_SOURCE = 0x25
+ LINUX_REBOOT_CMD_CAD_OFF = 0
+ LINUX_REBOOT_CMD_CAD_ON = 0x89abcdef
+ LINUX_REBOOT_CMD_HALT = 0xcdef0123
+ LINUX_REBOOT_CMD_KEXEC = 0x45584543
+ LINUX_REBOOT_CMD_POWER_OFF = 0x4321fedc
+ LINUX_REBOOT_CMD_RESTART = 0x1234567
+ LINUX_REBOOT_CMD_RESTART2 = 0xa1b2c3d4
+ LINUX_REBOOT_CMD_SW_SUSPEND = 0xd000fce2
+ LINUX_REBOOT_MAGIC1 = 0xfee1dead
+ LINUX_REBOOT_MAGIC2 = 0x28121969
+ MNT_DETACH = 0x2
+ MNT_EXPIRE = 0x4
+ MNT_FORCE = 0x1
+ MS_ASYNC = 0x1
+ MS_BIND = 0x1000
+ MS_INVALIDATE = 0x2
+ MS_MANDLOCK = 0x40
+ MS_MGC_MSK = 0xffff0000
+ MS_MGC_VAL = 0xc0ed0000
+ MS_NOATIME = 0x400
+ MS_NODEV = 0x4
+ MS_NODIRATIME = 0x800
+ MS_NOEXEC = 0x8
+ MS_NOSUID = 0x2
+ MS_RDONLY = 0x1
+ MS_REMOUNT = 0x20
+ MS_RMT_MASK = 0xc51
+ MS_SYNC = 0x4
+ MS_SYNCHRONOUS = 0x10
NAME_MAX = 0xff
O_ACCMODE = 0x3
O_APPEND = 0x400
@@ -542,6 +571,7 @@ const (
SO_TIMESTAMPING = 0x25
SO_TIMESTAMPNS = 0x23
SO_TYPE = 0x3
+ S_APPEND = 0x100
S_BLKSIZE = 0x200
S_IEXEC = 0x40
S_IFBLK = 0x6000
@@ -552,6 +582,7 @@ const (
S_IFMT = 0xf000
S_IFREG = 0x8000
S_IFSOCK = 0xc000
+ S_IMMUTABLE = 0x200
S_IREAD = 0x100
S_IRGRP = 0x20
S_IROTH = 0x4
@@ -569,6 +600,7 @@ const (
S_IXGRP = 0x8
S_IXOTH = 0x1
S_IXUSR = 0x40
+ S_WRITE = 0x80
TCP_CONGESTION = 0xd
TCP_CORK = 0x3
TCP_DEFER_ACCEPT = 0x9
diff --git a/src/pkg/syscall/zerrors_plan9_386.go b/src/pkg/syscall/zerrors_plan9_386.go
new file mode 100644
index 000000000..78b5c72bb
--- /dev/null
+++ b/src/pkg/syscall/zerrors_plan9_386.go
@@ -0,0 +1,25 @@
+package syscall
+
+// Constants
+const (
+ // Invented values to support what package os expects.
+ O_CREAT = 0x02000
+ O_NOCTTY = 0x00000
+ O_TRUNC = 0x00000
+ O_NONBLOCK = 0x00000
+ O_APPEND = 0x00000
+ O_SYNC = 0x00000
+ O_ASYNC = 0x00000
+
+
+ S_IFMT = 0x1f000
+ S_IFIFO = 0x1000
+ S_IFCHR = 0x2000
+ S_IFDIR = 0x4000
+ S_IFBLK = 0x6000
+ S_IFREG = 0x8000
+ S_IFLNK = 0xa000
+ S_IFSOCK = 0xc000
+)
+
+// Error table
diff --git a/src/pkg/syscall/zsyscall_darwin_386.go b/src/pkg/syscall/zsyscall_darwin_386.go
index 973f00ef8..2f5b2703b 100644
--- a/src/pkg/syscall/zsyscall_darwin_386.go
+++ b/src/pkg/syscall/zsyscall_darwin_386.go
@@ -1,4 +1,4 @@
-// mksyscall.sh -l32 syscall_bsd.go syscall_darwin.go syscall_darwin_386.go
+// mksyscall.pl -l32 syscall_bsd.go syscall_darwin.go syscall_darwin_386.go
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
package syscall
@@ -8,7 +8,7 @@ import "unsafe"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func getgroups(ngid int, gid *_Gid_t) (n int, errno int) {
- r0, _, e1 := Syscall(SYS_GETGROUPS, uintptr(ngid), uintptr(unsafe.Pointer(gid)), 0)
+ r0, _, e1 := RawSyscall(SYS_GETGROUPS, uintptr(ngid), uintptr(unsafe.Pointer(gid)), 0)
n = int(r0)
errno = int(e1)
return
@@ -17,7 +17,7 @@ func getgroups(ngid int, gid *_Gid_t) (n int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func setgroups(ngid int, gid *_Gid_t) (errno int) {
- _, _, e1 := Syscall(SYS_SETGROUPS, uintptr(ngid), uintptr(unsafe.Pointer(gid)), 0)
+ _, _, e1 := RawSyscall(SYS_SETGROUPS, uintptr(ngid), uintptr(unsafe.Pointer(gid)), 0)
errno = int(e1)
return
}
@@ -34,7 +34,7 @@ func wait4(pid int, wstatus *_C_int, options int, rusage *Rusage) (wpid int, err
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func pipe() (r int, w int, errno int) {
- r0, r1, e1 := Syscall(SYS_PIPE, 0, 0, 0)
+ r0, r1, e1 := RawSyscall(SYS_PIPE, 0, 0, 0)
r = int(r0)
w = int(r1)
errno = int(e1)
@@ -69,7 +69,7 @@ func connect(s int, addr uintptr, addrlen _Socklen) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func socket(domain int, typ int, proto int) (fd int, errno int) {
- r0, _, e1 := Syscall(SYS_SOCKET, uintptr(domain), uintptr(typ), uintptr(proto))
+ r0, _, e1 := RawSyscall(SYS_SOCKET, uintptr(domain), uintptr(typ), uintptr(proto))
fd = int(r0)
errno = int(e1)
return
@@ -77,6 +77,14 @@ func socket(domain int, typ int, proto int) (fd int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (errno int) {
+ _, _, e1 := Syscall6(SYS_GETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(unsafe.Pointer(vallen)), 0)
+ errno = int(e1)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func setsockopt(s int, level int, name int, val uintptr, vallen int) (errno int) {
_, _, e1 := Syscall6(SYS_SETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(vallen), 0)
errno = int(e1)
@@ -86,7 +94,7 @@ func setsockopt(s int, level int, name int, val uintptr, vallen int) (errno int)
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int) {
- _, _, e1 := Syscall(SYS_GETPEERNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
+ _, _, e1 := RawSyscall(SYS_GETPEERNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
errno = int(e1)
return
}
@@ -94,7 +102,7 @@ func getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int) {
- _, _, e1 := Syscall(SYS_GETSOCKNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
+ _, _, e1 := RawSyscall(SYS_GETSOCKNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
errno = int(e1)
return
}
@@ -110,7 +118,7 @@ func Shutdown(s int, how int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func socketpair(domain int, typ int, proto int, fd *[2]int) (errno int) {
- _, _, e1 := Syscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0)
+ _, _, e1 := RawSyscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0)
errno = int(e1)
return
}
@@ -194,6 +202,23 @@ func fcntl(fd int, cmd int, arg int) (val int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, errno int) {
+ r0, _, e1 := Syscall9(SYS_MMAP, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flag), uintptr(fd), uintptr(pos), uintptr(pos>>32), 0, 0)
+ ret = uintptr(r0)
+ errno = int(e1)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func munmap(addr uintptr, length uintptr) (errno int) {
+ _, _, e1 := Syscall(SYS_MUNMAP, uintptr(addr), uintptr(length), 0)
+ errno = int(e1)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func kill(pid int, signum int, posix int) (errno int) {
_, _, e1 := Syscall(SYS_KILL, uintptr(pid), uintptr(signum), uintptr(posix))
errno = int(e1)
@@ -267,7 +292,7 @@ func Close(fd int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Dup(fd int) (nfd int, errno int) {
- r0, _, e1 := Syscall(SYS_DUP, uintptr(fd), 0, 0)
+ r0, _, e1 := RawSyscall(SYS_DUP, uintptr(fd), 0, 0)
nfd = int(r0)
errno = int(e1)
return
@@ -276,7 +301,7 @@ func Dup(fd int) (nfd int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Dup2(from int, to int) (errno int) {
- _, _, e1 := Syscall(SYS_DUP2, uintptr(from), uintptr(to), 0)
+ _, _, e1 := RawSyscall(SYS_DUP2, uintptr(from), uintptr(to), 0)
errno = int(e1)
return
}
@@ -403,7 +428,7 @@ func Getdtablesize() (size int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getegid() (egid int) {
- r0, _, _ := Syscall(SYS_GETEGID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETEGID, 0, 0, 0)
egid = int(r0)
return
}
@@ -411,7 +436,7 @@ func Getegid() (egid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Geteuid() (uid int) {
- r0, _, _ := Syscall(SYS_GETEUID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETEUID, 0, 0, 0)
uid = int(r0)
return
}
@@ -434,7 +459,7 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getgid() (gid int) {
- r0, _, _ := Syscall(SYS_GETGID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETGID, 0, 0, 0)
gid = int(r0)
return
}
@@ -442,7 +467,7 @@ func Getgid() (gid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getpgid(pid int) (pgid int, errno int) {
- r0, _, e1 := Syscall(SYS_GETPGID, uintptr(pid), 0, 0)
+ r0, _, e1 := RawSyscall(SYS_GETPGID, uintptr(pid), 0, 0)
pgid = int(r0)
errno = int(e1)
return
@@ -451,7 +476,7 @@ func Getpgid(pid int) (pgid int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getpgrp() (pgrp int) {
- r0, _, _ := Syscall(SYS_GETPGRP, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETPGRP, 0, 0, 0)
pgrp = int(r0)
return
}
@@ -459,7 +484,7 @@ func Getpgrp() (pgrp int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getpid() (pid int) {
- r0, _, _ := Syscall(SYS_GETPID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETPID, 0, 0, 0)
pid = int(r0)
return
}
@@ -467,7 +492,7 @@ func Getpid() (pid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getppid() (ppid int) {
- r0, _, _ := Syscall(SYS_GETPPID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETPPID, 0, 0, 0)
ppid = int(r0)
return
}
@@ -484,7 +509,7 @@ func Getpriority(which int, who int) (prio int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getrlimit(which int, lim *Rlimit) (errno int) {
- _, _, e1 := Syscall(SYS_GETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0)
+ _, _, e1 := RawSyscall(SYS_GETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0)
errno = int(e1)
return
}
@@ -492,7 +517,7 @@ func Getrlimit(which int, lim *Rlimit) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getrusage(who int, rusage *Rusage) (errno int) {
- _, _, e1 := Syscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0)
+ _, _, e1 := RawSyscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0)
errno = int(e1)
return
}
@@ -500,7 +525,7 @@ func Getrusage(who int, rusage *Rusage) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getsid(pid int) (sid int, errno int) {
- r0, _, e1 := Syscall(SYS_GETSID, uintptr(pid), 0, 0)
+ r0, _, e1 := RawSyscall(SYS_GETSID, uintptr(pid), 0, 0)
sid = int(r0)
errno = int(e1)
return
@@ -509,7 +534,7 @@ func Getsid(pid int) (sid int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getuid() (uid int) {
- r0, _, _ := Syscall(SYS_GETUID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETUID, 0, 0, 0)
uid = int(r0)
return
}
@@ -517,7 +542,7 @@ func Getuid() (uid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Issetugid() (tainted bool) {
- r0, _, _ := Syscall(SYS_ISSETUGID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_ISSETUGID, 0, 0, 0)
tainted = bool(r0 != 0)
return
}
@@ -717,7 +742,7 @@ func Setegid(egid int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Seteuid(euid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETEUID, uintptr(euid), 0, 0)
+ _, _, e1 := RawSyscall(SYS_SETEUID, uintptr(euid), 0, 0)
errno = int(e1)
return
}
@@ -725,7 +750,7 @@ func Seteuid(euid int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setgid(gid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETGID, uintptr(gid), 0, 0)
+ _, _, e1 := RawSyscall(SYS_SETGID, uintptr(gid), 0, 0)
errno = int(e1)
return
}
@@ -741,7 +766,7 @@ func Setlogin(name string) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setpgid(pid int, pgid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETPGID, uintptr(pid), uintptr(pgid), 0)
+ _, _, e1 := RawSyscall(SYS_SETPGID, uintptr(pid), uintptr(pgid), 0)
errno = int(e1)
return
}
@@ -765,7 +790,7 @@ func Setprivexec(flag int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setregid(rgid int, egid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETREGID, uintptr(rgid), uintptr(egid), 0)
+ _, _, e1 := RawSyscall(SYS_SETREGID, uintptr(rgid), uintptr(egid), 0)
errno = int(e1)
return
}
@@ -773,7 +798,7 @@ func Setregid(rgid int, egid int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setreuid(ruid int, euid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETREUID, uintptr(ruid), uintptr(euid), 0)
+ _, _, e1 := RawSyscall(SYS_SETREUID, uintptr(ruid), uintptr(euid), 0)
errno = int(e1)
return
}
@@ -781,7 +806,7 @@ func Setreuid(ruid int, euid int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setrlimit(which int, lim *Rlimit) (errno int) {
- _, _, e1 := Syscall(SYS_SETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0)
+ _, _, e1 := RawSyscall(SYS_SETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0)
errno = int(e1)
return
}
@@ -789,7 +814,7 @@ func Setrlimit(which int, lim *Rlimit) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setsid() (pid int, errno int) {
- r0, _, e1 := Syscall(SYS_SETSID, 0, 0, 0)
+ r0, _, e1 := RawSyscall(SYS_SETSID, 0, 0, 0)
pid = int(r0)
errno = int(e1)
return
@@ -798,7 +823,7 @@ func Setsid() (pid int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Settimeofday(tp *Timeval) (errno int) {
- _, _, e1 := Syscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tp)), 0, 0)
+ _, _, e1 := RawSyscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tp)), 0, 0)
errno = int(e1)
return
}
@@ -806,7 +831,7 @@ func Settimeofday(tp *Timeval) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setuid(uid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETUID, uintptr(uid), 0, 0)
+ _, _, e1 := RawSyscall(SYS_SETUID, uintptr(uid), 0, 0)
errno = int(e1)
return
}
@@ -919,7 +944,7 @@ func write(fd int, buf *byte, nbuf int) (n int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func gettimeofday(tp *Timeval) (sec int32, usec int32, errno int) {
- r0, r1, e1 := Syscall(SYS_GETTIMEOFDAY, uintptr(unsafe.Pointer(tp)), 0, 0)
+ r0, r1, e1 := RawSyscall(SYS_GETTIMEOFDAY, uintptr(unsafe.Pointer(tp)), 0, 0)
sec = int32(r0)
usec = int32(r1)
errno = int(e1)
diff --git a/src/pkg/syscall/zsyscall_darwin_amd64.go b/src/pkg/syscall/zsyscall_darwin_amd64.go
index f7a37b63e..995c710b4 100644
--- a/src/pkg/syscall/zsyscall_darwin_amd64.go
+++ b/src/pkg/syscall/zsyscall_darwin_amd64.go
@@ -1,4 +1,4 @@
-// mksyscall.sh syscall_bsd.go syscall_darwin.go syscall_darwin_amd64.go
+// mksyscall.pl syscall_bsd.go syscall_darwin.go syscall_darwin_amd64.go
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
package syscall
@@ -8,7 +8,7 @@ import "unsafe"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func getgroups(ngid int, gid *_Gid_t) (n int, errno int) {
- r0, _, e1 := Syscall(SYS_GETGROUPS, uintptr(ngid), uintptr(unsafe.Pointer(gid)), 0)
+ r0, _, e1 := RawSyscall(SYS_GETGROUPS, uintptr(ngid), uintptr(unsafe.Pointer(gid)), 0)
n = int(r0)
errno = int(e1)
return
@@ -17,7 +17,7 @@ func getgroups(ngid int, gid *_Gid_t) (n int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func setgroups(ngid int, gid *_Gid_t) (errno int) {
- _, _, e1 := Syscall(SYS_SETGROUPS, uintptr(ngid), uintptr(unsafe.Pointer(gid)), 0)
+ _, _, e1 := RawSyscall(SYS_SETGROUPS, uintptr(ngid), uintptr(unsafe.Pointer(gid)), 0)
errno = int(e1)
return
}
@@ -34,7 +34,7 @@ func wait4(pid int, wstatus *_C_int, options int, rusage *Rusage) (wpid int, err
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func pipe() (r int, w int, errno int) {
- r0, r1, e1 := Syscall(SYS_PIPE, 0, 0, 0)
+ r0, r1, e1 := RawSyscall(SYS_PIPE, 0, 0, 0)
r = int(r0)
w = int(r1)
errno = int(e1)
@@ -69,7 +69,7 @@ func connect(s int, addr uintptr, addrlen _Socklen) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func socket(domain int, typ int, proto int) (fd int, errno int) {
- r0, _, e1 := Syscall(SYS_SOCKET, uintptr(domain), uintptr(typ), uintptr(proto))
+ r0, _, e1 := RawSyscall(SYS_SOCKET, uintptr(domain), uintptr(typ), uintptr(proto))
fd = int(r0)
errno = int(e1)
return
@@ -77,6 +77,14 @@ func socket(domain int, typ int, proto int) (fd int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (errno int) {
+ _, _, e1 := Syscall6(SYS_GETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(unsafe.Pointer(vallen)), 0)
+ errno = int(e1)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func setsockopt(s int, level int, name int, val uintptr, vallen int) (errno int) {
_, _, e1 := Syscall6(SYS_SETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(vallen), 0)
errno = int(e1)
@@ -86,7 +94,7 @@ func setsockopt(s int, level int, name int, val uintptr, vallen int) (errno int)
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int) {
- _, _, e1 := Syscall(SYS_GETPEERNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
+ _, _, e1 := RawSyscall(SYS_GETPEERNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
errno = int(e1)
return
}
@@ -94,7 +102,7 @@ func getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int) {
- _, _, e1 := Syscall(SYS_GETSOCKNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
+ _, _, e1 := RawSyscall(SYS_GETSOCKNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
errno = int(e1)
return
}
@@ -110,7 +118,7 @@ func Shutdown(s int, how int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func socketpair(domain int, typ int, proto int, fd *[2]int) (errno int) {
- _, _, e1 := Syscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0)
+ _, _, e1 := RawSyscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0)
errno = int(e1)
return
}
@@ -194,6 +202,23 @@ func fcntl(fd int, cmd int, arg int) (val int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, errno int) {
+ r0, _, e1 := Syscall6(SYS_MMAP, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flag), uintptr(fd), uintptr(pos))
+ ret = uintptr(r0)
+ errno = int(e1)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func munmap(addr uintptr, length uintptr) (errno int) {
+ _, _, e1 := Syscall(SYS_MUNMAP, uintptr(addr), uintptr(length), 0)
+ errno = int(e1)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func kill(pid int, signum int, posix int) (errno int) {
_, _, e1 := Syscall(SYS_KILL, uintptr(pid), uintptr(signum), uintptr(posix))
errno = int(e1)
@@ -267,7 +292,7 @@ func Close(fd int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Dup(fd int) (nfd int, errno int) {
- r0, _, e1 := Syscall(SYS_DUP, uintptr(fd), 0, 0)
+ r0, _, e1 := RawSyscall(SYS_DUP, uintptr(fd), 0, 0)
nfd = int(r0)
errno = int(e1)
return
@@ -276,7 +301,7 @@ func Dup(fd int) (nfd int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Dup2(from int, to int) (errno int) {
- _, _, e1 := Syscall(SYS_DUP2, uintptr(from), uintptr(to), 0)
+ _, _, e1 := RawSyscall(SYS_DUP2, uintptr(from), uintptr(to), 0)
errno = int(e1)
return
}
@@ -403,7 +428,7 @@ func Getdtablesize() (size int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getegid() (egid int) {
- r0, _, _ := Syscall(SYS_GETEGID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETEGID, 0, 0, 0)
egid = int(r0)
return
}
@@ -411,7 +436,7 @@ func Getegid() (egid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Geteuid() (uid int) {
- r0, _, _ := Syscall(SYS_GETEUID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETEUID, 0, 0, 0)
uid = int(r0)
return
}
@@ -434,7 +459,7 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getgid() (gid int) {
- r0, _, _ := Syscall(SYS_GETGID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETGID, 0, 0, 0)
gid = int(r0)
return
}
@@ -442,7 +467,7 @@ func Getgid() (gid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getpgid(pid int) (pgid int, errno int) {
- r0, _, e1 := Syscall(SYS_GETPGID, uintptr(pid), 0, 0)
+ r0, _, e1 := RawSyscall(SYS_GETPGID, uintptr(pid), 0, 0)
pgid = int(r0)
errno = int(e1)
return
@@ -451,7 +476,7 @@ func Getpgid(pid int) (pgid int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getpgrp() (pgrp int) {
- r0, _, _ := Syscall(SYS_GETPGRP, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETPGRP, 0, 0, 0)
pgrp = int(r0)
return
}
@@ -459,7 +484,7 @@ func Getpgrp() (pgrp int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getpid() (pid int) {
- r0, _, _ := Syscall(SYS_GETPID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETPID, 0, 0, 0)
pid = int(r0)
return
}
@@ -467,7 +492,7 @@ func Getpid() (pid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getppid() (ppid int) {
- r0, _, _ := Syscall(SYS_GETPPID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETPPID, 0, 0, 0)
ppid = int(r0)
return
}
@@ -484,7 +509,7 @@ func Getpriority(which int, who int) (prio int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getrlimit(which int, lim *Rlimit) (errno int) {
- _, _, e1 := Syscall(SYS_GETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0)
+ _, _, e1 := RawSyscall(SYS_GETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0)
errno = int(e1)
return
}
@@ -492,7 +517,7 @@ func Getrlimit(which int, lim *Rlimit) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getrusage(who int, rusage *Rusage) (errno int) {
- _, _, e1 := Syscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0)
+ _, _, e1 := RawSyscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0)
errno = int(e1)
return
}
@@ -500,7 +525,7 @@ func Getrusage(who int, rusage *Rusage) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getsid(pid int) (sid int, errno int) {
- r0, _, e1 := Syscall(SYS_GETSID, uintptr(pid), 0, 0)
+ r0, _, e1 := RawSyscall(SYS_GETSID, uintptr(pid), 0, 0)
sid = int(r0)
errno = int(e1)
return
@@ -509,7 +534,7 @@ func Getsid(pid int) (sid int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getuid() (uid int) {
- r0, _, _ := Syscall(SYS_GETUID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETUID, 0, 0, 0)
uid = int(r0)
return
}
@@ -517,7 +542,7 @@ func Getuid() (uid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Issetugid() (tainted bool) {
- r0, _, _ := Syscall(SYS_ISSETUGID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_ISSETUGID, 0, 0, 0)
tainted = bool(r0 != 0)
return
}
@@ -717,7 +742,7 @@ func Setegid(egid int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Seteuid(euid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETEUID, uintptr(euid), 0, 0)
+ _, _, e1 := RawSyscall(SYS_SETEUID, uintptr(euid), 0, 0)
errno = int(e1)
return
}
@@ -725,7 +750,7 @@ func Seteuid(euid int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setgid(gid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETGID, uintptr(gid), 0, 0)
+ _, _, e1 := RawSyscall(SYS_SETGID, uintptr(gid), 0, 0)
errno = int(e1)
return
}
@@ -741,7 +766,7 @@ func Setlogin(name string) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setpgid(pid int, pgid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETPGID, uintptr(pid), uintptr(pgid), 0)
+ _, _, e1 := RawSyscall(SYS_SETPGID, uintptr(pid), uintptr(pgid), 0)
errno = int(e1)
return
}
@@ -765,7 +790,7 @@ func Setprivexec(flag int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setregid(rgid int, egid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETREGID, uintptr(rgid), uintptr(egid), 0)
+ _, _, e1 := RawSyscall(SYS_SETREGID, uintptr(rgid), uintptr(egid), 0)
errno = int(e1)
return
}
@@ -773,7 +798,7 @@ func Setregid(rgid int, egid int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setreuid(ruid int, euid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETREUID, uintptr(ruid), uintptr(euid), 0)
+ _, _, e1 := RawSyscall(SYS_SETREUID, uintptr(ruid), uintptr(euid), 0)
errno = int(e1)
return
}
@@ -781,7 +806,7 @@ func Setreuid(ruid int, euid int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setrlimit(which int, lim *Rlimit) (errno int) {
- _, _, e1 := Syscall(SYS_SETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0)
+ _, _, e1 := RawSyscall(SYS_SETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0)
errno = int(e1)
return
}
@@ -789,7 +814,7 @@ func Setrlimit(which int, lim *Rlimit) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setsid() (pid int, errno int) {
- r0, _, e1 := Syscall(SYS_SETSID, 0, 0, 0)
+ r0, _, e1 := RawSyscall(SYS_SETSID, 0, 0, 0)
pid = int(r0)
errno = int(e1)
return
@@ -798,7 +823,7 @@ func Setsid() (pid int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Settimeofday(tp *Timeval) (errno int) {
- _, _, e1 := Syscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tp)), 0, 0)
+ _, _, e1 := RawSyscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tp)), 0, 0)
errno = int(e1)
return
}
@@ -806,7 +831,7 @@ func Settimeofday(tp *Timeval) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setuid(uid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETUID, uintptr(uid), 0, 0)
+ _, _, e1 := RawSyscall(SYS_SETUID, uintptr(uid), 0, 0)
errno = int(e1)
return
}
@@ -919,7 +944,7 @@ func write(fd int, buf *byte, nbuf int) (n int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func gettimeofday(tp *Timeval) (sec int64, usec int32, errno int) {
- r0, r1, e1 := Syscall(SYS_GETTIMEOFDAY, uintptr(unsafe.Pointer(tp)), 0, 0)
+ r0, r1, e1 := RawSyscall(SYS_GETTIMEOFDAY, uintptr(unsafe.Pointer(tp)), 0, 0)
sec = int64(r0)
usec = int32(r1)
errno = int(e1)
diff --git a/src/pkg/syscall/zsyscall_freebsd_386.go b/src/pkg/syscall/zsyscall_freebsd_386.go
index 1fab5e2d2..0ffb9a4b9 100644
--- a/src/pkg/syscall/zsyscall_freebsd_386.go
+++ b/src/pkg/syscall/zsyscall_freebsd_386.go
@@ -1,4 +1,4 @@
-// mksyscall.sh -l32 syscall_bsd.go syscall_freebsd.go syscall_freebsd_386.go
+// mksyscall.pl -l32 syscall_bsd.go syscall_freebsd.go syscall_freebsd_386.go
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
package syscall
@@ -8,7 +8,7 @@ import "unsafe"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func getgroups(ngid int, gid *_Gid_t) (n int, errno int) {
- r0, _, e1 := Syscall(SYS_GETGROUPS, uintptr(ngid), uintptr(unsafe.Pointer(gid)), 0)
+ r0, _, e1 := RawSyscall(SYS_GETGROUPS, uintptr(ngid), uintptr(unsafe.Pointer(gid)), 0)
n = int(r0)
errno = int(e1)
return
@@ -17,7 +17,7 @@ func getgroups(ngid int, gid *_Gid_t) (n int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func setgroups(ngid int, gid *_Gid_t) (errno int) {
- _, _, e1 := Syscall(SYS_SETGROUPS, uintptr(ngid), uintptr(unsafe.Pointer(gid)), 0)
+ _, _, e1 := RawSyscall(SYS_SETGROUPS, uintptr(ngid), uintptr(unsafe.Pointer(gid)), 0)
errno = int(e1)
return
}
@@ -34,7 +34,7 @@ func wait4(pid int, wstatus *_C_int, options int, rusage *Rusage) (wpid int, err
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func pipe() (r int, w int, errno int) {
- r0, r1, e1 := Syscall(SYS_PIPE, 0, 0, 0)
+ r0, r1, e1 := RawSyscall(SYS_PIPE, 0, 0, 0)
r = int(r0)
w = int(r1)
errno = int(e1)
@@ -69,7 +69,7 @@ func connect(s int, addr uintptr, addrlen _Socklen) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func socket(domain int, typ int, proto int) (fd int, errno int) {
- r0, _, e1 := Syscall(SYS_SOCKET, uintptr(domain), uintptr(typ), uintptr(proto))
+ r0, _, e1 := RawSyscall(SYS_SOCKET, uintptr(domain), uintptr(typ), uintptr(proto))
fd = int(r0)
errno = int(e1)
return
@@ -77,6 +77,14 @@ func socket(domain int, typ int, proto int) (fd int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (errno int) {
+ _, _, e1 := Syscall6(SYS_GETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(unsafe.Pointer(vallen)), 0)
+ errno = int(e1)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func setsockopt(s int, level int, name int, val uintptr, vallen int) (errno int) {
_, _, e1 := Syscall6(SYS_SETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(vallen), 0)
errno = int(e1)
@@ -86,7 +94,7 @@ func setsockopt(s int, level int, name int, val uintptr, vallen int) (errno int)
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int) {
- _, _, e1 := Syscall(SYS_GETPEERNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
+ _, _, e1 := RawSyscall(SYS_GETPEERNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
errno = int(e1)
return
}
@@ -94,7 +102,7 @@ func getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int) {
- _, _, e1 := Syscall(SYS_GETSOCKNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
+ _, _, e1 := RawSyscall(SYS_GETSOCKNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
errno = int(e1)
return
}
@@ -110,7 +118,7 @@ func Shutdown(s int, how int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func socketpair(domain int, typ int, proto int, fd *[2]int) (errno int) {
- _, _, e1 := Syscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0)
+ _, _, e1 := RawSyscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0)
errno = int(e1)
return
}
@@ -194,6 +202,23 @@ func fcntl(fd int, cmd int, arg int) (val int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, errno int) {
+ r0, _, e1 := Syscall9(SYS_MMAP, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flag), uintptr(fd), uintptr(pos), uintptr(pos>>32), 0, 0)
+ ret = uintptr(r0)
+ errno = int(e1)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func munmap(addr uintptr, length uintptr) (errno int) {
+ _, _, e1 := Syscall(SYS_MUNMAP, uintptr(addr), uintptr(length), 0)
+ errno = int(e1)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func Access(path string, mode uint32) (errno int) {
_, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
errno = int(e1)
@@ -259,7 +284,7 @@ func Close(fd int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Dup(fd int) (nfd int, errno int) {
- r0, _, e1 := Syscall(SYS_DUP, uintptr(fd), 0, 0)
+ r0, _, e1 := RawSyscall(SYS_DUP, uintptr(fd), 0, 0)
nfd = int(r0)
errno = int(e1)
return
@@ -268,7 +293,7 @@ func Dup(fd int) (nfd int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Dup2(from int, to int) (errno int) {
- _, _, e1 := Syscall(SYS_DUP2, uintptr(from), uintptr(to), 0)
+ _, _, e1 := RawSyscall(SYS_DUP2, uintptr(from), uintptr(to), 0)
errno = int(e1)
return
}
@@ -387,7 +412,7 @@ func Getdtablesize() (size int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getegid() (egid int) {
- r0, _, _ := Syscall(SYS_GETEGID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETEGID, 0, 0, 0)
egid = int(r0)
return
}
@@ -395,7 +420,7 @@ func Getegid() (egid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Geteuid() (uid int) {
- r0, _, _ := Syscall(SYS_GETEUID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETEUID, 0, 0, 0)
uid = int(r0)
return
}
@@ -418,7 +443,7 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getgid() (gid int) {
- r0, _, _ := Syscall(SYS_GETGID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETGID, 0, 0, 0)
gid = int(r0)
return
}
@@ -426,7 +451,7 @@ func Getgid() (gid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getpgid(pid int) (pgid int, errno int) {
- r0, _, e1 := Syscall(SYS_GETPGID, uintptr(pid), 0, 0)
+ r0, _, e1 := RawSyscall(SYS_GETPGID, uintptr(pid), 0, 0)
pgid = int(r0)
errno = int(e1)
return
@@ -435,7 +460,7 @@ func Getpgid(pid int) (pgid int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getpgrp() (pgrp int) {
- r0, _, _ := Syscall(SYS_GETPGRP, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETPGRP, 0, 0, 0)
pgrp = int(r0)
return
}
@@ -443,7 +468,7 @@ func Getpgrp() (pgrp int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getpid() (pid int) {
- r0, _, _ := Syscall(SYS_GETPID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETPID, 0, 0, 0)
pid = int(r0)
return
}
@@ -451,7 +476,7 @@ func Getpid() (pid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getppid() (ppid int) {
- r0, _, _ := Syscall(SYS_GETPPID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETPPID, 0, 0, 0)
ppid = int(r0)
return
}
@@ -468,7 +493,7 @@ func Getpriority(which int, who int) (prio int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getrlimit(which int, lim *Rlimit) (errno int) {
- _, _, e1 := Syscall(SYS_GETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0)
+ _, _, e1 := RawSyscall(SYS_GETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0)
errno = int(e1)
return
}
@@ -476,7 +501,7 @@ func Getrlimit(which int, lim *Rlimit) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getrusage(who int, rusage *Rusage) (errno int) {
- _, _, e1 := Syscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0)
+ _, _, e1 := RawSyscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0)
errno = int(e1)
return
}
@@ -484,7 +509,7 @@ func Getrusage(who int, rusage *Rusage) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getsid(pid int) (sid int, errno int) {
- r0, _, e1 := Syscall(SYS_GETSID, uintptr(pid), 0, 0)
+ r0, _, e1 := RawSyscall(SYS_GETSID, uintptr(pid), 0, 0)
sid = int(r0)
errno = int(e1)
return
@@ -493,7 +518,7 @@ func Getsid(pid int) (sid int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Gettimeofday(tv *Timeval) (errno int) {
- _, _, e1 := Syscall(SYS_GETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0)
+ _, _, e1 := RawSyscall(SYS_GETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0)
errno = int(e1)
return
}
@@ -501,7 +526,7 @@ func Gettimeofday(tv *Timeval) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getuid() (uid int) {
- r0, _, _ := Syscall(SYS_GETUID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETUID, 0, 0, 0)
uid = int(r0)
return
}
@@ -717,7 +742,7 @@ func Select(n int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setegid(egid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETEGID, uintptr(egid), 0, 0)
+ _, _, e1 := RawSyscall(SYS_SETEGID, uintptr(egid), 0, 0)
errno = int(e1)
return
}
@@ -725,7 +750,7 @@ func Setegid(egid int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Seteuid(euid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETEUID, uintptr(euid), 0, 0)
+ _, _, e1 := RawSyscall(SYS_SETEUID, uintptr(euid), 0, 0)
errno = int(e1)
return
}
@@ -733,7 +758,7 @@ func Seteuid(euid int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setgid(gid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETGID, uintptr(gid), 0, 0)
+ _, _, e1 := RawSyscall(SYS_SETGID, uintptr(gid), 0, 0)
errno = int(e1)
return
}
@@ -749,7 +774,7 @@ func Setlogin(name string) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setpgid(pid int, pgid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETPGID, uintptr(pid), uintptr(pgid), 0)
+ _, _, e1 := RawSyscall(SYS_SETPGID, uintptr(pid), uintptr(pgid), 0)
errno = int(e1)
return
}
@@ -765,7 +790,7 @@ func Setpriority(which int, who int, prio int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setregid(rgid int, egid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETREGID, uintptr(rgid), uintptr(egid), 0)
+ _, _, e1 := RawSyscall(SYS_SETREGID, uintptr(rgid), uintptr(egid), 0)
errno = int(e1)
return
}
@@ -773,7 +798,7 @@ func Setregid(rgid int, egid int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setreuid(ruid int, euid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETREUID, uintptr(ruid), uintptr(euid), 0)
+ _, _, e1 := RawSyscall(SYS_SETREUID, uintptr(ruid), uintptr(euid), 0)
errno = int(e1)
return
}
@@ -781,7 +806,7 @@ func Setreuid(ruid int, euid int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setrlimit(which int, lim *Rlimit) (errno int) {
- _, _, e1 := Syscall(SYS_SETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0)
+ _, _, e1 := RawSyscall(SYS_SETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0)
errno = int(e1)
return
}
@@ -789,7 +814,7 @@ func Setrlimit(which int, lim *Rlimit) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setsid() (pid int, errno int) {
- r0, _, e1 := Syscall(SYS_SETSID, 0, 0, 0)
+ r0, _, e1 := RawSyscall(SYS_SETSID, 0, 0, 0)
pid = int(r0)
errno = int(e1)
return
@@ -798,7 +823,7 @@ func Setsid() (pid int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Settimeofday(tp *Timeval) (errno int) {
- _, _, e1 := Syscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tp)), 0, 0)
+ _, _, e1 := RawSyscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tp)), 0, 0)
errno = int(e1)
return
}
@@ -806,7 +831,7 @@ func Settimeofday(tp *Timeval) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setuid(uid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETUID, uintptr(uid), 0, 0)
+ _, _, e1 := RawSyscall(SYS_SETUID, uintptr(uid), 0, 0)
errno = int(e1)
return
}
diff --git a/src/pkg/syscall/zsyscall_freebsd_amd64.go b/src/pkg/syscall/zsyscall_freebsd_amd64.go
index 53434b263..38a06ae3b 100644
--- a/src/pkg/syscall/zsyscall_freebsd_amd64.go
+++ b/src/pkg/syscall/zsyscall_freebsd_amd64.go
@@ -1,4 +1,4 @@
-// mksyscall.sh syscall_bsd.go syscall_freebsd.go syscall_freebsd_amd64.go
+// mksyscall.pl syscall_bsd.go syscall_freebsd.go syscall_freebsd_amd64.go
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
package syscall
@@ -8,7 +8,7 @@ import "unsafe"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func getgroups(ngid int, gid *_Gid_t) (n int, errno int) {
- r0, _, e1 := Syscall(SYS_GETGROUPS, uintptr(ngid), uintptr(unsafe.Pointer(gid)), 0)
+ r0, _, e1 := RawSyscall(SYS_GETGROUPS, uintptr(ngid), uintptr(unsafe.Pointer(gid)), 0)
n = int(r0)
errno = int(e1)
return
@@ -17,7 +17,7 @@ func getgroups(ngid int, gid *_Gid_t) (n int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func setgroups(ngid int, gid *_Gid_t) (errno int) {
- _, _, e1 := Syscall(SYS_SETGROUPS, uintptr(ngid), uintptr(unsafe.Pointer(gid)), 0)
+ _, _, e1 := RawSyscall(SYS_SETGROUPS, uintptr(ngid), uintptr(unsafe.Pointer(gid)), 0)
errno = int(e1)
return
}
@@ -34,7 +34,7 @@ func wait4(pid int, wstatus *_C_int, options int, rusage *Rusage) (wpid int, err
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func pipe() (r int, w int, errno int) {
- r0, r1, e1 := Syscall(SYS_PIPE, 0, 0, 0)
+ r0, r1, e1 := RawSyscall(SYS_PIPE, 0, 0, 0)
r = int(r0)
w = int(r1)
errno = int(e1)
@@ -69,7 +69,7 @@ func connect(s int, addr uintptr, addrlen _Socklen) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func socket(domain int, typ int, proto int) (fd int, errno int) {
- r0, _, e1 := Syscall(SYS_SOCKET, uintptr(domain), uintptr(typ), uintptr(proto))
+ r0, _, e1 := RawSyscall(SYS_SOCKET, uintptr(domain), uintptr(typ), uintptr(proto))
fd = int(r0)
errno = int(e1)
return
@@ -77,6 +77,14 @@ func socket(domain int, typ int, proto int) (fd int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (errno int) {
+ _, _, e1 := Syscall6(SYS_GETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(unsafe.Pointer(vallen)), 0)
+ errno = int(e1)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func setsockopt(s int, level int, name int, val uintptr, vallen int) (errno int) {
_, _, e1 := Syscall6(SYS_SETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(vallen), 0)
errno = int(e1)
@@ -86,7 +94,7 @@ func setsockopt(s int, level int, name int, val uintptr, vallen int) (errno int)
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int) {
- _, _, e1 := Syscall(SYS_GETPEERNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
+ _, _, e1 := RawSyscall(SYS_GETPEERNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
errno = int(e1)
return
}
@@ -94,7 +102,7 @@ func getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int) {
- _, _, e1 := Syscall(SYS_GETSOCKNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
+ _, _, e1 := RawSyscall(SYS_GETSOCKNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
errno = int(e1)
return
}
@@ -110,7 +118,7 @@ func Shutdown(s int, how int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func socketpair(domain int, typ int, proto int, fd *[2]int) (errno int) {
- _, _, e1 := Syscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0)
+ _, _, e1 := RawSyscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0)
errno = int(e1)
return
}
@@ -194,6 +202,23 @@ func fcntl(fd int, cmd int, arg int) (val int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, errno int) {
+ r0, _, e1 := Syscall6(SYS_MMAP, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flag), uintptr(fd), uintptr(pos))
+ ret = uintptr(r0)
+ errno = int(e1)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func munmap(addr uintptr, length uintptr) (errno int) {
+ _, _, e1 := Syscall(SYS_MUNMAP, uintptr(addr), uintptr(length), 0)
+ errno = int(e1)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func Access(path string, mode uint32) (errno int) {
_, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
errno = int(e1)
@@ -259,7 +284,7 @@ func Close(fd int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Dup(fd int) (nfd int, errno int) {
- r0, _, e1 := Syscall(SYS_DUP, uintptr(fd), 0, 0)
+ r0, _, e1 := RawSyscall(SYS_DUP, uintptr(fd), 0, 0)
nfd = int(r0)
errno = int(e1)
return
@@ -268,7 +293,7 @@ func Dup(fd int) (nfd int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Dup2(from int, to int) (errno int) {
- _, _, e1 := Syscall(SYS_DUP2, uintptr(from), uintptr(to), 0)
+ _, _, e1 := RawSyscall(SYS_DUP2, uintptr(from), uintptr(to), 0)
errno = int(e1)
return
}
@@ -387,7 +412,7 @@ func Getdtablesize() (size int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getegid() (egid int) {
- r0, _, _ := Syscall(SYS_GETEGID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETEGID, 0, 0, 0)
egid = int(r0)
return
}
@@ -395,7 +420,7 @@ func Getegid() (egid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Geteuid() (uid int) {
- r0, _, _ := Syscall(SYS_GETEUID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETEUID, 0, 0, 0)
uid = int(r0)
return
}
@@ -418,7 +443,7 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getgid() (gid int) {
- r0, _, _ := Syscall(SYS_GETGID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETGID, 0, 0, 0)
gid = int(r0)
return
}
@@ -426,7 +451,7 @@ func Getgid() (gid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getpgid(pid int) (pgid int, errno int) {
- r0, _, e1 := Syscall(SYS_GETPGID, uintptr(pid), 0, 0)
+ r0, _, e1 := RawSyscall(SYS_GETPGID, uintptr(pid), 0, 0)
pgid = int(r0)
errno = int(e1)
return
@@ -435,7 +460,7 @@ func Getpgid(pid int) (pgid int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getpgrp() (pgrp int) {
- r0, _, _ := Syscall(SYS_GETPGRP, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETPGRP, 0, 0, 0)
pgrp = int(r0)
return
}
@@ -443,7 +468,7 @@ func Getpgrp() (pgrp int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getpid() (pid int) {
- r0, _, _ := Syscall(SYS_GETPID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETPID, 0, 0, 0)
pid = int(r0)
return
}
@@ -451,7 +476,7 @@ func Getpid() (pid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getppid() (ppid int) {
- r0, _, _ := Syscall(SYS_GETPPID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETPPID, 0, 0, 0)
ppid = int(r0)
return
}
@@ -468,7 +493,7 @@ func Getpriority(which int, who int) (prio int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getrlimit(which int, lim *Rlimit) (errno int) {
- _, _, e1 := Syscall(SYS_GETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0)
+ _, _, e1 := RawSyscall(SYS_GETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0)
errno = int(e1)
return
}
@@ -476,7 +501,7 @@ func Getrlimit(which int, lim *Rlimit) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getrusage(who int, rusage *Rusage) (errno int) {
- _, _, e1 := Syscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0)
+ _, _, e1 := RawSyscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0)
errno = int(e1)
return
}
@@ -484,7 +509,7 @@ func Getrusage(who int, rusage *Rusage) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getsid(pid int) (sid int, errno int) {
- r0, _, e1 := Syscall(SYS_GETSID, uintptr(pid), 0, 0)
+ r0, _, e1 := RawSyscall(SYS_GETSID, uintptr(pid), 0, 0)
sid = int(r0)
errno = int(e1)
return
@@ -493,7 +518,7 @@ func Getsid(pid int) (sid int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Gettimeofday(tv *Timeval) (errno int) {
- _, _, e1 := Syscall(SYS_GETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0)
+ _, _, e1 := RawSyscall(SYS_GETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0)
errno = int(e1)
return
}
@@ -501,7 +526,7 @@ func Gettimeofday(tv *Timeval) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getuid() (uid int) {
- r0, _, _ := Syscall(SYS_GETUID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETUID, 0, 0, 0)
uid = int(r0)
return
}
@@ -717,7 +742,7 @@ func Select(n int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setegid(egid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETEGID, uintptr(egid), 0, 0)
+ _, _, e1 := RawSyscall(SYS_SETEGID, uintptr(egid), 0, 0)
errno = int(e1)
return
}
@@ -725,7 +750,7 @@ func Setegid(egid int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Seteuid(euid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETEUID, uintptr(euid), 0, 0)
+ _, _, e1 := RawSyscall(SYS_SETEUID, uintptr(euid), 0, 0)
errno = int(e1)
return
}
@@ -733,7 +758,7 @@ func Seteuid(euid int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setgid(gid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETGID, uintptr(gid), 0, 0)
+ _, _, e1 := RawSyscall(SYS_SETGID, uintptr(gid), 0, 0)
errno = int(e1)
return
}
@@ -749,7 +774,7 @@ func Setlogin(name string) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setpgid(pid int, pgid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETPGID, uintptr(pid), uintptr(pgid), 0)
+ _, _, e1 := RawSyscall(SYS_SETPGID, uintptr(pid), uintptr(pgid), 0)
errno = int(e1)
return
}
@@ -765,7 +790,7 @@ func Setpriority(which int, who int, prio int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setregid(rgid int, egid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETREGID, uintptr(rgid), uintptr(egid), 0)
+ _, _, e1 := RawSyscall(SYS_SETREGID, uintptr(rgid), uintptr(egid), 0)
errno = int(e1)
return
}
@@ -773,7 +798,7 @@ func Setregid(rgid int, egid int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setreuid(ruid int, euid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETREUID, uintptr(ruid), uintptr(euid), 0)
+ _, _, e1 := RawSyscall(SYS_SETREUID, uintptr(ruid), uintptr(euid), 0)
errno = int(e1)
return
}
@@ -781,7 +806,7 @@ func Setreuid(ruid int, euid int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setrlimit(which int, lim *Rlimit) (errno int) {
- _, _, e1 := Syscall(SYS_SETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0)
+ _, _, e1 := RawSyscall(SYS_SETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0)
errno = int(e1)
return
}
@@ -789,7 +814,7 @@ func Setrlimit(which int, lim *Rlimit) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setsid() (pid int, errno int) {
- r0, _, e1 := Syscall(SYS_SETSID, 0, 0, 0)
+ r0, _, e1 := RawSyscall(SYS_SETSID, 0, 0, 0)
pid = int(r0)
errno = int(e1)
return
@@ -798,7 +823,7 @@ func Setsid() (pid int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Settimeofday(tp *Timeval) (errno int) {
- _, _, e1 := Syscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tp)), 0, 0)
+ _, _, e1 := RawSyscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tp)), 0, 0)
errno = int(e1)
return
}
@@ -806,7 +831,7 @@ func Settimeofday(tp *Timeval) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setuid(uid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETUID, uintptr(uid), 0, 0)
+ _, _, e1 := RawSyscall(SYS_SETUID, uintptr(uid), 0, 0)
errno = int(e1)
return
}
diff --git a/src/pkg/syscall/zsyscall_linux_386.go b/src/pkg/syscall/zsyscall_linux_386.go
index 005cc1542..83f3bade1 100644
--- a/src/pkg/syscall/zsyscall_linux_386.go
+++ b/src/pkg/syscall/zsyscall_linux_386.go
@@ -1,4 +1,4 @@
-// mksyscall.sh -l32 syscall_linux.go syscall_linux_386.go
+// mksyscall.pl -l32 syscall_linux.go syscall_linux_386.go
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
package syscall
@@ -26,7 +26,7 @@ func openat(dirfd int, path string, flags int, mode uint32) (fd int, errno int)
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func pipe(p *[2]_C_int) (errno int) {
- _, _, e1 := Syscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0)
+ _, _, e1 := RawSyscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0)
errno = int(e1)
return
}
@@ -81,6 +81,14 @@ func ptrace(request int, pid int, addr uintptr, data uintptr) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func reboot(magic1 uint, magic2 uint, cmd int, arg string) (errno int) {
+ _, _, e1 := Syscall6(SYS_REBOOT, uintptr(magic1), uintptr(magic2), uintptr(cmd), uintptr(unsafe.Pointer(StringBytePtr(arg))), 0, 0)
+ errno = int(e1)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func Access(path string, mode uint32) (errno int) {
_, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
errno = int(e1)
@@ -148,7 +156,7 @@ func Creat(path string, mode uint32) (fd int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Dup(oldfd int) (fd int, errno int) {
- r0, _, e1 := Syscall(SYS_DUP, uintptr(oldfd), 0, 0)
+ r0, _, e1 := RawSyscall(SYS_DUP, uintptr(oldfd), 0, 0)
fd = int(r0)
errno = int(e1)
return
@@ -157,7 +165,7 @@ func Dup(oldfd int) (fd int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Dup2(oldfd int, newfd int) (fd int, errno int) {
- r0, _, e1 := Syscall(SYS_DUP2, uintptr(oldfd), uintptr(newfd), 0)
+ r0, _, e1 := RawSyscall(SYS_DUP2, uintptr(oldfd), uintptr(newfd), 0)
fd = int(r0)
errno = int(e1)
return
@@ -166,7 +174,7 @@ func Dup2(oldfd int, newfd int) (fd int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func EpollCreate(size int) (fd int, errno int) {
- r0, _, e1 := Syscall(SYS_EPOLL_CREATE, uintptr(size), 0, 0)
+ r0, _, e1 := RawSyscall(SYS_EPOLL_CREATE, uintptr(size), 0, 0)
fd = int(r0)
errno = int(e1)
return
@@ -175,7 +183,7 @@ func EpollCreate(size int) (fd int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (errno int) {
- _, _, e1 := Syscall6(SYS_EPOLL_CTL, uintptr(epfd), uintptr(op), uintptr(fd), uintptr(unsafe.Pointer(event)), 0, 0)
+ _, _, e1 := RawSyscall6(SYS_EPOLL_CTL, uintptr(epfd), uintptr(op), uintptr(fd), uintptr(unsafe.Pointer(event)), 0, 0)
errno = int(e1)
return
}
@@ -293,7 +301,7 @@ func Getdents(fd int, buf []byte) (n int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getpgid(pid int) (pgid int, errno int) {
- r0, _, e1 := Syscall(SYS_GETPGID, uintptr(pid), 0, 0)
+ r0, _, e1 := RawSyscall(SYS_GETPGID, uintptr(pid), 0, 0)
pgid = int(r0)
errno = int(e1)
return
@@ -302,7 +310,7 @@ func Getpgid(pid int) (pgid int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getpgrp() (pid int) {
- r0, _, _ := Syscall(SYS_GETPGRP, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETPGRP, 0, 0, 0)
pid = int(r0)
return
}
@@ -310,7 +318,7 @@ func Getpgrp() (pid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getpid() (pid int) {
- r0, _, _ := Syscall(SYS_GETPID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETPID, 0, 0, 0)
pid = int(r0)
return
}
@@ -318,7 +326,7 @@ func Getpid() (pid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getppid() (ppid int) {
- r0, _, _ := Syscall(SYS_GETPPID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETPPID, 0, 0, 0)
ppid = int(r0)
return
}
@@ -326,7 +334,7 @@ func Getppid() (ppid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getrlimit(resource int, rlim *Rlimit) (errno int) {
- _, _, e1 := Syscall(SYS_GETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0)
+ _, _, e1 := RawSyscall(SYS_GETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0)
errno = int(e1)
return
}
@@ -334,7 +342,7 @@ func Getrlimit(resource int, rlim *Rlimit) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getrusage(who int, rusage *Rusage) (errno int) {
- _, _, e1 := Syscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0)
+ _, _, e1 := RawSyscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0)
errno = int(e1)
return
}
@@ -342,7 +350,7 @@ func Getrusage(who int, rusage *Rusage) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Gettid() (tid int) {
- r0, _, _ := Syscall(SYS_GETTID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
tid = int(r0)
return
}
@@ -359,7 +367,7 @@ func InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, errno
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func InotifyInit() (fd int, errno int) {
- r0, _, e1 := Syscall(SYS_INOTIFY_INIT, 0, 0, 0)
+ r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT, 0, 0, 0)
fd = int(r0)
errno = int(e1)
return
@@ -368,7 +376,7 @@ func InotifyInit() (fd int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func InotifyInit1(flags int) (fd int, errno int) {
- r0, _, e1 := Syscall(SYS_INOTIFY_INIT1, uintptr(flags), 0, 0)
+ r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT1, uintptr(flags), 0, 0)
fd = int(r0)
errno = int(e1)
return
@@ -377,7 +385,7 @@ func InotifyInit1(flags int) (fd int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func InotifyRmWatch(fd int, watchdesc uint32) (success int, errno int) {
- r0, _, e1 := Syscall(SYS_INOTIFY_RM_WATCH, uintptr(fd), uintptr(watchdesc), 0)
+ r0, _, e1 := RawSyscall(SYS_INOTIFY_RM_WATCH, uintptr(fd), uintptr(watchdesc), 0)
success = int(r0)
errno = int(e1)
return
@@ -386,7 +394,7 @@ func InotifyRmWatch(fd int, watchdesc uint32) (success int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Kill(pid int, sig int) (errno int) {
- _, _, e1 := Syscall(SYS_KILL, uintptr(pid), uintptr(sig), 0)
+ _, _, e1 := RawSyscall(SYS_KILL, uintptr(pid), uintptr(sig), 0)
errno = int(e1)
return
}
@@ -448,6 +456,14 @@ func Mknodat(dirfd int, path string, mode uint32, dev int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func Mount(source string, target string, fstype string, flags int, data string) (errno int) {
+ _, _, e1 := Syscall6(SYS_MOUNT, uintptr(unsafe.Pointer(StringBytePtr(source))), uintptr(unsafe.Pointer(StringBytePtr(target))), uintptr(unsafe.Pointer(StringBytePtr(fstype))), uintptr(flags), uintptr(unsafe.Pointer(StringBytePtr(data))), 0)
+ errno = int(e1)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func Nanosleep(time *Timespec, leftover *Timespec) (errno int) {
_, _, e1 := Syscall(SYS_NANOSLEEP, uintptr(unsafe.Pointer(time)), uintptr(unsafe.Pointer(leftover)), 0)
errno = int(e1)
@@ -555,7 +571,7 @@ func Sethostname(p []byte) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setpgid(pid int, pgid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETPGID, uintptr(pid), uintptr(pgid), 0)
+ _, _, e1 := RawSyscall(SYS_SETPGID, uintptr(pid), uintptr(pgid), 0)
errno = int(e1)
return
}
@@ -563,7 +579,7 @@ func Setpgid(pid int, pgid int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setrlimit(resource int, rlim *Rlimit) (errno int) {
- _, _, e1 := Syscall(SYS_SETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0)
+ _, _, e1 := RawSyscall(SYS_SETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0)
errno = int(e1)
return
}
@@ -571,7 +587,7 @@ func Setrlimit(resource int, rlim *Rlimit) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setsid() (pid int, errno int) {
- r0, _, e1 := Syscall(SYS_SETSID, 0, 0, 0)
+ r0, _, e1 := RawSyscall(SYS_SETSID, 0, 0, 0)
pid = int(r0)
errno = int(e1)
return
@@ -580,7 +596,7 @@ func Setsid() (pid int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Settimeofday(tv *Timeval) (errno int) {
- _, _, e1 := Syscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0)
+ _, _, e1 := RawSyscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0)
errno = int(e1)
return
}
@@ -588,7 +604,7 @@ func Settimeofday(tv *Timeval) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setuid(uid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETUID, uintptr(uid), 0, 0)
+ _, _, e1 := RawSyscall(SYS_SETUID, uintptr(uid), 0, 0)
errno = int(e1)
return
}
@@ -611,7 +627,7 @@ func Sync() {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Sysinfo(info *Sysinfo_t) (errno int) {
- _, _, e1 := Syscall(SYS_SYSINFO, uintptr(unsafe.Pointer(info)), 0, 0)
+ _, _, e1 := RawSyscall(SYS_SYSINFO, uintptr(unsafe.Pointer(info)), 0, 0)
errno = int(e1)
return
}
@@ -628,7 +644,7 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Tgkill(tgid int, tid int, sig int) (errno int) {
- _, _, e1 := Syscall(SYS_TGKILL, uintptr(tgid), uintptr(tid), uintptr(sig))
+ _, _, e1 := RawSyscall(SYS_TGKILL, uintptr(tgid), uintptr(tid), uintptr(sig))
errno = int(e1)
return
}
@@ -636,7 +652,7 @@ func Tgkill(tgid int, tid int, sig int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Times(tms *Tms) (ticks uintptr, errno int) {
- r0, _, e1 := Syscall(SYS_TIMES, uintptr(unsafe.Pointer(tms)), 0, 0)
+ r0, _, e1 := RawSyscall(SYS_TIMES, uintptr(unsafe.Pointer(tms)), 0, 0)
ticks = uintptr(r0)
errno = int(e1)
return
@@ -645,7 +661,7 @@ func Times(tms *Tms) (ticks uintptr, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Umask(mask int) (oldmask int) {
- r0, _, _ := Syscall(SYS_UMASK, uintptr(mask), 0, 0)
+ r0, _, _ := RawSyscall(SYS_UMASK, uintptr(mask), 0, 0)
oldmask = int(r0)
return
}
@@ -653,7 +669,7 @@ func Umask(mask int) (oldmask int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Uname(buf *Utsname) (errno int) {
- _, _, e1 := Syscall(SYS_UNAME, uintptr(unsafe.Pointer(buf)), 0, 0)
+ _, _, e1 := RawSyscall(SYS_UNAME, uintptr(unsafe.Pointer(buf)), 0, 0)
errno = int(e1)
return
}
@@ -676,6 +692,14 @@ func Unlinkat(dirfd int, path string) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func Unmount(target string, flags int) (errno int) {
+ _, _, e1 := Syscall(SYS_UMOUNT2, uintptr(unsafe.Pointer(StringBytePtr(target))), uintptr(flags), 0)
+ errno = int(e1)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func Unshare(flags int) (errno int) {
_, _, e1 := Syscall(SYS_UNSHARE, uintptr(flags), 0, 0)
errno = int(e1)
@@ -741,6 +765,14 @@ func write(fd int, p *byte, np int) (n int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func munmap(addr uintptr, length uintptr) (errno int) {
+ _, _, e1 := Syscall(SYS_MUNMAP, uintptr(addr), uintptr(length), 0)
+ errno = int(e1)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func Chown(path string, uid int, gid int) (errno int) {
_, _, e1 := Syscall(SYS_CHOWN32, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid))
errno = int(e1)
@@ -774,7 +806,7 @@ func Ftruncate(fd int, length int64) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getegid() (egid int) {
- r0, _, _ := Syscall(SYS_GETEGID32, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETEGID32, 0, 0, 0)
egid = int(r0)
return
}
@@ -782,7 +814,7 @@ func Getegid() (egid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Geteuid() (euid int) {
- r0, _, _ := Syscall(SYS_GETEUID32, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETEUID32, 0, 0, 0)
euid = int(r0)
return
}
@@ -790,7 +822,7 @@ func Geteuid() (euid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getgid() (gid int) {
- r0, _, _ := Syscall(SYS_GETGID32, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETGID32, 0, 0, 0)
gid = int(r0)
return
}
@@ -798,7 +830,7 @@ func Getgid() (gid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getuid() (uid int) {
- r0, _, _ := Syscall(SYS_GETUID32, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETUID32, 0, 0, 0)
uid = int(r0)
return
}
@@ -884,7 +916,7 @@ func Setfsuid(uid int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setgid(gid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETGID32, uintptr(gid), 0, 0)
+ _, _, e1 := RawSyscall(SYS_SETGID32, uintptr(gid), 0, 0)
errno = int(e1)
return
}
@@ -892,7 +924,7 @@ func Setgid(gid int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setregid(rgid int, egid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETREGID32, uintptr(rgid), uintptr(egid), 0)
+ _, _, e1 := RawSyscall(SYS_SETREGID32, uintptr(rgid), uintptr(egid), 0)
errno = int(e1)
return
}
@@ -900,7 +932,7 @@ func Setregid(rgid int, egid int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setresgid(rgid int, egid int, sgid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETRESGID32, uintptr(rgid), uintptr(egid), uintptr(sgid))
+ _, _, e1 := RawSyscall(SYS_SETRESGID32, uintptr(rgid), uintptr(egid), uintptr(sgid))
errno = int(e1)
return
}
@@ -908,7 +940,7 @@ func Setresgid(rgid int, egid int, sgid int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setresuid(ruid int, euid int, suid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETRESUID32, uintptr(ruid), uintptr(euid), uintptr(suid))
+ _, _, e1 := RawSyscall(SYS_SETRESUID32, uintptr(ruid), uintptr(euid), uintptr(suid))
errno = int(e1)
return
}
@@ -916,7 +948,7 @@ func Setresuid(ruid int, euid int, suid int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setreuid(ruid int, euid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETREUID32, uintptr(ruid), uintptr(euid), 0)
+ _, _, e1 := RawSyscall(SYS_SETREUID32, uintptr(ruid), uintptr(euid), 0)
errno = int(e1)
return
}
@@ -957,7 +989,7 @@ func Truncate(path string, length int64) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func getgroups(n int, list *_Gid_t) (nn int, errno int) {
- r0, _, e1 := Syscall(SYS_GETGROUPS32, uintptr(n), uintptr(unsafe.Pointer(list)), 0)
+ r0, _, e1 := RawSyscall(SYS_GETGROUPS32, uintptr(n), uintptr(unsafe.Pointer(list)), 0)
nn = int(r0)
errno = int(e1)
return
@@ -966,7 +998,7 @@ func getgroups(n int, list *_Gid_t) (nn int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func setgroups(n int, list *_Gid_t) (errno int) {
- _, _, e1 := Syscall(SYS_SETGROUPS32, uintptr(n), uintptr(unsafe.Pointer(list)), 0)
+ _, _, e1 := RawSyscall(SYS_SETGROUPS32, uintptr(n), uintptr(unsafe.Pointer(list)), 0)
errno = int(e1)
return
}
@@ -982,8 +1014,17 @@ func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func mmap2(addr uintptr, length uintptr, prot int, flags int, fd int, pageOffset uintptr) (xaddr uintptr, errno int) {
+ r0, _, e1 := Syscall6(SYS_MMAP2, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flags), uintptr(fd), uintptr(pageOffset))
+ xaddr = uintptr(r0)
+ errno = int(e1)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func Gettimeofday(tv *Timeval) (errno int) {
- _, _, e1 := Syscall(SYS_GETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0)
+ _, _, e1 := RawSyscall(SYS_GETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0)
errno = int(e1)
return
}
@@ -991,7 +1032,7 @@ func Gettimeofday(tv *Timeval) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Time(t *Time_t) (tt Time_t, errno int) {
- r0, _, e1 := Syscall(SYS_TIME, uintptr(unsafe.Pointer(t)), 0, 0)
+ r0, _, e1 := RawSyscall(SYS_TIME, uintptr(unsafe.Pointer(t)), 0, 0)
tt = Time_t(r0)
errno = int(e1)
return
diff --git a/src/pkg/syscall/zsyscall_linux_amd64.go b/src/pkg/syscall/zsyscall_linux_amd64.go
index d449a3bfe..c054349c6 100644
--- a/src/pkg/syscall/zsyscall_linux_amd64.go
+++ b/src/pkg/syscall/zsyscall_linux_amd64.go
@@ -1,4 +1,4 @@
-// mksyscall.sh syscall_linux.go syscall_linux_amd64.go
+// mksyscall.pl syscall_linux.go syscall_linux_amd64.go
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
package syscall
@@ -26,7 +26,7 @@ func openat(dirfd int, path string, flags int, mode uint32) (fd int, errno int)
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func pipe(p *[2]_C_int) (errno int) {
- _, _, e1 := Syscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0)
+ _, _, e1 := RawSyscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0)
errno = int(e1)
return
}
@@ -81,6 +81,14 @@ func ptrace(request int, pid int, addr uintptr, data uintptr) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func reboot(magic1 uint, magic2 uint, cmd int, arg string) (errno int) {
+ _, _, e1 := Syscall6(SYS_REBOOT, uintptr(magic1), uintptr(magic2), uintptr(cmd), uintptr(unsafe.Pointer(StringBytePtr(arg))), 0, 0)
+ errno = int(e1)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func Access(path string, mode uint32) (errno int) {
_, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
errno = int(e1)
@@ -148,7 +156,7 @@ func Creat(path string, mode uint32) (fd int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Dup(oldfd int) (fd int, errno int) {
- r0, _, e1 := Syscall(SYS_DUP, uintptr(oldfd), 0, 0)
+ r0, _, e1 := RawSyscall(SYS_DUP, uintptr(oldfd), 0, 0)
fd = int(r0)
errno = int(e1)
return
@@ -157,7 +165,7 @@ func Dup(oldfd int) (fd int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Dup2(oldfd int, newfd int) (fd int, errno int) {
- r0, _, e1 := Syscall(SYS_DUP2, uintptr(oldfd), uintptr(newfd), 0)
+ r0, _, e1 := RawSyscall(SYS_DUP2, uintptr(oldfd), uintptr(newfd), 0)
fd = int(r0)
errno = int(e1)
return
@@ -166,7 +174,7 @@ func Dup2(oldfd int, newfd int) (fd int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func EpollCreate(size int) (fd int, errno int) {
- r0, _, e1 := Syscall(SYS_EPOLL_CREATE, uintptr(size), 0, 0)
+ r0, _, e1 := RawSyscall(SYS_EPOLL_CREATE, uintptr(size), 0, 0)
fd = int(r0)
errno = int(e1)
return
@@ -175,7 +183,7 @@ func EpollCreate(size int) (fd int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (errno int) {
- _, _, e1 := Syscall6(SYS_EPOLL_CTL, uintptr(epfd), uintptr(op), uintptr(fd), uintptr(unsafe.Pointer(event)), 0, 0)
+ _, _, e1 := RawSyscall6(SYS_EPOLL_CTL, uintptr(epfd), uintptr(op), uintptr(fd), uintptr(unsafe.Pointer(event)), 0, 0)
errno = int(e1)
return
}
@@ -293,7 +301,7 @@ func Getdents(fd int, buf []byte) (n int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getpgid(pid int) (pgid int, errno int) {
- r0, _, e1 := Syscall(SYS_GETPGID, uintptr(pid), 0, 0)
+ r0, _, e1 := RawSyscall(SYS_GETPGID, uintptr(pid), 0, 0)
pgid = int(r0)
errno = int(e1)
return
@@ -302,7 +310,7 @@ func Getpgid(pid int) (pgid int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getpgrp() (pid int) {
- r0, _, _ := Syscall(SYS_GETPGRP, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETPGRP, 0, 0, 0)
pid = int(r0)
return
}
@@ -310,7 +318,7 @@ func Getpgrp() (pid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getpid() (pid int) {
- r0, _, _ := Syscall(SYS_GETPID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETPID, 0, 0, 0)
pid = int(r0)
return
}
@@ -318,7 +326,7 @@ func Getpid() (pid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getppid() (ppid int) {
- r0, _, _ := Syscall(SYS_GETPPID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETPPID, 0, 0, 0)
ppid = int(r0)
return
}
@@ -326,7 +334,7 @@ func Getppid() (ppid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getrlimit(resource int, rlim *Rlimit) (errno int) {
- _, _, e1 := Syscall(SYS_GETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0)
+ _, _, e1 := RawSyscall(SYS_GETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0)
errno = int(e1)
return
}
@@ -334,7 +342,7 @@ func Getrlimit(resource int, rlim *Rlimit) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getrusage(who int, rusage *Rusage) (errno int) {
- _, _, e1 := Syscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0)
+ _, _, e1 := RawSyscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0)
errno = int(e1)
return
}
@@ -342,7 +350,7 @@ func Getrusage(who int, rusage *Rusage) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Gettid() (tid int) {
- r0, _, _ := Syscall(SYS_GETTID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
tid = int(r0)
return
}
@@ -359,7 +367,7 @@ func InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, errno
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func InotifyInit() (fd int, errno int) {
- r0, _, e1 := Syscall(SYS_INOTIFY_INIT, 0, 0, 0)
+ r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT, 0, 0, 0)
fd = int(r0)
errno = int(e1)
return
@@ -368,7 +376,7 @@ func InotifyInit() (fd int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func InotifyInit1(flags int) (fd int, errno int) {
- r0, _, e1 := Syscall(SYS_INOTIFY_INIT1, uintptr(flags), 0, 0)
+ r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT1, uintptr(flags), 0, 0)
fd = int(r0)
errno = int(e1)
return
@@ -377,7 +385,7 @@ func InotifyInit1(flags int) (fd int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func InotifyRmWatch(fd int, watchdesc uint32) (success int, errno int) {
- r0, _, e1 := Syscall(SYS_INOTIFY_RM_WATCH, uintptr(fd), uintptr(watchdesc), 0)
+ r0, _, e1 := RawSyscall(SYS_INOTIFY_RM_WATCH, uintptr(fd), uintptr(watchdesc), 0)
success = int(r0)
errno = int(e1)
return
@@ -386,7 +394,7 @@ func InotifyRmWatch(fd int, watchdesc uint32) (success int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Kill(pid int, sig int) (errno int) {
- _, _, e1 := Syscall(SYS_KILL, uintptr(pid), uintptr(sig), 0)
+ _, _, e1 := RawSyscall(SYS_KILL, uintptr(pid), uintptr(sig), 0)
errno = int(e1)
return
}
@@ -448,6 +456,14 @@ func Mknodat(dirfd int, path string, mode uint32, dev int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func Mount(source string, target string, fstype string, flags int, data string) (errno int) {
+ _, _, e1 := Syscall6(SYS_MOUNT, uintptr(unsafe.Pointer(StringBytePtr(source))), uintptr(unsafe.Pointer(StringBytePtr(target))), uintptr(unsafe.Pointer(StringBytePtr(fstype))), uintptr(flags), uintptr(unsafe.Pointer(StringBytePtr(data))), 0)
+ errno = int(e1)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func Nanosleep(time *Timespec, leftover *Timespec) (errno int) {
_, _, e1 := Syscall(SYS_NANOSLEEP, uintptr(unsafe.Pointer(time)), uintptr(unsafe.Pointer(leftover)), 0)
errno = int(e1)
@@ -555,7 +571,7 @@ func Sethostname(p []byte) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setpgid(pid int, pgid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETPGID, uintptr(pid), uintptr(pgid), 0)
+ _, _, e1 := RawSyscall(SYS_SETPGID, uintptr(pid), uintptr(pgid), 0)
errno = int(e1)
return
}
@@ -563,7 +579,7 @@ func Setpgid(pid int, pgid int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setrlimit(resource int, rlim *Rlimit) (errno int) {
- _, _, e1 := Syscall(SYS_SETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0)
+ _, _, e1 := RawSyscall(SYS_SETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0)
errno = int(e1)
return
}
@@ -571,7 +587,7 @@ func Setrlimit(resource int, rlim *Rlimit) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setsid() (pid int, errno int) {
- r0, _, e1 := Syscall(SYS_SETSID, 0, 0, 0)
+ r0, _, e1 := RawSyscall(SYS_SETSID, 0, 0, 0)
pid = int(r0)
errno = int(e1)
return
@@ -580,7 +596,7 @@ func Setsid() (pid int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Settimeofday(tv *Timeval) (errno int) {
- _, _, e1 := Syscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0)
+ _, _, e1 := RawSyscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0)
errno = int(e1)
return
}
@@ -588,7 +604,7 @@ func Settimeofday(tv *Timeval) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setuid(uid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETUID, uintptr(uid), 0, 0)
+ _, _, e1 := RawSyscall(SYS_SETUID, uintptr(uid), 0, 0)
errno = int(e1)
return
}
@@ -611,7 +627,7 @@ func Sync() {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Sysinfo(info *Sysinfo_t) (errno int) {
- _, _, e1 := Syscall(SYS_SYSINFO, uintptr(unsafe.Pointer(info)), 0, 0)
+ _, _, e1 := RawSyscall(SYS_SYSINFO, uintptr(unsafe.Pointer(info)), 0, 0)
errno = int(e1)
return
}
@@ -628,7 +644,7 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Tgkill(tgid int, tid int, sig int) (errno int) {
- _, _, e1 := Syscall(SYS_TGKILL, uintptr(tgid), uintptr(tid), uintptr(sig))
+ _, _, e1 := RawSyscall(SYS_TGKILL, uintptr(tgid), uintptr(tid), uintptr(sig))
errno = int(e1)
return
}
@@ -636,7 +652,7 @@ func Tgkill(tgid int, tid int, sig int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Times(tms *Tms) (ticks uintptr, errno int) {
- r0, _, e1 := Syscall(SYS_TIMES, uintptr(unsafe.Pointer(tms)), 0, 0)
+ r0, _, e1 := RawSyscall(SYS_TIMES, uintptr(unsafe.Pointer(tms)), 0, 0)
ticks = uintptr(r0)
errno = int(e1)
return
@@ -645,7 +661,7 @@ func Times(tms *Tms) (ticks uintptr, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Umask(mask int) (oldmask int) {
- r0, _, _ := Syscall(SYS_UMASK, uintptr(mask), 0, 0)
+ r0, _, _ := RawSyscall(SYS_UMASK, uintptr(mask), 0, 0)
oldmask = int(r0)
return
}
@@ -653,7 +669,7 @@ func Umask(mask int) (oldmask int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Uname(buf *Utsname) (errno int) {
- _, _, e1 := Syscall(SYS_UNAME, uintptr(unsafe.Pointer(buf)), 0, 0)
+ _, _, e1 := RawSyscall(SYS_UNAME, uintptr(unsafe.Pointer(buf)), 0, 0)
errno = int(e1)
return
}
@@ -676,6 +692,14 @@ func Unlinkat(dirfd int, path string) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func Unmount(target string, flags int) (errno int) {
+ _, _, e1 := Syscall(SYS_UMOUNT2, uintptr(unsafe.Pointer(StringBytePtr(target))), uintptr(flags), 0)
+ errno = int(e1)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func Unshare(flags int) (errno int) {
_, _, e1 := Syscall(SYS_UNSHARE, uintptr(flags), 0, 0)
errno = int(e1)
@@ -741,6 +765,14 @@ func write(fd int, p *byte, np int) (n int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func munmap(addr uintptr, length uintptr) (errno int) {
+ _, _, e1 := Syscall(SYS_MUNMAP, uintptr(addr), uintptr(length), 0)
+ errno = int(e1)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func Chown(path string, uid int, gid int) (errno int) {
_, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid))
errno = int(e1)
@@ -782,7 +814,7 @@ func Ftruncate(fd int, length int64) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getegid() (egid int) {
- r0, _, _ := Syscall(SYS_GETEGID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETEGID, 0, 0, 0)
egid = int(r0)
return
}
@@ -790,7 +822,7 @@ func Getegid() (egid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Geteuid() (euid int) {
- r0, _, _ := Syscall(SYS_GETEUID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETEUID, 0, 0, 0)
euid = int(r0)
return
}
@@ -798,7 +830,7 @@ func Geteuid() (euid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getgid() (gid int) {
- r0, _, _ := Syscall(SYS_GETGID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETGID, 0, 0, 0)
gid = int(r0)
return
}
@@ -806,7 +838,7 @@ func Getgid() (gid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getuid() (uid int) {
- r0, _, _ := Syscall(SYS_GETUID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETUID, 0, 0, 0)
uid = int(r0)
return
}
@@ -918,7 +950,7 @@ func Setfsuid(uid int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setgid(gid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETGID, uintptr(gid), 0, 0)
+ _, _, e1 := RawSyscall(SYS_SETGID, uintptr(gid), 0, 0)
errno = int(e1)
return
}
@@ -926,7 +958,7 @@ func Setgid(gid int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setregid(rgid int, egid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETREGID, uintptr(rgid), uintptr(egid), 0)
+ _, _, e1 := RawSyscall(SYS_SETREGID, uintptr(rgid), uintptr(egid), 0)
errno = int(e1)
return
}
@@ -934,7 +966,7 @@ func Setregid(rgid int, egid int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setresgid(rgid int, egid int, sgid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETRESGID, uintptr(rgid), uintptr(egid), uintptr(sgid))
+ _, _, e1 := RawSyscall(SYS_SETRESGID, uintptr(rgid), uintptr(egid), uintptr(sgid))
errno = int(e1)
return
}
@@ -942,7 +974,7 @@ func Setresgid(rgid int, egid int, sgid int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setresuid(ruid int, euid int, suid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETRESUID, uintptr(ruid), uintptr(euid), uintptr(suid))
+ _, _, e1 := RawSyscall(SYS_SETRESUID, uintptr(ruid), uintptr(euid), uintptr(suid))
errno = int(e1)
return
}
@@ -950,7 +982,7 @@ func Setresuid(ruid int, euid int, suid int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setreuid(ruid int, euid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETREUID, uintptr(ruid), uintptr(euid), 0)
+ _, _, e1 := RawSyscall(SYS_SETREUID, uintptr(ruid), uintptr(euid), 0)
errno = int(e1)
return
}
@@ -1032,7 +1064,7 @@ func connect(s int, addr uintptr, addrlen _Socklen) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func getgroups(n int, list *_Gid_t) (nn int, errno int) {
- r0, _, e1 := Syscall(SYS_GETGROUPS, uintptr(n), uintptr(unsafe.Pointer(list)), 0)
+ r0, _, e1 := RawSyscall(SYS_GETGROUPS, uintptr(n), uintptr(unsafe.Pointer(list)), 0)
nn = int(r0)
errno = int(e1)
return
@@ -1041,7 +1073,15 @@ func getgroups(n int, list *_Gid_t) (nn int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func setgroups(n int, list *_Gid_t) (errno int) {
- _, _, e1 := Syscall(SYS_SETGROUPS, uintptr(n), uintptr(unsafe.Pointer(list)), 0)
+ _, _, e1 := RawSyscall(SYS_SETGROUPS, uintptr(n), uintptr(unsafe.Pointer(list)), 0)
+ errno = int(e1)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (errno int) {
+ _, _, e1 := Syscall6(SYS_GETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(unsafe.Pointer(vallen)), 0)
errno = int(e1)
return
}
@@ -1057,7 +1097,7 @@ func setsockopt(s int, level int, name int, val uintptr, vallen int) (errno int)
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func socket(domain int, typ int, proto int) (fd int, errno int) {
- r0, _, e1 := Syscall(SYS_SOCKET, uintptr(domain), uintptr(typ), uintptr(proto))
+ r0, _, e1 := RawSyscall(SYS_SOCKET, uintptr(domain), uintptr(typ), uintptr(proto))
fd = int(r0)
errno = int(e1)
return
@@ -1066,7 +1106,7 @@ func socket(domain int, typ int, proto int) (fd int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func socketpair(domain int, typ int, proto int, fd *[2]int) (errno int) {
- _, _, e1 := Syscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0)
+ _, _, e1 := RawSyscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0)
errno = int(e1)
return
}
@@ -1074,7 +1114,7 @@ func socketpair(domain int, typ int, proto int, fd *[2]int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int) {
- _, _, e1 := Syscall(SYS_GETPEERNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
+ _, _, e1 := RawSyscall(SYS_GETPEERNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
errno = int(e1)
return
}
@@ -1082,7 +1122,7 @@ func getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int) {
- _, _, e1 := Syscall(SYS_GETSOCKNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
+ _, _, e1 := RawSyscall(SYS_GETSOCKNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
errno = int(e1)
return
}
@@ -1132,3 +1172,12 @@ func sendmsg(s int, msg *Msghdr, flags int) (errno int) {
errno = int(e1)
return
}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, errno int) {
+ r0, _, e1 := Syscall6(SYS_MMAP, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flags), uintptr(fd), uintptr(offset))
+ xaddr = uintptr(r0)
+ errno = int(e1)
+ return
+}
diff --git a/src/pkg/syscall/zsyscall_linux_arm.go b/src/pkg/syscall/zsyscall_linux_arm.go
index 22b736bfa..49d164a3c 100644
--- a/src/pkg/syscall/zsyscall_linux_arm.go
+++ b/src/pkg/syscall/zsyscall_linux_arm.go
@@ -1,4 +1,4 @@
-// mksyscall.sh -b32 syscall_linux.go syscall_linux_arm.go
+// mksyscall.pl -b32 syscall_linux.go syscall_linux_arm.go
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
package syscall
@@ -26,7 +26,7 @@ func openat(dirfd int, path string, flags int, mode uint32) (fd int, errno int)
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func pipe(p *[2]_C_int) (errno int) {
- _, _, e1 := Syscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0)
+ _, _, e1 := RawSyscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0)
errno = int(e1)
return
}
@@ -81,6 +81,14 @@ func ptrace(request int, pid int, addr uintptr, data uintptr) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func reboot(magic1 uint, magic2 uint, cmd int, arg string) (errno int) {
+ _, _, e1 := Syscall6(SYS_REBOOT, uintptr(magic1), uintptr(magic2), uintptr(cmd), uintptr(unsafe.Pointer(StringBytePtr(arg))), 0, 0)
+ errno = int(e1)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func Access(path string, mode uint32) (errno int) {
_, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
errno = int(e1)
@@ -148,7 +156,7 @@ func Creat(path string, mode uint32) (fd int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Dup(oldfd int) (fd int, errno int) {
- r0, _, e1 := Syscall(SYS_DUP, uintptr(oldfd), 0, 0)
+ r0, _, e1 := RawSyscall(SYS_DUP, uintptr(oldfd), 0, 0)
fd = int(r0)
errno = int(e1)
return
@@ -157,7 +165,7 @@ func Dup(oldfd int) (fd int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Dup2(oldfd int, newfd int) (fd int, errno int) {
- r0, _, e1 := Syscall(SYS_DUP2, uintptr(oldfd), uintptr(newfd), 0)
+ r0, _, e1 := RawSyscall(SYS_DUP2, uintptr(oldfd), uintptr(newfd), 0)
fd = int(r0)
errno = int(e1)
return
@@ -166,7 +174,7 @@ func Dup2(oldfd int, newfd int) (fd int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func EpollCreate(size int) (fd int, errno int) {
- r0, _, e1 := Syscall(SYS_EPOLL_CREATE, uintptr(size), 0, 0)
+ r0, _, e1 := RawSyscall(SYS_EPOLL_CREATE, uintptr(size), 0, 0)
fd = int(r0)
errno = int(e1)
return
@@ -175,7 +183,7 @@ func EpollCreate(size int) (fd int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (errno int) {
- _, _, e1 := Syscall6(SYS_EPOLL_CTL, uintptr(epfd), uintptr(op), uintptr(fd), uintptr(unsafe.Pointer(event)), 0, 0)
+ _, _, e1 := RawSyscall6(SYS_EPOLL_CTL, uintptr(epfd), uintptr(op), uintptr(fd), uintptr(unsafe.Pointer(event)), 0, 0)
errno = int(e1)
return
}
@@ -293,7 +301,7 @@ func Getdents(fd int, buf []byte) (n int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getpgid(pid int) (pgid int, errno int) {
- r0, _, e1 := Syscall(SYS_GETPGID, uintptr(pid), 0, 0)
+ r0, _, e1 := RawSyscall(SYS_GETPGID, uintptr(pid), 0, 0)
pgid = int(r0)
errno = int(e1)
return
@@ -302,7 +310,7 @@ func Getpgid(pid int) (pgid int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getpgrp() (pid int) {
- r0, _, _ := Syscall(SYS_GETPGRP, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETPGRP, 0, 0, 0)
pid = int(r0)
return
}
@@ -310,7 +318,7 @@ func Getpgrp() (pid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getpid() (pid int) {
- r0, _, _ := Syscall(SYS_GETPID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETPID, 0, 0, 0)
pid = int(r0)
return
}
@@ -318,7 +326,7 @@ func Getpid() (pid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getppid() (ppid int) {
- r0, _, _ := Syscall(SYS_GETPPID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETPPID, 0, 0, 0)
ppid = int(r0)
return
}
@@ -326,7 +334,7 @@ func Getppid() (ppid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getrlimit(resource int, rlim *Rlimit) (errno int) {
- _, _, e1 := Syscall(SYS_GETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0)
+ _, _, e1 := RawSyscall(SYS_GETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0)
errno = int(e1)
return
}
@@ -334,7 +342,7 @@ func Getrlimit(resource int, rlim *Rlimit) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getrusage(who int, rusage *Rusage) (errno int) {
- _, _, e1 := Syscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0)
+ _, _, e1 := RawSyscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0)
errno = int(e1)
return
}
@@ -342,7 +350,7 @@ func Getrusage(who int, rusage *Rusage) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Gettid() (tid int) {
- r0, _, _ := Syscall(SYS_GETTID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
tid = int(r0)
return
}
@@ -359,7 +367,7 @@ func InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, errno
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func InotifyInit() (fd int, errno int) {
- r0, _, e1 := Syscall(SYS_INOTIFY_INIT, 0, 0, 0)
+ r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT, 0, 0, 0)
fd = int(r0)
errno = int(e1)
return
@@ -368,7 +376,7 @@ func InotifyInit() (fd int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func InotifyInit1(flags int) (fd int, errno int) {
- r0, _, e1 := Syscall(SYS_INOTIFY_INIT1, uintptr(flags), 0, 0)
+ r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT1, uintptr(flags), 0, 0)
fd = int(r0)
errno = int(e1)
return
@@ -377,7 +385,7 @@ func InotifyInit1(flags int) (fd int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func InotifyRmWatch(fd int, watchdesc uint32) (success int, errno int) {
- r0, _, e1 := Syscall(SYS_INOTIFY_RM_WATCH, uintptr(fd), uintptr(watchdesc), 0)
+ r0, _, e1 := RawSyscall(SYS_INOTIFY_RM_WATCH, uintptr(fd), uintptr(watchdesc), 0)
success = int(r0)
errno = int(e1)
return
@@ -386,7 +394,7 @@ func InotifyRmWatch(fd int, watchdesc uint32) (success int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Kill(pid int, sig int) (errno int) {
- _, _, e1 := Syscall(SYS_KILL, uintptr(pid), uintptr(sig), 0)
+ _, _, e1 := RawSyscall(SYS_KILL, uintptr(pid), uintptr(sig), 0)
errno = int(e1)
return
}
@@ -448,6 +456,14 @@ func Mknodat(dirfd int, path string, mode uint32, dev int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func Mount(source string, target string, fstype string, flags int, data string) (errno int) {
+ _, _, e1 := Syscall6(SYS_MOUNT, uintptr(unsafe.Pointer(StringBytePtr(source))), uintptr(unsafe.Pointer(StringBytePtr(target))), uintptr(unsafe.Pointer(StringBytePtr(fstype))), uintptr(flags), uintptr(unsafe.Pointer(StringBytePtr(data))), 0)
+ errno = int(e1)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func Nanosleep(time *Timespec, leftover *Timespec) (errno int) {
_, _, e1 := Syscall(SYS_NANOSLEEP, uintptr(unsafe.Pointer(time)), uintptr(unsafe.Pointer(leftover)), 0)
errno = int(e1)
@@ -555,7 +571,7 @@ func Sethostname(p []byte) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setpgid(pid int, pgid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETPGID, uintptr(pid), uintptr(pgid), 0)
+ _, _, e1 := RawSyscall(SYS_SETPGID, uintptr(pid), uintptr(pgid), 0)
errno = int(e1)
return
}
@@ -563,7 +579,7 @@ func Setpgid(pid int, pgid int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setrlimit(resource int, rlim *Rlimit) (errno int) {
- _, _, e1 := Syscall(SYS_SETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0)
+ _, _, e1 := RawSyscall(SYS_SETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0)
errno = int(e1)
return
}
@@ -571,7 +587,7 @@ func Setrlimit(resource int, rlim *Rlimit) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setsid() (pid int, errno int) {
- r0, _, e1 := Syscall(SYS_SETSID, 0, 0, 0)
+ r0, _, e1 := RawSyscall(SYS_SETSID, 0, 0, 0)
pid = int(r0)
errno = int(e1)
return
@@ -580,7 +596,7 @@ func Setsid() (pid int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Settimeofday(tv *Timeval) (errno int) {
- _, _, e1 := Syscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0)
+ _, _, e1 := RawSyscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0)
errno = int(e1)
return
}
@@ -588,7 +604,7 @@ func Settimeofday(tv *Timeval) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setuid(uid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETUID, uintptr(uid), 0, 0)
+ _, _, e1 := RawSyscall(SYS_SETUID, uintptr(uid), 0, 0)
errno = int(e1)
return
}
@@ -611,7 +627,7 @@ func Sync() {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Sysinfo(info *Sysinfo_t) (errno int) {
- _, _, e1 := Syscall(SYS_SYSINFO, uintptr(unsafe.Pointer(info)), 0, 0)
+ _, _, e1 := RawSyscall(SYS_SYSINFO, uintptr(unsafe.Pointer(info)), 0, 0)
errno = int(e1)
return
}
@@ -628,7 +644,7 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Tgkill(tgid int, tid int, sig int) (errno int) {
- _, _, e1 := Syscall(SYS_TGKILL, uintptr(tgid), uintptr(tid), uintptr(sig))
+ _, _, e1 := RawSyscall(SYS_TGKILL, uintptr(tgid), uintptr(tid), uintptr(sig))
errno = int(e1)
return
}
@@ -636,7 +652,7 @@ func Tgkill(tgid int, tid int, sig int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Times(tms *Tms) (ticks uintptr, errno int) {
- r0, _, e1 := Syscall(SYS_TIMES, uintptr(unsafe.Pointer(tms)), 0, 0)
+ r0, _, e1 := RawSyscall(SYS_TIMES, uintptr(unsafe.Pointer(tms)), 0, 0)
ticks = uintptr(r0)
errno = int(e1)
return
@@ -645,7 +661,7 @@ func Times(tms *Tms) (ticks uintptr, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Umask(mask int) (oldmask int) {
- r0, _, _ := Syscall(SYS_UMASK, uintptr(mask), 0, 0)
+ r0, _, _ := RawSyscall(SYS_UMASK, uintptr(mask), 0, 0)
oldmask = int(r0)
return
}
@@ -653,7 +669,7 @@ func Umask(mask int) (oldmask int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Uname(buf *Utsname) (errno int) {
- _, _, e1 := Syscall(SYS_UNAME, uintptr(unsafe.Pointer(buf)), 0, 0)
+ _, _, e1 := RawSyscall(SYS_UNAME, uintptr(unsafe.Pointer(buf)), 0, 0)
errno = int(e1)
return
}
@@ -676,6 +692,14 @@ func Unlinkat(dirfd int, path string) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func Unmount(target string, flags int) (errno int) {
+ _, _, e1 := Syscall(SYS_UMOUNT2, uintptr(unsafe.Pointer(StringBytePtr(target))), uintptr(flags), 0)
+ errno = int(e1)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func Unshare(flags int) (errno int) {
_, _, e1 := Syscall(SYS_UNSHARE, uintptr(flags), 0, 0)
errno = int(e1)
@@ -741,6 +765,14 @@ func write(fd int, p *byte, np int) (n int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func munmap(addr uintptr, length uintptr) (errno int) {
+ _, _, e1 := Syscall(SYS_MUNMAP, uintptr(addr), uintptr(length), 0)
+ errno = int(e1)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, errno int) {
r0, _, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
fd = int(r0)
@@ -767,7 +799,7 @@ func connect(s int, addr uintptr, addrlen _Socklen) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func getgroups(n int, list *_Gid_t) (nn int, errno int) {
- r0, _, e1 := Syscall(SYS_GETGROUPS32, uintptr(n), uintptr(unsafe.Pointer(list)), 0)
+ r0, _, e1 := RawSyscall(SYS_GETGROUPS32, uintptr(n), uintptr(unsafe.Pointer(list)), 0)
nn = int(r0)
errno = int(e1)
return
@@ -776,7 +808,15 @@ func getgroups(n int, list *_Gid_t) (nn int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func setgroups(n int, list *_Gid_t) (errno int) {
- _, _, e1 := Syscall(SYS_SETGROUPS32, uintptr(n), uintptr(unsafe.Pointer(list)), 0)
+ _, _, e1 := RawSyscall(SYS_SETGROUPS32, uintptr(n), uintptr(unsafe.Pointer(list)), 0)
+ errno = int(e1)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (errno int) {
+ _, _, e1 := Syscall6(SYS_GETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(unsafe.Pointer(vallen)), 0)
errno = int(e1)
return
}
@@ -792,7 +832,7 @@ func setsockopt(s int, level int, name int, val uintptr, vallen int) (errno int)
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func socket(domain int, typ int, proto int) (fd int, errno int) {
- r0, _, e1 := Syscall(SYS_SOCKET, uintptr(domain), uintptr(typ), uintptr(proto))
+ r0, _, e1 := RawSyscall(SYS_SOCKET, uintptr(domain), uintptr(typ), uintptr(proto))
fd = int(r0)
errno = int(e1)
return
@@ -801,7 +841,7 @@ func socket(domain int, typ int, proto int) (fd int, errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int) {
- _, _, e1 := Syscall(SYS_GETPEERNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
+ _, _, e1 := RawSyscall(SYS_GETPEERNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
errno = int(e1)
return
}
@@ -809,7 +849,7 @@ func getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int) {
- _, _, e1 := Syscall(SYS_GETSOCKNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
+ _, _, e1 := RawSyscall(SYS_GETSOCKNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
errno = int(e1)
return
}
@@ -846,7 +886,7 @@ func sendto(s int, buf []byte, flags int, to uintptr, addrlen _Socklen) (errno i
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func socketpair(domain int, typ int, flags int, fd *[2]int) (errno int) {
- _, _, e1 := Syscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(flags), uintptr(unsafe.Pointer(fd)), 0, 0)
+ _, _, e1 := RawSyscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(flags), uintptr(unsafe.Pointer(fd)), 0, 0)
errno = int(e1)
return
}
@@ -911,7 +951,7 @@ func Ftruncate(fd int, length int64) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getegid() (egid int) {
- r0, _, _ := Syscall(SYS_GETEGID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETEGID, 0, 0, 0)
egid = int(r0)
return
}
@@ -919,7 +959,7 @@ func Getegid() (egid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Geteuid() (euid int) {
- r0, _, _ := Syscall(SYS_GETEUID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETEUID, 0, 0, 0)
euid = int(r0)
return
}
@@ -927,7 +967,7 @@ func Geteuid() (euid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getgid() (gid int) {
- r0, _, _ := Syscall(SYS_GETGID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETGID, 0, 0, 0)
gid = int(r0)
return
}
@@ -935,7 +975,7 @@ func Getgid() (gid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getuid() (uid int) {
- r0, _, _ := Syscall(SYS_GETUID, 0, 0, 0)
+ r0, _, _ := RawSyscall(SYS_GETUID, 0, 0, 0)
uid = int(r0)
return
}
@@ -992,7 +1032,7 @@ func Setfsuid(uid int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setgid(gid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETGID, uintptr(gid), 0, 0)
+ _, _, e1 := RawSyscall(SYS_SETGID, uintptr(gid), 0, 0)
errno = int(e1)
return
}
@@ -1000,7 +1040,7 @@ func Setgid(gid int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setregid(rgid int, egid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETREGID, uintptr(rgid), uintptr(egid), 0)
+ _, _, e1 := RawSyscall(SYS_SETREGID, uintptr(rgid), uintptr(egid), 0)
errno = int(e1)
return
}
@@ -1008,7 +1048,7 @@ func Setregid(rgid int, egid int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setresgid(rgid int, egid int, sgid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETRESGID, uintptr(rgid), uintptr(egid), uintptr(sgid))
+ _, _, e1 := RawSyscall(SYS_SETRESGID, uintptr(rgid), uintptr(egid), uintptr(sgid))
errno = int(e1)
return
}
@@ -1016,7 +1056,7 @@ func Setresgid(rgid int, egid int, sgid int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setresuid(ruid int, euid int, suid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETRESUID, uintptr(ruid), uintptr(euid), uintptr(suid))
+ _, _, e1 := RawSyscall(SYS_SETRESUID, uintptr(ruid), uintptr(euid), uintptr(suid))
errno = int(e1)
return
}
@@ -1024,7 +1064,7 @@ func Setresuid(ruid int, euid int, suid int) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setreuid(ruid int, euid int) (errno int) {
- _, _, e1 := Syscall(SYS_SETREUID, uintptr(ruid), uintptr(euid), 0)
+ _, _, e1 := RawSyscall(SYS_SETREUID, uintptr(ruid), uintptr(euid), 0)
errno = int(e1)
return
}
@@ -1073,7 +1113,7 @@ func Truncate(path string, length int64) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Gettimeofday(tv *Timeval) (errno int) {
- _, _, e1 := Syscall(SYS_GETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0)
+ _, _, e1 := RawSyscall(SYS_GETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0)
errno = int(e1)
return
}
@@ -1081,8 +1121,17 @@ func Gettimeofday(tv *Timeval) (errno int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Time(t *Time_t) (tt Time_t, errno int) {
- r0, _, e1 := Syscall(SYS_TIME, uintptr(unsafe.Pointer(t)), 0, 0)
+ r0, _, e1 := RawSyscall(SYS_TIME, uintptr(unsafe.Pointer(t)), 0, 0)
tt = Time_t(r0)
errno = int(e1)
return
}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func mmap2(addr uintptr, length uintptr, prot int, flags int, fd int, pageOffset uintptr) (xaddr uintptr, errno int) {
+ r0, _, e1 := Syscall6(SYS_MMAP2, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flags), uintptr(fd), uintptr(pageOffset))
+ xaddr = uintptr(r0)
+ errno = int(e1)
+ return
+}
diff --git a/src/pkg/syscall/zsyscall_plan9_386.go b/src/pkg/syscall/zsyscall_plan9_386.go
new file mode 100644
index 000000000..75c411ad6
--- /dev/null
+++ b/src/pkg/syscall/zsyscall_plan9_386.go
@@ -0,0 +1,267 @@
+// mksyscall.pl -l32 -plan9 syscall_plan9.go syscall_plan9_386.go
+// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+
+package syscall
+
+import "unsafe"
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func exits(msg *byte) {
+ Syscall(SYS_EXITS, uintptr(unsafe.Pointer(msg)), 0, 0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func fd2path(fd int, buf []byte) (err Error) {
+ var _p0 unsafe.Pointer
+ if len(buf) > 0 {
+ _p0 = unsafe.Pointer(&buf[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_FD2PATH, uintptr(fd), uintptr(_p0), uintptr(len(buf)))
+ err = nil
+ if int(r0) == -1 {
+ err = NewError(e1)
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func pipe(p *[2]_C_int) (err Error) {
+ r0, _, e1 := Syscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0)
+ err = nil
+ if int(r0) == -1 {
+ err = NewError(e1)
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func sleep(millisecs int32) (err Error) {
+ r0, _, e1 := Syscall(SYS_SLEEP, uintptr(millisecs), 0, 0)
+ err = nil
+ if int(r0) == -1 {
+ err = NewError(e1)
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func await(s []byte) (n int, err Error) {
+ var _p0 unsafe.Pointer
+ if len(s) > 0 {
+ _p0 = unsafe.Pointer(&s[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_AWAIT, uintptr(_p0), uintptr(len(s)), 0)
+ n = int(r0)
+ err = nil
+ if int(r0) == -1 {
+ err = NewError(e1)
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Dup(oldfd int, newfd int) (fd int, err Error) {
+ r0, _, e1 := Syscall(SYS_DUP, uintptr(oldfd), uintptr(newfd), 0)
+ fd = int(r0)
+ err = nil
+ if int(r0) == -1 {
+ err = NewError(e1)
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Open(path string, mode int) (fd int, err Error) {
+ r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ fd = int(r0)
+ err = nil
+ if int(r0) == -1 {
+ err = NewError(e1)
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Create(path string, mode int, perm uint32) (fd int, err Error) {
+ r0, _, e1 := Syscall(SYS_CREATE, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(perm))
+ fd = int(r0)
+ err = nil
+ if int(r0) == -1 {
+ err = NewError(e1)
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Remove(path string) (err Error) {
+ r0, _, e1 := Syscall(SYS_REMOVE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ err = nil
+ if int(r0) == -1 {
+ err = NewError(e1)
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Pread(fd int, p []byte, offset int64) (n int, err Error) {
+ var _p0 unsafe.Pointer
+ if len(p) > 0 {
+ _p0 = unsafe.Pointer(&p[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall6(SYS_PREAD, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0)
+ n = int(r0)
+ err = nil
+ if int(r0) == -1 {
+ err = NewError(e1)
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Pwrite(fd int, p []byte, offset int64) (n int, err Error) {
+ var _p0 unsafe.Pointer
+ if len(p) > 0 {
+ _p0 = unsafe.Pointer(&p[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall6(SYS_PWRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0)
+ n = int(r0)
+ err = nil
+ if int(r0) == -1 {
+ err = NewError(e1)
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Close(fd int) (err Error) {
+ r0, _, e1 := Syscall(SYS_CLOSE, uintptr(fd), 0, 0)
+ err = nil
+ if int(r0) == -1 {
+ err = NewError(e1)
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Chdir(path string) (err Error) {
+ r0, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ err = nil
+ if int(r0) == -1 {
+ err = NewError(e1)
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Bind(name string, old string, flag int) (err Error) {
+ r0, _, e1 := Syscall(SYS_BIND, uintptr(unsafe.Pointer(StringBytePtr(name))), uintptr(unsafe.Pointer(StringBytePtr(old))), uintptr(flag))
+ err = nil
+ if int(r0) == -1 {
+ err = NewError(e1)
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Mount(fd int, afd int, old string, flag int, aname string) (err Error) {
+ r0, _, e1 := Syscall6(SYS_MOUNT, uintptr(fd), uintptr(afd), uintptr(unsafe.Pointer(StringBytePtr(old))), uintptr(flag), uintptr(unsafe.Pointer(StringBytePtr(aname))), 0)
+ err = nil
+ if int(r0) == -1 {
+ err = NewError(e1)
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Stat(path string, edir []byte) (n int, err Error) {
+ var _p0 unsafe.Pointer
+ if len(edir) > 0 {
+ _p0 = unsafe.Pointer(&edir[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(_p0), uintptr(len(edir)))
+ n = int(r0)
+ err = nil
+ if int(r0) == -1 {
+ err = NewError(e1)
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fstat(fd int, edir []byte) (n int, err Error) {
+ var _p0 unsafe.Pointer
+ if len(edir) > 0 {
+ _p0 = unsafe.Pointer(&edir[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir)))
+ n = int(r0)
+ err = nil
+ if int(r0) == -1 {
+ err = NewError(e1)
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Wstat(path string, edir []byte) (err Error) {
+ var _p0 unsafe.Pointer
+ if len(edir) > 0 {
+ _p0 = unsafe.Pointer(&edir[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_WSTAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(_p0), uintptr(len(edir)))
+ err = nil
+ if int(r0) == -1 {
+ err = NewError(e1)
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fwstat(fd int, edir []byte) (err Error) {
+ var _p0 unsafe.Pointer
+ if len(edir) > 0 {
+ _p0 = unsafe.Pointer(&edir[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_FWSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir)))
+ err = nil
+ if int(r0) == -1 {
+ err = NewError(e1)
+ }
+ return
+}
diff --git a/src/pkg/syscall/zsyscall_windows_386.go b/src/pkg/syscall/zsyscall_windows_386.go
index 543992ea6..f4cfdeed8 100644
--- a/src/pkg/syscall/zsyscall_windows_386.go
+++ b/src/pkg/syscall/zsyscall_windows_386.go
@@ -1,4 +1,4 @@
-// mksyscall_windows.sh -l32 syscall_windows.go syscall_windows_386.go
+// mksyscall_windows.pl -l32 syscall_windows.go syscall_windows_386.go
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
package syscall
@@ -69,6 +69,7 @@ var (
procLocalFree = getSysProcAddr(modkernel32, "LocalFree")
procSetHandleInformation = getSysProcAddr(modkernel32, "SetHandleInformation")
procFlushFileBuffers = getSysProcAddr(modkernel32, "FlushFileBuffers")
+ procGetFullPathNameW = getSysProcAddr(modkernel32, "GetFullPathNameW")
procWSAStartup = getSysProcAddr(modwsock32, "WSAStartup")
procWSACleanup = getSysProcAddr(modwsock32, "WSACleanup")
procsocket = getSysProcAddr(modwsock32, "socket")
@@ -515,7 +516,7 @@ func CancelIo(s uint32) (errno int) {
return
}
-func CreateProcess(appName *int16, commandLine *uint16, procSecurity *int16, threadSecurity *int16, inheritHandles bool, creationFlags uint32, env *uint16, currentDir *uint16, startupInfo *StartupInfo, outProcInfo *ProcessInformation) (errno int) {
+func CreateProcess(appName *uint16, commandLine *uint16, procSecurity *SecurityAttributes, threadSecurity *SecurityAttributes, inheritHandles bool, creationFlags uint32, env *uint16, currentDir *uint16, startupInfo *StartupInfo, outProcInfo *ProcessInformation) (errno int) {
var _p0 uint32
if inheritHandles {
_p0 = 1
@@ -885,6 +886,21 @@ func FlushFileBuffers(handle int32) (errno int) {
return
}
+func GetFullPathName(path *uint16, buflen uint32, buf *uint16, fname **uint16) (n uint32, errno int) {
+ r0, _, e1 := Syscall6(procGetFullPathNameW, 4, uintptr(unsafe.Pointer(path)), uintptr(buflen), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(fname)), 0, 0)
+ n = uint32(r0)
+ if n == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
func WSAStartup(verreq uint32, data *WSAData) (sockerrno int) {
r0, _, _ := Syscall(procWSAStartup, 2, uintptr(verreq), uintptr(unsafe.Pointer(data)), 0)
sockerrno = int(r0)
diff --git a/src/pkg/syscall/zsysnum_darwin_amd64.go b/src/pkg/syscall/zsysnum_darwin_amd64.go
index 8d5c93478..f9c6e077d 100644
--- a/src/pkg/syscall/zsysnum_darwin_amd64.go
+++ b/src/pkg/syscall/zsysnum_darwin_amd64.go
@@ -1,4 +1,4 @@
-// mksysnum_darwin.sh /home/rsc/pub/xnu-1228/bsd/kern/syscalls.master
+// mksysnum_darwin.pl /home/rsc/pub/xnu-1228/bsd/kern/syscalls.master
// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
package syscall
diff --git a/src/pkg/syscall/zsysnum_linux_386.go b/src/pkg/syscall/zsysnum_linux_386.go
index 55529adaa..71e21c7a5 100644
--- a/src/pkg/syscall/zsysnum_linux_386.go
+++ b/src/pkg/syscall/zsysnum_linux_386.go
@@ -1,4 +1,4 @@
-// mksysnum_linux.sh /usr/include/asm/unistd_32.h
+// mksysnum_linux.pl /usr/include/asm/unistd_32.h
// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
package syscall
diff --git a/src/pkg/syscall/zsysnum_linux_amd64.go b/src/pkg/syscall/zsysnum_linux_amd64.go
index 2621999c7..77d4eea9e 100644
--- a/src/pkg/syscall/zsysnum_linux_amd64.go
+++ b/src/pkg/syscall/zsysnum_linux_amd64.go
@@ -1,4 +1,4 @@
-// mksysnum_linux.sh /usr/include/asm/unistd_64.h
+// mksysnum_linux.pl /usr/include/asm/unistd_64.h
// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
package syscall
diff --git a/src/pkg/syscall/zsysnum_plan9_386.go b/src/pkg/syscall/zsysnum_plan9_386.go
new file mode 100644
index 000000000..4135b8d81
--- /dev/null
+++ b/src/pkg/syscall/zsysnum_plan9_386.go
@@ -0,0 +1,47 @@
+// mksysnum_plan9.sh /media/sys/src/libc/9syscall/sys.h
+// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
+
+package syscall
+
+const (
+ SYS_SYSR1 = 0
+ SYS_BIND = 2
+ SYS_CHDIR = 3
+ SYS_CLOSE = 4
+ SYS_DUP = 5
+ SYS_ALARM = 6
+ SYS_EXEC = 7
+ SYS_EXITS = 8
+ SYS_FAUTH = 10
+ SYS_SEGBRK = 12
+ SYS_OPEN = 14
+ SYS_OSEEK = 16
+ SYS_SLEEP = 17
+ SYS_RFORK = 19
+ SYS_PIPE = 21
+ SYS_CREATE = 22
+ SYS_FD2PATH = 23
+ SYS_BRK_ = 24
+ SYS_REMOVE = 25
+ SYS_NOTIFY = 28
+ SYS_NOTED = 29
+ SYS_SEGATTACH = 30
+ SYS_SEGDETACH = 31
+ SYS_SEGFREE = 32
+ SYS_SEGFLUSH = 33
+ SYS_RENDEZVOUS = 34
+ SYS_UNMOUNT = 35
+ SYS_SEMACQUIRE = 37
+ SYS_SEMRELEASE = 38
+ SYS_SEEK = 39
+ SYS_FVERSION = 40
+ SYS_ERRSTR = 41
+ SYS_STAT = 42
+ SYS_FSTAT = 43
+ SYS_WSTAT = 44
+ SYS_FWSTAT = 45
+ SYS_MOUNT = 46
+ SYS_AWAIT = 47
+ SYS_PREAD = 50
+ SYS_PWRITE = 51
+)
diff --git a/src/pkg/syscall/ztypes_darwin_amd64.go b/src/pkg/syscall/ztypes_darwin_amd64.go
index 91ee45796..936a4e804 100644
--- a/src/pkg/syscall/ztypes_darwin_amd64.go
+++ b/src/pkg/syscall/ztypes_darwin_amd64.go
@@ -229,7 +229,7 @@ type Msghdr struct {
Name *byte
Namelen uint32
Pad_godefs_0 [4]byte
- Iov *Iovec
+ Iov uint64
Iovlen int32
Pad_godefs_1 [4]byte
Control *byte
diff --git a/src/pkg/syscall/ztypes_freebsd_386.go b/src/pkg/syscall/ztypes_freebsd_386.go
index 0f8e37abb..f4d256f4e 100644
--- a/src/pkg/syscall/ztypes_freebsd_386.go
+++ b/src/pkg/syscall/ztypes_freebsd_386.go
@@ -43,6 +43,13 @@ const (
SizeofIfaMsghdr = 0x14
SizeofRtMsghdr = 0x5c
SizeofRtMetrics = 0x38
+ SizeofBpfVersion = 0x4
+ SizeofBpfStat = 0x8
+ SizeofBpfZbuf = 0xc
+ SizeofBpfProgram = 0x8
+ SizeofBpfInsn = 0x8
+ SizeofBpfHdr = 0x14
+ SizeofBpfZbufHeader = 0x20
)
// Types
@@ -326,3 +333,46 @@ type RtMetrics struct {
Weight uint32
Filler [3]uint32
}
+
+type BpfVersion struct {
+ Major uint16
+ Minor uint16
+}
+
+type BpfStat struct {
+ Recv uint32
+ Drop uint32
+}
+
+type BpfZbuf struct {
+ Bufa *byte
+ Bufb *byte
+ Buflen uint32
+}
+
+type BpfProgram struct {
+ Len uint32
+ Insns *BpfInsn
+}
+
+type BpfInsn struct {
+ Code uint16
+ Jt uint8
+ Jf uint8
+ K uint32
+}
+
+type BpfHdr struct {
+ Tstamp Timeval
+ Caplen uint32
+ Datalen uint32
+ Hdrlen uint16
+ Pad_godefs_0 [2]byte
+}
+
+type BpfZbufHeader struct {
+ Kernel_gen uint32
+ Kernel_len uint32
+ User_gen uint32
+ X_bzh_pad [5]uint32
+}
diff --git a/src/pkg/syscall/ztypes_freebsd_amd64.go b/src/pkg/syscall/ztypes_freebsd_amd64.go
index 83a54f66b..cf6355caf 100644
--- a/src/pkg/syscall/ztypes_freebsd_amd64.go
+++ b/src/pkg/syscall/ztypes_freebsd_amd64.go
@@ -43,6 +43,13 @@ const (
SizeofIfaMsghdr = 0x14
SizeofRtMsghdr = 0x98
SizeofRtMetrics = 0x70
+ SizeofBpfVersion = 0x4
+ SizeofBpfStat = 0x8
+ SizeofBpfZbuf = 0x18
+ SizeofBpfProgram = 0x10
+ SizeofBpfInsn = 0x8
+ SizeofBpfHdr = 0x20
+ SizeofBpfZbufHeader = 0x20
)
// Types
@@ -329,3 +336,47 @@ type RtMetrics struct {
Weight uint64
Filler [3]uint64
}
+
+type BpfVersion struct {
+ Major uint16
+ Minor uint16
+}
+
+type BpfStat struct {
+ Recv uint32
+ Drop uint32
+}
+
+type BpfZbuf struct {
+ Bufa *byte
+ Bufb *byte
+ Buflen uint64
+}
+
+type BpfProgram struct {
+ Len uint32
+ Pad_godefs_0 [4]byte
+ Insns *BpfInsn
+}
+
+type BpfInsn struct {
+ Code uint16
+ Jt uint8
+ Jf uint8
+ K uint32
+}
+
+type BpfHdr struct {
+ Tstamp Timeval
+ Caplen uint32
+ Datalen uint32
+ Hdrlen uint16
+ Pad_godefs_0 [6]byte
+}
+
+type BpfZbufHeader struct {
+ Kernel_gen uint32
+ Kernel_len uint32
+ User_gen uint32
+ X_bzh_pad [5]uint32
+}
diff --git a/src/pkg/syscall/ztypes_plan9_386.go b/src/pkg/syscall/ztypes_plan9_386.go
new file mode 100644
index 000000000..8f823ba65
--- /dev/null
+++ b/src/pkg/syscall/ztypes_plan9_386.go
@@ -0,0 +1,74 @@
+// godefs -gsyscall -f -m32 types_plan9.c
+
+// MACHINE GENERATED - DO NOT EDIT.
+
+package syscall
+
+// Constants
+const (
+ O_RDONLY = 0
+ O_WRONLY = 0x1
+ O_RDWR = 0x2
+ O_CLOEXEC = 0x20
+ O_EXCL = 0x1000
+ STATMAX = 0xffff
+ ERRMAX = 0x80
+ MORDER = 0x3
+ MREPL = 0
+ MBEFORE = 0x1
+ MAFTER = 0x2
+ MCREATE = 0x4
+ MCACHE = 0x10
+ MMASK = 0x17
+ RFNAMEG = 0x1
+ RFENVG = 0x2
+ RFFDG = 0x4
+ RFNOTEG = 0x8
+ RFPROC = 0x10
+ RFMEM = 0x20
+ RFNOWAIT = 0x40
+ RFCNAMEG = 0x400
+ RFCENVG = 0x800
+ RFCFDG = 0x1000
+ RFREND = 0x2000
+ RFNOMNT = 0x4000
+ QTDIR = 0x80
+ QTAPPEND = 0x40
+ QTEXCL = 0x20
+ QTMOUNT = 0x10
+ QTAUTH = 0x8
+ QTTMP = 0x4
+ QTFILE = 0
+ DMDIR = 0x80000000
+ DMAPPEND = 0x40000000
+ DMEXCL = 0x20000000
+ DMMOUNT = 0x10000000
+ DMAUTH = 0x8000000
+ DMTMP = 0x4000000
+ DMREAD = 0x4
+ DMWRITE = 0x2
+ DMEXEC = 0x1
+ STATFIXLEN = 0x31
+)
+
+// Types
+
+type _C_int int32
+
+type Prof struct {
+ Pp *[0]byte /* sPlink */
+ Next *[0]byte /* sPlink */
+ Last *[0]byte /* sPlink */
+ First *[0]byte /* sPlink */
+ Pid uint32
+ What uint32
+}
+
+type Tos struct {
+ Prof Prof
+ Cyclefreq uint64
+ Kcycles int64
+ Pcycles int64
+ Pid uint32
+ Clock uint32
+}
diff --git a/src/pkg/syscall/ztypes_windows_386.go b/src/pkg/syscall/ztypes_windows_386.go
index ff367a858..56d4198dc 100644
--- a/src/pkg/syscall/ztypes_windows_386.go
+++ b/src/pkg/syscall/ztypes_windows_386.go
@@ -482,6 +482,10 @@ type DNSSRVData struct {
Pad uint16
}
+type DNSPTRData struct {
+ Host *uint16
+}
+
type DNSRecord struct {
Next *DNSRecord
Name *uint16
diff --git a/src/pkg/syslog/Makefile b/src/pkg/syslog/Makefile
index cf6863c87..82baf7253 100644
--- a/src/pkg/syslog/Makefile
+++ b/src/pkg/syslog/Makefile
@@ -7,5 +7,6 @@ include ../../Make.inc
TARG=syslog
GOFILES=\
syslog.go\
+ syslog_unix.go\
include ../../Make.pkg
diff --git a/src/pkg/syslog/syslog.go b/src/pkg/syslog/syslog.go
index 4924a76d0..4ada113f1 100644
--- a/src/pkg/syslog/syslog.go
+++ b/src/pkg/syslog/syslog.go
@@ -34,7 +34,17 @@ const (
type Writer struct {
priority Priority
prefix string
- conn net.Conn
+ conn serverConn
+}
+
+type serverConn interface {
+ writeBytes(p Priority, prefix string, b []byte) (int, os.Error)
+ writeString(p Priority, prefix string, s string) (int, os.Error)
+ close() os.Error
+}
+
+type netConn struct {
+ conn net.Conn
}
// New establishes a new connection to the system log daemon.
@@ -52,46 +62,30 @@ func Dial(network, raddr string, priority Priority, prefix string) (w *Writer, e
if prefix == "" {
prefix = os.Args[0]
}
- var conn net.Conn
+ var conn serverConn
if network == "" {
conn, err = unixSyslog()
} else {
- conn, err = net.Dial(network, "", raddr)
+ var c net.Conn
+ c, err = net.Dial(network, raddr)
+ conn = netConn{c}
}
return &Writer{priority, prefix, conn}, err
}
-func unixSyslog() (conn net.Conn, err os.Error) {
- logTypes := []string{"unixgram", "unix"}
- logPaths := []string{"/dev/log", "/var/run/syslog"}
- var raddr string
- for _, network := range logTypes {
- for _, path := range logPaths {
- raddr = path
- conn, err := net.Dial(network, "", raddr)
- if err != nil {
- continue
- } else {
- return conn, nil
- }
- }
- }
- return nil, os.ErrorString("Unix syslog delivery error")
-}
-
// Write sends a log message to the syslog daemon.
func (w *Writer) Write(b []byte) (int, os.Error) {
if w.priority > LOG_DEBUG || w.priority < LOG_EMERG {
return 0, os.EINVAL
}
- return fmt.Fprintf(w.conn, "<%d>%s: %s\n", w.priority, w.prefix, b)
+ return w.conn.writeBytes(w.priority, w.prefix, b)
}
func (w *Writer) writeString(p Priority, s string) (int, os.Error) {
- return fmt.Fprintf(w.conn, "<%d>%s: %s\n", p, w.prefix, s)
+ return w.conn.writeString(p, w.prefix, s)
}
-func (w *Writer) Close() os.Error { return w.conn.Close() }
+func (w *Writer) Close() os.Error { return w.conn.close() }
// Emerg logs a message using the LOG_EMERG priority.
func (w *Writer) Emerg(m string) (err os.Error) {
@@ -131,6 +125,18 @@ func (w *Writer) Debug(m string) (err os.Error) {
return err
}
+func (n netConn) writeBytes(p Priority, prefix string, b []byte) (int, os.Error) {
+ return fmt.Fprintf(n.conn, "<%d>%s: %s\n", p, prefix, b)
+}
+
+func (n netConn) writeString(p Priority, prefix string, s string) (int, os.Error) {
+ return fmt.Fprintf(n.conn, "<%d>%s: %s\n", p, prefix, s)
+}
+
+func (n netConn) close() os.Error {
+ return n.conn.Close()
+}
+
// NewLogger provides an object that implements the full log.Logger interface,
// but sends messages to Syslog instead; flag is passed as is to Logger;
// priority will be used for all messages sent using this interface.
diff --git a/src/pkg/syslog/syslog_unix.go b/src/pkg/syslog/syslog_unix.go
new file mode 100644
index 000000000..fa15e882d
--- /dev/null
+++ b/src/pkg/syslog/syslog_unix.go
@@ -0,0 +1,31 @@
+// 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 syslog
+
+import (
+ "net"
+ "os"
+)
+
+// unixSyslog opens a connection to the syslog daemon running on the
+// local machine using a Unix domain socket.
+
+func unixSyslog() (conn serverConn, err os.Error) {
+ logTypes := []string{"unixgram", "unix"}
+ logPaths := []string{"/dev/log", "/var/run/syslog"}
+ var raddr string
+ for _, network := range logTypes {
+ for _, path := range logPaths {
+ raddr = path
+ conn, err := net.Dial(network, raddr)
+ if err != nil {
+ continue
+ } else {
+ return netConn{conn}, nil
+ }
+ }
+ }
+ return nil, os.ErrorString("Unix syslog delivery error")
+}
diff --git a/src/pkg/template/template.go b/src/pkg/template/template.go
index c3cb8901a..28872dbee 100644
--- a/src/pkg/template/template.go
+++ b/src/pkg/template/template.go
@@ -267,7 +267,6 @@ func (t *Template) nextItem() []byte {
}
leadingSpace := i > start
// What's left is nothing, newline, delimited string, or plain text
-Switch:
switch {
case i == len(t.buf):
// EOF; nothing to do
@@ -622,7 +621,7 @@ func (t *Template) parse() {
// Evaluate interfaces and pointers looking for a value that can look up the name, via a
// struct field, method, or map key, and return the result of the lookup.
func (t *Template) lookup(st *state, v reflect.Value, name string) reflect.Value {
- for v != nil {
+ for v.IsValid() {
typ := v.Type()
if n := v.Type().NumMethod(); n > 0 {
for i := 0; i < n; i++ {
@@ -636,23 +635,23 @@ func (t *Template) lookup(st *state, v reflect.Value, name string) reflect.Value
}
}
}
- switch av := v.(type) {
- case *reflect.PtrValue:
+ switch av := v; av.Kind() {
+ case reflect.Ptr:
v = av.Elem()
- case *reflect.InterfaceValue:
+ case reflect.Interface:
v = av.Elem()
- case *reflect.StructValue:
+ case reflect.Struct:
if !isExported(name) {
t.execError(st, t.linenum, "name not exported: %s in type %s", name, st.data.Type())
}
return av.FieldByName(name)
- case *reflect.MapValue:
- if v := av.Elem(reflect.NewValue(name)); v != nil {
+ case reflect.Map:
+ if v := av.MapIndex(reflect.NewValue(name)); v.IsValid() {
return v
}
- return reflect.MakeZero(typ.(*reflect.MapType).Elem())
+ return reflect.Zero(typ.Elem())
default:
- return nil
+ return reflect.Value{}
}
}
return v
@@ -662,8 +661,8 @@ func (t *Template) lookup(st *state, v reflect.Value, name string) reflect.Value
// It is forgiving: if the value is not a pointer, it returns it rather than giving
// an error. If the pointer is nil, it is returned as is.
func indirectPtr(v reflect.Value, numLevels int) reflect.Value {
- for i := numLevels; v != nil && i > 0; i++ {
- if p, ok := v.(*reflect.PtrValue); ok {
+ for i := numLevels; v.IsValid() && i > 0; i++ {
+ if p := v; p.Kind() == reflect.Ptr {
if p.IsNil() {
return v
}
@@ -678,11 +677,11 @@ func indirectPtr(v reflect.Value, numLevels int) reflect.Value {
// Walk v through pointers and interfaces, extracting the elements within.
func indirect(v reflect.Value) reflect.Value {
loop:
- for v != nil {
- switch av := v.(type) {
- case *reflect.PtrValue:
+ for v.IsValid() {
+ switch av := v; av.Kind() {
+ case reflect.Ptr:
v = av.Elem()
- case *reflect.InterfaceValue:
+ case reflect.Interface:
v = av.Elem()
default:
break loop
@@ -709,8 +708,8 @@ func (t *Template) findVar(st *state, s string) reflect.Value {
for _, elem := range strings.Split(s, ".", -1) {
// Look up field; data must be a struct or map.
data = t.lookup(st, data, elem)
- if data == nil {
- return nil
+ if !data.IsValid() {
+ return reflect.Value{}
}
}
return indirectPtr(data, numStars)
@@ -719,21 +718,21 @@ func (t *Template) findVar(st *state, s string) reflect.Value {
// Is there no data to look at?
func empty(v reflect.Value) bool {
v = indirect(v)
- if v == nil {
+ if !v.IsValid() {
return true
}
- switch v := v.(type) {
- case *reflect.BoolValue:
- return v.Get() == false
- case *reflect.StringValue:
- return v.Get() == ""
- case *reflect.StructValue:
+ switch v.Kind() {
+ case reflect.Bool:
+ return v.Bool() == false
+ case reflect.String:
+ return v.String() == ""
+ case reflect.Struct:
return false
- case *reflect.MapValue:
+ case reflect.Map:
return false
- case *reflect.ArrayValue:
+ case reflect.Array:
return v.Len() == 0
- case *reflect.SliceValue:
+ case reflect.Slice:
return v.Len() == 0
}
return false
@@ -742,7 +741,7 @@ func empty(v reflect.Value) bool {
// Look up a variable or method, up through the parent if necessary.
func (t *Template) varValue(name string, st *state) reflect.Value {
field := t.findVar(st, name)
- if field == nil {
+ if !field.IsValid() {
if st.parent == nil {
t.execError(st, t.linenum, "name not found: %s in type %s", name, st.data.Type())
}
@@ -813,7 +812,7 @@ func (t *Template) execute(start, end int, st *state) {
func (t *Template) executeSection(s *sectionElement, st *state) {
// Find driver data for this section. It must be in the current struct.
field := t.varValue(s.field, st)
- if field == nil {
+ if !field.IsValid() {
t.execError(st, s.linenum, ".section: cannot find field %s in %s", s.field, st.data.Type())
}
st = st.clone(field)
@@ -836,29 +835,30 @@ func (t *Template) executeSection(s *sectionElement, st *state) {
}
// Return the result of calling the Iter method on v, or nil.
-func iter(v reflect.Value) *reflect.ChanValue {
+func iter(v reflect.Value) reflect.Value {
for j := 0; j < v.Type().NumMethod(); j++ {
mth := v.Type().Method(j)
fv := v.Method(j)
- ft := fv.Type().(*reflect.FuncType)
+ ft := fv.Type()
// TODO(rsc): NumIn() should return 0 here, because ft is from a curried FuncValue.
if mth.Name != "Iter" || ft.NumIn() != 1 || ft.NumOut() != 1 {
continue
}
- ct, ok := ft.Out(0).(*reflect.ChanType)
- if !ok || ct.Dir()&reflect.RecvDir == 0 {
+ ct := ft.Out(0)
+ if ct.Kind() != reflect.Chan ||
+ ct.ChanDir()&reflect.RecvDir == 0 {
continue
}
- return fv.Call(nil)[0].(*reflect.ChanValue)
+ return fv.Call(nil)[0]
}
- return nil
+ return reflect.Value{}
}
// Execute a .repeated section
func (t *Template) executeRepeated(r *repeatedElement, st *state) {
// Find driver data for this section. It must be in the current struct.
field := t.varValue(r.field, st)
- if field == nil {
+ if !field.IsValid() {
t.execError(st, r.linenum, ".repeated: cannot find field %s in %s", r.field, st.data.Type())
}
field = indirect(field)
@@ -886,18 +886,18 @@ func (t *Template) executeRepeated(r *repeatedElement, st *state) {
}
}
- if array, ok := field.(reflect.ArrayOrSliceValue); ok {
+ if array := field; array.Kind() == reflect.Array || array.Kind() == reflect.Slice {
for j := 0; j < array.Len(); j++ {
- loopBody(st.clone(array.Elem(j)))
+ loopBody(st.clone(array.Index(j)))
}
- } else if m, ok := field.(*reflect.MapValue); ok {
- for _, key := range m.Keys() {
- loopBody(st.clone(m.Elem(key)))
+ } else if m := field; m.Kind() == reflect.Map {
+ for _, key := range m.MapKeys() {
+ loopBody(st.clone(m.MapIndex(key)))
}
- } else if ch := iter(field); ch != nil {
+ } else if ch := iter(field); ch.IsValid() {
for {
- e := ch.Recv()
- if ch.Closed() {
+ e, ok := ch.Recv()
+ if !ok {
break
}
loopBody(st.clone(e))
diff --git a/src/pkg/testing/quick/quick.go b/src/pkg/testing/quick/quick.go
index a5568b048..52fd38d9c 100644
--- a/src/pkg/testing/quick/quick.go
+++ b/src/pkg/testing/quick/quick.go
@@ -53,96 +53,93 @@ const complexSize = 50
// If the type implements the Generator interface, that will be used.
// Note: in order to create arbitrary values for structs, all the members must be public.
func Value(t reflect.Type, rand *rand.Rand) (value reflect.Value, ok bool) {
- if m, ok := reflect.MakeZero(t).Interface().(Generator); ok {
+ if m, ok := reflect.Zero(t).Interface().(Generator); ok {
return m.Generate(rand, complexSize), true
}
- switch concrete := t.(type) {
- case *reflect.BoolType:
+ switch concrete := t; concrete.Kind() {
+ case reflect.Bool:
return reflect.NewValue(rand.Int()&1 == 0), true
- case *reflect.FloatType, *reflect.IntType, *reflect.UintType, *reflect.ComplexType:
- switch t.Kind() {
- case reflect.Float32:
- return reflect.NewValue(randFloat32(rand)), true
- case reflect.Float64:
- return reflect.NewValue(randFloat64(rand)), true
- case reflect.Complex64:
- return reflect.NewValue(complex(randFloat32(rand), randFloat32(rand))), true
- case reflect.Complex128:
- return reflect.NewValue(complex(randFloat64(rand), randFloat64(rand))), true
- case reflect.Int16:
- return reflect.NewValue(int16(randInt64(rand))), true
- case reflect.Int32:
- return reflect.NewValue(int32(randInt64(rand))), true
- case reflect.Int64:
- return reflect.NewValue(randInt64(rand)), true
- case reflect.Int8:
- return reflect.NewValue(int8(randInt64(rand))), true
- case reflect.Int:
- return reflect.NewValue(int(randInt64(rand))), true
- case reflect.Uint16:
- return reflect.NewValue(uint16(randInt64(rand))), true
- case reflect.Uint32:
- return reflect.NewValue(uint32(randInt64(rand))), true
- case reflect.Uint64:
- return reflect.NewValue(uint64(randInt64(rand))), true
- case reflect.Uint8:
- return reflect.NewValue(uint8(randInt64(rand))), true
- case reflect.Uint:
- return reflect.NewValue(uint(randInt64(rand))), true
- case reflect.Uintptr:
- return reflect.NewValue(uintptr(randInt64(rand))), true
- }
- case *reflect.MapType:
+ case reflect.Float32:
+ return reflect.NewValue(randFloat32(rand)), true
+ case reflect.Float64:
+ return reflect.NewValue(randFloat64(rand)), true
+ case reflect.Complex64:
+ return reflect.NewValue(complex(randFloat32(rand), randFloat32(rand))), true
+ case reflect.Complex128:
+ return reflect.NewValue(complex(randFloat64(rand), randFloat64(rand))), true
+ case reflect.Int16:
+ return reflect.NewValue(int16(randInt64(rand))), true
+ case reflect.Int32:
+ return reflect.NewValue(int32(randInt64(rand))), true
+ case reflect.Int64:
+ return reflect.NewValue(randInt64(rand)), true
+ case reflect.Int8:
+ return reflect.NewValue(int8(randInt64(rand))), true
+ case reflect.Int:
+ return reflect.NewValue(int(randInt64(rand))), true
+ case reflect.Uint16:
+ return reflect.NewValue(uint16(randInt64(rand))), true
+ case reflect.Uint32:
+ return reflect.NewValue(uint32(randInt64(rand))), true
+ case reflect.Uint64:
+ return reflect.NewValue(uint64(randInt64(rand))), true
+ case reflect.Uint8:
+ return reflect.NewValue(uint8(randInt64(rand))), true
+ case reflect.Uint:
+ return reflect.NewValue(uint(randInt64(rand))), true
+ case reflect.Uintptr:
+ return reflect.NewValue(uintptr(randInt64(rand))), true
+ case reflect.Map:
numElems := rand.Intn(complexSize)
m := reflect.MakeMap(concrete)
for i := 0; i < numElems; i++ {
key, ok1 := Value(concrete.Key(), rand)
value, ok2 := Value(concrete.Elem(), rand)
if !ok1 || !ok2 {
- return nil, false
+ return reflect.Value{}, false
}
- m.SetElem(key, value)
+ m.SetMapIndex(key, value)
}
return m, true
- case *reflect.PtrType:
+ case reflect.Ptr:
v, ok := Value(concrete.Elem(), rand)
if !ok {
- return nil, false
+ return reflect.Value{}, false
}
- p := reflect.MakeZero(concrete)
- p.(*reflect.PtrValue).PointTo(v)
+ p := reflect.Zero(concrete)
+ p.Set(v.Addr())
return p, true
- case *reflect.SliceType:
+ case reflect.Slice:
numElems := rand.Intn(complexSize)
s := reflect.MakeSlice(concrete, numElems, numElems)
for i := 0; i < numElems; i++ {
v, ok := Value(concrete.Elem(), rand)
if !ok {
- return nil, false
+ return reflect.Value{}, false
}
- s.Elem(i).SetValue(v)
+ s.Index(i).Set(v)
}
return s, true
- case *reflect.StringType:
+ case reflect.String:
numChars := rand.Intn(complexSize)
codePoints := make([]int, numChars)
for i := 0; i < numChars; i++ {
codePoints[i] = rand.Intn(0x10ffff)
}
return reflect.NewValue(string(codePoints)), true
- case *reflect.StructType:
- s := reflect.MakeZero(t).(*reflect.StructValue)
+ case reflect.Struct:
+ s := reflect.Zero(t)
for i := 0; i < s.NumField(); i++ {
v, ok := Value(concrete.Field(i).Type, rand)
if !ok {
- return nil, false
+ return reflect.Value{}, false
}
- s.Field(i).SetValue(v)
+ s.Field(i).Set(v)
}
return s, true
default:
- return nil, false
+ return reflect.Value{}, false
}
return
@@ -247,7 +244,7 @@ func Check(function interface{}, config *Config) (err os.Error) {
err = SetupError("function returns more than one value.")
return
}
- if _, ok := fType.Out(0).(*reflect.BoolType); !ok {
+ if fType.Out(0).Kind() != reflect.Bool {
err = SetupError("function does not return a bool")
return
}
@@ -262,7 +259,7 @@ func Check(function interface{}, config *Config) (err os.Error) {
return
}
- if !f.Call(arguments)[0].(*reflect.BoolValue).Get() {
+ if !f.Call(arguments)[0].Bool() {
err = &CheckError{i + 1, toInterfaces(arguments)}
return
}
@@ -320,7 +317,7 @@ func CheckEqual(f, g interface{}, config *Config) (err os.Error) {
// arbitraryValues writes Values to args such that args contains Values
// suitable for calling f.
-func arbitraryValues(args []reflect.Value, f *reflect.FuncType, config *Config, rand *rand.Rand) (err os.Error) {
+func arbitraryValues(args []reflect.Value, f reflect.Type, config *Config, rand *rand.Rand) (err os.Error) {
if config.Values != nil {
config.Values(args, rand)
return
@@ -338,12 +335,13 @@ func arbitraryValues(args []reflect.Value, f *reflect.FuncType, config *Config,
return
}
-func functionAndType(f interface{}) (v *reflect.FuncValue, t *reflect.FuncType, ok bool) {
- v, ok = reflect.NewValue(f).(*reflect.FuncValue)
+func functionAndType(f interface{}) (v reflect.Value, t reflect.Type, ok bool) {
+ v = reflect.NewValue(f)
+ ok = v.Kind() == reflect.Func
if !ok {
return
}
- t = v.Type().(*reflect.FuncType)
+ t = v.Type()
return
}
diff --git a/src/pkg/testing/script/script.go b/src/pkg/testing/script/script.go
index 11f5a7425..b18018497 100644
--- a/src/pkg/testing/script/script.go
+++ b/src/pkg/testing/script/script.go
@@ -134,16 +134,16 @@ type empty struct {
}
func newEmptyInterface(e empty) reflect.Value {
- return reflect.NewValue(e).(*reflect.StructValue).Field(0)
+ return reflect.NewValue(e).Field(0)
}
func (s Send) send() {
// With reflect.ChanValue.Send, we must match the types exactly. So, if
// s.Channel is a chan interface{} we convert s.Value to an interface{}
// first.
- c := reflect.NewValue(s.Channel).(*reflect.ChanValue)
+ c := reflect.NewValue(s.Channel)
var v reflect.Value
- if iface, ok := c.Type().(*reflect.ChanType).Elem().(*reflect.InterfaceType); ok && iface.NumMethod() == 0 {
+ if iface := c.Type().Elem(); iface.Kind() == reflect.Interface && iface.NumMethod() == 0 {
v = newEmptyInterface(empty{s.Value})
} else {
v = reflect.NewValue(s.Value)
@@ -162,7 +162,7 @@ func (s Close) getSend() sendAction { return s }
func (s Close) getChannel() interface{} { return s.Channel }
-func (s Close) send() { reflect.NewValue(s.Channel).(*reflect.ChanValue).Close() }
+func (s Close) send() { reflect.NewValue(s.Channel).Close() }
// A ReceivedUnexpected error results if no active Events match a value
// received from a channel.
@@ -278,7 +278,7 @@ func getChannels(events []*Event) ([]interface{}, os.Error) {
continue
}
c := event.action.getChannel()
- if _, ok := reflect.NewValue(c).(*reflect.ChanValue); !ok {
+ if reflect.NewValue(c).Kind() != reflect.Chan {
return nil, SetupError("one of the channel values is not a channel")
}
@@ -303,11 +303,11 @@ func getChannels(events []*Event) ([]interface{}, os.Error) {
// channel repeatedly, wrapping them up as either a channelRecv or
// channelClosed structure, and forwards them to the multiplex channel.
func recvValues(multiplex chan<- interface{}, channel interface{}) {
- c := reflect.NewValue(channel).(*reflect.ChanValue)
+ c := reflect.NewValue(channel)
for {
- v := c.Recv()
- if c.Closed() {
+ v, ok := c.Recv()
+ if !ok {
multiplex <- channelClosed{channel}
return
}
diff --git a/src/pkg/testing/testing.go b/src/pkg/testing/testing.go
index 324b5a70e..1e65528ef 100644
--- a/src/pkg/testing/testing.go
+++ b/src/pkg/testing/testing.go
@@ -43,12 +43,31 @@ import (
"fmt"
"os"
"runtime"
+ "runtime/pprof"
"time"
)
-// Report as tests are run; default is silent for success.
-var chatty = flag.Bool("test.v", false, "verbose: print additional output")
-var match = flag.String("test.run", "", "regular expression to select tests to run")
+var (
+ // The short flag requests that tests run more quickly, but its functionality
+ // is provided by test writers themselves. The testing package is just its
+ // home. The all.bash installation script sets it to make installation more
+ // efficient, but by default the flag is off so a plain "gotest" will do a
+ // full test of the package.
+ short = flag.Bool("test.short", false, "run smaller test suite to save time")
+
+ // Report as tests are run; default is silent for success.
+ chatty = flag.Bool("test.v", false, "verbose: print additional output")
+ match = flag.String("test.run", "", "regular expression to select tests to run")
+ memProfile = flag.String("test.memprofile", "", "write a memory profile to the named file after execution")
+ memProfileRate = flag.Int("test.memprofilerate", 0, "if >=0, sets runtime.MemProfileRate")
+ cpuProfile = flag.String("test.cpuprofile", "", "write a cpu profile to the named file during execution")
+ timeout = flag.Int64("test.timeout", 0, "if > 0, sets time limit for tests in seconds")
+)
+
+// Short reports whether the -test.short flag is set.
+func Short() bool {
+ return *short
+}
// Insert final newline if needed and tabs after internal newlines.
@@ -136,8 +155,18 @@ func tRunner(t *T, test *InternalTest) {
// An internal function but exported because it is cross-package; part of the implementation
// of gotest.
-func Main(matchString func(pat, str string) (bool, os.Error), tests []InternalTest) {
+func Main(matchString func(pat, str string) (bool, os.Error), tests []InternalTest, benchmarks []InternalBenchmark) {
flag.Parse()
+
+ before()
+ startAlarm()
+ RunTests(matchString, tests)
+ stopAlarm()
+ RunBenchmarks(matchString, benchmarks)
+ after()
+}
+
+func RunTests(matchString func(pat, str string) (bool, os.Error), tests []InternalTest) {
ok := true
if len(tests) == 0 {
println("testing: warning: no tests to run")
@@ -160,7 +189,7 @@ func Main(matchString func(pat, str string) (bool, os.Error), tests []InternalTe
go tRunner(t, &tests[i])
<-t.ch
ns += time.Nanoseconds()
- tstr := fmt.Sprintf("(%.1f seconds)", float64(ns)/1e9)
+ tstr := fmt.Sprintf("(%.2f seconds)", float64(ns)/1e9)
if t.failed {
println("--- FAIL:", tests[i].Name, tstr)
print(t.errors)
@@ -176,3 +205,63 @@ func Main(matchString func(pat, str string) (bool, os.Error), tests []InternalTe
}
println("PASS")
}
+
+// before runs before all testing.
+func before() {
+ if *memProfileRate > 0 {
+ runtime.MemProfileRate = *memProfileRate
+ }
+ if *cpuProfile != "" {
+ f, err := os.Create(*cpuProfile)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "testing: %s", err)
+ return
+ }
+ if err := pprof.StartCPUProfile(f); err != nil {
+ fmt.Fprintf(os.Stderr, "testing: can't start cpu profile: %s", err)
+ f.Close()
+ return
+ }
+ // Could save f so after can call f.Close; not worth the effort.
+ }
+
+}
+
+// after runs after all testing.
+func after() {
+ if *cpuProfile != "" {
+ pprof.StopCPUProfile() // flushes profile to disk
+ }
+ if *memProfile != "" {
+ f, err := os.Create(*memProfile)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "testing: %s", err)
+ return
+ }
+ if err = pprof.WriteHeapProfile(f); err != nil {
+ fmt.Fprintf(os.Stderr, "testing: can't write %s: %s", *memProfile, err)
+ }
+ f.Close()
+ }
+}
+
+var timer *time.Timer
+
+// startAlarm starts an alarm if requested.
+func startAlarm() {
+ if *timeout > 0 {
+ timer = time.AfterFunc(*timeout*1e9, alarm)
+ }
+}
+
+// stopAlarm turns off the alarm.
+func stopAlarm() {
+ if *timeout > 0 {
+ timer.Stop()
+ }
+}
+
+// alarm is called if the timeout expires.
+func alarm() {
+ panic("test timed out")
+}
diff --git a/src/pkg/time/sleep.go b/src/pkg/time/sleep.go
index 7b3f01f01..3bc253c94 100644
--- a/src/pkg/time/sleep.go
+++ b/src/pkg/time/sleep.go
@@ -5,9 +5,8 @@
package time
import (
- "syscall"
- "sync"
"container/heap"
+ "sync"
)
// The Timer type represents a single event.
@@ -126,7 +125,7 @@ func sleeper(sleeperId int64) {
dt = maxSleepTime
}
timerMutex.Unlock()
- syscall.Sleep(dt)
+ sysSleep(dt)
timerMutex.Lock()
if currentSleeper != sleeperId {
// Another sleeper has been started, making this one redundant.
diff --git a/src/pkg/time/sleep_test.go b/src/pkg/time/sleep_test.go
index 8bf599c3e..25e79f9fb 100644
--- a/src/pkg/time/sleep_test.go
+++ b/src/pkg/time/sleep_test.go
@@ -5,6 +5,7 @@
package time_test
import (
+ "fmt"
"os"
"syscall"
"testing"
@@ -132,6 +133,21 @@ func TestAfterStop(t *testing.T) {
}
}
+func TestAfterQueuing(t *testing.T) {
+ // This test flakes out on some systems,
+ // so we'll try it a few times before declaring it a failure.
+ const attempts = 3
+ err := os.NewError("!=nil")
+ for i := 0; i < attempts && err != nil; i++ {
+ if err = testAfterQueuing(t); err != nil {
+ t.Logf("attempt %v failed: %v", i, err)
+ }
+ }
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
var slots = []int{5, 3, 6, 6, 6, 1, 1, 2, 7, 9, 4, 8, 0}
type afterResult struct {
@@ -143,7 +159,7 @@ func await(slot int, result chan<- afterResult, ac <-chan int64) {
result <- afterResult{slot, <-ac}
}
-func TestAfterQueuing(t *testing.T) {
+func testAfterQueuing(t *testing.T) os.Error {
const (
Delta = 100 * 1e6
)
@@ -160,13 +176,14 @@ func TestAfterQueuing(t *testing.T) {
for _, slot := range slots {
r := <-result
if r.slot != slot {
- t.Fatalf("after queue got slot %d, expected %d", r.slot, slot)
+ return fmt.Errorf("after queue got slot %d, expected %d", r.slot, slot)
}
ns := r.t - t0
target := int64(slot * Delta)
slop := int64(Delta) / 4
if ns < target-slop || ns > target+slop {
- t.Fatalf("after queue slot %d arrived at %g, expected [%g,%g]", slot, float64(ns), float64(target-slop), float64(target+slop))
+ return fmt.Errorf("after queue slot %d arrived at %g, expected [%g,%g]", slot, float64(ns), float64(target-slop), float64(target+slop))
}
}
+ return nil
}
diff --git a/src/pkg/time/sys.go b/src/pkg/time/sys.go
index 8a2e6fadc..63f4cbf3d 100644
--- a/src/pkg/time/sys.go
+++ b/src/pkg/time/sys.go
@@ -44,11 +44,19 @@ func sleep(t, ns int64) (int64, os.Error) {
// TODO(cw): use monotonic-time once it's available
end := t + ns
for t < end {
- errno := syscall.Sleep(end - t)
- if errno != 0 && errno != syscall.EINTR {
- return 0, os.NewSyscallError("sleep", errno)
+ err := sysSleep(end - t)
+ if err != nil {
+ return 0, err
}
t = Nanoseconds()
}
return t, nil
}
+
+func sysSleep(t int64) os.Error {
+ errno := syscall.Sleep(t)
+ if errno != 0 && errno != syscall.EINTR {
+ return os.NewSyscallError("sleep", errno)
+ }
+ return nil
+}
diff --git a/src/pkg/time/time.go b/src/pkg/time/time.go
index 432b3523a..40338f775 100644
--- a/src/pkg/time/time.go
+++ b/src/pkg/time/time.go
@@ -23,7 +23,7 @@ type Time struct {
Month, Day int // Jan-2 is 1, 2
Hour, Minute, Second int // 15:04:05 is 15, 4, 5.
Weekday int // Sunday, Monday, ...
- ZoneOffset int // seconds east of UTC, e.g. -7*60 for -0700
+ ZoneOffset int // seconds east of UTC, e.g. -7*60*60 for -0700
Zone string // e.g., "MST"
}
diff --git a/src/pkg/time/time_test.go b/src/pkg/time/time_test.go
index c86bca1b4..1d83291c0 100644
--- a/src/pkg/time/time_test.go
+++ b/src/pkg/time/time_test.go
@@ -19,6 +19,18 @@ func init() {
os.Setenv("TZ", "America/Los_Angeles")
}
+// We should be in PST/PDT, but if the time zone files are missing we
+// won't be. The purpose of this test is to at least explain why some of
+// the subsequent tests fail.
+func TestZoneData(t *testing.T) {
+ lt := LocalTime()
+ // PST is 8 hours west, PDT is 7 hours west. We could use the name but it's not unique.
+ if off := lt.ZoneOffset; off != -8*60*60 && off != -7*60*60 {
+ t.Errorf("Unable to find US Pacific time zone data for testing; time zone is %q offset %d", lt.Zone, off)
+ t.Error("Likely problem: the time zone files have not been installed.")
+ }
+}
+
type TimeTest struct {
seconds int64
golden Time
diff --git a/src/pkg/try/try.go b/src/pkg/try/try.go
index af31d0d2c..1171c80c2 100644
--- a/src/pkg/try/try.go
+++ b/src/pkg/try/try.go
@@ -90,13 +90,13 @@ func tryMethod(pkg, firstArg string, method reflect.Method, args []interface{})
// tryFunction sees if fn satisfies the arguments.
func tryFunction(pkg, name string, fn interface{}, args []interface{}) {
defer func() { recover() }()
- rfn := reflect.NewValue(fn).(*reflect.FuncValue)
- typ := rfn.Type().(*reflect.FuncType)
+ rfn := reflect.NewValue(fn)
+ typ := rfn.Type()
tryOneFunction(pkg, "", name, typ, rfn, args)
}
// tryOneFunction is the common code for tryMethod and tryFunction.
-func tryOneFunction(pkg, firstArg, name string, typ *reflect.FuncType, rfn *reflect.FuncValue, args []interface{}) {
+func tryOneFunction(pkg, firstArg, name string, typ reflect.Type, rfn reflect.Value, args []interface{}) {
// Any results?
if typ.NumOut() == 0 {
return // Nothing to do.
@@ -166,7 +166,7 @@ func compatible(arg interface{}, typ reflect.Type) bool {
}
if arg == nil {
// nil is OK if the type is an interface.
- if _, ok := typ.(*reflect.InterfaceType); ok {
+ if typ.Kind() == reflect.Interface {
return true
}
}
diff --git a/src/pkg/utf8/string_test.go b/src/pkg/utf8/string_test.go
index 9dd847247..f376b628c 100644
--- a/src/pkg/utf8/string_test.go
+++ b/src/pkg/utf8/string_test.go
@@ -45,7 +45,12 @@ func TestScanBackwards(t *testing.T) {
}
}
-const randCount = 100000
+func randCount() int {
+ if testing.Short() {
+ return 100
+ }
+ return 100000
+}
func TestRandomAccess(t *testing.T) {
for _, s := range testStrings {
@@ -58,7 +63,7 @@ func TestRandomAccess(t *testing.T) {
t.Errorf("%s: expected %d runes; got %d", s, len(runes), str.RuneCount())
break
}
- for j := 0; j < randCount; j++ {
+ for j := 0; j < randCount(); j++ {
i := rand.Intn(len(runes))
expect := runes[i]
got := str.At(i)
@@ -80,7 +85,7 @@ func TestRandomSliceAccess(t *testing.T) {
t.Errorf("%s: expected %d runes; got %d", s, len(runes), str.RuneCount())
break
}
- for k := 0; k < randCount; k++ {
+ for k := 0; k < randCount(); k++ {
i := rand.Intn(len(runes))
j := rand.Intn(len(runes) + 1)
if i > j { // include empty strings
diff --git a/src/pkg/websocket/client.go b/src/pkg/websocket/client.go
index d8a7aa0a2..78c8b7f57 100644
--- a/src/pkg/websocket/client.go
+++ b/src/pkg/websocket/client.go
@@ -108,10 +108,10 @@ func Dial(url, protocol, origin string) (ws *Conn, err os.Error) {
switch parsedUrl.Scheme {
case "ws":
- client, err = net.Dial("tcp", "", parsedUrl.Host)
+ client, err = net.Dial("tcp", parsedUrl.Host)
case "wss":
- client, err = tls.Dial("tcp", "", parsedUrl.Host, nil)
+ client, err = tls.Dial("tcp", parsedUrl.Host, nil)
default:
err = ErrBadScheme
diff --git a/src/pkg/websocket/server.go b/src/pkg/websocket/server.go
index 37149f044..1119b2d34 100644
--- a/src/pkg/websocket/server.go
+++ b/src/pkg/websocket/server.go
@@ -98,7 +98,7 @@ func (f Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
var location string
- if w.UsingTLS() {
+ if req.TLS != nil {
location = "wss://" + req.Host + req.URL.RawPath
} else {
location = "ws://" + req.Host + req.URL.RawPath
@@ -192,7 +192,7 @@ func (f Draft75Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
defer rwc.Close()
var location string
- if w.UsingTLS() {
+ if req.TLS != nil {
location = "wss://" + req.Host + req.URL.RawPath
} else {
location = "ws://" + req.Host + req.URL.RawPath
diff --git a/src/pkg/websocket/websocket_test.go b/src/pkg/websocket/websocket_test.go
index 14d708a3b..8b3cf8925 100644
--- a/src/pkg/websocket/websocket_test.go
+++ b/src/pkg/websocket/websocket_test.go
@@ -53,7 +53,7 @@ func TestEcho(t *testing.T) {
once.Do(startServer)
// websocket.Dial()
- client, err := net.Dial("tcp", "", serverAddr)
+ client, err := net.Dial("tcp", serverAddr)
if err != nil {
t.Fatal("dialing", err)
}
@@ -84,7 +84,7 @@ func TestEchoDraft75(t *testing.T) {
once.Do(startServer)
// websocket.Dial()
- client, err := net.Dial("tcp", "", serverAddr)
+ client, err := net.Dial("tcp", serverAddr)
if err != nil {
t.Fatal("dialing", err)
}
@@ -114,7 +114,7 @@ func TestEchoDraft75(t *testing.T) {
func TestWithQuery(t *testing.T) {
once.Do(startServer)
- client, err := net.Dial("tcp", "", serverAddr)
+ client, err := net.Dial("tcp", serverAddr)
if err != nil {
t.Fatal("dialing", err)
}
@@ -131,7 +131,7 @@ func TestWithQuery(t *testing.T) {
func TestWithProtocol(t *testing.T) {
once.Do(startServer)
- client, err := net.Dial("tcp", "", serverAddr)
+ client, err := net.Dial("tcp", serverAddr)
if err != nil {
t.Fatal("dialing", err)
}
@@ -200,7 +200,7 @@ func TestSmallBuffer(t *testing.T) {
once.Do(startServer)
// websocket.Dial()
- client, err := net.Dial("tcp", "", serverAddr)
+ client, err := net.Dial("tcp", serverAddr)
if err != nil {
t.Fatal("dialing", err)
}
diff --git a/src/pkg/xml/read.go b/src/pkg/xml/read.go
index 9ae3bb8ee..a3ddb9d4c 100644
--- a/src/pkg/xml/read.go
+++ b/src/pkg/xml/read.go
@@ -139,8 +139,8 @@ import (
// to a freshly allocated value and then mapping the element to that value.
//
func Unmarshal(r io.Reader, val interface{}) os.Error {
- v, ok := reflect.NewValue(val).(*reflect.PtrValue)
- if !ok {
+ v := reflect.NewValue(val)
+ if v.Kind() != reflect.Ptr {
return os.NewError("non-pointer passed to Unmarshal")
}
p := NewParser(r)
@@ -176,8 +176,8 @@ func (e *TagPathError) String() string {
// Passing a nil start element indicates that Unmarshal should
// read the token stream to find the start element.
func (p *Parser) Unmarshal(val interface{}, start *StartElement) os.Error {
- v, ok := reflect.NewValue(val).(*reflect.PtrValue)
- if !ok {
+ v := reflect.NewValue(val)
+ if v.Kind() != reflect.Ptr {
return os.NewError("non-pointer passed to Unmarshal")
}
return p.unmarshal(v.Elem(), start)
@@ -219,10 +219,10 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error {
}
}
- if pv, ok := val.(*reflect.PtrValue); ok {
- if pv.Get() == 0 {
- zv := reflect.MakeZero(pv.Type().(*reflect.PtrType).Elem())
- pv.PointTo(zv)
+ if pv := val; pv.Kind() == reflect.Ptr {
+ if pv.Pointer() == 0 {
+ zv := reflect.Zero(pv.Type().Elem())
+ pv.Set(zv.Addr())
val = zv
} else {
val = pv.Elem()
@@ -237,17 +237,17 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error {
saveXML reflect.Value
saveXMLIndex int
saveXMLData []byte
- sv *reflect.StructValue
- styp *reflect.StructType
+ sv reflect.Value
+ styp reflect.Type
fieldPaths map[string]pathInfo
)
- switch v := val.(type) {
+ switch v := val; v.Kind() {
default:
return os.ErrorString("unknown type " + v.Type().String())
- case *reflect.SliceValue:
- typ := v.Type().(*reflect.SliceType)
+ case reflect.Slice:
+ typ := v.Type()
if typ.Elem().Kind() == reflect.Uint8 {
// []byte
saveData = v
@@ -269,23 +269,23 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error {
v.SetLen(n + 1)
// Recur to read element into slice.
- if err := p.unmarshal(v.Elem(n), start); err != nil {
+ if err := p.unmarshal(v.Index(n), start); err != nil {
v.SetLen(n)
return err
}
return nil
- case *reflect.BoolValue, *reflect.FloatValue, *reflect.IntValue, *reflect.UintValue, *reflect.StringValue:
+ case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.String:
saveData = v
- case *reflect.StructValue:
+ case reflect.Struct:
if _, ok := v.Interface().(Name); ok {
- v.Set(reflect.NewValue(start.Name).(*reflect.StructValue))
+ v.Set(reflect.NewValue(start.Name))
break
}
sv = v
- typ := sv.Type().(*reflect.StructType)
+ typ := sv.Type()
styp = typ
// Assign name.
if f, ok := typ.FieldByName("XMLName"); ok {
@@ -316,7 +316,7 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error {
if _, ok := v.Interface().(Name); !ok {
return UnmarshalError(sv.Type().String() + " field XMLName does not have type xml.Name")
}
- v.(*reflect.StructValue).Set(reflect.NewValue(start.Name).(*reflect.StructValue))
+ v.Set(reflect.NewValue(start.Name))
}
// Assign attributes.
@@ -325,8 +325,8 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error {
f := typ.Field(i)
switch f.Tag {
case "attr":
- strv, ok := sv.FieldByIndex(f.Index).(*reflect.StringValue)
- if !ok {
+ strv := sv.FieldByIndex(f.Index)
+ if strv.Kind() != reflect.String {
return UnmarshalError(sv.Type().String() + " field " + f.Name + " has attr tag but is not type string")
}
// Look for attribute.
@@ -338,20 +338,20 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error {
break
}
}
- strv.Set(val)
+ strv.SetString(val)
case "comment":
- if saveComment == nil {
+ if !saveComment.IsValid() {
saveComment = sv.FieldByIndex(f.Index)
}
case "chardata":
- if saveData == nil {
+ if !saveData.IsValid() {
saveData = sv.FieldByIndex(f.Index)
}
case "innerxml":
- if saveXML == nil {
+ if !saveXML.IsValid() {
saveXML = sv.FieldByIndex(f.Index)
if p.saved == nil {
saveXMLIndex = 0
@@ -387,7 +387,7 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error {
Loop:
for {
var savedOffset int
- if saveXML != nil {
+ if saveXML.IsValid() {
savedOffset = p.savedOffset()
}
tok, err := p.Token()
@@ -398,7 +398,7 @@ Loop:
case StartElement:
// Sub-element.
// Look up by tag name.
- if sv != nil {
+ if sv.IsValid() {
k := fieldName(t.Name.Local)
if fieldPaths != nil {
@@ -437,7 +437,7 @@ Loop:
}
case EndElement:
- if saveXML != nil {
+ if saveXML.IsValid() {
saveXMLData = p.saved.Bytes()[saveXMLIndex:savedOffset]
if saveXMLIndex == 0 {
p.saved = nil
@@ -446,12 +446,12 @@ Loop:
break Loop
case CharData:
- if saveData != nil {
+ if saveData.IsValid() {
data = append(data, t...)
}
case Comment:
- if saveComment != nil {
+ if saveComment.IsValid() {
comment = append(comment, t...)
}
}
@@ -479,50 +479,50 @@ Loop:
}
// Save accumulated data and comments
- switch t := saveData.(type) {
- case nil:
+ switch t := saveData; t.Kind() {
+ case reflect.Invalid:
// Probably a comment, handled below
default:
return os.ErrorString("cannot happen: unknown type " + t.Type().String())
- case *reflect.IntValue:
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if !getInt64() {
return err
}
- t.Set(itmp)
- case *reflect.UintValue:
+ t.SetInt(itmp)
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
if !getUint64() {
return err
}
- t.Set(utmp)
- case *reflect.FloatValue:
+ t.SetUint(utmp)
+ case reflect.Float32, reflect.Float64:
if !getFloat64() {
return err
}
- t.Set(ftmp)
- case *reflect.BoolValue:
+ t.SetFloat(ftmp)
+ case reflect.Bool:
value, err := strconv.Atob(strings.TrimSpace(string(data)))
if err != nil {
return err
}
- t.Set(value)
- case *reflect.StringValue:
- t.Set(string(data))
- case *reflect.SliceValue:
- t.Set(reflect.NewValue(data).(*reflect.SliceValue))
+ t.SetBool(value)
+ case reflect.String:
+ t.SetString(string(data))
+ case reflect.Slice:
+ t.Set(reflect.NewValue(data))
}
- switch t := saveComment.(type) {
- case *reflect.StringValue:
- t.Set(string(comment))
- case *reflect.SliceValue:
- t.Set(reflect.NewValue(comment).(*reflect.SliceValue))
+ switch t := saveComment; t.Kind() {
+ case reflect.String:
+ t.SetString(string(comment))
+ case reflect.Slice:
+ t.Set(reflect.NewValue(comment))
}
- switch t := saveXML.(type) {
- case *reflect.StringValue:
- t.Set(string(saveXMLData))
- case *reflect.SliceValue:
- t.Set(reflect.NewValue(saveXMLData).(*reflect.SliceValue))
+ switch t := saveXML; t.Kind() {
+ case reflect.String:
+ t.SetString(string(saveXMLData))
+ case reflect.Slice:
+ t.Set(reflect.NewValue(saveXMLData))
}
return nil
@@ -537,7 +537,7 @@ type pathInfo struct {
// paths map with all paths leading to it ("a", "a>b", and "a>b>c").
// It is okay for paths to share a common, shorter prefix but not ok
// for one path to itself be a prefix of another.
-func addFieldPath(sv *reflect.StructValue, paths map[string]pathInfo, path string, fieldIdx []int) os.Error {
+func addFieldPath(sv reflect.Value, paths map[string]pathInfo, path string, fieldIdx []int) os.Error {
if info, found := paths[path]; found {
return tagError(sv, info.fieldIdx, fieldIdx)
}
@@ -560,8 +560,8 @@ func addFieldPath(sv *reflect.StructValue, paths map[string]pathInfo, path strin
}
-func tagError(sv *reflect.StructValue, idx1 []int, idx2 []int) os.Error {
- t := sv.Type().(*reflect.StructType)
+func tagError(sv reflect.Value, idx1 []int, idx2 []int) os.Error {
+ t := sv.Type()
f1 := t.FieldByIndex(idx1)
f2 := t.FieldByIndex(idx2)
return &TagPathError{t, f1.Name, f1.Tag, f2.Name, f2.Tag}
@@ -569,7 +569,7 @@ func tagError(sv *reflect.StructValue, idx1 []int, idx2 []int) os.Error {
// unmarshalPaths walks down an XML structure looking for
// wanted paths, and calls unmarshal on them.
-func (p *Parser) unmarshalPaths(sv *reflect.StructValue, paths map[string]pathInfo, path string, start *StartElement) os.Error {
+func (p *Parser) unmarshalPaths(sv reflect.Value, paths map[string]pathInfo, path string, start *StartElement) os.Error {
if info, _ := paths[path]; info.complete {
return p.unmarshal(sv.FieldByIndex(info.fieldIdx), start)
}
diff --git a/src/pkg/xml/read_test.go b/src/pkg/xml/read_test.go
index a6b9a8ed1..0e28e73a6 100644
--- a/src/pkg/xml/read_test.go
+++ b/src/pkg/xml/read_test.go
@@ -288,8 +288,8 @@ var pathTests = []interface{}{
func TestUnmarshalPaths(t *testing.T) {
for _, pt := range pathTests {
- p := reflect.MakeZero(reflect.NewValue(pt).Type()).(*reflect.PtrValue)
- p.PointTo(reflect.MakeZero(p.Type().(*reflect.PtrType).Elem()))
+ p := reflect.Zero(reflect.NewValue(pt).Type())
+ p.Set(reflect.Zero(p.Type().Elem()).Addr())
v := p.Interface()
if err := Unmarshal(StringReader(pathTestString), v); err != nil {
t.Fatalf("Unmarshal: %s", err)
diff --git a/src/pkg/xml/xml.go b/src/pkg/xml/xml.go
index 691c13a11..f92abe825 100644
--- a/src/pkg/xml/xml.go
+++ b/src/pkg/xml/xml.go
@@ -815,7 +815,6 @@ Input:
// Parsers are required to recognize lt, gt, amp, apos, and quot
// even if they have not been declared. That's all we allow.
var i int
- CharLoop:
for i = 0; i < len(p.tmp); i++ {
var ok bool
p.tmp[i], ok = p.getc()
diff --git a/src/quietgcc.bash b/src/quietgcc.bash
index 748fc593e..e29ee4f64 100755
--- a/src/quietgcc.bash
+++ b/src/quietgcc.bash
@@ -35,15 +35,10 @@ esac
tmp=/tmp/qcc.$$.$USER.out
$gcc -Wall -Wno-sign-compare -Wno-missing-braces \
-Wno-parentheses -Wno-unknown-pragmas -Wno-switch -Wno-comment \
+ -Werror \
"$@" >$tmp 2>&1
status=$?
egrep -v "$ignore" $tmp | uniq | tee $tmp.1
-# Make incompatible pointer type "warnings" stop the build.
-# Not quite perfect--we should remove the object file--but
-# a step in the right direction.
-if egrep 'incompatible pointer type' $tmp.1 >/dev/null; then
- status=1
-fi
rm -f $tmp $tmp.1
exit $status
diff --git a/src/run.bash b/src/run.bash
index aec490109..ea98403f7 100755
--- a/src/run.bash
+++ b/src/run.bash
@@ -39,7 +39,7 @@ if $rebuild; then
fi
(xcd pkg
-gomake test
+gomake testshort
) || exit $?
(xcd pkg/sync;
@@ -47,16 +47,7 @@ if $rebuild; then
gomake clean;
time gomake
fi
-GOMAXPROCS=10 gomake test
-) || exit $?
-
-[ "$GOARCH" == arm ] ||
-(xcd cmd/gofmt
-if $rebuild; then
- gomake clean;
- time gomake
-fi
-time gomake smoketest
+GOMAXPROCS=10 gomake testshort
) || exit $?
(xcd cmd/ebnflint
@@ -75,12 +66,18 @@ gomake clean
) || exit $?
[ "$GOARCH" == arm ] ||
-[ "$GOHOSTOS" == windows ] ||
(xcd ../misc/cgo/life
gomake clean
./test.bash
) || exit $?
+[ "$GOARCH" == arm ] ||
+[ "$GOHOSTOS" == windows ] ||
+(xcd ../misc/cgo/test
+gomake clean
+gotest
+) || exit $?
+
(xcd pkg/exp/ogle
gomake clean
time gomake ogle
@@ -91,6 +88,7 @@ time gomake ogle
time ./run
) || exit $?
+[ "$GOARCH" == arm ] || # uses network, fails under QEMU
(xcd ../doc/codelab/wiki
gomake clean
gomake
@@ -106,7 +104,6 @@ do
done
[ "$GOARCH" == arm ] ||
-[ "$GOHOSTOS" == windows ] ||
(xcd ../test/bench
./timing.sh -test
) || exit $?
diff --git a/src/version.bash b/src/version.bash
index 0e6483150..b45f15a6c 100755
--- a/src/version.bash
+++ b/src/version.bash
@@ -17,7 +17,11 @@ if [ $? != 0 ]; then
fi
# Find most recent known release tag.
-TAG=$(hg tags | awk '$1~/^release\./ {print $1}' | sed -n 1p)
+TAG=$(hg tags |
+ sed 's/:.*//' |
+ sort -rn -k2 |
+ awk -v ver=$VERSION '$2 <= ver && $1~/^(release|weekly)\./ {print $1}' |
+ sed -n 1p)
if [ "$TAG" != "" ]; then
VERSION="$TAG $VERSION"
diff --git a/test/bench/regex-dna-parallel.go b/test/bench/regex-dna-parallel.go
index e8e62b806..1335e4d34 100644
--- a/test/bench/regex-dna-parallel.go
+++ b/test/bench/regex-dna-parallel.go
@@ -89,7 +89,7 @@ func countMatches(pat string, bytes []byte) int {
func main() {
runtime.GOMAXPROCS(4)
- bytes, err := ioutil.ReadFile("/dev/stdin")
+ bytes, err := ioutil.ReadAll(os.Stdin)
if err != nil {
fmt.Fprintf(os.Stderr, "can't read input: %s\n", err)
os.Exit(2)
diff --git a/test/bench/regex-dna.go b/test/bench/regex-dna.go
index dc31db768..042d7f283 100644
--- a/test/bench/regex-dna.go
+++ b/test/bench/regex-dna.go
@@ -87,7 +87,7 @@ func countMatches(pat string, bytes []byte) int {
}
func main() {
- bytes, err := ioutil.ReadFile("/dev/stdin")
+ bytes, err := ioutil.ReadAll(os.Stdin)
if err != nil {
fmt.Fprintf(os.Stderr, "can't read input: %s\n", err)
os.Exit(2)
diff --git a/test/bugs/bug322.dir/main.go b/test/bugs/bug322.dir/main.go
index a99ed3bc2..0ab5b32e4 100644
--- a/test/bugs/bug322.dir/main.go
+++ b/test/bugs/bug322.dir/main.go
@@ -19,8 +19,9 @@ func main() {
t.M()
t.PM()
- var i1 I = t
- i1.M()
+ // This is still an error.
+ // var i1 I = t
+ // i1.M()
// This combination is illegal because
// PM requires a pointer receiver.
@@ -42,6 +43,5 @@ func main() {
These should not be errors anymore:
bug322.dir/main.go:19: implicit assignment of unexported field 'x' of lib.T in method receiver
-bug322.dir/main.go:22: implicit assignment of unexported field 'x' of lib.T in assignment
-bug322.dir/main.go:31: implicit assignment of unexported field 'x' of lib.T in method receiver
-*/ \ No newline at end of file
+bug322.dir/main.go:32: implicit assignment of unexported field 'x' of lib.T in method receiver
+*/
diff --git a/test/bugs/bug324.dir/main.go b/test/bugs/bug324.dir/main.go
index 37f2a59e4..4c1a18d9c 100644
--- a/test/bugs/bug324.dir/main.go
+++ b/test/bugs/bug324.dir/main.go
@@ -40,7 +40,7 @@ func main() {
// x = px
// this assignment unexpectedly compiles and then executes
- x = px.(Exported) // ERROR "does not implement"
+ x = px.(Exported)
// this is a legitimate call, but because of the previous assignment,
// it invokes the method private in p!
diff --git a/test/bugs/bug324.go b/test/bugs/bug324.go
index 8b4e29200..e188515d7 100644
--- a/test/bugs/bug324.go
+++ b/test/bugs/bug324.go
@@ -1,4 +1,4 @@
-// $G $D/$F.dir/p.go && errchk $G $D/$F.dir/main.go
+// $G $D/$F.dir/p.go && $G $D/$F.dir/main.go && $L main.$A && ! ./$A.out || echo BUG: should fail
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
diff --git a/test/chan/perm.go b/test/chan/perm.go
index c725829d1..038ff94e3 100644
--- a/test/chan/perm.go
+++ b/test/chan/perm.go
@@ -22,21 +22,18 @@ func main() {
c <- 0 // ok
<-c // ok
- //TODO(rsc): uncomment when this syntax is valid for receive+check closed
- // x, ok := <-c // ok
- // _, _ = x, ok
+ x, ok := <-c // ok
+ _, _ = x, ok
cr <- 0 // ERROR "send"
<-cr // ok
- //TODO(rsc): uncomment when this syntax is valid for receive+check closed
- // x, ok = <-cr // ok
- // _, _ = x, ok
+ x, ok = <-cr // ok
+ _, _ = x, ok
cs <- 0 // ok
<-cs // ERROR "receive"
- ////TODO(rsc): uncomment when this syntax is valid for receive+check closed
- //// x, ok = <-cs // ERROR "receive"
- //// _, _ = x, ok
+ x, ok = <-cs // ERROR "receive"
+ _, _ = x, ok
select {
case c <- 0: // ok
diff --git a/test/chan/select3.go b/test/chan/select3.go
index 47941063c..b4e8f8e4b 100644
--- a/test/chan/select3.go
+++ b/test/chan/select3.go
@@ -88,12 +88,16 @@ func main() {
ch <- 7
})
- // receiving (a small number of times) from a closed channel never blocks
+ // receiving from a closed channel never blocks
testBlock(never, func() {
for i := 0; i < 10; i++ {
if <-closedch != 0 {
panic("expected zero value when reading from closed channel")
}
+ if x, ok := <-closedch; x != 0 || ok {
+ println("closedch:", x, ok)
+ panic("expected 0, false from closed channel")
+ }
}
})
@@ -191,12 +195,24 @@ func main() {
case <-closedch:
}
})
+ testBlock(never, func() {
+ select {
+ case x := <-closedch:
+ _ = x
+ }
+ })
+ testBlock(never, func() {
+ select {
+ case x, ok := <-closedch:
+ _, _ = x, ok
+ }
+ })
testPanic(always, func() {
select {
case closedch <- 7:
}
})
-
+
// select should not get confused if it sees itself
testBlock(always, func() {
c := make(chan int)
diff --git a/test/closedchan.go b/test/closedchan.go
index 46d9d0f5d..95314b334 100644
--- a/test/closedchan.go
+++ b/test/closedchan.go
@@ -4,7 +4,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Test close(c), closed(c).
+// Test close(c), receive of closed channel.
//
// TODO(rsc): Doesn't check behavior of close(c) when there
// are blocked senders/receivers.
@@ -14,10 +14,11 @@ package main
type Chan interface {
Send(int)
Nbsend(int) bool
- Recv() int
+ Recv() (int)
Nbrecv() (int, bool)
+ Recv2() (int, bool)
+ Nbrecv2() (int, bool, bool)
Close()
- Closed() bool
Impl() string
}
@@ -52,12 +53,23 @@ func (c XChan) Nbrecv() (int, bool) {
panic("nbrecv")
}
-func (c XChan) Close() {
- close(c)
+func (c XChan) Recv2() (int, bool) {
+ x, ok := <-c
+ return x, ok
+}
+
+func (c XChan) Nbrecv2() (int, bool, bool) {
+ select {
+ case x, ok := <-c:
+ return x, ok, true
+ default:
+ return 0, false, false
+ }
+ panic("nbrecv2")
}
-func (c XChan) Closed() bool {
- return closed(c)
+func (c XChan) Close() {
+ close(c)
}
func (c XChan) Impl() string {
@@ -101,12 +113,26 @@ func (c SChan) Nbrecv() (int, bool) {
panic("nbrecv")
}
-func (c SChan) Close() {
- close(c)
+func (c SChan) Recv2() (int, bool) {
+ select {
+ case x, ok := <-c:
+ return x, ok
+ }
+ panic("recv")
}
-func (c SChan) Closed() bool {
- return closed(c)
+func (c SChan) Nbrecv2() (int, bool, bool) {
+ select {
+ default:
+ return 0, false, false
+ case x, ok := <-c:
+ return x, ok, true
+ }
+ panic("nbrecv")
+}
+
+func (c SChan) Close() {
+ close(c)
}
func (c SChan) Impl() string {
@@ -156,12 +182,28 @@ func (c SSChan) Nbrecv() (int, bool) {
panic("nbrecv")
}
-func (c SSChan) Close() {
- close(c)
+func (c SSChan) Recv2() (int, bool) {
+ select {
+ case <-dummy:
+ case x, ok := <-c:
+ return x, ok
+ }
+ panic("recv")
}
-func (c SSChan) Closed() bool {
- return closed(c)
+func (c SSChan) Nbrecv2() (int, bool, bool) {
+ select {
+ case <-dummy:
+ default:
+ return 0, false, false
+ case x, ok := <-c:
+ return x, ok, true
+ }
+ panic("nbrecv")
+}
+
+func (c SSChan) Close() {
+ close(c)
}
func (c SSChan) Impl() string {
@@ -179,29 +221,23 @@ func shouldPanic(f func()) {
}
func test1(c Chan) {
- // not closed until the close signal (a zero value) has been received.
- if c.Closed() {
- println("test1: Closed before Recv zero:", c.Impl())
- }
-
for i := 0; i < 3; i++ {
// recv a close signal (a zero value)
if x := c.Recv(); x != 0 {
- println("test1: recv on closed got non-zero:", x, c.Impl())
+ println("test1: recv on closed:", x, c.Impl())
}
-
- // should now be closed.
- if !c.Closed() {
- println("test1: not closed after recv zero", c.Impl())
+ if x, ok := c.Recv2(); x != 0 || ok {
+ println("test1: recv2 on closed:", x, ok, c.Impl())
}
- // should work with ,ok: received a value without blocking, so ok == true.
- x, ok := c.Nbrecv()
- if !ok {
- println("test1: recv on closed got not ok", c.Impl())
+ // should work with select: received a value without blocking, so selected == true.
+ x, selected := c.Nbrecv()
+ if x != 0 || !selected {
+ println("test1: recv on closed nb:", x, selected, c.Impl())
}
- if x != 0 {
- println("test1: recv ,ok on closed got non-zero:", x, c.Impl())
+ x, ok, selected := c.Nbrecv2()
+ if x != 0 || ok || !selected {
+ println("test1: recv2 on closed nb:", x, ok, selected, c.Impl())
}
}
@@ -221,11 +257,6 @@ func test1(c Chan) {
}
func testasync1(c Chan) {
- // not closed until the close signal (a zero value) has been received.
- if c.Closed() {
- println("testasync1: Closed before Recv zero:", c.Impl())
- }
-
// should be able to get the last value via Recv
if x := c.Recv(); x != 1 {
println("testasync1: Recv did not get 1:", x, c.Impl())
@@ -235,19 +266,31 @@ func testasync1(c Chan) {
}
func testasync2(c Chan) {
- // not closed until the close signal (a zero value) has been received.
- if c.Closed() {
- println("testasync2: Closed before Recv zero:", c.Impl())
+ // should be able to get the last value via Recv2
+ if x, ok := c.Recv2(); x != 1 || !ok {
+ println("testasync1: Recv did not get 1, true:", x, ok, c.Impl())
}
+ test1(c)
+}
+
+func testasync3(c Chan) {
// should be able to get the last value via Nbrecv
- if x, ok := c.Nbrecv(); !ok || x != 1 {
- println("testasync2: Nbrecv did not get 1, true:", x, ok, c.Impl())
+ if x, selected := c.Nbrecv(); x != 1 || !selected {
+ println("testasync2: Nbrecv did not get 1, true:", x, selected, c.Impl())
}
test1(c)
}
+func testasync4(c Chan) {
+ // should be able to get the last value via Nbrecv2
+ if x, ok, selected := c.Nbrecv2(); x != 1 || !ok || !selected {
+ println("testasync2: Nbrecv did not get 1, true, true:", x, ok, selected, c.Impl())
+ }
+ test1(c)
+}
+
func closedsync() chan int {
c := make(chan int)
close(c)
@@ -261,15 +304,27 @@ func closedasync() chan int {
return c
}
+var mks = []func(chan int) Chan {
+ func(c chan int) Chan { return XChan(c) },
+ func(c chan int) Chan { return SChan(c) },
+ func(c chan int) Chan { return SSChan(c) },
+}
+
+var testcloseds = []func(Chan) {
+ testasync1,
+ testasync2,
+ testasync3,
+ testasync4,
+}
+
func main() {
- test1(XChan(closedsync()))
- test1(SChan(closedsync()))
- test1(SSChan(closedsync()))
-
- testasync1(XChan(closedasync()))
- testasync1(SChan(closedasync()))
- testasync1(SSChan(closedasync()))
- testasync2(XChan(closedasync()))
- testasync2(SChan(closedasync()))
- testasync2(SSChan(closedasync()))
+ for _, mk := range mks {
+ test1(mk(closedsync()))
+ }
+
+ for _, testclosed := range testcloseds {
+ for _, mk := range mks {
+ testclosed(mk(closedasync()))
+ }
+ }
}
diff --git a/test/cmp6.go b/test/cmp6.go
index 4c0601187..b3ea8ffeb 100644
--- a/test/cmp6.go
+++ b/test/cmp6.go
@@ -25,8 +25,8 @@ func main() {
var c2 <-chan int
var c3 chan int
- use(c1 == c2) // ERROR "invalid operation"
- use(c2 == c1) // ERROR "invalid operation"
+ use(c1 == c2) // ERROR "invalid operation|incompatible"
+ use(c2 == c1) // ERROR "invalid operation|incompatible"
use(c1 == c3)
use(c2 == c2)
use(c3 == c1)
@@ -37,13 +37,13 @@ func main() {
var p2 T2
var p3 *int
- use(p1 == p2) // ERROR "invalid operation"
- use(p2 == p1) // ERROR "invalid operation"
+ use(p1 == p2) // ERROR "invalid operation|incompatible"
+ use(p2 == p1) // ERROR "invalid operation|incompatible"
use(p1 == p3)
use(p2 == p2)
use(p3 == p1)
use(p3 == p2)
// Comparison of structs should have a good message
- use(t3 == t3) // ERROR "struct"
+ use(t3 == t3) // ERROR "struct|expected"
}
diff --git a/test/ddd1.go b/test/ddd1.go
index fcd32c282..a0bc73814 100644
--- a/test/ddd1.go
+++ b/test/ddd1.go
@@ -15,7 +15,7 @@ var (
_ = sum()
_ = sum(1.0, 2.0)
_ = sum(1.5) // ERROR "integer"
- _ = sum("hello") // ERROR "convert|incompatible"
+ _ = sum("hello") // ERROR "string.*as type int|incompatible"
_ = sum([]int{1}) // ERROR "slice literal.*as type int|incompatible"
)
@@ -35,7 +35,6 @@ func bad(args ...int) {
ch := make(chan int)
close(ch...) // ERROR "[.][.][.]"
_ = len(args...) // ERROR "[.][.][.]"
- _ = closed(ch...) // ERROR "[.][.][.]"
_ = new(int...) // ERROR "[.][.][.]"
n := 10
_ = make([]byte, n...) // ERROR "[.][.][.]"
diff --git a/test/env.go b/test/env.go
index 16b207644..28113bcb0 100644
--- a/test/env.go
+++ b/test/env.go
@@ -6,7 +6,10 @@
package main
-import os "os"
+import (
+ "os"
+ "runtime"
+)
func main() {
ga, e0 := os.Getenverror("GOARCH")
@@ -14,8 +17,8 @@ func main() {
print("$GOARCH: ", e0.String(), "\n")
os.Exit(1)
}
- if ga != "amd64" && ga != "386" && ga != "arm" {
- print("$GOARCH=", ga, "\n")
+ if ga != runtime.GOARCH {
+ print("$GOARCH=", ga, "!= runtime.GOARCH=", runtime.GOARCH, "\n")
os.Exit(1)
}
xxx, e1 := os.Getenverror("DOES_NOT_EXIST")
diff --git a/test/fixedbugs/bug016.go b/test/fixedbugs/bug016.go
index 1cdd8df08..4fbfd48fd 100644
--- a/test/fixedbugs/bug016.go
+++ b/test/fixedbugs/bug016.go
@@ -8,7 +8,7 @@ package main
func main() {
var i int = 100
- i = i << -3 // ERROR "overflows"
+ i = i << -3 // ERROR "overflows|negative"
}
/*
diff --git a/test/fixedbugs/bug055.go b/test/fixedbugs/bug055.go
index 0326d828f..861739610 100644
--- a/test/fixedbugs/bug055.go
+++ b/test/fixedbugs/bug055.go
@@ -7,16 +7,21 @@
package main
func main() {
- var i int;
- var j int;
- if true {}
- { return }
- i = 0;
- if true {} else i++;
- type s struct {};
- i = 0;
- type s2 int;
- var k = func (a int) int { return a+1 }(3);
- _, _ = j, k;
-ro: ;
+ var i int
+ var j int
+ if true {
+ }
+ {
+ return
+ }
+ i = 0
+ if true {
+ } else {
+ i++
+ }
+ type s struct{}
+ i = 0
+ type s2 int
+ var k = func(a int) int { return a + 1 }(3)
+ _, _ = j, k
}
diff --git a/test/fixedbugs/bug069.go b/test/fixedbugs/bug069.go
index bf7316313..9038387ac 100644
--- a/test/fixedbugs/bug069.go
+++ b/test/fixedbugs/bug069.go
@@ -7,15 +7,14 @@
package main
func main() {
- //TODO(rsc): uncomment when this syntax is valid for receive+check closed
- // c := make(chan int);
- // ok := false;
- // var i int;
- //
- // i, ok = <-c; // works
- // _, _ = i, ok;
- //
- // ca := new([2]chan int);
- // i, ok = <-(ca[0]); // fails: c.go:11: bad shape across assignment - cr=1 cl=2
- // _, _ = i, ok;
+ c := make(chan int);
+ ok := false;
+ var i int;
+
+ i, ok = <-c; // works
+ _, _ = i, ok;
+
+ ca := new([2]chan int);
+ i, ok = <-(ca[0]); // fails: c.go:11: bad shape across assignment - cr=1 cl=2
+ _, _ = i, ok;
}
diff --git a/test/fixedbugs/bug076.go b/test/fixedbugs/bug076.go
index 065cecc01..2ca518d76 100644
--- a/test/fixedbugs/bug076.go
+++ b/test/fixedbugs/bug076.go
@@ -1,4 +1,4 @@
-// $G $D/$F.go && $L $F.$A && ./$A.out
+// $G $D/$F.go && $L $F.$A
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
@@ -7,12 +7,16 @@
package main
func f() {
-exit: ;
+exit:
+ ;
+ goto exit
}
func main() {
-exit: ; // this should be legal (labels not properly scoped?)
+exit:
+ ; // this should be legal (labels not properly scoped?)
+ goto exit
}
/*
diff --git a/test/fixedbugs/bug077.go b/test/fixedbugs/bug077.go
index 08028ab10..2cbf96d98 100644
--- a/test/fixedbugs/bug077.go
+++ b/test/fixedbugs/bug077.go
@@ -7,7 +7,8 @@
package main
func main() {
- var exit int;
+ var exit int
exit:
- _ = exit;
+ _ = exit
+ goto exit
}
diff --git a/test/fixedbugs/bug081.go b/test/fixedbugs/bug081.go
index 8d3d538c8..026ce8002 100644
--- a/test/fixedbugs/bug081.go
+++ b/test/fixedbugs/bug081.go
@@ -6,7 +6,7 @@
package main
-const x x = 2 // ERROR "loop"
+const x x = 2 // ERROR "loop|type"
/*
bug081.go:3: first constant must evaluate an expression
diff --git a/test/fixedbugs/bug091.go b/test/fixedbugs/bug091.go
index cfbb09cd8..c2ede7153 100644
--- a/test/fixedbugs/bug091.go
+++ b/test/fixedbugs/bug091.go
@@ -7,18 +7,19 @@
package main
func f1() {
- exit:
- print("hi\n");
+exit:
+ print("hi\n")
+ goto exit
}
func f2() {
- const c = 1234;
+ const c = 1234
}
func f3() {
- i := c; // ERROR "undef"
+ i := c // ERROR "undef"
}
func main() {
- f3();
+ f3()
}
diff --git a/test/fixedbugs/bug137.go b/test/fixedbugs/bug137.go
index 152792411..9d43f431b 100644
--- a/test/fixedbugs/bug137.go
+++ b/test/fixedbugs/bug137.go
@@ -8,16 +8,21 @@ package main
func main() {
L1:
-L2: for i := 0; i < 10; i++ {
- print(i);
- break L2;
+L2:
+ for i := 0; i < 10; i++ {
+ print(i)
+ break L2
}
-L3: ;
-L4: for i := 0; i < 10; i++ {
- print(i);
- break L4;
+L3:
+ ;
+L4:
+ for i := 0; i < 10; i++ {
+ print(i)
+ break L4
}
+ goto L1
+ goto L3
}
/*
diff --git a/test/fixedbugs/bug140.go b/test/fixedbugs/bug140.go
index 298081663..e27b370e7 100644
--- a/test/fixedbugs/bug140.go
+++ b/test/fixedbugs/bug140.go
@@ -7,8 +7,17 @@
package main
func main() {
- if true {} else L1: ;
- if true {} else L2: main() ;
+ if true {
+ } else {
+ L1:
+ }
+ if true {
+ } else {
+ L2:
+ main()
+ }
+ goto L1
+ goto L2
}
/*
diff --git a/test/fixedbugs/bug177.go b/test/fixedbugs/bug177.go
index 84ff59d2f..aec382388 100644
--- a/test/fixedbugs/bug177.go
+++ b/test/fixedbugs/bug177.go
@@ -5,23 +5,26 @@
// license that can be found in the LICENSE file.
package main
+
import "reflect"
-type S1 struct { i int }
-type S2 struct { S1 }
+
+type S1 struct{ i int }
+type S2 struct{ S1 }
+
func main() {
- typ := reflect.Typeof(S2{}).(*reflect.StructType);
- f := typ.Field(0);
+ typ := reflect.Typeof(S2{})
+ f := typ.Field(0)
if f.Name != "S1" || f.Anonymous != true {
- println("BUG: ", f.Name, f.Anonymous);
- return;
+ println("BUG: ", f.Name, f.Anonymous)
+ return
}
- f, ok := typ.FieldByName("S1");
+ f, ok := typ.FieldByName("S1")
if !ok {
- println("BUG: missing S1");
- return;
+ println("BUG: missing S1")
+ return
}
if !f.Anonymous {
- println("BUG: S1 is not anonymous");
- return;
+ println("BUG: S1 is not anonymous")
+ return
}
}
diff --git a/test/fixedbugs/bug178.go b/test/fixedbugs/bug178.go
index 4f586342b..205961024 100644
--- a/test/fixedbugs/bug178.go
+++ b/test/fixedbugs/bug178.go
@@ -9,19 +9,25 @@ package main
func main() {
L:
for i := 0; i < 1; i++ {
-L1:
+ L1:
for {
- break L;
+ break L
}
- panic("BUG: not reached - break");
+ panic("BUG: not reached - break")
}
L2:
for i := 0; i < 1; i++ {
-L3:
+ L3:
for {
- continue L2;
+ continue L2
}
- panic("BUG: not reached - continue");
+ panic("BUG: not reached - continue")
+ }
+ if false {
+ goto L1
+ }
+ if false {
+ goto L3
}
}
diff --git a/test/fixedbugs/bug179.go b/test/fixedbugs/bug179.go
index 67548733c..3347613d8 100644
--- a/test/fixedbugs/bug179.go
+++ b/test/fixedbugs/bug179.go
@@ -10,16 +10,18 @@ func main() {
L:
for {
for {
- break L2; // ERROR "L2"
- continue L2; // ERROR "L2"
+ break L2 // ERROR "L2"
+ continue L2 // ERROR "L2"
}
}
L1:
- x := 1;
- _ = x;
+ x := 1
+ _ = x
for {
- break L1; // ERROR "L1"
- continue L1; // ERROR "L1"
+ break L1 // ERROR "L1"
+ continue L1 // ERROR "L1"
}
+
+ goto L
}
diff --git a/test/fixedbugs/bug196.go b/test/fixedbugs/bug196.go
index 8cb9c9990..ea8ab0dc1 100644
--- a/test/fixedbugs/bug196.go
+++ b/test/fixedbugs/bug196.go
@@ -13,12 +13,11 @@ var i int
func multi() (int, int) { return 1, 2 }
func xxx() {
- //TODO(rsc): uncomment when this syntax is valid for receive+check closed
- // var c chan int
- // x, ok := <-c
+ var c chan int
+ x, ok := <-c
var m map[int]int
- x, ok := m[1]
+ x, ok = m[1]
var i interface{}
var xx int
diff --git a/test/fixedbugs/bug234.go b/test/fixedbugs/bug234.go
index 9affad043..562109a05 100644
--- a/test/fixedbugs/bug234.go
+++ b/test/fixedbugs/bug234.go
@@ -7,17 +7,17 @@
package main
func main() {
- //TODO(rsc): uncomment when this syntax is valid for receive+check closed
- // c := make(chan int, 1)
- // c <- 100
- // x, ok := <-c
- // if x != 100 || !ok {
- // println("x=", x, " ok=", ok, " want 100, true")
- // panic("fail")
- // }
- // x, ok = <-c
- // if x != 0 || ok {
- // println("x=", x, " ok=", ok, " want 0, false")
- // panic("fail")
- // }
+ c := make(chan int, 1)
+ c <- 100
+ x, ok := <-c
+ if x != 100 || !ok {
+ println("x=", x, " ok=", ok, " want 100, true")
+ panic("fail")
+ }
+ close(c)
+ x, ok = <-c
+ if x != 0 || ok {
+ println("x=", x, " ok=", ok, " want 0, false")
+ panic("fail")
+ }
}
diff --git a/test/fixedbugs/bug242.go b/test/fixedbugs/bug242.go
index ad1cef8df..839dccd37 100644
--- a/test/fixedbugs/bug242.go
+++ b/test/fixedbugs/bug242.go
@@ -101,13 +101,11 @@ func main() {
c := make(chan byte, 1)
c <- 'C'
- //TODO(rsc): uncomment when this syntax is valid for receive+check closed
// 15 16
- // *f(), p1 = <-e1(c, 16)
- *f(), p1 = <-e1(c, 16), true // delete uncommenting above
+ *f(), p1 = <-e1(c, 16)
+ close(c)
// 17 18
- // *f(), p2 = <-e1(c, 18)
- *f(), p2, _ = 0, false, e1(c, 18) // delete when uncommenting above
+ *f(), p2 = <-e1(c, 18)
a[17] += '0'
if !p1 || p2 {
println("bad chan check", i, p1, p2)
diff --git a/test/fixedbugs/bug243.go b/test/fixedbugs/bug243.go
index 236c14402..0c531968e 100644
--- a/test/fixedbugs/bug243.go
+++ b/test/fixedbugs/bug243.go
@@ -6,12 +6,14 @@
package main
-import (
- "net"
-)
+import "os"
+
+// Issue 481: closures and var declarations
+// with multiple variables assigned from one
+// function call.
func main() {
- var listen, _ = net.Listen("tcp", "127.0.0.1:0")
+ var listen, _ = Listen("tcp", "127.0.0.1:0")
go func() {
for {
@@ -20,6 +22,31 @@ func main() {
}
}()
- var conn, _ = net.Dial("tcp", "", listen.Addr().String())
+ var conn, _ = Dial("tcp", "", listen.Addr().String())
_ = conn
}
+
+// Simulated net interface to exercise bug
+// without involving a real network.
+type T chan int
+
+var global T
+
+func Listen(x, y string) (T, string) {
+ global = make(chan int)
+ return global, y
+}
+
+func (t T) Addr() os.Error {
+ return os.ErrorString("stringer")
+}
+
+func (t T) Accept() (int, string) {
+ return <-t, ""
+}
+
+func Dial(x, y, z string) (int, string) {
+ global <- 1
+ return 0, ""
+}
+
diff --git a/test/fixedbugs/bug252.go b/test/fixedbugs/bug252.go
index 5615f84fa..a2c1dab9d 100644
--- a/test/fixedbugs/bug252.go
+++ b/test/fixedbugs/bug252.go
@@ -7,9 +7,9 @@
package main
func f(args ...int) {
- g(args) // ERROR "[.][.][.]"
+ g(args)
}
func g(args ...interface{}) {
- f(args) // ERROR "[.][.][.]"
+ f(args) // ERROR "cannot use|incompatible"
}
diff --git a/test/fixedbugs/bug274.go b/test/fixedbugs/bug274.go
index 621f31eed..348aed429 100644
--- a/test/fixedbugs/bug274.go
+++ b/test/fixedbugs/bug274.go
@@ -24,6 +24,7 @@ func main() {
case 1:
L1: // ERROR "statement"
default:
- L2: // correct since no semicolon is required before a '}'
+ // correct since no semicolon is required before a '}'
+ L2: // GCCGO_ERROR "not used"
}
}
diff --git a/test/fixedbugs/bug323.go b/test/fixedbugs/bug323.go
index bfb528318..23e2be660 100644
--- a/test/fixedbugs/bug323.go
+++ b/test/fixedbugs/bug323.go
@@ -15,6 +15,6 @@ func (t T) Meth2() {}
func main() {
t := &T{}
p := P(t)
- p.Meth() // ERROR "undefined \(type P"
- p.Meth2() // ERROR "undefined \(type P"
-} \ No newline at end of file
+ p.Meth() // ERROR "undefined"
+ p.Meth2() // ERROR "undefined"
+}
diff --git a/test/fixedbugs/bug325.go b/test/fixedbugs/bug325.go
index 23dbc8b3c..b86740fff 100644
--- a/test/fixedbugs/bug325.go
+++ b/test/fixedbugs/bug325.go
@@ -11,4 +11,5 @@ import "unsafe"
func main() {
var x unsafe.Pointer
println(*x) // ERROR "invalid indirect.*unsafe.Pointer"
+ var _ = (unsafe.Pointer)(nil).foo // ERROR "foo"
}
diff --git a/test/fixedbugs/bug326.go b/test/fixedbugs/bug326.go
new file mode 100644
index 000000000..efdd0ef71
--- /dev/null
+++ b/test/fixedbugs/bug326.go
@@ -0,0 +1,41 @@
+// errchk $G $D/$F.go
+
+// 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 p
+
+import "os"
+
+func f() (_ int, err os.Error) {
+ return
+}
+
+func g() (x int, _ os.Error) {
+ return
+}
+
+func h() (_ int, _ os.Error) {
+ return
+}
+
+func i() (int, os.Error) {
+ return // ERROR "not enough arguments to return"
+}
+
+func f1() (_ int, err os.Error) {
+ return 1, nil
+}
+
+func g1() (x int, _ os.Error) {
+ return 1, nil
+}
+
+func h1() (_ int, _ os.Error) {
+ return 1, nil
+}
+
+func ii() (int, os.Error) {
+ return 1, nil
+}
diff --git a/test/fixedbugs/bug327.go b/test/fixedbugs/bug327.go
new file mode 100644
index 000000000..4ba5f6072
--- /dev/null
+++ b/test/fixedbugs/bug327.go
@@ -0,0 +1,24 @@
+// $G $D/$F.go && $L $F.$A && ./$A.out
+
+// 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.
+
+// Conversion between identical interfaces.
+// Issue 1647.
+
+// The compiler used to not realize this was a no-op,
+// so it generated a call to the non-existent function runtime.convE2E.
+
+package main
+
+type (
+ a interface{}
+ b interface{}
+)
+
+func main() {
+ x := a(1)
+ z := b(x)
+ _ = z
+}
diff --git a/test/func6.go b/test/func6.go
new file mode 100644
index 000000000..1356b6aa8
--- /dev/null
+++ b/test/func6.go
@@ -0,0 +1,14 @@
+// $G $D/$F.go
+
+// 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 main() {
+ if func() bool { return true }() {} // 6g used to say this was a syntax error
+ if (func() bool { return true })() {}
+ if (func() bool { return true }()) {}
+}
+
diff --git a/test/gc2.go b/test/gc2.go
new file mode 100644
index 000000000..c5c6cbe4b
--- /dev/null
+++ b/test/gc2.go
@@ -0,0 +1,41 @@
+// $G $D/$F.go && $L $F.$A && ./$A.out
+
+// 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.
+
+// Check that buffered channels are garbage collected properly.
+// An interesting case because they have finalizers and used to
+// have self loops that kept them from being collected.
+// (Cyclic data with finalizers is never finalized, nor collected.)
+
+package main
+
+import (
+ "fmt"
+ "os"
+ "runtime"
+)
+
+func main() {
+ const N = 10000
+ st := runtime.MemStats
+ for i := 0; i < N; i++ {
+ c := make(chan int, 10)
+ _ = c
+ if i%100 == 0 {
+ for j := 0; j < 4; j++ {
+ runtime.GC()
+ runtime.Gosched()
+ runtime.GC()
+ runtime.Gosched()
+ }
+ }
+ }
+
+ obj := runtime.MemStats.HeapObjects - st.HeapObjects
+ if obj > N/5 {
+ fmt.Println("too many objects left:", obj)
+ os.Exit(1)
+ }
+}
diff --git a/test/golden.out b/test/golden.out
index cc699d450..f76db3e50 100644
--- a/test/golden.out
+++ b/test/golden.out
@@ -161,9 +161,10 @@ panic: interface conversion: interface is main.T, not main.T
=========== bugs/bug322.go
bugs/bug322.dir/main.go:19: implicit assignment of unexported field 'x' of lib.T in method receiver
-bugs/bug322.dir/main.go:22: implicit assignment of unexported field 'x' of lib.T in assignment
-bugs/bug322.dir/main.go:31: implicit assignment of unexported field 'x' of lib.T in method receiver
+bugs/bug322.dir/main.go:32: implicit assignment of unexported field 'x' of lib.T in method receiver
BUG: fails incorrectly
=========== bugs/bug324.go
-BUG: errchk: command succeeded unexpectedly
+main.Implementation.private()
+p.Implementation.private()
+BUG: should fail
diff --git a/test/init.go b/test/init.go
index b6c8c9706..74c2d5c26 100644
--- a/test/init.go
+++ b/test/init.go
@@ -12,7 +12,7 @@ func init() {
}
func main() {
- init() // ERROR "undefined: init"
+ init() // ERROR "undefined.*init"
runtime.init() // ERROR "unexported.*runtime\.init"
- var _ = init // ERROR "undefined: init"
+ var _ = init // ERROR "undefined.*init"
}
diff --git a/test/interface/fake.go b/test/interface/fake.go
index 5cf3be052..de8505d8d 100644
--- a/test/interface/fake.go
+++ b/test/interface/fake.go
@@ -46,34 +46,34 @@ func main() {
x.t = add("abc", "def")
x.u = 1
x.v = 2
- x.w = 1<<28
- x.x = 2<<28
+ x.w = 1 << 28
+ x.x = 2 << 28
x.y = 0x12345678
x.z = x.y
// check mem and string
v := reflect.NewValue(x)
- i := v.(*reflect.StructValue).Field(0)
- j := v.(*reflect.StructValue).Field(1)
+ i := v.Field(0)
+ j := v.Field(1)
assert(i.Interface() == j.Interface())
- s := v.(*reflect.StructValue).Field(2)
- t := v.(*reflect.StructValue).Field(3)
+ s := v.Field(2)
+ t := v.Field(3)
assert(s.Interface() == t.Interface())
// make sure different values are different.
// make sure whole word is being compared,
// not just a single byte.
- i = v.(*reflect.StructValue).Field(4)
- j = v.(*reflect.StructValue).Field(5)
+ i = v.Field(4)
+ j = v.Field(5)
assert(i.Interface() != j.Interface())
- i = v.(*reflect.StructValue).Field(6)
- j = v.(*reflect.StructValue).Field(7)
+ i = v.Field(6)
+ j = v.Field(7)
assert(i.Interface() != j.Interface())
- i = v.(*reflect.StructValue).Field(8)
- j = v.(*reflect.StructValue).Field(9)
+ i = v.Field(8)
+ j = v.Field(9)
assert(i.Interface() == j.Interface())
}
diff --git a/test/interface/private.go b/test/interface/private.go
new file mode 100644
index 000000000..37890c923
--- /dev/null
+++ b/test/interface/private.go
@@ -0,0 +1,32 @@
+// $G $D/${F}1.go && errchk $G $D/$F.go
+
+// 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 "./private1"
+
+type Exported interface {
+ private()
+}
+
+type Implementation struct{}
+
+func (p *Implementation) private() {}
+
+func main() {
+ var x Exported
+ x = new(Implementation)
+ x.private()
+
+ var px p.Exported
+ px = p.X
+
+ px.private() // ERROR "private"
+
+ px = new(Implementation) // ERROR "private"
+
+ x = px // ERROR "private"
+}
diff --git a/test/interface/private1.go b/test/interface/private1.go
new file mode 100644
index 000000000..3173fbef4
--- /dev/null
+++ b/test/interface/private1.go
@@ -0,0 +1,18 @@
+// true # used by private.go
+
+// 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 p
+
+type Exported interface {
+ private()
+}
+
+type Implementation struct{}
+
+func (p *Implementation) private() {}
+
+var X = new(Implementation)
+
diff --git a/test/ken/cplx3.go b/test/ken/cplx3.go
index 83acc15ff..979e53f56 100644
--- a/test/ken/cplx3.go
+++ b/test/ken/cplx3.go
@@ -25,9 +25,9 @@ func main() {
println(c)
var a interface{}
- switch c := reflect.NewValue(a).(type) {
- case *reflect.ComplexValue:
- v := c.Get()
+ switch c := reflect.NewValue(a); c.Kind() {
+ case reflect.Complex64, reflect.Complex128:
+ v := c.Complex()
_, _ = complex128(v), true
}
}
diff --git a/test/label.go b/test/label.go
new file mode 100644
index 000000000..e3d853266
--- /dev/null
+++ b/test/label.go
@@ -0,0 +1,60 @@
+// errchk $G -e $D/$F.go
+
+// 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.
+
+// Pass 1 label errors.
+
+package main
+
+var x int
+
+func f() {
+L1: // ERROR "label .*L1.* defined and not used"
+ for {
+ }
+L2: // ERROR "label .*L2.* defined and not used"
+ select {
+ }
+L3: // ERROR "label .*L3.* defined and not used"
+ switch {
+ }
+L4: // ERROR "label .*L4.* defined and not used"
+ if true {
+ }
+L5: // ERROR "label .*L5.* defined and not used"
+ f()
+L6: // GCCGO_ERROR "previous"
+ f()
+L6: // ERROR "label .*L6.* already defined"
+ f()
+ if x == 20 {
+ goto L6
+ }
+
+L7:
+ for {
+ break L7
+ }
+
+L8:
+ for {
+ if x == 21 {
+ continue L8
+ }
+ }
+
+L9:
+ switch {
+ case true:
+ break L9
+ defalt: // ERROR "label .*defalt.* defined and not used"
+ }
+
+L10:
+ select {
+ default:
+ break L10
+ }
+}
diff --git a/test/label1.go b/test/label1.go
new file mode 100644
index 000000000..656daaeea
--- /dev/null
+++ b/test/label1.go
@@ -0,0 +1,85 @@
+// errchk $G -e $D/$F.go
+
+// 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.
+
+// Pass 2 label errors.
+
+package main
+
+var x int
+
+func f() {
+L1:
+ for {
+ if x == 0 {
+ break L1
+ }
+ if x == 1 {
+ continue L1
+ }
+ goto L1
+ }
+
+L2:
+ select {
+ default:
+ if x == 0 {
+ break L2
+ }
+ if x == 1 {
+ continue L2 // ERROR "invalid continue label .*L2"
+ }
+ goto L2
+ }
+
+L3:
+ switch {
+ case x > 10:
+ if x == 11 {
+ break L3
+ }
+ if x == 12 {
+ continue L3 // ERROR "invalid continue label .*L3"
+ }
+ goto L3
+ }
+
+L4:
+ if true {
+ if x == 13 {
+ break L4 // ERROR "invalid break label .*L4"
+ }
+ if x == 14 {
+ continue L4 // ERROR "invalid continue label .*L4"
+ }
+ if x == 15 {
+ goto L4
+ }
+ }
+
+L5:
+ f()
+ if x == 16 {
+ break L5 // ERROR "invalid break label .*L5"
+ }
+ if x == 17 {
+ continue L5 // ERROR "invalid continue label .*L5"
+ }
+ if x == 18 {
+ goto L5
+ }
+
+ for {
+ if x == 19 {
+ break L1 // ERROR "invalid break label .*L1"
+ }
+ if x == 20 {
+ continue L1 // ERROR "invalid continue label .*L1"
+ }
+ if x == 21 {
+ goto L1
+ }
+ }
+}
diff --git a/test/named1.go b/test/named1.go
index 1776313f0..7e7aab9c1 100644
--- a/test/named1.go
+++ b/test/named1.go
@@ -43,10 +43,6 @@ func main() {
_, b = m[2] // ERROR "cannot .* bool.*type Bool"
m[2] = 1, b // ERROR "cannot use.*type Bool.*as type bool"
- ////TODO(rsc): uncomment when this syntax is valid for receive+check closed
- //// _, b = <-c // ERROR "cannot .* bool.*type Bool"
- //// _ = b
-
var inter interface{}
_, b = inter.(Map) // ERROR "cannot .* bool.*type Bool"
_ = b
@@ -57,8 +53,9 @@ func main() {
_, b = minter.(Map) // ERROR "cannot .* bool.*type Bool"
_ = b
- asBool(closed(c)) // ERROR "cannot use.*type bool.*as type Bool"
- b = closed(c) // ERROR "cannot use.*type bool.*type Bool"
+ _, bb := <-c
+ asBool(bb) // ERROR "cannot use.*type bool.*as type Bool"
+ _, b = <-c // ERROR "cannot .* bool.*type Bool"
_ = b
asString(String(slice)) // ERROR "cannot .*type Slice.*type String"
diff --git a/test/run b/test/run
index 28d0caa0f..628cc2d7b 100755
--- a/test/run
+++ b/test/run
@@ -5,6 +5,8 @@
eval $(gomake --no-print-directory -f ../src/Make.inc go-env)
+export E=
+
case X"$GOARCH" in
Xamd64)
export A=6
@@ -97,6 +99,7 @@ do
echo $i >>pass.out
fi
echo $(awk 'NR==1{print $2}' $TMP2FILE) $D/$F >>times.out
+ rm -f $F.$A $A.out
) done
done | # clean up some stack noise
egrep -v '^(r[0-9a-z]+|[cfg]s) +0x' |
diff --git a/test/syntax/chan.go b/test/syntax/chan.go
index 48beb1e70..ff3577502 100644
--- a/test/syntax/chan.go
+++ b/test/syntax/chan.go
@@ -8,9 +8,9 @@ package main
type xyz struct {
ch chan
-} // ERROR "unexpected } in channel type"
+} // ERROR "unexpected .*}.* in channel type"
-func Foo(y chan) { // ERROR "unexpected \) in channel type"
+func Foo(y chan) { // ERROR "unexpected .*\).* in channel type"
}
func Bar(x chan, y int) { // ERROR "unexpected comma in channel type"
diff --git a/test/syntax/if.go b/test/syntax/if.go
index 913d41885..a3b51f0c0 100644
--- a/test/syntax/if.go
+++ b/test/syntax/if.go
@@ -6,6 +6,9 @@
package main
+func x() {
+}
+
func main() {
if { // ERROR "missing condition"
}