summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cmd/5a/a.h1
-rw-r--r--src/cmd/5a/a.y35
-rw-r--r--src/cmd/5a/doc.go4
-rw-r--r--src/cmd/5a/lex.c83
-rw-r--r--src/cmd/5a/y.tab.c1877
-rw-r--r--src/cmd/5a/y.tab.h148
-rw-r--r--src/cmd/5c/cgen.c4
-rw-r--r--src/cmd/5c/gc.h1
-rw-r--r--src/cmd/5c/peep.c11
-rw-r--r--src/cmd/5c/reg.c8
-rw-r--r--src/cmd/5c/sgen.c8
-rw-r--r--src/cmd/5c/swt.c10
-rw-r--r--src/cmd/5c/txt.c44
-rw-r--r--src/cmd/5g/cgen.c95
-rw-r--r--src/cmd/5g/gg.h7
-rw-r--r--src/cmd/5g/ggen.c171
-rw-r--r--src/cmd/5g/gobj.c160
-rw-r--r--src/cmd/5g/gsubr.c157
-rw-r--r--src/cmd/5g/opt.h99
-rw-r--r--src/cmd/5g/peep.c487
-rw-r--r--src/cmd/5g/prog.c143
-rw-r--r--src/cmd/5g/reg.c812
-rw-r--r--src/cmd/5l/5.out.h15
-rw-r--r--src/cmd/5l/asm.c159
-rw-r--r--src/cmd/5l/l.h9
-rw-r--r--src/cmd/5l/list.c2
-rw-r--r--src/cmd/5l/noop.c484
-rw-r--r--src/cmd/5l/obj.c61
-rw-r--r--src/cmd/5l/optab.c36
-rw-r--r--src/cmd/5l/pass.c11
-rw-r--r--src/cmd/5l/span.c21
-rw-r--r--src/cmd/6a/a.y45
-rw-r--r--src/cmd/6a/doc.go4
-rw-r--r--src/cmd/6a/lex.c76
-rw-r--r--src/cmd/6a/y.tab.c2091
-rw-r--r--src/cmd/6a/y.tab.h138
-rw-r--r--src/cmd/6c/cgen.c18
-rw-r--r--src/cmd/6c/gc.h1
-rw-r--r--src/cmd/6c/peep.c2
-rw-r--r--src/cmd/6c/reg.c2
-rw-r--r--src/cmd/6c/sgen.c8
-rw-r--r--src/cmd/6c/swt.c78
-rw-r--r--src/cmd/6c/txt.c25
-rw-r--r--src/cmd/6g/cgen.c98
-rw-r--r--src/cmd/6g/gg.h6
-rw-r--r--src/cmd/6g/ggen.c154
-rw-r--r--src/cmd/6g/gobj.c178
-rw-r--r--src/cmd/6g/gsubr.c98
-rw-r--r--src/cmd/6g/opt.h107
-rw-r--r--src/cmd/6g/peep.c673
-rw-r--r--src/cmd/6g/prog.c313
-rw-r--r--src/cmd/6g/reg.c957
-rw-r--r--src/cmd/6l/6.out.h13
-rw-r--r--src/cmd/6l/asm.c32
-rw-r--r--src/cmd/6l/l.h9
-rw-r--r--src/cmd/6l/list.c7
-rw-r--r--src/cmd/6l/obj.c52
-rw-r--r--src/cmd/6l/optab.c45
-rw-r--r--src/cmd/6l/pass.c515
-rw-r--r--src/cmd/6l/span.c42
-rw-r--r--src/cmd/8a/a.y31
-rw-r--r--src/cmd/8a/doc.go5
-rw-r--r--src/cmd/8a/lex.c77
-rw-r--r--src/cmd/8a/y.tab.c2029
-rw-r--r--src/cmd/8a/y.tab.h110
-rw-r--r--src/cmd/8c/cgen.c8
-rw-r--r--src/cmd/8c/gc.h1
-rw-r--r--src/cmd/8c/peep.c2
-rw-r--r--src/cmd/8c/reg.c2
-rw-r--r--src/cmd/8c/sgen.c8
-rw-r--r--src/cmd/8c/swt.c80
-rw-r--r--src/cmd/8c/txt.c25
-rw-r--r--src/cmd/8g/cgen.c101
-rw-r--r--src/cmd/8g/gg.h6
-rw-r--r--src/cmd/8g/ggen.c152
-rw-r--r--src/cmd/8g/gobj.c175
-rw-r--r--src/cmd/8g/gsubr.c66
-rw-r--r--src/cmd/8g/opt.h89
-rw-r--r--src/cmd/8g/peep.c556
-rw-r--r--src/cmd/8g/prog.c340
-rw-r--r--src/cmd/8g/reg.c1051
-rw-r--r--src/cmd/8l/8.out.h12
-rw-r--r--src/cmd/8l/asm.c23
-rw-r--r--src/cmd/8l/l.h9
-rw-r--r--src/cmd/8l/list.c2
-rw-r--r--src/cmd/8l/obj.c27
-rw-r--r--src/cmd/8l/optab.c38
-rw-r--r--src/cmd/8l/pass.c425
-rw-r--r--src/cmd/8l/span.c13
-rw-r--r--src/cmd/addr2line/main.c13
-rw-r--r--src/cmd/api/clone.go251
-rw-r--r--src/cmd/api/goapi.go1221
-rw-r--r--src/cmd/api/goapi_test.go37
-rw-r--r--src/cmd/api/run.go186
-rw-r--r--src/cmd/api/testdata/src/pkg/p1/golden.txt21
-rw-r--r--src/cmd/api/testdata/src/pkg/p1/p1.go21
-rw-r--r--src/cmd/cc/bv.c45
-rw-r--r--src/cmd/cc/cc.h24
-rw-r--r--src/cmd/cc/cc.y12
-rw-r--r--src/cmd/cc/com.c13
-rw-r--r--src/cmd/cc/dcl.c27
-rw-r--r--src/cmd/cc/dpchk.c28
-rw-r--r--src/cmd/cc/funct.c2
-rw-r--r--src/cmd/cc/lex.c24
-rw-r--r--src/cmd/cc/lexbody14
-rw-r--r--src/cmd/cc/pgen.c172
-rw-r--r--src/cmd/cc/pswt.c23
-rw-r--r--src/cmd/cc/scon.c2
-rw-r--r--src/cmd/cc/sub.c5
-rw-r--r--src/cmd/cc/y.tab.c1068
-rw-r--r--src/cmd/cc/y.tab.h68
-rw-r--r--src/cmd/cgo/ast.go15
-rw-r--r--src/cmd/cgo/doc.go218
-rw-r--r--src/cmd/cgo/gcc.go414
-rw-r--r--src/cmd/cgo/godefs.go17
-rw-r--r--src/cmd/cgo/main.go9
-rw-r--r--src/cmd/cgo/out.go142
-rw-r--r--src/cmd/dist/a.h9
-rw-r--r--src/cmd/dist/build.c89
-rw-r--r--src/cmd/dist/buildgo.c49
-rw-r--r--src/cmd/dist/buildruntime.c115
-rw-r--r--src/cmd/dist/goc2c.c18
-rw-r--r--src/cmd/dist/main.c1
-rw-r--r--src/cmd/dist/plan9.c4
-rw-r--r--src/cmd/dist/unix.c38
-rw-r--r--src/cmd/dist/windows.c4
-rw-r--r--src/cmd/fix/netipv6zone.go2
-rw-r--r--src/cmd/fix/netipv6zone_test.go4
-rw-r--r--src/cmd/fix/testdata/reflect.asn1.go.in814
-rw-r--r--src/cmd/fix/testdata/reflect.asn1.go.out814
-rw-r--r--src/cmd/fix/testdata/reflect.datafmt.go.in710
-rw-r--r--src/cmd/fix/testdata/reflect.datafmt.go.out710
-rw-r--r--src/cmd/fix/testdata/reflect.decode.go.in905
-rw-r--r--src/cmd/fix/testdata/reflect.decode.go.out908
-rw-r--r--src/cmd/fix/testdata/reflect.decoder.go.in196
-rw-r--r--src/cmd/fix/testdata/reflect.decoder.go.out196
-rw-r--r--src/cmd/fix/testdata/reflect.dnsmsg.go.in777
-rw-r--r--src/cmd/fix/testdata/reflect.dnsmsg.go.out777
-rw-r--r--src/cmd/fix/testdata/reflect.encode.go.in367
-rw-r--r--src/cmd/fix/testdata/reflect.encode.go.out367
-rw-r--r--src/cmd/fix/testdata/reflect.encoder.go.in240
-rw-r--r--src/cmd/fix/testdata/reflect.encoder.go.out240
-rw-r--r--src/cmd/fix/testdata/reflect.export.go.in400
-rw-r--r--src/cmd/fix/testdata/reflect.export.go.out400
-rw-r--r--src/cmd/fix/testdata/reflect.print.go.in944
-rw-r--r--src/cmd/fix/testdata/reflect.print.go.out944
-rw-r--r--src/cmd/fix/testdata/reflect.quick.go.in364
-rw-r--r--src/cmd/fix/testdata/reflect.quick.go.out365
-rw-r--r--src/cmd/fix/testdata/reflect.read.go.in620
-rw-r--r--src/cmd/fix/testdata/reflect.read.go.out620
-rw-r--r--src/cmd/fix/testdata/reflect.scan.go.in1082
-rw-r--r--src/cmd/fix/testdata/reflect.scan.go.out1082
-rw-r--r--src/cmd/fix/testdata/reflect.script.go.in359
-rw-r--r--src/cmd/fix/testdata/reflect.script.go.out359
-rw-r--r--src/cmd/fix/testdata/reflect.template.go.in1043
-rw-r--r--src/cmd/fix/testdata/reflect.template.go.out1044
-rw-r--r--src/cmd/fix/testdata/reflect.type.go.in790
-rw-r--r--src/cmd/fix/testdata/reflect.type.go.out790
-rw-r--r--src/cmd/gc/align.c6
-rwxr-xr-xsrc/cmd/gc/bisonerrors3
-rw-r--r--src/cmd/gc/builtin.c6
-rw-r--r--src/cmd/gc/bv.c80
-rw-r--r--src/cmd/gc/closure.c18
-rw-r--r--src/cmd/gc/const.c2
-rw-r--r--src/cmd/gc/dcl.c20
-rw-r--r--src/cmd/gc/esc.c4
-rw-r--r--src/cmd/gc/export.c8
-rw-r--r--src/cmd/gc/fmt.c29
-rw-r--r--src/cmd/gc/gen.c32
-rw-r--r--src/cmd/gc/go.errors11
-rw-r--r--src/cmd/gc/go.h59
-rw-r--r--src/cmd/gc/go.y44
-rw-r--r--src/cmd/gc/inl.c1
-rw-r--r--src/cmd/gc/lex.c105
-rw-r--r--src/cmd/gc/md5.c2
-rw-r--r--src/cmd/gc/mkbuiltin1.c2
-rw-r--r--src/cmd/gc/mparith2.c10
-rw-r--r--src/cmd/gc/obj.c6
-rw-r--r--src/cmd/gc/order.c3
-rw-r--r--src/cmd/gc/pgen.c326
-rw-r--r--src/cmd/gc/popt.c948
-rw-r--r--src/cmd/gc/popt.h46
-rw-r--r--src/cmd/gc/racewalk.c122
-rw-r--r--src/cmd/gc/range.c3
-rw-r--r--src/cmd/gc/reflect.c201
-rw-r--r--src/cmd/gc/runtime.go8
-rw-r--r--src/cmd/gc/sinit.c79
-rw-r--r--src/cmd/gc/subr.c84
-rw-r--r--src/cmd/gc/swt.c9
-rw-r--r--src/cmd/gc/typecheck.c189
-rw-r--r--src/cmd/gc/walk.c340
-rw-r--r--src/cmd/gc/y.tab.c3771
-rw-r--r--src/cmd/gc/y.tab.h46
-rw-r--r--src/cmd/gc/yerr.h41
-rw-r--r--src/cmd/go/bootstrap.go2
-rw-r--r--src/cmd/go/build.go332
-rw-r--r--src/cmd/go/clean.go46
-rw-r--r--src/cmd/go/discovery.go22
-rw-r--r--src/cmd/go/doc.go257
-rw-r--r--src/cmd/go/env.go5
-rw-r--r--src/cmd/go/fmt.go37
-rw-r--r--src/cmd/go/get.go39
-rw-r--r--src/cmd/go/help.go79
-rw-r--r--src/cmd/go/list.go17
-rw-r--r--src/cmd/go/main.go64
-rw-r--r--src/cmd/go/match_test.go70
-rw-r--r--src/cmd/go/pkg.go171
-rw-r--r--src/cmd/go/run.go2
-rw-r--r--src/cmd/go/signal_notunix.go4
-rw-r--r--src/cmd/go/signal_unix.go6
-rwxr-xr-xsrc/cmd/go/test.bash333
-rw-r--r--src/cmd/go/test.go424
-rw-r--r--src/cmd/go/testdata/shadow/root1/src/foo/foo.go1
-rw-r--r--src/cmd/go/testdata/shadow/root1/src/math/math.go1
-rw-r--r--src/cmd/go/testdata/shadow/root2/src/foo/foo.go1
-rw-r--r--src/cmd/go/testdata/src/badpkg/x.go1
-rw-r--r--src/cmd/go/testdata/src/cgotest/m.go5
-rw-r--r--src/cmd/go/testdata/src/main_test/m.go4
-rw-r--r--src/cmd/go/testdata/src/main_test/m_test.go10
-rw-r--r--src/cmd/go/testdata/src/syntaxerror/x.go1
-rw-r--r--src/cmd/go/testdata/src/syntaxerror/x_test.go4
-rw-r--r--src/cmd/go/testflag.go61
-rw-r--r--src/cmd/go/tool.go34
-rw-r--r--src/cmd/go/vcs.go45
-rw-r--r--src/cmd/go/vet.go2
-rw-r--r--src/cmd/godoc/README.godoc-app61
-rw-r--r--src/cmd/godoc/appinit.go69
-rw-r--r--src/cmd/godoc/codewalk.go494
-rw-r--r--src/cmd/godoc/dirtrees.go320
-rw-r--r--src/cmd/godoc/doc.go135
-rw-r--r--src/cmd/godoc/filesystem.go562
-rw-r--r--src/cmd/godoc/format.go372
-rw-r--r--src/cmd/godoc/godoc.go1582
-rw-r--r--src/cmd/godoc/index.go1073
-rw-r--r--src/cmd/godoc/linkify.go234
-rw-r--r--src/cmd/godoc/main.go470
-rw-r--r--src/cmd/godoc/parser.go37
-rw-r--r--src/cmd/godoc/play-appengine.go35
-rw-r--r--src/cmd/godoc/play-local.go41
-rw-r--r--src/cmd/godoc/play.go52
-rwxr-xr-xsrc/cmd/godoc/setup-godoc-app.bash140
-rw-r--r--src/cmd/godoc/snippet.go112
-rw-r--r--src/cmd/godoc/spec.go179
-rw-r--r--src/cmd/godoc/template.go182
-rw-r--r--src/cmd/godoc/throttle.go88
-rw-r--r--src/cmd/godoc/utils.go91
-rw-r--r--src/cmd/godoc/zip.go236
-rw-r--r--src/cmd/gofmt/doc.go19
-rw-r--r--src/cmd/gofmt/simplify.go4
-rw-r--r--src/cmd/gofmt/testdata/import.golden18
-rw-r--r--src/cmd/gofmt/testdata/import.input23
-rw-r--r--src/cmd/ld/data.c198
-rw-r--r--src/cmd/ld/doc.go4
-rw-r--r--src/cmd/ld/dwarf.c47
-rw-r--r--src/cmd/ld/elf.c62
-rw-r--r--src/cmd/ld/elf.h5
-rw-r--r--src/cmd/ld/go.c25
-rw-r--r--src/cmd/ld/ldmacho.c25
-rw-r--r--src/cmd/ld/ldpe.c2
-rw-r--r--src/cmd/ld/lib.c843
-rw-r--r--src/cmd/ld/lib.h45
-rw-r--r--src/cmd/ld/pe.c1
-rw-r--r--src/cmd/ld/symtab.c48
-rw-r--r--src/cmd/ld/textflag.h21
-rw-r--r--src/cmd/nm/doc.go2
-rw-r--r--src/cmd/nm/nm.c37
-rw-r--r--src/cmd/pack/ar.c64
-rw-r--r--src/cmd/vet/Makefile14
-rw-r--r--src/cmd/vet/asmdecl.go533
-rw-r--r--src/cmd/vet/assign.go44
-rw-r--r--src/cmd/vet/atomic.go59
-rw-r--r--src/cmd/vet/buildtag.go91
-rw-r--r--src/cmd/vet/deadcode.go280
-rw-r--r--src/cmd/vet/doc.go76
-rw-r--r--src/cmd/vet/main.go422
-rw-r--r--src/cmd/vet/method.go162
-rw-r--r--src/cmd/vet/print.go351
-rw-r--r--src/cmd/vet/rangeloop.go65
-rw-r--r--src/cmd/vet/structtag.go37
-rw-r--r--src/cmd/vet/taglit.go164
-rw-r--r--src/cmd/vet/test_asm.go24
-rw-r--r--src/cmd/vet/test_asm1.s247
-rw-r--r--src/cmd/vet/test_asm2.s251
-rw-r--r--src/cmd/vet/test_asm3.s166
-rw-r--r--src/cmd/vet/test_assign.go20
-rw-r--r--src/cmd/vet/test_atomic.go43
-rw-r--r--src/cmd/vet/test_buildtag.go15
-rw-r--r--src/cmd/vet/test_buildtag_bad.go15
-rw-r--r--src/cmd/vet/test_deadcode.go2121
-rw-r--r--src/cmd/vet/test_method.go24
-rw-r--r--src/cmd/vet/test_print.go153
-rw-r--r--src/cmd/vet/test_rangeloop.go61
-rw-r--r--src/cmd/vet/test_structtag.go15
-rw-r--r--src/cmd/vet/test_taglit.go65
-rw-r--r--src/cmd/vet/types.go179
-rw-r--r--src/cmd/vet/typestub.go45
-rw-r--r--src/cmd/yacc/Makefile8
-rw-r--r--src/cmd/yacc/doc.go6
-rw-r--r--src/cmd/yacc/expr.y205
-rw-r--r--src/cmd/yacc/units.txt576
-rw-r--r--src/cmd/yacc/units.y768
-rw-r--r--src/cmd/yacc/yacc.go39
-rw-r--r--src/lib9/_p9dir.c15
-rw-r--r--src/lib9/atoi.c2
-rw-r--r--src/lib9/await.c4
-rw-r--r--src/lib9/dirfstat.c4
-rw-r--r--src/lib9/dirfwstat.c8
-rw-r--r--src/lib9/dirstat.c4
-rw-r--r--src/lib9/dirwstat.c4
-rw-r--r--src/lib9/execl.c2
-rw-r--r--src/lib9/flag.c22
-rw-r--r--src/lib9/fmt/dofmt.c88
-rw-r--r--src/lib9/fmt/dorfmt.c6
-rw-r--r--src/lib9/fmt/fltfmt.c26
-rw-r--r--src/lib9/fmt/fmt.c8
-rw-r--r--src/lib9/fmt/fmtdef.h6
-rw-r--r--src/lib9/fmt/fmtfdflush.c4
-rw-r--r--src/lib9/fmt/fmtquote.c12
-rw-r--r--src/lib9/fmt/fmtrune.c4
-rw-r--r--src/lib9/fmt/sprint.c4
-rw-r--r--src/lib9/fmt/strtod.c27
-rw-r--r--src/lib9/fmt/vsmprint.c6
-rw-r--r--src/lib9/fmt/vsnprint.c2
-rw-r--r--src/lib9/getwd.c2
-rw-r--r--src/lib9/readn.c2
-rw-r--r--src/lib9/rfork.c4
-rw-r--r--src/lib9/run_unix.c2
-rw-r--r--src/lib9/strecpy.c2
-rw-r--r--src/lib9/tempdir_unix.c2
-rw-r--r--src/lib9/tokenize.c8
-rw-r--r--src/lib9/utf/rune.c40
-rw-r--r--src/lib9/utf/utfecpy.c4
-rw-r--r--src/lib9/utf/utflen.c4
-rw-r--r--src/lib9/utf/utfnlen.c6
-rw-r--r--src/lib9/utf/utfrrune.c3
-rw-r--r--src/lib9/utf/utfrune.c3
-rw-r--r--src/lib9/utf/utfutf.c6
-rw-r--r--src/lib9/windows.c1
-rw-r--r--src/libbio/bflush.c2
-rw-r--r--src/libbio/bgetc.c24
-rw-r--r--src/libbio/bgetd.c2
-rw-r--r--src/libbio/bgetrune.c8
-rw-r--r--src/libbio/bprint.c4
-rw-r--r--src/libbio/bputc.c16
-rw-r--r--src/libbio/bputrune.c4
-rw-r--r--src/libbio/brdline.c16
-rw-r--r--src/libbio/brdstr.c4
-rw-r--r--src/libbio/bread.c8
-rw-r--r--src/libbio/bseek.c4
-rw-r--r--src/libbio/bwrite.c6
-rw-r--r--src/libmach/5obj.c26
-rw-r--r--src/libmach/6obj.c32
-rw-r--r--src/libmach/8db.c197
-rw-r--r--src/libmach/8obj.c30
-rw-r--r--src/libmach/darwin.c10
-rw-r--r--src/libmach/dragonfly.c62
-rw-r--r--src/libmach/executable.c2
-rw-r--r--src/libmach/freebsd.c16
-rw-r--r--src/libmach/macho.h1
-rw-r--r--src/libmach/netbsd.c10
-rw-r--r--src/libmach/obj.c8
-rw-r--r--src/libmach/openbsd.c10
-rw-r--r--src/libmach/sym.c372
-rw-r--r--src/libmach/windows.c16
-rwxr-xr-xsrc/make.bash14
-rw-r--r--src/make.bat2
-rwxr-xr-xsrc/make.rc2
-rw-r--r--src/pkg/archive/tar/common.go47
-rw-r--r--src/pkg/archive/tar/reader.go36
-rw-r--r--src/pkg/archive/tar/reader_test.go21
-rw-r--r--src/pkg/archive/tar/tar_test.go11
-rw-r--r--src/pkg/archive/tar/testdata/nil-uid.tarbin0 -> 1024 bytes
-rw-r--r--src/pkg/archive/tar/writer.go174
-rw-r--r--src/pkg/archive/tar/writer_test.go137
-rw-r--r--src/pkg/archive/zip/reader.go27
-rw-r--r--src/pkg/archive/zip/reader_test.go1
-rw-r--r--src/pkg/archive/zip/register.go71
-rw-r--r--src/pkg/archive/zip/struct.go6
-rw-r--r--src/pkg/archive/zip/writer.go18
-rw-r--r--src/pkg/archive/zip/zip_test.go170
-rw-r--r--src/pkg/bufio/bufio.go73
-rw-r--r--src/pkg/bufio/bufio_test.go75
-rw-r--r--src/pkg/bufio/example_test.go8
-rw-r--r--src/pkg/bufio/scan.go6
-rw-r--r--src/pkg/builtin/builtin.go13
-rw-r--r--src/pkg/bytes/asm_386.s17
-rw-r--r--src/pkg/bytes/asm_amd64.s91
-rw-r--r--src/pkg/bytes/asm_arm.s56
-rw-r--r--src/pkg/bytes/bytes.go33
-rw-r--r--src/pkg/bytes/bytes.s5
-rw-r--r--src/pkg/bytes/bytes_decl.go14
-rw-r--r--src/pkg/bytes/bytes_test.go60
-rw-r--r--src/pkg/bytes/compare_test.go204
-rw-r--r--src/pkg/bytes/reader_test.go35
-rw-r--r--src/pkg/compress/bzip2/bit_reader.go8
-rw-r--r--src/pkg/compress/bzip2/bzip2.go167
-rw-r--r--src/pkg/compress/bzip2/bzip2_test.go206
-rw-r--r--src/pkg/compress/bzip2/huffman.go9
-rw-r--r--src/pkg/compress/bzip2/move_to_front.go35
-rw-r--r--src/pkg/compress/bzip2/testdata/Mark.Twain-Tom.Sawyer.txt.bz2bin0 -> 124744 bytes
-rw-r--r--src/pkg/compress/bzip2/testdata/e.txt.bz2bin0 -> 43149 bytes
-rw-r--r--src/pkg/compress/flate/copy.go27
-rw-r--r--src/pkg/compress/flate/copy_test.go10
-rw-r--r--src/pkg/compress/flate/deflate.go67
-rw-r--r--src/pkg/compress/flate/deflate_test.go64
-rw-r--r--src/pkg/compress/flate/flate_test.go36
-rw-r--r--src/pkg/compress/flate/huffman_bit_writer.go25
-rw-r--r--src/pkg/compress/flate/huffman_code.go92
-rw-r--r--src/pkg/compress/flate/inflate.go19
-rw-r--r--src/pkg/compress/flate/reader_test.go5
-rw-r--r--src/pkg/compress/gzip/gunzip_test.go31
-rw-r--r--src/pkg/compress/gzip/gzip.go66
-rw-r--r--src/pkg/compress/gzip/gzip_test.go32
-rw-r--r--src/pkg/compress/gzip/testdata/issue6550.gzbin0 -> 65536 bytes
-rw-r--r--src/pkg/compress/zlib/writer.go29
-rw-r--r--src/pkg/compress/zlib/writer_test.go65
-rw-r--r--src/pkg/container/heap/example_intheap_test.go8
-rw-r--r--src/pkg/container/heap/heap.go13
-rw-r--r--src/pkg/container/heap/heap_test.go29
-rw-r--r--src/pkg/container/list/list.go25
-rw-r--r--src/pkg/container/list/list_test.go52
-rw-r--r--src/pkg/crypto/aes/asm_amd64.s20
-rw-r--r--src/pkg/crypto/cipher/cbc.go14
-rw-r--r--src/pkg/crypto/cipher/gcm.go350
-rw-r--r--src/pkg/crypto/cipher/gcm_test.go175
-rw-r--r--src/pkg/crypto/cipher/io.go11
-rw-r--r--src/pkg/crypto/crypto.go6
-rw-r--r--src/pkg/crypto/des/block.go137
-rw-r--r--src/pkg/crypto/des/des_test.go65
-rw-r--r--src/pkg/crypto/des/example_test.go25
-rw-r--r--src/pkg/crypto/ecdsa/ecdsa.go4
-rw-r--r--src/pkg/crypto/elliptic/elliptic.go12
-rw-r--r--src/pkg/crypto/elliptic/elliptic_test.go57
-rw-r--r--src/pkg/crypto/elliptic/p256.go1186
-rw-r--r--src/pkg/crypto/md5/example_test.go19
-rw-r--r--src/pkg/crypto/md5/gen.go2
-rw-r--r--src/pkg/crypto/md5/md5.go14
-rw-r--r--src/pkg/crypto/md5/md5_test.go32
-rw-r--r--src/pkg/crypto/md5/md5block.go2
-rw-r--r--src/pkg/crypto/md5/md5block_386.s4
-rw-r--r--src/pkg/crypto/md5/md5block_amd64.s4
-rw-r--r--src/pkg/crypto/md5/md5block_arm.s299
-rw-r--r--src/pkg/crypto/md5/md5block_decl.go4
-rw-r--r--src/pkg/crypto/rand/example_test.go5
-rw-r--r--src/pkg/crypto/rand/rand.go7
-rw-r--r--src/pkg/crypto/rand/rand_unix.go2
-rw-r--r--src/pkg/crypto/rc4/rc4_386.s4
-rw-r--r--src/pkg/crypto/rc4/rc4_amd64.s4
-rw-r--r--src/pkg/crypto/rc4/rc4_arm.s4
-rw-r--r--src/pkg/crypto/rsa/pkcs1v15.go6
-rw-r--r--src/pkg/crypto/rsa/pkcs1v15_test.go8
-rw-r--r--src/pkg/crypto/rsa/pss.go282
-rw-r--r--src/pkg/crypto/rsa/pss_test.go249
-rw-r--r--src/pkg/crypto/rsa/rsa.go2
-rw-r--r--src/pkg/crypto/rsa/rsa_test.go6
-rw-r--r--src/pkg/crypto/rsa/testdata/pss-vect.txt.bz2bin0 -> 28526 bytes
-rw-r--r--src/pkg/crypto/sha1/example_test.go18
-rw-r--r--src/pkg/crypto/sha1/sha1.go16
-rw-r--r--src/pkg/crypto/sha1/sha1_test.go11
-rw-r--r--src/pkg/crypto/sha1/sha1block_386.s4
-rw-r--r--src/pkg/crypto/sha1/sha1block_amd64.s4
-rw-r--r--src/pkg/crypto/sha1/sha1block_decl.go2
-rw-r--r--src/pkg/crypto/sha256/sha256.go32
-rw-r--r--src/pkg/crypto/sha256/sha256_test.go8
-rw-r--r--src/pkg/crypto/sha512/sha512.go30
-rw-r--r--src/pkg/crypto/sha512/sha512_test.go8
-rw-r--r--src/pkg/crypto/subtle/constant_time.go8
-rw-r--r--src/pkg/crypto/subtle/constant_time_test.go20
-rw-r--r--src/pkg/crypto/tls/cipher_suites.go146
-rw-r--r--src/pkg/crypto/tls/common.go146
-rw-r--r--src/pkg/crypto/tls/conn.go192
-rw-r--r--src/pkg/crypto/tls/generate_cert.go2
-rw-r--r--src/pkg/crypto/tls/handshake_client.go161
-rw-r--r--src/pkg/crypto/tls/handshake_client_test.go2654
-rw-r--r--src/pkg/crypto/tls/handshake_messages.go148
-rw-r--r--src/pkg/crypto/tls/handshake_messages_test.go3
-rw-r--r--src/pkg/crypto/tls/handshake_server.go121
-rw-r--r--src/pkg/crypto/tls/handshake_server_test.go2401
-rw-r--r--src/pkg/crypto/tls/key_agreement.go183
-rw-r--r--src/pkg/crypto/tls/prf.go141
-rw-r--r--src/pkg/crypto/tls/prf_test.go8
-rw-r--r--src/pkg/crypto/tls/tls.go2
-rw-r--r--src/pkg/crypto/x509/cert_pool.go11
-rw-r--r--src/pkg/crypto/x509/pkcs1.go4
-rw-r--r--src/pkg/crypto/x509/pkcs8.go6
-rw-r--r--src/pkg/crypto/x509/pkix/pkix.go2
-rw-r--r--src/pkg/crypto/x509/root_unix.go6
-rw-r--r--src/pkg/crypto/x509/root_windows.go6
-rw-r--r--src/pkg/crypto/x509/sec1.go22
-rw-r--r--src/pkg/crypto/x509/sec1_test.go10
-rw-r--r--src/pkg/crypto/x509/verify.go40
-rw-r--r--src/pkg/crypto/x509/verify_test.go273
-rw-r--r--src/pkg/crypto/x509/x509.go299
-rw-r--r--src/pkg/crypto/x509/x509_test.go46
-rw-r--r--src/pkg/database/sql/convert_test.go4
-rw-r--r--src/pkg/database/sql/driver/driver.go4
-rw-r--r--src/pkg/database/sql/driver/types_test.go4
-rw-r--r--src/pkg/database/sql/fakedb_test.go50
-rw-r--r--src/pkg/database/sql/sql.go380
-rw-r--r--src/pkg/database/sql/sql_test.go759
-rw-r--r--src/pkg/debug/dwarf/const.go3
-rw-r--r--src/pkg/debug/dwarf/entry.go32
-rw-r--r--src/pkg/debug/dwarf/type.go43
-rw-r--r--src/pkg/debug/elf/file_test.go1
-rw-r--r--src/pkg/debug/gosym/pclinetest.asm20
-rw-r--r--src/pkg/debug/gosym/pclntab.go360
-rw-r--r--src/pkg/debug/gosym/pclntab_test.go46
-rw-r--r--src/pkg/debug/gosym/symtab.go93
-rw-r--r--src/pkg/encoding/asn1/asn1.go44
-rw-r--r--src/pkg/encoding/asn1/asn1_test.go29
-rw-r--r--src/pkg/encoding/asn1/marshal.go14
-rw-r--r--src/pkg/encoding/asn1/marshal_test.go1
-rw-r--r--src/pkg/encoding/binary/binary.go243
-rw-r--r--src/pkg/encoding/binary/binary_test.go59
-rw-r--r--src/pkg/encoding/binary/example_test.go2
-rw-r--r--src/pkg/encoding/csv/reader.go58
-rw-r--r--src/pkg/encoding/csv/reader_test.go33
-rw-r--r--src/pkg/encoding/encoding.go48
-rw-r--r--src/pkg/encoding/gob/codec_test.go18
-rw-r--r--src/pkg/encoding/gob/debug.go10
-rw-r--r--src/pkg/encoding/gob/decode.go89
-rw-r--r--src/pkg/encoding/gob/doc.go40
-rw-r--r--src/pkg/encoding/gob/encode.go66
-rw-r--r--src/pkg/encoding/gob/encoder.go12
-rw-r--r--src/pkg/encoding/gob/encoder_test.go42
-rw-r--r--src/pkg/encoding/gob/example_encdec_test.go61
-rw-r--r--src/pkg/encoding/gob/example_interface_test.go81
-rw-r--r--src/pkg/encoding/gob/example_test.go60
-rw-r--r--src/pkg/encoding/gob/gobencdec_test.go161
-rw-r--r--src/pkg/encoding/gob/timing_test.go15
-rw-r--r--src/pkg/encoding/gob/type.go111
-rw-r--r--src/pkg/encoding/json/decode.go70
-rw-r--r--src/pkg/encoding/json/decode_test.go167
-rw-r--r--src/pkg/encoding/json/encode.go714
-rw-r--r--src/pkg/encoding/json/encode_test.go123
-rw-r--r--src/pkg/encoding/json/example_test.go46
-rw-r--r--src/pkg/encoding/json/indent.go9
-rw-r--r--src/pkg/encoding/json/scanner.go2
-rw-r--r--src/pkg/encoding/json/scanner_test.go19
-rw-r--r--src/pkg/encoding/json/stream.go11
-rw-r--r--src/pkg/encoding/json/stream_test.go13
-rw-r--r--src/pkg/encoding/json/tags.go2
-rw-r--r--src/pkg/encoding/xml/marshal.go575
-rw-r--r--src/pkg/encoding/xml/marshal_test.go128
-rw-r--r--src/pkg/encoding/xml/read.go208
-rw-r--r--src/pkg/encoding/xml/read_test.go64
-rw-r--r--src/pkg/encoding/xml/xml.go160
-rw-r--r--src/pkg/encoding/xml/xml_test.go14
-rw-r--r--src/pkg/flag/export_test.go7
-rw-r--r--src/pkg/flag/flag.go113
-rw-r--r--src/pkg/flag/flag_test.go50
-rw-r--r--src/pkg/fmt/doc.go33
-rw-r--r--src/pkg/fmt/fmt_test.go89
-rw-r--r--src/pkg/fmt/format.go23
-rw-r--r--src/pkg/fmt/print.go282
-rw-r--r--src/pkg/fmt/scan.go67
-rw-r--r--src/pkg/fmt/scan_test.go5
-rw-r--r--src/pkg/go/ast/ast.go76
-rw-r--r--src/pkg/go/ast/commentmap.go10
-rw-r--r--src/pkg/go/ast/filter.go2
-rw-r--r--src/pkg/go/ast/import.go98
-rw-r--r--src/pkg/go/ast/walk.go3
-rw-r--r--src/pkg/go/build/build.go285
-rw-r--r--src/pkg/go/build/build_test.go105
-rw-r--r--src/pkg/go/build/deps_test.go57
-rw-r--r--src/pkg/go/build/doc.go1
-rw-r--r--src/pkg/go/build/syslist.go2
-rw-r--r--src/pkg/go/build/syslist_test.go2
-rw-r--r--src/pkg/go/doc/comment.go11
-rw-r--r--src/pkg/go/doc/doc_test.go10
-rw-r--r--src/pkg/go/doc/example.go2
-rw-r--r--src/pkg/go/doc/example_test.go10
-rw-r--r--src/pkg/go/doc/reader.go2
-rw-r--r--src/pkg/go/doc/synopsis.go15
-rw-r--r--src/pkg/go/doc/synopsis_test.go2
-rw-r--r--src/pkg/go/doc/testdata/a.0.golden46
-rw-r--r--src/pkg/go/doc/testdata/a.1.golden46
-rw-r--r--src/pkg/go/doc/testdata/a.2.golden46
-rw-r--r--src/pkg/go/doc/testdata/bugpara.0.golden20
-rw-r--r--src/pkg/go/doc/testdata/bugpara.1.golden20
-rw-r--r--src/pkg/go/doc/testdata/bugpara.2.golden20
-rw-r--r--src/pkg/go/doc/testdata/bugpara.go5
-rw-r--r--src/pkg/go/doc/testdata/template.txt4
-rw-r--r--src/pkg/go/doc/testdata/testing.0.golden4
-rw-r--r--src/pkg/go/doc/testdata/testing.1.golden6
-rw-r--r--src/pkg/go/doc/testdata/testing.2.golden4
-rw-r--r--src/pkg/go/doc/testdata/testing.go2
-rw-r--r--src/pkg/go/format/format_test.go1
-rw-r--r--src/pkg/go/parser/interface.go15
-rw-r--r--src/pkg/go/parser/parser.go50
-rw-r--r--src/pkg/go/parser/parser_test.go18
-rw-r--r--src/pkg/go/parser/short_test.go6
-rw-r--r--src/pkg/go/printer/nodes.go32
-rw-r--r--src/pkg/go/printer/testdata/expressions.golden17
-rw-r--r--src/pkg/go/printer/testdata/expressions.input17
-rw-r--r--src/pkg/go/printer/testdata/expressions.raw17
-rw-r--r--src/pkg/go/token/position.go31
-rw-r--r--src/pkg/go/token/position_test.go8
-rw-r--r--src/pkg/hash/crc32/crc32_amd64.s6
-rw-r--r--src/pkg/hash/hash.go4
-rw-r--r--src/pkg/html/escape.go10
-rw-r--r--src/pkg/html/escape_test.go97
-rw-r--r--src/pkg/html/template/clone_test.go40
-rw-r--r--src/pkg/html/template/content.go6
-rw-r--r--src/pkg/html/template/content_test.go31
-rw-r--r--src/pkg/html/template/context.go2
-rw-r--r--src/pkg/html/template/css.go8
-rw-r--r--src/pkg/html/template/escape.go4
-rw-r--r--src/pkg/html/template/escape_test.go11
-rw-r--r--src/pkg/html/template/js.go2
-rw-r--r--src/pkg/html/template/template.go18
-rw-r--r--src/pkg/html/template/transition.go4
-rw-r--r--src/pkg/image/color/color.go26
-rw-r--r--src/pkg/image/color/palette/gen.go97
-rw-r--r--src/pkg/image/color/palette/palette.go500
-rw-r--r--src/pkg/image/decode_example_test.go113
-rw-r--r--src/pkg/image/draw/draw.go209
-rw-r--r--src/pkg/image/draw/draw_test.go75
-rw-r--r--src/pkg/image/format.go6
-rw-r--r--src/pkg/image/geom.go12
-rw-r--r--src/pkg/image/gif/reader.go13
-rw-r--r--src/pkg/image/gif/reader_test.go83
-rw-r--r--src/pkg/image/gif/writer.go323
-rw-r--r--src/pkg/image/gif/writer_test.go204
-rw-r--r--src/pkg/image/image.go18
-rw-r--r--src/pkg/image/jpeg/dct_test.go2
-rw-r--r--src/pkg/image/jpeg/reader.go4
-rw-r--r--src/pkg/image/names.go2
-rw-r--r--src/pkg/image/testdata/video-005.gray.gifbin0 -> 14505 bytes
-rw-r--r--src/pkg/io/io.go28
-rw-r--r--src/pkg/io/io_test.go68
-rw-r--r--src/pkg/io/ioutil/ioutil.go8
-rw-r--r--src/pkg/io/pipe.go4
-rw-r--r--src/pkg/io/pipe_test.go32
-rw-r--r--src/pkg/log/syslog/syslog.go59
-rw-r--r--src/pkg/log/syslog/syslog_test.go34
-rw-r--r--src/pkg/log/syslog/syslog_unix.go4
-rw-r--r--src/pkg/math/abs_386.s4
-rw-r--r--src/pkg/math/abs_amd64.s4
-rw-r--r--src/pkg/math/abs_arm.s4
-rw-r--r--src/pkg/math/asin.go4
-rw-r--r--src/pkg/math/asin_386.s6
-rw-r--r--src/pkg/math/asin_amd64.s6
-rw-r--r--src/pkg/math/asin_arm.s6
-rw-r--r--src/pkg/math/atan.go2
-rw-r--r--src/pkg/math/atan2_386.s4
-rw-r--r--src/pkg/math/atan2_amd64.s4
-rw-r--r--src/pkg/math/atan2_arm.s4
-rw-r--r--src/pkg/math/atan_386.s4
-rw-r--r--src/pkg/math/atan_amd64.s4
-rw-r--r--src/pkg/math/atan_arm.s4
-rw-r--r--src/pkg/math/big/arith_386.s26
-rw-r--r--src/pkg/math/big/arith_amd64.s26
-rw-r--r--src/pkg/math/big/arith_arm.s26
-rw-r--r--src/pkg/math/big/int.go18
-rw-r--r--src/pkg/math/big/int_test.go28
-rw-r--r--src/pkg/math/big/nat_test.go2
-rw-r--r--src/pkg/math/big/rat.go12
-rw-r--r--src/pkg/math/big/rat_test.go26
-rw-r--r--src/pkg/math/bits.go10
-rw-r--r--src/pkg/math/dim_386.s8
-rw-r--r--src/pkg/math/dim_amd64.s8
-rw-r--r--src/pkg/math/dim_arm.s8
-rw-r--r--src/pkg/math/exp2_386.s4
-rw-r--r--src/pkg/math/exp2_amd64.s4
-rw-r--r--src/pkg/math/exp2_arm.s4
-rw-r--r--src/pkg/math/exp_386.s4
-rw-r--r--src/pkg/math/exp_amd64.s4
-rw-r--r--src/pkg/math/exp_arm.s4
-rw-r--r--src/pkg/math/expm1_386.s4
-rw-r--r--src/pkg/math/expm1_amd64.s4
-rw-r--r--src/pkg/math/expm1_arm.s4
-rw-r--r--src/pkg/math/floor_386.s8
-rw-r--r--src/pkg/math/floor_amd64.s8
-rw-r--r--src/pkg/math/floor_arm.s8
-rw-r--r--src/pkg/math/fltasm_amd64.s67
-rw-r--r--src/pkg/math/frexp_386.s4
-rw-r--r--src/pkg/math/frexp_amd64.s4
-rw-r--r--src/pkg/math/frexp_arm.s4
-rw-r--r--src/pkg/math/hypot_386.s4
-rw-r--r--src/pkg/math/hypot_amd64.s4
-rw-r--r--src/pkg/math/hypot_arm.s4
-rw-r--r--src/pkg/math/ldexp_386.s4
-rw-r--r--src/pkg/math/ldexp_amd64.s4
-rw-r--r--src/pkg/math/ldexp_arm.s4
-rw-r--r--src/pkg/math/log10_386.s6
-rw-r--r--src/pkg/math/log10_amd64.s6
-rw-r--r--src/pkg/math/log10_arm.s6
-rw-r--r--src/pkg/math/log1p_386.s4
-rw-r--r--src/pkg/math/log1p_amd64.s4
-rw-r--r--src/pkg/math/log1p_arm.s4
-rw-r--r--src/pkg/math/log_386.s4
-rw-r--r--src/pkg/math/log_amd64.s4
-rw-r--r--src/pkg/math/log_arm.s4
-rw-r--r--src/pkg/math/mod_386.s4
-rw-r--r--src/pkg/math/mod_amd64.s4
-rw-r--r--src/pkg/math/mod_arm.s4
-rw-r--r--src/pkg/math/modf_386.s4
-rw-r--r--src/pkg/math/modf_amd64.s4
-rw-r--r--src/pkg/math/modf_arm.s4
-rw-r--r--src/pkg/math/rand/example_test.go32
-rw-r--r--src/pkg/math/rand/rand.go42
-rw-r--r--src/pkg/math/remainder_386.s4
-rw-r--r--src/pkg/math/remainder_amd64.s4
-rw-r--r--src/pkg/math/remainder_arm.s4
-rw-r--r--src/pkg/math/sin.go4
-rw-r--r--src/pkg/math/sin_386.s6
-rw-r--r--src/pkg/math/sin_amd64.s6
-rw-r--r--src/pkg/math/sin_arm.s6
-rw-r--r--src/pkg/math/sincos_386.s4
-rw-r--r--src/pkg/math/sincos_amd64.s4
-rw-r--r--src/pkg/math/sincos_arm.s4
-rw-r--r--src/pkg/math/sqrt_386.s4
-rw-r--r--src/pkg/math/sqrt_amd64.s4
-rw-r--r--src/pkg/math/sqrt_arm.s4
-rw-r--r--src/pkg/math/tan.go2
-rw-r--r--src/pkg/math/tan_386.s4
-rw-r--r--src/pkg/math/tan_amd64.s4
-rw-r--r--src/pkg/math/tan_arm.s4
-rw-r--r--src/pkg/mime/grammar.go13
-rw-r--r--src/pkg/mime/mediatype.go2
-rw-r--r--src/pkg/mime/mediatype_test.go9
-rw-r--r--src/pkg/mime/multipart/multipart.go6
-rw-r--r--src/pkg/mime/testdata/test.types.plan98
-rw-r--r--src/pkg/mime/type_plan9.go53
-rw-r--r--src/pkg/mime/type_unix.go2
-rw-r--r--src/pkg/net/cgo_bsd.go3
-rw-r--r--src/pkg/net/cgo_linux.go2
-rw-r--r--src/pkg/net/cgo_netbsd.go2
-rw-r--r--src/pkg/net/cgo_openbsd.go2
-rw-r--r--src/pkg/net/cgo_stub.go2
-rw-r--r--src/pkg/net/cgo_unix.go25
-rw-r--r--src/pkg/net/dial.go128
-rw-r--r--src/pkg/net/dial_gen.go51
-rw-r--r--src/pkg/net/dial_test.go178
-rw-r--r--src/pkg/net/dialgoogle_test.go77
-rw-r--r--src/pkg/net/dnsclient.go12
-rw-r--r--src/pkg/net/dnsclient_unix.go51
-rw-r--r--src/pkg/net/dnsclient_unix_test.go27
-rw-r--r--src/pkg/net/dnsconfig_unix.go2
-rw-r--r--src/pkg/net/dnsname_test.go20
-rw-r--r--src/pkg/net/fd_bsd.go123
-rw-r--r--src/pkg/net/fd_mutex.go184
-rw-r--r--src/pkg/net/fd_mutex_test.go186
-rw-r--r--src/pkg/net/fd_plan9.go16
-rw-r--r--src/pkg/net/fd_poll_runtime.go66
-rw-r--r--src/pkg/net/fd_poll_unix.go360
-rw-r--r--src/pkg/net/fd_posix_test.go57
-rw-r--r--src/pkg/net/fd_unix.go238
-rw-r--r--src/pkg/net/fd_unix_test.go2
-rw-r--r--src/pkg/net/fd_windows.go715
-rw-r--r--src/pkg/net/file_unix.go13
-rw-r--r--src/pkg/net/hosts_test.go13
-rw-r--r--src/pkg/net/http/cgi/child.go23
-rw-r--r--src/pkg/net/http/cgi/child_test.go28
-rw-r--r--src/pkg/net/http/client.go21
-rw-r--r--src/pkg/net/http/client_test.go99
-rw-r--r--src/pkg/net/http/cookie.go135
-rw-r--r--src/pkg/net/http/cookie_test.go49
-rw-r--r--src/pkg/net/http/cookiejar/jar.go8
-rw-r--r--src/pkg/net/http/doc.go2
-rw-r--r--src/pkg/net/http/example_test.go18
-rw-r--r--src/pkg/net/http/export_test.go8
-rw-r--r--src/pkg/net/http/fs.go56
-rw-r--r--src/pkg/net/http/fs_test.go59
-rw-r--r--src/pkg/net/http/header.go2
-rw-r--r--src/pkg/net/http/header_test.go5
-rw-r--r--src/pkg/net/http/httputil/dump.go28
-rw-r--r--src/pkg/net/http/httputil/dump_test.go30
-rw-r--r--src/pkg/net/http/request.go28
-rw-r--r--src/pkg/net/http/request_test.go2
-rw-r--r--src/pkg/net/http/response.go25
-rw-r--r--src/pkg/net/http/response_test.go62
-rw-r--r--src/pkg/net/http/serve_test.go456
-rw-r--r--src/pkg/net/http/server.go280
-rw-r--r--src/pkg/net/http/server_test.go104
-rw-r--r--src/pkg/net/http/sniff_test.go24
-rw-r--r--src/pkg/net/http/transfer.go44
-rw-r--r--src/pkg/net/http/transport.go40
-rw-r--r--src/pkg/net/http/transport_test.go94
-rw-r--r--src/pkg/net/http/z_last_test.go1
-rw-r--r--src/pkg/net/interface_bsd.go2
-rw-r--r--src/pkg/net/interface_bsd_test.go2
-rw-r--r--src/pkg/net/interface_dragonfly.go12
-rw-r--r--src/pkg/net/interface_linux_test.go2
-rw-r--r--src/pkg/net/interface_test.go17
-rw-r--r--src/pkg/net/interface_unix_test.go2
-rw-r--r--src/pkg/net/ip.go39
-rw-r--r--src/pkg/net/ip_test.go38
-rw-r--r--src/pkg/net/ipraw_test.go189
-rw-r--r--src/pkg/net/iprawsock.go9
-rw-r--r--src/pkg/net/iprawsock_posix.go48
-rw-r--r--src/pkg/net/ipsock.go186
-rw-r--r--src/pkg/net/ipsock_plan9.go9
-rw-r--r--src/pkg/net/ipsock_posix.go55
-rw-r--r--src/pkg/net/ipsock_test.go193
-rw-r--r--src/pkg/net/lookup.go58
-rw-r--r--src/pkg/net/lookup_plan9.go8
-rw-r--r--src/pkg/net/lookup_unix.go16
-rw-r--r--src/pkg/net/lookup_windows.go30
-rw-r--r--src/pkg/net/mail/message.go6
-rw-r--r--src/pkg/net/mail/message_test.go10
-rw-r--r--src/pkg/net/mockicmp_test.go116
-rw-r--r--src/pkg/net/mockserver_test.go82
-rw-r--r--src/pkg/net/multicast_test.go2
-rw-r--r--src/pkg/net/net.go54
-rw-r--r--src/pkg/net/net_test.go44
-rw-r--r--src/pkg/net/packetconn_test.go106
-rw-r--r--src/pkg/net/parse.go2
-rw-r--r--src/pkg/net/parse_test.go2
-rw-r--r--src/pkg/net/port_unix.go2
-rw-r--r--src/pkg/net/protoconn_test.go40
-rw-r--r--src/pkg/net/race.go31
-rw-r--r--src/pkg/net/race0.go26
-rw-r--r--src/pkg/net/rpc/client.go7
-rw-r--r--src/pkg/net/rpc/debug.go3
-rw-r--r--src/pkg/net/rpc/jsonrpc/server.go3
-rw-r--r--src/pkg/net/rpc/server.go24
-rw-r--r--src/pkg/net/rpc/server_test.go52
-rw-r--r--src/pkg/net/sendfile_dragonfly.go103
-rw-r--r--src/pkg/net/sendfile_freebsd.go6
-rw-r--r--src/pkg/net/sendfile_linux.go6
-rw-r--r--src/pkg/net/sendfile_windows.go35
-rw-r--r--src/pkg/net/singleflight.go53
-rw-r--r--src/pkg/net/smtp/smtp.go22
-rw-r--r--src/pkg/net/smtp/smtp_test.go47
-rw-r--r--src/pkg/net/sock_bsd.go2
-rw-r--r--src/pkg/net/sock_plan9.go10
-rw-r--r--src/pkg/net/sock_posix.go213
-rw-r--r--src/pkg/net/sock_unix.go36
-rw-r--r--src/pkg/net/sock_windows.go27
-rw-r--r--src/pkg/net/sockopt_bsd.go42
-rw-r--r--src/pkg/net/sockopt_linux.go37
-rw-r--r--src/pkg/net/sockopt_posix.go20
-rw-r--r--src/pkg/net/sockopt_windows.go44
-rw-r--r--src/pkg/net/sockoptip_bsd.go18
-rw-r--r--src/pkg/net/sockoptip_linux.go16
-rw-r--r--src/pkg/net/sockoptip_posix.go34
-rw-r--r--src/pkg/net/sockoptip_windows.go17
-rw-r--r--src/pkg/net/sys_cloexec.go2
-rw-r--r--src/pkg/net/tcp_test.go310
-rw-r--r--src/pkg/net/tcpsock.go14
-rw-r--r--src/pkg/net/tcpsock_plan9.go9
-rw-r--r--src/pkg/net/tcpsock_posix.go45
-rw-r--r--src/pkg/net/tcpsockopt_darwin.go27
-rw-r--r--src/pkg/net/tcpsockopt_openbsd.go27
-rw-r--r--src/pkg/net/tcpsockopt_posix.go20
-rw-r--r--src/pkg/net/tcpsockopt_unix.go31
-rw-r--r--src/pkg/net/tcpsockopt_windows.go21
-rw-r--r--src/pkg/net/testdata/hosts_singleline1
-rw-r--r--src/pkg/net/textproto/reader.go60
-rw-r--r--src/pkg/net/textproto/reader_test.go4
-rw-r--r--src/pkg/net/textproto/textproto.go2
-rw-r--r--src/pkg/net/timeout_test.go47
-rw-r--r--src/pkg/net/udp_test.go58
-rw-r--r--src/pkg/net/udpsock.go14
-rw-r--r--src/pkg/net/udpsock_plan9.go3
-rw-r--r--src/pkg/net/udpsock_posix.go52
-rw-r--r--src/pkg/net/unicast_posix_test.go12
-rw-r--r--src/pkg/net/unix_test.go17
-rw-r--r--src/pkg/net/unixsock.go4
-rw-r--r--src/pkg/net/unixsock_plan9.go2
-rw-r--r--src/pkg/net/unixsock_posix.go89
-rw-r--r--src/pkg/net/url/url.go11
-rw-r--r--src/pkg/net/url/url_test.go24
-rw-r--r--src/pkg/os/dir_unix.go2
-rw-r--r--src/pkg/os/doc.go8
-rw-r--r--src/pkg/os/env_unix_test.go2
-rw-r--r--src/pkg/os/error.go15
-rw-r--r--src/pkg/os/error_unix.go (renamed from src/pkg/os/error_posix.go)2
-rw-r--r--src/pkg/os/exec/exec.go38
-rw-r--r--src/pkg/os/exec/exec_test.go86
-rw-r--r--src/pkg/os/exec/lp_plan9.go1
-rw-r--r--src/pkg/os/exec/lp_unix.go3
-rw-r--r--src/pkg/os/exec/lp_unix_test.go2
-rw-r--r--src/pkg/os/exec/lp_windows.go16
-rw-r--r--src/pkg/os/exec/lp_windows_test.go391
-rw-r--r--src/pkg/os/exec_posix.go2
-rw-r--r--src/pkg/os/exec_unix.go2
-rw-r--r--src/pkg/os/exec_windows.go13
-rw-r--r--src/pkg/os/export_test.go1
-rw-r--r--src/pkg/os/file.go9
-rw-r--r--src/pkg/os/file_plan9.go17
-rw-r--r--src/pkg/os/file_posix.go11
-rw-r--r--src/pkg/os/file_unix.go16
-rw-r--r--src/pkg/os/file_windows.go15
-rw-r--r--src/pkg/os/getwd.go8
-rw-r--r--src/pkg/os/getwd_darwin.go15
-rw-r--r--src/pkg/os/os_test.go101
-rw-r--r--src/pkg/os/os_unix_test.go42
-rw-r--r--src/pkg/os/path_test.go6
-rw-r--r--src/pkg/os/path_unix.go2
-rw-r--r--src/pkg/os/pipe_bsd.go2
-rw-r--r--src/pkg/os/signal/sig.s8
-rw-r--r--src/pkg/os/signal/signal_test.go10
-rw-r--r--src/pkg/os/signal/signal_unix.go2
-rw-r--r--src/pkg/os/stat_dragonfly.go61
-rw-r--r--src/pkg/os/stat_windows.go3
-rw-r--r--src/pkg/os/sys_bsd.go2
-rw-r--r--src/pkg/os/user/lookup_plan9.go46
-rw-r--r--src/pkg/os/user/lookup_stubs.go2
-rw-r--r--src/pkg/os/user/lookup_unix.go10
-rw-r--r--src/pkg/os/user/lookup_windows.go77
-rw-r--r--src/pkg/os/user/user.go2
-rw-r--r--src/pkg/os/user/user_test.go14
-rw-r--r--src/pkg/path/filepath/match.go6
-rw-r--r--src/pkg/path/filepath/match_test.go5
-rw-r--r--src/pkg/path/filepath/path_test.go30
-rw-r--r--src/pkg/path/filepath/path_unix.go2
-rw-r--r--src/pkg/path/match_test.go5
-rw-r--r--src/pkg/path/path_test.go5
-rw-r--r--src/pkg/reflect/all_test.go203
-rw-r--r--src/pkg/reflect/asm_386.s8
-rw-r--r--src/pkg/reflect/asm_amd64.s8
-rw-r--r--src/pkg/reflect/asm_arm.s8
-rw-r--r--src/pkg/reflect/deepequal.go39
-rw-r--r--src/pkg/reflect/example_test.go14
-rw-r--r--src/pkg/reflect/makefunc.go2
-rw-r--r--src/pkg/reflect/type.go146
-rw-r--r--src/pkg/reflect/value.go117
-rw-r--r--src/pkg/regexp/all_test.go8
-rw-r--r--src/pkg/regexp/exec2_test.go20
-rw-r--r--src/pkg/regexp/exec_test.go20
-rw-r--r--src/pkg/regexp/regexp.go11
-rw-r--r--src/pkg/regexp/syntax/doc.go8
-rw-r--r--src/pkg/regexp/syntax/parse.go2
-rw-r--r--src/pkg/regexp/syntax/parse_test.go2
-rw-r--r--src/pkg/regexp/syntax/prog.go29
-rw-r--r--src/pkg/regexp/syntax/prog_test.go11
-rw-r--r--src/pkg/runtime/alg.c29
-rw-r--r--src/pkg/runtime/append_test.go48
-rw-r--r--src/pkg/runtime/arch_386.h3
-rw-r--r--src/pkg/runtime/arch_amd64.h3
-rw-r--r--src/pkg/runtime/arch_arm.h3
-rw-r--r--src/pkg/runtime/asm_386.s844
-rw-r--r--src/pkg/runtime/asm_amd64.s837
-rw-r--r--src/pkg/runtime/asm_arm.s493
-rw-r--r--src/pkg/runtime/atomic_386.c25
-rw-r--r--src/pkg/runtime/atomic_amd64.c7
-rw-r--r--src/pkg/runtime/atomic_arm.c30
-rw-r--r--src/pkg/runtime/callback_windows.c (renamed from src/pkg/runtime/callback_windows_386.c)84
-rw-r--r--src/pkg/runtime/callback_windows_amd64.c105
-rw-r--r--src/pkg/runtime/cgo/asm_386.s4
-rw-r--r--src/pkg/runtime/cgo/asm_amd64.s4
-rw-r--r--src/pkg/runtime/cgo/asm_arm.s13
-rw-r--r--src/pkg/runtime/cgo/callbacks.c3
-rw-r--r--src/pkg/runtime/cgo/cgo.go1
-rw-r--r--src/pkg/runtime/cgo/cgo_arm.c12
-rw-r--r--src/pkg/runtime/cgo/dragonfly.c13
-rw-r--r--src/pkg/runtime/cgo/gcc_arm.S21
-rw-r--r--src/pkg/runtime/cgo/gcc_dragonfly_386.c77
-rw-r--r--src/pkg/runtime/cgo/gcc_dragonfly_amd64.c77
-rw-r--r--src/pkg/runtime/cgo/gcc_freebsd_arm.c64
-rw-r--r--src/pkg/runtime/cgo/gcc_linux_arm.c55
-rw-r--r--src/pkg/runtime/cgo/gcc_netbsd_arm.c59
-rw-r--r--src/pkg/runtime/cgo/gcc_setenv.c2
-rw-r--r--src/pkg/runtime/cgo/gcc_util.c2
-rw-r--r--src/pkg/runtime/cgo/iscgo.c1
-rw-r--r--src/pkg/runtime/cgo/setenv.c2
-rw-r--r--src/pkg/runtime/cgocall.c102
-rw-r--r--src/pkg/runtime/chan.c116
-rw-r--r--src/pkg/runtime/cpuprof.c12
-rw-r--r--src/pkg/runtime/crash_cgo_test.go31
-rw-r--r--src/pkg/runtime/crash_test.go83
-rw-r--r--src/pkg/runtime/debug/garbage.go34
-rw-r--r--src/pkg/runtime/defs_dragonfly.go126
-rw-r--r--src/pkg/runtime/defs_dragonfly_386.h198
-rw-r--r--src/pkg/runtime/defs_dragonfly_amd64.h208
-rw-r--r--src/pkg/runtime/defs_freebsd.go16
-rw-r--r--src/pkg/runtime/defs_freebsd_386.h29
-rw-r--r--src/pkg/runtime/defs_freebsd_amd64.h27
-rw-r--r--src/pkg/runtime/defs_freebsd_arm.h23
-rw-r--r--src/pkg/runtime/defs_netbsd.go16
-rw-r--r--src/pkg/runtime/defs_netbsd_386.h23
-rw-r--r--src/pkg/runtime/defs_netbsd_amd64.h24
-rw-r--r--src/pkg/runtime/defs_netbsd_arm.h24
-rw-r--r--src/pkg/runtime/defs_openbsd.go15
-rw-r--r--src/pkg/runtime/defs_openbsd_386.h22
-rw-r--r--src/pkg/runtime/defs_openbsd_amd64.h22
-rw-r--r--src/pkg/runtime/defs_windows.go8
-rw-r--r--src/pkg/runtime/defs_windows_386.h180
-rw-r--r--src/pkg/runtime/defs_windows_amd64.h210
-rw-r--r--src/pkg/runtime/env_plan9.c3
-rw-r--r--src/pkg/runtime/env_posix.c5
-rw-r--r--src/pkg/runtime/error.go16
-rw-r--r--src/pkg/runtime/export_futex_test.go2
-rw-r--r--src/pkg/runtime/export_test.c13
-rw-r--r--src/pkg/runtime/export_test.go17
-rw-r--r--src/pkg/runtime/extern.go50
-rw-r--r--src/pkg/runtime/funcdata.h22
-rw-r--r--src/pkg/runtime/futex_test.go5
-rw-r--r--src/pkg/runtime/gc_test.go4
-rw-r--r--src/pkg/runtime/hash_test.go512
-rw-r--r--src/pkg/runtime/hashmap.c446
-rw-r--r--src/pkg/runtime/hashmap.h33
-rw-r--r--src/pkg/runtime/hashmap_fast.c164
-rw-r--r--src/pkg/runtime/iface.c27
-rw-r--r--src/pkg/runtime/lfstack.c8
-rw-r--r--src/pkg/runtime/lock_futex.c74
-rw-r--r--src/pkg/runtime/lock_sema.c99
-rw-r--r--src/pkg/runtime/malloc.goc438
-rw-r--r--src/pkg/runtime/malloc.h110
-rw-r--r--src/pkg/runtime/malloc_test.go156
-rw-r--r--src/pkg/runtime/map_test.go38
-rw-r--r--src/pkg/runtime/mapspeed_test.go62
-rw-r--r--src/pkg/runtime/mcache.c88
-rw-r--r--src/pkg/runtime/mcentral.c61
-rw-r--r--src/pkg/runtime/mem.go4
-rw-r--r--src/pkg/runtime/mem_darwin.c19
-rw-r--r--src/pkg/runtime/mem_dragonfly.c94
-rw-r--r--src/pkg/runtime/mem_freebsd.c19
-rw-r--r--src/pkg/runtime/mem_linux.c28
-rw-r--r--src/pkg/runtime/mem_netbsd.c19
-rw-r--r--src/pkg/runtime/mem_openbsd.c19
-rw-r--r--src/pkg/runtime/mem_plan9.c20
-rw-r--r--src/pkg/runtime/mem_windows.c40
-rw-r--r--src/pkg/runtime/memclr_arm.s4
-rw-r--r--src/pkg/runtime/memmove_386.s113
-rw-r--r--src/pkg/runtime/memmove_amd64.s140
-rw-r--r--src/pkg/runtime/memmove_arm.s20
-rw-r--r--src/pkg/runtime/memmove_test.go116
-rw-r--r--src/pkg/runtime/mfinal.c22
-rw-r--r--src/pkg/runtime/mfinal_test.go86
-rw-r--r--src/pkg/runtime/mfixalloc.c10
-rw-r--r--src/pkg/runtime/mgc0.c804
-rw-r--r--src/pkg/runtime/mgc0.h1
-rw-r--r--src/pkg/runtime/mheap.c140
-rw-r--r--src/pkg/runtime/mkversion.c21
-rw-r--r--src/pkg/runtime/mprof.goc88
-rw-r--r--src/pkg/runtime/msize.c39
-rw-r--r--src/pkg/runtime/netpoll.goc97
-rw-r--r--src/pkg/runtime/netpoll_epoll.c8
-rw-r--r--src/pkg/runtime/netpoll_kqueue.c32
-rw-r--r--src/pkg/runtime/netpoll_stub.c2
-rw-r--r--src/pkg/runtime/netpoll_windows.c148
-rw-r--r--src/pkg/runtime/noasm_arm.goc74
-rw-r--r--src/pkg/runtime/norace_test.go58
-rw-r--r--src/pkg/runtime/os_darwin.c104
-rw-r--r--src/pkg/runtime/os_darwin.h2
-rw-r--r--src/pkg/runtime/os_dragonfly.c282
-rw-r--r--src/pkg/runtime/os_dragonfly.h28
-rw-r--r--src/pkg/runtime/os_freebsd.c76
-rw-r--r--src/pkg/runtime/os_freebsd.h2
-rw-r--r--src/pkg/runtime/os_freebsd_arm.c3
-rw-r--r--src/pkg/runtime/os_linux.c67
-rw-r--r--src/pkg/runtime/os_linux_386.c3
-rw-r--r--src/pkg/runtime/os_linux_arm.c10
-rw-r--r--src/pkg/runtime/os_netbsd.c48
-rw-r--r--src/pkg/runtime/os_netbsd.h2
-rw-r--r--src/pkg/runtime/os_netbsd_arm.c3
-rw-r--r--src/pkg/runtime/os_openbsd.c51
-rw-r--r--src/pkg/runtime/os_openbsd.h2
-rw-r--r--src/pkg/runtime/os_plan9.c34
-rw-r--r--src/pkg/runtime/os_plan9_386.c6
-rw-r--r--src/pkg/runtime/os_plan9_amd64.c6
-rw-r--r--src/pkg/runtime/os_windows.c76
-rw-r--r--src/pkg/runtime/os_windows.h1
-rw-r--r--src/pkg/runtime/os_windows_386.c2
-rw-r--r--src/pkg/runtime/os_windows_amd64.c2
-rw-r--r--src/pkg/runtime/panic.c153
-rw-r--r--src/pkg/runtime/parfor.c4
-rw-r--r--src/pkg/runtime/parfor_test.go5
-rw-r--r--src/pkg/runtime/pprof/pprof.go6
-rw-r--r--src/pkg/runtime/pprof/pprof_test.go385
-rw-r--r--src/pkg/runtime/print.c9
-rw-r--r--src/pkg/runtime/proc.c1009
-rw-r--r--src/pkg/runtime/proc_test.go247
-rw-r--r--src/pkg/runtime/race.c131
-rw-r--r--src/pkg/runtime/race.go2
-rw-r--r--src/pkg/runtime/race.h6
-rw-r--r--src/pkg/runtime/race/README1
-rw-r--r--src/pkg/runtime/race/output_test.go156
-rw-r--r--src/pkg/runtime/race/race.go8
-rw-r--r--src/pkg/runtime/race/race_darwin_amd64.sysobin146952 -> 192988 bytes
-rw-r--r--src/pkg/runtime/race/race_linux_amd64.sysobin142848 -> 195144 bytes
-rw-r--r--src/pkg/runtime/race/race_test.go2
-rw-r--r--src/pkg/runtime/race/race_windows_amd64.sysobin121376 -> 161295 bytes
-rw-r--r--src/pkg/runtime/race/testdata/chan_test.go25
-rw-r--r--src/pkg/runtime/race/testdata/comp_test.go54
-rw-r--r--src/pkg/runtime/race/testdata/mop_test.go329
-rw-r--r--src/pkg/runtime/race/testdata/regression_test.go32
-rw-r--r--src/pkg/runtime/race/testdata/waitgroup_test.go128
-rw-r--r--src/pkg/runtime/race0.c9
-rw-r--r--src/pkg/runtime/race_amd64.s4
-rw-r--r--src/pkg/runtime/rt0_darwin_386.s8
-rw-r--r--src/pkg/runtime/rt0_darwin_amd64.s8
-rw-r--r--src/pkg/runtime/rt0_dragonfly_386.s16
-rw-r--r--src/pkg/runtime/rt0_dragonfly_amd64.s15
-rw-r--r--src/pkg/runtime/rt0_freebsd_386.s8
-rw-r--r--src/pkg/runtime/rt0_freebsd_amd64.s8
-rw-r--r--src/pkg/runtime/rt0_freebsd_arm.s9
-rw-r--r--src/pkg/runtime/rt0_linux_386.s10
-rw-r--r--src/pkg/runtime/rt0_linux_amd64.s8
-rw-r--r--src/pkg/runtime/rt0_linux_arm.s67
-rw-r--r--src/pkg/runtime/rt0_netbsd_386.s8
-rw-r--r--src/pkg/runtime/rt0_netbsd_amd64.s8
-rw-r--r--src/pkg/runtime/rt0_netbsd_arm.s9
-rw-r--r--src/pkg/runtime/rt0_openbsd_386.s8
-rw-r--r--src/pkg/runtime/rt0_openbsd_amd64.s8
-rw-r--r--src/pkg/runtime/rt0_plan9_386.s6
-rw-r--r--src/pkg/runtime/rt0_plan9_amd64.s6
-rw-r--r--src/pkg/runtime/rt0_windows_386.s8
-rw-r--r--src/pkg/runtime/rt0_windows_amd64.s7
-rw-r--r--src/pkg/runtime/runtime-gdb.py9
-rw-r--r--src/pkg/runtime/runtime.c87
-rw-r--r--src/pkg/runtime/runtime.h257
-rw-r--r--src/pkg/runtime/runtime_test.go43
-rw-r--r--src/pkg/runtime/sema.goc137
-rw-r--r--src/pkg/runtime/signal_386.c10
-rw-r--r--src/pkg/runtime/signal_amd64.c10
-rw-r--r--src/pkg/runtime/signal_arm.c9
-rw-r--r--src/pkg/runtime/signal_dragonfly_386.h23
-rw-r--r--src/pkg/runtime/signal_dragonfly_amd64.h31
-rw-r--r--src/pkg/runtime/signal_unix.c4
-rw-r--r--src/pkg/runtime/signals_dragonfly.h51
-rw-r--r--src/pkg/runtime/sigqueue.goc14
-rw-r--r--src/pkg/runtime/slice.c121
-rw-r--r--src/pkg/runtime/softfloat_arm.c33
-rw-r--r--src/pkg/runtime/stack.c208
-rw-r--r--src/pkg/runtime/stack.h24
-rw-r--r--src/pkg/runtime/stack_test.go2
-rw-r--r--src/pkg/runtime/string.goc74
-rw-r--r--src/pkg/runtime/symtab.c740
-rw-r--r--src/pkg/runtime/sys_arm.c35
-rw-r--r--src/pkg/runtime/sys_darwin_386.s79
-rw-r--r--src/pkg/runtime/sys_darwin_amd64.s82
-rw-r--r--src/pkg/runtime/sys_dragonfly_386.s369
-rw-r--r--src/pkg/runtime/sys_dragonfly_amd64.s330
-rw-r--r--src/pkg/runtime/sys_freebsd_386.s92
-rw-r--r--src/pkg/runtime/sys_freebsd_amd64.s119
-rw-r--r--src/pkg/runtime/sys_freebsd_arm.s253
-rw-r--r--src/pkg/runtime/sys_linux_386.s68
-rw-r--r--src/pkg/runtime/sys_linux_amd64.s68
-rw-r--r--src/pkg/runtime/sys_linux_arm.s100
-rw-r--r--src/pkg/runtime/sys_netbsd_386.s93
-rw-r--r--src/pkg/runtime/sys_netbsd_amd64.s93
-rw-r--r--src/pkg/runtime/sys_netbsd_arm.s162
-rw-r--r--src/pkg/runtime/sys_openbsd_386.s90
-rw-r--r--src/pkg/runtime/sys_openbsd_amd64.s87
-rw-r--r--src/pkg/runtime/sys_plan9_386.s39
-rw-r--r--src/pkg/runtime/sys_plan9_amd64.s45
-rw-r--r--src/pkg/runtime/sys_windows_386.s182
-rw-r--r--src/pkg/runtime/sys_windows_amd64.s164
-rw-r--r--src/pkg/runtime/sys_x86.c42
-rw-r--r--src/pkg/runtime/syscall_windows.goc5
-rw-r--r--src/pkg/runtime/time.goc77
-rw-r--r--src/pkg/runtime/time_plan9_386.c2
-rw-r--r--src/pkg/runtime/traceback_arm.c286
-rw-r--r--src/pkg/runtime/traceback_x86.c283
-rw-r--r--src/pkg/runtime/type.h6
-rw-r--r--src/pkg/runtime/vlop_386.s10
-rw-r--r--src/pkg/runtime/vlop_arm.s137
-rw-r--r--src/pkg/runtime/vlrt_386.c8
-rw-r--r--src/pkg/runtime/vlrt_arm.c50
-rw-r--r--src/pkg/sort/example_interface_test.go79
-rw-r--r--src/pkg/sort/example_multi_test.go19
-rw-r--r--src/pkg/sort/example_wrapper_test.go77
-rw-r--r--src/pkg/sort/search_test.go3
-rw-r--r--src/pkg/sort/sort.go193
-rw-r--r--src/pkg/sort/sort_test.go202
-rw-r--r--src/pkg/strconv/atof.go4
-rw-r--r--src/pkg/strconv/atoi.go7
-rw-r--r--src/pkg/strconv/quote.go2
-rw-r--r--src/pkg/strconv/strconv_test.go3
-rw-r--r--src/pkg/strings/replace.go5
-rw-r--r--src/pkg/strings/replace_test.go11
-rw-r--r--src/pkg/strings/strings.go14
-rw-r--r--src/pkg/strings/strings.s5
-rw-r--r--src/pkg/strings/strings_decl.go8
-rw-r--r--src/pkg/strings/strings_test.go33
-rw-r--r--src/pkg/sync/atomic/64bit_arm.go10
-rw-r--r--src/pkg/sync/atomic/asm_386.s95
-rw-r--r--src/pkg/sync/atomic/asm_amd64.s74
-rw-r--r--src/pkg/sync/atomic/asm_arm.s59
-rw-r--r--src/pkg/sync/atomic/asm_freebsd_arm.s66
-rw-r--r--src/pkg/sync/atomic/asm_linux_arm.s116
-rw-r--r--src/pkg/sync/atomic/asm_netbsd_arm.s66
-rw-r--r--src/pkg/sync/atomic/atomic_linux_arm_test.go14
-rw-r--r--src/pkg/sync/atomic/atomic_test.go397
-rw-r--r--src/pkg/sync/atomic/doc.go31
-rw-r--r--src/pkg/sync/atomic/export_linux_arm_test.go9
-rw-r--r--src/pkg/sync/atomic/race.go48
-rw-r--r--src/pkg/sync/cond.go113
-rw-r--r--src/pkg/sync/cond_test.go129
-rw-r--r--src/pkg/sync/example_test.go7
-rw-r--r--src/pkg/sync/once.go4
-rw-r--r--src/pkg/sync/race.go8
-rw-r--r--src/pkg/sync/race0.go6
-rw-r--r--src/pkg/sync/runtime.go18
-rw-r--r--src/pkg/sync/waitgroup.go26
-rw-r--r--src/pkg/syscall/asm_darwin_386.s12
-rw-r--r--src/pkg/syscall/asm_darwin_amd64.s10
-rw-r--r--src/pkg/syscall/asm_dragonfly_386.s139
-rw-r--r--src/pkg/syscall/asm_dragonfly_amd64.s133
-rw-r--r--src/pkg/syscall/asm_freebsd_386.s12
-rw-r--r--src/pkg/syscall/asm_freebsd_amd64.s17
-rw-r--r--src/pkg/syscall/asm_freebsd_arm.s70
-rw-r--r--src/pkg/syscall/asm_linux_386.s16
-rw-r--r--src/pkg/syscall/asm_linux_amd64.s14
-rw-r--r--src/pkg/syscall/asm_linux_arm.s12
-rw-r--r--src/pkg/syscall/asm_netbsd_386.s12
-rw-r--r--src/pkg/syscall/asm_netbsd_amd64.s24
-rw-r--r--src/pkg/syscall/asm_netbsd_arm.s12
-rw-r--r--src/pkg/syscall/asm_openbsd_386.s12
-rw-r--r--src/pkg/syscall/asm_openbsd_amd64.s24
-rw-r--r--src/pkg/syscall/asm_plan9_386.s14
-rw-r--r--src/pkg/syscall/asm_plan9_amd64.s14
-rw-r--r--src/pkg/syscall/bpf_bsd.go2
-rw-r--r--src/pkg/syscall/consistency_unix_test.go34
-rw-r--r--src/pkg/syscall/env_unix.go2
-rw-r--r--src/pkg/syscall/env_windows.go13
-rw-r--r--src/pkg/syscall/exec_bsd.go10
-rw-r--r--src/pkg/syscall/exec_linux.go16
-rw-r--r--src/pkg/syscall/exec_unix.go2
-rwxr-xr-xsrc/pkg/syscall/mkall.sh16
-rwxr-xr-xsrc/pkg/syscall/mkerrors.sh29
-rwxr-xr-xsrc/pkg/syscall/mksyscall.pl18
-rwxr-xr-xsrc/pkg/syscall/mksyscall_windows.pl2
-rwxr-xr-xsrc/pkg/syscall/mksysctl_openbsd.pl2
-rwxr-xr-xsrc/pkg/syscall/mksysnum_darwin.pl2
-rwxr-xr-xsrc/pkg/syscall/mksysnum_dragonfly.pl43
-rwxr-xr-xsrc/pkg/syscall/mksysnum_freebsd.pl2
-rwxr-xr-xsrc/pkg/syscall/mksysnum_linux.pl2
-rwxr-xr-xsrc/pkg/syscall/mksysnum_netbsd.pl2
-rwxr-xr-xsrc/pkg/syscall/mksysnum_openbsd.pl2
-rw-r--r--src/pkg/syscall/passfd_test.go7
-rw-r--r--src/pkg/syscall/race.go8
-rw-r--r--src/pkg/syscall/race0.go6
-rw-r--r--src/pkg/syscall/route_bsd.go37
-rw-r--r--src/pkg/syscall/route_dragonfly.go72
-rw-r--r--src/pkg/syscall/security_windows.go9
-rw-r--r--src/pkg/syscall/sockcmsg_unix.go4
-rw-r--r--src/pkg/syscall/syscall_bsd.go162
-rw-r--r--src/pkg/syscall/syscall_darwin.go98
-rw-r--r--src/pkg/syscall/syscall_darwin_386.go15
-rw-r--r--src/pkg/syscall/syscall_darwin_amd64.go17
-rw-r--r--src/pkg/syscall/syscall_dragonfly.go394
-rw-r--r--src/pkg/syscall/syscall_dragonfly_386.go58
-rw-r--r--src/pkg/syscall/syscall_dragonfly_amd64.go58
-rw-r--r--src/pkg/syscall/syscall_linux.go142
-rw-r--r--src/pkg/syscall/syscall_no_getwd.go11
-rw-r--r--src/pkg/syscall/syscall_plan9.go9
-rw-r--r--src/pkg/syscall/syscall_test.go30
-rw-r--r--src/pkg/syscall/syscall_unix.go155
-rw-r--r--src/pkg/syscall/syscall_windows.go12
-rw-r--r--src/pkg/syscall/types_darwin.go7
-rw-r--r--src/pkg/syscall/types_dragonfly.go241
-rw-r--r--src/pkg/syscall/types_freebsd.go7
-rw-r--r--src/pkg/syscall/types_linux.go7
-rw-r--r--src/pkg/syscall/types_netbsd.go7
-rw-r--r--src/pkg/syscall/types_openbsd.go7
-rw-r--r--src/pkg/syscall/zerrors_darwin_386.go4
-rw-r--r--src/pkg/syscall/zerrors_darwin_amd64.go4
-rw-r--r--src/pkg/syscall/zerrors_dragonfly_386.go1523
-rw-r--r--src/pkg/syscall/zerrors_dragonfly_amd64.go1523
-rw-r--r--src/pkg/syscall/zerrors_freebsd_386.go6
-rw-r--r--src/pkg/syscall/zerrors_freebsd_amd64.go6
-rw-r--r--src/pkg/syscall/zerrors_freebsd_arm.go4
-rw-r--r--src/pkg/syscall/zerrors_linux_386.go33
-rw-r--r--src/pkg/syscall/zerrors_linux_amd64.go33
-rw-r--r--src/pkg/syscall/zerrors_linux_arm.go29
-rw-r--r--src/pkg/syscall/zerrors_netbsd_386.go4
-rw-r--r--src/pkg/syscall/zerrors_netbsd_amd64.go4
-rw-r--r--src/pkg/syscall/zerrors_netbsd_arm.go4
-rw-r--r--src/pkg/syscall/zerrors_openbsd_386.go4
-rw-r--r--src/pkg/syscall/zerrors_openbsd_amd64.go4
-rw-r--r--src/pkg/syscall/zsyscall_dragonfly_386.go1303
-rw-r--r--src/pkg/syscall/zsyscall_dragonfly_amd64.go1303
-rw-r--r--src/pkg/syscall/zsyscall_linux_386.go31
-rw-r--r--src/pkg/syscall/zsyscall_linux_amd64.go31
-rw-r--r--src/pkg/syscall/zsyscall_linux_arm.go31
-rw-r--r--src/pkg/syscall/zsyscall_windows_386.go300
-rw-r--r--src/pkg/syscall/zsyscall_windows_amd64.go300
-rw-r--r--src/pkg/syscall/zsysnum_dragonfly_386.go303
-rw-r--r--src/pkg/syscall/zsysnum_dragonfly_amd64.go303
-rw-r--r--src/pkg/syscall/ztypes_darwin_386.go11
-rw-r--r--src/pkg/syscall/ztypes_darwin_amd64.go11
-rw-r--r--src/pkg/syscall/ztypes_dragonfly_386.go429
-rw-r--r--src/pkg/syscall/ztypes_dragonfly_amd64.go435
-rw-r--r--src/pkg/syscall/ztypes_freebsd_386.go11
-rw-r--r--src/pkg/syscall/ztypes_freebsd_amd64.go11
-rw-r--r--src/pkg/syscall/ztypes_freebsd_arm.go11
-rw-r--r--src/pkg/syscall/ztypes_linux_386.go11
-rw-r--r--src/pkg/syscall/ztypes_linux_amd64.go11
-rw-r--r--src/pkg/syscall/ztypes_linux_arm.go11
-rw-r--r--src/pkg/syscall/ztypes_netbsd_386.go11
-rw-r--r--src/pkg/syscall/ztypes_netbsd_amd64.go11
-rw-r--r--src/pkg/syscall/ztypes_netbsd_arm.go11
-rw-r--r--src/pkg/syscall/ztypes_openbsd_386.go11
-rw-r--r--src/pkg/syscall/ztypes_openbsd_amd64.go11
-rw-r--r--src/pkg/syscall/ztypes_windows.go69
-rw-r--r--src/pkg/testing/allocs.go8
-rw-r--r--src/pkg/testing/benchmark.go13
-rw-r--r--src/pkg/testing/benchmark_test.go58
-rw-r--r--src/pkg/testing/cover.go86
-rw-r--r--src/pkg/testing/export_test.go10
-rw-r--r--src/pkg/testing/quick/quick.go66
-rw-r--r--src/pkg/testing/quick/quick_test.go107
-rw-r--r--src/pkg/testing/testing.go139
-rw-r--r--src/pkg/text/template/doc.go46
-rw-r--r--src/pkg/text/template/exec.go19
-rw-r--r--src/pkg/text/template/exec_test.go127
-rw-r--r--src/pkg/text/template/funcs.go206
-rw-r--r--src/pkg/text/template/multi_test.go4
-rw-r--r--src/pkg/text/template/parse/lex.go9
-rw-r--r--src/pkg/text/template/parse/lex_test.go10
-rw-r--r--src/pkg/text/template/parse/node.go4
-rw-r--r--src/pkg/text/template/parse/parse.go66
-rw-r--r--src/pkg/text/template/parse/parse_test.go25
-rw-r--r--src/pkg/time/Makefile9
-rw-r--r--src/pkg/time/export_test.go5
-rw-r--r--src/pkg/time/export_windows_test.go10
-rw-r--r--src/pkg/time/format.go256
-rw-r--r--src/pkg/time/genzabbrs.go145
-rw-r--r--src/pkg/time/internal_test.go67
-rw-r--r--src/pkg/time/sleep.go3
-rw-r--r--src/pkg/time/sleep_test.go104
-rw-r--r--src/pkg/time/sys_unix.go2
-rw-r--r--src/pkg/time/time.go124
-rw-r--r--src/pkg/time/time_test.go148
-rw-r--r--src/pkg/time/zoneinfo.go13
-rw-r--r--src/pkg/time/zoneinfo_abbrs_windows.go115
-rw-r--r--src/pkg/time/zoneinfo_plan9.go4
-rw-r--r--src/pkg/time/zoneinfo_read.go4
-rw-r--r--src/pkg/time/zoneinfo_unix.go16
-rw-r--r--src/pkg/time/zoneinfo_windows.go138
-rw-r--r--src/pkg/time/zoneinfo_windows_test.go35
-rw-r--r--src/pkg/unicode/graphic.go19
-rw-r--r--src/pkg/unicode/graphic_test.go4
-rwxr-xr-xsrc/race.bash2
-rw-r--r--src/race.bat3
-rwxr-xr-xsrc/run.bash100
-rw-r--r--src/run.bat12
-rwxr-xr-xsrc/run.rc4
-rwxr-xr-xsrc/sudo.bash4
1335 files changed, 71669 insertions, 59026 deletions
diff --git a/src/cmd/5a/a.h b/src/cmd/5a/a.h
index a2c87cf48..8b39d610f 100644
--- a/src/cmd/5a/a.h
+++ b/src/cmd/5a/a.h
@@ -98,6 +98,7 @@ struct Gen
{
Sym* sym;
int32 offset;
+ int32 offset2;
short type;
short reg;
short name;
diff --git a/src/cmd/5a/a.y b/src/cmd/5a/a.y
index c9fdf05d5..c506ff9d5 100644
--- a/src/cmd/5a/a.y
+++ b/src/cmd/5a/a.y
@@ -33,6 +33,7 @@
#include <stdio.h> /* if we don't, bison will, and a.h re-#defines getc */
#include <libc.h>
#include "a.h"
+#include "../../pkg/runtime/funcdata.h"
%}
%union
{
@@ -50,11 +51,11 @@
%left '*' '/' '%'
%token <lval> LTYPE1 LTYPE2 LTYPE3 LTYPE4 LTYPE5
%token <lval> LTYPE6 LTYPE7 LTYPE8 LTYPE9 LTYPEA
-%token <lval> LTYPEB LTYPEC LTYPED LTYPEE LTYPEF
+%token <lval> LTYPEB LTYPEC LTYPED LTYPEE
%token <lval> LTYPEG LTYPEH LTYPEI LTYPEJ LTYPEK
%token <lval> LTYPEL LTYPEM LTYPEN LTYPEBX LTYPEPLD
%token <lval> LCONST LSP LSB LFP LPC
-%token <lval> LTYPEX LR LREG LF LFREG LC LCREG LPSR LFCR
+%token <lval> LTYPEX LTYPEPC LTYPEF LR LREG LF LFREG LC LCREG LPSR LFCR
%token <lval> LCOND LS LAT
%token <dval> LFCONST
%token <sval> LSCONST
@@ -217,10 +218,20 @@ inst:
*/
| LTYPEB name ',' imm
{
+ $4.type = D_CONST2;
+ $4.offset2 = ArgsSizeUnknown;
outcode($1, Always, &$2, 0, &$4);
}
| LTYPEB name ',' con ',' imm
{
+ $6.type = D_CONST2;
+ $6.offset2 = ArgsSizeUnknown;
+ outcode($1, Always, &$2, $4, &$6);
+ }
+| LTYPEB name ',' con ',' imm '-' con
+ {
+ $6.type = D_CONST2;
+ $6.offset2 = $8;
outcode($1, Always, &$2, $4, &$6);
}
/*
@@ -310,6 +321,26 @@ inst:
outcode($1, Always, &$2, NREG, &nullgen);
}
/*
+ * PCDATA
+ */
+| LTYPEPC gen ',' gen
+ {
+ if($2.type != D_CONST || $4.type != D_CONST)
+ yyerror("arguments to PCDATA must be integer constants");
+ outcode($1, Always, &$2, NREG, &$4);
+ }
+/*
+ * FUNCDATA
+ */
+| LTYPEF gen ',' gen
+ {
+ if($2.type != D_CONST)
+ yyerror("index for FUNCDATA must be integer constant");
+ if($4.type != D_EXTERN && $4.type != D_STATIC)
+ yyerror("value for FUNCDATA must be symbol reference");
+ outcode($1, Always, &$2, NREG, &$4);
+ }
+/*
* END
*/
| LTYPEE comma
diff --git a/src/cmd/5a/doc.go b/src/cmd/5a/doc.go
index 29725db04..3e9e78fe6 100644
--- a/src/cmd/5a/doc.go
+++ b/src/cmd/5a/doc.go
@@ -10,6 +10,10 @@
http://plan9.bell-labs.com/magic/man2html/1/8a
+Go-specific considerations are documented at
+
+ http://golang.org/doc/asm
+
Its target architecture is the ARM, referred to by these tools as arm.
*/
diff --git a/src/cmd/5a/lex.c b/src/cmd/5a/lex.c
index a77e3050d..c1b54e50b 100644
--- a/src/cmd/5a/lex.c
+++ b/src/cmd/5a/lex.c
@@ -68,7 +68,7 @@ main(int argc, char *argv[])
ARGBEGIN {
default:
c = ARGC();
- if(c >= 0 || c < sizeof(debug))
+ if(c >= 0 && c < sizeof(debug))
debug[c] = 1;
break;
@@ -191,8 +191,8 @@ struct
"R6", LREG, 6,
"R7", LREG, 7,
"R8", LREG, 8,
- "R9", LREG, 9,
- "R10", LREG, 10,
+ "m", LREG, 9, // avoid unintentionally clobber m/g using R9/R10
+ "g", LREG, 10,
"R11", LREG, 11,
"R12", LREG, 12,
"R13", LREG, 13,
@@ -414,6 +414,8 @@ struct
"MULAWB", LTYPEN, AMULAWB,
"USEFIELD", LTYPEN, AUSEFIELD,
+ "PCDATA", LTYPEPC, APCDATA,
+ "FUNCDATA", LTYPEF, AFUNCDATA,
0
};
@@ -483,14 +485,14 @@ void
zname(char *n, int t, int s)
{
- Bputc(&obuf, ANAME);
- Bputc(&obuf, t); /* type */
- Bputc(&obuf, s); /* sym */
+ BPUTC(&obuf, ANAME);
+ BPUTC(&obuf, t); /* type */
+ BPUTC(&obuf, s); /* sym */
while(*n) {
- Bputc(&obuf, *n);
+ BPUTC(&obuf, *n);
n++;
}
- Bputc(&obuf, 0);
+ BPUTC(&obuf, 0);
}
void
@@ -501,11 +503,11 @@ zaddr(Gen *a, int s)
char *n;
Ieee e;
- Bputc(&obuf, a->type);
- Bputc(&obuf, a->reg);
- Bputc(&obuf, s);
- Bputc(&obuf, a->name);
- Bputc(&obuf, 0);
+ BPUTC(&obuf, a->type);
+ BPUTC(&obuf, a->reg);
+ BPUTC(&obuf, s);
+ BPUTC(&obuf, a->name);
+ BPUTC(&obuf, 0);
switch(a->type) {
default:
print("unknown type %d\n", a->type);
@@ -520,38 +522,33 @@ zaddr(Gen *a, int s)
case D_REGREG:
case D_REGREG2:
- Bputc(&obuf, a->offset);
+ BPUTC(&obuf, a->offset);
break;
+ case D_CONST2:
+ l = a->offset2;
+ BPUTLE4(&obuf, l);
+ // fall through
case D_OREG:
case D_CONST:
case D_BRANCH:
case D_SHIFT:
l = a->offset;
- Bputc(&obuf, l);
- Bputc(&obuf, l>>8);
- Bputc(&obuf, l>>16);
- Bputc(&obuf, l>>24);
+ BPUTLE4(&obuf, l);
break;
case D_SCONST:
n = a->sval;
for(i=0; i<NSNAME; i++) {
- Bputc(&obuf, *n);
+ BPUTC(&obuf, *n);
n++;
}
break;
case D_FCONST:
ieeedtod(&e, a->dval);
- Bputc(&obuf, e.l);
- Bputc(&obuf, e.l>>8);
- Bputc(&obuf, e.l>>16);
- Bputc(&obuf, e.l>>24);
- Bputc(&obuf, e.h);
- Bputc(&obuf, e.h>>8);
- Bputc(&obuf, e.h>>16);
- Bputc(&obuf, e.h>>24);
+ BPUTLE4(&obuf, e.l);
+ BPUTLE4(&obuf, e.h);
break;
}
}
@@ -633,13 +630,10 @@ jackpot:
goto jackpot;
break;
}
- Bputc(&obuf, a);
- Bputc(&obuf, scond);
- Bputc(&obuf, reg);
- Bputc(&obuf, stmtline);
- Bputc(&obuf, stmtline>>8);
- Bputc(&obuf, stmtline>>16);
- Bputc(&obuf, stmtline>>24);
+ BPUTC(&obuf, a);
+ BPUTC(&obuf, scond);
+ BPUTC(&obuf, reg);
+ BPUTLE4(&obuf, stmtline);
zaddr(g1, sf);
zaddr(g2, st);
@@ -713,12 +707,12 @@ outhist(void)
q = 0;
}
if(n) {
- Bputc(&obuf, ANAME);
- Bputc(&obuf, D_FILE); /* type */
- Bputc(&obuf, 1); /* sym */
- Bputc(&obuf, '<');
+ BPUTC(&obuf, ANAME);
+ BPUTC(&obuf, D_FILE); /* type */
+ BPUTC(&obuf, 1); /* sym */
+ BPUTC(&obuf, '<');
Bwrite(&obuf, p, n);
- Bputc(&obuf, 0);
+ BPUTC(&obuf, 0);
}
p = q;
if(p == 0 && op) {
@@ -728,13 +722,10 @@ outhist(void)
}
g.offset = h->offset;
- Bputc(&obuf, AHISTORY);
- Bputc(&obuf, Always);
- Bputc(&obuf, 0);
- Bputc(&obuf, h->line);
- Bputc(&obuf, h->line>>8);
- Bputc(&obuf, h->line>>16);
- Bputc(&obuf, h->line>>24);
+ BPUTC(&obuf, AHISTORY);
+ BPUTC(&obuf, Always);
+ BPUTC(&obuf, 0);
+ BPUTLE4(&obuf, h->line);
zaddr(&nullgen, 0);
zaddr(&g, 0);
diff --git a/src/cmd/5a/y.tab.c b/src/cmd/5a/y.tab.c
index ce97ee315..dd102a09a 100644
--- a/src/cmd/5a/y.tab.c
+++ b/src/cmd/5a/y.tab.c
@@ -1,10 +1,8 @@
+/* A Bison parser, made by GNU Bison 2.5. */
-/* A Bison parser, made by GNU Bison 2.4.1. */
-
-/* Skeleton implementation for Bison's Yacc-like parsers in C
+/* Bison implementation for Yacc-like parsers in C
- Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
- Free Software Foundation, Inc.
+ Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -46,7 +44,7 @@
#define YYBISON 1
/* Bison version. */
-#define YYBISON_VERSION "2.4.1"
+#define YYBISON_VERSION "2.5"
/* Skeleton name. */
#define YYSKELETON_NAME "yacc.c"
@@ -67,17 +65,18 @@
/* Copy the first part of user declarations. */
-/* Line 189 of yacc.c */
+/* Line 268 of yacc.c */
#line 31 "a.y"
#include <u.h>
#include <stdio.h> /* if we don't, bison will, and a.h re-#defines getc */
#include <libc.h>
#include "a.h"
+#include "../../pkg/runtime/funcdata.h"
-/* Line 189 of yacc.c */
-#line 81 "y.tab.c"
+/* Line 268 of yacc.c */
+#line 80 "y.tab.c"
/* Enabling traces. */
#ifndef YYDEBUG
@@ -118,39 +117,40 @@
LTYPEC = 269,
LTYPED = 270,
LTYPEE = 271,
- LTYPEF = 272,
- LTYPEG = 273,
- LTYPEH = 274,
- LTYPEI = 275,
- LTYPEJ = 276,
- LTYPEK = 277,
- LTYPEL = 278,
- LTYPEM = 279,
- LTYPEN = 280,
- LTYPEBX = 281,
- LTYPEPLD = 282,
- LCONST = 283,
- LSP = 284,
- LSB = 285,
- LFP = 286,
- LPC = 287,
- LTYPEX = 288,
- LR = 289,
- LREG = 290,
- LF = 291,
- LFREG = 292,
- LC = 293,
- LCREG = 294,
- LPSR = 295,
- LFCR = 296,
- LCOND = 297,
- LS = 298,
- LAT = 299,
- LFCONST = 300,
- LSCONST = 301,
- LNAME = 302,
- LLAB = 303,
- LVAR = 304
+ LTYPEG = 272,
+ LTYPEH = 273,
+ LTYPEI = 274,
+ LTYPEJ = 275,
+ LTYPEK = 276,
+ LTYPEL = 277,
+ LTYPEM = 278,
+ LTYPEN = 279,
+ LTYPEBX = 280,
+ LTYPEPLD = 281,
+ LCONST = 282,
+ LSP = 283,
+ LSB = 284,
+ LFP = 285,
+ LPC = 286,
+ LTYPEX = 287,
+ LTYPEPC = 288,
+ LTYPEF = 289,
+ LR = 290,
+ LREG = 291,
+ LF = 292,
+ LFREG = 293,
+ LC = 294,
+ LCREG = 295,
+ LPSR = 296,
+ LFCR = 297,
+ LCOND = 298,
+ LS = 299,
+ LAT = 300,
+ LFCONST = 301,
+ LSCONST = 302,
+ LNAME = 303,
+ LLAB = 304,
+ LVAR = 305
};
#endif
/* Tokens. */
@@ -168,39 +168,40 @@
#define LTYPEC 269
#define LTYPED 270
#define LTYPEE 271
-#define LTYPEF 272
-#define LTYPEG 273
-#define LTYPEH 274
-#define LTYPEI 275
-#define LTYPEJ 276
-#define LTYPEK 277
-#define LTYPEL 278
-#define LTYPEM 279
-#define LTYPEN 280
-#define LTYPEBX 281
-#define LTYPEPLD 282
-#define LCONST 283
-#define LSP 284
-#define LSB 285
-#define LFP 286
-#define LPC 287
-#define LTYPEX 288
-#define LR 289
-#define LREG 290
-#define LF 291
-#define LFREG 292
-#define LC 293
-#define LCREG 294
-#define LPSR 295
-#define LFCR 296
-#define LCOND 297
-#define LS 298
-#define LAT 299
-#define LFCONST 300
-#define LSCONST 301
-#define LNAME 302
-#define LLAB 303
-#define LVAR 304
+#define LTYPEG 272
+#define LTYPEH 273
+#define LTYPEI 274
+#define LTYPEJ 275
+#define LTYPEK 276
+#define LTYPEL 277
+#define LTYPEM 278
+#define LTYPEN 279
+#define LTYPEBX 280
+#define LTYPEPLD 281
+#define LCONST 282
+#define LSP 283
+#define LSB 284
+#define LFP 285
+#define LPC 286
+#define LTYPEX 287
+#define LTYPEPC 288
+#define LTYPEF 289
+#define LR 290
+#define LREG 291
+#define LF 292
+#define LFREG 293
+#define LC 294
+#define LCREG 295
+#define LPSR 296
+#define LFCR 297
+#define LCOND 298
+#define LS 299
+#define LAT 300
+#define LFCONST 301
+#define LSCONST 302
+#define LNAME 303
+#define LLAB 304
+#define LVAR 305
@@ -209,8 +210,8 @@
typedef union YYSTYPE
{
-/* Line 214 of yacc.c */
-#line 38 "a.y"
+/* Line 293 of yacc.c */
+#line 39 "a.y"
Sym *sym;
int32 lval;
@@ -220,8 +221,8 @@ typedef union YYSTYPE
-/* Line 214 of yacc.c */
-#line 225 "y.tab.c"
+/* Line 293 of yacc.c */
+#line 226 "y.tab.c"
} YYSTYPE;
# define YYSTYPE_IS_TRIVIAL 1
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
@@ -232,8 +233,8 @@ typedef union YYSTYPE
/* Copy the second part of user declarations. */
-/* Line 264 of yacc.c */
-#line 237 "y.tab.c"
+/* Line 343 of yacc.c */
+#line 238 "y.tab.c"
#ifdef short
# undef short
@@ -283,7 +284,7 @@ typedef short int yytype_int16;
#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
#ifndef YY_
-# if YYENABLE_NLS
+# if defined YYENABLE_NLS && YYENABLE_NLS
# if ENABLE_NLS
# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
# define YY_(msgid) dgettext ("bison-runtime", msgid)
@@ -336,11 +337,11 @@ YYID (yyi)
# define alloca _alloca
# else
# define YYSTACK_ALLOC alloca
-# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
-# ifndef _STDLIB_H
-# define _STDLIB_H 1
+# ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
# endif
# endif
# endif
@@ -363,24 +364,24 @@ YYID (yyi)
# ifndef YYSTACK_ALLOC_MAXIMUM
# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
# endif
-# if (defined __cplusplus && ! defined _STDLIB_H \
+# if (defined __cplusplus && ! defined EXIT_SUCCESS \
&& ! ((defined YYMALLOC || defined malloc) \
&& (defined YYFREE || defined free)))
# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
-# ifndef _STDLIB_H
-# define _STDLIB_H 1
+# ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
# endif
# endif
# ifndef YYMALLOC
# define YYMALLOC malloc
-# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+# if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
# endif
# endif
# ifndef YYFREE
# define YYFREE free
-# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+# if ! defined free && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
void free (void *); /* INFRINGES ON USER NAME SPACE */
# endif
@@ -409,23 +410,7 @@ union yyalloc
((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+ YYSTACK_GAP_MAXIMUM)
-/* Copy COUNT objects from FROM to TO. The source and destination do
- not overlap. */
-# ifndef YYCOPY
-# if defined __GNUC__ && 1 < __GNUC__
-# define YYCOPY(To, From, Count) \
- __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
-# else
-# define YYCOPY(To, From, Count) \
- do \
- { \
- YYSIZE_T yyi; \
- for (yyi = 0; yyi < (Count); yyi++) \
- (To)[yyi] = (From)[yyi]; \
- } \
- while (YYID (0))
-# endif
-# endif
+# define YYCOPY_NEEDED 1
/* Relocate STACK from its old location to the new one. The
local variables YYSIZE and YYSTACKSIZE give the old and new number of
@@ -445,23 +430,43 @@ union yyalloc
#endif
+#if defined YYCOPY_NEEDED && YYCOPY_NEEDED
+/* Copy COUNT objects from FROM to TO. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if defined __GNUC__ && 1 < __GNUC__
+# define YYCOPY(To, From, Count) \
+ __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+# else
+# define YYCOPY(To, From, Count) \
+ do \
+ { \
+ YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (To)[yyi] = (From)[yyi]; \
+ } \
+ while (YYID (0))
+# endif
+# endif
+#endif /* !YYCOPY_NEEDED */
+
/* YYFINAL -- State number of the termination state. */
#define YYFINAL 2
/* YYLAST -- Last index in YYTABLE. */
-#define YYLAST 603
+#define YYLAST 609
/* YYNTOKENS -- Number of terminals. */
-#define YYNTOKENS 70
+#define YYNTOKENS 71
/* YYNNTS -- Number of nonterminals. */
#define YYNNTS 35
/* YYNRULES -- Number of rules. */
-#define YYNRULES 130
+#define YYNRULES 133
/* YYNRULES -- Number of states. */
-#define YYNSTATES 329
+#define YYNSTATES 339
/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
#define YYUNDEFTOK 2
-#define YYMAXUTOK 304
+#define YYMAXUTOK 305
#define YYTRANSLATE(YYX) \
((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
@@ -472,16 +477,16 @@ static const yytype_uint8 yytranslate[] =
0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 68, 12, 5, 2,
- 66, 67, 10, 8, 63, 9, 2, 11, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 60, 62,
- 6, 61, 7, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 69, 12, 5, 2,
+ 67, 68, 10, 8, 64, 9, 2, 11, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 61, 63,
+ 6, 62, 7, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 64, 2, 65, 4, 2, 2, 2, 2, 2,
+ 2, 65, 2, 66, 4, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 3, 2, 69, 2, 2, 2,
+ 2, 2, 2, 2, 3, 2, 70, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
@@ -499,7 +504,7 @@ static const yytype_uint8 yytranslate[] =
25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
- 55, 56, 57, 58, 59
+ 55, 56, 57, 58, 59, 60
};
#if YYDEBUG
@@ -510,92 +515,94 @@ static const yytype_uint16 yyprhs[] =
0, 0, 3, 4, 5, 9, 10, 15, 16, 21,
26, 31, 33, 36, 39, 47, 54, 60, 66, 72,
77, 82, 86, 90, 95, 102, 110, 118, 126, 133,
- 140, 144, 149, 156, 163, 168, 172, 178, 184, 192,
- 199, 212, 220, 230, 233, 236, 237, 240, 243, 244,
- 247, 252, 255, 258, 261, 264, 269, 272, 274, 277,
- 281, 283, 287, 291, 293, 295, 297, 302, 304, 306,
- 308, 310, 312, 314, 316, 320, 322, 327, 329, 334,
- 336, 338, 340, 342, 345, 347, 353, 358, 363, 368,
- 373, 375, 377, 379, 381, 386, 388, 390, 392, 397,
- 399, 401, 403, 408, 413, 419, 427, 428, 431, 434,
- 436, 438, 440, 442, 444, 447, 450, 453, 457, 458,
- 461, 463, 467, 471, 475, 479, 483, 488, 493, 497,
- 501
+ 140, 144, 149, 156, 165, 172, 177, 181, 187, 193,
+ 201, 208, 221, 229, 239, 242, 247, 252, 255, 256,
+ 259, 262, 263, 266, 271, 274, 277, 280, 283, 288,
+ 291, 293, 296, 300, 302, 306, 310, 312, 314, 316,
+ 321, 323, 325, 327, 329, 331, 333, 335, 339, 341,
+ 346, 348, 353, 355, 357, 359, 361, 364, 366, 372,
+ 377, 382, 387, 392, 394, 396, 398, 400, 405, 407,
+ 409, 411, 416, 418, 420, 422, 427, 432, 438, 446,
+ 447, 450, 453, 455, 457, 459, 461, 463, 466, 469,
+ 472, 476, 477, 480, 482, 486, 490, 494, 498, 502,
+ 507, 512, 516, 520
};
/* YYRHS -- A `-1'-separated list of the rules' RHS. */
static const yytype_int8 yyrhs[] =
{
- 71, 0, -1, -1, -1, 71, 72, 73, -1, -1,
- 58, 60, 74, 73, -1, -1, 57, 60, 75, 73,
- -1, 57, 61, 104, 62, -1, 59, 61, 104, 62,
- -1, 62, -1, 76, 62, -1, 1, 62, -1, 13,
- 77, 88, 63, 95, 63, 90, -1, 13, 77, 88,
- 63, 95, 63, -1, 13, 77, 88, 63, 90, -1,
- 14, 77, 88, 63, 90, -1, 15, 77, 83, 63,
- 83, -1, 16, 77, 78, 79, -1, 16, 77, 78,
- 84, -1, 36, 78, 85, -1, 17, 78, 79, -1,
- 18, 77, 78, 83, -1, 19, 77, 88, 63, 95,
- 78, -1, 20, 77, 86, 63, 64, 82, 65, -1,
- 20, 77, 64, 82, 65, 63, 86, -1, 21, 77,
- 90, 63, 85, 63, 90, -1, 21, 77, 90, 63,
- 85, 78, -1, 21, 77, 78, 85, 63, 90, -1,
- 22, 77, 78, -1, 23, 99, 63, 89, -1, 23,
- 99, 63, 102, 63, 89, -1, 24, 99, 11, 102,
- 63, 80, -1, 25, 77, 90, 78, -1, 29, 78,
- 80, -1, 30, 77, 98, 63, 98, -1, 32, 77,
- 97, 63, 98, -1, 32, 77, 97, 63, 47, 63,
- 98, -1, 33, 77, 98, 63, 98, 78, -1, 31,
- 77, 102, 63, 104, 63, 95, 63, 96, 63, 96,
- 103, -1, 34, 77, 90, 63, 90, 63, 91, -1,
- 35, 77, 90, 63, 90, 63, 90, 63, 95, -1,
- 37, 87, -1, 26, 78, -1, -1, 77, 52, -1,
- 77, 53, -1, -1, 63, 78, -1, 102, 66, 42,
- 67, -1, 57, 100, -1, 58, 100, -1, 68, 102,
- -1, 68, 87, -1, 68, 10, 68, 87, -1, 68,
- 56, -1, 81, -1, 68, 55, -1, 68, 9, 55,
- -1, 95, -1, 95, 9, 95, -1, 95, 78, 82,
- -1, 90, -1, 80, -1, 92, -1, 92, 66, 95,
- 67, -1, 50, -1, 51, -1, 102, -1, 87, -1,
- 98, -1, 85, -1, 99, -1, 66, 95, 67, -1,
- 85, -1, 102, 66, 94, 67, -1, 99, -1, 99,
- 66, 94, 67, -1, 86, -1, 90, -1, 89, -1,
- 92, -1, 68, 102, -1, 95, -1, 66, 95, 63,
- 95, 67, -1, 95, 6, 6, 93, -1, 95, 7,
- 7, 93, -1, 95, 9, 7, 93, -1, 95, 54,
- 7, 93, -1, 95, -1, 102, -1, 45, -1, 42,
- -1, 44, 66, 104, 67, -1, 94, -1, 39, -1,
- 49, -1, 48, 66, 104, 67, -1, 98, -1, 81,
- -1, 47, -1, 46, 66, 102, 67, -1, 102, 66,
- 101, 67, -1, 57, 100, 66, 101, 67, -1, 57,
- 6, 7, 100, 66, 40, 67, -1, -1, 8, 102,
- -1, 9, 102, -1, 40, -1, 39, -1, 41, -1,
- 38, -1, 59, -1, 9, 102, -1, 8, 102, -1,
- 69, 102, -1, 66, 104, 67, -1, -1, 63, 104,
- -1, 102, -1, 104, 8, 104, -1, 104, 9, 104,
- -1, 104, 10, 104, -1, 104, 11, 104, -1, 104,
- 12, 104, -1, 104, 6, 6, 104, -1, 104, 7,
- 7, 104, -1, 104, 5, 104, -1, 104, 4, 104,
- -1, 104, 3, 104, -1
+ 72, 0, -1, -1, -1, 72, 73, 74, -1, -1,
+ 59, 61, 75, 74, -1, -1, 58, 61, 76, 74,
+ -1, 58, 62, 105, 63, -1, 60, 62, 105, 63,
+ -1, 63, -1, 77, 63, -1, 1, 63, -1, 13,
+ 78, 89, 64, 96, 64, 91, -1, 13, 78, 89,
+ 64, 96, 64, -1, 13, 78, 89, 64, 91, -1,
+ 14, 78, 89, 64, 91, -1, 15, 78, 84, 64,
+ 84, -1, 16, 78, 79, 80, -1, 16, 78, 79,
+ 85, -1, 35, 79, 86, -1, 17, 79, 80, -1,
+ 18, 78, 79, 84, -1, 19, 78, 89, 64, 96,
+ 79, -1, 20, 78, 87, 64, 65, 83, 66, -1,
+ 20, 78, 65, 83, 66, 64, 87, -1, 21, 78,
+ 91, 64, 86, 64, 91, -1, 21, 78, 91, 64,
+ 86, 79, -1, 21, 78, 79, 86, 64, 91, -1,
+ 22, 78, 79, -1, 23, 100, 64, 90, -1, 23,
+ 100, 64, 103, 64, 90, -1, 23, 100, 64, 103,
+ 64, 90, 9, 103, -1, 24, 100, 11, 103, 64,
+ 81, -1, 25, 78, 91, 79, -1, 28, 79, 81,
+ -1, 29, 78, 99, 64, 99, -1, 31, 78, 98,
+ 64, 99, -1, 31, 78, 98, 64, 48, 64, 99,
+ -1, 32, 78, 99, 64, 99, 79, -1, 30, 78,
+ 103, 64, 105, 64, 96, 64, 97, 64, 97, 104,
+ -1, 33, 78, 91, 64, 91, 64, 92, -1, 34,
+ 78, 91, 64, 91, 64, 91, 64, 96, -1, 36,
+ 88, -1, 43, 84, 64, 84, -1, 44, 84, 64,
+ 84, -1, 26, 79, -1, -1, 78, 53, -1, 78,
+ 54, -1, -1, 64, 79, -1, 103, 67, 41, 68,
+ -1, 58, 101, -1, 59, 101, -1, 69, 103, -1,
+ 69, 88, -1, 69, 10, 69, 88, -1, 69, 57,
+ -1, 82, -1, 69, 56, -1, 69, 9, 56, -1,
+ 96, -1, 96, 9, 96, -1, 96, 79, 83, -1,
+ 91, -1, 81, -1, 93, -1, 93, 67, 96, 68,
+ -1, 51, -1, 52, -1, 103, -1, 88, -1, 99,
+ -1, 86, -1, 100, -1, 67, 96, 68, -1, 86,
+ -1, 103, 67, 95, 68, -1, 100, -1, 100, 67,
+ 95, 68, -1, 87, -1, 91, -1, 90, -1, 93,
+ -1, 69, 103, -1, 96, -1, 67, 96, 64, 96,
+ 68, -1, 96, 6, 6, 94, -1, 96, 7, 7,
+ 94, -1, 96, 9, 7, 94, -1, 96, 55, 7,
+ 94, -1, 96, -1, 103, -1, 46, -1, 41, -1,
+ 45, 67, 105, 68, -1, 95, -1, 38, -1, 50,
+ -1, 49, 67, 105, 68, -1, 99, -1, 82, -1,
+ 48, -1, 47, 67, 103, 68, -1, 103, 67, 102,
+ 68, -1, 58, 101, 67, 102, 68, -1, 58, 6,
+ 7, 101, 67, 39, 68, -1, -1, 8, 103, -1,
+ 9, 103, -1, 39, -1, 38, -1, 40, -1, 37,
+ -1, 60, -1, 9, 103, -1, 8, 103, -1, 70,
+ 103, -1, 67, 105, 68, -1, -1, 64, 105, -1,
+ 103, -1, 105, 8, 105, -1, 105, 9, 105, -1,
+ 105, 10, 105, -1, 105, 11, 105, -1, 105, 12,
+ 105, -1, 105, 6, 6, 105, -1, 105, 7, 7,
+ 105, -1, 105, 5, 105, -1, 105, 4, 105, -1,
+ 105, 3, 105, -1
};
/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
static const yytype_uint16 yyrline[] =
{
- 0, 67, 67, 69, 68, 76, 75, 83, 82, 88,
- 93, 99, 100, 101, 107, 111, 115, 122, 129, 136,
- 140, 147, 154, 161, 168, 175, 184, 196, 200, 204,
- 211, 218, 222, 229, 236, 243, 250, 254, 258, 262,
- 269, 291, 299, 308, 315, 321, 324, 328, 333, 334,
- 337, 343, 352, 360, 366, 371, 376, 382, 385, 391,
- 399, 403, 412, 418, 419, 420, 421, 426, 432, 438,
- 444, 445, 448, 449, 457, 466, 467, 476, 477, 483,
- 486, 487, 488, 490, 498, 506, 515, 521, 527, 533,
- 541, 547, 555, 556, 560, 568, 569, 575, 576, 584,
- 585, 588, 594, 602, 610, 618, 628, 631, 635, 641,
- 642, 643, 646, 647, 651, 655, 659, 663, 669, 672,
- 678, 679, 683, 687, 691, 695, 699, 703, 707, 711,
- 715
+ 0, 68, 68, 70, 69, 77, 76, 84, 83, 89,
+ 94, 100, 101, 102, 108, 112, 116, 123, 130, 137,
+ 141, 148, 155, 162, 169, 176, 185, 197, 201, 205,
+ 212, 219, 225, 231, 240, 247, 254, 261, 265, 269,
+ 273, 280, 302, 310, 319, 326, 335, 346, 352, 355,
+ 359, 364, 365, 368, 374, 383, 391, 397, 402, 407,
+ 413, 416, 422, 430, 434, 443, 449, 450, 451, 452,
+ 457, 463, 469, 475, 476, 479, 480, 488, 497, 498,
+ 507, 508, 514, 517, 518, 519, 521, 529, 537, 546,
+ 552, 558, 564, 572, 578, 586, 587, 591, 599, 600,
+ 606, 607, 615, 616, 619, 625, 633, 641, 649, 659,
+ 662, 666, 672, 673, 674, 677, 678, 682, 686, 690,
+ 694, 700, 703, 709, 710, 714, 718, 722, 726, 730,
+ 734, 738, 742, 746
};
#endif
@@ -607,16 +614,16 @@ static const char *const yytname[] =
"$end", "error", "$undefined", "'|'", "'^'", "'&'", "'<'", "'>'", "'+'",
"'-'", "'*'", "'/'", "'%'", "LTYPE1", "LTYPE2", "LTYPE3", "LTYPE4",
"LTYPE5", "LTYPE6", "LTYPE7", "LTYPE8", "LTYPE9", "LTYPEA", "LTYPEB",
- "LTYPEC", "LTYPED", "LTYPEE", "LTYPEF", "LTYPEG", "LTYPEH", "LTYPEI",
- "LTYPEJ", "LTYPEK", "LTYPEL", "LTYPEM", "LTYPEN", "LTYPEBX", "LTYPEPLD",
- "LCONST", "LSP", "LSB", "LFP", "LPC", "LTYPEX", "LR", "LREG", "LF",
- "LFREG", "LC", "LCREG", "LPSR", "LFCR", "LCOND", "LS", "LAT", "LFCONST",
- "LSCONST", "LNAME", "LLAB", "LVAR", "':'", "'='", "';'", "','", "'['",
- "']'", "'('", "')'", "'$'", "'~'", "$accept", "prog", "$@1", "line",
- "$@2", "$@3", "inst", "cond", "comma", "rel", "ximm", "fcon", "reglist",
- "gen", "nireg", "ireg", "ioreg", "oreg", "imsr", "imm", "reg", "regreg",
- "shift", "rcon", "sreg", "spreg", "creg", "frcon", "freg", "name",
- "offset", "pointer", "con", "oexpr", "expr", 0
+ "LTYPEC", "LTYPED", "LTYPEE", "LTYPEG", "LTYPEH", "LTYPEI", "LTYPEJ",
+ "LTYPEK", "LTYPEL", "LTYPEM", "LTYPEN", "LTYPEBX", "LTYPEPLD", "LCONST",
+ "LSP", "LSB", "LFP", "LPC", "LTYPEX", "LTYPEPC", "LTYPEF", "LR", "LREG",
+ "LF", "LFREG", "LC", "LCREG", "LPSR", "LFCR", "LCOND", "LS", "LAT",
+ "LFCONST", "LSCONST", "LNAME", "LLAB", "LVAR", "':'", "'='", "';'",
+ "','", "'['", "']'", "'('", "')'", "'$'", "'~'", "$accept", "prog",
+ "$@1", "line", "$@2", "$@3", "inst", "cond", "comma", "rel", "ximm",
+ "fcon", "reglist", "gen", "nireg", "ireg", "ioreg", "oreg", "imsr",
+ "imm", "reg", "regreg", "shift", "rcon", "sreg", "spreg", "creg",
+ "frcon", "freg", "name", "offset", "pointer", "con", "oexpr", "expr", 0
};
#endif
@@ -631,27 +638,28 @@ static const yytype_uint16 yytoknum[] =
275, 276, 277, 278, 279, 280, 281, 282, 283, 284,
285, 286, 287, 288, 289, 290, 291, 292, 293, 294,
295, 296, 297, 298, 299, 300, 301, 302, 303, 304,
- 58, 61, 59, 44, 91, 93, 40, 41, 36, 126
+ 305, 58, 61, 59, 44, 91, 93, 40, 41, 36,
+ 126
};
# endif
/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
static const yytype_uint8 yyr1[] =
{
- 0, 70, 71, 72, 71, 74, 73, 75, 73, 73,
- 73, 73, 73, 73, 76, 76, 76, 76, 76, 76,
- 76, 76, 76, 76, 76, 76, 76, 76, 76, 76,
- 76, 76, 76, 76, 76, 76, 76, 76, 76, 76,
- 76, 76, 76, 76, 76, 77, 77, 77, 78, 78,
- 79, 79, 79, 80, 80, 80, 80, 80, 81, 81,
- 82, 82, 82, 83, 83, 83, 83, 83, 83, 83,
- 83, 83, 84, 84, 85, 86, 86, 87, 87, 87,
- 88, 88, 88, 89, 90, 91, 92, 92, 92, 92,
- 93, 93, 94, 94, 94, 95, 95, 96, 96, 97,
- 97, 98, 98, 99, 99, 99, 100, 100, 100, 101,
- 101, 101, 102, 102, 102, 102, 102, 102, 103, 103,
- 104, 104, 104, 104, 104, 104, 104, 104, 104, 104,
- 104
+ 0, 71, 72, 73, 72, 75, 74, 76, 74, 74,
+ 74, 74, 74, 74, 77, 77, 77, 77, 77, 77,
+ 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+ 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+ 77, 77, 77, 77, 77, 77, 77, 77, 78, 78,
+ 78, 79, 79, 80, 80, 80, 81, 81, 81, 81,
+ 81, 82, 82, 83, 83, 83, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 85, 85, 86, 87, 87,
+ 88, 88, 88, 89, 89, 89, 90, 91, 92, 93,
+ 93, 93, 93, 94, 94, 95, 95, 95, 96, 96,
+ 97, 97, 98, 98, 99, 99, 100, 100, 100, 101,
+ 101, 101, 102, 102, 102, 103, 103, 103, 103, 103,
+ 103, 104, 104, 105, 105, 105, 105, 105, 105, 105,
+ 105, 105, 105, 105
};
/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
@@ -660,289 +668,297 @@ static const yytype_uint8 yyr2[] =
0, 2, 0, 0, 3, 0, 4, 0, 4, 4,
4, 1, 2, 2, 7, 6, 5, 5, 5, 4,
4, 3, 3, 4, 6, 7, 7, 7, 6, 6,
- 3, 4, 6, 6, 4, 3, 5, 5, 7, 6,
- 12, 7, 9, 2, 2, 0, 2, 2, 0, 2,
- 4, 2, 2, 2, 2, 4, 2, 1, 2, 3,
- 1, 3, 3, 1, 1, 1, 4, 1, 1, 1,
- 1, 1, 1, 1, 3, 1, 4, 1, 4, 1,
- 1, 1, 1, 2, 1, 5, 4, 4, 4, 4,
- 1, 1, 1, 1, 4, 1, 1, 1, 4, 1,
- 1, 1, 4, 4, 5, 7, 0, 2, 2, 1,
- 1, 1, 1, 1, 2, 2, 2, 3, 0, 2,
- 1, 3, 3, 3, 3, 3, 4, 4, 3, 3,
- 3
+ 3, 4, 6, 8, 6, 4, 3, 5, 5, 7,
+ 6, 12, 7, 9, 2, 4, 4, 2, 0, 2,
+ 2, 0, 2, 4, 2, 2, 2, 2, 4, 2,
+ 1, 2, 3, 1, 3, 3, 1, 1, 1, 4,
+ 1, 1, 1, 1, 1, 1, 1, 3, 1, 4,
+ 1, 4, 1, 1, 1, 1, 2, 1, 5, 4,
+ 4, 4, 4, 1, 1, 1, 1, 4, 1, 1,
+ 1, 4, 1, 1, 1, 4, 4, 5, 7, 0,
+ 2, 2, 1, 1, 1, 1, 1, 2, 2, 2,
+ 3, 0, 2, 1, 3, 3, 3, 3, 3, 4,
+ 4, 3, 3, 3
};
-/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
- STATE-NUM when YYTABLE doesn't specify something else to do. Zero
+/* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM.
+ Performed when YYTABLE doesn't specify something else to do. Zero
means the default is an error. */
static const yytype_uint8 yydefact[] =
{
- 2, 3, 1, 0, 0, 45, 45, 45, 45, 48,
- 45, 45, 45, 45, 45, 0, 0, 45, 48, 48,
- 45, 45, 45, 45, 45, 45, 48, 0, 0, 0,
- 0, 11, 4, 0, 13, 0, 0, 0, 48, 48,
- 0, 48, 0, 0, 48, 48, 0, 0, 112, 106,
- 113, 0, 0, 0, 0, 0, 0, 44, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 75, 79, 43,
- 77, 0, 7, 0, 5, 0, 12, 96, 93, 0,
- 92, 46, 47, 0, 0, 81, 80, 82, 95, 84,
- 0, 0, 101, 67, 68, 0, 64, 57, 0, 70,
- 63, 65, 71, 69, 0, 49, 106, 106, 22, 0,
- 0, 0, 0, 0, 0, 0, 0, 84, 30, 115,
- 114, 0, 0, 0, 0, 120, 0, 116, 0, 0,
- 0, 48, 35, 0, 0, 0, 100, 0, 99, 0,
- 0, 0, 0, 21, 0, 0, 0, 0, 0, 0,
- 0, 0, 83, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 58, 56, 54, 53, 0, 0, 106, 19,
- 20, 72, 73, 0, 51, 52, 0, 23, 0, 0,
- 48, 0, 0, 0, 0, 106, 107, 108, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 117,
- 31, 0, 110, 109, 111, 0, 0, 34, 0, 0,
- 0, 0, 0, 0, 0, 74, 0, 0, 8, 9,
- 6, 10, 0, 16, 84, 0, 0, 0, 0, 17,
- 0, 59, 0, 18, 0, 51, 0, 0, 48, 0,
- 0, 0, 0, 0, 48, 0, 0, 130, 129, 128,
- 0, 0, 121, 122, 123, 124, 125, 0, 103, 0,
- 36, 0, 101, 37, 48, 0, 0, 78, 76, 94,
- 15, 86, 90, 91, 87, 88, 89, 102, 55, 66,
- 50, 24, 0, 61, 62, 0, 29, 48, 28, 0,
- 104, 126, 127, 32, 33, 0, 0, 39, 0, 0,
- 14, 26, 25, 27, 0, 0, 38, 0, 41, 0,
- 105, 0, 0, 0, 0, 97, 0, 0, 42, 0,
- 0, 0, 0, 118, 85, 98, 0, 40, 119
+ 2, 3, 1, 0, 0, 48, 48, 48, 48, 51,
+ 48, 48, 48, 48, 48, 0, 0, 48, 51, 51,
+ 48, 48, 48, 48, 48, 48, 51, 0, 0, 0,
+ 0, 0, 0, 11, 4, 0, 13, 0, 0, 0,
+ 51, 51, 0, 51, 0, 0, 51, 51, 0, 0,
+ 115, 109, 116, 0, 0, 0, 0, 0, 0, 47,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 78,
+ 82, 44, 80, 0, 99, 96, 0, 95, 0, 104,
+ 70, 71, 0, 67, 60, 0, 73, 66, 68, 98,
+ 87, 74, 72, 0, 7, 0, 5, 0, 12, 49,
+ 50, 0, 0, 84, 83, 85, 0, 0, 0, 52,
+ 109, 109, 22, 0, 0, 0, 0, 0, 0, 0,
+ 0, 87, 30, 118, 117, 0, 0, 0, 0, 123,
+ 0, 119, 0, 0, 0, 51, 36, 0, 0, 0,
+ 103, 0, 102, 0, 0, 0, 0, 21, 0, 0,
+ 0, 0, 0, 0, 0, 61, 59, 57, 56, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 86, 0, 0, 0, 109, 19, 20, 75, 76, 0,
+ 54, 55, 0, 23, 0, 0, 51, 0, 0, 0,
+ 0, 109, 110, 111, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 120, 31, 0, 113, 112,
+ 114, 0, 0, 35, 0, 0, 0, 0, 0, 0,
+ 0, 77, 0, 0, 0, 0, 62, 0, 45, 0,
+ 0, 0, 0, 0, 46, 8, 9, 6, 10, 16,
+ 87, 17, 18, 54, 0, 0, 51, 0, 0, 0,
+ 0, 0, 51, 0, 0, 133, 132, 131, 0, 0,
+ 124, 125, 126, 127, 128, 0, 106, 0, 37, 0,
+ 104, 38, 51, 0, 0, 81, 79, 97, 105, 58,
+ 69, 89, 93, 94, 90, 91, 92, 15, 53, 24,
+ 0, 64, 65, 0, 29, 51, 28, 0, 107, 129,
+ 130, 32, 34, 0, 0, 40, 0, 0, 14, 26,
+ 25, 27, 0, 0, 0, 39, 0, 42, 0, 108,
+ 33, 0, 0, 0, 0, 100, 0, 0, 43, 0,
+ 0, 0, 0, 121, 88, 101, 0, 41, 122
};
/* YYDEFGOTO[NTERM-NUM]. */
static const yytype_int16 yydefgoto[] =
{
- -1, 1, 3, 32, 149, 147, 33, 35, 105, 108,
- 96, 97, 179, 98, 170, 67, 68, 99, 84, 85,
- 86, 308, 87, 271, 88, 117, 316, 137, 102, 70,
- 124, 205, 125, 327, 126
+ -1, 1, 3, 34, 168, 166, 35, 37, 109, 112,
+ 83, 84, 185, 85, 176, 69, 70, 86, 102, 103,
+ 87, 317, 88, 281, 89, 121, 326, 141, 91, 72,
+ 128, 211, 129, 337, 130
};
/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
STATE-NUM. */
-#define YYPACT_NINF -114
+#define YYPACT_NINF -128
static const yytype_int16 yypact[] =
{
- -114, 19, -114, 311, -20, -114, -114, -114, -114, -36,
- -114, -114, -114, -114, -114, 413, 413, -114, -36, -36,
- -114, -114, -114, -114, -114, -114, -36, 427, -22, -9,
- -7, -114, -114, 11, -114, 480, 480, 341, 45, -36,
- 229, 45, 480, 210, 436, 45, 435, 435, -114, 152,
- -114, 435, 435, 25, 15, 79, 517, -114, 24, 134,
- 393, 197, 134, 517, 517, 28, 71, -114, -114, -114,
- 35, 58, -114, 435, -114, 435, -114, -114, -114, 69,
- -114, -114, -114, 435, 51, -114, -114, -114, -114, 46,
- 57, 75, -114, -114, -114, 119, -114, -114, 82, -114,
- -114, 88, -114, 58, 368, -114, 159, 159, -114, 113,
- 373, 114, 411, 120, 123, 28, 145, -114, -114, -114,
- -114, 193, 435, 435, 173, -114, 54, -114, 395, 111,
- 435, -36, -114, 178, 182, 21, -114, 198, -114, 201,
- 207, 212, 411, -114, 206, 168, 558, 311, 310, 311,
- 506, 435, -114, 411, 271, 274, 285, 286, 411, 435,
- 87, 228, -114, -114, -114, 58, 373, 411, 152, -114,
- -114, -114, -114, 231, -114, -114, 257, -114, 411, 235,
- 5, 259, 168, 275, 28, 159, -114, -114, 111, 435,
- 435, 435, 333, 344, 435, 435, 435, 435, 435, -114,
- -114, 289, -114, -114, -114, 300, 308, -114, 151, 435,
- 319, 176, 151, 411, 411, -114, 317, 322, -114, -114,
- -114, -114, 224, -114, 312, 71, 71, 71, 71, -114,
- 323, -114, 427, -114, 328, 173, 64, 329, -36, 315,
- 411, 411, 411, 411, 334, 339, 332, 577, 247, 584,
- 435, 435, 273, 273, -114, -114, -114, 340, -114, 24,
- -114, 350, 351, -114, -36, 353, 365, -114, -114, -114,
- 411, -114, -114, -114, -114, -114, -114, -114, -114, -114,
- -114, -114, 439, -114, -114, 364, -114, 157, -114, 398,
- -114, 518, 518, -114, -114, 411, 151, -114, 374, 411,
- -114, -114, -114, -114, 382, 394, -114, 411, -114, 397,
- -114, 155, 403, 411, 392, -114, 404, 411, -114, 435,
- 155, 401, 299, 406, -114, -114, 435, -114, 568
+ -128, 4, -128, 315, -35, -128, -128, -128, -128, -10,
+ -128, -128, -128, -128, -128, 44, 44, -128, -10, -10,
+ -128, -128, -128, -128, -128, -128, -10, 416, 371, 371,
+ -49, 9, 32, -128, -128, 38, -128, 487, 487, 344,
+ 69, -10, 391, 69, 487, 209, 489, 69, 317, 317,
+ -128, 49, -128, 317, 317, 42, 48, 106, 67, -128,
+ 61, 191, 25, 93, 191, 67, 67, 68, 170, -128,
+ -128, -128, 72, 84, -128, -128, 86, -128, 109, -128,
+ -128, -128, 233, -128, -128, 80, -128, -128, 115, -128,
+ 426, -128, 84, 120, -128, 317, -128, 317, -128, -128,
+ -128, 317, 137, -128, -128, -128, 148, 155, 397, -128,
+ 74, 74, -128, 164, 371, 204, 240, 207, 206, 68,
+ 223, -128, -128, -128, -128, 270, 317, 317, 227, -128,
+ 183, -128, 90, 160, 317, -10, -128, 234, 237, 16,
+ -128, 254, -128, 255, 256, 257, 240, -128, 212, 168,
+ 548, 317, 317, 428, 258, -128, -128, -128, 84, 371,
+ 240, 318, 316, 335, 348, 371, 315, 502, 315, 512,
+ -128, 240, 240, 371, 49, -128, -128, -128, -128, 289,
+ -128, -128, 330, -128, 240, 291, 11, 307, 168, 312,
+ 68, 74, -128, -128, 160, 317, 317, 317, 377, 379,
+ 317, 317, 317, 317, 317, -128, -128, 324, -128, -128,
+ -128, 325, 337, -128, 77, 317, 338, 126, 77, 240,
+ 240, -128, 339, 342, 249, 347, -128, 416, -128, 352,
+ 170, 170, 170, 170, -128, -128, -128, -128, -128, -128,
+ 362, -128, -128, 227, -2, 359, -10, 366, 240, 240,
+ 240, 240, 375, 336, 384, 562, 590, 597, 317, 317,
+ 213, 213, -128, -128, -128, 385, -128, 61, -128, 357,
+ 395, -128, -10, 396, 398, -128, -128, -128, -128, -128,
+ -128, -128, -128, -128, -128, -128, -128, 240, -128, -128,
+ 434, -128, -128, 400, -128, 432, -128, 424, -128, 436,
+ 436, 459, -128, 240, 77, -128, 402, 240, -128, -128,
+ -128, -128, 404, 317, 411, -128, 240, -128, 415, -128,
+ -128, 216, 418, 240, 413, -128, 421, 240, -128, 317,
+ 216, 419, 302, 425, -128, -128, 317, -128, 573
};
/* YYPGOTO[NTERM-NUM]. */
static const yytype_int16 yypgoto[] =
{
- -114, -114, -114, -78, -114, -114, -114, 529, 2, 367,
- -50, 415, 48, -88, -114, -48, -40, -21, 47, -113,
- -19, -114, -28, 137, -59, -35, 154, -114, -49, 18,
- -83, 295, -11, -114, -25
+ -128, -128, -128, -77, -128, -128, -128, 538, 50, 382,
+ -57, 429, 33, -7, -128, -48, -43, -21, 36, -127,
+ -23, -128, 29, 17, -101, -28, 161, -128, -37, -8,
+ -65, 299, 2, -128, -32
};
/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
positive, shift that token. If negative, reduce the rule which
- number is the opposite. If zero, do what YYDEFACT says.
- If YYTABLE_NINF, syntax error. */
-#define YYTABLE_NINF -61
+ number is the opposite. If YYTABLE_NINF, syntax error. */
+#define YYTABLE_NINF -64
static const yytype_int16 yytable[] =
{
- 89, 89, 89, 113, 54, 54, 69, 89, 132, 101,
- 133, 40, 138, 139, 240, 200, 71, 143, 100, 2,
- 57, 58, 177, 174, 175, 116, 103, 39, 65, 109,
- 210, 144, 114, 53, 55, 119, 120, 131, 72, 73,
- 104, 127, 34, 110, 140, 141, 115, 118, 148, 134,
- 150, 74, 154, 155, 75, 156, 171, 189, 190, 191,
- 192, 193, 194, 195, 196, 197, 198, 183, 39, 218,
- -60, 220, 152, 76, 164, 89, 162, 180, 233, 46,
- 47, 129, 101, 90, 165, 235, 216, 217, 128, 111,
- 130, 100, 95, 173, 142, 46, 47, 81, 82, 103,
- 157, 145, 245, 202, 203, 204, 237, 144, 39, 48,
- 77, 186, 187, 78, 153, 79, 80, 201, 224, 206,
- 158, 199, 172, 217, 146, 48, 222, 46, 160, 161,
- 50, 89, 234, 207, 223, 151, 244, 51, 101, 229,
- 52, 159, 231, 238, 293, 166, 50, 100, 230, 120,
- 202, 203, 204, 51, 167, 103, 52, 48, 121, 260,
- 122, 123, 263, 264, 247, 248, 249, 122, 123, 252,
- 253, 254, 255, 256, 162, 163, 49, 178, 50, 176,
- 91, 92, 241, 181, 261, 66, 81, 82, 52, 182,
- 272, 272, 272, 272, 265, 266, 77, 91, 92, 78,
- 185, 79, 80, 314, 315, 283, 180, 180, 184, 294,
- 78, 278, 79, 80, 273, 273, 273, 273, 46, 47,
- 39, 71, 91, 262, 286, 291, 292, 189, 190, 191,
- 192, 193, 194, 195, 196, 197, 198, 46, 47, 188,
- 281, 208, 301, 91, 92, 209, 288, 306, 48, 81,
- 82, 300, 191, 192, 193, 194, 195, 196, 197, 198,
- 305, 211, 81, 82, 212, 135, 297, 48, 303, 50,
- 213, 114, 312, 215, 112, 214, 66, 225, 318, 52,
- 309, 226, 321, 196, 197, 198, 106, 107, 50, 284,
- 285, 269, 227, 228, 322, 51, 232, 236, 52, 237,
- 239, 328, 189, 190, 191, 192, 193, 194, 195, 196,
- 197, 198, 4, 189, 190, 191, 192, 193, 194, 195,
- 196, 197, 198, 242, 5, 6, 7, 8, 9, 10,
- 11, 12, 13, 14, 15, 16, 17, 18, 243, 250,
- 19, 20, 21, 22, 23, 24, 25, 26, 27, 46,
- 47, 251, 257, 189, 190, 191, 192, 193, 194, 195,
- 196, 197, 198, 274, 275, 276, 325, 258, 28, 29,
- 30, 259, 219, 31, 231, 270, 46, 47, 282, 48,
- 77, 46, 47, 78, 267, 79, 80, 91, 92, 268,
- 277, 93, 94, 81, 82, 279, 280, 287, 49, 290,
- 50, 46, 47, 46, 47, 289, 48, 66, 83, 95,
- 52, 48, 77, 295, 296, 78, 298, 79, 80, 91,
- 92, 46, 47, 93, 94, 168, 107, 50, 299, 302,
- 49, 48, 50, 48, 66, 46, 47, 52, 304, 66,
- 307, 95, 52, 46, 47, 81, 82, 46, 47, 310,
- 77, 48, 50, 78, 50, 79, 80, 311, 319, 51,
- 313, 51, 52, 83, 52, 48, 317, 320, 324, 326,
- 49, 169, 50, 48, 323, 77, 136, 48, 78, 51,
- 79, 80, 52, 246, 49, 0, 50, 0, 81, 82,
- 0, 0, 0, 66, 50, 0, 52, 0, 50, 39,
- 0, 51, 0, 0, 52, 66, 0, 0, 52, 189,
- 190, 191, 192, 193, 194, 195, 196, 197, 198, 77,
- 0, 0, 78, 0, 79, 80, 194, 195, 196, 197,
- 198, 0, 81, 82, 0, 36, 37, 38, 0, 41,
- 42, 43, 44, 45, 0, 0, 56, 0, 83, 59,
- 60, 61, 62, 63, 64, 0, 77, 0, 0, 78,
- 0, 79, 80, 0, 0, 0, 0, 0, 221, 81,
- 82, 189, 190, 191, 192, 193, 194, 195, 196, 197,
- 198, 190, 191, 192, 193, 194, 195, 196, 197, 198,
- 192, 193, 194, 195, 196, 197, 198, 202, 203, 204,
- 78, 0, 79, 80
+ 90, 90, 117, 136, 2, 206, 71, 55, 57, 90,
+ 90, 90, 94, 95, 104, 104, 90, 56, 56, 147,
+ 248, 104, 93, 120, 137, 216, 142, 143, 36, 73,
+ 92, 92, 107, 48, 49, 135, 208, 209, 210, 245,
+ 148, 92, 144, 145, 113, 180, 181, 118, 222, 223,
+ 123, 124, 48, 49, 41, 125, 131, 126, 127, 42,
+ 177, 157, 50, 167, 138, 169, 105, 105, 59, 60,
+ 96, 189, 155, 105, 106, 41, 67, -63, 99, 100,
+ 115, 50, 126, 127, 158, 52, 90, 223, 186, 235,
+ 108, 237, 53, 114, 97, 54, 119, 122, 48, 49,
+ 178, 98, 51, 170, 52, 74, 132, 183, 75, 243,
+ 179, 53, 76, 77, 54, 133, 92, 134, 148, 224,
+ 99, 100, 99, 100, 78, 79, 253, 50, 192, 193,
+ 82, 90, 229, 41, 207, 146, 212, 90, 301, 149,
+ 78, 79, 252, 240, 159, 90, 99, 100, 239, 241,
+ 52, 150, 228, 151, 225, 124, 246, 53, 234, 101,
+ 54, 92, 139, 255, 256, 257, 242, 92, 260, 261,
+ 262, 263, 264, 78, 270, 92, 152, 268, 48, 49,
+ 271, 272, 160, 269, 165, 213, 195, 196, 197, 198,
+ 199, 200, 201, 202, 203, 204, 273, 274, 208, 209,
+ 210, 171, 282, 282, 282, 282, 279, 50, 74, 75,
+ 302, 75, 172, 76, 77, 76, 77, 48, 49, 173,
+ 291, 186, 186, 202, 203, 204, 299, 300, 294, 73,
+ 52, 182, 283, 283, 283, 283, 249, 53, 78, 79,
+ 54, 48, 153, 154, 99, 100, 50, 309, 284, 285,
+ 286, 205, 195, 196, 197, 198, 199, 200, 201, 202,
+ 203, 204, 99, 100, 308, 324, 325, 315, 184, 52,
+ 50, 187, 311, 188, 116, 314, 68, 191, 74, 54,
+ 221, 75, 292, 293, 318, 76, 77, 190, 322, 155,
+ 156, 51, 118, 52, 194, 328, 289, 332, 214, 331,
+ 68, 215, 296, 54, 338, 195, 196, 197, 198, 199,
+ 200, 201, 202, 203, 204, 320, 4, 277, 217, 218,
+ 219, 220, 305, 231, 230, 48, 49, 227, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 232, 19, 20, 21, 22, 23, 24, 25,
+ 26, 27, 48, 49, 50, 233, 244, 247, 28, 29,
+ 195, 196, 197, 198, 199, 200, 201, 202, 203, 204,
+ 335, 245, 250, 30, 31, 32, 251, 52, 33, 48,
+ 49, 50, 74, 258, 53, 75, 259, 54, 265, 76,
+ 77, 78, 79, 266, 226, 80, 81, 99, 100, 48,
+ 49, 267, 51, 297, 52, 48, 49, 275, 50, 74,
+ 276, 68, 75, 82, 54, 278, 76, 77, 78, 79,
+ 280, 303, 80, 81, 48, 49, 287, 288, 50, 51,
+ 290, 52, 161, 162, 50, 163, 48, 49, 68, 295,
+ 82, 54, 48, 49, 200, 201, 202, 203, 204, 110,
+ 111, 52, 298, 50, 101, 174, 111, 52, 53, 304,
+ 306, 54, 307, 312, 68, 50, 310, 54, 313, 316,
+ 74, 50, 319, 75, 51, 321, 52, 76, 77, 323,
+ 329, 164, 327, 68, 226, 330, 54, 334, 52, 336,
+ 175, 333, 140, 254, 52, 53, 41, 0, 54, 0,
+ 0, 68, 0, 0, 54, 195, 196, 197, 198, 199,
+ 200, 201, 202, 203, 204, 195, 196, 197, 198, 199,
+ 200, 201, 202, 203, 204, 74, 0, 74, 75, 0,
+ 75, 0, 76, 77, 76, 77, 0, 0, 0, 0,
+ 99, 100, 99, 100, 38, 39, 40, 0, 43, 44,
+ 45, 46, 47, 41, 0, 58, 101, 0, 61, 62,
+ 63, 64, 65, 66, 0, 236, 196, 197, 198, 199,
+ 200, 201, 202, 203, 204, 238, 195, 196, 197, 198,
+ 199, 200, 201, 202, 203, 204, 208, 209, 210, 75,
+ 0, 0, 0, 76, 77, 197, 198, 199, 200, 201,
+ 202, 203, 204, 198, 199, 200, 201, 202, 203, 204
};
+#define yypact_value_is_default(yystate) \
+ ((yystate) == (-128))
+
+#define yytable_value_is_error(yytable_value) \
+ YYID (0)
+
static const yytype_int16 yycheck[] =
{
- 35, 36, 37, 43, 15, 16, 27, 42, 58, 37,
- 59, 9, 61, 62, 9, 128, 27, 65, 37, 0,
- 18, 19, 110, 106, 107, 44, 37, 63, 26, 40,
- 9, 66, 43, 15, 16, 46, 47, 56, 60, 61,
- 38, 52, 62, 41, 63, 64, 44, 45, 73, 60,
- 75, 60, 6, 7, 61, 9, 104, 3, 4, 5,
- 6, 7, 8, 9, 10, 11, 12, 115, 63, 147,
- 65, 149, 83, 62, 95, 110, 55, 112, 166, 8,
- 9, 66, 110, 36, 95, 168, 145, 146, 63, 42,
- 11, 110, 68, 104, 66, 8, 9, 52, 53, 110,
- 54, 66, 185, 39, 40, 41, 42, 142, 63, 38,
- 39, 122, 123, 42, 63, 44, 45, 128, 153, 130,
- 63, 67, 104, 182, 66, 38, 151, 8, 9, 10,
- 59, 166, 167, 131, 153, 66, 184, 66, 166, 158,
- 69, 66, 55, 178, 257, 63, 59, 166, 159, 160,
- 39, 40, 41, 66, 66, 166, 69, 38, 6, 208,
- 8, 9, 211, 212, 189, 190, 191, 8, 9, 194,
- 195, 196, 197, 198, 55, 56, 57, 63, 59, 66,
- 46, 47, 180, 63, 209, 66, 52, 53, 69, 66,
- 225, 226, 227, 228, 213, 214, 39, 46, 47, 42,
- 7, 44, 45, 48, 49, 240, 241, 242, 63, 259,
- 42, 232, 44, 45, 225, 226, 227, 228, 8, 9,
- 63, 232, 46, 47, 243, 250, 251, 3, 4, 5,
- 6, 7, 8, 9, 10, 11, 12, 8, 9, 66,
- 238, 63, 282, 46, 47, 63, 244, 296, 38, 52,
- 53, 270, 5, 6, 7, 8, 9, 10, 11, 12,
- 295, 63, 52, 53, 63, 68, 264, 38, 287, 59,
- 63, 282, 307, 67, 64, 63, 66, 6, 313, 69,
- 299, 7, 317, 10, 11, 12, 57, 58, 59, 241,
- 242, 67, 7, 7, 319, 66, 68, 66, 69, 42,
- 65, 326, 3, 4, 5, 6, 7, 8, 9, 10,
- 11, 12, 1, 3, 4, 5, 6, 7, 8, 9,
- 10, 11, 12, 64, 13, 14, 15, 16, 17, 18,
- 19, 20, 21, 22, 23, 24, 25, 26, 63, 6,
- 29, 30, 31, 32, 33, 34, 35, 36, 37, 8,
- 9, 7, 63, 3, 4, 5, 6, 7, 8, 9,
- 10, 11, 12, 226, 227, 228, 67, 67, 57, 58,
- 59, 63, 62, 62, 55, 63, 8, 9, 63, 38,
- 39, 8, 9, 42, 67, 44, 45, 46, 47, 67,
- 67, 50, 51, 52, 53, 67, 67, 63, 57, 67,
- 59, 8, 9, 8, 9, 66, 38, 66, 68, 68,
- 69, 38, 39, 63, 63, 42, 63, 44, 45, 46,
- 47, 8, 9, 50, 51, 57, 58, 59, 63, 65,
- 57, 38, 59, 38, 66, 8, 9, 69, 40, 66,
- 66, 68, 69, 8, 9, 52, 53, 8, 9, 67,
- 39, 38, 59, 42, 59, 44, 45, 63, 66, 66,
- 63, 66, 69, 68, 69, 38, 63, 63, 67, 63,
- 57, 104, 59, 38, 320, 39, 61, 38, 42, 66,
- 44, 45, 69, 188, 57, -1, 59, -1, 52, 53,
- -1, -1, -1, 66, 59, -1, 69, -1, 59, 63,
- -1, 66, -1, -1, 69, 66, -1, -1, 69, 3,
- 4, 5, 6, 7, 8, 9, 10, 11, 12, 39,
- -1, -1, 42, -1, 44, 45, 8, 9, 10, 11,
- 12, -1, 52, 53, -1, 6, 7, 8, -1, 10,
- 11, 12, 13, 14, -1, -1, 17, -1, 68, 20,
- 21, 22, 23, 24, 25, -1, 39, -1, -1, 42,
- -1, 44, 45, -1, -1, -1, -1, -1, 62, 52,
- 53, 3, 4, 5, 6, 7, 8, 9, 10, 11,
- 12, 4, 5, 6, 7, 8, 9, 10, 11, 12,
- 6, 7, 8, 9, 10, 11, 12, 39, 40, 41,
- 42, -1, 44, 45
+ 28, 29, 45, 60, 0, 132, 27, 15, 16, 37,
+ 38, 39, 61, 62, 37, 38, 44, 15, 16, 67,
+ 9, 44, 29, 46, 61, 9, 63, 64, 63, 27,
+ 28, 29, 39, 8, 9, 58, 38, 39, 40, 41,
+ 68, 39, 65, 66, 42, 110, 111, 45, 149, 150,
+ 48, 49, 8, 9, 64, 6, 54, 8, 9, 9,
+ 108, 82, 37, 95, 62, 97, 37, 38, 18, 19,
+ 61, 119, 56, 44, 38, 64, 26, 66, 53, 54,
+ 44, 37, 8, 9, 82, 60, 114, 188, 116, 166,
+ 40, 168, 67, 43, 62, 70, 46, 47, 8, 9,
+ 108, 63, 58, 101, 60, 38, 64, 114, 41, 174,
+ 108, 67, 45, 46, 70, 67, 114, 11, 146, 151,
+ 53, 54, 53, 54, 47, 48, 191, 37, 126, 127,
+ 69, 159, 160, 64, 132, 67, 134, 165, 265, 67,
+ 47, 48, 190, 171, 64, 173, 53, 54, 171, 172,
+ 60, 67, 159, 67, 152, 153, 184, 67, 165, 69,
+ 70, 159, 69, 195, 196, 197, 173, 165, 200, 201,
+ 202, 203, 204, 47, 48, 173, 67, 214, 8, 9,
+ 217, 218, 67, 215, 64, 135, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 219, 220, 38, 39,
+ 40, 64, 230, 231, 232, 233, 227, 37, 38, 41,
+ 267, 41, 64, 45, 46, 45, 46, 8, 9, 64,
+ 248, 249, 250, 10, 11, 12, 258, 259, 251, 227,
+ 60, 67, 230, 231, 232, 233, 186, 67, 47, 48,
+ 70, 8, 9, 10, 53, 54, 37, 290, 231, 232,
+ 233, 68, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 53, 54, 287, 49, 50, 304, 64, 60,
+ 37, 64, 295, 67, 65, 303, 67, 7, 38, 70,
+ 68, 41, 249, 250, 307, 45, 46, 64, 316, 56,
+ 57, 58, 290, 60, 67, 323, 246, 329, 64, 327,
+ 67, 64, 252, 70, 336, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 313, 1, 68, 64, 64,
+ 64, 64, 272, 7, 6, 8, 9, 69, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 7, 28, 29, 30, 31, 32, 33, 34,
+ 35, 36, 8, 9, 37, 7, 67, 66, 43, 44,
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
+ 68, 41, 65, 58, 59, 60, 64, 60, 63, 8,
+ 9, 37, 38, 6, 67, 41, 7, 70, 64, 45,
+ 46, 47, 48, 68, 56, 51, 52, 53, 54, 8,
+ 9, 64, 58, 67, 60, 8, 9, 68, 37, 38,
+ 68, 67, 41, 69, 70, 68, 45, 46, 47, 48,
+ 68, 64, 51, 52, 8, 9, 64, 68, 37, 58,
+ 64, 60, 6, 7, 37, 9, 8, 9, 67, 64,
+ 69, 70, 8, 9, 8, 9, 10, 11, 12, 58,
+ 59, 60, 68, 37, 69, 58, 59, 60, 67, 64,
+ 64, 70, 64, 39, 67, 37, 66, 70, 9, 67,
+ 38, 37, 68, 41, 58, 64, 60, 45, 46, 64,
+ 67, 55, 64, 67, 56, 64, 70, 68, 60, 64,
+ 108, 330, 63, 194, 60, 67, 64, -1, 70, -1,
+ -1, 67, -1, -1, 70, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 38, -1, 38, 41, -1,
+ 41, -1, 45, 46, 45, 46, -1, -1, -1, -1,
+ 53, 54, 53, 54, 6, 7, 8, -1, 10, 11,
+ 12, 13, 14, 64, -1, 17, 69, -1, 20, 21,
+ 22, 23, 24, 25, -1, 63, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 63, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 38, 39, 40, 41,
+ -1, -1, -1, 45, 46, 5, 6, 7, 8, 9,
+ 10, 11, 12, 6, 7, 8, 9, 10, 11, 12
};
/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
symbol of state STATE-NUM. */
static const yytype_uint8 yystos[] =
{
- 0, 71, 0, 72, 1, 13, 14, 15, 16, 17,
- 18, 19, 20, 21, 22, 23, 24, 25, 26, 29,
- 30, 31, 32, 33, 34, 35, 36, 37, 57, 58,
- 59, 62, 73, 76, 62, 77, 77, 77, 77, 63,
- 78, 77, 77, 77, 77, 77, 8, 9, 38, 57,
- 59, 66, 69, 99, 102, 99, 77, 78, 78, 77,
- 77, 77, 77, 77, 77, 78, 66, 85, 86, 87,
- 99, 102, 60, 61, 60, 61, 62, 39, 42, 44,
- 45, 52, 53, 68, 88, 89, 90, 92, 94, 95,
- 88, 46, 47, 50, 51, 68, 80, 81, 83, 87,
- 90, 92, 98, 102, 78, 78, 57, 58, 79, 102,
- 78, 88, 64, 86, 102, 78, 90, 95, 78, 102,
- 102, 6, 8, 9, 100, 102, 104, 102, 63, 66,
- 11, 90, 80, 98, 102, 68, 81, 97, 98, 98,
- 90, 90, 66, 85, 95, 66, 66, 75, 104, 74,
- 104, 66, 102, 63, 6, 7, 9, 54, 63, 66,
- 9, 10, 55, 56, 87, 102, 63, 66, 57, 79,
- 84, 85, 99, 102, 100, 100, 66, 83, 63, 82,
- 95, 63, 66, 85, 63, 7, 102, 102, 66, 3,
- 4, 5, 6, 7, 8, 9, 10, 11, 12, 67,
- 89, 102, 39, 40, 41, 101, 102, 78, 63, 63,
- 9, 63, 63, 63, 63, 67, 94, 94, 73, 62,
- 73, 62, 104, 90, 95, 6, 7, 7, 7, 90,
- 102, 55, 68, 83, 95, 100, 66, 42, 95, 65,
- 9, 78, 64, 63, 85, 100, 101, 104, 104, 104,
- 6, 7, 104, 104, 104, 104, 104, 63, 67, 63,
- 98, 104, 47, 98, 98, 90, 90, 67, 67, 67,
- 63, 93, 95, 102, 93, 93, 93, 67, 87, 67,
- 67, 78, 63, 95, 82, 82, 90, 63, 78, 66,
- 67, 104, 104, 89, 80, 63, 63, 78, 63, 63,
- 90, 86, 65, 90, 40, 95, 98, 66, 91, 90,
- 67, 63, 95, 63, 48, 49, 96, 63, 95, 66,
- 63, 95, 104, 96, 67, 67, 63, 103, 104
+ 0, 72, 0, 73, 1, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, 22, 23, 24, 25, 26, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 43, 44,
+ 58, 59, 60, 63, 74, 77, 63, 78, 78, 78,
+ 78, 64, 79, 78, 78, 78, 78, 78, 8, 9,
+ 37, 58, 60, 67, 70, 100, 103, 100, 78, 79,
+ 79, 78, 78, 78, 78, 78, 78, 79, 67, 86,
+ 87, 88, 100, 103, 38, 41, 45, 46, 47, 48,
+ 51, 52, 69, 81, 82, 84, 88, 91, 93, 95,
+ 96, 99, 103, 84, 61, 62, 61, 62, 63, 53,
+ 54, 69, 89, 90, 91, 93, 89, 84, 79, 79,
+ 58, 59, 80, 103, 79, 89, 65, 87, 103, 79,
+ 91, 96, 79, 103, 103, 6, 8, 9, 101, 103,
+ 105, 103, 64, 67, 11, 91, 81, 99, 103, 69,
+ 82, 98, 99, 99, 91, 91, 67, 86, 96, 67,
+ 67, 67, 67, 9, 10, 56, 57, 88, 103, 64,
+ 67, 6, 7, 9, 55, 64, 76, 105, 75, 105,
+ 103, 64, 64, 64, 58, 80, 85, 86, 100, 103,
+ 101, 101, 67, 84, 64, 83, 96, 64, 67, 86,
+ 64, 7, 103, 103, 67, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 68, 90, 103, 38, 39,
+ 40, 102, 103, 79, 64, 64, 9, 64, 64, 64,
+ 64, 68, 95, 95, 105, 103, 56, 69, 84, 96,
+ 6, 7, 7, 7, 84, 74, 63, 74, 63, 91,
+ 96, 91, 84, 101, 67, 41, 96, 66, 9, 79,
+ 65, 64, 86, 101, 102, 105, 105, 105, 6, 7,
+ 105, 105, 105, 105, 105, 64, 68, 64, 99, 105,
+ 48, 99, 99, 91, 91, 68, 68, 68, 68, 88,
+ 68, 94, 96, 103, 94, 94, 94, 64, 68, 79,
+ 64, 96, 83, 83, 91, 64, 79, 67, 68, 105,
+ 105, 90, 81, 64, 64, 79, 64, 64, 91, 87,
+ 66, 91, 39, 9, 96, 99, 67, 92, 91, 68,
+ 103, 64, 96, 64, 49, 50, 97, 64, 96, 67,
+ 64, 96, 105, 97, 68, 68, 64, 104, 105
};
#define yyerrok (yyerrstatus = 0)
@@ -957,9 +973,18 @@ static const yytype_uint8 yystos[] =
/* Like YYERROR except do call yyerror. This remains here temporarily
to ease the transition to the new meaning of YYERROR, for GCC.
- Once GCC version 2 has supplanted version 1, this can go. */
+ Once GCC version 2 has supplanted version 1, this can go. However,
+ YYFAIL appears to be in use. Nevertheless, it is formally deprecated
+ in Bison 2.4.2's NEWS entry, where a plan to phase it out is
+ discussed. */
#define YYFAIL goto yyerrlab
+#if defined YYFAIL
+ /* This is here to suppress warnings from the GCC cpp's
+ -Wunused-macros. Normally we don't worry about that warning, but
+ some users do, and we want to make it easy for users to remove
+ YYFAIL uses, which will produce warnings from Bison 2.5. */
+#endif
#define YYRECOVERING() (!!yyerrstatus)
@@ -969,7 +994,6 @@ do \
{ \
yychar = (Token); \
yylval = (Value); \
- yytoken = YYTRANSLATE (yychar); \
YYPOPSTACK (1); \
goto yybackup; \
} \
@@ -1011,19 +1035,10 @@ while (YYID (0))
#endif
-/* YY_LOCATION_PRINT -- Print the location on the stream.
- This macro was not mandated originally: define only if we know
- we won't break user code: when these are the locations we know. */
+/* This macro is provided for backward compatibility. */
#ifndef YY_LOCATION_PRINT
-# if YYLTYPE_IS_TRIVIAL
-# define YY_LOCATION_PRINT(File, Loc) \
- fprintf (File, "%d.%d-%d.%d", \
- (Loc).first_line, (Loc).first_column, \
- (Loc).last_line, (Loc).last_column)
-# else
-# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
-# endif
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
#endif
@@ -1215,7 +1230,6 @@ int yydebug;
# define YYMAXDEPTH 10000
#endif
-
#if YYERROR_VERBOSE
@@ -1318,115 +1332,142 @@ yytnamerr (char *yyres, const char *yystr)
}
# endif
-/* Copy into YYRESULT an error message about the unexpected token
- YYCHAR while in state YYSTATE. Return the number of bytes copied,
- including the terminating null byte. If YYRESULT is null, do not
- copy anything; just return the number of bytes that would be
- copied. As a special case, return 0 if an ordinary "syntax error"
- message will do. Return YYSIZE_MAXIMUM if overflow occurs during
- size calculation. */
-static YYSIZE_T
-yysyntax_error (char *yyresult, int yystate, int yychar)
-{
- int yyn = yypact[yystate];
+/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message
+ about the unexpected token YYTOKEN for the state stack whose top is
+ YYSSP.
- if (! (YYPACT_NINF < yyn && yyn <= YYLAST))
- return 0;
- else
- {
- int yytype = YYTRANSLATE (yychar);
- YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]);
- YYSIZE_T yysize = yysize0;
- YYSIZE_T yysize1;
- int yysize_overflow = 0;
- enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
- char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
- int yyx;
-
-# if 0
- /* This is so xgettext sees the translatable formats that are
- constructed on the fly. */
- YY_("syntax error, unexpected %s");
- YY_("syntax error, unexpected %s, expecting %s");
- YY_("syntax error, unexpected %s, expecting %s or %s");
- YY_("syntax error, unexpected %s, expecting %s or %s or %s");
- YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s");
-# endif
- char *yyfmt;
- char const *yyf;
- static char const yyunexpected[] = "syntax error, unexpected %s";
- static char const yyexpecting[] = ", expecting %s";
- static char const yyor[] = " or %s";
- char yyformat[sizeof yyunexpected
- + sizeof yyexpecting - 1
- + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2)
- * (sizeof yyor - 1))];
- char const *yyprefix = yyexpecting;
-
- /* Start YYX at -YYN if negative to avoid negative indexes in
- YYCHECK. */
- int yyxbegin = yyn < 0 ? -yyn : 0;
-
- /* Stay within bounds of both yycheck and yytname. */
- int yychecklim = YYLAST - yyn + 1;
- int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
- int yycount = 1;
-
- yyarg[0] = yytname[yytype];
- yyfmt = yystpcpy (yyformat, yyunexpected);
-
- for (yyx = yyxbegin; yyx < yyxend; ++yyx)
- if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
- {
- if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
- {
- yycount = 1;
- yysize = yysize0;
- yyformat[sizeof yyunexpected - 1] = '\0';
- break;
- }
- yyarg[yycount++] = yytname[yyx];
- yysize1 = yysize + yytnamerr (0, yytname[yyx]);
- yysize_overflow |= (yysize1 < yysize);
- yysize = yysize1;
- yyfmt = yystpcpy (yyfmt, yyprefix);
- yyprefix = yyor;
- }
+ Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is
+ not large enough to hold the message. In that case, also set
+ *YYMSG_ALLOC to the required number of bytes. Return 2 if the
+ required number of bytes is too large to store. */
+static int
+yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg,
+ yytype_int16 *yyssp, int yytoken)
+{
+ YYSIZE_T yysize0 = yytnamerr (0, yytname[yytoken]);
+ YYSIZE_T yysize = yysize0;
+ YYSIZE_T yysize1;
+ enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+ /* Internationalized format string. */
+ const char *yyformat = 0;
+ /* Arguments of yyformat. */
+ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+ /* Number of reported tokens (one for the "unexpected", one per
+ "expected"). */
+ int yycount = 0;
+
+ /* There are many possibilities here to consider:
+ - Assume YYFAIL is not used. It's too flawed to consider. See
+ <http://lists.gnu.org/archive/html/bison-patches/2009-12/msg00024.html>
+ for details. YYERROR is fine as it does not invoke this
+ function.
+ - If this state is a consistent state with a default action, then
+ the only way this function was invoked is if the default action
+ is an error action. In that case, don't check for expected
+ tokens because there are none.
+ - The only way there can be no lookahead present (in yychar) is if
+ this state is a consistent state with a default action. Thus,
+ detecting the absence of a lookahead is sufficient to determine
+ that there is no unexpected or expected token to report. In that
+ case, just report a simple "syntax error".
+ - Don't assume there isn't a lookahead just because this state is a
+ consistent state with a default action. There might have been a
+ previous inconsistent state, consistent state with a non-default
+ action, or user semantic action that manipulated yychar.
+ - Of course, the expected token list depends on states to have
+ correct lookahead information, and it depends on the parser not
+ to perform extra reductions after fetching a lookahead from the
+ scanner and before detecting a syntax error. Thus, state merging
+ (from LALR or IELR) and default reductions corrupt the expected
+ token list. However, the list is correct for canonical LR with
+ one exception: it will still contain any token that will not be
+ accepted due to an error action in a later state.
+ */
+ if (yytoken != YYEMPTY)
+ {
+ int yyn = yypact[*yyssp];
+ yyarg[yycount++] = yytname[yytoken];
+ if (!yypact_value_is_default (yyn))
+ {
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. In other words, skip the first -YYN actions for
+ this state because they are default actions. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn + 1;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yyx;
+
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR
+ && !yytable_value_is_error (yytable[yyx + yyn]))
+ {
+ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+ {
+ yycount = 1;
+ yysize = yysize0;
+ break;
+ }
+ yyarg[yycount++] = yytname[yyx];
+ yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+ if (! (yysize <= yysize1
+ && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+ return 2;
+ yysize = yysize1;
+ }
+ }
+ }
- yyf = YY_(yyformat);
- yysize1 = yysize + yystrlen (yyf);
- yysize_overflow |= (yysize1 < yysize);
- yysize = yysize1;
+ switch (yycount)
+ {
+# define YYCASE_(N, S) \
+ case N: \
+ yyformat = S; \
+ break
+ YYCASE_(0, YY_("syntax error"));
+ YYCASE_(1, YY_("syntax error, unexpected %s"));
+ YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s"));
+ YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s"));
+ YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s"));
+ YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"));
+# undef YYCASE_
+ }
- if (yysize_overflow)
- return YYSIZE_MAXIMUM;
+ yysize1 = yysize + yystrlen (yyformat);
+ if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+ return 2;
+ yysize = yysize1;
- if (yyresult)
- {
- /* Avoid sprintf, as that infringes on the user's name space.
- Don't have undefined behavior even if the translation
- produced a string with the wrong number of "%s"s. */
- char *yyp = yyresult;
- int yyi = 0;
- while ((*yyp = *yyf) != '\0')
- {
- if (*yyp == '%' && yyf[1] == 's' && yyi < yycount)
- {
- yyp += yytnamerr (yyp, yyarg[yyi++]);
- yyf += 2;
- }
- else
- {
- yyp++;
- yyf++;
- }
- }
- }
- return yysize;
+ if (*yymsg_alloc < yysize)
+ {
+ *yymsg_alloc = 2 * yysize;
+ if (! (yysize <= *yymsg_alloc
+ && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM))
+ *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM;
+ return 1;
}
+
+ /* Avoid sprintf, as that infringes on the user's name space.
+ Don't have undefined behavior even if the translation
+ produced a string with the wrong number of "%s"s. */
+ {
+ char *yyp = *yymsg;
+ int yyi = 0;
+ while ((*yyp = *yyformat) != '\0')
+ if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount)
+ {
+ yyp += yytnamerr (yyp, yyarg[yyi++]);
+ yyformat += 2;
+ }
+ else
+ {
+ yyp++;
+ yyformat++;
+ }
+ }
+ return 0;
}
#endif /* YYERROR_VERBOSE */
-
/*-----------------------------------------------.
| Release the memory associated to this symbol. |
@@ -1459,6 +1500,7 @@ yydestruct (yymsg, yytype, yyvaluep)
}
}
+
/* Prevent warnings from -Wmissing-prototypes. */
#ifdef YYPARSE_PARAM
#if defined __STDC__ || defined __cplusplus
@@ -1485,10 +1527,9 @@ YYSTYPE yylval;
int yynerrs;
-
-/*-------------------------.
-| yyparse or yypush_parse. |
-`-------------------------*/
+/*----------.
+| yyparse. |
+`----------*/
#ifdef YYPARSE_PARAM
#if (defined __STDC__ || defined __C99__FUNC__ \
@@ -1512,8 +1553,6 @@ yyparse ()
#endif
#endif
{
-
-
int yystate;
/* Number of tokens to shift before error messages enabled. */
int yyerrstatus;
@@ -1668,7 +1707,7 @@ yybackup:
/* First try to decide what to do without reference to lookahead token. */
yyn = yypact[yystate];
- if (yyn == YYPACT_NINF)
+ if (yypact_value_is_default (yyn))
goto yydefault;
/* Not known => get a lookahead token if don't already have one. */
@@ -1699,8 +1738,8 @@ yybackup:
yyn = yytable[yyn];
if (yyn <= 0)
{
- if (yyn == 0 || yyn == YYTABLE_NINF)
- goto yyerrlab;
+ if (yytable_value_is_error (yyn))
+ goto yyerrlab;
yyn = -yyn;
goto yyreduce;
}
@@ -1755,8 +1794,8 @@ yyreduce:
{
case 3:
-/* Line 1455 of yacc.c */
-#line 69 "a.y"
+/* Line 1806 of yacc.c */
+#line 70 "a.y"
{
stmtline = lineno;
}
@@ -1764,8 +1803,8 @@ yyreduce:
case 5:
-/* Line 1455 of yacc.c */
-#line 76 "a.y"
+/* Line 1806 of yacc.c */
+#line 77 "a.y"
{
if((yyvsp[(1) - (2)].sym)->value != pc)
yyerror("redeclaration of %s", (yyvsp[(1) - (2)].sym)->name);
@@ -1775,8 +1814,8 @@ yyreduce:
case 7:
-/* Line 1455 of yacc.c */
-#line 83 "a.y"
+/* Line 1806 of yacc.c */
+#line 84 "a.y"
{
(yyvsp[(1) - (2)].sym)->type = LLAB;
(yyvsp[(1) - (2)].sym)->value = pc;
@@ -1785,8 +1824,8 @@ yyreduce:
case 9:
-/* Line 1455 of yacc.c */
-#line 89 "a.y"
+/* Line 1806 of yacc.c */
+#line 90 "a.y"
{
(yyvsp[(1) - (4)].sym)->type = LVAR;
(yyvsp[(1) - (4)].sym)->value = (yyvsp[(3) - (4)].lval);
@@ -1795,8 +1834,8 @@ yyreduce:
case 10:
-/* Line 1455 of yacc.c */
-#line 94 "a.y"
+/* Line 1806 of yacc.c */
+#line 95 "a.y"
{
if((yyvsp[(1) - (4)].sym)->value != (yyvsp[(3) - (4)].lval))
yyerror("redeclaration of %s", (yyvsp[(1) - (4)].sym)->name);
@@ -1806,8 +1845,8 @@ yyreduce:
case 14:
-/* Line 1455 of yacc.c */
-#line 108 "a.y"
+/* Line 1806 of yacc.c */
+#line 109 "a.y"
{
outcode((yyvsp[(1) - (7)].lval), (yyvsp[(2) - (7)].lval), &(yyvsp[(3) - (7)].gen), (yyvsp[(5) - (7)].lval), &(yyvsp[(7) - (7)].gen));
}
@@ -1815,8 +1854,8 @@ yyreduce:
case 15:
-/* Line 1455 of yacc.c */
-#line 112 "a.y"
+/* Line 1806 of yacc.c */
+#line 113 "a.y"
{
outcode((yyvsp[(1) - (6)].lval), (yyvsp[(2) - (6)].lval), &(yyvsp[(3) - (6)].gen), (yyvsp[(5) - (6)].lval), &nullgen);
}
@@ -1824,8 +1863,8 @@ yyreduce:
case 16:
-/* Line 1455 of yacc.c */
-#line 116 "a.y"
+/* Line 1806 of yacc.c */
+#line 117 "a.y"
{
outcode((yyvsp[(1) - (5)].lval), (yyvsp[(2) - (5)].lval), &(yyvsp[(3) - (5)].gen), NREG, &(yyvsp[(5) - (5)].gen));
}
@@ -1833,8 +1872,8 @@ yyreduce:
case 17:
-/* Line 1455 of yacc.c */
-#line 123 "a.y"
+/* Line 1806 of yacc.c */
+#line 124 "a.y"
{
outcode((yyvsp[(1) - (5)].lval), (yyvsp[(2) - (5)].lval), &(yyvsp[(3) - (5)].gen), NREG, &(yyvsp[(5) - (5)].gen));
}
@@ -1842,8 +1881,8 @@ yyreduce:
case 18:
-/* Line 1455 of yacc.c */
-#line 130 "a.y"
+/* Line 1806 of yacc.c */
+#line 131 "a.y"
{
outcode((yyvsp[(1) - (5)].lval), (yyvsp[(2) - (5)].lval), &(yyvsp[(3) - (5)].gen), NREG, &(yyvsp[(5) - (5)].gen));
}
@@ -1851,8 +1890,8 @@ yyreduce:
case 19:
-/* Line 1455 of yacc.c */
-#line 137 "a.y"
+/* Line 1806 of yacc.c */
+#line 138 "a.y"
{
outcode((yyvsp[(1) - (4)].lval), (yyvsp[(2) - (4)].lval), &nullgen, NREG, &(yyvsp[(4) - (4)].gen));
}
@@ -1860,8 +1899,8 @@ yyreduce:
case 20:
-/* Line 1455 of yacc.c */
-#line 141 "a.y"
+/* Line 1806 of yacc.c */
+#line 142 "a.y"
{
outcode((yyvsp[(1) - (4)].lval), (yyvsp[(2) - (4)].lval), &nullgen, NREG, &(yyvsp[(4) - (4)].gen));
}
@@ -1869,8 +1908,8 @@ yyreduce:
case 21:
-/* Line 1455 of yacc.c */
-#line 148 "a.y"
+/* Line 1806 of yacc.c */
+#line 149 "a.y"
{
outcode((yyvsp[(1) - (3)].lval), Always, &nullgen, NREG, &(yyvsp[(3) - (3)].gen));
}
@@ -1878,8 +1917,8 @@ yyreduce:
case 22:
-/* Line 1455 of yacc.c */
-#line 155 "a.y"
+/* Line 1806 of yacc.c */
+#line 156 "a.y"
{
outcode((yyvsp[(1) - (3)].lval), Always, &nullgen, NREG, &(yyvsp[(3) - (3)].gen));
}
@@ -1887,8 +1926,8 @@ yyreduce:
case 23:
-/* Line 1455 of yacc.c */
-#line 162 "a.y"
+/* Line 1806 of yacc.c */
+#line 163 "a.y"
{
outcode((yyvsp[(1) - (4)].lval), (yyvsp[(2) - (4)].lval), &nullgen, NREG, &(yyvsp[(4) - (4)].gen));
}
@@ -1896,8 +1935,8 @@ yyreduce:
case 24:
-/* Line 1455 of yacc.c */
-#line 169 "a.y"
+/* Line 1806 of yacc.c */
+#line 170 "a.y"
{
outcode((yyvsp[(1) - (6)].lval), (yyvsp[(2) - (6)].lval), &(yyvsp[(3) - (6)].gen), (yyvsp[(5) - (6)].lval), &nullgen);
}
@@ -1905,8 +1944,8 @@ yyreduce:
case 25:
-/* Line 1455 of yacc.c */
-#line 176 "a.y"
+/* Line 1806 of yacc.c */
+#line 177 "a.y"
{
Gen g;
@@ -1919,8 +1958,8 @@ yyreduce:
case 26:
-/* Line 1455 of yacc.c */
-#line 185 "a.y"
+/* Line 1806 of yacc.c */
+#line 186 "a.y"
{
Gen g;
@@ -1933,8 +1972,8 @@ yyreduce:
case 27:
-/* Line 1455 of yacc.c */
-#line 197 "a.y"
+/* Line 1806 of yacc.c */
+#line 198 "a.y"
{
outcode((yyvsp[(1) - (7)].lval), (yyvsp[(2) - (7)].lval), &(yyvsp[(5) - (7)].gen), (yyvsp[(3) - (7)].gen).reg, &(yyvsp[(7) - (7)].gen));
}
@@ -1942,8 +1981,8 @@ yyreduce:
case 28:
-/* Line 1455 of yacc.c */
-#line 201 "a.y"
+/* Line 1806 of yacc.c */
+#line 202 "a.y"
{
outcode((yyvsp[(1) - (6)].lval), (yyvsp[(2) - (6)].lval), &(yyvsp[(5) - (6)].gen), (yyvsp[(3) - (6)].gen).reg, &(yyvsp[(3) - (6)].gen));
}
@@ -1951,8 +1990,8 @@ yyreduce:
case 29:
-/* Line 1455 of yacc.c */
-#line 205 "a.y"
+/* Line 1806 of yacc.c */
+#line 206 "a.y"
{
outcode((yyvsp[(1) - (6)].lval), (yyvsp[(2) - (6)].lval), &(yyvsp[(4) - (6)].gen), (yyvsp[(6) - (6)].gen).reg, &(yyvsp[(6) - (6)].gen));
}
@@ -1960,8 +1999,8 @@ yyreduce:
case 30:
-/* Line 1455 of yacc.c */
-#line 212 "a.y"
+/* Line 1806 of yacc.c */
+#line 213 "a.y"
{
outcode((yyvsp[(1) - (3)].lval), (yyvsp[(2) - (3)].lval), &nullgen, NREG, &nullgen);
}
@@ -1969,62 +2008,68 @@ yyreduce:
case 31:
-/* Line 1455 of yacc.c */
-#line 219 "a.y"
+/* Line 1806 of yacc.c */
+#line 220 "a.y"
{
+ (yyvsp[(4) - (4)].gen).type = D_CONST2;
+ (yyvsp[(4) - (4)].gen).offset2 = ArgsSizeUnknown;
outcode((yyvsp[(1) - (4)].lval), Always, &(yyvsp[(2) - (4)].gen), 0, &(yyvsp[(4) - (4)].gen));
}
break;
case 32:
-/* Line 1455 of yacc.c */
-#line 223 "a.y"
+/* Line 1806 of yacc.c */
+#line 226 "a.y"
{
+ (yyvsp[(6) - (6)].gen).type = D_CONST2;
+ (yyvsp[(6) - (6)].gen).offset2 = ArgsSizeUnknown;
outcode((yyvsp[(1) - (6)].lval), Always, &(yyvsp[(2) - (6)].gen), (yyvsp[(4) - (6)].lval), &(yyvsp[(6) - (6)].gen));
}
break;
case 33:
-/* Line 1455 of yacc.c */
-#line 230 "a.y"
+/* Line 1806 of yacc.c */
+#line 232 "a.y"
{
- outcode((yyvsp[(1) - (6)].lval), Always, &(yyvsp[(2) - (6)].gen), (yyvsp[(4) - (6)].lval), &(yyvsp[(6) - (6)].gen));
+ (yyvsp[(6) - (8)].gen).type = D_CONST2;
+ (yyvsp[(6) - (8)].gen).offset2 = (yyvsp[(8) - (8)].lval);
+ outcode((yyvsp[(1) - (8)].lval), Always, &(yyvsp[(2) - (8)].gen), (yyvsp[(4) - (8)].lval), &(yyvsp[(6) - (8)].gen));
}
break;
case 34:
-/* Line 1455 of yacc.c */
-#line 237 "a.y"
+/* Line 1806 of yacc.c */
+#line 241 "a.y"
{
- outcode((yyvsp[(1) - (4)].lval), (yyvsp[(2) - (4)].lval), &(yyvsp[(3) - (4)].gen), NREG, &nullgen);
+ outcode((yyvsp[(1) - (6)].lval), Always, &(yyvsp[(2) - (6)].gen), (yyvsp[(4) - (6)].lval), &(yyvsp[(6) - (6)].gen));
}
break;
case 35:
-/* Line 1455 of yacc.c */
-#line 244 "a.y"
+/* Line 1806 of yacc.c */
+#line 248 "a.y"
{
- outcode((yyvsp[(1) - (3)].lval), Always, &nullgen, NREG, &(yyvsp[(3) - (3)].gen));
+ outcode((yyvsp[(1) - (4)].lval), (yyvsp[(2) - (4)].lval), &(yyvsp[(3) - (4)].gen), NREG, &nullgen);
}
break;
case 36:
-/* Line 1455 of yacc.c */
-#line 251 "a.y"
+/* Line 1806 of yacc.c */
+#line 255 "a.y"
{
- outcode((yyvsp[(1) - (5)].lval), (yyvsp[(2) - (5)].lval), &(yyvsp[(3) - (5)].gen), NREG, &(yyvsp[(5) - (5)].gen));
+ outcode((yyvsp[(1) - (3)].lval), Always, &nullgen, NREG, &(yyvsp[(3) - (3)].gen));
}
break;
case 37:
-/* Line 1455 of yacc.c */
-#line 255 "a.y"
+/* Line 1806 of yacc.c */
+#line 262 "a.y"
{
outcode((yyvsp[(1) - (5)].lval), (yyvsp[(2) - (5)].lval), &(yyvsp[(3) - (5)].gen), NREG, &(yyvsp[(5) - (5)].gen));
}
@@ -2032,26 +2077,35 @@ yyreduce:
case 38:
-/* Line 1455 of yacc.c */
-#line 259 "a.y"
+/* Line 1806 of yacc.c */
+#line 266 "a.y"
{
- outcode((yyvsp[(1) - (7)].lval), (yyvsp[(2) - (7)].lval), &(yyvsp[(3) - (7)].gen), (yyvsp[(5) - (7)].lval), &(yyvsp[(7) - (7)].gen));
+ outcode((yyvsp[(1) - (5)].lval), (yyvsp[(2) - (5)].lval), &(yyvsp[(3) - (5)].gen), NREG, &(yyvsp[(5) - (5)].gen));
}
break;
case 39:
-/* Line 1455 of yacc.c */
-#line 263 "a.y"
+/* Line 1806 of yacc.c */
+#line 270 "a.y"
{
- outcode((yyvsp[(1) - (6)].lval), (yyvsp[(2) - (6)].lval), &(yyvsp[(3) - (6)].gen), (yyvsp[(5) - (6)].gen).reg, &nullgen);
+ outcode((yyvsp[(1) - (7)].lval), (yyvsp[(2) - (7)].lval), &(yyvsp[(3) - (7)].gen), (yyvsp[(5) - (7)].lval), &(yyvsp[(7) - (7)].gen));
}
break;
case 40:
-/* Line 1455 of yacc.c */
-#line 270 "a.y"
+/* Line 1806 of yacc.c */
+#line 274 "a.y"
+ {
+ outcode((yyvsp[(1) - (6)].lval), (yyvsp[(2) - (6)].lval), &(yyvsp[(3) - (6)].gen), (yyvsp[(5) - (6)].gen).reg, &nullgen);
+ }
+ break;
+
+ case 41:
+
+/* Line 1806 of yacc.c */
+#line 281 "a.y"
{
Gen g;
@@ -2072,19 +2126,19 @@ yyreduce:
}
break;
- case 41:
+ case 42:
-/* Line 1455 of yacc.c */
-#line 292 "a.y"
+/* Line 1806 of yacc.c */
+#line 303 "a.y"
{
outcode((yyvsp[(1) - (7)].lval), (yyvsp[(2) - (7)].lval), &(yyvsp[(3) - (7)].gen), (yyvsp[(5) - (7)].gen).reg, &(yyvsp[(7) - (7)].gen));
}
break;
- case 42:
+ case 43:
-/* Line 1455 of yacc.c */
-#line 300 "a.y"
+/* Line 1806 of yacc.c */
+#line 311 "a.y"
{
(yyvsp[(7) - (9)].gen).type = D_REGREG2;
(yyvsp[(7) - (9)].gen).offset = (yyvsp[(9) - (9)].lval);
@@ -2092,55 +2146,79 @@ yyreduce:
}
break;
- case 43:
+ case 44:
-/* Line 1455 of yacc.c */
-#line 309 "a.y"
+/* Line 1806 of yacc.c */
+#line 320 "a.y"
{
outcode((yyvsp[(1) - (2)].lval), Always, &(yyvsp[(2) - (2)].gen), NREG, &nullgen);
}
break;
- case 44:
+ case 45:
-/* Line 1455 of yacc.c */
-#line 316 "a.y"
+/* Line 1806 of yacc.c */
+#line 327 "a.y"
+ {
+ if((yyvsp[(2) - (4)].gen).type != D_CONST || (yyvsp[(4) - (4)].gen).type != D_CONST)
+ yyerror("arguments to PCDATA must be integer constants");
+ outcode((yyvsp[(1) - (4)].lval), Always, &(yyvsp[(2) - (4)].gen), NREG, &(yyvsp[(4) - (4)].gen));
+ }
+ break;
+
+ case 46:
+
+/* Line 1806 of yacc.c */
+#line 336 "a.y"
+ {
+ if((yyvsp[(2) - (4)].gen).type != D_CONST)
+ yyerror("index for FUNCDATA must be integer constant");
+ if((yyvsp[(4) - (4)].gen).type != D_EXTERN && (yyvsp[(4) - (4)].gen).type != D_STATIC)
+ yyerror("value for FUNCDATA must be symbol reference");
+ outcode((yyvsp[(1) - (4)].lval), Always, &(yyvsp[(2) - (4)].gen), NREG, &(yyvsp[(4) - (4)].gen));
+ }
+ break;
+
+ case 47:
+
+/* Line 1806 of yacc.c */
+#line 347 "a.y"
{
outcode((yyvsp[(1) - (2)].lval), Always, &nullgen, NREG, &nullgen);
}
break;
- case 45:
+ case 48:
-/* Line 1455 of yacc.c */
-#line 321 "a.y"
+/* Line 1806 of yacc.c */
+#line 352 "a.y"
{
(yyval.lval) = Always;
}
break;
- case 46:
+ case 49:
-/* Line 1455 of yacc.c */
-#line 325 "a.y"
+/* Line 1806 of yacc.c */
+#line 356 "a.y"
{
(yyval.lval) = ((yyvsp[(1) - (2)].lval) & ~C_SCOND) | (yyvsp[(2) - (2)].lval);
}
break;
- case 47:
+ case 50:
-/* Line 1455 of yacc.c */
-#line 329 "a.y"
+/* Line 1806 of yacc.c */
+#line 360 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (2)].lval) | (yyvsp[(2) - (2)].lval);
}
break;
- case 50:
+ case 53:
-/* Line 1455 of yacc.c */
-#line 338 "a.y"
+/* Line 1806 of yacc.c */
+#line 369 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_BRANCH;
@@ -2148,10 +2226,10 @@ yyreduce:
}
break;
- case 51:
+ case 54:
-/* Line 1455 of yacc.c */
-#line 344 "a.y"
+/* Line 1806 of yacc.c */
+#line 375 "a.y"
{
(yyval.gen) = nullgen;
if(pass == 2)
@@ -2162,10 +2240,10 @@ yyreduce:
}
break;
- case 52:
+ case 55:
-/* Line 1455 of yacc.c */
-#line 353 "a.y"
+/* Line 1806 of yacc.c */
+#line 384 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_BRANCH;
@@ -2174,10 +2252,10 @@ yyreduce:
}
break;
- case 53:
+ case 56:
-/* Line 1455 of yacc.c */
-#line 361 "a.y"
+/* Line 1806 of yacc.c */
+#line 392 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_CONST;
@@ -2185,30 +2263,30 @@ yyreduce:
}
break;
- case 54:
+ case 57:
-/* Line 1455 of yacc.c */
-#line 367 "a.y"
+/* Line 1806 of yacc.c */
+#line 398 "a.y"
{
(yyval.gen) = (yyvsp[(2) - (2)].gen);
(yyval.gen).type = D_CONST;
}
break;
- case 55:
+ case 58:
-/* Line 1455 of yacc.c */
-#line 372 "a.y"
+/* Line 1806 of yacc.c */
+#line 403 "a.y"
{
(yyval.gen) = (yyvsp[(4) - (4)].gen);
(yyval.gen).type = D_OCONST;
}
break;
- case 56:
+ case 59:
-/* Line 1455 of yacc.c */
-#line 377 "a.y"
+/* Line 1806 of yacc.c */
+#line 408 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_SCONST;
@@ -2216,10 +2294,10 @@ yyreduce:
}
break;
- case 58:
+ case 61:
-/* Line 1455 of yacc.c */
-#line 386 "a.y"
+/* Line 1806 of yacc.c */
+#line 417 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_FCONST;
@@ -2227,10 +2305,10 @@ yyreduce:
}
break;
- case 59:
+ case 62:
-/* Line 1455 of yacc.c */
-#line 392 "a.y"
+/* Line 1806 of yacc.c */
+#line 423 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_FCONST;
@@ -2238,19 +2316,19 @@ yyreduce:
}
break;
- case 60:
+ case 63:
-/* Line 1455 of yacc.c */
-#line 400 "a.y"
+/* Line 1806 of yacc.c */
+#line 431 "a.y"
{
(yyval.lval) = 1 << (yyvsp[(1) - (1)].lval);
}
break;
- case 61:
+ case 64:
-/* Line 1455 of yacc.c */
-#line 404 "a.y"
+/* Line 1806 of yacc.c */
+#line 435 "a.y"
{
int i;
(yyval.lval)=0;
@@ -2261,29 +2339,29 @@ yyreduce:
}
break;
- case 62:
+ case 65:
-/* Line 1455 of yacc.c */
-#line 413 "a.y"
+/* Line 1806 of yacc.c */
+#line 444 "a.y"
{
(yyval.lval) = (1<<(yyvsp[(1) - (3)].lval)) | (yyvsp[(3) - (3)].lval);
}
break;
- case 66:
+ case 69:
-/* Line 1455 of yacc.c */
-#line 422 "a.y"
+/* Line 1806 of yacc.c */
+#line 453 "a.y"
{
(yyval.gen) = (yyvsp[(1) - (4)].gen);
(yyval.gen).reg = (yyvsp[(3) - (4)].lval);
}
break;
- case 67:
+ case 70:
-/* Line 1455 of yacc.c */
-#line 427 "a.y"
+/* Line 1806 of yacc.c */
+#line 458 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_PSR;
@@ -2291,10 +2369,10 @@ yyreduce:
}
break;
- case 68:
+ case 71:
-/* Line 1455 of yacc.c */
-#line 433 "a.y"
+/* Line 1806 of yacc.c */
+#line 464 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_FPCR;
@@ -2302,10 +2380,10 @@ yyreduce:
}
break;
- case 69:
+ case 72:
-/* Line 1455 of yacc.c */
-#line 439 "a.y"
+/* Line 1806 of yacc.c */
+#line 470 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_OREG;
@@ -2313,10 +2391,10 @@ yyreduce:
}
break;
- case 73:
+ case 76:
-/* Line 1455 of yacc.c */
-#line 450 "a.y"
+/* Line 1806 of yacc.c */
+#line 481 "a.y"
{
(yyval.gen) = (yyvsp[(1) - (1)].gen);
if((yyvsp[(1) - (1)].gen).name != D_EXTERN && (yyvsp[(1) - (1)].gen).name != D_STATIC) {
@@ -2324,10 +2402,10 @@ yyreduce:
}
break;
- case 74:
+ case 77:
-/* Line 1455 of yacc.c */
-#line 458 "a.y"
+/* Line 1806 of yacc.c */
+#line 489 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_OREG;
@@ -2336,10 +2414,10 @@ yyreduce:
}
break;
- case 76:
+ case 79:
-/* Line 1455 of yacc.c */
-#line 468 "a.y"
+/* Line 1806 of yacc.c */
+#line 499 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_OREG;
@@ -2348,10 +2426,10 @@ yyreduce:
}
break;
- case 78:
+ case 81:
-/* Line 1455 of yacc.c */
-#line 478 "a.y"
+/* Line 1806 of yacc.c */
+#line 509 "a.y"
{
(yyval.gen) = (yyvsp[(1) - (4)].gen);
(yyval.gen).type = D_OREG;
@@ -2359,10 +2437,10 @@ yyreduce:
}
break;
- case 83:
+ case 86:
-/* Line 1455 of yacc.c */
-#line 491 "a.y"
+/* Line 1806 of yacc.c */
+#line 522 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_CONST;
@@ -2370,10 +2448,10 @@ yyreduce:
}
break;
- case 84:
+ case 87:
-/* Line 1455 of yacc.c */
-#line 499 "a.y"
+/* Line 1806 of yacc.c */
+#line 530 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_REG;
@@ -2381,10 +2459,10 @@ yyreduce:
}
break;
- case 85:
+ case 88:
-/* Line 1455 of yacc.c */
-#line 507 "a.y"
+/* Line 1806 of yacc.c */
+#line 538 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_REGREG;
@@ -2393,10 +2471,10 @@ yyreduce:
}
break;
- case 86:
+ case 89:
-/* Line 1455 of yacc.c */
-#line 516 "a.y"
+/* Line 1806 of yacc.c */
+#line 547 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_SHIFT;
@@ -2404,10 +2482,10 @@ yyreduce:
}
break;
- case 87:
+ case 90:
-/* Line 1455 of yacc.c */
-#line 522 "a.y"
+/* Line 1806 of yacc.c */
+#line 553 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_SHIFT;
@@ -2415,10 +2493,10 @@ yyreduce:
}
break;
- case 88:
+ case 91:
-/* Line 1455 of yacc.c */
-#line 528 "a.y"
+/* Line 1806 of yacc.c */
+#line 559 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_SHIFT;
@@ -2426,10 +2504,10 @@ yyreduce:
}
break;
- case 89:
+ case 92:
-/* Line 1455 of yacc.c */
-#line 534 "a.y"
+/* Line 1806 of yacc.c */
+#line 565 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_SHIFT;
@@ -2437,10 +2515,10 @@ yyreduce:
}
break;
- case 90:
+ case 93:
-/* Line 1455 of yacc.c */
-#line 542 "a.y"
+/* Line 1806 of yacc.c */
+#line 573 "a.y"
{
if((yyval.lval) < 0 || (yyval.lval) >= 16)
print("register value out of range\n");
@@ -2448,10 +2526,10 @@ yyreduce:
}
break;
- case 91:
+ case 94:
-/* Line 1455 of yacc.c */
-#line 548 "a.y"
+/* Line 1806 of yacc.c */
+#line 579 "a.y"
{
if((yyval.lval) < 0 || (yyval.lval) >= 32)
print("shift value out of range\n");
@@ -2459,19 +2537,19 @@ yyreduce:
}
break;
- case 93:
+ case 96:
-/* Line 1455 of yacc.c */
-#line 557 "a.y"
+/* Line 1806 of yacc.c */
+#line 588 "a.y"
{
(yyval.lval) = REGPC;
}
break;
- case 94:
+ case 97:
-/* Line 1455 of yacc.c */
-#line 561 "a.y"
+/* Line 1806 of yacc.c */
+#line 592 "a.y"
{
if((yyvsp[(3) - (4)].lval) < 0 || (yyvsp[(3) - (4)].lval) >= NREG)
print("register value out of range\n");
@@ -2479,19 +2557,19 @@ yyreduce:
}
break;
- case 96:
+ case 99:
-/* Line 1455 of yacc.c */
-#line 570 "a.y"
+/* Line 1806 of yacc.c */
+#line 601 "a.y"
{
(yyval.lval) = REGSP;
}
break;
- case 98:
+ case 101:
-/* Line 1455 of yacc.c */
-#line 577 "a.y"
+/* Line 1806 of yacc.c */
+#line 608 "a.y"
{
if((yyvsp[(3) - (4)].lval) < 0 || (yyvsp[(3) - (4)].lval) >= NREG)
print("register value out of range\n");
@@ -2499,10 +2577,10 @@ yyreduce:
}
break;
- case 101:
+ case 104:
-/* Line 1455 of yacc.c */
-#line 589 "a.y"
+/* Line 1806 of yacc.c */
+#line 620 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_FREG;
@@ -2510,10 +2588,10 @@ yyreduce:
}
break;
- case 102:
+ case 105:
-/* Line 1455 of yacc.c */
-#line 595 "a.y"
+/* Line 1806 of yacc.c */
+#line 626 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_FREG;
@@ -2521,10 +2599,10 @@ yyreduce:
}
break;
- case 103:
+ case 106:
-/* Line 1455 of yacc.c */
-#line 603 "a.y"
+/* Line 1806 of yacc.c */
+#line 634 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_OREG;
@@ -2534,10 +2612,10 @@ yyreduce:
}
break;
- case 104:
+ case 107:
-/* Line 1455 of yacc.c */
-#line 611 "a.y"
+/* Line 1806 of yacc.c */
+#line 642 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_OREG;
@@ -2547,10 +2625,10 @@ yyreduce:
}
break;
- case 105:
+ case 108:
-/* Line 1455 of yacc.c */
-#line 619 "a.y"
+/* Line 1806 of yacc.c */
+#line 650 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_OREG;
@@ -2560,181 +2638,181 @@ yyreduce:
}
break;
- case 106:
+ case 109:
-/* Line 1455 of yacc.c */
-#line 628 "a.y"
+/* Line 1806 of yacc.c */
+#line 659 "a.y"
{
(yyval.lval) = 0;
}
break;
- case 107:
+ case 110:
-/* Line 1455 of yacc.c */
-#line 632 "a.y"
+/* Line 1806 of yacc.c */
+#line 663 "a.y"
{
(yyval.lval) = (yyvsp[(2) - (2)].lval);
}
break;
- case 108:
+ case 111:
-/* Line 1455 of yacc.c */
-#line 636 "a.y"
+/* Line 1806 of yacc.c */
+#line 667 "a.y"
{
(yyval.lval) = -(yyvsp[(2) - (2)].lval);
}
break;
- case 113:
+ case 116:
-/* Line 1455 of yacc.c */
-#line 648 "a.y"
+/* Line 1806 of yacc.c */
+#line 679 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (1)].sym)->value;
}
break;
- case 114:
+ case 117:
-/* Line 1455 of yacc.c */
-#line 652 "a.y"
+/* Line 1806 of yacc.c */
+#line 683 "a.y"
{
(yyval.lval) = -(yyvsp[(2) - (2)].lval);
}
break;
- case 115:
+ case 118:
-/* Line 1455 of yacc.c */
-#line 656 "a.y"
+/* Line 1806 of yacc.c */
+#line 687 "a.y"
{
(yyval.lval) = (yyvsp[(2) - (2)].lval);
}
break;
- case 116:
+ case 119:
-/* Line 1455 of yacc.c */
-#line 660 "a.y"
+/* Line 1806 of yacc.c */
+#line 691 "a.y"
{
(yyval.lval) = ~(yyvsp[(2) - (2)].lval);
}
break;
- case 117:
+ case 120:
-/* Line 1455 of yacc.c */
-#line 664 "a.y"
+/* Line 1806 of yacc.c */
+#line 695 "a.y"
{
(yyval.lval) = (yyvsp[(2) - (3)].lval);
}
break;
- case 118:
+ case 121:
-/* Line 1455 of yacc.c */
-#line 669 "a.y"
+/* Line 1806 of yacc.c */
+#line 700 "a.y"
{
(yyval.lval) = 0;
}
break;
- case 119:
+ case 122:
-/* Line 1455 of yacc.c */
-#line 673 "a.y"
+/* Line 1806 of yacc.c */
+#line 704 "a.y"
{
(yyval.lval) = (yyvsp[(2) - (2)].lval);
}
break;
- case 121:
+ case 124:
-/* Line 1455 of yacc.c */
-#line 680 "a.y"
+/* Line 1806 of yacc.c */
+#line 711 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (3)].lval) + (yyvsp[(3) - (3)].lval);
}
break;
- case 122:
+ case 125:
-/* Line 1455 of yacc.c */
-#line 684 "a.y"
+/* Line 1806 of yacc.c */
+#line 715 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (3)].lval) - (yyvsp[(3) - (3)].lval);
}
break;
- case 123:
+ case 126:
-/* Line 1455 of yacc.c */
-#line 688 "a.y"
+/* Line 1806 of yacc.c */
+#line 719 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (3)].lval) * (yyvsp[(3) - (3)].lval);
}
break;
- case 124:
+ case 127:
-/* Line 1455 of yacc.c */
-#line 692 "a.y"
+/* Line 1806 of yacc.c */
+#line 723 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (3)].lval) / (yyvsp[(3) - (3)].lval);
}
break;
- case 125:
+ case 128:
-/* Line 1455 of yacc.c */
-#line 696 "a.y"
+/* Line 1806 of yacc.c */
+#line 727 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (3)].lval) % (yyvsp[(3) - (3)].lval);
}
break;
- case 126:
+ case 129:
-/* Line 1455 of yacc.c */
-#line 700 "a.y"
+/* Line 1806 of yacc.c */
+#line 731 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (4)].lval) << (yyvsp[(4) - (4)].lval);
}
break;
- case 127:
+ case 130:
-/* Line 1455 of yacc.c */
-#line 704 "a.y"
+/* Line 1806 of yacc.c */
+#line 735 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (4)].lval) >> (yyvsp[(4) - (4)].lval);
}
break;
- case 128:
+ case 131:
-/* Line 1455 of yacc.c */
-#line 708 "a.y"
+/* Line 1806 of yacc.c */
+#line 739 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (3)].lval) & (yyvsp[(3) - (3)].lval);
}
break;
- case 129:
+ case 132:
-/* Line 1455 of yacc.c */
-#line 712 "a.y"
+/* Line 1806 of yacc.c */
+#line 743 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (3)].lval) ^ (yyvsp[(3) - (3)].lval);
}
break;
- case 130:
+ case 133:
-/* Line 1455 of yacc.c */
-#line 716 "a.y"
+/* Line 1806 of yacc.c */
+#line 747 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (3)].lval) | (yyvsp[(3) - (3)].lval);
}
@@ -2742,10 +2820,21 @@ yyreduce:
-/* Line 1455 of yacc.c */
-#line 2747 "y.tab.c"
+/* Line 1806 of yacc.c */
+#line 2825 "y.tab.c"
default: break;
}
+ /* User semantic actions sometimes alter yychar, and that requires
+ that yytoken be updated with the new translation. We take the
+ approach of translating immediately before every use of yytoken.
+ One alternative is translating here after every semantic action,
+ but that translation would be missed if the semantic action invokes
+ YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or
+ if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an
+ incorrect destructor might then be invoked immediately. In the
+ case of YYERROR or YYBACKUP, subsequent parser actions might lead
+ to an incorrect destructor call or verbose syntax error message
+ before the lookahead is translated. */
YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
YYPOPSTACK (yylen);
@@ -2773,6 +2862,10 @@ yyreduce:
| yyerrlab -- here on detecting error |
`------------------------------------*/
yyerrlab:
+ /* Make sure we have latest lookahead translation. See comments at
+ user semantic actions for why this is necessary. */
+ yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar);
+
/* If not already recovering from an error, report this error. */
if (!yyerrstatus)
{
@@ -2780,37 +2873,36 @@ yyerrlab:
#if ! YYERROR_VERBOSE
yyerror (YY_("syntax error"));
#else
+# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \
+ yyssp, yytoken)
{
- YYSIZE_T yysize = yysyntax_error (0, yystate, yychar);
- if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM)
- {
- YYSIZE_T yyalloc = 2 * yysize;
- if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM))
- yyalloc = YYSTACK_ALLOC_MAXIMUM;
- if (yymsg != yymsgbuf)
- YYSTACK_FREE (yymsg);
- yymsg = (char *) YYSTACK_ALLOC (yyalloc);
- if (yymsg)
- yymsg_alloc = yyalloc;
- else
- {
- yymsg = yymsgbuf;
- yymsg_alloc = sizeof yymsgbuf;
- }
- }
-
- if (0 < yysize && yysize <= yymsg_alloc)
- {
- (void) yysyntax_error (yymsg, yystate, yychar);
- yyerror (yymsg);
- }
- else
- {
- yyerror (YY_("syntax error"));
- if (yysize != 0)
- goto yyexhaustedlab;
- }
+ char const *yymsgp = YY_("syntax error");
+ int yysyntax_error_status;
+ yysyntax_error_status = YYSYNTAX_ERROR;
+ if (yysyntax_error_status == 0)
+ yymsgp = yymsg;
+ else if (yysyntax_error_status == 1)
+ {
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+ yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc);
+ if (!yymsg)
+ {
+ yymsg = yymsgbuf;
+ yymsg_alloc = sizeof yymsgbuf;
+ yysyntax_error_status = 2;
+ }
+ else
+ {
+ yysyntax_error_status = YYSYNTAX_ERROR;
+ yymsgp = yymsg;
+ }
+ }
+ yyerror (yymsgp);
+ if (yysyntax_error_status == 2)
+ goto yyexhaustedlab;
}
+# undef YYSYNTAX_ERROR
#endif
}
@@ -2869,7 +2961,7 @@ yyerrlab1:
for (;;)
{
yyn = yypact[yystate];
- if (yyn != YYPACT_NINF)
+ if (!yypact_value_is_default (yyn))
{
yyn += YYTERROR;
if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
@@ -2928,8 +3020,13 @@ yyexhaustedlab:
yyreturn:
if (yychar != YYEMPTY)
- yydestruct ("Cleanup: discarding lookahead",
- yytoken, &yylval);
+ {
+ /* Make sure we have latest lookahead translation. See comments at
+ user semantic actions for why this is necessary. */
+ yytoken = YYTRANSLATE (yychar);
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval);
+ }
/* Do not reclaim the symbols of the rule which action triggered
this YYABORT or YYACCEPT. */
YYPOPSTACK (yylen);
diff --git a/src/cmd/5a/y.tab.h b/src/cmd/5a/y.tab.h
index 1dd0cb08a..92230a2a5 100644
--- a/src/cmd/5a/y.tab.h
+++ b/src/cmd/5a/y.tab.h
@@ -1,10 +1,8 @@
+/* A Bison parser, made by GNU Bison 2.5. */
-/* A Bison parser, made by GNU Bison 2.4.1. */
-
-/* Skeleton interface for Bison's Yacc-like parsers in C
+/* Bison interface for Yacc-like parsers in C
- Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
- Free Software Foundation, Inc.
+ Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -53,39 +51,40 @@
LTYPEC = 269,
LTYPED = 270,
LTYPEE = 271,
- LTYPEF = 272,
- LTYPEG = 273,
- LTYPEH = 274,
- LTYPEI = 275,
- LTYPEJ = 276,
- LTYPEK = 277,
- LTYPEL = 278,
- LTYPEM = 279,
- LTYPEN = 280,
- LTYPEBX = 281,
- LTYPEPLD = 282,
- LCONST = 283,
- LSP = 284,
- LSB = 285,
- LFP = 286,
- LPC = 287,
- LTYPEX = 288,
- LR = 289,
- LREG = 290,
- LF = 291,
- LFREG = 292,
- LC = 293,
- LCREG = 294,
- LPSR = 295,
- LFCR = 296,
- LCOND = 297,
- LS = 298,
- LAT = 299,
- LFCONST = 300,
- LSCONST = 301,
- LNAME = 302,
- LLAB = 303,
- LVAR = 304
+ LTYPEG = 272,
+ LTYPEH = 273,
+ LTYPEI = 274,
+ LTYPEJ = 275,
+ LTYPEK = 276,
+ LTYPEL = 277,
+ LTYPEM = 278,
+ LTYPEN = 279,
+ LTYPEBX = 280,
+ LTYPEPLD = 281,
+ LCONST = 282,
+ LSP = 283,
+ LSB = 284,
+ LFP = 285,
+ LPC = 286,
+ LTYPEX = 287,
+ LTYPEPC = 288,
+ LTYPEF = 289,
+ LR = 290,
+ LREG = 291,
+ LF = 292,
+ LFREG = 293,
+ LC = 294,
+ LCREG = 295,
+ LPSR = 296,
+ LFCR = 297,
+ LCOND = 298,
+ LS = 299,
+ LAT = 300,
+ LFCONST = 301,
+ LSCONST = 302,
+ LNAME = 303,
+ LLAB = 304,
+ LVAR = 305
};
#endif
/* Tokens. */
@@ -103,39 +102,40 @@
#define LTYPEC 269
#define LTYPED 270
#define LTYPEE 271
-#define LTYPEF 272
-#define LTYPEG 273
-#define LTYPEH 274
-#define LTYPEI 275
-#define LTYPEJ 276
-#define LTYPEK 277
-#define LTYPEL 278
-#define LTYPEM 279
-#define LTYPEN 280
-#define LTYPEBX 281
-#define LTYPEPLD 282
-#define LCONST 283
-#define LSP 284
-#define LSB 285
-#define LFP 286
-#define LPC 287
-#define LTYPEX 288
-#define LR 289
-#define LREG 290
-#define LF 291
-#define LFREG 292
-#define LC 293
-#define LCREG 294
-#define LPSR 295
-#define LFCR 296
-#define LCOND 297
-#define LS 298
-#define LAT 299
-#define LFCONST 300
-#define LSCONST 301
-#define LNAME 302
-#define LLAB 303
-#define LVAR 304
+#define LTYPEG 272
+#define LTYPEH 273
+#define LTYPEI 274
+#define LTYPEJ 275
+#define LTYPEK 276
+#define LTYPEL 277
+#define LTYPEM 278
+#define LTYPEN 279
+#define LTYPEBX 280
+#define LTYPEPLD 281
+#define LCONST 282
+#define LSP 283
+#define LSB 284
+#define LFP 285
+#define LPC 286
+#define LTYPEX 287
+#define LTYPEPC 288
+#define LTYPEF 289
+#define LR 290
+#define LREG 291
+#define LF 292
+#define LFREG 293
+#define LC 294
+#define LCREG 295
+#define LPSR 296
+#define LFCR 297
+#define LCOND 298
+#define LS 299
+#define LAT 300
+#define LFCONST 301
+#define LSCONST 302
+#define LNAME 303
+#define LLAB 304
+#define LVAR 305
@@ -144,8 +144,8 @@
typedef union YYSTYPE
{
-/* Line 1676 of yacc.c */
-#line 38 "a.y"
+/* Line 2068 of yacc.c */
+#line 39 "a.y"
Sym *sym;
int32 lval;
@@ -155,7 +155,7 @@ typedef union YYSTYPE
-/* Line 1676 of yacc.c */
+/* Line 2068 of yacc.c */
#line 160 "y.tab.h"
} YYSTYPE;
# define YYSTYPE_IS_TRIVIAL 1
diff --git a/src/cmd/5c/cgen.c b/src/cmd/5c/cgen.c
index 5ff4f633d..08ed36055 100644
--- a/src/cmd/5c/cgen.c
+++ b/src/cmd/5c/cgen.c
@@ -28,8 +28,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-
#include "gc.h"
+#include "../../pkg/runtime/funcdata.h"
void
_cgen(Node *n, Node *nn, int inrel)
@@ -366,12 +366,14 @@ _cgen(Node *n, Node *nn, int inrel)
if(REGARG >= 0)
o = reg[REGARG];
gargs(r, &nod, &nod1);
+ gpcdata(PCDATA_ArgSize, curarg);
if(l->addable < INDEXED) {
reglcgen(&nod, l, Z);
gopcode(OFUNC, Z, Z, &nod);
regfree(&nod);
} else
gopcode(OFUNC, Z, Z, l);
+ gpcdata(PCDATA_ArgSize, -1);
if(REGARG >= 0)
if(o != reg[REGARG])
reg[REGARG]--;
diff --git a/src/cmd/5c/gc.h b/src/cmd/5c/gc.h
index a0fc63c60..084da7e6a 100644
--- a/src/cmd/5c/gc.h
+++ b/src/cmd/5c/gc.h
@@ -298,6 +298,7 @@ int sconst(Node*);
int sval(int32);
void gpseudo(int, Sym*, Node*);
void gprefetch(Node*);
+void gpcdata(int, int);
/*
* swt.c
diff --git a/src/cmd/5c/peep.c b/src/cmd/5c/peep.c
index 2f902e02a..22328c18c 100644
--- a/src/cmd/5c/peep.c
+++ b/src/cmd/5c/peep.c
@@ -127,8 +127,10 @@ loop1:
}
continue;
case AMOVH:
+ case AMOVHS:
case AMOVHU:
case AMOVB:
+ case AMOVBS:
case AMOVBU:
if(p->to.type != D_REG)
continue;
@@ -152,6 +154,7 @@ loop1:
switch(p->as) {
case AMOVW:
case AMOVB:
+ case AMOVBS:
case AMOVBU:
if(p->from.type == D_OREG && p->from.offset == 0)
xtramodes(r, &p->from);
@@ -462,7 +465,7 @@ copy1(Adr *v1, Adr *v2, Reg *r, int f)
}
t = copyu(p, v2, A);
switch(t) {
- case 2: /* rar, cant split */
+ case 2: /* rar, can't split */
if(debug['P'])
print("; %Drar; return 0\n", v2);
return 0;
@@ -824,7 +827,7 @@ xtramodes(Reg *r, Adr *a)
Adr v;
p = r->prog;
- if(p->as == AMOVB && p->from.type == D_OREG) /* byte load */
+ if((p->as == AMOVB || p->as == AMOVBS) && p->from.type == D_OREG) /* byte load */
return 0;
v = *a;
v.type = D_REG;
@@ -836,7 +839,7 @@ xtramodes(Reg *r, Adr *a)
case AADD:
if(p1->from.type == D_REG ||
(p1->from.type == D_SHIFT && (p1->from.offset&(1<<4)) == 0 &&
- (p->as != AMOVB || (a == &p->from && (p1->from.offset&~0xf) == 0))) ||
+ ((p->as != AMOVB && p->as != AMOVBS) || (a == &p->from && (p1->from.offset&~0xf) == 0))) ||
(p1->from.type == D_CONST &&
p1->from.offset > -4096 && p1->from.offset < 4096))
if(nochange(uniqs(r1), r, p1)) {
@@ -961,8 +964,10 @@ copyu(Prog *p, Adr *v, Adr *s)
case AMOVF:
case AMOVD:
case AMOVH:
+ case AMOVHS:
case AMOVHU:
case AMOVB:
+ case AMOVBS:
case AMOVBU:
case AMOVDW:
case AMOVWD:
diff --git a/src/cmd/5c/reg.c b/src/cmd/5c/reg.c
index 42c5193de..3d67872b4 100644
--- a/src/cmd/5c/reg.c
+++ b/src/cmd/5c/reg.c
@@ -112,6 +112,7 @@ regopt(Prog *p)
case AGLOBL:
case ANAME:
case ASIGNAME:
+ case AFUNCDATA:
continue;
}
r = rega();
@@ -174,8 +175,10 @@ regopt(Prog *p)
*/
case ANOP:
case AMOVB:
+ case AMOVBS:
case AMOVBU:
case AMOVH:
+ case AMOVHS:
case AMOVHU:
case AMOVW:
case AMOVF:
@@ -460,6 +463,7 @@ brk:
case AGLOBL:
case ANAME:
case ASIGNAME:
+ case AFUNCDATA:
break;
}
}
@@ -555,9 +559,9 @@ addmove(Reg *r, int bn, int rn, int f)
p1->as = AMOVW;
if(v->etype == TCHAR || v->etype == TUCHAR)
- p1->as = AMOVB;
+ p1->as = AMOVBS;
if(v->etype == TSHORT || v->etype == TUSHORT)
- p1->as = AMOVH;
+ p1->as = AMOVHS;
if(v->etype == TFLOAT)
p1->as = AMOVF;
if(v->etype == TDOUBLE)
diff --git a/src/cmd/5c/sgen.c b/src/cmd/5c/sgen.c
index 92a0f64f8..efcc0437b 100644
--- a/src/cmd/5c/sgen.c
+++ b/src/cmd/5c/sgen.c
@@ -35,11 +35,9 @@ Prog*
gtext(Sym *s, int32 stkoff)
{
int32 a;
-
- a = 0;
- if(!(textflag & NOSPLIT))
- a = argsize();
- else if(stkoff >= 128)
+
+ a = argsize();
+ if((textflag & NOSPLIT) != 0 && stkoff >= 128)
yyerror("stack frame too large for NOSPLIT function");
gpseudo(ATEXT, s, nodconst(stkoff));
diff --git a/src/cmd/5c/swt.c b/src/cmd/5c/swt.c
index 87b77518b..0f0c457f8 100644
--- a/src/cmd/5c/swt.c
+++ b/src/cmd/5c/swt.c
@@ -525,12 +525,12 @@ outhist(Biobuf *b)
q = 0;
}
if(n) {
- Bputc(b, ANAME);
- Bputc(b, D_FILE);
- Bputc(b, 1);
- Bputc(b, '<');
+ BPUTC(b, ANAME);
+ BPUTC(b, D_FILE);
+ BPUTC(b, 1);
+ BPUTC(b, '<');
Bwrite(b, p, n);
- Bputc(b, 0);
+ BPUTC(b, 0);
}
p = q;
if(p == 0 && op) {
diff --git a/src/cmd/5c/txt.c b/src/cmd/5c/txt.c
index b8675fe60..6d9b69d00 100644
--- a/src/cmd/5c/txt.c
+++ b/src/cmd/5c/txt.c
@@ -139,9 +139,7 @@ gclean(void)
continue;
if(s->type == types[TENUM])
continue;
- textflag = s->dataflag;
gpseudo(AGLOBL, s, nodconst(s->type->width));
- textflag = 0;
}
nextpc();
p->as = AEND;
@@ -596,13 +594,13 @@ gmove(Node *f, Node *t)
a = AMOVD;
break;
case TCHAR:
- a = AMOVB;
+ a = AMOVBS;
break;
case TUCHAR:
a = AMOVBU;
break;
case TSHORT:
- a = AMOVH;
+ a = AMOVHS;
break;
case TUSHORT:
a = AMOVHU;
@@ -632,13 +630,13 @@ gmove(Node *f, Node *t)
a = AMOVBU;
break;
case TCHAR:
- a = AMOVB;
+ a = AMOVBS;
break;
case TUSHORT:
a = AMOVHU;
break;
case TSHORT:
- a = AMOVH;
+ a = AMOVHS;
break;
case TFLOAT:
a = AMOVF;
@@ -763,13 +761,13 @@ gmove(Node *f, Node *t)
switch(tt) {
case TDOUBLE:
regalloc(&nod, f, Z);
- gins(AMOVH, f, &nod);
+ gins(AMOVHS, f, &nod);
gins(AMOVWD, &nod, t);
regfree(&nod);
return;
case TFLOAT:
regalloc(&nod, f, Z);
- gins(AMOVH, f, &nod);
+ gins(AMOVHS, f, &nod);
gins(AMOVWF, &nod, t);
regfree(&nod);
return;
@@ -778,7 +776,7 @@ gmove(Node *f, Node *t)
case TULONG:
case TLONG:
case TIND:
- a = AMOVH;
+ a = AMOVHS;
break;
case TSHORT:
case TUSHORT:
@@ -821,13 +819,13 @@ gmove(Node *f, Node *t)
switch(tt) {
case TDOUBLE:
regalloc(&nod, f, Z);
- gins(AMOVB, f, &nod);
+ gins(AMOVBS, f, &nod);
gins(AMOVWD, &nod, t);
regfree(&nod);
return;
case TFLOAT:
regalloc(&nod, f, Z);
- gins(AMOVB, f, &nod);
+ gins(AMOVBS, f, &nod);
gins(AMOVWF, &nod, t);
regfree(&nod);
return;
@@ -838,7 +836,7 @@ gmove(Node *f, Node *t)
case TIND:
case TSHORT:
case TUSHORT:
- a = AMOVB;
+ a = AMOVBS;
break;
case TCHAR:
case TUCHAR:
@@ -895,13 +893,13 @@ gmover(Node *f, Node *t)
if(typechlp[ft] && typechlp[tt] && ewidth[ft] >= ewidth[tt]){
switch(tt){
case TSHORT:
- a = AMOVH;
+ a = AMOVHS;
break;
case TUSHORT:
a = AMOVHU;
break;
case TCHAR:
- a = AMOVB;
+ a = AMOVBS;
break;
case TUCHAR:
a = AMOVBU;
@@ -1181,10 +1179,17 @@ gpseudo(int a, Sym *s, Node *n)
p->from.type = D_OREG;
p->from.sym = s;
p->from.name = D_EXTERN;
- if(a == ATEXT || a == AGLOBL) {
+
+ switch(a) {
+ case ATEXT:
p->reg = textflag;
textflag = 0;
+ break;
+ case AGLOBL:
+ p->reg = s->dataflag;
+ break;
}
+
if(s->class == CSTATIC)
p->from.name = D_STATIC;
naddr(n, &p->to);
@@ -1193,6 +1198,15 @@ gpseudo(int a, Sym *s, Node *n)
}
void
+gpcdata(int index, int value)
+{
+ Node n1;
+
+ n1 = *nodconst(index);
+ gins(APCDATA, &n1, nodconst(value));
+}
+
+void
gprefetch(Node *n)
{
Node n1;
diff --git a/src/cmd/5g/cgen.c b/src/cmd/5g/cgen.c
index 1620f410a..2d260e72d 100644
--- a/src/cmd/5g/cgen.c
+++ b/src/cmd/5g/cgen.c
@@ -34,6 +34,8 @@ cgen(Node *n, Node *res)
case OSLICE:
case OSLICEARR:
case OSLICESTR:
+ case OSLICE3:
+ case OSLICE3ARR:
if (res->op != ONAME || !res->addable) {
tempname(&n1, n->type);
cgen_slice(n, &n1);
@@ -77,6 +79,7 @@ cgen(Node *n, Node *res)
// can't do in walk because n->left->addable
// changes if n->left is an escaping local variable.
switch(n->op) {
+ case OSPTR:
case OLEN:
if(isslice(n->left->type) || istype(n->left->type, TSTRING))
n->addable = n->left->addable;
@@ -315,6 +318,22 @@ cgen(Node *n, Node *res)
regfree(&n1);
break;
+ case OSPTR:
+ // pointer is the first word of string or slice.
+ if(isconst(nl, CTSTR)) {
+ regalloc(&n1, types[tptr], res);
+ p1 = gins(AMOVW, N, &n1);
+ datastring(nl->val.u.sval->s, nl->val.u.sval->len, &p1->from);
+ gmove(&n1, res);
+ regfree(&n1);
+ break;
+ }
+ igen(nl, &n1, res);
+ n1.type = n->type;
+ gmove(&n1, res);
+ regfree(&n1);
+ break;
+
case OLEN:
if(istype(nl->type, TMAP) || istype(nl->type, TCHAN)) {
// map has len in the first 32-bit word.
@@ -464,6 +483,16 @@ abop: // asymmetric binary
cgen(nl, &n1);
}
gins(a, &n2, &n1);
+ // Normalize result for types smaller than word.
+ if(n->type->width < widthptr) {
+ switch(n->op) {
+ case OADD:
+ case OSUB:
+ case OMUL:
+ gins(optoas(OAS, n->type), &n1, &n1);
+ break;
+ }
+ }
gmove(&n1, res);
regfree(&n1);
if(n2.op != OLITERAL)
@@ -550,6 +579,7 @@ cgenindex(Node *n, Node *res, int bounded)
/*
* generate:
* res = &n;
+ * The generated code checks that the result is not nil.
*/
void
agen(Node *n, Node *res)
@@ -629,6 +659,8 @@ agen(Node *n, Node *res)
case OSLICE:
case OSLICEARR:
case OSLICESTR:
+ case OSLICE3:
+ case OSLICE3ARR:
tempname(&n1, n->type);
cgen_slice(n, &n1);
agen(&n1, res);
@@ -675,25 +707,11 @@ agen(Node *n, Node *res)
case OIND:
cgen(nl, res);
+ cgen_checknil(res);
break;
case ODOT:
agen(nl, res);
- // explicit check for nil if struct is large enough
- // that we might derive too big a pointer. If the left node
- // was ODOT we have already done the nil check.
- if(nl->op != ODOT)
- if(nl->type->width >= unmappedzero) {
- regalloc(&n1, types[tptr], N);
- gmove(res, &n1);
- regalloc(&n2, types[TUINT8], &n1);
- n1.op = OINDREG;
- n1.type = types[TUINT8];
- n1.xoffset = 0;
- gmove(&n1, &n2);
- regfree(&n1);
- regfree(&n2);
- }
if(n->xoffset != 0) {
nodconst(&n1, types[TINT32], n->xoffset);
regalloc(&n2, n1.type, N);
@@ -709,19 +727,7 @@ agen(Node *n, Node *res)
case ODOTPTR:
cgen(nl, res);
- // explicit check for nil if struct is large enough
- // that we might derive too big a pointer.
- if(nl->type->type->width >= unmappedzero) {
- regalloc(&n1, types[tptr], N);
- gmove(res, &n1);
- regalloc(&n2, types[TUINT8], &n1);
- n1.op = OINDREG;
- n1.type = types[TUINT8];
- n1.xoffset = 0;
- gmove(&n1, &n2);
- regfree(&n1);
- regfree(&n2);
- }
+ cgen_checknil(res);
if(n->xoffset != 0) {
nodconst(&n1, types[TINT32], n->xoffset);
regalloc(&n2, n1.type, N);
@@ -747,11 +753,12 @@ ret:
*
* on exit, a has been changed to be *newreg.
* caller must regfree(a).
+ * The generated code checks that the result is not *nil.
*/
void
igen(Node *n, Node *a, Node *res)
{
- Node n1, n2;
+ Node n1;
int r;
if(debug['g']) {
@@ -792,19 +799,7 @@ igen(Node *n, Node *a, Node *res)
regalloc(a, types[tptr], res);
cgen(n->left, a);
}
- // explicit check for nil if struct is large enough
- // that we might derive too big a pointer.
- if(n->left->type->type->width >= unmappedzero) {
- regalloc(&n1, types[tptr], N);
- gmove(a, &n1);
- regalloc(&n2, types[TUINT8], &n1);
- n1.op = OINDREG;
- n1.type = types[TUINT8];
- n1.xoffset = 0;
- gmove(&n1, &n2);
- regfree(&n1);
- regfree(&n2);
- }
+ cgen_checknil(a);
a->op = OINDREG;
a->xoffset = n->xoffset;
a->type = n->type;
@@ -894,6 +889,7 @@ cgenr(Node *n, Node *a, Node *res)
* newreg = &n;
*
* caller must regfree(a).
+ * The generated code checks that the result is not nil.
*/
void
agenr(Node *n, Node *a, Node *res)
@@ -925,6 +921,7 @@ agenr(Node *n, Node *a, Node *res)
case OIND:
cgenr(n->left, a, res);
+ cgen_checknil(a);
break;
case OINDEX:
@@ -966,20 +963,6 @@ agenr(Node *n, Node *a, Node *res)
// i is in &n1 (if not constant)
// w is width
- // explicit check for nil if array is large enough
- // that we might derive too big a pointer.
- if(isfixedarray(nl->type) && nl->type->width >= unmappedzero) {
- regalloc(&n4, types[tptr], N);
- gmove(&n3, &n4);
- regalloc(&tmp, types[TUINT8], &n4);
- n4.op = OINDREG;
- n4.type = types[TUINT8];
- n4.xoffset = 0;
- gmove(&n4, &tmp);
- regfree(&n4);
- regfree(&tmp);
- }
-
// constant index
if(isconst(nr, CTINT)) {
if(isconst(nl, CTSTR))
diff --git a/src/cmd/5g/gg.h b/src/cmd/5g/gg.h
index 45a9a887e..860817f69 100644
--- a/src/cmd/5g/gg.h
+++ b/src/cmd/5g/gg.h
@@ -39,7 +39,7 @@ struct Prog
uint32 loc; // pc offset in this func
uint32 lineno; // source line that generated this
Prog* link; // next instruction in this func
- void* regp; // points to enclosing Reg struct
+ void* opt; // for optimizer passes
short as; // opcode
uchar reg; // doubles as width in DATA op
uchar scond;
@@ -51,7 +51,7 @@ struct Prog
#define REGALLOC_R0 0
#define REGALLOC_RMAX REGEXT
-#define REGALLOC_F0 (REGALLOC_RMAX+1)
+#define REGALLOC_F0 NREG
#define REGALLOC_FMAX (REGALLOC_F0 + FREGEXT)
EXTERN int32 dynloc;
@@ -73,7 +73,6 @@ EXTERN int maxstksize;
* gen.c
*/
void compile(Node*);
-void proglist(void);
void gen(Node*);
Node* lookdot(Node*, Node*, int);
void cgen_as(Node*, Node*);
@@ -120,7 +119,6 @@ void cgen64(Node*, Node*);
* gsubr.c
*/
void clearp(Prog*);
-void proglist(void);
Prog* gbranch(int, Type*, int);
Prog* prog(int);
void gconv(int, int);
@@ -148,6 +146,7 @@ void split64(Node*, Node*, Node*);
void splitclean(void);
Node* ncon(uint32 i);
void gtrack(Sym*);
+void gargsize(int32);
/*
* obj.c
diff --git a/src/cmd/5g/ggen.c b/src/cmd/5g/ggen.c
index de1671bb6..040c3d2a9 100644
--- a/src/cmd/5g/ggen.c
+++ b/src/cmd/5g/ggen.c
@@ -9,9 +9,15 @@
#include "gg.h"
#include "opt.h"
+static Prog* appendp(Prog*, int, int, int, int32, int, int, int32);
+
void
-defframe(Prog *ptxt)
+defframe(Prog *ptxt, Bvec *bv)
{
+ int i, j, first;
+ uint32 frame;
+ Prog *p, *p1;
+
// fill in argument size
ptxt->to.type = D_CONST2;
ptxt->to.offset2 = rnd(curfn->type->argwid, widthptr);
@@ -19,8 +25,62 @@ defframe(Prog *ptxt)
// fill in final stack size
if(stksize > maxstksize)
maxstksize = stksize;
- ptxt->to.offset = rnd(maxstksize+maxarg, widthptr);
+ frame = rnd(maxstksize+maxarg, widthptr);
+ ptxt->to.offset = frame;
maxstksize = 0;
+
+ // insert code to clear pointered part of the frame,
+ // so that garbage collector only sees initialized values
+ // when it looks for pointers.
+ p = ptxt;
+ while(p->link->as == AFUNCDATA || p->link->as == APCDATA || p->link->as == ATYPE)
+ p = p->link;
+ if(stkzerosize >= 8*widthptr) {
+ p = appendp(p, AMOVW, D_CONST, NREG, 0, D_REG, 0, 0);
+ p = appendp(p, AADD, D_CONST, NREG, 4+frame-stkzerosize, D_REG, 1, 0);
+ p->reg = REGSP;
+ p = appendp(p, AADD, D_CONST, NREG, stkzerosize, D_REG, 2, 0);
+ p->reg = 1;
+ p1 = p = appendp(p, AMOVW, D_REG, 0, 0, D_OREG, 1, 4);
+ p->scond |= C_PBIT;
+ p = appendp(p, ACMP, D_REG, 1, 0, D_NONE, 0, 0);
+ p->reg = 2;
+ p = appendp(p, ABNE, D_NONE, NREG, 0, D_BRANCH, NREG, 0);
+ patch(p, p1);
+ } else {
+ first = 1;
+ j = (stkptrsize - stkzerosize)/widthptr * 2;
+ for(i=0; i<stkzerosize; i+=widthptr) {
+ if(bvget(bv, j) || bvget(bv, j+1)) {
+ if(first) {
+ p = appendp(p, AMOVW, D_CONST, NREG, 0, D_REG, 0, 0);
+ first = 0;
+ }
+ p = appendp(p, AMOVW, D_REG, 0, 0, D_OREG, REGSP, 4+frame-stkzerosize+i);
+ }
+ j += 2;
+ }
+ }
+}
+
+static Prog*
+appendp(Prog *p, int as, int ftype, int freg, int32 foffset, int ttype, int treg, int32 toffset)
+{
+ Prog *q;
+
+ q = mal(sizeof(*q));
+ clearp(q);
+ q->as = as;
+ q->lineno = p->lineno;
+ q->from.type = ftype;
+ q->from.reg = freg;
+ q->from.offset = foffset;
+ q->to.type = ttype;
+ q->to.reg = treg;
+ q->to.offset = toffset;
+ q->link = p->link;
+ p->link = q;
+ return q;
}
// Sweep the prog list to mark any used nodes.
@@ -39,7 +99,7 @@ markautoused(Prog* p)
}
}
-// Fixup instructions after compactframe has moved all autos around.
+// Fixup instructions after allocauto (formerly compactframe) has moved all autos around.
void
fixautoused(Prog* p)
{
@@ -73,9 +133,28 @@ fixautoused(Prog* p)
void
ginscall(Node *f, int proc)
{
+ int32 arg;
Prog *p;
Node n1, r, r1, con;
+ if(f->type != T)
+ setmaxarg(f->type);
+
+ arg = -1;
+ // Most functions have a fixed-size argument block, so traceback uses that during unwind.
+ // Not all, though: there are some variadic functions in package runtime,
+ // and for those we emit call-specific metadata recorded by caller.
+ // Reflect generates functions with variable argsize (see reflect.methodValueCall/makeFuncStub),
+ // so we do this for all indirect calls as well.
+ if(f->type != T && (f->sym == S || (f->sym != S && f->sym->pkg == runtimepkg) || proc == 1 || proc == 2)) {
+ arg = f->type->argwid;
+ if(proc == 1 || proc == 2)
+ arg += 3*widthptr;
+ }
+
+ if(arg != -1)
+ gargsize(arg);
+
switch(proc) {
default:
fatal("ginscall: bad proc %d", proc);
@@ -84,6 +163,20 @@ ginscall(Node *f, int proc)
case 0: // normal call
case -1: // normal call but no return
if(f->op == ONAME && f->class == PFUNC) {
+ if(f == deferreturn) {
+ // Deferred calls will appear to be returning to
+ // the BL deferreturn(SB) that we are about to emit.
+ // However, the stack trace code will show the line
+ // of the instruction before that return PC.
+ // To avoid that instruction being an unrelated instruction,
+ // insert a NOP so that we will have the right line number.
+ // ARM NOP 0x00000000 is really AND.EQ R0, R0, R0.
+ // Use the latter form because the NOP pseudo-instruction
+ // would be removed by the linker.
+ nodreg(&r, types[TINT], 0);
+ p = gins(AAND, &r, &r);
+ p->scond = C_SCOND_EQ;
+ }
p = gins(ABL, N, f);
afunclit(&p->to, f);
if(proc == -1 || noreturn(p))
@@ -156,6 +249,9 @@ ginscall(Node *f, int proc)
}
break;
}
+
+ if(arg != -1)
+ gargsize(-1);
}
/*
@@ -211,6 +307,7 @@ cgen_callinter(Node *n, Node *res, int proc)
nodo.xoffset -= widthptr;
cgen(&nodo, &nodr); // REG = 0(REG) -- i.tab
+ cgen_checknil(&nodr); // in case offset is huge
nodo.xoffset = n->left->xoffset + 3*widthptr + 8;
@@ -225,14 +322,11 @@ cgen_callinter(Node *n, Node *res, int proc)
p->from.type = D_CONST; // REG = &(20+offset(REG)) -- i.tab->fun[f]
}
- // BOTCH nodr.type = fntype;
nodr.type = n->left->type;
ginscall(&nodr, proc);
regfree(&nodr);
regfree(&nodo);
-
- setmaxarg(n->left->type);
}
/*
@@ -260,8 +354,6 @@ cgen_call(Node *n, int proc)
genlist(n->list); // assign the args
t = n->left->type;
- setmaxarg(t);
-
// call tempname pointer
if(n->left->ullman >= UINF) {
regalloc(&nod, types[tptr], N);
@@ -365,11 +457,19 @@ cgen_aret(Node *n, Node *res)
void
cgen_ret(Node *n)
{
+ Prog *p;
+
genlist(n->list); // copy out args
- if(hasdefer || curfn->exit)
+ if(hasdefer || curfn->exit) {
gjmp(retpc);
- else
- gins(ARET, N, N);
+ return;
+ }
+ p = gins(ARET, N, N);
+ if(n->op == ORETJMP) {
+ p->to.name = D_EXTERN;
+ p->to.type = D_CONST;
+ p->to.sym = n->left->sym;
+ }
}
/*
@@ -601,6 +701,8 @@ cgen_shift(int op, int bounded, Node *nl, Node *nr, Node *res)
gshift(AMOVW, &n2, SHIFT_LL, v, &n1);
gshift(AORR, &n2, SHIFT_LR, w-v, &n1);
regfree(&n2);
+ // Ensure sign/zero-extended result.
+ gins(optoas(OAS, nl->type), &n1, &n1);
}
gmove(&n1, res);
regfree(&n1);
@@ -626,6 +728,8 @@ cgen_shift(int op, int bounded, Node *nl, Node *nr, Node *res)
else // OLSH
gshift(AMOVW, &n1, SHIFT_LL, sc, &n1);
}
+ if(w < 32 && op == OLSH)
+ gins(optoas(OAS, nl->type), &n1, &n1);
gmove(&n1, res);
regfree(&n1);
return;
@@ -699,6 +803,9 @@ cgen_shift(int op, int bounded, Node *nl, Node *nr, Node *res)
regfree(&n3);
patch(p3, pc);
+ // Left-shift of smaller word must be sign/zero-extended.
+ if(w < 32 && op == OLSH)
+ gins(optoas(OAS, nl->type), &n2, &n2);
gmove(&n2, res);
regfree(&n1);
@@ -759,7 +866,7 @@ clearfat(Node *nl)
}
while(c > 0) {
- p = gins(AMOVBU, &nz, &dst);
+ p = gins(AMOVB, &nz, &dst);
p->to.type = D_OREG;
p->to.offset = 1;
p->scond |= C_PBIT;
@@ -769,3 +876,43 @@ clearfat(Node *nl)
regfree(&dst);
regfree(&nz);
}
+
+// Called after regopt and peep have run.
+// Expand CHECKNIL pseudo-op into actual nil pointer check.
+void
+expandchecks(Prog *firstp)
+{
+ int reg;
+ Prog *p, *p1;
+
+ for(p = firstp; p != P; p = p->link) {
+ if(p->as != ACHECKNIL)
+ continue;
+ if(debug_checknil && p->lineno > 1) // p->lineno==1 in generated wrappers
+ warnl(p->lineno, "generated nil check");
+ if(p->from.type != D_REG)
+ fatal("invalid nil check %P", p);
+ reg = p->from.reg;
+ // check is
+ // CMP arg, $0
+ // MOV.EQ arg, 0(arg)
+ p1 = mal(sizeof *p1);
+ clearp(p1);
+ p1->link = p->link;
+ p->link = p1;
+ p1->lineno = p->lineno;
+ p1->loc = 9999;
+ p1->as = AMOVW;
+ p1->from.type = D_REG;
+ p1->from.reg = reg;
+ p1->to.type = D_OREG;
+ p1->to.reg = reg;
+ p1->to.offset = 0;
+ p1->scond = C_SCOND_EQ;
+ p->as = ACMP;
+ p->from.type = D_CONST;
+ p->from.reg = NREG;
+ p->from.offset = 0;
+ p->reg = reg;
+ }
+}
diff --git a/src/cmd/5g/gobj.c b/src/cmd/5g/gobj.c
index 9c5fb2a96..212ffc271 100644
--- a/src/cmd/5g/gobj.c
+++ b/src/cmd/5g/gobj.c
@@ -35,9 +35,9 @@
void
zname(Biobuf *b, Sym *s, int t)
{
- Bputc(b, ANAME); /* as */
- Bputc(b, t); /* type */
- Bputc(b, s->sym); /* sym */
+ BPUTC(b, ANAME); /* as */
+ BPUTC(b, t); /* type */
+ BPUTC(b, s->sym); /* sym */
Bputname(b, s);
}
@@ -45,12 +45,12 @@ zname(Biobuf *b, Sym *s, int t)
void
zfile(Biobuf *b, char *p, int n)
{
- Bputc(b, ANAME);
- Bputc(b, D_FILE);
- Bputc(b, 1);
- Bputc(b, '<');
+ BPUTC(b, ANAME);
+ BPUTC(b, D_FILE);
+ BPUTC(b, 1);
+ BPUTC(b, '<');
Bwrite(b, p, n);
- Bputc(b, 0);
+ BPUTC(b, 0);
}
void
@@ -58,13 +58,10 @@ zhist(Biobuf *b, int line, vlong offset)
{
Addr a;
- Bputc(b, AHISTORY);
- Bputc(b, C_SCOND_NONE);
- Bputc(b, NREG);
- Bputc(b, line);
- Bputc(b, line>>8);
- Bputc(b, line>>16);
- Bputc(b, line>>24);
+ BPUTC(b, AHISTORY);
+ BPUTC(b, C_SCOND_NONE);
+ BPUTC(b, NREG);
+ BPUTLE4(b, line);
zaddr(b, &zprog.from, 0, 0);
a = zprog.to;
if(offset != 0) {
@@ -91,11 +88,11 @@ zaddr(Biobuf *b, Addr *a, int s, int gotype)
fatal("We should no longer generate these as types");
default:
- Bputc(b, a->type);
- Bputc(b, a->reg);
- Bputc(b, s);
- Bputc(b, a->name);
- Bputc(b, gotype);
+ BPUTC(b, a->type);
+ BPUTC(b, a->reg);
+ BPUTC(b, s);
+ BPUTC(b, a->name);
+ BPUTC(b, gotype);
}
switch(a->type) {
@@ -110,10 +107,7 @@ zaddr(Biobuf *b, Addr *a, int s, int gotype)
case D_CONST2:
l = a->offset2;
- Bputc(b, l);
- Bputc(b, l>>8);
- Bputc(b, l>>16);
- Bputc(b, l>>24); // fall through
+ BPUTLE4(b, l); // fall through
case D_OREG:
case D_CONST:
case D_SHIFT:
@@ -122,10 +116,7 @@ zaddr(Biobuf *b, Addr *a, int s, int gotype)
case D_EXTERN:
case D_PARAM:
l = a->offset;
- Bputc(b, l);
- Bputc(b, l>>8);
- Bputc(b, l>>16);
- Bputc(b, l>>24);
+ BPUTLE4(b, l);
break;
case D_BRANCH:
@@ -133,37 +124,26 @@ zaddr(Biobuf *b, Addr *a, int s, int gotype)
fatal("unpatched branch");
a->offset = a->u.branch->loc;
l = a->offset;
- Bputc(b, l);
- Bputc(b, l>>8);
- Bputc(b, l>>16);
- Bputc(b, l>>24);
+ BPUTLE4(b, l);
break;
case D_SCONST:
n = a->u.sval;
for(i=0; i<NSNAME; i++) {
- Bputc(b, *n);
+ BPUTC(b, *n);
n++;
}
break;
case D_REGREG:
case D_REGREG2:
- Bputc(b, a->offset);
+ BPUTC(b, a->offset);
break;
case D_FCONST:
ieeedtod(&e, a->u.dval);
- l = e;
- Bputc(b, l);
- Bputc(b, l>>8);
- Bputc(b, l>>16);
- Bputc(b, l>>24);
- l = e >> 32;
- Bputc(b, l);
- Bputc(b, l>>8);
- Bputc(b, l>>16);
- Bputc(b, l>>24);
+ BPUTLE4(b, e);
+ BPUTLE4(b, e >> 32);
break;
}
}
@@ -271,13 +251,10 @@ dumpfuncs(void)
break;
}
- Bputc(bout, p->as);
- Bputc(bout, p->scond);
- Bputc(bout, p->reg);
- Bputc(bout, p->lineno);
- Bputc(bout, p->lineno>>8);
- Bputc(bout, p->lineno>>16);
- Bputc(bout, p->lineno>>24);
+ BPUTC(bout, p->as);
+ BPUTC(bout, p->scond);
+ BPUTC(bout, p->reg);
+ BPUTLE4(bout, p->lineno);
zaddr(bout, &p->from, sf, gf);
zaddr(bout, &p->to, st, gt);
}
@@ -518,87 +495,6 @@ dsymptr(Sym *s, int off, Sym *x, int xoff)
return off;
}
-
-void
-genembedtramp(Type *rcvr, Type *method, Sym *newnam, int iface)
-{
- // TODO(kaib): re-implement genembedtramp
- genwrapper(rcvr, method, newnam, iface);
-/*
- Sym *e;
- int c, d, o;
- Prog *p;
- Type *f;
-
- e = method->sym;
- for(d=0; d<nelem(dotlist); d++) {
- c = adddot1(e, rcvr, d, nil, 0);
- if(c == 1)
- goto out;
- }
- fatal("genembedtramp %T.%S", rcvr, method->sym);
-
-out:
- newplist()->name = newname(newnam);
-
- //TEXT main·S_test2(SB),7,$0
- p = pc;
- gins(ATEXT, N, N);
- p->from.type = D_OREG;
- p->from.name = D_EXTERN;
- p->from.sym = newnam;
- p->to.type = D_CONST2;
- p->reg = 7;
- p->to.offset2 = 0;
- p->to.reg = NREG;
-//print("1. %P\n", p);
-
- o = 0;
- for(c=d-1; c>=0; c--) {
- f = dotlist[c].field;
- o += f->width;
- if(!isptr[f->type->etype])
- continue;
-
- //MOVW o(R0), R0
- p = pc;
- gins(AMOVW, N, N);
- p->from.type = D_OREG;
- p->from.reg = REGARG;
- p->from.offset = o;
- p->to.type = D_REG;
- p->to.reg = REGARG;
-//print("2. %P\n", p);
- o = 0;
- }
- if(o != 0) {
- //MOVW $XX(R0), R0
- p = pc;
- gins(AMOVW, N, N);
- p->from.type = D_CONST;
- p->from.reg = REGARG;
- p->from.offset = o;
- p->to.type = D_REG;
- p->to.reg = REGARG;
-//print("3. %P\n", p);
- }
-
- f = dotlist[0].field;
- //B main·*Sub_test2(SB)
- if(isptr[f->type->etype])
- f = f->type;
- p = pc;
- gins(AB, N, N);
- p->to.type = D_OREG;
- p->to.reg = NREG;
- p->to.name = D_EXTERN;
- p->to.sym = methodsym(method->sym, ptrto(f->type), 0);
-//print("4. %P\n", p);
-
- pc->as = ARET; // overwrite AEND
-*/
-}
-
void
nopout(Prog *p)
{
diff --git a/src/cmd/5g/gsubr.c b/src/cmd/5g/gsubr.c
index 815d6fab2..27749b7a7 100644
--- a/src/cmd/5g/gsubr.c
+++ b/src/cmd/5g/gsubr.c
@@ -31,9 +31,11 @@
#include <u.h>
#include <libc.h>
#include "gg.h"
+#include "../../pkg/runtime/funcdata.h"
-// TODO(kaib): Can make this bigger if we move
+// TODO(rsc): Can make this bigger if we move
// the text segment up higher in 5l for all GOOS.
+// At the same time, can raise StackBig in ../../pkg/runtime/stack.h.
long unmappedzero = 4096;
void
@@ -209,6 +211,16 @@ ggloblnod(Node *nam)
}
void
+gargsize(int32 size)
+{
+ Node n1, n2;
+
+ nodconst(&n1, types[TINT32], PCDATA_ArgSize);
+ nodconst(&n2, types[TINT32], size);
+ gins(APCDATA, &n1, &n2);
+}
+
+void
ggloblsym(Sym *s, int32 width, int dupok, int rodata)
{
Prog *p;
@@ -694,16 +706,24 @@ gmove(Node *f, Node *t)
* integer copy and truncate
*/
case CASE(TINT8, TINT8): // same size
+ if(!ismem(f)) {
+ a = AMOVB;
+ break;
+ }
case CASE(TUINT8, TINT8):
case CASE(TINT16, TINT8): // truncate
case CASE(TUINT16, TINT8):
case CASE(TINT32, TINT8):
case CASE(TUINT32, TINT8):
- a = AMOVB;
+ a = AMOVBS;
break;
- case CASE(TINT8, TUINT8):
case CASE(TUINT8, TUINT8):
+ if(!ismem(f)) {
+ a = AMOVB;
+ break;
+ }
+ case CASE(TINT8, TUINT8):
case CASE(TINT16, TUINT8):
case CASE(TUINT16, TUINT8):
case CASE(TINT32, TUINT8):
@@ -713,7 +733,7 @@ gmove(Node *f, Node *t)
case CASE(TINT64, TINT8): // truncate low word
case CASE(TUINT64, TINT8):
- a = AMOVB;
+ a = AMOVBS;
goto trunc64;
case CASE(TINT64, TUINT8):
@@ -722,14 +742,22 @@ gmove(Node *f, Node *t)
goto trunc64;
case CASE(TINT16, TINT16): // same size
+ if(!ismem(f)) {
+ a = AMOVH;
+ break;
+ }
case CASE(TUINT16, TINT16):
case CASE(TINT32, TINT16): // truncate
case CASE(TUINT32, TINT16):
- a = AMOVH;
+ a = AMOVHS;
break;
- case CASE(TINT16, TUINT16):
case CASE(TUINT16, TUINT16):
+ if(!ismem(f)) {
+ a = AMOVH;
+ break;
+ }
+ case CASE(TINT16, TUINT16):
case CASE(TINT32, TUINT16):
case CASE(TUINT32, TUINT16):
a = AMOVHU;
@@ -737,7 +765,7 @@ gmove(Node *f, Node *t)
case CASE(TINT64, TINT16): // truncate low word
case CASE(TUINT64, TINT16):
- a = AMOVH;
+ a = AMOVHS;
goto trunc64;
case CASE(TINT64, TUINT16):
@@ -789,7 +817,7 @@ gmove(Node *f, Node *t)
case CASE(TINT8, TUINT16):
case CASE(TINT8, TINT32):
case CASE(TINT8, TUINT32):
- a = AMOVB;
+ a = AMOVBS;
goto rdst;
case CASE(TINT8, TINT64): // convert via int32
case CASE(TINT8, TUINT64):
@@ -809,7 +837,7 @@ gmove(Node *f, Node *t)
case CASE(TINT16, TINT32): // sign extend int16
case CASE(TINT16, TUINT32):
- a = AMOVH;
+ a = AMOVHS;
goto rdst;
case CASE(TINT16, TINT64): // convert via int32
case CASE(TINT16, TUINT64):
@@ -881,13 +909,13 @@ gmove(Node *f, Node *t)
ta = AMOVW;
switch(tt) {
case TINT8:
- ta = AMOVB;
+ ta = AMOVBS;
break;
case TUINT8:
ta = AMOVBU;
break;
case TINT16:
- ta = AMOVH;
+ ta = AMOVHS;
break;
case TUINT16:
ta = AMOVHU;
@@ -928,13 +956,13 @@ gmove(Node *f, Node *t)
fa = AMOVW;
switch(ft) {
case TINT8:
- fa = AMOVB;
+ fa = AMOVBS;
break;
case TUINT8:
fa = AMOVBU;
break;
case TINT16:
- fa = AMOVH;
+ fa = AMOVHS;
break;
case TUINT16:
fa = AMOVHU;
@@ -1161,48 +1189,6 @@ gregshift(int as, Node *lhs, int32 stype, Node *reg, Node *rhs)
return p;
}
-// Generate an instruction referencing *n
-// to force segv on nil pointer dereference.
-void
-checkref(Node *n, int force)
-{
- Node m1, m2;
-
- if(!force && isptr[n->type->etype] && n->type->type->width < unmappedzero)
- return;
-
- regalloc(&m1, types[TUINTPTR], n);
- regalloc(&m2, types[TUINT8], n);
- cgen(n, &m1);
- m1.xoffset = 0;
- m1.op = OINDREG;
- m1.type = types[TUINT8];
- gins(AMOVBU, &m1, &m2);
- regfree(&m2);
- regfree(&m1);
-}
-
-static void
-checkoffset(Addr *a, int canemitcode)
-{
- Prog *p;
- Node n1;
-
- if(a->offset < unmappedzero)
- return;
- if(!canemitcode)
- fatal("checkoffset %#x, cannot emit code", a->offset);
-
- // cannot rely on unmapped nil page at 0 to catch
- // reference with large offset. instead, emit explicit
- // test of 0(reg).
- regalloc(&n1, types[TUINTPTR], N);
- p = gins(AMOVB, N, &n1);
- p->from = *a;
- p->from.offset = 0;
- regfree(&n1);
-}
-
/*
* generate code to compute n;
* make a refer to result.
@@ -1266,7 +1252,6 @@ naddr(Node *n, Addr *a, int canemitcode)
a->reg = n->val.u.reg;
a->sym = n->sym;
a->offset = n->xoffset;
- checkoffset(a, canemitcode);
break;
case OPARAM:
@@ -1374,8 +1359,16 @@ naddr(Node *n, Addr *a, int canemitcode)
a->etype = TINT32;
if(a->type == D_CONST && a->offset == 0)
break; // len(nil)
- if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero)
- checkoffset(a, canemitcode);
+ break;
+
+ case OSPTR:
+ // pointer in a string or slice
+ naddr(n->left, a, canemitcode);
+ if(a->type == D_CONST && a->offset == 0)
+ break; // ptr(nil)
+ a->etype = simtype[TUINTPTR];
+ a->offset += Array_array;
+ a->width = widthptr;
break;
case OLEN:
@@ -1385,8 +1378,6 @@ naddr(Node *n, Addr *a, int canemitcode)
if(a->type == D_CONST && a->offset == 0)
break; // len(nil)
a->offset += Array_nel;
- if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero)
- checkoffset(a, canemitcode);
break;
case OCAP:
@@ -1396,8 +1387,6 @@ naddr(Node *n, Addr *a, int canemitcode)
if(a->type == D_CONST && a->offset == 0)
break; // cap(nil)
a->offset += Array_cap;
- if(a->offset >= unmappedzero && a->offset-Array_cap < unmappedzero)
- checkoffset(a, canemitcode);
break;
case OADDR:
@@ -1563,16 +1552,19 @@ optoas(int op, Type *t)
break;
case CASE(OAS, TBOOL):
- case CASE(OAS, TINT8):
a = AMOVB;
break;
+ case CASE(OAS, TINT8):
+ a = AMOVBS;
+ break;
+
case CASE(OAS, TUINT8):
a = AMOVBU;
break;
case CASE(OAS, TINT16):
- a = AMOVH;
+ a = AMOVHS;
break;
case CASE(OAS, TUINT16):
@@ -1865,7 +1857,8 @@ lit:
default:
return 0;
case AADD: case ASUB: case AAND: case AORR: case AEOR:
- case AMOVB: case AMOVBU: case AMOVH: case AMOVHU:
+ case AMOVB: case AMOVBS: case AMOVBU:
+ case AMOVH: case AMOVHS: case AMOVHU:
case AMOVW:
break;
}
@@ -1900,13 +1893,15 @@ odot:
n1.xoffset = oary[0];
} else {
cgen(nn, reg);
+ cgen_checknil(reg);
n1.xoffset = -(oary[0]+1);
}
for(i=1; i<o; i++) {
if(oary[i] >= 0)
- fatal("cant happen");
+ fatal("can't happen");
gins(AMOVW, &n1, reg);
+ cgen_checknil(reg);
n1.xoffset = -(oary[i]+1);
}
@@ -1954,9 +1949,10 @@ oindex:
// load the array (reg)
if(l->ullman > r->ullman) {
regalloc(reg, types[tptr], N);
- if(o & OPtrto)
+ if(o & OPtrto) {
cgen(l, reg);
- else
+ cgen_checknil(reg);
+ } else
agen(l, reg);
}
@@ -1973,9 +1969,10 @@ oindex:
// load the array (reg)
if(l->ullman <= r->ullman) {
regalloc(reg, types[tptr], N);
- if(o & OPtrto)
+ if(o & OPtrto) {
cgen(l, reg);
- else
+ cgen_checknil(reg);
+ } else
agen(l, reg);
}
@@ -1987,20 +1984,10 @@ oindex:
n2.type = types[tptr];
n2.xoffset = Array_nel;
} else {
- if(l->type->width >= unmappedzero && l->op == OIND) {
- // cannot rely on page protections to
- // catch array ptr == 0, so dereference.
- n2 = *reg;
- n2.op = OINDREG;
- n2.type = types[TUINTPTR];
- n2.xoffset = 0;
- regalloc(&n3, n2.type, N);
- gins(AMOVW, &n2, &n3);
- regfree(&n3);
- }
- nodconst(&n2, types[TUINT32], l->type->bound);
if(o & OPtrto)
nodconst(&n2, types[TUINT32], l->type->type->bound);
+ else
+ nodconst(&n2, types[TUINT32], l->type->bound);
}
regalloc(&n3, n2.type, N);
cgen(&n2, &n3);
@@ -2048,14 +2035,14 @@ oindex_const:
// can multiply by width statically
regalloc(reg, types[tptr], N);
- if(o & OPtrto)
+ if(o & OPtrto) {
cgen(l, reg);
- else
+ cgen_checknil(reg);
+ } else
agen(l, reg);
v = mpgetfix(r->val.u.xval);
if(o & ODynam) {
-
if(!debug['B'] && !n->bounded) {
n1 = *reg;
n1.op = OINDREG;
diff --git a/src/cmd/5g/opt.h b/src/cmd/5g/opt.h
index af7d654de..15b9d1458 100644
--- a/src/cmd/5g/opt.h
+++ b/src/cmd/5g/opt.h
@@ -28,6 +28,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#include "../gc/popt.h"
+
#define Z N
#define Adr Addr
@@ -50,9 +52,10 @@ typedef struct Rgn Rgn;
// A Reg is a wrapper around a single Prog (one instruction) that holds
// register optimization information while the optimizer runs.
// r->prog is the instruction.
-// r->prog->regp points back to r.
+// r->prog->opt points back to r.
struct Reg
{
+ Flow f;
Bits set; // variables written by this instruction.
Bits use1; // variables read by prog->from.
@@ -66,19 +69,6 @@ struct Reg
Bits act;
int32 regu; // register used bitmap
- int32 rpo; // reverse post ordering
- int32 active;
-
- uint16 loop; // x5 for every loop
- uchar refset; // diagnostic generated
-
- Reg* p1; // predecessors of this instruction: p1,
- Reg* p2; // and then p2 linked though p2link.
- Reg* p2link;
- Reg* s1; // successors of this instruction (at most two: s1 and s2).
- Reg* s2;
- Reg* link; // next instruction in function code
- Prog* prog; // actual instruction
};
#define R ((Reg*)0)
@@ -93,8 +83,6 @@ struct Rgn
EXTERN int32 exregoffset; // not set
EXTERN int32 exfregoffset; // not set
-EXTERN Reg* firstr;
-EXTERN Reg* lastr;
EXTERN Reg zreg;
EXTERN Reg* freer;
EXTERN Reg** rpo2r;
@@ -132,36 +120,81 @@ void regopt(Prog*);
void addmove(Reg*, int, int, int);
Bits mkvar(Reg *r, Adr *a);
void prop(Reg*, Bits, Bits);
-void loopit(Reg*, int32);
void synch(Reg*, Bits);
uint32 allreg(uint32, Rgn*);
void paint1(Reg*, int);
uint32 paint2(Reg*, int);
void paint3(Reg*, int, int32, int);
void addreg(Adr*, int);
-void dumpit(char *str, Reg *r0);
-int noreturn(Prog *p);
+void dumpit(char *str, Flow *r0, int);
/*
* peep.c
*/
-void peep(void);
-void excise(Reg*);
-Reg* uniqp(Reg*);
-Reg* uniqs(Reg*);
-int regtyp(Adr*);
-int anyvar(Adr*);
-int subprop(Reg*);
-int copyprop(Reg*);
-int copy1(Adr*, Adr*, Reg*, int);
+void peep(Prog*);
+void excise(Flow*);
int copyu(Prog*, Adr*, Adr*);
-int copyas(Adr*, Adr*);
-int copyau(Adr*, Adr*);
-int copysub(Adr*, Adr*, Adr*, int);
-int copysub1(Prog*, Adr*, Adr*, int);
-
int32 RtoB(int);
int32 FtoB(int);
int BtoR(int32);
int BtoF(int32);
+
+/*
+ * prog.c
+ */
+typedef struct ProgInfo ProgInfo;
+struct ProgInfo
+{
+ uint32 flags; // the bits below
+};
+
+enum
+{
+ // Pseudo-op, like TEXT, GLOBL, TYPE, PCDATA, FUNCDATA.
+ Pseudo = 1<<1,
+
+ // There's nothing to say about the instruction,
+ // but it's still okay to see.
+ OK = 1<<2,
+
+ // Size of right-side write, or right-side read if no write.
+ SizeB = 1<<3,
+ SizeW = 1<<4,
+ SizeL = 1<<5,
+ SizeQ = 1<<6,
+ SizeF = 1<<7, // float aka float32
+ SizeD = 1<<8, // double aka float64
+
+ // Left side: address taken, read, write.
+ LeftAddr = 1<<9,
+ LeftRead = 1<<10,
+ LeftWrite = 1<<11,
+
+ // Register in middle; never written.
+ RegRead = 1<<12,
+ CanRegRead = 1<<13,
+
+ // Right side: address taken, read, write.
+ RightAddr = 1<<14,
+ RightRead = 1<<15,
+ RightWrite = 1<<16,
+
+ // Instruction kinds
+ Move = 1<<17, // straight move
+ Conv = 1<<18, // size conversion
+ Cjmp = 1<<19, // conditional jump
+ Break = 1<<20, // breaks control flow (no fallthrough)
+ Call = 1<<21, // function call
+ Jump = 1<<22, // jump
+ Skip = 1<<23, // data instruction
+};
+
+void proginfo(ProgInfo*, Prog*);
+
+// To allow use of AJMP and ACALL in ../gc/popt.c.
+enum
+{
+ AJMP = AB,
+ ACALL = ABL,
+};
diff --git a/src/cmd/5g/peep.c b/src/cmd/5g/peep.c
index b6202a882..c78fb3d1c 100644
--- a/src/cmd/5g/peep.c
+++ b/src/cmd/5g/peep.c
@@ -34,59 +34,45 @@
#include "gg.h"
#include "opt.h"
-int xtramodes(Reg*, Adr*);
-int shiftprop(Reg *r);
-void constprop(Adr *c1, Adr *v1, Reg *r);
-void predicate(void);
-int copyau1(Prog *p, Adr *v);
-int isdconst(Addr *a);
+static int xtramodes(Graph*, Flow*, Adr*);
+static int shortprop(Flow *r);
+static int subprop(Flow*);
+static int copyprop(Graph*, Flow*);
+static int copy1(Adr*, Adr*, Flow*, int);
+static int copyas(Adr*, Adr*);
+static int copyau(Adr*, Adr*);
+static int copysub(Adr*, Adr*, Adr*, int);
+static int copysub1(Prog*, Adr*, Adr*, int);
+static Flow* findpre(Flow *r, Adr *v);
+static int copyau1(Prog *p, Adr *v);
+static int isdconst(Addr *a);
+
+static uint32 gactive;
+
+// UNUSED
+int shiftprop(Flow *r);
+void constprop(Adr *c1, Adr *v1, Flow *r);
+void predicate(Graph*);
void
-peep(void)
+peep(Prog *firstp)
{
- Reg *r, *r1, *r2;
- Prog *p, *p1;
+ Flow *r;
+ Graph *g;
+ Prog *p;
int t;
- p1 = nil;
-/*
- * complete R structure
- */
- for(r=firstr; r!=R; r=r1) {
- r1 = r->link;
- if(r1 == R)
- break;
- p = r->prog->link;
- while(p != r1->prog)
- switch(p->as) {
- default:
- r2 = rega();
- r->link = r2;
- r2->link = r1;
-
- r2->prog = p;
- r2->p1 = r;
- r->s1 = r2;
- r2->s1 = r1;
- r1->p1 = r2;
-
- r = r2;
-
- case ADATA:
- case AGLOBL:
- case ANAME:
- case ASIGNAME:
- case ALOCALS:
- case ATYPE:
- p = p->link;
- }
- }
-//dumpit("begin", firstr);
+ g = flowstart(firstp, sizeof(Flow));
+ if(g == nil)
+ return;
+ gactive = 0;
loop1:
+ if(debug['P'] && debug['v'])
+ dumpit("loop1", g->start, 0);
t = 0;
- for(r=firstr; r!=R; r=r->link) {
+ for(r=g->start; r!=nil; r=r->link) {
p = r->prog;
switch(p->as) {
case ASLL:
@@ -102,18 +88,20 @@ loop1:
// }
break;
+ case AMOVB:
+ case AMOVH:
case AMOVW:
case AMOVF:
case AMOVD:
if(regtyp(&p->from))
if(p->from.type == p->to.type)
if(p->scond == C_SCOND_NONE) {
- if(copyprop(r)) {
+ if(copyprop(g, r)) {
excise(r);
t++;
break;
}
- if(subprop(r) && copyprop(r)) {
+ if(subprop(r) && copyprop(g, r)) {
excise(r);
t++;
break;
@@ -121,6 +109,16 @@ loop1:
}
break;
+ case AMOVHS:
+ case AMOVHU:
+ case AMOVBS:
+ case AMOVBU:
+ if(p->from.type == D_REG) {
+ if(shortprop(r))
+ t++;
+ }
+ break;
+
#ifdef NOTDEF
if(p->scond == C_SCOND_NONE)
if(regtyp(&p->to))
@@ -134,8 +132,7 @@ loop1:
if(t)
goto loop1;
-
- for(r=firstr; r!=R; r=r->link) {
+ for(r=g->start; r!=nil; r=r->link) {
p = r->prog;
switch(p->as) {
case AEOR:
@@ -152,42 +149,21 @@ loop1:
p->reg = NREG;
}
break;
-
- case AMOVH:
- case AMOVHU:
- case AMOVB:
- case AMOVBU:
- /*
- * look for MOVB x,R; MOVB R,R
- */
- r1 = r->link;
- if(p->to.type != D_REG)
- break;
- if(r1 == R)
- break;
- p1 = r1->prog;
- if(p1->as != p->as)
- break;
- if(p1->from.type != D_REG || p1->from.reg != p->to.reg)
- break;
- if(p1->to.type != D_REG || p1->to.reg != p->to.reg)
- break;
- excise(r1);
- break;
}
}
- for(r=firstr; r!=R; r=r->link) {
+ for(r=g->start; r!=nil; r=r->link) {
p = r->prog;
switch(p->as) {
case AMOVW:
case AMOVB:
+ case AMOVBS:
case AMOVBU:
if(p->from.type == D_OREG && p->from.offset == 0)
- xtramodes(r, &p->from);
+ xtramodes(g, r, &p->from);
else
if(p->to.type == D_OREG && p->to.offset == 0)
- xtramodes(r, &p->to);
+ xtramodes(g, r, &p->to);
else
continue;
break;
@@ -198,7 +174,7 @@ loop1:
// if(isdconst(&p->from) || p->from.offset != 0)
// continue;
// r2 = r->s1;
-// if(r2 == R)
+// if(r2 == nil)
// continue;
// t = r2->prog->as;
// switch(t) {
@@ -225,8 +201,8 @@ loop1:
// r1 = r;
// do
// r1 = uniqp(r1);
-// while (r1 != R && r1->prog->as == ANOP);
-// if(r1 == R)
+// while (r1 != nil && r1->prog->as == ANOP);
+// if(r1 == nil)
// continue;
// p1 = r1->prog;
// if(p1->to.type != D_REG)
@@ -261,44 +237,9 @@ loop1:
}
}
-// predicate();
-}
+// predicate(g);
-/*
- * uniqp returns a "unique" predecessor to instruction r.
- * If the instruction is the first one or has multiple
- * predecessors due to jump, R is returned.
- */
-Reg*
-uniqp(Reg *r)
-{
- Reg *r1;
-
- r1 = r->p1;
- if(r1 == R) {
- r1 = r->p2;
- if(r1 == R || r1->p2link != R)
- return R;
- } else
- if(r->p2 != R)
- return R;
- return r1;
-}
-
-Reg*
-uniqs(Reg *r)
-{
- Reg *r1;
-
- r1 = r->s1;
- if(r1 == R) {
- r1 = r->s2;
- if(r1 == R)
- return R;
- } else
- if(r->s2 != R)
- return R;
- return r1;
+ flowend(g);
}
int
@@ -326,13 +267,14 @@ regtyp(Adr *a)
* hopefully, then the former or latter MOV
* will be eliminated by copy propagation.
*/
-int
-subprop(Reg *r0)
+static int
+subprop(Flow *r0)
{
Prog *p;
Adr *v1, *v2;
- Reg *r;
+ Flow *r;
int t;
+ ProgInfo info;
p = r0->prog;
v1 = &p->from;
@@ -341,70 +283,34 @@ subprop(Reg *r0)
v2 = &p->to;
if(!regtyp(v2))
return 0;
- for(r=uniqp(r0); r!=R; r=uniqp(r)) {
- if(uniqs(r) == R)
+ for(r=uniqp(r0); r!=nil; r=uniqp(r)) {
+ if(uniqs(r) == nil)
break;
p = r->prog;
- switch(p->as) {
- case ABL:
+ proginfo(&info, p);
+ if(info.flags & Call)
return 0;
+ if((info.flags & CanRegRead) && p->to.type == D_REG) {
+ info.flags |= RegRead;
+ info.flags &= ~(CanRegRead | RightRead);
+ p->reg = p->to.reg;
+ }
+
+ switch(p->as) {
case AMULLU:
case AMULA:
case AMVN:
return 0;
-
- case ACMN:
- case AADD:
- case ASUB:
- case ASBC:
- case ARSB:
- case ASLL:
- case ASRL:
- case ASRA:
- case AORR:
- case AAND:
- case AEOR:
- case AMUL:
- case AMULU:
- case ADIV:
- case ADIVU:
- case AMOD:
- case AMODU:
-
- case AADDD:
- case AADDF:
- case ASUBD:
- case ASUBF:
- case AMULD:
- case AMULF:
- case ADIVD:
- case ADIVF:
- if(p->to.type == v1->type)
- if(p->to.reg == v1->reg)
- if(p->scond == C_SCOND_NONE) {
- if(p->reg == NREG)
- p->reg = p->to.reg;
- goto gotit;
- }
- break;
-
- case AMOVF:
- case AMOVD:
- case AMOVW:
+ }
+
+ if((info.flags & (RightRead|RightWrite)) == RightWrite) {
if(p->to.type == v1->type)
if(p->to.reg == v1->reg)
if(p->scond == C_SCOND_NONE)
goto gotit;
- break;
-
- case AMOVM:
- t = 1<<v2->reg;
- if((p->from.type == D_CONST && (p->from.offset&t)) ||
- (p->to.type == D_CONST && (p->to.offset&t)))
- return 0;
- break;
}
+
if(copyau(&p->from, v2) ||
copyau1(p, v2) ||
copyau(&p->to, v2))
@@ -452,49 +358,48 @@ gotit:
* set v1 F=1
* set v2 return success
*/
-int
-copyprop(Reg *r0)
+static int
+copyprop(Graph *g, Flow *r0)
{
Prog *p;
Adr *v1, *v2;
- Reg *r;
+ USED(g);
p = r0->prog;
v1 = &p->from;
v2 = &p->to;
if(copyas(v1, v2))
return 1;
- for(r=firstr; r!=R; r=r->link)
- r->active = 0;
+ gactive++;
return copy1(v1, v2, r0->s1, 0);
}
-int
-copy1(Adr *v1, Adr *v2, Reg *r, int f)
+static int
+copy1(Adr *v1, Adr *v2, Flow *r, int f)
{
int t;
Prog *p;
- if(r->active) {
+ if(r->active == gactive) {
if(debug['P'])
print("act set; return 1\n");
return 1;
}
- r->active = 1;
+ r->active = gactive;
if(debug['P'])
print("copy %D->%D f=%d\n", v1, v2, f);
- for(; r != R; r = r->s1) {
+ for(; r != nil; r = r->s1) {
p = r->prog;
if(debug['P'])
print("%P", p);
- if(!f && uniqp(r) == R) {
+ if(!f && uniqp(r) == nil) {
f = 1;
if(debug['P'])
print("; merge; f=%d", f);
}
t = copyu(p, v2, A);
switch(t) {
- case 2: /* rar, cant split */
+ case 2: /* rar, can't split */
if(debug['P'])
print("; %Drar; return 0\n", v2);
return 0;
@@ -546,6 +451,7 @@ copy1(Adr *v1, Adr *v2, Reg *r, int f)
return 1;
}
+// UNUSED
/*
* The idea is to remove redundant constants.
* $c1->v1
@@ -554,17 +460,17 @@ copy1(Adr *v1, Adr *v2, Reg *r, int f)
* The v1->v2 should be eliminated by copy propagation.
*/
void
-constprop(Adr *c1, Adr *v1, Reg *r)
+constprop(Adr *c1, Adr *v1, Flow *r)
{
Prog *p;
if(debug['P'])
print("constprop %D->%D\n", c1, v1);
- for(; r != R; r = r->s1) {
+ for(; r != nil; r = r->s1) {
p = r->prog;
if(debug['P'])
print("%P", p);
- if(uniqp(r) == R) {
+ if(uniqp(r) == nil) {
if(debug['P'])
print("; merge; return\n");
return;
@@ -586,6 +492,65 @@ constprop(Adr *c1, Adr *v1, Reg *r)
}
/*
+ * shortprop eliminates redundant zero/sign extensions.
+ *
+ * MOVBS x, R
+ * <no use R>
+ * MOVBS R, R'
+ *
+ * changed to
+ *
+ * MOVBS x, R
+ * ...
+ * MOVB R, R' (compiled to mov)
+ *
+ * MOVBS above can be a MOVBS, MOVBU, MOVHS or MOVHU.
+ */
+static int
+shortprop(Flow *r)
+{
+ Prog *p, *p1;
+ Flow *r1;
+
+ p = r->prog;
+ r1 = findpre(r, &p->from);
+ if(r1 == nil)
+ return 0;
+
+ p1 = r1->prog;
+ if(p1->as == p->as) {
+ // Two consecutive extensions.
+ goto gotit;
+ }
+
+ if(p1->as == AMOVW && isdconst(&p1->from)
+ && p1->from.offset >= 0 && p1->from.offset < 128) {
+ // Loaded an immediate.
+ goto gotit;
+ }
+
+ return 0;
+
+gotit:
+ if(debug['P'])
+ print("shortprop\n%P\n%P", p1, p);
+ switch(p->as) {
+ case AMOVBS:
+ case AMOVBU:
+ p->as = AMOVB;
+ break;
+ case AMOVHS:
+ case AMOVHU:
+ p->as = AMOVH;
+ break;
+ }
+ if(debug['P'])
+ print(" => %A\n", p->as);
+ return 1;
+}
+
+// UNUSED
+/*
* ASLL x,y,w
* .. (not use w, not set x y w)
* AXXX w,a,b (a != w)
@@ -598,9 +563,9 @@ constprop(Adr *c1, Adr *v1, Reg *r)
*/
#define FAIL(msg) { if(debug['P']) print("\t%s; FAILURE\n", msg); return 0; }
int
-shiftprop(Reg *r)
+shiftprop(Flow *r)
{
- Reg *r1;
+ Flow *r1;
Prog *p, *p1, *p2;
int n, o;
Adr a;
@@ -620,9 +585,9 @@ shiftprop(Reg *r)
for(;;) {
/* find first use of shift result; abort if shift operands or result are changed */
r1 = uniqs(r1);
- if(r1 == R)
+ if(r1 == nil)
FAIL("branch");
- if(uniqp(r1) == R)
+ if(uniqp(r1) == nil)
FAIL("merge");
p1 = r1->prog;
if(debug['P'])
@@ -693,7 +658,7 @@ shiftprop(Reg *r)
if(p1->to.reg != n)
for (;;) {
r1 = uniqs(r1);
- if(r1 == R)
+ if(r1 == nil)
FAIL("inconclusive");
p1 = r1->prog;
if(debug['P'])
@@ -746,40 +711,40 @@ shiftprop(Reg *r)
* before r. It must be a set, and there must be
* a unique path from that instruction to r.
*/
-Reg*
-findpre(Reg *r, Adr *v)
+static Flow*
+findpre(Flow *r, Adr *v)
{
- Reg *r1;
+ Flow *r1;
- for(r1=uniqp(r); r1!=R; r=r1,r1=uniqp(r)) {
+ for(r1=uniqp(r); r1!=nil; r=r1,r1=uniqp(r)) {
if(uniqs(r1) != r)
- return R;
+ return nil;
switch(copyu(r1->prog, v, A)) {
case 1: /* used */
case 2: /* read-alter-rewrite */
- return R;
+ return nil;
case 3: /* set */
case 4: /* set and used */
return r1;
}
}
- return R;
+ return nil;
}
/*
* findinc finds ADD instructions with a constant
* argument which falls within the immed_12 range.
*/
-Reg*
-findinc(Reg *r, Reg *r2, Adr *v)
+static Flow*
+findinc(Flow *r, Flow *r2, Adr *v)
{
- Reg *r1;
+ Flow *r1;
Prog *p;
- for(r1=uniqs(r); r1!=R && r1!=r2; r=r1,r1=uniqs(r)) {
+ for(r1=uniqs(r); r1!=nil && r1!=r2; r=r1,r1=uniqs(r)) {
if(uniqp(r1) != r)
- return R;
+ return nil;
switch(copyu(r1->prog, v, A)) {
case 0: /* not touched */
continue;
@@ -790,14 +755,14 @@ findinc(Reg *r, Reg *r2, Adr *v)
if(p->from.offset > -4096 && p->from.offset < 4096)
return r1;
default:
- return R;
+ return nil;
}
}
- return R;
+ return nil;
}
-int
-nochange(Reg *r, Reg *r2, Prog *p)
+static int
+nochange(Flow *r, Flow *r2, Prog *p)
{
Adr a[3];
int i, n;
@@ -819,7 +784,7 @@ nochange(Reg *r, Reg *r2, Prog *p)
}
if(n == 0)
return 1;
- for(; r!=R && r!=r2; r=uniqs(r)) {
+ for(; r!=nil && r!=r2; r=uniqs(r)) {
p = r->prog;
for(i=0; i<n; i++)
if(copyu(p, &a[i], A) > 1)
@@ -828,10 +793,10 @@ nochange(Reg *r, Reg *r2, Prog *p)
return 1;
}
-int
-findu1(Reg *r, Adr *v)
+static int
+findu1(Flow *r, Adr *v)
{
- for(; r != R; r = r->s1) {
+ for(; r != nil; r = r->s1) {
if(r->active)
return 0;
r->active = 1;
@@ -850,12 +815,12 @@ findu1(Reg *r, Adr *v)
return 0;
}
-int
-finduse(Reg *r, Adr *v)
+static int
+finduse(Graph *g, Flow *r, Adr *v)
{
- Reg *r1;
+ Flow *r1;
- for(r1=firstr; r1!=R; r1=r1->link)
+ for(r1=g->start; r1!=nil; r1=r1->link)
r1->active = 0;
return findu1(r, v);
}
@@ -873,10 +838,10 @@ finduse(Reg *r, Adr *v)
* into
* MOVBU R0<<0(R1),R0
*/
-int
-xtramodes(Reg *r, Adr *a)
+static int
+xtramodes(Graph *g, Flow *r, Adr *a)
{
- Reg *r1, *r2, *r3;
+ Flow *r1, *r2, *r3;
Prog *p, *p1;
Adr v;
@@ -884,7 +849,7 @@ xtramodes(Reg *r, Adr *a)
v = *a;
v.type = D_REG;
r1 = findpre(r, &v);
- if(r1 != R) {
+ if(r1 != nil) {
p1 = r1->prog;
if(p1->to.type == D_REG && p1->to.reg == v.reg)
switch(p1->as) {
@@ -894,12 +859,12 @@ xtramodes(Reg *r, Adr *a)
break;
if(p1->from.type == D_REG ||
(p1->from.type == D_SHIFT && (p1->from.offset&(1<<4)) == 0 &&
- (p->as != AMOVB || (a == &p->from && (p1->from.offset&~0xf) == 0))) ||
+ ((p->as != AMOVB && p->as != AMOVBS) || (a == &p->from && (p1->from.offset&~0xf) == 0))) ||
(p1->from.type == D_CONST &&
p1->from.offset > -4096 && p1->from.offset < 4096))
if(nochange(uniqs(r1), r, p1)) {
if(a != &p->from || v.reg != p->to.reg)
- if (finduse(r->s1, &v)) {
+ if (finduse(g, r->s1, &v)) {
if(p1->reg == NREG || p1->reg == v.reg)
/* pre-indexing */
p->scond |= C_WBIT;
@@ -927,7 +892,7 @@ xtramodes(Reg *r, Adr *a)
break;
case AMOVW:
if(p1->from.type == D_REG)
- if((r2 = findinc(r1, r, &p1->from)) != R) {
+ if((r2 = findinc(r1, r, &p1->from)) != nil) {
for(r3=uniqs(r2); r3->prog->as==ANOP; r3=uniqs(r3))
;
if(r3 == r) {
@@ -936,7 +901,7 @@ xtramodes(Reg *r, Adr *a)
a->reg = p1->to.reg;
a->offset = p1->from.offset;
p->scond |= C_PBIT;
- if(!finduse(r, &r1->prog->to))
+ if(!finduse(g, r, &r1->prog->to))
excise(r1);
excise(r2);
return 1;
@@ -946,7 +911,7 @@ xtramodes(Reg *r, Adr *a)
}
}
if(a != &p->from || a->reg != p->to.reg)
- if((r1 = findinc(r, R, &v)) != R) {
+ if((r1 = findinc(r, nil, &v)) != nil) {
/* post-indexing */
p1 = r1->prog;
a->offset = p1->from.offset;
@@ -971,7 +936,7 @@ copyu(Prog *p, Adr *v, Adr *s)
switch(p->as) {
default:
- print("copyu: cant find %A\n", p->as);
+ print("copyu: can't find %A\n", p->as);
return 2;
case AMOVM:
@@ -1017,8 +982,10 @@ copyu(Prog *p, Adr *v, Adr *s)
case AMOVF:
case AMOVD:
case AMOVH:
+ case AMOVHS:
case AMOVHU:
case AMOVB:
+ case AMOVBS:
case AMOVBU:
case AMOVFW:
case AMOVWF:
@@ -1089,6 +1056,7 @@ copyu(Prog *p, Adr *v, Adr *s)
case ADIVF:
case ADIVD:
+ case ACHECKNIL: /* read */
case ACMPF: /* read, read, */
case ACMPD:
case ACMP:
@@ -1194,7 +1162,8 @@ copyu(Prog *p, Adr *v, Adr *s)
return 3;
return 0;
- case ALOCALS: /* funny */
+ case APCDATA:
+ case AFUNCDATA:
return 0;
}
}
@@ -1204,7 +1173,7 @@ copyu(Prog *p, Adr *v, Adr *s)
* could be set/use depending on
* semantics
*/
-int
+static int
copyas(Adr *a, Adr *v)
{
@@ -1224,10 +1193,24 @@ copyas(Adr *a, Adr *v)
return 0;
}
+int
+sameaddr(Adr *a, Adr *v)
+{
+ if(a->type != v->type)
+ return 0;
+ if(regtyp(v) && a->reg == v->reg)
+ return 1;
+ if(v->type == D_AUTO || v->type == D_PARAM) {
+ if(v->offset == a->offset)
+ return 1;
+ }
+ return 0;
+}
+
/*
* either direct or indirect
*/
-int
+static int
copyau(Adr *a, Adr *v)
{
@@ -1268,7 +1251,7 @@ copyau(Adr *a, Adr *v)
* ADD r,r,r
* CMP r,r,
*/
-int
+static int
copyau1(Prog *p, Adr *v)
{
@@ -1284,7 +1267,7 @@ copyau1(Prog *p, Adr *v)
return 1;
return 0;
}
- print("copyau1: cant tell %P\n", p);
+ print("copyau1: can't tell %P\n", p);
}
return 0;
}
@@ -1293,7 +1276,7 @@ copyau1(Prog *p, Adr *v)
* substitute s for v in a
* return failure to substitute
*/
-int
+static int
copysub(Adr *a, Adr *v, Adr *s, int f)
{
@@ -1316,7 +1299,7 @@ copysub(Adr *a, Adr *v, Adr *s, int f)
return 0;
}
-int
+static int
copysub1(Prog *p1, Adr *v, Adr *s, int f)
{
@@ -1351,9 +1334,9 @@ struct {
};
typedef struct {
- Reg *start;
- Reg *last;
- Reg *end;
+ Flow *start;
+ Flow *last;
+ Flow *end;
int len;
} Joininfo;
@@ -1373,13 +1356,13 @@ enum {
Keepbranch
};
-int
+static int
isbranch(Prog *p)
{
return (ABEQ <= p->as) && (p->as <= ABLE);
}
-int
+static int
predicable(Prog *p)
{
switch(p->as) {
@@ -1409,7 +1392,7 @@ predicable(Prog *p)
*
* C_SBIT may also have been set explicitly in p->scond.
*/
-int
+static int
modifiescpsr(Prog *p)
{
switch(p->as) {
@@ -1438,8 +1421,8 @@ modifiescpsr(Prog *p)
* Find the maximal chain of instructions starting with r which could
* be executed conditionally
*/
-int
-joinsplit(Reg *r, Joininfo *j)
+static int
+joinsplit(Flow *r, Joininfo *j)
{
j->start = r;
j->last = r;
@@ -1474,8 +1457,8 @@ joinsplit(Reg *r, Joininfo *j)
return Toolong;
}
-Reg*
-successor(Reg *r)
+static Flow*
+successor(Flow *r)
{
if(r->s1)
return r->s1;
@@ -1483,11 +1466,11 @@ successor(Reg *r)
return r->s2;
}
-void
-applypred(Reg *rstart, Joininfo *j, int cond, int branch)
+static void
+applypred(Flow *rstart, Joininfo *j, int cond, int branch)
{
int pred;
- Reg *r;
+ Flow *r;
if(j->len == 0)
return;
@@ -1520,13 +1503,13 @@ applypred(Reg *rstart, Joininfo *j, int cond, int branch)
}
void
-predicate(void)
+predicate(Graph *g)
{
- Reg *r;
+ Flow *r;
int t1, t2;
Joininfo j1, j2;
- for(r=firstr; r!=R; r=r->link) {
+ for(r=g->start; r!=nil; r=r->link) {
if (isbranch(r->prog)) {
t1 = joinsplit(r->s1, &j1);
t2 = joinsplit(r->s2, &j2);
@@ -1549,10 +1532,24 @@ predicate(void)
}
}
-int
+static int
isdconst(Addr *a)
{
if(a->type == D_CONST && a->reg == NREG)
return 1;
return 0;
}
+
+int
+stackaddr(Addr *a)
+{
+ return regtyp(a) && a->reg == REGSP;
+}
+
+int
+smallindir(Addr *a, Addr *reg)
+{
+ return reg->type == D_REG && a->type == D_OREG &&
+ a->reg == reg->reg &&
+ 0 <= a->offset && a->offset < 4096;
+}
diff --git a/src/cmd/5g/prog.c b/src/cmd/5g/prog.c
new file mode 100644
index 000000000..5aa6163d8
--- /dev/null
+++ b/src/cmd/5g/prog.c
@@ -0,0 +1,143 @@
+// Copyright 2013 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 <u.h>
+#include <libc.h>
+#include "gg.h"
+#include "opt.h"
+
+enum
+{
+ RightRdwr = RightRead | RightWrite,
+};
+
+// This table gives the basic information about instruction
+// generated by the compiler and processed in the optimizer.
+// See opt.h for bit definitions.
+//
+// Instructions not generated need not be listed.
+// As an exception to that rule, we typically write down all the
+// size variants of an operation even if we just use a subset.
+//
+// The table is formatted for 8-space tabs.
+static ProgInfo progtable[ALAST] = {
+ [ATYPE]= {Pseudo | Skip},
+ [ATEXT]= {Pseudo},
+ [AFUNCDATA]= {Pseudo},
+ [APCDATA]= {Pseudo},
+ [AUNDEF]= {OK},
+ [AUSEFIELD]= {OK},
+ [ACHECKNIL]= {LeftRead},
+
+ // NOP is an internal no-op that also stands
+ // for USED and SET annotations, not the Intel opcode.
+ [ANOP]= {LeftRead | RightWrite},
+
+ // Integer.
+ [AADC]= {SizeL | LeftRead | RegRead | RightWrite},
+ [AADD]= {SizeL | LeftRead | RegRead | RightWrite},
+ [AAND]= {SizeL | LeftRead | RegRead | RightWrite},
+ [ABIC]= {SizeL | LeftRead | RegRead | RightWrite},
+ [ACMN]= {SizeL | LeftRead | RightRead},
+ [ACMP]= {SizeL | LeftRead | RightRead},
+ [ADIVU]= {SizeL | LeftRead | RegRead | RightWrite},
+ [ADIV]= {SizeL | LeftRead | RegRead | RightWrite},
+ [AEOR]= {SizeL | LeftRead | RegRead | RightWrite},
+ [AMODU]= {SizeL | LeftRead | RegRead | RightWrite},
+ [AMOD]= {SizeL | LeftRead | RegRead | RightWrite},
+ [AMULALU]= {SizeL | LeftRead | RegRead | RightRdwr},
+ [AMULAL]= {SizeL | LeftRead | RegRead | RightRdwr},
+ [AMULA]= {SizeL | LeftRead | RegRead | RightRdwr},
+ [AMULU]= {SizeL | LeftRead | RegRead | RightWrite},
+ [AMUL]= {SizeL | LeftRead | RegRead | RightWrite},
+ [AMULL]= {SizeL | LeftRead | RegRead | RightWrite},
+ [AMULLU]= {SizeL | LeftRead | RegRead | RightWrite},
+ [AMVN]= {SizeL | LeftRead | RightWrite},
+ [AORR]= {SizeL | LeftRead | RegRead | RightWrite},
+ [ARSB]= {SizeL | LeftRead | RegRead | RightWrite},
+ [ARSC]= {SizeL | LeftRead | RegRead | RightWrite},
+ [ASBC]= {SizeL | LeftRead | RegRead | RightWrite},
+ [ASLL]= {SizeL | LeftRead | RegRead | RightWrite},
+ [ASRA]= {SizeL | LeftRead | RegRead | RightWrite},
+ [ASRL]= {SizeL | LeftRead | RegRead | RightWrite},
+ [ASUB]= {SizeL | LeftRead | RegRead | RightWrite},
+ [ATEQ]= {SizeL | LeftRead | RightRead},
+ [ATST]= {SizeL | LeftRead | RightRead},
+
+ // Floating point.
+ [AADDD]= {SizeD | LeftRead | RightRdwr},
+ [AADDF]= {SizeF | LeftRead | RightRdwr},
+ [ACMPD]= {SizeD | LeftRead | RightRead},
+ [ACMPF]= {SizeF | LeftRead | RightRead},
+ [ADIVD]= {SizeD | LeftRead | RightRdwr},
+ [ADIVF]= {SizeF | LeftRead | RightRdwr},
+ [AMULD]= {SizeD | LeftRead | RightRdwr},
+ [AMULF]= {SizeF | LeftRead | RightRdwr},
+ [ASUBD]= {SizeD | LeftRead | RightRdwr},
+ [ASUBF]= {SizeF | LeftRead | RightRdwr},
+
+ // Conversions.
+ [AMOVWD]= {SizeD | LeftRead | RightWrite | Conv},
+ [AMOVWF]= {SizeF | LeftRead | RightWrite | Conv},
+ [AMOVDF]= {SizeF | LeftRead | RightWrite | Conv},
+ [AMOVDW]= {SizeL | LeftRead | RightWrite | Conv},
+ [AMOVFD]= {SizeD | LeftRead | RightWrite | Conv},
+ [AMOVFW]= {SizeL | LeftRead | RightWrite | Conv},
+
+ // Moves.
+ [AMOVB]= {SizeB | LeftRead | RightWrite | Move},
+ [AMOVD]= {SizeD | LeftRead | RightWrite | Move},
+ [AMOVF]= {SizeF | LeftRead | RightWrite | Move},
+ [AMOVH]= {SizeW | LeftRead | RightWrite | Move},
+ [AMOVW]= {SizeL | LeftRead | RightWrite | Move},
+
+ // These should be split into the two different conversions instead
+ // of overloading the one.
+ [AMOVBS]= {SizeB | LeftRead | RightWrite | Conv},
+ [AMOVBU]= {SizeB | LeftRead | RightWrite | Conv},
+ [AMOVHS]= {SizeW | LeftRead | RightWrite | Conv},
+ [AMOVHU]= {SizeW | LeftRead | RightWrite | Conv},
+
+ // Jumps.
+ [AB]= {Jump | Break},
+ [ABL]= {Call},
+ [ABEQ]= {Cjmp},
+ [ABNE]= {Cjmp},
+ [ABCS]= {Cjmp},
+ [ABHS]= {Cjmp},
+ [ABCC]= {Cjmp},
+ [ABLO]= {Cjmp},
+ [ABMI]= {Cjmp},
+ [ABPL]= {Cjmp},
+ [ABVS]= {Cjmp},
+ [ABVC]= {Cjmp},
+ [ABHI]= {Cjmp},
+ [ABLS]= {Cjmp},
+ [ABGE]= {Cjmp},
+ [ABLT]= {Cjmp},
+ [ABGT]= {Cjmp},
+ [ABLE]= {Cjmp},
+ [ARET]= {Break},
+};
+
+void
+proginfo(ProgInfo *info, Prog *p)
+{
+ *info = progtable[p->as];
+ if(info->flags == 0)
+ fatal("unknown instruction %P", p);
+
+ if(p->from.type == D_CONST && p->from.sym != nil && (info->flags & LeftRead)) {
+ info->flags &= ~LeftRead;
+ info->flags |= LeftAddr;
+ }
+
+ if((info->flags & RegRead) && p->reg == NREG) {
+ info->flags &= ~RegRead;
+ info->flags |= CanRegRead | RightRead;
+ }
+
+ if(((p->scond & C_SCOND) != C_SCOND_NONE) && (info->flags & RightWrite))
+ info->flags |= RightRead;
+}
diff --git a/src/cmd/5g/reg.c b/src/cmd/5g/reg.c
index eaaaf9be3..d2a8cc488 100644
--- a/src/cmd/5g/reg.c
+++ b/src/cmd/5g/reg.c
@@ -36,29 +36,10 @@
#define NREGVAR 32
#define REGBITS ((uint32)0xffffffff)
-#define P2R(p) (Reg*)(p->reg)
void addsplits(void);
- int noreturn(Prog *p);
-static int first = 0;
-
-static void fixjmp(Prog*);
-
-
-Reg*
-rega(void)
-{
- Reg *r;
-
- r = freer;
- if(r == R) {
- r = mal(sizeof(*r));
- } else
- freer = r->link;
-
- *r = zreg;
- return r;
-}
+static Reg* firstr;
+static int first = 1;
int
rcmp(const void *a1, const void *a2)
@@ -100,7 +81,7 @@ setoutvar(void)
}
void
-excise(Reg *r)
+excise(Flow *r)
{
Prog *p;
@@ -177,38 +158,19 @@ regopt(Prog *firstp)
{
Reg *r, *r1;
Prog *p;
- int i, z, nr;
+ Graph *g;
+ int i, z;
uint32 vreg;
Bits bit;
-
- if(first == 0) {
+ ProgInfo info;
+
+ if(first) {
fmtinstall('Q', Qconv);
+ first = 0;
}
fixjmp(firstp);
-
- first++;
- if(debug['K']) {
- if(first != 13)
- return;
-// debug['R'] = 2;
-// debug['P'] = 2;
- print("optimizing %S\n", curfn->nname->sym);
- }
-
- // count instructions
- nr = 0;
- for(p=firstp; p!=P; p=p->link)
- nr++;
-
- // if too big dont bother
- if(nr >= 10000) {
-// print("********** %S is too big (%d)\n", curfn->nname->sym, nr);
- return;
- }
-
- firstr = R;
- lastr = R;
+ mergetemp(firstp);
/*
* control flow is more complicated in generated go code
@@ -241,178 +203,43 @@ regopt(Prog *firstp)
* allocate pcs
* find use and set of variables
*/
- nr = 0;
- for(p=firstp; p != P; p = p->link) {
- switch(p->as) {
- case ADATA:
- case AGLOBL:
- case ANAME:
- case ASIGNAME:
- case ALOCALS:
- case ATYPE:
- continue;
- }
- r = rega();
- nr++;
- if(firstr == R) {
- firstr = r;
- lastr = r;
- } else {
- lastr->link = r;
- r->p1 = lastr;
- lastr->s1 = r;
- lastr = r;
- }
- r->prog = p;
- p->regp = r;
-
- r1 = r->p1;
- if(r1 != R) {
- switch(r1->prog->as) {
- case ARET:
- case AB:
- case ARFE:
- r->p1 = R;
- r1->s1 = R;
- }
- }
+ g = flowstart(firstp, sizeof(Reg));
+ if(g == nil)
+ return;
+ firstr = (Reg*)g->start;
+
+ for(r = firstr; r != R; r = (Reg*)r->f.link) {
+ p = r->f.prog;
+ proginfo(&info, p);
// Avoid making variables for direct-called functions.
if(p->as == ABL && p->to.type == D_EXTERN)
continue;
- /*
- * left side always read
- */
bit = mkvar(r, &p->from);
- for(z=0; z<BITS; z++)
- r->use1.b[z] |= bit.b[z];
-
- /*
- * middle always read when present
- */
- if(p->reg != NREG) {
+ if(info.flags & LeftRead)
+ for(z=0; z<BITS; z++)
+ r->use1.b[z] |= bit.b[z];
+ if(info.flags & LeftAddr)
+ setaddrs(bit);
+
+ if(info.flags & RegRead) {
if(p->from.type != D_FREG)
r->use1.b[0] |= RtoB(p->reg);
else
r->use1.b[0] |= FtoB(p->reg);
}
- /*
- * right side depends on opcode
- */
- bit = mkvar(r, &p->to);
- if(bany(&bit))
- switch(p->as) {
- default:
- yyerror("reg: unknown op: %A", p->as);
- break;
-
- /*
- * right side read
- */
- case ATST:
- case ATEQ:
- case ACMP:
- case ACMN:
- case ACMPD:
- case ACMPF:
- rightread:
- for(z=0; z<BITS; z++)
- r->use2.b[z] |= bit.b[z];
- break;
-
- /*
- * right side read or read+write, depending on middle
- * ADD x, z => z += x
- * ADD x, y, z => z = x + y
- */
- case AADD:
- case AAND:
- case AEOR:
- case ASUB:
- case ARSB:
- case AADC:
- case ASBC:
- case ARSC:
- case AORR:
- case ABIC:
- case ASLL:
- case ASRL:
- case ASRA:
- case AMUL:
- case AMULU:
- case ADIV:
- case AMOD:
- case AMODU:
- case ADIVU:
- if(p->reg != NREG)
- goto rightread;
- // fall through
-
- /*
- * right side read+write
- */
- case AADDF:
- case AADDD:
- case ASUBF:
- case ASUBD:
- case AMULF:
- case AMULD:
- case ADIVF:
- case ADIVD:
- case AMULA:
- case AMULAL:
- case AMULALU:
- for(z=0; z<BITS; z++) {
- r->use2.b[z] |= bit.b[z];
- r->set.b[z] |= bit.b[z];
- }
- break;
-
- /*
- * right side write
- */
- case ANOP:
- case AMOVB:
- case AMOVBU:
- case AMOVD:
- case AMOVDF:
- case AMOVDW:
- case AMOVF:
- case AMOVFW:
- case AMOVH:
- case AMOVHU:
- case AMOVW:
- case AMOVWD:
- case AMOVWF:
- case AMVN:
- case AMULL:
- case AMULLU:
- if((p->scond & C_SCOND) != C_SCOND_NONE)
+ if(info.flags & (RightAddr | RightRead | RightWrite)) {
+ bit = mkvar(r, &p->to);
+ if(info.flags & RightAddr)
+ setaddrs(bit);
+ if(info.flags & RightRead)
for(z=0; z<BITS; z++)
r->use2.b[z] |= bit.b[z];
- for(z=0; z<BITS; z++)
- r->set.b[z] |= bit.b[z];
- break;
-
- /*
- * funny
- */
- case ABL:
- setaddrs(bit);
- break;
- }
-
- if(p->as == AMOVM) {
- z = p->to.offset;
- if(p->from.type == D_CONST)
- z = p->from.offset;
- for(i=0; z; i++) {
- if(z&1)
- regbits |= RtoB(i);
- z >>= 1;
- }
+ if(info.flags & RightWrite)
+ for(z=0; z<BITS; z++)
+ r->set.b[z] |= bit.b[z];
}
}
if(firstr == R)
@@ -432,50 +259,16 @@ regopt(Prog *firstp)
}
if(debug['R'] && debug['v'])
- dumpit("pass1", firstr);
+ dumpit("pass1", &firstr->f, 1);
/*
* pass 2
- * turn branch references to pointers
- * build back pointers
- */
- for(r=firstr; r!=R; r=r->link) {
- p = r->prog;
- if(p->to.type == D_BRANCH) {
- if(p->to.u.branch == P)
- fatal("pnil %P", p);
- r1 = p->to.u.branch->regp;
- if(r1 == R)
- fatal("rnil %P", p);
- if(r1 == r) {
- //fatal("ref to self %P", p);
- continue;
- }
- r->s2 = r1;
- r->p2link = r1->p2;
- r1->p2 = r;
- }
- }
- if(debug['R']) {
- p = firstr->prog;
- print("\n%L %D\n", p->lineno, &p->from);
- print(" addr = %Q\n", addrs);
- }
-
- if(debug['R'] && debug['v'])
- dumpit("pass2", firstr);
-
- /*
- * pass 2.5
* find looping structure
*/
- for(r = firstr; r != R; r = r->link)
- r->active = 0;
- change = 0;
- loopit(firstr, nr);
+ flowrpo(g);
if(debug['R'] && debug['v'])
- dumpit("pass2.5", firstr);
+ dumpit("pass2", &firstr->f, 1);
/*
* pass 3
@@ -484,17 +277,17 @@ regopt(Prog *firstp)
*/
loop1:
change = 0;
- for(r = firstr; r != R; r = r->link)
- r->active = 0;
- for(r = firstr; r != R; r = r->link)
- if(r->prog->as == ARET)
+ for(r = firstr; r != R; r = (Reg*)r->f.link)
+ r->f.active = 0;
+ for(r = firstr; r != R; r = (Reg*)r->f.link)
+ if(r->f.prog->as == ARET)
prop(r, zbits, zbits);
loop11:
/* pick up unreachable code */
i = 0;
for(r = firstr; r != R; r = r1) {
- r1 = r->link;
- if(r1 && r1->active && !r->active) {
+ r1 = (Reg*)r->f.link;
+ if(r1 && r1->f.active && !r->f.active) {
prop(r, zbits, zbits);
i = 1;
}
@@ -505,7 +298,7 @@ loop11:
goto loop1;
if(debug['R'] && debug['v'])
- dumpit("pass3", firstr);
+ dumpit("pass3", &firstr->f, 1);
/*
@@ -515,8 +308,8 @@ loop11:
*/
loop2:
change = 0;
- for(r = firstr; r != R; r = r->link)
- r->active = 0;
+ for(r = firstr; r != R; r = (Reg*)r->f.link)
+ r->f.active = 0;
synch(firstr, zbits);
if(change)
goto loop2;
@@ -524,12 +317,12 @@ loop2:
addsplits();
if(debug['R'] && debug['v'])
- dumpit("pass4", firstr);
+ dumpit("pass4", &firstr->f, 1);
if(debug['R'] > 1) {
print("\nprop structure:\n");
- for(r = firstr; r != R; r = r->link) {
- print("%d:%P", r->loop, r->prog);
+ for(r = firstr; r != R; r = (Reg*)r->f.link) {
+ print("%d:%P", r->f.loop, r->f.prog);
for(z=0; z<BITS; z++) {
bit.b[z] = r->set.b[z] |
r->refahead.b[z] | r->calahead.b[z] |
@@ -563,7 +356,7 @@ loop2:
* pass 4.5
* move register pseudo-variables into regu.
*/
- for(r = firstr; r != R; r = r->link) {
+ for(r = firstr; r != R; r = (Reg*)r->f.link) {
r->regu = (r->refbehind.b[0] | r->set.b[0]) & REGBITS;
r->set.b[0] &= ~REGBITS;
@@ -578,7 +371,7 @@ loop2:
}
if(debug['R'] && debug['v'])
- dumpit("pass4.5", firstr);
+ dumpit("pass4.5", &firstr->f, 1);
/*
* pass 5
@@ -590,27 +383,27 @@ loop2:
for(z=0; z<BITS; z++)
bit.b[z] = (r->refahead.b[z] | r->calahead.b[z]) &
~(externs.b[z] | params.b[z] | addrs.b[z] | consts.b[z]);
- if(bany(&bit) & !r->refset) {
+ if(bany(&bit) & !r->f.refset) {
// should never happen - all variables are preset
if(debug['w'])
- print("%L: used and not set: %Q\n", r->prog->lineno, bit);
- r->refset = 1;
+ print("%L: used and not set: %Q\n", r->f.prog->lineno, bit);
+ r->f.refset = 1;
}
}
- for(r = firstr; r != R; r = r->link)
+ for(r = firstr; r != R; r = (Reg*)r->f.link)
r->act = zbits;
rgp = region;
nregion = 0;
- for(r = firstr; r != R; r = r->link) {
+ for(r = firstr; r != R; r = (Reg*)r->f.link) {
for(z=0; z<BITS; z++)
bit.b[z] = r->set.b[z] &
~(r->refahead.b[z] | r->calahead.b[z] | addrs.b[z]);
- if(bany(&bit) && !r->refset) {
+ if(bany(&bit) && !r->f.refset) {
if(debug['w'])
- print("%L: set and not used: %Q\n", r->prog->lineno, bit);
- r->refset = 1;
- excise(r);
+ print("%L: set and not used: %Q\n", r->f.prog->lineno, bit);
+ r->f.refset = 1;
+ excise(&r->f);
}
for(z=0; z<BITS; z++)
bit.b[z] = LOAD(r) & ~(r->act.b[z] | addrs.b[z]);
@@ -626,7 +419,7 @@ loop2:
if(change <= 0) {
if(debug['R'])
print("%L $%d: %Q\n",
- r->prog->lineno, change, blsh(i));
+ r->f.prog->lineno, change, blsh(i));
continue;
}
rgp->cost = change;
@@ -643,7 +436,7 @@ brk:
qsort(region, nregion, sizeof(region[0]), rcmp);
if(debug['R'] && debug['v'])
- dumpit("pass5", firstr);
+ dumpit("pass5", &firstr->f, 1);
/*
* pass 6
@@ -658,13 +451,13 @@ brk:
if(debug['R']) {
if(rgp->regno >= NREG)
print("%L $%d F%d: %Q\n",
- rgp->enter->prog->lineno,
+ rgp->enter->f.prog->lineno,
rgp->cost,
rgp->regno-NREG,
bit);
else
print("%L $%d R%d: %Q\n",
- rgp->enter->prog->lineno,
+ rgp->enter->f.prog->lineno,
rgp->cost,
rgp->regno,
bit);
@@ -675,18 +468,18 @@ brk:
}
if(debug['R'] && debug['v'])
- dumpit("pass6", firstr);
+ dumpit("pass6", &firstr->f, 1);
/*
* pass 7
* peep-hole on basic block
*/
if(!debug['R'] || debug['P']) {
- peep();
+ peep(firstp);
}
if(debug['R'] && debug['v'])
- dumpit("pass7", firstr);
+ dumpit("pass7", &firstr->f, 1);
/*
* last pass
@@ -742,11 +535,8 @@ brk:
}
}
}
- if(lastr != R) {
- lastr->link = freer;
- freer = firstr;
- }
+ flowend(g);
}
void
@@ -756,13 +546,13 @@ addsplits(void)
int z, i;
Bits bit;
- for(r = firstr; r != R; r = r->link) {
- if(r->loop > 1)
+ for(r = firstr; r != R; r = (Reg*)r->f.link) {
+ if(r->f.loop > 1)
continue;
- if(r->prog->as == ABL)
+ if(r->f.prog->as == ABL)
continue;
- for(r1 = r->p2; r1 != R; r1 = r1->p2link) {
- if(r1->loop <= 1)
+ for(r1 = (Reg*)r->f.p2; r1 != R; r1 = (Reg*)r1->f.p2link) {
+ if(r1->f.loop <= 1)
continue;
for(z=0; z<BITS; z++)
bit.b[z] = r1->calbehind.b[z] &
@@ -789,7 +579,7 @@ addmove(Reg *r, int bn, int rn, int f)
p1 = mal(sizeof(*p1));
*p1 = zprog;
- p = r->prog;
+ p = r->f.prog;
// If there's a stack fixup coming (after BL newproc or BL deferproc),
// delay the load until after the fixup.
@@ -814,14 +604,14 @@ addmove(Reg *r, int bn, int rn, int f)
a->type = D_CONST;
if(v->addr)
- fatal("addmove: shouldnt be doing this %A\n", a);
+ fatal("addmove: shouldn't be doing this %A\n", a);
switch(v->etype) {
default:
print("What is this %E\n", v->etype);
case TINT8:
- p1->as = AMOVB;
+ p1->as = AMOVBS;
break;
case TBOOL:
case TUINT8:
@@ -829,7 +619,7 @@ addmove(Reg *r, int bn, int rn, int f)
p1->as = AMOVBU;
break;
case TINT16:
- p1->as = AMOVH;
+ p1->as = AMOVHS;
break;
case TUINT16:
p1->as = AMOVHU;
@@ -908,9 +698,6 @@ mkvar(Reg *r, Adr *a)
case D_BRANCH:
break;
- case D_CONST:
- flag = 1;
- goto onereg;
case D_REGREG:
case D_REGREG2:
@@ -921,9 +708,9 @@ mkvar(Reg *r, Adr *a)
bit.b[0] |= RtoB(a->reg);
return bit;
+ case D_CONST:
case D_REG:
case D_SHIFT:
- onereg:
if(a->reg != NREG) {
bit = zbits;
bit.b[0] = RtoB(a->reg);
@@ -933,11 +720,11 @@ mkvar(Reg *r, Adr *a)
case D_OREG:
if(a->reg != NREG) {
- if(a == &r->prog->from)
+ if(a == &r->f.prog->from)
r->use1.b[0] |= RtoB(a->reg);
else
r->use2.b[0] |= RtoB(a->reg);
- if(r->prog->scond & (C_PBIT|C_WBIT))
+ if(r->f.prog->scond & (C_PBIT|C_WBIT))
r->set.b[0] |= RtoB(a->reg);
}
break;
@@ -1040,7 +827,7 @@ prop(Reg *r, Bits ref, Bits cal)
Reg *r1, *r2;
int z;
- for(r1 = r; r1 != R; r1 = r1->p1) {
+ for(r1 = r; r1 != R; r1 = (Reg*)r1->f.p1) {
for(z=0; z<BITS; z++) {
ref.b[z] |= r1->refahead.b[z];
if(ref.b[z] != r1->refahead.b[z]) {
@@ -1053,9 +840,9 @@ prop(Reg *r, Bits ref, Bits cal)
change++;
}
}
- switch(r1->prog->as) {
+ switch(r1->f.prog->as) {
case ABL:
- if(noreturn(r1->prog))
+ if(noreturn(r1->f.prog))
break;
for(z=0; z<BITS; z++) {
cal.b[z] |= ref.b[z] | externs.b[z];
@@ -1095,158 +882,22 @@ prop(Reg *r, Bits ref, Bits cal)
r1->refbehind.b[z] = ref.b[z];
r1->calbehind.b[z] = cal.b[z];
}
- if(r1->active)
+ if(r1->f.active)
break;
- r1->active = 1;
+ r1->f.active = 1;
}
- for(; r != r1; r = r->p1)
- for(r2 = r->p2; r2 != R; r2 = r2->p2link)
+ for(; r != r1; r = (Reg*)r->f.p1)
+ for(r2 = (Reg*)r->f.p2; r2 != R; r2 = (Reg*)r2->f.p2link)
prop(r2, r->refbehind, r->calbehind);
}
-/*
- * find looping structure
- *
- * 1) find reverse postordering
- * 2) find approximate dominators,
- * the actual dominators if the flow graph is reducible
- * otherwise, dominators plus some other non-dominators.
- * See Matthew S. Hecht and Jeffrey D. Ullman,
- * "Analysis of a Simple Algorithm for Global Data Flow Problems",
- * Conf. Record of ACM Symp. on Principles of Prog. Langs, Boston, Massachusetts,
- * Oct. 1-3, 1973, pp. 207-217.
- * 3) find all nodes with a predecessor dominated by the current node.
- * such a node is a loop head.
- * recursively, all preds with a greater rpo number are in the loop
- */
-int32
-postorder(Reg *r, Reg **rpo2r, int32 n)
-{
- Reg *r1;
-
- r->rpo = 1;
- r1 = r->s1;
- if(r1 && !r1->rpo)
- n = postorder(r1, rpo2r, n);
- r1 = r->s2;
- if(r1 && !r1->rpo)
- n = postorder(r1, rpo2r, n);
- rpo2r[n] = r;
- n++;
- return n;
-}
-
-int32
-rpolca(int32 *idom, int32 rpo1, int32 rpo2)
-{
- int32 t;
-
- if(rpo1 == -1)
- return rpo2;
- while(rpo1 != rpo2){
- if(rpo1 > rpo2){
- t = rpo2;
- rpo2 = rpo1;
- rpo1 = t;
- }
- while(rpo1 < rpo2){
- t = idom[rpo2];
- if(t >= rpo2)
- fatal("bad idom");
- rpo2 = t;
- }
- }
- return rpo1;
-}
-
-int
-doms(int32 *idom, int32 r, int32 s)
-{
- while(s > r)
- s = idom[s];
- return s == r;
-}
-
-int
-loophead(int32 *idom, Reg *r)
-{
- int32 src;
-
- src = r->rpo;
- if(r->p1 != R && doms(idom, src, r->p1->rpo))
- return 1;
- for(r = r->p2; r != R; r = r->p2link)
- if(doms(idom, src, r->rpo))
- return 1;
- return 0;
-}
-
-void
-loopmark(Reg **rpo2r, int32 head, Reg *r)
-{
- if(r->rpo < head || r->active == head)
- return;
- r->active = head;
- r->loop += LOOP;
- if(r->p1 != R)
- loopmark(rpo2r, head, r->p1);
- for(r = r->p2; r != R; r = r->p2link)
- loopmark(rpo2r, head, r);
-}
-
-void
-loopit(Reg *r, int32 nr)
-{
- Reg *r1;
- int32 i, d, me;
-
- if(nr > maxnr) {
- rpo2r = mal(nr * sizeof(Reg*));
- idom = mal(nr * sizeof(int32));
- maxnr = nr;
- }
- d = postorder(r, rpo2r, 0);
- if(d > nr)
- fatal("too many reg nodes");
- nr = d;
- for(i = 0; i < nr / 2; i++){
- r1 = rpo2r[i];
- rpo2r[i] = rpo2r[nr - 1 - i];
- rpo2r[nr - 1 - i] = r1;
- }
- for(i = 0; i < nr; i++)
- rpo2r[i]->rpo = i;
-
- idom[0] = 0;
- for(i = 0; i < nr; i++){
- r1 = rpo2r[i];
- me = r1->rpo;
- d = -1;
- // rpo2r[r->rpo] == r protects against considering dead code,
- // which has r->rpo == 0.
- if(r1->p1 != R && rpo2r[r1->p1->rpo] == r1->p1 && r1->p1->rpo < me)
- d = r1->p1->rpo;
- for(r1 = r1->p2; r1 != nil; r1 = r1->p2link)
- if(rpo2r[r1->rpo] == r1 && r1->rpo < me)
- d = rpolca(idom, d, r1->rpo);
- idom[i] = d;
- }
-
- for(i = 0; i < nr; i++){
- r1 = rpo2r[i];
- r1->loop++;
- if(r1->p2 != R && loophead(idom, r1))
- loopmark(rpo2r, i, r1);
- }
-}
-
void
synch(Reg *r, Bits dif)
{
Reg *r1;
int z;
- for(r1 = r; r1 != R; r1 = r1->s1) {
+ for(r1 = r; r1 != R; r1 = (Reg*)r1->f.s1) {
for(z=0; z<BITS; z++) {
dif.b[z] = (dif.b[z] &
~(~r1->refbehind.b[z] & r1->refahead.b[z])) |
@@ -1256,13 +907,13 @@ synch(Reg *r, Bits dif)
change++;
}
}
- if(r1->active)
+ if(r1->f.active)
break;
- r1->active = 1;
+ r1->f.active = 1;
for(z=0; z<BITS; z++)
dif.b[z] &= ~(~r1->calbehind.b[z] & r1->calahead.b[z]);
- if(r1->s2 != R)
- synch(r1->s2, dif);
+ if(r1->f.s2 != nil)
+ synch((Reg*)r1->f.s2, dif);
}
}
@@ -1333,7 +984,7 @@ paint1(Reg *r, int bn)
for(;;) {
if(!(r->refbehind.b[z] & bb))
break;
- r1 = r->p1;
+ r1 = (Reg*)r->f.p1;
if(r1 == R)
break;
if(!(r1->refahead.b[z] & bb))
@@ -1344,48 +995,48 @@ paint1(Reg *r, int bn)
}
if(LOAD(r) & ~(r->set.b[z] & ~(r->use1.b[z]|r->use2.b[z])) & bb) {
- change -= CLOAD * r->loop;
+ change -= CLOAD * r->f.loop;
if(debug['R'] > 1)
- print("%d%P\td %Q $%d\n", r->loop,
- r->prog, blsh(bn), change);
+ print("%d%P\td %Q $%d\n", r->f.loop,
+ r->f.prog, blsh(bn), change);
}
for(;;) {
r->act.b[z] |= bb;
- p = r->prog;
+ p = r->f.prog;
if(r->use1.b[z] & bb) {
- change += CREF * r->loop;
+ change += CREF * r->f.loop;
if(debug['R'] > 1)
- print("%d%P\tu1 %Q $%d\n", r->loop,
+ print("%d%P\tu1 %Q $%d\n", r->f.loop,
p, blsh(bn), change);
}
if((r->use2.b[z]|r->set.b[z]) & bb) {
- change += CREF * r->loop;
+ change += CREF * r->f.loop;
if(debug['R'] > 1)
- print("%d%P\tu2 %Q $%d\n", r->loop,
+ print("%d%P\tu2 %Q $%d\n", r->f.loop,
p, blsh(bn), change);
}
if(STORE(r) & r->regdiff.b[z] & bb) {
- change -= CLOAD * r->loop;
+ change -= CLOAD * r->f.loop;
if(debug['R'] > 1)
- print("%d%P\tst %Q $%d\n", r->loop,
+ print("%d%P\tst %Q $%d\n", r->f.loop,
p, blsh(bn), change);
}
if(r->refbehind.b[z] & bb)
- for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+ for(r1 = (Reg*)r->f.p2; r1 != R; r1 = (Reg*)r1->f.p2link)
if(r1->refahead.b[z] & bb)
paint1(r1, bn);
if(!(r->refahead.b[z] & bb))
break;
- r1 = r->s2;
+ r1 = (Reg*)r->f.s2;
if(r1 != R)
if(r1->refbehind.b[z] & bb)
paint1(r1, bn);
- r = r->s1;
+ r = (Reg*)r->f.s1;
if(r == R)
break;
if(r->act.b[z] & bb)
@@ -1410,7 +1061,7 @@ paint2(Reg *r, int bn)
for(;;) {
if(!(r->refbehind.b[z] & bb))
break;
- r1 = r->p1;
+ r1 = (Reg*)r->f.p1;
if(r1 == R)
break;
if(!(r1->refahead.b[z] & bb))
@@ -1425,17 +1076,17 @@ paint2(Reg *r, int bn)
vreg |= r->regu;
if(r->refbehind.b[z] & bb)
- for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+ for(r1 = (Reg*)r->f.p2; r1 != R; r1 = (Reg*)r1->f.p2link)
if(r1->refahead.b[z] & bb)
vreg |= paint2(r1, bn);
if(!(r->refahead.b[z] & bb))
break;
- r1 = r->s2;
+ r1 = (Reg*)r->f.s2;
if(r1 != R)
if(r1->refbehind.b[z] & bb)
vreg |= paint2(r1, bn);
- r = r->s1;
+ r = (Reg*)r->f.s1;
if(r == R)
break;
if(!(r->act.b[z] & bb))
@@ -1461,7 +1112,7 @@ paint3(Reg *r, int bn, int32 rb, int rn)
for(;;) {
if(!(r->refbehind.b[z] & bb))
break;
- r1 = r->p1;
+ r1 = (Reg*)r->f.p1;
if(r1 == R)
break;
if(!(r1->refahead.b[z] & bb))
@@ -1476,7 +1127,7 @@ paint3(Reg *r, int bn, int32 rb, int rn)
for(;;) {
r->act.b[z] |= bb;
- p = r->prog;
+ p = r->f.prog;
if(r->use1.b[z] & bb) {
if(debug['R'])
@@ -1498,17 +1149,17 @@ paint3(Reg *r, int bn, int32 rb, int rn)
r->regu |= rb;
if(r->refbehind.b[z] & bb)
- for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+ for(r1 = (Reg*)r->f.p2; r1 != R; r1 = (Reg*)r1->f.p2link)
if(r1->refahead.b[z] & bb)
paint3(r1, bn, rb, rn);
if(!(r->refahead.b[z] & bb))
break;
- r1 = r->s2;
+ r1 = (Reg*)r->f.s2;
if(r1 != R)
if(r1->refbehind.b[z] & bb)
paint3(r1, bn, rb, rn);
- r = r->s1;
+ r = (Reg*)r->f.s1;
if(r == R)
break;
if(r->act.b[z] & bb)
@@ -1582,91 +1233,74 @@ BtoF(int32 b)
return bitno(b) - 16;
}
-static Sym* symlist[10];
-
-int
-noreturn(Prog *p)
-{
- Sym *s;
- int i;
-
- if(symlist[0] == S) {
- symlist[0] = pkglookup("panicindex", runtimepkg);
- symlist[1] = pkglookup("panicslice", runtimepkg);
- symlist[2] = pkglookup("throwinit", runtimepkg);
- symlist[3] = pkglookup("panic", runtimepkg);
- symlist[4] = pkglookup("panicwrap", runtimepkg);
- }
-
- s = p->to.sym;
- if(s == S)
- return 0;
- for(i=0; symlist[i]!=S; i++)
- if(s == symlist[i])
- return 1;
- return 0;
-}
-
void
-dumpone(Reg *r)
+dumpone(Flow *f, int isreg)
{
int z;
Bits bit;
+ Reg *r;
- print("%d:%P", r->loop, r->prog);
- for(z=0; z<BITS; z++)
- bit.b[z] =
- r->set.b[z] |
- r->use1.b[z] |
- r->use2.b[z] |
- r->refbehind.b[z] |
- r->refahead.b[z] |
- r->calbehind.b[z] |
- r->calahead.b[z] |
- r->regdiff.b[z] |
- r->act.b[z] |
- 0;
- if(bany(&bit)) {
- print("\t");
- if(bany(&r->set))
- print(" s:%Q", r->set);
- if(bany(&r->use1))
- print(" u1:%Q", r->use1);
- if(bany(&r->use2))
- print(" u2:%Q", r->use2);
- if(bany(&r->refbehind))
- print(" rb:%Q ", r->refbehind);
- if(bany(&r->refahead))
- print(" ra:%Q ", r->refahead);
- if(bany(&r->calbehind))
- print(" cb:%Q ", r->calbehind);
- if(bany(&r->calahead))
- print(" ca:%Q ", r->calahead);
- if(bany(&r->regdiff))
- print(" d:%Q ", r->regdiff);
- if(bany(&r->act))
- print(" a:%Q ", r->act);
+ print("%d:%P", f->loop, f->prog);
+ if(isreg) {
+ r = (Reg*)f;
+ for(z=0; z<BITS; z++)
+ bit.b[z] =
+ r->set.b[z] |
+ r->use1.b[z] |
+ r->use2.b[z] |
+ r->refbehind.b[z] |
+ r->refahead.b[z] |
+ r->calbehind.b[z] |
+ r->calahead.b[z] |
+ r->regdiff.b[z] |
+ r->act.b[z] |
+ 0;
+ if(bany(&bit)) {
+ print("\t");
+ if(bany(&r->set))
+ print(" s:%Q", r->set);
+ if(bany(&r->use1))
+ print(" u1:%Q", r->use1);
+ if(bany(&r->use2))
+ print(" u2:%Q", r->use2);
+ if(bany(&r->refbehind))
+ print(" rb:%Q ", r->refbehind);
+ if(bany(&r->refahead))
+ print(" ra:%Q ", r->refahead);
+ if(bany(&r->calbehind))
+ print(" cb:%Q ", r->calbehind);
+ if(bany(&r->calahead))
+ print(" ca:%Q ", r->calahead);
+ if(bany(&r->regdiff))
+ print(" d:%Q ", r->regdiff);
+ if(bany(&r->act))
+ print(" a:%Q ", r->act);
+ }
}
print("\n");
}
void
-dumpit(char *str, Reg *r0)
+dumpit(char *str, Flow *r0, int isreg)
{
- Reg *r, *r1;
+ Flow *r, *r1;
print("\n%s\n", str);
- for(r = r0; r != R; r = r->link) {
- dumpone(r);
+ for(r = r0; r != nil; r = r->link) {
+ dumpone(r, isreg);
r1 = r->p2;
- if(r1 != R) {
+ if(r1 != nil) {
print(" pred:");
- for(; r1 != R; r1 = r1->p2link)
+ for(; r1 != nil; r1 = r1->p2link)
print(" %.4ud", r1->prog->loc);
+ if(r->p1 != nil)
+ print(" (and %.4ud)", r->p1->prog->loc);
+ else
+ print(" (only)");
print("\n");
}
// r1 = r->s1;
-// if(r1 != R) {
+// if(r1 != nil) {
// print(" succ:");
// for(; r1 != R; r1 = r1->s1)
// print(" %.4ud", r1->prog->loc);
@@ -1674,123 +1308,3 @@ dumpit(char *str, Reg *r0)
// }
}
}
-
-/*
- * the code generator depends on being able to write out JMP (B)
- * instructions that it can jump to now but fill in later.
- * the linker will resolve them nicely, but they make the code
- * longer and more difficult to follow during debugging.
- * remove them.
- */
-
-/* what instruction does a JMP to p eventually land on? */
-static Prog*
-chasejmp(Prog *p, int *jmploop)
-{
- int n;
-
- n = 0;
- while(p != P && p->as == AB && p->to.type == D_BRANCH) {
- if(++n > 10) {
- *jmploop = 1;
- break;
- }
- p = p->to.u.branch;
- }
- return p;
-}
-
-/*
- * reuse reg pointer for mark/sweep state.
- * leave reg==nil at end because alive==nil.
- */
-#define alive ((void*)0)
-#define dead ((void*)1)
-
-/* mark all code reachable from firstp as alive */
-static void
-mark(Prog *firstp)
-{
- Prog *p;
-
- for(p=firstp; p; p=p->link) {
- if(p->regp != dead)
- break;
- p->regp = alive;
- if(p->as != ABL && p->to.type == D_BRANCH && p->to.u.branch)
- mark(p->to.u.branch);
- if(p->as == AB || p->as == ARET || (p->as == ABL && noreturn(p)))
- break;
- }
-}
-
-static void
-fixjmp(Prog *firstp)
-{
- int jmploop;
- Prog *p, *last;
-
- if(debug['R'] && debug['v'])
- print("\nfixjmp\n");
-
- // pass 1: resolve jump to B, mark all code as dead.
- jmploop = 0;
- for(p=firstp; p; p=p->link) {
- if(debug['R'] && debug['v'])
- print("%P\n", p);
- if(p->as != ABL && p->to.type == D_BRANCH && p->to.u.branch && p->to.u.branch->as == AB) {
- p->to.u.branch = chasejmp(p->to.u.branch, &jmploop);
- if(debug['R'] && debug['v'])
- print("->%P\n", p);
- }
- p->regp = dead;
- }
- if(debug['R'] && debug['v'])
- print("\n");
-
- // pass 2: mark all reachable code alive
- mark(firstp);
-
- // pass 3: delete dead code (mostly JMPs).
- last = nil;
- for(p=firstp; p; p=p->link) {
- if(p->regp == dead) {
- if(p->link == P && p->as == ARET && last && last->as != ARET) {
- // This is the final ARET, and the code so far doesn't have one.
- // Let it stay.
- } else {
- if(debug['R'] && debug['v'])
- print("del %P\n", p);
- continue;
- }
- }
- if(last)
- last->link = p;
- last = p;
- }
- last->link = P;
-
- // pass 4: elide JMP to next instruction.
- // only safe if there are no jumps to JMPs anymore.
- if(!jmploop) {
- last = nil;
- for(p=firstp; p; p=p->link) {
- if(p->as == AB && p->to.type == D_BRANCH && p->to.u.branch == p->link) {
- if(debug['R'] && debug['v'])
- print("del %P\n", p);
- continue;
- }
- if(last)
- last->link = p;
- last = p;
- }
- last->link = P;
- }
-
- if(debug['R'] && debug['v']) {
- print("\n");
- for(p=firstp; p; p=p->link)
- print("%P\n", p);
- print("\n");
- }
-}
diff --git a/src/cmd/5l/5.out.h b/src/cmd/5l/5.out.h
index 97a2421b3..e8cf83ddd 100644
--- a/src/cmd/5l/5.out.h
+++ b/src/cmd/5l/5.out.h
@@ -31,12 +31,7 @@
#define NSNAME 8
#define NSYM 50
#define NREG 16
-
-#define NOPROF (1<<0)
-#define DUPOK (1<<1)
-#define NOSPLIT (1<<2)
-#define RODATA (1<<3)
-#define NOPTR (1<<4)
+#include "../ld/textflag.h"
#define REGRET 0
/* -1 disables use of REGARG */
@@ -140,8 +135,10 @@ enum as
AMODU,
AMOVB,
+ AMOVBS,
AMOVBU,
AMOVH,
+ AMOVHS,
AMOVHU,
AMOVW,
AMOVM,
@@ -197,8 +194,10 @@ enum as
AMULAWB,
AUSEFIELD,
- ALOCALS,
ATYPE,
+ AFUNCDATA,
+ APCDATA,
+ ACHECKNIL,
ALAST,
};
@@ -275,7 +274,7 @@ enum as
#define D_PLT1 (D_NONE+44) // R_ARM_PLT32, 2nd inst: add ip, ip, #0xNN000
#define D_PLT2 (D_NONE+45) // R_ARM_PLT32, 3rd inst: ldr pc, [ip, #0xNNN]!
#define D_CALL (D_NONE+46) // R_ARM_PLT32/R_ARM_CALL/R_ARM_JUMP24, bl xxxxx or b yyyyy
-#define D_TLS (D_NONE+47)
+#define D_TLS (D_NONE+47) // R_ARM_TLS_LE32
/*
* this is the ranlib header
diff --git a/src/cmd/5l/asm.c b/src/cmd/5l/asm.c
index a1220a38e..33cdf8096 100644
--- a/src/cmd/5l/asm.c
+++ b/src/cmd/5l/asm.c
@@ -41,6 +41,7 @@ char linuxdynld[] = "/lib/ld-linux.so.3"; // 2 for OABI, 3 for EABI
char freebsddynld[] = "/usr/libexec/ld-elf.so.1";
char openbsddynld[] = "XXX";
char netbsddynld[] = "/libexec/ld.elf_so";
+char dragonflydynld[] = "XXX";
int32
entryvalue(void)
@@ -93,12 +94,6 @@ braddoff(int32 a, int32 b)
return (((uint32)a) & 0xff000000U) | (0x00ffffffU & (uint32)(a + b));
}
-Sym *
-lookuprel(void)
-{
- return lookup(".rel", 0);
-}
-
void
adddynrela(Sym *rel, Sym *s, Reloc *r)
{
@@ -264,6 +259,26 @@ elfreloc1(Reloc *r, vlong sectoff)
else
return -1;
break;
+
+ case D_CALL:
+ if(r->siz == 4) {
+ if((r->add & 0xff000000) == 0xeb000000) // BL
+ LPUT(R_ARM_CALL | elfsym<<8);
+ else
+ LPUT(R_ARM_JUMP24 | elfsym<<8);
+ } else
+ return -1;
+ break;
+
+ case D_TLS:
+ if(r->siz == 4) {
+ if(flag_shared)
+ LPUT(R_ARM_TLS_IE32 | elfsym<<8);
+ else
+ LPUT(R_ARM_TLS_LE32 | elfsym<<8);
+ } else
+ return -1;
+ break;
}
return 0;
@@ -308,6 +323,34 @@ machoreloc1(Reloc *r, vlong sectoff)
int
archreloc(Reloc *r, Sym *s, vlong *val)
{
+ Sym *rs;
+
+ if(linkmode == LinkExternal) {
+ switch(r->type) {
+ case D_CALL:
+ r->done = 0;
+
+ // set up addend for eventual relocation via outer symbol.
+ rs = r->sym;
+ r->xadd = r->add;
+ if(r->xadd & 0x800000)
+ r->xadd |= ~0xffffff;
+ r->xadd *= 4;
+ while(rs->outer != nil) {
+ r->xadd += symaddr(rs) - symaddr(rs->outer);
+ rs = rs->outer;
+ }
+
+ if(rs->type != SHOSTOBJ && rs->sect == nil)
+ diag("missing section for %s", rs->name);
+ r->xsym = rs;
+
+ *val = braddoff((0xff000000U & (uint32)r->add),
+ (0xffffff & (uint32)(r->xadd / 4)));
+ return 0;
+ }
+ return -1;
+ }
switch(r->type) {
case D_CONST:
*val = r->add;
@@ -550,13 +593,20 @@ asmb(void)
sect = segtext.sect;
cseek(sect->vaddr - segtext.vaddr + segtext.fileoff);
codeblk(sect->vaddr, sect->len);
-
- /* output read-only data in text segment (rodata, gosymtab, pclntab, ...) */
for(sect = sect->next; sect != nil; sect = sect->next) {
cseek(sect->vaddr - segtext.vaddr + segtext.fileoff);
datblk(sect->vaddr, sect->len);
}
+ if(segrodata.filelen > 0) {
+ if(debug['v'])
+ Bprint(&bso, "%5.2f rodatblk\n", cputime());
+ Bflush(&bso);
+
+ cseek(segrodata.fileoff);
+ datblk(segrodata.vaddr, segrodata.filelen);
+ }
+
if(debug['v'])
Bprint(&bso, "%5.2f datblk\n", cputime());
Bflush(&bso);
@@ -587,7 +637,7 @@ asmb(void)
symo = HEADR+segtext.len+segdata.filelen;
break;
ElfSym:
- symo = rnd(HEADR+segtext.filelen, INITRND)+segdata.filelen;
+ symo = rnd(HEADR+segtext.filelen, INITRND)+rnd(HEADR+segrodata.filelen, INITRND)+segdata.filelen;
symo = rnd(symo, INITRND);
break;
}
@@ -759,7 +809,7 @@ nopstat(char *f, Count *c)
}
void
-asmout(Prog *p, Optab *o, int32 *out)
+asmout(Prog *p, Optab *o, int32 *out, Sym *gmsym)
{
int32 o1, o2, o3, o4, o5, o6, v;
int r, rf, rt, rt2;
@@ -791,7 +841,7 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p-
r = p->reg;
if(p->to.type == D_NONE)
rt = 0;
- if(p->as == AMOVW || p->as == AMVN)
+ if(p->as == AMOVB || p->as == AMOVH || p->as == AMOVW || p->as == AMVN)
r = 0;
else
if(r == NREG)
@@ -842,11 +892,19 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p-
break;
case 5: /* bra s */
+ o1 = opbra(p->as, p->scond);
v = -8;
- // TODO: Use addrel.
+ if(p->to.sym != S && p->to.sym->type != 0) {
+ rel = addrel(cursym);
+ rel->off = pc - cursym->value;
+ rel->siz = 4;
+ rel->sym = p->to.sym;
+ rel->add = o1 | ((v >> 2) & 0xffffff);
+ rel->type = D_CALL;
+ break;
+ }
if(p->cond != P)
v = (p->cond->pc - pc) - 8;
- o1 = opbra(p->as, p->scond);
o1 |= (v >> 2) & 0xffffff;
break;
@@ -858,17 +916,12 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p-
o1 |= REGPC << 12;
break;
- case 7: /* bl ,O(R) -> mov PC,link; add $O,R,PC */
+ case 7: /* bl (R) -> blx R */
aclass(&p->to);
- o1 = oprrr(AADD, p->scond);
- o1 |= immrot(0);
- o1 |= REGPC << 16;
- o1 |= REGLINK << 12;
-
- o2 = oprrr(AADD, p->scond);
- o2 |= immrot(instoffset);
- o2 |= p->to.reg << 16;
- o2 |= REGPC << 12;
+ if(instoffset != 0)
+ diag("%P: doesn't support BL offset(REG) where offset != 0", p);
+ o1 = oprrr(ABL, p->scond);
+ o1 |= p->to.reg;
break;
case 8: /* sll $c,[R],R -> mov (R<<$c),R */
@@ -909,7 +962,13 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p-
rel->siz = 4;
rel->sym = p->to.sym;
rel->add = p->to.offset;
- if(flag_shared) {
+ if(rel->sym == gmsym) {
+ rel->type = D_TLS;
+ if(flag_shared)
+ rel->add += pc - p->pcrel->pc - 8 - rel->siz;
+ rel->xadd = rel->add;
+ rel->xsym = rel->sym;
+ } else if(flag_shared) {
rel->type = D_PCREL;
rel->add += pc - p->pcrel->pc - 8;
} else
@@ -952,7 +1011,7 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p-
r = p->to.reg;
o1 |= (p->from.reg)|(r<<12);
o2 |= (r)|(r<<12);
- if(p->as == AMOVB || p->as == AMOVBU) {
+ if(p->as == AMOVB || p->as == AMOVBS || p->as == AMOVBU) {
o1 |= (24<<7);
o2 |= (24<<7);
} else {
@@ -1033,7 +1092,7 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p-
if(r == NREG)
r = o->param;
o2 = olrr(REGTMP,r, p->to.reg, p->scond);
- if(p->as == AMOVBU || p->as == AMOVB)
+ if(p->as == AMOVBU || p->as == AMOVBS || p->as == AMOVB)
o2 |= 1<<22;
break;
@@ -1222,7 +1281,7 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p-
if(p->to.reg == NREG)
diag("MOV to shifter operand");
o1 = osrr(p->from.reg, p->to.offset, p->to.reg, p->scond);
- if(p->as == AMOVB || p->as == AMOVBU)
+ if(p->as == AMOVB || p->as == AMOVBS || p->as == AMOVBU)
o1 |= 1<<22;
break;
@@ -1240,9 +1299,22 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p-
case 63: /* bcase */
if(p->cond != P) {
- o1 = p->cond->pc;
- if(flag_shared)
- o1 = o1 - p->pcrel->pc - 16;
+ rel = addrel(cursym);
+ rel->off = pc - cursym->value;
+ rel->siz = 4;
+ if(p->to.sym != S && p->to.sym->type != 0) {
+ rel->sym = p->to.sym;
+ rel->add = p->to.offset;
+ } else {
+ rel->sym = cursym;
+ rel->add = p->cond->pc - cursym->value;
+ }
+ if(o->flag & LPCREL) {
+ rel->type = D_PCREL;
+ rel->add += pc - p->pcrel->pc - 16 + rel->siz;
+ } else
+ rel->type = D_ADDR;
+ o1 = 0;
}
break;
@@ -1263,7 +1335,7 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p-
if(!o1)
break;
o2 = olr(0, REGTMP, p->to.reg, p->scond);
- if(p->as == AMOVBU || p->as == AMOVB)
+ if(p->as == AMOVBU || p->as == AMOVBS || p->as == AMOVB)
o2 |= 1<<22;
if(o->flag & LPCREL) {
o3 = o2;
@@ -1307,9 +1379,9 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p-
if(r == NREG)
r = o->param;
o1 = olhr(instoffset, r, p->to.reg, p->scond);
- if(p->as == AMOVB)
+ if(p->as == AMOVB || p->as == AMOVBS)
o1 ^= (1<<5)|(1<<6);
- else if(p->as == AMOVH)
+ else if(p->as == AMOVH || p->as == AMOVHS)
o1 ^= (1<<6);
break;
case 72: /* movh/movhu R,L(R) -> strh */
@@ -1329,9 +1401,9 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p-
if(r == NREG)
r = o->param;
o2 = olhrr(REGTMP, r, p->to.reg, p->scond);
- if(p->as == AMOVB)
+ if(p->as == AMOVB || p->as == AMOVBS)
o2 ^= (1<<5)|(1<<6);
- else if(p->as == AMOVH)
+ else if(p->as == AMOVH || p->as == AMOVHS)
o2 ^= (1<<6);
break;
case 74: /* bx $I */
@@ -1483,9 +1555,9 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p-
if(!o1)
break;
o2 = olhr(0, REGTMP, p->to.reg, p->scond);
- if(p->as == AMOVB)
+ if(p->as == AMOVB || p->as == AMOVBS)
o2 ^= (1<<5)|(1<<6);
- else if(p->as == AMOVH)
+ else if(p->as == AMOVH || p->as == AMOVHS)
o2 ^= (1<<6);
if(o->flag & LPCREL) {
o3 = o2;
@@ -1515,11 +1587,9 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p-
// This is supposed to be something that stops execution.
// It's not supposed to be reached, ever, but if it is, we'd
// like to be able to tell how we got there. Assemble as
- // BL $0
- // TODO: Use addrel.
- v = (0 - pc) - 8;
- o1 = opbra(ABL, C_SCOND_NONE);
- o1 |= (v >> 2) & 0xffffff;
+ // 0xf7fabcfd which is guranteed to raise undefined instruction
+ // exception.
+ o1 = 0xf7fabcfd;
break;
case 97: /* CLZ Rm, Rd */
o1 = oprrr(p->as, p->scond);
@@ -1639,6 +1709,8 @@ oprrr(int a, int sc)
case ACMP: return o | (0xa<<21) | (1<<20);
case ACMN: return o | (0xb<<21) | (1<<20);
case AORR: return o | (0xc<<21);
+ case AMOVB:
+ case AMOVH:
case AMOVW: return o | (0xd<<21);
case ABIC: return o | (0xe<<21);
case AMVN: return o | (0xf<<21);
@@ -1711,6 +1783,9 @@ oprrr(int a, int sc)
return (o & (0xf<<28)) | (0x12 << 20) | (0xc<<4);
case AMULAWB:
return (o & (0xf<<28)) | (0x12 << 20) | (0x8<<4);
+
+ case ABL: // BLX REG
+ return (o & (0xf<<28)) | (0x12fff3 << 4);
}
diag("bad rrr %d", a);
prasm(curp);
diff --git a/src/cmd/5l/l.h b/src/cmd/5l/l.h
index e7794c723..ae4b05ba1 100644
--- a/src/cmd/5l/l.h
+++ b/src/cmd/5l/l.h
@@ -170,6 +170,7 @@ struct Sym
char* dynimplib;
char* dynimpvers;
struct Section* sect;
+ struct Hist* hist;
// STEXT
Auto* autom;
@@ -182,7 +183,6 @@ struct Sym
Reloc* r;
int32 nr;
int32 maxr;
- int rel_ro;
};
#define SIGNINTERN (1729*325*1729)
@@ -280,7 +280,7 @@ enum
MINSIZ = 64,
NENT = 100,
MAXIO = 8192,
- MAXHIST = 20, /* limit of path elements for history symbols */
+ MAXHIST = 40, /* limit of path elements for history symbols */
MINLC = 4,
};
@@ -292,7 +292,6 @@ EXTERN int32 INITDAT; /* data location */
EXTERN int32 INITRND; /* data round above text location */
EXTERN int32 INITTEXT; /* text location */
EXTERN char* INITENTRY; /* entry point */
-EXTERN char* LIBINITENTRY; /* shared library entry point */
EXTERN int32 autosize;
EXTERN Auto* curauto;
EXTERN Auto* curhist;
@@ -317,7 +316,6 @@ EXTERN char* rpath;
EXTERN uint32 stroffset;
EXTERN int32 symsize;
EXTERN Sym* textp;
-EXTERN int version;
EXTERN char xcmp[C_GOK+1][C_GOK+1];
EXTERN Prog zprg;
EXTERN int dtype;
@@ -364,7 +362,7 @@ int aclass(Adr*);
void addhist(int32, int);
Prog* appendp(Prog*);
void asmb(void);
-void asmout(Prog*, Optab*, int32*);
+void asmout(Prog*, Optab*, int32*, Sym*);
int32 atolwhex(char*);
Prog* brloop(Prog*);
void buildop(void);
@@ -434,7 +432,6 @@ int32 immaddr(int32);
int32 opbra(int, int);
int brextra(Prog*);
int isbranch(Prog*);
-void fnptrs(void);
void doelf(void);
void dozerostk(void); // used by -Z
diff --git a/src/cmd/5l/list.c b/src/cmd/5l/list.c
index a051774b4..7502a3b81 100644
--- a/src/cmd/5l/list.c
+++ b/src/cmd/5l/list.c
@@ -473,7 +473,7 @@ Oconv(Fmt *fp)
void
diag(char *fmt, ...)
{
- char buf[STRINGSZ], *tn, *sep;
+ char buf[1024], *tn, *sep;
va_list arg;
tn = "";
diff --git a/src/cmd/5l/noop.c b/src/cmd/5l/noop.c
index 99a096a31..305ed684e 100644
--- a/src/cmd/5l/noop.c
+++ b/src/cmd/5l/noop.c
@@ -32,18 +32,16 @@
#include "l.h"
#include "../ld/lib.h"
-
-// see ../../runtime/proc.c:/StackGuard
-enum
-{
- StackBig = 4096,
- StackSmall = 128,
-};
+#include "../../pkg/runtime/stack.h"
static Sym* sym_div;
static Sym* sym_divu;
static Sym* sym_mod;
static Sym* sym_modu;
+static Sym* symmorestack;
+static Prog* pmorestack;
+
+static Prog* stacksplit(Prog*, int32);
static void
linkcase(Prog *casep)
@@ -62,16 +60,16 @@ linkcase(Prog *casep)
void
noops(void)
{
- Prog *p, *q, *q1;
+ Prog *p, *q, *q1, *q2;
int o;
- Prog *pmorestack;
- Sym *symmorestack;
+ Sym *tlsfallback, *gmsym;
/*
* find leaf subroutines
* strip NOPs
* expand RET
* expand BECOME pseudo
+ * fixup TLS
*/
if(debug['v'])
@@ -86,6 +84,10 @@ noops(void)
pmorestack = symmorestack->text;
pmorestack->reg |= NOSPLIT;
+ tlsfallback = lookup("runtime.read_tls_fallback", 0);
+ gmsym = S;
+ if(linkmode == LinkExternal)
+ gmsym = lookup("runtime.tlsgm", 0);
q = P;
for(cursym = textp; cursym != nil; cursym = cursym->next) {
for(p = cursym->text; p != P; p = p->link) {
@@ -150,6 +152,82 @@ noops(void)
}
}
break;
+ case AWORD:
+ // Rewrite TLS register fetch: MRC 15, 0, <reg>, C13, C0, 3
+ if((p->to.offset & 0xffff0fff) == 0xee1d0f70) {
+ if(HEADTYPE == Hopenbsd) {
+ p->as = ARET;
+ } else if(goarm < 7) {
+ if(tlsfallback->type != STEXT) {
+ diag("runtime·read_tls_fallback not defined");
+ errorexit();
+ }
+ // BL runtime.read_tls_fallback(SB)
+ p->as = ABL;
+ p->to.type = D_BRANCH;
+ p->to.sym = tlsfallback;
+ p->cond = tlsfallback->text;
+ p->to.offset = 0;
+ cursym->text->mark &= ~LEAF;
+ }
+ if(linkmode == LinkExternal) {
+ // runtime.tlsgm is relocated with R_ARM_TLS_LE32
+ // and $runtime.tlsgm will contain the TLS offset.
+ //
+ // MOV $runtime.tlsgm+tlsoffset(SB), REGTMP
+ // ADD REGTMP, <reg>
+ //
+ // In shared mode, runtime.tlsgm is relocated with
+ // R_ARM_TLS_IE32 and runtime.tlsgm(SB) will point
+ // to the GOT entry containing the TLS offset.
+ //
+ // MOV runtime.tlsgm(SB), REGTMP
+ // ADD REGTMP, <reg>
+ // SUB -tlsoffset, <reg>
+ //
+ // The SUB compensates for tlsoffset
+ // used in runtime.save_gm and runtime.load_gm.
+ q = p;
+ p = appendp(p);
+ p->as = AMOVW;
+ p->scond = 14;
+ p->reg = NREG;
+ if(flag_shared) {
+ p->from.type = D_OREG;
+ p->from.offset = 0;
+ } else {
+ p->from.type = D_CONST;
+ p->from.offset = tlsoffset;
+ }
+ p->from.sym = gmsym;
+ p->from.name = D_EXTERN;
+ p->to.type = D_REG;
+ p->to.reg = REGTMP;
+ p->to.offset = 0;
+
+ p = appendp(p);
+ p->as = AADD;
+ p->scond = 14;
+ p->reg = NREG;
+ p->from.type = D_REG;
+ p->from.reg = REGTMP;
+ p->to.type = D_REG;
+ p->to.reg = (q->to.offset & 0xf000) >> 12;
+ p->to.offset = 0;
+
+ if(flag_shared) {
+ p = appendp(p);
+ p->as = ASUB;
+ p->scond = 14;
+ p->reg = NREG;
+ p->from.type = D_CONST;
+ p->from.offset = -tlsoffset;
+ p->to.type = D_REG;
+ p->to.reg = (q->to.offset & 0xf000) >> 12;
+ p->to.offset = 0;
+ }
+ }
+ }
}
q = p;
}
@@ -180,160 +258,47 @@ noops(void)
break;
}
- if(p->reg & NOSPLIT) {
- q1 = prg();
- q1->as = AMOVW;
- q1->scond |= C_WBIT;
- q1->line = p->line;
- q1->from.type = D_REG;
- q1->from.reg = REGLINK;
- q1->to.type = D_OREG;
- q1->to.offset = -autosize;
- q1->to.reg = REGSP;
- q1->spadj = autosize;
- q1->link = p->link;
- p->link = q1;
- } else if (autosize < StackBig) {
- // split stack check for small functions
- // MOVW g_stackguard(g), R1
- // CMP R1, $-autosize(SP)
- // MOVW.LO $autosize, R1
- // MOVW.LO $args, R2
- // MOVW.LO R14, R3
- // BL.LO runtime.morestack(SB) // modifies LR
- // MOVW.W R14,$-autosize(SP)
-
- // TODO(kaib): add more trampolines
- // TODO(kaib): put stackguard in register
- // TODO(kaib): add support for -K and underflow detection
-
- // MOVW g_stackguard(g), R1
+ if(!(p->reg & NOSPLIT))
+ p = stacksplit(p, autosize); // emit split check
+
+ // MOVW.W R14,$-autosize(SP)
+ p = appendp(p);
+ p->as = AMOVW;
+ p->scond |= C_WBIT;
+ p->from.type = D_REG;
+ p->from.reg = REGLINK;
+ p->to.type = D_OREG;
+ p->to.offset = -autosize;
+ p->to.reg = REGSP;
+ p->spadj = autosize;
+
+ if(cursym->text->reg & WRAPPER) {
+ // g->panicwrap += autosize;
+ // MOVW panicwrap_offset(g), R3
+ // ADD $autosize, R3
+ // MOVW R3 panicwrap_offset(g)
p = appendp(p);
p->as = AMOVW;
p->from.type = D_OREG;
p->from.reg = REGG;
- p->to.type = D_REG;
- p->to.reg = 1;
-
- if(autosize < StackSmall) {
- // CMP R1, SP
- p = appendp(p);
- p->as = ACMP;
- p->from.type = D_REG;
- p->from.reg = 1;
- p->reg = REGSP;
- } else {
- // MOVW $-autosize(SP), R2
- // CMP R1, R2
- p = appendp(p);
- p->as = AMOVW;
- p->from.type = D_CONST;
- p->from.reg = REGSP;
- p->from.offset = -autosize;
- p->to.type = D_REG;
- p->to.reg = 2;
-
- p = appendp(p);
- p->as = ACMP;
- p->from.type = D_REG;
- p->from.reg = 1;
- p->reg = 2;
- }
-
- // MOVW.LO $autosize, R1
- p = appendp(p);
- p->as = AMOVW;
- p->scond = C_SCOND_LO;
- p->from.type = D_CONST;
- p->from.offset = autosize;
- p->to.type = D_REG;
- p->to.reg = 1;
-
- // MOVW.LO $args, R2
- p = appendp(p);
- p->as = AMOVW;
- p->scond = C_SCOND_LO;
- p->from.type = D_CONST;
- p->from.offset = (cursym->text->to.offset2 + 3) & ~3;
- p->to.type = D_REG;
- p->to.reg = 2;
-
- // MOVW.LO R14, R3
- p = appendp(p);
- p->as = AMOVW;
- p->scond = C_SCOND_LO;
- p->from.type = D_REG;
- p->from.reg = REGLINK;
+ p->from.offset = 2*PtrSize;
p->to.type = D_REG;
p->to.reg = 3;
-
- // BL.LO runtime.morestack(SB) // modifies LR
- p = appendp(p);
- p->as = ABL;
- p->scond = C_SCOND_LO;
- 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->from.type = D_REG;
- p->from.reg = REGLINK;
- p->to.type = D_OREG;
- p->to.offset = -autosize;
- p->to.reg = REGSP;
- p->spadj = autosize;
- } else { // > StackBig
- // MOVW $autosize, R1
- // MOVW $args, R2
- // MOVW R14, R3
- // BL runtime.morestack(SB) // modifies LR
- // MOVW.W R14,$-autosize(SP)
-
- // MOVW $autosize, R1
+
p = appendp(p);
- p->as = AMOVW;
+ p->as = AADD;
p->from.type = D_CONST;
p->from.offset = autosize;
p->to.type = D_REG;
- p->to.reg = 1;
-
- // MOVW $args, R2
- // also need to store the extra 4 bytes.
- p = appendp(p);
- p->as = AMOVW;
- p->from.type = D_CONST;
- p->from.offset = (cursym->text->to.offset2 + 3) & ~3;
- p->to.type = D_REG;
- p->to.reg = 2;
-
- // MOVW R14, R3
- p = appendp(p);
- p->as = AMOVW;
- p->from.type = D_REG;
- p->from.reg = REGLINK;
- p->to.type = D_REG;
p->to.reg = 3;
-
- // BL runtime.morestack(SB) // modifies LR
- p = appendp(p);
- p->as = ABL;
- 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->from.type = D_REG;
- p->from.reg = REGLINK;
+ p->from.reg = 3;
p->to.type = D_OREG;
- p->to.offset = -autosize;
- p->to.reg = REGSP;
- p->spadj = autosize;
+ p->to.reg = REGG;
+ p->to.offset = 2*PtrSize;
}
break;
@@ -343,12 +308,56 @@ noops(void)
if(!autosize) {
p->as = AB;
p->from = zprg.from;
- p->to.type = D_OREG;
- p->to.offset = 0;
- p->to.reg = REGLINK;
+ if(p->to.sym) { // retjmp
+ p->to.type = D_BRANCH;
+ p->cond = p->to.sym->text;
+ } else {
+ p->to.type = D_OREG;
+ p->to.offset = 0;
+ p->to.reg = REGLINK;
+ }
break;
}
}
+
+ if(cursym->text->reg & WRAPPER) {
+ int cond;
+
+ // Preserve original RET's cond, to allow RET.EQ
+ // in the implementation of reflect.call.
+ cond = p->scond;
+ p->scond = C_SCOND_NONE;
+
+ // g->panicwrap -= autosize;
+ // MOVW panicwrap_offset(g), R3
+ // SUB $autosize, R3
+ // MOVW R3 panicwrap_offset(g)
+ p->as = AMOVW;
+ p->from.type = D_OREG;
+ p->from.reg = REGG;
+ p->from.offset = 2*PtrSize;
+ p->to.type = D_REG;
+ p->to.reg = 3;
+ p = appendp(p);
+
+ p->as = ASUB;
+ p->from.type = D_CONST;
+ p->from.offset = autosize;
+ p->to.type = D_REG;
+ p->to.reg = 3;
+ p = appendp(p);
+
+ p->as = AMOVW;
+ p->from.type = D_REG;
+ p->from.reg = 3;
+ p->to.type = D_OREG;
+ p->to.reg = REGG;
+ p->to.offset = 2*PtrSize;
+ p = appendp(p);
+
+ p->scond = cond;
+ }
+
p->as = AMOVW;
p->scond |= C_PBIT;
p->from.type = D_OREG;
@@ -359,6 +368,17 @@ noops(void)
// If there are instructions following
// this ARET, they come from a branch
// with the same stackframe, so no spadj.
+
+ if(p->to.sym) { // retjmp
+ p->to.reg = REGLINK;
+ q2 = appendp(p);
+ q2->as = AB;
+ q2->to.type = D_BRANCH;
+ q2->to.sym = p->to.sym;
+ q2->cond = p->to.sym->text;
+ p->to.sym = nil;
+ p = q2;
+ }
break;
case AADD:
@@ -452,14 +472,27 @@ noops(void)
p->to.reg = REGSP;
p->spadj = -8;
- /* SUB $8,SP */
- q1->as = ASUB;
- q1->from.type = D_CONST;
- q1->from.offset = 8;
- q1->from.reg = NREG;
+ /* Keep saved LR at 0(SP) after SP change. */
+ /* MOVW 0(SP), REGTMP; MOVW REGTMP, -8!(SP) */
+ /* TODO: Remove SP adjustments; see issue 6699. */
+ q1->as = AMOVW;
+ q1->from.type = D_OREG;
+ q1->from.reg = REGSP;
+ q1->from.offset = 0;
q1->reg = NREG;
q1->to.type = D_REG;
+ q1->to.reg = REGTMP;
+
+ /* SUB $8,SP */
+ q1 = appendp(q1);
+ q1->as = AMOVW;
+ q1->from.type = D_REG;
+ q1->from.reg = REGTMP;
+ q1->reg = NREG;
+ q1->to.type = D_OREG;
q1->to.reg = REGSP;
+ q1->to.offset = -8;
+ q1->scond |= C_WBIT;
q1->spadj = 8;
break;
@@ -476,6 +509,143 @@ noops(void)
}
}
+static Prog*
+stacksplit(Prog *p, int32 framesize)
+{
+ int32 arg;
+
+ // MOVW g_stackguard(g), R1
+ p = appendp(p);
+ p->as = AMOVW;
+ p->from.type = D_OREG;
+ p->from.reg = REGG;
+ p->to.type = D_REG;
+ p->to.reg = 1;
+
+ if(framesize <= StackSmall) {
+ // small stack: SP < stackguard
+ // CMP stackguard, SP
+ p = appendp(p);
+ p->as = ACMP;
+ p->from.type = D_REG;
+ p->from.reg = 1;
+ p->reg = REGSP;
+ } else if(framesize <= StackBig) {
+ // large stack: SP-framesize < stackguard-StackSmall
+ // MOVW $-framesize(SP), R2
+ // CMP stackguard, R2
+ p = appendp(p);
+ p->as = AMOVW;
+ p->from.type = D_CONST;
+ p->from.reg = REGSP;
+ p->from.offset = -framesize;
+ p->to.type = D_REG;
+ p->to.reg = 2;
+
+ p = appendp(p);
+ p->as = ACMP;
+ p->from.type = D_REG;
+ p->from.reg = 1;
+ p->reg = 2;
+ } else {
+ // Such a large stack we need to protect against wraparound
+ // if SP is close to zero.
+ // SP-stackguard+StackGuard < framesize + (StackGuard-StackSmall)
+ // The +StackGuard on both sides is required to keep the left side positive:
+ // SP is allowed to be slightly below stackguard. See stack.h.
+ // CMP $StackPreempt, R1
+ // MOVW.NE $StackGuard(SP), R2
+ // SUB.NE R1, R2
+ // MOVW.NE $(framesize+(StackGuard-StackSmall)), R3
+ // CMP.NE R3, R2
+ p = appendp(p);
+ p->as = ACMP;
+ p->from.type = D_CONST;
+ p->from.offset = (uint32)StackPreempt;
+ p->reg = 1;
+
+ p = appendp(p);
+ p->as = AMOVW;
+ p->from.type = D_CONST;
+ p->from.reg = REGSP;
+ p->from.offset = StackGuard;
+ p->to.type = D_REG;
+ p->to.reg = 2;
+ p->scond = C_SCOND_NE;
+
+ p = appendp(p);
+ p->as = ASUB;
+ p->from.type = D_REG;
+ p->from.reg = 1;
+ p->to.type = D_REG;
+ p->to.reg = 2;
+ p->scond = C_SCOND_NE;
+
+ p = appendp(p);
+ p->as = AMOVW;
+ p->from.type = D_CONST;
+ p->from.offset = framesize + (StackGuard - StackSmall);
+ p->to.type = D_REG;
+ p->to.reg = 3;
+ p->scond = C_SCOND_NE;
+
+ p = appendp(p);
+ p->as = ACMP;
+ p->from.type = D_REG;
+ p->from.reg = 3;
+ p->reg = 2;
+ p->scond = C_SCOND_NE;
+ }
+
+ // MOVW.LS $framesize, R1
+ p = appendp(p);
+ p->as = AMOVW;
+ p->scond = C_SCOND_LS;
+ p->from.type = D_CONST;
+ p->from.offset = framesize;
+ p->to.type = D_REG;
+ p->to.reg = 1;
+
+ // MOVW.LS $args, R2
+ p = appendp(p);
+ p->as = AMOVW;
+ p->scond = C_SCOND_LS;
+ p->from.type = D_CONST;
+ arg = cursym->text->to.offset2;
+ if(arg == 1) // special marker for known 0
+ arg = 0;
+ if(arg&3)
+ diag("misaligned argument size in stack split");
+ p->from.offset = arg;
+ p->to.type = D_REG;
+ p->to.reg = 2;
+
+ // MOVW.LS R14, R3
+ p = appendp(p);
+ p->as = AMOVW;
+ p->scond = C_SCOND_LS;
+ p->from.type = D_REG;
+ p->from.reg = REGLINK;
+ p->to.type = D_REG;
+ p->to.reg = 3;
+
+ // BL.LS runtime.morestack(SB) // modifies LR, returns with LO still asserted
+ p = appendp(p);
+ p->as = ABL;
+ p->scond = C_SCOND_LS;
+ p->to.type = D_BRANCH;
+ p->to.sym = symmorestack;
+ p->cond = pmorestack;
+
+ // BLS start
+ p = appendp(p);
+ p->as = ABLS;
+ p->to.type = D_BRANCH;
+ p->cond = cursym->text->link;
+
+ return p;
+}
+
static void
sigdiv(char *n)
{
diff --git a/src/cmd/5l/obj.c b/src/cmd/5l/obj.c
index 24e6294a8..80f5787dc 100644
--- a/src/cmd/5l/obj.c
+++ b/src/cmd/5l/obj.c
@@ -81,8 +81,7 @@ main(int argc, char *argv[])
INITDAT = -1;
INITRND = -1;
INITENTRY = 0;
- LIBINITENTRY = 0;
- linkmode = LinkInternal; // TODO: LinkAuto once everything works.
+ linkmode = LinkAuto;
nuxiinit();
p = getgoarm();
@@ -118,6 +117,7 @@ main(int argc, char *argv[])
flagstr("extldflags", "flags for external linker", &extldflags);
flagcount("f", "ignore version mismatch", &debug['f']);
flagcount("g", "disable go package data checks", &debug['g']);
+ flagstr("installsuffix", "pkg directory suffix", &flag_installsuffix);
flagstr("k", "sym: set field tracking symbol", &tracksym);
flagfn1("linkmode", "mode: set link mode (internal, external, auto)", setlinkmode);
flagcount("n", "dump symbol table", &debug['n']);
@@ -126,34 +126,43 @@ main(int argc, char *argv[])
flagstr("r", "dir1:dir2:...: set ELF dynamic linker search path", &rpath);
flagcount("race", "enable race detector", &flag_race);
flagcount("s", "disable symbol table", &debug['s']);
+ flagcount("shared", "generate shared object (implies -linkmode external)", &flag_shared);
flagstr("tmpdir", "leave temporary files in this directory", &tmpdir);
flagcount("u", "reject unsafe packages", &debug['u']);
flagcount("v", "print link trace", &debug['v']);
flagcount("w", "disable DWARF generation", &debug['w']);
- flagcount("shared", "generate shared object", &flag_shared);
- // TODO: link mode flag
flagparse(&argc, &argv, usage);
if(argc != 1)
usage();
+ if(flag_shared)
+ linkmode = LinkExternal;
+
+ mywhatsys();
+
+ if(HEADTYPE == -1)
+ HEADTYPE = headtype(goos);
+
// getgoextlinkenabled is based on GO_EXTLINK_ENABLED when
// Go was built; see ../../make.bash.
if(linkmode == LinkAuto && strcmp(getgoextlinkenabled(), "0") == 0)
linkmode = LinkInternal;
- if(linkmode == LinkExternal) {
- diag("only -linkmode=internal is supported");
- errorexit();
- } else if(linkmode == LinkAuto) {
- linkmode = LinkInternal;
+ switch(HEADTYPE) {
+ default:
+ if(linkmode == LinkAuto)
+ linkmode = LinkInternal;
+ if(linkmode == LinkExternal && strcmp(getgoextlinkenabled(), "1") != 0)
+ sysfatal("cannot use -linkmode=external with -H %s", headstr(HEADTYPE));
+ break;
+ case Hlinux:
+ break;
}
libinit();
- if(HEADTYPE == -1)
- HEADTYPE = headtype(goos);
switch(HEADTYPE) {
default:
diag("unknown -H option");
@@ -208,7 +217,7 @@ main(int argc, char *argv[])
case Hnetbsd:
debug['d'] = 0; // with dynamic linking
tlsoffset = -8; // hardcoded number, first 4-byte word for g, and then 4-byte word for m
- // this number is known to ../../pkg/runtime/cgo/gcc_linux_arm.c
+ // this number is known to ../../pkg/runtime/rt0_*_arm.s
elfinit();
HEADR = ELFRESERVE;
if(INITTEXT == -1)
@@ -253,6 +262,7 @@ main(int argc, char *argv[])
// mark some functions that are only referenced after linker code editing
if(debug['F'])
mark(rlookup("_sfloat", 0));
+ mark(lookup("runtime.read_tls_fallback", 0));
deadcode();
if(textp == nil) {
diag("no code");
@@ -325,7 +335,7 @@ zaddr(char *pn, Biobuf *f, Adr *a, Sym *h[])
c = BGETC(f);
if(c < 0 || c > NSYM){
print("sym out of range: %d\n", c);
- Bputc(f, ALAST+1);
+ BPUTC(f, ALAST+1);
return;
}
a->sym = h[c];
@@ -334,7 +344,7 @@ zaddr(char *pn, Biobuf *f, Adr *a, Sym *h[])
if((schar)a->reg < 0 || a->reg > NREG) {
print("register out of range %d\n", a->reg);
- Bputc(f, ALAST+1);
+ BPUTC(f, ALAST+1);
return; /* force real diagnostic */
}
@@ -352,7 +362,7 @@ zaddr(char *pn, Biobuf *f, Adr *a, Sym *h[])
switch(a->type) {
default:
print("unknown type %d\n", a->type);
- Bputc(f, ALAST+1);
+ BPUTC(f, ALAST+1);
return; /* force real diagnostic */
case D_NONE:
@@ -368,13 +378,13 @@ zaddr(char *pn, Biobuf *f, Adr *a, Sym *h[])
break;
case D_CONST2:
- a->offset2 = Bget4(f); // fall through
+ a->offset2 = BGETLE4(f); // fall through
case D_BRANCH:
case D_OREG:
case D_CONST:
case D_OCONST:
case D_SHIFT:
- a->offset = Bget4(f);
+ a->offset = BGETLE4(f);
break;
case D_SCONST:
@@ -383,8 +393,8 @@ zaddr(char *pn, Biobuf *f, Adr *a, Sym *h[])
break;
case D_FCONST:
- a->ieee.l = Bget4(f);
- a->ieee.h = Bget4(f);
+ a->ieee.l = BGETLE4(f);
+ a->ieee.h = BGETLE4(f);
break;
}
s = a->sym;
@@ -467,7 +477,7 @@ loop:
if(o == ANAME || o == ASIGNAME) {
sig = 0;
if(o == ASIGNAME)
- sig = Bget4(f);
+ sig = BGETLE4(f);
v = BGETC(f); /* type */
o = BGETC(f); /* sym */
r = 0;
@@ -522,7 +532,7 @@ loop:
p->as = o;
p->scond = BGETC(f);
p->reg = BGETC(f);
- p->line = Bget4(f);
+ p->line = BGETLE4(f);
zaddr(pn, f, &p->from, h);
fromgotype = adrgotype;
@@ -549,6 +559,7 @@ loop:
addhist(p->line, D_FILE); /* 'z' */
if(p->to.offset)
addhist(p->to.offset, D_FILE1); /* 'Z' */
+ savehist(p->line, p->to.offset);
histfrogp = 0;
goto loop;
@@ -614,13 +625,6 @@ loop:
pc++;
break;
- case ALOCALS:
- if(skip)
- goto casedef;
- cursym->locals = p->to.offset;
- pc++;
- break;
-
case ATYPE:
if(skip)
goto casedef;
@@ -667,6 +671,7 @@ loop:
p->to.offset = autosize;
autosize += 4;
s->type = STEXT;
+ s->hist = gethist();
s->text = p;
s->value = pc;
s->args = p->to.offset2;
diff --git a/src/cmd/5l/optab.c b/src/cmd/5l/optab.c
index 231071f20..3d05d6d09 100644
--- a/src/cmd/5l/optab.c
+++ b/src/cmd/5l/optab.c
@@ -62,8 +62,8 @@ Optab optab[] =
{ ABEQ, C_NONE, C_NONE, C_SBRA, 5, 4, 0 },
{ AB, C_NONE, C_NONE, C_ROREG, 6, 4, 0, LPOOL },
- { ABL, C_NONE, C_NONE, C_ROREG, 7, 8, 0 },
- { ABL, C_REG, C_NONE, C_ROREG, 7, 8, 0 },
+ { ABL, C_NONE, C_NONE, C_ROREG, 7, 4, 0 },
+ { ABL, C_REG, C_NONE, C_ROREG, 7, 4, 0 },
{ ABX, C_NONE, C_NONE, C_ROREG, 75, 12, 0 },
{ ABXRET, C_NONE, C_NONE, C_ROREG, 76, 4, 0 },
@@ -94,9 +94,11 @@ Optab optab[] =
{ AMVN, C_LCON, C_NONE, C_REG, 13, 8, 0, LFROM },
{ ACMP, C_LCON, C_REG, C_NONE, 13, 8, 0, LFROM },
- { AMOVB, C_REG, C_NONE, C_REG, 14, 8, 0 },
+ { AMOVB, C_REG, C_NONE, C_REG, 1, 4, 0 },
+ { AMOVBS, C_REG, C_NONE, C_REG, 14, 8, 0 },
{ AMOVBU, C_REG, C_NONE, C_REG, 58, 4, 0 },
- { AMOVH, C_REG, C_NONE, C_REG, 14, 8, 0 },
+ { AMOVH, C_REG, C_NONE, C_REG, 1, 4, 0 },
+ { AMOVHS, C_REG, C_NONE, C_REG, 14, 8, 0 },
{ AMOVHU, C_REG, C_NONE, C_REG, 14, 8, 0 },
{ AMUL, C_REG, C_REG, C_REG, 15, 4, 0 },
@@ -112,6 +114,8 @@ Optab optab[] =
{ AMOVW, C_REG, C_NONE, C_SOREG, 20, 4, 0 },
{ AMOVB, C_REG, C_NONE, C_SAUTO, 20, 4, REGSP },
{ AMOVB, C_REG, C_NONE, C_SOREG, 20, 4, 0 },
+ { AMOVBS, C_REG, C_NONE, C_SAUTO, 20, 4, REGSP },
+ { AMOVBS, C_REG, C_NONE, C_SOREG, 20, 4, 0 },
{ AMOVBU, C_REG, C_NONE, C_SAUTO, 20, 4, REGSP },
{ AMOVBU, C_REG, C_NONE, C_SOREG, 20, 4, 0 },
@@ -126,6 +130,9 @@ Optab optab[] =
{ AMOVB, C_REG, C_NONE, C_LAUTO, 30, 8, REGSP, LTO },
{ AMOVB, C_REG, C_NONE, C_LOREG, 30, 8, 0, LTO },
{ AMOVB, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO | LPCREL, 4 },
+ { AMOVBS, C_REG, C_NONE, C_LAUTO, 30, 8, REGSP, LTO },
+ { AMOVBS, C_REG, C_NONE, C_LOREG, 30, 8, 0, LTO },
+ { AMOVBS, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO | LPCREL, 4 },
{ AMOVBU, C_REG, C_NONE, C_LAUTO, 30, 8, REGSP, LTO },
{ AMOVBU, C_REG, C_NONE, C_LOREG, 30, 8, 0, LTO },
{ AMOVBU, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO | LPCREL, 4 },
@@ -176,29 +183,40 @@ Optab optab[] =
{ AMOVBU, C_SHIFT,C_NONE, C_REG, 59, 4, 0 },
{ AMOVB, C_SHIFT,C_NONE, C_REG, 60, 4, 0 },
+ { AMOVBS, C_SHIFT,C_NONE, C_REG, 60, 4, 0 },
{ AMOVW, C_REG, C_NONE, C_SHIFT, 61, 4, 0 },
{ AMOVB, C_REG, C_NONE, C_SHIFT, 61, 4, 0 },
+ { AMOVBS, C_REG, C_NONE, C_SHIFT, 61, 4, 0 },
{ AMOVBU, C_REG, C_NONE, C_SHIFT, 61, 4, 0 },
{ ACASE, C_REG, C_NONE, C_NONE, 62, 4, 0, LPCREL, 8 },
- { ABCASE, C_NONE, C_NONE, C_SBRA, 63, 4, 0 },
+ { ABCASE, C_NONE, C_NONE, C_SBRA, 63, 4, 0, LPCREL, 0 },
{ AMOVH, C_REG, C_NONE, C_HAUTO, 70, 4, REGSP, 0 },
{ AMOVH, C_REG, C_NONE, C_HOREG, 70, 4, 0, 0 },
+ { AMOVHS, C_REG, C_NONE, C_HAUTO, 70, 4, REGSP, 0 },
+ { AMOVHS, C_REG, C_NONE, C_HOREG, 70, 4, 0, 0 },
{ AMOVHU, C_REG, C_NONE, C_HAUTO, 70, 4, REGSP, 0 },
{ AMOVHU, C_REG, C_NONE, C_HOREG, 70, 4, 0, 0 },
{ AMOVB, C_HAUTO,C_NONE, C_REG, 71, 4, REGSP, 0 },
{ AMOVB, C_HOREG,C_NONE, C_REG, 71, 4, 0, 0 },
+ { AMOVBS, C_HAUTO,C_NONE, C_REG, 71, 4, REGSP, 0 },
+ { AMOVBS, C_HOREG,C_NONE, C_REG, 71, 4, 0, 0 },
{ AMOVH, C_HAUTO,C_NONE, C_REG, 71, 4, REGSP, 0 },
{ AMOVH, C_HOREG,C_NONE, C_REG, 71, 4, 0, 0 },
+ { AMOVHS, C_HAUTO,C_NONE, C_REG, 71, 4, REGSP, 0 },
+ { AMOVHS, C_HOREG,C_NONE, C_REG, 71, 4, 0, 0 },
{ AMOVHU, C_HAUTO,C_NONE, C_REG, 71, 4, REGSP, 0 },
{ AMOVHU, C_HOREG,C_NONE, C_REG, 71, 4, 0, 0 },
{ AMOVH, C_REG, C_NONE, C_LAUTO, 72, 8, REGSP, LTO },
{ AMOVH, C_REG, C_NONE, C_LOREG, 72, 8, 0, LTO },
{ AMOVH, C_REG, C_NONE, C_ADDR, 94, 8, 0, LTO | LPCREL, 4 },
+ { AMOVHS, C_REG, C_NONE, C_LAUTO, 72, 8, REGSP, LTO },
+ { AMOVHS, C_REG, C_NONE, C_LOREG, 72, 8, 0, LTO },
+ { AMOVHS, C_REG, C_NONE, C_ADDR, 94, 8, 0, LTO | LPCREL, 4 },
{ AMOVHU, C_REG, C_NONE, C_LAUTO, 72, 8, REGSP, LTO },
{ AMOVHU, C_REG, C_NONE, C_LOREG, 72, 8, 0, LTO },
{ AMOVHU, C_REG, C_NONE, C_ADDR, 94, 8, 0, LTO | LPCREL, 4 },
@@ -206,9 +224,15 @@ Optab optab[] =
{ AMOVB, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM },
{ AMOVB, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM },
{ AMOVB, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM | LPCREL, 4 },
+ { AMOVBS, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM },
+ { AMOVBS, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM },
+ { AMOVBS, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM | LPCREL, 4 },
{ AMOVH, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM },
{ AMOVH, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM },
{ AMOVH, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM | LPCREL, 4 },
+ { AMOVHS, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM },
+ { AMOVHS, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM },
+ { AMOVHS, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM | LPCREL, 4 },
{ AMOVHU, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM },
{ AMOVHU, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM },
{ AMOVHU, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM | LPCREL, 4 },
@@ -246,6 +270,8 @@ Optab optab[] =
{ AMULAWT, C_REG, C_REG, C_REGREG2, 99, 4, 0 },
{ AUSEFIELD, C_ADDR, C_NONE, C_NONE, 0, 0, 0 },
+ { APCDATA, C_LCON, C_NONE, C_LCON, 0, 0, 0 },
+ { AFUNCDATA, C_LCON, C_NONE, C_ADDR, 0, 0, 0 },
{ AXXX, C_NONE, C_NONE, C_NONE, 0, 4, 0 },
};
diff --git a/src/cmd/5l/pass.c b/src/cmd/5l/pass.c
index c22b86085..cd8897989 100644
--- a/src/cmd/5l/pass.c
+++ b/src/cmd/5l/pass.c
@@ -130,7 +130,7 @@ loop:
r = prg();
*r = *p;
if(!(r->mark&FOLL))
- print("cant happen 1\n");
+ print("can't happen 1\n");
r->mark |= FOLL;
if(p != q) {
p = p->link;
@@ -150,7 +150,7 @@ loop:
if(!(r->link->mark&FOLL))
xfol(r->link, last);
if(!(r->cond->mark&FOLL))
- print("cant happen 2\n");
+ print("can't happen 2\n");
return;
}
}
@@ -246,6 +246,13 @@ patch(void)
p->cond = q;
}
}
+ if(flag_shared) {
+ s = lookup("init_array", 0);
+ s->type = SINITARR;
+ s->reachable = 1;
+ s->hide = 1;
+ addaddr(s, lookup(INITENTRY, 0));
+ }
for(cursym = textp; cursym != nil; cursym = cursym->next) {
for(p = cursym->text; p != P; p = p->link) {
diff --git a/src/cmd/5l/span.c b/src/cmd/5l/span.c
index a5afa02e7..e7cc0b4b1 100644
--- a/src/cmd/5l/span.c
+++ b/src/cmd/5l/span.c
@@ -90,7 +90,7 @@ span(void)
int32 c, otxt, out[6];
Section *sect;
uchar *bp;
- Sym *sub;
+ Sym *sub, *gmsym;
if(debug['v'])
Bprint(&bso, "%5.2f span\n", cputime());
@@ -141,7 +141,7 @@ span(void)
if(checkpool(op, p->as == ACASE ? casesz(p) : m))
c = p->pc = scan(op, p, c);
}
- if(m == 0) {
+ if(m == 0 && (p->as != AFUNCDATA && p->as != APCDATA)) {
diag("zero-width instruction\n%P", p);
continue;
}
@@ -211,7 +211,7 @@ span(void)
}
*/
m = o->size;
- if(m == 0) {
+ if(m == 0 && (p->as != AFUNCDATA && p->as != APCDATA)) {
if(p->as == ATEXT) {
autosize = p->to.offset + 4;
if(p->from.sym != S)
@@ -237,6 +237,9 @@ span(void)
* code references to be relocated too, and then
* perhaps we'd be able to parallelize the span loop above.
*/
+ gmsym = S;
+ if(linkmode == LinkExternal)
+ gmsym = lookup("runtime.tlsgm", 0);
for(cursym = textp; cursym != nil; cursym = cursym->next) {
p = cursym->text;
if(p == P || p->link == P)
@@ -249,7 +252,7 @@ span(void)
pc = p->pc;
curp = p;
o = oplook(p);
- asmout(p, o, out);
+ asmout(p, o, out, gmsym);
for(i=0; i<o->size/4; i++) {
v = out[i];
*bp++ = v;
@@ -574,10 +577,7 @@ aclass(Adr *a)
if(s == S)
break;
instoffset = 0; // s.b. unused but just in case
- if(flag_shared)
- return C_LCONADDR;
- else
- return C_LCON;
+ return C_LCONADDR;
case D_AUTO:
instoffset = autosize + a->offset;
@@ -813,8 +813,10 @@ buildop(void)
break;
case AMOVW:
case AMOVB:
+ case AMOVBS:
case AMOVBU:
case AMOVH:
+ case AMOVHS:
case AMOVHU:
break;
case ASWPW:
@@ -830,7 +832,6 @@ buildop(void)
case ARFE:
case ATEXT:
case AUSEFIELD:
- case ALOCALS:
case ACASE:
case ABCASE:
case ATYPE:
@@ -890,6 +891,8 @@ buildop(void)
case APLD:
case AUNDEF:
case ACLZ:
+ case AFUNCDATA:
+ case APCDATA:
break;
}
}
diff --git a/src/cmd/6a/a.y b/src/cmd/6a/a.y
index 42af65e35..ed72916b2 100644
--- a/src/cmd/6a/a.y
+++ b/src/cmd/6a/a.y
@@ -33,6 +33,7 @@
#include <stdio.h> /* if we don't, bison will, and a.h re-#defines getc */
#include <libc.h>
#include "a.h"
+#include "../../pkg/runtime/funcdata.h"
%}
%union {
Sym *sym;
@@ -49,8 +50,8 @@
%left '+' '-'
%left '*' '/' '%'
%token <lval> LTYPE0 LTYPE1 LTYPE2 LTYPE3 LTYPE4
-%token <lval> LTYPEC LTYPED LTYPEN LTYPER LTYPET LTYPEG
-%token <lval> LTYPES LTYPEM LTYPEI LTYPEXC LTYPEX LTYPERT
+%token <lval> LTYPEC LTYPED LTYPEN LTYPER LTYPET LTYPEG LTYPEPC
+%token <lval> LTYPES LTYPEM LTYPEI LTYPEXC LTYPEX LTYPERT LTYPEF
%token <lval> LCONST LFP LPC LSB
%token <lval> LBREG LLREG LSREG LFREG LMREG LXREG
%token <dval> LFCONST
@@ -58,8 +59,9 @@
%token <sym> LNAME LLAB LVAR
%type <lval> con con2 expr pointer offset
%type <gen> mem imm imm2 reg nam rel rem rim rom omem nmem
-%type <gen2> nonnon nonrel nonrem rimnon rimrem remrim spec10 spec11
+%type <gen2> nonnon nonrel nonrem rimnon rimrem remrim
%type <gen2> spec1 spec2 spec3 spec4 spec5 spec6 spec7 spec8 spec9
+%type <gen2> spec10 spec11 spec12 spec13
%%
prog:
| prog
@@ -115,6 +117,8 @@ inst:
| LTYPEX spec9 { outcode($1, &$2); }
| LTYPERT spec10 { outcode($1, &$2); }
| LTYPEG spec11 { outcode($1, &$2); }
+| LTYPEPC spec12 { outcode($1, &$2); }
+| LTYPEF spec13 { outcode($1, &$2); }
nonnon:
{
@@ -308,6 +312,26 @@ spec11: /* GLOBL */
$$.to = $5;
}
+spec12: /* PCDATA */
+ rim ',' rim
+ {
+ if($1.type != D_CONST || $3.type != D_CONST)
+ yyerror("arguments to PCDATA must be integer constants");
+ $$.from = $1;
+ $$.to = $3;
+ }
+
+spec13: /* FUNCDATA */
+ rim ',' rim
+ {
+ if($1.type != D_CONST)
+ yyerror("index for FUNCDATA must be integer constant");
+ if($3.type != D_EXTERN && $3.type != D_STATIC)
+ yyerror("value for FUNCDATA must be symbol reference");
+ $$.from = $1;
+ $$.to = $3;
+ }
+
rem:
reg
| mem
@@ -494,6 +518,15 @@ omem:
$$.scale = $8;
checkscale($$.scale);
}
+| con '(' LLREG ')' '(' LSREG '*' con ')'
+ {
+ $$ = nullgen;
+ $$.type = D_INDIR+$3;
+ $$.offset = $1;
+ $$.index = $6;
+ $$.scale = $8;
+ checkscale($$.scale);
+ }
| '(' LLREG ')'
{
$$ = nullgen;
@@ -597,11 +630,13 @@ con:
con2:
LCONST
{
- $$ = $1 & 0xffffffffLL;
+ $$ = ($1 & 0xffffffffLL) +
+ ((vlong)ArgsSizeUnknown << 32);
}
| '-' LCONST
{
- $$ = -$2 & 0xffffffffLL;
+ $$ = (-$2 & 0xffffffffLL) +
+ ((vlong)ArgsSizeUnknown << 32);
}
| LCONST '-' LCONST
{
diff --git a/src/cmd/6a/doc.go b/src/cmd/6a/doc.go
index a5f3f87f0..9f14cc0d0 100644
--- a/src/cmd/6a/doc.go
+++ b/src/cmd/6a/doc.go
@@ -10,6 +10,10 @@
http://plan9.bell-labs.com/magic/man2html/1/8a
+Go-specific considerations are documented at
+
+ http://golang.org/doc/asm
+
Its target architecture is the x86-64, referred to by these tools as amd64.
*/
diff --git a/src/cmd/6a/lex.c b/src/cmd/6a/lex.c
index c969e98e5..ab34e8220 100644
--- a/src/cmd/6a/lex.c
+++ b/src/cmd/6a/lex.c
@@ -74,7 +74,7 @@ main(int argc, char *argv[])
ARGBEGIN {
default:
c = ARGC();
- if(c >= 0 || c < sizeof(debug))
+ if(c >= 0 && c < sizeof(debug))
debug[c] = 1;
break;
@@ -1019,7 +1019,9 @@ struct
"AESKEYGENASSIST", LTYPEX, AAESKEYGENASSIST,
"PSHUFD", LTYPEX, APSHUFD,
"USEFIELD", LTYPEN, AUSEFIELD,
-
+ "PCLMULQDQ", LTYPEX, APCLMULQDQ,
+ "PCDATA", LTYPEPC, APCDATA,
+ "FUNCDATA", LTYPEF, AFUNCDATA,
0
};
@@ -1099,15 +1101,14 @@ void
zname(char *n, int t, int s)
{
- Bputc(&obuf, ANAME); /* as(2) */
- Bputc(&obuf, ANAME>>8);
- Bputc(&obuf, t); /* type */
- Bputc(&obuf, s); /* sym */
+ BPUTLE2(&obuf, ANAME); /* as(2) */
+ BPUTC(&obuf, t); /* type */
+ BPUTC(&obuf, s); /* sym */
while(*n) {
- Bputc(&obuf, *n);
+ BPUTC(&obuf, *n);
n++;
}
- Bputc(&obuf, 0);
+ BPUTC(&obuf, 0);
}
void
@@ -1143,52 +1144,40 @@ zaddr(Gen *a, int s)
case D_NONE:
break;
}
- Bputc(&obuf, t);
+ BPUTC(&obuf, t);
if(t & T_INDEX) { /* implies index, scale */
- Bputc(&obuf, a->index);
- Bputc(&obuf, a->scale);
+ BPUTC(&obuf, a->index);
+ BPUTC(&obuf, a->scale);
}
if(t & T_OFFSET) { /* implies offset */
l = a->offset;
- Bputc(&obuf, l);
- Bputc(&obuf, l>>8);
- Bputc(&obuf, l>>16);
- Bputc(&obuf, l>>24);
+ BPUTLE4(&obuf, l);
if(t & T_64) {
l = a->offset>>32;
- Bputc(&obuf, l);
- Bputc(&obuf, l>>8);
- Bputc(&obuf, l>>16);
- Bputc(&obuf, l>>24);
+ BPUTLE4(&obuf, l);
}
}
if(t & T_SYM) /* implies sym */
- Bputc(&obuf, s);
+ BPUTC(&obuf, s);
if(t & T_FCONST) {
ieeedtod(&e, a->dval);
l = e.l;
- Bputc(&obuf, l);
- Bputc(&obuf, l>>8);
- Bputc(&obuf, l>>16);
- Bputc(&obuf, l>>24);
+ BPUTLE4(&obuf, l);
l = e.h;
- Bputc(&obuf, l);
- Bputc(&obuf, l>>8);
- Bputc(&obuf, l>>16);
- Bputc(&obuf, l>>24);
+ BPUTLE4(&obuf, l);
return;
}
if(t & T_SCONST) {
n = a->sval;
for(i=0; i<NSNAME; i++) {
- Bputc(&obuf, *n);
+ BPUTC(&obuf, *n);
n++;
}
return;
}
if(t & T_TYPE)
- Bputc(&obuf, a->type);
+ BPUTC(&obuf, a->type);
}
void
@@ -1247,12 +1236,8 @@ jackpot:
goto jackpot;
break;
}
- Bputc(&obuf, a);
- Bputc(&obuf, a>>8);
- Bputc(&obuf, stmtline);
- Bputc(&obuf, stmtline>>8);
- Bputc(&obuf, stmtline>>16);
- Bputc(&obuf, stmtline>>24);
+ BPUTLE2(&obuf, a);
+ BPUTLE4(&obuf, stmtline);
zaddr(&g2->from, sf);
zaddr(&g2->to, st);
@@ -1327,13 +1312,12 @@ outhist(void)
q = 0;
}
if(n) {
- Bputc(&obuf, ANAME);
- Bputc(&obuf, ANAME>>8);
- Bputc(&obuf, D_FILE); /* type */
- Bputc(&obuf, 1); /* sym */
- Bputc(&obuf, '<');
+ BPUTLE2(&obuf, ANAME);
+ BPUTC(&obuf, D_FILE); /* type */
+ BPUTC(&obuf, 1); /* sym */
+ BPUTC(&obuf, '<');
Bwrite(&obuf, p, n);
- Bputc(&obuf, 0);
+ BPUTC(&obuf, 0);
}
p = q;
if(p == 0 && op) {
@@ -1343,12 +1327,8 @@ outhist(void)
}
g.offset = h->offset;
- Bputc(&obuf, AHISTORY);
- Bputc(&obuf, AHISTORY>>8);
- Bputc(&obuf, h->line);
- Bputc(&obuf, h->line>>8);
- Bputc(&obuf, h->line>>16);
- Bputc(&obuf, h->line>>24);
+ BPUTLE2(&obuf, AHISTORY);
+ BPUTLE4(&obuf, h->line);
zaddr(&nullgen, 0);
zaddr(&g, 0);
diff --git a/src/cmd/6a/y.tab.c b/src/cmd/6a/y.tab.c
index 75c4ad5ea..3e5058b9d 100644
--- a/src/cmd/6a/y.tab.c
+++ b/src/cmd/6a/y.tab.c
@@ -1,24 +1,21 @@
-/* A Bison parser, made by GNU Bison 2.3. */
+/* A Bison parser, made by GNU Bison 2.5. */
-/* Skeleton implementation for Bison's Yacc-like parsers in C
-
- Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
- Free Software Foundation, Inc.
-
- This program is free software; you can redistribute it and/or modify
+/* Bison implementation for Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
+
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* As a special exception, you may create a larger work that contains
part or all of the Bison parser skeleton and distribute that work
@@ -29,7 +26,7 @@
special exception, which will cause the skeleton and the resulting
Bison output files to be licensed under the GNU General Public
License without this special exception.
-
+
This special exception was added by the Free Software Foundation in
version 2.2 of Bison. */
@@ -47,7 +44,7 @@
#define YYBISON 1
/* Bison version. */
-#define YYBISON_VERSION "2.3"
+#define YYBISON_VERSION "2.5"
/* Skeleton name. */
#define YYSKELETON_NAME "yacc.c"
@@ -55,11 +52,51 @@
/* Pure parsers. */
#define YYPURE 0
+/* Push parsers. */
+#define YYPUSH 0
+
+/* Pull parsers. */
+#define YYPULL 1
+
/* Using locations. */
#define YYLSP_NEEDED 0
+/* Copy the first part of user declarations. */
+
+/* Line 268 of yacc.c */
+#line 31 "a.y"
+
+#include <u.h>
+#include <stdio.h> /* if we don't, bison will, and a.h re-#defines getc */
+#include <libc.h>
+#include "a.h"
+#include "../../pkg/runtime/funcdata.h"
+
+
+/* Line 268 of yacc.c */
+#line 80 "y.tab.c"
+
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+/* Enabling the token table. */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
+
/* Tokens. */
#ifndef YYTOKENTYPE
# define YYTOKENTYPE
@@ -77,28 +114,30 @@
LTYPER = 266,
LTYPET = 267,
LTYPEG = 268,
- LTYPES = 269,
- LTYPEM = 270,
- LTYPEI = 271,
- LTYPEXC = 272,
- LTYPEX = 273,
- LTYPERT = 274,
- LCONST = 275,
- LFP = 276,
- LPC = 277,
- LSB = 278,
- LBREG = 279,
- LLREG = 280,
- LSREG = 281,
- LFREG = 282,
- LMREG = 283,
- LXREG = 284,
- LFCONST = 285,
- LSCONST = 286,
- LSP = 287,
- LNAME = 288,
- LLAB = 289,
- LVAR = 290
+ LTYPEPC = 269,
+ LTYPES = 270,
+ LTYPEM = 271,
+ LTYPEI = 272,
+ LTYPEXC = 273,
+ LTYPEX = 274,
+ LTYPERT = 275,
+ LTYPEF = 276,
+ LCONST = 277,
+ LFP = 278,
+ LPC = 279,
+ LSB = 280,
+ LBREG = 281,
+ LLREG = 282,
+ LSREG = 283,
+ LFREG = 284,
+ LMREG = 285,
+ LXREG = 286,
+ LFCONST = 287,
+ LSCONST = 288,
+ LSP = 289,
+ LNAME = 290,
+ LLAB = 291,
+ LVAR = 292
};
#endif
/* Tokens. */
@@ -113,85 +152,64 @@
#define LTYPER 266
#define LTYPET 267
#define LTYPEG 268
-#define LTYPES 269
-#define LTYPEM 270
-#define LTYPEI 271
-#define LTYPEXC 272
-#define LTYPEX 273
-#define LTYPERT 274
-#define LCONST 275
-#define LFP 276
-#define LPC 277
-#define LSB 278
-#define LBREG 279
-#define LLREG 280
-#define LSREG 281
-#define LFREG 282
-#define LMREG 283
-#define LXREG 284
-#define LFCONST 285
-#define LSCONST 286
-#define LSP 287
-#define LNAME 288
-#define LLAB 289
-#define LVAR 290
-
-
-
-
-/* Copy the first part of user declarations. */
-#line 31 "a.y"
-
-#include <u.h>
-#include <stdio.h> /* if we don't, bison will, and a.h re-#defines getc */
-#include <libc.h>
-#include "a.h"
-
+#define LTYPEPC 269
+#define LTYPES 270
+#define LTYPEM 271
+#define LTYPEI 272
+#define LTYPEXC 273
+#define LTYPEX 274
+#define LTYPERT 275
+#define LTYPEF 276
+#define LCONST 277
+#define LFP 278
+#define LPC 279
+#define LSB 280
+#define LBREG 281
+#define LLREG 282
+#define LSREG 283
+#define LFREG 284
+#define LMREG 285
+#define LXREG 286
+#define LFCONST 287
+#define LSCONST 288
+#define LSP 289
+#define LNAME 290
+#define LLAB 291
+#define LVAR 292
-/* Enabling traces. */
-#ifndef YYDEBUG
-# define YYDEBUG 0
-#endif
-/* Enabling verbose error messages. */
-#ifdef YYERROR_VERBOSE
-# undef YYERROR_VERBOSE
-# define YYERROR_VERBOSE 1
-#else
-# define YYERROR_VERBOSE 0
-#endif
-/* Enabling the token table. */
-#ifndef YYTOKEN_TABLE
-# define YYTOKEN_TABLE 0
-#endif
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
typedef union YYSTYPE
-#line 37 "a.y"
{
+
+/* Line 293 of yacc.c */
+#line 38 "a.y"
+
Sym *sym;
vlong lval;
double dval;
char sval[8];
Gen gen;
Gen2 gen2;
-}
-/* Line 193 of yacc.c. */
-#line 182 "y.tab.c"
- YYSTYPE;
+
+
+
+/* Line 293 of yacc.c */
+#line 201 "y.tab.c"
+} YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
# define YYSTYPE_IS_DECLARED 1
-# define YYSTYPE_IS_TRIVIAL 1
#endif
-
/* Copy the second part of user declarations. */
-/* Line 216 of yacc.c. */
-#line 195 "y.tab.c"
+/* Line 343 of yacc.c */
+#line 213 "y.tab.c"
#ifdef short
# undef short
@@ -266,14 +284,14 @@ typedef short int yytype_int16;
#if (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
static int
-YYID (int i)
+YYID (int yyi)
#else
static int
-YYID (i)
- int i;
+YYID (yyi)
+ int yyi;
#endif
{
- return i;
+ return yyi;
}
#endif
@@ -294,11 +312,11 @@ YYID (i)
# define alloca _alloca
# else
# define YYSTACK_ALLOC alloca
-# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
-# ifndef _STDLIB_H
-# define _STDLIB_H 1
+# ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
# endif
# endif
# endif
@@ -321,24 +339,24 @@ YYID (i)
# ifndef YYSTACK_ALLOC_MAXIMUM
# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
# endif
-# if (defined __cplusplus && ! defined _STDLIB_H \
+# if (defined __cplusplus && ! defined EXIT_SUCCESS \
&& ! ((defined YYMALLOC || defined malloc) \
&& (defined YYFREE || defined free)))
# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
-# ifndef _STDLIB_H
-# define _STDLIB_H 1
+# ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
# endif
# endif
# ifndef YYMALLOC
# define YYMALLOC malloc
-# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+# if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
# endif
# endif
# ifndef YYFREE
# define YYFREE free
-# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+# if ! defined free && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
void free (void *); /* INFRINGES ON USER NAME SPACE */
# endif
@@ -354,9 +372,9 @@ void free (void *); /* INFRINGES ON USER NAME SPACE */
/* A type that is properly aligned for any stack member. */
union yyalloc
{
- yytype_int16 yyss;
- YYSTYPE yyvs;
- };
+ yytype_int16 yyss_alloc;
+ YYSTYPE yyvs_alloc;
+};
/* The size of the maximum gap between one aligned stack and the next. */
# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
@@ -367,6 +385,27 @@ union yyalloc
((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+ YYSTACK_GAP_MAXIMUM)
+# define YYCOPY_NEEDED 1
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack_alloc, Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \
+ Stack = &yyptr->Stack_alloc; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (YYID (0))
+
+#endif
+
+#if defined YYCOPY_NEEDED && YYCOPY_NEEDED
/* Copy COUNT objects from FROM to TO. The source and destination do
not overlap. */
# ifndef YYCOPY
@@ -384,42 +423,25 @@ union yyalloc
while (YYID (0))
# endif
# endif
-
-/* Relocate STACK from its old location to the new one. The
- local variables YYSIZE and YYSTACKSIZE give the old and new number of
- elements in the stack, and YYPTR gives the new location of the
- stack. Advance YYPTR to a properly aligned location for the next
- stack. */
-# define YYSTACK_RELOCATE(Stack) \
- do \
- { \
- YYSIZE_T yynewbytes; \
- YYCOPY (&yyptr->Stack, Stack, yysize); \
- Stack = &yyptr->Stack; \
- yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
- yyptr += yynewbytes / sizeof (*yyptr); \
- } \
- while (YYID (0))
-
-#endif
+#endif /* !YYCOPY_NEEDED */
/* YYFINAL -- State number of the termination state. */
#define YYFINAL 2
/* YYLAST -- Last index in YYTABLE. */
-#define YYLAST 554
+#define YYLAST 560
/* YYNTOKENS -- Number of terminals. */
-#define YYNTOKENS 54
+#define YYNTOKENS 56
/* YYNNTS -- Number of nonterminals. */
-#define YYNNTS 40
+#define YYNNTS 42
/* YYNRULES -- Number of rules. */
-#define YYNRULES 132
+#define YYNRULES 137
/* YYNRULES -- Number of states. */
-#define YYNSTATES 263
+#define YYNSTATES 277
/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
#define YYUNDEFTOK 2
-#define YYMAXUTOK 290
+#define YYMAXUTOK 292
#define YYTRANSLATE(YYX) \
((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
@@ -430,16 +452,16 @@ static const yytype_uint8 yytranslate[] =
0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 52, 12, 5, 2,
- 50, 51, 10, 8, 49, 9, 2, 11, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 46, 47,
- 6, 48, 7, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 54, 12, 5, 2,
+ 52, 53, 10, 8, 51, 9, 2, 11, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 48, 49,
+ 6, 50, 7, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 4, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 3, 2, 53, 2, 2, 2,
+ 2, 2, 2, 2, 3, 2, 55, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
@@ -456,7 +478,7 @@ static const yytype_uint8 yytranslate[] =
15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
- 45
+ 45, 46, 47
};
#if YYDEBUG
@@ -467,86 +489,88 @@ static const yytype_uint16 yyprhs[] =
0, 0, 3, 4, 5, 9, 10, 15, 16, 21,
23, 26, 29, 33, 37, 40, 43, 46, 49, 52,
55, 58, 61, 64, 67, 70, 73, 76, 79, 82,
- 85, 88, 89, 91, 95, 99, 102, 104, 107, 109,
- 112, 114, 118, 124, 128, 134, 137, 139, 141, 143,
- 147, 153, 157, 163, 166, 168, 172, 178, 184, 185,
- 187, 191, 197, 199, 201, 203, 205, 208, 211, 213,
- 215, 217, 219, 224, 227, 230, 232, 234, 236, 238,
- 240, 242, 244, 247, 250, 253, 256, 259, 264, 270,
- 274, 276, 278, 280, 285, 290, 295, 302, 312, 316,
- 320, 326, 335, 337, 344, 350, 358, 359, 362, 365,
- 367, 369, 371, 373, 375, 378, 381, 384, 388, 390,
- 393, 397, 402, 404, 408, 412, 416, 420, 424, 429,
- 434, 438, 442
+ 85, 88, 91, 94, 95, 97, 101, 105, 108, 110,
+ 113, 115, 118, 120, 124, 130, 134, 140, 143, 145,
+ 147, 149, 153, 159, 163, 169, 172, 174, 178, 184,
+ 190, 191, 193, 197, 203, 207, 211, 213, 215, 217,
+ 219, 222, 225, 227, 229, 231, 233, 238, 241, 244,
+ 246, 248, 250, 252, 254, 256, 258, 261, 264, 267,
+ 270, 273, 278, 284, 288, 290, 292, 294, 299, 304,
+ 309, 316, 326, 336, 340, 344, 350, 359, 361, 368,
+ 374, 382, 383, 386, 389, 391, 393, 395, 397, 399,
+ 402, 405, 408, 412, 414, 417, 421, 426, 428, 432,
+ 436, 440, 444, 448, 453, 458, 462, 466
};
/* YYRHS -- A `-1'-separated list of the rules' RHS. */
static const yytype_int8 yyrhs[] =
{
- 55, 0, -1, -1, -1, 55, 56, 57, -1, -1,
- 44, 46, 58, 57, -1, -1, 43, 46, 59, 57,
- -1, 47, -1, 60, 47, -1, 1, 47, -1, 43,
- 48, 93, -1, 45, 48, 93, -1, 13, 61, -1,
- 14, 65, -1, 15, 64, -1, 16, 62, -1, 17,
- 63, -1, 21, 66, -1, 19, 67, -1, 22, 68,
- -1, 18, 69, -1, 20, 70, -1, 24, 71, -1,
- 25, 72, -1, 26, 73, -1, 27, 74, -1, 28,
- 75, -1, 29, 76, -1, 23, 77, -1, -1, 49,
- -1, 80, 49, 78, -1, 78, 49, 80, -1, 80,
- 49, -1, 80, -1, 49, 78, -1, 78, -1, 49,
- 81, -1, 81, -1, 84, 49, 81, -1, 88, 11,
- 91, 49, 84, -1, 85, 49, 83, -1, 85, 49,
- 91, 49, 83, -1, 49, 79, -1, 79, -1, 61,
- -1, 65, -1, 80, 49, 78, -1, 80, 49, 78,
- 46, 35, -1, 80, 49, 78, -1, 80, 49, 78,
- 46, 36, -1, 80, 49, -1, 80, -1, 80, 49,
- 78, -1, 82, 49, 78, 49, 91, -1, 84, 49,
- 78, 49, 82, -1, -1, 84, -1, 85, 49, 84,
- -1, 85, 49, 91, 49, 84, -1, 82, -1, 85,
- -1, 81, -1, 87, -1, 10, 82, -1, 10, 86,
- -1, 82, -1, 86, -1, 78, -1, 84, -1, 91,
- 50, 32, 51, -1, 43, 89, -1, 44, 89, -1,
- 34, -1, 37, -1, 35, -1, 38, -1, 42, -1,
- 36, -1, 39, -1, 52, 92, -1, 52, 91, -1,
- 52, 88, -1, 52, 41, -1, 52, 40, -1, 52,
- 50, 40, 51, -1, 52, 50, 9, 40, 51, -1,
- 52, 9, 40, -1, 86, -1, 87, -1, 91, -1,
- 91, 50, 35, 51, -1, 91, 50, 42, 51, -1,
- 91, 50, 36, 51, -1, 91, 50, 35, 10, 91,
- 51, -1, 91, 50, 35, 51, 50, 35, 10, 91,
- 51, -1, 50, 35, 51, -1, 50, 42, 51, -1,
- 50, 35, 10, 91, 51, -1, 50, 35, 51, 50,
- 35, 10, 91, 51, -1, 88, -1, 88, 50, 35,
- 10, 91, 51, -1, 43, 89, 50, 90, 51, -1,
- 43, 6, 7, 89, 50, 33, 51, -1, -1, 8,
- 91, -1, 9, 91, -1, 33, -1, 42, -1, 31,
- -1, 30, -1, 45, -1, 9, 91, -1, 8, 91,
- -1, 53, 91, -1, 50, 93, 51, -1, 30, -1,
- 9, 30, -1, 30, 9, 30, -1, 9, 30, 9,
- 30, -1, 91, -1, 93, 8, 93, -1, 93, 9,
- 93, -1, 93, 10, 93, -1, 93, 11, 93, -1,
- 93, 12, 93, -1, 93, 6, 6, 93, -1, 93,
- 7, 7, 93, -1, 93, 5, 93, -1, 93, 4,
- 93, -1, 93, 3, 93, -1
+ 57, 0, -1, -1, -1, 57, 58, 59, -1, -1,
+ 46, 48, 60, 59, -1, -1, 45, 48, 61, 59,
+ -1, 49, -1, 62, 49, -1, 1, 49, -1, 45,
+ 50, 97, -1, 47, 50, 97, -1, 13, 63, -1,
+ 14, 67, -1, 15, 66, -1, 16, 64, -1, 17,
+ 65, -1, 21, 68, -1, 19, 69, -1, 22, 70,
+ -1, 18, 71, -1, 20, 72, -1, 25, 73, -1,
+ 26, 74, -1, 27, 75, -1, 28, 76, -1, 29,
+ 77, -1, 30, 78, -1, 23, 79, -1, 24, 80,
+ -1, 31, 81, -1, -1, 51, -1, 84, 51, 82,
+ -1, 82, 51, 84, -1, 84, 51, -1, 84, -1,
+ 51, 82, -1, 82, -1, 51, 85, -1, 85, -1,
+ 88, 51, 85, -1, 92, 11, 95, 51, 88, -1,
+ 89, 51, 87, -1, 89, 51, 95, 51, 87, -1,
+ 51, 83, -1, 83, -1, 63, -1, 67, -1, 84,
+ 51, 82, -1, 84, 51, 82, 48, 37, -1, 84,
+ 51, 82, -1, 84, 51, 82, 48, 38, -1, 84,
+ 51, -1, 84, -1, 84, 51, 82, -1, 86, 51,
+ 82, 51, 95, -1, 88, 51, 82, 51, 86, -1,
+ -1, 88, -1, 89, 51, 88, -1, 89, 51, 95,
+ 51, 88, -1, 84, 51, 84, -1, 84, 51, 84,
+ -1, 86, -1, 89, -1, 85, -1, 91, -1, 10,
+ 86, -1, 10, 90, -1, 86, -1, 90, -1, 82,
+ -1, 88, -1, 95, 52, 34, 53, -1, 45, 93,
+ -1, 46, 93, -1, 36, -1, 39, -1, 37, -1,
+ 40, -1, 44, -1, 38, -1, 41, -1, 54, 96,
+ -1, 54, 95, -1, 54, 92, -1, 54, 43, -1,
+ 54, 42, -1, 54, 52, 42, 53, -1, 54, 52,
+ 9, 42, 53, -1, 54, 9, 42, -1, 90, -1,
+ 91, -1, 95, -1, 95, 52, 37, 53, -1, 95,
+ 52, 44, 53, -1, 95, 52, 38, 53, -1, 95,
+ 52, 37, 10, 95, 53, -1, 95, 52, 37, 53,
+ 52, 37, 10, 95, 53, -1, 95, 52, 37, 53,
+ 52, 38, 10, 95, 53, -1, 52, 37, 53, -1,
+ 52, 44, 53, -1, 52, 37, 10, 95, 53, -1,
+ 52, 37, 53, 52, 37, 10, 95, 53, -1, 92,
+ -1, 92, 52, 37, 10, 95, 53, -1, 45, 93,
+ 52, 94, 53, -1, 45, 6, 7, 93, 52, 35,
+ 53, -1, -1, 8, 95, -1, 9, 95, -1, 35,
+ -1, 44, -1, 33, -1, 32, -1, 47, -1, 9,
+ 95, -1, 8, 95, -1, 55, 95, -1, 52, 97,
+ 53, -1, 32, -1, 9, 32, -1, 32, 9, 32,
+ -1, 9, 32, 9, 32, -1, 95, -1, 97, 8,
+ 97, -1, 97, 9, 97, -1, 97, 10, 97, -1,
+ 97, 11, 97, -1, 97, 12, 97, -1, 97, 6,
+ 6, 97, -1, 97, 7, 7, 97, -1, 97, 5,
+ 97, -1, 97, 4, 97, -1, 97, 3, 97, -1
};
/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
static const yytype_uint16 yyrline[] =
{
- 0, 64, 64, 66, 65, 73, 72, 80, 79, 85,
- 86, 87, 90, 95, 101, 102, 103, 104, 105, 106,
- 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,
- 117, 120, 124, 131, 138, 145, 150, 157, 162, 169,
- 174, 179, 186, 194, 199, 207, 212, 219, 220, 223,
- 228, 238, 243, 253, 258, 263, 270, 278, 288, 292,
- 299, 304, 312, 313, 316, 317, 318, 322, 326, 327,
- 330, 331, 334, 340, 349, 358, 363, 368, 373, 378,
- 383, 388, 394, 402, 408, 419, 425, 431, 437, 443,
- 451, 452, 455, 461, 467, 473, 479, 488, 497, 502,
- 507, 515, 525, 529, 538, 545, 554, 557, 561, 567,
- 568, 572, 575, 576, 580, 584, 588, 592, 598, 602,
- 606, 611, 618, 619, 623, 627, 631, 635, 639, 643,
- 647, 651, 655
+ 0, 66, 66, 68, 67, 75, 74, 82, 81, 87,
+ 88, 89, 92, 97, 103, 104, 105, 106, 107, 108,
+ 109, 110, 111, 112, 113, 114, 115, 116, 117, 118,
+ 119, 120, 121, 124, 128, 135, 142, 149, 154, 161,
+ 166, 173, 178, 183, 190, 198, 203, 211, 216, 223,
+ 224, 227, 232, 242, 247, 257, 262, 267, 274, 282,
+ 292, 296, 303, 308, 316, 325, 336, 337, 340, 341,
+ 342, 346, 350, 351, 354, 355, 358, 364, 373, 382,
+ 387, 392, 397, 402, 407, 412, 418, 426, 432, 443,
+ 449, 455, 461, 467, 475, 476, 479, 485, 491, 497,
+ 503, 512, 521, 530, 535, 540, 548, 558, 562, 571,
+ 578, 587, 590, 594, 600, 601, 605, 608, 609, 613,
+ 617, 621, 625, 631, 636, 641, 646, 653, 654, 658,
+ 662, 666, 670, 674, 678, 682, 686, 690
};
#endif
@@ -558,15 +582,16 @@ static const char *const yytname[] =
"$end", "error", "$undefined", "'|'", "'^'", "'&'", "'<'", "'>'", "'+'",
"'-'", "'*'", "'/'", "'%'", "LTYPE0", "LTYPE1", "LTYPE2", "LTYPE3",
"LTYPE4", "LTYPEC", "LTYPED", "LTYPEN", "LTYPER", "LTYPET", "LTYPEG",
- "LTYPES", "LTYPEM", "LTYPEI", "LTYPEXC", "LTYPEX", "LTYPERT", "LCONST",
- "LFP", "LPC", "LSB", "LBREG", "LLREG", "LSREG", "LFREG", "LMREG",
- "LXREG", "LFCONST", "LSCONST", "LSP", "LNAME", "LLAB", "LVAR", "':'",
- "';'", "'='", "','", "'('", "')'", "'$'", "'~'", "$accept", "prog", "@1",
- "line", "@2", "@3", "inst", "nonnon", "rimrem", "remrim", "rimnon",
- "nonrem", "nonrel", "spec1", "spec2", "spec3", "spec4", "spec5", "spec6",
- "spec7", "spec8", "spec9", "spec10", "spec11", "rem", "rom", "rim",
- "rel", "reg", "imm2", "imm", "mem", "omem", "nmem", "nam", "offset",
- "pointer", "con", "con2", "expr", 0
+ "LTYPEPC", "LTYPES", "LTYPEM", "LTYPEI", "LTYPEXC", "LTYPEX", "LTYPERT",
+ "LTYPEF", "LCONST", "LFP", "LPC", "LSB", "LBREG", "LLREG", "LSREG",
+ "LFREG", "LMREG", "LXREG", "LFCONST", "LSCONST", "LSP", "LNAME", "LLAB",
+ "LVAR", "':'", "';'", "'='", "','", "'('", "')'", "'$'", "'~'",
+ "$accept", "prog", "$@1", "line", "$@2", "$@3", "inst", "nonnon",
+ "rimrem", "remrim", "rimnon", "nonrem", "nonrel", "spec1", "spec2",
+ "spec3", "spec4", "spec5", "spec6", "spec7", "spec8", "spec9", "spec10",
+ "spec11", "spec12", "spec13", "rem", "rom", "rim", "rel", "reg", "imm2",
+ "imm", "mem", "omem", "nmem", "nam", "offset", "pointer", "con", "con2",
+ "expr", 0
};
#endif
@@ -579,28 +604,28 @@ static const yytype_uint16 yytoknum[] =
42, 47, 37, 258, 259, 260, 261, 262, 263, 264,
265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
275, 276, 277, 278, 279, 280, 281, 282, 283, 284,
- 285, 286, 287, 288, 289, 290, 58, 59, 61, 44,
- 40, 41, 36, 126
+ 285, 286, 287, 288, 289, 290, 291, 292, 58, 59,
+ 61, 44, 40, 41, 36, 126
};
# endif
/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
static const yytype_uint8 yyr1[] =
{
- 0, 54, 55, 56, 55, 58, 57, 59, 57, 57,
- 57, 57, 60, 60, 60, 60, 60, 60, 60, 60,
- 60, 60, 60, 60, 60, 60, 60, 60, 60, 60,
- 60, 61, 61, 62, 63, 64, 64, 65, 65, 66,
- 66, 66, 67, 68, 68, 69, 69, 70, 70, 71,
- 71, 72, 72, 73, 73, 73, 74, 75, 76, 76,
- 77, 77, 78, 78, 79, 79, 79, 79, 79, 79,
- 80, 80, 81, 81, 81, 82, 82, 82, 82, 82,
- 82, 82, 83, 84, 84, 84, 84, 84, 84, 84,
- 85, 85, 86, 86, 86, 86, 86, 86, 86, 86,
- 86, 86, 87, 87, 88, 88, 89, 89, 89, 90,
- 90, 90, 91, 91, 91, 91, 91, 91, 92, 92,
- 92, 92, 93, 93, 93, 93, 93, 93, 93, 93,
- 93, 93, 93
+ 0, 56, 57, 58, 57, 60, 59, 61, 59, 59,
+ 59, 59, 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 63, 63, 64, 65, 66, 66, 67,
+ 67, 68, 68, 68, 69, 70, 70, 71, 71, 72,
+ 72, 73, 73, 74, 74, 75, 75, 75, 76, 77,
+ 78, 78, 79, 79, 80, 81, 82, 82, 83, 83,
+ 83, 83, 83, 83, 84, 84, 85, 85, 85, 86,
+ 86, 86, 86, 86, 86, 86, 87, 88, 88, 88,
+ 88, 88, 88, 88, 89, 89, 90, 90, 90, 90,
+ 90, 90, 90, 90, 90, 90, 90, 91, 91, 92,
+ 92, 93, 93, 93, 94, 94, 94, 95, 95, 95,
+ 95, 95, 95, 96, 96, 96, 96, 97, 97, 97,
+ 97, 97, 97, 97, 97, 97, 97, 97
};
/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
@@ -609,261 +634,273 @@ static const yytype_uint8 yyr2[] =
0, 2, 0, 0, 3, 0, 4, 0, 4, 1,
2, 2, 3, 3, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 0, 1, 3, 3, 2, 1, 2, 1, 2,
- 1, 3, 5, 3, 5, 2, 1, 1, 1, 3,
- 5, 3, 5, 2, 1, 3, 5, 5, 0, 1,
- 3, 5, 1, 1, 1, 1, 2, 2, 1, 1,
- 1, 1, 4, 2, 2, 1, 1, 1, 1, 1,
- 1, 1, 2, 2, 2, 2, 2, 4, 5, 3,
- 1, 1, 1, 4, 4, 4, 6, 9, 3, 3,
- 5, 8, 1, 6, 5, 7, 0, 2, 2, 1,
- 1, 1, 1, 1, 2, 2, 2, 3, 1, 2,
- 3, 4, 1, 3, 3, 3, 3, 3, 4, 4,
- 3, 3, 3
+ 2, 2, 2, 0, 1, 3, 3, 2, 1, 2,
+ 1, 2, 1, 3, 5, 3, 5, 2, 1, 1,
+ 1, 3, 5, 3, 5, 2, 1, 3, 5, 5,
+ 0, 1, 3, 5, 3, 3, 1, 1, 1, 1,
+ 2, 2, 1, 1, 1, 1, 4, 2, 2, 1,
+ 1, 1, 1, 1, 1, 1, 2, 2, 2, 2,
+ 2, 4, 5, 3, 1, 1, 1, 4, 4, 4,
+ 6, 9, 9, 3, 3, 5, 8, 1, 6, 5,
+ 7, 0, 2, 2, 1, 1, 1, 1, 1, 2,
+ 2, 2, 3, 1, 2, 3, 4, 1, 3, 3,
+ 3, 3, 3, 4, 4, 3, 3, 3
};
-/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
- STATE-NUM when YYTABLE doesn't specify something else to do. Zero
+/* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM.
+ Performed when YYTABLE doesn't specify something else to do. Zero
means the default is an error. */
static const yytype_uint8 yydefact[] =
{
- 2, 3, 1, 0, 0, 31, 0, 0, 0, 0,
- 0, 0, 31, 0, 0, 0, 0, 0, 0, 0,
- 0, 58, 0, 0, 0, 9, 4, 0, 11, 32,
- 14, 0, 0, 112, 75, 77, 80, 76, 78, 81,
- 79, 106, 113, 0, 0, 0, 15, 38, 62, 63,
- 90, 91, 102, 92, 0, 16, 70, 36, 71, 17,
- 0, 18, 0, 0, 106, 106, 0, 22, 46, 64,
- 68, 69, 65, 92, 20, 0, 32, 47, 48, 23,
- 106, 0, 0, 19, 40, 0, 0, 21, 0, 30,
- 0, 24, 0, 25, 0, 26, 54, 27, 0, 28,
- 0, 29, 59, 7, 0, 5, 0, 10, 115, 114,
- 0, 0, 0, 0, 37, 0, 0, 122, 0, 116,
- 0, 0, 0, 86, 85, 0, 84, 83, 35, 0,
- 0, 66, 67, 73, 74, 45, 0, 0, 73, 39,
- 0, 0, 0, 0, 0, 0, 53, 0, 0, 0,
- 12, 0, 13, 106, 107, 108, 0, 0, 98, 99,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 117, 0, 0, 0, 0, 89, 0, 0, 33, 34,
- 0, 0, 41, 0, 43, 0, 60, 0, 49, 51,
- 55, 0, 0, 8, 6, 0, 111, 109, 110, 0,
- 0, 0, 132, 131, 130, 0, 0, 123, 124, 125,
- 126, 127, 0, 0, 93, 95, 94, 0, 87, 72,
- 0, 0, 118, 82, 0, 0, 0, 0, 0, 0,
- 0, 104, 100, 0, 128, 129, 0, 0, 0, 88,
- 42, 119, 0, 44, 61, 50, 52, 56, 57, 0,
- 0, 103, 96, 0, 0, 120, 105, 0, 0, 121,
- 101, 0, 97
+ 2, 3, 1, 0, 0, 33, 0, 0, 0, 0,
+ 0, 0, 33, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 60, 0, 0, 0, 0, 9, 4, 0,
+ 11, 34, 14, 0, 0, 117, 79, 81, 84, 80,
+ 82, 85, 83, 111, 118, 0, 0, 0, 15, 40,
+ 66, 67, 94, 95, 107, 96, 0, 16, 74, 38,
+ 75, 17, 0, 18, 0, 0, 111, 111, 0, 22,
+ 48, 68, 72, 73, 69, 96, 20, 0, 34, 49,
+ 50, 23, 111, 0, 0, 19, 42, 0, 0, 21,
+ 0, 30, 0, 31, 0, 24, 0, 25, 0, 26,
+ 56, 27, 0, 28, 0, 29, 61, 32, 0, 7,
+ 0, 5, 0, 10, 120, 119, 0, 0, 0, 0,
+ 39, 0, 0, 127, 0, 121, 0, 0, 0, 90,
+ 89, 0, 88, 87, 37, 0, 0, 70, 71, 77,
+ 78, 47, 0, 0, 77, 41, 0, 0, 0, 0,
+ 0, 0, 0, 55, 0, 0, 0, 0, 12, 0,
+ 13, 111, 112, 113, 0, 0, 103, 104, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 122, 0,
+ 0, 0, 0, 93, 0, 0, 35, 36, 0, 0,
+ 43, 0, 45, 0, 62, 0, 64, 51, 53, 57,
+ 0, 0, 65, 8, 6, 0, 116, 114, 115, 0,
+ 0, 0, 137, 136, 135, 0, 0, 128, 129, 130,
+ 131, 132, 0, 0, 97, 99, 98, 0, 91, 76,
+ 0, 0, 123, 86, 0, 0, 0, 0, 0, 0,
+ 0, 109, 105, 0, 133, 134, 0, 0, 0, 92,
+ 44, 124, 0, 46, 63, 52, 54, 58, 59, 0,
+ 0, 108, 100, 0, 0, 0, 125, 110, 0, 0,
+ 0, 126, 106, 0, 0, 101, 102
};
/* YYDEFGOTO[NTERM-NUM]. */
static const yytype_int16 yydefgoto[] =
{
- -1, 1, 3, 26, 151, 149, 27, 30, 59, 61,
- 55, 46, 83, 74, 87, 67, 79, 91, 93, 95,
- 97, 99, 101, 89, 56, 68, 57, 69, 48, 184,
- 58, 49, 50, 51, 52, 113, 199, 53, 223, 118
+ -1, 1, 3, 28, 159, 157, 29, 32, 61, 63,
+ 57, 48, 85, 76, 89, 69, 81, 95, 97, 99,
+ 101, 103, 105, 91, 93, 107, 58, 70, 59, 71,
+ 50, 192, 60, 51, 52, 53, 54, 119, 209, 55,
+ 233, 124
};
/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
STATE-NUM. */
-#define YYPACT_NINF -97
+#define YYPACT_NINF -94
static const yytype_int16 yypact[] =
{
- -97, 40, -97, 208, 5, -4, 140, 295, 295, 343,
- 233, 15, 319, 381, 91, 91, 295, 295, 295, 222,
- 24, 24, -15, 22, 14, -97, -97, 30, -97, -97,
- -97, 486, 486, -97, -97, -97, -97, -97, -97, -97,
- -97, 42, -97, 343, 406, 486, -97, -97, -97, -97,
- -97, -97, 45, 48, 399, -97, -97, 20, -97, -97,
- 67, -97, 68, 367, 42, 19, 271, -97, -97, -97,
- -97, -97, -97, 87, -97, 127, 343, -97, -97, -97,
- 19, 437, 486, -97, -97, 90, 92, -97, 94, -97,
- 96, -97, 104, -97, 105, -97, 111, -97, 118, -97,
- 119, -97, -97, -97, 486, -97, 486, -97, -97, -97,
- 133, 486, 486, 121, -97, 8, 122, -97, 80, -97,
- 134, 78, 413, -97, -97, 446, -97, -97, -97, 343,
- 295, -97, -97, 121, -97, -97, 7, 486, -97, -97,
- 437, 148, 453, 462, 343, 343, 343, 343, 343, 208,
- 284, 208, 284, 19, -97, -97, -1, 486, 131, -97,
- 486, 486, 486, 166, 177, 486, 486, 486, 486, 486,
- -97, 176, 10, 136, 137, -97, 480, 141, -97, -97,
- 143, 142, -97, 16, -97, 149, -97, 150, 151, 154,
- -97, 152, 155, -97, -97, 156, -97, -97, -97, 157,
- 159, 170, 99, 535, 542, 486, 486, 26, 26, -97,
- -97, -97, 486, 486, 161, -97, -97, 162, -97, -97,
- 24, 184, 198, -97, 163, 24, 181, 183, 486, 222,
- 205, -97, -97, 229, 114, 114, 193, 194, 211, -97,
- -97, 238, 219, -97, -97, -97, -97, -97, -97, 199,
- 486, -97, -97, 244, 232, -97, -97, 214, 486, -97,
- -97, 215, -97
+ -94, 15, -94, 218, -28, -25, 264, 285, 285, 340,
+ 163, 2, 319, 97, 415, 415, 285, 285, 285, 285,
+ 306, -24, -24, 285, -17, -14, 4, -94, -94, 48,
+ -94, -94, -94, 481, 481, -94, -94, -94, -94, -94,
+ -94, -94, -94, 19, -94, 340, 399, 481, -94, -94,
+ -94, -94, -94, -94, 46, 47, 385, -94, -94, 52,
+ -94, -94, 59, -94, 60, 374, 19, 56, 243, -94,
+ -94, -94, -94, -94, -94, 63, -94, 106, 340, -94,
+ -94, -94, 56, 138, 481, -94, -94, 69, 72, -94,
+ 74, -94, 76, -94, 77, -94, 79, -94, 80, -94,
+ 81, -94, 83, -94, 89, -94, -94, -94, 94, -94,
+ 481, -94, 481, -94, -94, -94, 119, 481, 481, 98,
+ -94, -1, 100, -94, 84, -94, 117, 23, 426, -94,
+ -94, 433, -94, -94, -94, 340, 285, -94, -94, 98,
+ -94, -94, 75, 481, -94, -94, 138, 122, 440, 444,
+ 285, 340, 340, 340, 340, 340, 285, 218, 393, 218,
+ 393, 56, -94, -94, -15, 481, 105, -94, 481, 481,
+ 481, 156, 162, 481, 481, 481, 481, 481, -94, 165,
+ 0, 123, 133, -94, 474, 134, -94, -94, 136, 140,
+ -94, 7, -94, 141, -94, 143, -94, 148, 149, -94,
+ 147, 160, -94, -94, -94, 164, -94, -94, -94, 167,
+ 168, 180, 533, 541, 548, 481, 481, 58, 58, -94,
+ -94, -94, 481, 481, 171, -94, -94, 172, -94, -94,
+ -24, 192, 217, -94, 175, -24, 219, 216, 481, 306,
+ 220, -94, -94, 247, 33, 33, 205, 208, 41, -94,
+ -94, 253, 234, -94, -94, -94, -94, -94, -94, 215,
+ 481, -94, -94, 259, 260, 239, -94, -94, 221, 481,
+ 481, -94, -94, 223, 224, -94, -94
};
/* YYPGOTO[NTERM-NUM]. */
static const yytype_int16 yypgoto[] =
{
- -97, -97, -97, -96, -97, -97, -97, 261, -97, -97,
- -97, 262, -97, -97, -97, -97, -97, -97, -97, -97,
- -97, -97, -97, -97, 17, 218, -2, -11, -9, 61,
- -8, 51, 1, -3, -7, -56, -97, -10, -97, -87
+ -94, -94, -94, -43, -94, -94, -94, 266, -94, -94,
+ -94, 273, -94, -94, -94, -94, -94, -94, -94, -94,
+ -94, -94, -94, -94, -94, -94, 26, 229, 32, -11,
+ -9, 57, -8, 71, -2, -6, 1, -60, -94, -10,
+ -94, -93
};
/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
positive, shift that token. If negative, reduce the rule which
- number is the opposite. If zero, do what YYDEFACT says.
- If YYTABLE_NINF, syntax error. */
+ number is the opposite. If YYTABLE_NINF, syntax error. */
#define YYTABLE_NINF -1
static const yytype_uint16 yytable[] =
{
- 73, 70, 84, 86, 75, 85, 60, 72, 133, 134,
- 98, 71, 100, 102, 92, 94, 96, 150, 157, 152,
- 213, 108, 109, 47, 138, 221, 62, 111, 112, 47,
- 196, 103, 197, 104, 117, 119, 167, 168, 169, 180,
- 2, 198, 172, 173, 127, 29, 222, 126, 110, 174,
- 111, 112, 28, 193, 131, 194, 73, 70, 41, 158,
- 114, 214, 106, 72, 132, 88, 90, 71, 105, 128,
- 139, 86, 117, 202, 203, 204, 54, 107, 207, 208,
- 209, 210, 211, 160, 161, 162, 163, 164, 165, 166,
- 167, 168, 169, 114, 117, 120, 117, 195, 121, 31,
- 32, 154, 155, 161, 162, 163, 164, 165, 166, 167,
- 168, 169, 109, 172, 173, 117, 129, 130, 234, 235,
- 174, 33, 165, 166, 167, 168, 169, 181, 179, 182,
- 86, 170, 185, 187, 41, 186, 42, 136, 137, 140,
- 153, 44, 141, 142, 45, 143, 178, 200, 31, 32,
- 117, 117, 117, 144, 145, 117, 117, 117, 117, 117,
- 146, 188, 189, 190, 191, 192, 109, 147, 148, 171,
- 33, 156, 205, 159, 34, 35, 36, 37, 38, 39,
- 180, 201, 40, 41, 206, 42, 212, 215, 216, 43,
- 44, 220, 218, 45, 219, 117, 117, 226, 224, 225,
- 227, 228, 236, 237, 229, 233, 230, 242, 231, 4,
- 232, 238, 240, 239, 241, 183, 245, 244, 247, 246,
- 248, 5, 6, 7, 8, 9, 10, 11, 12, 13,
- 14, 15, 16, 17, 18, 19, 20, 21, 249, 250,
- 257, 31, 32, 63, 251, 252, 253, 254, 261, 255,
- 256, 22, 23, 24, 258, 25, 34, 35, 36, 37,
- 38, 39, 259, 33, 40, 260, 262, 34, 35, 36,
- 37, 38, 39, 77, 78, 40, 64, 65, 42, 31,
- 32, 63, 66, 44, 135, 243, 45, 160, 161, 162,
- 163, 164, 165, 166, 167, 168, 169, 0, 0, 0,
- 0, 33, 0, 31, 32, 34, 35, 36, 37, 38,
- 39, 0, 0, 40, 64, 65, 42, 0, 0, 0,
- 0, 44, 0, 0, 45, 33, 0, 31, 32, 34,
- 35, 36, 37, 38, 39, 0, 0, 40, 41, 0,
- 42, 0, 0, 0, 0, 44, 0, 54, 45, 33,
- 0, 31, 32, 34, 35, 36, 37, 38, 39, 0,
- 0, 40, 41, 0, 42, 0, 0, 0, 76, 44,
- 0, 0, 45, 33, 0, 31, 32, 34, 35, 36,
- 37, 38, 39, 0, 0, 40, 41, 0, 42, 31,
- 32, 0, 0, 44, 0, 0, 45, 33, 0, 0,
- 0, 34, 35, 36, 37, 38, 39, 31, 122, 40,
- 0, 33, 42, 0, 31, 32, 0, 44, 0, 0,
- 45, 31, 32, 0, 80, 65, 42, 0, 0, 33,
- 81, 82, 0, 54, 45, 0, 33, 0, 0, 123,
- 124, 115, 41, 33, 42, 31, 32, 0, 116, 125,
- 0, 42, 45, 175, 31, 176, 82, 0, 42, 45,
- 0, 31, 32, 82, 0, 0, 45, 33, 0, 0,
- 31, 32, 0, 0, 0, 0, 33, 0, 0, 0,
- 80, 65, 42, 33, 0, 0, 177, 82, 31, 32,
- 45, 42, 33, 0, 31, 32, 82, 0, 42, 45,
- 0, 0, 0, 82, 0, 183, 45, 42, 0, 0,
- 33, 0, 82, 0, 54, 45, 33, 0, 0, 0,
- 217, 0, 0, 0, 0, 42, 0, 0, 0, 0,
- 82, 42, 0, 45, 0, 0, 82, 0, 0, 45,
- 162, 163, 164, 165, 166, 167, 168, 169, 163, 164,
- 165, 166, 167, 168, 169
+ 75, 72, 86, 88, 74, 87, 139, 140, 73, 165,
+ 223, 102, 77, 104, 106, 2, 231, 158, 206, 160,
+ 207, 30, 144, 114, 115, 116, 31, 117, 118, 208,
+ 56, 109, 49, 110, 111, 64, 123, 125, 49, 232,
+ 62, 173, 174, 175, 176, 177, 133, 43, 94, 96,
+ 98, 100, 166, 224, 112, 108, 137, 132, 75, 72,
+ 180, 181, 74, 138, 117, 118, 73, 182, 175, 176,
+ 177, 120, 145, 88, 123, 212, 213, 214, 263, 264,
+ 217, 218, 219, 220, 221, 90, 92, 168, 169, 170,
+ 171, 172, 173, 174, 175, 176, 177, 113, 126, 127,
+ 123, 205, 123, 134, 120, 33, 34, 162, 163, 188,
+ 135, 136, 180, 181, 203, 142, 204, 143, 115, 182,
+ 146, 123, 244, 245, 147, 148, 161, 149, 150, 35,
+ 151, 152, 153, 189, 154, 190, 88, 178, 193, 195,
+ 155, 194, 82, 67, 44, 156, 33, 34, 83, 84,
+ 164, 56, 47, 167, 179, 210, 188, 211, 123, 123,
+ 123, 186, 215, 123, 123, 123, 123, 123, 187, 216,
+ 35, 33, 34, 65, 115, 222, 225, 197, 198, 199,
+ 200, 201, 196, 82, 67, 44, 226, 228, 202, 229,
+ 84, 230, 234, 47, 235, 35, 236, 237, 238, 36,
+ 37, 38, 39, 40, 41, 123, 123, 42, 66, 67,
+ 44, 239, 246, 247, 68, 46, 240, 243, 47, 4,
+ 241, 242, 250, 248, 251, 249, 252, 254, 257, 191,
+ 258, 5, 6, 7, 8, 9, 10, 11, 12, 13,
+ 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+ 268, 33, 34, 65, 256, 259, 255, 260, 261, 273,
+ 274, 262, 265, 24, 25, 26, 266, 27, 267, 269,
+ 270, 271, 33, 34, 272, 35, 275, 276, 79, 36,
+ 37, 38, 39, 40, 41, 80, 0, 42, 66, 67,
+ 44, 253, 0, 33, 34, 46, 35, 141, 47, 0,
+ 36, 37, 38, 39, 40, 41, 0, 0, 42, 43,
+ 0, 44, 0, 0, 0, 45, 46, 35, 0, 47,
+ 0, 36, 37, 38, 39, 40, 41, 33, 34, 42,
+ 43, 0, 44, 0, 0, 0, 0, 46, 0, 56,
+ 47, 0, 36, 37, 38, 39, 40, 41, 33, 34,
+ 42, 35, 0, 0, 0, 36, 37, 38, 39, 40,
+ 41, 0, 0, 42, 43, 0, 44, 0, 0, 0,
+ 78, 46, 35, 0, 47, 0, 36, 37, 38, 39,
+ 40, 41, 33, 34, 42, 43, 0, 44, 0, 0,
+ 0, 0, 46, 33, 128, 47, 168, 169, 170, 171,
+ 172, 173, 174, 175, 176, 177, 35, 33, 34, 0,
+ 36, 37, 38, 39, 40, 41, 0, 35, 42, 0,
+ 0, 44, 0, 33, 34, 0, 46, 129, 130, 47,
+ 43, 35, 44, 0, 33, 34, 121, 131, 0, 0,
+ 47, 33, 184, 122, 0, 0, 44, 35, 33, 34,
+ 0, 84, 33, 34, 47, 0, 0, 0, 35, 0,
+ 43, 0, 44, 0, 0, 35, 0, 46, 183, 0,
+ 47, 0, 35, 44, 0, 185, 35, 0, 84, 0,
+ 44, 47, 33, 34, 0, 84, 0, 44, 47, 33,
+ 34, 44, 84, 0, 191, 47, 84, 0, 56, 47,
+ 0, 0, 0, 0, 0, 0, 35, 0, 0, 0,
+ 0, 0, 0, 35, 0, 0, 227, 0, 0, 0,
+ 0, 44, 0, 0, 0, 0, 84, 0, 44, 47,
+ 0, 0, 0, 84, 0, 0, 47, 169, 170, 171,
+ 172, 173, 174, 175, 176, 177, 170, 171, 172, 173,
+ 174, 175, 176, 177, 171, 172, 173, 174, 175, 176,
+ 177
};
+#define yypact_value_is_default(yystate) \
+ ((yystate) == (-94))
+
+#define yytable_value_is_error(yytable_value) \
+ YYID (0)
+
static const yytype_int16 yycheck[] =
{
- 10, 10, 13, 13, 11, 13, 8, 10, 64, 65,
- 19, 10, 20, 21, 16, 17, 18, 104, 10, 106,
- 10, 31, 32, 6, 80, 9, 9, 8, 9, 12,
- 31, 46, 33, 48, 44, 45, 10, 11, 12, 32,
- 0, 42, 35, 36, 54, 49, 30, 54, 6, 42,
- 8, 9, 47, 149, 63, 151, 66, 66, 43, 51,
- 43, 51, 48, 66, 63, 14, 15, 66, 46, 49,
- 81, 81, 82, 160, 161, 162, 52, 47, 165, 166,
- 167, 168, 169, 3, 4, 5, 6, 7, 8, 9,
- 10, 11, 12, 76, 104, 50, 106, 153, 50, 8,
- 9, 111, 112, 4, 5, 6, 7, 8, 9, 10,
- 11, 12, 122, 35, 36, 125, 49, 49, 205, 206,
- 42, 30, 8, 9, 10, 11, 12, 137, 130, 140,
- 140, 51, 142, 143, 43, 143, 45, 50, 11, 49,
- 7, 50, 50, 49, 53, 49, 129, 157, 8, 9,
- 160, 161, 162, 49, 49, 165, 166, 167, 168, 169,
- 49, 144, 145, 146, 147, 148, 176, 49, 49, 35,
- 30, 50, 6, 51, 34, 35, 36, 37, 38, 39,
- 32, 50, 42, 43, 7, 45, 10, 51, 51, 49,
- 50, 49, 51, 53, 51, 205, 206, 46, 49, 49,
- 46, 49, 212, 213, 49, 35, 50, 9, 51, 1,
- 51, 50, 220, 51, 30, 52, 35, 225, 228, 36,
- 229, 13, 14, 15, 16, 17, 18, 19, 20, 21,
- 22, 23, 24, 25, 26, 27, 28, 29, 33, 10,
- 250, 8, 9, 10, 51, 51, 35, 9, 258, 30,
- 51, 43, 44, 45, 10, 47, 34, 35, 36, 37,
- 38, 39, 30, 30, 42, 51, 51, 34, 35, 36,
- 37, 38, 39, 12, 12, 42, 43, 44, 45, 8,
- 9, 10, 49, 50, 66, 224, 53, 3, 4, 5,
- 6, 7, 8, 9, 10, 11, 12, -1, -1, -1,
- -1, 30, -1, 8, 9, 34, 35, 36, 37, 38,
- 39, -1, -1, 42, 43, 44, 45, -1, -1, -1,
- -1, 50, -1, -1, 53, 30, -1, 8, 9, 34,
- 35, 36, 37, 38, 39, -1, -1, 42, 43, -1,
- 45, -1, -1, -1, -1, 50, -1, 52, 53, 30,
- -1, 8, 9, 34, 35, 36, 37, 38, 39, -1,
- -1, 42, 43, -1, 45, -1, -1, -1, 49, 50,
- -1, -1, 53, 30, -1, 8, 9, 34, 35, 36,
- 37, 38, 39, -1, -1, 42, 43, -1, 45, 8,
- 9, -1, -1, 50, -1, -1, 53, 30, -1, -1,
- -1, 34, 35, 36, 37, 38, 39, 8, 9, 42,
- -1, 30, 45, -1, 8, 9, -1, 50, -1, -1,
- 53, 8, 9, -1, 43, 44, 45, -1, -1, 30,
- 49, 50, -1, 52, 53, -1, 30, -1, -1, 40,
- 41, 35, 43, 30, 45, 8, 9, -1, 42, 50,
- -1, 45, 53, 40, 8, 9, 50, -1, 45, 53,
- -1, 8, 9, 50, -1, -1, 53, 30, -1, -1,
- 8, 9, -1, -1, -1, -1, 30, -1, -1, -1,
- 43, 44, 45, 30, -1, -1, 40, 50, 8, 9,
- 53, 45, 30, -1, 8, 9, 50, -1, 45, 53,
- -1, -1, -1, 50, -1, 52, 53, 45, -1, -1,
- 30, -1, 50, -1, 52, 53, 30, -1, -1, -1,
- 40, -1, -1, -1, -1, 45, -1, -1, -1, -1,
- 50, 45, -1, 53, -1, -1, 50, -1, -1, 53,
- 5, 6, 7, 8, 9, 10, 11, 12, 6, 7,
- 8, 9, 10, 11, 12
+ 10, 10, 13, 13, 10, 13, 66, 67, 10, 10,
+ 10, 20, 11, 21, 22, 0, 9, 110, 33, 112,
+ 35, 49, 82, 33, 34, 6, 51, 8, 9, 44,
+ 54, 48, 6, 50, 48, 9, 46, 47, 12, 32,
+ 8, 8, 9, 10, 11, 12, 56, 45, 16, 17,
+ 18, 19, 53, 53, 50, 23, 65, 56, 68, 68,
+ 37, 38, 68, 65, 8, 9, 68, 44, 10, 11,
+ 12, 45, 83, 83, 84, 168, 169, 170, 37, 38,
+ 173, 174, 175, 176, 177, 14, 15, 3, 4, 5,
+ 6, 7, 8, 9, 10, 11, 12, 49, 52, 52,
+ 110, 161, 112, 51, 78, 8, 9, 117, 118, 34,
+ 51, 51, 37, 38, 157, 52, 159, 11, 128, 44,
+ 51, 131, 215, 216, 52, 51, 7, 51, 51, 32,
+ 51, 51, 51, 143, 51, 146, 146, 53, 148, 149,
+ 51, 149, 45, 46, 47, 51, 8, 9, 51, 52,
+ 52, 54, 55, 53, 37, 165, 34, 52, 168, 169,
+ 170, 135, 6, 173, 174, 175, 176, 177, 136, 7,
+ 32, 8, 9, 10, 184, 10, 53, 151, 152, 153,
+ 154, 155, 150, 45, 46, 47, 53, 53, 156, 53,
+ 52, 51, 51, 55, 51, 32, 48, 48, 51, 36,
+ 37, 38, 39, 40, 41, 215, 216, 44, 45, 46,
+ 47, 51, 222, 223, 51, 52, 52, 37, 55, 1,
+ 53, 53, 230, 52, 32, 53, 9, 235, 238, 54,
+ 239, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 260, 8, 9, 10, 38, 35, 37, 10, 53, 269,
+ 270, 53, 9, 45, 46, 47, 32, 49, 53, 10,
+ 10, 32, 8, 9, 53, 32, 53, 53, 12, 36,
+ 37, 38, 39, 40, 41, 12, -1, 44, 45, 46,
+ 47, 234, -1, 8, 9, 52, 32, 68, 55, -1,
+ 36, 37, 38, 39, 40, 41, -1, -1, 44, 45,
+ -1, 47, -1, -1, -1, 51, 52, 32, -1, 55,
+ -1, 36, 37, 38, 39, 40, 41, 8, 9, 44,
+ 45, -1, 47, -1, -1, -1, -1, 52, -1, 54,
+ 55, -1, 36, 37, 38, 39, 40, 41, 8, 9,
+ 44, 32, -1, -1, -1, 36, 37, 38, 39, 40,
+ 41, -1, -1, 44, 45, -1, 47, -1, -1, -1,
+ 51, 52, 32, -1, 55, -1, 36, 37, 38, 39,
+ 40, 41, 8, 9, 44, 45, -1, 47, -1, -1,
+ -1, -1, 52, 8, 9, 55, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 32, 8, 9, -1,
+ 36, 37, 38, 39, 40, 41, -1, 32, 44, -1,
+ -1, 47, -1, 8, 9, -1, 52, 42, 43, 55,
+ 45, 32, 47, -1, 8, 9, 37, 52, -1, -1,
+ 55, 8, 9, 44, -1, -1, 47, 32, 8, 9,
+ -1, 52, 8, 9, 55, -1, -1, -1, 32, -1,
+ 45, -1, 47, -1, -1, 32, -1, 52, 42, -1,
+ 55, -1, 32, 47, -1, 42, 32, -1, 52, -1,
+ 47, 55, 8, 9, -1, 52, -1, 47, 55, 8,
+ 9, 47, 52, -1, 54, 55, 52, -1, 54, 55,
+ -1, -1, -1, -1, -1, -1, 32, -1, -1, -1,
+ -1, -1, -1, 32, -1, -1, 42, -1, -1, -1,
+ -1, 47, -1, -1, -1, -1, 52, -1, 47, 55,
+ -1, -1, -1, 52, -1, -1, 55, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 5, 6, 7, 8,
+ 9, 10, 11, 12, 6, 7, 8, 9, 10, 11,
+ 12
};
/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
symbol of state STATE-NUM. */
static const yytype_uint8 yystos[] =
{
- 0, 55, 0, 56, 1, 13, 14, 15, 16, 17,
+ 0, 57, 0, 58, 1, 13, 14, 15, 16, 17,
18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
- 28, 29, 43, 44, 45, 47, 57, 60, 47, 49,
- 61, 8, 9, 30, 34, 35, 36, 37, 38, 39,
- 42, 43, 45, 49, 50, 53, 65, 78, 82, 85,
- 86, 87, 88, 91, 52, 64, 78, 80, 84, 62,
- 80, 63, 78, 10, 43, 44, 49, 69, 79, 81,
- 82, 86, 87, 91, 67, 88, 49, 61, 65, 70,
- 43, 49, 50, 66, 81, 84, 91, 68, 85, 77,
- 85, 71, 80, 72, 80, 73, 80, 74, 82, 75,
- 84, 76, 84, 46, 48, 46, 48, 47, 91, 91,
- 6, 8, 9, 89, 78, 35, 42, 91, 93, 91,
- 50, 50, 9, 40, 41, 50, 88, 91, 49, 49,
- 49, 82, 86, 89, 89, 79, 50, 11, 89, 81,
- 49, 50, 49, 49, 49, 49, 49, 49, 49, 59,
- 93, 58, 93, 7, 91, 91, 50, 10, 51, 51,
- 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
- 51, 35, 35, 36, 42, 40, 9, 40, 78, 80,
- 32, 91, 81, 52, 83, 91, 84, 91, 78, 78,
- 78, 78, 78, 57, 57, 89, 31, 33, 42, 90,
- 91, 50, 93, 93, 93, 6, 7, 93, 93, 93,
- 93, 93, 10, 10, 51, 51, 51, 40, 51, 51,
- 49, 9, 30, 92, 49, 49, 46, 46, 49, 49,
- 50, 51, 51, 35, 93, 93, 91, 91, 50, 51,
- 84, 30, 9, 83, 84, 35, 36, 91, 82, 33,
- 10, 51, 51, 35, 9, 30, 51, 91, 10, 30,
- 51, 91, 51
+ 28, 29, 30, 31, 45, 46, 47, 49, 59, 62,
+ 49, 51, 63, 8, 9, 32, 36, 37, 38, 39,
+ 40, 41, 44, 45, 47, 51, 52, 55, 67, 82,
+ 86, 89, 90, 91, 92, 95, 54, 66, 82, 84,
+ 88, 64, 84, 65, 82, 10, 45, 46, 51, 71,
+ 83, 85, 86, 90, 91, 95, 69, 92, 51, 63,
+ 67, 72, 45, 51, 52, 68, 85, 88, 95, 70,
+ 89, 79, 89, 80, 84, 73, 84, 74, 84, 75,
+ 84, 76, 86, 77, 88, 78, 88, 81, 84, 48,
+ 50, 48, 50, 49, 95, 95, 6, 8, 9, 93,
+ 82, 37, 44, 95, 97, 95, 52, 52, 9, 42,
+ 43, 52, 92, 95, 51, 51, 51, 86, 90, 93,
+ 93, 83, 52, 11, 93, 85, 51, 52, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51, 61, 97, 60,
+ 97, 7, 95, 95, 52, 10, 53, 53, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 53, 37,
+ 37, 38, 44, 42, 9, 42, 82, 84, 34, 95,
+ 85, 54, 87, 95, 88, 95, 84, 82, 82, 82,
+ 82, 82, 84, 59, 59, 93, 33, 35, 44, 94,
+ 95, 52, 97, 97, 97, 6, 7, 97, 97, 97,
+ 97, 97, 10, 10, 53, 53, 53, 42, 53, 53,
+ 51, 9, 32, 96, 51, 51, 48, 48, 51, 51,
+ 52, 53, 53, 37, 97, 97, 95, 95, 52, 53,
+ 88, 32, 9, 87, 88, 37, 38, 95, 86, 35,
+ 10, 53, 53, 37, 38, 9, 32, 53, 95, 10,
+ 10, 32, 53, 95, 95, 53, 53
};
#define yyerrok (yyerrstatus = 0)
@@ -878,9 +915,18 @@ static const yytype_uint8 yystos[] =
/* Like YYERROR except do call yyerror. This remains here temporarily
to ease the transition to the new meaning of YYERROR, for GCC.
- Once GCC version 2 has supplanted version 1, this can go. */
+ Once GCC version 2 has supplanted version 1, this can go. However,
+ YYFAIL appears to be in use. Nevertheless, it is formally deprecated
+ in Bison 2.4.2's NEWS entry, where a plan to phase it out is
+ discussed. */
#define YYFAIL goto yyerrlab
+#if defined YYFAIL
+ /* This is here to suppress warnings from the GCC cpp's
+ -Wunused-macros. Normally we don't worry about that warning, but
+ some users do, and we want to make it easy for users to remove
+ YYFAIL uses, which will produce warnings from Bison 2.5. */
+#endif
#define YYRECOVERING() (!!yyerrstatus)
@@ -890,7 +936,6 @@ do \
{ \
yychar = (Token); \
yylval = (Value); \
- yytoken = YYTRANSLATE (yychar); \
YYPOPSTACK (1); \
goto yybackup; \
} \
@@ -932,19 +977,10 @@ while (YYID (0))
#endif
-/* YY_LOCATION_PRINT -- Print the location on the stream.
- This macro was not mandated originally: define only if we know
- we won't break user code: when these are the locations we know. */
+/* This macro is provided for backward compatibility. */
#ifndef YY_LOCATION_PRINT
-# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL
-# define YY_LOCATION_PRINT(File, Loc) \
- fprintf (File, "%d.%d-%d.%d", \
- (Loc).first_line, (Loc).first_column, \
- (Loc).last_line, (Loc).last_column)
-# else
-# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
-# endif
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
#endif
@@ -1048,17 +1084,20 @@ yy_symbol_print (yyoutput, yytype, yyvaluep)
#if (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
static void
-yy_stack_print (yytype_int16 *bottom, yytype_int16 *top)
+yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop)
#else
static void
-yy_stack_print (bottom, top)
- yytype_int16 *bottom;
- yytype_int16 *top;
+yy_stack_print (yybottom, yytop)
+ yytype_int16 *yybottom;
+ yytype_int16 *yytop;
#endif
{
YYFPRINTF (stderr, "Stack now");
- for (; bottom <= top; ++bottom)
- YYFPRINTF (stderr, " %d", *bottom);
+ for (; yybottom <= yytop; yybottom++)
+ {
+ int yybot = *yybottom;
+ YYFPRINTF (stderr, " %d", yybot);
+ }
YYFPRINTF (stderr, "\n");
}
@@ -1092,11 +1131,11 @@ yy_reduce_print (yyvsp, yyrule)
/* The symbols being reduced. */
for (yyi = 0; yyi < yynrhs; yyi++)
{
- fprintf (stderr, " $%d = ", yyi + 1);
+ YYFPRINTF (stderr, " $%d = ", yyi + 1);
yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
&(yyvsp[(yyi + 1) - (yynrhs)])
);
- fprintf (stderr, "\n");
+ YYFPRINTF (stderr, "\n");
}
}
@@ -1133,7 +1172,6 @@ int yydebug;
# define YYMAXDEPTH 10000
#endif
-
#if YYERROR_VERBOSE
@@ -1236,115 +1274,142 @@ yytnamerr (char *yyres, const char *yystr)
}
# endif
-/* Copy into YYRESULT an error message about the unexpected token
- YYCHAR while in state YYSTATE. Return the number of bytes copied,
- including the terminating null byte. If YYRESULT is null, do not
- copy anything; just return the number of bytes that would be
- copied. As a special case, return 0 if an ordinary "syntax error"
- message will do. Return YYSIZE_MAXIMUM if overflow occurs during
- size calculation. */
-static YYSIZE_T
-yysyntax_error (char *yyresult, int yystate, int yychar)
-{
- int yyn = yypact[yystate];
+/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message
+ about the unexpected token YYTOKEN for the state stack whose top is
+ YYSSP.
- if (! (YYPACT_NINF < yyn && yyn <= YYLAST))
- return 0;
- else
- {
- int yytype = YYTRANSLATE (yychar);
- YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]);
- YYSIZE_T yysize = yysize0;
- YYSIZE_T yysize1;
- int yysize_overflow = 0;
- enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
- char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
- int yyx;
-
-# if 0
- /* This is so xgettext sees the translatable formats that are
- constructed on the fly. */
- YY_("syntax error, unexpected %s");
- YY_("syntax error, unexpected %s, expecting %s");
- YY_("syntax error, unexpected %s, expecting %s or %s");
- YY_("syntax error, unexpected %s, expecting %s or %s or %s");
- YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s");
-# endif
- char *yyfmt;
- char const *yyf;
- static char const yyunexpected[] = "syntax error, unexpected %s";
- static char const yyexpecting[] = ", expecting %s";
- static char const yyor[] = " or %s";
- char yyformat[sizeof yyunexpected
- + sizeof yyexpecting - 1
- + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2)
- * (sizeof yyor - 1))];
- char const *yyprefix = yyexpecting;
-
- /* Start YYX at -YYN if negative to avoid negative indexes in
- YYCHECK. */
- int yyxbegin = yyn < 0 ? -yyn : 0;
-
- /* Stay within bounds of both yycheck and yytname. */
- int yychecklim = YYLAST - yyn + 1;
- int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
- int yycount = 1;
-
- yyarg[0] = yytname[yytype];
- yyfmt = yystpcpy (yyformat, yyunexpected);
-
- for (yyx = yyxbegin; yyx < yyxend; ++yyx)
- if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
- {
- if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
- {
- yycount = 1;
- yysize = yysize0;
- yyformat[sizeof yyunexpected - 1] = '\0';
- break;
- }
- yyarg[yycount++] = yytname[yyx];
- yysize1 = yysize + yytnamerr (0, yytname[yyx]);
- yysize_overflow |= (yysize1 < yysize);
- yysize = yysize1;
- yyfmt = yystpcpy (yyfmt, yyprefix);
- yyprefix = yyor;
- }
+ Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is
+ not large enough to hold the message. In that case, also set
+ *YYMSG_ALLOC to the required number of bytes. Return 2 if the
+ required number of bytes is too large to store. */
+static int
+yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg,
+ yytype_int16 *yyssp, int yytoken)
+{
+ YYSIZE_T yysize0 = yytnamerr (0, yytname[yytoken]);
+ YYSIZE_T yysize = yysize0;
+ YYSIZE_T yysize1;
+ enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+ /* Internationalized format string. */
+ const char *yyformat = 0;
+ /* Arguments of yyformat. */
+ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+ /* Number of reported tokens (one for the "unexpected", one per
+ "expected"). */
+ int yycount = 0;
+
+ /* There are many possibilities here to consider:
+ - Assume YYFAIL is not used. It's too flawed to consider. See
+ <http://lists.gnu.org/archive/html/bison-patches/2009-12/msg00024.html>
+ for details. YYERROR is fine as it does not invoke this
+ function.
+ - If this state is a consistent state with a default action, then
+ the only way this function was invoked is if the default action
+ is an error action. In that case, don't check for expected
+ tokens because there are none.
+ - The only way there can be no lookahead present (in yychar) is if
+ this state is a consistent state with a default action. Thus,
+ detecting the absence of a lookahead is sufficient to determine
+ that there is no unexpected or expected token to report. In that
+ case, just report a simple "syntax error".
+ - Don't assume there isn't a lookahead just because this state is a
+ consistent state with a default action. There might have been a
+ previous inconsistent state, consistent state with a non-default
+ action, or user semantic action that manipulated yychar.
+ - Of course, the expected token list depends on states to have
+ correct lookahead information, and it depends on the parser not
+ to perform extra reductions after fetching a lookahead from the
+ scanner and before detecting a syntax error. Thus, state merging
+ (from LALR or IELR) and default reductions corrupt the expected
+ token list. However, the list is correct for canonical LR with
+ one exception: it will still contain any token that will not be
+ accepted due to an error action in a later state.
+ */
+ if (yytoken != YYEMPTY)
+ {
+ int yyn = yypact[*yyssp];
+ yyarg[yycount++] = yytname[yytoken];
+ if (!yypact_value_is_default (yyn))
+ {
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. In other words, skip the first -YYN actions for
+ this state because they are default actions. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn + 1;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yyx;
+
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR
+ && !yytable_value_is_error (yytable[yyx + yyn]))
+ {
+ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+ {
+ yycount = 1;
+ yysize = yysize0;
+ break;
+ }
+ yyarg[yycount++] = yytname[yyx];
+ yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+ if (! (yysize <= yysize1
+ && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+ return 2;
+ yysize = yysize1;
+ }
+ }
+ }
- yyf = YY_(yyformat);
- yysize1 = yysize + yystrlen (yyf);
- yysize_overflow |= (yysize1 < yysize);
- yysize = yysize1;
+ switch (yycount)
+ {
+# define YYCASE_(N, S) \
+ case N: \
+ yyformat = S; \
+ break
+ YYCASE_(0, YY_("syntax error"));
+ YYCASE_(1, YY_("syntax error, unexpected %s"));
+ YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s"));
+ YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s"));
+ YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s"));
+ YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"));
+# undef YYCASE_
+ }
- if (yysize_overflow)
- return YYSIZE_MAXIMUM;
+ yysize1 = yysize + yystrlen (yyformat);
+ if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+ return 2;
+ yysize = yysize1;
- if (yyresult)
- {
- /* Avoid sprintf, as that infringes on the user's name space.
- Don't have undefined behavior even if the translation
- produced a string with the wrong number of "%s"s. */
- char *yyp = yyresult;
- int yyi = 0;
- while ((*yyp = *yyf) != '\0')
- {
- if (*yyp == '%' && yyf[1] == 's' && yyi < yycount)
- {
- yyp += yytnamerr (yyp, yyarg[yyi++]);
- yyf += 2;
- }
- else
- {
- yyp++;
- yyf++;
- }
- }
- }
- return yysize;
+ if (*yymsg_alloc < yysize)
+ {
+ *yymsg_alloc = 2 * yysize;
+ if (! (yysize <= *yymsg_alloc
+ && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM))
+ *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM;
+ return 1;
}
+
+ /* Avoid sprintf, as that infringes on the user's name space.
+ Don't have undefined behavior even if the translation
+ produced a string with the wrong number of "%s"s. */
+ {
+ char *yyp = *yymsg;
+ int yyi = 0;
+ while ((*yyp = *yyformat) != '\0')
+ if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount)
+ {
+ yyp += yytnamerr (yyp, yyarg[yyi++]);
+ yyformat += 2;
+ }
+ else
+ {
+ yyp++;
+ yyformat++;
+ }
+ }
+ return 0;
}
#endif /* YYERROR_VERBOSE */
-
/*-----------------------------------------------.
| Release the memory associated to this symbol. |
@@ -1376,10 +1441,9 @@ yydestruct (yymsg, yytype, yyvaluep)
break;
}
}
-
-/* Prevent warnings from -Wmissing-prototypes. */
+/* Prevent warnings from -Wmissing-prototypes. */
#ifdef YYPARSE_PARAM
#if defined __STDC__ || defined __cplusplus
int yyparse (void *YYPARSE_PARAM);
@@ -1395,18 +1459,16 @@ int yyparse ();
#endif /* ! YYPARSE_PARAM */
-
-/* The look-ahead symbol. */
+/* The lookahead symbol. */
int yychar;
-/* The semantic value of the look-ahead symbol. */
+/* The semantic value of the lookahead symbol. */
YYSTYPE yylval;
/* Number of syntax errors so far. */
int yynerrs;
-
/*----------.
| yyparse. |
`----------*/
@@ -1433,66 +1495,66 @@ yyparse ()
#endif
#endif
{
-
- int yystate;
- int yyn;
- int yyresult;
- /* Number of tokens to shift before error messages enabled. */
- int yyerrstatus;
- /* Look-ahead token as an internal (translated) token number. */
- int yytoken = 0;
-#if YYERROR_VERBOSE
- /* Buffer for error messages, and its allocated size. */
- char yymsgbuf[128];
- char *yymsg = yymsgbuf;
- YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
-#endif
+ int yystate;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
- /* Three stacks and their tools:
- `yyss': related to states,
- `yyvs': related to semantic values,
- `yyls': related to locations.
+ /* The stacks and their tools:
+ `yyss': related to states.
+ `yyvs': related to semantic values.
- Refer to the stacks thru separate pointers, to allow yyoverflow
- to reallocate them elsewhere. */
+ Refer to the stacks thru separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
- /* The state stack. */
- yytype_int16 yyssa[YYINITDEPTH];
- yytype_int16 *yyss = yyssa;
- yytype_int16 *yyssp;
+ /* The state stack. */
+ yytype_int16 yyssa[YYINITDEPTH];
+ yytype_int16 *yyss;
+ yytype_int16 *yyssp;
- /* The semantic value stack. */
- YYSTYPE yyvsa[YYINITDEPTH];
- YYSTYPE *yyvs = yyvsa;
- YYSTYPE *yyvsp;
-
-
-
-#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N))
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs;
+ YYSTYPE *yyvsp;
- YYSIZE_T yystacksize = YYINITDEPTH;
+ YYSIZE_T yystacksize;
+ int yyn;
+ int yyresult;
+ /* Lookahead token as an internal (translated) token number. */
+ int yytoken;
/* The variables used to return semantic value and location from the
action routines. */
YYSTYPE yyval;
+#if YYERROR_VERBOSE
+ /* Buffer for error messages, and its allocated size. */
+ char yymsgbuf[128];
+ char *yymsg = yymsgbuf;
+ YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N))
/* The number of symbols on the RHS of the reduced rule.
Keep to zero when no symbol should be popped. */
int yylen = 0;
+ yytoken = 0;
+ yyss = yyssa;
+ yyvs = yyvsa;
+ yystacksize = YYINITDEPTH;
+
YYDPRINTF ((stderr, "Starting parse\n"));
yystate = 0;
yyerrstatus = 0;
yynerrs = 0;
- yychar = YYEMPTY; /* Cause a token to be read. */
+ yychar = YYEMPTY; /* Cause a token to be read. */
/* Initialize stack pointers.
Waste one element of value and location stack
so that they stay on the same level as the state stack.
The wasted elements are never initialized. */
-
yyssp = yyss;
yyvsp = yyvs;
@@ -1522,7 +1584,6 @@ yyparse ()
YYSTYPE *yyvs1 = yyvs;
yytype_int16 *yyss1 = yyss;
-
/* Each stack pointer address is followed by the size of the
data in use in that stack, in bytes. This used to be a
conditional around just the two extra args, but that might
@@ -1530,7 +1591,6 @@ yyparse ()
yyoverflow (YY_("memory exhausted"),
&yyss1, yysize * sizeof (*yyssp),
&yyvs1, yysize * sizeof (*yyvsp),
-
&yystacksize);
yyss = yyss1;
@@ -1553,9 +1613,8 @@ yyparse ()
(union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
if (! yyptr)
goto yyexhaustedlab;
- YYSTACK_RELOCATE (yyss);
- YYSTACK_RELOCATE (yyvs);
-
+ YYSTACK_RELOCATE (yyss_alloc, yyss);
+ YYSTACK_RELOCATE (yyvs_alloc, yyvs);
# undef YYSTACK_RELOCATE
if (yyss1 != yyssa)
YYSTACK_FREE (yyss1);
@@ -1566,7 +1625,6 @@ yyparse ()
yyssp = yyss + yysize - 1;
yyvsp = yyvs + yysize - 1;
-
YYDPRINTF ((stderr, "Stack size increased to %lu\n",
(unsigned long int) yystacksize));
@@ -1576,6 +1634,9 @@ yyparse ()
YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+ if (yystate == YYFINAL)
+ YYACCEPT;
+
goto yybackup;
/*-----------.
@@ -1584,16 +1645,16 @@ yyparse ()
yybackup:
/* Do appropriate processing given the current state. Read a
- look-ahead token if we need one and don't already have one. */
+ lookahead token if we need one and don't already have one. */
- /* First try to decide what to do without reference to look-ahead token. */
+ /* First try to decide what to do without reference to lookahead token. */
yyn = yypact[yystate];
- if (yyn == YYPACT_NINF)
+ if (yypact_value_is_default (yyn))
goto yydefault;
- /* Not known => get a look-ahead token if don't already have one. */
+ /* Not known => get a lookahead token if don't already have one. */
- /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */
if (yychar == YYEMPTY)
{
YYDPRINTF ((stderr, "Reading a token: "));
@@ -1619,26 +1680,22 @@ yybackup:
yyn = yytable[yyn];
if (yyn <= 0)
{
- if (yyn == 0 || yyn == YYTABLE_NINF)
- goto yyerrlab;
+ if (yytable_value_is_error (yyn))
+ goto yyerrlab;
yyn = -yyn;
goto yyreduce;
}
- if (yyn == YYFINAL)
- YYACCEPT;
-
/* Count tokens shifted since error; after three, turn off error
status. */
if (yyerrstatus)
yyerrstatus--;
- /* Shift the look-ahead token. */
+ /* Shift the lookahead token. */
YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
- /* Discard the shifted token unless it is eof. */
- if (yychar != YYEOF)
- yychar = YYEMPTY;
+ /* Discard the shifted token. */
+ yychar = YYEMPTY;
yystate = yyn;
*++yyvsp = yylval;
@@ -1678,14 +1735,18 @@ yyreduce:
switch (yyn)
{
case 3:
-#line 66 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 68 "a.y"
{
stmtline = lineno;
}
break;
case 5:
-#line 73 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 75 "a.y"
{
if((yyvsp[(1) - (2)].sym)->value != pc)
yyerror("redeclaration of %s", (yyvsp[(1) - (2)].sym)->name);
@@ -1694,7 +1755,9 @@ yyreduce:
break;
case 7:
-#line 80 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 82 "a.y"
{
(yyvsp[(1) - (2)].sym)->type = LLAB;
(yyvsp[(1) - (2)].sym)->value = pc;
@@ -1702,7 +1765,9 @@ yyreduce:
break;
case 12:
-#line 91 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 93 "a.y"
{
(yyvsp[(1) - (3)].sym)->type = LVAR;
(yyvsp[(1) - (3)].sym)->value = (yyvsp[(3) - (3)].lval);
@@ -1710,7 +1775,9 @@ yyreduce:
break;
case 13:
-#line 96 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 98 "a.y"
{
if((yyvsp[(1) - (3)].sym)->value != (yyvsp[(3) - (3)].lval))
yyerror("redeclaration of %s", (yyvsp[(1) - (3)].sym)->name);
@@ -1719,180 +1786,252 @@ yyreduce:
break;
case 14:
-#line 101 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 103 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 15:
-#line 102 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 104 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 16:
-#line 103 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 105 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 17:
-#line 104 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 106 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 18:
-#line 105 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 107 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 19:
-#line 106 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 108 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 20:
-#line 107 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 109 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 21:
-#line 108 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 110 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 22:
-#line 109 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 111 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 23:
-#line 110 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 112 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 24:
-#line 111 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 113 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 25:
-#line 112 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 114 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 26:
-#line 113 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 115 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 27:
-#line 114 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 116 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 28:
-#line 115 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 117 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 29:
-#line 116 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 118 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 30:
-#line 117 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 119 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 31:
+
+/* Line 1806 of yacc.c */
#line 120 "a.y"
+ { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
+ break;
+
+ case 32:
+
+/* Line 1806 of yacc.c */
+#line 121 "a.y"
+ { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
+ break;
+
+ case 33:
+
+/* Line 1806 of yacc.c */
+#line 124 "a.y"
{
(yyval.gen2).from = nullgen;
(yyval.gen2).to = nullgen;
}
break;
- case 32:
-#line 125 "a.y"
+ case 34:
+
+/* Line 1806 of yacc.c */
+#line 129 "a.y"
{
(yyval.gen2).from = nullgen;
(yyval.gen2).to = nullgen;
}
break;
- case 33:
-#line 132 "a.y"
+ case 35:
+
+/* Line 1806 of yacc.c */
+#line 136 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (3)].gen);
(yyval.gen2).to = (yyvsp[(3) - (3)].gen);
}
break;
- case 34:
-#line 139 "a.y"
+ case 36:
+
+/* Line 1806 of yacc.c */
+#line 143 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (3)].gen);
(yyval.gen2).to = (yyvsp[(3) - (3)].gen);
}
break;
- case 35:
-#line 146 "a.y"
+ case 37:
+
+/* Line 1806 of yacc.c */
+#line 150 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (2)].gen);
(yyval.gen2).to = nullgen;
}
break;
- case 36:
-#line 151 "a.y"
+ case 38:
+
+/* Line 1806 of yacc.c */
+#line 155 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (1)].gen);
(yyval.gen2).to = nullgen;
}
break;
- case 37:
-#line 158 "a.y"
+ case 39:
+
+/* Line 1806 of yacc.c */
+#line 162 "a.y"
{
(yyval.gen2).from = nullgen;
(yyval.gen2).to = (yyvsp[(2) - (2)].gen);
}
break;
- case 38:
-#line 163 "a.y"
+ case 40:
+
+/* Line 1806 of yacc.c */
+#line 167 "a.y"
{
(yyval.gen2).from = nullgen;
(yyval.gen2).to = (yyvsp[(1) - (1)].gen);
}
break;
- case 39:
-#line 170 "a.y"
+ case 41:
+
+/* Line 1806 of yacc.c */
+#line 174 "a.y"
{
(yyval.gen2).from = nullgen;
(yyval.gen2).to = (yyvsp[(2) - (2)].gen);
}
break;
- case 40:
-#line 175 "a.y"
+ case 42:
+
+/* Line 1806 of yacc.c */
+#line 179 "a.y"
{
(yyval.gen2).from = nullgen;
(yyval.gen2).to = (yyvsp[(1) - (1)].gen);
}
break;
- case 41:
-#line 180 "a.y"
+ case 43:
+
+/* Line 1806 of yacc.c */
+#line 184 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (3)].gen);
(yyval.gen2).to = (yyvsp[(3) - (3)].gen);
}
break;
- case 42:
-#line 187 "a.y"
+ case 44:
+
+/* Line 1806 of yacc.c */
+#line 191 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (5)].gen);
(yyval.gen2).from.scale = (yyvsp[(3) - (5)].lval);
@@ -1900,16 +2039,20 @@ yyreduce:
}
break;
- case 43:
-#line 195 "a.y"
+ case 45:
+
+/* Line 1806 of yacc.c */
+#line 199 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (3)].gen);
(yyval.gen2).to = (yyvsp[(3) - (3)].gen);
}
break;
- case 44:
-#line 200 "a.y"
+ case 46:
+
+/* Line 1806 of yacc.c */
+#line 204 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (5)].gen);
(yyval.gen2).from.scale = (yyvsp[(3) - (5)].lval);
@@ -1917,32 +2060,40 @@ yyreduce:
}
break;
- case 45:
-#line 208 "a.y"
+ case 47:
+
+/* Line 1806 of yacc.c */
+#line 212 "a.y"
{
(yyval.gen2).from = nullgen;
(yyval.gen2).to = (yyvsp[(2) - (2)].gen);
}
break;
- case 46:
-#line 213 "a.y"
+ case 48:
+
+/* Line 1806 of yacc.c */
+#line 217 "a.y"
{
(yyval.gen2).from = nullgen;
(yyval.gen2).to = (yyvsp[(1) - (1)].gen);
}
break;
- case 49:
-#line 224 "a.y"
+ case 51:
+
+/* Line 1806 of yacc.c */
+#line 228 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (3)].gen);
(yyval.gen2).to = (yyvsp[(3) - (3)].gen);
}
break;
- case 50:
-#line 229 "a.y"
+ case 52:
+
+/* Line 1806 of yacc.c */
+#line 233 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (5)].gen);
(yyval.gen2).to = (yyvsp[(3) - (5)].gen);
@@ -1952,16 +2103,20 @@ yyreduce:
}
break;
- case 51:
-#line 239 "a.y"
+ case 53:
+
+/* Line 1806 of yacc.c */
+#line 243 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (3)].gen);
(yyval.gen2).to = (yyvsp[(3) - (3)].gen);
}
break;
- case 52:
-#line 244 "a.y"
+ case 54:
+
+/* Line 1806 of yacc.c */
+#line 248 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (5)].gen);
(yyval.gen2).to = (yyvsp[(3) - (5)].gen);
@@ -1971,32 +2126,40 @@ yyreduce:
}
break;
- case 53:
-#line 254 "a.y"
+ case 55:
+
+/* Line 1806 of yacc.c */
+#line 258 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (2)].gen);
(yyval.gen2).to = nullgen;
}
break;
- case 54:
-#line 259 "a.y"
+ case 56:
+
+/* Line 1806 of yacc.c */
+#line 263 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (1)].gen);
(yyval.gen2).to = nullgen;
}
break;
- case 55:
-#line 264 "a.y"
+ case 57:
+
+/* Line 1806 of yacc.c */
+#line 268 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (3)].gen);
(yyval.gen2).to = (yyvsp[(3) - (3)].gen);
}
break;
- case 56:
-#line 271 "a.y"
+ case 58:
+
+/* Line 1806 of yacc.c */
+#line 275 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (5)].gen);
(yyval.gen2).to = (yyvsp[(3) - (5)].gen);
@@ -2004,8 +2167,10 @@ yyreduce:
}
break;
- case 57:
-#line 279 "a.y"
+ case 59:
+
+/* Line 1806 of yacc.c */
+#line 283 "a.y"
{
(yyval.gen2).from = (yyvsp[(3) - (5)].gen);
(yyval.gen2).to = (yyvsp[(5) - (5)].gen);
@@ -2015,32 +2180,40 @@ yyreduce:
}
break;
- case 58:
-#line 288 "a.y"
+ case 60:
+
+/* Line 1806 of yacc.c */
+#line 292 "a.y"
{
(yyval.gen2).from = nullgen;
(yyval.gen2).to = nullgen;
}
break;
- case 59:
-#line 293 "a.y"
+ case 61:
+
+/* Line 1806 of yacc.c */
+#line 297 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (1)].gen);
(yyval.gen2).to = nullgen;
}
break;
- case 60:
-#line 300 "a.y"
+ case 62:
+
+/* Line 1806 of yacc.c */
+#line 304 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (3)].gen);
(yyval.gen2).to = (yyvsp[(3) - (3)].gen);
}
break;
- case 61:
-#line 305 "a.y"
+ case 63:
+
+/* Line 1806 of yacc.c */
+#line 309 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (5)].gen);
(yyval.gen2).from.scale = (yyvsp[(3) - (5)].lval);
@@ -2048,22 +2221,54 @@ yyreduce:
}
break;
- case 66:
-#line 319 "a.y"
+ case 64:
+
+/* Line 1806 of yacc.c */
+#line 317 "a.y"
+ {
+ if((yyvsp[(1) - (3)].gen).type != D_CONST || (yyvsp[(3) - (3)].gen).type != D_CONST)
+ yyerror("arguments to PCDATA must be integer constants");
+ (yyval.gen2).from = (yyvsp[(1) - (3)].gen);
+ (yyval.gen2).to = (yyvsp[(3) - (3)].gen);
+ }
+ break;
+
+ case 65:
+
+/* Line 1806 of yacc.c */
+#line 326 "a.y"
+ {
+ if((yyvsp[(1) - (3)].gen).type != D_CONST)
+ yyerror("index for FUNCDATA must be integer constant");
+ if((yyvsp[(3) - (3)].gen).type != D_EXTERN && (yyvsp[(3) - (3)].gen).type != D_STATIC)
+ yyerror("value for FUNCDATA must be symbol reference");
+ (yyval.gen2).from = (yyvsp[(1) - (3)].gen);
+ (yyval.gen2).to = (yyvsp[(3) - (3)].gen);
+ }
+ break;
+
+ case 70:
+
+/* Line 1806 of yacc.c */
+#line 343 "a.y"
{
(yyval.gen) = (yyvsp[(2) - (2)].gen);
}
break;
- case 67:
-#line 323 "a.y"
+ case 71:
+
+/* Line 1806 of yacc.c */
+#line 347 "a.y"
{
(yyval.gen) = (yyvsp[(2) - (2)].gen);
}
break;
- case 72:
-#line 335 "a.y"
+ case 76:
+
+/* Line 1806 of yacc.c */
+#line 359 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_BRANCH;
@@ -2071,8 +2276,10 @@ yyreduce:
}
break;
- case 73:
-#line 341 "a.y"
+ case 77:
+
+/* Line 1806 of yacc.c */
+#line 365 "a.y"
{
(yyval.gen) = nullgen;
if(pass == 2)
@@ -2083,8 +2290,10 @@ yyreduce:
}
break;
- case 74:
-#line 350 "a.y"
+ case 78:
+
+/* Line 1806 of yacc.c */
+#line 374 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_BRANCH;
@@ -2093,64 +2302,80 @@ yyreduce:
}
break;
- case 75:
-#line 359 "a.y"
+ case 79:
+
+/* Line 1806 of yacc.c */
+#line 383 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = (yyvsp[(1) - (1)].lval);
}
break;
- case 76:
-#line 364 "a.y"
+ case 80:
+
+/* Line 1806 of yacc.c */
+#line 388 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = (yyvsp[(1) - (1)].lval);
}
break;
- case 77:
-#line 369 "a.y"
+ case 81:
+
+/* Line 1806 of yacc.c */
+#line 393 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = (yyvsp[(1) - (1)].lval);
}
break;
- case 78:
-#line 374 "a.y"
+ case 82:
+
+/* Line 1806 of yacc.c */
+#line 398 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = (yyvsp[(1) - (1)].lval);
}
break;
- case 79:
-#line 379 "a.y"
+ case 83:
+
+/* Line 1806 of yacc.c */
+#line 403 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_SP;
}
break;
- case 80:
-#line 384 "a.y"
+ case 84:
+
+/* Line 1806 of yacc.c */
+#line 408 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = (yyvsp[(1) - (1)].lval);
}
break;
- case 81:
-#line 389 "a.y"
+ case 85:
+
+/* Line 1806 of yacc.c */
+#line 413 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = (yyvsp[(1) - (1)].lval);
}
break;
- case 82:
-#line 395 "a.y"
+ case 86:
+
+/* Line 1806 of yacc.c */
+#line 419 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_CONST;
@@ -2158,8 +2383,10 @@ yyreduce:
}
break;
- case 83:
-#line 403 "a.y"
+ case 87:
+
+/* Line 1806 of yacc.c */
+#line 427 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_CONST;
@@ -2167,8 +2394,10 @@ yyreduce:
}
break;
- case 84:
-#line 409 "a.y"
+ case 88:
+
+/* Line 1806 of yacc.c */
+#line 433 "a.y"
{
(yyval.gen) = (yyvsp[(2) - (2)].gen);
(yyval.gen).index = (yyvsp[(2) - (2)].gen).type;
@@ -2181,8 +2410,10 @@ yyreduce:
}
break;
- case 85:
-#line 420 "a.y"
+ case 89:
+
+/* Line 1806 of yacc.c */
+#line 444 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_SCONST;
@@ -2190,8 +2421,10 @@ yyreduce:
}
break;
- case 86:
-#line 426 "a.y"
+ case 90:
+
+/* Line 1806 of yacc.c */
+#line 450 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_FCONST;
@@ -2199,8 +2432,10 @@ yyreduce:
}
break;
- case 87:
-#line 432 "a.y"
+ case 91:
+
+/* Line 1806 of yacc.c */
+#line 456 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_FCONST;
@@ -2208,8 +2443,10 @@ yyreduce:
}
break;
- case 88:
-#line 438 "a.y"
+ case 92:
+
+/* Line 1806 of yacc.c */
+#line 462 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_FCONST;
@@ -2217,8 +2454,10 @@ yyreduce:
}
break;
- case 89:
-#line 444 "a.y"
+ case 93:
+
+/* Line 1806 of yacc.c */
+#line 468 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_FCONST;
@@ -2226,8 +2465,10 @@ yyreduce:
}
break;
- case 92:
-#line 456 "a.y"
+ case 96:
+
+/* Line 1806 of yacc.c */
+#line 480 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_INDIR+D_NONE;
@@ -2235,8 +2476,10 @@ yyreduce:
}
break;
- case 93:
-#line 462 "a.y"
+ case 97:
+
+/* Line 1806 of yacc.c */
+#line 486 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_INDIR+(yyvsp[(3) - (4)].lval);
@@ -2244,8 +2487,10 @@ yyreduce:
}
break;
- case 94:
-#line 468 "a.y"
+ case 98:
+
+/* Line 1806 of yacc.c */
+#line 492 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_INDIR+D_SP;
@@ -2253,8 +2498,10 @@ yyreduce:
}
break;
- case 95:
-#line 474 "a.y"
+ case 99:
+
+/* Line 1806 of yacc.c */
+#line 498 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_INDIR+(yyvsp[(3) - (4)].lval);
@@ -2262,8 +2509,10 @@ yyreduce:
}
break;
- case 96:
-#line 480 "a.y"
+ case 100:
+
+/* Line 1806 of yacc.c */
+#line 504 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_INDIR+D_NONE;
@@ -2274,8 +2523,10 @@ yyreduce:
}
break;
- case 97:
-#line 489 "a.y"
+ case 101:
+
+/* Line 1806 of yacc.c */
+#line 513 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_INDIR+(yyvsp[(3) - (9)].lval);
@@ -2286,24 +2537,44 @@ yyreduce:
}
break;
- case 98:
-#line 498 "a.y"
+ case 102:
+
+/* Line 1806 of yacc.c */
+#line 522 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_INDIR+(yyvsp[(3) - (9)].lval);
+ (yyval.gen).offset = (yyvsp[(1) - (9)].lval);
+ (yyval.gen).index = (yyvsp[(6) - (9)].lval);
+ (yyval.gen).scale = (yyvsp[(8) - (9)].lval);
+ checkscale((yyval.gen).scale);
+ }
+ break;
+
+ case 103:
+
+/* Line 1806 of yacc.c */
+#line 531 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_INDIR+(yyvsp[(2) - (3)].lval);
}
break;
- case 99:
-#line 503 "a.y"
+ case 104:
+
+/* Line 1806 of yacc.c */
+#line 536 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_INDIR+D_SP;
}
break;
- case 100:
-#line 508 "a.y"
+ case 105:
+
+/* Line 1806 of yacc.c */
+#line 541 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_INDIR+D_NONE;
@@ -2313,8 +2584,10 @@ yyreduce:
}
break;
- case 101:
-#line 516 "a.y"
+ case 106:
+
+/* Line 1806 of yacc.c */
+#line 549 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_INDIR+(yyvsp[(2) - (8)].lval);
@@ -2324,15 +2597,19 @@ yyreduce:
}
break;
- case 102:
-#line 526 "a.y"
+ case 107:
+
+/* Line 1806 of yacc.c */
+#line 559 "a.y"
{
(yyval.gen) = (yyvsp[(1) - (1)].gen);
}
break;
- case 103:
-#line 530 "a.y"
+ case 108:
+
+/* Line 1806 of yacc.c */
+#line 563 "a.y"
{
(yyval.gen) = (yyvsp[(1) - (6)].gen);
(yyval.gen).index = (yyvsp[(3) - (6)].lval);
@@ -2341,8 +2618,10 @@ yyreduce:
}
break;
- case 104:
-#line 539 "a.y"
+ case 109:
+
+/* Line 1806 of yacc.c */
+#line 572 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = (yyvsp[(4) - (5)].lval);
@@ -2351,8 +2630,10 @@ yyreduce:
}
break;
- case 105:
-#line 546 "a.y"
+ case 110:
+
+/* Line 1806 of yacc.c */
+#line 579 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_STATIC;
@@ -2361,174 +2642,234 @@ yyreduce:
}
break;
- case 106:
-#line 554 "a.y"
+ case 111:
+
+/* Line 1806 of yacc.c */
+#line 587 "a.y"
{
(yyval.lval) = 0;
}
break;
- case 107:
-#line 558 "a.y"
+ case 112:
+
+/* Line 1806 of yacc.c */
+#line 591 "a.y"
{
(yyval.lval) = (yyvsp[(2) - (2)].lval);
}
break;
- case 108:
-#line 562 "a.y"
+ case 113:
+
+/* Line 1806 of yacc.c */
+#line 595 "a.y"
{
(yyval.lval) = -(yyvsp[(2) - (2)].lval);
}
break;
- case 110:
-#line 569 "a.y"
+ case 115:
+
+/* Line 1806 of yacc.c */
+#line 602 "a.y"
{
(yyval.lval) = D_AUTO;
}
break;
- case 113:
-#line 577 "a.y"
+ case 118:
+
+/* Line 1806 of yacc.c */
+#line 610 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (1)].sym)->value;
}
break;
- case 114:
-#line 581 "a.y"
+ case 119:
+
+/* Line 1806 of yacc.c */
+#line 614 "a.y"
{
(yyval.lval) = -(yyvsp[(2) - (2)].lval);
}
break;
- case 115:
-#line 585 "a.y"
+ case 120:
+
+/* Line 1806 of yacc.c */
+#line 618 "a.y"
{
(yyval.lval) = (yyvsp[(2) - (2)].lval);
}
break;
- case 116:
-#line 589 "a.y"
+ case 121:
+
+/* Line 1806 of yacc.c */
+#line 622 "a.y"
{
(yyval.lval) = ~(yyvsp[(2) - (2)].lval);
}
break;
- case 117:
-#line 593 "a.y"
+ case 122:
+
+/* Line 1806 of yacc.c */
+#line 626 "a.y"
{
(yyval.lval) = (yyvsp[(2) - (3)].lval);
}
break;
- case 118:
-#line 599 "a.y"
+ case 123:
+
+/* Line 1806 of yacc.c */
+#line 632 "a.y"
{
- (yyval.lval) = (yyvsp[(1) - (1)].lval) & 0xffffffffLL;
+ (yyval.lval) = ((yyvsp[(1) - (1)].lval) & 0xffffffffLL) +
+ ((vlong)ArgsSizeUnknown << 32);
}
break;
- case 119:
-#line 603 "a.y"
+ case 124:
+
+/* Line 1806 of yacc.c */
+#line 637 "a.y"
{
- (yyval.lval) = -(yyvsp[(2) - (2)].lval) & 0xffffffffLL;
+ (yyval.lval) = (-(yyvsp[(2) - (2)].lval) & 0xffffffffLL) +
+ ((vlong)ArgsSizeUnknown << 32);
}
break;
- case 120:
-#line 607 "a.y"
+ case 125:
+
+/* Line 1806 of yacc.c */
+#line 642 "a.y"
{
(yyval.lval) = ((yyvsp[(1) - (3)].lval) & 0xffffffffLL) +
(((yyvsp[(3) - (3)].lval) & 0xffffLL) << 32);
}
break;
- case 121:
-#line 612 "a.y"
+ case 126:
+
+/* Line 1806 of yacc.c */
+#line 647 "a.y"
{
(yyval.lval) = (-(yyvsp[(2) - (4)].lval) & 0xffffffffLL) +
(((yyvsp[(4) - (4)].lval) & 0xffffLL) << 32);
}
break;
- case 123:
-#line 620 "a.y"
+ case 128:
+
+/* Line 1806 of yacc.c */
+#line 655 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (3)].lval) + (yyvsp[(3) - (3)].lval);
}
break;
- case 124:
-#line 624 "a.y"
+ case 129:
+
+/* Line 1806 of yacc.c */
+#line 659 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (3)].lval) - (yyvsp[(3) - (3)].lval);
}
break;
- case 125:
-#line 628 "a.y"
+ case 130:
+
+/* Line 1806 of yacc.c */
+#line 663 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (3)].lval) * (yyvsp[(3) - (3)].lval);
}
break;
- case 126:
-#line 632 "a.y"
+ case 131:
+
+/* Line 1806 of yacc.c */
+#line 667 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (3)].lval) / (yyvsp[(3) - (3)].lval);
}
break;
- case 127:
-#line 636 "a.y"
+ case 132:
+
+/* Line 1806 of yacc.c */
+#line 671 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (3)].lval) % (yyvsp[(3) - (3)].lval);
}
break;
- case 128:
-#line 640 "a.y"
+ case 133:
+
+/* Line 1806 of yacc.c */
+#line 675 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (4)].lval) << (yyvsp[(4) - (4)].lval);
}
break;
- case 129:
-#line 644 "a.y"
+ case 134:
+
+/* Line 1806 of yacc.c */
+#line 679 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (4)].lval) >> (yyvsp[(4) - (4)].lval);
}
break;
- case 130:
-#line 648 "a.y"
+ case 135:
+
+/* Line 1806 of yacc.c */
+#line 683 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (3)].lval) & (yyvsp[(3) - (3)].lval);
}
break;
- case 131:
-#line 652 "a.y"
+ case 136:
+
+/* Line 1806 of yacc.c */
+#line 687 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (3)].lval) ^ (yyvsp[(3) - (3)].lval);
}
break;
- case 132:
-#line 656 "a.y"
+ case 137:
+
+/* Line 1806 of yacc.c */
+#line 691 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (3)].lval) | (yyvsp[(3) - (3)].lval);
}
break;
-/* Line 1267 of yacc.c. */
-#line 2530 "y.tab.c"
+
+/* Line 1806 of yacc.c */
+#line 2860 "y.tab.c"
default: break;
}
+ /* User semantic actions sometimes alter yychar, and that requires
+ that yytoken be updated with the new translation. We take the
+ approach of translating immediately before every use of yytoken.
+ One alternative is translating here after every semantic action,
+ but that translation would be missed if the semantic action invokes
+ YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or
+ if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an
+ incorrect destructor might then be invoked immediately. In the
+ case of YYERROR or YYBACKUP, subsequent parser actions might lead
+ to an incorrect destructor call or verbose syntax error message
+ before the lookahead is translated. */
YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
YYPOPSTACK (yylen);
@@ -2537,7 +2878,6 @@ yyreduce:
*++yyvsp = yyval;
-
/* Now `shift' the result of the reduction. Determine what state
that goes to, based on the state we popped back to and the rule
number reduced by. */
@@ -2557,6 +2897,10 @@ yyreduce:
| yyerrlab -- here on detecting error |
`------------------------------------*/
yyerrlab:
+ /* Make sure we have latest lookahead translation. See comments at
+ user semantic actions for why this is necessary. */
+ yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar);
+
/* If not already recovering from an error, report this error. */
if (!yyerrstatus)
{
@@ -2564,37 +2908,36 @@ yyerrlab:
#if ! YYERROR_VERBOSE
yyerror (YY_("syntax error"));
#else
+# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \
+ yyssp, yytoken)
{
- YYSIZE_T yysize = yysyntax_error (0, yystate, yychar);
- if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM)
- {
- YYSIZE_T yyalloc = 2 * yysize;
- if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM))
- yyalloc = YYSTACK_ALLOC_MAXIMUM;
- if (yymsg != yymsgbuf)
- YYSTACK_FREE (yymsg);
- yymsg = (char *) YYSTACK_ALLOC (yyalloc);
- if (yymsg)
- yymsg_alloc = yyalloc;
- else
- {
- yymsg = yymsgbuf;
- yymsg_alloc = sizeof yymsgbuf;
- }
- }
-
- if (0 < yysize && yysize <= yymsg_alloc)
- {
- (void) yysyntax_error (yymsg, yystate, yychar);
- yyerror (yymsg);
- }
- else
- {
- yyerror (YY_("syntax error"));
- if (yysize != 0)
- goto yyexhaustedlab;
- }
+ char const *yymsgp = YY_("syntax error");
+ int yysyntax_error_status;
+ yysyntax_error_status = YYSYNTAX_ERROR;
+ if (yysyntax_error_status == 0)
+ yymsgp = yymsg;
+ else if (yysyntax_error_status == 1)
+ {
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+ yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc);
+ if (!yymsg)
+ {
+ yymsg = yymsgbuf;
+ yymsg_alloc = sizeof yymsgbuf;
+ yysyntax_error_status = 2;
+ }
+ else
+ {
+ yysyntax_error_status = YYSYNTAX_ERROR;
+ yymsgp = yymsg;
+ }
+ }
+ yyerror (yymsgp);
+ if (yysyntax_error_status == 2)
+ goto yyexhaustedlab;
}
+# undef YYSYNTAX_ERROR
#endif
}
@@ -2602,7 +2945,7 @@ yyerrlab:
if (yyerrstatus == 3)
{
- /* If just tried and failed to reuse look-ahead token after an
+ /* If just tried and failed to reuse lookahead token after an
error, discard it. */
if (yychar <= YYEOF)
@@ -2619,7 +2962,7 @@ yyerrlab:
}
}
- /* Else will try to reuse look-ahead token after shifting the error
+ /* Else will try to reuse lookahead token after shifting the error
token. */
goto yyerrlab1;
@@ -2653,7 +2996,7 @@ yyerrlab1:
for (;;)
{
yyn = yypact[yystate];
- if (yyn != YYPACT_NINF)
+ if (!yypact_value_is_default (yyn))
{
yyn += YYTERROR;
if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
@@ -2676,9 +3019,6 @@ yyerrlab1:
YY_STACK_PRINT (yyss, yyssp);
}
- if (yyn == YYFINAL)
- YYACCEPT;
-
*++yyvsp = yylval;
@@ -2703,7 +3043,7 @@ yyabortlab:
yyresult = 1;
goto yyreturn;
-#ifndef yyoverflow
+#if !defined(yyoverflow) || YYERROR_VERBOSE
/*-------------------------------------------------.
| yyexhaustedlab -- memory exhaustion comes here. |
`-------------------------------------------------*/
@@ -2714,9 +3054,14 @@ yyexhaustedlab:
#endif
yyreturn:
- if (yychar != YYEOF && yychar != YYEMPTY)
- yydestruct ("Cleanup: discarding lookahead",
- yytoken, &yylval);
+ if (yychar != YYEMPTY)
+ {
+ /* Make sure we have latest lookahead translation. See comments at
+ user semantic actions for why this is necessary. */
+ yytoken = YYTRANSLATE (yychar);
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval);
+ }
/* Do not reclaim the symbols of the rule which action triggered
this YYABORT or YYACCEPT. */
YYPOPSTACK (yylen);
diff --git a/src/cmd/6a/y.tab.h b/src/cmd/6a/y.tab.h
index 3cca766d3..bba6081dc 100644
--- a/src/cmd/6a/y.tab.h
+++ b/src/cmd/6a/y.tab.h
@@ -1,24 +1,21 @@
-/* A Bison parser, made by GNU Bison 2.3. */
+/* A Bison parser, made by GNU Bison 2.5. */
-/* Skeleton interface for Bison's Yacc-like parsers in C
-
- Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
- Free Software Foundation, Inc.
-
- This program is free software; you can redistribute it and/or modify
+/* Bison interface for Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
+
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* As a special exception, you may create a larger work that contains
part or all of the Bison parser skeleton and distribute that work
@@ -29,10 +26,11 @@
special exception, which will cause the skeleton and the resulting
Bison output files to be licensed under the GNU General Public
License without this special exception.
-
+
This special exception was added by the Free Software Foundation in
version 2.2 of Bison. */
+
/* Tokens. */
#ifndef YYTOKENTYPE
# define YYTOKENTYPE
@@ -50,28 +48,30 @@
LTYPER = 266,
LTYPET = 267,
LTYPEG = 268,
- LTYPES = 269,
- LTYPEM = 270,
- LTYPEI = 271,
- LTYPEXC = 272,
- LTYPEX = 273,
- LTYPERT = 274,
- LCONST = 275,
- LFP = 276,
- LPC = 277,
- LSB = 278,
- LBREG = 279,
- LLREG = 280,
- LSREG = 281,
- LFREG = 282,
- LMREG = 283,
- LXREG = 284,
- LFCONST = 285,
- LSCONST = 286,
- LSP = 287,
- LNAME = 288,
- LLAB = 289,
- LVAR = 290
+ LTYPEPC = 269,
+ LTYPES = 270,
+ LTYPEM = 271,
+ LTYPEI = 272,
+ LTYPEXC = 273,
+ LTYPEX = 274,
+ LTYPERT = 275,
+ LTYPEF = 276,
+ LCONST = 277,
+ LFP = 278,
+ LPC = 279,
+ LSB = 280,
+ LBREG = 281,
+ LLREG = 282,
+ LSREG = 283,
+ LFREG = 284,
+ LMREG = 285,
+ LXREG = 286,
+ LFCONST = 287,
+ LSCONST = 288,
+ LSP = 289,
+ LNAME = 290,
+ LLAB = 291,
+ LVAR = 292
};
#endif
/* Tokens. */
@@ -86,50 +86,58 @@
#define LTYPER 266
#define LTYPET 267
#define LTYPEG 268
-#define LTYPES 269
-#define LTYPEM 270
-#define LTYPEI 271
-#define LTYPEXC 272
-#define LTYPEX 273
-#define LTYPERT 274
-#define LCONST 275
-#define LFP 276
-#define LPC 277
-#define LSB 278
-#define LBREG 279
-#define LLREG 280
-#define LSREG 281
-#define LFREG 282
-#define LMREG 283
-#define LXREG 284
-#define LFCONST 285
-#define LSCONST 286
-#define LSP 287
-#define LNAME 288
-#define LLAB 289
-#define LVAR 290
+#define LTYPEPC 269
+#define LTYPES 270
+#define LTYPEM 271
+#define LTYPEI 272
+#define LTYPEXC 273
+#define LTYPEX 274
+#define LTYPERT 275
+#define LTYPEF 276
+#define LCONST 277
+#define LFP 278
+#define LPC 279
+#define LSB 280
+#define LBREG 281
+#define LLREG 282
+#define LSREG 283
+#define LFREG 284
+#define LMREG 285
+#define LXREG 286
+#define LFCONST 287
+#define LSCONST 288
+#define LSP 289
+#define LNAME 290
+#define LLAB 291
+#define LVAR 292
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
typedef union YYSTYPE
-#line 37 "a.y"
{
+
+/* Line 2068 of yacc.c */
+#line 38 "a.y"
+
Sym *sym;
vlong lval;
double dval;
char sval[8];
Gen gen;
Gen2 gen2;
-}
-/* Line 1529 of yacc.c. */
-#line 128 "y.tab.h"
- YYSTYPE;
+
+
+
+/* Line 2068 of yacc.c */
+#line 135 "y.tab.h"
+} YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
# define YYSTYPE_IS_DECLARED 1
-# define YYSTYPE_IS_TRIVIAL 1
#endif
extern YYSTYPE yylval;
+
diff --git a/src/cmd/6c/cgen.c b/src/cmd/6c/cgen.c
index 95400c445..bdef76ff0 100644
--- a/src/cmd/6c/cgen.c
+++ b/src/cmd/6c/cgen.c
@@ -29,6 +29,7 @@
// THE SOFTWARE.
#include "gc.h"
+#include "../../pkg/runtime/funcdata.h"
/* ,x/^(print|prtree)\(/i/\/\/ */
int castup(Type*, Type*);
@@ -392,13 +393,13 @@ cgen(Node *n, Node *nn)
}
}
- if(o == OMUL) {
+ if(o == OMUL || o == OLMUL) {
if(l->addable >= INDEXED) {
t = l;
l = r;
r = t;
}
- /* should favour AX */
+ reg[D_DX]++; // for gopcode case OMUL
regalloc(&nod, l, nn);
cgen(l, &nod);
if(r->addable < INDEXED || hardconst(r)) {
@@ -410,6 +411,7 @@ cgen(Node *n, Node *nn)
gopcode(OMUL, n->type, r, &nod); /* addressible */
gmove(&nod, nn);
regfree(&nod);
+ reg[D_DX]--;
break;
}
@@ -943,6 +945,7 @@ cgen(Node *n, Node *nn)
return;
}
gargs(r, &nod, &nod1);
+ gpcdata(PCDATA_ArgSize, curarg);
if(l->addable < INDEXED) {
reglcgen(&nod, l, nn);
nod.op = OREGISTER;
@@ -950,6 +953,7 @@ cgen(Node *n, Node *nn)
regfree(&nod);
} else
gopcode(OFUNC, n->type, Z, l);
+ gpcdata(PCDATA_ArgSize, -1);
if(REGARG >= 0 && reg[REGARG])
reg[REGARG]--;
if(nn != Z) {
@@ -1678,7 +1682,7 @@ copy:
if(n->complex >= FNX && nn != nil && nn->complex >= FNX) {
t = nn->type;
- nn->type = types[TLONG];
+ nn->type = types[TIND];
regialloc(&nod1, nn, Z);
lcgen(nn, &nod1);
regsalloc(&nod2, nn);
@@ -1785,7 +1789,7 @@ copy:
c = 0;
if(n->complex > nn->complex) {
t = n->type;
- n->type = types[TLONG];
+ n->type = types[TIND];
nodreg(&nod1, n, D_SI);
if(reg[D_SI]) {
gins(APUSHQ, &nod1, Z);
@@ -1796,7 +1800,7 @@ copy:
n->type = t;
t = nn->type;
- nn->type = types[TLONG];
+ nn->type = types[TIND];
nodreg(&nod2, nn, D_DI);
if(reg[D_DI]) {
warn(Z, "DI botch");
@@ -1808,7 +1812,7 @@ warn(Z, "DI botch");
nn->type = t;
} else {
t = nn->type;
- nn->type = types[TLONG];
+ nn->type = types[TIND];
nodreg(&nod2, nn, D_DI);
if(reg[D_DI]) {
warn(Z, "DI botch");
@@ -1820,7 +1824,7 @@ warn(Z, "DI botch");
nn->type = t;
t = n->type;
- n->type = types[TLONG];
+ n->type = types[TIND];
nodreg(&nod1, n, D_SI);
if(reg[D_SI]) {
gins(APUSHQ, &nod1, Z);
diff --git a/src/cmd/6c/gc.h b/src/cmd/6c/gc.h
index d1133ee21..c466a3afe 100644
--- a/src/cmd/6c/gc.h
+++ b/src/cmd/6c/gc.h
@@ -293,6 +293,7 @@ void patch(Prog*, int32);
int sconst(Node*);
void gpseudo(int, Sym*, Node*);
void gprefetch(Node*);
+void gpcdata(int, int);
/*
* swt.c
diff --git a/src/cmd/6c/peep.c b/src/cmd/6c/peep.c
index c648d8c00..0a3bd84bc 100644
--- a/src/cmd/6c/peep.c
+++ b/src/cmd/6c/peep.c
@@ -483,7 +483,7 @@ copy1(Adr *v1, Adr *v2, Reg *r, int f)
}
t = copyu(p, v2, A);
switch(t) {
- case 2: /* rar, cant split */
+ case 2: /* rar, can't split */
if(debug['P'])
print("; %D rar; return 0\n", v2);
return 0;
diff --git a/src/cmd/6c/reg.c b/src/cmd/6c/reg.c
index e40e6c8f0..edd93a0a0 100644
--- a/src/cmd/6c/reg.c
+++ b/src/cmd/6c/reg.c
@@ -111,6 +111,7 @@ regopt(Prog *p)
case AGLOBL:
case ANAME:
case ASIGNAME:
+ case AFUNCDATA:
continue;
}
r = rega();
@@ -645,6 +646,7 @@ brk:
case AGLOBL:
case ANAME:
case ASIGNAME:
+ case AFUNCDATA:
break;
}
}
diff --git a/src/cmd/6c/sgen.c b/src/cmd/6c/sgen.c
index 2402a020d..744a60222 100644
--- a/src/cmd/6c/sgen.c
+++ b/src/cmd/6c/sgen.c
@@ -29,16 +29,14 @@
// THE SOFTWARE.
#include "gc.h"
+#include "../../pkg/runtime/funcdata.h"
Prog*
gtext(Sym *s, int32 stkoff)
{
vlong v;
-
- v = 0;
- if(!(textflag & NOSPLIT))
- v |= argsize() << 32;
- v |= stkoff & 0xffffffff;
+
+ v = ((uvlong)argsize() << 32) | (stkoff & 0xffffffff);
if((textflag & NOSPLIT) && stkoff >= 128)
yyerror("stack frame too large for NOSPLIT function");
diff --git a/src/cmd/6c/swt.c b/src/cmd/6c/swt.c
index 541c7beaa..2496da477 100644
--- a/src/cmd/6c/swt.c
+++ b/src/cmd/6c/swt.c
@@ -311,12 +311,8 @@ outcode(void)
goto jackpot;
break;
}
- Bputc(&b, p->as);
- Bputc(&b, p->as>>8);
- Bputc(&b, p->lineno);
- Bputc(&b, p->lineno>>8);
- Bputc(&b, p->lineno>>16);
- Bputc(&b, p->lineno>>24);
+ BPUTLE2(&b, p->as);
+ BPUTLE4(&b, p->lineno);
zaddr(&b, &p->from, sf);
zaddr(&b, &p->to, st);
}
@@ -392,13 +388,12 @@ outhist(Biobuf *b)
q = 0;
}
if(n) {
- Bputc(b, ANAME);
- Bputc(b, ANAME>>8);
- Bputc(b, D_FILE);
- Bputc(b, 1);
- Bputc(b, '<');
+ BPUTLE2(b, ANAME);
+ BPUTC(b, D_FILE);
+ BPUTC(b, 1);
+ BPUTC(b, '<');
Bwrite(b, p, n);
- Bputc(b, 0);
+ BPUTC(b, 0);
}
p = q;
if(p == 0 && op) {
@@ -412,12 +407,8 @@ outhist(Biobuf *b)
if(h->offset)
pg.to.type = D_CONST;
- Bputc(b, pg.as);
- Bputc(b, pg.as>>8);
- Bputc(b, pg.lineno);
- Bputc(b, pg.lineno>>8);
- Bputc(b, pg.lineno>>16);
- Bputc(b, pg.lineno>>24);
+ BPUTLE2(b, pg.as);
+ BPUTLE4(b, pg.lineno);
zaddr(b, &pg.from, 0);
zaddr(b, &pg.to, 0);
@@ -436,26 +427,21 @@ zname(Biobuf *b, Sym *s, int t)
if(debug['T'] && t == D_EXTERN && s->sig != SIGDONE && s->type != types[TENUM] && s != symrathole){
sig = sign(s);
- Bputc(b, ASIGNAME);
- Bputc(b, ASIGNAME>>8);
- Bputc(b, sig);
- Bputc(b, sig>>8);
- Bputc(b, sig>>16);
- Bputc(b, sig>>24);
+ BPUTLE2(b, ASIGNAME);
+ BPUTLE4(b, sig);
s->sig = SIGDONE;
}
else{
- Bputc(b, ANAME); /* as */
- Bputc(b, ANAME>>8); /* as */
+ BPUTLE2(b, ANAME); /* as */
}
- Bputc(b, t); /* type */
- Bputc(b, s->sym); /* sym */
+ BPUTC(b, t); /* type */
+ BPUTC(b, s->sym); /* sym */
n = s->name;
while(*n) {
- Bputc(b, *n);
+ BPUTC(b, *n);
n++;
}
- Bputc(b, 0);
+ BPUTC(b, 0);
}
void
@@ -490,52 +476,40 @@ zaddr(Biobuf *b, Adr *a, int s)
t |= T_SCONST;
break;
}
- Bputc(b, t);
+ BPUTC(b, t);
if(t & T_INDEX) { /* implies index, scale */
- Bputc(b, a->index);
- Bputc(b, a->scale);
+ BPUTC(b, a->index);
+ BPUTC(b, a->scale);
}
if(t & T_OFFSET) { /* implies offset */
l = a->offset;
- Bputc(b, l);
- Bputc(b, l>>8);
- Bputc(b, l>>16);
- Bputc(b, l>>24);
+ BPUTLE4(b, l);
if(t & T_64) {
l = a->offset>>32;
- Bputc(b, l);
- Bputc(b, l>>8);
- Bputc(b, l>>16);
- Bputc(b, l>>24);
+ BPUTLE4(b, l);
}
}
if(t & T_SYM) /* implies sym */
- Bputc(b, s);
+ BPUTC(b, s);
if(t & T_FCONST) {
ieeedtod(&e, a->dval);
l = e.l;
- Bputc(b, l);
- Bputc(b, l>>8);
- Bputc(b, l>>16);
- Bputc(b, l>>24);
+ BPUTLE4(b, l);
l = e.h;
- Bputc(b, l);
- Bputc(b, l>>8);
- Bputc(b, l>>16);
- Bputc(b, l>>24);
+ BPUTLE4(b, l);
return;
}
if(t & T_SCONST) {
n = a->sval;
for(i=0; i<NSNAME; i++) {
- Bputc(b, *n);
+ BPUTC(b, *n);
n++;
}
return;
}
if(t & T_TYPE)
- Bputc(b, a->type);
+ BPUTC(b, a->type);
}
int32
diff --git a/src/cmd/6c/txt.c b/src/cmd/6c/txt.c
index 364b189f2..6f5d42da5 100644
--- a/src/cmd/6c/txt.c
+++ b/src/cmd/6c/txt.c
@@ -158,9 +158,7 @@ gclean(void)
continue;
if(s->type == types[TENUM])
continue;
- textflag = s->dataflag;
gpseudo(AGLOBL, s, nodconst(s->type->width));
- textflag = 0;
}
nextpc();
p->as = AEND;
@@ -1190,7 +1188,7 @@ print("botch in doindex\n");
else if(n->left->op == OREGISTER)
idx.ptr = n->left->reg;
else if(n->left->op != OADDR) {
- reg[D_BP]++; // cant be used as a base
+ reg[D_BP]++; // can't be used as a base
regalloc(&nod1, &qregnode, Z);
cgen(n->left, &nod1);
idx.ptr = nod1.reg;
@@ -1502,8 +1500,16 @@ gpseudo(int a, Sym *s, Node *n)
p->as = a;
p->from.type = D_EXTERN;
p->from.sym = s;
- p->from.scale = textflag;
- textflag = 0;
+
+ switch(a) {
+ case ATEXT:
+ p->from.scale = textflag;
+ textflag = 0;
+ break;
+ case AGLOBL:
+ p->from.scale = s->dataflag;
+ break;
+ }
if(s->class == CSTATIC)
p->from.type = D_STATIC;
@@ -1513,6 +1519,15 @@ gpseudo(int a, Sym *s, Node *n)
}
void
+gpcdata(int index, int value)
+{
+ Node n1;
+
+ n1 = *nodconst(index);
+ gins(APCDATA, &n1, nodconst(value));
+}
+
+void
gprefetch(Node *n)
{
Node n1;
diff --git a/src/cmd/6g/cgen.c b/src/cmd/6g/cgen.c
index 2eae865f3..ada2baa81 100644
--- a/src/cmd/6g/cgen.c
+++ b/src/cmd/6g/cgen.c
@@ -37,6 +37,8 @@ cgen(Node *n, Node *res)
case OSLICE:
case OSLICEARR:
case OSLICESTR:
+ case OSLICE3:
+ case OSLICE3ARR:
if (res->op != ONAME || !res->addable) {
tempname(&n1, n->type);
cgen_slice(n, &n1);
@@ -134,6 +136,7 @@ cgen(Node *n, Node *res)
// can't do in walk because n->left->addable
// changes if n->left is an escaping local variable.
switch(n->op) {
+ case OSPTR:
case OLEN:
if(isslice(n->left->type) || istype(n->left->type, TSTRING))
n->addable = n->left->addable;
@@ -312,6 +315,22 @@ cgen(Node *n, Node *res)
regfree(&n1);
break;
+ case OSPTR:
+ // pointer is the first word of string or slice.
+ if(isconst(nl, CTSTR)) {
+ regalloc(&n1, types[tptr], res);
+ p1 = gins(ALEAQ, N, &n1);
+ datastring(nl->val.u.sval->s, nl->val.u.sval->len, &p1->from);
+ gmove(&n1, res);
+ regfree(&n1);
+ break;
+ }
+ igen(nl, &n1, res);
+ n1.type = n->type;
+ gmove(&n1, res);
+ regfree(&n1);
+ break;
+
case OLEN:
if(istype(nl->type, TMAP) || istype(nl->type, TCHAN)) {
// map and chan have len in the first int-sized word.
@@ -382,7 +401,11 @@ cgen(Node *n, Node *res)
break;
case OADDR:
+ if(n->bounded) // let race detector avoid nil checks
+ disable_checknil++;
agen(nl, res);
+ if(n->bounded)
+ disable_checknil--;
break;
case OCALLMETH:
@@ -516,8 +539,8 @@ ret:
}
/*
- * allocate a register in res and generate
- * newreg = &n
+ * allocate a register (reusing res if possible) and generate
+ * a = n
* The caller must call regfree(a).
*/
void
@@ -558,14 +581,16 @@ cgenr(Node *n, Node *a, Node *res)
}
/*
- * allocate a register in res and generate
- * res = &n
+ * allocate a register (reusing res if possible) and generate
+ * a = &n
+ * The caller must call regfree(a).
+ * The generated code checks that the result is not nil.
*/
void
agenr(Node *n, Node *a, Node *res)
{
Node *nl, *nr;
- Node n1, n2, n3, n4, n5, tmp, tmp2, nlen;
+ Node n1, n2, n3, n5, tmp, tmp2, nlen;
Prog *p1;
Type *t;
uint64 w;
@@ -593,6 +618,7 @@ agenr(Node *n, Node *a, Node *res)
case OIND:
cgenr(n->left, a, res);
+ cgen_checknil(a);
break;
case OINDEX:
@@ -654,18 +680,6 @@ agenr(Node *n, Node *a, Node *res)
// len(a) is in nlen (if needed)
// w is width
- // explicit check for nil if array is large enough
- // that we might derive too big a pointer.
- if(isfixedarray(nl->type) && nl->type->width >= unmappedzero) {
- regalloc(&n4, types[tptr], &n3);
- gmove(&n3, &n4);
- n4.op = OINDREG;
- n4.type = types[TUINT8];
- n4.xoffset = 0;
- gins(ATESTB, nodintconst(0), &n4);
- regfree(&n4);
- }
-
// constant index
if(isconst(nr, CTINT)) {
if(isconst(nl, CTSTR))
@@ -775,6 +789,7 @@ agenr(Node *n, Node *a, Node *res)
/*
* generate:
* res = &n;
+ * The generated code checks that the result is not nil.
*/
void
agen(Node *n, Node *res)
@@ -841,6 +856,8 @@ agen(Node *n, Node *res)
case OSLICE:
case OSLICEARR:
case OSLICESTR:
+ case OSLICE3:
+ case OSLICE3ARR:
tempname(&n1, n->type);
cgen_slice(n, &n1);
agen(&n1, res);
@@ -878,43 +895,20 @@ agen(Node *n, Node *res)
case OIND:
cgen(nl, res);
+ cgen_checknil(res);
break;
case ODOT:
agen(nl, res);
- // explicit check for nil if struct is large enough
- // that we might derive too big a pointer. If the left node
- // was ODOT we have already done the nil check.
- if(nl->op != ODOT)
- if(nl->type->width >= unmappedzero) {
- regalloc(&n1, types[tptr], res);
- gmove(res, &n1);
- n1.op = OINDREG;
- n1.type = types[TUINT8];
- n1.xoffset = 0;
- gins(ATESTB, nodintconst(0), &n1);
- regfree(&n1);
- }
if(n->xoffset != 0)
ginscon(optoas(OADD, types[tptr]), n->xoffset, res);
break;
case ODOTPTR:
cgen(nl, res);
- // explicit check for nil if struct is large enough
- // that we might derive too big a pointer.
- if(nl->type->type->width >= unmappedzero) {
- regalloc(&n1, types[tptr], res);
- gmove(res, &n1);
- n1.op = OINDREG;
- n1.type = types[TUINT8];
- n1.xoffset = 0;
- gins(ATESTB, nodintconst(0), &n1);
- regfree(&n1);
- }
- if(n->xoffset != 0) {
+ cgen_checknil(res);
+ if(n->xoffset != 0)
ginscon(optoas(OADD, types[tptr]), n->xoffset, res);
- }
break;
}
@@ -929,6 +923,7 @@ ret:
*
* on exit, a has been changed to be *newreg.
* caller must regfree(a).
+ * The generated code checks that the result is not *nil.
*/
void
igen(Node *n, Node *a, Node *res)
@@ -959,22 +954,16 @@ igen(Node *n, Node *a, Node *res)
igen(n->left, a, res);
a->xoffset += n->xoffset;
a->type = n->type;
+ fixlargeoffset(a);
return;
case ODOTPTR:
cgenr(n->left, a, res);
- // explicit check for nil if struct is large enough
- // that we might derive too big a pointer.
- if(n->left->type->type->width >= unmappedzero) {
- n1 = *a;
- n1.op = OINDREG;
- n1.type = types[TUINT8];
- n1.xoffset = 0;
- gins(ATESTB, nodintconst(0), &n1);
- }
+ cgen_checknil(a);
a->op = OINDREG;
a->xoffset += n->xoffset;
a->type = n->type;
+ fixlargeoffset(a);
return;
case OCALLFUNC:
@@ -1013,6 +1002,7 @@ igen(Node *n, Node *a, Node *res)
igen(n->left, a, res);
else {
igen(n->left, &n1, res);
+ cgen_checknil(&n1);
regalloc(a, types[tptr], res);
gmove(&n1, a);
regfree(&n1);
@@ -1022,8 +1012,10 @@ igen(Node *n, Node *a, Node *res)
// Compute &a[i] as &a + i*width.
a->type = n->type;
a->xoffset += mpgetfix(n->right->val.u.xval)*n->type->width;
+ fixlargeoffset(a);
return;
}
+ break;
}
agenr(n, a, res);
@@ -1480,7 +1472,7 @@ cadable(Node *n)
* Small structs or arrays with elements of basic type are
* also supported.
* nr is N when assigning a zero value.
- * return 1 if can do, 0 if cant.
+ * return 1 if can do, 0 if can't.
*/
int
componentgen(Node *nr, Node *nl)
diff --git a/src/cmd/6g/gg.h b/src/cmd/6g/gg.h
index ceb6a2caa..3ef59c788 100644
--- a/src/cmd/6g/gg.h
+++ b/src/cmd/6g/gg.h
@@ -41,7 +41,7 @@ struct Prog
Addr from; // src address
Addr to; // dst address
Prog* link; // next instruction in this func
- void* reg; // pointer to containing Reg struct
+ void* opt; // for optimizer passes
};
#define TEXTFLAG from.scale
@@ -64,7 +64,6 @@ extern vlong unmappedzero;
* ggen.c
*/
void compile(Node*);
-void proglist(void);
void gen(Node*);
Node* lookdot(Node*, Node*, int);
void cgen_as(Node*, Node*);
@@ -107,7 +106,6 @@ int componentgen(Node*, Node*);
* gsubr.c
*/
void clearp(Prog*);
-void proglist(void);
Prog* gbranch(int, Type*, int);
Prog* prog(int);
void gconv(int, int);
@@ -133,6 +131,8 @@ int sudoaddable(int, Node*, Addr*);
void afunclit(Addr*, Node*);
void nodfconst(Node*, Type*, Mpflt*);
void gtrack(Sym*);
+void gargsize(vlong);
+void fixlargeoffset(Node *n);
/*
* cplx.c
diff --git a/src/cmd/6g/ggen.c b/src/cmd/6g/ggen.c
index 5e426753c..9fad9f7f1 100644
--- a/src/cmd/6g/ggen.c
+++ b/src/cmd/6g/ggen.c
@@ -9,15 +9,59 @@
#include "gg.h"
#include "opt.h"
+static Prog* appendp(Prog*, int, int, vlong, int, vlong);
+
void
-defframe(Prog *ptxt)
+defframe(Prog *ptxt, Bvec *bv)
{
+ int i, j;
+ uint32 frame;
+ Prog *p;
+
// fill in argument size
ptxt->to.offset = rnd(curfn->type->argwid, widthptr);
// fill in final stack size
ptxt->to.offset <<= 32;
- ptxt->to.offset |= rnd(stksize+maxarg, widthptr);
+ frame = rnd(stksize+maxarg, widthptr);
+ ptxt->to.offset |= frame;
+
+ // insert code to clear pointered part of the frame,
+ // so that garbage collector only sees initialized values
+ // when it looks for pointers.
+ p = ptxt;
+ if(stkzerosize >= 8*widthptr) {
+ p = appendp(p, AMOVQ, D_CONST, 0, D_AX, 0);
+ p = appendp(p, AMOVQ, D_CONST, stkzerosize/widthptr, D_CX, 0);
+ p = appendp(p, ALEAQ, D_SP+D_INDIR, frame-stkzerosize, D_DI, 0);
+ p = appendp(p, AREP, D_NONE, 0, D_NONE, 0);
+ appendp(p, ASTOSQ, D_NONE, 0, D_NONE, 0);
+ } else {
+ j = (stkptrsize - stkzerosize)/widthptr * 2;
+ for(i=0; i<stkzerosize; i+=widthptr) {
+ if(bvget(bv, j) || bvget(bv, j+1))
+ p = appendp(p, AMOVQ, D_CONST, 0, D_SP+D_INDIR, frame-stkzerosize+i);
+ j += 2;
+ }
+ }
+}
+
+static Prog*
+appendp(Prog *p, int as, int ftype, vlong foffset, int ttype, vlong toffset)
+{
+ Prog *q;
+
+ q = mal(sizeof(*q));
+ clearp(q);
+ q->as = as;
+ q->lineno = p->lineno;
+ q->from.type = ftype;
+ q->from.offset = foffset;
+ q->to.type = ttype;
+ q->to.offset = toffset;
+ q->link = p->link;
+ p->link = q;
+ return q;
}
// Sweep the prog list to mark any used nodes.
@@ -36,7 +80,7 @@ markautoused(Prog* p)
}
}
-// Fixup instructions after compactframe has moved all autos around.
+// Fixup instructions after allocauto (formerly compactframe) has moved all autos around.
void
fixautoused(Prog *p)
{
@@ -70,10 +114,29 @@ fixautoused(Prog *p)
void
ginscall(Node *f, int proc)
{
+ int32 arg;
Prog *p;
Node reg, con;
Node r1;
+ if(f->type != T)
+ setmaxarg(f->type);
+
+ arg = -1;
+ // Most functions have a fixed-size argument block, so traceback uses that during unwind.
+ // Not all, though: there are some variadic functions in package runtime,
+ // and for those we emit call-specific metadata recorded by caller.
+ // Reflect generates functions with variable argsize (see reflect.methodValueCall/makeFuncStub),
+ // so we do this for all indirect calls as well.
+ if(f->type != T && (f->sym == S || (f->sym != S && f->sym->pkg == runtimepkg) || proc == 1 || proc == 2)) {
+ arg = f->type->argwid;
+ if(proc == 1 || proc == 2)
+ arg += 2*widthptr;
+ }
+
+ if(arg != -1)
+ gargsize(arg);
+
switch(proc) {
default:
fatal("ginscall: bad proc %d", proc);
@@ -82,6 +145,19 @@ ginscall(Node *f, int proc)
case 0: // normal call
case -1: // normal call but no return
if(f->op == ONAME && f->class == PFUNC) {
+ if(f == deferreturn) {
+ // Deferred calls will appear to be returning to
+ // the CALL deferreturn(SB) that we are about to emit.
+ // However, the stack trace code will show the line
+ // of the instruction byte before the return PC.
+ // To avoid that being an unrelated instruction,
+ // insert an x86 NOP that we will have the right line number.
+ // x86 NOP 0x90 is really XCHG AX, AX; use that description
+ // because the NOP pseudo-instruction would be removed by
+ // the linker.
+ nodreg(&reg, types[TINT], D_AX);
+ gins(AXCHGL, &reg, &reg);
+ }
p = gins(ACALL, N, f);
afunclit(&p->to, f);
if(proc == -1 || noreturn(p))
@@ -130,6 +206,9 @@ ginscall(Node *f, int proc)
}
break;
}
+
+ if(arg != -1)
+ gargsize(-1);
}
/*
@@ -178,6 +257,7 @@ cgen_callinter(Node *n, Node *res, int proc)
regalloc(&nodr, types[tptr], &nodo);
if(n->left->xoffset == BADWIDTH)
fatal("cgen_callinter: badwidth");
+ cgen_checknil(&nodo); // in case offset is huge
nodo.op = OINDREG;
nodo.xoffset = n->left->xoffset + 3*widthptr + 8;
if(proc == 0) {
@@ -189,14 +269,11 @@ cgen_callinter(Node *n, Node *res, int proc)
gins(ALEAQ, &nodo, &nodr); // REG = &(32+offset(REG)) -- i.tab->fun[f]
}
- // BOTCH nodr.type = fntype;
nodr.type = n->left->type;
ginscall(&nodr, proc);
regfree(&nodr);
regfree(&nodo);
-
- setmaxarg(n->left->type);
}
/*
@@ -224,8 +301,6 @@ cgen_call(Node *n, int proc)
genlist(n->list); // assign the args
t = n->left->type;
- setmaxarg(t);
-
// call tempname pointer
if(n->left->ullman >= UINF) {
regalloc(&nod, types[tptr], N);
@@ -325,11 +400,18 @@ cgen_aret(Node *n, Node *res)
void
cgen_ret(Node *n)
{
+ Prog *p;
+
genlist(n->list); // copy out args
- if(hasdefer || curfn->exit)
+ if(hasdefer || curfn->exit) {
gjmp(retpc);
- else
- gins(ARET, N, N);
+ return;
+ }
+ p = gins(ARET, N, N);
+ if(n->op == ORETJMP) {
+ p->to.type = D_EXTERN;
+ p->to.sym = n->left->sym;
+ }
}
/*
@@ -514,7 +596,7 @@ dodiv(int op, Node *nl, Node *nr, Node *res)
check = 0;
if(issigned[t->etype]) {
check = 1;
- if(isconst(nl, CTINT) && mpgetfix(nl->val.u.xval) != -1LL<<(t->width*8-1))
+ if(isconst(nl, CTINT) && mpgetfix(nl->val.u.xval) != -(1ULL<<(t->width*8-1)))
check = 0;
else if(isconst(nr, CTINT) && mpgetfix(nr->val.u.xval) != -1)
check = 0;
@@ -987,3 +1069,51 @@ clearfat(Node *nl)
restx(&n1, &oldn1);
restx(&ax, &oldax);
}
+
+// Called after regopt and peep have run.
+// Expand CHECKNIL pseudo-op into actual nil pointer check.
+void
+expandchecks(Prog *firstp)
+{
+ Prog *p, *p1, *p2;
+
+ for(p = firstp; p != P; p = p->link) {
+ if(p->as != ACHECKNIL)
+ continue;
+ if(debug_checknil && p->lineno > 1) // p->lineno==1 in generated wrappers
+ warnl(p->lineno, "generated nil check");
+ // check is
+ // CMP arg, $0
+ // JNE 2(PC) (likely)
+ // MOV AX, 0
+ p1 = mal(sizeof *p1);
+ p2 = mal(sizeof *p2);
+ clearp(p1);
+ clearp(p2);
+ p1->link = p2;
+ p2->link = p->link;
+ p->link = p1;
+ p1->lineno = p->lineno;
+ p2->lineno = p->lineno;
+ p1->loc = 9999;
+ p2->loc = 9999;
+ p->as = ACMPQ;
+ p->to.type = D_CONST;
+ p->to.offset = 0;
+ p1->as = AJNE;
+ p1->from.type = D_CONST;
+ p1->from.offset = 1; // likely
+ p1->to.type = D_BRANCH;
+ p1->to.u.branch = p2->link;
+ // crash by write to memory address 0.
+ // if possible, since we know arg is 0, use 0(arg),
+ // which will be shorter to encode than plain 0.
+ p2->as = AMOVL;
+ p2->from.type = D_AX;
+ if(regtyp(&p->from))
+ p2->to.type = p->from.type + D_INDIR;
+ else
+ p2->to.type = D_INDIR+D_NONE;
+ p2->to.offset = 0;
+ }
+}
diff --git a/src/cmd/6g/gobj.c b/src/cmd/6g/gobj.c
index cdbbd5d9d..a9bd5e833 100644
--- a/src/cmd/6g/gobj.c
+++ b/src/cmd/6g/gobj.c
@@ -35,10 +35,9 @@
void
zname(Biobuf *b, Sym *s, int t)
{
- Bputc(b, ANAME); /* as */
- Bputc(b, ANAME>>8); /* as */
- Bputc(b, t); /* type */
- Bputc(b, s->sym); /* sym */
+ BPUTLE2(b, ANAME); /* as */
+ BPUTC(b, t); /* type */
+ BPUTC(b, s->sym); /* sym */
Bputname(b, s);
}
@@ -46,13 +45,12 @@ zname(Biobuf *b, Sym *s, int t)
void
zfile(Biobuf *b, char *p, int n)
{
- Bputc(b, ANAME);
- Bputc(b, ANAME>>8);
- Bputc(b, D_FILE);
- Bputc(b, 1);
- Bputc(b, '<');
+ BPUTLE2(b, ANAME);
+ BPUTC(b, D_FILE);
+ BPUTC(b, 1);
+ BPUTC(b, '<');
Bwrite(b, p, n);
- Bputc(b, 0);
+ BPUTC(b, 0);
}
void
@@ -60,12 +58,8 @@ zhist(Biobuf *b, int line, vlong offset)
{
Addr a;
- Bputc(b, AHISTORY);
- Bputc(b, AHISTORY>>8);
- Bputc(b, line);
- Bputc(b, line>>8);
- Bputc(b, line>>16);
- Bputc(b, line>>24);
+ BPUTLE2(b, AHISTORY);
+ BPUTLE4(b, line);
zaddr(b, &zprog.from, 0, 0);
a = zprog.to;
if(offset != 0) {
@@ -116,54 +110,40 @@ zaddr(Biobuf *b, Addr *a, int s, int gotype)
t |= T_SCONST;
break;
}
- Bputc(b, t);
+ BPUTC(b, t);
if(t & T_INDEX) { /* implies index, scale */
- Bputc(b, a->index);
- Bputc(b, a->scale);
+ BPUTC(b, a->index);
+ BPUTC(b, a->scale);
}
if(t & T_OFFSET) { /* implies offset */
l = a->offset;
- Bputc(b, l);
- Bputc(b, l>>8);
- Bputc(b, l>>16);
- Bputc(b, l>>24);
+ BPUTLE4(b, l);
if(t & T_64) {
l = a->offset>>32;
- Bputc(b, l);
- Bputc(b, l>>8);
- Bputc(b, l>>16);
- Bputc(b, l>>24);
+ BPUTLE4(b, l);
}
}
if(t & T_SYM) /* implies sym */
- Bputc(b, s);
+ BPUTC(b, s);
if(t & T_FCONST) {
ieeedtod(&e, a->u.dval);
- l = e;
- Bputc(b, l);
- Bputc(b, l>>8);
- Bputc(b, l>>16);
- Bputc(b, l>>24);
- l = e >> 32;
- Bputc(b, l);
- Bputc(b, l>>8);
- Bputc(b, l>>16);
- Bputc(b, l>>24);
+ BPUTLE4(b, e);
+ BPUTLE4(b, e >> 32);
return;
}
if(t & T_SCONST) {
n = a->u.sval;
for(i=0; i<NSNAME; i++) {
- Bputc(b, *n);
+ BPUTC(b, *n);
n++;
}
return;
}
if(t & T_TYPE)
- Bputc(b, a->type);
+ BPUTC(b, a->type);
if(t & T_GOTYPE)
- Bputc(b, gotype);
+ BPUTC(b, gotype);
}
static struct {
@@ -269,12 +249,8 @@ dumpfuncs(void)
break;
}
- Bputc(bout, p->as);
- Bputc(bout, p->as>>8);
- Bputc(bout, p->lineno);
- Bputc(bout, p->lineno>>8);
- Bputc(bout, p->lineno>>16);
- Bputc(bout, p->lineno>>24);
+ BPUTLE2(bout, p->as);
+ BPUTLE4(bout, p->lineno);
zaddr(bout, &p->from, sf, gf);
zaddr(bout, &p->to, st, gt);
}
@@ -498,114 +474,6 @@ dsymptr(Sym *s, int off, Sym *x, int xoff)
}
void
-genembedtramp(Type *rcvr, Type *method, Sym *newnam, int iface)
-{
- Sym *e;
- int c, d, mov, add, loaded;
- int64 o;
- Prog *p;
- Type *f;
-
- USED(iface);
-
- if(0 && debug['r'])
- print("genembedtramp %T %T %S\n", rcvr, method, newnam);
-
- e = method->sym;
- for(d=0; d<nelem(dotlist); d++) {
- c = adddot1(e, rcvr, d, nil, 0);
- if(c == 1)
- goto out;
- }
- fatal("genembedtramp %T.%S", rcvr, method->sym);
-
-out:
- newplist()->name = newname(newnam);
-
- //TEXT main·S_test2(SB),7,$0
- p = pc;
- gins(ATEXT, N, N);
- p->from.type = D_EXTERN;
- p->from.sym = newnam;
- p->to.type = D_CONST;
- p->to.offset = 0;
- p->from.scale = 7;
-//print("1. %P\n", p);
-
- mov = AMOVQ;
- add = AADDQ;
- loaded = 0;
- o = 0;
- for(c=d-1; c>=0; c--) {
- f = dotlist[c].field;
- o += f->width;
- if(!isptr[f->type->etype])
- continue;
- if(!loaded) {
- loaded = 1;
- //MOVQ 8(SP), AX
- p = pc;
- gins(mov, N, N);
- p->from.type = D_INDIR+D_SP;
- p->from.offset = widthptr;
- p->to.type = D_AX;
-//print("2. %P\n", p);
- }
-
- //MOVQ o(AX), AX
- p = pc;
- gins(mov, N, N);
- p->from.type = D_INDIR+D_AX;
- p->from.offset = o;
- p->to.type = D_AX;
-//print("3. %P\n", p);
- o = 0;
- }
- if(o != 0) {
- //ADDQ $XX, AX
- p = pc;
- gins(add, N, N);
- p->from.type = D_CONST;
- p->from.offset = o;
- if(loaded)
- p->to.type = D_AX;
- else {
- p->to.type = D_INDIR+D_SP;
- p->to.offset = widthptr;
- }
-//print("4. %P\n", p);
- }
-
- //MOVQ AX, 8(SP)
- if(loaded) {
- p = pc;
- gins(mov, N, N);
- p->from.type = D_AX;
- p->to.type = D_INDIR+D_SP;
- p->to.offset = widthptr;
-//print("5. %P\n", p);
- } else {
- // TODO(rsc): obviously this is unnecessary,
- // but 6l has a bug, and it can't handle
- // JMP instructions too close to the top of
- // a new function.
- gins(ANOP, N, N);
- }
-
- f = dotlist[0].field;
- //JMP main·*Sub_test2(SB)
- if(isptr[f->type->etype])
- f = f->type;
- p = pc;
- gins(AJMP, N, N);
- p->to.type = D_EXTERN;
- p->to.sym = methodsym(method->sym, ptrto(f->type), 0);
-//print("6. %P\n", p);
-
- pc->as = ARET; // overwrite AEND
-}
-
-void
nopout(Prog *p)
{
p->as = ANOP;
diff --git a/src/cmd/6g/gsubr.c b/src/cmd/6g/gsubr.c
index 55864c34e..7318909bb 100644
--- a/src/cmd/6g/gsubr.c
+++ b/src/cmd/6g/gsubr.c
@@ -31,9 +31,11 @@
#include <u.h>
#include <libc.h>
#include "gg.h"
+#include "../../pkg/runtime/funcdata.h"
// TODO(rsc): Can make this bigger if we move
// the text segment up higher in 6l for all GOOS.
+// At the same time, can raise StackBig in ../../pkg/runtime/stack.h.
vlong unmappedzero = 4096;
void
@@ -218,6 +220,16 @@ gtrack(Sym *s)
}
void
+gargsize(vlong size)
+{
+ Node n1, n2;
+
+ nodconst(&n1, types[TINT32], PCDATA_ArgSize);
+ nodconst(&n2, types[TINT32], size);
+ gins(APCDATA, &n1, &n2);
+}
+
+void
ggloblsym(Sym *s, int32 width, int dupok, int rodata)
{
Prog *p;
@@ -494,7 +506,7 @@ fp:
break;
case 2: // offset output arg
-fatal("shouldnt be used");
+fatal("shouldn't be used");
n->op = OINDREG;
n->val.u.reg = D_SP;
n->xoffset += types[tptr]->width;
@@ -528,7 +540,7 @@ ginscon(int as, vlong c, Node *n2)
nodconst(&n1, types[TINT64], c);
- if(as != AMOVQ && (c < -1LL<<31 || c >= 1LL<<31)) {
+ if(as != AMOVQ && (c < -(1LL<<31) || c >= 1LL<<31)) {
// cannot have 64-bit immediokate in ADD, etc.
// instead, MOV into register first.
regalloc(&ntmp, types[TINT64], N);
@@ -550,6 +562,7 @@ ismem(Node *n)
{
switch(n->op) {
case OITAB:
+ case OSPTR:
case OLEN:
case OCAP:
case OINDREG:
@@ -1055,41 +1068,27 @@ gins(int as, Node *f, Node *t)
return p;
}
-// Generate an instruction referencing *n
-// to force segv on nil pointer dereference.
void
-checkref(Node *n, int force)
+fixlargeoffset(Node *n)
{
- Node m;
+ Node a;
- if(!force && isptr[n->type->etype] && n->type->type->width < unmappedzero)
+ if(n == N)
return;
-
- regalloc(&m, types[TUINTPTR], n);
- cgen(n, &m);
- m.xoffset = 0;
- m.op = OINDREG;
- m.type = types[TUINT8];
- gins(ATESTB, nodintconst(0), &m);
- regfree(&m);
-}
-
-static void
-checkoffset(Addr *a, int canemitcode)
-{
- Prog *p;
-
- if(a->offset < unmappedzero)
+ if(n->op != OINDREG)
return;
- if(!canemitcode)
- fatal("checkoffset %#llx, cannot emit code", a->offset);
-
- // cannot rely on unmapped nil page at 0 to catch
- // reference with large offset. instead, emit explicit
- // test of 0(reg).
- p = gins(ATESTB, nodintconst(0), N);
- p->to = *a;
- p->to.offset = 0;
+ if(n->val.u.reg == D_SP) // stack offset cannot be large
+ return;
+ if(n->xoffset != (int32)n->xoffset) {
+ // offset too large, add to register instead.
+ a = *n;
+ a.op = OREGISTER;
+ a.type = types[tptr];
+ a.xoffset = 0;
+ cgen_checknil(&a);
+ ginscon(optoas(OADD, types[tptr]), n->xoffset, &a);
+ n->xoffset = 0;
+ }
}
/*
@@ -1149,7 +1148,6 @@ naddr(Node *n, Addr *a, int canemitcode)
a->offset = n->xoffset;
if(a->offset != (int32)a->offset)
yyerror("offset %lld too large for OINDREG", a->offset);
- checkoffset(a, canemitcode);
break;
case OPARAM:
@@ -1268,8 +1266,16 @@ naddr(Node *n, Addr *a, int canemitcode)
break; // itab(nil)
a->etype = tptr;
a->width = widthptr;
- if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero)
- checkoffset(a, canemitcode);
+ break;
+
+ case OSPTR:
+ // pointer in a string or slice
+ naddr(n->left, a, canemitcode);
+ if(a->type == D_CONST && a->offset == 0)
+ break; // ptr(nil)
+ a->etype = simtype[TUINTPTR];
+ a->offset += Array_array;
+ a->width = widthptr;
break;
case OLEN:
@@ -1280,8 +1286,6 @@ naddr(Node *n, Addr *a, int canemitcode)
a->etype = simtype[TUINT];
a->offset += Array_nel;
a->width = widthint;
- if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero)
- checkoffset(a, canemitcode);
break;
case OCAP:
@@ -1292,8 +1296,6 @@ naddr(Node *n, Addr *a, int canemitcode)
a->etype = simtype[TUINT];
a->offset += Array_cap;
a->width = widthint;
- if(a->offset >= unmappedzero && a->offset-Array_cap < unmappedzero)
- checkoffset(a, canemitcode);
break;
// case OADD:
@@ -2033,18 +2035,21 @@ odot:
n1.xoffset = oary[0];
} else {
cgen(nn, reg);
+ cgen_checknil(reg);
n1.xoffset = -(oary[0]+1);
}
for(i=1; i<o; i++) {
if(oary[i] >= 0)
- fatal("cant happen");
+ fatal("can't happen");
gins(AMOVQ, &n1, reg);
+ cgen_checknil(reg);
n1.xoffset = -(oary[i]+1);
}
a->type = D_NONE;
a->index = D_NONE;
+ fixlargeoffset(&n1);
naddr(&n1, a, 1);
goto yes;
@@ -2105,16 +2110,6 @@ oindex:
o |= OAddable;
}
- if(!(o & ODynam) && l->type->width >= unmappedzero && l->op == OIND) {
- // cannot rely on page protections to
- // catch array ptr == 0, so dereference.
- n2 = *reg;
- n2.xoffset = 0;
- n2.op = OINDREG;
- n2.type = types[TUINT8];
- gins(ATESTB, nodintconst(0), &n2);
- }
-
// check bounds
if(!debug['B'] && !n->bounded) {
// check bounds
@@ -2216,6 +2211,7 @@ oindex_const:
n2 = *reg;
n2.op = OINDREG;
n2.xoffset = v*w;
+ fixlargeoffset(&n2);
a->type = D_NONE;
a->index = D_NONE;
naddr(&n2, a, 1);
@@ -2228,6 +2224,7 @@ oindex_const:
reg->op = OREGISTER;
}
n1.xoffset += v*w;
+ fixlargeoffset(&n1);
a->type = D_NONE;
a->index= D_NONE;
naddr(&n1, a, 1);
@@ -2263,6 +2260,7 @@ oindex_const_sudo:
n2 = *reg;
n2.op = OINDREG;
n2.xoffset = v*w;
+ fixlargeoffset(&n2);
a->type = D_NONE;
a->index = D_NONE;
naddr(&n2, a, 1);
diff --git a/src/cmd/6g/opt.h b/src/cmd/6g/opt.h
index 9b0ea1b5a..3dcc3d747 100644
--- a/src/cmd/6g/opt.h
+++ b/src/cmd/6g/opt.h
@@ -28,6 +28,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#include "../gc/popt.h"
+
#define Z N
#define Adr Addr
@@ -50,9 +52,10 @@ typedef struct Rgn Rgn;
// A Reg is a wrapper around a single Prog (one instruction) that holds
// register optimization information while the optimizer runs.
// r->prog is the instruction.
-// r->prog->regp points back to r.
+// r->prog->opt points back to r.
struct Reg
{
+ Flow f;
Bits set; // variables written by this instruction.
Bits use1; // variables read by prog->from.
@@ -66,19 +69,6 @@ struct Reg
Bits act;
int32 regu; // register used bitmap
- int32 rpo; // reverse post ordering
- int32 active;
-
- uint16 loop; // x5 for every loop
- uchar refset; // diagnostic generated
-
- Reg* p1; // predecessors of this instruction: p1,
- Reg* p2; // and then p2 linked though p2link.
- Reg* p2link;
- Reg* s1; // successors of this instruction (at most two: s1 and s2).
- Reg* s2;
- Reg* link; // next instruction in function code
- Prog* prog; // actual instruction
};
#define R ((Reg*)0)
@@ -93,11 +83,7 @@ struct Rgn
EXTERN int32 exregoffset; // not set
EXTERN int32 exfregoffset; // not set
-EXTERN Reg* firstr;
-EXTERN Reg* lastr;
EXTERN Reg zreg;
-EXTERN Reg* freer;
-EXTERN Reg** rpo2r;
EXTERN Rgn region[NRGN];
EXTERN Rgn* rgp;
EXTERN int nregion;
@@ -111,7 +97,6 @@ EXTERN Bits addrs;
EXTERN Bits ovar;
EXTERN int change;
EXTERN int32 maxnr;
-EXTERN int32* idom;
EXTERN struct
{
@@ -126,43 +111,89 @@ EXTERN struct
/*
* reg.c
*/
-Reg* rega(void);
int rcmp(const void*, const void*);
void regopt(Prog*);
void addmove(Reg*, int, int, int);
Bits mkvar(Reg*, Adr*);
void prop(Reg*, Bits, Bits);
-void loopit(Reg*, int32);
void synch(Reg*, Bits);
uint32 allreg(uint32, Rgn*);
void paint1(Reg*, int);
uint32 paint2(Reg*, int);
void paint3(Reg*, int, int32, int);
void addreg(Adr*, int);
-void dumpone(Reg*);
-void dumpit(char*, Reg*);
-int noreturn(Prog *p);
+void dumpone(Flow*, int);
+void dumpit(char*, Flow*, int);
/*
* peep.c
*/
-void peep(void);
-void excise(Reg*);
-Reg* uniqp(Reg*);
-Reg* uniqs(Reg*);
-int regtyp(Adr*);
-int anyvar(Adr*);
-int subprop(Reg*);
-int copyprop(Reg*);
-int copy1(Adr*, Adr*, Reg*, int);
+void peep(Prog*);
+void excise(Flow*);
int copyu(Prog*, Adr*, Adr*);
-int copyas(Adr*, Adr*);
-int copyau(Adr*, Adr*);
-int copysub(Adr*, Adr*, Adr*, int);
-int copysub1(Prog*, Adr*, Adr*, int);
-
int32 RtoB(int);
int32 FtoB(int);
int BtoR(int32);
int BtoF(int32);
+
+/*
+ * prog.c
+ */
+typedef struct ProgInfo ProgInfo;
+struct ProgInfo
+{
+ uint32 flags; // the bits below
+ uint32 reguse; // required registers used by this instruction
+ uint32 regset; // required registers set by this instruction
+ uint32 regindex; // registers used by addressing mode
+};
+
+enum
+{
+ // Pseudo-op, like TEXT, GLOBL, TYPE, PCDATA, FUNCDATA.
+ Pseudo = 1<<1,
+
+ // There's nothing to say about the instruction,
+ // but it's still okay to see.
+ OK = 1<<2,
+
+ // Size of right-side write, or right-side read if no write.
+ SizeB = 1<<3,
+ SizeW = 1<<4,
+ SizeL = 1<<5,
+ SizeQ = 1<<6,
+ SizeF = 1<<7, // float aka float32
+ SizeD = 1<<8, // double aka float64
+
+ // Left side: address taken, read, write.
+ LeftAddr = 1<<9,
+ LeftRead = 1<<10,
+ LeftWrite = 1<<11,
+
+ // Right side: address taken, read, write.
+ RightAddr = 1<<12,
+ RightRead = 1<<13,
+ RightWrite = 1<<14,
+
+ // Set, use, or kill of carry bit.
+ // Kill means we never look at the carry bit after this kind of instruction.
+ SetCarry = 1<<15,
+ UseCarry = 1<<16,
+ KillCarry = 1<<17,
+
+ // Instruction kinds
+ Move = 1<<18, // straight move
+ Conv = 1<<19, // size conversion
+ Cjmp = 1<<20, // conditional jump
+ Break = 1<<21, // breaks control flow (no fallthrough)
+ Call = 1<<22, // function call
+ Jump = 1<<23, // jump
+ Skip = 1<<24, // data instruction
+
+ // Special cases for register use.
+ ShiftCX = 1<<25, // possible shift by CX
+ ImulAXDX = 1<<26, // possible multiply into DX:AX
+};
+
+void proginfo(ProgInfo*, Prog*);
diff --git a/src/cmd/6g/peep.c b/src/cmd/6g/peep.c
index cd2881ec4..5ccf90103 100644
--- a/src/cmd/6g/peep.c
+++ b/src/cmd/6g/peep.c
@@ -33,61 +33,50 @@
#include "gg.h"
#include "opt.h"
-static void conprop(Reg *r);
-static void elimshortmov(Reg *r);
-static int prevl(Reg *r, int reg);
-static void pushback(Reg *r);
-static int regconsttyp(Adr*);
+static void conprop(Flow *r);
+static void elimshortmov(Graph *g);
+static int prevl(Flow *r, int reg);
+static void pushback(Flow *r);
+static int regconsttyp(Adr*);
+static int subprop(Flow*);
+static int copyprop(Graph*, Flow*);
+static int copy1(Adr*, Adr*, Flow*, int);
+static int copyas(Adr*, Adr*);
+static int copyau(Adr*, Adr*);
+static int copysub(Adr*, Adr*, Adr*, int);
+
+static uint32 gactive;
// do we need the carry bit
static int
needc(Prog *p)
{
+ ProgInfo info;
+
while(p != P) {
- switch(p->as) {
- case AADCL:
- case AADCQ:
- case ASBBL:
- case ASBBQ:
- case ARCRB:
- case ARCRW:
- case ARCRL:
- case ARCRQ:
+ proginfo(&info, p);
+ if(info.flags & UseCarry)
return 1;
- case AADDB:
- case AADDW:
- case AADDL:
- case AADDQ:
- case ASUBB:
- case ASUBW:
- case ASUBL:
- case ASUBQ:
- case AJMP:
- case ARET:
- case ACALL:
+ if(info.flags & (SetCarry|KillCarry))
return 0;
- default:
- if(p->to.type == D_BRANCH)
- return 0;
- }
p = p->link;
}
return 0;
}
-static Reg*
-rnops(Reg *r)
+static Flow*
+rnops(Flow *r)
{
Prog *p;
- Reg *r1;
+ Flow *r1;
- if(r != R)
+ if(r != nil)
for(;;) {
p = r->prog;
if(p->as != ANOP || p->from.type != D_NONE || p->to.type != D_NONE)
break;
r1 = uniqs(r);
- if(r1 == R)
+ if(r1 == nil)
break;
r = r1;
}
@@ -95,57 +84,26 @@ rnops(Reg *r)
}
void
-peep(void)
+peep(Prog *firstp)
{
- Reg *r, *r1, *r2;
+ Flow *r, *r1;
+ Graph *g;
Prog *p, *p1;
int t;
- /*
- * complete R structure
- */
- t = 0;
- for(r=firstr; r!=R; r=r1) {
- r1 = r->link;
- if(r1 == R)
- break;
- p = r->prog->link;
- while(p != r1->prog)
- switch(p->as) {
- default:
- r2 = rega();
- r->link = r2;
- r2->link = r1;
-
- r2->prog = p;
- p->reg = r2;
-
- r2->p1 = r;
- r->s1 = r2;
- r2->s1 = r1;
- r1->p1 = r2;
-
- r = r2;
- t++;
-
- case ADATA:
- case AGLOBL:
- case ANAME:
- case ASIGNAME:
- case ALOCALS:
- case ATYPE:
- p = p->link;
- }
- }
-
+ g = flowstart(firstp, sizeof(Flow));
+ if(g == nil)
+ return;
+ gactive = 0;
+
// byte, word arithmetic elimination.
- elimshortmov(r);
+ elimshortmov(g);
// constant propagation
// find MOV $con,R followed by
// another MOV $con,R without
// setting R in the interim
- for(r=firstr; r!=R; r=r->link) {
+ for(r=g->start; r!=nil; r=r->link) {
p = r->prog;
switch(p->as) {
case ALEAL:
@@ -171,10 +129,10 @@ peep(void)
loop1:
if(debug['P'] && debug['v'])
- dumpit("loop1", firstr);
+ dumpit("loop1", g->start, 0);
t = 0;
- for(r=firstr; r!=R; r=r->link) {
+ for(r=g->start; r!=nil; r=r->link) {
p = r->prog;
switch(p->as) {
case AMOVL:
@@ -183,11 +141,11 @@ loop1:
case AMOVSD:
if(regtyp(&p->to))
if(regtyp(&p->from)) {
- if(copyprop(r)) {
+ if(copyprop(g, r)) {
excise(r);
t++;
} else
- if(subprop(r) && copyprop(r)) {
+ if(subprop(r) && copyprop(g, r)) {
excise(r);
t++;
}
@@ -200,7 +158,7 @@ loop1:
case AMOVWLSX:
if(regtyp(&p->to)) {
r1 = rnops(uniqs(r));
- if(r1 != R) {
+ if(r1 != nil) {
p1 = r1->prog;
if(p->as == p1->as && p->to.type == p1->from.type){
p1->as = AMOVL;
@@ -219,7 +177,7 @@ loop1:
case AMOVQL:
if(regtyp(&p->to)) {
r1 = rnops(uniqs(r));
- if(r1 != R) {
+ if(r1 != nil) {
p1 = r1->prog;
if(p->as == p1->as && p->to.type == p1->from.type){
p1->as = AMOVQ;
@@ -302,7 +260,7 @@ loop1:
// can be replaced by MOVAPD, which moves the pair of float64s
// instead of just the lower one. We only use the lower one, but
// the processor can do better if we do moves using both.
- for(r=firstr; r!=R; r=r->link) {
+ for(r=g->start; r!=nil; r=r->link) {
p = r->prog;
if(p->as == AMOVLQZX)
if(regtyp(&p->from))
@@ -319,7 +277,7 @@ loop1:
// load pipelining
// push any load from memory as early as possible
// to give it time to complete before use.
- for(r=firstr; r!=R; r=r->link) {
+ for(r=g->start; r!=nil; r=r->link) {
p = r->prog;
switch(p->as) {
case AMOVB:
@@ -331,17 +289,19 @@ loop1:
pushback(r);
}
}
+
+ flowend(g);
}
static void
-pushback(Reg *r0)
+pushback(Flow *r0)
{
- Reg *r, *b;
+ Flow *r, *b;
Prog *p0, *p, t;
- b = R;
+ b = nil;
p0 = r0->prog;
- for(r=uniqp(r0); r!=R && uniqs(r)!=R; r=uniqp(r)) {
+ for(r=uniqp(r0); r!=nil && uniqs(r)!=nil; r=uniqp(r)) {
p = r->prog;
if(p->as != ANOP) {
if(!regconsttyp(&p->from) || !regtyp(&p->to))
@@ -354,11 +314,11 @@ pushback(Reg *r0)
b = r;
}
- if(b == R) {
+ if(b == nil) {
if(debug['v']) {
print("no pushback: %P\n", r0->prog);
if(r)
- print("\t%P [%d]\n", r->prog, uniqs(r)!=R);
+ print("\t%P [%d]\n", r->prog, uniqs(r)!=nil);
}
return;
}
@@ -401,7 +361,7 @@ pushback(Reg *r0)
}
void
-excise(Reg *r)
+excise(Flow *r)
{
Prog *p;
@@ -416,38 +376,6 @@ excise(Reg *r)
ostats.ndelmov++;
}
-Reg*
-uniqp(Reg *r)
-{
- Reg *r1;
-
- r1 = r->p1;
- if(r1 == R) {
- r1 = r->p2;
- if(r1 == R || r1->p2link != R)
- return R;
- } else
- if(r->p2 != R)
- return R;
- return r1;
-}
-
-Reg*
-uniqs(Reg *r)
-{
- Reg *r1;
-
- r1 = r->s1;
- if(r1 == R) {
- r1 = r->s2;
- if(r1 == R)
- return R;
- } else
- if(r->s2 != R)
- return R;
- return r1;
-}
-
int
regtyp(Adr *a)
{
@@ -468,13 +396,16 @@ regtyp(Adr *a)
// when possible. a movb into a register
// can smash the entire 32-bit register without
// causing any trouble.
+//
+// TODO: Using the Q forms here instead of the L forms
+// seems unnecessary, and it makes the instructions longer.
static void
-elimshortmov(Reg *r)
+elimshortmov(Graph *g)
{
Prog *p;
+ Flow *r;
- USED(r);
- for(r=firstr; r!=R; r=r->link) {
+ for(r=g->start; r!=nil; r=r->link) {
p = r->prog;
if(regtyp(&p->to)) {
switch(p->as) {
@@ -557,6 +488,7 @@ elimshortmov(Reg *r)
}
}
+// is 'a' a register or constant?
static int
regconsttyp(Adr *a)
{
@@ -574,38 +506,21 @@ regconsttyp(Adr *a)
// is reg guaranteed to be truncated by a previous L instruction?
static int
-prevl(Reg *r0, int reg)
+prevl(Flow *r0, int reg)
{
Prog *p;
- Reg *r;
+ Flow *r;
+ ProgInfo info;
- for(r=uniqp(r0); r!=R; r=uniqp(r)) {
+ for(r=uniqp(r0); r!=nil; r=uniqp(r)) {
p = r->prog;
if(p->to.type == reg) {
- switch(p->as) {
- case AADDL:
- case AANDL:
- case ADECL:
- case ADIVL:
- case AIDIVL:
- case AIMULL:
- case AINCL:
- case AMOVL:
- case AMULL:
- case AORL:
- case ARCLL:
- case ARCRL:
- case AROLL:
- case ARORL:
- case ASALL:
- case ASARL:
- case ASHLL:
- case ASHRL:
- case ASUBL:
- case AXORL:
- return 1;
+ proginfo(&info, p);
+ if(info.flags & RightWrite) {
+ if(info.flags & SizeL)
+ return 1;
+ return 0;
}
- return 0;
}
}
return 0;
@@ -625,12 +540,13 @@ prevl(Reg *r0, int reg)
* hopefully, then the former or latter MOV
* will be eliminated by copy propagation.
*/
-int
-subprop(Reg *r0)
+static int
+subprop(Flow *r0)
{
Prog *p;
+ ProgInfo info;
Adr *v1, *v2;
- Reg *r;
+ Flow *r;
int t;
if(debug['P'] && debug['v'])
@@ -648,104 +564,31 @@ subprop(Reg *r0)
print("\tnot regtype %D; return 0\n", v2);
return 0;
}
- for(r=uniqp(r0); r!=R; r=uniqp(r)) {
+ for(r=uniqp(r0); r!=nil; r=uniqp(r)) {
if(debug['P'] && debug['v'])
print("\t? %P\n", r->prog);
- if(uniqs(r) == R) {
+ if(uniqs(r) == nil) {
if(debug['P'] && debug['v'])
print("\tno unique successor\n");
break;
}
p = r->prog;
- switch(p->as) {
- case ACALL:
+ proginfo(&info, p);
+ if(info.flags & Call) {
if(debug['P'] && debug['v'])
print("\tfound %P; return 0\n", p);
return 0;
+ }
- case AIMULL:
- case AIMULQ:
- case AIMULW:
- if(p->to.type != D_NONE)
- break;
- goto giveup;
-
- case ARCLB:
- case ARCLL:
- case ARCLQ:
- case ARCLW:
- case ARCRB:
- case ARCRL:
- case ARCRQ:
- case ARCRW:
- case AROLB:
- case AROLL:
- case AROLQ:
- case AROLW:
- case ARORB:
- case ARORL:
- case ARORQ:
- case ARORW:
- case ASALB:
- case ASALL:
- case ASALQ:
- case ASALW:
- case ASARB:
- case ASARL:
- case ASARQ:
- case ASARW:
- case ASHLB:
- case ASHLL:
- case ASHLQ:
- case ASHLW:
- case ASHRB:
- case ASHRL:
- case ASHRQ:
- case ASHRW:
- if(p->from.type == D_CONST)
- break;
- goto giveup;
-
- case ADIVB:
- case ADIVL:
- case ADIVQ:
- case ADIVW:
- case AIDIVB:
- case AIDIVL:
- case AIDIVQ:
- case AIDIVW:
- case AIMULB:
- case AMULB:
- case AMULL:
- case AMULQ:
- case AMULW:
-
- case AREP:
- case AREPN:
-
- case ACWD:
- case ACDQ:
- case ACQO:
-
- case ASTOSB:
- case ASTOSL:
- case ASTOSQ:
- case AMOVSB:
- case AMOVSL:
- case AMOVSQ:
- giveup:
+ if(info.reguse | info.regset) {
if(debug['P'] && debug['v'])
print("\tfound %P; return 0\n", p);
return 0;
-
- case AMOVL:
- case AMOVQ:
- case AMOVSS:
- case AMOVSD:
- if(p->to.type == v1->type)
- goto gotit;
- break;
}
+
+ if((info.flags & Move) && (info.flags & (SizeL|SizeQ|SizeF|SizeD)) && p->to.type == v1->type)
+ goto gotit;
+
if(copyau(&p->from, v2) ||
copyau(&p->to, v2)) {
if(debug['P'] && debug['v'])
@@ -798,13 +641,13 @@ gotit:
* set v1 F=1
* set v2 return success
*/
-int
-copyprop(Reg *r0)
+static int
+copyprop(Graph *g, Flow *r0)
{
Prog *p;
Adr *v1, *v2;
- Reg *r;
+ USED(g);
if(debug['P'] && debug['v'])
print("copyprop %P\n", r0->prog);
p = r0->prog;
@@ -812,37 +655,36 @@ copyprop(Reg *r0)
v2 = &p->to;
if(copyas(v1, v2))
return 1;
- for(r=firstr; r!=R; r=r->link)
- r->active = 0;
+ gactive++;
return copy1(v1, v2, r0->s1, 0);
}
-int
-copy1(Adr *v1, Adr *v2, Reg *r, int f)
+static int
+copy1(Adr *v1, Adr *v2, Flow *r, int f)
{
int t;
Prog *p;
- if(r->active) {
+ if(r->active == gactive) {
if(debug['P'])
print("act set; return 1\n");
return 1;
}
- r->active = 1;
+ r->active = gactive;
if(debug['P'])
print("copy %D->%D f=%d\n", v1, v2, f);
- for(; r != R; r = r->s1) {
+ for(; r != nil; r = r->s1) {
p = r->prog;
if(debug['P'])
print("%P", p);
- if(!f && uniqp(r) == R) {
+ if(!f && uniqp(r) == nil) {
f = 1;
if(debug['P'])
print("; merge; f=%d", f);
}
t = copyu(p, v2, A);
switch(t) {
- case 2: /* rar, cant split */
+ case 2: /* rar, can't split */
if(debug['P'])
print("; %D rar; return 0\n", v2);
return 0;
@@ -905,255 +747,10 @@ copy1(Adr *v1, Adr *v2, Reg *r, int f)
int
copyu(Prog *p, Adr *v, Adr *s)
{
+ ProgInfo info;
switch(p->as) {
-
- default:
- if(debug['P'])
- print("unknown op %A\n", p->as);
- /* SBBL; ADCL; FLD1; SAHF */
- return 2;
-
-
- case ANEGB:
- case ANEGW:
- case ANEGL:
- case ANEGQ:
- case ANOTB:
- case ANOTW:
- case ANOTL:
- case ANOTQ:
- if(copyas(&p->to, v))
- return 2;
- break;
-
- case ALEAL: /* lhs addr, rhs store */
- case ALEAQ:
- if(copyas(&p->from, v))
- return 2;
-
-
- case ANOP: /* rhs store */
- case AMOVL:
- case AMOVQ:
- case AMOVBLSX:
- case AMOVBLZX:
- case AMOVBQSX:
- case AMOVBQZX:
- case AMOVLQSX:
- case AMOVLQZX:
- case AMOVWLSX:
- case AMOVWLZX:
- case AMOVWQSX:
- case AMOVWQZX:
- case AMOVQL:
-
- case AMOVSS:
- case AMOVSD:
- case ACVTSD2SL:
- case ACVTSD2SQ:
- case ACVTSD2SS:
- case ACVTSL2SD:
- case ACVTSL2SS:
- case ACVTSQ2SD:
- case ACVTSQ2SS:
- case ACVTSS2SD:
- case ACVTSS2SL:
- case ACVTSS2SQ:
- case ACVTTSD2SL:
- case ACVTTSD2SQ:
- case ACVTTSS2SL:
- case ACVTTSS2SQ:
- if(copyas(&p->to, v)) {
- if(s != A)
- return copysub(&p->from, v, s, 1);
- if(copyau(&p->from, v))
- return 4;
- return 3;
- }
- goto caseread;
-
- case ARCLB:
- case ARCLL:
- case ARCLQ:
- case ARCLW:
- case ARCRB:
- case ARCRL:
- case ARCRQ:
- case ARCRW:
- case AROLB:
- case AROLL:
- case AROLQ:
- case AROLW:
- case ARORB:
- case ARORL:
- case ARORQ:
- case ARORW:
- case ASALB:
- case ASALL:
- case ASALQ:
- case ASALW:
- case ASARB:
- case ASARL:
- case ASARQ:
- case ASARW:
- case ASHLB:
- case ASHLL:
- case ASHLQ:
- case ASHLW:
- case ASHRB:
- case ASHRL:
- case ASHRQ:
- case ASHRW:
- if(copyas(&p->to, v))
- return 2;
- if(copyas(&p->from, v))
- if(p->from.type == D_CX)
- return 2;
- goto caseread;
-
- case AADDB: /* rhs rar */
- case AADDL:
- case AADDQ:
- case AADDW:
- case AANDB:
- case AANDL:
- case AANDQ:
- case AANDW:
- case ADECL:
- case ADECQ:
- case ADECW:
- case AINCL:
- case AINCQ:
- case AINCW:
- case ASUBB:
- case ASUBL:
- case ASUBQ:
- case ASUBW:
- case AORB:
- case AORL:
- case AORQ:
- case AORW:
- case AXORB:
- case AXORL:
- case AXORQ:
- case AXORW:
- case AMOVB:
- case AMOVW:
-
- case AADDSD:
- case AADDSS:
- case ACMPSD:
- case ACMPSS:
- case ADIVSD:
- case ADIVSS:
- case AMAXSD:
- case AMAXSS:
- case AMINSD:
- case AMINSS:
- case AMULSD:
- case AMULSS:
- case ARCPSS:
- case ARSQRTSS:
- case ASQRTSD:
- case ASQRTSS:
- case ASUBSD:
- case ASUBSS:
- case AXORPD:
- if(copyas(&p->to, v))
- return 2;
- goto caseread;
-
- case ACMPL: /* read only */
- case ACMPW:
- case ACMPB:
- case ACMPQ:
-
- case ACOMISD:
- case ACOMISS:
- case AUCOMISD:
- case AUCOMISS:
- caseread:
- if(s != A) {
- if(copysub(&p->from, v, s, 1))
- return 1;
- return copysub(&p->to, v, s, 1);
- }
- if(copyau(&p->from, v))
- return 1;
- if(copyau(&p->to, v))
- return 1;
- break;
-
- case AJGE: /* no reference */
- case AJNE:
- case AJLE:
- case AJEQ:
- case AJHI:
- case AJLS:
- case AJMI:
- case AJPL:
- case AJGT:
- case AJLT:
- case AJCC:
- case AJCS:
-
- case AADJSP:
- case AWAIT:
- case ACLD:
- break;
-
- case AIMULL:
- case AIMULQ:
- case AIMULW:
- if(p->to.type != D_NONE) {
- if(copyas(&p->to, v))
- return 2;
- goto caseread;
- }
-
- case ADIVB:
- case ADIVL:
- case ADIVQ:
- case ADIVW:
- case AIDIVB:
- case AIDIVL:
- case AIDIVQ:
- case AIDIVW:
- case AIMULB:
- case AMULB:
- case AMULL:
- case AMULQ:
- case AMULW:
-
- case ACWD:
- case ACDQ:
- case ACQO:
- if(v->type == D_AX || v->type == D_DX)
- return 2;
- goto caseread;
-
- case AREP:
- case AREPN:
- if(v->type == D_CX)
- return 2;
- goto caseread;
-
- case AMOVSB:
- case AMOVSL:
- case AMOVSQ:
- if(v->type == D_DI || v->type == D_SI)
- return 2;
- goto caseread;
-
- case ASTOSB:
- case ASTOSL:
- case ASTOSQ:
- if(v->type == D_AX || v->type == D_DI)
- return 2;
- goto caseread;
-
- case AJMP: /* funny */
+ case AJMP:
if(s != A) {
if(copysub(&p->to, v, s, 1))
return 1;
@@ -1163,12 +760,12 @@ copyu(Prog *p, Adr *v, Adr *s)
return 1;
return 0;
- case ARET: /* funny */
+ case ARET:
if(s != A)
return 1;
return 3;
- case ACALL: /* funny */
+ case ACALL:
if(REGEXT && v->type <= REGEXT && v->type > exregoffset)
return 2;
if(REGARG >= 0 && v->type == (uchar)REGARG)
@@ -1185,11 +782,47 @@ copyu(Prog *p, Adr *v, Adr *s)
return 4;
return 3;
- case ATEXT: /* funny */
+ case ATEXT:
if(REGARG >= 0 && v->type == (uchar)REGARG)
return 3;
return 0;
}
+
+ proginfo(&info, p);
+
+ if((info.reguse|info.regset) & RtoB(v->type))
+ return 2;
+
+ if(info.flags & LeftAddr)
+ if(copyas(&p->from, v))
+ return 2;
+
+ if((info.flags & (RightRead|RightWrite)) == (RightRead|RightWrite))
+ if(copyas(&p->to, v))
+ return 2;
+
+ if(info.flags & RightWrite) {
+ if(copyas(&p->to, v)) {
+ if(s != A)
+ return copysub(&p->from, v, s, 1);
+ if(copyau(&p->from, v))
+ return 4;
+ return 3;
+ }
+ }
+
+ if(info.flags & (LeftAddr|LeftRead|LeftWrite|RightAddr|RightRead|RightWrite)) {
+ if(s != A) {
+ if(copysub(&p->from, v, s, 1))
+ return 1;
+ return copysub(&p->to, v, s, 1);
+ }
+ if(copyau(&p->from, v))
+ return 1;
+ if(copyau(&p->to, v))
+ return 1;
+ }
+
return 0;
}
@@ -1198,7 +831,7 @@ copyu(Prog *p, Adr *v, Adr *s)
* could be set/use depending on
* semantics
*/
-int
+static int
copyas(Adr *a, Adr *v)
{
if(a->type != v->type)
@@ -1211,10 +844,23 @@ copyas(Adr *a, Adr *v)
return 0;
}
+int
+sameaddr(Addr *a, Addr *v)
+{
+ if(a->type != v->type)
+ return 0;
+ if(regtyp(v))
+ return 1;
+ if(v->type == D_AUTO || v->type == D_PARAM)
+ if(v->offset == a->offset)
+ return 1;
+ return 0;
+}
+
/*
* either direct or indirect
*/
-int
+static int
copyau(Adr *a, Adr *v)
{
@@ -1242,7 +888,7 @@ copyau(Adr *a, Adr *v)
* substitute s for v in a
* return failure to substitute
*/
-int
+static int
copysub(Adr *a, Adr *v, Adr *s, int f)
{
int t;
@@ -1275,9 +921,9 @@ copysub(Adr *a, Adr *v, Adr *s, int f)
}
static void
-conprop(Reg *r0)
+conprop(Flow *r0)
{
- Reg *r;
+ Flow *r;
Prog *p, *p0;
int t;
Adr *v0;
@@ -1288,9 +934,9 @@ conprop(Reg *r0)
loop:
r = uniqs(r);
- if(r == R || r == r0)
+ if(r == nil || r == r0)
return;
- if(uniqp(r) == R)
+ if(uniqp(r) == nil)
return;
p = r->prog;
@@ -1318,3 +964,18 @@ loop:
break;
}
}
+
+int
+smallindir(Addr *a, Addr *reg)
+{
+ return regtyp(reg) &&
+ a->type == D_INDIR + reg->type &&
+ a->index == D_NONE &&
+ 0 <= a->offset && a->offset < 4096;
+}
+
+int
+stackaddr(Addr *a)
+{
+ return regtyp(a) && a->type == D_SP;
+}
diff --git a/src/cmd/6g/prog.c b/src/cmd/6g/prog.c
new file mode 100644
index 000000000..90571a21a
--- /dev/null
+++ b/src/cmd/6g/prog.c
@@ -0,0 +1,313 @@
+// Copyright 2013 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 <u.h>
+#include <libc.h>
+#include "gg.h"
+#include "opt.h"
+
+// Matches real RtoB but can be used in global initializer.
+#define RtoB(r) (1<<((r)-D_AX))
+
+enum {
+ AX = RtoB(D_AX),
+ BX = RtoB(D_BX),
+ CX = RtoB(D_CX),
+ DX = RtoB(D_DX),
+ DI = RtoB(D_DI),
+ SI = RtoB(D_SI),
+
+ LeftRdwr = LeftRead | LeftWrite,
+ RightRdwr = RightRead | RightWrite,
+};
+
+#undef RtoB
+
+// This table gives the basic information about instruction
+// generated by the compiler and processed in the optimizer.
+// See opt.h for bit definitions.
+//
+// Instructions not generated need not be listed.
+// As an exception to that rule, we typically write down all the
+// size variants of an operation even if we just use a subset.
+//
+// The table is formatted for 8-space tabs.
+static ProgInfo progtable[ALAST] = {
+ [ATYPE]= {Pseudo | Skip},
+ [ATEXT]= {Pseudo},
+ [AFUNCDATA]= {Pseudo},
+ [APCDATA]= {Pseudo},
+ [AUNDEF]= {OK},
+ [AUSEFIELD]= {OK},
+ [ACHECKNIL]= {LeftRead},
+
+ // NOP is an internal no-op that also stands
+ // for USED and SET annotations, not the Intel opcode.
+ [ANOP]= {LeftRead | RightWrite},
+
+ [AADCL]= {SizeL | LeftRead | RightRdwr | SetCarry | UseCarry},
+ [AADCQ]= {SizeQ | LeftRead | RightRdwr | SetCarry | UseCarry},
+ [AADCW]= {SizeW | LeftRead | RightRdwr | SetCarry | UseCarry},
+
+ [AADDB]= {SizeB | LeftRead | RightRdwr | SetCarry},
+ [AADDL]= {SizeL | LeftRead | RightRdwr | SetCarry},
+ [AADDW]= {SizeW | LeftRead | RightRdwr | SetCarry},
+ [AADDQ]= {SizeQ | LeftRead | RightRdwr | SetCarry},
+
+ [AADDSD]= {SizeD | LeftRead | RightRdwr},
+ [AADDSS]= {SizeF | LeftRead | RightRdwr},
+
+ [AANDB]= {SizeB | LeftRead | RightRdwr | SetCarry},
+ [AANDL]= {SizeL | LeftRead | RightRdwr | SetCarry},
+ [AANDQ]= {SizeQ | LeftRead | RightRdwr | SetCarry},
+ [AANDW]= {SizeW | LeftRead | RightRdwr | SetCarry},
+
+ [ACALL]= {RightAddr | Call | KillCarry},
+
+ [ACDQ]= {OK, AX, AX | DX},
+ [ACQO]= {OK, AX, AX | DX},
+ [ACWD]= {OK, AX, AX | DX},
+
+ [ACLD]= {OK},
+ [ASTD]= {OK},
+
+ [ACMPB]= {SizeB | LeftRead | RightRead | SetCarry},
+ [ACMPL]= {SizeL | LeftRead | RightRead | SetCarry},
+ [ACMPQ]= {SizeQ | LeftRead | RightRead | SetCarry},
+ [ACMPW]= {SizeW | LeftRead | RightRead | SetCarry},
+
+ [ACOMISD]= {SizeD | LeftRead | RightRead | SetCarry},
+ [ACOMISS]= {SizeF | LeftRead | RightRead | SetCarry},
+
+ [ACVTSD2SL]= {SizeL | LeftRead | RightWrite | Conv},
+ [ACVTSD2SQ]= {SizeQ | LeftRead | RightWrite | Conv},
+ [ACVTSD2SS]= {SizeF | LeftRead | RightWrite | Conv},
+ [ACVTSL2SD]= {SizeD | LeftRead | RightWrite | Conv},
+ [ACVTSL2SS]= {SizeF | LeftRead | RightWrite | Conv},
+ [ACVTSQ2SD]= {SizeD | LeftRead | RightWrite | Conv},
+ [ACVTSQ2SS]= {SizeF | LeftRead | RightWrite | Conv},
+ [ACVTSS2SD]= {SizeD | LeftRead | RightWrite | Conv},
+ [ACVTSS2SL]= {SizeL | LeftRead | RightWrite | Conv},
+ [ACVTSS2SQ]= {SizeQ | LeftRead | RightWrite | Conv},
+ [ACVTTSD2SL]= {SizeL | LeftRead | RightWrite | Conv},
+ [ACVTTSD2SQ]= {SizeQ | LeftRead | RightWrite | Conv},
+ [ACVTTSS2SL]= {SizeL | LeftRead | RightWrite | Conv},
+ [ACVTTSS2SQ]= {SizeQ | LeftRead | RightWrite | Conv},
+
+ [ADECB]= {SizeB | RightRdwr},
+ [ADECL]= {SizeL | RightRdwr},
+ [ADECQ]= {SizeQ | RightRdwr},
+ [ADECW]= {SizeW | RightRdwr},
+
+ [ADIVB]= {SizeB | LeftRead | SetCarry, AX, AX},
+ [ADIVL]= {SizeL | LeftRead | SetCarry, AX|DX, AX|DX},
+ [ADIVQ]= {SizeQ | LeftRead | SetCarry, AX|DX, AX|DX},
+ [ADIVW]= {SizeW | LeftRead | SetCarry, AX|DX, AX|DX},
+
+ [ADIVSD]= {SizeD | LeftRead | RightRdwr},
+ [ADIVSS]= {SizeF | LeftRead | RightRdwr},
+
+ [AIDIVB]= {SizeB | LeftRead | SetCarry, AX, AX},
+ [AIDIVL]= {SizeL | LeftRead | SetCarry, AX|DX, AX|DX},
+ [AIDIVQ]= {SizeQ | LeftRead | SetCarry, AX|DX, AX|DX},
+ [AIDIVW]= {SizeW | LeftRead | SetCarry, AX|DX, AX|DX},
+
+ [AIMULB]= {SizeB | LeftRead | SetCarry, AX, AX},
+ [AIMULL]= {SizeL | LeftRead | ImulAXDX | SetCarry},
+ [AIMULQ]= {SizeQ | LeftRead | ImulAXDX | SetCarry},
+ [AIMULW]= {SizeW | LeftRead | ImulAXDX | SetCarry},
+
+ [AINCB]= {SizeB | RightRdwr},
+ [AINCL]= {SizeL | RightRdwr},
+ [AINCQ]= {SizeQ | RightRdwr},
+ [AINCW]= {SizeW | RightRdwr},
+
+ [AJCC]= {Cjmp | UseCarry},
+ [AJCS]= {Cjmp | UseCarry},
+ [AJEQ]= {Cjmp | UseCarry},
+ [AJGE]= {Cjmp | UseCarry},
+ [AJGT]= {Cjmp | UseCarry},
+ [AJHI]= {Cjmp | UseCarry},
+ [AJLE]= {Cjmp | UseCarry},
+ [AJLS]= {Cjmp | UseCarry},
+ [AJLT]= {Cjmp | UseCarry},
+ [AJMI]= {Cjmp | UseCarry},
+ [AJNE]= {Cjmp | UseCarry},
+ [AJOC]= {Cjmp | UseCarry},
+ [AJOS]= {Cjmp | UseCarry},
+ [AJPC]= {Cjmp | UseCarry},
+ [AJPL]= {Cjmp | UseCarry},
+ [AJPS]= {Cjmp | UseCarry},
+
+ [AJMP]= {Jump | Break | KillCarry},
+
+ [ALEAQ]= {LeftAddr | RightWrite},
+
+ [AMOVBLSX]= {SizeL | LeftRead | RightWrite | Conv},
+ [AMOVBLZX]= {SizeL | LeftRead | RightWrite | Conv},
+ [AMOVBQSX]= {SizeQ | LeftRead | RightWrite | Conv},
+ [AMOVBQZX]= {SizeQ | LeftRead | RightWrite | Conv},
+ [AMOVBWSX]= {SizeW | LeftRead | RightWrite | Conv},
+ [AMOVBWZX]= {SizeW | LeftRead | RightWrite | Conv},
+ [AMOVLQSX]= {SizeQ | LeftRead | RightWrite | Conv},
+ [AMOVLQZX]= {SizeQ | LeftRead | RightWrite | Conv},
+ [AMOVWLSX]= {SizeL | LeftRead | RightWrite | Conv},
+ [AMOVWLZX]= {SizeL | LeftRead | RightWrite | Conv},
+ [AMOVWQSX]= {SizeQ | LeftRead | RightWrite | Conv},
+ [AMOVWQZX]= {SizeQ | LeftRead | RightWrite | Conv},
+ [AMOVQL]= {SizeL | LeftRead | RightWrite | Conv},
+
+ [AMOVB]= {SizeB | LeftRead | RightWrite | Move},
+ [AMOVL]= {SizeL | LeftRead | RightWrite | Move},
+ [AMOVQ]= {SizeQ | LeftRead | RightWrite | Move},
+ [AMOVW]= {SizeW | LeftRead | RightWrite | Move},
+
+ [AMOVSB]= {OK, DI|SI, DI|SI},
+ [AMOVSL]= {OK, DI|SI, DI|SI},
+ [AMOVSQ]= {OK, DI|SI, DI|SI},
+ [AMOVSW]= {OK, DI|SI, DI|SI},
+
+ [AMOVSD]= {SizeD | LeftRead | RightWrite | Move},
+ [AMOVSS]= {SizeF | LeftRead | RightWrite | Move},
+
+ // We use MOVAPD as a faster synonym for MOVSD.
+ [AMOVAPD]= {SizeD | LeftRead | RightWrite | Move},
+
+ [AMULB]= {SizeB | LeftRead | SetCarry, AX, AX},
+ [AMULL]= {SizeL | LeftRead | SetCarry, AX, AX|DX},
+ [AMULQ]= {SizeQ | LeftRead | SetCarry, AX, AX|DX},
+ [AMULW]= {SizeW | LeftRead | SetCarry, AX, AX|DX},
+
+ [AMULSD]= {SizeD | LeftRead | RightRdwr},
+ [AMULSS]= {SizeF | LeftRead | RightRdwr},
+
+ [ANEGB]= {SizeB | RightRdwr | SetCarry},
+ [ANEGL]= {SizeL | RightRdwr | SetCarry},
+ [ANEGQ]= {SizeQ | RightRdwr | SetCarry},
+ [ANEGW]= {SizeW | RightRdwr | SetCarry},
+
+ [ANOTB]= {SizeB | RightRdwr},
+ [ANOTL]= {SizeL | RightRdwr},
+ [ANOTQ]= {SizeQ | RightRdwr},
+ [ANOTW]= {SizeW | RightRdwr},
+
+ [AORB]= {SizeB | LeftRead | RightRdwr | SetCarry},
+ [AORL]= {SizeL | LeftRead | RightRdwr | SetCarry},
+ [AORQ]= {SizeQ | LeftRead | RightRdwr | SetCarry},
+ [AORW]= {SizeW | LeftRead | RightRdwr | SetCarry},
+
+ [APOPQ]= {SizeQ | RightWrite},
+ [APUSHQ]= {SizeQ | LeftRead},
+
+ [ARCLB]= {SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry | UseCarry},
+ [ARCLL]= {SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry | UseCarry},
+ [ARCLQ]= {SizeQ | LeftRead | RightRdwr | ShiftCX | SetCarry | UseCarry},
+ [ARCLW]= {SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry | UseCarry},
+
+ [ARCRB]= {SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry | UseCarry},
+ [ARCRL]= {SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry | UseCarry},
+ [ARCRQ]= {SizeQ | LeftRead | RightRdwr | ShiftCX | SetCarry | UseCarry},
+ [ARCRW]= {SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry | UseCarry},
+
+ [AREP]= {OK, CX, CX},
+ [AREPN]= {OK, CX, CX},
+
+ [ARET]= {Break | KillCarry},
+
+ [AROLB]= {SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry},
+ [AROLL]= {SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry},
+ [AROLQ]= {SizeQ | LeftRead | RightRdwr | ShiftCX | SetCarry},
+ [AROLW]= {SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry},
+
+ [ARORB]= {SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry},
+ [ARORL]= {SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry},
+ [ARORQ]= {SizeQ | LeftRead | RightRdwr | ShiftCX | SetCarry},
+ [ARORW]= {SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry},
+
+ [ASALB]= {SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry},
+ [ASALL]= {SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry},
+ [ASALQ]= {SizeQ | LeftRead | RightRdwr | ShiftCX | SetCarry},
+ [ASALW]= {SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry},
+
+ [ASARB]= {SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry},
+ [ASARL]= {SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry},
+ [ASARQ]= {SizeQ | LeftRead | RightRdwr | ShiftCX | SetCarry},
+ [ASARW]= {SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry},
+
+ [ASBBB]= {SizeB | LeftRead | RightRdwr | SetCarry | UseCarry},
+ [ASBBL]= {SizeL | LeftRead | RightRdwr | SetCarry | UseCarry},
+ [ASBBQ]= {SizeQ | LeftRead | RightRdwr | SetCarry | UseCarry},
+ [ASBBW]= {SizeW | LeftRead | RightRdwr | SetCarry | UseCarry},
+
+ [ASHLB]= {SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry},
+ [ASHLL]= {SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry},
+ [ASHLQ]= {SizeQ | LeftRead | RightRdwr | ShiftCX | SetCarry},
+ [ASHLW]= {SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry},
+
+ [ASHRB]= {SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry},
+ [ASHRL]= {SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry},
+ [ASHRQ]= {SizeQ | LeftRead | RightRdwr | ShiftCX | SetCarry},
+ [ASHRW]= {SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry},
+
+ [ASTOSB]= {OK, AX|DI, DI},
+ [ASTOSL]= {OK, AX|DI, DI},
+ [ASTOSQ]= {OK, AX|DI, DI},
+ [ASTOSW]= {OK, AX|DI, DI},
+
+ [ASUBB]= {SizeB | LeftRead | RightRdwr | SetCarry},
+ [ASUBL]= {SizeL | LeftRead | RightRdwr | SetCarry},
+ [ASUBQ]= {SizeQ | LeftRead | RightRdwr | SetCarry},
+ [ASUBW]= {SizeW | LeftRead | RightRdwr | SetCarry},
+
+ [ASUBSD]= {SizeD | LeftRead | RightRdwr},
+ [ASUBSS]= {SizeF | LeftRead | RightRdwr},
+
+ [ATESTB]= {SizeB | LeftRead | RightRead | SetCarry},
+ [ATESTL]= {SizeL | LeftRead | RightRead | SetCarry},
+ [ATESTQ]= {SizeQ | LeftRead | RightRead | SetCarry},
+ [ATESTW]= {SizeW | LeftRead | RightRead | SetCarry},
+
+ [AUCOMISD]= {SizeD | LeftRead | RightRead},
+ [AUCOMISS]= {SizeF | LeftRead | RightRead},
+
+ [AXCHGB]= {SizeB | LeftRdwr | RightRdwr},
+ [AXCHGL]= {SizeL | LeftRdwr | RightRdwr},
+ [AXCHGQ]= {SizeQ | LeftRdwr | RightRdwr},
+ [AXCHGW]= {SizeW | LeftRdwr | RightRdwr},
+
+ [AXORB]= {SizeB | LeftRead | RightRdwr | SetCarry},
+ [AXORL]= {SizeL | LeftRead | RightRdwr | SetCarry},
+ [AXORQ]= {SizeQ | LeftRead | RightRdwr | SetCarry},
+ [AXORW]= {SizeW | LeftRead | RightRdwr | SetCarry},
+};
+
+void
+proginfo(ProgInfo *info, Prog *p)
+{
+ *info = progtable[p->as];
+ if(info->flags == 0)
+ fatal("unknown instruction %P", p);
+
+ if((info->flags & ShiftCX) && p->from.type != D_CONST)
+ info->reguse |= CX;
+
+ if(info->flags & ImulAXDX) {
+ if(p->to.type == D_NONE) {
+ info->reguse |= AX;
+ info->regset |= AX | DX;
+ } else {
+ info->flags |= RightRdwr;
+ }
+ }
+
+ // Addressing makes some registers used.
+ if(p->from.type >= D_INDIR)
+ info->regindex |= RtoB(p->from.type-D_INDIR);
+ if(p->from.index != D_NONE)
+ info->regindex |= RtoB(p->from.index);
+ if(p->to.type >= D_INDIR)
+ info->regindex |= RtoB(p->to.type-D_INDIR);
+ if(p->to.index != D_NONE)
+ info->regindex |= RtoB(p->to.index);
+}
diff --git a/src/cmd/6g/reg.c b/src/cmd/6g/reg.c
index ab826d431..63fd0deca 100644
--- a/src/cmd/6g/reg.c
+++ b/src/cmd/6g/reg.c
@@ -35,25 +35,10 @@
#define NREGVAR 32 /* 16 general + 16 floating */
#define REGBITS ((uint32)0xffffffff)
-#define P2R(p) (Reg*)(p->reg)
+static Reg* firstr;
static int first = 1;
-Reg*
-rega(void)
-{
- Reg *r;
-
- r = freer;
- if(r == R) {
- r = mal(sizeof(*r));
- } else
- freer = r->link;
-
- *r = zreg;
- return r;
-}
-
int
rcmp(const void *a1, const void *a2)
{
@@ -153,14 +138,14 @@ static char* regname[] = {
static Node* regnodes[NREGVAR];
-static void fixjmp(Prog*);
-
void
regopt(Prog *firstp)
{
Reg *r, *r1;
Prog *p;
- int i, z, nr;
+ Graph *g;
+ ProgInfo info;
+ int i, z;
uint32 vreg;
Bits bit;
@@ -171,20 +156,8 @@ regopt(Prog *firstp)
}
fixjmp(firstp);
-
- // count instructions
- nr = 0;
- for(p=firstp; p!=P; p=p->link)
- nr++;
- // if too big dont bother
- if(nr >= 10000) {
-// print("********** %S is too big (%d)\n", curfn->nname->sym, nr);
- return;
- }
-
- firstr = R;
- lastr = R;
-
+ mergetemp(firstp);
+
/*
* control flow is more complicated in generated go code
* than in generated c code. define pseudo-variables for
@@ -216,380 +189,46 @@ regopt(Prog *firstp)
* allocate pcs
* find use and set of variables
*/
- nr = 0;
- for(p=firstp; p!=P; p=p->link) {
- switch(p->as) {
- case ADATA:
- case AGLOBL:
- case ANAME:
- case ASIGNAME:
- case ALOCALS:
- case ATYPE:
- continue;
- }
- r = rega();
- nr++;
- if(firstr == R) {
- firstr = r;
- lastr = r;
- } else {
- lastr->link = r;
- r->p1 = lastr;
- lastr->s1 = r;
- lastr = r;
- }
- r->prog = p;
- p->reg = r;
-
- r1 = r->p1;
- if(r1 != R) {
- switch(r1->prog->as) {
- case ARET:
- case AJMP:
- case AIRETL:
- case AIRETQ:
- r->p1 = R;
- r1->s1 = R;
- }
- }
+ g = flowstart(firstp, sizeof(Reg));
+ if(g == nil)
+ return;
+ firstr = (Reg*)g->start;
+
+ for(r = firstr; r != R; r = (Reg*)r->f.link) {
+ p = r->f.prog;
+ proginfo(&info, p);
// Avoid making variables for direct-called functions.
if(p->as == ACALL && p->to.type == D_EXTERN)
continue;
- // Addressing makes some registers used.
- if(p->from.type >= D_INDIR)
- r->use1.b[0] |= RtoB(p->from.type-D_INDIR);
- if(p->from.index != D_NONE)
- r->use1.b[0] |= RtoB(p->from.index);
- if(p->to.type >= D_INDIR)
- r->use2.b[0] |= RtoB(p->to.type-D_INDIR);
- if(p->to.index != D_NONE)
- r->use2.b[0] |= RtoB(p->to.index);
+ r->use1.b[0] |= info.reguse | info.regindex;
+ r->set.b[0] |= info.regset;
bit = mkvar(r, &p->from);
- if(bany(&bit))
- switch(p->as) {
- /*
- * funny
- */
- case ALEAL:
- case ALEAQ:
- setaddrs(bit);
- break;
-
- /*
- * left side read
- */
- default:
- for(z=0; z<BITS; z++)
- r->use1.b[z] |= bit.b[z];
- break;
-
- /*
- * left side read+write
- */
- case AXCHGB:
- case AXCHGW:
- case AXCHGL:
- case AXCHGQ:
- for(z=0; z<BITS; z++) {
- r->use1.b[z] |= bit.b[z];
- r->set.b[z] |= bit.b[z];
- }
- break;
+ if(bany(&bit)) {
+ if(info.flags & LeftAddr)
+ setaddrs(bit);
+ if(info.flags & LeftRead)
+ for(z=0; z<BITS; z++)
+ r->use1.b[z] |= bit.b[z];
+ if(info.flags & LeftWrite)
+ for(z=0; z<BITS; z++)
+ r->set.b[z] |= bit.b[z];
}
bit = mkvar(r, &p->to);
- if(bany(&bit))
- switch(p->as) {
- default:
- yyerror("reg: unknown op: %A", p->as);
- break;
-
- /*
- * right side read
- */
- case ACMPB:
- case ACMPL:
- case ACMPQ:
- case ACMPW:
- case ACOMISS:
- case ACOMISD:
- case AUCOMISS:
- case AUCOMISD:
- case ATESTB:
- case ATESTL:
- case ATESTQ:
- for(z=0; z<BITS; z++)
- r->use2.b[z] |= bit.b[z];
- break;
-
- /*
- * right side write
- */
- case ALEAQ:
- case ANOP:
- case AMOVL:
- case AMOVQ:
- case AMOVB:
- case AMOVW:
- case AMOVBLSX:
- case AMOVBLZX:
- case AMOVBWSX:
- case AMOVBWZX:
- case AMOVBQSX:
- case AMOVBQZX:
- case AMOVLQSX:
- case AMOVLQZX:
- case AMOVWLSX:
- case AMOVWLZX:
- case AMOVWQSX:
- case AMOVWQZX:
- case AMOVQL:
- case APOPQ:
-
- case AMOVSS:
- case AMOVSD:
- case ACVTSD2SL:
- case ACVTSD2SQ:
- case ACVTSD2SS:
- case ACVTSL2SD:
- case ACVTSL2SS:
- case ACVTSQ2SD:
- case ACVTSQ2SS:
- case ACVTSS2SD:
- case ACVTSS2SL:
- case ACVTSS2SQ:
- case ACVTTSD2SL:
- case ACVTTSD2SQ:
- case ACVTTSS2SL:
- case ACVTTSS2SQ:
- for(z=0; z<BITS; z++)
- r->set.b[z] |= bit.b[z];
- break;
-
- /*
- * right side read+write
- */
- case AINCB:
- case AINCL:
- case AINCQ:
- case AINCW:
- case ADECB:
- case ADECL:
- case ADECQ:
- case ADECW:
-
- case AADDB:
- case AADDL:
- case AADDQ:
- case AADDW:
- case AANDB:
- case AANDL:
- case AANDQ:
- case AANDW:
- case ASUBB:
- case ASUBL:
- case ASUBQ:
- case ASUBW:
- case AORB:
- case AORL:
- case AORQ:
- case AORW:
- case AXORB:
- case AXORL:
- case AXORQ:
- case AXORW:
- case ASALB:
- case ASALL:
- case ASALQ:
- case ASALW:
- case ASARB:
- case ASARL:
- case ASARQ:
- case ASARW:
- case ARCLB:
- case ARCLL:
- case ARCLQ:
- case ARCLW:
- case ARCRB:
- case ARCRL:
- case ARCRQ:
- case ARCRW:
- case AROLB:
- case AROLL:
- case AROLQ:
- case AROLW:
- case ARORB:
- case ARORL:
- case ARORQ:
- case ARORW:
- case ASHLB:
- case ASHLL:
- case ASHLQ:
- case ASHLW:
- case ASHRB:
- case ASHRL:
- case ASHRQ:
- case ASHRW:
- case AIMULL:
- case AIMULQ:
- case AIMULW:
- case ANEGB:
- case ANEGW:
- case ANEGL:
- case ANEGQ:
- case ANOTL:
- case ANOTQ:
- case AADCL:
- case AADCQ:
- case ASBBL:
- case ASBBQ:
-
- case ASETCC:
- case ASETCS:
- case ASETEQ:
- case ASETGE:
- case ASETGT:
- case ASETHI:
- case ASETLE:
- case ASETLS:
- case ASETLT:
- case ASETMI:
- case ASETNE:
- case ASETOC:
- case ASETOS:
- case ASETPC:
- case ASETPL:
- case ASETPS:
-
- case AXCHGB:
- case AXCHGW:
- case AXCHGL:
- case AXCHGQ:
-
- case AADDSD:
- case AADDSS:
- case ACMPSD:
- case ACMPSS:
- case ADIVSD:
- case ADIVSS:
- case AMAXSD:
- case AMAXSS:
- case AMINSD:
- case AMINSS:
- case AMULSD:
- case AMULSS:
- case ARCPSS:
- case ARSQRTSS:
- case ASQRTSD:
- case ASQRTSS:
- case ASUBSD:
- case ASUBSS:
- case AXORPD:
- for(z=0; z<BITS; z++) {
- r->set.b[z] |= bit.b[z];
- r->use2.b[z] |= bit.b[z];
- }
- break;
-
- /*
- * funny
- */
- case ACALL:
- setaddrs(bit);
- break;
- }
-
- switch(p->as) {
- case AIMULL:
- case AIMULQ:
- case AIMULW:
- if(p->to.type != D_NONE)
- break;
-
- case AIDIVL:
- case AIDIVW:
- case AIDIVQ:
- case ADIVL:
- case ADIVW:
- case ADIVQ:
- case AMULL:
- case AMULW:
- case AMULQ:
- r->set.b[0] |= RtoB(D_AX) | RtoB(D_DX);
- r->use1.b[0] |= RtoB(D_AX) | RtoB(D_DX);
- break;
-
- case AIDIVB:
- case AIMULB:
- case ADIVB:
- case AMULB:
- r->set.b[0] |= RtoB(D_AX);
- r->use1.b[0] |= RtoB(D_AX);
- break;
-
- case ACWD:
- r->set.b[0] |= RtoB(D_AX) | RtoB(D_DX);
- r->use1.b[0] |= RtoB(D_AX);
- break;
-
- case ACDQ:
- r->set.b[0] |= RtoB(D_DX);
- r->use1.b[0] |= RtoB(D_AX);
- break;
-
- case AREP:
- case AREPN:
- case ALOOP:
- case ALOOPEQ:
- case ALOOPNE:
- r->set.b[0] |= RtoB(D_CX);
- r->use1.b[0] |= RtoB(D_CX);
- break;
-
- case AMOVSB:
- case AMOVSL:
- case AMOVSQ:
- case AMOVSW:
- case ACMPSB:
- case ACMPSL:
- case ACMPSQ:
- case ACMPSW:
- r->set.b[0] |= RtoB(D_SI) | RtoB(D_DI);
- r->use1.b[0] |= RtoB(D_SI) | RtoB(D_DI);
- break;
-
- case ASTOSB:
- case ASTOSL:
- case ASTOSQ:
- case ASTOSW:
- case ASCASB:
- case ASCASL:
- case ASCASQ:
- case ASCASW:
- r->set.b[0] |= RtoB(D_DI);
- r->use1.b[0] |= RtoB(D_AX) | RtoB(D_DI);
- break;
-
- case AINSB:
- case AINSL:
- case AINSW:
- r->set.b[0] |= RtoB(D_DX) | RtoB(D_DI);
- r->use1.b[0] |= RtoB(D_DI);
- break;
-
- case AOUTSB:
- case AOUTSL:
- case AOUTSW:
- r->set.b[0] |= RtoB(D_DI);
- r->use1.b[0] |= RtoB(D_DX) | RtoB(D_DI);
- break;
+ if(bany(&bit)) {
+ if(info.flags & RightAddr)
+ setaddrs(bit);
+ if(info.flags & RightRead)
+ for(z=0; z<BITS; z++)
+ r->use2.b[z] |= bit.b[z];
+ if(info.flags & RightWrite)
+ for(z=0; z<BITS; z++)
+ r->set.b[z] |= bit.b[z];
}
}
- if(firstr == R)
- return;
for(i=0; i<nvar; i++) {
Var *v = var+i;
@@ -605,45 +244,16 @@ regopt(Prog *firstp)
}
if(debug['R'] && debug['v'])
- dumpit("pass1", firstr);
+ dumpit("pass1", &firstr->f, 1);
/*
* pass 2
- * turn branch references to pointers
- * build back pointers
- */
- for(r=firstr; r!=R; r=r->link) {
- p = r->prog;
- if(p->to.type == D_BRANCH) {
- if(p->to.u.branch == P)
- fatal("pnil %P", p);
- r1 = p->to.u.branch->reg;
- if(r1 == R)
- fatal("rnil %P", p);
- if(r1 == r) {
- //fatal("ref to self %P", p);
- continue;
- }
- r->s2 = r1;
- r->p2link = r1->p2;
- r1->p2 = r;
- }
- }
-
- if(debug['R'] && debug['v'])
- dumpit("pass2", firstr);
-
- /*
- * pass 2.5
* find looping structure
*/
- for(r = firstr; r != R; r = r->link)
- r->active = 0;
- change = 0;
- loopit(firstr, nr);
+ flowrpo(g);
if(debug['R'] && debug['v'])
- dumpit("pass2.5", firstr);
+ dumpit("pass2", &firstr->f, 1);
/*
* pass 3
@@ -652,17 +262,17 @@ regopt(Prog *firstp)
*/
loop1:
change = 0;
- for(r = firstr; r != R; r = r->link)
- r->active = 0;
- for(r = firstr; r != R; r = r->link)
- if(r->prog->as == ARET)
+ for(r = firstr; r != R; r = (Reg*)r->f.link)
+ r->f.active = 0;
+ for(r = firstr; r != R; r = (Reg*)r->f.link)
+ if(r->f.prog->as == ARET)
prop(r, zbits, zbits);
loop11:
/* pick up unreachable code */
i = 0;
for(r = firstr; r != R; r = r1) {
- r1 = r->link;
- if(r1 && r1->active && !r->active) {
+ r1 = (Reg*)r->f.link;
+ if(r1 && r1->f.active && !r->f.active) {
prop(r, zbits, zbits);
i = 1;
}
@@ -673,7 +283,7 @@ loop11:
goto loop1;
if(debug['R'] && debug['v'])
- dumpit("pass3", firstr);
+ dumpit("pass3", &firstr->f, 1);
/*
* pass 4
@@ -682,20 +292,20 @@ loop11:
*/
loop2:
change = 0;
- for(r = firstr; r != R; r = r->link)
- r->active = 0;
+ for(r = firstr; r != R; r = (Reg*)r->f.link)
+ r->f.active = 0;
synch(firstr, zbits);
if(change)
goto loop2;
if(debug['R'] && debug['v'])
- dumpit("pass4", firstr);
+ dumpit("pass4", &firstr->f, 1);
/*
* pass 4.5
* move register pseudo-variables into regu.
*/
- for(r = firstr; r != R; r = r->link) {
+ for(r = firstr; r != R; r = (Reg*)r->f.link) {
r->regu = (r->refbehind.b[0] | r->set.b[0]) & REGBITS;
r->set.b[0] &= ~REGBITS;
@@ -719,26 +329,26 @@ loop2:
for(z=0; z<BITS; z++)
bit.b[z] = (r->refahead.b[z] | r->calahead.b[z]) &
~(externs.b[z] | params.b[z] | addrs.b[z] | consts.b[z]);
- if(bany(&bit) && !r->refset) {
+ if(bany(&bit) && !r->f.refset) {
// should never happen - all variables are preset
if(debug['w'])
- print("%L: used and not set: %Q\n", r->prog->lineno, bit);
- r->refset = 1;
+ print("%L: used and not set: %Q\n", r->f.prog->lineno, bit);
+ r->f.refset = 1;
}
}
- for(r = firstr; r != R; r = r->link)
+ for(r = firstr; r != R; r = (Reg*)r->f.link)
r->act = zbits;
rgp = region;
nregion = 0;
- for(r = firstr; r != R; r = r->link) {
+ for(r = firstr; r != R; r = (Reg*)r->f.link) {
for(z=0; z<BITS; z++)
bit.b[z] = r->set.b[z] &
~(r->refahead.b[z] | r->calahead.b[z] | addrs.b[z]);
- if(bany(&bit) && !r->refset) {
+ if(bany(&bit) && !r->f.refset) {
if(debug['w'])
- print("%L: set and not used: %Q\n", r->prog->lineno, bit);
- r->refset = 1;
- excise(r);
+ print("%L: set and not used: %Q\n", r->f.prog->lineno, bit);
+ r->f.refset = 1;
+ excise(&r->f);
}
for(z=0; z<BITS; z++)
bit.b[z] = LOAD(r) & ~(r->act.b[z] | addrs.b[z]);
@@ -765,7 +375,7 @@ brk:
qsort(region, nregion, sizeof(region[0]), rcmp);
if(debug['R'] && debug['v'])
- dumpit("pass5", firstr);
+ dumpit("pass5", &firstr->f, 1);
/*
* pass 6
@@ -791,19 +401,23 @@ brk:
}
if(debug['R'] && debug['v'])
- dumpit("pass6", firstr);
+ dumpit("pass6", &firstr->f, 1);
+
+ /*
+ * free aux structures. peep allocates new ones.
+ */
+ flowend(g);
+ firstr = R;
/*
* pass 7
* peep-hole on basic block
*/
- if(!debug['R'] || debug['P']) {
- peep();
- }
+ if(!debug['R'] || debug['P'])
+ peep(firstp);
/*
* eliminate nops
- * free aux structures
*/
for(p=firstp; p!=P; p=p->link) {
while(p->link != P && p->link->as == ANOP)
@@ -813,11 +427,6 @@ brk:
p->to.u.branch = p->to.u.branch->link;
}
- if(lastr != R) {
- lastr->link = freer;
- freer = firstr;
- }
-
if(debug['R']) {
if(ostats.ncvtreg ||
ostats.nspill ||
@@ -860,7 +469,7 @@ addmove(Reg *r, int bn, int rn, int f)
clearp(p1);
p1->loc = 9999;
- p = r->prog;
+ p = r->f.prog;
p1->link = p->link;
p->link = p1;
p1->lineno = p->lineno;
@@ -1084,7 +693,7 @@ prop(Reg *r, Bits ref, Bits cal)
Reg *r1, *r2;
int z;
- for(r1 = r; r1 != R; r1 = r1->p1) {
+ for(r1 = r; r1 != R; r1 = (Reg*)r1->f.p1) {
for(z=0; z<BITS; z++) {
ref.b[z] |= r1->refahead.b[z];
if(ref.b[z] != r1->refahead.b[z]) {
@@ -1097,9 +706,9 @@ prop(Reg *r, Bits ref, Bits cal)
change++;
}
}
- switch(r1->prog->as) {
+ switch(r1->f.prog->as) {
case ACALL:
- if(noreturn(r1->prog))
+ if(noreturn(r1->f.prog))
break;
for(z=0; z<BITS; z++) {
cal.b[z] |= ref.b[z] | externs.b[z];
@@ -1139,159 +748,22 @@ prop(Reg *r, Bits ref, Bits cal)
r1->refbehind.b[z] = ref.b[z];
r1->calbehind.b[z] = cal.b[z];
}
- if(r1->active)
+ if(r1->f.active)
break;
- r1->active = 1;
+ r1->f.active = 1;
}
- for(; r != r1; r = r->p1)
- for(r2 = r->p2; r2 != R; r2 = r2->p2link)
+ for(; r != r1; r = (Reg*)r->f.p1)
+ for(r2 = (Reg*)r->f.p2; r2 != R; r2 = (Reg*)r2->f.p2link)
prop(r2, r->refbehind, r->calbehind);
}
-/*
- * find looping structure
- *
- * 1) find reverse postordering
- * 2) find approximate dominators,
- * the actual dominators if the flow graph is reducible
- * otherwise, dominators plus some other non-dominators.
- * See Matthew S. Hecht and Jeffrey D. Ullman,
- * "Analysis of a Simple Algorithm for Global Data Flow Problems",
- * Conf. Record of ACM Symp. on Principles of Prog. Langs, Boston, Massachusetts,
- * Oct. 1-3, 1973, pp. 207-217.
- * 3) find all nodes with a predecessor dominated by the current node.
- * such a node is a loop head.
- * recursively, all preds with a greater rpo number are in the loop
- */
-int32
-postorder(Reg *r, Reg **rpo2r, int32 n)
-{
- Reg *r1;
-
- r->rpo = 1;
- r1 = r->s1;
- if(r1 && !r1->rpo)
- n = postorder(r1, rpo2r, n);
- r1 = r->s2;
- if(r1 && !r1->rpo)
- n = postorder(r1, rpo2r, n);
- rpo2r[n] = r;
- n++;
- return n;
-}
-
-int32
-rpolca(int32 *idom, int32 rpo1, int32 rpo2)
-{
- int32 t;
-
- if(rpo1 == -1)
- return rpo2;
- while(rpo1 != rpo2){
- if(rpo1 > rpo2){
- t = rpo2;
- rpo2 = rpo1;
- rpo1 = t;
- }
- while(rpo1 < rpo2){
- t = idom[rpo2];
- if(t >= rpo2)
- fatal("bad idom");
- rpo2 = t;
- }
- }
- return rpo1;
-}
-
-int
-doms(int32 *idom, int32 r, int32 s)
-{
- while(s > r)
- s = idom[s];
- return s == r;
-}
-
-int
-loophead(int32 *idom, Reg *r)
-{
- int32 src;
-
- src = r->rpo;
- if(r->p1 != R && doms(idom, src, r->p1->rpo))
- return 1;
- for(r = r->p2; r != R; r = r->p2link)
- if(doms(idom, src, r->rpo))
- return 1;
- return 0;
-}
-
-void
-loopmark(Reg **rpo2r, int32 head, Reg *r)
-{
- if(r->rpo < head || r->active == head)
- return;
- r->active = head;
- r->loop += LOOP;
- if(r->p1 != R)
- loopmark(rpo2r, head, r->p1);
- for(r = r->p2; r != R; r = r->p2link)
- loopmark(rpo2r, head, r);
-}
-
-void
-loopit(Reg *r, int32 nr)
-{
- Reg *r1;
- int32 i, d, me;
-
- if(nr > maxnr) {
- rpo2r = mal(nr * sizeof(Reg*));
- idom = mal(nr * sizeof(int32));
- maxnr = nr;
- }
-
- d = postorder(r, rpo2r, 0);
- if(d > nr)
- fatal("too many reg nodes %d %d", d, nr);
- nr = d;
- for(i = 0; i < nr / 2; i++) {
- r1 = rpo2r[i];
- rpo2r[i] = rpo2r[nr - 1 - i];
- rpo2r[nr - 1 - i] = r1;
- }
- for(i = 0; i < nr; i++)
- rpo2r[i]->rpo = i;
-
- idom[0] = 0;
- for(i = 0; i < nr; i++) {
- r1 = rpo2r[i];
- me = r1->rpo;
- d = -1;
- // rpo2r[r->rpo] == r protects against considering dead code,
- // which has r->rpo == 0.
- if(r1->p1 != R && rpo2r[r1->p1->rpo] == r1->p1 && r1->p1->rpo < me)
- d = r1->p1->rpo;
- for(r1 = r1->p2; r1 != nil; r1 = r1->p2link)
- if(rpo2r[r1->rpo] == r1 && r1->rpo < me)
- d = rpolca(idom, d, r1->rpo);
- idom[i] = d;
- }
-
- for(i = 0; i < nr; i++) {
- r1 = rpo2r[i];
- r1->loop++;
- if(r1->p2 != R && loophead(idom, r1))
- loopmark(rpo2r, i, r1);
- }
-}
-
void
synch(Reg *r, Bits dif)
{
Reg *r1;
int z;
- for(r1 = r; r1 != R; r1 = r1->s1) {
+ for(r1 = r; r1 != R; r1 = (Reg*)r1->f.s1) {
for(z=0; z<BITS; z++) {
dif.b[z] = (dif.b[z] &
~(~r1->refbehind.b[z] & r1->refahead.b[z])) |
@@ -1301,13 +773,13 @@ synch(Reg *r, Bits dif)
change++;
}
}
- if(r1->active)
+ if(r1->f.active)
break;
- r1->active = 1;
+ r1->f.active = 1;
for(z=0; z<BITS; z++)
dif.b[z] &= ~(~r1->calbehind.b[z] & r1->calahead.b[z]);
- if(r1->s2 != R)
- synch(r1->s2, dif);
+ if(r1->f.s2 != nil)
+ synch((Reg*)r1->f.s2, dif);
}
}
@@ -1372,7 +844,7 @@ paint1(Reg *r, int bn)
for(;;) {
if(!(r->refbehind.b[z] & bb))
break;
- r1 = r->p1;
+ r1 = (Reg*)r->f.p1;
if(r1 == R)
break;
if(!(r1->refahead.b[z] & bb))
@@ -1383,35 +855,35 @@ paint1(Reg *r, int bn)
}
if(LOAD(r) & ~(r->set.b[z]&~(r->use1.b[z]|r->use2.b[z])) & bb) {
- change -= CLOAD * r->loop;
+ change -= CLOAD * r->f.loop;
}
for(;;) {
r->act.b[z] |= bb;
if(r->use1.b[z] & bb) {
- change += CREF * r->loop;
+ change += CREF * r->f.loop;
}
if((r->use2.b[z]|r->set.b[z]) & bb) {
- change += CREF * r->loop;
+ change += CREF * r->f.loop;
}
if(STORE(r) & r->regdiff.b[z] & bb) {
- change -= CLOAD * r->loop;
+ change -= CLOAD * r->f.loop;
}
if(r->refbehind.b[z] & bb)
- for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+ for(r1 = (Reg*)r->f.p2; r1 != R; r1 = (Reg*)r1->f.p2link)
if(r1->refahead.b[z] & bb)
paint1(r1, bn);
if(!(r->refahead.b[z] & bb))
break;
- r1 = r->s2;
+ r1 = (Reg*)r->f.s2;
if(r1 != R)
if(r1->refbehind.b[z] & bb)
paint1(r1, bn);
- r = r->s1;
+ r = (Reg*)r->f.s1;
if(r == R)
break;
if(r->act.b[z] & bb)
@@ -1434,7 +906,7 @@ regset(Reg *r, uint32 bb)
v.type = b & 0xFFFF? BtoR(b): BtoF(b);
if(v.type == 0)
fatal("zero v.type for %#ux", b);
- c = copyu(r->prog, &v, A);
+ c = copyu(r->f.prog, &v, A);
if(c == 3)
set |= b;
bb &= ~b;
@@ -1453,7 +925,7 @@ reguse(Reg *r, uint32 bb)
v = zprog.from;
while(b = bb & ~(bb-1)) {
v.type = b & 0xFFFF? BtoR(b): BtoF(b);
- c = copyu(r->prog, &v, A);
+ c = copyu(r->f.prog, &v, A);
if(c == 1 || c == 2 || c == 4)
set |= b;
bb &= ~b;
@@ -1476,7 +948,7 @@ paint2(Reg *r, int bn)
for(;;) {
if(!(r->refbehind.b[z] & bb))
break;
- r1 = r->p1;
+ r1 = (Reg*)r->f.p1;
if(r1 == R)
break;
if(!(r1->refahead.b[z] & bb))
@@ -1491,17 +963,17 @@ paint2(Reg *r, int bn)
vreg |= r->regu;
if(r->refbehind.b[z] & bb)
- for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+ for(r1 = (Reg*)r->f.p2; r1 != R; r1 = (Reg*)r1->f.p2link)
if(r1->refahead.b[z] & bb)
vreg |= paint2(r1, bn);
if(!(r->refahead.b[z] & bb))
break;
- r1 = r->s2;
+ r1 = (Reg*)r->f.s2;
if(r1 != R)
if(r1->refbehind.b[z] & bb)
vreg |= paint2(r1, bn);
- r = r->s1;
+ r = (Reg*)r->f.s1;
if(r == R)
break;
if(!(r->act.b[z] & bb))
@@ -1511,7 +983,7 @@ paint2(Reg *r, int bn)
}
bb = vreg;
- for(; r; r=r->s1) {
+ for(; r; r=(Reg*)r->f.s1) {
x = r->regu & ~bb;
if(x) {
vreg |= reguse(r, x);
@@ -1536,7 +1008,7 @@ paint3(Reg *r, int bn, int32 rb, int rn)
for(;;) {
if(!(r->refbehind.b[z] & bb))
break;
- r1 = r->p1;
+ r1 = (Reg*)r->f.p1;
if(r1 == R)
break;
if(!(r1->refahead.b[z] & bb))
@@ -1550,7 +1022,7 @@ paint3(Reg *r, int bn, int32 rb, int rn)
addmove(r, bn, rn, 0);
for(;;) {
r->act.b[z] |= bb;
- p = r->prog;
+ p = r->f.prog;
if(r->use1.b[z] & bb) {
if(debug['R'] && debug['v'])
@@ -1572,17 +1044,17 @@ paint3(Reg *r, int bn, int32 rb, int rn)
r->regu |= rb;
if(r->refbehind.b[z] & bb)
- for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+ for(r1 = (Reg*)r->f.p2; r1 != R; r1 = (Reg*)r1->f.p2link)
if(r1->refahead.b[z] & bb)
paint3(r1, bn, rb, rn);
if(!(r->refahead.b[z] & bb))
break;
- r1 = r->s2;
+ r1 = (Reg*)r->f.s2;
if(r1 != R)
if(r1->refbehind.b[z] & bb)
paint3(r1, bn, rb, rn);
- r = r->s1;
+ r = (Reg*)r->f.s1;
if(r == R)
break;
if(r->act.b[z] & bb)
@@ -1646,60 +1118,64 @@ BtoF(int32 b)
}
void
-dumpone(Reg *r)
+dumpone(Flow *f, int isreg)
{
int z;
Bits bit;
+ Reg *r;
- print("%d:%P", r->loop, r->prog);
- for(z=0; z<BITS; z++)
- bit.b[z] =
- r->set.b[z] |
- r->use1.b[z] |
- r->use2.b[z] |
- r->refbehind.b[z] |
- r->refahead.b[z] |
- r->calbehind.b[z] |
- r->calahead.b[z] |
- r->regdiff.b[z] |
- r->act.b[z] |
- 0;
- if(bany(&bit)) {
- print("\t");
- if(bany(&r->set))
- print(" s:%Q", r->set);
- if(bany(&r->use1))
- print(" u1:%Q", r->use1);
- if(bany(&r->use2))
- print(" u2:%Q", r->use2);
- if(bany(&r->refbehind))
- print(" rb:%Q ", r->refbehind);
- if(bany(&r->refahead))
- print(" ra:%Q ", r->refahead);
- if(bany(&r->calbehind))
- print(" cb:%Q ", r->calbehind);
- if(bany(&r->calahead))
- print(" ca:%Q ", r->calahead);
- if(bany(&r->regdiff))
- print(" d:%Q ", r->regdiff);
- if(bany(&r->act))
- print(" a:%Q ", r->act);
+ print("%d:%P", f->loop, f->prog);
+ if(isreg) {
+ r = (Reg*)f;
+ for(z=0; z<BITS; z++)
+ bit.b[z] =
+ r->set.b[z] |
+ r->use1.b[z] |
+ r->use2.b[z] |
+ r->refbehind.b[z] |
+ r->refahead.b[z] |
+ r->calbehind.b[z] |
+ r->calahead.b[z] |
+ r->regdiff.b[z] |
+ r->act.b[z] |
+ 0;
+ if(bany(&bit)) {
+ print("\t");
+ if(bany(&r->set))
+ print(" s:%Q", r->set);
+ if(bany(&r->use1))
+ print(" u1:%Q", r->use1);
+ if(bany(&r->use2))
+ print(" u2:%Q", r->use2);
+ if(bany(&r->refbehind))
+ print(" rb:%Q ", r->refbehind);
+ if(bany(&r->refahead))
+ print(" ra:%Q ", r->refahead);
+ if(bany(&r->calbehind))
+ print(" cb:%Q ", r->calbehind);
+ if(bany(&r->calahead))
+ print(" ca:%Q ", r->calahead);
+ if(bany(&r->regdiff))
+ print(" d:%Q ", r->regdiff);
+ if(bany(&r->act))
+ print(" a:%Q ", r->act);
+ }
}
print("\n");
}
void
-dumpit(char *str, Reg *r0)
+dumpit(char *str, Flow *r0, int isreg)
{
- Reg *r, *r1;
+ Flow *r, *r1;
print("\n%s\n", str);
- for(r = r0; r != R; r = r->link) {
- dumpone(r);
+ for(r = r0; r != nil; r = r->link) {
+ dumpone(r, isreg);
r1 = r->p2;
- if(r1 != R) {
+ if(r1 != nil) {
print(" pred:");
- for(; r1 != R; r1 = r1->p2link)
+ for(; r1 != nil; r1 = r1->p2link)
print(" %.4ud", r1->prog->loc);
print("\n");
}
@@ -1712,148 +1188,3 @@ dumpit(char *str, Reg *r0)
// }
}
}
-
-static Sym* symlist[10];
-
-int
-noreturn(Prog *p)
-{
- Sym *s;
- int i;
-
- if(symlist[0] == S) {
- symlist[0] = pkglookup("panicindex", runtimepkg);
- symlist[1] = pkglookup("panicslice", runtimepkg);
- symlist[2] = pkglookup("throwinit", runtimepkg);
- symlist[3] = pkglookup("panic", runtimepkg);
- symlist[4] = pkglookup("panicwrap", runtimepkg);
- }
-
- s = p->to.sym;
- if(s == S)
- return 0;
- for(i=0; symlist[i]!=S; i++)
- if(s == symlist[i])
- return 1;
- return 0;
-}
-
-/*
- * the code generator depends on being able to write out JMP
- * instructions that it can jump to now but fill in later.
- * the linker will resolve them nicely, but they make the code
- * longer and more difficult to follow during debugging.
- * remove them.
- */
-
-/* what instruction does a JMP to p eventually land on? */
-static Prog*
-chasejmp(Prog *p, int *jmploop)
-{
- int n;
-
- n = 0;
- while(p != P && p->as == AJMP && p->to.type == D_BRANCH) {
- if(++n > 10) {
- *jmploop = 1;
- break;
- }
- p = p->to.u.branch;
- }
- return p;
-}
-
-/*
- * reuse reg pointer for mark/sweep state.
- * leave reg==nil at end because alive==nil.
- */
-#define alive ((void*)0)
-#define dead ((void*)1)
-
-/* mark all code reachable from firstp as alive */
-static void
-mark(Prog *firstp)
-{
- Prog *p;
-
- for(p=firstp; p; p=p->link) {
- if(p->reg != dead)
- break;
- p->reg = alive;
- if(p->as != ACALL && p->to.type == D_BRANCH && p->to.u.branch)
- mark(p->to.u.branch);
- if(p->as == AJMP || p->as == ARET || p->as == AUNDEF)
- break;
- }
-}
-
-static void
-fixjmp(Prog *firstp)
-{
- int jmploop;
- Prog *p, *last;
-
- if(debug['R'] && debug['v'])
- print("\nfixjmp\n");
-
- // pass 1: resolve jump to AJMP, mark all code as dead.
- jmploop = 0;
- for(p=firstp; p; p=p->link) {
- if(debug['R'] && debug['v'])
- print("%P\n", p);
- if(p->as != ACALL && p->to.type == D_BRANCH && p->to.u.branch && p->to.u.branch->as == AJMP) {
- p->to.u.branch = chasejmp(p->to.u.branch, &jmploop);
- if(debug['R'] && debug['v'])
- print("->%P\n", p);
- }
- p->reg = dead;
- }
- if(debug['R'] && debug['v'])
- print("\n");
-
- // pass 2: mark all reachable code alive
- mark(firstp);
-
- // pass 3: delete dead code (mostly JMPs).
- last = nil;
- for(p=firstp; p; p=p->link) {
- if(p->reg == dead) {
- if(p->link == P && p->as == ARET && last && last->as != ARET) {
- // This is the final ARET, and the code so far doesn't have one.
- // Let it stay.
- } else {
- if(debug['R'] && debug['v'])
- print("del %P\n", p);
- continue;
- }
- }
- if(last)
- last->link = p;
- last = p;
- }
- last->link = P;
-
- // pass 4: elide JMP to next instruction.
- // only safe if there are no jumps to JMPs anymore.
- if(!jmploop) {
- last = nil;
- for(p=firstp; p; p=p->link) {
- if(p->as == AJMP && p->to.type == D_BRANCH && p->to.u.branch == p->link) {
- if(debug['R'] && debug['v'])
- print("del %P\n", p);
- continue;
- }
- if(last)
- last->link = p;
- last = p;
- }
- last->link = P;
- }
-
- if(debug['R'] && debug['v']) {
- print("\n");
- for(p=firstp; p; p=p->link)
- print("%P\n", p);
- print("\n");
- }
-}
diff --git a/src/cmd/6l/6.out.h b/src/cmd/6l/6.out.h
index 237a802cd..5fa73a65b 100644
--- a/src/cmd/6l/6.out.h
+++ b/src/cmd/6l/6.out.h
@@ -30,11 +30,7 @@
#define NSYM 50
#define NSNAME 8
-#define NOPROF (1<<0)
-#define DUPOK (1<<1)
-#define NOSPLIT (1<<2)
-#define RODATA (1<<3)
-#define NOPTR (1<<4)
+#include "../ld/textflag.h"
/*
* amd64
@@ -759,11 +755,14 @@ enum as
AAESKEYGENASSIST,
APSHUFD,
+ APCLMULQDQ,
AUSEFIELD,
- ALOCALS,
ATYPE,
-
+ AFUNCDATA,
+ APCDATA,
+ ACHECKNIL,
+
ALAST
};
diff --git a/src/cmd/6l/asm.c b/src/cmd/6l/asm.c
index 8807a6ed5..a09cc9727 100644
--- a/src/cmd/6l/asm.c
+++ b/src/cmd/6l/asm.c
@@ -43,6 +43,7 @@ char linuxdynld[] = "/lib64/ld-linux-x86-64.so.2";
char freebsddynld[] = "/libexec/ld-elf.so.1";
char openbsddynld[] = "/usr/libexec/ld.so";
char netbsddynld[] = "/libexec/ld.elf_so";
+char dragonflydynld[] = "/usr/libexec/ld-elf.so.2";
char zeroes[32];
@@ -99,12 +100,6 @@ int nelfsym = 1;
static void addpltsym(Sym*);
static void addgotsym(Sym*);
-Sym *
-lookuprel(void)
-{
- return lookup(".rela", 0);
-}
-
void
adddynrela(Sym *rela, Sym *s, Reloc *r)
{
@@ -312,9 +307,12 @@ elfreloc1(Reloc *r, vlong sectoff)
break;
case D_TLS:
- if(r->siz == 4)
- VPUT(R_X86_64_TPOFF32 | (uint64)elfsym<<32);
- else
+ if(r->siz == 4) {
+ if(flag_shared)
+ VPUT(R_X86_64_GOTTPOFF | (uint64)elfsym<<32);
+ else
+ VPUT(R_X86_64_TPOFF32 | (uint64)elfsym<<32);
+ } else
return -1;
break;
}
@@ -625,13 +623,20 @@ asmb(void)
sect = segtext.sect;
cseek(sect->vaddr - segtext.vaddr + segtext.fileoff);
codeblk(sect->vaddr, sect->len);
-
- /* output read-only data in text segment (rodata, gosymtab, pclntab, ...) */
for(sect = sect->next; sect != nil; sect = sect->next) {
cseek(sect->vaddr - segtext.vaddr + segtext.fileoff);
datblk(sect->vaddr, sect->len);
}
+ if(segrodata.filelen > 0) {
+ if(debug['v'])
+ Bprint(&bso, "%5.2f rodatblk\n", cputime());
+ Bflush(&bso);
+
+ cseek(segrodata.fileoff);
+ datblk(segrodata.vaddr, segrodata.filelen);
+ }
+
if(debug['v'])
Bprint(&bso, "%5.2f datblk\n", cputime());
Bflush(&bso);
@@ -668,6 +673,7 @@ asmb(void)
case Hfreebsd:
case Hnetbsd:
case Hopenbsd:
+ case Hdragonfly:
debug['8'] = 1; /* 64-bit addresses */
break;
case Hwindows:
@@ -696,7 +702,8 @@ asmb(void)
case Hfreebsd:
case Hnetbsd:
case Hopenbsd:
- symo = rnd(HEADR+segtext.len, INITRND)+segdata.filelen;
+ case Hdragonfly:
+ symo = rnd(HEADR+segtext.len, INITRND)+rnd(segrodata.len, INITRND)+segdata.filelen;
symo = rnd(symo, INITRND);
break;
case Hwindows:
@@ -786,6 +793,7 @@ asmb(void)
case Hfreebsd:
case Hnetbsd:
case Hopenbsd:
+ case Hdragonfly:
asmbelf(symo);
break;
case Hwindows:
diff --git a/src/cmd/6l/l.h b/src/cmd/6l/l.h
index 4d481c69d..ecab867e4 100644
--- a/src/cmd/6l/l.h
+++ b/src/cmd/6l/l.h
@@ -159,7 +159,6 @@ struct Sym
int32 got;
int32 align; // if non-zero, required alignment in bytes
int32 elfsym;
- int32 locals; // size of stack frame locals area
int32 args; // size of stack frame incoming arguments area
Sym* hash; // in hash table
Sym* allsym; // in all symbol list
@@ -175,6 +174,7 @@ struct Sym
char* dynimplib;
char* dynimpvers;
struct Section* sect;
+ struct Hist* hist; // for ATEXT
// STEXT
Auto* autom;
@@ -187,14 +187,13 @@ struct Sym
Reloc* r;
int32 nr;
int32 maxr;
- int rel_ro;
};
struct Optab
{
short as;
uchar* ytab;
uchar prefix;
- uchar op[22];
+ uchar op[23];
};
struct Movtab
{
@@ -211,7 +210,7 @@ enum
STRINGSZ = 200,
MINLC = 1,
MAXIO = 8192,
- MAXHIST = 20, /* limit of path elements for history symbols */
+ MAXHIST = 40, /* limit of path elements for history symbols */
Yxxx = 0,
Ynone,
@@ -329,7 +328,6 @@ EXTERN int32 INITRND;
EXTERN int64 INITTEXT;
EXTERN int64 INITDAT;
EXTERN char* INITENTRY; /* entry point */
-EXTERN char* LIBINITENTRY; /* shared library entry point */
EXTERN char* pcstr;
EXTERN Auto* curauto;
EXTERN Auto* curhist;
@@ -357,7 +355,6 @@ EXTERN int32 spsize;
EXTERN Sym* symlist;
EXTERN int32 symsize;
EXTERN int tlsoffset;
-EXTERN int version;
EXTERN Prog zprg;
EXTERN int dtype;
EXTERN char* paramspace;
diff --git a/src/cmd/6l/list.c b/src/cmd/6l/list.c
index f39efa2e8..5040e4327 100644
--- a/src/cmd/6l/list.c
+++ b/src/cmd/6l/list.c
@@ -57,10 +57,13 @@ Pconv(Fmt *fp)
switch(p->as) {
case ATEXT:
if(p->from.scale) {
- fmtprint(fp, "(%d) %A %D,%d,%D",
+ fmtprint(fp, "(%d) %A %D,%d,%lD",
p->line, p->as, &p->from, p->from.scale, &p->to);
break;
}
+ fmtprint(fp, "(%d) %A %D,%lD",
+ p->line, p->as, &p->from, &p->to);
+ break;
default:
fmtprint(fp, "(%d) %A %D,%D",
p->line, p->as, &p->from, &p->to);
@@ -423,7 +426,7 @@ Iconv(Fmt *fp)
void
diag(char *fmt, ...)
{
- char buf[STRINGSZ], *tn, *sep;
+ char buf[1024], *tn, *sep;
va_list arg;
tn = "";
diff --git a/src/cmd/6l/obj.c b/src/cmd/6l/obj.c
index e98f91eeb..ae649a74b 100644
--- a/src/cmd/6l/obj.c
+++ b/src/cmd/6l/obj.c
@@ -48,6 +48,7 @@ Header headers[] = {
"plan9", Hplan9x64,
"elf", Helf,
"darwin", Hdarwin,
+ "dragonfly", Hdragonfly,
"linux", Hlinux,
"freebsd", Hfreebsd,
"netbsd", Hnetbsd,
@@ -62,6 +63,7 @@ Header headers[] = {
* -Hplan9 -T0x200028 -R0x200000 is plan9 64-bit format
* -Helf -T0x80110000 -R4096 is ELF32
* -Hdarwin -Tx -Rx is apple MH-exec
+ * -Hdragonfly -Tx -Rx is DragonFly elf-exec
* -Hlinux -Tx -Rx is linux elf-exec
* -Hfreebsd -Tx -Rx is FreeBSD elf-exec
* -Hnetbsd -Tx -Rx is NetBSD elf-exec
@@ -82,7 +84,6 @@ main(int argc, char *argv[])
INITDAT = -1;
INITRND = -1;
INITENTRY = 0;
- LIBINITENTRY = 0;
linkmode = LinkAuto;
nuxiinit();
@@ -111,6 +112,7 @@ main(int argc, char *argv[])
flagstr("extldflags", "flags for external linker", &extldflags);
flagcount("f", "ignore version mismatch", &debug['f']);
flagcount("g", "disable go package data checks", &debug['g']);
+ flagstr("installsuffix", "pkg directory suffix", &flag_installsuffix);
flagfn1("linkmode", "mode: set link mode (internal, external, auto)", setlinkmode);
flagstr("k", "sym: set field tracking symbol", &tracksym);
flagcount("n", "dump symbol table", &debug['n']);
@@ -119,7 +121,7 @@ main(int argc, char *argv[])
flagstr("r", "dir1:dir2:...: set ELF dynamic linker search path", &rpath);
flagcount("race", "enable race detector", &flag_race);
flagcount("s", "disable symbol table", &debug['s']);
- flagcount("shared", "generate shared object", &flag_shared);
+ flagcount("shared", "generate shared object (implies -linkmode external)", &flag_shared);
flagstr("tmpdir", "leave temporary files in this directory", &tmpdir);
flagcount("u", "reject unsafe packages", &debug['u']);
flagcount("v", "print link trace", &debug['v']);
@@ -140,6 +142,9 @@ main(int argc, char *argv[])
if(linkmode == LinkAuto && strcmp(getgoextlinkenabled(), "0") == 0)
linkmode = LinkInternal;
+ if(flag_shared)
+ linkmode = LinkExternal;
+
switch(HEADTYPE) {
default:
if(linkmode == LinkAuto)
@@ -148,6 +153,7 @@ main(int argc, char *argv[])
sysfatal("cannot use -linkmode=external with -H %s", headstr(HEADTYPE));
break;
case Hdarwin:
+ case Hdragonfly:
case Hfreebsd:
case Hlinux:
case Hnetbsd:
@@ -168,7 +174,7 @@ main(int argc, char *argv[])
default:
diag("unknown -H option");
errorexit();
- case Hplan9x32: /* plan 9 */
+ case Hplan9x32: /* plan 9 */
HEADR = 32L;
if(INITTEXT == -1)
INITTEXT = 4096+HEADR;
@@ -177,7 +183,7 @@ main(int argc, char *argv[])
if(INITRND == -1)
INITRND = 4096;
break;
- case Hplan9x64: /* plan 9 */
+ case Hplan9x64: /* plan 9 */
HEADR = 32L + 8L;
if(INITTEXT == -1)
INITTEXT = 0x200000+HEADR;
@@ -186,7 +192,7 @@ main(int argc, char *argv[])
if(INITRND == -1)
INITRND = 0x200000;
break;
- case Helf: /* elf32 executable */
+ case Helf: /* elf32 executable */
HEADR = rnd(52L+3*32L, 16);
if(INITTEXT == -1)
INITTEXT = 0x80110000L;
@@ -195,7 +201,7 @@ main(int argc, char *argv[])
if(INITRND == -1)
INITRND = 4096;
break;
- case Hdarwin: /* apple MACH */
+ case Hdarwin: /* apple MACH */
/*
* OS X system constant - offset from 0(GS) to our TLS.
* Explained in ../../pkg/runtime/cgo/gcc_darwin_amd64.c.
@@ -210,10 +216,11 @@ main(int argc, char *argv[])
if(INITDAT == -1)
INITDAT = 0;
break;
- case Hlinux: /* elf64 executable */
- case Hfreebsd: /* freebsd */
- case Hnetbsd: /* netbsd */
- case Hopenbsd: /* openbsd */
+ case Hlinux: /* elf64 executable */
+ case Hfreebsd: /* freebsd */
+ case Hnetbsd: /* netbsd */
+ case Hopenbsd: /* openbsd */
+ case Hdragonfly: /* dragonfly */
/*
* ELF uses TLS offset negative from FS.
* Translate 0(FS) and 8(FS) into -16(FS) and -8(FS).
@@ -230,7 +237,7 @@ main(int argc, char *argv[])
if(INITRND == -1)
INITRND = 4096;
break;
- case Hwindows: /* PE executable */
+ case Hwindows: /* PE executable */
peinit();
HEADR = PEFILEHEADR;
if(INITTEXT == -1)
@@ -337,10 +344,10 @@ zaddr(char *pn, Biobuf *f, Adr *a, Sym *h[])
}
a->offset = 0;
if(t & T_OFFSET) {
- a->offset = Bget4(f);
+ a->offset = BGETLE4(f);
if(t & T_64) {
a->offset &= 0xFFFFFFFFULL;
- a->offset |= (vlong)Bget4(f) << 32;
+ a->offset |= (uvlong)BGETLE4(f) << 32;
}
}
a->sym = S;
@@ -348,8 +355,8 @@ zaddr(char *pn, Biobuf *f, Adr *a, Sym *h[])
a->sym = zsym(pn, f, h);
a->type = D_NONE;
if(t & T_FCONST) {
- a->ieee.l = Bget4(f);
- a->ieee.h = Bget4(f);
+ a->ieee.l = BGETLE4(f);
+ a->ieee.h = BGETLE4(f);
a->type = D_FCONST;
} else
if(t & T_SCONST) {
@@ -365,7 +372,7 @@ zaddr(char *pn, Biobuf *f, Adr *a, Sym *h[])
adrgotype = zsym(pn, f, h);
s = a->sym;
t = a->type;
- if(t == D_INDIR+D_GS)
+ if(t == D_INDIR+D_GS || a->index == D_GS)
a->offset += tlsoffset;
if(t != D_AUTO && t != D_PARAM) {
if(s && adrgotype)
@@ -456,7 +463,7 @@ loop:
if(o == ANAME || o == ASIGNAME) {
sig = 0;
if(o == ASIGNAME)
- sig = Bget4(f);
+ sig = BGETLE4(f);
v = BGETC(f); /* type */
o = BGETC(f); /* sym */
r = 0;
@@ -511,7 +518,7 @@ loop:
p = mal(sizeof(*p));
p->as = o;
- p->line = Bget4(f);
+ p->line = BGETLE4(f);
p->back = 2;
p->mode = mode;
zaddr(pn, f, &p->from, h);
@@ -542,6 +549,7 @@ loop:
addhist(p->line, D_FILE); /* 'z' */
if(p->to.offset)
addhist(p->to.offset, D_FILE1); /* 'Z' */
+ savehist(p->line, p->to.offset);
histfrogp = 0;
goto loop;
@@ -603,13 +611,6 @@ loop:
pc++;
goto loop;
- case ALOCALS:
- if(skip)
- goto casdef;
- cursym->locals = p->to.offset;
- pc++;
- goto loop;
-
case ATYPE:
if(skip)
goto casdef;
@@ -658,6 +659,7 @@ loop:
s->gotype = fromgotype;
}
s->type = STEXT;
+ s->hist = gethist();
s->value = pc;
s->args = p->to.offset >> 32;
lastp = p;
diff --git a/src/cmd/6l/optab.c b/src/cmd/6l/optab.c
index b0d5ca788..46603ad45 100644
--- a/src/cmd/6l/optab.c
+++ b/src/cmd/6l/optab.c
@@ -42,11 +42,25 @@ uchar ytext[] =
};
uchar ynop[] =
{
- Ynone, Ynone, Zpseudo,1,
- Ynone, Yml, Zpseudo,1,
- Ynone, Yrf, Zpseudo,1,
- Yml, Ynone, Zpseudo,1,
- Yrf, Ynone, Zpseudo,1,
+ Ynone, Ynone, Zpseudo,0,
+ Ynone, Yiauto, Zpseudo,0,
+ Ynone, Yml, Zpseudo,0,
+ Ynone, Yrf, Zpseudo,0,
+ Ynone, Yxr, Zpseudo,0,
+ Yiauto, Ynone, Zpseudo,0,
+ Yml, Ynone, Zpseudo,0,
+ Yrf, Ynone, Zpseudo,0,
+ Yxr, Ynone, Zpseudo,1,
+ 0
+};
+uchar yfuncdata[] =
+{
+ Yi32, Ym, Zpseudo, 0,
+ 0
+};
+uchar ypcdata[] =
+{
+ Yi32, Yi32, Zpseudo, 0,
0
};
uchar yxorb[] =
@@ -243,8 +257,10 @@ uchar yrb_mb[] =
Yrb, Ymb, Zr_m, 1,
0
};
-uchar yml_ml[] =
+uchar yxchg[] =
{
+ Yax, Yrl, Z_rp, 1,
+ Yrl, Yax, Zrp_, 1,
Yrl, Yml, Zr_m, 1,
Yml, Yrl, Zm_r, 1,
0
@@ -897,7 +913,7 @@ Optab optab[] =
{ AMOVHLPS, yxr, Pm, 0x12 },
{ AMOVHPD, yxmov, Pe, 0x16,0x17 },
{ AMOVHPS, yxmov, Pm, 0x16,0x17 },
- { AMOVL, ymovl, Px, 0x89,0x8b,0x31,0xb8,0xc7,(00),0x6e,0x7e,Pe,0x6e,Pe,0x7e },
+ { AMOVL, ymovl, Px, 0x89,0x8b,0x31,0xb8,0xc7,(00),0x6e,0x7e,Pe,0x6e,Pe,0x7e,0 },
{ AMOVLHPS, yxr, Pm, 0x16 },
{ AMOVLPD, yxmov, Pe, 0x12,0x13 },
{ AMOVLPS, yxmov, Pm, 0x12,0x13 },
@@ -909,7 +925,7 @@ Optab optab[] =
{ AMOVNTPD, yxr_ml, Pe, 0x2b },
{ AMOVNTPS, yxr_ml, Pm, 0x2b },
{ AMOVNTQ, ymr_ml, Pm, 0xe7 },
- { AMOVQ, ymovq, Pw, 0x89, 0x8b, 0x31, 0xc7,(00), 0xb8, 0xc7,(00), 0x6f, 0x7f, 0x6e, 0x7e, Pf2,0xd6, Pf3,0x7e, Pe,0xd6, Pe,0x6e, Pe,0x7e },
+ { AMOVQ, ymovq, Pw, 0x89, 0x8b, 0x31, 0xc7,(00), 0xb8, 0xc7,(00), 0x6f, 0x7f, 0x6e, 0x7e, Pf2,0xd6, Pf3,0x7e, Pe,0xd6, Pe,0x6e, Pe,0x7e,0 },
{ AMOVQOZX, ymrxr, Pf3, 0xd6,0x7e },
{ AMOVSB, ynone, Pb, 0xa4 },
{ AMOVSD, yxmov, Pf2, 0x10,0x11 },
@@ -919,7 +935,7 @@ Optab optab[] =
{ AMOVSW, ynone, Pe, 0xa5 },
{ AMOVUPD, yxmov, Pe, 0x10,0x11 },
{ AMOVUPS, yxmov, Pm, 0x10,0x11 },
- { AMOVW, ymovw, Pe, 0x89,0x8b,0x31,0xb8,0xc7,(00) },
+ { AMOVW, ymovw, Pe, 0x89,0x8b,0x31,0xb8,0xc7,(00),0 },
{ AMOVWLSX, yml_rl, Pm, 0xbf },
{ AMOVWLZX, yml_rl, Pm, 0xb7 },
{ AMOVWQSX, yml_rl, Pw, 0x0f,0xbf },
@@ -1170,9 +1186,9 @@ Optab optab[] =
{ AWAIT, ynone, Px, 0x9b },
{ AWORD, ybyte, Px, 2 },
{ AXCHGB, yml_mb, Pb, 0x86,0x86 },
- { AXCHGL, yml_ml, Px, 0x87,0x87 },
- { AXCHGQ, yml_ml, Pw, 0x87,0x87 },
- { AXCHGW, yml_ml, Pe, 0x87,0x87 },
+ { AXCHGL, yxchg, Px, 0x90,0x90,0x87,0x87 },
+ { AXCHGQ, yxchg, Pw, 0x90,0x90,0x87,0x87 },
+ { AXCHGW, yxchg, Pe, 0x90,0x90,0x87,0x87 },
{ AXLAT, ynone, Px, 0xd7 },
{ AXORB, yxorb, Pb, 0x34,0x80,(06),0x30,0x32 },
{ AXORL, yxorl, Px, 0x83,(06),0x35,0x81,(06),0x31,0x33 },
@@ -1333,10 +1349,11 @@ Optab optab[] =
{ AAESKEYGENASSIST, yaes2, Pq, 0x3a,0xdf,(0) },
{ APSHUFD, yaes2, Pq, 0x70,(0) },
+ { APCLMULQDQ, yxshuf, Pq, 0x3a,0x44,0 },
{ AUSEFIELD, ynop, Px, 0,0 },
- { ALOCALS },
- { ATYPE },
+ { AFUNCDATA, yfuncdata, Px, 0,0 },
+ { APCDATA, ypcdata, Px, 0,0 },
{ AEND },
0
diff --git a/src/cmd/6l/pass.c b/src/cmd/6l/pass.c
index 0054b329f..1be3c18fe 100644
--- a/src/cmd/6l/pass.c
+++ b/src/cmd/6l/pass.c
@@ -273,6 +273,7 @@ patch(void)
Prog *p, *q;
Sym *s;
int32 vexit;
+ Sym *gmsym;
if(debug['v'])
Bprint(&bso, "%5.2f mkfwd\n", cputime());
@@ -282,6 +283,17 @@ patch(void)
Bprint(&bso, "%5.2f patch\n", cputime());
Bflush(&bso);
+ if(flag_shared) {
+ s = lookup("init_array", 0);
+ s->type = SINITARR;
+ s->reachable = 1;
+ s->hide = 1;
+ addaddr(s, lookup(INITENTRY, 0));
+ }
+
+ gmsym = lookup("runtime.tlsgm", 0);
+ if(linkmode != LinkExternal)
+ gmsym->reachable = 0;
s = lookup("exit", 0);
vexit = s->value;
for(cursym = textp; cursym != nil; cursym = cursym->next)
@@ -311,14 +323,67 @@ patch(void)
}
if(HEADTYPE == Hlinux || HEADTYPE == Hfreebsd
|| HEADTYPE == Hopenbsd || HEADTYPE == Hnetbsd
- || HEADTYPE == Hplan9x64) {
+ || HEADTYPE == Hplan9x64 || HEADTYPE == Hdragonfly) {
// ELF uses FS instead of GS.
if(p->from.type == D_INDIR+D_GS)
p->from.type = D_INDIR+D_FS;
if(p->to.type == D_INDIR+D_GS)
p->to.type = D_INDIR+D_FS;
+ if(p->from.index == D_GS)
+ p->from.index = D_FS;
+ if(p->to.index == D_GS)
+ p->to.index = D_FS;
+ }
+ if(!flag_shared) {
+ // Convert g() or m() accesses of the form
+ // op n(reg)(GS*1), reg
+ // to
+ // op n(GS*1), reg
+ if(p->from.index == D_FS || p->from.index == D_GS) {
+ p->from.type = D_INDIR + p->from.index;
+ p->from.index = D_NONE;
+ }
+ // Convert g() or m() accesses of the form
+ // op reg, n(reg)(GS*1)
+ // to
+ // op reg, n(GS*1)
+ if(p->to.index == D_FS || p->to.index == D_GS) {
+ p->to.type = D_INDIR + p->to.index;
+ p->to.index = D_NONE;
+ }
+ // Convert get_tls access of the form
+ // op runtime.tlsgm(SB), reg
+ // to
+ // NOP
+ if(gmsym != S && p->from.sym == gmsym) {
+ p->as = ANOP;
+ p->from.type = D_NONE;
+ p->to.type = D_NONE;
+ p->from.sym = nil;
+ p->to.sym = nil;
+ continue;
+ }
+ } else {
+ // Convert TLS reads of the form
+ // op n(GS), reg
+ // to
+ // MOVQ $runtime.tlsgm(SB), reg
+ // op n(reg)(GS*1), reg
+ if((p->from.type == D_INDIR+D_FS || p->from.type == D_INDIR + D_GS) && p->to.type >= D_AX && p->to.type <= D_DI) {
+ q = appendp(p);
+ q->to = p->to;
+ q->as = p->as;
+ q->from.type = D_INDIR+p->to.type;
+ q->from.index = p->from.type - D_INDIR;
+ q->from.scale = 1;
+ q->from.offset = p->from.offset;
+ p->as = AMOVQ;
+ p->from.type = D_EXTERN;
+ p->from.sym = gmsym;
+ p->from.offset = 0;
+ }
}
- if(p->as == ACALL || (p->as == AJMP && p->to.type != D_BRANCH)) {
+ if(p->as == ACALL || (p->as == AJMP && p->to.type != D_BRANCH) || (p->as == ARET && p->to.sym != nil)) {
s = p->to.sym;
if(s) {
if(debug['c'])
@@ -403,6 +468,10 @@ morename[] =
};
Prog* pmorestack[nelem(morename)];
Sym* symmorestack[nelem(morename)];
+Sym* gmsym;
+
+static Prog* load_g_cx(Prog*);
+static Prog* stacksplit(Prog*, int32, Prog**);
void
dostkoff(void)
@@ -410,8 +479,9 @@ dostkoff(void)
Prog *p, *q, *q1;
int32 autoffset, deltasp;
int a, pcsize;
- uint32 moreconst1, moreconst2, i;
+ uint32 i;
+ gmsym = lookup("runtime.tlsgm", 0);
for(i=0; i<nelem(morename); i++) {
symmorestack[i] = lookup(morename[i], 0);
if(symmorestack[i]->type != STEXT)
@@ -437,164 +507,16 @@ dostkoff(void)
noleaf:;
}
- q = P;
if((p->from.scale & NOSPLIT) && autoffset >= StackSmall)
diag("nosplit func likely to overflow stack");
- if(!(p->from.scale & NOSPLIT)) {
- p = appendp(p); // load g into CX
- p->as = AMOVQ;
- if(HEADTYPE == Hlinux || HEADTYPE == Hfreebsd
- || HEADTYPE == Hopenbsd || HEADTYPE == Hnetbsd
- || HEADTYPE == Hplan9x64) // ELF uses FS
- p->from.type = D_INDIR+D_FS;
- else
- p->from.type = D_INDIR+D_GS;
- p->from.offset = tlsoffset+0;
- p->to.type = D_CX;
- if(HEADTYPE == Hwindows) {
- // movq %gs:0x28, %rcx
- // movq (%rcx), %rcx
- p->as = AMOVQ;
- p->from.type = D_INDIR+D_GS;
- p->from.offset = 0x28;
- p->to.type = D_CX;
-
-
- p = appendp(p);
- p->as = AMOVQ;
- p->from.type = D_INDIR+D_CX;
- p->from.offset = 0;
- p->to.type = D_CX;
- }
-
- if(debug['K']) {
- // 6l -K means check not only for stack
- // overflow but stack underflow.
- // On underflow, INT 3 (breakpoint).
- // Underflow itself is rare but this also
- // catches out-of-sync stack guard info
-
- p = appendp(p);
- p->as = ACMPQ;
- p->from.type = D_INDIR+D_CX;
- p->from.offset = 8;
- p->to.type = D_SP;
-
- p = appendp(p);
- p->as = AJHI;
- p->to.type = D_BRANCH;
- p->to.offset = 4;
- q1 = p;
-
- p = appendp(p);
- p->as = AINT;
- p->from.type = D_CONST;
- p->from.offset = 3;
-
- p = appendp(p);
- p->as = ANOP;
- q1->pcond = p;
- }
-
- if(autoffset < StackBig) { // do we need to call morestack?
- if(autoffset <= StackSmall) {
- // small stack
- p = appendp(p);
- p->as = ACMPQ;
- p->from.type = D_SP;
- p->to.type = D_INDIR+D_CX;
- } else {
- // large stack
- p = appendp(p);
- p->as = ALEAQ;
- p->from.type = D_INDIR+D_SP;
- p->from.offset = -(autoffset-StackSmall);
- p->to.type = D_AX;
-
- p = appendp(p);
- p->as = ACMPQ;
- p->from.type = D_AX;
- p->to.type = D_INDIR+D_CX;
- }
-
- // common
- p = appendp(p);
- p->as = AJHI;
- p->to.type = D_BRANCH;
- p->to.offset = 4;
- q = p;
- }
-
- // If we ask for more stack, we'll get a minimum of StackMin bytes.
- // We need a stack frame large enough to hold the top-of-stack data,
- // the function arguments+results, our caller's PC, our frame,
- // a word for the return PC of the next call, and then the StackLimit bytes
- // that must be available on entry to any function called from a function
- // that did a stack check. If StackMin is enough, don't ask for a specific
- // amount: then we can use the custom functions and save a few
- // instructions.
- moreconst1 = 0;
- if(StackTop + textarg + PtrSize + autoffset + PtrSize + StackLimit >= StackMin)
- moreconst1 = autoffset;
- moreconst2 = textarg;
-
- // 4 varieties varieties (const1==0 cross const2==0)
- // and 6 subvarieties of (const1==0 and const2!=0)
+ q = P;
+ if(!(p->from.scale & NOSPLIT) || (p->from.scale & WRAPPER)) {
p = appendp(p);
- if(moreconst1 == 0 && moreconst2 == 0) {
- p->as = ACALL;
- p->to.type = D_BRANCH;
- p->pcond = pmorestack[0];
- p->to.sym = symmorestack[0];
- } else
- if(moreconst1 != 0 && moreconst2 == 0) {
- p->as = AMOVL;
- p->from.type = D_CONST;
- p->from.offset = moreconst1;
- p->to.type = D_AX;
-
- p = appendp(p);
- p->as = ACALL;
- p->to.type = D_BRANCH;
- p->pcond = pmorestack[1];
- p->to.sym = symmorestack[1];
- } else
- if(moreconst1 == 0 && moreconst2 <= 48 && moreconst2%8 == 0) {
- i = moreconst2/8 + 3;
- p->as = ACALL;
- p->to.type = D_BRANCH;
- p->pcond = pmorestack[i];
- p->to.sym = symmorestack[i];
- } else
- if(moreconst1 == 0 && moreconst2 != 0) {
- p->as = AMOVL;
- p->from.type = D_CONST;
- p->from.offset = moreconst2;
- p->to.type = D_AX;
-
- p = appendp(p);
- p->as = ACALL;
- p->to.type = D_BRANCH;
- p->pcond = pmorestack[2];
- p->to.sym = symmorestack[2];
- } else {
- p->as = AMOVQ;
- p->from.type = D_CONST;
- p->from.offset = (uint64)moreconst2 << 32;
- p->from.offset |= moreconst1;
- p->to.type = D_AX;
-
- p = appendp(p);
- p->as = ACALL;
- p->to.type = D_BRANCH;
- p->pcond = pmorestack[3];
- p->to.sym = symmorestack[3];
- }
+ p = load_g_cx(p); // load g into CX
}
-
- if(q != P)
- q->pcond = p->link;
+ if(!(cursym->text->from.scale & NOSPLIT))
+ p = stacksplit(p, autoffset, &q); // emit split check
if(autoffset) {
p = appendp(p);
@@ -602,8 +524,6 @@ dostkoff(void)
p->from.type = D_CONST;
p->from.offset = autoffset;
p->spadj = autoffset;
- if(q != P)
- q->pcond = p;
} else {
// zero-byte stack adjustment.
// Insert a fake non-zero adjustment so that stkcheck can
@@ -615,7 +535,19 @@ dostkoff(void)
p->as = ANOP;
p->spadj = PtrSize;
}
+ if(q != P)
+ q->pcond = p;
deltasp = autoffset;
+
+ if(cursym->text->from.scale & WRAPPER) {
+ // g->panicwrap += autoffset + PtrSize;
+ p = appendp(p);
+ p->as = AADDL;
+ p->from.type = D_CONST;
+ p->from.offset = autoffset + PtrSize;
+ p->to.type = D_INDIR+D_CX;
+ p->to.offset = 2*PtrSize;
+ }
if(debug['K'] > 1 && autoffset) {
// 6l -KK means double-check for stack overflow
@@ -733,6 +665,19 @@ dostkoff(void)
if(autoffset != deltasp)
diag("unbalanced PUSH/POP");
+
+ if(cursym->text->from.scale & WRAPPER) {
+ p = load_g_cx(p);
+ p = appendp(p);
+ // g->panicwrap -= autoffset + PtrSize;
+ p->as = ASUBL;
+ p->from.type = D_CONST;
+ p->from.offset = autoffset + PtrSize;
+ p->to.type = D_INDIR+D_CX;
+ p->to.offset = 2*PtrSize;
+ p = appendp(p);
+ p->as = ARET;
+ }
if(autoffset) {
p->as = AADJSP;
@@ -747,10 +692,264 @@ dostkoff(void)
// the cleanup.
p->spadj = +autoffset;
}
+ if(p->to.sym) // retjmp
+ p->as = AJMP;
}
}
}
+// Append code to p to load g into cx.
+// Overwrites p with the first instruction (no first appendp).
+// Overwriting p is unusual but it lets use this in both the
+// prologue (caller must call appendp first) and in the epilogue.
+// Returns last new instruction.
+static Prog*
+load_g_cx(Prog *p)
+{
+ if(flag_shared) {
+ // Load TLS offset with MOVQ $runtime.tlsgm(SB), CX
+ p->as = AMOVQ;
+ p->from.type = D_EXTERN;
+ p->from.sym = gmsym;
+ p->to.type = D_CX;
+ p = appendp(p);
+ }
+ p->as = AMOVQ;
+ if(HEADTYPE == Hlinux || HEADTYPE == Hfreebsd
+ || HEADTYPE == Hopenbsd || HEADTYPE == Hnetbsd
+ || HEADTYPE == Hplan9x64 || HEADTYPE == Hdragonfly)
+ // ELF uses FS
+ p->from.type = D_INDIR+D_FS;
+ else
+ p->from.type = D_INDIR+D_GS;
+ if(flag_shared) {
+ // Add TLS offset stored in CX
+ p->from.index = p->from.type - D_INDIR;
+ p->from.type = D_INDIR + D_CX;
+ }
+ p->from.offset = tlsoffset+0;
+ p->to.type = D_CX;
+ if(HEADTYPE == Hwindows) {
+ // movq %gs:0x28, %rcx
+ // movq (%rcx), %rcx
+ p->as = AMOVQ;
+ p->from.type = D_INDIR+D_GS;
+ p->from.offset = 0x28;
+ p->to.type = D_CX;
+
+ p = appendp(p);
+ p->as = AMOVQ;
+ p->from.type = D_INDIR+D_CX;
+ p->from.offset = 0;
+ p->to.type = D_CX;
+ }
+ return p;
+}
+
+// Append code to p to check for stack split.
+// Appends to (does not overwrite) p.
+// Assumes g is in CX.
+// Returns last new instruction.
+// On return, *jmpok is the instruction that should jump
+// to the stack frame allocation if no split is needed.
+static Prog*
+stacksplit(Prog *p, int32 framesize, Prog **jmpok)
+{
+ Prog *q, *q1;
+ uint32 moreconst1, moreconst2, i;
+
+ if(debug['K']) {
+ // 6l -K means check not only for stack
+ // overflow but stack underflow.
+ // On underflow, INT 3 (breakpoint).
+ // Underflow itself is rare but this also
+ // catches out-of-sync stack guard info
+
+ p = appendp(p);
+ p->as = ACMPQ;
+ p->from.type = D_INDIR+D_CX;
+ p->from.offset = 8;
+ p->to.type = D_SP;
+
+ p = appendp(p);
+ p->as = AJHI;
+ p->to.type = D_BRANCH;
+ p->to.offset = 4;
+ q1 = p;
+
+ p = appendp(p);
+ p->as = AINT;
+ p->from.type = D_CONST;
+ p->from.offset = 3;
+
+ p = appendp(p);
+ p->as = ANOP;
+ q1->pcond = p;
+ }
+
+ q = P;
+ q1 = P;
+ if(framesize <= StackSmall) {
+ // small stack: SP <= stackguard
+ // CMPQ SP, stackguard
+ p = appendp(p);
+ p->as = ACMPQ;
+ p->from.type = D_SP;
+ p->to.type = D_INDIR+D_CX;
+ } else if(framesize <= StackBig) {
+ // large stack: SP-framesize <= stackguard-StackSmall
+ // LEAQ -xxx(SP), AX
+ // CMPQ AX, stackguard
+ p = appendp(p);
+ p->as = ALEAQ;
+ p->from.type = D_INDIR+D_SP;
+ p->from.offset = -(framesize-StackSmall);
+ p->to.type = D_AX;
+
+ p = appendp(p);
+ p->as = ACMPQ;
+ p->from.type = D_AX;
+ p->to.type = D_INDIR+D_CX;
+ } else {
+ // Such a large stack we need to protect against wraparound.
+ // If SP is close to zero:
+ // SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
+ // The +StackGuard on both sides is required to keep the left side positive:
+ // SP is allowed to be slightly below stackguard. See stack.h.
+ //
+ // Preemption sets stackguard to StackPreempt, a very large value.
+ // That breaks the math above, so we have to check for that explicitly.
+ // MOVQ stackguard, CX
+ // CMPQ CX, $StackPreempt
+ // JEQ label-of-call-to-morestack
+ // LEAQ StackGuard(SP), AX
+ // SUBQ CX, AX
+ // CMPQ AX, $(framesize+(StackGuard-StackSmall))
+
+ p = appendp(p);
+ p->as = AMOVQ;
+ p->from.type = D_INDIR+D_CX;
+ p->from.offset = 0;
+ p->to.type = D_SI;
+
+ p = appendp(p);
+ p->as = ACMPQ;
+ p->from.type = D_SI;
+ p->to.type = D_CONST;
+ p->to.offset = StackPreempt;
+
+ p = appendp(p);
+ p->as = AJEQ;
+ p->to.type = D_BRANCH;
+ q1 = p;
+
+ p = appendp(p);
+ p->as = ALEAQ;
+ p->from.type = D_INDIR+D_SP;
+ p->from.offset = StackGuard;
+ p->to.type = D_AX;
+
+ p = appendp(p);
+ p->as = ASUBQ;
+ p->from.type = D_SI;
+ p->to.type = D_AX;
+
+ p = appendp(p);
+ p->as = ACMPQ;
+ p->from.type = D_AX;
+ p->to.type = D_CONST;
+ p->to.offset = framesize+(StackGuard-StackSmall);
+ }
+
+ // common
+ p = appendp(p);
+ p->as = AJHI;
+ p->to.type = D_BRANCH;
+ q = p;
+
+ // If we ask for more stack, we'll get a minimum of StackMin bytes.
+ // We need a stack frame large enough to hold the top-of-stack data,
+ // the function arguments+results, our caller's PC, our frame,
+ // a word for the return PC of the next call, and then the StackLimit bytes
+ // that must be available on entry to any function called from a function
+ // that did a stack check. If StackMin is enough, don't ask for a specific
+ // amount: then we can use the custom functions and save a few
+ // instructions.
+ moreconst1 = 0;
+ if(StackTop + textarg + PtrSize + framesize + PtrSize + StackLimit >= StackMin)
+ moreconst1 = framesize;
+ moreconst2 = textarg;
+ if(moreconst2 == 1) // special marker
+ moreconst2 = 0;
+ if((moreconst2&7) != 0)
+ diag("misaligned argument size in stack split");
+ // 4 varieties varieties (const1==0 cross const2==0)
+ // and 6 subvarieties of (const1==0 and const2!=0)
+ p = appendp(p);
+ if(moreconst1 == 0 && moreconst2 == 0) {
+ p->as = ACALL;
+ p->to.type = D_BRANCH;
+ p->pcond = pmorestack[0];
+ p->to.sym = symmorestack[0];
+ } else
+ if(moreconst1 != 0 && moreconst2 == 0) {
+ p->as = AMOVL;
+ p->from.type = D_CONST;
+ p->from.offset = moreconst1;
+ p->to.type = D_AX;
+
+ p = appendp(p);
+ p->as = ACALL;
+ p->to.type = D_BRANCH;
+ p->pcond = pmorestack[1];
+ p->to.sym = symmorestack[1];
+ } else
+ if(moreconst1 == 0 && moreconst2 <= 48 && moreconst2%8 == 0) {
+ i = moreconst2/8 + 3;
+ p->as = ACALL;
+ p->to.type = D_BRANCH;
+ p->pcond = pmorestack[i];
+ p->to.sym = symmorestack[i];
+ } else
+ if(moreconst1 == 0 && moreconst2 != 0) {
+ p->as = AMOVL;
+ p->from.type = D_CONST;
+ p->from.offset = moreconst2;
+ p->to.type = D_AX;
+
+ p = appendp(p);
+ p->as = ACALL;
+ p->to.type = D_BRANCH;
+ p->pcond = pmorestack[2];
+ p->to.sym = symmorestack[2];
+ } else {
+ p->as = AMOVQ;
+ p->from.type = D_CONST;
+ p->from.offset = (uint64)moreconst2 << 32;
+ p->from.offset |= moreconst1;
+ p->to.type = D_AX;
+
+ p = appendp(p);
+ p->as = ACALL;
+ p->to.type = D_BRANCH;
+ p->pcond = pmorestack[3];
+ p->to.sym = symmorestack[3];
+ }
+
+ p = appendp(p);
+ p->as = AJMP;
+ p->to.type = D_BRANCH;
+ p->pcond = cursym->text->link;
+
+ if(q != P)
+ q->pcond = p->link;
+ if(q1 != P)
+ q1->pcond = q->link;
+
+ *jmpok = q;
+ return p;
+}
+
vlong
atolwhex(char *s)
{
diff --git a/src/cmd/6l/span.c b/src/cmd/6l/span.c
index 460a34f2f..74f11d635 100644
--- a/src/cmd/6l/span.c
+++ b/src/cmd/6l/span.c
@@ -358,6 +358,18 @@ prefixof(Adr *a)
case D_INDIR+D_GS:
return 0x65;
}
+ switch(a->index) {
+ case D_CS:
+ return 0x2e;
+ case D_DS:
+ return 0x3e;
+ case D_ES:
+ return 0x26;
+ case D_FS:
+ return 0x64;
+ case D_GS:
+ return 0x65;
+ }
return 0;
}
@@ -735,15 +747,20 @@ vaddr(Adr *a, Reloc *r)
diag("need reloc for %D", a);
errorexit();
}
- if(flag_shared)
- r->type = D_PCREL;
- else
- r->type = D_ADDR;
r->siz = 4; // TODO: 8 for external symbols
r->off = -1; // caller must fill in
r->sym = s;
r->add = v;
v = 0;
+ if(flag_shared) {
+ if(s->type == STLSBSS) {
+ r->xadd = r->add - r->siz;
+ r->type = D_TLS;
+ r->xsym = s;
+ } else
+ r->type = D_PCREL;
+ } else
+ r->type = D_ADDR;
}
return v;
}
@@ -760,7 +777,7 @@ asmandsz(Adr *a, int r, int rex, int m64)
v = a->offset;
t = a->type;
rel.siz = 0;
- if(a->index != D_NONE) {
+ if(a->index != D_NONE && a->index != D_FS && a->index != D_GS) {
if(t < D_INDIR) {
switch(t) {
default:
@@ -888,18 +905,11 @@ putrelv:
r = addrel(cursym);
r->off = curp->pc + andptr - and;
- r->add = 0;
- r->xadd = 0;
+ r->add = a->offset-tlsoffset;
+ r->xadd = r->add;
r->siz = 4;
r->type = D_TLS;
- if(a->offset == tlsoffset+0)
- s = lookup("runtime.g", 0);
- else
- s = lookup("runtime.m", 0);
- s->type = STLSBSS;
- s->reachable = 1;
- s->size = PtrSize;
- s->hide = 1;
+ s = lookup("runtime.tlsgm", 0);
r->sym = s;
r->xsym = s;
v = 0;
@@ -1227,6 +1237,8 @@ found:
break;
}
+ if(z >= nelem(o->op))
+ sysfatal("asmins bad table %P", p);
op = o->op[z];
if(op == 0x0f) {
*andptr++ = op;
diff --git a/src/cmd/8a/a.y b/src/cmd/8a/a.y
index 246643427..13ccc985b 100644
--- a/src/cmd/8a/a.y
+++ b/src/cmd/8a/a.y
@@ -33,6 +33,7 @@
#include <stdio.h> /* if we don't, bison will, and a.h re-#defines getc */
#include <libc.h>
#include "a.h"
+#include "../../pkg/runtime/funcdata.h"
%}
%union {
Sym *sym;
@@ -54,7 +55,7 @@
%left '*' '/' '%'
%token <lval> LTYPE0 LTYPE1 LTYPE2 LTYPE3 LTYPE4
%token <lval> LTYPEC LTYPED LTYPEN LTYPER LTYPET LTYPES LTYPEM LTYPEI LTYPEG LTYPEXC
-%token <lval> LTYPEX LCONST LFP LPC LSB
+%token <lval> LTYPEX LTYPEPC LTYPEF LCONST LFP LPC LSB
%token <lval> LBREG LLREG LSREG LFREG LXREG
%token <dval> LFCONST
%token <sval> LSCONST LSP
@@ -63,7 +64,7 @@
%type <con2> con2
%type <gen> mem imm imm2 reg nam rel rem rim rom omem nmem
%type <gen2> nonnon nonrel nonrem rimnon rimrem remrim
-%type <gen2> spec1 spec2 spec3 spec4 spec5 spec6 spec7 spec8 spec9 spec10
+%type <gen2> spec1 spec2 spec3 spec4 spec5 spec6 spec7 spec8 spec9 spec10 spec11 spec12
%%
prog:
| prog
@@ -118,6 +119,8 @@ inst:
| LTYPEG spec8 { outcode($1, &$2); }
| LTYPEXC spec9 { outcode($1, &$2); }
| LTYPEX spec10 { outcode($1, &$2); }
+| LTYPEPC spec11 { outcode($1, &$2); }
+| LTYPEF spec12 { outcode($1, &$2); }
nonnon:
{
@@ -307,6 +310,26 @@ spec10: /* PINSRD */
$$.to.offset = $1.offset;
}
+spec11: /* PCDATA */
+ rim ',' rim
+ {
+ if($1.type != D_CONST || $3.type != D_CONST)
+ yyerror("arguments to PCDATA must be integer constants");
+ $$.from = $1;
+ $$.to = $3;
+ }
+
+spec12: /* FUNCDATA */
+ rim ',' rim
+ {
+ if($1.type != D_CONST)
+ yyerror("index for FUNCDATA must be integer constant");
+ if($3.type != D_EXTERN && $3.type != D_STATIC)
+ yyerror("value for FUNCDATA must be symbol reference");
+ $$.from = $1;
+ $$.to = $3;
+ }
+
rem:
reg
| mem
@@ -448,12 +471,12 @@ con2:
LCONST
{
$$.v1 = $1;
- $$.v2 = 0;
+ $$.v2 = ArgsSizeUnknown;
}
| '-' LCONST
{
$$.v1 = -$2;
- $$.v2 = 0;
+ $$.v2 = ArgsSizeUnknown;
}
| LCONST '-' LCONST
{
diff --git a/src/cmd/8a/doc.go b/src/cmd/8a/doc.go
index 737c56f13..84c7254c8 100644
--- a/src/cmd/8a/doc.go
+++ b/src/cmd/8a/doc.go
@@ -10,6 +10,11 @@
http://plan9.bell-labs.com/magic/man2html/1/8a
+Go-specific considerations are documented at
+
+ http://golang.org/doc/asm
+
+I
Its target architecture is the x86, referred to by these tools for historical reasons as 386.
*/
diff --git a/src/cmd/8a/lex.c b/src/cmd/8a/lex.c
index a7840f625..f2ccc3361 100644
--- a/src/cmd/8a/lex.c
+++ b/src/cmd/8a/lex.c
@@ -74,7 +74,7 @@ main(int argc, char *argv[])
ARGBEGIN {
default:
c = ARGC();
- if(c >= 0 || c < sizeof(debug))
+ if(c >= 0 && c < sizeof(debug))
debug[c] = 1;
break;
@@ -799,7 +799,8 @@ struct
"XORPD", LTYPE3, AXORPD,
"XORPS", LTYPE3, AXORPS,
"USEFIELD", LTYPEN, AUSEFIELD,
-
+ "PCDATA", LTYPEPC, APCDATA,
+ "FUNCDATA", LTYPEF, AFUNCDATA,
0
};
@@ -879,15 +880,14 @@ void
zname(char *n, int t, int s)
{
- Bputc(&obuf, ANAME); /* as(2) */
- Bputc(&obuf, ANAME>>8);
- Bputc(&obuf, t); /* type */
- Bputc(&obuf, s); /* sym */
+ BPUTLE2(&obuf, ANAME); /* as(2) */
+ BPUTC(&obuf, t); /* type */
+ BPUTC(&obuf, s); /* sym */
while(*n) {
- Bputc(&obuf, *n);
+ BPUTC(&obuf, *n);
n++;
}
- Bputc(&obuf, 0);
+ BPUTC(&obuf, 0);
}
void
@@ -922,52 +922,38 @@ zaddr(Gen *a, int s)
case D_NONE:
break;
}
- Bputc(&obuf, t);
+ BPUTC(&obuf, t);
if(t & T_INDEX) { /* implies index, scale */
- Bputc(&obuf, a->index);
- Bputc(&obuf, a->scale);
+ BPUTC(&obuf, a->index);
+ BPUTC(&obuf, a->scale);
}
if(t & T_OFFSET) { /* implies offset */
l = a->offset;
- Bputc(&obuf, l);
- Bputc(&obuf, l>>8);
- Bputc(&obuf, l>>16);
- Bputc(&obuf, l>>24);
+ BPUTLE4(&obuf, l);
}
if(t & T_OFFSET2) {
l = a->offset2;
- Bputc(&obuf, l);
- Bputc(&obuf, l>>8);
- Bputc(&obuf, l>>16);
- Bputc(&obuf, l>>24);
+ BPUTLE4(&obuf, l);
}
if(t & T_SYM) /* implies sym */
- Bputc(&obuf, s);
+ BPUTC(&obuf, s);
if(t & T_FCONST) {
ieeedtod(&e, a->dval);
- l = e.l;
- Bputc(&obuf, l);
- Bputc(&obuf, l>>8);
- Bputc(&obuf, l>>16);
- Bputc(&obuf, l>>24);
- l = e.h;
- Bputc(&obuf, l);
- Bputc(&obuf, l>>8);
- Bputc(&obuf, l>>16);
- Bputc(&obuf, l>>24);
+ BPUTLE4(&obuf, e.l);
+ BPUTLE4(&obuf, e.h);
return;
}
if(t & T_SCONST) {
n = a->sval;
for(i=0; i<NSNAME; i++) {
- Bputc(&obuf, *n);
+ BPUTC(&obuf, *n);
n++;
}
return;
}
if(t & T_TYPE)
- Bputc(&obuf, a->type);
+ BPUTC(&obuf, a->type);
}
void
@@ -1026,12 +1012,8 @@ jackpot:
goto jackpot;
break;
}
- Bputc(&obuf, a);
- Bputc(&obuf, a>>8);
- Bputc(&obuf, stmtline);
- Bputc(&obuf, stmtline>>8);
- Bputc(&obuf, stmtline>>16);
- Bputc(&obuf, stmtline>>24);
+ BPUTLE2(&obuf, a);
+ BPUTLE4(&obuf, stmtline);
zaddr(&g2->from, sf);
zaddr(&g2->to, st);
@@ -1106,13 +1088,12 @@ outhist(void)
q = 0;
}
if(n) {
- Bputc(&obuf, ANAME);
- Bputc(&obuf, ANAME>>8);
- Bputc(&obuf, D_FILE); /* type */
- Bputc(&obuf, 1); /* sym */
- Bputc(&obuf, '<');
+ BPUTLE2(&obuf, ANAME);
+ BPUTC(&obuf, D_FILE); /* type */
+ BPUTC(&obuf, 1); /* sym */
+ BPUTC(&obuf, '<');
Bwrite(&obuf, p, n);
- Bputc(&obuf, 0);
+ BPUTC(&obuf, 0);
}
p = q;
if(p == 0 && op) {
@@ -1122,12 +1103,8 @@ outhist(void)
}
g.offset = h->offset;
- Bputc(&obuf, AHISTORY);
- Bputc(&obuf, AHISTORY>>8);
- Bputc(&obuf, h->line);
- Bputc(&obuf, h->line>>8);
- Bputc(&obuf, h->line>>16);
- Bputc(&obuf, h->line>>24);
+ BPUTLE2(&obuf, AHISTORY);
+ BPUTLE4(&obuf, h->line);
zaddr(&nullgen, 0);
zaddr(&g, 0);
diff --git a/src/cmd/8a/y.tab.c b/src/cmd/8a/y.tab.c
index 6616c9139..aec4856f6 100644
--- a/src/cmd/8a/y.tab.c
+++ b/src/cmd/8a/y.tab.c
@@ -1,24 +1,21 @@
-/* A Bison parser, made by GNU Bison 2.3. */
+/* A Bison parser, made by GNU Bison 2.5. */
-/* Skeleton implementation for Bison's Yacc-like parsers in C
-
- Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
- Free Software Foundation, Inc.
-
- This program is free software; you can redistribute it and/or modify
+/* Bison implementation for Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
+
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* As a special exception, you may create a larger work that contains
part or all of the Bison parser skeleton and distribute that work
@@ -29,7 +26,7 @@
special exception, which will cause the skeleton and the resulting
Bison output files to be licensed under the GNU General Public
License without this special exception.
-
+
This special exception was added by the Free Software Foundation in
version 2.2 of Bison. */
@@ -47,7 +44,7 @@
#define YYBISON 1
/* Bison version. */
-#define YYBISON_VERSION "2.3"
+#define YYBISON_VERSION "2.5"
/* Skeleton name. */
#define YYSKELETON_NAME "yacc.c"
@@ -55,11 +52,51 @@
/* Pure parsers. */
#define YYPURE 0
+/* Push parsers. */
+#define YYPUSH 0
+
+/* Pull parsers. */
+#define YYPULL 1
+
/* Using locations. */
#define YYLSP_NEEDED 0
+/* Copy the first part of user declarations. */
+
+/* Line 268 of yacc.c */
+#line 31 "a.y"
+
+#include <u.h>
+#include <stdio.h> /* if we don't, bison will, and a.h re-#defines getc */
+#include <libc.h>
+#include "a.h"
+#include "../../pkg/runtime/funcdata.h"
+
+
+/* Line 268 of yacc.c */
+#line 80 "y.tab.c"
+
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+/* Enabling the token table. */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
+
/* Tokens. */
#ifndef YYTOKENTYPE
# define YYTOKENTYPE
@@ -82,21 +119,23 @@
LTYPEG = 271,
LTYPEXC = 272,
LTYPEX = 273,
- LCONST = 274,
- LFP = 275,
- LPC = 276,
- LSB = 277,
- LBREG = 278,
- LLREG = 279,
- LSREG = 280,
- LFREG = 281,
- LXREG = 282,
- LFCONST = 283,
- LSCONST = 284,
- LSP = 285,
- LNAME = 286,
- LLAB = 287,
- LVAR = 288
+ LTYPEPC = 274,
+ LTYPEF = 275,
+ LCONST = 276,
+ LFP = 277,
+ LPC = 278,
+ LSB = 279,
+ LBREG = 280,
+ LLREG = 281,
+ LSREG = 282,
+ LFREG = 283,
+ LXREG = 284,
+ LFCONST = 285,
+ LSCONST = 286,
+ LSP = 287,
+ LNAME = 288,
+ LLAB = 289,
+ LVAR = 290
};
#endif
/* Tokens. */
@@ -116,56 +155,34 @@
#define LTYPEG 271
#define LTYPEXC 272
#define LTYPEX 273
-#define LCONST 274
-#define LFP 275
-#define LPC 276
-#define LSB 277
-#define LBREG 278
-#define LLREG 279
-#define LSREG 280
-#define LFREG 281
-#define LXREG 282
-#define LFCONST 283
-#define LSCONST 284
-#define LSP 285
-#define LNAME 286
-#define LLAB 287
-#define LVAR 288
-
-
-
-
-/* Copy the first part of user declarations. */
-#line 31 "a.y"
-
-#include <u.h>
-#include <stdio.h> /* if we don't, bison will, and a.h re-#defines getc */
-#include <libc.h>
-#include "a.h"
-
+#define LTYPEPC 274
+#define LTYPEF 275
+#define LCONST 276
+#define LFP 277
+#define LPC 278
+#define LSB 279
+#define LBREG 280
+#define LLREG 281
+#define LSREG 282
+#define LFREG 283
+#define LXREG 284
+#define LFCONST 285
+#define LSCONST 286
+#define LSP 287
+#define LNAME 288
+#define LLAB 289
+#define LVAR 290
-/* Enabling traces. */
-#ifndef YYDEBUG
-# define YYDEBUG 0
-#endif
-/* Enabling verbose error messages. */
-#ifdef YYERROR_VERBOSE
-# undef YYERROR_VERBOSE
-# define YYERROR_VERBOSE 1
-#else
-# define YYERROR_VERBOSE 0
-#endif
-/* Enabling the token table. */
-#ifndef YYTOKEN_TABLE
-# define YYTOKEN_TABLE 0
-#endif
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
typedef union YYSTYPE
-#line 37 "a.y"
{
+
+/* Line 293 of yacc.c */
+#line 38 "a.y"
+
Sym *sym;
int32 lval;
struct {
@@ -176,22 +193,23 @@ typedef union YYSTYPE
char sval[8];
Gen gen;
Gen2 gen2;
-}
-/* Line 193 of yacc.c. */
-#line 182 "y.tab.c"
- YYSTYPE;
+
+
+
+/* Line 293 of yacc.c */
+#line 201 "y.tab.c"
+} YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
# define YYSTYPE_IS_DECLARED 1
-# define YYSTYPE_IS_TRIVIAL 1
#endif
-
/* Copy the second part of user declarations. */
-/* Line 216 of yacc.c. */
-#line 195 "y.tab.c"
+/* Line 343 of yacc.c */
+#line 213 "y.tab.c"
#ifdef short
# undef short
@@ -266,14 +284,14 @@ typedef short int yytype_int16;
#if (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
static int
-YYID (int i)
+YYID (int yyi)
#else
static int
-YYID (i)
- int i;
+YYID (yyi)
+ int yyi;
#endif
{
- return i;
+ return yyi;
}
#endif
@@ -294,11 +312,11 @@ YYID (i)
# define alloca _alloca
# else
# define YYSTACK_ALLOC alloca
-# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
-# ifndef _STDLIB_H
-# define _STDLIB_H 1
+# ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
# endif
# endif
# endif
@@ -321,24 +339,24 @@ YYID (i)
# ifndef YYSTACK_ALLOC_MAXIMUM
# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
# endif
-# if (defined __cplusplus && ! defined _STDLIB_H \
+# if (defined __cplusplus && ! defined EXIT_SUCCESS \
&& ! ((defined YYMALLOC || defined malloc) \
&& (defined YYFREE || defined free)))
# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
-# ifndef _STDLIB_H
-# define _STDLIB_H 1
+# ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
# endif
# endif
# ifndef YYMALLOC
# define YYMALLOC malloc
-# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+# if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
# endif
# endif
# ifndef YYFREE
# define YYFREE free
-# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+# if ! defined free && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
void free (void *); /* INFRINGES ON USER NAME SPACE */
# endif
@@ -354,9 +372,9 @@ void free (void *); /* INFRINGES ON USER NAME SPACE */
/* A type that is properly aligned for any stack member. */
union yyalloc
{
- yytype_int16 yyss;
- YYSTYPE yyvs;
- };
+ yytype_int16 yyss_alloc;
+ YYSTYPE yyvs_alloc;
+};
/* The size of the maximum gap between one aligned stack and the next. */
# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
@@ -367,6 +385,27 @@ union yyalloc
((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+ YYSTACK_GAP_MAXIMUM)
+# define YYCOPY_NEEDED 1
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack_alloc, Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \
+ Stack = &yyptr->Stack_alloc; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (YYID (0))
+
+#endif
+
+#if defined YYCOPY_NEEDED && YYCOPY_NEEDED
/* Copy COUNT objects from FROM to TO. The source and destination do
not overlap. */
# ifndef YYCOPY
@@ -384,42 +423,25 @@ union yyalloc
while (YYID (0))
# endif
# endif
-
-/* Relocate STACK from its old location to the new one. The
- local variables YYSIZE and YYSTACKSIZE give the old and new number of
- elements in the stack, and YYPTR gives the new location of the
- stack. Advance YYPTR to a properly aligned location for the next
- stack. */
-# define YYSTACK_RELOCATE(Stack) \
- do \
- { \
- YYSIZE_T yynewbytes; \
- YYCOPY (&yyptr->Stack, Stack, yysize); \
- Stack = &yyptr->Stack; \
- yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
- yyptr += yynewbytes / sizeof (*yyptr); \
- } \
- while (YYID (0))
-
-#endif
+#endif /* !YYCOPY_NEEDED */
/* YYFINAL -- State number of the termination state. */
#define YYFINAL 2
/* YYLAST -- Last index in YYTABLE. */
-#define YYLAST 537
+#define YYLAST 546
/* YYNTOKENS -- Number of terminals. */
-#define YYNTOKENS 52
+#define YYNTOKENS 54
/* YYNNTS -- Number of nonterminals. */
-#define YYNNTS 39
+#define YYNNTS 41
/* YYNRULES -- Number of rules. */
-#define YYNRULES 131
+#define YYNRULES 135
/* YYNRULES -- Number of states. */
-#define YYNSTATES 266
+#define YYNSTATES 276
/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
#define YYUNDEFTOK 2
-#define YYMAXUTOK 288
+#define YYMAXUTOK 290
#define YYTRANSLATE(YYX) \
((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
@@ -430,16 +452,16 @@ static const yytype_uint8 yytranslate[] =
0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 50, 12, 5, 2,
- 48, 49, 10, 8, 47, 9, 2, 11, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 44, 45,
- 6, 46, 7, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 52, 12, 5, 2,
+ 50, 51, 10, 8, 49, 9, 2, 11, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 46, 47,
+ 6, 48, 7, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 4, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 3, 2, 51, 2, 2, 2,
+ 2, 2, 2, 2, 3, 2, 53, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
@@ -455,7 +477,8 @@ static const yytype_uint8 yytranslate[] =
2, 2, 2, 2, 2, 2, 1, 2, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
- 35, 36, 37, 38, 39, 40, 41, 42, 43
+ 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
+ 45
};
#if YYDEBUG
@@ -466,87 +489,88 @@ static const yytype_uint16 yyprhs[] =
0, 0, 3, 4, 5, 9, 10, 15, 16, 21,
23, 26, 29, 33, 37, 40, 43, 46, 49, 52,
55, 58, 61, 64, 67, 70, 73, 76, 79, 82,
- 85, 86, 88, 92, 96, 99, 101, 104, 106, 109,
- 111, 115, 121, 125, 131, 134, 136, 139, 141, 143,
- 147, 153, 157, 163, 166, 168, 172, 176, 182, 188,
- 194, 196, 198, 200, 202, 205, 208, 210, 212, 214,
- 216, 218, 223, 226, 229, 231, 233, 235, 237, 239,
- 241, 244, 247, 250, 253, 258, 264, 268, 271, 273,
- 276, 280, 285, 287, 289, 291, 296, 301, 308, 318,
- 328, 332, 336, 341, 347, 356, 358, 365, 371, 379,
- 380, 383, 386, 388, 390, 392, 394, 396, 399, 402,
- 405, 409, 411, 415, 419, 423, 427, 431, 436, 441,
- 445, 449
+ 85, 88, 91, 92, 94, 98, 102, 105, 107, 110,
+ 112, 115, 117, 121, 127, 131, 137, 140, 142, 145,
+ 147, 149, 153, 159, 163, 169, 172, 174, 178, 182,
+ 188, 194, 200, 204, 208, 210, 212, 214, 216, 219,
+ 222, 224, 226, 228, 230, 232, 237, 240, 243, 245,
+ 247, 249, 251, 253, 255, 258, 261, 264, 267, 272,
+ 278, 282, 285, 287, 290, 294, 299, 301, 303, 305,
+ 310, 315, 322, 332, 342, 346, 350, 355, 361, 370,
+ 372, 379, 385, 393, 394, 397, 400, 402, 404, 406,
+ 408, 410, 413, 416, 419, 423, 425, 429, 433, 437,
+ 441, 445, 450, 455, 459, 463
};
/* YYRHS -- A `-1'-separated list of the rules' RHS. */
static const yytype_int8 yyrhs[] =
{
- 53, 0, -1, -1, -1, 53, 54, 55, -1, -1,
- 42, 44, 56, 55, -1, -1, 41, 44, 57, 55,
- -1, 45, -1, 58, 45, -1, 1, 45, -1, 41,
- 46, 90, -1, 43, 46, 90, -1, 13, 59, -1,
- 14, 63, -1, 15, 62, -1, 16, 60, -1, 17,
- 61, -1, 21, 64, -1, 19, 65, -1, 22, 66,
- -1, 18, 67, -1, 20, 68, -1, 23, 69, -1,
- 24, 70, -1, 25, 71, -1, 26, 72, -1, 27,
- 73, -1, 28, 74, -1, -1, 47, -1, 77, 47,
- 75, -1, 75, 47, 77, -1, 77, 47, -1, 77,
- -1, 47, 75, -1, 75, -1, 47, 78, -1, 78,
- -1, 80, 47, 78, -1, 86, 11, 89, 47, 80,
- -1, 83, 47, 81, -1, 83, 47, 89, 47, 81,
- -1, 47, 76, -1, 76, -1, 10, 86, -1, 59,
- -1, 63, -1, 77, 47, 75, -1, 77, 47, 75,
- 44, 34, -1, 77, 47, 75, -1, 77, 47, 75,
- 44, 35, -1, 77, 47, -1, 77, -1, 77, 47,
- 75, -1, 83, 47, 80, -1, 83, 47, 89, 47,
- 80, -1, 79, 47, 75, 47, 89, -1, 80, 47,
- 75, 47, 79, -1, 79, -1, 83, -1, 78, -1,
- 85, -1, 10, 79, -1, 10, 84, -1, 79, -1,
- 84, -1, 80, -1, 75, -1, 80, -1, 89, 48,
- 31, 49, -1, 41, 87, -1, 42, 87, -1, 33,
- -1, 36, -1, 34, -1, 37, -1, 40, -1, 35,
- -1, 50, 89, -1, 50, 86, -1, 50, 39, -1,
- 50, 38, -1, 50, 48, 38, 49, -1, 50, 48,
- 9, 38, 49, -1, 50, 9, 38, -1, 50, 82,
- -1, 29, -1, 9, 29, -1, 29, 9, 29, -1,
- 9, 29, 9, 29, -1, 84, -1, 85, -1, 89,
- -1, 89, 48, 34, 49, -1, 89, 48, 40, 49,
- -1, 89, 48, 34, 10, 89, 49, -1, 89, 48,
- 34, 49, 48, 34, 10, 89, 49, -1, 89, 48,
- 34, 49, 48, 35, 10, 89, 49, -1, 48, 34,
- 49, -1, 48, 40, 49, -1, 89, 48, 35, 49,
- -1, 48, 34, 10, 89, 49, -1, 48, 34, 49,
- 48, 34, 10, 89, 49, -1, 86, -1, 86, 48,
- 34, 10, 89, 49, -1, 41, 87, 48, 88, 49,
- -1, 41, 6, 7, 87, 48, 32, 49, -1, -1,
- 8, 89, -1, 9, 89, -1, 32, -1, 40, -1,
- 30, -1, 29, -1, 43, -1, 9, 89, -1, 8,
- 89, -1, 51, 89, -1, 48, 90, 49, -1, 89,
- -1, 90, 8, 90, -1, 90, 9, 90, -1, 90,
- 10, 90, -1, 90, 11, 90, -1, 90, 12, 90,
- -1, 90, 6, 6, 90, -1, 90, 7, 7, 90,
- -1, 90, 5, 90, -1, 90, 4, 90, -1, 90,
- 3, 90, -1
+ 55, 0, -1, -1, -1, 55, 56, 57, -1, -1,
+ 44, 46, 58, 57, -1, -1, 43, 46, 59, 57,
+ -1, 47, -1, 60, 47, -1, 1, 47, -1, 43,
+ 48, 94, -1, 45, 48, 94, -1, 13, 61, -1,
+ 14, 65, -1, 15, 64, -1, 16, 62, -1, 17,
+ 63, -1, 21, 66, -1, 19, 67, -1, 22, 68,
+ -1, 18, 69, -1, 20, 70, -1, 23, 71, -1,
+ 24, 72, -1, 25, 73, -1, 26, 74, -1, 27,
+ 75, -1, 28, 76, -1, 29, 77, -1, 30, 78,
+ -1, -1, 49, -1, 81, 49, 79, -1, 79, 49,
+ 81, -1, 81, 49, -1, 81, -1, 49, 79, -1,
+ 79, -1, 49, 82, -1, 82, -1, 84, 49, 82,
+ -1, 90, 11, 93, 49, 84, -1, 87, 49, 85,
+ -1, 87, 49, 93, 49, 85, -1, 49, 80, -1,
+ 80, -1, 10, 90, -1, 61, -1, 65, -1, 81,
+ 49, 79, -1, 81, 49, 79, 46, 36, -1, 81,
+ 49, 79, -1, 81, 49, 79, 46, 37, -1, 81,
+ 49, -1, 81, -1, 81, 49, 79, -1, 87, 49,
+ 84, -1, 87, 49, 93, 49, 84, -1, 83, 49,
+ 79, 49, 93, -1, 84, 49, 79, 49, 83, -1,
+ 81, 49, 81, -1, 81, 49, 81, -1, 83, -1,
+ 87, -1, 82, -1, 89, -1, 10, 83, -1, 10,
+ 88, -1, 83, -1, 88, -1, 84, -1, 79, -1,
+ 84, -1, 93, 50, 33, 51, -1, 43, 91, -1,
+ 44, 91, -1, 35, -1, 38, -1, 36, -1, 39,
+ -1, 42, -1, 37, -1, 52, 93, -1, 52, 90,
+ -1, 52, 41, -1, 52, 40, -1, 52, 50, 40,
+ 51, -1, 52, 50, 9, 40, 51, -1, 52, 9,
+ 40, -1, 52, 86, -1, 31, -1, 9, 31, -1,
+ 31, 9, 31, -1, 9, 31, 9, 31, -1, 88,
+ -1, 89, -1, 93, -1, 93, 50, 36, 51, -1,
+ 93, 50, 42, 51, -1, 93, 50, 36, 10, 93,
+ 51, -1, 93, 50, 36, 51, 50, 36, 10, 93,
+ 51, -1, 93, 50, 36, 51, 50, 37, 10, 93,
+ 51, -1, 50, 36, 51, -1, 50, 42, 51, -1,
+ 93, 50, 37, 51, -1, 50, 36, 10, 93, 51,
+ -1, 50, 36, 51, 50, 36, 10, 93, 51, -1,
+ 90, -1, 90, 50, 36, 10, 93, 51, -1, 43,
+ 91, 50, 92, 51, -1, 43, 6, 7, 91, 50,
+ 34, 51, -1, -1, 8, 93, -1, 9, 93, -1,
+ 34, -1, 42, -1, 32, -1, 31, -1, 45, -1,
+ 9, 93, -1, 8, 93, -1, 53, 93, -1, 50,
+ 94, 51, -1, 93, -1, 94, 8, 94, -1, 94,
+ 9, 94, -1, 94, 10, 94, -1, 94, 11, 94,
+ -1, 94, 12, 94, -1, 94, 6, 6, 94, -1,
+ 94, 7, 7, 94, -1, 94, 5, 94, -1, 94,
+ 4, 94, -1, 94, 3, 94, -1
};
/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
static const yytype_uint16 yyrline[] =
{
- 0, 68, 68, 70, 69, 77, 76, 84, 83, 89,
- 90, 91, 94, 99, 105, 106, 107, 108, 109, 110,
- 111, 112, 113, 114, 115, 116, 117, 118, 119, 120,
- 123, 127, 134, 141, 148, 153, 160, 165, 172, 177,
- 182, 189, 197, 202, 210, 215, 220, 229, 230, 233,
- 238, 248, 253, 263, 268, 273, 280, 285, 293, 301,
- 311, 312, 315, 316, 317, 321, 325, 326, 327, 330,
- 331, 334, 340, 349, 358, 363, 368, 373, 378, 383,
- 390, 396, 407, 413, 419, 425, 431, 439, 448, 453,
- 458, 463, 470, 471, 474, 480, 486, 492, 501, 510,
- 519, 524, 529, 535, 543, 553, 557, 566, 573, 582,
- 585, 589, 595, 596, 600, 603, 604, 608, 612, 616,
- 620, 626, 627, 631, 635, 639, 643, 647, 651, 655,
- 659, 663
+ 0, 69, 69, 71, 70, 78, 77, 85, 84, 90,
+ 91, 92, 95, 100, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
+ 122, 123, 126, 130, 137, 144, 151, 156, 163, 168,
+ 175, 180, 185, 192, 200, 205, 213, 218, 223, 232,
+ 233, 236, 241, 251, 256, 266, 271, 276, 283, 288,
+ 296, 304, 314, 323, 334, 335, 338, 339, 340, 344,
+ 348, 349, 350, 353, 354, 357, 363, 372, 381, 386,
+ 391, 396, 401, 406, 413, 419, 430, 436, 442, 448,
+ 454, 462, 471, 476, 481, 486, 493, 494, 497, 503,
+ 509, 515, 524, 533, 542, 547, 552, 558, 566, 576,
+ 580, 589, 596, 605, 608, 612, 618, 619, 623, 626,
+ 627, 631, 635, 639, 643, 649, 650, 654, 658, 662,
+ 666, 670, 674, 678, 682, 686
};
#endif
@@ -558,14 +582,15 @@ static const char *const yytname[] =
"$end", "error", "$undefined", "'|'", "'^'", "'&'", "'<'", "'>'", "'+'",
"'-'", "'*'", "'/'", "'%'", "LTYPE0", "LTYPE1", "LTYPE2", "LTYPE3",
"LTYPE4", "LTYPEC", "LTYPED", "LTYPEN", "LTYPER", "LTYPET", "LTYPES",
- "LTYPEM", "LTYPEI", "LTYPEG", "LTYPEXC", "LTYPEX", "LCONST", "LFP",
- "LPC", "LSB", "LBREG", "LLREG", "LSREG", "LFREG", "LXREG", "LFCONST",
- "LSCONST", "LSP", "LNAME", "LLAB", "LVAR", "':'", "';'", "'='", "','",
- "'('", "')'", "'$'", "'~'", "$accept", "prog", "@1", "line", "@2", "@3",
- "inst", "nonnon", "rimrem", "remrim", "rimnon", "nonrem", "nonrel",
- "spec1", "spec2", "spec3", "spec4", "spec5", "spec6", "spec7", "spec8",
- "spec9", "spec10", "rem", "rom", "rim", "rel", "reg", "imm", "imm2",
- "con2", "mem", "omem", "nmem", "nam", "offset", "pointer", "con", "expr", 0
+ "LTYPEM", "LTYPEI", "LTYPEG", "LTYPEXC", "LTYPEX", "LTYPEPC", "LTYPEF",
+ "LCONST", "LFP", "LPC", "LSB", "LBREG", "LLREG", "LSREG", "LFREG",
+ "LXREG", "LFCONST", "LSCONST", "LSP", "LNAME", "LLAB", "LVAR", "':'",
+ "';'", "'='", "','", "'('", "')'", "'$'", "'~'", "$accept", "prog",
+ "$@1", "line", "$@2", "$@3", "inst", "nonnon", "rimrem", "remrim",
+ "rimnon", "nonrem", "nonrel", "spec1", "spec2", "spec3", "spec4",
+ "spec5", "spec6", "spec7", "spec8", "spec9", "spec10", "spec11",
+ "spec12", "rem", "rom", "rim", "rel", "reg", "imm", "imm2", "con2",
+ "mem", "omem", "nmem", "nam", "offset", "pointer", "con", "expr", 0
};
#endif
@@ -578,28 +603,28 @@ static const yytype_uint16 yytoknum[] =
42, 47, 37, 258, 259, 260, 261, 262, 263, 264,
265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
275, 276, 277, 278, 279, 280, 281, 282, 283, 284,
- 285, 286, 287, 288, 58, 59, 61, 44, 40, 41,
- 36, 126
+ 285, 286, 287, 288, 289, 290, 58, 59, 61, 44,
+ 40, 41, 36, 126
};
# endif
/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
static const yytype_uint8 yyr1[] =
{
- 0, 52, 53, 54, 53, 56, 55, 57, 55, 55,
- 55, 55, 58, 58, 58, 58, 58, 58, 58, 58,
- 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
- 59, 59, 60, 61, 62, 62, 63, 63, 64, 64,
- 64, 65, 66, 66, 67, 67, 67, 68, 68, 69,
- 69, 70, 70, 71, 71, 71, 72, 72, 73, 74,
- 75, 75, 76, 76, 76, 76, 76, 76, 76, 77,
- 77, 78, 78, 78, 79, 79, 79, 79, 79, 79,
- 80, 80, 80, 80, 80, 80, 80, 81, 82, 82,
- 82, 82, 83, 83, 84, 84, 84, 84, 84, 84,
- 84, 84, 84, 84, 84, 85, 85, 86, 86, 87,
- 87, 87, 88, 88, 88, 89, 89, 89, 89, 89,
- 89, 90, 90, 90, 90, 90, 90, 90, 90, 90,
- 90, 90
+ 0, 54, 55, 56, 55, 58, 57, 59, 57, 57,
+ 57, 57, 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 61, 61, 62, 63, 64, 64, 65, 65,
+ 66, 66, 66, 67, 68, 68, 69, 69, 69, 70,
+ 70, 71, 71, 72, 72, 73, 73, 73, 74, 74,
+ 75, 76, 77, 78, 79, 79, 80, 80, 80, 80,
+ 80, 80, 80, 81, 81, 82, 82, 82, 83, 83,
+ 83, 83, 83, 83, 84, 84, 84, 84, 84, 84,
+ 84, 85, 86, 86, 86, 86, 87, 87, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88, 88, 89,
+ 89, 90, 90, 91, 91, 91, 92, 92, 92, 93,
+ 93, 93, 93, 93, 93, 94, 94, 94, 94, 94,
+ 94, 94, 94, 94, 94, 94
};
/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
@@ -608,257 +633,269 @@ static const yytype_uint8 yyr2[] =
0, 2, 0, 0, 3, 0, 4, 0, 4, 1,
2, 2, 3, 3, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 0, 1, 3, 3, 2, 1, 2, 1, 2, 1,
- 3, 5, 3, 5, 2, 1, 2, 1, 1, 3,
- 5, 3, 5, 2, 1, 3, 3, 5, 5, 5,
- 1, 1, 1, 1, 2, 2, 1, 1, 1, 1,
- 1, 4, 2, 2, 1, 1, 1, 1, 1, 1,
- 2, 2, 2, 2, 4, 5, 3, 2, 1, 2,
- 3, 4, 1, 1, 1, 4, 4, 6, 9, 9,
- 3, 3, 4, 5, 8, 1, 6, 5, 7, 0,
- 2, 2, 1, 1, 1, 1, 1, 2, 2, 2,
- 3, 1, 3, 3, 3, 3, 3, 4, 4, 3,
- 3, 3
+ 2, 2, 0, 1, 3, 3, 2, 1, 2, 1,
+ 2, 1, 3, 5, 3, 5, 2, 1, 2, 1,
+ 1, 3, 5, 3, 5, 2, 1, 3, 3, 5,
+ 5, 5, 3, 3, 1, 1, 1, 1, 2, 2,
+ 1, 1, 1, 1, 1, 4, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 4, 5,
+ 3, 2, 1, 2, 3, 4, 1, 1, 1, 4,
+ 4, 6, 9, 9, 3, 3, 4, 5, 8, 1,
+ 6, 5, 7, 0, 2, 2, 1, 1, 1, 1,
+ 1, 2, 2, 2, 3, 1, 3, 3, 3, 3,
+ 3, 4, 4, 3, 3, 3
};
-/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
- STATE-NUM when YYTABLE doesn't specify something else to do. Zero
+/* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM.
+ Performed when YYTABLE doesn't specify something else to do. Zero
means the default is an error. */
static const yytype_uint8 yydefact[] =
{
- 2, 3, 1, 0, 0, 30, 0, 0, 0, 0,
- 0, 0, 30, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 9, 4, 0, 11, 31, 14,
- 0, 0, 115, 74, 76, 79, 75, 77, 78, 109,
- 116, 0, 0, 0, 15, 37, 60, 61, 92, 93,
- 105, 94, 0, 16, 69, 35, 70, 17, 0, 18,
- 0, 0, 109, 109, 0, 22, 45, 62, 66, 68,
- 67, 63, 94, 20, 0, 31, 47, 48, 23, 109,
- 0, 0, 19, 39, 0, 0, 21, 0, 24, 0,
- 25, 0, 26, 54, 27, 0, 28, 0, 29, 0,
- 7, 0, 5, 0, 10, 118, 117, 0, 0, 0,
- 0, 36, 0, 0, 121, 0, 119, 0, 0, 0,
- 83, 82, 0, 81, 80, 34, 0, 0, 64, 65,
- 46, 72, 73, 0, 44, 0, 0, 72, 38, 0,
- 0, 0, 0, 0, 53, 0, 0, 0, 0, 12,
- 0, 13, 109, 110, 111, 0, 0, 100, 101, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 120,
- 0, 0, 0, 0, 86, 0, 0, 32, 33, 0,
- 0, 40, 0, 42, 0, 49, 51, 55, 56, 0,
- 0, 0, 8, 6, 0, 114, 112, 113, 0, 0,
- 0, 131, 130, 129, 0, 0, 122, 123, 124, 125,
- 126, 0, 0, 95, 102, 96, 0, 84, 71, 0,
- 0, 88, 87, 0, 0, 0, 0, 0, 0, 0,
- 107, 103, 0, 127, 128, 0, 0, 0, 85, 41,
- 89, 0, 43, 50, 52, 57, 58, 59, 0, 0,
- 106, 97, 0, 0, 0, 90, 108, 0, 0, 0,
- 91, 104, 0, 0, 98, 99
+ 2, 3, 1, 0, 0, 32, 0, 0, 0, 0,
+ 0, 0, 32, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 9, 4, 0, 11,
+ 33, 14, 0, 0, 119, 78, 80, 83, 79, 81,
+ 82, 113, 120, 0, 0, 0, 15, 39, 64, 65,
+ 96, 97, 109, 98, 0, 16, 73, 37, 74, 17,
+ 0, 18, 0, 0, 113, 113, 0, 22, 47, 66,
+ 70, 72, 71, 67, 98, 20, 0, 33, 49, 50,
+ 23, 113, 0, 0, 19, 41, 0, 0, 21, 0,
+ 24, 0, 25, 0, 26, 56, 27, 0, 28, 0,
+ 29, 0, 30, 0, 31, 0, 7, 0, 5, 0,
+ 10, 122, 121, 0, 0, 0, 0, 38, 0, 0,
+ 125, 0, 123, 0, 0, 0, 87, 86, 0, 85,
+ 84, 36, 0, 0, 68, 69, 48, 76, 77, 0,
+ 46, 0, 0, 76, 40, 0, 0, 0, 0, 0,
+ 55, 0, 0, 0, 0, 0, 0, 12, 0, 13,
+ 113, 114, 115, 0, 0, 104, 105, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 124, 0, 0,
+ 0, 0, 90, 0, 0, 34, 35, 0, 0, 42,
+ 0, 44, 0, 51, 53, 57, 58, 0, 0, 0,
+ 62, 63, 8, 6, 0, 118, 116, 117, 0, 0,
+ 0, 135, 134, 133, 0, 0, 126, 127, 128, 129,
+ 130, 0, 0, 99, 106, 100, 0, 88, 75, 0,
+ 0, 92, 91, 0, 0, 0, 0, 0, 0, 0,
+ 111, 107, 0, 131, 132, 0, 0, 0, 89, 43,
+ 93, 0, 45, 52, 54, 59, 60, 61, 0, 0,
+ 110, 101, 0, 0, 0, 94, 112, 0, 0, 0,
+ 95, 108, 0, 0, 102, 103
};
/* YYDEFGOTO[NTERM-NUM]. */
static const yytype_int16 yydefgoto[] =
{
- -1, 1, 3, 25, 150, 148, 26, 29, 57, 59,
- 53, 44, 82, 73, 86, 65, 78, 88, 90, 92,
- 94, 96, 98, 54, 66, 55, 67, 46, 56, 183,
- 222, 47, 48, 49, 50, 110, 198, 51, 115
+ -1, 1, 3, 27, 158, 156, 28, 31, 59, 61,
+ 55, 46, 84, 75, 88, 67, 80, 90, 92, 94,
+ 96, 98, 100, 102, 104, 56, 68, 57, 69, 48,
+ 58, 191, 232, 49, 50, 51, 52, 116, 208, 53,
+ 121
};
/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
STATE-NUM. */
-#define YYPACT_NINF -100
+#define YYPACT_NINF -104
static const yytype_int16 yypact[] =
{
- -100, 22, -100, 165, -32, -22, 265, 288, 288, 334,
- 195, -1, 311, 212, 416, 288, 288, 288, 416, 81,
- 7, -16, 24, -12, -100, -100, -4, -100, -100, -100,
- 469, 469, -100, -100, -100, -100, -100, -100, -100, 39,
- -100, 334, 387, 469, -100, -100, -100, -100, -100, -100,
- 46, 65, 372, -100, -100, 72, -100, -100, 83, -100,
- 86, 334, 39, 102, 242, -100, -100, -100, -100, -100,
- -100, -100, 77, -100, 117, 334, -100, -100, -100, 102,
- 410, 469, -100, -100, 89, 90, -100, 92, -100, 97,
- -100, 98, -100, 100, -100, 101, -100, 105, -100, 106,
- -100, 469, -100, 469, -100, -100, -100, 135, 469, 469,
- 114, -100, -6, 128, -100, 71, -100, 175, 32, 218,
- -100, -100, 425, -100, -100, -100, 334, 288, -100, -100,
- -100, 114, -100, 357, -100, 29, 469, -100, -100, 410,
- 181, 440, 334, 334, 334, 457, 334, 334, 165, 164,
- 165, 164, 102, -100, -100, 6, 469, 166, -100, 469,
- 469, 469, 207, 208, 469, 469, 469, 469, 469, -100,
- 206, 4, 173, 174, -100, 463, 176, -100, -100, 184,
- 187, -100, 15, -100, 193, 200, 213, -100, -100, 211,
- 217, 220, -100, -100, 222, -100, -100, -100, 216, 219,
- 238, 517, 525, 78, 469, 469, 95, 95, -100, -100,
- -100, 469, 469, 232, -100, -100, 237, -100, -100, 7,
- 252, 278, -100, 239, 254, 256, 7, 469, 81, 263,
- -100, -100, 293, 188, 188, 255, 258, 88, -100, -100,
- 300, 281, -100, -100, -100, -100, -100, -100, 262, 469,
- -100, -100, 304, 305, 289, -100, -100, 277, 469, 469,
- -100, -100, 283, 284, -100, -100
+ -104, 4, -104, 173, -26, -25, 277, 297, 297, 349,
+ 225, -14, 329, 396, 18, 297, 297, 297, 18, 171,
+ -20, 297, 297, 2, -4, 26, -104, -104, 43, -104,
+ -104, -104, 478, 478, -104, -104, -104, -104, -104, -104,
+ -104, 111, -104, 349, 402, 478, -104, -104, -104, -104,
+ -104, -104, -12, -5, 83, -104, -104, 44, -104, -104,
+ 46, -104, 49, 349, 111, 113, 245, -104, -104, -104,
+ -104, -104, -104, -104, 50, -104, 100, 349, -104, -104,
+ -104, 113, 420, 478, -104, -104, 64, 66, -104, 78,
+ -104, 80, -104, 85, -104, 89, -104, 93, -104, 98,
+ -104, 101, -104, 112, -104, 121, -104, 478, -104, 478,
+ -104, -104, -104, 153, 478, 478, 135, -104, 8, 163,
+ -104, 74, -104, 179, 52, 427, -104, -104, 445, -104,
+ -104, -104, 349, 297, -104, -104, -104, 135, -104, 381,
+ -104, 33, 478, -104, -104, 420, 186, 451, 349, 349,
+ 349, 460, 349, 349, 297, 297, 173, 172, 173, 172,
+ 113, -104, -104, 5, 478, 180, -104, 478, 478, 478,
+ 226, 224, 478, 478, 478, 478, 478, -104, 235, 36,
+ 195, 196, -104, 466, 197, -104, -104, 199, 202, -104,
+ 21, -104, 203, 211, 219, -104, -104, 217, 222, 223,
+ -104, -104, -104, -104, 229, -104, -104, -104, 240, 241,
+ 237, 232, 527, 534, 478, 478, 134, 134, -104, -104,
+ -104, 478, 478, 243, -104, -104, 248, -104, -104, -20,
+ 263, 287, -104, 249, 264, 265, -20, 478, 171, 269,
+ -104, -104, 294, 214, 214, 256, 258, 119, -104, -104,
+ 301, 280, -104, -104, -104, -104, -104, -104, 266, 478,
+ -104, -104, 308, 311, 292, -104, -104, 273, 478, 478,
+ -104, -104, 274, 278, -104, -104
};
/* YYPGOTO[NTERM-NUM]. */
static const yytype_int16 yypgoto[] =
{
- -100, -100, -100, -99, -100, -100, -100, 315, -100, -100,
- -100, 318, -100, -100, -100, -100, -100, -100, -100, -100,
- -100, -100, -100, 17, 270, 0, -7, -9, -8, 112,
- -100, 13, 1, -3, -2, -44, -100, -10, -64
+ -104, -104, -104, -103, -104, -104, -104, 319, -104, -104,
+ -104, 331, -104, -104, -104, -104, -104, -104, -104, -104,
+ -104, -104, -104, -104, -104, 19, 275, -2, -6, -9,
+ -8, 115, -104, 22, 1, -1, -3, -48, -104, -10,
+ -66
};
/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
positive, shift that token. If negative, reduce the rule which
- number is the opposite. If zero, do what YYDEFACT says.
- If YYTABLE_NINF, syntax error. */
+ number is the opposite. If YYTABLE_NINF, syntax error. */
#define YYTABLE_NINF -1
static const yytype_uint16 yytable[] =
{
- 72, 68, 69, 85, 156, 84, 83, 71, 58, 74,
- 97, 70, 99, 27, 212, 89, 91, 93, 131, 132,
- 105, 106, 2, 45, 220, 28, 60, 87, 100, 45,
- 101, 95, 114, 116, 103, 137, 195, 149, 196, 151,
- 39, 104, 124, 157, 221, 107, 197, 108, 109, 192,
- 123, 193, 128, 213, 72, 68, 69, 52, 111, 130,
- 179, 71, 129, 171, 172, 70, 171, 172, 102, 173,
- 85, 114, 173, 138, 159, 160, 161, 162, 163, 164,
- 165, 166, 167, 168, 162, 163, 164, 165, 166, 167,
- 168, 114, 111, 114, 117, 201, 202, 203, 153, 154,
- 206, 207, 208, 209, 210, 166, 167, 168, 194, 106,
- 108, 109, 114, 118, 33, 34, 35, 36, 37, 125,
- 169, 38, 252, 253, 128, 135, 180, 178, 136, 85,
- 126, 184, 181, 127, 129, 189, 139, 188, 140, 141,
- 233, 234, 152, 177, 142, 143, 199, 144, 145, 114,
- 114, 114, 146, 147, 114, 114, 114, 114, 114, 185,
- 186, 187, 155, 190, 191, 106, 4, 159, 160, 161,
- 162, 163, 164, 165, 166, 167, 168, 158, 5, 6,
- 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
- 17, 18, 19, 20, 114, 114, 164, 165, 166, 167,
- 168, 235, 236, 30, 31, 61, 21, 22, 23, 170,
- 24, 239, 179, 204, 200, 205, 211, 246, 245, 247,
- 30, 31, 214, 215, 32, 217, 30, 31, 33, 34,
- 35, 36, 37, 218, 219, 38, 62, 63, 40, 257,
- 223, 32, 64, 42, 224, 52, 43, 32, 262, 263,
- 30, 31, 133, 79, 63, 40, 174, 225, 226, 80,
- 81, 40, 52, 43, 227, 230, 81, 228, 231, 43,
- 229, 32, 232, 30, 31, 33, 34, 35, 36, 37,
- 237, 240, 38, 62, 63, 40, 238, 241, 243, 182,
- 42, 244, 52, 43, 32, 248, 30, 31, 33, 34,
- 35, 36, 37, 249, 250, 38, 39, 251, 40, 254,
- 255, 256, 41, 42, 258, 259, 43, 32, 260, 30,
- 31, 33, 34, 35, 36, 37, 261, 76, 38, 39,
- 77, 40, 264, 265, 134, 242, 42, 0, 52, 43,
- 32, 0, 30, 31, 33, 34, 35, 36, 37, 0,
- 0, 38, 39, 0, 40, 0, 0, 0, 75, 42,
- 0, 0, 43, 32, 0, 30, 31, 33, 34, 35,
- 36, 37, 0, 0, 38, 39, 0, 40, 0, 0,
- 30, 119, 42, 0, 0, 43, 32, 0, 0, 0,
- 33, 34, 35, 36, 37, 30, 31, 38, 0, 0,
- 40, 32, 0, 0, 0, 42, 0, 0, 43, 0,
- 120, 121, 0, 39, 0, 40, 32, 0, 30, 31,
- 122, 112, 0, 43, 30, 31, 0, 113, 0, 0,
- 40, 0, 0, 30, 175, 81, 0, 0, 43, 32,
- 0, 0, 0, 0, 0, 32, 0, 0, 30, 31,
- 0, 79, 63, 40, 32, 0, 0, 39, 81, 40,
- 0, 43, 0, 176, 42, 30, 31, 43, 40, 32,
- 0, 30, 31, 81, 0, 0, 43, 30, 31, 0,
- 0, 0, 0, 40, 0, 0, 32, 0, 81, 0,
- 182, 43, 32, 0, 0, 0, 0, 0, 32, 0,
- 40, 216, 0, 0, 0, 81, 40, 52, 43, 0,
- 0, 81, 40, 0, 43, 0, 0, 81, 0, 0,
- 43, 160, 161, 162, 163, 164, 165, 166, 167, 168,
- 161, 162, 163, 164, 165, 166, 167, 168
+ 74, 70, 71, 87, 2, 86, 60, 85, 76, 73,
+ 99, 72, 101, 91, 93, 95, 137, 138, 164, 103,
+ 105, 29, 111, 112, 30, 47, 32, 33, 62, 41,
+ 230, 47, 54, 143, 120, 122, 89, 205, 123, 206,
+ 97, 157, 108, 159, 130, 124, 222, 207, 106, 34,
+ 107, 129, 231, 202, 134, 203, 74, 70, 71, 165,
+ 136, 41, 117, 42, 135, 73, 187, 72, 44, 179,
+ 180, 45, 87, 120, 109, 181, 144, 167, 168, 169,
+ 170, 171, 172, 173, 174, 175, 176, 223, 179, 180,
+ 110, 32, 125, 131, 181, 132, 117, 120, 133, 120,
+ 141, 211, 212, 213, 161, 162, 216, 217, 218, 219,
+ 220, 142, 204, 145, 34, 112, 146, 113, 120, 114,
+ 115, 114, 115, 126, 127, 177, 41, 147, 42, 148,
+ 134, 186, 188, 128, 149, 87, 45, 192, 150, 189,
+ 135, 197, 151, 196, 174, 175, 176, 152, 243, 244,
+ 153, 185, 200, 201, 209, 262, 263, 120, 120, 120,
+ 160, 154, 120, 120, 120, 120, 120, 193, 194, 195,
+ 155, 198, 199, 112, 4, 167, 168, 169, 170, 171,
+ 172, 173, 174, 175, 176, 163, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
+ 19, 20, 21, 22, 120, 120, 35, 36, 37, 38,
+ 39, 245, 246, 40, 166, 178, 23, 24, 25, 187,
+ 26, 249, 172, 173, 174, 175, 176, 256, 255, 257,
+ 210, 215, 214, 32, 33, 63, 168, 169, 170, 171,
+ 172, 173, 174, 175, 176, 221, 224, 225, 227, 267,
+ 228, 229, 233, 32, 33, 139, 34, 234, 272, 273,
+ 35, 36, 37, 38, 39, 235, 236, 40, 64, 65,
+ 42, 237, 238, 242, 66, 44, 34, 54, 45, 239,
+ 35, 36, 37, 38, 39, 32, 33, 40, 64, 65,
+ 42, 240, 241, 247, 250, 44, 251, 54, 45, 248,
+ 253, 190, 254, 258, 259, 32, 33, 260, 34, 261,
+ 264, 265, 35, 36, 37, 38, 39, 266, 268, 40,
+ 41, 269, 42, 270, 271, 274, 43, 44, 34, 275,
+ 45, 78, 35, 36, 37, 38, 39, 32, 33, 40,
+ 41, 140, 42, 79, 0, 0, 0, 44, 252, 54,
+ 45, 0, 0, 0, 0, 0, 0, 32, 33, 0,
+ 34, 0, 0, 0, 35, 36, 37, 38, 39, 0,
+ 0, 40, 41, 0, 42, 0, 0, 0, 77, 44,
+ 34, 0, 45, 0, 35, 36, 37, 38, 39, 32,
+ 33, 40, 41, 0, 42, 0, 0, 0, 0, 44,
+ 0, 0, 45, 0, 32, 33, 0, 0, 0, 0,
+ 32, 33, 34, 0, 0, 0, 35, 36, 37, 38,
+ 39, 0, 0, 40, 0, 0, 42, 34, 32, 33,
+ 0, 44, 0, 34, 45, 32, 33, 0, 118, 81,
+ 65, 42, 0, 0, 119, 82, 83, 42, 54, 45,
+ 0, 34, 83, 32, 183, 45, 0, 0, 34, 32,
+ 33, 0, 0, 81, 65, 42, 0, 182, 32, 33,
+ 83, 0, 42, 45, 32, 33, 34, 83, 0, 0,
+ 45, 0, 34, 0, 0, 184, 32, 33, 0, 0,
+ 42, 34, 0, 0, 0, 83, 42, 34, 45, 0,
+ 0, 83, 0, 190, 45, 42, 226, 0, 0, 34,
+ 83, 42, 54, 45, 0, 0, 83, 0, 0, 45,
+ 0, 0, 0, 42, 0, 0, 0, 0, 83, 0,
+ 0, 45, 169, 170, 171, 172, 173, 174, 175, 176,
+ 170, 171, 172, 173, 174, 175, 176
};
+#define yypact_value_is_default(yystate) \
+ ((yystate) == (-104))
+
+#define yytable_value_is_error(yytable_value) \
+ YYID (0)
+
static const yytype_int16 yycheck[] =
{
- 10, 10, 10, 13, 10, 13, 13, 10, 8, 11,
- 19, 10, 20, 45, 10, 15, 16, 17, 62, 63,
- 30, 31, 0, 6, 9, 47, 9, 14, 44, 12,
- 46, 18, 42, 43, 46, 79, 30, 101, 32, 103,
- 41, 45, 52, 49, 29, 6, 40, 8, 9, 148,
- 52, 150, 61, 49, 64, 64, 64, 50, 41, 61,
- 31, 64, 61, 34, 35, 64, 34, 35, 44, 40,
- 80, 81, 40, 80, 3, 4, 5, 6, 7, 8,
- 9, 10, 11, 12, 6, 7, 8, 9, 10, 11,
- 12, 101, 75, 103, 48, 159, 160, 161, 108, 109,
- 164, 165, 166, 167, 168, 10, 11, 12, 152, 119,
- 8, 9, 122, 48, 33, 34, 35, 36, 37, 47,
- 49, 40, 34, 35, 133, 48, 136, 127, 11, 139,
- 47, 141, 139, 47, 133, 145, 47, 145, 48, 47,
- 204, 205, 7, 126, 47, 47, 156, 47, 47, 159,
- 160, 161, 47, 47, 164, 165, 166, 167, 168, 142,
- 143, 144, 48, 146, 147, 175, 1, 3, 4, 5,
- 6, 7, 8, 9, 10, 11, 12, 49, 13, 14,
- 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
- 25, 26, 27, 28, 204, 205, 8, 9, 10, 11,
- 12, 211, 212, 8, 9, 10, 41, 42, 43, 34,
- 45, 219, 31, 6, 48, 7, 10, 227, 226, 228,
- 8, 9, 49, 49, 29, 49, 8, 9, 33, 34,
- 35, 36, 37, 49, 47, 40, 41, 42, 43, 249,
- 47, 29, 47, 48, 44, 50, 51, 29, 258, 259,
- 8, 9, 10, 41, 42, 43, 38, 44, 47, 47,
- 48, 43, 50, 51, 47, 49, 48, 47, 49, 51,
- 48, 29, 34, 8, 9, 33, 34, 35, 36, 37,
- 48, 29, 40, 41, 42, 43, 49, 9, 34, 50,
- 48, 35, 50, 51, 29, 32, 8, 9, 33, 34,
- 35, 36, 37, 10, 49, 40, 41, 49, 43, 9,
- 29, 49, 47, 48, 10, 10, 51, 29, 29, 8,
- 9, 33, 34, 35, 36, 37, 49, 12, 40, 41,
- 12, 43, 49, 49, 64, 223, 48, -1, 50, 51,
- 29, -1, 8, 9, 33, 34, 35, 36, 37, -1,
- -1, 40, 41, -1, 43, -1, -1, -1, 47, 48,
- -1, -1, 51, 29, -1, 8, 9, 33, 34, 35,
- 36, 37, -1, -1, 40, 41, -1, 43, -1, -1,
- 8, 9, 48, -1, -1, 51, 29, -1, -1, -1,
- 33, 34, 35, 36, 37, 8, 9, 40, -1, -1,
- 43, 29, -1, -1, -1, 48, -1, -1, 51, -1,
- 38, 39, -1, 41, -1, 43, 29, -1, 8, 9,
- 48, 34, -1, 51, 8, 9, -1, 40, -1, -1,
- 43, -1, -1, 8, 9, 48, -1, -1, 51, 29,
- -1, -1, -1, -1, -1, 29, -1, -1, 8, 9,
- -1, 41, 42, 43, 29, -1, -1, 41, 48, 43,
- -1, 51, -1, 38, 48, 8, 9, 51, 43, 29,
- -1, 8, 9, 48, -1, -1, 51, 8, 9, -1,
- -1, -1, -1, 43, -1, -1, 29, -1, 48, -1,
- 50, 51, 29, -1, -1, -1, -1, -1, 29, -1,
- 43, 38, -1, -1, -1, 48, 43, 50, 51, -1,
- -1, 48, 43, -1, 51, -1, -1, 48, -1, -1,
- 51, 4, 5, 6, 7, 8, 9, 10, 11, 12,
- 5, 6, 7, 8, 9, 10, 11, 12
+ 10, 10, 10, 13, 0, 13, 8, 13, 11, 10,
+ 19, 10, 20, 15, 16, 17, 64, 65, 10, 21,
+ 22, 47, 32, 33, 49, 6, 8, 9, 9, 43,
+ 9, 12, 52, 81, 44, 45, 14, 32, 50, 34,
+ 18, 107, 46, 109, 54, 50, 10, 42, 46, 31,
+ 48, 54, 31, 156, 63, 158, 66, 66, 66, 51,
+ 63, 43, 43, 45, 63, 66, 33, 66, 50, 36,
+ 37, 53, 82, 83, 48, 42, 82, 3, 4, 5,
+ 6, 7, 8, 9, 10, 11, 12, 51, 36, 37,
+ 47, 8, 9, 49, 42, 49, 77, 107, 49, 109,
+ 50, 167, 168, 169, 114, 115, 172, 173, 174, 175,
+ 176, 11, 160, 49, 31, 125, 50, 6, 128, 8,
+ 9, 8, 9, 40, 41, 51, 43, 49, 45, 49,
+ 139, 133, 142, 50, 49, 145, 53, 147, 49, 145,
+ 139, 151, 49, 151, 10, 11, 12, 49, 214, 215,
+ 49, 132, 154, 155, 164, 36, 37, 167, 168, 169,
+ 7, 49, 172, 173, 174, 175, 176, 148, 149, 150,
+ 49, 152, 153, 183, 1, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 50, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
+ 27, 28, 29, 30, 214, 215, 35, 36, 37, 38,
+ 39, 221, 222, 42, 51, 36, 43, 44, 45, 33,
+ 47, 229, 8, 9, 10, 11, 12, 237, 236, 238,
+ 50, 7, 6, 8, 9, 10, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 10, 51, 51, 51, 259,
+ 51, 49, 49, 8, 9, 10, 31, 46, 268, 269,
+ 35, 36, 37, 38, 39, 46, 49, 42, 43, 44,
+ 45, 49, 49, 36, 49, 50, 31, 52, 53, 50,
+ 35, 36, 37, 38, 39, 8, 9, 42, 43, 44,
+ 45, 51, 51, 50, 31, 50, 9, 52, 53, 51,
+ 36, 52, 37, 34, 10, 8, 9, 51, 31, 51,
+ 9, 31, 35, 36, 37, 38, 39, 51, 10, 42,
+ 43, 10, 45, 31, 51, 51, 49, 50, 31, 51,
+ 53, 12, 35, 36, 37, 38, 39, 8, 9, 42,
+ 43, 66, 45, 12, -1, -1, -1, 50, 233, 52,
+ 53, -1, -1, -1, -1, -1, -1, 8, 9, -1,
+ 31, -1, -1, -1, 35, 36, 37, 38, 39, -1,
+ -1, 42, 43, -1, 45, -1, -1, -1, 49, 50,
+ 31, -1, 53, -1, 35, 36, 37, 38, 39, 8,
+ 9, 42, 43, -1, 45, -1, -1, -1, -1, 50,
+ -1, -1, 53, -1, 8, 9, -1, -1, -1, -1,
+ 8, 9, 31, -1, -1, -1, 35, 36, 37, 38,
+ 39, -1, -1, 42, -1, -1, 45, 31, 8, 9,
+ -1, 50, -1, 31, 53, 8, 9, -1, 36, 43,
+ 44, 45, -1, -1, 42, 49, 50, 45, 52, 53,
+ -1, 31, 50, 8, 9, 53, -1, -1, 31, 8,
+ 9, -1, -1, 43, 44, 45, -1, 40, 8, 9,
+ 50, -1, 45, 53, 8, 9, 31, 50, -1, -1,
+ 53, -1, 31, -1, -1, 40, 8, 9, -1, -1,
+ 45, 31, -1, -1, -1, 50, 45, 31, 53, -1,
+ -1, 50, -1, 52, 53, 45, 40, -1, -1, 31,
+ 50, 45, 52, 53, -1, -1, 50, -1, -1, 53,
+ -1, -1, -1, 45, -1, -1, -1, -1, 50, -1,
+ -1, 53, 5, 6, 7, 8, 9, 10, 11, 12,
+ 6, 7, 8, 9, 10, 11, 12
};
/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
symbol of state STATE-NUM. */
static const yytype_uint8 yystos[] =
{
- 0, 53, 0, 54, 1, 13, 14, 15, 16, 17,
+ 0, 55, 0, 56, 1, 13, 14, 15, 16, 17,
18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
- 28, 41, 42, 43, 45, 55, 58, 45, 47, 59,
- 8, 9, 29, 33, 34, 35, 36, 37, 40, 41,
- 43, 47, 48, 51, 63, 75, 79, 83, 84, 85,
- 86, 89, 50, 62, 75, 77, 80, 60, 77, 61,
- 75, 10, 41, 42, 47, 67, 76, 78, 79, 80,
- 84, 85, 89, 65, 86, 47, 59, 63, 68, 41,
- 47, 48, 64, 78, 80, 89, 66, 83, 69, 77,
- 70, 77, 71, 77, 72, 83, 73, 79, 74, 80,
- 44, 46, 44, 46, 45, 89, 89, 6, 8, 9,
- 87, 75, 34, 40, 89, 90, 89, 48, 48, 9,
- 38, 39, 48, 86, 89, 47, 47, 47, 79, 84,
- 86, 87, 87, 10, 76, 48, 11, 87, 78, 47,
- 48, 47, 47, 47, 47, 47, 47, 47, 57, 90,
- 56, 90, 7, 89, 89, 48, 10, 49, 49, 3,
- 4, 5, 6, 7, 8, 9, 10, 11, 12, 49,
- 34, 34, 35, 40, 38, 9, 38, 75, 77, 31,
- 89, 78, 50, 81, 89, 75, 75, 75, 80, 89,
- 75, 75, 55, 55, 87, 30, 32, 40, 88, 89,
- 48, 90, 90, 90, 6, 7, 90, 90, 90, 90,
- 90, 10, 10, 49, 49, 49, 38, 49, 49, 47,
- 9, 29, 82, 47, 44, 44, 47, 47, 47, 48,
- 49, 49, 34, 90, 90, 89, 89, 48, 49, 80,
- 29, 9, 81, 34, 35, 80, 89, 79, 32, 10,
- 49, 49, 34, 35, 9, 29, 49, 89, 10, 10,
- 29, 49, 89, 89, 49, 49
+ 28, 29, 30, 43, 44, 45, 47, 57, 60, 47,
+ 49, 61, 8, 9, 31, 35, 36, 37, 38, 39,
+ 42, 43, 45, 49, 50, 53, 65, 79, 83, 87,
+ 88, 89, 90, 93, 52, 64, 79, 81, 84, 62,
+ 81, 63, 79, 10, 43, 44, 49, 69, 80, 82,
+ 83, 84, 88, 89, 93, 67, 90, 49, 61, 65,
+ 70, 43, 49, 50, 66, 82, 84, 93, 68, 87,
+ 71, 81, 72, 81, 73, 81, 74, 87, 75, 83,
+ 76, 84, 77, 81, 78, 81, 46, 48, 46, 48,
+ 47, 93, 93, 6, 8, 9, 91, 79, 36, 42,
+ 93, 94, 93, 50, 50, 9, 40, 41, 50, 90,
+ 93, 49, 49, 49, 83, 88, 90, 91, 91, 10,
+ 80, 50, 11, 91, 82, 49, 50, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 59, 94, 58, 94,
+ 7, 93, 93, 50, 10, 51, 51, 3, 4, 5,
+ 6, 7, 8, 9, 10, 11, 12, 51, 36, 36,
+ 37, 42, 40, 9, 40, 79, 81, 33, 93, 82,
+ 52, 85, 93, 79, 79, 79, 84, 93, 79, 79,
+ 81, 81, 57, 57, 91, 32, 34, 42, 92, 93,
+ 50, 94, 94, 94, 6, 7, 94, 94, 94, 94,
+ 94, 10, 10, 51, 51, 51, 40, 51, 51, 49,
+ 9, 31, 86, 49, 46, 46, 49, 49, 49, 50,
+ 51, 51, 36, 94, 94, 93, 93, 50, 51, 84,
+ 31, 9, 85, 36, 37, 84, 93, 83, 34, 10,
+ 51, 51, 36, 37, 9, 31, 51, 93, 10, 10,
+ 31, 51, 93, 93, 51, 51
};
#define yyerrok (yyerrstatus = 0)
@@ -873,9 +910,18 @@ static const yytype_uint8 yystos[] =
/* Like YYERROR except do call yyerror. This remains here temporarily
to ease the transition to the new meaning of YYERROR, for GCC.
- Once GCC version 2 has supplanted version 1, this can go. */
+ Once GCC version 2 has supplanted version 1, this can go. However,
+ YYFAIL appears to be in use. Nevertheless, it is formally deprecated
+ in Bison 2.4.2's NEWS entry, where a plan to phase it out is
+ discussed. */
#define YYFAIL goto yyerrlab
+#if defined YYFAIL
+ /* This is here to suppress warnings from the GCC cpp's
+ -Wunused-macros. Normally we don't worry about that warning, but
+ some users do, and we want to make it easy for users to remove
+ YYFAIL uses, which will produce warnings from Bison 2.5. */
+#endif
#define YYRECOVERING() (!!yyerrstatus)
@@ -885,7 +931,6 @@ do \
{ \
yychar = (Token); \
yylval = (Value); \
- yytoken = YYTRANSLATE (yychar); \
YYPOPSTACK (1); \
goto yybackup; \
} \
@@ -927,19 +972,10 @@ while (YYID (0))
#endif
-/* YY_LOCATION_PRINT -- Print the location on the stream.
- This macro was not mandated originally: define only if we know
- we won't break user code: when these are the locations we know. */
+/* This macro is provided for backward compatibility. */
#ifndef YY_LOCATION_PRINT
-# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL
-# define YY_LOCATION_PRINT(File, Loc) \
- fprintf (File, "%d.%d-%d.%d", \
- (Loc).first_line, (Loc).first_column, \
- (Loc).last_line, (Loc).last_column)
-# else
-# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
-# endif
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
#endif
@@ -1043,17 +1079,20 @@ yy_symbol_print (yyoutput, yytype, yyvaluep)
#if (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
static void
-yy_stack_print (yytype_int16 *bottom, yytype_int16 *top)
+yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop)
#else
static void
-yy_stack_print (bottom, top)
- yytype_int16 *bottom;
- yytype_int16 *top;
+yy_stack_print (yybottom, yytop)
+ yytype_int16 *yybottom;
+ yytype_int16 *yytop;
#endif
{
YYFPRINTF (stderr, "Stack now");
- for (; bottom <= top; ++bottom)
- YYFPRINTF (stderr, " %d", *bottom);
+ for (; yybottom <= yytop; yybottom++)
+ {
+ int yybot = *yybottom;
+ YYFPRINTF (stderr, " %d", yybot);
+ }
YYFPRINTF (stderr, "\n");
}
@@ -1087,11 +1126,11 @@ yy_reduce_print (yyvsp, yyrule)
/* The symbols being reduced. */
for (yyi = 0; yyi < yynrhs; yyi++)
{
- fprintf (stderr, " $%d = ", yyi + 1);
+ YYFPRINTF (stderr, " $%d = ", yyi + 1);
yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
&(yyvsp[(yyi + 1) - (yynrhs)])
);
- fprintf (stderr, "\n");
+ YYFPRINTF (stderr, "\n");
}
}
@@ -1128,7 +1167,6 @@ int yydebug;
# define YYMAXDEPTH 10000
#endif
-
#if YYERROR_VERBOSE
@@ -1231,115 +1269,142 @@ yytnamerr (char *yyres, const char *yystr)
}
# endif
-/* Copy into YYRESULT an error message about the unexpected token
- YYCHAR while in state YYSTATE. Return the number of bytes copied,
- including the terminating null byte. If YYRESULT is null, do not
- copy anything; just return the number of bytes that would be
- copied. As a special case, return 0 if an ordinary "syntax error"
- message will do. Return YYSIZE_MAXIMUM if overflow occurs during
- size calculation. */
-static YYSIZE_T
-yysyntax_error (char *yyresult, int yystate, int yychar)
-{
- int yyn = yypact[yystate];
+/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message
+ about the unexpected token YYTOKEN for the state stack whose top is
+ YYSSP.
- if (! (YYPACT_NINF < yyn && yyn <= YYLAST))
- return 0;
- else
- {
- int yytype = YYTRANSLATE (yychar);
- YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]);
- YYSIZE_T yysize = yysize0;
- YYSIZE_T yysize1;
- int yysize_overflow = 0;
- enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
- char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
- int yyx;
-
-# if 0
- /* This is so xgettext sees the translatable formats that are
- constructed on the fly. */
- YY_("syntax error, unexpected %s");
- YY_("syntax error, unexpected %s, expecting %s");
- YY_("syntax error, unexpected %s, expecting %s or %s");
- YY_("syntax error, unexpected %s, expecting %s or %s or %s");
- YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s");
-# endif
- char *yyfmt;
- char const *yyf;
- static char const yyunexpected[] = "syntax error, unexpected %s";
- static char const yyexpecting[] = ", expecting %s";
- static char const yyor[] = " or %s";
- char yyformat[sizeof yyunexpected
- + sizeof yyexpecting - 1
- + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2)
- * (sizeof yyor - 1))];
- char const *yyprefix = yyexpecting;
-
- /* Start YYX at -YYN if negative to avoid negative indexes in
- YYCHECK. */
- int yyxbegin = yyn < 0 ? -yyn : 0;
-
- /* Stay within bounds of both yycheck and yytname. */
- int yychecklim = YYLAST - yyn + 1;
- int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
- int yycount = 1;
-
- yyarg[0] = yytname[yytype];
- yyfmt = yystpcpy (yyformat, yyunexpected);
-
- for (yyx = yyxbegin; yyx < yyxend; ++yyx)
- if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
- {
- if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
- {
- yycount = 1;
- yysize = yysize0;
- yyformat[sizeof yyunexpected - 1] = '\0';
- break;
- }
- yyarg[yycount++] = yytname[yyx];
- yysize1 = yysize + yytnamerr (0, yytname[yyx]);
- yysize_overflow |= (yysize1 < yysize);
- yysize = yysize1;
- yyfmt = yystpcpy (yyfmt, yyprefix);
- yyprefix = yyor;
- }
+ Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is
+ not large enough to hold the message. In that case, also set
+ *YYMSG_ALLOC to the required number of bytes. Return 2 if the
+ required number of bytes is too large to store. */
+static int
+yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg,
+ yytype_int16 *yyssp, int yytoken)
+{
+ YYSIZE_T yysize0 = yytnamerr (0, yytname[yytoken]);
+ YYSIZE_T yysize = yysize0;
+ YYSIZE_T yysize1;
+ enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+ /* Internationalized format string. */
+ const char *yyformat = 0;
+ /* Arguments of yyformat. */
+ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+ /* Number of reported tokens (one for the "unexpected", one per
+ "expected"). */
+ int yycount = 0;
+
+ /* There are many possibilities here to consider:
+ - Assume YYFAIL is not used. It's too flawed to consider. See
+ <http://lists.gnu.org/archive/html/bison-patches/2009-12/msg00024.html>
+ for details. YYERROR is fine as it does not invoke this
+ function.
+ - If this state is a consistent state with a default action, then
+ the only way this function was invoked is if the default action
+ is an error action. In that case, don't check for expected
+ tokens because there are none.
+ - The only way there can be no lookahead present (in yychar) is if
+ this state is a consistent state with a default action. Thus,
+ detecting the absence of a lookahead is sufficient to determine
+ that there is no unexpected or expected token to report. In that
+ case, just report a simple "syntax error".
+ - Don't assume there isn't a lookahead just because this state is a
+ consistent state with a default action. There might have been a
+ previous inconsistent state, consistent state with a non-default
+ action, or user semantic action that manipulated yychar.
+ - Of course, the expected token list depends on states to have
+ correct lookahead information, and it depends on the parser not
+ to perform extra reductions after fetching a lookahead from the
+ scanner and before detecting a syntax error. Thus, state merging
+ (from LALR or IELR) and default reductions corrupt the expected
+ token list. However, the list is correct for canonical LR with
+ one exception: it will still contain any token that will not be
+ accepted due to an error action in a later state.
+ */
+ if (yytoken != YYEMPTY)
+ {
+ int yyn = yypact[*yyssp];
+ yyarg[yycount++] = yytname[yytoken];
+ if (!yypact_value_is_default (yyn))
+ {
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. In other words, skip the first -YYN actions for
+ this state because they are default actions. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn + 1;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yyx;
+
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR
+ && !yytable_value_is_error (yytable[yyx + yyn]))
+ {
+ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+ {
+ yycount = 1;
+ yysize = yysize0;
+ break;
+ }
+ yyarg[yycount++] = yytname[yyx];
+ yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+ if (! (yysize <= yysize1
+ && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+ return 2;
+ yysize = yysize1;
+ }
+ }
+ }
- yyf = YY_(yyformat);
- yysize1 = yysize + yystrlen (yyf);
- yysize_overflow |= (yysize1 < yysize);
- yysize = yysize1;
+ switch (yycount)
+ {
+# define YYCASE_(N, S) \
+ case N: \
+ yyformat = S; \
+ break
+ YYCASE_(0, YY_("syntax error"));
+ YYCASE_(1, YY_("syntax error, unexpected %s"));
+ YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s"));
+ YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s"));
+ YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s"));
+ YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"));
+# undef YYCASE_
+ }
- if (yysize_overflow)
- return YYSIZE_MAXIMUM;
+ yysize1 = yysize + yystrlen (yyformat);
+ if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+ return 2;
+ yysize = yysize1;
- if (yyresult)
- {
- /* Avoid sprintf, as that infringes on the user's name space.
- Don't have undefined behavior even if the translation
- produced a string with the wrong number of "%s"s. */
- char *yyp = yyresult;
- int yyi = 0;
- while ((*yyp = *yyf) != '\0')
- {
- if (*yyp == '%' && yyf[1] == 's' && yyi < yycount)
- {
- yyp += yytnamerr (yyp, yyarg[yyi++]);
- yyf += 2;
- }
- else
- {
- yyp++;
- yyf++;
- }
- }
- }
- return yysize;
+ if (*yymsg_alloc < yysize)
+ {
+ *yymsg_alloc = 2 * yysize;
+ if (! (yysize <= *yymsg_alloc
+ && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM))
+ *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM;
+ return 1;
}
+
+ /* Avoid sprintf, as that infringes on the user's name space.
+ Don't have undefined behavior even if the translation
+ produced a string with the wrong number of "%s"s. */
+ {
+ char *yyp = *yymsg;
+ int yyi = 0;
+ while ((*yyp = *yyformat) != '\0')
+ if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount)
+ {
+ yyp += yytnamerr (yyp, yyarg[yyi++]);
+ yyformat += 2;
+ }
+ else
+ {
+ yyp++;
+ yyformat++;
+ }
+ }
+ return 0;
}
#endif /* YYERROR_VERBOSE */
-
/*-----------------------------------------------.
| Release the memory associated to this symbol. |
@@ -1371,10 +1436,9 @@ yydestruct (yymsg, yytype, yyvaluep)
break;
}
}
-
-/* Prevent warnings from -Wmissing-prototypes. */
+/* Prevent warnings from -Wmissing-prototypes. */
#ifdef YYPARSE_PARAM
#if defined __STDC__ || defined __cplusplus
int yyparse (void *YYPARSE_PARAM);
@@ -1390,18 +1454,16 @@ int yyparse ();
#endif /* ! YYPARSE_PARAM */
-
-/* The look-ahead symbol. */
+/* The lookahead symbol. */
int yychar;
-/* The semantic value of the look-ahead symbol. */
+/* The semantic value of the lookahead symbol. */
YYSTYPE yylval;
/* Number of syntax errors so far. */
int yynerrs;
-
/*----------.
| yyparse. |
`----------*/
@@ -1428,66 +1490,66 @@ yyparse ()
#endif
#endif
{
-
- int yystate;
- int yyn;
- int yyresult;
- /* Number of tokens to shift before error messages enabled. */
- int yyerrstatus;
- /* Look-ahead token as an internal (translated) token number. */
- int yytoken = 0;
-#if YYERROR_VERBOSE
- /* Buffer for error messages, and its allocated size. */
- char yymsgbuf[128];
- char *yymsg = yymsgbuf;
- YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
-#endif
-
- /* Three stacks and their tools:
- `yyss': related to states,
- `yyvs': related to semantic values,
- `yyls': related to locations.
-
- Refer to the stacks thru separate pointers, to allow yyoverflow
- to reallocate them elsewhere. */
-
- /* The state stack. */
- yytype_int16 yyssa[YYINITDEPTH];
- yytype_int16 *yyss = yyssa;
- yytype_int16 *yyssp;
+ int yystate;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
- /* The semantic value stack. */
- YYSTYPE yyvsa[YYINITDEPTH];
- YYSTYPE *yyvs = yyvsa;
- YYSTYPE *yyvsp;
+ /* The stacks and their tools:
+ `yyss': related to states.
+ `yyvs': related to semantic values.
+ Refer to the stacks thru separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+ /* The state stack. */
+ yytype_int16 yyssa[YYINITDEPTH];
+ yytype_int16 *yyss;
+ yytype_int16 *yyssp;
-#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N))
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs;
+ YYSTYPE *yyvsp;
- YYSIZE_T yystacksize = YYINITDEPTH;
+ YYSIZE_T yystacksize;
+ int yyn;
+ int yyresult;
+ /* Lookahead token as an internal (translated) token number. */
+ int yytoken;
/* The variables used to return semantic value and location from the
action routines. */
YYSTYPE yyval;
+#if YYERROR_VERBOSE
+ /* Buffer for error messages, and its allocated size. */
+ char yymsgbuf[128];
+ char *yymsg = yymsgbuf;
+ YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N))
/* The number of symbols on the RHS of the reduced rule.
Keep to zero when no symbol should be popped. */
int yylen = 0;
+ yytoken = 0;
+ yyss = yyssa;
+ yyvs = yyvsa;
+ yystacksize = YYINITDEPTH;
+
YYDPRINTF ((stderr, "Starting parse\n"));
yystate = 0;
yyerrstatus = 0;
yynerrs = 0;
- yychar = YYEMPTY; /* Cause a token to be read. */
+ yychar = YYEMPTY; /* Cause a token to be read. */
/* Initialize stack pointers.
Waste one element of value and location stack
so that they stay on the same level as the state stack.
The wasted elements are never initialized. */
-
yyssp = yyss;
yyvsp = yyvs;
@@ -1517,7 +1579,6 @@ yyparse ()
YYSTYPE *yyvs1 = yyvs;
yytype_int16 *yyss1 = yyss;
-
/* Each stack pointer address is followed by the size of the
data in use in that stack, in bytes. This used to be a
conditional around just the two extra args, but that might
@@ -1525,7 +1586,6 @@ yyparse ()
yyoverflow (YY_("memory exhausted"),
&yyss1, yysize * sizeof (*yyssp),
&yyvs1, yysize * sizeof (*yyvsp),
-
&yystacksize);
yyss = yyss1;
@@ -1548,9 +1608,8 @@ yyparse ()
(union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
if (! yyptr)
goto yyexhaustedlab;
- YYSTACK_RELOCATE (yyss);
- YYSTACK_RELOCATE (yyvs);
-
+ YYSTACK_RELOCATE (yyss_alloc, yyss);
+ YYSTACK_RELOCATE (yyvs_alloc, yyvs);
# undef YYSTACK_RELOCATE
if (yyss1 != yyssa)
YYSTACK_FREE (yyss1);
@@ -1561,7 +1620,6 @@ yyparse ()
yyssp = yyss + yysize - 1;
yyvsp = yyvs + yysize - 1;
-
YYDPRINTF ((stderr, "Stack size increased to %lu\n",
(unsigned long int) yystacksize));
@@ -1571,6 +1629,9 @@ yyparse ()
YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+ if (yystate == YYFINAL)
+ YYACCEPT;
+
goto yybackup;
/*-----------.
@@ -1579,16 +1640,16 @@ yyparse ()
yybackup:
/* Do appropriate processing given the current state. Read a
- look-ahead token if we need one and don't already have one. */
+ lookahead token if we need one and don't already have one. */
- /* First try to decide what to do without reference to look-ahead token. */
+ /* First try to decide what to do without reference to lookahead token. */
yyn = yypact[yystate];
- if (yyn == YYPACT_NINF)
+ if (yypact_value_is_default (yyn))
goto yydefault;
- /* Not known => get a look-ahead token if don't already have one. */
+ /* Not known => get a lookahead token if don't already have one. */
- /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */
if (yychar == YYEMPTY)
{
YYDPRINTF ((stderr, "Reading a token: "));
@@ -1614,26 +1675,22 @@ yybackup:
yyn = yytable[yyn];
if (yyn <= 0)
{
- if (yyn == 0 || yyn == YYTABLE_NINF)
- goto yyerrlab;
+ if (yytable_value_is_error (yyn))
+ goto yyerrlab;
yyn = -yyn;
goto yyreduce;
}
- if (yyn == YYFINAL)
- YYACCEPT;
-
/* Count tokens shifted since error; after three, turn off error
status. */
if (yyerrstatus)
yyerrstatus--;
- /* Shift the look-ahead token. */
+ /* Shift the lookahead token. */
YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
- /* Discard the shifted token unless it is eof. */
- if (yychar != YYEOF)
- yychar = YYEMPTY;
+ /* Discard the shifted token. */
+ yychar = YYEMPTY;
yystate = yyn;
*++yyvsp = yylval;
@@ -1673,14 +1730,18 @@ yyreduce:
switch (yyn)
{
case 3:
-#line 70 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 71 "a.y"
{
stmtline = lineno;
}
break;
case 5:
-#line 77 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 78 "a.y"
{
if((yyvsp[(1) - (2)].sym)->value != pc)
yyerror("redeclaration of %s", (yyvsp[(1) - (2)].sym)->name);
@@ -1689,7 +1750,9 @@ yyreduce:
break;
case 7:
-#line 84 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 85 "a.y"
{
(yyvsp[(1) - (2)].sym)->type = LLAB;
(yyvsp[(1) - (2)].sym)->value = pc;
@@ -1697,7 +1760,9 @@ yyreduce:
break;
case 12:
-#line 95 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 96 "a.y"
{
(yyvsp[(1) - (3)].sym)->type = LVAR;
(yyvsp[(1) - (3)].sym)->value = (yyvsp[(3) - (3)].lval);
@@ -1705,7 +1770,9 @@ yyreduce:
break;
case 13:
-#line 100 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 101 "a.y"
{
if((yyvsp[(1) - (3)].sym)->value != (yyvsp[(3) - (3)].lval))
yyerror("redeclaration of %s", (yyvsp[(1) - (3)].sym)->name);
@@ -1714,175 +1781,245 @@ yyreduce:
break;
case 14:
-#line 105 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 106 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 15:
-#line 106 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 107 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 16:
-#line 107 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 108 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 17:
-#line 108 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 109 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 18:
-#line 109 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 110 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 19:
-#line 110 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 111 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 20:
-#line 111 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 112 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 21:
-#line 112 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 113 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 22:
-#line 113 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 114 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 23:
-#line 114 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 115 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 24:
-#line 115 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 116 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 25:
-#line 116 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 117 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 26:
-#line 117 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 118 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 27:
-#line 118 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 119 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 28:
-#line 119 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 120 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 29:
-#line 120 "a.y"
+
+/* Line 1806 of yacc.c */
+#line 121 "a.y"
{ outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
break;
case 30:
+
+/* Line 1806 of yacc.c */
+#line 122 "a.y"
+ { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
+ break;
+
+ case 31:
+
+/* Line 1806 of yacc.c */
#line 123 "a.y"
+ { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
+ break;
+
+ case 32:
+
+/* Line 1806 of yacc.c */
+#line 126 "a.y"
{
(yyval.gen2).from = nullgen;
(yyval.gen2).to = nullgen;
}
break;
- case 31:
-#line 128 "a.y"
+ case 33:
+
+/* Line 1806 of yacc.c */
+#line 131 "a.y"
{
(yyval.gen2).from = nullgen;
(yyval.gen2).to = nullgen;
}
break;
- case 32:
-#line 135 "a.y"
+ case 34:
+
+/* Line 1806 of yacc.c */
+#line 138 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (3)].gen);
(yyval.gen2).to = (yyvsp[(3) - (3)].gen);
}
break;
- case 33:
-#line 142 "a.y"
+ case 35:
+
+/* Line 1806 of yacc.c */
+#line 145 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (3)].gen);
(yyval.gen2).to = (yyvsp[(3) - (3)].gen);
}
break;
- case 34:
-#line 149 "a.y"
+ case 36:
+
+/* Line 1806 of yacc.c */
+#line 152 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (2)].gen);
(yyval.gen2).to = nullgen;
}
break;
- case 35:
-#line 154 "a.y"
+ case 37:
+
+/* Line 1806 of yacc.c */
+#line 157 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (1)].gen);
(yyval.gen2).to = nullgen;
}
break;
- case 36:
-#line 161 "a.y"
+ case 38:
+
+/* Line 1806 of yacc.c */
+#line 164 "a.y"
{
(yyval.gen2).from = nullgen;
(yyval.gen2).to = (yyvsp[(2) - (2)].gen);
}
break;
- case 37:
-#line 166 "a.y"
+ case 39:
+
+/* Line 1806 of yacc.c */
+#line 169 "a.y"
{
(yyval.gen2).from = nullgen;
(yyval.gen2).to = (yyvsp[(1) - (1)].gen);
}
break;
- case 38:
-#line 173 "a.y"
+ case 40:
+
+/* Line 1806 of yacc.c */
+#line 176 "a.y"
{
(yyval.gen2).from = nullgen;
(yyval.gen2).to = (yyvsp[(2) - (2)].gen);
}
break;
- case 39:
-#line 178 "a.y"
+ case 41:
+
+/* Line 1806 of yacc.c */
+#line 181 "a.y"
{
(yyval.gen2).from = nullgen;
(yyval.gen2).to = (yyvsp[(1) - (1)].gen);
}
break;
- case 40:
-#line 183 "a.y"
+ case 42:
+
+/* Line 1806 of yacc.c */
+#line 186 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (3)].gen);
(yyval.gen2).to = (yyvsp[(3) - (3)].gen);
}
break;
- case 41:
-#line 190 "a.y"
+ case 43:
+
+/* Line 1806 of yacc.c */
+#line 193 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (5)].gen);
(yyval.gen2).from.scale = (yyvsp[(3) - (5)].lval);
@@ -1890,16 +2027,20 @@ yyreduce:
}
break;
- case 42:
-#line 198 "a.y"
+ case 44:
+
+/* Line 1806 of yacc.c */
+#line 201 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (3)].gen);
(yyval.gen2).to = (yyvsp[(3) - (3)].gen);
}
break;
- case 43:
-#line 203 "a.y"
+ case 45:
+
+/* Line 1806 of yacc.c */
+#line 206 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (5)].gen);
(yyval.gen2).from.scale = (yyvsp[(3) - (5)].lval);
@@ -1907,24 +2048,30 @@ yyreduce:
}
break;
- case 44:
-#line 211 "a.y"
+ case 46:
+
+/* Line 1806 of yacc.c */
+#line 214 "a.y"
{
(yyval.gen2).from = nullgen;
(yyval.gen2).to = (yyvsp[(2) - (2)].gen);
}
break;
- case 45:
-#line 216 "a.y"
+ case 47:
+
+/* Line 1806 of yacc.c */
+#line 219 "a.y"
{
(yyval.gen2).from = nullgen;
(yyval.gen2).to = (yyvsp[(1) - (1)].gen);
}
break;
- case 46:
-#line 221 "a.y"
+ case 48:
+
+/* Line 1806 of yacc.c */
+#line 224 "a.y"
{
(yyval.gen2).from = nullgen;
(yyval.gen2).to = (yyvsp[(2) - (2)].gen);
@@ -1933,16 +2080,20 @@ yyreduce:
}
break;
- case 49:
-#line 234 "a.y"
+ case 51:
+
+/* Line 1806 of yacc.c */
+#line 237 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (3)].gen);
(yyval.gen2).to = (yyvsp[(3) - (3)].gen);
}
break;
- case 50:
-#line 239 "a.y"
+ case 52:
+
+/* Line 1806 of yacc.c */
+#line 242 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (5)].gen);
(yyval.gen2).to = (yyvsp[(3) - (5)].gen);
@@ -1952,16 +2103,20 @@ yyreduce:
}
break;
- case 51:
-#line 249 "a.y"
+ case 53:
+
+/* Line 1806 of yacc.c */
+#line 252 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (3)].gen);
(yyval.gen2).to = (yyvsp[(3) - (3)].gen);
}
break;
- case 52:
-#line 254 "a.y"
+ case 54:
+
+/* Line 1806 of yacc.c */
+#line 257 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (5)].gen);
(yyval.gen2).to = (yyvsp[(3) - (5)].gen);
@@ -1971,40 +2126,50 @@ yyreduce:
}
break;
- case 53:
-#line 264 "a.y"
+ case 55:
+
+/* Line 1806 of yacc.c */
+#line 267 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (2)].gen);
(yyval.gen2).to = nullgen;
}
break;
- case 54:
-#line 269 "a.y"
+ case 56:
+
+/* Line 1806 of yacc.c */
+#line 272 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (1)].gen);
(yyval.gen2).to = nullgen;
}
break;
- case 55:
-#line 274 "a.y"
+ case 57:
+
+/* Line 1806 of yacc.c */
+#line 277 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (3)].gen);
(yyval.gen2).to = (yyvsp[(3) - (3)].gen);
}
break;
- case 56:
-#line 281 "a.y"
+ case 58:
+
+/* Line 1806 of yacc.c */
+#line 284 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (3)].gen);
(yyval.gen2).to = (yyvsp[(3) - (3)].gen);
}
break;
- case 57:
-#line 286 "a.y"
+ case 59:
+
+/* Line 1806 of yacc.c */
+#line 289 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (5)].gen);
(yyval.gen2).from.scale = (yyvsp[(3) - (5)].lval);
@@ -2012,8 +2177,10 @@ yyreduce:
}
break;
- case 58:
-#line 294 "a.y"
+ case 60:
+
+/* Line 1806 of yacc.c */
+#line 297 "a.y"
{
(yyval.gen2).from = (yyvsp[(1) - (5)].gen);
(yyval.gen2).to = (yyvsp[(3) - (5)].gen);
@@ -2021,8 +2188,10 @@ yyreduce:
}
break;
- case 59:
-#line 302 "a.y"
+ case 61:
+
+/* Line 1806 of yacc.c */
+#line 305 "a.y"
{
(yyval.gen2).from = (yyvsp[(3) - (5)].gen);
(yyval.gen2).to = (yyvsp[(5) - (5)].gen);
@@ -2032,22 +2201,54 @@ yyreduce:
}
break;
- case 64:
-#line 318 "a.y"
+ case 62:
+
+/* Line 1806 of yacc.c */
+#line 315 "a.y"
+ {
+ if((yyvsp[(1) - (3)].gen).type != D_CONST || (yyvsp[(3) - (3)].gen).type != D_CONST)
+ yyerror("arguments to PCDATA must be integer constants");
+ (yyval.gen2).from = (yyvsp[(1) - (3)].gen);
+ (yyval.gen2).to = (yyvsp[(3) - (3)].gen);
+ }
+ break;
+
+ case 63:
+
+/* Line 1806 of yacc.c */
+#line 324 "a.y"
+ {
+ if((yyvsp[(1) - (3)].gen).type != D_CONST)
+ yyerror("index for FUNCDATA must be integer constant");
+ if((yyvsp[(3) - (3)].gen).type != D_EXTERN && (yyvsp[(3) - (3)].gen).type != D_STATIC)
+ yyerror("value for FUNCDATA must be symbol reference");
+ (yyval.gen2).from = (yyvsp[(1) - (3)].gen);
+ (yyval.gen2).to = (yyvsp[(3) - (3)].gen);
+ }
+ break;
+
+ case 68:
+
+/* Line 1806 of yacc.c */
+#line 341 "a.y"
{
(yyval.gen) = (yyvsp[(2) - (2)].gen);
}
break;
- case 65:
-#line 322 "a.y"
+ case 69:
+
+/* Line 1806 of yacc.c */
+#line 345 "a.y"
{
(yyval.gen) = (yyvsp[(2) - (2)].gen);
}
break;
- case 71:
-#line 335 "a.y"
+ case 75:
+
+/* Line 1806 of yacc.c */
+#line 358 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_BRANCH;
@@ -2055,8 +2256,10 @@ yyreduce:
}
break;
- case 72:
-#line 341 "a.y"
+ case 76:
+
+/* Line 1806 of yacc.c */
+#line 364 "a.y"
{
(yyval.gen) = nullgen;
if(pass == 2)
@@ -2067,8 +2270,10 @@ yyreduce:
}
break;
- case 73:
-#line 350 "a.y"
+ case 77:
+
+/* Line 1806 of yacc.c */
+#line 373 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_BRANCH;
@@ -2077,56 +2282,70 @@ yyreduce:
}
break;
- case 74:
-#line 359 "a.y"
+ case 78:
+
+/* Line 1806 of yacc.c */
+#line 382 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = (yyvsp[(1) - (1)].lval);
}
break;
- case 75:
-#line 364 "a.y"
+ case 79:
+
+/* Line 1806 of yacc.c */
+#line 387 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = (yyvsp[(1) - (1)].lval);
}
break;
- case 76:
-#line 369 "a.y"
+ case 80:
+
+/* Line 1806 of yacc.c */
+#line 392 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = (yyvsp[(1) - (1)].lval);
}
break;
- case 77:
-#line 374 "a.y"
+ case 81:
+
+/* Line 1806 of yacc.c */
+#line 397 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = (yyvsp[(1) - (1)].lval);
}
break;
- case 78:
-#line 379 "a.y"
+ case 82:
+
+/* Line 1806 of yacc.c */
+#line 402 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_SP;
}
break;
- case 79:
-#line 384 "a.y"
+ case 83:
+
+/* Line 1806 of yacc.c */
+#line 407 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = (yyvsp[(1) - (1)].lval);
}
break;
- case 80:
-#line 391 "a.y"
+ case 84:
+
+/* Line 1806 of yacc.c */
+#line 414 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_CONST;
@@ -2134,8 +2353,10 @@ yyreduce:
}
break;
- case 81:
-#line 397 "a.y"
+ case 85:
+
+/* Line 1806 of yacc.c */
+#line 420 "a.y"
{
(yyval.gen) = (yyvsp[(2) - (2)].gen);
(yyval.gen).index = (yyvsp[(2) - (2)].gen).type;
@@ -2148,8 +2369,10 @@ yyreduce:
}
break;
- case 82:
-#line 408 "a.y"
+ case 86:
+
+/* Line 1806 of yacc.c */
+#line 431 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_SCONST;
@@ -2157,8 +2380,10 @@ yyreduce:
}
break;
- case 83:
-#line 414 "a.y"
+ case 87:
+
+/* Line 1806 of yacc.c */
+#line 437 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_FCONST;
@@ -2166,8 +2391,10 @@ yyreduce:
}
break;
- case 84:
-#line 420 "a.y"
+ case 88:
+
+/* Line 1806 of yacc.c */
+#line 443 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_FCONST;
@@ -2175,8 +2402,10 @@ yyreduce:
}
break;
- case 85:
-#line 426 "a.y"
+ case 89:
+
+/* Line 1806 of yacc.c */
+#line 449 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_FCONST;
@@ -2184,8 +2413,10 @@ yyreduce:
}
break;
- case 86:
-#line 432 "a.y"
+ case 90:
+
+/* Line 1806 of yacc.c */
+#line 455 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_FCONST;
@@ -2193,8 +2424,10 @@ yyreduce:
}
break;
- case 87:
-#line 440 "a.y"
+ case 91:
+
+/* Line 1806 of yacc.c */
+#line 463 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_CONST2;
@@ -2203,40 +2436,50 @@ yyreduce:
}
break;
- case 88:
-#line 449 "a.y"
+ case 92:
+
+/* Line 1806 of yacc.c */
+#line 472 "a.y"
{
(yyval.con2).v1 = (yyvsp[(1) - (1)].lval);
- (yyval.con2).v2 = 0;
+ (yyval.con2).v2 = ArgsSizeUnknown;
}
break;
- case 89:
-#line 454 "a.y"
+ case 93:
+
+/* Line 1806 of yacc.c */
+#line 477 "a.y"
{
(yyval.con2).v1 = -(yyvsp[(2) - (2)].lval);
- (yyval.con2).v2 = 0;
+ (yyval.con2).v2 = ArgsSizeUnknown;
}
break;
- case 90:
-#line 459 "a.y"
+ case 94:
+
+/* Line 1806 of yacc.c */
+#line 482 "a.y"
{
(yyval.con2).v1 = (yyvsp[(1) - (3)].lval);
(yyval.con2).v2 = (yyvsp[(3) - (3)].lval);
}
break;
- case 91:
-#line 464 "a.y"
+ case 95:
+
+/* Line 1806 of yacc.c */
+#line 487 "a.y"
{
(yyval.con2).v1 = -(yyvsp[(2) - (4)].lval);
(yyval.con2).v2 = (yyvsp[(4) - (4)].lval);
}
break;
- case 94:
-#line 475 "a.y"
+ case 98:
+
+/* Line 1806 of yacc.c */
+#line 498 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_INDIR+D_NONE;
@@ -2244,8 +2487,10 @@ yyreduce:
}
break;
- case 95:
-#line 481 "a.y"
+ case 99:
+
+/* Line 1806 of yacc.c */
+#line 504 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_INDIR+(yyvsp[(3) - (4)].lval);
@@ -2253,8 +2498,10 @@ yyreduce:
}
break;
- case 96:
-#line 487 "a.y"
+ case 100:
+
+/* Line 1806 of yacc.c */
+#line 510 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_INDIR+D_SP;
@@ -2262,8 +2509,10 @@ yyreduce:
}
break;
- case 97:
-#line 493 "a.y"
+ case 101:
+
+/* Line 1806 of yacc.c */
+#line 516 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_INDIR+D_NONE;
@@ -2274,8 +2523,10 @@ yyreduce:
}
break;
- case 98:
-#line 502 "a.y"
+ case 102:
+
+/* Line 1806 of yacc.c */
+#line 525 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_INDIR+(yyvsp[(3) - (9)].lval);
@@ -2286,8 +2537,10 @@ yyreduce:
}
break;
- case 99:
-#line 511 "a.y"
+ case 103:
+
+/* Line 1806 of yacc.c */
+#line 534 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_INDIR+(yyvsp[(3) - (9)].lval);
@@ -2298,24 +2551,30 @@ yyreduce:
}
break;
- case 100:
-#line 520 "a.y"
+ case 104:
+
+/* Line 1806 of yacc.c */
+#line 543 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_INDIR+(yyvsp[(2) - (3)].lval);
}
break;
- case 101:
-#line 525 "a.y"
+ case 105:
+
+/* Line 1806 of yacc.c */
+#line 548 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_INDIR+D_SP;
}
break;
- case 102:
-#line 530 "a.y"
+ case 106:
+
+/* Line 1806 of yacc.c */
+#line 553 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_INDIR+(yyvsp[(3) - (4)].lval);
@@ -2323,8 +2582,10 @@ yyreduce:
}
break;
- case 103:
-#line 536 "a.y"
+ case 107:
+
+/* Line 1806 of yacc.c */
+#line 559 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_INDIR+D_NONE;
@@ -2334,8 +2595,10 @@ yyreduce:
}
break;
- case 104:
-#line 544 "a.y"
+ case 108:
+
+/* Line 1806 of yacc.c */
+#line 567 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_INDIR+(yyvsp[(2) - (8)].lval);
@@ -2345,15 +2608,19 @@ yyreduce:
}
break;
- case 105:
-#line 554 "a.y"
+ case 109:
+
+/* Line 1806 of yacc.c */
+#line 577 "a.y"
{
(yyval.gen) = (yyvsp[(1) - (1)].gen);
}
break;
- case 106:
-#line 558 "a.y"
+ case 110:
+
+/* Line 1806 of yacc.c */
+#line 581 "a.y"
{
(yyval.gen) = (yyvsp[(1) - (6)].gen);
(yyval.gen).index = (yyvsp[(3) - (6)].lval);
@@ -2362,8 +2629,10 @@ yyreduce:
}
break;
- case 107:
-#line 567 "a.y"
+ case 111:
+
+/* Line 1806 of yacc.c */
+#line 590 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = (yyvsp[(4) - (5)].lval);
@@ -2372,8 +2641,10 @@ yyreduce:
}
break;
- case 108:
-#line 574 "a.y"
+ case 112:
+
+/* Line 1806 of yacc.c */
+#line 597 "a.y"
{
(yyval.gen) = nullgen;
(yyval.gen).type = D_STATIC;
@@ -2382,144 +2653,194 @@ yyreduce:
}
break;
- case 109:
-#line 582 "a.y"
+ case 113:
+
+/* Line 1806 of yacc.c */
+#line 605 "a.y"
{
(yyval.lval) = 0;
}
break;
- case 110:
-#line 586 "a.y"
+ case 114:
+
+/* Line 1806 of yacc.c */
+#line 609 "a.y"
{
(yyval.lval) = (yyvsp[(2) - (2)].lval);
}
break;
- case 111:
-#line 590 "a.y"
+ case 115:
+
+/* Line 1806 of yacc.c */
+#line 613 "a.y"
{
(yyval.lval) = -(yyvsp[(2) - (2)].lval);
}
break;
- case 113:
-#line 597 "a.y"
+ case 117:
+
+/* Line 1806 of yacc.c */
+#line 620 "a.y"
{
(yyval.lval) = D_AUTO;
}
break;
- case 116:
-#line 605 "a.y"
+ case 120:
+
+/* Line 1806 of yacc.c */
+#line 628 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (1)].sym)->value;
}
break;
- case 117:
-#line 609 "a.y"
+ case 121:
+
+/* Line 1806 of yacc.c */
+#line 632 "a.y"
{
(yyval.lval) = -(yyvsp[(2) - (2)].lval);
}
break;
- case 118:
-#line 613 "a.y"
+ case 122:
+
+/* Line 1806 of yacc.c */
+#line 636 "a.y"
{
(yyval.lval) = (yyvsp[(2) - (2)].lval);
}
break;
- case 119:
-#line 617 "a.y"
+ case 123:
+
+/* Line 1806 of yacc.c */
+#line 640 "a.y"
{
(yyval.lval) = ~(yyvsp[(2) - (2)].lval);
}
break;
- case 120:
-#line 621 "a.y"
+ case 124:
+
+/* Line 1806 of yacc.c */
+#line 644 "a.y"
{
(yyval.lval) = (yyvsp[(2) - (3)].lval);
}
break;
- case 122:
-#line 628 "a.y"
+ case 126:
+
+/* Line 1806 of yacc.c */
+#line 651 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (3)].lval) + (yyvsp[(3) - (3)].lval);
}
break;
- case 123:
-#line 632 "a.y"
+ case 127:
+
+/* Line 1806 of yacc.c */
+#line 655 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (3)].lval) - (yyvsp[(3) - (3)].lval);
}
break;
- case 124:
-#line 636 "a.y"
+ case 128:
+
+/* Line 1806 of yacc.c */
+#line 659 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (3)].lval) * (yyvsp[(3) - (3)].lval);
}
break;
- case 125:
-#line 640 "a.y"
+ case 129:
+
+/* Line 1806 of yacc.c */
+#line 663 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (3)].lval) / (yyvsp[(3) - (3)].lval);
}
break;
- case 126:
-#line 644 "a.y"
+ case 130:
+
+/* Line 1806 of yacc.c */
+#line 667 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (3)].lval) % (yyvsp[(3) - (3)].lval);
}
break;
- case 127:
-#line 648 "a.y"
+ case 131:
+
+/* Line 1806 of yacc.c */
+#line 671 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (4)].lval) << (yyvsp[(4) - (4)].lval);
}
break;
- case 128:
-#line 652 "a.y"
+ case 132:
+
+/* Line 1806 of yacc.c */
+#line 675 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (4)].lval) >> (yyvsp[(4) - (4)].lval);
}
break;
- case 129:
-#line 656 "a.y"
+ case 133:
+
+/* Line 1806 of yacc.c */
+#line 679 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (3)].lval) & (yyvsp[(3) - (3)].lval);
}
break;
- case 130:
-#line 660 "a.y"
+ case 134:
+
+/* Line 1806 of yacc.c */
+#line 683 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (3)].lval) ^ (yyvsp[(3) - (3)].lval);
}
break;
- case 131:
-#line 664 "a.y"
+ case 135:
+
+/* Line 1806 of yacc.c */
+#line 687 "a.y"
{
(yyval.lval) = (yyvsp[(1) - (3)].lval) | (yyvsp[(3) - (3)].lval);
}
break;
-/* Line 1267 of yacc.c. */
-#line 2521 "y.tab.c"
+
+/* Line 1806 of yacc.c */
+#line 2831 "y.tab.c"
default: break;
}
+ /* User semantic actions sometimes alter yychar, and that requires
+ that yytoken be updated with the new translation. We take the
+ approach of translating immediately before every use of yytoken.
+ One alternative is translating here after every semantic action,
+ but that translation would be missed if the semantic action invokes
+ YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or
+ if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an
+ incorrect destructor might then be invoked immediately. In the
+ case of YYERROR or YYBACKUP, subsequent parser actions might lead
+ to an incorrect destructor call or verbose syntax error message
+ before the lookahead is translated. */
YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
YYPOPSTACK (yylen);
@@ -2528,7 +2849,6 @@ yyreduce:
*++yyvsp = yyval;
-
/* Now `shift' the result of the reduction. Determine what state
that goes to, based on the state we popped back to and the rule
number reduced by. */
@@ -2548,6 +2868,10 @@ yyreduce:
| yyerrlab -- here on detecting error |
`------------------------------------*/
yyerrlab:
+ /* Make sure we have latest lookahead translation. See comments at
+ user semantic actions for why this is necessary. */
+ yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar);
+
/* If not already recovering from an error, report this error. */
if (!yyerrstatus)
{
@@ -2555,37 +2879,36 @@ yyerrlab:
#if ! YYERROR_VERBOSE
yyerror (YY_("syntax error"));
#else
+# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \
+ yyssp, yytoken)
{
- YYSIZE_T yysize = yysyntax_error (0, yystate, yychar);
- if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM)
- {
- YYSIZE_T yyalloc = 2 * yysize;
- if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM))
- yyalloc = YYSTACK_ALLOC_MAXIMUM;
- if (yymsg != yymsgbuf)
- YYSTACK_FREE (yymsg);
- yymsg = (char *) YYSTACK_ALLOC (yyalloc);
- if (yymsg)
- yymsg_alloc = yyalloc;
- else
- {
- yymsg = yymsgbuf;
- yymsg_alloc = sizeof yymsgbuf;
- }
- }
-
- if (0 < yysize && yysize <= yymsg_alloc)
- {
- (void) yysyntax_error (yymsg, yystate, yychar);
- yyerror (yymsg);
- }
- else
- {
- yyerror (YY_("syntax error"));
- if (yysize != 0)
- goto yyexhaustedlab;
- }
+ char const *yymsgp = YY_("syntax error");
+ int yysyntax_error_status;
+ yysyntax_error_status = YYSYNTAX_ERROR;
+ if (yysyntax_error_status == 0)
+ yymsgp = yymsg;
+ else if (yysyntax_error_status == 1)
+ {
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+ yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc);
+ if (!yymsg)
+ {
+ yymsg = yymsgbuf;
+ yymsg_alloc = sizeof yymsgbuf;
+ yysyntax_error_status = 2;
+ }
+ else
+ {
+ yysyntax_error_status = YYSYNTAX_ERROR;
+ yymsgp = yymsg;
+ }
+ }
+ yyerror (yymsgp);
+ if (yysyntax_error_status == 2)
+ goto yyexhaustedlab;
}
+# undef YYSYNTAX_ERROR
#endif
}
@@ -2593,7 +2916,7 @@ yyerrlab:
if (yyerrstatus == 3)
{
- /* If just tried and failed to reuse look-ahead token after an
+ /* If just tried and failed to reuse lookahead token after an
error, discard it. */
if (yychar <= YYEOF)
@@ -2610,7 +2933,7 @@ yyerrlab:
}
}
- /* Else will try to reuse look-ahead token after shifting the error
+ /* Else will try to reuse lookahead token after shifting the error
token. */
goto yyerrlab1;
@@ -2644,7 +2967,7 @@ yyerrlab1:
for (;;)
{
yyn = yypact[yystate];
- if (yyn != YYPACT_NINF)
+ if (!yypact_value_is_default (yyn))
{
yyn += YYTERROR;
if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
@@ -2667,9 +2990,6 @@ yyerrlab1:
YY_STACK_PRINT (yyss, yyssp);
}
- if (yyn == YYFINAL)
- YYACCEPT;
-
*++yyvsp = yylval;
@@ -2694,7 +3014,7 @@ yyabortlab:
yyresult = 1;
goto yyreturn;
-#ifndef yyoverflow
+#if !defined(yyoverflow) || YYERROR_VERBOSE
/*-------------------------------------------------.
| yyexhaustedlab -- memory exhaustion comes here. |
`-------------------------------------------------*/
@@ -2705,9 +3025,14 @@ yyexhaustedlab:
#endif
yyreturn:
- if (yychar != YYEOF && yychar != YYEMPTY)
- yydestruct ("Cleanup: discarding lookahead",
- yytoken, &yylval);
+ if (yychar != YYEMPTY)
+ {
+ /* Make sure we have latest lookahead translation. See comments at
+ user semantic actions for why this is necessary. */
+ yytoken = YYTRANSLATE (yychar);
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval);
+ }
/* Do not reclaim the symbols of the rule which action triggered
this YYABORT or YYACCEPT. */
YYPOPSTACK (yylen);
diff --git a/src/cmd/8a/y.tab.h b/src/cmd/8a/y.tab.h
index 4a84b53e4..14637cb33 100644
--- a/src/cmd/8a/y.tab.h
+++ b/src/cmd/8a/y.tab.h
@@ -1,24 +1,21 @@
-/* A Bison parser, made by GNU Bison 2.3. */
+/* A Bison parser, made by GNU Bison 2.5. */
-/* Skeleton interface for Bison's Yacc-like parsers in C
-
- Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
- Free Software Foundation, Inc.
-
- This program is free software; you can redistribute it and/or modify
+/* Bison interface for Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
+
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* As a special exception, you may create a larger work that contains
part or all of the Bison parser skeleton and distribute that work
@@ -29,10 +26,11 @@
special exception, which will cause the skeleton and the resulting
Bison output files to be licensed under the GNU General Public
License without this special exception.
-
+
This special exception was added by the Free Software Foundation in
version 2.2 of Bison. */
+
/* Tokens. */
#ifndef YYTOKENTYPE
# define YYTOKENTYPE
@@ -55,21 +53,23 @@
LTYPEG = 271,
LTYPEXC = 272,
LTYPEX = 273,
- LCONST = 274,
- LFP = 275,
- LPC = 276,
- LSB = 277,
- LBREG = 278,
- LLREG = 279,
- LSREG = 280,
- LFREG = 281,
- LXREG = 282,
- LFCONST = 283,
- LSCONST = 284,
- LSP = 285,
- LNAME = 286,
- LLAB = 287,
- LVAR = 288
+ LTYPEPC = 274,
+ LTYPEF = 275,
+ LCONST = 276,
+ LFP = 277,
+ LPC = 278,
+ LSB = 279,
+ LBREG = 280,
+ LLREG = 281,
+ LSREG = 282,
+ LFREG = 283,
+ LXREG = 284,
+ LFCONST = 285,
+ LSCONST = 286,
+ LSP = 287,
+ LNAME = 288,
+ LLAB = 289,
+ LVAR = 290
};
#endif
/* Tokens. */
@@ -89,29 +89,34 @@
#define LTYPEG 271
#define LTYPEXC 272
#define LTYPEX 273
-#define LCONST 274
-#define LFP 275
-#define LPC 276
-#define LSB 277
-#define LBREG 278
-#define LLREG 279
-#define LSREG 280
-#define LFREG 281
-#define LXREG 282
-#define LFCONST 283
-#define LSCONST 284
-#define LSP 285
-#define LNAME 286
-#define LLAB 287
-#define LVAR 288
+#define LTYPEPC 274
+#define LTYPEF 275
+#define LCONST 276
+#define LFP 277
+#define LPC 278
+#define LSB 279
+#define LBREG 280
+#define LLREG 281
+#define LSREG 282
+#define LFREG 283
+#define LXREG 284
+#define LFCONST 285
+#define LSCONST 286
+#define LSP 287
+#define LNAME 288
+#define LLAB 289
+#define LVAR 290
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
typedef union YYSTYPE
-#line 37 "a.y"
{
+
+/* Line 2068 of yacc.c */
+#line 38 "a.y"
+
Sym *sym;
int32 lval;
struct {
@@ -122,14 +127,17 @@ typedef union YYSTYPE
char sval[8];
Gen gen;
Gen2 gen2;
-}
-/* Line 1529 of yacc.c. */
-#line 128 "y.tab.h"
- YYSTYPE;
+
+
+
+/* Line 2068 of yacc.c */
+#line 135 "y.tab.h"
+} YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
# define YYSTYPE_IS_DECLARED 1
-# define YYSTYPE_IS_TRIVIAL 1
#endif
extern YYSTYPE yylval;
+
diff --git a/src/cmd/8c/cgen.c b/src/cmd/8c/cgen.c
index 78eb7eced..f54102245 100644
--- a/src/cmd/8c/cgen.c
+++ b/src/cmd/8c/cgen.c
@@ -29,6 +29,7 @@
// THE SOFTWARE.
#include "gc.h"
+#include "../../pkg/runtime/funcdata.h"
/* ,x/^(print|prtree)\(/i/\/\/ */
@@ -404,13 +405,13 @@ cgen(Node *n, Node *nn)
}
}
- if(o == OMUL) {
+ if(o == OMUL || o == OLMUL) {
if(l->addable >= INDEXED) {
t = l;
l = r;
r = t;
}
- /* should favour AX */
+ reg[D_DX]++; // for gopcode case OMUL
regalloc(&nod, l, nn);
cgen(l, &nod);
if(r->addable < INDEXED) {
@@ -422,6 +423,7 @@ cgen(Node *n, Node *nn)
gopcode(OMUL, n->type, r, &nod); /* addressible */
gmove(&nod, nn);
regfree(&nod);
+ reg[D_DX]--;
break;
}
@@ -936,6 +938,7 @@ cgen(Node *n, Node *nn)
return;
}
gargs(r, &nod, &nod1);
+ gpcdata(PCDATA_ArgSize, curarg);
if(l->addable < INDEXED) {
reglcgen(&nod, l, nn);
nod.op = OREGISTER;
@@ -943,6 +946,7 @@ cgen(Node *n, Node *nn)
regfree(&nod);
} else
gopcode(OFUNC, n->type, Z, l);
+ gpcdata(PCDATA_ArgSize, -1);
if(REGARG >= 0 && reg[REGARG])
reg[REGARG]--;
if(nn != Z) {
diff --git a/src/cmd/8c/gc.h b/src/cmd/8c/gc.h
index bdf981b4c..b668b4c63 100644
--- a/src/cmd/8c/gc.h
+++ b/src/cmd/8c/gc.h
@@ -298,6 +298,7 @@ void patch(Prog*, int32);
int sconst(Node*);
void gpseudo(int, Sym*, Node*);
void gprefetch(Node*);
+void gpcdata(int, int);
/*
* swt.c
diff --git a/src/cmd/8c/peep.c b/src/cmd/8c/peep.c
index 9c3e9a5af..da0127d11 100644
--- a/src/cmd/8c/peep.c
+++ b/src/cmd/8c/peep.c
@@ -403,7 +403,7 @@ copy1(Adr *v1, Adr *v2, Reg *r, int f)
}
t = copyu(p, v2, A);
switch(t) {
- case 2: /* rar, cant split */
+ case 2: /* rar, can't split */
if(debug['P'])
print("; %D rar; return 0\n", v2);
return 0;
diff --git a/src/cmd/8c/reg.c b/src/cmd/8c/reg.c
index 6c87d70a5..a3d5d6115 100644
--- a/src/cmd/8c/reg.c
+++ b/src/cmd/8c/reg.c
@@ -111,6 +111,7 @@ regopt(Prog *p)
case AGLOBL:
case ANAME:
case ASIGNAME:
+ case AFUNCDATA:
continue;
}
r = rega();
@@ -584,6 +585,7 @@ brk:
case AGLOBL:
case ANAME:
case ASIGNAME:
+ case AFUNCDATA:
break;
}
}
diff --git a/src/cmd/8c/sgen.c b/src/cmd/8c/sgen.c
index b0f2bc544..069bbc1fc 100644
--- a/src/cmd/8c/sgen.c
+++ b/src/cmd/8c/sgen.c
@@ -34,11 +34,9 @@ Prog*
gtext(Sym *s, int32 stkoff)
{
int32 a;
-
- a = 0;
- if(!(textflag & NOSPLIT))
- a = argsize();
- else if(stkoff >= 128)
+
+ a = argsize();
+ if((textflag & NOSPLIT) != 0 && stkoff >= 128)
yyerror("stack frame too large for NOSPLIT function");
gpseudo(ATEXT, s, nodconst(stkoff));
diff --git a/src/cmd/8c/swt.c b/src/cmd/8c/swt.c
index 1b8ceb0c6..b68197447 100644
--- a/src/cmd/8c/swt.c
+++ b/src/cmd/8c/swt.c
@@ -315,12 +315,8 @@ outcode(void)
goto jackpot;
break;
}
- Bputc(&b, p->as);
- Bputc(&b, p->as>>8);
- Bputc(&b, p->lineno);
- Bputc(&b, p->lineno>>8);
- Bputc(&b, p->lineno>>16);
- Bputc(&b, p->lineno>>24);
+ BPUTLE2(&b, p->as);
+ BPUTLE4(&b, p->lineno);
zaddr(&b, &p->from, sf);
zaddr(&b, &p->to, st);
}
@@ -396,13 +392,12 @@ outhist(Biobuf *b)
q = 0;
}
if(n) {
- Bputc(b, ANAME);
- Bputc(b, ANAME>>8);
- Bputc(b, D_FILE);
- Bputc(b, 1);
- Bputc(b, '<');
+ BPUTLE2(b, ANAME);
+ BPUTC(b, D_FILE);
+ BPUTC(b, 1);
+ BPUTC(b, '<');
Bwrite(b, p, n);
- Bputc(b, 0);
+ BPUTC(b, 0);
}
p = q;
if(p == 0 && op) {
@@ -416,12 +411,8 @@ outhist(Biobuf *b)
if(h->offset)
pg.to.type = D_CONST;
- Bputc(b, pg.as);
- Bputc(b, pg.as>>8);
- Bputc(b, pg.lineno);
- Bputc(b, pg.lineno>>8);
- Bputc(b, pg.lineno>>16);
- Bputc(b, pg.lineno>>24);
+ BPUTLE2(b, pg.as);
+ BPUTLE4(b, pg.lineno);
zaddr(b, &pg.from, 0);
zaddr(b, &pg.to, 0);
@@ -440,26 +431,21 @@ zname(Biobuf *b, Sym *s, int t)
if(debug['T'] && t == D_EXTERN && s->sig != SIGDONE && s->type != types[TENUM] && s != symrathole){
sig = sign(s);
- Bputc(b, ASIGNAME);
- Bputc(b, ASIGNAME>>8);
- Bputc(b, sig);
- Bputc(b, sig>>8);
- Bputc(b, sig>>16);
- Bputc(b, sig>>24);
+ BPUTLE2(b, ASIGNAME);
+ BPUTLE4(b, sig);
s->sig = SIGDONE;
}
else{
- Bputc(b, ANAME); /* as */
- Bputc(b, ANAME>>8); /* as */
+ BPUTLE2(b, ANAME); /* as */
}
- Bputc(b, t); /* type */
- Bputc(b, s->sym); /* sym */
+ BPUTC(b, t); /* type */
+ BPUTC(b, s->sym); /* sym */
n = s->name;
while(*n) {
- Bputc(b, *n);
+ BPUTC(b, *n);
n++;
}
- Bputc(b, 0);
+ BPUTC(b, 0);
}
void
@@ -493,52 +479,38 @@ zaddr(Biobuf *b, Adr *a, int s)
t |= T_OFFSET|T_OFFSET2;
break;
}
- Bputc(b, t);
+ BPUTC(b, t);
if(t & T_INDEX) { /* implies index, scale */
- Bputc(b, a->index);
- Bputc(b, a->scale);
+ BPUTC(b, a->index);
+ BPUTC(b, a->scale);
}
if(t & T_OFFSET) { /* implies offset */
l = a->offset;
- Bputc(b, l);
- Bputc(b, l>>8);
- Bputc(b, l>>16);
- Bputc(b, l>>24);
+ BPUTLE4(b, l);
}
if(t & T_OFFSET2) { /* implies offset2 */
l = a->offset2;
- Bputc(b, l);
- Bputc(b, l>>8);
- Bputc(b, l>>16);
- Bputc(b, l>>24);
+ BPUTLE4(b, l);
}
if(t & T_SYM) /* implies sym */
- Bputc(b, s);
+ BPUTC(b, s);
if(t & T_FCONST) {
ieeedtod(&e, a->dval);
- l = e.l;
- Bputc(b, l);
- Bputc(b, l>>8);
- Bputc(b, l>>16);
- Bputc(b, l>>24);
- l = e.h;
- Bputc(b, l);
- Bputc(b, l>>8);
- Bputc(b, l>>16);
- Bputc(b, l>>24);
+ BPUTLE4(b, e.l);
+ BPUTLE4(b, e.h);
return;
}
if(t & T_SCONST) {
n = a->sval;
for(i=0; i<NSNAME; i++) {
- Bputc(b, *n);
+ BPUTC(b, *n);
n++;
}
return;
}
if(t & T_TYPE)
- Bputc(b, a->type);
+ BPUTC(b, a->type);
}
int32
diff --git a/src/cmd/8c/txt.c b/src/cmd/8c/txt.c
index 1b7617bc5..5c486af38 100644
--- a/src/cmd/8c/txt.c
+++ b/src/cmd/8c/txt.c
@@ -146,9 +146,7 @@ gclean(void)
continue;
if(s->type == types[TENUM])
continue;
- textflag = s->dataflag;
gpseudo(AGLOBL, s, nodconst(s->type->width));
- textflag = 0;
}
nextpc();
p->as = AEND;
@@ -965,7 +963,7 @@ print("botch in doindex\n");
else if(n->left->op == OREGISTER)
idx.ptr = n->left->reg;
else if(n->left->op != OADDR) {
- reg[D_BP]++; // cant be used as a base
+ reg[D_BP]++; // can't be used as a base
regalloc(&nod1, &regnode, Z);
cgen(n->left, &nod1);
idx.ptr = nod1.reg;
@@ -1381,8 +1379,16 @@ gpseudo(int a, Sym *s, Node *n)
p->as = a;
p->from.type = D_EXTERN;
p->from.sym = s;
- p->from.scale = textflag;
- textflag = 0;
+
+ switch(a) {
+ case ATEXT:
+ p->from.scale = textflag;
+ textflag = 0;
+ break;
+ case AGLOBL:
+ p->from.scale = s->dataflag;
+ break;
+ }
if(s->class == CSTATIC)
p->from.type = D_STATIC;
@@ -1392,6 +1398,15 @@ gpseudo(int a, Sym *s, Node *n)
}
void
+gpcdata(int index, int value)
+{
+ Node n1;
+
+ n1 = *nodconst(index);
+ gins(APCDATA, &n1, nodconst(value));
+}
+
+void
gprefetch(Node *n)
{
Node n1;
diff --git a/src/cmd/8g/cgen.c b/src/cmd/8g/cgen.c
index d54db7e62..cc28a3145 100644
--- a/src/cmd/8g/cgen.c
+++ b/src/cmd/8g/cgen.c
@@ -67,6 +67,8 @@ cgen(Node *n, Node *res)
case OSLICE:
case OSLICEARR:
case OSLICESTR:
+ case OSLICE3:
+ case OSLICE3ARR:
if (res->op != ONAME || !res->addable) {
tempname(&n1, n->type);
cgen_slice(n, &n1);
@@ -107,6 +109,7 @@ cgen(Node *n, Node *res)
// can't do in walk because n->left->addable
// changes if n->left is an escaping local variable.
switch(n->op) {
+ case OSPTR:
case OLEN:
if(isslice(n->left->type) || istype(n->left->type, TSTRING))
n->addable = n->left->addable;
@@ -286,6 +289,22 @@ cgen(Node *n, Node *res)
regfree(&n1);
break;
+ case OSPTR:
+ // pointer is the first word of string or slice.
+ if(isconst(nl, CTSTR)) {
+ regalloc(&n1, types[tptr], res);
+ p1 = gins(ALEAL, N, &n1);
+ datastring(nl->val.u.sval->s, nl->val.u.sval->len, &p1->from);
+ gmove(&n1, res);
+ regfree(&n1);
+ break;
+ }
+ igen(nl, &n1, res);
+ n1.type = n->type;
+ gmove(&n1, res);
+ regfree(&n1);
+ break;
+
case OLEN:
if(istype(nl->type, TMAP) || istype(nl->type, TCHAN)) {
// map has len in the first 32-bit word.
@@ -474,12 +493,13 @@ igenindex(Node *n, Node *res, int bounded)
/*
* address gen
* res = &n;
+ * The generated code checks that the result is not nil.
*/
void
agen(Node *n, Node *res)
{
Node *nl, *nr;
- Node n1, n2, n3, n4, tmp, nlen;
+ Node n1, n2, n3, tmp, nlen;
Type *t;
uint32 w;
uint64 v;
@@ -547,6 +567,8 @@ agen(Node *n, Node *res)
case OSLICE:
case OSLICEARR:
case OSLICESTR:
+ case OSLICE3:
+ case OSLICE3ARR:
tempname(&n1, n->type);
cgen_slice(n, &n1);
agen(&n1, res);
@@ -602,16 +624,6 @@ agen(Node *n, Node *res)
// len(a) is in nlen (if needed)
// w is width
- // explicit check for nil if array is large enough
- // that we might derive too big a pointer.
- if(isfixedarray(nl->type) && nl->type->width >= unmappedzero) {
- n4 = n3;
- n4.op = OINDREG;
- n4.type = types[TUINT8];
- n4.xoffset = 0;
- gins(ATESTB, nodintconst(0), &n4);
- }
-
// constant index
if(isconst(nr, CTINT)) {
if(isconst(nl, CTSTR))
@@ -735,23 +747,11 @@ agen(Node *n, Node *res)
case OIND:
cgen(nl, res);
+ cgen_checknil(res);
break;
case ODOT:
agen(nl, res);
- // explicit check for nil if struct is large enough
- // that we might derive too big a pointer. If the left node
- // was ODOT we have already done the nil check.
- if(nl->op != ODOT)
- if(nl->type->width >= unmappedzero) {
- regalloc(&n1, types[tptr], res);
- gmove(res, &n1);
- n1.op = OINDREG;
- n1.type = types[TUINT8];
- n1.xoffset = 0;
- gins(ATESTB, nodintconst(0), &n1);
- regfree(&n1);
- }
if(n->xoffset != 0) {
nodconst(&n1, types[tptr], n->xoffset);
gins(optoas(OADD, types[tptr]), &n1, res);
@@ -763,17 +763,7 @@ agen(Node *n, Node *res)
if(!isptr[t->etype])
fatal("agen: not ptr %N", n);
cgen(nl, res);
- // explicit check for nil if struct is large enough
- // that we might derive too big a pointer.
- if(nl->type->type->width >= unmappedzero) {
- regalloc(&n1, types[tptr], res);
- gmove(res, &n1);
- n1.op = OINDREG;
- n1.type = types[TUINT8];
- n1.xoffset = 0;
- gins(ATESTB, nodintconst(0), &n1);
- regfree(&n1);
- }
+ cgen_checknil(res);
if(n->xoffset != 0) {
nodconst(&n1, types[tptr], n->xoffset);
gins(optoas(OADD, types[tptr]), &n1, res);
@@ -789,6 +779,7 @@ agen(Node *n, Node *res)
*
* on exit, a has been changed to be *newreg.
* caller must regfree(a).
+ * The generated code checks that the result is not *nil.
*/
void
igen(Node *n, Node *a, Node *res)
@@ -838,15 +829,7 @@ igen(Node *n, Node *a, Node *res)
regalloc(a, types[tptr], res);
cgen(n->left, a);
}
- // explicit check for nil if struct is large enough
- // that we might derive too big a pointer.
- if(n->left->type->type->width >= unmappedzero) {
- n1 = *a;
- n1.op = OINDREG;
- n1.type = types[TUINT8];
- n1.xoffset = 0;
- gins(ATESTB, nodintconst(0), &n1);
- }
+ cgen_checknil(a);
a->op = OINDREG;
a->xoffset += n->xoffset;
a->type = n->type;
@@ -874,7 +857,35 @@ igen(Node *n, Node *a, Node *res)
a->xoffset = fp->width;
a->type = n->type;
return;
+
+ case OINDEX:
+ // Index of fixed-size array by constant can
+ // put the offset in the addressing.
+ // Could do the same for slice except that we need
+ // to use the real index for the bounds checking.
+ if(isfixedarray(n->left->type) ||
+ (isptr[n->left->type->etype] && isfixedarray(n->left->left->type)))
+ if(isconst(n->right, CTINT)) {
+ // Compute &a.
+ if(!isptr[n->left->type->etype])
+ igen(n->left, a, res);
+ else {
+ igen(n->left, &n1, res);
+ cgen_checknil(&n1);
+ regalloc(a, types[tptr], res);
+ gmove(&n1, a);
+ regfree(&n1);
+ a->op = OINDREG;
+ }
+
+ // Compute &a[i] as &a + i*width.
+ a->type = n->type;
+ a->xoffset += mpgetfix(n->right->val.u.xval)*n->type->width;
+ return;
+ }
+ break;
}
+
// release register for now, to avoid
// confusing tempname.
if(res != N && res->op == OREGISTER)
@@ -1319,7 +1330,7 @@ cadable(Node *n)
* copy a composite value by moving its individual components.
* Slices, strings and interfaces are supported.
* nr is N when assigning a zero value.
- * return 1 if can do, 0 if cant.
+ * return 1 if can do, 0 if can't.
*/
int
componentgen(Node *nr, Node *nl)
diff --git a/src/cmd/8g/gg.h b/src/cmd/8g/gg.h
index 03c206aa9..55fdded0b 100644
--- a/src/cmd/8g/gg.h
+++ b/src/cmd/8g/gg.h
@@ -42,7 +42,7 @@ struct Prog
Addr from; // src address
Addr to; // dst address
Prog* link; // next instruction in this func
- void* reg; // pointer to containing Reg struct
+ void* opt; // for optimizer passes
};
#define TEXTFLAG from.scale
@@ -75,7 +75,6 @@ extern uint32 unmappedzero;
* ggen.c
*/
void compile(Node*);
-void proglist(void);
void gen(Node*);
Node* lookdot(Node*, Node*, int);
void cgen_as(Node*, Node*);
@@ -123,7 +122,6 @@ void cgen64(Node*, Node*);
* gsubr.c
*/
void clearp(Prog*);
-void proglist(void);
Prog* gbranch(int, Type*, int);
Prog* prog(int);
void gconv(int, int);
@@ -153,7 +151,7 @@ void split64(Node*, Node*, Node*);
void splitclean(void);
void nswap(Node*, Node*);
void gtrack(Sym*);
-
+void gargsize(int32);
/*
* cplx.c
*/
diff --git a/src/cmd/8g/ggen.c b/src/cmd/8g/ggen.c
index b0154bb80..fa5ed00dd 100644
--- a/src/cmd/8g/ggen.c
+++ b/src/cmd/8g/ggen.c
@@ -9,17 +9,61 @@
#include "gg.h"
#include "opt.h"
+static Prog* appendp(Prog*, int, int, int32, int, int32);
+
void
-defframe(Prog *ptxt)
+defframe(Prog *ptxt, Bvec *bv)
{
+ uint32 frame;
+ Prog *p;
+ int i, j;
+
// fill in argument size
ptxt->to.offset2 = rnd(curfn->type->argwid, widthptr);
// fill in final stack size
if(stksize > maxstksize)
maxstksize = stksize;
- ptxt->to.offset = rnd(maxstksize+maxarg, widthptr);
+ frame = rnd(maxstksize+maxarg, widthptr);
+ ptxt->to.offset = frame;
maxstksize = 0;
+
+ // insert code to clear pointered part of the frame,
+ // so that garbage collector only sees initialized values
+ // when it looks for pointers.
+ p = ptxt;
+ if(stkzerosize >= 8*widthptr) {
+ p = appendp(p, AMOVL, D_CONST, 0, D_AX, 0);
+ p = appendp(p, AMOVL, D_CONST, stkzerosize/widthptr, D_CX, 0);
+ p = appendp(p, ALEAL, D_SP+D_INDIR, frame-stkzerosize, D_DI, 0);
+ p = appendp(p, AREP, D_NONE, 0, D_NONE, 0);
+ appendp(p, ASTOSL, D_NONE, 0, D_NONE, 0);
+ } else {
+ j = (stkptrsize - stkzerosize)/widthptr * 2;
+ for(i=0; i<stkzerosize; i+=widthptr) {
+ if(bvget(bv, j) || bvget(bv, j+1))
+ p = appendp(p, AMOVL, D_CONST, 0, D_SP+D_INDIR, frame-stkzerosize+i);
+ j += 2;
+ }
+ }
+}
+
+static Prog*
+appendp(Prog *p, int as, int ftype, int32 foffset, int ttype, int32 toffset)
+{
+ Prog *q;
+
+ q = mal(sizeof(*q));
+ clearp(q);
+ q->as = as;
+ q->lineno = p->lineno;
+ q->from.type = ftype;
+ q->from.offset = foffset;
+ q->to.type = ttype;
+ q->to.offset = toffset;
+ q->link = p->link;
+ p->link = q;
+ return q;
}
// Sweep the prog list to mark any used nodes.
@@ -38,7 +82,7 @@ markautoused(Prog* p)
}
}
-// Fixup instructions after compactframe has moved all autos around.
+// Fixup instructions after allocauto (formerly compactframe) has moved all autos around.
void
fixautoused(Prog* p)
{
@@ -115,9 +159,28 @@ clearfat(Node *nl)
void
ginscall(Node *f, int proc)
{
+ int32 arg;
Prog *p;
Node reg, r1, con;
+ if(f->type != T)
+ setmaxarg(f->type);
+
+ arg = -1;
+ // Most functions have a fixed-size argument block, so traceback uses that during unwind.
+ // Not all, though: there are some variadic functions in package runtime,
+ // and for those we emit call-specific metadata recorded by caller.
+ // Reflect generates functions with variable argsize (see reflect.methodValueCall/makeFuncStub),
+ // so we do this for all indirect calls as well.
+ if(f->type != T && (f->sym == S || (f->sym != S && f->sym->pkg == runtimepkg) || proc == 1 || proc == 2)) {
+ arg = f->type->argwid;
+ if(proc == 1 || proc == 2)
+ arg += 2*widthptr;
+ }
+
+ if(arg != -1)
+ gargsize(arg);
+
switch(proc) {
default:
fatal("ginscall: bad proc %d", proc);
@@ -126,6 +189,19 @@ ginscall(Node *f, int proc)
case 0: // normal call
case -1: // normal call but no return
if(f->op == ONAME && f->class == PFUNC) {
+ if(f == deferreturn) {
+ // Deferred calls will appear to be returning to
+ // the CALL deferreturn(SB) that we are about to emit.
+ // However, the stack trace code will show the line
+ // of the instruction byte before the return PC.
+ // To avoid that being an unrelated instruction,
+ // insert an x86 NOP that we will have the right line number.
+ // x86 NOP 0x90 is really XCHG AX, AX; use that description
+ // because the NOP pseudo-instruction will be removed by
+ // the linker.
+ nodreg(&reg, types[TINT], D_AX);
+ gins(AXCHGL, &reg, &reg);
+ }
p = gins(ACALL, N, f);
afunclit(&p->to, f);
if(proc == -1 || noreturn(p))
@@ -164,6 +240,9 @@ ginscall(Node *f, int proc)
}
break;
}
+
+ if(arg != -1)
+ gargsize(-1);
}
/*
@@ -212,6 +291,7 @@ cgen_callinter(Node *n, Node *res, int proc)
regalloc(&nodr, types[tptr], &nodo);
if(n->left->xoffset == BADWIDTH)
fatal("cgen_callinter: badwidth");
+ cgen_checknil(&nodo);
nodo.op = OINDREG;
nodo.xoffset = n->left->xoffset + 3*widthptr + 8;
@@ -224,14 +304,11 @@ cgen_callinter(Node *n, Node *res, int proc)
gins(ALEAL, &nodo, &nodr); // REG = &(20+offset(REG)) -- i.tab->fun[f]
}
- // BOTCH nodr.type = fntype;
nodr.type = n->left->type;
ginscall(&nodr, proc);
regfree(&nodr);
regfree(&nodo);
-
- setmaxarg(n->left->type);
}
/*
@@ -259,8 +336,6 @@ cgen_call(Node *n, int proc)
genlist(n->list); // assign the args
t = n->left->type;
- setmaxarg(t);
-
// call tempname pointer
if(n->left->ullman >= UINF) {
regalloc(&nod, types[tptr], N);
@@ -360,11 +435,18 @@ cgen_aret(Node *n, Node *res)
void
cgen_ret(Node *n)
{
+ Prog *p;
+
genlist(n->list); // copy out args
- if(retpc)
+ if(retpc) {
gjmp(retpc);
- else
- gins(ARET, N, N);
+ return;
+ }
+ p = gins(ARET, N, N);
+ if(n->op == ORETJMP) {
+ p->to.type = D_EXTERN;
+ p->to.sym = n->left->sym;
+ }
}
/*
@@ -1138,3 +1220,51 @@ ret:
patch(gbranch(optoas(a, nr->type), T, likely), to);
}
+
+// Called after regopt and peep have run.
+// Expand CHECKNIL pseudo-op into actual nil pointer check.
+void
+expandchecks(Prog *firstp)
+{
+ Prog *p, *p1, *p2;
+
+ for(p = firstp; p != P; p = p->link) {
+ if(p->as != ACHECKNIL)
+ continue;
+ if(debug_checknil && p->lineno > 1) // p->lineno==1 in generated wrappers
+ warnl(p->lineno, "generated nil check");
+ // check is
+ // CMP arg, $0
+ // JNE 2(PC) (likely)
+ // MOV AX, 0
+ p1 = mal(sizeof *p1);
+ p2 = mal(sizeof *p2);
+ clearp(p1);
+ clearp(p2);
+ p1->link = p2;
+ p2->link = p->link;
+ p->link = p1;
+ p1->lineno = p->lineno;
+ p2->lineno = p->lineno;
+ p1->loc = 9999;
+ p2->loc = 9999;
+ p->as = ACMPL;
+ p->to.type = D_CONST;
+ p->to.offset = 0;
+ p1->as = AJNE;
+ p1->from.type = D_CONST;
+ p1->from.offset = 1; // likely
+ p1->to.type = D_BRANCH;
+ p1->to.u.branch = p2->link;
+ // crash by write to memory address 0.
+ // if possible, since we know arg is 0, use 0(arg),
+ // which will be shorter to encode than plain 0.
+ p2->as = AMOVL;
+ p2->from.type = D_AX;
+ if(regtyp(&p->from))
+ p2->to.type = p->from.type + D_INDIR;
+ else
+ p2->to.type = D_INDIR+D_NONE;
+ p2->to.offset = 0;
+ }
+}
diff --git a/src/cmd/8g/gobj.c b/src/cmd/8g/gobj.c
index 39717d5b1..0517824e0 100644
--- a/src/cmd/8g/gobj.c
+++ b/src/cmd/8g/gobj.c
@@ -35,10 +35,9 @@
void
zname(Biobuf *b, Sym *s, int t)
{
- Bputc(b, ANAME); /* as */
- Bputc(b, ANAME>>8); /* as */
- Bputc(b, t); /* type */
- Bputc(b, s->sym); /* sym */
+ BPUTLE2(b, ANAME); /* as */
+ BPUTC(b, t); /* type */
+ BPUTC(b, s->sym); /* sym */
Bputname(b, s);
}
@@ -46,13 +45,12 @@ zname(Biobuf *b, Sym *s, int t)
void
zfile(Biobuf *b, char *p, int n)
{
- Bputc(b, ANAME);
- Bputc(b, ANAME>>8);
- Bputc(b, D_FILE);
- Bputc(b, 1);
- Bputc(b, '<');
+ BPUTLE2(b, ANAME);
+ BPUTC(b, D_FILE);
+ BPUTC(b, 1);
+ BPUTC(b, '<');
Bwrite(b, p, n);
- Bputc(b, 0);
+ BPUTC(b, 0);
}
void
@@ -60,12 +58,8 @@ zhist(Biobuf *b, int line, vlong offset)
{
Addr a;
- Bputc(b, AHISTORY);
- Bputc(b, AHISTORY>>8);
- Bputc(b, line);
- Bputc(b, line>>8);
- Bputc(b, line>>16);
- Bputc(b, line>>24);
+ BPUTLE2(b, AHISTORY);
+ BPUTLE4(b, line);
zaddr(b, &zprog.from, 0, 0);
a = zprog.to;
if(offset != 0) {
@@ -114,54 +108,40 @@ zaddr(Biobuf *b, Addr *a, int s, int gotype)
t |= T_SCONST;
break;
}
- Bputc(b, t);
+ BPUTC(b, t);
if(t & T_INDEX) { /* implies index, scale */
- Bputc(b, a->index);
- Bputc(b, a->scale);
+ BPUTC(b, a->index);
+ BPUTC(b, a->scale);
}
if(t & T_OFFSET) { /* implies offset */
l = a->offset;
- Bputc(b, l);
- Bputc(b, l>>8);
- Bputc(b, l>>16);
- Bputc(b, l>>24);
+ BPUTLE4(b, l);
}
if(t & T_OFFSET2) { /* implies offset */
l = a->offset2;
- Bputc(b, l);
- Bputc(b, l>>8);
- Bputc(b, l>>16);
- Bputc(b, l>>24);
+ BPUTLE4(b, l);
}
if(t & T_SYM) /* implies sym */
- Bputc(b, s);
+ BPUTC(b, s);
if(t & T_FCONST) {
ieeedtod(&e, a->u.dval);
- l = e;
- Bputc(b, l);
- Bputc(b, l>>8);
- Bputc(b, l>>16);
- Bputc(b, l>>24);
- l = e >> 32;
- Bputc(b, l);
- Bputc(b, l>>8);
- Bputc(b, l>>16);
- Bputc(b, l>>24);
+ BPUTLE4(b, e);
+ BPUTLE4(b, e >> 32);
return;
}
if(t & T_SCONST) {
n = a->u.sval;
for(i=0; i<NSNAME; i++) {
- Bputc(b, *n);
+ BPUTC(b, *n);
n++;
}
return;
}
if(t & T_TYPE)
- Bputc(b, a->type);
+ BPUTC(b, a->type);
if(t & T_GOTYPE)
- Bputc(b, gotype);
+ BPUTC(b, gotype);
}
static struct {
@@ -267,12 +247,8 @@ dumpfuncs(void)
break;
}
- Bputc(bout, p->as);
- Bputc(bout, p->as>>8);
- Bputc(bout, p->lineno);
- Bputc(bout, p->lineno>>8);
- Bputc(bout, p->lineno>>16);
- Bputc(bout, p->lineno>>24);
+ BPUTLE2(bout, p->as);
+ BPUTLE4(bout, p->lineno);
zaddr(bout, &p->from, sf, gf);
zaddr(bout, &p->to, st, gt);
}
@@ -508,111 +484,6 @@ dsymptr(Sym *s, int off, Sym *x, int xoff)
}
void
-genembedtramp(Type *rcvr, Type *method, Sym *newnam, int iface)
-{
- Sym *e;
- int c, d, o, mov, add, loaded;
- Prog *p;
- Type *f;
-
- USED(iface);
-
- e = method->sym;
- for(d=0; d<nelem(dotlist); d++) {
- c = adddot1(e, rcvr, d, nil, 0);
- if(c == 1)
- goto out;
- }
- fatal("genembedtramp %T.%S", rcvr, method->sym);
-
-out:
- newplist()->name = newname(newnam);
-
- //TEXT main·S_test2(SB),7,$0
- p = pc;
- gins(ATEXT, N, N);
- p->from.type = D_EXTERN;
- p->from.sym = newnam;
- p->to.type = D_CONST;
- p->to.offset = 0;
- p->from.scale = 7;
-//print("1. %P\n", p);
-
- mov = AMOVL;
- add = AADDL;
-
- loaded = 0;
- o = 0;
- for(c=d-1; c>=0; c--) {
- f = dotlist[c].field;
- o += f->width;
- if(!isptr[f->type->etype])
- continue;
- if(!loaded) {
- loaded = 1;
- //MOVL 4(SP), AX
- p = pc;
- gins(mov, N, N);
- p->from.type = D_INDIR+D_SP;
- p->from.offset = widthptr;
- p->to.type = D_AX;
-//print("2. %P\n", p);
- }
-
- //MOVL o(AX), AX
- p = pc;
- gins(mov, N, N);
- p->from.type = D_INDIR+D_AX;
- p->from.offset = o;
- p->to.type = D_AX;
-//print("3. %P\n", p);
- o = 0;
- }
- if(o != 0) {
- //ADDL $XX, AX
- p = pc;
- gins(add, N, N);
- p->from.type = D_CONST;
- p->from.offset = o;
- if(loaded)
- p->to.type = D_AX;
- else {
- p->to.type = D_INDIR+D_SP;
- p->to.offset = widthptr;
- }
-//print("4. %P\n", p);
- }
-
- //MOVL AX, 4(SP)
- if(loaded) {
- p = pc;
- gins(mov, N, N);
- p->from.type = D_AX;
- p->to.type = D_INDIR+D_SP;
- p->to.offset = widthptr;
-//print("5. %P\n", p);
- } else {
- // TODO(rsc): obviously this is unnecessary,
- // but 6l has a bug, and it can't handle
- // JMP instructions too close to the top of
- // a new function.
- gins(ANOP, N, N);
- }
-
- f = dotlist[0].field;
- //JMP main·*Sub_test2(SB)
- if(isptr[f->type->etype])
- f = f->type;
- p = pc;
- gins(AJMP, N, N);
- p->to.type = D_EXTERN;
- p->to.sym = methodsym(method->sym, ptrto(f->type), 0);
-//print("6. %P\n", p);
-
- pc->as = ARET; // overwrite AEND
-}
-
-void
nopout(Prog *p)
{
p->as = ANOP;
diff --git a/src/cmd/8g/gsubr.c b/src/cmd/8g/gsubr.c
index 756bdd203..34703ba6e 100644
--- a/src/cmd/8g/gsubr.c
+++ b/src/cmd/8g/gsubr.c
@@ -31,9 +31,11 @@
#include <u.h>
#include <libc.h>
#include "gg.h"
+#include "../../pkg/runtime/funcdata.h"
// TODO(rsc): Can make this bigger if we move
// the text segment up higher in 8l for all GOOS.
+// At the same time, can raise StackBig in ../../pkg/runtime/stack.h.
uint32 unmappedzero = 4096;
#define CASE(a,b) (((a)<<16)|((b)<<0))
@@ -208,6 +210,16 @@ ggloblnod(Node *nam)
}
void
+gargsize(int32 size)
+{
+ Node n1, n2;
+
+ nodconst(&n1, types[TINT32], PCDATA_ArgSize);
+ nodconst(&n2, types[TINT32], size);
+ gins(APCDATA, &n1, &n2);
+}
+
+void
ggloblsym(Sym *s, int32 width, int dupok, int rodata)
{
Prog *p;
@@ -1145,6 +1157,7 @@ ismem(Node *n)
{
switch(n->op) {
case OITAB:
+ case OSPTR:
case OLEN:
case OCAP:
case OINDREG:
@@ -2158,43 +2171,6 @@ gins(int as, Node *f, Node *t)
return p;
}
-// Generate an instruction referencing *n
-// to force segv on nil pointer dereference.
-void
-checkref(Node *n, int force)
-{
- Node m;
-
- if(!force && isptr[n->type->etype] && n->type->type->width < unmappedzero)
- return;
-
- regalloc(&m, types[TUINTPTR], n);
- cgen(n, &m);
- m.xoffset = 0;
- m.op = OINDREG;
- m.type = types[TUINT8];
- gins(ATESTB, nodintconst(0), &m);
- regfree(&m);
-}
-
-static void
-checkoffset(Addr *a, int canemitcode)
-{
- Prog *p;
-
- if(a->offset < unmappedzero)
- return;
- if(!canemitcode)
- fatal("checkoffset %#x, cannot emit code", a->offset);
-
- // cannot rely on unmapped nil page at 0 to catch
- // reference with large offset. instead, emit explicit
- // test of 0(reg).
- p = gins(ATESTB, nodintconst(0), N);
- p->to = *a;
- p->to.offset = 0;
-}
-
/*
* generate code to compute n;
* make a refer to result.
@@ -2344,8 +2320,16 @@ naddr(Node *n, Addr *a, int canemitcode)
break; // len(nil)
a->etype = tptr;
a->width = widthptr;
- if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero)
- checkoffset(a, canemitcode);
+ break;
+
+ case OSPTR:
+ // pointer in a string or slice
+ naddr(n->left, a, canemitcode);
+ if(a->type == D_CONST && a->offset == 0)
+ break; // ptr(nil)
+ a->etype = simtype[TUINTPTR];
+ a->offset += Array_array;
+ a->width = widthptr;
break;
case OLEN:
@@ -2356,8 +2340,6 @@ naddr(Node *n, Addr *a, int canemitcode)
a->etype = TUINT32;
a->offset += Array_nel;
a->width = 4;
- if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero)
- checkoffset(a, canemitcode);
break;
case OCAP:
@@ -2368,8 +2350,6 @@ naddr(Node *n, Addr *a, int canemitcode)
a->etype = TUINT32;
a->offset += Array_cap;
a->width = 4;
- if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero)
- checkoffset(a, canemitcode);
break;
// case OADD:
diff --git a/src/cmd/8g/opt.h b/src/cmd/8g/opt.h
index b80043e0f..0d99bdb97 100644
--- a/src/cmd/8g/opt.h
+++ b/src/cmd/8g/opt.h
@@ -28,6 +28,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#include "../gc/popt.h"
+
#define Z N
#define Adr Addr
@@ -50,9 +52,10 @@ typedef struct Rgn Rgn;
// A Reg is a wrapper around a single Prog (one instruction) that holds
// register optimization information while the optimizer runs.
// r->prog is the instruction.
-// r->prog->regp points back to r.
+// r->prog->opt points back to r.
struct Reg
{
+ Flow f;
Bits set; // variables written by this instruction.
Bits use1; // variables read by prog->from.
@@ -93,8 +96,6 @@ struct Rgn
EXTERN int32 exregoffset; // not set
EXTERN int32 exfregoffset; // not set
-EXTERN Reg* firstr;
-EXTERN Reg* lastr;
EXTERN Reg zreg;
EXTERN Reg* freer;
EXTERN Reg** rpo2r;
@@ -139,32 +140,80 @@ void paint1(Reg*, int);
uint32 paint2(Reg*, int);
void paint3(Reg*, int, int32, int);
void addreg(Adr*, int);
-void dumpone(Reg*);
-void dumpit(char*, Reg*);
-int noreturn(Prog *p);
+void dumpone(Flow*, int);
+void dumpit(char*, Flow*, int);
/*
* peep.c
*/
-void peep(void);
-void excise(Reg*);
-Reg* uniqp(Reg*);
-Reg* uniqs(Reg*);
-int regtyp(Adr*);
-int anyvar(Adr*);
-int subprop(Reg*);
-int copyprop(Reg*);
-int copy1(Adr*, Adr*, Reg*, int);
+void peep(Prog*);
+void excise(Flow*);
int copyu(Prog*, Adr*, Adr*);
-int copyas(Adr*, Adr*);
-int copyau(Adr*, Adr*);
-int copysub(Adr*, Adr*, Adr*, int);
-int copysub1(Prog*, Adr*, Adr*, int);
-
int32 RtoB(int);
int32 FtoB(int);
int BtoR(int32);
int BtoF(int32);
#pragma varargck type "D" Adr*
+
+/*
+ * prog.c
+ */
+typedef struct ProgInfo ProgInfo;
+struct ProgInfo
+{
+ uint32 flags; // the bits below
+ uint32 reguse; // required registers used by this instruction
+ uint32 regset; // required registers set by this instruction
+ uint32 regindex; // registers used by addressing mode
+};
+
+enum
+{
+ // Pseudo-op, like TEXT, GLOBL, TYPE, PCDATA, FUNCDATA.
+ Pseudo = 1<<1,
+
+ // There's nothing to say about the instruction,
+ // but it's still okay to see.
+ OK = 1<<2,
+
+ // Size of right-side write, or right-side read if no write.
+ SizeB = 1<<3,
+ SizeW = 1<<4,
+ SizeL = 1<<5,
+ SizeQ = 1<<6,
+ SizeF = 1<<7, // float aka float32
+ SizeD = 1<<8, // double aka float64
+
+ // Left side: address taken, read, write.
+ LeftAddr = 1<<9,
+ LeftRead = 1<<10,
+ LeftWrite = 1<<11,
+
+ // Right side: address taken, read, write.
+ RightAddr = 1<<12,
+ RightRead = 1<<13,
+ RightWrite = 1<<14,
+
+ // Set, use, or kill of carry bit.
+ // Kill means we never look at the carry bit after this kind of instruction.
+ SetCarry = 1<<15,
+ UseCarry = 1<<16,
+ KillCarry = 1<<17,
+
+ // Instruction kinds
+ Move = 1<<18, // straight move
+ Conv = 1<<19, // size conversion
+ Cjmp = 1<<20, // conditional jump
+ Break = 1<<21, // breaks control flow (no fallthrough)
+ Call = 1<<22, // function call
+ Jump = 1<<23, // jump
+ Skip = 1<<24, // data instruction
+
+ // Special cases for register use.
+ ShiftCX = 1<<25, // possible shift by CX
+ ImulAXDX = 1<<26, // possible multiply into DX:AX
+};
+
+void proginfo(ProgInfo*, Prog*);
diff --git a/src/cmd/8g/peep.c b/src/cmd/8g/peep.c
index b8a1eaa08..966c0421b 100644
--- a/src/cmd/8g/peep.c
+++ b/src/cmd/8g/peep.c
@@ -35,53 +35,47 @@
#define REGEXT 0
-static void conprop(Reg *r);
-static void elimshortmov(Reg *r);
+static void conprop(Flow *r);
+static void elimshortmov(Graph*);
+static int subprop(Flow*);
+static int copyprop(Graph*, Flow*);
+static int copy1(Adr*, Adr*, Flow*, int);
+static int copyas(Adr*, Adr*);
+static int copyau(Adr*, Adr*);
+static int copysub(Adr*, Adr*, Adr*, int);
+
+static uint32 gactive;
// do we need the carry bit
static int
needc(Prog *p)
{
+ ProgInfo info;
+
while(p != P) {
- switch(p->as) {
- case AADCL:
- case ASBBL:
- case ARCRB:
- case ARCRW:
- case ARCRL:
+ proginfo(&info, p);
+ if(info.flags & UseCarry)
return 1;
- case AADDB:
- case AADDW:
- case AADDL:
- case ASUBB:
- case ASUBW:
- case ASUBL:
- case AJMP:
- case ARET:
- case ACALL:
+ if(info.flags & (SetCarry|KillCarry))
return 0;
- default:
- if(p->to.type == D_BRANCH)
- return 0;
- }
p = p->link;
}
return 0;
}
-static Reg*
-rnops(Reg *r)
+static Flow*
+rnops(Flow *r)
{
Prog *p;
- Reg *r1;
+ Flow *r1;
- if(r != R)
+ if(r != nil)
for(;;) {
p = r->prog;
if(p->as != ANOP || p->from.type != D_NONE || p->to.type != D_NONE)
break;
r1 = uniqs(r);
- if(r1 == R)
+ if(r1 == nil)
break;
r = r1;
}
@@ -89,57 +83,26 @@ rnops(Reg *r)
}
void
-peep(void)
+peep(Prog *firstp)
{
- Reg *r, *r1, *r2;
+ Flow *r, *r1;
+ Graph *g;
Prog *p, *p1;
int t;
- /*
- * complete R structure
- */
- t = 0;
- for(r=firstr; r!=R; r=r1) {
- r1 = r->link;
- if(r1 == R)
- break;
- p = r->prog->link;
- while(p != r1->prog)
- switch(p->as) {
- default:
- r2 = rega();
- r->link = r2;
- r2->link = r1;
-
- r2->prog = p;
- p->reg = r2;
-
- r2->p1 = r;
- r->s1 = r2;
- r2->s1 = r1;
- r1->p1 = r2;
-
- r = r2;
- t++;
-
- case ADATA:
- case AGLOBL:
- case ANAME:
- case ASIGNAME:
- case ALOCALS:
- case ATYPE:
- p = p->link;
- }
- }
+ g = flowstart(firstp, sizeof(Flow));
+ if(g == nil)
+ return;
+ gactive = 0;
// byte, word arithmetic elimination.
- elimshortmov(r);
+ elimshortmov(g);
// constant propagation
// find MOV $con,R followed by
// another MOV $con,R without
// setting R in the interim
- for(r=firstr; r!=R; r=r->link) {
+ for(r=g->start; r!=nil; r=r->link) {
p = r->prog;
switch(p->as) {
case ALEAL:
@@ -163,10 +126,10 @@ peep(void)
loop1:
if(debug['P'] && debug['v'])
- dumpit("loop1", firstr);
+ dumpit("loop1", g->start, 0);
t = 0;
- for(r=firstr; r!=R; r=r->link) {
+ for(r=g->start; r!=nil; r=r->link) {
p = r->prog;
switch(p->as) {
case AMOVL:
@@ -174,11 +137,11 @@ loop1:
case AMOVSD:
if(regtyp(&p->to))
if(regtyp(&p->from)) {
- if(copyprop(r)) {
+ if(copyprop(g, r)) {
excise(r);
t++;
} else
- if(subprop(r) && copyprop(r)) {
+ if(subprop(r) && copyprop(g, r)) {
excise(r);
t++;
}
@@ -191,7 +154,7 @@ loop1:
case AMOVWLSX:
if(regtyp(&p->to)) {
r1 = rnops(uniqs(r));
- if(r1 != R) {
+ if(r1 != nil) {
p1 = r1->prog;
if(p->as == p1->as && p->to.type == p1->from.type){
p1->as = AMOVL;
@@ -254,17 +217,19 @@ loop1:
// can be replaced by MOVAPD, which moves the pair of float64s
// instead of just the lower one. We only use the lower one, but
// the processor can do better if we do moves using both.
- for(r=firstr; r!=R; r=r->link) {
+ for(r=g->start; r!=nil; r=r->link) {
p = r->prog;
if(p->as == AMOVSD)
if(regtyp(&p->from))
if(regtyp(&p->to))
p->as = AMOVAPD;
}
+
+ flowend(g);
}
void
-excise(Reg *r)
+excise(Flow *r)
{
Prog *p;
@@ -279,38 +244,6 @@ excise(Reg *r)
ostats.ndelmov++;
}
-Reg*
-uniqp(Reg *r)
-{
- Reg *r1;
-
- r1 = r->p1;
- if(r1 == R) {
- r1 = r->p2;
- if(r1 == R || r1->p2link != R)
- return R;
- } else
- if(r->p2 != R)
- return R;
- return r1;
-}
-
-Reg*
-uniqs(Reg *r)
-{
- Reg *r1;
-
- r1 = r->s1;
- if(r1 == R) {
- r1 = r->s2;
- if(r1 == R)
- return R;
- } else
- if(r->s2 != R)
- return R;
- return r1;
-}
-
int
regtyp(Adr *a)
{
@@ -332,11 +265,12 @@ regtyp(Adr *a)
// can smash the entire 64-bit register without
// causing any trouble.
static void
-elimshortmov(Reg *r)
+elimshortmov(Graph *g)
{
Prog *p;
+ Flow *r;
- for(r=firstr; r!=R; r=r->link) {
+ for(r=g->start; r!=nil; r=r->link) {
p = r->prog;
if(regtyp(&p->to)) {
switch(p->as) {
@@ -431,13 +365,14 @@ elimshortmov(Reg *r)
* hopefully, then the former or latter MOV
* will be eliminated by copy propagation.
*/
-int
-subprop(Reg *r0)
+static int
+subprop(Flow *r0)
{
Prog *p;
Adr *v1, *v2;
- Reg *r;
+ Flow *r;
int t;
+ ProgInfo info;
p = r0->prog;
v1 = &p->from;
@@ -446,86 +381,25 @@ subprop(Reg *r0)
v2 = &p->to;
if(!regtyp(v2))
return 0;
- for(r=uniqp(r0); r!=R; r=uniqp(r)) {
- if(uniqs(r) == R)
+ for(r=uniqp(r0); r!=nil; r=uniqp(r)) {
+ if(debug['P'] && debug['v'])
+ print("\t? %P\n", r->prog);
+ if(uniqs(r) == nil)
break;
p = r->prog;
- switch(p->as) {
- case ACALL:
+ proginfo(&info, p);
+ if(info.flags & Call)
return 0;
- case AIMULL:
- case AIMULW:
- if(p->to.type != D_NONE)
- break;
-
- case ARCLB:
- case ARCLL:
- case ARCLW:
- case ARCRB:
- case ARCRL:
- case ARCRW:
- case AROLB:
- case AROLL:
- case AROLW:
- case ARORB:
- case ARORL:
- case ARORW:
- case ASALB:
- case ASALL:
- case ASALW:
- case ASARB:
- case ASARL:
- case ASARW:
- case ASHLB:
- case ASHLL:
- case ASHLW:
- case ASHRB:
- case ASHRL:
- case ASHRW:
- if(p->from.type == D_CONST)
- break;
-
- case ADIVB:
- case ADIVL:
- case ADIVW:
- case AIDIVB:
- case AIDIVL:
- case AIDIVW:
- case AIMULB:
- case AMULB:
- case AMULL:
- case AMULW:
-
- case AREP:
- case AREPN:
-
- case ACWD:
- case ACDQ:
-
- case ASTOSB:
- case ASTOSL:
- case AMOVSB:
- case AMOVSL:
-
- case AFMOVF:
- case AFMOVD:
- case AFMOVFP:
- case AFMOVDP:
+ if(info.reguse | info.regset)
return 0;
- case AMOVL:
- case AMOVSS:
- case AMOVSD:
- if(p->to.type == v1->type)
- goto gotit;
- break;
- }
- if(copyau(&p->from, v2) ||
- copyau(&p->to, v2))
+ if((info.flags & Move) && (info.flags & (SizeL|SizeQ|SizeF|SizeD)) && p->to.type == v1->type)
+ goto gotit;
+
+ if(copyau(&p->from, v2) || copyau(&p->to, v2))
break;
- if(copysub(&p->from, v1, v2, 0) ||
- copysub(&p->to, v1, v2, 0))
+ if(copysub(&p->from, v1, v2, 0) || copysub(&p->to, v1, v2, 0))
break;
}
return 0;
@@ -565,49 +439,48 @@ gotit:
* set v1 F=1
* set v2 return success
*/
-int
-copyprop(Reg *r0)
+static int
+copyprop(Graph *g, Flow *r0)
{
Prog *p;
Adr *v1, *v2;
- Reg *r;
+ USED(g);
p = r0->prog;
v1 = &p->from;
v2 = &p->to;
if(copyas(v1, v2))
return 1;
- for(r=firstr; r!=R; r=r->link)
- r->active = 0;
+ gactive++;
return copy1(v1, v2, r0->s1, 0);
}
-int
-copy1(Adr *v1, Adr *v2, Reg *r, int f)
+static int
+copy1(Adr *v1, Adr *v2, Flow *r, int f)
{
int t;
Prog *p;
- if(r->active) {
+ if(r->active == gactive) {
if(debug['P'])
print("act set; return 1\n");
return 1;
}
- r->active = 1;
+ r->active = gactive;
if(debug['P'])
print("copy %D->%D f=%d\n", v1, v2, f);
- for(; r != R; r = r->s1) {
+ for(; r != nil; r = r->s1) {
p = r->prog;
if(debug['P'])
print("%P", p);
- if(!f && uniqp(r) == R) {
+ if(!f && uniqp(r) == nil) {
f = 1;
if(debug['P'])
print("; merge; f=%d", f);
}
t = copyu(p, v2, A);
switch(t) {
- case 2: /* rar, cant split */
+ case 2: /* rar, can't split */
if(debug['P'])
print("; %D rar; return 0\n", v2);
return 0;
@@ -670,215 +543,10 @@ copy1(Adr *v1, Adr *v2, Reg *r, int f)
int
copyu(Prog *p, Adr *v, Adr *s)
{
+ ProgInfo info;
switch(p->as) {
-
- default:
- if(debug['P'])
- print("unknown op %A\n", p->as);
- /* SBBL; ADCL; FLD1; SAHF */
- return 2;
-
-
- case ANEGB:
- case ANEGW:
- case ANEGL:
- case ANOTB:
- case ANOTW:
- case ANOTL:
- if(copyas(&p->to, v))
- return 2;
- break;
-
- case ALEAL: /* lhs addr, rhs store */
- if(copyas(&p->from, v))
- return 2;
-
-
- case ANOP: /* rhs store */
- case AMOVL:
- case AMOVBLSX:
- case AMOVBLZX:
- case AMOVWLSX:
- case AMOVWLZX:
-
- case AMOVSS:
- case AMOVSD:
- case ACVTSD2SL:
- case ACVTSD2SS:
- case ACVTSL2SD:
- case ACVTSL2SS:
- case ACVTSS2SD:
- case ACVTSS2SL:
- case ACVTTSD2SL:
- case ACVTTSS2SL:
- if(copyas(&p->to, v)) {
- if(s != A)
- return copysub(&p->from, v, s, 1);
- if(copyau(&p->from, v))
- return 4;
- return 3;
- }
- goto caseread;
-
- case ARCLB:
- case ARCLL:
- case ARCLW:
- case ARCRB:
- case ARCRL:
- case ARCRW:
- case AROLB:
- case AROLL:
- case AROLW:
- case ARORB:
- case ARORL:
- case ARORW:
- case ASALB:
- case ASALL:
- case ASALW:
- case ASARB:
- case ASARL:
- case ASARW:
- case ASHLB:
- case ASHLL:
- case ASHLW:
- case ASHRB:
- case ASHRL:
- case ASHRW:
- if(copyas(&p->to, v))
- return 2;
- if(copyas(&p->from, v))
- if(p->from.type == D_CX)
- return 2;
- goto caseread;
-
- case AADDB: /* rhs rar */
- case AADDL:
- case AADDW:
- case AANDB:
- case AANDL:
- case AANDW:
- case ADECL:
- case ADECW:
- case AINCL:
- case AINCW:
- case ASUBB:
- case ASUBL:
- case ASUBW:
- case AORB:
- case AORL:
- case AORW:
- case AXORB:
- case AXORL:
- case AXORW:
- case AMOVB:
- case AMOVW:
-
- case AADDSD:
- case AADDSS:
- case ACMPSD:
- case ACMPSS:
- case ADIVSD:
- case ADIVSS:
- case AMAXSD:
- case AMAXSS:
- case AMINSD:
- case AMINSS:
- case AMULSD:
- case AMULSS:
- case ARCPSS:
- case ARSQRTSS:
- case ASQRTSD:
- case ASQRTSS:
- case ASUBSD:
- case ASUBSS:
- case AXORPD:
- if(copyas(&p->to, v))
- return 2;
- goto caseread;
-
- case ACMPL: /* read only */
- case ACMPW:
- case ACMPB:
-
- case ACOMISD:
- case ACOMISS:
- case AUCOMISD:
- case AUCOMISS:
- caseread:
- if(s != A) {
- if(copysub(&p->from, v, s, 1))
- return 1;
- return copysub(&p->to, v, s, 1);
- }
- if(copyau(&p->from, v))
- return 1;
- if(copyau(&p->to, v))
- return 1;
- break;
-
- case AJGE: /* no reference */
- case AJNE:
- case AJLE:
- case AJEQ:
- case AJHI:
- case AJLS:
- case AJMI:
- case AJPL:
- case AJGT:
- case AJLT:
- case AJCC:
- case AJCS:
-
- case AADJSP:
- case AWAIT:
- case ACLD:
- break;
-
- case AIMULL:
- case AIMULW:
- if(p->to.type != D_NONE) {
- if(copyas(&p->to, v))
- return 2;
- goto caseread;
- }
-
- case ADIVB:
- case ADIVL:
- case ADIVW:
- case AIDIVB:
- case AIDIVL:
- case AIDIVW:
- case AIMULB:
- case AMULB:
- case AMULL:
- case AMULW:
-
- case ACWD:
- case ACDQ:
- if(v->type == D_AX || v->type == D_DX)
- return 2;
- goto caseread;
-
- case AREP:
- case AREPN:
- if(v->type == D_CX)
- return 2;
- goto caseread;
-
- case AMOVSB:
- case AMOVSL:
- if(v->type == D_DI || v->type == D_SI)
- return 2;
- goto caseread;
-
- case ASTOSB:
- case ASTOSL:
- if(v->type == D_AX || v->type == D_DI)
- return 2;
- goto caseread;
-
- case AJMP: /* funny */
+ case AJMP:
if(s != A) {
if(copysub(&p->to, v, s, 1))
return 1;
@@ -888,12 +556,12 @@ copyu(Prog *p, Adr *v, Adr *s)
return 1;
return 0;
- case ARET: /* funny */
+ case ARET:
if(s != A)
return 1;
return 3;
- case ACALL: /* funny */
+ case ACALL:
if(REGEXT && v->type <= REGEXT && v->type > exregoffset)
return 2;
if(REGARG >= 0 && v->type == (uchar)REGARG)
@@ -910,11 +578,47 @@ copyu(Prog *p, Adr *v, Adr *s)
return 4;
return 3;
- case ATEXT: /* funny */
+ case ATEXT:
if(REGARG >= 0 && v->type == (uchar)REGARG)
return 3;
return 0;
}
+
+ proginfo(&info, p);
+
+ if((info.reguse|info.regset) & RtoB(v->type))
+ return 2;
+
+ if(info.flags & LeftAddr)
+ if(copyas(&p->from, v))
+ return 2;
+
+ if((info.flags & (RightRead|RightWrite)) == (RightRead|RightWrite))
+ if(copyas(&p->to, v))
+ return 2;
+
+ if(info.flags & RightWrite) {
+ if(copyas(&p->to, v)) {
+ if(s != A)
+ return copysub(&p->from, v, s, 1);
+ if(copyau(&p->from, v))
+ return 4;
+ return 3;
+ }
+ }
+
+ if(info.flags & (LeftAddr|LeftRead|LeftWrite|RightAddr|RightRead|RightWrite)) {
+ if(s != A) {
+ if(copysub(&p->from, v, s, 1))
+ return 1;
+ return copysub(&p->to, v, s, 1);
+ }
+ if(copyau(&p->from, v))
+ return 1;
+ if(copyau(&p->to, v))
+ return 1;
+ }
+
return 0;
}
@@ -923,7 +627,7 @@ copyu(Prog *p, Adr *v, Adr *s)
* could be set/use depending on
* semantics
*/
-int
+static int
copyas(Adr *a, Adr *v)
{
if(a->type != v->type)
@@ -936,10 +640,23 @@ copyas(Adr *a, Adr *v)
return 0;
}
+int
+sameaddr(Addr *a, Addr *v)
+{
+ if(a->type != v->type)
+ return 0;
+ if(regtyp(v))
+ return 1;
+ if(v->type == D_AUTO || v->type == D_PARAM)
+ if(v->offset == a->offset)
+ return 1;
+ return 0;
+}
+
/*
* either direct or indirect
*/
-int
+static int
copyau(Adr *a, Adr *v)
{
@@ -958,7 +675,7 @@ copyau(Adr *a, Adr *v)
* substitute s for v in a
* return failure to substitute
*/
-int
+static int
copysub(Adr *a, Adr *v, Adr *s, int f)
{
int t;
@@ -991,9 +708,9 @@ copysub(Adr *a, Adr *v, Adr *s, int f)
}
static void
-conprop(Reg *r0)
+conprop(Flow *r0)
{
- Reg *r;
+ Flow *r;
Prog *p, *p0;
int t;
Adr *v0;
@@ -1004,9 +721,9 @@ conprop(Reg *r0)
loop:
r = uniqs(r);
- if(r == R || r == r0)
+ if(r == nil || r == r0)
return;
- if(uniqp(r) == R)
+ if(uniqp(r) == nil)
return;
p = r->prog;
@@ -1034,3 +751,18 @@ loop:
break;
}
}
+
+int
+smallindir(Addr *a, Addr *reg)
+{
+ return regtyp(reg) &&
+ a->type == D_INDIR + reg->type &&
+ a->index == D_NONE &&
+ 0 <= a->offset && a->offset < 4096;
+}
+
+int
+stackaddr(Addr *a)
+{
+ return regtyp(a) && a->type == D_SP;
+}
diff --git a/src/cmd/8g/prog.c b/src/cmd/8g/prog.c
new file mode 100644
index 000000000..14f197b6a
--- /dev/null
+++ b/src/cmd/8g/prog.c
@@ -0,0 +1,340 @@
+// Copyright 2013 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 <u.h>
+#include <libc.h>
+#include "gg.h"
+#include "opt.h"
+
+// Matches real RtoB but can be used in global initializer.
+#define RtoB(r) (1<<((r)-D_AX))
+
+enum {
+ AX = RtoB(D_AX),
+ BX = RtoB(D_BX),
+ CX = RtoB(D_CX),
+ DX = RtoB(D_DX),
+ DI = RtoB(D_DI),
+ SI = RtoB(D_SI),
+
+ LeftRdwr = LeftRead | LeftWrite,
+ RightRdwr = RightRead | RightWrite,
+};
+
+#undef RtoB
+
+// This table gives the basic information about instruction
+// generated by the compiler and processed in the optimizer.
+// See opt.h for bit definitions.
+//
+// Instructions not generated need not be listed.
+// As an exception to that rule, we typically write down all the
+// size variants of an operation even if we just use a subset.
+//
+// The table is formatted for 8-space tabs.
+static ProgInfo progtable[ALAST] = {
+ [ATYPE]= {Pseudo | Skip},
+ [ATEXT]= {Pseudo},
+ [AFUNCDATA]= {Pseudo},
+ [APCDATA]= {Pseudo},
+ [AUNDEF]= {OK},
+ [AUSEFIELD]= {OK},
+ [ACHECKNIL]= {LeftRead},
+
+ // NOP is an internal no-op that also stands
+ // for USED and SET annotations, not the Intel opcode.
+ [ANOP]= {LeftRead | RightWrite},
+
+ [AADCL]= {SizeL | LeftRead | RightRdwr | SetCarry | UseCarry},
+ [AADCW]= {SizeW | LeftRead | RightRdwr | SetCarry | UseCarry},
+
+ [AADDB]= {SizeB | LeftRead | RightRdwr | SetCarry},
+ [AADDL]= {SizeL | LeftRead | RightRdwr | SetCarry},
+ [AADDW]= {SizeW | LeftRead | RightRdwr | SetCarry},
+
+ [AADDSD]= {SizeD | LeftRead | RightRdwr},
+ [AADDSS]= {SizeF | LeftRead | RightRdwr},
+
+ [AANDB]= {SizeB | LeftRead | RightRdwr | SetCarry},
+ [AANDL]= {SizeL | LeftRead | RightRdwr | SetCarry},
+ [AANDW]= {SizeW | LeftRead | RightRdwr | SetCarry},
+
+ [ACALL]= {RightAddr | Call | KillCarry},
+
+ [ACDQ]= {OK, AX, AX | DX},
+ [ACWD]= {OK, AX, AX | DX},
+
+ [ACLD]= {OK},
+ [ASTD]= {OK},
+
+ [ACMPB]= {SizeB | LeftRead | RightRead | SetCarry},
+ [ACMPL]= {SizeL | LeftRead | RightRead | SetCarry},
+ [ACMPW]= {SizeW | LeftRead | RightRead | SetCarry},
+
+ [ACOMISD]= {SizeD | LeftRead | RightRead | SetCarry},
+ [ACOMISS]= {SizeF | LeftRead | RightRead | SetCarry},
+
+ [ACVTSD2SL]= {SizeL | LeftRead | RightWrite | Conv},
+ [ACVTSD2SS]= {SizeF | LeftRead | RightWrite | Conv},
+ [ACVTSL2SD]= {SizeD | LeftRead | RightWrite | Conv},
+ [ACVTSL2SS]= {SizeF | LeftRead | RightWrite | Conv},
+ [ACVTSS2SD]= {SizeD | LeftRead | RightWrite | Conv},
+ [ACVTSS2SL]= {SizeL | LeftRead | RightWrite | Conv},
+ [ACVTTSD2SL]= {SizeL | LeftRead | RightWrite | Conv},
+ [ACVTTSS2SL]= {SizeL | LeftRead | RightWrite | Conv},
+
+ [ADECB]= {SizeB | RightRdwr},
+ [ADECL]= {SizeL | RightRdwr},
+ [ADECW]= {SizeW | RightRdwr},
+
+ [ADIVB]= {SizeB | LeftRead | SetCarry, AX, AX},
+ [ADIVL]= {SizeL | LeftRead | SetCarry, AX|DX, AX|DX},
+ [ADIVW]= {SizeW | LeftRead | SetCarry, AX|DX, AX|DX},
+
+ [ADIVSD]= {SizeD | LeftRead | RightRdwr},
+ [ADIVSS]= {SizeF | LeftRead | RightRdwr},
+
+ [AFLDCW]= {SizeW | LeftAddr},
+ [AFSTCW]= {SizeW | RightAddr},
+
+ [AFSTSW]= {SizeW | RightAddr | RightWrite},
+
+ [AFADDD]= {SizeD | LeftAddr | RightRdwr},
+ [AFADDDP]= {SizeD | LeftAddr | RightRdwr},
+ [AFADDF]= {SizeF | LeftAddr | RightRdwr},
+
+ [AFCOMD]= {SizeD | LeftAddr | RightRead},
+ [AFCOMDP]= {SizeD | LeftAddr | RightRead},
+ [AFCOMDPP]= {SizeD | LeftAddr | RightRead},
+ [AFCOMF]= {SizeF | LeftAddr | RightRead},
+ [AFCOMFP]= {SizeF | LeftAddr | RightRead},
+ [AFUCOMIP]= {SizeF | LeftAddr | RightRead},
+
+ [AFCHS]= {SizeD | RightRdwr}, // also SizeF
+
+ [AFDIVDP]= {SizeD | LeftAddr | RightRdwr},
+ [AFDIVF]= {SizeF | LeftAddr | RightRdwr},
+ [AFDIVD]= {SizeD | LeftAddr | RightRdwr},
+
+ [AFDIVRDP]= {SizeD | LeftAddr | RightRdwr},
+ [AFDIVRF]= {SizeF | LeftAddr | RightRdwr},
+ [AFDIVRD]= {SizeD | LeftAddr | RightRdwr},
+
+ [AFXCHD]= {SizeD | LeftRdwr | RightRdwr},
+
+ [AFSUBD]= {SizeD | LeftAddr | RightRdwr},
+ [AFSUBDP]= {SizeD | LeftAddr | RightRdwr},
+ [AFSUBF]= {SizeF | LeftAddr | RightRdwr},
+ [AFSUBRD]= {SizeD | LeftAddr | RightRdwr},
+ [AFSUBRDP]= {SizeD | LeftAddr | RightRdwr},
+ [AFSUBRF]= {SizeF | LeftAddr | RightRdwr},
+
+ [AFMOVD]= {SizeD | LeftAddr | RightWrite},
+ [AFMOVF]= {SizeF | LeftAddr | RightWrite},
+ [AFMOVL]= {SizeL | LeftAddr | RightWrite},
+ [AFMOVW]= {SizeW | LeftAddr | RightWrite},
+ [AFMOVV]= {SizeQ | LeftAddr | RightWrite},
+
+ [AFMOVDP]= {SizeD | LeftRead | RightAddr},
+ [AFMOVFP]= {SizeF | LeftRead | RightAddr},
+ [AFMOVLP]= {SizeL | LeftRead | RightAddr},
+ [AFMOVWP]= {SizeW | LeftRead | RightAddr},
+ [AFMOVVP]= {SizeQ | LeftRead | RightAddr},
+
+ [AFMULD]= {SizeD | LeftAddr | RightRdwr},
+ [AFMULDP]= {SizeD | LeftAddr | RightRdwr},
+ [AFMULF]= {SizeF | LeftAddr | RightRdwr},
+
+ [AIDIVB]= {SizeB | LeftRead | SetCarry, AX, AX},
+ [AIDIVL]= {SizeL | LeftRead | SetCarry, AX|DX, AX|DX},
+ [AIDIVW]= {SizeW | LeftRead | SetCarry, AX|DX, AX|DX},
+
+ [AIMULB]= {SizeB | LeftRead | SetCarry, AX, AX},
+ [AIMULL]= {SizeL | LeftRead | ImulAXDX | SetCarry},
+ [AIMULW]= {SizeW | LeftRead | ImulAXDX | SetCarry},
+
+ [AINCB]= {SizeB | RightRdwr},
+ [AINCL]= {SizeL | RightRdwr},
+ [AINCW]= {SizeW | RightRdwr},
+
+ [AJCC]= {Cjmp | UseCarry},
+ [AJCS]= {Cjmp | UseCarry},
+ [AJEQ]= {Cjmp | UseCarry},
+ [AJGE]= {Cjmp | UseCarry},
+ [AJGT]= {Cjmp | UseCarry},
+ [AJHI]= {Cjmp | UseCarry},
+ [AJLE]= {Cjmp | UseCarry},
+ [AJLS]= {Cjmp | UseCarry},
+ [AJLT]= {Cjmp | UseCarry},
+ [AJMI]= {Cjmp | UseCarry},
+ [AJNE]= {Cjmp | UseCarry},
+ [AJOC]= {Cjmp | UseCarry},
+ [AJOS]= {Cjmp | UseCarry},
+ [AJPC]= {Cjmp | UseCarry},
+ [AJPL]= {Cjmp | UseCarry},
+ [AJPS]= {Cjmp | UseCarry},
+
+ [AJMP]= {Jump | Break | KillCarry},
+
+ [ALEAL]= {LeftAddr | RightWrite},
+
+ [AMOVBLSX]= {SizeL | LeftRead | RightWrite | Conv},
+ [AMOVBLZX]= {SizeL | LeftRead | RightWrite | Conv},
+ [AMOVBWSX]= {SizeW | LeftRead | RightWrite | Conv},
+ [AMOVBWZX]= {SizeW | LeftRead | RightWrite | Conv},
+ [AMOVWLSX]= {SizeL | LeftRead | RightWrite | Conv},
+ [AMOVWLZX]= {SizeL | LeftRead | RightWrite | Conv},
+
+ [AMOVB]= {SizeB | LeftRead | RightWrite | Move},
+ [AMOVL]= {SizeL | LeftRead | RightWrite | Move},
+ [AMOVW]= {SizeW | LeftRead | RightWrite | Move},
+
+ [AMOVSB]= {OK, DI|SI, DI|SI},
+ [AMOVSL]= {OK, DI|SI, DI|SI},
+ [AMOVSW]= {OK, DI|SI, DI|SI},
+
+ [AMOVSD]= {SizeD | LeftRead | RightWrite | Move},
+ [AMOVSS]= {SizeF | LeftRead | RightWrite | Move},
+
+ // We use MOVAPD as a faster synonym for MOVSD.
+ [AMOVAPD]= {SizeD | LeftRead | RightWrite | Move},
+
+ [AMULB]= {SizeB | LeftRead | SetCarry, AX, AX},
+ [AMULL]= {SizeL | LeftRead | SetCarry, AX, AX|DX},
+ [AMULW]= {SizeW | LeftRead | SetCarry, AX, AX|DX},
+
+ [AMULSD]= {SizeD | LeftRead | RightRdwr},
+ [AMULSS]= {SizeF | LeftRead | RightRdwr},
+
+ [ANEGB]= {SizeB | RightRdwr | SetCarry},
+ [ANEGL]= {SizeL | RightRdwr | SetCarry},
+ [ANEGW]= {SizeW | RightRdwr | SetCarry},
+
+ [ANOTB]= {SizeB | RightRdwr},
+ [ANOTL]= {SizeL | RightRdwr},
+ [ANOTW]= {SizeW | RightRdwr},
+
+ [AORB]= {SizeB | LeftRead | RightRdwr | SetCarry},
+ [AORL]= {SizeL | LeftRead | RightRdwr | SetCarry},
+ [AORW]= {SizeW | LeftRead | RightRdwr | SetCarry},
+
+ [APOPL]= {SizeL | RightWrite},
+ [APUSHL]= {SizeL | LeftRead},
+
+ [ARCLB]= {SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry | UseCarry},
+ [ARCLL]= {SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry | UseCarry},
+ [ARCLW]= {SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry | UseCarry},
+
+ [ARCRB]= {SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry | UseCarry},
+ [ARCRL]= {SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry | UseCarry},
+ [ARCRW]= {SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry | UseCarry},
+
+ [AREP]= {OK, CX, CX},
+ [AREPN]= {OK, CX, CX},
+
+ [ARET]= {Break | KillCarry},
+
+ [AROLB]= {SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry},
+ [AROLL]= {SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry},
+ [AROLW]= {SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry},
+
+ [ARORB]= {SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry},
+ [ARORL]= {SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry},
+ [ARORW]= {SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry},
+
+ [ASAHF]= {OK, AX, AX},
+
+ [ASALB]= {SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry},
+ [ASALL]= {SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry},
+ [ASALW]= {SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry},
+
+ [ASARB]= {SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry},
+ [ASARL]= {SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry},
+ [ASARW]= {SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry},
+
+ [ASBBB]= {SizeB | LeftRead | RightRdwr | SetCarry | UseCarry},
+ [ASBBL]= {SizeL | LeftRead | RightRdwr | SetCarry | UseCarry},
+ [ASBBW]= {SizeW | LeftRead | RightRdwr | SetCarry | UseCarry},
+
+ [ASETCC]= {SizeB | RightRdwr | UseCarry},
+ [ASETCS]= {SizeB | RightRdwr | UseCarry},
+ [ASETEQ]= {SizeB | RightRdwr | UseCarry},
+ [ASETGE]= {SizeB | RightRdwr | UseCarry},
+ [ASETGT]= {SizeB | RightRdwr | UseCarry},
+ [ASETHI]= {SizeB | RightRdwr | UseCarry},
+ [ASETLE]= {SizeB | RightRdwr | UseCarry},
+ [ASETLS]= {SizeB | RightRdwr | UseCarry},
+ [ASETLT]= {SizeB | RightRdwr | UseCarry},
+ [ASETMI]= {SizeB | RightRdwr | UseCarry},
+ [ASETNE]= {SizeB | RightRdwr | UseCarry},
+ [ASETOC]= {SizeB | RightRdwr | UseCarry},
+ [ASETOS]= {SizeB | RightRdwr | UseCarry},
+ [ASETPC]= {SizeB | RightRdwr | UseCarry},
+ [ASETPL]= {SizeB | RightRdwr | UseCarry},
+ [ASETPS]= {SizeB | RightRdwr | UseCarry},
+
+ [ASHLB]= {SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry},
+ [ASHLL]= {SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry},
+ [ASHLW]= {SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry},
+
+ [ASHRB]= {SizeB | LeftRead | RightRdwr | ShiftCX | SetCarry},
+ [ASHRL]= {SizeL | LeftRead | RightRdwr | ShiftCX | SetCarry},
+ [ASHRW]= {SizeW | LeftRead | RightRdwr | ShiftCX | SetCarry},
+
+ [ASTOSB]= {OK, AX|DI, DI},
+ [ASTOSL]= {OK, AX|DI, DI},
+ [ASTOSW]= {OK, AX|DI, DI},
+
+ [ASUBB]= {SizeB | LeftRead | RightRdwr | SetCarry},
+ [ASUBL]= {SizeL | LeftRead | RightRdwr | SetCarry},
+ [ASUBW]= {SizeW | LeftRead | RightRdwr | SetCarry},
+
+ [ASUBSD]= {SizeD | LeftRead | RightRdwr},
+ [ASUBSS]= {SizeF | LeftRead | RightRdwr},
+
+ [ATESTB]= {SizeB | LeftRead | RightRead | SetCarry},
+ [ATESTL]= {SizeL | LeftRead | RightRead | SetCarry},
+ [ATESTW]= {SizeW | LeftRead | RightRead | SetCarry},
+
+ [AUCOMISD]= {SizeD | LeftRead | RightRead},
+ [AUCOMISS]= {SizeF | LeftRead | RightRead},
+
+ [AXCHGB]= {SizeB | LeftRdwr | RightRdwr},
+ [AXCHGL]= {SizeL | LeftRdwr | RightRdwr},
+ [AXCHGW]= {SizeW | LeftRdwr | RightRdwr},
+
+ [AXORB]= {SizeB | LeftRead | RightRdwr | SetCarry},
+ [AXORL]= {SizeL | LeftRead | RightRdwr | SetCarry},
+ [AXORW]= {SizeW | LeftRead | RightRdwr | SetCarry},
+};
+
+void
+proginfo(ProgInfo *info, Prog *p)
+{
+ *info = progtable[p->as];
+ if(info->flags == 0)
+ fatal("unknown instruction %P", p);
+
+ if((info->flags & ShiftCX) && p->from.type != D_CONST)
+ info->reguse |= CX;
+
+ if(info->flags & ImulAXDX) {
+ if(p->to.type == D_NONE) {
+ info->reguse |= AX;
+ info->regset |= AX | DX;
+ } else {
+ info->flags |= RightRdwr;
+ }
+ }
+
+ // Addressing makes some registers used.
+ if(p->from.type >= D_INDIR)
+ info->regindex |= RtoB(p->from.type-D_INDIR);
+ if(p->from.index != D_NONE)
+ info->regindex |= RtoB(p->from.index);
+ if(p->to.type >= D_INDIR)
+ info->regindex |= RtoB(p->to.type-D_INDIR);
+ if(p->to.index != D_NONE)
+ info->regindex |= RtoB(p->to.index);
+}
diff --git a/src/cmd/8g/reg.c b/src/cmd/8g/reg.c
index 985f6ccbc..a85c6608a 100644
--- a/src/cmd/8g/reg.c
+++ b/src/cmd/8g/reg.c
@@ -35,28 +35,10 @@
#define NREGVAR 16 /* 8 integer + 8 floating */
#define REGBITS ((uint32)0xffff)
-#define P2R(p) (Reg*)(p->reg)
+static Reg* firstr;
static int first = 1;
-static void fixjmp(Prog*);
-static void fixtemp(Prog*);
-
-Reg*
-rega(void)
-{
- Reg *r;
-
- r = freer;
- if(r == R) {
- r = mal(sizeof(*r));
- } else
- freer = r->link;
-
- *r = zreg;
- return r;
-}
-
int
rcmp(const void *a1, const void *a2)
{
@@ -131,7 +113,9 @@ regopt(Prog *firstp)
{
Reg *r, *r1;
Prog *p;
- int i, z, nr;
+ Graph *g;
+ ProgInfo info;
+ int i, z;
uint32 vreg;
Bits bit;
@@ -141,22 +125,9 @@ regopt(Prog *firstp)
first = 0;
}
- fixtemp(firstp);
fixjmp(firstp);
+ mergetemp(firstp);
- // count instructions
- nr = 0;
- for(p=firstp; p!=P; p=p->link)
- nr++;
- // if too big dont bother
- if(nr >= 10000) {
-// print("********** %S is too big (%d)\n", curfn->nname->sym, nr);
- return;
- }
-
- firstr = R;
- lastr = R;
-
/*
* control flow is more complicated in generated go code
* than in generated c code. define pseudo-variables for
@@ -188,342 +159,44 @@ regopt(Prog *firstp)
* allocate pcs
* find use and set of variables
*/
- nr = 0;
- for(p=firstp; p!=P; p=p->link) {
- switch(p->as) {
- case ADATA:
- case AGLOBL:
- case ANAME:
- case ASIGNAME:
- case ALOCALS:
- case ATYPE:
- continue;
- }
- r = rega();
- nr++;
- if(firstr == R) {
- firstr = r;
- lastr = r;
- } else {
- lastr->link = r;
- r->p1 = lastr;
- lastr->s1 = r;
- lastr = r;
- }
- r->prog = p;
- p->reg = r;
-
- r1 = r->p1;
- if(r1 != R) {
- switch(r1->prog->as) {
- case ARET:
- case AJMP:
- case AIRETL:
- r->p1 = R;
- r1->s1 = R;
- }
- }
+ g = flowstart(firstp, sizeof(Reg));
+ if(g == nil)
+ return;
+ firstr = (Reg*)g->start;
+
+ for(r = firstr; r != R; r = (Reg*)r->f.link) {
+ p = r->f.prog;
+ proginfo(&info, p);
// Avoid making variables for direct-called functions.
if(p->as == ACALL && p->to.type == D_EXTERN)
continue;
- // Addressing makes some registers used.
- if(p->from.type >= D_INDIR)
- r->use1.b[0] |= RtoB(p->from.type-D_INDIR);
- if(p->from.index != D_NONE)
- r->use1.b[0] |= RtoB(p->from.index);
- if(p->to.type >= D_INDIR)
- r->use2.b[0] |= RtoB(p->to.type-D_INDIR);
- if(p->to.index != D_NONE)
- r->use2.b[0] |= RtoB(p->to.index);
+ r->use1.b[0] |= info.reguse | info.regindex;
+ r->set.b[0] |= info.regset;
bit = mkvar(r, &p->from);
- if(bany(&bit))
- switch(p->as) {
- /*
- * funny
- */
- case ALEAL:
- case AFMOVD:
- case AFMOVF:
- case AFMOVL:
- case AFMOVW:
- case AFMOVV:
- setaddrs(bit);
- break;
-
- /*
- * left side read
- */
- default:
- for(z=0; z<BITS; z++)
- r->use1.b[z] |= bit.b[z];
- break;
-
- /*
- * left side read+write
- */
- case AXCHGB:
- case AXCHGW:
- case AXCHGL:
- for(z=0; z<BITS; z++) {
- r->use1.b[z] |= bit.b[z];
- r->set.b[z] |= bit.b[z];
- }
- break;
+ if(bany(&bit)) {
+ if(info.flags & LeftAddr)
+ setaddrs(bit);
+ if(info.flags & LeftRead)
+ for(z=0; z<BITS; z++)
+ r->use1.b[z] |= bit.b[z];
+ if(info.flags & LeftWrite)
+ for(z=0; z<BITS; z++)
+ r->set.b[z] |= bit.b[z];
}
bit = mkvar(r, &p->to);
- if(bany(&bit))
- switch(p->as) {
- default:
- yyerror("reg: unknown op: %A", p->as);
- break;
-
- /*
- * right side read
- */
- case ACMPB:
- case ACMPL:
- case ACMPW:
- case ACOMISS:
- case ACOMISD:
- case AUCOMISS:
- case AUCOMISD:
- case ATESTB:
- case ATESTL:
- case ATESTW:
- for(z=0; z<BITS; z++)
- r->use2.b[z] |= bit.b[z];
- break;
-
- /*
- * right side write
- */
- case AFSTSW:
- case ALEAL:
- case ANOP:
- case AMOVL:
- case AMOVB:
- case AMOVW:
- case AMOVBLSX:
- case AMOVBLZX:
- case AMOVBWSX:
- case AMOVBWZX:
- case AMOVWLSX:
- case AMOVWLZX:
- case APOPL:
-
- case AMOVSS:
- case AMOVSD:
- case ACVTSD2SL:
- case ACVTSD2SS:
- case ACVTSL2SD:
- case ACVTSL2SS:
- case ACVTSS2SD:
- case ACVTSS2SL:
- case ACVTTSD2SL:
- case ACVTTSS2SL:
- for(z=0; z<BITS; z++)
- r->set.b[z] |= bit.b[z];
- break;
-
- /*
- * right side read+write
- */
- case AINCB:
- case AINCL:
- case AINCW:
- case ADECB:
- case ADECL:
- case ADECW:
-
- case AADDB:
- case AADDL:
- case AADDW:
- case AANDB:
- case AANDL:
- case AANDW:
- case ASUBB:
- case ASUBL:
- case ASUBW:
- case AORB:
- case AORL:
- case AORW:
- case AXORB:
- case AXORL:
- case AXORW:
- case ASALB:
- case ASALL:
- case ASALW:
- case ASARB:
- case ASARL:
- case ASARW:
- case ARCLB:
- case ARCLL:
- case ARCLW:
- case ARCRB:
- case ARCRL:
- case ARCRW:
- case AROLB:
- case AROLL:
- case AROLW:
- case ARORB:
- case ARORL:
- case ARORW:
- case ASHLB:
- case ASHLL:
- case ASHLW:
- case ASHRB:
- case ASHRL:
- case ASHRW:
- case AIMULL:
- case AIMULW:
- case ANEGB:
- case ANEGL:
- case ANEGW:
- case ANOTB:
- case ANOTL:
- case ANOTW:
- case AADCL:
- case ASBBL:
-
- case ASETCC:
- case ASETCS:
- case ASETEQ:
- case ASETGE:
- case ASETGT:
- case ASETHI:
- case ASETLE:
- case ASETLS:
- case ASETLT:
- case ASETMI:
- case ASETNE:
- case ASETOC:
- case ASETOS:
- case ASETPC:
- case ASETPL:
- case ASETPS:
-
- case AXCHGB:
- case AXCHGW:
- case AXCHGL:
-
- case AADDSD:
- case AADDSS:
- case ACMPSD:
- case ACMPSS:
- case ADIVSD:
- case ADIVSS:
- case AMAXSD:
- case AMAXSS:
- case AMINSD:
- case AMINSS:
- case AMULSD:
- case AMULSS:
- case ARCPSS:
- case ARSQRTSS:
- case ASQRTSD:
- case ASQRTSS:
- case ASUBSD:
- case ASUBSS:
- case AXORPD:
- for(z=0; z<BITS; z++) {
- r->set.b[z] |= bit.b[z];
- r->use2.b[z] |= bit.b[z];
- }
- break;
-
- /*
- * funny
- */
- case AFMOVDP:
- case AFMOVFP:
- case AFMOVLP:
- case AFMOVVP:
- case AFMOVWP:
- case ACALL:
- setaddrs(bit);
- break;
- }
-
- switch(p->as) {
- case AIMULL:
- case AIMULW:
- if(p->to.type != D_NONE)
- break;
-
- case AIDIVL:
- case AIDIVW:
- case ADIVL:
- case ADIVW:
- case AMULL:
- case AMULW:
- r->set.b[0] |= RtoB(D_AX) | RtoB(D_DX);
- r->use1.b[0] |= RtoB(D_AX) | RtoB(D_DX);
- break;
-
- case AIDIVB:
- case AIMULB:
- case ADIVB:
- case AMULB:
- r->set.b[0] |= RtoB(D_AX);
- r->use1.b[0] |= RtoB(D_AX);
- break;
-
- case ACWD:
- r->set.b[0] |= RtoB(D_AX) | RtoB(D_DX);
- r->use1.b[0] |= RtoB(D_AX);
- break;
-
- case ACDQ:
- r->set.b[0] |= RtoB(D_DX);
- r->use1.b[0] |= RtoB(D_AX);
- break;
-
- case AREP:
- case AREPN:
- case ALOOP:
- case ALOOPEQ:
- case ALOOPNE:
- r->set.b[0] |= RtoB(D_CX);
- r->use1.b[0] |= RtoB(D_CX);
- break;
-
- case AMOVSB:
- case AMOVSL:
- case AMOVSW:
- case ACMPSB:
- case ACMPSL:
- case ACMPSW:
- r->set.b[0] |= RtoB(D_SI) | RtoB(D_DI);
- r->use1.b[0] |= RtoB(D_SI) | RtoB(D_DI);
- break;
-
- case ASTOSB:
- case ASTOSL:
- case ASTOSW:
- case ASCASB:
- case ASCASL:
- case ASCASW:
- r->set.b[0] |= RtoB(D_DI);
- r->use1.b[0] |= RtoB(D_AX) | RtoB(D_DI);
- break;
-
- case AINSB:
- case AINSL:
- case AINSW:
- r->set.b[0] |= RtoB(D_DX) | RtoB(D_DI);
- r->use1.b[0] |= RtoB(D_DI);
- break;
-
- case AOUTSB:
- case AOUTSL:
- case AOUTSW:
- r->set.b[0] |= RtoB(D_DI);
- r->use1.b[0] |= RtoB(D_DX) | RtoB(D_DI);
- break;
+ if(bany(&bit)) {
+ if(info.flags & RightAddr)
+ setaddrs(bit);
+ if(info.flags & RightRead)
+ for(z=0; z<BITS; z++)
+ r->use2.b[z] |= bit.b[z];
+ if(info.flags & RightWrite)
+ for(z=0; z<BITS; z++)
+ r->set.b[z] |= bit.b[z];
}
}
if(firstr == R)
@@ -543,45 +216,16 @@ regopt(Prog *firstp)
}
if(debug['R'] && debug['v'])
- dumpit("pass1", firstr);
+ dumpit("pass1", &firstr->f, 1);
/*
* pass 2
- * turn branch references to pointers
- * build back pointers
- */
- for(r=firstr; r!=R; r=r->link) {
- p = r->prog;
- if(p->to.type == D_BRANCH) {
- if(p->to.u.branch == P)
- fatal("pnil %P", p);
- r1 = p->to.u.branch->reg;
- if(r1 == R)
- fatal("rnil %P", p);
- if(r1 == r) {
- //fatal("ref to self %P", p);
- continue;
- }
- r->s2 = r1;
- r->p2link = r1->p2;
- r1->p2 = r;
- }
- }
-
- if(debug['R'] && debug['v'])
- dumpit("pass2", firstr);
-
- /*
- * pass 2.5
* find looping structure
*/
- for(r = firstr; r != R; r = r->link)
- r->active = 0;
- change = 0;
- loopit(firstr, nr);
+ flowrpo(g);
if(debug['R'] && debug['v'])
- dumpit("pass2.5", firstr);
+ dumpit("pass2", &firstr->f, 1);
/*
* pass 3
@@ -590,17 +234,17 @@ regopt(Prog *firstp)
*/
loop1:
change = 0;
- for(r = firstr; r != R; r = r->link)
- r->active = 0;
- for(r = firstr; r != R; r = r->link)
- if(r->prog->as == ARET)
+ for(r = firstr; r != R; r = (Reg*)r->f.link)
+ r->f.active = 0;
+ for(r = firstr; r != R; r = (Reg*)r->f.link)
+ if(r->f.prog->as == ARET)
prop(r, zbits, zbits);
loop11:
/* pick up unreachable code */
i = 0;
for(r = firstr; r != R; r = r1) {
- r1 = r->link;
- if(r1 && r1->active && !r->active) {
+ r1 = (Reg*)r->f.link;
+ if(r1 && r1->f.active && !r->f.active) {
prop(r, zbits, zbits);
i = 1;
}
@@ -611,7 +255,7 @@ loop11:
goto loop1;
if(debug['R'] && debug['v'])
- dumpit("pass3", firstr);
+ dumpit("pass3", &firstr->f, 1);
/*
* pass 4
@@ -620,20 +264,20 @@ loop11:
*/
loop2:
change = 0;
- for(r = firstr; r != R; r = r->link)
- r->active = 0;
+ for(r = firstr; r != R; r = (Reg*)r->f.link)
+ r->f.active = 0;
synch(firstr, zbits);
if(change)
goto loop2;
if(debug['R'] && debug['v'])
- dumpit("pass4", firstr);
+ dumpit("pass4", &firstr->f, 1);
/*
* pass 4.5
* move register pseudo-variables into regu.
*/
- for(r = firstr; r != R; r = r->link) {
+ for(r = firstr; r != R; r = (Reg*)r->f.link) {
r->regu = (r->refbehind.b[0] | r->set.b[0]) & REGBITS;
r->set.b[0] &= ~REGBITS;
@@ -657,26 +301,26 @@ loop2:
for(z=0; z<BITS; z++)
bit.b[z] = (r->refahead.b[z] | r->calahead.b[z]) &
~(externs.b[z] | params.b[z] | addrs.b[z] | consts.b[z]);
- if(bany(&bit) && !r->refset) {
+ if(bany(&bit) && !r->f.refset) {
// should never happen - all variables are preset
if(debug['w'])
- print("%L: used and not set: %Q\n", r->prog->lineno, bit);
- r->refset = 1;
+ print("%L: used and not set: %Q\n", r->f.prog->lineno, bit);
+ r->f.refset = 1;
}
}
- for(r = firstr; r != R; r = r->link)
+ for(r = firstr; r != R; r = (Reg*)r->f.link)
r->act = zbits;
rgp = region;
nregion = 0;
- for(r = firstr; r != R; r = r->link) {
+ for(r = firstr; r != R; r = (Reg*)r->f.link) {
for(z=0; z<BITS; z++)
bit.b[z] = r->set.b[z] &
~(r->refahead.b[z] | r->calahead.b[z] | addrs.b[z]);
- if(bany(&bit) && !r->refset) {
+ if(bany(&bit) && !r->f.refset) {
if(debug['w'])
- print("%L: set and not used: %Q\n", r->prog->lineno, bit);
- r->refset = 1;
- excise(r);
+ print("%L: set and not used: %Q\n", r->f.prog->lineno, bit);
+ r->f.refset = 1;
+ excise(&r->f);
}
for(z=0; z<BITS; z++)
bit.b[z] = LOAD(r) & ~(r->act.b[z] | addrs.b[z]);
@@ -718,19 +362,23 @@ brk:
}
if(debug['R'] && debug['v'])
- dumpit("pass6", firstr);
+ dumpit("pass6", &firstr->f, 1);
+
+ /*
+ * free aux structures. peep allocates new ones.
+ */
+ flowend(g);
+ firstr = R;
/*
* pass 7
* peep-hole on basic block
*/
- if(!debug['R'] || debug['P']) {
- peep();
- }
+ if(!debug['R'] || debug['P'])
+ peep(firstp);
/*
* eliminate nops
- * free aux structures
*/
for(p=firstp; p!=P; p=p->link) {
while(p->link != P && p->link->as == ANOP)
@@ -748,11 +396,6 @@ brk:
fatal("invalid use of %R with GO386=387: %P", p->to.type, p);
}
- if(lastr != R) {
- lastr->link = freer;
- freer = firstr;
- }
-
if(debug['R']) {
if(ostats.ncvtreg ||
ostats.nspill ||
@@ -795,7 +438,7 @@ addmove(Reg *r, int bn, int rn, int f)
clearp(p1);
p1->loc = 9999;
- p = r->prog;
+ p = r->f.prog;
p1->link = p->link;
p->link = p1;
p1->lineno = p->lineno;
@@ -1012,7 +655,7 @@ prop(Reg *r, Bits ref, Bits cal)
Reg *r1, *r2;
int z;
- for(r1 = r; r1 != R; r1 = r1->p1) {
+ for(r1 = r; r1 != R; r1 = (Reg*)r1->f.p1) {
for(z=0; z<BITS; z++) {
ref.b[z] |= r1->refahead.b[z];
if(ref.b[z] != r1->refahead.b[z]) {
@@ -1025,9 +668,9 @@ prop(Reg *r, Bits ref, Bits cal)
change++;
}
}
- switch(r1->prog->as) {
+ switch(r1->f.prog->as) {
case ACALL:
- if(noreturn(r1->prog))
+ if(noreturn(r1->f.prog))
break;
for(z=0; z<BITS; z++) {
cal.b[z] |= ref.b[z] | externs.b[z];
@@ -1067,159 +710,22 @@ prop(Reg *r, Bits ref, Bits cal)
r1->refbehind.b[z] = ref.b[z];
r1->calbehind.b[z] = cal.b[z];
}
- if(r1->active)
+ if(r1->f.active)
break;
- r1->active = 1;
+ r1->f.active = 1;
}
- for(; r != r1; r = r->p1)
- for(r2 = r->p2; r2 != R; r2 = r2->p2link)
+ for(; r != r1; r = (Reg*)r->f.p1)
+ for(r2 = (Reg*)r->f.p2; r2 != R; r2 = (Reg*)r2->f.p2link)
prop(r2, r->refbehind, r->calbehind);
}
-/*
- * find looping structure
- *
- * 1) find reverse postordering
- * 2) find approximate dominators,
- * the actual dominators if the flow graph is reducible
- * otherwise, dominators plus some other non-dominators.
- * See Matthew S. Hecht and Jeffrey D. Ullman,
- * "Analysis of a Simple Algorithm for Global Data Flow Problems",
- * Conf. Record of ACM Symp. on Principles of Prog. Langs, Boston, Massachusetts,
- * Oct. 1-3, 1973, pp. 207-217.
- * 3) find all nodes with a predecessor dominated by the current node.
- * such a node is a loop head.
- * recursively, all preds with a greater rpo number are in the loop
- */
-int32
-postorder(Reg *r, Reg **rpo2r, int32 n)
-{
- Reg *r1;
-
- r->rpo = 1;
- r1 = r->s1;
- if(r1 && !r1->rpo)
- n = postorder(r1, rpo2r, n);
- r1 = r->s2;
- if(r1 && !r1->rpo)
- n = postorder(r1, rpo2r, n);
- rpo2r[n] = r;
- n++;
- return n;
-}
-
-int32
-rpolca(int32 *idom, int32 rpo1, int32 rpo2)
-{
- int32 t;
-
- if(rpo1 == -1)
- return rpo2;
- while(rpo1 != rpo2){
- if(rpo1 > rpo2){
- t = rpo2;
- rpo2 = rpo1;
- rpo1 = t;
- }
- while(rpo1 < rpo2){
- t = idom[rpo2];
- if(t >= rpo2)
- fatal("bad idom");
- rpo2 = t;
- }
- }
- return rpo1;
-}
-
-int
-doms(int32 *idom, int32 r, int32 s)
-{
- while(s > r)
- s = idom[s];
- return s == r;
-}
-
-int
-loophead(int32 *idom, Reg *r)
-{
- int32 src;
-
- src = r->rpo;
- if(r->p1 != R && doms(idom, src, r->p1->rpo))
- return 1;
- for(r = r->p2; r != R; r = r->p2link)
- if(doms(idom, src, r->rpo))
- return 1;
- return 0;
-}
-
-void
-loopmark(Reg **rpo2r, int32 head, Reg *r)
-{
- if(r->rpo < head || r->active == head)
- return;
- r->active = head;
- r->loop += LOOP;
- if(r->p1 != R)
- loopmark(rpo2r, head, r->p1);
- for(r = r->p2; r != R; r = r->p2link)
- loopmark(rpo2r, head, r);
-}
-
-void
-loopit(Reg *r, int32 nr)
-{
- Reg *r1;
- int32 i, d, me;
-
- if(nr > maxnr) {
- rpo2r = mal(nr * sizeof(Reg*));
- idom = mal(nr * sizeof(int32));
- maxnr = nr;
- }
-
- d = postorder(r, rpo2r, 0);
- if(d > nr)
- fatal("too many reg nodes %d %d", d, nr);
- nr = d;
- for(i = 0; i < nr / 2; i++) {
- r1 = rpo2r[i];
- rpo2r[i] = rpo2r[nr - 1 - i];
- rpo2r[nr - 1 - i] = r1;
- }
- for(i = 0; i < nr; i++)
- rpo2r[i]->rpo = i;
-
- idom[0] = 0;
- for(i = 0; i < nr; i++) {
- r1 = rpo2r[i];
- me = r1->rpo;
- d = -1;
- // rpo2r[r->rpo] == r protects against considering dead code,
- // which has r->rpo == 0.
- if(r1->p1 != R && rpo2r[r1->p1->rpo] == r1->p1 && r1->p1->rpo < me)
- d = r1->p1->rpo;
- for(r1 = r1->p2; r1 != nil; r1 = r1->p2link)
- if(rpo2r[r1->rpo] == r1 && r1->rpo < me)
- d = rpolca(idom, d, r1->rpo);
- idom[i] = d;
- }
-
- for(i = 0; i < nr; i++) {
- r1 = rpo2r[i];
- r1->loop++;
- if(r1->p2 != R && loophead(idom, r1))
- loopmark(rpo2r, i, r1);
- }
-}
-
void
synch(Reg *r, Bits dif)
{
Reg *r1;
int z;
- for(r1 = r; r1 != R; r1 = r1->s1) {
+ for(r1 = r; r1 != R; r1 = (Reg*)r1->f.s1) {
for(z=0; z<BITS; z++) {
dif.b[z] = (dif.b[z] &
~(~r1->refbehind.b[z] & r1->refahead.b[z])) |
@@ -1229,13 +735,13 @@ synch(Reg *r, Bits dif)
change++;
}
}
- if(r1->active)
+ if(r1->f.active)
break;
- r1->active = 1;
+ r1->f.active = 1;
for(z=0; z<BITS; z++)
dif.b[z] &= ~(~r1->calbehind.b[z] & r1->calahead.b[z]);
- if(r1->s2 != R)
- synch(r1->s2, dif);
+ if((Reg*)r1->f.s2 != R)
+ synch((Reg*)r1->f.s2, dif);
}
}
@@ -1301,7 +807,7 @@ paint1(Reg *r, int bn)
for(;;) {
if(!(r->refbehind.b[z] & bb))
break;
- r1 = r->p1;
+ r1 = (Reg*)r->f.p1;
if(r1 == R)
break;
if(!(r1->refahead.b[z] & bb))
@@ -1312,45 +818,45 @@ paint1(Reg *r, int bn)
}
if(LOAD(r) & ~(r->set.b[z]&~(r->use1.b[z]|r->use2.b[z])) & bb) {
- change -= CLOAD * r->loop;
+ change -= CLOAD * r->f.loop;
}
for(;;) {
r->act.b[z] |= bb;
- p = r->prog;
+ p = r->f.prog;
if(r->use1.b[z] & bb) {
- change += CREF * r->loop;
+ change += CREF * r->f.loop;
if(p->as == AFMOVL || p->as == AFMOVW)
if(BtoR(bb) != D_F0)
change = -CINF;
}
if((r->use2.b[z]|r->set.b[z]) & bb) {
- change += CREF * r->loop;
+ change += CREF * r->f.loop;
if(p->as == AFMOVL || p->as == AFMOVW)
if(BtoR(bb) != D_F0)
change = -CINF;
}
if(STORE(r) & r->regdiff.b[z] & bb) {
- change -= CLOAD * r->loop;
+ change -= CLOAD * r->f.loop;
if(p->as == AFMOVL || p->as == AFMOVW)
if(BtoR(bb) != D_F0)
change = -CINF;
}
if(r->refbehind.b[z] & bb)
- for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+ for(r1 = (Reg*)r->f.p2; r1 != R; r1 = (Reg*)r1->f.p2link)
if(r1->refahead.b[z] & bb)
paint1(r1, bn);
if(!(r->refahead.b[z] & bb))
break;
- r1 = r->s2;
+ r1 = (Reg*)r->f.s2;
if(r1 != R)
if(r1->refbehind.b[z] & bb)
paint1(r1, bn);
- r = r->s1;
+ r = (Reg*)r->f.s1;
if(r == R)
break;
if(r->act.b[z] & bb)
@@ -1371,7 +877,7 @@ regset(Reg *r, uint32 bb)
v = zprog.from;
while(b = bb & ~(bb-1)) {
v.type = b & 0xFF ? BtoR(b): BtoF(b);
- c = copyu(r->prog, &v, A);
+ c = copyu(r->f.prog, &v, A);
if(c == 3)
set |= b;
bb &= ~b;
@@ -1390,7 +896,7 @@ reguse(Reg *r, uint32 bb)
v = zprog.from;
while(b = bb & ~(bb-1)) {
v.type = b & 0xFF ? BtoR(b): BtoF(b);
- c = copyu(r->prog, &v, A);
+ c = copyu(r->f.prog, &v, A);
if(c == 1 || c == 2 || c == 4)
set |= b;
bb &= ~b;
@@ -1413,7 +919,7 @@ paint2(Reg *r, int bn)
for(;;) {
if(!(r->refbehind.b[z] & bb))
break;
- r1 = r->p1;
+ r1 = (Reg*)r->f.p1;
if(r1 == R)
break;
if(!(r1->refahead.b[z] & bb))
@@ -1428,17 +934,17 @@ paint2(Reg *r, int bn)
vreg |= r->regu;
if(r->refbehind.b[z] & bb)
- for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+ for(r1 = (Reg*)r->f.p2; r1 != R; r1 = (Reg*)r1->f.p2link)
if(r1->refahead.b[z] & bb)
vreg |= paint2(r1, bn);
if(!(r->refahead.b[z] & bb))
break;
- r1 = r->s2;
+ r1 = (Reg*)r->f.s2;
if(r1 != R)
if(r1->refbehind.b[z] & bb)
vreg |= paint2(r1, bn);
- r = r->s1;
+ r = (Reg*)r->f.s1;
if(r == R)
break;
if(!(r->act.b[z] & bb))
@@ -1448,7 +954,7 @@ paint2(Reg *r, int bn)
}
bb = vreg;
- for(; r; r=r->s1) {
+ for(; r; r=(Reg*)r->f.s1) {
x = r->regu & ~bb;
if(x) {
vreg |= reguse(r, x);
@@ -1473,7 +979,7 @@ paint3(Reg *r, int bn, int32 rb, int rn)
for(;;) {
if(!(r->refbehind.b[z] & bb))
break;
- r1 = r->p1;
+ r1 = (Reg*)r->f.p1;
if(r1 == R)
break;
if(!(r1->refahead.b[z] & bb))
@@ -1487,7 +993,7 @@ paint3(Reg *r, int bn, int32 rb, int rn)
addmove(r, bn, rn, 0);
for(;;) {
r->act.b[z] |= bb;
- p = r->prog;
+ p = r->f.prog;
if(r->use1.b[z] & bb) {
if(debug['R'] && debug['v'])
@@ -1509,17 +1015,17 @@ paint3(Reg *r, int bn, int32 rb, int rn)
r->regu |= rb;
if(r->refbehind.b[z] & bb)
- for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+ for(r1 = (Reg*)r->f.p2; r1 != R; r1 = (Reg*)r1->f.p2link)
if(r1->refahead.b[z] & bb)
paint3(r1, bn, rb, rn);
if(!(r->refahead.b[z] & bb))
break;
- r1 = r->s2;
+ r1 = (Reg*)r->f.s2;
if(r1 != R)
if(r1->refbehind.b[z] & bb)
paint3(r1, bn, rb, rn);
- r = r->s1;
+ r = (Reg*)r->f.s1;
if(r == R)
break;
if(r->act.b[z] & bb)
@@ -1577,65 +1083,69 @@ BtoF(int32 b)
}
void
-dumpone(Reg *r)
+dumpone(Flow *f, int isreg)
{
int z;
Bits bit;
+ Reg *r;
- print("%d:%P", r->loop, r->prog);
- for(z=0; z<BITS; z++)
- bit.b[z] =
- r->set.b[z] |
- r->use1.b[z] |
- r->use2.b[z] |
- r->refbehind.b[z] |
- r->refahead.b[z] |
- r->calbehind.b[z] |
- r->calahead.b[z] |
- r->regdiff.b[z] |
- r->act.b[z] |
- 0;
- if(bany(&bit)) {
- print("\t");
- if(bany(&r->set))
- print(" s:%Q", r->set);
- if(bany(&r->use1))
- print(" u1:%Q", r->use1);
- if(bany(&r->use2))
- print(" u2:%Q", r->use2);
- if(bany(&r->refbehind))
- print(" rb:%Q ", r->refbehind);
- if(bany(&r->refahead))
- print(" ra:%Q ", r->refahead);
- if(bany(&r->calbehind))
- print(" cb:%Q ", r->calbehind);
- if(bany(&r->calahead))
- print(" ca:%Q ", r->calahead);
- if(bany(&r->regdiff))
- print(" d:%Q ", r->regdiff);
- if(bany(&r->act))
- print(" a:%Q ", r->act);
+ print("%d:%P", f->loop, f->prog);
+ if(isreg) {
+ r = (Reg*)f;
+ for(z=0; z<BITS; z++)
+ bit.b[z] =
+ r->set.b[z] |
+ r->use1.b[z] |
+ r->use2.b[z] |
+ r->refbehind.b[z] |
+ r->refahead.b[z] |
+ r->calbehind.b[z] |
+ r->calahead.b[z] |
+ r->regdiff.b[z] |
+ r->act.b[z] |
+ 0;
+ if(bany(&bit)) {
+ print("\t");
+ if(bany(&r->set))
+ print(" s:%Q", r->set);
+ if(bany(&r->use1))
+ print(" u1:%Q", r->use1);
+ if(bany(&r->use2))
+ print(" u2:%Q", r->use2);
+ if(bany(&r->refbehind))
+ print(" rb:%Q ", r->refbehind);
+ if(bany(&r->refahead))
+ print(" ra:%Q ", r->refahead);
+ if(bany(&r->calbehind))
+ print(" cb:%Q ", r->calbehind);
+ if(bany(&r->calahead))
+ print(" ca:%Q ", r->calahead);
+ if(bany(&r->regdiff))
+ print(" d:%Q ", r->regdiff);
+ if(bany(&r->act))
+ print(" a:%Q ", r->act);
+ }
}
print("\n");
}
void
-dumpit(char *str, Reg *r0)
+dumpit(char *str, Flow *r0, int isreg)
{
- Reg *r, *r1;
+ Flow *r, *r1;
print("\n%s\n", str);
- for(r = r0; r != R; r = r->link) {
- dumpone(r);
+ for(r = r0; r != nil; r = r->link) {
+ dumpone(r, isreg);
r1 = r->p2;
- if(r1 != R) {
+ if(r1 != nil) {
print(" pred:");
- for(; r1 != R; r1 = r1->p2link)
+ for(; r1 != nil; r1 = r->p2link)
print(" %.4ud", r1->prog->loc);
print("\n");
}
// r1 = r->s1;
-// if(r1 != R) {
+// if(r1 != nil) {
// print(" succ:");
// for(; r1 != R; r1 = r1->s1)
// print(" %.4ud", r1->prog->loc);
@@ -1643,276 +1153,3 @@ dumpit(char *str, Reg *r0)
// }
}
}
-
-static Sym* symlist[10];
-
-int
-noreturn(Prog *p)
-{
- Sym *s;
- int i;
-
- if(symlist[0] == S) {
- symlist[0] = pkglookup("panicindex", runtimepkg);
- symlist[1] = pkglookup("panicslice", runtimepkg);
- symlist[2] = pkglookup("throwinit", runtimepkg);
- symlist[3] = pkglookup("panic", runtimepkg);
- symlist[4] = pkglookup("panicwrap", runtimepkg);
- }
-
- s = p->to.sym;
- if(s == S)
- return 0;
- for(i=0; symlist[i]!=S; i++)
- if(s == symlist[i])
- return 1;
- return 0;
-}
-
-/*
- * the code generator depends on being able to write out JMP
- * instructions that it can jump to now but fill in later.
- * the linker will resolve them nicely, but they make the code
- * longer and more difficult to follow during debugging.
- * remove them.
- */
-
-/* what instruction does a JMP to p eventually land on? */
-static Prog*
-chasejmp(Prog *p, int *jmploop)
-{
- int n;
-
- n = 0;
- while(p != P && p->as == AJMP && p->to.type == D_BRANCH) {
- if(++n > 10) {
- *jmploop = 1;
- break;
- }
- p = p->to.u.branch;
- }
- return p;
-}
-
-/*
- * reuse reg pointer for mark/sweep state.
- * leave reg==nil at end because alive==nil.
- */
-#define alive ((void*)0)
-#define dead ((void*)1)
-
-/* mark all code reachable from firstp as alive */
-static void
-mark(Prog *firstp)
-{
- Prog *p;
-
- for(p=firstp; p; p=p->link) {
- if(p->reg != dead)
- break;
- p->reg = alive;
- if(p->as != ACALL && p->to.type == D_BRANCH && p->to.u.branch)
- mark(p->to.u.branch);
- if(p->as == AJMP || p->as == ARET || p->as == AUNDEF)
- break;
- }
-}
-
-static void
-fixjmp(Prog *firstp)
-{
- int jmploop;
- Prog *p, *last;
-
- if(debug['R'] && debug['v'])
- print("\nfixjmp\n");
-
- // pass 1: resolve jump to AJMP, mark all code as dead.
- jmploop = 0;
- for(p=firstp; p; p=p->link) {
- if(debug['R'] && debug['v'])
- print("%P\n", p);
- if(p->as != ACALL && p->to.type == D_BRANCH && p->to.u.branch && p->to.u.branch->as == AJMP) {
- p->to.u.branch = chasejmp(p->to.u.branch, &jmploop);
- if(debug['R'] && debug['v'])
- print("->%P\n", p);
- }
- p->reg = dead;
- }
- if(debug['R'] && debug['v'])
- print("\n");
-
- // pass 2: mark all reachable code alive
- mark(firstp);
-
- // pass 3: delete dead code (mostly JMPs).
- last = nil;
- for(p=firstp; p; p=p->link) {
- if(p->reg == dead) {
- if(p->link == P && p->as == ARET && last && last->as != ARET) {
- // This is the final ARET, and the code so far doesn't have one.
- // Let it stay.
- } else {
- if(debug['R'] && debug['v'])
- print("del %P\n", p);
- continue;
- }
- }
- if(last)
- last->link = p;
- last = p;
- }
- last->link = P;
-
- // pass 4: elide JMP to next instruction.
- // only safe if there are no jumps to JMPs anymore.
- if(!jmploop) {
- last = nil;
- for(p=firstp; p; p=p->link) {
- if(p->as == AJMP && p->to.type == D_BRANCH && p->to.u.branch == p->link) {
- if(debug['R'] && debug['v'])
- print("del %P\n", p);
- continue;
- }
- if(last)
- last->link = p;
- last = p;
- }
- last->link = P;
- }
-
- if(debug['R'] && debug['v']) {
- print("\n");
- for(p=firstp; p; p=p->link)
- print("%P\n", p);
- print("\n");
- }
-}
-
-static uint32
-fnv1(Sym *sym)
-{
- uint32 h;
- char *s;
-
- h = 2166136261U;
- for(s=sym->name;*s;s++) {
- h = (16777619 * h) ^ (uint32)(uint8)(*s);
- }
- return h;
-}
-
-static uint16
-hash32to16(uint32 h)
-{
- return (h & 0xffff) ^ (h >> 16);
-}
-
-/*
- * fixtemp eliminates sequences like:
- * MOV reg1, mem
- * OP mem, reg2
- * when mem is a stack variable which is not mentioned
- * anywhere else. The instructions are replaced by
- * OP reg1, reg2
- * this reduces the number of variables that the register optimizer
- * sees, which lets it do a better job and makes it less likely to turn
- * itself off.
- */
-static void
-fixtemp(Prog *firstp)
-{
- static uint8 counts[1<<16]; // A hash table to count variable occurences.
- int i;
- Prog *p, *p2;
- uint32 h;
-
- if(debug['R'] && debug['v'])
- print("\nfixtemp\n");
-
- // Count variable references. We actually use a hashtable so this
- // is only approximate.
- for(i=0; i<nelem(counts); i++)
- counts[i] = 0;
- for(p=firstp; p!=P; p=p->link) {
- if(p->from.type == D_AUTO) {
- h = hash32to16(fnv1(p->from.sym));
- //print("seen %S hash %d\n", p->from.sym, hash32to16(h));
- if(counts[h] < 10)
- counts[h]++;
- }
- if(p->to.type == D_AUTO) {
- h = hash32to16(fnv1(p->to.sym));
- //print("seen %S hash %d\n", p->to.sym, hash32to16(h));
- if(counts[h] < 10)
- counts[h]++;
- }
- }
-
- // Eliminate single-write, single-read stack variables.
- for(p=firstp; p!=P; p=p->link) {
- if(debug['R'] && debug['v'])
- print("%P\n", p);
- if(p->link == P || p->to.type != D_AUTO)
- continue;
- if(isfloat[p->to.etype] && FtoB(p->from.type)) {
- switch(p->as) {
- case AMOVSS:
- case AMOVSD:
- break;
- default:
- continue;
- }
- } else if(!isfloat[p->to.etype] && RtoB(p->from.type)) {
- switch(p->as) {
- case AMOVB:
- if(p->to.width == 1)
- break;
- case AMOVW:
- if(p->to.width == 2)
- break;
- case AMOVL:
- if(p->to.width == 4)
- break;
- default:
- continue;
- }
- } else
- continue;
- // p is a MOV reg, mem.
- p2 = p->link;
- h = hash32to16(fnv1(p->to.sym));
- if(counts[h] != 2) {
- continue;
- }
- switch(p2->as) {
- case ALEAL:
- case AFMOVD:
- case AFMOVF:
- case AFMOVL:
- case AFMOVW:
- case AFMOVV:
- // funny
- continue;
- }
- // p2 is OP mem, reg2
- // and OP is not a funny instruction.
- if(p2->from.sym == p->to.sym
- && p2->from.offset == p->to.offset
- && p2->from.type == p->to.type) {
- if(debug['R'] && debug['v']) {
- print(" ===elide== %D\n", &p->to);
- print("%P", p2);
- }
- // p2 is OP mem, reg2.
- // change to OP reg, reg2 and
- // eliminate the mov.
- p2->from = p->from;
- *p = *p2;
- p->link = p2->link;
- if(debug['R'] && debug['v']) {
- print(" ===change== %P\n", p);
- }
- }
- }
-}
diff --git a/src/cmd/8l/8.out.h b/src/cmd/8l/8.out.h
index cf0bc9fee..988e50f3e 100644
--- a/src/cmd/8l/8.out.h
+++ b/src/cmd/8l/8.out.h
@@ -30,11 +30,7 @@
#define NSYM 50
#define NSNAME 8
-#define NOPROF (1<<0)
-#define DUPOK (1<<1)
-#define NOSPLIT (1<<2)
-#define RODATA (1<<3)
-#define NOPTR (1<<4)
+#include "../ld/textflag.h"
enum as
{
@@ -578,9 +574,11 @@ enum as
APSHUFB,
AUSEFIELD,
- ALOCALS,
ATYPE,
-
+ AFUNCDATA,
+ APCDATA,
+ ACHECKNIL,
+
ALAST
};
diff --git a/src/cmd/8l/asm.c b/src/cmd/8l/asm.c
index 18591cd2f..3be37ea22 100644
--- a/src/cmd/8l/asm.c
+++ b/src/cmd/8l/asm.c
@@ -41,6 +41,7 @@ char linuxdynld[] = "/lib/ld-linux.so.2";
char freebsddynld[] = "/usr/libexec/ld-elf.so.1";
char openbsddynld[] = "/usr/libexec/ld.so";
char netbsddynld[] = "/usr/libexec/ld.elf_so";
+char dragonflydynld[] = "/usr/libexec/ld-elf.so.2";
int32
entryvalue(void)
@@ -95,12 +96,6 @@ int nelfsym = 1;
static void addpltsym(Sym*);
static void addgotsym(Sym*);
-Sym *
-lookuprel(void)
-{
- return lookup(".rel", 0);
-}
-
void
adddynrela(Sym *rela, Sym *s, Reloc *r)
{
@@ -366,6 +361,8 @@ int
archreloc(Reloc *r, Sym *s, vlong *val)
{
USED(s);
+ if(linkmode == LinkExternal)
+ return -1;
switch(r->type) {
case D_CONST:
*val = r->add;
@@ -595,12 +592,19 @@ asmb(void)
sect = segtext.sect;
cseek(sect->vaddr - segtext.vaddr + segtext.fileoff);
codeblk(sect->vaddr, sect->len);
-
- /* output read-only data in text segment (rodata, gosymtab, pclntab, ...) */
for(sect = sect->next; sect != nil; sect = sect->next) {
cseek(sect->vaddr - segtext.vaddr + segtext.fileoff);
datblk(sect->vaddr, sect->len);
}
+
+ if(segrodata.filelen > 0) {
+ if(debug['v'])
+ Bprint(&bso, "%5.2f rodatblk\n", cputime());
+ Bflush(&bso);
+
+ cseek(segrodata.fileoff);
+ datblk(segrodata.vaddr, segrodata.filelen);
+ }
if(debug['v'])
Bprint(&bso, "%5.2f datblk\n", cputime());
@@ -655,7 +659,7 @@ asmb(void)
symo = rnd(HEADR+segtext.filelen, INITRND)+rnd(segdata.filelen, INITRND)+machlink;
break;
Elfsym:
- symo = rnd(HEADR+segtext.filelen, INITRND)+segdata.filelen;
+ symo = rnd(HEADR+segtext.filelen, INITRND)+rnd(HEADR+segrodata.filelen, INITRND)+segdata.filelen;
symo = rnd(symo, INITRND);
break;
case Hwindows:
@@ -843,6 +847,7 @@ asmb(void)
case Hfreebsd:
case Hnetbsd:
case Hopenbsd:
+ case Hdragonfly:
asmbelf(symo);
break;
case Hwindows:
diff --git a/src/cmd/8l/l.h b/src/cmd/8l/l.h
index ce12d59ba..814aa1458 100644
--- a/src/cmd/8l/l.h
+++ b/src/cmd/8l/l.h
@@ -143,7 +143,6 @@ struct Sym
int32 got;
int32 align; // if non-zero, required alignment in bytes
int32 elfsym;
- int32 locals; // size of stack frame locals area
int32 args; // size of stack frame incoming arguments area
Sym* hash; // in hash table
Sym* allsym; // in all symbol list
@@ -157,6 +156,7 @@ struct Sym
char* dynimplib;
char* dynimpvers;
struct Section* sect;
+ struct Hist* hist; // for ATEXT
// STEXT
Auto* autom;
@@ -169,14 +169,13 @@ struct Sym
Reloc* r;
int32 nr;
int32 maxr;
- int rel_ro;
};
struct Optab
{
short as;
uchar* ytab;
uchar prefix;
- uchar op[12];
+ uchar op[13];
};
enum
@@ -185,7 +184,7 @@ enum
STRINGSZ = 200,
MINLC = 1,
MAXIO = 8192,
- MAXHIST = 20, /* limit of path elements for history symbols */
+ MAXHIST = 40, /* limit of path elements for history symbols */
Yxxx = 0,
Ynone,
@@ -285,7 +284,6 @@ EXTERN int32 INITRND;
EXTERN int32 INITTEXT;
EXTERN int32 INITDAT;
EXTERN char* INITENTRY; /* entry point */
-EXTERN char* LIBINITENTRY; /* shared library entry point */
EXTERN char* pcstr;
EXTERN Auto* curauto;
EXTERN Auto* curhist;
@@ -311,7 +309,6 @@ EXTERN Sym* symlist;
EXTERN int32 symsize;
EXTERN Sym* textp;
EXTERN int32 textsize;
-EXTERN int version;
EXTERN Prog zprg;
EXTERN int dtype;
EXTERN int tlsoffset;
diff --git a/src/cmd/8l/list.c b/src/cmd/8l/list.c
index 0b544fbce..e2a2ec5ed 100644
--- a/src/cmd/8l/list.c
+++ b/src/cmd/8l/list.c
@@ -356,7 +356,7 @@ Iconv(Fmt *fp)
void
diag(char *fmt, ...)
{
- char buf[STRINGSZ], *tn, *sep;
+ char buf[1024], *tn, *sep;
va_list arg;
tn = "";
diff --git a/src/cmd/8l/obj.c b/src/cmd/8l/obj.c
index c819b9936..3fdc41381 100644
--- a/src/cmd/8l/obj.c
+++ b/src/cmd/8l/obj.c
@@ -53,6 +53,7 @@ Header headers[] = {
"msdoscom", Hmsdoscom,
"msdosexe", Hmsdosexe,
"darwin", Hdarwin,
+ "dragonfly", Hdragonfly,
"linux", Hlinux,
"freebsd", Hfreebsd,
"netbsd", Hnetbsd,
@@ -69,6 +70,7 @@ Header headers[] = {
* -Hmsdoscom -Tx -Rx is MS-DOS .COM
* -Hmsdosexe -Tx -Rx is fake MS-DOS .EXE
* -Hdarwin -Tx -Rx is Apple Mach-O
+ * -Hdragonfly -Tx -Rx is DragonFly ELF32
* -Hlinux -Tx -Rx is Linux ELF32
* -Hfreebsd -Tx -Rx is FreeBSD ELF32
* -Hnetbsd -Tx -Rx is NetBSD ELF32
@@ -89,7 +91,6 @@ main(int argc, char *argv[])
INITDAT = -1;
INITRND = -1;
INITENTRY = 0;
- LIBINITENTRY = 0;
linkmode = LinkAuto;
nuxiinit();
@@ -117,6 +118,7 @@ main(int argc, char *argv[])
flagstr("extldflags", "flags for external linker", &extldflags);
flagcount("f", "ignore version mismatch", &debug['f']);
flagcount("g", "disable go package data checks", &debug['g']);
+ flagstr("installsuffix", "pkg directory suffix", &flag_installsuffix);
flagfn1("linkmode", "mode: set link mode (internal, external, auto)", setlinkmode);
flagstr("k", "sym: set field tracking symbol", &tracksym);
flagstr("o", "outfile: set output file", &outfile);
@@ -154,6 +156,7 @@ main(int argc, char *argv[])
sysfatal("cannot use -linkmode=external with -H %s", headstr(HEADTYPE));
break;
case Hdarwin:
+ case Hdragonfly:
case Hfreebsd:
case Hlinux:
case Hnetbsd:
@@ -243,6 +246,7 @@ main(int argc, char *argv[])
case Hfreebsd:
case Hnetbsd:
case Hopenbsd:
+ case Hdragonfly:
/*
* ELF uses TLS offsets negative from %gs.
* Translate 0(GS) and 4(GS) into -8(GS) and -4(GS).
@@ -366,18 +370,18 @@ zaddr(char *pn, Biobuf *f, Adr *a, Sym *h[])
a->type = D_NONE;
a->offset = 0;
if(t & T_OFFSET)
- a->offset = Bget4(f);
+ a->offset = BGETLE4(f);
a->offset2 = 0;
if(t & T_OFFSET2) {
- a->offset2 = Bget4(f);
+ a->offset2 = BGETLE4(f);
a->type = D_CONST2;
}
a->sym = S;
if(t & T_SYM)
a->sym = zsym(pn, f, h);
if(t & T_FCONST) {
- a->ieee.l = Bget4(f);
- a->ieee.h = Bget4(f);
+ a->ieee.l = BGETLE4(f);
+ a->ieee.h = BGETLE4(f);
a->type = D_FCONST;
} else
if(t & T_SCONST) {
@@ -476,7 +480,7 @@ loop:
if(o == ANAME || o == ASIGNAME) {
sig = 0;
if(o == ASIGNAME)
- sig = Bget4(f);
+ sig = BGETLE4(f);
v = BGETC(f); /* type */
o = BGETC(f); /* sym */
r = 0;
@@ -531,7 +535,7 @@ loop:
p = mal(sizeof(*p));
p->as = o;
- p->line = Bget4(f);
+ p->line = BGETLE4(f);
p->back = 2;
zaddr(pn, f, &p->from, h);
fromgotype = adrgotype;
@@ -552,6 +556,7 @@ loop:
addhist(p->line, D_FILE); /* 'z' */
if(p->to.offset)
addhist(p->to.offset, D_FILE1); /* 'Z' */
+ savehist(p->line, p->to.offset);
histfrogp = 0;
goto loop;
@@ -613,13 +618,6 @@ loop:
pc++;
goto loop;
- case ALOCALS:
- if(skip)
- goto casdef;
- cursym->locals = p->to.offset;
- pc++;
- goto loop;
-
case ATYPE:
if(skip)
goto casdef;
@@ -663,6 +661,7 @@ loop:
diag("%s: redefinition: %s\n%P", pn, s->name, p);
}
s->type = STEXT;
+ s->hist = gethist();
s->value = pc;
s->args = p->to.offset2;
lastp = p;
diff --git a/src/cmd/8l/optab.c b/src/cmd/8l/optab.c
index 1d9d2f55f..a4c40e8e3 100644
--- a/src/cmd/8l/optab.c
+++ b/src/cmd/8l/optab.c
@@ -49,6 +49,16 @@ uchar ynop[] =
Yrf, Ynone, Zpseudo,1,
0
};
+uchar yfuncdata[] =
+{
+ Yi32, Ym, Zpseudo, 0,
+ 0
+};
+uchar ypcdata[] =
+{
+ Yi32, Yi32, Zpseudo, 0,
+ 0,
+};
uchar yxorb[] =
{
Yi32, Yal, Zib_, 1,
@@ -142,6 +152,17 @@ uchar ymovb[] =
Yi32, Ymb, Zibo_m, 2,
0
};
+uchar ymovw[] =
+{
+ Yrl, Yml, Zr_m, 1,
+ Yml, Yrl, Zm_r, 1,
+ Yi0, Yrl, Zclr, 1+2,
+// Yi0, Yml, Zibo_m, 2, // shorter but slower AND $0,dst
+ Yi32, Yrl, Zil_rp, 1,
+ Yi32, Yml, Zilo_m, 2,
+ Yiauto, Yrl, Zaut_r, 1,
+ 0
+};
uchar ymovl[] =
{
Yrl, Yml, Zr_m, 1,
@@ -152,7 +173,7 @@ uchar ymovl[] =
Yi32, Yml, Zilo_m, 2,
Yml, Yxr, Zm_r_xm, 2, // XMM MOVD (32 bit)
Yxr, Yml, Zr_m_xm, 2, // XMM MOVD (32 bit)
- Yiauto, Yrl, Zaut_r, 2,
+ Yiauto, Yrl, Zaut_r, 1,
0
};
uchar ymovq[] =
@@ -196,8 +217,10 @@ uchar yml_mb[] =
Ymb, Yrb, Zm_r, 1,
0
};
-uchar yml_ml[] =
+uchar yxchg[] =
{
+ Yax, Yrl, Z_rp, 1,
+ Yrl, Yax, Zrp_, 1,
Yrl, Yml, Zr_m, 1,
Yml, Yrl, Zm_r, 1,
0
@@ -580,8 +603,8 @@ Optab optab[] =
{ ALSLL, yml_rl, Pm, 0x03 },
{ ALSLW, yml_rl, Pq, 0x03 },
{ AMOVB, ymovb, Pb, 0x88,0x8a,0xb0,0xc6,(00) },
- { AMOVL, ymovl, Px, 0x89,0x8b,0x31,0x83,(04),0xb8,0xc7,(00),Pe,0x6e,Pe,0x7e },
- { AMOVW, ymovl, Pe, 0x89,0x8b,0x31,0x83,(04),0xb8,0xc7,(00) },
+ { AMOVL, ymovl, Px, 0x89,0x8b,0x31,0x83,(04),0xb8,0xc7,(00),Pe,0x6e,Pe,0x7e,0 },
+ { AMOVW, ymovw, Pe, 0x89,0x8b,0x31,0x83,(04),0xb8,0xc7,(00),0 },
{ AMOVQ, ymovq, Pf3, 0x7e },
{ AMOVBLSX, ymb_rl, Pm, 0xbe },
{ AMOVBLZX, ymb_rl, Pm, 0xb6 },
@@ -696,8 +719,8 @@ Optab optab[] =
{ AWAIT, ynone, Px, 0x9b },
{ AWORD, ybyte, Px, 2 },
{ AXCHGB, yml_mb, Pb, 0x86,0x86 },
- { AXCHGL, yml_ml, Px, 0x87,0x87 },
- { AXCHGW, yml_ml, Pe, 0x87,0x87 },
+ { AXCHGL, yxchg, Px, 0x90,0x90,0x87,0x87 },
+ { AXCHGW, yxchg, Pe, 0x90,0x90,0x87,0x87 },
{ AXLAT, ynone, Px, 0xd7 },
{ AXORB, yxorb, Pb, 0x34,0x80,(06),0x30,0x32 },
{ AXORL, yxorl, Px, 0x83,(06),0x35,0x81,(06),0x31,0x33 },
@@ -999,8 +1022,9 @@ Optab optab[] =
{ APSHUFB, ymshufb,Pq, 0x38, 0x00 },
{ AUSEFIELD, ynop, Px, 0,0 },
- { ALOCALS },
{ ATYPE },
+ { AFUNCDATA, yfuncdata, Px, 0,0 },
+ { APCDATA, ypcdata, Px, 0,0 },
0
};
diff --git a/src/cmd/8l/pass.c b/src/cmd/8l/pass.c
index 4871761ff..1eaf78fe0 100644
--- a/src/cmd/8l/pass.c
+++ b/src/cmd/8l/pass.c
@@ -329,7 +329,7 @@ patch(void)
p->from.offset = 0;
}
}
- if((p->as == ACALL && p->to.type != D_BRANCH) || (p->as == AJMP && p->to.type != D_BRANCH)) {
+ if((p->as == ACALL && p->to.type != D_BRANCH) || (p->as == AJMP && p->to.type != D_BRANCH) || (p->as == ARET && p->to.sym != nil)) {
s = p->to.sym;
if(p->to.type == D_INDIR+D_ADDR) {
/* skip check if this is an indirect call (CALL *symbol(SB)) */
@@ -405,15 +405,19 @@ brloop(Prog *p)
return q;
}
+static Prog* load_g_cx(Prog*);
+static Prog* stacksplit(Prog*, int32, Prog**);
+
+static Sym *plan9_tos;
+static Prog *pmorestack;
+static Sym *symmorestack;
+
void
dostkoff(void)
{
- Prog *p, *q, *q1;
+ Prog *p, *q;
int32 autoffset, deltasp;
int a;
- Prog *pmorestack;
- Sym *symmorestack;
- Sym *plan9_tos;
pmorestack = P;
symmorestack = lookup("runtime.morestack", 0);
@@ -439,154 +443,13 @@ dostkoff(void)
autoffset = 0;
q = P;
- if(pmorestack != P)
- if(!(p->from.scale & NOSPLIT)) {
- p = appendp(p); // load g into CX
- switch(HEADTYPE) {
- case Hwindows:
- p->as = AMOVL;
- p->from.type = D_INDIR+D_FS;
- p->from.offset = 0x14;
- p->to.type = D_CX;
-
- p = appendp(p);
- p->as = AMOVL;
- p->from.type = D_INDIR+D_CX;
- p->from.offset = 0;
- p->to.type = D_CX;
- break;
-
- case Hlinux:
- if(linkmode != LinkExternal) {
- p->as = AMOVL;
- p->from.type = D_INDIR+D_GS;
- p->from.offset = 0;
- p->to.type = D_CX;
-
- p = appendp(p);
- p->as = AMOVL;
- p->from.type = D_INDIR+D_CX;
- p->from.offset = tlsoffset + 0;
- p->to.type = D_CX;
- } else {
- p->as = AMOVL;
- p->from.type = D_INDIR+D_GS;
- p->from.offset = tlsoffset + 0;
- p->to.type = D_CX;
- p->from.index = D_GS;
- p->from.scale = 1;
- }
- break;
-
- case Hplan9x32:
- p->as = AMOVL;
- p->from.type = D_EXTERN;
- p->from.sym = plan9_tos;
- p->to.type = D_CX;
-
- p = appendp(p);
- p->as = AMOVL;
- p->from.type = D_INDIR+D_CX;
- p->from.offset = tlsoffset + 0;
- p->to.type = D_CX;
- break;
-
- default:
- p->as = AMOVL;
- p->from.type = D_INDIR+D_GS;
- p->from.offset = tlsoffset + 0;
- p->to.type = D_CX;
- }
-
- if(debug['K']) {
- // 8l -K means check not only for stack
- // overflow but stack underflow.
- // On underflow, INT 3 (breakpoint).
- // Underflow itself is rare but this also
- // catches out-of-sync stack guard info.
- p = appendp(p);
- p->as = ACMPL;
- p->from.type = D_INDIR+D_CX;
- p->from.offset = 4;
- p->to.type = D_SP;
-
- p = appendp(p);
- p->as = AJCC;
- p->to.type = D_BRANCH;
- p->to.offset = 4;
- q1 = p;
-
- p = appendp(p);
- p->as = AINT;
- p->from.type = D_CONST;
- p->from.offset = 3;
-
- p = appendp(p);
- p->as = ANOP;
- q1->pcond = p;
- }
-
- if(autoffset < StackBig) { // do we need to call morestack
- if(autoffset <= StackSmall) {
- // small stack
- p = appendp(p);
- p->as = ACMPL;
- p->from.type = D_SP;
- p->to.type = D_INDIR+D_CX;
- } else {
- // large stack
- p = appendp(p);
- p->as = ALEAL;
- p->from.type = D_INDIR+D_SP;
- p->from.offset = -(autoffset-StackSmall);
- p->to.type = D_AX;
-
- p = appendp(p);
- p->as = ACMPL;
- p->from.type = D_AX;
- p->to.type = D_INDIR+D_CX;
- }
-
- // common
- p = appendp(p);
- p->as = AJHI;
- p->to.type = D_BRANCH;
- p->to.offset = 4;
- q = p;
- }
-
- p = appendp(p); // save frame size in DI
- p->as = AMOVL;
- p->to.type = D_DI;
- p->from.type = D_CONST;
-
- // If we ask for more stack, we'll get a minimum of StackMin bytes.
- // We need a stack frame large enough to hold the top-of-stack data,
- // the function arguments+results, our caller's PC, our frame,
- // a word for the return PC of the next call, and then the StackLimit bytes
- // that must be available on entry to any function called from a function
- // that did a stack check. If StackMin is enough, don't ask for a specific
- // amount: then we can use the custom functions and save a few
- // instructions.
- if(StackTop + cursym->text->to.offset2 + PtrSize + autoffset + PtrSize + StackLimit >= StackMin)
- p->from.offset = (autoffset+7) & ~7LL;
-
- p = appendp(p); // save arg size in AX
- p->as = AMOVL;
- p->to.type = D_AX;
- p->from.type = D_CONST;
- p->from.offset = cursym->text->to.offset2;
+ if(!(p->from.scale & NOSPLIT) || (p->from.scale & WRAPPER)) {
p = appendp(p);
- p->as = ACALL;
- p->to.type = D_BRANCH;
- p->pcond = pmorestack;
- p->to.sym = symmorestack;
-
+ p = load_g_cx(p); // load g into CX
}
-
- if(q != P)
- q->pcond = p->link;
+ if(!(cursym->text->from.scale & NOSPLIT))
+ p = stacksplit(p, autoffset, &q); // emit split check
if(autoffset) {
p = appendp(p);
@@ -594,8 +457,6 @@ dostkoff(void)
p->from.type = D_CONST;
p->from.offset = autoffset;
p->spadj = autoffset;
- if(q != P)
- q->pcond = p;
} else {
// zero-byte stack adjustment.
// Insert a fake non-zero adjustment so that stkcheck can
@@ -607,8 +468,20 @@ dostkoff(void)
p->as = ANOP;
p->spadj = PtrSize;
}
+ if(q != P)
+ q->pcond = p;
deltasp = autoffset;
+ if(cursym->text->from.scale & WRAPPER) {
+ // g->panicwrap += autoffset + PtrSize;
+ p = appendp(p);
+ p->as = AADDL;
+ p->from.type = D_CONST;
+ p->from.offset = autoffset + PtrSize;
+ p->to.type = D_INDIR+D_CX;
+ p->to.offset = 2*PtrSize;
+ }
+
if(debug['Z'] && autoffset && !(cursym->text->from.scale&NOSPLIT)) {
// 8l -Z means zero the stack frame on entry.
// This slows down function calls but can help avoid
@@ -678,6 +551,19 @@ dostkoff(void)
if(autoffset != deltasp)
diag("unbalanced PUSH/POP");
+
+ if(cursym->text->from.scale & WRAPPER) {
+ p = load_g_cx(p);
+ p = appendp(p);
+ // g->panicwrap -= autoffset + PtrSize;
+ p->as = ASUBL;
+ p->from.type = D_CONST;
+ p->from.offset = autoffset + PtrSize;
+ p->to.type = D_INDIR+D_CX;
+ p->to.offset = 2*PtrSize;
+ p = appendp(p);
+ p->as = ARET;
+ }
if(autoffset) {
p->as = AADJSP;
@@ -692,8 +578,243 @@ dostkoff(void)
// the cleanup.
p->spadj = +autoffset;
}
+ if(p->to.sym) // retjmp
+ p->as = AJMP;
+ }
+ }
+}
+
+// Append code to p to load g into cx.
+// Overwrites p with the first instruction (no first appendp).
+// Overwriting p is unusual but it lets use this in both the
+// prologue (caller must call appendp first) and in the epilogue.
+// Returns last new instruction.
+static Prog*
+load_g_cx(Prog *p)
+{
+ switch(HEADTYPE) {
+ case Hwindows:
+ p->as = AMOVL;
+ p->from.type = D_INDIR+D_FS;
+ p->from.offset = 0x14;
+ p->to.type = D_CX;
+
+ p = appendp(p);
+ p->as = AMOVL;
+ p->from.type = D_INDIR+D_CX;
+ p->from.offset = 0;
+ p->to.type = D_CX;
+ break;
+
+ case Hlinux:
+ if(linkmode != LinkExternal) {
+ p->as = AMOVL;
+ p->from.type = D_INDIR+D_GS;
+ p->from.offset = 0;
+ p->to.type = D_CX;
+
+ p = appendp(p);
+ p->as = AMOVL;
+ p->from.type = D_INDIR+D_CX;
+ p->from.offset = tlsoffset + 0;
+ p->to.type = D_CX;
+ } else {
+ p->as = AMOVL;
+ p->from.type = D_INDIR+D_GS;
+ p->from.offset = tlsoffset + 0;
+ p->to.type = D_CX;
+ p->from.index = D_GS;
+ p->from.scale = 1;
}
+ break;
+
+ case Hplan9x32:
+ p->as = AMOVL;
+ p->from.type = D_EXTERN;
+ p->from.sym = plan9_tos;
+ p->to.type = D_CX;
+
+ p = appendp(p);
+ p->as = AMOVL;
+ p->from.type = D_INDIR+D_CX;
+ p->from.offset = tlsoffset + 0;
+ p->to.type = D_CX;
+ break;
+
+ default:
+ p->as = AMOVL;
+ p->from.type = D_INDIR+D_GS;
+ p->from.offset = tlsoffset + 0;
+ p->to.type = D_CX;
+ }
+ return p;
+}
+
+// Append code to p to check for stack split.
+// Appends to (does not overwrite) p.
+// Assumes g is in CX.
+// Returns last new instruction.
+// On return, *jmpok is the instruction that should jump
+// to the stack frame allocation if no split is needed.
+static Prog*
+stacksplit(Prog *p, int32 framesize, Prog **jmpok)
+{
+ Prog *q, *q1;
+ int arg;
+
+ if(debug['K']) {
+ // 8l -K means check not only for stack
+ // overflow but stack underflow.
+ // On underflow, INT 3 (breakpoint).
+ // Underflow itself is rare but this also
+ // catches out-of-sync stack guard info.
+ p = appendp(p);
+ p->as = ACMPL;
+ p->from.type = D_INDIR+D_CX;
+ p->from.offset = 4;
+ p->to.type = D_SP;
+
+ p = appendp(p);
+ p->as = AJCC;
+ p->to.type = D_BRANCH;
+ p->to.offset = 4;
+ q1 = p;
+
+ p = appendp(p);
+ p->as = AINT;
+ p->from.type = D_CONST;
+ p->from.offset = 3;
+
+ p = appendp(p);
+ p->as = ANOP;
+ q1->pcond = p;
}
+ q1 = P;
+
+ if(framesize <= StackSmall) {
+ // small stack: SP <= stackguard
+ // CMPL SP, stackguard
+ p = appendp(p);
+ p->as = ACMPL;
+ p->from.type = D_SP;
+ p->to.type = D_INDIR+D_CX;
+ } else if(framesize <= StackBig) {
+ // large stack: SP-framesize <= stackguard-StackSmall
+ // LEAL -(framesize-StackSmall)(SP), AX
+ // CMPL AX, stackguard
+ p = appendp(p);
+ p->as = ALEAL;
+ p->from.type = D_INDIR+D_SP;
+ p->from.offset = -(framesize-StackSmall);
+ p->to.type = D_AX;
+
+ p = appendp(p);
+ p->as = ACMPL;
+ p->from.type = D_AX;
+ p->to.type = D_INDIR+D_CX;
+ } else {
+ // Such a large stack we need to protect against wraparound
+ // if SP is close to zero.
+ // SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
+ // The +StackGuard on both sides is required to keep the left side positive:
+ // SP is allowed to be slightly below stackguard. See stack.h.
+ //
+ // Preemption sets stackguard to StackPreempt, a very large value.
+ // That breaks the math above, so we have to check for that explicitly.
+ // MOVL stackguard, CX
+ // CMPL CX, $StackPreempt
+ // JEQ label-of-call-to-morestack
+ // LEAL StackGuard(SP), AX
+ // SUBL stackguard, AX
+ // CMPL AX, $(framesize+(StackGuard-StackSmall))
+ p = appendp(p);
+ p->as = AMOVL;
+ p->from.type = D_INDIR+D_CX;
+ p->from.offset = 0;
+ p->to.type = D_SI;
+
+ p = appendp(p);
+ p->as = ACMPL;
+ p->from.type = D_SI;
+ p->to.type = D_CONST;
+ p->to.offset = (uint32)StackPreempt;
+
+ p = appendp(p);
+ p->as = AJEQ;
+ p->to.type = D_BRANCH;
+ q1 = p;
+
+ p = appendp(p);
+ p->as = ALEAL;
+ p->from.type = D_INDIR+D_SP;
+ p->from.offset = StackGuard;
+ p->to.type = D_AX;
+
+ p = appendp(p);
+ p->as = ASUBL;
+ p->from.type = D_SI;
+ p->from.offset = 0;
+ p->to.type = D_AX;
+
+ p = appendp(p);
+ p->as = ACMPL;
+ p->from.type = D_AX;
+ p->to.type = D_CONST;
+ p->to.offset = framesize+(StackGuard-StackSmall);
+ }
+
+ // common
+ p = appendp(p);
+ p->as = AJHI;
+ p->to.type = D_BRANCH;
+ p->to.offset = 4;
+ q = p;
+
+ p = appendp(p); // save frame size in DI
+ p->as = AMOVL;
+ p->to.type = D_DI;
+ p->from.type = D_CONST;
+
+ // If we ask for more stack, we'll get a minimum of StackMin bytes.
+ // We need a stack frame large enough to hold the top-of-stack data,
+ // the function arguments+results, our caller's PC, our frame,
+ // a word for the return PC of the next call, and then the StackLimit bytes
+ // that must be available on entry to any function called from a function
+ // that did a stack check. If StackMin is enough, don't ask for a specific
+ // amount: then we can use the custom functions and save a few
+ // instructions.
+ if(StackTop + cursym->text->to.offset2 + PtrSize + framesize + PtrSize + StackLimit >= StackMin)
+ p->from.offset = (framesize+7) & ~7LL;
+
+ arg = cursym->text->to.offset2;
+ if(arg == 1) // special marker for known 0
+ arg = 0;
+ if(arg&3)
+ diag("misaligned argument size in stack split");
+ p = appendp(p); // save arg size in AX
+ p->as = AMOVL;
+ p->to.type = D_AX;
+ p->from.type = D_CONST;
+ p->from.offset = arg;
+
+ p = appendp(p);
+ p->as = ACALL;
+ p->to.type = D_BRANCH;
+ p->pcond = pmorestack;
+ p->to.sym = symmorestack;
+
+ p = appendp(p);
+ p->as = AJMP;
+ p->to.type = D_BRANCH;
+ p->pcond = cursym->text->link;
+
+ if(q != P)
+ q->pcond = p->link;
+ if(q1 != P)
+ q1->pcond = q->link;
+
+ *jmpok = q;
+ return p;
}
int32
diff --git a/src/cmd/8l/span.c b/src/cmd/8l/span.c
index 980186b16..acf973cab 100644
--- a/src/cmd/8l/span.c
+++ b/src/cmd/8l/span.c
@@ -695,18 +695,11 @@ putrelv:
r = addrel(cursym);
r->off = curp->pc + andptr - and;
- r->add = 0;
- r->xadd = 0;
+ r->add = a->offset-tlsoffset;
+ r->xadd = r->add;
r->siz = 4;
r->type = D_TLS;
- if(a->offset == tlsoffset+0)
- s = lookup("runtime.g", 0);
- else
- s = lookup("runtime.m", 0);
- s->type = STLSBSS;
- s->reachable = 1;
- s->hide = 1;
- s->size = PtrSize;
+ s = lookup("runtime.tlsgm", 0);
r->sym = s;
r->xsym = s;
v = 0;
diff --git a/src/cmd/addr2line/main.c b/src/cmd/addr2line/main.c
index 9faadc27b..54c4d90b5 100644
--- a/src/cmd/addr2line/main.c
+++ b/src/cmd/addr2line/main.c
@@ -31,7 +31,7 @@ void
main(int argc, char **argv)
{
int fd;
- char *p;
+ char *p, *q;
uvlong pc;
Symbol s;
Fhdr fhdr;
@@ -67,6 +67,17 @@ main(int argc, char **argv)
if(p == nil)
break;
p[Blinelen(&bin)-1] = '\0';
+ q = strchr(p, ':');
+ if(q != nil) {
+ // reverse: translate file:line to pc
+ *q++ = '\0';
+ pc = file2pc(p, atoi(q));
+ if(pc == ~(uvlong)0)
+ Bprint(&bout, "!%r\n");
+ else
+ Bprint(&bout, "0x%llux\n", pc);
+ continue;
+ }
pc = strtoull(p, 0, 16);
if(!findsym(pc, CTEXT, &s))
s.name = "??";
diff --git a/src/cmd/api/clone.go b/src/cmd/api/clone.go
deleted file mode 100644
index 180215f4b..000000000
--- a/src/cmd/api/clone.go
+++ /dev/null
@@ -1,251 +0,0 @@
-// Copyright 2012 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"
- "log"
- "reflect"
-)
-
-const debugClone = false
-
-// TODO(bradfitz): delete this function (and whole file) once
-// http://golang.org/issue/4380 is fixed.
-func clone(i interface{}) (cloned interface{}) {
- if debugClone {
- defer func() {
- if !reflect.DeepEqual(i, cloned) {
- log.Printf("cloned %T doesn't match: in=%#v out=%#v", i, i, cloned)
- }
- }()
- }
- switch v := i.(type) {
- case nil:
- return nil
- case *ast.File:
- o := &ast.File{
- Doc: v.Doc, // shallow
- Package: v.Package,
- Comments: v.Comments, // shallow
- Name: v.Name,
- Scope: v.Scope,
- }
- for _, x := range v.Decls {
- o.Decls = append(o.Decls, clone(x).(ast.Decl))
- }
- for _, x := range v.Imports {
- o.Imports = append(o.Imports, clone(x).(*ast.ImportSpec))
- }
- for _, x := range v.Unresolved {
- o.Unresolved = append(o.Unresolved, x)
- }
- return o
- case *ast.GenDecl:
- o := new(ast.GenDecl)
- *o = *v
- o.Specs = nil
- for _, x := range v.Specs {
- o.Specs = append(o.Specs, clone(x).(ast.Spec))
- }
- return o
- case *ast.TypeSpec:
- o := new(ast.TypeSpec)
- *o = *v
- o.Type = cloneExpr(v.Type)
- return o
- case *ast.InterfaceType:
- o := new(ast.InterfaceType)
- *o = *v
- o.Methods = clone(v.Methods).(*ast.FieldList)
- return o
- case *ast.FieldList:
- if v == nil {
- return v
- }
- o := new(ast.FieldList)
- *o = *v
- o.List = nil
- for _, x := range v.List {
- o.List = append(o.List, clone(x).(*ast.Field))
- }
- return o
- case *ast.Field:
- o := &ast.Field{
- Doc: v.Doc, // shallow
- Type: cloneExpr(v.Type),
- Tag: clone(v.Tag).(*ast.BasicLit),
- Comment: v.Comment, // shallow
- }
- for _, x := range v.Names {
- o.Names = append(o.Names, clone(x).(*ast.Ident))
- }
- return o
- case *ast.FuncType:
- if v == nil {
- return v
- }
- return &ast.FuncType{
- Func: v.Func,
- Params: clone(v.Params).(*ast.FieldList),
- Results: clone(v.Results).(*ast.FieldList),
- }
- case *ast.FuncDecl:
- if v == nil {
- return v
- }
- return &ast.FuncDecl{
- Recv: clone(v.Recv).(*ast.FieldList),
- Name: v.Name,
- Type: clone(v.Type).(*ast.FuncType),
- Body: v.Body, // shallow
- }
- case *ast.ValueSpec:
- if v == nil {
- return v
- }
- o := &ast.ValueSpec{
- Type: cloneExpr(v.Type),
- }
- for _, x := range v.Names {
- o.Names = append(o.Names, x)
- }
- for _, x := range v.Values {
- o.Values = append(o.Values, cloneExpr(x))
- }
- return o
- case *ast.CallExpr:
- if v == nil {
- return v
- }
- o := &ast.CallExpr{}
- *o = *v
- o.Args = cloneExprs(v.Args)
- o.Fun = cloneExpr(v.Fun)
- return o
- case *ast.SelectorExpr:
- if v == nil {
- return nil
- }
- return &ast.SelectorExpr{
- X: cloneExpr(v.X),
- Sel: v.Sel,
- }
- case *ast.ArrayType:
- return &ast.ArrayType{
- Lbrack: v.Lbrack,
- Len: cloneExpr(v.Len),
- Elt: cloneExpr(v.Elt),
- }
- case *ast.StructType:
- return &ast.StructType{
- Struct: v.Struct,
- Fields: clone(v.Fields).(*ast.FieldList),
- Incomplete: v.Incomplete,
- }
- case *ast.StarExpr:
- return &ast.StarExpr{
- Star: v.Star,
- X: cloneExpr(v.X),
- }
- case *ast.CompositeLit:
- return &ast.CompositeLit{
- Type: cloneExpr(v.Type),
- Lbrace: v.Lbrace,
- Elts: cloneExprs(v.Elts),
- Rbrace: v.Rbrace,
- }
- case *ast.UnaryExpr:
- return &ast.UnaryExpr{
- OpPos: v.OpPos,
- Op: v.Op,
- X: cloneExpr(v.X),
- }
- case *ast.BinaryExpr:
- return &ast.BinaryExpr{
- OpPos: v.OpPos,
- Op: v.Op,
- X: cloneExpr(v.X),
- Y: cloneExpr(v.Y),
- }
- case *ast.Ellipsis:
- return &ast.Ellipsis{
- Ellipsis: v.Ellipsis,
- Elt: cloneExpr(v.Elt),
- }
- case *ast.KeyValueExpr:
- return &ast.KeyValueExpr{
- Key: cloneExpr(v.Key),
- Colon: v.Colon,
- Value: cloneExpr(v.Value),
- }
- case *ast.FuncLit:
- return &ast.FuncLit{
- Type: clone(v.Type).(*ast.FuncType),
- Body: v.Body, // shallow
- }
- case *ast.MapType:
- return &ast.MapType{
- Map: v.Map,
- Key: cloneExpr(v.Key),
- Value: cloneExpr(v.Value),
- }
- case *ast.ParenExpr:
- return &ast.ParenExpr{
- Lparen: v.Lparen,
- X: cloneExpr(v.X),
- Rparen: v.Rparen,
- }
- case *ast.Ident, *ast.BasicLit:
- return v
- case *ast.ImportSpec:
- return &ast.ImportSpec{
- Doc: v.Doc, // shallow
- Name: v.Name,
- Path: clone(v.Path).(*ast.BasicLit),
- Comment: v.Comment, // shallow
- EndPos: v.EndPos,
- }
- case *ast.ChanType:
- return &ast.ChanType{
- Begin: v.Begin,
- Arrow: v.Arrow,
- Dir: v.Dir,
- Value: cloneExpr(v.Value),
- }
- case *ast.TypeAssertExpr:
- return &ast.TypeAssertExpr{
- X: cloneExpr(v.X),
- Type: cloneExpr(v.Type),
- }
- case *ast.IndexExpr:
- return &ast.IndexExpr{
- X: cloneExpr(v.X),
- Index: cloneExpr(v.Index),
- Lbrack: v.Lbrack,
- Rbrack: v.Rbrack,
- }
- }
- panic(fmt.Sprintf("Uncloneable type %T", i))
-}
-
-func cloneExpr(x ast.Expr) ast.Expr {
- if x == nil {
- return nil
- }
- return clone(x).(ast.Expr)
-}
-
-func cloneExprs(x []ast.Expr) []ast.Expr {
- if x == nil {
- return nil
- }
- o := make([]ast.Expr, len(x))
- for i, x := range x {
- o[i] = cloneExpr(x)
- }
- return o
-}
diff --git a/src/cmd/api/goapi.go b/src/cmd/api/goapi.go
index ff75f00e3..a62c87421 100644
--- a/src/cmd/api/goapi.go
+++ b/src/cmd/api/goapi.go
@@ -2,38 +2,32 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Api computes the exported API of a set of Go packages.
-//
-// BUG(bradfitz): Note that this tool is only currently suitable
-// for use on the Go standard library, not arbitrary packages.
-// Once the Go AST has type information, this tool will be more
-// reliable without hard-coded hacks throughout.
+// +build api_tool
+
+// Binary api computes the exported API of a set of Go packages.
package main
import (
"bufio"
"bytes"
- "errors"
"flag"
"fmt"
"go/ast"
"go/build"
- "go/doc"
"go/parser"
- "go/printer"
"go/token"
"io"
"io/ioutil"
"log"
"os"
"os/exec"
- "path"
"path/filepath"
"regexp"
"runtime"
"sort"
- "strconv"
"strings"
+
+ "code.google.com/p/go.tools/go/types"
)
// Flags
@@ -53,6 +47,7 @@ var contexts = []*build.Context{
{GOOS: "linux", GOARCH: "386"},
{GOOS: "linux", GOARCH: "amd64", CgoEnabled: true},
{GOOS: "linux", GOARCH: "amd64"},
+ {GOOS: "linux", GOARCH: "arm", CgoEnabled: true},
{GOOS: "linux", GOARCH: "arm"},
{GOOS: "darwin", GOARCH: "386", CgoEnabled: true},
{GOOS: "darwin", GOARCH: "386"},
@@ -60,8 +55,22 @@ var contexts = []*build.Context{
{GOOS: "darwin", GOARCH: "amd64"},
{GOOS: "windows", GOARCH: "amd64"},
{GOOS: "windows", GOARCH: "386"},
- {GOOS: "freebsd", GOARCH: "amd64"},
+ {GOOS: "freebsd", GOARCH: "386", CgoEnabled: true},
{GOOS: "freebsd", GOARCH: "386"},
+ {GOOS: "freebsd", GOARCH: "amd64", CgoEnabled: true},
+ {GOOS: "freebsd", GOARCH: "amd64"},
+ {GOOS: "freebsd", GOARCH: "arm", CgoEnabled: true},
+ {GOOS: "freebsd", GOARCH: "arm"},
+ {GOOS: "netbsd", GOARCH: "386", CgoEnabled: true},
+ {GOOS: "netbsd", GOARCH: "386"},
+ {GOOS: "netbsd", GOARCH: "amd64", CgoEnabled: true},
+ {GOOS: "netbsd", GOARCH: "amd64"},
+ {GOOS: "netbsd", GOARCH: "arm", CgoEnabled: true},
+ {GOOS: "netbsd", GOARCH: "arm"},
+ {GOOS: "openbsd", GOARCH: "386", CgoEnabled: true},
+ {GOOS: "openbsd", GOARCH: "386"},
+ {GOOS: "openbsd", GOARCH: "amd64", CgoEnabled: true},
+ {GOOS: "openbsd", GOARCH: "amd64"},
}
func contextName(c *build.Context) string {
@@ -115,35 +124,35 @@ func main() {
c.Compiler = build.Default.Compiler
}
- var pkgs []string
+ var pkgNames []string
if flag.NArg() > 0 {
- pkgs = flag.Args()
+ pkgNames = flag.Args()
} else {
stds, err := exec.Command("go", "list", "std").Output()
if err != nil {
log.Fatal(err)
}
- pkgs = strings.Fields(string(stds))
+ pkgNames = strings.Fields(string(stds))
}
var featureCtx = make(map[string]map[string]bool) // feature -> context name -> true
for _, context := range contexts {
- w := NewWalker()
- w.context = context
-
- for _, pkg := range pkgs {
- w.wantedPkg[pkg] = true
- }
-
- for _, pkg := range pkgs {
- if strings.HasPrefix(pkg, "cmd/") {
- continue
- }
- if fi, err := os.Stat(filepath.Join(w.root, pkg)); err != nil || !fi.IsDir() {
- log.Fatalf("no source in tree for package %q", pkg)
+ w := NewWalker(context, filepath.Join(build.Default.GOROOT, "src/pkg"))
+
+ for _, name := range pkgNames {
+ // - Package "unsafe" contains special signatures requiring
+ // extra care when printing them - ignore since it is not
+ // going to change w/o a language change.
+ // - We don't care about the API of commands.
+ if name != "unsafe" && !strings.HasPrefix(name, "cmd/") {
+ if name == "runtime/cgo" && !context.CgoEnabled {
+ // w.Import(name) will return nil
+ continue
+ }
+ w.export(w.Import(name))
}
- w.WalkPackage(pkg)
}
+
ctxName := contextName(context)
for _, f := range w.Features() {
if featureCtx[f] == nil {
@@ -179,7 +188,7 @@ func main() {
if *checkFile == "" {
sort.Strings(features)
for _, f := range features {
- fmt.Fprintf(bw, "%s\n", f)
+ fmt.Fprintln(bw, f)
}
return
}
@@ -193,6 +202,22 @@ func main() {
fail = !compareAPI(bw, features, required, optional, exception)
}
+// export emits the exported package features.
+func (w *Walker) export(pkg *types.Package) {
+ if *verbose {
+ log.Println(pkg)
+ }
+ pop := w.pushScope("pkg " + pkg.Path())
+ w.current = pkg
+ scope := pkg.Scope()
+ for _, name := range scope.Names() {
+ if ast.IsExported(name) {
+ w.emitObj(scope.Lookup(name))
+ }
+ }
+ pop()
+}
+
func set(items []string) map[string]bool {
s := make(map[string]bool)
for _, v := range items {
@@ -231,7 +256,12 @@ func compareAPI(w io.Writer, features, required, optional, exception []string) (
case len(features) == 0 || (len(required) > 0 && required[0] < features[0]):
feature := take(&required)
if exceptionSet[feature] {
- fmt.Fprintf(w, "~%s\n", feature)
+ // An "unfortunate" case: the feature was once
+ // included in the API (e.g. go1.txt), but was
+ // subsequently removed. These are already
+ // acknowledged by being in the file
+ // "api/except.txt". No need to print them out
+ // here.
} else if featureSet[featureWithoutContext(feature)] {
// okay.
} else {
@@ -284,53 +314,26 @@ func fileFeatures(filename string) []string {
return strings.Split(text, "\n")
}
-// pkgSymbol represents a symbol in a package
-type pkgSymbol struct {
- pkg string // "net/http"
- symbol string // "RoundTripper"
-}
-
var fset = token.NewFileSet()
type Walker struct {
- context *build.Context
- root string
- scope []string
- features map[string]bool // set
- lastConstType string
- curPackageName string
- curPackage *ast.Package
- prevConstType map[pkgSymbol]string
- constDep map[string]string // key's const identifier has type of future value const identifier
- packageState map[string]loadState
- interfaces map[pkgSymbol]*ast.InterfaceType
- functionTypes map[pkgSymbol]string // symbol => return type
- selectorFullPkg map[string]string // "http" => "net/http", updated by imports
- wantedPkg map[string]bool // packages requested on the command line
+ context *build.Context
+ root string
+ scope []string
+ current *types.Package
+ features map[string]bool // set
+ imported map[string]*types.Package // packages already imported
}
-func NewWalker() *Walker {
+func NewWalker(context *build.Context, root string) *Walker {
return &Walker{
- features: make(map[string]bool),
- packageState: make(map[string]loadState),
- interfaces: make(map[pkgSymbol]*ast.InterfaceType),
- functionTypes: make(map[pkgSymbol]string),
- selectorFullPkg: make(map[string]string),
- wantedPkg: make(map[string]bool),
- prevConstType: make(map[pkgSymbol]string),
- root: filepath.Join(build.Default.GOROOT, "src/pkg"),
+ context: context,
+ root: root,
+ features: map[string]bool{},
+ imported: map[string]*types.Package{"unsafe": types.Unsafe},
}
}
-// loadState is the state of a package's parsing.
-type loadState int
-
-const (
- notLoaded loadState = iota
- loading
- loaded
-)
-
func (w *Walker) Features() (fs []string) {
for f := range w.features {
fs = append(fs, f)
@@ -339,127 +342,187 @@ func (w *Walker) Features() (fs []string) {
return
}
-// fileDeps returns the imports in a file.
-func fileDeps(f *ast.File) (pkgs []string) {
- for _, is := range f.Imports {
- fpkg, err := strconv.Unquote(is.Path.Value)
+var parsedFileCache = make(map[string]*ast.File)
+
+func (w *Walker) parseFile(dir, file string) (*ast.File, error) {
+ filename := filepath.Join(dir, file)
+ f, _ := parsedFileCache[filename]
+ if f != nil {
+ return f, nil
+ }
+
+ var err error
+
+ // generate missing context-dependent files.
+
+ if w.context != nil && file == fmt.Sprintf("zgoos_%s.go", w.context.GOOS) {
+ src := fmt.Sprintf("package runtime; const theGoos = `%s`", w.context.GOOS)
+ f, err = parser.ParseFile(fset, filename, src, 0)
if err != nil {
- log.Fatalf("error unquoting import string %q: %v", is.Path.Value, err)
- }
- if fpkg != "C" {
- pkgs = append(pkgs, fpkg)
+ log.Fatalf("incorrect generated file: %s", err)
}
}
- return
-}
-var parsedFileCache = make(map[string]*ast.File)
+ if w.context != nil && file == fmt.Sprintf("zgoarch_%s.go", w.context.GOARCH) {
+ src := fmt.Sprintf("package runtime; const theGoarch = `%s`", w.context.GOARCH)
+ f, err = parser.ParseFile(fset, filename, src, 0)
+ if err != nil {
+ log.Fatalf("incorrect generated file: %s", err)
+ }
+ }
-func parseFile(filename string) (*ast.File, error) {
- f, ok := parsedFileCache[filename]
- if !ok {
- var err error
+ if f == nil {
f, err = parser.ParseFile(fset, filename, nil, 0)
if err != nil {
return nil, err
}
- parsedFileCache[filename] = f
}
- return clone(f).(*ast.File), nil
+
+ parsedFileCache[filename] = f
+ return f, nil
}
-// WalkPackage walks all files in package `name'.
-// WalkPackage does nothing if the package has already been loaded.
-func (w *Walker) WalkPackage(name string) {
- switch w.packageState[name] {
- case loading:
- log.Fatalf("import cycle loading package %q?", name)
- case loaded:
- return
+func contains(list []string, s string) bool {
+ for _, t := range list {
+ if t == s {
+ return true
+ }
}
- w.packageState[name] = loading
- defer func() {
- w.packageState[name] = loaded
- }()
- dir := filepath.Join(w.root, filepath.FromSlash(name))
+ return false
+}
- ctxt := w.context
- if ctxt == nil {
- ctxt = &build.Default
+var (
+ pkgCache = map[string]*types.Package{} // map tagKey to package
+ pkgTags = map[string][]string{} // map import dir to list of relevant tags
+)
+
+// tagKey returns the tag-based key to use in the pkgCache.
+// It is a comma-separated string; the first part is dir, the rest tags.
+// The satisfied tags are derived from context but only those that
+// matter (the ones listed in the tags argument) are used.
+// The tags list, which came from go/build's Package.AllTags,
+// is known to be sorted.
+func tagKey(dir string, context *build.Context, tags []string) string {
+ ctags := map[string]bool{
+ context.GOOS: true,
+ context.GOARCH: true,
}
- info, err := ctxt.ImportDir(dir, 0)
- if err != nil {
- if strings.Contains(err.Error(), "no Go source files") {
- return
- }
- log.Fatalf("pkg %q, dir %q: ScanDir: %v", name, dir, err)
+ if context.CgoEnabled {
+ ctags["cgo"] = true
}
-
- apkg := &ast.Package{
- Files: make(map[string]*ast.File),
+ for _, tag := range context.BuildTags {
+ ctags[tag] = true
}
-
- files := append(append([]string{}, info.GoFiles...), info.CgoFiles...)
- for _, file := range files {
- f, err := parseFile(filepath.Join(dir, file))
- if err != nil {
- log.Fatalf("error parsing package %s, file %s: %v", name, file, err)
+ // TODO: ReleaseTags (need to load default)
+ key := dir
+ for _, tag := range tags {
+ if ctags[tag] {
+ key += "," + tag
}
- apkg.Files[file] = f
+ }
+ return key
+}
- for _, dep := range fileDeps(f) {
- w.WalkPackage(dep)
+// Importing is a sentinel taking the place in Walker.imported
+// for a package that is in the process of being imported.
+var importing types.Package
+
+func (w *Walker) Import(name string) (pkg *types.Package) {
+ pkg = w.imported[name]
+ if pkg != nil {
+ if pkg == &importing {
+ log.Fatalf("cycle importing package %q", name)
}
+ return pkg
}
+ w.imported[name] = &importing
- if *verbose {
- log.Printf("package %s", name)
+ // Determine package files.
+ dir := filepath.Join(w.root, filepath.FromSlash(name))
+ if fi, err := os.Stat(dir); err != nil || !fi.IsDir() {
+ log.Fatalf("no source in tree for package %q", pkg)
}
- pop := w.pushScope("pkg " + name)
- defer pop()
- w.curPackageName = name
- w.curPackage = apkg
- w.constDep = map[string]string{}
+ context := w.context
+ if context == nil {
+ context = &build.Default
+ }
- for _, afile := range apkg.Files {
- w.recordTypes(afile)
+ // Look in cache.
+ // If we've already done an import with the same set
+ // of relevant tags, reuse the result.
+ var key string
+ if tags, ok := pkgTags[dir]; ok {
+ key = tagKey(dir, context, tags)
+ if pkg := pkgCache[key]; pkg != nil {
+ w.imported[name] = pkg
+ return pkg
+ }
}
- // Register all function declarations first.
- for _, afile := range apkg.Files {
- for _, di := range afile.Decls {
- if d, ok := di.(*ast.FuncDecl); ok {
- w.peekFuncDecl(d)
- }
+ info, err := context.ImportDir(dir, 0)
+ if err != nil {
+ if _, nogo := err.(*build.NoGoError); nogo {
+ return
}
+ log.Fatalf("pkg %q, dir %q: ScanDir: %v", name, dir, err)
}
- for _, afile := range apkg.Files {
- w.walkFile(afile)
+ // Save tags list first time we see a directory.
+ if _, ok := pkgTags[dir]; !ok {
+ pkgTags[dir] = info.AllTags
+ key = tagKey(dir, context, info.AllTags)
}
- w.resolveConstantDeps()
+ filenames := append(append([]string{}, info.GoFiles...), info.CgoFiles...)
- // Now that we're done walking types, vars and consts
- // in the *ast.Package, use go/doc to do the rest
- // (functions and methods). This is done here because
- // go/doc is destructive. We can't use the
- // *ast.Package after this.
- dpkg := doc.New(apkg, name, doc.AllMethods)
+ // Certain files only exist when building for the specified context.
+ // Add them manually.
+ if name == "runtime" {
+ n := fmt.Sprintf("zgoos_%s.go", w.context.GOOS)
+ if !contains(filenames, n) {
+ filenames = append(filenames, n)
+ }
- for _, t := range dpkg.Types {
- // Move funcs up to the top-level, not hiding in the Types.
- dpkg.Funcs = append(dpkg.Funcs, t.Funcs...)
+ n = fmt.Sprintf("zgoarch_%s.go", w.context.GOARCH)
+ if !contains(filenames, n) {
+ filenames = append(filenames, n)
+ }
+ }
- for _, m := range t.Methods {
- w.walkFuncDecl(m.Decl)
+ // Parse package files.
+ var files []*ast.File
+ for _, file := range filenames {
+ f, err := w.parseFile(dir, file)
+ if err != nil {
+ log.Fatalf("error parsing package %s: %s", name, err)
}
+ files = append(files, f)
}
- for _, f := range dpkg.Funcs {
- w.walkFuncDecl(f.Decl)
+ // Type-check package files.
+ conf := types.Config{
+ IgnoreFuncBodies: true,
+ FakeImportC: true,
+ Import: func(imports map[string]*types.Package, name string) (*types.Package, error) {
+ pkg := w.Import(name)
+ imports[name] = pkg
+ return pkg, nil
+ },
+ }
+ pkg, err = conf.Check(name, fset, files, nil)
+ if err != nil {
+ ctxt := "<no context>"
+ if w.context != nil {
+ ctxt = fmt.Sprintf("%s-%s", w.context.GOOS, w.context.GOARCH)
+ }
+ log.Fatalf("error typechecking package %s: %s (%s)", name, err, ctxt)
}
+
+ pkgCache[key] = pkg
+
+ w.imported[name] = pkg
+ return
}
// pushScope enters a new scope (walking a package, type, node, etc)
@@ -478,707 +541,307 @@ func (w *Walker) pushScope(name string) (popFunc func()) {
}
}
-func (w *Walker) recordTypes(file *ast.File) {
- for _, di := range file.Decls {
- switch d := di.(type) {
- case *ast.GenDecl:
- switch d.Tok {
- case token.TYPE:
- for _, sp := range d.Specs {
- ts := sp.(*ast.TypeSpec)
- name := ts.Name.Name
- if ast.IsExported(name) {
- if it, ok := ts.Type.(*ast.InterfaceType); ok {
- w.noteInterface(name, it)
- }
- }
- }
- }
- }
- }
-}
-
-func (w *Walker) walkFile(file *ast.File) {
- // Not entering a scope here; file boundaries aren't interesting.
- for _, di := range file.Decls {
- switch d := di.(type) {
- case *ast.GenDecl:
- switch d.Tok {
- case token.IMPORT:
- for _, sp := range d.Specs {
- is := sp.(*ast.ImportSpec)
- fpath, err := strconv.Unquote(is.Path.Value)
- if err != nil {
- log.Fatal(err)
- }
- name := path.Base(fpath)
- if is.Name != nil {
- name = is.Name.Name
- }
- w.selectorFullPkg[name] = fpath
- }
- case token.CONST:
- for _, sp := range d.Specs {
- w.walkConst(sp.(*ast.ValueSpec))
- }
- case token.TYPE:
- for _, sp := range d.Specs {
- w.walkTypeSpec(sp.(*ast.TypeSpec))
- }
- case token.VAR:
- for _, sp := range d.Specs {
- w.walkVar(sp.(*ast.ValueSpec))
- }
- default:
- log.Fatalf("unknown token type %d in GenDecl", d.Tok)
- }
- case *ast.FuncDecl:
- // Ignore. Handled in subsequent pass, by go/doc.
+func sortedMethodNames(typ *types.Interface) []string {
+ n := typ.NumMethods()
+ list := make([]string, n)
+ for i := range list {
+ list[i] = typ.Method(i).Name()
+ }
+ sort.Strings(list)
+ return list
+}
+
+func (w *Walker) writeType(buf *bytes.Buffer, typ types.Type) {
+ switch typ := typ.(type) {
+ case *types.Basic:
+ s := typ.Name()
+ switch typ.Kind() {
+ case types.UnsafePointer:
+ s = "unsafe.Pointer"
+ case types.UntypedBool:
+ s = "ideal-bool"
+ case types.UntypedInt:
+ s = "ideal-int"
+ case types.UntypedRune:
+ // "ideal-char" for compatibility with old tool
+ // TODO(gri) change to "ideal-rune"
+ s = "ideal-char"
+ case types.UntypedFloat:
+ s = "ideal-float"
+ case types.UntypedComplex:
+ s = "ideal-complex"
+ case types.UntypedString:
+ s = "ideal-string"
+ case types.UntypedNil:
+ panic("should never see untyped nil type")
+ default:
+ switch s {
+ case "byte":
+ s = "uint8"
+ case "rune":
+ s = "int32"
+ }
+ }
+ buf.WriteString(s)
+
+ case *types.Array:
+ fmt.Fprintf(buf, "[%d]", typ.Len())
+ w.writeType(buf, typ.Elem())
+
+ case *types.Slice:
+ buf.WriteString("[]")
+ w.writeType(buf, typ.Elem())
+
+ case *types.Struct:
+ buf.WriteString("struct")
+
+ case *types.Pointer:
+ buf.WriteByte('*')
+ w.writeType(buf, typ.Elem())
+
+ case *types.Tuple:
+ panic("should never see a tuple type")
+
+ case *types.Signature:
+ buf.WriteString("func")
+ w.writeSignature(buf, typ)
+
+ case *types.Interface:
+ buf.WriteString("interface{")
+ if typ.NumMethods() > 0 {
+ buf.WriteByte(' ')
+ buf.WriteString(strings.Join(sortedMethodNames(typ), ", "))
+ buf.WriteByte(' ')
+ }
+ buf.WriteString("}")
+
+ case *types.Map:
+ buf.WriteString("map[")
+ w.writeType(buf, typ.Key())
+ buf.WriteByte(']')
+ w.writeType(buf, typ.Elem())
+
+ case *types.Chan:
+ var s string
+ switch typ.Dir() {
+ case ast.SEND:
+ s = "chan<- "
+ case ast.RECV:
+ s = "<-chan "
default:
- log.Printf("unhandled %T, %#v\n", di, di)
- printer.Fprint(os.Stderr, fset, di)
- os.Stderr.Write([]byte("\n"))
+ s = "chan "
}
- }
-}
-
-var constType = map[token.Token]string{
- token.INT: "ideal-int",
- token.FLOAT: "ideal-float",
- token.STRING: "ideal-string",
- token.CHAR: "ideal-char",
- token.IMAG: "ideal-imag",
-}
-
-var varType = map[token.Token]string{
- token.INT: "int",
- token.FLOAT: "float64",
- token.STRING: "string",
- token.CHAR: "rune",
- token.IMAG: "complex128",
-}
-
-var errTODO = errors.New("TODO")
+ buf.WriteString(s)
+ w.writeType(buf, typ.Elem())
-func (w *Walker) constValueType(vi interface{}) (string, error) {
- switch v := vi.(type) {
- case *ast.BasicLit:
- litType, ok := constType[v.Kind]
- if !ok {
- return "", fmt.Errorf("unknown basic literal kind %#v", v)
+ case *types.Named:
+ obj := typ.Obj()
+ pkg := obj.Pkg()
+ if pkg != nil && pkg != w.current {
+ buf.WriteString(pkg.Name())
+ buf.WriteByte('.')
}
- return litType, nil
- case *ast.UnaryExpr:
- return w.constValueType(v.X)
- case *ast.SelectorExpr:
- lhs := w.nodeString(v.X)
- rhs := w.nodeString(v.Sel)
- pkg, ok := w.selectorFullPkg[lhs]
- if !ok {
- return "", fmt.Errorf("unknown constant reference; unknown package in expression %s.%s", lhs, rhs)
- }
- if t, ok := w.prevConstType[pkgSymbol{pkg, rhs}]; ok {
- return t, nil
- }
- return "", fmt.Errorf("unknown constant reference to %s.%s", lhs, rhs)
- case *ast.Ident:
- if v.Name == "iota" {
- return "ideal-int", nil // hack.
- }
- if v.Name == "false" || v.Name == "true" {
- return "bool", nil
- }
- if v.Name == "intSize" && w.curPackageName == "strconv" {
- // Hack.
- return "ideal-int", nil
- }
- if t, ok := w.prevConstType[pkgSymbol{w.curPackageName, v.Name}]; ok {
- return t, nil
- }
- return constDepPrefix + v.Name, nil
- case *ast.BinaryExpr:
- switch v.Op {
- case token.EQL, token.LSS, token.GTR, token.NOT, token.NEQ, token.LEQ, token.GEQ:
- return "bool", nil
- }
- left, err := w.constValueType(v.X)
- if err != nil {
- return "", err
- }
- right, err := w.constValueType(v.Y)
- if err != nil {
- return "", err
- }
- if left != right {
- // TODO(bradfitz): encode the real rules here,
- // rather than this mess.
- if left == "ideal-int" && right == "ideal-float" {
- return "ideal-float", nil // math.Log2E
- }
- if left == "ideal-char" && right == "ideal-int" {
- return "ideal-int", nil // math/big.MaxBase
- }
- if left == "ideal-int" && right == "ideal-char" {
- return "ideal-int", nil // text/scanner.GoWhitespace
- }
- if left == "ideal-int" && right == "Duration" {
- // Hack, for package time.
- return "Duration", nil
- }
- if left == "ideal-int" && !strings.HasPrefix(right, "ideal-") {
- return right, nil
- }
- if right == "ideal-int" && !strings.HasPrefix(left, "ideal-") {
- return left, nil
- }
- if strings.HasPrefix(left, constDepPrefix) && strings.HasPrefix(right, constDepPrefix) {
- // Just pick one.
- // e.g. text/scanner GoTokens const-dependency:ScanIdents, const-dependency:ScanFloats
- return left, nil
- }
- return "", fmt.Errorf("in BinaryExpr, unhandled type mismatch; left=%q, right=%q", left, right)
- }
- return left, nil
- case *ast.CallExpr:
- // Not a call, but a type conversion.
- return w.nodeString(v.Fun), nil
- case *ast.ParenExpr:
- return w.constValueType(v.X)
- }
- return "", fmt.Errorf("unknown const value type %T", vi)
-}
+ buf.WriteString(typ.Obj().Name())
-func (w *Walker) varValueType(vi interface{}) (string, error) {
- switch v := vi.(type) {
- case *ast.BasicLit:
- litType, ok := varType[v.Kind]
- if !ok {
- return "", fmt.Errorf("unknown basic literal kind %#v", v)
- }
- return litType, nil
- case *ast.CompositeLit:
- return w.nodeString(v.Type), nil
- case *ast.FuncLit:
- return w.nodeString(w.namelessType(v.Type)), nil
- case *ast.UnaryExpr:
- if v.Op == token.AND {
- typ, err := w.varValueType(v.X)
- return "*" + typ, err
- }
- return "", fmt.Errorf("unknown unary expr: %#v", v)
- case *ast.SelectorExpr:
- return "", errTODO
- case *ast.Ident:
- node, _, ok := w.resolveName(v.Name)
- if !ok {
- return "", fmt.Errorf("unresolved identifier: %q", v.Name)
- }
- return w.varValueType(node)
- case *ast.BinaryExpr:
- left, err := w.varValueType(v.X)
- if err != nil {
- return "", err
- }
- right, err := w.varValueType(v.Y)
- if err != nil {
- return "", err
- }
- if left != right {
- return "", fmt.Errorf("in BinaryExpr, unhandled type mismatch; left=%q, right=%q", left, right)
- }
- return left, nil
- case *ast.ParenExpr:
- return w.varValueType(v.X)
- case *ast.CallExpr:
- var funSym pkgSymbol
- if selnode, ok := v.Fun.(*ast.SelectorExpr); ok {
- // assume it is not a method.
- pkg, ok := w.selectorFullPkg[w.nodeString(selnode.X)]
- if !ok {
- return "", fmt.Errorf("not a package: %s", w.nodeString(selnode.X))
- }
- funSym = pkgSymbol{pkg, selnode.Sel.Name}
- if retType, ok := w.functionTypes[funSym]; ok {
- if ast.IsExported(retType) && pkg != w.curPackageName {
- // otherpkg.F returning an exported type from otherpkg.
- return pkg + "." + retType, nil
- } else {
- return retType, nil
- }
- }
- } else {
- funSym = pkgSymbol{w.curPackageName, w.nodeString(v.Fun)}
- if retType, ok := w.functionTypes[funSym]; ok {
- return retType, nil
- }
- }
- // maybe a function call; maybe a conversion. Need to lookup type.
- // TODO(bradfitz): this is a hack, but arguably most of this tool is,
- // until the Go AST has type information.
- nodeStr := w.nodeString(v.Fun)
- switch nodeStr {
- case "string", "[]byte":
- return nodeStr, nil
- }
- return "", fmt.Errorf("not a known function %q", nodeStr)
default:
- return "", fmt.Errorf("unknown const value type %T", vi)
+ panic(fmt.Sprintf("unknown type %T", typ))
}
}
-// resolveName finds a top-level node named name and returns the node
-// v and its type t, if known.
-func (w *Walker) resolveName(name string) (v interface{}, t interface{}, ok bool) {
- for _, file := range w.curPackage.Files {
- for _, di := range file.Decls {
- switch d := di.(type) {
- case *ast.GenDecl:
- switch d.Tok {
- case token.VAR:
- for _, sp := range d.Specs {
- vs := sp.(*ast.ValueSpec)
- for i, vname := range vs.Names {
- if vname.Name == name {
- if len(vs.Values) > i {
- return vs.Values[i], vs.Type, true
- }
- return nil, vs.Type, true
- }
- }
- }
- }
- }
- }
+func (w *Walker) writeSignature(buf *bytes.Buffer, sig *types.Signature) {
+ w.writeParams(buf, sig.Params(), sig.IsVariadic())
+ switch res := sig.Results(); res.Len() {
+ case 0:
+ // nothing to do
+ case 1:
+ buf.WriteByte(' ')
+ w.writeType(buf, res.At(0).Type())
+ default:
+ buf.WriteByte(' ')
+ w.writeParams(buf, res, false)
}
- return nil, nil, false
}
-// constDepPrefix is a magic prefix that is used by constValueType
-// and walkConst to signal that a type isn't known yet. These are
-// resolved at the end of walking of a package's files.
-const constDepPrefix = "const-dependency:"
-
-func (w *Walker) walkConst(vs *ast.ValueSpec) {
- for _, ident := range vs.Names {
- litType := ""
- if vs.Type != nil {
- litType = w.nodeString(vs.Type)
- } else {
- litType = w.lastConstType
- if vs.Values != nil {
- if len(vs.Values) != 1 {
- log.Fatalf("const %q, values: %#v", ident.Name, vs.Values)
- }
- var err error
- litType, err = w.constValueType(vs.Values[0])
- if err != nil {
- log.Fatalf("unknown kind in const %q (%T): %v", ident.Name, vs.Values[0], err)
- }
- }
- }
- if dep := strings.TrimPrefix(litType, constDepPrefix); dep != litType {
- w.constDep[ident.Name] = dep
- continue
- }
- if litType == "" {
- log.Fatalf("unknown kind in const %q", ident.Name)
+func (w *Walker) writeParams(buf *bytes.Buffer, t *types.Tuple, variadic bool) {
+ buf.WriteByte('(')
+ for i, n := 0, t.Len(); i < n; i++ {
+ if i > 0 {
+ buf.WriteString(", ")
}
- w.lastConstType = litType
-
- w.prevConstType[pkgSymbol{w.curPackageName, ident.Name}] = litType
-
- if ast.IsExported(ident.Name) {
- w.emitFeature(fmt.Sprintf("const %s %s", ident, litType))
+ typ := t.At(i).Type()
+ if variadic && i+1 == n {
+ buf.WriteString("...")
+ typ = typ.(*types.Slice).Elem()
}
+ w.writeType(buf, typ)
}
+ buf.WriteByte(')')
}
-func (w *Walker) resolveConstantDeps() {
- var findConstType func(string) string
- findConstType = func(ident string) string {
- if dep, ok := w.constDep[ident]; ok {
- return findConstType(dep)
- }
- if t, ok := w.prevConstType[pkgSymbol{w.curPackageName, ident}]; ok {
- return t
- }
- return ""
- }
- for ident := range w.constDep {
- if !ast.IsExported(ident) {
- continue
- }
- t := findConstType(ident)
- if t == "" {
- log.Fatalf("failed to resolve constant %q", ident)
- }
- w.emitFeature(fmt.Sprintf("const %s %s", ident, t))
- }
+func (w *Walker) typeString(typ types.Type) string {
+ var buf bytes.Buffer
+ w.writeType(&buf, typ)
+ return buf.String()
}
-func (w *Walker) walkVar(vs *ast.ValueSpec) {
- for i, ident := range vs.Names {
- if !ast.IsExported(ident.Name) {
- continue
- }
-
- typ := ""
- if vs.Type != nil {
- typ = w.nodeString(vs.Type)
- } else {
- if len(vs.Values) == 0 {
- log.Fatalf("no values for var %q", ident.Name)
- }
- if len(vs.Values) > 1 {
- log.Fatalf("more than 1 values in ValueSpec not handled, var %q", ident.Name)
- }
- var err error
- typ, err = w.varValueType(vs.Values[i])
- if err != nil {
- log.Fatalf("unknown type of variable %q, type %T, error = %v\ncode: %s",
- ident.Name, vs.Values[i], err, w.nodeString(vs.Values[i]))
- }
- }
- w.emitFeature(fmt.Sprintf("var %s %s", ident, typ))
- }
-}
-
-func (w *Walker) nodeString(node interface{}) string {
- if node == nil {
- return ""
- }
- var b bytes.Buffer
- printer.Fprint(&b, fset, node)
- return b.String()
+func (w *Walker) signatureString(sig *types.Signature) string {
+ var buf bytes.Buffer
+ w.writeSignature(&buf, sig)
+ return buf.String()
}
-func (w *Walker) nodeDebug(node interface{}) string {
- if node == nil {
- return ""
+func (w *Walker) emitObj(obj types.Object) {
+ switch obj := obj.(type) {
+ case *types.Const:
+ w.emitf("const %s %s", obj.Name(), w.typeString(obj.Type()))
+ w.emitf("const %s = %s", obj.Name(), obj.Val())
+ case *types.Var:
+ w.emitf("var %s %s", obj.Name(), w.typeString(obj.Type()))
+ case *types.TypeName:
+ w.emitType(obj)
+ case *types.Func:
+ w.emitFunc(obj)
+ default:
+ panic("unknown object: " + obj.String())
}
- var b bytes.Buffer
- ast.Fprint(&b, fset, node, nil)
- return b.String()
-}
-
-func (w *Walker) noteInterface(name string, it *ast.InterfaceType) {
- w.interfaces[pkgSymbol{w.curPackageName, name}] = it
}
-func (w *Walker) walkTypeSpec(ts *ast.TypeSpec) {
- name := ts.Name.Name
- if !ast.IsExported(name) {
- return
- }
- switch t := ts.Type.(type) {
- case *ast.StructType:
- w.walkStructType(name, t)
- case *ast.InterfaceType:
- w.walkInterfaceType(name, t)
+func (w *Walker) emitType(obj *types.TypeName) {
+ name := obj.Name()
+ typ := obj.Type()
+ switch typ := typ.Underlying().(type) {
+ case *types.Struct:
+ w.emitStructType(name, typ)
+ case *types.Interface:
+ w.emitIfaceType(name, typ)
+ return // methods are handled by emitIfaceType
default:
- w.emitFeature(fmt.Sprintf("type %s %s", name, w.nodeString(w.namelessType(ts.Type))))
+ w.emitf("type %s %s", name, w.typeString(typ.Underlying()))
}
-}
-func (w *Walker) walkStructType(name string, t *ast.StructType) {
- typeStruct := fmt.Sprintf("type %s struct", name)
- w.emitFeature(typeStruct)
- pop := w.pushScope(typeStruct)
- defer pop()
- for _, f := range t.Fields.List {
- typ := f.Type
- for _, name := range f.Names {
- if ast.IsExported(name.Name) {
- w.emitFeature(fmt.Sprintf("%s %s", name, w.nodeString(w.namelessType(typ))))
+ // emit methods with value receiver
+ var methodNames map[string]bool
+ vset := typ.MethodSet()
+ for i, n := 0, vset.Len(); i < n; i++ {
+ m := vset.At(i)
+ if m.Obj().IsExported() {
+ w.emitMethod(m)
+ if methodNames == nil {
+ methodNames = make(map[string]bool)
}
+ methodNames[m.Obj().Name()] = true
}
- if f.Names == nil {
- switch v := typ.(type) {
- case *ast.Ident:
- if ast.IsExported(v.Name) {
- w.emitFeature(fmt.Sprintf("embedded %s", v.Name))
- }
- case *ast.StarExpr:
- switch vv := v.X.(type) {
- case *ast.Ident:
- if ast.IsExported(vv.Name) {
- w.emitFeature(fmt.Sprintf("embedded *%s", vv.Name))
- }
- case *ast.SelectorExpr:
- w.emitFeature(fmt.Sprintf("embedded %s", w.nodeString(typ)))
- default:
- log.Fatalf("unable to handle embedded starexpr before %T", typ)
- }
- case *ast.SelectorExpr:
- w.emitFeature(fmt.Sprintf("embedded %s", w.nodeString(typ)))
- default:
- log.Fatalf("unable to handle embedded %T", typ)
- }
+ }
+
+ // emit methods with pointer receiver; exclude
+ // methods that we have emitted already
+ // (the method set of *T includes the methods of T)
+ pset := types.NewPointer(typ).MethodSet()
+ for i, n := 0, pset.Len(); i < n; i++ {
+ m := pset.At(i)
+ if m.Obj().IsExported() && !methodNames[m.Obj().Name()] {
+ w.emitMethod(m)
}
}
}
-// method is a method of an interface.
-type method struct {
- name string // "Read"
- sig string // "([]byte) (int, error)", from funcSigString
-}
+func (w *Walker) emitStructType(name string, typ *types.Struct) {
+ typeStruct := fmt.Sprintf("type %s struct", name)
+ w.emitf(typeStruct)
+ defer w.pushScope(typeStruct)()
-// interfaceMethods returns the expanded list of exported methods for an interface.
-// The boolean complete reports whether the list contains all methods (that is, the
-// interface has no unexported methods).
-// pkg is the complete package name ("net/http")
-// iname is the interface name.
-func (w *Walker) interfaceMethods(pkg, iname string) (methods []method, complete bool) {
- t, ok := w.interfaces[pkgSymbol{pkg, iname}]
- if !ok {
- log.Fatalf("failed to find interface %s.%s", pkg, iname)
- }
-
- complete = true
- for _, f := range t.Methods.List {
- typ := f.Type
- switch tv := typ.(type) {
- case *ast.FuncType:
- for _, mname := range f.Names {
- if ast.IsExported(mname.Name) {
- ft := typ.(*ast.FuncType)
- methods = append(methods, method{
- name: mname.Name,
- sig: w.funcSigString(ft),
- })
- } else {
- complete = false
- }
- }
- case *ast.Ident:
- embedded := typ.(*ast.Ident).Name
- if embedded == "error" {
- methods = append(methods, method{
- name: "Error",
- sig: "() string",
- })
- continue
- }
- if !ast.IsExported(embedded) {
- log.Fatalf("unexported embedded interface %q in exported interface %s.%s; confused",
- embedded, pkg, iname)
- }
- m, c := w.interfaceMethods(pkg, embedded)
- methods = append(methods, m...)
- complete = complete && c
- case *ast.SelectorExpr:
- lhs := w.nodeString(tv.X)
- rhs := w.nodeString(tv.Sel)
- fpkg, ok := w.selectorFullPkg[lhs]
- if !ok {
- log.Fatalf("can't resolve selector %q in interface %s.%s", lhs, pkg, iname)
- }
- m, c := w.interfaceMethods(fpkg, rhs)
- methods = append(methods, m...)
- complete = complete && c
- default:
- log.Fatalf("unknown type %T in interface field", typ)
+ for i := 0; i < typ.NumFields(); i++ {
+ f := typ.Field(i)
+ if !f.IsExported() {
+ continue
+ }
+ typ := f.Type()
+ if f.Anonymous() {
+ w.emitf("embedded %s", w.typeString(typ))
+ continue
}
+ w.emitf("%s %s", f.Name(), w.typeString(typ))
}
- return
}
-func (w *Walker) walkInterfaceType(name string, t *ast.InterfaceType) {
- methNames := []string{}
+func (w *Walker) emitIfaceType(name string, typ *types.Interface) {
pop := w.pushScope("type " + name + " interface")
- methods, complete := w.interfaceMethods(w.curPackageName, name)
- for _, m := range methods {
- methNames = append(methNames, m.name)
- w.emitFeature(fmt.Sprintf("%s%s", m.name, m.sig))
+
+ var methodNames []string
+ complete := true
+ mset := typ.MethodSet()
+ for i, n := 0, mset.Len(); i < n; i++ {
+ m := mset.At(i).Obj().(*types.Func)
+ if !m.IsExported() {
+ complete = false
+ continue
+ }
+ methodNames = append(methodNames, m.Name())
+ w.emitf("%s%s", m.Name(), w.signatureString(m.Type().(*types.Signature)))
}
+
if !complete {
// The method set has unexported methods, so all the
// implementations are provided by the same package,
// so the method set can be extended. Instead of recording
// the full set of names (below), record only that there were
// unexported methods. (If the interface shrinks, we will notice
- // because a method signature emitted during the last loop,
+ // because a method signature emitted during the last loop
// will disappear.)
- w.emitFeature("unexported methods")
+ w.emitf("unexported methods")
}
+
pop()
if !complete {
return
}
- sort.Strings(methNames)
- if len(methNames) == 0 {
- w.emitFeature(fmt.Sprintf("type %s interface {}", name))
- } else {
- w.emitFeature(fmt.Sprintf("type %s interface { %s }", name, strings.Join(methNames, ", ")))
- }
-}
-
-func (w *Walker) peekFuncDecl(f *ast.FuncDecl) {
- if f.Recv != nil {
+ if len(methodNames) == 0 {
+ w.emitf("type %s interface {}", name)
return
}
- // Record return type for later use.
- if f.Type.Results != nil && len(f.Type.Results.List) == 1 {
- retType := w.nodeString(w.namelessType(f.Type.Results.List[0].Type))
- w.functionTypes[pkgSymbol{w.curPackageName, f.Name.Name}] = retType
- }
-}
-func (w *Walker) walkFuncDecl(f *ast.FuncDecl) {
- if !ast.IsExported(f.Name.Name) {
- return
- }
- if f.Recv != nil {
- // Method.
- recvType := w.nodeString(f.Recv.List[0].Type)
- keep := ast.IsExported(recvType) ||
- (strings.HasPrefix(recvType, "*") &&
- ast.IsExported(recvType[1:]))
- if !keep {
- return
- }
- w.emitFeature(fmt.Sprintf("method (%s) %s%s", recvType, f.Name.Name, w.funcSigString(f.Type)))
- return
- }
- // Else, a function
- w.emitFeature(fmt.Sprintf("func %s%s", f.Name.Name, w.funcSigString(f.Type)))
-}
-
-func (w *Walker) funcSigString(ft *ast.FuncType) string {
- var b bytes.Buffer
- writeField := func(b *bytes.Buffer, f *ast.Field) {
- if n := len(f.Names); n > 1 {
- for i := 0; i < n; i++ {
- if i > 0 {
- b.WriteString(", ")
- }
- b.WriteString(w.nodeString(w.namelessType(f.Type)))
- }
- } else {
- b.WriteString(w.nodeString(w.namelessType(f.Type)))
- }
- }
- b.WriteByte('(')
- if ft.Params != nil {
- for i, f := range ft.Params.List {
- if i > 0 {
- b.WriteString(", ")
- }
- writeField(&b, f)
- }
- }
- b.WriteByte(')')
- if ft.Results != nil {
- nr := 0
- for _, f := range ft.Results.List {
- if n := len(f.Names); n > 1 {
- nr += n
- } else {
- nr++
- }
- }
- if nr > 0 {
- b.WriteByte(' ')
- if nr > 1 {
- b.WriteByte('(')
- }
- for i, f := range ft.Results.List {
- if i > 0 {
- b.WriteString(", ")
- }
- writeField(&b, f)
- }
- if nr > 1 {
- b.WriteByte(')')
- }
- }
- }
- return b.String()
+ sort.Strings(methodNames)
+ w.emitf("type %s interface { %s }", name, strings.Join(methodNames, ", "))
}
-// namelessType returns a type node that lacks any variable names.
-func (w *Walker) namelessType(t interface{}) interface{} {
- ft, ok := t.(*ast.FuncType)
- if !ok {
- return t
- }
- return &ast.FuncType{
- Params: w.namelessFieldList(ft.Params),
- Results: w.namelessFieldList(ft.Results),
+func (w *Walker) emitFunc(f *types.Func) {
+ sig := f.Type().(*types.Signature)
+ if sig.Recv() != nil {
+ panic("method considered a regular function: " + f.String())
}
+ w.emitf("func %s%s", f.Name(), w.signatureString(sig))
}
-// namelessFieldList returns a deep clone of fl, with the cloned fields
-// lacking names.
-func (w *Walker) namelessFieldList(fl *ast.FieldList) *ast.FieldList {
- fl2 := &ast.FieldList{}
- if fl != nil {
- for _, f := range fl.List {
- repeats := 1
- if len(f.Names) > 1 {
- repeats = len(f.Names)
- }
- for i := 0; i < repeats; i++ {
- fl2.List = append(fl2.List, w.namelessField(f))
- }
+func (w *Walker) emitMethod(m *types.Selection) {
+ sig := m.Type().(*types.Signature)
+ recv := sig.Recv().Type()
+ // report exported methods with unexported reveiver base type
+ if true {
+ base := recv
+ if p, _ := recv.(*types.Pointer); p != nil {
+ base = p.Elem()
+ }
+ if obj := base.(*types.Named).Obj(); !obj.IsExported() {
+ log.Fatalf("exported method with unexported receiver base type: %s", m)
}
}
- return fl2
+ w.emitf("method (%s) %s%s", w.typeString(recv), m.Obj().Name(), w.signatureString(sig))
}
-// namelessField clones f, but not preserving the names of fields.
-// (comments and tags are also ignored)
-func (w *Walker) namelessField(f *ast.Field) *ast.Field {
- return &ast.Field{
- Type: f.Type,
+func (w *Walker) emitf(format string, args ...interface{}) {
+ f := strings.Join(w.scope, ", ") + ", " + fmt.Sprintf(format, args...)
+ if strings.Contains(f, "\n") {
+ panic("feature contains newlines: " + f)
}
-}
-
-var (
- byteRx = regexp.MustCompile(`\bbyte\b`)
- runeRx = regexp.MustCompile(`\brune\b`)
-)
-func (w *Walker) emitFeature(feature string) {
- if !w.wantedPkg[w.curPackageName] {
- return
- }
- if strings.Contains(feature, "byte") {
- feature = byteRx.ReplaceAllString(feature, "uint8")
- }
- if strings.Contains(feature, "rune") {
- feature = runeRx.ReplaceAllString(feature, "int32")
- }
- f := strings.Join(w.scope, ", ") + ", " + feature
if _, dup := w.features[f]; dup {
panic("duplicate feature inserted: " + f)
}
-
- if strings.Contains(f, "\n") {
- // TODO: for now, just skip over the
- // runtime.MemStatsType.BySize type, which this tool
- // doesn't properly handle. It's pretty low-level,
- // though, so not super important to protect against.
- if strings.HasPrefix(f, "pkg runtime") && strings.Contains(f, "BySize [61]struct") {
- return
- }
- panic("feature contains newlines: " + f)
- }
-
w.features[f] = true
+
if *verbose {
log.Printf("feature: %s", f)
}
}
-
-func strListContains(l []string, s string) bool {
- for _, v := range l {
- if v == s {
- return true
- }
- }
- return false
-}
diff --git a/src/cmd/api/goapi_test.go b/src/cmd/api/goapi_test.go
index 1a86c0ec7..b909c32b3 100644
--- a/src/cmd/api/goapi_test.go
+++ b/src/cmd/api/goapi_test.go
@@ -1,3 +1,5 @@
+// +build api_tool
+
// 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.
@@ -8,8 +10,10 @@ import (
"bytes"
"flag"
"fmt"
+ "go/build"
"io/ioutil"
"os"
+ "os/exec"
"path/filepath"
"sort"
"strings"
@@ -33,12 +37,10 @@ func TestGolden(t *testing.T) {
if !fi.IsDir() {
continue
}
- w := NewWalker()
- w.wantedPkg[fi.Name()] = true
- w.root = "testdata/src/pkg"
goldenFile := filepath.Join("testdata", "src", "pkg", fi.Name(), "golden.txt")
- w.WalkPackage(fi.Name())
+ w := NewWalker(nil, "testdata/src/pkg")
+ w.export(w.Import(fi.Name()))
if *updateGolden {
os.Remove(goldenFile)
@@ -110,7 +112,7 @@ func TestCompareAPI(t *testing.T) {
features: []string{"A", "C"},
exception: []string{"B"},
ok: true,
- out: "~B\n",
+ out: "",
},
{
// http://golang.org/issue/4303
@@ -139,3 +141,28 @@ func TestCompareAPI(t *testing.T) {
}
}
}
+
+func BenchmarkAll(b *testing.B) {
+ stds, err := exec.Command("go", "list", "std").Output()
+ if err != nil {
+ b.Fatal(err)
+ }
+ b.ResetTimer()
+ pkgNames := strings.Fields(string(stds))
+
+ for _, c := range contexts {
+ c.Compiler = build.Default.Compiler
+ }
+
+ for i := 0; i < b.N; i++ {
+ for _, context := range contexts {
+ w := NewWalker(context, filepath.Join(build.Default.GOROOT, "src/pkg"))
+ for _, name := range pkgNames {
+ if name != "unsafe" && !strings.HasPrefix(name, "cmd/") {
+ w.export(w.Import(name))
+ }
+ }
+ w.Features()
+ }
+ }
+}
diff --git a/src/cmd/api/run.go b/src/cmd/api/run.go
new file mode 100644
index 000000000..1e10dc600
--- /dev/null
+++ b/src/cmd/api/run.go
@@ -0,0 +1,186 @@
+// Copyright 2013 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.
+
+// +build ignore
+
+// The run program is invoked via "go run" from src/run.bash or
+// src/run.bat conditionally builds and runs the cmd/api tool.
+//
+// TODO(bradfitz): the "conditional" condition is always true.
+// We should only do this if the user has the hg codereview extension
+// enabled and verifies that the go.tools subrepo is checked out with
+// a suitably recently version. In prep for the cmd/api rewrite.
+package main
+
+import (
+ "fmt"
+ "log"
+ "net/http"
+ "os"
+ "os/exec"
+ "os/user"
+ "path/filepath"
+ "strings"
+)
+
+// goToolsVersion is the hg revision of the go.tools subrepo we need
+// to build cmd/api. This only needs to be updated whenever a go/types
+// bug fix is needed by the cmd/api tool.
+const goToolsVersion = "6698ca2900e2"
+
+var goroot string
+
+func main() {
+ log.SetFlags(0)
+ goroot = os.Getenv("GOROOT") // should be set by run.{bash,bat}
+ if goroot == "" {
+ log.Fatal("No $GOROOT set.")
+ }
+ _, err := exec.LookPath("hg")
+ if err != nil {
+ fmt.Println("Skipping cmd/api checks; hg not available")
+ return
+ }
+
+ gopath := prepGoPath()
+
+ cmd := exec.Command("go", "install", "--tags=api_tool", "cmd/api")
+ cmd.Env = append([]string{"GOPATH=" + gopath}, filterOut(os.Environ(), "GOARCH")...)
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ log.Fatalf("Error installing cmd/api: %v\n%s", err, out)
+ }
+
+ out, err = exec.Command("go", "tool", "api",
+ "-c", file("go1", "go1.1", "go1.2"),
+ "-next", file("next"),
+ "-except", file("except")).CombinedOutput()
+ if err != nil {
+ log.Fatalf("Error running API checker: %v\n%s", err, out)
+ }
+ fmt.Print(string(out))
+}
+
+// filterOut returns a copy of the src environment without environment
+// variables from remove.
+// TODO: delete when issue 6201 is fixed.
+func filterOut(src []string, remove ...string) (out []string) {
+S:
+ for _, s := range src {
+ for _, r := range remove {
+ if strings.HasPrefix(s, r) && strings.HasPrefix(s, r+"=") {
+ continue S
+ }
+ }
+ out = append(out, s)
+ }
+ return
+}
+
+// file expands s to $GOROOT/api/s.txt.
+// If there are more than 1, they're comma-separated.
+func file(s ...string) string {
+ if len(s) > 1 {
+ return file(s[0]) + "," + file(s[1:]...)
+ }
+ return filepath.Join(goroot, "api", s[0]+".txt")
+}
+
+// prepGoPath returns a GOPATH for the "go" tool to compile the API tool with.
+// It tries to re-use a go.tools checkout from a previous run if possible,
+// else it hg clones it.
+func prepGoPath() string {
+ const tempBase = "go.tools.TMP"
+
+ username := ""
+ u, err := user.Current()
+ if err == nil {
+ username = u.Username
+ } else {
+ // Only need to handle Unix here, as Windows's os/user uses
+ // native syscall and should work fine without cgo.
+ username = os.Getenv("USER")
+ if username == "" {
+ log.Fatalf("Error getting current user: %v", err)
+ }
+ }
+
+ // The GOPATH we'll return
+ gopath := filepath.Join(os.TempDir(), "gopath-api-"+cleanUsername(username), goToolsVersion)
+
+ // cloneDir is where we run "hg clone".
+ cloneDir := filepath.Join(gopath, "src", "code.google.com", "p")
+
+ // The dir we clone into. We only atomically rename it to finalDir on
+ // clone success.
+ tmpDir := filepath.Join(cloneDir, tempBase)
+
+ // finalDir is where the checkout will live once it's complete.
+ finalDir := filepath.Join(cloneDir, "go.tools")
+
+ if goToolsCheckoutGood(finalDir) {
+ return gopath
+ }
+ os.RemoveAll(finalDir) // in case it's there but corrupt
+ os.RemoveAll(tmpDir) // in case of aborted hg clone before
+
+ if err := os.MkdirAll(cloneDir, 0700); err != nil {
+ log.Fatal(err)
+ }
+ cmd := exec.Command("hg",
+ "clone", "--rev="+goToolsVersion,
+ "https://code.google.com/p/go.tools",
+ tempBase)
+ cmd.Dir = cloneDir
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ if _, err := http.Head("http://ip.appspot.com/"); err != nil {
+ log.Printf("# Skipping API check; network appears to be unavailable")
+ os.Exit(0)
+ }
+ log.Fatalf("Error running hg clone on go.tools: %v\n%s", err, out)
+ }
+ if err := os.Rename(tmpDir, finalDir); err != nil {
+ log.Fatal(err)
+ }
+ return gopath
+}
+
+func cleanUsername(n string) string {
+ b := make([]rune, len(n))
+ for i, r := range n {
+ if r == '\\' || r == '/' || r == ':' {
+ b[i] = '_'
+ } else {
+ b[i] = r
+ }
+ }
+ return string(b)
+}
+
+func goToolsCheckoutGood(dir string) bool {
+ if _, err := os.Stat(dir); err != nil {
+ return false
+ }
+
+ cmd := exec.Command("hg", "id", "--id")
+ cmd.Dir = dir
+ out, err := cmd.Output()
+ if err != nil {
+ return false
+ }
+ id := strings.TrimSpace(string(out))
+ if id != goToolsVersion {
+ return false
+ }
+
+ cmd = exec.Command("hg", "status")
+ cmd.Dir = dir
+ out, err = cmd.Output()
+ if err != nil || len(out) > 0 {
+ return false
+ }
+
+ return true
+}
diff --git a/src/cmd/api/testdata/src/pkg/p1/golden.txt b/src/cmd/api/testdata/src/pkg/p1/golden.txt
index abcc0ce6c..3c43a226f 100644
--- a/src/cmd/api/testdata/src/pkg/p1/golden.txt
+++ b/src/cmd/api/testdata/src/pkg/p1/golden.txt
@@ -1,15 +1,24 @@
+pkg p1, const A = 1
pkg p1, const A ideal-int
+pkg p1, const A64 = 1
pkg p1, const A64 int64
+pkg p1, const AIsLowerA = 11
pkg p1, const AIsLowerA ideal-int
-pkg p1, const B ideal-int
+pkg p1, const B0 = 2
+pkg p1, const B0 ideal-int
+pkg p1, const ConstChase2 = 11
pkg p1, const ConstChase2 ideal-int
+pkg p1, const ConversionConst = 5
pkg p1, const ConversionConst MyInt
+pkg p1, const FloatConst = 3/2
pkg p1, const FloatConst ideal-float
+pkg p1, const StrConst = "foo"
pkg p1, const StrConst ideal-string
pkg p1, func Bar(int8, int16, int64)
pkg p1, func Bar1(int8, int16, int64) uint64
pkg p1, func Bar2(int8, int16, int64) (uint8, uint64)
pkg p1, func BarE() Error
+pkg p1, func Now() Time
pkg p1, func PlainFunc(int, int, string) (*B, error)
pkg p1, func TakesFunc(func(int) int)
pkg p1, method (*B) JustOnB()
@@ -34,9 +43,9 @@ pkg p1, type ByteStruct struct, R int32
pkg p1, type Codec struct
pkg p1, type Codec struct, Func func(int, int) int
pkg p1, type EmbedSelector struct
-pkg p1, type EmbedSelector struct, embedded time.Time
+pkg p1, type EmbedSelector struct, embedded Time
pkg p1, type EmbedURLPtr struct
-pkg p1, type EmbedURLPtr struct, embedded *url.URL
+pkg p1, type EmbedURLPtr struct, embedded *URL
pkg p1, type Embedded struct
pkg p1, type Error interface { Error, Temporary }
pkg p1, type Error interface, Error() string
@@ -58,7 +67,7 @@ pkg p1, type Public interface, X()
pkg p1, type Public interface, Y()
pkg p1, type S struct
pkg p1, type S struct, Public *int
-pkg p1, type S struct, PublicTime time.Time
+pkg p1, type S struct, PublicTime Time
pkg p1, type S2 struct
pkg p1, type S2 struct, Extra bool
pkg p1, type S2 struct, embedded S
@@ -68,6 +77,8 @@ pkg p1, type T struct
pkg p1, type TPtrExported struct
pkg p1, type TPtrExported struct, embedded *Embedded
pkg p1, type TPtrUnexported struct
+pkg p1, type Time struct
+pkg p1, type URL struct
pkg p1, var Byte uint8
pkg p1, var ByteConv []uint8
pkg p1, var ByteFunc func(uint8) int32
@@ -81,5 +92,5 @@ pkg p1, var V1 uint64
pkg p1, var V2 p2.Twoer
pkg p1, var VError Error
pkg p1, var X I
-pkg p1, var X int64
+pkg p1, var X0 int64
pkg p1, var Y int
diff --git a/src/cmd/api/testdata/src/pkg/p1/p1.go b/src/cmd/api/testdata/src/pkg/p1/p1.go
index f94c9ceeb..65181b248 100644
--- a/src/cmd/api/testdata/src/pkg/p1/p1.go
+++ b/src/cmd/api/testdata/src/pkg/p1/p1.go
@@ -35,7 +35,7 @@ var (
var ChecksumError = ptwo.NewError("gzip checksum error")
-const B = 2
+const B0 = 2
const StrConst = "foo"
const FloatConst = 1.5
@@ -43,14 +43,18 @@ type myInt int
type MyInt int
+type Time struct{}
+
type S struct {
Public *int
private *int
- PublicTime time.Time
+ PublicTime Time
}
+type URL struct{}
+
type EmbedURLPtr struct {
- *url.URL
+ *URL
}
type S2 struct {
@@ -58,7 +62,7 @@ type S2 struct {
Extra bool
}
-var X int64
+var X0 int64
var (
Y int
@@ -163,7 +167,7 @@ func (*common) OnBothTandBPtr() {}
func (common) OnBothTandBVal() {}
type EmbedSelector struct {
- time.Time
+ Time
}
const (
@@ -174,10 +178,15 @@ const (
func ellipsis(...string) {}
+func Now() Time {
+ var now Time
+ return now
+}
+
var x = &S{
Public: nil,
private: nil,
- publicTime: time.Now(),
+ PublicTime: Now(),
}
var parenExpr = (1 + 5)
diff --git a/src/cmd/cc/bv.c b/src/cmd/cc/bv.c
new file mode 100644
index 000000000..51b7f4076
--- /dev/null
+++ b/src/cmd/cc/bv.c
@@ -0,0 +1,45 @@
+// Copyright 2013 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 <u.h>
+#include "cc.h"
+
+enum {
+ WORDSIZE = sizeof(uint32),
+ WORDBITS = 32,
+};
+
+uintptr
+bvsize(uintptr n)
+{
+ return ((n + WORDBITS - 1) / WORDBITS) * WORDSIZE;
+}
+
+Bvec*
+bvalloc(int32 n)
+{
+ Bvec *bv;
+ uintptr nbytes;
+
+ if(n < 0)
+ fatal(Z, "bvalloc: initial size is negative\n");
+ nbytes = sizeof(Bvec) + bvsize(n);
+ bv = malloc(nbytes);
+ if(bv == nil)
+ fatal(Z, "bvalloc: malloc failed\n");
+ memset(bv, 0, nbytes);
+ bv->n = n;
+ return bv;
+}
+
+void
+bvset(Bvec *bv, int32 i)
+{
+ uint32 mask;
+
+ if(i < 0 || i >= bv->n)
+ fatal(Z, "bvset: index %d is out of bounds with length %d\n", i, bv->n);
+ mask = 1 << (i % WORDBITS);
+ bv->b[i / WORDBITS] |= mask;
+}
diff --git a/src/cmd/cc/cc.h b/src/cmd/cc/cc.h
index c8de94120..af2339c97 100644
--- a/src/cmd/cc/cc.h
+++ b/src/cmd/cc/cc.h
@@ -52,9 +52,12 @@ typedef struct Hist Hist;
typedef struct Term Term;
typedef struct Init Init;
typedef struct Bits Bits;
+typedef struct Bvec Bvec;
typedef struct Dynimp Dynimp;
typedef struct Dynexp Dynexp;
+typedef Rune TRune; /* target system type */
+
#define BUFSIZ 8192
#define NSYMB 500
#define NHASH 1024
@@ -74,6 +77,12 @@ struct Bits
uint32 b[BITS];
};
+struct Bvec
+{
+ int32 n; // number of bits
+ uint32 b[];
+};
+
struct Node
{
Node* left;
@@ -85,7 +94,7 @@ struct Node
double fconst; /* fp constant */
vlong vconst; /* non fp const */
char* cstring; /* character string */
- ushort* rstring; /* rune string */
+ TRune* rstring; /* rune string */
Sym* sym;
Type* type;
@@ -367,6 +376,9 @@ enum
TFILE,
TOLD,
NALLTYPES,
+
+ /* adapt size of Rune to target system's size */
+ TRUNE = sizeof(TRune)==4? TUINT: TUSHORT,
};
enum
{
@@ -746,6 +758,12 @@ int beq(Bits, Bits);
int bset(Bits, uint);
/*
+ * bv.c
+ */
+Bvec* bvalloc(int32 n);
+void bvset(Bvec *bv, int32 i);
+
+/*
* dpchk.c
*/
void dpcheck(Node*);
@@ -766,12 +784,13 @@ void gclean(void);
void gextern(Sym*, Node*, int32, int32);
void ginit(void);
int32 outstring(char*, int32);
-int32 outlstring(ushort*, int32);
+int32 outlstring(TRune*, int32);
void sextern(Sym*, Node*, int32, int32);
void xcom(Node*);
int32 exreg(Type*);
int32 align(int32, Type*, int, int32*);
int32 maxround(int32, int32);
+int hasdotdotdot(void);
extern schar ewidth[];
@@ -800,7 +819,6 @@ int machcap(Node*);
#pragma varargck type "Q" int32
#pragma varargck type "O" int
#pragma varargck type "O" uint
-#pragma varargck type "S" ushort*
#pragma varargck type "T" Type*
#pragma varargck type "U" char*
#pragma varargck type "|" int
diff --git a/src/cmd/cc/cc.y b/src/cmd/cc/cc.y
index 830dd21f8..11ee444b7 100644
--- a/src/cmd/cc/cc.y
+++ b/src/cmd/cc/cc.y
@@ -891,9 +891,9 @@ lstring:
LLSTRING
{
$$ = new(OLSTRING, Z, Z);
- $$->type = typ(TARRAY, types[TUSHORT]);
- $$->type->width = $1.l + sizeof(ushort);
- $$->rstring = (ushort*)$1.s;
+ $$->type = typ(TARRAY, types[TRUNE]);
+ $$->type->width = $1.l + sizeof(TRune);
+ $$->rstring = (TRune*)$1.s;
$$->sym = symstring;
$$->etype = TARRAY;
$$->class = CSTATIC;
@@ -903,16 +903,16 @@ lstring:
char *s;
int n;
- n = $1->type->width - sizeof(ushort);
+ n = $1->type->width - sizeof(TRune);
s = alloc(n+$2.l+MAXALIGN);
memcpy(s, $1->rstring, n);
memcpy(s+n, $2.s, $2.l);
- *(ushort*)(s+n+$2.l) = 0;
+ *(TRune*)(s+n+$2.l) = 0;
$$ = $1;
$$->type->width += $2.l;
- $$->rstring = (ushort*)s;
+ $$->rstring = (TRune*)s;
}
zelist:
diff --git a/src/cmd/cc/com.c b/src/cmd/cc/com.c
index 6e470ee64..4886b73eb 100644
--- a/src/cmd/cc/com.c
+++ b/src/cmd/cc/com.c
@@ -87,6 +87,7 @@ tcomo(Node *n, int f)
Node *l, *r;
Type *t;
int o;
+ static TRune zer;
if(n == Z) {
diag(Z, "Z in tcom");
@@ -651,12 +652,10 @@ tcomo(Node *n, int f)
break;
case OLSTRING:
- if(n->type->link != types[TUSHORT]) {
+ if(n->type->link != types[TRUNE]) {
o = outstring(0, 0);
while(o & 3) {
- ushort a[1];
- a[0] = 0;
- outlstring(a, sizeof(ushort));
+ outlstring(&zer, sizeof(TRune));
o = outlstring(0, 0);
}
}
@@ -1326,10 +1325,10 @@ compar(Node *n, int reverse)
if(lt->width == 8)
hi = big(0, ~0ULL);
else
- hi = big(0, (1LL<<(l->type->width*8))-1);
+ hi = big(0, (1ULL<<(l->type->width*8))-1);
}else{
- lo = big(~0ULL, -(1LL<<(l->type->width*8-1)));
- hi = big(0, (1LL<<(l->type->width*8-1))-1);
+ lo = big(~0ULL, -(1ULL<<(l->type->width*8-1)));
+ hi = big(0, (1ULL<<(l->type->width*8-1))-1);
}
switch(op){
diff --git a/src/cmd/cc/dcl.c b/src/cmd/cc/dcl.c
index edfc7e75a..a7a942686 100644
--- a/src/cmd/cc/dcl.c
+++ b/src/cmd/cc/dcl.c
@@ -268,7 +268,7 @@ nextinit(void)
a->cstring++;
}
if(a->op == OLSTRING) {
- b->vconst = convvtox(*a->rstring, TUSHORT);
+ b->vconst = convvtox(*a->rstring, TRUNE);
a->rstring++;
}
a->type->width -= b->type->width;
@@ -554,6 +554,28 @@ newlist(Node *l, Node *r)
return new(OLIST, l, r);
}
+static int
+haspointers(Type *t)
+{
+ Type *fld;
+
+ switch(t->etype) {
+ case TSTRUCT:
+ for(fld = t->link; fld != T; fld = fld->down) {
+ if(haspointers(fld))
+ return 1;
+ }
+ return 0;
+ case TARRAY:
+ return haspointers(t->link);
+ case TFUNC:
+ case TIND:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
void
sualign(Type *t)
{
@@ -608,6 +630,9 @@ sualign(Type *t)
diag(Z, "incomplete union element");
l->offset = 0;
l->shift = 0;
+ if((debug['q'] || debug['Q']) && haspointers(l))
+ diag(Z, "precise garbage collector cannot handle unions with pointers");
+
o = align(align(0, l, Ael1, &maxal), l, Ael2, &maxal);
if(o > w)
w = o;
diff --git a/src/cmd/cc/dpchk.c b/src/cmd/cc/dpchk.c
index 34163ff92..606bf40dd 100644
--- a/src/cmd/cc/dpchk.c
+++ b/src/cmd/cc/dpchk.c
@@ -567,7 +567,19 @@ pragfpround(void)
void
pragtextflag(void)
{
- textflag = getnsn();
+ Sym *s;
+
+ s = getsym();
+ if(s == S) {
+ textflag = getnsn();
+ } else {
+ if(s->macro) {
+ macexpand(s, symb);
+ }
+ if(symb[0] < '0' || symb[0] > '9')
+ yyerror("pragma textflag not an integer");
+ textflag = atoi(symb);
+ }
while(getnsc() != '\n')
;
if(debug['f'])
@@ -577,7 +589,19 @@ pragtextflag(void)
void
pragdataflag(void)
{
- dataflag = getnsn();
+ Sym *s;
+
+ s = getsym();
+ if(s == S) {
+ dataflag = getnsn();
+ } else {
+ if(s->macro) {
+ macexpand(s, symb);
+ }
+ if(symb[0] < '0' || symb[0] > '9')
+ yyerror("pragma dataflag not an integer");
+ dataflag = atoi(symb);
+ }
while(getnsc() != '\n')
;
if(debug['f'])
diff --git a/src/cmd/cc/funct.c b/src/cmd/cc/funct.c
index 7921277b4..92c067db8 100644
--- a/src/cmd/cc/funct.c
+++ b/src/cmd/cc/funct.c
@@ -235,7 +235,7 @@ no:
return 0;
bad:
- diag(n, "cant rewrite typestr for op %O\n", o);
+ diag(n, "can't rewrite typestr for op %O\n", o);
prtree(n, "isfunct");
n->type = T;
return 1;
diff --git a/src/cmd/cc/lex.c b/src/cmd/cc/lex.c
index 4fb0be9a4..049dc5196 100644
--- a/src/cmd/cc/lex.c
+++ b/src/cmd/cc/lex.c
@@ -118,6 +118,7 @@ main(int argc, char *argv[])
{
int c;
+ quotefmtinstall(); // before cinit, which overrides %Q
ensuresymb(NSYMB);
memset(debug, 0, sizeof(debug));
tinit();
@@ -126,7 +127,6 @@ main(int argc, char *argv[])
arginit();
fmtstrinit(&pragcgobuf);
- quotefmtinstall();
tufield = simplet((1L<<tfield->etype) | BUNSIGNED);
ndef = 0;
@@ -533,7 +533,7 @@ l1:
yyerror("missing '");
peekc = c1;
}
- yylval.vval = convvtox(c, TUSHORT);
+ yylval.vval = convvtox(c, TRUNE);
return LUCONST;
}
if(c == '"') {
@@ -607,15 +607,15 @@ l1:
c = escchar('"', 1, 0);
if(c == EOF)
break;
- cp = allocn(cp, c1, sizeof(ushort));
- *(ushort*)(cp + c1) = c;
- c1 += sizeof(ushort);
+ cp = allocn(cp, c1, sizeof(TRune));
+ *(TRune*)(cp + c1) = c;
+ c1 += sizeof(TRune);
}
yylval.sval.l = c1;
do {
- cp = allocn(cp, c1, sizeof(ushort));
- *(ushort*)(cp + c1) = 0;
- c1 += sizeof(ushort);
+ cp = allocn(cp, c1, sizeof(TRune));
+ *(TRune*)(cp + c1) = 0;
+ c1 += sizeof(TRune);
} while(c1 & MAXALIGN);
yylval.sval.s = cp;
return LLSTRING;
@@ -1019,7 +1019,7 @@ hex:
c += 10-'A';
else
goto bad;
- nn = n*16 + c;
+ nn = (uvlong)n*16 + c;
if(n < 0 && nn >= 0)
goto bad;
n = nn;
@@ -1093,7 +1093,7 @@ getnsc(void)
} else
c = GETC();
for(;;) {
- if(!isspace(c))
+ if(c >= Runeself || !isspace(c))
return c;
if(c == '\n') {
lineno++;
@@ -1137,7 +1137,7 @@ loop:
*/
i = 2;
if(longflg)
- i = 4;
+ i = 6;
l = 0;
for(; i>0; i--) {
c = getc();
@@ -1167,7 +1167,7 @@ loop:
*/
i = 2;
if(longflg)
- i = 5;
+ i = 8;
l = c - '0';
for(; i>0; i--) {
c = getc();
diff --git a/src/cmd/cc/lexbody b/src/cmd/cc/lexbody
index f4a69739c..9d293b089 100644
--- a/src/cmd/cc/lexbody
+++ b/src/cmd/cc/lexbody
@@ -224,7 +224,7 @@ Sym*
lookup(void)
{
Sym *s;
- int32 h;
+ uint32 h;
char *p;
int c, l;
char *r, *w;
@@ -400,7 +400,7 @@ l1:
if(c >= '0' && c <= '9') {
if(c > '7' && c1 == 3)
break;
- yylval.lval <<= c1;
+ yylval.lval = (uvlong)yylval.lval << c1;
yylval.lval += c - '0';
c = GETC();
continue;
@@ -410,7 +410,7 @@ l1:
if(c >= 'A' && c <= 'F')
c += 'a' - 'A';
if(c >= 'a' && c <= 'f') {
- yylval.lval <<= c1;
+ yylval.lval = (uvlong)yylval.lval << c1;
yylval.lval += c - 'a' + 10;
c = GETC();
continue;
@@ -665,7 +665,7 @@ loop:
goto pop;
}
fi.p = i->b + 1;
- return i->b[0];
+ return i->b[0] & 0xff;
pop:
iostack = i->link;
@@ -678,7 +678,7 @@ pop:
fi.c = i->c;
if(--fi.c < 0)
goto loop;
- return *fi.p++;
+ return *fi.p++ & 0xff;
}
void
@@ -762,7 +762,7 @@ ieeedtod(Ieee *ieee, double native)
return;
}
fr = frexp(native, &exp);
- f = 2097152L; /* shouldnt use fp constants here */
+ f = 2097152L; /* shouldn't use fp constants here */
fr = modf(fr*f, &ho);
ieee->h = ho;
ieee->h &= 0xfffffL;
@@ -770,6 +770,6 @@ ieeedtod(Ieee *ieee, double native)
f = 65536L;
fr = modf(fr*f, &ho);
ieee->l = ho;
- ieee->l <<= 16;
+ ieee->l = (uint32)ieee->l << 16;
ieee->l |= (int32)(fr*f);
}
diff --git a/src/cmd/cc/pgen.c b/src/cmd/cc/pgen.c
index b06aa996d..b82872bc5 100644
--- a/src/cmd/cc/pgen.c
+++ b/src/cmd/cc/pgen.c
@@ -29,6 +29,22 @@
// THE SOFTWARE.
#include "gc.h"
+#include "../../pkg/runtime/funcdata.h"
+
+enum { BitsPerPointer = 2 };
+
+static void dumpgcargs(Type *fn, Sym *sym);
+
+int
+hasdotdotdot(void)
+{
+ Type *t;
+
+ for(t=thisfn->down; t!=T; t=t->down)
+ if(t->etype == TDOT)
+ return 1;
+ return 0;
+}
vlong
argsize(void)
@@ -43,9 +59,9 @@ argsize(void)
case TVOID:
break;
case TDOT:
- yyerror("function takes ... without textflag NOSPLIT");
- s += 64;
- break;
+ if((textflag & NOSPLIT) == 0)
+ yyerror("function takes ... without textflag NOSPLIT");
+ return ArgsSizeUnknown;
default:
s = align(s, t, Aarg1, nil);
s = align(s, t, Aarg2, nil);
@@ -64,7 +80,10 @@ void
codgen(Node *n, Node *nn)
{
Prog *sp;
- Node *n1, nod, nod1;
+ Node *n1, nod, nod1, nod2;
+ Sym *gcsym, *gclocalssym;
+ static int ngcsym, ngclocalssym;
+ static char namebuf[40];
cursafe = 0;
curarg = 0;
@@ -75,7 +94,7 @@ codgen(Node *n, Node *nn)
*/
for(n1 = nn;; n1 = n1->left) {
if(n1 == Z) {
- diag(nn, "cant find function name");
+ diag(nn, "can't find function name");
return;
}
if(n1->op == ONAME)
@@ -87,6 +106,30 @@ codgen(Node *n, Node *nn)
sp = p;
/*
+ * generate funcdata symbol for this function.
+ * data is filled in at the end of codgen().
+ */
+ snprint(namebuf, sizeof namebuf, "gc·%d", ngcsym++);
+ gcsym = slookup(namebuf);
+ gcsym->class = CSTATIC;
+
+ memset(&nod, 0, sizeof nod);
+ nod.op = ONAME;
+ nod.sym = gcsym;
+ nod.class = CSTATIC;
+ gins(AFUNCDATA, nodconst(FUNCDATA_GCArgs), &nod);
+
+ snprint(namebuf, sizeof(namebuf), "gclocalssym·%d", ngclocalssym++);
+ gclocalssym = slookup(namebuf);
+ gclocalssym->class = CSTATIC;
+
+ memset(&nod2, 0, sizeof(nod2));
+ nod2.op = ONAME;
+ nod2.sym = gclocalssym;
+ nod2.class = CSTATIC;
+ gins(AFUNCDATA, nodconst(FUNCDATA_GCLocals), &nod2);
+
+ /*
* isolate first argument
*/
if(REGARG >= 0) {
@@ -122,6 +165,21 @@ codgen(Node *n, Node *nn)
if(thechar=='6' || thechar=='7') /* [sic] */
maxargsafe = xround(maxargsafe, 8);
sp->to.offset += maxargsafe;
+
+ dumpgcargs(thisfn, gcsym);
+
+ // TODO(rsc): "stkoff" is not right. It does not account for
+ // the possibility of data stored in .safe variables.
+ // Unfortunately those move up and down just like
+ // the argument frame (and in fact dovetail with it)
+ // so the number we need is not available or even
+ // well-defined. Probably we need to make the safe
+ // area its own section.
+ // That said, we've been using stkoff for months
+ // and nothing too terrible has happened.
+ gextern(gclocalssym, nodconst(-stkoff), 0, 4); // locals
+ gclocalssym->type = typ(0, T);
+ gclocalssym->type->width = 4;
}
void
@@ -589,3 +647,107 @@ bcomplex(Node *n, Node *c)
boolgen(n, 1, Z);
return 0;
}
+
+// Updates the bitvector with a set bit for each pointer containing
+// value in the type description starting at offset.
+static void
+walktype1(Type *t, int32 offset, Bvec *bv, int param)
+{
+ Type *t1;
+ int32 o;
+
+ switch(t->etype) {
+ case TCHAR:
+ case TUCHAR:
+ case TSHORT:
+ case TUSHORT:
+ case TINT:
+ case TUINT:
+ case TLONG:
+ case TULONG:
+ case TVLONG:
+ case TUVLONG:
+ case TFLOAT:
+ case TDOUBLE:
+ // non-pointer types
+ break;
+
+ case TIND:
+ pointer:
+ // pointer types
+ if((offset + t->offset) % ewidth[TIND] != 0)
+ yyerror("unaligned pointer");
+ bvset(bv, ((offset + t->offset) / ewidth[TIND])*BitsPerPointer);
+ break;
+
+ case TARRAY:
+ if(param) // unlike Go, C passes arrays by reference
+ goto pointer;
+ // array in struct or union is an actual array
+ for(o = 0; o < t->width; o += t->link->width)
+ walktype1(t->link, offset+o, bv, 0);
+ break;
+
+ case TSTRUCT:
+ // build map recursively
+ for(t1 = t->link; t1 != T; t1 = t1->down)
+ walktype1(t1, offset, bv, 0);
+ break;
+
+ case TUNION:
+ walktype1(t->link, offset, bv, 0);
+ break;
+
+ default:
+ yyerror("can't handle arg type %s\n", tnames[t->etype]);
+ }
+}
+
+// Compute a bit vector to describe the pointer containing locations
+// in the argument list. Adds the data to gcsym and returns the offset
+// of end of the bit vector.
+static void
+dumpgcargs(Type *fn, Sym *sym)
+{
+ Bvec *bv;
+ Type *t;
+ int32 i;
+ int32 argbytes;
+ int32 symoffset, argoffset;
+
+ if(hasdotdotdot()) {
+ // give up for C vararg functions.
+ // TODO: maybe make a map just for the args we do know?
+ gextern(sym, nodconst(0), 0, 4); // nptrs=0
+ symoffset = 4;
+ } else {
+ argbytes = (argsize() + ewidth[TIND] - 1);
+ bv = bvalloc((argbytes / ewidth[TIND]) * BitsPerPointer);
+ argoffset = align(0, fn->link, Aarg0, nil);
+ if(argoffset > 0) {
+ // The C calling convention returns structs by
+ // copying them to a location pointed to by a
+ // hidden first argument. This first argument
+ // is a pointer.
+ if(argoffset != ewidth[TIND])
+ yyerror("passbyptr arg not the right size");
+ bvset(bv, 0);
+ }
+ for(t = fn->down; t != T; t = t->down) {
+ if(t->etype == TVOID)
+ continue;
+ argoffset = align(argoffset, t, Aarg1, nil);
+ walktype1(t, argoffset, bv, 1);
+ argoffset = align(argoffset, t, Aarg2, nil);
+ }
+ gextern(sym, nodconst(bv->n), 0, 4);
+ symoffset = 4;
+ for(i = 0; i < bv->n; i += 32) {
+ gextern(sym, nodconst(bv->b[i/32]), symoffset, 4);
+ symoffset += 4;
+ }
+ free(bv);
+ }
+ sym->type = typ(0, T);
+ sym->type->width = symoffset;
+}
diff --git a/src/cmd/cc/pswt.c b/src/cmd/cc/pswt.c
index b94035faa..cc9c22763 100644
--- a/src/cmd/cc/pswt.c
+++ b/src/cmd/cc/pswt.c
@@ -102,28 +102,29 @@ newcase(void)
}
int32
-outlstring(ushort *s, int32 n)
+outlstring(TRune *s, int32 n)
{
- char buf[2];
- int c;
+ char buf[sizeof(TRune)];
+ uint c;
+ int i;
int32 r;
if(suppress)
return nstring;
- while(nstring & 1)
+ while(nstring & (sizeof(TRune)-1))
outstring("", 1);
r = nstring;
while(n > 0) {
c = *s++;
if(align(0, types[TCHAR], Aarg1, nil)) {
- buf[0] = c>>8;
- buf[1] = c;
+ for(i = 0; i < sizeof(TRune); i++)
+ buf[i] = c>>(8*(sizeof(TRune) - i - 1));
} else {
- buf[0] = c;
- buf[1] = c>>8;
+ for(i = 0; i < sizeof(TRune); i++)
+ buf[i] = c>>(8*i);
}
- outstring(buf, 2);
- n -= sizeof(ushort);
+ outstring(buf, sizeof(TRune));
+ n -= sizeof(TRune);
}
return r;
}
@@ -155,7 +156,7 @@ ieeedtod(Ieee *ieee, double native)
return;
}
fr = frexp(native, &exp);
- f = 2097152L; /* shouldnt use fp constants here */
+ f = 2097152L; /* shouldn't use fp constants here */
fr = modf(fr*f, &ho);
ieee->h = ho;
ieee->h &= 0xfffffL;
diff --git a/src/cmd/cc/scon.c b/src/cmd/cc/scon.c
index f6031a5be..b0b909759 100644
--- a/src/cmd/cc/scon.c
+++ b/src/cmd/cc/scon.c
@@ -186,7 +186,7 @@ evconst(Node *n)
break;
case OASHL:
- v = l->vconst << r->vconst;
+ v = (uvlong)l->vconst << r->vconst;
break;
case OLO:
diff --git a/src/cmd/cc/sub.c b/src/cmd/cc/sub.c
index 3a5576385..bed989102 100644
--- a/src/cmd/cc/sub.c
+++ b/src/cmd/cc/sub.c
@@ -116,7 +116,10 @@ prtree1(Node *n, int d, int f)
break;
case OLSTRING:
- print(" \"%S\"", n->rstring);
+ if(sizeof(TRune) == sizeof(Rune))
+ print(" \"%S\"", (Rune*)n->rstring);
+ else
+ print(" \"...\"");
i = 0;
break;
diff --git a/src/cmd/cc/y.tab.c b/src/cmd/cc/y.tab.c
index 10938c10b..8588515ab 100644
--- a/src/cmd/cc/y.tab.c
+++ b/src/cmd/cc/y.tab.c
@@ -1,24 +1,21 @@
-/* A Bison parser, made by GNU Bison 2.3. */
+/* A Bison parser, made by GNU Bison 2.7.12-4996. */
-/* Skeleton implementation for Bison's Yacc-like parsers in C
-
- Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
- Free Software Foundation, Inc.
-
- This program is free software; you can redistribute it and/or modify
+/* Bison implementation for Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
+
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* As a special exception, you may create a larger work that contains
part or all of the Bison parser skeleton and distribute that work
@@ -29,7 +26,7 @@
special exception, which will cause the skeleton and the resulting
Bison output files to be licensed under the GNU General Public
License without this special exception.
-
+
This special exception was added by the Free Software Foundation in
version 2.2 of Bison. */
@@ -47,7 +44,7 @@
#define YYBISON 1
/* Bison version. */
-#define YYBISON_VERSION "2.3"
+#define YYBISON_VERSION "2.7.12-4996"
/* Skeleton name. */
#define YYSKELETON_NAME "yacc.c"
@@ -55,11 +52,54 @@
/* Pure parsers. */
#define YYPURE 0
-/* Using locations. */
-#define YYLSP_NEEDED 0
+/* Push parsers. */
+#define YYPUSH 0
+
+/* Pull parsers. */
+#define YYPULL 1
+
+/* Copy the first part of user declarations. */
+/* Line 371 of yacc.c */
+#line 31 "cc.y"
+
+#include <u.h>
+#include <stdio.h> /* if we don't, bison will, and cc.h re-#defines getc */
+#include "cc.h"
+
+/* Line 371 of yacc.c */
+#line 74 "y.tab.c"
+
+# ifndef YY_NULL
+# if defined __cplusplus && 201103L <= __cplusplus
+# define YY_NULL nullptr
+# else
+# define YY_NULL 0
+# endif
+# endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+/* In a future release of Bison, this section will be replaced
+ by #include "y.tab.h". */
+#ifndef YY_YY_Y_TAB_H_INCLUDED
+# define YY_YY_Y_TAB_H_INCLUDED
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+#if YYDEBUG
+extern int yydebug;
+#endif
+
/* Tokens. */
#ifndef YYTOKENTYPE
# define YYTOKENTYPE
@@ -216,37 +256,12 @@
-
-/* Copy the first part of user declarations. */
-#line 31 "cc.y"
-
-#include <u.h>
-#include <stdio.h> /* if we don't, bison will, and cc.h re-#defines getc */
-#include "cc.h"
-
-
-/* Enabling traces. */
-#ifndef YYDEBUG
-# define YYDEBUG 0
-#endif
-
-/* Enabling verbose error messages. */
-#ifdef YYERROR_VERBOSE
-# undef YYERROR_VERBOSE
-# define YYERROR_VERBOSE 1
-#else
-# define YYERROR_VERBOSE 0
-#endif
-
-/* Enabling the token table. */
-#ifndef YYTOKEN_TABLE
-# define YYTOKEN_TABLE 0
-#endif
-
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
typedef union YYSTYPE
-#line 36 "cc.y"
{
+/* Line 387 of yacc.c */
+#line 36 "cc.y"
+
Node* node;
Sym* sym;
Type* type;
@@ -270,22 +285,38 @@ typedef union YYSTYPE
int32 lval;
double dval;
vlong vval;
-}
-/* Line 193 of yacc.c. */
-#line 276 "y.tab.c"
- YYSTYPE;
+
+
+/* Line 387 of yacc.c */
+#line 292 "y.tab.c"
+} YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
# define YYSTYPE_IS_DECLARED 1
-# define YYSTYPE_IS_TRIVIAL 1
#endif
+extern YYSTYPE yylval;
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
-/* Copy the second part of user declarations. */
+#endif /* !YY_YY_Y_TAB_H_INCLUDED */
+/* Copy the second part of user declarations. */
-/* Line 216 of yacc.c. */
-#line 289 "y.tab.c"
+/* Line 390 of yacc.c */
+#line 320 "y.tab.c"
#ifdef short
# undef short
@@ -338,36 +369,45 @@ typedef short int yytype_int16;
# if defined YYENABLE_NLS && YYENABLE_NLS
# if ENABLE_NLS
# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
-# define YY_(msgid) dgettext ("bison-runtime", msgid)
+# define YY_(Msgid) dgettext ("bison-runtime", Msgid)
# endif
# endif
# ifndef YY_
-# define YY_(msgid) msgid
+# define YY_(Msgid) Msgid
+# endif
+#endif
+
+#ifndef __attribute__
+/* This feature is available in gcc versions 2.5 and later. */
+# if (! defined __GNUC__ || __GNUC__ < 2 \
+ || (__GNUC__ == 2 && __GNUC_MINOR__ < 5))
+# define __attribute__(Spec) /* empty */
# endif
#endif
/* Suppress unused-variable warnings by "using" E. */
#if ! defined lint || defined __GNUC__
-# define YYUSE(e) ((void) (e))
+# define YYUSE(E) ((void) (E))
#else
-# define YYUSE(e) /* empty */
+# define YYUSE(E) /* empty */
#endif
+
/* Identity function, used to suppress warnings about constant conditions. */
#ifndef lint
-# define YYID(n) (n)
+# define YYID(N) (N)
#else
#if (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
static int
-YYID (int i)
+YYID (int yyi)
#else
static int
-YYID (i)
- int i;
+YYID (yyi)
+ int yyi;
#endif
{
- return i;
+ return yyi;
}
#endif
@@ -388,11 +428,12 @@ YYID (i)
# define alloca _alloca
# else
# define YYSTACK_ALLOC alloca
-# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
-# ifndef _STDLIB_H
-# define _STDLIB_H 1
+ /* Use EXIT_SUCCESS as a witness for stdlib.h. */
+# ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
# endif
# endif
# endif
@@ -415,24 +456,24 @@ YYID (i)
# ifndef YYSTACK_ALLOC_MAXIMUM
# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
# endif
-# if (defined __cplusplus && ! defined _STDLIB_H \
+# if (defined __cplusplus && ! defined EXIT_SUCCESS \
&& ! ((defined YYMALLOC || defined malloc) \
&& (defined YYFREE || defined free)))
# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
-# ifndef _STDLIB_H
-# define _STDLIB_H 1
+# ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
# endif
# endif
# ifndef YYMALLOC
# define YYMALLOC malloc
-# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+# if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
# endif
# endif
# ifndef YYFREE
# define YYFREE free
-# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+# if ! defined free && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
void free (void *); /* INFRINGES ON USER NAME SPACE */
# endif
@@ -448,9 +489,9 @@ void free (void *); /* INFRINGES ON USER NAME SPACE */
/* A type that is properly aligned for any stack member. */
union yyalloc
{
- yytype_int16 yyss;
- YYSTYPE yyvs;
- };
+ yytype_int16 yyss_alloc;
+ YYSTYPE yyvs_alloc;
+};
/* The size of the maximum gap between one aligned stack and the next. */
# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
@@ -461,35 +502,19 @@ union yyalloc
((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+ YYSTACK_GAP_MAXIMUM)
-/* Copy COUNT objects from FROM to TO. The source and destination do
- not overlap. */
-# ifndef YYCOPY
-# if defined __GNUC__ && 1 < __GNUC__
-# define YYCOPY(To, From, Count) \
- __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
-# else
-# define YYCOPY(To, From, Count) \
- do \
- { \
- YYSIZE_T yyi; \
- for (yyi = 0; yyi < (Count); yyi++) \
- (To)[yyi] = (From)[yyi]; \
- } \
- while (YYID (0))
-# endif
-# endif
+# define YYCOPY_NEEDED 1
/* Relocate STACK from its old location to the new one. The
local variables YYSIZE and YYSTACKSIZE give the old and new number of
elements in the stack, and YYPTR gives the new location of the
stack. Advance YYPTR to a properly aligned location for the next
stack. */
-# define YYSTACK_RELOCATE(Stack) \
+# define YYSTACK_RELOCATE(Stack_alloc, Stack) \
do \
{ \
YYSIZE_T yynewbytes; \
- YYCOPY (&yyptr->Stack, Stack, yysize); \
- Stack = &yyptr->Stack; \
+ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \
+ Stack = &yyptr->Stack_alloc; \
yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
yyptr += yynewbytes / sizeof (*yyptr); \
} \
@@ -497,6 +522,26 @@ union yyalloc
#endif
+#if defined YYCOPY_NEEDED && YYCOPY_NEEDED
+/* Copy COUNT objects from SRC to DST. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if defined __GNUC__ && 1 < __GNUC__
+# define YYCOPY(Dst, Src, Count) \
+ __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src)))
+# else
+# define YYCOPY(Dst, Src, Count) \
+ do \
+ { \
+ YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (Dst)[yyi] = (Src)[yyi]; \
+ } \
+ while (YYID (0))
+# endif
+# endif
+#endif /* !YYCOPY_NEEDED */
+
/* YYFINAL -- State number of the termination state. */
#define YYFINAL 2
/* YYLAST -- Last index in YYTABLE. */
@@ -701,7 +746,7 @@ static const yytype_uint16 yyrline[] =
};
#endif
-#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+#if YYDEBUG || YYERROR_VERBOSE || 0
/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
First, the terminals, then, starting at YYNTOKENS, nonterminals. */
static const char *const yytname[] =
@@ -719,16 +764,16 @@ static const char *const yytname[] =
"LTYPEDEF", "LTYPESTR", "LUNION", "LUNSIGNED", "LWHILE", "LVOID",
"LENUM", "LSIGNED", "LCONSTNT", "LVOLATILE", "LSET", "LSIGNOF",
"LRESTRICT", "LINLINE", "')'", "']'", "'{'", "'}'", "'!'", "'~'",
- "$accept", "prog", "xdecl", "@1", "@2", "xdlist", "@3", "xdecor",
- "xdecor2", "adecl", "adlist", "@4", "pdecl", "pdlist", "edecl", "@5",
- "@6", "zedlist", "edlist", "edecor", "abdecor", "abdecor1", "abdecor2",
+ "$accept", "prog", "xdecl", "$@1", "$@2", "xdlist", "$@3", "xdecor",
+ "xdecor2", "adecl", "adlist", "$@4", "pdecl", "pdlist", "edecl", "$@5",
+ "$@6", "zedlist", "edlist", "edecor", "abdecor", "abdecor1", "abdecor2",
"abdecor3", "init", "qual", "qlist", "ilist", "zarglist", "arglist",
- "block", "slist", "labels", "label", "stmnt", "forexpr", "ulstmnt", "@7",
- "@8", "zcexpr", "zexpr", "lexpr", "cexpr", "expr", "xuexpr", "uexpr",
- "pexpr", "string", "lstring", "zelist", "elist", "sbody", "@9",
- "zctlist", "types", "tlist", "ctlist", "complex", "@10", "@11", "@12",
- "@13", "@14", "gctnlist", "zgnlist", "gctname", "gcnlist", "gcname",
- "enum", "tname", "cname", "gname", "name", "tag", "ltag", 0
+ "block", "slist", "labels", "label", "stmnt", "forexpr", "ulstmnt",
+ "$@7", "$@8", "zcexpr", "zexpr", "lexpr", "cexpr", "expr", "xuexpr",
+ "uexpr", "pexpr", "string", "lstring", "zelist", "elist", "sbody", "@9",
+ "zctlist", "types", "tlist", "ctlist", "complex", "$@10", "$@11", "$@12",
+ "$@13", "$@14", "gctnlist", "zgnlist", "gctname", "gcnlist", "gcname",
+ "enum", "tname", "cname", "gname", "name", "tag", "ltag", YY_NULL
};
#endif
@@ -810,8 +855,8 @@ static const yytype_uint8 yyr2[] =
1, 1, 1, 1, 1, 1, 1, 1
};
-/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
- STATE-NUM when YYTABLE doesn't specify something else to do. Zero
+/* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM.
+ Performed when YYTABLE doesn't specify something else to do. Zero
means the default is an error. */
static const yytype_uint8 yydefact[] =
{
@@ -936,8 +981,7 @@ static const yytype_int16 yypgoto[] =
/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
positive, shift that token. If negative, reduce the rule which
- number is the opposite. If zero, do what YYDEFACT says.
- If YYTABLE_NINF, syntax error. */
+ number is the opposite. If YYTABLE_NINF, syntax error. */
#define YYTABLE_NINF -205
static const yytype_int16 yytable[] =
{
@@ -1062,6 +1106,12 @@ static const yytype_int16 yytable[] =
178, 179, 180, 181, 182, 183, 184, 185, 186
};
+#define yypact_value_is_default(Yystate) \
+ (!!((Yystate) == (-331)))
+
+#define yytable_value_is_error(Yytable_value) \
+ YYID (0)
+
static const yytype_int16 yycheck[] =
{
1, 27, 14, 91, 131, 17, 30, 58, 20, 33,
@@ -1245,78 +1295,50 @@ static const yytype_uint8 yystos[] =
/* Like YYERROR except do call yyerror. This remains here temporarily
to ease the transition to the new meaning of YYERROR, for GCC.
- Once GCC version 2 has supplanted version 1, this can go. */
+ Once GCC version 2 has supplanted version 1, this can go. However,
+ YYFAIL appears to be in use. Nevertheless, it is formally deprecated
+ in Bison 2.4.2's NEWS entry, where a plan to phase it out is
+ discussed. */
#define YYFAIL goto yyerrlab
+#if defined YYFAIL
+ /* This is here to suppress warnings from the GCC cpp's
+ -Wunused-macros. Normally we don't worry about that warning, but
+ some users do, and we want to make it easy for users to remove
+ YYFAIL uses, which will produce warnings from Bison 2.5. */
+#endif
#define YYRECOVERING() (!!yyerrstatus)
-#define YYBACKUP(Token, Value) \
-do \
- if (yychar == YYEMPTY && yylen == 1) \
- { \
- yychar = (Token); \
- yylval = (Value); \
- yytoken = YYTRANSLATE (yychar); \
- YYPOPSTACK (1); \
- goto yybackup; \
- } \
- else \
- { \
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ YYPOPSTACK (yylen); \
+ yystate = *yyssp; \
+ goto yybackup; \
+ } \
+ else \
+ { \
yyerror (YY_("syntax error: cannot back up")); \
YYERROR; \
} \
while (YYID (0))
-
+/* Error token number */
#define YYTERROR 1
#define YYERRCODE 256
-/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
- If N is 0, then set CURRENT to the empty location which ends
- the previous symbol: RHS[0] (always defined). */
-
-#define YYRHSLOC(Rhs, K) ((Rhs)[K])
-#ifndef YYLLOC_DEFAULT
-# define YYLLOC_DEFAULT(Current, Rhs, N) \
- do \
- if (YYID (N)) \
- { \
- (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \
- (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \
- (Current).last_line = YYRHSLOC (Rhs, N).last_line; \
- (Current).last_column = YYRHSLOC (Rhs, N).last_column; \
- } \
- else \
- { \
- (Current).first_line = (Current).last_line = \
- YYRHSLOC (Rhs, 0).last_line; \
- (Current).first_column = (Current).last_column = \
- YYRHSLOC (Rhs, 0).last_column; \
- } \
- while (YYID (0))
-#endif
-
-
-/* YY_LOCATION_PRINT -- Print the location on the stream.
- This macro was not mandated originally: define only if we know
- we won't break user code: when these are the locations we know. */
-
+/* This macro is provided for backward compatibility. */
#ifndef YY_LOCATION_PRINT
-# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL
-# define YY_LOCATION_PRINT(File, Loc) \
- fprintf (File, "%d.%d-%d.%d", \
- (Loc).first_line, (Loc).first_column, \
- (Loc).last_line, (Loc).last_column)
-# else
-# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
-# endif
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
#endif
/* YYLEX -- calling `yylex' with the right arguments. */
-
#ifdef YYLEX_PARAM
# define YYLEX yylex (YYLEX_PARAM)
#else
@@ -1366,6 +1388,8 @@ yy_symbol_value_print (yyoutput, yytype, yyvaluep)
YYSTYPE const * const yyvaluep;
#endif
{
+ FILE *yyo = yyoutput;
+ YYUSE (yyo);
if (!yyvaluep)
return;
# ifdef YYPRINT
@@ -1374,11 +1398,7 @@ yy_symbol_value_print (yyoutput, yytype, yyvaluep)
# else
YYUSE (yyoutput);
# endif
- switch (yytype)
- {
- default:
- break;
- }
+ YYUSE (yytype);
}
@@ -1415,17 +1435,20 @@ yy_symbol_print (yyoutput, yytype, yyvaluep)
#if (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
static void
-yy_stack_print (yytype_int16 *bottom, yytype_int16 *top)
+yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop)
#else
static void
-yy_stack_print (bottom, top)
- yytype_int16 *bottom;
- yytype_int16 *top;
+yy_stack_print (yybottom, yytop)
+ yytype_int16 *yybottom;
+ yytype_int16 *yytop;
#endif
{
YYFPRINTF (stderr, "Stack now");
- for (; bottom <= top; ++bottom)
- YYFPRINTF (stderr, " %d", *bottom);
+ for (; yybottom <= yytop; yybottom++)
+ {
+ int yybot = *yybottom;
+ YYFPRINTF (stderr, " %d", yybot);
+ }
YYFPRINTF (stderr, "\n");
}
@@ -1459,11 +1482,11 @@ yy_reduce_print (yyvsp, yyrule)
/* The symbols being reduced. */
for (yyi = 0; yyi < yynrhs; yyi++)
{
- fprintf (stderr, " $%d = ", yyi + 1);
+ YYFPRINTF (stderr, " $%d = ", yyi + 1);
yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
&(yyvsp[(yyi + 1) - (yynrhs)])
);
- fprintf (stderr, "\n");
+ YYFPRINTF (stderr, "\n");
}
}
@@ -1500,7 +1523,6 @@ int yydebug;
# define YYMAXDEPTH 10000
#endif
-
#if YYERROR_VERBOSE
@@ -1603,115 +1625,145 @@ yytnamerr (char *yyres, const char *yystr)
}
# endif
-/* Copy into YYRESULT an error message about the unexpected token
- YYCHAR while in state YYSTATE. Return the number of bytes copied,
- including the terminating null byte. If YYRESULT is null, do not
- copy anything; just return the number of bytes that would be
- copied. As a special case, return 0 if an ordinary "syntax error"
- message will do. Return YYSIZE_MAXIMUM if overflow occurs during
- size calculation. */
-static YYSIZE_T
-yysyntax_error (char *yyresult, int yystate, int yychar)
-{
- int yyn = yypact[yystate];
+/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message
+ about the unexpected token YYTOKEN for the state stack whose top is
+ YYSSP.
- if (! (YYPACT_NINF < yyn && yyn <= YYLAST))
- return 0;
- else
- {
- int yytype = YYTRANSLATE (yychar);
- YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]);
- YYSIZE_T yysize = yysize0;
- YYSIZE_T yysize1;
- int yysize_overflow = 0;
- enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
- char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
- int yyx;
-
-# if 0
- /* This is so xgettext sees the translatable formats that are
- constructed on the fly. */
- YY_("syntax error, unexpected %s");
- YY_("syntax error, unexpected %s, expecting %s");
- YY_("syntax error, unexpected %s, expecting %s or %s");
- YY_("syntax error, unexpected %s, expecting %s or %s or %s");
- YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s");
-# endif
- char *yyfmt;
- char const *yyf;
- static char const yyunexpected[] = "syntax error, unexpected %s";
- static char const yyexpecting[] = ", expecting %s";
- static char const yyor[] = " or %s";
- char yyformat[sizeof yyunexpected
- + sizeof yyexpecting - 1
- + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2)
- * (sizeof yyor - 1))];
- char const *yyprefix = yyexpecting;
-
- /* Start YYX at -YYN if negative to avoid negative indexes in
- YYCHECK. */
- int yyxbegin = yyn < 0 ? -yyn : 0;
-
- /* Stay within bounds of both yycheck and yytname. */
- int yychecklim = YYLAST - yyn + 1;
- int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
- int yycount = 1;
-
- yyarg[0] = yytname[yytype];
- yyfmt = yystpcpy (yyformat, yyunexpected);
-
- for (yyx = yyxbegin; yyx < yyxend; ++yyx)
- if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
- {
- if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
- {
- yycount = 1;
- yysize = yysize0;
- yyformat[sizeof yyunexpected - 1] = '\0';
- break;
- }
- yyarg[yycount++] = yytname[yyx];
- yysize1 = yysize + yytnamerr (0, yytname[yyx]);
- yysize_overflow |= (yysize1 < yysize);
- yysize = yysize1;
- yyfmt = yystpcpy (yyfmt, yyprefix);
- yyprefix = yyor;
- }
+ Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is
+ not large enough to hold the message. In that case, also set
+ *YYMSG_ALLOC to the required number of bytes. Return 2 if the
+ required number of bytes is too large to store. */
+static int
+yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg,
+ yytype_int16 *yyssp, int yytoken)
+{
+ YYSIZE_T yysize0 = yytnamerr (YY_NULL, yytname[yytoken]);
+ YYSIZE_T yysize = yysize0;
+ enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+ /* Internationalized format string. */
+ const char *yyformat = YY_NULL;
+ /* Arguments of yyformat. */
+ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+ /* Number of reported tokens (one for the "unexpected", one per
+ "expected"). */
+ int yycount = 0;
+
+ /* There are many possibilities here to consider:
+ - Assume YYFAIL is not used. It's too flawed to consider. See
+ <http://lists.gnu.org/archive/html/bison-patches/2009-12/msg00024.html>
+ for details. YYERROR is fine as it does not invoke this
+ function.
+ - If this state is a consistent state with a default action, then
+ the only way this function was invoked is if the default action
+ is an error action. In that case, don't check for expected
+ tokens because there are none.
+ - The only way there can be no lookahead present (in yychar) is if
+ this state is a consistent state with a default action. Thus,
+ detecting the absence of a lookahead is sufficient to determine
+ that there is no unexpected or expected token to report. In that
+ case, just report a simple "syntax error".
+ - Don't assume there isn't a lookahead just because this state is a
+ consistent state with a default action. There might have been a
+ previous inconsistent state, consistent state with a non-default
+ action, or user semantic action that manipulated yychar.
+ - Of course, the expected token list depends on states to have
+ correct lookahead information, and it depends on the parser not
+ to perform extra reductions after fetching a lookahead from the
+ scanner and before detecting a syntax error. Thus, state merging
+ (from LALR or IELR) and default reductions corrupt the expected
+ token list. However, the list is correct for canonical LR with
+ one exception: it will still contain any token that will not be
+ accepted due to an error action in a later state.
+ */
+ if (yytoken != YYEMPTY)
+ {
+ int yyn = yypact[*yyssp];
+ yyarg[yycount++] = yytname[yytoken];
+ if (!yypact_value_is_default (yyn))
+ {
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. In other words, skip the first -YYN actions for
+ this state because they are default actions. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn + 1;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yyx;
+
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR
+ && !yytable_value_is_error (yytable[yyx + yyn]))
+ {
+ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+ {
+ yycount = 1;
+ yysize = yysize0;
+ break;
+ }
+ yyarg[yycount++] = yytname[yyx];
+ {
+ YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULL, yytname[yyx]);
+ if (! (yysize <= yysize1
+ && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+ return 2;
+ yysize = yysize1;
+ }
+ }
+ }
+ }
- yyf = YY_(yyformat);
- yysize1 = yysize + yystrlen (yyf);
- yysize_overflow |= (yysize1 < yysize);
- yysize = yysize1;
+ switch (yycount)
+ {
+# define YYCASE_(N, S) \
+ case N: \
+ yyformat = S; \
+ break
+ YYCASE_(0, YY_("syntax error"));
+ YYCASE_(1, YY_("syntax error, unexpected %s"));
+ YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s"));
+ YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s"));
+ YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s"));
+ YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"));
+# undef YYCASE_
+ }
- if (yysize_overflow)
- return YYSIZE_MAXIMUM;
+ {
+ YYSIZE_T yysize1 = yysize + yystrlen (yyformat);
+ if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+ return 2;
+ yysize = yysize1;
+ }
- if (yyresult)
- {
- /* Avoid sprintf, as that infringes on the user's name space.
- Don't have undefined behavior even if the translation
- produced a string with the wrong number of "%s"s. */
- char *yyp = yyresult;
- int yyi = 0;
- while ((*yyp = *yyf) != '\0')
- {
- if (*yyp == '%' && yyf[1] == 's' && yyi < yycount)
- {
- yyp += yytnamerr (yyp, yyarg[yyi++]);
- yyf += 2;
- }
- else
- {
- yyp++;
- yyf++;
- }
- }
- }
- return yysize;
+ if (*yymsg_alloc < yysize)
+ {
+ *yymsg_alloc = 2 * yysize;
+ if (! (yysize <= *yymsg_alloc
+ && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM))
+ *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM;
+ return 1;
}
+
+ /* Avoid sprintf, as that infringes on the user's name space.
+ Don't have undefined behavior even if the translation
+ produced a string with the wrong number of "%s"s. */
+ {
+ char *yyp = *yymsg;
+ int yyi = 0;
+ while ((*yyp = *yyformat) != '\0')
+ if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount)
+ {
+ yyp += yytnamerr (yyp, yyarg[yyi++]);
+ yyformat += 2;
+ }
+ else
+ {
+ yyp++;
+ yyformat++;
+ }
+ }
+ return 0;
}
#endif /* YYERROR_VERBOSE */
-
/*-----------------------------------------------.
| Release the memory associated to this symbol. |
@@ -1736,44 +1788,31 @@ yydestruct (yymsg, yytype, yyvaluep)
yymsg = "Deleting";
YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
- switch (yytype)
- {
-
- default:
- break;
- }
+ YYUSE (yytype);
}
-
-/* Prevent warnings from -Wmissing-prototypes. */
-
-#ifdef YYPARSE_PARAM
-#if defined __STDC__ || defined __cplusplus
-int yyparse (void *YYPARSE_PARAM);
-#else
-int yyparse ();
-#endif
-#else /* ! YYPARSE_PARAM */
-#if defined __STDC__ || defined __cplusplus
-int yyparse (void);
-#else
-int yyparse ();
-#endif
-#endif /* ! YYPARSE_PARAM */
-/* The look-ahead symbol. */
+/* The lookahead symbol. */
int yychar;
-/* The semantic value of the look-ahead symbol. */
-YYSTYPE yylval;
+
+#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_END
+#endif
+#ifndef YY_INITIAL_VALUE
+# define YY_INITIAL_VALUE(Value) /* Nothing. */
+#endif
+
+/* The semantic value of the lookahead symbol. */
+YYSTYPE yylval YY_INITIAL_VALUE(yyval_default);
/* Number of syntax errors so far. */
int yynerrs;
-
/*----------.
| yyparse. |
`----------*/
@@ -1800,14 +1839,37 @@ yyparse ()
#endif
#endif
{
-
- int yystate;
+ int yystate;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+
+ /* The stacks and their tools:
+ `yyss': related to states.
+ `yyvs': related to semantic values.
+
+ Refer to the stacks through separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ yytype_int16 yyssa[YYINITDEPTH];
+ yytype_int16 *yyss;
+ yytype_int16 *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs;
+ YYSTYPE *yyvsp;
+
+ YYSIZE_T yystacksize;
+
int yyn;
int yyresult;
- /* Number of tokens to shift before error messages enabled. */
- int yyerrstatus;
- /* Look-ahead token as an internal (translated) token number. */
+ /* Lookahead token as an internal (translated) token number. */
int yytoken = 0;
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+
#if YYERROR_VERBOSE
/* Buffer for error messages, and its allocated size. */
char yymsgbuf[128];
@@ -1815,54 +1877,22 @@ yyparse ()
YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
#endif
- /* Three stacks and their tools:
- `yyss': related to states,
- `yyvs': related to semantic values,
- `yyls': related to locations.
-
- Refer to the stacks thru separate pointers, to allow yyoverflow
- to reallocate them elsewhere. */
-
- /* The state stack. */
- yytype_int16 yyssa[YYINITDEPTH];
- yytype_int16 *yyss = yyssa;
- yytype_int16 *yyssp;
-
- /* The semantic value stack. */
- YYSTYPE yyvsa[YYINITDEPTH];
- YYSTYPE *yyvs = yyvsa;
- YYSTYPE *yyvsp;
-
-
-
#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N))
- YYSIZE_T yystacksize = YYINITDEPTH;
-
- /* The variables used to return semantic value and location from the
- action routines. */
- YYSTYPE yyval;
-
-
/* The number of symbols on the RHS of the reduced rule.
Keep to zero when no symbol should be popped. */
int yylen = 0;
+ yyssp = yyss = yyssa;
+ yyvsp = yyvs = yyvsa;
+ yystacksize = YYINITDEPTH;
+
YYDPRINTF ((stderr, "Starting parse\n"));
yystate = 0;
yyerrstatus = 0;
yynerrs = 0;
- yychar = YYEMPTY; /* Cause a token to be read. */
-
- /* Initialize stack pointers.
- Waste one element of value and location stack
- so that they stay on the same level as the state stack.
- The wasted elements are never initialized. */
-
- yyssp = yyss;
- yyvsp = yyvs;
-
+ yychar = YYEMPTY; /* Cause a token to be read. */
goto yysetstate;
/*------------------------------------------------------------.
@@ -1889,7 +1919,6 @@ yyparse ()
YYSTYPE *yyvs1 = yyvs;
yytype_int16 *yyss1 = yyss;
-
/* Each stack pointer address is followed by the size of the
data in use in that stack, in bytes. This used to be a
conditional around just the two extra args, but that might
@@ -1897,7 +1926,6 @@ yyparse ()
yyoverflow (YY_("memory exhausted"),
&yyss1, yysize * sizeof (*yyssp),
&yyvs1, yysize * sizeof (*yyvsp),
-
&yystacksize);
yyss = yyss1;
@@ -1920,9 +1948,8 @@ yyparse ()
(union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
if (! yyptr)
goto yyexhaustedlab;
- YYSTACK_RELOCATE (yyss);
- YYSTACK_RELOCATE (yyvs);
-
+ YYSTACK_RELOCATE (yyss_alloc, yyss);
+ YYSTACK_RELOCATE (yyvs_alloc, yyvs);
# undef YYSTACK_RELOCATE
if (yyss1 != yyssa)
YYSTACK_FREE (yyss1);
@@ -1933,7 +1960,6 @@ yyparse ()
yyssp = yyss + yysize - 1;
yyvsp = yyvs + yysize - 1;
-
YYDPRINTF ((stderr, "Stack size increased to %lu\n",
(unsigned long int) yystacksize));
@@ -1943,6 +1969,9 @@ yyparse ()
YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+ if (yystate == YYFINAL)
+ YYACCEPT;
+
goto yybackup;
/*-----------.
@@ -1951,16 +1980,16 @@ yyparse ()
yybackup:
/* Do appropriate processing given the current state. Read a
- look-ahead token if we need one and don't already have one. */
+ lookahead token if we need one and don't already have one. */
- /* First try to decide what to do without reference to look-ahead token. */
+ /* First try to decide what to do without reference to lookahead token. */
yyn = yypact[yystate];
- if (yyn == YYPACT_NINF)
+ if (yypact_value_is_default (yyn))
goto yydefault;
- /* Not known => get a look-ahead token if don't already have one. */
+ /* Not known => get a lookahead token if don't already have one. */
- /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */
if (yychar == YYEMPTY)
{
YYDPRINTF ((stderr, "Reading a token: "));
@@ -1986,29 +2015,27 @@ yybackup:
yyn = yytable[yyn];
if (yyn <= 0)
{
- if (yyn == 0 || yyn == YYTABLE_NINF)
- goto yyerrlab;
+ if (yytable_value_is_error (yyn))
+ goto yyerrlab;
yyn = -yyn;
goto yyreduce;
}
- if (yyn == YYFINAL)
- YYACCEPT;
-
/* Count tokens shifted since error; after three, turn off error
status. */
if (yyerrstatus)
yyerrstatus--;
- /* Shift the look-ahead token. */
+ /* Shift the lookahead token. */
YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
- /* Discard the shifted token unless it is eof. */
- if (yychar != YYEOF)
- yychar = YYEMPTY;
+ /* Discard the shifted token. */
+ yychar = YYEMPTY;
yystate = yyn;
+ YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
*++yyvsp = yylval;
+ YY_IGNORE_MAYBE_UNINITIALIZED_END
goto yynewstate;
@@ -2045,6 +2072,7 @@ yyreduce:
switch (yyn)
{
case 4:
+/* Line 1787 of yacc.c */
#line 109 "cc.y"
{
dodecl(xdecl, lastclass, lasttype, Z);
@@ -2052,6 +2080,7 @@ yyreduce:
break;
case 6:
+/* Line 1787 of yacc.c */
#line 114 "cc.y"
{
lastdcl = T;
@@ -2069,6 +2098,7 @@ yyreduce:
break;
case 7:
+/* Line 1787 of yacc.c */
#line 128 "cc.y"
{
argmark((yyvsp[(2) - (4)].node), 1);
@@ -2076,6 +2106,7 @@ yyreduce:
break;
case 8:
+/* Line 1787 of yacc.c */
#line 132 "cc.y"
{
Node *n;
@@ -2089,6 +2120,7 @@ yyreduce:
break;
case 9:
+/* Line 1787 of yacc.c */
#line 144 "cc.y"
{
dodecl(xdecl, lastclass, lasttype, (yyvsp[(1) - (1)].node));
@@ -2096,6 +2128,7 @@ yyreduce:
break;
case 10:
+/* Line 1787 of yacc.c */
#line 148 "cc.y"
{
(yyvsp[(1) - (1)].node) = dodecl(xdecl, lastclass, lasttype, (yyvsp[(1) - (1)].node));
@@ -2103,6 +2136,7 @@ yyreduce:
break;
case 11:
+/* Line 1787 of yacc.c */
#line 152 "cc.y"
{
doinit((yyvsp[(1) - (4)].node)->sym, (yyvsp[(1) - (4)].node)->type, 0L, (yyvsp[(4) - (4)].node));
@@ -2110,6 +2144,7 @@ yyreduce:
break;
case 14:
+/* Line 1787 of yacc.c */
#line 160 "cc.y"
{
(yyval.node) = new(OIND, (yyvsp[(3) - (3)].node), Z);
@@ -2118,6 +2153,7 @@ yyreduce:
break;
case 16:
+/* Line 1787 of yacc.c */
#line 168 "cc.y"
{
(yyval.node) = (yyvsp[(2) - (3)].node);
@@ -2125,6 +2161,7 @@ yyreduce:
break;
case 17:
+/* Line 1787 of yacc.c */
#line 172 "cc.y"
{
(yyval.node) = new(OFUNC, (yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].node));
@@ -2132,6 +2169,7 @@ yyreduce:
break;
case 18:
+/* Line 1787 of yacc.c */
#line 176 "cc.y"
{
(yyval.node) = new(OARRAY, (yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].node));
@@ -2139,6 +2177,7 @@ yyreduce:
break;
case 19:
+/* Line 1787 of yacc.c */
#line 185 "cc.y"
{
(yyval.node) = dodecl(adecl, lastclass, lasttype, Z);
@@ -2146,6 +2185,7 @@ yyreduce:
break;
case 20:
+/* Line 1787 of yacc.c */
#line 189 "cc.y"
{
(yyval.node) = (yyvsp[(2) - (3)].node);
@@ -2153,6 +2193,7 @@ yyreduce:
break;
case 21:
+/* Line 1787 of yacc.c */
#line 195 "cc.y"
{
dodecl(adecl, lastclass, lasttype, (yyvsp[(1) - (1)].node));
@@ -2161,6 +2202,7 @@ yyreduce:
break;
case 22:
+/* Line 1787 of yacc.c */
#line 200 "cc.y"
{
(yyvsp[(1) - (1)].node) = dodecl(adecl, lastclass, lasttype, (yyvsp[(1) - (1)].node));
@@ -2168,6 +2210,7 @@ yyreduce:
break;
case 23:
+/* Line 1787 of yacc.c */
#line 204 "cc.y"
{
int32 w;
@@ -2179,6 +2222,7 @@ yyreduce:
break;
case 24:
+/* Line 1787 of yacc.c */
#line 212 "cc.y"
{
(yyval.node) = (yyvsp[(1) - (3)].node);
@@ -2191,6 +2235,7 @@ yyreduce:
break;
case 27:
+/* Line 1787 of yacc.c */
#line 229 "cc.y"
{
dodecl(pdecl, lastclass, lasttype, (yyvsp[(1) - (1)].node));
@@ -2198,6 +2243,7 @@ yyreduce:
break;
case 29:
+/* Line 1787 of yacc.c */
#line 239 "cc.y"
{
lasttype = (yyvsp[(1) - (1)].type);
@@ -2205,6 +2251,7 @@ yyreduce:
break;
case 31:
+/* Line 1787 of yacc.c */
#line 244 "cc.y"
{
lasttype = (yyvsp[(2) - (2)].type);
@@ -2212,6 +2259,7 @@ yyreduce:
break;
case 33:
+/* Line 1787 of yacc.c */
#line 250 "cc.y"
{
lastfield = 0;
@@ -2220,6 +2268,7 @@ yyreduce:
break;
case 35:
+/* Line 1787 of yacc.c */
#line 258 "cc.y"
{
dodecl(edecl, CXXX, lasttype, (yyvsp[(1) - (1)].node));
@@ -2227,6 +2276,7 @@ yyreduce:
break;
case 37:
+/* Line 1787 of yacc.c */
#line 265 "cc.y"
{
lastbit = 0;
@@ -2235,6 +2285,7 @@ yyreduce:
break;
case 38:
+/* Line 1787 of yacc.c */
#line 270 "cc.y"
{
(yyval.node) = new(OBIT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
@@ -2242,6 +2293,7 @@ yyreduce:
break;
case 39:
+/* Line 1787 of yacc.c */
#line 274 "cc.y"
{
(yyval.node) = new(OBIT, Z, (yyvsp[(2) - (2)].node));
@@ -2249,6 +2301,7 @@ yyreduce:
break;
case 40:
+/* Line 1787 of yacc.c */
#line 282 "cc.y"
{
(yyval.node) = (Z);
@@ -2256,6 +2309,7 @@ yyreduce:
break;
case 42:
+/* Line 1787 of yacc.c */
#line 289 "cc.y"
{
(yyval.node) = new(OIND, (Z), Z);
@@ -2264,6 +2318,7 @@ yyreduce:
break;
case 43:
+/* Line 1787 of yacc.c */
#line 294 "cc.y"
{
(yyval.node) = new(OIND, (yyvsp[(3) - (3)].node), Z);
@@ -2272,6 +2327,7 @@ yyreduce:
break;
case 46:
+/* Line 1787 of yacc.c */
#line 303 "cc.y"
{
(yyval.node) = new(OFUNC, (yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].node));
@@ -2279,6 +2335,7 @@ yyreduce:
break;
case 47:
+/* Line 1787 of yacc.c */
#line 307 "cc.y"
{
(yyval.node) = new(OARRAY, (yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].node));
@@ -2286,6 +2343,7 @@ yyreduce:
break;
case 48:
+/* Line 1787 of yacc.c */
#line 313 "cc.y"
{
(yyval.node) = new(OFUNC, (Z), Z);
@@ -2293,6 +2351,7 @@ yyreduce:
break;
case 49:
+/* Line 1787 of yacc.c */
#line 317 "cc.y"
{
(yyval.node) = new(OARRAY, (Z), (yyvsp[(2) - (3)].node));
@@ -2300,6 +2359,7 @@ yyreduce:
break;
case 50:
+/* Line 1787 of yacc.c */
#line 321 "cc.y"
{
(yyval.node) = (yyvsp[(2) - (3)].node);
@@ -2307,6 +2367,7 @@ yyreduce:
break;
case 52:
+/* Line 1787 of yacc.c */
#line 328 "cc.y"
{
(yyval.node) = new(OINIT, invert((yyvsp[(2) - (3)].node)), Z);
@@ -2314,6 +2375,7 @@ yyreduce:
break;
case 53:
+/* Line 1787 of yacc.c */
#line 334 "cc.y"
{
(yyval.node) = new(OARRAY, (yyvsp[(2) - (3)].node), Z);
@@ -2321,6 +2383,7 @@ yyreduce:
break;
case 54:
+/* Line 1787 of yacc.c */
#line 338 "cc.y"
{
(yyval.node) = new(OELEM, Z, Z);
@@ -2329,6 +2392,7 @@ yyreduce:
break;
case 57:
+/* Line 1787 of yacc.c */
#line 347 "cc.y"
{
(yyval.node) = new(OLIST, (yyvsp[(1) - (3)].node), (yyvsp[(2) - (3)].node));
@@ -2336,6 +2400,7 @@ yyreduce:
break;
case 59:
+/* Line 1787 of yacc.c */
#line 352 "cc.y"
{
(yyval.node) = new(OLIST, (yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node));
@@ -2343,6 +2408,7 @@ yyreduce:
break;
case 62:
+/* Line 1787 of yacc.c */
#line 360 "cc.y"
{
(yyval.node) = new(OLIST, (yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node));
@@ -2350,6 +2416,7 @@ yyreduce:
break;
case 63:
+/* Line 1787 of yacc.c */
#line 365 "cc.y"
{
(yyval.node) = Z;
@@ -2357,6 +2424,7 @@ yyreduce:
break;
case 64:
+/* Line 1787 of yacc.c */
#line 369 "cc.y"
{
(yyval.node) = invert((yyvsp[(1) - (1)].node));
@@ -2364,6 +2432,7 @@ yyreduce:
break;
case 66:
+/* Line 1787 of yacc.c */
#line 377 "cc.y"
{
(yyval.node) = new(OPROTO, (yyvsp[(2) - (2)].node), Z);
@@ -2372,6 +2441,7 @@ yyreduce:
break;
case 67:
+/* Line 1787 of yacc.c */
#line 382 "cc.y"
{
(yyval.node) = new(OPROTO, (yyvsp[(2) - (2)].node), Z);
@@ -2380,6 +2450,7 @@ yyreduce:
break;
case 68:
+/* Line 1787 of yacc.c */
#line 387 "cc.y"
{
(yyval.node) = new(ODOTDOT, Z, Z);
@@ -2387,6 +2458,7 @@ yyreduce:
break;
case 69:
+/* Line 1787 of yacc.c */
#line 391 "cc.y"
{
(yyval.node) = new(OLIST, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
@@ -2394,6 +2466,7 @@ yyreduce:
break;
case 70:
+/* Line 1787 of yacc.c */
#line 397 "cc.y"
{
(yyval.node) = invert((yyvsp[(2) - (3)].node));
@@ -2405,6 +2478,7 @@ yyreduce:
break;
case 71:
+/* Line 1787 of yacc.c */
#line 406 "cc.y"
{
(yyval.node) = Z;
@@ -2412,6 +2486,7 @@ yyreduce:
break;
case 72:
+/* Line 1787 of yacc.c */
#line 410 "cc.y"
{
(yyval.node) = new(OLIST, (yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node));
@@ -2419,6 +2494,7 @@ yyreduce:
break;
case 73:
+/* Line 1787 of yacc.c */
#line 414 "cc.y"
{
(yyval.node) = new(OLIST, (yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node));
@@ -2426,6 +2502,7 @@ yyreduce:
break;
case 75:
+/* Line 1787 of yacc.c */
#line 421 "cc.y"
{
(yyval.node) = new(OLIST, (yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node));
@@ -2433,6 +2510,7 @@ yyreduce:
break;
case 76:
+/* Line 1787 of yacc.c */
#line 427 "cc.y"
{
(yyval.node) = new(OCASE, (yyvsp[(2) - (3)].node), Z);
@@ -2440,6 +2518,7 @@ yyreduce:
break;
case 77:
+/* Line 1787 of yacc.c */
#line 431 "cc.y"
{
(yyval.node) = new(OCASE, Z, Z);
@@ -2447,6 +2526,7 @@ yyreduce:
break;
case 78:
+/* Line 1787 of yacc.c */
#line 435 "cc.y"
{
(yyval.node) = new(OLABEL, dcllabel((yyvsp[(1) - (2)].sym), 1), Z);
@@ -2454,6 +2534,7 @@ yyreduce:
break;
case 79:
+/* Line 1787 of yacc.c */
#line 441 "cc.y"
{
(yyval.node) = Z;
@@ -2461,6 +2542,7 @@ yyreduce:
break;
case 81:
+/* Line 1787 of yacc.c */
#line 446 "cc.y"
{
(yyval.node) = new(OLIST, (yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node));
@@ -2468,6 +2550,7 @@ yyreduce:
break;
case 83:
+/* Line 1787 of yacc.c */
#line 453 "cc.y"
{
(yyval.node) = (yyvsp[(2) - (2)].node);
@@ -2475,6 +2558,7 @@ yyreduce:
break;
case 85:
+/* Line 1787 of yacc.c */
#line 459 "cc.y"
{
markdcl();
@@ -2482,6 +2566,7 @@ yyreduce:
break;
case 86:
+/* Line 1787 of yacc.c */
#line 463 "cc.y"
{
(yyval.node) = revertdcl();
@@ -2493,6 +2578,7 @@ yyreduce:
break;
case 87:
+/* Line 1787 of yacc.c */
#line 471 "cc.y"
{
(yyval.node) = new(OIF, (yyvsp[(3) - (5)].node), new(OLIST, (yyvsp[(5) - (5)].node), Z));
@@ -2502,6 +2588,7 @@ yyreduce:
break;
case 88:
+/* Line 1787 of yacc.c */
#line 477 "cc.y"
{
(yyval.node) = new(OIF, (yyvsp[(3) - (7)].node), new(OLIST, (yyvsp[(5) - (7)].node), (yyvsp[(7) - (7)].node)));
@@ -2513,11 +2600,13 @@ yyreduce:
break;
case 89:
+/* Line 1787 of yacc.c */
#line 484 "cc.y"
{ markdcl(); }
break;
case 90:
+/* Line 1787 of yacc.c */
#line 485 "cc.y"
{
(yyval.node) = revertdcl();
@@ -2532,6 +2621,7 @@ yyreduce:
break;
case 91:
+/* Line 1787 of yacc.c */
#line 496 "cc.y"
{
(yyval.node) = new(OWHILE, (yyvsp[(3) - (5)].node), (yyvsp[(5) - (5)].node));
@@ -2539,6 +2629,7 @@ yyreduce:
break;
case 92:
+/* Line 1787 of yacc.c */
#line 500 "cc.y"
{
(yyval.node) = new(ODWHILE, (yyvsp[(5) - (7)].node), (yyvsp[(2) - (7)].node));
@@ -2546,6 +2637,7 @@ yyreduce:
break;
case 93:
+/* Line 1787 of yacc.c */
#line 504 "cc.y"
{
(yyval.node) = new(ORETURN, (yyvsp[(2) - (3)].node), Z);
@@ -2554,6 +2646,7 @@ yyreduce:
break;
case 94:
+/* Line 1787 of yacc.c */
#line 509 "cc.y"
{
(yyval.node) = new(OCONST, Z, Z);
@@ -2571,6 +2664,7 @@ yyreduce:
break;
case 95:
+/* Line 1787 of yacc.c */
#line 523 "cc.y"
{
(yyval.node) = new(OBREAK, Z, Z);
@@ -2578,6 +2672,7 @@ yyreduce:
break;
case 96:
+/* Line 1787 of yacc.c */
#line 527 "cc.y"
{
(yyval.node) = new(OCONTINUE, Z, Z);
@@ -2585,6 +2680,7 @@ yyreduce:
break;
case 97:
+/* Line 1787 of yacc.c */
#line 531 "cc.y"
{
(yyval.node) = new(OGOTO, dcllabel((yyvsp[(2) - (3)].sym), 0), Z);
@@ -2592,6 +2688,7 @@ yyreduce:
break;
case 98:
+/* Line 1787 of yacc.c */
#line 535 "cc.y"
{
(yyval.node) = new(OUSED, (yyvsp[(3) - (5)].node), Z);
@@ -2599,6 +2696,7 @@ yyreduce:
break;
case 99:
+/* Line 1787 of yacc.c */
#line 539 "cc.y"
{
(yyval.node) = new(OPREFETCH, (yyvsp[(3) - (5)].node), Z);
@@ -2606,6 +2704,7 @@ yyreduce:
break;
case 100:
+/* Line 1787 of yacc.c */
#line 543 "cc.y"
{
(yyval.node) = new(OSET, (yyvsp[(3) - (5)].node), Z);
@@ -2613,6 +2712,7 @@ yyreduce:
break;
case 101:
+/* Line 1787 of yacc.c */
#line 548 "cc.y"
{
(yyval.node) = Z;
@@ -2620,6 +2720,7 @@ yyreduce:
break;
case 103:
+/* Line 1787 of yacc.c */
#line 554 "cc.y"
{
(yyval.node) = Z;
@@ -2627,6 +2728,7 @@ yyreduce:
break;
case 105:
+/* Line 1787 of yacc.c */
#line 561 "cc.y"
{
(yyval.node) = new(OCAST, (yyvsp[(1) - (1)].node), Z);
@@ -2635,6 +2737,7 @@ yyreduce:
break;
case 107:
+/* Line 1787 of yacc.c */
#line 569 "cc.y"
{
(yyval.node) = new(OCOMMA, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
@@ -2642,6 +2745,7 @@ yyreduce:
break;
case 109:
+/* Line 1787 of yacc.c */
#line 576 "cc.y"
{
(yyval.node) = new(OMUL, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
@@ -2649,6 +2753,7 @@ yyreduce:
break;
case 110:
+/* Line 1787 of yacc.c */
#line 580 "cc.y"
{
(yyval.node) = new(ODIV, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
@@ -2656,6 +2761,7 @@ yyreduce:
break;
case 111:
+/* Line 1787 of yacc.c */
#line 584 "cc.y"
{
(yyval.node) = new(OMOD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
@@ -2663,6 +2769,7 @@ yyreduce:
break;
case 112:
+/* Line 1787 of yacc.c */
#line 588 "cc.y"
{
(yyval.node) = new(OADD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
@@ -2670,6 +2777,7 @@ yyreduce:
break;
case 113:
+/* Line 1787 of yacc.c */
#line 592 "cc.y"
{
(yyval.node) = new(OSUB, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
@@ -2677,6 +2785,7 @@ yyreduce:
break;
case 114:
+/* Line 1787 of yacc.c */
#line 596 "cc.y"
{
(yyval.node) = new(OASHR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
@@ -2684,6 +2793,7 @@ yyreduce:
break;
case 115:
+/* Line 1787 of yacc.c */
#line 600 "cc.y"
{
(yyval.node) = new(OASHL, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
@@ -2691,6 +2801,7 @@ yyreduce:
break;
case 116:
+/* Line 1787 of yacc.c */
#line 604 "cc.y"
{
(yyval.node) = new(OLT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
@@ -2698,6 +2809,7 @@ yyreduce:
break;
case 117:
+/* Line 1787 of yacc.c */
#line 608 "cc.y"
{
(yyval.node) = new(OGT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
@@ -2705,6 +2817,7 @@ yyreduce:
break;
case 118:
+/* Line 1787 of yacc.c */
#line 612 "cc.y"
{
(yyval.node) = new(OLE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
@@ -2712,6 +2825,7 @@ yyreduce:
break;
case 119:
+/* Line 1787 of yacc.c */
#line 616 "cc.y"
{
(yyval.node) = new(OGE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
@@ -2719,6 +2833,7 @@ yyreduce:
break;
case 120:
+/* Line 1787 of yacc.c */
#line 620 "cc.y"
{
(yyval.node) = new(OEQ, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
@@ -2726,6 +2841,7 @@ yyreduce:
break;
case 121:
+/* Line 1787 of yacc.c */
#line 624 "cc.y"
{
(yyval.node) = new(ONE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
@@ -2733,6 +2849,7 @@ yyreduce:
break;
case 122:
+/* Line 1787 of yacc.c */
#line 628 "cc.y"
{
(yyval.node) = new(OAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
@@ -2740,6 +2857,7 @@ yyreduce:
break;
case 123:
+/* Line 1787 of yacc.c */
#line 632 "cc.y"
{
(yyval.node) = new(OXOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
@@ -2747,6 +2865,7 @@ yyreduce:
break;
case 124:
+/* Line 1787 of yacc.c */
#line 636 "cc.y"
{
(yyval.node) = new(OOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
@@ -2754,6 +2873,7 @@ yyreduce:
break;
case 125:
+/* Line 1787 of yacc.c */
#line 640 "cc.y"
{
(yyval.node) = new(OANDAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
@@ -2761,6 +2881,7 @@ yyreduce:
break;
case 126:
+/* Line 1787 of yacc.c */
#line 644 "cc.y"
{
(yyval.node) = new(OOROR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
@@ -2768,6 +2889,7 @@ yyreduce:
break;
case 127:
+/* Line 1787 of yacc.c */
#line 648 "cc.y"
{
(yyval.node) = new(OCOND, (yyvsp[(1) - (5)].node), new(OLIST, (yyvsp[(3) - (5)].node), (yyvsp[(5) - (5)].node)));
@@ -2775,6 +2897,7 @@ yyreduce:
break;
case 128:
+/* Line 1787 of yacc.c */
#line 652 "cc.y"
{
(yyval.node) = new(OAS, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
@@ -2782,6 +2905,7 @@ yyreduce:
break;
case 129:
+/* Line 1787 of yacc.c */
#line 656 "cc.y"
{
(yyval.node) = new(OASADD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
@@ -2789,6 +2913,7 @@ yyreduce:
break;
case 130:
+/* Line 1787 of yacc.c */
#line 660 "cc.y"
{
(yyval.node) = new(OASSUB, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
@@ -2796,6 +2921,7 @@ yyreduce:
break;
case 131:
+/* Line 1787 of yacc.c */
#line 664 "cc.y"
{
(yyval.node) = new(OASMUL, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
@@ -2803,6 +2929,7 @@ yyreduce:
break;
case 132:
+/* Line 1787 of yacc.c */
#line 668 "cc.y"
{
(yyval.node) = new(OASDIV, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
@@ -2810,6 +2937,7 @@ yyreduce:
break;
case 133:
+/* Line 1787 of yacc.c */
#line 672 "cc.y"
{
(yyval.node) = new(OASMOD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
@@ -2817,6 +2945,7 @@ yyreduce:
break;
case 134:
+/* Line 1787 of yacc.c */
#line 676 "cc.y"
{
(yyval.node) = new(OASASHL, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
@@ -2824,6 +2953,7 @@ yyreduce:
break;
case 135:
+/* Line 1787 of yacc.c */
#line 680 "cc.y"
{
(yyval.node) = new(OASASHR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
@@ -2831,6 +2961,7 @@ yyreduce:
break;
case 136:
+/* Line 1787 of yacc.c */
#line 684 "cc.y"
{
(yyval.node) = new(OASAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
@@ -2838,6 +2969,7 @@ yyreduce:
break;
case 137:
+/* Line 1787 of yacc.c */
#line 688 "cc.y"
{
(yyval.node) = new(OASXOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
@@ -2845,6 +2977,7 @@ yyreduce:
break;
case 138:
+/* Line 1787 of yacc.c */
#line 692 "cc.y"
{
(yyval.node) = new(OASOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
@@ -2852,6 +2985,7 @@ yyreduce:
break;
case 140:
+/* Line 1787 of yacc.c */
#line 699 "cc.y"
{
(yyval.node) = new(OCAST, (yyvsp[(5) - (5)].node), Z);
@@ -2862,6 +2996,7 @@ yyreduce:
break;
case 141:
+/* Line 1787 of yacc.c */
#line 706 "cc.y"
{
(yyval.node) = new(OSTRUCT, (yyvsp[(6) - (7)].node), Z);
@@ -2871,6 +3006,7 @@ yyreduce:
break;
case 143:
+/* Line 1787 of yacc.c */
#line 715 "cc.y"
{
(yyval.node) = new(OIND, (yyvsp[(2) - (2)].node), Z);
@@ -2878,6 +3014,7 @@ yyreduce:
break;
case 144:
+/* Line 1787 of yacc.c */
#line 719 "cc.y"
{
(yyval.node) = new(OADDR, (yyvsp[(2) - (2)].node), Z);
@@ -2885,6 +3022,7 @@ yyreduce:
break;
case 145:
+/* Line 1787 of yacc.c */
#line 723 "cc.y"
{
(yyval.node) = new(OPOS, (yyvsp[(2) - (2)].node), Z);
@@ -2892,6 +3030,7 @@ yyreduce:
break;
case 146:
+/* Line 1787 of yacc.c */
#line 727 "cc.y"
{
(yyval.node) = new(ONEG, (yyvsp[(2) - (2)].node), Z);
@@ -2899,6 +3038,7 @@ yyreduce:
break;
case 147:
+/* Line 1787 of yacc.c */
#line 731 "cc.y"
{
(yyval.node) = new(ONOT, (yyvsp[(2) - (2)].node), Z);
@@ -2906,6 +3046,7 @@ yyreduce:
break;
case 148:
+/* Line 1787 of yacc.c */
#line 735 "cc.y"
{
(yyval.node) = new(OCOM, (yyvsp[(2) - (2)].node), Z);
@@ -2913,6 +3054,7 @@ yyreduce:
break;
case 149:
+/* Line 1787 of yacc.c */
#line 739 "cc.y"
{
(yyval.node) = new(OPREINC, (yyvsp[(2) - (2)].node), Z);
@@ -2920,6 +3062,7 @@ yyreduce:
break;
case 150:
+/* Line 1787 of yacc.c */
#line 743 "cc.y"
{
(yyval.node) = new(OPREDEC, (yyvsp[(2) - (2)].node), Z);
@@ -2927,6 +3070,7 @@ yyreduce:
break;
case 151:
+/* Line 1787 of yacc.c */
#line 747 "cc.y"
{
(yyval.node) = new(OSIZE, (yyvsp[(2) - (2)].node), Z);
@@ -2934,6 +3078,7 @@ yyreduce:
break;
case 152:
+/* Line 1787 of yacc.c */
#line 751 "cc.y"
{
(yyval.node) = new(OSIGN, (yyvsp[(2) - (2)].node), Z);
@@ -2941,6 +3086,7 @@ yyreduce:
break;
case 153:
+/* Line 1787 of yacc.c */
#line 757 "cc.y"
{
(yyval.node) = (yyvsp[(2) - (3)].node);
@@ -2948,6 +3094,7 @@ yyreduce:
break;
case 154:
+/* Line 1787 of yacc.c */
#line 761 "cc.y"
{
(yyval.node) = new(OSIZE, Z, Z);
@@ -2957,6 +3104,7 @@ yyreduce:
break;
case 155:
+/* Line 1787 of yacc.c */
#line 767 "cc.y"
{
(yyval.node) = new(OSIGN, Z, Z);
@@ -2966,6 +3114,7 @@ yyreduce:
break;
case 156:
+/* Line 1787 of yacc.c */
#line 773 "cc.y"
{
(yyval.node) = new(OFUNC, (yyvsp[(1) - (4)].node), Z);
@@ -2977,6 +3126,7 @@ yyreduce:
break;
case 157:
+/* Line 1787 of yacc.c */
#line 781 "cc.y"
{
(yyval.node) = new(OIND, new(OADD, (yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].node)), Z);
@@ -2984,6 +3134,7 @@ yyreduce:
break;
case 158:
+/* Line 1787 of yacc.c */
#line 785 "cc.y"
{
(yyval.node) = new(ODOT, new(OIND, (yyvsp[(1) - (3)].node), Z), Z);
@@ -2992,6 +3143,7 @@ yyreduce:
break;
case 159:
+/* Line 1787 of yacc.c */
#line 790 "cc.y"
{
(yyval.node) = new(ODOT, (yyvsp[(1) - (3)].node), Z);
@@ -3000,6 +3152,7 @@ yyreduce:
break;
case 160:
+/* Line 1787 of yacc.c */
#line 795 "cc.y"
{
(yyval.node) = new(OPOSTINC, (yyvsp[(1) - (2)].node), Z);
@@ -3007,6 +3160,7 @@ yyreduce:
break;
case 161:
+/* Line 1787 of yacc.c */
#line 799 "cc.y"
{
(yyval.node) = new(OPOSTDEC, (yyvsp[(1) - (2)].node), Z);
@@ -3014,6 +3168,7 @@ yyreduce:
break;
case 163:
+/* Line 1787 of yacc.c */
#line 804 "cc.y"
{
(yyval.node) = new(OCONST, Z, Z);
@@ -3024,6 +3179,7 @@ yyreduce:
break;
case 164:
+/* Line 1787 of yacc.c */
#line 811 "cc.y"
{
(yyval.node) = new(OCONST, Z, Z);
@@ -3034,6 +3190,7 @@ yyreduce:
break;
case 165:
+/* Line 1787 of yacc.c */
#line 818 "cc.y"
{
(yyval.node) = new(OCONST, Z, Z);
@@ -3044,6 +3201,7 @@ yyreduce:
break;
case 166:
+/* Line 1787 of yacc.c */
#line 825 "cc.y"
{
(yyval.node) = new(OCONST, Z, Z);
@@ -3054,6 +3212,7 @@ yyreduce:
break;
case 167:
+/* Line 1787 of yacc.c */
#line 832 "cc.y"
{
(yyval.node) = new(OCONST, Z, Z);
@@ -3064,6 +3223,7 @@ yyreduce:
break;
case 168:
+/* Line 1787 of yacc.c */
#line 839 "cc.y"
{
(yyval.node) = new(OCONST, Z, Z);
@@ -3074,6 +3234,7 @@ yyreduce:
break;
case 169:
+/* Line 1787 of yacc.c */
#line 846 "cc.y"
{
(yyval.node) = new(OCONST, Z, Z);
@@ -3084,6 +3245,7 @@ yyreduce:
break;
case 170:
+/* Line 1787 of yacc.c */
#line 853 "cc.y"
{
(yyval.node) = new(OCONST, Z, Z);
@@ -3094,6 +3256,7 @@ yyreduce:
break;
case 173:
+/* Line 1787 of yacc.c */
#line 864 "cc.y"
{
(yyval.node) = new(OSTRING, Z, Z);
@@ -3107,6 +3270,7 @@ yyreduce:
break;
case 174:
+/* Line 1787 of yacc.c */
#line 874 "cc.y"
{
char *s;
@@ -3126,12 +3290,13 @@ yyreduce:
break;
case 175:
+/* Line 1787 of yacc.c */
#line 892 "cc.y"
{
(yyval.node) = new(OLSTRING, Z, Z);
- (yyval.node)->type = typ(TARRAY, types[TUSHORT]);
- (yyval.node)->type->width = (yyvsp[(1) - (1)].sval).l + sizeof(ushort);
- (yyval.node)->rstring = (ushort*)(yyvsp[(1) - (1)].sval).s;
+ (yyval.node)->type = typ(TARRAY, types[TRUNE]);
+ (yyval.node)->type->width = (yyvsp[(1) - (1)].sval).l + sizeof(TRune);
+ (yyval.node)->rstring = (TRune*)(yyvsp[(1) - (1)].sval).s;
(yyval.node)->sym = symstring;
(yyval.node)->etype = TARRAY;
(yyval.node)->class = CSTATIC;
@@ -3139,25 +3304,27 @@ yyreduce:
break;
case 176:
+/* Line 1787 of yacc.c */
#line 902 "cc.y"
{
char *s;
int n;
- n = (yyvsp[(1) - (2)].node)->type->width - sizeof(ushort);
+ n = (yyvsp[(1) - (2)].node)->type->width - sizeof(TRune);
s = alloc(n+(yyvsp[(2) - (2)].sval).l+MAXALIGN);
memcpy(s, (yyvsp[(1) - (2)].node)->rstring, n);
memcpy(s+n, (yyvsp[(2) - (2)].sval).s, (yyvsp[(2) - (2)].sval).l);
- *(ushort*)(s+n+(yyvsp[(2) - (2)].sval).l) = 0;
+ *(TRune*)(s+n+(yyvsp[(2) - (2)].sval).l) = 0;
(yyval.node) = (yyvsp[(1) - (2)].node);
(yyval.node)->type->width += (yyvsp[(2) - (2)].sval).l;
- (yyval.node)->rstring = (ushort*)s;
+ (yyval.node)->rstring = (TRune*)s;
}
break;
case 177:
+/* Line 1787 of yacc.c */
#line 919 "cc.y"
{
(yyval.node) = Z;
@@ -3165,6 +3332,7 @@ yyreduce:
break;
case 180:
+/* Line 1787 of yacc.c */
#line 927 "cc.y"
{
(yyval.node) = new(OLIST, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
@@ -3172,6 +3340,7 @@ yyreduce:
break;
case 181:
+/* Line 1787 of yacc.c */
#line 933 "cc.y"
{
(yyval.tyty).t1 = strf;
@@ -3188,6 +3357,7 @@ yyreduce:
break;
case 182:
+/* Line 1787 of yacc.c */
#line 946 "cc.y"
{
(yyval.type) = strf;
@@ -3199,6 +3369,7 @@ yyreduce:
break;
case 183:
+/* Line 1787 of yacc.c */
#line 955 "cc.y"
{
lastclass = CXXX;
@@ -3207,6 +3378,7 @@ yyreduce:
break;
case 185:
+/* Line 1787 of yacc.c */
#line 963 "cc.y"
{
(yyval.tycl).t = (yyvsp[(1) - (1)].type);
@@ -3215,6 +3387,7 @@ yyreduce:
break;
case 186:
+/* Line 1787 of yacc.c */
#line 968 "cc.y"
{
(yyval.tycl).t = simplet((yyvsp[(1) - (1)].lval));
@@ -3223,6 +3396,7 @@ yyreduce:
break;
case 187:
+/* Line 1787 of yacc.c */
#line 973 "cc.y"
{
(yyval.tycl).t = simplet((yyvsp[(1) - (1)].lval));
@@ -3232,6 +3406,7 @@ yyreduce:
break;
case 188:
+/* Line 1787 of yacc.c */
#line 979 "cc.y"
{
(yyval.tycl).t = (yyvsp[(1) - (2)].type);
@@ -3243,6 +3418,7 @@ yyreduce:
break;
case 189:
+/* Line 1787 of yacc.c */
#line 987 "cc.y"
{
(yyval.tycl).t = simplet(typebitor((yyvsp[(1) - (2)].lval), (yyvsp[(2) - (2)].lval)));
@@ -3252,6 +3428,7 @@ yyreduce:
break;
case 190:
+/* Line 1787 of yacc.c */
#line 993 "cc.y"
{
(yyval.tycl).t = (yyvsp[(2) - (3)].type);
@@ -3261,6 +3438,7 @@ yyreduce:
break;
case 191:
+/* Line 1787 of yacc.c */
#line 999 "cc.y"
{
(yyval.tycl).t = simplet((yyvsp[(2) - (2)].lval));
@@ -3270,6 +3448,7 @@ yyreduce:
break;
case 192:
+/* Line 1787 of yacc.c */
#line 1005 "cc.y"
{
(yyval.tycl).t = simplet(typebitor((yyvsp[(2) - (3)].lval), (yyvsp[(3) - (3)].lval)));
@@ -3279,6 +3458,7 @@ yyreduce:
break;
case 193:
+/* Line 1787 of yacc.c */
#line 1013 "cc.y"
{
(yyval.type) = (yyvsp[(1) - (1)].tycl).t;
@@ -3288,6 +3468,7 @@ yyreduce:
break;
case 194:
+/* Line 1787 of yacc.c */
#line 1021 "cc.y"
{
lasttype = (yyvsp[(1) - (1)].tycl).t;
@@ -3296,6 +3477,7 @@ yyreduce:
break;
case 195:
+/* Line 1787 of yacc.c */
#line 1028 "cc.y"
{
dotag((yyvsp[(2) - (2)].sym), TSTRUCT, 0);
@@ -3304,6 +3486,7 @@ yyreduce:
break;
case 196:
+/* Line 1787 of yacc.c */
#line 1033 "cc.y"
{
dotag((yyvsp[(2) - (2)].sym), TSTRUCT, autobn);
@@ -3311,6 +3494,7 @@ yyreduce:
break;
case 197:
+/* Line 1787 of yacc.c */
#line 1037 "cc.y"
{
(yyval.type) = (yyvsp[(2) - (4)].sym)->suetag;
@@ -3322,6 +3506,7 @@ yyreduce:
break;
case 198:
+/* Line 1787 of yacc.c */
#line 1045 "cc.y"
{
taggen++;
@@ -3333,6 +3518,7 @@ yyreduce:
break;
case 199:
+/* Line 1787 of yacc.c */
#line 1053 "cc.y"
{
dotag((yyvsp[(2) - (2)].sym), TUNION, 0);
@@ -3341,6 +3527,7 @@ yyreduce:
break;
case 200:
+/* Line 1787 of yacc.c */
#line 1058 "cc.y"
{
dotag((yyvsp[(2) - (2)].sym), TUNION, autobn);
@@ -3348,6 +3535,7 @@ yyreduce:
break;
case 201:
+/* Line 1787 of yacc.c */
#line 1062 "cc.y"
{
(yyval.type) = (yyvsp[(2) - (4)].sym)->suetag;
@@ -3359,6 +3547,7 @@ yyreduce:
break;
case 202:
+/* Line 1787 of yacc.c */
#line 1070 "cc.y"
{
taggen++;
@@ -3370,6 +3559,7 @@ yyreduce:
break;
case 203:
+/* Line 1787 of yacc.c */
#line 1078 "cc.y"
{
dotag((yyvsp[(2) - (2)].sym), TENUM, 0);
@@ -3381,6 +3571,7 @@ yyreduce:
break;
case 204:
+/* Line 1787 of yacc.c */
#line 1086 "cc.y"
{
dotag((yyvsp[(2) - (2)].sym), TENUM, autobn);
@@ -3388,6 +3579,7 @@ yyreduce:
break;
case 205:
+/* Line 1787 of yacc.c */
#line 1090 "cc.y"
{
en.tenum = T;
@@ -3396,6 +3588,7 @@ yyreduce:
break;
case 206:
+/* Line 1787 of yacc.c */
#line 1095 "cc.y"
{
(yyval.type) = (yyvsp[(2) - (7)].sym)->suetag;
@@ -3411,6 +3604,7 @@ yyreduce:
break;
case 207:
+/* Line 1787 of yacc.c */
#line 1107 "cc.y"
{
en.tenum = T;
@@ -3419,6 +3613,7 @@ yyreduce:
break;
case 208:
+/* Line 1787 of yacc.c */
#line 1112 "cc.y"
{
(yyval.type) = en.tenum;
@@ -3426,6 +3621,7 @@ yyreduce:
break;
case 209:
+/* Line 1787 of yacc.c */
#line 1116 "cc.y"
{
(yyval.type) = tcopy((yyvsp[(1) - (1)].sym)->type);
@@ -3433,6 +3629,7 @@ yyreduce:
break;
case 211:
+/* Line 1787 of yacc.c */
#line 1123 "cc.y"
{
(yyval.lval) = typebitor((yyvsp[(1) - (2)].lval), (yyvsp[(2) - (2)].lval));
@@ -3440,6 +3637,7 @@ yyreduce:
break;
case 212:
+/* Line 1787 of yacc.c */
#line 1128 "cc.y"
{
(yyval.lval) = 0;
@@ -3447,6 +3645,7 @@ yyreduce:
break;
case 213:
+/* Line 1787 of yacc.c */
#line 1132 "cc.y"
{
(yyval.lval) = typebitor((yyvsp[(1) - (2)].lval), (yyvsp[(2) - (2)].lval));
@@ -3454,6 +3653,7 @@ yyreduce:
break;
case 218:
+/* Line 1787 of yacc.c */
#line 1144 "cc.y"
{
(yyval.lval) = typebitor((yyvsp[(1) - (2)].lval), (yyvsp[(2) - (2)].lval));
@@ -3461,6 +3661,7 @@ yyreduce:
break;
case 221:
+/* Line 1787 of yacc.c */
#line 1154 "cc.y"
{
doenum((yyvsp[(1) - (1)].sym), Z);
@@ -3468,6 +3669,7 @@ yyreduce:
break;
case 222:
+/* Line 1787 of yacc.c */
#line 1158 "cc.y"
{
doenum((yyvsp[(1) - (3)].sym), (yyvsp[(3) - (3)].node));
@@ -3475,101 +3677,121 @@ yyreduce:
break;
case 225:
+/* Line 1787 of yacc.c */
#line 1165 "cc.y"
{ (yyval.lval) = BCHAR; }
break;
case 226:
+/* Line 1787 of yacc.c */
#line 1166 "cc.y"
{ (yyval.lval) = BSHORT; }
break;
case 227:
+/* Line 1787 of yacc.c */
#line 1167 "cc.y"
{ (yyval.lval) = BINT; }
break;
case 228:
+/* Line 1787 of yacc.c */
#line 1168 "cc.y"
{ (yyval.lval) = BLONG; }
break;
case 229:
+/* Line 1787 of yacc.c */
#line 1169 "cc.y"
{ (yyval.lval) = BSIGNED; }
break;
case 230:
+/* Line 1787 of yacc.c */
#line 1170 "cc.y"
{ (yyval.lval) = BUNSIGNED; }
break;
case 231:
+/* Line 1787 of yacc.c */
#line 1171 "cc.y"
{ (yyval.lval) = BFLOAT; }
break;
case 232:
+/* Line 1787 of yacc.c */
#line 1172 "cc.y"
{ (yyval.lval) = BDOUBLE; }
break;
case 233:
+/* Line 1787 of yacc.c */
#line 1173 "cc.y"
{ (yyval.lval) = BVOID; }
break;
case 234:
+/* Line 1787 of yacc.c */
#line 1176 "cc.y"
{ (yyval.lval) = BAUTO; }
break;
case 235:
+/* Line 1787 of yacc.c */
#line 1177 "cc.y"
{ (yyval.lval) = BSTATIC; }
break;
case 236:
+/* Line 1787 of yacc.c */
#line 1178 "cc.y"
{ (yyval.lval) = BEXTERN; }
break;
case 237:
+/* Line 1787 of yacc.c */
#line 1179 "cc.y"
{ (yyval.lval) = BTYPEDEF; }
break;
case 238:
+/* Line 1787 of yacc.c */
#line 1180 "cc.y"
{ (yyval.lval) = BTYPESTR; }
break;
case 239:
+/* Line 1787 of yacc.c */
#line 1181 "cc.y"
{ (yyval.lval) = BREGISTER; }
break;
case 240:
+/* Line 1787 of yacc.c */
#line 1182 "cc.y"
{ (yyval.lval) = 0; }
break;
case 241:
+/* Line 1787 of yacc.c */
#line 1185 "cc.y"
{ (yyval.lval) = BCONSTNT; }
break;
case 242:
+/* Line 1787 of yacc.c */
#line 1186 "cc.y"
{ (yyval.lval) = BVOLATILE; }
break;
case 243:
+/* Line 1787 of yacc.c */
#line 1187 "cc.y"
{ (yyval.lval) = 0; }
break;
case 244:
+/* Line 1787 of yacc.c */
#line 1191 "cc.y"
{
(yyval.node) = new(ONAME, Z, Z);
@@ -3587,6 +3809,7 @@ yyreduce:
break;
case 245:
+/* Line 1787 of yacc.c */
#line 1206 "cc.y"
{
(yyval.node) = new(ONAME, Z, Z);
@@ -3601,10 +3824,21 @@ yyreduce:
break;
-/* Line 1267 of yacc.c. */
-#line 3606 "y.tab.c"
+/* Line 1787 of yacc.c */
+#line 3829 "y.tab.c"
default: break;
}
+ /* User semantic actions sometimes alter yychar, and that requires
+ that yytoken be updated with the new translation. We take the
+ approach of translating immediately before every use of yytoken.
+ One alternative is translating here after every semantic action,
+ but that translation would be missed if the semantic action invokes
+ YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or
+ if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an
+ incorrect destructor might then be invoked immediately. In the
+ case of YYERROR or YYBACKUP, subsequent parser actions might lead
+ to an incorrect destructor call or verbose syntax error message
+ before the lookahead is translated. */
YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
YYPOPSTACK (yylen);
@@ -3613,7 +3847,6 @@ yyreduce:
*++yyvsp = yyval;
-
/* Now `shift' the result of the reduction. Determine what state
that goes to, based on the state we popped back to and the rule
number reduced by. */
@@ -3633,6 +3866,10 @@ yyreduce:
| yyerrlab -- here on detecting error |
`------------------------------------*/
yyerrlab:
+ /* Make sure we have latest lookahead translation. See comments at
+ user semantic actions for why this is necessary. */
+ yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar);
+
/* If not already recovering from an error, report this error. */
if (!yyerrstatus)
{
@@ -3640,37 +3877,36 @@ yyerrlab:
#if ! YYERROR_VERBOSE
yyerror (YY_("syntax error"));
#else
+# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \
+ yyssp, yytoken)
{
- YYSIZE_T yysize = yysyntax_error (0, yystate, yychar);
- if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM)
- {
- YYSIZE_T yyalloc = 2 * yysize;
- if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM))
- yyalloc = YYSTACK_ALLOC_MAXIMUM;
- if (yymsg != yymsgbuf)
- YYSTACK_FREE (yymsg);
- yymsg = (char *) YYSTACK_ALLOC (yyalloc);
- if (yymsg)
- yymsg_alloc = yyalloc;
- else
- {
- yymsg = yymsgbuf;
- yymsg_alloc = sizeof yymsgbuf;
- }
- }
-
- if (0 < yysize && yysize <= yymsg_alloc)
- {
- (void) yysyntax_error (yymsg, yystate, yychar);
- yyerror (yymsg);
- }
- else
- {
- yyerror (YY_("syntax error"));
- if (yysize != 0)
- goto yyexhaustedlab;
- }
+ char const *yymsgp = YY_("syntax error");
+ int yysyntax_error_status;
+ yysyntax_error_status = YYSYNTAX_ERROR;
+ if (yysyntax_error_status == 0)
+ yymsgp = yymsg;
+ else if (yysyntax_error_status == 1)
+ {
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+ yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc);
+ if (!yymsg)
+ {
+ yymsg = yymsgbuf;
+ yymsg_alloc = sizeof yymsgbuf;
+ yysyntax_error_status = 2;
+ }
+ else
+ {
+ yysyntax_error_status = YYSYNTAX_ERROR;
+ yymsgp = yymsg;
+ }
+ }
+ yyerror (yymsgp);
+ if (yysyntax_error_status == 2)
+ goto yyexhaustedlab;
}
+# undef YYSYNTAX_ERROR
#endif
}
@@ -3678,7 +3914,7 @@ yyerrlab:
if (yyerrstatus == 3)
{
- /* If just tried and failed to reuse look-ahead token after an
+ /* If just tried and failed to reuse lookahead token after an
error, discard it. */
if (yychar <= YYEOF)
@@ -3695,7 +3931,7 @@ yyerrlab:
}
}
- /* Else will try to reuse look-ahead token after shifting the error
+ /* Else will try to reuse lookahead token after shifting the error
token. */
goto yyerrlab1;
@@ -3729,7 +3965,7 @@ yyerrlab1:
for (;;)
{
yyn = yypact[yystate];
- if (yyn != YYPACT_NINF)
+ if (!yypact_value_is_default (yyn))
{
yyn += YYTERROR;
if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
@@ -3752,10 +3988,9 @@ yyerrlab1:
YY_STACK_PRINT (yyss, yyssp);
}
- if (yyn == YYFINAL)
- YYACCEPT;
-
+ YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
*++yyvsp = yylval;
+ YY_IGNORE_MAYBE_UNINITIALIZED_END
/* Shift the error token. */
@@ -3779,7 +4014,7 @@ yyabortlab:
yyresult = 1;
goto yyreturn;
-#ifndef yyoverflow
+#if !defined yyoverflow || YYERROR_VERBOSE
/*-------------------------------------------------.
| yyexhaustedlab -- memory exhaustion comes here. |
`-------------------------------------------------*/
@@ -3790,9 +4025,14 @@ yyexhaustedlab:
#endif
yyreturn:
- if (yychar != YYEOF && yychar != YYEMPTY)
- yydestruct ("Cleanup: discarding lookahead",
- yytoken, &yylval);
+ if (yychar != YYEMPTY)
+ {
+ /* Make sure we have latest lookahead translation. See comments at
+ user semantic actions for why this is necessary. */
+ yytoken = YYTRANSLATE (yychar);
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval);
+ }
/* Do not reclaim the symbols of the rule which action triggered
this YYABORT or YYACCEPT. */
YYPOPSTACK (yylen);
@@ -3816,6 +4056,6 @@ yyreturn:
}
+/* Line 2050 of yacc.c */
#line 1219 "cc.y"
-
diff --git a/src/cmd/cc/y.tab.h b/src/cmd/cc/y.tab.h
index 32daca9b6..b26d659ef 100644
--- a/src/cmd/cc/y.tab.h
+++ b/src/cmd/cc/y.tab.h
@@ -1,24 +1,21 @@
-/* A Bison parser, made by GNU Bison 2.3. */
+/* A Bison parser, made by GNU Bison 2.7.12-4996. */
-/* Skeleton interface for Bison's Yacc-like parsers in C
-
- Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
- Free Software Foundation, Inc.
-
- This program is free software; you can redistribute it and/or modify
+/* Bison interface for Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
+
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* As a special exception, you may create a larger work that contains
part or all of the Bison parser skeleton and distribute that work
@@ -29,10 +26,20 @@
special exception, which will cause the skeleton and the resulting
Bison output files to be licensed under the GNU General Public
License without this special exception.
-
+
This special exception was added by the Free Software Foundation in
version 2.2 of Bison. */
+#ifndef YY_YY_Y_TAB_H_INCLUDED
+# define YY_YY_Y_TAB_H_INCLUDED
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+#if YYDEBUG
+extern int yydebug;
+#endif
+
/* Tokens. */
#ifndef YYTOKENTYPE
# define YYTOKENTYPE
@@ -189,11 +196,12 @@
-
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
typedef union YYSTYPE
-#line 36 "cc.y"
{
+/* Line 2053 of yacc.c */
+#line 36 "cc.y"
+
Node* node;
Sym* sym;
Type* type;
@@ -217,14 +225,30 @@ typedef union YYSTYPE
int32 lval;
double dval;
vlong vval;
-}
-/* Line 1529 of yacc.c. */
-#line 223 "y.tab.h"
- YYSTYPE;
+
+
+/* Line 2053 of yacc.c */
+#line 232 "y.tab.h"
+} YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
# define YYSTYPE_IS_DECLARED 1
-# define YYSTYPE_IS_TRIVIAL 1
#endif
extern YYSTYPE yylval;
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+#endif /* !YY_YY_Y_TAB_H_INCLUDED */
diff --git a/src/cmd/cgo/ast.go b/src/cmd/cgo/ast.go
index dbae3b7b1..7757efa1b 100644
--- a/src/cmd/cgo/ast.go
+++ b/src/cmd/cgo/ast.go
@@ -13,6 +13,7 @@ import (
"go/scanner"
"go/token"
"os"
+ "path/filepath"
"strings"
)
@@ -44,6 +45,13 @@ func sourceLine(n ast.Node) int {
// a list of exported functions, and the actual AST, to be rewritten and
// printed.
func (f *File) ReadGo(name string) {
+ // Create absolute path for file, so that it will be used in error
+ // messages and recorded in debug line number information.
+ // This matches the rest of the toolchain. See golang.org/issue/5122.
+ if aname, err := filepath.Abs(name); err == nil {
+ name = aname
+ }
+
// Two different parses: once with comments, once without.
// The printer is not good enough at printing comments in the
// right place when we start editing the AST behind its back,
@@ -179,6 +187,13 @@ func (f *File) saveRef(x interface{}, context string) {
error_(sel.Pos(), "cannot refer to errno directly; see documentation")
return
}
+ if goname == "_CMalloc" {
+ error_(sel.Pos(), "cannot refer to C._CMalloc; use C.malloc")
+ return
+ }
+ if goname == "malloc" {
+ goname = "_CMalloc"
+ }
name := f.Name[goname]
if name == nil {
name = &Name{
diff --git a/src/cmd/cgo/doc.go b/src/cmd/cgo/doc.go
index a1b02d4be..605bab6d2 100644
--- a/src/cmd/cgo/doc.go
+++ b/src/cmd/cgo/doc.go
@@ -6,15 +6,11 @@
Cgo enables the creation of Go packages that call C code.
-Usage:
- go tool cgo [compiler options] file.go
-
-The compiler options are passed through uninterpreted when
-invoking gcc to compile the C parts of the package.
+Using cgo with the go command
-The input file.go is a syntactically valid Go source file that imports
-the pseudo-package "C" and then refers to types such as C.size_t,
-variables such as C.stdout, or functions such as C.putchar.
+To use cgo write normal Go code that imports a pseudo-package "C".
+The Go code can then refer to types such as C.size_t, variables such
+as C.stdout, or functions such as C.putchar.
If the import of "C" is immediately preceded by a comment, that
comment, called the preamble, is used as a header when compiling
@@ -24,19 +20,25 @@ the C parts of the package. For example:
// #include <errno.h>
import "C"
-CFLAGS and LDFLAGS may be defined with pseudo #cgo directives
-within these comments to tweak the behavior of gcc. Values defined
-in multiple directives are concatenated together. Options prefixed
-by $GOOS, $GOARCH, or $GOOS/$GOARCH are only defined in matching
-systems. For example:
+See $GOROOT/misc/cgo/stdio and $GOROOT/misc/cgo/gmp for examples. See
+"C? Go? Cgo!" for an introduction to using cgo:
+http://golang.org/doc/articles/c_go_cgo.html.
+
+CFLAGS, CPPFLAGS, CXXFLAGS and LDFLAGS may be defined with pseudo #cgo
+directives within these comments to tweak the behavior of the C or C++
+compiler. Values defined in multiple directives are concatenated
+together. The directive can include a list of build constraints limiting its
+effect to systems satisfying one of the constraints
+(see http://golang.org/pkg/go/build/#hdr-Build_Constraints for details about the constraint syntax).
+For example:
// #cgo CFLAGS: -DPNG_DEBUG=1
- // #cgo linux CFLAGS: -DLINUX=1
+ // #cgo amd64 386 CFLAGS: -DX86=1
// #cgo LDFLAGS: -lpng
// #include <png.h>
import "C"
-Alternatively, CFLAGS and LDFLAGS may be obtained via the pkg-config
+Alternatively, CPPFLAGS and LDFLAGS may be obtained via the pkg-config
tool using a '#cgo pkg-config:' directive followed by the package names.
For example:
@@ -44,12 +46,26 @@ For example:
// #include <png.h>
import "C"
-The CGO_CFLAGS and CGO_LDFLAGS environment variables are added
-to the flags derived from these directives. Package-specific flags should
-be set using the directives, not the environment variables, so that builds
-work in unmodified environments.
-
-Within the Go file, C identifiers or field names that are keywords in Go
+When building, the CGO_CFLAGS, CGO_CPPFLAGS, CGO_CXXFLAGS and
+CGO_LDFLAGS environment variables are added to the flags derived from
+these directives. Package-specific flags should be set using the
+directives, not the environment variables, so that builds work in
+unmodified environments.
+
+When the Go tool sees that one or more Go files use the special import
+"C", it will look for other non-Go files in the directory and compile
+them as part of the Go package. Any .c, .s, or .S files will be
+compiled with the C compiler. Any .cc, .cpp, or .cxx files will be
+compiled with the C++ compiler. Any .h, .hh, .hpp, or .hxx files will
+not be compiled separately, but, if these header files are changed,
+the C and C++ files will be recompiled. The default C and C++
+compilers may be changed by the CC and CXX environment variables,
+respectively; those environment variables may include command line
+options.
+
+Go references to C
+
+Within the Go file, C's struct field names that are keywords in Go
can be accessed by prefixing them with an underscore: if x points at a C
struct with a field named "type", x._type accesses the field.
@@ -63,6 +79,9 @@ The C type void* is represented by Go's unsafe.Pointer.
To access a struct, union, or enum type directly, prefix it with
struct_, union_, or enum_, as in C.struct_stat.
+As Go doesn't have support for C's union type in the general case,
+C's union types are represented as a Go byte array with the same length.
+
Go structs cannot embed fields with C types.
Any C function (even void functions) may be called in a multiple
@@ -73,6 +92,34 @@ function returns void). For example:
n, err := C.sqrt(-1)
_, err := C.voidFunc()
+Calling C function pointers is currently not supported, however you can
+declare Go variables which hold C function pointers and pass them
+back and forth between Go and C. C code may call function pointers
+received from Go. For example:
+
+ package main
+
+ // typedef int (*intFunc) ();
+ //
+ // int
+ // bridge_int_func(intFunc f)
+ // {
+ // return f();
+ // }
+ //
+ // int fortytwo()
+ // {
+ // return 42;
+ // }
+ import "C"
+ import "fmt"
+
+ func main() {
+ f := C.intFunc(C.fortytwo)
+ fmt.Println(int(C.bridge_int_func(f)))
+ // Output: 42
+ }
+
In C, a function argument written as a fixed size array
actually requires a pointer to the first element of the array.
C compilers are aware of this calling convention and adjust
@@ -98,6 +145,8 @@ by making copies of the data. In pseudo-Go definitions:
// C pointer, length to Go []byte
func C.GoBytes(unsafe.Pointer, C.int) []byte
+C references to Go
+
Go functions can be exported for use by C code in the following way:
//export MyFunction
@@ -111,7 +160,7 @@ They will be available in the C code as:
extern int64 MyFunction(int arg1, int arg2, GoString arg3);
extern struct MyFunction2_return MyFunction2(int arg1, int arg2, GoString arg3);
-found in _cgo_export.h generated header, after any preambles
+found in the _cgo_export.h generated header, after any preambles
copied from the cgo input files. Functions with multiple
return values are mapped to functions returning a struct.
Not all Go types can be mapped to C types in a useful way.
@@ -121,17 +170,54 @@ since it is copied into two different C output files, it must not
contain any definitions, only declarations. Definitions must be
placed in preambles in other files, or in C source files.
-Cgo transforms the input file into four output files: two Go source
-files, a C file for 6c (or 8c or 5c), and a C file for gcc.
+Using cgo directly
-The standard package construction rules of the go command
-automate the process of using cgo. See $GOROOT/misc/cgo/stdio
-and $GOROOT/misc/cgo/gmp for examples.
+Usage:
+ go tool cgo [cgo options] [-- compiler options] file.go
-Cgo does not yet work with gccgo.
+Cgo transforms the input file.go into four output files: two Go source
+files, a C file for 6c (or 8c or 5c), and a C file for gcc.
-See "C? Go? Cgo!" for an introduction to using cgo:
-http://golang.org/doc/articles/c_go_cgo.html
+The compiler options are passed through uninterpreted when
+invoking the C compiler to compile the C parts of the package.
+
+The following options are available when running cgo directly:
+
+ -dynimport file
+ Write list of symbols imported by file. Write to
+ -dynout argument or to standard output. Used by go
+ build when building a cgo package.
+ -dynout file
+ Write -dynimport output to file.
+ -dynlinker
+ Write dynamic linker as part of -dynimport output.
+ -godefs
+ Write out input file in Go syntax replacing C package
+ names with real values. Used to generate files in the
+ syscall package when bootstrapping a new target.
+ -cdefs
+ Like -godefs, but write file in C syntax.
+ Used to generate files in the runtime package when
+ bootstrapping a new target.
+ -objdir directory
+ Put all generated files in directory.
+ -gccgo
+ Generate output for the gccgo compiler rather than the
+ gc compiler.
+ -gccgoprefix prefix
+ The -fgo-prefix option to be used with gccgo.
+ -gccgopkgpath path
+ The -fgo-pkgpath option to be used with gccgo.
+ -import_runtime_cgo
+ If set (which it is by default) import runtime/cgo in
+ generated output.
+ -import_syscall
+ If set (which it is by default) import syscall in
+ generated output.
+ -debug-define
+ Debugging option. Print #defines.
+ -debug-gcc
+ Debugging option. Trace C compiler execution and output.
*/
package main
@@ -183,29 +269,30 @@ Next, cgo needs to identify the kinds for each identifier. For the
identifiers C.foo and C.bar, cgo generates this C program:
<preamble>
- void __cgo__f__(void) {
- #line 1 "cgo-test"
- foo;
- enum { _cgo_enum_0 = foo };
- bar;
- enum { _cgo_enum_1 = bar };
- }
-
-This program will not compile, but cgo can look at the error messages
-to infer the kind of each identifier. The line number given in the
-error tells cgo which identifier is involved.
-
-An error like "unexpected type name" or "useless type name in empty
-declaration" or "declaration does not declare anything" tells cgo that
-the identifier is a type.
-
-An error like "statement with no effect" or "expression result unused"
-tells cgo that the identifier is not a type, but not whether it is a
-constant, function, or global variable.
-
-An error like "not an integer constant" tells cgo that the identifier
-is not a constant. If it is also not a type, it must be a function or
-global variable. For now, those can be treated the same.
+ #line 1 "not-declared"
+ void __cgo_f_xxx_1(void) { __typeof__(foo) *__cgo_undefined__; }
+ #line 1 "not-type"
+ void __cgo_f_xxx_2(void) { foo *__cgo_undefined__; }
+ #line 1 "not-const"
+ void __cgo_f_xxx_3(void) { enum { __cgo_undefined__ = (foo)*1 }; }
+ #line 2 "not-declared"
+ void __cgo_f_xxx_1(void) { __typeof__(bar) *__cgo_undefined__; }
+ #line 2 "not-type"
+ void __cgo_f_xxx_2(void) { bar *__cgo_undefined__; }
+ #line 2 "not-const"
+ void __cgo_f_xxx_3(void) { enum { __cgo_undefined__ = (bar)*1 }; }
+
+This program will not compile, but cgo can use the presence or absence
+of an error message on a given line to deduce the information it
+needs. The program is syntactically valid regardless of whether each
+name is a type or an ordinary identifier, so there will be no syntax
+errors that might stop parsing early.
+
+An error on not-declared:1 indicates that foo is undeclared.
+An error on not-type:1 indicates that foo is not a type (if declared at all, it is an identifier).
+An error on not-const:1 indicates that foo is not an integer constant.
+
+The line number specifies the name involved. In the example, 1 is foo and 2 is bar.
Next, cgo must learn the details of each type, variable, function, or
constant. It can do this by reading object files. If cgo has decided
@@ -213,14 +300,14 @@ that t1 is a type, v2 and v3 are variables or functions, and c4, c5,
and c6 are constants, it generates:
<preamble>
- typeof(t1) *__cgo__1;
- typeof(v2) *__cgo__2;
- typeof(v3) *__cgo__3;
- typeof(c4) *__cgo__4;
+ __typeof__(t1) *__cgo__1;
+ __typeof__(v2) *__cgo__2;
+ __typeof__(v3) *__cgo__3;
+ __typeof__(c4) *__cgo__4;
enum { __cgo_enum__4 = c4 };
- typeof(c5) *__cgo__5;
+ __typeof__(c5) *__cgo__5;
enum { __cgo_enum__5 = c5 };
- typeof(c6) *__cgo__6;
+ __typeof__(c6) *__cgo__6;
enum { __cgo_enum__6 = c6 };
long long __cgo_debug_data[] = {
@@ -310,7 +397,7 @@ file compiled by gcc, the file x.cgo2.c:
char* p0;
int r;
char __pad12[4];
- } __attribute__((__packed__)) *a = v;
+ } __attribute__((__packed__, __gcc_struct__)) *a = v;
a->r = puts((void*)a->p0);
}
@@ -396,8 +483,6 @@ and libcgo_thread_start to a gcc-compiled function that can be used to
create a new thread, in place of the runtime's usual direct system
calls.
-[NOTE: From here down is planned but not yet implemented.]
-
Internal and External Linking
The text above describes "internal" linking, in which 6l parses and
@@ -615,15 +700,18 @@ the godoc binary, which uses net but no other cgo, can run without
needing gcc available. The second rule means that a build of a
cgo-wrapped library like sqlite3 can generate a standalone executable
instead of needing to refer to a dynamic library. The specific choice
-can be overridden using a command line flag: 6l -cgolink=internal or
-6l -cgolink=external.
+can be overridden using a command line flag: 6l -linkmode=internal or
+6l -linkmode=external.
In an external link, 6l will create a temporary directory, write any
host object files found in package archives to that directory (renamed
to avoid conflicts), write the go.o file to that directory, and invoke
the host linker. The default value for the host linker is $CC, split
into fields, or else "gcc". The specific host linker command line can
-be overridden using a command line flag: 6l -hostld='gcc -ggdb'
+be overridden using command line flags: 6l -extld=clang
+-extldflags='-ggdb -O3'. If any package in a build includes a .cc or
+other file compiled by the C++ compiler, the go tool will use the
+-extld option to set the host linker to the C++ compiler.
These defaults mean that Go-aware build systems can ignore the linking
changes and keep running plain '6l' and get reasonable results, but
diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go
index bc7a6472f..3e1837ebf 100644
--- a/src/cmd/cgo/gcc.go
+++ b/src/cmd/cgo/gcc.go
@@ -76,6 +76,8 @@ func (f *File) DiscardCgoDirectives() {
l := strings.TrimSpace(line)
if len(l) < 5 || l[:4] != "#cgo" || !unicode.IsSpace(rune(l[4])) {
linesOut = append(linesOut, line)
+ } else {
+ linesOut = append(linesOut, "")
}
}
f.Preamble = strings.Join(linesOut, "\n")
@@ -186,8 +188,8 @@ func (p *Package) Translate(f *File) {
// in the file f and saves relevant renamings in f.Name[name].Define.
func (p *Package) loadDefines(f *File) {
var b bytes.Buffer
- b.WriteString(builtinProlog)
b.WriteString(f.Preamble)
+ b.WriteString(builtinProlog)
stdout := p.gccDefines(b.Bytes())
for _, line := range strings.Split(stdout, "\n") {
@@ -224,42 +226,22 @@ func (p *Package) loadDefines(f *File) {
// name xxx for the references C.xxx in the Go input.
// The kind is either a constant, type, or variable.
func (p *Package) guessKinds(f *File) []*Name {
- // Coerce gcc into telling us whether each name is
- // a type, a value, or undeclared. We compile a function
- // containing the line:
- // name;
- // If name is a type, gcc will print:
- // cgo-test:2: warning: useless type name in empty declaration
- // If name is a value, gcc will print
- // cgo-test:2: warning: statement with no effect
- // If name is undeclared, gcc will print
- // cgo-test:2: error: 'name' undeclared (first use in this function)
- // A line number directive causes the line number to
- // correspond to the index in the names array.
- //
- // The line also has an enum declaration:
- // name; enum { _cgo_enum_1 = name };
- // If name is not a constant, gcc will print:
- // cgo-test:4: error: enumerator value for '_cgo_enum_4' is not an integer constant
- // we assume lines without that error are constants.
-
- // Make list of names that need sniffing, type lookup.
- toSniff := make([]*Name, 0, len(f.Name))
- needType := make([]*Name, 0, len(f.Name))
-
+ // Determine kinds for names we already know about,
+ // like #defines or 'struct foo', before bothering with gcc.
+ var names, needType []*Name
for _, n := range f.Name {
// If we've already found this name as a #define
// and we can translate it as a constant value, do so.
if n.Define != "" {
- ok := false
+ isConst := false
if _, err := strconv.Atoi(n.Define); err == nil {
- ok = true
+ isConst = true
} else if n.Define[0] == '"' || n.Define[0] == '\'' {
if _, err := parser.ParseExpr(n.Define); err == nil {
- ok = true
+ isConst = true
}
}
- if ok {
+ if isConst {
n.Kind = "const"
// Turn decimal into hex, just for consistency
// with enum-derived constants. Otherwise
@@ -279,113 +261,139 @@ func (p *Package) guessKinds(f *File) []*Name {
}
}
- // If this is a struct, union, or enum type name,
- // record the kind but also that we need type information.
+ needType = append(needType, n)
+
+ // If this is a struct, union, or enum type name, no need to guess the kind.
if strings.HasPrefix(n.C, "struct ") || strings.HasPrefix(n.C, "union ") || strings.HasPrefix(n.C, "enum ") {
n.Kind = "type"
- i := len(needType)
- needType = needType[0 : i+1]
- needType[i] = n
continue
}
- i := len(toSniff)
- toSniff = toSniff[0 : i+1]
- toSniff[i] = n
+ // Otherwise, we'll need to find out from gcc.
+ names = append(names, n)
}
- if len(toSniff) == 0 {
+ // Bypass gcc if there's nothing left to find out.
+ if len(names) == 0 {
return needType
}
+ // Coerce gcc into telling us whether each name is a type, a value, or undeclared.
+ // For names, find out whether they are integer constants.
+ // We used to look at specific warning or error messages here, but that tied the
+ // behavior too closely to specific versions of the compilers.
+ // Instead, arrange that we can infer what we need from only the presence or absence
+ // of an error on a specific line.
+ //
+ // For each name, we generate these lines, where xxx is the index in toSniff plus one.
+ //
+ // #line xxx "not-declared"
+ // void __cgo_f_xxx_1(void) { __typeof__(name) *__cgo_undefined__; }
+ // #line xxx "not-type"
+ // void __cgo_f_xxx_2(void) { name *__cgo_undefined__; }
+ // #line xxx "not-const"
+ // void __cgo_f_xxx_3(void) { enum { __cgo_undefined__ = (name)*1 }; }
+ //
+ // If we see an error at not-declared:xxx, the corresponding name is not declared.
+ // If we see an error at not-type:xxx, the corresponding name is a type.
+ // If we see an error at not-const:xxx, the corresponding name is not an integer constant.
+ // If we see no errors, we assume the name is an expression but not a constant
+ // (so a variable or a function).
+ //
+ // The specific input forms are chosen so that they are valid C syntax regardless of
+ // whether name denotes a type or an expression.
+
var b bytes.Buffer
- b.WriteString(builtinProlog)
b.WriteString(f.Preamble)
- b.WriteString("void __cgo__f__(void) {\n")
- b.WriteString("#line 1 \"cgo-test\"\n")
- for i, n := range toSniff {
- fmt.Fprintf(&b, "%s; /* #%d */\nenum { _cgo_enum_%d = %s }; /* #%d */\n", n.C, i, i, n.C, i)
- }
- b.WriteString("}\n")
- stderr := p.gccErrors(b.Bytes())
- if stderr == "" {
- fatalf("gcc produced no output\non input:\n%s", b.Bytes())
- }
+ b.WriteString(builtinProlog)
- names := make([]*Name, len(toSniff))
- copy(names, toSniff)
+ for i, n := range names {
+ fmt.Fprintf(&b, "#line %d \"not-declared\"\n"+
+ "void __cgo_f_%d_1(void) { __typeof__(%s) *__cgo_undefined__; }\n"+
+ "#line %d \"not-type\"\n"+
+ "void __cgo_f_%d_2(void) { %s *__cgo_undefined__; }\n"+
+ "#line %d \"not-const\"\n"+
+ "void __cgo_f_%d_3(void) { enum { __cgo__undefined__ = (%s)*1 }; }\n",
+ i+1, i+1, n.C,
+ i+1, i+1, n.C,
+ i+1, i+1, n.C)
+ }
+ fmt.Fprintf(&b, "#line 1 \"completed\"\n"+
+ "int __cgo__1 = __cgo__2;\n")
- isConst := make([]bool, len(toSniff))
- for i := range isConst {
- isConst[i] = true // until proven otherwise
+ stderr := p.gccErrors(b.Bytes())
+ if stderr == "" {
+ fatalf("%s produced no output\non input:\n%s", p.gccBaseCmd()[0], b.Bytes())
}
+ completed := false
+ sniff := make([]int, len(names))
+ const (
+ notType = 1 << iota
+ notConst
+ )
for _, line := range strings.Split(stderr, "\n") {
- if len(line) < 9 || line[0:9] != "cgo-test:" {
- // the user will see any compiler errors when the code is compiled later.
+ if !strings.Contains(line, ": error:") {
+ // we only care about errors.
+ // we tried to turn off warnings on the command line, but one never knows.
continue
}
- line = line[9:]
- colon := strings.Index(line, ":")
- if colon < 0 {
- continue
- }
- i, err := strconv.Atoi(line[0:colon])
- if err != nil {
+
+ c1 := strings.Index(line, ":")
+ if c1 < 0 {
continue
}
- i = (i - 1) / 2
- what := ""
- switch {
- default:
- continue
- case strings.Contains(line, ": useless type name in empty declaration"),
- strings.Contains(line, ": declaration does not declare anything"),
- strings.Contains(line, ": unexpected type name"):
- what = "type"
- isConst[i] = false
- case strings.Contains(line, ": statement with no effect"),
- strings.Contains(line, ": expression result unused"):
- what = "not-type" // const or func or var
- case strings.Contains(line, "undeclared"):
- error_(token.NoPos, "%s", strings.TrimSpace(line[colon+1:]))
- case strings.Contains(line, "is not an integer constant"):
- isConst[i] = false
+ c2 := strings.Index(line[c1+1:], ":")
+ if c2 < 0 {
continue
}
- n := toSniff[i]
- if n == nil {
+ c2 += c1 + 1
+
+ filename := line[:c1]
+ i, _ := strconv.Atoi(line[c1+1 : c2])
+ i--
+ if i < 0 || i >= len(names) {
continue
}
- toSniff[i] = nil
- n.Kind = what
- j := len(needType)
- needType = needType[0 : j+1]
- needType[j] = n
- }
- for i, b := range isConst {
- if b {
- names[i].Kind = "const"
- if toSniff[i] != nil && names[i].Const == "" {
- j := len(needType)
- needType = needType[0 : j+1]
- needType[j] = names[i]
- }
+ switch filename {
+ case "completed":
+ // Strictly speaking, there is no guarantee that seeing the error at completed:1
+ // (at the end of the file) means we've seen all the errors from earlier in the file,
+ // but usually it does. Certainly if we don't see the completed:1 error, we did
+ // not get all the errors we expected.
+ completed = true
+
+ case "not-declared":
+ error_(token.NoPos, "%s", strings.TrimSpace(line[c2+1:]))
+ case "not-type":
+ sniff[i] |= notType
+ case "not-const":
+ sniff[i] |= notConst
}
}
- for _, n := range toSniff {
- if n == nil {
- continue
- }
- if n.Kind != "" {
- continue
+
+ if !completed {
+ fatalf("%s did not produce error at completed:1\non input:\n%s", p.gccBaseCmd()[0], b.Bytes())
+ }
+
+ for i, n := range names {
+ switch sniff[i] {
+ case 0:
+ error_(token.NoPos, "could not determine kind of name for C.%s", fixGo(n.Go))
+ case notType:
+ n.Kind = "const"
+ case notConst:
+ n.Kind = "type"
+ case notConst | notType:
+ n.Kind = "not-type"
}
- error_(token.NoPos, "could not determine kind of name for C.%s", n.Go)
}
if nerrors > 0 {
fatalf("unresolved names")
}
+
+ needType = append(needType, names...)
return needType
}
@@ -398,14 +406,14 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
// for symbols in the object file, so it is not enough to print the
// preamble and hope the symbols we care about will be there.
// Instead, emit
- // typeof(names[i]) *__cgo__i;
+ // __typeof__(names[i]) *__cgo__i;
// for each entry in names and then dereference the type we
// learn for __cgo__i.
var b bytes.Buffer
- b.WriteString(builtinProlog)
b.WriteString(f.Preamble)
+ b.WriteString(builtinProlog)
for i, n := range names {
- fmt.Fprintf(&b, "typeof(%s) *__cgo__%d;\n", n.C, i)
+ fmt.Fprintf(&b, "__typeof__(%s) *__cgo__%d;\n", n.C, i)
if n.Kind == "const" {
fmt.Fprintf(&b, "enum { __cgo_enum__%d = %s };\n", i, n.C)
}
@@ -548,25 +556,40 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
}
+// mangleName does name mangling to translate names
+// from the original Go source files to the names
+// used in the final Go files generated by cgo.
+func (p *Package) mangleName(n *Name) {
+ // When using gccgo variables have to be
+ // exported so that they become global symbols
+ // that the C code can refer to.
+ prefix := "_C"
+ if *gccgo && n.IsVar() {
+ prefix = "C"
+ }
+ n.Mangle = prefix + n.Kind + "_" + n.Go
+}
+
// rewriteRef rewrites all the C.xxx references in f.AST to refer to the
// Go equivalents, now that we have figured out the meaning of all
// the xxx. In *godefs or *cdefs mode, rewriteRef replaces the names
// with full definitions instead of mangled names.
func (p *Package) rewriteRef(f *File) {
+ // Keep a list of all the functions, to remove the ones
+ // only used as expressions and avoid generating bridge
+ // code for them.
+ functions := make(map[string]bool)
+
// Assign mangled names.
for _, n := range f.Name {
if n.Kind == "not-type" {
n.Kind = "var"
}
if n.Mangle == "" {
- // When using gccgo variables have to be
- // exported so that they become global symbols
- // that the C code can refer to.
- prefix := "_C"
- if *gccgo && n.Kind == "var" {
- prefix = "C"
- }
- n.Mangle = prefix + n.Kind + "_" + n.Go
+ p.mangleName(n)
+ }
+ if n.Kind == "func" {
+ functions[n.Go] = false
}
}
@@ -576,7 +599,7 @@ func (p *Package) rewriteRef(f *File) {
// functions are only used in calls.
for _, r := range f.Ref {
if r.Name.Kind == "const" && r.Name.Const == "" {
- error_(r.Pos(), "unable to find value of constant C.%s", r.Name.Go)
+ error_(r.Pos(), "unable to find value of constant C.%s", fixGo(r.Name.Go))
}
var expr ast.Expr = ast.NewIdent(r.Name.Mangle) // default
switch r.Context {
@@ -587,10 +610,15 @@ func (p *Package) rewriteRef(f *File) {
expr = r.Name.Type.Go
break
}
- error_(r.Pos(), "call of non-function C.%s", r.Name.Go)
+ error_(r.Pos(), "call of non-function C.%s", fixGo(r.Name.Go))
break
}
+ functions[r.Name.Go] = true
if r.Context == "call2" {
+ if r.Name.Go == "_CMalloc" {
+ error_(r.Pos(), "no two-result form for C.malloc")
+ break
+ }
// Invent new Name for the two-result function.
n := f.Name["2"+r.Name.Go]
if n == nil {
@@ -606,29 +634,42 @@ func (p *Package) rewriteRef(f *File) {
}
case "expr":
if r.Name.Kind == "func" {
- error_(r.Pos(), "must call C.%s", r.Name.Go)
- }
- if r.Name.Kind == "type" {
+ // Function is being used in an expression, to e.g. pass around a C function pointer.
+ // Create a new Name for this Ref which causes the variable to be declared in Go land.
+ fpName := "fp_" + r.Name.Go
+ name := f.Name[fpName]
+ if name == nil {
+ name = &Name{
+ Go: fpName,
+ C: r.Name.C,
+ Kind: "fpvar",
+ Type: &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("void*"), Go: ast.NewIdent("unsafe.Pointer")},
+ }
+ p.mangleName(name)
+ f.Name[fpName] = name
+ }
+ r.Name = name
+ expr = ast.NewIdent(name.Mangle)
+ } else if r.Name.Kind == "type" {
// Okay - might be new(T)
expr = r.Name.Type.Go
- }
- if r.Name.Kind == "var" {
- expr = &ast.StarExpr{X: expr}
+ } else if r.Name.Kind == "var" {
+ expr = &ast.StarExpr{Star: (*r.Expr).Pos(), X: expr}
}
case "type":
if r.Name.Kind != "type" {
- error_(r.Pos(), "expression C.%s used as type", r.Name.Go)
+ error_(r.Pos(), "expression C.%s used as type", fixGo(r.Name.Go))
} else if r.Name.Type == nil {
// Use of C.enum_x, C.struct_x or C.union_x without C definition.
// GCC won't raise an error when using pointers to such unknown types.
- error_(r.Pos(), "type C.%s: undefined C type '%s'", r.Name.Go, r.Name.C)
+ error_(r.Pos(), "type C.%s: undefined C type '%s'", fixGo(r.Name.Go), r.Name.C)
} else {
expr = r.Name.Type.Go
}
default:
if r.Name.Kind == "func" {
- error_(r.Pos(), "must call C.%s", r.Name.Go)
+ error_(r.Pos(), "must call C.%s", fixGo(r.Name.Go))
}
}
if *godefs || *cdefs {
@@ -642,23 +683,42 @@ func (p *Package) rewriteRef(f *File) {
}
}
}
+
+ // Copy position information from old expr into new expr,
+ // in case expression being replaced is first on line.
+ // See golang.org/issue/6563.
+ pos := (*r.Expr).Pos()
+ switch x := expr.(type) {
+ case *ast.Ident:
+ expr = &ast.Ident{NamePos: pos, Name: x.Name}
+ }
+
*r.Expr = expr
}
-}
-// gccName returns the name of the compiler to run. Use $CC if set in
-// the environment, otherwise just "gcc".
+ // Remove functions only used as expressions, so their respective
+ // bridge functions are not generated.
+ for name, used := range functions {
+ if !used {
+ delete(f.Name, name)
+ }
+ }
+}
-func (p *Package) gccName() string {
+// gccBaseCmd returns the start of the compiler command line.
+// It uses $CC if set, or else $GCC, or else the compiler recorded
+// during the initial build as defaultCC.
+// defaultCC is defined in zdefaultcc.go, written by cmd/dist.
+func (p *Package) gccBaseCmd() []string {
// Use $CC if set, since that's what the build uses.
- if ret := os.Getenv("CC"); ret != "" {
+ if ret := strings.Fields(os.Getenv("CC")); len(ret) > 0 {
return ret
}
- // Fall back to $GCC if set, since that's what we used to use.
- if ret := os.Getenv("GCC"); ret != "" {
+ // Try $GCC if set, since that's what we used to use.
+ if ret := strings.Fields(os.Getenv("GCC")); len(ret) > 0 {
return ret
}
- return "gcc"
+ return strings.Fields(defaultCC)
}
// gccMachine returns the gcc -m flag to use, either "-m32", "-m64" or "-marm".
@@ -681,17 +741,15 @@ func gccTmp() string {
// gccCmd returns the gcc command line to use for compiling
// the input.
func (p *Package) gccCmd() []string {
- c := []string{
- p.gccName(),
- "-Wall", // many warnings
- "-Werror", // warnings are errors
- "-o" + gccTmp(), // write object to tmp
- "-gdwarf-2", // generate DWARF v2 debugging symbols
- "-fno-eliminate-unused-debug-types", // gets rid of e.g. untyped enum otherwise
- "-c", // do not link
- "-xc", // input language is C
- }
- if strings.Contains(p.gccName(), "clang") {
+ c := append(p.gccBaseCmd(),
+ "-w", // no warnings
+ "-Wno-error", // warnings are not errors
+ "-o"+gccTmp(), // write object to tmp
+ "-gdwarf-2", // generate DWARF v2 debugging symbols
+ "-c", // do not link
+ "-xc", // input language is C
+ )
+ if strings.Contains(c[0], "clang") {
c = append(c,
"-ferror-limit=0",
// Apple clang version 1.7 (tags/Apple/clang-77) (based on LLVM 2.9svn)
@@ -701,6 +759,13 @@ func (p *Package) gccCmd() []string {
"-Wno-unneeded-internal-declaration",
"-Wno-unused-function",
"-Qunused-arguments",
+ // Clang embeds prototypes for some builtin functions,
+ // like malloc and calloc, but all size_t parameters are
+ // incorrectly typed unsigned long. We work around that
+ // by disabling the builtin functions (this is safe as
+ // it won't affect the actual compilation of the C code).
+ // See: http://golang.org/issue/6506.
+ "-fno-builtin",
)
}
@@ -715,7 +780,13 @@ func (p *Package) gccCmd() []string {
func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte) {
runGcc(stdin, p.gccCmd())
+ isDebugData := func(s string) bool {
+ // Some systems use leading _ to denote non-assembly symbols.
+ return s == "__cgodebug_data" || s == "___cgodebug_data"
+ }
+
if f, err := macho.Open(gccTmp()); err == nil {
+ defer f.Close()
d, err := f.DWARF()
if err != nil {
fatalf("cannot load DWARF output from %s: %v", gccTmp(), err)
@@ -724,8 +795,7 @@ func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte)
if f.Symtab != nil {
for i := range f.Symtab.Syms {
s := &f.Symtab.Syms[i]
- // Mach-O still uses a leading _ to denote non-assembly symbols.
- if s.Name == "_"+"__cgodebug_data" {
+ if isDebugData(s.Name) {
// Found it. Now find data section.
if i := int(s.Sect) - 1; 0 <= i && i < len(f.Sections) {
sect := f.Sections[i]
@@ -742,6 +812,7 @@ func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte)
}
if f, err := elf.Open(gccTmp()); err == nil {
+ defer f.Close()
d, err := f.DWARF()
if err != nil {
fatalf("cannot load DWARF output from %s: %v", gccTmp(), err)
@@ -751,7 +822,7 @@ func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte)
if err == nil {
for i := range symtab {
s := &symtab[i]
- if s.Name == "__cgodebug_data" {
+ if isDebugData(s.Name) {
// Found it. Now find data section.
if i := int(s.Section); 0 <= i && i < len(f.Sections) {
sect := f.Sections[i]
@@ -768,13 +839,14 @@ func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte)
}
if f, err := pe.Open(gccTmp()); err == nil {
+ defer f.Close()
d, err := f.DWARF()
if err != nil {
fatalf("cannot load DWARF output from %s: %v", gccTmp(), err)
}
var data []byte
for _, s := range f.Symbols {
- if s.Name == "_"+"__cgodebug_data" {
+ if isDebugData(s.Name) {
if i := int(s.SectionNumber) - 1; 0 <= i && i < len(f.Sections) {
sect := f.Sections[i]
if s.Value < sect.Size {
@@ -797,7 +869,7 @@ func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte)
// #defines that gcc encountered while processing the input
// and its included files.
func (p *Package) gccDefines(stdin []byte) string {
- base := []string{p.gccName(), "-E", "-dM", "-xc"}
+ base := append(p.gccBaseCmd(), "-E", "-dM", "-xc")
base = append(base, p.gccMachine()...)
stdout, _ := runGcc(stdin, append(append(base, p.GccOptions...), "-"))
return stdout
@@ -810,14 +882,6 @@ func (p *Package) gccErrors(stdin []byte) string {
// TODO(rsc): require failure
args := p.gccCmd()
- // GCC 4.8.0 has a bug: it sometimes does not apply
- // -Wunused-value to values that are macros defined in system
- // headers. See issue 5118. Adding -Wsystem-headers avoids
- // that problem. This will produce additional errors, but it
- // doesn't matter because we will ignore all errors that are
- // not marked for the cgo-test file.
- args = append(args, "-Wsystem-headers")
-
if *debugGcc {
fmt.Fprintf(os.Stderr, "$ %s <<EOF\n", strings.Join(args, " "))
os.Stderr.Write(stdin)
@@ -982,21 +1046,11 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
}
t := new(Type)
- t.Size = dtype.Size()
+ t.Size = dtype.Size() // note: wrong for array of pointers, corrected below
t.Align = -1
t.C = &TypeRepr{Repr: dtype.Common().Name}
c.m[dtype] = t
- if t.Size < 0 {
- // Unsized types are [0]byte
- t.Size = 0
- t.Go = c.Opaque(0)
- if t.C.Empty() {
- t.C.Set("void")
- }
- return t
- }
-
switch dt := dtype.(type) {
default:
fatalf("%s: unexpected type: %s", lineno(pos), dtype)
@@ -1021,7 +1075,7 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
sub := c.Type(dt.Type, pos)
t.Align = sub.Align
gt.Elt = sub.Go
- t.C.Set("typeof(%s[%d])", sub.C, dt.Count)
+ t.C.Set("__typeof__(%s[%d])", sub.C, dt.Count)
case *dwarf.BoolType:
t.Go = c.bool
@@ -1143,6 +1197,9 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
return t
case *dwarf.StructType:
+ if dt.ByteSize < 0 { // opaque struct
+ break
+ }
// Convert to Go struct, being careful about alignment.
// Have to give it a name to simulate C "struct foo" references.
tag := dt.StructName
@@ -1159,7 +1216,7 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
case "class", "union":
t.Go = c.Opaque(t.Size)
if t.C.Empty() {
- t.C.Set("typeof(unsigned char[%d])", t.Size)
+ t.C.Set("__typeof__(unsigned char[%d])", t.Size)
}
t.Align = 1 // TODO: should probably base this on field alignment.
typedef[name.Name] = t
@@ -1261,6 +1318,25 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
}
}
+ if t.Size <= 0 {
+ // Clang does not record the size of a pointer in its DWARF entry,
+ // so if dtype is an array, the call to dtype.Size at the top of the function
+ // computed the size as the array length * 0 = 0.
+ // The type switch called Type (this function) recursively on the pointer
+ // entry, and the code near the top of the function updated the size to
+ // be correct, so calling dtype.Size again will produce the correct value.
+ t.Size = dtype.Size()
+ if t.Size < 0 {
+ // Unsized types are [0]byte
+ t.Size = 0
+ t.Go = c.Opaque(0)
+ if t.C.Empty() {
+ t.C.Set("void")
+ }
+ return t
+ }
+ }
+
if t.C.Empty() {
fatalf("%s: internal error: did not create C name for %s", lineno(pos), dtype)
}
diff --git a/src/cmd/cgo/godefs.go b/src/cmd/cgo/godefs.go
index 20376170d..ce5ac2736 100644
--- a/src/cmd/cgo/godefs.go
+++ b/src/cmd/cgo/godefs.go
@@ -204,6 +204,11 @@ func (p *Package) cdefs(f *File, srcfile string) string {
// byte Z[4];
// }
if strings.HasPrefix(line, "type ") && strings.HasSuffix(line, " struct {") {
+ if len(lines) > i+1 && lines[i+1] == "}" {
+ // do not output empty struct
+ i++
+ continue
+ }
s := line[len("type ") : len(line)-len(" struct {")]
printf("struct %s {\n", s)
for i++; i < len(lines) && lines[i] != "}"; i++ {
@@ -256,17 +261,17 @@ func cdecl(name, typ string) string {
if strings.HasPrefix(typ, "*[0]") {
typ = "*void"
}
- // X *byte -> *X byte
- if strings.HasPrefix(typ, "*") {
- name = "*" + name
- typ = typ[1:]
- }
// X [4]byte -> X[4] byte
- if strings.HasPrefix(typ, "[") {
+ for strings.HasPrefix(typ, "[") {
i := strings.Index(typ, "]") + 1
name = name + typ[:i]
typ = typ[i:]
}
+ // X *byte -> *X byte
+ for strings.HasPrefix(typ, "*") {
+ name = "*" + name
+ typ = typ[1:]
+ }
// X T -> T X
// Handle the special case: 'unsafe.Pointer' is 'void *'
if typ == "unsafe.Pointer" {
diff --git a/src/cmd/cgo/main.go b/src/cmd/cgo/main.go
index 9bd326e1d..17b0cdd16 100644
--- a/src/cmd/cgo/main.go
+++ b/src/cmd/cgo/main.go
@@ -80,13 +80,18 @@ type Name struct {
Mangle string // name used in generated Go
C string // name used in C
Define string // #define expansion
- Kind string // "const", "type", "var", "func", "not-type"
+ Kind string // "const", "type", "var", "fpvar", "func", "not-type"
Type *Type // the type of xxx
FuncType *FuncType
AddError bool
Const string // constant definition
}
+// IsVar returns true if Kind is either "var" or "fpvar"
+func (n *Name) IsVar() bool {
+ return n.Kind == "var" || n.Kind == "fpvar"
+}
+
// A ExpFunc is an exported function, callable from C.
// Such functions are identified in the Go input file
// by doc comments containing the line //export ExpName
@@ -336,7 +341,7 @@ func (p *Package) Record(f *File) {
if p.Name[k] == nil {
p.Name[k] = v
} else if !reflect.DeepEqual(p.Name[k], v) {
- error_(token.NoPos, "inconsistent definitions for C.%s", k)
+ error_(token.NoPos, "inconsistent definitions for C.%s", fixGo(k))
}
}
}
diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go
index 7fb818168..83ab95251 100644
--- a/src/cmd/cgo/out.go
+++ b/src/cmd/cgo/out.go
@@ -32,7 +32,7 @@ func (p *Package) writeDefs() {
fflg := creat(*objDir + "_cgo_flags")
for k, v := range p.CgoFlags {
fmt.Fprintf(fflg, "_CGO_%s=%s\n", k, strings.Join(v, " "))
- if k == "LDFLAGS" {
+ if k == "LDFLAGS" && !*gccgo {
for _, arg := range v {
fmt.Fprintf(fc, "#pragma cgo_ldflag %q\n", arg)
}
@@ -47,7 +47,7 @@ func (p *Package) writeDefs() {
} else {
// If we're not importing runtime/cgo, we *are* runtime/cgo,
// which provides crosscall2. We just need a prototype.
- fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*, int), void *a, int c);")
+ fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*, int), void *a, int c);\n")
}
fmt.Fprintf(fm, "void _cgo_allocate(void *a, int c) { }\n")
fmt.Fprintf(fm, "void _cgo_panic(void *a, int c) { }\n")
@@ -87,7 +87,7 @@ func (p *Package) writeDefs() {
}
if *gccgo {
- fmt.Fprintf(fc, cPrologGccgo)
+ fmt.Fprintf(fc, p.cPrologGccgo())
} else {
fmt.Fprintf(fc, cProlog)
}
@@ -97,7 +97,7 @@ func (p *Package) writeDefs() {
cVars := make(map[string]bool)
for _, key := range nameKeys(p.Name) {
n := p.Name[key]
- if n.Kind != "var" {
+ if !n.IsVar() {
continue
}
@@ -105,22 +105,37 @@ func (p *Package) writeDefs() {
fmt.Fprintf(fm, "extern char %s[];\n", n.C)
fmt.Fprintf(fm, "void *_cgohack_%s = %s;\n\n", n.C, n.C)
- fmt.Fprintf(fc, "#pragma cgo_import_static %s\n", n.C)
+ if !*gccgo {
+ fmt.Fprintf(fc, "#pragma cgo_import_static %s\n", n.C)
+ }
+
fmt.Fprintf(fc, "extern byte *%s;\n", n.C)
cVars[n.C] = true
}
-
+ var amp string
+ var node ast.Node
+ if n.Kind == "var" {
+ amp = "&"
+ node = &ast.StarExpr{X: n.Type.Go}
+ } else if n.Kind == "fpvar" {
+ node = n.Type.Go
+ if *gccgo {
+ amp = "&"
+ }
+ } else {
+ panic(fmt.Errorf("invalid var kind %q", n.Kind))
+ }
if *gccgo {
fmt.Fprintf(fc, `extern void *%s __asm__("%s.%s");`, n.Mangle, gccgoSymbolPrefix, n.Mangle)
- fmt.Fprintf(&gccgoInit, "\t%s = &%s;\n", n.Mangle, n.C)
+ fmt.Fprintf(&gccgoInit, "\t%s = %s%s;\n", n.Mangle, amp, n.C)
} else {
- fmt.Fprintf(fc, "void *·%s = &%s;\n", n.Mangle, n.C)
+ fmt.Fprintf(fc, "void *·%s = %s%s;\n", n.Mangle, amp, n.C)
}
fmt.Fprintf(fc, "\n")
fmt.Fprintf(fgo2, "var %s ", n.Mangle)
- conf.Fprint(fgo2, fset, &ast.StarExpr{X: n.Type.Go})
+ conf.Fprint(fgo2, fset, node)
fmt.Fprintf(fgo2, "\n")
}
fmt.Fprintf(fc, "\n")
@@ -282,7 +297,7 @@ func (p *Package) structType(n *Name) (string, int64) {
off += pad
}
if n.AddError {
- fmt.Fprint(&buf, "\t\tvoid *e[2]; /* error */\n")
+ fmt.Fprint(&buf, "\t\tint e[2*sizeof(void *)/sizeof(int)]; /* error */\n")
off += 2 * p.PtrSize
}
if off == 0 {
@@ -319,7 +334,7 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) {
}
// Builtins defined in the C prolog.
- inProlog := name == "CString" || name == "GoString" || name == "GoStringN" || name == "GoBytes"
+ inProlog := name == "CString" || name == "GoString" || name == "GoStringN" || name == "GoBytes" || name == "_CMalloc"
if *gccgo {
// Gccgo style hooks.
@@ -368,11 +383,7 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) {
fmt.Fprint(fgo2, "}\n")
// declare the C function.
- if inProlog {
- fmt.Fprintf(fgo2, "//extern %s\n", n.C)
- } else {
- fmt.Fprintf(fgo2, "//extern _cgo%s%s\n", cPrefix, n.Mangle)
- }
+ fmt.Fprintf(fgo2, "//extern _cgo%s%s\n", cPrefix, n.Mangle)
d.Name = ast.NewIdent(cname)
if n.AddError {
l := d.Type.Results.List
@@ -401,7 +412,17 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) {
if argSize == 0 {
argSize++
}
- fmt.Fprintf(fc, "·%s(struct{uint8 x[%d];}p)\n", n.Mangle, argSize)
+ // TODO(rsc): The struct here should declare pointers only where
+ // there are pointers in the actual argument frame.
+ // This is a workaround for golang.org/issue/6397.
+ fmt.Fprintf(fc, "·%s(struct{", n.Mangle)
+ if n := argSize / p.PtrSize; n > 0 {
+ fmt.Fprintf(fc, "void *y[%d];", n)
+ }
+ if n := argSize % p.PtrSize; n > 0 {
+ fmt.Fprintf(fc, "uint8 x[%d];", n)
+ }
+ fmt.Fprintf(fc, "}p)\n")
fmt.Fprintf(fc, "{\n")
fmt.Fprintf(fc, "\truntime·cgocall(_cgo%s%s, &p);\n", cPrefix, n.Mangle)
if n.AddError {
@@ -464,9 +485,27 @@ func (p *Package) writeOutput(f *File, srcfile string) {
fgcc.Close()
}
+// fixGo convers the internal Name.Go field into the name we should show
+// to users in error messages. There's only one for now: on input we rewrite
+// C.malloc into C._CMalloc, so change it back here.
+func fixGo(name string) string {
+ if name == "_CMalloc" {
+ return "malloc"
+ }
+ return name
+}
+
+var isBuiltin = map[string]bool{
+ "_Cfunc_CString": true,
+ "_Cfunc_GoString": true,
+ "_Cfunc_GoStringN": true,
+ "_Cfunc_GoBytes": true,
+ "_Cfunc__CMalloc": true,
+}
+
func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) {
name := n.Mangle
- if name == "_Cfunc_CString" || name == "_Cfunc_GoString" || name == "_Cfunc_GoStringN" || name == "_Cfunc_GoBytes" || p.Written[name] {
+ if isBuiltin[name] || p.Written[name] {
// The builtins are already defined in the C prolog, and we don't
// want to duplicate function definitions we've already done.
return
@@ -486,19 +525,24 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) {
fmt.Fprintf(fgcc, "_cgo%s%s(void *v)\n", cPrefix, n.Mangle)
fmt.Fprintf(fgcc, "{\n")
if n.AddError {
- fmt.Fprintf(fgcc, "\tint e;\n") // assuming 32 bit (see comment above structType)
fmt.Fprintf(fgcc, "\terrno = 0;\n")
}
// We're trying to write a gcc struct that matches 6c/8c/5c's layout.
// Use packed attribute to force no padding in this struct in case
// gcc has different packing requirements. For example,
// on 386 Windows, gcc wants to 8-align int64s, but 8c does not.
- fmt.Fprintf(fgcc, "\t%s __attribute__((__packed__)) *a = v;\n", ctype)
+ // Use __gcc_struct__ to work around http://gcc.gnu.org/PR52991 on x86,
+ // and http://golang.org/issue/5603.
+ extraAttr := ""
+ if !strings.Contains(p.gccBaseCmd()[0], "clang") && (goarch == "amd64" || goarch == "386") {
+ extraAttr = ", __gcc_struct__"
+ }
+ fmt.Fprintf(fgcc, "\t%s __attribute__((__packed__%v)) *a = v;\n", ctype, extraAttr)
fmt.Fprintf(fgcc, "\t")
if t := n.FuncType.Result; t != nil {
fmt.Fprintf(fgcc, "a->r = ")
if c := t.C.String(); c[len(c)-1] == '*' {
- fmt.Fprintf(fgcc, "(const %s) ", t.C)
+ fmt.Fprint(fgcc, "(__typeof__(a->r)) ")
}
}
fmt.Fprintf(fgcc, "%s(", n.C)
@@ -993,7 +1037,8 @@ func (p *Package) cgoType(e ast.Expr) *Type {
return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("%s*", x.C)}
case *ast.ArrayType:
if t.Len == nil {
- return &Type{Size: p.PtrSize + 8, Align: p.PtrSize, C: c("GoSlice")}
+ // Slice: pointer, len, cap.
+ return &Type{Size: p.PtrSize * 3, Align: p.PtrSize, C: c("GoSlice")}
}
case *ast.StructType:
// TODO
@@ -1030,8 +1075,7 @@ func (p *Package) cgoType(e ast.Expr) *Type {
return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("GoUintptr")}
}
if t.Name == "string" {
- // The string data is 1 pointer + 1 int, but this always
- // rounds to 2 pointers due to alignment.
+ // The string data is 1 pointer + 1 (pointer-sized) int.
return &Type{Size: 2 * p.PtrSize, Align: p.PtrSize, C: c("GoString")}
}
if t.Name == "error" {
@@ -1084,12 +1128,24 @@ __cgo_size_assert(double, 8)
`
const builtinProlog = `
-typedef struct { char *p; int n; } _GoString_;
-typedef struct { char *p; int n; int c; } _GoBytes_;
+#include <sys/types.h> /* for size_t below */
+
+/* Define intgo when compiling with GCC. */
+#ifdef __PTRDIFF_TYPE__
+typedef __PTRDIFF_TYPE__ intgo;
+#elif defined(_LP64)
+typedef long long intgo;
+#else
+typedef int intgo;
+#endif
+
+typedef struct { char *p; intgo n; } _GoString_;
+typedef struct { char *p; intgo n; intgo c; } _GoBytes_;
_GoString_ GoString(char *p);
_GoString_ GoStringN(char *p, int l);
_GoBytes_ GoBytes(void *p, int n);
char *CString(_GoString_);
+void *_CMalloc(size_t);
`
const cProlog = `
@@ -1127,10 +1183,22 @@ void
p[s.len] = 0;
FLUSH(&p);
}
+
+void
+·_Cfunc__CMalloc(uintptr n, int8 *p)
+{
+ p = runtime·cmalloc(n);
+ FLUSH(&p);
+}
`
+func (p *Package) cPrologGccgo() string {
+ return strings.Replace(cPrologGccgo, "PREFIX", cPrefix, -1)
+}
+
const cPrologGccgo = `
#include <stdint.h>
+#include <stdlib.h>
#include <string.h>
typedef unsigned char byte;
@@ -1150,23 +1218,33 @@ typedef struct __go_open_array {
struct __go_string __go_byte_array_to_string(const void* p, intgo len);
struct __go_open_array __go_string_to_byte_array (struct __go_string str);
-const char *CString(struct __go_string s) {
+const char *_cgoPREFIX_Cfunc_CString(struct __go_string s) {
return strndup((const char*)s.__data, s.__length);
}
-struct __go_string GoString(char *p) {
+struct __go_string _cgoPREFIX_Cfunc_GoString(char *p) {
intgo len = (p != NULL) ? strlen(p) : 0;
return __go_byte_array_to_string(p, len);
}
-struct __go_string GoStringN(char *p, intgo n) {
+struct __go_string _cgoPREFIX_Cfunc_GoStringN(char *p, int32_t n) {
return __go_byte_array_to_string(p, n);
}
-Slice GoBytes(char *p, intgo n) {
+Slice _cgoPREFIX_Cfunc_GoBytes(char *p, int32_t n) {
struct __go_string s = { (const unsigned char *)p, n };
return __go_string_to_byte_array(s);
}
+
+extern void runtime_throw(const char *);
+void *_cgoPREFIX_Cfunc__CMalloc(size_t n) {
+ void *p = malloc(n);
+ if(p == NULL && n == 0)
+ p = malloc(1);
+ if(p == NULL)
+ runtime_throw("runtime: C malloc failed");
+ return p;
+}
`
func (p *Package) gccExportHeaderProlog() string {
@@ -1190,9 +1268,9 @@ typedef double GoFloat64;
typedef __complex float GoComplex64;
typedef __complex double GoComplex128;
-typedef struct { char *p; int n; } GoString;
+typedef struct { char *p; GoInt n; } GoString;
typedef void *GoMap;
typedef void *GoChan;
typedef struct { void *t; void *v; } GoInterface;
-typedef struct { void *data; int len; int cap; } GoSlice;
+typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
`
diff --git a/src/cmd/dist/a.h b/src/cmd/dist/a.h
index 73c126476..9de93180f 100644
--- a/src/cmd/dist/a.h
+++ b/src/cmd/dist/a.h
@@ -74,10 +74,13 @@ extern char *goroot;
extern char *goroot_final;
extern char *goextlinkenabled;
extern char *goversion;
+extern char *defaultcc;
+extern char *defaultcxx;
extern char *workdir;
extern char *tooldir;
extern char *slash;
extern bool rebuildall;
+extern bool defaultclang;
int find(char*, char**, int);
void init(void);
@@ -94,16 +97,22 @@ void mkenam(char*, char*);
// buildruntime.c
void mkzasm(char*, char*);
+void mkzsys(char*, char*);
void mkzgoarch(char*, char*);
void mkzgoos(char*, char*);
void mkzruntimedefs(char*, char*);
void mkzversion(char*, char*);
+void mkzexperiment(char*, char*);
+
+// buildgo.c
+void mkzdefaultcc(char*, char*);
// goc2c.c
void goc2c(char*, char*);
// main.c
extern int vflag;
+extern int sflag;
void usage(void);
void xmain(int argc, char **argv);
diff --git a/src/cmd/dist/build.c b/src/cmd/dist/build.c
index 169e5cadd..e6e5f0cf7 100644
--- a/src/cmd/dist/build.c
+++ b/src/cmd/dist/build.c
@@ -26,8 +26,10 @@ char *tooldir;
char *gochar;
char *goversion;
char *slash; // / for unix, \ for windows
-
-bool rebuildall = 0;
+char *defaultcc;
+char *defaultcxx;
+bool rebuildall;
+bool defaultclang;
static bool shouldbuild(char*, char*);
static void copy(char*, char*, int);
@@ -47,6 +49,7 @@ static char *okgoarch[] = {
// The known operating systems.
static char *okgoos[] = {
"darwin",
+ "dragonfly",
"linux",
"freebsd",
"netbsd",
@@ -146,6 +149,29 @@ init(void)
if(!streq(goextlinkenabled, "0") && !streq(goextlinkenabled, "1"))
fatal("unknown $GO_EXTLINK_ENABLED %s", goextlinkenabled);
}
+
+ xgetenv(&b, "CC");
+ if(b.len == 0) {
+ // Use clang on OS X, because gcc is deprecated there.
+ // Xcode for OS X 10.9 Mavericks will ship a fake "gcc" binary that
+ // actually runs clang. We prepare different command
+ // lines for the two binaries, so it matters what we call it.
+ // See golang.org/issue/5822.
+ if(defaultclang)
+ bprintf(&b, "clang");
+ else
+ bprintf(&b, "gcc");
+ }
+ defaultcc = btake(&b);
+
+ xgetenv(&b, "CXX");
+ if(b.len == 0) {
+ if(defaultclang)
+ bprintf(&b, "clang++");
+ else
+ bprintf(&b, "g++");
+ }
+ defaultcxx = btake(&b);
xsetenv("GOROOT", goroot);
xsetenv("GOARCH", goarch);
@@ -318,7 +344,6 @@ static char *oldtool[] = {
// Unreleased directories (relative to $GOROOT) that should
// not be in release branches.
static char *unreleased[] = {
- "src/cmd/cov",
"src/cmd/prof",
"src/pkg/old",
};
@@ -408,12 +433,16 @@ static char *proto_gccargs[] = {
// native Plan 9 compilers don't like non-standard prototypes
// so let gcc catch them.
"-Wstrict-prototypes",
+ "-Wextra",
+ "-Wunused",
+ "-Wuninitialized",
"-Wno-sign-compare",
"-Wno-missing-braces",
"-Wno-parentheses",
"-Wno-unknown-pragmas",
"-Wno-switch",
"-Wno-comment",
+ "-Wno-missing-field-initializers",
"-Werror",
"-fno-common",
"-ggdb",
@@ -471,6 +500,7 @@ static struct {
{"cmd/gc", {
"-cplx.c",
"-pgen.c",
+ "-popt.c",
"-y1.tab.c", // makefile dreg
"opnames.h",
}},
@@ -495,18 +525,24 @@ static struct {
{"cmd/5g", {
"../gc/cplx.c",
"../gc/pgen.c",
+ "../gc/popt.c",
+ "../gc/popt.h",
"../5l/enam.c",
"$GOROOT/pkg/obj/$GOOS_$GOARCH/libgc.a",
}},
{"cmd/6g", {
"../gc/cplx.c",
"../gc/pgen.c",
+ "../gc/popt.c",
+ "../gc/popt.h",
"../6l/enam.c",
"$GOROOT/pkg/obj/$GOOS_$GOARCH/libgc.a",
}},
{"cmd/8g", {
"../gc/cplx.c",
"../gc/pgen.c",
+ "../gc/popt.c",
+ "../gc/popt.h",
"../8l/enam.c",
"$GOROOT/pkg/obj/$GOOS_$GOARCH/libgc.a",
}},
@@ -522,13 +558,18 @@ static struct {
"../ld/*",
"enam.c",
}},
+ {"cmd/go", {
+ "zdefaultcc.go",
+ }},
{"cmd/", {
"$GOROOT/pkg/obj/$GOOS_$GOARCH/libmach.a",
"$GOROOT/pkg/obj/$GOOS_$GOARCH/libbio.a",
"$GOROOT/pkg/obj/$GOOS_$GOARCH/lib9.a",
}},
{"pkg/runtime", {
+ "zaexperiment.h", // must sort above zasm
"zasm_$GOOS_$GOARCH.h",
+ "zsys_$GOOS_$GOARCH.s",
"zgoarch_$GOARCH.go",
"zgoos_$GOOS.go",
"zruntime_defs_$GOOS_$GOARCH.go",
@@ -553,10 +594,13 @@ static struct {
{"opnames.h", gcopnames},
{"enam.c", mkenam},
{"zasm_", mkzasm},
+ {"zdefaultcc.go", mkzdefaultcc},
+ {"zsys_", mkzsys},
{"zgoarch_", mkzgoarch},
{"zgoos_", mkzgoos},
{"zruntime_defs_", mkzruntimedefs},
{"zversion.go", mkzversion},
+ {"zaexperiment.h", mkzexperiment},
};
// install installs the library, package, or binary associated with dir,
@@ -565,7 +609,7 @@ static void
install(char *dir)
{
char *name, *p, *elem, *prefix, *exe;
- bool islib, ispkg, isgo, stale, clang;
+ bool islib, ispkg, isgo, stale;
Buf b, b1, path;
Vec compile, files, link, go, missing, clean, lib, extra;
Time ttarg, t;
@@ -602,8 +646,8 @@ install(char *dir)
goto out;
}
- // For release, cmd/prof and cmd/cov are not included.
- if((streq(dir, "cmd/cov") || streq(dir, "cmd/prof")) && !isdir(bstr(&path))) {
+ // For release, cmd/prof is not included.
+ if((streq(dir, "cmd/prof")) && !isdir(bstr(&path))) {
if(vflag > 1)
errprintf("skipping %s - does not exist\n", dir);
goto out;
@@ -611,14 +655,13 @@ install(char *dir)
// set up gcc command line on first run.
if(gccargs.len == 0) {
- xgetenv(&b, "CC");
- if(b.len == 0)
- bprintf(&b, "gcc");
- clang = contains(bstr(&b), "clang");
+ bprintf(&b, "%s", defaultcc);
splitfields(&gccargs, bstr(&b));
for(i=0; i<nelem(proto_gccargs); i++)
vadd(&gccargs, proto_gccargs[i]);
- if(clang) {
+ if(contains(gccargs.p[0], "clang")) {
+ // disable ASCII art in clang errors, if possible
+ vadd(&gccargs, "-fno-caret-diagnostics");
// clang is too smart about unused command-line arguments
vadd(&gccargs, "-Qunused-arguments");
}
@@ -677,6 +720,8 @@ install(char *dir)
vadd(&link, bpathf(&b, "%s/%s", tooldir, name));
} else {
vcopy(&link, gccargs.p, gccargs.len);
+ if(sflag)
+ vadd(&link, "-static");
vadd(&link, "-o");
targ = link.len;
vadd(&link, bpathf(&b, "%s/%s%s", tooldir, name, exe));
@@ -901,6 +946,8 @@ install(char *dir)
vadd(&compile, "-Bp+");
vadd(&compile, bpathf(&b, "-I%s/include/plan9", goroot));
vadd(&compile, bpathf(&b, "-I%s/include/plan9/%s", goroot, gohostarch));
+ // Work around Plan 9 C compiler's handling of #include with .. path.
+ vadd(&compile, bpathf(&b, "-I%s/src/cmd/ld", goroot));
} else {
vcopy(&compile, gccargs.p, gccargs.len);
vadd(&compile, "-c");
@@ -962,6 +1009,8 @@ install(char *dir)
vadd(&compile, bprintf(&b, "GOOS_%s", goos));
vadd(&compile, "-D");
vadd(&compile, bprintf(&b, "GOARCH_%s", goarch));
+ vadd(&compile, "-D");
+ vadd(&compile, bprintf(&b, "GOOS_GOARCH_%s_%s", goos, goarch));
}
bpathf(&b, "%s/%s", workdir, lastelem(files.p[i]));
@@ -1198,7 +1247,6 @@ static char *buildorder[] = {
"misc/pprof",
"cmd/addr2line",
- "cmd/cov",
"cmd/nm",
"cmd/objdump",
"cmd/pack",
@@ -1236,6 +1284,7 @@ static char *buildorder[] = {
"pkg/os",
"pkg/reflect",
"pkg/fmt",
+ "pkg/encoding",
"pkg/encoding/json",
"pkg/flag",
"pkg/path/filepath",
@@ -1276,7 +1325,6 @@ static char *cleantab[] = {
"cmd/8l",
"cmd/addr2line",
"cmd/cc",
- "cmd/cov",
"cmd/gc",
"cmd/go",
"cmd/nm",
@@ -1289,6 +1337,7 @@ static char *cleantab[] = {
"pkg/bufio",
"pkg/bytes",
"pkg/container/heap",
+ "pkg/encoding",
"pkg/encoding/base64",
"pkg/encoding/json",
"pkg/errors",
@@ -1339,7 +1388,7 @@ clean(void)
vinit(&dir);
for(i=0; i<nelem(cleantab); i++) {
- if((streq(cleantab[i], "cmd/cov") || streq(cleantab[i], "cmd/prof")) && !isdir(cleantab[i]))
+ if((streq(cleantab[i], "cmd/prof")) && !isdir(cleantab[i]))
continue;
bpathf(&path, "%s/src/%s", goroot, cleantab[i]);
xreaddir(&dir, bstr(&path));
@@ -1438,6 +1487,7 @@ cmdenv(int argc, char **argv)
if(argc > 0)
usage();
+ xprintf(format, "CC", defaultcc);
xprintf(format, "GOROOT", goroot);
xprintf(format, "GOBIN", gobin);
xprintf(format, "GOARCH", goarch);
@@ -1479,6 +1529,9 @@ cmdbootstrap(int argc, char **argv)
case 'a':
rebuildall = 1;
break;
+ case 's':
+ sflag++;
+ break;
case 'v':
vflag++;
break;
@@ -1565,6 +1618,9 @@ cmdinstall(int argc, char **argv)
int i;
ARGBEGIN{
+ case 's':
+ sflag++;
+ break;
case 'v':
vflag++;
break;
@@ -1626,7 +1682,10 @@ cmdbanner(int argc, char **argv)
xprintf("Installed Go for %s/%s in %s\n", goos, goarch, goroot);
xprintf("Installed commands in %s\n", gobin);
- if(streq(gohostos, "plan9")) {
+ if(!xsamefile(goroot_final, goroot)) {
+ // If the files are to be moved, don't check that gobin
+ // is on PATH; assume they know what they are doing.
+ } else if(streq(gohostos, "plan9")) {
// Check that gobin is bound before /bin.
readfile(&b, "#c/pid");
bsubst(&b, " ", "");
diff --git a/src/cmd/dist/buildgo.c b/src/cmd/dist/buildgo.c
new file mode 100644
index 000000000..a340252bc
--- /dev/null
+++ b/src/cmd/dist/buildgo.c
@@ -0,0 +1,49 @@
+// Copyright 2012 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 "a.h"
+
+/*
+ * Helpers for building cmd/go and cmd/cgo.
+ */
+
+// mkzdefaultcc writes zdefaultcc.go:
+//
+// package main
+// const defaultCC = <defaultcc>
+// const defaultCXX = <defaultcxx>
+//
+// It is invoked to write cmd/go/zdefaultcc.go
+// but we also write cmd/cgo/zdefaultcc.go.
+void
+mkzdefaultcc(char *dir, char *file)
+{
+ Buf b, out;
+
+ USED(dir);
+
+ binit(&out);
+ bprintf(&out,
+ "// auto generated by go tool dist\n"
+ "\n"
+ "package main\n"
+ "\n"
+ "const defaultCC = `%s`\n"
+ "const defaultCXX = `%s`\n",
+ defaultcc, defaultcxx);
+
+ writefile(&out, file, 0);
+
+ // Convert file name to replace.
+ binit(&b);
+ bwritestr(&b, file);
+ if(slash[0] == '/')
+ bsubst(&b, "/go/zdefaultcc.go", "/cgo/zdefaultcc.go");
+ else
+ bsubst(&b, "\\go\\zdefaultcc.go", "\\cgo\\zdefaultcc.go");
+ writefile(&out, bstr(&b), 0);
+
+ bfree(&b);
+ bfree(&out);
+}
diff --git a/src/cmd/dist/buildruntime.c b/src/cmd/dist/buildruntime.c
index ee867566f..e6e309d92 100644
--- a/src/cmd/dist/buildruntime.c
+++ b/src/cmd/dist/buildruntime.c
@@ -38,6 +38,34 @@ mkzversion(char *dir, char *file)
bfree(&out);
}
+// mkzexperiment writes zaexperiment.h (sic):
+//
+// #define GOEXPERIMENT "experiment string"
+//
+void
+mkzexperiment(char *dir, char *file)
+{
+ Buf b, out, exp;
+
+ USED(dir);
+
+ binit(&b);
+ binit(&out);
+ binit(&exp);
+
+ xgetenv(&exp, "GOEXPERIMENT");
+ bwritestr(&out, bprintf(&b,
+ "// auto generated by go tool dist\n"
+ "\n"
+ "#define GOEXPERIMENT \"%s\"\n", bstr(&exp)));
+
+ writefile(&out, file, 0);
+
+ bfree(&b);
+ bfree(&out);
+ bfree(&exp);
+}
+
// mkzgoarch writes zgoarch_$GOARCH.go:
//
// package runtime
@@ -162,24 +190,27 @@ static struct {
"#define m(r) 8(GS)\n"
"#define procid(r) 16(GS)\n"
},
- {"amd64", "",
- "// The offsets 0 and 8 are known to:\n"
- "// ../../cmd/6l/pass.c:/D_GS\n"
- "// cgo/gcc_linux_amd64.c:/^threadentry\n"
- "// cgo/gcc_darwin_amd64.c:/^threadentry\n"
- "//\n"
- "#define get_tls(r)\n"
- "#define g(r) 0(GS)\n"
- "#define m(r) 8(GS)\n"
+ // The TLS accessors here are defined here to use initial exec model.
+ // If the linker is not outputting a shared library, it will reduce
+ // the TLS accessors to the local exec model, effectively removing
+ // get_tls().
+ {"amd64", "linux",
+ "#define get_tls(r) MOVQ runtime·tlsgm(SB), r\n"
+ "#define g(r) 0(r)(GS*1)\n"
+ "#define m(r) 8(r)(GS*1)\n"
},
-
+ {"amd64", "",
+ "#define get_tls(r)\n"
+ "#define g(r) 0(GS)\n"
+ "#define m(r) 8(GS)\n"
+ },
{"arm", "",
- "#define g R10\n"
- "#define m R9\n"
"#define LR R14\n"
},
};
+#define MAXWINCB 2000 /* maximum number of windows callbacks allowed */
+
// mkzasm writes zasm_$GOOS_$GOARCH.h,
// which contains struct offsets for use by
// assembly files. It also writes a copy to the work space
@@ -190,12 +221,13 @@ mkzasm(char *dir, char *file)
{
int i, n;
char *aggr, *p;
- Buf in, b, out;
+ Buf in, b, out, exp;
Vec argv, lines, fields;
binit(&in);
binit(&b);
binit(&out);
+ binit(&exp);
vinit(&argv);
vinit(&lines);
vinit(&fields);
@@ -234,10 +266,11 @@ ok:
// Gobuf 24 sched;
// 'Y' 48 stack0;
// }
+ // StackMin = 128;
// into output like
// #define g_sched 24
// #define g_stack0 48
- //
+ // #define const_StackMin 128
aggr = nil;
splitlines(&lines, bstr(&in));
for(i=0; i<lines.len; i++) {
@@ -247,10 +280,14 @@ ok:
aggr = "g";
else if(streq(fields.p[1], "M"))
aggr = "m";
+ else if(streq(fields.p[1], "P"))
+ aggr = "p";
else if(streq(fields.p[1], "Gobuf"))
aggr = "gobuf";
else if(streq(fields.p[1], "WinCall"))
aggr = "wincall";
+ else if(streq(fields.p[1], "WinCallbackContext"))
+ aggr = "cbctxt";
else if(streq(fields.p[1], "SEH"))
aggr = "seh";
}
@@ -263,8 +300,22 @@ ok:
p[xstrlen(p)-1] = '\0';
bwritestr(&out, bprintf(&b, "#define %s_%s %s\n", aggr, fields.p[n-1], fields.p[n-2]));
}
+ if(fields.len == 3 && streq(fields.p[1], "=")) { // generated from enumerated constants
+ p = fields.p[2];
+ if(p[xstrlen(p)-1] == ';')
+ p[xstrlen(p)-1] = '\0';
+ bwritestr(&out, bprintf(&b, "#define const_%s %s\n", fields.p[0], p));
+ }
+ }
+
+ // Some #defines that are used for .c files.
+ if(streq(goos, "windows")) {
+ bwritestr(&out, bprintf(&b, "#define cb_max %d\n", MAXWINCB));
}
+ xgetenv(&exp, "GOEXPERIMENT");
+ bwritestr(&out, bprintf(&b, "#define GOEXPERIMENT \"%s\"\n", bstr(&exp)));
+
// Write both to file and to workdir/zasm_GOOS_GOARCH.h.
writefile(&out, file, 0);
writefile(&out, bprintf(&b, "%s/zasm_GOOS_GOARCH.h", workdir), 0);
@@ -272,11 +323,47 @@ ok:
bfree(&in);
bfree(&b);
bfree(&out);
+ bfree(&exp);
vfree(&argv);
vfree(&lines);
vfree(&fields);
}
+// mkzsys writes zsys_$GOOS_$GOARCH.h,
+// which contains arch or os specific asm code.
+//
+void
+mkzsys(char *dir, char *file)
+{
+ int i;
+ Buf out;
+
+ USED(dir);
+
+ binit(&out);
+
+ bwritestr(&out, "// auto generated by go tool dist\n\n");
+ if(streq(goos, "windows")) {
+ bwritef(&out,
+ "// runtime·callbackasm is called by external code to\n"
+ "// execute Go implemented callback function. It is not\n"
+ "// called from the start, instead runtime·compilecallback\n"
+ "// always returns address into runtime·callbackasm offset\n"
+ "// appropriately so different callbacks start with different\n"
+ "// CALL instruction in runtime·callbackasm. This determines\n"
+ "// which Go callback function is executed later on.\n"
+ "TEXT runtime·callbackasm(SB),7,$0\n");
+ for(i=0; i<MAXWINCB; i++) {
+ bwritef(&out, "\tCALL\truntime·callbackasm1(SB)\n");
+ }
+ bwritef(&out, "\tRET\n");
+ }
+
+ writefile(&out, file, 0);
+
+ bfree(&out);
+}
+
static char *runtimedefs[] = {
"proc.c",
"iface.c",
diff --git a/src/cmd/dist/goc2c.c b/src/cmd/dist/goc2c.c
index f58460397..f0fa04335 100644
--- a/src/cmd/dist/goc2c.c
+++ b/src/cmd/dist/goc2c.c
@@ -694,17 +694,29 @@ copy_body(void)
static void
process_file(void)
{
- char *package, *name;
+ char *package, *name, *p, *n;
struct params *params, *rets;
int paramwid;
package = read_package();
read_preprocessor_lines();
while (read_func_header(&name, &params, &paramwid, &rets)) {
- write_func_header(package, name, params, paramwid, rets);
+ // name may have a package override already
+ n = xstrstr(name, "·");
+ if(n != nil) {
+ p = xmalloc(n - name + 1);
+ xmemmove(p, name, n - name);
+ p[n - name] = 0;
+ n += xstrlen("·");
+ } else {
+ p = package;
+ n = name;
+ }
+ write_func_header(p, n, params, paramwid, rets);
copy_body();
- write_func_trailer(package, name, rets);
+ write_func_trailer(p, n, rets);
xfree(name);
+ if(p != package) xfree(p);
free_params(params);
free_params(rets);
}
diff --git a/src/cmd/dist/main.c b/src/cmd/dist/main.c
index 72a7579d1..fad01802a 100644
--- a/src/cmd/dist/main.c
+++ b/src/cmd/dist/main.c
@@ -5,6 +5,7 @@
#include "a.h"
int vflag;
+int sflag;
char *argv0;
// cmdtab records the available commands.
diff --git a/src/cmd/dist/plan9.c b/src/cmd/dist/plan9.c
index 8a7c0ab1c..8d492ebc6 100644
--- a/src/cmd/dist/plan9.c
+++ b/src/cmd/dist/plan9.c
@@ -578,7 +578,7 @@ hassuffix(char *p, char *suffix)
return np >= ns && strcmp(p+np-ns, suffix) == 0;
}
-// hasprefix reports whether p begins wtih prefix.
+// hasprefix reports whether p begins with prefix.
bool
hasprefix(char *p, char *prefix)
{
@@ -736,7 +736,7 @@ xstrrchr(char *p, int c)
return strrchr(p, c);
}
-// xsamefile returns whether f1 and f2 are the same file (or dir)
+// xsamefile reports whether f1 and f2 are the same file (or dir)
int
xsamefile(char *f1, char *f2)
{
diff --git a/src/cmd/dist/unix.c b/src/cmd/dist/unix.c
index f2ea48974..fa388e058 100644
--- a/src/cmd/dist/unix.c
+++ b/src/cmd/dist/unix.c
@@ -466,9 +466,12 @@ xworkdir(void)
xgetenv(&b, "TMPDIR");
if(b.len == 0)
bwritestr(&b, "/var/tmp");
- bwritestr(&b, "/go-cbuild-XXXXXX");
- if(mkdtemp(bstr(&b)) == nil)
- fatal("mkdtemp: %s", strerror(errno));
+ if(b.p[b.len-1] != '/')
+ bwrite(&b, "/", 1);
+ bwritestr(&b, "go-cbuild-XXXXXX");
+ p = bstr(&b);
+ if(mkdtemp(p) == nil)
+ fatal("mkdtemp(%s): %s", p, strerror(errno));
p = btake(&b);
bfree(&b);
@@ -548,7 +551,7 @@ hassuffix(char *p, char *suffix)
return np >= ns && strcmp(p+np-ns, suffix) == 0;
}
-// hasprefix reports whether p begins wtih prefix.
+// hasprefix reports whether p begins with prefix.
bool
hasprefix(char *p, char *prefix)
{
@@ -651,11 +654,14 @@ int
main(int argc, char **argv)
{
Buf b;
+ int osx;
struct utsname u;
setvbuf(stdout, nil, _IOLBF, 0);
setvbuf(stderr, nil, _IOLBF, 0);
+ setenv("TERM", "dumb", 1); // disable escape codes in clang errors
+
binit(&b);
slash = "/";
@@ -668,6 +674,8 @@ main(int argc, char **argv)
gohostarch = "amd64";
#elif defined(__linux__)
gohostos = "linux";
+#elif defined(__DragonFly__)
+ gohostos = "dragonfly";
#elif defined(__FreeBSD__)
gohostos = "freebsd";
#elif defined(__FreeBSD_kernel__)
@@ -698,17 +706,23 @@ main(int argc, char **argv)
if(strcmp(gohostarch, "arm") == 0)
maxnbg = 1;
- // The OS X 10.6 linker does not support external
- // linking mode; see
- // https://code.google.com/p/go/issues/detail?id=5130 .
- // The mapping from the uname release field to the OS X
- // version number is complicated, but basically 10 or under is
- // OS X 10.6 or earlier.
+ // The OS X 10.6 linker does not support external linking mode.
+ // See golang.org/issue/5130.
+ //
+ // OS X 10.6 does not work with clang either, but OS X 10.9 requires it.
+ // It seems to work with OS X 10.8, so we default to clang for 10.8 and later.
+ // See golang.org/issue/5822.
+ //
+ // Roughly, OS X 10.N shows up as uname release (N+4),
+ // so OS X 10.6 is uname version 10 and OS X 10.8 is uname version 12.
if(strcmp(gohostos, "darwin") == 0) {
if(uname(&u) < 0)
fatal("uname: %s", strerror(errno));
- if(u.release[1] == '.' || hasprefix(u.release, "10"))
+ osx = atoi(u.release) - 4;
+ if(osx <= 6)
goextlinkenabled = "0";
+ if(osx >= 8)
+ defaultclang = 1;
}
init();
@@ -745,7 +759,7 @@ xstrrchr(char *p, int c)
return strrchr(p, c);
}
-// xsamefile returns whether f1 and f2 are the same file (or dir)
+// xsamefile reports whether f1 and f2 are the same file (or dir)
int
xsamefile(char *f1, char *f2)
{
diff --git a/src/cmd/dist/windows.c b/src/cmd/dist/windows.c
index ba23a7ae8..7d03989b2 100644
--- a/src/cmd/dist/windows.c
+++ b/src/cmd/dist/windows.c
@@ -465,7 +465,7 @@ xrealwd(Buf *b, char *path)
torune(&rnew, path);
if(!SetCurrentDirectoryW(rnew))
fatal("chdir %s: %s", path, errstr());
- free(rnew);
+ xfree(rnew);
xgetwd(b);
if(!SetCurrentDirectoryW(old)) {
breset(b);
@@ -929,7 +929,7 @@ xstrrchr(char *p, int c)
return nil;
}
-// xsamefile returns whether f1 and f2 are the same file (or dir)
+// xsamefile reports whether f1 and f2 are the same file (or dir)
int
xsamefile(char *f1, char *f2)
{
diff --git a/src/cmd/fix/netipv6zone.go b/src/cmd/fix/netipv6zone.go
index fe973a211..195c21807 100644
--- a/src/cmd/fix/netipv6zone.go
+++ b/src/cmd/fix/netipv6zone.go
@@ -51,7 +51,7 @@ func netipv6zone(f *ast.File) bool {
Value: e,
}
case 1:
- if e.(*ast.BasicLit).Value == "0" {
+ if elit, ok := e.(*ast.BasicLit); ok && elit.Value == "0" {
cl.Elts = append(cl.Elts[:i], cl.Elts[i+1:]...)
} else {
cl.Elts[i] = &ast.KeyValueExpr{
diff --git a/src/cmd/fix/netipv6zone_test.go b/src/cmd/fix/netipv6zone_test.go
index 0fab00531..142880a12 100644
--- a/src/cmd/fix/netipv6zone_test.go
+++ b/src/cmd/fix/netipv6zone_test.go
@@ -20,6 +20,8 @@ func f() net.Addr {
sub(&net.UDPAddr{ip2, 12345})
c := &net.TCPAddr{IP: ip3, Port: 54321}
d := &net.TCPAddr{ip4, 0}
+ p := 1234
+ e := &net.TCPAddr{ip4, p}
return &net.TCPAddr{ip5}, nil
}
`,
@@ -32,6 +34,8 @@ func f() net.Addr {
sub(&net.UDPAddr{IP: ip2, Port: 12345})
c := &net.TCPAddr{IP: ip3, Port: 54321}
d := &net.TCPAddr{IP: ip4}
+ p := 1234
+ e := &net.TCPAddr{IP: ip4, Port: p}
return &net.TCPAddr{IP: ip5}, nil
}
`,
diff --git a/src/cmd/fix/testdata/reflect.asn1.go.in b/src/cmd/fix/testdata/reflect.asn1.go.in
deleted file mode 100644
index 43128f6b2..000000000
--- a/src/cmd/fix/testdata/reflect.asn1.go.in
+++ /dev/null
@@ -1,814 +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 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/fix/testdata/reflect.asn1.go.out b/src/cmd/fix/testdata/reflect.asn1.go.out
deleted file mode 100644
index ba6224e6d..000000000
--- a/src/cmd/fix/testdata/reflect.asn1.go.out
+++ /dev/null
@@ -1,814 +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 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.ValueOf(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.ValueOf(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.ValueOf(newSlice))
- }
- err = err1
- return
- case bitStringType:
- structValue := v
- bs, err1 := parseBitString(innerBytes)
- if err1 == nil {
- structValue.Set(reflect.ValueOf(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.ValueOf(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.ValueOf(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.ValueOf(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.ValueOf(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/fix/testdata/reflect.datafmt.go.in b/src/cmd/fix/testdata/reflect.datafmt.go.in
deleted file mode 100644
index 91f885f9a..000000000
--- a/src/cmd/fix/testdata/reflect.datafmt.go.in
+++ /dev/null
@@ -1,710 +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 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/fix/testdata/reflect.datafmt.go.out b/src/cmd/fix/testdata/reflect.datafmt.go.out
deleted file mode 100644
index fd447588b..000000000
--- a/src/cmd/fix/testdata/reflect.datafmt.go.out
+++ /dev/null
@@ -1,710 +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 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.ValueOf(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/fix/testdata/reflect.decode.go.in b/src/cmd/fix/testdata/reflect.decode.go.in
deleted file mode 100644
index f831abee3..000000000
--- a/src/cmd/fix/testdata/reflect.decode.go.in
+++ /dev/null
@@ -1,905 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// 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/fix/testdata/reflect.decode.go.out b/src/cmd/fix/testdata/reflect.decode.go.out
deleted file mode 100644
index fb7910ee3..000000000
--- a/src/cmd/fix/testdata/reflect.decode.go.out
+++ /dev/null
@@ -1,908 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// 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.ValueOf(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.ValueOf(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.ValueOf(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.ValueOf(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.ValueOf(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.ValueOf(b[0:n]))
- case reflect.String:
- v.SetString(string(s))
- case reflect.Interface:
- v.Set(reflect.ValueOf(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.ValueOf(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/fix/testdata/reflect.decoder.go.in b/src/cmd/fix/testdata/reflect.decoder.go.in
deleted file mode 100644
index 0ce9b06fd..000000000
--- a/src/cmd/fix/testdata/reflect.decoder.go.in
+++ /dev/null
@@ -1,196 +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 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.NewError("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.NewError("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.NewError("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/fix/testdata/reflect.decoder.go.out b/src/cmd/fix/testdata/reflect.decoder.go.out
deleted file mode 100644
index ece88ecbe..000000000
--- a/src/cmd/fix/testdata/reflect.decoder.go.out
+++ /dev/null
@@ -1,196 +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 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.NewError("gob: duplicate type received")
- return
- }
-
- // Type:
- wire := new(wireType)
- dec.decodeValue(tWireType, reflect.ValueOf(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.NewError("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.ValueOf(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.NewError("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/fix/testdata/reflect.dnsmsg.go.in b/src/cmd/fix/testdata/reflect.dnsmsg.go.in
deleted file mode 100644
index 3d9c312f2..000000000
--- a/src/cmd/fix/testdata/reflect.dnsmsg.go.in
+++ /dev/null
@@ -1,777 +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.
-
-// 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/fix/testdata/reflect.dnsmsg.go.out b/src/cmd/fix/testdata/reflect.dnsmsg.go.out
deleted file mode 100644
index c777fe27c..000000000
--- a/src/cmd/fix/testdata/reflect.dnsmsg.go.out
+++ /dev/null
@@ -1,777 +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.
-
-// 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.ValueOf(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.ValueOf(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.ValueOf(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/fix/testdata/reflect.encode.go.in b/src/cmd/fix/testdata/reflect.encode.go.in
deleted file mode 100644
index 26ce47039..000000000
--- a/src/cmd/fix/testdata/reflect.encode.go.in
+++ /dev/null
@@ -1,367 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// 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/fix/testdata/reflect.encode.go.out b/src/cmd/fix/testdata/reflect.encode.go.out
deleted file mode 100644
index 9a13a75ab..000000000
--- a/src/cmd/fix/testdata/reflect.encode.go.out
+++ /dev/null
@@ -1,367 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// 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.ValueOf(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/fix/testdata/reflect.encoder.go.in b/src/cmd/fix/testdata/reflect.encoder.go.in
deleted file mode 100644
index 702f6dc06..000000000
--- a/src/cmd/fix/testdata/reflect.encoder.go.in
+++ /dev/null
@@ -1,240 +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 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.NewError("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/fix/testdata/reflect.encoder.go.out b/src/cmd/fix/testdata/reflect.encoder.go.out
deleted file mode 100644
index f1a7b98f1..000000000
--- a/src/cmd/fix/testdata/reflect.encoder.go.out
+++ /dev/null
@@ -1,240 +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 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.NewError("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.ValueOf(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.ValueOf(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/fix/testdata/reflect.export.go.in b/src/cmd/fix/testdata/reflect.export.go.in
deleted file mode 100644
index 722387ac5..000000000
--- a/src/cmd/fix/testdata/reflect.export.go.in
+++ /dev/null
@@ -1,400 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-/*
- 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 (
- "io"
- "log"
- "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.NewError("not a channel")
- }
- if dir != Send && dir != Recv {
- return nil, os.NewError("unknown channel direction")
- }
- switch chanType.Dir() {
- case reflect.BothDir:
- case reflect.SendDir:
- if dir != Recv {
- return nil, os.NewError("to import/export with Send, must provide <-chan")
- }
- case reflect.RecvDir:
- if dir != Send {
- return nil, os.NewError("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.NewError("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.NewError("netchan export: hangup: no such channel: " + name)
- }
- chDir.ch.Close()
- return nil
-}
diff --git a/src/cmd/fix/testdata/reflect.export.go.out b/src/cmd/fix/testdata/reflect.export.go.out
deleted file mode 100644
index d1324f346..000000000
--- a/src/cmd/fix/testdata/reflect.export.go.out
+++ /dev/null
@@ -1,400 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-/*
- 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 (
- "io"
- "log"
- "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.ValueOf(hdr)
- req := new(request)
- reqValue := reflect.ValueOf(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.NewError("not a channel")
- }
- if dir != Send && dir != Recv {
- return reflect.Value{}, os.NewError("unknown channel direction")
- }
- switch chanType.ChanDir() {
- case reflect.BothDir:
- case reflect.SendDir:
- if dir != Recv {
- return reflect.Value{}, os.NewError("to import/export with Send, must provide <-chan")
- }
- case reflect.RecvDir:
- if dir != Send {
- return reflect.Value{}, os.NewError("to import/export with Recv, must provide chan<-")
- }
- }
- return reflect.ValueOf(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.NewError("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.NewError("netchan export: hangup: no such channel: " + name)
- }
- chDir.ch.Close()
- return nil
-}
diff --git a/src/cmd/fix/testdata/reflect.print.go.in b/src/cmd/fix/testdata/reflect.print.go.in
deleted file mode 100644
index 14cf2b215..000000000
--- a/src/cmd/fix/testdata/reflect.print.go.in
+++ /dev/null
@@ -1,944 +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 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.NewError(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/fix/testdata/reflect.print.go.out b/src/cmd/fix/testdata/reflect.print.go.out
deleted file mode 100644
index e4e4c7368..000000000
--- a/src/cmd/fix/testdata/reflect.print.go.out
+++ /dev/null
@@ -1,944 +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 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.NewError(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.ValueOf(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.ValueOf(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.ValueOf(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/fix/testdata/reflect.quick.go.in b/src/cmd/fix/testdata/reflect.quick.go.in
deleted file mode 100644
index a5568b048..000000000
--- a/src/cmd/fix/testdata/reflect.quick.go.in
+++ /dev/null
@@ -1,364 +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.
-
-// 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/fix/testdata/reflect.quick.go.out b/src/cmd/fix/testdata/reflect.quick.go.out
deleted file mode 100644
index c62305b83..000000000
--- a/src/cmd/fix/testdata/reflect.quick.go.out
+++ /dev/null
@@ -1,365 +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.
-
-// 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.ValueOf(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.ValueOf(randFloat32(rand)), true
- case reflect.Float64:
- return reflect.ValueOf(randFloat64(rand)), true
- case reflect.Complex64:
- return reflect.ValueOf(complex(randFloat32(rand), randFloat32(rand))), true
- case reflect.Complex128:
- return reflect.ValueOf(complex(randFloat64(rand), randFloat64(rand))), true
- case reflect.Int16:
- return reflect.ValueOf(int16(randInt64(rand))), true
- case reflect.Int32:
- return reflect.ValueOf(int32(randInt64(rand))), true
- case reflect.Int64:
- return reflect.ValueOf(randInt64(rand)), true
- case reflect.Int8:
- return reflect.ValueOf(int8(randInt64(rand))), true
- case reflect.Int:
- return reflect.ValueOf(int(randInt64(rand))), true
- case reflect.Uint16:
- return reflect.ValueOf(uint16(randInt64(rand))), true
- case reflect.Uint32:
- return reflect.ValueOf(uint32(randInt64(rand))), true
- case reflect.Uint64:
- return reflect.ValueOf(uint64(randInt64(rand))), true
- case reflect.Uint8:
- return reflect.ValueOf(uint8(randInt64(rand))), true
- case reflect.Uint:
- return reflect.ValueOf(uint(randInt64(rand))), true
- case reflect.Uintptr:
- return reflect.ValueOf(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.ValueOf(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.ValueOf(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/fix/testdata/reflect.read.go.in b/src/cmd/fix/testdata/reflect.read.go.in
deleted file mode 100644
index 487994ac6..000000000
--- a/src/cmd/fix/testdata/reflect.read.go.in
+++ /dev/null
@@ -1,620 +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 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.NewError("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.NewError("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/fix/testdata/reflect.read.go.out b/src/cmd/fix/testdata/reflect.read.go.out
deleted file mode 100644
index a6b126744..000000000
--- a/src/cmd/fix/testdata/reflect.read.go.out
+++ /dev/null
@@ -1,620 +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 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.ValueOf(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.ValueOf(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.NewError("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.ValueOf(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.ValueOf(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.NewError("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.ValueOf(data))
- }
-
- switch t := saveComment; t.Kind() {
- case reflect.String:
- t.SetString(string(comment))
- case reflect.Slice:
- t.Set(reflect.ValueOf(comment))
- }
-
- switch t := saveXML; t.Kind() {
- case reflect.String:
- t.SetString(string(saveXMLData))
- case reflect.Slice:
- t.Set(reflect.ValueOf(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/fix/testdata/reflect.scan.go.in b/src/cmd/fix/testdata/reflect.scan.go.in
deleted file mode 100644
index 51898181f..000000000
--- a/src/cmd/fix/testdata/reflect.scan.go.in
+++ /dev/null
@@ -1,1082 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-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.NewError("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.NewError(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.NewError("syntax error scanning complex number")
-var boolError = os.NewError("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/fix/testdata/reflect.scan.go.out b/src/cmd/fix/testdata/reflect.scan.go.out
deleted file mode 100644
index 956c13bde..000000000
--- a/src/cmd/fix/testdata/reflect.scan.go.out
+++ /dev/null
@@ -1,1082 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-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.NewError("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.NewError(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.NewError("syntax error scanning complex number")
-var boolError = os.NewError("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.ValueOf(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/fix/testdata/reflect.script.go.in b/src/cmd/fix/testdata/reflect.script.go.in
deleted file mode 100644
index b341b1f89..000000000
--- a/src/cmd/fix/testdata/reflect.script.go.in
+++ /dev/null
@@ -1,359 +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.
-
-// 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/fix/testdata/reflect.script.go.out b/src/cmd/fix/testdata/reflect.script.go.out
deleted file mode 100644
index bc5a6a41d..000000000
--- a/src/cmd/fix/testdata/reflect.script.go.out
+++ /dev/null
@@ -1,359 +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.
-
-// 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.ValueOf(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.ValueOf(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.ValueOf(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.ValueOf(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.ValueOf(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.ValueOf(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/fix/testdata/reflect.template.go.in b/src/cmd/fix/testdata/reflect.template.go.in
deleted file mode 100644
index 1f5a8128f..000000000
--- a/src/cmd/fix/testdata/reflect.template.go.in
+++ /dev/null
@@ -1,1043 +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.
-
-/*
- 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:], "|")
- }
-
- // 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, ".") {
- // 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/fix/testdata/reflect.template.go.out b/src/cmd/fix/testdata/reflect.template.go.out
deleted file mode 100644
index f2f56ef3c..000000000
--- a/src/cmd/fix/testdata/reflect.template.go.out
+++ /dev/null
@@ -1,1044 +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.
-
-/*
- 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:], "|")
- }
-
- // 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.ValueOf(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, ".") {
- // 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.ValueOf(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.ValueOf(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/fix/testdata/reflect.type.go.in b/src/cmd/fix/testdata/reflect.type.go.in
deleted file mode 100644
index 34963bef9..000000000
--- a/src/cmd/fix/testdata/reflect.type.go.in
+++ /dev/null
@@ -1,790 +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 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.NewError("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.NewError("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/fix/testdata/reflect.type.go.out b/src/cmd/fix/testdata/reflect.type.go.out
deleted file mode 100644
index d729ea471..000000000
--- a/src/cmd/fix/testdata/reflect.type.go.out
+++ /dev/null
@@ -1,790 +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 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.NewError("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.NewError("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/gc/align.c b/src/cmd/gc/align.c
index 80c65387b..8e9677e75 100644
--- a/src/cmd/gc/align.c
+++ b/src/cmd/gc/align.c
@@ -119,7 +119,8 @@ dowidth(Type *t)
if(t->width == -2) {
lno = lineno;
lineno = t->lineno;
- yyerror("invalid recursive type %T", t);
+ if(!t->broke)
+ yyerror("invalid recursive type %T", t);
t->width = 0;
lineno = lno;
return;
@@ -219,7 +220,8 @@ dowidth(Type *t)
checkwidth(t->down);
break;
case TFORW: // should have been filled in
- yyerror("invalid recursive type %T", t);
+ if(!t->broke)
+ yyerror("invalid recursive type %T", t);
w = 1; // anything will do
break;
case TANY:
diff --git a/src/cmd/gc/bisonerrors b/src/cmd/gc/bisonerrors
index 8886a8e52..1f97fc8ce 100755
--- a/src/cmd/gc/bisonerrors
+++ b/src/cmd/gc/bisonerrors
@@ -35,6 +35,9 @@ grammar && NF>0 {
}
rulelhs[$1] = r
rulesize[$1] = NF-2
+ if(rulesize[$1] == 1 && $3 == "%empty") {
+ rulesize[$1] = 0
+ }
if(rulesize[$1] == 3 && $3 $4 $5 == "/*empty*/") {
rulesize[$1] = 0
}
diff --git a/src/cmd/gc/builtin.c b/src/cmd/gc/builtin.c
index 9053dfe10..309dc1ea0 100644
--- a/src/cmd/gc/builtin.c
+++ b/src/cmd/gc/builtin.c
@@ -24,9 +24,6 @@ char *runtimeimport =
"func @\"\".printsp ()\n"
"func @\"\".goprintf ()\n"
"func @\"\".concatstring ()\n"
- "func @\"\".append ()\n"
- "func @\"\".appendslice (@\"\".typ·2 *byte, @\"\".x·3 any, @\"\".y·4 []any) (? any)\n"
- "func @\"\".appendstr (@\"\".typ·2 *byte, @\"\".x·3 []byte, @\"\".y·4 string) (? []byte)\n"
"func @\"\".cmpstring (? string, ? string) (? int)\n"
"func @\"\".eqstring (? string, ? string) (? bool)\n"
"func @\"\".intstring (? int64) (? string)\n"
@@ -94,6 +91,7 @@ char *runtimeimport =
"func @\"\".block ()\n"
"func @\"\".makeslice (@\"\".typ·2 *byte, @\"\".nel·3 int64, @\"\".cap·4 int64) (@\"\".ary·1 []any)\n"
"func @\"\".growslice (@\"\".typ·2 *byte, @\"\".old·3 []any, @\"\".n·4 int64) (@\"\".ary·1 []any)\n"
+ "func @\"\".memmove (@\"\".to·1 *any, @\"\".frm·2 *any, @\"\".length·3 uintptr)\n"
"func @\"\".memequal (@\"\".eq·1 *bool, @\"\".size·2 uintptr, @\"\".x·3 *any, @\"\".y·4 *any)\n"
"func @\"\".memequal8 (@\"\".eq·1 *bool, @\"\".size·2 uintptr, @\"\".x·3 *any, @\"\".y·4 *any)\n"
"func @\"\".memequal16 (@\"\".eq·1 *bool, @\"\".size·2 uintptr, @\"\".x·3 *any, @\"\".y·4 *any)\n"
@@ -113,6 +111,8 @@ char *runtimeimport =
"func @\"\".racefuncexit ()\n"
"func @\"\".raceread (? uintptr)\n"
"func @\"\".racewrite (? uintptr)\n"
+ "func @\"\".racereadrange (@\"\".addr·1 uintptr, @\"\".size·2 uintptr)\n"
+ "func @\"\".racewriterange (@\"\".addr·1 uintptr, @\"\".size·2 uintptr)\n"
"\n"
"$$\n";
char *unsafeimport =
diff --git a/src/cmd/gc/bv.c b/src/cmd/gc/bv.c
new file mode 100644
index 000000000..92834a97b
--- /dev/null
+++ b/src/cmd/gc/bv.c
@@ -0,0 +1,80 @@
+// Copyright 2013 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 <u.h>
+#include <libc.h>
+#include "go.h"
+
+enum {
+ WORDSIZE = sizeof(uint32),
+ WORDBITS = 32,
+};
+
+uintptr
+bvsize(uintptr n)
+{
+ return ((n + WORDBITS - 1) / WORDBITS) * WORDSIZE;
+}
+
+Bvec*
+bvalloc(int32 n)
+{
+ Bvec *bv;
+ uintptr nbytes;
+
+ if(n < 0)
+ fatal("bvalloc: initial size is negative\n");
+ nbytes = sizeof(Bvec) + bvsize(n);
+ bv = malloc(nbytes);
+ if(bv == nil)
+ fatal("bvalloc: malloc failed\n");
+ memset(bv, 0, nbytes);
+ bv->n = n;
+ return bv;
+}
+
+void
+bvset(Bvec *bv, int32 i)
+{
+ uint32 mask;
+
+ if(i < 0 || i >= bv->n)
+ fatal("bvset: index %d is out of bounds with length %d\n", i, bv->n);
+ mask = 1U << (i % WORDBITS);
+ bv->b[i / WORDBITS] |= mask;
+}
+
+void
+bvres(Bvec *bv, int32 i)
+{
+ uint32 mask;
+
+ if(i < 0 || i >= bv->n)
+ fatal("bvres: index %d is out of bounds with length %d\n", i, bv->n);
+ mask = ~(1 << (i % WORDBITS));
+ bv->b[i / WORDBITS] &= mask;
+}
+
+int
+bvget(Bvec *bv, int32 i)
+{
+ uint32 mask, word;
+
+ if(i < 0 || i >= bv->n)
+ fatal("bvget: index %d is out of bounds with length %d\n", i, bv->n);
+ mask = 1 << (i % WORDBITS);
+ word = bv->b[i / WORDBITS] & mask;
+ return word ? 1 : 0;
+}
+
+int
+bvisempty(Bvec *bv)
+{
+ int32 i;
+
+ for(i = 0; i < bv->n; i += WORDBITS)
+ if(bv->b[i / WORDBITS] != 0)
+ return 0;
+ return 1;
+}
diff --git a/src/cmd/gc/closure.c b/src/cmd/gc/closure.c
index 996504a11..5a84dfb1b 100644
--- a/src/cmd/gc/closure.c
+++ b/src/cmd/gc/closure.c
@@ -285,6 +285,8 @@ makepartialcall(Node *fn, Type *t0, Node *meth)
NodeList *body, *l, *callargs, *retargs;
char *p;
Sym *sym;
+ Pkg *spkg;
+ static Pkg* gopkg;
int i, ddd;
// TODO: names are not right
@@ -296,10 +298,18 @@ makepartialcall(Node *fn, Type *t0, Node *meth)
basetype = rcvrtype;
if(isptr[rcvrtype->etype])
basetype = basetype->type;
- if(basetype->sym == S)
+ if(basetype->etype != TINTER && basetype->sym == S)
fatal("missing base type for %T", rcvrtype);
- sym = pkglookup(p, basetype->sym->pkg);
+ spkg = nil;
+ if(basetype->sym != S)
+ spkg = basetype->sym->pkg;
+ if(spkg == nil) {
+ if(gopkg == nil)
+ gopkg = mkpkg(strlit("go"));
+ spkg = gopkg;
+ }
+ sym = pkglookup(p, spkg);
free(p);
if(sym->flags & SymUniq)
return sym->def;
@@ -407,8 +417,10 @@ walkpartialcall(Node *n, NodeList **init)
// Like walkclosure above.
if(isinter(n->left->type)) {
+ // Trigger panic for method on nil interface now.
+ // Otherwise it happens in the wrapper and is confusing.
n->left = cheapexpr(n->left, init);
- checknotnil(n->left, init);
+ checknil(n->left, init);
}
typ = nod(OTSTRUCT, N, N);
diff --git a/src/cmd/gc/const.c b/src/cmd/gc/const.c
index e9d99df18..cfb1f0ade 100644
--- a/src/cmd/gc/const.c
+++ b/src/cmd/gc/const.c
@@ -1056,7 +1056,7 @@ nodcplxlit(Val r, Val i)
}
// idealkind returns a constant kind like consttype
-// but for an arbitrary "ideal" expression.
+// but for an arbitrary "ideal" (untyped constant) expression.
static int
idealkind(Node *n)
{
diff --git a/src/cmd/gc/dcl.c b/src/cmd/gc/dcl.c
index d3759efde..c7d13ef06 100644
--- a/src/cmd/gc/dcl.c
+++ b/src/cmd/gc/dcl.c
@@ -141,6 +141,8 @@ testdclstack(void)
for(d=dclstack; d!=S; d=d->link) {
if(d->name == nil) {
+ if(nerrors != 0)
+ errorexit();
yyerror("mark left on the stack");
continue;
}
@@ -287,7 +289,7 @@ variter(NodeList *vl, Node *t, NodeList *el)
for(; vl; vl=vl->next) {
if(doexpr) {
if(el == nil) {
- yyerror("missing expr in var dcl");
+ yyerror("missing expression in var declaration");
break;
}
e = el->n;
@@ -310,7 +312,7 @@ variter(NodeList *vl, Node *t, NodeList *el)
}
}
if(el != nil)
- yyerror("extra expr in var dcl");
+ yyerror("extra expression in var declaration");
return init;
}
@@ -327,7 +329,7 @@ constiter(NodeList *vl, Node *t, NodeList *cl)
vv = nil;
if(cl == nil) {
if(t != N)
- yyerror("constdcl cannot have type without expr");
+ yyerror("const declaration cannot have type without expression");
cl = lastconst;
t = lasttype;
} else {
@@ -338,7 +340,7 @@ constiter(NodeList *vl, Node *t, NodeList *cl)
for(; vl; vl=vl->next) {
if(cl == nil) {
- yyerror("missing expr in const dcl");
+ yyerror("missing value in const declaration");
break;
}
c = cl->n;
@@ -354,7 +356,7 @@ constiter(NodeList *vl, Node *t, NodeList *cl)
vv = list(vv, nod(ODCLCONST, v, N));
}
if(cl != nil)
- yyerror("extra expr in const dcl");
+ yyerror("extra expression in const declaration");
iota += 1;
return vv;
}
@@ -1019,7 +1021,7 @@ tointerface(NodeList *l)
}
Node*
-embedded(Sym *s)
+embedded(Sym *s, Pkg *pkg)
{
Node *n;
char *name;
@@ -1036,9 +1038,9 @@ embedded(Sym *s)
if(exportname(name))
n = newname(lookup(name));
- else if(s->pkg == builtinpkg && importpkg != nil)
- // The name of embedded builtins during imports belongs to importpkg.
- n = newname(pkglookup(name, importpkg));
+ else if(s->pkg == builtinpkg)
+ // The name of embedded builtins belongs to pkg.
+ n = newname(pkglookup(name, pkg));
else
n = newname(pkglookup(name, s->pkg));
n = nod(ODCLFIELD, n, oldname(s));
diff --git a/src/cmd/gc/esc.c b/src/cmd/gc/esc.c
index df273e392..b84b66ef1 100644
--- a/src/cmd/gc/esc.c
+++ b/src/cmd/gc/esc.c
@@ -144,7 +144,7 @@ visitcode(Node *n, uint32 min)
fn = n->left;
if(n->op == OCALLMETH)
fn = n->left->right->sym->def;
- if(fn && fn->op == ONAME && fn->class == PFUNC && fn->defn && fn->defn->nbody)
+ if(fn && fn->op == ONAME && fn->class == PFUNC && fn->defn)
if((m = visit(fn->defn)) < min)
min = m;
}
@@ -749,6 +749,8 @@ escassign(EscState *e, Node *dst, Node *src)
case ODOTTYPE2:
case OSLICE:
case OSLICEARR:
+ case OSLICE3:
+ case OSLICE3ARR:
// Conversions, field access, slice all preserve the input value.
escassign(e, dst, src->left);
break;
diff --git a/src/cmd/gc/export.c b/src/cmd/gc/export.c
index caac330d5..31bcdf8e7 100644
--- a/src/cmd/gc/export.c
+++ b/src/cmd/gc/export.c
@@ -44,7 +44,7 @@ initname(char *s)
return strcmp(s, "init") == 0;
}
-// exportedsym returns whether a symbol will be visible
+// exportedsym reports whether a symbol will be visible
// to files that import our package.
static int
exportedsym(Sym *sym)
@@ -481,9 +481,10 @@ importvar(Sym *s, Type *t)
if(s->def != N && s->def->op == ONAME) {
if(eqtype(t, s->def->type))
return;
- yyerror("inconsistent definition for var %S during import\n\t%T\n\t%T", s, s->def->type, t);
+ yyerror("inconsistent definition for var %S during import\n\t%T (in \"%Z\")\n\t%T (in \"%Z\")", s, s->def->type, s->importdef->path, t, importpkg->path);
}
n = newname(s);
+ s->importdef = importpkg;
n->type = t;
declare(n, PEXTERN);
@@ -509,11 +510,12 @@ importtype(Type *pt, Type *t)
n = pt->nod;
copytype(pt->nod, t);
pt->nod = n; // unzero nod
+ pt->sym->importdef = importpkg;
pt->sym->lastlineno = parserline();
declare(n, PEXTERN);
checkwidth(pt);
} else 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 (in \"%Z\")\n\t%lT (in \"%Z\")", pt->sym, pt, pt->sym->importdef->path, t, importpkg->path);
if(debug['E'])
print("import type %T %lT\n", pt, t);
diff --git a/src/cmd/gc/fmt.c b/src/cmd/gc/fmt.c
index b5f6a9b8f..9cd344870 100644
--- a/src/cmd/gc/fmt.c
+++ b/src/cmd/gc/fmt.c
@@ -415,6 +415,8 @@ Zconv(Fmt *fp)
s = sp->s;
se = s + sp->len;
+
+ // NOTE: Keep in sync with ../ld/go.c:/^Zconv.
while(s < se) {
n = chartorune(&r, s);
s += n;
@@ -575,7 +577,7 @@ basicnames[] =
[TANY] = "any",
[TSTRING] = "string",
[TNIL] = "nil",
- [TIDEAL] = "ideal",
+ [TIDEAL] = "untyped number",
[TBLANK] = "blank",
};
@@ -602,8 +604,11 @@ typefmt(Fmt *fp, Type *t)
if(!(fp->flags&FmtLong) && t->sym && t->etype != TFIELD && t != types[t->etype]) {
switch(fmtmode) {
case FTypeId:
- if(fp->flags&FmtShort)
+ if(fp->flags&FmtShort) {
+ if(t->vargen)
+ return fmtprint(fp, "%hS·%d", t->sym, t->vargen);
return fmtprint(fp, "%hS", t->sym);
+ }
if(fp->flags&FmtUnsigned)
return fmtprint(fp, "%uS", t->sym);
// fallthrough
@@ -617,7 +622,7 @@ typefmt(Fmt *fp, Type *t)
if(t->etype < nelem(basicnames) && basicnames[t->etype] != nil) {
if(fmtmode == FErr && (t == idealbool || t == idealstring))
- fmtstrcpy(fp, "ideal ");
+ fmtstrcpy(fp, "untyped ");
return fmtstrcpy(fp, basicnames[t->etype]);
}
@@ -695,6 +700,13 @@ typefmt(Fmt *fp, Type *t)
return 0;
case TSTRUCT:
+ // Format the bucket struct for map[x]y as map.bucket[x]y.
+ // This avoids a recursive print that generates very long names.
+ if(t->hmap != T) {
+ t = t->hmap;
+ return fmtprint(fp, "map.bucket[%T]%T", t->down, t->type);
+ }
+
if(t->funarg) {
fmtstrcpy(fp, "(");
if(fmtmode == FTypeId || fmtmode == FErr) { // no argument names on function signature, and no "noescape" tags
@@ -749,6 +761,9 @@ typefmt(Fmt *fp, Type *t)
//if(t->funarg)
// fmtstrcpy(fp, "_ ");
//else
+ if(t->embedded && s->pkg != nil && s->pkg->path->len > 0)
+ fmtprint(fp, "@\"%Z\".? ", s->pkg->path);
+ else
fmtstrcpy(fp, "? ");
}
}
@@ -870,6 +885,10 @@ stmtfmt(Fmt *f, Node *n)
fmtprint(f, "return %,H", n->list);
break;
+ case ORETJMP:
+ fmtprint(f, "retjmp %S", n->sym);
+ break;
+
case OPROC:
fmtprint(f, "go %N", n->left);
break;
@@ -1018,6 +1037,8 @@ static int opprec[] = {
[OSLICE] = 8,
[OSLICESTR] = 8,
[OSLICEARR] = 8,
+ [OSLICE3] = 8,
+ [OSLICE3ARR] = 8,
[ODOTINTER] = 8,
[ODOTMETH] = 8,
[ODOTPTR] = 8,
@@ -1291,6 +1312,8 @@ exprfmt(Fmt *f, Node *n, int prec)
case OSLICE:
case OSLICESTR:
case OSLICEARR:
+ case OSLICE3:
+ case OSLICE3ARR:
exprfmt(f, n->left, nprec);
return fmtprint(f, "[%N]", n->right);
diff --git a/src/cmd/gc/gen.c b/src/cmd/gc/gen.c
index 955ec2c5b..ada16eacc 100644
--- a/src/cmd/gc/gen.c
+++ b/src/cmd/gc/gen.c
@@ -489,11 +489,12 @@ gen(Node *n)
break;
case ORETURN:
+ case ORETJMP:
cgen_ret(n);
break;
- case OCHECKNOTNIL:
- checkref(n->left, 1);
+ case OCHECKNIL:
+ cgen_checknil(n->left);
}
ret:
@@ -628,6 +629,10 @@ cgen_discard(Node *nr)
case OPLUS:
cgen_discard(nr->left);
break;
+
+ case OIND:
+ cgen_checknil(nr->left);
+ break;
// special enough to just evaluate
default:
@@ -776,6 +781,8 @@ cgen_eface(Node *n, Node *res)
* n->left is s
* n->list is (cap(s)-lo(TUINT), hi-lo(TUINT)[, lo*width(TUINTPTR)])
* caller (cgen) guarantees res is an addable ONAME.
+ *
+ * called for OSLICE, OSLICE3, OSLICEARR, OSLICE3ARR, OSLICESTR.
*/
void
cgen_slice(Node *n, Node *res)
@@ -807,21 +814,26 @@ cgen_slice(Node *n, Node *res)
dst.xoffset += Array_array;
dst.type = types[TUINTPTR];
- if(n->op == OSLICEARR) {
- if(!isptr[n->left->type->etype])
- fatal("slicearr is supposed to work on pointer: %+N\n", n);
- checkref(n->left, 0);
- }
-
if(isnil(n->left)) {
tempname(&src, n->left->type);
cgen(n->left, &src);
} else
src = *n->left;
- src.xoffset += Array_array;
+ if(n->op == OSLICE || n->op == OSLICE3 || n->op == OSLICESTR)
+ src.xoffset += Array_array;
src.type = types[TUINTPTR];
- if(offs == N) {
+ if(n->op == OSLICEARR || n->op == OSLICE3ARR) {
+ if(!isptr[n->left->type->etype])
+ fatal("slicearr is supposed to work on pointer: %+N\n", n);
+ cgen(&src, &dst);
+ cgen_checknil(&dst);
+ if(offs != N) {
+ add = nod(OADD, &dst, offs);
+ typecheck(&add, Erv);
+ cgen(add, &dst);
+ }
+ } else if(offs == N) {
cgen(&src, &dst);
} else {
add = nod(OADD, &src, offs);
diff --git a/src/cmd/gc/go.errors b/src/cmd/gc/go.errors
index 68a5e5af3..f90d61990 100644
--- a/src/cmd/gc/go.errors
+++ b/src/cmd/gc/go.errors
@@ -17,17 +17,20 @@ static struct {
% loadsys package LIMPORT '(' LLITERAL import_package import_there ','
"unexpected comma during import block",
+ % loadsys package LIMPORT LNAME ';'
+ "missing import path; require quoted string",
+
% loadsys package imports LFUNC LNAME '(' ')' '{' LIF if_header ';'
- "unexpected semicolon or newline before {",
+ "missing { after if clause",
% loadsys package imports LFUNC LNAME '(' ')' '{' LSWITCH if_header ';'
- "unexpected semicolon or newline before {",
+ "missing { after switch clause",
% loadsys package imports LFUNC LNAME '(' ')' '{' LFOR for_header ';'
- "unexpected semicolon or newline before {",
+ "missing { after for clause",
% loadsys package imports LFUNC LNAME '(' ')' '{' LFOR ';' LBODY
- "unexpected semicolon or newline before {",
+ "missing { after for clause",
% loadsys package imports LFUNC LNAME '(' ')' ';' '{'
"unexpected semicolon or newline before {",
diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h
index e94eb90ee..562f16890 100644
--- a/src/cmd/gc/go.h
+++ b/src/cmd/gc/go.h
@@ -63,6 +63,8 @@ enum
ACPLX128,
BADWIDTH = -1000000000,
+
+ MaxStackVarSize = 10*1024*1024,
};
extern vlong MAXWIDTH;
@@ -76,7 +78,7 @@ typedef struct Strlit Strlit;
struct Strlit
{
int32 len;
- char s[3]; // variable
+ char s[1]; // variable
};
enum
@@ -127,6 +129,7 @@ struct Val
} u;
};
+typedef struct Bvec Bvec;
typedef struct Pkg Pkg;
typedef struct Sym Sym;
typedef struct Node Node;
@@ -138,6 +141,7 @@ struct Type
{
uchar etype;
uchar nointerface;
+ uchar noalg;
uchar chan;
uchar trecur; // to detect loops
uchar printed;
@@ -150,6 +154,7 @@ struct Type
uchar broke; // broken type definition.
uchar isddd; // TFIELD is ... argument
uchar align;
+ uchar haspointers; // 0 unknown, 1 no, 2 yes
Node* nod; // canonical OTYPE node
Type* orig; // original type (type literal or predefined type)
@@ -182,14 +187,17 @@ struct Type
// TARRAY
vlong bound; // negative is dynamic array
+ // TMAP
+ Type* bucket; // internal type representing a hash bucket
+ Type* hmap; // internal type representing a Hmap (map header object)
+
int32 maplineno; // first use of TFORW as map key
int32 embedlineno; // first use of TFORW as embedded type
// for TFORW, where to copy the eventual value to
NodeList *copyto;
- // for usefield
- Node *lastfn;
+ Node *lastfn; // for usefield
};
#define T ((Type*)0)
@@ -265,8 +273,10 @@ struct Node
uchar implicit;
uchar addrtaken; // address taken, even if not moved to heap
uchar dupok; // duplicate definitions ok (for func)
+ uchar wrapper; // is method wrapper (for func)
schar likely; // likeliness of if statement
uchar hasbreak; // has break statement
+ uchar needzero; // if it contains pointers, needs to be zeroed on function entry
uint esc; // EscXXX
int funcdepth;
@@ -327,6 +337,7 @@ struct Node
int32 iota;
uint32 walkgen;
int32 esclevel;
+ void* opt; // for optimization passes
};
#define N ((Node*)0)
@@ -371,6 +382,7 @@ struct Sym
Sym* link;
int32 npkg; // number of imported packages with this name
uint32 uniqgen;
+ Pkg* importdef; // where imported definition was found
// saved and restored by dcopy
Pkg* pkg;
@@ -519,6 +531,8 @@ enum
OSLICE, // v[1:2], typechecking may convert to a more specfic OSLICEXXX.
OSLICEARR, // a[1:2]
OSLICESTR, // s[1:2]
+ OSLICE3, // v[1:2:3], typechecking may convert to OSLICE3ARR.
+ OSLICE3ARR, // a[1:2:3]
ORECOVER, // recover
ORECV, // <-c
ORUNESTR, // string(i)
@@ -565,9 +579,10 @@ enum
OINLCALL, // intermediary representation of an inlined call.
OEFACE, // itable and data words of an empty-interface value.
OITAB, // itable word of an interface value.
+ OSPTR, // base pointer of a slice or string.
OCLOSUREVAR, // variable reference at beginning of closure function
OCFUNC, // reference to c function pointer (not go func value)
- OCHECKNOTNIL, // emit code to ensure pointer/interface not nil
+ OCHECKNIL, // emit code to ensure pointer/interface not nil
// arch-specific registers
OREGISTER, // a register, such as AX.
@@ -581,6 +596,7 @@ enum
OHMUL, // high mul: AMUL/AIMUL for unsigned/signed (OMUL uses AIMUL for both).
OLROT, // left rotate: AROL.
ORROTC, // right rotate-carry: ARCR.
+ ORETJMP, // return to other function
OEND,
};
@@ -697,6 +713,12 @@ struct Bits
EXTERN Bits zbits;
+struct Bvec
+{
+ int32 n; // number of bits
+ uint32 b[];
+};
+
typedef struct Var Var;
struct Var
{
@@ -741,6 +763,7 @@ struct Io
int32 ilineno;
int nlsemi;
int eofnl;
+ int last;
int peekc;
int peekc1; // second peekc for ...
char* cp; // used for content when bin==nil
@@ -851,6 +874,8 @@ EXTERN char namebuf[NSYMB];
EXTERN char lexbuf[NSYMB];
EXTERN char litbuf[NSYMB];
EXTERN int debug[256];
+EXTERN char* debugstr;
+EXTERN int debug_checknil;
EXTERN Sym* hash[NHASH];
EXTERN Sym* importmyname; // my name for package
EXTERN Pkg* localpkg; // package being compiled
@@ -924,6 +949,8 @@ EXTERN NodeList* lastconst;
EXTERN Node* lasttype;
EXTERN vlong maxarg;
EXTERN vlong stksize; // stack size for current frame
+EXTERN vlong stkptrsize; // prefix of stack containing pointers
+EXTERN vlong stkzerosize; // prefix of stack that must be zeroed on entry
EXTERN int32 blockgen; // max block number
EXTERN int32 block; // current block number
EXTERN int hasdefer; // flag that curfn has defer statetment
@@ -949,12 +976,14 @@ EXTERN int typecheckok;
EXTERN int compiling_runtime;
EXTERN int compiling_wrappers;
EXTERN int pure_go;
+EXTERN char* flag_installsuffix;
EXTERN int flag_race;
EXTERN int flag_largemodel;
EXTERN int noescape;
EXTERN int nointerface;
EXTERN int fieldtrack_enabled;
+EXTERN int precisestack_enabled;
/*
* y.tab.c
@@ -987,6 +1016,16 @@ Bits bor(Bits a, Bits b);
int bset(Bits a, uint n);
/*
+ * bv.c
+ */
+Bvec* bvalloc(int32 n);
+void bvset(Bvec *bv, int32 i);
+void bvres(Bvec *bv, int32 i);
+int bvget(Bvec *bv, int32 i);
+int bvisempty(Bvec *bv);
+int bvcmp(Bvec *bv1, Bvec *bv2);
+
+/*
* closure.c
*/
Node* closurebody(NodeList *body);
@@ -1042,7 +1081,7 @@ NodeList* constiter(NodeList *vl, Node *t, NodeList *cl);
Node* dclname(Sym *s);
void declare(Node *n, int ctxt);
void dumpdcl(char *st);
-Node* embedded(Sym *s);
+Node* embedded(Sym *s, Pkg *pkg);
Node* fakethis(void);
void funcbody(Node *n);
void funccompile(Node *n, int isclosure);
@@ -1420,20 +1459,22 @@ EXTERN Prog* firstpc;
EXTERN Prog* retpc;
EXTERN Node* nodfp;
+EXTERN int disable_checknil;
int anyregalloc(void);
void betypeinit(void);
void bgen(Node *n, int true, int likely, Prog *to);
-void checkref(Node *n, int force);
-void checknotnil(Node*, NodeList**);
+void checknil(Node*, NodeList**);
+void expandchecks(Prog*);
void cgen(Node*, Node*);
void cgen_asop(Node *n);
void cgen_call(Node *n, int proc);
void cgen_callinter(Node *n, Node *res, int proc);
+void cgen_checknil(Node*);
void cgen_ret(Node *n);
void clearfat(Node *n);
void compile(Node*);
-void defframe(Prog*);
+void defframe(Prog*, Bvec*);
int dgostringptr(Sym*, int off, char *str);
int dgostrlitptr(Sym*, int off, Strlit*);
int dstringptr(Sym *s, int off, char *str);
@@ -1445,11 +1486,11 @@ void fixautoused(Prog*);
void gdata(Node*, Node*, int);
void gdatacomplex(Node*, Mpcplx*);
void gdatastring(Node*, Strlit*);
-void genembedtramp(Type*, Type*, Sym*, int iface);
void ggloblnod(Node *nam);
void ggloblsym(Sym *s, int32 width, int dupok, int rodata);
Prog* gjmp(Prog*);
void gused(Node*);
+void movelarge(NodeList*);
int isfat(Type*);
void markautoused(Prog*);
Plist* newplist(void);
diff --git a/src/cmd/gc/go.y b/src/cmd/gc/go.y
index 29cd37008..5432eca85 100644
--- a/src/cmd/gc/go.y
+++ b/src/cmd/gc/go.y
@@ -134,8 +134,7 @@ package:
{
prevlineno = lineno;
yyerror("package statement must be first");
- flusherrors();
- mkpackage("main");
+ errorexit();
}
| LPACKAGE sym ';'
{
@@ -195,6 +194,10 @@ import_stmt:
importdot(ipkg, pack);
break;
}
+ if(strcmp(my->name, "init") == 0) {
+ yyerror("cannot import package as init - init must be a func");
+ break;
+ }
if(my->name[0] == '_' && my->name[1] == '\0')
break;
if(my->def) {
@@ -950,6 +953,14 @@ pexpr_no_paren:
{
$$ = nod(OSLICE, $1, nod(OKEY, $3, $5));
}
+| pexpr '[' oexpr ':' oexpr ':' oexpr ']'
+ {
+ if($5 == N)
+ yyerror("middle index required in 3-index slice");
+ if($7 == N)
+ yyerror("final index required in 3-index slice");
+ $$ = nod(OSLICE3, $1, nod(OKEY, $3, nod(OKEY, $5, $7)));
+ }
| pseudocall
| convtype '(' expr ocomma ')'
{
@@ -1115,6 +1126,19 @@ hidden_importsym:
}
$$ = pkglookup($4->name, p);
}
+| '@' LLITERAL '.' '?'
+ {
+ Pkg *p;
+
+ if($2.u.sval->len == 0)
+ p = importpkg;
+ else {
+ if(isbadimport($2.u.sval))
+ errorexit();
+ p = mkpkg($2.u.sval);
+ }
+ $$ = pkglookup("?", p);
+ }
name:
sym %prec NotParen
@@ -1520,12 +1544,12 @@ structdcl:
Node *n;
l = $1;
- if(l != nil && l->next == nil && l->n == nil) {
- // ? symbol, during import
+ if(l == nil) {
+ // ? symbol, during import (list1(N) == nil)
n = $2;
if(n->op == OIND)
n = n->left;
- n = embedded(n->sym);
+ n = embedded(n->sym, importpkg);
n->right = $2;
n->val = $3;
$$ = list1(n);
@@ -1596,7 +1620,7 @@ packname:
embed:
packname
{
- $$ = embedded($1);
+ $$ = embedded($1, localpkg);
}
interfacedcl:
@@ -2050,15 +2074,19 @@ hidden_structdcl:
sym hidden_type oliteral
{
Sym *s;
+ Pkg *p;
- if($1 != S) {
+ if($1 != S && strcmp($1->name, "?") != 0) {
$$ = nod(ODCLFIELD, newname($1), typenod($2));
$$->val = $3;
} else {
s = $2->sym;
if(s == S && isptr[$2->etype])
s = $2->type->sym;
- $$ = embedded(s);
+ p = importpkg;
+ if($1 != S)
+ p = $1->pkg;
+ $$ = embedded(s, p);
$$->right = typenod($2);
$$->val = $3;
}
diff --git a/src/cmd/gc/inl.c b/src/cmd/gc/inl.c
index 08b462e13..6800884a0 100644
--- a/src/cmd/gc/inl.c
+++ b/src/cmd/gc/inl.c
@@ -198,6 +198,7 @@ ishairy(Node *n, int *budget)
case ODEFER:
case ODCLTYPE: // can't print yet
case ODCLCONST: // can't print yet
+ case ORETJMP:
return 1;
break;
diff --git a/src/cmd/gc/lex.c b/src/cmd/gc/lex.c
index b7f71d553..8c739391a 100644
--- a/src/cmd/gc/lex.c
+++ b/src/cmd/gc/lex.c
@@ -41,6 +41,19 @@ static struct {
} exper[] = {
// {"rune32", &rune32},
{"fieldtrack", &fieldtrack_enabled},
+ {"precisestack", &precisestack_enabled},
+ {nil, nil},
+};
+
+// Debug arguments.
+// These can be specified with the -d flag, as in "-d checknil"
+// to set the debug_checknil variable. In general the list passed
+// to -d can be comma-separated.
+static struct {
+ char *name;
+ int *val;
+} debugtab[] = {
+ {"nil", &debug_checknil},
{nil, nil},
};
@@ -238,12 +251,13 @@ main(int argc, char *argv[])
flagfn0("V", "print compiler version", doversion);
flagcount("W", "debug parse tree after type checking", &debug['W']);
flagcount("complete", "compiling complete package (no C or assembly)", &pure_go);
- flagcount("d", "debug declarations", &debug['d']);
+ flagstr("d", "list: print debug information about items in list", &debugstr);
flagcount("e", "no limit on number of errors reported", &debug['e']);
flagcount("f", "debug stack frames", &debug['f']);
flagcount("g", "debug code generation", &debug['g']);
flagcount("h", "halt on error", &debug['h']);
flagcount("i", "debug line number stack", &debug['i']);
+ flagstr("installsuffix", "pkg directory suffix", &flag_installsuffix);
flagcount("j", "debug runtime-initialized variables", &debug['j']);
flagcount("l", "disable inlining", &debug['l']);
flagcount("m", "print optimization decisions", &debug['m']);
@@ -269,6 +283,24 @@ main(int argc, char *argv[])
racepkg = mkpkg(strlit("runtime/race"));
racepkg->name = "race";
}
+
+ // parse -d argument
+ if(debugstr) {
+ char *f[100];
+ int i, j, nf;
+
+ nf = getfields(debugstr, f, nelem(f), 1, ",");
+ for(i=0; i<nf; i++) {
+ for(j=0; debugtab[j].name != nil; j++) {
+ if(strcmp(debugtab[j].name, f[i]) == 0) {
+ *debugtab[j].val = 1;
+ break;
+ }
+ }
+ if(j == nelem(debugtab))
+ fatal("unknown debug information -d '%s'\n", f[i]);
+ }
+ }
// enable inlining. for now:
// default: inlining on. (debug['l'] == 1)
@@ -329,6 +361,8 @@ main(int argc, char *argv[])
curio.peekc = 0;
curio.peekc1 = 0;
curio.nlsemi = 0;
+ curio.eofnl = 0;
+ curio.last = 0;
// Skip initial BOM if present.
if(Bgetrune(curio.bin) != BOM)
@@ -416,6 +450,10 @@ main(int argc, char *argv[])
// Phase 5: Escape analysis.
if(!debug['N'])
escapes(xtop);
+
+ // Escape analysis moved escaped values off stack.
+ // Move large values off stack too.
+ movelarge(xtop);
// Phase 6: Compile top level functions.
for(l=xtop; l; l=l->next)
@@ -540,7 +578,7 @@ static int
findpkg(Strlit *name)
{
Idir *p;
- char *q, *race;
+ char *q, *suffix, *suffixsep;
if(islocalname(name)) {
if(safemode)
@@ -578,13 +616,19 @@ findpkg(Strlit *name)
return 1;
}
if(goroot != nil) {
- race = "";
- if(flag_race)
- race = "_race";
- snprint(namebuf, sizeof(namebuf), "%s/pkg/%s_%s%s/%Z.a", goroot, goos, goarch, race, name);
+ suffix = "";
+ suffixsep = "";
+ if(flag_installsuffix != nil) {
+ suffixsep = "_";
+ suffix = flag_installsuffix;
+ } else if(flag_race) {
+ suffixsep = "_";
+ suffix = "race";
+ }
+ snprint(namebuf, sizeof(namebuf), "%s/pkg/%s_%s%s%s/%Z.a", goroot, goos, goarch, suffixsep, suffix, name);
if(access(namebuf, 0) >= 0)
return 1;
- snprint(namebuf, sizeof(namebuf), "%s/pkg/%s_%s%s/%Z.%c", goroot, goos, goarch, race, name, thechar);
+ snprint(namebuf, sizeof(namebuf), "%s/pkg/%s_%s%s%s/%Z.%c", goroot, goos, goarch, suffixsep, suffix, name, thechar);
if(access(namebuf, 0) >= 0)
return 1;
}
@@ -1580,10 +1624,10 @@ getc(void)
curio.cp++;
} else {
loop:
- c = Bgetc(curio.bin);
+ c = BGETC(curio.bin);
if(c == 0xef) {
- c1 = Bgetc(curio.bin);
- c2 = Bgetc(curio.bin);
+ c1 = BGETC(curio.bin);
+ c2 = BGETC(curio.bin);
if(c1 == 0xbb && c2 == 0xbf) {
yyerrorl(lexlineno, "Unicode (UTF-8) BOM in middle of file");
goto loop;
@@ -1602,7 +1646,7 @@ check:
}
case EOF:
// insert \n at EOF
- if(curio.eofnl)
+ if(curio.eofnl || curio.last == '\n')
return EOF;
curio.eofnl = 1;
c = '\n';
@@ -1611,6 +1655,7 @@ check:
lexlineno++;
break;
}
+ curio.last = c;
return c;
}
@@ -1971,27 +2016,27 @@ lexinit1(void)
// error type
s = lookup("error");
s->lexical = LNAME;
- errortype = t;
- errortype->sym = s;
s1 = pkglookup("error", builtinpkg);
+ errortype = t;
+ errortype->sym = s1;
s1->lexical = LNAME;
s1->def = typenod(errortype);
// byte alias
s = lookup("byte");
s->lexical = LNAME;
- bytetype = typ(TUINT8);
- bytetype->sym = s;
s1 = pkglookup("byte", builtinpkg);
+ bytetype = typ(TUINT8);
+ bytetype->sym = s1;
s1->lexical = LNAME;
s1->def = typenod(bytetype);
// rune alias
s = lookup("rune");
s->lexical = LNAME;
- runetype = typ(TINT32);
- runetype->sym = s;
s1 = pkglookup("rune", builtinpkg);
+ runetype = typ(TINT32);
+ runetype->sym = s1;
s1->lexical = LNAME;
s1->def = typenod(runetype);
}
@@ -2242,6 +2287,28 @@ yytinit(void)
}
}
+static void
+pkgnotused(int lineno, Strlit *path, char *name)
+{
+ char *elem;
+
+ // If the package was imported with a name other than the final
+ // import path element, show it explicitly in the error message.
+ // Note that this handles both renamed imports and imports of
+ // packages containing unconventional package declarations.
+ // Note that this uses / always, even on Windows, because Go import
+ // paths always use forward slashes.
+ elem = strrchr(path->s, '/');
+ if(elem != nil)
+ elem++;
+ else
+ elem = path->s;
+ if(name == nil || strcmp(elem, name) == 0)
+ yyerrorl(lineno, "imported and not used: \"%Z\"", path);
+ else
+ yyerrorl(lineno, "imported and not used: \"%Z\" as %s", path, name);
+}
+
void
mkpackage(char* pkgname)
{
@@ -2267,7 +2334,7 @@ mkpackage(char* pkgname)
// errors if a conflicting top-level name is
// introduced by a different file.
if(!s->def->used && !nsyntaxerrors)
- yyerrorl(s->def->lineno, "imported and not used: \"%Z\"", s->def->pkg->path);
+ pkgnotused(s->def->lineno, s->def->pkg->path, s->name);
s->def = N;
continue;
}
@@ -2275,7 +2342,7 @@ mkpackage(char* pkgname)
// throw away top-level name left over
// from previous import . "x"
if(s->def->pack != N && !s->def->pack->used && !nsyntaxerrors) {
- yyerrorl(s->def->pack->lineno, "imported and not used: \"%Z\"", s->def->pack->pkg->path);
+ pkgnotused(s->def->pack->lineno, s->def->pack->pkg->path, nil);
s->def->pack->used = 1;
}
s->def = N;
diff --git a/src/cmd/gc/md5.c b/src/cmd/gc/md5.c
index 5856aab51..bbd4e298f 100644
--- a/src/cmd/gc/md5.c
+++ b/src/cmd/gc/md5.c
@@ -196,7 +196,7 @@ md5block(MD5 *dig, uchar *p, int nn)
for(i=0; i<16; i++) {
j = i*4;
- X[i] = p[j] | (p[j+1]<<8) | (p[j+2]<<16) | (p[j+3]<<24);
+ X[i] = p[j] | (p[j+1]<<8) | (p[j+2]<<16) | ((uint32)p[j+3]<<24);
}
// Round 1.
diff --git a/src/cmd/gc/mkbuiltin1.c b/src/cmd/gc/mkbuiltin1.c
index f8f61c278..69027fdf5 100644
--- a/src/cmd/gc/mkbuiltin1.c
+++ b/src/cmd/gc/mkbuiltin1.c
@@ -62,7 +62,7 @@ begin:
// sys.go claims to be in package PACKAGE to avoid
// conflicts during "6g sys.go". rename PACKAGE to $2.
printf("\t\"");
- while(q = strstr(p, "PACKAGE")) {
+ while((q = strstr(p, "PACKAGE")) != NULL) {
*q = 0;
esc(p); // up to the substitution
printf("%s", name); // the sub name
diff --git a/src/cmd/gc/mparith2.c b/src/cmd/gc/mparith2.c
index 8e52ff216..9b2f664f7 100644
--- a/src/cmd/gc/mparith2.c
+++ b/src/cmd/gc/mparith2.c
@@ -565,11 +565,11 @@ mpgetfix(Mpint *a)
return 0;
}
- v = (vlong)a->a[0];
- v |= (vlong)a->a[1] << Mpscale;
- v |= (vlong)a->a[2] << (Mpscale+Mpscale);
+ v = (uvlong)a->a[0];
+ v |= (uvlong)a->a[1] << Mpscale;
+ v |= (uvlong)a->a[2] << (Mpscale+Mpscale);
if(a->neg)
- v = -v;
+ v = -(uvlong)v;
return v;
}
@@ -586,7 +586,7 @@ mpmovecfix(Mpint *a, vlong c)
x = c;
if(x < 0) {
a->neg = 1;
- x = -x;
+ x = -(uvlong)x;
}
a1 = &a->a[0];
diff --git a/src/cmd/gc/obj.c b/src/cmd/gc/obj.c
index e4bcd1170..4cad08924 100644
--- a/src/cmd/gc/obj.c
+++ b/src/cmd/gc/obj.c
@@ -87,7 +87,7 @@ void
Bputname(Biobuf *b, Sym *s)
{
Bprint(b, "%s", s->pkg->prefix);
- Bputc(b, '.');
+ BPUTC(b, '.');
Bwrite(b, s->name, strlen(s->name)+1);
}
@@ -124,7 +124,7 @@ outwinname(Biobuf *b, Hist *h, char *ds, char *p)
outzfile(b, p+1);
} else {
// relative name
- if(h->offset == 0 && pathname && pathname[1] == ':') {
+ if(h->offset >= 0 && pathname && pathname[1] == ':') {
if(tolowerrune(ds[0]) == tolowerrune(pathname[0])) {
// using current drive
zfile(b, pathname, 3); // leading "c:/"
@@ -235,7 +235,7 @@ ieeedtod(uint64 *ieee, double native)
return;
}
fr = frexp(native, &exp);
- f = 2097152L; /* shouldnt use fp constants here */
+ f = 2097152L; /* shouldn't use fp constants here */
fr = modf(fr*f, &ho);
h = ho;
h &= 0xfffffL;
diff --git a/src/cmd/gc/order.c b/src/cmd/gc/order.c
index 499a4e746..7552510e9 100644
--- a/src/cmd/gc/order.c
+++ b/src/cmd/gc/order.c
@@ -218,6 +218,7 @@ orderstmt(Node *n, NodeList **out)
case_OFALL:
case OGOTO:
case OLABEL:
+ case ORETJMP:
// Special: n->left is not an expression; save as is.
*out = list(*out, n);
break;
@@ -263,7 +264,7 @@ orderstmt(Node *n, NodeList **out)
ordercallargs(&n->list, out);
*out = list(*out, n);
break;
-
+
case OSELECT:
for(l=n->list; l; l=l->next) {
if(l->n->op != OXCASE)
diff --git a/src/cmd/gc/pgen.c b/src/cmd/gc/pgen.c
index 82d8186b0..2850af6bb 100644
--- a/src/cmd/gc/pgen.c
+++ b/src/cmd/gc/pgen.c
@@ -2,24 +2,36 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// "Portable" code generation.
+// Compiled separately for 5g, 6g, and 8g, so allowed to use gg.h, opt.h.
+// Must code to the intersection of the three back ends.
+
#include <u.h>
#include <libc.h>
#include "gg.h"
#include "opt.h"
+#include "../../pkg/runtime/funcdata.h"
+
+enum { BitsPerPointer = 2 };
static void allocauto(Prog* p);
+static void dumpgcargs(Node*, Sym*);
+static Bvec* dumpgclocals(Node*, Sym*);
void
compile(Node *fn)
{
+ Bvec *bv;
Plist *pl;
- Node nod1, *n;
- Prog *plocals, *ptxt, *p, *p1;
+ Node nod1, *n, *gcargsnod, *gclocalsnod;
+ Prog *ptxt, *p, *p1;
int32 lno;
Type *t;
Iter save;
vlong oldstksize;
NodeList *l;
+ Sym *gcargssym, *gclocalssym;
+ static int ngcargs, ngclocals;
if(newproc == N) {
newproc = sysfunc("newproc");
@@ -83,12 +95,37 @@ compile(Node *fn)
nodconst(&nod1, types[TINT32], 0);
ptxt = gins(ATEXT, isblank(curfn->nname) ? N : curfn->nname, &nod1);
if(fn->dupok)
- ptxt->TEXTFLAG = DUPOK;
+ ptxt->TEXTFLAG |= DUPOK;
+ if(fn->wrapper)
+ ptxt->TEXTFLAG |= WRAPPER;
+
+ // Clumsy but important.
+ // See test/recover.go for test cases and src/pkg/reflect/value.go
+ // for the actual functions being considered.
+ if(myimportpath != nil && strcmp(myimportpath, "reflect") == 0) {
+ if(strcmp(curfn->nname->sym->name, "callReflect") == 0 || strcmp(curfn->nname->sym->name, "callMethod") == 0)
+ ptxt->TEXTFLAG |= WRAPPER;
+ }
+
afunclit(&ptxt->from, curfn->nname);
ginit();
- plocals = gins(ALOCALS, N, N);
+ snprint(namebuf, sizeof namebuf, "gcargs·%d", ngcargs++);
+ gcargssym = lookup(namebuf);
+ gcargsnod = newname(gcargssym);
+ gcargsnod->class = PEXTERN;
+
+ nodconst(&nod1, types[TINT32], FUNCDATA_GCArgs);
+ gins(AFUNCDATA, &nod1, gcargsnod);
+
+ snprint(namebuf, sizeof(namebuf), "gclocals·%d", ngclocals++);
+ gclocalssym = lookup(namebuf);
+ gclocalsnod = newname(gclocalssym);
+ gclocalsnod->class = PEXTERN;
+
+ nodconst(&nod1, types[TINT32], FUNCDATA_GCLocals);
+ gins(AFUNCDATA, &nod1, gclocalsnod);
for(t=curfn->paramfld; t; t=t->down)
gtrack(tracksym(t->type));
@@ -144,22 +181,28 @@ compile(Node *fn)
if(!debug['N'] || debug['R'] || debug['P']) {
regopt(ptxt);
+ nilopt(ptxt);
}
+ expandchecks(ptxt);
oldstksize = stksize;
allocauto(ptxt);
- plocals->to.type = D_CONST;
- plocals->to.offset = stksize;
-
if(0)
print("allocauto: %lld to %lld\n", oldstksize, (vlong)stksize);
setlineno(curfn);
- if((int64)stksize+maxarg > (1ULL<<31))
+ if((int64)stksize+maxarg > (1ULL<<31)) {
yyerror("stack frame too large (>2GB)");
+ goto ret;
+ }
+
+ // Emit garbage collection symbols.
+ dumpgcargs(fn, gcargssym);
+ bv = dumpgclocals(curfn, gclocalssym);
- defframe(ptxt);
+ defframe(ptxt, bv);
+ free(bv);
if(0)
frame(0);
@@ -168,26 +211,211 @@ ret:
lineno = lno;
}
+static void
+walktype1(Type *t, vlong *xoffset, Bvec *bv)
+{
+ vlong fieldoffset, i, o;
+ Type *t1;
+
+ if(t->align > 0 && (*xoffset % t->align) != 0)
+ fatal("walktype1: invalid initial alignment, %T", t);
+
+ switch(t->etype) {
+ case TINT8:
+ case TUINT8:
+ case TINT16:
+ case TUINT16:
+ case TINT32:
+ case TUINT32:
+ case TINT64:
+ case TUINT64:
+ case TINT:
+ case TUINT:
+ case TUINTPTR:
+ case TBOOL:
+ case TFLOAT32:
+ case TFLOAT64:
+ case TCOMPLEX64:
+ case TCOMPLEX128:
+ *xoffset += t->width;
+ break;
+
+ case TPTR32:
+ case TPTR64:
+ case TUNSAFEPTR:
+ case TFUNC:
+ case TCHAN:
+ case TMAP:
+ if(*xoffset % widthptr != 0)
+ fatal("walktype1: invalid alignment, %T", t);
+ bvset(bv, (*xoffset / widthptr) * BitsPerPointer);
+ *xoffset += t->width;
+ break;
+
+ case TSTRING:
+ // struct { byte *str; intgo len; }
+ if(*xoffset % widthptr != 0)
+ fatal("walktype1: invalid alignment, %T", t);
+ bvset(bv, (*xoffset / widthptr) * BitsPerPointer);
+ *xoffset += t->width;
+ break;
+
+ case TINTER:
+ // struct { Itab* tab; union { void* ptr, uintptr val } data; }
+ // or, when isnilinter(t)==true:
+ // struct { Type* type; union { void* ptr, uintptr val } data; }
+ if(*xoffset % widthptr != 0)
+ fatal("walktype1: invalid alignment, %T", t);
+ bvset(bv, ((*xoffset / widthptr) * BitsPerPointer) + 1);
+ if(isnilinter(t))
+ bvset(bv, ((*xoffset / widthptr) * BitsPerPointer));
+ *xoffset += t->width;
+ break;
+
+ case TARRAY:
+ // The value of t->bound is -1 for slices types and >0 for
+ // for fixed array types. All other values are invalid.
+ if(t->bound < -1)
+ fatal("walktype1: invalid bound, %T", t);
+ if(isslice(t)) {
+ // struct { byte* array; uintgo len; uintgo cap; }
+ if(*xoffset % widthptr != 0)
+ fatal("walktype1: invalid TARRAY alignment, %T", t);
+ bvset(bv, (*xoffset / widthptr) * BitsPerPointer);
+ *xoffset += t->width;
+ } else if(!haspointers(t->type))
+ *xoffset += t->width;
+ else
+ for(i = 0; i < t->bound; ++i)
+ walktype1(t->type, xoffset, bv);
+ break;
+
+ case TSTRUCT:
+ o = 0;
+ for(t1 = t->type; t1 != T; t1 = t1->down) {
+ fieldoffset = t1->width;
+ *xoffset += fieldoffset - o;
+ walktype1(t1->type, xoffset, bv);
+ o = fieldoffset + t1->type->width;
+ }
+ *xoffset += t->width - o;
+ break;
+
+ default:
+ fatal("walktype1: unexpected type, %T", t);
+ }
+}
+
+static void
+walktype(Type *type, Bvec *bv)
+{
+ vlong xoffset;
+
+ // Start the walk at offset 0. The correct offset will be
+ // filled in by the first type encountered during the walk.
+ xoffset = 0;
+ walktype1(type, &xoffset, bv);
+}
+
+// Compute a bit vector to describe the pointer-containing locations
+// in the in and out argument list and dump the bitvector length and
+// data to the provided symbol.
+static void
+dumpgcargs(Node *fn, Sym *sym)
+{
+ Type *thistype, *inargtype, *outargtype;
+ Bvec *bv;
+ int32 i;
+ int off;
+
+ thistype = getthisx(fn->type);
+ inargtype = getinargx(fn->type);
+ outargtype = getoutargx(fn->type);
+ bv = bvalloc((fn->type->argwid / widthptr) * BitsPerPointer);
+ if(thistype != nil)
+ walktype(thistype, bv);
+ if(inargtype != nil)
+ walktype(inargtype, bv);
+ if(outargtype != nil)
+ walktype(outargtype, bv);
+ off = duint32(sym, 0, bv->n);
+ for(i = 0; i < bv->n; i += 32)
+ off = duint32(sym, off, bv->b[i/32]);
+ free(bv);
+ ggloblsym(sym, off, 0, 1);
+}
-// Sort the list of stack variables. autos after anything else,
-// within autos, unused after used, and within used on reverse alignment.
-// non-autos sort on offset.
+// Compute a bit vector to describe the pointer-containing locations
+// in local variables and dump the bitvector length and data out to
+// the provided symbol. Return the vector for use and freeing by caller.
+static Bvec*
+dumpgclocals(Node* fn, Sym *sym)
+{
+ Bvec *bv;
+ NodeList *ll;
+ Node *node;
+ vlong xoffset;
+ int32 i;
+ int off;
+
+ bv = bvalloc((stkptrsize / widthptr) * BitsPerPointer);
+ for(ll = fn->dcl; ll != nil; ll = ll->next) {
+ node = ll->n;
+ if(node->class == PAUTO && node->op == ONAME) {
+ if(haspointers(node->type)) {
+ xoffset = node->xoffset + stkptrsize;
+ walktype1(node->type, &xoffset, bv);
+ }
+ }
+ }
+ off = duint32(sym, 0, bv->n);
+ for(i = 0; i < bv->n; i += 32) {
+ off = duint32(sym, off, bv->b[i/32]);
+ }
+ ggloblsym(sym, off, 0, 1);
+ return bv;
+}
+
+// Sort the list of stack variables. Autos after anything else,
+// within autos, unused after used, within used, things with
+// pointers first, zeroed things first, and then decreasing size.
+// Because autos are laid out in decreasing addresses
+// on the stack, pointers first, zeroed things first and decreasing size
+// really means, in memory, things with pointers needing zeroing at
+// the top of the stack and increasing in size.
+// Non-autos sort on offset.
static int
cmpstackvar(Node *a, Node *b)
{
+ int ap, bp;
+
if (a->class != b->class)
- return (a->class == PAUTO) ? 1 : -1;
+ return (a->class == PAUTO) ? +1 : -1;
if (a->class != PAUTO) {
if (a->xoffset < b->xoffset)
return -1;
if (a->xoffset > b->xoffset)
- return 1;
+ return +1;
return 0;
}
if ((a->used == 0) != (b->used == 0))
return b->used - a->used;
- return b->type->align - a->type->align;
+ ap = haspointers(a->type);
+ bp = haspointers(b->type);
+ if(ap != bp)
+ return bp - ap;
+
+ ap = a->needzero;
+ bp = b->needzero;
+ if(ap != bp)
+ return bp - ap;
+
+ if(a->type->width < b->type->width)
+ return +1;
+ if(a->type->width > b->type->width)
+ return -1;
+ return 0;
}
// TODO(lvd) find out where the PAUTO/OLITERAL nodes come from.
@@ -198,6 +426,10 @@ allocauto(Prog* ptxt)
Node* n;
vlong w;
+ stksize = 0;
+ stkptrsize = 0;
+ stkzerosize = 0;
+
if(curfn->dcl == nil)
return;
@@ -208,6 +440,13 @@ allocauto(Prog* ptxt)
markautoused(ptxt);
+ if(precisestack_enabled) {
+ // TODO: Remove when liveness analysis sets needzero instead.
+ for(ll=curfn->dcl; ll != nil; ll=ll->next)
+ if(ll->n->class == PAUTO)
+ ll->n->needzero = 1; // ll->n->addrtaken;
+ }
+
listsort(&curfn->dcl, cmpstackvar);
// Unused autos are at the end, chop 'em off.
@@ -216,7 +455,6 @@ allocauto(Prog* ptxt)
if (n->class == PAUTO && n->op == ONAME && !n->used) {
// No locals used at all
curfn->dcl = nil;
- stksize = 0;
fixautoused(ptxt);
return;
}
@@ -231,7 +469,6 @@ allocauto(Prog* ptxt)
}
// Reassign stack offsets of the locals that are still there.
- stksize = 0;
for(ll = curfn->dcl; ll != nil; ll=ll->next) {
n = ll->n;
if (n->class != PAUTO || n->op != ONAME)
@@ -243,6 +480,11 @@ allocauto(Prog* ptxt)
fatal("bad width");
stksize += w;
stksize = rnd(stksize, n->type->align);
+ if(haspointers(n->type)) {
+ stkptrsize = stksize;
+ if(n->needzero)
+ stkzerosize = stksize;
+ }
if(thechar == '5')
stksize = rnd(stksize, widthptr);
if(stksize >= (1ULL<<31)) {
@@ -251,14 +493,62 @@ allocauto(Prog* ptxt)
}
n->stkdelta = -stksize - n->xoffset;
}
+ stksize = rnd(stksize, widthptr);
+ stkptrsize = rnd(stkptrsize, widthptr);
+ stkzerosize = rnd(stkzerosize, widthptr);
fixautoused(ptxt);
// The debug information needs accurate offsets on the symbols.
- for(ll = curfn->dcl ;ll != nil; ll=ll->next) {
+ for(ll = curfn->dcl; ll != nil; ll=ll->next) {
if (ll->n->class != PAUTO || ll->n->op != ONAME)
continue;
ll->n->xoffset += ll->n->stkdelta;
ll->n->stkdelta = 0;
}
}
+
+static void movelargefn(Node*);
+
+void
+movelarge(NodeList *l)
+{
+ for(; l; l=l->next)
+ if(l->n->op == ODCLFUNC)
+ movelargefn(l->n);
+}
+
+static void
+movelargefn(Node *fn)
+{
+ NodeList *l;
+ Node *n;
+
+ for(l=fn->dcl; l != nil; l=l->next) {
+ n = l->n;
+ if(n->class == PAUTO && n->type != T && n->type->width > MaxStackVarSize)
+ addrescapes(n);
+ }
+}
+
+void
+cgen_checknil(Node *n)
+{
+ Node reg;
+
+ if(disable_checknil)
+ return;
+ // Ideally we wouldn't see any TUINTPTR here, but we do.
+ if(n->type == T || (!isptr[n->type->etype] && n->type->etype != TUINTPTR && n->type->etype != TUNSAFEPTR)) {
+ dump("checknil", n);
+ fatal("bad checknil");
+ }
+ if((thechar == '5' && n->op != OREGISTER) || !n->addable) {
+ regalloc(&reg, types[tptr], n);
+ cgen(n, &reg);
+ gins(ACHECKNIL, &reg, N);
+ regfree(&reg);
+ return;
+ }
+ gins(ACHECKNIL, n, N);
+}
diff --git a/src/cmd/gc/popt.c b/src/cmd/gc/popt.c
new file mode 100644
index 000000000..8d7afa011
--- /dev/null
+++ b/src/cmd/gc/popt.c
@@ -0,0 +1,948 @@
+// Derived from Inferno utils/6c/reg.c
+// http://code.google.com/p/inferno-os/source/browse/utils/6c/reg.c
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+// "Portable" optimizations.
+// Compiled separately for 5g, 6g, and 8g, so allowed to use gg.h, opt.h.
+// Must code to the intersection of the three back ends.
+
+#include <u.h>
+#include <libc.h>
+#include "gg.h"
+#include "opt.h"
+
+// p is a call instruction. Does the call fail to return?
+int
+noreturn(Prog *p)
+{
+ Sym *s;
+ int i;
+ static Sym* symlist[10];
+
+ if(symlist[0] == S) {
+ symlist[0] = pkglookup("panicindex", runtimepkg);
+ symlist[1] = pkglookup("panicslice", runtimepkg);
+ symlist[2] = pkglookup("throwinit", runtimepkg);
+ symlist[3] = pkglookup("panic", runtimepkg);
+ symlist[4] = pkglookup("panicwrap", runtimepkg);
+ }
+
+ s = p->to.sym;
+ if(s == S)
+ return 0;
+ for(i=0; symlist[i]!=S; i++)
+ if(s == symlist[i])
+ return 1;
+ return 0;
+}
+
+// JMP chasing and removal.
+//
+// The code generator depends on being able to write out jump
+// instructions that it can jump to now but fill in later.
+// the linker will resolve them nicely, but they make the code
+// longer and more difficult to follow during debugging.
+// Remove them.
+
+/* what instruction does a JMP to p eventually land on? */
+static Prog*
+chasejmp(Prog *p, int *jmploop)
+{
+ int n;
+
+ n = 0;
+ while(p != P && p->as == AJMP && p->to.type == D_BRANCH) {
+ if(++n > 10) {
+ *jmploop = 1;
+ break;
+ }
+ p = p->to.u.branch;
+ }
+ return p;
+}
+
+/*
+ * reuse reg pointer for mark/sweep state.
+ * leave reg==nil at end because alive==nil.
+ */
+#define alive ((void*)0)
+#define dead ((void*)1)
+
+/* mark all code reachable from firstp as alive */
+static void
+mark(Prog *firstp)
+{
+ Prog *p;
+
+ for(p=firstp; p; p=p->link) {
+ if(p->opt != dead)
+ break;
+ p->opt = alive;
+ if(p->as != ACALL && p->to.type == D_BRANCH && p->to.u.branch)
+ mark(p->to.u.branch);
+ if(p->as == AJMP || p->as == ARET || p->as == AUNDEF)
+ break;
+ }
+}
+
+void
+fixjmp(Prog *firstp)
+{
+ int jmploop;
+ Prog *p, *last;
+
+ if(debug['R'] && debug['v'])
+ print("\nfixjmp\n");
+
+ // pass 1: resolve jump to jump, mark all code as dead.
+ jmploop = 0;
+ for(p=firstp; p; p=p->link) {
+ if(debug['R'] && debug['v'])
+ print("%P\n", p);
+ if(p->as != ACALL && p->to.type == D_BRANCH && p->to.u.branch && p->to.u.branch->as == AJMP) {
+ p->to.u.branch = chasejmp(p->to.u.branch, &jmploop);
+ if(debug['R'] && debug['v'])
+ print("->%P\n", p);
+ }
+ p->opt = dead;
+ }
+ if(debug['R'] && debug['v'])
+ print("\n");
+
+ // pass 2: mark all reachable code alive
+ mark(firstp);
+
+ // pass 3: delete dead code (mostly JMPs).
+ last = nil;
+ for(p=firstp; p; p=p->link) {
+ if(p->opt == dead) {
+ if(p->link == P && p->as == ARET && last && last->as != ARET) {
+ // This is the final ARET, and the code so far doesn't have one.
+ // Let it stay.
+ } else {
+ if(debug['R'] && debug['v'])
+ print("del %P\n", p);
+ continue;
+ }
+ }
+ if(last)
+ last->link = p;
+ last = p;
+ }
+ last->link = P;
+
+ // pass 4: elide JMP to next instruction.
+ // only safe if there are no jumps to JMPs anymore.
+ if(!jmploop) {
+ last = nil;
+ for(p=firstp; p; p=p->link) {
+ if(p->as == AJMP && p->to.type == D_BRANCH && p->to.u.branch == p->link) {
+ if(debug['R'] && debug['v'])
+ print("del %P\n", p);
+ continue;
+ }
+ if(last)
+ last->link = p;
+ last = p;
+ }
+ last->link = P;
+ }
+
+ if(debug['R'] && debug['v']) {
+ print("\n");
+ for(p=firstp; p; p=p->link)
+ print("%P\n", p);
+ print("\n");
+ }
+}
+
+#undef alive
+#undef dead
+
+// Control flow analysis. The Flow structures hold predecessor and successor
+// information as well as basic loop analysis.
+//
+// graph = flowstart(firstp, sizeof(Flow));
+// ... use flow graph ...
+// flowend(graph); // free graph
+//
+// Typical uses of the flow graph are to iterate over all the flow-relevant instructions:
+//
+// for(f = graph->start; f != nil; f = f->link)
+//
+// or, given an instruction f, to iterate over all the predecessors, which is
+// f->p1 and this list:
+//
+// for(f2 = f->p2; f2 != nil; f2 = f2->p2link)
+//
+// Often the Flow struct is embedded as the first field inside a larger struct S.
+// In that case casts are needed to convert Flow* to S* in many places but the
+// idea is the same. Pass sizeof(S) instead of sizeof(Flow) to flowstart.
+
+Graph*
+flowstart(Prog *firstp, int size)
+{
+ int nf;
+ Flow *f, *f1, *start, *last;
+ Graph *graph;
+ Prog *p;
+ ProgInfo info;
+
+ // Count and mark instructions to annotate.
+ nf = 0;
+ for(p = firstp; p != P; p = p->link) {
+ p->opt = nil; // should be already, but just in case
+ proginfo(&info, p);
+ if(info.flags & Skip)
+ continue;
+ p->opt = (void*)1;
+ nf++;
+ }
+
+ if(nf == 0)
+ return nil;
+
+ if(nf >= 20000) {
+ // fatal("%S is too big (%d instructions)", curfn->nname->sym, nf);
+ return nil;
+ }
+
+ // Allocate annotations and assign to instructions.
+ graph = calloc(sizeof *graph + size*nf, 1);
+ if(graph == nil)
+ fatal("out of memory");
+ start = (Flow*)(graph+1);
+ last = nil;
+ f = start;
+ for(p = firstp; p != P; p = p->link) {
+ if(p->opt == nil)
+ continue;
+ p->opt = f;
+ f->prog = p;
+ if(last)
+ last->link = f;
+ last = f;
+
+ f = (Flow*)((uchar*)f + size);
+ }
+
+ // Fill in pred/succ information.
+ for(f = start; f != nil; f = f->link) {
+ p = f->prog;
+ proginfo(&info, p);
+ if(!(info.flags & Break)) {
+ f1 = f->link;
+ f->s1 = f1;
+ f1->p1 = f;
+ }
+ if(p->to.type == D_BRANCH) {
+ if(p->to.u.branch == P)
+ fatal("pnil %P", p);
+ f1 = p->to.u.branch->opt;
+ if(f1 == nil)
+ fatal("fnil %P / %P", p, p->to.u.branch);
+ if(f1 == f) {
+ //fatal("self loop %P", p);
+ continue;
+ }
+ f->s2 = f1;
+ f->p2link = f1->p2;
+ f1->p2 = f;
+ }
+ }
+
+ graph->start = start;
+ graph->num = nf;
+ return graph;
+}
+
+void
+flowend(Graph *graph)
+{
+ Flow *f;
+
+ for(f = graph->start; f != nil; f = f->link)
+ f->prog->opt = nil;
+ free(graph);
+}
+
+/*
+ * find looping structure
+ *
+ * 1) find reverse postordering
+ * 2) find approximate dominators,
+ * the actual dominators if the flow graph is reducible
+ * otherwise, dominators plus some other non-dominators.
+ * See Matthew S. Hecht and Jeffrey D. Ullman,
+ * "Analysis of a Simple Algorithm for Global Data Flow Problems",
+ * Conf. Record of ACM Symp. on Principles of Prog. Langs, Boston, Massachusetts,
+ * Oct. 1-3, 1973, pp. 207-217.
+ * 3) find all nodes with a predecessor dominated by the current node.
+ * such a node is a loop head.
+ * recursively, all preds with a greater rpo number are in the loop
+ */
+static int32
+postorder(Flow *r, Flow **rpo2r, int32 n)
+{
+ Flow *r1;
+
+ r->rpo = 1;
+ r1 = r->s1;
+ if(r1 && !r1->rpo)
+ n = postorder(r1, rpo2r, n);
+ r1 = r->s2;
+ if(r1 && !r1->rpo)
+ n = postorder(r1, rpo2r, n);
+ rpo2r[n] = r;
+ n++;
+ return n;
+}
+
+static int32
+rpolca(int32 *idom, int32 rpo1, int32 rpo2)
+{
+ int32 t;
+
+ if(rpo1 == -1)
+ return rpo2;
+ while(rpo1 != rpo2){
+ if(rpo1 > rpo2){
+ t = rpo2;
+ rpo2 = rpo1;
+ rpo1 = t;
+ }
+ while(rpo1 < rpo2){
+ t = idom[rpo2];
+ if(t >= rpo2)
+ fatal("bad idom");
+ rpo2 = t;
+ }
+ }
+ return rpo1;
+}
+
+static int
+doms(int32 *idom, int32 r, int32 s)
+{
+ while(s > r)
+ s = idom[s];
+ return s == r;
+}
+
+static int
+loophead(int32 *idom, Flow *r)
+{
+ int32 src;
+
+ src = r->rpo;
+ if(r->p1 != nil && doms(idom, src, r->p1->rpo))
+ return 1;
+ for(r = r->p2; r != nil; r = r->p2link)
+ if(doms(idom, src, r->rpo))
+ return 1;
+ return 0;
+}
+
+static void
+loopmark(Flow **rpo2r, int32 head, Flow *r)
+{
+ if(r->rpo < head || r->active == head)
+ return;
+ r->active = head;
+ r->loop += LOOP;
+ if(r->p1 != nil)
+ loopmark(rpo2r, head, r->p1);
+ for(r = r->p2; r != nil; r = r->p2link)
+ loopmark(rpo2r, head, r);
+}
+
+void
+flowrpo(Graph *g)
+{
+ Flow *r1;
+ int32 i, d, me, nr, *idom;
+ Flow **rpo2r;
+
+ free(g->rpo);
+ g->rpo = calloc(g->num*sizeof g->rpo[0], 1);
+ idom = calloc(g->num*sizeof idom[0], 1);
+ if(g->rpo == nil || idom == nil)
+ fatal("out of memory");
+
+ for(r1 = g->start; r1 != nil; r1 = r1->link)
+ r1->active = 0;
+
+ rpo2r = g->rpo;
+ d = postorder(g->start, rpo2r, 0);
+ nr = g->num;
+ if(d > nr)
+ fatal("too many reg nodes %d %d", d, nr);
+ nr = d;
+ for(i = 0; i < nr / 2; i++) {
+ r1 = rpo2r[i];
+ rpo2r[i] = rpo2r[nr - 1 - i];
+ rpo2r[nr - 1 - i] = r1;
+ }
+ for(i = 0; i < nr; i++)
+ rpo2r[i]->rpo = i;
+
+ idom[0] = 0;
+ for(i = 0; i < nr; i++) {
+ r1 = rpo2r[i];
+ me = r1->rpo;
+ d = -1;
+ // rpo2r[r->rpo] == r protects against considering dead code,
+ // which has r->rpo == 0.
+ if(r1->p1 != nil && rpo2r[r1->p1->rpo] == r1->p1 && r1->p1->rpo < me)
+ d = r1->p1->rpo;
+ for(r1 = r1->p2; r1 != nil; r1 = r1->p2link)
+ if(rpo2r[r1->rpo] == r1 && r1->rpo < me)
+ d = rpolca(idom, d, r1->rpo);
+ idom[i] = d;
+ }
+
+ for(i = 0; i < nr; i++) {
+ r1 = rpo2r[i];
+ r1->loop++;
+ if(r1->p2 != nil && loophead(idom, r1))
+ loopmark(rpo2r, i, r1);
+ }
+ free(idom);
+
+ for(r1 = g->start; r1 != nil; r1 = r1->link)
+ r1->active = 0;
+}
+
+Flow*
+uniqp(Flow *r)
+{
+ Flow *r1;
+
+ r1 = r->p1;
+ if(r1 == nil) {
+ r1 = r->p2;
+ if(r1 == nil || r1->p2link != nil)
+ return nil;
+ } else
+ if(r->p2 != nil)
+ return nil;
+ return r1;
+}
+
+Flow*
+uniqs(Flow *r)
+{
+ Flow *r1;
+
+ r1 = r->s1;
+ if(r1 == nil) {
+ r1 = r->s2;
+ if(r1 == nil)
+ return nil;
+ } else
+ if(r->s2 != nil)
+ return nil;
+ return r1;
+}
+
+// The compilers assume they can generate temporary variables
+// as needed to preserve the right semantics or simplify code
+// generation and the back end will still generate good code.
+// This results in a large number of ephemeral temporary variables.
+// Merge temps with non-overlapping lifetimes and equal types using the
+// greedy algorithm in Poletto and Sarkar, "Linear Scan Register Allocation",
+// ACM TOPLAS 1999.
+
+typedef struct TempVar TempVar;
+typedef struct TempFlow TempFlow;
+
+struct TempVar
+{
+ Node *node;
+ TempFlow *def; // definition of temp var
+ TempFlow *use; // use list, chained through TempFlow.uselink
+ TempVar *freelink; // next free temp in Type.opt list
+ TempVar *merge; // merge var with this one
+ uint32 start; // smallest Prog.loc in live range
+ uint32 end; // largest Prog.loc in live range
+ uchar addr; // address taken - no accurate end
+ uchar removed; // removed from program
+};
+
+struct TempFlow
+{
+ Flow f;
+ TempFlow *uselink;
+};
+
+static int
+startcmp(const void *va, const void *vb)
+{
+ TempVar *a, *b;
+
+ a = *(TempVar**)va;
+ b = *(TempVar**)vb;
+
+ if(a->start < b->start)
+ return -1;
+ if(a->start > b->start)
+ return +1;
+ return 0;
+}
+
+// Is n available for merging?
+static int
+canmerge(Node *n)
+{
+ return n->class == PAUTO && !n->addrtaken && strncmp(n->sym->name, "autotmp", 7) == 0;
+}
+
+static void mergewalk(TempVar*, TempFlow*, uint32);
+
+void
+mergetemp(Prog *firstp)
+{
+ int i, j, nvar, ninuse, nfree, nkill;
+ TempVar *var, *v, *v1, **bystart, **inuse;
+ TempFlow *r;
+ NodeList *l, **lp;
+ Node *n;
+ Prog *p, *p1;
+ Type *t;
+ ProgInfo info, info1;
+ int32 gen;
+ Graph *g;
+
+ enum { Debug = 0 };
+
+ g = flowstart(firstp, sizeof(TempFlow));
+ if(g == nil)
+ return;
+
+ // Build list of all mergeable variables.
+ nvar = 0;
+ for(l = curfn->dcl; l != nil; l = l->next)
+ if(canmerge(l->n))
+ nvar++;
+
+ var = calloc(nvar*sizeof var[0], 1);
+ nvar = 0;
+ for(l = curfn->dcl; l != nil; l = l->next) {
+ n = l->n;
+ if(canmerge(n)) {
+ v = &var[nvar++];
+ n->opt = v;
+ v->node = n;
+ }
+ }
+
+ // Build list of uses.
+ // We assume that the earliest reference to a temporary is its definition.
+ // This is not true of variables in general but our temporaries are all
+ // single-use (that's why we have so many!).
+ for(r = (TempFlow*)g->start; r != nil; r = (TempFlow*)r->f.link) {
+ p = r->f.prog;
+ proginfo(&info, p);
+
+ if(p->from.node != N && p->from.node->opt && p->to.node != N && p->to.node->opt)
+ fatal("double node %P", p);
+ if((n = p->from.node) != N && (v = n->opt) != nil ||
+ (n = p->to.node) != N && (v = n->opt) != nil) {
+ if(v->def == nil)
+ v->def = r;
+ r->uselink = v->use;
+ v->use = r;
+ if(n == p->from.node && (info.flags & LeftAddr))
+ v->addr = 1;
+ }
+ }
+
+ if(Debug > 1)
+ dumpit("before", g->start, 0);
+
+ nkill = 0;
+
+ // Special case.
+ for(v = var; v < var+nvar; v++) {
+ if(v->addr)
+ continue;
+ // Used in only one instruction, which had better be a write.
+ if((r = v->use) != nil && r->uselink == nil) {
+ p = r->f.prog;
+ proginfo(&info, p);
+ if(p->to.node == v->node && (info.flags & RightWrite) && !(info.flags & RightRead)) {
+ p->as = ANOP;
+ p->to = zprog.to;
+ v->removed = 1;
+ if(Debug)
+ print("drop write-only %S\n", v->node->sym);
+ } else
+ fatal("temp used and not set: %P", p);
+ nkill++;
+ continue;
+ }
+
+ // Written in one instruction, read in the next, otherwise unused,
+ // no jumps to the next instruction. Happens mainly in 386 compiler.
+ if((r = v->use) != nil && r->f.link == &r->uselink->f && r->uselink->uselink == nil && uniqp(r->f.link) == &r->f) {
+ p = r->f.prog;
+ proginfo(&info, p);
+ p1 = r->f.link->prog;
+ proginfo(&info1, p1);
+ enum {
+ SizeAny = SizeB | SizeW | SizeL | SizeQ | SizeF | SizeD,
+ };
+ if(p->from.node == v->node && p1->to.node == v->node && (info.flags & Move) &&
+ !((info.flags|info1.flags) & (LeftAddr|RightAddr)) &&
+ (info.flags & SizeAny) == (info1.flags & SizeAny)) {
+ p1->from = p->from;
+ excise(&r->f);
+ v->removed = 1;
+ if(Debug)
+ print("drop immediate-use %S\n", v->node->sym);
+ }
+ nkill++;
+ continue;
+ }
+ }
+
+ // Traverse live range of each variable to set start, end.
+ // Each flood uses a new value of gen so that we don't have
+ // to clear all the r->f.active words after each variable.
+ gen = 0;
+ for(v = var; v < var+nvar; v++) {
+ gen++;
+ for(r = v->use; r != nil; r = r->uselink)
+ mergewalk(v, r, gen);
+ }
+
+ // Sort variables by start.
+ bystart = malloc(nvar*sizeof bystart[0]);
+ for(i=0; i<nvar; i++)
+ bystart[i] = &var[i];
+ qsort(bystart, nvar, sizeof bystart[0], startcmp);
+
+ // List of in-use variables, sorted by end, so that the ones that
+ // will last the longest are the earliest ones in the array.
+ // The tail inuse[nfree:] holds no-longer-used variables.
+ // In theory we should use a sorted tree so that insertions are
+ // guaranteed O(log n) and then the loop is guaranteed O(n log n).
+ // In practice, it doesn't really matter.
+ inuse = malloc(nvar*sizeof inuse[0]);
+ ninuse = 0;
+ nfree = nvar;
+ for(i=0; i<nvar; i++) {
+ v = bystart[i];
+ if(v->addr || v->removed)
+ continue;
+
+ // Expire no longer in use.
+ while(ninuse > 0 && inuse[ninuse-1]->end < v->start) {
+ v1 = inuse[--ninuse];
+ inuse[--nfree] = v1;
+ }
+
+ // Find old temp to reuse if possible.
+ t = v->node->type;
+ for(j=nfree; j<nvar; j++) {
+ v1 = inuse[j];
+ if(eqtype(t, v1->node->type)) {
+ inuse[j] = inuse[nfree++];
+ if(v1->merge)
+ v->merge = v1->merge;
+ else
+ v->merge = v1;
+ nkill++;
+ break;
+ }
+ }
+
+ // Sort v into inuse.
+ j = ninuse++;
+ while(j > 0 && inuse[j-1]->end < v->end) {
+ inuse[j] = inuse[j-1];
+ j--;
+ }
+ inuse[j] = v;
+ }
+
+ if(Debug) {
+ print("%S [%d - %d]\n", curfn->nname->sym, nvar, nkill);
+ for(v=var; v<var+nvar; v++) {
+ print("var %#N %T %d-%d", v->node, v->node->type, v->start, v->end);
+ if(v->addr)
+ print(" addr=1");
+ if(v->removed)
+ print(" dead=1");
+ if(v->merge)
+ print(" merge %#N", v->merge->node);
+ if(v->start == v->end)
+ print(" %P", v->def->f.prog);
+ print("\n");
+ }
+
+ if(Debug > 1)
+ dumpit("after", g->start, 0);
+ }
+
+ // Update node references to use merged temporaries.
+ for(r = (TempFlow*)g->start; r != nil; r = (TempFlow*)r->f.link) {
+ p = r->f.prog;
+ if((n = p->from.node) != N && (v = n->opt) != nil && v->merge != nil)
+ p->from.node = v->merge->node;
+ if((n = p->to.node) != N && (v = n->opt) != nil && v->merge != nil)
+ p->to.node = v->merge->node;
+ }
+
+ // Delete merged nodes from declaration list.
+ for(lp = &curfn->dcl; (l = *lp); ) {
+ curfn->dcl->end = l;
+ n = l->n;
+ v = n->opt;
+ if(v && (v->merge || v->removed)) {
+ *lp = l->next;
+ continue;
+ }
+ lp = &l->next;
+ }
+
+ // Clear aux structures.
+ for(v=var; v<var+nvar; v++)
+ v->node->opt = nil;
+ free(var);
+ free(bystart);
+ free(inuse);
+ flowend(g);
+}
+
+static void
+mergewalk(TempVar *v, TempFlow *r0, uint32 gen)
+{
+ Prog *p;
+ TempFlow *r1, *r, *r2;
+
+ for(r1 = r0; r1 != nil; r1 = (TempFlow*)r1->f.p1) {
+ if(r1->f.active == gen)
+ break;
+ r1->f.active = gen;
+ p = r1->f.prog;
+ if(v->end < p->loc)
+ v->end = p->loc;
+ if(r1 == v->def) {
+ v->start = p->loc;
+ break;
+ }
+ }
+
+ for(r = r0; r != r1; r = (TempFlow*)r->f.p1)
+ for(r2 = (TempFlow*)r->f.p2; r2 != nil; r2 = (TempFlow*)r2->f.p2link)
+ mergewalk(v, r2, gen);
+}
+
+// Eliminate redundant nil pointer checks.
+//
+// The code generation pass emits a CHECKNIL for every possibly nil pointer.
+// This pass removes a CHECKNIL if every predecessor path has already
+// checked this value for nil.
+//
+// Simple backwards flood from check to definition.
+// Run prog loop backward from end of program to beginning to avoid quadratic
+// behavior removing a run of checks.
+//
+// Assume that stack variables with address not taken can be loaded multiple times
+// from memory without being rechecked. Other variables need to be checked on
+// each load.
+
+typedef struct NilVar NilVar;
+typedef struct NilFlow NilFlow;
+
+struct NilFlow {
+ Flow f;
+ int kill;
+};
+
+static void nilwalkback(NilFlow *rcheck);
+static void nilwalkfwd(NilFlow *rcheck);
+
+void
+nilopt(Prog *firstp)
+{
+ NilFlow *r;
+ Prog *p;
+ Graph *g;
+ int ncheck, nkill;
+
+ g = flowstart(firstp, sizeof(NilFlow));
+ if(g == nil)
+ return;
+
+ if(debug_checknil > 1 /* || strcmp(curfn->nname->sym->name, "f1") == 0 */)
+ dumpit("nilopt", g->start, 0);
+
+ ncheck = 0;
+ nkill = 0;
+ for(r = (NilFlow*)g->start; r != nil; r = (NilFlow*)r->f.link) {
+ p = r->f.prog;
+ if(p->as != ACHECKNIL || !regtyp(&p->from))
+ continue;
+ ncheck++;
+ if(stackaddr(&p->from)) {
+ if(debug_checknil && p->lineno > 1)
+ warnl(p->lineno, "removed nil check of SP address");
+ r->kill = 1;
+ continue;
+ }
+ nilwalkfwd(r);
+ if(r->kill) {
+ if(debug_checknil && p->lineno > 1)
+ warnl(p->lineno, "removed nil check before indirect");
+ continue;
+ }
+ nilwalkback(r);
+ if(r->kill) {
+ if(debug_checknil && p->lineno > 1)
+ warnl(p->lineno, "removed repeated nil check");
+ continue;
+ }
+ }
+
+ for(r = (NilFlow*)g->start; r != nil; r = (NilFlow*)r->f.link) {
+ if(r->kill) {
+ nkill++;
+ excise(&r->f);
+ }
+ }
+
+ flowend(g);
+
+ if(debug_checknil > 1)
+ print("%S: removed %d of %d nil checks\n", curfn->nname->sym, nkill, ncheck);
+}
+
+static void
+nilwalkback(NilFlow *rcheck)
+{
+ Prog *p;
+ ProgInfo info;
+ NilFlow *r;
+
+ for(r = rcheck; r != nil; r = (NilFlow*)uniqp(&r->f)) {
+ p = r->f.prog;
+ proginfo(&info, p);
+ if((info.flags & RightWrite) && sameaddr(&p->to, &rcheck->f.prog->from)) {
+ // Found initialization of value we're checking for nil.
+ // without first finding the check, so this one is unchecked.
+ return;
+ }
+ if(r != rcheck && p->as == ACHECKNIL && sameaddr(&p->from, &rcheck->f.prog->from)) {
+ rcheck->kill = 1;
+ return;
+ }
+ }
+
+ // Here is a more complex version that scans backward across branches.
+ // It assumes rcheck->kill = 1 has been set on entry, and its job is to find a reason
+ // to keep the check (setting rcheck->kill = 0).
+ // It doesn't handle copying of aggregates as well as I would like,
+ // nor variables with their address taken,
+ // and it's too subtle to turn on this late in Go 1.2. Perhaps for Go 1.3.
+ /*
+ for(r1 = r0; r1 != nil; r1 = (NilFlow*)r1->f.p1) {
+ if(r1->f.active == gen)
+ break;
+ r1->f.active = gen;
+ p = r1->f.prog;
+
+ // If same check, stop this loop but still check
+ // alternate predecessors up to this point.
+ if(r1 != rcheck && p->as == ACHECKNIL && sameaddr(&p->from, &rcheck->f.prog->from))
+ break;
+
+ proginfo(&info, p);
+ if((info.flags & RightWrite) && sameaddr(&p->to, &rcheck->f.prog->from)) {
+ // Found initialization of value we're checking for nil.
+ // without first finding the check, so this one is unchecked.
+ rcheck->kill = 0;
+ return;
+ }
+
+ if(r1->f.p1 == nil && r1->f.p2 == nil) {
+ print("lost pred for %P\n", rcheck->f.prog);
+ for(r1=r0; r1!=nil; r1=(NilFlow*)r1->f.p1) {
+ proginfo(&info, r1->f.prog);
+ print("\t%P %d %d %D %D\n", r1->f.prog, info.flags&RightWrite, sameaddr(&r1->f.prog->to, &rcheck->f.prog->from), &r1->f.prog->to, &rcheck->f.prog->from);
+ }
+ fatal("lost pred trail");
+ }
+ }
+
+ for(r = r0; r != r1; r = (NilFlow*)r->f.p1)
+ for(r2 = (NilFlow*)r->f.p2; r2 != nil; r2 = (NilFlow*)r2->f.p2link)
+ nilwalkback(rcheck, r2, gen);
+ */
+}
+
+static void
+nilwalkfwd(NilFlow *rcheck)
+{
+ NilFlow *r;
+ Prog *p;
+ ProgInfo info;
+
+ // If the path down from rcheck dereferences the address
+ // (possibly with a small offset) before writing to memory
+ // and before any subsequent checks, it's okay to wait for
+ // that implicit check. Only consider this basic block to
+ // avoid problems like:
+ // _ = *x // should panic
+ // for {} // no writes but infinite loop may be considered visible
+ for(r = (NilFlow*)uniqs(&rcheck->f); r != nil; r = (NilFlow*)uniqs(&r->f)) {
+ p = r->f.prog;
+ proginfo(&info, p);
+
+ if((info.flags & LeftRead) && smallindir(&p->from, &rcheck->f.prog->from)) {
+ rcheck->kill = 1;
+ return;
+ }
+ if((info.flags & (RightRead|RightWrite)) && smallindir(&p->to, &rcheck->f.prog->from)) {
+ rcheck->kill = 1;
+ return;
+ }
+
+ // Stop if another nil check happens.
+ if(p->as == ACHECKNIL)
+ return;
+ // Stop if value is lost.
+ if((info.flags & RightWrite) && sameaddr(&p->to, &rcheck->f.prog->from))
+ return;
+ // Stop if memory write.
+ if((info.flags & RightWrite) && !regtyp(&p->to))
+ return;
+ }
+}
diff --git a/src/cmd/gc/popt.h b/src/cmd/gc/popt.h
new file mode 100644
index 000000000..8d5dfff1a
--- /dev/null
+++ b/src/cmd/gc/popt.h
@@ -0,0 +1,46 @@
+// Copyright 2013 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.
+
+typedef struct Flow Flow;
+typedef struct Graph Graph;
+
+struct Flow {
+ Prog* prog; // actual instruction
+ Flow* p1; // predecessors of this instruction: p1,
+ Flow* p2; // and then p2 linked though p2link.
+ Flow* p2link;
+ Flow* s1; // successors of this instruction (at most two: s1 and s2).
+ Flow* s2;
+ Flow* link; // next instruction in function code
+
+ int32 active; // usable by client
+
+ int32 rpo; // reverse post ordering
+ uint16 loop; // x5 for every loop
+ uchar refset; // diagnostic generated
+};
+
+struct Graph
+{
+ Flow* start;
+ int num;
+
+ // After calling flowrpo, rpo lists the flow nodes in reverse postorder,
+ // and each non-dead Flow node f has g->rpo[f->rpo] == f.
+ Flow** rpo;
+};
+
+void fixjmp(Prog*);
+Graph* flowstart(Prog*, int);
+void flowrpo(Graph*);
+void flowend(Graph*);
+void mergetemp(Prog*);
+void nilopt(Prog*);
+int noreturn(Prog*);
+int regtyp(Addr*);
+int sameaddr(Addr*, Addr*);
+int smallindir(Addr*, Addr*);
+int stackaddr(Addr*);
+Flow* uniqp(Flow*);
+Flow* uniqs(Flow*);
diff --git a/src/cmd/gc/racewalk.c b/src/cmd/gc/racewalk.c
index 5d4f62e76..d6a5b3cce 100644
--- a/src/cmd/gc/racewalk.c
+++ b/src/cmd/gc/racewalk.c
@@ -23,6 +23,7 @@ static void racewalklist(NodeList *l, NodeList **init);
static void racewalknode(Node **np, NodeList **init, int wr, int skip);
static int callinstr(Node **n, NodeList **init, int wr, int skip);
static Node* uintptraddr(Node *n);
+static void makeaddable(Node *n);
static Node* basenod(Node *n);
static void foreach(Node *n, void(*f)(Node*, void*), void *c);
static void hascallspred(Node *n, void *c);
@@ -50,6 +51,18 @@ ispkgin(const char **pkgs, int n)
return 0;
}
+static int
+isforkfunc(Node *fn)
+{
+ // Special case for syscall.forkAndExecInChild.
+ // 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.
+ // Race instrumentation does all of the above.
+ return myimportpath != nil && strcmp(myimportpath, "syscall") == 0 &&
+ strcmp(fn->nname->sym->name, "forkAndExecInChild") == 0;
+}
+
void
racewalk(Node *fn)
{
@@ -57,7 +70,7 @@ racewalk(Node *fn)
Node *nodpc;
char s[1024];
- if(ispkgin(omit_pkgs, nelem(omit_pkgs)))
+ if(ispkgin(omit_pkgs, nelem(omit_pkgs)) || isforkfunc(fn))
return;
if(!ispkgin(noinst_pkgs, nelem(noinst_pkgs))) {
@@ -121,8 +134,20 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)
if(debug['w'] > 1)
dump("racewalk-before", n);
setlineno(n);
- if(init == nil || init == &n->ninit)
+ if(init == nil)
fatal("racewalk: bad init list");
+ if(init == &n->ninit) {
+ // If init == &n->ninit and n->ninit is non-nil,
+ // racewalknode might append it to itself.
+ // nil it out and handle it separately before putting it back.
+ l = n->ninit;
+ n->ninit = nil;
+ racewalklist(l, nil);
+ racewalknode(&n, &l, wr, skip); // recurse with nil n->ninit
+ appendinit(&n, l);
+ *np = n;
+ return;
+ }
racewalklist(n->ninit, nil);
@@ -213,6 +238,7 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)
callinstr(&n, init, wr, skip);
goto ret;
+ case OSPTR:
case OLEN:
case OCAP:
racewalknode(&n->left, init, 0, 0);
@@ -254,9 +280,7 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)
// side effects are safe.
// n->right may not be executed,
// so instrumentation goes to n->right->ninit, not init.
- l = nil;
- racewalknode(&n->right, &l, wr, 0);
- appendinit(&n->right, l);
+ racewalknode(&n->right, &n->right->ninit, wr, 0);
goto ret;
case ONAME:
@@ -293,6 +317,8 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)
case OSLICE:
case OSLICEARR:
+ case OSLICE3:
+ case OSLICE3ARR:
// Seems to only lead to double instrumentation.
//racewalknode(&n->left, init, 0, 0);
goto ret;
@@ -310,10 +336,6 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)
racewalknode(&n->left, init, 0, 0);
goto ret;
- case OTYPESW:
- racewalknode(&n->right, init, 0, 0);
- goto ret;
-
// should not appear in AST by now
case OSEND:
case ORECV:
@@ -363,6 +385,7 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)
case OIF:
case OCALLMETH:
case ORETURN:
+ case ORETJMP:
case OSWITCH:
case OSELECT:
case OEMPTY:
@@ -376,7 +399,7 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)
// does not require instrumentation
case OPRINT: // don't bother instrumenting it
case OPRINTN: // don't bother instrumenting it
- case OCHECKNOTNIL: // always followed by a read.
+ case OCHECKNIL: // always followed by a read.
case OPARAM: // it appears only in fn->exit to copy heap params back
case OCLOSUREVAR:// immutable pointer to captured variable
case ODOTMETH: // either part of CALLMETH or CALLPART (lowered to PTRLIT)
@@ -388,18 +411,15 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)
case ONONAME:
case OLITERAL:
case OSLICESTR: // always preceded by bounds checking, avoid double instrumentation.
+ case OTYPESW: // ignored by code generation, do not instrument.
goto ret;
}
ret:
if(n->op != OBLOCK) // OBLOCK is handled above in a special way.
racewalklist(n->list, init);
- l = nil;
- racewalknode(&n->ntest, &l, 0, 0);
- n->ninit = concat(n->ninit, l);
- l = nil;
- racewalknode(&n->nincr, &l, 0, 0);
- n->ninit = concat(n->ninit, l);
+ racewalknode(&n->ntest, &n->ntest->ninit, 0, 0);
+ racewalknode(&n->nincr, &n->nincr->ninit, 0, 0);
racewalklist(n->nbody, nil);
racewalklist(n->nelse, nil);
racewalklist(n->rlist, nil);
@@ -431,8 +451,8 @@ static int
callinstr(Node **np, NodeList **init, int wr, int skip)
{
Node *f, *b, *n;
- Type *t, *t1;
- int class, res, hascalls;
+ Type *t;
+ int class, hascalls;
n = *np;
//print("callinstr for %+N [ %O ] etype=%E class=%d\n",
@@ -443,33 +463,6 @@ callinstr(Node **np, NodeList **init, int wr, int skip)
t = n->type;
if(isartificial(n))
return 0;
- if(t->etype == TSTRUCT) {
- // TODO: instrument arrays similarly.
- // PARAMs w/o PHEAP are not interesting.
- if(n->class == PPARAM || n->class == PPARAMOUT)
- return 0;
- res = 0;
- hascalls = 0;
- foreach(n, hascallspred, &hascalls);
- if(hascalls) {
- n = detachexpr(n, init);
- *np = n;
- }
- for(t1=t->type; t1; t1=t1->down) {
- if(t1->sym && strcmp(t1->sym->name, "_")) {
- n = treecopy(n);
- f = nod(OXDOT, n, newname(t1->sym));
- f->type = t1;
- if(f->type->etype == TFIELD)
- f->type = f->type->type;
- if(callinstr(&f, init, wr, 0)) {
- typecheck(&f, Erv);
- res = 1;
- }
- }
- }
- return res;
- }
b = basenod(n);
// it skips e.g. stores to ... parameter array
@@ -489,19 +482,56 @@ callinstr(Node **np, NodeList **init, int wr, int skip)
*np = n;
}
n = treecopy(n);
- f = mkcall(wr ? "racewrite" : "raceread", T, init, uintptraddr(n));
+ makeaddable(n);
+ if(t->etype == TSTRUCT || isfixedarray(t)) {
+ f = mkcall(wr ? "racewriterange" : "racereadrange", T, init, uintptraddr(n),
+ nodintconst(t->width));
+ } else
+ f = mkcall(wr ? "racewrite" : "raceread", T, init, uintptraddr(n));
*init = list(*init, f);
return 1;
}
return 0;
}
+// makeaddable returns a node whose memory location is the
+// same as n, but which is addressable in the Go language
+// sense.
+// This is different from functions like cheapexpr that may make
+// a copy of their argument.
+static void
+makeaddable(Node *n)
+{
+ // The arguments to uintptraddr technically have an address but
+ // may not be addressable in the Go sense: for example, in the case
+ // of T(v).Field where T is a struct type and v is
+ // an addressable value.
+ switch(n->op) {
+ case OINDEX:
+ if(isfixedarray(n->left->type))
+ makeaddable(n->left);
+ break;
+ case ODOT:
+ case OXDOT:
+ // Turn T(v).Field into v.Field
+ if(n->left->op == OCONVNOP)
+ n->left = n->left->left;
+ makeaddable(n->left);
+ break;
+ case ODOTPTR:
+ default:
+ // nothing to do
+ break;
+ }
+}
+
static Node*
uintptraddr(Node *n)
{
Node *r;
r = nod(OADDR, n, N);
+ r->bounded = 1;
r = conv(r, types[TUNSAFEPTR]);
r = conv(r, types[TUINTPTR]);
return r;
diff --git a/src/cmd/gc/range.c b/src/cmd/gc/range.c
index 8af45b9d2..bd271da38 100644
--- a/src/cmd/gc/range.c
+++ b/src/cmd/gc/range.c
@@ -129,6 +129,9 @@ walkrange(Node *n)
v2 = N;
if(n->list->next)
v2 = n->list->next->n;
+ // n->list has no meaning anymore, clear it
+ // to avoid erroneous processing by racewalk.
+ n->list = nil;
hv2 = N;
if(v2 == N && t->etype == TARRAY) {
diff --git a/src/cmd/gc/reflect.c b/src/cmd/gc/reflect.c
index fc182b03e..0a8aa8d7a 100644
--- a/src/cmd/gc/reflect.c
+++ b/src/cmd/gc/reflect.c
@@ -101,6 +101,138 @@ lsort(Sig *l, int(*f)(Sig*, Sig*))
return l;
}
+// Builds a type respresenting a Bucket structure for
+// the given map type. This type is not visible to users -
+// we include only enough information to generate a correct GC
+// program for it.
+// Make sure this stays in sync with ../../pkg/runtime/hashmap.c!
+enum {
+ BUCKETSIZE = 8,
+ MAXKEYSIZE = 128,
+ MAXVALSIZE = 128,
+};
+
+static Type*
+mapbucket(Type *t)
+{
+ Type *keytype, *valtype;
+ Type *bucket;
+ Type *overflowfield, *keysfield, *valuesfield;
+ int32 offset;
+
+ if(t->bucket != T)
+ return t->bucket;
+
+ keytype = t->down;
+ valtype = t->type;
+ if(keytype->width > MAXKEYSIZE)
+ keytype = ptrto(keytype);
+ if(valtype->width > MAXVALSIZE)
+ valtype = ptrto(valtype);
+
+ bucket = typ(TSTRUCT);
+ bucket->noalg = 1;
+
+ // The first field is: uint8 topbits[BUCKETSIZE].
+ // We don't need to encode it as GC doesn't care about it.
+ offset = BUCKETSIZE * 1;
+
+ overflowfield = typ(TFIELD);
+ overflowfield->type = ptrto(bucket);
+ overflowfield->width = offset; // "width" is offset in structure
+ overflowfield->sym = mal(sizeof(Sym)); // not important but needs to be set to give this type a name
+ overflowfield->sym->name = "overflow";
+ offset += widthptr;
+
+ keysfield = typ(TFIELD);
+ keysfield->type = typ(TARRAY);
+ keysfield->type->type = keytype;
+ keysfield->type->bound = BUCKETSIZE;
+ keysfield->type->width = BUCKETSIZE * keytype->width;
+ keysfield->width = offset;
+ keysfield->sym = mal(sizeof(Sym));
+ keysfield->sym->name = "keys";
+ offset += BUCKETSIZE * keytype->width;
+
+ valuesfield = typ(TFIELD);
+ valuesfield->type = typ(TARRAY);
+ valuesfield->type->type = valtype;
+ valuesfield->type->bound = BUCKETSIZE;
+ valuesfield->type->width = BUCKETSIZE * valtype->width;
+ valuesfield->width = offset;
+ valuesfield->sym = mal(sizeof(Sym));
+ valuesfield->sym->name = "values";
+ offset += BUCKETSIZE * valtype->width;
+
+ // link up fields
+ bucket->type = overflowfield;
+ overflowfield->down = keysfield;
+ keysfield->down = valuesfield;
+ valuesfield->down = T;
+
+ bucket->width = offset;
+ bucket->local = t->local;
+ t->bucket = bucket;
+ return bucket;
+}
+
+// Builds a type respresenting a Hmap structure for
+// the given map type. This type is not visible to users -
+// we include only enough information to generate a correct GC
+// program for it.
+// Make sure this stays in sync with ../../pkg/runtime/hashmap.c!
+static Type*
+hmap(Type *t)
+{
+ Type *h, *bucket;
+ Type *bucketsfield, *oldbucketsfield;
+ int32 offset;
+
+ if(t->hmap != T)
+ return t->hmap;
+
+ bucket = mapbucket(t);
+ h = typ(TSTRUCT);
+ h->noalg = 1;
+
+ offset = widthint; // count
+ offset += 4; // flags
+ offset += 4; // hash0
+ offset += 1; // B
+ offset += 1; // keysize
+ offset += 1; // valuesize
+ offset = (offset + 1) / 2 * 2;
+ offset += 2; // bucketsize
+ offset = (offset + widthptr - 1) / widthptr * widthptr;
+
+ bucketsfield = typ(TFIELD);
+ bucketsfield->type = ptrto(bucket);
+ bucketsfield->width = offset;
+ bucketsfield->sym = mal(sizeof(Sym));
+ bucketsfield->sym->name = "buckets";
+ offset += widthptr;
+
+ oldbucketsfield = typ(TFIELD);
+ oldbucketsfield->type = ptrto(bucket);
+ oldbucketsfield->width = offset;
+ oldbucketsfield->sym = mal(sizeof(Sym));
+ oldbucketsfield->sym->name = "oldbuckets";
+ offset += widthptr;
+
+ offset += widthptr; // nevacuate (last field in Hmap)
+
+ // link up fields
+ h->type = bucketsfield;
+ bucketsfield->down = oldbucketsfield;
+ oldbucketsfield->down = T;
+
+ h->width = offset;
+ h->local = t->local;
+ t->hmap = h;
+ h->hmap = t;
+ return h;
+}
+
/*
* f is method type, with receiver.
* return function type, receiver as first argument (or not).
@@ -208,16 +340,8 @@ methods(Type *t)
if(!(a->isym->flags & SymSiggen)) {
a->isym->flags |= SymSiggen;
if(!eqtype(this, it) || this->width < types[tptr]->width) {
- // Is okay to call genwrapper here always,
- // but we can generate more efficient code
- // using genembedtramp if all that is necessary
- // is a pointer adjustment and a JMP.
compiling_wrappers = 1;
- if(isptr[it->etype] && isptr[this->etype]
- && f->embedded && !isifacemethod(f->type))
- genembedtramp(it, f, a->isym, 1);
- else
- genwrapper(it, f, a->isym, 1);
+ genwrapper(it, f, a->isym, 1);
compiling_wrappers = 0;
}
}
@@ -226,11 +350,7 @@ methods(Type *t)
a->tsym->flags |= SymSiggen;
if(!eqtype(this, t)) {
compiling_wrappers = 1;
- if(isptr[t->etype] && isptr[this->etype]
- && f->embedded && !isifacemethod(f->type))
- genembedtramp(t, f, a->tsym, 0);
- else
- genwrapper(t, f, a->tsym, 0);
+ genwrapper(t, f, a->tsym, 0);
compiling_wrappers = 0;
}
}
@@ -277,8 +397,8 @@ imethods(Type *t)
last = a;
// Compiler can only refer to wrappers for
- // named interface types.
- if(t->sym == S)
+ // named interface types and non-blank methods.
+ if(t->sym == S || isblanksym(method))
continue;
// NOTE(rsc): Perhaps an oversight that
@@ -471,6 +591,10 @@ int
haspointers(Type *t)
{
Type *t1;
+ int ret;
+
+ if(t->haspointers != 0)
+ return t->haspointers - 1;
switch(t->etype) {
case TINT:
@@ -486,17 +610,27 @@ haspointers(Type *t)
case TUINTPTR:
case TFLOAT32:
case TFLOAT64:
+ case TCOMPLEX64:
+ case TCOMPLEX128:
case TBOOL:
- return 0;
+ ret = 0;
+ break;
case TARRAY:
- if(t->bound < 0) // slice
- return 1;
- return haspointers(t->type);
+ if(t->bound < 0) { // slice
+ ret = 1;
+ break;
+ }
+ ret = haspointers(t->type);
+ break;
case TSTRUCT:
- for(t1=t->type; t1!=T; t1=t1->down)
- if(haspointers(t1->type))
- return 1;
- return 0;
+ ret = 0;
+ for(t1=t->type; t1!=T; t1=t1->down) {
+ if(haspointers(t1->type)) {
+ ret = 1;
+ break;
+ }
+ }
+ break;
case TSTRING:
case TPTR32:
case TPTR64:
@@ -506,8 +640,12 @@ haspointers(Type *t)
case TMAP:
case TFUNC:
default:
- return 1;
+ ret = 1;
+ break;
}
+
+ t->haspointers = 1+ret;
+ return ret;
}
/*
@@ -709,7 +847,7 @@ static Sym*
dtypesym(Type *t)
{
int ot, xt, n, isddd, dupok;
- Sym *s, *s1, *s2, *slink;
+ Sym *s, *s1, *s2, *s3, *s4, *slink;
Sig *a, *m;
Type *t1, *tbase, *t2;
@@ -849,10 +987,14 @@ ok:
// ../../pkg/runtime/type.go:/MapType
s1 = dtypesym(t->down);
s2 = dtypesym(t->type);
+ s3 = dtypesym(mapbucket(t));
+ s4 = dtypesym(hmap(t));
ot = dcommontype(s, ot, t);
xt = ot - 2*widthptr;
ot = dsymptr(s, ot, s1, 0);
ot = dsymptr(s, ot, s2, 0);
+ ot = dsymptr(s, ot, s3, 0);
+ ot = dsymptr(s, ot, s4, 0);
break;
case TPTR32:
@@ -1026,7 +1168,8 @@ dalgsym(Type *t)
}
static int
-gcinline(Type *t) {
+gcinline(Type *t)
+{
switch(t->etype) {
case TARRAY:
if(t->bound == 1)
@@ -1111,9 +1254,9 @@ dgcsym1(Sym *s, int ot, Type *t, vlong *off, int stack_size)
// NOTE: Any changes here need to be made to reflect.MapOf as well.
if(*off % widthptr != 0)
fatal("dgcsym1: invalid alignment, %T", t);
- ot = duintptr(s, ot, GC_MAP_PTR);
+ ot = duintptr(s, ot, GC_PTR);
ot = duintptr(s, ot, *off);
- ot = dsymptr(s, ot, dtypesym(t), 0);
+ ot = dsymptr(s, ot, dgcsym(hmap(t)), 0);
*off += t->width;
break;
diff --git a/src/cmd/gc/runtime.go b/src/cmd/gc/runtime.go
index 2139a95d9..c8d57ab33 100644
--- a/src/cmd/gc/runtime.go
+++ b/src/cmd/gc/runtime.go
@@ -39,11 +39,6 @@ func goprintf()
// filled in by compiler: int n, string, string, ...
func concatstring()
-// filled in by compiler: Type*, int n, Slice, ...
-func append()
-func appendslice(typ *byte, x any, y []any) any
-func appendstr(typ *byte, x []byte, y string) []byte
-
func cmpstring(string, string) int
func eqstring(string, string) bool
func intstring(int64) string
@@ -124,6 +119,7 @@ func block()
func makeslice(typ *byte, nel int64, cap int64) (ary []any)
func growslice(typ *byte, old []any, n int64) (ary []any)
+func memmove(to *any, frm *any, length uintptr)
func memequal(eq *bool, size uintptr, x, y *any)
func memequal8(eq *bool, size uintptr, x, y *any)
@@ -149,3 +145,5 @@ func racefuncenter(uintptr)
func racefuncexit()
func raceread(uintptr)
func racewrite(uintptr)
+func racereadrange(addr, size uintptr)
+func racewriterange(addr, size uintptr)
diff --git a/src/cmd/gc/sinit.c b/src/cmd/gc/sinit.c
index 51c5f7022..446b1110a 100644
--- a/src/cmd/gc/sinit.c
+++ b/src/cmd/gc/sinit.c
@@ -25,10 +25,13 @@ static void init2list(NodeList*, NodeList**);
static int staticinit(Node*, NodeList**);
static Node *staticname(Type*, int);
+// init1 walks the AST starting at n, and accumulates in out
+// the list of definitions needing init code in dependency order.
static void
init1(Node *n, NodeList **out)
{
NodeList *l;
+ Node *nv;
if(n == N)
return;
@@ -61,9 +64,29 @@ init1(Node *n, NodeList **out)
if(n->initorder == InitDone)
return;
if(n->initorder == InitPending) {
- if(n->class == PFUNC)
- return;
+ // Since mutually recursive sets of functions are allowed,
+ // we don't necessarily raise an error if n depends on a node
+ // which is already waiting for its dependencies to be visited.
+ //
+ // initlist contains a cycle of identifiers referring to each other.
+ // If this cycle contains a variable, then this variable refers to itself.
+ // Conversely, if there exists an initialization cycle involving
+ // a variable in the program, the tree walk will reach a cycle
+ // involving that variable.
+ if(n->class != PFUNC) {
+ nv = n;
+ goto foundinitloop;
+ }
+ for(l=initlist; l->n!=n; l=l->next) {
+ if(l->n->class != PFUNC) {
+ nv = l->n;
+ goto foundinitloop;
+ }
+ }
+ // The loop involves only functions, ok.
+ return;
+ foundinitloop:
// if there have already been errors printed,
// those errors probably confused us and
// there might not be a loop. let the user
@@ -72,17 +95,26 @@ init1(Node *n, NodeList **out)
if(nerrors > 0)
errorexit();
- print("%L: initialization loop:\n", n->lineno);
- for(l=initlist;; l=l->next) {
- if(l->next == nil)
- break;
- l->next->end = l;
- }
+ // There is a loop involving nv. We know about
+ // n and initlist = n1 <- ... <- nv <- ... <- n <- ...
+ print("%L: initialization loop:\n", nv->lineno);
+ // Build back pointers in initlist.
+ for(l=initlist; l; l=l->next)
+ if(l->next != nil)
+ l->next->end = l;
+ // Print nv -> ... -> n1 -> n.
+ for(l=initlist; l->n!=nv; l=l->next);
for(; l; l=l->end)
print("\t%L %S refers to\n", l->n->lineno, l->n->sym);
- print("\t%L %S\n", n->lineno, n->sym);
+ // Print n -> ... -> nv.
+ for(l=initlist; l->n!=n; l=l->next);
+ for(; l->n != nv; l=l->end)
+ print("\t%L %S refers to\n", l->n->lineno, l->n->sym);
+ print("\t%L %S\n", nv->lineno, nv->sym);
errorexit();
}
+
+ // reached a new unvisited node.
n->initorder = InitPending;
l = malloc(sizeof *l);
if(l == nil) {
@@ -116,31 +148,16 @@ init1(Node *n, NodeList **out)
break;
}
- /*
- n->defn->dodata = 1;
- init1(n->defn->right, out);
+ init2(n->defn->right, out);
if(debug['j'])
print("%S\n", n->sym);
- *out = list(*out, n->defn);
- break;
- */
- if(1) {
- init2(n->defn->right, out);
- if(debug['j'])
- print("%S\n", n->sym);
- if(isblank(n) || !staticinit(n, out)) {
- if(debug['%']) dump("nonstatic", n->defn);
- *out = list(*out, n->defn);
- }
- } else if(0) {
- n->defn->dodata = 1;
- init1(n->defn->right, out);
- if(debug['j'])
- print("%S\n", n->sym);
+ if(isblank(n) || !staticinit(n, out)) {
+ if(debug['%'])
+ dump("nonstatic", n->defn);
*out = list(*out, n->defn);
}
break;
-
+
case OAS2FUNC:
case OAS2MAPR:
case OAS2DOTTYPE:
@@ -220,6 +237,9 @@ initreorder(NodeList *l, NodeList **out)
}
}
+// initfix computes initialization order for a list l of top-level
+// declarations and outputs the corresponding list of statements
+// to include in the init() function body.
NodeList*
initfix(NodeList *l)
{
@@ -686,6 +706,7 @@ slicelit(int ctxt, Node *n, Node *var, NodeList **init)
t->bound = mpgetfix(n->right->val.u.xval);
t->width = 0;
t->sym = nil;
+ t->haspointers = 0;
dowidth(t);
if(ctxt != 0) {
diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c
index 20a15bc71..bea90b87b 100644
--- a/src/cmd/gc/subr.c
+++ b/src/cmd/gc/subr.c
@@ -322,7 +322,7 @@ setlineno(Node *n)
uint32
stringhash(char *p)
{
- int32 h;
+ uint32 h;
int c;
h = 0;
@@ -333,9 +333,9 @@ stringhash(char *p)
h = h*PRIME1 + c;
}
- if(h < 0) {
+ if((int32)h < 0) {
h = -h;
- if(h < 0)
+ if((int32)h < 0)
h = 0;
}
return h;
@@ -525,7 +525,7 @@ saveorignode(Node *n)
n->orig = norig;
}
-// ispaddedfield returns whether the given field
+// ispaddedfield reports whether the given field
// is followed by padding. For the case where t is
// the last field, total gives the size of the enclosing struct.
static int
@@ -547,6 +547,9 @@ algtype1(Type *t, Type **bad)
if(bad)
*bad = T;
+ if(t->noalg)
+ return ANOEQ;
+
switch(t->etype) {
case TANY:
case TFORW:
@@ -615,23 +618,23 @@ algtype1(Type *t, Type **bad)
return -1; // needs special compare
case TSTRUCT:
- if(t->type != T && t->type->down == T) {
+ if(t->type != T && t->type->down == T && !isblanksym(t->type->sym)) {
// One-field struct is same as that one field alone.
return algtype1(t->type->type, bad);
}
ret = AMEM;
for(t1=t->type; t1!=T; t1=t1->down) {
- // Blank fields and padding must be ignored,
- // so need special compare.
- if(isblanksym(t1->sym) || ispaddedfield(t1, t->width)) {
+ // All fields must be comparable.
+ a = algtype1(t1->type, bad);
+ if(a == ANOEQ)
+ return ANOEQ;
+
+ // Blank fields, padded fields, fields with non-memory
+ // equality need special compare.
+ if(a != AMEM || isblanksym(t1->sym) || ispaddedfield(t1, t->width)) {
ret = -1;
continue;
}
- a = algtype1(t1->type, bad);
- if(a == ANOEQ)
- return ANOEQ; // not comparable
- if(a != AMEM)
- ret = -1; // needs special compare
}
return ret;
}
@@ -1260,7 +1263,7 @@ assignop(Type *src, Type *dst, char **why)
"\t\thave %S%hhT\n\t\twant %S%hhT", src, dst, missing->sym,
have->sym, have->type, missing->sym, missing->type);
else if(ptr)
- *why = smprint(":\n\t%T does not implement %T (%S method requires pointer receiver)",
+ *why = smprint(":\n\t%T does not implement %T (%S method has pointer receiver)",
src, dst, missing->sym);
else if(have)
*why = smprint(":\n\t%T does not implement %T (missing %S method)\n"
@@ -1411,6 +1414,9 @@ assignconv(Node *n, Type *t, char *context)
if(n == N || n->type == T || n->type->broke)
return n;
+ if(t->etype == TBLANK && n->type->etype == TNIL)
+ yyerror("use of untyped nil");
+
old = n;
old->diag++; // silence errors about n; we'll issue one below
defaultlit(&n, t);
@@ -2177,7 +2183,7 @@ lookdot0(Sym *s, Type *t, Type **save, int ignorecase)
c = 0;
if(u->etype == TSTRUCT || u->etype == TINTER) {
for(f=u->type; f!=T; f=f->down)
- if(f->sym == s || (ignorecase && ucistrcmp(f->sym->name, s->name) == 0)) {
+ if(f->sym == s || (ignorecase && f->type->etype == TFUNC && f->type->thistuple > 0 && ucistrcmp(f->sym->name, s->name) == 0)) {
if(save)
*save = f;
c++;
@@ -2495,13 +2501,13 @@ structargs(Type **tl, int mustname)
void
genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface)
{
- Node *this, *fn, *call, *n, *t, *pad;
+ Node *this, *fn, *call, *n, *t, *pad, *dot, *as;
NodeList *l, *args, *in, *out;
- Type *tpad;
+ Type *tpad, *methodrcvr;
int isddd;
Val v;
- if(debug['r'])
+ if(0 && debug['r'])
print("genwrapper rcvrtype=%T method=%T newnam=%S\n",
rcvr, method, newnam);
@@ -2547,8 +2553,10 @@ genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface)
isddd = l->n->left->isddd;
}
+ methodrcvr = getthisx(method->type)->type->type;
+
// generate nil pointer check for better error
- if(isptr[rcvr->etype] && rcvr->type == getthisx(method->type)->type->type) {
+ if(isptr[rcvr->etype] && rcvr->type == methodrcvr) {
// generating wrapper from *T to T.
n = nod(OIF, N, N);
n->ntest = nod(OEQ, this->left, nodnil());
@@ -2567,17 +2575,33 @@ genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface)
n->nbody = list1(call);
fn->nbody = list(fn->nbody, n);
}
-
+
+ dot = adddot(nod(OXDOT, this->left, newname(method->sym)));
+
// generate call
- call = nod(OCALL, adddot(nod(OXDOT, this->left, newname(method->sym))), N);
- call->list = args;
- call->isddd = isddd;
- if(method->type->outtuple > 0) {
- n = nod(ORETURN, N, N);
- n->list = list1(call);
- call = n;
+ if(!flag_race && isptr[rcvr->etype] && isptr[methodrcvr->etype] && method->embedded && !isifacemethod(method->type)) {
+ // generate tail call: adjust pointer receiver and jump to embedded method.
+ dot = dot->left; // skip final .M
+ if(!isptr[dotlist[0].field->type->etype])
+ dot = nod(OADDR, dot, N);
+ as = nod(OAS, this->left, nod(OCONVNOP, dot, N));
+ as->right->type = rcvr;
+ fn->nbody = list(fn->nbody, as);
+ n = nod(ORETJMP, N, N);
+ n->left = newname(methodsym(method->sym, methodrcvr, 0));
+ fn->nbody = list(fn->nbody, n);
+ } else {
+ fn->wrapper = 1; // ignore frame for panic+recover matching
+ call = nod(OCALL, dot, N);
+ call->list = args;
+ call->isddd = isddd;
+ if(method->type->outtuple > 0) {
+ n = nod(ORETURN, N, N);
+ n->list = list1(call);
+ call = n;
+ }
+ fn->nbody = list(fn->nbody, call);
}
- fn->nbody = list(fn->nbody, call);
if(0 && debug['r'])
dumplist("genwrapper body", fn->nbody);
@@ -3752,7 +3776,7 @@ isbadimport(Strlit *path)
}
void
-checknotnil(Node *x, NodeList **init)
+checknil(Node *x, NodeList **init)
{
Node *n;
@@ -3760,7 +3784,7 @@ checknotnil(Node *x, NodeList **init)
x = nod(OITAB, x, N);
typecheck(&x, Erv);
}
- n = nod(OCHECKNOTNIL, x, N);
+ n = nod(OCHECKNIL, x, N);
n->typecheck = 1;
*init = list(*init, n);
}
diff --git a/src/cmd/gc/swt.c b/src/cmd/gc/swt.c
index 3ad5f02a5..d6aa021a9 100644
--- a/src/cmd/gc/swt.c
+++ b/src/cmd/gc/swt.c
@@ -315,7 +315,7 @@ casebody(Node *sw, Node *typeswvar)
}
stat = concat(stat, n->nbody);
- // botch - shouldnt fall thru declaration
+ // botch - shouldn't fall thru declaration
last = stat->end->n;
if(last->op == OXFALL) {
if(typeswvar) {
@@ -415,7 +415,7 @@ mkcaselist(Node *sw, int arg)
break;
if(!eqtype(c1->node->left->type, c2->node->left->type))
continue;
- yyerrorl(c2->node->lineno, "duplicate case in switch\n\tprevious case at %L", c1->node->lineno);
+ yyerrorl(c2->node->lineno, "duplicate case %T in type switch\n\tprevious case at %L", c2->node->left->type, c1->node->lineno);
}
}
break;
@@ -427,7 +427,7 @@ mkcaselist(Node *sw, int arg)
if(exprcmp(c1, c1->link) != 0)
continue;
setlineno(c1->link->node);
- yyerror("duplicate case in switch\n\tprevious case at %L", c1->node->lineno);
+ yyerror("duplicate case %N in switch\n\tprevious case at %L", c1->node->left, c1->node->lineno);
}
break;
}
@@ -820,6 +820,9 @@ walkswitch(Node *sw)
return;
}
exprswitch(sw);
+ // Discard old AST elements after a walk. They can confuse racewealk.
+ sw->ntest = nil;
+ sw->list = nil;
}
/*
diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c
index 40eecd337..31a2f2c5c 100644
--- a/src/cmd/gc/typecheck.c
+++ b/src/cmd/gc/typecheck.c
@@ -33,6 +33,8 @@ static void stringtoarraylit(Node**);
static Node* resolve(Node*);
static void checkdefergo(Node*);
static int checkmake(Type*, char*, Node*);
+static int checksliceindex(Node*, Type*);
+static int checksliceconst(Node*, Node*);
static NodeList* typecheckdefstack;
@@ -88,7 +90,7 @@ static char* _typekind[] = {
[TARRAY] = "array",
[TFUNC] = "func",
[TNIL] = "nil",
- [TIDEAL] = "ideal number",
+ [TIDEAL] = "untyped number",
};
static char*
@@ -303,7 +305,7 @@ static void
typecheck1(Node **np, int top)
{
int et, aop, op, ptr;
- Node *n, *l, *r;
+ Node *n, *l, *r, *lo, *mid, *hi;
NodeList *args;
int ok, ntop;
Type *t, *tp, *missing, *have, *badtype;
@@ -420,12 +422,12 @@ reswitch:
goto error;
}
t->bound = mpgetfix(v.u.xval);
- if(t->bound < 0) {
- yyerror("array bound must be non-negative");
- goto error;
- } else if(doesoverflow(v, types[TINT])) {
+ if(doesoverflow(v, types[TINT])) {
yyerror("array bound is too large");
goto error;
+ } else if(t->bound < 0) {
+ yyerror("array bound must be non-negative");
+ goto error;
}
}
typecheck(&r, Etype);
@@ -837,7 +839,7 @@ reswitch:
"\t\thave %S%hhT\n\t\twant %S%hhT", n->type, t, missing->sym,
have->sym, have->type, missing->sym, missing->type);
else if(ptr)
- yyerror("impossible type assertion:\n\t%T does not implement %T (%S method requires pointer receiver)",
+ yyerror("impossible type assertion:\n\t%T does not implement %T (%S method has pointer receiver)",
n->type, t, missing->sym);
else if(have)
yyerror("impossible type assertion:\n\t%T does not implement %T (missing %S method)\n"
@@ -926,11 +928,7 @@ reswitch:
goto ret;
case OSEND:
- if(top & Erv) {
- yyerror("send statement %N used as value; use select for non-blocking send", n);
- goto error;
- }
- ok |= Etop | Erv;
+ ok |= Etop;
l = typecheck(&n->left, Erv);
typecheck(&n->right, Erv);
defaultlit(&n->left, T);
@@ -993,54 +991,63 @@ reswitch:
yyerror("cannot slice %N (type %T)", l, t);
goto error;
}
- if(n->right->left != N) {
- if((t = n->right->left->type) == T)
- goto error;
- if(!isint[t->etype]) {
- yyerror("invalid slice index %N (type %T)", n->right->left, t);
+ if((lo = n->right->left) != N && checksliceindex(lo, tp) < 0)
+ goto error;
+ if((hi = n->right->right) != N && checksliceindex(hi, tp) < 0)
+ goto error;
+ if(checksliceconst(lo, hi) < 0)
+ goto error;
+ goto ret;
+
+ case OSLICE3:
+ ok |= Erv;
+ typecheck(&n->left, top);
+ typecheck(&n->right->left, Erv);
+ typecheck(&n->right->right->left, Erv);
+ typecheck(&n->right->right->right, Erv);
+ defaultlit(&n->left, T);
+ indexlit(&n->right->left);
+ indexlit(&n->right->right->left);
+ indexlit(&n->right->right->right);
+ l = n->left;
+ if(isfixedarray(l->type)) {
+ if(!islvalue(n->left)) {
+ yyerror("invalid operation %N (slice of unaddressable value)", n);
goto error;
}
- if(n->right->left->op == OLITERAL) {
- if(mpgetfix(n->right->left->val.u.xval) < 0) {
- yyerror("invalid slice index %N (index must be non-negative)", n->right->left);
- goto error;
- } else if(tp != nil && tp->bound > 0 && mpgetfix(n->right->left->val.u.xval) > tp->bound) {
- yyerror("invalid slice index %N (out of bounds for %d-element array)", n->right->left, tp->bound);
- goto error;
- } else if(mpcmpfixfix(n->right->left->val.u.xval, maxintval[TINT]) > 0) {
- yyerror("invalid slice index %N (index too large)", n->right->left);
- goto error;
- }
- }
+ n->left = nod(OADDR, n->left, N);
+ n->left->implicit = 1;
+ typecheck(&n->left, Erv);
+ l = n->left;
}
- if(n->right->right != N) {
- if((t = n->right->right->type) == T)
- goto error;
- if(!isint[t->etype]) {
- yyerror("invalid slice index %N (type %T)", n->right->right, t);
- goto error;
- }
- if(n->right->right->op == OLITERAL) {
- if(mpgetfix(n->right->right->val.u.xval) < 0) {
- yyerror("invalid slice index %N (index must be non-negative)", n->right->right);
- goto error;
- } else if(tp != nil && tp->bound > 0 && mpgetfix(n->right->right->val.u.xval) > tp->bound) {
- yyerror("invalid slice index %N (out of bounds for %d-element array)", n->right->right, tp->bound);
- goto error;
- } else if(mpcmpfixfix(n->right->right->val.u.xval, maxintval[TINT]) > 0) {
- yyerror("invalid slice index %N (index too large)", n->right->right);
- goto error;
- }
- }
+ if((t = l->type) == T)
+ goto error;
+ tp = nil;
+ if(istype(t, TSTRING)) {
+ yyerror("invalid operation %N (3-index slice of string)", n);
+ goto error;
}
- if(n->right->left != N
- && n->right->right != N
- && n->right->left->op == OLITERAL
- && n->right->right->op == OLITERAL
- && mpcmpfixfix(n->right->left->val.u.xval, n->right->right->val.u.xval) > 0) {
- yyerror("inverted slice index %N > %N", n->right->left, n->right->right);
+ if(isptr[t->etype] && isfixedarray(t->type)) {
+ tp = t->type;
+ n->type = typ(TARRAY);
+ n->type->type = tp->type;
+ n->type->bound = -1;
+ dowidth(n->type);
+ n->op = OSLICE3ARR;
+ } else if(isslice(t)) {
+ n->type = t;
+ } else {
+ yyerror("cannot slice %N (type %T)", l, t);
goto error;
}
+ if((lo = n->right->left) != N && checksliceindex(lo, tp) < 0)
+ goto error;
+ if((mid = n->right->right->left) != N && checksliceindex(mid, tp) < 0)
+ goto error;
+ if((hi = n->right->right->right) != N && checksliceindex(hi, tp) < 0)
+ goto error;
+ if(checksliceconst(lo, hi) < 0 || checksliceconst(lo, mid) < 0 || checksliceconst(mid, hi) < 0)
+ goto error;
goto ret;
/*
@@ -1565,7 +1572,20 @@ reswitch:
fatal("OITAB of %T", t);
n->type = ptrto(types[TUINTPTR]);
goto ret;
-
+
+ case OSPTR:
+ ok |= Erv;
+ typecheck(&n->left, Erv);
+ if((t = n->left->type) == T)
+ goto error;
+ if(!isslice(t) && t->etype != TSTRING)
+ fatal("OSPTR of %T", t);
+ if(t->etype == TSTRING)
+ n->type = ptrto(types[TUINT8]);
+ else
+ n->type = ptrto(t->type);
+ goto ret;
+
case OCLOSUREVAR:
ok |= Erv;
goto ret;
@@ -1651,6 +1671,10 @@ reswitch:
goto ret;
typecheckaste(ORETURN, nil, 0, getoutargx(curfn->type), n->list, "return argument");
goto ret;
+
+ case ORETJMP:
+ ok |= Etop;
+ goto ret;
case OSELECT:
ok |= Etop;
@@ -1753,6 +1777,43 @@ out:
*np = n;
}
+static int
+checksliceindex(Node *r, Type *tp)
+{
+ Type *t;
+
+ if((t = r->type) == T)
+ return -1;
+ if(!isint[t->etype]) {
+ yyerror("invalid slice index %N (type %T)", r, t);
+ return -1;
+ }
+ if(r->op == OLITERAL) {
+ if(mpgetfix(r->val.u.xval) < 0) {
+ yyerror("invalid slice index %N (index must be non-negative)", r);
+ return -1;
+ } else if(tp != nil && tp->bound > 0 && mpgetfix(r->val.u.xval) > tp->bound) {
+ yyerror("invalid slice index %N (out of bounds for %d-element array)", r, tp->bound);
+ return -1;
+ } else if(mpcmpfixfix(r->val.u.xval, maxintval[TINT]) > 0) {
+ yyerror("invalid slice index %N (index too large)", r);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int
+checksliceconst(Node *lo, Node *hi)
+{
+ if(lo != N && hi != N && lo->op == OLITERAL && hi->op == OLITERAL
+ && mpcmpfixfix(lo->val.u.xval, hi->val.u.xval) > 0) {
+ yyerror("invalid slice index: %N > %N", lo, hi);
+ return -1;
+ }
+ return 0;
+}
+
static void
checkdefergo(Node *n)
{
@@ -1793,6 +1854,11 @@ checkdefergo(Node *n)
break;
default:
conv:
+ // type is broken or missing, most likely a method call on a broken type
+ // we will warn about the broken type elsewhere. no need to emit a potentially confusing error
+ if(n->left->type == T || n->left->type->broke)
+ break;
+
if(!n->diag) {
// The syntax made sure it was a call, so this must be
// a conversion.
@@ -2074,9 +2140,9 @@ typecheckaste(int op, Node *call, int isddd, Type *tstruct, NodeList *nl, char *
for(; tn; tn=tn->down) {
if(assignop(tn->type, tl->type->type, &why) == 0) {
if(call != N)
- yyerror("cannot use %T as type %T in argument to %N%s", tn->type, tl->type, call, why);
+ yyerror("cannot use %T as type %T in argument to %N%s", tn->type, tl->type->type, call, why);
else
- yyerror("cannot use %T as type %T in %s%s", tn->type, tl->type, desc, why);
+ yyerror("cannot use %T as type %T in %s%s", tn->type, tl->type->type, desc, why);
}
}
goto out;
@@ -2993,7 +3059,7 @@ queuemethod(Node *n)
Node*
typecheckdef(Node *n)
{
- int lno;
+ int lno, nerrors0;
Node *e;
Type *t;
NodeList *l;
@@ -3121,7 +3187,13 @@ typecheckdef(Node *n)
n->walkdef = 1;
n->type = typ(TFORW);
n->type->sym = n->sym;
+ nerrors0 = nerrors;
typecheckdeftype(n);
+ if(n->type->etype == TFORW && nerrors > nerrors0) {
+ // Something went wrong during type-checking,
+ // but it was reported. Silence future errors.
+ n->type->broke = 1;
+ }
if(curfn)
resumecheckwidth();
break;
@@ -3277,6 +3349,7 @@ isterminating(NodeList *l, int top)
case OGOTO:
case ORETURN:
+ case ORETJMP:
case OPANIC:
case OXFALL:
return 1;
diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c
index e8ebd2892..66409d530 100644
--- a/src/cmd/gc/walk.c
+++ b/src/cmd/gc/walk.c
@@ -21,6 +21,7 @@ static NodeList* reorder3(NodeList*);
static Node* addstr(Node*, NodeList**);
static Node* appendslice(Node*, NodeList**);
static Node* append(Node*, NodeList**);
+static Node* copyany(Node*, NodeList**);
static Node* sliceany(Node*, NodeList**);
static void walkcompare(Node**, NodeList**);
static void walkrotate(Node**);
@@ -174,6 +175,8 @@ walkstmt(Node **np)
n->ninit = nil;
walkexpr(&n, &init);
addinit(&n, init);
+ if((*np)->op == OCOPY && n->op == OCONVNOP)
+ n->op = OEMPTY; // don't leave plain values as statements.
break;
case OBREAK:
@@ -184,7 +187,7 @@ walkstmt(Node **np)
case OLABEL:
case ODCLCONST:
case ODCLTYPE:
- case OCHECKNOTNIL:
+ case OCHECKNIL:
break;
case OBLOCK:
@@ -282,6 +285,9 @@ walkstmt(Node **np)
n->list = ll;
break;
+ case ORETJMP:
+ break;
+
case OSELECT:
walkselect(n);
break;
@@ -337,7 +343,7 @@ walkexpr(Node **np, NodeList **init)
Node *r, *l, *var, *a;
NodeList *ll, *lr, *lpost;
Type *t;
- int et;
+ int et, old_safemode;
int64 v;
int32 lno;
Node *n, *fn, *n1, *n2;
@@ -401,10 +407,6 @@ walkexpr(Node **np, NodeList **init)
goto ret;
case OIND:
- if(n->left->type->type->width == 0) {
- n->left = cheapexpr(n->left, init);
- checknotnil(n->left, init);
- }
walkexpr(&n->left, init);
goto ret;
@@ -416,8 +418,9 @@ walkexpr(Node **np, NodeList **init)
case ODOTPTR:
usefield(n);
if(n->op == ODOTPTR && n->left->type->type->width == 0) {
+ // No actual copy will be generated, so emit an explicit nil check.
n->left = cheapexpr(n->left, init);
- checknotnil(n->left, init);
+ checknil(n->left, init);
}
walkexpr(&n->left, init);
goto ret;
@@ -427,6 +430,7 @@ walkexpr(Node **np, NodeList **init)
walkexpr(&n->right, init);
goto ret;
+ case OSPTR:
case OITAB:
walkexpr(&n->left, init);
goto ret;
@@ -483,7 +487,15 @@ walkexpr(Node **np, NodeList **init)
case ONE:
walkexpr(&n->left, init);
walkexpr(&n->right, init);
+ // Disable safemode while compiling this code: the code we
+ // generate internally can refer to unsafe.Pointer.
+ // In this case it can happen if we need to generate an ==
+ // for a struct containing a reflect.Value, which itself has
+ // an unexported field of type unsafe.Pointer.
+ old_safemode = safemode;
+ safemode = 0;
walkcompare(&n, init);
+ safemode = old_safemode;
goto ret;
case OANDAND:
@@ -1032,8 +1044,8 @@ walkexpr(Node **np, NodeList **init)
if(!n->bounded)
yyerror("index out of bounds");
else {
- // replace "abc"[2] with 'b'.
- // delayed until now because "abc"[2] is not
+ // replace "abc"[1] with 'b'.
+ // delayed until now because "abc"[1] is not
// an ideal constant.
v = mpgetfix(n->right->val.u.xval);
nodconst(n, n->type, n->left->val.u.sval->s[v]);
@@ -1113,6 +1125,27 @@ walkexpr(Node **np, NodeList **init)
n->right->right = safeexpr(n->right->right, init);
n = sliceany(n, init); // chops n->right, sets n->list
goto ret;
+
+ case OSLICE3:
+ case OSLICE3ARR:
+ if(n->right == N) // already processed
+ goto ret;
+
+ walkexpr(&n->left, init);
+ // TODO the OINDEX case is a bug elsewhere that needs to be traced. it causes a crash on ([2][]int{ ... })[1][lo:hi]
+ // TODO the comment on the previous line was copied from case OSLICE. it might not even be true.
+ if(n->left->op == OINDEX)
+ n->left = copyexpr(n->left, n->left->type, init);
+ else
+ n->left = safeexpr(n->left, init);
+ walkexpr(&n->right->left, init);
+ n->right->left = safeexpr(n->right->left, init);
+ walkexpr(&n->right->right->left, init);
+ n->right->right->left = safeexpr(n->right->right->left, init);
+ walkexpr(&n->right->right->right, init);
+ n->right->right->right = safeexpr(n->right->right->right, init);
+ n = sliceany(n, init); // chops n->right, sets n->list
+ goto ret;
case OADDR:
walkexpr(&n->left, init);
@@ -1198,26 +1231,26 @@ walkexpr(Node **np, NodeList **init)
goto ret;
case OAPPEND:
- if(n->isddd) {
- if(istype(n->type->type, TUINT8) && istype(n->list->next->n->type, TSTRING))
- n = mkcall("appendstr", n->type, init, typename(n->type), n->list->n, n->list->next->n);
- else
- n = appendslice(n, init);
- }
+ if(n->isddd)
+ n = appendslice(n, init); // also works for append(slice, string).
else
n = append(n, init);
goto ret;
case OCOPY:
- if(n->right->type->etype == TSTRING)
- fn = syslook("slicestringcopy", 1);
- else
- fn = syslook("copy", 1);
- argtype(fn, n->left->type);
- argtype(fn, n->right->type);
- n = mkcall1(fn, n->type, init,
- n->left, n->right,
- nodintconst(n->left->type->type->width));
+ if(flag_race) {
+ if(n->right->type->etype == TSTRING)
+ fn = syslook("slicestringcopy", 1);
+ else
+ fn = syslook("copy", 1);
+ argtype(fn, n->left->type);
+ argtype(fn, n->right->type);
+ n = mkcall1(fn, n->type, init,
+ n->left, n->right,
+ nodintconst(n->left->type->type->width));
+ goto ret;
+ }
+ n = copyany(n, init);
goto ret;
case OCLOSE:
@@ -1246,18 +1279,35 @@ walkexpr(Node **np, NodeList **init)
goto ret;
case OMAKESLICE:
- // makeslice(t *Type, nel int64, max int64) (ary []any)
l = n->left;
r = n->right;
if(r == nil)
l = r = safeexpr(l, init);
t = n->type;
- fn = syslook("makeslice", 1);
- argtype(fn, t->type); // any-1
- n = mkcall1(fn, n->type, init,
- typename(n->type),
- conv(l, types[TINT64]),
- conv(r, types[TINT64]));
+ if(n->esc == EscNone
+ && smallintconst(l) && smallintconst(r)
+ && (t->type->width == 0 || mpgetfix(r->val.u.xval) < (1ULL<<16) / t->type->width)) {
+ // var arr [r]T
+ // n = arr[:l]
+ t = aindex(r, t->type); // [r]T
+ var = temp(t);
+ a = nod(OAS, var, N); // zero temp
+ typecheck(&a, Etop);
+ *init = list(*init, a);
+ r = nod(OSLICE, var, nod(OKEY, N, l)); // arr[:l]
+ r = conv(r, n->type); // in case n->type is named.
+ typecheck(&r, Erv);
+ walkexpr(&r, init);
+ n = r;
+ } else {
+ // makeslice(t *Type, nel int64, max int64) (ary []any)
+ fn = syslook("makeslice", 1);
+ argtype(fn, t->type); // any-1
+ n = mkcall1(fn, n->type, init,
+ typename(n->type),
+ conv(l, types[TINT64]),
+ conv(r, types[TINT64]));
+ }
goto ret;
case ORUNESTR:
@@ -1343,7 +1393,11 @@ ret:
// constants until walk. For example, if n is y%1 == 0, the
// walk of y%1 may have replaced it by 0.
// Check whether n with its updated args is itself now a constant.
+ t = n->type;
evconst(n);
+ n->type = t;
+ if(n->op == OLITERAL)
+ typecheck(&n, Erv);
ullmancalc(n);
@@ -2268,7 +2322,9 @@ paramstoheap(Type **argin, int out)
v = t->nname;
if(v && v->sym && v->sym->name[0] == '~')
v = N;
- if(v == N && out && hasdefer) {
+ // In precisestack mode, the garbage collector assumes results
+ // are always live, so zero them always.
+ if(out && (precisestack_enabled || (v == N && hasdefer))) {
// Defer might stop a panic and show the
// return values as they exist at the time of panic.
// Make sure to zero them on entry to the function.
@@ -2473,16 +2529,104 @@ addstr(Node *n, NodeList **init)
return r;
}
+// expand append(l1, l2...) to
+// init {
+// s := l1
+// if n := len(l1) + len(l2) - cap(s); n > 0 {
+// s = growslice(s, n)
+// }
+// s = s[:len(l1)+len(l2)]
+// memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T))
+// }
+// s
+//
+// l2 is allowed to be a string.
static Node*
appendslice(Node *n, NodeList **init)
{
- Node *f;
+ NodeList *l;
+ Node *l1, *l2, *nt, *nif, *fn;
+ Node *nptr1, *nptr2, *nwid;
+ Node *s;
+
+ walkexprlistsafe(n->list, init);
+
+ // walkexprlistsafe will leave OINDEX (s[n]) alone if both s
+ // and n are name or literal, but those may index the slice we're
+ // modifying here. Fix explicitly.
+ for(l=n->list; l; l=l->next)
+ l->n = cheapexpr(l->n, init);
+
+ l1 = n->list->n;
+ l2 = n->list->next->n;
+
+ s = temp(l1->type); // var s []T
+ l = nil;
+ l = list(l, nod(OAS, s, l1)); // s = l1
+
+ nt = temp(types[TINT]);
+ nif = nod(OIF, N, N);
+ // n := len(s) + len(l2) - cap(s)
+ nif->ninit = list1(nod(OAS, nt,
+ nod(OSUB, nod(OADD, nod(OLEN, s, N), nod(OLEN, l2, N)), nod(OCAP, s, N))));
+ nif->ntest = nod(OGT, nt, nodintconst(0));
+ // instantiate growslice(Type*, []any, int64) []any
+ fn = syslook("growslice", 1);
+ argtype(fn, s->type->type);
+ argtype(fn, s->type->type);
+
+ // s = growslice(T, s, n)
+ nif->nbody = list1(nod(OAS, s, mkcall1(fn, s->type, &nif->ninit,
+ typename(s->type),
+ s,
+ conv(nt, types[TINT64]))));
+
+ l = list(l, nif);
+
+ if(flag_race) {
+ // rely on runtime to instrument copy.
+ // copy(s[len(l1):len(l1)+len(l2)], l2)
+ nptr1 = nod(OSLICE, s, nod(OKEY,
+ nod(OLEN, l1, N),
+ nod(OADD, nod(OLEN, l1, N), nod(OLEN, l2, N))));
+ nptr1->etype = 1;
+ nptr2 = l2;
+ if(l2->type->etype == TSTRING)
+ fn = syslook("slicestringcopy", 1);
+ else
+ fn = syslook("copy", 1);
+ argtype(fn, l1->type);
+ argtype(fn, l2->type);
+ l = list(l, mkcall1(fn, types[TINT], init,
+ nptr1, nptr2,
+ nodintconst(s->type->type->width)));
+ } else {
+ // memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T))
+ nptr1 = nod(OINDEX, s, nod(OLEN, l1, N));
+ nptr1->bounded = 1;
+ nptr1 = nod(OADDR, nptr1, N);
+
+ nptr2 = nod(OSPTR, l2, N);
- f = syslook("appendslice", 1);
- argtype(f, n->type);
- argtype(f, n->type->type);
- argtype(f, n->type);
- return mkcall1(f, n->type, init, typename(n->type), n->list->n, n->list->next->n);
+ fn = syslook("memmove", 1);
+ argtype(fn, s->type->type); // 1 old []any
+ argtype(fn, s->type->type); // 2 ret []any
+
+ nwid = cheapexpr(conv(nod(OLEN, l2, N), types[TUINTPTR]), &l);
+ nwid = nod(OMUL, nwid, nodintconst(s->type->type->width));
+ l = list(l, mkcall1(fn, T, init, nptr1, nptr2, nwid));
+ }
+
+ // s = s[:len(l1)+len(l2)]
+ nt = nod(OADD, nod(OLEN, l1, N), nod(OLEN, l2, N));
+ nt = nod(OSLICE, s, nod(OKEY, N, nt));
+ nt->etype = 1;
+ l = list(l, nod(OAS, s, nt));
+
+ typechecklist(l, Etop);
+ walkstmtlist(l);
+ *init = concat(*init, l);
+ return s;
}
// expand append(src, a [, b]* ) to
@@ -2544,7 +2688,7 @@ append(Node *n, NodeList **init)
l = list(l, nod(OAS, nn, nod(OLEN, ns, N))); // n = len(s)
nx = nod(OSLICE, ns, nod(OKEY, N, nod(OADD, nn, na))); // ...s[:n+argc]
- nx->bounded = 1;
+ nx->etype = 1;
l = list(l, nod(OAS, ns, nx)); // s = s[:n+argc]
for (a = n->list->next; a != nil; a = a->next) {
@@ -2561,22 +2705,81 @@ append(Node *n, NodeList **init)
return ns;
}
+// Lower copy(a, b) to a memmove call.
+//
+// init {
+// n := len(a)
+// if n > len(b) { n = len(b) }
+// memmove(a.ptr, b.ptr, n*sizeof(elem(a)))
+// }
+// n;
+//
+// Also works if b is a string.
+//
+static Node*
+copyany(Node *n, NodeList **init)
+{
+ Node *nl, *nr, *nfrm, *nto, *nif, *nlen, *nwid, *fn;
+ NodeList *l;
+
+ walkexpr(&n->left, init);
+ walkexpr(&n->right, init);
+ nl = temp(n->left->type);
+ nr = temp(n->right->type);
+ l = nil;
+ l = list(l, nod(OAS, nl, n->left));
+ l = list(l, nod(OAS, nr, n->right));
+
+ nfrm = nod(OSPTR, nr, N);
+ nto = nod(OSPTR, nl, N);
+
+ nlen = temp(types[TINT]);
+ // n = len(to)
+ l = list(l, nod(OAS, nlen, nod(OLEN, nl, N)));
+ // if n > len(frm) { n = len(frm) }
+ nif = nod(OIF, N, N);
+ nif->ntest = nod(OGT, nlen, nod(OLEN, nr, N));
+ nif->nbody = list(nif->nbody,
+ nod(OAS, nlen, nod(OLEN, nr, N)));
+ l = list(l, nif);
+
+ // Call memmove.
+ fn = syslook("memmove", 1);
+ argtype(fn, nl->type->type);
+ argtype(fn, nl->type->type);
+ nwid = temp(types[TUINTPTR]);
+ l = list(l, nod(OAS, nwid, conv(nlen, types[TUINTPTR])));
+ nwid = nod(OMUL, nwid, nodintconst(nl->type->type->width));
+ l = list(l, mkcall1(fn, T, init, nto, nfrm, nwid));
+
+ typechecklist(l, Etop);
+ walkstmtlist(l);
+ *init = concat(*init, l);
+ return nlen;
+}
-// Generate frontend part for OSLICE[ARR|STR]
+// Generate frontend part for OSLICE[3][ARR|STR]
//
static Node*
sliceany(Node* n, NodeList **init)
{
- int bounded;
- Node *src, *lb, *hb, *bound, *chk, *chk1, *chk2;
- int64 lbv, hbv, bv, w;
+ int bounded, slice3;
+ Node *src, *lb, *hb, *cb, *bound, *chk, *chk0, *chk1, *chk2;
+ int64 lbv, hbv, cbv, bv, w;
Type *bt;
// print("before sliceany: %+N\n", n);
src = n->left;
lb = n->right->left;
- hb = n->right->right;
+ slice3 = n->op == OSLICE3 || n->op == OSLICE3ARR;
+ if(slice3) {
+ hb = n->right->right->left;
+ cb = n->right->right->right;
+ } else {
+ hb = n->right->right;
+ cb = N;
+ }
bounded = n->etype;
@@ -2597,6 +2800,13 @@ sliceany(Node* n, NodeList **init)
bv = mpgetfix(bound->val.u.xval);
}
+ if(isconst(cb, CTINT)) {
+ cbv = mpgetfix(cb->val.u.xval);
+ if(cbv < 0 || cbv > bv) {
+ yyerror("slice index out of bounds");
+ cbv = -1;
+ }
+ }
if(isconst(hb, CTINT)) {
hbv = mpgetfix(hb->val.u.xval);
if(hbv < 0 || hbv > bv) {
@@ -2618,10 +2828,13 @@ sliceany(Node* n, NodeList **init)
// generate
// if hb > bound || lb > hb { panicslice() }
chk = N;
+ chk0 = N;
chk1 = N;
chk2 = N;
bt = types[simtype[TUINT]];
+ if(cb != N && cb->type->width > 4)
+ bt = types[TUINT64];
if(hb != N && hb->type->width > 4)
bt = types[TUINT64];
if(lb != N && lb->type->width > 4)
@@ -2629,10 +2842,26 @@ sliceany(Node* n, NodeList **init)
bound = cheapexpr(conv(bound, bt), init);
+ if(cb != N) {
+ cb = cheapexpr(conv(cb, bt), init);
+ if(!bounded)
+ chk0 = nod(OLT, bound, cb);
+ } else if(slice3) {
+ // When we figure out what this means, implement it.
+ fatal("slice3 with cb == N"); // rejected by parser
+ }
+
if(hb != N) {
hb = cheapexpr(conv(hb, bt), init);
- if(!bounded)
- chk1 = nod(OLT, bound, hb);
+ if(!bounded) {
+ if(cb != N)
+ chk1 = nod(OLT, cb, hb);
+ else
+ chk1 = nod(OLT, bound, hb);
+ }
+ } else if(slice3) {
+ // When we figure out what this means, implement it.
+ fatal("slice3 with hb == N"); // rejected by parser
} else if(n->op == OSLICEARR) {
hb = bound;
} else {
@@ -2648,11 +2877,18 @@ sliceany(Node* n, NodeList **init)
chk2 = nod(OLT, hb, lb);
}
- if(chk1 != N || chk2 != N) {
+ if(chk0 != N || chk1 != N || chk2 != N) {
chk = nod(OIF, N, N);
chk->nbody = list1(mkcall("panicslice", T, init));
- if(chk1 != N)
- chk->ntest = chk1;
+ chk->likely = -1;
+ if(chk0 != N)
+ chk->ntest = chk0;
+ if(chk1 != N) {
+ if(chk->ntest == N)
+ chk->ntest = chk1;
+ else
+ chk->ntest = nod(OOROR, chk->ntest, chk1);
+ }
if(chk2 != N) {
if(chk->ntest == N)
chk->ntest = chk2;
@@ -2670,10 +2906,12 @@ sliceany(Node* n, NodeList **init)
// cap = bound [ - lo ]
n->right = N;
n->list = nil;
+ if(!slice3)
+ cb = bound;
if(lb == N)
- bound = conv(bound, types[simtype[TUINT]]);
+ bound = conv(cb, types[simtype[TUINT]]);
else
- bound = nod(OSUB, conv(bound, types[simtype[TUINT]]), conv(lb, types[simtype[TUINT]]));
+ bound = nod(OSUB, conv(cb, types[simtype[TUINT]]), conv(lb, types[simtype[TUINT]]));
typecheck(&bound, Erv);
walkexpr(&bound, init);
n->list = list(n->list, bound);
diff --git a/src/cmd/gc/y.tab.c b/src/cmd/gc/y.tab.c
index 21c67e805..390ad80b3 100644
--- a/src/cmd/gc/y.tab.c
+++ b/src/cmd/gc/y.tab.c
@@ -1,24 +1,21 @@
-/* A Bison parser, made by GNU Bison 2.3. */
+/* A Bison parser, made by GNU Bison 2.5. */
-/* Skeleton implementation for Bison's Yacc-like parsers in C
-
- Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
- Free Software Foundation, Inc.
-
- This program is free software; you can redistribute it and/or modify
+/* Bison implementation for Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
+
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* As a special exception, you may create a larger work that contains
part or all of the Bison parser skeleton and distribute that work
@@ -29,7 +26,7 @@
special exception, which will cause the skeleton and the resulting
Bison output files to be licensed under the GNU General Public
License without this special exception.
-
+
This special exception was added by the Free Software Foundation in
version 2.2 of Bison. */
@@ -47,7 +44,7 @@
#define YYBISON 1
/* Bison version. */
-#define YYBISON_VERSION "2.3"
+#define YYBISON_VERSION "2.5"
/* Skeleton name. */
#define YYSKELETON_NAME "yacc.c"
@@ -55,11 +52,52 @@
/* Pure parsers. */
#define YYPURE 0
+/* Push parsers. */
+#define YYPUSH 0
+
+/* Pull parsers. */
+#define YYPULL 1
+
/* Using locations. */
#define YYLSP_NEEDED 0
+/* Copy the first part of user declarations. */
+
+/* Line 268 of yacc.c */
+#line 20 "go.y"
+
+#include <u.h>
+#include <stdio.h> /* if we don't, bison will, and go.h re-#defines getc */
+#include <libc.h>
+#include "go.h"
+
+static void fixlbrace(int);
+
+
+/* Line 268 of yacc.c */
+#line 81 "y.tab.c"
+
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 1
+#endif
+
+/* Enabling the token table. */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
+
/* Tokens. */
#ifndef YYTOKENTYPE
# define YYTOKENTYPE
@@ -171,61 +209,36 @@
-/* Copy the first part of user declarations. */
-#line 20 "go.y"
-
-#include <u.h>
-#include <stdio.h> /* if we don't, bison will, and go.h re-#defines getc */
-#include <libc.h>
-#include "go.h"
-
-static void fixlbrace(int);
-
-
-/* Enabling traces. */
-#ifndef YYDEBUG
-# define YYDEBUG 0
-#endif
-
-/* Enabling verbose error messages. */
-#ifdef YYERROR_VERBOSE
-# undef YYERROR_VERBOSE
-# define YYERROR_VERBOSE 1
-#else
-# define YYERROR_VERBOSE 1
-#endif
-
-/* Enabling the token table. */
-#ifndef YYTOKEN_TABLE
-# define YYTOKEN_TABLE 0
-#endif
-
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
typedef union YYSTYPE
-#line 28 "go.y"
{
+
+/* Line 293 of yacc.c */
+#line 28 "go.y"
+
Node* node;
NodeList* list;
Type* type;
Sym* sym;
struct Val val;
int i;
-}
-/* Line 193 of yacc.c. */
-#line 216 "y.tab.c"
- YYSTYPE;
+
+
+
+/* Line 293 of yacc.c */
+#line 230 "y.tab.c"
+} YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
# define YYSTYPE_IS_DECLARED 1
-# define YYSTYPE_IS_TRIVIAL 1
#endif
-
/* Copy the second part of user declarations. */
-/* Line 216 of yacc.c. */
-#line 229 "y.tab.c"
+/* Line 343 of yacc.c */
+#line 242 "y.tab.c"
#ifdef short
# undef short
@@ -300,14 +313,14 @@ typedef short int yytype_int16;
#if (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
static int
-YYID (int i)
+YYID (int yyi)
#else
static int
-YYID (i)
- int i;
+YYID (yyi)
+ int yyi;
#endif
{
- return i;
+ return yyi;
}
#endif
@@ -328,11 +341,11 @@ YYID (i)
# define alloca _alloca
# else
# define YYSTACK_ALLOC alloca
-# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
-# ifndef _STDLIB_H
-# define _STDLIB_H 1
+# ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
# endif
# endif
# endif
@@ -355,24 +368,24 @@ YYID (i)
# ifndef YYSTACK_ALLOC_MAXIMUM
# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
# endif
-# if (defined __cplusplus && ! defined _STDLIB_H \
+# if (defined __cplusplus && ! defined EXIT_SUCCESS \
&& ! ((defined YYMALLOC || defined malloc) \
&& (defined YYFREE || defined free)))
# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
-# ifndef _STDLIB_H
-# define _STDLIB_H 1
+# ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
# endif
# endif
# ifndef YYMALLOC
# define YYMALLOC malloc
-# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+# if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
# endif
# endif
# ifndef YYFREE
# define YYFREE free
-# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+# if ! defined free && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
void free (void *); /* INFRINGES ON USER NAME SPACE */
# endif
@@ -388,9 +401,9 @@ void free (void *); /* INFRINGES ON USER NAME SPACE */
/* A type that is properly aligned for any stack member. */
union yyalloc
{
- yytype_int16 yyss;
- YYSTYPE yyvs;
- };
+ yytype_int16 yyss_alloc;
+ YYSTYPE yyvs_alloc;
+};
/* The size of the maximum gap between one aligned stack and the next. */
# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
@@ -401,6 +414,27 @@ union yyalloc
((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+ YYSTACK_GAP_MAXIMUM)
+# define YYCOPY_NEEDED 1
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack_alloc, Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \
+ Stack = &yyptr->Stack_alloc; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (YYID (0))
+
+#endif
+
+#if defined YYCOPY_NEEDED && YYCOPY_NEEDED
/* Copy COUNT objects from FROM to TO. The source and destination do
not overlap. */
# ifndef YYCOPY
@@ -418,38 +452,21 @@ union yyalloc
while (YYID (0))
# endif
# endif
-
-/* Relocate STACK from its old location to the new one. The
- local variables YYSIZE and YYSTACKSIZE give the old and new number of
- elements in the stack, and YYPTR gives the new location of the
- stack. Advance YYPTR to a properly aligned location for the next
- stack. */
-# define YYSTACK_RELOCATE(Stack) \
- do \
- { \
- YYSIZE_T yynewbytes; \
- YYCOPY (&yyptr->Stack, Stack, yysize); \
- Stack = &yyptr->Stack; \
- yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
- yyptr += yynewbytes / sizeof (*yyptr); \
- } \
- while (YYID (0))
-
-#endif
+#endif /* !YYCOPY_NEEDED */
/* YYFINAL -- State number of the termination state. */
#define YYFINAL 4
/* YYLAST -- Last index in YYTABLE. */
-#define YYLAST 2194
+#define YYLAST 2270
/* YYNTOKENS -- Number of terminals. */
#define YYNTOKENS 76
/* YYNNTS -- Number of nonterminals. */
#define YYNNTS 142
/* YYNRULES -- Number of rules. */
-#define YYNRULES 349
+#define YYNRULES 351
/* YYNRULES -- Number of states. */
-#define YYNSTATES 663
+#define YYNSTATES 667
/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
#define YYUNDEFTOK 2
@@ -512,28 +529,29 @@ static const yytype_uint16 yyprhs[] =
326, 330, 334, 338, 342, 346, 350, 354, 358, 362,
366, 370, 374, 378, 382, 384, 387, 390, 393, 396,
399, 402, 405, 408, 412, 418, 425, 427, 429, 433,
- 439, 445, 450, 457, 459, 465, 471, 477, 485, 487,
- 488, 492, 494, 499, 501, 506, 508, 512, 514, 516,
- 518, 520, 522, 524, 526, 527, 529, 531, 533, 535,
- 540, 542, 544, 546, 549, 551, 553, 555, 557, 559,
- 563, 565, 567, 569, 572, 574, 576, 578, 580, 584,
- 586, 588, 590, 592, 594, 596, 598, 600, 602, 606,
- 611, 616, 619, 623, 629, 631, 633, 636, 640, 646,
- 650, 656, 660, 664, 670, 679, 685, 694, 700, 701,
- 705, 706, 708, 712, 714, 719, 722, 723, 727, 729,
- 733, 735, 739, 741, 745, 747, 751, 753, 757, 761,
- 764, 769, 773, 779, 785, 787, 791, 793, 796, 798,
- 802, 807, 809, 812, 815, 817, 819, 823, 824, 827,
- 828, 830, 832, 834, 836, 838, 840, 842, 844, 846,
- 847, 852, 854, 857, 860, 863, 866, 869, 872, 874,
- 878, 880, 884, 886, 890, 892, 896, 898, 902, 904,
- 906, 910, 914, 915, 918, 919, 921, 922, 924, 925,
- 927, 928, 930, 931, 933, 934, 936, 937, 939, 940,
- 942, 943, 945, 950, 955, 961, 968, 973, 978, 980,
- 982, 984, 986, 988, 990, 992, 994, 996, 1000, 1005,
- 1011, 1016, 1021, 1024, 1027, 1032, 1036, 1040, 1046, 1050,
- 1055, 1059, 1065, 1067, 1068, 1070, 1074, 1076, 1078, 1081,
- 1083, 1085, 1091, 1092, 1095, 1097, 1101, 1103, 1107, 1109
+ 439, 445, 450, 457, 466, 468, 474, 480, 486, 494,
+ 496, 497, 501, 503, 508, 510, 515, 517, 521, 523,
+ 525, 527, 529, 531, 533, 535, 536, 538, 540, 542,
+ 544, 549, 554, 556, 558, 560, 563, 565, 567, 569,
+ 571, 573, 577, 579, 581, 583, 586, 588, 590, 592,
+ 594, 598, 600, 602, 604, 606, 608, 610, 612, 614,
+ 616, 620, 625, 630, 633, 637, 643, 645, 647, 650,
+ 654, 660, 664, 670, 674, 678, 684, 693, 699, 708,
+ 714, 715, 719, 720, 722, 726, 728, 733, 736, 737,
+ 741, 743, 747, 749, 753, 755, 759, 761, 765, 767,
+ 771, 775, 778, 783, 787, 793, 799, 801, 805, 807,
+ 810, 812, 816, 821, 823, 826, 829, 831, 833, 837,
+ 838, 841, 842, 844, 846, 848, 850, 852, 854, 856,
+ 858, 860, 861, 866, 868, 871, 874, 877, 880, 883,
+ 886, 888, 892, 894, 898, 900, 904, 906, 910, 912,
+ 916, 918, 920, 924, 928, 929, 932, 933, 935, 936,
+ 938, 939, 941, 942, 944, 945, 947, 948, 950, 951,
+ 953, 954, 956, 957, 959, 964, 969, 975, 982, 987,
+ 992, 994, 996, 998, 1000, 1002, 1004, 1006, 1008, 1010,
+ 1014, 1019, 1025, 1030, 1035, 1038, 1041, 1046, 1050, 1054,
+ 1060, 1064, 1069, 1073, 1079, 1081, 1082, 1084, 1088, 1090,
+ 1092, 1095, 1097, 1099, 1105, 1106, 1109, 1111, 1115, 1117,
+ 1121, 1123
};
/* YYRHS -- A `-1'-separated list of the rules' RHS. */
@@ -584,113 +602,115 @@ static const yytype_int16 yyrhs[] =
187, 11, 191, 60, -1, 3, -1, 143, -1, 134,
63, 141, -1, 134, 63, 59, 135, 60, -1, 134,
63, 59, 31, 60, -1, 134, 71, 126, 72, -1,
- 134, 71, 192, 66, 192, 72, -1, 128, -1, 149,
- 59, 126, 191, 60, -1, 150, 137, 130, 189, 68,
- -1, 129, 67, 130, 189, 68, -1, 59, 135, 60,
- 67, 130, 189, 68, -1, 165, -1, -1, 126, 66,
- 133, -1, 126, -1, 67, 130, 189, 68, -1, 126,
- -1, 67, 130, 189, 68, -1, 129, -1, 59, 135,
- 60, -1, 126, -1, 147, -1, 146, -1, 35, -1,
- 67, -1, 141, -1, 141, -1, -1, 138, -1, 24,
- -1, 142, -1, 73, -1, 74, 3, 63, 24, -1,
- 141, -1, 138, -1, 11, -1, 11, 146, -1, 155,
- -1, 161, -1, 153, -1, 154, -1, 152, -1, 59,
- 146, 60, -1, 155, -1, 161, -1, 153, -1, 53,
- 147, -1, 161, -1, 153, -1, 154, -1, 152, -1,
- 59, 146, 60, -1, 161, -1, 153, -1, 153, -1,
- 155, -1, 161, -1, 153, -1, 154, -1, 152, -1,
- 143, -1, 143, 63, 141, -1, 71, 192, 72, 146,
- -1, 71, 11, 72, 146, -1, 8, 148, -1, 8,
- 36, 146, -1, 23, 71, 146, 72, 146, -1, 156,
- -1, 157, -1, 53, 146, -1, 36, 8, 146, -1,
- 29, 137, 170, 190, 68, -1, 29, 137, 68, -1,
- 22, 137, 171, 190, 68, -1, 22, 137, 68, -1,
- 17, 159, 162, -1, 141, 59, 179, 60, 163, -1,
- 59, 179, 60, 141, 59, 179, 60, 163, -1, 200,
- 59, 195, 60, 210, -1, 59, 215, 60, 141, 59,
- 195, 60, 210, -1, 17, 59, 179, 60, 163, -1,
- -1, 67, 183, 68, -1, -1, 151, -1, 59, 179,
- 60, -1, 161, -1, 164, 137, 183, 68, -1, 164,
- 1, -1, -1, 166, 90, 62, -1, 93, -1, 167,
- 62, 93, -1, 95, -1, 168, 62, 95, -1, 97,
- -1, 169, 62, 97, -1, 172, -1, 170, 62, 172,
- -1, 175, -1, 171, 62, 175, -1, 184, 146, 198,
- -1, 174, 198, -1, 59, 174, 60, 198, -1, 53,
- 174, 198, -1, 59, 53, 174, 60, 198, -1, 53,
- 59, 174, 60, 198, -1, 24, -1, 24, 63, 141,
- -1, 173, -1, 138, 176, -1, 173, -1, 59, 173,
- 60, -1, 59, 179, 60, 163, -1, 136, -1, 141,
- 136, -1, 141, 145, -1, 145, -1, 177, -1, 178,
- 75, 177, -1, -1, 178, 191, -1, -1, 100, -1,
- 91, -1, 181, -1, 1, -1, 98, -1, 110, -1,
- 121, -1, 124, -1, 113, -1, -1, 144, 66, 182,
- 180, -1, 15, -1, 6, 140, -1, 10, 140, -1,
- 18, 128, -1, 13, 128, -1, 19, 138, -1, 27,
- 193, -1, 180, -1, 183, 62, 180, -1, 138, -1,
- 184, 75, 138, -1, 139, -1, 185, 75, 139, -1,
- 126, -1, 186, 75, 126, -1, 135, -1, 187, 75,
- 135, -1, 131, -1, 132, -1, 188, 75, 131, -1,
- 188, 75, 132, -1, -1, 188, 191, -1, -1, 62,
- -1, -1, 75, -1, -1, 126, -1, -1, 186, -1,
- -1, 98, -1, -1, 215, -1, -1, 216, -1, -1,
- 217, -1, -1, 3, -1, 21, 24, 3, 62, -1,
- 32, 200, 202, 62, -1, 9, 200, 65, 213, 62,
- -1, 9, 200, 202, 65, 213, 62, -1, 31, 201,
- 202, 62, -1, 17, 160, 162, 62, -1, 142, -1,
- 200, -1, 204, -1, 205, -1, 206, -1, 204, -1,
- 206, -1, 142, -1, 24, -1, 71, 72, 202, -1,
- 71, 3, 72, 202, -1, 23, 71, 202, 72, 202,
- -1, 29, 67, 196, 68, -1, 22, 67, 197, 68,
- -1, 53, 202, -1, 8, 203, -1, 8, 59, 205,
- 60, -1, 8, 36, 202, -1, 36, 8, 202, -1,
- 17, 59, 195, 60, 210, -1, 141, 202, 198, -1,
- 141, 11, 202, 198, -1, 141, 202, 198, -1, 141,
- 59, 195, 60, 210, -1, 202, -1, -1, 211, -1,
- 59, 195, 60, -1, 202, -1, 3, -1, 50, 3,
- -1, 141, -1, 212, -1, 59, 212, 49, 212, 60,
- -1, -1, 214, 199, -1, 207, -1, 215, 75, 207,
- -1, 208, -1, 216, 62, 208, -1, 209, -1, 217,
- 62, 209, -1
+ 134, 71, 192, 66, 192, 72, -1, 134, 71, 192,
+ 66, 192, 66, 192, 72, -1, 128, -1, 149, 59,
+ 126, 191, 60, -1, 150, 137, 130, 189, 68, -1,
+ 129, 67, 130, 189, 68, -1, 59, 135, 60, 67,
+ 130, 189, 68, -1, 165, -1, -1, 126, 66, 133,
+ -1, 126, -1, 67, 130, 189, 68, -1, 126, -1,
+ 67, 130, 189, 68, -1, 129, -1, 59, 135, 60,
+ -1, 126, -1, 147, -1, 146, -1, 35, -1, 67,
+ -1, 141, -1, 141, -1, -1, 138, -1, 24, -1,
+ 142, -1, 73, -1, 74, 3, 63, 24, -1, 74,
+ 3, 63, 73, -1, 141, -1, 138, -1, 11, -1,
+ 11, 146, -1, 155, -1, 161, -1, 153, -1, 154,
+ -1, 152, -1, 59, 146, 60, -1, 155, -1, 161,
+ -1, 153, -1, 53, 147, -1, 161, -1, 153, -1,
+ 154, -1, 152, -1, 59, 146, 60, -1, 161, -1,
+ 153, -1, 153, -1, 155, -1, 161, -1, 153, -1,
+ 154, -1, 152, -1, 143, -1, 143, 63, 141, -1,
+ 71, 192, 72, 146, -1, 71, 11, 72, 146, -1,
+ 8, 148, -1, 8, 36, 146, -1, 23, 71, 146,
+ 72, 146, -1, 156, -1, 157, -1, 53, 146, -1,
+ 36, 8, 146, -1, 29, 137, 170, 190, 68, -1,
+ 29, 137, 68, -1, 22, 137, 171, 190, 68, -1,
+ 22, 137, 68, -1, 17, 159, 162, -1, 141, 59,
+ 179, 60, 163, -1, 59, 179, 60, 141, 59, 179,
+ 60, 163, -1, 200, 59, 195, 60, 210, -1, 59,
+ 215, 60, 141, 59, 195, 60, 210, -1, 17, 59,
+ 179, 60, 163, -1, -1, 67, 183, 68, -1, -1,
+ 151, -1, 59, 179, 60, -1, 161, -1, 164, 137,
+ 183, 68, -1, 164, 1, -1, -1, 166, 90, 62,
+ -1, 93, -1, 167, 62, 93, -1, 95, -1, 168,
+ 62, 95, -1, 97, -1, 169, 62, 97, -1, 172,
+ -1, 170, 62, 172, -1, 175, -1, 171, 62, 175,
+ -1, 184, 146, 198, -1, 174, 198, -1, 59, 174,
+ 60, 198, -1, 53, 174, 198, -1, 59, 53, 174,
+ 60, 198, -1, 53, 59, 174, 60, 198, -1, 24,
+ -1, 24, 63, 141, -1, 173, -1, 138, 176, -1,
+ 173, -1, 59, 173, 60, -1, 59, 179, 60, 163,
+ -1, 136, -1, 141, 136, -1, 141, 145, -1, 145,
+ -1, 177, -1, 178, 75, 177, -1, -1, 178, 191,
+ -1, -1, 100, -1, 91, -1, 181, -1, 1, -1,
+ 98, -1, 110, -1, 121, -1, 124, -1, 113, -1,
+ -1, 144, 66, 182, 180, -1, 15, -1, 6, 140,
+ -1, 10, 140, -1, 18, 128, -1, 13, 128, -1,
+ 19, 138, -1, 27, 193, -1, 180, -1, 183, 62,
+ 180, -1, 138, -1, 184, 75, 138, -1, 139, -1,
+ 185, 75, 139, -1, 126, -1, 186, 75, 126, -1,
+ 135, -1, 187, 75, 135, -1, 131, -1, 132, -1,
+ 188, 75, 131, -1, 188, 75, 132, -1, -1, 188,
+ 191, -1, -1, 62, -1, -1, 75, -1, -1, 126,
+ -1, -1, 186, -1, -1, 98, -1, -1, 215, -1,
+ -1, 216, -1, -1, 217, -1, -1, 3, -1, 21,
+ 24, 3, 62, -1, 32, 200, 202, 62, -1, 9,
+ 200, 65, 213, 62, -1, 9, 200, 202, 65, 213,
+ 62, -1, 31, 201, 202, 62, -1, 17, 160, 162,
+ 62, -1, 142, -1, 200, -1, 204, -1, 205, -1,
+ 206, -1, 204, -1, 206, -1, 142, -1, 24, -1,
+ 71, 72, 202, -1, 71, 3, 72, 202, -1, 23,
+ 71, 202, 72, 202, -1, 29, 67, 196, 68, -1,
+ 22, 67, 197, 68, -1, 53, 202, -1, 8, 203,
+ -1, 8, 59, 205, 60, -1, 8, 36, 202, -1,
+ 36, 8, 202, -1, 17, 59, 195, 60, 210, -1,
+ 141, 202, 198, -1, 141, 11, 202, 198, -1, 141,
+ 202, 198, -1, 141, 59, 195, 60, 210, -1, 202,
+ -1, -1, 211, -1, 59, 195, 60, -1, 202, -1,
+ 3, -1, 50, 3, -1, 141, -1, 212, -1, 59,
+ 212, 49, 212, 60, -1, -1, 214, 199, -1, 207,
+ -1, 215, 75, 207, -1, 208, -1, 216, 62, 208,
+ -1, 209, -1, 217, 62, 209, -1
};
/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
static const yytype_uint16 yyrline[] =
{
- 0, 124, 124, 133, 140, 151, 151, 166, 167, 170,
- 171, 172, 175, 208, 219, 220, 223, 230, 237, 246,
- 260, 261, 268, 268, 281, 285, 286, 290, 295, 301,
- 305, 309, 313, 319, 325, 331, 336, 340, 344, 350,
- 356, 360, 364, 370, 374, 380, 381, 385, 391, 400,
- 406, 424, 429, 441, 457, 462, 469, 489, 507, 516,
- 535, 534, 549, 548, 579, 582, 589, 588, 599, 605,
- 614, 625, 631, 634, 642, 641, 652, 658, 670, 674,
- 679, 669, 700, 699, 712, 715, 721, 724, 736, 740,
- 735, 758, 757, 773, 774, 778, 782, 786, 790, 794,
- 798, 802, 806, 810, 814, 818, 822, 826, 830, 834,
- 838, 842, 846, 851, 857, 858, 862, 873, 877, 881,
- 885, 890, 894, 904, 908, 913, 921, 925, 926, 937,
- 941, 945, 949, 953, 954, 960, 967, 973, 980, 983,
- 990, 996, 1013, 1020, 1021, 1028, 1029, 1048, 1049, 1052,
- 1055, 1059, 1070, 1079, 1085, 1088, 1091, 1098, 1099, 1105,
- 1120, 1128, 1140, 1145, 1151, 1152, 1153, 1154, 1155, 1156,
- 1162, 1163, 1164, 1165, 1171, 1172, 1173, 1174, 1175, 1181,
- 1182, 1185, 1188, 1189, 1190, 1191, 1192, 1195, 1196, 1209,
- 1213, 1218, 1223, 1228, 1232, 1233, 1236, 1242, 1249, 1255,
- 1262, 1268, 1279, 1293, 1322, 1362, 1387, 1405, 1414, 1417,
- 1425, 1429, 1433, 1440, 1446, 1451, 1463, 1466, 1476, 1477,
- 1483, 1484, 1490, 1494, 1500, 1501, 1507, 1511, 1517, 1540,
- 1545, 1551, 1557, 1564, 1573, 1582, 1597, 1603, 1608, 1612,
- 1619, 1632, 1633, 1639, 1645, 1648, 1652, 1658, 1661, 1670,
- 1673, 1674, 1678, 1679, 1685, 1686, 1687, 1688, 1689, 1691,
- 1690, 1705, 1710, 1714, 1718, 1722, 1726, 1731, 1750, 1756,
- 1764, 1768, 1774, 1778, 1784, 1788, 1794, 1798, 1807, 1811,
- 1815, 1819, 1825, 1828, 1836, 1837, 1839, 1840, 1843, 1846,
- 1849, 1852, 1855, 1858, 1861, 1864, 1867, 1870, 1873, 1876,
- 1879, 1882, 1888, 1892, 1896, 1900, 1904, 1908, 1928, 1935,
- 1946, 1947, 1948, 1951, 1952, 1955, 1959, 1969, 1973, 1977,
- 1981, 1985, 1989, 1993, 1999, 2005, 2013, 2021, 2027, 2034,
- 2050, 2068, 2072, 2078, 2081, 2084, 2088, 2098, 2102, 2117,
- 2125, 2126, 2138, 2139, 2142, 2146, 2152, 2156, 2162, 2166
+ 0, 124, 124, 133, 139, 150, 150, 165, 166, 169,
+ 170, 171, 174, 211, 222, 223, 226, 233, 240, 249,
+ 263, 264, 271, 271, 284, 288, 289, 293, 298, 304,
+ 308, 312, 316, 322, 328, 334, 339, 343, 347, 353,
+ 359, 363, 367, 373, 377, 383, 384, 388, 394, 403,
+ 409, 427, 432, 444, 460, 465, 472, 492, 510, 519,
+ 538, 537, 552, 551, 582, 585, 592, 591, 602, 608,
+ 617, 628, 634, 637, 645, 644, 655, 661, 673, 677,
+ 682, 672, 703, 702, 715, 718, 724, 727, 739, 743,
+ 738, 761, 760, 776, 777, 781, 785, 789, 793, 797,
+ 801, 805, 809, 813, 817, 821, 825, 829, 833, 837,
+ 841, 845, 849, 854, 860, 861, 865, 876, 880, 884,
+ 888, 893, 897, 907, 911, 916, 924, 928, 929, 940,
+ 944, 948, 952, 956, 964, 965, 971, 978, 984, 991,
+ 994, 1001, 1007, 1024, 1031, 1032, 1039, 1040, 1059, 1060,
+ 1063, 1066, 1070, 1081, 1090, 1096, 1099, 1102, 1109, 1110,
+ 1116, 1129, 1144, 1152, 1164, 1169, 1175, 1176, 1177, 1178,
+ 1179, 1180, 1186, 1187, 1188, 1189, 1195, 1196, 1197, 1198,
+ 1199, 1205, 1206, 1209, 1212, 1213, 1214, 1215, 1216, 1219,
+ 1220, 1233, 1237, 1242, 1247, 1252, 1256, 1257, 1260, 1266,
+ 1273, 1279, 1286, 1292, 1303, 1317, 1346, 1386, 1411, 1429,
+ 1438, 1441, 1449, 1453, 1457, 1464, 1470, 1475, 1487, 1490,
+ 1500, 1501, 1507, 1508, 1514, 1518, 1524, 1525, 1531, 1535,
+ 1541, 1564, 1569, 1575, 1581, 1588, 1597, 1606, 1621, 1627,
+ 1632, 1636, 1643, 1656, 1657, 1663, 1669, 1672, 1676, 1682,
+ 1685, 1694, 1697, 1698, 1702, 1703, 1709, 1710, 1711, 1712,
+ 1713, 1715, 1714, 1729, 1734, 1738, 1742, 1746, 1750, 1755,
+ 1774, 1780, 1788, 1792, 1798, 1802, 1808, 1812, 1818, 1822,
+ 1831, 1835, 1839, 1843, 1849, 1852, 1860, 1861, 1863, 1864,
+ 1867, 1870, 1873, 1876, 1879, 1882, 1885, 1888, 1891, 1894,
+ 1897, 1900, 1903, 1906, 1912, 1916, 1920, 1924, 1928, 1932,
+ 1952, 1959, 1970, 1971, 1972, 1975, 1976, 1979, 1983, 1993,
+ 1997, 2001, 2005, 2009, 2013, 2017, 2023, 2029, 2037, 2045,
+ 2051, 2058, 2074, 2096, 2100, 2106, 2109, 2112, 2116, 2126,
+ 2130, 2145, 2153, 2154, 2166, 2167, 2170, 2174, 2180, 2184,
+ 2190, 2194
};
#endif
@@ -709,16 +729,16 @@ const char *yytname[] =
"'/'", "'%'", "'&'", "NotPackage", "NotParen", "'('", "')'",
"PreferToRightParen", "';'", "'.'", "'$'", "'='", "':'", "'{'", "'}'",
"'!'", "'~'", "'['", "']'", "'?'", "'@'", "','", "$accept", "file",
- "package", "loadsys", "@1", "imports", "import", "import_stmt",
+ "package", "loadsys", "$@1", "imports", "import", "import_stmt",
"import_stmt_list", "import_here", "import_package", "import_safety",
- "import_there", "@2", "xdcl", "common_dcl", "lconst", "vardcl",
+ "import_there", "$@2", "xdcl", "common_dcl", "lconst", "vardcl",
"constdcl", "constdcl1", "typedclname", "typedcl", "simple_stmt", "case",
- "compound_stmt", "@3", "caseblock", "@4", "caseblock_list", "loop_body",
- "@5", "range_stmt", "for_header", "for_body", "for_stmt", "@6",
- "if_header", "if_stmt", "@7", "@8", "@9", "elseif", "@10", "elseif_list",
- "else", "switch_stmt", "@11", "@12", "select_stmt", "@13", "expr",
- "uexpr", "pseudocall", "pexpr_no_paren", "start_complit", "keyval",
- "bare_complitexpr", "complitexpr", "pexpr", "expr_or_type",
+ "compound_stmt", "$@3", "caseblock", "$@4", "caseblock_list",
+ "loop_body", "$@5", "range_stmt", "for_header", "for_body", "for_stmt",
+ "$@6", "if_header", "if_stmt", "$@7", "$@8", "$@9", "elseif", "$@10",
+ "elseif_list", "else", "switch_stmt", "$@11", "$@12", "select_stmt",
+ "$@13", "expr", "uexpr", "pseudocall", "pexpr_no_paren", "start_complit",
+ "keyval", "bare_complitexpr", "complitexpr", "pexpr", "expr_or_type",
"name_or_type", "lbrace", "new_name", "dcl_name", "onew_name", "sym",
"hidden_importsym", "name", "labelname", "dotdotdot", "ntype",
"non_expr_type", "non_recvchantype", "convtype", "comptype",
@@ -728,7 +748,7 @@ const char *yytname[] =
"vardcl_list", "constdcl_list", "typedcl_list", "structdcl_list",
"interfacedcl_list", "structdcl", "packname", "embed", "interfacedcl",
"indcl", "arg_type", "arg_type_list", "oarg_type_list_ocomma", "stmt",
- "non_dcl_stmt", "@14", "stmt_list", "new_name_list", "dcl_name_list",
+ "non_dcl_stmt", "$@14", "stmt_list", "new_name_list", "dcl_name_list",
"expr_list", "expr_or_type_list", "keyval_list", "braced_keyval_list",
"osemi", "ocomma", "oexpr", "oexpr_list", "osimple_stmt",
"ohidden_funarg_list", "ohidden_structdcl_list",
@@ -775,28 +795,29 @@ static const yytype_uint8 yyr1[] =
126, 126, 126, 126, 126, 126, 126, 126, 126, 126,
126, 126, 126, 126, 127, 127, 127, 127, 127, 127,
127, 127, 127, 128, 128, 128, 129, 129, 129, 129,
- 129, 129, 129, 129, 129, 129, 129, 129, 129, 130,
- 131, 132, 132, 133, 133, 134, 134, 135, 135, 136,
- 137, 137, 138, 139, 140, 140, 141, 141, 141, 142,
- 143, 144, 145, 145, 146, 146, 146, 146, 146, 146,
- 147, 147, 147, 147, 148, 148, 148, 148, 148, 149,
- 149, 150, 151, 151, 151, 151, 151, 152, 152, 153,
- 153, 153, 153, 153, 153, 153, 154, 155, 156, 156,
- 157, 157, 158, 159, 159, 160, 160, 161, 162, 162,
- 163, 163, 163, 164, 165, 165, 166, 166, 167, 167,
- 168, 168, 169, 169, 170, 170, 171, 171, 172, 172,
- 172, 172, 172, 172, 173, 173, 174, 175, 175, 175,
- 176, 177, 177, 177, 177, 178, 178, 179, 179, 180,
- 180, 180, 180, 180, 181, 181, 181, 181, 181, 182,
- 181, 181, 181, 181, 181, 181, 181, 181, 183, 183,
- 184, 184, 185, 185, 186, 186, 187, 187, 188, 188,
- 188, 188, 189, 189, 190, 190, 191, 191, 192, 192,
- 193, 193, 194, 194, 195, 195, 196, 196, 197, 197,
- 198, 198, 199, 199, 199, 199, 199, 199, 200, 201,
- 202, 202, 202, 203, 203, 204, 204, 204, 204, 204,
- 204, 204, 204, 204, 204, 204, 205, 206, 207, 207,
- 208, 209, 209, 210, 210, 211, 211, 212, 212, 212,
- 213, 213, 214, 214, 215, 215, 216, 216, 217, 217
+ 129, 129, 129, 129, 129, 129, 129, 129, 129, 129,
+ 130, 131, 132, 132, 133, 133, 134, 134, 135, 135,
+ 136, 137, 137, 138, 139, 140, 140, 141, 141, 141,
+ 142, 142, 143, 144, 145, 145, 146, 146, 146, 146,
+ 146, 146, 147, 147, 147, 147, 148, 148, 148, 148,
+ 148, 149, 149, 150, 151, 151, 151, 151, 151, 152,
+ 152, 153, 153, 153, 153, 153, 153, 153, 154, 155,
+ 156, 156, 157, 157, 158, 159, 159, 160, 160, 161,
+ 162, 162, 163, 163, 163, 164, 165, 165, 166, 166,
+ 167, 167, 168, 168, 169, 169, 170, 170, 171, 171,
+ 172, 172, 172, 172, 172, 172, 173, 173, 174, 175,
+ 175, 175, 176, 177, 177, 177, 177, 178, 178, 179,
+ 179, 180, 180, 180, 180, 180, 181, 181, 181, 181,
+ 181, 182, 181, 181, 181, 181, 181, 181, 181, 181,
+ 183, 183, 184, 184, 185, 185, 186, 186, 187, 187,
+ 188, 188, 188, 188, 189, 189, 190, 190, 191, 191,
+ 192, 192, 193, 193, 194, 194, 195, 195, 196, 196,
+ 197, 197, 198, 198, 199, 199, 199, 199, 199, 199,
+ 200, 201, 202, 202, 202, 203, 203, 204, 204, 204,
+ 204, 204, 204, 204, 204, 204, 204, 204, 205, 206,
+ 207, 207, 208, 209, 209, 210, 210, 211, 211, 212,
+ 212, 212, 213, 213, 214, 214, 215, 215, 216, 216,
+ 217, 217
};
/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
@@ -815,669 +836,691 @@ static const yytype_uint8 yyr2[] =
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 1, 2, 2, 2, 2, 2,
2, 2, 2, 3, 5, 6, 1, 1, 3, 5,
- 5, 4, 6, 1, 5, 5, 5, 7, 1, 0,
- 3, 1, 4, 1, 4, 1, 3, 1, 1, 1,
- 1, 1, 1, 1, 0, 1, 1, 1, 1, 4,
- 1, 1, 1, 2, 1, 1, 1, 1, 1, 3,
- 1, 1, 1, 2, 1, 1, 1, 1, 3, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 3, 4,
- 4, 2, 3, 5, 1, 1, 2, 3, 5, 3,
- 5, 3, 3, 5, 8, 5, 8, 5, 0, 3,
- 0, 1, 3, 1, 4, 2, 0, 3, 1, 3,
- 1, 3, 1, 3, 1, 3, 1, 3, 3, 2,
- 4, 3, 5, 5, 1, 3, 1, 2, 1, 3,
- 4, 1, 2, 2, 1, 1, 3, 0, 2, 0,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
- 4, 1, 2, 2, 2, 2, 2, 2, 1, 3,
- 1, 3, 1, 3, 1, 3, 1, 3, 1, 1,
- 3, 3, 0, 2, 0, 1, 0, 1, 0, 1,
+ 5, 4, 6, 8, 1, 5, 5, 5, 7, 1,
+ 0, 3, 1, 4, 1, 4, 1, 3, 1, 1,
+ 1, 1, 1, 1, 1, 0, 1, 1, 1, 1,
+ 4, 4, 1, 1, 1, 2, 1, 1, 1, 1,
+ 1, 3, 1, 1, 1, 2, 1, 1, 1, 1,
+ 3, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 3, 4, 4, 2, 3, 5, 1, 1, 2, 3,
+ 5, 3, 5, 3, 3, 5, 8, 5, 8, 5,
+ 0, 3, 0, 1, 3, 1, 4, 2, 0, 3,
+ 1, 3, 1, 3, 1, 3, 1, 3, 1, 3,
+ 3, 2, 4, 3, 5, 5, 1, 3, 1, 2,
+ 1, 3, 4, 1, 2, 2, 1, 1, 3, 0,
+ 2, 0, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 0, 4, 1, 2, 2, 2, 2, 2, 2,
+ 1, 3, 1, 3, 1, 3, 1, 3, 1, 3,
+ 1, 1, 3, 3, 0, 2, 0, 1, 0, 1,
0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
- 0, 1, 4, 4, 5, 6, 4, 4, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 3, 4, 5,
- 4, 4, 2, 2, 4, 3, 3, 5, 3, 4,
- 3, 5, 1, 0, 1, 3, 1, 1, 2, 1,
- 1, 5, 0, 2, 1, 3, 1, 3, 1, 3
+ 0, 1, 0, 1, 4, 4, 5, 6, 4, 4,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 3,
+ 4, 5, 4, 4, 2, 2, 4, 3, 3, 5,
+ 3, 4, 3, 5, 1, 0, 1, 3, 1, 1,
+ 2, 1, 1, 5, 0, 2, 1, 3, 1, 3,
+ 1, 3
};
-/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
- STATE-NUM when YYTABLE doesn't specify something else to do. Zero
+/* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM.
+ Performed when YYTABLE doesn't specify something else to do. Zero
means the default is an error. */
static const yytype_uint16 yydefact[] =
{
- 5, 0, 3, 0, 1, 0, 7, 0, 22, 156,
- 158, 0, 0, 157, 216, 20, 6, 342, 0, 4,
+ 5, 0, 3, 0, 1, 0, 7, 0, 22, 157,
+ 159, 0, 0, 158, 218, 20, 6, 344, 0, 4,
0, 0, 0, 21, 0, 0, 0, 16, 0, 0,
- 9, 22, 0, 8, 28, 126, 154, 0, 39, 154,
- 0, 261, 74, 0, 0, 0, 78, 0, 0, 290,
+ 9, 22, 0, 8, 28, 126, 155, 0, 39, 155,
+ 0, 263, 74, 0, 0, 0, 78, 0, 0, 292,
91, 0, 88, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 288, 0, 25, 0, 254, 255,
- 258, 256, 257, 50, 93, 133, 145, 114, 161, 160,
- 127, 0, 0, 0, 181, 194, 195, 26, 213, 0,
- 138, 27, 0, 19, 0, 0, 0, 0, 0, 0,
- 343, 159, 11, 14, 284, 18, 22, 13, 17, 155,
- 262, 152, 0, 0, 0, 0, 160, 187, 191, 177,
- 175, 176, 174, 263, 133, 0, 292, 247, 0, 208,
- 133, 266, 292, 150, 151, 0, 0, 274, 291, 267,
- 0, 0, 292, 0, 0, 36, 48, 0, 29, 272,
- 153, 0, 122, 117, 118, 121, 115, 116, 0, 0,
- 147, 0, 148, 172, 170, 171, 119, 120, 0, 289,
- 0, 217, 0, 32, 0, 0, 0, 0, 0, 55,
- 0, 0, 0, 54, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 139, 0,
- 0, 288, 259, 0, 139, 215, 0, 0, 0, 0,
- 308, 0, 0, 208, 0, 0, 309, 0, 0, 23,
- 285, 0, 12, 247, 0, 0, 192, 168, 166, 167,
- 164, 165, 196, 0, 0, 293, 72, 0, 75, 0,
- 71, 162, 241, 160, 244, 149, 245, 286, 0, 247,
- 0, 202, 79, 76, 156, 0, 201, 0, 284, 238,
- 226, 0, 64, 0, 0, 199, 270, 284, 224, 236,
- 300, 0, 89, 38, 222, 284, 49, 31, 218, 284,
- 0, 0, 40, 0, 173, 146, 0, 0, 35, 284,
- 0, 0, 51, 95, 110, 113, 96, 100, 101, 99,
- 111, 98, 97, 94, 112, 102, 103, 104, 105, 106,
- 107, 108, 109, 282, 123, 276, 286, 0, 128, 289,
- 0, 0, 286, 282, 253, 60, 251, 250, 268, 252,
- 0, 53, 52, 275, 0, 0, 0, 0, 316, 0,
- 0, 0, 0, 0, 315, 0, 310, 311, 312, 0,
- 344, 0, 0, 294, 0, 0, 0, 15, 10, 0,
- 0, 0, 178, 188, 66, 73, 0, 0, 292, 163,
- 242, 243, 287, 248, 210, 0, 0, 0, 292, 0,
- 234, 0, 247, 237, 285, 0, 0, 0, 0, 300,
- 0, 0, 285, 0, 301, 229, 0, 300, 0, 285,
- 0, 285, 0, 42, 273, 0, 0, 0, 197, 168,
- 166, 167, 165, 139, 190, 189, 285, 0, 44, 0,
- 139, 141, 278, 279, 286, 0, 286, 287, 0, 0,
- 0, 131, 288, 260, 287, 0, 0, 0, 0, 214,
- 0, 0, 323, 313, 314, 294, 298, 0, 296, 0,
- 322, 337, 0, 0, 339, 340, 0, 0, 0, 0,
- 0, 300, 0, 0, 307, 0, 295, 302, 306, 303,
- 210, 169, 0, 0, 0, 0, 246, 247, 160, 211,
- 186, 184, 185, 182, 183, 207, 210, 209, 80, 77,
- 235, 239, 0, 227, 200, 193, 0, 0, 92, 62,
- 65, 0, 231, 0, 300, 225, 198, 271, 228, 64,
- 223, 37, 219, 30, 41, 0, 282, 45, 220, 284,
- 47, 33, 43, 282, 0, 287, 283, 136, 0, 277,
- 124, 130, 129, 0, 134, 135, 0, 269, 325, 0,
- 0, 316, 0, 315, 0, 332, 348, 299, 0, 0,
- 0, 346, 297, 326, 338, 0, 304, 0, 317, 0,
- 300, 328, 0, 345, 333, 0, 69, 68, 292, 0,
- 247, 203, 84, 210, 0, 59, 0, 300, 300, 230,
- 0, 169, 0, 285, 0, 46, 0, 139, 143, 140,
- 280, 281, 125, 132, 61, 324, 333, 294, 321, 0,
- 0, 300, 320, 0, 0, 318, 305, 329, 294, 294,
- 336, 205, 334, 67, 70, 212, 0, 86, 240, 0,
- 0, 56, 0, 63, 233, 232, 90, 137, 221, 34,
- 142, 282, 327, 0, 349, 319, 330, 347, 0, 0,
- 0, 210, 0, 85, 81, 0, 0, 0, 333, 341,
- 333, 335, 204, 82, 87, 58, 57, 144, 331, 206,
- 292, 0, 83
+ 0, 0, 0, 0, 290, 0, 25, 0, 256, 257,
+ 260, 258, 259, 50, 93, 134, 146, 114, 163, 162,
+ 127, 0, 0, 0, 183, 196, 197, 26, 215, 0,
+ 139, 27, 0, 19, 0, 0, 0, 0, 0, 0,
+ 345, 160, 161, 11, 14, 286, 18, 22, 13, 17,
+ 156, 264, 153, 0, 0, 0, 0, 162, 189, 193,
+ 179, 177, 178, 176, 265, 134, 0, 294, 249, 0,
+ 210, 134, 268, 294, 151, 152, 0, 0, 276, 293,
+ 269, 0, 0, 294, 0, 0, 36, 48, 0, 29,
+ 274, 154, 0, 122, 117, 118, 121, 115, 116, 0,
+ 0, 148, 0, 149, 174, 172, 173, 119, 120, 0,
+ 291, 0, 219, 0, 32, 0, 0, 0, 0, 0,
+ 55, 0, 0, 0, 54, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 140,
+ 0, 0, 290, 261, 0, 140, 217, 0, 0, 0,
+ 0, 310, 0, 0, 210, 0, 0, 311, 0, 0,
+ 23, 287, 0, 12, 249, 0, 0, 194, 170, 168,
+ 169, 166, 167, 198, 0, 0, 295, 72, 0, 75,
+ 0, 71, 164, 243, 162, 246, 150, 247, 288, 0,
+ 249, 0, 204, 79, 76, 157, 0, 203, 0, 286,
+ 240, 228, 0, 64, 0, 0, 201, 272, 286, 226,
+ 238, 302, 0, 89, 38, 224, 286, 49, 31, 220,
+ 286, 0, 0, 40, 0, 175, 147, 0, 0, 35,
+ 286, 0, 0, 51, 95, 110, 113, 96, 100, 101,
+ 99, 111, 98, 97, 94, 112, 102, 103, 104, 105,
+ 106, 107, 108, 109, 284, 123, 278, 288, 0, 128,
+ 291, 0, 0, 288, 284, 255, 60, 253, 252, 270,
+ 254, 0, 53, 52, 277, 0, 0, 0, 0, 318,
+ 0, 0, 0, 0, 0, 317, 0, 312, 313, 314,
+ 0, 346, 0, 0, 296, 0, 0, 0, 15, 10,
+ 0, 0, 0, 180, 190, 66, 73, 0, 0, 294,
+ 165, 244, 245, 289, 250, 212, 0, 0, 0, 294,
+ 0, 236, 0, 249, 239, 287, 0, 0, 0, 0,
+ 302, 0, 0, 287, 0, 303, 231, 0, 302, 0,
+ 287, 0, 287, 0, 42, 275, 0, 0, 0, 199,
+ 170, 168, 169, 167, 140, 192, 191, 287, 0, 44,
+ 0, 140, 142, 280, 281, 288, 0, 288, 289, 0,
+ 0, 0, 131, 290, 262, 289, 0, 0, 0, 0,
+ 216, 0, 0, 325, 315, 316, 296, 300, 0, 298,
+ 0, 324, 339, 0, 0, 341, 342, 0, 0, 0,
+ 0, 0, 302, 0, 0, 309, 0, 297, 304, 308,
+ 305, 212, 171, 0, 0, 0, 0, 248, 249, 162,
+ 213, 188, 186, 187, 184, 185, 209, 212, 211, 80,
+ 77, 237, 241, 0, 229, 202, 195, 0, 0, 92,
+ 62, 65, 0, 233, 0, 302, 227, 200, 273, 230,
+ 64, 225, 37, 221, 30, 41, 0, 284, 45, 222,
+ 286, 47, 33, 43, 284, 0, 289, 285, 137, 0,
+ 279, 124, 130, 129, 0, 135, 136, 0, 271, 327,
+ 0, 0, 318, 0, 317, 0, 334, 350, 301, 0,
+ 0, 0, 348, 299, 328, 340, 0, 306, 0, 319,
+ 0, 302, 330, 0, 347, 335, 0, 69, 68, 294,
+ 0, 249, 205, 84, 212, 0, 59, 0, 302, 302,
+ 232, 0, 171, 0, 287, 0, 46, 0, 140, 144,
+ 141, 282, 283, 125, 290, 132, 61, 326, 335, 296,
+ 323, 0, 0, 302, 322, 0, 0, 320, 307, 331,
+ 296, 296, 338, 207, 336, 67, 70, 214, 0, 86,
+ 242, 0, 0, 56, 0, 63, 235, 234, 90, 138,
+ 223, 34, 143, 284, 0, 329, 0, 351, 321, 332,
+ 349, 0, 0, 0, 212, 0, 85, 81, 0, 0,
+ 0, 133, 335, 343, 335, 337, 206, 82, 87, 58,
+ 57, 145, 333, 208, 294, 0, 83
};
/* YYDEFGOTO[NTERM-NUM]. */
static const yytype_int16 yydefgoto[] =
{
- -1, 1, 6, 2, 3, 14, 21, 30, 104, 31,
- 8, 24, 16, 17, 65, 326, 67, 148, 517, 518,
- 144, 145, 68, 499, 327, 437, 500, 576, 387, 365,
- 472, 236, 237, 238, 69, 126, 252, 70, 132, 377,
- 572, 643, 660, 617, 644, 71, 142, 398, 72, 140,
- 73, 74, 75, 76, 313, 422, 423, 589, 77, 315,
- 242, 135, 78, 149, 110, 116, 13, 80, 81, 244,
- 245, 162, 118, 82, 83, 479, 227, 84, 229, 230,
- 85, 86, 87, 129, 213, 88, 251, 485, 89, 90,
- 22, 279, 519, 275, 267, 258, 268, 269, 270, 260,
- 383, 246, 247, 248, 328, 329, 321, 330, 271, 151,
- 92, 316, 424, 425, 221, 373, 170, 139, 253, 465,
- 550, 544, 395, 100, 211, 217, 610, 442, 346, 347,
- 348, 350, 551, 546, 611, 612, 455, 456, 25, 466,
- 552, 547
+ -1, 1, 6, 2, 3, 14, 21, 30, 105, 31,
+ 8, 24, 16, 17, 65, 327, 67, 149, 518, 519,
+ 145, 146, 68, 500, 328, 438, 501, 577, 388, 366,
+ 473, 237, 238, 239, 69, 127, 253, 70, 133, 378,
+ 573, 646, 664, 619, 647, 71, 143, 399, 72, 141,
+ 73, 74, 75, 76, 314, 423, 424, 590, 77, 316,
+ 243, 136, 78, 150, 111, 117, 13, 80, 81, 245,
+ 246, 163, 119, 82, 83, 480, 228, 84, 230, 231,
+ 85, 86, 87, 130, 214, 88, 252, 486, 89, 90,
+ 22, 280, 520, 276, 268, 259, 269, 270, 271, 261,
+ 384, 247, 248, 249, 329, 330, 322, 331, 272, 152,
+ 92, 317, 425, 426, 222, 374, 171, 140, 254, 466,
+ 551, 545, 396, 100, 212, 218, 612, 443, 347, 348,
+ 349, 351, 552, 547, 613, 614, 456, 457, 25, 467,
+ 553, 548
};
/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
STATE-NUM. */
-#define YYPACT_NINF -485
+#define YYPACT_NINF -474
static const yytype_int16 yypact[] =
{
- -485, 67, 35, 55, -485, 44, -485, 64, -485, -485,
- -485, 96, 38, -485, 77, 85, -485, -485, 66, -485,
- 34, 84, 1059, -485, 86, 294, 147, -485, 165, 210,
- -485, 55, 221, -485, -485, -485, 44, 1762, -485, 44,
- 290, -485, -485, 442, 290, 44, -485, 80, 69, 1608,
- -485, 80, -485, 450, 452, 1608, 1608, 1608, 1608, 1608,
- 1608, 1651, 1608, 1608, 920, 157, -485, 460, -485, -485,
- -485, -485, -485, 718, -485, -485, 167, 344, -485, 176,
- -485, 180, 193, 80, 206, -485, -485, -485, 218, 91,
- -485, -485, 76, -485, 205, 10, 260, 205, 205, 223,
- -485, -485, -485, -485, 230, -485, -485, -485, -485, -485,
- -485, -485, 237, 1770, 1770, 1770, -485, 236, -485, -485,
- -485, -485, -485, -485, 220, 344, 1608, 990, 241, 235,
- 262, -485, 1608, -485, -485, 405, 1770, 2090, 254, -485,
- 297, 444, 1608, 61, 1770, -485, -485, 271, -485, -485,
- -485, 671, -485, -485, -485, -485, -485, -485, 1694, 1651,
- 2090, 291, -485, 181, -485, 60, -485, -485, 287, 2090,
- 301, -485, 496, -485, 912, 1608, 1608, 1608, 1608, -485,
- 1608, 1608, 1608, -485, 1608, 1608, 1608, 1608, 1608, 1608,
- 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, -485, 1290,
- 468, 1608, -485, 1608, -485, -485, 1221, 1608, 1608, 1608,
- -485, 573, 44, 235, 275, 347, -485, 1301, 1301, -485,
- 113, 302, -485, 990, 358, 1770, -485, -485, -485, -485,
- -485, -485, -485, 316, 44, -485, -485, 340, -485, 78,
- 318, 1770, -485, 990, -485, -485, -485, 307, 325, 990,
- 1221, -485, -485, 324, 117, 365, -485, 343, 337, -485,
- -485, 333, -485, 32, 23, -485, -485, 350, -485, -485,
- 406, 1737, -485, -485, -485, 351, -485, -485, -485, 352,
- 1608, 44, 354, 1796, -485, 353, 1770, 1770, -485, 359,
- 1608, 357, 2090, 1928, -485, 2114, 1212, 1212, 1212, 1212,
- -485, 1212, 1212, 2138, -485, 566, 566, 566, 566, -485,
- -485, -485, -485, 1345, -485, -485, 31, 1400, -485, 1988,
- 360, 1147, 1955, 1345, -485, -485, -485, -485, -485, -485,
- 95, 254, 254, 2090, 1857, 368, 361, 371, -485, 363,
- 427, 1301, 247, 51, -485, 374, -485, -485, -485, 1890,
- -485, 36, 382, 44, 384, 385, 387, -485, -485, 391,
- 1770, 395, -485, -485, -485, -485, 1455, 1510, 1608, -485,
- -485, -485, 990, -485, 1823, 399, 135, 340, 1608, 44,
- 397, 403, 990, -485, 542, 407, 1770, 278, 365, 406,
- 365, 411, 364, 413, -485, -485, 44, 406, 430, 44,
- 423, 44, 425, 254, -485, 1608, 1849, 1770, -485, 216,
- 219, 274, 288, -485, -485, -485, 44, 426, 254, 1608,
- -485, 2018, -485, -485, 414, 422, 416, 1651, 433, 434,
- 436, -485, 1608, -485, -485, 439, 437, 1221, 1147, -485,
- 1301, 466, -485, -485, -485, 44, 1882, 1301, 44, 1301,
- -485, -485, 504, 207, -485, -485, 446, 438, 1301, 247,
- 1301, 406, 44, 44, -485, 453, 455, -485, -485, -485,
- 1823, -485, 1221, 1608, 1608, 467, -485, 990, 472, -485,
- -485, -485, -485, -485, -485, -485, 1823, -485, -485, -485,
- -485, -485, 475, -485, -485, -485, 1651, 470, -485, -485,
- -485, 490, -485, 493, 406, -485, -485, -485, -485, -485,
- -485, -485, -485, -485, 254, 495, 1345, -485, -485, 498,
- 912, -485, 254, 1345, 1553, 1345, -485, -485, 497, -485,
- -485, -485, -485, 486, -485, -485, 143, -485, -485, 501,
- 502, 473, 508, 513, 505, -485, -485, 515, 503, 1301,
- 511, -485, 518, -485, -485, 533, -485, 1301, -485, 522,
- 406, -485, 526, -485, 1916, 144, 2090, 2090, 1608, 527,
- 990, -485, -485, 1823, 39, -485, 1147, 406, 406, -485,
- 315, 293, 521, 44, 548, 357, 525, -485, 2090, -485,
- -485, -485, -485, -485, -485, -485, 1916, 44, -485, 1882,
- 1301, 406, -485, 44, 207, -485, -485, -485, 44, 44,
- -485, -485, -485, -485, -485, -485, 551, 572, -485, 1608,
- 1608, -485, 1651, 550, -485, -485, -485, -485, -485, -485,
- -485, 1345, -485, 558, -485, -485, -485, -485, 563, 564,
- 565, 1823, 46, -485, -485, 2042, 2066, 559, 1916, -485,
- 1916, -485, -485, -485, -485, -485, -485, -485, -485, -485,
- 1608, 340, -485
+ -474, 48, 28, 35, -474, 258, -474, 37, -474, -474,
+ -474, 61, 12, -474, 85, 107, -474, -474, 70, -474,
+ 156, 82, 1059, -474, 122, 328, 22, -474, 56, 199,
+ -474, 35, 211, -474, -474, -474, 258, 767, -474, 258,
+ 459, -474, -474, 152, 459, 258, -474, 23, 145, 1650,
+ -474, 23, -474, 294, 359, 1650, 1650, 1650, 1650, 1650,
+ 1650, 1693, 1650, 1650, 1289, 159, -474, 412, -474, -474,
+ -474, -474, -474, 939, -474, -474, 157, 302, -474, 168,
+ -474, 175, 184, 23, 204, -474, -474, -474, 219, 54,
+ -474, -474, 47, -474, 227, -12, 269, 227, 227, 239,
+ -474, -474, -474, -474, -474, 240, -474, -474, -474, -474,
+ -474, -474, -474, 250, 1813, 1813, 1813, -474, 259, -474,
+ -474, -474, -474, -474, -474, 64, 302, 1650, 1805, 262,
+ 260, 174, -474, 1650, -474, -474, 221, 1813, 2166, 255,
+ -474, 290, 237, 1650, 304, 1813, -474, -474, 420, -474,
+ -474, -474, 580, -474, -474, -474, -474, -474, -474, 1736,
+ 1693, 2166, 280, -474, 253, -474, 50, -474, -474, 275,
+ 2166, 285, -474, 430, -474, 612, 1650, 1650, 1650, 1650,
+ -474, 1650, 1650, 1650, -474, 1650, 1650, 1650, 1650, 1650,
+ 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, -474,
+ 1332, 428, 1650, -474, 1650, -474, -474, 1234, 1650, 1650,
+ 1650, -474, 763, 258, 260, 293, 369, -474, 1992, 1992,
+ -474, 51, 326, -474, 1805, 392, 1813, -474, -474, -474,
+ -474, -474, -474, -474, 341, 258, -474, -474, 371, -474,
+ 89, 342, 1813, -474, 1805, -474, -474, -474, 335, 360,
+ 1805, 1234, -474, -474, 357, 99, 399, -474, 365, 380,
+ -474, -474, 377, -474, 173, 151, -474, -474, 381, -474,
+ -474, 456, 1779, -474, -474, -474, 401, -474, -474, -474,
+ 404, 1650, 258, 366, 1838, -474, 405, 1813, 1813, -474,
+ 407, 1650, 410, 2166, 650, -474, 2190, 877, 877, 877,
+ 877, -474, 877, 877, 2214, -474, 461, 461, 461, 461,
+ -474, -474, -474, -474, 1387, -474, -474, 52, 1442, -474,
+ 2064, 411, 1160, 2031, 1387, -474, -474, -474, -474, -474,
+ -474, 19, 255, 255, 2166, 1905, 447, 441, 439, -474,
+ 444, 505, 1992, 225, 27, -474, 454, -474, -474, -474,
+ 1931, -474, 125, 458, 258, 460, 465, 466, -474, -474,
+ 463, 1813, 480, -474, -474, -474, -474, 1497, 1552, 1650,
+ -474, -474, -474, 1805, -474, 1872, 484, 24, 371, 1650,
+ 258, 485, 487, 1805, -474, 472, 481, 1813, 81, 399,
+ 456, 399, 490, 289, 483, -474, -474, 258, 456, 519,
+ 258, 495, 258, 496, 255, -474, 1650, 1897, 1813, -474,
+ 321, 349, 350, 354, -474, -474, -474, 258, 497, 255,
+ 1650, -474, 2094, -474, -474, 488, 491, 489, 1693, 498,
+ 500, 502, -474, 1650, -474, -474, 506, 503, 1234, 1160,
+ -474, 1992, 534, -474, -474, -474, 258, 1958, 1992, 258,
+ 1992, -474, -474, 565, 149, -474, -474, 510, 504, 1992,
+ 225, 1992, 456, 258, 258, -474, 514, 507, -474, -474,
+ -474, 1872, -474, 1234, 1650, 1650, 515, -474, 1805, 520,
+ -474, -474, -474, -474, -474, -474, -474, 1872, -474, -474,
+ -474, -474, -474, 518, -474, -474, -474, 1693, 517, -474,
+ -474, -474, 524, -474, 525, 456, -474, -474, -474, -474,
+ -474, -474, -474, -474, -474, 255, 526, 1387, -474, -474,
+ 527, 612, -474, 255, 1387, 1595, 1387, -474, -474, 530,
+ -474, -474, -474, -474, 116, -474, -474, 141, -474, -474,
+ 539, 540, 521, 542, 546, 538, -474, -474, 548, 543,
+ 1992, 549, -474, 552, -474, -474, 562, -474, 1992, -474,
+ 556, 456, -474, 560, -474, 1984, 238, 2166, 2166, 1650,
+ 561, 1805, -474, -474, 1872, 32, -474, 1160, 456, 456,
+ -474, 186, 370, 554, 258, 563, 410, 557, -474, 2166,
+ -474, -474, -474, -474, 1650, -474, -474, -474, 1984, 258,
+ -474, 1958, 1992, 456, -474, 258, 149, -474, -474, -474,
+ 258, 258, -474, -474, -474, -474, -474, -474, 564, 613,
+ -474, 1650, 1650, -474, 1693, 566, -474, -474, -474, -474,
+ -474, -474, -474, 1387, 558, -474, 571, -474, -474, -474,
+ -474, 577, 582, 583, 1872, 36, -474, -474, 2118, 2142,
+ 572, -474, 1984, -474, 1984, -474, -474, -474, -474, -474,
+ -474, -474, -474, -474, 1650, 371, -474
};
/* YYPGOTO[NTERM-NUM]. */
static const yytype_int16 yypgoto[] =
{
- -485, -485, -485, -485, -485, -485, -485, -6, -485, -485,
- 597, -485, -3, -485, -485, 608, -485, -131, -28, 50,
- -485, -135, -106, -485, -7, -485, -485, -485, 125, -370,
- -485, -485, -485, -485, -485, -485, -138, -485, -485, -485,
- -485, -485, -485, -485, -485, -485, -485, -485, -485, -485,
- 665, 15, 116, -485, -190, 111, 112, -485, 164, -59,
- 398, 137, 14, 367, 603, -5, 454, 432, -485, 402,
- -50, 491, -485, -485, -485, -485, -36, 18, -34, -9,
- -485, -485, -485, -485, -485, 257, 441, -445, -485, -485,
- -485, -485, -485, -485, -485, -485, 259, -116, -218, 265,
- -485, 284, -485, -217, -286, 636, -485, -237, -485, -62,
- -24, 166, -485, -314, -246, -265, -177, -485, -115, -415,
- -485, -485, -379, -485, -8, -485, 435, -485, 326, 225,
- 327, 204, 65, 70, -484, -485, -426, 211, -485, 462,
- -485, -485
+ -474, -474, -474, -474, -474, -474, -474, -15, -474, -474,
+ 616, -474, -3, -474, -474, 622, -474, -125, -27, 66,
+ -474, -124, -112, -474, 11, -474, -474, -474, 147, -368,
+ -474, -474, -474, -474, -474, -474, -140, -474, -474, -474,
+ -474, -474, -474, -474, -474, -474, -474, -474, -474, -474,
+ 532, 10, 247, -474, -194, 132, 135, -474, 279, -59,
+ 418, 67, 5, 384, 624, 425, 317, 20, -474, 424,
+ 636, 509, -474, -474, -474, -474, -36, -37, -31, -49,
+ -474, -474, -474, -474, -474, -32, 464, -473, -474, -474,
+ -474, -474, -474, -474, -474, -474, 277, -119, -231, 287,
+ -474, 300, -474, -205, -300, 652, -474, -242, -474, -63,
+ 106, 182, -474, -316, -241, -285, -195, -474, -111, -420,
+ -474, -474, -245, -474, 402, -474, -176, -474, 345, 249,
+ 346, 218, 87, 96, -415, -474, -429, 252, -474, 522,
+ -474, -474
};
/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
positive, shift that token. If negative, reduce the rule which
- number is the opposite. If zero, do what YYDEFACT says.
- If YYTABLE_NINF, syntax error. */
-#define YYTABLE_NINF -275
+ number is the opposite. If YYTABLE_NINF, syntax error. */
+#define YYTABLE_NINF -277
static const yytype_int16 yytable[] =
{
- 12, 119, 161, 121, 272, 174, 359, 488, 274, 436,
- 502, 240, 385, 376, 323, 32, 278, 79, 508, 259,
- 235, 393, 103, 32, 320, 138, 235, 555, 107, 400,
- 540, 111, 375, 402, 111, 433, 235, 27, 128, 173,
- 111, 571, 426, 417, 619, 389, 391, 380, 146, 150,
- 109, 428, 164, 109, 457, 120, 380, 435, 9, 131,
- 5, -213, 150, 226, 232, 233, 653, 4, 9, 212,
- 152, 153, 154, 155, 156, 157, 390, 166, 167, 163,
- 7, 207, 561, 366, 11, 9, 261, 214, 15, 216,
- 218, 388, 205, 28, 276, -213, 462, 29, 20, 18,
- 19, 282, 239, 222, 620, 621, 427, 10, 11, 23,
- 174, 463, 632, 325, 622, 133, 27, 10, 11, -179,
- -234, 273, 243, 458, 291, 579, 133, -213, 618, 26,
- 111, 228, 228, 228, 10, 11, 111, 9, 146, 381,
- 136, 208, 150, 367, 289, 228, 33, 134, 93, 257,
- 164, 209, 537, 209, 228, 266, 124, 438, 134, 526,
- 130, 528, 228, 439, 658, 492, 659, 150, 27, 228,
- 501, 101, 503, 152, 156, 361, 29, 163, 638, -234,
- 379, 607, 633, 331, 332, -234, 10, 11, 141, 9,
- 164, 369, 228, 639, 640, 318, 652, 438, 624, 625,
- 536, 79, 582, 487, 125, 438, 438, 349, 125, 586,
- 451, 594, 613, 105, 357, 32, -181, 163, 243, 171,
- 204, 397, 636, 516, 108, 102, 206, -265, 29, 363,
- 523, 9, -265, 408, 198, 565, 414, 415, 10, 11,
- -180, 228, -152, 228, 243, 79, 202, 409, -181, 411,
- 451, -177, 203, 475, -175, 533, 403, 452, 430, 228,
- 569, 228, 235, 489, 510, -180, 418, 228, 259, -264,
- 512, 9, 235, 584, -264, -177, 150, -179, -175, 11,
- 10, 11, -265, -177, 215, 496, -175, 219, -265, 228,
- 497, 662, 220, 35, 122, 9, 223, 452, 37, 234,
- 249, 410, 250, 94, 228, 228, 453, 112, 164, -176,
- 408, 95, 47, 48, 9, 96, 79, 647, 165, 51,
- 10, 11, 496, -174, -264, 97, 98, 497, -178, 209,
- -264, 277, 262, -176, 353, 163, 495, 454, 480, 623,
- 482, -176, 331, 332, 10, 11, 498, -174, 349, 61,
- 354, 285, -178, 616, 520, -174, 226, 515, 99, 286,
- -178, 64, 358, 10, 11, 483, 360, 243, 529, 478,
- 231, 231, 231, 287, 490, 364, 362, 243, 228, 111,
- 368, 514, 372, 626, 231, 374, 378, 111, 254, 380,
- 228, 111, 481, 231, 146, 522, 150, 631, 257, 384,
- 228, 231, 382, 199, 228, 386, 266, 200, 231, 394,
- 507, 150, 392, 399, 401, 201, 165, 263, 164, 405,
- 413, 416, 419, 264, 228, 228, 432, 445, 446, 254,
- 448, 231, 79, 79, 480, 449, 482, 10, 11, 459,
- 349, 542, 447, 549, 464, 163, 467, 468, 454, 469,
- 480, 470, 482, 614, 454, 471, 165, 562, 349, 486,
- 379, 483, 235, 491, 255, 509, 9, 79, 254, 117,
- 585, 504, 243, 256, 9, 494, 9, 483, 10, 11,
- 231, 506, 231, 511, 9, 513, 521, 164, 481, 525,
- 527, 434, 9, 530, 531, 228, 532, 263, 231, 534,
- 231, 127, 340, 264, 481, 535, 231, 554, 556, 143,
- 557, 147, 265, 564, 163, 10, 11, 10, 11, 172,
- 9, 520, 661, 10, 11, 10, 11, 317, 231, 568,
- 463, 570, -156, 10, 11, 573, 575, 480, 228, 482,
- 412, 10, 11, 231, 231, 117, 117, 117, 210, 210,
- 577, 210, 210, 578, 235, 581, 288, 592, 593, 117,
- 583, 595, 596, 529, 483, 243, 254, 597, 117, 10,
- 11, 79, -157, 598, 165, 600, 117, 599, 150, 602,
- 603, 334, 604, 117, 606, 608, 642, 615, 228, 627,
- 335, 481, 349, 630, 542, 336, 337, 338, 549, 454,
- 177, 255, 339, 349, 349, 480, 117, 482, 629, 340,
- 185, 641, 438, 164, 189, 10, 11, 231, 648, 194,
- 195, 196, 197, 649, 650, 651, 341, 657, 106, 231,
- 66, 484, 483, 628, 580, 654, 590, 591, 342, 231,
- 163, 370, 123, 231, 343, 371, 345, 11, 404, 493,
- 284, 505, 355, 356, 352, 117, 476, 117, 91, 481,
- 443, 444, 574, 231, 231, 344, 539, 563, 637, 634,
- 559, 344, 344, 117, 351, 117, 0, 0, 0, 37,
- 0, 117, 0, 0, 165, 0, 0, 0, 112, 0,
- 0, 0, 0, 47, 48, 9, 0, 0, 0, 0,
- 51, 0, 0, 117, 0, 0, 0, 224, 0, 0,
- 0, 0, 0, 0, 137, 117, 0, 0, 117, 117,
- 0, 0, 175, -274, 114, 0, 160, 484, 0, 169,
- 225, 0, 0, 0, 231, 0, 280, 0, 0, 0,
- 0, 0, 64, 484, 10, 11, 281, 0, 0, 0,
- 0, 176, 177, 165, 178, 179, 180, 181, 182, 0,
- 183, 184, 185, 186, 187, 188, 189, 190, 191, 192,
- 193, 194, 195, 196, 197, 0, 450, 231, 0, 0,
- 0, 0, 0, -274, 461, 0, 0, 0, 344, 0,
- 0, 0, 117, -274, 0, 344, 0, 0, 0, 0,
- 0, 0, 0, 344, 117, 0, 117, 0, 0, 0,
- 0, 0, 0, 0, 117, 0, 0, 0, 117, 0,
- 0, 0, 0, 0, 0, 0, 0, 231, 0, 0,
- 484, 0, 0, 0, 0, 0, 0, 0, 117, 117,
- 292, 293, 294, 295, 0, 296, 297, 298, 0, 299,
- 300, 301, 302, 303, 304, 305, 306, 307, 308, 309,
- 310, 311, 312, 0, 160, 0, 319, 0, 322, 0,
- 0, 0, 137, 137, 333, 538, 0, 0, 0, 165,
- 0, 545, 548, 0, 553, 0, 0, 0, 0, 0,
- 0, 0, 0, 558, 344, 560, 0, 0, 484, 0,
- 543, 344, 117, 344, 0, 0, 0, 0, 0, 117,
- 0, 0, 344, 0, 344, 0, 0, 0, 117, 0,
- 37, 0, 0, 35, 0, 0, 0, 0, 37, 112,
- 0, 168, 0, 0, 47, 48, 9, 112, 0, 0,
- 0, 51, 47, 48, 9, 137, 0, 0, 224, 51,
- 0, 0, 117, 0, 0, 137, 55, 0, 0, 0,
- 0, 0, 0, 0, 0, 114, 0, 0, 0, 56,
- 57, 225, 58, 59, 0, 0, 60, 290, 421, 61,
- 0, 0, 160, 64, 601, 10, 11, 281, 421, 62,
- 63, 64, 605, 10, 11, 0, 0, 0, 37, 0,
- 0, 241, 117, 344, 0, 117, 0, 112, 0, 0,
- 0, 344, 47, 48, 9, 0, 0, 0, 344, 51,
- 0, 0, 0, 0, 0, 0, 224, 0, 0, 0,
- 0, 137, 137, 0, 545, 635, 0, 0, 0, 0,
- 0, 0, 0, 114, 0, 0, 0, 0, 0, 225,
- 344, 0, 0, 543, 344, 0, 0, 0, 0, -2,
- 34, 64, 35, 10, 11, 36, 0, 37, 38, 39,
- 137, 0, 40, 117, 41, 42, 43, 44, 45, 46,
- 0, 47, 48, 9, 137, 0, 49, 50, 51, 52,
- 53, 54, 160, 0, 0, 55, 0, 169, 0, 0,
- 0, 0, 344, 0, 344, 0, 0, 0, 56, 57,
+ 121, 120, 162, 273, 175, 123, 122, 321, 437, 377,
+ 489, 324, 165, 104, 572, 236, 241, 260, 386, 360,
+ 275, 236, 434, 279, 164, 556, 541, 394, 108, 166,
+ 458, 236, 429, 390, 392, 401, 346, 621, 436, 403,
+ 174, 110, 356, 357, 110, 376, 101, 213, 4, 418,
+ 132, -215, 208, 5, 27, 206, 657, 118, 134, 27,
+ 7, 15, 11, 427, 18, 153, 154, 155, 156, 157,
+ 158, -267, 167, 168, 19, 9, -267, 229, 229, 229,
+ 9, 439, 232, 232, 232, -215, 439, 440, 497, 134,
+ 135, 229, 488, 498, 367, 102, 232, 622, 623, 459,
+ 229, 620, -236, 326, 223, 232, 20, 624, 229, -181,
+ 175, 165, 209, 232, 29, 229, 103, -215, 142, 29,
+ 232, 135, 210, 164, 10, 11, -267, 428, 166, 10,
+ 11, 23, -267, 26, 118, 118, 118, 382, 229, 538,
+ 527, 258, 529, 232, 33, 503, 290, 267, 118, 499,
+ 205, 165, 452, 509, 368, 139, 207, 118, 502, 27,
+ 504, -236, 380, 164, 210, 118, 451, -236, 166, 153,
+ 157, 656, 118, 9, 462, 381, 9, 641, 493, 636,
+ 9, -266, 594, 635, 93, 463, -266, 229, 595, 229,
+ 642, 643, 232, 497, 232, 118, 537, 381, 498, 453,
+ 464, 583, 106, 439, 391, 229, 358, 229, 587, 596,
+ 232, 128, 232, 229, 109, 28, 137, 562, 232, 29,
+ 517, 172, 10, 11, 199, 10, 11, 524, 452, 10,
+ 11, 566, 389, 240, -153, 229, -266, 662, 534, 663,
+ 232, 203, -266, 204, 118, 255, 118, 411, 410, 9,
+ 229, 229, 413, 412, 628, 232, 232, 236, 476, 431,
+ 580, 255, 118, -182, 118, 539, 260, 236, 490, 165,
+ 118, 546, 549, 570, 554, 453, 511, 513, -181, 585,
+ 256, 164, 9, 559, 454, 561, 166, 125, -183, 257,
+ 264, 131, 118, 216, 10, 11, 265, 666, 10, 11,
+ 439, 11, 221, 220, 118, 266, 615, 118, 118, 224,
+ 10, 11, -182, 255, 332, 333, 609, 650, 9, 126,
+ -183, 250, 235, 126, 229, 263, 484, 251, 9, 232,
+ 210, 10, 11, 626, 627, 625, 229, 94, 482, 481,
+ 286, 232, 264, 485, 483, 95, 229, 287, 265, 96,
+ 229, 232, 354, 144, 521, 232, -179, 288, 639, 97,
+ 98, 200, 10, 11, 274, 201, 618, 10, 11, 530,
+ 229, 229, 355, 202, 603, 232, 232, 10, 11, 165,
+ -179, 118, 607, 9, -177, -178, 359, 404, -179, -176,
+ 258, 164, 99, 118, 633, 118, 166, 419, 267, 634,
+ 361, 363, 508, 118, 369, -180, 365, 118, -177, -178,
+ 373, 211, 211, -176, 211, 211, -177, -178, 148, 379,
+ 375, -176, 484, 381, 383, 546, 638, 118, 118, -180,
+ 12, 406, 10, 11, 482, 481, 9, -180, 484, 485,
+ 483, 229, 385, 393, 9, 32, 232, 79, 165, 387,
+ 482, 481, 9, 32, 9, 485, 483, 236, 616, 395,
+ 164, 112, 35, 400, 112, 166, 402, 37, 129, 417,
+ 112, 173, 414, 332, 333, 420, 113, 433, 147, 151,
+ 278, 47, 48, 9, 229, 10, 11, 318, 51, 232,
+ 289, 118, 151, 10, 11, 178, 255, 215, 118, 217,
+ 219, 10, 11, 10, 11, 186, 446, 118, 447, 190,
+ 448, 449, 515, 450, 195, 196, 197, 198, 61, 460,
+ 465, 521, 468, 471, 665, 484, 523, 469, 470, 345,
+ 64, 256, 10, 11, 229, 345, 345, 482, 481, 232,
+ 472, 118, 485, 483, 487, 10, 11, 492, 380, 495,
+ 505, 507, 236, 244, 510, 512, 514, 522, 531, 528,
+ 532, 112, 533, 526, 435, 530, 535, 112, 555, 147,
+ 341, 536, 557, 151, 565, 165, 558, 569, 574, 571,
+ -157, 138, 464, 576, 578, 579, 582, 164, 37, 584,
+ 593, 118, 166, 161, 118, 484, 170, 113, 151, 597,
+ 598, 599, 47, 48, 9, -158, 600, 482, 481, 51,
+ 601, 606, 485, 483, 605, 602, 225, 604, 608, 610,
+ 37, 617, 629, 631, 644, 632, 319, 645, 439, 113,
+ 651, 652, 79, 115, 47, 48, 9, 653, 350, 226,
+ 661, 51, 654, 655, 66, 281, 32, 107, 225, 244,
+ 630, 64, 345, 10, 11, 282, 658, 581, 591, 345,
+ 364, 592, 371, 124, 118, 115, 405, 345, 372, 285,
+ 506, 226, 494, 477, 91, 244, 79, 291, 353, 575,
+ 444, 445, 564, 64, 178, 10, 11, 282, 181, 182,
+ 183, 540, 640, 185, 186, 187, 188, 637, 190, 191,
+ 192, 193, 194, 195, 196, 197, 198, 151, 293, 294,
+ 295, 296, 560, 297, 298, 299, 0, 300, 301, 302,
+ 303, 304, 305, 306, 307, 308, 309, 310, 311, 312,
+ 313, 0, 161, 0, 320, 352, 323, 0, 0, 0,
+ 138, 138, 334, 0, 0, 0, 0, 79, 0, 0,
+ 227, 233, 234, 0, 0, 0, 0, 0, 345, 0,
+ 0, 0, 0, 0, 544, 345, 0, 345, 455, 0,
+ 0, 335, 0, 262, 0, 37, 345, 0, 345, 350,
+ 336, 277, 0, 0, 113, 337, 338, 339, 283, 47,
+ 48, 9, 340, 0, 0, 0, 51, 0, 244, 341,
+ 479, 0, 0, 114, 0, 491, 0, 0, 244, 0,
+ 112, 292, 0, 138, 0, 0, 342, 0, 112, 0,
+ 115, 0, 112, 138, 0, 147, 116, 151, 343, 0,
+ 0, 0, 0, 0, 344, 0, 0, 11, 64, 0,
+ 10, 11, 151, 0, 0, 0, 422, 0, 0, 0,
+ 161, 0, 0, 0, 0, 0, 422, 0, 0, 0,
+ 0, 0, 362, 79, 79, 0, 0, 345, 0, 0,
+ 0, 350, 543, 0, 550, 345, 0, 0, 370, 455,
+ 0, 0, 345, 0, 0, 455, 0, 0, 563, 350,
+ 0, 0, 0, 0, 0, 0, 0, 0, 79, 138,
+ 138, 0, 0, 244, 0, 0, 0, 0, 398, 0,
+ 0, 178, 0, 0, 0, 345, 0, 0, 544, 345,
+ 409, 186, 0, 415, 416, 190, 191, 192, 193, 194,
+ 195, 196, 197, 198, 0, 0, 0, 0, 138, 0,
+ 0, 0, 0, 176, -276, 0, 0, 0, 0, 0,
+ 0, 0, 138, 0, 0, 0, 0, 0, 0, 0,
+ 161, 0, 0, 0, 0, 170, 0, 0, 0, 345,
+ 0, 345, 177, 178, 0, 179, 180, 181, 182, 183,
+ 0, 184, 185, 186, 187, 188, 189, 190, 191, 192,
+ 193, 194, 195, 196, 197, 198, 244, 409, 0, 0,
+ 0, 0, 79, 0, -276, 0, 567, 568, 0, 151,
+ 0, 0, 0, 0, -276, 0, 0, 0, 0, 0,
+ 0, 0, 0, 496, 350, 0, 543, 0, 0, 161,
+ 550, 455, 0, 0, 0, 350, 350, 0, 0, 0,
+ 0, 0, 0, 227, 516, 0, 0, 0, 0, 422,
+ 0, 0, 0, 0, 0, 0, 422, 589, 422, -2,
+ 34, 0, 35, 0, 0, 36, 0, 37, 38, 39,
+ 0, 0, 40, 0, 41, 42, 43, 44, 45, 46,
+ 0, 47, 48, 9, 0, 0, 49, 50, 51, 52,
+ 53, 54, 0, 0, 0, 55, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 56, 57,
0, 58, 59, 0, 0, 60, 0, 0, 61, 0,
- 0, -24, 0, 0, 0, 0, 0, 0, 62, 63,
- 64, 0, 10, 11, 0, 0, 0, 0, 566, 567,
- 0, 0, 0, 0, 0, 0, 0, 0, 324, 0,
- 35, 0, 0, 36, -249, 37, 38, 39, 0, -249,
- 40, 160, 41, 42, 112, 44, 45, 46, 0, 47,
- 48, 9, 0, 0, 49, 50, 51, 52, 53, 54,
- 0, 421, 0, 55, 0, 0, 0, 0, 421, 588,
- 421, 0, 0, 0, 0, 0, 56, 57, 0, 58,
- 59, 0, 0, 60, 0, 0, 61, 0, 0, -249,
- 0, 0, 0, 0, 325, -249, 62, 63, 64, 0,
- 10, 11, 324, 0, 35, 0, 0, 36, 0, 37,
- 38, 39, 0, 0, 40, 0, 41, 42, 112, 44,
- 45, 46, 0, 47, 48, 9, 177, 0, 49, 50,
- 51, 52, 53, 54, 0, 0, 185, 55, 0, 0,
- 189, 190, 191, 192, 193, 194, 195, 196, 197, 0,
- 56, 57, 0, 58, 59, 0, 0, 60, 0, 0,
- 61, 0, 0, -249, 645, 646, 0, 160, 325, -249,
- 62, 63, 64, 35, 10, 11, 421, 0, 37, 0,
- 0, 0, 0, 0, 0, 0, 0, 112, 0, 334,
- 0, 0, 47, 48, 9, 0, 0, 0, 335, 51,
- 0, 0, 0, 336, 337, 338, 158, 0, 0, 0,
- 339, 0, 0, 0, 0, 0, 0, 340, 0, 56,
- 57, 0, 58, 159, 0, 0, 60, 0, 35, 61,
- 314, 0, 0, 37, 341, 0, 0, 0, 0, 62,
- 63, 64, 112, 10, 11, 0, 0, 47, 48, 9,
- 0, 0, 343, 0, 51, 11, 0, 0, 0, 0,
- 0, 55, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 56, 57, 0, 58, 59, 0,
- 0, 60, 0, 35, 61, 0, 0, 0, 37, 0,
- 0, 0, 420, 0, 62, 63, 64, 112, 10, 11,
- 0, 0, 47, 48, 9, 0, 0, 0, 0, 51,
- 0, 429, 0, 0, 0, 0, 158, 0, 0, 0,
+ 0, -24, 0, 0, 0, 0, 170, 0, 62, 63,
+ 64, 0, 10, 11, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 648, 649, 0, 161, 586, 0, 0,
+ 0, 325, 0, 35, 0, 422, 36, -251, 37, 38,
+ 39, 0, -251, 40, 0, 41, 42, 113, 44, 45,
+ 46, 0, 47, 48, 9, 0, 0, 49, 50, 51,
+ 52, 53, 54, 0, 0, 0, 55, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 56,
- 57, 0, 58, 159, 0, 0, 60, 0, 35, 61,
- 0, 0, 0, 37, 0, 0, 0, 0, 0, 62,
- 63, 64, 112, 10, 11, 0, 0, 47, 48, 9,
- 0, 473, 0, 0, 51, 0, 0, 0, 0, 0,
+ 57, 0, 58, 59, 0, 0, 60, 0, 0, 61,
+ 0, 0, -251, 0, 0, 0, 0, 326, -251, 62,
+ 63, 64, 0, 10, 11, 325, 0, 35, 0, 0,
+ 36, 0, 37, 38, 39, 0, 0, 40, 0, 41,
+ 42, 113, 44, 45, 46, 0, 47, 48, 9, 0,
+ 0, 49, 50, 51, 52, 53, 54, 0, 0, 0,
+ 55, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 56, 57, 0, 58, 59, 0, 0,
+ 60, 0, 35, 61, 0, 0, -251, 37, 0, 0,
+ 169, 326, -251, 62, 63, 64, 113, 10, 11, 0,
+ 0, 47, 48, 9, 0, 0, 0, 0, 51, 0,
+ 0, 0, 0, 0, 0, 55, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 35, 0, 0, 56, 57,
+ 37, 58, 59, 0, 0, 60, 0, 0, 61, 113,
+ 0, 0, 0, 0, 47, 48, 9, 0, 62, 63,
+ 64, 51, 10, 11, 0, 0, 0, 0, 159, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 56, 57, 0, 58, 160, 0, 0, 60, 0,
+ 35, 61, 315, 0, 0, 37, 0, 0, 0, 0,
+ 0, 62, 63, 64, 113, 10, 11, 0, 0, 47,
+ 48, 9, 0, 0, 0, 0, 51, 0, 0, 0,
+ 0, 0, 0, 55, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 56, 57, 0, 58,
+ 59, 0, 0, 60, 0, 35, 61, 0, 0, 0,
+ 37, 0, 0, 0, 421, 0, 62, 63, 64, 113,
+ 10, 11, 0, 0, 47, 48, 9, 0, 0, 0,
+ 0, 51, 0, 430, 0, 0, 0, 0, 159, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 56, 57, 0, 58, 160, 0, 0, 60, 0,
+ 35, 61, 0, 0, 0, 37, 0, 0, 0, 0,
+ 0, 62, 63, 64, 113, 10, 11, 0, 0, 47,
+ 48, 9, 0, 474, 0, 0, 51, 0, 0, 0,
+ 0, 0, 0, 55, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 56, 57, 0, 58,
+ 59, 0, 0, 60, 0, 35, 61, 0, 0, 0,
+ 37, 0, 0, 0, 0, 0, 62, 63, 64, 113,
+ 10, 11, 0, 0, 47, 48, 9, 0, 475, 0,
+ 0, 51, 0, 0, 0, 0, 0, 0, 55, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 35, 0,
+ 0, 56, 57, 37, 58, 59, 0, 0, 60, 0,
+ 0, 61, 113, 0, 0, 0, 0, 47, 48, 9,
+ 0, 62, 63, 64, 51, 10, 11, 0, 0, 0,
0, 55, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 56, 57, 0, 58, 59, 0,
0, 60, 0, 35, 61, 0, 0, 0, 37, 0,
- 0, 0, 0, 0, 62, 63, 64, 112, 10, 11,
- 0, 0, 47, 48, 9, 0, 474, 0, 0, 51,
+ 0, 0, 588, 0, 62, 63, 64, 113, 10, 11,
+ 0, 0, 47, 48, 9, 0, 0, 0, 0, 51,
0, 0, 0, 0, 0, 0, 55, 0, 0, 0,
0, 0, 0, 0, 0, 0, 35, 0, 0, 56,
57, 37, 58, 59, 0, 0, 60, 0, 0, 61,
- 112, 0, 0, 0, 0, 47, 48, 9, 0, 62,
- 63, 64, 51, 10, 11, 0, 0, 0, 0, 55,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 56, 57, 0, 58, 59, 0, 0, 60,
- 0, 35, 61, 0, 0, 0, 37, 0, 0, 0,
- 587, 0, 62, 63, 64, 112, 10, 11, 0, 0,
- 47, 48, 9, 0, 0, 0, 0, 51, 0, 0,
- 0, 0, 0, 0, 55, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 35, 0, 0, 56, 57, 37,
- 58, 59, 0, 0, 60, 0, 0, 61, 112, 0,
- 0, 0, 0, 47, 48, 9, 0, 62, 63, 64,
- 51, 10, 11, 0, 0, 0, 0, 158, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 35, 0, 0,
- 56, 57, 283, 58, 159, 0, 0, 60, 0, 0,
- 61, 112, 0, 0, 0, 0, 47, 48, 9, 0,
- 62, 63, 64, 51, 10, 11, 0, 0, 0, 0,
- 55, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 56, 57, 37, 58, 59, 0, 0,
- 60, 0, 0, 61, 112, 0, 0, 0, 0, 47,
- 48, 9, 0, 62, 63, 64, 51, 10, 11, 0,
- 37, 0, 0, 224, 0, 0, 0, 0, 37, 112,
- 0, 0, 0, 0, 47, 48, 9, 112, 0, 0,
- 114, 51, 47, 48, 9, 0, 225, 0, 113, 51,
- 0, 0, 0, 0, 37, 0, 224, 0, 64, 0,
- 10, 11, 396, 112, 0, 114, 0, 0, 47, 48,
- 9, 115, 0, 114, 0, 51, 0, 0, 0, 225,
- 0, 37, 406, 64, 0, 10, 11, 0, 0, 0,
- 112, 64, 0, 10, 11, 47, 48, 9, 0, 114,
- 0, 0, 51, 0, 0, 407, 0, 283, 0, 224,
- 0, 0, 0, 0, 0, 334, 112, 64, 0, 10,
- 11, 47, 48, 9, 335, 0, 114, 0, 51, 336,
- 337, 338, 477, 0, 0, 224, 339, 0, 0, 0,
- 334, 0, 0, 440, 64, 0, 10, 11, 334, 335,
- 0, 460, 114, 0, 336, 337, 541, 335, 225, 0,
- 341, 339, 336, 337, 338, 0, 441, 0, 340, 339,
- 64, 0, 10, 11, 334, 0, 340, 0, 343, 0,
- 0, 11, 0, 335, 0, 341, 0, 0, 336, 337,
- 338, 0, 0, 341, 0, 339, 0, 0, 0, 0,
- 0, 0, 340, 343, 0, 10, 11, 0, 0, 0,
- 0, 343, 177, 0, 11, 0, 180, 181, 182, 341,
- 0, 184, 185, 186, 187, 609, 189, 190, 191, 192,
- 193, 194, 195, 196, 197, 0, 0, 343, 176, 177,
- 11, 178, 0, 180, 181, 182, 0, 0, 184, 185,
+ 113, 0, 0, 0, 0, 47, 48, 9, 0, 62,
+ 63, 64, 51, 10, 11, 0, 0, 0, 0, 159,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 35,
+ 0, 0, 56, 57, 284, 58, 160, 0, 0, 60,
+ 0, 0, 61, 113, 0, 0, 0, 0, 47, 48,
+ 9, 0, 62, 63, 64, 51, 10, 11, 0, 0,
+ 0, 0, 55, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 56, 57, 37, 58, 59,
+ 0, 0, 60, 0, 0, 61, 113, 0, 0, 0,
+ 0, 47, 48, 9, 0, 62, 63, 64, 51, 10,
+ 11, 0, 0, 37, 0, 225, 242, 0, 0, 0,
+ 0, 37, 113, 0, 0, 0, 0, 47, 48, 9,
+ 113, 0, 115, 0, 51, 47, 48, 9, 226, 0,
+ 0, 225, 51, 0, 0, 0, 37, 0, 0, 225,
+ 64, 0, 10, 11, 397, 113, 0, 0, 115, 0,
+ 47, 48, 9, 0, 226, 0, 115, 51, 0, 0,
+ 0, 0, 226, 0, 407, 0, 64, 0, 10, 11,
+ 37, 0, 0, 0, 64, 0, 10, 11, 0, 113,
+ 0, 115, 0, 0, 47, 48, 9, 408, 0, 0,
+ 0, 51, 0, 0, 0, 284, 0, 0, 225, 64,
+ 0, 10, 11, 335, 113, 0, 0, 0, 0, 47,
+ 48, 9, 336, 0, 0, 115, 51, 337, 338, 339,
+ 0, 478, 0, 225, 340, 0, 0, 0, 0, 335,
+ 0, 441, 461, 64, 0, 10, 11, 0, 336, 0,
+ 115, 0, 0, 337, 338, 339, 226, 0, 342, 0,
+ 340, 0, 0, 0, 442, 0, 335, 341, 64, 0,
+ 10, 11, 0, 0, 0, 336, 344, 0, 0, 11,
+ 337, 338, 542, 0, 342, 0, 0, 340, 0, 0,
+ 0, 0, 335, 0, 341, 0, 0, 0, 0, 0,
+ 335, 336, 344, 0, 0, 11, 337, 338, 339, 336,
+ 0, 342, 0, 340, 337, 338, 339, 0, 0, 0,
+ 341, 340, 0, 0, 0, 0, 0, 0, 341, 344,
+ 0, 10, 11, 0, 0, 0, 0, 342, 0, 0,
+ 0, 0, 0, 611, 0, 342, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 344, 0, 0, 11, 0,
+ 0, 0, 0, 344, 177, 178, 11, 179, 0, 181,
+ 182, 183, 0, 0, 185, 186, 187, 188, 189, 190,
+ 191, 192, 193, 194, 195, 196, 197, 198, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 177, 178, 0,
+ 179, 0, 181, 182, 183, 0, 435, 185, 186, 187,
+ 188, 189, 190, 191, 192, 193, 194, 195, 196, 197,
+ 198, 0, 0, 0, 0, 0, 0, 177, 178, 0,
+ 179, 0, 181, 182, 183, 0, 432, 185, 186, 187,
+ 188, 189, 190, 191, 192, 193, 194, 195, 196, 197,
+ 198, 177, 178, 0, 179, 0, 181, 182, 183, 0,
+ 525, 185, 186, 187, 188, 189, 190, 191, 192, 193,
+ 194, 195, 196, 197, 198, 177, 178, 0, 179, 0,
+ 181, 182, 183, 0, 659, 185, 186, 187, 188, 189,
+ 190, 191, 192, 193, 194, 195, 196, 197, 198, 177,
+ 178, 0, 179, 0, 181, 182, 183, 0, 660, 185,
186, 187, 188, 189, 190, 191, 192, 193, 194, 195,
- 196, 197, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 176, 177, 0, 178, 0, 180, 181, 182, 0,
- 434, 184, 185, 186, 187, 188, 189, 190, 191, 192,
- 193, 194, 195, 196, 197, 0, 0, 0, 0, 0,
- 0, 176, 177, 0, 178, 0, 180, 181, 182, 0,
- 431, 184, 185, 186, 187, 188, 189, 190, 191, 192,
- 193, 194, 195, 196, 197, 176, 177, 0, 178, 0,
- 180, 181, 182, 0, 524, 184, 185, 186, 187, 188,
- 189, 190, 191, 192, 193, 194, 195, 196, 197, 176,
- 177, 0, 178, 0, 180, 181, 182, 0, 655, 184,
- 185, 186, 187, 188, 189, 190, 191, 192, 193, 194,
- 195, 196, 197, 176, 177, 0, 178, 0, 180, 181,
- 182, 0, 656, 184, 185, 186, 187, 188, 189, 190,
- 191, 192, 193, 194, 195, 196, 197, 176, 177, 0,
- 0, 0, 180, 181, 182, 0, 0, 184, 185, 186,
- 187, 188, 189, 190, 191, 192, 193, 194, 195, 196,
- 197, 176, 177, 0, 0, 0, 180, 181, 182, 0,
- 0, 184, 185, 186, 187, 0, 189, 190, 191, 192,
- 193, 194, 195, 196, 197
+ 196, 197, 198, 177, 178, 0, 0, 0, 181, 182,
+ 183, 0, 0, 185, 186, 187, 188, 189, 190, 191,
+ 192, 193, 194, 195, 196, 197, 198, 177, 178, 0,
+ 0, 0, 181, 182, 183, 0, 0, 185, 186, 187,
+ 188, 0, 190, 191, 192, 193, 194, 195, 196, 197,
+ 198
};
+#define yypact_value_is_default(yystate) \
+ ((yystate) == (-474))
+
+#define yytable_value_is_error(yytable_value) \
+ YYID (0)
+
static const yytype_int16 yycheck[] =
{
- 5, 37, 61, 37, 142, 67, 223, 377, 143, 323,
- 389, 126, 258, 250, 204, 20, 147, 22, 397, 135,
- 126, 267, 28, 28, 201, 49, 132, 453, 31, 275,
- 445, 36, 249, 279, 39, 321, 142, 3, 43, 67,
- 45, 486, 11, 289, 5, 263, 264, 24, 53, 54,
- 36, 316, 61, 39, 3, 37, 24, 322, 24, 45,
- 25, 1, 67, 113, 114, 115, 20, 0, 24, 59,
- 55, 56, 57, 58, 59, 60, 53, 62, 63, 61,
- 25, 5, 461, 5, 74, 24, 136, 95, 24, 97,
- 98, 59, 1, 59, 144, 35, 60, 63, 21, 3,
- 62, 151, 126, 106, 65, 66, 75, 73, 74, 24,
- 172, 75, 596, 67, 75, 35, 3, 73, 74, 59,
- 3, 60, 127, 72, 174, 504, 35, 67, 573, 63,
- 135, 113, 114, 115, 73, 74, 141, 24, 143, 255,
- 71, 65, 147, 65, 172, 127, 62, 67, 62, 135,
- 159, 75, 438, 75, 136, 141, 40, 62, 67, 424,
- 44, 426, 144, 68, 648, 382, 650, 172, 3, 151,
- 388, 24, 390, 158, 159, 225, 63, 159, 604, 62,
- 63, 560, 597, 207, 208, 68, 73, 74, 51, 24,
- 199, 241, 174, 608, 609, 200, 641, 62, 577, 578,
- 437, 206, 516, 68, 40, 62, 62, 212, 44, 523,
- 3, 68, 68, 3, 220, 220, 35, 199, 223, 62,
- 83, 271, 601, 413, 3, 60, 89, 7, 63, 234,
- 420, 24, 12, 283, 67, 472, 286, 287, 73, 74,
- 59, 223, 66, 225, 249, 250, 66, 283, 67, 283,
- 3, 35, 59, 368, 35, 432, 280, 50, 317, 241,
- 477, 243, 368, 378, 399, 59, 290, 249, 384, 7,
- 401, 24, 378, 519, 12, 59, 281, 59, 59, 74,
- 73, 74, 62, 67, 24, 7, 67, 64, 68, 271,
- 12, 661, 62, 3, 37, 24, 59, 50, 8, 63,
- 59, 283, 67, 9, 286, 287, 59, 17, 317, 35,
- 360, 17, 22, 23, 24, 21, 321, 631, 61, 29,
- 73, 74, 7, 35, 62, 31, 32, 12, 35, 75,
- 68, 60, 35, 59, 59, 317, 386, 342, 374, 576,
- 374, 67, 366, 367, 73, 74, 68, 59, 353, 59,
- 3, 60, 59, 570, 416, 67, 406, 407, 64, 72,
- 67, 71, 60, 73, 74, 374, 8, 372, 427, 374,
- 113, 114, 115, 72, 379, 35, 60, 382, 360, 384,
- 62, 405, 75, 68, 127, 60, 62, 392, 24, 24,
- 372, 396, 374, 136, 399, 419, 401, 587, 384, 62,
- 382, 144, 59, 59, 386, 72, 392, 63, 151, 3,
- 396, 416, 62, 62, 62, 71, 159, 53, 427, 65,
- 67, 62, 65, 59, 406, 407, 66, 59, 67, 24,
- 67, 174, 437, 438, 470, 8, 470, 73, 74, 65,
- 445, 446, 71, 448, 62, 427, 62, 62, 453, 62,
- 486, 60, 486, 568, 459, 60, 199, 462, 463, 60,
- 63, 470, 568, 60, 59, 35, 24, 472, 24, 37,
- 520, 60, 477, 68, 24, 68, 24, 486, 73, 74,
- 223, 68, 225, 60, 24, 60, 60, 496, 470, 75,
- 68, 75, 24, 60, 60, 477, 60, 53, 241, 60,
- 243, 59, 36, 59, 486, 68, 249, 3, 62, 59,
- 72, 59, 68, 60, 496, 73, 74, 73, 74, 59,
- 24, 583, 660, 73, 74, 73, 74, 59, 271, 62,
- 75, 59, 59, 73, 74, 60, 66, 573, 520, 573,
- 283, 73, 74, 286, 287, 113, 114, 115, 94, 95,
- 60, 97, 98, 60, 660, 60, 60, 60, 72, 127,
- 62, 60, 60, 622, 573, 570, 24, 59, 136, 73,
- 74, 576, 59, 68, 317, 72, 144, 62, 583, 68,
- 62, 8, 49, 151, 62, 59, 14, 60, 570, 68,
- 17, 573, 597, 68, 599, 22, 23, 24, 603, 604,
- 34, 59, 29, 608, 609, 641, 174, 641, 60, 36,
- 44, 60, 62, 622, 48, 73, 74, 360, 60, 53,
- 54, 55, 56, 60, 60, 60, 53, 68, 31, 372,
- 22, 374, 641, 583, 509, 642, 525, 525, 65, 382,
- 622, 243, 39, 386, 71, 243, 211, 74, 281, 384,
- 159, 392, 217, 218, 213, 223, 372, 225, 22, 641,
- 334, 334, 496, 406, 407, 211, 441, 463, 603, 599,
- 459, 217, 218, 241, 212, 243, -1, -1, -1, 8,
- -1, 249, -1, -1, 427, -1, -1, -1, 17, -1,
- -1, -1, -1, 22, 23, 24, -1, -1, -1, -1,
- 29, -1, -1, 271, -1, -1, -1, 36, -1, -1,
- -1, -1, -1, -1, 49, 283, -1, -1, 286, 287,
- -1, -1, 4, 5, 53, -1, 61, 470, -1, 64,
- 59, -1, -1, -1, 477, -1, 65, -1, -1, -1,
- -1, -1, 71, 486, 73, 74, 75, -1, -1, -1,
- -1, 33, 34, 496, 36, 37, 38, 39, 40, -1,
- 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
- 52, 53, 54, 55, 56, -1, 341, 520, -1, -1,
- -1, -1, -1, 65, 349, -1, -1, -1, 334, -1,
- -1, -1, 360, 75, -1, 341, -1, -1, -1, -1,
- -1, -1, -1, 349, 372, -1, 374, -1, -1, -1,
- -1, -1, -1, -1, 382, -1, -1, -1, 386, -1,
- -1, -1, -1, -1, -1, -1, -1, 570, -1, -1,
- 573, -1, -1, -1, -1, -1, -1, -1, 406, 407,
- 175, 176, 177, 178, -1, 180, 181, 182, -1, 184,
- 185, 186, 187, 188, 189, 190, 191, 192, 193, 194,
- 195, 196, 197, -1, 199, -1, 201, -1, 203, -1,
- -1, -1, 207, 208, 209, 440, -1, -1, -1, 622,
- -1, 446, 447, -1, 449, -1, -1, -1, -1, -1,
- -1, -1, -1, 458, 440, 460, -1, -1, 641, -1,
- 446, 447, 470, 449, -1, -1, -1, -1, -1, 477,
- -1, -1, 458, -1, 460, -1, -1, -1, 486, -1,
- 8, -1, -1, 3, -1, -1, -1, -1, 8, 17,
- -1, 11, -1, -1, 22, 23, 24, 17, -1, -1,
- -1, 29, 22, 23, 24, 280, -1, -1, 36, 29,
- -1, -1, 520, -1, -1, 290, 36, -1, -1, -1,
- -1, -1, -1, -1, -1, 53, -1, -1, -1, 49,
- 50, 59, 52, 53, -1, -1, 56, 65, 313, 59,
- -1, -1, 317, 71, 549, 73, 74, 75, 323, 69,
- 70, 71, 557, 73, 74, -1, -1, -1, 8, -1,
- -1, 11, 570, 549, -1, 573, -1, 17, -1, -1,
- -1, 557, 22, 23, 24, -1, -1, -1, 564, 29,
- -1, -1, -1, -1, -1, -1, 36, -1, -1, -1,
- -1, 366, 367, -1, 599, 600, -1, -1, -1, -1,
- -1, -1, -1, 53, -1, -1, -1, -1, -1, 59,
- 596, -1, -1, 599, 600, -1, -1, -1, -1, 0,
- 1, 71, 3, 73, 74, 6, -1, 8, 9, 10,
- 405, -1, 13, 641, 15, 16, 17, 18, 19, 20,
- -1, 22, 23, 24, 419, -1, 27, 28, 29, 30,
- 31, 32, 427, -1, -1, 36, -1, 432, -1, -1,
- -1, -1, 648, -1, 650, -1, -1, -1, 49, 50,
+ 37, 37, 61, 143, 67, 37, 37, 202, 324, 251,
+ 378, 205, 61, 28, 487, 127, 127, 136, 259, 224,
+ 144, 133, 322, 148, 61, 454, 446, 268, 31, 61,
+ 3, 143, 317, 264, 265, 276, 212, 5, 323, 280,
+ 67, 36, 218, 219, 39, 250, 24, 59, 0, 290,
+ 45, 1, 5, 25, 3, 1, 20, 37, 35, 3,
+ 25, 24, 74, 11, 3, 55, 56, 57, 58, 59,
+ 60, 7, 62, 63, 62, 24, 12, 114, 115, 116,
+ 24, 62, 114, 115, 116, 35, 62, 68, 7, 35,
+ 67, 128, 68, 12, 5, 73, 128, 65, 66, 72,
+ 137, 574, 3, 67, 107, 137, 21, 75, 145, 59,
+ 173, 160, 65, 145, 63, 152, 60, 67, 51, 63,
+ 152, 67, 75, 160, 73, 74, 62, 75, 160, 73,
+ 74, 24, 68, 63, 114, 115, 116, 256, 175, 439,
+ 425, 136, 427, 175, 62, 390, 173, 142, 128, 68,
+ 83, 200, 3, 398, 65, 49, 89, 137, 389, 3,
+ 391, 62, 63, 200, 75, 145, 342, 68, 200, 159,
+ 160, 644, 152, 24, 350, 24, 24, 606, 383, 599,
+ 24, 7, 66, 598, 62, 60, 12, 224, 72, 226,
+ 610, 611, 224, 7, 226, 175, 438, 24, 12, 50,
+ 75, 517, 3, 62, 53, 242, 221, 244, 524, 68,
+ 242, 59, 244, 250, 3, 59, 71, 462, 250, 63,
+ 414, 62, 73, 74, 67, 73, 74, 421, 3, 73,
+ 74, 473, 59, 127, 66, 272, 62, 652, 433, 654,
+ 272, 66, 68, 59, 224, 24, 226, 284, 284, 24,
+ 287, 288, 284, 284, 68, 287, 288, 369, 369, 318,
+ 505, 24, 242, 59, 244, 441, 385, 379, 379, 318,
+ 250, 447, 448, 478, 450, 50, 400, 402, 59, 520,
+ 59, 318, 24, 459, 59, 461, 318, 40, 35, 68,
+ 53, 44, 272, 24, 73, 74, 59, 665, 73, 74,
+ 62, 74, 62, 64, 284, 68, 68, 287, 288, 59,
+ 73, 74, 59, 24, 208, 209, 561, 633, 24, 40,
+ 67, 59, 63, 44, 361, 35, 375, 67, 24, 361,
+ 75, 73, 74, 578, 579, 577, 373, 9, 375, 375,
+ 60, 373, 53, 375, 375, 17, 383, 72, 59, 21,
+ 387, 383, 59, 59, 417, 387, 35, 72, 603, 31,
+ 32, 59, 73, 74, 60, 63, 571, 73, 74, 428,
+ 407, 408, 3, 71, 550, 407, 408, 73, 74, 428,
+ 59, 361, 558, 24, 35, 35, 60, 281, 67, 35,
+ 385, 428, 64, 373, 588, 375, 428, 291, 393, 594,
+ 8, 60, 397, 383, 62, 35, 35, 387, 59, 59,
+ 75, 94, 95, 59, 97, 98, 67, 67, 59, 62,
+ 60, 67, 471, 24, 59, 601, 602, 407, 408, 59,
+ 5, 65, 73, 74, 471, 471, 24, 67, 487, 471,
+ 471, 478, 62, 62, 24, 20, 478, 22, 497, 72,
+ 487, 487, 24, 28, 24, 487, 487, 569, 569, 3,
+ 497, 36, 3, 62, 39, 497, 62, 8, 43, 62,
+ 45, 59, 67, 367, 368, 65, 17, 66, 53, 54,
+ 60, 22, 23, 24, 521, 73, 74, 59, 29, 521,
+ 60, 471, 67, 73, 74, 34, 24, 95, 478, 97,
+ 98, 73, 74, 73, 74, 44, 59, 487, 67, 48,
+ 71, 67, 406, 8, 53, 54, 55, 56, 59, 65,
+ 62, 584, 62, 60, 664, 574, 420, 62, 62, 212,
+ 71, 59, 73, 74, 571, 218, 219, 574, 574, 571,
+ 60, 521, 574, 574, 60, 73, 74, 60, 63, 68,
+ 60, 68, 664, 128, 35, 60, 60, 60, 60, 68,
+ 60, 136, 60, 75, 75, 624, 60, 142, 3, 144,
+ 36, 68, 62, 148, 60, 624, 72, 62, 60, 59,
+ 59, 49, 75, 66, 60, 60, 60, 624, 8, 62,
+ 60, 571, 624, 61, 574, 644, 64, 17, 173, 60,
+ 60, 59, 22, 23, 24, 59, 68, 644, 644, 29,
+ 62, 49, 644, 644, 62, 72, 36, 68, 62, 59,
+ 8, 60, 68, 60, 60, 68, 201, 14, 62, 17,
+ 72, 60, 207, 53, 22, 23, 24, 60, 213, 59,
+ 68, 29, 60, 60, 22, 65, 221, 31, 36, 224,
+ 584, 71, 335, 73, 74, 75, 645, 510, 526, 342,
+ 235, 526, 244, 39, 644, 53, 282, 350, 244, 160,
+ 393, 59, 385, 373, 22, 250, 251, 65, 214, 497,
+ 335, 335, 464, 71, 34, 73, 74, 75, 38, 39,
+ 40, 442, 605, 43, 44, 45, 46, 601, 48, 49,
+ 50, 51, 52, 53, 54, 55, 56, 282, 176, 177,
+ 178, 179, 460, 181, 182, 183, -1, 185, 186, 187,
+ 188, 189, 190, 191, 192, 193, 194, 195, 196, 197,
+ 198, -1, 200, -1, 202, 213, 204, -1, -1, -1,
+ 208, 209, 210, -1, -1, -1, -1, 322, -1, -1,
+ 114, 115, 116, -1, -1, -1, -1, -1, 441, -1,
+ -1, -1, -1, -1, 447, 448, -1, 450, 343, -1,
+ -1, 8, -1, 137, -1, 8, 459, -1, 461, 354,
+ 17, 145, -1, -1, 17, 22, 23, 24, 152, 22,
+ 23, 24, 29, -1, -1, -1, 29, -1, 373, 36,
+ 375, -1, -1, 36, -1, 380, -1, -1, 383, -1,
+ 385, 175, -1, 281, -1, -1, 53, -1, 393, -1,
+ 53, -1, 397, 291, -1, 400, 59, 402, 65, -1,
+ -1, -1, -1, -1, 71, -1, -1, 74, 71, -1,
+ 73, 74, 417, -1, -1, -1, 314, -1, -1, -1,
+ 318, -1, -1, -1, -1, -1, 324, -1, -1, -1,
+ -1, -1, 226, 438, 439, -1, -1, 550, -1, -1,
+ -1, 446, 447, -1, 449, 558, -1, -1, 242, 454,
+ -1, -1, 565, -1, -1, 460, -1, -1, 463, 464,
+ -1, -1, -1, -1, -1, -1, -1, -1, 473, 367,
+ 368, -1, -1, 478, -1, -1, -1, -1, 272, -1,
+ -1, 34, -1, -1, -1, 598, -1, -1, 601, 602,
+ 284, 44, -1, 287, 288, 48, 49, 50, 51, 52,
+ 53, 54, 55, 56, -1, -1, -1, -1, 406, -1,
+ -1, -1, -1, 4, 5, -1, -1, -1, -1, -1,
+ -1, -1, 420, -1, -1, -1, -1, -1, -1, -1,
+ 428, -1, -1, -1, -1, 433, -1, -1, -1, 652,
+ -1, 654, 33, 34, -1, 36, 37, 38, 39, 40,
+ -1, 42, 43, 44, 45, 46, 47, 48, 49, 50,
+ 51, 52, 53, 54, 55, 56, 571, 361, -1, -1,
+ -1, -1, 577, -1, 65, -1, 474, 475, -1, 584,
+ -1, -1, -1, -1, 75, -1, -1, -1, -1, -1,
+ -1, -1, -1, 387, 599, -1, 601, -1, -1, 497,
+ 605, 606, -1, -1, -1, 610, 611, -1, -1, -1,
+ -1, -1, -1, 407, 408, -1, -1, -1, -1, 517,
+ -1, -1, -1, -1, -1, -1, 524, 525, 526, 0,
+ 1, -1, 3, -1, -1, 6, -1, 8, 9, 10,
+ -1, -1, 13, -1, 15, 16, 17, 18, 19, 20,
+ -1, 22, 23, 24, -1, -1, 27, 28, 29, 30,
+ 31, 32, -1, -1, -1, 36, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 49, 50,
-1, 52, 53, -1, -1, 56, -1, -1, 59, -1,
- -1, 62, -1, -1, -1, -1, -1, -1, 69, 70,
- 71, -1, 73, 74, -1, -1, -1, -1, 473, 474,
- -1, -1, -1, -1, -1, -1, -1, -1, 1, -1,
- 3, -1, -1, 6, 7, 8, 9, 10, -1, 12,
- 13, 496, 15, 16, 17, 18, 19, 20, -1, 22,
- 23, 24, -1, -1, 27, 28, 29, 30, 31, 32,
- -1, 516, -1, 36, -1, -1, -1, -1, 523, 524,
- 525, -1, -1, -1, -1, -1, 49, 50, -1, 52,
- 53, -1, -1, 56, -1, -1, 59, -1, -1, 62,
- -1, -1, -1, -1, 67, 68, 69, 70, 71, -1,
- 73, 74, 1, -1, 3, -1, -1, 6, -1, 8,
- 9, 10, -1, -1, 13, -1, 15, 16, 17, 18,
- 19, 20, -1, 22, 23, 24, 34, -1, 27, 28,
- 29, 30, 31, 32, -1, -1, 44, 36, -1, -1,
- 48, 49, 50, 51, 52, 53, 54, 55, 56, -1,
- 49, 50, -1, 52, 53, -1, -1, 56, -1, -1,
- 59, -1, -1, 62, 619, 620, -1, 622, 67, 68,
- 69, 70, 71, 3, 73, 74, 631, -1, 8, -1,
- -1, -1, -1, -1, -1, -1, -1, 17, -1, 8,
- -1, -1, 22, 23, 24, -1, -1, -1, 17, 29,
- -1, -1, -1, 22, 23, 24, 36, -1, -1, -1,
- 29, -1, -1, -1, -1, -1, -1, 36, -1, 49,
- 50, -1, 52, 53, -1, -1, 56, -1, 3, 59,
- 60, -1, -1, 8, 53, -1, -1, -1, -1, 69,
- 70, 71, 17, 73, 74, -1, -1, 22, 23, 24,
- -1, -1, 71, -1, 29, 74, -1, -1, -1, -1,
+ -1, 62, -1, -1, -1, -1, 594, -1, 69, 70,
+ 71, -1, 73, 74, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 621, 622, -1, 624, 521, -1, -1,
+ -1, 1, -1, 3, -1, 633, 6, 7, 8, 9,
+ 10, -1, 12, 13, -1, 15, 16, 17, 18, 19,
+ 20, -1, 22, 23, 24, -1, -1, 27, 28, 29,
+ 30, 31, 32, -1, -1, -1, 36, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 49,
+ 50, -1, 52, 53, -1, -1, 56, -1, -1, 59,
+ -1, -1, 62, -1, -1, -1, -1, 67, 68, 69,
+ 70, 71, -1, 73, 74, 1, -1, 3, -1, -1,
+ 6, -1, 8, 9, 10, -1, -1, 13, -1, 15,
+ 16, 17, 18, 19, 20, -1, 22, 23, 24, -1,
+ -1, 27, 28, 29, 30, 31, 32, -1, -1, -1,
+ 36, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 49, 50, -1, 52, 53, -1, -1,
+ 56, -1, 3, 59, -1, -1, 62, 8, -1, -1,
+ 11, 67, 68, 69, 70, 71, 17, 73, 74, -1,
+ -1, 22, 23, 24, -1, -1, -1, -1, 29, -1,
+ -1, -1, -1, -1, -1, 36, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 3, -1, -1, 49, 50,
+ 8, 52, 53, -1, -1, 56, -1, -1, 59, 17,
+ -1, -1, -1, -1, 22, 23, 24, -1, 69, 70,
+ 71, 29, 73, 74, -1, -1, -1, -1, 36, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 49, 50, -1, 52, 53, -1, -1, 56, -1,
+ 3, 59, 60, -1, -1, 8, -1, -1, -1, -1,
+ -1, 69, 70, 71, 17, 73, 74, -1, -1, 22,
+ 23, 24, -1, -1, -1, -1, 29, -1, -1, -1,
+ -1, -1, -1, 36, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 49, 50, -1, 52,
+ 53, -1, -1, 56, -1, 3, 59, -1, -1, -1,
+ 8, -1, -1, -1, 67, -1, 69, 70, 71, 17,
+ 73, 74, -1, -1, 22, 23, 24, -1, -1, -1,
+ -1, 29, -1, 31, -1, -1, -1, -1, 36, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 49, 50, -1, 52, 53, -1, -1, 56, -1,
+ 3, 59, -1, -1, -1, 8, -1, -1, -1, -1,
+ -1, 69, 70, 71, 17, 73, 74, -1, -1, 22,
+ 23, 24, -1, 26, -1, -1, 29, -1, -1, -1,
+ -1, -1, -1, 36, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 49, 50, -1, 52,
+ 53, -1, -1, 56, -1, 3, 59, -1, -1, -1,
+ 8, -1, -1, -1, -1, -1, 69, 70, 71, 17,
+ 73, 74, -1, -1, 22, 23, 24, -1, 26, -1,
+ -1, 29, -1, -1, -1, -1, -1, -1, 36, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 3, -1,
+ -1, 49, 50, 8, 52, 53, -1, -1, 56, -1,
+ -1, 59, 17, -1, -1, -1, -1, 22, 23, 24,
+ -1, 69, 70, 71, 29, 73, 74, -1, -1, -1,
-1, 36, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, 49, 50, -1, 52, 53, -1,
-1, 56, -1, 3, 59, -1, -1, -1, 8, -1,
-1, -1, 67, -1, 69, 70, 71, 17, 73, 74,
-1, -1, 22, 23, 24, -1, -1, -1, -1, 29,
- -1, 31, -1, -1, -1, -1, 36, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, 49,
- 50, -1, 52, 53, -1, -1, 56, -1, 3, 59,
- -1, -1, -1, 8, -1, -1, -1, -1, -1, 69,
- 70, 71, 17, 73, 74, -1, -1, 22, 23, 24,
- -1, 26, -1, -1, 29, -1, -1, -1, -1, -1,
- -1, 36, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 49, 50, -1, 52, 53, -1,
- -1, 56, -1, 3, 59, -1, -1, -1, 8, -1,
- -1, -1, -1, -1, 69, 70, 71, 17, 73, 74,
- -1, -1, 22, 23, 24, -1, 26, -1, -1, 29,
-1, -1, -1, -1, -1, -1, 36, -1, -1, -1,
-1, -1, -1, -1, -1, -1, 3, -1, -1, 49,
50, 8, 52, 53, -1, -1, 56, -1, -1, 59,
17, -1, -1, -1, -1, 22, 23, 24, -1, 69,
70, 71, 29, 73, 74, -1, -1, -1, -1, 36,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 49, 50, -1, 52, 53, -1, -1, 56,
- -1, 3, 59, -1, -1, -1, 8, -1, -1, -1,
- 67, -1, 69, 70, 71, 17, 73, 74, -1, -1,
- 22, 23, 24, -1, -1, -1, -1, 29, -1, -1,
- -1, -1, -1, -1, 36, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 3, -1, -1, 49, 50, 8,
- 52, 53, -1, -1, 56, -1, -1, 59, 17, -1,
- -1, -1, -1, 22, 23, 24, -1, 69, 70, 71,
- 29, 73, 74, -1, -1, -1, -1, 36, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 3, -1, -1,
- 49, 50, 8, 52, 53, -1, -1, 56, -1, -1,
- 59, 17, -1, -1, -1, -1, 22, 23, 24, -1,
- 69, 70, 71, 29, 73, 74, -1, -1, -1, -1,
- 36, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 49, 50, 8, 52, 53, -1, -1,
- 56, -1, -1, 59, 17, -1, -1, -1, -1, 22,
- 23, 24, -1, 69, 70, 71, 29, 73, 74, -1,
- 8, -1, -1, 36, -1, -1, -1, -1, 8, 17,
- -1, -1, -1, -1, 22, 23, 24, 17, -1, -1,
- 53, 29, 22, 23, 24, -1, 59, -1, 36, 29,
- -1, -1, -1, -1, 8, -1, 36, -1, 71, -1,
- 73, 74, 75, 17, -1, 53, -1, -1, 22, 23,
- 24, 59, -1, 53, -1, 29, -1, -1, -1, 59,
- -1, 8, 36, 71, -1, 73, 74, -1, -1, -1,
- 17, 71, -1, 73, 74, 22, 23, 24, -1, 53,
- -1, -1, 29, -1, -1, 59, -1, 8, -1, 36,
- -1, -1, -1, -1, -1, 8, 17, 71, -1, 73,
- 74, 22, 23, 24, 17, -1, 53, -1, 29, 22,
- 23, 24, 59, -1, -1, 36, 29, -1, -1, -1,
- 8, -1, -1, 36, 71, -1, 73, 74, 8, 17,
- -1, 11, 53, -1, 22, 23, 24, 17, 59, -1,
- 53, 29, 22, 23, 24, -1, 59, -1, 36, 29,
- 71, -1, 73, 74, 8, -1, 36, -1, 71, -1,
- -1, 74, -1, 17, -1, 53, -1, -1, 22, 23,
- 24, -1, -1, 53, -1, 29, -1, -1, -1, -1,
- -1, -1, 36, 71, -1, 73, 74, -1, -1, -1,
- -1, 71, 34, -1, 74, -1, 38, 39, 40, 53,
- -1, 43, 44, 45, 46, 59, 48, 49, 50, 51,
- 52, 53, 54, 55, 56, -1, -1, 71, 33, 34,
- 74, 36, -1, 38, 39, 40, -1, -1, 43, 44,
- 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
- 55, 56, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 33, 34, -1, 36, -1, 38, 39, 40, -1,
- 75, 43, 44, 45, 46, 47, 48, 49, 50, 51,
- 52, 53, 54, 55, 56, -1, -1, -1, -1, -1,
- -1, 33, 34, -1, 36, -1, 38, 39, 40, -1,
- 72, 43, 44, 45, 46, 47, 48, 49, 50, 51,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 3,
+ -1, -1, 49, 50, 8, 52, 53, -1, -1, 56,
+ -1, -1, 59, 17, -1, -1, -1, -1, 22, 23,
+ 24, -1, 69, 70, 71, 29, 73, 74, -1, -1,
+ -1, -1, 36, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 49, 50, 8, 52, 53,
+ -1, -1, 56, -1, -1, 59, 17, -1, -1, -1,
+ -1, 22, 23, 24, -1, 69, 70, 71, 29, 73,
+ 74, -1, -1, 8, -1, 36, 11, -1, -1, -1,
+ -1, 8, 17, -1, -1, -1, -1, 22, 23, 24,
+ 17, -1, 53, -1, 29, 22, 23, 24, 59, -1,
+ -1, 36, 29, -1, -1, -1, 8, -1, -1, 36,
+ 71, -1, 73, 74, 75, 17, -1, -1, 53, -1,
+ 22, 23, 24, -1, 59, -1, 53, 29, -1, -1,
+ -1, -1, 59, -1, 36, -1, 71, -1, 73, 74,
+ 8, -1, -1, -1, 71, -1, 73, 74, -1, 17,
+ -1, 53, -1, -1, 22, 23, 24, 59, -1, -1,
+ -1, 29, -1, -1, -1, 8, -1, -1, 36, 71,
+ -1, 73, 74, 8, 17, -1, -1, -1, -1, 22,
+ 23, 24, 17, -1, -1, 53, 29, 22, 23, 24,
+ -1, 59, -1, 36, 29, -1, -1, -1, -1, 8,
+ -1, 36, 11, 71, -1, 73, 74, -1, 17, -1,
+ 53, -1, -1, 22, 23, 24, 59, -1, 53, -1,
+ 29, -1, -1, -1, 59, -1, 8, 36, 71, -1,
+ 73, 74, -1, -1, -1, 17, 71, -1, -1, 74,
+ 22, 23, 24, -1, 53, -1, -1, 29, -1, -1,
+ -1, -1, 8, -1, 36, -1, -1, -1, -1, -1,
+ 8, 17, 71, -1, -1, 74, 22, 23, 24, 17,
+ -1, 53, -1, 29, 22, 23, 24, -1, -1, -1,
+ 36, 29, -1, -1, -1, -1, -1, -1, 36, 71,
+ -1, 73, 74, -1, -1, -1, -1, 53, -1, -1,
+ -1, -1, -1, 59, -1, 53, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 71, -1, -1, 74, -1,
+ -1, -1, -1, 71, 33, 34, 74, 36, -1, 38,
+ 39, 40, -1, -1, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 33, 34, -1,
+ 36, -1, 38, 39, 40, -1, 75, 43, 44, 45,
+ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, -1, -1, -1, -1, -1, -1, 33, 34, -1,
+ 36, -1, 38, 39, 40, -1, 72, 43, 44, 45,
+ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 33, 34, -1, 36, -1, 38, 39, 40, -1,
+ 66, 43, 44, 45, 46, 47, 48, 49, 50, 51,
52, 53, 54, 55, 56, 33, 34, -1, 36, -1,
38, 39, 40, -1, 66, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55, 56, 33,
34, -1, 36, -1, 38, 39, 40, -1, 66, 43,
44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
- 54, 55, 56, 33, 34, -1, 36, -1, 38, 39,
- 40, -1, 66, 43, 44, 45, 46, 47, 48, 49,
+ 54, 55, 56, 33, 34, -1, -1, -1, 38, 39,
+ 40, -1, -1, 43, 44, 45, 46, 47, 48, 49,
50, 51, 52, 53, 54, 55, 56, 33, 34, -1,
-1, -1, 38, 39, 40, -1, -1, 43, 44, 45,
- 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
- 56, 33, 34, -1, -1, -1, 38, 39, 40, -1,
- -1, 43, 44, 45, 46, -1, 48, 49, 50, 51,
- 52, 53, 54, 55, 56
+ 46, -1, 48, 49, 50, 51, 52, 53, 54, 55,
+ 56
};
/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
@@ -1494,63 +1537,63 @@ static const yytype_uint8 yystos[] =
113, 121, 124, 126, 127, 128, 129, 134, 138, 141,
143, 144, 149, 150, 153, 156, 157, 158, 161, 164,
165, 181, 186, 62, 9, 17, 21, 31, 32, 64,
- 199, 24, 60, 83, 84, 3, 86, 88, 3, 138,
- 140, 141, 17, 36, 53, 59, 141, 143, 148, 152,
- 153, 154, 161, 140, 128, 134, 111, 59, 141, 159,
- 128, 138, 114, 35, 67, 137, 71, 126, 186, 193,
- 125, 137, 122, 59, 96, 97, 141, 59, 93, 139,
- 141, 185, 127, 127, 127, 127, 127, 127, 36, 53,
- 126, 135, 147, 153, 155, 161, 127, 127, 11, 126,
- 192, 62, 59, 94, 185, 4, 33, 34, 36, 37,
- 38, 39, 40, 42, 43, 44, 45, 46, 47, 48,
- 49, 50, 51, 52, 53, 54, 55, 56, 67, 59,
- 63, 71, 66, 59, 137, 1, 137, 5, 65, 75,
- 142, 200, 59, 160, 200, 24, 200, 201, 200, 64,
- 62, 190, 88, 59, 36, 59, 146, 152, 153, 154,
- 155, 161, 146, 146, 63, 98, 107, 108, 109, 186,
- 194, 11, 136, 141, 145, 146, 177, 178, 179, 59,
- 67, 162, 112, 194, 24, 59, 68, 138, 171, 173,
- 175, 146, 35, 53, 59, 68, 138, 170, 172, 173,
- 174, 184, 112, 60, 97, 169, 146, 60, 93, 167,
- 65, 75, 146, 8, 147, 60, 72, 72, 60, 94,
- 65, 146, 126, 126, 126, 126, 126, 126, 126, 126,
+ 199, 24, 73, 60, 83, 84, 3, 86, 88, 3,
+ 138, 140, 141, 17, 36, 53, 59, 141, 143, 148,
+ 152, 153, 154, 161, 140, 128, 134, 111, 59, 141,
+ 159, 128, 138, 114, 35, 67, 137, 71, 126, 186,
+ 193, 125, 137, 122, 59, 96, 97, 141, 59, 93,
+ 139, 141, 185, 127, 127, 127, 127, 127, 127, 36,
+ 53, 126, 135, 147, 153, 155, 161, 127, 127, 11,
+ 126, 192, 62, 59, 94, 185, 4, 33, 34, 36,
+ 37, 38, 39, 40, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 67,
+ 59, 63, 71, 66, 59, 137, 1, 137, 5, 65,
+ 75, 142, 200, 59, 160, 200, 24, 200, 201, 200,
+ 64, 62, 190, 88, 59, 36, 59, 146, 152, 153,
+ 154, 155, 161, 146, 146, 63, 98, 107, 108, 109,
+ 186, 194, 11, 136, 141, 145, 146, 177, 178, 179,
+ 59, 67, 162, 112, 194, 24, 59, 68, 138, 171,
+ 173, 175, 146, 35, 53, 59, 68, 138, 170, 172,
+ 173, 174, 184, 112, 60, 97, 169, 146, 60, 93,
+ 167, 65, 75, 146, 8, 147, 60, 72, 72, 60,
+ 94, 65, 146, 126, 126, 126, 126, 126, 126, 126,
126, 126, 126, 126, 126, 126, 126, 126, 126, 126,
- 126, 126, 126, 130, 60, 135, 187, 59, 141, 126,
- 192, 182, 126, 130, 1, 67, 91, 100, 180, 181,
- 183, 186, 186, 126, 8, 17, 22, 23, 24, 29,
- 36, 53, 65, 71, 142, 202, 204, 205, 206, 141,
- 207, 215, 162, 59, 3, 202, 202, 83, 60, 179,
- 8, 146, 60, 141, 35, 105, 5, 65, 62, 146,
- 136, 145, 75, 191, 60, 179, 183, 115, 62, 63,
- 24, 173, 59, 176, 62, 190, 72, 104, 59, 174,
- 53, 174, 62, 190, 3, 198, 75, 146, 123, 62,
- 190, 62, 190, 186, 139, 65, 36, 59, 146, 152,
- 153, 154, 161, 67, 146, 146, 62, 190, 186, 65,
- 67, 126, 131, 132, 188, 189, 11, 75, 191, 31,
- 135, 72, 66, 180, 75, 191, 189, 101, 62, 68,
- 36, 59, 203, 204, 206, 59, 67, 71, 67, 8,
- 202, 3, 50, 59, 141, 212, 213, 3, 72, 65,
- 11, 202, 60, 75, 62, 195, 215, 62, 62, 62,
- 60, 60, 106, 26, 26, 194, 177, 59, 141, 151,
- 152, 153, 154, 155, 161, 163, 60, 68, 105, 194,
- 141, 60, 179, 175, 68, 146, 7, 12, 68, 99,
- 102, 174, 198, 174, 60, 172, 68, 138, 198, 35,
- 97, 60, 93, 60, 186, 146, 130, 94, 95, 168,
- 185, 60, 186, 130, 66, 75, 191, 68, 191, 135,
- 60, 60, 60, 192, 60, 68, 183, 180, 202, 205,
- 195, 24, 141, 142, 197, 202, 209, 217, 202, 141,
- 196, 208, 216, 202, 3, 212, 62, 72, 202, 213,
- 202, 198, 141, 207, 60, 183, 126, 126, 62, 179,
- 59, 163, 116, 60, 187, 66, 103, 60, 60, 198,
- 104, 60, 189, 62, 190, 146, 189, 67, 126, 133,
- 131, 132, 60, 72, 68, 60, 60, 59, 68, 62,
- 72, 202, 68, 62, 49, 202, 62, 198, 59, 59,
- 202, 210, 211, 68, 194, 60, 179, 119, 163, 5,
- 65, 66, 75, 183, 198, 198, 68, 68, 95, 60,
- 68, 130, 210, 195, 209, 202, 198, 208, 212, 195,
- 195, 60, 14, 117, 120, 126, 126, 189, 60, 60,
- 60, 60, 163, 20, 100, 66, 66, 68, 210, 210,
- 118, 112, 105
+ 126, 126, 126, 126, 130, 60, 135, 187, 59, 141,
+ 126, 192, 182, 126, 130, 1, 67, 91, 100, 180,
+ 181, 183, 186, 186, 126, 8, 17, 22, 23, 24,
+ 29, 36, 53, 65, 71, 142, 202, 204, 205, 206,
+ 141, 207, 215, 162, 59, 3, 202, 202, 83, 60,
+ 179, 8, 146, 60, 141, 35, 105, 5, 65, 62,
+ 146, 136, 145, 75, 191, 60, 179, 183, 115, 62,
+ 63, 24, 173, 59, 176, 62, 190, 72, 104, 59,
+ 174, 53, 174, 62, 190, 3, 198, 75, 146, 123,
+ 62, 190, 62, 190, 186, 139, 65, 36, 59, 146,
+ 152, 153, 154, 161, 67, 146, 146, 62, 190, 186,
+ 65, 67, 126, 131, 132, 188, 189, 11, 75, 191,
+ 31, 135, 72, 66, 180, 75, 191, 189, 101, 62,
+ 68, 36, 59, 203, 204, 206, 59, 67, 71, 67,
+ 8, 202, 3, 50, 59, 141, 212, 213, 3, 72,
+ 65, 11, 202, 60, 75, 62, 195, 215, 62, 62,
+ 62, 60, 60, 106, 26, 26, 194, 177, 59, 141,
+ 151, 152, 153, 154, 155, 161, 163, 60, 68, 105,
+ 194, 141, 60, 179, 175, 68, 146, 7, 12, 68,
+ 99, 102, 174, 198, 174, 60, 172, 68, 138, 198,
+ 35, 97, 60, 93, 60, 186, 146, 130, 94, 95,
+ 168, 185, 60, 186, 130, 66, 75, 191, 68, 191,
+ 135, 60, 60, 60, 192, 60, 68, 183, 180, 202,
+ 205, 195, 24, 141, 142, 197, 202, 209, 217, 202,
+ 141, 196, 208, 216, 202, 3, 212, 62, 72, 202,
+ 213, 202, 198, 141, 207, 60, 183, 126, 126, 62,
+ 179, 59, 163, 116, 60, 187, 66, 103, 60, 60,
+ 198, 104, 60, 189, 62, 190, 146, 189, 67, 126,
+ 133, 131, 132, 60, 66, 72, 68, 60, 60, 59,
+ 68, 62, 72, 202, 68, 62, 49, 202, 62, 198,
+ 59, 59, 202, 210, 211, 68, 194, 60, 179, 119,
+ 163, 5, 65, 66, 75, 183, 198, 198, 68, 68,
+ 95, 60, 68, 130, 192, 210, 195, 209, 202, 198,
+ 208, 212, 195, 195, 60, 14, 117, 120, 126, 126,
+ 189, 72, 60, 60, 60, 60, 163, 20, 100, 66,
+ 66, 68, 210, 210, 118, 112, 105
};
#define yyerrok (yyerrstatus = 0)
@@ -1565,9 +1608,18 @@ static const yytype_uint8 yystos[] =
/* Like YYERROR except do call yyerror. This remains here temporarily
to ease the transition to the new meaning of YYERROR, for GCC.
- Once GCC version 2 has supplanted version 1, this can go. */
+ Once GCC version 2 has supplanted version 1, this can go. However,
+ YYFAIL appears to be in use. Nevertheless, it is formally deprecated
+ in Bison 2.4.2's NEWS entry, where a plan to phase it out is
+ discussed. */
#define YYFAIL goto yyerrlab
+#if defined YYFAIL
+ /* This is here to suppress warnings from the GCC cpp's
+ -Wunused-macros. Normally we don't worry about that warning, but
+ some users do, and we want to make it easy for users to remove
+ YYFAIL uses, which will produce warnings from Bison 2.5. */
+#endif
#define YYRECOVERING() (!!yyerrstatus)
@@ -1577,7 +1629,6 @@ do \
{ \
yychar = (Token); \
yylval = (Value); \
- yytoken = YYTRANSLATE (yychar); \
YYPOPSTACK (1); \
goto yybackup; \
} \
@@ -1619,19 +1670,10 @@ while (YYID (0))
#endif
-/* YY_LOCATION_PRINT -- Print the location on the stream.
- This macro was not mandated originally: define only if we know
- we won't break user code: when these are the locations we know. */
+/* This macro is provided for backward compatibility. */
#ifndef YY_LOCATION_PRINT
-# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL
-# define YY_LOCATION_PRINT(File, Loc) \
- fprintf (File, "%d.%d-%d.%d", \
- (Loc).first_line, (Loc).first_column, \
- (Loc).last_line, (Loc).last_column)
-# else
-# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
-# endif
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
#endif
@@ -1735,17 +1777,20 @@ yy_symbol_print (yyoutput, yytype, yyvaluep)
#if (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
static void
-yy_stack_print (yytype_int16 *bottom, yytype_int16 *top)
+yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop)
#else
static void
-yy_stack_print (bottom, top)
- yytype_int16 *bottom;
- yytype_int16 *top;
+yy_stack_print (yybottom, yytop)
+ yytype_int16 *yybottom;
+ yytype_int16 *yytop;
#endif
{
YYFPRINTF (stderr, "Stack now");
- for (; bottom <= top; ++bottom)
- YYFPRINTF (stderr, " %d", *bottom);
+ for (; yybottom <= yytop; yybottom++)
+ {
+ int yybot = *yybottom;
+ YYFPRINTF (stderr, " %d", yybot);
+ }
YYFPRINTF (stderr, "\n");
}
@@ -1779,11 +1824,11 @@ yy_reduce_print (yyvsp, yyrule)
/* The symbols being reduced. */
for (yyi = 0; yyi < yynrhs; yyi++)
{
- fprintf (stderr, " $%d = ", yyi + 1);
+ YYFPRINTF (stderr, " $%d = ", yyi + 1);
yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
&(yyvsp[(yyi + 1) - (yynrhs)])
);
- fprintf (stderr, "\n");
+ YYFPRINTF (stderr, "\n");
}
}
@@ -1820,7 +1865,6 @@ int yydebug;
# define YYMAXDEPTH 10000
#endif
-
#if YYERROR_VERBOSE
@@ -1923,115 +1967,142 @@ yytnamerr (char *yyres, const char *yystr)
}
# endif
-/* Copy into YYRESULT an error message about the unexpected token
- YYCHAR while in state YYSTATE. Return the number of bytes copied,
- including the terminating null byte. If YYRESULT is null, do not
- copy anything; just return the number of bytes that would be
- copied. As a special case, return 0 if an ordinary "syntax error"
- message will do. Return YYSIZE_MAXIMUM if overflow occurs during
- size calculation. */
-static YYSIZE_T
-yysyntax_error (char *yyresult, int yystate, int yychar)
-{
- int yyn = yypact[yystate];
+/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message
+ about the unexpected token YYTOKEN for the state stack whose top is
+ YYSSP.
- if (! (YYPACT_NINF < yyn && yyn <= YYLAST))
- return 0;
- else
- {
- int yytype = YYTRANSLATE (yychar);
- YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]);
- YYSIZE_T yysize = yysize0;
- YYSIZE_T yysize1;
- int yysize_overflow = 0;
- enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
- char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
- int yyx;
-
-# if 0
- /* This is so xgettext sees the translatable formats that are
- constructed on the fly. */
- YY_("syntax error, unexpected %s");
- YY_("syntax error, unexpected %s, expecting %s");
- YY_("syntax error, unexpected %s, expecting %s or %s");
- YY_("syntax error, unexpected %s, expecting %s or %s or %s");
- YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s");
-# endif
- char *yyfmt;
- char const *yyf;
- static char const yyunexpected[] = "syntax error, unexpected %s";
- static char const yyexpecting[] = ", expecting %s";
- static char const yyor[] = " or %s";
- char yyformat[sizeof yyunexpected
- + sizeof yyexpecting - 1
- + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2)
- * (sizeof yyor - 1))];
- char const *yyprefix = yyexpecting;
-
- /* Start YYX at -YYN if negative to avoid negative indexes in
- YYCHECK. */
- int yyxbegin = yyn < 0 ? -yyn : 0;
-
- /* Stay within bounds of both yycheck and yytname. */
- int yychecklim = YYLAST - yyn + 1;
- int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
- int yycount = 1;
-
- yyarg[0] = yytname[yytype];
- yyfmt = yystpcpy (yyformat, yyunexpected);
-
- for (yyx = yyxbegin; yyx < yyxend; ++yyx)
- if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
- {
- if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
- {
- yycount = 1;
- yysize = yysize0;
- yyformat[sizeof yyunexpected - 1] = '\0';
- break;
- }
- yyarg[yycount++] = yytname[yyx];
- yysize1 = yysize + yytnamerr (0, yytname[yyx]);
- yysize_overflow |= (yysize1 < yysize);
- yysize = yysize1;
- yyfmt = yystpcpy (yyfmt, yyprefix);
- yyprefix = yyor;
- }
+ Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is
+ not large enough to hold the message. In that case, also set
+ *YYMSG_ALLOC to the required number of bytes. Return 2 if the
+ required number of bytes is too large to store. */
+static int
+yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg,
+ yytype_int16 *yyssp, int yytoken)
+{
+ YYSIZE_T yysize0 = yytnamerr (0, yytname[yytoken]);
+ YYSIZE_T yysize = yysize0;
+ YYSIZE_T yysize1;
+ enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+ /* Internationalized format string. */
+ const char *yyformat = 0;
+ /* Arguments of yyformat. */
+ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+ /* Number of reported tokens (one for the "unexpected", one per
+ "expected"). */
+ int yycount = 0;
+
+ /* There are many possibilities here to consider:
+ - Assume YYFAIL is not used. It's too flawed to consider. See
+ <http://lists.gnu.org/archive/html/bison-patches/2009-12/msg00024.html>
+ for details. YYERROR is fine as it does not invoke this
+ function.
+ - If this state is a consistent state with a default action, then
+ the only way this function was invoked is if the default action
+ is an error action. In that case, don't check for expected
+ tokens because there are none.
+ - The only way there can be no lookahead present (in yychar) is if
+ this state is a consistent state with a default action. Thus,
+ detecting the absence of a lookahead is sufficient to determine
+ that there is no unexpected or expected token to report. In that
+ case, just report a simple "syntax error".
+ - Don't assume there isn't a lookahead just because this state is a
+ consistent state with a default action. There might have been a
+ previous inconsistent state, consistent state with a non-default
+ action, or user semantic action that manipulated yychar.
+ - Of course, the expected token list depends on states to have
+ correct lookahead information, and it depends on the parser not
+ to perform extra reductions after fetching a lookahead from the
+ scanner and before detecting a syntax error. Thus, state merging
+ (from LALR or IELR) and default reductions corrupt the expected
+ token list. However, the list is correct for canonical LR with
+ one exception: it will still contain any token that will not be
+ accepted due to an error action in a later state.
+ */
+ if (yytoken != YYEMPTY)
+ {
+ int yyn = yypact[*yyssp];
+ yyarg[yycount++] = yytname[yytoken];
+ if (!yypact_value_is_default (yyn))
+ {
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. In other words, skip the first -YYN actions for
+ this state because they are default actions. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn + 1;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yyx;
+
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR
+ && !yytable_value_is_error (yytable[yyx + yyn]))
+ {
+ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+ {
+ yycount = 1;
+ yysize = yysize0;
+ break;
+ }
+ yyarg[yycount++] = yytname[yyx];
+ yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+ if (! (yysize <= yysize1
+ && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+ return 2;
+ yysize = yysize1;
+ }
+ }
+ }
- yyf = YY_(yyformat);
- yysize1 = yysize + yystrlen (yyf);
- yysize_overflow |= (yysize1 < yysize);
- yysize = yysize1;
+ switch (yycount)
+ {
+# define YYCASE_(N, S) \
+ case N: \
+ yyformat = S; \
+ break
+ YYCASE_(0, YY_("syntax error"));
+ YYCASE_(1, YY_("syntax error, unexpected %s"));
+ YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s"));
+ YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s"));
+ YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s"));
+ YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"));
+# undef YYCASE_
+ }
- if (yysize_overflow)
- return YYSIZE_MAXIMUM;
+ yysize1 = yysize + yystrlen (yyformat);
+ if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+ return 2;
+ yysize = yysize1;
- if (yyresult)
- {
- /* Avoid sprintf, as that infringes on the user's name space.
- Don't have undefined behavior even if the translation
- produced a string with the wrong number of "%s"s. */
- char *yyp = yyresult;
- int yyi = 0;
- while ((*yyp = *yyf) != '\0')
- {
- if (*yyp == '%' && yyf[1] == 's' && yyi < yycount)
- {
- yyp += yytnamerr (yyp, yyarg[yyi++]);
- yyf += 2;
- }
- else
- {
- yyp++;
- yyf++;
- }
- }
- }
- return yysize;
+ if (*yymsg_alloc < yysize)
+ {
+ *yymsg_alloc = 2 * yysize;
+ if (! (yysize <= *yymsg_alloc
+ && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM))
+ *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM;
+ return 1;
}
+
+ /* Avoid sprintf, as that infringes on the user's name space.
+ Don't have undefined behavior even if the translation
+ produced a string with the wrong number of "%s"s. */
+ {
+ char *yyp = *yymsg;
+ int yyi = 0;
+ while ((*yyp = *yyformat) != '\0')
+ if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount)
+ {
+ yyp += yytnamerr (yyp, yyarg[yyi++]);
+ yyformat += 2;
+ }
+ else
+ {
+ yyp++;
+ yyformat++;
+ }
+ }
+ return 0;
}
#endif /* YYERROR_VERBOSE */
-
/*-----------------------------------------------.
| Release the memory associated to this symbol. |
@@ -2063,10 +2134,9 @@ yydestruct (yymsg, yytype, yyvaluep)
break;
}
}
-
-/* Prevent warnings from -Wmissing-prototypes. */
+/* Prevent warnings from -Wmissing-prototypes. */
#ifdef YYPARSE_PARAM
#if defined __STDC__ || defined __cplusplus
int yyparse (void *YYPARSE_PARAM);
@@ -2082,18 +2152,16 @@ int yyparse ();
#endif /* ! YYPARSE_PARAM */
-
-/* The look-ahead symbol. */
+/* The lookahead symbol. */
int yychar, yystate;
-/* The semantic value of the look-ahead symbol. */
+/* The semantic value of the lookahead symbol. */
YYSTYPE yylval;
/* Number of syntax errors so far. */
int yynerrs;
-
/*----------.
| yyparse. |
`----------*/
@@ -2120,65 +2188,65 @@ yyparse ()
#endif
#endif
{
-
- int yyn;
- int yyresult;
- /* Number of tokens to shift before error messages enabled. */
- int yyerrstatus;
- /* Look-ahead token as an internal (translated) token number. */
- int yytoken = 0;
-#if YYERROR_VERBOSE
- /* Buffer for error messages, and its allocated size. */
- char yymsgbuf[128];
- char *yymsg = yymsgbuf;
- YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
-#endif
-
- /* Three stacks and their tools:
- `yyss': related to states,
- `yyvs': related to semantic values,
- `yyls': related to locations.
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
- Refer to the stacks thru separate pointers, to allow yyoverflow
- to reallocate them elsewhere. */
+ /* The stacks and their tools:
+ `yyss': related to states.
+ `yyvs': related to semantic values.
- /* The state stack. */
- yytype_int16 yyssa[YYINITDEPTH];
- yytype_int16 *yyss = yyssa;
- yytype_int16 *yyssp;
+ Refer to the stacks thru separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
- /* The semantic value stack. */
- YYSTYPE yyvsa[YYINITDEPTH];
- YYSTYPE *yyvs = yyvsa;
- YYSTYPE *yyvsp;
+ /* The state stack. */
+ yytype_int16 yyssa[YYINITDEPTH];
+ yytype_int16 *yyss;
+ yytype_int16 *yyssp;
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs;
+ YYSTYPE *yyvsp;
+ YYSIZE_T yystacksize;
-#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N))
-
- YYSIZE_T yystacksize = YYINITDEPTH;
-
+ int yyn;
+ int yyresult;
+ /* Lookahead token as an internal (translated) token number. */
+ int yytoken;
/* The variables used to return semantic value and location from the
action routines. */
YYSTYPE yyval;
+#if YYERROR_VERBOSE
+ /* Buffer for error messages, and its allocated size. */
+ char yymsgbuf[128];
+ char *yymsg = yymsgbuf;
+ YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N))
/* The number of symbols on the RHS of the reduced rule.
Keep to zero when no symbol should be popped. */
int yylen = 0;
+ yytoken = 0;
+ yyss = yyssa;
+ yyvs = yyvsa;
+ yystacksize = YYINITDEPTH;
+
YYDPRINTF ((stderr, "Starting parse\n"));
yystate = 0;
yyerrstatus = 0;
yynerrs = 0;
- yychar = YYEMPTY; /* Cause a token to be read. */
+ yychar = YYEMPTY; /* Cause a token to be read. */
/* Initialize stack pointers.
Waste one element of value and location stack
so that they stay on the same level as the state stack.
The wasted elements are never initialized. */
-
yyssp = yyss;
yyvsp = yyvs;
@@ -2208,7 +2276,6 @@ yyparse ()
YYSTYPE *yyvs1 = yyvs;
yytype_int16 *yyss1 = yyss;
-
/* Each stack pointer address is followed by the size of the
data in use in that stack, in bytes. This used to be a
conditional around just the two extra args, but that might
@@ -2216,7 +2283,6 @@ yyparse ()
yyoverflow (YY_("memory exhausted"),
&yyss1, yysize * sizeof (*yyssp),
&yyvs1, yysize * sizeof (*yyvsp),
-
&yystacksize);
yyss = yyss1;
@@ -2239,9 +2305,8 @@ yyparse ()
(union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
if (! yyptr)
goto yyexhaustedlab;
- YYSTACK_RELOCATE (yyss);
- YYSTACK_RELOCATE (yyvs);
-
+ YYSTACK_RELOCATE (yyss_alloc, yyss);
+ YYSTACK_RELOCATE (yyvs_alloc, yyvs);
# undef YYSTACK_RELOCATE
if (yyss1 != yyssa)
YYSTACK_FREE (yyss1);
@@ -2252,7 +2317,6 @@ yyparse ()
yyssp = yyss + yysize - 1;
yyvsp = yyvs + yysize - 1;
-
YYDPRINTF ((stderr, "Stack size increased to %lu\n",
(unsigned long int) yystacksize));
@@ -2262,6 +2326,9 @@ yyparse ()
YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+ if (yystate == YYFINAL)
+ YYACCEPT;
+
goto yybackup;
/*-----------.
@@ -2270,16 +2337,16 @@ yyparse ()
yybackup:
/* Do appropriate processing given the current state. Read a
- look-ahead token if we need one and don't already have one. */
+ lookahead token if we need one and don't already have one. */
- /* First try to decide what to do without reference to look-ahead token. */
+ /* First try to decide what to do without reference to lookahead token. */
yyn = yypact[yystate];
- if (yyn == YYPACT_NINF)
+ if (yypact_value_is_default (yyn))
goto yydefault;
- /* Not known => get a look-ahead token if don't already have one. */
+ /* Not known => get a lookahead token if don't already have one. */
- /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */
if (yychar == YYEMPTY)
{
YYDPRINTF ((stderr, "Reading a token: "));
@@ -2305,26 +2372,22 @@ yybackup:
yyn = yytable[yyn];
if (yyn <= 0)
{
- if (yyn == 0 || yyn == YYTABLE_NINF)
- goto yyerrlab;
+ if (yytable_value_is_error (yyn))
+ goto yyerrlab;
yyn = -yyn;
goto yyreduce;
}
- if (yyn == YYFINAL)
- YYACCEPT;
-
/* Count tokens shifted since error; after three, turn off error
status. */
if (yyerrstatus)
yyerrstatus--;
- /* Shift the look-ahead token. */
+ /* Shift the lookahead token. */
YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
- /* Discard the shifted token unless it is eof. */
- if (yychar != YYEOF)
- yychar = YYEMPTY;
+ /* Discard the shifted token. */
+ yychar = YYEMPTY;
yystate = yyn;
*++yyvsp = yylval;
@@ -2364,6 +2427,8 @@ yyreduce:
switch (yyn)
{
case 2:
+
+/* Line 1806 of yacc.c */
#line 128 "go.y"
{
xtop = concat(xtop, (yyvsp[(4) - (4)].list));
@@ -2371,24 +2436,29 @@ yyreduce:
break;
case 3:
+
+/* Line 1806 of yacc.c */
#line 134 "go.y"
{
prevlineno = lineno;
yyerror("package statement must be first");
- flusherrors();
- mkpackage("main");
+ errorexit();
}
break;
case 4:
-#line 141 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 140 "go.y"
{
mkpackage((yyvsp[(2) - (3)].sym)->name);
}
break;
case 5:
-#line 151 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 150 "go.y"
{
importpkg = runtimepkg;
@@ -2401,14 +2471,18 @@ yyreduce:
break;
case 6:
-#line 162 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 161 "go.y"
{
importpkg = nil;
}
break;
case 12:
-#line 176 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 175 "go.y"
{
Pkg *ipkg;
Sym *my;
@@ -2431,6 +2505,10 @@ yyreduce:
importdot(ipkg, pack);
break;
}
+ if(strcmp(my->name, "init") == 0) {
+ yyerror("cannot import package as init - init must be a func");
+ break;
+ }
if(my->name[0] == '_' && my->name[1] == '\0')
break;
if(my->def) {
@@ -2444,7 +2522,9 @@ yyreduce:
break;
case 13:
-#line 209 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 212 "go.y"
{
// When an invalid import path is passed to importfile,
// it calls yyerror and then sets up a fake import with
@@ -2456,7 +2536,9 @@ yyreduce:
break;
case 16:
-#line 224 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 227 "go.y"
{
// import with original name
(yyval.i) = parserline();
@@ -2466,7 +2548,9 @@ yyreduce:
break;
case 17:
-#line 231 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 234 "go.y"
{
// import with given name
(yyval.i) = parserline();
@@ -2476,7 +2560,9 @@ yyreduce:
break;
case 18:
-#line 238 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 241 "go.y"
{
// import into my name space
(yyval.i) = parserline();
@@ -2486,7 +2572,9 @@ yyreduce:
break;
case 19:
-#line 247 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 250 "go.y"
{
if(importpkg->name == nil) {
importpkg->name = (yyvsp[(2) - (4)].sym)->name;
@@ -2502,7 +2590,9 @@ yyreduce:
break;
case 21:
-#line 262 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 265 "go.y"
{
if(strcmp((yyvsp[(1) - (1)].sym)->name, "safe") == 0)
curio.importsafe = 1;
@@ -2510,14 +2600,18 @@ yyreduce:
break;
case 22:
-#line 268 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 271 "go.y"
{
defercheckwidth();
}
break;
case 23:
-#line 272 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 275 "go.y"
{
resumecheckwidth();
unimportfile();
@@ -2525,7 +2619,9 @@ yyreduce:
break;
case 24:
-#line 281 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 284 "go.y"
{
yyerror("empty top-level declaration");
(yyval.list) = nil;
@@ -2533,14 +2629,18 @@ yyreduce:
break;
case 26:
-#line 287 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 290 "go.y"
{
(yyval.list) = list1((yyvsp[(1) - (1)].node));
}
break;
case 27:
-#line 291 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 294 "go.y"
{
yyerror("non-declaration statement outside function body");
(yyval.list) = nil;
@@ -2548,35 +2648,45 @@ yyreduce:
break;
case 28:
-#line 296 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 299 "go.y"
{
(yyval.list) = nil;
}
break;
case 29:
-#line 302 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 305 "go.y"
{
(yyval.list) = (yyvsp[(2) - (2)].list);
}
break;
case 30:
-#line 306 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 309 "go.y"
{
(yyval.list) = (yyvsp[(3) - (5)].list);
}
break;
case 31:
-#line 310 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 313 "go.y"
{
(yyval.list) = nil;
}
break;
case 32:
-#line 314 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 317 "go.y"
{
(yyval.list) = (yyvsp[(2) - (2)].list);
iota = -100000;
@@ -2585,7 +2695,9 @@ yyreduce:
break;
case 33:
-#line 320 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 323 "go.y"
{
(yyval.list) = (yyvsp[(3) - (5)].list);
iota = -100000;
@@ -2594,7 +2706,9 @@ yyreduce:
break;
case 34:
-#line 326 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 329 "go.y"
{
(yyval.list) = concat((yyvsp[(3) - (7)].list), (yyvsp[(5) - (7)].list));
iota = -100000;
@@ -2603,7 +2717,9 @@ yyreduce:
break;
case 35:
-#line 332 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 335 "go.y"
{
(yyval.list) = nil;
iota = -100000;
@@ -2611,84 +2727,108 @@ yyreduce:
break;
case 36:
-#line 337 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 340 "go.y"
{
(yyval.list) = list1((yyvsp[(2) - (2)].node));
}
break;
case 37:
-#line 341 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 344 "go.y"
{
(yyval.list) = (yyvsp[(3) - (5)].list);
}
break;
case 38:
-#line 345 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 348 "go.y"
{
(yyval.list) = nil;
}
break;
case 39:
-#line 351 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 354 "go.y"
{
iota = 0;
}
break;
case 40:
-#line 357 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 360 "go.y"
{
(yyval.list) = variter((yyvsp[(1) - (2)].list), (yyvsp[(2) - (2)].node), nil);
}
break;
case 41:
-#line 361 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 364 "go.y"
{
(yyval.list) = variter((yyvsp[(1) - (4)].list), (yyvsp[(2) - (4)].node), (yyvsp[(4) - (4)].list));
}
break;
case 42:
-#line 365 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 368 "go.y"
{
(yyval.list) = variter((yyvsp[(1) - (3)].list), nil, (yyvsp[(3) - (3)].list));
}
break;
case 43:
-#line 371 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 374 "go.y"
{
(yyval.list) = constiter((yyvsp[(1) - (4)].list), (yyvsp[(2) - (4)].node), (yyvsp[(4) - (4)].list));
}
break;
case 44:
-#line 375 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 378 "go.y"
{
(yyval.list) = constiter((yyvsp[(1) - (3)].list), N, (yyvsp[(3) - (3)].list));
}
break;
case 46:
-#line 382 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 385 "go.y"
{
(yyval.list) = constiter((yyvsp[(1) - (2)].list), (yyvsp[(2) - (2)].node), nil);
}
break;
case 47:
-#line 386 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 389 "go.y"
{
(yyval.list) = constiter((yyvsp[(1) - (1)].list), N, nil);
}
break;
case 48:
-#line 392 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 395 "go.y"
{
// different from dclname because the name
// becomes visible right here, not at the end
@@ -2698,14 +2838,18 @@ yyreduce:
break;
case 49:
-#line 401 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 404 "go.y"
{
(yyval.node) = typedcl1((yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node), 1);
}
break;
case 50:
-#line 407 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 410 "go.y"
{
(yyval.node) = (yyvsp[(1) - (1)].node);
@@ -2726,7 +2870,9 @@ yyreduce:
break;
case 51:
-#line 425 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 428 "go.y"
{
(yyval.node) = nod(OASOP, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
(yyval.node)->etype = (yyvsp[(2) - (3)].i); // rathole to pass opcode
@@ -2734,7 +2880,9 @@ yyreduce:
break;
case 52:
-#line 430 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 433 "go.y"
{
if((yyvsp[(1) - (3)].list)->next == nil && (yyvsp[(3) - (3)].list)->next == nil) {
// simple
@@ -2749,7 +2897,9 @@ yyreduce:
break;
case 53:
-#line 442 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 445 "go.y"
{
if((yyvsp[(3) - (3)].list)->n->op == OTYPESW) {
(yyval.node) = nod(OTYPESW, N, (yyvsp[(3) - (3)].list)->n->right);
@@ -2768,7 +2918,9 @@ yyreduce:
break;
case 54:
-#line 458 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 461 "go.y"
{
(yyval.node) = nod(OASOP, (yyvsp[(1) - (2)].node), nodintconst(1));
(yyval.node)->etype = OADD;
@@ -2776,7 +2928,9 @@ yyreduce:
break;
case 55:
-#line 463 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 466 "go.y"
{
(yyval.node) = nod(OASOP, (yyvsp[(1) - (2)].node), nodintconst(1));
(yyval.node)->etype = OSUB;
@@ -2784,7 +2938,9 @@ yyreduce:
break;
case 56:
-#line 470 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 473 "go.y"
{
Node *n, *nn;
@@ -2807,7 +2963,9 @@ yyreduce:
break;
case 57:
-#line 490 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 493 "go.y"
{
Node *n;
@@ -2828,7 +2986,9 @@ yyreduce:
break;
case 58:
-#line 508 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 511 "go.y"
{
// will be converted to OCASE
// right will point to next case
@@ -2840,7 +3000,9 @@ yyreduce:
break;
case 59:
-#line 517 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 520 "go.y"
{
Node *n, *nn;
@@ -2859,14 +3021,18 @@ yyreduce:
break;
case 60:
-#line 535 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 538 "go.y"
{
markdcl();
}
break;
case 61:
-#line 539 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 542 "go.y"
{
if((yyvsp[(3) - (4)].list) == nil)
(yyval.node) = nod(OEMPTY, N, N);
@@ -2877,7 +3043,9 @@ yyreduce:
break;
case 62:
-#line 549 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 552 "go.y"
{
// If the last token read by the lexer was consumed
// as part of the case, clear it (parser has cleared yychar).
@@ -2890,7 +3058,9 @@ yyreduce:
break;
case 63:
-#line 559 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 562 "go.y"
{
int last;
@@ -2912,28 +3082,36 @@ yyreduce:
break;
case 64:
-#line 579 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 582 "go.y"
{
(yyval.list) = nil;
}
break;
case 65:
-#line 583 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 586 "go.y"
{
(yyval.list) = list((yyvsp[(1) - (2)].list), (yyvsp[(2) - (2)].node));
}
break;
case 66:
-#line 589 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 592 "go.y"
{
markdcl();
}
break;
case 67:
-#line 593 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 596 "go.y"
{
(yyval.list) = (yyvsp[(3) - (4)].list);
popdcl();
@@ -2941,7 +3119,9 @@ yyreduce:
break;
case 68:
-#line 600 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 603 "go.y"
{
(yyval.node) = nod(ORANGE, N, (yyvsp[(4) - (4)].node));
(yyval.node)->list = (yyvsp[(1) - (4)].list);
@@ -2950,7 +3130,9 @@ yyreduce:
break;
case 69:
-#line 606 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 609 "go.y"
{
(yyval.node) = nod(ORANGE, N, (yyvsp[(4) - (4)].node));
(yyval.node)->list = (yyvsp[(1) - (4)].list);
@@ -2960,7 +3142,9 @@ yyreduce:
break;
case 70:
-#line 615 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 618 "go.y"
{
// init ; test ; incr
if((yyvsp[(5) - (5)].node) != N && (yyvsp[(5) - (5)].node)->colas != 0)
@@ -2974,7 +3158,9 @@ yyreduce:
break;
case 71:
-#line 626 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 629 "go.y"
{
// normal test
(yyval.node) = nod(OFOR, N, N);
@@ -2983,7 +3169,9 @@ yyreduce:
break;
case 73:
-#line 635 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 638 "go.y"
{
(yyval.node) = (yyvsp[(1) - (2)].node);
(yyval.node)->nbody = concat((yyval.node)->nbody, (yyvsp[(2) - (2)].list));
@@ -2991,14 +3179,18 @@ yyreduce:
break;
case 74:
-#line 642 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 645 "go.y"
{
markdcl();
}
break;
case 75:
-#line 646 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 649 "go.y"
{
(yyval.node) = (yyvsp[(3) - (3)].node);
popdcl();
@@ -3006,7 +3198,9 @@ yyreduce:
break;
case 76:
-#line 653 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 656 "go.y"
{
// test
(yyval.node) = nod(OIF, N, N);
@@ -3015,7 +3209,9 @@ yyreduce:
break;
case 77:
-#line 659 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 662 "go.y"
{
// init ; test
(yyval.node) = nod(OIF, N, N);
@@ -3026,14 +3222,18 @@ yyreduce:
break;
case 78:
-#line 670 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 673 "go.y"
{
markdcl();
}
break;
case 79:
-#line 674 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 677 "go.y"
{
if((yyvsp[(3) - (3)].node)->ntest == N)
yyerror("missing condition in if statement");
@@ -3041,14 +3241,18 @@ yyreduce:
break;
case 80:
-#line 679 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 682 "go.y"
{
(yyvsp[(3) - (5)].node)->nbody = (yyvsp[(5) - (5)].list);
}
break;
case 81:
-#line 683 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 686 "go.y"
{
Node *n;
NodeList *nn;
@@ -3066,14 +3270,18 @@ yyreduce:
break;
case 82:
-#line 700 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 703 "go.y"
{
markdcl();
}
break;
case 83:
-#line 704 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 707 "go.y"
{
if((yyvsp[(4) - (5)].node)->ntest == N)
yyerror("missing condition in if statement");
@@ -3083,28 +3291,36 @@ yyreduce:
break;
case 84:
-#line 712 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 715 "go.y"
{
(yyval.list) = nil;
}
break;
case 85:
-#line 716 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 719 "go.y"
{
(yyval.list) = concat((yyvsp[(1) - (2)].list), (yyvsp[(2) - (2)].list));
}
break;
case 86:
-#line 721 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 724 "go.y"
{
(yyval.list) = nil;
}
break;
case 87:
-#line 725 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 728 "go.y"
{
NodeList *node;
@@ -3116,14 +3332,18 @@ yyreduce:
break;
case 88:
-#line 736 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 739 "go.y"
{
markdcl();
}
break;
case 89:
-#line 740 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 743 "go.y"
{
Node *n;
n = (yyvsp[(3) - (3)].node)->ntest;
@@ -3134,7 +3354,9 @@ yyreduce:
break;
case 90:
-#line 748 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 751 "go.y"
{
(yyval.node) = (yyvsp[(3) - (7)].node);
(yyval.node)->op = OSWITCH;
@@ -3145,14 +3367,18 @@ yyreduce:
break;
case 91:
-#line 758 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 761 "go.y"
{
typesw = nod(OXXX, typesw, N);
}
break;
case 92:
-#line 762 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 765 "go.y"
{
(yyval.node) = nod(OSELECT, N, N);
(yyval.node)->lineno = typesw->lineno;
@@ -3162,154 +3388,198 @@ yyreduce:
break;
case 94:
-#line 775 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 778 "go.y"
{
(yyval.node) = nod(OOROR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
case 95:
-#line 779 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 782 "go.y"
{
(yyval.node) = nod(OANDAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
case 96:
-#line 783 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 786 "go.y"
{
(yyval.node) = nod(OEQ, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
case 97:
-#line 787 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 790 "go.y"
{
(yyval.node) = nod(ONE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
case 98:
-#line 791 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 794 "go.y"
{
(yyval.node) = nod(OLT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
case 99:
-#line 795 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 798 "go.y"
{
(yyval.node) = nod(OLE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
case 100:
-#line 799 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 802 "go.y"
{
(yyval.node) = nod(OGE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
case 101:
-#line 803 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 806 "go.y"
{
(yyval.node) = nod(OGT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
case 102:
-#line 807 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 810 "go.y"
{
(yyval.node) = nod(OADD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
case 103:
-#line 811 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 814 "go.y"
{
(yyval.node) = nod(OSUB, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
case 104:
-#line 815 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 818 "go.y"
{
(yyval.node) = nod(OOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
case 105:
-#line 819 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 822 "go.y"
{
(yyval.node) = nod(OXOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
case 106:
-#line 823 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 826 "go.y"
{
(yyval.node) = nod(OMUL, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
case 107:
-#line 827 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 830 "go.y"
{
(yyval.node) = nod(ODIV, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
case 108:
-#line 831 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 834 "go.y"
{
(yyval.node) = nod(OMOD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
case 109:
-#line 835 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 838 "go.y"
{
(yyval.node) = nod(OAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
case 110:
-#line 839 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 842 "go.y"
{
(yyval.node) = nod(OANDNOT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
case 111:
-#line 843 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 846 "go.y"
{
(yyval.node) = nod(OLSH, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
case 112:
-#line 847 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 850 "go.y"
{
(yyval.node) = nod(ORSH, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
case 113:
-#line 852 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 855 "go.y"
{
(yyval.node) = nod(OSEND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
case 115:
-#line 859 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 862 "go.y"
{
(yyval.node) = nod(OIND, (yyvsp[(2) - (2)].node), N);
}
break;
case 116:
-#line 863 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 866 "go.y"
{
if((yyvsp[(2) - (2)].node)->op == OCOMPLIT) {
// Special case for &T{...}: turn into (*T){...}.
@@ -3323,28 +3593,36 @@ yyreduce:
break;
case 117:
-#line 874 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 877 "go.y"
{
(yyval.node) = nod(OPLUS, (yyvsp[(2) - (2)].node), N);
}
break;
case 118:
-#line 878 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 881 "go.y"
{
(yyval.node) = nod(OMINUS, (yyvsp[(2) - (2)].node), N);
}
break;
case 119:
-#line 882 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 885 "go.y"
{
(yyval.node) = nod(ONOT, (yyvsp[(2) - (2)].node), N);
}
break;
case 120:
-#line 886 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 889 "go.y"
{
yyerror("the bitwise complement operator is ^");
(yyval.node) = nod(OCOM, (yyvsp[(2) - (2)].node), N);
@@ -3352,28 +3630,36 @@ yyreduce:
break;
case 121:
-#line 891 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 894 "go.y"
{
(yyval.node) = nod(OCOM, (yyvsp[(2) - (2)].node), N);
}
break;
case 122:
-#line 895 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 898 "go.y"
{
(yyval.node) = nod(ORECV, (yyvsp[(2) - (2)].node), N);
}
break;
case 123:
-#line 905 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 908 "go.y"
{
(yyval.node) = nod(OCALL, (yyvsp[(1) - (3)].node), N);
}
break;
case 124:
-#line 909 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 912 "go.y"
{
(yyval.node) = nod(OCALL, (yyvsp[(1) - (5)].node), N);
(yyval.node)->list = (yyvsp[(3) - (5)].list);
@@ -3381,7 +3667,9 @@ yyreduce:
break;
case 125:
-#line 914 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 917 "go.y"
{
(yyval.node) = nod(OCALL, (yyvsp[(1) - (6)].node), N);
(yyval.node)->list = (yyvsp[(3) - (6)].list);
@@ -3390,14 +3678,18 @@ yyreduce:
break;
case 126:
-#line 922 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 925 "go.y"
{
(yyval.node) = nodlit((yyvsp[(1) - (1)].val));
}
break;
case 128:
-#line 927 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 930 "go.y"
{
if((yyvsp[(1) - (3)].node)->op == OPACK) {
Sym *s;
@@ -3411,35 +3703,58 @@ yyreduce:
break;
case 129:
-#line 938 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 941 "go.y"
{
(yyval.node) = nod(ODOTTYPE, (yyvsp[(1) - (5)].node), (yyvsp[(4) - (5)].node));
}
break;
case 130:
-#line 942 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 945 "go.y"
{
(yyval.node) = nod(OTYPESW, N, (yyvsp[(1) - (5)].node));
}
break;
case 131:
-#line 946 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 949 "go.y"
{
(yyval.node) = nod(OINDEX, (yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].node));
}
break;
case 132:
-#line 950 "go.y"
+
+/* Line 1806 of yacc.c */
+#line 953 "go.y"
{
(yyval.node) = nod(OSLICE, (yyvsp[(1) - (6)].node), nod(OKEY, (yyvsp[(3) - (6)].node), (yyvsp[(5) - (6)].node)));
}
break;
- case 134:
-#line 955 "go.y"
+ case 133:
+
+/* Line 1806 of yacc.c */
+#line 957 "go.y"
+ {
+ if((yyvsp[(5) - (8)].node) == N)
+ yyerror("middle index required in 3-index slice");
+ if((yyvsp[(7) - (8)].node) == N)
+ yyerror("final index required in 3-index slice");
+ (yyval.node) = nod(OSLICE3, (yyvsp[(1) - (8)].node), nod(OKEY, (yyvsp[(3) - (8)].node), nod(OKEY, (yyvsp[(5) - (8)].node), (yyvsp[(7) - (8)].node))));
+ }
+ break;
+
+ case 135:
+
+/* Line 1806 of yacc.c */
+#line 966 "go.y"
{
// conversion
(yyval.node) = nod(OCALL, (yyvsp[(1) - (5)].node), N);
@@ -3447,8 +3762,10 @@ yyreduce:
}
break;
- case 135:
-#line 961 "go.y"
+ case 136:
+
+/* Line 1806 of yacc.c */
+#line 972 "go.y"
{
(yyval.node) = (yyvsp[(3) - (5)].node);
(yyval.node)->right = (yyvsp[(1) - (5)].node);
@@ -3457,8 +3774,10 @@ yyreduce:
}
break;
- case 136:
-#line 968 "go.y"
+ case 137:
+
+/* Line 1806 of yacc.c */
+#line 979 "go.y"
{
(yyval.node) = (yyvsp[(3) - (5)].node);
(yyval.node)->right = (yyvsp[(1) - (5)].node);
@@ -3466,8 +3785,10 @@ yyreduce:
}
break;
- case 137:
-#line 974 "go.y"
+ case 138:
+
+/* Line 1806 of yacc.c */
+#line 985 "go.y"
{
yyerror("cannot parenthesize type in composite literal");
(yyval.node) = (yyvsp[(5) - (7)].node);
@@ -3476,8 +3797,10 @@ yyreduce:
}
break;
- case 139:
-#line 983 "go.y"
+ case 140:
+
+/* Line 1806 of yacc.c */
+#line 994 "go.y"
{
// composite expression.
// make node early so we get the right line number.
@@ -3485,15 +3808,19 @@ yyreduce:
}
break;
- case 140:
-#line 991 "go.y"
+ case 141:
+
+/* Line 1806 of yacc.c */
+#line 1002 "go.y"
{
(yyval.node) = nod(OKEY, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
}
break;
- case 141:
-#line 997 "go.y"
+ case 142:
+
+/* Line 1806 of yacc.c */
+#line 1008 "go.y"
{
// These nodes do not carry line numbers.
// Since a composite literal commonly spans several lines,
@@ -3512,24 +3839,30 @@ yyreduce:
}
break;
- case 142:
-#line 1014 "go.y"
+ case 143:
+
+/* Line 1806 of yacc.c */
+#line 1025 "go.y"
{
(yyval.node) = (yyvsp[(2) - (4)].node);
(yyval.node)->list = (yyvsp[(3) - (4)].list);
}
break;
- case 144:
-#line 1022 "go.y"
+ case 145:
+
+/* Line 1806 of yacc.c */
+#line 1033 "go.y"
{
(yyval.node) = (yyvsp[(2) - (4)].node);
(yyval.node)->list = (yyvsp[(3) - (4)].list);
}
break;
- case 146:
-#line 1030 "go.y"
+ case 147:
+
+/* Line 1806 of yacc.c */
+#line 1041 "go.y"
{
(yyval.node) = (yyvsp[(2) - (3)].node);
@@ -3548,22 +3881,28 @@ yyreduce:
}
break;
- case 150:
-#line 1056 "go.y"
+ case 151:
+
+/* Line 1806 of yacc.c */
+#line 1067 "go.y"
{
(yyval.i) = LBODY;
}
break;
- case 151:
-#line 1060 "go.y"
+ case 152:
+
+/* Line 1806 of yacc.c */
+#line 1071 "go.y"
{
(yyval.i) = '{';
}
break;
- case 152:
-#line 1071 "go.y"
+ case 153:
+
+/* Line 1806 of yacc.c */
+#line 1082 "go.y"
{
if((yyvsp[(1) - (1)].sym) == S)
(yyval.node) = N;
@@ -3572,22 +3911,28 @@ yyreduce:
}
break;
- case 153:
-#line 1080 "go.y"
+ case 154:
+
+/* Line 1806 of yacc.c */
+#line 1091 "go.y"
{
(yyval.node) = dclname((yyvsp[(1) - (1)].sym));
}
break;
- case 154:
-#line 1085 "go.y"
+ case 155:
+
+/* Line 1806 of yacc.c */
+#line 1096 "go.y"
{
(yyval.node) = N;
}
break;
- case 156:
-#line 1092 "go.y"
+ case 157:
+
+/* Line 1806 of yacc.c */
+#line 1103 "go.y"
{
(yyval.sym) = (yyvsp[(1) - (1)].sym);
// during imports, unqualified non-exported identifiers are from builtinpkg
@@ -3596,15 +3941,19 @@ yyreduce:
}
break;
- case 158:
-#line 1100 "go.y"
+ case 159:
+
+/* Line 1806 of yacc.c */
+#line 1111 "go.y"
{
(yyval.sym) = S;
}
break;
- case 159:
-#line 1106 "go.y"
+ case 160:
+
+/* Line 1806 of yacc.c */
+#line 1117 "go.y"
{
Pkg *p;
@@ -3619,8 +3968,28 @@ yyreduce:
}
break;
- case 160:
-#line 1121 "go.y"
+ case 161:
+
+/* Line 1806 of yacc.c */
+#line 1130 "go.y"
+ {
+ Pkg *p;
+
+ if((yyvsp[(2) - (4)].val).u.sval->len == 0)
+ p = importpkg;
+ else {
+ if(isbadimport((yyvsp[(2) - (4)].val).u.sval))
+ errorexit();
+ p = mkpkg((yyvsp[(2) - (4)].val).u.sval);
+ }
+ (yyval.sym) = pkglookup("?", p);
+ }
+ break;
+
+ case 162:
+
+/* Line 1806 of yacc.c */
+#line 1145 "go.y"
{
(yyval.node) = oldname((yyvsp[(1) - (1)].sym));
if((yyval.node)->pack != N)
@@ -3628,44 +3997,56 @@ yyreduce:
}
break;
- case 162:
-#line 1141 "go.y"
+ case 164:
+
+/* Line 1806 of yacc.c */
+#line 1165 "go.y"
{
yyerror("final argument in variadic function missing type");
(yyval.node) = nod(ODDD, typenod(typ(TINTER)), N);
}
break;
- case 163:
-#line 1146 "go.y"
+ case 165:
+
+/* Line 1806 of yacc.c */
+#line 1170 "go.y"
{
(yyval.node) = nod(ODDD, (yyvsp[(2) - (2)].node), N);
}
break;
- case 169:
-#line 1157 "go.y"
+ case 171:
+
+/* Line 1806 of yacc.c */
+#line 1181 "go.y"
{
(yyval.node) = nod(OTPAREN, (yyvsp[(2) - (3)].node), N);
}
break;
- case 173:
-#line 1166 "go.y"
+ case 175:
+
+/* Line 1806 of yacc.c */
+#line 1190 "go.y"
{
(yyval.node) = nod(OIND, (yyvsp[(2) - (2)].node), N);
}
break;
- case 178:
-#line 1176 "go.y"
+ case 180:
+
+/* Line 1806 of yacc.c */
+#line 1200 "go.y"
{
(yyval.node) = nod(OTPAREN, (yyvsp[(2) - (3)].node), N);
}
break;
- case 188:
-#line 1197 "go.y"
+ case 190:
+
+/* Line 1806 of yacc.c */
+#line 1221 "go.y"
{
if((yyvsp[(1) - (3)].node)->op == OPACK) {
Sym *s;
@@ -3678,61 +4059,77 @@ yyreduce:
}
break;
- case 189:
-#line 1210 "go.y"
+ case 191:
+
+/* Line 1806 of yacc.c */
+#line 1234 "go.y"
{
(yyval.node) = nod(OTARRAY, (yyvsp[(2) - (4)].node), (yyvsp[(4) - (4)].node));
}
break;
- case 190:
-#line 1214 "go.y"
+ case 192:
+
+/* Line 1806 of yacc.c */
+#line 1238 "go.y"
{
// array literal of nelem
(yyval.node) = nod(OTARRAY, nod(ODDD, N, N), (yyvsp[(4) - (4)].node));
}
break;
- case 191:
-#line 1219 "go.y"
+ case 193:
+
+/* Line 1806 of yacc.c */
+#line 1243 "go.y"
{
(yyval.node) = nod(OTCHAN, (yyvsp[(2) - (2)].node), N);
(yyval.node)->etype = Cboth;
}
break;
- case 192:
-#line 1224 "go.y"
+ case 194:
+
+/* Line 1806 of yacc.c */
+#line 1248 "go.y"
{
(yyval.node) = nod(OTCHAN, (yyvsp[(3) - (3)].node), N);
(yyval.node)->etype = Csend;
}
break;
- case 193:
-#line 1229 "go.y"
+ case 195:
+
+/* Line 1806 of yacc.c */
+#line 1253 "go.y"
{
(yyval.node) = nod(OTMAP, (yyvsp[(3) - (5)].node), (yyvsp[(5) - (5)].node));
}
break;
- case 196:
-#line 1237 "go.y"
+ case 198:
+
+/* Line 1806 of yacc.c */
+#line 1261 "go.y"
{
(yyval.node) = nod(OIND, (yyvsp[(2) - (2)].node), N);
}
break;
- case 197:
-#line 1243 "go.y"
+ case 199:
+
+/* Line 1806 of yacc.c */
+#line 1267 "go.y"
{
(yyval.node) = nod(OTCHAN, (yyvsp[(3) - (3)].node), N);
(yyval.node)->etype = Crecv;
}
break;
- case 198:
-#line 1250 "go.y"
+ case 200:
+
+/* Line 1806 of yacc.c */
+#line 1274 "go.y"
{
(yyval.node) = nod(OTSTRUCT, N, N);
(yyval.node)->list = (yyvsp[(3) - (5)].list);
@@ -3740,16 +4137,20 @@ yyreduce:
}
break;
- case 199:
-#line 1256 "go.y"
+ case 201:
+
+/* Line 1806 of yacc.c */
+#line 1280 "go.y"
{
(yyval.node) = nod(OTSTRUCT, N, N);
fixlbrace((yyvsp[(2) - (3)].i));
}
break;
- case 200:
-#line 1263 "go.y"
+ case 202:
+
+/* Line 1806 of yacc.c */
+#line 1287 "go.y"
{
(yyval.node) = nod(OTINTER, N, N);
(yyval.node)->list = (yyvsp[(3) - (5)].list);
@@ -3757,16 +4158,20 @@ yyreduce:
}
break;
- case 201:
-#line 1269 "go.y"
+ case 203:
+
+/* Line 1806 of yacc.c */
+#line 1293 "go.y"
{
(yyval.node) = nod(OTINTER, N, N);
fixlbrace((yyvsp[(2) - (3)].i));
}
break;
- case 202:
-#line 1280 "go.y"
+ case 204:
+
+/* Line 1806 of yacc.c */
+#line 1304 "go.y"
{
(yyval.node) = (yyvsp[(2) - (3)].node);
if((yyval.node) == N)
@@ -3780,8 +4185,10 @@ yyreduce:
}
break;
- case 203:
-#line 1294 "go.y"
+ case 205:
+
+/* Line 1806 of yacc.c */
+#line 1318 "go.y"
{
Node *t;
@@ -3812,8 +4219,10 @@ yyreduce:
}
break;
- case 204:
-#line 1323 "go.y"
+ case 206:
+
+/* Line 1806 of yacc.c */
+#line 1347 "go.y"
{
Node *rcvr, *t;
@@ -3853,8 +4262,10 @@ yyreduce:
}
break;
- case 205:
-#line 1363 "go.y"
+ case 207:
+
+/* Line 1806 of yacc.c */
+#line 1387 "go.y"
{
Sym *s;
Type *t;
@@ -3881,8 +4292,10 @@ yyreduce:
}
break;
- case 206:
-#line 1388 "go.y"
+ case 208:
+
+/* Line 1806 of yacc.c */
+#line 1412 "go.y"
{
(yyval.node) = methodname1(newname((yyvsp[(4) - (8)].sym)), (yyvsp[(2) - (8)].list)->n->right);
(yyval.node)->type = functype((yyvsp[(2) - (8)].list)->n, (yyvsp[(6) - (8)].list), (yyvsp[(8) - (8)].list));
@@ -3900,8 +4313,10 @@ yyreduce:
}
break;
- case 207:
-#line 1406 "go.y"
+ case 209:
+
+/* Line 1806 of yacc.c */
+#line 1430 "go.y"
{
(yyvsp[(3) - (5)].list) = checkarglist((yyvsp[(3) - (5)].list), 1);
(yyval.node) = nod(OTFUNC, N, N);
@@ -3910,15 +4325,19 @@ yyreduce:
}
break;
- case 208:
-#line 1414 "go.y"
+ case 210:
+
+/* Line 1806 of yacc.c */
+#line 1438 "go.y"
{
(yyval.list) = nil;
}
break;
- case 209:
-#line 1418 "go.y"
+ case 211:
+
+/* Line 1806 of yacc.c */
+#line 1442 "go.y"
{
(yyval.list) = (yyvsp[(2) - (3)].list);
if((yyval.list) == nil)
@@ -3926,59 +4345,75 @@ yyreduce:
}
break;
- case 210:
-#line 1426 "go.y"
+ case 212:
+
+/* Line 1806 of yacc.c */
+#line 1450 "go.y"
{
(yyval.list) = nil;
}
break;
- case 211:
-#line 1430 "go.y"
+ case 213:
+
+/* Line 1806 of yacc.c */
+#line 1454 "go.y"
{
(yyval.list) = list1(nod(ODCLFIELD, N, (yyvsp[(1) - (1)].node)));
}
break;
- case 212:
-#line 1434 "go.y"
+ case 214:
+
+/* Line 1806 of yacc.c */
+#line 1458 "go.y"
{
(yyvsp[(2) - (3)].list) = checkarglist((yyvsp[(2) - (3)].list), 0);
(yyval.list) = (yyvsp[(2) - (3)].list);
}
break;
- case 213:
-#line 1441 "go.y"
+ case 215:
+
+/* Line 1806 of yacc.c */
+#line 1465 "go.y"
{
closurehdr((yyvsp[(1) - (1)].node));
}
break;
- case 214:
-#line 1447 "go.y"
+ case 216:
+
+/* Line 1806 of yacc.c */
+#line 1471 "go.y"
{
(yyval.node) = closurebody((yyvsp[(3) - (4)].list));
fixlbrace((yyvsp[(2) - (4)].i));
}
break;
- case 215:
-#line 1452 "go.y"
+ case 217:
+
+/* Line 1806 of yacc.c */
+#line 1476 "go.y"
{
(yyval.node) = closurebody(nil);
}
break;
- case 216:
-#line 1463 "go.y"
+ case 218:
+
+/* Line 1806 of yacc.c */
+#line 1487 "go.y"
{
(yyval.list) = nil;
}
break;
- case 217:
-#line 1467 "go.y"
+ case 219:
+
+/* Line 1806 of yacc.c */
+#line 1491 "go.y"
{
(yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(2) - (3)].list));
if(nsyntaxerrors == 0)
@@ -3988,68 +4423,84 @@ yyreduce:
}
break;
- case 219:
-#line 1478 "go.y"
+ case 221:
+
+/* Line 1806 of yacc.c */
+#line 1502 "go.y"
{
(yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].list));
}
break;
- case 221:
-#line 1485 "go.y"
+ case 223:
+
+/* Line 1806 of yacc.c */
+#line 1509 "go.y"
{
(yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].list));
}
break;
- case 222:
-#line 1491 "go.y"
+ case 224:
+
+/* Line 1806 of yacc.c */
+#line 1515 "go.y"
{
(yyval.list) = list1((yyvsp[(1) - (1)].node));
}
break;
- case 223:
-#line 1495 "go.y"
+ case 225:
+
+/* Line 1806 of yacc.c */
+#line 1519 "go.y"
{
(yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
}
break;
- case 225:
-#line 1502 "go.y"
+ case 227:
+
+/* Line 1806 of yacc.c */
+#line 1526 "go.y"
{
(yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].list));
}
break;
- case 226:
-#line 1508 "go.y"
+ case 228:
+
+/* Line 1806 of yacc.c */
+#line 1532 "go.y"
{
(yyval.list) = list1((yyvsp[(1) - (1)].node));
}
break;
- case 227:
-#line 1512 "go.y"
+ case 229:
+
+/* Line 1806 of yacc.c */
+#line 1536 "go.y"
{
(yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
}
break;
- case 228:
-#line 1518 "go.y"
+ case 230:
+
+/* Line 1806 of yacc.c */
+#line 1542 "go.y"
{
NodeList *l;
Node *n;
l = (yyvsp[(1) - (3)].list);
- if(l != nil && l->next == nil && l->n == nil) {
- // ? symbol, during import
+ if(l == nil) {
+ // ? symbol, during import (list1(N) == nil)
n = (yyvsp[(2) - (3)].node);
if(n->op == OIND)
n = n->left;
- n = embedded(n->sym);
+ n = embedded(n->sym, importpkg);
n->right = (yyvsp[(2) - (3)].node);
n->val = (yyvsp[(3) - (3)].val);
(yyval.list) = list1(n);
@@ -4063,16 +4514,20 @@ yyreduce:
}
break;
- case 229:
-#line 1541 "go.y"
+ case 231:
+
+/* Line 1806 of yacc.c */
+#line 1565 "go.y"
{
(yyvsp[(1) - (2)].node)->val = (yyvsp[(2) - (2)].val);
(yyval.list) = list1((yyvsp[(1) - (2)].node));
}
break;
- case 230:
-#line 1546 "go.y"
+ case 232:
+
+/* Line 1806 of yacc.c */
+#line 1570 "go.y"
{
(yyvsp[(2) - (4)].node)->val = (yyvsp[(4) - (4)].val);
(yyval.list) = list1((yyvsp[(2) - (4)].node));
@@ -4080,8 +4535,10 @@ yyreduce:
}
break;
- case 231:
-#line 1552 "go.y"
+ case 233:
+
+/* Line 1806 of yacc.c */
+#line 1576 "go.y"
{
(yyvsp[(2) - (3)].node)->right = nod(OIND, (yyvsp[(2) - (3)].node)->right, N);
(yyvsp[(2) - (3)].node)->val = (yyvsp[(3) - (3)].val);
@@ -4089,8 +4546,10 @@ yyreduce:
}
break;
- case 232:
-#line 1558 "go.y"
+ case 234:
+
+/* Line 1806 of yacc.c */
+#line 1582 "go.y"
{
(yyvsp[(3) - (5)].node)->right = nod(OIND, (yyvsp[(3) - (5)].node)->right, N);
(yyvsp[(3) - (5)].node)->val = (yyvsp[(5) - (5)].val);
@@ -4099,8 +4558,10 @@ yyreduce:
}
break;
- case 233:
-#line 1565 "go.y"
+ case 235:
+
+/* Line 1806 of yacc.c */
+#line 1589 "go.y"
{
(yyvsp[(3) - (5)].node)->right = nod(OIND, (yyvsp[(3) - (5)].node)->right, N);
(yyvsp[(3) - (5)].node)->val = (yyvsp[(5) - (5)].val);
@@ -4109,8 +4570,10 @@ yyreduce:
}
break;
- case 234:
-#line 1574 "go.y"
+ case 236:
+
+/* Line 1806 of yacc.c */
+#line 1598 "go.y"
{
Node *n;
@@ -4121,8 +4584,10 @@ yyreduce:
}
break;
- case 235:
-#line 1583 "go.y"
+ case 237:
+
+/* Line 1806 of yacc.c */
+#line 1607 "go.y"
{
Pkg *pkg;
@@ -4137,38 +4602,48 @@ yyreduce:
}
break;
- case 236:
-#line 1598 "go.y"
+ case 238:
+
+/* Line 1806 of yacc.c */
+#line 1622 "go.y"
{
- (yyval.node) = embedded((yyvsp[(1) - (1)].sym));
+ (yyval.node) = embedded((yyvsp[(1) - (1)].sym), localpkg);
}
break;
- case 237:
-#line 1604 "go.y"
+ case 239:
+
+/* Line 1806 of yacc.c */
+#line 1628 "go.y"
{
(yyval.node) = nod(ODCLFIELD, (yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node));
ifacedcl((yyval.node));
}
break;
- case 238:
-#line 1609 "go.y"
+ case 240:
+
+/* Line 1806 of yacc.c */
+#line 1633 "go.y"
{
(yyval.node) = nod(ODCLFIELD, N, oldname((yyvsp[(1) - (1)].sym)));
}
break;
- case 239:
-#line 1613 "go.y"
+ case 241:
+
+/* Line 1806 of yacc.c */
+#line 1637 "go.y"
{
(yyval.node) = nod(ODCLFIELD, N, oldname((yyvsp[(2) - (3)].sym)));
yyerror("cannot parenthesize embedded type");
}
break;
- case 240:
-#line 1620 "go.y"
+ case 242:
+
+/* Line 1806 of yacc.c */
+#line 1644 "go.y"
{
// without func keyword
(yyvsp[(2) - (4)].list) = checkarglist((yyvsp[(2) - (4)].list), 1);
@@ -4178,8 +4653,10 @@ yyreduce:
}
break;
- case 242:
-#line 1634 "go.y"
+ case 244:
+
+/* Line 1806 of yacc.c */
+#line 1658 "go.y"
{
(yyval.node) = nod(ONONAME, N, N);
(yyval.node)->sym = (yyvsp[(1) - (2)].sym);
@@ -4187,8 +4664,10 @@ yyreduce:
}
break;
- case 243:
-#line 1640 "go.y"
+ case 245:
+
+/* Line 1806 of yacc.c */
+#line 1664 "go.y"
{
(yyval.node) = nod(ONONAME, N, N);
(yyval.node)->sym = (yyvsp[(1) - (2)].sym);
@@ -4196,65 +4675,83 @@ yyreduce:
}
break;
- case 245:
-#line 1649 "go.y"
+ case 247:
+
+/* Line 1806 of yacc.c */
+#line 1673 "go.y"
{
(yyval.list) = list1((yyvsp[(1) - (1)].node));
}
break;
- case 246:
-#line 1653 "go.y"
+ case 248:
+
+/* Line 1806 of yacc.c */
+#line 1677 "go.y"
{
(yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
}
break;
- case 247:
-#line 1658 "go.y"
+ case 249:
+
+/* Line 1806 of yacc.c */
+#line 1682 "go.y"
{
(yyval.list) = nil;
}
break;
- case 248:
-#line 1662 "go.y"
+ case 250:
+
+/* Line 1806 of yacc.c */
+#line 1686 "go.y"
{
(yyval.list) = (yyvsp[(1) - (2)].list);
}
break;
- case 249:
-#line 1670 "go.y"
+ case 251:
+
+/* Line 1806 of yacc.c */
+#line 1694 "go.y"
{
(yyval.node) = N;
}
break;
- case 251:
-#line 1675 "go.y"
+ case 253:
+
+/* Line 1806 of yacc.c */
+#line 1699 "go.y"
{
(yyval.node) = liststmt((yyvsp[(1) - (1)].list));
}
break;
- case 253:
-#line 1680 "go.y"
+ case 255:
+
+/* Line 1806 of yacc.c */
+#line 1704 "go.y"
{
(yyval.node) = N;
}
break;
- case 259:
-#line 1691 "go.y"
+ case 261:
+
+/* Line 1806 of yacc.c */
+#line 1715 "go.y"
{
(yyvsp[(1) - (2)].node) = nod(OLABEL, (yyvsp[(1) - (2)].node), N);
(yyvsp[(1) - (2)].node)->sym = dclstack; // context, for goto restrictions
}
break;
- case 260:
-#line 1696 "go.y"
+ case 262:
+
+/* Line 1806 of yacc.c */
+#line 1720 "go.y"
{
NodeList *l;
@@ -4266,52 +4763,66 @@ yyreduce:
}
break;
- case 261:
-#line 1706 "go.y"
+ case 263:
+
+/* Line 1806 of yacc.c */
+#line 1730 "go.y"
{
// will be converted to OFALL
(yyval.node) = nod(OXFALL, N, N);
}
break;
- case 262:
-#line 1711 "go.y"
+ case 264:
+
+/* Line 1806 of yacc.c */
+#line 1735 "go.y"
{
(yyval.node) = nod(OBREAK, (yyvsp[(2) - (2)].node), N);
}
break;
- case 263:
-#line 1715 "go.y"
+ case 265:
+
+/* Line 1806 of yacc.c */
+#line 1739 "go.y"
{
(yyval.node) = nod(OCONTINUE, (yyvsp[(2) - (2)].node), N);
}
break;
- case 264:
-#line 1719 "go.y"
+ case 266:
+
+/* Line 1806 of yacc.c */
+#line 1743 "go.y"
{
(yyval.node) = nod(OPROC, (yyvsp[(2) - (2)].node), N);
}
break;
- case 265:
-#line 1723 "go.y"
+ case 267:
+
+/* Line 1806 of yacc.c */
+#line 1747 "go.y"
{
(yyval.node) = nod(ODEFER, (yyvsp[(2) - (2)].node), N);
}
break;
- case 266:
-#line 1727 "go.y"
+ case 268:
+
+/* Line 1806 of yacc.c */
+#line 1751 "go.y"
{
(yyval.node) = nod(OGOTO, (yyvsp[(2) - (2)].node), N);
(yyval.node)->sym = dclstack; // context, for goto restrictions
}
break;
- case 267:
-#line 1732 "go.y"
+ case 269:
+
+/* Line 1806 of yacc.c */
+#line 1756 "go.y"
{
(yyval.node) = nod(ORETURN, N, N);
(yyval.node)->list = (yyvsp[(2) - (2)].list);
@@ -4330,8 +4841,10 @@ yyreduce:
}
break;
- case 268:
-#line 1751 "go.y"
+ case 270:
+
+/* Line 1806 of yacc.c */
+#line 1775 "go.y"
{
(yyval.list) = nil;
if((yyvsp[(1) - (1)].node) != N)
@@ -4339,8 +4852,10 @@ yyreduce:
}
break;
- case 269:
-#line 1757 "go.y"
+ case 271:
+
+/* Line 1806 of yacc.c */
+#line 1781 "go.y"
{
(yyval.list) = (yyvsp[(1) - (3)].list);
if((yyvsp[(3) - (3)].node) != N)
@@ -4348,190 +4863,244 @@ yyreduce:
}
break;
- case 270:
-#line 1765 "go.y"
+ case 272:
+
+/* Line 1806 of yacc.c */
+#line 1789 "go.y"
{
(yyval.list) = list1((yyvsp[(1) - (1)].node));
}
break;
- case 271:
-#line 1769 "go.y"
+ case 273:
+
+/* Line 1806 of yacc.c */
+#line 1793 "go.y"
{
(yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
}
break;
- case 272:
-#line 1775 "go.y"
+ case 274:
+
+/* Line 1806 of yacc.c */
+#line 1799 "go.y"
{
(yyval.list) = list1((yyvsp[(1) - (1)].node));
}
break;
- case 273:
-#line 1779 "go.y"
+ case 275:
+
+/* Line 1806 of yacc.c */
+#line 1803 "go.y"
{
(yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
}
break;
- case 274:
-#line 1785 "go.y"
+ case 276:
+
+/* Line 1806 of yacc.c */
+#line 1809 "go.y"
{
(yyval.list) = list1((yyvsp[(1) - (1)].node));
}
break;
- case 275:
-#line 1789 "go.y"
+ case 277:
+
+/* Line 1806 of yacc.c */
+#line 1813 "go.y"
{
(yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
}
break;
- case 276:
-#line 1795 "go.y"
+ case 278:
+
+/* Line 1806 of yacc.c */
+#line 1819 "go.y"
{
(yyval.list) = list1((yyvsp[(1) - (1)].node));
}
break;
- case 277:
-#line 1799 "go.y"
+ case 279:
+
+/* Line 1806 of yacc.c */
+#line 1823 "go.y"
{
(yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
}
break;
- case 278:
-#line 1808 "go.y"
+ case 280:
+
+/* Line 1806 of yacc.c */
+#line 1832 "go.y"
{
(yyval.list) = list1((yyvsp[(1) - (1)].node));
}
break;
- case 279:
-#line 1812 "go.y"
+ case 281:
+
+/* Line 1806 of yacc.c */
+#line 1836 "go.y"
{
(yyval.list) = list1((yyvsp[(1) - (1)].node));
}
break;
- case 280:
-#line 1816 "go.y"
+ case 282:
+
+/* Line 1806 of yacc.c */
+#line 1840 "go.y"
{
(yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
}
break;
- case 281:
-#line 1820 "go.y"
+ case 283:
+
+/* Line 1806 of yacc.c */
+#line 1844 "go.y"
{
(yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
}
break;
- case 282:
-#line 1825 "go.y"
+ case 284:
+
+/* Line 1806 of yacc.c */
+#line 1849 "go.y"
{
(yyval.list) = nil;
}
break;
- case 283:
-#line 1829 "go.y"
+ case 285:
+
+/* Line 1806 of yacc.c */
+#line 1853 "go.y"
{
(yyval.list) = (yyvsp[(1) - (2)].list);
}
break;
- case 288:
-#line 1843 "go.y"
+ case 290:
+
+/* Line 1806 of yacc.c */
+#line 1867 "go.y"
{
(yyval.node) = N;
}
break;
- case 290:
-#line 1849 "go.y"
+ case 292:
+
+/* Line 1806 of yacc.c */
+#line 1873 "go.y"
{
(yyval.list) = nil;
}
break;
- case 292:
-#line 1855 "go.y"
+ case 294:
+
+/* Line 1806 of yacc.c */
+#line 1879 "go.y"
{
(yyval.node) = N;
}
break;
- case 294:
-#line 1861 "go.y"
+ case 296:
+
+/* Line 1806 of yacc.c */
+#line 1885 "go.y"
{
(yyval.list) = nil;
}
break;
- case 296:
-#line 1867 "go.y"
+ case 298:
+
+/* Line 1806 of yacc.c */
+#line 1891 "go.y"
{
(yyval.list) = nil;
}
break;
- case 298:
-#line 1873 "go.y"
+ case 300:
+
+/* Line 1806 of yacc.c */
+#line 1897 "go.y"
{
(yyval.list) = nil;
}
break;
- case 300:
-#line 1879 "go.y"
+ case 302:
+
+/* Line 1806 of yacc.c */
+#line 1903 "go.y"
{
(yyval.val).ctype = CTxxx;
}
break;
- case 302:
-#line 1889 "go.y"
+ case 304:
+
+/* Line 1806 of yacc.c */
+#line 1913 "go.y"
{
importimport((yyvsp[(2) - (4)].sym), (yyvsp[(3) - (4)].val).u.sval);
}
break;
- case 303:
-#line 1893 "go.y"
+ case 305:
+
+/* Line 1806 of yacc.c */
+#line 1917 "go.y"
{
importvar((yyvsp[(2) - (4)].sym), (yyvsp[(3) - (4)].type));
}
break;
- case 304:
-#line 1897 "go.y"
+ case 306:
+
+/* Line 1806 of yacc.c */
+#line 1921 "go.y"
{
importconst((yyvsp[(2) - (5)].sym), types[TIDEAL], (yyvsp[(4) - (5)].node));
}
break;
- case 305:
-#line 1901 "go.y"
+ case 307:
+
+/* Line 1806 of yacc.c */
+#line 1925 "go.y"
{
importconst((yyvsp[(2) - (6)].sym), (yyvsp[(3) - (6)].type), (yyvsp[(5) - (6)].node));
}
break;
- case 306:
-#line 1905 "go.y"
+ case 308:
+
+/* Line 1806 of yacc.c */
+#line 1929 "go.y"
{
importtype((yyvsp[(2) - (4)].type), (yyvsp[(3) - (4)].type));
}
break;
- case 307:
-#line 1909 "go.y"
+ case 309:
+
+/* Line 1806 of yacc.c */
+#line 1933 "go.y"
{
if((yyvsp[(2) - (4)].node) == N) {
dclcontext = PEXTERN; // since we skip the funcbody below
@@ -4551,31 +5120,39 @@ yyreduce:
}
break;
- case 308:
-#line 1929 "go.y"
+ case 310:
+
+/* Line 1806 of yacc.c */
+#line 1953 "go.y"
{
(yyval.sym) = (yyvsp[(1) - (1)].sym);
structpkg = (yyval.sym)->pkg;
}
break;
- case 309:
-#line 1936 "go.y"
+ case 311:
+
+/* Line 1806 of yacc.c */
+#line 1960 "go.y"
{
(yyval.type) = pkgtype((yyvsp[(1) - (1)].sym));
importsym((yyvsp[(1) - (1)].sym), OTYPE);
}
break;
- case 315:
-#line 1956 "go.y"
+ case 317:
+
+/* Line 1806 of yacc.c */
+#line 1980 "go.y"
{
(yyval.type) = pkgtype((yyvsp[(1) - (1)].sym));
}
break;
- case 316:
-#line 1960 "go.y"
+ case 318:
+
+/* Line 1806 of yacc.c */
+#line 1984 "go.y"
{
// predefined name like uint8
(yyvsp[(1) - (1)].sym) = pkglookup((yyvsp[(1) - (1)].sym)->name, builtinpkg);
@@ -4587,50 +5164,64 @@ yyreduce:
}
break;
- case 317:
-#line 1970 "go.y"
+ case 319:
+
+/* Line 1806 of yacc.c */
+#line 1994 "go.y"
{
(yyval.type) = aindex(N, (yyvsp[(3) - (3)].type));
}
break;
- case 318:
-#line 1974 "go.y"
+ case 320:
+
+/* Line 1806 of yacc.c */
+#line 1998 "go.y"
{
(yyval.type) = aindex(nodlit((yyvsp[(2) - (4)].val)), (yyvsp[(4) - (4)].type));
}
break;
- case 319:
-#line 1978 "go.y"
+ case 321:
+
+/* Line 1806 of yacc.c */
+#line 2002 "go.y"
{
(yyval.type) = maptype((yyvsp[(3) - (5)].type), (yyvsp[(5) - (5)].type));
}
break;
- case 320:
-#line 1982 "go.y"
+ case 322:
+
+/* Line 1806 of yacc.c */
+#line 2006 "go.y"
{
(yyval.type) = tostruct((yyvsp[(3) - (4)].list));
}
break;
- case 321:
-#line 1986 "go.y"
+ case 323:
+
+/* Line 1806 of yacc.c */
+#line 2010 "go.y"
{
(yyval.type) = tointerface((yyvsp[(3) - (4)].list));
}
break;
- case 322:
-#line 1990 "go.y"
+ case 324:
+
+/* Line 1806 of yacc.c */
+#line 2014 "go.y"
{
(yyval.type) = ptrto((yyvsp[(2) - (2)].type));
}
break;
- case 323:
-#line 1994 "go.y"
+ case 325:
+
+/* Line 1806 of yacc.c */
+#line 2018 "go.y"
{
(yyval.type) = typ(TCHAN);
(yyval.type)->type = (yyvsp[(2) - (2)].type);
@@ -4638,8 +5229,10 @@ yyreduce:
}
break;
- case 324:
-#line 2000 "go.y"
+ case 326:
+
+/* Line 1806 of yacc.c */
+#line 2024 "go.y"
{
(yyval.type) = typ(TCHAN);
(yyval.type)->type = (yyvsp[(3) - (4)].type);
@@ -4647,8 +5240,10 @@ yyreduce:
}
break;
- case 325:
-#line 2006 "go.y"
+ case 327:
+
+/* Line 1806 of yacc.c */
+#line 2030 "go.y"
{
(yyval.type) = typ(TCHAN);
(yyval.type)->type = (yyvsp[(3) - (3)].type);
@@ -4656,8 +5251,10 @@ yyreduce:
}
break;
- case 326:
-#line 2014 "go.y"
+ case 328:
+
+/* Line 1806 of yacc.c */
+#line 2038 "go.y"
{
(yyval.type) = typ(TCHAN);
(yyval.type)->type = (yyvsp[(3) - (3)].type);
@@ -4665,15 +5262,19 @@ yyreduce:
}
break;
- case 327:
-#line 2022 "go.y"
+ case 329:
+
+/* Line 1806 of yacc.c */
+#line 2046 "go.y"
{
(yyval.type) = functype(nil, (yyvsp[(3) - (5)].list), (yyvsp[(5) - (5)].list));
}
break;
- case 328:
-#line 2028 "go.y"
+ case 330:
+
+/* Line 1806 of yacc.c */
+#line 2052 "go.y"
{
(yyval.node) = nod(ODCLFIELD, N, typenod((yyvsp[(2) - (3)].type)));
if((yyvsp[(1) - (3)].sym))
@@ -4682,8 +5283,10 @@ yyreduce:
}
break;
- case 329:
-#line 2035 "go.y"
+ case 331:
+
+/* Line 1806 of yacc.c */
+#line 2059 "go.y"
{
Type *t;
@@ -4699,69 +5302,89 @@ yyreduce:
}
break;
- case 330:
-#line 2051 "go.y"
+ case 332:
+
+/* Line 1806 of yacc.c */
+#line 2075 "go.y"
{
Sym *s;
+ Pkg *p;
- if((yyvsp[(1) - (3)].sym) != S) {
+ if((yyvsp[(1) - (3)].sym) != S && strcmp((yyvsp[(1) - (3)].sym)->name, "?") != 0) {
(yyval.node) = nod(ODCLFIELD, newname((yyvsp[(1) - (3)].sym)), typenod((yyvsp[(2) - (3)].type)));
(yyval.node)->val = (yyvsp[(3) - (3)].val);
} else {
s = (yyvsp[(2) - (3)].type)->sym;
if(s == S && isptr[(yyvsp[(2) - (3)].type)->etype])
s = (yyvsp[(2) - (3)].type)->type->sym;
- (yyval.node) = embedded(s);
+ p = importpkg;
+ if((yyvsp[(1) - (3)].sym) != S)
+ p = (yyvsp[(1) - (3)].sym)->pkg;
+ (yyval.node) = embedded(s, p);
(yyval.node)->right = typenod((yyvsp[(2) - (3)].type));
(yyval.node)->val = (yyvsp[(3) - (3)].val);
}
}
break;
- case 331:
-#line 2069 "go.y"
+ case 333:
+
+/* Line 1806 of yacc.c */
+#line 2097 "go.y"
{
(yyval.node) = nod(ODCLFIELD, newname((yyvsp[(1) - (5)].sym)), typenod(functype(fakethis(), (yyvsp[(3) - (5)].list), (yyvsp[(5) - (5)].list))));
}
break;
- case 332:
-#line 2073 "go.y"
+ case 334:
+
+/* Line 1806 of yacc.c */
+#line 2101 "go.y"
{
(yyval.node) = nod(ODCLFIELD, N, typenod((yyvsp[(1) - (1)].type)));
}
break;
- case 333:
-#line 2078 "go.y"
+ case 335:
+
+/* Line 1806 of yacc.c */
+#line 2106 "go.y"
{
(yyval.list) = nil;
}
break;
- case 335:
-#line 2085 "go.y"
+ case 337:
+
+/* Line 1806 of yacc.c */
+#line 2113 "go.y"
{
(yyval.list) = (yyvsp[(2) - (3)].list);
}
break;
- case 336:
-#line 2089 "go.y"
+ case 338:
+
+/* Line 1806 of yacc.c */
+#line 2117 "go.y"
{
(yyval.list) = list1(nod(ODCLFIELD, N, typenod((yyvsp[(1) - (1)].type))));
}
break;
- case 337:
-#line 2099 "go.y"
+ case 339:
+
+/* Line 1806 of yacc.c */
+#line 2127 "go.y"
{
(yyval.node) = nodlit((yyvsp[(1) - (1)].val));
}
break;
- case 338:
-#line 2103 "go.y"
+ case 340:
+
+/* Line 1806 of yacc.c */
+#line 2131 "go.y"
{
(yyval.node) = nodlit((yyvsp[(2) - (2)].val));
switch((yyval.node)->val.ctype){
@@ -4778,8 +5401,10 @@ yyreduce:
}
break;
- case 339:
-#line 2118 "go.y"
+ case 341:
+
+/* Line 1806 of yacc.c */
+#line 2146 "go.y"
{
(yyval.node) = oldname(pkglookup((yyvsp[(1) - (1)].sym)->name, builtinpkg));
if((yyval.node)->op != OLITERAL)
@@ -4787,8 +5412,10 @@ yyreduce:
}
break;
- case 341:
-#line 2127 "go.y"
+ case 343:
+
+/* Line 1806 of yacc.c */
+#line 2155 "go.y"
{
if((yyvsp[(2) - (5)].node)->val.ctype == CTRUNE && (yyvsp[(4) - (5)].node)->val.ctype == CTINT) {
(yyval.node) = (yyvsp[(2) - (5)].node);
@@ -4801,53 +5428,77 @@ yyreduce:
}
break;
- case 344:
-#line 2143 "go.y"
+ case 346:
+
+/* Line 1806 of yacc.c */
+#line 2171 "go.y"
{
(yyval.list) = list1((yyvsp[(1) - (1)].node));
}
break;
- case 345:
-#line 2147 "go.y"
+ case 347:
+
+/* Line 1806 of yacc.c */
+#line 2175 "go.y"
{
(yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
}
break;
- case 346:
-#line 2153 "go.y"
+ case 348:
+
+/* Line 1806 of yacc.c */
+#line 2181 "go.y"
{
(yyval.list) = list1((yyvsp[(1) - (1)].node));
}
break;
- case 347:
-#line 2157 "go.y"
+ case 349:
+
+/* Line 1806 of yacc.c */
+#line 2185 "go.y"
{
(yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
}
break;
- case 348:
-#line 2163 "go.y"
+ case 350:
+
+/* Line 1806 of yacc.c */
+#line 2191 "go.y"
{
(yyval.list) = list1((yyvsp[(1) - (1)].node));
}
break;
- case 349:
-#line 2167 "go.y"
+ case 351:
+
+/* Line 1806 of yacc.c */
+#line 2195 "go.y"
{
(yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
}
break;
-/* Line 1267 of yacc.c. */
-#line 4850 "y.tab.c"
+
+/* Line 1806 of yacc.c */
+#line 5490 "y.tab.c"
default: break;
}
+ /* User semantic actions sometimes alter yychar, and that requires
+ that yytoken be updated with the new translation. We take the
+ approach of translating immediately before every use of yytoken.
+ One alternative is translating here after every semantic action,
+ but that translation would be missed if the semantic action invokes
+ YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or
+ if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an
+ incorrect destructor might then be invoked immediately. In the
+ case of YYERROR or YYBACKUP, subsequent parser actions might lead
+ to an incorrect destructor call or verbose syntax error message
+ before the lookahead is translated. */
YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
YYPOPSTACK (yylen);
@@ -4856,7 +5507,6 @@ yyreduce:
*++yyvsp = yyval;
-
/* Now `shift' the result of the reduction. Determine what state
that goes to, based on the state we popped back to and the rule
number reduced by. */
@@ -4876,6 +5526,10 @@ yyreduce:
| yyerrlab -- here on detecting error |
`------------------------------------*/
yyerrlab:
+ /* Make sure we have latest lookahead translation. See comments at
+ user semantic actions for why this is necessary. */
+ yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar);
+
/* If not already recovering from an error, report this error. */
if (!yyerrstatus)
{
@@ -4883,37 +5537,36 @@ yyerrlab:
#if ! YYERROR_VERBOSE
yyerror (YY_("syntax error"));
#else
+# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \
+ yyssp, yytoken)
{
- YYSIZE_T yysize = yysyntax_error (0, yystate, yychar);
- if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM)
- {
- YYSIZE_T yyalloc = 2 * yysize;
- if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM))
- yyalloc = YYSTACK_ALLOC_MAXIMUM;
- if (yymsg != yymsgbuf)
- YYSTACK_FREE (yymsg);
- yymsg = (char *) YYSTACK_ALLOC (yyalloc);
- if (yymsg)
- yymsg_alloc = yyalloc;
- else
- {
- yymsg = yymsgbuf;
- yymsg_alloc = sizeof yymsgbuf;
- }
- }
-
- if (0 < yysize && yysize <= yymsg_alloc)
- {
- (void) yysyntax_error (yymsg, yystate, yychar);
- yyerror (yymsg);
- }
- else
- {
- yyerror (YY_("syntax error"));
- if (yysize != 0)
- goto yyexhaustedlab;
- }
+ char *yymsgp = YY_("syntax error");
+ int yysyntax_error_status;
+ yysyntax_error_status = YYSYNTAX_ERROR;
+ if (yysyntax_error_status == 0)
+ yymsgp = yymsg;
+ else if (yysyntax_error_status == 1)
+ {
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+ yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc);
+ if (!yymsg)
+ {
+ yymsg = yymsgbuf;
+ yymsg_alloc = sizeof yymsgbuf;
+ yysyntax_error_status = 2;
+ }
+ else
+ {
+ yysyntax_error_status = YYSYNTAX_ERROR;
+ yymsgp = yymsg;
+ }
+ }
+ yyerror (yymsgp);
+ if (yysyntax_error_status == 2)
+ goto yyexhaustedlab;
}
+# undef YYSYNTAX_ERROR
#endif
}
@@ -4921,7 +5574,7 @@ yyerrlab:
if (yyerrstatus == 3)
{
- /* If just tried and failed to reuse look-ahead token after an
+ /* If just tried and failed to reuse lookahead token after an
error, discard it. */
if (yychar <= YYEOF)
@@ -4938,7 +5591,7 @@ yyerrlab:
}
}
- /* Else will try to reuse look-ahead token after shifting the error
+ /* Else will try to reuse lookahead token after shifting the error
token. */
goto yyerrlab1;
@@ -4972,7 +5625,7 @@ yyerrlab1:
for (;;)
{
yyn = yypact[yystate];
- if (yyn != YYPACT_NINF)
+ if (!yypact_value_is_default (yyn))
{
yyn += YYTERROR;
if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
@@ -4995,9 +5648,6 @@ yyerrlab1:
YY_STACK_PRINT (yyss, yyssp);
}
- if (yyn == YYFINAL)
- YYACCEPT;
-
*++yyvsp = yylval;
@@ -5022,7 +5672,7 @@ yyabortlab:
yyresult = 1;
goto yyreturn;
-#ifndef yyoverflow
+#if !defined(yyoverflow) || YYERROR_VERBOSE
/*-------------------------------------------------.
| yyexhaustedlab -- memory exhaustion comes here. |
`-------------------------------------------------*/
@@ -5033,9 +5683,14 @@ yyexhaustedlab:
#endif
yyreturn:
- if (yychar != YYEOF && yychar != YYEMPTY)
- yydestruct ("Cleanup: discarding lookahead",
- yytoken, &yylval);
+ if (yychar != YYEMPTY)
+ {
+ /* Make sure we have latest lookahead translation. See comments at
+ user semantic actions for why this is necessary. */
+ yytoken = YYTRANSLATE (yychar);
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval);
+ }
/* Do not reclaim the symbols of the rule which action triggered
this YYABORT or YYACCEPT. */
YYPOPSTACK (yylen);
@@ -5059,7 +5714,9 @@ yyreturn:
}
-#line 2171 "go.y"
+
+/* Line 2067 of yacc.c */
+#line 2199 "go.y"
static void
diff --git a/src/cmd/gc/y.tab.h b/src/cmd/gc/y.tab.h
index d01fbe198..6eeb831b2 100644
--- a/src/cmd/gc/y.tab.h
+++ b/src/cmd/gc/y.tab.h
@@ -1,24 +1,21 @@
-/* A Bison parser, made by GNU Bison 2.3. */
+/* A Bison parser, made by GNU Bison 2.5. */
-/* Skeleton interface for Bison's Yacc-like parsers in C
-
- Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
- Free Software Foundation, Inc.
-
- This program is free software; you can redistribute it and/or modify
+/* Bison interface for Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
+
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* As a special exception, you may create a larger work that contains
part or all of the Bison parser skeleton and distribute that work
@@ -29,10 +26,11 @@
special exception, which will cause the skeleton and the resulting
Bison output files to be licensed under the GNU General Public
License without this special exception.
-
+
This special exception was added by the Free Software Foundation in
version 2.2 of Bison. */
+
/* Tokens. */
#ifndef YYTOKENTYPE
# define YYTOKENTYPE
@@ -146,22 +144,28 @@
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
typedef union YYSTYPE
-#line 28 "go.y"
{
+
+/* Line 2068 of yacc.c */
+#line 28 "go.y"
+
Node* node;
NodeList* list;
Type* type;
Sym* sym;
struct Val val;
int i;
-}
-/* Line 1529 of yacc.c. */
-#line 160 "y.tab.h"
- YYSTYPE;
+
+
+
+/* Line 2068 of yacc.c */
+#line 163 "y.tab.h"
+} YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
# define YYSTYPE_IS_DECLARED 1
-# define YYSTYPE_IS_TRIVIAL 1
#endif
extern YYSTYPE yylval;
+
diff --git a/src/cmd/gc/yerr.h b/src/cmd/gc/yerr.h
index e7eb6516c..1526d8231 100644
--- a/src/cmd/gc/yerr.h
+++ b/src/cmd/gc/yerr.h
@@ -14,25 +14,28 @@ static struct {
// is converted by bisonerrors into the yystate and yychar caused
// by that token list.
- 221, ',',
+ 222, ',',
"unexpected comma during import block",
- 377, ';',
- "unexpected semicolon or newline before {",
+ 32, ';',
+ "missing import path; require quoted string",
- 398, ';',
- "unexpected semicolon or newline before {",
+ 378, ';',
+ "missing { after if clause",
- 237, ';',
- "unexpected semicolon or newline before {",
+ 399, ';',
+ "missing { after switch clause",
- 475, LBODY,
- "unexpected semicolon or newline before {",
+ 238, ';',
+ "missing { after for clause",
+
+ 476, LBODY,
+ "missing { after for clause",
22, '{',
"unexpected semicolon or newline before {",
- 144, ';',
+ 145, ';',
"unexpected semicolon or newline in type declaration",
37, '}',
@@ -44,33 +47,33 @@ static struct {
37, ',',
"unexpected comma in channel type",
- 438, LELSE,
+ 439, LELSE,
"unexpected semicolon or newline before else",
- 257, ',',
+ 258, ',',
"name list not allowed in interface type",
- 237, LVAR,
+ 238, LVAR,
"var declaration not allowed in for initializer",
65, '{',
"unexpected { at end of statement",
- 376, '{',
+ 377, '{',
"unexpected { at end of statement",
- 125, ';',
+ 126, ';',
"argument to go/defer must be function call",
- 425, ';',
+ 426, ';',
"need trailing comma before newline in composite literal",
- 436, ';',
+ 437, ';',
"need trailing comma before newline in composite literal",
- 112, LNAME,
+ 113, LNAME,
"nested func not allowed",
- 642, ';',
+ 645, ';',
"else must be followed by if or statement block"
};
diff --git a/src/cmd/go/bootstrap.go b/src/cmd/go/bootstrap.go
index 32941404c..dc7ed5f4c 100644
--- a/src/cmd/go/bootstrap.go
+++ b/src/cmd/go/bootstrap.go
@@ -25,6 +25,6 @@ func httpsOrHTTP(importPath string) (string, io.ReadCloser, error) {
return "", nil, errHTTP
}
-func parseMetaGoImports(r io.Reader) (imports []metaImport) {
+func parseMetaGoImports(r io.Reader) ([]metaImport, error) {
panic("unreachable")
}
diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go
index 025b258bf..f70f778d9 100644
--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -94,7 +94,8 @@ in an element in the list, surround it with either single or double quotes.
For more about specifying packages, see 'go help packages'.
For more about where packages and binaries are installed,
-see 'go help gopath'.
+run 'go help gopath'. For more about calling between Go and C/C++,
+run 'go help c'.
See also: go install, go get, go clean.
`,
@@ -185,6 +186,18 @@ func isSpaceByte(c byte) bool {
return c == ' ' || c == '\t' || c == '\n' || c == '\r'
}
+// fileExtSplit expects a filename and returns the name
+// and ext (without the dot). If the file has no
+// extension, ext will be empty.
+func fileExtSplit(file string) (name, ext string) {
+ dotExt := filepath.Ext(file)
+ name = file[:len(file)-len(dotExt)]
+ if dotExt != "" {
+ ext = dotExt[1:]
+ }
+ return
+}
+
type stringsFlag []string
func (v *stringsFlag) Set(s string) error {
@@ -299,7 +312,13 @@ func runInstall(cmd *Command, args []string) {
for _, p := range pkgs {
if p.Target == "" && (!p.Standard || p.ImportPath != "unsafe") {
- errorf("go install: no install location for directory %s outside GOPATH", p.Dir)
+ if p.cmdline {
+ errorf("go install: no install location for .go files listed on command line (GOBIN not set)")
+ } else if p.ConflictDir != "" {
+ errorf("go install: no install location for %s: hidden by %s", p.Dir, p.ConflictDir)
+ } else {
+ errorf("go install: no install location for directory %s outside GOPATH", p.Dir)
+ }
}
}
exitIfErrors()
@@ -416,7 +435,7 @@ func (b *builder) init() {
fatalf("%s", err)
}
if buildX || buildWork {
- fmt.Printf("WORK=%s\n", b.work)
+ fmt.Fprintf(os.Stderr, "WORK=%s\n", b.work)
}
if !buildWork {
atexit(func() { os.RemoveAll(b.work) })
@@ -470,6 +489,7 @@ func goFilesPackage(gofiles []string) *Package {
bp, err := ctxt.ImportDir(dir, 0)
pkg := new(Package)
pkg.local = true
+ pkg.cmdline = true
pkg.load(&stk, bp, err)
pkg.localPrefix = dirToImportPath(dir)
pkg.ImportPath = "command-line-arguments"
@@ -558,8 +578,12 @@ func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action
// Imported via local path. No permanent target.
mode = modeBuild
}
- a.objdir = filepath.Join(b.work, a.p.ImportPath, "_obj") + string(filepath.Separator)
- a.objpkg = buildToolchain.pkgpath(b.work, a.p)
+ work := p.pkgdir
+ if work == "" {
+ work = b.work
+ }
+ a.objdir = filepath.Join(work, a.p.ImportPath, "_obj") + string(filepath.Separator)
+ a.objpkg = buildToolchain.pkgpath(work, a.p)
a.link = p.Name == "main"
switch mode {
@@ -727,6 +751,15 @@ func hasString(strings []string, s string) bool {
// build is the action for building a single package or command.
func (b *builder) build(a *action) (err error) {
+ // Return an error if the package has CXX files but it's not using
+ // cgo nor SWIG, since the CXX files can only be processed by cgo
+ // and SWIG (it's possible to have packages with C files without
+ // using cgo, they will get compiled with the plan9 C compiler and
+ // linked with the rest of the package).
+ if len(a.p.CXXFiles) > 0 && !a.p.usesCgo() && !a.p.usesSwig() {
+ return fmt.Errorf("can't build package %s because it contains C++ files (%s) but it's not using cgo nor SWIG",
+ a.p.ImportPath, strings.Join(a.p.CXXFiles, ","))
+ }
defer func() {
if err != nil && err != errPrintedOutput {
err = fmt.Errorf("go build %s: %v", a.p.ImportPath, err)
@@ -747,7 +780,7 @@ func (b *builder) build(a *action) (err error) {
if a.p.Standard && a.p.ImportPath == "runtime" && buildContext.Compiler == "gc" &&
!hasString(a.p.HFiles, "zasm_"+buildContext.GOOS+"_"+buildContext.GOARCH+".h") {
- return fmt.Errorf("%s/%s must be bootstrapped using make.bash", buildContext.GOOS, buildContext.GOARCH)
+ return fmt.Errorf("%s/%s must be bootstrapped using make%v", buildContext.GOOS, buildContext.GOARCH, defaultSuffix())
}
// Make build directory.
@@ -765,13 +798,32 @@ func (b *builder) build(a *action) (err error) {
}
var gofiles, cfiles, sfiles, objects, cgoObjects []string
- gofiles = append(gofiles, a.p.GoFiles...)
+
+ // If we're doing coverage, preprocess the .go files and put them in the work directory
+ if a.p.coverMode != "" {
+ for _, file := range a.p.GoFiles {
+ sourceFile := filepath.Join(a.p.Dir, file)
+ cover := a.p.coverVars[file]
+ if cover == nil || isTestFile(file) {
+ // Not covering this file.
+ gofiles = append(gofiles, file)
+ continue
+ }
+ coverFile := filepath.Join(obj, file)
+ if err := b.cover(a, coverFile, sourceFile, 0666, cover.Var); err != nil {
+ return err
+ }
+ gofiles = append(gofiles, coverFile)
+ }
+ } else {
+ gofiles = append(gofiles, a.p.GoFiles...)
+ }
cfiles = append(cfiles, a.p.CFiles...)
sfiles = append(sfiles, a.p.SFiles...)
// Run cgo.
- if len(a.p.CgoFiles) > 0 {
- // In a package using cgo, cgo compiles the C and assembly files with gcc.
+ if a.p.usesCgo() {
+ // In a package using cgo, cgo compiles the C, C++ and assembly files with gcc.
// There is one exception: runtime/cgo's job is to bridge the
// cgo and non-cgo worlds, so it necessarily has files in both.
// In that case gcc only gets the gcc_* files.
@@ -799,7 +851,7 @@ func (b *builder) build(a *action) (err error) {
if a.cgo != nil && a.cgo.target != "" {
cgoExe = a.cgo.target
}
- outGo, outObj, err := b.cgo(a.p, cgoExe, obj, gccfiles)
+ outGo, outObj, err := b.cgo(a.p, cgoExe, obj, gccfiles, a.p.CXXFiles)
if err != nil {
return err
}
@@ -814,7 +866,7 @@ func (b *builder) build(a *action) (err error) {
gccfiles := append(cfiles, sfiles...)
cfiles = nil
sfiles = nil
- outGo, outObj, err := b.swig(a.p, obj, gccfiles)
+ outGo, outObj, err := b.swig(a.p, obj, gccfiles, a.p.CXXFiles)
if err != nil {
return err
}
@@ -843,23 +895,24 @@ func (b *builder) build(a *action) (err error) {
// Copy .h files named for goos or goarch or goos_goarch
// to names using GOOS and GOARCH.
// For example, defs_linux_amd64.h becomes defs_GOOS_GOARCH.h.
- _goos_goarch := "_" + goos + "_" + goarch + ".h"
- _goos := "_" + goos + ".h"
- _goarch := "_" + goarch + ".h"
+ _goos_goarch := "_" + goos + "_" + goarch
+ _goos := "_" + goos
+ _goarch := "_" + goarch
for _, file := range a.p.HFiles {
+ name, ext := fileExtSplit(file)
switch {
- case strings.HasSuffix(file, _goos_goarch):
- targ := file[:len(file)-len(_goos_goarch)] + "_GOOS_GOARCH.h"
+ case strings.HasSuffix(name, _goos_goarch):
+ targ := file[:len(name)-len(_goos_goarch)] + "_GOOS_GOARCH." + ext
if err := b.copyFile(a, obj+targ, filepath.Join(a.p.Dir, file), 0666); err != nil {
return err
}
- case strings.HasSuffix(file, _goarch):
- targ := file[:len(file)-len(_goarch)] + "_GOARCH.h"
+ case strings.HasSuffix(name, _goarch):
+ targ := file[:len(name)-len(_goarch)] + "_GOARCH." + ext
if err := b.copyFile(a, obj+targ, filepath.Join(a.p.Dir, file), 0666); err != nil {
return err
}
- case strings.HasSuffix(file, _goos):
- targ := file[:len(file)-len(_goos)] + "_GOOS.h"
+ case strings.HasSuffix(name, _goos):
+ targ := file[:len(name)-len(_goos)] + "_GOOS." + ext
if err := b.copyFile(a, obj+targ, filepath.Join(a.p.Dir, file), 0666); err != nil {
return err
}
@@ -955,8 +1008,9 @@ func (b *builder) install(a *action) (err error) {
return err
}
soname := a.p.swigSoname(f)
+ source := filepath.Join(a.objdir, soname)
target := filepath.Join(dir, soname)
- if err = b.copyFile(a, target, soname, perm); err != nil {
+ if err = b.copyFile(a, target, source, perm); err != nil {
return err
}
}
@@ -997,8 +1051,8 @@ func (b *builder) includeArgs(flag string, all []*action) []string {
dir = filepath.Join(dir, "gccgo_"+goos+"_"+goarch)
} else {
dir = filepath.Join(dir, goos+"_"+goarch)
- if buildRace {
- dir += "_race"
+ if buildContext.InstallSuffix != "" {
+ dir += "_" + buildContext.InstallSuffix
}
}
inc = append(inc, flag, dir)
@@ -1047,7 +1101,7 @@ func (b *builder) copyFile(a *action, dst, src string, perm os.FileMode) error {
if err != nil && toolIsWindows {
// Windows does not allow deletion of a binary file
// while it is executing. Try to move it out of the way.
- // If the remove fails, which is likely, we'll try again the
+ // If the move fails, which is likely, we'll try again the
// next time we do an install of this binary.
if err := os.Rename(dst, dst+"~"); err == nil {
os.Remove(dst + "~")
@@ -1067,6 +1121,17 @@ func (b *builder) copyFile(a *action, dst, src string, perm os.FileMode) error {
return nil
}
+// cover runs, in effect,
+// go tool cover -mode=b.coverMode -var="varName" -o dst.go src.go
+func (b *builder) cover(a *action, dst, src string, perm os.FileMode, varName string) error {
+ return b.run(a.objdir, "cover "+a.p.ImportPath, nil,
+ tool("cover"),
+ "-mode", a.p.coverMode,
+ "-var", varName,
+ "-o", dst,
+ src)
+}
+
var objectMagic = [][]byte{
{'!', '<', 'a', 'r', 'c', 'h', '>', '\n'}, // Package archive
{'\x7F', 'E', 'L', 'F'}, // ELF
@@ -1454,7 +1519,7 @@ func (gcToolchain) gc(b *builder, p *Package, obj string, importArgs []string, g
// so that it can give good error messages about forward declarations.
// Exceptions: a few standard packages have forward declarations for
// pieces supplied behind-the-scenes by package runtime.
- extFiles := len(p.CgoFiles) + len(p.CFiles) + len(p.SFiles) + len(p.SysoFiles) + len(p.SwigFiles) + len(p.SwigCXXFiles)
+ extFiles := len(p.CgoFiles) + len(p.CFiles) + len(p.CXXFiles) + len(p.SFiles) + len(p.SysoFiles) + len(p.SwigFiles) + len(p.SwigCXXFiles)
if p.Standard {
switch p.ImportPath {
case "os", "runtime/pprof", "sync", "time":
@@ -1464,6 +1529,9 @@ func (gcToolchain) gc(b *builder, p *Package, obj string, importArgs []string, g
if extFiles == 0 {
gcargs = append(gcargs, "-complete")
}
+ if buildContext.InstallSuffix != "" {
+ gcargs = append(gcargs, "-installsuffix", buildContext.InstallSuffix)
+ }
args := stringList(tool(archChar+"g"), "-o", ofile, buildGcflags, gcargs, "-D", p.localPrefix, importArgs)
for _, f := range gofiles {
@@ -1496,6 +1564,7 @@ func (gcToolchain) ld(b *builder, p *Package, out string, allactions []*action,
importArgs := b.includeArgs("-L", allactions)
swigDirs := make(map[string]bool)
swigArg := []string{}
+ cxx := false
for _, a := range allactions {
if a.p != nil && a.p.usesSwig() {
sd := a.p.swigDir(&buildContext)
@@ -1506,9 +1575,59 @@ func (gcToolchain) ld(b *builder, p *Package, out string, allactions []*action,
swigArg[1] += sd
}
swigDirs[sd] = true
+ if a.objdir != "" && !swigDirs[a.objdir] {
+ swigArg[1] += ":"
+ swigArg[1] += a.objdir
+ swigDirs[a.objdir] = true
+ }
+ }
+ if a.p != nil && len(a.p.CXXFiles) > 0 {
+ cxx = true
+ }
+ }
+ ldflags := buildLdflags
+ if buildContext.InstallSuffix != "" {
+ ldflags = append(ldflags, "-installsuffix", buildContext.InstallSuffix)
+ }
+ if cxx {
+ // The program includes C++ code. If the user has not
+ // specified the -extld option, then default to
+ // linking with the compiler named by the CXX
+ // environment variable, or g++ if CXX is not set.
+ extld := false
+ for _, f := range ldflags {
+ if f == "-extld" || strings.HasPrefix(f, "-extld=") {
+ extld = true
+ break
+ }
+ }
+ if !extld {
+ compiler := strings.Fields(os.Getenv("CXX"))
+ if len(compiler) == 0 {
+ compiler = []string{"g++"}
+ }
+ ldflags = append(ldflags, "-extld="+compiler[0])
+ if len(compiler) > 1 {
+ extldflags := false
+ add := strings.Join(compiler[1:], " ")
+ for i, f := range ldflags {
+ if f == "-extldflags" && i+1 < len(ldflags) {
+ ldflags[i+1] = add + " " + ldflags[i+1]
+ extldflags = true
+ break
+ } else if strings.HasPrefix(f, "-extldflags=") {
+ ldflags[i] = "-extldflags=" + add + " " + ldflags[i][len("-extldflags="):]
+ extldflags = true
+ break
+ }
+ }
+ if !extldflags {
+ ldflags = append(ldflags, "-extldflags="+add)
+ }
+ }
}
}
- return b.run(".", p.ImportPath, nil, tool(archChar+"l"), "-o", out, importArgs, swigArg, buildLdflags, mainpkg)
+ return b.run(".", p.ImportPath, nil, tool(archChar+"l"), "-o", out, importArgs, swigArg, ldflags, mainpkg)
}
func (gcToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error {
@@ -1584,6 +1703,7 @@ func (tools gccgoToolchain) ld(b *builder, p *Package, out string, allactions []
ldflags := b.gccArchArgs()
cgoldflags := []string{}
usesCgo := false
+ cxx := false
for _, a := range allactions {
if a.p != nil {
if !a.p.Standard {
@@ -1597,12 +1717,18 @@ func (tools gccgoToolchain) ld(b *builder, p *Package, out string, allactions []
}
if a.p.usesSwig() {
sd := a.p.swigDir(&buildContext)
+ if a.objdir != "" {
+ sd = a.objdir
+ }
for _, f := range stringList(a.p.SwigFiles, a.p.SwigCXXFiles) {
soname := a.p.swigSoname(f)
sfiles[a.p] = append(sfiles[a.p], filepath.Join(sd, soname))
}
usesCgo = true
}
+ if len(a.p.CXXFiles) > 0 {
+ cxx = true
+ }
}
}
for _, afile := range afiles {
@@ -1615,6 +1741,9 @@ func (tools gccgoToolchain) ld(b *builder, p *Package, out string, allactions []
if usesCgo && goos == "linux" {
ldflags = append(ldflags, "-Wl,-E")
}
+ if cxx {
+ ldflags = append(ldflags, "-lstdc++")
+ }
return b.run(".", p.ImportPath, nil, "gccgo", "-o", out, ofiles, "-Wl,-(", ldflags, "-Wl,-)", buildGccgoflags)
}
@@ -1689,26 +1818,55 @@ func (b *builder) libgcc(p *Package) (string, error) {
// gcc runs the gcc C compiler to create an object from a single C file.
func (b *builder) gcc(p *Package, out string, flags []string, cfile string) error {
- cfile = mkAbs(p.Dir, cfile)
- return b.run(p.Dir, p.ImportPath, nil, b.gccCmd(p.Dir), flags, "-o", out, "-c", cfile)
+ return b.ccompile(p, out, flags, cfile, b.gccCmd(p.Dir))
}
-// gccld runs the gcc linker to create an executable from a set of object files
+// gxx runs the g++ C++ compiler to create an object from a single C++ file.
+func (b *builder) gxx(p *Package, out string, flags []string, cxxfile string) error {
+ return b.ccompile(p, out, flags, cxxfile, b.gxxCmd(p.Dir))
+}
+
+// ccompile runs the given C or C++ compiler and creates an object from a single source file.
+func (b *builder) ccompile(p *Package, out string, flags []string, file string, compiler []string) error {
+ file = mkAbs(p.Dir, file)
+ return b.run(p.Dir, p.ImportPath, nil, compiler, flags, "-o", out, "-c", file)
+}
+
+// gccld runs the gcc linker to create an executable from a set of object files.
func (b *builder) gccld(p *Package, out string, flags []string, obj []string) error {
- return b.run(p.Dir, p.ImportPath, nil, b.gccCmd(p.Dir), "-o", out, obj, flags)
+ var cmd []string
+ if len(p.CXXFiles) > 0 {
+ cmd = b.gxxCmd(p.Dir)
+ } else {
+ cmd = b.gccCmd(p.Dir)
+ }
+ return b.run(p.Dir, p.ImportPath, nil, cmd, "-o", out, obj, flags)
}
// gccCmd returns a gcc command line prefix
+// defaultCC is defined in zdefaultcc.go, written by cmd/dist.
func (b *builder) gccCmd(objdir string) []string {
+ return b.ccompilerCmd("CC", defaultCC, objdir)
+}
+
+// gxxCmd returns a g++ command line prefix
+// defaultCXX is defined in zdefaultcc.go, written by cmd/dist.
+func (b *builder) gxxCmd(objdir string) []string {
+ return b.ccompilerCmd("CXX", defaultCXX, objdir)
+}
+
+// ccompilerCmd returns a command line prefix for the given environment
+// variable and using the default command when the variable is empty
+func (b *builder) ccompilerCmd(envvar, defcmd, objdir string) []string {
// NOTE: env.go's mkEnv knows that the first three
// strings returned are "gcc", "-I", objdir (and cuts them off).
- gcc := strings.Fields(os.Getenv("CC"))
- if len(gcc) == 0 {
- gcc = append(gcc, "gcc")
+ compiler := strings.Fields(os.Getenv(envvar))
+ if len(compiler) == 0 {
+ compiler = strings.Fields(defcmd)
}
- a := []string{gcc[0], "-I", objdir, "-g", "-O2"}
- a = append(a, gcc[1:]...)
+ a := []string{compiler[0], "-I", objdir, "-g", "-O2"}
+ a = append(a, compiler[1:]...)
// Definitely want -fPIC but on Windows gcc complains
// "-fPIC ignored for target (all code is position independent)"
@@ -1727,8 +1885,10 @@ func (b *builder) gccCmd(objdir string) []string {
}
}
- // clang is too smart about command-line arguments
if strings.Contains(a[0], "clang") {
+ // disable ASCII art in clang errors, if possible
+ a = append(a, "-fno-caret-diagnostics")
+ // clang is too smart about command-line arguments
a = append(a, "-Qunused-arguments")
}
@@ -1767,12 +1927,14 @@ var (
cgoLibGccFileOnce sync.Once
)
-func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, outObj []string, err error) {
+func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string, gxxfiles []string) (outGo, outObj []string, err error) {
if goos != toolGOOS {
return nil, nil, errors.New("cannot use cgo when compiling for a different operating system")
}
+ cgoCPPFLAGS := stringList(envList("CGO_CPPFLAGS"), p.CgoCPPFLAGS)
cgoCFLAGS := stringList(envList("CGO_CFLAGS"), p.CgoCFLAGS)
+ cgoCXXFLAGS := stringList(envList("CGO_CXXFLAGS"), p.CgoCXXFLAGS)
cgoLDFLAGS := stringList(envList("CGO_LDFLAGS"), p.CgoLDFLAGS)
if pkgs := p.CgoPkgConfig; len(pkgs) > 0 {
@@ -1783,7 +1945,7 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo,
return nil, nil, errPrintedOutput
}
if len(out) > 0 {
- cgoCFLAGS = append(cgoCFLAGS, strings.Fields(string(out))...)
+ cgoCPPFLAGS = append(cgoCPPFLAGS, strings.Fields(string(out))...)
}
out, err = b.runOut(p.Dir, p.ImportPath, nil, "pkg-config", "--libs", pkgs)
if err != nil {
@@ -1797,7 +1959,7 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo,
}
// Allows including _cgo_export.h from .[ch] files in the package.
- cgoCFLAGS = append(cgoCFLAGS, "-I", obj)
+ cgoCPPFLAGS = append(cgoCPPFLAGS, "-I", obj)
// cgo
// TODO: CGOPKGPATH, CGO_FLAGS?
@@ -1839,7 +2001,7 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo,
}
objExt = "o"
}
- if err := b.run(p.Dir, p.ImportPath, cgoenv, cgoExe, "-objdir", obj, cgoflags, "--", cgoCFLAGS, p.CgoFiles); err != nil {
+ if err := b.run(p.Dir, p.ImportPath, cgoenv, cgoExe, "-objdir", obj, cgoflags, "--", cgoCPPFLAGS, cgoCFLAGS, p.CgoFiles); err != nil {
return nil, nil, err
}
outGo = append(outGo, gofiles...)
@@ -1855,14 +2017,24 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo,
var linkobj []string
var bareLDFLAGS []string
- // filter out -lsomelib, and -framework X if on Darwin
+ // filter out -lsomelib, -l somelib, *.{so,dll,dylib}, and (on Darwin) -framework X
for i := 0; i < len(cgoLDFLAGS); i++ {
f := cgoLDFLAGS[i]
- if !strings.HasPrefix(f, "-l") {
- if goos == "darwin" && f == "-framework" { // skip the -framework X
- i += 1
- continue
+ switch {
+ // skip "-lc" or "-l somelib"
+ case strings.HasPrefix(f, "-l"):
+ if f == "-l" {
+ i++
}
+ // skip "-framework X" on Darwin
+ case goos == "darwin" && f == "-framework":
+ i++
+ // skip "*.{dylib,so,dll}"
+ case strings.HasSuffix(f, ".dylib"),
+ strings.HasSuffix(f, ".so"),
+ strings.HasSuffix(f, ".dll"):
+ continue
+ default:
bareLDFLAGS = append(bareLDFLAGS, f)
}
}
@@ -1876,16 +2048,18 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo,
var staticLibs []string
if goos == "windows" {
- // libmingw32 and libmingwex might also use libgcc, so libgcc must come last
- staticLibs = []string{"-lmingwex", "-lmingw32"}
+ // libmingw32 and libmingwex might also use libgcc, so libgcc must come last,
+ // and they also have some inter-dependencies, so must use linker groups.
+ staticLibs = []string{"-Wl,--start-group", "-lmingwex", "-lmingw32", "-Wl,--end-group"}
}
if cgoLibGccFile != "" {
staticLibs = append(staticLibs, cgoLibGccFile)
}
+ cflags := stringList(cgoCPPFLAGS, cgoCFLAGS)
for _, cfile := range cfiles {
ofile := obj + cfile[:len(cfile)-1] + "o"
- if err := b.gcc(p, ofile, cgoCFLAGS, obj+cfile); err != nil {
+ if err := b.gcc(p, ofile, cflags, obj+cfile); err != nil {
return nil, nil, err
}
linkobj = append(linkobj, ofile)
@@ -1893,14 +2067,27 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo,
outObj = append(outObj, ofile)
}
}
+
for _, file := range gccfiles {
ofile := obj + cgoRe.ReplaceAllString(file[:len(file)-1], "_") + "o"
- if err := b.gcc(p, ofile, cgoCFLAGS, file); err != nil {
+ if err := b.gcc(p, ofile, cflags, file); err != nil {
return nil, nil, err
}
linkobj = append(linkobj, ofile)
outObj = append(outObj, ofile)
}
+
+ cxxflags := stringList(cgoCPPFLAGS, cgoCXXFLAGS)
+ for _, file := range gxxfiles {
+ // Append .o to the file, just in case the pkg has file.c and file.cpp
+ ofile := obj + cgoRe.ReplaceAllString(file, "_") + ".o"
+ if err := b.gxx(p, ofile, cxxflags, file); err != nil {
+ return nil, nil, err
+ }
+ linkobj = append(linkobj, ofile)
+ outObj = append(outObj, ofile)
+ }
+
linkobj = append(linkobj, p.SysoFiles...)
dynobj := obj + "_cgo_.o"
if goarch == "arm" && goos == "linux" { // we need to use -pie for Linux/ARM to get accurate imported sym
@@ -1956,7 +2143,27 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo,
}
// Run SWIG on all SWIG input files.
-func (b *builder) swig(p *Package, obj string, gccfiles []string) (outGo, outObj []string, err error) {
+// TODO: Don't build a shared library, once SWIG emits the necessary
+// pragmas for external linking.
+func (b *builder) swig(p *Package, obj string, gccfiles, gxxfiles []string) (outGo, outObj []string, err error) {
+
+ var extraObj []string
+ for _, file := range gccfiles {
+ ofile := obj + cgoRe.ReplaceAllString(file[:len(file)-1], "_") + "o"
+ if err := b.gcc(p, ofile, nil, file); err != nil {
+ return nil, nil, err
+ }
+ extraObj = append(extraObj, ofile)
+ }
+
+ for _, file := range gxxfiles {
+ // Append .o to the file, just in case the pkg has file.c and file.cpp
+ ofile := obj + cgoRe.ReplaceAllString(file, "_") + ".o"
+ if err := b.gxx(p, ofile, nil, file); err != nil {
+ return nil, nil, err
+ }
+ extraObj = append(extraObj, ofile)
+ }
intgosize, err := b.swigIntSize(obj)
if err != nil {
@@ -1964,7 +2171,7 @@ func (b *builder) swig(p *Package, obj string, gccfiles []string) (outGo, outObj
}
for _, f := range p.SwigFiles {
- goFile, objFile, err := b.swigOne(p, f, obj, false, intgosize)
+ goFile, objFile, err := b.swigOne(p, f, obj, false, intgosize, extraObj)
if err != nil {
return nil, nil, err
}
@@ -1976,7 +2183,7 @@ func (b *builder) swig(p *Package, obj string, gccfiles []string) (outGo, outObj
}
}
for _, f := range p.SwigCXXFiles {
- goFile, objFile, err := b.swigOne(p, f, obj, true, intgosize)
+ goFile, objFile, err := b.swigOne(p, f, obj, true, intgosize, extraObj)
if err != nil {
return nil, nil, err
}
@@ -1999,6 +2206,9 @@ const i int = 1 << 32
// Determine the size of int on the target system for the -intgosize option
// of swig >= 2.0.9
func (b *builder) swigIntSize(obj string) (intsize string, err error) {
+ if buildN {
+ return "$INTBITS", nil
+ }
src := filepath.Join(b.work, "swig_intsize.go")
if err = ioutil.WriteFile(src, []byte(swigIntSizeCode), 0644); err != nil {
return
@@ -2014,7 +2224,7 @@ func (b *builder) swigIntSize(obj string) (intsize string, err error) {
}
// Run SWIG on one SWIG input file.
-func (b *builder) swigOne(p *Package, file, obj string, cxx bool, intgosize string) (outGo, outObj string, err error) {
+func (b *builder) swigOne(p *Package, file, obj string, cxx bool, intgosize string, extraObj []string) (outGo, outObj string, err error) {
n := 5 // length of ".swig"
if cxx {
n = 8 // length of ".swigcxx"
@@ -2085,7 +2295,8 @@ func (b *builder) swigOne(p *Package, file, obj string, cxx bool, intgosize stri
cxxlib = []string{"-lstdc++"}
}
ldflags := stringList(osldflags[goos], cxxlib)
- b.run(p.Dir, p.ImportPath, nil, b.gccCmd(p.Dir), "-o", soname, gccObj, ldflags)
+ target := filepath.Join(obj, soname)
+ b.run(p.Dir, p.ImportPath, nil, b.gccCmd(p.Dir), "-o", target, gccObj, extraObj, ldflags)
return obj + goFile, cObj, nil
}
@@ -2130,3 +2341,16 @@ func raceInit() {
buildContext.InstallSuffix += "race"
buildContext.BuildTags = append(buildContext.BuildTags, "race")
}
+
+// defaultSuffix returns file extension used for command files in
+// current os environment.
+func defaultSuffix() string {
+ switch runtime.GOOS {
+ case "windows":
+ return ".bat"
+ case "plan9":
+ return ".rc"
+ default:
+ return ".bash"
+ }
+}
diff --git a/src/cmd/go/clean.go b/src/cmd/go/clean.go
index 8345c9af1..16687f72f 100644
--- a/src/cmd/go/clean.go
+++ b/src/cmd/go/clean.go
@@ -137,22 +137,38 @@ func clean(p *Package) {
}
_, elem := filepath.Split(p.Dir)
- allRemove := []string{
- elem,
- elem + ".exe",
- elem + ".test",
- elem + ".test.exe",
+ var allRemove []string
+
+ // Remove dir-named executable only if this is package main.
+ if p.Name == "main" {
+ allRemove = append(allRemove,
+ elem,
+ elem+".exe",
+ )
}
+
+ // Remove package test executables.
+ allRemove = append(allRemove,
+ elem+".test",
+ elem+".test.exe",
+ )
+
+ // Remove a potental executable for each .go file in the directory that
+ // is not part of the directory's package.
for _, dir := range dirs {
name := dir.Name()
if packageFile[name] {
continue
}
if !dir.IsDir() && strings.HasSuffix(name, ".go") {
+ // TODO(adg,rsc): check that this .go file is actually
+ // in "package main", and therefore capable of building
+ // to an executable file.
base := name[:len(name)-len(".go")]
allRemove = append(allRemove, base, base+".exe")
}
}
+
if cleanN || cleanX {
b.showcmd(p.Dir, "rm -f %s", strings.Join(allRemove, " "))
}
@@ -221,7 +237,23 @@ func clean(p *Package) {
// removeFile tries to remove file f, if error other than file doesn't exist
// occurs, it will report the error.
func removeFile(f string) {
- if err := os.Remove(f); err != nil && !os.IsNotExist(err) {
- errorf("go clean: %v", err)
+ err := os.Remove(f)
+ if err == nil || os.IsNotExist(err) {
+ return
+ }
+ // Windows does not allow deletion of a binary file while it is executing.
+ if toolIsWindows {
+ // Remove lingering ~ file from last attempt.
+ if _, err2 := os.Stat(f + "~"); err2 == nil {
+ os.Remove(f + "~")
+ }
+ // Try to move it out of the way. If the move fails,
+ // which is likely, we'll try again the
+ // next time we do an install of this binary.
+ if err2 := os.Rename(f, f+"~"); err2 == nil {
+ os.Remove(f + "~")
+ return
+ }
}
+ errorf("go clean: %v", err)
}
diff --git a/src/cmd/go/discovery.go b/src/cmd/go/discovery.go
index 047834050..75228b52a 100644
--- a/src/cmd/go/discovery.go
+++ b/src/cmd/go/discovery.go
@@ -13,17 +13,35 @@ package main
import (
"encoding/xml"
+ "fmt"
"io"
"strings"
)
+// charsetReader returns a reader for the given charset. Currently
+// it only supports UTF-8 and ASCII. Otherwise, it returns a meaningful
+// error which is printed by go get, so the user can find why the package
+// wasn't downloaded if the encoding is not supported. Note that, in
+// order to reduce potential errors, ASCII is treated as UTF-8 (i.e. characters
+// greater than 0x7f are not rejected).
+func charsetReader(charset string, input io.Reader) (io.Reader, error) {
+ switch strings.ToLower(charset) {
+ case "ascii":
+ return input, nil
+ default:
+ return nil, fmt.Errorf("can't decode XML document using charset %q", charset)
+ }
+}
+
// parseMetaGoImports returns meta imports from the HTML in r.
// Parsing ends at the end of the <head> section or the beginning of the <body>.
-func parseMetaGoImports(r io.Reader) (imports []metaImport) {
+func parseMetaGoImports(r io.Reader) (imports []metaImport, err error) {
d := xml.NewDecoder(r)
+ d.CharsetReader = charsetReader
d.Strict = false
+ var t xml.Token
for {
- t, err := d.Token()
+ t, err = d.Token()
if err != nil {
return
}
diff --git a/src/cmd/go/doc.go b/src/cmd/go/doc.go
index df82ab45b..ebb2f37fd 100644
--- a/src/cmd/go/doc.go
+++ b/src/cmd/go/doc.go
@@ -16,7 +16,6 @@ The commands are:
build compile packages and dependencies
clean remove object files
- doc run godoc on package sources
env print Go environment information
fix run go tool fix on packages
fmt run gofmt on package sources
@@ -33,9 +32,10 @@ Use "go help [command]" for more information about a command.
Additional help topics:
+ c calling between Go and C
gopath GOPATH environment variable
+ importpath import path syntax
packages description of package lists
- remote remote import path syntax
testflag description of testing flags
testfunc description of testing functions
@@ -112,7 +112,8 @@ in an element in the list, surround it with either single or double quotes.
For more about specifying packages, see 'go help packages'.
For more about where packages and binaries are installed,
-see 'go help gopath'.
+run 'go help gopath'. For more about calling between Go and C/C++,
+run 'go help c'.
See also: go install, go get, go clean.
@@ -162,26 +163,6 @@ The -x flag causes clean to print remove commands as it executes them.
For more about specifying packages, see 'go help packages'.
-Run godoc on package sources
-
-Usage:
-
- go doc [-n] [-x] [packages]
-
-Doc runs the godoc command on the packages named by the
-import paths.
-
-For more about godoc, see 'godoc godoc'.
-For more about specifying packages, see 'go help packages'.
-
-The -n flag prints commands that would be executed.
-The -x flag prints commands as they are executed.
-
-To run godoc with specific options, run godoc itself.
-
-See also: go fix, go fmt, go vet.
-
-
Print Go environment information
Usage:
@@ -229,14 +210,14 @@ The -x flag prints commands as they are executed.
To run gofmt with specific options, run gofmt itself.
-See also: go doc, go fix, go vet.
+See also: go fix, go vet.
Download and install packages and dependencies
Usage:
- go get [-d] [-fix] [-u] [build flags] [packages]
+ go get [-d] [-fix] [-t] [-u] [build flags] [packages]
Get downloads and installs the packages named by the import paths,
along with their dependencies.
@@ -247,6 +228,9 @@ it instructs get not to install the packages.
The -fix flag instructs get to run the fix tool on the downloaded packages
before resolving dependencies or building the code.
+The -t flag instructs get to also download the packages required to build
+the tests for the specified packages.
+
The -u flag instructs get to use the network to update the named packages
and their dependencies. By default, get uses the network to check out
missing packages but does not use it to look for updates to existing packages.
@@ -263,7 +247,7 @@ retrieves the most recent version of the package.
For more about specifying packages, see 'go help packages'.
For more about how 'go get' finds source code to
-download, see 'go help remote'.
+download, see 'go help importpath'.
See also: go build, go install, go clean.
@@ -287,7 +271,7 @@ List packages
Usage:
- go list [-e] [-f format] [-json] [-tags 'tag list'] [packages]
+ go list [-e] [-race] [-f format] [-json] [-tags 'tag list'] [packages]
List lists the packages named by the import paths, one per line.
@@ -318,14 +302,17 @@ which calls strings.Join. The struct being passed to the template is:
CgoFiles []string // .go sources files that import "C"
IgnoredGoFiles []string // .go sources ignored due to build constraints
CFiles []string // .c source files
- HFiles []string // .h source files
+ CXXFiles []string // .cc, .cxx and .cpp source files
+ HFiles []string // .h, .hh, .hpp and .hxx source files
SFiles []string // .s source files
- SysoFiles []string // .syso object files to add to archive
SwigFiles []string // .swig files
SwigCXXFiles []string // .swigcxx files
+ SysoFiles []string // .syso object files to add to archive
// Cgo directives
CgoCFLAGS []string // cgo: flags for C compiler
+ CgoCPPFLAGS []string // cgo: flags for C preprocessor
+ CgoCXXFLAGS []string // cgo: flags for C++ compiler
CgoLDFLAGS []string // cgo: flags for linker
CgoPkgConfig []string // cgo: pkg-config names
@@ -360,6 +347,9 @@ a non-nil Error field; other information may or may not be missing
The -tags flag specifies a list of build tags, like in the 'go build'
command.
+The -race flag causes the package data to include the dependencies
+required by the race detector.
+
For more about specifying packages, see 'go help packages'.
@@ -370,7 +360,7 @@ Usage:
go run [build flags] gofiles... [arguments...]
Run compiles and runs the main package comprising the named Go source files.
-If no files are named, it compiles and runs all non-test Go source files.
+A Go source file is defined to be a file ending in a literal ".go" suffix.
For more about build flags, see 'go help build'.
@@ -381,7 +371,7 @@ Test packages
Usage:
- go test [-c] [-i] [build flags] [packages] [flags for test binary]
+ go test [-c] [-i] [build and test flags] [packages] [flags for test binary]
'Go test' automates testing the packages named by the import paths.
It prints a summary of the test results in the format:
@@ -394,8 +384,10 @@ It prints a summary of the test results in the format:
followed by detailed output for each failed package.
'Go test' recompiles each package along with any files with names matching
-the file pattern "*_test.go". These additional files can contain test functions,
-benchmark functions, and example functions. See 'go help testfunc' for more.
+the file pattern "*_test.go".
+Files whose names begin with "_" (including "_test.go") or "." are ignored.
+These additional files can contain test functions, benchmark functions, and
+example functions. See 'go help testfunc' for more.
Each listed package causes the execution of a separate test binary.
Test files that declare a package with the suffix "_test" will be compiled as a
@@ -419,6 +411,11 @@ In addition to the build flags, the flags handled by 'go test' itself are:
The test binary also accepts flags that control execution of the test; these
flags are also accessible by 'go test'. See 'go help testflag' for details.
+If the test binary needs any other flags, they should be presented after the
+package names. The go tool treats as a flag the first argument that begins with
+a minus sign that it does not recognize itself; that argument and all subsequent
+arguments are passed as arguments to the test binary.
+
For more about build flags, see 'go help build'.
For more about specifying packages, see 'go help packages'.
@@ -457,7 +454,7 @@ Usage:
Vet runs the Go vet command on the packages named by the import paths.
-For more about vet, see 'godoc vet'.
+For more about vet, see 'godoc code.google.com/p/go.tools/cmd/vet'.
For more about specifying packages, see 'go help packages'.
To run the vet tool with specific options, run 'go tool vet'.
@@ -468,6 +465,25 @@ The -x flag prints commands as they are executed.
See also: go fmt, go fix.
+Calling between Go and C
+
+There are two different ways to call between Go and C/C++ code.
+
+The first is the cgo tool, which is part of the Go distribution. For
+information on how to use it see the cgo documentation (godoc cmd/cgo).
+
+The second is the SWIG program, which is a general tool for
+interfacing between languages. For information on SWIG see
+http://swig.org/. When running go build, any file with a .swig
+extension will be passed to SWIG. Any file with a .swigcxx extension
+will be passed to SWIG with the -c++ option.
+
+When either cgo or SWIG is used, go build will pass any .c, .s, or .S
+files to the C compiler, and any .cc, .cpp, .cxx files to the C++
+compiler. The CC or CXX environment variables may be set to determine
+the C or C++ compiler, respectively, to use.
+
+
GOPATH environment variable
The Go path is used to resolve import statements.
@@ -528,59 +544,40 @@ but new packages are always downloaded into the first directory
in the list.
-Description of package lists
-
-Many commands apply to a set of packages:
-
- go action [packages]
-
-Usually, [packages] is a list of import paths.
-
-An import path that is a rooted path or that begins with
-a . or .. element is interpreted as a file system path and
-denotes the package in that directory.
-
-Otherwise, the import path P denotes the package found in
-the directory DIR/src/P for some DIR listed in the GOPATH
-environment variable (see 'go help gopath').
-
-If no import paths are given, the action applies to the
-package in the current directory.
-
-The special import path "all" expands to all package directories
-found in all the GOPATH trees. For example, 'go list all'
-lists all the packages on the local system.
-
-The special import path "std" is like all but expands to just the
-packages in the standard Go library.
-
-An import path is a pattern if it includes one or more "..." wildcards,
-each of which can match any string, including the empty string and
-strings containing slashes. Such a pattern expands to all package
-directories found in the GOPATH trees with names matching the
-patterns. As a special case, x/... matches x as well as x's subdirectories.
-For example, net/... expands to net and packages in its subdirectories.
-
-An import path can also name a package to be downloaded from
-a remote repository. Run 'go help remote' for details.
-
-Every package in a program must have a unique import path.
-By convention, this is arranged by starting each path with a
-unique prefix that belongs to you. For example, paths used
-internally at Google all begin with 'google', and paths
-denoting remote repositories begin with the path to the code,
-such as 'code.google.com/p/project'.
-
-As a special case, if the package list is a list of .go files from a
-single directory, the command is applied to a single synthesized
-package made up of exactly those files, ignoring any build constraints
-in those files and ignoring any other files in the directory.
-
-
-Remote import path syntax
+Import path syntax
An import path (see 'go help packages') denotes a package
-stored in the local file system. Certain import paths also
+stored in the local file system. In general, an import path denotes
+either a standard package (such as "unicode/utf8") or a package
+found in one of the work spaces (see 'go help gopath').
+
+Relative import paths
+
+An import path beginning with ./ or ../ is called a relative path.
+The toolchain supports relative import paths as a shortcut in two ways.
+
+First, a relative path can be used as a shorthand on the command line.
+If you are working in the directory containing the code imported as
+"unicode" and want to run the tests for "unicode/utf8", you can type
+"go test ./utf8" instead of needing to specify the full path.
+Similarly, in the reverse situation, "go test .." will test "unicode" from
+the "unicode/utf8" directory. Relative patterns are also allowed, like
+"go test ./..." to test all subdirectories. See 'go help packages' for details
+on the pattern syntax.
+
+Second, if you are compiling a Go program not in a work space,
+you can use a relative path in an import statement in that program
+to refer to nearby code also not in a work space.
+This makes it easy to experiment with small multipackage programs
+outside of the usual work spaces, but such programs cannot be
+installed with "go install" (there is no work space in which to install them),
+so they are rebuilt from scratch each time they are built.
+To avoid ambiguity, Go programs cannot use relative import paths
+within a work space.
+
+Remote import paths
+
+Certain import paths also
describe how to obtain the source code for the package using
a revision control system.
@@ -691,6 +688,62 @@ package appropriate for the Go release being used.
Run 'go help install' for more.
+Description of package lists
+
+Many commands apply to a set of packages:
+
+ go action [packages]
+
+Usually, [packages] is a list of import paths.
+
+An import path that is a rooted path or that begins with
+a . or .. element is interpreted as a file system path and
+denotes the package in that directory.
+
+Otherwise, the import path P denotes the package found in
+the directory DIR/src/P for some DIR listed in the GOPATH
+environment variable (see 'go help gopath').
+
+If no import paths are given, the action applies to the
+package in the current directory.
+
+There are three reserved names for paths that should not be used
+for packages to be built with the go tool:
+
+- "main" denotes the top-level package in a stand-alone executable.
+
+- "all" expands to all package directories found in all the GOPATH
+trees. For example, 'go list all' lists all the packages on the local
+system.
+
+- "std" is like all but expands to just the packages in the standard
+Go library.
+
+An import path is a pattern if it includes one or more "..." wildcards,
+each of which can match any string, including the empty string and
+strings containing slashes. Such a pattern expands to all package
+directories found in the GOPATH trees with names matching the
+patterns. As a special case, x/... matches x as well as x's subdirectories.
+For example, net/... expands to net and packages in its subdirectories.
+
+An import path can also name a package to be downloaded from
+a remote repository. Run 'go help importpath' for details.
+
+Every package in a program must have a unique import path.
+By convention, this is arranged by starting each path with a
+unique prefix that belongs to you. For example, paths used
+internally at Google all begin with 'google', and paths
+denoting remote repositories begin with the path to the code,
+such as 'code.google.com/p/project'.
+
+As a special case, if the package list is a list of .go files from a
+single directory, the command is applied to a single synthesized
+package made up of exactly those files, ignoring any build constraints
+in those files and ignoring any other files in the directory.
+
+File names that begin with "." or "_" are ignored by the go tool.
+
+
Description of testing flags
The 'go test' command takes both flags that apply to 'go test' itself
@@ -730,6 +783,30 @@ control the execution of any test:
if -test.blockprofile is set without this flag, all blocking events
are recorded, equivalent to -test.blockprofilerate=1.
+ -cover
+ Enable coverage analysis.
+
+ -covermode set,count,atomic
+ Set the mode for coverage analysis for the package[s]
+ being tested. The default is "set".
+ The values:
+ set: bool: does this statement run?
+ count: int: how many times does this statement run?
+ atomic: int: count, but correct in multithreaded tests;
+ significantly more expensive.
+ Sets -cover.
+
+ -coverpkg pkg1,pkg2,pkg3
+ Apply coverage analysis in each test to the given list of packages.
+ The default is for each test to analyze only the package being tested.
+ Packages are specified as import paths.
+ Sets -cover.
+
+ -coverprofile cover.out
+ Write a coverage profile to the specified file after all tests
+ have passed.
+ Sets -cover.
+
-cpu 1,2,4
Specify a list of GOMAXPROCS values for which the tests or
benchmarks should be executed. The default is the current value
@@ -750,6 +827,10 @@ control the execution of any test:
garbage collector, provided the test can run in the available
memory without garbage collection.
+ -outputdir directory
+ Place output files from profiling in the specified directory,
+ by default the directory in which "go test" is running.
+
-parallel n
Allow parallel execution of test functions that call t.Parallel.
The value of this flag is the maximum number of tests to run
@@ -787,8 +868,8 @@ will compile the test binary and then run it as
pkg.test -test.v -test.cpuprofile=prof.out -dir=testdata -update
-The test flags that generate profiles also leave the test binary in pkg.test
-for use when analyzing the profiles.
+The test flags that generate profiles (other than for coverage) also
+leave the test binary in pkg.test for use when analyzing the profiles.
Flags not recognized by 'go test' must be placed after any specified packages.
@@ -837,5 +918,3 @@ See the documentation of the testing package for more information.
*/
package main
-
-// NOTE: cmdDoc is in fmt.go.
diff --git a/src/cmd/go/env.go b/src/cmd/go/env.go
index 00e03e9bd..2db821797 100644
--- a/src/cmd/go/env.go
+++ b/src/cmd/go/env.go
@@ -45,12 +45,17 @@ func mkEnv() []envVar {
{"GORACE", os.Getenv("GORACE")},
{"GOROOT", goroot},
{"GOTOOLDIR", toolDir},
+
+ // disable escape codes in clang errors
+ {"TERM", "dumb"},
}
if goos != "plan9" {
cmd := b.gccCmd(".")
env = append(env, envVar{"CC", cmd[0]})
env = append(env, envVar{"GOGCCFLAGS", strings.Join(cmd[3:], " ")})
+ cmd = b.gxxCmd(".")
+ env = append(env, envVar{"CXX", cmd[0]})
}
if buildContext.CgoEnabled {
diff --git a/src/cmd/go/fmt.go b/src/cmd/go/fmt.go
index 9d3c911dd..65dc3ca59 100644
--- a/src/cmd/go/fmt.go
+++ b/src/cmd/go/fmt.go
@@ -6,7 +6,6 @@ package main
func init() {
addBuildFlagsNX(cmdFmt)
- addBuildFlagsNX(cmdDoc)
}
var cmdFmt = &Command{
@@ -25,7 +24,7 @@ The -x flag prints commands as they are executed.
To run gofmt with specific options, run gofmt itself.
-See also: go doc, go fix, go vet.
+See also: go fix, go vet.
`,
}
@@ -37,37 +36,3 @@ func runFmt(cmd *Command, args []string) {
run(stringList("gofmt", "-l", "-w", relPaths(pkg.allgofiles)))
}
}
-
-var cmdDoc = &Command{
- Run: runDoc,
- UsageLine: "doc [-n] [-x] [packages]",
- Short: "run godoc on package sources",
- Long: `
-Doc runs the godoc command on the packages named by the
-import paths.
-
-For more about godoc, see 'godoc godoc'.
-For more about specifying packages, see 'go help packages'.
-
-The -n flag prints commands that would be executed.
-The -x flag prints commands as they are executed.
-
-To run godoc with specific options, run godoc itself.
-
-See also: go fix, go fmt, go vet.
- `,
-}
-
-func runDoc(cmd *Command, args []string) {
- for _, pkg := range packages(args) {
- if pkg.ImportPath == "command-line arguments" {
- errorf("go doc: cannot use package file list")
- continue
- }
- if pkg.local {
- run("godoc", pkg.Dir)
- } else {
- run("godoc", pkg.ImportPath)
- }
- }
-}
diff --git a/src/cmd/go/get.go b/src/cmd/go/get.go
index 8c08ab261..e61da7e2a 100644
--- a/src/cmd/go/get.go
+++ b/src/cmd/go/get.go
@@ -18,7 +18,7 @@ import (
)
var cmdGet = &Command{
- UsageLine: "get [-d] [-fix] [-u] [build flags] [packages]",
+ UsageLine: "get [-d] [-fix] [-t] [-u] [build flags] [packages]",
Short: "download and install packages and dependencies",
Long: `
Get downloads and installs the packages named by the import paths,
@@ -30,6 +30,9 @@ it instructs get not to install the packages.
The -fix flag instructs get to run the fix tool on the downloaded packages
before resolving dependencies or building the code.
+The -t flag instructs get to also download the packages required to build
+the tests for the specified packages.
+
The -u flag instructs get to use the network to update the named packages
and their dependencies. By default, get uses the network to check out
missing packages but does not use it to look for updates to existing packages.
@@ -46,13 +49,14 @@ retrieves the most recent version of the package.
For more about specifying packages, see 'go help packages'.
For more about how 'go get' finds source code to
-download, see 'go help remote'.
+download, see 'go help importpath'.
See also: go build, go install, go clean.
`,
}
var getD = cmdGet.Flag.Bool("d", false, "")
+var getT = cmdGet.Flag.Bool("t", false, "")
var getU = cmdGet.Flag.Bool("u", false, "")
var getFix = cmdGet.Flag.Bool("fix", false, "")
@@ -65,7 +69,7 @@ func runGet(cmd *Command, args []string) {
// Phase 1. Download/update.
var stk importStack
for _, arg := range downloadPaths(args) {
- download(arg, &stk)
+ download(arg, &stk, *getT)
}
exitIfErrors()
@@ -137,7 +141,7 @@ var downloadRootCache = map[string]bool{}
// download runs the download half of the get command
// for the package named by the argument.
-func download(arg string, stk *importStack) {
+func download(arg string, stk *importStack, getTestDeps bool) {
p := loadPackage(arg, stk)
// There's nothing to do if this is a package in the standard library.
@@ -153,6 +157,7 @@ func download(arg string, stk *importStack) {
pkgs := []*Package{p}
wildcardOkay := len(*stk) == 0
+ isWildcard := false
// Download if the package is missing, or update if we're using -u.
if p.Dir == "" || *getU {
@@ -175,6 +180,7 @@ func download(arg string, stk *importStack) {
} else {
args = matchPackages(arg)
}
+ isWildcard = true
}
// Clear all relevant package cache entries before
@@ -214,9 +220,30 @@ func download(arg string, stk *importStack) {
}
}
+ if isWildcard {
+ // Report both the real package and the
+ // wildcard in any error message.
+ stk.push(p.ImportPath)
+ }
+
// Process dependencies, now that we know what they are.
for _, dep := range p.deps {
- download(dep.ImportPath, stk)
+ // Don't get test dependencies recursively.
+ download(dep.ImportPath, stk, false)
+ }
+ if getTestDeps {
+ // Process test dependencies when -t is specified.
+ // (Don't get test dependencies for test dependencies.)
+ for _, path := range p.TestImports {
+ download(path, stk, false)
+ }
+ for _, path := range p.XTestImports {
+ download(path, stk, false)
+ }
+ }
+
+ if isWildcard {
+ stk.pop()
}
}
}
@@ -286,7 +313,7 @@ func downloadPackage(p *Package) error {
}
// Some version control tools require the parent of the target to exist.
parent, _ := filepath.Split(root)
- if err := os.MkdirAll(parent, 0777); err != nil {
+ if err = os.MkdirAll(parent, 0777); err != nil {
return err
}
if err = vcs.create(root, repo); err != nil {
diff --git a/src/cmd/go/help.go b/src/cmd/go/help.go
index c70a25fdd..71e55175a 100644
--- a/src/cmd/go/help.go
+++ b/src/cmd/go/help.go
@@ -4,6 +4,28 @@
package main
+var helpC = &Command{
+ UsageLine: "c",
+ Short: "calling between Go and C",
+ Long: `
+There are two different ways to call between Go and C/C++ code.
+
+The first is the cgo tool, which is part of the Go distribution. For
+information on how to use it see the cgo documentation (godoc cmd/cgo).
+
+The second is the SWIG program, which is a general tool for
+interfacing between languages. For information on SWIG see
+http://swig.org/. When running go build, any file with a .swig
+extension will be passed to SWIG. Any file with a .swigcxx extension
+will be passed to SWIG with the -c++ option.
+
+When either cgo or SWIG is used, go build will pass any .c, .s, or .S
+files to the C compiler, and any .cc, .cpp, .cxx files to the C++
+compiler. The CC or CXX environment variables may be set to determine
+the C or C++ compiler, respectively, to use.
+ `,
+}
+
var helpPackages = &Command{
UsageLine: "packages",
Short: "description of package lists",
@@ -25,12 +47,17 @@ environment variable (see 'go help gopath').
If no import paths are given, the action applies to the
package in the current directory.
-The special import path "all" expands to all package directories
-found in all the GOPATH trees. For example, 'go list all'
-lists all the packages on the local system.
+There are three reserved names for paths that should not be used
+for packages to be built with the go tool:
+
+- "main" denotes the top-level package in a stand-alone executable.
-The special import path "std" is like all but expands to just the
-packages in the standard Go library.
+- "all" expands to all package directories found in all the GOPATH
+trees. For example, 'go list all' lists all the packages on the local
+system.
+
+- "std" is like all but expands to just the packages in the standard
+Go library.
An import path is a pattern if it includes one or more "..." wildcards,
each of which can match any string, including the empty string and
@@ -40,7 +67,7 @@ patterns. As a special case, x/... matches x as well as x's subdirectories.
For example, net/... expands to net and packages in its subdirectories.
An import path can also name a package to be downloaded from
-a remote repository. Run 'go help remote' for details.
+a remote repository. Run 'go help importpath' for details.
Every package in a program must have a unique import path.
By convention, this is arranged by starting each path with a
@@ -53,16 +80,48 @@ As a special case, if the package list is a list of .go files from a
single directory, the command is applied to a single synthesized
package made up of exactly those files, ignoring any build constraints
in those files and ignoring any other files in the directory.
+
+File names that begin with "." or "_" are ignored by the go tool.
`,
}
-var helpRemote = &Command{
- UsageLine: "remote",
- Short: "remote import path syntax",
+var helpImportPath = &Command{
+ UsageLine: "importpath",
+ Short: "import path syntax",
Long: `
An import path (see 'go help packages') denotes a package
-stored in the local file system. Certain import paths also
+stored in the local file system. In general, an import path denotes
+either a standard package (such as "unicode/utf8") or a package
+found in one of the work spaces (see 'go help gopath').
+
+Relative import paths
+
+An import path beginning with ./ or ../ is called a relative path.
+The toolchain supports relative import paths as a shortcut in two ways.
+
+First, a relative path can be used as a shorthand on the command line.
+If you are working in the directory containing the code imported as
+"unicode" and want to run the tests for "unicode/utf8", you can type
+"go test ./utf8" instead of needing to specify the full path.
+Similarly, in the reverse situation, "go test .." will test "unicode" from
+the "unicode/utf8" directory. Relative patterns are also allowed, like
+"go test ./..." to test all subdirectories. See 'go help packages' for details
+on the pattern syntax.
+
+Second, if you are compiling a Go program not in a work space,
+you can use a relative path in an import statement in that program
+to refer to nearby code also not in a work space.
+This makes it easy to experiment with small multipackage programs
+outside of the usual work spaces, but such programs cannot be
+installed with "go install" (there is no work space in which to install them),
+so they are rebuilt from scratch each time they are built.
+To avoid ambiguity, Go programs cannot use relative import paths
+within a work space.
+
+Remote import paths
+
+Certain import paths also
describe how to obtain the source code for the package using
a revision control system.
diff --git a/src/cmd/go/list.go b/src/cmd/go/list.go
index 2d23d077e..f56ebed38 100644
--- a/src/cmd/go/list.go
+++ b/src/cmd/go/list.go
@@ -14,7 +14,7 @@ import (
)
var cmdList = &Command{
- UsageLine: "list [-e] [-f format] [-json] [-tags 'tag list'] [packages]",
+ UsageLine: "list [-e] [-race] [-f format] [-json] [-tags 'tag list'] [packages]",
Short: "list packages",
Long: `
List lists the packages named by the import paths, one per line.
@@ -46,14 +46,17 @@ which calls strings.Join. The struct being passed to the template is:
CgoFiles []string // .go sources files that import "C"
IgnoredGoFiles []string // .go sources ignored due to build constraints
CFiles []string // .c source files
- HFiles []string // .h source files
+ CXXFiles []string // .cc, .cxx and .cpp source files
+ HFiles []string // .h, .hh, .hpp and .hxx source files
SFiles []string // .s source files
- SysoFiles []string // .syso object files to add to archive
SwigFiles []string // .swig files
SwigCXXFiles []string // .swigcxx files
+ SysoFiles []string // .syso object files to add to archive
// Cgo directives
CgoCFLAGS []string // cgo: flags for C compiler
+ CgoCPPFLAGS []string // cgo: flags for C preprocessor
+ CgoCXXFLAGS []string // cgo: flags for C++ compiler
CgoLDFLAGS []string // cgo: flags for linker
CgoPkgConfig []string // cgo: pkg-config names
@@ -88,6 +91,9 @@ a non-nil Error field; other information may or may not be missing
The -tags flag specifies a list of build tags, like in the 'go build'
command.
+The -race flag causes the package data to include the dependencies
+required by the race detector.
+
For more about specifying packages, see 'go help packages'.
`,
}
@@ -101,12 +107,17 @@ func init() {
var listE = cmdList.Flag.Bool("e", false, "")
var listFmt = cmdList.Flag.String("f", "{{.ImportPath}}", "")
var listJson = cmdList.Flag.Bool("json", false, "")
+var listRace = cmdList.Flag.Bool("race", false, "")
var nl = []byte{'\n'}
func runList(cmd *Command, args []string) {
out := newTrackingWriter(os.Stdout)
defer out.w.Flush()
+ if *listRace {
+ buildRace = true
+ }
+
var do func(*Package)
if *listJson {
do = func(p *Package) {
diff --git a/src/cmd/go/main.go b/src/cmd/go/main.go
index 3180dbeed..df0cf1b3f 100644
--- a/src/cmd/go/main.go
+++ b/src/cmd/go/main.go
@@ -76,7 +76,6 @@ func (c *Command) Runnable() bool {
var commands = []*Command{
cmdBuild,
cmdClean,
- cmdDoc,
cmdEnv,
cmdFix,
cmdFmt,
@@ -89,9 +88,10 @@ var commands = []*Command{
cmdVersion,
cmdVet,
+ helpC,
helpGopath,
+ helpImportPath,
helpPackages,
- helpRemote,
helpTestflag,
helpTestfunc,
}
@@ -144,6 +144,11 @@ func main() {
}
}
+ if fi, err := os.Stat(goroot); err != nil || !fi.IsDir() {
+ fmt.Fprintf(os.Stderr, "go: cannot find GOROOT directory: %v\n", goroot)
+ os.Exit(2)
+ }
+
for _, cmd := range commands {
if cmd.Name() == args[0] && cmd.Run != nil {
cmd.Flag.Usage = func() { cmd.Usage() }
@@ -208,8 +213,6 @@ var documentationTemplate = `// Copyright 2011 The Go Authors. All rights reser
{{end}}*/
package main
-
-// NOTE: cmdDoc is in fmt.go.
`
// tmpl executes the given template text on data, writing the result to w.
@@ -358,7 +361,7 @@ func exitIfErrors() {
func run(cmdargs ...interface{}) {
cmdline := stringList(cmdargs...)
- if buildN || buildV {
+ if buildN || buildX {
fmt.Printf("%s\n", strings.Join(cmdline, " "))
if buildN {
return
@@ -432,6 +435,37 @@ func matchPattern(pattern string) func(name string) bool {
}
}
+// hasPathPrefix reports whether the path s begins with the
+// elements in prefix.
+func hasPathPrefix(s, prefix string) bool {
+ switch {
+ default:
+ return false
+ case len(s) == len(prefix):
+ return s == prefix
+ case len(s) > len(prefix):
+ if prefix != "" && prefix[len(prefix)-1] == '/' {
+ return strings.HasPrefix(s, prefix)
+ }
+ return s[len(prefix)] == '/' && s[:len(prefix)] == prefix
+ }
+}
+
+// treeCanMatchPattern(pattern)(name) reports whether
+// name or children of name can possibly match pattern.
+// Pattern is the same limited glob accepted by matchPattern.
+func treeCanMatchPattern(pattern string) func(name string) bool {
+ wildCard := false
+ if i := strings.Index(pattern, "..."); i >= 0 {
+ wildCard = true
+ pattern = pattern[:i]
+ }
+ return func(name string) bool {
+ return len(name) <= len(pattern) && hasPathPrefix(pattern, name) ||
+ wildCard && strings.HasPrefix(name, pattern)
+ }
+}
+
// allPackages returns all the packages that can be found
// under the $GOPATH directories and $GOROOT matching pattern.
// The pattern is either "all" (all packages), "std" (standard packages)
@@ -446,8 +480,10 @@ func allPackages(pattern string) []string {
func matchPackages(pattern string) []string {
match := func(string) bool { return true }
+ treeCanMatch := func(string) bool { return true }
if pattern != "all" && pattern != "std" {
match = matchPattern(pattern)
+ treeCanMatch = treeCanMatchPattern(pattern)
}
have := map[string]bool{
@@ -465,6 +501,9 @@ func matchPackages(pattern string) []string {
return nil
}
name := path[len(cmd):]
+ if !treeCanMatch(name) {
+ return filepath.SkipDir
+ }
// Commands are all in cmd/, not in subdirectories.
if strings.Contains(name, string(filepath.Separator)) {
return filepath.SkipDir
@@ -481,6 +520,9 @@ func matchPackages(pattern string) []string {
}
_, err = buildContext.ImportDir(path, 0)
if err != nil {
+ if _, noGo := err.(*build.NoGoError); !noGo {
+ log.Print(err)
+ }
return nil
}
pkgs = append(pkgs, name)
@@ -507,6 +549,9 @@ func matchPackages(pattern string) []string {
if pattern == "std" && strings.Contains(name, ".") {
return filepath.SkipDir
}
+ if !treeCanMatch(name) {
+ return filepath.SkipDir
+ }
if have[name] {
return nil
}
@@ -515,8 +560,10 @@ func matchPackages(pattern string) []string {
return nil
}
_, err = buildContext.ImportDir(path, 0)
- if err != nil && strings.Contains(err.Error(), "no Go source files") {
- return nil
+ if err != nil {
+ if _, noGo := err.(*build.NoGoError); noGo {
+ return nil
+ }
}
pkgs = append(pkgs, name)
return nil
@@ -583,6 +630,9 @@ func matchPackagesInFS(pattern string) []string {
return nil
}
if _, err = build.ImportDir(path, 0); err != nil {
+ if _, noGo := err.(*build.NoGoError); !noGo {
+ log.Print(err)
+ }
return nil
}
pkgs = append(pkgs, name)
diff --git a/src/cmd/go/match_test.go b/src/cmd/go/match_test.go
index f058f235a..38b9b115e 100644
--- a/src/cmd/go/match_test.go
+++ b/src/cmd/go/match_test.go
@@ -6,11 +6,7 @@ package main
import "testing"
-var matchTests = []struct {
- pattern string
- path string
- match bool
-}{
+var matchPatternTests = []stringPairTest{
{"...", "foo", true},
{"net", "net", true},
{"net", "net/http", false},
@@ -27,10 +23,66 @@ var matchTests = []struct {
}
func TestMatchPattern(t *testing.T) {
- for _, tt := range matchTests {
- match := matchPattern(tt.pattern)(tt.path)
- if match != tt.match {
- t.Errorf("matchPattern(%q)(%q) = %v, want %v", tt.pattern, tt.path, match, tt.match)
+ testStringPairs(t, "matchPattern", matchPatternTests, func(pattern, name string) bool {
+ return matchPattern(pattern)(name)
+ })
+}
+
+var treeCanMatchPatternTests = []stringPairTest{
+ {"...", "foo", true},
+ {"net", "net", true},
+ {"net", "net/http", false},
+ {"net/http", "net", true},
+ {"net/http", "net/http", true},
+ {"net...", "netchan", true},
+ {"net...", "net", true},
+ {"net...", "net/http", true},
+ {"net...", "not/http", false},
+ {"net/...", "netchan", false},
+ {"net/...", "net", true},
+ {"net/...", "net/http", true},
+ {"net/...", "not/http", false},
+ {"abc.../def", "abcxyz", true},
+ {"abc.../def", "xyxabc", false},
+ {"x/y/z/...", "x", true},
+ {"x/y/z/...", "x/y", true},
+ {"x/y/z/...", "x/y/z", true},
+ {"x/y/z/...", "x/y/z/w", true},
+ {"x/y/z", "x", true},
+ {"x/y/z", "x/y", true},
+ {"x/y/z", "x/y/z", true},
+ {"x/y/z", "x/y/z/w", false},
+ {"x/.../y/z", "x/a/b/c", true},
+ {"x/.../y/z", "y/x/a/b/c", false},
+}
+
+func TestChildrenCanMatchPattern(t *testing.T) {
+ testStringPairs(t, "treeCanMatchPattern", treeCanMatchPatternTests, func(pattern, name string) bool {
+ return treeCanMatchPattern(pattern)(name)
+ })
+}
+
+var hasPathPrefixTests = []stringPairTest{
+ {"abc", "a", false},
+ {"a/bc", "a", true},
+ {"a", "a", true},
+ {"a/bc", "a/", true},
+}
+
+func TestHasPathPrefix(t *testing.T) {
+ testStringPairs(t, "hasPathPrefix", hasPathPrefixTests, hasPathPrefix)
+}
+
+type stringPairTest struct {
+ in1 string
+ in2 string
+ out bool
+}
+
+func testStringPairs(t *testing.T, name string, tests []stringPairTest, f func(string, string) bool) {
+ for _, tt := range tests {
+ if out := f(tt.in1, tt.in2); out != tt.out {
+ t.Errorf("%s(%q, %q) = %v, want %v", name, tt.in1, tt.in2, out, tt.out)
}
}
}
diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go
index b33d800bf..71f14c74a 100644
--- a/src/cmd/go/pkg.go
+++ b/src/cmd/go/pkg.go
@@ -25,29 +25,33 @@ type Package struct {
// Note: These fields are part of the go command's public API.
// See list.go. It is okay to add fields, but not to change or
// remove existing ones. Keep in sync with list.go
- Dir string `json:",omitempty"` // directory containing package sources
- ImportPath string `json:",omitempty"` // import path of package in dir
- Name string `json:",omitempty"` // package name
- Doc string `json:",omitempty"` // package documentation string
- Target string `json:",omitempty"` // install path
- Goroot bool `json:",omitempty"` // is this package found in the Go root?
- Standard bool `json:",omitempty"` // is this package part of the standard Go library?
- Stale bool `json:",omitempty"` // would 'go install' do anything for this package?
- Root string `json:",omitempty"` // Go root or Go path dir containing this package
+ Dir string `json:",omitempty"` // directory containing package sources
+ ImportPath string `json:",omitempty"` // import path of package in dir
+ Name string `json:",omitempty"` // package name
+ Doc string `json:",omitempty"` // package documentation string
+ Target string `json:",omitempty"` // install path
+ Goroot bool `json:",omitempty"` // is this package found in the Go root?
+ Standard bool `json:",omitempty"` // is this package part of the standard Go library?
+ Stale bool `json:",omitempty"` // would 'go install' do anything for this package?
+ Root string `json:",omitempty"` // Go root or Go path dir containing this package
+ ConflictDir string `json:",omitempty"` // Dir is hidden by this other directory
// Source files
GoFiles []string `json:",omitempty"` // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
CgoFiles []string `json:",omitempty"` // .go sources files that import "C"
IgnoredGoFiles []string `json:",omitempty"` // .go sources ignored due to build constraints
CFiles []string `json:",omitempty"` // .c source files
- HFiles []string `json:",omitempty"` // .h source files
+ CXXFiles []string `json:",omitempty"` // .cc, .cpp and .cxx source files
+ HFiles []string `json:",omitempty"` // .h, .hh, .hpp and .hxx source files
SFiles []string `json:",omitempty"` // .s source files
- SysoFiles []string `json:",omitempty"` // .syso system object files added to package
SwigFiles []string `json:",omitempty"` // .swig files
SwigCXXFiles []string `json:",omitempty"` // .swigcxx files
+ SysoFiles []string `json:",omitempty"` // .syso system object files added to package
// Cgo directives
CgoCFLAGS []string `json:",omitempty"` // cgo: flags for C compiler
+ CgoCPPFLAGS []string `json:",omitempty"` // cgo: flags for C preprocessor
+ CgoCXXFLAGS []string `json:",omitempty"` // cgo: flags for C++ compiler
CgoLDFLAGS []string `json:",omitempty"` // cgo: flags for linker
CgoPkgConfig []string `json:",omitempty"` // cgo: pkg-config names
@@ -73,14 +77,23 @@ type Package struct {
deps []*Package
gofiles []string // GoFiles+CgoFiles+TestGoFiles+XTestGoFiles files, absolute paths
sfiles []string
- allgofiles []string // gofiles + IgnoredGoFiles, absolute paths
- target string // installed file for this package (may be executable)
- fake bool // synthesized package
- forceBuild bool // this package must be rebuilt
- forceLibrary bool // this package is a library (even if named "main")
- local bool // imported via local path (./ or ../)
- localPrefix string // interpret ./ and ../ imports relative to this prefix
- exeName string // desired name for temporary executable
+ allgofiles []string // gofiles + IgnoredGoFiles, absolute paths
+ target string // installed file for this package (may be executable)
+ fake bool // synthesized package
+ forceBuild bool // this package must be rebuilt
+ forceLibrary bool // this package is a library (even if named "main")
+ cmdline bool // defined by files listed on command line
+ local bool // imported via local path (./ or ../)
+ localPrefix string // interpret ./ and ../ imports relative to this prefix
+ exeName string // desired name for temporary executable
+ coverMode string // preprocess Go source files with the coverage tool in this mode
+ coverVars map[string]*CoverVar // variables created by coverage analysis
+}
+
+// CoverVar holds the name of the generated coverage variables targeting the named file.
+type CoverVar struct {
+ File string // local file name
+ Var string // name of count struct
}
func (p *Package) copyBuild(pp *build.Package) {
@@ -91,6 +104,7 @@ func (p *Package) copyBuild(pp *build.Package) {
p.Name = pp.Name
p.Doc = pp.Doc
p.Root = pp.Root
+ p.ConflictDir = pp.ConflictDir
// TODO? Target
p.Goroot = pp.Goroot
p.Standard = p.Goroot && p.ImportPath != "" && !strings.Contains(p.ImportPath, ".")
@@ -98,12 +112,15 @@ func (p *Package) copyBuild(pp *build.Package) {
p.CgoFiles = pp.CgoFiles
p.IgnoredGoFiles = pp.IgnoredGoFiles
p.CFiles = pp.CFiles
+ p.CXXFiles = pp.CXXFiles
p.HFiles = pp.HFiles
p.SFiles = pp.SFiles
- p.SysoFiles = pp.SysoFiles
p.SwigFiles = pp.SwigFiles
p.SwigCXXFiles = pp.SwigCXXFiles
+ p.SysoFiles = pp.SysoFiles
p.CgoCFLAGS = pp.CgoCFLAGS
+ p.CgoCPPFLAGS = pp.CgoCPPFLAGS
+ p.CgoCXXFLAGS = pp.CgoCXXFLAGS
p.CgoLDFLAGS = pp.CgoLDFLAGS
p.CgoPkgConfig = pp.CgoPkgConfig
p.Imports = pp.Imports
@@ -115,12 +132,17 @@ func (p *Package) copyBuild(pp *build.Package) {
// A PackageError describes an error loading information about a package.
type PackageError struct {
- ImportStack []string // shortest path from package named on command line to this one
- Pos string // position of error
- Err string // the error itself
+ ImportStack []string // shortest path from package named on command line to this one
+ Pos string // position of error
+ Err string // the error itself
+ isImportCycle bool // the error is an import cycle
}
func (p *PackageError) Error() string {
+ // Import cycles deserve special treatment.
+ if p.isImportCycle {
+ return fmt.Sprintf("%s: %s\npackage %s\n", p.Pos, p.Err, strings.Join(p.ImportStack, "\n\timports "))
+ }
if p.Pos != "" {
// Omit import stack. The full path to the file where the error
// is the most important thing.
@@ -257,26 +279,38 @@ func reusePackage(p *Package, stk *importStack) *Package {
if p.imports == nil {
if p.Error == nil {
p.Error = &PackageError{
- ImportStack: stk.copy(),
- Err: "import cycle not allowed",
+ ImportStack: stk.copy(),
+ Err: "import cycle not allowed",
+ isImportCycle: true,
}
}
p.Incomplete = true
}
- if p.Error != nil && stk.shorterThan(p.Error.ImportStack) {
+ // Don't rewrite the import stack in the error if we have an import cycle.
+ // If we do, we'll lose the path that describes the cycle.
+ if p.Error != nil && !p.Error.isImportCycle && stk.shorterThan(p.Error.ImportStack) {
p.Error.ImportStack = stk.copy()
}
return p
}
-// isGoTool is the list of directories for Go programs that are installed in
-// $GOROOT/pkg/tool.
-var isGoTool = map[string]bool{
- "cmd/api": true,
- "cmd/cgo": true,
- "cmd/fix": true,
- "cmd/vet": true,
- "cmd/yacc": true,
+type targetDir int
+
+const (
+ toRoot targetDir = iota // to bin dir inside package root (default)
+ toTool // GOROOT/pkg/tool
+ toBin // GOROOT/bin
+)
+
+// goTools is a map of Go program import path to install target directory.
+var goTools = map[string]targetDir{
+ "cmd/api": toTool,
+ "cmd/cgo": toTool,
+ "cmd/fix": toTool,
+ "cmd/yacc": toTool,
+ "code.google.com/p/go.tools/cmd/cover": toTool,
+ "code.google.com/p/go.tools/cmd/godoc": toBin,
+ "code.google.com/p/go.tools/cmd/vet": toTool,
}
// expandScanner expands a scanner.List error into all the errors in the list.
@@ -300,6 +334,23 @@ func expandScanner(err error) error {
return err
}
+var raceExclude = map[string]bool{
+ "runtime/race": true,
+ "runtime/cgo": true,
+ "cmd/cgo": true,
+ "syscall": true,
+ "errors": true,
+}
+
+var cgoExclude = map[string]bool{
+ "runtime/cgo": true,
+}
+
+var cgoSyscallExclude = map[string]bool{
+ "runtime/cgo": true,
+ "runtime/race": true,
+}
+
// load populates p using information from bp, err, which should
// be the result of calling build.Context.Import.
func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package {
@@ -326,10 +377,18 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
// Install cross-compiled binaries to subdirectories of bin.
elem = full
}
- if p.build.BinDir != "" {
+ if p.build.BinDir != gobin && goTools[p.ImportPath] == toBin {
+ // Override BinDir.
+ // This is from a subrepo but installs to $GOROOT/bin
+ // by default anyway (like godoc).
+ p.target = filepath.Join(gorootBin, elem)
+ } else if p.build.BinDir != "" {
+ // Install to GOBIN or bin of GOPATH entry.
p.target = filepath.Join(p.build.BinDir, elem)
}
- if p.Goroot && (isGoTool[p.ImportPath] || strings.HasPrefix(p.ImportPath, "exp/")) {
+ if goTools[p.ImportPath] == toTool {
+ // This is for 'go tool'.
+ // Override all the usual logic and force it into the tool directory.
p.target = filepath.Join(gorootPkg, "tool", full)
}
if p.target != "" && buildContext.GOOS == "windows" {
@@ -344,17 +403,22 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
}
importPaths := p.Imports
- // Packages that use cgo import runtime/cgo implicitly,
- // except runtime/cgo itself.
- if len(p.CgoFiles) > 0 && (!p.Standard || p.ImportPath != "runtime/cgo") {
+ // Packages that use cgo import runtime/cgo implicitly.
+ // Packages that use cgo also import syscall implicitly,
+ // to wrap errno.
+ // Exclude certain packages to avoid circular dependencies.
+ if len(p.CgoFiles) > 0 && (!p.Standard || !cgoExclude[p.ImportPath]) {
importPaths = append(importPaths, "runtime/cgo")
}
+ if len(p.CgoFiles) > 0 && (!p.Standard || !cgoSyscallExclude[p.ImportPath]) {
+ importPaths = append(importPaths, "syscall")
+ }
// Everything depends on runtime, except runtime and unsafe.
if !p.Standard || (p.ImportPath != "runtime" && p.ImportPath != "unsafe") {
importPaths = append(importPaths, "runtime")
// When race detection enabled everything depends on runtime/race.
- // Exclude runtime/cgo and cmd/cgo to avoid circular dependencies.
- if buildRace && (!p.Standard || (p.ImportPath != "runtime/race" && p.ImportPath != "runtime/cgo" && p.ImportPath != "cmd/cgo")) {
+ // Exclude certain packages to avoid circular dependencies.
+ if buildRace && (!p.Standard || !raceExclude[p.ImportPath]) {
importPaths = append(importPaths, "runtime/race")
}
}
@@ -389,6 +453,7 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
p.CgoFiles,
p.IgnoredGoFiles,
p.CFiles,
+ p.CXXFiles,
p.HFiles,
p.SFiles,
p.SysoFiles,
@@ -476,11 +541,16 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
return p
}
-// usesSwig returns whether the package needs to run SWIG.
+// usesSwig reports whether the package needs to run SWIG.
func (p *Package) usesSwig() bool {
return len(p.SwigFiles) > 0 || len(p.SwigCXXFiles) > 0
}
+// usesCgo reports whether the package needs to run cgo
+func (p *Package) usesCgo() bool {
+ return len(p.CgoFiles) > 0
+}
+
// swigSoname returns the name of the shared library we create for a
// SWIG input file.
func (p *Package) swigSoname(file string) string {
@@ -611,28 +681,13 @@ func isStale(p *Package, topRoot map[string]bool) bool {
return false
}
- srcs := stringList(p.GoFiles, p.CFiles, p.HFiles, p.SFiles, p.CgoFiles, p.SysoFiles)
+ srcs := stringList(p.GoFiles, p.CFiles, p.CXXFiles, p.HFiles, p.SFiles, p.CgoFiles, p.SysoFiles, p.SwigFiles, p.SwigCXXFiles)
for _, src := range srcs {
if olderThan(filepath.Join(p.Dir, src)) {
return true
}
}
- for _, src := range stringList(p.SwigFiles, p.SwigCXXFiles) {
- if olderThan(filepath.Join(p.Dir, src)) {
- return true
- }
- soname := p.swigSoname(src)
- fi, err := os.Stat(soname)
- if err != nil {
- return true
- }
- fiSrc, err := os.Stat(src)
- if err != nil || fiSrc.ModTime().After(fi.ModTime()) {
- return true
- }
- }
-
return false
}
diff --git a/src/cmd/go/run.go b/src/cmd/go/run.go
index 91bdc1be2..e6dadd229 100644
--- a/src/cmd/go/run.go
+++ b/src/cmd/go/run.go
@@ -16,7 +16,7 @@ var cmdRun = &Command{
Short: "compile and run Go program",
Long: `
Run compiles and runs the main package comprising the named Go source files.
-If no files are named, it compiles and runs all non-test Go source files.
+A Go source file is defined to be a file ending in a literal ".go" suffix.
For more about build flags, see 'go help build'.
diff --git a/src/cmd/go/signal_notunix.go b/src/cmd/go/signal_notunix.go
index ef13c1919..29aa9d8c2 100644
--- a/src/cmd/go/signal_notunix.go
+++ b/src/cmd/go/signal_notunix.go
@@ -11,3 +11,7 @@ import (
)
var signalsToIgnore = []os.Signal{os.Interrupt}
+
+// signalTrace is the signal to send to make a Go program
+// crash with a stack trace.
+var signalTrace os.Signal = nil
diff --git a/src/cmd/go/signal_unix.go b/src/cmd/go/signal_unix.go
index 489a73b83..00c71657f 100644
--- a/src/cmd/go/signal_unix.go
+++ b/src/cmd/go/signal_unix.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.
-// +build darwin freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd
package main
@@ -12,3 +12,7 @@ import (
)
var signalsToIgnore = []os.Signal{os.Interrupt, syscall.SIGQUIT}
+
+// signalTrace is the signal to send to make a Go program
+// crash with a stack trace.
+var signalTrace os.Signal = syscall.SIGQUIT
diff --git a/src/cmd/go/test.bash b/src/cmd/go/test.bash
index e2264a46e..f71d67818 100755
--- a/src/cmd/go/test.bash
+++ b/src/cmd/go/test.bash
@@ -5,15 +5,44 @@
set -e
go build -o testgo
+go() {
+ echo TEST ERROR: ran go, not testgo: go "$@" >&2
+ exit 2
+}
+
+started=false
+TEST() {
+ if $started; then
+ stop
+ fi
+ echo TEST: "$@"
+ started=true
+ ok=true
+}
+stop() {
+ if ! $started; then
+ echo TEST ERROR: stop missing start >&2
+ exit 2
+ fi
+ started=false
+ if $ok; then
+ echo PASS
+ else
+ echo FAIL
+ allok=false
+ fi
+}
ok=true
+allok=true
unset GOPATH
unset GOBIN
+TEST 'file:line in error messages'
# Test that error messages have file:line information at beginning of
# the line. Also test issue 4917: that the error is on stderr.
-d=$(mktemp -d -t testgoXXX)
+d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX)
fn=$d/err.go
echo "package main" > $fn
echo 'import "bar"' >> $fn
@@ -28,6 +57,7 @@ rm -r $d
# Test local (./) imports.
testlocal() {
local="$1"
+ TEST local imports $2 '(easy)'
./testgo build -o hello "testdata/$local/easy.go"
./hello >hello.out
if ! grep -q '^easysub\.Hello' hello.out; then
@@ -36,6 +66,7 @@ testlocal() {
ok=false
fi
+ TEST local imports $2 '(easysub)'
./testgo build -o hello "testdata/$local/easysub/main.go"
./hello >hello.out
if ! grep -q '^easysub\.Hello' hello.out; then
@@ -44,6 +75,7 @@ testlocal() {
ok=false
fi
+ TEST local imports $2 '(hard)'
./testgo build -o hello "testdata/$local/hard.go"
./hello >hello.out
if ! grep -q '^sub\.Hello' hello.out || ! grep -q '^subsub\.Hello' hello.out ; then
@@ -55,6 +87,7 @@ testlocal() {
rm -f hello.out hello
# Test that go install x.go fails.
+ TEST local imports $2 '(go install should fail)'
if ./testgo install "testdata/$local/easy.go" >/dev/null 2>&1; then
echo "go install testdata/$local/easy.go succeeded"
ok=false
@@ -62,22 +95,53 @@ testlocal() {
}
# Test local imports
-testlocal local
+testlocal local ''
# Test local imports again, with bad characters in the directory name.
bad='#$%:, &()*;<=>?\^{}'
rm -rf "testdata/$bad"
cp -R testdata/local "testdata/$bad"
-testlocal "$bad"
+testlocal "$bad" 'with bad characters in path'
rm -rf "testdata/$bad"
+TEST error message for syntax error in test go file says FAIL
+export GOPATH=$(pwd)/testdata
+if ./testgo test syntaxerror 2>testdata/err; then
+ echo 'go test syntaxerror succeeded'
+ ok=false
+elif ! grep FAIL testdata/err >/dev/null; then
+ echo 'go test did not say FAIL:'
+ cat testdata/err
+ ok=false
+fi
+rm -f ./testdata/err
+unset GOPATH
+
+TEST wildcards do not look in useless directories
+export GOPATH=$(pwd)/testdata
+if ./testgo list ... >testdata/err 2>&1; then
+ echo "go list ... succeeded"
+ ok=false
+elif ! grep badpkg testdata/err >/dev/null; then
+ echo "go list ... failure does not mention badpkg"
+ cat testdata/err
+ ok=false
+elif ! ./testgo list m... >testdata/err 2>&1; then
+ echo "go list m... failed"
+ ok=false
+fi
+rm -rf ./testdata/err
+unset GOPATH
+
# Test tests with relative imports.
+TEST relative imports '(go test)'
if ! ./testgo test ./testdata/testimport; then
echo "go test ./testdata/testimport failed"
ok=false
fi
# Test installation with relative imports.
+TEST relative imports '(go test -i)'
if ! ./testgo test -i ./testdata/testimport; then
echo "go test -i ./testdata/testimport failed"
ok=false
@@ -85,13 +149,40 @@ fi
# Test tests with relative imports in packages synthesized
# from Go files named on the command line.
+TEST relative imports in command-line package
if ! ./testgo test ./testdata/testimport/*.go; then
echo "go test ./testdata/testimport/*.go failed"
ok=false
fi
+TEST version control error message includes correct directory
+export GOPATH=$(pwd)/testdata/shadow/root1
+if ./testgo get -u foo 2>testdata/err; then
+ echo "go get -u foo succeeded unexpectedly"
+ ok=false
+elif ! grep testdata/shadow/root1/src/foo testdata/err >/dev/null; then
+ echo "go get -u error does not mention shadow/root1/src/foo:"
+ cat testdata/err
+ ok=false
+fi
+unset GOPATH
+
+TEST go install fails with no buildable files
+export GOPATH=$(pwd)/testdata
+export CGO_ENABLED=0
+if ./testgo install cgotest 2>testdata/err; then
+ echo "go install cgotest succeeded unexpectedly"
+elif ! grep 'no buildable Go source files' testdata/err >/dev/null; then
+ echo "go install cgotest did not report 'no buildable Go source files'"
+ cat testdata/err
+ ok=false
+fi
+unset CGO_ENABLED
+unset GOPATH
+
# Test that without $GOBIN set, binaries get installed
# into the GOPATH bin directory.
+TEST install into GOPATH
rm -rf testdata/bin
if ! GOPATH=$(pwd)/testdata ./testgo install go-cmd-test; then
echo "go install go-cmd-test failed"
@@ -101,7 +192,29 @@ elif ! test -x testdata/bin/go-cmd-test; then
ok=false
fi
+TEST package main_test imports archive not binary
+export GOBIN=$(pwd)/testdata/bin
+mkdir -p $GOBIN
+export GOPATH=$(pwd)/testdata
+touch ./testdata/src/main_test/m.go
+if ! ./testgo test main_test; then
+ echo "go test main_test failed without install"
+ ok=false
+elif ! ./testgo install main_test; then
+ echo "go test main_test failed"
+ ok=false
+elif [ "$(./testgo list -f '{{.Stale}}' main_test)" != false ]; then
+ echo "after go install, main listed as stale"
+ ok=false
+elif ! ./testgo test main_test; then
+ echo "go test main_test failed after install"
+ ok=false
+fi
+rm -rf $GOBIN
+unset GOBIN
+
# And with $GOBIN set, binaries get installed to $GOBIN.
+TEST install into GOBIN
if ! GOBIN=$(pwd)/testdata/bin1 GOPATH=$(pwd)/testdata ./testgo install go-cmd-test; then
echo "go install go-cmd-test failed"
ok=false
@@ -112,12 +225,19 @@ fi
# Without $GOBIN set, installing a program outside $GOPATH should fail
# (there is nowhere to install it).
-if ./testgo install testdata/src/go-cmd-test/helloworld.go; then
+TEST install without destination fails
+if ./testgo install testdata/src/go-cmd-test/helloworld.go 2>testdata/err; then
echo "go install testdata/src/go-cmd-test/helloworld.go should have failed, did not"
ok=false
+elif ! grep 'no install location for .go files listed on command line' testdata/err; then
+ echo "wrong error:"
+ cat testdata/err
+ ok=false
fi
+rm -f testdata/err
# With $GOBIN set, should install there.
+TEST install to GOBIN '(command-line package)'
if ! GOBIN=$(pwd)/testdata/bin1 ./testgo install testdata/src/go-cmd-test/helloworld.go; then
echo "go install testdata/src/go-cmd-test/helloworld.go failed"
ok=false
@@ -126,24 +246,88 @@ elif ! test -x testdata/bin1/helloworld; then
ok=false
fi
+TEST godoc installs into GOBIN
+d=$(mktemp -d -t testgoXXX)
+export GOPATH=$d
+mkdir $d/gobin
+GOBIN=$d/gobin ./testgo get code.google.com/p/go.tools/cmd/godoc
+if [ ! -x $d/gobin/godoc ]; then
+ echo did not install godoc to '$GOBIN'
+ GOBIN=$d/gobin ./testgo list -f 'Target: {{.Target}}' code.google.com/p/go.tools/cmd/godoc
+ ok=false
+fi
+
+TEST godoc installs into GOROOT
+rm -f $GOROOT/bin/godoc
+./testgo install code.google.com/p/go.tools/cmd/godoc
+if [ ! -x $GOROOT/bin/godoc ]; then
+ echo did not install godoc to '$GOROOT/bin'
+ ./testgo list -f 'Target: {{.Target}}' code.google.com/p/go.tools/cmd/godoc
+ ok=false
+fi
+
+TEST cmd/fix installs into tool
+GOOS=$(./testgo env GOOS)
+GOARCH=$(./testgo env GOARCH)
+rm -f $GOROOT/pkg/tool/${GOOS}_${GOARCH}/fix
+./testgo install cmd/fix
+if [ ! -x $GOROOT/pkg/tool/${GOOS}_${GOARCH}/fix ]; then
+ echo 'did not install cmd/fix to $GOROOT/pkg/tool'
+ GOBIN=$d/gobin ./testgo list -f 'Target: {{.Target}}' cmd/fix
+ ok=false
+fi
+rm -f $GOROOT/pkg/tool/${GOOS}_${GOARCH}/fix
+GOBIN=$d/gobin ./testgo install cmd/fix
+if [ ! -x $GOROOT/pkg/tool/${GOOS}_${GOARCH}/fix ]; then
+ echo 'did not install cmd/fix to $GOROOT/pkg/tool with $GOBIN set'
+ GOBIN=$d/gobin ./testgo list -f 'Target: {{.Target}}' cmd/fix
+ ok=false
+fi
+
+TEST gopath program installs into GOBIN
+mkdir $d/src/progname
+echo 'package main; func main() {}' >$d/src/progname/p.go
+GOBIN=$d/gobin ./testgo install progname
+if [ ! -x $d/gobin/progname ]; then
+ echo 'did not install progname to $GOBIN/progname'
+ ./testgo list -f 'Target: {{.Target}}' cmd/api
+ ok=false
+fi
+rm -f $d/gobin/progname $d/bin/progname
+
+TEST gopath program installs into GOPATH/bin
+./testgo install progname
+if [ ! -x $d/bin/progname ]; then
+ echo 'did not install progname to $GOPATH/bin/progname'
+ ./testgo list -f 'Target: {{.Target}}' progname
+ ok=false
+fi
+
+unset GOPATH
+rm -rf $d
+
# Reject relative paths in GOPATH.
+TEST reject relative paths in GOPATH '(command-line package)'
if GOPATH=. ./testgo build testdata/src/go-cmd-test/helloworld.go; then
echo 'GOPATH="." go build should have failed, did not'
ok=false
fi
+TEST reject relative paths in GOPATH
if GOPATH=:$(pwd)/testdata:. ./testgo build go-cmd-test; then
echo 'GOPATH=":$(pwd)/testdata:." go build should have failed, did not'
ok=false
fi
# issue 4104
+TEST go test with package listed multiple times
if [ $(./testgo test fmt fmt fmt fmt fmt | wc -l) -ne 1 ] ; then
echo 'go test fmt fmt fmt fmt fmt tested the same package multiple times'
ok=false
fi
# ensure that output of 'go list' is consistent between runs
+TEST go list is consistent
./testgo list std > test_std.list
if ! ./testgo list std | cmp -s test_std.list - ; then
echo "go list std ordering is inconsistent"
@@ -151,32 +335,38 @@ if ! ./testgo list std | cmp -s test_std.list - ; then
fi
rm -f test_std.list
-# issue 4096. Validate the output of unsucessful go install foo/quxx
+# issue 4096. Validate the output of unsuccessful go install foo/quxx
+TEST unsuccessful go install should mention missing package
if [ $(./testgo install 'foo/quxx' 2>&1 | grep -c 'cannot find package "foo/quxx" in any of') -ne 1 ] ; then
echo 'go install foo/quxx expected error: .*cannot find package "foo/quxx" in any of'
ok=false
fi
# test GOROOT search failure is reported
+TEST GOROOT search failure reporting
if [ $(./testgo install 'foo/quxx' 2>&1 | egrep -c 'foo/quxx \(from \$GOROOT\)$') -ne 1 ] ; then
echo 'go install foo/quxx expected error: .*foo/quxx (from $GOROOT)'
ok=false
fi
# test multiple GOPATH entries are reported separately
+TEST multiple GOPATH entries reported separately
if [ $(GOPATH=$(pwd)/testdata/a:$(pwd)/testdata/b ./testgo install 'foo/quxx' 2>&1 | egrep -c 'testdata/./src/foo/quxx') -ne 2 ] ; then
echo 'go install foo/quxx expected error: .*testdata/a/src/foo/quxx (from $GOPATH)\n.*testdata/b/src/foo/quxx'
ok=false
fi
# test (from $GOPATH) annotation is reported for the first GOPATH entry
+TEST mention GOPATH in first GOPATH entry
if [ $(GOPATH=$(pwd)/testdata/a:$(pwd)/testdata/b ./testgo install 'foo/quxx' 2>&1 | egrep -c 'testdata/a/src/foo/quxx \(from \$GOPATH\)$') -ne 1 ] ; then
echo 'go install foo/quxx expected error: .*testdata/a/src/foo/quxx (from $GOPATH)'
ok=false
fi
# but not on the second
+TEST but not the second entry
if [ $(GOPATH=$(pwd)/testdata/a:$(pwd)/testdata/b ./testgo install 'foo/quxx' 2>&1 | egrep -c 'testdata/b/src/foo/quxx$') -ne 1 ] ; then
echo 'go install foo/quxx expected error: .*testdata/b/src/foo/quxx'
ok=false
fi
# test missing GOPATH is reported
+TEST missing GOPATH is reported
if [ $(GOPATH= ./testgo install 'foo/quxx' 2>&1 | egrep -c '\(\$GOPATH not set\)$') -ne 1 ] ; then
echo 'go install foo/quxx expected error: ($GOPATH not set)'
ok=false
@@ -184,6 +374,7 @@ fi
# issue 4186. go get cannot be used to download packages to $GOROOT
# Test that without GOPATH set, go get should fail
+TEST without GOPATH, go get fails
d=$(mktemp -d -t testgoXXX)
mkdir -p $d/src/pkg
if GOPATH= GOROOT=$d ./testgo get -d code.google.com/p/go.codereview/cmd/hgpatch ; then
@@ -191,7 +382,9 @@ if GOPATH= GOROOT=$d ./testgo get -d code.google.com/p/go.codereview/cmd/hgpatch
ok=false
fi
rm -rf $d
+
# Test that with GOPATH=$GOROOT, go get should fail
+TEST with GOPATH=GOROOT, go get fails
d=$(mktemp -d -t testgoXXX)
mkdir -p $d/src/pkg
if GOPATH=$d GOROOT=$d ./testgo get -d code.google.com/p/go.codereview/cmd/hgpatch ; then
@@ -200,7 +393,7 @@ if GOPATH=$d GOROOT=$d ./testgo get -d code.google.com/p/go.codereview/cmd/hgpat
fi
rm -rf $d
-# issue 3941: args with spaces
+TEST ldflags arguments with spaces '(issue 3941)'
d=$(mktemp -d -t testgoXXX)
cat >$d/main.go<<EOF
package main
@@ -215,9 +408,9 @@ if ! grep -q '^hello world' hello.out; then
cat hello.out
ok=false
fi
-rm -rf $d
+rm -rf $d hello.out
-# test that go test -cpuprofile leaves binary behind
+TEST go test -cpuprofile leaves binary behind
./testgo test -cpuprofile strings.prof strings || ok=false
if [ ! -x strings.test ]; then
echo "go test -cpuprofile did not create strings.test"
@@ -225,9 +418,10 @@ if [ ! -x strings.test ]; then
fi
rm -f strings.prof strings.test
-# issue 4568. test that symlinks don't screw things up too badly.
+TEST symlinks do not confuse go list '(issue 4568)'
old=$(pwd)
-d=$(mktemp -d -t testgoXXX)
+tmp=$(cd /tmp && pwd -P)
+d=$(TMPDIR=$tmp mktemp -d -t testgoXXX)
mkdir -p $d/src
(
ln -s $d $d/src/dir1
@@ -235,8 +429,8 @@ mkdir -p $d/src
echo package p >p.go
export GOPATH=$d
if [ "$($old/testgo list -f '{{.Root}}' .)" != "$d" ]; then
- echo got lost in symlink tree:
- pwd
+ echo Confused by symlinks.
+ echo "Package in current directory $(pwd) should have Root $d"
env|grep WD
$old/testgo list -json . dir1
touch $d/failed
@@ -247,8 +441,8 @@ if [ -f $d/failed ]; then
fi
rm -rf $d
-# issue 4515.
-d=$(mktemp -d -t testgoXXX)
+TEST 'install with tags (issue 4515)'
+d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX)
mkdir -p $d/src/example/a $d/src/example/b $d/bin
cat >$d/src/example/a/main.go <<EOF
package main
@@ -280,8 +474,8 @@ fi
unset GOPATH
rm -rf $d
-# issue 4773. case-insensitive collisions
-d=$(mktemp -d -t testgoXXX)
+TEST case collisions '(issue 4773)'
+d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX)
export GOPATH=$d
mkdir -p $d/src/example/a $d/src/example/b
cat >$d/src/example/a/a.go <<EOF
@@ -318,17 +512,120 @@ elif ! grep "case-insensitive file name collision" $d/out >/dev/null; then
echo go list example/b did not report file name collision.
ok=false
fi
+
+TEST go get cover
+./testgo get code.google.com/p/go.tools/cmd/cover || ok=false
+
unset GOPATH
rm -rf $d
+TEST shadowing logic
+export GOPATH=$(pwd)/testdata/shadow/root1:$(pwd)/testdata/shadow/root2
+
+# The math in root1 is not "math" because the standard math is.
+cdir=$(./testgo list -f '({{.ImportPath}}) ({{.ConflictDir}})' ./testdata/shadow/root1/src/math)
+if [ "$cdir" != "(_$(pwd)/testdata/shadow/root1/src/math) ($GOROOT/src/pkg/math)" ]; then
+ echo shadowed math is not shadowed: "$cdir"
+ ok=false
+fi
+
+# The foo in root1 is "foo".
+cdir=$(./testgo list -f '({{.ImportPath}}) ({{.ConflictDir}})' ./testdata/shadow/root1/src/foo)
+if [ "$cdir" != "(foo) ()" ]; then
+ echo unshadowed foo is shadowed: "$cdir"
+ ok=false
+fi
+
+# The foo in root2 is not "foo" because the foo in root1 got there first.
+cdir=$(./testgo list -f '({{.ImportPath}}) ({{.ConflictDir}})' ./testdata/shadow/root2/src/foo)
+if [ "$cdir" != "(_$(pwd)/testdata/shadow/root2/src/foo) ($(pwd)/testdata/shadow/root1/src/foo)" ]; then
+ echo shadowed foo is not shadowed: "$cdir"
+ ok=false
+fi
+
+# The error for go install should mention the conflicting directory.
+err=$(! ./testgo install ./testdata/shadow/root2/src/foo 2>&1)
+if [ "$err" != "go install: no install location for directory $(pwd)/testdata/shadow/root2/src/foo hidden by $(pwd)/testdata/shadow/root1/src/foo" ]; then
+ echo wrong shadowed install error: "$err"
+ ok=false
+fi
+
# Only succeeds if source order is preserved.
-./testgo test testdata/example[12]_test.go
+TEST source file name order preserved
+./testgo test testdata/example[12]_test.go || ok=false
+
+# Check that coverage analysis works at all.
+# Don't worry about the exact numbers
+TEST coverage runs
+./testgo test -short -coverpkg=strings strings regexp || ok=false
+./testgo test -short -cover strings math regexp || ok=false
+
+TEST cgo depends on syscall
+rm -rf $GOROOT/pkg/*_race
+d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX)
+export GOPATH=$d
+mkdir -p $d/src/foo
+echo '
+package foo
+//#include <stdio.h>
+import "C"
+' >$d/src/foo/foo.go
+./testgo build -race foo || ok=false
+rm -rf $d
+unset GOPATH
+
+TEST cgo shows full path names
+d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX)
+export GOPATH=$d
+mkdir -p $d/src/x/y/dirname
+echo '
+package foo
+import "C"
+func f() {
+' >$d/src/x/y/dirname/foo.go
+if ./testgo build x/y/dirname >$d/err 2>&1; then
+ echo build succeeded unexpectedly.
+ ok=false
+elif ! grep x/y/dirname $d/err >/dev/null; then
+ echo error did not use full path.
+ cat $d/err
+ ok=false
+fi
+rm -rf $d
+unset GOPATH
+
+TEST 'cgo handles -Wl,$ORIGIN'
+d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX)
+export GOPATH=$d
+mkdir -p $d/src/origin
+echo '
+package origin
+// #cgo LDFLAGS: -Wl,-rpath -Wl,$ORIGIN
+// void f(void) {}
+import "C"
+
+func f() { C.f() }
+' >$d/src/origin/origin.go
+if ! ./testgo build origin; then
+ echo build failed
+ ok=false
+fi
+rm -rf $d
+unset GOPATH
+
+TEST 'Issue 6480: "go test -c -test.bench=XXX fmt" should not hang'
+if ! ./testgo test -c -test.bench=XXX fmt; then
+ echo build test failed
+ ok=false
+fi
+rm -f fmt.test
# clean up
+if $started; then stop; fi
rm -rf testdata/bin testdata/bin1
rm -f testgo
-if $ok; then
+if $allok; then
echo PASS
else
echo FAIL
diff --git a/src/cmd/go/test.go b/src/cmd/go/test.go
index b1db16f77..06ac9d206 100644
--- a/src/cmd/go/test.go
+++ b/src/cmd/go/test.go
@@ -12,10 +12,12 @@ import (
"go/doc"
"go/parser"
"go/token"
+ "log"
"os"
"os/exec"
"path"
"path/filepath"
+ "regexp"
"runtime"
"sort"
"strings"
@@ -32,7 +34,7 @@ func init() {
var cmdTest = &Command{
CustomFlags: true,
- UsageLine: "test [-c] [-i] [build flags] [packages] [flags for test binary]",
+ UsageLine: "test [-c] [-i] [build and test flags] [packages] [flags for test binary]",
Short: "test packages",
Long: `
'Go test' automates testing the packages named by the import paths.
@@ -46,8 +48,10 @@ It prints a summary of the test results in the format:
followed by detailed output for each failed package.
'Go test' recompiles each package along with any files with names matching
-the file pattern "*_test.go". These additional files can contain test functions,
-benchmark functions, and example functions. See 'go help testfunc' for more.
+the file pattern "*_test.go".
+Files whose names begin with "_" (including "_test.go") or "." are ignored.
+These additional files can contain test functions, benchmark functions, and
+example functions. See 'go help testfunc' for more.
Each listed package causes the execution of a separate test binary.
Test files that declare a package with the suffix "_test" will be compiled as a
@@ -71,6 +75,11 @@ In addition to the build flags, the flags handled by 'go test' itself are:
The test binary also accepts flags that control execution of the test; these
flags are also accessible by 'go test'. See 'go help testflag' for details.
+If the test binary needs any other flags, they should be presented after the
+package names. The go tool treats as a flag the first argument that begins with
+a minus sign that it does not recognize itself; that argument and all subsequent
+arguments are passed as arguments to the test binary.
+
For more about build flags, see 'go help build'.
For more about specifying packages, see 'go help packages'.
@@ -119,6 +128,30 @@ control the execution of any test:
if -test.blockprofile is set without this flag, all blocking events
are recorded, equivalent to -test.blockprofilerate=1.
+ -cover
+ Enable coverage analysis.
+
+ -covermode set,count,atomic
+ Set the mode for coverage analysis for the package[s]
+ being tested. The default is "set".
+ The values:
+ set: bool: does this statement run?
+ count: int: how many times does this statement run?
+ atomic: int: count, but correct in multithreaded tests;
+ significantly more expensive.
+ Sets -cover.
+
+ -coverpkg pkg1,pkg2,pkg3
+ Apply coverage analysis in each test to the given list of packages.
+ The default is for each test to analyze only the package being tested.
+ Packages are specified as import paths.
+ Sets -cover.
+
+ -coverprofile cover.out
+ Write a coverage profile to the specified file after all tests
+ have passed.
+ Sets -cover.
+
-cpu 1,2,4
Specify a list of GOMAXPROCS values for which the tests or
benchmarks should be executed. The default is the current value
@@ -139,6 +172,10 @@ control the execution of any test:
garbage collector, provided the test can run in the available
memory without garbage collection.
+ -outputdir directory
+ Place output files from profiling in the specified directory,
+ by default the directory in which "go test" is running.
+
-parallel n
Allow parallel execution of test functions that call t.Parallel.
The value of this flag is the maximum number of tests to run
@@ -176,8 +213,8 @@ will compile the test binary and then run it as
pkg.test -test.v -test.cpuprofile=prof.out -dir=testdata -update
-The test flags that generate profiles also leave the test binary in pkg.test
-for use when analyzing the profiles.
+The test flags that generate profiles (other than for coverage) also
+leave the test binary in pkg.test for use when analyzing the profiles.
Flags not recognized by 'go test' must be placed after any specified packages.
`,
@@ -229,12 +266,17 @@ See the documentation of the testing package for more information.
}
var (
- testC bool // -c flag
- testProfile bool // some profiling flag
- testI bool // -i flag
- testV bool // -v flag
- testFiles []string // -file flag(s) TODO: not respected
- testTimeout string // -timeout flag
+ testC bool // -c flag
+ testCover bool // -cover flag
+ testCoverMode string // -covermode flag
+ testCoverPaths []string // -coverpkg flag
+ testCoverPkgs []*Package // -coverpkg flag
+ testProfile bool // some profiling flag
+ testNeedBinary bool // profile needs to keep binary around
+ testI bool // -i flag
+ testV bool // -v flag
+ testFiles []string // -file flag(s) TODO: not respected
+ testTimeout string // -timeout flag
testArgs []string
testBench bool
testStreamOutput bool // show output as it is generated
@@ -243,6 +285,12 @@ var (
testKillTimeout = 10 * time.Minute
)
+var testMainDeps = map[string]bool{
+ // Dependencies for testmain.
+ "testing": true,
+ "regexp": true,
+}
+
func runTest(cmd *Command, args []string) {
var pkgArgs []string
pkgArgs, testArgs = testFlags(args)
@@ -289,11 +337,11 @@ func runTest(cmd *Command, args []string) {
if testI {
buildV = testV
- deps := map[string]bool{
- // Dependencies for testmain.
- "testing": true,
- "regexp": true,
+ deps := make(map[string]bool)
+ for dep := range testMainDeps {
+ deps[dep] = true
}
+
for _, p := range pkgs {
// Dependencies for each test.
for _, path := range p.Imports {
@@ -331,7 +379,7 @@ func runTest(cmd *Command, args []string) {
a.deps = append(a.deps, b.action(modeInstall, modeInstall, p))
}
b.do(a)
- if !testC {
+ if !testC || a.failed {
return
}
b.init()
@@ -339,6 +387,34 @@ func runTest(cmd *Command, args []string) {
var builds, runs, prints []*action
+ if testCoverPaths != nil {
+ // Load packages that were asked about for coverage.
+ // packagesForBuild exits if the packages cannot be loaded.
+ testCoverPkgs = packagesForBuild(testCoverPaths)
+
+ // Warn about -coverpkg arguments that are not actually used.
+ used := make(map[string]bool)
+ for _, p := range pkgs {
+ used[p.ImportPath] = true
+ for _, dep := range p.Deps {
+ used[dep] = true
+ }
+ }
+ for _, p := range testCoverPkgs {
+ if !used[p.ImportPath] {
+ log.Printf("warning: no packages being tested depend on %s", p.ImportPath)
+ }
+ }
+
+ // Mark all the coverage packages for rebuilding with coverage.
+ for _, p := range testCoverPkgs {
+ p.Stale = true // rebuild
+ p.fake = true // do not warn about rebuild
+ p.coverMode = testCoverMode
+ p.coverVars = declareCoverVars(p.ImportPath, p.GoFiles...)
+ }
+ }
+
// Prepare build + run + print actions for all packages being tested.
for _, p := range pkgs {
buildTest, runTest, printTest, err := b.test(p)
@@ -347,10 +423,12 @@ func runTest(cmd *Command, args []string) {
if strings.HasPrefix(str, "\n") {
str = str[1:]
}
+ failed := fmt.Sprintf("FAIL\t%s [setup failed]\n", p.ImportPath)
+
if p.ImportPath != "" {
- errorf("# %s\n%s", p.ImportPath, str)
+ errorf("# %s\n%s\n%s", p.ImportPath, str, failed)
} else {
- errorf("%s", str)
+ errorf("%s\n%s", str, failed)
}
continue
}
@@ -370,16 +448,15 @@ func runTest(cmd *Command, args []string) {
}
}
- // If we are benchmarking, force everything to
- // happen in serial. Could instead allow all the
- // builds to run before any benchmarks start,
- // but try this for now.
- if testBench {
- for i, a := range builds {
- if i > 0 {
- // Make build of test i depend on
- // completing the run of test i-1.
- a.deps = append(a.deps, runs[i-1])
+ // Force benchmarks to run in serial.
+ if !testC && testBench {
+ // The first run must wait for all builds.
+ // Later runs must wait for the previous run's print.
+ for i, run := range runs {
+ if i == 0 {
+ run.deps = append(run.deps, builds...)
+ } else {
+ run.deps = append(run.deps, prints[i-1])
}
}
}
@@ -390,11 +467,22 @@ func runTest(cmd *Command, args []string) {
for _, p := range pkgs {
okBuild[p] = true
}
-
warned := false
for _, a := range actionList(root) {
- if a.p != nil && a.f != nil && !okBuild[a.p] && !a.p.fake && !a.p.local {
- okBuild[a.p] = true // don't warn again
+ if a.p == nil || okBuild[a.p] {
+ continue
+ }
+ okBuild[a.p] = true // warn at most once
+
+ // Don't warn about packages being rebuilt because of
+ // things like coverage analysis.
+ for _, p1 := range a.p.imports {
+ if p1.fake {
+ a.p.fake = true
+ }
+ }
+
+ if a.f != nil && !okBuild[a.p] && !a.p.fake && !a.p.local {
if !warned {
fmt.Fprintf(os.Stderr, "warning: building out-of-date packages:\n")
warned = true
@@ -417,11 +505,20 @@ func runTest(cmd *Command, args []string) {
b.do(root)
}
+func contains(x []string, s string) bool {
+ for _, t := range x {
+ if t == s {
+ return true
+ }
+ }
+ return false
+}
+
func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, err error) {
if len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 {
build := &action{p: p}
- run := &action{p: p}
- print := &action{f: (*builder).notest, p: p, deps: []*action{build}}
+ run := &action{p: p, deps: []*action{build}}
+ print := &action{f: (*builder).notest, p: p, deps: []*action{run}}
return build, run, print, nil
}
@@ -487,12 +584,15 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,
if err := b.mkdir(ptestDir); err != nil {
return nil, nil, nil, err
}
- if err := writeTestmain(filepath.Join(testDir, "_testmain.go"), p); err != nil {
- return nil, nil, nil, err
- }
+
+ // Should we apply coverage analysis locally,
+ // only for this package and only for this test?
+ // Yes, if -cover is on but -coverpkg has not specified
+ // a list of packages for global coverage.
+ localCover := testCover && testCoverPaths == nil
// Test package.
- if len(p.TestGoFiles) > 0 {
+ if len(p.TestGoFiles) > 0 || localCover || p.Name == "main" {
ptest = new(Package)
*ptest = *p
ptest.GoFiles = nil
@@ -515,6 +615,11 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,
m[k] = append(m[k], v...)
}
ptest.build.ImportPos = m
+
+ if localCover {
+ ptest.coverMode = testCoverMode
+ ptest.coverVars = declareCoverVars(ptest.ImportPath, ptest.GoFiles...)
+ }
} else {
ptest = p
}
@@ -548,6 +653,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,
Root: p.Root,
imports: []*Package{ptest},
build: &build.Package{Name: "main"},
+ pkgdir: testDir,
fake: true,
Stale: true,
}
@@ -557,15 +663,50 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,
// The generated main also imports testing and regexp.
stk.push("testmain")
- ptesting := loadImport("testing", "", &stk, nil)
- if ptesting.Error != nil {
- return nil, nil, nil, ptesting.Error
+ for dep := range testMainDeps {
+ if ptest.ImportPath != dep {
+ p1 := loadImport("testing", "", &stk, nil)
+ if p1.Error != nil {
+ return nil, nil, nil, p1.Error
+ }
+ pmain.imports = append(pmain.imports, p1)
+ }
+ }
+
+ if testCoverPkgs != nil {
+ // Add imports, but avoid duplicates.
+ seen := map[*Package]bool{p: true, ptest: true}
+ for _, p1 := range pmain.imports {
+ seen[p1] = true
+ }
+ for _, p1 := range testCoverPkgs {
+ if !seen[p1] {
+ seen[p1] = true
+ pmain.imports = append(pmain.imports, p1)
+ }
+ }
}
- pregexp := loadImport("regexp", "", &stk, nil)
- if pregexp.Error != nil {
- return nil, nil, nil, pregexp.Error
+
+ if ptest != p && localCover {
+ // We have made modifications to the package p being tested
+ // and are rebuilding p (as ptest), writing it to the testDir tree.
+ // Arrange to rebuild, writing to that same tree, all packages q
+ // such that the test depends on q, and q depends on p.
+ // This makes sure that q sees the modifications to p.
+ // Strictly speaking, the rebuild is only necessary if the
+ // modifications to p change its export metadata, but
+ // determining that is a bit tricky, so we rebuild always.
+ //
+ // This will cause extra compilation, so for now we only do it
+ // when testCover is set. The conditions are more general, though,
+ // and we may find that we need to do it always in the future.
+ recompileForTest(pmain, p, ptest, testDir)
}
- pmain.imports = append(pmain.imports, ptesting, pregexp)
+
+ if err := writeTestmain(filepath.Join(testDir, "_testmain.go"), pmain, ptest); err != nil {
+ return nil, nil, nil, err
+ }
+
computeStale(pmain)
if ptest != p {
@@ -589,7 +730,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,
a.target = filepath.Join(testDir, testBinary) + exeSuffix
pmainAction := a
- if testC || testProfile {
+ if testC || testNeedBinary {
// -c or profiling flag: create action to copy binary to ./test.out.
runAction = &action{
f: (*builder).install,
@@ -624,6 +765,75 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,
return pmainAction, runAction, printAction, nil
}
+func recompileForTest(pmain, preal, ptest *Package, testDir string) {
+ // The "test copy" of preal is ptest.
+ // For each package that depends on preal, make a "test copy"
+ // that depends on ptest. And so on, up the dependency tree.
+ testCopy := map[*Package]*Package{preal: ptest}
+ for _, p := range packageList([]*Package{pmain}) {
+ // Copy on write.
+ didSplit := false
+ split := func() {
+ if didSplit {
+ return
+ }
+ didSplit = true
+ if p.pkgdir != testDir {
+ p1 := new(Package)
+ testCopy[p] = p1
+ *p1 = *p
+ p1.imports = make([]*Package, len(p.imports))
+ copy(p1.imports, p.imports)
+ p = p1
+ p.pkgdir = testDir
+ p.target = ""
+ p.fake = true
+ p.Stale = true
+ }
+ }
+
+ // Update p.deps and p.imports to use at test copies.
+ for i, dep := range p.deps {
+ if p1 := testCopy[dep]; p1 != nil && p1 != dep {
+ split()
+ p.deps[i] = p1
+ }
+ }
+ for i, imp := range p.imports {
+ if p1 := testCopy[imp]; p1 != nil && p1 != imp {
+ split()
+ p.imports[i] = p1
+ }
+ }
+ }
+}
+
+var coverIndex = 0
+
+// isTestFile reports whether the source file is a set of tests and should therefore
+// be excluded from coverage analysis.
+func isTestFile(file string) bool {
+ // We don't cover tests, only the code they test.
+ return strings.HasSuffix(file, "_test.go")
+}
+
+// declareCoverVars attaches the required cover variables names
+// to the files, to be used when annotating the files.
+func declareCoverVars(importPath string, files ...string) map[string]*CoverVar {
+ coverVars := make(map[string]*CoverVar)
+ for _, file := range files {
+ if isTestFile(file) {
+ continue
+ }
+ coverVars[file] = &CoverVar{
+ File: filepath.Join(importPath, file),
+ Var: fmt.Sprintf("GoCover_%d", coverIndex),
+ }
+ coverIndex++
+ }
+ return coverVars
+}
+
// runTest is the action for running a test binary.
func (b *builder) runTest(a *action) error {
args := stringList(a.deps[0].target, testArgs)
@@ -688,10 +898,23 @@ func (b *builder) runTest(a *action) error {
go func() {
done <- cmd.Wait()
}()
+ Outer:
select {
case err = <-done:
// ok
case <-tick.C:
+ if signalTrace != nil {
+ // Send a quit signal in the hope that the program will print
+ // a stack trace and exit. Give it five seconds before resorting
+ // to Kill.
+ cmd.Process.Signal(signalTrace)
+ select {
+ case err = <-done:
+ fmt.Fprintf(&buf, "*** Test killed with %v: ran too long (%v).\n", signalTrace, testKillTimeout)
+ break Outer
+ case <-time.After(5 * time.Second):
+ }
+ }
cmd.Process.Kill()
err = <-done
fmt.Fprintf(&buf, "*** Test killed: ran too long (%v).\n", testKillTimeout)
@@ -704,7 +927,7 @@ func (b *builder) runTest(a *action) error {
if testShowPass {
a.testOutput.Write(out)
}
- fmt.Fprintf(a.testOutput, "ok \t%s\t%s\n", a.p.ImportPath, t)
+ fmt.Fprintf(a.testOutput, "ok \t%s\t%s%s\n", a.p.ImportPath, t, coveragePercentage(out))
return nil
}
@@ -720,6 +943,25 @@ func (b *builder) runTest(a *action) error {
return nil
}
+// coveragePercentage returns the coverage results (if enabled) for the
+// test. It uncovers the data by scanning the output from the test run.
+func coveragePercentage(out []byte) string {
+ if !testCover {
+ return ""
+ }
+ // The string looks like
+ // test coverage for encoding/binary: 79.9% of statements
+ // Extract the piece from the percentage to the end of the line.
+ re := regexp.MustCompile(`coverage: (.*)\n`)
+ matches := re.FindSubmatch(out)
+ if matches == nil {
+ // Probably running "go test -cover" not "go test -cover fmt".
+ // The coverage output will appear in the output directly.
+ return ""
+ }
+ return fmt.Sprintf("\tcoverage: %s", matches[1])
+}
+
// cleanTest is the action for cleaning up after a test.
func (b *builder) cleanTest(a *action) error {
if buildWork {
@@ -760,11 +1002,24 @@ func isTest(name, prefix string) bool {
return !unicode.IsLower(rune)
}
+type coverInfo struct {
+ Package *Package
+ Vars map[string]*CoverVar
+}
+
// writeTestmain writes the _testmain.go file for package p to
// the file named out.
-func writeTestmain(out string, p *Package) error {
+func writeTestmain(out string, pmain, p *Package) error {
+ var cover []coverInfo
+ for _, cp := range pmain.imports {
+ if len(cp.coverVars) > 0 {
+ cover = append(cover, coverInfo{cp, cp.coverVars})
+ }
+ }
+
t := &testFuncs{
Package: p,
+ Cover: cover,
}
for _, file := range p.TestGoFiles {
if err := t.load(filepath.Join(p.Dir, file), "_test", &t.NeedTest); err != nil {
@@ -797,6 +1052,31 @@ type testFuncs struct {
Package *Package
NeedTest bool
NeedXtest bool
+ Cover []coverInfo
+}
+
+func (t *testFuncs) CoverMode() string {
+ return testCoverMode
+}
+
+func (t *testFuncs) CoverEnabled() bool {
+ return testCover
+}
+
+// Covered returns a string describing which packages are being tested for coverage.
+// If the covered package is the same as the tested package, it returns the empty string.
+// Otherwise it is a comma-separated human-readable list of packages beginning with
+// " in", ready for use in the coverage message.
+func (t *testFuncs) Covered() string {
+ if testCoverPaths == nil {
+ return ""
+ }
+ return " in " + strings.Join(testCoverPaths, ", ")
+}
+
+// Tested returns the name of the package being tested.
+func (t *testFuncs) Tested() string {
+ return t.Package.Name
}
type testFunc struct {
@@ -862,6 +1142,9 @@ import (
{{if .NeedXtest}}
_xtest {{.Package.ImportPath | printf "%s_test" | printf "%q"}}
{{end}}
+{{range $i, $p := .Cover}}
+ _cover{{$i}} {{$p.Package.ImportPath | printf "%q"}}
+{{end}}
)
var tests = []testing.InternalTest{
@@ -896,7 +1179,54 @@ func matchString(pat, str string) (result bool, err error) {
return matchRe.MatchString(str), nil
}
+{{if .CoverEnabled}}
+
+// Only updated by init functions, so no need for atomicity.
+var (
+ coverCounters = make(map[string][]uint32)
+ coverBlocks = make(map[string][]testing.CoverBlock)
+)
+
+func init() {
+ {{range $i, $p := .Cover}}
+ {{range $file, $cover := $p.Vars}}
+ coverRegisterFile({{printf "%q" $cover.File}}, _cover{{$i}}.{{$cover.Var}}.Count[:], _cover{{$i}}.{{$cover.Var}}.Pos[:], _cover{{$i}}.{{$cover.Var}}.NumStmt[:])
+ {{end}}
+ {{end}}
+}
+
+func coverRegisterFile(fileName string, counter []uint32, pos []uint32, numStmts []uint16) {
+ if 3*len(counter) != len(pos) || len(counter) != len(numStmts) {
+ panic("coverage: mismatched sizes")
+ }
+ if coverCounters[fileName] != nil {
+ // Already registered.
+ return
+ }
+ coverCounters[fileName] = counter
+ block := make([]testing.CoverBlock, len(counter))
+ for i := range counter {
+ block[i] = testing.CoverBlock{
+ Line0: pos[3*i+0],
+ Col0: uint16(pos[3*i+2]),
+ Line1: pos[3*i+1],
+ Col1: uint16(pos[3*i+2]>>16),
+ Stmts: numStmts[i],
+ }
+ }
+ coverBlocks[fileName] = block
+}
+{{end}}
+
func main() {
+{{if .CoverEnabled}}
+ testing.RegisterCover(testing.Cover{
+ Mode: {{printf "%q" .CoverMode}},
+ Counters: coverCounters,
+ Blocks: coverBlocks,
+ CoveredPackages: {{printf "%q" .Covered}},
+ })
+{{end}}
testing.Main(matchString, tests, benchmarks, examples)
}
diff --git a/src/cmd/go/testdata/shadow/root1/src/foo/foo.go b/src/cmd/go/testdata/shadow/root1/src/foo/foo.go
new file mode 100644
index 000000000..f52652b1b
--- /dev/null
+++ b/src/cmd/go/testdata/shadow/root1/src/foo/foo.go
@@ -0,0 +1 @@
+package foo
diff --git a/src/cmd/go/testdata/shadow/root1/src/math/math.go b/src/cmd/go/testdata/shadow/root1/src/math/math.go
new file mode 100644
index 000000000..c91c24e96
--- /dev/null
+++ b/src/cmd/go/testdata/shadow/root1/src/math/math.go
@@ -0,0 +1 @@
+package math
diff --git a/src/cmd/go/testdata/shadow/root2/src/foo/foo.go b/src/cmd/go/testdata/shadow/root2/src/foo/foo.go
new file mode 100644
index 000000000..f52652b1b
--- /dev/null
+++ b/src/cmd/go/testdata/shadow/root2/src/foo/foo.go
@@ -0,0 +1 @@
+package foo
diff --git a/src/cmd/go/testdata/src/badpkg/x.go b/src/cmd/go/testdata/src/badpkg/x.go
new file mode 100644
index 000000000..dda35e8ed
--- /dev/null
+++ b/src/cmd/go/testdata/src/badpkg/x.go
@@ -0,0 +1 @@
+pkg badpkg
diff --git a/src/cmd/go/testdata/src/cgotest/m.go b/src/cmd/go/testdata/src/cgotest/m.go
new file mode 100644
index 000000000..4d68307cf
--- /dev/null
+++ b/src/cmd/go/testdata/src/cgotest/m.go
@@ -0,0 +1,5 @@
+package cgotest
+
+import "C"
+
+var _ C.int
diff --git a/src/cmd/go/testdata/src/main_test/m.go b/src/cmd/go/testdata/src/main_test/m.go
new file mode 100644
index 000000000..c682f030b
--- /dev/null
+++ b/src/cmd/go/testdata/src/main_test/m.go
@@ -0,0 +1,4 @@
+package main
+
+func F() {}
+func main() {}
diff --git a/src/cmd/go/testdata/src/main_test/m_test.go b/src/cmd/go/testdata/src/main_test/m_test.go
new file mode 100644
index 000000000..f865b7734
--- /dev/null
+++ b/src/cmd/go/testdata/src/main_test/m_test.go
@@ -0,0 +1,10 @@
+package main_test
+
+import (
+ . "main_test"
+ "testing"
+)
+
+func Test1(t *testing.T) {
+ F()
+}
diff --git a/src/cmd/go/testdata/src/syntaxerror/x.go b/src/cmd/go/testdata/src/syntaxerror/x.go
new file mode 100644
index 000000000..c89cd18d0
--- /dev/null
+++ b/src/cmd/go/testdata/src/syntaxerror/x.go
@@ -0,0 +1 @@
+package p
diff --git a/src/cmd/go/testdata/src/syntaxerror/x_test.go b/src/cmd/go/testdata/src/syntaxerror/x_test.go
new file mode 100644
index 000000000..2460743e5
--- /dev/null
+++ b/src/cmd/go/testdata/src/syntaxerror/x_test.go
@@ -0,0 +1,4 @@
+package p
+
+func f() (x.y, z int) {
+}
diff --git a/src/cmd/go/testflag.go b/src/cmd/go/testflag.go
index b2ca66b09..aea81d8f8 100644
--- a/src/cmd/go/testflag.go
+++ b/src/cmd/go/testflag.go
@@ -27,12 +27,17 @@ var usageMessage = `Usage of go test:
-bench="": passes -test.bench to test
-benchmem=false: print memory allocation statistics for benchmarks
-benchtime=1s: passes -test.benchtime to test
+ -cover=false: enable coverage analysis
+ -covermode="set": specifies mode for coverage analysis
+ -coverpkg="": comma-separated list of packages for coverage analysis
+ -coverprofile="": passes -test.coverprofile to test if -cover
-cpu="": passes -test.cpu to test
-cpuprofile="": passes -test.cpuprofile to test
-memprofile="": passes -test.memprofile to test
-memprofilerate=0: passes -test.memprofilerate to test
-blockprofile="": pases -test.blockprofile to test
-blockprofilerate=0: passes -test.blockprofilerate to test
+ -outputdir=$PWD: passes -test.outputdir to test
-parallel=0: passes -test.parallel to test
-run="": passes -test.run to test
-short=false: passes -test.short to test
@@ -62,6 +67,8 @@ var testFlagDefn = []*testFlagSpec{
{name: "c", boolVar: &testC},
{name: "file", multiOK: true},
{name: "i", boolVar: &testI},
+ {name: "cover", boolVar: &testCover},
+ {name: "coverpkg"},
// build flags.
{name: "a", boolVar: &buildA},
@@ -75,17 +82,21 @@ var testFlagDefn = []*testFlagSpec{
{name: "tags"},
{name: "compiler"},
{name: "race", boolVar: &buildRace},
+ {name: "installsuffix"},
// passed to 6.out, adding a "test." prefix to the name if necessary: -v becomes -test.v.
{name: "bench", passToTest: true},
{name: "benchmem", boolVar: new(bool), passToTest: true},
{name: "benchtime", passToTest: true},
+ {name: "covermode"},
+ {name: "coverprofile", passToTest: true},
{name: "cpu", passToTest: true},
{name: "cpuprofile", passToTest: true},
{name: "memprofile", passToTest: true},
{name: "memprofilerate", passToTest: true},
{name: "blockprofile", passToTest: true},
{name: "blockprofilerate", passToTest: true},
+ {name: "outputdir", passToTest: true},
{name: "parallel", passToTest: true},
{name: "run", passToTest: true},
{name: "short", boolVar: new(bool), passToTest: true},
@@ -104,6 +115,8 @@ var testFlagDefn = []*testFlagSpec{
// go test -x math
func testFlags(args []string) (packageNames, passToTest []string) {
inPkg := false
+ outputDir := ""
+ testCoverMode = "set"
for i := 0; i < len(args); i++ {
if !strings.HasPrefix(args[i], "-") {
if !inPkg && packageNames == nil {
@@ -137,7 +150,7 @@ func testFlags(args []string) (packageNames, passToTest []string) {
var err error
switch f.name {
// bool flags.
- case "a", "c", "i", "n", "x", "v", "work", "race":
+ case "a", "c", "i", "n", "x", "v", "race", "cover", "work":
setBoolFlag(f.boolVar, value)
case "p":
setIntFlag(&buildP, value)
@@ -169,6 +182,27 @@ func testFlags(args []string) (packageNames, passToTest []string) {
testTimeout = value
case "blockprofile", "cpuprofile", "memprofile":
testProfile = true
+ testNeedBinary = true
+ case "coverpkg":
+ testCover = true
+ if value == "" {
+ testCoverPaths = nil
+ } else {
+ testCoverPaths = strings.Split(value, ",")
+ }
+ case "coverprofile":
+ testCover = true
+ testProfile = true
+ case "covermode":
+ switch value {
+ case "set", "count", "atomic":
+ testCoverMode = value
+ default:
+ fatalf("invalid flag argument for -cover: %q", value)
+ }
+ testCover = true
+ case "outputdir":
+ outputDir = value
}
if extraWord {
i++
@@ -177,6 +211,15 @@ func testFlags(args []string) (packageNames, passToTest []string) {
passToTest = append(passToTest, "-test."+f.name+"="+value)
}
}
+
+ // Tell the test what directory we're running in, so it can write the profiles there.
+ if testProfile && outputDir == "" {
+ dir, err := os.Getwd()
+ if err != nil {
+ fatalf("error from os.Getwd: %s", err)
+ }
+ passToTest = append(passToTest, "-test.outputdir", dir)
+ }
return
}
@@ -215,13 +258,13 @@ func testFlag(args []string, i int) (f *testFlagSpec, value string, extra bool)
extra = equals < 0
if extra {
if i+1 >= len(args) {
- usage()
+ testSyntaxError("missing argument for flag " + f.name)
}
value = args[i+1]
}
}
if f.present && !f.multiOK {
- usage()
+ testSyntaxError(f.name + " flag may be set only once")
}
f.present = true
return
@@ -235,8 +278,7 @@ func testFlag(args []string, i int) (f *testFlagSpec, value string, extra bool)
func setBoolFlag(flag *bool, value string) {
x, err := strconv.ParseBool(value)
if err != nil {
- fmt.Fprintf(os.Stderr, "go test: illegal bool flag value %s\n", value)
- usage()
+ testSyntaxError("illegal bool flag value " + value)
}
*flag = x
}
@@ -245,8 +287,13 @@ func setBoolFlag(flag *bool, value string) {
func setIntFlag(flag *int, value string) {
x, err := strconv.Atoi(value)
if err != nil {
- fmt.Fprintf(os.Stderr, "go test: illegal int flag value %s\n", value)
- usage()
+ testSyntaxError("illegal int flag value " + value)
}
*flag = x
}
+
+func testSyntaxError(msg string) {
+ fmt.Fprintf(os.Stderr, "go test: %s\n", msg)
+ fmt.Fprintf(os.Stderr, `run "go help test" or "go help testflag" for more information`+"\n")
+ os.Exit(2)
+}
diff --git a/src/cmd/go/tool.go b/src/cmd/go/tool.go
index 299b94cb3..6d26f7a4b 100644
--- a/src/cmd/go/tool.go
+++ b/src/cmd/go/tool.go
@@ -45,12 +45,30 @@ func init() {
const toolWindowsExtension = ".exe"
-func tool(name string) string {
- p := filepath.Join(toolDir, name)
- if toolIsWindows && name != "pprof" {
- p += toolWindowsExtension
+func tool(toolName string) string {
+ toolPath := filepath.Join(toolDir, toolName)
+ if toolIsWindows && toolName != "pprof" {
+ toolPath += toolWindowsExtension
}
- return p
+ // Give a nice message if there is no tool with that name.
+ if _, err := os.Stat(toolPath); err != nil {
+ if isInGoToolsRepo(toolName) {
+ fmt.Fprintf(os.Stderr, "go tool: no such tool %q; to install:\n\tgo get code.google.com/p/go.tools/cmd/%s\n", toolName, toolName)
+ } else {
+ fmt.Fprintf(os.Stderr, "go tool: no such tool %q\n", toolName)
+ }
+ setExitStatus(3)
+ exit()
+ }
+ return toolPath
+}
+
+func isInGoToolsRepo(toolName string) bool {
+ switch toolName {
+ case "cover", "vet":
+ return true
+ }
+ return false
}
func runTool(cmd *Command, args []string) {
@@ -70,10 +88,7 @@ func runTool(cmd *Command, args []string) {
}
}
toolPath := tool(toolName)
- // Give a nice message if there is no tool with that name.
- if _, err := os.Stat(toolPath); err != nil {
- fmt.Fprintf(os.Stderr, "go tool: no such tool %q\n", toolName)
- setExitStatus(3)
+ if toolPath == "" {
return
}
if toolIsWindows && toolName == "pprof" {
@@ -86,7 +101,6 @@ func runTool(cmd *Command, args []string) {
return
}
}
-
if toolN {
fmt.Printf("%s %s\n", toolPath, strings.Join(args[1:], " "))
return
diff --git a/src/cmd/go/vcs.go b/src/cmd/go/vcs.go
index 39881a6dc..22d5ebc24 100644
--- a/src/cmd/go/vcs.go
+++ b/src/cmd/go/vcs.go
@@ -91,7 +91,7 @@ var vcsGit = &vcsCmd{
cmd: "git",
createCmd: "clone {repo} {dir}",
- downloadCmd: "fetch",
+ downloadCmd: "pull --ff-only",
tagCmd: []tagCmd{
// tags/xxx matches a git tag named xxx
@@ -102,7 +102,7 @@ var vcsGit = &vcsCmd{
{"show-ref tags/{tag} origin/{tag}", `((?:tags|origin)/\S+)$`},
},
tagSyncCmd: "checkout {tag}",
- tagSyncDefault: "checkout origin/master",
+ tagSyncDefault: "checkout master",
scheme: []string{"git", "https", "http", "git+ssh"},
pingCmd: "ls-remote {scheme}://{repo}",
@@ -223,9 +223,36 @@ func (v *vcsCmd) create(dir, repo string) error {
// download downloads any new changes for the repo in dir.
func (v *vcsCmd) download(dir string) error {
+ if err := v.fixDetachedHead(dir); err != nil {
+ return err
+ }
return v.run(dir, v.downloadCmd)
}
+// fixDetachedHead switches a Git repository in dir from a detached head to the master branch.
+// Go versions before 1.2 downloaded Git repositories in an unfortunate way
+// that resulted in the working tree state being on a detached head.
+// That meant the repository was not usable for normal Git operations.
+// Go 1.2 fixed that, but we can't pull into a detached head, so if this is
+// a Git repository we check for being on a detached head and switch to the
+// real branch, almost always called "master".
+// TODO(dsymonds): Consider removing this for Go 1.3.
+func (v *vcsCmd) fixDetachedHead(dir string) error {
+ if v != vcsGit {
+ return nil
+ }
+
+ // "git symbolic-ref HEAD" succeeds iff we are not on a detached head.
+ if err := v.runVerboseOnly(dir, "symbolic-ref HEAD"); err == nil {
+ // not on a detached head
+ return nil
+ }
+ if buildV {
+ log.Printf("%s on detached head; repairing", dir)
+ }
+ return v.run(dir, "checkout master")
+}
+
// tags returns the list of available tags for the repo in dir.
func (v *vcsCmd) tags(dir string) ([]string, error) {
var tags []string
@@ -294,6 +321,7 @@ func vcsForDir(p *Package) (vcs *vcsCmd, root string, err error) {
return nil, "", fmt.Errorf("directory %q is outside source root %q", dir, srcRoot)
}
+ origDir := dir
for len(dir) > len(srcRoot) {
for _, vcs := range vcsList {
if fi, err := os.Stat(filepath.Join(dir, "."+vcs.cmd)); err == nil && fi.IsDir() {
@@ -310,7 +338,7 @@ func vcsForDir(p *Package) (vcs *vcsCmd, root string, err error) {
dir = ndir
}
- return nil, "", fmt.Errorf("directory %q is not using a known version control system", dir)
+ return nil, "", fmt.Errorf("directory %q is not using a known version control system", origDir)
}
// repoRoot represents a version control system, a repo, and a root of
@@ -442,7 +470,11 @@ func repoRootForImportDynamic(importPath string) (*repoRoot, error) {
return nil, fmt.Errorf("http/https fetch: %v", err)
}
defer body.Close()
- metaImport, err := matchGoImport(parseMetaGoImports(body), importPath)
+ imports, err := parseMetaGoImports(body)
+ if err != nil {
+ return nil, fmt.Errorf("parsing %s: %v", importPath, err)
+ }
+ metaImport, err := matchGoImport(imports, importPath)
if err != nil {
if err != errNoMatch {
return nil, fmt.Errorf("parse %s: %v", urlStr, err)
@@ -467,7 +499,10 @@ func repoRootForImportDynamic(importPath string) (*repoRoot, error) {
if err != nil {
return nil, fmt.Errorf("fetch %s: %v", urlStr, err)
}
- imports := parseMetaGoImports(body)
+ imports, err := parseMetaGoImports(body)
+ if err != nil {
+ return nil, fmt.Errorf("parsing %s: %v", importPath, err)
+ }
if len(imports) == 0 {
return nil, fmt.Errorf("fetch %s: no go-import meta tag", urlStr)
}
diff --git a/src/cmd/go/vet.go b/src/cmd/go/vet.go
index 503e16362..ffb431837 100644
--- a/src/cmd/go/vet.go
+++ b/src/cmd/go/vet.go
@@ -15,7 +15,7 @@ var cmdVet = &Command{
Long: `
Vet runs the Go vet command on the packages named by the import paths.
-For more about vet, see 'godoc vet'.
+For more about vet, see 'godoc code.google.com/p/go.tools/cmd/vet'.
For more about specifying packages, see 'go help packages'.
To run the vet tool with specific options, run 'go tool vet'.
diff --git a/src/cmd/godoc/README.godoc-app b/src/cmd/godoc/README.godoc-app
deleted file mode 100644
index cff7d387c..000000000
--- a/src/cmd/godoc/README.godoc-app
+++ /dev/null
@@ -1,61 +0,0 @@
-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.
-
-godoc on appengine
-------------------
-
-Prerequisites
--------------
-
-* Go appengine SDK
- https://developers.google.com/appengine/downloads#Google_App_Engine_SDK_for_Go
-
-* Go sources at tip under $GOROOT
-
-
-Directory structure
--------------------
-
-* Let $APPDIR be the directory containing the app engine files.
- (e.g., $APPDIR=$HOME/godoc-app)
-
-* $APPDIR contains the following entries (this may change depending on
- app-engine release and version of godoc):
-
- app.yaml
- godoc.zip
- godoc/
- index.split.*
-
-* The app.yaml file is set up per app engine documentation.
- For instance:
-
- application: godoc-app
- version: 1
- runtime: go
- api_version: go1
-
- handlers:
- - url: /.*
- script: _go_app
-
-* The godoc/ directory contains a copy of the files under $GOROOT/src/cmd/godoc
- with doc.go excluded (it belongs to pseudo-package "documentation")
-
-
-Configuring and running godoc
------------------------------
-
-To configure godoc, run
-
- bash setup-godoc-app.bash
-
-to create the godoc.zip, index.split.*, and godoc/appconfig.go files
-based on $GOROOT and $APPDIR. See the script for details on usage.
-
-To run godoc locally, using the app-engine emulator, run
-
- <path to google_appengine>/dev_appserver.py $APPDIR
-
-godoc should come up at http://localhost:8080 .
diff --git a/src/cmd/godoc/appinit.go b/src/cmd/godoc/appinit.go
deleted file mode 100644
index 996b2b850..000000000
--- a/src/cmd/godoc/appinit.go
+++ /dev/null
@@ -1,69 +0,0 @@
-// 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.
-
-// +build appengine
-
-package main
-
-// This file replaces main.go when running godoc under app-engine.
-// See README.godoc-app for details.
-
-import (
- "archive/zip"
- "log"
- "net/http"
- "path"
-)
-
-func serveError(w http.ResponseWriter, r *http.Request, relpath string, err error) {
- w.WriteHeader(http.StatusNotFound)
- servePage(w, Page{
- Title: "File " + relpath,
- Subtitle: relpath,
- Body: applyTemplate(errorHTML, "errorHTML", err), // err may contain an absolute path!
- })
-}
-
-func init() {
- log.Println("initializing godoc ...")
- log.Printf(".zip file = %s", zipFilename)
- log.Printf(".zip GOROOT = %s", zipGoroot)
- log.Printf("index files = %s", indexFilenames)
-
- // initialize flags for app engine
- *goroot = path.Join("/", zipGoroot) // fsHttp paths are relative to '/'
- *indexEnabled = true
- *indexFiles = indexFilenames
- *maxResults = 100 // reduce latency by limiting the number of fulltext search results
- *indexThrottle = 0.3 // in case *indexFiles is empty (and thus the indexer is run)
- *showPlayground = true
-
- // read .zip file and set up file systems
- const zipfile = zipFilename
- rc, err := zip.OpenReader(zipfile)
- if err != nil {
- log.Fatalf("%s: %s\n", zipfile, err)
- }
- // rc is never closed (app running forever)
- fs.Bind("/", NewZipFS(rc, zipFilename), *goroot, bindReplace)
-
- // initialize http handlers
- readTemplates()
- initHandlers()
- registerPublicHandlers(http.DefaultServeMux)
- registerPlaygroundHandlers(http.DefaultServeMux)
-
- // initialize default directory tree with corresponding timestamp.
- initFSTree()
-
- // Immediately update metadata.
- updateMetadata()
-
- // initialize search index
- if *indexEnabled {
- go indexer()
- }
-
- log.Println("godoc initialization complete")
-}
diff --git a/src/cmd/godoc/codewalk.go b/src/cmd/godoc/codewalk.go
deleted file mode 100644
index e68c0fa6b..000000000
--- a/src/cmd/godoc/codewalk.go
+++ /dev/null
@@ -1,494 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// The /doc/codewalk/ tree is synthesized from codewalk descriptions,
-// files named $GOROOT/doc/codewalk/*.xml.
-// For an example and a description of the format, see
-// http://golang.org/doc/codewalk/codewalk or run godoc -http=:6060
-// and see http://localhost:6060/doc/codewalk/codewalk .
-// That page is itself a codewalk; the source code for it is
-// $GOROOT/doc/codewalk/codewalk.xml.
-
-package main
-
-import (
- "encoding/xml"
- "errors"
- "fmt"
- "io"
- "log"
- "net/http"
- "os"
- "regexp"
- "sort"
- "strconv"
- "strings"
- "text/template"
- "unicode/utf8"
-)
-
-// Handler for /doc/codewalk/ and below.
-func codewalk(w http.ResponseWriter, r *http.Request) {
- relpath := r.URL.Path[len("/doc/codewalk/"):]
- abspath := r.URL.Path
-
- r.ParseForm()
- if f := r.FormValue("fileprint"); f != "" {
- codewalkFileprint(w, r, f)
- return
- }
-
- // If directory exists, serve list of code walks.
- dir, err := fs.Lstat(abspath)
- if err == nil && dir.IsDir() {
- codewalkDir(w, r, relpath, abspath)
- return
- }
-
- // If file exists, serve using standard file server.
- if err == nil {
- serveFile(w, r)
- return
- }
-
- // Otherwise append .xml and hope to find
- // a codewalk description, but before trim
- // the trailing /.
- abspath = strings.TrimRight(abspath, "/")
- cw, err := loadCodewalk(abspath + ".xml")
- if err != nil {
- log.Print(err)
- serveError(w, r, relpath, err)
- return
- }
-
- // Canonicalize the path and redirect if changed
- if redirect(w, r) {
- return
- }
-
- servePage(w, Page{
- Title: "Codewalk: " + cw.Title,
- Tabtitle: cw.Title,
- Body: applyTemplate(codewalkHTML, "codewalk", cw),
- })
-}
-
-// A Codewalk represents a single codewalk read from an XML file.
-type Codewalk struct {
- Title string `xml:"title,attr"`
- File []string `xml:"file"`
- Step []*Codestep `xml:"step"`
-}
-
-// A Codestep is a single step in a codewalk.
-type Codestep struct {
- // Filled in from XML
- Src string `xml:"src,attr"`
- Title string `xml:"title,attr"`
- XML string `xml:",innerxml"`
-
- // Derived from Src; not in XML.
- Err error
- File string
- Lo int
- LoByte int
- Hi int
- HiByte int
- Data []byte
-}
-
-// String method for printing in template.
-// Formats file address nicely.
-func (st *Codestep) String() string {
- s := st.File
- if st.Lo != 0 || st.Hi != 0 {
- s += fmt.Sprintf(":%d", st.Lo)
- if st.Lo != st.Hi {
- s += fmt.Sprintf(",%d", st.Hi)
- }
- }
- return s
-}
-
-// loadCodewalk reads a codewalk from the named XML file.
-func loadCodewalk(filename string) (*Codewalk, error) {
- f, err := fs.Open(filename)
- if err != nil {
- return nil, err
- }
- defer f.Close()
- cw := new(Codewalk)
- d := xml.NewDecoder(f)
- d.Entity = xml.HTMLEntity
- err = d.Decode(cw)
- if err != nil {
- return nil, &os.PathError{Op: "parsing", Path: filename, Err: err}
- }
-
- // Compute file list, evaluate line numbers for addresses.
- m := make(map[string]bool)
- for _, st := range cw.Step {
- i := strings.Index(st.Src, ":")
- if i < 0 {
- i = len(st.Src)
- }
- filename := st.Src[0:i]
- data, err := ReadFile(fs, filename)
- if err != nil {
- st.Err = err
- continue
- }
- if i < len(st.Src) {
- lo, hi, err := addrToByteRange(st.Src[i+1:], 0, data)
- if err != nil {
- st.Err = err
- continue
- }
- // Expand match to line boundaries.
- for lo > 0 && data[lo-1] != '\n' {
- lo--
- }
- for hi < len(data) && (hi == 0 || data[hi-1] != '\n') {
- hi++
- }
- st.Lo = byteToLine(data, lo)
- st.Hi = byteToLine(data, hi-1)
- }
- st.Data = data
- st.File = filename
- m[filename] = true
- }
-
- // Make list of files
- cw.File = make([]string, len(m))
- i := 0
- for f := range m {
- cw.File[i] = f
- i++
- }
- sort.Strings(cw.File)
-
- return cw, nil
-}
-
-// codewalkDir serves the codewalk directory listing.
-// It scans the directory for subdirectories or files named *.xml
-// and prepares a table.
-func codewalkDir(w http.ResponseWriter, r *http.Request, relpath, abspath string) {
- type elem struct {
- Name string
- Title string
- }
-
- dir, err := fs.ReadDir(abspath)
- if err != nil {
- log.Print(err)
- serveError(w, r, relpath, err)
- return
- }
- var v []interface{}
- for _, fi := range dir {
- name := fi.Name()
- if fi.IsDir() {
- v = append(v, &elem{name + "/", ""})
- } else if strings.HasSuffix(name, ".xml") {
- cw, err := loadCodewalk(abspath + "/" + name)
- if err != nil {
- continue
- }
- v = append(v, &elem{name[0 : len(name)-len(".xml")], cw.Title})
- }
- }
-
- servePage(w, Page{
- Title: "Codewalks",
- Body: applyTemplate(codewalkdirHTML, "codewalkdir", v),
- })
-}
-
-// codewalkFileprint serves requests with ?fileprint=f&lo=lo&hi=hi.
-// The filename f has already been retrieved and is passed as an argument.
-// Lo and hi are the numbers of the first and last line to highlight
-// in the response. This format is used for the middle window pane
-// of the codewalk pages. It is a separate iframe and does not get
-// the usual godoc HTML wrapper.
-func codewalkFileprint(w http.ResponseWriter, r *http.Request, f string) {
- abspath := f
- data, err := ReadFile(fs, abspath)
- if err != nil {
- log.Print(err)
- serveError(w, r, f, err)
- return
- }
- lo, _ := strconv.Atoi(r.FormValue("lo"))
- hi, _ := strconv.Atoi(r.FormValue("hi"))
- if hi < lo {
- hi = lo
- }
- lo = lineToByte(data, lo)
- hi = lineToByte(data, hi+1)
-
- // Put the mark 4 lines before lo, so that the iframe
- // shows a few lines of context before the highlighted
- // section.
- n := 4
- mark := lo
- for ; mark > 0 && n > 0; mark-- {
- if data[mark-1] == '\n' {
- if n--; n == 0 {
- break
- }
- }
- }
-
- io.WriteString(w, `<style type="text/css">@import "/doc/codewalk/codewalk.css";</style><pre>`)
- template.HTMLEscape(w, data[0:mark])
- io.WriteString(w, "<a name='mark'></a>")
- template.HTMLEscape(w, data[mark:lo])
- if lo < hi {
- io.WriteString(w, "<div class='codewalkhighlight'>")
- template.HTMLEscape(w, data[lo:hi])
- io.WriteString(w, "</div>")
- }
- template.HTMLEscape(w, data[hi:])
- io.WriteString(w, "</pre>")
-}
-
-// addrToByte evaluates the given address starting at offset start in data.
-// It returns the lo and hi byte offset of the matched region within data.
-// See http://plan9.bell-labs.com/sys/doc/sam/sam.html Table II
-// for details on the syntax.
-func addrToByteRange(addr string, start int, data []byte) (lo, hi int, err error) {
- var (
- dir byte
- prevc byte
- charOffset bool
- )
- lo = start
- hi = start
- for addr != "" && err == nil {
- c := addr[0]
- switch c {
- default:
- err = errors.New("invalid address syntax near " + string(c))
- case ',':
- if len(addr) == 1 {
- hi = len(data)
- } else {
- _, hi, err = addrToByteRange(addr[1:], hi, data)
- }
- return
-
- case '+', '-':
- if prevc == '+' || prevc == '-' {
- lo, hi, err = addrNumber(data, lo, hi, prevc, 1, charOffset)
- }
- dir = c
-
- case '$':
- lo = len(data)
- hi = len(data)
- if len(addr) > 1 {
- dir = '+'
- }
-
- case '#':
- charOffset = true
-
- case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
- var i int
- for i = 1; i < len(addr); i++ {
- if addr[i] < '0' || addr[i] > '9' {
- break
- }
- }
- var n int
- n, err = strconv.Atoi(addr[0:i])
- if err != nil {
- break
- }
- lo, hi, err = addrNumber(data, lo, hi, dir, n, charOffset)
- dir = 0
- charOffset = false
- prevc = c
- addr = addr[i:]
- continue
-
- case '/':
- var i, j int
- Regexp:
- for i = 1; i < len(addr); i++ {
- switch addr[i] {
- case '\\':
- i++
- case '/':
- j = i + 1
- break Regexp
- }
- }
- if j == 0 {
- j = i
- }
- pattern := addr[1:i]
- lo, hi, err = addrRegexp(data, lo, hi, dir, pattern)
- prevc = c
- addr = addr[j:]
- continue
- }
- prevc = c
- addr = addr[1:]
- }
-
- if err == nil && dir != 0 {
- lo, hi, err = addrNumber(data, lo, hi, dir, 1, charOffset)
- }
- if err != nil {
- return 0, 0, err
- }
- return lo, hi, nil
-}
-
-// addrNumber applies the given dir, n, and charOffset to the address lo, hi.
-// dir is '+' or '-', n is the count, and charOffset is true if the syntax
-// used was #n. Applying +n (or +#n) means to advance n lines
-// (or characters) after hi. Applying -n (or -#n) means to back up n lines
-// (or characters) before lo.
-// The return value is the new lo, hi.
-func addrNumber(data []byte, lo, hi int, dir byte, n int, charOffset bool) (int, int, error) {
- switch dir {
- case 0:
- lo = 0
- hi = 0
- fallthrough
-
- case '+':
- if charOffset {
- pos := hi
- for ; n > 0 && pos < len(data); n-- {
- _, size := utf8.DecodeRune(data[pos:])
- pos += size
- }
- if n == 0 {
- return pos, pos, nil
- }
- break
- }
- // find next beginning of line
- if hi > 0 {
- for hi < len(data) && data[hi-1] != '\n' {
- hi++
- }
- }
- lo = hi
- if n == 0 {
- return lo, hi, nil
- }
- for ; hi < len(data); hi++ {
- if data[hi] != '\n' {
- continue
- }
- switch n--; n {
- case 1:
- lo = hi + 1
- case 0:
- return lo, hi + 1, nil
- }
- }
-
- case '-':
- if charOffset {
- // Scan backward for bytes that are not UTF-8 continuation bytes.
- pos := lo
- for ; pos > 0 && n > 0; pos-- {
- if data[pos]&0xc0 != 0x80 {
- n--
- }
- }
- if n == 0 {
- return pos, pos, nil
- }
- break
- }
- // find earlier beginning of line
- for lo > 0 && data[lo-1] != '\n' {
- lo--
- }
- hi = lo
- if n == 0 {
- return lo, hi, nil
- }
- for ; lo >= 0; lo-- {
- if lo > 0 && data[lo-1] != '\n' {
- continue
- }
- switch n--; n {
- case 1:
- hi = lo
- case 0:
- return lo, hi, nil
- }
- }
- }
-
- return 0, 0, errors.New("address out of range")
-}
-
-// addrRegexp searches for pattern in the given direction starting at lo, hi.
-// The direction dir is '+' (search forward from hi) or '-' (search backward from lo).
-// Backward searches are unimplemented.
-func addrRegexp(data []byte, lo, hi int, dir byte, pattern string) (int, int, error) {
- re, err := regexp.Compile(pattern)
- if err != nil {
- return 0, 0, err
- }
- if dir == '-' {
- // Could implement reverse search using binary search
- // through file, but that seems like overkill.
- return 0, 0, errors.New("reverse search not implemented")
- }
- m := re.FindIndex(data[hi:])
- if len(m) > 0 {
- m[0] += hi
- m[1] += hi
- } else if hi > 0 {
- // No match. Wrap to beginning of data.
- m = re.FindIndex(data)
- }
- if len(m) == 0 {
- return 0, 0, errors.New("no match for " + pattern)
- }
- return m[0], m[1], nil
-}
-
-// lineToByte returns the byte index of the first byte of line n.
-// Line numbers begin at 1.
-func lineToByte(data []byte, n int) int {
- if n <= 1 {
- return 0
- }
- n--
- for i, c := range data {
- if c == '\n' {
- if n--; n == 0 {
- return i + 1
- }
- }
- }
- return len(data)
-}
-
-// byteToLine returns the number of the line containing the byte at index i.
-func byteToLine(data []byte, i int) int {
- l := 1
- for j, c := range data {
- if j == i {
- return l
- }
- if c == '\n' {
- l++
- }
- }
- return l
-}
diff --git a/src/cmd/godoc/dirtrees.go b/src/cmd/godoc/dirtrees.go
deleted file mode 100644
index fda7adce5..000000000
--- a/src/cmd/godoc/dirtrees.go
+++ /dev/null
@@ -1,320 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This file contains the code dealing with package directory trees.
-
-package main
-
-import (
- "bytes"
- "go/doc"
- "go/parser"
- "go/token"
- "log"
- "os"
- pathpkg "path"
- "strings"
-)
-
-// Conventional name for directories containing test data.
-// Excluded from directory trees.
-//
-const testdataDirName = "testdata"
-
-type Directory struct {
- Depth int
- Path string // directory path; includes Name
- Name string // directory name
- HasPkg bool // true if the directory contains at least one package
- Synopsis string // package documentation, if any
- Dirs []*Directory // subdirectories
-}
-
-func isGoFile(fi os.FileInfo) bool {
- name := fi.Name()
- return !fi.IsDir() &&
- len(name) > 0 && name[0] != '.' && // ignore .files
- pathpkg.Ext(name) == ".go"
-}
-
-func isPkgFile(fi os.FileInfo) bool {
- return isGoFile(fi) &&
- !strings.HasSuffix(fi.Name(), "_test.go") // ignore test files
-}
-
-func isPkgDir(fi os.FileInfo) bool {
- name := fi.Name()
- return fi.IsDir() && len(name) > 0 &&
- name[0] != '_' && name[0] != '.' // ignore _files and .files
-}
-
-type treeBuilder struct {
- maxDepth int
-}
-
-func (b *treeBuilder) newDirTree(fset *token.FileSet, path, name string, depth int) *Directory {
- if name == testdataDirName {
- return nil
- }
-
- if depth >= b.maxDepth {
- // return a dummy directory so that the parent directory
- // doesn't get discarded just because we reached the max
- // directory depth
- return &Directory{
- Depth: depth,
- Path: path,
- Name: name,
- }
- }
-
- list, _ := fs.ReadDir(path)
-
- // determine number of subdirectories and if there are package files
- ndirs := 0
- hasPkgFiles := false
- var synopses [3]string // prioritized package documentation (0 == highest priority)
- for _, d := range list {
- switch {
- case isPkgDir(d):
- ndirs++
- case isPkgFile(d):
- // looks like a package file, but may just be a file ending in ".go";
- // don't just count it yet (otherwise we may end up with hasPkgFiles even
- // though the directory doesn't contain any real package files - was bug)
- if synopses[0] == "" {
- // no "optimal" package synopsis yet; continue to collect synopses
- file, err := parseFile(fset, pathpkg.Join(path, d.Name()),
- parser.ParseComments|parser.PackageClauseOnly)
- if err == nil {
- hasPkgFiles = true
- if file.Doc != nil {
- // prioritize documentation
- i := -1
- switch file.Name.Name {
- case name:
- i = 0 // normal case: directory name matches package name
- case "main":
- i = 1 // directory contains a main package
- default:
- i = 2 // none of the above
- }
- if 0 <= i && i < len(synopses) && synopses[i] == "" {
- synopses[i] = doc.Synopsis(file.Doc.Text())
- }
- }
- }
- }
- }
- }
-
- // create subdirectory tree
- var dirs []*Directory
- if ndirs > 0 {
- dirs = make([]*Directory, ndirs)
- i := 0
- for _, d := range list {
- if isPkgDir(d) {
- name := d.Name()
- dd := b.newDirTree(fset, pathpkg.Join(path, name), name, depth+1)
- if dd != nil {
- dirs[i] = dd
- i++
- }
- }
- }
- dirs = dirs[0:i]
- }
-
- // if there are no package files and no subdirectories
- // containing package files, ignore the directory
- if !hasPkgFiles && len(dirs) == 0 {
- return nil
- }
-
- // select the highest-priority synopsis for the directory entry, if any
- synopsis := ""
- for _, synopsis = range synopses {
- if synopsis != "" {
- break
- }
- }
-
- return &Directory{
- Depth: depth,
- Path: path,
- Name: name,
- HasPkg: hasPkgFiles,
- Synopsis: synopsis,
- Dirs: dirs,
- }
-}
-
-// newDirectory creates a new package directory tree with at most maxDepth
-// levels, anchored at root. The result tree is pruned such that it only
-// contains directories that contain package files or that contain
-// subdirectories containing package files (transitively). If a non-nil
-// pathFilter is provided, directory paths additionally must be accepted
-// by the filter (i.e., pathFilter(path) must be true). If a value >= 0 is
-// provided for maxDepth, nodes at larger depths are pruned as well; they
-// are assumed to contain package files even if their contents are not known
-// (i.e., in this case the tree may contain directories w/o any package files).
-//
-func newDirectory(root string, maxDepth int) *Directory {
- // The root could be a symbolic link so use Stat not Lstat.
- d, err := fs.Stat(root)
- // If we fail here, report detailed error messages; otherwise
- // is is hard to see why a directory tree was not built.
- switch {
- case err != nil:
- log.Printf("newDirectory(%s): %s", root, err)
- return nil
- case !isPkgDir(d):
- log.Printf("newDirectory(%s): not a package directory", root)
- return nil
- }
- if maxDepth < 0 {
- maxDepth = 1e6 // "infinity"
- }
- b := treeBuilder{maxDepth}
- // the file set provided is only for local parsing, no position
- // information escapes and thus we don't need to save the set
- return b.newDirTree(token.NewFileSet(), root, d.Name(), 0)
-}
-
-func (dir *Directory) writeLeafs(buf *bytes.Buffer) {
- if dir != nil {
- if len(dir.Dirs) == 0 {
- buf.WriteString(dir.Path)
- buf.WriteByte('\n')
- return
- }
-
- for _, d := range dir.Dirs {
- d.writeLeafs(buf)
- }
- }
-}
-
-func (dir *Directory) walk(c chan<- *Directory, skipRoot bool) {
- if dir != nil {
- if !skipRoot {
- c <- dir
- }
- for _, d := range dir.Dirs {
- d.walk(c, false)
- }
- }
-}
-
-func (dir *Directory) iter(skipRoot bool) <-chan *Directory {
- c := make(chan *Directory)
- go func() {
- dir.walk(c, skipRoot)
- close(c)
- }()
- return c
-}
-
-func (dir *Directory) lookupLocal(name string) *Directory {
- for _, d := range dir.Dirs {
- if d.Name == name {
- return d
- }
- }
- return nil
-}
-
-func splitPath(p string) []string {
- p = strings.TrimPrefix(p, "/")
- if p == "" {
- return nil
- }
- return strings.Split(p, "/")
-}
-
-// lookup looks for the *Directory for a given path, relative to dir.
-func (dir *Directory) lookup(path string) *Directory {
- d := splitPath(dir.Path)
- p := splitPath(path)
- i := 0
- for i < len(d) {
- if i >= len(p) || d[i] != p[i] {
- return nil
- }
- i++
- }
- for dir != nil && i < len(p) {
- dir = dir.lookupLocal(p[i])
- i++
- }
- return dir
-}
-
-// DirEntry describes a directory entry. The Depth and Height values
-// are useful for presenting an entry in an indented fashion.
-//
-type DirEntry struct {
- Depth int // >= 0
- Height int // = DirList.MaxHeight - Depth, > 0
- Path string // directory path; includes Name, relative to DirList root
- Name string // directory name
- HasPkg bool // true if the directory contains at least one package
- Synopsis string // package documentation, if any
-}
-
-type DirList struct {
- MaxHeight int // directory tree height, > 0
- List []DirEntry
-}
-
-// listing creates a (linear) directory listing from a directory tree.
-// If skipRoot is set, the root directory itself is excluded from the list.
-//
-func (root *Directory) listing(skipRoot bool) *DirList {
- if root == nil {
- return nil
- }
-
- // determine number of entries n and maximum height
- n := 0
- minDepth := 1 << 30 // infinity
- maxDepth := 0
- for d := range root.iter(skipRoot) {
- n++
- if minDepth > d.Depth {
- minDepth = d.Depth
- }
- if maxDepth < d.Depth {
- maxDepth = d.Depth
- }
- }
- maxHeight := maxDepth - minDepth + 1
-
- if n == 0 {
- return nil
- }
-
- // create list
- list := make([]DirEntry, n)
- i := 0
- for d := range root.iter(skipRoot) {
- p := &list[i]
- p.Depth = d.Depth - minDepth
- p.Height = maxHeight - p.Depth
- // the path is relative to root.Path - remove the root.Path
- // prefix (the prefix should always be present but avoid
- // crashes and check)
- path := strings.TrimPrefix(d.Path, root.Path)
- // remove leading separator if any - path must be relative
- path = strings.TrimPrefix(path, "/")
- p.Path = path
- p.Name = d.Name
- p.HasPkg = d.HasPkg
- p.Synopsis = d.Synopsis
- i++
- }
-
- return &DirList{maxHeight, list}
-}
diff --git a/src/cmd/godoc/doc.go b/src/cmd/godoc/doc.go
deleted file mode 100644
index 1fa57a8b3..000000000
--- a/src/cmd/godoc/doc.go
+++ /dev/null
@@ -1,135 +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.
-
-/*
-
-Godoc extracts and generates documentation for Go programs.
-
-It has two modes.
-
-Without the -http flag, it runs in command-line mode and prints plain text
-documentation to standard output and exits. If both a library package and
-a command with the same name exists, using the prefix cmd/ will force
-documentation on the command rather than the library package. If the -src
-flag is specified, godoc prints the exported interface of a package in Go
-source form, or the implementation of a specific exported language entity:
-
- godoc fmt # documentation for package fmt
- godoc fmt Printf # documentation for fmt.Printf
- godoc cmd/go # force documentation for the go command
- godoc -src fmt # fmt package interface in Go source form
- godoc -src fmt Printf # implementation of fmt.Printf
-
-In command-line mode, the -q flag enables search queries against a godoc running
-as a webserver. If no explicit server address is specified with the -server flag,
-godoc first tries localhost:6060 and then http://golang.org.
-
- godoc -q Reader
- godoc -q math.Sin
- godoc -server=:6060 -q sin
-
-With the -http flag, it runs as a web server and presents the documentation as a
-web page.
-
- godoc -http=:6060
-
-Usage:
- godoc [flag] package [name ...]
-
-The flags are:
- -v
- verbose mode
- -q
- arguments are considered search queries: a legal query is a
- single identifier (such as ToLower) or a qualified identifier
- (such as math.Sin).
- -src
- print (exported) source in command-line mode
- -tabwidth=4
- width of tabs in units of spaces
- -timestamps=true
- show timestamps with directory listings
- -index
- enable identifier and full text search index
- (no search box is shown if -index is not set)
- -index_files=""
- glob pattern specifying index files; if not empty,
- the index is read from these files in sorted order
- -index_throttle=0.75
- index throttle value; a value of 0 means no time is allocated
- to the indexer (the indexer will never finish), a value of 1.0
- means that index creation is running at full throttle (other
- goroutines may get no time while the index is built)
- -links=true:
- link identifiers to their declarations
- -write_index=false
- write index to a file; the file name must be specified with
- -index_files
- -maxresults=10000
- maximum number of full text search results shown
- (no full text index is built if maxresults <= 0)
- -notes="BUG"
- regular expression matching note markers to show
- (e.g., "BUG|TODO", ".*")
- -html
- print HTML in command-line mode
- -goroot=$GOROOT
- Go root directory
- -http=addr
- HTTP service address (e.g., '127.0.0.1:6060' or just ':6060')
- -server=addr
- webserver address for command line searches
- -templates=""
- directory containing alternate template files; if set,
- the directory may provide alternative template files
- for the files in $GOROOT/lib/godoc
- -url=path
- print to standard output the data that would be served by
- an HTTP request for path
- -zip=""
- zip file providing the file system to serve; disabled if empty
-
-By default, godoc looks at the packages it finds via $GOROOT and $GOPATH (if set).
-This behavior can be altered by providing an alternative $GOROOT with the -goroot
-flag.
-
-When godoc runs as a web server and -index is set, a search index is maintained.
-The index is created at startup.
-
-The index contains both identifier and full text search information (searchable
-via regular expressions). The maximum number of full text search results shown
-can be set with the -maxresults flag; if set to 0, no full text results are
-shown, and only an identifier index but no full text search index is created.
-
-The presentation mode of web pages served by godoc can be controlled with the
-"m" URL parameter; it accepts a comma-separated list of flag names as value:
-
- all show documentation for all declarations, not just the exported ones
- methods show all embedded methods, not just those of unexported anonymous fields
- src show the original source code rather then the extracted documentation
- text present the page in textual (command-line) form rather than HTML
- flat present flat (not indented) directory listings using full paths
-
-For instance, http://golang.org/pkg/math/big/?m=all,text shows the documentation
-for all (not just the exported) declarations of package big, in textual form (as
-it would appear when using godoc from the command line: "godoc -src math/big .*").
-
-By default, godoc serves files from the file system of the underlying OS.
-Instead, a .zip file may be provided via the -zip flag, which contains
-the file system to serve. The file paths stored in the .zip file must use
-slash ('/') as path separator; and they must be unrooted. $GOROOT (or -goroot)
-must be set to the .zip file directory path containing the Go root directory.
-For instance, for a .zip file created by the command:
-
- zip go.zip $HOME/go
-
-one may run godoc as follows:
-
- godoc -http=:6060 -zip=go.zip -goroot=$HOME/go
-
-See "Godoc: documenting Go code" for how to write good comments for godoc:
-http://golang.org/doc/articles/godoc_documenting_go_code.html
-
-*/
-package main
diff --git a/src/cmd/godoc/filesystem.go b/src/cmd/godoc/filesystem.go
deleted file mode 100644
index 0309d7cab..000000000
--- a/src/cmd/godoc/filesystem.go
+++ /dev/null
@@ -1,562 +0,0 @@
-// 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 defines types for abstract file system access and
-// provides an implementation accessing the file system of the
-// underlying OS.
-
-package main
-
-import (
- "fmt"
- "io"
- "io/ioutil"
- "net/http"
- "os"
- pathpkg "path"
- "path/filepath"
- "sort"
- "strings"
- "time"
-)
-
-// fs is the file system that godoc reads from and serves.
-// It is a virtual file system that operates on slash-separated paths,
-// and its root corresponds to the Go distribution root: /src/pkg
-// holds the source tree, and so on. This means that the URLs served by
-// the godoc server are the same as the paths in the virtual file
-// system, which helps keep things simple.
-//
-// New file trees - implementations of FileSystem - can be added to
-// the virtual file system using nameSpace's Bind method.
-// The usual setup is to bind OS(runtime.GOROOT) to the root
-// of the name space and then bind any GOPATH/src directories
-// on top of /src/pkg, so that all sources are in /src/pkg.
-//
-// For more about name spaces, see the nameSpace type's
-// documentation below.
-//
-// The use of this virtual file system means that most code processing
-// paths can assume they are slash-separated and should be using
-// package path (often imported as pathpkg) to manipulate them,
-// even on Windows.
-//
-var fs = nameSpace{} // the underlying file system for godoc
-
-// Setting debugNS = true will enable debugging prints about
-// name space translations.
-const debugNS = false
-
-// The FileSystem interface specifies the methods godoc is using
-// to access the file system for which it serves documentation.
-type FileSystem interface {
- Open(path string) (readSeekCloser, error)
- Lstat(path string) (os.FileInfo, error)
- Stat(path string) (os.FileInfo, error)
- ReadDir(path string) ([]os.FileInfo, error)
- String() string
-}
-
-type readSeekCloser interface {
- io.Reader
- io.Seeker
- io.Closer
-}
-
-// ReadFile reads the file named by path from fs and returns the contents.
-func ReadFile(fs FileSystem, path string) ([]byte, error) {
- rc, err := fs.Open(path)
- if err != nil {
- return nil, err
- }
- defer rc.Close()
- return ioutil.ReadAll(rc)
-}
-
-// OS returns an implementation of FileSystem reading from the
-// tree rooted at root. Recording a root is convenient everywhere
-// but necessary on Windows, because the slash-separated path
-// passed to Open has no way to specify a drive letter. Using a root
-// lets code refer to OS(`c:\`), OS(`d:\`) and so on.
-func OS(root string) FileSystem {
- return osFS(root)
-}
-
-type osFS string
-
-func (root osFS) String() string { return "os(" + string(root) + ")" }
-
-func (root osFS) resolve(path string) string {
- // Clean the path so that it cannot possibly begin with ../.
- // If it did, the result of filepath.Join would be outside the
- // tree rooted at root. We probably won't ever see a path
- // with .. in it, but be safe anyway.
- path = pathpkg.Clean("/" + path)
-
- return filepath.Join(string(root), path)
-}
-
-func (root osFS) Open(path string) (readSeekCloser, error) {
- f, err := os.Open(root.resolve(path))
- if err != nil {
- return nil, err
- }
- fi, err := f.Stat()
- if err != nil {
- return nil, err
- }
- if fi.IsDir() {
- return nil, fmt.Errorf("Open: %s is a directory", path)
- }
- return f, nil
-}
-
-func (root osFS) Lstat(path string) (os.FileInfo, error) {
- return os.Lstat(root.resolve(path))
-}
-
-func (root osFS) Stat(path string) (os.FileInfo, error) {
- return os.Stat(root.resolve(path))
-}
-
-func (root osFS) ReadDir(path string) ([]os.FileInfo, error) {
- return ioutil.ReadDir(root.resolve(path)) // is sorted
-}
-
-// hasPathPrefix returns true if x == y or x == y + "/" + more
-func hasPathPrefix(x, y string) bool {
- return x == y || strings.HasPrefix(x, y) && (strings.HasSuffix(y, "/") || strings.HasPrefix(x[len(y):], "/"))
-}
-
-// A nameSpace is a file system made up of other file systems
-// mounted at specific locations in the name space.
-//
-// The representation is a map from mount point locations
-// to the list of file systems mounted at that location. A traditional
-// Unix mount table would use a single file system per mount point,
-// but we want to be able to mount multiple file systems on a single
-// mount point and have the system behave as if the union of those
-// file systems were present at the mount point.
-// For example, if the OS file system has a Go installation in
-// c:\Go and additional Go path trees in d:\Work1 and d:\Work2, then
-// this name space creates the view we want for the godoc server:
-//
-// nameSpace{
-// "/": {
-// {old: "/", fs: OS(`c:\Go`), new: "/"},
-// },
-// "/src/pkg": {
-// {old: "/src/pkg", fs: OS(`c:\Go`), new: "/src/pkg"},
-// {old: "/src/pkg", fs: OS(`d:\Work1`), new: "/src"},
-// {old: "/src/pkg", fs: OS(`d:\Work2`), new: "/src"},
-// },
-// }
-//
-// This is created by executing:
-//
-// ns := nameSpace{}
-// ns.Bind("/", OS(`c:\Go`), "/", bindReplace)
-// ns.Bind("/src/pkg", OS(`d:\Work1`), "/src", bindAfter)
-// ns.Bind("/src/pkg", OS(`d:\Work2`), "/src", bindAfter)
-//
-// A particular mount point entry is a triple (old, fs, new), meaning that to
-// operate on a path beginning with old, replace that prefix (old) with new
-// and then pass that path to the FileSystem implementation fs.
-//
-// Given this name space, a ReadDir of /src/pkg/code will check each prefix
-// of the path for a mount point (first /src/pkg/code, then /src/pkg, then /src,
-// then /), stopping when it finds one. For the above example, /src/pkg/code
-// will find the mount point at /src/pkg:
-//
-// {old: "/src/pkg", fs: OS(`c:\Go`), new: "/src/pkg"},
-// {old: "/src/pkg", fs: OS(`d:\Work1`), new: "/src"},
-// {old: "/src/pkg", fs: OS(`d:\Work2`), new: "/src"},
-//
-// ReadDir will when execute these three calls and merge the results:
-//
-// OS(`c:\Go`).ReadDir("/src/pkg/code")
-// OS(`d:\Work1').ReadDir("/src/code")
-// OS(`d:\Work2').ReadDir("/src/code")
-//
-// Note that the "/src/pkg" in "/src/pkg/code" has been replaced by
-// just "/src" in the final two calls.
-//
-// OS is itself an implementation of a file system: it implements
-// OS(`c:\Go`).ReadDir("/src/pkg/code") as ioutil.ReadDir(`c:\Go\src\pkg\code`).
-//
-// Because the new path is evaluated by fs (here OS(root)), another way
-// to read the mount table is to mentally combine fs+new, so that this table:
-//
-// {old: "/src/pkg", fs: OS(`c:\Go`), new: "/src/pkg"},
-// {old: "/src/pkg", fs: OS(`d:\Work1`), new: "/src"},
-// {old: "/src/pkg", fs: OS(`d:\Work2`), new: "/src"},
-//
-// reads as:
-//
-// "/src/pkg" -> c:\Go\src\pkg
-// "/src/pkg" -> d:\Work1\src
-// "/src/pkg" -> d:\Work2\src
-//
-// An invariant (a redundancy) of the name space representation is that
-// ns[mtpt][i].old is always equal to mtpt (in the example, ns["/src/pkg"]'s
-// mount table entries always have old == "/src/pkg"). The 'old' field is
-// useful to callers, because they receive just a []mountedFS and not any
-// other indication of which mount point was found.
-//
-type nameSpace map[string][]mountedFS
-
-// A mountedFS handles requests for path by replacing
-// a prefix 'old' with 'new' and then calling the fs methods.
-type mountedFS struct {
- old string
- fs FileSystem
- new string
-}
-
-// translate translates path for use in m, replacing old with new.
-//
-// mountedFS{"/src/pkg", fs, "/src"}.translate("/src/pkg/code") == "/src/code".
-func (m mountedFS) translate(path string) string {
- path = pathpkg.Clean("/" + path)
- if !hasPathPrefix(path, m.old) {
- panic("translate " + path + " but old=" + m.old)
- }
- return pathpkg.Join(m.new, path[len(m.old):])
-}
-
-func (nameSpace) String() string {
- return "ns"
-}
-
-// Fprint writes a text representation of the name space to w.
-func (ns nameSpace) Fprint(w io.Writer) {
- fmt.Fprint(w, "name space {\n")
- var all []string
- for mtpt := range ns {
- all = append(all, mtpt)
- }
- sort.Strings(all)
- for _, mtpt := range all {
- fmt.Fprintf(w, "\t%s:\n", mtpt)
- for _, m := range ns[mtpt] {
- fmt.Fprintf(w, "\t\t%s %s\n", m.fs, m.new)
- }
- }
- fmt.Fprint(w, "}\n")
-}
-
-// clean returns a cleaned, rooted path for evaluation.
-// It canonicalizes the path so that we can use string operations
-// to analyze it.
-func (nameSpace) clean(path string) string {
- return pathpkg.Clean("/" + path)
-}
-
-// Bind causes references to old to redirect to the path new in newfs.
-// If mode is bindReplace, old redirections are discarded.
-// If mode is bindBefore, this redirection takes priority over existing ones,
-// but earlier ones are still consulted for paths that do not exist in newfs.
-// If mode is bindAfter, this redirection happens only after existing ones
-// have been tried and failed.
-
-const (
- bindReplace = iota
- bindBefore
- bindAfter
-)
-
-func (ns nameSpace) Bind(old string, newfs FileSystem, new string, mode int) {
- old = ns.clean(old)
- new = ns.clean(new)
- m := mountedFS{old, newfs, new}
- var mtpt []mountedFS
- switch mode {
- case bindReplace:
- mtpt = append(mtpt, m)
- case bindAfter:
- mtpt = append(mtpt, ns.resolve(old)...)
- mtpt = append(mtpt, m)
- case bindBefore:
- mtpt = append(mtpt, m)
- mtpt = append(mtpt, ns.resolve(old)...)
- }
-
- // Extend m.old, m.new in inherited mount point entries.
- for i := range mtpt {
- m := &mtpt[i]
- if m.old != old {
- if !hasPathPrefix(old, m.old) {
- // This should not happen. If it does, panic so
- // that we can see the call trace that led to it.
- panic(fmt.Sprintf("invalid Bind: old=%q m={%q, %s, %q}", old, m.old, m.fs.String(), m.new))
- }
- suffix := old[len(m.old):]
- m.old = pathpkg.Join(m.old, suffix)
- m.new = pathpkg.Join(m.new, suffix)
- }
- }
-
- ns[old] = mtpt
-}
-
-// resolve resolves a path to the list of mountedFS to use for path.
-func (ns nameSpace) resolve(path string) []mountedFS {
- path = ns.clean(path)
- for {
- if m := ns[path]; m != nil {
- if debugNS {
- fmt.Printf("resolve %s: %v\n", path, m)
- }
- return m
- }
- if path == "/" {
- break
- }
- path = pathpkg.Dir(path)
- }
- return nil
-}
-
-// Open implements the FileSystem Open method.
-func (ns nameSpace) Open(path string) (readSeekCloser, error) {
- var err error
- for _, m := range ns.resolve(path) {
- if debugNS {
- fmt.Printf("tx %s: %v\n", path, m.translate(path))
- }
- r, err1 := m.fs.Open(m.translate(path))
- if err1 == nil {
- return r, nil
- }
- if err == nil {
- err = err1
- }
- }
- if err == nil {
- err = &os.PathError{Op: "open", Path: path, Err: os.ErrNotExist}
- }
- return nil, err
-}
-
-// stat implements the FileSystem Stat and Lstat methods.
-func (ns nameSpace) stat(path string, f func(FileSystem, string) (os.FileInfo, error)) (os.FileInfo, error) {
- var err error
- for _, m := range ns.resolve(path) {
- fi, err1 := f(m.fs, m.translate(path))
- if err1 == nil {
- return fi, nil
- }
- if err == nil {
- err = err1
- }
- }
- if err == nil {
- err = &os.PathError{Op: "stat", Path: path, Err: os.ErrNotExist}
- }
- return nil, err
-}
-
-func (ns nameSpace) Stat(path string) (os.FileInfo, error) {
- return ns.stat(path, FileSystem.Stat)
-}
-
-func (ns nameSpace) Lstat(path string) (os.FileInfo, error) {
- return ns.stat(path, FileSystem.Lstat)
-}
-
-// dirInfo is a trivial implementation of os.FileInfo for a directory.
-type dirInfo string
-
-func (d dirInfo) Name() string { return string(d) }
-func (d dirInfo) Size() int64 { return 0 }
-func (d dirInfo) Mode() os.FileMode { return os.ModeDir | 0555 }
-func (d dirInfo) ModTime() time.Time { return startTime }
-func (d dirInfo) IsDir() bool { return true }
-func (d dirInfo) Sys() interface{} { return nil }
-
-var startTime = time.Now()
-
-// ReadDir implements the FileSystem ReadDir method. It's where most of the magic is.
-// (The rest is in resolve.)
-//
-// Logically, ReadDir must return the union of all the directories that are named
-// by path. In order to avoid misinterpreting Go packages, of all the directories
-// that contain Go source code, we only include the files from the first,
-// but we include subdirectories from all.
-//
-// ReadDir must also return directory entries needed to reach mount points.
-// If the name space looks like the example in the type nameSpace comment,
-// but c:\Go does not have a src/pkg subdirectory, we still want to be able
-// to find that subdirectory, because we've mounted d:\Work1 and d:\Work2
-// there. So if we don't see "src" in the directory listing for c:\Go, we add an
-// entry for it before returning.
-//
-func (ns nameSpace) ReadDir(path string) ([]os.FileInfo, error) {
- path = ns.clean(path)
-
- var (
- haveGo = false
- haveName = map[string]bool{}
- all []os.FileInfo
- err error
- first []os.FileInfo
- )
-
- for _, m := range ns.resolve(path) {
- dir, err1 := m.fs.ReadDir(m.translate(path))
- if err1 != nil {
- if err == nil {
- err = err1
- }
- continue
- }
-
- if dir == nil {
- dir = []os.FileInfo{}
- }
-
- if first == nil {
- first = dir
- }
-
- // If we don't yet have Go files in 'all' and this directory
- // has some, add all the files from this directory.
- // Otherwise, only add subdirectories.
- useFiles := false
- if !haveGo {
- for _, d := range dir {
- if strings.HasSuffix(d.Name(), ".go") {
- useFiles = true
- haveGo = true
- break
- }
- }
- }
-
- for _, d := range dir {
- name := d.Name()
- if (d.IsDir() || useFiles) && !haveName[name] {
- haveName[name] = true
- all = append(all, d)
- }
- }
- }
-
- // We didn't find any directories containing Go files.
- // If some directory returned successfully, use that.
- if !haveGo {
- for _, d := range first {
- if !haveName[d.Name()] {
- haveName[d.Name()] = true
- all = append(all, d)
- }
- }
- }
-
- // Built union. Add any missing directories needed to reach mount points.
- for old := range ns {
- if hasPathPrefix(old, path) && old != path {
- // Find next element after path in old.
- elem := old[len(path):]
- elem = strings.TrimPrefix(elem, "/")
- if i := strings.Index(elem, "/"); i >= 0 {
- elem = elem[:i]
- }
- if !haveName[elem] {
- haveName[elem] = true
- all = append(all, dirInfo(elem))
- }
- }
- }
-
- if len(all) == 0 {
- return nil, err
- }
-
- sort.Sort(byName(all))
- return all, nil
-}
-
-// byName implements sort.Interface.
-type byName []os.FileInfo
-
-func (f byName) Len() int { return len(f) }
-func (f byName) Less(i, j int) bool { return f[i].Name() < f[j].Name() }
-func (f byName) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
-
-// An httpFS implements http.FileSystem using a FileSystem.
-type httpFS struct {
- fs FileSystem
-}
-
-func (h *httpFS) Open(name string) (http.File, error) {
- fi, err := h.fs.Stat(name)
- if err != nil {
- return nil, err
- }
- if fi.IsDir() {
- return &httpDir{h.fs, name, nil}, nil
- }
- f, err := h.fs.Open(name)
- if err != nil {
- return nil, err
- }
- return &httpFile{h.fs, f, name}, nil
-}
-
-// httpDir implements http.File for a directory in a FileSystem.
-type httpDir struct {
- fs FileSystem
- name string
- pending []os.FileInfo
-}
-
-func (h *httpDir) Close() error { return nil }
-func (h *httpDir) Stat() (os.FileInfo, error) { return h.fs.Stat(h.name) }
-func (h *httpDir) Read([]byte) (int, error) {
- return 0, fmt.Errorf("cannot Read from directory %s", h.name)
-}
-
-func (h *httpDir) Seek(offset int64, whence int) (int64, error) {
- if offset == 0 && whence == 0 {
- h.pending = nil
- return 0, nil
- }
- return 0, fmt.Errorf("unsupported Seek in directory %s", h.name)
-}
-
-func (h *httpDir) Readdir(count int) ([]os.FileInfo, error) {
- if h.pending == nil {
- d, err := h.fs.ReadDir(h.name)
- if err != nil {
- return nil, err
- }
- if d == nil {
- d = []os.FileInfo{} // not nil
- }
- h.pending = d
- }
-
- if len(h.pending) == 0 && count > 0 {
- return nil, io.EOF
- }
- if count <= 0 || count > len(h.pending) {
- count = len(h.pending)
- }
- d := h.pending[:count]
- h.pending = h.pending[count:]
- return d, nil
-}
-
-// httpFile implements http.File for a file (not directory) in a FileSystem.
-type httpFile struct {
- fs FileSystem
- readSeekCloser
- name string
-}
-
-func (h *httpFile) Stat() (os.FileInfo, error) { return h.fs.Stat(h.name) }
-func (h *httpFile) Readdir(int) ([]os.FileInfo, error) {
- return nil, fmt.Errorf("cannot Readdir from file %s", h.name)
-}
diff --git a/src/cmd/godoc/format.go b/src/cmd/godoc/format.go
deleted file mode 100644
index 59a89c5bf..000000000
--- a/src/cmd/godoc/format.go
+++ /dev/null
@@ -1,372 +0,0 @@
-// 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 FormatSelections and FormatText.
-// FormatText is used to HTML-format Go and non-Go source
-// text with line numbers and highlighted sections. It is
-// built on top of FormatSelections, a generic formatter
-// for "selected" text.
-
-package main
-
-import (
- "fmt"
- "go/scanner"
- "go/token"
- "io"
- "regexp"
- "strconv"
- "text/template"
-)
-
-// ----------------------------------------------------------------------------
-// Implementation of FormatSelections
-
-// A Segment describes a text segment [start, end).
-// The zero value of a Segment is a ready-to-use empty segment.
-//
-type Segment struct {
- start, end int
-}
-
-func (seg *Segment) isEmpty() bool { return seg.start >= seg.end }
-
-// A Selection is an "iterator" function returning a text segment.
-// Repeated calls to a selection return consecutive, non-overlapping,
-// non-empty segments, followed by an infinite sequence of empty
-// segments. The first empty segment marks the end of the selection.
-//
-type Selection func() Segment
-
-// A LinkWriter writes some start or end "tag" to w for the text offset offs.
-// It is called by FormatSelections at the start or end of each link segment.
-//
-type LinkWriter func(w io.Writer, offs int, start bool)
-
-// A SegmentWriter formats a text according to selections and writes it to w.
-// The selections parameter is a bit set indicating which selections provided
-// to FormatSelections overlap with the text segment: If the n'th bit is set
-// in selections, the n'th selection provided to FormatSelections is overlapping
-// with the text.
-//
-type SegmentWriter func(w io.Writer, text []byte, selections int)
-
-// FormatSelections takes a text and writes it to w using link and segment
-// writers lw and sw as follows: lw is invoked for consecutive segment starts
-// and ends as specified through the links selection, and sw is invoked for
-// consecutive segments of text overlapped by the same selections as specified
-// by selections. The link writer lw may be nil, in which case the links
-// Selection is ignored.
-//
-func FormatSelections(w io.Writer, text []byte, lw LinkWriter, links Selection, sw SegmentWriter, selections ...Selection) {
- // If we have a link writer, make the links
- // selection the last entry in selections
- if lw != nil {
- selections = append(selections, links)
- }
-
- // compute the sequence of consecutive segment changes
- changes := newMerger(selections)
-
- // The i'th bit in bitset indicates that the text
- // at the current offset is covered by selections[i].
- bitset := 0
- lastOffs := 0
-
- // Text segments are written in a delayed fashion
- // such that consecutive segments belonging to the
- // same selection can be combined (peephole optimization).
- // last describes the last segment which has not yet been written.
- var last struct {
- begin, end int // valid if begin < end
- bitset int
- }
-
- // flush writes the last delayed text segment
- flush := func() {
- if last.begin < last.end {
- sw(w, text[last.begin:last.end], last.bitset)
- }
- last.begin = last.end // invalidate last
- }
-
- // segment runs the segment [lastOffs, end) with the selection
- // indicated by bitset through the segment peephole optimizer.
- segment := func(end int) {
- if lastOffs < end { // ignore empty segments
- if last.end != lastOffs || last.bitset != bitset {
- // the last segment is not adjacent to or
- // differs from the new one
- flush()
- // start a new segment
- last.begin = lastOffs
- }
- last.end = end
- last.bitset = bitset
- }
- }
-
- for {
- // get the next segment change
- index, offs, start := changes.next()
- if index < 0 || offs > len(text) {
- // no more segment changes or the next change
- // is past the end of the text - we're done
- break
- }
- // determine the kind of segment change
- if lw != nil && index == len(selections)-1 {
- // we have a link segment change (see start of this function):
- // format the previous selection segment, write the
- // link tag and start a new selection segment
- segment(offs)
- flush()
- lastOffs = offs
- lw(w, offs, start)
- } else {
- // we have a selection change:
- // format the previous selection segment, determine
- // the new selection bitset and start a new segment
- segment(offs)
- lastOffs = offs
- mask := 1 << uint(index)
- if start {
- bitset |= mask
- } else {
- bitset &^= mask
- }
- }
- }
- segment(len(text))
- flush()
-}
-
-// A merger merges a slice of Selections and produces a sequence of
-// consecutive segment change events through repeated next() calls.
-//
-type merger struct {
- selections []Selection
- segments []Segment // segments[i] is the next segment of selections[i]
-}
-
-const infinity int = 2e9
-
-func newMerger(selections []Selection) *merger {
- segments := make([]Segment, len(selections))
- for i, sel := range selections {
- segments[i] = Segment{infinity, infinity}
- if sel != nil {
- if seg := sel(); !seg.isEmpty() {
- segments[i] = seg
- }
- }
- }
- return &merger{selections, segments}
-}
-
-// next returns the next segment change: index specifies the Selection
-// to which the segment belongs, offs is the segment start or end offset
-// as determined by the start value. If there are no more segment changes,
-// next returns an index value < 0.
-//
-func (m *merger) next() (index, offs int, start bool) {
- // find the next smallest offset where a segment starts or ends
- offs = infinity
- index = -1
- for i, seg := range m.segments {
- switch {
- case seg.start < offs:
- offs = seg.start
- index = i
- start = true
- case seg.end < offs:
- offs = seg.end
- index = i
- start = false
- }
- }
- if index < 0 {
- // no offset found => all selections merged
- return
- }
- // offset found - it's either the start or end offset but
- // either way it is ok to consume the start offset: set it
- // to infinity so it won't be considered in the following
- // next call
- m.segments[index].start = infinity
- if start {
- return
- }
- // end offset found - consume it
- m.segments[index].end = infinity
- // advance to the next segment for that selection
- seg := m.selections[index]()
- if !seg.isEmpty() {
- m.segments[index] = seg
- }
- return
-}
-
-// ----------------------------------------------------------------------------
-// Implementation of FormatText
-
-// lineSelection returns the line segments for text as a Selection.
-func lineSelection(text []byte) Selection {
- i, j := 0, 0
- return func() (seg Segment) {
- // find next newline, if any
- for j < len(text) {
- j++
- if text[j-1] == '\n' {
- break
- }
- }
- if i < j {
- // text[i:j] constitutes a line
- seg = Segment{i, j}
- i = j
- }
- return
- }
-}
-
-// tokenSelection returns, as a selection, the sequence of
-// consecutive occurrences of token sel in the Go src text.
-//
-func tokenSelection(src []byte, sel token.Token) Selection {
- var s scanner.Scanner
- fset := token.NewFileSet()
- file := fset.AddFile("", fset.Base(), len(src))
- s.Init(file, src, nil, scanner.ScanComments)
- return func() (seg Segment) {
- for {
- pos, tok, lit := s.Scan()
- if tok == token.EOF {
- break
- }
- offs := file.Offset(pos)
- if tok == sel {
- seg = Segment{offs, offs + len(lit)}
- break
- }
- }
- return
- }
-}
-
-// makeSelection is a helper function to make a Selection from a slice of pairs.
-// Pairs describing empty segments are ignored.
-//
-func makeSelection(matches [][]int) Selection {
- i := 0
- return func() Segment {
- for i < len(matches) {
- m := matches[i]
- i++
- if m[0] < m[1] {
- // non-empty segment
- return Segment{m[0], m[1]}
- }
- }
- return Segment{}
- }
-}
-
-// regexpSelection computes the Selection for the regular expression expr in text.
-func regexpSelection(text []byte, expr string) Selection {
- var matches [][]int
- if rx, err := regexp.Compile(expr); err == nil {
- matches = rx.FindAllIndex(text, -1)
- }
- return makeSelection(matches)
-}
-
-var selRx = regexp.MustCompile(`^([0-9]+):([0-9]+)`)
-
-// rangeSelection computes the Selection for a text range described
-// by the argument str; the range description must match the selRx
-// regular expression.
-//
-func rangeSelection(str string) Selection {
- m := selRx.FindStringSubmatch(str)
- if len(m) >= 2 {
- from, _ := strconv.Atoi(m[1])
- to, _ := strconv.Atoi(m[2])
- if from < to {
- return makeSelection([][]int{{from, to}})
- }
- }
- return nil
-}
-
-// Span tags for all the possible selection combinations that may
-// be generated by FormatText. Selections are indicated by a bitset,
-// and the value of the bitset specifies the tag to be used.
-//
-// bit 0: comments
-// bit 1: highlights
-// bit 2: selections
-//
-var startTags = [][]byte{
- /* 000 */ []byte(``),
- /* 001 */ []byte(`<span class="comment">`),
- /* 010 */ []byte(`<span class="highlight">`),
- /* 011 */ []byte(`<span class="highlight-comment">`),
- /* 100 */ []byte(`<span class="selection">`),
- /* 101 */ []byte(`<span class="selection-comment">`),
- /* 110 */ []byte(`<span class="selection-highlight">`),
- /* 111 */ []byte(`<span class="selection-highlight-comment">`),
-}
-
-var endTag = []byte(`</span>`)
-
-func selectionTag(w io.Writer, text []byte, selections int) {
- if selections < len(startTags) {
- if tag := startTags[selections]; len(tag) > 0 {
- w.Write(tag)
- template.HTMLEscape(w, text)
- w.Write(endTag)
- return
- }
- }
- template.HTMLEscape(w, text)
-}
-
-// FormatText HTML-escapes text and writes it to w.
-// Consecutive text segments are wrapped in HTML spans (with tags as
-// defined by startTags and endTag) as follows:
-//
-// - if line >= 0, line number (ln) spans are inserted before each line,
-// starting with the value of line
-// - if the text is Go source, comments get the "comment" span class
-// - each occurrence of the regular expression pattern gets the "highlight"
-// span class
-// - text segments covered by selection get the "selection" span class
-//
-// Comments, highlights, and selections may overlap arbitrarily; the respective
-// HTML span classes are specified in the startTags variable.
-//
-func FormatText(w io.Writer, text []byte, line int, goSource bool, pattern string, selection Selection) {
- var comments, highlights Selection
- if goSource {
- comments = tokenSelection(text, token.COMMENT)
- }
- if pattern != "" {
- highlights = regexpSelection(text, pattern)
- }
- if line >= 0 || comments != nil || highlights != nil || selection != nil {
- var lineTag LinkWriter
- if line >= 0 {
- lineTag = func(w io.Writer, _ int, start bool) {
- if start {
- fmt.Fprintf(w, "<a id=\"L%d\"></a><span class=\"ln\">%6d</span>\t", line, line)
- line++
- }
- }
- }
- FormatSelections(w, text, lineTag, lineSelection(text), selectionTag, comments, highlights, selection)
- } else {
- template.HTMLEscape(w, text)
- }
-}
diff --git a/src/cmd/godoc/godoc.go b/src/cmd/godoc/godoc.go
deleted file mode 100644
index 26b0b97e1..000000000
--- a/src/cmd/godoc/godoc.go
+++ /dev/null
@@ -1,1582 +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 main
-
-import (
- "bytes"
- "encoding/json"
- "flag"
- "fmt"
- "go/ast"
- "go/build"
- "go/doc"
- "go/format"
- "go/printer"
- "go/token"
- htmlpkg "html"
- "io"
- "io/ioutil"
- "log"
- "net/http"
- "net/url"
- "os"
- pathpkg "path"
- "path/filepath"
- "regexp"
- "runtime"
- "sort"
- "strings"
- "text/template"
- "time"
- "unicode"
- "unicode/utf8"
-)
-
-// ----------------------------------------------------------------------------
-// Globals
-
-type delayTime struct {
- RWValue
-}
-
-func (dt *delayTime) backoff(max time.Duration) {
- dt.mutex.Lock()
- v := dt.value.(time.Duration) * 2
- if v > max {
- v = max
- }
- dt.value = v
- // don't change dt.timestamp - calling backoff indicates an error condition
- dt.mutex.Unlock()
-}
-
-var (
- verbose = flag.Bool("v", false, "verbose mode")
-
- // file system roots
- // TODO(gri) consider the invariant that goroot always end in '/'
- goroot = flag.String("goroot", runtime.GOROOT(), "Go root directory")
- testDir = flag.String("testdir", "", "Go root subdirectory - for testing only (faster startups)")
-
- // layout control
- tabwidth = flag.Int("tabwidth", 4, "tab width")
- showTimestamps = flag.Bool("timestamps", false, "show timestamps with directory listings")
- templateDir = flag.String("templates", "", "directory containing alternate template files")
- showPlayground = flag.Bool("play", false, "enable playground in web interface")
- showExamples = flag.Bool("ex", false, "show examples in command line mode")
- declLinks = flag.Bool("links", true, "link identifiers to their declarations")
-
- // search index
- indexEnabled = flag.Bool("index", false, "enable search index")
- indexFiles = flag.String("index_files", "", "glob pattern specifying index files;"+
- "if not empty, the index is read from these files in sorted order")
- maxResults = flag.Int("maxresults", 10000, "maximum number of full text search results shown")
- indexThrottle = flag.Float64("index_throttle", 0.75, "index throttle value; 0.0 = no time allocated, 1.0 = full throttle")
-
- // file system information
- fsTree RWValue // *Directory tree of packages, updated with each sync (but sync code is removed now)
- fsModified RWValue // timestamp of last call to invalidateIndex
- docMetadata RWValue // mapping from paths to *Metadata
-
- // http handlers
- fileServer http.Handler // default file server
- cmdHandler docServer
- pkgHandler docServer
-
- // source code notes
- notes = flag.String("notes", "BUG", "regular expression matching note markers to show")
-)
-
-func initHandlers() {
- fileServer = http.FileServer(&httpFS{fs})
- cmdHandler = docServer{"/cmd/", "/src/cmd"}
- pkgHandler = docServer{"/pkg/", "/src/pkg"}
-}
-
-func registerPublicHandlers(mux *http.ServeMux) {
- mux.Handle(cmdHandler.pattern, &cmdHandler)
- mux.Handle(pkgHandler.pattern, &pkgHandler)
- mux.HandleFunc("/doc/codewalk/", codewalk)
- mux.Handle("/doc/play/", fileServer)
- mux.HandleFunc("/search", search)
- mux.Handle("/robots.txt", fileServer)
- mux.HandleFunc("/opensearch.xml", serveSearchDesc)
- mux.HandleFunc("/", serveFile)
-}
-
-func initFSTree() {
- dir := newDirectory(pathpkg.Join("/", *testDir), -1)
- if dir == nil {
- log.Println("Warning: FSTree is nil")
- return
- }
- fsTree.set(dir)
- invalidateIndex()
-}
-
-// ----------------------------------------------------------------------------
-// Tab conversion
-
-var spaces = []byte(" ") // 32 spaces seems like a good number
-
-const (
- indenting = iota
- collecting
-)
-
-// A tconv is an io.Writer filter for converting leading tabs into spaces.
-type tconv struct {
- output io.Writer
- state int // indenting or collecting
- indent int // valid if state == indenting
-}
-
-func (p *tconv) writeIndent() (err error) {
- i := p.indent
- for i >= len(spaces) {
- i -= len(spaces)
- if _, err = p.output.Write(spaces); err != nil {
- return
- }
- }
- // i < len(spaces)
- if i > 0 {
- _, err = p.output.Write(spaces[0:i])
- }
- return
-}
-
-func (p *tconv) Write(data []byte) (n int, err error) {
- if len(data) == 0 {
- return
- }
- pos := 0 // valid if p.state == collecting
- var b byte
- for n, b = range data {
- switch p.state {
- case indenting:
- switch b {
- case '\t':
- p.indent += *tabwidth
- case '\n':
- p.indent = 0
- if _, err = p.output.Write(data[n : n+1]); err != nil {
- return
- }
- case ' ':
- p.indent++
- default:
- p.state = collecting
- pos = n
- if err = p.writeIndent(); err != nil {
- return
- }
- }
- case collecting:
- if b == '\n' {
- p.state = indenting
- p.indent = 0
- if _, err = p.output.Write(data[pos : n+1]); err != nil {
- return
- }
- }
- }
- }
- n = len(data)
- if pos < n && p.state == collecting {
- _, err = p.output.Write(data[pos:])
- }
- return
-}
-
-// ----------------------------------------------------------------------------
-// Templates
-
-// Write an AST node to w.
-func writeNode(w io.Writer, fset *token.FileSet, x interface{}) {
- // convert trailing tabs into spaces using a tconv filter
- // to ensure a good outcome in most browsers (there may still
- // be tabs in comments and strings, but converting those into
- // the right number of spaces is much harder)
- //
- // TODO(gri) rethink printer flags - perhaps tconv can be eliminated
- // with an another printer mode (which is more efficiently
- // implemented in the printer than here with another layer)
- mode := printer.TabIndent | printer.UseSpaces
- err := (&printer.Config{Mode: mode, Tabwidth: *tabwidth}).Fprint(&tconv{output: w}, fset, x)
- if err != nil {
- log.Print(err)
- }
-}
-
-func filenameFunc(path string) string {
- _, localname := pathpkg.Split(path)
- return localname
-}
-
-func fileInfoNameFunc(fi os.FileInfo) string {
- name := fi.Name()
- if fi.IsDir() {
- name += "/"
- }
- return name
-}
-
-func fileInfoTimeFunc(fi os.FileInfo) string {
- if t := fi.ModTime(); t.Unix() != 0 {
- return t.Local().String()
- }
- return "" // don't return epoch if time is obviously not set
-}
-
-// The strings in infoKinds must be properly html-escaped.
-var infoKinds = [nKinds]string{
- PackageClause: "package&nbsp;clause",
- ImportDecl: "import&nbsp;decl",
- ConstDecl: "const&nbsp;decl",
- TypeDecl: "type&nbsp;decl",
- VarDecl: "var&nbsp;decl",
- FuncDecl: "func&nbsp;decl",
- MethodDecl: "method&nbsp;decl",
- Use: "use",
-}
-
-func infoKind_htmlFunc(info SpotInfo) string {
- return infoKinds[info.Kind()] // infoKind entries are html-escaped
-}
-
-func infoLineFunc(info SpotInfo) int {
- line := info.Lori()
- if info.IsIndex() {
- index, _ := searchIndex.get()
- if index != nil {
- line = index.(*Index).Snippet(line).Line
- } else {
- // no line information available because
- // we don't have an index - this should
- // never happen; be conservative and don't
- // crash
- line = 0
- }
- }
- return line
-}
-
-func infoSnippet_htmlFunc(info SpotInfo) string {
- if info.IsIndex() {
- index, _ := searchIndex.get()
- // Snippet.Text was HTML-escaped when it was generated
- return index.(*Index).Snippet(info.Lori()).Text
- }
- return `<span class="alert">no snippet text available</span>`
-}
-
-func nodeFunc(info *PageInfo, node interface{}) string {
- var buf bytes.Buffer
- writeNode(&buf, info.FSet, node)
- return buf.String()
-}
-
-func node_htmlFunc(info *PageInfo, node interface{}, linkify bool) string {
- var buf1 bytes.Buffer
- writeNode(&buf1, info.FSet, node)
-
- var buf2 bytes.Buffer
- if n, _ := node.(ast.Node); n != nil && linkify && *declLinks {
- LinkifyText(&buf2, buf1.Bytes(), n)
- } else {
- FormatText(&buf2, buf1.Bytes(), -1, true, "", nil)
- }
-
- return buf2.String()
-}
-
-func comment_htmlFunc(comment string) string {
- var buf bytes.Buffer
- // TODO(gri) Provide list of words (e.g. function parameters)
- // to be emphasized by ToHTML.
- doc.ToHTML(&buf, comment, nil) // does html-escaping
- return buf.String()
-}
-
-// punchCardWidth is the number of columns of fixed-width
-// characters to assume when wrapping text. Very few people
-// use terminals or cards smaller than 80 characters, so 80 it is.
-// We do not try to sniff the environment or the tty to adapt to
-// the situation; instead, by using a constant we make sure that
-// godoc always produces the same output regardless of context,
-// a consistency that is lost otherwise. For example, if we sniffed
-// the environment or tty, then http://golang.org/pkg/math/?m=text
-// would depend on the width of the terminal where godoc started,
-// which is clearly bogus. More generally, the Unix tools that behave
-// differently when writing to a tty than when writing to a file have
-// a history of causing confusion (compare `ls` and `ls | cat`), and we
-// want to avoid that mistake here.
-const punchCardWidth = 80
-
-func comment_textFunc(comment, indent, preIndent string) string {
- var buf bytes.Buffer
- doc.ToText(&buf, comment, indent, preIndent, punchCardWidth-2*len(indent))
- return buf.String()
-}
-
-func startsWithUppercase(s string) bool {
- r, _ := utf8.DecodeRuneInString(s)
- return unicode.IsUpper(r)
-}
-
-var exampleOutputRx = regexp.MustCompile(`(?i)//[[:space:]]*output:`)
-
-// stripExampleSuffix strips lowercase braz in Foo_braz or Foo_Bar_braz from name
-// while keeping uppercase Braz in Foo_Braz.
-func stripExampleSuffix(name string) string {
- if i := strings.LastIndex(name, "_"); i != -1 {
- if i < len(name)-1 && !startsWithUppercase(name[i+1:]) {
- name = name[:i]
- }
- }
- return name
-}
-
-func example_textFunc(info *PageInfo, funcName, indent string) string {
- if !*showExamples {
- return ""
- }
-
- var buf bytes.Buffer
- first := true
- for _, eg := range info.Examples {
- name := stripExampleSuffix(eg.Name)
- if name != funcName {
- continue
- }
-
- if !first {
- buf.WriteString("\n")
- }
- first = false
-
- // print code
- cnode := &printer.CommentedNode{Node: eg.Code, Comments: eg.Comments}
- var buf1 bytes.Buffer
- writeNode(&buf1, info.FSet, cnode)
- code := buf1.String()
- // Additional formatting if this is a function body.
- if n := len(code); n >= 2 && code[0] == '{' && code[n-1] == '}' {
- // remove surrounding braces
- code = code[1 : n-1]
- // unindent
- code = strings.Replace(code, "\n ", "\n", -1)
- }
- code = strings.Trim(code, "\n")
- code = strings.Replace(code, "\n", "\n\t", -1)
-
- buf.WriteString(indent)
- buf.WriteString("Example:\n\t")
- buf.WriteString(code)
- buf.WriteString("\n")
- }
- return buf.String()
-}
-
-func example_htmlFunc(info *PageInfo, funcName string) string {
- var buf bytes.Buffer
- for _, eg := range info.Examples {
- name := stripExampleSuffix(eg.Name)
-
- if name != funcName {
- continue
- }
-
- // print code
- cnode := &printer.CommentedNode{Node: eg.Code, Comments: eg.Comments}
- code := node_htmlFunc(info, cnode, true)
- out := eg.Output
- wholeFile := true
-
- // Additional formatting if this is a function body.
- if n := len(code); n >= 2 && code[0] == '{' && code[n-1] == '}' {
- wholeFile = false
- // remove surrounding braces
- code = code[1 : n-1]
- // unindent
- code = strings.Replace(code, "\n ", "\n", -1)
- // remove output comment
- if loc := exampleOutputRx.FindStringIndex(code); loc != nil {
- code = strings.TrimSpace(code[:loc[0]])
- }
- }
-
- // Write out the playground code in standard Go style
- // (use tabs, no comment highlight, etc).
- play := ""
- if eg.Play != nil && *showPlayground {
- var buf bytes.Buffer
- if err := format.Node(&buf, info.FSet, eg.Play); err != nil {
- log.Print(err)
- } else {
- play = buf.String()
- }
- }
-
- // Drop output, as the output comment will appear in the code.
- if wholeFile && play == "" {
- out = ""
- }
-
- err := exampleHTML.Execute(&buf, struct {
- Name, Doc, Code, Play, Output string
- }{eg.Name, eg.Doc, code, play, out})
- if err != nil {
- log.Print(err)
- }
- }
- return buf.String()
-}
-
-// example_nameFunc takes an example function name and returns its display
-// name. For example, "Foo_Bar_quux" becomes "Foo.Bar (Quux)".
-func example_nameFunc(s string) string {
- name, suffix := splitExampleName(s)
- // replace _ with . for method names
- name = strings.Replace(name, "_", ".", 1)
- // use "Package" if no name provided
- if name == "" {
- name = "Package"
- }
- return name + suffix
-}
-
-// example_suffixFunc takes an example function name and returns its suffix in
-// parenthesized form. For example, "Foo_Bar_quux" becomes " (Quux)".
-func example_suffixFunc(name string) string {
- _, suffix := splitExampleName(name)
- return suffix
-}
-
-func noteTitle(note string) string {
- return strings.Title(strings.ToLower(note))
-}
-
-func splitExampleName(s string) (name, suffix string) {
- i := strings.LastIndex(s, "_")
- if 0 <= i && i < len(s)-1 && !startsWithUppercase(s[i+1:]) {
- name = s[:i]
- suffix = " (" + strings.Title(s[i+1:]) + ")"
- return
- }
- name = s
- return
-}
-
-func pkgLinkFunc(path string) string {
- relpath := path[1:]
- // because of the irregular mapping under goroot
- // we need to correct certain relative paths
- relpath = strings.TrimPrefix(relpath, "src/pkg/")
- return pkgHandler.pattern[1:] + relpath // remove trailing '/' for relative URL
-}
-
-// n must be an ast.Node or a *doc.Note
-func posLink_urlFunc(info *PageInfo, n interface{}) string {
- var pos, end token.Pos
-
- switch n := n.(type) {
- case ast.Node:
- pos = n.Pos()
- end = n.End()
- case *doc.Note:
- pos = n.Pos
- end = n.End
- default:
- panic(fmt.Sprintf("wrong type for posLink_url template formatter: %T", n))
- }
-
- var relpath string
- var line int
- var low, high int // selection offset range
-
- if pos.IsValid() {
- p := info.FSet.Position(pos)
- relpath = p.Filename
- line = p.Line
- low = p.Offset
- }
- if end.IsValid() {
- high = info.FSet.Position(end).Offset
- }
-
- var buf bytes.Buffer
- template.HTMLEscape(&buf, []byte(relpath))
- // selection ranges are of form "s=low:high"
- if low < high {
- fmt.Fprintf(&buf, "?s=%d:%d", low, high) // no need for URL escaping
- // if we have a selection, position the page
- // such that the selection is a bit below the top
- line -= 10
- if line < 1 {
- line = 1
- }
- }
- // line id's in html-printed source are of the
- // form "L%d" where %d stands for the line number
- if line > 0 {
- fmt.Fprintf(&buf, "#L%d", line) // no need for URL escaping
- }
-
- return buf.String()
-}
-
-func srcLinkFunc(s string) string {
- return pathpkg.Clean("/" + s)
-}
-
-// fmap describes the template functions installed with all godoc templates.
-// Convention: template function names ending in "_html" or "_url" produce
-// HTML- or URL-escaped strings; all other function results may
-// require explicit escaping in the template.
-var fmap = template.FuncMap{
- // various helpers
- "filename": filenameFunc,
- "repeat": strings.Repeat,
-
- // access to FileInfos (directory listings)
- "fileInfoName": fileInfoNameFunc,
- "fileInfoTime": fileInfoTimeFunc,
-
- // access to search result information
- "infoKind_html": infoKind_htmlFunc,
- "infoLine": infoLineFunc,
- "infoSnippet_html": infoSnippet_htmlFunc,
-
- // formatting of AST nodes
- "node": nodeFunc,
- "node_html": node_htmlFunc,
- "comment_html": comment_htmlFunc,
- "comment_text": comment_textFunc,
-
- // support for URL attributes
- "pkgLink": pkgLinkFunc,
- "srcLink": srcLinkFunc,
- "posLink_url": posLink_urlFunc,
-
- // formatting of Examples
- "example_html": example_htmlFunc,
- "example_text": example_textFunc,
- "example_name": example_nameFunc,
- "example_suffix": example_suffixFunc,
-
- // formatting of Notes
- "noteTitle": noteTitle,
-}
-
-func readTemplate(name string) *template.Template {
- path := "lib/godoc/" + name
-
- // use underlying file system fs to read the template file
- // (cannot use template ParseFile functions directly)
- data, err := ReadFile(fs, path)
- if err != nil {
- log.Fatal("readTemplate: ", err)
- }
- // be explicit with errors (for app engine use)
- t, err := template.New(name).Funcs(fmap).Parse(string(data))
- if err != nil {
- log.Fatal("readTemplate: ", err)
- }
- return t
-}
-
-var (
- codewalkHTML,
- codewalkdirHTML,
- dirlistHTML,
- errorHTML,
- exampleHTML,
- godocHTML,
- packageHTML,
- packageText,
- searchHTML,
- searchText,
- searchDescXML *template.Template
-)
-
-func readTemplates() {
- // have to delay until after flags processing since paths depend on goroot
- codewalkHTML = readTemplate("codewalk.html")
- codewalkdirHTML = readTemplate("codewalkdir.html")
- dirlistHTML = readTemplate("dirlist.html")
- errorHTML = readTemplate("error.html")
- exampleHTML = readTemplate("example.html")
- godocHTML = readTemplate("godoc.html")
- packageHTML = readTemplate("package.html")
- packageText = readTemplate("package.txt")
- searchHTML = readTemplate("search.html")
- searchText = readTemplate("search.txt")
- searchDescXML = readTemplate("opensearch.xml")
-}
-
-// ----------------------------------------------------------------------------
-// Generic HTML wrapper
-
-// Page describes the contents of the top-level godoc webpage.
-type Page struct {
- Title string
- Tabtitle string
- Subtitle string
- Query string
- Body []byte
-
- // filled in by servePage
- SearchBox bool
- Playground bool
- Version string
-}
-
-func servePage(w http.ResponseWriter, page Page) {
- if page.Tabtitle == "" {
- page.Tabtitle = page.Title
- }
- page.SearchBox = *indexEnabled
- page.Playground = *showPlayground
- page.Version = runtime.Version()
- if err := godocHTML.Execute(w, page); err != nil {
- log.Printf("godocHTML.Execute: %s", err)
- }
-}
-
-func serveText(w http.ResponseWriter, text []byte) {
- w.Header().Set("Content-Type", "text/plain; charset=utf-8")
- w.Write(text)
-}
-
-// ----------------------------------------------------------------------------
-// Files
-
-var (
- doctype = []byte("<!DOCTYPE ")
- jsonStart = []byte("<!--{")
- jsonEnd = []byte("}-->")
-)
-
-func serveHTMLDoc(w http.ResponseWriter, r *http.Request, abspath, relpath string) {
- // get HTML body contents
- src, err := ReadFile(fs, abspath)
- if err != nil {
- log.Printf("ReadFile: %s", err)
- serveError(w, r, relpath, err)
- return
- }
-
- // if it begins with "<!DOCTYPE " assume it is standalone
- // html that doesn't need the template wrapping.
- if bytes.HasPrefix(src, doctype) {
- w.Write(src)
- return
- }
-
- // if it begins with a JSON blob, read in the metadata.
- meta, src, err := extractMetadata(src)
- if err != nil {
- log.Printf("decoding metadata %s: %v", relpath, err)
- }
-
- // evaluate as template if indicated
- if meta.Template {
- tmpl, err := template.New("main").Funcs(templateFuncs).Parse(string(src))
- if err != nil {
- log.Printf("parsing template %s: %v", relpath, err)
- serveError(w, r, relpath, err)
- return
- }
- var buf bytes.Buffer
- if err := tmpl.Execute(&buf, nil); err != nil {
- log.Printf("executing template %s: %v", relpath, err)
- serveError(w, r, relpath, err)
- return
- }
- src = buf.Bytes()
- }
-
- // if it's the language spec, add tags to EBNF productions
- if strings.HasSuffix(abspath, "go_spec.html") {
- var buf bytes.Buffer
- Linkify(&buf, src)
- src = buf.Bytes()
- }
-
- servePage(w, Page{
- Title: meta.Title,
- Subtitle: meta.Subtitle,
- Body: src,
- })
-}
-
-func applyTemplate(t *template.Template, name string, data interface{}) []byte {
- var buf bytes.Buffer
- if err := t.Execute(&buf, data); err != nil {
- log.Printf("%s.Execute: %s", name, err)
- }
- return buf.Bytes()
-}
-
-func redirect(w http.ResponseWriter, r *http.Request) (redirected bool) {
- canonical := pathpkg.Clean(r.URL.Path)
- if !strings.HasSuffix(canonical, "/") {
- canonical += "/"
- }
- if r.URL.Path != canonical {
- url := *r.URL
- url.Path = canonical
- http.Redirect(w, r, url.String(), http.StatusMovedPermanently)
- redirected = true
- }
- return
-}
-
-func redirectFile(w http.ResponseWriter, r *http.Request) (redirected bool) {
- c := pathpkg.Clean(r.URL.Path)
- c = strings.TrimRight(c, "/")
- if r.URL.Path != c {
- url := *r.URL
- url.Path = c
- http.Redirect(w, r, url.String(), http.StatusMovedPermanently)
- redirected = true
- }
- return
-}
-
-func serveTextFile(w http.ResponseWriter, r *http.Request, abspath, relpath, title string) {
- src, err := ReadFile(fs, abspath)
- if err != nil {
- log.Printf("ReadFile: %s", err)
- serveError(w, r, relpath, err)
- return
- }
-
- if r.FormValue("m") == "text" {
- serveText(w, src)
- return
- }
-
- var buf bytes.Buffer
- buf.WriteString("<pre>")
- FormatText(&buf, src, 1, pathpkg.Ext(abspath) == ".go", r.FormValue("h"), rangeSelection(r.FormValue("s")))
- buf.WriteString("</pre>")
- fmt.Fprintf(&buf, `<p><a href="/%s?m=text">View as plain text</a></p>`, htmlpkg.EscapeString(relpath))
-
- servePage(w, Page{
- Title: title + " " + relpath,
- Tabtitle: relpath,
- Body: buf.Bytes(),
- })
-}
-
-func serveDirectory(w http.ResponseWriter, r *http.Request, abspath, relpath string) {
- if redirect(w, r) {
- return
- }
-
- list, err := fs.ReadDir(abspath)
- if err != nil {
- serveError(w, r, relpath, err)
- return
- }
-
- servePage(w, Page{
- Title: "Directory " + relpath,
- Tabtitle: relpath,
- Body: applyTemplate(dirlistHTML, "dirlistHTML", list),
- })
-}
-
-func serveFile(w http.ResponseWriter, r *http.Request) {
- relpath := r.URL.Path
-
- // Check to see if we need to redirect or serve another file.
- if m := metadataFor(relpath); m != nil {
- if m.Path != relpath {
- // Redirect to canonical path.
- http.Redirect(w, r, m.Path, http.StatusMovedPermanently)
- return
- }
- // Serve from the actual filesystem path.
- relpath = m.filePath
- }
-
- abspath := relpath
- relpath = relpath[1:] // strip leading slash
-
- switch pathpkg.Ext(relpath) {
- case ".html":
- if strings.HasSuffix(relpath, "/index.html") {
- // We'll show index.html for the directory.
- // Use the dir/ version as canonical instead of dir/index.html.
- http.Redirect(w, r, r.URL.Path[0:len(r.URL.Path)-len("index.html")], http.StatusMovedPermanently)
- return
- }
- serveHTMLDoc(w, r, abspath, relpath)
- return
-
- case ".go":
- serveTextFile(w, r, abspath, relpath, "Source file")
- return
- }
-
- dir, err := fs.Lstat(abspath)
- if err != nil {
- log.Print(err)
- serveError(w, r, relpath, err)
- return
- }
-
- if dir != nil && dir.IsDir() {
- if redirect(w, r) {
- return
- }
- if index := pathpkg.Join(abspath, "index.html"); isTextFile(index) {
- serveHTMLDoc(w, r, index, index)
- return
- }
- serveDirectory(w, r, abspath, relpath)
- return
- }
-
- if isTextFile(abspath) {
- if redirectFile(w, r) {
- return
- }
- serveTextFile(w, r, abspath, relpath, "Text file")
- return
- }
-
- fileServer.ServeHTTP(w, r)
-}
-
-func serveSearchDesc(w http.ResponseWriter, r *http.Request) {
- w.Header().Set("Content-Type", "application/opensearchdescription+xml")
- data := map[string]interface{}{
- "BaseURL": fmt.Sprintf("http://%s", r.Host),
- }
- if err := searchDescXML.Execute(w, &data); err != nil {
- log.Printf("searchDescXML.Execute: %s", err)
- }
-}
-
-// ----------------------------------------------------------------------------
-// Packages
-
-// Fake relative package path for built-ins. Documentation for all globals
-// (not just exported ones) will be shown for packages in this directory.
-const builtinPkgPath = "builtin"
-
-type PageInfoMode uint
-
-const (
- noFiltering PageInfoMode = 1 << iota // do not filter exports
- allMethods // show all embedded methods
- showSource // show source code, do not extract documentation
- noHtml // show result in textual form, do not generate HTML
- flatDir // show directory in a flat (non-indented) manner
-)
-
-// modeNames defines names for each PageInfoMode flag.
-var modeNames = map[string]PageInfoMode{
- "all": noFiltering,
- "methods": allMethods,
- "src": showSource,
- "text": noHtml,
- "flat": flatDir,
-}
-
-// getPageInfoMode computes the PageInfoMode flags by analyzing the request
-// URL form value "m". It is value is a comma-separated list of mode names
-// as defined by modeNames (e.g.: m=src,text).
-func getPageInfoMode(r *http.Request) PageInfoMode {
- var mode PageInfoMode
- for _, k := range strings.Split(r.FormValue("m"), ",") {
- if m, found := modeNames[strings.TrimSpace(k)]; found {
- mode |= m
- }
- }
- return adjustPageInfoMode(r, mode)
-}
-
-// Specialized versions of godoc may adjust the PageInfoMode by overriding
-// this variable.
-var adjustPageInfoMode = func(_ *http.Request, mode PageInfoMode) PageInfoMode {
- return mode
-}
-
-// remoteSearchURL returns the search URL for a given query as needed by
-// remoteSearch. If html is set, an html result is requested; otherwise
-// the result is in textual form.
-// Adjust this function as necessary if modeNames or FormValue parameters
-// change.
-func remoteSearchURL(query string, html bool) string {
- s := "/search?m=text&q="
- if html {
- s = "/search?q="
- }
- return s + url.QueryEscape(query)
-}
-
-type PageInfo struct {
- Dirname string // directory containing the package
- Err error // error or nil
-
- // package info
- FSet *token.FileSet // nil if no package documentation
- PDoc *doc.Package // nil if no package documentation
- Examples []*doc.Example // nil if no example code
- Notes map[string][]*doc.Note // nil if no package Notes
- PAst *ast.File // nil if no AST with package exports
- IsMain bool // true for package main
-
- // directory info
- Dirs *DirList // nil if no directory information
- DirTime time.Time // directory time stamp
- DirFlat bool // if set, show directory in a flat (non-indented) manner
-}
-
-func (info *PageInfo) IsEmpty() bool {
- return info.Err != nil || info.PAst == nil && info.PDoc == nil && info.Dirs == nil
-}
-
-type docServer struct {
- pattern string // url pattern; e.g. "/pkg/"
- fsRoot string // file system root to which the pattern is mapped
-}
-
-// fsReadDir implements ReadDir for the go/build package.
-func fsReadDir(dir string) ([]os.FileInfo, error) {
- return fs.ReadDir(filepath.ToSlash(dir))
-}
-
-// fsOpenFile implements OpenFile for the go/build package.
-func fsOpenFile(name string) (r io.ReadCloser, err error) {
- data, err := ReadFile(fs, filepath.ToSlash(name))
- if err != nil {
- return nil, err
- }
- return ioutil.NopCloser(bytes.NewReader(data)), nil
-}
-
-// packageExports is a local implementation of ast.PackageExports
-// which correctly updates each package file's comment list.
-// (The ast.PackageExports signature is frozen, hence the local
-// implementation).
-//
-func packageExports(fset *token.FileSet, pkg *ast.Package) {
- for _, src := range pkg.Files {
- cmap := ast.NewCommentMap(fset, src, src.Comments)
- ast.FileExports(src)
- src.Comments = cmap.Filter(src).Comments()
- }
-}
-
-// addNames adds the names declared by decl to the names set.
-// Method names are added in the form ReceiverTypeName_Method.
-func addNames(names map[string]bool, decl ast.Decl) {
- switch d := decl.(type) {
- case *ast.FuncDecl:
- name := d.Name.Name
- if d.Recv != nil {
- var typeName string
- switch r := d.Recv.List[0].Type.(type) {
- case *ast.StarExpr:
- typeName = r.X.(*ast.Ident).Name
- case *ast.Ident:
- typeName = r.Name
- }
- name = typeName + "_" + name
- }
- names[name] = true
- case *ast.GenDecl:
- for _, spec := range d.Specs {
- switch s := spec.(type) {
- case *ast.TypeSpec:
- names[s.Name.Name] = true
- case *ast.ValueSpec:
- for _, id := range s.Names {
- names[id.Name] = true
- }
- }
- }
- }
-}
-
-// globalNames returns a set of the names declared by all package-level
-// declarations. Method names are returned in the form Receiver_Method.
-func globalNames(pkg *ast.Package) map[string]bool {
- names := make(map[string]bool)
- for _, file := range pkg.Files {
- for _, decl := range file.Decls {
- addNames(names, decl)
- }
- }
- return names
-}
-
-// collectExamples collects examples for pkg from testfiles.
-func collectExamples(pkg *ast.Package, testfiles map[string]*ast.File) []*doc.Example {
- var files []*ast.File
- for _, f := range testfiles {
- files = append(files, f)
- }
-
- var examples []*doc.Example
- globals := globalNames(pkg)
- for _, e := range doc.Examples(files...) {
- name := stripExampleSuffix(e.Name)
- if name == "" || globals[name] {
- examples = append(examples, e)
- } else {
- log.Printf("skipping example 'Example%s' because '%s' is not a known function or type", e.Name, e.Name)
- }
- }
-
- return examples
-}
-
-// poorMansImporter returns a (dummy) package object named
-// by the last path component of the provided package path
-// (as is the convention for packages). This is sufficient
-// to resolve package identifiers without doing an actual
-// import. It never returns an error.
-//
-func poorMansImporter(imports map[string]*ast.Object, path string) (*ast.Object, error) {
- pkg := imports[path]
- if pkg == nil {
- // note that strings.LastIndex returns -1 if there is no "/"
- pkg = ast.NewObj(ast.Pkg, path[strings.LastIndex(path, "/")+1:])
- pkg.Data = ast.NewScope(nil) // required by ast.NewPackage for dot-import
- imports[path] = pkg
- }
- return pkg, nil
-}
-
-// getPageInfo returns the PageInfo for a package directory abspath. If the
-// parameter genAST is set, an AST containing only the package exports is
-// computed (PageInfo.PAst), otherwise package documentation (PageInfo.Doc)
-// is extracted from the AST. If there is no corresponding package in the
-// directory, PageInfo.PAst and PageInfo.PDoc are nil. If there are no sub-
-// directories, PageInfo.Dirs is nil. If an error occurred, PageInfo.Err is
-// set to the respective error but the error is not logged.
-//
-func (h *docServer) getPageInfo(abspath, relpath string, mode PageInfoMode) *PageInfo {
- info := &PageInfo{Dirname: abspath}
-
- // Restrict to the package files that would be used when building
- // the package on this system. This makes sure that if there are
- // separate implementations for, say, Windows vs Unix, we don't
- // jumble them all together.
- // Note: Uses current binary's GOOS/GOARCH.
- // To use different pair, such as if we allowed the user to choose,
- // set ctxt.GOOS and ctxt.GOARCH before calling ctxt.ImportDir.
- ctxt := build.Default
- ctxt.IsAbsPath = pathpkg.IsAbs
- ctxt.ReadDir = fsReadDir
- ctxt.OpenFile = fsOpenFile
- pkginfo, err := ctxt.ImportDir(abspath, 0)
- // continue if there are no Go source files; we still want the directory info
- if _, nogo := err.(*build.NoGoError); err != nil && !nogo {
- info.Err = err
- return info
- }
-
- // collect package files
- pkgname := pkginfo.Name
- pkgfiles := append(pkginfo.GoFiles, pkginfo.CgoFiles...)
- if len(pkgfiles) == 0 {
- // Commands written in C have no .go files in the build.
- // Instead, documentation may be found in an ignored file.
- // The file may be ignored via an explicit +build ignore
- // constraint (recommended), or by defining the package
- // documentation (historic).
- pkgname = "main" // assume package main since pkginfo.Name == ""
- pkgfiles = pkginfo.IgnoredGoFiles
- }
-
- // get package information, if any
- if len(pkgfiles) > 0 {
- // build package AST
- fset := token.NewFileSet()
- files, err := parseFiles(fset, abspath, pkgfiles)
- if err != nil {
- info.Err = err
- return info
- }
-
- // ignore any errors - they are due to unresolved identifiers
- pkg, _ := ast.NewPackage(fset, files, poorMansImporter, nil)
-
- // extract package documentation
- info.FSet = fset
- if mode&showSource == 0 {
- // show extracted documentation
- var m doc.Mode
- if mode&noFiltering != 0 {
- m = doc.AllDecls
- }
- if mode&allMethods != 0 {
- m |= doc.AllMethods
- }
- info.PDoc = doc.New(pkg, pathpkg.Clean(relpath), m) // no trailing '/' in importpath
-
- // collect examples
- testfiles := append(pkginfo.TestGoFiles, pkginfo.XTestGoFiles...)
- files, err = parseFiles(fset, abspath, testfiles)
- if err != nil {
- log.Println("parsing examples:", err)
- }
- info.Examples = collectExamples(pkg, files)
-
- // collect any notes that we want to show
- if info.PDoc.Notes != nil {
- // could regexp.Compile only once per godoc, but probably not worth it
- if rx, err := regexp.Compile(*notes); err == nil {
- for m, n := range info.PDoc.Notes {
- if rx.MatchString(m) {
- if info.Notes == nil {
- info.Notes = make(map[string][]*doc.Note)
- }
- info.Notes[m] = n
- }
- }
- }
- }
-
- } else {
- // show source code
- // TODO(gri) Consider eliminating export filtering in this mode,
- // or perhaps eliminating the mode altogether.
- if mode&noFiltering == 0 {
- packageExports(fset, pkg)
- }
- info.PAst = ast.MergePackageFiles(pkg, 0)
- }
- info.IsMain = pkgname == "main"
- }
-
- // get directory information, if any
- var dir *Directory
- var timestamp time.Time
- if tree, ts := fsTree.get(); tree != nil && tree.(*Directory) != nil {
- // directory tree is present; lookup respective directory
- // (may still fail if the file system was updated and the
- // new directory tree has not yet been computed)
- dir = tree.(*Directory).lookup(abspath)
- timestamp = ts
- }
- if dir == nil {
- // no directory tree present (too early after startup or
- // command-line mode); compute one level for this page
- // note: cannot use path filter here because in general
- // it doesn't contain the fsTree path
- dir = newDirectory(abspath, 1)
- timestamp = time.Now()
- }
- info.Dirs = dir.listing(true)
- info.DirTime = timestamp
- info.DirFlat = mode&flatDir != 0
-
- return info
-}
-
-func (h *docServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- if redirect(w, r) {
- return
- }
-
- relpath := pathpkg.Clean(r.URL.Path[len(h.pattern):])
- abspath := pathpkg.Join(h.fsRoot, relpath)
- mode := getPageInfoMode(r)
- if relpath == builtinPkgPath {
- mode = noFiltering
- }
- info := h.getPageInfo(abspath, relpath, mode)
- if info.Err != nil {
- log.Print(info.Err)
- serveError(w, r, relpath, info.Err)
- return
- }
-
- if mode&noHtml != 0 {
- serveText(w, applyTemplate(packageText, "packageText", info))
- return
- }
-
- var tabtitle, title, subtitle string
- switch {
- case info.PAst != nil:
- tabtitle = info.PAst.Name.Name
- case info.PDoc != nil:
- tabtitle = info.PDoc.Name
- default:
- tabtitle = info.Dirname
- title = "Directory "
- if *showTimestamps {
- subtitle = "Last update: " + info.DirTime.String()
- }
- }
- if title == "" {
- if info.IsMain {
- // assume that the directory name is the command name
- _, tabtitle = pathpkg.Split(relpath)
- title = "Command "
- } else {
- title = "Package "
- }
- }
- title += tabtitle
-
- // special cases for top-level package/command directories
- switch tabtitle {
- case "/src/pkg":
- tabtitle = "Packages"
- case "/src/cmd":
- tabtitle = "Commands"
- }
-
- servePage(w, Page{
- Title: title,
- Tabtitle: tabtitle,
- Subtitle: subtitle,
- Body: applyTemplate(packageHTML, "packageHTML", info),
- })
-}
-
-// ----------------------------------------------------------------------------
-// Search
-
-var searchIndex RWValue
-
-type SearchResult struct {
- Query string
- Alert string // error or warning message
-
- // identifier matches
- Pak HitList // packages matching Query
- Hit *LookupResult // identifier matches of Query
- Alt *AltWords // alternative identifiers to look for
-
- // textual matches
- Found int // number of textual occurrences found
- Textual []FileLines // textual matches of Query
- Complete bool // true if all textual occurrences of Query are reported
-}
-
-func lookup(query string) (result SearchResult) {
- result.Query = query
-
- index, timestamp := searchIndex.get()
- if index != nil {
- index := index.(*Index)
-
- // identifier search
- var err error
- result.Pak, result.Hit, result.Alt, err = index.Lookup(query)
- if err != nil && *maxResults <= 0 {
- // ignore the error if full text search is enabled
- // since the query may be a valid regular expression
- result.Alert = "Error in query string: " + err.Error()
- return
- }
-
- // full text search
- if *maxResults > 0 && query != "" {
- rx, err := regexp.Compile(query)
- if err != nil {
- result.Alert = "Error in query regular expression: " + err.Error()
- return
- }
- // If we get maxResults+1 results we know that there are more than
- // maxResults results and thus the result may be incomplete (to be
- // precise, we should remove one result from the result set, but
- // nobody is going to count the results on the result page).
- result.Found, result.Textual = index.LookupRegexp(rx, *maxResults+1)
- result.Complete = result.Found <= *maxResults
- if !result.Complete {
- result.Found-- // since we looked for maxResults+1
- }
- }
- }
-
- // is the result accurate?
- if *indexEnabled {
- if _, ts := fsModified.get(); timestamp.Before(ts) {
- // The index is older than the latest file system change under godoc's observation.
- result.Alert = "Indexing in progress: result may be inaccurate"
- }
- } else {
- result.Alert = "Search index disabled: no results available"
- }
-
- return
-}
-
-func search(w http.ResponseWriter, r *http.Request) {
- query := strings.TrimSpace(r.FormValue("q"))
- result := lookup(query)
-
- if getPageInfoMode(r)&noHtml != 0 {
- serveText(w, applyTemplate(searchText, "searchText", result))
- return
- }
-
- var title string
- if result.Hit != nil || len(result.Textual) > 0 {
- title = fmt.Sprintf(`Results for query %q`, query)
- } else {
- title = fmt.Sprintf(`No results found for query %q`, query)
- }
-
- servePage(w, Page{
- Title: title,
- Tabtitle: query,
- Query: query,
- Body: applyTemplate(searchHTML, "searchHTML", result),
- })
-}
-
-// ----------------------------------------------------------------------------
-// Documentation Metadata
-
-type Metadata struct {
- Title string
- Subtitle string
- Template bool // execute as template
- Path string // canonical path for this page
- filePath string // filesystem path relative to goroot
-}
-
-// extractMetadata extracts the Metadata from a byte slice.
-// It returns the Metadata value and the remaining data.
-// If no metadata is present the original byte slice is returned.
-//
-func extractMetadata(b []byte) (meta Metadata, tail []byte, err error) {
- tail = b
- if !bytes.HasPrefix(b, jsonStart) {
- return
- }
- end := bytes.Index(b, jsonEnd)
- if end < 0 {
- return
- }
- b = b[len(jsonStart)-1 : end+1] // drop leading <!-- and include trailing }
- if err = json.Unmarshal(b, &meta); err != nil {
- return
- }
- tail = tail[end+len(jsonEnd):]
- return
-}
-
-// updateMetadata scans $GOROOT/doc for HTML files, reads their metadata,
-// and updates the docMetadata map.
-//
-func updateMetadata() {
- metadata := make(map[string]*Metadata)
- var scan func(string) // scan is recursive
- scan = func(dir string) {
- fis, err := fs.ReadDir(dir)
- if err != nil {
- log.Println("updateMetadata:", err)
- return
- }
- for _, fi := range fis {
- name := pathpkg.Join(dir, fi.Name())
- if fi.IsDir() {
- scan(name) // recurse
- continue
- }
- if !strings.HasSuffix(name, ".html") {
- continue
- }
- // Extract metadata from the file.
- b, err := ReadFile(fs, name)
- if err != nil {
- log.Printf("updateMetadata %s: %v", name, err)
- continue
- }
- meta, _, err := extractMetadata(b)
- if err != nil {
- log.Printf("updateMetadata: %s: %v", name, err)
- continue
- }
- // Store relative filesystem path in Metadata.
- meta.filePath = name
- if meta.Path == "" {
- // If no Path, canonical path is actual path.
- meta.Path = meta.filePath
- }
- // Store under both paths.
- metadata[meta.Path] = &meta
- metadata[meta.filePath] = &meta
- }
- }
- scan("/doc")
- docMetadata.set(metadata)
-}
-
-// Send a value on this channel to trigger a metadata refresh.
-// It is buffered so that if a signal is not lost if sent during a refresh.
-//
-var refreshMetadataSignal = make(chan bool, 1)
-
-// refreshMetadata sends a signal to update docMetadata. If a refresh is in
-// progress the metadata will be refreshed again afterward.
-//
-func refreshMetadata() {
- select {
- case refreshMetadataSignal <- true:
- default:
- }
-}
-
-// refreshMetadataLoop runs forever, updating docMetadata when the underlying
-// file system changes. It should be launched in a goroutine by main.
-//
-func refreshMetadataLoop() {
- for {
- <-refreshMetadataSignal
- updateMetadata()
- time.Sleep(10 * time.Second) // at most once every 10 seconds
- }
-}
-
-// metadataFor returns the *Metadata for a given relative path or nil if none
-// exists.
-//
-func metadataFor(relpath string) *Metadata {
- if m, _ := docMetadata.get(); m != nil {
- meta := m.(map[string]*Metadata)
- // If metadata for this relpath exists, return it.
- if p := meta[relpath]; p != nil {
- return p
- }
- // Try with or without trailing slash.
- if strings.HasSuffix(relpath, "/") {
- relpath = relpath[:len(relpath)-1]
- } else {
- relpath = relpath + "/"
- }
- return meta[relpath]
- }
- return nil
-}
-
-// ----------------------------------------------------------------------------
-// Indexer
-
-// invalidateIndex should be called whenever any of the file systems
-// under godoc's observation change so that the indexer is kicked on.
-//
-func invalidateIndex() {
- fsModified.set(nil)
- refreshMetadata()
-}
-
-// indexUpToDate() returns true if the search index is not older
-// than any of the file systems under godoc's observation.
-//
-func indexUpToDate() bool {
- _, fsTime := fsModified.get()
- _, siTime := searchIndex.get()
- return !fsTime.After(siTime)
-}
-
-// feedDirnames feeds the directory names of all directories
-// under the file system given by root to channel c.
-//
-func feedDirnames(root *RWValue, c chan<- string) {
- if dir, _ := root.get(); dir != nil {
- for d := range dir.(*Directory).iter(false) {
- c <- d.Path
- }
- }
-}
-
-// fsDirnames() returns a channel sending all directory names
-// of all the file systems under godoc's observation.
-//
-func fsDirnames() <-chan string {
- c := make(chan string, 256) // buffered for fewer context switches
- go func() {
- feedDirnames(&fsTree, c)
- close(c)
- }()
- return c
-}
-
-func readIndex(filenames string) error {
- matches, err := filepath.Glob(filenames)
- if err != nil {
- return err
- } else if matches == nil {
- return fmt.Errorf("no index files match %q", filenames)
- }
- sort.Strings(matches) // make sure files are in the right order
- files := make([]io.Reader, 0, len(matches))
- for _, filename := range matches {
- f, err := os.Open(filename)
- if err != nil {
- return err
- }
- defer f.Close()
- files = append(files, f)
- }
- x := new(Index)
- if err := x.Read(io.MultiReader(files...)); err != nil {
- return err
- }
- searchIndex.set(x)
- return nil
-}
-
-func updateIndex() {
- if *verbose {
- log.Printf("updating index...")
- }
- start := time.Now()
- index := NewIndex(fsDirnames(), *maxResults > 0, *indexThrottle)
- stop := time.Now()
- searchIndex.set(index)
- if *verbose {
- secs := stop.Sub(start).Seconds()
- stats := index.Stats()
- log.Printf("index updated (%gs, %d bytes of source, %d files, %d lines, %d unique words, %d spots)",
- secs, stats.Bytes, stats.Files, stats.Lines, stats.Words, stats.Spots)
- }
- memstats := new(runtime.MemStats)
- runtime.ReadMemStats(memstats)
- log.Printf("before GC: bytes = %d footprint = %d", memstats.HeapAlloc, memstats.Sys)
- runtime.GC()
- runtime.ReadMemStats(memstats)
- log.Printf("after GC: bytes = %d footprint = %d", memstats.HeapAlloc, memstats.Sys)
-}
-
-func indexer() {
- // initialize the index from disk if possible
- if *indexFiles != "" {
- if err := readIndex(*indexFiles); err != nil {
- log.Printf("error reading index: %s", err)
- }
- }
-
- // repeatedly update the index when it goes out of date
- for {
- if !indexUpToDate() {
- // index possibly out of date - make a new one
- updateIndex()
- }
- delay := 60 * time.Second // by default, try every 60s
- if *testDir != "" {
- // in test mode, try once a second for fast startup
- delay = 1 * time.Second
- }
- time.Sleep(delay)
- }
-}
diff --git a/src/cmd/godoc/index.go b/src/cmd/godoc/index.go
deleted file mode 100644
index 8198fca0d..000000000
--- a/src/cmd/godoc/index.go
+++ /dev/null
@@ -1,1073 +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.
-
-// This file contains the infrastructure to create an
-// identifier and full-text index for a set of Go files.
-//
-// Algorithm for identifier index:
-// - traverse all .go files of the file tree specified by root
-// - for each identifier (word) encountered, collect all occurrences (spots)
-// into a list; this produces a list of spots for each word
-// - reduce the lists: from a list of spots to a list of FileRuns,
-// and from a list of FileRuns into a list of PakRuns
-// - make a HitList from the PakRuns
-//
-// Details:
-// - keep two lists per word: one containing package-level declarations
-// that have snippets, and one containing all other spots
-// - keep the snippets in a separate table indexed by snippet index
-// and store the snippet index in place of the line number in a SpotInfo
-// (the line number for spots with snippets is stored in the snippet)
-// - at the end, create lists of alternative spellings for a given
-// word
-//
-// Algorithm for full text index:
-// - concatenate all source code in a byte buffer (in memory)
-// - add the files to a file set in lockstep as they are added to the byte
-// buffer such that a byte buffer offset corresponds to the Pos value for
-// that file location
-// - create a suffix array from the concatenated sources
-//
-// String lookup in full text index:
-// - use the suffix array to lookup a string's offsets - the offsets
-// correspond to the Pos values relative to the file set
-// - translate the Pos values back into file and line information and
-// sort the result
-
-package main
-
-import (
- "bufio"
- "bytes"
- "encoding/gob"
- "errors"
- "go/ast"
- "go/parser"
- "go/token"
- "index/suffixarray"
- "io"
- "os"
- pathpkg "path"
- "regexp"
- "sort"
- "strings"
- "time"
- "unicode"
-)
-
-// ----------------------------------------------------------------------------
-// InterfaceSlice is a helper type for sorting interface
-// slices according to some slice-specific sort criteria.
-
-type Comparer func(x, y interface{}) bool
-
-type InterfaceSlice struct {
- slice []interface{}
- less Comparer
-}
-
-func (p *InterfaceSlice) Len() int { return len(p.slice) }
-func (p *InterfaceSlice) Less(i, j int) bool { return p.less(p.slice[i], p.slice[j]) }
-func (p *InterfaceSlice) Swap(i, j int) { p.slice[i], p.slice[j] = p.slice[j], p.slice[i] }
-
-// ----------------------------------------------------------------------------
-// RunList
-
-// A RunList is a list of entries that can be sorted according to some
-// criteria. A RunList may be compressed by grouping "runs" of entries
-// which are equal (according to the sort critera) into a new RunList of
-// runs. For instance, a RunList containing pairs (x, y) may be compressed
-// into a RunList containing pair runs (x, {y}) where each run consists of
-// a list of y's with the same x.
-type RunList []interface{}
-
-func (h RunList) sort(less Comparer) {
- sort.Sort(&InterfaceSlice{h, less})
-}
-
-// Compress entries which are the same according to a sort criteria
-// (specified by less) into "runs".
-func (h RunList) reduce(less Comparer, newRun func(h RunList) interface{}) RunList {
- if len(h) == 0 {
- return nil
- }
- // len(h) > 0
-
- // create runs of entries with equal values
- h.sort(less)
-
- // for each run, make a new run object and collect them in a new RunList
- var hh RunList
- i, x := 0, h[0]
- for j, y := range h {
- if less(x, y) {
- hh = append(hh, newRun(h[i:j]))
- i, x = j, h[j] // start a new run
- }
- }
- // add final run, if any
- if i < len(h) {
- hh = append(hh, newRun(h[i:]))
- }
-
- return hh
-}
-
-// ----------------------------------------------------------------------------
-// SpotInfo
-
-// A SpotInfo value describes a particular identifier spot in a given file;
-// It encodes three values: the SpotKind (declaration or use), a line or
-// snippet index "lori", and whether it's a line or index.
-//
-// The following encoding is used:
-//
-// bits 32 4 1 0
-// value [lori|kind|isIndex]
-//
-type SpotInfo uint32
-
-// SpotKind describes whether an identifier is declared (and what kind of
-// declaration) or used.
-type SpotKind uint32
-
-const (
- PackageClause SpotKind = iota
- ImportDecl
- ConstDecl
- TypeDecl
- VarDecl
- FuncDecl
- MethodDecl
- Use
- nKinds
-)
-
-func init() {
- // sanity check: if nKinds is too large, the SpotInfo
- // accessor functions may need to be updated
- if nKinds > 8 {
- panic("internal error: nKinds > 8")
- }
-}
-
-// makeSpotInfo makes a SpotInfo.
-func makeSpotInfo(kind SpotKind, lori int, isIndex bool) SpotInfo {
- // encode lori: bits [4..32)
- x := SpotInfo(lori) << 4
- if int(x>>4) != lori {
- // lori value doesn't fit - since snippet indices are
- // most certainly always smaller then 1<<28, this can
- // only happen for line numbers; give it no line number (= 0)
- x = 0
- }
- // encode kind: bits [1..4)
- x |= SpotInfo(kind) << 1
- // encode isIndex: bit 0
- if isIndex {
- x |= 1
- }
- return x
-}
-
-func (x SpotInfo) Kind() SpotKind { return SpotKind(x >> 1 & 7) }
-func (x SpotInfo) Lori() int { return int(x >> 4) }
-func (x SpotInfo) IsIndex() bool { return x&1 != 0 }
-
-// ----------------------------------------------------------------------------
-// KindRun
-
-// Debugging support. Disable to see multiple entries per line.
-const removeDuplicates = true
-
-// A KindRun is a run of SpotInfos of the same kind in a given file.
-// The kind (3 bits) is stored in each SpotInfo element; to find the
-// kind of a KindRun, look at any of it's elements.
-type KindRun []SpotInfo
-
-// KindRuns are sorted by line number or index. Since the isIndex bit
-// is always the same for all infos in one list we can compare lori's.
-func (k KindRun) Len() int { return len(k) }
-func (k KindRun) Less(i, j int) bool { return k[i].Lori() < k[j].Lori() }
-func (k KindRun) Swap(i, j int) { k[i], k[j] = k[j], k[i] }
-
-// FileRun contents are sorted by Kind for the reduction into KindRuns.
-func lessKind(x, y interface{}) bool { return x.(SpotInfo).Kind() < y.(SpotInfo).Kind() }
-
-// newKindRun allocates a new KindRun from the SpotInfo run h.
-func newKindRun(h RunList) interface{} {
- run := make(KindRun, len(h))
- for i, x := range h {
- run[i] = x.(SpotInfo)
- }
-
- // Spots were sorted by file and kind to create this run.
- // Within this run, sort them by line number or index.
- sort.Sort(run)
-
- if removeDuplicates {
- // Since both the lori and kind field must be
- // same for duplicates, and since the isIndex
- // bit is always the same for all infos in one
- // list we can simply compare the entire info.
- k := 0
- prev := SpotInfo(1<<32 - 1) // an unlikely value
- for _, x := range run {
- if x != prev {
- run[k] = x
- k++
- prev = x
- }
- }
- run = run[0:k]
- }
-
- return run
-}
-
-// ----------------------------------------------------------------------------
-// FileRun
-
-// A Pak describes a Go package.
-type Pak struct {
- Path string // path of directory containing the package
- Name string // package name as declared by package clause
-}
-
-// Paks are sorted by name (primary key) and by import path (secondary key).
-func (p *Pak) less(q *Pak) bool {
- return p.Name < q.Name || p.Name == q.Name && p.Path < q.Path
-}
-
-// A File describes a Go file.
-type File struct {
- Name string // directory-local file name
- Pak *Pak // the package to which the file belongs
-}
-
-// Path returns the file path of f.
-func (f *File) Path() string {
- return pathpkg.Join(f.Pak.Path, f.Name)
-}
-
-// A Spot describes a single occurrence of a word.
-type Spot struct {
- File *File
- Info SpotInfo
-}
-
-// A FileRun is a list of KindRuns belonging to the same file.
-type FileRun struct {
- File *File
- Groups []KindRun
-}
-
-// Spots are sorted by file path for the reduction into FileRuns.
-func lessSpot(x, y interface{}) bool {
- fx := x.(Spot).File
- fy := y.(Spot).File
- // same as "return fx.Path() < fy.Path()" but w/o computing the file path first
- px := fx.Pak.Path
- py := fy.Pak.Path
- return px < py || px == py && fx.Name < fy.Name
-}
-
-// newFileRun allocates a new FileRun from the Spot run h.
-func newFileRun(h RunList) interface{} {
- file := h[0].(Spot).File
-
- // reduce the list of Spots into a list of KindRuns
- h1 := make(RunList, len(h))
- for i, x := range h {
- h1[i] = x.(Spot).Info
- }
- h2 := h1.reduce(lessKind, newKindRun)
-
- // create the FileRun
- groups := make([]KindRun, len(h2))
- for i, x := range h2 {
- groups[i] = x.(KindRun)
- }
- return &FileRun{file, groups}
-}
-
-// ----------------------------------------------------------------------------
-// PakRun
-
-// A PakRun describes a run of *FileRuns of a package.
-type PakRun struct {
- Pak *Pak
- Files []*FileRun
-}
-
-// Sorting support for files within a PakRun.
-func (p *PakRun) Len() int { return len(p.Files) }
-func (p *PakRun) Less(i, j int) bool { return p.Files[i].File.Name < p.Files[j].File.Name }
-func (p *PakRun) Swap(i, j int) { p.Files[i], p.Files[j] = p.Files[j], p.Files[i] }
-
-// FileRuns are sorted by package for the reduction into PakRuns.
-func lessFileRun(x, y interface{}) bool {
- return x.(*FileRun).File.Pak.less(y.(*FileRun).File.Pak)
-}
-
-// newPakRun allocates a new PakRun from the *FileRun run h.
-func newPakRun(h RunList) interface{} {
- pak := h[0].(*FileRun).File.Pak
- files := make([]*FileRun, len(h))
- for i, x := range h {
- files[i] = x.(*FileRun)
- }
- run := &PakRun{pak, files}
- sort.Sort(run) // files were sorted by package; sort them by file now
- return run
-}
-
-// ----------------------------------------------------------------------------
-// HitList
-
-// A HitList describes a list of PakRuns.
-type HitList []*PakRun
-
-// PakRuns are sorted by package.
-func lessPakRun(x, y interface{}) bool { return x.(*PakRun).Pak.less(y.(*PakRun).Pak) }
-
-func reduce(h0 RunList) HitList {
- // reduce a list of Spots into a list of FileRuns
- h1 := h0.reduce(lessSpot, newFileRun)
- // reduce a list of FileRuns into a list of PakRuns
- h2 := h1.reduce(lessFileRun, newPakRun)
- // sort the list of PakRuns by package
- h2.sort(lessPakRun)
- // create a HitList
- h := make(HitList, len(h2))
- for i, p := range h2 {
- h[i] = p.(*PakRun)
- }
- return h
-}
-
-// filter returns a new HitList created by filtering
-// all PakRuns from h that have a matching pakname.
-func (h HitList) filter(pakname string) HitList {
- var hh HitList
- for _, p := range h {
- if p.Pak.Name == pakname {
- hh = append(hh, p)
- }
- }
- return hh
-}
-
-// ----------------------------------------------------------------------------
-// AltWords
-
-type wordPair struct {
- canon string // canonical word spelling (all lowercase)
- alt string // alternative spelling
-}
-
-// An AltWords describes a list of alternative spellings for a
-// canonical (all lowercase) spelling of a word.
-type AltWords struct {
- Canon string // canonical word spelling (all lowercase)
- Alts []string // alternative spelling for the same word
-}
-
-// wordPairs are sorted by their canonical spelling.
-func lessWordPair(x, y interface{}) bool { return x.(*wordPair).canon < y.(*wordPair).canon }
-
-// newAltWords allocates a new AltWords from the *wordPair run h.
-func newAltWords(h RunList) interface{} {
- canon := h[0].(*wordPair).canon
- alts := make([]string, len(h))
- for i, x := range h {
- alts[i] = x.(*wordPair).alt
- }
- return &AltWords{canon, alts}
-}
-
-func (a *AltWords) filter(s string) *AltWords {
- var alts []string
- for _, w := range a.Alts {
- if w != s {
- alts = append(alts, w)
- }
- }
- if len(alts) > 0 {
- return &AltWords{a.Canon, alts}
- }
- return nil
-}
-
-// ----------------------------------------------------------------------------
-// Indexer
-
-// Adjust these flags as seems best.
-const includeMainPackages = true
-const includeTestFiles = true
-
-type IndexResult struct {
- Decls RunList // package-level declarations (with snippets)
- Others RunList // all other occurrences
-}
-
-// Statistics provides statistics information for an index.
-type Statistics struct {
- Bytes int // total size of indexed source files
- Files int // number of indexed source files
- Lines int // number of lines (all files)
- Words int // number of different identifiers
- Spots int // number of identifier occurrences
-}
-
-// An Indexer maintains the data structures and provides the machinery
-// for indexing .go files under a file tree. It implements the path.Visitor
-// interface for walking file trees, and the ast.Visitor interface for
-// walking Go ASTs.
-type Indexer struct {
- fset *token.FileSet // file set for all indexed files
- sources bytes.Buffer // concatenated sources
- packages map[string]*Pak // map of canonicalized *Paks
- words map[string]*IndexResult // RunLists of Spots
- snippets []*Snippet // indices are stored in SpotInfos
- current *token.File // last file added to file set
- file *File // AST for current file
- decl ast.Decl // AST for current decl
- stats Statistics
-}
-
-func (x *Indexer) lookupPackage(path, name string) *Pak {
- // In the source directory tree, more than one package may
- // live in the same directory. For the packages map, construct
- // a key that includes both the directory path and the package
- // name.
- key := path + ":" + name
- pak := x.packages[key]
- if pak == nil {
- pak = &Pak{path, name}
- x.packages[key] = pak
- }
- return pak
-}
-
-func (x *Indexer) addSnippet(s *Snippet) int {
- index := len(x.snippets)
- x.snippets = append(x.snippets, s)
- return index
-}
-
-func (x *Indexer) visitIdent(kind SpotKind, id *ast.Ident) {
- if id != nil {
- lists, found := x.words[id.Name]
- if !found {
- lists = new(IndexResult)
- x.words[id.Name] = lists
- }
-
- if kind == Use || x.decl == nil {
- // not a declaration or no snippet required
- info := makeSpotInfo(kind, x.current.Line(id.Pos()), false)
- lists.Others = append(lists.Others, Spot{x.file, info})
- } else {
- // a declaration with snippet
- index := x.addSnippet(NewSnippet(x.fset, x.decl, id))
- info := makeSpotInfo(kind, index, true)
- lists.Decls = append(lists.Decls, Spot{x.file, info})
- }
-
- x.stats.Spots++
- }
-}
-
-func (x *Indexer) visitFieldList(kind SpotKind, list *ast.FieldList) {
- for _, f := range list.List {
- x.decl = nil // no snippets for fields
- for _, name := range f.Names {
- x.visitIdent(kind, name)
- }
- ast.Walk(x, f.Type)
- // ignore tag - not indexed at the moment
- }
-}
-
-func (x *Indexer) visitSpec(kind SpotKind, spec ast.Spec) {
- switch n := spec.(type) {
- case *ast.ImportSpec:
- x.visitIdent(ImportDecl, n.Name)
- // ignore path - not indexed at the moment
-
- case *ast.ValueSpec:
- for _, n := range n.Names {
- x.visitIdent(kind, n)
- }
- ast.Walk(x, n.Type)
- for _, v := range n.Values {
- ast.Walk(x, v)
- }
-
- case *ast.TypeSpec:
- x.visitIdent(TypeDecl, n.Name)
- ast.Walk(x, n.Type)
- }
-}
-
-func (x *Indexer) visitGenDecl(decl *ast.GenDecl) {
- kind := VarDecl
- if decl.Tok == token.CONST {
- kind = ConstDecl
- }
- x.decl = decl
- for _, s := range decl.Specs {
- x.visitSpec(kind, s)
- }
-}
-
-func (x *Indexer) Visit(node ast.Node) ast.Visitor {
- switch n := node.(type) {
- case nil:
- // nothing to do
-
- case *ast.Ident:
- x.visitIdent(Use, n)
-
- case *ast.FieldList:
- x.visitFieldList(VarDecl, n)
-
- case *ast.InterfaceType:
- x.visitFieldList(MethodDecl, n.Methods)
-
- case *ast.DeclStmt:
- // local declarations should only be *ast.GenDecls;
- // ignore incorrect ASTs
- if decl, ok := n.Decl.(*ast.GenDecl); ok {
- x.decl = nil // no snippets for local declarations
- x.visitGenDecl(decl)
- }
-
- case *ast.GenDecl:
- x.decl = n
- x.visitGenDecl(n)
-
- case *ast.FuncDecl:
- kind := FuncDecl
- if n.Recv != nil {
- kind = MethodDecl
- ast.Walk(x, n.Recv)
- }
- x.decl = n
- x.visitIdent(kind, n.Name)
- ast.Walk(x, n.Type)
- if n.Body != nil {
- ast.Walk(x, n.Body)
- }
-
- case *ast.File:
- x.decl = nil
- x.visitIdent(PackageClause, n.Name)
- for _, d := range n.Decls {
- ast.Walk(x, d)
- }
-
- default:
- return x
- }
-
- return nil
-}
-
-func pkgName(filename string) string {
- // use a new file set each time in order to not pollute the indexer's
- // file set (which must stay in sync with the concatenated source code)
- file, err := parser.ParseFile(token.NewFileSet(), filename, nil, parser.PackageClauseOnly)
- if err != nil || file == nil {
- return ""
- }
- return file.Name.Name
-}
-
-// addFile adds a file to the index if possible and returns the file set file
-// and the file's AST if it was successfully parsed as a Go file. If addFile
-// 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 := fs.Open(filename)
- if err != nil {
- return
- }
- defer f.Close()
-
- // The file set's base offset and x.sources size must be in lock-step;
- // this permits the direct mapping of suffix array lookup results to
- // to corresponding Pos values.
- //
- // When a file is added to the file set, its offset base increases by
- // the size of the file + 1; and the initial base offset is 1. Add an
- // extra byte to the sources here.
- x.sources.WriteByte(0)
-
- // If the sources length doesn't match the file set base at this point
- // the file set implementation changed or we have another error.
- base := x.fset.Base()
- if x.sources.Len() != base {
- panic("internal error: file base incorrect")
- }
-
- // append file contents (src) to x.sources
- if _, err := x.sources.ReadFrom(f); err == nil {
- src := x.sources.Bytes()[base:]
-
- if goFile {
- // parse the file and in the process add it to the file set
- if ast, err = parser.ParseFile(x.fset, filename, src, parser.ParseComments); err == nil {
- file = x.fset.File(ast.Pos()) // ast.Pos() is inside the file
- return
- }
- // file has parse errors, and the AST may be incorrect -
- // set lines information explicitly and index as ordinary
- // text file (cannot fall through to the text case below
- // because the file has already been added to the file set
- // by the parser)
- file = x.fset.File(token.Pos(base)) // token.Pos(base) is inside the file
- file.SetLinesForContent(src)
- ast = nil
- return
- }
-
- if isText(src) {
- // only add the file to the file set (for the full text index)
- file = x.fset.AddFile(filename, x.fset.Base(), len(src))
- file.SetLinesForContent(src)
- return
- }
- }
-
- // discard possibly added data
- x.sources.Truncate(base - 1) // -1 to remove added byte 0 since no file was added
- return
-}
-
-// Design note: Using an explicit white list of permitted files for indexing
-// makes sure that the important files are included and massively reduces the
-// number of files to index. The advantage over a blacklist is that unexpected
-// (non-blacklisted) files won't suddenly explode the index.
-
-// Files are whitelisted if they have a file name or extension
-// present as key in whitelisted.
-var whitelisted = map[string]bool{
- ".bash": true,
- ".c": true,
- ".css": true,
- ".go": true,
- ".goc": true,
- ".h": true,
- ".html": true,
- ".js": true,
- ".out": true,
- ".py": true,
- ".s": true,
- ".sh": true,
- ".txt": true,
- ".xml": true,
- "AUTHORS": true,
- "CONTRIBUTORS": true,
- "LICENSE": true,
- "Makefile": true,
- "PATENTS": true,
- "README": true,
-}
-
-// isWhitelisted returns true if a file is on the list
-// of "permitted" files for indexing. The filename must
-// be the directory-local name of the file.
-func isWhitelisted(filename string) bool {
- key := pathpkg.Ext(filename)
- if key == "" {
- // file has no extension - use entire filename
- key = filename
- }
- return whitelisted[key]
-}
-
-func (x *Indexer) visitFile(dirname string, f os.FileInfo, fulltextIndex bool) {
- if f.IsDir() {
- return
- }
-
- filename := pathpkg.Join(dirname, f.Name())
- goFile := false
-
- switch {
- case isGoFile(f):
- if !includeTestFiles && (!isPkgFile(f) || strings.HasPrefix(filename, "test/")) {
- return
- }
- if !includeMainPackages && pkgName(filename) == "main" {
- return
- }
- goFile = true
-
- case !fulltextIndex || !isWhitelisted(f.Name()):
- return
- }
-
- file, fast := x.addFile(filename, goFile)
- if file == nil {
- return // addFile failed
- }
-
- if fast != nil {
- // we've got a Go file to index
- x.current = file
- pak := x.lookupPackage(dirname, fast.Name.Name)
- x.file = &File{f.Name(), pak}
- ast.Walk(x, fast)
- }
-
- // update statistics
- x.stats.Bytes += file.Size()
- x.stats.Files++
- x.stats.Lines += file.LineCount()
-}
-
-// ----------------------------------------------------------------------------
-// Index
-
-type LookupResult struct {
- Decls HitList // package-level declarations (with snippets)
- Others HitList // all other occurrences
-}
-
-type Index struct {
- fset *token.FileSet // file set used during indexing; nil if no textindex
- suffixes *suffixarray.Index // suffixes for concatenated sources; nil if no textindex
- words map[string]*LookupResult // maps words to hit lists
- alts map[string]*AltWords // maps canonical(words) to lists of alternative spellings
- snippets []*Snippet // all snippets, indexed by snippet index
- stats Statistics
-}
-
-func canonical(w string) string { return strings.ToLower(w) }
-
-// NewIndex creates a new index for the .go files
-// in the directories given by dirnames.
-//
-func NewIndex(dirnames <-chan string, fulltextIndex bool, throttle float64) *Index {
- var x Indexer
- th := NewThrottle(throttle, 100*time.Millisecond) // run at least 0.1s at a time
-
- // initialize Indexer
- // (use some reasonably sized maps to start)
- x.fset = token.NewFileSet()
- x.packages = make(map[string]*Pak, 256)
- x.words = make(map[string]*IndexResult, 8192)
-
- // index all files in the directories given by dirnames
- for dirname := range dirnames {
- list, err := fs.ReadDir(dirname)
- if err != nil {
- continue // ignore this directory
- }
- for _, f := range list {
- if !f.IsDir() {
- x.visitFile(dirname, f, fulltextIndex)
- }
- th.Throttle()
- }
- }
-
- if !fulltextIndex {
- // the file set, the current file, and the sources are
- // not needed after indexing if no text index is built -
- // help GC and clear them
- x.fset = nil
- x.sources.Reset()
- x.current = nil // contains reference to fset!
- }
-
- // for each word, reduce the RunLists into a LookupResult;
- // also collect the word with its canonical spelling in a
- // word list for later computation of alternative spellings
- words := make(map[string]*LookupResult)
- var wlist RunList
- for w, h := range x.words {
- decls := reduce(h.Decls)
- others := reduce(h.Others)
- words[w] = &LookupResult{
- Decls: decls,
- Others: others,
- }
- wlist = append(wlist, &wordPair{canonical(w), w})
- th.Throttle()
- }
- x.stats.Words = len(words)
-
- // reduce the word list {canonical(w), w} into
- // a list of AltWords runs {canonical(w), {w}}
- alist := wlist.reduce(lessWordPair, newAltWords)
-
- // convert alist into a map of alternative spellings
- alts := make(map[string]*AltWords)
- for i := 0; i < len(alist); i++ {
- a := alist[i].(*AltWords)
- alts[a.Canon] = a
- }
-
- // create text index
- var suffixes *suffixarray.Index
- if fulltextIndex {
- suffixes = suffixarray.New(x.sources.Bytes())
- }
-
- return &Index{x.fset, suffixes, words, alts, x.snippets, x.stats}
-}
-
-type fileIndex struct {
- Words map[string]*LookupResult
- Alts map[string]*AltWords
- Snippets []*Snippet
- Fulltext bool
-}
-
-func (x *fileIndex) Write(w io.Writer) error {
- return gob.NewEncoder(w).Encode(x)
-}
-
-func (x *fileIndex) Read(r io.Reader) error {
- return gob.NewDecoder(r).Decode(x)
-}
-
-// Write writes the index x to w.
-func (x *Index) Write(w io.Writer) error {
- fulltext := false
- if x.suffixes != nil {
- fulltext = true
- }
- fx := fileIndex{
- x.words,
- x.alts,
- x.snippets,
- fulltext,
- }
- if err := fx.Write(w); err != nil {
- return err
- }
- if fulltext {
- encode := func(x interface{}) error {
- return gob.NewEncoder(w).Encode(x)
- }
- if err := x.fset.Write(encode); err != nil {
- return err
- }
- if err := x.suffixes.Write(w); err != nil {
- return err
- }
- }
- return nil
-}
-
-// Read reads the index from r into x; x must not be nil.
-// If r does not also implement io.ByteReader, it will be wrapped in a bufio.Reader.
-func (x *Index) Read(r io.Reader) error {
- // We use the ability to read bytes as a plausible surrogate for buffering.
- if _, ok := r.(io.ByteReader); !ok {
- r = bufio.NewReader(r)
- }
- var fx fileIndex
- if err := fx.Read(r); err != nil {
- return err
- }
- x.words = fx.Words
- x.alts = fx.Alts
- x.snippets = fx.Snippets
- if fx.Fulltext {
- x.fset = token.NewFileSet()
- decode := func(x interface{}) error {
- return gob.NewDecoder(r).Decode(x)
- }
- if err := x.fset.Read(decode); err != nil {
- return err
- }
- x.suffixes = new(suffixarray.Index)
- if err := x.suffixes.Read(r); err != nil {
- return err
- }
- }
- return nil
-}
-
-// Stats() returns index statistics.
-func (x *Index) Stats() Statistics {
- return x.stats
-}
-
-func (x *Index) lookupWord(w string) (match *LookupResult, alt *AltWords) {
- match = x.words[w]
- alt = x.alts[canonical(w)]
- // remove current spelling from alternatives
- // (if there is no match, the alternatives do
- // not contain the current spelling)
- if match != nil && alt != nil {
- alt = alt.filter(w)
- }
- return
-}
-
-// isIdentifier reports whether s is a Go identifier.
-func isIdentifier(s string) bool {
- for i, ch := range s {
- if unicode.IsLetter(ch) || ch == ' ' || i > 0 && unicode.IsDigit(ch) {
- continue
- }
- return false
- }
- return len(s) > 0
-}
-
-// For a given query, which is either a single identifier or a qualified
-// identifier, Lookup returns a list of packages, a LookupResult, and a
-// list of alternative spellings, if any. Any and all results may be nil.
-// If the query syntax is wrong, an error is reported.
-func (x *Index) Lookup(query string) (paks HitList, match *LookupResult, alt *AltWords, err error) {
- ss := strings.Split(query, ".")
-
- // check query syntax
- for _, s := range ss {
- if !isIdentifier(s) {
- err = errors.New("all query parts must be identifiers")
- return
- }
- }
-
- // handle simple and qualified identifiers
- switch len(ss) {
- case 1:
- ident := ss[0]
- match, alt = x.lookupWord(ident)
- if match != nil {
- // found a match - filter packages with same name
- // for the list of packages called ident, if any
- paks = match.Others.filter(ident)
- }
-
- case 2:
- pakname, ident := ss[0], ss[1]
- match, alt = x.lookupWord(ident)
- if match != nil {
- // found a match - filter by package name
- // (no paks - package names are not qualified)
- decls := match.Decls.filter(pakname)
- others := match.Others.filter(pakname)
- match = &LookupResult{decls, others}
- }
-
- default:
- err = errors.New("query is not a (qualified) identifier")
- }
-
- return
-}
-
-func (x *Index) Snippet(i int) *Snippet {
- // handle illegal snippet indices gracefully
- if 0 <= i && i < len(x.snippets) {
- return x.snippets[i]
- }
- return nil
-}
-
-type positionList []struct {
- filename string
- line int
-}
-
-func (list positionList) Len() int { return len(list) }
-func (list positionList) Less(i, j int) bool { return list[i].filename < list[j].filename }
-func (list positionList) Swap(i, j int) { list[i], list[j] = list[j], list[i] }
-
-// unique returns the list sorted and with duplicate entries removed
-func unique(list []int) []int {
- sort.Ints(list)
- var last int
- i := 0
- for _, x := range list {
- if i == 0 || x != last {
- last = x
- list[i] = x
- i++
- }
- }
- return list[0:i]
-}
-
-// A FileLines value specifies a file and line numbers within that file.
-type FileLines struct {
- Filename string
- Lines []int
-}
-
-// LookupRegexp returns the number of matches and the matches where a regular
-// expression r is found in the full text index. At most n matches are
-// returned (thus found <= n).
-//
-func (x *Index) LookupRegexp(r *regexp.Regexp, n int) (found int, result []FileLines) {
- if x.suffixes == nil || n <= 0 {
- return
- }
- // n > 0
-
- var list positionList
- // FindAllIndex may returns matches that span across file boundaries.
- // Such matches are unlikely, buf after eliminating them we may end up
- // with fewer than n matches. If we don't have enough at the end, redo
- // the search with an increased value n1, but only if FindAllIndex
- // returned all the requested matches in the first place (if it
- // returned fewer than that there cannot be more).
- for n1 := n; found < n; n1 += n - found {
- found = 0
- matches := x.suffixes.FindAllIndex(r, n1)
- // compute files, exclude matches that span file boundaries,
- // and map offsets to file-local offsets
- list = make(positionList, len(matches))
- for _, m := range matches {
- // by construction, an offset corresponds to the Pos value
- // for the file set - use it to get the file and line
- p := token.Pos(m[0])
- if file := x.fset.File(p); file != nil {
- if base := file.Base(); base <= m[1] && m[1] <= base+file.Size() {
- // match [m[0], m[1]) is within the file boundaries
- list[found].filename = file.Name()
- list[found].line = file.Line(p)
- found++
- }
- }
- }
- if found == n || len(matches) < n1 {
- // found all matches or there's no chance to find more
- break
- }
- }
- list = list[0:found]
- sort.Sort(list) // sort by filename
-
- // collect matches belonging to the same file
- var last string
- var lines []int
- addLines := func() {
- if len(lines) > 0 {
- // remove duplicate lines
- result = append(result, FileLines{last, unique(lines)})
- lines = nil
- }
- }
- for _, m := range list {
- if m.filename != last {
- addLines()
- last = m.filename
- }
- lines = append(lines, m.line)
- }
- addLines()
-
- return
-}
diff --git a/src/cmd/godoc/linkify.go b/src/cmd/godoc/linkify.go
deleted file mode 100644
index 5b4862419..000000000
--- a/src/cmd/godoc/linkify.go
+++ /dev/null
@@ -1,234 +0,0 @@
-// Copyright 2013 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 LinkifyText which introduces
-// links for identifiers pointing to their declarations.
-// The approach does not cover all cases because godoc
-// doesn't have complete type information, but it's
-// reasonably good for browsing.
-
-package main
-
-import (
- "fmt"
- "go/ast"
- "go/token"
- "io"
- "strconv"
-)
-
-// LinkifyText HTML-escapes source text and writes it to w.
-// Identifiers that are in a "use" position (i.e., that are
-// not being declared), are wrapped with HTML links pointing
-// to the respective declaration, if possible. Comments are
-// formatted the same way as with FormatText.
-//
-func LinkifyText(w io.Writer, text []byte, n ast.Node) {
- links := linksFor(n)
-
- i := 0 // links index
- prev := "" // prev HTML tag
- linkWriter := func(w io.Writer, _ int, start bool) {
- // end tag
- if !start {
- if prev != "" {
- fmt.Fprintf(w, `</%s>`, prev)
- prev = ""
- }
- return
- }
-
- // start tag
- prev = ""
- if i < len(links) {
- switch info := links[i]; {
- case info.path != "" && info.name == "":
- // package path
- fmt.Fprintf(w, `<a href="/pkg/%s/">`, info.path)
- prev = "a"
- case info.path != "" && info.name != "":
- // qualified identifier
- fmt.Fprintf(w, `<a href="/pkg/%s/#%s">`, info.path, info.name)
- prev = "a"
- case info.path == "" && info.name != "":
- // local identifier
- if info.mode == identVal {
- fmt.Fprintf(w, `<span id="%s">`, info.name)
- prev = "span"
- } else {
- fmt.Fprintf(w, `<a href="#%s">`, info.name)
- prev = "a"
- }
- }
- i++
- }
- }
-
- idents := tokenSelection(text, token.IDENT)
- comments := tokenSelection(text, token.COMMENT)
- FormatSelections(w, text, linkWriter, idents, selectionTag, comments)
-}
-
-// A link describes the (HTML) link information for an identifier.
-// The zero value of a link represents "no link".
-//
-type link struct {
- mode identMode
- path, name string // package path, identifier name
-}
-
-// linksFor returns the list of links for the identifiers used
-// by node in the same order as they appear in the source.
-//
-func linksFor(node ast.Node) (list []link) {
- modes := identModesFor(node)
-
- // NOTE: We are expecting ast.Inspect to call the
- // callback function in source text order.
- ast.Inspect(node, func(node ast.Node) bool {
- switch n := node.(type) {
- case *ast.Ident:
- m := modes[n]
- info := link{mode: m}
- switch m {
- case identUse:
- if n.Obj == nil && predeclared[n.Name] {
- info.path = builtinPkgPath
- }
- info.name = n.Name
- case identDef:
- // any declaration expect const or var - empty link
- case identVal:
- // const or var declaration
- info.name = n.Name
- }
- list = append(list, info)
- return false
- case *ast.SelectorExpr:
- // Detect qualified identifiers of the form pkg.ident.
- // If anything fails we return true and collect individual
- // identifiers instead.
- if x, _ := n.X.(*ast.Ident); x != nil {
- // x must be a package for a qualified identifier
- if obj := x.Obj; obj != nil && obj.Kind == ast.Pkg {
- if spec, _ := obj.Decl.(*ast.ImportSpec); spec != nil {
- // spec.Path.Value is the import path
- if path, err := strconv.Unquote(spec.Path.Value); err == nil {
- // Register two links, one for the package
- // and one for the qualified identifier.
- info := link{path: path}
- list = append(list, info)
- info.name = n.Sel.Name
- list = append(list, info)
- return false
- }
- }
- }
- }
- }
- return true
- })
-
- return
-}
-
-// The identMode describes how an identifier is "used" at its source location.
-type identMode int
-
-const (
- identUse identMode = iota // identifier is used (must be zero value for identMode)
- identDef // identifier is defined
- identVal // identifier is defined in a const or var declaration
-)
-
-// identModesFor returns a map providing the identMode for each identifier used by node.
-func identModesFor(node ast.Node) map[*ast.Ident]identMode {
- m := make(map[*ast.Ident]identMode)
-
- ast.Inspect(node, func(node ast.Node) bool {
- switch n := node.(type) {
- case *ast.Field:
- for _, n := range n.Names {
- m[n] = identDef
- }
- case *ast.ImportSpec:
- if name := n.Name; name != nil {
- m[name] = identDef
- }
- case *ast.ValueSpec:
- for _, n := range n.Names {
- m[n] = identVal
- }
- case *ast.TypeSpec:
- m[n.Name] = identDef
- case *ast.FuncDecl:
- m[n.Name] = identDef
- case *ast.AssignStmt:
- // Short variable declarations only show up if we apply
- // this code to all source code (as opposed to exported
- // declarations only).
- if n.Tok == token.DEFINE {
- // Some of the lhs variables may be re-declared,
- // so technically they are not defs. We don't
- // care for now.
- for _, x := range n.Lhs {
- // Each lhs expression should be an
- // ident, but we are conservative and check.
- if n, _ := x.(*ast.Ident); n != nil {
- m[n] = identVal
- }
- }
- }
- }
- return true
- })
-
- return m
-}
-
-// The predeclared map represents the set of all predeclared identifiers.
-// TODO(gri) This information is also encoded in similar maps in go/doc,
-// but not exported. Consider exporting an accessor and using
-// it instead.
-var predeclared = map[string]bool{
- "bool": true,
- "byte": true,
- "complex64": true,
- "complex128": true,
- "error": true,
- "float32": true,
- "float64": true,
- "int": true,
- "int8": true,
- "int16": true,
- "int32": true,
- "int64": true,
- "rune": true,
- "string": true,
- "uint": true,
- "uint8": true,
- "uint16": true,
- "uint32": true,
- "uint64": true,
- "uintptr": true,
- "true": true,
- "false": true,
- "iota": true,
- "nil": true,
- "append": true,
- "cap": true,
- "close": true,
- "complex": true,
- "copy": true,
- "delete": true,
- "imag": true,
- "len": true,
- "make": true,
- "new": true,
- "panic": true,
- "print": true,
- "println": true,
- "real": true,
- "recover": true,
-}
diff --git a/src/cmd/godoc/main.go b/src/cmd/godoc/main.go
deleted file mode 100644
index 81e739d20..000000000
--- a/src/cmd/godoc/main.go
+++ /dev/null
@@ -1,470 +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.
-
-// godoc: Go Documentation Server
-
-// Web server tree:
-//
-// http://godoc/ main landing page
-// http://godoc/doc/ serve from $GOROOT/doc - spec, mem, etc.
-// http://godoc/src/ serve files from $GOROOT/src; .go gets pretty-printed
-// http://godoc/cmd/ serve documentation about commands
-// http://godoc/pkg/ serve documentation about packages
-// (idea is if you say import "compress/zlib", you go to
-// http://godoc/pkg/compress/zlib)
-//
-// Command-line interface:
-//
-// godoc packagepath [name ...]
-//
-// godoc compress/zlib
-// - prints doc for package compress/zlib
-// godoc crypto/block Cipher NewCMAC
-// - prints doc for Cipher and NewCMAC in package crypto/block
-
-// +build !appengine
-
-package main
-
-import (
- "archive/zip"
- "bytes"
- "errors"
- _ "expvar" // to serve /debug/vars
- "flag"
- "fmt"
- "go/ast"
- "go/build"
- "go/printer"
- "io"
- "log"
- "net/http"
- _ "net/http/pprof" // to serve /debug/pprof/*
- "net/url"
- "os"
- pathpkg "path"
- "path/filepath"
- "regexp"
- "runtime"
- "strings"
-)
-
-const defaultAddr = ":6060" // default webserver address
-
-var (
- // file system to serve
- // (with e.g.: zip -r go.zip $GOROOT -i \*.go -i \*.html -i \*.css -i \*.js -i \*.txt -i \*.c -i \*.h -i \*.s -i \*.png -i \*.jpg -i \*.sh -i favicon.ico)
- zipfile = flag.String("zip", "", "zip file providing the file system to serve; disabled if empty")
-
- // file-based index
- writeIndex = flag.Bool("write_index", false, "write index to a file; the file name must be specified with -index_files")
-
- // network
- httpAddr = flag.String("http", "", "HTTP service address (e.g., '"+defaultAddr+"')")
- serverAddr = flag.String("server", "", "webserver address for command line searches")
-
- // layout control
- html = flag.Bool("html", false, "print HTML in command-line mode")
- srcMode = flag.Bool("src", false, "print (exported) source in command-line mode")
- urlFlag = flag.String("url", "", "print HTML for named URL")
-
- // command-line searches
- query = flag.Bool("q", false, "arguments are considered search queries")
-)
-
-func serveError(w http.ResponseWriter, r *http.Request, relpath string, err error) {
- w.WriteHeader(http.StatusNotFound)
- servePage(w, Page{
- Title: "File " + relpath,
- Subtitle: relpath,
- Body: applyTemplate(errorHTML, "errorHTML", err), // err may contain an absolute path!
- })
-}
-
-func usage() {
- fmt.Fprintf(os.Stderr,
- "usage: godoc package [name ...]\n"+
- " godoc -http="+defaultAddr+"\n")
- flag.PrintDefaults()
- os.Exit(2)
-}
-
-func loggingHandler(h http.Handler) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
- log.Printf("%s\t%s", req.RemoteAddr, req.URL)
- h.ServeHTTP(w, req)
- })
-}
-
-func remoteSearch(query string) (res *http.Response, err error) {
- // list of addresses to try
- var addrs []string
- if *serverAddr != "" {
- // explicit server address - only try this one
- addrs = []string{*serverAddr}
- } else {
- addrs = []string{
- defaultAddr,
- "golang.org",
- }
- }
-
- // remote search
- search := remoteSearchURL(query, *html)
- for _, addr := range addrs {
- url := "http://" + addr + search
- res, err = http.Get(url)
- if err == nil && res.StatusCode == http.StatusOK {
- break
- }
- }
-
- if err == nil && res.StatusCode != http.StatusOK {
- err = errors.New(res.Status)
- }
-
- return
-}
-
-// Does s look like a regular expression?
-func isRegexp(s string) bool {
- return strings.IndexAny(s, ".(|)*+?^$[]") >= 0
-}
-
-// Make a regular expression of the form
-// names[0]|names[1]|...names[len(names)-1].
-// Returns nil if the regular expression is illegal.
-func makeRx(names []string) (rx *regexp.Regexp) {
- if len(names) > 0 {
- s := ""
- for i, name := range names {
- if i > 0 {
- s += "|"
- }
- if isRegexp(name) {
- s += name
- } else {
- s += "^" + name + "$" // must match exactly
- }
- }
- rx, _ = regexp.Compile(s) // rx is nil if there's a compilation error
- }
- return
-}
-
-func main() {
- flag.Usage = usage
- flag.Parse()
-
- // Check usage: either server and no args, command line and args, or index creation mode
- if (*httpAddr != "" || *urlFlag != "") != (flag.NArg() == 0) && !*writeIndex {
- usage()
- }
-
- if *tabwidth < 0 {
- log.Fatalf("negative tabwidth %d", *tabwidth)
- }
-
- // Determine file system to use.
- // TODO(gri) - fs and fsHttp should really be the same. Try to unify.
- // - fsHttp doesn't need to be set up in command-line mode,
- // same is true for the http handlers in initHandlers.
- if *zipfile == "" {
- // use file system of underlying OS
- fs.Bind("/", OS(*goroot), "/", bindReplace)
- if *templateDir != "" {
- fs.Bind("/lib/godoc", OS(*templateDir), "/", bindBefore)
- }
- } else {
- // use file system specified via .zip file (path separator must be '/')
- rc, err := zip.OpenReader(*zipfile)
- if err != nil {
- log.Fatalf("%s: %s\n", *zipfile, err)
- }
- defer rc.Close() // be nice (e.g., -writeIndex mode)
- fs.Bind("/", NewZipFS(rc, *zipfile), *goroot, bindReplace)
- }
-
- // Bind $GOPATH trees into Go root.
- for _, p := range filepath.SplitList(build.Default.GOPATH) {
- fs.Bind("/src/pkg", OS(p), "/src", bindAfter)
- }
-
- readTemplates()
- initHandlers()
-
- if *writeIndex {
- // Write search index and exit.
- if *indexFiles == "" {
- log.Fatal("no index file specified")
- }
-
- log.Println("initialize file systems")
- *verbose = true // want to see what happens
- initFSTree()
-
- *indexThrottle = 1
- updateIndex()
-
- log.Println("writing index file", *indexFiles)
- f, err := os.Create(*indexFiles)
- if err != nil {
- log.Fatal(err)
- }
- index, _ := searchIndex.get()
- err = index.(*Index).Write(f)
- if err != nil {
- log.Fatal(err)
- }
-
- log.Println("done")
- return
- }
-
- // Print content that would be served at the URL *urlFlag.
- if *urlFlag != "" {
- registerPublicHandlers(http.DefaultServeMux)
- initFSTree()
- updateMetadata()
- // Try up to 10 fetches, following redirects.
- urlstr := *urlFlag
- for i := 0; i < 10; i++ {
- // Prepare request.
- u, err := url.Parse(urlstr)
- if err != nil {
- log.Fatal(err)
- }
- req := &http.Request{
- URL: u,
- }
-
- // Invoke default HTTP handler to serve request
- // to our buffering httpWriter.
- w := &httpWriter{h: http.Header{}, code: 200}
- http.DefaultServeMux.ServeHTTP(w, req)
-
- // Return data, error, or follow redirect.
- switch w.code {
- case 200: // ok
- os.Stdout.Write(w.Bytes())
- return
- case 301, 302, 303, 307: // redirect
- redirect := w.h.Get("Location")
- if redirect == "" {
- log.Fatalf("HTTP %d without Location header", w.code)
- }
- urlstr = redirect
- default:
- log.Fatalf("HTTP error %d", w.code)
- }
- }
- log.Fatalf("too many redirects")
- }
-
- if *httpAddr != "" {
- // HTTP server mode.
- var handler http.Handler = http.DefaultServeMux
- if *verbose {
- log.Printf("Go Documentation Server")
- log.Printf("version = %s", runtime.Version())
- log.Printf("address = %s", *httpAddr)
- log.Printf("goroot = %s", *goroot)
- log.Printf("tabwidth = %d", *tabwidth)
- switch {
- case !*indexEnabled:
- log.Print("search index disabled")
- case *maxResults > 0:
- log.Printf("full text index enabled (maxresults = %d)", *maxResults)
- default:
- log.Print("identifier search index enabled")
- }
- fs.Fprint(os.Stderr)
- handler = loggingHandler(handler)
- }
-
- registerPublicHandlers(http.DefaultServeMux)
- registerPlaygroundHandlers(http.DefaultServeMux)
-
- // Initialize default directory tree with corresponding timestamp.
- // (Do it in a goroutine so that launch is quick.)
- go initFSTree()
-
- // Immediately update metadata.
- updateMetadata()
- // Periodically refresh metadata.
- go refreshMetadataLoop()
-
- // Initialize search index.
- if *indexEnabled {
- go indexer()
- }
-
- // Start http server.
- if err := http.ListenAndServe(*httpAddr, handler); err != nil {
- log.Fatalf("ListenAndServe %s: %v", *httpAddr, err)
- }
-
- return
- }
-
- // Command line mode.
- if *html {
- packageText = packageHTML
- searchText = packageHTML
- }
-
- if *query {
- // Command-line queries.
- for i := 0; i < flag.NArg(); i++ {
- res, err := remoteSearch(flag.Arg(i))
- if err != nil {
- log.Fatalf("remoteSearch: %s", err)
- }
- io.Copy(os.Stdout, res.Body)
- }
- return
- }
-
- // Determine paths.
- //
- // If we are passed an operating system path like . or ./foo or /foo/bar or c:\mysrc,
- // we need to map that path somewhere in the fs name space so that routines
- // like getPageInfo will see it. We use the arbitrarily-chosen virtual path "/target"
- // for this. That is, if we get passed a directory like the above, we map that
- // directory so that getPageInfo sees it as /target.
- const target = "/target"
- const cmdPrefix = "cmd/"
- path := flag.Arg(0)
- var forceCmd bool
- var abspath, relpath string
- if filepath.IsAbs(path) {
- fs.Bind(target, OS(path), "/", bindReplace)
- abspath = target
- } else if build.IsLocalImport(path) {
- cwd, _ := os.Getwd() // ignore errors
- path = filepath.Join(cwd, path)
- fs.Bind(target, OS(path), "/", bindReplace)
- abspath = target
- } else if strings.HasPrefix(path, cmdPrefix) {
- path = strings.TrimPrefix(path, cmdPrefix)
- forceCmd = true
- } else if bp, _ := build.Import(path, "", build.FindOnly); bp.Dir != "" && bp.ImportPath != "" {
- fs.Bind(target, OS(bp.Dir), "/", bindReplace)
- abspath = target
- relpath = bp.ImportPath
- } else {
- abspath = pathpkg.Join(pkgHandler.fsRoot, path)
- }
- if relpath == "" {
- relpath = abspath
- }
-
- var mode PageInfoMode
- if relpath == builtinPkgPath {
- // the fake built-in package contains unexported identifiers
- mode = noFiltering
- }
- if *srcMode {
- // only filter exports if we don't have explicit command-line filter arguments
- if flag.NArg() > 1 {
- mode |= noFiltering
- }
- mode |= showSource
- }
-
- // first, try as package unless forced as command
- var info *PageInfo
- if !forceCmd {
- info = pkgHandler.getPageInfo(abspath, relpath, mode)
- }
-
- // second, try as command unless the path is absolute
- // (the go command invokes godoc w/ absolute paths; don't override)
- var cinfo *PageInfo
- if !filepath.IsAbs(path) {
- abspath = pathpkg.Join(cmdHandler.fsRoot, path)
- cinfo = cmdHandler.getPageInfo(abspath, relpath, mode)
- }
-
- // determine what to use
- if info == nil || info.IsEmpty() {
- if cinfo != nil && !cinfo.IsEmpty() {
- // only cinfo exists - switch to cinfo
- info = cinfo
- }
- } else if cinfo != nil && !cinfo.IsEmpty() {
- // both info and cinfo exist - use cinfo if info
- // contains only subdirectory information
- if info.PAst == nil && info.PDoc == nil {
- info = cinfo
- } else {
- fmt.Printf("use 'godoc %s%s' for documentation on the %s command \n\n", cmdPrefix, relpath, relpath)
- }
- }
-
- if info == nil {
- log.Fatalf("%s: no such directory or package", flag.Arg(0))
- }
- if info.Err != nil {
- log.Fatalf("%v", info.Err)
- }
-
- if info.PDoc != nil && info.PDoc.ImportPath == target {
- // Replace virtual /target with actual argument from command line.
- info.PDoc.ImportPath = flag.Arg(0)
- }
-
- // If we have more than one argument, use the remaining arguments for filtering.
- if flag.NArg() > 1 {
- args := flag.Args()[1:]
- rx := makeRx(args)
- if rx == nil {
- log.Fatalf("illegal regular expression from %v", args)
- }
-
- filter := func(s string) bool { return rx.MatchString(s) }
- switch {
- case info.PAst != nil:
- cmap := ast.NewCommentMap(info.FSet, info.PAst, info.PAst.Comments)
- ast.FilterFile(info.PAst, filter)
- // Special case: Don't use templates for printing
- // so we only get the filtered declarations without
- // package clause or extra whitespace.
- for i, d := range info.PAst.Decls {
- // determine the comments associated with d only
- comments := cmap.Filter(d).Comments()
- cn := &printer.CommentedNode{Node: d, Comments: comments}
- if i > 0 {
- fmt.Println()
- }
- if *html {
- var buf bytes.Buffer
- writeNode(&buf, info.FSet, cn)
- FormatText(os.Stdout, buf.Bytes(), -1, true, "", nil)
- } else {
- writeNode(os.Stdout, info.FSet, cn)
- }
- fmt.Println()
- }
- return
-
- case info.PDoc != nil:
- info.PDoc.Filter(filter)
- }
- }
-
- if err := packageText.Execute(os.Stdout, info); err != nil {
- log.Printf("packageText.Execute: %s", err)
- }
-}
-
-// An httpWriter is an http.ResponseWriter writing to a bytes.Buffer.
-type httpWriter struct {
- bytes.Buffer
- h http.Header
- code int
-}
-
-func (w *httpWriter) Header() http.Header { return w.h }
-func (w *httpWriter) WriteHeader(code int) { w.code = code }
diff --git a/src/cmd/godoc/parser.go b/src/cmd/godoc/parser.go
deleted file mode 100644
index 42a5d2d98..000000000
--- a/src/cmd/godoc/parser.go
+++ /dev/null
@@ -1,37 +0,0 @@
-// 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 contains support functions for parsing .go files
-// accessed via godoc's file system fs.
-
-package main
-
-import (
- "go/ast"
- "go/parser"
- "go/token"
- pathpkg "path"
-)
-
-func parseFile(fset *token.FileSet, filename string, mode parser.Mode) (*ast.File, error) {
- src, err := ReadFile(fs, filename)
- if err != nil {
- return nil, err
- }
- return parser.ParseFile(fset, filename, src, mode)
-}
-
-func parseFiles(fset *token.FileSet, abspath string, localnames []string) (map[string]*ast.File, error) {
- files := make(map[string]*ast.File)
- for _, f := range localnames {
- absname := pathpkg.Join(abspath, f)
- file, err := parseFile(fset, absname, parser.ParseComments)
- if err != nil {
- return nil, err
- }
- files[absname] = file
- }
-
- return files, nil
-}
diff --git a/src/cmd/godoc/play-appengine.go b/src/cmd/godoc/play-appengine.go
deleted file mode 100644
index 9e351d1a2..000000000
--- a/src/cmd/godoc/play-appengine.go
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2012 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.
-
-// App Engine godoc Playground functionality.
-
-// +build appengine
-
-package main
-
-import (
- "io"
- "net/http"
-
- "appengine"
- "appengine/urlfetch"
-)
-
-func bounceToPlayground(w http.ResponseWriter, req *http.Request) {
- c := appengine.NewContext(req)
- client := urlfetch.Client(c)
- url := playgroundBaseURL + req.URL.Path
- defer req.Body.Close()
- resp, err := client.Post(url, req.Header.Get("Content-type"), req.Body)
- if err != nil {
- http.Error(w, "Internal Server Error", 500)
- c.Errorf("making POST request: %v", err)
- return
- }
- defer resp.Body.Close()
- if _, err := io.Copy(w, resp.Body); err != nil {
- http.Error(w, "Internal Server Error", 500)
- c.Errorf("making POST request: %v", err)
- }
-}
diff --git a/src/cmd/godoc/play-local.go b/src/cmd/godoc/play-local.go
deleted file mode 100644
index 637ce5e1a..000000000
--- a/src/cmd/godoc/play-local.go
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2012 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.
-
-// Stand-alone godoc Playground functionality.
-
-// +build !appengine
-
-package main
-
-import (
- "io"
- "net/http"
- "net/url"
-)
-
-var playgroundScheme, playgroundHost string
-
-func init() {
- u, err := url.Parse(playgroundBaseURL)
- if err != nil {
- panic(err)
- }
- playgroundScheme = u.Scheme
- playgroundHost = u.Host
-}
-
-// bounceToPlayground forwards the request to play.golang.org.
-func bounceToPlayground(w http.ResponseWriter, req *http.Request) {
- defer req.Body.Close()
- req.URL.Scheme = playgroundScheme
- req.URL.Host = playgroundHost
- resp, err := http.Post(req.URL.String(), req.Header.Get("Content-type"), req.Body)
- if err != nil {
- http.Error(w, err.Error(), 500)
- return
- }
- w.WriteHeader(resp.StatusCode)
- io.Copy(w, resp.Body)
- resp.Body.Close()
-}
diff --git a/src/cmd/godoc/play.go b/src/cmd/godoc/play.go
deleted file mode 100644
index 47a11f6c0..000000000
--- a/src/cmd/godoc/play.go
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2012 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.
-
-// Common Playground functionality.
-
-package main
-
-import (
- "encoding/json"
- "fmt"
- "go/format"
- "net/http"
-)
-
-// The server that will service compile and share requests.
-const playgroundBaseURL = "http://play.golang.org"
-
-func registerPlaygroundHandlers(mux *http.ServeMux) {
- if *showPlayground {
- mux.HandleFunc("/compile", bounceToPlayground)
- mux.HandleFunc("/share", bounceToPlayground)
- } else {
- mux.HandleFunc("/compile", disabledHandler)
- mux.HandleFunc("/share", disabledHandler)
- }
- http.HandleFunc("/fmt", fmtHandler)
-}
-
-type fmtResponse struct {
- Body string
- Error string
-}
-
-// fmtHandler takes a Go program in its "body" form value, formats it with
-// standard gofmt formatting, and writes a fmtResponse as a JSON object.
-func fmtHandler(w http.ResponseWriter, r *http.Request) {
- resp := new(fmtResponse)
- body, err := format.Source([]byte(r.FormValue("body")))
- if err != nil {
- resp.Error = err.Error()
- } else {
- resp.Body = string(body)
- }
- json.NewEncoder(w).Encode(resp)
-}
-
-// disabledHandler serves a 501 "Not Implemented" response.
-func disabledHandler(w http.ResponseWriter, r *http.Request) {
- w.WriteHeader(http.StatusNotImplemented)
- fmt.Fprint(w, "This functionality is not available via local godoc.")
-}
diff --git a/src/cmd/godoc/setup-godoc-app.bash b/src/cmd/godoc/setup-godoc-app.bash
deleted file mode 100755
index 792e0d450..000000000
--- a/src/cmd/godoc/setup-godoc-app.bash
+++ /dev/null
@@ -1,140 +0,0 @@
-#!/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.
-
-# This script creates a complete godoc app in $APPDIR.
-# It copies the cmd/godoc and src/pkg/go/... sources from GOROOT,
-# synthesizes an app.yaml file, and creates the .zip, index, and
-# configuration files.
-#
-# If an argument is provided it is assumed to be the app-engine godoc directory.
-# Without an argument, $APPDIR is used instead. If GOROOT is not set, "go env"
-# is consulted to find the $GOROOT.
-#
-# The script creates a .zip file representing the $GOROOT file system
-# and computes the correspondig search index files. These files are then
-# copied to $APPDIR. A corresponding godoc configuration file is created
-# in $APPDIR/appconfig.go.
-
-ZIPFILE=godoc.zip
-INDEXFILE=godoc.index
-SPLITFILES=index.split.
-CONFIGFILE=godoc/appconfig.go
-
-error() {
- echo "error: $1"
- exit 2
-}
-
-getArgs() {
- if [ -z $GOROOT ]; then
- GOROOT=$(go env GOROOT)
- echo "GOROOT not set explicitly, using $GOROOT instead"
- fi
- if [ -z $APPDIR ]; then
- if [ $# == 0 ]; then
- error "APPDIR not set, and no argument provided"
- fi
- APPDIR=$1
- echo "APPDIR not set, using argument instead"
- fi
-
- # safety checks
- if [ ! -d $GOROOT ]; then
- error "$GOROOT is not a directory"
- fi
- if [ ! -x $GOROOT/bin/godoc ]; then
- error "$GOROOT/bin/godoc does not exist or is not executable"
- fi
- if [ -e $APPDIR ]; then
- error "$APPDIR exists; check and remove it before trying again"
- fi
-
- # reporting
- echo "GOROOT = $GOROOT"
- echo "APPDIR = $APPDIR"
-}
-
-copyGodoc() {
- echo "*** copy $GOROOT/src/cmd/godoc to $APPDIR/godoc"
- cp -r $GOROOT/src/cmd/godoc $APPDIR/godoc
-}
-
-copyGoPackages() {
- echo "*** copy $GOROOT/src/pkg/go to $APPDIR/newgo and rewrite imports"
- cp -r $GOROOT/src/pkg/go $APPDIR/newgo
- find $APPDIR/newgo -type d -name testdata | xargs rm -r
- gofiles=$(find $APPDIR -name '*.go')
- sed -i '' 's_^\(."\)\(go/[a-z]*\)"$_\1new\2"_' $gofiles
- sed -i '' 's_^\(import "\)\(go/[a-z]*\)"$_\1new\2"_' $gofiles
-}
-
-makeAppYaml() {
- echo "*** make $APPDIR/app.yaml"
- cat > $APPDIR/app.yaml <<EOF
-application: godoc
-version: 1
-runtime: go
-api_version: go1
-
-handlers:
-- url: /.*
- script: _go_app
-EOF
-}
-
-makeZipfile() {
- echo "*** make $APPDIR/$ZIPFILE"
- zip -q -r $APPDIR/$ZIPFILE $GOROOT -i \*.go -i \*.html -i \*.xml -i \*.css -i \*.js -i \*.txt -i \*.c -i \*.h -i \*.s -i \*.png -i \*.jpg -i \*.sh -i \*.ico
-}
-
-makeIndexfile() {
- echo "*** make $APPDIR/$INDEXFILE"
- OUT=/tmp/godoc.out
- $GOROOT/bin/godoc -write_index -index_files=$APPDIR/$INDEXFILE -zip=$APPDIR/$ZIPFILE 2> $OUT
- if [ $? != 0 ]; then
- error "$GOROOT/bin/godoc failed - see $OUT for details"
- fi
-}
-
-splitIndexfile() {
- echo "*** split $APPDIR/$INDEXFILE"
- split -b8m $APPDIR/$INDEXFILE $APPDIR/$SPLITFILES
-}
-
-makeConfigfile() {
- echo "*** make $APPDIR/$CONFIGFILE"
- cat > $APPDIR/$CONFIGFILE <<EOF
-package main
-
-// GENERATED FILE - DO NOT MODIFY BY HAND.
-// (generated by $GOROOT/src/cmd/godoc/setup-godoc-app.bash)
-
-const (
- // .zip filename
- zipFilename = "$ZIPFILE"
-
- // goroot directory in .zip file
- zipGoroot = "$GOROOT"
-
- // glob pattern describing search index files
- // (if empty, the index is built at run-time)
- indexFilenames = "$SPLITFILES*"
-)
-EOF
-}
-
-getArgs "$@"
-set -e
-mkdir $APPDIR
-copyGodoc
-copyGoPackages
-makeAppYaml
-makeZipfile
-makeIndexfile
-splitIndexfile
-makeConfigfile
-
-echo "*** setup complete"
diff --git a/src/cmd/godoc/snippet.go b/src/cmd/godoc/snippet.go
deleted file mode 100644
index b482b7487..000000000
--- a/src/cmd/godoc/snippet.go
+++ /dev/null
@@ -1,112 +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.
-
-// This file contains the infrastructure to create a code
-// snippet for search results.
-//
-// Note: At the moment, this only creates HTML snippets.
-
-package main
-
-import (
- "bytes"
- "fmt"
- "go/ast"
- "go/token"
-)
-
-type Snippet struct {
- Line int
- Text string // HTML-escaped
-}
-
-func newSnippet(fset *token.FileSet, decl ast.Decl, id *ast.Ident) *Snippet {
- // TODO instead of pretty-printing the node, should use the original source instead
- var buf1 bytes.Buffer
- writeNode(&buf1, fset, decl)
- // wrap text with <pre> tag
- var buf2 bytes.Buffer
- buf2.WriteString("<pre>")
- FormatText(&buf2, buf1.Bytes(), -1, true, id.Name, nil)
- buf2.WriteString("</pre>")
- return &Snippet{fset.Position(id.Pos()).Line, buf2.String()}
-}
-
-func findSpec(list []ast.Spec, id *ast.Ident) ast.Spec {
- for _, spec := range list {
- switch s := spec.(type) {
- case *ast.ImportSpec:
- if s.Name == id {
- return s
- }
- case *ast.ValueSpec:
- for _, n := range s.Names {
- if n == id {
- return s
- }
- }
- case *ast.TypeSpec:
- if s.Name == id {
- return s
- }
- }
- }
- return nil
-}
-
-func genSnippet(fset *token.FileSet, d *ast.GenDecl, id *ast.Ident) *Snippet {
- s := findSpec(d.Specs, id)
- if s == nil {
- return nil // declaration doesn't contain id - exit gracefully
- }
-
- // only use the spec containing the id for the snippet
- dd := &ast.GenDecl{
- Doc: d.Doc,
- TokPos: d.Pos(),
- Tok: d.Tok,
- Lparen: d.Lparen,
- Specs: []ast.Spec{s},
- Rparen: d.Rparen,
- }
-
- return newSnippet(fset, dd, id)
-}
-
-func funcSnippet(fset *token.FileSet, d *ast.FuncDecl, id *ast.Ident) *Snippet {
- if d.Name != id {
- return nil // declaration doesn't contain id - exit gracefully
- }
-
- // only use the function signature for the snippet
- dd := &ast.FuncDecl{
- Doc: d.Doc,
- Recv: d.Recv,
- Name: d.Name,
- Type: d.Type,
- }
-
- return newSnippet(fset, dd, id)
-}
-
-// NewSnippet creates a text snippet from a declaration decl containing an
-// identifier id. Parts of the declaration not containing the identifier
-// may be removed for a more compact snippet.
-//
-func NewSnippet(fset *token.FileSet, decl ast.Decl, id *ast.Ident) (s *Snippet) {
- switch d := decl.(type) {
- case *ast.GenDecl:
- s = genSnippet(fset, d, id)
- case *ast.FuncDecl:
- s = funcSnippet(fset, d, id)
- }
-
- // handle failure gracefully
- if s == nil {
- var buf bytes.Buffer
- fmt.Fprintf(&buf, `<span class="alert">could not generate a snippet for <span class="highlight">%s</span></span>`, id.Name)
- s = &Snippet{fset.Position(id.Pos()).Line, buf.String()}
- }
- return
-}
diff --git a/src/cmd/godoc/spec.go b/src/cmd/godoc/spec.go
deleted file mode 100644
index c11f25d20..000000000
--- a/src/cmd/godoc/spec.go
+++ /dev/null
@@ -1,179 +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 main
-
-// This file contains the mechanism to "linkify" html source
-// text containing EBNF sections (as found in go_spec.html).
-// The result is the input source text with the EBNF sections
-// modified such that identifiers are linked to the respective
-// definitions.
-
-import (
- "bytes"
- "fmt"
- "io"
- "text/scanner"
-)
-
-type ebnfParser struct {
- out io.Writer // parser output
- src []byte // parser input
- scanner scanner.Scanner
- prev int // offset of previous token
- pos int // offset of current token
- tok rune // one token look-ahead
- lit string // token literal
-}
-
-func (p *ebnfParser) flush() {
- p.out.Write(p.src[p.prev:p.pos])
- p.prev = p.pos
-}
-
-func (p *ebnfParser) next() {
- p.tok = p.scanner.Scan()
- p.pos = p.scanner.Position.Offset
- p.lit = p.scanner.TokenText()
-}
-
-func (p *ebnfParser) printf(format string, args ...interface{}) {
- p.flush()
- fmt.Fprintf(p.out, format, args...)
-}
-
-func (p *ebnfParser) errorExpected(msg string) {
- p.printf(`<span class="highlight">error: expected %s, found %s</span>`, msg, scanner.TokenString(p.tok))
-}
-
-func (p *ebnfParser) expect(tok rune) {
- if p.tok != tok {
- p.errorExpected(scanner.TokenString(tok))
- }
- p.next() // make progress in any case
-}
-
-func (p *ebnfParser) parseIdentifier(def bool) {
- if p.tok == scanner.Ident {
- name := p.lit
- if def {
- p.printf(`<a id="%s">%s</a>`, name, name)
- } else {
- p.printf(`<a href="#%s" class="noline">%s</a>`, name, name)
- }
- p.prev += len(name) // skip identifier when printing next time
- p.next()
- } else {
- p.expect(scanner.Ident)
- }
-}
-
-func (p *ebnfParser) parseTerm() bool {
- switch p.tok {
- case scanner.Ident:
- p.parseIdentifier(false)
-
- case scanner.String:
- p.next()
- const ellipsis = '…' // U+2026, the horizontal ellipsis character
- if p.tok == ellipsis {
- p.next()
- p.expect(scanner.String)
- }
-
- case '(':
- p.next()
- p.parseExpression()
- p.expect(')')
-
- case '[':
- p.next()
- p.parseExpression()
- p.expect(']')
-
- case '{':
- p.next()
- p.parseExpression()
- p.expect('}')
-
- default:
- return false // no term found
- }
-
- return true
-}
-
-func (p *ebnfParser) parseSequence() {
- if !p.parseTerm() {
- p.errorExpected("term")
- }
- for p.parseTerm() {
- }
-}
-
-func (p *ebnfParser) parseExpression() {
- for {
- p.parseSequence()
- if p.tok != '|' {
- break
- }
- p.next()
- }
-}
-
-func (p *ebnfParser) parseProduction() {
- p.parseIdentifier(true)
- p.expect('=')
- if p.tok != '.' {
- p.parseExpression()
- }
- p.expect('.')
-}
-
-func (p *ebnfParser) parse(out io.Writer, src []byte) {
- // initialize ebnfParser
- p.out = out
- p.src = src
- p.scanner.Init(bytes.NewBuffer(src))
- p.next() // initializes pos, tok, lit
-
- // process source
- for p.tok != scanner.EOF {
- p.parseProduction()
- }
- p.flush()
-}
-
-// Markers around EBNF sections
-var (
- openTag = []byte(`<pre class="ebnf">`)
- closeTag = []byte(`</pre>`)
-)
-
-func Linkify(out io.Writer, src []byte) {
- for len(src) > 0 {
- // i: beginning of EBNF text (or end of source)
- i := bytes.Index(src, openTag)
- if i < 0 {
- i = len(src) - len(openTag)
- }
- i += len(openTag)
-
- // j: end of EBNF text (or end of source)
- j := bytes.Index(src[i:], closeTag) // close marker
- if j < 0 {
- j = len(src) - i
- }
- j += i
-
- // write text before EBNF
- out.Write(src[0:i])
- // process EBNF
- var p ebnfParser
- p.parse(out, src[i:j])
-
- // advance
- src = src[j:]
- }
-}
diff --git a/src/cmd/godoc/template.go b/src/cmd/godoc/template.go
deleted file mode 100644
index 7b9b9cfeb..000000000
--- a/src/cmd/godoc/template.go
+++ /dev/null
@@ -1,182 +0,0 @@
-// 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.
-
-// Template support for writing HTML documents.
-// Documents that include Template: true in their
-// metadata are executed as input to text/template.
-//
-// This file defines functions for those templates to invoke.
-
-// The template uses the function "code" to inject program
-// source into the output by extracting code from files and
-// injecting them as HTML-escaped <pre> blocks.
-//
-// The syntax is simple: 1, 2, or 3 space-separated arguments:
-//
-// Whole file:
-// {{code "foo.go"}}
-// One line (here the signature of main):
-// {{code "foo.go" `/^func.main/`}}
-// Block of text, determined by start and end (here the body of main):
-// {{code "foo.go" `/^func.main/` `/^}/`
-//
-// Patterns can be `/regular expression/`, a decimal number, or "$"
-// to signify the end of the file. In multi-line matches,
-// lines that end with the four characters
-// OMIT
-// are omitted from the output, making it easy to provide marker
-// lines in the input that will not appear in the output but are easy
-// to identify by pattern.
-
-package main
-
-import (
- "bytes"
- "fmt"
- "log"
- "regexp"
- "strings"
- "text/template"
-)
-
-// Functions in this file panic on error, but the panic is recovered
-// to an error by 'code'.
-
-var templateFuncs = template.FuncMap{
- "code": code,
-}
-
-// contents reads and returns the content of the named file
-// (from the virtual file system, so for example /doc refers to $GOROOT/doc).
-func contents(name string) string {
- file, err := ReadFile(fs, name)
- if err != nil {
- log.Panic(err)
- }
- return string(file)
-}
-
-// stringFor returns a textual representation of the arg, formatted according to its nature.
-func stringFor(arg interface{}) string {
- switch arg := arg.(type) {
- case int:
- return fmt.Sprintf("%d", arg)
- case string:
- if len(arg) > 2 && arg[0] == '/' && arg[len(arg)-1] == '/' {
- return fmt.Sprintf("%#q", arg)
- }
- return fmt.Sprintf("%q", arg)
- default:
- log.Panicf("unrecognized argument: %v type %T", arg, arg)
- }
- return ""
-}
-
-func code(file string, arg ...interface{}) (s string, err error) {
- defer func() {
- if r := recover(); r != nil {
- err = fmt.Errorf("%v", r)
- }
- }()
-
- text := contents(file)
- var command string
- switch len(arg) {
- case 0:
- // text is already whole file.
- command = fmt.Sprintf("code %q", file)
- case 1:
- command = fmt.Sprintf("code %q %s", file, stringFor(arg[0]))
- text = oneLine(file, text, arg[0])
- case 2:
- command = fmt.Sprintf("code %q %s %s", file, stringFor(arg[0]), stringFor(arg[1]))
- text = multipleLines(file, text, arg[0], arg[1])
- default:
- return "", fmt.Errorf("incorrect code invocation: code %q %q", file, arg)
- }
- // Trim spaces from output.
- text = strings.Trim(text, "\n")
- // Replace tabs by spaces, which work better in HTML.
- text = strings.Replace(text, "\t", " ", -1)
- var buf bytes.Buffer
- // HTML-escape text and syntax-color comments like elsewhere.
- FormatText(&buf, []byte(text), -1, true, "", nil)
- // Include the command as a comment.
- text = fmt.Sprintf("<pre><!--{{%s}}\n-->%s</pre>", command, buf.Bytes())
- return text, nil
-}
-
-// parseArg returns the integer or string value of the argument and tells which it is.
-func parseArg(arg interface{}, file string, max int) (ival int, sval string, isInt bool) {
- switch n := arg.(type) {
- case int:
- if n <= 0 || n > max {
- log.Panicf("%q:%d is out of range", file, n)
- }
- return n, "", true
- case string:
- return 0, n, false
- }
- log.Panicf("unrecognized argument %v type %T", arg, arg)
- return
-}
-
-// oneLine returns the single line generated by a two-argument code invocation.
-func oneLine(file, text string, arg interface{}) string {
- lines := strings.SplitAfter(contents(file), "\n")
- line, pattern, isInt := parseArg(arg, file, len(lines))
- if isInt {
- return lines[line-1]
- }
- return lines[match(file, 0, lines, pattern)-1]
-}
-
-// multipleLines returns the text generated by a three-argument code invocation.
-func multipleLines(file, text string, arg1, arg2 interface{}) string {
- lines := strings.SplitAfter(contents(file), "\n")
- line1, pattern1, isInt1 := parseArg(arg1, file, len(lines))
- line2, pattern2, isInt2 := parseArg(arg2, file, len(lines))
- if !isInt1 {
- line1 = match(file, 0, lines, pattern1)
- }
- if !isInt2 {
- line2 = match(file, line1, lines, pattern2)
- } else if line2 < line1 {
- log.Panicf("lines out of order for %q: %d %d", text, line1, line2)
- }
- for k := line1 - 1; k < line2; k++ {
- if strings.HasSuffix(lines[k], "OMIT\n") {
- lines[k] = ""
- }
- }
- return strings.Join(lines[line1-1:line2], "")
-}
-
-// match identifies the input line that matches the pattern in a code invocation.
-// If start>0, match lines starting there rather than at the beginning.
-// The return value is 1-indexed.
-func match(file string, start int, lines []string, pattern string) int {
- // $ matches the end of the file.
- if pattern == "$" {
- if len(lines) == 0 {
- log.Panicf("%q: empty file", file)
- }
- return len(lines)
- }
- // /regexp/ matches the line that matches the regexp.
- if len(pattern) > 2 && pattern[0] == '/' && pattern[len(pattern)-1] == '/' {
- re, err := regexp.Compile(pattern[1 : len(pattern)-1])
- if err != nil {
- log.Panic(err)
- }
- for i := start; i < len(lines); i++ {
- if re.MatchString(lines[i]) {
- return i + 1
- }
- }
- log.Panicf("%s: no match for %#q", file, pattern)
- }
- log.Panicf("unrecognized pattern: %q", pattern)
- return 0
-}
diff --git a/src/cmd/godoc/throttle.go b/src/cmd/godoc/throttle.go
deleted file mode 100644
index ac18b44e0..000000000
--- a/src/cmd/godoc/throttle.go
+++ /dev/null
@@ -1,88 +0,0 @@
-// 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 "time"
-
-// A Throttle permits throttling of a goroutine by
-// calling the Throttle method repeatedly.
-//
-type Throttle struct {
- f float64 // f = (1-r)/r for 0 < r < 1
- dt time.Duration // minimum run time slice; >= 0
- tr time.Duration // accumulated time running
- ts time.Duration // accumulated time stopped
- tt time.Time // earliest throttle time (= time Throttle returned + tm)
-}
-
-// NewThrottle creates a new Throttle with a throttle value r and
-// a minimum allocated run time slice of dt:
-//
-// r == 0: "empty" throttle; the goroutine is always sleeping
-// r == 1: full throttle; the goroutine is never sleeping
-//
-// A value of r == 0.6 throttles a goroutine such that it runs
-// approx. 60% of the time, and sleeps approx. 40% of the time.
-// Values of r < 0 or r > 1 are clamped down to values between 0 and 1.
-// Values of dt < 0 are set to 0.
-//
-func NewThrottle(r float64, dt time.Duration) *Throttle {
- var f float64
- switch {
- case r <= 0:
- f = -1 // indicates always sleep
- case r >= 1:
- f = 0 // assume r == 1 (never sleep)
- default:
- // 0 < r < 1
- f = (1 - r) / r
- }
- if dt < 0 {
- dt = 0
- }
- return &Throttle{f: f, dt: dt, tt: time.Now().Add(dt)}
-}
-
-// Throttle calls time.Sleep such that over time the ratio tr/ts between
-// accumulated run (tr) and sleep times (ts) approximates the value 1/(1-r)
-// where r is the throttle value. Throttle returns immediately (w/o sleeping)
-// if less than tm ns have passed since the last call to Throttle.
-//
-func (p *Throttle) Throttle() {
- if p.f < 0 {
- select {} // always sleep
- }
-
- t0 := time.Now()
- if t0.Before(p.tt) {
- return // keep running (minimum time slice not exhausted yet)
- }
-
- // accumulate running time
- p.tr += t0.Sub(p.tt) + p.dt
-
- // compute sleep time
- // Over time we want:
- //
- // tr/ts = r/(1-r)
- //
- // Thus:
- //
- // ts = tr*f with f = (1-r)/r
- //
- // After some incremental run time δr added to the total run time
- // tr, the incremental sleep-time δs to get to the same ratio again
- // after waking up from time.Sleep is:
- if δs := time.Duration(float64(p.tr)*p.f) - p.ts; δs > 0 {
- time.Sleep(δs)
- }
-
- // accumulate (actual) sleep time
- t1 := time.Now()
- p.ts += t1.Sub(t0)
-
- // set earliest next throttle time
- p.tt = t1.Add(p.dt)
-}
diff --git a/src/cmd/godoc/utils.go b/src/cmd/godoc/utils.go
deleted file mode 100644
index 0cdb7ff7a..000000000
--- a/src/cmd/godoc/utils.go
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This file contains support functionality for godoc.
-
-package main
-
-import (
- pathpkg "path"
- "sync"
- "time"
- "unicode/utf8"
-)
-
-// An RWValue wraps a value and permits mutually exclusive
-// access to it and records the time the value was last set.
-//
-type RWValue struct {
- mutex sync.RWMutex
- value interface{}
- timestamp time.Time // time of last set()
-}
-
-func (v *RWValue) set(value interface{}) {
- v.mutex.Lock()
- v.value = value
- v.timestamp = time.Now()
- v.mutex.Unlock()
-}
-
-func (v *RWValue) get() (interface{}, time.Time) {
- v.mutex.RLock()
- defer v.mutex.RUnlock()
- return v.value, v.timestamp
-}
-
-// isText returns true if a significant prefix of s looks like correct UTF-8;
-// that is, if it is likely that s is human-readable text.
-//
-func isText(s []byte) bool {
- const max = 1024 // at least utf8.UTFMax
- if len(s) > max {
- s = s[0:max]
- }
- for i, c := range string(s) {
- if i+utf8.UTFMax > len(s) {
- // last char may be incomplete - ignore
- break
- }
- if c == 0xFFFD || c < ' ' && c != '\n' && c != '\t' && c != '\f' {
- // decoding error or control character - not a text file
- return false
- }
- }
- return true
-}
-
-// textExt[x] is true if the extension x indicates a text file, and false otherwise.
-var textExt = map[string]bool{
- ".css": false, // must be served raw
- ".js": false, // must be served raw
-}
-
-// isTextFile returns true if the file has a known extension indicating
-// a text file, or if a significant chunk of the specified file looks like
-// correct UTF-8; that is, if it is likely that the file contains human-
-// readable text.
-//
-func isTextFile(filename string) bool {
- // if the extension is known, use it for decision making
- if isText, found := textExt[pathpkg.Ext(filename)]; found {
- return isText
- }
-
- // the extension is not known; read an initial chunk
- // of the file and check if it looks like text
- f, err := fs.Open(filename)
- if err != nil {
- return false
- }
- defer f.Close()
-
- var buf [1024]byte
- n, err := f.Read(buf[0:])
- if err != nil {
- return false
- }
-
- return isText(buf[0:n])
-}
diff --git a/src/cmd/godoc/zip.go b/src/cmd/godoc/zip.go
deleted file mode 100644
index 620eb4f3c..000000000
--- a/src/cmd/godoc/zip.go
+++ /dev/null
@@ -1,236 +0,0 @@
-// 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 provides an implementation of the FileSystem
-// interface based on the contents of a .zip file.
-//
-// Assumptions:
-//
-// - The file paths stored in the zip file must use a slash ('/') as path
-// separator; and they must be relative (i.e., they must not start with
-// a '/' - this is usually the case if the file was created w/o special
-// options).
-// - The zip file system treats the file paths found in the zip internally
-// like absolute paths w/o a leading '/'; i.e., the paths are considered
-// relative to the root of the file system.
-// - All path arguments to file system methods must be absolute paths.
-
-package main
-
-import (
- "archive/zip"
- "fmt"
- "io"
- "os"
- "path"
- "sort"
- "strings"
- "time"
-)
-
-// zipFI is the zip-file based implementation of FileInfo
-type zipFI struct {
- name string // directory-local name
- file *zip.File // nil for a directory
-}
-
-func (fi zipFI) Name() string {
- return fi.name
-}
-
-func (fi zipFI) Size() int64 {
- if f := fi.file; f != nil {
- return int64(f.UncompressedSize)
- }
- return 0 // directory
-}
-
-func (fi zipFI) ModTime() time.Time {
- if f := fi.file; f != nil {
- return f.ModTime()
- }
- return time.Time{} // directory has no modified time entry
-}
-
-func (fi zipFI) Mode() os.FileMode {
- if fi.file == nil {
- // Unix directories typically are executable, hence 555.
- return os.ModeDir | 0555
- }
- return 0444
-}
-
-func (fi zipFI) IsDir() bool {
- return fi.file == nil
-}
-
-func (fi zipFI) Sys() interface{} {
- return nil
-}
-
-// zipFS is the zip-file based implementation of FileSystem
-type zipFS struct {
- *zip.ReadCloser
- list zipList
- name string
-}
-
-func (fs *zipFS) String() string {
- return "zip(" + fs.name + ")"
-}
-
-func (fs *zipFS) Close() error {
- fs.list = nil
- return fs.ReadCloser.Close()
-}
-
-func zipPath(name string) string {
- name = path.Clean(name)
- if !path.IsAbs(name) {
- panic(fmt.Sprintf("stat: not an absolute path: %s", name))
- }
- return name[1:] // strip leading '/'
-}
-
-func (fs *zipFS) stat(abspath string) (int, zipFI, error) {
- i, exact := fs.list.lookup(abspath)
- if i < 0 {
- // abspath has leading '/' stripped - print it explicitly
- return -1, zipFI{}, fmt.Errorf("file not found: /%s", abspath)
- }
- _, name := path.Split(abspath)
- var file *zip.File
- if exact {
- file = fs.list[i] // exact match found - must be a file
- }
- return i, zipFI{name, file}, nil
-}
-
-func (fs *zipFS) Open(abspath string) (readSeekCloser, error) {
- _, fi, err := fs.stat(zipPath(abspath))
- if err != nil {
- return nil, err
- }
- if fi.IsDir() {
- return nil, fmt.Errorf("Open: %s is a directory", abspath)
- }
- r, err := fi.file.Open()
- if err != nil {
- return nil, err
- }
- return &zipSeek{fi.file, r}, nil
-}
-
-type zipSeek struct {
- file *zip.File
- io.ReadCloser
-}
-
-func (f *zipSeek) Seek(offset int64, whence int) (int64, error) {
- if whence == 0 && offset == 0 {
- r, err := f.file.Open()
- if err != nil {
- return 0, err
- }
- f.Close()
- f.ReadCloser = r
- return 0, nil
- }
- return 0, fmt.Errorf("unsupported Seek in %s", f.file.Name)
-}
-
-func (fs *zipFS) Lstat(abspath string) (os.FileInfo, error) {
- _, fi, err := fs.stat(zipPath(abspath))
- return fi, err
-}
-
-func (fs *zipFS) Stat(abspath string) (os.FileInfo, error) {
- _, fi, err := fs.stat(zipPath(abspath))
- return fi, err
-}
-
-func (fs *zipFS) ReadDir(abspath string) ([]os.FileInfo, error) {
- path := zipPath(abspath)
- i, fi, err := fs.stat(path)
- if err != nil {
- return nil, err
- }
- if !fi.IsDir() {
- return nil, fmt.Errorf("ReadDir: %s is not a directory", abspath)
- }
-
- var list []os.FileInfo
- dirname := path + "/"
- prevname := ""
- for _, e := range fs.list[i:] {
- if !strings.HasPrefix(e.Name, dirname) {
- break // not in the same directory anymore
- }
- name := e.Name[len(dirname):] // local name
- file := e
- if i := strings.IndexRune(name, '/'); i >= 0 {
- // We infer directories from files in subdirectories.
- // If we have x/y, return a directory entry for x.
- name = name[0:i] // keep local directory name only
- file = nil
- }
- // If we have x/y and x/z, don't return two directory entries for x.
- // TODO(gri): It should be possible to do this more efficiently
- // by determining the (fs.list) range of local directory entries
- // (via two binary searches).
- if name != prevname {
- list = append(list, zipFI{name, file})
- prevname = name
- }
- }
-
- return list, nil
-}
-
-func NewZipFS(rc *zip.ReadCloser, name string) FileSystem {
- list := make(zipList, len(rc.File))
- copy(list, rc.File) // sort a copy of rc.File
- sort.Sort(list)
- return &zipFS{rc, list, name}
-}
-
-type zipList []*zip.File
-
-// zipList implements sort.Interface
-func (z zipList) Len() int { return len(z) }
-func (z zipList) Less(i, j int) bool { return z[i].Name < z[j].Name }
-func (z zipList) Swap(i, j int) { z[i], z[j] = z[j], z[i] }
-
-// lookup returns the smallest index of an entry with an exact match
-// for name, or an inexact match starting with name/. If there is no
-// such entry, the result is -1, false.
-func (z zipList) lookup(name string) (index int, exact bool) {
- // look for exact match first (name comes before name/ in z)
- i := sort.Search(len(z), func(i int) bool {
- return name <= z[i].Name
- })
- if i >= len(z) {
- return -1, false
- }
- // 0 <= i < len(z)
- if z[i].Name == name {
- return i, true
- }
-
- // look for inexact match (must be in z[i:], if present)
- z = z[i:]
- name += "/"
- j := sort.Search(len(z), func(i int) bool {
- return name <= z[i].Name
- })
- if j >= len(z) {
- return -1, false
- }
- // 0 <= j < len(z)
- if strings.HasPrefix(z[j].Name, name) {
- return i + j, false
- }
-
- return -1, false
-}
diff --git a/src/cmd/gofmt/doc.go b/src/cmd/gofmt/doc.go
index fffc7f06e..94e67fd89 100644
--- a/src/cmd/gofmt/doc.go
+++ b/src/cmd/gofmt/doc.go
@@ -71,6 +71,25 @@ To remove the parentheses:
To convert the package tree from explicit slice upper bounds to implicit ones:
gofmt -r 'α[β:len(α)] -> α[β:]' -w $GOROOT/src/pkg
+
+The simplify command
+
+When invoked with -s gofmt will make the following source transformations where possible.
+
+ An array, slice, or map composite literal of the form:
+ []T{T{}, T{}}
+ will be simplified to:
+ []T{{}, {}}
+
+ A slice expression of the form:
+ s[a:len(s)]
+ will be simplified to:
+ s[a:]
+
+ A range of the form:
+ for x, _ = range v {...}
+ will be simplified to:
+ for x = range v {...}
*/
package main
diff --git a/src/cmd/gofmt/simplify.go b/src/cmd/gofmt/simplify.go
index e9a67a73a..45d000d67 100644
--- a/src/cmd/gofmt/simplify.go
+++ b/src/cmd/gofmt/simplify.go
@@ -90,6 +90,10 @@ func (s *simplifier) Visit(node ast.Node) ast.Visitor {
// Note: We could also simplify slice expressions of the form s[0:b] to s[:b]
// but we leave them as is since sometimes we want to be very explicit
// about the lower bound.
+ // An example where the 0 helps:
+ // x, y, z := b[0:2], b[2:4], b[4:6]
+ // An example where it does not:
+ // x, y := b[:n], b[n:]
case *ast.RangeStmt:
// a range of the form: for x, _ = range v {...}
diff --git a/src/cmd/gofmt/testdata/import.golden b/src/cmd/gofmt/testdata/import.golden
index e8ee44988..51d7be79d 100644
--- a/src/cmd/gofmt/testdata/import.golden
+++ b/src/cmd/gofmt/testdata/import.golden
@@ -106,3 +106,21 @@ import (
"log" // for Fatal
"math"
)
+
+// Test deduping and extended sorting
+import (
+ a "A" // aA
+ b "A" // bA1
+ b "A" // bA2
+ "B" // B
+ . "B" // .B
+ _ "B" // _b
+ "C"
+ a "D" // aD
+)
+
+import (
+ "dedup_by_group"
+
+ "dedup_by_group"
+)
diff --git a/src/cmd/gofmt/testdata/import.input b/src/cmd/gofmt/testdata/import.input
index cc36c3e01..9a4b09dbf 100644
--- a/src/cmd/gofmt/testdata/import.input
+++ b/src/cmd/gofmt/testdata/import.input
@@ -106,3 +106,26 @@ import (
"errors"
"io" // for Reader
)
+
+// Test deduping and extended sorting
+import (
+ "B" // B
+ a "A" // aA
+ b "A" // bA2
+ b "A" // bA1
+ . "B" // .B
+ . "B"
+ "C"
+ "C"
+ "C"
+ a "D" // aD
+ "B"
+ _ "B" // _b
+)
+
+import (
+ "dedup_by_group"
+ "dedup_by_group"
+
+ "dedup_by_group"
+)
diff --git a/src/cmd/ld/data.c b/src/cmd/ld/data.c
index 23fc23e5f..30d7c8185 100644
--- a/src/cmd/ld/data.c
+++ b/src/cmd/ld/data.c
@@ -178,12 +178,14 @@ relocsym(Sym *s)
switch(r->type) {
default:
o = 0;
- if(linkmode == LinkExternal || archreloc(r, s, &o) < 0)
+ if(archreloc(r, s, &o) < 0)
diag("unknown reloc %d", r->type);
break;
case D_TLS:
r->done = 0;
o = 0;
+ if(thechar != '6')
+ o = r->add;
break;
case D_ADDR:
if(linkmode == LinkExternal && r->sym->type != SCONST) {
@@ -305,8 +307,6 @@ void
dynrelocsym(Sym *s)
{
Reloc *r;
- Sym *rel;
- Sym *got;
if(HEADTYPE == Hwindows) {
Sym *rel, *targ;
@@ -343,22 +343,9 @@ dynrelocsym(Sym *s)
return;
}
- got = rel = nil;
- if(flag_shared) {
- rel = lookuprel();
- got = lookup(".got", 0);
- }
- s->rel_ro = 0;
for(r=s->r; r<s->r+s->nr; r++) {
if(r->sym != S && r->sym->type == SDYNIMPORT || r->type >= 256)
adddynrel(s, r);
- if(flag_shared && r->sym != S && s->type != SDYNIMPORT && r->type == D_ADDR
- && (s == got || s->type == SDATA || s->type == SGOSTRING || s->type == STYPE || s->type == SRODATA)) {
- // Create address based RELATIVE relocation
- adddynrela(rel, s, r);
- if(s->type < SNOPTRDATA)
- s->rel_ro = 1;
- }
}
}
@@ -367,7 +354,7 @@ dynreloc(void)
{
Sym *s;
- // -d supresses dynamic loader format, so we may as well not
+ // -d suppresses dynamic loader format, so we may as well not
// compute these sections or mark their symbols as reachable.
if(debug['d'] && HEADTYPE != Hwindows)
return;
@@ -389,6 +376,12 @@ symgrow(Sym *s, int32 siz)
if(s->np >= siz)
return;
+ if(s->np > s->maxp) {
+ cursym = s;
+ diag("corrupt symbol data: np=%lld > maxp=%lld", (vlong)s->np, (vlong)s->maxp);
+ errorexit();
+ }
+
if(s->maxp < siz) {
if(s->maxp == 0)
s->maxp = 8;
@@ -763,7 +756,7 @@ setuintxx(Sym *s, vlong off, uint64 v, vlong wid)
s->p[off+i] = cast[inuxi8[i]];
break;
}
- return off;
+ return off+wid;
}
vlong
@@ -800,28 +793,28 @@ adduint64(Sym *s, uint64 v)
return adduintxx(s, v, 8);
}
-void
+vlong
setuint8(Sym *s, vlong r, uint8 v)
{
- setuintxx(s, r, v, 1);
+ return setuintxx(s, r, v, 1);
}
-void
+vlong
setuint16(Sym *s, vlong r, uint16 v)
{
- setuintxx(s, r, v, 2);
+ return setuintxx(s, r, v, 2);
}
-void
+vlong
setuint32(Sym *s, vlong r, uint32 v)
{
- setuintxx(s, r, v, 4);
+ return setuintxx(s, r, v, 4);
}
-void
+vlong
setuint64(Sym *s, vlong r, uint64 v)
{
- setuintxx(s, r, v, 8);
+ return setuintxx(s, r, v, 8);
}
vlong
@@ -842,7 +835,7 @@ addaddrplus(Sym *s, Sym *t, vlong add)
r->siz = PtrSize;
r->type = D_ADDR;
r->add = add;
- return i;
+ return i + r->siz;
}
static vlong
@@ -863,7 +856,7 @@ addaddrplus4(Sym *s, Sym *t, vlong add)
r->siz = 4;
r->type = D_ADDR;
r->add = add;
- return i;
+ return i + r->siz;
}
vlong
@@ -884,7 +877,7 @@ addpcrelplus(Sym *s, Sym *t, vlong add)
r->add = add;
r->type = D_PCREL;
r->siz = 4;
- return i;
+ return i + r->siz;
}
vlong
@@ -911,7 +904,7 @@ setaddrplus(Sym *s, vlong off, Sym *t, vlong add)
r->siz = PtrSize;
r->type = D_ADDR;
r->add = add;
- return off;
+ return off + r->siz;
}
vlong
@@ -937,7 +930,7 @@ addsize(Sym *s, Sym *t)
r->off = i;
r->siz = PtrSize;
r->type = D_SIZE;
- return i;
+ return i + r->siz;
}
void
@@ -1040,6 +1033,7 @@ dodata(void)
int32 n;
vlong datsize;
Section *sect;
+ Segment *segro;
Sym *s, *last, **l;
Sym *gcdata1, *gcbss1;
@@ -1047,13 +1041,8 @@ dodata(void)
Bprint(&bso, "%5.2f dodata\n", cputime());
Bflush(&bso);
- // define garbage collection symbols
gcdata1 = lookup("gcdata", 0);
- gcdata1->type = STYPE;
- gcdata1->reachable = 1;
gcbss1 = lookup("gcbss", 0);
- gcbss1->type = STYPE;
- gcbss1->reachable = 1;
// size of .data and .bss section. the zero value is later replaced by the actual size of the section.
adduintxx(gcdata1, 0, PtrSize);
@@ -1103,12 +1092,6 @@ dodata(void)
}
*l = nil;
- if(flag_shared) {
- for(s=datap; s != nil; s = s->next) {
- if(s->rel_ro)
- s->type = SDATARELRO;
- }
- }
datap = listsort(datap, datcmp, offsetof(Sym, next));
/*
@@ -1135,40 +1118,37 @@ dodata(void)
sect->vaddr = datsize;
s->sect = sect;
s->type = SDATA;
- s->value = datsize;
+ s->value = datsize - sect->vaddr;
growdatsize(&datsize, s);
sect->len = datsize - sect->vaddr;
}
/* pointer-free data */
sect = addsection(&segdata, ".noptrdata", 06);
- sect->align = maxalign(s, SDATARELRO-1);
+ sect->align = maxalign(s, SINITARR-1);
datsize = rnd(datsize, sect->align);
sect->vaddr = datsize;
lookup("noptrdata", 0)->sect = sect;
lookup("enoptrdata", 0)->sect = sect;
- for(; s != nil && s->type < SDATARELRO; s = s->next) {
+ for(; s != nil && s->type < SINITARR; s = s->next) {
datsize = aligndatsize(datsize, s);
s->sect = sect;
s->type = SDATA;
- s->value = datsize;
+ s->value = datsize - sect->vaddr;
growdatsize(&datsize, s);
}
sect->len = datsize - sect->vaddr;
- /* dynamic relocated rodata */
+ /* shared library initializer */
if(flag_shared) {
- sect = addsection(&segdata, ".data.rel.ro", 06);
- sect->align = maxalign(s, SDATARELRO);
+ sect = addsection(&segdata, ".init_array", 06);
+ sect->align = maxalign(s, SINITARR);
datsize = rnd(datsize, sect->align);
sect->vaddr = datsize;
- lookup("datarelro", 0)->sect = sect;
- lookup("edatarelro", 0)->sect = sect;
- for(; s != nil && s->type == SDATARELRO; s = s->next) {
+ for(; s != nil && s->type == SINITARR; s = s->next) {
datsize = aligndatsize(datsize, s);
s->sect = sect;
- s->type = SDATA;
- s->value = datsize;
+ s->value = datsize - sect->vaddr;
growdatsize(&datsize, s);
}
sect->len = datsize - sect->vaddr;
@@ -1182,14 +1162,14 @@ dodata(void)
lookup("data", 0)->sect = sect;
lookup("edata", 0)->sect = sect;
for(; s != nil && s->type < SBSS; s = s->next) {
- if(s->type == SDATARELRO) {
+ if(s->type == SINITARR) {
cursym = s;
diag("unexpected symbol type %d", s->type);
}
s->sect = sect;
s->type = SDATA;
datsize = aligndatsize(datsize, s);
- s->value = datsize;
+ s->value = datsize - sect->vaddr;
gcaddsym(gcdata1, s, datsize - sect->vaddr); // gc
growdatsize(&datsize, s);
}
@@ -1208,7 +1188,7 @@ dodata(void)
for(; s != nil && s->type < SNOPTRBSS; s = s->next) {
s->sect = sect;
datsize = aligndatsize(datsize, s);
- s->value = datsize;
+ s->value = datsize - sect->vaddr;
gcaddsym(gcbss1, s, datsize - sect->vaddr); // gc
growdatsize(&datsize, s);
}
@@ -1227,7 +1207,7 @@ dodata(void)
for(; s != nil && s->type == SNOPTRBSS; s = s->next) {
datsize = aligndatsize(datsize, s);
s->sect = sect;
- s->value = datsize;
+ s->value = datsize - sect->vaddr;
growdatsize(&datsize, s);
}
sect->len = datsize - sect->vaddr;
@@ -1246,7 +1226,7 @@ dodata(void)
for(; s != nil && s->type == STLSBSS; s = s->next) {
datsize = aligndatsize(datsize, s);
s->sect = sect;
- s->value = datsize;
+ s->value = datsize - sect->vaddr;
growdatsize(&datsize, s);
}
sect->len = datsize;
@@ -1257,27 +1237,56 @@ dodata(void)
diag("unexpected symbol type %d for %s", s->type, s->name);
}
- /* we finished segdata, begin segtext */
+ /*
+ * We finished data, begin read-only data.
+ * Not all systems support a separate read-only non-executable data section.
+ * ELF systems do.
+ * OS X and Plan 9 do not.
+ * Windows PE may, but if so we have not implemented it.
+ * And if we're using external linking mode, the point is moot,
+ * since it's not our decision; that code expects the sections in
+ * segtext.
+ */
+ if(iself && linkmode == LinkInternal)
+ segro = &segrodata;
+ else
+ segro = &segtext;
+
s = datap;
+
+ datsize = 0;
+
+ /* read-only executable ELF, Mach-O sections */
+ for(; s != nil && s->type < STYPE; s = s->next) {
+ sect = addsection(&segtext, s->name, 04);
+ sect->align = symalign(s);
+ datsize = rnd(datsize, sect->align);
+ sect->vaddr = datsize;
+ s->sect = sect;
+ s->type = SRODATA;
+ s->value = datsize - sect->vaddr;
+ growdatsize(&datsize, s);
+ sect->len = datsize - sect->vaddr;
+ }
/* read-only data */
- sect = addsection(&segtext, ".rodata", 04);
+ sect = addsection(segro, ".rodata", 04);
sect->align = maxalign(s, STYPELINK-1);
+ datsize = rnd(datsize, sect->align);
sect->vaddr = 0;
lookup("rodata", 0)->sect = sect;
lookup("erodata", 0)->sect = sect;
- datsize = 0;
for(; s != nil && s->type < STYPELINK; s = s->next) {
datsize = aligndatsize(datsize, s);
s->sect = sect;
s->type = SRODATA;
- s->value = datsize;
+ s->value = datsize - sect->vaddr;
growdatsize(&datsize, s);
}
sect->len = datsize - sect->vaddr;
/* typelink */
- sect = addsection(&segtext, ".typelink", 04);
+ sect = addsection(segro, ".typelink", 04);
sect->align = maxalign(s, STYPELINK);
datsize = rnd(datsize, sect->align);
sect->vaddr = datsize;
@@ -1287,13 +1296,13 @@ dodata(void)
datsize = aligndatsize(datsize, s);
s->sect = sect;
s->type = SRODATA;
- s->value = datsize;
+ s->value = datsize - sect->vaddr;
growdatsize(&datsize, s);
}
sect->len = datsize - sect->vaddr;
/* gosymtab */
- sect = addsection(&segtext, ".gosymtab", 04);
+ sect = addsection(segro, ".gosymtab", 04);
sect->align = maxalign(s, SPCLNTAB-1);
datsize = rnd(datsize, sect->align);
sect->vaddr = datsize;
@@ -1303,13 +1312,13 @@ dodata(void)
datsize = aligndatsize(datsize, s);
s->sect = sect;
s->type = SRODATA;
- s->value = datsize;
+ s->value = datsize - sect->vaddr;
growdatsize(&datsize, s);
}
sect->len = datsize - sect->vaddr;
/* gopclntab */
- sect = addsection(&segtext, ".gopclntab", 04);
+ sect = addsection(segro, ".gopclntab", 04);
sect->align = maxalign(s, SELFROSECT-1);
datsize = rnd(datsize, sect->align);
sect->vaddr = datsize;
@@ -1319,33 +1328,35 @@ dodata(void)
datsize = aligndatsize(datsize, s);
s->sect = sect;
s->type = SRODATA;
- s->value = datsize;
+ s->value = datsize - sect->vaddr;
growdatsize(&datsize, s);
}
sect->len = datsize - sect->vaddr;
/* read-only ELF, Mach-O sections */
for(; s != nil && s->type < SELFSECT; s = s->next) {
- sect = addsection(&segtext, s->name, 04);
+ sect = addsection(segro, s->name, 04);
sect->align = symalign(s);
datsize = rnd(datsize, sect->align);
sect->vaddr = datsize;
s->sect = sect;
s->type = SRODATA;
- s->value = datsize;
+ s->value = datsize - sect->vaddr;
growdatsize(&datsize, s);
sect->len = datsize - sect->vaddr;
}
// 6g uses 4-byte relocation offsets, so the entire segment must fit in 32 bits.
if(datsize != (uint32)datsize) {
- diag("text segment too large");
+ diag("read-only data segment too large");
}
/* number the sections */
n = 1;
for(sect = segtext.sect; sect != nil; sect = sect->next)
sect->extnum = n++;
+ for(sect = segrodata.sect; sect != nil; sect = sect->next)
+ sect->extnum = n++;
for(sect = segdata.sect; sect != nil; sect = sect->next)
sect->extnum = n++;
}
@@ -1396,7 +1407,7 @@ textaddress(void)
void
address(void)
{
- Section *s, *text, *data, *rodata, *symtab, *pclntab, *noptr, *bss, *noptrbss, *datarelro;
+ Section *s, *text, *data, *rodata, *symtab, *pclntab, *noptr, *bss, *noptrbss;
Section *typelink;
Sym *sym, *sub;
uvlong va;
@@ -1407,6 +1418,7 @@ address(void)
segtext.vaddr = va;
segtext.fileoff = HEADR;
for(s=segtext.sect; s != nil; s=s->next) {
+//print("%s at %#llux + %#llux\n", s->name, va, (vlong)s->len);
va = rnd(va, s->align);
s->vaddr = va;
va += s->len;
@@ -1414,8 +1426,25 @@ address(void)
segtext.len = va - INITTEXT;
segtext.filelen = segtext.len;
- va = rnd(va, INITRND);
+ if(segrodata.sect != nil) {
+ // align to page boundary so as not to mix
+ // rodata and executable text.
+ va = rnd(va, INITRND);
+
+ segrodata.rwx = 04;
+ segrodata.vaddr = va;
+ segrodata.fileoff = va - segtext.vaddr + segtext.fileoff;
+ segrodata.filelen = 0;
+ for(s=segrodata.sect; s != nil; s=s->next) {
+ va = rnd(va, s->align);
+ s->vaddr = va;
+ va += s->len;
+ }
+ segrodata.len = va - segrodata.vaddr;
+ segrodata.filelen = segrodata.len;
+ }
+ va = rnd(va, INITRND);
segdata.rwx = 06;
segdata.vaddr = va;
segdata.fileoff = va - segtext.vaddr + segtext.fileoff;
@@ -1428,7 +1457,6 @@ address(void)
noptr = nil;
bss = nil;
noptrbss = nil;
- datarelro = nil;
for(s=segdata.sect; s != nil; s=s->next) {
vlen = s->len;
if(s->next)
@@ -1444,23 +1472,21 @@ address(void)
bss = s;
if(strcmp(s->name, ".noptrbss") == 0)
noptrbss = s;
- if(strcmp(s->name, ".data.rel.ro") == 0)
- datarelro = s;
}
segdata.filelen = bss->vaddr - segdata.vaddr;
text = segtext.sect;
- rodata = text->next;
+ if(segrodata.sect)
+ rodata = segrodata.sect;
+ else
+ rodata = text->next;
typelink = rodata->next;
symtab = typelink->next;
pclntab = symtab->next;
for(sym = datap; sym != nil; sym = sym->next) {
cursym = sym;
- if(sym->type < SNOPTRDATA)
- sym->value += rodata->vaddr;
- else
- sym->value += segdata.sect->vaddr;
+ sym->value += sym->sect->vaddr;
for(sub = sym->sub; sub != nil; sub = sub->sub)
sub->value += sym->value;
}
@@ -1471,17 +1497,13 @@ address(void)
xdefine("erodata", SRODATA, rodata->vaddr + rodata->len);
xdefine("typelink", SRODATA, typelink->vaddr);
xdefine("etypelink", SRODATA, typelink->vaddr + typelink->len);
- if(datarelro != nil) {
- xdefine("datarelro", SRODATA, datarelro->vaddr);
- xdefine("edatarelro", SRODATA, datarelro->vaddr + datarelro->len);
- }
sym = lookup("gcdata", 0);
- xdefine("egcdata", STYPE, symaddr(sym) + sym->size);
+ xdefine("egcdata", SRODATA, symaddr(sym) + sym->size);
lookup("egcdata", 0)->sect = sym->sect;
sym = lookup("gcbss", 0);
- xdefine("egcbss", STYPE, symaddr(sym) + sym->size);
+ xdefine("egcbss", SRODATA, symaddr(sym) + sym->size);
lookup("egcbss", 0)->sect = sym->sect;
xdefine("symtab", SRODATA, symtab->vaddr);
diff --git a/src/cmd/ld/doc.go b/src/cmd/ld/doc.go
index 3493f41d8..2adda25f2 100644
--- a/src/cmd/ld/doc.go
+++ b/src/cmd/ld/doc.go
@@ -33,6 +33,8 @@ Options new in this version:
linker. This flag cannot be used when $GOOS is windows.
-H darwin (only in 6l/8l)
Write Apple Mach-O binaries (default when $GOOS is darwin)
+ -H dragonfly (only in 6l/8l)
+ Write DragonFly ELF binaries (default when $GOOS is dragonfly)
-H linux
Write Linux ELF binaries (default when $GOOS is linux)
-H freebsd
@@ -52,6 +54,8 @@ Options new in this version:
The default is the single location $GOROOT/pkg/$GOOS_$GOARCH.
-r dir1:dir2:...
Set the dynamic linker search path when using ELF.
+ -s
+ Omit the symbol table and debug information.
-V
Print the linker version.
-X symbol value
diff --git a/src/cmd/ld/dwarf.c b/src/cmd/ld/dwarf.c
index 98b03f1c3..c832bcc94 100644
--- a/src/cmd/ld/dwarf.c
+++ b/src/cmd/ld/dwarf.c
@@ -39,6 +39,8 @@ static Sym* infosym;
static vlong infosympos;
static vlong frameo;
static vlong framesize;
+static Sym* framesym;
+static vlong framesympos;
static vlong pubnameso;
static vlong pubnamessize;
static vlong pubtypeso;
@@ -60,6 +62,10 @@ static Sym *linesec;
static vlong linereloco;
static vlong linerelocsize;
+static Sym *framesec;
+static vlong framereloco;
+static vlong framerelocsize;
+
static char gdbscript[1024];
/*
@@ -133,7 +139,7 @@ sleb128put(vlong v)
/*
* Defining Abbrevs. This is hardcoded, and there will be
* only a handful of them. The DWARF spec places no restriction on
- * the ordering of atributes in the Abbrevs and DIEs, and we will
+ * the ordering of attributes in the Abbrevs and DIEs, and we will
* always write them out in the order of declaration in the abbrev.
* This implementation relies on tag, attr < 127, so they serialize as
* a char. Higher numbered user-defined tags or attributes can be used
@@ -1646,7 +1652,7 @@ guesslang(char *s)
}
/*
- * Generate short opcodes when possible, long ones when neccesary.
+ * Generate short opcodes when possible, long ones when necessary.
* See section 6.2.5
*/
@@ -1938,7 +1944,7 @@ enum
{
CIERESERVE = 16,
DATAALIGNMENTFACTOR = -4, // TODO -PtrSize?
- FAKERETURNCOLUMN = 16 // TODO gdb6 doesnt like > 15?
+ FAKERETURNCOLUMN = 16 // TODO gdb6 doesn't like > 15?
};
static void
@@ -1968,6 +1974,9 @@ writeframes(void)
Sym *s;
vlong fdeo, fdesize, pad, cfa, pc;
+ if(framesec == S)
+ framesec = lookup(".dwarfframe", 0);
+ framesec->nr = 0;
frameo = cpos();
// Emit the CIE, Section 6.4.1
@@ -2026,8 +2035,14 @@ writeframes(void)
// Emit the FDE header for real, Section 6.4.1.
cseek(fdeo);
LPUT(fdesize);
- LPUT(0);
- addrput(p->pc);
+ if(linkmode == LinkExternal) {
+ adddwarfrel(framesec, framesym, frameo, 4, 0);
+ adddwarfrel(framesec, s, frameo, PtrSize, 0);
+ }
+ else {
+ LPUT(0);
+ addrput(p->pc);
+ }
addrput(s->size);
cseek(fdeo + 4 + fdesize);
}
@@ -2360,6 +2375,10 @@ dwarfemitdebugsections(void)
linereloco = writedwarfreloc(linesec);
linerelocsize = cpos() - linereloco;
align(linerelocsize);
+
+ framereloco = writedwarfreloc(framesec);
+ framerelocsize = cpos() - framereloco;
+ align(framerelocsize);
}
/*
@@ -2382,6 +2401,7 @@ enum
ElfStrRelDebugInfo,
ElfStrRelDebugAranges,
ElfStrRelDebugLine,
+ ElfStrRelDebugFrame,
NElfStrDbg
};
@@ -2410,10 +2430,12 @@ dwarfaddshstrings(Sym *shstrtab)
elfstrdbg[ElfStrRelDebugInfo] = addstring(shstrtab, ".rela.debug_info");
elfstrdbg[ElfStrRelDebugAranges] = addstring(shstrtab, ".rela.debug_aranges");
elfstrdbg[ElfStrRelDebugLine] = addstring(shstrtab, ".rela.debug_line");
+ elfstrdbg[ElfStrRelDebugFrame] = addstring(shstrtab, ".rela.debug_frame");
} else {
elfstrdbg[ElfStrRelDebugInfo] = addstring(shstrtab, ".rel.debug_info");
elfstrdbg[ElfStrRelDebugAranges] = addstring(shstrtab, ".rel.debug_aranges");
elfstrdbg[ElfStrRelDebugLine] = addstring(shstrtab, ".rel.debug_line");
+ elfstrdbg[ElfStrRelDebugFrame] = addstring(shstrtab, ".rel.debug_frame");
}
infosym = lookup(".debug_info", 0);
@@ -2424,6 +2446,9 @@ dwarfaddshstrings(Sym *shstrtab)
linesym = lookup(".debug_line", 0);
linesym->hide = 1;
+
+ framesym = lookup(".debug_frame", 0);
+ framesym->hide = 1;
}
}
@@ -2444,6 +2469,10 @@ dwarfaddelfsectionsyms()
linesympos = cpos();
putelfsectionsym(linesym, 0);
}
+ if(framesym != nil) {
+ framesympos = cpos();
+ putelfsectionsym(framesym, 0);
+ }
}
static void
@@ -2469,7 +2498,7 @@ dwarfaddelfrelocheader(int elfstr, ElfShdr *shdata, vlong off, vlong size)
void
dwarfaddelfheaders(void)
{
- ElfShdr *sh, *shinfo, *sharanges, *shline;
+ ElfShdr *sh, *shinfo, *sharanges, *shline, *shframe;
if(debug['w']) // disable dwarf
return;
@@ -2496,6 +2525,9 @@ dwarfaddelfheaders(void)
sh->off = frameo;
sh->size = framesize;
sh->addralign = 1;
+ if(framesympos > 0)
+ putelfsymshndx(framesympos, sh->shnum);
+ shframe = sh;
sh = newElfShdr(elfstrdbg[ElfStrDebugInfo]);
sh->type = SHT_PROGBITS;
@@ -2548,6 +2580,9 @@ dwarfaddelfheaders(void)
if(linerelocsize)
dwarfaddelfrelocheader(ElfStrRelDebugLine, shline, linereloco, linerelocsize);
+
+ if(framerelocsize)
+ dwarfaddelfrelocheader(ElfStrRelDebugFrame, shframe, framereloco, framerelocsize);
}
/*
diff --git a/src/cmd/ld/elf.c b/src/cmd/ld/elf.c
index daef5793f..6b3638ec5 100644
--- a/src/cmd/ld/elf.c
+++ b/src/cmd/ld/elf.c
@@ -864,6 +864,8 @@ elfemitreloc(void)
elfrelocsect(segtext.sect, textp);
for(sect=segtext.sect->next; sect!=nil; sect=sect->next)
elfrelocsect(sect, datap);
+ for(sect=segrodata.sect; sect!=nil; sect=sect->next)
+ elfrelocsect(sect, datap);
for(sect=segdata.sect; sect!=nil; sect=sect->next)
elfrelocsect(sect, datap);
}
@@ -887,7 +889,12 @@ doelf(void)
addstring(shstrtab, ".data");
addstring(shstrtab, ".bss");
addstring(shstrtab, ".noptrbss");
- if(linkmode == LinkExternal && HEADTYPE != Hopenbsd)
+ // generate .tbss section (except for OpenBSD where it's not supported)
+ // for dynamic internal linker or external linking, so that various
+ // binutils could correctly calculate PT_TLS size.
+ // see http://golang.org/issue/5200.
+ if(HEADTYPE != Hopenbsd)
+ if(!debug['d'] || linkmode == LinkExternal)
addstring(shstrtab, ".tbss");
if(HEADTYPE == Hnetbsd)
addstring(shstrtab, ".note.netbsd.ident");
@@ -898,14 +905,11 @@ doelf(void)
addstring(shstrtab, ".elfdata");
addstring(shstrtab, ".rodata");
addstring(shstrtab, ".typelink");
- if(flag_shared)
- addstring(shstrtab, ".data.rel.ro");
- addstring(shstrtab, ".gcdata");
- addstring(shstrtab, ".gcbss");
addstring(shstrtab, ".gosymtab");
addstring(shstrtab, ".gopclntab");
if(linkmode == LinkExternal) {
+ debug_s = debug['s'];
debug['s'] = 0;
debug['d'] = 1;
@@ -913,8 +917,6 @@ doelf(void)
addstring(shstrtab, ".rela.text");
addstring(shstrtab, ".rela.rodata");
addstring(shstrtab, ".rela.typelink");
- addstring(shstrtab, ".rela.gcdata");
- addstring(shstrtab, ".rela.gcbss");
addstring(shstrtab, ".rela.gosymtab");
addstring(shstrtab, ".rela.gopclntab");
addstring(shstrtab, ".rela.noptrdata");
@@ -923,8 +925,6 @@ doelf(void)
addstring(shstrtab, ".rel.text");
addstring(shstrtab, ".rel.rodata");
addstring(shstrtab, ".rel.typelink");
- addstring(shstrtab, ".rel.gcdata");
- addstring(shstrtab, ".rel.gcbss");
addstring(shstrtab, ".rel.gosymtab");
addstring(shstrtab, ".rel.gopclntab");
addstring(shstrtab, ".rel.noptrdata");
@@ -934,6 +934,14 @@ doelf(void)
addstring(shstrtab, ".note.GNU-stack");
}
+ if(flag_shared) {
+ addstring(shstrtab, ".init_array");
+ if(thechar == '6')
+ addstring(shstrtab, ".rela.init_array");
+ else
+ addstring(shstrtab, ".rel.init_array");
+ }
+
if(!debug['s']) {
addstring(shstrtab, ".symtab");
addstring(shstrtab, ".strtab");
@@ -1001,7 +1009,7 @@ doelf(void)
s = lookup(".plt", 0);
s->reachable = 1;
- s->type = SELFROSECT;
+ s->type = SELFRXSECT;
elfsetupplt();
@@ -1062,13 +1070,6 @@ doelf(void)
elfwritedynent(s, DT_DEBUG, 0);
- if(flag_shared) {
- Sym *init_sym = lookup(LIBINITENTRY, 0);
- if(init_sym->type != STEXT)
- diag("entry not text: %s", init_sym->name);
- elfwritedynentsym(s, DT_INIT, init_sym);
- }
-
// Do not write DT_NULL. elfdynhash will finish it.
}
}
@@ -1105,6 +1106,8 @@ asmbelfsetup(void)
for(sect=segtext.sect; sect!=nil; sect=sect->next)
elfshalloc(sect);
+ for(sect=segrodata.sect; sect!=nil; sect=sect->next)
+ elfshalloc(sect);
for(sect=segdata.sect; sect!=nil; sect=sect->next)
elfshalloc(sect);
}
@@ -1186,6 +1189,9 @@ asmbelf(vlong symo)
case Hopenbsd:
interpreter = openbsddynld;
break;
+ case Hdragonfly:
+ interpreter = dragonflydynld;
+ break;
}
}
resoff -= elfinterp(sh, startva, resoff, interpreter);
@@ -1232,6 +1238,8 @@ asmbelf(vlong symo)
USED(resoff);
elfphload(&segtext);
+ if(segrodata.sect != nil)
+ elfphload(&segrodata);
elfphload(&segdata);
/* Dynamic linking sections */
@@ -1397,12 +1405,16 @@ elfobj:
for(sect=segtext.sect; sect!=nil; sect=sect->next)
elfshbits(sect);
+ for(sect=segrodata.sect; sect!=nil; sect=sect->next)
+ elfshbits(sect);
for(sect=segdata.sect; sect!=nil; sect=sect->next)
elfshbits(sect);
if(linkmode == LinkExternal) {
for(sect=segtext.sect; sect!=nil; sect=sect->next)
elfshreloc(sect);
+ for(sect=segrodata.sect; sect!=nil; sect=sect->next)
+ elfshreloc(sect);
for(sect=segdata.sect; sect!=nil; sect=sect->next)
elfshreloc(sect);
// add a .note.GNU-stack section to mark the stack as non-executable
@@ -1412,6 +1424,16 @@ elfobj:
sh->flags = 0;
}
+ // generate .tbss section for dynamic internal linking (except for OpenBSD)
+ // external linking generates .tbss in data.c
+ if(linkmode == LinkInternal && !debug['d'] && HEADTYPE != Hopenbsd) {
+ sh = elfshname(".tbss");
+ sh->type = SHT_NOBITS;
+ sh->addralign = PtrSize;
+ sh->size = -tlsoffset;
+ sh->flags = SHF_ALLOC | SHF_TLS | SHF_WRITE;
+ }
+
if(!debug['s']) {
sh = elfshname(".symtab");
sh->type = SHT_SYMTAB;
@@ -1442,6 +1464,8 @@ elfobj:
eh->ident[EI_OSABI] = ELFOSABI_NETBSD;
else if(HEADTYPE == Hopenbsd)
eh->ident[EI_OSABI] = ELFOSABI_OPENBSD;
+ else if(HEADTYPE == Hdragonfly)
+ eh->ident[EI_OSABI] = ELFOSABI_NONE;
if(PtrSize == 8)
eh->ident[EI_CLASS] = ELFCLASS64;
else
@@ -1449,9 +1473,7 @@ elfobj:
eh->ident[EI_DATA] = ELFDATA2LSB;
eh->ident[EI_VERSION] = EV_CURRENT;
- if(flag_shared)
- eh->type = ET_DYN;
- else if(linkmode == LinkExternal)
+ if(linkmode == LinkExternal)
eh->type = ET_REL;
else
eh->type = ET_EXEC;
diff --git a/src/cmd/ld/elf.h b/src/cmd/ld/elf.h
index 24c0ac43e..5b2ff041a 100644
--- a/src/cmd/ld/elf.h
+++ b/src/cmd/ld/elf.h
@@ -569,6 +569,8 @@ typedef struct {
#define R_ARM_GOT_PREL 96
#define R_ARM_GNU_VTENTRY 100
#define R_ARM_GNU_VTINHERIT 101
+#define R_ARM_TLS_IE32 107
+#define R_ARM_TLS_LE32 108
#define R_ARM_RSBREL32 250
#define R_ARM_THM_RPC22 251
#define R_ARM_RREL32 252
@@ -576,7 +578,7 @@ typedef struct {
#define R_ARM_RPC24 254
#define R_ARM_RBASE 255
-#define R_ARM_COUNT 37 /* Count of defined relocation types. */
+#define R_ARM_COUNT 38 /* Count of defined relocation types. */
#define R_386_NONE 0 /* No relocation. */
@@ -1007,6 +1009,7 @@ extern char linuxdynld[];
extern char freebsddynld[];
extern char netbsddynld[];
extern char openbsddynld[];
+extern char dragonflydynld[];
int elfreloc1(Reloc*, vlong sectoff);
void putelfsectionsyms(void);
diff --git a/src/cmd/ld/go.c b/src/cmd/ld/go.c
index 47fdbe944..39ffa3d87 100644
--- a/src/cmd/ld/go.c
+++ b/src/cmd/ld/go.c
@@ -37,13 +37,12 @@ static void imported(char *pkg, char *import);
static int
hashstr(char *name)
{
- int h;
+ uint32 h;
char *cp;
h = 0;
for(cp = name; *cp; h += *cp++)
h *= 1119;
- // not if(h < 0) h = ~h, because gcc 4.3 -O2 miscompiles it.
h &= 0xffffff;
return h;
}
@@ -499,6 +498,9 @@ loadcgo(char *file, char *pkg, char *p, int n)
local = expandpkg(local, pkg);
s = lookup(local, 0);
+ if(flag_shared && s == lookup("main", 0))
+ continue;
+
// export overrides import, for openbsd/cgo.
// see issue 4878.
if(s->dynimplib != nil) {
@@ -613,7 +615,7 @@ markflood(void)
}
static char*
-morename[] =
+markextra[] =
{
"runtime.morestack",
"runtime.morestackx",
@@ -629,6 +631,12 @@ morename[] =
"runtime.morestack32",
"runtime.morestack40",
"runtime.morestack48",
+
+ // on arm, lock in the div/mod helpers too
+ "_div",
+ "_divu",
+ "_mod",
+ "_modu",
};
static int
@@ -674,10 +682,8 @@ deadcode(void)
Bprint(&bso, "%5.2f deadcode\n", cputime());
mark(lookup(INITENTRY, 0));
- if(flag_shared)
- mark(lookup(LIBINITENTRY, 0));
- for(i=0; i<nelem(morename); i++)
- mark(lookup(morename[i], 0));
+ for(i=0; i<nelem(markextra); i++)
+ mark(lookup(markextra[i], 0));
for(i=0; i<ndynexp; i++)
mark(dynexp[i]);
@@ -794,6 +800,8 @@ Zconv(Fmt *fp)
return fmtstrcpy(fp, "<nil>");
se = s + strlen(s);
+
+ // NOTE: Keep in sync with ../gc/go.c:/^Zconv.
while(s < se) {
n = chartorune(&r, s);
s += n;
@@ -822,6 +830,9 @@ Zconv(Fmt *fp)
fmtrune(fp, '\\');
fmtrune(fp, r);
break;
+ case 0xFEFF: // BOM, basically disallowed in source code
+ fmtstrcpy(fp, "\\uFEFF");
+ break;
}
}
return 0;
diff --git a/src/cmd/ld/ldmacho.c b/src/cmd/ld/ldmacho.c
index d384a5094..e0f5405f6 100644
--- a/src/cmd/ld/ldmacho.c
+++ b/src/cmd/ld/ldmacho.c
@@ -432,7 +432,7 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn)
int64 base;
MachoSect *sect;
MachoRel *rel;
- Sym *s, *outer;
+ Sym *s, *s1, *outer;
MachoCmd *c;
MachoSymtab *symtab;
MachoDysymtab *dsymtab;
@@ -611,6 +611,8 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn)
if(!(sym->type&N_EXT))
v = version;
s = lookup(name, v);
+ if(!(sym->type&N_EXT))
+ s->dupok = 1;
sym->sym = s;
if(sym->sectnum == 0) // undefined
continue;
@@ -635,10 +637,6 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn)
outer->sub = s;
s->outer = outer;
s->value = sym->value - sect->addr;
- if(i+1 < symtab->nsym)
- s->size = (sym+1)->value - sym->value;
- else
- s->size = sect->addr + sect->size - sym->value;
if(!(s->cgoexport & CgoExportDynamic))
s->dynimplib = nil; // satisfy dynimport
if(outer->type == STEXT) {
@@ -668,17 +666,26 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn)
sect = &c->seg.sect[i];
if((s = sect->sym) == S)
continue;
- if(s->sub)
+ if(s->sub) {
s->sub = listsort(s->sub, valuecmp, offsetof(Sym, sub));
+
+ // assign sizes, now that we know symbols in sorted order.
+ for(s1 = s->sub; s1 != S; s1 = s1->sub) {
+ if(s1->sub)
+ s1->size = s1->sub->value - s1->value;
+ else
+ s1->size = s->value + s->size - s1->value;
+ }
+ }
if(s->type == STEXT) {
if(etextp)
etextp->next = s;
else
textp = s;
etextp = s;
- for(s = s->sub; s != S; s = s->sub) {
- etextp->next = s;
- etextp = s;
+ for(s1 = s->sub; s1 != S; s1 = s1->sub) {
+ etextp->next = s1;
+ etextp = s1;
}
}
}
diff --git a/src/cmd/ld/ldpe.c b/src/cmd/ld/ldpe.c
index 033e522f2..6bcda2cb6 100644
--- a/src/cmd/ld/ldpe.c
+++ b/src/cmd/ld/ldpe.c
@@ -468,7 +468,9 @@ readsym(PeObj *obj, int i, PeSym **y)
break;
case IMAGE_SYM_CLASS_NULL:
case IMAGE_SYM_CLASS_STATIC:
+ case IMAGE_SYM_CLASS_LABEL:
s = lookup(name, version);
+ s->dupok = 1;
break;
default:
werrstr("%s: invalid symbol binding %d", sym->name, sym->sclass);
diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c
index 0a6bd3e8f..da522dc0c 100644
--- a/src/cmd/ld/lib.c
+++ b/src/cmd/ld/lib.c
@@ -33,9 +33,17 @@
#include "lib.h"
#include "../ld/elf.h"
#include "../../pkg/runtime/stack.h"
+#include "../../pkg/runtime/funcdata.h"
#include <ar.h>
+enum
+{
+ // Whether to assume that the external linker is "gold"
+ // (http://sourceware.org/ml/binutils/2008-03/msg00162.html).
+ AssumeGoldLinker = 0,
+};
+
int iconv(Fmt*);
char symname[] = SYMDEF;
@@ -45,6 +53,14 @@ int nlibdir = 0;
static int maxlibdir = 0;
static int cout = -1;
+// symbol version, incremented each time a file is loaded.
+// version==1 is reserved for savehist.
+enum
+{
+ HistVersion = 1,
+};
+int version = HistVersion;
+
// Set if we see an object compiled by the host compiler that is not
// from a package that is known to support internal linking mode.
static int externalobj = 0;
@@ -75,7 +91,7 @@ Lflag(char *arg)
void
libinit(void)
{
- char *race;
+ char *suffix, *suffixsep;
fmtinstall('i', iconv);
fmtinstall('Y', Yconv);
@@ -85,10 +101,16 @@ libinit(void)
print("goarch is not known: %s\n", goarch);
// add goroot to the end of the libdir list.
- race = "";
- if(flag_race)
- race = "_race";
- Lflag(smprint("%s/pkg/%s_%s%s", goroot, goos, goarch, race));
+ suffix = "";
+ suffixsep = "";
+ if(flag_installsuffix != nil) {
+ suffixsep = "_";
+ suffix = flag_installsuffix;
+ } else if(flag_race) {
+ suffixsep = "_";
+ suffix = "race";
+ }
+ Lflag(smprint("%s/pkg/%s_%s%s%s", goroot, goos, goarch, suffixsep, suffix));
// Unix doesn't like it when we write to a running (or, sometimes,
// recently run) binary, so remove the output file before writing it.
@@ -103,17 +125,14 @@ libinit(void)
}
if(INITENTRY == nil) {
- INITENTRY = mal(strlen(goarch)+strlen(goos)+10);
- sprint(INITENTRY, "_rt0_%s_%s", goarch, goos);
- }
- lookup(INITENTRY, 0)->type = SXREF;
- if(flag_shared) {
- if(LIBINITENTRY == nil) {
- LIBINITENTRY = mal(strlen(goarch)+strlen(goos)+20);
- sprint(LIBINITENTRY, "_rt0_%s_%s_lib", goarch, goos);
+ INITENTRY = mal(strlen(goarch)+strlen(goos)+20);
+ if(!flag_shared) {
+ sprint(INITENTRY, "_rt0_%s_%s", goarch, goos);
+ } else {
+ sprint(INITENTRY, "_rt0_%s_%s_lib", goarch, goos);
}
- lookup(LIBINITENTRY, 0)->type = SXREF;
}
+ lookup(INITENTRY, 0)->type = SXREF;
}
void
@@ -215,7 +234,7 @@ addlib(char *src, char *obj)
if(p != nil)
*p = '\0';
- if(debug['v'])
+ if(debug['v'] > 1)
Bprint(&bso, "%5.2f addlib: %s %s pulls in %s\n", cputime(), obj, src, pname);
addlibpath(src, obj, pname, name);
@@ -292,7 +311,13 @@ void
loadlib(void)
{
int i, w, x;
- Sym *s;
+ Sym *s, *gmsym;
+
+ if(flag_shared) {
+ s = lookup("runtime.islibrary", 0);
+ s->dupok = 1;
+ adduint8(s, 1);
+ }
loadinternal("runtime");
if(thechar == '5')
@@ -314,7 +339,7 @@ loadlib(void)
}
for(i=0; i<libraryp; i++) {
- if(debug['v'])
+ if(debug['v'] > 1)
Bprint(&bso, "%5.2f autolib: %s (from %s)\n", cputime(), library[i].file, library[i].objref);
iscgo |= strcmp(library[i].pkg, "runtime/cgo") == 0;
objfile(library[i].file, library[i].pkg);
@@ -343,6 +368,15 @@ loadlib(void)
}
}
+ gmsym = lookup("runtime.tlsgm", 0);
+ gmsym->type = STLSBSS;
+ gmsym->size = 2*PtrSize;
+ gmsym->hide = 1;
+ if(linkmode == LinkExternal && iself && HEADTYPE != Hopenbsd)
+ gmsym->reachable = 1;
+ else
+ gmsym->reachable = 0;
+
// Now that we know the link mode, trim the dynexp list.
x = CgoExportDynamic;
if(linkmode == LinkExternal)
@@ -417,7 +451,7 @@ objfile(char *file, char *pkg)
pkg = smprint("%i", pkg);
- if(debug['v'])
+ if(debug['v'] > 1)
Bprint(&bso, "%5.2f ldobj: %s (%s)\n", cputime(), file, pkg);
Bflush(&bso);
f = Bopen(file, 0);
@@ -554,6 +588,16 @@ ldhostobj(void (*ld)(Biobuf*, char*, int64, char*), Biobuf *f, char *pkg, int64
}
}
+ // DragonFly declares errno with __thread, which results in a symbol
+ // type of R_386_TLS_GD or R_X86_64_TLSGD. The Go linker does not
+ // currently know how to handle TLS relocations, hence we have to
+ // force external linking for any libraries that link in code that
+ // uses errno. This can be removed if the Go linker ever supports
+ // these relocation types.
+ if(HEADTYPE == Hdragonfly)
+ if(strcmp(pkg, "net") == 0 || strcmp(pkg, "os/user") == 0)
+ isinternal = 0;
+
if(!isinternal)
externalobj = 1;
@@ -653,7 +697,7 @@ hostlink(void)
p = strchr(p + 1, ' ');
}
- argv = malloc((10+nhostobj+nldflag+c)*sizeof argv[0]);
+ argv = malloc((13+nhostobj+nldflag+c)*sizeof argv[0]);
argc = 0;
if(extld == nil)
extld = "gcc";
@@ -665,11 +709,25 @@ hostlink(void)
case '6':
argv[argc++] = "-m64";
break;
+ case '5':
+ argv[argc++] = "-marm";
+ break;
}
- if(!debug['s'])
+ if(!debug['s'] && !debug_s) {
argv[argc++] = "-gdwarf-2";
+ } else {
+ argv[argc++] = "-s";
+ }
if(HEADTYPE == Hdarwin)
argv[argc++] = "-Wl,-no_pie,-pagezero_size,4000000";
+
+ if(iself && AssumeGoldLinker)
+ argv[argc++] = "-Wl,--rosegment";
+
+ if(flag_shared) {
+ argv[argc++] = "-Wl,-Bsymbolic";
+ argv[argc++] = "-shared";
+ }
argv[argc++] = "-o";
argv[argc++] = outfile;
@@ -759,10 +817,10 @@ ldobj(Biobuf *f, char *pkg, int64 len, char *pn, char *file, int whence)
pn = estrdup(pn);
- c1 = Bgetc(f);
- c2 = Bgetc(f);
- c3 = Bgetc(f);
- c4 = Bgetc(f);
+ c1 = BGETC(f);
+ c2 = BGETC(f);
+ c3 = BGETC(f);
+ c4 = BGETC(f);
Bungetc(f);
Bungetc(f);
Bungetc(f);
@@ -840,12 +898,12 @@ ldobj(Biobuf *f, char *pkg, int64 len, char *pn, char *file, int whence)
/* skip over exports and other info -- ends with \n!\n */
import0 = Boffset(f);
c1 = '\n'; // the last line ended in \n
- c2 = Bgetc(f);
- c3 = Bgetc(f);
+ c2 = BGETC(f);
+ c3 = BGETC(f);
while(c1 != '\n' || c2 != '!' || c3 != '\n') {
c1 = c2;
c2 = c3;
- c3 = Bgetc(f);
+ c3 = BGETC(f);
if(c3 == Beof)
goto eof;
}
@@ -899,7 +957,7 @@ _lookup(char *symb, int v, int creat)
{
Sym *s;
char *p;
- int32 h;
+ uint32 h;
int c;
h = v;
@@ -1179,16 +1237,6 @@ zerosig(char *sp)
s->sig = 0;
}
-int32
-Bget4(Biobuf *f)
-{
- uchar p[4];
-
- if(Bread(f, p, 4) != 4)
- return 0;
- return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
-}
-
void
mywhatsys(void)
{
@@ -1334,100 +1382,192 @@ addsection(Segment *seg, char *name, int rwx)
}
void
-pclntab(void)
+addvarint(Sym *s, uint32 val)
{
- vlong oldpc;
+ int32 n;
+ uint32 v;
+ uchar *p;
+
+ n = 0;
+ for(v = val; v >= 0x80; v >>= 7)
+ n++;
+ n++;
+
+ symgrow(s, s->np+n);
+
+ p = s->p + s->np - n;
+ for(v = val; v >= 0x80; v >>= 7)
+ *p++ = v | 0x80;
+ *p = v;
+}
+
+// funcpctab appends to dst a pc-value table mapping the code in func to the values
+// returned by valfunc parameterized by arg. The invocation of valfunc to update the
+// current value is, for each p,
+//
+// val = valfunc(func, val, p, 0, arg);
+// record val as value at p->pc;
+// val = valfunc(func, val, p, 1, arg);
+//
+// where func is the function, val is the current value, p is the instruction being
+// considered, and arg can be used to further parameterize valfunc.
+void
+funcpctab(Sym *dst, Sym *func, char *desc, int32 (*valfunc)(Sym*, int32, Prog*, int32, int32), int32 arg)
+{
+ int dbg, i, start;
+ int32 oldval, val, started;
+ uint32 delta;
+ vlong pc;
Prog *p;
- int32 oldlc, v, s;
- Sym *sym;
- uchar *bp;
+
+ // To debug a specific function, uncomment second line and change name.
+ dbg = 0;
+ //dbg = strcmp(func->name, "main.main") == 0;
+
+ debug['O'] += dbg;
+
+ start = dst->np;
+
+ if(debug['O'])
+ Bprint(&bso, "funcpctab %s -> %s [valfunc=%s]\n", func->name, dst->name, desc);
+
+ val = -1;
+ oldval = val;
+ pc = func->value;
- sym = lookup("pclntab", 0);
- sym->type = SPCLNTAB;
- sym->reachable = 1;
- if(debug['s'])
- return;
+ if(debug['O'])
+ Bprint(&bso, "%6llux %6d %P\n", pc, val, func->text);
+
+ started = 0;
+ for(p=func->text; p != P; p = p->link) {
+ // Update val. If it's not changing, keep going.
+ val = valfunc(func, val, p, 0, arg);
+ if(val == oldval && started) {
+ val = valfunc(func, val, p, 1, arg);
+ if(debug['O'])
+ Bprint(&bso, "%6llux %6s %P\n", (vlong)p->pc, "", p);
+ continue;
+ }
- oldpc = INITTEXT;
- oldlc = 0;
- for(cursym = textp; cursym != nil; cursym = cursym->next) {
- for(p = cursym->text; p != P; p = p->link) {
- if(p->line == oldlc || p->as == ATEXT || p->as == ANOP) {
- if(debug['O'])
- Bprint(&bso, "%6llux %P\n",
- (vlong)p->pc, p);
- continue;
- }
+ // If the pc of the next instruction is the same as the
+ // pc of this instruction, this instruction is not a real
+ // instruction. Keep going, so that we only emit a delta
+ // for a true instruction boundary in the program.
+ if(p->link && p->link->pc == p->pc) {
+ val = valfunc(func, val, p, 1, arg);
if(debug['O'])
- Bprint(&bso, "\t\t%6d", lcsize);
- v = (p->pc - oldpc) / MINLC;
- while(v) {
- s = 127;
- if(v < 127)
- s = v;
- symgrow(sym, lcsize+1);
- bp = sym->p + lcsize;
- *bp = s+128; /* 129-255 +pc */
- if(debug['O'])
- Bprint(&bso, " pc+%d*%d(%d)", s, MINLC, s+128);
- v -= s;
- lcsize++;
- }
- s = p->line - oldlc;
- oldlc = p->line;
- oldpc = p->pc + MINLC;
- if(s > 64 || s < -64) {
- symgrow(sym, lcsize+5);
- bp = sym->p + lcsize;
- *bp++ = 0; /* 0 vv +lc */
- *bp++ = s>>24;
- *bp++ = s>>16;
- *bp++ = s>>8;
- *bp = s;
- if(debug['O']) {
- if(s > 0)
- Bprint(&bso, " lc+%d(%d,%d)\n",
- s, 0, s);
- else
- Bprint(&bso, " lc%d(%d,%d)\n",
- s, 0, s);
- Bprint(&bso, "%6llux %P\n",
- (vlong)p->pc, p);
- }
- lcsize += 5;
- continue;
- }
- symgrow(sym, lcsize+1);
- bp = sym->p + lcsize;
- if(s > 0) {
- *bp = 0+s; /* 1-64 +lc */
- if(debug['O']) {
- Bprint(&bso, " lc+%d(%d)\n", s, 0+s);
- Bprint(&bso, "%6llux %P\n",
- (vlong)p->pc, p);
- }
- } else {
- *bp = 64-s; /* 65-128 -lc */
- if(debug['O']) {
- Bprint(&bso, " lc%d(%d)\n", s, 64-s);
- Bprint(&bso, "%6llux %P\n",
- (vlong)p->pc, p);
- }
- }
- lcsize++;
+ Bprint(&bso, "%6llux %6s %P\n", (vlong)p->pc, "", p);
+ continue;
+ }
+
+ // The table is a sequence of (value, pc) pairs, where each
+ // pair states that the given value is in effect from the current position
+ // up to the given pc, which becomes the new current position.
+ // To generate the table as we scan over the program instructions,
+ // we emit a "(value" when pc == func->value, and then
+ // each time we observe a change in value we emit ", pc) (value".
+ // When the scan is over, we emit the closing ", pc)".
+ //
+ // The table is delta-encoded. The value deltas are signed and
+ // transmitted in zig-zag form, where a complement bit is placed in bit 0,
+ // and the pc deltas are unsigned. Both kinds of deltas are sent
+ // as variable-length little-endian base-128 integers,
+ // where the 0x80 bit indicates that the integer continues.
+
+ if(debug['O'])
+ Bprint(&bso, "%6llux %6d %P\n", (vlong)p->pc, val, p);
+
+ if(started) {
+ addvarint(dst, (p->pc - pc) / MINLC);
+ pc = p->pc;
}
+ delta = val - oldval;
+ if(delta>>31)
+ delta = 1 | ~(delta<<1);
+ else
+ delta <<= 1;
+ addvarint(dst, delta);
+ oldval = val;
+ started = 1;
+ val = valfunc(func, val, p, 1, arg);
}
- if(lcsize & 1) {
- symgrow(sym, lcsize+1);
- sym->p[lcsize] = 129;
- lcsize++;
+
+ if(started) {
+ if(debug['O'])
+ Bprint(&bso, "%6llux done\n", (vlong)func->value+func->size);
+ addvarint(dst, (func->value+func->size - pc) / MINLC);
+ addvarint(dst, 0); // terminator
}
- sym->size = lcsize;
- lcsize = 0;
- if(debug['v'] || debug['O'])
- Bprint(&bso, "lcsize = %d\n", lcsize);
- Bflush(&bso);
+ if(debug['O']) {
+ Bprint(&bso, "wrote %d bytes\n", dst->np - start);
+ for(i=start; i<dst->np; i++)
+ Bprint(&bso, " %02ux", dst->p[i]);
+ Bprint(&bso, "\n");
+ }
+
+ debug['O'] -= dbg;
+}
+
+// pctofileline computes either the file number (arg == 0)
+// or the line number (arg == 1) to use at p.
+// Because p->lineno applies to p, phase == 0 (before p)
+// takes care of the update.
+static int32
+pctofileline(Sym *sym, int32 oldval, Prog *p, int32 phase, int32 arg)
+{
+ int32 f, l;
+
+ if(p->as == ATEXT || p->as == ANOP || p->as == AUSEFIELD || p->line == 0 || phase == 1)
+ return oldval;
+ getline(sym->hist, p->line, &f, &l);
+ if(f == 0) {
+ // print("getline failed for %s %P\n", cursym->name, p);
+ return oldval;
+ }
+ if(arg == 0)
+ return f;
+ return l;
+}
+
+// pctospadj computes the sp adjustment in effect.
+// It is oldval plus any adjustment made by p itself.
+// The adjustment by p takes effect only after p, so we
+// apply the change during phase == 1.
+static int32
+pctospadj(Sym *sym, int32 oldval, Prog *p, int32 phase, int32 arg)
+{
+ USED(arg);
+ USED(sym);
+
+ if(oldval == -1) // starting
+ oldval = 0;
+ if(phase == 0)
+ return oldval;
+ if(oldval + p->spadj < -10000 || oldval + p->spadj > 1100000000) {
+ diag("overflow in spadj: %d + %d = %d", oldval, p->spadj, oldval + p->spadj);
+ errorexit();
+ }
+ return oldval + p->spadj;
+}
+
+// pctopcdata computes the pcdata value in effect at p.
+// A PCDATA instruction sets the value in effect at future
+// non-PCDATA instructions.
+// Since PCDATA instructions have no width in the final code,
+// it does not matter which phase we use for the update.
+static int32
+pctopcdata(Sym *sym, int32 oldval, Prog *p, int32 phase, int32 arg)
+{
+ USED(sym);
+
+ if(phase == 0 || p->as != APCDATA || p->from.offset != arg)
+ return oldval;
+ if((int32)p->to.offset != p->to.offset) {
+ diag("overflow in PCDATA instruction: %P", p);
+ errorexit();
+ }
+ return p->to.offset;
}
#define LOG 5
@@ -1479,7 +1619,7 @@ le16(uchar *b)
uint32
le32(uchar *b)
{
- return b[0] | b[1]<<8 | b[2]<<16 | b[3]<<24;
+ return b[0] | b[1]<<8 | b[2]<<16 | (uint32)b[3]<<24;
}
uint64
@@ -1497,7 +1637,7 @@ be16(uchar *b)
uint32
be32(uchar *b)
{
- return b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3];
+ return (uint32)b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3];
}
uint64
@@ -1900,24 +2040,8 @@ genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*))
if(s->text == nil)
continue;
- /* filenames first */
- for(a=s->autom; a; a=a->link)
- if(a->type == D_FILE)
- put(nil, a->asym->name, 'z', a->aoffset, 0, 0, 0);
- else
- if(a->type == D_FILE1)
- put(nil, a->asym->name, 'Z', a->aoffset, 0, 0, 0);
-
put(s, s->name, 'T', s->value, s->size, s->version, s->gotype);
- /* frame, locals, args, auto and param after */
- put(nil, ".frame", 'm', (uint32)s->text->to.offset+PtrSize, 0, 0, 0);
- put(nil, ".locals", 'm', s->locals, 0, 0, 0);
- if(s->text->textflag & NOSPLIT)
- put(nil, ".args", 'm', ArgsSizeUnknown, 0, 0, 0);
- else
- put(nil, ".args", 'm', s->args, 0, 0, 0);
-
for(a=s->autom; a; a=a->link) {
// Emit a or p according to actual offset, even if label is wrong.
// This avoids negative offsets, which cannot be encoded.
@@ -1947,7 +2071,7 @@ genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*))
}
}
if(debug['v'] || debug['n'])
- Bprint(&bso, "symsize = %ud\n", symsize);
+ Bprint(&bso, "%5.2f symsize = %ud\n", cputime(), symsize);
Bflush(&bso);
}
@@ -1974,3 +2098,438 @@ erealloc(void *p, long n)
}
return p;
}
+
+// Saved history stacks encountered while reading archives.
+// Keeping them allows us to answer virtual lineno -> file:line
+// queries.
+//
+// The history stack is a complex data structure, described best at the
+// bottom of http://plan9.bell-labs.com/magic/man2html/6/a.out.
+// One of the key benefits of interpreting it here is that the runtime
+// does not have to. Perhaps some day the compilers could generate
+// a simpler linker input too.
+
+struct Hist
+{
+ int32 line;
+ int32 off;
+ Sym *file;
+};
+
+static Hist *histcopy;
+static Hist *hist;
+static int32 nhist;
+static int32 maxhist;
+static int32 histdepth;
+static int32 nhistfile;
+static Sym *filesyms;
+
+// savehist processes a single line, off history directive
+// found in the input object file.
+void
+savehist(int32 line, int32 off)
+{
+ char tmp[1024];
+ Sym *file;
+ Hist *h;
+
+ // NOTE(rsc): We used to do the copyhistfrog first and this
+ // condition was if(tmp[0] != '\0') to check for an empty string,
+ // implying that histfrogp == 0, implying that this is a history pop.
+ // However, on Windows in the misc/cgo test, the linker is
+ // presented with an ANAME corresponding to an empty string,
+ // that ANAME ends up being the only histfrog, and thus we have
+ // a situation where histfrogp > 0 (not a pop) but the path we find
+ // is the empty string. Really that shouldn't happen, but it doesn't
+ // seem to be bothering anyone yet, and it's easier to fix the condition
+ // to test histfrogp than to track down where that empty string is
+ // coming from. Probably it is coming from go tool pack's P command.
+ if(histfrogp > 0) {
+ tmp[0] = '\0';
+ copyhistfrog(tmp, sizeof tmp);
+ file = lookup(tmp, HistVersion);
+ if(file->type != SFILEPATH) {
+ file->value = ++nhistfile;
+ file->type = SFILEPATH;
+ file->next = filesyms;
+ filesyms = file;
+ }
+ } else
+ file = nil;
+
+ if(file != nil && line == 1 && off == 0) {
+ // start of new stack
+ if(histdepth != 0) {
+ diag("history stack phase error: unexpected start of new stack depth=%d file=%s", histdepth, tmp);
+ errorexit();
+ }
+ nhist = 0;
+ histcopy = nil;
+ }
+
+ if(nhist >= maxhist) {
+ if(maxhist == 0)
+ maxhist = 1;
+ maxhist *= 2;
+ hist = erealloc(hist, maxhist*sizeof hist[0]);
+ }
+ h = &hist[nhist++];
+ h->line = line;
+ h->off = off;
+ h->file = file;
+
+ if(file != nil) {
+ if(off == 0)
+ histdepth++;
+ } else {
+ if(off != 0) {
+ diag("history stack phase error: bad offset in pop");
+ errorexit();
+ }
+ histdepth--;
+ }
+}
+
+// gethist returns the history stack currently in effect.
+// The result is valid indefinitely.
+Hist*
+gethist(void)
+{
+ if(histcopy == nil) {
+ if(nhist == 0)
+ return nil;
+ histcopy = mal((nhist+1)*sizeof hist[0]);
+ memmove(histcopy, hist, nhist*sizeof hist[0]);
+ histcopy[nhist].line = -1;
+ }
+ return histcopy;
+}
+
+typedef struct Hstack Hstack;
+struct Hstack
+{
+ Hist *h;
+ int delta;
+};
+
+// getline sets *f to the file number and *l to the line number
+// of the virtual line number line according to the history stack h.
+void
+getline(Hist *h, int32 line, int32 *f, int32 *l)
+{
+ Hstack stk[100];
+ int nstk, start;
+ Hist *top, *h0;
+ static Hist *lasth;
+ static int32 laststart, lastend, lastdelta, lastfile;
+
+ h0 = h;
+ *f = 0;
+ *l = 0;
+ start = 0;
+ if(h == nil || line == 0) {
+ print("%s: getline: h=%p line=%d\n", cursym->name, h, line);
+ return;
+ }
+
+ // Cache span used during last lookup, so that sequential
+ // translation of line numbers in compiled code is efficient.
+ if(!debug['O'] && lasth == h && laststart <= line && line < lastend) {
+ *f = lastfile;
+ *l = line - lastdelta;
+ return;
+ }
+
+ if(debug['O'])
+ print("getline %d laststart=%d lastend=%d\n", line, laststart, lastend);
+
+ nstk = 0;
+ for(; h->line != -1; h++) {
+ if(debug['O'])
+ print("\t%s %d %d\n", h->file ? h->file->name : "?", h->line, h->off);
+
+ if(h->line > line) {
+ if(nstk == 0) {
+ diag("history stack phase error: empty stack at line %d", (int)line);
+ errorexit();
+ }
+ top = stk[nstk-1].h;
+ lasth = h;
+ lastfile = top->file->value;
+ laststart = start;
+ lastend = h->line;
+ lastdelta = stk[nstk-1].delta;
+ *f = lastfile;
+ *l = line - lastdelta;
+ if(debug['O'])
+ print("\tgot %d %d [%d %d %d]\n", *f, *l, laststart, lastend, lastdelta);
+ return;
+ }
+ if(h->file == nil) {
+ // pop included file
+ if(nstk == 0) {
+ diag("history stack phase error: stack underflow");
+ errorexit();
+ }
+ nstk--;
+ if(nstk > 0)
+ stk[nstk-1].delta += h->line - stk[nstk].h->line;
+ start = h->line;
+ } else if(h->off == 0) {
+ // push included file
+ if(nstk >= nelem(stk)) {
+ diag("history stack phase error: stack overflow");
+ errorexit();
+ }
+ start = h->line;
+ stk[nstk].h = h;
+ stk[nstk].delta = h->line - 1;
+ nstk++;
+ } else {
+ // #line directive
+ if(nstk == 0) {
+ diag("history stack phase error: stack underflow");
+ errorexit();
+ }
+ stk[nstk-1].h = h;
+ stk[nstk-1].delta = h->line - h->off;
+ start = h->line;
+ }
+ if(debug['O'])
+ print("\t\tnstk=%d delta=%d\n", nstk, stk[nstk].delta);
+ }
+
+ diag("history stack phase error: cannot find line for %d", line);
+ nstk = 0;
+ for(h = h0; h->line != -1; h++) {
+ print("\t%d %d %s\n", h->line, h->off, h->file ? h->file->name : "");
+ if(h->file == nil)
+ nstk--;
+ else if(h->off == 0)
+ nstk++;
+ }
+}
+
+// defgostring returns a symbol for the Go string containing text.
+Sym*
+defgostring(char *text)
+{
+ char *p;
+ Sym *s;
+ int32 n;
+
+ n = strlen(text);
+ p = smprint("go.string.\"%Z\"", text);
+ s = lookup(p, 0);
+ if(s->size == 0) {
+ s->type = SGOSTRING;
+ s->reachable = 1;
+ s->size = 2*PtrSize+n;
+ symgrow(s, 2*PtrSize+n);
+ setaddrplus(s, 0, s, 2*PtrSize);
+ setuintxx(s, PtrSize, n, PtrSize);
+ memmove(s->p+2*PtrSize, text, n);
+ }
+ s->reachable = 1;
+ return s;
+}
+
+// addpctab appends to f a pc-value table, storing its offset at off.
+// The pc-value table is for func and reports the value of valfunc
+// parameterized by arg.
+static int32
+addpctab(Sym *f, int32 off, Sym *func, char *desc, int32 (*valfunc)(Sym*, int32, Prog*, int32, int32), int32 arg)
+{
+ int32 start;
+
+ start = f->np;
+ funcpctab(f, func, desc, valfunc, arg);
+ if(start == f->np) {
+ // no table
+ return setuint32(f, off, 0);
+ }
+ if((int32)start > (int32)f->np) {
+ diag("overflow adding pc-table: symbol too large");
+ errorexit();
+ }
+ return setuint32(f, off, start);
+}
+
+static int32
+ftabaddstring(Sym *ftab, char *s)
+{
+ int32 n, start;
+
+ n = strlen(s)+1;
+ start = ftab->np;
+ symgrow(ftab, start+n+1);
+ strcpy((char*)ftab->p + start, s);
+ return start;
+}
+
+// pclntab initializes the pclntab symbol with
+// runtime function and file name information.
+void
+pclntab(void)
+{
+ Prog *p;
+ int32 i, n, nfunc, start, funcstart;
+ uint32 *havepc, *havefunc;
+ Sym *ftab, *s;
+ int32 npcdata, nfuncdata, off, end;
+ int64 funcdata_bytes;
+
+ funcdata_bytes = 0;
+ ftab = lookup("pclntab", 0);
+ ftab->type = SPCLNTAB;
+ ftab->reachable = 1;
+
+ // See golang.org/s/go12symtab for the format. Briefly:
+ // 8-byte header
+ // nfunc [PtrSize bytes]
+ // function table, alternating PC and offset to func struct [each entry PtrSize bytes]
+ // end PC [PtrSize bytes]
+ // offset to file table [4 bytes]
+ nfunc = 0;
+ for(cursym = textp; cursym != nil; cursym = cursym->next)
+ nfunc++;
+ symgrow(ftab, 8+PtrSize+nfunc*2*PtrSize+PtrSize+4);
+ setuint32(ftab, 0, 0xfffffffb);
+ setuint8(ftab, 6, MINLC);
+ setuint8(ftab, 7, PtrSize);
+ setuintxx(ftab, 8, nfunc, PtrSize);
+
+ nfunc = 0;
+ for(cursym = textp; cursym != nil; cursym = cursym->next, nfunc++) {
+ funcstart = ftab->np;
+ funcstart += -ftab->np & (PtrSize-1);
+
+ setaddr(ftab, 8+PtrSize+nfunc*2*PtrSize, cursym);
+ setuintxx(ftab, 8+PtrSize+nfunc*2*PtrSize+PtrSize, funcstart, PtrSize);
+
+ npcdata = 0;
+ nfuncdata = 0;
+ for(p = cursym->text; p != P; p = p->link) {
+ if(p->as == APCDATA && p->from.offset >= npcdata)
+ npcdata = p->from.offset+1;
+ if(p->as == AFUNCDATA && p->from.offset >= nfuncdata)
+ nfuncdata = p->from.offset+1;
+ }
+
+ // fixed size of struct, checked below
+ off = funcstart;
+ end = funcstart + PtrSize + 3*4 + 5*4 + npcdata*4 + nfuncdata*PtrSize;
+ if(nfuncdata > 0 && (end&(PtrSize-1)))
+ end += 4;
+ symgrow(ftab, end);
+
+ // entry uintptr
+ off = setaddr(ftab, off, cursym);
+
+ // name int32
+ off = setuint32(ftab, off, ftabaddstring(ftab, cursym->name));
+
+ // args int32
+ // TODO: Move into funcinfo.
+ if(cursym->text == nil)
+ off = setuint32(ftab, off, ArgsSizeUnknown);
+ else
+ off = setuint32(ftab, off, cursym->args);
+
+ // frame int32
+ // TODO: Remove entirely. The pcsp table is more precise.
+ // This is only used by a fallback case during stack walking
+ // when a called function doesn't have argument information.
+ // We need to make sure everything has argument information
+ // and then remove this.
+ if(cursym->text == nil)
+ off = setuint32(ftab, off, 0);
+ else
+ off = setuint32(ftab, off, (uint32)cursym->text->to.offset+PtrSize);
+
+ // pcsp table (offset int32)
+ off = addpctab(ftab, off, cursym, "pctospadj", pctospadj, 0);
+
+ // pcfile table (offset int32)
+ off = addpctab(ftab, off, cursym, "pctofileline file", pctofileline, 0);
+
+ // pcln table (offset int32)
+ off = addpctab(ftab, off, cursym, "pctofileline line", pctofileline, 1);
+
+ // npcdata int32
+ off = setuint32(ftab, off, npcdata);
+
+ // nfuncdata int32
+ off = setuint32(ftab, off, nfuncdata);
+
+ // tabulate which pc and func data we have.
+ n = ((npcdata+31)/32 + (nfuncdata+31)/32)*4;
+ havepc = mal(n);
+ havefunc = havepc + (npcdata+31)/32;
+ for(p = cursym->text; p != P; p = p->link) {
+ if(p->as == AFUNCDATA) {
+ if((havefunc[p->from.offset/32]>>(p->from.offset%32))&1)
+ diag("multiple definitions for FUNCDATA $%d", p->from.offset);
+ havefunc[p->from.offset/32] |= 1<<(p->from.offset%32);
+ }
+ if(p->as == APCDATA)
+ havepc[p->from.offset/32] |= 1<<(p->from.offset%32);
+ }
+
+ // pcdata.
+ for(i=0; i<npcdata; i++) {
+ if(!(havepc[i/32]>>(i%32))&1) {
+ off = setuint32(ftab, off, 0);
+ continue;
+ }
+ off = addpctab(ftab, off, cursym, "pctopcdata", pctopcdata, i);
+ }
+
+ unmal(havepc, n);
+
+ // funcdata, must be pointer-aligned and we're only int32-aligned.
+ // Unlike pcdata, can gather in a single pass.
+ // Missing funcdata will be 0 (nil pointer).
+ if(nfuncdata > 0) {
+ if(off&(PtrSize-1))
+ off += 4;
+ for(p = cursym->text; p != P; p = p->link) {
+ if(p->as == AFUNCDATA) {
+ i = p->from.offset;
+ if(p->to.type == D_CONST)
+ setuintxx(ftab, off+PtrSize*i, p->to.offset, PtrSize);
+ else {
+ // TODO: Dedup.
+ funcdata_bytes += p->to.sym->size;
+ setaddrplus(ftab, off+PtrSize*i, p->to.sym, p->to.offset);
+ }
+ }
+ }
+ off += nfuncdata*PtrSize;
+ }
+
+ if(off != end) {
+ diag("bad math in functab: funcstart=%d off=%d but end=%d (npcdata=%d nfuncdata=%d)", funcstart, off, end, npcdata, nfuncdata);
+ errorexit();
+ }
+
+ // Final entry of table is just end pc.
+ if(cursym->next == nil)
+ setaddrplus(ftab, 8+PtrSize+(nfunc+1)*2*PtrSize, cursym, cursym->size);
+ }
+
+ // Start file table.
+ start = ftab->np;
+ start += -ftab->np & (PtrSize-1);
+ setuint32(ftab, 8+PtrSize+nfunc*2*PtrSize+PtrSize, start);
+
+ symgrow(ftab, start+(nhistfile+1)*4);
+ setuint32(ftab, start, nhistfile);
+ for(s = filesyms; s != S; s = s->next)
+ setuint32(ftab, start + s->value*4, ftabaddstring(ftab, s->name));
+
+ ftab->size = ftab->np;
+
+ if(debug['v'])
+ Bprint(&bso, "%5.2f pclntab=%lld bytes, funcdata total %lld bytes\n", cputime(), (vlong)ftab->size, (vlong)funcdata_bytes);
+}
diff --git a/src/cmd/ld/lib.h b/src/cmd/ld/lib.h
index e552deb02..be95bb46e 100644
--- a/src/cmd/ld/lib.h
+++ b/src/cmd/ld/lib.h
@@ -33,33 +33,43 @@ enum
Sxxx,
/* order here is order in output file */
+ /* readonly, executable */
STEXT,
+ SELFRXSECT,
+
+ /* readonly, non-executable */
STYPE,
SSTRING,
SGOSTRING,
+ SGOFUNC,
SRODATA,
+ SFUNCTAB,
STYPELINK,
- SSYMTAB,
+ SSYMTAB, // TODO: move to unmapped section
SPCLNTAB,
SELFROSECT,
+
+ /* writable, non-executable */
SMACHOPLT,
SELFSECT,
SMACHO, /* Mach-O __nl_symbol_ptr */
SMACHOGOT,
SNOPTRDATA,
- SDATARELRO,
+ SINITARR,
SDATA,
SWINDOWS,
SBSS,
SNOPTRBSS,
STLSBSS,
+ /* not mapped */
SXREF,
SMACHOSYMSTR,
SMACHOSYMTAB,
SMACHOINDIRECTPLT,
SMACHOINDIRECTGOT,
SFILE,
+ SFILEPATH,
SCONST,
SDYNIMPORT,
SHOSTOBJ,
@@ -71,13 +81,6 @@ enum
NHASH = 100003,
};
-enum
-{
- // This value is known to the garbage collector and should be kept in
- // sync with runtime/pkg/runtime.h
- ArgsSizeUnknown = 0x80000000
-};
-
typedef struct Library Library;
struct Library
{
@@ -122,9 +125,14 @@ struct Section
uvlong rellen;
};
+typedef struct Hist Hist;
+
+#pragma incomplete struct Hist
+
extern char symname[];
extern char **libdir;
extern int nlibdir;
+extern int version;
EXTERN char* INITENTRY;
EXTERN char* thestring;
@@ -152,6 +160,7 @@ EXTERN char** ldflag;
EXTERN int havedynamic;
EXTERN int iscgo;
EXTERN int elfglobalsymndx;
+EXTERN char* flag_installsuffix;
EXTERN int flag_race;
EXTERN int flag_shared;
EXTERN char* tracksym;
@@ -159,6 +168,7 @@ EXTERN char* interpreter;
EXTERN char* tmpdir;
EXTERN char* extld;
EXTERN char* extldflags;
+EXTERN int debug_s; // backup old value of debug['s']
enum
{
@@ -176,6 +186,7 @@ enum
};
EXTERN Segment segtext;
+EXTERN Segment segrodata;
EXTERN Segment segdata;
EXTERN Segment segdwarf;
@@ -185,6 +196,9 @@ void addlibpath(char *srcref, char *objref, char *file, char *pkg);
Section* addsection(Segment*, char*, int);
void copyhistfrog(char *buf, int nbuf);
void addhist(int32 line, int type);
+void savehist(int32 line, int32 off);
+Hist* gethist(void);
+void getline(Hist*, int32 line, int32 *f, int32 *l);
void asmlc(void);
void histtoauto(void);
void collapsefrog(Sym *s);
@@ -199,7 +213,6 @@ double ieeedtod(Ieee *e);
void undefsym(Sym *s);
void zerosig(char *sp);
void readundefs(char *f, int t);
-int32 Bget4(Biobuf *f);
void loadlib(void);
void errorexit(void);
void mangle(char*);
@@ -211,7 +224,6 @@ void Lflag(char *arg);
void usage(void);
void adddynrel(Sym*, Reloc*);
void adddynrela(Sym*, Sym*, Reloc*);
-Sym* lookuprel(void);
void ldobj1(Biobuf *f, char*, int64 len, char *pn);
void ldobj(Biobuf*, char*, int64, char*, char*, int);
void ldelf(Biobuf*, char*, int64, char*);
@@ -242,10 +254,11 @@ vlong addpcrelplus(Sym*, Sym*, vlong);
vlong addsize(Sym*, Sym*);
vlong setaddrplus(Sym*, vlong, Sym*, vlong);
vlong setaddr(Sym*, vlong, Sym*);
-void setuint8(Sym*, vlong, uint8);
-void setuint16(Sym*, vlong, uint16);
-void setuint32(Sym*, vlong, uint32);
-void setuint64(Sym*, vlong, uint64);
+vlong setuint8(Sym*, vlong, uint8);
+vlong setuint16(Sym*, vlong, uint16);
+vlong setuint32(Sym*, vlong, uint32);
+vlong setuint64(Sym*, vlong, uint64);
+vlong setuintxx(Sym*, vlong, uint64, vlong);
void asmsym(void);
void asmelfsym(void);
void asmplan9sym(void);
@@ -275,6 +288,7 @@ void hostobjs(void);
void hostlink(void);
char* estrdup(char*);
void* erealloc(void*, long);
+Sym* defgostring(char*);
int pathchar(void);
void* mal(uint32);
@@ -330,6 +344,7 @@ enum {
Hfreebsd, // FreeBSD ELF
Hwindows, // MS Windows PE
Hopenbsd, // OpenBSD ELF
+ Hdragonfly, // DragonFly ELF
};
typedef struct Header Header;
diff --git a/src/cmd/ld/pe.c b/src/cmd/ld/pe.c
index 090d083f5..7b9a596fc 100644
--- a/src/cmd/ld/pe.c
+++ b/src/cmd/ld/pe.c
@@ -151,6 +151,7 @@ peinit(void)
// some mingw libs depend on this symbol, for example, FindPESectionByName
xdefine("__image_base__", SDATA, PEBASE);
+ xdefine("_image_base__", SDATA, PEBASE);
}
static void
diff --git a/src/cmd/ld/symtab.c b/src/cmd/ld/symtab.c
index 7c8ba642f..c9b4657f7 100644
--- a/src/cmd/ld/symtab.c
+++ b/src/cmd/ld/symtab.c
@@ -181,22 +181,13 @@ asmelfsym(void)
genasmsym(putelfsym);
if(linkmode == LinkExternal && HEADTYPE != Hopenbsd) {
- s = lookup("runtime.m", 0);
+ s = lookup("runtime.tlsgm", 0);
if(s->sect == nil) {
cursym = nil;
diag("missing section for %s", s->name);
errorexit();
}
- putelfsyment(putelfstr(s->name), 0, PtrSize, (STB_LOCAL<<4)|STT_TLS, s->sect->elfsect->shnum, 0);
- s->elfsym = numelfsym++;
-
- s = lookup("runtime.g", 0);
- if(s->sect == nil) {
- cursym = nil;
- diag("missing section for %s", s->name);
- errorexit();
- }
- putelfsyment(putelfstr(s->name), PtrSize, PtrSize, (STB_LOCAL<<4)|STT_TLS, s->sect->elfsect->shnum, 0);
+ putelfsyment(putelfstr(s->name), 0, 2*PtrSize, (STB_LOCAL<<4)|STT_TLS, s->sect->elfsect->shnum, 0);
s->elfsym = numelfsym++;
}
@@ -371,7 +362,7 @@ putsymb(Sym *s, char *name, int t, vlong v, vlong size, int ver, Sym *typ)
Reloc *rel;
USED(size);
-
+
// type byte
if('A' <= t && t <= 'Z')
c = t - 'A' + (ver ? 26 : 0);
@@ -466,7 +457,8 @@ putsymb(Sym *s, char *name, int t, vlong v, vlong size, int ver, Sym *typ)
void
symtab(void)
{
- Sym *s, *symtype, *symtypelink, *symgostring;
+ Sym *s, *symtype, *symtypelink, *symgostring, *symgofunc;
+
dosymtype();
// Define these so that they'll get put into the symbol table.
@@ -477,12 +469,6 @@ symtab(void)
xdefine("etypelink", SRODATA, 0);
xdefine("rodata", SRODATA, 0);
xdefine("erodata", SRODATA, 0);
- if(flag_shared) {
- xdefine("datarelro", SDATARELRO, 0);
- xdefine("edatarelro", SDATARELRO, 0);
- }
- xdefine("egcdata", STYPE, 0);
- xdefine("egcbss", STYPE, 0);
xdefine("noptrdata", SNOPTRDATA, 0);
xdefine("enoptrdata", SNOPTRDATA, 0);
xdefine("data", SDATA, 0);
@@ -495,6 +481,19 @@ symtab(void)
xdefine("epclntab", SRODATA, 0);
xdefine("esymtab", SRODATA, 0);
+ // garbage collection symbols
+ s = lookup("gcdata", 0);
+ s->type = SRODATA;
+ s->size = 0;
+ s->reachable = 1;
+ xdefine("egcdata", SRODATA, 0);
+
+ s = lookup("gcbss", 0);
+ s->type = SRODATA;
+ s->size = 0;
+ s->reachable = 1;
+ xdefine("egcbss", SRODATA, 0);
+
// pseudo-symbols to mark locations of type, string, and go string data.
s = lookup("type.*", 0);
s->type = STYPE;
@@ -508,6 +507,12 @@ symtab(void)
s->reachable = 1;
symgostring = s;
+ s = lookup("go.func.*", 0);
+ s->type = SGOFUNC;
+ s->size = 0;
+ s->reachable = 1;
+ symgofunc = s;
+
symtypelink = lookup("typelink", 0);
symt = lookup("symtab", 0);
@@ -537,6 +542,11 @@ symtab(void)
s->hide = 1;
s->outer = symgostring;
}
+ if(strncmp(s->name, "go.func.", 8) == 0) {
+ s->type = SGOFUNC;
+ s->hide = 1;
+ s->outer = symgofunc;
+ }
}
if(debug['s'])
diff --git a/src/cmd/ld/textflag.h b/src/cmd/ld/textflag.h
new file mode 100644
index 000000000..1d62db736
--- /dev/null
+++ b/src/cmd/ld/textflag.h
@@ -0,0 +1,21 @@
+// Copyright 2013 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 defines flags attached to various functions
+// and data objects. The compilers, assemblers, and linker must
+// all agree on these values.
+
+// Don't profile the marked routine. This flag is deprecated.
+#define NOPROF 1
+// It is ok for the linker to get multiple of these symbols. It will
+// pick one of the duplicates to use.
+#define DUPOK 2
+// Don't insert stack check preamble.
+#define NOSPLIT 4
+// Put this data in a read-only section.
+#define RODATA 8
+// This data contains no pointers.
+#define NOPTR 16
+// This is a wrapper function and should not count as disabling 'recover'.
+#define WRAPPER 32
diff --git a/src/cmd/nm/doc.go b/src/cmd/nm/doc.go
index 480c1c3dd..8e88e2e63 100644
--- a/src/cmd/nm/doc.go
+++ b/src/cmd/nm/doc.go
@@ -17,7 +17,7 @@ This implementation adds the flag -S, which prints each symbol's size
in decimal after its address.
Usage:
- go tool nm [-aghnsTu] file
+ go tool nm [-aghnsSTu] file
*/
package main
diff --git a/src/cmd/nm/nm.c b/src/cmd/nm/nm.c
index aa056b882..820942426 100644
--- a/src/cmd/nm/nm.c
+++ b/src/cmd/nm/nm.c
@@ -72,7 +72,7 @@ void zenter(Sym*);
void
usage(void)
{
- fprint(2, "usage: nm [-aghnsTu] file ...\n");
+ fprint(2, "usage: nm [-aghnsSTu] file ...\n");
exits("usage");
}
@@ -299,6 +299,37 @@ psym(Sym *s, void* p)
symptr[nsym++] = s;
}
+const char *skipnames[] = {
+ "bss",
+ "data",
+ "ebss",
+ "edata",
+ "egcbss",
+ "egcdata",
+ "enoptrbss",
+ "enoptrdata",
+ "epclntab",
+ "erodata",
+ "esymtab",
+ "etext",
+ "etypelink",
+ "noptrbss",
+ "noptrdata",
+ "rodata",
+ "text",
+};
+
+int
+skipsize(char *name)
+{
+ int i;
+
+ for(i=0; i<nelem(skipnames); i++)
+ if(strcmp(skipnames[i], name) == 0)
+ return 1;
+ return 0;
+}
+
void
printsyms(Sym **symptr, long nsym)
{
@@ -332,12 +363,12 @@ printsyms(Sym **symptr, long nsym)
Bprint(&bout, "%*llux ", wid, s->value);
else
Bprint(&bout, "%*s ", wid, "");
- if(Sflag) {
+ if(Sflag && !skipsize(cp)) {
vlong siz;
siz = 0;
for(j=i+1; j<nsym; j++) {
- if(symptr[j]->type != 'a' && symptr[j]->type != 'p') {
+ if(!skipsize(symptr[j]->name) && symptr[j]->type != 'a' && symptr[j]->type != 'p') {
siz = symptr[j]->value - s->value;
break;
}
diff --git a/src/cmd/pack/ar.c b/src/cmd/pack/ar.c
index 7053f841f..5b300dbb9 100644
--- a/src/cmd/pack/ar.c
+++ b/src/cmd/pack/ar.c
@@ -700,7 +700,7 @@ scanobj(Biobuf *b, Arfile *ap, long size)
// the ! comes immediately. Catch that so we can avoid
// the call to scanpkg below, since scanpkg assumes that the
// Go metadata is present.
- if(Bgetc(b) == '!')
+ if(BGETC(b) == '!')
goobject = 0;
Bseek(b, offset1, 0);
@@ -807,13 +807,13 @@ scanpkg(Biobuf *b, long size)
* scan until $$
*/
for (n=0; n<size; ) {
- c = Bgetc(b);
+ c = BGETC(b);
if(c == Beof)
break;
n++;
if(c != '$')
continue;
- c = Bgetc(b);
+ c = BGETC(b);
if(c == Beof)
break;
n++;
@@ -828,7 +828,7 @@ scanpkg(Biobuf *b, long size)
foundstart:
/* found $$; skip rest of line */
- while((c = Bgetc(b)) != '\n')
+ while((c = BGETC(b)) != '\n')
if(c == Beof)
goto bad;
@@ -937,21 +937,12 @@ objsym(Sym *s, void *p)
int
hashstr(char *name)
{
- int h;
+ uint32 h;
char *cp;
h = 0;
for(cp = name; *cp; h += *cp++)
h *= 1119;
-
- // the code used to say
- // if(h < 0)
- // h = ~h;
- // but on gcc 4.3 with -O2 on some systems,
- // the if(h < 0) gets compiled away as not possible.
- // use a mask instead, leaving plenty of bits but
- // definitely not the sign bit.
-
return h & 0xfffffff;
}
@@ -1263,7 +1254,7 @@ rl(int fd)
wrsym(&b, len, aend->sym);
if(symdefsize&0x01)
- Bputc(&b, 0);
+ BPUTC(&b, 0);
if (gflag) {
len = pkgdefsize;
@@ -1283,7 +1274,7 @@ rl(int fd)
if (Bwrite(&b, pkgdefdata, pkgdefsize) != pkgdefsize)
wrerr();
if(len&0x01)
- Bputc(&b, 0);
+ BPUTC(&b, 0);
}
Bterm(&b);
}
@@ -1297,12 +1288,9 @@ wrsym(Biobuf *bp, long offset, Arsymref *as)
int off;
while(as) {
- Bputc(bp, as->type);
+ BPUTC(bp, as->type);
off = as->offset+offset;
- Bputc(bp, off);
- Bputc(bp, off>>8);
- Bputc(bp, off>>16);
- Bputc(bp, off>>24);
+ BPUTLE4(bp, off);
if (Bwrite(bp, as->name, as->len+1) != as->len+1)
wrerr();
as = as->next;
@@ -1454,7 +1442,7 @@ select(int *ap, long mode)
n = *ap++;
while(--n>=0 && (mode&*ap++)==0)
ap++;
- Bputc(&bout, *ap);
+ BPUTC(&bout, *ap);
}
/*
@@ -1506,7 +1494,7 @@ arread(Biobuf *b, Armember *bp) /* read an image into a member buffer */
rderr();
}
if(bp->size&1)
- Bgetc(b);
+ BGETC(b);
}
/*
@@ -1618,6 +1606,25 @@ int (*reader[256])(Biobuf*, Prog*) = {
[Obj386] = _read8,
};
+#define isdelim(c) ((c) == '/' || (c) == '\\')
+
+/*
+ * check if p is start of windows full path, like C:\ or c:/.
+ * return 1 if so. also set drive parameter to its
+ * upper-case drive letter.
+ */
+int
+iswinpathstart(char *p, char *drive)
+{
+ if('A' <= p[0] || p[0] <= 'Z')
+ *drive = p[0];
+ else if('a' <= p[0] || p[0] <= 'z')
+ *drive = p[0] - ('a' - 'A');
+ else
+ return 0;
+ return p[1] == ':' && isdelim(p[2]);
+}
+
/*
* copy b into bp->member but rewrite object
* during copy to drop prefix from all file names.
@@ -1630,7 +1637,7 @@ arread_cutprefix(Biobuf *b, Armember *bp)
vlong offset, o, end;
int n, t;
int (*rd)(Biobuf*, Prog*);
- char *w, *inprefix;
+ char *w, *inprefix, d1, d2;
Prog p;
offset = Boffset(b);
@@ -1666,12 +1673,15 @@ arread_cutprefix(Biobuf *b, Armember *bp)
if(inprefix == nil && prefix[0] == '/' && p.id[1] == '/' && p.id[2] == '\0') {
// leading /
inprefix = prefix+1;
+ } else if(inprefix == nil && iswinpathstart(prefix, &d1) && iswinpathstart(p.id + 1, &d2) && d1 == d2 && p.id[4] == '\0') {
+ // leading c:\ ...
+ inprefix = prefix+3;
} 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')) {
+ if(strncmp(p.id+1, inprefix, n) == 0 && (isdelim(inprefix[n]) || inprefix[n] == '\0')) {
inprefix += n;
- if(inprefix[0] == '/')
+ if(isdelim(inprefix[0]))
inprefix++;
}
}
@@ -1712,6 +1722,6 @@ arread_cutprefix(Biobuf *b, Armember *bp)
strncpy(bp->hdr.fmag, ARFMAG, 2);
Bseek(b, end, 0);
if(Boffset(b)&1)
- Bgetc(b);
+ BGETC(b);
return 1;
}
diff --git a/src/cmd/vet/Makefile b/src/cmd/vet/Makefile
deleted file mode 100644
index 67c7e1997..000000000
--- a/src/cmd/vet/Makefile
+++ /dev/null
@@ -1,14 +0,0 @@
-# Copyright 2010 The Go Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file.
-
-# Assumes go/types is installed
-test testshort:
- go build -tags 'vet_test gotypes'
- ../../../test/errchk ./vet -printfuncs='Warn:1,Warnf:1' test_*.go test_*.s
-
-test_notypes:
- go build -tags 'vet_test'
- # Only those tests that do not depend on types.
- # Excluded: test_print.go
- ../../../test/errchk ./vet -printfuncs='Warn:1,Warnf:1' test_asm.go test_assign.go test_atomic.go test_buildtag.go test_buildtag_bad.go test_deadcode.go test_method.go test_rangeloop.go test_structtag.go test_taglit.go test_*.s
diff --git a/src/cmd/vet/asmdecl.go b/src/cmd/vet/asmdecl.go
deleted file mode 100644
index c23514c28..000000000
--- a/src/cmd/vet/asmdecl.go
+++ /dev/null
@@ -1,533 +0,0 @@
-// Copyright 2013 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.
-
-// Identify mismatches between assembly files and Go func declarations.
-
-package main
-
-import (
- "bytes"
- "fmt"
- "go/ast"
- "go/token"
- "regexp"
- "strconv"
- "strings"
-)
-
-// 'kind' is a kind of assembly variable.
-// The kinds 1, 2, 4, 8 stand for values of that size.
-type asmKind int
-
-// These special kinds are not valid sizes.
-const (
- asmString asmKind = 100 + iota
- asmSlice
- asmInterface
- asmEmptyInterface
-)
-
-// An asmArch describes assembly parameters for an architecture
-type asmArch struct {
- name string
- ptrSize int
- intSize int
- bigEndian bool
-}
-
-// An asmFunc describes the expected variables for a function on a given architecture.
-type asmFunc struct {
- arch *asmArch
- size int // size of all arguments
- vars map[string]*asmVar
- varByOffset map[int]*asmVar
-}
-
-// An asmVar describes a single assembly variable.
-type asmVar struct {
- name string
- kind asmKind
- typ string
- off int
- size int
- inner []*asmVar
-}
-
-var (
- asmArch386 = asmArch{"386", 4, 4, false}
- asmArchArm = asmArch{"arm", 4, 4, false}
- asmArchAmd64 = asmArch{"amd64", 8, 8, false}
-
- arches = []*asmArch{
- &asmArch386,
- &asmArchArm,
- &asmArchAmd64,
- }
-)
-
-var (
- re = regexp.MustCompile
- asmPlusBuild = re(`//\s+\+build\s+([^\n]+)`)
- asmTEXT = re(`\bTEXT\b.*·([^\(]+)\(SB\)(?:\s*,\s*([0-9]+))?(?:\s*,\s*\$([0-9]+)(?:-([0-9]+))?)?`)
- asmDATA = re(`\b(DATA|GLOBL)\b`)
- asmNamedFP = re(`([a-zA-Z0-9_\xFF-\x{10FFFF}]+)(?:\+([0-9]+))\(FP\)`)
- asmUnnamedFP = re(`[^+\-0-9]](([0-9]+)\(FP\))`)
- asmOpcode = re(`^\s*(?:[A-Z0-9a-z_]+:)?\s*([A-Z]+)\s*([^,]*)(?:,\s*(.*))?`)
-)
-
-func asmCheck(pkg *Package) {
- if !vet("asmdecl") {
- return
- }
-
- // No work if no assembly files.
- if !pkg.hasFileWithSuffix(".s") {
- return
- }
-
- // Gather declarations. knownFunc[name][arch] is func description.
- knownFunc := make(map[string]map[string]*asmFunc)
-
- for _, f := range pkg.files {
- if f.file != nil {
- for _, decl := range f.file.Decls {
- if decl, ok := decl.(*ast.FuncDecl); ok && decl.Body == nil {
- knownFunc[decl.Name.Name] = f.asmParseDecl(decl)
- }
- }
- }
- }
-
- var fn *asmFunc
- for _, f := range pkg.files {
- if !strings.HasSuffix(f.name, ".s") {
- continue
- }
- Println("Checking file", f.name)
-
- // Determine architecture from file name if possible.
- var arch string
- for _, a := range arches {
- if strings.HasSuffix(f.name, "_"+a.name+".s") {
- arch = a.name
- break
- }
- }
-
- lines := strings.SplitAfter(string(f.content), "\n")
- for lineno, line := range lines {
- lineno++
-
- warnf := func(format string, args ...interface{}) {
- f.Warnf(token.NoPos, "%s:%d: [%s] %s", f.name, lineno, arch, fmt.Sprintf(format, args...))
- }
-
- if arch == "" {
- // Determine architecture from +build line if possible.
- if m := asmPlusBuild.FindStringSubmatch(line); m != nil {
- Fields:
- for _, fld := range strings.Fields(m[1]) {
- for _, a := range arches {
- if a.name == fld {
- arch = a.name
- break Fields
- }
- }
- }
- }
- }
-
- if m := asmTEXT.FindStringSubmatch(line); m != nil {
- if arch == "" {
- f.Warnf(token.NoPos, "%s: cannot determine architecture for assembly file", f.name)
- return
- }
- fn = knownFunc[m[1]][arch]
- if fn != nil {
- size, _ := strconv.Atoi(m[4])
- if size != fn.size && (m[2] != "7" || size != 0) {
- warnf("wrong argument size %d; expected $...-%d", size, fn.size)
- }
- }
- continue
- } else if strings.Contains(line, "TEXT") && strings.Contains(line, "SB") {
- // function, but not visible from Go (didn't match asmTEXT), so stop checking
- fn = nil
- continue
- }
-
- if asmDATA.FindStringSubmatch(line) != nil {
- fn = nil
- }
- if fn == nil {
- continue
- }
-
- for _, m := range asmUnnamedFP.FindAllStringSubmatch(line, -1) {
- warnf("use of unnamed argument %s", m[1])
- }
-
- for _, m := range asmNamedFP.FindAllStringSubmatch(line, -1) {
- name := m[1]
- off := 0
- if m[2] != "" {
- off, _ = strconv.Atoi(m[2])
- }
- v := fn.vars[name]
- if v == nil {
- // Allow argframe+0(FP).
- if name == "argframe" && off == 0 {
- continue
- }
- v = fn.varByOffset[off]
- if v != nil {
- warnf("unknown variable %s; offset %d is %s+%d(FP)", name, off, v.name, v.off)
- } else {
- warnf("unknown variable %s", name)
- }
- continue
- }
- asmCheckVar(warnf, fn, line, m[0], off, v)
- }
- }
- }
-}
-
-// asmParseDecl parses a function decl for expected assembly variables.
-func (f *File) asmParseDecl(decl *ast.FuncDecl) map[string]*asmFunc {
- var (
- arch *asmArch
- fn *asmFunc
- offset int
- failed bool
- )
-
- addVar := func(outer string, v asmVar) {
- if vo := fn.vars[outer]; vo != nil {
- vo.inner = append(vo.inner, &v)
- }
- fn.vars[v.name] = &v
- for i := 0; i < v.size; i++ {
- fn.varByOffset[v.off+i] = &v
- }
- }
-
- addParams := func(list []*ast.Field) {
- for i, fld := range list {
- // Determine alignment, size, and kind of type in declaration.
- var align, size int
- var kind asmKind
- names := fld.Names
- typ := f.gofmt(fld.Type)
- switch t := fld.Type.(type) {
- default:
- switch typ {
- default:
- f.Warnf(fld.Type.Pos(), "unknown assembly argument type %s", typ)
- failed = true
- return
- case "int8", "uint8", "byte", "bool":
- size = 1
- case "int16", "uint16":
- size = 2
- case "int32", "uint32", "float32":
- size = 4
- case "int64", "uint64", "float64":
- align = arch.ptrSize
- size = 8
- case "int", "uint":
- size = arch.intSize
- case "uintptr", "iword", "Word", "Errno", "unsafe.Pointer":
- size = arch.ptrSize
- case "string":
- size = arch.ptrSize * 2
- align = arch.ptrSize
- kind = asmString
- }
- case *ast.ChanType, *ast.FuncType, *ast.MapType, *ast.StarExpr:
- size = arch.ptrSize
- case *ast.InterfaceType:
- align = arch.ptrSize
- size = 2 * arch.ptrSize
- if len(t.Methods.List) > 0 {
- kind = asmInterface
- } else {
- kind = asmEmptyInterface
- }
- case *ast.ArrayType:
- if t.Len == nil {
- size = arch.ptrSize + 2*arch.intSize
- align = arch.ptrSize
- kind = asmSlice
- break
- }
- f.Warnf(fld.Type.Pos(), "unsupported assembly argument type %s", typ)
- failed = true
- case *ast.StructType:
- f.Warnf(fld.Type.Pos(), "unsupported assembly argument type %s", typ)
- failed = true
- }
- if align == 0 {
- align = size
- }
- if kind == 0 {
- kind = asmKind(size)
- }
- offset += -offset & (align - 1)
-
- // Create variable for each name being declared with this type.
- if len(names) == 0 {
- name := "unnamed"
- if decl.Type.Results != nil && len(decl.Type.Results.List) > 0 && &list[0] == &decl.Type.Results.List[0] && i == 0 {
- // Assume assembly will refer to single unnamed result as r.
- name = "ret"
- }
- names = []*ast.Ident{{Name: name}}
- }
- for _, id := range names {
- name := id.Name
- addVar("", asmVar{
- name: name,
- kind: kind,
- typ: typ,
- off: offset,
- size: size,
- })
- switch kind {
- case 8:
- if arch.ptrSize == 4 {
- w1, w2 := "lo", "hi"
- if arch.bigEndian {
- w1, w2 = w2, w1
- }
- addVar(name, asmVar{
- name: name + "_" + w1,
- kind: 4,
- typ: "half " + typ,
- off: offset,
- size: 4,
- })
- addVar(name, asmVar{
- name: name + "_" + w2,
- kind: 4,
- typ: "half " + typ,
- off: offset + 4,
- size: 4,
- })
- }
-
- case asmEmptyInterface:
- addVar(name, asmVar{
- name: name + "_type",
- kind: asmKind(arch.ptrSize),
- typ: "interface type",
- off: offset,
- size: arch.ptrSize,
- })
- addVar(name, asmVar{
- name: name + "_data",
- kind: asmKind(arch.ptrSize),
- typ: "interface data",
- off: offset + arch.ptrSize,
- size: arch.ptrSize,
- })
-
- case asmInterface:
- addVar(name, asmVar{
- name: name + "_itable",
- kind: asmKind(arch.ptrSize),
- typ: "interface itable",
- off: offset,
- size: arch.ptrSize,
- })
- addVar(name, asmVar{
- name: name + "_data",
- kind: asmKind(arch.ptrSize),
- typ: "interface data",
- off: offset + arch.ptrSize,
- size: arch.ptrSize,
- })
-
- case asmSlice:
- addVar(name, asmVar{
- name: name + "_base",
- kind: asmKind(arch.ptrSize),
- typ: "slice base",
- off: offset,
- size: arch.ptrSize,
- })
- addVar(name, asmVar{
- name: name + "_len",
- kind: asmKind(arch.intSize),
- typ: "slice len",
- off: offset + arch.ptrSize,
- size: arch.intSize,
- })
- addVar(name, asmVar{
- name: name + "_cap",
- kind: asmKind(arch.intSize),
- typ: "slice cap",
- off: offset + arch.ptrSize + arch.intSize,
- size: arch.intSize,
- })
-
- case asmString:
- addVar(name, asmVar{
- name: name + "_base",
- kind: asmKind(arch.ptrSize),
- typ: "string base",
- off: offset,
- size: arch.ptrSize,
- })
- addVar(name, asmVar{
- name: name + "_len",
- kind: asmKind(arch.intSize),
- typ: "string len",
- off: offset + arch.ptrSize,
- size: arch.intSize,
- })
- }
- offset += size
- }
- }
- }
-
- m := make(map[string]*asmFunc)
- for _, arch = range arches {
- fn = &asmFunc{
- arch: arch,
- vars: make(map[string]*asmVar),
- varByOffset: make(map[int]*asmVar),
- }
- offset = 0
- addParams(decl.Type.Params.List)
- if decl.Type.Results != nil && len(decl.Type.Results.List) > 0 {
- offset += -offset & (arch.ptrSize - 1)
- addParams(decl.Type.Results.List)
- }
- fn.size = offset
- m[arch.name] = fn
- }
-
- if failed {
- return nil
- }
- return m
-}
-
-// asmCheckVar checks a single variable reference.
-func asmCheckVar(warnf func(string, ...interface{}), fn *asmFunc, line, expr string, off int, v *asmVar) {
- m := asmOpcode.FindStringSubmatch(line)
- if m == nil {
- warnf("cannot find assembly opcode")
- }
-
- // Determine operand sizes from instruction.
- // Typically the suffix suffices, but there are exceptions.
- var src, dst, kind asmKind
- op := m[1]
- switch fn.arch.name + "." + op {
- case "386.FMOVLP":
- src, dst = 8, 4
- case "arm.MOVD":
- src = 8
- case "arm.MOVW":
- src = 4
- case "arm.MOVH", "arm.MOVHU":
- src = 2
- case "arm.MOVB", "arm.MOVBU":
- src = 1
- default:
- if fn.arch.name == "386" || fn.arch.name == "amd64" {
- if strings.HasPrefix(op, "F") && (strings.HasSuffix(op, "D") || strings.HasSuffix(op, "DP")) {
- // FMOVDP, FXCHD, etc
- src = 8
- break
- }
- if strings.HasPrefix(op, "F") && (strings.HasSuffix(op, "F") || strings.HasSuffix(op, "FP")) {
- // FMOVFP, FXCHF, etc
- src = 4
- break
- }
- if strings.HasSuffix(op, "SD") {
- // MOVSD, SQRTSD, etc
- src = 8
- break
- }
- if strings.HasSuffix(op, "SS") {
- // MOVSS, SQRTSS, etc
- src = 4
- break
- }
- if strings.HasPrefix(op, "SET") {
- // SETEQ, etc
- src = 1
- break
- }
- switch op[len(op)-1] {
- case 'B':
- src = 1
- case 'W':
- src = 2
- case 'L':
- src = 4
- case 'D', 'Q':
- src = 8
- }
- }
- }
- if dst == 0 {
- dst = src
- }
-
- // Determine whether the match we're holding
- // is the first or second argument.
- if strings.Index(line, expr) > strings.Index(line, ",") {
- kind = dst
- } else {
- kind = src
- }
-
- vk := v.kind
- vt := v.typ
- switch vk {
- case asmInterface, asmEmptyInterface, asmString, asmSlice:
- // allow reference to first word (pointer)
- vk = v.inner[0].kind
- vt = v.inner[0].typ
- }
-
- if off != v.off {
- var inner bytes.Buffer
- for i, vi := range v.inner {
- if len(v.inner) > 1 {
- fmt.Fprintf(&inner, ",")
- }
- fmt.Fprintf(&inner, " ")
- if i == len(v.inner)-1 {
- fmt.Fprintf(&inner, "or ")
- }
- fmt.Fprintf(&inner, "%s+%d(FP)", vi.name, vi.off)
- }
- warnf("invalid offset %s; expected %s+%d(FP)%s", expr, v.name, v.off, inner.String())
- return
- }
- if kind != 0 && kind != vk {
- var inner bytes.Buffer
- if len(v.inner) > 0 {
- fmt.Fprintf(&inner, " containing")
- for i, vi := range v.inner {
- if i > 0 && len(v.inner) > 2 {
- fmt.Fprintf(&inner, ",")
- }
- fmt.Fprintf(&inner, " ")
- if i > 0 && i == len(v.inner)-1 {
- fmt.Fprintf(&inner, "and ")
- }
- fmt.Fprintf(&inner, "%s+%d(FP)", vi.name, vi.off)
- }
- }
- warnf("invalid %s of %s; %s is %d-byte value%s", op, expr, vt, vk, inner.String())
- }
-}
diff --git a/src/cmd/vet/assign.go b/src/cmd/vet/assign.go
deleted file mode 100644
index a11f0f875..000000000
--- a/src/cmd/vet/assign.go
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-/*
-This file contains the code to check for useless assignments.
-*/
-
-package main
-
-import (
- "go/ast"
- "go/token"
- "reflect"
-)
-
-// TODO: should also check for assignments to struct fields inside methods
-// that are on T instead of *T.
-
-// checkAssignStmt checks for assignments of the form "<expr> = <expr>".
-// These are almost always useless, and even when they aren't they are usually a mistake.
-func (f *File) checkAssignStmt(stmt *ast.AssignStmt) {
- if !vet("assign") {
- return
- }
- if stmt.Tok != token.ASSIGN {
- return // ignore :=
- }
- if len(stmt.Lhs) != len(stmt.Rhs) {
- // If LHS and RHS have different cardinality, they can't be the same.
- return
- }
- for i, lhs := range stmt.Lhs {
- rhs := stmt.Rhs[i]
- if reflect.TypeOf(lhs) != reflect.TypeOf(rhs) {
- continue // short-circuit the heavy-weight gofmt check
- }
- le := f.gofmt(lhs)
- re := f.gofmt(rhs)
- if le == re {
- f.Warnf(stmt.Pos(), "self-assignment of %s to %s", re, le)
- }
- }
-}
diff --git a/src/cmd/vet/atomic.go b/src/cmd/vet/atomic.go
deleted file mode 100644
index 4ab256f64..000000000
--- a/src/cmd/vet/atomic.go
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2013 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"
-)
-
-// checkAtomicAssignment walks the assignment statement checking for common
-// mistaken usage of atomic package, such as: x = atomic.AddUint64(&x, 1)
-func (f *File) checkAtomicAssignment(n *ast.AssignStmt) {
- if !vet("atomic") {
- return
- }
-
- if len(n.Lhs) != len(n.Rhs) {
- return
- }
-
- for i, right := range n.Rhs {
- call, ok := right.(*ast.CallExpr)
- if !ok {
- continue
- }
- sel, ok := call.Fun.(*ast.SelectorExpr)
- if !ok {
- continue
- }
- pkg, ok := sel.X.(*ast.Ident)
- if !ok || pkg.Name != "atomic" {
- continue
- }
-
- switch sel.Sel.Name {
- case "AddInt32", "AddInt64", "AddUint32", "AddUint64", "AddUintptr":
- f.checkAtomicAddAssignment(n.Lhs[i], call)
- }
- }
-}
-
-// checkAtomicAddAssignment walks the atomic.Add* method calls checking for assigning the return value
-// to the same variable being used in the operation
-func (f *File) checkAtomicAddAssignment(left ast.Expr, call *ast.CallExpr) {
- arg := call.Args[0]
- broken := false
-
- if uarg, ok := arg.(*ast.UnaryExpr); ok && uarg.Op == token.AND {
- broken = f.gofmt(left) == f.gofmt(uarg.X)
- } else if star, ok := left.(*ast.StarExpr); ok {
- broken = f.gofmt(star.X) == f.gofmt(arg)
- }
-
- if broken {
- f.Warn(left.Pos(), "direct assignment to atomic value")
- }
-}
diff --git a/src/cmd/vet/buildtag.go b/src/cmd/vet/buildtag.go
deleted file mode 100644
index 0ab13cb8a..000000000
--- a/src/cmd/vet/buildtag.go
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright 2013 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"
- "fmt"
- "os"
- "strings"
- "unicode"
-)
-
-var (
- nl = []byte("\n")
- slashSlash = []byte("//")
- plusBuild = []byte("+build")
-)
-
-// checkBuildTag checks that build tags are in the correct location and well-formed.
-func checkBuildTag(name string, data []byte) {
- if !vet("buildtags") {
- return
- }
- lines := bytes.SplitAfter(data, nl)
-
- // Determine cutpoint where +build comments are no longer valid.
- // They are valid in leading // comments in the file followed by
- // a blank line.
- var cutoff int
- for i, line := range lines {
- line = bytes.TrimSpace(line)
- if len(line) == 0 {
- cutoff = i
- continue
- }
- if bytes.HasPrefix(line, slashSlash) {
- continue
- }
- break
- }
-
- for i, line := range lines {
- line = bytes.TrimSpace(line)
- if !bytes.HasPrefix(line, slashSlash) {
- continue
- }
- text := bytes.TrimSpace(line[2:])
- if bytes.HasPrefix(text, plusBuild) {
- fields := bytes.Fields(text)
- if !bytes.Equal(fields[0], plusBuild) {
- // Comment is something like +buildasdf not +build.
- fmt.Fprintf(os.Stderr, "%s:%d: possible malformed +build comment\n", name, i+1)
- continue
- }
- if i >= cutoff {
- fmt.Fprintf(os.Stderr, "%s:%d: +build comment appears too late in file\n", name, i+1)
- setExit(1)
- continue
- }
- // Check arguments.
- Args:
- for _, arg := range fields[1:] {
- for _, elem := range strings.Split(string(arg), ",") {
- if strings.HasPrefix(elem, "!!") {
- fmt.Fprintf(os.Stderr, "%s:%d: invalid double negative in build constraint: %s\n", name, i+1, arg)
- setExit(1)
- break Args
- }
- if strings.HasPrefix(elem, "!") {
- elem = elem[1:]
- }
- for _, c := range elem {
- if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' {
- fmt.Fprintf(os.Stderr, "%s:%d: invalid non-alphanumeric build constraint: %s\n", name, i+1, arg)
- setExit(1)
- break Args
- }
- }
- }
- }
- continue
- }
- // Comment with +build but not at beginning.
- if bytes.Contains(line, plusBuild) && i < cutoff {
- fmt.Fprintf(os.Stderr, "%s:%d: possible malformed +build comment\n", name, i+1)
- continue
- }
- }
-}
diff --git a/src/cmd/vet/deadcode.go b/src/cmd/vet/deadcode.go
deleted file mode 100644
index f90fc14a4..000000000
--- a/src/cmd/vet/deadcode.go
+++ /dev/null
@@ -1,280 +0,0 @@
-// Copyright 2013 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 for syntactically unreachable code.
-
-package main
-
-import (
- "go/ast"
- "go/token"
-)
-
-type deadState struct {
- f *File
- hasBreak map[ast.Stmt]bool
- hasGoto map[string]bool
- labels map[string]ast.Stmt
- breakTarget ast.Stmt
-
- reachable bool
-}
-
-// checkUnreachable checks a function body for dead code.
-func (f *File) checkUnreachable(body *ast.BlockStmt) {
- if !vet("unreachable") || body == nil {
- return
- }
-
- d := &deadState{
- f: f,
- hasBreak: make(map[ast.Stmt]bool),
- hasGoto: make(map[string]bool),
- labels: make(map[string]ast.Stmt),
- }
-
- d.findLabels(body)
-
- d.reachable = true
- d.findDead(body)
-}
-
-// findLabels gathers information about the labels defined and used by stmt
-// and about which statements break, whether a label is involved or not.
-func (d *deadState) findLabels(stmt ast.Stmt) {
- switch x := stmt.(type) {
- default:
- d.f.Warnf(x.Pos(), "internal error in findLabels: unexpected statement %T", x)
-
- case *ast.AssignStmt,
- *ast.BadStmt,
- *ast.DeclStmt,
- *ast.DeferStmt,
- *ast.EmptyStmt,
- *ast.ExprStmt,
- *ast.GoStmt,
- *ast.IncDecStmt,
- *ast.ReturnStmt,
- *ast.SendStmt:
- // no statements inside
-
- case *ast.BlockStmt:
- for _, stmt := range x.List {
- d.findLabels(stmt)
- }
-
- case *ast.BranchStmt:
- switch x.Tok {
- case token.GOTO:
- d.hasGoto[x.Label.Name] = true
-
- case token.BREAK:
- stmt := d.breakTarget
- if x.Label != nil {
- stmt = d.labels[x.Label.Name]
- }
- if stmt != nil {
- d.hasBreak[stmt] = true
- }
- }
-
- case *ast.IfStmt:
- d.findLabels(x.Body)
- if x.Else != nil {
- d.findLabels(x.Else)
- }
-
- case *ast.LabeledStmt:
- d.labels[x.Label.Name] = x.Stmt
- d.findLabels(x.Stmt)
-
- // These cases are all the same, but the x.Body only works
- // when the specific type of x is known, so the cases cannot
- // be merged.
- case *ast.ForStmt:
- outer := d.breakTarget
- d.breakTarget = x
- d.findLabels(x.Body)
- d.breakTarget = outer
-
- case *ast.RangeStmt:
- outer := d.breakTarget
- d.breakTarget = x
- d.findLabels(x.Body)
- d.breakTarget = outer
-
- case *ast.SelectStmt:
- outer := d.breakTarget
- d.breakTarget = x
- d.findLabels(x.Body)
- d.breakTarget = outer
-
- case *ast.SwitchStmt:
- outer := d.breakTarget
- d.breakTarget = x
- d.findLabels(x.Body)
- d.breakTarget = outer
-
- case *ast.TypeSwitchStmt:
- outer := d.breakTarget
- d.breakTarget = x
- d.findLabels(x.Body)
- d.breakTarget = outer
-
- case *ast.CommClause:
- for _, stmt := range x.Body {
- d.findLabels(stmt)
- }
-
- case *ast.CaseClause:
- for _, stmt := range x.Body {
- d.findLabels(stmt)
- }
- }
-}
-
-// findDead walks the statement looking for dead code.
-// If d.reachable is false on entry, stmt itself is dead.
-// When findDead returns, d.reachable tells whether the
-// statement following stmt is reachable.
-func (d *deadState) findDead(stmt ast.Stmt) {
- // Is this a labeled goto target?
- // If so, assume it is reachable due to the goto.
- // This is slightly conservative, in that we don't
- // check that the goto is reachable, so
- // L: goto L
- // will not provoke a warning.
- // But it's good enough.
- if x, isLabel := stmt.(*ast.LabeledStmt); isLabel && d.hasGoto[x.Label.Name] {
- d.reachable = true
- }
-
- if !d.reachable {
- switch stmt.(type) {
- case *ast.EmptyStmt:
- // do not warn about unreachable empty statements
- default:
- d.f.Warnf(stmt.Pos(), "unreachable code")
- d.reachable = true // silence error about next statement
- }
- }
-
- switch x := stmt.(type) {
- default:
- d.f.Warnf(x.Pos(), "internal error in findDead: unexpected statement %T", x)
-
- case *ast.AssignStmt,
- *ast.BadStmt,
- *ast.DeclStmt,
- *ast.DeferStmt,
- *ast.EmptyStmt,
- *ast.GoStmt,
- *ast.IncDecStmt,
- *ast.SendStmt:
- // no control flow
-
- case *ast.BlockStmt:
- for _, stmt := range x.List {
- d.findDead(stmt)
- }
-
- case *ast.BranchStmt:
- switch x.Tok {
- case token.BREAK, token.GOTO, token.FALLTHROUGH:
- d.reachable = false
- case token.CONTINUE:
- // NOTE: We accept "continue" statements as terminating.
- // They are not necessary in the spec definition of terminating,
- // because a continue statement cannot be the final statement
- // before a return. But for the more general problem of syntactically
- // identifying dead code, continue redirects control flow just
- // like the other terminating statements.
- d.reachable = false
- }
-
- case *ast.ExprStmt:
- // Call to panic?
- call, ok := x.X.(*ast.CallExpr)
- if ok {
- name, ok := call.Fun.(*ast.Ident)
- if ok && name.Name == "panic" && name.Obj == nil {
- d.reachable = false
- }
- }
-
- case *ast.ForStmt:
- d.findDead(x.Body)
- d.reachable = x.Cond != nil || d.hasBreak[x]
-
- case *ast.IfStmt:
- d.findDead(x.Body)
- if x.Else != nil {
- r := d.reachable
- d.reachable = true
- d.findDead(x.Else)
- d.reachable = d.reachable || r
- } else {
- // might not have executed if statement
- d.reachable = true
- }
-
- case *ast.LabeledStmt:
- d.findDead(x.Stmt)
-
- case *ast.RangeStmt:
- d.findDead(x.Body)
- d.reachable = true
-
- case *ast.ReturnStmt:
- d.reachable = false
-
- case *ast.SelectStmt:
- // NOTE: Unlike switch and type switch below, we don't care
- // whether a select has a default, because a select without a
- // default blocks until one of the cases can run. That's different
- // from a switch without a default, which behaves like it has
- // a default with an empty body.
- anyReachable := false
- for _, comm := range x.Body.List {
- d.reachable = true
- for _, stmt := range comm.(*ast.CommClause).Body {
- d.findDead(stmt)
- }
- anyReachable = anyReachable || d.reachable
- }
- d.reachable = anyReachable || d.hasBreak[x]
-
- case *ast.SwitchStmt:
- anyReachable := false
- hasDefault := false
- for _, cas := range x.Body.List {
- cc := cas.(*ast.CaseClause)
- if cc.List == nil {
- hasDefault = true
- }
- d.reachable = true
- for _, stmt := range cc.Body {
- d.findDead(stmt)
- }
- anyReachable = anyReachable || d.reachable
- }
- d.reachable = anyReachable || d.hasBreak[x] || !hasDefault
-
- case *ast.TypeSwitchStmt:
- anyReachable := false
- hasDefault := false
- for _, cas := range x.Body.List {
- cc := cas.(*ast.CaseClause)
- if cc.List == nil {
- hasDefault = true
- }
- d.reachable = true
- for _, stmt := range cc.Body {
- d.findDead(stmt)
- }
- anyReachable = anyReachable || d.reachable
- }
- d.reachable = anyReachable || d.hasBreak[x] || !hasDefault
- }
-}
diff --git a/src/cmd/vet/doc.go b/src/cmd/vet/doc.go
deleted file mode 100644
index eb1e436f0..000000000
--- a/src/cmd/vet/doc.go
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-/*
-
-Vet examines Go source code and reports suspicious constructs, such as Printf
-calls whose arguments do not align with the format string. Vet uses heuristics
-that do not guarantee all reports are genuine problems, but it can find errors
-not caught by the compilers.
-
-Its exit code is 2 for erroneous invocation of the tool, 1 if a
-problem was reported, and 0 otherwise. Note that the tool does not
-check every possible problem and depends on unreliable heuristics
-so it should be used as guidance only, not as a firm indicator of
-program correctness.
-
-By default all checks are performed, but if explicit flags are provided, only
-those identified by the flags are performed.
-
-Available checks:
-
-1. Printf family, flag -printf
-
-Suspicious calls to functions in the Printf family, including any functions
-with these names:
- Print Printf Println
- Fprint Fprintf Fprintln
- Sprint Sprintf Sprintln
- Error Errorf
- Fatal Fatalf
- Panic Panicf Panicln
-If the function name ends with an 'f', the function is assumed to take
-a format descriptor string in the manner of fmt.Printf. If not, vet
-complains about arguments that look like format descriptor strings.
-
-It also checks for errors such as using a Writer as the first argument of
-Printf.
-
-2. Methods, flag -methods
-
-Non-standard signatures for methods with familiar names, including:
- Format GobEncode GobDecode MarshalJSON MarshalXML
- Peek ReadByte ReadFrom ReadRune Scan Seek
- UnmarshalJSON UnreadByte UnreadRune WriteByte
- WriteTo
-
-3. Struct tags, flag -structtags
-
-Struct tags that do not follow the format understood by reflect.StructTag.Get.
-
-4. Untagged composite literals, flag -composites
-
-Composite struct literals that do not use the type-tagged syntax.
-
-
-Usage:
-
- go tool vet [flag] [file.go ...]
- go tool vet [flag] [directory ...] # Scan all .go files under directory, recursively
-
-The other flags are:
- -v
- Verbose mode
- -printfuncs
- A comma-separated list of print-like functions to supplement
- the standard list. Each entry is in the form Name:N where N
- is the zero-based argument position of the first argument
- involved in the print: either the format or the first print
- argument for non-formatted prints. For example,
- if you have Warn and Warnf functions that take an
- io.Writer as their first argument, like Fprintf,
- -printfuncs=Warn:1,Warnf:1
-
-*/
-package main
diff --git a/src/cmd/vet/main.go b/src/cmd/vet/main.go
deleted file mode 100644
index 2fefa0b47..000000000
--- a/src/cmd/vet/main.go
+++ /dev/null
@@ -1,422 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Vet is a simple checker for static errors in Go source code.
-// See doc.go for more information.
-package main
-
-import (
- "bytes"
- "flag"
- "fmt"
- "go/ast"
- "go/build"
- "go/parser"
- "go/printer"
- "go/token"
- "io/ioutil"
- "os"
- "path/filepath"
- "strconv"
- "strings"
-)
-
-var verbose = flag.Bool("v", false, "verbose")
-var exitCode = 0
-
-// Flags to control which checks to perform. "all" is set to true here, and disabled later if
-// a flag is set explicitly.
-var report = map[string]*bool{
- "all": flag.Bool("all", true, "check everything; disabled if any explicit check is requested"),
- "asmdecl": flag.Bool("asmdecl", false, "check assembly against Go declarations"),
- "assign": flag.Bool("assign", false, "check for useless assignments"),
- "atomic": flag.Bool("atomic", false, "check for common mistaken usages of the sync/atomic package"),
- "buildtags": flag.Bool("buildtags", false, "check that +build tags are valid"),
- "composites": flag.Bool("composites", false, "check that composite literals used type-tagged elements"),
- "methods": flag.Bool("methods", false, "check that canonically named methods are canonically defined"),
- "printf": flag.Bool("printf", false, "check printf-like invocations"),
- "rangeloops": flag.Bool("rangeloops", false, "check that range loop variables are used correctly"),
- "structtags": flag.Bool("structtags", false, "check that struct field tags have canonical format"),
- "unreachable": flag.Bool("unreachable", false, "check for unreachable code"),
-}
-
-// vet tells whether to report errors for the named check, a flag name.
-func vet(name string) bool {
- return *report["all"] || *report[name]
-}
-
-// setExit sets the value for os.Exit when it is called, later. It
-// remembers the highest value.
-func setExit(err int) {
- if err > exitCode {
- exitCode = err
- }
-}
-
-// Usage is a replacement usage function for the flags package.
-func Usage() {
- fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
- fmt.Fprintf(os.Stderr, "\tvet [flags] directory...\n")
- fmt.Fprintf(os.Stderr, "\tvet [flags] files... # Must be a single package\n")
- flag.PrintDefaults()
- os.Exit(2)
-}
-
-// File is a wrapper for the state of a file used in the parser.
-// The parse tree walkers are all methods of this type.
-type File struct {
- pkg *Package
- fset *token.FileSet
- name string
- content []byte
- file *ast.File
- b bytes.Buffer // for use by methods
-}
-
-func main() {
- flag.Usage = Usage
- flag.Parse()
-
- // If a check is named explicitly, turn off the 'all' flag.
- for name, ptr := range report {
- if name != "all" && *ptr {
- *report["all"] = false
- break
- }
- }
-
- if *printfuncs != "" {
- for _, name := range strings.Split(*printfuncs, ",") {
- if len(name) == 0 {
- flag.Usage()
- }
- skip := 0
- if colon := strings.LastIndex(name, ":"); colon > 0 {
- var err error
- skip, err = strconv.Atoi(name[colon+1:])
- if err != nil {
- errorf(`illegal format for "Func:N" argument %q; %s`, name, err)
- }
- name = name[:colon]
- }
- name = strings.ToLower(name)
- if name[len(name)-1] == 'f' {
- printfList[name] = skip
- } else {
- printList[name] = skip
- }
- }
- }
-
- if flag.NArg() == 0 {
- Usage()
- }
- dirs := false
- files := false
- for _, name := range flag.Args() {
- // Is it a directory?
- fi, err := os.Stat(name)
- if err != nil {
- warnf("error walking tree: %s", err)
- continue
- }
- if fi.IsDir() {
- dirs = true
- } else {
- files = true
- }
- }
- if dirs && files {
- Usage()
- }
- if dirs {
- for _, name := range flag.Args() {
- walkDir(name)
- }
- return
- }
- doPackage(flag.Args())
- os.Exit(exitCode)
-}
-
-// prefixDirectory places the directory name on the beginning of each name in the list.
-func prefixDirectory(directory string, names []string) {
- if directory != "." {
- for i, name := range names {
- names[i] = filepath.Join(directory, name)
- }
- }
-}
-
-// doPackageDir analyzes the single package found in the directory, if there is one,
-// plus a test package, if there is one.
-func doPackageDir(directory string) {
- pkg, err := build.Default.ImportDir(directory, 0)
- if err != nil {
- // If it's just that there are no go source files, that's fine.
- if _, nogo := err.(*build.NoGoError); nogo {
- return
- }
- // Non-fatal: we are doing a recursive walk and there may be other directories.
- warnf("cannot process directory %s: %s", directory, err)
- return
- }
- var names []string
- names = append(names, pkg.GoFiles...)
- names = append(names, pkg.CgoFiles...)
- names = append(names, pkg.TestGoFiles...) // These are also in the "foo" package.
- names = append(names, pkg.SFiles...)
- prefixDirectory(directory, names)
- doPackage(names)
- // Is there also a "foo_test" package? If so, do that one as well.
- if len(pkg.XTestGoFiles) > 0 {
- names = pkg.XTestGoFiles
- prefixDirectory(directory, names)
- doPackage(names)
- }
-}
-
-type Package struct {
- types map[ast.Expr]Type
- values map[ast.Expr]interface{}
- files []*File
-}
-
-// doPackage analyzes the single package constructed from the named files.
-func doPackage(names []string) {
- var files []*File
- var astFiles []*ast.File
- fs := token.NewFileSet()
- for _, name := range names {
- f, err := os.Open(name)
- if err != nil {
- // Warn but continue to next package.
- warnf("%s: %s", name, err)
- return
- }
- defer f.Close()
- data, err := ioutil.ReadAll(f)
- if err != nil {
- warnf("%s: %s", name, err)
- return
- }
- checkBuildTag(name, data)
- var parsedFile *ast.File
- if strings.HasSuffix(name, ".go") {
- parsedFile, err = parser.ParseFile(fs, name, bytes.NewReader(data), 0)
- if err != nil {
- warnf("%s: %s", name, err)
- return
- }
- astFiles = append(astFiles, parsedFile)
- }
- files = append(files, &File{fset: fs, content: data, name: name, file: parsedFile})
- }
- pkg := new(Package)
- pkg.files = files
- // Type check the package.
- err := pkg.check(fs, astFiles)
- if err != nil && *verbose {
- warnf("%s", err)
- }
- for _, file := range files {
- file.pkg = pkg
- if file.file != nil {
- file.walkFile(file.name, file.file)
- }
- }
- asmCheck(pkg)
-}
-
-func visit(path string, f os.FileInfo, err error) error {
- if err != nil {
- warnf("walk error: %s", err)
- return err
- }
- // One package per directory. Ignore the files themselves.
- if !f.IsDir() {
- return nil
- }
- doPackageDir(path)
- return nil
-}
-
-func (pkg *Package) hasFileWithSuffix(suffix string) bool {
- for _, f := range pkg.files {
- if strings.HasSuffix(f.name, suffix) {
- return true
- }
- }
- return false
-}
-
-// walkDir recursively walks the tree looking for Go packages.
-func walkDir(root string) {
- filepath.Walk(root, visit)
-}
-
-// errorf formats the error to standard error, adding program
-// identification and a newline, and exits.
-func errorf(format string, args ...interface{}) {
- fmt.Fprintf(os.Stderr, "vet: "+format+"\n", args...)
- os.Exit(2)
-}
-
-// warnf formats the error to standard error, adding program
-// identification and a newline, but does not exit.
-func warnf(format string, args ...interface{}) {
- fmt.Fprintf(os.Stderr, "vet: "+format+"\n", args...)
- setExit(1)
-}
-
-// Println is fmt.Println guarded by -v.
-func Println(args ...interface{}) {
- if !*verbose {
- return
- }
- fmt.Println(args...)
-}
-
-// Printf is fmt.Printf guarded by -v.
-func Printf(format string, args ...interface{}) {
- if !*verbose {
- return
- }
- fmt.Printf(format+"\n", args...)
-}
-
-// Bad reports an error and sets the exit code..
-func (f *File) Bad(pos token.Pos, args ...interface{}) {
- f.Warn(pos, args...)
- setExit(1)
-}
-
-// Badf reports a formatted error and sets the exit code.
-func (f *File) Badf(pos token.Pos, format string, args ...interface{}) {
- f.Warnf(pos, format, args...)
- setExit(1)
-}
-
-func (f *File) loc(pos token.Pos) string {
- if pos == token.NoPos {
- return ""
- }
- // Do not print columns. Because the pos often points to the start of an
- // expression instead of the inner part with the actual error, the
- // precision can mislead.
- posn := f.fset.Position(pos)
- return fmt.Sprintf("%s:%d: ", posn.Filename, posn.Line)
-}
-
-// Warn reports an error but does not set the exit code.
-func (f *File) Warn(pos token.Pos, args ...interface{}) {
- fmt.Fprint(os.Stderr, f.loc(pos)+fmt.Sprintln(args...))
-}
-
-// Warnf reports a formatted error but does not set the exit code.
-func (f *File) Warnf(pos token.Pos, format string, args ...interface{}) {
- fmt.Fprintf(os.Stderr, f.loc(pos)+format+"\n", args...)
-}
-
-// walkFile walks the file's tree.
-func (f *File) walkFile(name string, file *ast.File) {
- Println("Checking file", name)
- ast.Walk(f, file)
-}
-
-// Visit implements the ast.Visitor interface.
-func (f *File) Visit(node ast.Node) ast.Visitor {
- switch n := node.(type) {
- case *ast.AssignStmt:
- f.walkAssignStmt(n)
- case *ast.CallExpr:
- f.walkCallExpr(n)
- case *ast.CompositeLit:
- f.walkCompositeLit(n)
- case *ast.Field:
- f.walkFieldTag(n)
- case *ast.FuncDecl:
- f.walkFuncDecl(n)
- case *ast.FuncLit:
- f.walkFuncLit(n)
- case *ast.InterfaceType:
- f.walkInterfaceType(n)
- case *ast.RangeStmt:
- f.walkRangeStmt(n)
- }
- return f
-}
-
-// walkAssignStmt walks an assignment statement
-func (f *File) walkAssignStmt(stmt *ast.AssignStmt) {
- f.checkAssignStmt(stmt)
- f.checkAtomicAssignment(stmt)
-}
-
-// walkCall walks a call expression.
-func (f *File) walkCall(call *ast.CallExpr, name string) {
- f.checkFmtPrintfCall(call, name)
-}
-
-// walkCallExpr walks a call expression.
-func (f *File) walkCallExpr(call *ast.CallExpr) {
- switch x := call.Fun.(type) {
- case *ast.Ident:
- f.walkCall(call, x.Name)
- case *ast.SelectorExpr:
- f.walkCall(call, x.Sel.Name)
- }
-}
-
-// walkCompositeLit walks a composite literal.
-func (f *File) walkCompositeLit(c *ast.CompositeLit) {
- f.checkUntaggedLiteral(c)
-}
-
-// walkFieldTag walks a struct field tag.
-func (f *File) walkFieldTag(field *ast.Field) {
- if field.Tag == nil {
- return
- }
- f.checkCanonicalFieldTag(field)
-}
-
-// walkMethod walks the method's signature.
-func (f *File) walkMethod(id *ast.Ident, t *ast.FuncType) {
- f.checkCanonicalMethod(id, t)
-}
-
-// walkFuncDecl walks a function declaration.
-func (f *File) walkFuncDecl(d *ast.FuncDecl) {
- f.checkUnreachable(d.Body)
- if d.Recv != nil {
- f.walkMethod(d.Name, d.Type)
- }
-}
-
-// walkFuncLit walks a function literal.
-func (f *File) walkFuncLit(x *ast.FuncLit) {
- f.checkUnreachable(x.Body)
-}
-
-// walkInterfaceType walks the method signatures of an interface.
-func (f *File) walkInterfaceType(t *ast.InterfaceType) {
- for _, field := range t.Methods.List {
- for _, id := range field.Names {
- f.walkMethod(id, field.Type.(*ast.FuncType))
- }
- }
-}
-
-// walkRangeStmt walks a range statement.
-func (f *File) walkRangeStmt(n *ast.RangeStmt) {
- checkRangeLoop(f, n)
-}
-
-// gofmt returns a string representation of the expression.
-func (f *File) gofmt(x ast.Expr) string {
- f.b.Reset()
- printer.Fprint(&f.b, f.fset, x)
- return f.b.String()
-}
diff --git a/src/cmd/vet/method.go b/src/cmd/vet/method.go
deleted file mode 100644
index 8064235f4..000000000
--- a/src/cmd/vet/method.go
+++ /dev/null
@@ -1,162 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This file contains the code to check canonical methods.
-
-package main
-
-import (
- "fmt"
- "go/ast"
- "go/printer"
- "strings"
-)
-
-type MethodSig struct {
- args []string
- results []string
-}
-
-// canonicalMethods lists the input and output types for Go methods
-// that are checked using dynamic interface checks. Because the
-// checks are dynamic, such methods would not cause a compile error
-// if they have the wrong signature: instead the dynamic check would
-// fail, sometimes mysteriously. If a method is found with a name listed
-// here but not the input/output types listed here, vet complains.
-//
-// A few of the canonical methods have very common names.
-// For example, a type might implement a Scan method that
-// has nothing to do with fmt.Scanner, but we still want to check
-// the methods that are intended to implement fmt.Scanner.
-// To do that, the arguments that have a = prefix are treated as
-// signals that the canonical meaning is intended: if a Scan
-// method doesn't have a fmt.ScanState as its first argument,
-// we let it go. But if it does have a fmt.ScanState, then the
-// rest has to match.
-var canonicalMethods = map[string]MethodSig{
- // "Flush": {{}, {"error"}}, // http.Flusher and jpeg.writer conflict
- "Format": {[]string{"=fmt.State", "rune"}, []string{}}, // fmt.Formatter
- "GobDecode": {[]string{"[]byte"}, []string{"error"}}, // gob.GobDecoder
- "GobEncode": {[]string{}, []string{"[]byte", "error"}}, // gob.GobEncoder
- "MarshalJSON": {[]string{}, []string{"[]byte", "error"}}, // json.Marshaler
- "MarshalXML": {[]string{}, []string{"[]byte", "error"}}, // xml.Marshaler
- "Peek": {[]string{"=int"}, []string{"[]byte", "error"}}, // image.reader (matching bufio.Reader)
- "ReadByte": {[]string{}, []string{"byte", "error"}}, // io.ByteReader
- "ReadFrom": {[]string{"=io.Reader"}, []string{"int64", "error"}}, // io.ReaderFrom
- "ReadRune": {[]string{}, []string{"rune", "int", "error"}}, // io.RuneReader
- "Scan": {[]string{"=fmt.ScanState", "rune"}, []string{"error"}}, // fmt.Scanner
- "Seek": {[]string{"=int64", "int"}, []string{"int64", "error"}}, // io.Seeker
- "UnmarshalJSON": {[]string{"[]byte"}, []string{"error"}}, // json.Unmarshaler
- "UnreadByte": {[]string{}, []string{"error"}},
- "UnreadRune": {[]string{}, []string{"error"}},
- "WriteByte": {[]string{"byte"}, []string{"error"}}, // jpeg.writer (matching bufio.Writer)
- "WriteTo": {[]string{"=io.Writer"}, []string{"int64", "error"}}, // io.WriterTo
-}
-
-func (f *File) checkCanonicalMethod(id *ast.Ident, t *ast.FuncType) {
- if !vet("methods") {
- return
- }
- // Expected input/output.
- expect, ok := canonicalMethods[id.Name]
- if !ok {
- return
- }
-
- // Actual input/output
- args := typeFlatten(t.Params.List)
- var results []ast.Expr
- if t.Results != nil {
- results = typeFlatten(t.Results.List)
- }
-
- // Do the =s (if any) all match?
- if !f.matchParams(expect.args, args, "=") || !f.matchParams(expect.results, results, "=") {
- return
- }
-
- // Everything must match.
- if !f.matchParams(expect.args, args, "") || !f.matchParams(expect.results, results, "") {
- expectFmt := id.Name + "(" + argjoin(expect.args) + ")"
- if len(expect.results) == 1 {
- expectFmt += " " + argjoin(expect.results)
- } else if len(expect.results) > 1 {
- expectFmt += " (" + argjoin(expect.results) + ")"
- }
-
- f.b.Reset()
- if err := printer.Fprint(&f.b, f.fset, t); err != nil {
- fmt.Fprintf(&f.b, "<%s>", err)
- }
- actual := f.b.String()
- actual = strings.TrimPrefix(actual, "func")
- actual = id.Name + actual
-
- f.Badf(id.Pos(), "method %s should have signature %s", actual, expectFmt)
- }
-}
-
-func argjoin(x []string) string {
- y := make([]string, len(x))
- for i, s := range x {
- if s[0] == '=' {
- s = s[1:]
- }
- y[i] = s
- }
- return strings.Join(y, ", ")
-}
-
-// Turn parameter list into slice of types
-// (in the ast, types are Exprs).
-// Have to handle f(int, bool) and f(x, y, z int)
-// so not a simple 1-to-1 conversion.
-func typeFlatten(l []*ast.Field) []ast.Expr {
- var t []ast.Expr
- for _, f := range l {
- if len(f.Names) == 0 {
- t = append(t, f.Type)
- continue
- }
- for _ = range f.Names {
- t = append(t, f.Type)
- }
- }
- return t
-}
-
-// Does each type in expect with the given prefix match the corresponding type in actual?
-func (f *File) matchParams(expect []string, actual []ast.Expr, prefix string) bool {
- for i, x := range expect {
- if !strings.HasPrefix(x, prefix) {
- continue
- }
- if i >= len(actual) {
- return false
- }
- if !f.matchParamType(x, actual[i]) {
- return false
- }
- }
- if prefix == "" && len(actual) > len(expect) {
- return false
- }
- return true
-}
-
-// Does this one type match?
-func (f *File) matchParamType(expect string, actual ast.Expr) bool {
- if strings.HasPrefix(expect, "=") {
- expect = expect[1:]
- }
- // Strip package name if we're in that package.
- if n := len(f.file.Name.Name); len(expect) > n && expect[:n] == f.file.Name.Name && expect[n] == '.' {
- expect = expect[n+1:]
- }
-
- // Overkill but easy.
- f.b.Reset()
- printer.Fprint(&f.b, f.fset, actual)
- return f.b.String() == expect
-}
diff --git a/src/cmd/vet/print.go b/src/cmd/vet/print.go
deleted file mode 100644
index debfbf0bf..000000000
--- a/src/cmd/vet/print.go
+++ /dev/null
@@ -1,351 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This file contains the printf-checker.
-
-package main
-
-import (
- "flag"
- "go/ast"
- "go/token"
- "strconv"
- "strings"
- "unicode/utf8"
-)
-
-var printfuncs = flag.String("printfuncs", "", "comma-separated list of print function names to check")
-
-// printfList records the formatted-print functions. The value is the location
-// 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,
-}
-
-// printList records the unformatted-print functions. The value is the location
-// 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,
-}
-
-// checkCall triggers the print-specific checks if the call invokes a print function.
-func (f *File) checkFmtPrintfCall(call *ast.CallExpr, Name string) {
- if !vet("printf") {
- return
- }
- name := strings.ToLower(Name)
- if skip, ok := printfList[name]; ok {
- f.checkPrintf(call, Name, skip)
- return
- }
- if skip, ok := printList[name]; ok {
- f.checkPrint(call, Name, skip)
- return
- }
-}
-
-// literal returns the literal value represented by the expression, or nil if it is not a literal.
-func (f *File) literal(value ast.Expr) *ast.BasicLit {
- switch v := value.(type) {
- case *ast.BasicLit:
- return v
- case *ast.ParenExpr:
- return f.literal(v.X)
- case *ast.BinaryExpr:
- if v.Op != token.ADD {
- break
- }
- litX := f.literal(v.X)
- litY := f.literal(v.Y)
- if litX != nil && litY != nil {
- lit := *litX
- x, errX := strconv.Unquote(litX.Value)
- y, errY := strconv.Unquote(litY.Value)
- if errX == nil && errY == nil {
- return &ast.BasicLit{
- ValuePos: lit.ValuePos,
- Kind: lit.Kind,
- Value: strconv.Quote(x + y),
- }
- }
- }
- case *ast.Ident:
- // See if it's a constant or initial value (we can't tell the difference).
- if v.Obj == nil || v.Obj.Decl == nil {
- return nil
- }
- valueSpec, ok := v.Obj.Decl.(*ast.ValueSpec)
- if ok && len(valueSpec.Names) == len(valueSpec.Values) {
- // Find the index in the list of names
- var i int
- for i = 0; i < len(valueSpec.Names); i++ {
- if valueSpec.Names[i].Name == v.Name {
- if lit, ok := valueSpec.Values[i].(*ast.BasicLit); ok {
- return lit
- }
- return nil
- }
- }
- }
- }
- return nil
-}
-
-// checkPrintf checks a call to a formatted print routine such as Printf.
-// call.Args[formatIndex] is (well, should be) the format argument.
-func (f *File) checkPrintf(call *ast.CallExpr, name string, formatIndex int) {
- if formatIndex >= len(call.Args) {
- return
- }
- lit := f.literal(call.Args[formatIndex])
- if lit == nil {
- if *verbose {
- f.Warn(call.Pos(), "can't check non-literal format in call to", name)
- }
- return
- }
- if lit.Kind != token.STRING {
- f.Badf(call.Pos(), "literal %v not a string in call to", lit.Value, name)
- }
- format, err := strconv.Unquote(lit.Value)
- if err != nil {
- // Shouldn't happen if parser returned no errors, but be safe.
- f.Badf(call.Pos(), "invalid quoted string literal")
- }
- firstArg := formatIndex + 1 // Arguments are immediately after format string.
- if !strings.Contains(format, "%") {
- if len(call.Args) > firstArg {
- f.Badf(call.Pos(), "no formatting directive in %s call", name)
- }
- return
- }
- // Hard part: check formats against args.
- argNum := firstArg
- for i, w := 0, 0; i < len(format); i += w {
- w = 1
- if format[i] == '%' {
- verb, flags, nbytes, nargs := f.parsePrintfVerb(call, format[i:])
- w = nbytes
- if verb == '%' { // "%%" does nothing interesting.
- continue
- }
- // If we've run out of args, print after loop will pick that up.
- if argNum+nargs <= len(call.Args) {
- f.checkPrintfArg(call, verb, flags, argNum, nargs)
- }
- argNum += nargs
- }
- }
- // TODO: Dotdotdot is hard.
- if call.Ellipsis.IsValid() && argNum != len(call.Args) {
- return
- }
- if argNum != len(call.Args) {
- expect := argNum - firstArg
- numArgs := len(call.Args) - firstArg
- f.Badf(call.Pos(), "wrong number of args for format in %s call: %d needed but %d args", name, expect, numArgs)
- }
-}
-
-// parsePrintfVerb returns the verb that begins the format string, along with its flags,
-// the number of bytes to advance the format to step past the verb, and number of
-// arguments it consumes.
-func (f *File) parsePrintfVerb(call *ast.CallExpr, format string) (verb rune, flags []byte, nbytes, nargs int) {
- // There's guaranteed a percent sign.
- flags = make([]byte, 0, 5)
- nbytes = 1
- end := len(format)
- // There may be flags.
-FlagLoop:
- for nbytes < end {
- switch format[nbytes] {
- case '#', '0', '+', '-', ' ':
- flags = append(flags, format[nbytes])
- nbytes++
- default:
- break FlagLoop
- }
- }
- getNum := func() {
- if nbytes < end && format[nbytes] == '*' {
- nbytes++
- nargs++
- } else {
- for nbytes < end && '0' <= format[nbytes] && format[nbytes] <= '9' {
- nbytes++
- }
- }
- }
- // There may be a width.
- getNum()
- // If there's a period, there may be a precision.
- if nbytes < end && format[nbytes] == '.' {
- flags = append(flags, '.') // Treat precision as a flag.
- nbytes++
- getNum()
- }
- // Now a verb.
- c, w := utf8.DecodeRuneInString(format[nbytes:])
- nbytes += w
- verb = c
- if c != '%' {
- nargs++
- }
- return
-}
-
-// printfArgType encodes the types of expressions a printf verb accepts. It is a bitmask.
-type printfArgType int
-
-const (
- argBool printfArgType = 1 << iota
- argInt
- argRune
- argString
- argFloat
- argPointer
- anyType printfArgType = ^0
-)
-
-type printVerb struct {
- verb rune
- flags string // known flags are all ASCII
- typ printfArgType
-}
-
-// Common flag sets for printf verbs.
-const (
- numFlag = " -+.0"
- sharpNumFlag = " -+.0#"
- allFlags = " -+.0#"
-)
-
-// printVerbs identifies which flags are known to printf for each verb.
-// TODO: A type that implements Formatter may do what it wants, and vet
-// will complain incorrectly.
-var printVerbs = []printVerb{
- // '-' is a width modifier, always valid.
- // '.' is a precision for float, max width for strings.
- // '+' is required sign for numbers, Go format for %v.
- // '#' is alternate format for several verbs.
- // ' ' is spacer for numbers
- {'b', numFlag, argInt | argFloat},
- {'c', "-", argRune | argInt},
- {'d', numFlag, argInt},
- {'e', numFlag, argFloat},
- {'E', numFlag, argFloat},
- {'f', numFlag, argFloat},
- {'F', numFlag, argFloat},
- {'g', numFlag, argFloat},
- {'G', numFlag, argFloat},
- {'o', sharpNumFlag, argInt},
- {'p', "-#", argPointer},
- {'q', " -+.0#", argRune | argInt | argString},
- {'s', " -+.0", argString},
- {'t', "-", argBool},
- {'T', "-", anyType},
- {'U', "-#", argRune | argInt},
- {'v', allFlags, anyType},
- {'x', sharpNumFlag, argRune | argInt | argString},
- {'X', sharpNumFlag, argRune | argInt | argString},
-}
-
-const printfVerbs = "bcdeEfFgGopqstTvxUX"
-
-func (f *File) checkPrintfArg(call *ast.CallExpr, verb rune, flags []byte, argNum, nargs int) {
- // Linear scan is fast enough for a small list.
- for _, v := range printVerbs {
- if v.verb == verb {
- for _, flag := range flags {
- if !strings.ContainsRune(v.flags, rune(flag)) {
- f.Badf(call.Pos(), "unrecognized printf flag for verb %q: %q", verb, flag)
- return
- }
- }
- // Verb is good. If nargs>1, we have something like %.*s and all but the final
- // arg must be integer.
- for i := 0; i < nargs-1; i++ {
- if !f.matchArgType(argInt, call.Args[argNum+i]) {
- f.Badf(call.Pos(), "arg %s for * in printf format not of type int", f.gofmt(call.Args[argNum+i]))
- }
- }
- for _, v := range printVerbs {
- if v.verb == verb {
- arg := call.Args[argNum+nargs-1]
- if !f.matchArgType(v.typ, arg) {
- typeString := ""
- if typ := f.pkg.types[arg]; typ != nil {
- typeString = typ.String()
- }
- f.Badf(call.Pos(), "arg %s for printf verb %%%c of wrong type: %s", f.gofmt(arg), verb, typeString)
- }
- break
- }
- }
- return
- }
- }
- f.Badf(call.Pos(), "unrecognized printf verb %q", verb)
-}
-
-// checkPrint checks a call to an unformatted print routine such as Println.
-// call.Args[firstArg] is the first argument to be printed.
-func (f *File) checkPrint(call *ast.CallExpr, name string, firstArg int) {
- isLn := strings.HasSuffix(name, "ln")
- isF := strings.HasPrefix(name, "F")
- args := call.Args
- // check for Println(os.Stderr, ...)
- if firstArg == 0 && !isF && len(args) > 0 {
- if sel, ok := args[0].(*ast.SelectorExpr); ok {
- if x, ok := sel.X.(*ast.Ident); ok {
- if x.Name == "os" && strings.HasPrefix(sel.Sel.Name, "Std") {
- f.Badf(call.Pos(), "first argument to %s is %s.%s", name, x.Name, sel.Sel.Name)
- }
- }
- }
- }
- if len(args) <= firstArg {
- // If we have a call to a method called Error that satisfies the Error interface,
- // then it's ok. Otherwise it's something like (*T).Error from the testing package
- // and we need to check it.
- if name == "Error" && f.isErrorMethodCall(call) {
- return
- }
- // If it's an Error call now, it's probably for printing errors.
- if !isLn {
- // Check the signature to be sure: there are niladic functions called "error".
- if firstArg != 0 || f.numArgsInSignature(call) != firstArg {
- f.Badf(call.Pos(), "no args in %s call", name)
- }
- }
- return
- }
- arg := args[firstArg]
- if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING {
- if strings.Contains(lit.Value, "%") {
- f.Badf(call.Pos(), "possible formatting directive in %s call", name)
- }
- }
- if isLn {
- // 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 strings.HasSuffix(lit.Value, `\n"`) {
- f.Badf(call.Pos(), "%s call ends with newline", name)
- }
- }
- }
-}
diff --git a/src/cmd/vet/rangeloop.go b/src/cmd/vet/rangeloop.go
deleted file mode 100644
index ecc595427..000000000
--- a/src/cmd/vet/rangeloop.go
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2012 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-/*
-This file contains the code to check range loop variables bound inside function
-literals that are deferred or launched in new goroutines. We only check
-instances where the defer or go statement is the last statement in the loop
-body, as otherwise we would need whole program analysis.
-
-For example:
-
- for i, v := range s {
- go func() {
- println(i, v) // not what you might expect
- }()
- }
-
-See: http://golang.org/doc/go_faq.html#closures_and_goroutines
-*/
-
-package main
-
-import "go/ast"
-
-// checkRangeLoop walks the body of the provided range statement, checking if
-// its index or value variables are used unsafely inside goroutines or deferred
-// function literals.
-func checkRangeLoop(f *File, n *ast.RangeStmt) {
- if !vet("rangeloops") {
- return
- }
- key, _ := n.Key.(*ast.Ident)
- val, _ := n.Value.(*ast.Ident)
- if key == nil && val == nil {
- return
- }
- sl := n.Body.List
- if len(sl) == 0 {
- return
- }
- var last *ast.CallExpr
- switch s := sl[len(sl)-1].(type) {
- case *ast.GoStmt:
- last = s.Call
- case *ast.DeferStmt:
- last = s.Call
- default:
- return
- }
- lit, ok := last.Fun.(*ast.FuncLit)
- if !ok {
- return
- }
- ast.Inspect(lit.Body, func(n ast.Node) bool {
- id, ok := n.(*ast.Ident)
- if !ok || id.Obj == nil {
- return true
- }
- if key != nil && id.Obj == key.Obj || val != nil && id.Obj == val.Obj {
- f.Warn(id.Pos(), "range variable", id.Name, "enclosed by function")
- }
- return true
- })
-}
diff --git a/src/cmd/vet/structtag.go b/src/cmd/vet/structtag.go
deleted file mode 100644
index d83578836..000000000
--- a/src/cmd/vet/structtag.go
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This file contains the test for canonical struct tags.
-
-package main
-
-import (
- "go/ast"
- "reflect"
- "strconv"
-)
-
-// checkField checks a struct field tag.
-func (f *File) checkCanonicalFieldTag(field *ast.Field) {
- if !vet("structtags") {
- return
- }
- if field.Tag == nil {
- return
- }
-
- tag, err := strconv.Unquote(field.Tag.Value)
- if err != nil {
- f.Badf(field.Pos(), "unable to read struct tag %s", field.Tag.Value)
- return
- }
-
- // Check tag for validity by appending
- // new key:value to end and checking that
- // the tag parsing code can find it.
- if reflect.StructTag(tag+` _gofix:"_magic"`).Get("_gofix") != "_magic" {
- f.Badf(field.Pos(), "struct field tag %s not compatible with reflect.StructTag.Get", field.Tag.Value)
- return
- }
-}
diff --git a/src/cmd/vet/taglit.go b/src/cmd/vet/taglit.go
deleted file mode 100644
index bcad2fe0a..000000000
--- a/src/cmd/vet/taglit.go
+++ /dev/null
@@ -1,164 +0,0 @@
-// Copyright 2012 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This file contains the test for untagged struct literals.
-
-package main
-
-import (
- "flag"
- "go/ast"
- "strings"
-)
-
-var compositeWhiteList = flag.Bool("compositewhitelist", true, "use composite white list; for testing only")
-
-// checkUntaggedLiteral checks if a composite literal is a struct literal with
-// untagged fields.
-func (f *File) checkUntaggedLiteral(c *ast.CompositeLit) {
- if !vet("composites") {
- return
- }
-
- typ := c.Type
- for {
- if typ1, ok := c.Type.(*ast.ParenExpr); ok {
- typ = typ1
- continue
- }
- break
- }
-
- switch typ.(type) {
- case *ast.ArrayType:
- return
- case *ast.MapType:
- return
- case *ast.StructType:
- return // a literal struct type does not need to use tags
- case *ast.Ident:
- // A simple type name like t or T does not need tags either,
- // since it is almost certainly declared in the current package.
- // (The exception is names being used via import . "pkg", but
- // those are already breaking the Go 1 compatibility promise,
- // so not reporting potential additional breakage seems okay.)
- return
- }
-
- // Otherwise the type is a selector like pkg.Name.
- // We only care if pkg.Name is a struct, not if it's a map, array, or slice.
- isStruct, typeString := f.pkg.isStruct(c)
- if !isStruct {
- return
- }
-
- if typeString == "" { // isStruct doesn't know
- typeString = f.gofmt(typ)
- }
-
- // It's a struct, or we can't tell it's not a struct because we don't have types.
-
- // Check if the CompositeLit contains an untagged field.
- allKeyValue := true
- for _, e := range c.Elts {
- if _, ok := e.(*ast.KeyValueExpr); !ok {
- allKeyValue = false
- break
- }
- }
- if allKeyValue {
- return
- }
-
- // Check that the CompositeLit's type has the form pkg.Typ.
- s, ok := c.Type.(*ast.SelectorExpr)
- if !ok {
- return
- }
- pkg, ok := s.X.(*ast.Ident)
- if !ok {
- return
- }
-
- // Convert the package name to an import path, and compare to a whitelist.
- path := pkgPath(f, pkg.Name)
- if path == "" {
- f.Badf(c.Pos(), "unresolvable package for %s.%s literal", pkg.Name, s.Sel.Name)
- return
- }
- typeName := path + "." + s.Sel.Name
- if *compositeWhiteList && untaggedLiteralWhitelist[typeName] {
- return
- }
-
- f.Warn(c.Pos(), typeString+" composite literal uses untagged fields")
-}
-
-// pkgPath returns the import path "image/png" for the package name "png".
-//
-// This is based purely on syntax and convention, and not on the imported
-// package's contents. It will be incorrect if a package name differs from the
-// leaf element of the import path, or if the package was a dot import.
-func pkgPath(f *File, pkgName string) (path string) {
- for _, x := range f.file.Imports {
- s := strings.Trim(x.Path.Value, `"`)
- if x.Name != nil {
- // Catch `import pkgName "foo/bar"`.
- if x.Name.Name == pkgName {
- return s
- }
- } else {
- // Catch `import "pkgName"` or `import "foo/bar/pkgName"`.
- if s == pkgName || strings.HasSuffix(s, "/"+pkgName) {
- return s
- }
- }
- }
- return ""
-}
-
-var untaggedLiteralWhitelist = map[string]bool{
- /*
- These types are actually slices. Syntactically, we cannot tell
- whether the Typ in pkg.Typ{1, 2, 3} is a slice or a struct, so we
- whitelist all the standard package library's exported slice types.
-
- find $GOROOT/src/pkg -type f | grep -v _test.go | xargs grep '^type.*\[\]' | \
- grep -v ' map\[' | sed 's,/[^/]*go.type,,' | sed 's,.*src/pkg/,,' | \
- sed 's, ,.,' | sed 's, .*,,' | grep -v '\.[a-z]' | \
- sort | awk '{ print "\"" $0 "\": true," }'
- */
- "crypto/x509/pkix.RDNSequence": true,
- "crypto/x509/pkix.RelativeDistinguishedNameSET": true,
- "database/sql.RawBytes": true,
- "debug/macho.LoadBytes": true,
- "encoding/asn1.ObjectIdentifier": true,
- "encoding/asn1.RawContent": true,
- "encoding/json.RawMessage": true,
- "encoding/xml.CharData": true,
- "encoding/xml.Comment": true,
- "encoding/xml.Directive": true,
- "go/scanner.ErrorList": true,
- "image/color.Palette": true,
- "net.HardwareAddr": true,
- "net.IP": true,
- "net.IPMask": true,
- "sort.Float64Slice": true,
- "sort.IntSlice": true,
- "sort.StringSlice": true,
- "unicode.SpecialCase": true,
-
- // These image and image/color struct types are frozen. We will never add fields to them.
- "image/color.Alpha16": true,
- "image/color.Alpha": true,
- "image/color.Gray16": true,
- "image/color.Gray": true,
- "image/color.NRGBA64": true,
- "image/color.NRGBA": true,
- "image/color.RGBA64": true,
- "image/color.RGBA": true,
- "image/color.YCbCr": true,
- "image.Point": true,
- "image.Rectangle": true,
-}
diff --git a/src/cmd/vet/test_asm.go b/src/cmd/vet/test_asm.go
deleted file mode 100644
index 098bdd15c..000000000
--- a/src/cmd/vet/test_asm.go
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build ignore
-
-// This file contains declarations to test the assembly in test_asm.s.
-
-package main
-
-func arg1(x int8, y uint8)
-func arg2(x int16, y uint16)
-func arg4(x int32, y uint32)
-func arg8(x int64, y uint64)
-func argint(x int, y uint)
-func argptr(x *byte, y *byte, c chan int, m map[int]int, f func())
-func argstring(x, y string)
-func argslice(x, y []string)
-func argiface(x interface{}, y interface {
- m()
-})
-func returnint() int
-func returnbyte(x int) byte
-func returnnamed(x byte) (r1 int, r2 int16, r3 string, r4 byte)
diff --git a/src/cmd/vet/test_asm1.s b/src/cmd/vet/test_asm1.s
deleted file mode 100644
index 8cd9eeab6..000000000
--- a/src/cmd/vet/test_asm1.s
+++ /dev/null
@@ -1,247 +0,0 @@
-// Copyright 2013 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.
-
-// +build amd64
-// +build vet_test
-
-TEXT ·arg1(SB),0,$0-2
- MOVB x+0(FP), AX
- MOVB y+1(FP), BX
- MOVW x+0(FP), AX // ERROR "\[amd64\] invalid MOVW of x\+0\(FP\); int8 is 1-byte value"
- MOVW y+1(FP), AX // ERROR "invalid MOVW of y\+1\(FP\); uint8 is 1-byte value"
- MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int8 is 1-byte value"
- MOVL y+1(FP), AX // ERROR "invalid MOVL of y\+1\(FP\); uint8 is 1-byte value"
- MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int8 is 1-byte value"
- MOVQ y+1(FP), AX // ERROR "invalid MOVQ of y\+1\(FP\); uint8 is 1-byte value"
- MOVB x+1(FP), AX // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)"
- MOVB y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)"
- TESTB x+0(FP), AX
- TESTB y+1(FP), BX
- TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int8 is 1-byte value"
- TESTW y+1(FP), AX // ERROR "invalid TESTW of y\+1\(FP\); uint8 is 1-byte value"
- TESTL x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int8 is 1-byte value"
- TESTL y+1(FP), AX // ERROR "invalid TESTL of y\+1\(FP\); uint8 is 1-byte value"
- TESTQ x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int8 is 1-byte value"
- TESTQ y+1(FP), AX // ERROR "invalid TESTQ of y\+1\(FP\); uint8 is 1-byte value"
- TESTB x+1(FP), AX // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)"
- TESTB y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)"
- RET
-
-TEXT ·arg2(SB),0,$0-4
- MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int16 is 2-byte value"
- MOVB y+2(FP), AX // ERROR "invalid MOVB of y\+2\(FP\); uint16 is 2-byte value"
- MOVW x+0(FP), AX
- MOVW y+2(FP), BX
- MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int16 is 2-byte value"
- MOVL y+2(FP), AX // ERROR "invalid MOVL of y\+2\(FP\); uint16 is 2-byte value"
- MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int16 is 2-byte value"
- MOVQ y+2(FP), AX // ERROR "invalid MOVQ of y\+2\(FP\); uint16 is 2-byte value"
- MOVW x+2(FP), AX // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)"
- MOVW y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+2\(FP\)"
- TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int16 is 2-byte value"
- TESTB y+2(FP), AX // ERROR "invalid TESTB of y\+2\(FP\); uint16 is 2-byte value"
- TESTW x+0(FP), AX
- TESTW y+2(FP), BX
- TESTL x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int16 is 2-byte value"
- TESTL y+2(FP), AX // ERROR "invalid TESTL of y\+2\(FP\); uint16 is 2-byte value"
- TESTQ x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int16 is 2-byte value"
- TESTQ y+2(FP), AX // ERROR "invalid TESTQ of y\+2\(FP\); uint16 is 2-byte value"
- TESTW x+2(FP), AX // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)"
- TESTW y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+2\(FP\)"
- RET
-
-TEXT ·arg4(SB),0,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-8"
- MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int32 is 4-byte value"
- MOVB y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); uint32 is 4-byte value"
- MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int32 is 4-byte value"
- MOVW y+4(FP), AX // ERROR "invalid MOVW of y\+4\(FP\); uint32 is 4-byte value"
- MOVL x+0(FP), AX
- MOVL y+4(FP), AX
- MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int32 is 4-byte value"
- MOVQ y+4(FP), AX // ERROR "invalid MOVQ of y\+4\(FP\); uint32 is 4-byte value"
- MOVL x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
- MOVL y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
- TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int32 is 4-byte value"
- TESTB y+4(FP), BX // ERROR "invalid TESTB of y\+4\(FP\); uint32 is 4-byte value"
- TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int32 is 4-byte value"
- TESTW y+4(FP), AX // ERROR "invalid TESTW of y\+4\(FP\); uint32 is 4-byte value"
- TESTL x+0(FP), AX
- TESTL y+4(FP), AX
- TESTQ x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int32 is 4-byte value"
- TESTQ y+4(FP), AX // ERROR "invalid TESTQ of y\+4\(FP\); uint32 is 4-byte value"
- TESTL x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
- TESTL y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
- RET
-
-TEXT ·arg8(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-16"
- MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int64 is 8-byte value"
- MOVB y+8(FP), BX // ERROR "invalid MOVB of y\+8\(FP\); uint64 is 8-byte value"
- MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int64 is 8-byte value"
- MOVW y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); uint64 is 8-byte value"
- MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int64 is 8-byte value"
- MOVL y+8(FP), AX // ERROR "invalid MOVL of y\+8\(FP\); uint64 is 8-byte value"
- MOVQ x+0(FP), AX
- MOVQ y+8(FP), AX
- MOVQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
- MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
- TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int64 is 8-byte value"
- TESTB y+8(FP), BX // ERROR "invalid TESTB of y\+8\(FP\); uint64 is 8-byte value"
- TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int64 is 8-byte value"
- TESTW y+8(FP), AX // ERROR "invalid TESTW of y\+8\(FP\); uint64 is 8-byte value"
- TESTL x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int64 is 8-byte value"
- TESTL y+8(FP), AX // ERROR "invalid TESTL of y\+8\(FP\); uint64 is 8-byte value"
- TESTQ x+0(FP), AX
- TESTQ y+8(FP), AX
- TESTQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
- TESTQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
- RET
-
-TEXT ·argint(SB),0,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-16"
- MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int is 8-byte value"
- MOVB y+8(FP), BX // ERROR "invalid MOVB of y\+8\(FP\); uint is 8-byte value"
- MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int is 8-byte value"
- MOVW y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); uint is 8-byte value"
- MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int is 8-byte value"
- MOVL y+8(FP), AX // ERROR "invalid MOVL of y\+8\(FP\); uint is 8-byte value"
- MOVQ x+0(FP), AX
- MOVQ y+8(FP), AX
- MOVQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
- MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
- TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int is 8-byte value"
- TESTB y+8(FP), BX // ERROR "invalid TESTB of y\+8\(FP\); uint is 8-byte value"
- TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int is 8-byte value"
- TESTW y+8(FP), AX // ERROR "invalid TESTW of y\+8\(FP\); uint is 8-byte value"
- TESTL x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int is 8-byte value"
- TESTL y+8(FP), AX // ERROR "invalid TESTL of y\+8\(FP\); uint is 8-byte value"
- TESTQ x+0(FP), AX
- TESTQ y+8(FP), AX
- TESTQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
- TESTQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
- RET
-
-TEXT ·argptr(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-40"
- MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); \*byte is 8-byte value"
- MOVB y+8(FP), BX // ERROR "invalid MOVB of y\+8\(FP\); \*byte is 8-byte value"
- MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); \*byte is 8-byte value"
- MOVW y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); \*byte is 8-byte value"
- MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); \*byte is 8-byte value"
- MOVL y+8(FP), AX // ERROR "invalid MOVL of y\+8\(FP\); \*byte is 8-byte value"
- MOVQ x+0(FP), AX
- MOVQ y+8(FP), AX
- MOVQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
- MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
- TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); \*byte is 8-byte value"
- TESTB y+8(FP), BX // ERROR "invalid TESTB of y\+8\(FP\); \*byte is 8-byte value"
- TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); \*byte is 8-byte value"
- TESTW y+8(FP), AX // ERROR "invalid TESTW of y\+8\(FP\); \*byte is 8-byte value"
- TESTL x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); \*byte is 8-byte value"
- TESTL y+8(FP), AX // ERROR "invalid TESTL of y\+8\(FP\); \*byte is 8-byte value"
- TESTQ x+0(FP), AX
- TESTQ y+8(FP), AX
- TESTQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
- TESTQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
- MOVL c+16(FP), AX // ERROR "invalid MOVL of c\+16\(FP\); chan int is 8-byte value"
- MOVL m+24(FP), AX // ERROR "invalid MOVL of m\+24\(FP\); map\[int\]int is 8-byte value"
- MOVL f+32(FP), AX // ERROR "invalid MOVL of f\+32\(FP\); func\(\) is 8-byte value"
- RET
-
-TEXT ·argstring(SB),0,$32 // ERROR "wrong argument size 0; expected \$\.\.\.-32"
- MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); string base is 8-byte value"
- MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); string base is 8-byte value"
- MOVQ x+0(FP), AX
- MOVW x_base+0(FP), AX // ERROR "invalid MOVW of x_base\+0\(FP\); string base is 8-byte value"
- MOVL x_base+0(FP), AX // ERROR "invalid MOVL of x_base\+0\(FP\); string base is 8-byte value"
- MOVQ x_base+0(FP), AX
- MOVW x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
- MOVL x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
- MOVQ x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
- MOVW x_len+8(FP), AX // ERROR "invalid MOVW of x_len\+8\(FP\); string len is 8-byte value"
- MOVL x_len+8(FP), AX // ERROR "invalid MOVL of x_len\+8\(FP\); string len is 8-byte value"
- MOVQ x_len+8(FP), AX
- MOVQ y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+16\(FP\)"
- MOVQ y_len+8(FP), AX // ERROR "invalid offset y_len\+8\(FP\); expected y_len\+24\(FP\)"
- RET
-
-TEXT ·argslice(SB),0,$48 // ERROR "wrong argument size 0; expected \$\.\.\.-48"
- MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); slice base is 8-byte value"
- MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); slice base is 8-byte value"
- MOVQ x+0(FP), AX
- MOVW x_base+0(FP), AX // ERROR "invalid MOVW of x_base\+0\(FP\); slice base is 8-byte value"
- MOVL x_base+0(FP), AX // ERROR "invalid MOVL of x_base\+0\(FP\); slice base is 8-byte value"
- MOVQ x_base+0(FP), AX
- MOVW x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
- MOVL x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
- MOVQ x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
- MOVW x_len+8(FP), AX // ERROR "invalid MOVW of x_len\+8\(FP\); slice len is 8-byte value"
- MOVL x_len+8(FP), AX // ERROR "invalid MOVL of x_len\+8\(FP\); slice len is 8-byte value"
- MOVQ x_len+8(FP), AX
- MOVW x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)"
- MOVL x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)"
- MOVQ x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)"
- MOVW x_cap+16(FP), AX // ERROR "invalid MOVW of x_cap\+16\(FP\); slice cap is 8-byte value"
- MOVL x_cap+16(FP), AX // ERROR "invalid MOVL of x_cap\+16\(FP\); slice cap is 8-byte value"
- MOVQ x_cap+16(FP), AX
- MOVQ y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+24\(FP\)"
- MOVQ y_len+8(FP), AX // ERROR "invalid offset y_len\+8\(FP\); expected y_len\+32\(FP\)"
- MOVQ y_cap+16(FP), AX // ERROR "invalid offset y_cap\+16\(FP\); expected y_cap\+40\(FP\)"
- RET
-
-TEXT ·argiface(SB),0,$0-32
- MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); interface type is 8-byte value"
- MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); interface type is 8-byte value"
- MOVQ x+0(FP), AX
- MOVW x_type+0(FP), AX // ERROR "invalid MOVW of x_type\+0\(FP\); interface type is 8-byte value"
- MOVL x_type+0(FP), AX // ERROR "invalid MOVL of x_type\+0\(FP\); interface type is 8-byte value"
- MOVQ x_type+0(FP), AX
- MOVQ x_itable+0(FP), AX // ERROR "unknown variable x_itable; offset 0 is x_type\+0\(FP\)"
- MOVQ x_itable+1(FP), AX // ERROR "unknown variable x_itable; offset 1 is x_type\+0\(FP\)"
- MOVW x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)"
- MOVL x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)"
- MOVQ x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)"
- MOVW x_data+8(FP), AX // ERROR "invalid MOVW of x_data\+8\(FP\); interface data is 8-byte value"
- MOVL x_data+8(FP), AX // ERROR "invalid MOVL of x_data\+8\(FP\); interface data is 8-byte value"
- MOVQ x_data+8(FP), AX
- MOVW y+16(FP), AX // ERROR "invalid MOVW of y\+16\(FP\); interface itable is 8-byte value"
- MOVL y+16(FP), AX // ERROR "invalid MOVL of y\+16\(FP\); interface itable is 8-byte value"
- MOVQ y+16(FP), AX
- MOVW y_itable+16(FP), AX // ERROR "invalid MOVW of y_itable\+16\(FP\); interface itable is 8-byte value"
- MOVL y_itable+16(FP), AX // ERROR "invalid MOVL of y_itable\+16\(FP\); interface itable is 8-byte value"
- MOVQ y_itable+16(FP), AX
- MOVQ y_type+16(FP), AX // ERROR "unknown variable y_type; offset 16 is y_itable\+16\(FP\)"
- MOVW y_data+16(FP), AX // ERROR "invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)"
- MOVL y_data+16(FP), AX // ERROR "invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)"
- MOVQ y_data+16(FP), AX // ERROR "invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)"
- MOVW y_data+24(FP), AX // ERROR "invalid MOVW of y_data\+24\(FP\); interface data is 8-byte value"
- MOVL y_data+24(FP), AX // ERROR "invalid MOVL of y_data\+24\(FP\); interface data is 8-byte value"
- MOVQ y_data+24(FP), AX
- RET
-
-TEXT ·returnint(SB),0,$0-8
- MOVB AX, ret+0(FP) // ERROR "invalid MOVB of ret\+0\(FP\); int is 8-byte value"
- MOVW AX, ret+0(FP) // ERROR "invalid MOVW of ret\+0\(FP\); int is 8-byte value"
- MOVL AX, ret+0(FP) // ERROR "invalid MOVL of ret\+0\(FP\); int is 8-byte value"
- MOVQ AX, ret+0(FP)
- MOVQ AX, ret+1(FP) // ERROR "invalid offset ret\+1\(FP\); expected ret\+0\(FP\)"
- MOVQ AX, r+0(FP) // ERROR "unknown variable r; offset 0 is ret\+0\(FP\)"
- RET
-
-TEXT ·returnbyte(SB),0,$0-9
- MOVQ x+0(FP), AX
- MOVB AX, ret+8(FP)
- MOVW AX, ret+8(FP) // ERROR "invalid MOVW of ret\+8\(FP\); byte is 1-byte value"
- MOVL AX, ret+8(FP) // ERROR "invalid MOVL of ret\+8\(FP\); byte is 1-byte value"
- MOVQ AX, ret+8(FP) // ERROR "invalid MOVQ of ret\+8\(FP\); byte is 1-byte value"
- MOVB AX, ret+7(FP) // ERROR "invalid offset ret\+7\(FP\); expected ret\+8\(FP\)"
- RET
-
-TEXT ·returnnamed(SB),0,$0-41
- MOVB x+0(FP), AX
- MOVQ AX, r1+8(FP)
- MOVW AX, r2+16(FP)
- MOVQ AX, r3+24(FP)
- MOVQ AX, r3_base+24(FP)
- MOVQ AX, r3_len+32(FP)
- MOVB AX, r4+40(FP)
- MOVL AX, r1+8(FP) // ERROR "invalid MOVL of r1\+8\(FP\); int is 8-byte value"
- RET
diff --git a/src/cmd/vet/test_asm2.s b/src/cmd/vet/test_asm2.s
deleted file mode 100644
index d8679c574..000000000
--- a/src/cmd/vet/test_asm2.s
+++ /dev/null
@@ -1,251 +0,0 @@
-// Copyright 2013 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.
-
-// +build 386
-// +build vet_test
-
-TEXT ·arg1(SB),0,$0-2
- MOVB x+0(FP), AX
- MOVB y+1(FP), BX
- MOVW x+0(FP), AX // ERROR "\[386\] invalid MOVW of x\+0\(FP\); int8 is 1-byte value"
- MOVW y+1(FP), AX // ERROR "invalid MOVW of y\+1\(FP\); uint8 is 1-byte value"
- MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int8 is 1-byte value"
- MOVL y+1(FP), AX // ERROR "invalid MOVL of y\+1\(FP\); uint8 is 1-byte value"
- MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int8 is 1-byte value"
- MOVQ y+1(FP), AX // ERROR "invalid MOVQ of y\+1\(FP\); uint8 is 1-byte value"
- MOVB x+1(FP), AX // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)"
- MOVB y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)"
- TESTB x+0(FP), AX
- TESTB y+1(FP), BX
- TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int8 is 1-byte value"
- TESTW y+1(FP), AX // ERROR "invalid TESTW of y\+1\(FP\); uint8 is 1-byte value"
- TESTL x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int8 is 1-byte value"
- TESTL y+1(FP), AX // ERROR "invalid TESTL of y\+1\(FP\); uint8 is 1-byte value"
- TESTQ x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int8 is 1-byte value"
- TESTQ y+1(FP), AX // ERROR "invalid TESTQ of y\+1\(FP\); uint8 is 1-byte value"
- TESTB x+1(FP), AX // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)"
- TESTB y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)"
- RET
-
-TEXT ·arg2(SB),0,$0-4
- MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int16 is 2-byte value"
- MOVB y+2(FP), AX // ERROR "invalid MOVB of y\+2\(FP\); uint16 is 2-byte value"
- MOVW x+0(FP), AX
- MOVW y+2(FP), BX
- MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int16 is 2-byte value"
- MOVL y+2(FP), AX // ERROR "invalid MOVL of y\+2\(FP\); uint16 is 2-byte value"
- MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int16 is 2-byte value"
- MOVQ y+2(FP), AX // ERROR "invalid MOVQ of y\+2\(FP\); uint16 is 2-byte value"
- MOVW x+2(FP), AX // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)"
- MOVW y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+2\(FP\)"
- TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int16 is 2-byte value"
- TESTB y+2(FP), AX // ERROR "invalid TESTB of y\+2\(FP\); uint16 is 2-byte value"
- TESTW x+0(FP), AX
- TESTW y+2(FP), BX
- TESTL x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int16 is 2-byte value"
- TESTL y+2(FP), AX // ERROR "invalid TESTL of y\+2\(FP\); uint16 is 2-byte value"
- TESTQ x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int16 is 2-byte value"
- TESTQ y+2(FP), AX // ERROR "invalid TESTQ of y\+2\(FP\); uint16 is 2-byte value"
- TESTW x+2(FP), AX // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)"
- TESTW y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+2\(FP\)"
- RET
-
-TEXT ·arg4(SB),0,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-8"
- MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int32 is 4-byte value"
- MOVB y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); uint32 is 4-byte value"
- MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int32 is 4-byte value"
- MOVW y+4(FP), AX // ERROR "invalid MOVW of y\+4\(FP\); uint32 is 4-byte value"
- MOVL x+0(FP), AX
- MOVL y+4(FP), AX
- MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int32 is 4-byte value"
- MOVQ y+4(FP), AX // ERROR "invalid MOVQ of y\+4\(FP\); uint32 is 4-byte value"
- MOVL x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
- MOVL y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
- TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int32 is 4-byte value"
- TESTB y+4(FP), BX // ERROR "invalid TESTB of y\+4\(FP\); uint32 is 4-byte value"
- TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int32 is 4-byte value"
- TESTW y+4(FP), AX // ERROR "invalid TESTW of y\+4\(FP\); uint32 is 4-byte value"
- TESTL x+0(FP), AX
- TESTL y+4(FP), AX
- TESTQ x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int32 is 4-byte value"
- TESTQ y+4(FP), AX // ERROR "invalid TESTQ of y\+4\(FP\); uint32 is 4-byte value"
- TESTL x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
- TESTL y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
- RET
-
-TEXT ·arg8(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-16"
- MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int64 is 8-byte value"
- MOVB y+8(FP), BX // ERROR "invalid MOVB of y\+8\(FP\); uint64 is 8-byte value"
- MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int64 is 8-byte value"
- MOVW y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); uint64 is 8-byte value"
- MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int64 is 8-byte value containing x_lo\+0\(FP\) and x_hi\+4\(FP\)"
- MOVL x_lo+0(FP), AX
- MOVL x_hi+4(FP), AX
- MOVL y+8(FP), AX // ERROR "invalid MOVL of y\+8\(FP\); uint64 is 8-byte value containing y_lo\+8\(FP\) and y_hi\+12\(FP\)"
- MOVL y_lo+8(FP), AX
- MOVL y_hi+12(FP), AX
- MOVQ x+0(FP), AX
- MOVQ y+8(FP), AX
- MOVQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
- MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
- TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int64 is 8-byte value"
- TESTB y+8(FP), BX // ERROR "invalid TESTB of y\+8\(FP\); uint64 is 8-byte value"
- TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int64 is 8-byte value"
- TESTW y+8(FP), AX // ERROR "invalid TESTW of y\+8\(FP\); uint64 is 8-byte value"
- TESTL x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int64 is 8-byte value containing x_lo\+0\(FP\) and x_hi\+4\(FP\)"
- TESTL y+8(FP), AX // ERROR "invalid TESTL of y\+8\(FP\); uint64 is 8-byte value containing y_lo\+8\(FP\) and y_hi\+12\(FP\)"
- TESTQ x+0(FP), AX
- TESTQ y+8(FP), AX
- TESTQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
- TESTQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
- RET
-
-TEXT ·argint(SB),0,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-8"
- MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int is 4-byte value"
- MOVB y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); uint is 4-byte value"
- MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int is 4-byte value"
- MOVW y+4(FP), AX // ERROR "invalid MOVW of y\+4\(FP\); uint is 4-byte value"
- MOVL x+0(FP), AX
- MOVL y+4(FP), AX
- MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int is 4-byte value"
- MOVQ y+4(FP), AX // ERROR "invalid MOVQ of y\+4\(FP\); uint is 4-byte value"
- MOVQ x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
- MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
- TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int is 4-byte value"
- TESTB y+4(FP), BX // ERROR "invalid TESTB of y\+4\(FP\); uint is 4-byte value"
- TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int is 4-byte value"
- TESTW y+4(FP), AX // ERROR "invalid TESTW of y\+4\(FP\); uint is 4-byte value"
- TESTL x+0(FP), AX
- TESTL y+4(FP), AX
- TESTQ x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int is 4-byte value"
- TESTQ y+4(FP), AX // ERROR "invalid TESTQ of y\+4\(FP\); uint is 4-byte value"
- TESTQ x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
- TESTQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
- RET
-
-TEXT ·argptr(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-20"
- MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); \*byte is 4-byte value"
- MOVB y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); \*byte is 4-byte value"
- MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); \*byte is 4-byte value"
- MOVW y+4(FP), AX // ERROR "invalid MOVW of y\+4\(FP\); \*byte is 4-byte value"
- MOVL x+0(FP), AX
- MOVL y+4(FP), AX
- MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); \*byte is 4-byte value"
- MOVQ y+4(FP), AX // ERROR "invalid MOVQ of y\+4\(FP\); \*byte is 4-byte value"
- MOVQ x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
- MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
- TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); \*byte is 4-byte value"
- TESTB y+4(FP), BX // ERROR "invalid TESTB of y\+4\(FP\); \*byte is 4-byte value"
- TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); \*byte is 4-byte value"
- TESTW y+4(FP), AX // ERROR "invalid TESTW of y\+4\(FP\); \*byte is 4-byte value"
- TESTL x+0(FP), AX
- TESTL y+4(FP), AX
- TESTQ x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); \*byte is 4-byte value"
- TESTQ y+4(FP), AX // ERROR "invalid TESTQ of y\+4\(FP\); \*byte is 4-byte value"
- TESTQ x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
- TESTQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
- MOVW c+8(FP), AX // ERROR "invalid MOVW of c\+8\(FP\); chan int is 4-byte value"
- MOVW m+12(FP), AX // ERROR "invalid MOVW of m\+12\(FP\); map\[int\]int is 4-byte value"
- MOVW f+16(FP), AX // ERROR "invalid MOVW of f\+16\(FP\); func\(\) is 4-byte value"
- RET
-
-TEXT ·argstring(SB),0,$16 // ERROR "wrong argument size 0; expected \$\.\.\.-16"
- MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); string base is 4-byte value"
- MOVL x+0(FP), AX
- MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); string base is 4-byte value"
- MOVW x_base+0(FP), AX // ERROR "invalid MOVW of x_base\+0\(FP\); string base is 4-byte value"
- MOVL x_base+0(FP), AX
- MOVQ x_base+0(FP), AX // ERROR "invalid MOVQ of x_base\+0\(FP\); string base is 4-byte value"
- MOVW x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
- MOVL x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
- MOVQ x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
- MOVW x_len+4(FP), AX // ERROR "invalid MOVW of x_len\+4\(FP\); string len is 4-byte value"
- MOVL x_len+4(FP), AX
- MOVQ x_len+4(FP), AX // ERROR "invalid MOVQ of x_len\+4\(FP\); string len is 4-byte value"
- MOVQ y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+8\(FP\)"
- MOVQ y_len+4(FP), AX // ERROR "invalid offset y_len\+4\(FP\); expected y_len\+12\(FP\)"
- RET
-
-TEXT ·argslice(SB),0,$24 // ERROR "wrong argument size 0; expected \$\.\.\.-24"
- MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); slice base is 4-byte value"
- MOVL x+0(FP), AX
- MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); slice base is 4-byte value"
- MOVW x_base+0(FP), AX // ERROR "invalid MOVW of x_base\+0\(FP\); slice base is 4-byte value"
- MOVL x_base+0(FP), AX
- MOVQ x_base+0(FP), AX // ERROR "invalid MOVQ of x_base\+0\(FP\); slice base is 4-byte value"
- MOVW x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
- MOVL x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
- MOVQ x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
- MOVW x_len+4(FP), AX // ERROR "invalid MOVW of x_len\+4\(FP\); slice len is 4-byte value"
- MOVL x_len+4(FP), AX
- MOVQ x_len+4(FP), AX // ERROR "invalid MOVQ of x_len\+4\(FP\); slice len is 4-byte value"
- MOVW x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)"
- MOVL x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)"
- MOVQ x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)"
- MOVW x_cap+8(FP), AX // ERROR "invalid MOVW of x_cap\+8\(FP\); slice cap is 4-byte value"
- MOVL x_cap+8(FP), AX
- MOVQ x_cap+8(FP), AX // ERROR "invalid MOVQ of x_cap\+8\(FP\); slice cap is 4-byte value"
- MOVQ y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+12\(FP\)"
- MOVQ y_len+4(FP), AX // ERROR "invalid offset y_len\+4\(FP\); expected y_len\+16\(FP\)"
- MOVQ y_cap+8(FP), AX // ERROR "invalid offset y_cap\+8\(FP\); expected y_cap\+20\(FP\)"
- RET
-
-TEXT ·argiface(SB),0,$0-16
- MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); interface type is 4-byte value"
- MOVL x+0(FP), AX
- MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); interface type is 4-byte value"
- MOVW x_type+0(FP), AX // ERROR "invalid MOVW of x_type\+0\(FP\); interface type is 4-byte value"
- MOVL x_type+0(FP), AX
- MOVQ x_type+0(FP), AX // ERROR "invalid MOVQ of x_type\+0\(FP\); interface type is 4-byte value"
- MOVQ x_itable+0(FP), AX // ERROR "unknown variable x_itable; offset 0 is x_type\+0\(FP\)"
- MOVQ x_itable+1(FP), AX // ERROR "unknown variable x_itable; offset 1 is x_type\+0\(FP\)"
- MOVW x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)"
- MOVL x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)"
- MOVQ x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)"
- MOVW x_data+4(FP), AX // ERROR "invalid MOVW of x_data\+4\(FP\); interface data is 4-byte value"
- MOVL x_data+4(FP), AX
- MOVQ x_data+4(FP), AX // ERROR "invalid MOVQ of x_data\+4\(FP\); interface data is 4-byte value"
- MOVW y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); interface itable is 4-byte value"
- MOVL y+8(FP), AX
- MOVQ y+8(FP), AX // ERROR "invalid MOVQ of y\+8\(FP\); interface itable is 4-byte value"
- MOVW y_itable+8(FP), AX // ERROR "invalid MOVW of y_itable\+8\(FP\); interface itable is 4-byte value"
- MOVL y_itable+8(FP), AX
- MOVQ y_itable+8(FP), AX // ERROR "invalid MOVQ of y_itable\+8\(FP\); interface itable is 4-byte value"
- MOVQ y_type+8(FP), AX // ERROR "unknown variable y_type; offset 8 is y_itable\+8\(FP\)"
- MOVW y_data+8(FP), AX // ERROR "invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)"
- MOVL y_data+8(FP), AX // ERROR "invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)"
- MOVQ y_data+8(FP), AX // ERROR "invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)"
- MOVW y_data+12(FP), AX // ERROR "invalid MOVW of y_data\+12\(FP\); interface data is 4-byte value"
- MOVL y_data+12(FP), AX
- MOVQ y_data+12(FP), AX // ERROR "invalid MOVQ of y_data\+12\(FP\); interface data is 4-byte value"
- RET
-
-TEXT ·returnint(SB),0,$0-4
- MOVB AX, ret+0(FP) // ERROR "invalid MOVB of ret\+0\(FP\); int is 4-byte value"
- MOVW AX, ret+0(FP) // ERROR "invalid MOVW of ret\+0\(FP\); int is 4-byte value"
- MOVL AX, ret+0(FP)
- MOVQ AX, ret+0(FP) // ERROR "invalid MOVQ of ret\+0\(FP\); int is 4-byte value"
- MOVQ AX, ret+1(FP) // ERROR "invalid offset ret\+1\(FP\); expected ret\+0\(FP\)"
- MOVQ AX, r+0(FP) // ERROR "unknown variable r; offset 0 is ret\+0\(FP\)"
- RET
-
-TEXT ·returnbyte(SB),0,$0-5
- MOVL x+0(FP), AX
- MOVB AX, ret+4(FP)
- MOVW AX, ret+4(FP) // ERROR "invalid MOVW of ret\+4\(FP\); byte is 1-byte value"
- MOVL AX, ret+4(FP) // ERROR "invalid MOVL of ret\+4\(FP\); byte is 1-byte value"
- MOVQ AX, ret+4(FP) // ERROR "invalid MOVQ of ret\+4\(FP\); byte is 1-byte value"
- MOVB AX, ret+3(FP) // ERROR "invalid offset ret\+3\(FP\); expected ret\+4\(FP\)"
- RET
-
-TEXT ·returnnamed(SB),0,$0-21
- MOVB x+0(FP), AX
- MOVL AX, r1+4(FP)
- MOVW AX, r2+8(FP)
- MOVL AX, r3+12(FP)
- MOVL AX, r3_base+12(FP)
- MOVL AX, r3_len+16(FP)
- MOVB AX, r4+20(FP)
- MOVQ AX, r1+4(FP) // ERROR "invalid MOVQ of r1\+4\(FP\); int is 4-byte value"
- RET
diff --git a/src/cmd/vet/test_asm3.s b/src/cmd/vet/test_asm3.s
deleted file mode 100644
index bf98805a2..000000000
--- a/src/cmd/vet/test_asm3.s
+++ /dev/null
@@ -1,166 +0,0 @@
-// Copyright 2013 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.
-
-// +build arm
-// +build vet_test
-
-TEXT ·arg1(SB),0,$0-2
- MOVB x+0(FP), AX
- MOVB y+1(FP), BX
- MOVH x+0(FP), AX // ERROR "\[arm\] invalid MOVH of x\+0\(FP\); int8 is 1-byte value"
- MOVH y+1(FP), AX // ERROR "invalid MOVH of y\+1\(FP\); uint8 is 1-byte value"
- MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int8 is 1-byte value"
- MOVW y+1(FP), AX // ERROR "invalid MOVW of y\+1\(FP\); uint8 is 1-byte value"
- MOVB x+1(FP), AX // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)"
- MOVB y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)"
- RET
-
-TEXT ·arg2(SB),0,$0-4
- MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int16 is 2-byte value"
- MOVB y+2(FP), AX // ERROR "invalid MOVB of y\+2\(FP\); uint16 is 2-byte value"
- MOVH x+0(FP), AX
- MOVH y+2(FP), BX
- MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int16 is 2-byte value"
- MOVW y+2(FP), AX // ERROR "invalid MOVW of y\+2\(FP\); uint16 is 2-byte value"
- MOVH x+2(FP), AX // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)"
- MOVH y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+2\(FP\)"
- RET
-
-TEXT ·arg4(SB),0,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-8"
- MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int32 is 4-byte value"
- MOVB y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); uint32 is 4-byte value"
- MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); int32 is 4-byte value"
- MOVH y+4(FP), AX // ERROR "invalid MOVH of y\+4\(FP\); uint32 is 4-byte value"
- MOVW x+0(FP), AX
- MOVW y+4(FP), AX
- MOVW x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
- MOVW y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
- RET
-
-TEXT ·arg8(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-16"
- MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int64 is 8-byte value"
- MOVB y+8(FP), BX // ERROR "invalid MOVB of y\+8\(FP\); uint64 is 8-byte value"
- MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); int64 is 8-byte value"
- MOVH y+8(FP), AX // ERROR "invalid MOVH of y\+8\(FP\); uint64 is 8-byte value"
- MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int64 is 8-byte value containing x_lo\+0\(FP\) and x_hi\+4\(FP\)"
- MOVW x_lo+0(FP), AX
- MOVW x_hi+4(FP), AX
- MOVW y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); uint64 is 8-byte value containing y_lo\+8\(FP\) and y_hi\+12\(FP\)"
- MOVW y_lo+8(FP), AX
- MOVW y_hi+12(FP), AX
- MOVQ x+0(FP), AX
- MOVQ y+8(FP), AX
- MOVQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
- MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
- RET
-
-TEXT ·argint(SB),0,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-8"
- MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int is 4-byte value"
- MOVB y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); uint is 4-byte value"
- MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); int is 4-byte value"
- MOVH y+4(FP), AX // ERROR "invalid MOVH of y\+4\(FP\); uint is 4-byte value"
- MOVW x+0(FP), AX
- MOVW y+4(FP), AX
- MOVQ x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
- MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
- RET
-
-TEXT ·argptr(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-20"
- MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); \*byte is 4-byte value"
- MOVB y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); \*byte is 4-byte value"
- MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); \*byte is 4-byte value"
- MOVH y+4(FP), AX // ERROR "invalid MOVH of y\+4\(FP\); \*byte is 4-byte value"
- MOVW x+0(FP), AX
- MOVW y+4(FP), AX
- MOVQ x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
- MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
- MOVH c+8(FP), AX // ERROR "invalid MOVH of c\+8\(FP\); chan int is 4-byte value"
- MOVH m+12(FP), AX // ERROR "invalid MOVH of m\+12\(FP\); map\[int\]int is 4-byte value"
- MOVH f+16(FP), AX // ERROR "invalid MOVH of f\+16\(FP\); func\(\) is 4-byte value"
- RET
-
-TEXT ·argstring(SB),0,$16 // ERROR "wrong argument size 0; expected \$\.\.\.-16"
- MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); string base is 4-byte value"
- MOVW x+0(FP), AX
- MOVH x_base+0(FP), AX // ERROR "invalid MOVH of x_base\+0\(FP\); string base is 4-byte value"
- MOVW x_base+0(FP), AX
- MOVH x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
- MOVW x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
- MOVQ x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
- MOVH x_len+4(FP), AX // ERROR "invalid MOVH of x_len\+4\(FP\); string len is 4-byte value"
- MOVW x_len+4(FP), AX
- MOVQ y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+8\(FP\)"
- MOVQ y_len+4(FP), AX // ERROR "invalid offset y_len\+4\(FP\); expected y_len\+12\(FP\)"
- RET
-
-TEXT ·argslice(SB),0,$24 // ERROR "wrong argument size 0; expected \$\.\.\.-24"
- MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); slice base is 4-byte value"
- MOVW x+0(FP), AX
- MOVH x_base+0(FP), AX // ERROR "invalid MOVH of x_base\+0\(FP\); slice base is 4-byte value"
- MOVW x_base+0(FP), AX
- MOVH x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
- MOVW x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
- MOVQ x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
- MOVH x_len+4(FP), AX // ERROR "invalid MOVH of x_len\+4\(FP\); slice len is 4-byte value"
- MOVW x_len+4(FP), AX
- MOVH x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)"
- MOVW x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)"
- MOVQ x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)"
- MOVH x_cap+8(FP), AX // ERROR "invalid MOVH of x_cap\+8\(FP\); slice cap is 4-byte value"
- MOVW x_cap+8(FP), AX
- MOVQ y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+12\(FP\)"
- MOVQ y_len+4(FP), AX // ERROR "invalid offset y_len\+4\(FP\); expected y_len\+16\(FP\)"
- MOVQ y_cap+8(FP), AX // ERROR "invalid offset y_cap\+8\(FP\); expected y_cap\+20\(FP\)"
- RET
-
-TEXT ·argiface(SB),0,$0-16
- MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); interface type is 4-byte value"
- MOVW x+0(FP), AX
- MOVH x_type+0(FP), AX // ERROR "invalid MOVH of x_type\+0\(FP\); interface type is 4-byte value"
- MOVW x_type+0(FP), AX
- MOVQ x_itable+0(FP), AX // ERROR "unknown variable x_itable; offset 0 is x_type\+0\(FP\)"
- MOVQ x_itable+1(FP), AX // ERROR "unknown variable x_itable; offset 1 is x_type\+0\(FP\)"
- MOVH x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)"
- MOVW x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)"
- MOVQ x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)"
- MOVH x_data+4(FP), AX // ERROR "invalid MOVH of x_data\+4\(FP\); interface data is 4-byte value"
- MOVW x_data+4(FP), AX
- MOVH y+8(FP), AX // ERROR "invalid MOVH of y\+8\(FP\); interface itable is 4-byte value"
- MOVW y+8(FP), AX
- MOVH y_itable+8(FP), AX // ERROR "invalid MOVH of y_itable\+8\(FP\); interface itable is 4-byte value"
- MOVW y_itable+8(FP), AX
- MOVQ y_type+8(FP), AX // ERROR "unknown variable y_type; offset 8 is y_itable\+8\(FP\)"
- MOVH y_data+8(FP), AX // ERROR "invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)"
- MOVW y_data+8(FP), AX // ERROR "invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)"
- MOVQ y_data+8(FP), AX // ERROR "invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)"
- MOVH y_data+12(FP), AX // ERROR "invalid MOVH of y_data\+12\(FP\); interface data is 4-byte value"
- MOVW y_data+12(FP), AX
- RET
-
-TEXT ·returnint(SB),0,$0-4
- MOVB AX, ret+0(FP) // ERROR "invalid MOVB of ret\+0\(FP\); int is 4-byte value"
- MOVH AX, ret+0(FP) // ERROR "invalid MOVH of ret\+0\(FP\); int is 4-byte value"
- MOVW AX, ret+0(FP)
- MOVQ AX, ret+1(FP) // ERROR "invalid offset ret\+1\(FP\); expected ret\+0\(FP\)"
- MOVQ AX, r+0(FP) // ERROR "unknown variable r; offset 0 is ret\+0\(FP\)"
- RET
-
-TEXT ·returnbyte(SB),0,$0-5
- MOVW x+0(FP), AX
- MOVB AX, ret+4(FP)
- MOVH AX, ret+4(FP) // ERROR "invalid MOVH of ret\+4\(FP\); byte is 1-byte value"
- MOVW AX, ret+4(FP) // ERROR "invalid MOVW of ret\+4\(FP\); byte is 1-byte value"
- MOVB AX, ret+3(FP) // ERROR "invalid offset ret\+3\(FP\); expected ret\+4\(FP\)"
- RET
-
-TEXT ·returnnamed(SB),0,$0-21
- MOVB x+0(FP), AX
- MOVW AX, r1+4(FP)
- MOVH AX, r2+8(FP)
- MOVW AX, r3+12(FP)
- MOVW AX, r3_base+12(FP)
- MOVW AX, r3_len+16(FP)
- MOVB AX, r4+20(FP)
- MOVB AX, r1+4(FP) // ERROR "invalid MOVB of r1\+4\(FP\); int is 4-byte value"
- RET
diff --git a/src/cmd/vet/test_assign.go b/src/cmd/vet/test_assign.go
deleted file mode 100644
index 8e0f45e53..000000000
--- a/src/cmd/vet/test_assign.go
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This file contains tests for the useless-assignment checker.
-
-// +build vet_test
-
-package main
-
-type ST struct {
- x int
-}
-
-func (s *ST) SetX(x int) {
- // Accidental self-assignment; it should be "s.x = x"
- x = x // ERROR "self-assignment of x to x"
- // Another mistake
- s.x = s.x // ERROR "self-assignment of s.x to s.x"
-}
diff --git a/src/cmd/vet/test_atomic.go b/src/cmd/vet/test_atomic.go
deleted file mode 100644
index 9231e9dc0..000000000
--- a/src/cmd/vet/test_atomic.go
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2013 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.
-
-// +build vet_test
-
-// This file contains tests for the atomic checker.
-
-package main
-
-import (
- "sync/atomic"
-)
-
-type Counter uint64
-
-func AtomicTests() {
- x := uint64(1)
- x = atomic.AddUint64(&x, 1) // ERROR "direct assignment to atomic value"
- _, x = 10, atomic.AddUint64(&x, 1) // ERROR "direct assignment to atomic value"
- x, _ = atomic.AddUint64(&x, 1), 10 // ERROR "direct assignment to atomic value"
-
- y := &x
- *y = atomic.AddUint64(y, 1) // ERROR "direct assignment to atomic value"
-
- var su struct{ Counter uint64 }
- su.Counter = atomic.AddUint64(&su.Counter, 1) // ERROR "direct assignment to atomic value"
- z1 := atomic.AddUint64(&su.Counter, 1)
- _ = z1 // Avoid err "z declared and not used"
-
- var sp struct{ Counter *uint64 }
- *sp.Counter = atomic.AddUint64(sp.Counter, 1) // ERROR "direct assignment to atomic value"
- z2 := atomic.AddUint64(sp.Counter, 1)
- _ = z2 // Avoid err "z declared and not used"
-
- au := []uint64{10, 20}
- au[0] = atomic.AddUint64(&au[0], 1) // ERROR "direct assignment to atomic value"
- au[1] = atomic.AddUint64(&au[0], 1)
-
- ap := []*uint64{&au[0], &au[1]}
- *ap[0] = atomic.AddUint64(ap[0], 1) // ERROR "direct assignment to atomic value"
- *ap[1] = atomic.AddUint64(ap[0], 1)
-}
diff --git a/src/cmd/vet/test_buildtag.go b/src/cmd/vet/test_buildtag.go
deleted file mode 100644
index d7174ade2..000000000
--- a/src/cmd/vet/test_buildtag.go
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This file contains tests for the buildtag checker.
-
-// +build vet_test
-// +builder // ERROR "possible malformed \+build comment"
-// +build !ignore
-
-package main
-
-// +build toolate // ERROR "build comment appears too late in file"
-
-var _ = 3
diff --git a/src/cmd/vet/test_buildtag_bad.go b/src/cmd/vet/test_buildtag_bad.go
deleted file mode 100644
index 0a0a39bd1..000000000
--- a/src/cmd/vet/test_buildtag_bad.go
+++ /dev/null
@@ -1,15 +0,0 @@
-// This file contains misplaced or malformed build constraints.
-// The Go tool will skip it, because the constraints are invalid.
-// It serves only to test the tag checker during make test.
-
-// Mention +build // ERROR "possible malformed \+build comment"
-
-// +build !!bang // ERROR "invalid double negative in build constraint"
-// +build @#$ // ERROR "invalid non-alphanumeric build constraint"
-
-// +build toolate // ERROR "build comment appears too late in file"
-package bad
-
-// This is package 'bad' rather than 'main' so the erroneous build
-// tag doesn't end up looking like a package doc for the vet command
-// when examined by godoc.
diff --git a/src/cmd/vet/test_deadcode.go b/src/cmd/vet/test_deadcode.go
deleted file mode 100644
index d08e57782..000000000
--- a/src/cmd/vet/test_deadcode.go
+++ /dev/null
@@ -1,2121 +0,0 @@
-// Copyright 2013 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.
-
-// +build vet_test
-// +build ignore
-
-// This file contains tests for the dead code checker.
-
-package main
-
-type T int
-
-var x interface{}
-var c chan int
-
-func external() int // ok
-
-func _() int {
-}
-
-func _() int {
- print(1)
-}
-
-func _() int {
- print(1)
- return 2
- println() // ERROR "unreachable code"
-}
-
-func _() int {
-L:
- print(1)
- goto L
- println() // ERROR "unreachable code"
-}
-
-func _() int {
- print(1)
- panic(2)
- println() // ERROR "unreachable code"
-}
-
-// but only builtin panic
-func _() int {
- var panic = func(int) {}
- print(1)
- panic(2)
- println() // ok
-}
-
-func _() int {
- {
- print(1)
- return 2
- println() // ERROR "unreachable code"
- }
- println() // ok
-}
-
-func _() int {
- {
- print(1)
- return 2
- }
- println() // ERROR "unreachable code"
-}
-
-func _() int {
-L:
- {
- print(1)
- goto L
- println() // ERROR "unreachable code"
- }
- println() // ok
-}
-
-func _() int {
-L:
- {
- print(1)
- goto L
- }
- println() // ERROR "unreachable code"
-}
-
-func _() int {
- print(1)
- {
- panic(2)
- }
-}
-
-func _() int {
- print(1)
- {
- panic(2)
- println() // ERROR "unreachable code"
- }
-}
-
-func _() int {
- print(1)
- {
- panic(2)
- }
- println() // ERROR "unreachable code"
-}
-
-func _() int {
- print(1)
- return 2
- { // ERROR "unreachable code"
- }
-}
-
-func _() int {
-L:
- print(1)
- goto L
- { // ERROR "unreachable code"
- }
-}
-
-func _() int {
- print(1)
- panic(2)
- { // ERROR "unreachable code"
- }
-}
-
-func _() int {
- {
- print(1)
- return 2
- { // ERROR "unreachable code"
- }
- }
-}
-
-func _() int {
-L:
- {
- print(1)
- goto L
- { // ERROR "unreachable code"
- }
- }
-}
-
-func _() int {
- print(1)
- {
- panic(2)
- { // ERROR "unreachable code"
- }
- }
-}
-
-func _() int {
- {
- print(1)
- return 2
- }
- { // ERROR "unreachable code"
- }
-}
-
-func _() int {
-L:
- {
- print(1)
- goto L
- }
- { // ERROR "unreachable code"
- }
-}
-
-func _() int {
- print(1)
- {
- panic(2)
- }
- { // ERROR "unreachable code"
- }
-}
-
-func _() int {
- print(1)
- if x == nil {
- panic(2)
- } else {
- panic(3)
- }
- println() // ERROR "unreachable code"
-}
-
-func _() int {
-L:
- print(1)
- if x == nil {
- panic(2)
- } else {
- goto L
- }
- println() // ERROR "unreachable code"
-}
-
-func _() int {
-L:
- print(1)
- if x == nil {
- panic(2)
- } else if x == 1 {
- return 0
- } else if x != 2 {
- panic(3)
- } else {
- goto L
- }
- println() // ERROR "unreachable code"
-}
-
-// if-else chain missing final else is not okay, even if the
-// conditions cover every possible case.
-
-func _() int {
- print(1)
- if x == nil {
- panic(2)
- } else if x != nil {
- panic(3)
- }
- println() // ok
-}
-
-func _() int {
- print(1)
- if x == nil {
- panic(2)
- }
- println() // ok
-}
-
-func _() int {
-L:
- print(1)
- if x == nil {
- panic(2)
- } else if x == 1 {
- return 0
- } else if x != 1 {
- panic(3)
- }
- println() // ok
-}
-
-func _() int {
- print(1)
- for {
- }
- println() // ERROR "unreachable code"
-}
-
-func _() int {
- for {
- for {
- break
- }
- }
- println() // ERROR "unreachable code"
-}
-
-func _() int {
- for {
- for {
- break
- println() // ERROR "unreachable code"
- }
- }
-}
-
-func _() int {
- for {
- for {
- continue
- println() // ERROR "unreachable code"
- }
- }
-}
-
-func _() int {
- for {
- L:
- for {
- break L
- }
- }
- println() // ERROR "unreachable code"
-}
-
-func _() int {
- print(1)
- for {
- break
- }
- println() // ok
-}
-
-func _() int {
- for {
- for {
- }
- break // ERROR "unreachable code"
- }
- println() // ok
-}
-
-func _() int {
-L:
- for {
- for {
- break L
- }
- }
- println() // ok
-}
-
-func _() int {
- print(1)
- for x == nil {
- }
- println() // ok
-}
-
-func _() int {
- for x == nil {
- for {
- break
- }
- }
- println() // ok
-}
-
-func _() int {
- for x == nil {
- L:
- for {
- break L
- }
- }
- println() // ok
-}
-
-func _() int {
- print(1)
- for true {
- }
- println() // ok
-}
-
-func _() int {
- for true {
- for {
- break
- }
- }
- println() // ok
-}
-
-func _() int {
- for true {
- L:
- for {
- break L
- }
- }
- println() // ok
-}
-
-func _() int {
- print(1)
- select {}
- println() // ERROR "unreachable code"
-}
-
-func _() int {
- print(1)
- select {
- case <-c:
- print(2)
- panic("abc")
- println() // ERROR "unreachable code"
- }
-}
-
-func _() int {
- print(1)
- select {
- case <-c:
- print(2)
- panic("abc")
- }
- println() // ERROR "unreachable code"
-}
-
-func _() int {
- print(1)
- select {
- case <-c:
- print(2)
- for {
- }
- println() // ERROR "unreachable code"
- }
-}
-
-func _() int {
- print(1)
- select {
- case <-c:
- print(2)
- for {
- }
- }
- println() // ERROR "unreachable code"
-}
-
-func _() int {
-L:
- print(1)
- select {
- case <-c:
- print(2)
- panic("abc")
- println() // ERROR "unreachable code"
- case c <- 1:
- print(2)
- goto L
- println() // ERROR "unreachable code"
- }
-}
-
-func _() int {
-L:
- print(1)
- select {
- case <-c:
- print(2)
- panic("abc")
- case c <- 1:
- print(2)
- goto L
- }
- println() // ERROR "unreachable code"
-}
-
-func _() int {
- print(1)
- select {
- case <-c:
- print(2)
- panic("abc")
- println() // ERROR "unreachable code"
- default:
- select {}
- println() // ERROR "unreachable code"
- }
-}
-
-func _() int {
- print(1)
- select {
- case <-c:
- print(2)
- panic("abc")
- default:
- select {}
- }
- println() // ERROR "unreachable code"
-}
-
-func _() int {
- print(1)
- select {
- case <-c:
- print(2)
- }
- println() // ok
-}
-
-func _() int {
-L:
- print(1)
- select {
- case <-c:
- print(2)
- panic("abc")
- goto L // ERROR "unreachable code"
- case c <- 1:
- print(2)
- }
- println() // ok
-}
-
-func _() int {
- print(1)
- select {
- case <-c:
- print(2)
- panic("abc")
- default:
- print(2)
- }
- println() // ok
-}
-
-func _() int {
- print(1)
- select {
- default:
- break
- }
- println() // ok
-}
-
-func _() int {
- print(1)
- select {
- case <-c:
- print(2)
- panic("abc")
- break // ERROR "unreachable code"
- }
- println() // ok
-}
-
-func _() int {
- print(1)
-L:
- select {
- case <-c:
- print(2)
- for {
- break L
- }
- }
- println() // ok
-}
-
-func _() int {
- print(1)
-L:
- select {
- case <-c:
- print(2)
- panic("abc")
- case c <- 1:
- print(2)
- break L
- }
- println() // ok
-}
-
-func _() int {
- print(1)
- select {
- case <-c:
- print(1)
- panic("abc")
- default:
- select {}
- break // ERROR "unreachable code"
- }
- println() // ok
-}
-
-func _() int {
- print(1)
- switch x {
- case 1:
- print(2)
- panic(3)
- println() // ERROR "unreachable code"
- default:
- return 4
- println() // ERROR "unreachable code"
- }
-}
-
-func _() int {
- print(1)
- switch x {
- case 1:
- print(2)
- panic(3)
- default:
- return 4
- }
- println() // ERROR "unreachable code"
-}
-
-func _() int {
- print(1)
- switch x {
- default:
- return 4
- println() // ERROR "unreachable code"
- case 1:
- print(2)
- panic(3)
- println() // ERROR "unreachable code"
- }
-}
-
-func _() int {
- print(1)
- switch x {
- default:
- return 4
- case 1:
- print(2)
- panic(3)
- }
- println() // ERROR "unreachable code"
-}
-
-func _() int {
- print(1)
- switch x {
- case 1:
- print(2)
- fallthrough
- default:
- return 4
- println() // ERROR "unreachable code"
- }
-}
-
-func _() int {
- print(1)
- switch x {
- case 1:
- print(2)
- fallthrough
- default:
- return 4
- }
- println() // ERROR "unreachable code"
-}
-
-func _() int {
- print(1)
- switch {
- }
- println() // ok
-}
-
-func _() int {
- print(1)
- switch x {
- case 1:
- print(2)
- panic(3)
- case 2:
- return 4
- }
- println() // ok
-}
-
-func _() int {
- print(1)
- switch x {
- case 2:
- return 4
- case 1:
- print(2)
- panic(3)
- }
- println() // ok
-}
-
-func _() int {
- print(1)
- switch x {
- case 1:
- print(2)
- fallthrough
- case 2:
- return 4
- }
- println() // ok
-}
-
-func _() int {
- print(1)
- switch x {
- case 1:
- print(2)
- panic(3)
- }
- println() // ok
-}
-
-func _() int {
- print(1)
-L:
- switch x {
- case 1:
- print(2)
- panic(3)
- break L // ERROR "unreachable code"
- default:
- return 4
- }
- println() // ok
-}
-
-func _() int {
- print(1)
- switch x {
- default:
- return 4
- break // ERROR "unreachable code"
- case 1:
- print(2)
- panic(3)
- }
- println() // ok
-}
-
-func _() int {
- print(1)
-L:
- switch x {
- case 1:
- print(2)
- for {
- break L
- }
- default:
- return 4
- }
- println() // ok
-}
-
-func _() int {
- print(1)
- switch x.(type) {
- case int:
- print(2)
- panic(3)
- println() // ERROR "unreachable code"
- default:
- return 4
- println() // ERROR "unreachable code"
- }
-}
-
-func _() int {
- print(1)
- switch x.(type) {
- case int:
- print(2)
- panic(3)
- default:
- return 4
- }
- println() // ERROR "unreachable code"
-}
-
-func _() int {
- print(1)
- switch x.(type) {
- default:
- return 4
- println() // ERROR "unreachable code"
- case int:
- print(2)
- panic(3)
- println() // ERROR "unreachable code"
- }
-}
-
-func _() int {
- print(1)
- switch x.(type) {
- default:
- return 4
- case int:
- print(2)
- panic(3)
- }
- println() // ERROR "unreachable code"
-}
-
-func _() int {
- print(1)
- switch x.(type) {
- case int:
- print(2)
- fallthrough
- default:
- return 4
- println() // ERROR "unreachable code"
- }
-}
-
-func _() int {
- print(1)
- switch x.(type) {
- case int:
- print(2)
- fallthrough
- default:
- return 4
- }
- println() // ERROR "unreachable code"
-}
-
-func _() int {
- print(1)
- switch {
- }
- println() // ok
-}
-
-func _() int {
- print(1)
- switch x.(type) {
- case int:
- print(2)
- panic(3)
- case float64:
- return 4
- }
- println() // ok
-}
-
-func _() int {
- print(1)
- switch x.(type) {
- case float64:
- return 4
- case int:
- print(2)
- panic(3)
- }
- println() // ok
-}
-
-func _() int {
- print(1)
- switch x.(type) {
- case int:
- print(2)
- fallthrough
- case float64:
- return 4
- }
- println() // ok
-}
-
-func _() int {
- print(1)
- switch x.(type) {
- case int:
- print(2)
- panic(3)
- }
- println() // ok
-}
-
-func _() int {
- print(1)
-L:
- switch x.(type) {
- case int:
- print(2)
- panic(3)
- break L // ERROR "unreachable code"
- default:
- return 4
- }
- println() // ok
-}
-
-func _() int {
- print(1)
- switch x.(type) {
- default:
- return 4
- break // ERROR "unreachable code"
- case int:
- print(2)
- panic(3)
- }
- println() // ok
-}
-
-func _() int {
- print(1)
-L:
- switch x.(type) {
- case int:
- print(2)
- for {
- break L
- }
- default:
- return 4
- }
- println() // ok
-}
-
-// again, but without the leading print(1).
-// testing that everything works when the terminating statement is first.
-
-func _() int {
- println() // ok
-}
-
-func _() int {
- return 2
- println() // ERROR "unreachable code"
-}
-
-func _() int {
-L:
- goto L
- println() // ERROR "unreachable code"
-}
-
-func _() int {
- panic(2)
- println() // ERROR "unreachable code"
-}
-
-// but only builtin panic
-func _() int {
- var panic = func(int) {}
- panic(2)
- println() // ok
-}
-
-func _() int {
- {
- return 2
- println() // ERROR "unreachable code"
- }
-}
-
-func _() int {
- {
- return 2
- }
- println() // ERROR "unreachable code"
-}
-
-func _() int {
-L:
- {
- goto L
- println() // ERROR "unreachable code"
- }
-}
-
-func _() int {
-L:
- {
- goto L
- }
- println() // ERROR "unreachable code"
-}
-
-func _() int {
- {
- panic(2)
- println() // ERROR "unreachable code"
- }
-}
-
-func _() int {
- {
- panic(2)
- }
- println() // ERROR "unreachable code"
-}
-
-func _() int {
- return 2
- { // ERROR "unreachable code"
- }
- println() // ok
-}
-
-func _() int {
-L:
- goto L
- { // ERROR "unreachable code"
- }
- println() // ok
-}
-
-func _() int {
- panic(2)
- { // ERROR "unreachable code"
- }
- println() // ok
-}
-
-func _() int {
- {
- return 2
- { // ERROR "unreachable code"
- }
- }
- println() // ok
-}
-
-func _() int {
-L:
- {
- goto L
- { // ERROR "unreachable code"
- }
- }
- println() // ok
-}
-
-func _() int {
- {
- panic(2)
- { // ERROR "unreachable code"
- }
- }
- println() // ok
-}
-
-func _() int {
- {
- return 2
- }
- { // ERROR "unreachable code"
- }
- println() // ok
-}
-
-func _() int {
-L:
- {
- goto L
- }
- { // ERROR "unreachable code"
- }
- println() // ok
-}
-
-func _() int {
- {
- panic(2)
- }
- { // ERROR "unreachable code"
- }
- println() // ok
-}
-
-// again, with func literals
-
-var _ = func() int {
-}
-
-var _ = func() int {
- print(1)
-}
-
-var _ = func() int {
- print(1)
- return 2
- println() // ERROR "unreachable code"
-}
-
-var _ = func() int {
-L:
- print(1)
- goto L
- println() // ERROR "unreachable code"
-}
-
-var _ = func() int {
- print(1)
- panic(2)
- println() // ERROR "unreachable code"
-}
-
-// but only builtin panic
-var _ = func() int {
- var panic = func(int) {}
- print(1)
- panic(2)
- println() // ok
-}
-
-var _ = func() int {
- {
- print(1)
- return 2
- println() // ERROR "unreachable code"
- }
- println() // ok
-}
-
-var _ = func() int {
- {
- print(1)
- return 2
- }
- println() // ERROR "unreachable code"
-}
-
-var _ = func() int {
-L:
- {
- print(1)
- goto L
- println() // ERROR "unreachable code"
- }
- println() // ok
-}
-
-var _ = func() int {
-L:
- {
- print(1)
- goto L
- }
- println() // ERROR "unreachable code"
-}
-
-var _ = func() int {
- print(1)
- {
- panic(2)
- }
-}
-
-var _ = func() int {
- print(1)
- {
- panic(2)
- println() // ERROR "unreachable code"
- }
-}
-
-var _ = func() int {
- print(1)
- {
- panic(2)
- }
- println() // ERROR "unreachable code"
-}
-
-var _ = func() int {
- print(1)
- return 2
- { // ERROR "unreachable code"
- }
-}
-
-var _ = func() int {
-L:
- print(1)
- goto L
- { // ERROR "unreachable code"
- }
-}
-
-var _ = func() int {
- print(1)
- panic(2)
- { // ERROR "unreachable code"
- }
-}
-
-var _ = func() int {
- {
- print(1)
- return 2
- { // ERROR "unreachable code"
- }
- }
-}
-
-var _ = func() int {
-L:
- {
- print(1)
- goto L
- { // ERROR "unreachable code"
- }
- }
-}
-
-var _ = func() int {
- print(1)
- {
- panic(2)
- { // ERROR "unreachable code"
- }
- }
-}
-
-var _ = func() int {
- {
- print(1)
- return 2
- }
- { // ERROR "unreachable code"
- }
-}
-
-var _ = func() int {
-L:
- {
- print(1)
- goto L
- }
- { // ERROR "unreachable code"
- }
-}
-
-var _ = func() int {
- print(1)
- {
- panic(2)
- }
- { // ERROR "unreachable code"
- }
-}
-
-var _ = func() int {
- print(1)
- if x == nil {
- panic(2)
- } else {
- panic(3)
- }
- println() // ERROR "unreachable code"
-}
-
-var _ = func() int {
-L:
- print(1)
- if x == nil {
- panic(2)
- } else {
- goto L
- }
- println() // ERROR "unreachable code"
-}
-
-var _ = func() int {
-L:
- print(1)
- if x == nil {
- panic(2)
- } else if x == 1 {
- return 0
- } else if x != 2 {
- panic(3)
- } else {
- goto L
- }
- println() // ERROR "unreachable code"
-}
-
-// if-else chain missing final else is not okay, even if the
-// conditions cover every possible case.
-
-var _ = func() int {
- print(1)
- if x == nil {
- panic(2)
- } else if x != nil {
- panic(3)
- }
- println() // ok
-}
-
-var _ = func() int {
- print(1)
- if x == nil {
- panic(2)
- }
- println() // ok
-}
-
-var _ = func() int {
-L:
- print(1)
- if x == nil {
- panic(2)
- } else if x == 1 {
- return 0
- } else if x != 1 {
- panic(3)
- }
- println() // ok
-}
-
-var _ = func() int {
- print(1)
- for {
- }
- println() // ERROR "unreachable code"
-}
-
-var _ = func() int {
- for {
- for {
- break
- }
- }
- println() // ERROR "unreachable code"
-}
-
-var _ = func() int {
- for {
- for {
- break
- println() // ERROR "unreachable code"
- }
- }
-}
-
-var _ = func() int {
- for {
- for {
- continue
- println() // ERROR "unreachable code"
- }
- }
-}
-
-var _ = func() int {
- for {
- L:
- for {
- break L
- }
- }
- println() // ERROR "unreachable code"
-}
-
-var _ = func() int {
- print(1)
- for {
- break
- }
- println() // ok
-}
-
-var _ = func() int {
- for {
- for {
- }
- break // ERROR "unreachable code"
- }
- println() // ok
-}
-
-var _ = func() int {
-L:
- for {
- for {
- break L
- }
- }
- println() // ok
-}
-
-var _ = func() int {
- print(1)
- for x == nil {
- }
- println() // ok
-}
-
-var _ = func() int {
- for x == nil {
- for {
- break
- }
- }
- println() // ok
-}
-
-var _ = func() int {
- for x == nil {
- L:
- for {
- break L
- }
- }
- println() // ok
-}
-
-var _ = func() int {
- print(1)
- for true {
- }
- println() // ok
-}
-
-var _ = func() int {
- for true {
- for {
- break
- }
- }
- println() // ok
-}
-
-var _ = func() int {
- for true {
- L:
- for {
- break L
- }
- }
- println() // ok
-}
-
-var _ = func() int {
- print(1)
- select {}
- println() // ERROR "unreachable code"
-}
-
-var _ = func() int {
- print(1)
- select {
- case <-c:
- print(2)
- panic("abc")
- println() // ERROR "unreachable code"
- }
-}
-
-var _ = func() int {
- print(1)
- select {
- case <-c:
- print(2)
- panic("abc")
- }
- println() // ERROR "unreachable code"
-}
-
-var _ = func() int {
- print(1)
- select {
- case <-c:
- print(2)
- for {
- }
- println() // ERROR "unreachable code"
- }
-}
-
-var _ = func() int {
- print(1)
- select {
- case <-c:
- print(2)
- for {
- }
- }
- println() // ERROR "unreachable code"
-}
-
-var _ = func() int {
-L:
- print(1)
- select {
- case <-c:
- print(2)
- panic("abc")
- println() // ERROR "unreachable code"
- case c <- 1:
- print(2)
- goto L
- println() // ERROR "unreachable code"
- }
-}
-
-var _ = func() int {
-L:
- print(1)
- select {
- case <-c:
- print(2)
- panic("abc")
- case c <- 1:
- print(2)
- goto L
- }
- println() // ERROR "unreachable code"
-}
-
-var _ = func() int {
- print(1)
- select {
- case <-c:
- print(2)
- panic("abc")
- println() // ERROR "unreachable code"
- default:
- select {}
- println() // ERROR "unreachable code"
- }
-}
-
-var _ = func() int {
- print(1)
- select {
- case <-c:
- print(2)
- panic("abc")
- default:
- select {}
- }
- println() // ERROR "unreachable code"
-}
-
-var _ = func() int {
- print(1)
- select {
- case <-c:
- print(2)
- }
- println() // ok
-}
-
-var _ = func() int {
-L:
- print(1)
- select {
- case <-c:
- print(2)
- panic("abc")
- goto L // ERROR "unreachable code"
- case c <- 1:
- print(2)
- }
- println() // ok
-}
-
-var _ = func() int {
- print(1)
- select {
- case <-c:
- print(2)
- panic("abc")
- default:
- print(2)
- }
- println() // ok
-}
-
-var _ = func() int {
- print(1)
- select {
- default:
- break
- }
- println() // ok
-}
-
-var _ = func() int {
- print(1)
- select {
- case <-c:
- print(2)
- panic("abc")
- break // ERROR "unreachable code"
- }
- println() // ok
-}
-
-var _ = func() int {
- print(1)
-L:
- select {
- case <-c:
- print(2)
- for {
- break L
- }
- }
- println() // ok
-}
-
-var _ = func() int {
- print(1)
-L:
- select {
- case <-c:
- print(2)
- panic("abc")
- case c <- 1:
- print(2)
- break L
- }
- println() // ok
-}
-
-var _ = func() int {
- print(1)
- select {
- case <-c:
- print(1)
- panic("abc")
- default:
- select {}
- break // ERROR "unreachable code"
- }
- println() // ok
-}
-
-var _ = func() int {
- print(1)
- switch x {
- case 1:
- print(2)
- panic(3)
- println() // ERROR "unreachable code"
- default:
- return 4
- println() // ERROR "unreachable code"
- }
-}
-
-var _ = func() int {
- print(1)
- switch x {
- case 1:
- print(2)
- panic(3)
- default:
- return 4
- }
- println() // ERROR "unreachable code"
-}
-
-var _ = func() int {
- print(1)
- switch x {
- default:
- return 4
- println() // ERROR "unreachable code"
- case 1:
- print(2)
- panic(3)
- println() // ERROR "unreachable code"
- }
-}
-
-var _ = func() int {
- print(1)
- switch x {
- default:
- return 4
- case 1:
- print(2)
- panic(3)
- }
- println() // ERROR "unreachable code"
-}
-
-var _ = func() int {
- print(1)
- switch x {
- case 1:
- print(2)
- fallthrough
- default:
- return 4
- println() // ERROR "unreachable code"
- }
-}
-
-var _ = func() int {
- print(1)
- switch x {
- case 1:
- print(2)
- fallthrough
- default:
- return 4
- }
- println() // ERROR "unreachable code"
-}
-
-var _ = func() int {
- print(1)
- switch {
- }
- println() // ok
-}
-
-var _ = func() int {
- print(1)
- switch x {
- case 1:
- print(2)
- panic(3)
- case 2:
- return 4
- }
- println() // ok
-}
-
-var _ = func() int {
- print(1)
- switch x {
- case 2:
- return 4
- case 1:
- print(2)
- panic(3)
- }
- println() // ok
-}
-
-var _ = func() int {
- print(1)
- switch x {
- case 1:
- print(2)
- fallthrough
- case 2:
- return 4
- }
- println() // ok
-}
-
-var _ = func() int {
- print(1)
- switch x {
- case 1:
- print(2)
- panic(3)
- }
- println() // ok
-}
-
-var _ = func() int {
- print(1)
-L:
- switch x {
- case 1:
- print(2)
- panic(3)
- break L // ERROR "unreachable code"
- default:
- return 4
- }
- println() // ok
-}
-
-var _ = func() int {
- print(1)
- switch x {
- default:
- return 4
- break // ERROR "unreachable code"
- case 1:
- print(2)
- panic(3)
- }
- println() // ok
-}
-
-var _ = func() int {
- print(1)
-L:
- switch x {
- case 1:
- print(2)
- for {
- break L
- }
- default:
- return 4
- }
- println() // ok
-}
-
-var _ = func() int {
- print(1)
- switch x.(type) {
- case int:
- print(2)
- panic(3)
- println() // ERROR "unreachable code"
- default:
- return 4
- println() // ERROR "unreachable code"
- }
-}
-
-var _ = func() int {
- print(1)
- switch x.(type) {
- case int:
- print(2)
- panic(3)
- default:
- return 4
- }
- println() // ERROR "unreachable code"
-}
-
-var _ = func() int {
- print(1)
- switch x.(type) {
- default:
- return 4
- println() // ERROR "unreachable code"
- case int:
- print(2)
- panic(3)
- println() // ERROR "unreachable code"
- }
-}
-
-var _ = func() int {
- print(1)
- switch x.(type) {
- default:
- return 4
- case int:
- print(2)
- panic(3)
- }
- println() // ERROR "unreachable code"
-}
-
-var _ = func() int {
- print(1)
- switch x.(type) {
- case int:
- print(2)
- fallthrough
- default:
- return 4
- println() // ERROR "unreachable code"
- }
-}
-
-var _ = func() int {
- print(1)
- switch x.(type) {
- case int:
- print(2)
- fallthrough
- default:
- return 4
- }
- println() // ERROR "unreachable code"
-}
-
-var _ = func() int {
- print(1)
- switch {
- }
- println() // ok
-}
-
-var _ = func() int {
- print(1)
- switch x.(type) {
- case int:
- print(2)
- panic(3)
- case float64:
- return 4
- }
- println() // ok
-}
-
-var _ = func() int {
- print(1)
- switch x.(type) {
- case float64:
- return 4
- case int:
- print(2)
- panic(3)
- }
- println() // ok
-}
-
-var _ = func() int {
- print(1)
- switch x.(type) {
- case int:
- print(2)
- fallthrough
- case float64:
- return 4
- }
- println() // ok
-}
-
-var _ = func() int {
- print(1)
- switch x.(type) {
- case int:
- print(2)
- panic(3)
- }
- println() // ok
-}
-
-var _ = func() int {
- print(1)
-L:
- switch x.(type) {
- case int:
- print(2)
- panic(3)
- break L // ERROR "unreachable code"
- default:
- return 4
- }
- println() // ok
-}
-
-var _ = func() int {
- print(1)
- switch x.(type) {
- default:
- return 4
- break // ERROR "unreachable code"
- case int:
- print(2)
- panic(3)
- }
- println() // ok
-}
-
-var _ = func() int {
- print(1)
-L:
- switch x.(type) {
- case int:
- print(2)
- for {
- break L
- }
- default:
- return 4
- }
- println() // ok
-}
-
-// again, but without the leading print(1).
-// testing that everything works when the terminating statement is first.
-
-var _ = func() int {
- println() // ok
-}
-
-var _ = func() int {
- return 2
- println() // ERROR "unreachable code"
-}
-
-var _ = func() int {
-L:
- goto L
- println() // ERROR "unreachable code"
-}
-
-var _ = func() int {
- panic(2)
- println() // ERROR "unreachable code"
-}
-
-// but only builtin panic
-var _ = func() int {
- var panic = func(int) {}
- panic(2)
- println() // ok
-}
-
-var _ = func() int {
- {
- return 2
- println() // ERROR "unreachable code"
- }
-}
-
-var _ = func() int {
- {
- return 2
- }
- println() // ERROR "unreachable code"
-}
-
-var _ = func() int {
-L:
- {
- goto L
- println() // ERROR "unreachable code"
- }
-}
-
-var _ = func() int {
-L:
- {
- goto L
- }
- println() // ERROR "unreachable code"
-}
-
-var _ = func() int {
- {
- panic(2)
- println() // ERROR "unreachable code"
- }
-}
-
-var _ = func() int {
- {
- panic(2)
- }
- println() // ERROR "unreachable code"
-}
-
-var _ = func() int {
- return 2
- { // ERROR "unreachable code"
- }
- println() // ok
-}
-
-var _ = func() int {
-L:
- goto L
- { // ERROR "unreachable code"
- }
- println() // ok
-}
-
-var _ = func() int {
- panic(2)
- { // ERROR "unreachable code"
- }
- println() // ok
-}
-
-var _ = func() int {
- {
- return 2
- { // ERROR "unreachable code"
- }
- }
- println() // ok
-}
-
-var _ = func() int {
-L:
- {
- goto L
- { // ERROR "unreachable code"
- }
- }
- println() // ok
-}
-
-var _ = func() int {
- {
- panic(2)
- { // ERROR "unreachable code"
- }
- }
- println() // ok
-}
-
-var _ = func() int {
- {
- return 2
- }
- { // ERROR "unreachable code"
- }
- println() // ok
-}
-
-var _ = func() int {
-L:
- {
- goto L
- }
- { // ERROR "unreachable code"
- }
- println() // ok
-}
-
-var _ = func() int {
- {
- panic(2)
- }
- { // ERROR "unreachable code"
- }
- println() // ok
-}
diff --git a/src/cmd/vet/test_method.go b/src/cmd/vet/test_method.go
deleted file mode 100644
index 41de62bb1..000000000
--- a/src/cmd/vet/test_method.go
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This file contains tests for the canonical method checker.
-
-// +build vet_test
-
-// This file contains the code to check canonical methods.
-
-package main
-
-import (
- "fmt"
-)
-
-type MethodTest int
-
-func (t *MethodTest) Scan(x fmt.ScanState, c byte) { // ERROR "should have signature Scan"
-}
-
-type MethodTestInterface interface {
- ReadByte() byte // ERROR "should have signature ReadByte"
-}
diff --git a/src/cmd/vet/test_print.go b/src/cmd/vet/test_print.go
deleted file mode 100644
index 8b41e6c69..000000000
--- a/src/cmd/vet/test_print.go
+++ /dev/null
@@ -1,153 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build vet_test
-
-// This file contains tests for the printf checker.
-
-package main
-
-import (
- "fmt"
- "unsafe" // just for test case printing unsafe.Pointer
-)
-
-func UnsafePointerPrintfTest() {
- var up unsafe.Pointer
- fmt.Printf("%p, %x %X", up, up, up)
-}
-
-// Error methods that do not satisfy the Error interface and should be checked.
-type errorTest1 int
-
-func (errorTest1) Error(...interface{}) string {
- return "hi"
-}
-
-type errorTest2 int // Analogous to testing's *T type.
-func (errorTest2) Error(...interface{}) {
-}
-
-type errorTest3 int
-
-func (errorTest3) Error() { // No return value.
-}
-
-type errorTest4 int
-
-func (errorTest4) Error() int { // Different return type.
- return 3
-}
-
-type errorTest5 int
-
-func (errorTest5) error() { // niladic; don't complain if no args (was bug)
-}
-
-// This function never executes, but it serves as a simple test for the program.
-// Test with make test.
-func PrintfTests() {
- var b bool
- var i int
- var r rune
- var s string
- var x float64
- var p *int
- // Some good format/argtypes
- fmt.Printf("")
- fmt.Printf("%b %b", 3, i)
- fmt.Printf("%c %c %c %c", 3, i, 'x', r)
- fmt.Printf("%d %d", 3, i)
- fmt.Printf("%e %e", 3e9, x)
- fmt.Printf("%E %E", 3e9, x)
- fmt.Printf("%f %f", 3e9, x)
- fmt.Printf("%F %F", 3e9, x)
- fmt.Printf("%g %g", 3e9, x)
- fmt.Printf("%G %G", 3e9, x)
- fmt.Printf("%o %o", 3, i)
- fmt.Printf("%p %p", p, nil)
- fmt.Printf("%q %q %q %q", 3, i, 'x', r)
- fmt.Printf("%s %s", "hi", s)
- fmt.Printf("%t %t", true, b)
- fmt.Printf("%T %T", 3, i)
- fmt.Printf("%U %U", 3, i)
- fmt.Printf("%v %v", 3, i)
- fmt.Printf("%x %x %x %x", 3, i, "hi", s)
- fmt.Printf("%X %X %X %X", 3, i, "hi", s)
- fmt.Printf("%.*s %d %g", 3, "hi", 23, 2.3)
- // Some bad format/argTypes
- fmt.Printf("%b", "hi") // ERROR "arg .hi. for printf verb %b of wrong type"
- fmt.Printf("%c", 2.3) // ERROR "arg 2.3 for printf verb %c of wrong type"
- fmt.Printf("%d", 2.3) // ERROR "arg 2.3 for printf verb %d of wrong type"
- fmt.Printf("%e", "hi") // ERROR "arg .hi. for printf verb %e of wrong type"
- fmt.Printf("%E", true) // ERROR "arg true for printf verb %E of wrong type"
- fmt.Printf("%f", "hi") // ERROR "arg .hi. for printf verb %f of wrong type"
- fmt.Printf("%F", 'x') // ERROR "arg 'x' for printf verb %F of wrong type"
- fmt.Printf("%g", "hi") // ERROR "arg .hi. for printf verb %g of wrong type"
- fmt.Printf("%G", i) // ERROR "arg i for printf verb %G of wrong type"
- fmt.Printf("%o", x) // ERROR "arg x for printf verb %o of wrong type"
- fmt.Printf("%p", 23) // ERROR "arg 23 for printf verb %p of wrong type"
- fmt.Printf("%q", x) // ERROR "arg x for printf verb %q of wrong type"
- fmt.Printf("%s", b) // ERROR "arg b for printf verb %s of wrong type"
- fmt.Printf("%t", 23) // ERROR "arg 23 for printf verb %t of wrong type"
- fmt.Printf("%U", x) // ERROR "arg x for printf verb %U of wrong type"
- fmt.Printf("%x", nil) // ERROR "arg nil for printf verb %x of wrong type"
- fmt.Printf("%X", 2.3) // ERROR "arg 2.3 for printf verb %X of wrong type"
- fmt.Printf("%.*s %d %g", 3, "hi", 23, 'x') // ERROR "arg 'x' for printf verb %g of wrong type"
- // TODO
- fmt.Println() // not an error
- fmt.Println("%s", "hi") // ERROR "possible formatting directive in Println call"
- fmt.Printf("%s", "hi", 3) // ERROR "wrong number of args for format in Printf call"
- fmt.Printf("%"+("s"), "hi", 3) // ERROR "wrong number of args for format in Printf call"
- fmt.Printf("%s%%%d", "hi", 3) // correct
- fmt.Printf("%08s", "woo") // correct
- fmt.Printf("% 8s", "woo") // correct
- fmt.Printf("%.*d", 3, 3) // correct
- fmt.Printf("%.*d", 3, 3, 3) // ERROR "wrong number of args for format in Printf call"
- fmt.Printf("%.*d", "hi", 3) // ERROR "arg .hi. for \* in printf format not of type int"
- fmt.Printf("%.*d", i, 3) // correct
- fmt.Printf("%.*d", s, 3) // ERROR "arg s for \* in printf format not of type int"
- fmt.Printf("%q %q", multi()...) // ok
- fmt.Printf("%#q", `blah`) // ok
- printf("now is the time", "buddy") // ERROR "no formatting directive"
- Printf("now is the time", "buddy") // ERROR "no formatting directive"
- Printf("hi") // ok
- const format = "%s %s\n"
- Printf(format, "hi", "there")
- Printf(format, "hi") // ERROR "wrong number of args for format in Printf call"
- f := new(File)
- f.Warn(0, "%s", "hello", 3) // ERROR "possible formatting directive in Warn call"
- f.Warnf(0, "%s", "hello", 3) // ERROR "wrong number of args for format in Warnf call"
- f.Warnf(0, "%r", "hello") // ERROR "unrecognized printf verb"
- f.Warnf(0, "%#s", "hello") // ERROR "unrecognized printf flag"
- // Something that satisfies the error interface.
- var e error
- fmt.Println(e.Error()) // ok
- // Something that looks like an error interface but isn't, such as the (*T).Error method
- // in the testing package.
- var et1 errorTest1
- fmt.Println(et1.Error()) // ERROR "no args in Error call"
- fmt.Println(et1.Error("hi")) // ok
- fmt.Println(et1.Error("%d", 3)) // ERROR "possible formatting directive in Error call"
- var et2 errorTest2
- et2.Error() // ERROR "no args in Error call"
- et2.Error("hi") // ok, not an error method.
- et2.Error("%d", 3) // ERROR "possible formatting directive in Error call"
- var et3 errorTest3
- et3.Error() // ok, not an error method.
- var et4 errorTest4
- et4.Error() // ok, not an error method.
- var et5 errorTest5
- et5.error() // ok, not an error method.
-}
-
-// printf is used by the test.
-func printf(format string, args ...interface{}) {
- panic("don't call - testing only")
-}
-
-// multi is used by the test.
-func multi() []interface{} {
- panic("don't call - testing only")
-}
diff --git a/src/cmd/vet/test_rangeloop.go b/src/cmd/vet/test_rangeloop.go
deleted file mode 100644
index 941fd72aa..000000000
--- a/src/cmd/vet/test_rangeloop.go
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2012 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This file contains tests for the rangeloop checker.
-
-// +build vet_test
-
-package main
-
-func RangeLoopTests() {
- var s []int
- for i, v := range s {
- go func() {
- println(i) // ERROR "range variable i enclosed by function"
- println(v) // ERROR "range variable v enclosed by function"
- }()
- }
- for i, v := range s {
- defer func() {
- println(i) // ERROR "range variable i enclosed by function"
- println(v) // ERROR "range variable v enclosed by function"
- }()
- }
- for i := range s {
- go func() {
- println(i) // ERROR "range variable i enclosed by function"
- }()
- }
- for _, v := range s {
- go func() {
- println(v) // ERROR "range variable v enclosed by function"
- }()
- }
- for i, v := range s {
- go func() {
- println(i, v)
- }()
- println("unfortunately, we don't catch the error above because of this statement")
- }
- for i, v := range s {
- go func(i, v int) {
- println(i, v)
- }(i, v)
- }
- for i, v := range s {
- i, v := i, v
- go func() {
- println(i, v)
- }()
- }
- // If the key of the range statement is not an identifier
- // the code should not panic (it used to).
- var x [2]int
- var f int
- for x[0], f = range s {
- go func() {
- _ = f // ERROR "range variable f enclosed by function"
- }()
- }
-}
diff --git a/src/cmd/vet/test_structtag.go b/src/cmd/vet/test_structtag.go
deleted file mode 100644
index 08cf737fd..000000000
--- a/src/cmd/vet/test_structtag.go
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This file contains tests for the structtag checker.
-
-// +build vet_test
-
-// This file contains the test for canonical struct tags.
-
-package main
-
-type StructTagTest struct {
- X int "hello" // ERROR "not compatible with reflect.StructTag.Get"
-}
diff --git a/src/cmd/vet/test_taglit.go b/src/cmd/vet/test_taglit.go
deleted file mode 100644
index f34062f18..000000000
--- a/src/cmd/vet/test_taglit.go
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2012 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This file contains tests for the untagged struct literal checker.
-
-// +build vet_test
-
-// This file contains the test for untagged struct literals.
-
-package main
-
-import (
- "flag"
- "go/scanner"
-)
-
-var Okay1 = []string{
- "Name",
- "Usage",
- "DefValue",
-}
-
-var Okay2 = map[string]bool{
- "Name": true,
- "Usage": true,
- "DefValue": true,
-}
-
-var Okay3 = struct {
- X string
- Y string
- Z string
-}{
- "Name",
- "Usage",
- "DefValue",
-}
-
-type MyStruct struct {
- X string
- Y string
- Z string
-}
-
-var Okay4 = MyStruct{
- "Name",
- "Usage",
- "DefValue",
-}
-
-// Testing is awkward because we need to reference things from a separate package
-// to trigger the warnings.
-
-var BadStructLiteralUsedInTests = flag.Flag{ // ERROR "untagged fields"
- "Name",
- "Usage",
- nil, // Value
- "DefValue",
-}
-
-// Used to test the check for slices and arrays: If that test is disabled and
-// vet is run with --compositewhitelist=false, this line triggers an error.
-// Clumsy but sufficient.
-var scannerErrorListTest = scanner.ErrorList{nil, nil}
diff --git a/src/cmd/vet/types.go b/src/cmd/vet/types.go
deleted file mode 100644
index 75f195b0f..000000000
--- a/src/cmd/vet/types.go
+++ /dev/null
@@ -1,179 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build gotypes
-
-// This file contains the pieces of the tool that require the go/types package.
-// To compile this file, you must first run
-// $ go get code.google.com/p/go.exp/go/types
-
-package main
-
-import (
- "go/ast"
- "go/token"
-
- "code.google.com/p/go.exp/go/types"
-)
-
-// Type is equivalent to go/types.Type. Repeating it here allows us to avoid
-// depending on the go/types package.
-type Type interface {
- String() string
-}
-
-func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) error {
- pkg.types = make(map[ast.Expr]Type)
- pkg.values = make(map[ast.Expr]interface{})
- exprFn := func(x ast.Expr, typ types.Type, val interface{}) {
- pkg.types[x] = typ
- if val != nil {
- pkg.values[x] = val
- }
- }
- // By providing the Context with our own error function, it will continue
- // past the first error. There is no need for that function to do anything.
- context := types.Context{
- Expr: exprFn,
- Error: func(error) {},
- }
- _, err := context.Check(fs, astFiles)
- return err
-}
-
-// isStruct reports whether the composite literal c is a struct.
-// If it is not (probably a struct), it returns a printable form of the type.
-func (pkg *Package) isStruct(c *ast.CompositeLit) (bool, string) {
- // Check that the CompositeLit's type is a slice or array (which needs no tag), if possible.
- typ := pkg.types[c]
- // If it's a named type, pull out the underlying type.
- actual := typ
- if namedType, ok := typ.(*types.NamedType); ok {
- actual = namedType.Underlying
- }
- if actual == nil {
- // No type information available. Assume true, so we do the check.
- return true, ""
- }
- switch actual.(type) {
- case *types.Struct:
- return true, typ.String()
- default:
- return false, ""
- }
-}
-
-func (f *File) matchArgType(t printfArgType, arg ast.Expr) bool {
- // TODO: for now, we can only test builtin types and untyped constants.
- typ := f.pkg.types[arg]
- if typ == nil {
- return true
- }
- basic, ok := typ.(*types.Basic)
- if !ok {
- return true
- }
- switch basic.Kind {
- case types.Bool:
- return t&argBool != 0
- case types.Int, types.Int8, types.Int16, types.Int32, types.Int64:
- fallthrough
- case types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uint64, types.Uintptr:
- return t&argInt != 0
- case types.Float32, types.Float64, types.Complex64, types.Complex128:
- return t&argFloat != 0
- case types.String:
- return t&argString != 0
- case types.UnsafePointer:
- return t&(argPointer|argInt) != 0
- case types.UntypedBool:
- return t&argBool != 0
- case types.UntypedComplex:
- return t&argFloat != 0
- case types.UntypedFloat:
- // If it's integral, we can use an int format.
- switch f.pkg.values[arg].(type) {
- case int, int8, int16, int32, int64:
- return t&(argInt|argFloat) != 0
- case uint, uint8, uint16, uint32, uint64:
- return t&(argInt|argFloat) != 0
- }
- return t&argFloat != 0
- case types.UntypedInt:
- return t&argInt != 0
- case types.UntypedRune:
- return t&(argInt|argRune) != 0
- case types.UntypedString:
- return t&argString != 0
- case types.UntypedNil:
- return t&argPointer != 0 // TODO?
- case types.Invalid:
- if *verbose {
- f.Warnf(arg.Pos(), "printf argument %v has invalid or unknown type", arg)
- }
- return true // Probably a type check problem.
- }
- return false
-}
-
-// numArgsInSignature tells how many formal arguments the function type
-// being called has.
-func (f *File) numArgsInSignature(call *ast.CallExpr) int {
- // Check the type of the function or method declaration
- typ := f.pkg.types[call.Fun]
- if typ == nil {
- return 0
- }
- // The type must be a signature, but be sure for safety.
- sig, ok := typ.(*types.Signature)
- if !ok {
- return 0
- }
- return len(sig.Params)
-}
-
-// isErrorMethodCall reports whether the call is of a method with signature
-// func Error() string
-// where "string" is the universe's string type. We know the method is called "Error".
-func (f *File) isErrorMethodCall(call *ast.CallExpr) bool {
- // Is it a selector expression? Otherwise it's a function call, not a method call.
- sel, ok := call.Fun.(*ast.SelectorExpr)
- if !ok {
- return false
- }
- // The package is type-checked, so if there are no arguments, we're done.
- if len(call.Args) > 0 {
- return false
- }
- // Check the type of the method declaration
- typ := f.pkg.types[sel]
- if typ == nil {
- return false
- }
- // The type must be a signature, but be sure for safety.
- sig, ok := typ.(*types.Signature)
- if !ok {
- return false
- }
- // There must be a receiver for it to be a method call. Otherwise it is
- // a function, not something that satisfies the error interface.
- if sig.Recv == nil {
- return false
- }
- // There must be no arguments. Already verified by type checking, but be thorough.
- if len(sig.Params) > 0 {
- return false
- }
- // Finally the real questions.
- // There must be one result.
- if len(sig.Results) != 1 {
- return false
- }
- // It must have return type "string" from the universe.
- result := sig.Results[0].Type
- if types.IsIdentical(result, types.Typ[types.String]) {
- return true
- }
- return false
-}
diff --git a/src/cmd/vet/typestub.go b/src/cmd/vet/typestub.go
deleted file mode 100644
index fabbbe19d..000000000
--- a/src/cmd/vet/typestub.go
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build !gotypes
-
-// This file contains stubs for the pieces of the tool that require the go/types package,
-// to be used if go/types is not available.
-
-package main
-
-import (
- "go/ast"
- "go/token"
-)
-
-// Type is equivalent to go/types.Type. Repeating it here allows us to avoid
-// depending on the go/types package.
-type Type interface {
- String() string
-}
-
-func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) error {
- return nil
-}
-
-func (pkg *Package) isStruct(c *ast.CompositeLit) (bool, string) {
- return true, "" // Assume true, so we do the check.
-}
-
-func (f *File) matchArgType(t printfArgType, arg ast.Expr) bool {
- return true // We can't tell without types.
-}
-
-func (f *File) numArgsInSignature(call *ast.CallExpr) int {
- return 0 // We don't know.
-}
-
-func (f *File) isErrorMethodCall(call *ast.CallExpr) bool {
- // Is it a selector expression? Otherwise it's a function call, not a method call.
- if _, ok := call.Fun.(*ast.SelectorExpr); !ok {
- return false
- }
- return true // Best guess we can make without types.
-}
diff --git a/src/cmd/yacc/Makefile b/src/cmd/yacc/Makefile
index 56e954289..480844805 100644
--- a/src/cmd/yacc/Makefile
+++ b/src/cmd/yacc/Makefile
@@ -2,9 +2,9 @@
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
-units: yacc.go units.y
- go run yacc.go -p units_ units.y
- go build -o units y.go
+expr: yacc.go expr.y
+ go run yacc.go -p expr expr.y
+ go build -o expr y.go
clean:
- rm -f y.go y.output units
+ rm -f y.go y.output expr
diff --git a/src/cmd/yacc/doc.go b/src/cmd/yacc/doc.go
index 792c104e3..ceaaf2448 100644
--- a/src/cmd/yacc/doc.go
+++ b/src/cmd/yacc/doc.go
@@ -20,10 +20,8 @@ written in C and documented at
Adepts of the original yacc will have no trouble adapting to this
form of the tool.
-The file units.y in this directory is a yacc grammar for a version of
-the Unix tool units, also written in Go and largely transliterated
-from the Plan 9 C version. It needs the flag "-p units_" (see
-below).
+The file expr.y in this directory is a yacc grammar for a very simple
+expression parser. It needs the flag "-p expr" (see below).
The generated parser is reentrant. Parse expects to be given an
argument that conforms to the following interface:
diff --git a/src/cmd/yacc/expr.y b/src/cmd/yacc/expr.y
new file mode 100644
index 000000000..3afffe7ee
--- /dev/null
+++ b/src/cmd/yacc/expr.y
@@ -0,0 +1,205 @@
+// Copyright 2013 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 an example of a goyacc program.
+// To build it:
+// go tool yacc -p "expr" expr.y (produces y.go)
+// go build -o expr y.go
+// expr
+// > <type an expression>
+
+%{
+
+// This tag will be copied to the generated file to prevent that file
+// confusing a future build.
+
+// +build ignore
+
+package main
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "io"
+ "log"
+ "math/big"
+ "os"
+ "unicode/utf8"
+)
+
+%}
+
+%union {
+ num *big.Rat
+}
+
+%type <num> expr expr1 expr2 expr3
+
+%token <num> NUM
+
+%%
+
+top:
+ expr
+ {
+ if $1.IsInt() {
+ fmt.Println($1.Num().String())
+ } else {
+ fmt.Println($1.String())
+ }
+ }
+
+expr:
+ expr1
+| '+' expr
+ {
+ $$ = $2
+ }
+| '-' expr
+ {
+ $$.Neg($2)
+ }
+
+expr1:
+ expr2
+| expr1 '+' expr2
+ {
+ $$.Add($1, $3)
+ }
+| expr1 '-' expr2
+ {
+ $$.Sub($1, $3)
+ }
+
+expr2:
+ expr3
+| expr2 '*' expr3
+ {
+ $$.Mul($1, $3)
+ }
+| expr2 '/' expr3
+ {
+ $$.Quo($1, $3)
+ }
+
+expr3:
+ NUM
+| '(' expr ')'
+ {
+ $$ = $2
+ }
+
+
+%%
+
+// The parser expects the lexer to return 0 on EOF. Give it a name
+// for clarity.
+const eof = 0
+
+// The parser uses the type <prefix>Lex as a lexer. It must provide
+// the methods Lex(*<prefix>SymType) int and Error(string).
+type exprLex struct {
+ line []byte
+ peek rune
+}
+
+// The parser calls this method to get each new token. This
+// implementation returns operators and NUM.
+func (x *exprLex) Lex(yylval *exprSymType) int {
+ for {
+ c := x.next()
+ switch c {
+ case eof:
+ return eof
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ return x.num(c, yylval)
+ case '+', '-', '*', '/', '(', ')':
+ return int(c)
+
+ // Recognize Unicode multiplication and division
+ // symbols, returning what the parser expects.
+ case '×':
+ return '*'
+ case '÷':
+ return '/'
+
+ case ' ', '\t', '\n':
+ default:
+ log.Printf("unrecognized character %q", c)
+ }
+ }
+}
+
+// Lex a number.
+func (x *exprLex) num(c rune, yylval *exprSymType) int {
+ add := func(b *bytes.Buffer, c rune) {
+ if _, err := b.WriteRune(c); err != nil {
+ log.Fatalf("WriteRune: %s", err)
+ }
+ }
+ var b bytes.Buffer
+ add(&b, c)
+ L: for {
+ c = x.next()
+ switch c {
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', 'e', 'E':
+ add(&b, c)
+ default:
+ break L
+ }
+ }
+ if c != eof {
+ x.peek = c
+ }
+ yylval.num = &big.Rat{}
+ _, ok := yylval.num.SetString(b.String())
+ if !ok {
+ log.Printf("bad number %q", b.String())
+ return eof
+ }
+ return NUM
+}
+
+// Return the next rune for the lexer.
+func (x *exprLex) next() rune {
+ if x.peek != eof {
+ r := x.peek
+ x.peek = eof
+ return r
+ }
+ if len(x.line) == 0 {
+ return eof
+ }
+ c, size := utf8.DecodeRune(x.line)
+ x.line = x.line[size:]
+ if c == utf8.RuneError && size == 1 {
+ log.Print("invalid utf8")
+ return x.next()
+ }
+ return c
+}
+
+// The parser calls this method on a parse error.
+func (x *exprLex) Error(s string) {
+ log.Printf("parse error: %s", s)
+}
+
+func main() {
+ in := bufio.NewReader(os.Stdin)
+ for {
+ if _, err := os.Stdout.WriteString("> "); err != nil {
+ log.Fatalf("WriteString: %s", err)
+ }
+ line, err := in.ReadBytes('\n')
+ if err == io.EOF {
+ return
+ }
+ if err != nil {
+ log.Fatalf("ReadBytes: %s", err)
+ }
+
+ exprParse(&exprLex{line: line})
+ }
+}
diff --git a/src/cmd/yacc/units.txt b/src/cmd/yacc/units.txt
deleted file mode 100644
index df8f567d9..000000000
--- a/src/cmd/yacc/units.txt
+++ /dev/null
@@ -1,576 +0,0 @@
-/ Plan 9's /lib/units
-/ http://plan9.bell-labs.com/sources/plan9/lib/units
-/
-/ Copyright (C) 2003, Lucent Technologies Inc. and others. All Rights Reserved.
-/ Distributed under the terms of the Lucent Public License Version 1.02
-/ See http://plan9.bell-labs.com/plan9/license.html
-/
-/order of evaluation
-/ + -
-/ * /
-/ juxtaposition (meaning *)
-/ ¹ ² ³ ^
-/ | (meaning /)
-/ name number ()
-
-/dimensions
-m #
-kg #
-sec #
-coul #
-candela #
-$ #
-radian #
-bit #
-erlang #
-°K #
-°C #
-°F #
-
-/constants
-
-π 3.14159265358979323846
-pi π
-c 2.997925e+8 m/sec
-g 9.80665 m/sec²
-au 1.49597871e+11 m
-mole 6.022169e+23
-e 1.6021917e-19 coul
-energy c²
-force g
-mercury 1.33322e+5 kg/m²sec²
-hg mercury
-h 6.62620e-34 m²kg/sec
-ℏ h/2 π
-hbar ℏ
-nonillion 1e30
-octillion 1e27
-septillion 1e24
-sextillion 1e21
-pentillion 1e18
-quadrillion 1e15
-trillion 1e12
-billion 1e9
-million 1e6
-thousand 1e3
-hundred 1e2
-
-/dimensionless
-
-° 1|180 π radian
-degree °
-circle 2 π radian
-turn 2 π radian
-grad .9 °
-arcdeg 1 °
-arcmin 1|60 °
-arcsec 1|3600 °
-ccs 1|36 erlang
-
-steradian radian²
-sphere 4 π steradian
-sr steradian
-giga 1024 1024 1024
-
-/Time
-
-second sec
-s sec
-minute 60 sec
-min minute
-hour 60 min
-hr hour
-day 24 hr
-da day
-week 7 day
-year 365.24219879 day
-yr year
-month 1|12 year
-ms millisec
-us microsec
-
-/Mass
-
-gram millikg
-gm gram
-mg milligram
-metricton kilokg
-
-/Avoirdupois
-
-lb .45359237 kg
-lbf lb g
-pound lb
-ounce 1|16 lb
-oz ounce
-dram 1|16 oz
-dr dram
-grain 1|7000 lb
-gr grain
-shortton 2000 lb
-ton shortton
-longton 2240 lb
-
-/Apothecary
-
-scruple 20 grain
-apdram 60 grain
-apounce 480 grain
-troyounce apounce
-appound 5760 grain
-troypound appound
-
-/Length
-
-meter m
-cm centimeter
-mm millimeter
-km kilometer
-nm nanometer
-micron micrometer
-µ micrometer
-Å decinanometer
-angstrom Å
-
-inch 2.54 cm
-" inch
-in inch
-inches inch
-' 12"
-foot 12 in
-feet foot
-ft foot
-yard 3 ft
-yd yard
-rod 5.5 yd
-rd rod
-mile 5280 ft
-mi mile
-
-british 1200|3937 m/ft
-nmile 1852 m
-
-acre 4840 yd²
-
-cc cm³
-liter kilocc
-ml milliliter
-
-/US Liquid
-
-gallon 231 in³
-imperial 1.20095
-epa 0.8
-gal gallon
-quart 1|4 gal
-qt quart
-pint 1|2 qt
-pt pint
-
-floz 1|16 pt
-fldr 1|8 floz
-
-/US Dry
-
-dry 268.8025 in³/gallon
-peck 8 dry quart
-pk peck
-bushel 4 peck
-bu bushel
-
-/British
-
-brgallon 277.420 in³
-brquart 1|4 brgallon
-brpint 1|2 brquart
-brfloz 1|20 brpint
-brpeck 554.84 in³
-brbushel 4 brpeck
-
-/Energy Work
-
-newton kg m/sec²
-nt newton
-joule nt m
-cal 4.1868 joule
-
-/Electrical
-
-coulomb coul
-ampere coul/sec
-amp ampere
-watt joule/sec
-volt watt/amp
-Ω volt/amp
-ohm Ω
-mho 1/Ω
-farad coul/volt
-henry sec²/farad
-weber volt sec
-
-/Light
-
-cd candela
-lumen cd sr
-lux cd sr/m²
-
-/ MONEY DATE
-/ Wed Aug 29, 2012
-
-argentinapeso $ 0.2160
-australiadollar $ 1.0372
-boliviaboliviano $ 0.1427
-brazilreal $ 0.4872
-britainpound $ 1.5843
-canadadollar $ 1.0117
-chilepeso $ 1 | 480.6
-chinayuan $ 0.1574
-colombiapeso $ 1 | 1834
-czechkoruna $ 0.0506
-denmarkkrone $ 0.1681
-dominicanpeso $ 0.0256
-egyptpound $ 0.1640
-elsalvadorcolon $ 1 | 8.75
-europeuro $ 1.2528
-guatemalaquetzal $ 0.1290
-honduraslempira $ 0.0511
-hongkongdollar $ 0.1289
-hungaryforint $ 1 | 226.5
-indiarupee $ 0.0180
-indonesiarupiah $ 1 | 9540
-israelshekel $ 0.2479
-japanyen $ 0.0127
-kenyashilling $ 0.0119
-kuwaitdinar $ 3.5456
-lebanonpound $ 1 | 1505.5
-malaysiaringgit $ 0.3204
-mexicopeso $ 0.0754
-newzealanddollar $ 0.8035
-nicaraguacordoba $ 0.0421
-norwaykrone $ 0.1717
-pakistanrupee $ 0.0106
-paraguayguarani $ 1 | 4415
-perunewsol $ 0.3832
-philippinespeso $ 0.0236
-polandzloty $ 0.3001
-russiaruble $ 0.0311
-saudiarabiariyal $ 1 | 3.75
-singaporedollar $ 0.7976
-slovakkoruna 1 | 30.126 europeuro
-southafricarand $ 0.1188
-southkoreawon $ 1 | 1135
-swedenkrona $ 0.1502
-switzerlandfranc $ 1.0431
-taiwandollar $ 0.0334
-thailandbaht $ 0.0319
-turkeynewlira $ 0.5504
-uaedirham $ 0.2723
-uruguaynewpeso $ 0.0465
-vietnamdong $ 1 | 20865
-
-/ END MONEY
-
-€ europeuro
-£ britainpound
-¥ japanyen
-dollar $
-
-baht thailandbaht
-brpound britainpound
-dirham uaedirham
-euro europeuro
-forint hungaryforint
-krona swedenkrona
-peso mexicopeso
-rand southafricarand
-real brazilreal
-yuan chinayuan
-ringgit malaysiaringgit
-riyal saudiarabiariyal
-ruble russiaruble
-rupee indiarupee
-rupiah indonesiarupiah
-shekel israelshekel
-sol perunewsol
-won southkoreawon
-yen japanyen
-zloty polandzloty
-
-usdollar dollar
-sterling britainpound | pound
-poundsterling britainpound
-
-/bits
-
-baud bit/sec
-byte 8 bit
-short 2 byte
-long 4 byte
-vlong 8 bytes
-frame 2352 byte
-
-/Australian liquid measure
-
-pony 7 brfloz
-midie 10 brfloz
-pot midie
-handle midie
-schooner 15 brfloz
-jug 40 brfloz
-resch midie
-alf midie
-tinny 13 brfloz
-stubby tinny
-twisty 250 ml
-longneck 2 tinny
-slab 24 tinny
-sixpack 6 tinny
-nip brfloz
-
-/wine
-winebottle 750 ml
-balthazar 16 winebottle
-jeroboam 4 winebottle
-magnum 2 winebottle
-mathusalem 8 winebottle
-methuselah 8 winebottle
-nebuchadnezzar 20 winebottle
-rehoboam 6 winebottle
-salmanazar 12 winebottle
-split 0.25 winebottle
-jigger 1.5 floz
-
-/Trivia
-
-% 1|100
-admiraltyknot 6080 ft/hr
-ε₀ (1e-9/36π) farad/m
-α (1/4π ε₀) e²/ℏ c
-alpha α
-apostilb cd/π m²
-are 1e+2 m²
-arpentcan 27.52 mi
-arpentlin 191.835 ft
-astronomicalunit au
-atmosphere 1.01325e+5 nt/m²
-atm atmosphere
-atomicmassunit 1.66044e-27 kg
-amu atomicmassunit
-bag 94 lb
-bakersdozen 13
-bar 1e+5 nt/m²
-barie 1e-1 nt/m²
-barleycorn 1|3 in
-barn 1e-28 m²
-barrel 42 gal
-barye 1e-1 nt/m²
-bev 1e+9 e volt
-biot 10 amp
-blondel cd/π m²
-boardfoot 144 in³
-bolt 40 yd
-bottommeasure 1|40 in
-britishthermalunit 1.05506e+3 joule
-btu britishthermalunit
-quad 1.0e+15 btu
-refrigeration 12000 btu/ton hour
-buck dollar
-cable 720 ft
-caliber 1e-2 in
-calorie cal
-carat 205 mg
-cent centidollar
-cental 100 lb
-centesimalminute 1e-2 grad
-centesimalsecond 1e-4 grad
-century 100 year
-cfs ft³/sec
-chain 66 ft
-circularinch 1|4 π in²
-circularmil 1e-6|4 π in²
-clusec 1e-8 mm hg m³/s
-coomb 4 bu
-cord 128 ft³
-cordfoot cord
-crith 9.06e-2 gm
-cubit 18 in
-cup 1|2 pt
-curie 3.7e+10/sec
-cusec ft³/sec
-dalton amu
-decade 10 yr
-degK °K
-degC °C
-degF °F
-dipotre 1/m
-displacementton 35 ft³
-doppelzentner 100 kg
-dozen 12
-drop .03 cm³
-dyne cm gm/sec²
-electronvolt e volt
-ell 45 in
-engineerschain 100 ft
-engineerslink 100|100 ft
-equivalentfootcandle lumen/π ft²
-equivalentlux lumen/π m²
-equivalentphot cd/π cm²
-erg cm²gm/sec²
-ev e volt
-faraday 9.652e+4 coul
-fathom 6 ft
-fermi 1e-15 m
-fifth 4|5 qt
-fin 5 dollar
-finger 7|8 in
-firkin 9 gal
-footcandle lumen/ft²
-footlambert cd/π ft²
-fortnight 14 da
-franklin 3.33564e-10 coul
-frigorie kilocal
-furlong 220 yd
-galileo 1e-2 m/sec²
-gamma 1e-9 weber/m²
-gauss 1e-4 weber/m²
-geodeticfoot british ft
-geographicalmile 1852 m
-gilbert 7.95775e-1 amp
-gill 1|4 pt
-gross 144
-gunterschain 22 yd
-hand 4 in
-hectare 1e+4 m²
-hefnercandle .92 cd
-hertz 1/sec
-hogshead 2 barrel
-hd hogshead
-homestead 1|4 mi²
-horsepower 550 ft lb g/sec
-hp horsepower
-hyl gm force sec²/m
-hz 1/sec
-imaginarycubicfoot 1.4 ft³
-karat 1|24
-kcal kilocal
-kcalorie kilocal
-kev 1e+3 e volt
-key kg
-khz 1e+3/sec
-kilderkin 18 gal
-knot nmile/hr
-kwh kilowatt hour
-lambert cd/π cm²
-langley cal/cm²
-last 80 bu
-league 3 mi
-lightyear c yr
-ly lightyear
-lightsecond c sec
-line 1|12 in
-link 66|100 ft
-longhundredweight 112 lb
-longquarter 28 lb
-lusec 1e-6 mm hg m³/s
-mach 331.46 m/sec
-marineleague 3 nmile
-maxwell 1e-8 weber
-metriccarat 200 mg
-mev 1e+6 e volt
-mgd megagal/day
-mh millihenry
-mhz 1e+6/sec
-mil 1e-3 in
-millenium 1000 year
-minersinch 1.5 ft³/min
-minim 1|60 fldr
-mo month
-mpg mile/gal
-mph mile/hr
-nail 1|16 yd
-nauticalmile nmile
-nit cd/m²
-noggin 1|8 qt
-nox 1e-3 lux
-ns nanosec
-oersted 2.5e+2 amp/m π
-oe oersted
-pace 36 in
-palm 3 in
-parasang 3.5 mi
-parsec au radian/arcsec
-pascal nt/m²
-pc parsec
-pennyweight 1|20 oz
-percent %
-perch rd
-pf picofarad
-phot lumen/cm²
-pica 1|6 in
-pieze 1e+3 nt/m²
-pipe 4 barrel
-point 1|72 in
-poise gm/cm sec
-pole rd
-poundal ft lb/sec²
-pdl poundal
-proof 1/200
-psi lb g/in²
-quarter 9 in
-quartersection 1|4 mi²
-quintal 100 kg
-quire 25
-rad 100 erg/gm
-ream 500
-registerton 100 ft³
-rhe 10 m²/nt sec
-rontgen 2.58e-4 curie/kg
-rood 1.21e+3 yd
-rope 20 ft
-rutherford 1e+6/sec
-rydberg 1.36054e+1 ev
-sabin 1 ft²
-sack 3 bu
-seam 8 bu
-section mi²
-shippington 40 ft³
-shorthundredweight 100 lb
-shortquarter 25 lb
-siemens 1/Ω
-σ 5.66956e-5 erg/cm² °K^4 sec
-sigma σ
-skein 120 yd
-skot 1e-3 apostilb
-slug lb g sec²/ft
-span 9 in
-spat 4 π sr
-spindle 14400 yd
-square 100 ft²
-squidge 1|972 inch
-catsquidge 1|432 inch
-stere m³
-sthene 1e+3 nt
-stilb cd/cm²
-stoke 1e-4 m²/sec
-stone 14 lb
-strike 2 bu
-surveyfoot british ft
-surveyorschain 66 ft
-surveyorslink 66|100 ft
-tablespoon 4 fldr
-teaspoon 4|3 fldr
-tesla weber/m²
-therm 1e+5 btu
-thermie 1e+6 cal
-timberfoot ft³
-tnt 4.6e+6 m²/sec²
-tonne 1e+6 gm
-torr mm hg
-township 36 mi²
-tun 8 barrel
-water .22491|2.54 kg/m²sec²
-wey 40 bu
-weymass 252 lb
-Xunit 1.00202e-13 m
-k 1.38047e-16 erg/°K
-foal 9223372036854775807
diff --git a/src/cmd/yacc/units.y b/src/cmd/yacc/units.y
deleted file mode 100644
index 9c1b0b336..000000000
--- a/src/cmd/yacc/units.y
+++ /dev/null
@@ -1,768 +0,0 @@
-// Derived from Plan 9's /sys/src/cmd/units.y
-// http://plan9.bell-labs.com/sources/plan9/sys/src/cmd/units.y
-//
-// Copyright (C) 2003, Lucent Technologies Inc. and others. All Rights Reserved.
-// Portions Copyright 2009 The Go Authors. All Rights Reserved.
-// Distributed under the terms of the Lucent Public License Version 1.02
-// See http://plan9.bell-labs.com/plan9/license.html
-
-// Generate parser with prefix "units_":
-// go tool yacc -p "units_"
-
-%{
-
-// This tag will end up in the generated y.go, so that forgetting
-// 'make clean' does not fail the next build.
-
-// +build ignore
-
-// units.y
-// example of a Go yacc program
-// usage is
-// go tool yacc -p "units_" units.y (produces y.go)
-// go build -o units y.go
-// ./units $GOROOT/src/cmd/yacc/units.txt
-// you have: c
-// you want: furlongs/fortnight
-// * 1.8026178e+12
-// / 5.5474878e-13
-// you have:
-
-package main
-
-import (
- "bufio"
- "flag"
- "fmt"
- "math"
- "runtime"
- "os"
- "path/filepath"
- "strconv"
- "unicode/utf8"
-)
-
-const (
- Ndim = 15 // number of dimensions
- Maxe = 695 // log of largest number
-)
-
-type Node struct {
- vval float64
- dim [Ndim]int8
-}
-
-type Var struct {
- name string
- node Node
-}
-
-var fi *bufio.Reader // input
-var fund [Ndim]*Var // names of fundamental units
-var line string // current input line
-var lineno int // current input line number
-var linep int // index to next rune in unput
-var nerrors int // error count
-var one Node // constant one
-var peekrune rune // backup runt from input
-var retnode1 Node
-var retnode2 Node
-var retnode Node
-var sym string
-var vflag bool
-%}
-
-%union {
- node Node
- vvar *Var
- numb int
- vval float64
-}
-
-%type <node> prog expr expr0 expr1 expr2 expr3 expr4
-
-%token <vval> VÄL // dieresis to test UTF-8
-%token <vvar> VAR
-%token <numb> _SUP // tests leading underscore in token name
-%%
-prog:
- ':' VAR expr
- {
- var f int
- f = int($2.node.dim[0])
- $2.node = $3
- $2.node.dim[0] = 1
- if f != 0 {
- Errorf("redefinition of %v", $2.name)
- } else if vflag {
- fmt.Printf("%v\t%v\n", $2.name, &$2.node)
- }
- }
-| ':' VAR '#'
- {
- var f, i int
- for i = 1; i < Ndim; i++ {
- if fund[i] == nil {
- break
- }
- }
- if i >= Ndim {
- Error("too many dimensions")
- i = Ndim - 1
- }
- fund[i] = $2
- f = int($2.node.dim[0])
- $2.node = one
- $2.node.dim[0] = 1
- $2.node.dim[i] = 1
- if f != 0 {
- Errorf("redefinition of %v", $2.name)
- } else if vflag {
- fmt.Printf("%v\t#\n", $2.name)
- }
- }
-| ':'
- {
- }
-| '?' expr
- {
- retnode1 = $2
- }
-| '?'
- {
- retnode1 = one
- }
-
-expr:
- expr4
-| expr '+' expr4
- {
- add(&$$, &$1, &$3)
- }
-| expr '-' expr4
- {
- sub(&$$, &$1, &$3)
- }
-
-expr4:
- expr3
-| expr4 '*' expr3
- {
- mul(&$$, &$1, &$3)
- }
-| expr4 '/' expr3
- {
- div(&$$, &$1, &$3)
- }
-
-expr3:
- expr2
-| expr3 expr2
- {
- mul(&$$, &$1, &$2)
- }
-
-expr2:
- expr1
-| expr2 _SUP
- {
- xpn(&$$, &$1, $2)
- }
-| expr2 '^' expr1
- {
- var i int
- for i = 1; i < Ndim; i++ {
- if $3.dim[i] != 0 {
- Error("exponent has units")
- $$ = $1
- break
- }
- }
- if i >= Ndim {
- i = int($3.vval)
- if float64(i) != $3.vval {
- Error("exponent not integral")
- }
- xpn(&$$, &$1, i)
- }
- }
-
-expr1:
- expr0
-| expr1 '|' expr0
- {
- div(&$$, &$1, &$3)
- }
-
-expr0:
- VAR
- {
- if $1.node.dim[0] == 0 {
- Errorf("undefined %v", $1.name)
- $$ = one
- } else {
- $$ = $1.node
- }
- }
-| VÄL
- {
- $$ = one
- $$.vval = $1
- }
-| '(' expr ')'
- {
- $$ = $2
- }
-%%
-
-type UnitsLex int
-
-func (UnitsLex) Lex(yylval *units_SymType) int {
- var c rune
- var i int
-
- c = peekrune
- peekrune = ' '
-
-loop:
- if (c >= '0' && c <= '9') || c == '.' {
- goto numb
- }
- if ralpha(c) {
- goto alpha
- }
- switch c {
- case ' ', '\t':
- c = getrune()
- goto loop
- case '×':
- return '*'
- case '÷':
- return '/'
- case '¹', 'ⁱ':
- yylval.numb = 1
- return _SUP
- case '²', '⁲':
- yylval.numb = 2
- return _SUP
- case '³', '⁳':
- yylval.numb = 3
- return _SUP
- }
- return int(c)
-
-alpha:
- sym = ""
- for i = 0; ; i++ {
- sym += string(c)
- c = getrune()
- if !ralpha(c) {
- break
- }
- }
- peekrune = c
- yylval.vvar = lookup(0)
- return VAR
-
-numb:
- sym = ""
- for i = 0; ; i++ {
- sym += string(c)
- c = getrune()
- if !rdigit(c) {
- break
- }
- }
- peekrune = c
- f, err := strconv.ParseFloat(sym, 64)
- if err != nil {
- fmt.Printf("error converting %v\n", sym)
- f = 0
- }
- yylval.vval = f
- return VÄL
-}
-
-func (UnitsLex) Error(s string) {
- Errorf("syntax error, last name: %v", sym)
-}
-
-func main() {
- var file string
-
- flag.BoolVar(&vflag, "v", false, "verbose")
-
- flag.Parse()
-
- file = filepath.Join(runtime.GOROOT(), "src/cmd/yacc/units.txt")
- if flag.NArg() > 0 {
- file = flag.Arg(0)
- } else if file == "" {
- fmt.Fprintf(os.Stderr, "cannot find data file units.txt; provide it as argument or set $GOROOT\n")
- os.Exit(1)
- }
-
- f, err := os.Open(file)
- if err != nil {
- fmt.Fprintf(os.Stderr, "error opening %v: %v\n", file, err)
- os.Exit(1)
- }
- fi = bufio.NewReader(f)
-
- one.vval = 1
-
- /*
- * read the 'units' file to
- * develop a database
- */
- lineno = 0
- for {
- lineno++
- if readline() {
- break
- }
- if len(line) == 0 || line[0] == '/' {
- continue
- }
- peekrune = ':'
- units_Parse(UnitsLex(0))
- }
-
- /*
- * read the console to
- * print ratio of pairs
- */
- fi = bufio.NewReader(os.NewFile(0, "stdin"))
-
- lineno = 0
- for {
- if (lineno & 1) != 0 {
- fmt.Printf("you want: ")
- } else {
- fmt.Printf("you have: ")
- }
- if readline() {
- break
- }
- peekrune = '?'
- nerrors = 0
- units_Parse(UnitsLex(0))
- if nerrors != 0 {
- continue
- }
- if (lineno & 1) != 0 {
- if specialcase(&retnode, &retnode2, &retnode1) {
- fmt.Printf("\tis %v\n", &retnode)
- } else {
- div(&retnode, &retnode2, &retnode1)
- fmt.Printf("\t* %v\n", &retnode)
- div(&retnode, &retnode1, &retnode2)
- fmt.Printf("\t/ %v\n", &retnode)
- }
- } else {
- retnode2 = retnode1
- }
- lineno++
- }
- fmt.Printf("\n")
- os.Exit(0)
-}
-
-/*
- * all characters that have some
- * meaning. rest are usable as names
- */
-func ralpha(c rune) bool {
- switch c {
- case 0, '+', '-', '*', '/', '[', ']', '(', ')',
- '^', ':', '?', ' ', '\t', '.', '|', '#',
- '×', '÷', '¹', 'ⁱ', '²', '⁲', '³', '⁳':
- return false
- }
- return true
-}
-
-/*
- * number forming character
- */
-func rdigit(c rune) bool {
- switch c {
- case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
- '.', 'e', '+', '-':
- return true
- }
- return false
-}
-
-func Errorf(s string, v ...interface{}) {
- fmt.Printf("%v: %v\n\t", lineno, line)
- fmt.Printf(s, v...)
- fmt.Printf("\n")
-
- nerrors++
- if nerrors > 5 {
- fmt.Printf("too many errors\n")
- os.Exit(1)
- }
-}
-
-func Error(s string) {
- Errorf("%s", s)
-}
-
-func add(c, a, b *Node) {
- var i int
- var d int8
-
- for i = 0; i < Ndim; i++ {
- d = a.dim[i]
- c.dim[i] = d
- if d != b.dim[i] {
- Error("add must be like units")
- }
- }
- c.vval = fadd(a.vval, b.vval)
-}
-
-func sub(c, a, b *Node) {
- var i int
- var d int8
-
- for i = 0; i < Ndim; i++ {
- d = a.dim[i]
- c.dim[i] = d
- if d != b.dim[i] {
- Error("sub must be like units")
- }
- }
- c.vval = fadd(a.vval, -b.vval)
-}
-
-func mul(c, a, b *Node) {
- var i int
-
- for i = 0; i < Ndim; i++ {
- c.dim[i] = a.dim[i] + b.dim[i]
- }
- c.vval = fmul(a.vval, b.vval)
-}
-
-func div(c, a, b *Node) {
- var i int
-
- for i = 0; i < Ndim; i++ {
- c.dim[i] = a.dim[i] - b.dim[i]
- }
- c.vval = fdiv(a.vval, b.vval)
-}
-
-func xpn(c, a *Node, b int) {
- var i int
-
- *c = one
- if b < 0 {
- b = -b
- for i = 0; i < b; i++ {
- div(c, c, a)
- }
- } else {
- for i = 0; i < b; i++ {
- mul(c, c, a)
- }
- }
-}
-
-func specialcase(c, a, b *Node) bool {
- var i int
- var d, d1, d2 int8
-
- d1 = 0
- d2 = 0
- for i = 1; i < Ndim; i++ {
- d = a.dim[i]
- if d != 0 {
- if d != 1 || d1 != 0 {
- return false
- }
- d1 = int8(i)
- }
- d = b.dim[i]
- if d != 0 {
- if d != 1 || d2 != 0 {
- return false
- }
- d2 = int8(i)
- }
- }
- if d1 == 0 || d2 == 0 {
- return false
- }
-
- if fund[d1].name == "°C" && fund[d2].name == "°F" &&
- b.vval == 1 {
- for ll := 0; ll < len(c.dim); ll++ {
- c.dim[ll] = b.dim[ll]
- }
- c.vval = a.vval*9./5. + 32.
- return true
- }
-
- if fund[d1].name == "°F" && fund[d2].name == "°C" &&
- b.vval == 1 {
- for ll := 0; ll < len(c.dim); ll++ {
- c.dim[ll] = b.dim[ll]
- }
- c.vval = (a.vval - 32.) * 5. / 9.
- return true
- }
- return false
-}
-
-func printdim(str string, d, n int) string {
- var v *Var
-
- if n != 0 {
- v = fund[d]
- if v != nil {
- str += fmt.Sprintf("%v", v.name)
- } else {
- str += fmt.Sprintf("[%d]", d)
- }
- switch n {
- case 1:
- break
- case 2:
- str += "²"
- case 3:
- str += "³"
- default:
- str += fmt.Sprintf("^%d", n)
- }
- }
- return str
-}
-
-func (n Node) String() string {
- var str string
- var f, i, d int
-
- str = fmt.Sprintf("%.7e ", n.vval)
-
- f = 0
- for i = 1; i < Ndim; i++ {
- d = int(n.dim[i])
- if d > 0 {
- str = printdim(str, i, d)
- } else if d < 0 {
- f = 1
- }
- }
-
- if f != 0 {
- str += " /"
- for i = 1; i < Ndim; i++ {
- d = int(n.dim[i])
- if d < 0 {
- str = printdim(str, i, -d)
- }
- }
- }
-
- return str
-}
-
-func (v *Var) String() string {
- var str string
- str = fmt.Sprintf("%v %v", v.name, v.node)
- return str
-}
-
-func readline() bool {
- s, err := fi.ReadString('\n')
- if err != nil {
- return true
- }
- line = s
- linep = 0
- return false
-}
-
-func getrune() rune {
- var c rune
- var n int
-
- if linep >= len(line) {
- return 0
- }
- c, n = utf8.DecodeRuneInString(line[linep:len(line)])
- linep += n
- if c == '\n' {
- c = 0
- }
- return c
-}
-
-var symmap = make(map[string]*Var) // symbol table
-
-func lookup(f int) *Var {
- var p float64
- var w *Var
-
- v, ok := symmap[sym]
- if ok {
- return v
- }
- if f != 0 {
- return nil
- }
- v = new(Var)
- v.name = sym
- symmap[sym] = v
-
- p = 1
- for {
- p = fmul(p, pname())
- if p == 0 {
- break
- }
- w = lookup(1)
- if w != nil {
- v.node = w.node
- v.node.vval = fmul(v.node.vval, p)
- break
- }
- }
- return v
-}
-
-type Prefix struct {
- vval float64
- name string
-}
-
-var prefix = []Prefix{ // prefix table
- {1e-24, "yocto"},
- {1e-21, "zepto"},
- {1e-18, "atto"},
- {1e-15, "femto"},
- {1e-12, "pico"},
- {1e-9, "nano"},
- {1e-6, "micro"},
- {1e-6, "μ"},
- {1e-3, "milli"},
- {1e-2, "centi"},
- {1e-1, "deci"},
- {1e1, "deka"},
- {1e2, "hecta"},
- {1e2, "hecto"},
- {1e3, "kilo"},
- {1e6, "mega"},
- {1e6, "meg"},
- {1e9, "giga"},
- {1e12, "tera"},
- {1e15, "peta"},
- {1e18, "exa"},
- {1e21, "zetta"},
- {1e24, "yotta"},
-}
-
-func pname() float64 {
- var i, j, n int
- var s string
-
- /*
- * rip off normal prefixs
- */
- n = len(sym)
- for i = 0; i < len(prefix); i++ {
- s = prefix[i].name
- j = len(s)
- if j < n && sym[0:j] == s {
- sym = sym[j:n]
- return prefix[i].vval
- }
- }
-
- /*
- * rip off 's' suffixes
- */
- if n > 2 && sym[n-1] == 's' {
- sym = sym[0 : n-1]
- return 1
- }
-
- return 0
-}
-
-// careful multiplication
-// exponents (log) are checked before multiply
-func fmul(a, b float64) float64 {
- var l float64
-
- if b <= 0 {
- if b == 0 {
- return 0
- }
- l = math.Log(-b)
- } else {
- l = math.Log(b)
- }
-
- if a <= 0 {
- if a == 0 {
- return 0
- }
- l += math.Log(-a)
- } else {
- l += math.Log(a)
- }
-
- if l > Maxe {
- Error("overflow in multiply")
- return 1
- }
- if l < -Maxe {
- Error("underflow in multiply")
- return 0
- }
- return a * b
-}
-
-// careful division
-// exponents (log) are checked before divide
-func fdiv(a, b float64) float64 {
- var l float64
-
- if b <= 0 {
- if b == 0 {
- Errorf("division by zero: %v %v", a, b)
- return 1
- }
- l = math.Log(-b)
- } else {
- l = math.Log(b)
- }
-
- if a <= 0 {
- if a == 0 {
- return 0
- }
- l -= math.Log(-a)
- } else {
- l -= math.Log(a)
- }
-
- if l < -Maxe {
- Error("overflow in divide")
- return 1
- }
- if l > Maxe {
- Error("underflow in divide")
- return 0
- }
- return a / b
-}
-
-func fadd(a, b float64) float64 {
- return a + b
-}
diff --git a/src/cmd/yacc/yacc.go b/src/cmd/yacc/yacc.go
index 76b3aeac5..c53dc3b74 100644
--- a/src/cmd/yacc/yacc.go
+++ b/src/cmd/yacc/yacc.go
@@ -357,7 +357,7 @@ func main() {
func setup() {
var j, ty int
- stderr = bufio.NewWriter(os.NewFile(2, "stderr"))
+ stderr = bufio.NewWriter(os.Stderr)
foutput = nil
flag.Parse()
@@ -555,17 +555,18 @@ outer:
// process a rule
rlines[nprod] = lineno
+ ruleline := lineno
if t == '|' {
curprod[mem] = prdptr[nprod-1][0]
mem++
} else if t == IDENTCOLON {
curprod[mem] = chfind(1, tokname)
if curprod[mem] < NTBASE {
- errorf("token illegal on LHS of grammar rule")
+ lerrorf(ruleline, "token illegal on LHS of grammar rule")
}
mem++
} else {
- errorf("illegal rule: missing semicolon or | ?")
+ lerrorf(ruleline, "illegal rule: missing semicolon or | ?")
}
// read rule body
@@ -586,11 +587,11 @@ outer:
}
if t == PREC {
if gettok() != IDENTIFIER {
- errorf("illegal %%prec syntax")
+ lerrorf(ruleline, "illegal %%prec syntax")
}
j = chfind(2, tokname)
if j >= NTBASE {
- errorf("nonterminal " + nontrst[j-NTBASE].name + " illegal after %%prec")
+ lerrorf(ruleline, "nonterminal "+nontrst[j-NTBASE].name+" illegal after %%prec")
}
levprd[nprod] = toklev[j]
t = gettok()
@@ -646,7 +647,7 @@ outer:
// no explicit action, LHS has value
tempty := curprod[1]
if tempty < 0 {
- errorf("must return a value, since LHS has a type")
+ lerrorf(ruleline, "must return a value, since LHS has a type")
}
if tempty >= NTBASE {
tempty = nontrst[tempty-NTBASE].value
@@ -654,7 +655,7 @@ outer:
tempty = TYPE(toklev[tempty])
}
if tempty != nontrst[curprod[0]-NTBASE].value {
- errorf("default action causes potential type clash")
+ lerrorf(ruleline, "default action causes potential type clash")
}
fmt.Fprintf(fcode, "\n\tcase %v:", nprod)
fmt.Fprintf(fcode, "\n\t\t%sVAL.%v = %sS[%spt-0].%v",
@@ -1130,7 +1131,9 @@ func emitcode(code []rune, lineno int) {
writecode(line)
if !fmtImported && isPackageClause(line) {
fmt.Fprintln(ftable, `import __yyfmt__ "fmt"`)
- fmt.Fprintf(ftable, "//line %v:%v\n\t\t", infile, lineno+i)
+ if !lflag {
+ fmt.Fprintf(ftable, "//line %v:%v\n\t\t", infile, lineno+i)
+ }
fmtImported = true
}
}
@@ -2193,8 +2196,10 @@ nextk:
func output() {
var c, u, v int
- fmt.Fprintf(ftable, "\n//line yacctab:1\n")
- fmt.Fprintf(ftable, "var %sExca = []int{\n", prefix)
+ if !lflag {
+ fmt.Fprintf(ftable, "\n//line yacctab:1")
+ }
+ fmt.Fprintf(ftable, "\nvar %sExca = []int{\n", prefix)
noset := mkset()
@@ -2963,7 +2968,9 @@ func others() {
}
// copy yaccpar
- fmt.Fprintf(ftable, "\n//line yaccpar:1\n")
+ if !lflag {
+ fmt.Fprintf(ftable, "\n//line yaccpar:1\n")
+ }
parts := strings.SplitN(yaccpar, prefix+"run()", 2)
fmt.Fprintf(ftable, "%v", parts[0])
@@ -3187,7 +3194,7 @@ func create(s string) *bufio.Writer {
//
// write out error comment
//
-func errorf(s string, v ...interface{}) {
+func lerrorf(lineno int, s string, v ...interface{}) {
nerrors++
fmt.Fprintf(stderr, s, v...)
fmt.Fprintf(stderr, ": %v:%v\n", infile, lineno)
@@ -3197,6 +3204,10 @@ func errorf(s string, v ...interface{}) {
}
}
+func errorf(s string, v ...interface{}) {
+ lerrorf(lineno, s, v...)
+}
+
func exit(status int) {
if ftable != nil {
ftable.Flush()
@@ -3275,7 +3286,7 @@ out:
c = $$Tok2[1] /* unknown char */
}
if $$Debug >= 3 {
- __yyfmt__.Printf("lex %U %s\n", uint(char), $$Tokname(c))
+ __yyfmt__.Printf("lex %s(%d)\n", $$Tokname(c), uint(char))
}
return c
}
@@ -3372,7 +3383,7 @@ $$default:
Nerrs++
if $$Debug >= 1 {
__yyfmt__.Printf("%s", $$Statname($$state))
- __yyfmt__.Printf("saw %s\n", $$Tokname($$char))
+ __yyfmt__.Printf(" saw %s\n", $$Tokname($$char))
}
fallthrough
diff --git a/src/lib9/_p9dir.c b/src/lib9/_p9dir.c
index 58c0822a4..d65edcfbf 100644
--- a/src/lib9/_p9dir.c
+++ b/src/lib9/_p9dir.c
@@ -43,6 +43,9 @@ _p9dir(struct stat *lst, struct stat *st, char *name, Dir *d, char **str, char *
char tmp[20];
int sz, fd;
+#ifdef _WIN32
+ USED(lst);
+#endif
fd = -1;
USED(fd);
sz = 0;
@@ -68,12 +71,12 @@ _p9dir(struct stat *lst, struct stat *st, char *name, Dir *d, char **str, char *
*str += strlen(*str)+1;
}
}
- sz += strlen(s)+1;
+ sz += (int)strlen(s)+1;
/* user */
snprint(tmp, sizeof tmp, "%d", (int)st->st_uid);
s = tmp;
- sz += strlen(s)+1;
+ sz += (int)strlen(s)+1;
if(d){
if(*str+strlen(s)+1 > estr)
d->uid = "oops";
@@ -87,7 +90,7 @@ _p9dir(struct stat *lst, struct stat *st, char *name, Dir *d, char **str, char *
/* group */
snprint(tmp, sizeof tmp, "%d", (int)st->st_gid);
s = tmp;
- sz += strlen(s)+1;
+ sz += (int)strlen(s)+1;
if(d){
if(*str + strlen(s)+1 > estr)
d->gid = "oops";
@@ -107,10 +110,10 @@ _p9dir(struct stat *lst, struct stat *st, char *name, Dir *d, char **str, char *
d->qid.vers = st->st_gen;
#endif
if(d->qid.vers == 0)
- d->qid.vers = st->st_mtime + st->st_ctime;
+ d->qid.vers = (ulong)(st->st_mtime + st->st_ctime);
d->mode = st->st_mode&0777;
- d->atime = st->st_atime;
- d->mtime = st->st_mtime;
+ d->atime = (ulong)st->st_atime;
+ d->mtime = (ulong)st->st_mtime;
d->length = st->st_size;
if(S_ISDIR(st->st_mode)){
diff --git a/src/lib9/atoi.c b/src/lib9/atoi.c
index 37a178280..3162b0117 100644
--- a/src/lib9/atoi.c
+++ b/src/lib9/atoi.c
@@ -29,7 +29,7 @@ THE SOFTWARE.
int
atoi(char *s)
{
- return strtol(s, 0, 0);
+ return (int)strtol(s, 0, 0);
}
long
diff --git a/src/lib9/await.c b/src/lib9/await.c
index 0f00a94bd..690a61e5c 100644
--- a/src/lib9/await.c
+++ b/src/lib9/await.c
@@ -134,8 +134,8 @@ _wait(int pid4, int opt)
free(w);
return nil;
}
- u = ru.ru_utime.tv_sec*1000+((ru.ru_utime.tv_usec+500)/1000);
- s = ru.ru_stime.tv_sec*1000+((ru.ru_stime.tv_usec+500)/1000);
+ u = (ulong)(ru.ru_utime.tv_sec*1000+((ru.ru_utime.tv_usec+500)/1000));
+ s = (ulong)(ru.ru_stime.tv_sec*1000+((ru.ru_stime.tv_usec+500)/1000));
w->pid = pid;
w->time[0] = u;
w->time[1] = s;
diff --git a/src/lib9/dirfstat.c b/src/lib9/dirfstat.c
index 17fe10aee..8cc338409 100644
--- a/src/lib9/dirfstat.c
+++ b/src/lib9/dirfstat.c
@@ -43,10 +43,10 @@ dirfstat(int fd)
snprint(tmp, sizeof tmp, "/dev/fd/%d", fd);
nstr = _p9dir(&st, &st, tmp, nil, nil, nil);
- d = malloc(sizeof(Dir)+nstr);
+ d = malloc(sizeof(Dir)+(size_t)nstr);
if(d == nil)
return nil;
- memset(d, 0, sizeof(Dir)+nstr);
+ memset(d, 0, sizeof(Dir)+(size_t)nstr);
str = (char*)&d[1];
_p9dir(&st, &st, tmp, d, &str, str+nstr);
return d;
diff --git a/src/lib9/dirfwstat.c b/src/lib9/dirfwstat.c
index fe9153b9b..e32ddeaea 100644
--- a/src/lib9/dirfwstat.c
+++ b/src/lib9/dirfwstat.c
@@ -48,6 +48,8 @@ futimes(int fd, struct timeval *tv)
static int
futimes(int fd, struct timeval *tv)
{
+ USED(fd);
+ USED(tv);
werrstr("futimes not available");
return -1;
}
@@ -63,14 +65,14 @@ dirfwstat(int fd, Dir *dir)
ret = 0;
#ifndef _WIN32
if(~dir->mode != 0){
- if(fchmod(fd, dir->mode) < 0)
+ if(fchmod(fd, (mode_t)dir->mode) < 0)
ret = -1;
}
#endif
if(~dir->mtime != 0){
- tv[0].tv_sec = dir->mtime;
+ tv[0].tv_sec = (time_t)dir->mtime;
tv[0].tv_usec = 0;
- tv[1].tv_sec = dir->mtime;
+ tv[1].tv_sec = (time_t)dir->mtime;
tv[1].tv_usec = 0;
if(futimes(fd, tv) < 0)
ret = -1;
diff --git a/src/lib9/dirstat.c b/src/lib9/dirstat.c
index 6d804ca7c..df2f85bd0 100644
--- a/src/lib9/dirstat.c
+++ b/src/lib9/dirstat.c
@@ -52,10 +52,10 @@ dirstat(char *file)
#endif
nstr = _p9dir(&lst, &st, file, nil, nil, nil);
- d = malloc(sizeof(Dir)+nstr);
+ d = malloc(sizeof(Dir)+(size_t)nstr);
if(d == nil)
return nil;
- memset(d, 0, sizeof(Dir)+nstr);
+ memset(d, 0, sizeof(Dir)+(size_t)nstr);
str = (char*)&d[1];
_p9dir(&lst, &st, file, d, &str, str+nstr);
return d;
diff --git a/src/lib9/dirwstat.c b/src/lib9/dirwstat.c
index 2646cba40..9bf348af6 100644
--- a/src/lib9/dirwstat.c
+++ b/src/lib9/dirwstat.c
@@ -37,7 +37,7 @@ dirwstat(char *file, Dir *dir)
if(~dir->mtime == 0)
return 0;
- ub.actime = dir->mtime;
- ub.modtime = dir->mtime;
+ ub.actime = (time_t)dir->mtime;
+ ub.modtime = (time_t)dir->mtime;
return utime(file, &ub);
}
diff --git a/src/lib9/execl.c b/src/lib9/execl.c
index 9e42ad34b..81d315883 100644
--- a/src/lib9/execl.c
+++ b/src/lib9/execl.c
@@ -37,7 +37,7 @@ execl(char *prog, ...)
;
va_end(arg);
- argv = malloc((i+1)*sizeof(char*));
+ argv = malloc((size_t)(i+1)*sizeof(char*));
if(argv == nil)
return -1;
diff --git a/src/lib9/flag.c b/src/lib9/flag.c
index 7c79c1a6d..db46b9809 100644
--- a/src/lib9/flag.c
+++ b/src/lib9/flag.c
@@ -54,7 +54,7 @@ lookflag(char *name, int namelen, int creat)
h = fnv(name, namelen) & (nelem(fhash)-1);
for(f=fhash[h]; f; f=f->next) {
- if(f->namelen == namelen && memcmp(f->name, name, namelen) == 0) {
+ if(f->namelen == namelen && memcmp(f->name, name, (size_t)namelen) == 0) {
if(creat)
sysfatal("multiple definitions of flag -%s", name);
return f;
@@ -97,7 +97,7 @@ flagcount(char *name, char *desc, int *p)
{
Flag *f;
- f = lookflag(name, strlen(name), 1);
+ f = lookflag(name, (int)strlen(name), 1);
f->desc = desc;
f->iscount = 1;
f->set = count;
@@ -119,7 +119,7 @@ flagint64(char *name, char *desc, int64 *p)
{
Flag *f;
- f = lookflag(name, strlen(name), 1);
+ f = lookflag(name, (int)strlen(name), 1);
f->desc = desc;
f->set = atollwhex;
f->arg = p;
@@ -130,7 +130,7 @@ atolwhex(char *s, void *p)
{
char *t;
- *(int32*)p = strtol(s, &t, 0);
+ *(int32*)p = (int32)strtol(s, &t, 0);
if(*s == '\0' || *t != '\0')
sysfatal("invalid numeric argument -%s=%s", curflag->name, s);
}
@@ -140,7 +140,7 @@ flagint32(char *name, char *desc, int32 *p)
{
Flag *f;
- f = lookflag(name, strlen(name), 1);
+ f = lookflag(name, (int)strlen(name), 1);
f->desc = desc;
f->set = atolwhex;
f->arg = p;
@@ -158,7 +158,7 @@ flagstr(char *name, char *desc, char **p)
Flag *f;
- f = lookflag(name, strlen(name), 1);
+ f = lookflag(name, (int)strlen(name), 1);
f->desc = desc;
f->set = string;
f->arg = p;
@@ -176,7 +176,7 @@ flagfn0(char *name, char *desc, void (*fn)(void))
{
Flag *f;
- f = lookflag(name, strlen(name), 1);
+ f = lookflag(name, (int)strlen(name), 1);
f->desc = desc;
f->set = fn0;
f->arg = fn;
@@ -194,7 +194,7 @@ flagfn1(char *name, char *desc, void (*fn)(char*))
{
Flag *f;
- f = lookflag(name, strlen(name), 1);
+ f = lookflag(name, (int)strlen(name), 1);
f->desc = desc;
f->set = fn1;
f->arg = fn;
@@ -211,7 +211,7 @@ flagfn2(char *name, char *desc, void (*fn)(char*, char*))
{
Flag *f;
- f = lookflag(name, strlen(name), 1);
+ f = lookflag(name, (int)strlen(name), 1);
f->desc = desc;
f->set2 = fn2;
f->arg = fn;
@@ -253,9 +253,9 @@ flagparse(int *argcp, char ***argvp, void (*usage)(void))
name = p+1;
q = strchr(name, '=');
if(q != nil)
- namelen = q++ - name;
+ namelen = (int)(q++ - name);
else
- namelen = strlen(name);
+ namelen = (int)strlen(name);
f = lookflag(name, namelen, 0);
if(f == nil) {
if(strcmp(p, "-h") == 0 || strcmp(p, "-help") == 0 || strcmp(p, "-?") == 0)
diff --git a/src/lib9/fmt/dofmt.c b/src/lib9/fmt/dofmt.c
index cc6ab9225..94a91a2aa 100644
--- a/src/lib9/fmt/dofmt.c
+++ b/src/lib9/fmt/dofmt.c
@@ -25,7 +25,7 @@ int
dofmt(Fmt *f, char *fmt)
{
Rune rune, *rt, *rs;
- int r;
+ Rune r;
char *t, *s;
int n, nfmt;
@@ -34,7 +34,7 @@ dofmt(Fmt *f, char *fmt)
if(f->runes){
rt = (Rune*)f->to;
rs = (Rune*)f->stop;
- while((r = *(uchar*)fmt) && r != '%'){
+ while((r = (Rune)*(uchar*)fmt) && r != '%'){
if(r < Runeself)
fmt++;
else{
@@ -44,7 +44,7 @@ dofmt(Fmt *f, char *fmt)
FMTRCHAR(f, rt, rs, r);
}
fmt++;
- f->nfmt += rt - (Rune *)f->to;
+ f->nfmt += (int)(rt - (Rune *)f->to);
f->to = rt;
if(!r)
return f->nfmt - nfmt;
@@ -52,7 +52,7 @@ dofmt(Fmt *f, char *fmt)
}else{
t = (char*)f->to;
s = (char*)f->stop;
- while((r = *(uchar*)fmt) && r != '%'){
+ while((r = (Rune)*(uchar*)fmt) && r != '%'){
if(r < Runeself){
FMTCHAR(f, t, s, r);
fmt++;
@@ -70,7 +70,7 @@ dofmt(Fmt *f, char *fmt)
}
}
fmt++;
- f->nfmt += t - (char *)f->to;
+ f->nfmt += (int)(t - (char *)f->to);
f->to = t;
if(!r)
return f->nfmt - nfmt;
@@ -87,9 +87,9 @@ void *
__fmtflush(Fmt *f, void *t, int len)
{
if(f->runes)
- f->nfmt += (Rune*)t - (Rune*)f->to;
+ f->nfmt += (int)((Rune*)t - (Rune*)f->to);
else
- f->nfmt += (char*)t - (char *)f->to;
+ f->nfmt += (int)((char*)t - (char *)f->to);
f->to = t;
if(f->flush == 0 || (*f->flush)(f) == 0 || (char*)f->to + len > (char*)f->stop){
f->stop = f->to;
@@ -112,7 +112,7 @@ __fmtpad(Fmt *f, int n)
s = (char*)f->stop;
for(i = 0; i < n; i++)
FMTCHAR(f, t, s, ' ');
- f->nfmt += t - (char *)f->to;
+ f->nfmt += (int)(t - (char *)f->to);
f->to = t;
return 0;
}
@@ -127,7 +127,7 @@ __rfmtpad(Fmt *f, int n)
s = (Rune*)f->stop;
for(i = 0; i < n; i++)
FMTRCHAR(f, t, s, ' ');
- f->nfmt += t - (Rune *)f->to;
+ f->nfmt += (int)(t - (Rune *)f->to);
f->to = t;
return 0;
}
@@ -157,13 +157,13 @@ __fmtcpy(Fmt *f, const void *vm, int n, int sz)
r = *(uchar*)m;
if(r < Runeself)
m++;
- else if((me - m) >= UTFmax || fullrune(m, me-m))
+ else if((me - m) >= UTFmax || fullrune(m, (int)(me-m)))
m += chartorune(&r, m);
else
break;
FMTRCHAR(f, rt, rs, r);
}
- f->nfmt += rt - (Rune *)f->to;
+ f->nfmt += (int)(rt - (Rune *)f->to);
f->to = rt;
if(fl & FmtLeft && __rfmtpad(f, w - n) < 0)
return -1;
@@ -176,13 +176,13 @@ __fmtcpy(Fmt *f, const void *vm, int n, int sz)
r = *(uchar*)m;
if(r < Runeself)
m++;
- else if((me - m) >= UTFmax || fullrune(m, me-m))
+ else if((me - m) >= UTFmax || fullrune(m, (int)(me-m)))
m += chartorune(&r, m);
else
break;
FMTRUNE(f, t, s, r);
}
- f->nfmt += t - (char *)f->to;
+ f->nfmt += (int)(t - (char *)f->to);
f->to = t;
if(fl & FmtLeft && __fmtpad(f, w - n) < 0)
return -1;
@@ -212,7 +212,7 @@ __fmtrcpy(Fmt *f, const void *vm, int n)
rs = (Rune*)f->stop;
for(me = m + n; m < me; m++)
FMTRCHAR(f, rt, rs, *m);
- f->nfmt += rt - (Rune *)f->to;
+ f->nfmt += (int)(rt - (Rune *)f->to);
f->to = rt;
if(fl & FmtLeft && __rfmtpad(f, w - n) < 0)
return -1;
@@ -225,7 +225,7 @@ __fmtrcpy(Fmt *f, const void *vm, int n)
r = *m;
FMTRUNE(f, t, s, r);
}
- f->nfmt += t - (char *)f->to;
+ f->nfmt += (int)(t - (char *)f->to);
f->to = t;
if(fl & FmtLeft && __fmtpad(f, w - n) < 0)
return -1;
@@ -239,7 +239,7 @@ __charfmt(Fmt *f)
{
char x[1];
- x[0] = va_arg(f->args, int);
+ x[0] = (char)va_arg(f->args, int);
f->prec = 1;
return __fmtcpy(f, (const char*)x, 1, 1);
}
@@ -250,7 +250,7 @@ __runefmt(Fmt *f)
{
Rune x[1];
- x[0] = va_arg(f->args, int);
+ x[0] = (Rune)va_arg(f->args, int);
return __fmtrcpy(f, (const void*)x, 1);
}
@@ -278,7 +278,7 @@ fmtstrcpy(Fmt *f, char *s)
#endif
return __fmtcpy(f, s, j, i);
}
- return __fmtcpy(f, s, utflen(s), strlen(s));
+ return __fmtcpy(f, s, utflen(s), (int)strlen(s));
}
/* fmt out a null terminated utf string */
@@ -309,7 +309,7 @@ fmtrunestrcpy(Fmt *f, Rune *s)
}else{
for(e = s; *e; e++)
;
- n = e - s;
+ n = (int)(e - s);
}
return __fmtrcpy(f, s, n);
}
@@ -342,8 +342,8 @@ __ifmt(Fmt *f)
char buf[140], *p, *conv;
/* 140: for 64 bits of binary + 3-byte sep every 4 digits */
uvlong vu;
- ulong u;
- int neg, base, i, n, fl, w, isv;
+ ulong fl, u;
+ int neg, base, i, n, w, isv;
int ndig, len, excess, bytelen;
char *grouping;
char *thousands;
@@ -377,27 +377,27 @@ __ifmt(Fmt *f)
if(fl & FmtUnsigned)
vu = va_arg(f->args, uvlong);
else
- vu = va_arg(f->args, vlong);
+ vu = (uvlong)va_arg(f->args, vlong);
}else if(fl & FmtLong){
if(fl & FmtUnsigned)
u = va_arg(f->args, ulong);
else
- u = va_arg(f->args, long);
+ u = (ulong)va_arg(f->args, long);
}else if(fl & FmtByte){
if(fl & FmtUnsigned)
u = (uchar)va_arg(f->args, int);
else
- u = (char)va_arg(f->args, int);
+ u = (ulong)(char)va_arg(f->args, int);
}else if(fl & FmtShort){
if(fl & FmtUnsigned)
u = (ushort)va_arg(f->args, int);
else
- u = (short)va_arg(f->args, int);
+ u = (ulong)(short)va_arg(f->args, int);
}else{
if(fl & FmtUnsigned)
u = va_arg(f->args, uint);
else
- u = va_arg(f->args, int);
+ u = (ulong)va_arg(f->args, int);
}
conv = "0123456789abcdef";
grouping = "\4"; /* for hex, octal etc. (undefined by spec but nice) */
@@ -428,10 +428,10 @@ __ifmt(Fmt *f)
}
if(!(fl & FmtUnsigned)){
if(isv && (vlong)vu < 0){
- vu = -(vlong)vu;
+ vu = (uvlong)-(vlong)vu;
neg = 1;
}else if(!isv && (long)u < 0){
- u = -(long)u;
+ u = (ulong)-(long)u;
neg = 1;
}
}
@@ -440,11 +440,11 @@ __ifmt(Fmt *f)
excess = 0; /* number of bytes > number runes */
ndig = 0;
len = utflen(thousands);
- bytelen = strlen(thousands);
+ bytelen = (int)strlen(thousands);
if(isv){
while(vu){
- i = vu % base;
- vu /= base;
+ i = (int)(vu % (uvlong)base);
+ vu /= (uvlong)base;
if((fl & FmtComma) && n % 4 == 3){
*p-- = ',';
n++;
@@ -453,15 +453,15 @@ __ifmt(Fmt *f)
n += len;
excess += bytelen - len;
p -= bytelen;
- memmove(p+1, thousands, bytelen);
+ memmove(p+1, thousands, (size_t)bytelen);
}
*p-- = conv[i];
n++;
}
}else{
while(u){
- i = u % base;
- u /= base;
+ i = (int)(u % (ulong)base);
+ u /= (ulong)base;
if((fl & FmtComma) && n % 4 == 3){
*p-- = ',';
n++;
@@ -470,7 +470,7 @@ __ifmt(Fmt *f)
n += len;
excess += bytelen - len;
p -= bytelen;
- memmove(p+1, thousands, bytelen);
+ memmove(p+1, thousands, (size_t)bytelen);
}
*p-- = conv[i];
n++;
@@ -496,14 +496,14 @@ __ifmt(Fmt *f)
* Zero values don't get 0x.
*/
if(f->r == 'x' || f->r == 'X')
- fl &= ~FmtSharp;
+ fl &= ~(ulong)FmtSharp;
}
for(w = f->prec; n < w && p > buf+3; n++){
if((fl & FmtApost) && __needsep(&ndig, &grouping)){
n += len;
excess += bytelen - len;
p -= bytelen;
- memmove(p+1, thousands, bytelen);
+ memmove(p+1, thousands, (size_t)bytelen);
}
*p-- = '0';
}
@@ -514,7 +514,7 @@ __ifmt(Fmt *f)
n += 2;
else if(base == 8){
if(p[1] == '0')
- fl &= ~FmtSharp;
+ fl &= ~(ulong)FmtSharp;
else
n++;
}
@@ -528,15 +528,15 @@ __ifmt(Fmt *f)
n += len;
excess += bytelen - len;
p -= bytelen;
- memmove(p+1, thousands, bytelen);
+ memmove(p+1, thousands, (size_t)bytelen);
}
*p-- = '0';
}
- f->flags &= ~FmtWidth;
+ f->flags &= ~(ulong)FmtWidth;
}
if(fl & FmtSharp){
if(base == 16)
- *p-- = f->r;
+ *p-- = (char)f->r;
if(base == 16 || base == 8)
*p-- = '0';
}
@@ -546,7 +546,7 @@ __ifmt(Fmt *f)
*p-- = '+';
else if(fl & FmtSpace)
*p-- = ' ';
- f->flags &= ~FmtPrec;
+ f->flags &= ~(ulong)FmtPrec;
return __fmtcpy(f, p + 1, n, n + excess);
}
@@ -563,9 +563,9 @@ __countfmt(Fmt *f)
}else if(fl & FmtLong){
*(long*)p = f->nfmt;
}else if(fl & FmtByte){
- *(char*)p = f->nfmt;
+ *(char*)p = (char)f->nfmt;
}else if(fl & FmtShort){
- *(short*)p = f->nfmt;
+ *(short*)p = (short)f->nfmt;
}else{
*(int*)p = f->nfmt;
}
diff --git a/src/lib9/fmt/dorfmt.c b/src/lib9/fmt/dorfmt.c
index 672742f02..c18d9ee5d 100644
--- a/src/lib9/fmt/dorfmt.c
+++ b/src/lib9/fmt/dorfmt.c
@@ -27,7 +27,7 @@ int
dorfmt(Fmt *f, const Rune *fmt)
{
Rune *rt, *rs;
- int r;
+ Rune r;
char *t, *s;
int nfmt;
@@ -39,7 +39,7 @@ dorfmt(Fmt *f, const Rune *fmt)
while((r = *fmt++) && r != '%'){
FMTRCHAR(f, rt, rs, r);
}
- f->nfmt += rt - (Rune *)f->to;
+ f->nfmt += (int)(rt - (Rune *)f->to);
f->to = rt;
if(!r)
return f->nfmt - nfmt;
@@ -50,7 +50,7 @@ dorfmt(Fmt *f, const Rune *fmt)
while((r = *fmt++) && r != '%'){
FMTRUNE(f, t, f->stop, r);
}
- f->nfmt += t - (char *)f->to;
+ f->nfmt += (int)(t - (char *)f->to);
f->to = t;
if(!r)
return f->nfmt - nfmt;
diff --git a/src/lib9/fmt/fltfmt.c b/src/lib9/fmt/fltfmt.c
index 9f3f3edab..dec6f8480 100644
--- a/src/lib9/fmt/fltfmt.c
+++ b/src/lib9/fmt/fltfmt.c
@@ -103,7 +103,7 @@ xadd1(char *a, int n)
for(b = a+n-1; b >= a; b--) {
c = *b + 1;
if(c <= '9') {
- *b = c;
+ *b = (char)c;
return 0;
}
*b = '0';
@@ -144,7 +144,7 @@ xsub1(char *a, int n)
*b = '9';
return 1;
}
- *b = c;
+ *b = (char)c;
return 0;
}
*b = '9';
@@ -173,7 +173,7 @@ xfmtexp(char *p, int e, int ucase)
*p++ = '+';
i = 0;
while(e) {
- se[i++] = e % 10 + '0';
+ se[i++] = (char)(e % 10 + '0');
e /= 10;
}
while(i < 2)
@@ -192,7 +192,8 @@ xfmtexp(char *p, int e, int ucase)
static void
xdtoa(double f, char *s, int *exp, int *neg, int *ns)
{
- int c, d, e2, e, ee, i, ndigit, oerrno;
+ int d, e2, e, ee, i, ndigit, oerrno;
+ char c;
char tmp[NSIGNIF+10];
double g;
@@ -239,7 +240,7 @@ xdtoa(double f, char *s, int *exp, int *neg, int *ns)
*/
for(i=0; i<NSIGNIF; i++) {
d = (int)g;
- s[i] = d+'0';
+ s[i] = (char)(d+'0');
g = (g-d) * 10;
}
s[i] = 0;
@@ -350,12 +351,13 @@ __efgfmt(Fmt *fmt)
{
char buf[NSIGNIF+10], *dot, *digits, *p, *s, suf[10], *t;
double f;
- int c, chr, dotwid, e, exp, fl, ndigits, neg, newndigits;
+ int c, chr, dotwid, e, exp, ndigits, neg, newndigits;
int pad, point, prec, realchr, sign, sufwid, ucase, wid, z1, z2;
+ ulong fl;
Rune r, *rs, *rt;
if(fmt->flags&FmtLong)
- f = va_arg(fmt->args, long double);
+ f = (double)va_arg(fmt->args, long double);
else
f = va_arg(fmt->args, double);
@@ -367,7 +369,7 @@ __efgfmt(Fmt *fmt)
prec = FDEFLT;
if(fl & FmtPrec)
prec = fmt->prec;
- chr = fmt->r;
+ chr = (int)fmt->r;
ucase = 0;
switch(chr) {
case 'A':
@@ -386,7 +388,7 @@ __efgfmt(Fmt *fmt)
s = special[0+ucase];
special:
fmt->flags = fl & (FmtWidth|FmtLeft);
- return __fmtcpy(fmt, s, strlen(s), strlen(s));
+ return __fmtcpy(fmt, s, (int)strlen(s), (int)strlen(s));
}
if(__isInf(f, 1)) {
s = special[2+ucase];
@@ -488,7 +490,7 @@ __efgfmt(Fmt *fmt)
z2 = 0;
}
xfmtexp(suf, e, ucase);
- sufwid = strlen(suf);
+ sufwid = (int)strlen(suf);
break;
casef:
@@ -638,7 +640,7 @@ __efgfmt(Fmt *fmt)
}
}
}
- fmt->nfmt += rt - (Rune*)fmt->to;
+ fmt->nfmt += (int)(rt - (Rune*)fmt->to);
fmt->to = rt;
if(sufwid && __fmtcpy(fmt, suf, sufwid, sufwid) < 0)
return -1;
@@ -667,7 +669,7 @@ __efgfmt(Fmt *fmt)
for(p=dot; *p; p++)
FMTCHAR(fmt, t, s, *p);
}
- fmt->nfmt += t - (char*)fmt->to;
+ fmt->nfmt += (int)(t - (char*)fmt->to);
fmt->to = t;
if(sufwid && __fmtcpy(fmt, suf, sufwid, sufwid) < 0)
return -1;
diff --git a/src/lib9/fmt/fmt.c b/src/lib9/fmt/fmt.c
index 7a747b1b1..7e576773b 100644
--- a/src/lib9/fmt/fmt.c
+++ b/src/lib9/fmt/fmt.c
@@ -187,12 +187,12 @@ __fmtdispatch(Fmt *f, void *fmt, int isrunes)
case '5': case '6': case '7': case '8': case '9':
i = 0;
while(r >= '0' && r <= '9'){
- i = i * 10 + r - '0';
+ i = i * 10 + (int)r - '0';
if(isrunes){
r = *(Rune*)fmt;
fmt = (Rune*)fmt + 1;
}else{
- r = *(char*)fmt;
+ r = (Rune)*(char*)fmt;
fmt = (char*)fmt + 1;
}
}
@@ -217,7 +217,7 @@ __fmtdispatch(Fmt *f, void *fmt, int isrunes)
* ignore the precision.
*/
if(f->flags & FmtPrec){
- f->flags &= ~FmtPrec;
+ f->flags &= ~(ulong)FmtPrec;
f->prec = 0;
continue;
}
@@ -226,7 +226,7 @@ __fmtdispatch(Fmt *f, void *fmt, int isrunes)
}
goto numflag;
}
- n = (*fmtfmt(r))(f);
+ n = (*fmtfmt((int)r))(f);
if(n < 0)
return nil;
if(n == 0)
diff --git a/src/lib9/fmt/fmtdef.h b/src/lib9/fmt/fmtdef.h
index 74cb8a8d2..4bbd9f578 100644
--- a/src/lib9/fmt/fmtdef.h
+++ b/src/lib9/fmt/fmtdef.h
@@ -75,7 +75,7 @@ int __strfmt(Fmt *f);
else\
return -1;\
}\
- *t++ = c;\
+ *t++ = (char)c;\
}while(0)
#define FMTRCHAR(f, t, s, c)\
@@ -87,7 +87,7 @@ int __strfmt(Fmt *f);
else\
return -1;\
}\
- *t++ = c;\
+ *t++ = (Rune)c;\
}while(0)
#define FMTRUNE(f, t, s, r)\
@@ -102,7 +102,7 @@ int __strfmt(Fmt *f);
return -1;\
}\
if(r < Runeself)\
- *t++ = r;\
+ *t++ = (char)r;\
else{\
_rune = r;\
t += runetochar(t, &_rune);\
diff --git a/src/lib9/fmt/fmtfdflush.c b/src/lib9/fmt/fmtfdflush.c
index c9854cee5..401acbea7 100644
--- a/src/lib9/fmt/fmtfdflush.c
+++ b/src/lib9/fmt/fmtfdflush.c
@@ -29,8 +29,8 @@ __fmtFdFlush(Fmt *f)
{
int n;
- n = (char*)f->to - (char*)f->start;
- if(n && write((uintptr)f->farg, f->start, n) != n)
+ n = (int)((char*)f->to - (char*)f->start);
+ if(n && (int)write((int)(uintptr)f->farg, f->start, (size_t)n) != n)
return 0;
f->to = f->start;
return 1;
diff --git a/src/lib9/fmt/fmtquote.c b/src/lib9/fmt/fmtquote.c
index b9ac772ed..93b2abbe7 100644
--- a/src/lib9/fmt/fmtquote.c
+++ b/src/lib9/fmt/fmtquote.c
@@ -69,7 +69,7 @@ __quotesetup(char *s, Rune *r, int nin, int nout, Quoteinfo *q, int sharp, int r
break;
}
- if((c <= L' ') || (c == L'\'') || (fmtdoquote!=nil && fmtdoquote(c))){
+ if((c <= L' ') || (c == L'\'') || (fmtdoquote!=nil && fmtdoquote((int)c))){
if(!q->quoted){
if(runesout){
if(1+q->nrunesout+1+1 > nout) /* no room for quotes */
@@ -152,7 +152,7 @@ qstrfmt(char *sin, Rune *rin, Quoteinfo *q, Fmt *f)
r = *(uchar*)m;
if(r < Runeself)
m++;
- else if((me - m) >= UTFmax || fullrune(m, me-m))
+ else if((me - m) >= UTFmax || fullrune(m, (int)(me-m)))
m += chartorune(&r, m);
else
break;
@@ -175,14 +175,14 @@ qstrfmt(char *sin, Rune *rin, Quoteinfo *q, Fmt *f)
if(f->runes){
FMTRCHAR(f, rt, rs, '\'');
USED(rs);
- f->nfmt += rt - (Rune *)f->to;
+ f->nfmt += (int)(rt - (Rune *)f->to);
f->to = rt;
if(fl & FmtLeft && __rfmtpad(f, w - q->nrunesout) < 0)
return -1;
}else{
FMTRUNE(f, t, s, '\'');
USED(s);
- f->nfmt += t - (char *)f->to;
+ f->nfmt += (int)(t - (char *)f->to);
f->to = t;
if(fl & FmtLeft && __fmtpad(f, w - q->nbytesout) < 0)
return -1;
@@ -214,9 +214,9 @@ __quotestrfmt(int runesin, Fmt *f)
if(f->flush)
outlen = 0x7FFFFFFF; /* if we can flush, no output limit */
else if(f->runes)
- outlen = (Rune*)f->stop - (Rune*)f->to;
+ outlen = (int)((Rune*)f->stop - (Rune*)f->to);
else
- outlen = (char*)f->stop - (char*)f->to;
+ outlen = (int)((char*)f->stop - (char*)f->to);
__quotesetup(s, r, nin, outlen, &q, f->flags&FmtSharp, f->runes);
/*print("bytes in %d bytes out %d runes in %d runesout %d\n", q.nbytesin, q.nbytesout, q.nrunesin, q.nrunesout); */
diff --git a/src/lib9/fmt/fmtrune.c b/src/lib9/fmt/fmtrune.c
index da8c5d746..2bc8d28e6 100644
--- a/src/lib9/fmt/fmtrune.c
+++ b/src/lib9/fmt/fmtrune.c
@@ -34,8 +34,8 @@ fmtrune(Fmt *f, int r)
n = 1;
}else{
t = (char*)f->to;
- FMTRUNE(f, t, f->stop, r);
- n = t - (char*)f->to;
+ FMTRUNE(f, t, f->stop, (Rune)r);
+ n = (int)(t - (char*)f->to);
f->to = t;
}
f->nfmt += n;
diff --git a/src/lib9/fmt/sprint.c b/src/lib9/fmt/sprint.c
index 38d430744..02655ad12 100644
--- a/src/lib9/fmt/sprint.c
+++ b/src/lib9/fmt/sprint.c
@@ -36,10 +36,10 @@ sprint(char *buf, char *fmt, ...)
* optimizes the test away. casting to uintptr works around this bug.
*/
if((uintptr)buf+len < (uintptr)buf)
- len = -(uintptr)buf-1;
+ len = (uint)-(uintptr)buf-1;
va_start(args, fmt);
- n = vsnprint(buf, len, fmt, args);
+ n = (int)vsnprint(buf, (int)len, fmt, args);
va_end(args);
return n;
}
diff --git a/src/lib9/fmt/strtod.c b/src/lib9/fmt/strtod.c
index 6bb56c112..ec185d2d5 100644
--- a/src/lib9/fmt/strtod.c
+++ b/src/lib9/fmt/strtod.c
@@ -124,7 +124,7 @@ fmtstrtod(const char *as, char **aas)
continue;
}
if(na < Ndig-50)
- a[na++] = c;
+ a[na++] = (char)c;
continue;
}
switch(c) {
@@ -240,7 +240,7 @@ fmtstrtod(const char *as, char **aas)
mid[0] = 0;
mid[1] = 1;
for(i=0; (c=a[i]) != '\0'; i++) {
- mid[0] = mid[0]*10 + (c-'0');
+ mid[0] = mid[0]*10 + (ulong)(c-'0');
mid[1] = mid[1]*10;
if(i >= 8)
break;
@@ -287,13 +287,13 @@ fmtstrtod(const char *as, char **aas)
/* only hard part is if even/odd roundings wants to go up */
c = mid[Prec-1] & (Sigbit-1);
if(c == Sigbit/2 && (mid[Prec-1]&Sigbit) == 0)
- mid[Prec-1] -= c;
+ mid[Prec-1] -= (ulong)c;
break; /* exactly mid */
}
/* normal rounding applies */
c = mid[Prec-1] & (Sigbit-1);
- mid[Prec-1] -= c;
+ mid[Prec-1] -= (ulong)c;
if(c >= Sigbit/2) {
mid[Prec-1] += Sigbit;
frnorm(mid);
@@ -317,7 +317,7 @@ retinf:
out:
d = 0;
for(i=0; i<Prec; i++)
- d = d*One + mid[i];
+ d = d*One + (double)mid[i];
if(flag & Fsign)
d = -d;
d = ldexp(d, bp - Prec*Nbits);
@@ -330,7 +330,8 @@ out:
static void
frnorm(ulong *f)
{
- int i, c;
+ int i;
+ ulong c;
c = 0;
for(i=Prec-1; i>0; i--) {
@@ -355,7 +356,7 @@ fpcmp(char *a, ulong* f)
for(i=0; i<Prec; i++)
tf[i] = tf[i]*10;
frnorm(tf);
- d = (tf[0] >> Nbits) + '0';
+ d = (int)(tf[0] >> Nbits) + '0';
tf[0] &= One-1;
/* compare next digit */
@@ -404,7 +405,7 @@ divby(char *a, int *na, int b)
for(;;) {
c = n>>b;
n -= c<<b;
- *p++ = c + '0';
+ *p++ = (char)(c + '0');
c = *a++;
if(c == 0)
break;
@@ -416,7 +417,7 @@ xx:
n = n*10;
c = n>>b;
n -= c<<b;
- *p++ = c + '0';
+ *p++ = (char)(c + '0');
(*na)++;
}
*p = 0;
@@ -447,7 +448,7 @@ divascii(char *a, int *na, int *dp, int *bp)
d = (int)(nelem(tab1))-1;
t = tab1 + d;
b = t->bp;
- if(memcmp(a, t->cmp, t->siz) > 0)
+ if(memcmp(a, t->cmp, (size_t)t->siz) > 0)
d--;
*dp -= d;
*bp += b;
@@ -470,14 +471,14 @@ mulby(char *a, char *p, char *q, int b)
n = c/10;
c -= n*10;
p--;
- *p = c + '0';
+ *p = (char)(c + '0');
}
while(n) {
c = n;
n = c/10;
c -= n*10;
p--;
- *p = c + '0';
+ *p = (char)(c + '0');
}
}
@@ -507,7 +508,7 @@ mulascii(char *a, int *na, int *dp, int *bp)
d = (int)(nelem(tab2))-1;
t = tab2 + d;
b = t->bp;
- if(memcmp(a, t->cmp, t->siz) < 0)
+ if(memcmp(a, t->cmp, (size_t)t->siz) < 0)
d--;
p = a + *na;
*bp -= b;
diff --git a/src/lib9/fmt/vsmprint.c b/src/lib9/fmt/vsmprint.c
index 4bd0bc4b7..46086f9e2 100644
--- a/src/lib9/fmt/vsmprint.c
+++ b/src/lib9/fmt/vsmprint.c
@@ -28,10 +28,10 @@ fmtStrFlush(Fmt *f)
if(f->start == nil)
return 0;
- n = (uintptr)f->farg;
+ n = (int)(uintptr)f->farg;
n *= 2;
s = (char*)f->start;
- f->start = realloc(s, n);
+ f->start = realloc(s, (size_t)n);
if(f->start == nil){
f->farg = nil;
f->to = nil;
@@ -53,7 +53,7 @@ fmtstrinit(Fmt *f)
memset(f, 0, sizeof *f);
f->runes = 0;
n = 32;
- f->start = malloc(n);
+ f->start = malloc((size_t)n);
if(f->start == nil)
return -1;
f->to = f->start;
diff --git a/src/lib9/fmt/vsnprint.c b/src/lib9/fmt/vsnprint.c
index 33d6bba4d..6b3877205 100644
--- a/src/lib9/fmt/vsnprint.c
+++ b/src/lib9/fmt/vsnprint.c
@@ -39,5 +39,5 @@ vsnprint(char *buf, int len, char *fmt, va_list args)
dofmt(&f, fmt);
VA_END(f.args);
*(char*)f.to = '\0';
- return (char*)f.to - buf;
+ return (int)((char*)f.to - buf);
}
diff --git a/src/lib9/getwd.c b/src/lib9/getwd.c
index 566d3f647..cbfd9d643 100644
--- a/src/lib9/getwd.c
+++ b/src/lib9/getwd.c
@@ -50,5 +50,5 @@ p9getwd(char *s, int ns)
return s;
}
- return getcwd(s, ns);
+ return getcwd(s, (size_t)ns);
}
diff --git a/src/lib9/readn.c b/src/lib9/readn.c
index f39b4a4c2..3c80a4fc0 100644
--- a/src/lib9/readn.c
+++ b/src/lib9/readn.c
@@ -36,7 +36,7 @@ readn(int f, void *av, long n)
a = av;
t = 0;
while(t < n){
- m = read(f, a+t, n-t);
+ m = read(f, a+t, (size_t)(n-t));
if(m <= 0){
if(t == 0)
return m;
diff --git a/src/lib9/rfork.c b/src/lib9/rfork.c
index 5a6eaeb94..c4ae90f97 100644
--- a/src/lib9/rfork.c
+++ b/src/lib9/rfork.c
@@ -82,7 +82,7 @@ p9rfork(int flags)
close(p[0]);
return -1;
}
- n = readn(p[0], buf, sizeof buf-1);
+ n = (int)readn(p[0], buf, sizeof buf-1);
close(p[0]);
if(!WIFEXITED(status) || WEXITSTATUS(status)!=0 || n <= 0){
if(!WIFEXITED(status))
@@ -102,7 +102,7 @@ p9rfork(int flags)
werrstr("%s", buf+2);
return -1;
}
- pid = strtol(buf, &q, 0);
+ pid = (int)strtol(buf, &q, 0);
}else{
/*
* Child - fork a new child whose wait message can't
diff --git a/src/lib9/run_unix.c b/src/lib9/run_unix.c
index c26cf0afb..3db33c76e 100644
--- a/src/lib9/run_unix.c
+++ b/src/lib9/run_unix.c
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd
#include <u.h>
#include <errno.h>
diff --git a/src/lib9/strecpy.c b/src/lib9/strecpy.c
index 389fdc8a0..03dc6ea50 100644
--- a/src/lib9/strecpy.c
+++ b/src/lib9/strecpy.c
@@ -32,7 +32,7 @@ strecpy(char *to, char *e, char *from)
{
if(to >= e)
return to;
- to = memccpy(to, from, '\0', e - to);
+ to = memccpy(to, from, '\0', (size_t)(e - to));
if(to == nil){
to = e - 1;
*to = '\0';
diff --git a/src/lib9/tempdir_unix.c b/src/lib9/tempdir_unix.c
index 99a7092b6..3ce87751b 100644
--- a/src/lib9/tempdir_unix.c
+++ b/src/lib9/tempdir_unix.c
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd
#include <u.h>
#include <dirent.h>
diff --git a/src/lib9/tokenize.c b/src/lib9/tokenize.c
index 52167ff2f..a9b593567 100644
--- a/src/lib9/tokenize.c
+++ b/src/lib9/tokenize.c
@@ -37,7 +37,7 @@ qtoken(char *s, char *sep)
quoting = 0;
t = s; /* s is output string, t is input string */
- while(*t!='\0' && (quoting || utfrune(sep, *t)==nil)){
+ while(*t!='\0' && (quoting || utfrune(sep, (Rune)*t)==nil)){
if(*t != '\''){
*s++ = *t++;
continue;
@@ -74,7 +74,7 @@ etoken(char *t, char *sep)
/* move to end of next token */
quoting = 0;
- while(*t!='\0' && (quoting || utfrune(sep, *t)==nil)){
+ while(*t!='\0' && (quoting || utfrune(sep, (Rune)*t)==nil)){
if(*t != '\''){
t++;
continue;
@@ -104,7 +104,7 @@ gettokens(char *s, char **args, int maxargs, char *sep)
int nargs;
for(nargs=0; nargs<maxargs; nargs++){
- while(*s!='\0' && utfrune(sep, *s)!=nil)
+ while(*s!='\0' && utfrune(sep, (Rune)*s)!=nil)
*s++ = '\0';
if(*s == '\0')
break;
@@ -121,7 +121,7 @@ tokenize(char *s, char **args, int maxargs)
int nargs;
for(nargs=0; nargs<maxargs; nargs++){
- while(*s!='\0' && utfrune(qsep, *s)!=nil)
+ while(*s!='\0' && utfrune(qsep, (Rune)*s)!=nil)
s++;
if(*s == '\0')
break;
diff --git a/src/lib9/utf/rune.c b/src/lib9/utf/rune.c
index 818771cfd..99f03eaa3 100644
--- a/src/lib9/utf/rune.c
+++ b/src/lib9/utf/rune.c
@@ -12,8 +12,6 @@
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
-#include <stdarg.h>
-#include <string.h>
#include "utf.h"
#include "utfdef.h"
@@ -82,7 +80,7 @@ charntorune(Rune *rune, const char *str, int length)
*/
c = *(uchar*)str;
if(c < Tx) {
- *rune = c;
+ *rune = (Rune)c;
return 1;
}
@@ -104,7 +102,7 @@ charntorune(Rune *rune, const char *str, int length)
l = ((c << Bitx) | c1) & Rune2;
if(l <= Rune1)
goto bad;
- *rune = l;
+ *rune = (Rune)l;
return 2;
}
@@ -126,7 +124,7 @@ charntorune(Rune *rune, const char *str, int length)
goto bad;
if (SurrogateMin <= l && l <= SurrogateMax)
goto bad;
- *rune = l;
+ *rune = (Rune)l;
return 3;
}
@@ -144,7 +142,7 @@ charntorune(Rune *rune, const char *str, int length)
l = ((((((c << Bitx) | c1) << Bitx) | c2) << Bitx) | c3) & Rune4;
if (l <= Rune3 || l > Runemax)
goto bad;
- *rune = l;
+ *rune = (Rune)l;
return 4;
}
@@ -180,7 +178,7 @@ chartorune(Rune *rune, const char *str)
*/
c = *(uchar*)str;
if(c < Tx) {
- *rune = c;
+ *rune = (Rune)c;
return 1;
}
@@ -197,7 +195,7 @@ chartorune(Rune *rune, const char *str)
l = ((c << Bitx) | c1) & Rune2;
if(l <= Rune1)
goto bad;
- *rune = l;
+ *rune = (Rune)l;
return 2;
}
@@ -214,7 +212,7 @@ chartorune(Rune *rune, const char *str)
goto bad;
if (SurrogateMin <= l && l <= SurrogateMax)
goto bad;
- *rune = l;
+ *rune = (Rune)l;
return 3;
}
@@ -229,7 +227,7 @@ chartorune(Rune *rune, const char *str)
l = ((((((c << Bitx) | c1) << Bitx) | c2) << Bitx) | c3) & Rune4;
if (l <= Rune3 || l > Runemax)
goto bad;
- *rune = l;
+ *rune = (Rune)l;
return 4;
}
@@ -265,7 +263,7 @@ runetochar(char *str, const Rune *rune)
*/
c = *rune;
if(c <= Rune1) {
- str[0] = c;
+ str[0] = (char)c;
return 1;
}
@@ -274,8 +272,8 @@ runetochar(char *str, const Rune *rune)
* 0080-07FF => T2 Tx
*/
if(c <= Rune2) {
- str[0] = T2 | (c >> 1*Bitx);
- str[1] = Tx | (c & Maskx);
+ str[0] = (char)(T2 | (c >> 1*Bitx));
+ str[1] = (char)(Tx | (c & Maskx));
return 2;
}
@@ -295,9 +293,9 @@ runetochar(char *str, const Rune *rune)
* 0800-FFFF => T3 Tx Tx
*/
if (c <= Rune3) {
- str[0] = T3 | (c >> 2*Bitx);
- str[1] = Tx | ((c >> 1*Bitx) & Maskx);
- str[2] = Tx | (c & Maskx);
+ str[0] = (char)(T3 | (c >> 2*Bitx));
+ str[1] = (char)(Tx | ((c >> 1*Bitx) & Maskx));
+ str[2] = (char)(Tx | (c & Maskx));
return 3;
}
@@ -305,10 +303,10 @@ runetochar(char *str, const Rune *rune)
* four character sequence (21-bit value)
* 10000-1FFFFF => T4 Tx Tx Tx
*/
- str[0] = T4 | (c >> 3*Bitx);
- str[1] = Tx | ((c >> 2*Bitx) & Maskx);
- str[2] = Tx | ((c >> 1*Bitx) & Maskx);
- str[3] = Tx | (c & Maskx);
+ str[0] = (char)(T4 | (c >> 3*Bitx));
+ str[1] = (char)(Tx | ((c >> 2*Bitx) & Maskx));
+ str[2] = (char)(Tx | ((c >> 1*Bitx) & Maskx));
+ str[3] = (char)(Tx | (c & Maskx));
return 4;
}
@@ -327,7 +325,7 @@ runenlen(const Rune *r, int nrune)
nb = 0;
while(nrune--) {
- c = *r++;
+ c = (int)*r++;
if (c <= Rune1)
nb++;
else if (c <= Rune2)
diff --git a/src/lib9/utf/utfecpy.c b/src/lib9/utf/utfecpy.c
index d6dc091c4..2eca85ef6 100644
--- a/src/lib9/utf/utfecpy.c
+++ b/src/lib9/utf/utfecpy.c
@@ -11,7 +11,7 @@
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
-#include <stdarg.h>
+#define _BSD_SOURCE 1
#include <string.h>
#include "utf.h"
#include "utfdef.h"
@@ -23,7 +23,7 @@ utfecpy(char *to, char *e, const char *from)
if(to >= e)
return to;
- end = memccpy(to, from, '\0', e - to);
+ end = memccpy(to, from, '\0', (size_t)(e - to));
if(end == nil){
end = e-1;
while(end>to && (*--end&0xC0)==0x80)
diff --git a/src/lib9/utf/utflen.c b/src/lib9/utf/utflen.c
index 45653d540..42fcb33ab 100644
--- a/src/lib9/utf/utflen.c
+++ b/src/lib9/utf/utflen.c
@@ -11,8 +11,6 @@
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
-#include <stdarg.h>
-#include <string.h>
#include "utf.h"
#include "utfdef.h"
@@ -20,7 +18,7 @@ int
utflen(const char *s)
{
int c;
- long n;
+ int n;
Rune rune;
n = 0;
diff --git a/src/lib9/utf/utfnlen.c b/src/lib9/utf/utfnlen.c
index d673c8290..d6ef5fa06 100644
--- a/src/lib9/utf/utfnlen.c
+++ b/src/lib9/utf/utfnlen.c
@@ -11,8 +11,6 @@
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
-#include <stdarg.h>
-#include <string.h>
#include "utf.h"
#include "utfdef.h"
@@ -20,7 +18,7 @@ int
utfnlen(const char *s, long m)
{
int c;
- long n;
+ int n;
Rune rune;
const char *es;
@@ -33,7 +31,7 @@ utfnlen(const char *s, long m)
s++;
continue;
}
- if(!fullrune(s, es-s))
+ if(!fullrune(s, (int)(es-s)))
break;
s += chartorune(&rune, s);
}
diff --git a/src/lib9/utf/utfrrune.c b/src/lib9/utf/utfrrune.c
index 95d2a9d8a..9e28af82a 100644
--- a/src/lib9/utf/utfrrune.c
+++ b/src/lib9/utf/utfrrune.c
@@ -11,7 +11,6 @@
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
-#include <stdarg.h>
#include <string.h>
#include "utf.h"
#include "utfdef.h"
@@ -25,7 +24,7 @@ utfrrune(const char *s, Rune c)
const char *s1;
if(c < Runesync) /* not part of utf sequence */
- return strrchr(s, c);
+ return strrchr(s, (char)c);
s1 = 0;
for(;;) {
diff --git a/src/lib9/utf/utfrune.c b/src/lib9/utf/utfrune.c
index b4017d26c..0136b2821 100644
--- a/src/lib9/utf/utfrune.c
+++ b/src/lib9/utf/utfrune.c
@@ -11,7 +11,6 @@
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
-#include <stdarg.h>
#include <string.h>
#include "utf.h"
#include "utfdef.h"
@@ -25,7 +24,7 @@ utfrune(const char *s, Rune c)
int n;
if(c < Runesync) /* not part of utf sequence */
- return strchr(s, c);
+ return strchr(s, (char)c);
for(;;) {
c1 = *(uchar*)s;
diff --git a/src/lib9/utf/utfutf.c b/src/lib9/utf/utfutf.c
index ec4923165..e46ddd923 100644
--- a/src/lib9/utf/utfutf.c
+++ b/src/lib9/utf/utfutf.c
@@ -11,7 +11,6 @@
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
-#include <stdarg.h>
#include <string.h>
#include "utf.h"
#include "utfdef.h"
@@ -26,7 +25,8 @@ char*
utfutf(const char *s1, const char *s2)
{
const char *p;
- long f, n1, n2;
+ long f, n1;
+ size_t n2;
Rune r;
n1 = chartorune(&r, s2);
@@ -35,7 +35,7 @@ utfutf(const char *s1, const char *s2)
return strstr(s1, s2);
n2 = strlen(s2);
- for(p=s1; (p=utfrune(p, f)) != 0; p+=n1)
+ for(p=s1; (p=utfrune(p, r)) != 0; p+=n1)
if(strncmp(p, s2, n2) == 0)
return p;
return 0;
diff --git a/src/lib9/windows.c b/src/lib9/windows.c
index d8ee402a2..082f33997 100644
--- a/src/lib9/windows.c
+++ b/src/lib9/windows.c
@@ -14,6 +14,7 @@ fork(void)
int
p9rfork(int flags)
{
+ USED(flags);
return -1;
}
diff --git a/src/libbio/bflush.c b/src/libbio/bflush.c
index 8a071cb5c..ea7ae2c62 100644
--- a/src/libbio/bflush.c
+++ b/src/libbio/bflush.c
@@ -37,7 +37,7 @@ Bflush(Biobuf *bp)
n = bp->bsize+bp->ocount;
if(n == 0)
return 0;
- c = write(bp->fid, bp->bbuf, n);
+ c = (int)write(bp->fid, bp->bbuf, (size_t)n);
if(n == c) {
bp->offset += n;
bp->ocount = -bp->bsize;
diff --git a/src/libbio/bgetc.c b/src/libbio/bgetc.c
index 52ed241f9..3399fb16b 100644
--- a/src/libbio/bgetc.c
+++ b/src/libbio/bgetc.c
@@ -49,7 +49,7 @@ loop:
* buffer to allow that many ungets.
*/
memmove(bp->bbuf-Bungetsize, bp->ebuf-Bungetsize, Bungetsize);
- i = read(bp->fid, bp->bbuf, bp->bsize);
+ i = (int)read(bp->fid, bp->bbuf, (size_t)bp->bsize);
bp->gbuf = bp->bbuf;
if(i <= 0) {
bp->state = Bracteof;
@@ -58,7 +58,7 @@ loop:
return Beof;
}
if(i < bp->bsize) {
- memmove(bp->ebuf-i-Bungetsize, bp->bbuf-Bungetsize, i+Bungetsize);
+ memmove(bp->ebuf-i-Bungetsize, bp->bbuf-Bungetsize, (size_t)(i+Bungetsize));
bp->gbuf = bp->ebuf-i;
}
bp->icount = -i;
@@ -67,6 +67,26 @@ loop:
}
int
+Bgetle2(Biobuf *bp)
+{
+ int l, h;
+
+ l = Bgetc(bp);
+ h = Bgetc(bp);
+ return l|(h<<8);
+}
+
+int
+Bgetle4(Biobuf *bp)
+{
+ int l, h;
+
+ l = Bgetle2(bp);
+ h = Bgetle2(bp);
+ return l|((uint32)h<<16);
+}
+
+int
Bungetc(Biobuf *bp)
{
diff --git a/src/libbio/bgetd.c b/src/libbio/bgetd.c
index cf76a755a..12d2c5b37 100644
--- a/src/libbio/bgetd.c
+++ b/src/libbio/bgetd.c
@@ -39,7 +39,7 @@ Bgetdf(void *vp)
int c;
struct bgetd *bg = vp;
- c = Bgetc(bg->b);
+ c = BGETC(bg->b);
if(c == Beof)
bg->eof = 1;
return c;
diff --git a/src/libbio/bgetrune.c b/src/libbio/bgetrune.c
index 1538f3ea7..441c07991 100644
--- a/src/libbio/bgetrune.c
+++ b/src/libbio/bgetrune.c
@@ -35,18 +35,18 @@ Bgetrune(Biobuf *bp)
Rune rune;
char str[UTFmax];
- c = Bgetc(bp);
+ c = BGETC(bp);
if(c < Runeself) { /* one char */
bp->runesize = 1;
return c;
}
- str[0] = c;
+ str[0] = (char)c;
for(i=1;;) {
- c = Bgetc(bp);
+ c = BGETC(bp);
if(c < 0)
return c;
- str[i++] = c;
+ str[i++] = (char)c;
if(fullrune(str, i)) {
bp->runesize = chartorune(&rune, str);
diff --git a/src/libbio/bprint.c b/src/libbio/bprint.c
index b5d3e9ece..301dc0c7f 100644
--- a/src/libbio/bprint.c
+++ b/src/libbio/bprint.c
@@ -49,7 +49,7 @@ bflush(Fmt *f)
return 0;
bp = f->farg;
- bp->ocount = (char*)f->to - (char*)f->stop;
+ bp->ocount = (int)((char*)f->to - (char*)f->stop);
if(Bflush(bp) < 0) {
f->stop = nil;
f->to = nil;
@@ -76,7 +76,7 @@ Bvprint(Biobuf *bp, char *fmt, va_list arg)
n = fmtvprint(&f, fmt, arg);
if(f.stop != nil)
- bp->ocount = (char*)f.to - (char*)f.stop;
+ bp->ocount = (int)((char*)f.to - (char*)f.stop);
return n;
}
diff --git a/src/libbio/bputc.c b/src/libbio/bputc.c
index 4cdbe8f7a..07b4789b7 100644
--- a/src/libbio/bputc.c
+++ b/src/libbio/bputc.c
@@ -35,7 +35,7 @@ Bputc(Biobuf *bp, int c)
for(;;) {
i = bp->ocount;
if(i) {
- bp->ebuf[i++] = c;
+ bp->ebuf[i++] = (unsigned char)c;
bp->ocount = i;
return 0;
}
@@ -44,3 +44,17 @@ Bputc(Biobuf *bp, int c)
}
return Beof;
}
+
+int
+Bputle2(Biobuf *bp, int c)
+{
+ Bputc(bp, c);
+ return Bputc(bp, c>>8);
+}
+
+int
+Bputle4(Biobuf *bp, int c)
+{
+ Bputle2(bp, c);
+ return Bputle2(bp, c>>16);
+}
diff --git a/src/libbio/bputrune.c b/src/libbio/bputrune.c
index e46f3c710..7fe0e6569 100644
--- a/src/libbio/bputrune.c
+++ b/src/libbio/bputrune.c
@@ -35,9 +35,9 @@ Bputrune(Biobuf *bp, long c)
char str[UTFmax];
int n;
- rune = c;
+ rune = (Rune)c;
if(rune < Runeself) {
- Bputc(bp, rune);
+ BPUTC(bp, (int)rune);
return 1;
}
n = runetochar(str, &rune);
diff --git a/src/libbio/brdline.c b/src/libbio/brdline.c
index a02bf106d..1c3093ecf 100644
--- a/src/libbio/brdline.c
+++ b/src/libbio/brdline.c
@@ -51,9 +51,9 @@ Brdline(Biobuf *bp, int delim)
* first try in remainder of buffer (gbuf doesn't change)
*/
ip = (char*)bp->ebuf - i;
- ep = memchr(ip, delim, i);
+ ep = memchr(ip, delim, (size_t)i);
if(ep) {
- j = (ep - ip) + 1;
+ j = (int)((ep - ip) + 1);
bp->rdline = j;
bp->icount += j;
return ip;
@@ -63,7 +63,7 @@ Brdline(Biobuf *bp, int delim)
* copy data to beginning of buffer
*/
if(i < bp->bsize)
- memmove(bp->bbuf, ip, i);
+ memmove(bp->bbuf, ip, (size_t)i);
bp->gbuf = bp->bbuf;
/*
@@ -71,12 +71,12 @@ Brdline(Biobuf *bp, int delim)
*/
ip = (char*)bp->bbuf + i;
while(i < bp->bsize) {
- j = read(bp->fid, ip, bp->bsize-i);
+ j = (int)read(bp->fid, ip, (size_t)(bp->bsize-i));
if(j <= 0) {
/*
* end of file with no delim
*/
- memmove(bp->ebuf-i, bp->bbuf, i);
+ memmove(bp->ebuf-i, bp->bbuf, (size_t)i);
bp->rdline = i;
bp->icount = -i;
bp->gbuf = bp->ebuf-i;
@@ -84,7 +84,7 @@ Brdline(Biobuf *bp, int delim)
}
bp->offset += j;
i += j;
- ep = memchr(ip, delim, j);
+ ep = memchr(ip, delim, (size_t)j);
if(ep) {
/*
* found in new piece
@@ -92,10 +92,10 @@ Brdline(Biobuf *bp, int delim)
*/
ip = (char*)bp->ebuf - i;
if(i < bp->bsize){
- memmove(ip, bp->bbuf, i);
+ memmove(ip, bp->bbuf, (size_t)i);
bp->gbuf = (unsigned char*)ip;
}
- j = (ep - (char*)bp->bbuf) + 1;
+ j = (int)((ep - (char*)bp->bbuf) + 1);
bp->rdline = j;
bp->icount = j - i;
return ip;
diff --git a/src/libbio/brdstr.c b/src/libbio/brdstr.c
index 0398ab07b..6a90cf69b 100644
--- a/src/libbio/brdstr.c
+++ b/src/libbio/brdstr.c
@@ -37,14 +37,14 @@ Brdstr(Biobuf *bp, int delim, int nulldelim)
linelen = Blinelen(bp);
if(n == 0 && linelen == 0)
return nil;
- nq = realloc(q, n+linelen+1);
+ nq = realloc(q, (size_t)(n+linelen+1));
if(nq == nil) {
free(q);
return nil;
}
q = nq;
if(p != nil) {
- memmove(q+n, p, linelen);
+ memmove(q+n, p, (size_t)linelen);
n += linelen;
if(nulldelim)
q[n-1] = '\0';
diff --git a/src/libbio/bread.c b/src/libbio/bread.c
index 5cf9a05c8..343a0bf29 100644
--- a/src/libbio/bread.c
+++ b/src/libbio/bread.c
@@ -41,11 +41,11 @@ Bread(Biobuf *bp, void *ap, long count)
while(c > 0) {
n = -ic;
if(n > c)
- n = c;
+ n = (int)c;
if(n == 0) {
if(bp->state != Bractive)
break;
- i = read(bp->fid, bp->bbuf, bp->bsize);
+ i = (int)read(bp->fid, bp->bbuf, (size_t)bp->bsize);
if(i <= 0) {
bp->state = Bracteof;
if(i < 0)
@@ -55,13 +55,13 @@ Bread(Biobuf *bp, void *ap, long count)
bp->gbuf = bp->bbuf;
bp->offset += i;
if(i < bp->bsize) {
- memmove(bp->ebuf-i, bp->bbuf, i);
+ memmove(bp->ebuf-i, bp->bbuf, (size_t)i);
bp->gbuf = bp->ebuf-i;
}
ic = -i;
continue;
}
- memmove(p, bp->ebuf+ic, n);
+ memmove(p, bp->ebuf+ic, (size_t)n);
c -= n;
ic += n;
p += n;
diff --git a/src/libbio/bseek.c b/src/libbio/bseek.c
index 291498108..eb426ccfc 100644
--- a/src/libbio/bseek.c
+++ b/src/libbio/bseek.c
@@ -62,9 +62,9 @@ Bseek(Biobuf *bp, vlong offset, int base)
*/
if(base == 0) {
d = n - Boffset(bp);
- bufsz = bp->ebuf - bp->gbuf;
+ bufsz = (int)(bp->ebuf - bp->gbuf);
if(-bufsz <= d && d <= bufsz){
- bp->icount += d;
+ bp->icount += (int)d;
if(d >= 0) {
if(bp->icount <= 0)
return n;
diff --git a/src/libbio/bwrite.c b/src/libbio/bwrite.c
index daed161cb..8b9943ab0 100644
--- a/src/libbio/bwrite.c
+++ b/src/libbio/bwrite.c
@@ -41,11 +41,11 @@ Bwrite(Biobuf *bp, void *ap, long count)
while(c > 0) {
n = -oc;
if(n > c)
- n = c;
+ n = (int)c;
if(n == 0) {
if(bp->state != Bwactive)
return Beof;
- i = write(bp->fid, bp->bbuf, bp->bsize);
+ i = (int)write(bp->fid, bp->bbuf, (size_t)bp->bsize);
if(i != bp->bsize) {
bp->state = Binactive;
return Beof;
@@ -54,7 +54,7 @@ Bwrite(Biobuf *bp, void *ap, long count)
oc = -bp->bsize;
continue;
}
- memmove(bp->ebuf+oc, p, n);
+ memmove(bp->ebuf+oc, p, (size_t)n);
oc += n;
c -= n;
p += n;
diff --git a/src/libmach/5obj.c b/src/libmach/5obj.c
index 57573b866..7fd3459a8 100644
--- a/src/libmach/5obj.c
+++ b/src/libmach/5obj.c
@@ -63,7 +63,7 @@ _read5(Biobuf *bp, Prog *p)
int as, n;
Addr a;
- as = Bgetc(bp); /* as */
+ as = BGETC(bp); /* as */
if(as < 0)
return 0;
p->kind = aNone;
@@ -74,11 +74,11 @@ _read5(Biobuf *bp, Prog *p)
p->sig = leswal(p->sig);
}
p->kind = aName;
- p->type = type2char(Bgetc(bp)); /* type */
- p->sym = Bgetc(bp); /* sym */
+ p->type = type2char(BGETC(bp)); /* type */
+ p->sym = BGETC(bp); /* sym */
n = 0;
for(;;) {
- as = Bgetc(bp);
+ as = BGETC(bp);
if(as < 0)
return 0;
n++;
@@ -112,11 +112,11 @@ addr(Biobuf *bp)
Addr a;
long off;
- a.type = Bgetc(bp); /* a.type */
+ a.type = BGETC(bp); /* a.type */
skip(bp,1); /* reg */
- a.sym = Bgetc(bp); /* sym index */
- a.name = Bgetc(bp); /* sym type */
- a.gotype = Bgetc(bp); /* go type */
+ a.sym = BGETC(bp); /* sym index */
+ a.name = BGETC(bp); /* sym type */
+ a.gotype = BGETC(bp); /* go type */
switch(a.type){
default:
case D_NONE:
@@ -130,18 +130,12 @@ addr(Biobuf *bp)
Bgetc(bp);
break;
case D_CONST2:
- Bgetc(bp);
- Bgetc(bp);
- Bgetc(bp);
- Bgetc(bp); // fall through
+ Bgetle4(bp); // fall through
case D_OREG:
case D_CONST:
case D_BRANCH:
case D_SHIFT:
- off = Bgetc(bp);
- off |= Bgetc(bp) << 8;
- off |= Bgetc(bp) << 16;
- off |= Bgetc(bp) << 24;
+ off = BGETLE4(bp);
if(off < 0)
off = -off;
if(a.sym && (a.name==D_PARAM || a.name==D_AUTO))
diff --git a/src/libmach/6obj.c b/src/libmach/6obj.c
index 578ae2b5e..1921c9e4c 100644
--- a/src/libmach/6obj.c
+++ b/src/libmach/6obj.c
@@ -52,7 +52,7 @@ _is6(char *t)
{
uchar *s = (uchar*)t;
- return s[0] == (ANAME&0xff) /* aslo = ANAME */
+ return s[0] == (ANAME&0xff) /* also = ANAME */
&& s[1] == ((ANAME>>8)&0xff)
&& s[2] == D_FILE /* type */
&& s[3] == 1 /* sym */
@@ -65,10 +65,10 @@ _read6(Biobuf *bp, Prog* p)
int as, n, c;
Addr a;
- as = Bgetc(bp); /* as(low) */
+ as = BGETC(bp); /* as(low) */
if(as < 0)
return 0;
- c = Bgetc(bp); /* as(high) */
+ c = BGETC(bp); /* as(high) */
if(c < 0)
return 0;
as |= ((c & 0xff) << 8);
@@ -80,11 +80,11 @@ _read6(Biobuf *bp, Prog* p)
p->sig = leswal(p->sig);
}
p->kind = aName;
- p->type = type2char(Bgetc(bp)); /* type */
- p->sym = Bgetc(bp); /* sym */
+ p->type = type2char(BGETC(bp)); /* type */
+ p->sym = BGETC(bp); /* sym */
n = 0;
for(;;) {
- as = Bgetc(bp);
+ as = BGETC(bp);
if(as < 0)
return 0;
n++;
@@ -122,40 +122,34 @@ addr(Biobuf *bp)
off = 0;
a.sym = -1;
- a.flags = Bgetc(bp); /* flags */
+ a.flags = BGETC(bp); /* flags */
a.gotype = 0;
if(a.flags & T_INDEX)
skip(bp, 2);
if(a.flags & T_OFFSET){
- l = Bgetc(bp);
- l |= Bgetc(bp) << 8;
- l |= Bgetc(bp) << 16;
- l |= Bgetc(bp) << 24;
+ l = BGETLE4(bp);
off = l;
if(a.flags & T_64){
- l = Bgetc(bp);
- l |= Bgetc(bp) << 8;
- l |= Bgetc(bp) << 16;
- l |= Bgetc(bp) << 24;
+ l = BGETLE4(bp);
off = ((vlong)l << 32) | (off & 0xFFFFFFFF);
}
if(off < 0)
- off = -off;
+ off = -(uvlong)off;
}
if(a.flags & T_SYM)
- a.sym = Bgetc(bp);
+ a.sym = BGETC(bp);
if(a.flags & T_FCONST)
skip(bp, 8);
else
if(a.flags & T_SCONST)
skip(bp, NSNAME);
if(a.flags & T_TYPE) {
- t = Bgetc(bp);
+ t = BGETC(bp);
if(a.sym > 0 && (t==D_PARAM || t==D_AUTO))
_offset(a.sym, off);
}
if(a.flags & T_GOTYPE)
- a.gotype = Bgetc(bp);
+ a.gotype = BGETC(bp);
return a;
}
diff --git a/src/libmach/8db.c b/src/libmach/8db.c
index 9ef02c428..cfc9cb99f 100644
--- a/src/libmach/8db.c
+++ b/src/libmach/8db.c
@@ -202,6 +202,11 @@ i386trace(Map *map, uvlong pc, uvlong sp, uvlong link, Tracer trace)
break;
if(s.value == morestack) {
+ // This code is old and won't work anymore.
+ // But no one uses it anyway.
+ // Leave it obviously broken until someone needs it.
+ werrstr("morestack not implemented correctly");
+ return -1;
// In the middle of morestack.
// Caller is m->morepc.
// Caller's caller is in m->morearg.
@@ -475,7 +480,14 @@ static Optable optab0FAE[8]=
[0x07] = { 0,0, "SFENCE" },
};
-/* 0F18 */
+static Optable optab0F18[4]=
+{
+[0x00] = { 0,0, "PREFETCHNTA %e" },
+[0x01] = { 0,0, "PREFECTCH0 %e" },
+[0x02] = { 0,0, "PREFECTCH1 %e" },
+[0x03] = { 0,0, "PREFECTCH2 %e" },
+};
+
/* 0F0D */
static Optable optab0FBA[8]=
@@ -518,6 +530,22 @@ static Optable optab0FC7[8]=
[0x01] = { 0,0, "CMPXCHG8B %e" },
};
+static Optable optab660F38[256]=
+{
+[0x00] = { RM,0, "PSHUFB %x,%X" },
+[0xdc] = { RM,0, "AESENC %x,%X" },
+[0xdb] = { RM,0, "AESIMC %x,%X," },
+[0xdd] = { RM,0, "AESENCLAST %x,%X" },
+[0xde] = { RM,0, "AESDEC %x,%X" },
+[0xdf] = { RM,0, "AESDECLAST %x,%X" },
+};
+
+static Optable optab660F3A[256]=
+{
+[0x22] = { RM,Ib, "PINSR%S %i,%e,%X" },
+[0xdf] = { RM,Ib, "AESKEYGENASSIST %i,%x,%X" },
+};
+
static Optable optab660F71[8]=
{
[0x02] = { Ib,0, "PSRLW %i,%X" },
@@ -545,12 +573,14 @@ static Optable optab660F[256]=
[0x2B] = { RM,0, "MOVNTPD %x,%e" },
[0x2E] = { RM,0, "UCOMISD %x,%X" },
[0x2F] = { RM,0, "COMISD %x,%X" },
+[0x38] = { AUX,0, optab660F38 },
+[0x3A] = { AUX,0, optab660F3A },
[0x5A] = { RM,0, "CVTPD2PS %x,%X" },
[0x5B] = { RM,0, "CVTPS2PL %x,%X" },
-[0x6A] = { RM,0, "PUNPCKHLQ %x,%X" },
-[0x6B] = { RM,0, "PACKSSLW %x,%X" },
-[0x6C] = { RM,0, "PUNPCKLQDQ %x,%X" },
-[0x6D] = { RM,0, "PUNPCKHQDQ %x,%X" },
+[0x6A] = { RM,0, "PUNPCKHLQ %x,%X" },
+[0x6B] = { RM,0, "PACKSSLW %x,%X" },
+[0x6C] = { RM,0, "PUNPCKLQDQ %x,%X" },
+[0x6D] = { RM,0, "PUNPCKHQDQ %x,%X" },
[0x6E] = { RM,0, "MOV%S %e,%X" },
[0x6F] = { RM,0, "MOVO %x,%X" }, /* MOVDQA */
[0x70] = { RM,Ib, "PSHUFL %i,%x,%X" },
@@ -569,6 +599,12 @@ static Optable optab660F[256]=
[0xF7] = { RM,0, "MASKMOVOU %x,%X" },
};
+static Optable optabF20F38[256]=
+{
+[0xf0] = { RM,0, "CRC32B %e, %r" },
+[0xf1] = { RM,0, "CRC32%S %e, %r" },
+};
+
static Optable optabF20F[256]=
{
[0x10] = { RM,0, "MOVSD %x,%X" },
@@ -576,6 +612,7 @@ static Optable optabF20F[256]=
[0x2A] = { RM,0, "CVTS%S2SD %e,%X" },
[0x2C] = { RM,0, "CVTTSD2S%S %x,%r" },
[0x2D] = { RM,0, "CVTSD2S%S %x,%r" },
+[0x38] = { AUX,0, optabF20F38 },
[0x5A] = { RM,0, "CVTSD2SS %x,%X" },
[0x6F] = { RM,0, "MOVOU %x,%X" },
[0x70] = { RM,Ib, "PSHUFLW %i,%x,%X" },
@@ -622,6 +659,7 @@ static Optable optab0F[256]=
[0x15] = { RM,0, "UNPCKH%s %x,%X" },
[0x16] = { RM,0, "MOV[L]H%s %x,%X" }, /* TO DO: L if source is XMM */
[0x17] = { RM,0, "MOVH%s %X,%x" },
+[0x18] = { RMOP,0, optab0F18 },
[0x1F] = { RM,0, "NOP%S %e" },
[0x20] = { RMR,0, "MOVL %C,%e" },
[0x21] = { RMR,0, "MOVL %D,%e" },
@@ -671,27 +709,27 @@ static Optable optab0F[256]=
[0x5D] = { RM,0, "MIN%s %x,%X" },
[0x5E] = { RM,0, "DIV%s %x,%X" }, /* TO DO: S/P S/D */
[0x5F] = { RM,0, "MAX%s %x,%X" },
-[0x60] = { RM,0, "PUNPCKLBW %m,%M" },
-[0x61] = { RM,0, "PUNPCKLWL %m,%M" },
-[0x62] = { RM,0, "PUNPCKLLQ %m,%M" },
-[0x63] = { RM,0, "PACKSSWB %m,%M" },
-[0x64] = { RM,0, "PCMPGTB %m,%M" },
-[0x65] = { RM,0, "PCMPGTW %m,%M" },
-[0x66] = { RM,0, "PCMPGTL %m,%M" },
-[0x67] = { RM,0, "PACKUSWB %m,%M" },
-[0x68] = { RM,0, "PUNPCKHBW %m,%M" },
-[0x69] = { RM,0, "PUNPCKHWL %m,%M" },
-[0x6A] = { RM,0, "PUNPCKHLQ %m,%M" },
-[0x6B] = { RM,0, "PACKSSLW %m,%M" },
-[0x6E] = { RM,0, "MOV%S %e,%M" },
-[0x6F] = { RM,0, "MOVQ %m,%M" },
+[0x60] = { RM,0, "PUNPCKLBW %m,%M" },
+[0x61] = { RM,0, "PUNPCKLWL %m,%M" },
+[0x62] = { RM,0, "PUNPCKLLQ %m,%M" },
+[0x63] = { RM,0, "PACKSSWB %m,%M" },
+[0x64] = { RM,0, "PCMPGTB %m,%M" },
+[0x65] = { RM,0, "PCMPGTW %m,%M" },
+[0x66] = { RM,0, "PCMPGTL %m,%M" },
+[0x67] = { RM,0, "PACKUSWB %m,%M" },
+[0x68] = { RM,0, "PUNPCKHBW %m,%M" },
+[0x69] = { RM,0, "PUNPCKHWL %m,%M" },
+[0x6A] = { RM,0, "PUNPCKHLQ %m,%M" },
+[0x6B] = { RM,0, "PACKSSLW %m,%M" },
+[0x6E] = { RM,0, "MOV%S %e,%M" },
+[0x6F] = { RM,0, "MOVQ %m,%M" },
[0x70] = { RM,Ib, "PSHUFW %i,%m,%M" },
-[0x74] = { RM,0, "PCMPEQB %m,%M" },
-[0x75] = { RM,0, "PCMPEQW %m,%M" },
-[0x76] = { RM,0, "PCMPEQL %m,%M" },
+[0x74] = { RM,0, "PCMPEQB %m,%M" },
+[0x75] = { RM,0, "PCMPEQW %m,%M" },
+[0x76] = { RM,0, "PCMPEQL %m,%M" },
[0x77] = { 0,0, "EMMS" },
-[0x7E] = { RM,0, "MOV%S %M,%e" },
-[0x7F] = { RM,0, "MOVQ %M,%m" },
+[0x7E] = { RM,0, "MOV%S %M,%e" },
+[0x7F] = { RM,0, "MOVQ %M,%m" },
[0xAE] = { RMOP,0, optab0FAE },
[0xAA] = { 0,0, "RSM" },
[0xB0] = { RM,0, "CMPXCHGB %r,%e" },
@@ -709,48 +747,48 @@ static Optable optab0F[256]=
[0xCD] = { 0,0, "BSWAP BP" },
[0xCE] = { 0,0, "BSWAP SI" },
[0xCF] = { 0,0, "BSWAP DI" },
-[0xD1] = { RM,0, "PSRLW %m,%M" },
-[0xD2] = { RM,0, "PSRLL %m,%M" },
-[0xD3] = { RM,0, "PSRLQ %m,%M" },
-[0xD5] = { RM,0, "PMULLW %m,%M" },
+[0xD1] = { RM,0, "PSRLW %m,%M" },
+[0xD2] = { RM,0, "PSRLL %m,%M" },
+[0xD3] = { RM,0, "PSRLQ %m,%M" },
+[0xD5] = { RM,0, "PMULLW %m,%M" },
[0xD6] = { RM,0, "MOVQOZX %m*,%X" },
-[0xD7] = { RM,0, "PMOVMSKB %m,%r" },
-[0xD8] = { RM,0, "PSUBUSB %m,%M" },
-[0xD9] = { RM,0, "PSUBUSW %m,%M" },
-[0xDA] = { RM,0, "PMINUB %m,%M" },
-[0xDB] = { RM,0, "PAND %m,%M" },
-[0xDC] = { RM,0, "PADDUSB %m,%M" },
-[0xDD] = { RM,0, "PADDUSW %m,%M" },
-[0xDE] = { RM,0, "PMAXUB %m,%M" },
-[0xDF] = { RM,0, "PANDN %m,%M" },
-[0xE0] = { RM,0, "PAVGB %m,%M" },
-[0xE1] = { RM,0, "PSRAW %m,%M" },
-[0xE2] = { RM,0, "PSRAL %m,%M" },
-[0xE3] = { RM,0, "PAVGW %m,%M" },
-[0xE4] = { RM,0, "PMULHUW %m,%M" },
-[0xE5] = { RM,0, "PMULHW %m,%M" },
+[0xD7] = { RM,0, "PMOVMSKB %m,%r" },
+[0xD8] = { RM,0, "PSUBUSB %m,%M" },
+[0xD9] = { RM,0, "PSUBUSW %m,%M" },
+[0xDA] = { RM,0, "PMINUB %m,%M" },
+[0xDB] = { RM,0, "PAND %m,%M" },
+[0xDC] = { RM,0, "PADDUSB %m,%M" },
+[0xDD] = { RM,0, "PADDUSW %m,%M" },
+[0xDE] = { RM,0, "PMAXUB %m,%M" },
+[0xDF] = { RM,0, "PANDN %m,%M" },
+[0xE0] = { RM,0, "PAVGB %m,%M" },
+[0xE1] = { RM,0, "PSRAW %m,%M" },
+[0xE2] = { RM,0, "PSRAL %m,%M" },
+[0xE3] = { RM,0, "PAVGW %m,%M" },
+[0xE4] = { RM,0, "PMULHUW %m,%M" },
+[0xE5] = { RM,0, "PMULHW %m,%M" },
[0xE7] = { RM,0, "MOVNTQ %M,%e" },
-[0xE8] = { RM,0, "PSUBSB %m,%M" },
-[0xE9] = { RM,0, "PSUBSW %m,%M" },
-[0xEA] = { RM,0, "PMINSW %m,%M" },
-[0xEB] = { RM,0, "POR %m,%M" },
-[0xEC] = { RM,0, "PADDSB %m,%M" },
-[0xED] = { RM,0, "PADDSW %m,%M" },
-[0xEE] = { RM,0, "PMAXSW %m,%M" },
-[0xEF] = { RM,0, "PXOR %m,%M" },
-[0xF1] = { RM,0, "PSLLW %m,%M" },
-[0xF2] = { RM,0, "PSLLL %m,%M" },
-[0xF3] = { RM,0, "PSLLQ %m,%M" },
+[0xE8] = { RM,0, "PSUBSB %m,%M" },
+[0xE9] = { RM,0, "PSUBSW %m,%M" },
+[0xEA] = { RM,0, "PMINSW %m,%M" },
+[0xEB] = { RM,0, "POR %m,%M" },
+[0xEC] = { RM,0, "PADDSB %m,%M" },
+[0xED] = { RM,0, "PADDSW %m,%M" },
+[0xEE] = { RM,0, "PMAXSW %m,%M" },
+[0xEF] = { RM,0, "PXOR %m,%M" },
+[0xF1] = { RM,0, "PSLLW %m,%M" },
+[0xF2] = { RM,0, "PSLLL %m,%M" },
+[0xF3] = { RM,0, "PSLLQ %m,%M" },
[0xF4] = { RM,0, "PMULULQ %m,%M" },
-[0xF5] = { RM,0, "PMADDWL %m,%M" },
-[0xF6] = { RM,0, "PSADBW %m,%M" },
+[0xF5] = { RM,0, "PMADDWL %m,%M" },
+[0xF6] = { RM,0, "PSADBW %m,%M" },
[0xF7] = { RMR,0, "MASKMOVQ %m,%M" },
-[0xF8] = { RM,0, "PSUBB %m,%M" },
-[0xF9] = { RM,0, "PSUBW %m,%M" },
-[0xFA] = { RM,0, "PSUBL %m,%M" },
-[0xFC] = { RM,0, "PADDB %m,%M" },
-[0xFD] = { RM,0, "PADDW %m,%M" },
-[0xFE] = { RM,0, "PADDL %m,%M" },
+[0xF8] = { RM,0, "PSUBB %m,%M" },
+[0xF9] = { RM,0, "PSUBW %m,%M" },
+[0xFA] = { RM,0, "PSUBL %m,%M" },
+[0xFC] = { RM,0, "PADDB %m,%M" },
+[0xFD] = { RM,0, "PADDW %m,%M" },
+[0xFE] = { RM,0, "PADDL %m,%M" },
[0x80] = { Iwds,0, "JOS %p" },
[0x81] = { Iwds,0, "JOC %p" },
@@ -945,9 +983,9 @@ static Optable optabD9[64+8] =
[0x00] = { 0,0, "FMOVF %e,F0" },
[0x02] = { 0,0, "FMOVF F0,%e" },
[0x03] = { 0,0, "FMOVFP F0,%e" },
-[0x04] = { 0,0, "FLDENV%S %e" },
+[0x04] = { 0,0, "FLDENV%S %e" },
[0x05] = { 0,0, "FLDCW %e" },
-[0x06] = { 0,0, "FSTENV%S %e" },
+[0x06] = { 0,0, "FSTENV%S %e" },
[0x07] = { 0,0, "FSTCW %e" },
[0x08] = { 0,0, "FMOVD F0,F0" }, /* Mod R/M = 11xx xxxx*/
[0x09] = { 0,0, "FMOVD F1,F0" },
@@ -1094,14 +1132,14 @@ static Optable optabDD[8+8] =
[0x00] = { 0,0, "FMOVD %e,F0" },
[0x02] = { 0,0, "FMOVD F0,%e" },
[0x03] = { 0,0, "FMOVDP F0,%e" },
-[0x04] = { 0,0, "FRSTOR%S %e" },
-[0x06] = { 0,0, "FSAVE%S %e" },
+[0x04] = { 0,0, "FRSTOR%S %e" },
+[0x06] = { 0,0, "FSAVE%S %e" },
[0x07] = { 0,0, "FSTSW %e" },
[0x08] = { 0,0, "FFREED %f" },
[0x0a] = { 0,0, "FMOVD %f,F0" },
[0x0b] = { 0,0, "FMOVDP %f,F0" },
[0x0c] = { 0,0, "FUCOMD %f,F0" },
-[0x0d] = { 0,0, "FUCOMDP %f,F0" },
+[0x0d] = { 0,0, "FUCOMDP %f,F0" },
};
static Optable optabDE[8+8] =
@@ -1117,9 +1155,9 @@ static Optable optabDE[8+8] =
[0x08] = { 0,0, "FADDDP F0,%f" },
[0x09] = { 0,0, "FMULDP F0,%f" },
[0x0b] = { Op_R1,0, "FCOMPDP" },
-[0x0c] = { 0,0, "FSUBRDP F0,%f" },
+[0x0c] = { 0,0, "FSUBRDP F0,%f" },
[0x0d] = { 0,0, "FSUBDP F0,%f" },
-[0x0e] = { 0,0, "FDIVRDP F0,%f" },
+[0x0e] = { 0,0, "FDIVRDP F0,%f" },
[0x0f] = { 0,0, "FDIVDP F0,%f" },
};
@@ -1858,14 +1896,23 @@ badop:
return 0;
obase = (Optable*)op->proto;
switch (ip->opre) {
- case 0x66: op = optab660F; break;
- case 0xF2: op = optabF20F; break;
- case 0xF3: op = optabF30F; break;
- default: op = nil; break;
+ case 0x66:
+ op = optab660F;
+ break;
+ case 0xF2:
+ op = optabF20F;
+ ip->prefix = 0; /* discard REPNE */
+ break;
+ case 0xF3:
+ op = optabF30F;
+ ip->prefix = 0; /* discard REP */
+ break;
+ default:
+ op = nil;
+ break;
}
if(op != nil && op[c].proto != nil)
obase = op;
- norex = 1; /* no more rex prefixes */
/* otherwise the optab entry captures it */
goto newop;
case AUX: /* Multi-byte op code - Auxiliary table */
@@ -1880,8 +1927,6 @@ badop:
ip->prefix = (char*)op->proto;
if (igetc(map, ip, &c) < 0)
return 0;
- if (ip->opre && c == 0x0F)
- ip->prefix = 0;
goto newop;
case SEG: /* Segment Prefix */
ip->segment = (char*)op->proto;
diff --git a/src/libmach/8obj.c b/src/libmach/8obj.c
index af4ebc877..c44d92c55 100644
--- a/src/libmach/8obj.c
+++ b/src/libmach/8obj.c
@@ -52,7 +52,7 @@ _is8(char *t)
{
uchar *s = (uchar*)t;
- return s[0] == (ANAME&0xff) /* aslo = ANAME */
+ return s[0] == (ANAME&0xff) /* also = ANAME */
&& s[1] == ((ANAME>>8)&0xff)
&& s[2] == D_FILE /* type */
&& s[3] == 1 /* sym */
@@ -65,10 +65,10 @@ _read8(Biobuf *bp, Prog* p)
int as, n, c;
Addr a;
- as = Bgetc(bp); /* as(low) */
+ as = BGETC(bp); /* as(low) */
if(as < 0)
return 0;
- c = Bgetc(bp); /* as(high) */
+ c = BGETC(bp); /* as(high) */
if(c < 0)
return 0;
as |= ((c & 0xff) << 8);
@@ -80,11 +80,11 @@ _read8(Biobuf *bp, Prog* p)
p->sig = leswal(p->sig);
}
p->kind = aName;
- p->type = type2char(Bgetc(bp)); /* type */
- p->sym = Bgetc(bp); /* sym */
+ p->type = type2char(BGETC(bp)); /* type */
+ p->sym = BGETC(bp); /* sym */
n = 0;
for(;;) {
- as = Bgetc(bp);
+ as = BGETC(bp);
if(as < 0)
return 0;
n++;
@@ -122,37 +122,31 @@ addr(Biobuf *bp)
off = 0;
a.gotype = 0;
a.sym = -1;
- a.flags = Bgetc(bp); /* flags */
+ a.flags = BGETC(bp); /* flags */
if(a.flags & T_INDEX)
skip(bp, 2);
if(a.flags & T_OFFSET){
- off = Bgetc(bp);
- off |= Bgetc(bp) << 8;
- off |= Bgetc(bp) << 16;
- off |= Bgetc(bp) << 24;
+ off = BGETLE4(bp);
if(off < 0)
off = -off;
}
if(a.flags & T_OFFSET2){
- Bgetc(bp);
- Bgetc(bp);
- Bgetc(bp);
- Bgetc(bp);
+ Bgetle4(bp);
}
if(a.flags & T_SYM)
- a.sym = Bgetc(bp);
+ a.sym = BGETC(bp);
if(a.flags & T_FCONST)
skip(bp, 8);
else
if(a.flags & T_SCONST)
skip(bp, NSNAME);
if(a.flags & T_TYPE) {
- t = Bgetc(bp);
+ t = BGETC(bp);
if(a.sym > 0 && (t==D_PARAM || t==D_AUTO))
_offset(a.sym, off);
}
if(a.flags & T_GOTYPE)
- a.gotype = Bgetc(bp);
+ a.gotype = BGETC(bp);
return a;
}
diff --git a/src/libmach/darwin.c b/src/libmach/darwin.c
index 753d940fd..807dfa0d8 100644
--- a/src/libmach/darwin.c
+++ b/src/libmach/darwin.c
@@ -391,6 +391,7 @@ detachproc(Map *m)
int
procnotes(int pid, char ***pnotes)
{
+ USED(pid);
*pnotes = 0;
return 0;
}
@@ -400,6 +401,7 @@ procnotes(int pid, char ***pnotes)
char*
proctextfile(int pid)
{
+ USED(pid);
return nil;
}
@@ -410,6 +412,8 @@ machsegrw(Map *map, Seg *seg, uvlong addr, void *v, uint n, int isr)
mach_port_t task;
int r;
+ USED(seg);
+
task = idtotask(map->pid);
if(task == -1)
return -1;
@@ -544,6 +548,8 @@ machregrw(Map *map, Seg *seg, uvlong addr, void *v, uint n, int isr)
} u;
uchar *p;
+ USED(seg);
+
if(n > 8){
werrstr("asked for %d-byte register", n);
return -1;
@@ -711,6 +717,9 @@ catch_exception_raise(mach_port_t eport, mach_port_t thread,
Thread *t;
int i;
+ USED(eport);
+ USED(task);
+
t = nil;
for(i=0; i<nthr; i++){
if(thr[i].thread == thread){
@@ -751,6 +760,7 @@ havet:
static void*
excthread(void *v)
{
+ USED(v);
extern boolean_t exc_server(mach_msg_header_t *, mach_msg_header_t *);
mach_msg_server(exc_server, 2048, excport, 0);
return 0;
diff --git a/src/libmach/dragonfly.c b/src/libmach/dragonfly.c
new file mode 100644
index 000000000..43dd005e9
--- /dev/null
+++ b/src/libmach/dragonfly.c
@@ -0,0 +1,62 @@
+// This is stubbed out for the moment. Will revisit when the time comes.
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+
+int
+ctlproc(int pid, char *msg)
+{
+ USED(pid);
+ USED(msg);
+
+ sysfatal("ctlproc unimplemented in DragonFly");
+ return -1;
+}
+
+char*
+proctextfile(int pid)
+{
+ USED(pid);
+
+ sysfatal("proctextfile unimplemented in DragonFly");
+ return nil;
+}
+
+char*
+procstatus(int pid)
+{
+ USED(pid);
+
+ sysfatal("procstatus unimplemented in DragonFly");
+ return nil;
+}
+
+Map*
+attachproc(int pid, Fhdr *fp)
+{
+ USED(pid);
+ USED(fp);
+
+ sysfatal("attachproc unimplemented in DragonFly");
+ return nil;
+}
+
+void
+detachproc(Map *m)
+{
+ USED(m);
+
+ sysfatal("detachproc unimplemented in DragonFly");
+}
+
+int
+procthreadpids(int pid, int *p, int np)
+{
+ USED(pid);
+ USED(p);
+ USED(np);
+
+ sysfatal("procthreadpids unimplemented in DragonFly");
+ return -1;
+}
diff --git a/src/libmach/executable.c b/src/libmach/executable.c
index 91c0cbe76..eae14441a 100644
--- a/src/libmach/executable.c
+++ b/src/libmach/executable.c
@@ -1076,7 +1076,7 @@ machdotout(int fd, Fhdr *fp, ExecHdr *hp)
return 0;
}
- if (mp->cpusubtype != MACH_CPU_SUBTYPE_X86) {
+ if (mp->cpusubtype != MACH_CPU_SUBTYPE_X86 && mp->cpusubtype != MACH_CPU_SUBTYPE_X86_64) {
werrstr("bad MACH cpu subtype - not amd64");
return 0;
}
diff --git a/src/libmach/freebsd.c b/src/libmach/freebsd.c
index 45de966ec..c4e5efddf 100644
--- a/src/libmach/freebsd.c
+++ b/src/libmach/freebsd.c
@@ -7,6 +7,9 @@
int
ctlproc(int pid, char *msg)
{
+ USED(pid);
+ USED(msg);
+
sysfatal("ctlproc unimplemented in FreeBSD");
return -1;
}
@@ -14,6 +17,8 @@ ctlproc(int pid, char *msg)
char*
proctextfile(int pid)
{
+ USED(pid);
+
sysfatal("proctextfile unimplemented in FreeBSD");
return nil;
}
@@ -21,6 +26,8 @@ proctextfile(int pid)
char*
procstatus(int pid)
{
+ USED(pid);
+
sysfatal("procstatus unimplemented in FreeBSD");
return nil;
}
@@ -28,6 +35,9 @@ procstatus(int pid)
Map*
attachproc(int pid, Fhdr *fp)
{
+ USED(pid);
+ USED(fp);
+
sysfatal("attachproc unimplemented in FreeBSD");
return nil;
}
@@ -35,12 +45,18 @@ attachproc(int pid, Fhdr *fp)
void
detachproc(Map *m)
{
+ USED(m);
+
sysfatal("detachproc unimplemented in FreeBSD");
}
int
procthreadpids(int pid, int *p, int np)
{
+ USED(pid);
+ USED(p);
+ USED(np);
+
sysfatal("procthreadpids unimplemented in FreeBSD");
return -1;
}
diff --git a/src/libmach/macho.h b/src/libmach/macho.h
index df039d048..9dfea5a85 100644
--- a/src/libmach/macho.h
+++ b/src/libmach/macho.h
@@ -87,6 +87,7 @@ enum {
MACH_CPU_TYPE_X86_64 = (1<<24)|7,
MACH_CPU_TYPE_X86 = 7,
MACH_CPU_SUBTYPE_X86 = 3,
+ MACH_CPU_SUBTYPE_X86_64 = (1<<31)|3,
MACH_EXECUTABLE_TYPE = 2,
MACH_SEGMENT_32 = 1, /* 32-bit mapped segment */
MACH_SEGMENT_64 = 0x19, /* 64-bit mapped segment */
diff --git a/src/libmach/netbsd.c b/src/libmach/netbsd.c
index 03e08d9e8..adeeff333 100644
--- a/src/libmach/netbsd.c
+++ b/src/libmach/netbsd.c
@@ -7,6 +7,8 @@
int
ctlproc(int pid, char *msg)
{
+ USED(pid);
+ USED(msg);
sysfatal("ctlproc unimplemented in NetBSD");
return -1;
}
@@ -14,6 +16,7 @@ ctlproc(int pid, char *msg)
char*
proctextfile(int pid)
{
+ USED(pid);
sysfatal("proctextfile unimplemented in NetBSD");
return nil;
}
@@ -21,6 +24,7 @@ proctextfile(int pid)
char*
procstatus(int pid)
{
+ USED(pid);
sysfatal("procstatus unimplemented in NetBSD");
return nil;
}
@@ -28,6 +32,8 @@ procstatus(int pid)
Map*
attachproc(int pid, Fhdr *fp)
{
+ USED(pid);
+ USED(fp);
sysfatal("attachproc unimplemented in NetBSD");
return nil;
}
@@ -35,12 +41,16 @@ attachproc(int pid, Fhdr *fp)
void
detachproc(Map *m)
{
+ USED(m);
sysfatal("detachproc unimplemented in NetBSD");
}
int
procthreadpids(int pid, int *p, int np)
{
+ USED(pid);
+ USED(p);
+ USED(np);
sysfatal("procthreadpids unimplemented in NetBSD");
return -1;
}
diff --git a/src/libmach/obj.c b/src/libmach/obj.c
index 2a5e04758..729a3eab8 100644
--- a/src/libmach/obj.c
+++ b/src/libmach/obj.c
@@ -132,16 +132,16 @@ objtype(Biobuf *bp, char **name)
* Found one. Skip until "\n!\n"
*/
for(;;) {
- if((c = Bgetc(bp)) == Beof)
+ if((c = BGETC(bp)) == Beof)
return -1;
if(c != '\n')
continue;
- c = Bgetc(bp);
+ c = BGETC(bp);
if(c != '!'){
Bungetc(bp);
continue;
}
- c = Bgetc(bp);
+ c = BGETC(bp);
if(c != '\n'){
Bungetc(bp);
continue;
@@ -244,7 +244,7 @@ processprog(Prog *p, int doautos)
static void
objlookup(int id, char *name, int type, uint sig)
{
- int32 h;
+ uint32 h;
char *cp;
Sym *s;
Symtab *sp;
diff --git a/src/libmach/openbsd.c b/src/libmach/openbsd.c
index d919383f3..ace8a22a5 100644
--- a/src/libmach/openbsd.c
+++ b/src/libmach/openbsd.c
@@ -7,6 +7,8 @@
int
ctlproc(int pid, char *msg)
{
+ USED(pid);
+ USED(msg);
sysfatal("ctlproc unimplemented in OpenBSD");
return -1;
}
@@ -14,6 +16,7 @@ ctlproc(int pid, char *msg)
char*
proctextfile(int pid)
{
+ USED(pid);
sysfatal("proctextfile unimplemented in OpenBSD");
return nil;
}
@@ -21,6 +24,7 @@ proctextfile(int pid)
char*
procstatus(int pid)
{
+ USED(pid);
sysfatal("procstatus unimplemented in OpenBSD");
return nil;
}
@@ -28,6 +32,8 @@ procstatus(int pid)
Map*
attachproc(int pid, Fhdr *fp)
{
+ USED(pid);
+ USED(fp);
sysfatal("attachproc unimplemented in OpenBSD");
return nil;
}
@@ -35,12 +41,16 @@ attachproc(int pid, Fhdr *fp)
void
detachproc(Map *m)
{
+ USED(m);
sysfatal("detachproc unimplemented in OpenBSD");
}
int
procthreadpids(int pid, int *p, int np)
{
+ USED(pid);
+ USED(p);
+ USED(np);
sysfatal("procthreadpids unimplemented in OpenBSD");
return -1;
}
diff --git a/src/libmach/sym.c b/src/libmach/sym.c
index 28c80d641..474cc0c62 100644
--- a/src/libmach/sym.c
+++ b/src/libmach/sym.c
@@ -103,6 +103,17 @@ static int txtcomp(const void*, const void*);
static int filecomp(const void*, const void*);
/*
+ * Go 1.2 pcln table (also contains pcsp).
+ */
+#define Go12PclnMagic 0xfffffffb
+#define Go12PclnMagicRev 0xfbffffff
+static int isgo12pcline(void);
+static uvlong go12pc2sp(uvlong);
+static int32 go12fileline(char*, int, uvlong);
+static void go12clean(void);
+static uvlong go12file2pc(char*, int);
+
+/*
* initialize the symbol tables
*/
int
@@ -360,13 +371,13 @@ decodename(Biobuf *bp, Sym *p)
p->type &= ~0x80;
if(p->type == 'z' || p->type == 'Z') {
o = Bseek(bp, 0, 1);
- if(Bgetc(bp) < 0) {
+ if(BGETC(bp) < 0) {
werrstr("can't read symbol name");
return -1;
}
for(;;) {
- c1 = Bgetc(bp);
- c2 = Bgetc(bp);
+ c1 = BGETC(bp);
+ c2 = BGETC(bp);
if(c1 < 0 || c2 < 0) {
werrstr("can't read symbol name");
return -1;
@@ -444,6 +455,7 @@ cleansyms(void)
if(pcline)
free(pcline);
pcline = 0;
+ go12clean();
}
/*
@@ -998,12 +1010,14 @@ file2pc(char *file, int32 line)
uvlong pc, start, end;
short *name;
+ if(isgo12pcline())
+ return go12file2pc(file, line);
if(buildtbls() == 0 || files == 0)
- return ~0;
+ return ~(uvlong)0;
name = encfname(file);
if(name == 0) { /* encode the file name */
werrstr("file %s not found", file);
- return ~0;
+ return ~(uvlong)0;
}
/* find this history stack */
for(i = 0, fp = files; i < nfiles; i++, fp++)
@@ -1012,7 +1026,7 @@ file2pc(char *file, int32 line)
free(name);
if(i >= nfiles) {
werrstr("line %d in file %s not found", line, file);
- return ~0;
+ return ~(uvlong)0;
}
start = fp->addr; /* first text addr this file */
if(i < nfiles-1)
@@ -1026,9 +1040,9 @@ file2pc(char *file, int32 line)
if(debug)
print("find pc for %d - between: %llux and %llux\n", line, start, end);
pc = line2addr(line, start, end);
- if(pc == ~0) {
+ if(pc == ~(uvlong)0) {
werrstr("line %d not in file %s", line, file);
- return ~0;
+ return ~(uvlong)0;
}
return pc;
}
@@ -1169,6 +1183,9 @@ fileline(char *str, int n, uvlong dot)
int32 line, top, bot, mid;
File *f;
+ if(isgo12pcline())
+ return go12fileline(str, n, dot);
+
*str = 0;
if(buildtbls() == 0)
return 0;
@@ -1368,13 +1385,16 @@ pc2sp(uvlong pc)
uchar *c, u;
uvlong currpc, currsp;
+ if(isgo12pcline())
+ return go12pc2sp(pc);
+
if(spoff == 0)
- return ~0;
+ return ~(uvlong)0;
currsp = 0;
currpc = txtstart - mach->pcquant;
if(pc<currpc || pc>txtend)
- return ~0;
+ return ~(uvlong)0;
for(c = spoff; c < spoffend; c++) {
if (currpc >= pc)
return currsp;
@@ -1391,7 +1411,7 @@ pc2sp(uvlong pc)
currpc += mach->pcquant*(u-129);
currpc += mach->pcquant;
}
- return ~0;
+ return ~(uvlong)0;
}
/*
@@ -1412,7 +1432,7 @@ pc2line(uvlong pc)
else
currpc = txtstart-mach->pcquant;
if(pc<currpc || pc>txtend)
- return ~0;
+ return -1;
for(c = pcline; c < pclineend && currpc < pc; c++) {
u = *c;
@@ -1448,11 +1468,11 @@ line2addr(int32 line, uvlong basepc, uvlong endpc)
int found;
if(pcline == 0 || line == 0)
- return ~0;
+ return ~(uvlong)0;
currline = 0;
currpc = txtstart-mach->pcquant;
- pc = ~0;
+ pc = ~(uvlong)0;
found = 0;
delta = HUGEINT;
@@ -1485,7 +1505,7 @@ line2addr(int32 line, uvlong basepc, uvlong endpc)
}
if(found)
return pc;
- return ~0;
+ return ~(uvlong)0;
}
/*
@@ -1539,3 +1559,325 @@ dumphist(char *name)
free(fname);
}
#endif
+
+// Go 1.2 pcln table
+// See golang.org/s/go12symtab.
+
+// Func layout
+#define FuncEntry (0)
+#define FuncName (pcptrsize)
+#define FuncArgs (pcptrsize+4)
+#define FuncFrame (pcptrsize+2*4)
+#define FuncPCSP (pcptrsize+3*4)
+#define FuncPCFile (pcptrsize+4*4)
+#define FuncPCLine (pcptrsize+5*4)
+
+static int32 pcquantum;
+static int32 pcptrsize;
+static uvlong (*pcswav)(uvlong);
+static uint32 (*pcswal)(uint32);
+static uvlong (*pcuintptr)(uchar*);
+static uchar *functab;
+static uint32 nfunctab;
+static uint32 *filetab;
+static uint32 nfiletab;
+
+static uint32
+xswal(uint32 v)
+{
+ return (v>>24) | ((v>>8)&0xFF00) | ((v<<8)&0xFF0000) | v<<24;
+}
+
+static uvlong
+xswav(uvlong v)
+{
+ return (uvlong)xswal(v)<<32 | xswal(v>>32);
+}
+
+static uvlong
+noswav(uvlong v)
+{
+ return v;
+}
+
+static uint32
+noswal(uint32 v)
+{
+ return v;
+}
+
+static uvlong
+readuintptr64(uchar *p)
+{
+ return pcswav(*(uvlong*)p);
+}
+
+static uvlong
+readuintptr32(uchar *p)
+{
+ return pcswal(*(uint32*)p);
+}
+
+static void
+go12clean(void)
+{
+ pcquantum = 0;
+ pcswav = nil;
+ pcswal = nil;
+ functab = nil;
+ nfunctab = 0;
+ filetab = nil;
+ nfiletab = 0;
+}
+
+static void
+go12init(void)
+{
+ uint32 m;
+ uchar *p;
+
+ if(pcquantum != 0)
+ return;
+ pcquantum = -1; // not go 1.2
+ if(pcline == nil || pclineend - pcline < 16 ||
+ pcline[4] != 0 || pcline[5] != 0 ||
+ (pcline[6] != 1 && pcline[6] != 4) ||
+ (pcline[7] != 4 && pcline[7] != 8))
+ return;
+
+ // header is magic, 00 00 pcquantum ptrsize
+ m = *(uint32*)pcline;
+ if(m == Go12PclnMagic) {
+ pcswav = noswav;
+ pcswal = noswal;
+ } else {
+ pcswav = xswav;
+ pcswal = xswal;
+ }
+ pcptrsize = pcline[7];
+
+ if(pcptrsize == 4)
+ pcuintptr = readuintptr32;
+ else
+ pcuintptr = readuintptr64;
+
+ nfunctab = pcuintptr(pcline+8);
+ functab = pcline + 8 + pcptrsize;
+
+ // functab is 2*nfunctab pointer-sized values.
+ // The offset to the file table follows.
+ p = functab + nfunctab*2*pcptrsize + pcptrsize;
+ if(p+4 > pclineend)
+ return;
+ filetab = (uint32*)(pcline + pcswal(*(uint32*)p));
+ if((uchar*)filetab+4 > pclineend)
+ return;
+
+ // File table begins with count.
+ nfiletab = pcswal(filetab[0]);
+ if((uchar*)(filetab + nfiletab) > pclineend)
+ return;
+
+ // Committed.
+ pcquantum = pcline[6];
+}
+
+static int
+isgo12pcline(void)
+{
+ go12init();
+ return pcquantum > 0;
+}
+
+static uchar*
+go12findfunc(uvlong pc)
+{
+ uchar *f, *fm;
+ int32 nf, m;
+
+ if(pc < pcuintptr(functab) || pc >= pcuintptr(functab+2*nfunctab*pcptrsize))
+ return nil;
+
+ // binary search to find func with entry <= addr.
+ f = functab;
+ nf = nfunctab;
+ while(nf > 0) {
+ m = nf/2;
+ fm = f + 2*pcptrsize*m;
+ if(pcuintptr(fm) <= pc && pc < pcuintptr(fm+2*pcptrsize)) {
+ f = pcline + pcuintptr(fm+pcptrsize);
+ if(f >= pclineend)
+ return nil;
+ return f;
+ } else if(pc < pcuintptr(fm))
+ nf = m;
+ else {
+ f += (m+1)*2*pcptrsize;
+ nf -= m+1;
+ }
+ }
+ return nil;
+}
+
+static uint32
+readvarint(uchar **pp)
+{
+ uchar *p;
+ uint32 v;
+ int32 shift;
+
+ v = 0;
+ p = *pp;
+ for(shift = 0;; shift += 7) {
+ v |= (*p & 0x7F) << shift;
+ if(!(*p++ & 0x80))
+ break;
+ }
+ *pp = p;
+ return v;
+}
+
+static char*
+pcstring(uint32 off)
+{
+ if(off == 0 || off >= pclineend - pcline ||
+ memchr(pcline + off, '\0', pclineend - (pcline + off)) == nil)
+ return "?";
+ return (char*)pcline+off;
+}
+
+
+static int
+step(uchar **pp, uvlong *pc, int32 *value, int first)
+{
+ uint32 uvdelta, pcdelta;
+ int32 vdelta;
+
+ uvdelta = readvarint(pp);
+ if(uvdelta == 0 && !first)
+ return 0;
+ if(uvdelta&1)
+ uvdelta = ~(uvdelta>>1);
+ else
+ uvdelta >>= 1;
+ vdelta = (int32)uvdelta;
+ pcdelta = readvarint(pp) * pcquantum;
+ *value += vdelta;
+ *pc += pcdelta;
+ return 1;
+}
+
+static int32
+pcvalue(uint32 off, uvlong entry, uvlong targetpc)
+{
+ uvlong pc;
+ int32 val;
+ uchar *p;
+
+ val = -1;
+ pc = entry;
+ if(off == 0 || off >= pclineend - pcline)
+ return -1;
+ p = pcline + off;
+ while(step(&p, &pc, &val, pc == entry)) {
+ if(targetpc < pc)
+ return val;
+ }
+ return -1;
+}
+
+static uvlong
+go12pc2sp(uvlong pc)
+{
+ uchar *f;
+ uint32 off;
+ uvlong entry;
+ int32 sp;
+
+ f = go12findfunc(pc);
+ if(f == nil)
+ return ~(uvlong)0;
+ entry = pcuintptr(f+FuncEntry);
+ off = pcswal(*(uint32*)(f+FuncPCSP));
+ sp = pcvalue(off, entry, pc);
+ if(sp < 0)
+ return ~(uvlong)0;
+ return sp;
+}
+
+static int32
+go12fileline(char *str, int n, uvlong pc)
+{
+ uchar *f;
+ uint32 fileoff, lineoff;
+ uvlong entry;
+ int lno, fno;
+
+ f = go12findfunc(pc);
+ if(f == nil)
+ return 0;
+ entry = pcuintptr(f+FuncEntry);
+ fileoff = pcswal(*(uint32*)(f+FuncPCFile));
+ lineoff = pcswal(*(uint32*)(f+FuncPCLine));
+ lno = pcvalue(lineoff, entry, pc);
+ fno = pcvalue(fileoff, entry, pc);
+ if(lno < 0 || fno <= 0 || fno >= nfiletab) {
+ return 0;
+ }
+ snprint(str, n, "%s:%d", pcstring(pcswal(filetab[fno])), lno);
+ return 1;
+}
+
+static uvlong
+go12file2pc(char *file, int line)
+{
+ int fno;
+ int32 i, fval, lval;
+ uchar *func, *fp, *lp;
+ uvlong fpc, lpc, fstartpc, lstartpc, entry;
+
+ // Map file to file number.
+ // NOTE(rsc): Could introduce a hash table for repeated
+ // lookups if anyone ever calls this.
+ for(fno=1; fno<nfiletab; fno++)
+ if(strcmp(pcstring(pcswal(filetab[fno])), file) == 0)
+ goto havefile;
+ werrstr("cannot find file");
+ return ~(uvlong)0;
+
+havefile:
+ // Consider each func.
+ // Run file number program to find file match,
+ // then run line number program to find line match.
+ // Most file number programs are tiny, and most will
+ // not mention the file number, so this should be fairly
+ // quick.
+ for(i=0; i<nfunctab; i++) {
+ func = pcline + pcuintptr(functab+i*2*pcptrsize+pcptrsize);
+ entry = pcuintptr(func+FuncEntry);
+ fp = pcline + pcswal(*(uint32*)(func+FuncPCFile));
+ lp = pcline + pcswal(*(uint32*)(func+FuncPCLine));
+ fval = lval = -1;
+ fpc = lpc = entry;
+ fstartpc = fpc;
+ while(step(&fp, &fpc, &fval, fpc==entry)) {
+ if(fval == fno && fstartpc < fpc) {
+ lstartpc = lpc;
+ while(lpc < fpc && step(&lp, &lpc, &lval, lpc==entry)) {
+ if(lval == line) {
+ if(fstartpc <= lstartpc) {
+ return lstartpc;
+ }
+ if(fstartpc < lpc) {
+ return fstartpc;
+ }
+ }
+ lstartpc = lpc;
+ }
+ }
+ fstartpc = fpc;
+ }
+ }
+ werrstr("cannot find line in file");
+ return ~(uvlong)0;
+}
diff --git a/src/libmach/windows.c b/src/libmach/windows.c
index aea7bace0..9ffc3af01 100644
--- a/src/libmach/windows.c
+++ b/src/libmach/windows.c
@@ -7,6 +7,8 @@
int
ctlproc(int pid, char *msg)
{
+ USED(pid);
+ USED(msg);
sysfatal("ctlproc unimplemented in Windows");
return -1;
}
@@ -14,6 +16,7 @@ ctlproc(int pid, char *msg)
char*
proctextfile(int pid)
{
+ USED(pid);
sysfatal("proctextfile unimplemented in Windows");
return nil;
}
@@ -21,6 +24,7 @@ proctextfile(int pid)
char*
procstatus(int pid)
{
+ USED(pid);
sysfatal("procstatus unimplemented in Windows");
return nil;
}
@@ -28,6 +32,8 @@ procstatus(int pid)
Map*
attachproc(int pid, Fhdr *fp)
{
+ USED(pid);
+ USED(fp);
sysfatal("attachproc unimplemented in Windows");
return nil;
}
@@ -35,12 +41,16 @@ attachproc(int pid, Fhdr *fp)
void
detachproc(Map *m)
{
+ USED(m);
sysfatal("detachproc unimplemented in Windows");
}
int
procthreadpids(int pid, int *p, int np)
{
+ USED(pid);
+ USED(p);
+ USED(np);
sysfatal("procthreadpids unimplemented in Windows");
return -1;
}
@@ -59,6 +69,10 @@ pread(int fd, void *buf, int count, int offset)
int
pwrite(int fd, void *buf, int count, int offset)
{
+ USED(fd);
+ USED(buf);
+ USED(count);
+ USED(offset);
sysfatal("pwrite unimplemented in Windows");
return -1;
}
@@ -66,6 +80,8 @@ pwrite(int fd, void *buf, int count, int offset)
int
nanosleep(const struct timespec *rqtp, struct timespec *rmtp)
{
+ USED(rqtp);
+ USED(rmtp);
sysfatal("nanosleep unimplemented in Windows");
return -1;
}
diff --git a/src/make.bash b/src/make.bash
index 10696e765..877d1e5eb 100755
--- a/src/make.bash
+++ b/src/make.bash
@@ -37,6 +37,11 @@
#
# CC: Command line to run to get at host C compiler.
# Default is "gcc". Also supported: "clang".
+# CXX: Command line to run to get at host C++ compiler, only recorded
+# for cgo use. Default is "g++". Also supported: "clang++".
+#
+# GO_DISTFLAGS: extra flags to provide to "dist bootstrap". Use "-s"
+# to build a statically linked toolchain.
set -e
if [ ! -f run.bash ]; then
@@ -117,7 +122,12 @@ if [ "$(uname)" == "Darwin" ]; then
fi
${CC:-gcc} $mflag -O2 -Wall -Werror -o cmd/dist/dist -Icmd/dist "$DEFGOROOT" cmd/dist/*.c
-eval $(./cmd/dist/dist env -p)
+# -e doesn't propagate out of eval, so check success by hand.
+eval $(./cmd/dist/dist env -p || echo FAIL=true)
+if [ "$FAIL" = true ]; then
+ exit 1
+fi
+
echo
if [ "$1" = "--dist-tool" ]; then
@@ -135,7 +145,7 @@ buildall="-a"
if [ "$1" = "--no-clean" ]; then
buildall=""
fi
-./cmd/dist/dist bootstrap $buildall -v # builds go_bootstrap
+./cmd/dist/dist bootstrap $buildall $GO_DISTFLAGS -v # builds go_bootstrap
# Delay move of dist tool to now, because bootstrap may clear tool directory.
mv cmd/dist/dist "$GOTOOLDIR"/dist
"$GOTOOLDIR"/go_bootstrap clean -i std
diff --git a/src/make.bat b/src/make.bat
index c1f171de4..23b799e36 100644
--- a/src/make.bat
+++ b/src/make.bat
@@ -60,7 +60,7 @@ echo # Building C bootstrap tool.
echo cmd/dist
if not exist ..\bin\tool mkdir ..\bin\tool
:: Windows has no glob expansion, so spell out cmd/dist/*.c.
-gcc -O2 -Wall -Werror -o cmd/dist/dist.exe -Icmd/dist %DEFGOROOT% cmd/dist/buf.c cmd/dist/build.c cmd/dist/buildgc.c cmd/dist/buildruntime.c cmd/dist/goc2c.c cmd/dist/main.c cmd/dist/windows.c cmd/dist/arm.c
+gcc -O2 -Wall -Werror -o cmd/dist/dist.exe -Icmd/dist %DEFGOROOT% cmd/dist/buf.c cmd/dist/build.c cmd/dist/buildgc.c cmd/dist/buildgo.c cmd/dist/buildruntime.c cmd/dist/goc2c.c cmd/dist/main.c cmd/dist/windows.c cmd/dist/arm.c
if errorlevel 1 goto fail
.\cmd\dist\dist env -wp >env.bat
if errorlevel 1 goto fail
diff --git a/src/make.rc b/src/make.rc
index 83d16c045..222bb8a18 100755
--- a/src/make.rc
+++ b/src/make.rc
@@ -95,3 +95,5 @@ rm -f $GOTOOLDIR/go_bootstrap
if(! ~ $1 --no-banner)
$GOTOOLDIR/dist banner
+
+status=''
diff --git a/src/pkg/archive/tar/common.go b/src/pkg/archive/tar/common.go
index 60d207c48..1b961e3ec 100644
--- a/src/pkg/archive/tar/common.go
+++ b/src/pkg/archive/tar/common.go
@@ -13,6 +13,7 @@
package tar
import (
+ "bytes"
"errors"
"fmt"
"os"
@@ -82,9 +83,9 @@ func (fi headerFileInfo) Sys() interface{} { return fi.h }
// Name returns the base name of the file.
func (fi headerFileInfo) Name() string {
if fi.IsDir() {
- return path.Clean(fi.h.Name)
+ return path.Base(path.Clean(fi.h.Name))
}
- return fi.h.Name
+ return path.Base(fi.h.Name)
}
// Mode returns the permission and mode bits for the headerFileInfo.
@@ -174,9 +175,29 @@ const (
c_ISSOCK = 0140000 // Socket
)
+// Keywords for the PAX Extended Header
+const (
+ paxAtime = "atime"
+ paxCharset = "charset"
+ paxComment = "comment"
+ paxCtime = "ctime" // please note that ctime is not a valid pax header.
+ paxGid = "gid"
+ paxGname = "gname"
+ paxLinkpath = "linkpath"
+ paxMtime = "mtime"
+ paxPath = "path"
+ paxSize = "size"
+ paxUid = "uid"
+ paxUname = "uname"
+ paxNone = ""
+)
+
// FileInfoHeader creates a partially-populated Header from fi.
// If fi describes a symlink, FileInfoHeader records link as the link target.
// If fi describes a directory, a slash is appended to the name.
+// Because os.FileInfo's Name method returns only the base name of
+// the file it describes, it may be necessary to modify the Name field
+// of the returned header to provide the full path name of the file.
func FileInfoHeader(fi os.FileInfo, link string) (*Header, error) {
if fi == nil {
return nil, errors.New("tar: FileInfo is nil")
@@ -257,3 +278,25 @@ func (sp *slicer) next(n int) (b []byte) {
b, *sp = s[0:n], s[n:]
return
}
+
+func isASCII(s string) bool {
+ for _, c := range s {
+ if c >= 0x80 {
+ return false
+ }
+ }
+ return true
+}
+
+func toASCII(s string) string {
+ if isASCII(s) {
+ return s
+ }
+ var buf bytes.Buffer
+ for _, c := range s {
+ if c < 0x80 {
+ buf.WriteByte(byte(c))
+ }
+ }
+ return buf.String()
+}
diff --git a/src/pkg/archive/tar/reader.go b/src/pkg/archive/tar/reader.go
index 05f82a40d..b2d62f3c5 100644
--- a/src/pkg/archive/tar/reader.go
+++ b/src/pkg/archive/tar/reader.go
@@ -95,45 +95,45 @@ func (tr *Reader) Next() (*Header, error) {
func mergePAX(hdr *Header, headers map[string]string) error {
for k, v := range headers {
switch k {
- case "path":
+ case paxPath:
hdr.Name = v
- case "linkpath":
+ case paxLinkpath:
hdr.Linkname = v
- case "gname":
+ case paxGname:
hdr.Gname = v
- case "uname":
+ case paxUname:
hdr.Uname = v
- case "uid":
+ case paxUid:
uid, err := strconv.ParseInt(v, 10, 0)
if err != nil {
return err
}
hdr.Uid = int(uid)
- case "gid":
+ case paxGid:
gid, err := strconv.ParseInt(v, 10, 0)
if err != nil {
return err
}
hdr.Gid = int(gid)
- case "atime":
+ case paxAtime:
t, err := parsePAXTime(v)
if err != nil {
return err
}
hdr.AccessTime = t
- case "mtime":
+ case paxMtime:
t, err := parsePAXTime(v)
if err != nil {
return err
}
hdr.ModTime = t
- case "ctime":
+ case paxCtime:
t, err := parsePAXTime(v)
if err != nil {
return err
}
hdr.ChangeTime = t
- case "size":
+ case paxSize:
size, err := strconv.ParseInt(v, 10, 0)
if err != nil {
return err
@@ -243,13 +243,15 @@ func (tr *Reader) octal(b []byte) int64 {
return x
}
- // Removing leading spaces.
- for len(b) > 0 && b[0] == ' ' {
- b = b[1:]
- }
- // Removing trailing NULs and spaces.
- for len(b) > 0 && (b[len(b)-1] == ' ' || b[len(b)-1] == '\x00') {
- b = b[0 : len(b)-1]
+ // Because unused fields are filled with NULs, we need
+ // to skip leading NULs. Fields may also be padded with
+ // spaces or NULs.
+ // So we remove leading and trailing NULs and spaces to
+ // be sure.
+ b = bytes.Trim(b, " \x00")
+
+ if len(b) == 0 {
+ return 0
}
x, err := strconv.ParseUint(cString(b), 8, 64)
if err != nil {
diff --git a/src/pkg/archive/tar/reader_test.go b/src/pkg/archive/tar/reader_test.go
index 9a1968237..128561656 100644
--- a/src/pkg/archive/tar/reader_test.go
+++ b/src/pkg/archive/tar/reader_test.go
@@ -142,6 +142,25 @@ var untarTests = []*untarTest{
},
},
},
+ {
+ file: "testdata/nil-uid.tar", // golang.org/issue/5290
+ headers: []*Header{
+ {
+ Name: "P1050238.JPG.log",
+ Mode: 0664,
+ Uid: 0,
+ Gid: 0,
+ Size: 14,
+ ModTime: time.Unix(1365454838, 0),
+ Typeflag: TypeReg,
+ Linkname: "",
+ Uname: "eyefi",
+ Gname: "eyefi",
+ Devmajor: 0,
+ Devminor: 0,
+ },
+ },
+ },
}
func TestReader(t *testing.T) {
@@ -152,6 +171,7 @@ testLoop:
t.Errorf("test %d: Unexpected error: %v", i, err)
continue
}
+ defer f.Close()
tr := NewReader(f)
for j, header := range test.headers {
hdr, err := tr.Next()
@@ -172,7 +192,6 @@ testLoop:
if hdr != nil || err != nil {
t.Errorf("test %d: Unexpected entry or error: hdr=%v err=%v", i, hdr, err)
}
- f.Close()
}
}
diff --git a/src/pkg/archive/tar/tar_test.go b/src/pkg/archive/tar/tar_test.go
index dd6310313..616a9cc57 100644
--- a/src/pkg/archive/tar/tar_test.go
+++ b/src/pkg/archive/tar/tar_test.go
@@ -8,7 +8,9 @@ import (
"bytes"
"io/ioutil"
"os"
+ "path"
"reflect"
+ "strings"
"testing"
"time"
)
@@ -249,7 +251,14 @@ func TestHeaderRoundTrip(t *testing.T) {
t.Error(err)
continue
}
- if got, want := h2.Name, g.h.Name; got != want {
+ if strings.Contains(fi.Name(), "/") {
+ t.Errorf("FileInfo of %q contains slash: %q", g.h.Name, fi.Name())
+ }
+ name := path.Base(g.h.Name)
+ if fi.IsDir() {
+ name += "/"
+ }
+ if got, want := h2.Name, name; got != want {
t.Errorf("i=%d: Name: got %v, want %v", i, got, want)
}
if got, want := h2.Size, g.h.Size; got != want {
diff --git a/src/pkg/archive/tar/testdata/nil-uid.tar b/src/pkg/archive/tar/testdata/nil-uid.tar
new file mode 100644
index 000000000..cc9cfaa33
--- /dev/null
+++ b/src/pkg/archive/tar/testdata/nil-uid.tar
Binary files differ
diff --git a/src/pkg/archive/tar/writer.go b/src/pkg/archive/tar/writer.go
index d92dd06ea..549f1464c 100644
--- a/src/pkg/archive/tar/writer.go
+++ b/src/pkg/archive/tar/writer.go
@@ -24,6 +24,7 @@ var (
ErrFieldTooLong = errors.New("archive/tar: header field too long")
ErrWriteAfterClose = errors.New("archive/tar: write after close")
errNameTooLong = errors.New("archive/tar: name too long")
+ errInvalidHeader = errors.New("archive/tar: header field too long or contains invalid values")
)
// A Writer provides sequential writing of a tar archive in POSIX.1 format.
@@ -37,6 +38,7 @@ type Writer struct {
pad int64 // amount of padding to write after current file entry
closed bool
usedBinary bool // whether the binary numeric field extension was used
+ preferPax bool // use pax header instead of binary numeric header
}
// NewWriter creates a new Writer writing to w.
@@ -65,16 +67,23 @@ func (tw *Writer) Flush() error {
}
// Write s into b, terminating it with a NUL if there is room.
-func (tw *Writer) cString(b []byte, s string) {
+// If the value is too long for the field and allowPax is true add a paxheader record instead
+func (tw *Writer) cString(b []byte, s string, allowPax bool, paxKeyword string, paxHeaders map[string]string) {
+ needsPaxHeader := allowPax && len(s) > len(b) || !isASCII(s)
+ if needsPaxHeader {
+ paxHeaders[paxKeyword] = s
+ return
+ }
if len(s) > len(b) {
if tw.err == nil {
tw.err = ErrFieldTooLong
}
return
}
- copy(b, s)
- if len(s) < len(b) {
- b[len(s)] = 0
+ ascii := toASCII(s)
+ copy(b, ascii)
+ if len(ascii) < len(b) {
+ b[len(ascii)] = 0
}
}
@@ -85,17 +94,27 @@ func (tw *Writer) octal(b []byte, x int64) {
for len(s)+1 < len(b) {
s = "0" + s
}
- tw.cString(b, s)
+ tw.cString(b, s, false, paxNone, nil)
}
// Write x into b, either as octal or as binary (GNUtar/star extension).
-func (tw *Writer) numeric(b []byte, x int64) {
+// If the value is too long for the field and writingPax is enabled both for the field and the add a paxheader record instead
+func (tw *Writer) numeric(b []byte, x int64, allowPax bool, paxKeyword string, paxHeaders map[string]string) {
// Try octal first.
s := strconv.FormatInt(x, 8)
if len(s) < len(b) {
tw.octal(b, x)
return
}
+
+ // If it is too long for octal, and pax is preferred, use a pax header
+ if allowPax && tw.preferPax {
+ tw.octal(b, 0)
+ s := strconv.FormatInt(x, 10)
+ paxHeaders[paxKeyword] = s
+ return
+ }
+
// Too big: use binary (big-endian).
tw.usedBinary = true
for i := len(b) - 1; x > 0 && i >= 0; i-- {
@@ -115,6 +134,15 @@ var (
// WriteHeader calls Flush if it is not the first header.
// Calling after a Close will return ErrWriteAfterClose.
func (tw *Writer) WriteHeader(hdr *Header) error {
+ return tw.writeHeader(hdr, true)
+}
+
+// WriteHeader writes hdr and prepares to accept the file's contents.
+// WriteHeader calls Flush if it is not the first header.
+// Calling after a Close will return ErrWriteAfterClose.
+// As this method is called internally by writePax header to allow it to
+// suppress writing the pax header.
+func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error {
if tw.closed {
return ErrWriteAfterClose
}
@@ -124,31 +152,21 @@ func (tw *Writer) WriteHeader(hdr *Header) error {
if tw.err != nil {
return tw.err
}
- // Decide whether or not to use PAX extensions
+
+ // a map to hold pax header records, if any are needed
+ paxHeaders := make(map[string]string)
+
// TODO(shanemhansen): we might want to use PAX headers for
// subsecond time resolution, but for now let's just capture
- // the long name/long symlink use case.
- suffix := hdr.Name
- prefix := ""
- if len(hdr.Name) > fileNameSize || len(hdr.Linkname) > fileNameSize {
- var err error
- prefix, suffix, err = tw.splitUSTARLongName(hdr.Name)
- // Either we were unable to pack the long name into ustar format
- // or the link name is too long; use PAX headers.
- if err == errNameTooLong || len(hdr.Linkname) > fileNameSize {
- if err := tw.writePAXHeader(hdr); err != nil {
- return err
- }
- } else if err != nil {
- return err
- }
- }
- tw.nb = int64(hdr.Size)
- tw.pad = -tw.nb & (blockSize - 1) // blockSize is a power of two
+ // too long fields or non ascii characters
header := make([]byte, blockSize)
s := slicer(header)
- tw.cString(s.next(fileNameSize), suffix)
+
+ // keep a reference to the filename to allow to overwrite it later if we detect that we can use ustar longnames instead of pax
+ pathHeaderBytes := s.next(fileNameSize)
+
+ tw.cString(pathHeaderBytes, hdr.Name, true, paxPath, paxHeaders)
// Handle out of range ModTime carefully.
var modTime int64
@@ -156,27 +174,55 @@ func (tw *Writer) WriteHeader(hdr *Header) error {
modTime = hdr.ModTime.Unix()
}
- tw.octal(s.next(8), hdr.Mode) // 100:108
- tw.numeric(s.next(8), int64(hdr.Uid)) // 108:116
- tw.numeric(s.next(8), int64(hdr.Gid)) // 116:124
- tw.numeric(s.next(12), hdr.Size) // 124:136
- tw.numeric(s.next(12), modTime) // 136:148
- s.next(8) // chksum (148:156)
- s.next(1)[0] = hdr.Typeflag // 156:157
- tw.cString(s.next(100), hdr.Linkname) // linkname (157:257)
- copy(s.next(8), []byte("ustar\x0000")) // 257:265
- tw.cString(s.next(32), hdr.Uname) // 265:297
- tw.cString(s.next(32), hdr.Gname) // 297:329
- tw.numeric(s.next(8), hdr.Devmajor) // 329:337
- tw.numeric(s.next(8), hdr.Devminor) // 337:345
- tw.cString(s.next(155), prefix) // 345:500
+ tw.octal(s.next(8), hdr.Mode) // 100:108
+ tw.numeric(s.next(8), int64(hdr.Uid), true, paxUid, paxHeaders) // 108:116
+ tw.numeric(s.next(8), int64(hdr.Gid), true, paxGid, paxHeaders) // 116:124
+ tw.numeric(s.next(12), hdr.Size, true, paxSize, paxHeaders) // 124:136
+ tw.numeric(s.next(12), modTime, false, paxNone, nil) // 136:148 --- consider using pax for finer granularity
+ s.next(8) // chksum (148:156)
+ s.next(1)[0] = hdr.Typeflag // 156:157
+
+ tw.cString(s.next(100), hdr.Linkname, true, paxLinkpath, paxHeaders)
+
+ copy(s.next(8), []byte("ustar\x0000")) // 257:265
+ tw.cString(s.next(32), hdr.Uname, true, paxUname, paxHeaders) // 265:297
+ tw.cString(s.next(32), hdr.Gname, true, paxGname, paxHeaders) // 297:329
+ tw.numeric(s.next(8), hdr.Devmajor, false, paxNone, nil) // 329:337
+ tw.numeric(s.next(8), hdr.Devminor, false, paxNone, nil) // 337:345
+
+ // keep a reference to the prefix to allow to overwrite it later if we detect that we can use ustar longnames instead of pax
+ prefixHeaderBytes := s.next(155)
+ tw.cString(prefixHeaderBytes, "", false, paxNone, nil) // 345:500 prefix
+
// Use the GNU magic instead of POSIX magic if we used any GNU extensions.
if tw.usedBinary {
copy(header[257:265], []byte("ustar \x00"))
}
- // Use the ustar magic if we used ustar long names.
- if len(prefix) > 0 {
- copy(header[257:265], []byte("ustar\000"))
+
+ _, paxPathUsed := paxHeaders[paxPath]
+ // try to use a ustar header when only the name is too long
+ if !tw.preferPax && len(paxHeaders) == 1 && paxPathUsed {
+ suffix := hdr.Name
+ prefix := ""
+ if len(hdr.Name) > fileNameSize && isASCII(hdr.Name) {
+ var err error
+ prefix, suffix, err = tw.splitUSTARLongName(hdr.Name)
+ if err == nil {
+ // ok we can use a ustar long name instead of pax, now correct the fields
+
+ // remove the path field from the pax header. this will suppress the pax header
+ delete(paxHeaders, paxPath)
+
+ // update the path fields
+ tw.cString(pathHeaderBytes, suffix, false, paxNone, nil)
+ tw.cString(prefixHeaderBytes, prefix, false, paxNone, nil)
+
+ // Use the ustar magic if we used ustar long names.
+ if len(prefix) > 0 {
+ copy(header[257:265], []byte("ustar\000"))
+ }
+ }
+ }
}
// The chksum field is terminated by a NUL and a space.
@@ -190,8 +236,18 @@ func (tw *Writer) WriteHeader(hdr *Header) error {
return tw.err
}
- _, tw.err = tw.w.Write(header)
+ if len(paxHeaders) > 0 {
+ if !allowPax {
+ return errInvalidHeader
+ }
+ if err := tw.writePAXHeader(hdr, paxHeaders); err != nil {
+ return err
+ }
+ }
+ tw.nb = int64(hdr.Size)
+ tw.pad = (blockSize - (tw.nb % blockSize)) % blockSize
+ _, tw.err = tw.w.Write(header)
return tw.err
}
@@ -207,8 +263,11 @@ func (tw *Writer) splitUSTARLongName(name string) (prefix, suffix string, err er
length--
}
i := strings.LastIndex(name[:length], "/")
- nlen := length - i - 1
- if i <= 0 || nlen > fileNameSize || nlen == 0 {
+ // nlen contains the resulting length in the name field.
+ // plen contains the resulting length in the prefix field.
+ nlen := len(name) - i - 1
+ plen := i
+ if i <= 0 || nlen > fileNameSize || nlen == 0 || plen > fileNamePrefixSize {
err = errNameTooLong
return
}
@@ -218,7 +277,7 @@ func (tw *Writer) splitUSTARLongName(name string) (prefix, suffix string, err er
// writePaxHeader writes an extended pax header to the
// archive.
-func (tw *Writer) writePAXHeader(hdr *Header) error {
+func (tw *Writer) writePAXHeader(hdr *Header, paxHeaders map[string]string) error {
// Prepare extended header
ext := new(Header)
ext.Typeflag = TypeXHeader
@@ -229,18 +288,23 @@ func (tw *Writer) writePAXHeader(hdr *Header) error {
// with the current pid.
pid := os.Getpid()
dir, file := path.Split(hdr.Name)
- ext.Name = path.Join(dir,
- fmt.Sprintf("PaxHeaders.%d", pid), file)[0:100]
+ fullName := path.Join(dir,
+ fmt.Sprintf("PaxHeaders.%d", pid), file)
+
+ ascii := toASCII(fullName)
+ if len(ascii) > 100 {
+ ascii = ascii[:100]
+ }
+ ext.Name = ascii
// Construct the body
var buf bytes.Buffer
- if len(hdr.Name) > fileNameSize {
- fmt.Fprint(&buf, paxHeader("path="+hdr.Name))
- }
- if len(hdr.Linkname) > fileNameSize {
- fmt.Fprint(&buf, paxHeader("linkpath="+hdr.Linkname))
+
+ for k, v := range paxHeaders {
+ fmt.Fprint(&buf, paxHeader(k+"="+v))
}
+
ext.Size = int64(len(buf.Bytes()))
- if err := tw.WriteHeader(ext); err != nil {
+ if err := tw.writeHeader(ext, false); err != nil {
return err
}
if _, err := tw.Write(buf.Bytes()); err != nil {
diff --git a/src/pkg/archive/tar/writer_test.go b/src/pkg/archive/tar/writer_test.go
index 4cf7c72af..30ebf977a 100644
--- a/src/pkg/archive/tar/writer_test.go
+++ b/src/pkg/archive/tar/writer_test.go
@@ -243,15 +243,110 @@ func TestPax(t *testing.T) {
}
}
+func TestPaxSymlink(t *testing.T) {
+ // Create an archive with a large linkname
+ fileinfo, err := os.Stat("testdata/small.txt")
+ if err != nil {
+ t.Fatal(err)
+ }
+ hdr, err := FileInfoHeader(fileinfo, "")
+ hdr.Typeflag = TypeSymlink
+ if err != nil {
+ t.Fatalf("os.Stat:1 %v", err)
+ }
+ // Force a PAX long linkname to be written
+ longLinkname := strings.Repeat("1234567890/1234567890", 10)
+ hdr.Linkname = longLinkname
+
+ hdr.Size = 0
+ var buf bytes.Buffer
+ writer := NewWriter(&buf)
+ if err := writer.WriteHeader(hdr); err != nil {
+ t.Fatal(err)
+ }
+ if err := writer.Close(); err != nil {
+ t.Fatal(err)
+ }
+ // Simple test to make sure PAX extensions are in effect
+ if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.")) {
+ t.Fatal("Expected at least one PAX header to be written.")
+ }
+ // Test that we can get a long name back out of the archive.
+ reader := NewReader(&buf)
+ hdr, err = reader.Next()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if hdr.Linkname != longLinkname {
+ t.Fatal("Couldn't recover long link name")
+ }
+}
+
+func TestPaxNonAscii(t *testing.T) {
+ // Create an archive with non ascii. These should trigger a pax header
+ // because pax headers have a defined utf-8 encoding.
+ fileinfo, err := os.Stat("testdata/small.txt")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ hdr, err := FileInfoHeader(fileinfo, "")
+ if err != nil {
+ t.Fatalf("os.Stat:1 %v", err)
+ }
+
+ // some sample data
+ chineseFilename := "文件名"
+ chineseGroupname := "組"
+ chineseUsername := "用戶名"
+
+ hdr.Name = chineseFilename
+ hdr.Gname = chineseGroupname
+ hdr.Uname = chineseUsername
+
+ contents := strings.Repeat(" ", int(hdr.Size))
+
+ var buf bytes.Buffer
+ writer := NewWriter(&buf)
+ if err := writer.WriteHeader(hdr); err != nil {
+ t.Fatal(err)
+ }
+ if _, err = writer.Write([]byte(contents)); err != nil {
+ t.Fatal(err)
+ }
+ if err := writer.Close(); err != nil {
+ t.Fatal(err)
+ }
+ // Simple test to make sure PAX extensions are in effect
+ if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.")) {
+ t.Fatal("Expected at least one PAX header to be written.")
+ }
+ // Test that we can get a long name back out of the archive.
+ reader := NewReader(&buf)
+ hdr, err = reader.Next()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if hdr.Name != chineseFilename {
+ t.Fatal("Couldn't recover unicode name")
+ }
+ if hdr.Gname != chineseGroupname {
+ t.Fatal("Couldn't recover unicode group")
+ }
+ if hdr.Uname != chineseUsername {
+ t.Fatal("Couldn't recover unicode user")
+ }
+}
+
func TestPAXHeader(t *testing.T) {
medName := strings.Repeat("CD", 50)
longName := strings.Repeat("AB", 100)
paxTests := [][2]string{
- {"name=/etc/hosts", "19 name=/etc/hosts\n"},
+ {paxPath + "=/etc/hosts", "19 path=/etc/hosts\n"},
{"a=b", "6 a=b\n"}, // Single digit length
{"a=names", "11 a=names\n"}, // Test case involving carries
- {"name=" + longName, fmt.Sprintf("210 name=%s\n", longName)},
- {"name=" + medName, fmt.Sprintf("110 name=%s\n", medName)}}
+ {paxPath + "=" + longName, fmt.Sprintf("210 path=%s\n", longName)},
+ {paxPath + "=" + medName, fmt.Sprintf("110 path=%s\n", medName)}}
for _, test := range paxTests {
key, expected := test[0], test[1]
@@ -260,3 +355,39 @@ func TestPAXHeader(t *testing.T) {
}
}
}
+
+func TestUSTARLongName(t *testing.T) {
+ // Create an archive with a path that failed to split with USTAR extension in previous versions.
+ fileinfo, err := os.Stat("testdata/small.txt")
+ if err != nil {
+ t.Fatal(err)
+ }
+ hdr, err := FileInfoHeader(fileinfo, "")
+ hdr.Typeflag = TypeDir
+ if err != nil {
+ t.Fatalf("os.Stat:1 %v", err)
+ }
+ // Force a PAX long name to be written. The name was taken from a practical example
+ // that fails and replaced ever char through numbers to anonymize the sample.
+ longName := "/0000_0000000/00000-000000000/0000_0000000/00000-0000000000000/0000_0000000/00000-0000000-00000000/0000_0000000/00000000/0000_0000000/000/0000_0000000/00000000v00/0000_0000000/000000/0000_0000000/0000000/0000_0000000/00000y-00/0000/0000/00000000/0x000000/"
+ hdr.Name = longName
+
+ hdr.Size = 0
+ var buf bytes.Buffer
+ writer := NewWriter(&buf)
+ if err := writer.WriteHeader(hdr); err != nil {
+ t.Fatal(err)
+ }
+ if err := writer.Close(); err != nil {
+ t.Fatal(err)
+ }
+ // Test that we can get a long name back out of the archive.
+ reader := NewReader(&buf)
+ hdr, err = reader.Next()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if hdr.Name != longName {
+ t.Fatal("Couldn't recover long name")
+ }
+}
diff --git a/src/pkg/archive/zip/reader.go b/src/pkg/archive/zip/reader.go
index f19cf2d1f..116737337 100644
--- a/src/pkg/archive/zip/reader.go
+++ b/src/pkg/archive/zip/reader.go
@@ -6,13 +6,11 @@ package zip
import (
"bufio"
- "compress/flate"
"encoding/binary"
"errors"
"hash"
"hash/crc32"
"io"
- "io/ioutil"
"os"
)
@@ -116,6 +114,19 @@ func (rc *ReadCloser) Close() error {
return rc.f.Close()
}
+// DataOffset returns the offset of the file's possibly-compressed
+// data, relative to the beginning of the zip file.
+//
+// Most callers should instead use Open, which transparently
+// decompresses data and verifies checksums.
+func (f *File) DataOffset() (offset int64, err error) {
+ bodyOffset, err := f.findBodyOffset()
+ if err != nil {
+ return
+ }
+ return f.headerOffset + bodyOffset, nil
+}
+
// Open returns a ReadCloser that provides access to the File's contents.
// Multiple files may be read concurrently.
func (f *File) Open() (rc io.ReadCloser, err error) {
@@ -125,15 +136,12 @@ func (f *File) Open() (rc io.ReadCloser, err error) {
}
size := int64(f.CompressedSize64)
r := io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset, size)
- switch f.Method {
- case Store: // (no compression)
- rc = ioutil.NopCloser(r)
- case Deflate:
- rc = flate.NewReader(r)
- default:
+ dcomp := decompressor(f.Method)
+ if dcomp == nil {
err = ErrAlgorithm
return
}
+ rc = dcomp(r)
var desr io.Reader
if f.hasDataDescriptor() {
desr = io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset+size, dataDescriptorLen)
@@ -184,9 +192,8 @@ func (r *checksumReader) Close() error { return r.rc.Close() }
// findBodyOffset does the minimum work to verify the file has a header
// and returns the file body offset.
func (f *File) findBodyOffset() (int64, error) {
- r := io.NewSectionReader(f.zipr, f.headerOffset, f.zipsize-f.headerOffset)
var buf [fileHeaderLen]byte
- if _, err := io.ReadFull(r, buf[:]); err != nil {
+ if _, err := f.zipr.ReadAt(buf[:], f.headerOffset); err != nil {
return 0, err
}
b := readBuf(buf[:])
diff --git a/src/pkg/archive/zip/reader_test.go b/src/pkg/archive/zip/reader_test.go
index 833ba28ad..78875ecbf 100644
--- a/src/pkg/archive/zip/reader_test.go
+++ b/src/pkg/archive/zip/reader_test.go
@@ -276,6 +276,7 @@ func readTestZip(t *testing.T, zt ZipTest) {
var rc *ReadCloser
rc, err = OpenReader(filepath.Join("testdata", zt.Name))
if err == nil {
+ defer rc.Close()
z = &rc.Reader
}
}
diff --git a/src/pkg/archive/zip/register.go b/src/pkg/archive/zip/register.go
new file mode 100644
index 000000000..c046f081b
--- /dev/null
+++ b/src/pkg/archive/zip/register.go
@@ -0,0 +1,71 @@
+// 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 zip
+
+import (
+ "compress/flate"
+ "io"
+ "io/ioutil"
+ "sync"
+)
+
+// A Compressor returns a compressing writer, writing to the
+// provided writer. On Close, any pending data should be flushed.
+type Compressor func(io.Writer) (io.WriteCloser, error)
+
+// Decompressor is a function that wraps a Reader with a decompressing Reader.
+// The decompressed ReadCloser is returned to callers who open files from
+// within the archive. These callers are responsible for closing this reader
+// when they're finished reading.
+type Decompressor func(io.Reader) io.ReadCloser
+
+var (
+ mu sync.RWMutex // guards compressor and decompressor maps
+
+ compressors = map[uint16]Compressor{
+ Store: func(w io.Writer) (io.WriteCloser, error) { return &nopCloser{w}, nil },
+ Deflate: func(w io.Writer) (io.WriteCloser, error) { return flate.NewWriter(w, 5) },
+ }
+
+ decompressors = map[uint16]Decompressor{
+ Store: ioutil.NopCloser,
+ Deflate: flate.NewReader,
+ }
+)
+
+// RegisterDecompressor allows custom decompressors for a specified method ID.
+func RegisterDecompressor(method uint16, d Decompressor) {
+ mu.Lock()
+ defer mu.Unlock()
+
+ if _, ok := decompressors[method]; ok {
+ panic("decompressor already registered")
+ }
+ decompressors[method] = d
+}
+
+// RegisterCompressor registers custom compressors for a specified method ID.
+// The common methods Store and Deflate are built in.
+func RegisterCompressor(method uint16, comp Compressor) {
+ mu.Lock()
+ defer mu.Unlock()
+
+ if _, ok := compressors[method]; ok {
+ panic("compressor already registered")
+ }
+ compressors[method] = comp
+}
+
+func compressor(method uint16) Compressor {
+ mu.RLock()
+ defer mu.RUnlock()
+ return compressors[method]
+}
+
+func decompressor(method uint16) Decompressor {
+ mu.RLock()
+ defer mu.RUnlock()
+ return decompressors[method]
+}
diff --git a/src/pkg/archive/zip/struct.go b/src/pkg/archive/zip/struct.go
index 73972d41c..65e5238c3 100644
--- a/src/pkg/archive/zip/struct.go
+++ b/src/pkg/archive/zip/struct.go
@@ -21,6 +21,7 @@ package zip
import (
"os"
+ "path"
"time"
)
@@ -99,7 +100,7 @@ type headerFileInfo struct {
fh *FileHeader
}
-func (fi headerFileInfo) Name() string { return fi.fh.Name }
+func (fi headerFileInfo) Name() string { return path.Base(fi.fh.Name) }
func (fi headerFileInfo) Size() int64 {
if fi.fh.UncompressedSize64 > 0 {
return int64(fi.fh.UncompressedSize64)
@@ -113,6 +114,9 @@ func (fi headerFileInfo) Sys() interface{} { return fi.fh }
// FileInfoHeader creates a partially-populated FileHeader from an
// os.FileInfo.
+// Because os.FileInfo's Name method returns only the base name of
+// the file it describes, it may be necessary to modify the Name field
+// of the returned header to provide the full path name of the file.
func FileInfoHeader(fi os.FileInfo) (*FileHeader, error) {
size := fi.Size()
fh := &FileHeader{
diff --git a/src/pkg/archive/zip/writer.go b/src/pkg/archive/zip/writer.go
index e9f147cea..6c9800a78 100644
--- a/src/pkg/archive/zip/writer.go
+++ b/src/pkg/archive/zip/writer.go
@@ -6,7 +6,6 @@ package zip
import (
"bufio"
- "compress/flate"
"encoding/binary"
"errors"
"hash"
@@ -198,18 +197,15 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) {
compCount: &countWriter{w: w.cw},
crc32: crc32.NewIEEE(),
}
- switch fh.Method {
- case Store:
- fw.comp = nopCloser{fw.compCount}
- case Deflate:
- var err error
- fw.comp, err = flate.NewWriter(fw.compCount, 5)
- if err != nil {
- return nil, err
- }
- default:
+ comp := compressor(fh.Method)
+ if comp == nil {
return nil, ErrAlgorithm
}
+ var err error
+ fw.comp, err = comp(fw.compCount)
+ if err != nil {
+ return nil, err
+ }
fw.rawCount = &countWriter{w: fw.comp}
h := &header{
diff --git a/src/pkg/archive/zip/zip_test.go b/src/pkg/archive/zip/zip_test.go
index a8af206a8..32a16a79e 100644
--- a/src/pkg/archive/zip/zip_test.go
+++ b/src/pkg/archive/zip/zip_test.go
@@ -9,22 +9,24 @@ package zip
import (
"bytes"
"fmt"
+ "hash"
"io"
"io/ioutil"
+ "sort"
"strings"
"testing"
"time"
)
func TestOver65kFiles(t *testing.T) {
- if testing.Short() {
- t.Skip("slow test; skipping")
- }
buf := new(bytes.Buffer)
w := NewWriter(buf)
const nFiles = (1 << 16) + 42
for i := 0; i < nFiles; i++ {
- _, err := w.Create(fmt.Sprintf("%d.dat", i))
+ _, err := w.CreateHeader(&FileHeader{
+ Name: fmt.Sprintf("%d.dat", i),
+ Method: Store, // avoid Issue 6136 and Issue 6138
+ })
if err != nil {
t.Fatalf("creating file %d: %v", i, err)
}
@@ -105,29 +107,156 @@ func TestFileHeaderRoundTrip64(t *testing.T) {
testHeaderRoundTrip(fh, uint32max, fh.UncompressedSize64, t)
}
+type repeatedByte struct {
+ off int64
+ b byte
+ n int64
+}
+
+// rleBuffer is a run-length-encoded byte buffer.
+// It's an io.Writer (like a bytes.Buffer) and also an io.ReaderAt,
+// allowing random-access reads.
+type rleBuffer struct {
+ buf []repeatedByte
+}
+
+func (r *rleBuffer) Size() int64 {
+ if len(r.buf) == 0 {
+ return 0
+ }
+ last := &r.buf[len(r.buf)-1]
+ return last.off + last.n
+}
+
+func (r *rleBuffer) Write(p []byte) (n int, err error) {
+ var rp *repeatedByte
+ if len(r.buf) > 0 {
+ rp = &r.buf[len(r.buf)-1]
+ // Fast path, if p is entirely the same byte repeated.
+ if lastByte := rp.b; len(p) > 0 && p[0] == lastByte {
+ all := true
+ for _, b := range p {
+ if b != lastByte {
+ all = false
+ break
+ }
+ }
+ if all {
+ rp.n += int64(len(p))
+ return len(p), nil
+ }
+ }
+ }
+
+ for _, b := range p {
+ if rp == nil || rp.b != b {
+ r.buf = append(r.buf, repeatedByte{r.Size(), b, 1})
+ rp = &r.buf[len(r.buf)-1]
+ } else {
+ rp.n++
+ }
+ }
+ return len(p), nil
+}
+
+func (r *rleBuffer) ReadAt(p []byte, off int64) (n int, err error) {
+ if len(p) == 0 {
+ return
+ }
+ skipParts := sort.Search(len(r.buf), func(i int) bool {
+ part := &r.buf[i]
+ return part.off+part.n > off
+ })
+ parts := r.buf[skipParts:]
+ if len(parts) > 0 {
+ skipBytes := off - parts[0].off
+ for len(parts) > 0 {
+ part := parts[0]
+ for i := skipBytes; i < part.n; i++ {
+ if n == len(p) {
+ return
+ }
+ p[n] = part.b
+ n++
+ }
+ parts = parts[1:]
+ skipBytes = 0
+ }
+ }
+ if n != len(p) {
+ err = io.ErrUnexpectedEOF
+ }
+ return
+}
+
+// Just testing the rleBuffer used in the Zip64 test above. Not used by the zip code.
+func TestRLEBuffer(t *testing.T) {
+ b := new(rleBuffer)
+ var all []byte
+ writes := []string{"abcdeee", "eeeeeee", "eeeefghaaiii"}
+ for _, w := range writes {
+ b.Write([]byte(w))
+ all = append(all, w...)
+ }
+ if len(b.buf) != 10 {
+ t.Fatalf("len(b.buf) = %d; want 10", len(b.buf))
+ }
+
+ for i := 0; i < len(all); i++ {
+ for j := 0; j < len(all)-i; j++ {
+ buf := make([]byte, j)
+ n, err := b.ReadAt(buf, int64(i))
+ if err != nil || n != len(buf) {
+ t.Errorf("ReadAt(%d, %d) = %d, %v; want %d, nil", i, j, n, err, len(buf))
+ }
+ if !bytes.Equal(buf, all[i:i+j]) {
+ t.Errorf("ReadAt(%d, %d) = %q; want %q", i, j, buf, all[i:i+j])
+ }
+ }
+ }
+}
+
+// fakeHash32 is a dummy Hash32 that always returns 0.
+type fakeHash32 struct {
+ hash.Hash32
+}
+
+func (fakeHash32) Write(p []byte) (int, error) { return len(p), nil }
+func (fakeHash32) Sum32() uint32 { return 0 }
+
func TestZip64(t *testing.T) {
if testing.Short() {
t.Skip("slow test; skipping")
}
+ const size = 1 << 32 // before the "END\n" part
+ testZip64(t, size)
+}
+
+func testZip64(t testing.TB, size int64) {
+ const chunkSize = 1024
+ chunks := int(size / chunkSize)
// write 2^32 bytes plus "END\n" to a zip file
- buf := new(bytes.Buffer)
+ buf := new(rleBuffer)
w := NewWriter(buf)
- f, err := w.Create("huge.txt")
+ f, err := w.CreateHeader(&FileHeader{
+ Name: "huge.txt",
+ Method: Store,
+ })
if err != nil {
t.Fatal(err)
}
- chunk := make([]byte, 1024)
+ f.(*fileWriter).crc32 = fakeHash32{}
+ chunk := make([]byte, chunkSize)
for i := range chunk {
chunk[i] = '.'
}
- chunk[len(chunk)-1] = '\n'
- end := []byte("END\n")
- for i := 0; i < (1<<32)/1024; i++ {
+ for i := 0; i < chunks; i++ {
_, err := f.Write(chunk)
if err != nil {
t.Fatal("write chunk:", err)
}
}
+ end := []byte("END\n")
_, err = f.Write(end)
if err != nil {
t.Fatal("write end:", err)
@@ -137,7 +266,7 @@ func TestZip64(t *testing.T) {
}
// read back zip file and check that we get to the end of it
- r, err := NewReader(bytes.NewReader(buf.Bytes()), int64(buf.Len()))
+ r, err := NewReader(buf, int64(buf.Size()))
if err != nil {
t.Fatal("reader:", err)
}
@@ -146,7 +275,8 @@ func TestZip64(t *testing.T) {
if err != nil {
t.Fatal("opening:", err)
}
- for i := 0; i < (1<<32)/1024; i++ {
+ rc.(*checksumReader).hash = fakeHash32{}
+ for i := 0; i < chunks; i++ {
_, err := io.ReadFull(rc, chunk)
if err != nil {
t.Fatal("read:", err)
@@ -163,11 +293,13 @@ func TestZip64(t *testing.T) {
if err != nil {
t.Fatal("closing:", err)
}
- if got, want := f0.UncompressedSize, uint32(uint32max); got != want {
- t.Errorf("UncompressedSize %d, want %d", got, want)
+ if size == 1<<32 {
+ if got, want := f0.UncompressedSize, uint32(uint32max); got != want {
+ t.Errorf("UncompressedSize %d, want %d", got, want)
+ }
}
- if got, want := f0.UncompressedSize64, (1<<32)+uint64(len(end)); got != want {
+ if got, want := f0.UncompressedSize64, uint64(size)+uint64(len(end)); got != want {
t.Errorf("UncompressedSize64 %d, want %d", got, want)
}
}
@@ -253,3 +385,11 @@ func TestZeroLengthHeader(t *testing.T) {
}
testValidHeader(&h, t)
}
+
+// Just benchmarking how fast the Zip64 test above is. Not related to
+// our zip performance, since the test above disabled CRC32 and flate.
+func BenchmarkZip64Test(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ testZip64(b, 1<<26)
+ }
+}
diff --git a/src/pkg/bufio/bufio.go b/src/pkg/bufio/bufio.go
index 10e12d42b..d1ff3c9ed 100644
--- a/src/pkg/bufio/bufio.go
+++ b/src/pkg/bufio/bufio.go
@@ -51,12 +51,9 @@ func NewReaderSize(rd io.Reader, size int) *Reader {
if size < minReadBufferSize {
size = minReadBufferSize
}
- return &Reader{
- buf: make([]byte, size),
- rd: rd,
- lastByte: -1,
- lastRuneSize: -1,
- }
+ r := new(Reader)
+ r.reset(make([]byte, size), rd)
+ return r
}
// NewReader returns a new Reader whose buffer has the default size.
@@ -64,6 +61,21 @@ func NewReader(rd io.Reader) *Reader {
return NewReaderSize(rd, defaultBufSize)
}
+// Reset discards any buffered data, resets all state, and switches
+// the buffered reader to read from r.
+func (b *Reader) Reset(r io.Reader) {
+ b.reset(b.buf, r)
+}
+
+func (b *Reader) reset(buf []byte, r io.Reader) {
+ *b = Reader{
+ buf: buf,
+ rd: r,
+ lastByte: -1,
+ lastRuneSize: -1,
+ }
+}
+
var errNegativeRead = errors.New("bufio: reader returned negative count from Read")
// fill reads a new chunk into the buffer.
@@ -234,7 +246,7 @@ func (b *Reader) Buffered() int { return b.w - b.r }
// ReadSlice reads until the first occurrence of delim in the input,
// returning a slice pointing at the bytes in the buffer.
-// The bytes stop being valid at the next read call.
+// The bytes stop being valid at the next read.
// If ReadSlice encounters an error before finding a delimiter,
// it returns all the data in the buffer and the error itself (often io.EOF).
// ReadSlice fails with error ErrBufferFull if the buffer fills without a delim.
@@ -381,7 +393,8 @@ func (b *Reader) ReadBytes(delim byte) (line []byte, err error) {
// For simple uses, a Scanner may be more convenient.
func (b *Reader) ReadString(delim byte) (line string, err error) {
bytes, err := b.ReadBytes(delim)
- return string(bytes), err
+ line = string(bytes)
+ return line, err
}
// WriteTo implements io.WriterTo.
@@ -424,6 +437,9 @@ func (b *Reader) writeBuf(w io.Writer) (int64, error) {
// Writer implements buffering for an io.Writer object.
// If an error occurs writing to a Writer, no more data will be
// accepted and all subsequent writes will return the error.
+// After all data has been written, the client should call the
+// Flush method to guarantee all data has been forwarded to
+// the underlying io.Writer.
type Writer struct {
err error
buf []byte
@@ -434,28 +450,41 @@ type Writer struct {
// NewWriterSize returns a new Writer whose buffer has at least the specified
// size. If the argument io.Writer is already a Writer with large enough
// size, it returns the underlying Writer.
-func NewWriterSize(wr io.Writer, size int) *Writer {
+func NewWriterSize(w io.Writer, size int) *Writer {
// Is it already a Writer?
- b, ok := wr.(*Writer)
+ b, ok := w.(*Writer)
if ok && len(b.buf) >= size {
return b
}
if size <= 0 {
size = defaultBufSize
}
- b = new(Writer)
- b.buf = make([]byte, size)
- b.wr = wr
- return b
+ return &Writer{
+ buf: make([]byte, size),
+ wr: w,
+ }
}
// NewWriter returns a new Writer whose buffer has the default size.
-func NewWriter(wr io.Writer) *Writer {
- return NewWriterSize(wr, defaultBufSize)
+func NewWriter(w io.Writer) *Writer {
+ return NewWriterSize(w, defaultBufSize)
+}
+
+// Reset discards any unflushed buffered data, clears any error, and
+// resets b to write its output to w.
+func (b *Writer) Reset(w io.Writer) {
+ b.err = nil
+ b.n = 0
+ b.wr = w
}
// Flush writes any buffered data to the underlying io.Writer.
func (b *Writer) Flush() error {
+ err := b.flush()
+ return err
+}
+
+func (b *Writer) flush() error {
if b.err != nil {
return b.err
}
@@ -498,7 +527,7 @@ func (b *Writer) Write(p []byte) (nn int, err error) {
} else {
n = copy(b.buf[b.n:], p)
b.n += n
- b.Flush()
+ b.flush()
}
nn += n
p = p[n:]
@@ -517,7 +546,7 @@ func (b *Writer) WriteByte(c byte) error {
if b.err != nil {
return b.err
}
- if b.Available() <= 0 && b.Flush() != nil {
+ if b.Available() <= 0 && b.flush() != nil {
return b.err
}
b.buf[b.n] = c
@@ -540,7 +569,7 @@ func (b *Writer) WriteRune(r rune) (size int, err error) {
}
n := b.Available()
if n < utf8.UTFMax {
- if b.Flush(); b.err != nil {
+ if b.flush(); b.err != nil {
return 0, b.err
}
n = b.Available()
@@ -565,7 +594,7 @@ func (b *Writer) WriteString(s string) (int, error) {
b.n += n
nn += n
s = s[n:]
- b.Flush()
+ b.flush()
}
if b.err != nil {
return nn, b.err
@@ -586,7 +615,7 @@ func (b *Writer) ReadFrom(r io.Reader) (n int64, err error) {
var m int
for {
if b.Available() == 0 {
- if err1 := b.Flush(); err1 != nil {
+ if err1 := b.flush(); err1 != nil {
return n, err1
}
}
@@ -603,7 +632,7 @@ func (b *Writer) ReadFrom(r io.Reader) (n int64, err error) {
if err == io.EOF {
// If we filled the buffer exactly, flush pre-emptively.
if b.Available() == 0 {
- err = b.Flush()
+ err = b.flush()
} else {
err = nil
}
diff --git a/src/pkg/bufio/bufio_test.go b/src/pkg/bufio/bufio_test.go
index 9fcbfda68..41bd3d456 100644
--- a/src/pkg/bufio/bufio_test.go
+++ b/src/pkg/bufio/bufio_test.go
@@ -1025,6 +1025,38 @@ func TestWriterReadFromWhileFull(t *testing.T) {
}
}
+func TestReaderReset(t *testing.T) {
+ r := NewReader(strings.NewReader("foo foo"))
+ buf := make([]byte, 3)
+ r.Read(buf)
+ if string(buf) != "foo" {
+ t.Errorf("buf = %q; want foo", buf)
+ }
+ r.Reset(strings.NewReader("bar bar"))
+ all, err := ioutil.ReadAll(r)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if string(all) != "bar bar" {
+ t.Errorf("ReadAll = %q; want bar bar", all)
+ }
+}
+
+func TestWriterReset(t *testing.T) {
+ var buf1, buf2 bytes.Buffer
+ w := NewWriter(&buf1)
+ w.WriteString("foo")
+ w.Reset(&buf2) // and not flushed
+ w.WriteString("bar")
+ w.Flush()
+ if buf1.String() != "" {
+ t.Errorf("buf1 = %q; want empty", buf1.String())
+ }
+ if buf2.String() != "bar" {
+ t.Errorf("buf2 = %q; want bar", buf2.String())
+ }
+}
+
// An onlyReader only implements io.Reader, no matter what other methods the underlying implementation may have.
type onlyReader struct {
r io.Reader
@@ -1105,3 +1137,46 @@ func BenchmarkWriterCopyNoReadFrom(b *testing.B) {
io.Copy(dst, src)
}
}
+
+func BenchmarkReaderEmpty(b *testing.B) {
+ b.ReportAllocs()
+ str := strings.Repeat("x", 16<<10)
+ for i := 0; i < b.N; i++ {
+ br := NewReader(strings.NewReader(str))
+ n, err := io.Copy(ioutil.Discard, br)
+ if err != nil {
+ b.Fatal(err)
+ }
+ if n != int64(len(str)) {
+ b.Fatal("wrong length")
+ }
+ }
+}
+
+func BenchmarkWriterEmpty(b *testing.B) {
+ b.ReportAllocs()
+ str := strings.Repeat("x", 1<<10)
+ bs := []byte(str)
+ for i := 0; i < b.N; i++ {
+ bw := NewWriter(ioutil.Discard)
+ bw.Flush()
+ bw.WriteByte('a')
+ bw.Flush()
+ bw.WriteRune('B')
+ bw.Flush()
+ bw.Write(bs)
+ bw.Flush()
+ bw.WriteString(str)
+ bw.Flush()
+ }
+}
+
+func BenchmarkWriterFlush(b *testing.B) {
+ b.ReportAllocs()
+ bw := NewWriter(ioutil.Discard)
+ str := strings.Repeat("x", 50)
+ for i := 0; i < b.N; i++ {
+ bw.WriteString(str)
+ bw.Flush()
+ }
+}
diff --git a/src/pkg/bufio/example_test.go b/src/pkg/bufio/example_test.go
index 08a39441e..3da914142 100644
--- a/src/pkg/bufio/example_test.go
+++ b/src/pkg/bufio/example_test.go
@@ -12,6 +12,14 @@ import (
"strings"
)
+func ExampleWriter() {
+ w := bufio.NewWriter(os.Stdout)
+ fmt.Fprint(w, "Hello, ")
+ fmt.Fprint(w, "world!")
+ w.Flush() // Don't forget to flush!
+ // Output: Hello, world!
+}
+
// The simplest use of a Scanner, to read standard input as a set of lines.
func ExampleScanner_lines() {
scanner := bufio.NewScanner(os.Stdin)
diff --git a/src/pkg/bufio/scan.go b/src/pkg/bufio/scan.go
index 2e1a2e999..423505fbc 100644
--- a/src/pkg/bufio/scan.go
+++ b/src/pkg/bufio/scan.go
@@ -44,8 +44,8 @@ type Scanner struct {
// to give. The return values are the number of bytes to advance the input
// and the next token to return to the user, plus an error, if any. If the
// data does not yet hold a complete token, for instance if it has no newline
-// while scanning lines, SplitFunc can return (0, nil) to signal the Scanner
-// to read more data into the slice and try again with a longer slice
+// while scanning lines, SplitFunc can return (0, nil, nil) to signal the
+// Scanner to read more data into the slice and try again with a longer slice
// starting at the same point in the input.
//
// If the returned error is non-nil, scanning stops and the error
@@ -287,7 +287,7 @@ func ScanLines(data []byte, atEOF bool) (advance int, token []byte, err error) {
return 0, nil, nil
}
-// isSpace returns whether the character is a Unicode white space character.
+// isSpace reports whether the character is a Unicode white space character.
// We avoid dependency on the unicode package, but check validity of the implementation
// in the tests.
func isSpace(r rune) bool {
diff --git a/src/pkg/builtin/builtin.go b/src/pkg/builtin/builtin.go
index 128a1b5f8..51550a459 100644
--- a/src/pkg/builtin/builtin.go
+++ b/src/pkg/builtin/builtin.go
@@ -236,6 +236,19 @@ func panic(v interface{})
// panicking.
func recover() interface{}
+// The print built-in function formats its arguments in an implementation-
+// specific way and writes the result to standard error.
+// Print is useful for bootstrapping and debugging; it is not guaranteed
+// to stay in the language.
+func print(args ...Type)
+
+// The println built-in function formats its arguments in an implementation-
+// specific way and writes the result to standard error.
+// Spaces are always added between arguments and a newline is appended.
+// Println is useful for bootstrapping and debugging; it is not guaranteed
+// to stay in the language.
+func println(args ...Type)
+
// The error built-in interface type is the conventional interface for
// representing an error condition, with the nil value representing no error.
type error interface {
diff --git a/src/pkg/bytes/asm_386.s b/src/pkg/bytes/asm_386.s
deleted file mode 100644
index 27cd4e787..000000000
--- a/src/pkg/bytes/asm_386.s
+++ /dev/null
@@ -1,17 +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.
-
-TEXT ·IndexByte(SB),7,$0
- MOVL s+0(FP), SI
- MOVL s_len+4(FP), CX
- MOVB c+12(FP), AL
- MOVL SI, DI
- CLD; REPN; SCASB
- JZ 3(PC)
- MOVL $-1, ret+16(FP)
- RET
- SUBL SI, DI
- SUBL $1, DI
- MOVL DI, ret+16(FP)
- RET
diff --git a/src/pkg/bytes/asm_amd64.s b/src/pkg/bytes/asm_amd64.s
deleted file mode 100644
index b84957b6d..000000000
--- a/src/pkg/bytes/asm_amd64.s
+++ /dev/null
@@ -1,91 +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.
-
-TEXT ·IndexByte(SB),7,$0
- MOVQ s+0(FP), SI
- MOVQ s_len+8(FP), BX
- MOVB c+24(FP), AL
- MOVQ SI, DI
-
- CMPQ BX, $16
- JLT small
-
- // round up to first 16-byte boundary
- TESTQ $15, SI
- JZ aligned
- MOVQ SI, CX
- ANDQ $~15, CX
- ADDQ $16, CX
-
- // search the beginning
- SUBQ SI, CX
- REPN; SCASB
- JZ success
-
-// DI is 16-byte aligned; get ready to search using SSE instructions
-aligned:
- // round down to last 16-byte boundary
- MOVQ BX, R11
- ADDQ SI, R11
- ANDQ $~15, R11
-
- // shuffle X0 around so that each byte contains c
- MOVD AX, X0
- PUNPCKLBW X0, X0
- PUNPCKLBW X0, X0
- PSHUFL $0, X0, X0
- JMP condition
-
-sse:
- // move the next 16-byte chunk of the buffer into X1
- MOVO (DI), X1
- // compare bytes in X0 to X1
- PCMPEQB X0, X1
- // take the top bit of each byte in X1 and put the result in DX
- PMOVMSKB X1, DX
- TESTL DX, DX
- JNZ ssesuccess
- ADDQ $16, DI
-
-condition:
- CMPQ DI, R11
- JLT sse
-
- // search the end
- MOVQ SI, CX
- ADDQ BX, CX
- SUBQ R11, CX
- // if CX == 0, the zero flag will be set and we'll end up
- // returning a false success
- JZ failure
- REPN; SCASB
- JZ success
-
-failure:
- MOVQ $-1, ret+32(FP)
- RET
-
-// handle for lengths < 16
-small:
- MOVQ BX, CX
- REPN; SCASB
- JZ success
- MOVQ $-1, ret+32(FP)
- RET
-
-// we've found the chunk containing the byte
-// now just figure out which specific byte it is
-ssesuccess:
- // get the index of the least significant set bit
- BSFW DX, DX
- SUBQ SI, DI
- ADDQ DI, DX
- MOVQ DX, ret+32(FP)
- RET
-
-success:
- SUBQ SI, DI
- SUBL $1, DI
- MOVQ DI, ret+32(FP)
- RET
diff --git a/src/pkg/bytes/asm_arm.s b/src/pkg/bytes/asm_arm.s
deleted file mode 100644
index 2e9f805a4..000000000
--- a/src/pkg/bytes/asm_arm.s
+++ /dev/null
@@ -1,56 +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.
-
-TEXT ·IndexByte(SB),7,$0
- MOVW s+0(FP), R0
- MOVW s_len+4(FP), R1
- MOVBU c+12(FP), R2 // byte to find
- MOVW R0, R4 // store base for later
- ADD R0, R1 // end
-
-_loop:
- CMP R0, R1
- B.EQ _notfound
- MOVBU.P 1(R0), R3
- CMP R2, R3
- B.NE _loop
-
- SUB $1, R0 // R0 will be one beyond the position we want
- SUB R4, R0 // remove base
- MOVW R0, ret+16(FP)
- RET
-
-_notfound:
- MOVW $-1, R0
- MOVW R0, ret+16(FP)
- RET
-
-TEXT ·Equal(SB),7,$0
- MOVW a_len+4(FP), R1
- MOVW b_len+16(FP), R3
-
- CMP R1, R3 // unequal lengths are not equal
- B.NE _notequal
-
- MOVW a+0(FP), R0
- MOVW b+12(FP), R2
- ADD R0, R1 // end
-
-_next:
- CMP R0, R1
- B.EQ _equal // reached the end
- MOVBU.P 1(R0), R4
- MOVBU.P 1(R2), R5
- CMP R4, R5
- B.EQ _next
-
-_notequal:
- MOVW $0, R0
- MOVBU R0, ret+24(FP)
- RET
-
-_equal:
- MOVW $1, R0
- MOVBU R0, ret+24(FP)
- RET
diff --git a/src/pkg/bytes/bytes.go b/src/pkg/bytes/bytes.go
index e42f74439..01a5d9ae4 100644
--- a/src/pkg/bytes/bytes.go
+++ b/src/pkg/bytes/bytes.go
@@ -11,32 +11,6 @@ import (
"unicode/utf8"
)
-// Compare returns an integer comparing two byte slices lexicographically.
-// The result will be 0 if a==b, -1 if a < b, and +1 if a > b.
-// A nil argument is equivalent to an empty slice.
-func Compare(a, b []byte) int {
- m := len(a)
- if m > len(b) {
- m = len(b)
- }
- for i, ac := range a[0:m] {
- bc := b[i]
- switch {
- case ac > bc:
- return 1
- case ac < bc:
- return -1
- }
- }
- switch {
- case len(a) < len(b):
- return -1
- case len(a) > len(b):
- return 1
- }
- return 0
-}
-
func equalPortable(a, b []byte) bool {
if len(a) != len(b) {
return false
@@ -103,7 +77,7 @@ func Count(s, sep []byte) int {
return count
}
-// Contains returns whether subslice is within b.
+// Contains reports whether subslice is within b.
func Contains(b, subslice []byte) bool {
return Index(b, subslice) != -1
}
@@ -401,10 +375,7 @@ func Repeat(b []byte, count int) []byte {
nb := make([]byte, len(b)*count)
bp := 0
for i := 0; i < count; i++ {
- for j := 0; j < len(b); j++ {
- nb[bp] = b[j]
- bp++
- }
+ bp += copy(nb[bp:], b)
}
return nb
}
diff --git a/src/pkg/bytes/bytes.s b/src/pkg/bytes/bytes.s
new file mode 100644
index 000000000..55103bae0
--- /dev/null
+++ b/src/pkg/bytes/bytes.s
@@ -0,0 +1,5 @@
+// Copyright 2013 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 here just to make the go tool happy.
diff --git a/src/pkg/bytes/bytes_decl.go b/src/pkg/bytes/bytes_decl.go
index fbf928275..617d7489a 100644
--- a/src/pkg/bytes/bytes_decl.go
+++ b/src/pkg/bytes/bytes_decl.go
@@ -7,10 +7,18 @@ package bytes
//go:noescape
// IndexByte returns the index of the first instance of c in s, or -1 if c is not present in s.
-func IndexByte(s []byte, c byte) int // asm_$GOARCH.s
+func IndexByte(s []byte, c byte) int // ../runtime/asm_$GOARCH.s
//go:noescape
-// Equal returns a boolean reporting whether a == b.
+// Equal returns a boolean reporting whether a and b
+// are the same length and contain the same bytes.
// A nil argument is equivalent to an empty slice.
-func Equal(a, b []byte) bool // asm_arm.s or ../runtime/asm_{386,amd64}.s
+func Equal(a, b []byte) bool // ../runtime/asm_$GOARCH.s
+
+//go:noescape
+
+// Compare returns an integer comparing two byte slices lexicographically.
+// The result will be 0 if a==b, -1 if a < b, and +1 if a > b.
+// A nil argument is equivalent to an empty slice.
+func Compare(a, b []byte) int // ../runtime/noasm_arm.goc or ../runtime/asm_{386,amd64}.s
diff --git a/src/pkg/bytes/bytes_test.go b/src/pkg/bytes/bytes_test.go
index d296224ac..ab5da4fbf 100644
--- a/src/pkg/bytes/bytes_test.go
+++ b/src/pkg/bytes/bytes_test.go
@@ -47,7 +47,7 @@ type BinOpTest struct {
i int
}
-var compareTests = []struct {
+var equalTests = []struct {
a, b []byte
i int
}{
@@ -73,12 +73,8 @@ var compareTests = []struct {
{nil, []byte("a"), -1},
}
-func TestCompare(t *testing.T) {
+func TestEqual(t *testing.T) {
for _, tt := range compareTests {
- cmp := Compare(tt.a, tt.b)
- if cmp != tt.i {
- t.Errorf(`Compare(%q, %q) = %v`, tt.a, tt.b, cmp)
- }
eql := Equal(tt.a, tt.b)
if eql != (tt.i == 0) {
t.Errorf(`Equal(%q, %q) = %v`, tt.a, tt.b, eql)
@@ -90,7 +86,7 @@ func TestCompare(t *testing.T) {
}
}
-func TestEqual(t *testing.T) {
+func TestEqualExhaustive(t *testing.T) {
var size = 128
if testing.Short() {
size = 32
@@ -147,6 +143,7 @@ var indexTests = []BinOpTest{
{"", "a", -1},
{"", "foo", -1},
{"fo", "foo", -1},
+ {"foo", "baz", -1},
{"foo", "foo", 0},
{"oofofoofooo", "f", 2},
{"oofofoofooo", "foo", 4},
@@ -1086,6 +1083,24 @@ func TestTitle(t *testing.T) {
}
}
+var ToTitleTests = []TitleTest{
+ {"", ""},
+ {"a", "A"},
+ {" aaa aaa aaa ", " AAA AAA AAA "},
+ {" Aaa Aaa Aaa ", " AAA AAA AAA "},
+ {"123a456", "123A456"},
+ {"double-blind", "DOUBLE-BLIND"},
+ {"ÿøû", "ŸØÛ"},
+}
+
+func TestToTitle(t *testing.T) {
+ for _, tt := range ToTitleTests {
+ if s := string(ToTitle([]byte(tt.in))); s != tt.out {
+ t.Errorf("ToTitle(%q) = %q, want %q", tt.in, s, tt.out)
+ }
+ }
+}
+
var EqualFoldTests = []struct {
s, t string
out bool
@@ -1114,6 +1129,37 @@ func TestEqualFold(t *testing.T) {
}
}
+func TestBufferGrowNegative(t *testing.T) {
+ defer func() {
+ if err := recover(); err == nil {
+ t.Fatal("Grow(-1) should have paniced")
+ }
+ }()
+ var b Buffer
+ b.Grow(-1)
+}
+
+func TestBufferTruncateNegative(t *testing.T) {
+ defer func() {
+ if err := recover(); err == nil {
+ t.Fatal("Truncate(-1) should have paniced")
+ }
+ }()
+ var b Buffer
+ b.Truncate(-1)
+}
+
+func TestBufferTruncateOutOfRange(t *testing.T) {
+ defer func() {
+ if err := recover(); err == nil {
+ t.Fatal("Truncate(20) should have paniced")
+ }
+ }()
+ var b Buffer
+ b.Write(make([]byte, 10))
+ b.Truncate(20)
+}
+
var makeFieldsInput = func() []byte {
x := make([]byte, 1<<20)
// Input is ~10% space, ~10% 2-byte UTF-8, rest ASCII non-space.
diff --git a/src/pkg/bytes/compare_test.go b/src/pkg/bytes/compare_test.go
new file mode 100644
index 000000000..0a36f5ad3
--- /dev/null
+++ b/src/pkg/bytes/compare_test.go
@@ -0,0 +1,204 @@
+package bytes_test
+
+import (
+ . "bytes"
+ "testing"
+)
+
+var compareTests = []struct {
+ a, b []byte
+ i int
+}{
+ {[]byte(""), []byte(""), 0},
+ {[]byte("a"), []byte(""), 1},
+ {[]byte(""), []byte("a"), -1},
+ {[]byte("abc"), []byte("abc"), 0},
+ {[]byte("ab"), []byte("abc"), -1},
+ {[]byte("abc"), []byte("ab"), 1},
+ {[]byte("x"), []byte("ab"), 1},
+ {[]byte("ab"), []byte("x"), -1},
+ {[]byte("x"), []byte("a"), 1},
+ {[]byte("b"), []byte("x"), -1},
+ // test runtime·memeq's chunked implementation
+ {[]byte("abcdefgh"), []byte("abcdefgh"), 0},
+ {[]byte("abcdefghi"), []byte("abcdefghi"), 0},
+ {[]byte("abcdefghi"), []byte("abcdefghj"), -1},
+ // nil tests
+ {nil, nil, 0},
+ {[]byte(""), nil, 0},
+ {nil, []byte(""), 0},
+ {[]byte("a"), nil, 1},
+ {nil, []byte("a"), -1},
+}
+
+func TestCompare(t *testing.T) {
+ for _, tt := range compareTests {
+ cmp := Compare(tt.a, tt.b)
+ if cmp != tt.i {
+ t.Errorf(`Compare(%q, %q) = %v`, tt.a, tt.b, cmp)
+ }
+ }
+}
+
+func TestCompareIdenticalSlice(t *testing.T) {
+ var b = []byte("Hello Gophers!")
+ if Compare(b, b) != 0 {
+ t.Error("b != b")
+ }
+ if Compare(b, b[:1]) != 1 {
+ t.Error("b > b[:1] failed")
+ }
+}
+
+func TestCompareBytes(t *testing.T) {
+ n := 128
+ a := make([]byte, n+1)
+ b := make([]byte, n+1)
+ for len := 0; len < 128; len++ {
+ // randomish but deterministic data. No 0 or 255.
+ for i := 0; i < len; i++ {
+ a[i] = byte(1 + 31*i%254)
+ b[i] = byte(1 + 31*i%254)
+ }
+ // data past the end is different
+ for i := len; i <= n; i++ {
+ a[i] = 8
+ b[i] = 9
+ }
+ cmp := Compare(a[:len], b[:len])
+ if cmp != 0 {
+ t.Errorf(`CompareIdentical(%d) = %d`, len, cmp)
+ }
+ if len > 0 {
+ cmp = Compare(a[:len-1], b[:len])
+ if cmp != -1 {
+ t.Errorf(`CompareAshorter(%d) = %d`, len, cmp)
+ }
+ cmp = Compare(a[:len], b[:len-1])
+ if cmp != 1 {
+ t.Errorf(`CompareBshorter(%d) = %d`, len, cmp)
+ }
+ }
+ for k := 0; k < len; k++ {
+ b[k] = a[k] - 1
+ cmp = Compare(a[:len], b[:len])
+ if cmp != 1 {
+ t.Errorf(`CompareAbigger(%d,%d) = %d`, len, k, cmp)
+ }
+ b[k] = a[k] + 1
+ cmp = Compare(a[:len], b[:len])
+ if cmp != -1 {
+ t.Errorf(`CompareBbigger(%d,%d) = %d`, len, k, cmp)
+ }
+ b[k] = a[k]
+ }
+ }
+}
+
+func BenchmarkCompareBytesEqual(b *testing.B) {
+ b1 := []byte("Hello Gophers!")
+ b2 := []byte("Hello Gophers!")
+ for i := 0; i < b.N; i++ {
+ if Compare(b1, b2) != 0 {
+ b.Fatal("b1 != b2")
+ }
+ }
+}
+
+func BenchmarkCompareBytesToNil(b *testing.B) {
+ b1 := []byte("Hello Gophers!")
+ var b2 []byte
+ for i := 0; i < b.N; i++ {
+ if Compare(b1, b2) != 1 {
+ b.Fatal("b1 > b2 failed")
+ }
+ }
+}
+
+func BenchmarkCompareBytesEmpty(b *testing.B) {
+ b1 := []byte("")
+ b2 := b1
+ for i := 0; i < b.N; i++ {
+ if Compare(b1, b2) != 0 {
+ b.Fatal("b1 != b2")
+ }
+ }
+}
+
+func BenchmarkCompareBytesIdentical(b *testing.B) {
+ b1 := []byte("Hello Gophers!")
+ b2 := b1
+ for i := 0; i < b.N; i++ {
+ if Compare(b1, b2) != 0 {
+ b.Fatal("b1 != b2")
+ }
+ }
+}
+
+func BenchmarkCompareBytesSameLength(b *testing.B) {
+ b1 := []byte("Hello Gophers!")
+ b2 := []byte("Hello, Gophers")
+ for i := 0; i < b.N; i++ {
+ if Compare(b1, b2) != -1 {
+ b.Fatal("b1 < b2 failed")
+ }
+ }
+}
+
+func BenchmarkCompareBytesDifferentLength(b *testing.B) {
+ b1 := []byte("Hello Gophers!")
+ b2 := []byte("Hello, Gophers!")
+ for i := 0; i < b.N; i++ {
+ if Compare(b1, b2) != -1 {
+ b.Fatal("b1 < b2 failed")
+ }
+ }
+}
+
+func BenchmarkCompareBytesBigUnaligned(b *testing.B) {
+ b.StopTimer()
+ b1 := make([]byte, 0, 1<<20)
+ for len(b1) < 1<<20 {
+ b1 = append(b1, "Hello Gophers!"...)
+ }
+ b2 := append([]byte("hello"), b1...)
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ if Compare(b1, b2[len("hello"):]) != 0 {
+ b.Fatal("b1 != b2")
+ }
+ }
+ b.SetBytes(int64(len(b1)))
+}
+
+func BenchmarkCompareBytesBig(b *testing.B) {
+ b.StopTimer()
+ b1 := make([]byte, 0, 1<<20)
+ for len(b1) < 1<<20 {
+ b1 = append(b1, "Hello Gophers!"...)
+ }
+ b2 := append([]byte{}, b1...)
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ if Compare(b1, b2) != 0 {
+ b.Fatal("b1 != b2")
+ }
+ }
+ b.SetBytes(int64(len(b1)))
+}
+
+func BenchmarkCompareBytesBigIdentical(b *testing.B) {
+ b.StopTimer()
+ b1 := make([]byte, 0, 1<<20)
+ for len(b1) < 1<<20 {
+ b1 = append(b1, "Hello Gophers!"...)
+ }
+ b2 := b1
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ if Compare(b1, b2) != 0 {
+ b.Fatal("b1 != b2")
+ }
+ }
+ b.SetBytes(int64(len(b1)))
+}
diff --git a/src/pkg/bytes/reader_test.go b/src/pkg/bytes/reader_test.go
index f0a3e26c4..19f014da0 100644
--- a/src/pkg/bytes/reader_test.go
+++ b/src/pkg/bytes/reader_test.go
@@ -113,6 +113,41 @@ func TestReaderWriteTo(t *testing.T) {
}
}
+func TestReaderLen(t *testing.T) {
+ const data = "hello world"
+ r := NewReader([]byte(data))
+ if got, want := r.Len(), 11; got != want {
+ t.Errorf("r.Len(): got %d, want %d", got, want)
+ }
+ if n, err := r.Read(make([]byte, 10)); err != nil || n != 10 {
+ t.Errorf("Read failed: read %d %v", n, err)
+ }
+ if got, want := r.Len(), 1; got != want {
+ t.Errorf("r.Len(): got %d, want %d", got, want)
+ }
+ if n, err := r.Read(make([]byte, 1)); err != nil || n != 1 {
+ t.Errorf("Read failed: read %d %v", n, err)
+ }
+ if got, want := r.Len(), 0; got != want {
+ t.Errorf("r.Len(): got %d, want %d", got, want)
+ }
+}
+
+func TestReaderDoubleUnreadRune(t *testing.T) {
+ buf := NewBuffer([]byte("groucho"))
+ if _, _, err := buf.ReadRune(); err != nil {
+ // should not happen
+ t.Fatal(err)
+ }
+ if err := buf.UnreadByte(); err != nil {
+ // should not happen
+ t.Fatal(err)
+ }
+ if err := buf.UnreadByte(); err == nil {
+ t.Fatal("UnreadByte: expected error, got nil")
+ }
+}
+
// verify that copying from an empty reader always has the same results,
// regardless of the presence of a WriteTo method.
func TestReaderCopyNothing(t *testing.T) {
diff --git a/src/pkg/compress/bzip2/bit_reader.go b/src/pkg/compress/bzip2/bit_reader.go
index ab1d60651..32d1036ae 100644
--- a/src/pkg/compress/bzip2/bit_reader.go
+++ b/src/pkg/compress/bzip2/bit_reader.go
@@ -77,6 +77,14 @@ func (br *bitReader) ReadBit() bool {
return n != 0
}
+func (br *bitReader) TryReadBit() (bit byte, ok bool) {
+ if br.bits > 0 {
+ br.bits--
+ return byte(br.n>>br.bits) & 1, true
+ }
+ return 0, false
+}
+
func (br *bitReader) Err() error {
return br.err
}
diff --git a/src/pkg/compress/bzip2/bzip2.go b/src/pkg/compress/bzip2/bzip2.go
index 3dc8c6206..82e30c7c9 100644
--- a/src/pkg/compress/bzip2/bzip2.go
+++ b/src/pkg/compress/bzip2/bzip2.go
@@ -22,14 +22,17 @@ func (s StructuralError) Error() string {
// A reader decompresses bzip2 compressed data.
type reader struct {
- br bitReader
- setupDone bool // true if we have parsed the bzip2 header.
- blockSize int // blockSize in bytes, i.e. 900 * 1024.
- eof bool
- buf []byte // stores Burrows-Wheeler transformed data.
- c [256]uint // the `C' array for the inverse BWT.
- tt []uint32 // mirrors the `tt' array in the bzip2 source and contains the P array in the upper 24 bits.
- tPos uint32 // Index of the next output byte in tt.
+ br bitReader
+ fileCRC uint32
+ blockCRC uint32
+ wantBlockCRC uint32
+ setupDone bool // true if we have parsed the bzip2 header.
+ blockSize int // blockSize in bytes, i.e. 900 * 1024.
+ eof bool
+ buf []byte // stores Burrows-Wheeler transformed data.
+ c [256]uint // the `C' array for the inverse BWT.
+ tt []uint32 // mirrors the `tt' array in the bzip2 source and contains the P array in the upper 24 bits.
+ tPos uint32 // Index of the next output byte in tt.
preRLE []uint32 // contains the RLE data still to be processed.
preRLEUsed int // number of entries of preRLE used.
@@ -50,12 +53,14 @@ const bzip2BlockMagic = 0x314159265359
const bzip2FinalMagic = 0x177245385090
// setup parses the bzip2 header.
-func (bz2 *reader) setup() error {
+func (bz2 *reader) setup(needMagic bool) error {
br := &bz2.br
- magic := br.ReadBits(16)
- if magic != bzip2FileMagic {
- return StructuralError("bad magic value")
+ if needMagic {
+ magic := br.ReadBits(16)
+ if magic != bzip2FileMagic {
+ return StructuralError("bad magic value")
+ }
}
t := br.ReadBits(8)
@@ -68,8 +73,11 @@ func (bz2 *reader) setup() error {
return StructuralError("invalid compression level")
}
+ bz2.fileCRC = 0
bz2.blockSize = 100 * 1024 * (int(level) - '0')
- bz2.tt = make([]uint32, bz2.blockSize)
+ if bz2.blockSize > len(bz2.tt) {
+ bz2.tt = make([]uint32, bz2.blockSize)
+ }
return nil
}
@@ -79,7 +87,7 @@ func (bz2 *reader) Read(buf []byte) (n int, err error) {
}
if !bz2.setupDone {
- err = bz2.setup()
+ err = bz2.setup(true)
brErr := bz2.br.Err()
if brErr != nil {
err = brErr
@@ -98,14 +106,14 @@ func (bz2 *reader) Read(buf []byte) (n int, err error) {
return
}
-func (bz2 *reader) read(buf []byte) (n int, err error) {
+func (bz2 *reader) readFromBlock(buf []byte) int {
// bzip2 is a block based compressor, except that it has a run-length
// preprocessing step. The block based nature means that we can
// preallocate fixed-size buffers and reuse them. However, the RLE
// preprocessing would require allocating huge buffers to store the
// maximum expansion. Thus we process blocks all at once, except for
// the RLE which we decompress as required.
-
+ n := 0
for (bz2.repeats > 0 || bz2.preRLEUsed < len(bz2.preRLE)) && n < len(buf) {
// We have RLE data pending.
@@ -148,34 +156,87 @@ func (bz2 *reader) read(buf []byte) (n int, err error) {
n++
}
- if n > 0 {
- return
- }
+ return n
+}
- // No RLE data is pending so we need to read a block.
+func (bz2 *reader) read(buf []byte) (int, error) {
+ for {
+ n := bz2.readFromBlock(buf)
+ if n > 0 {
+ bz2.blockCRC = updateCRC(bz2.blockCRC, buf[:n])
+ return n, nil
+ }
- br := &bz2.br
- magic := br.ReadBits64(48)
- if magic == bzip2FinalMagic {
- br.ReadBits64(32) // ignored CRC
- bz2.eof = true
- return 0, io.EOF
- } else if magic != bzip2BlockMagic {
- return 0, StructuralError("bad magic value found")
- }
+ // End of block. Check CRC.
+ if bz2.blockCRC != bz2.wantBlockCRC {
+ bz2.br.err = StructuralError("block checksum mismatch")
+ return 0, bz2.br.err
+ }
- err = bz2.readBlock()
- if err != nil {
- return 0, err
- }
+ // Find next block.
+ br := &bz2.br
+ switch br.ReadBits64(48) {
+ default:
+ return 0, StructuralError("bad magic value found")
+
+ case bzip2BlockMagic:
+ // Start of block.
+ err := bz2.readBlock()
+ if err != nil {
+ return 0, err
+ }
- return bz2.read(buf)
+ case bzip2FinalMagic:
+ // Check end-of-file CRC.
+ wantFileCRC := uint32(br.ReadBits64(32))
+ if br.err != nil {
+ return 0, br.err
+ }
+ if bz2.fileCRC != wantFileCRC {
+ br.err = StructuralError("file checksum mismatch")
+ return 0, br.err
+ }
+
+ // Skip ahead to byte boundary.
+ // Is there a file concatenated to this one?
+ // It would start with BZ.
+ if br.bits%8 != 0 {
+ br.ReadBits(br.bits % 8)
+ }
+ b, err := br.r.ReadByte()
+ if err == io.EOF {
+ br.err = io.EOF
+ bz2.eof = true
+ return 0, io.EOF
+ }
+ if err != nil {
+ br.err = err
+ return 0, err
+ }
+ z, err := br.r.ReadByte()
+ if err != nil {
+ if err == io.EOF {
+ err = io.ErrUnexpectedEOF
+ }
+ br.err = err
+ return 0, err
+ }
+ if b != 'B' || z != 'Z' {
+ return 0, StructuralError("bad magic value in continuation file")
+ }
+ if err := bz2.setup(false); err != nil {
+ return 0, err
+ }
+ }
+ }
}
// readBlock reads a bzip2 block. The magic number should already have been consumed.
func (bz2 *reader) readBlock() (err error) {
br := &bz2.br
- br.ReadBits64(32) // skip checksum. TODO: check it if we can figure out what it is.
+ bz2.wantBlockCRC = uint32(br.ReadBits64(32)) // skip checksum. TODO: check it if we can figure out what it is.
+ bz2.blockCRC = 0
+ bz2.fileCRC = (bz2.fileCRC<<1 | bz2.fileCRC>>31) ^ bz2.wantBlockCRC
randomized := br.ReadBits(1)
if randomized != 0 {
return StructuralError("deprecated randomized files")
@@ -316,6 +377,9 @@ func (bz2 *reader) readBlock() (err error) {
if repeat > 0 {
// We have decoded a complete run-length so we need to
// replicate the last output symbol.
+ if repeat > bz2.blockSize-bufIndex {
+ return StructuralError("repeats past end of block")
+ }
for i := 0; i < repeat; i++ {
b := byte(mtf.First())
bz2.tt[bufIndex] = uint32(b)
@@ -339,6 +403,9 @@ func (bz2 *reader) readBlock() (err error) {
// doesn't need to be encoded and we have |v-1| in the next
// line.
b := byte(mtf.Decode(int(v - 1)))
+ if bufIndex >= bz2.blockSize {
+ return StructuralError("data exceeds block size")
+ }
bz2.tt[bufIndex] = uint32(b)
bz2.c[b]++
bufIndex++
@@ -385,3 +452,33 @@ func inverseBWT(tt []uint32, origPtr uint, c []uint) uint32 {
return tt[origPtr] >> 8
}
+
+// This is a standard CRC32 like in hash/crc32 except that all the shifts are reversed,
+// causing the bits in the input to be processed in the reverse of the usual order.
+
+var crctab [256]uint32
+
+func init() {
+ const poly = 0x04C11DB7
+ for i := range crctab {
+ crc := uint32(i) << 24
+ for j := 0; j < 8; j++ {
+ if crc&0x80000000 != 0 {
+ crc = (crc << 1) ^ poly
+ } else {
+ crc <<= 1
+ }
+ }
+ crctab[i] = crc
+ }
+}
+
+// updateCRC updates the crc value to incorporate the data in b.
+// The initial value is 0.
+func updateCRC(val uint32, b []byte) uint32 {
+ crc := ^val
+ for _, v := range b {
+ crc = crctab[byte(crc>>24)^v] ^ (crc << 8)
+ }
+ return ^crc
+}
diff --git a/src/pkg/compress/bzip2/bzip2_test.go b/src/pkg/compress/bzip2/bzip2_test.go
index 7b227ac9f..ada1f9a00 100644
--- a/src/pkg/compress/bzip2/bzip2_test.go
+++ b/src/pkg/compress/bzip2/bzip2_test.go
@@ -6,6 +6,7 @@ package bzip2
import (
"bytes"
+ "encoding/base64"
"encoding/hex"
"io"
"io/ioutil"
@@ -62,6 +63,19 @@ func TestHelloWorldBZ2(t *testing.T) {
}
}
+func TestConcat(t *testing.T) {
+ out, err := decompressHex(helloWorldBZ2Hex + helloWorldBZ2Hex)
+ if err != nil {
+ t.Errorf("error from Read: %s", err)
+ return
+ }
+
+ hello2 := bytes.Repeat(helloWorld, 2)
+ if !bytes.Equal(hello2, out) {
+ t.Errorf("got %x, want %x", out, hello2)
+ }
+}
+
func testZeros(t *testing.T, inHex string, n int) {
out, err := decompressHex(inHex)
if err != nil {
@@ -155,3 +169,195 @@ const rand2Hex = "92d5652616ac444a4a04af1a8a3964aca0450d43d6cf233bd03233f4ba92f8
const rand3BZ2Hex = "425a68393141592653593be669d00000327ffffffffffffffffffffffffffffffffffff7ffffffffffffffffffffffffffffffc002b3b2b1b6e2bae400004c00132300004c0d268c004c08c0130026001a008683234c0684c34008c230261a04c0260064d07a8d00034000d27a1268c9931a8d327a3427a41faa69ea0da264c1a34219326869b51b49a6469a3268c689fa53269a62794687a9a68f5189994c9e487a8f534fd49a3d34043629e8c93d04da4f4648d30d4f44d3234c4d3023d0840680984d309934c234d3131a000640984f536a6132601300130130c8d00d04d1841ea7a8d31a02609b40023460010c01a34d4c1a0d04d3069306810034d0d0d4c0046130d034d0131a9a64d321804c68003400098344c13000991808c0001a00000000098004d3d4da4604c47a13012140aadf8d673c922c607ef6212a8c0403adea4b28aee578900e653b9cdeb8d11e6b838815f3ebaad5a01c5408d84a332170aff8734d4e06612d3c2889f31925fb89e33561f5100ae89b1f7047102e729373d3667e58d73aaa80fa7be368a1cc2dadd81d81ec8e1b504bd772ca31d03649269b01ceddaca07bf3d4eba24de141be3f86f93601e03714c0f64654671684f9f9528626fd4e1b76753dc0c54b842486b8d59d8ab314e86ca818e7a1f079463cbbd70d9b79b283c7edc419406311022e4be98c2c1374df9cdde2d008ce1d00e5f06ad1024baf555631f70831fc1023034e62be7c4bcb648caf276963ffa20e96bb50377fe1c113da0db4625b50741c35a058edb009c6ee5dbf93b8a6b060eec568180e8db791b82aab96cbf4326ca98361461379425ba8dcc347be670bdba7641883e5526ae3d833f6e9cb9bac9557747c79e206151072f7f0071dff3880411846f66bf4075c7462f302b53cb3400a74cf35652ad5641ed33572fd54e7ed7f85f58a0acba89327e7c6be5c58cb71528b99df2431f1d0358f8d28d81d95292da631fb06701decabb205fac59ff0fb1df536afc681eece6ea658c4d9eaa45f1342aa1ff70bdaff2ddaf25ec88c22f12829a0553db1ec2505554cb17d7b282e213a5a2aa30431ded2bce665bb199d023840832fedb2c0c350a27291407ff77440792872137df281592e82076a05c64c345ffb058c64f7f7c207ef78420b7010520610f17e302cc4dfcfaef72a0ed091aab4b541eb0531bbe941ca2f792bf7b31ca6162882b68054a8470115bc2c19f2df2023f7800432b39b04d3a304e8085ba3f1f0ca5b1ba4d38d339e6084de979cdea6d0e244c6c9fa0366bd890621e3d30846f5e8497e21597b8f29bbf52c961a485dfbea647600da0fc1f25ce4d203a8352ece310c39073525044e7ac46acf2ed9120bae1b4f6f02364abfe343f80b290983160c103557af1c68416480d024cc31b6c06cfec011456f1e95c420a12b48b1c3fe220c2879a982fb099948ac440db844b9a112a5188c7783fd3b19593290785f908d95c9db4b280bafe89c1313aeec24772046d9bc089645f0d182a21184e143823c5f52de50e5d7e98d3d7ab56f5413bbccd1415c9bcff707def475b643fb7f29842582104d4cc1dbaaca8f10a2f44273c339e0984f2b1e06ab2f0771db01fafa8142298345f3196f23e5847bda024034b6f59b11c29e981c881456e40d211929fd4f766200258aad8212016322bd5c605790dcfdf1bd2a93d99c9b8f498722d311d7eae7ff420496a31804c55f4759a7b13aaaf5f7ce006c3a8a998897d5e0a504398c2b627852545baf440798bcc5cc049357cf3f17d9771e4528a1af3d77dc794a11346e1bdf5efe37a405b127b4c43b616d61fbc5dc914e14240ef99a7400"
const rand3Hex = "1744b384d68c042371244e13500d4bfb98c6244e3d71a5b700224420b59c593553f33bd786e3d0ce31626f511bc985f59d1a88aa38ba8ad6218d306abee60dd9172540232b95be1af146c69e72e5fde667a090dc3f93bdc5c5af0ab80acdbaa7a505f628c59dc0247b31a439cacf5010a94376d71521df08c178b02fb96fdb1809144ea38c68536187c53201fea8631fb0a880b4451ccdca7cc61f6aafca21cc7449d920599db61789ac3b1e164b3390124f95022aeea39ccca3ec1053f4fa10de2978e2861ea58e477085c2220021a0927aa94c5d0006b5055abba340e4f9eba22e969978dfd18e278a8b89d877328ae34268bc0174cfe211954c0036f078025217d1269fac1932a03b05a0b616012271bbe1fb554171c7a59b196d8a4479f45a77931b5d97aaf6c0c673cbe597b79b96e2a0c1eae2e66e46ccc8c85798e23ffe972ebdaa3f6caea243c004e60321eb47cd79137d78fd0613be606feacc5b3637bdc96a89c13746db8cad886f3ccf912b2178c823bcac395f06d28080269bdca2debf3419c66c690fd1adcfbd53e32e79443d7a42511a84cb22ca94fffad9149275a075b2f8ae0b021dcde9bf62b102db920733b897560518b06e1ad7f4b03458493ddaa7f4fa2c1609f7a1735aeeb1b3e2cea3ab45fc376323cc91873b7e9c90d07c192e38d3f5dfc9bfab1fd821c854da9e607ea596c391c7ec4161c6c4493929a8176badaa5a5af7211c623f29643a937677d3df0da9266181b7c4da5dd40376db677fe8f4a1dc456adf6f33c1e37cec471dd318c2647644fe52f93707a77da7d1702380a80e14cc0fdce7bf2eed48a529090bae0388ee277ce6c7018c5fb00b88362554362205c641f0d0fab94fd5b8357b5ff08b207fee023709bc126ec90cfb17c006754638f8186aaeb1265e80be0c1189ec07d01d5f6f96cb9ce82744147d18490de7dc72862f42f024a16968891a356f5e7e0e695d8c933ba5b5e43ad4c4ade5399bc2cae9bb6189b7870d7f22956194d277f28b10e01c10c6ffe3e065f7e2d6d056aa790db5649ca84dc64c35566c0af1b68c32b5b7874aaa66467afa44f40e9a0846a07ae75360a641dd2acc69d93219b2891f190621511e62a27f5e4fbe641ece1fa234fc7e9a74f48d2a760d82160d9540f649256b169d1fed6fbefdc491126530f3cbad7913e19fbd7aa53b1e243fbf28d5f38c10ebd77c8b986775975cc1d619efb27cdcd733fa1ca36cffe9c0a33cc9f02463c91a886601fd349efee85ef1462065ef9bd2c8f533220ad93138b8382d5938103ab25b2d9af8ae106e1211eb9b18793fba033900c809c02cd6d17e2f3e6fc84dae873411f8e87c3f0a8f1765b7825d185ce3730f299c3028d4a62da9ee95c2b870fb70c79370d485f9d5d9acb78926d20444033d960524d2776dc31988ec7c0dbf23b9905d"
+
+const (
+ digits = iota
+ twain
+)
+
+var testfiles = []string{
+ // Digits is the digits of the irrational number e. Its decimal representation
+ // does not repeat, but there are only 10 posible digits, so it should be
+ // reasonably compressible.
+ digits: "testdata/e.txt.bz2",
+ // Twain is Project Gutenberg's edition of Mark Twain's classic English novel.
+ twain: "testdata/Mark.Twain-Tom.Sawyer.txt.bz2",
+}
+
+func benchmarkDecode(b *testing.B, testfile int) {
+ compressed, err := ioutil.ReadFile(testfiles[testfile])
+ if err != nil {
+ b.Fatal(err)
+ }
+ b.SetBytes(int64(len(compressed)))
+ for i := 0; i < b.N; i++ {
+ r := bytes.NewBuffer(compressed)
+ io.Copy(ioutil.Discard, NewReader(r))
+ }
+}
+
+func BenchmarkDecodeDigits(b *testing.B) { benchmarkDecode(b, digits) }
+func BenchmarkDecodeTwain(b *testing.B) { benchmarkDecode(b, twain) }
+
+func TestBufferOverrun(t *testing.T) {
+ // Tests https://code.google.com/p/go/issues/detail?id=5747.
+ buffer := bytes.NewBuffer([]byte(bufferOverrunBase64))
+ decoder := base64.NewDecoder(base64.StdEncoding, buffer)
+ decompressor := NewReader(decoder)
+ // This shouldn't panic.
+ ioutil.ReadAll(decompressor)
+}
+
+var bufferOverrunBase64 string = `
+QlpoNTFBWSZTWTzyiGcACMP/////////////////////////////////3/7f3///
+////4N/fCZODak2Xo44GIHZgkGzDRbFAuwAAKoFV7T6AO6qwA6APb6s2rOoAkAAD
+oACUoDtndh0iQAPkAAAAaPWihQoCgr5t97Obju21ChQB0NBm3RbA7apXrRoBooAA
+AhA+IAHWl2Us3O7t9yieb3udvd76+4+fd33nd3HO1bVvfcGRne6+3vfPvfc++995
+w7k973eJhasLVec970tzDNXdX28LoPXZ3H3K9z0s5ufWAfes49d5594c3dUYtI+2
++h1dvtpRa+uvrVEAG9bl893RVEN7cWvroSqWjPMGgAQi7Gq8TJSgKKdjKFBIB9Ae
+LqWxleu715eXe7ml9e5098Z6G1vr7t1QZ6ot76YzPd3j7333t2ql2Chm7XrA9ICQ
+VF77z3rVBWqkSXtlfb099hyezAr6USbGpICTSCFAaqHrKo+tUnm32rpE4Ue+t2mj
+bKUeipEqwc93EdhhTwmQpOhhesC9iqDSPNTWYNSnUtBdm1nsA0nqqNd7OWwDXtFL
+ONmmA6Ubke26I9UblvWIPR5VOWOnctai443URunnDy77uVC59OfRvezlDu33Z7Ly
+3NNuuHW63088xu3t3NHZhkZbG7tXRlj00qOtbaXTJUUdspTbABR9R6EUwQAEAAAA
+EMEwRpoAAAABMmhoAAjBNNAaCMhponpoGpgJpk9TEyp6niGKZkAaAEfqMQ09U80p
++pMGSCKngIAAAAgAAg0AAJhGgABGCEaaTyTKeNI1PE0wkj01GajMSNPSZGnqbU9T
+anlPUNAHqGQ0DQAMg9TamgAAYRU/IAAICAmjQJgjQBMEwp5DTSaaYmhTeqfplPID
+U1T9TynoU82pT1NPU/VP0j1NHqRpk9TTR7SnqaNNGmmQAaAD1Aeo0PSAAAAaaBiK
+eBAQBGgIABGQA0AmBNNBoaAgaJmpglPEyYap6npiTT0agGjJjUaaDTQAAAAAAM1A
+9QAaAAAADU8iEAQAEyAJk0NNNJgIZTJ5E00YSemiaZNGm1MpGNJ+lPU9qm9U2RDM
+oY0EzJB6h6nqDID1NMBDDRpo1AGNAjCMmhkMgaYSJIgAAAQyAAEyBoATECCNhTT0
+U/IZAmCM1DSTxkzUE8p6NDaGiZGJqntTFHvUyU9qPQp7Kn5GgKNPU9QAGg9QAAA3
+wz0Pk/g/m/m9P9H4vxv2+dH3gCS8nhbbbbbYxtgNsBsG0m2MbG0NNtsbYNsaY0wb
+bBibGmm22mxptNpsaGNDTY02JsG0MY0xg2MaYNNDbGwG0L5vsK/F9DO+EAA447Kq
+p7Wdf6Y+5c20T7DfHyMXIzRKrZexw72uiQI+y55vOe52xpqbCLC2uR20JdER7Zvr
+7ufuKb6zhiBxLuj0eA27v8RpMLucw9Ohwcizi2wrpt+yU1FdpM7ZYPcwS3XTef+A
+Wzjxwhdrgw3aH1LeC1eZW900x8V9Nv4hTPXp4l067P/4ANVZFF/imOe/d5bdueam
+/DFFokQWnFaU+ZqLBCM+d0PialJQWnLqRQZk/KhfbbYc2pCUTgffcSYbrCM1N+8l
+HU6gSz+h2GJXs+tbrNviL83M97X0vcTn/F82P8wen8/3/h3sHY+sf9CSej9ThYTV
+3lQ+FUHpfpGD4kv7dYMV995dpDX/y3xR8FoXx1bjUxBTNxuutwQ/h/Eedn9wpn6w
+E3+ND8YhN1HSriIxRE/6uFyMv6/oC6Elarw3aHMMqHJkGiiz6tejmvnYLQa+Qm6G
+deZ7jXTZV6NlpocgDnRdimS06bTYSkvPAL/xoWNLkX6N6VljU0dfKSBmm2uZE/xu
+sutQ1EdP7GdjhglIq4xlOFUFEQpmX+xx7R8y6c0GSAaqusOjNZwxZRudOvmXm1tZ
+T+YnbeB2ir9eiHNrtJNSLD/J/WDyuQpwBUtLKo0krccY/wIILP7f86teb9Z/9oyz
+OX05qEWbObfhpRw+9+rCvp/35ML8KX3aHaI0n+tudbFRsV5FLW+Oa8ruLN4peyVL
+DWjTHrXNthq/s7zAJYMeFJZkZt5mT9rfpH+5g3nc+piOSZ+J5nHtOnKI7Ff8Xl+j
+0t76XTNucCHQ6whav1OHdF53TY5wuv5OzvrdnxoId8fTyUvERr0ERINu/8XxZZ5f
+B5/kTZ8bBO0wv54Jp+ED/GQI8lZHzIQCP3vfQhwnCTj9TvITic7P4mYLDbH3fyzR
+i+6EajCcpXLWSGf+ZXkOrWspDWDhXtEKas0v3UqWksqgY1rTj45krX4KihN+daXs
+pZl5WPlta5p06CX6Xm2SfzqkMw12/3ix1bpnnZ+kFeBNX7A+E9zzG6OZaN78GOpl
+9Ht/eZn9PqWdav852zr0zqkDK2H5IjdvNah+b1YVGdQGzwR4Nw+f13yEKnV+y66W
+djfq7zWp7m5w+hzfv+Ly8O7oet5Vvd8/wQvO7qzOZ2vjf9X8Tj8PnMb/nc/nKqRR
++ml4UEhOOwfCeJEEI109CMYSh91iAJqPjMyH6KjrPD7W25llZVcREYNCTg6htbQt
+M38wYoquCWP6tdKYlVIv14xTNUeUf4El/FunCf6csZkmv+9tfWx7t59wuKIa3saU
+tZs9M+3HFOZtz3OLg/Unoaj9BYazYqA78xBU9tZzrtmF/rQL9CGJt90o/oYnSfcS
+SL3haaw351LXWQ1XOsv1SmH3v6ymuxEpPPnEDmBELaTYsvvMIWJsmPZFFww++Kd7
+s/Jo0JFeUU7uNtI+gVosAIpVVuWfI/9tOIycz7I5Z7zjV+NR2OuZbYtW5F08KX4o
+2k/xuJIchcNFPtxPfw9dkDgscRbMckyFMrzuZ3IvrcGzk0J6iI5ytrv37bGpAXMz
+WK9mMMPebepNevmLjjo/QWoM968Sjv7ldlPS5AinHcXwsFv6dmmh8lJt7UOJWoKu
+lMD1cB2ksIGpMdv8iuqR42Rn/kn+17BhhUZcwDBaUXVdX6bKW7fxlUYbq+mlqIcf
+a9v8HF87M9ANbi9bq9onf9TD7nQ6Xf6vZci8TBPX+/GI0He6j31fTVQYW+NsQxvO
+J8xrx+e58CCLQNjxeIyPt+F+qk/QMiXw+LyxGVkV/XcGQT9X03jSDP6beJ5QG1JW
+9Q3qLv/YixWI7gPV9Mrhf2oRYTc/9KLFRhkE3SjKOTKuSSBKQ24fI+hEznamH71D
+66Hwez8/0et7AtTv9zvamv2OD5He6fMV4k+ePl6+qPfO5CdHtK+eCDZL5+4f5yrl
+gTcRFiq8fXbc5IaI5fbbc1KMM/2T0Mr7+Hwaco6FtXm0fmhCgTZRqY4pKiEIfmaz
+QwHNOOCrtMJ2VwsyMumt7xsOolGnizRev6lILH43qPcczQM7Gc5zRin80YvFt1Qm
+h/57Z0auR2h0fuX50MBO4XQ+26y5l6v4j902R66c0j3z2KHstKQ04J/h6LbuNQE4
+D6cu/lyfK69DxxX8wb8XaQkMUcJdo1LzqUGDAb3Kfn/A3P/JYc99MO9qv67+SxWb
+wYTyqKdWTd+1KbR/Rcn0Io5zI/QquX7FA1bxfMytjQ/X+l0fh0Pf+Hx97meH4fQL
+7/T8/sdTm9Tn8nELvedyhydLlPPTScINdXyLIq9wgIJr4fWPbp9ZhFh/56fdSgOG
+HDXg+gkXsN2Rddr4HQ5P3u+RhLzmSjhzoqY5EsPC4QvRlX9JXjB84rPV5USR66qa
+/kjw4156GJnzoXtydKJE53t6PHfZWO+3ujsfI6iAdshc7OFzGXiZB9PtItKodhYq
+nABkTKdcpu4+TOpf9h5piX5slsaBjkeTnj/Ba02ilboQfcDVigxrYn/iTH5ySWUW
+/lHtg78s5UZM8sErwhNe3N3w+6ZOMnU+5i86/xFNtqZfDdXTGy1H3PzGbdtZXYT+
+Ixx2vpwBYzbPVYHxKosM5rPiVmcTllI9nuoSfeh9ib4foFWauOpvdmhBDqpTpKTX
+u8EO2l2Z195G2RIV7TlKSxGWjR5sl/nALu1uzBeLd9zpSujzMTd1uTX9Qk/Q1S+r
+vaW6bm8qqPO4jb6Wx6XIkm321nrIF6Ae25d1+Dpv/P5G4NoLd2j6/EtENC3FeR5z
+oo7bA+tI8yEQRhiF0z1FlJXLD5ZbhNNWQm/j/IbzRfh8JtOFZU7ruShLvHXysW9S
+9V909tr9jn8/E/Hb5N/1NVNHnZu2HIUvJvHJiHd2ucmeI9PWUMnppmE65GQ5E9xV
+ZRlGEH0X85EvmHyEupkMrCC0oMv9RCq+/H8gcfpe00Hs/S+regT5p58cyYomh93v
+qvuw/A06BE/wzJESuYbN9pqYpoXqXFemW1NksHEJ2w+PYMJ27WJyD5FpaXB85VaW
+qMOhDfO8E3QdH8ybyKt/UgI8/tDGpFbyOlaVdIv1FXJhoLp8soAA4Djg6/KZ066N
+ZFYuS8WdjpSZGP4/Lw+1yaXlzNznc/k2uHe2uXP3uFuPcHx+Dm44utxldoO1uBPy
++jzOs14+MIgOjOHMVNqAbMd8fUedLlhJMCfMtm4uz01enLNKcMrtLlPIR37Yukh1
+YEMXYpm7eU4XU+j+Jj3pDyaXtXs+p1fWfTN/cy9/Oxs4umUXQ4uHh1kObtayDJ56
+/QMxiHobjHNKuKfMxsrYEwN+QVIyVjAwMDYuMjQ1AAA9IwJniiBLRkZDAAAXt0Ja
+aDQxQVkmU1lZtwytAACLf///////////////////+//////v//////////bv78//
+/+AXO133uwO2xB2UxIvbKXrCqCoURUBL2ytFI82AFdcOwMhVTHtk5rD3szEVNYD4
+aIQINCaMRoTaSn7SbSMJiYmEwieTEp+psqbMCp+VNPaFNpqbBNR7UmanlPUeKfqm
+j1PU0/VPU08o9Q9EeKHlPJtKbYqeTCYhN6U9T1NH6mp+lPyoGNTI/Knkyg1MggAg
+CaMEyQnqZoaaRtRtJpppppoDaTR6hpphGh6mmgHpMQBpkGTTEAAaAAAA00AZDag0
+ADIBkGgABqemiRNTI0k8aU0PRGRoAZlP0UAAAGgAAAyAADQaAAAaAAAAAAAAAAAA
+AaAAAAM0kgRBJ5MlPFP1Gj0jTTTUaekxNAbUGjTQMgaZANNAAAAaAADTQAAAAAAA
+ANAA0AAANADQ0QAAAAAAAAAaGgAAAAAAABoA0AAA0AAAAAAAAAAAAANAAAAAkSEI
+aTRpomp5DUxNNDTJPTKaep6T09Kemmo2JG0aTQ9ENogaaGhkABo0NHqaBoDTI0DC
+Gj0gNAMhoDQ9QMQNAGQAaDDwyMPIMlbG1vhRBTFo6JksSupgpAjPbY0ec02IGXjb
+eS+FBsh01+O4ZOaD+srUZCFaT4DRjVDLx7uKIsFtESIDUg1ZkhyCSYov05C00MtR
+BdNNa/AYPGOQZWcs+VegXOPrkushFbZ3mBoRD6WamClkpBaHZrUhUl02bIfRXX4w
+b3/9cW9nHDVxh2qFBxqgRKfmq7/Jc/tdJk05nVrGbckGVy2PnIy30CDhpWmqrSot
+K2bOnX0NbP1iy2cd0Na0ZmbRstm4MzMzbbMySTd35F7f+zPP8DC+NJLYcakkkkRd
+NZlupJt3OMFoDAD2g+N3FAMCydhIpoRHRQAdFI5nNg4ugEXHCYxkMyGCwtaJmial
+y0IMlpSYYM/weXNJAhFqS0GNmvaPEtYGjbvaucMdklOTmBX1vfVAkTYB1uXCSK64
+UNIixOqRKLuRCFtqIQtgwqaFrCkIYbbewErWABa+VGADWsJXJjfx5SJViLuwiGXq
+Ru6vCuwmU5CJiJz3UiBpmLv0r2wskxUhY4tzPVGQ9RMXJl65eLSNwZVwaSyGZ9Cm
+A3jztQUUpFeUryBTskW95iVwRMFrhBCwZBAFJBZvhMEMNoDJJlUoIhQkAkjbExp2
+YZio+ZYeAZUwmH1qUbdQixmxf0+61+aVgJ1hwxsO1yG3hFx4pfjc09ITVht0pG8u
+FtVFhPa1KE0gTRUSVXywkITucqk0Waz5Fs6qJpVHYdNrbYRFxnFsQGY1qmsTLjK6
+4QX5Rddo6krM/Bx9CqIAKq4CzVQYHrmIAd2EBhYmwVYwLvhzKIUrc2EirnGIvyuD
+O4YZDSwsVTA0BpVvUOjDErkCraBoSutcKwUSSLGhVvNYHLz3klgZD++wWsa/swLw
+gvNDY2De+sncOv8X2lq4HD95ZdwPuTIMXCwSbg4RrIqv+L0y6F17pqDecyQYPEj3
+iN/0BBeWZlJAyBMi5U3Q1zAlsK8IlDhaXGmvZrgISq5CfNjmUgxDeMggOKqxu4sI
+OrilS49Lkl1J3u3GjXTuH+rX+4ccyFAQnizCpPClcY77F59j63S6fr5vr+y99tuO
+7Ox7Wg/ljwhdyaK4xMmXczeJbx7x07htJNtC4xcQfAtvzeznLrN6MN/ILIBOI65I
+qIA2D5fHHj1XN4aN6TvOjWDaSbSWqxCSCvXUpzkNJAkWXAuTwF8k5uSJvQj/rVo0
+hAhEMEIYkCRGx9AX+byIuXWlLMbbVeliHNUL5AQYmNwLFu4SkmGD+UWtBMyVHQOQ
+ss0ggoVKSKOBUgnVS6ljt7WE1qXqJJ4QA1pEwYNLEaguEE1LtPNoVr5WzjbSbWPk
+V9OW3y9IneUDLoIV5pAkEFTEFGFVjeTFxtpzBBfGgycBxVCdz8eESBIzsamRchAa
+TQunQH8DHnpfod9QuAuRvc7JBlKUCYmCjMvynLcxIFohxCaYrDvGw4QbXZB7oWQ7
+hpoGlz23ayDfB8NrRRzdilsEQyQniu9ASLQg7RrGZnoTr1ai12IbCEUCGdFq03P5
+nBnRFAGmisQGcyykV9gKtcVMWLhCuVmXg86dndn7slUpRNSSEAU20oaWIm1maFTu
+E0DT4gTbg0nuhjtz3kNOz+i7sBm0bkXjxQWuLqlZEmp60ZTyRZJDUqKSEKg6hqcy
+ERxdU22CSNOO10RYUUiDVpKhPNdKTOIE1thp02sBNoNTFSht8WJtaBQ09qN3jd5r
+dOLX4IA5fevRyCCzDgRXfV4wzik4KROjmxmTMglBySlIMEzcXehnDXCRiZSlvwA2
+0YsIOROcm4UrIRFxJHctJH7OdN5u1aHVHb5UaLHpv48NgmFRE56KTSoaWunqm2st
+S0mrAdOiqcR12PWVbdVRJKcQ0DQuhwlAPcRtpxN3D4kbXJjToSYJIFw406G2CSaK
+jQMIJPZGlQmgyFhoCSzeGS1VSq5SKKQQxs5RqKUcVUNY57YUETb4mXzV84SPngKi
+nsce0mXByZq5BKUA9puHZWLNwQIYuDaJUNgG+E01E3pDYVNLKYQ0hsVesgV5gZY0
+htDsRdGtm0+iGnkN6+Ea9YJtUZNAkx2GgSoix12nTW0avTUfxR3oYcpvZ7IdtABE
+UhBcjG4qZtDZsS1JQHys243vhLaDTSvvTeBiJA2tmokqECTBcSOCAGkAxMKlVAva
+4IsLRaBBqhxDbcGtgdw03mFcLUaFuhtKuuEIEkUleJQwby/zwu9uvvZK4xTV+ECM
+a8lmzxKmqkBggYK1+xPdbmJclm6tSZhE/OSJtCEjs+unJIQkT9hCWgBJqGMS07Eh
+AJNmBiuVEVdTyjkIJkavuZmx2sJF13htgEZUCC23lZFOE6gWbM9WyYNJTM8yCQrb
+0Sx3OQvBML5cRATAQkSQkAJOAhoxpQkNi4ZiEVDbdtJAME0RXNDXGHA3M3Q0mm1o
+IEwbWpaM1DQCSMbGRCAu3iRIQiT6RlBpT1n3tfwvUXz3gIVlx3mEximY/kZW1kNG
+sgEJIrBisaEoGYPJ+1CQUYFBw+eGEHJQBpNHjErXUJY2iWHQ30hXwFBuMSxQ2lB5
+bg+/LX3euG6HsHUB1lFvBvaiaBrITVwkCTa1d0s9CHZCiDZjbWReKyrpPE2oSa7o
+LPrR4BJvys9ttjUpzETSSMxh8vsr9dXTwKBtK+1xCTGDQmNIaE29HmHdS5GSxpya
+MismcAUSEgSxHBrKtgsZzduG7vHZn16l3kFkVITtENIzS2JsiBwFTDlhgexsjBHv
+5HXOYxHBzoSDCcPZ0ctvkY9aS5XpoQuFYkGJgCsqjJZeUMNUEpDSbKcnUc1PifIA
+CbR2UoXawBlspkEBr9HBfvUi/MUakZVOf1WKYrqSaIXce62JOyhJLq3qJBloTA0F
+VbILEtM+heFmNRCFt70GJrExVJri0ArYbCRbADSGDBpBXxxb/6fo+s3C7uaL7RjM
+LV2IQBNrAJrKFeJwTsPnxbAsemirUx2lk1kaxschzdK4TQNJN5wQnolIFg401OZ4
+2na11LnT3lR+1k1TMJhiAjXMk0F1ooHnYlt9LKfJ3ZIOmeY+2l9bUQHWFNGyEyfj
+EAcu3kpGLq0Ez7XOS+EpAASRQTAYMATfVQibHLTT30zG732+pNe9za1JNt8sNJYn
+RjWuJ6jL5ILV0rcd9vT7X9fObvcXitpvJ2XBJE+PhX2HaTkyWeF9pwnlQNrTe9hV
+tzhA+ihZrDrHNmLcQjZbnv/IMubqq8egxY80t5n6vZ6U5TR6U9uZJvai1xtqAyCR
+NWkW52m00rDTEuO6BA4q2RHDWwbETF55rRsWLIgNW9qJCyMHPbTM/dMBmWMQSMxz
+4M2pRzt47SICxA327UqSCEERqMFybmYi3nUxePtLgHYplqRiw4ynMbXd/kiQ0LE0
+PKJSSCXA42ymziCpAxNWflzpzQdJZusahRFr6t6m+4p273/Taj7k+hZyNgBAgXAY
+8F7pTts6orLb8IA6o4TOwkwQYmKvKu9VwMrE7+GUhVIAgY9a8DyQMiDBkEAwh7S1
+KgCBfao8DK1CwSS8Z3WjL5MEgt93z2koUQCD/YxMBppiCMp7SDVSmkkIHptfGpeh
+t+M13Ccv1tavIASFiaQl6rBz3K4N3DSGwNkCibrvEAC0fQirOWnc4NVbcLKpFG1l
+NQXF/eqdT79wq1Mvlap3QSCLhcD2D3fCkKVWid4aSjtp9FOX1Uaf7P9eT93zd9Sv
+mj2yNLRUGzyI/0oONNSzmmkvJ5Cq2X2CdldIWMGZO57RJ8oyATAWTQmRmNkfh0Sx
+uuR/J9oUsomVy1AEntc0dlPivkqBkBqrxU3j5PnWkaI3ZRGc0gg9spCQEISh4xEU
+pMhVrnmDQLfLP8Ouqpx917MAw7hkjQk6BJFTAbXDsz3LSHIxo/gB8qrA1vbvdZZh
+LtR0frJdfdppX8nAQX/TAxOQ8+H6yw8a9i7/zJEfSYIhop59N/fhcWW2F14cj2Xc
+fyHaZ04lTO4uPnly91jwuFPaREuZVp8AxImIhlkxkAN61tWdWG7tEbaCgszh6VIz
+ThFnHo2Vi8SQXPrXCN7J9Tc9ZYiAYqoThV/u6SYsea5aZL8deOvKBQCgZZuIxX1z
+4EnfcqG176vY4VqMBIC4pMJz0WcHJYqN+j7BiwGoMBwExrIdTB7q4XIFLotcIpS0
+1MqyVsesvoQq7WObmGQXdMliMirSLcDuSx8Qy+4pIBgGDIyMp1qbonnGdcHYvU8S
+O0A8s/iua5oFdNZTWvbVI4FUH9sKcLiB3/fIAF+sB4n8q6L+UCfmbPcAo/crQ6b3
+HqhDBMY9J0q/jdz9GNYZ/1fbXdkUqAQKFePhtzJDRBZba27+LPQNMCcrHMq06F1T
+4QmLmkHt7LxB2pAczUO+T2O9bHEw/HWw+dYf2MoRDUw=
+`
diff --git a/src/pkg/compress/bzip2/huffman.go b/src/pkg/compress/bzip2/huffman.go
index f755019bb..8f6b0c9ca 100644
--- a/src/pkg/compress/bzip2/huffman.go
+++ b/src/pkg/compress/bzip2/huffman.go
@@ -33,14 +33,17 @@ const invalidNodeValue = 0xffff
// Decode reads bits from the given bitReader and navigates the tree until a
// symbol is found.
-func (t huffmanTree) Decode(br *bitReader) (v uint16) {
+func (t *huffmanTree) Decode(br *bitReader) (v uint16) {
nodeIndex := uint16(0) // node 0 is the root of the tree.
for {
node := &t.nodes[nodeIndex]
- bit := br.ReadBit()
+ bit, ok := br.TryReadBit()
+ if !ok && br.ReadBit() {
+ bit = 1
+ }
// bzip2 encodes left as a true bit.
- if bit {
+ if bit != 0 {
// left
if node.left == invalidNodeValue {
return node.leftValue
diff --git a/src/pkg/compress/bzip2/move_to_front.go b/src/pkg/compress/bzip2/move_to_front.go
index 0ed19dec3..b7e75a700 100644
--- a/src/pkg/compress/bzip2/move_to_front.go
+++ b/src/pkg/compress/bzip2/move_to_front.go
@@ -15,10 +15,11 @@ type moveToFrontDecoder struct {
// Rather than actually keep the list in memory, the symbols are stored
// as a circular, double linked list with the symbol indexed by head
// at the front of the list.
- symbols []byte
- next []uint8
- prev []uint8
+ symbols [256]byte
+ next [256]uint8
+ prev [256]uint8
head uint8
+ len int
}
// newMTFDecoder creates a move-to-front decoder with an explicit initial list
@@ -28,12 +29,9 @@ func newMTFDecoder(symbols []byte) *moveToFrontDecoder {
panic("too many symbols")
}
- m := &moveToFrontDecoder{
- symbols: symbols,
- next: make([]uint8, len(symbols)),
- prev: make([]uint8, len(symbols)),
- }
-
+ m := new(moveToFrontDecoder)
+ copy(m.symbols[:], symbols)
+ m.len = len(symbols)
m.threadLinkedList()
return m
}
@@ -45,34 +43,29 @@ func newMTFDecoderWithRange(n int) *moveToFrontDecoder {
panic("newMTFDecoderWithRange: cannot have > 256 symbols")
}
- m := &moveToFrontDecoder{
- symbols: make([]uint8, n),
- next: make([]uint8, n),
- prev: make([]uint8, n),
- }
-
+ m := new(moveToFrontDecoder)
for i := 0; i < n; i++ {
- m.symbols[i] = byte(i)
+ m.symbols[byte(i)] = byte(i)
}
-
+ m.len = n
m.threadLinkedList()
return m
}
// threadLinkedList creates the initial linked-list pointers.
func (m *moveToFrontDecoder) threadLinkedList() {
- if len(m.symbols) == 0 {
+ if m.len == 0 {
return
}
- m.prev[0] = uint8(len(m.symbols) - 1)
+ m.prev[0] = uint8(m.len - 1)
- for i := 0; i < len(m.symbols)-1; i++ {
+ for i := byte(0); int(i) < m.len-1; i++ {
m.next[i] = uint8(i + 1)
m.prev[i+1] = uint8(i)
}
- m.next[len(m.symbols)-1] = 0
+ m.next[m.len-1] = 0
}
func (m *moveToFrontDecoder) Decode(n int) (b byte) {
diff --git a/src/pkg/compress/bzip2/testdata/Mark.Twain-Tom.Sawyer.txt.bz2 b/src/pkg/compress/bzip2/testdata/Mark.Twain-Tom.Sawyer.txt.bz2
new file mode 100644
index 000000000..0bd61a6d4
--- /dev/null
+++ b/src/pkg/compress/bzip2/testdata/Mark.Twain-Tom.Sawyer.txt.bz2
Binary files differ
diff --git a/src/pkg/compress/bzip2/testdata/e.txt.bz2 b/src/pkg/compress/bzip2/testdata/e.txt.bz2
new file mode 100644
index 000000000..65bf3b4c3
--- /dev/null
+++ b/src/pkg/compress/bzip2/testdata/e.txt.bz2
Binary files differ
diff --git a/src/pkg/compress/flate/copy.go b/src/pkg/compress/flate/copy.go
index 06e5d2e66..a3200a8f4 100644
--- a/src/pkg/compress/flate/copy.go
+++ b/src/pkg/compress/flate/copy.go
@@ -6,12 +6,27 @@ package flate
// forwardCopy is like the built-in copy function except that it always goes
// forward from the start, even if the dst and src overlap.
-func forwardCopy(dst, src []byte) int {
- if len(src) > len(dst) {
- src = src[:len(dst)]
+// It is equivalent to:
+// for i := 0; i < n; i++ {
+// mem[dst+i] = mem[src+i]
+// }
+func forwardCopy(mem []byte, dst, src, n int) {
+ if dst <= src {
+ copy(mem[dst:dst+n], mem[src:src+n])
+ return
}
- for i, x := range src {
- dst[i] = x
+ for {
+ if dst >= src+n {
+ copy(mem[dst:dst+n], mem[src:src+n])
+ return
+ }
+ // There is some forward overlap. The destination
+ // will be filled with a repeated pattern of mem[src:src+k].
+ // We copy one instance of the pattern here, then repeat.
+ // Each time around this loop k will double.
+ k := dst - src
+ copy(mem[dst:dst+k], mem[src:src+k])
+ n -= k
+ dst += k
}
- return len(src)
}
diff --git a/src/pkg/compress/flate/copy_test.go b/src/pkg/compress/flate/copy_test.go
index a9281d446..2011b1547 100644
--- a/src/pkg/compress/flate/copy_test.go
+++ b/src/pkg/compress/flate/copy_test.go
@@ -30,10 +30,12 @@ func TestForwardCopy(t *testing.T) {
}
for _, tc := range testCases {
b := []byte("0123456789")
- dst := b[tc.dst0:tc.dst1]
- src := b[tc.src0:tc.src1]
- n := forwardCopy(dst, src)
- got := string(dst[:n])
+ n := tc.dst1 - tc.dst0
+ if tc.src1-tc.src0 < n {
+ n = tc.src1 - tc.src0
+ }
+ forwardCopy(b, tc.dst0, tc.src0, n)
+ got := string(b[tc.dst0 : tc.dst0+n])
if got != tc.want {
t.Errorf("dst=b[%d:%d], src=b[%d:%d]: got %q, want %q",
tc.dst0, tc.dst1, tc.src0, tc.src1, got, tc.want)
diff --git a/src/pkg/compress/flate/deflate.go b/src/pkg/compress/flate/deflate.go
index d357fe361..8c79df0c6 100644
--- a/src/pkg/compress/flate/deflate.go
+++ b/src/pkg/compress/flate/deflate.go
@@ -416,6 +416,50 @@ func (d *compressor) init(w io.Writer, level int) (err error) {
return nil
}
+var zeroes [32]int
+var bzeroes [256]byte
+
+func (d *compressor) reset(w io.Writer) {
+ d.w.reset(w)
+ d.sync = false
+ d.err = nil
+ switch d.compressionLevel.chain {
+ case 0:
+ // level was NoCompression.
+ for i := range d.window {
+ d.window[i] = 0
+ }
+ d.windowEnd = 0
+ default:
+ d.chainHead = -1
+ for s := d.hashHead; len(s) > 0; {
+ n := copy(s, zeroes[:])
+ s = s[n:]
+ }
+ for s := d.hashPrev; len(s) > 0; s = s[len(zeroes):] {
+ copy(s, zeroes[:])
+ }
+ d.hashOffset = 1
+
+ d.index, d.windowEnd = 0, 0
+ for s := d.window; len(s) > 0; {
+ n := copy(s, bzeroes[:])
+ s = s[n:]
+ }
+ d.blockStart, d.byteAvailable = 0, false
+
+ d.tokens = d.tokens[:maxFlateBlockTokens+1]
+ for i := 0; i <= maxFlateBlockTokens; i++ {
+ d.tokens[i] = 0
+ }
+ d.tokens = d.tokens[:0]
+ d.length = minMatchLength - 1
+ d.offset = 0
+ d.hash = 0
+ d.maxInsertIndex = 0
+ }
+}
+
func (d *compressor) close() error {
d.sync = true
d.step(d)
@@ -439,7 +483,6 @@ func (d *compressor) close() error {
// If level is in the range [-1, 9] then the error returned will be nil.
// Otherwise the error returned will be non-nil.
func NewWriter(w io.Writer, level int) (*Writer, error) {
- const logWindowSize = logMaxOffsetSize
var dw Writer
if err := dw.d.init(w, level); err != nil {
return nil, err
@@ -462,6 +505,7 @@ func NewWriterDict(w io.Writer, level int, dict []byte) (*Writer, error) {
zw.Write(dict)
zw.Flush()
dw.enabled = true
+ zw.dict = append(zw.dict, dict...) // duplicate dictionary for Reset method.
return zw, err
}
@@ -480,7 +524,8 @@ func (w *dictWriter) Write(b []byte) (n int, err error) {
// A Writer takes data written to it and writes the compressed
// form of that data to an underlying writer (see NewWriter).
type Writer struct {
- d compressor
+ d compressor
+ dict []byte
}
// Write writes data to w, which will eventually write the
@@ -506,3 +551,21 @@ func (w *Writer) Flush() error {
func (w *Writer) Close() error {
return w.d.close()
}
+
+// Reset discards the writer's state and makes it equivalent to
+// the result of NewWriter or NewWriterDict called with dst
+// and w's level and dictionary.
+func (w *Writer) Reset(dst io.Writer) {
+ if dw, ok := w.d.w.w.(*dictWriter); ok {
+ // w was created with NewWriterDict
+ dw.w = dst
+ w.d.reset(dw)
+ dw.enabled = false
+ w.Write(w.dict)
+ w.Flush()
+ dw.enabled = true
+ } else {
+ // w was created with NewWriter
+ w.d.reset(dst)
+ }
+}
diff --git a/src/pkg/compress/flate/deflate_test.go b/src/pkg/compress/flate/deflate_test.go
index 8c4a6d6b3..730234c38 100644
--- a/src/pkg/compress/flate/deflate_test.go
+++ b/src/pkg/compress/flate/deflate_test.go
@@ -9,6 +9,7 @@ import (
"fmt"
"io"
"io/ioutil"
+ "reflect"
"sync"
"testing"
)
@@ -424,3 +425,66 @@ func TestRegression2508(t *testing.T) {
}
w.Close()
}
+
+func TestWriterReset(t *testing.T) {
+ for level := 0; level <= 9; level++ {
+ if testing.Short() && level > 1 {
+ break
+ }
+ w, err := NewWriter(ioutil.Discard, level)
+ if err != nil {
+ t.Fatalf("NewWriter: %v", err)
+ }
+ buf := []byte("hello world")
+ for i := 0; i < 1024; i++ {
+ w.Write(buf)
+ }
+ w.Reset(ioutil.Discard)
+
+ wref, err := NewWriter(ioutil.Discard, level)
+ if err != nil {
+ t.Fatalf("NewWriter: %v", err)
+ }
+
+ // DeepEqual doesn't compare functions.
+ w.d.fill, wref.d.fill = nil, nil
+ w.d.step, wref.d.step = nil, nil
+ if !reflect.DeepEqual(w, wref) {
+ t.Errorf("level %d Writer not reset after Reset", level)
+ }
+ }
+ testResetOutput(t, func(w io.Writer) (*Writer, error) { return NewWriter(w, NoCompression) })
+ testResetOutput(t, func(w io.Writer) (*Writer, error) { return NewWriter(w, DefaultCompression) })
+ testResetOutput(t, func(w io.Writer) (*Writer, error) { return NewWriter(w, BestCompression) })
+ dict := []byte("we are the world")
+ testResetOutput(t, func(w io.Writer) (*Writer, error) { return NewWriterDict(w, NoCompression, dict) })
+ testResetOutput(t, func(w io.Writer) (*Writer, error) { return NewWriterDict(w, DefaultCompression, dict) })
+ testResetOutput(t, func(w io.Writer) (*Writer, error) { return NewWriterDict(w, BestCompression, dict) })
+}
+
+func testResetOutput(t *testing.T, newWriter func(w io.Writer) (*Writer, error)) {
+ buf := new(bytes.Buffer)
+ w, err := newWriter(buf)
+ if err != nil {
+ t.Fatalf("NewWriter: %v", err)
+ }
+ b := []byte("hello world")
+ for i := 0; i < 1024; i++ {
+ w.Write(b)
+ }
+ w.Close()
+ out1 := buf.String()
+
+ buf2 := new(bytes.Buffer)
+ w.Reset(buf2)
+ for i := 0; i < 1024; i++ {
+ w.Write(b)
+ }
+ w.Close()
+ out2 := buf2.String()
+
+ if out1 != out2 {
+ t.Errorf("got %q, expected %q", out2, out1)
+ }
+ t.Logf("got %d bytes", len(out1))
+}
diff --git a/src/pkg/compress/flate/flate_test.go b/src/pkg/compress/flate/flate_test.go
index aba820a1f..57fea5ab4 100644
--- a/src/pkg/compress/flate/flate_test.go
+++ b/src/pkg/compress/flate/flate_test.go
@@ -24,3 +24,39 @@ func TestUncompressedSource(t *testing.T) {
t.Errorf("output[0] = %x, want 0x11", output[0])
}
}
+
+// The following test should not panic.
+func TestIssue5915(t *testing.T) {
+ bits := []int{4, 0, 0, 6, 4, 3, 2, 3, 3, 4, 4, 5, 0, 0, 0, 0, 5, 5, 6,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 8, 6, 0, 11, 0, 8, 0, 6, 6, 10, 8}
+ h := new(huffmanDecoder)
+ ok := h.init(bits)
+ if ok == true {
+ t.Fatalf("Given sequence of bits is bad, and should not succeed.")
+ }
+}
+
+// The following test should not panic.
+func TestIssue5962(t *testing.T) {
+ bits := []int{4, 0, 0, 6, 4, 3, 2, 3, 3, 4, 4, 5, 0, 0, 0, 0,
+ 5, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11}
+ h := new(huffmanDecoder)
+ ok := h.init(bits)
+ if ok == true {
+ t.Fatalf("Given sequence of bits is bad, and should not succeed.")
+ }
+}
+
+// The following test should not panic.
+func TestIssue6255(t *testing.T) {
+ bits1 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11}
+ bits2 := []int{11, 13}
+ h := new(huffmanDecoder)
+ if !h.init(bits1) {
+ t.Fatalf("Given sequence of bits is good and should succeed.")
+ }
+ if h.init(bits2) {
+ t.Fatalf("Given sequence of bits is bad and should not succeed.")
+ }
+}
diff --git a/src/pkg/compress/flate/huffman_bit_writer.go b/src/pkg/compress/flate/huffman_bit_writer.go
index 25e1da336..b182a710b 100644
--- a/src/pkg/compress/flate/huffman_bit_writer.go
+++ b/src/pkg/compress/flate/huffman_bit_writer.go
@@ -97,6 +97,31 @@ func newHuffmanBitWriter(w io.Writer) *huffmanBitWriter {
}
}
+func (w *huffmanBitWriter) reset(writer io.Writer) {
+ w.w = writer
+ w.bits, w.nbits, w.nbytes, w.err = 0, 0, 0, nil
+ w.bytes = [64]byte{}
+ for i := range w.codegen {
+ w.codegen[i] = 0
+ }
+ for _, s := range [...][]int32{w.literalFreq, w.offsetFreq, w.codegenFreq} {
+ for i := range s {
+ s[i] = 0
+ }
+ }
+ for _, enc := range [...]*huffmanEncoder{
+ w.literalEncoding,
+ w.offsetEncoding,
+ w.codegenEncoding} {
+ for i := range enc.code {
+ enc.code[i] = 0
+ }
+ for i := range enc.codeBits {
+ enc.codeBits[i] = 0
+ }
+ }
+}
+
func (w *huffmanBitWriter) flushBits() {
if w.err != nil {
w.nbits = 0
diff --git a/src/pkg/compress/flate/huffman_code.go b/src/pkg/compress/flate/huffman_code.go
index 009cce626..3b9fce466 100644
--- a/src/pkg/compress/flate/huffman_code.go
+++ b/src/pkg/compress/flate/huffman_code.go
@@ -19,23 +19,13 @@ type literalNode struct {
freq int32
}
-type chain struct {
- // The sum of the leaves in this tree
- freq int32
-
- // The number of literals to the left of this item at this level
- leafCount int32
-
- // The right child of this chain in the previous level.
- up *chain
-}
-
+// A levelInfo describes the state of the constructed tree for a given depth.
type levelInfo struct {
// Our level. for better printing
level int32
- // The most recent chain generated for this level
- lastChain *chain
+ // The frequency of the last node at this level
+ lastFreq int32
// The frequency of the next character to add to this level
nextCharFreq int32
@@ -47,12 +37,6 @@ type levelInfo struct {
// The number of chains remaining to generate for this level before moving
// up to the next level
needed int32
-
- // The levelInfo for level+1
- up *levelInfo
-
- // The levelInfo for level-1
- down *levelInfo
}
func maxNode() literalNode { return literalNode{math.MaxUint16, math.MaxInt32} }
@@ -121,6 +105,8 @@ func (h *huffmanEncoder) bitLength(freq []int32) int64 {
return total
}
+const maxBitsLimit = 16
+
// Return the number of literals assigned to each bit size in the Huffman encoding
//
// This method is only called when list.length >= 3
@@ -131,9 +117,13 @@ func (h *huffmanEncoder) bitLength(freq []int32) int64 {
// frequency, and has as its last element a special element with frequency
// MaxInt32
// maxBits The maximum number of bits that should be used to encode any literal.
+// Must be less than 16.
// return An integer array in which array[i] indicates the number of literals
// that should be encoded in i bits.
func (h *huffmanEncoder) bitCounts(list []literalNode, maxBits int32) []int32 {
+ if maxBits >= maxBitsLimit {
+ panic("flate: maxBits too large")
+ }
n := int32(len(list))
list = list[0 : n+1]
list[n] = maxNode()
@@ -148,53 +138,61 @@ func (h *huffmanEncoder) bitCounts(list []literalNode, maxBits int32) []int32 {
// A bogus "Level 0" whose sole purpose is so that
// level1.prev.needed==0. This makes level1.nextPairFreq
// be a legitimate value that never gets chosen.
- top := &levelInfo{needed: 0}
- chain2 := &chain{list[1].freq, 2, new(chain)}
+ var levels [maxBitsLimit]levelInfo
+ // leafCounts[i] counts the number of literals at the left
+ // of ancestors of the rightmost node at level i.
+ // leafCounts[i][j] is the number of literals at the left
+ // of the level j ancestor.
+ var leafCounts [maxBitsLimit][maxBitsLimit]int32
+
for level := int32(1); level <= maxBits; level++ {
// For every level, the first two items are the first two characters.
// We initialize the levels as if we had already figured this out.
- top = &levelInfo{
+ levels[level] = levelInfo{
level: level,
- lastChain: chain2,
+ lastFreq: list[1].freq,
nextCharFreq: list[2].freq,
nextPairFreq: list[0].freq + list[1].freq,
- down: top,
}
- top.down.up = top
+ leafCounts[level][level] = 2
if level == 1 {
- top.nextPairFreq = math.MaxInt32
+ levels[level].nextPairFreq = math.MaxInt32
}
}
// We need a total of 2*n - 2 items at top level and have already generated 2.
- top.needed = 2*n - 4
+ levels[maxBits].needed = 2*n - 4
- l := top
+ level := maxBits
for {
+ l := &levels[level]
if l.nextPairFreq == math.MaxInt32 && l.nextCharFreq == math.MaxInt32 {
// We've run out of both leafs and pairs.
// End all calculations for this level.
- // To m sure we never come back to this level or any lower level,
+ // To make sure we never come back to this level or any lower level,
// set nextPairFreq impossibly large.
- l.lastChain = nil
l.needed = 0
- l = l.up
- l.nextPairFreq = math.MaxInt32
+ levels[level+1].nextPairFreq = math.MaxInt32
+ level++
continue
}
- prevFreq := l.lastChain.freq
+ prevFreq := l.lastFreq
if l.nextCharFreq < l.nextPairFreq {
// The next item on this row is a leaf node.
- n := l.lastChain.leafCount + 1
- l.lastChain = &chain{l.nextCharFreq, n, l.lastChain.up}
+ n := leafCounts[level][level] + 1
+ l.lastFreq = l.nextCharFreq
+ // Lower leafCounts are the same of the previous node.
+ leafCounts[level][level] = n
l.nextCharFreq = list[n].freq
} else {
// The next item on this row is a pair from the previous row.
// nextPairFreq isn't valid until we generate two
// more values in the level below
- l.lastChain = &chain{l.nextPairFreq, l.lastChain.leafCount, l.down.lastChain}
- l.down.needed = 2
+ l.lastFreq = l.nextPairFreq
+ // Take leaf counts from the lower level, except counts[level] remains the same.
+ copy(leafCounts[level][:level], leafCounts[level-1][:level])
+ levels[l.level-1].needed = 2
}
if l.needed--; l.needed == 0 {
@@ -202,33 +200,33 @@ func (h *huffmanEncoder) bitCounts(list []literalNode, maxBits int32) []int32 {
// Continue calculating one level up. Fill in nextPairFreq
// of that level with the sum of the two nodes we've just calculated on
// this level.
- up := l.up
- if up == nil {
+ if l.level == maxBits {
// All done!
break
}
- up.nextPairFreq = prevFreq + l.lastChain.freq
- l = up
+ levels[l.level+1].nextPairFreq = prevFreq + l.lastFreq
+ level++
} else {
// If we stole from below, move down temporarily to replenish it.
- for l.down.needed > 0 {
- l = l.down
+ for levels[level-1].needed > 0 {
+ level--
}
}
}
// Somethings is wrong if at the end, the top level is null or hasn't used
// all of the leaves.
- if top.lastChain.leafCount != n {
- panic("top.lastChain.leafCount != n")
+ if leafCounts[maxBits][maxBits] != n {
+ panic("leafCounts[maxBits][maxBits] != n")
}
bitCount := make([]int32, maxBits+1)
bits := 1
- for chain := top.lastChain; chain.up != nil; chain = chain.up {
+ counts := &leafCounts[maxBits]
+ for level := maxBits; level > 0; level-- {
// chain.leafCount gives the number of literals requiring at least "bits"
// bits to encode.
- bitCount[bits] = chain.leafCount - chain.up.leafCount
+ bitCount[bits] = counts[level] - counts[level-1]
bits++
}
return bitCount
diff --git a/src/pkg/compress/flate/inflate.go b/src/pkg/compress/flate/inflate.go
index beca34b4d..3eb3b2b83 100644
--- a/src/pkg/compress/flate/inflate.go
+++ b/src/pkg/compress/flate/inflate.go
@@ -91,6 +91,10 @@ type huffmanDecoder struct {
// Initialize Huffman decoding tables from array of code lengths.
func (h *huffmanDecoder) init(bits []int) bool {
+ if h.min != 0 {
+ *h = huffmanDecoder{}
+ }
+
// Count number of codes of each length,
// compute min and max length.
var count [maxCodeLen]int
@@ -125,6 +129,9 @@ func (h *huffmanDecoder) init(bits []int) bool {
if i == huffmanChunkBits+1 {
// create link tables
link := code >> 1
+ if huffmanNumChunks < link {
+ return false
+ }
h.links = make([][]uint32, huffmanNumChunks-link)
for j := uint(link); j < huffmanNumChunks; j++ {
reverse := int(reverseByte[j>>8]) | int(reverseByte[j&0xff])<<8
@@ -154,7 +161,11 @@ func (h *huffmanDecoder) init(bits []int) bool {
h.chunks[off] = chunk
}
} else {
- linktab := h.links[h.chunks[reverse&(huffmanNumChunks-1)]>>huffmanValueShift]
+ value := h.chunks[reverse&(huffmanNumChunks-1)] >> huffmanValueShift
+ if value >= uint32(len(h.links)) {
+ return false
+ }
+ linktab := h.links[value]
reverse >>= huffmanChunkBits
for off := reverse; off < numLinks; off += 1 << uint(n-huffmanChunkBits) {
linktab[off] = chunk
@@ -511,7 +522,7 @@ func (f *decompressor) copyHist() bool {
if x := len(f.hist) - p; n > x {
n = x
}
- forwardCopy(f.hist[f.hp:f.hp+n], f.hist[p:p+n])
+ forwardCopy(f.hist[:], f.hp, p, n)
p += n
f.hp += n
f.copyLen -= n
@@ -633,6 +644,10 @@ func (f *decompressor) huffSym(h *huffmanDecoder) (int, error) {
if n > huffmanChunkBits {
chunk = h.links[chunk>>huffmanValueShift][(f.b>>huffmanChunkBits)&h.linkMask]
n = uint(chunk & huffmanCountMask)
+ if n == 0 {
+ f.err = CorruptInputError(f.roffset)
+ return 0, f.err
+ }
}
if n <= f.nb {
f.b >>= n
diff --git a/src/pkg/compress/flate/reader_test.go b/src/pkg/compress/flate/reader_test.go
index 54ed788db..2a8ebbc94 100644
--- a/src/pkg/compress/flate/reader_test.go
+++ b/src/pkg/compress/flate/reader_test.go
@@ -37,6 +37,7 @@ var testfiles = []string{
}
func benchmarkDecode(b *testing.B, testfile, level, n int) {
+ b.ReportAllocs()
b.StopTimer()
b.SetBytes(int64(n))
buf0, err := ioutil.ReadFile(testfiles[testfile])
@@ -55,7 +56,7 @@ func benchmarkDecode(b *testing.B, testfile, level, n int) {
if len(buf0) > n-i {
buf0 = buf0[:n-i]
}
- io.Copy(w, bytes.NewBuffer(buf0))
+ io.Copy(w, bytes.NewReader(buf0))
}
w.Close()
buf1 := compressed.Bytes()
@@ -63,7 +64,7 @@ func benchmarkDecode(b *testing.B, testfile, level, n int) {
runtime.GC()
b.StartTimer()
for i := 0; i < b.N; i++ {
- io.Copy(ioutil.Discard, NewReader(bytes.NewBuffer(buf1)))
+ io.Copy(ioutil.Discard, NewReader(bytes.NewReader(buf1)))
}
}
diff --git a/src/pkg/compress/gzip/gunzip_test.go b/src/pkg/compress/gzip/gunzip_test.go
index a1333580d..572fb5848 100644
--- a/src/pkg/compress/gzip/gunzip_test.go
+++ b/src/pkg/compress/gzip/gunzip_test.go
@@ -7,7 +7,10 @@ package gzip
import (
"bytes"
"io"
+ "io/ioutil"
+ "os"
"testing"
+ "time"
)
type gunzipTest struct {
@@ -302,3 +305,31 @@ func TestDecompressor(t *testing.T) {
}
}
}
+
+func TestIssue6550(t *testing.T) {
+ f, err := os.Open("testdata/issue6550.gz")
+ if err != nil {
+ t.Fatal(err)
+ }
+ gzip, err := NewReader(f)
+ if err != nil {
+ t.Fatalf("NewReader(testdata/issue6550.gz): %v", err)
+ }
+ defer gzip.Close()
+ done := make(chan bool, 1)
+ go func() {
+ _, err := io.Copy(ioutil.Discard, gzip)
+ if err == nil {
+ t.Errorf("Copy succeeded")
+ } else {
+ t.Logf("Copy failed (correctly): %v", err)
+ }
+ done <- true
+ }()
+ select {
+ case <-time.After(1 * time.Second):
+ t.Errorf("Copy hung")
+ case <-done:
+ // ok
+ }
+}
diff --git a/src/pkg/compress/gzip/gzip.go b/src/pkg/compress/gzip/gzip.go
index 45558b742..fe32d6871 100644
--- a/src/pkg/compress/gzip/gzip.go
+++ b/src/pkg/compress/gzip/gzip.go
@@ -26,14 +26,15 @@ const (
// to its wrapped io.Writer.
type Writer struct {
Header
- w io.Writer
- level int
- compressor *flate.Writer
- digest hash.Hash32
- size uint32
- closed bool
- buf [10]byte
- err error
+ w io.Writer
+ level int
+ wroteHeader bool
+ compressor *flate.Writer
+ digest hash.Hash32
+ size uint32
+ closed bool
+ buf [10]byte
+ err error
}
// NewWriter creates a new Writer that satisfies writes by compressing data
@@ -62,14 +63,39 @@ func NewWriterLevel(w io.Writer, level int) (*Writer, error) {
if level < DefaultCompression || level > BestCompression {
return nil, fmt.Errorf("gzip: invalid compression level: %d", level)
}
- return &Writer{
+ z := new(Writer)
+ z.init(w, level)
+ return z, nil
+}
+
+func (z *Writer) init(w io.Writer, level int) {
+ digest := z.digest
+ if digest != nil {
+ digest.Reset()
+ } else {
+ digest = crc32.NewIEEE()
+ }
+ compressor := z.compressor
+ if compressor != nil {
+ compressor.Reset(w)
+ }
+ *z = Writer{
Header: Header{
OS: 255, // unknown
},
- w: w,
- level: level,
- digest: crc32.NewIEEE(),
- }, nil
+ w: w,
+ level: level,
+ digest: digest,
+ compressor: compressor,
+ }
+}
+
+// Reset discards the Writer z's state and makes it equivalent to the
+// result of its original state from NewWriter or NewWriterLevel, but
+// writing to w instead. This permits reusing a Writer rather than
+// allocating a new one.
+func (z *Writer) Reset(w io.Writer) {
+ z.init(w, z.level)
}
// GZIP (RFC 1952) is little-endian, unlike ZLIB (RFC 1950).
@@ -138,7 +164,8 @@ func (z *Writer) Write(p []byte) (int, error) {
}
var n int
// Write the GZIP header lazily.
- if z.compressor == nil {
+ if !z.wroteHeader {
+ z.wroteHeader = true
z.buf[0] = gzipID1
z.buf[1] = gzipID2
z.buf[2] = gzipDeflate
@@ -183,7 +210,9 @@ func (z *Writer) Write(p []byte) (int, error) {
return n, z.err
}
}
- z.compressor, _ = flate.NewWriter(z.w, z.level)
+ if z.compressor == nil {
+ z.compressor, _ = flate.NewWriter(z.w, z.level)
+ }
}
z.size += uint32(len(p))
z.digest.Write(p)
@@ -206,8 +235,11 @@ func (z *Writer) Flush() error {
if z.closed {
return nil
}
- if z.compressor == nil {
+ if !z.wroteHeader {
z.Write(nil)
+ if z.err != nil {
+ return z.err
+ }
}
z.err = z.compressor.Flush()
return z.err
@@ -222,7 +254,7 @@ func (z *Writer) Close() error {
return nil
}
z.closed = true
- if z.compressor == nil {
+ if !z.wroteHeader {
z.Write(nil)
if z.err != nil {
return z.err
diff --git a/src/pkg/compress/gzip/gzip_test.go b/src/pkg/compress/gzip/gzip_test.go
index 4d1af9438..119be2e13 100644
--- a/src/pkg/compress/gzip/gzip_test.go
+++ b/src/pkg/compress/gzip/gzip_test.go
@@ -197,3 +197,35 @@ func TestWriterFlush(t *testing.T) {
t.Fatal("Flush didn't flush any data")
}
}
+
+// Multiple gzip files concatenated form a valid gzip file.
+func TestConcat(t *testing.T) {
+ var buf bytes.Buffer
+ w := NewWriter(&buf)
+ w.Write([]byte("hello "))
+ w.Close()
+ w = NewWriter(&buf)
+ w.Write([]byte("world\n"))
+ w.Close()
+
+ r, err := NewReader(&buf)
+ data, err := ioutil.ReadAll(r)
+ if string(data) != "hello world\n" || err != nil {
+ t.Fatalf("ReadAll = %q, %v, want %q, nil", data, err, "hello world")
+ }
+}
+
+func TestWriterReset(t *testing.T) {
+ buf := new(bytes.Buffer)
+ buf2 := new(bytes.Buffer)
+ z := NewWriter(buf)
+ msg := []byte("hello world")
+ z.Write(msg)
+ z.Close()
+ z.Reset(buf2)
+ z.Write(msg)
+ z.Close()
+ if buf.String() != buf2.String() {
+ t.Errorf("buf2 %q != original buf of %q", buf2.String(), buf.String())
+ }
+}
diff --git a/src/pkg/compress/gzip/testdata/issue6550.gz b/src/pkg/compress/gzip/testdata/issue6550.gz
new file mode 100644
index 000000000..57972b636
--- /dev/null
+++ b/src/pkg/compress/gzip/testdata/issue6550.gz
Binary files differ
diff --git a/src/pkg/compress/zlib/writer.go b/src/pkg/compress/zlib/writer.go
index cd8dea460..99ff6549a 100644
--- a/src/pkg/compress/zlib/writer.go
+++ b/src/pkg/compress/zlib/writer.go
@@ -70,6 +70,23 @@ func NewWriterLevelDict(w io.Writer, level int, dict []byte) (*Writer, error) {
}, nil
}
+// Reset clears the state of the Writer z such that it is equivalent to its
+// initial state from NewWriterLevel or NewWriterLevelDict, but instead writing
+// to w.
+func (z *Writer) Reset(w io.Writer) {
+ z.w = w
+ // z.level and z.dict left unchanged.
+ if z.compressor != nil {
+ z.compressor.Reset(w)
+ }
+ if z.digest != nil {
+ z.digest.Reset()
+ }
+ z.err = nil
+ z.scratch = [4]byte{}
+ z.wroteHeader = false
+}
+
// writeHeader writes the ZLIB header.
func (z *Writer) writeHeader() (err error) {
z.wroteHeader = true
@@ -111,11 +128,15 @@ func (z *Writer) writeHeader() (err error) {
return err
}
}
- z.compressor, err = flate.NewWriterDict(z.w, z.level, z.dict)
- if err != nil {
- return err
+ if z.compressor == nil {
+ // Initialize deflater unless the Writer is being reused
+ // after a Reset call.
+ z.compressor, err = flate.NewWriterDict(z.w, z.level, z.dict)
+ if err != nil {
+ return err
+ }
+ z.digest = adler32.New()
}
- z.digest = adler32.New()
return nil
}
diff --git a/src/pkg/compress/zlib/writer_test.go b/src/pkg/compress/zlib/writer_test.go
index aee1a5c2f..cf9c83254 100644
--- a/src/pkg/compress/zlib/writer_test.go
+++ b/src/pkg/compress/zlib/writer_test.go
@@ -89,6 +89,56 @@ func testLevelDict(t *testing.T, fn string, b0 []byte, level int, d string) {
}
}
+func testFileLevelDictReset(t *testing.T, fn string, level int, dict []byte) {
+ var b0 []byte
+ var err error
+ if fn != "" {
+ b0, err = ioutil.ReadFile(fn)
+ if err != nil {
+ t.Errorf("%s (level=%d): %v", fn, level, err)
+ return
+ }
+ }
+
+ // Compress once.
+ buf := new(bytes.Buffer)
+ var zlibw *Writer
+ if dict == nil {
+ zlibw, err = NewWriterLevel(buf, level)
+ } else {
+ zlibw, err = NewWriterLevelDict(buf, level, dict)
+ }
+ if err == nil {
+ _, err = zlibw.Write(b0)
+ }
+ if err == nil {
+ err = zlibw.Close()
+ }
+ if err != nil {
+ t.Errorf("%s (level=%d): %v", fn, level, err)
+ return
+ }
+ out := buf.String()
+
+ // Reset and comprses again.
+ buf2 := new(bytes.Buffer)
+ zlibw.Reset(buf2)
+ _, err = zlibw.Write(b0)
+ if err == nil {
+ err = zlibw.Close()
+ }
+ if err != nil {
+ t.Errorf("%s (level=%d): %v", fn, level, err)
+ return
+ }
+ out2 := buf2.String()
+
+ if out2 != out {
+ t.Errorf("%s (level=%d): different output after reset (got %d bytes, expected %d",
+ fn, level, len(out2), len(out))
+ }
+}
+
func TestWriter(t *testing.T) {
for i, s := range data {
b := []byte(s)
@@ -122,6 +172,21 @@ func TestWriterDict(t *testing.T) {
}
}
+func TestWriterReset(t *testing.T) {
+ const dictionary = "0123456789."
+ for _, fn := range filenames {
+ testFileLevelDictReset(t, fn, NoCompression, nil)
+ testFileLevelDictReset(t, fn, DefaultCompression, nil)
+ testFileLevelDictReset(t, fn, NoCompression, []byte(dictionary))
+ testFileLevelDictReset(t, fn, DefaultCompression, []byte(dictionary))
+ if !testing.Short() {
+ for level := BestSpeed; level <= BestCompression; level++ {
+ testFileLevelDictReset(t, fn, level, nil)
+ }
+ }
+ }
+}
+
func TestWriterDictIsUsed(t *testing.T) {
var input = []byte("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.")
var buf bytes.Buffer
diff --git a/src/pkg/container/heap/example_intheap_test.go b/src/pkg/container/heap/example_intheap_test.go
index e718cbc58..02d3d8668 100644
--- a/src/pkg/container/heap/example_intheap_test.go
+++ b/src/pkg/container/heap/example_intheap_test.go
@@ -31,13 +31,17 @@ func (h *IntHeap) Pop() interface{} {
return x
}
-// This example inserts several ints into an IntHeap and removes them in order of priority.
+// This example inserts several ints into an IntHeap, checks the minimum,
+// and removes them in order of priority.
func Example_intHeap() {
h := &IntHeap{2, 1, 5}
heap.Init(h)
heap.Push(h, 3)
+ fmt.Printf("minimum: %d\n", (*h)[0])
for h.Len() > 0 {
fmt.Printf("%d ", heap.Pop(h))
}
- // Output: 1 2 3 5
+ // Output:
+ // minimum: 1
+ // 1 2 3 5
}
diff --git a/src/pkg/container/heap/heap.go b/src/pkg/container/heap/heap.go
index c37e50e3c..52c8507b4 100644
--- a/src/pkg/container/heap/heap.go
+++ b/src/pkg/container/heap/heap.go
@@ -6,6 +6,8 @@
// heap.Interface. A heap is a tree with the property that each node is the
// minimum-valued node in its subtree.
//
+// The minimum element in the tree is the root, at index 0.
+//
// A heap is a common way to implement a priority queue. To build a priority
// queue, implement the Heap interface with the (negative) priority as the
// ordering for the Less method, so Push adds items while Pop removes the
@@ -54,7 +56,7 @@ func Push(h Interface, x interface{}) {
// Pop removes the minimum element (according to Less) from the heap
// and returns it. The complexity is O(log(n)) where n = h.Len().
-// Same as Remove(h, 0).
+// It is equivalent to Remove(h, 0).
//
func Pop(h Interface) interface{} {
n := h.Len() - 1
@@ -76,6 +78,15 @@ func Remove(h Interface, i int) interface{} {
return h.Pop()
}
+// Fix reestablishes the heap ordering after the element at index i has changed its value.
+// Changing the value of the element at index i and then calling Fix is equivalent to,
+// but less expensive than, calling Remove(h, i) followed by a Push of the new value.
+// The complexity is O(log(n)) where n = h.Len().
+func Fix(h Interface, i int) {
+ down(h, i, h.Len())
+ up(h, i)
+}
+
func up(h Interface, j int) {
for {
i := (j - 1) / 2 // parent
diff --git a/src/pkg/container/heap/heap_test.go b/src/pkg/container/heap/heap_test.go
index 274d587d8..b3d054c5f 100644
--- a/src/pkg/container/heap/heap_test.go
+++ b/src/pkg/container/heap/heap_test.go
@@ -5,6 +5,7 @@
package heap
import (
+ "math/rand"
"testing"
)
@@ -182,3 +183,31 @@ func BenchmarkDup(b *testing.B) {
}
}
}
+
+func TestFix(t *testing.T) {
+ h := new(myHeap)
+ h.verify(t, 0)
+
+ for i := 200; i > 0; i -= 10 {
+ Push(h, i)
+ }
+ h.verify(t, 0)
+
+ if (*h)[0] != 10 {
+ t.Fatalf("Expected head to be 10, was %d", (*h)[0])
+ }
+ (*h)[0] = 210
+ Fix(h, 0)
+ h.verify(t, 0)
+
+ for i := 100; i > 0; i-- {
+ elem := rand.Intn(h.Len())
+ if i&1 == 0 {
+ (*h)[elem] *= 2
+ } else {
+ (*h)[elem] /= 2
+ }
+ Fix(h, elem)
+ h.verify(t, 0)
+ }
+}
diff --git a/src/pkg/container/list/list.go b/src/pkg/container/list/list.go
index 562a5badb..ed2d15a45 100644
--- a/src/pkg/container/list/list.go
+++ b/src/pkg/container/list/list.go
@@ -29,7 +29,7 @@ type Element struct {
// Next returns the next list element or nil.
func (e *Element) Next() *Element {
- if p := e.next; p != &e.list.root {
+ if p := e.next; e.list != nil && p != &e.list.root {
return p
}
return nil
@@ -37,7 +37,7 @@ func (e *Element) Next() *Element {
// Prev returns the previous list element or nil.
func (e *Element) Prev() *Element {
- if p := e.prev; p != &e.list.root {
+ if p := e.prev; e.list != nil && p != &e.list.root {
return p
}
return nil
@@ -62,6 +62,7 @@ func (l *List) Init() *List {
func New() *List { return new(List).Init() }
// Len returns the number of elements of list l.
+// The complexity is O(1).
func (l *List) Len() int { return l.len }
// Front returns the first element of list l or nil
@@ -126,7 +127,7 @@ func (l *List) Remove(e *Element) interface{} {
return e.Value
}
-// Pushfront inserts a new element e with value v at the front of list l and returns e.
+// PushFront inserts a new element e with value v at the front of list l and returns e.
func (l *List) PushFront(v interface{}) *Element {
l.lazyInit()
return l.insertValue(v, &l.root)
@@ -178,6 +179,24 @@ func (l *List) MoveToBack(e *Element) {
l.insert(l.remove(e), l.root.prev)
}
+// MoveBefore moves element e to its new position before mark.
+// If e is not an element of l, or e == mark, the list is not modified.
+func (l *List) MoveBefore(e, mark *Element) {
+ if e.list != l || e == mark {
+ return
+ }
+ l.insert(l.remove(e), mark.prev)
+}
+
+// MoveAfter moves element e to its new position after mark.
+// If e is not an element of l, or e == mark, the list is not modified.
+func (l *List) MoveAfter(e, mark *Element) {
+ if e.list != l || e == mark {
+ return
+ }
+ l.insert(l.remove(e), mark)
+}
+
// PushBackList inserts a copy of an other list at the back of list l.
// The lists l and other may be the same.
func (l *List) PushBackList(other *List) {
diff --git a/src/pkg/container/list/list_test.go b/src/pkg/container/list/list_test.go
index b4fc77d14..ee52afe82 100644
--- a/src/pkg/container/list/list_test.go
+++ b/src/pkg/container/list/list_test.go
@@ -233,3 +233,55 @@ func TestIssue4103(t *testing.T) {
t.Errorf("l1.Len() = %d, want 3", n)
}
}
+
+func TestIssue6349(t *testing.T) {
+ l := New()
+ l.PushBack(1)
+ l.PushBack(2)
+
+ e := l.Front()
+ l.Remove(e)
+ if e.Value != 1 {
+ t.Errorf("e.value = %d, want 1", e.Value)
+ }
+ if e.Next() != nil {
+ t.Errorf("e.Next() != nil")
+ }
+ if e.Prev() != nil {
+ t.Errorf("e.Prev() != nil")
+ }
+}
+
+func TestMove(t *testing.T) {
+ l := New()
+ e1 := l.PushBack(1)
+ e2 := l.PushBack(2)
+ e3 := l.PushBack(3)
+ e4 := l.PushBack(4)
+
+ l.MoveAfter(e3, e3)
+ checkListPointers(t, l, []*Element{e1, e2, e3, e4})
+ l.MoveBefore(e2, e2)
+ checkListPointers(t, l, []*Element{e1, e2, e3, e4})
+
+ l.MoveAfter(e3, e2)
+ checkListPointers(t, l, []*Element{e1, e2, e3, e4})
+ l.MoveBefore(e2, e3)
+ checkListPointers(t, l, []*Element{e1, e2, e3, e4})
+
+ l.MoveBefore(e2, e4)
+ checkListPointers(t, l, []*Element{e1, e3, e2, e4})
+ e1, e2, e3, e4 = e1, e3, e2, e4
+
+ l.MoveBefore(e4, e1)
+ checkListPointers(t, l, []*Element{e4, e1, e2, e3})
+ e1, e2, e3, e4 = e4, e1, e2, e3
+
+ l.MoveAfter(e4, e1)
+ checkListPointers(t, l, []*Element{e1, e4, e2, e3})
+ e1, e2, e3, e4 = e1, e4, e2, e3
+
+ l.MoveAfter(e2, e3)
+ checkListPointers(t, l, []*Element{e1, e3, e2, e4})
+ e1, e2, e3, e4 = e1, e3, e2, e4
+}
diff --git a/src/pkg/crypto/aes/asm_amd64.s b/src/pkg/crypto/aes/asm_amd64.s
index 25decf978..5c22881e9 100644
--- a/src/pkg/crypto/aes/asm_amd64.s
+++ b/src/pkg/crypto/aes/asm_amd64.s
@@ -2,9 +2,11 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../../cmd/ld/textflag.h"
+
// func hasAsm() bool
// returns whether AES-NI is supported
-TEXT ·hasAsm(SB),7,$0
+TEXT ·hasAsm(SB),NOSPLIT,$0
XORQ AX, AX
INCL AX
CPUID
@@ -14,7 +16,7 @@ TEXT ·hasAsm(SB),7,$0
RET
// func encryptBlockAsm(nr int, xk *uint32, dst, src *byte)
-TEXT ·encryptBlockAsm(SB),7,$0
+TEXT ·encryptBlockAsm(SB),NOSPLIT,$0
MOVQ nr+0(FP), CX
MOVQ xk+8(FP), AX
MOVQ dst+16(FP), DX
@@ -63,7 +65,7 @@ Lenc128:
RET
// func decryptBlockAsm(nr int, xk *uint32, dst, src *byte)
-TEXT ·decryptBlockAsm(SB),7,$0
+TEXT ·decryptBlockAsm(SB),NOSPLIT,$0
MOVQ nr+0(FP), CX
MOVQ xk+8(FP), AX
MOVQ dst+16(FP), DX
@@ -113,7 +115,7 @@ Ldec128:
// func expandKeyAsm(nr int, key *byte, enc, dec *uint32) {
// Note that round keys are stored in uint128 format, not uint32
-TEXT ·expandKeyAsm(SB),7,$0
+TEXT ·expandKeyAsm(SB),NOSPLIT,$0
MOVQ nr+0(FP), CX
MOVQ key+8(FP), AX
MOVQ enc+16(FP), BX
@@ -217,7 +219,7 @@ Lexp_dec_loop:
#define PSHUFD_X0_X0_ BYTE $0x66; BYTE $0x0f; BYTE $0x70; BYTE $0xc0
#define PSHUFD_X1_X1_ BYTE $0x66; BYTE $0x0f; BYTE $0x70; BYTE $0xc9
-TEXT _expand_key_128<>(SB),7,$0
+TEXT _expand_key_128<>(SB),NOSPLIT,$0
PSHUFD $0xff, X1, X1
SHUFPS $0x10, X0, X4
PXOR X4, X0
@@ -230,7 +232,7 @@ TEXT _expand_key_128<>(SB),7,$0
#define PSLLDQ_X5_ BYTE $0x66; BYTE $0x0f; BYTE $0x73; BYTE $0xfd
#define PSHUFD_X0_X3_ BYTE $0x66; BYTE $0x0f; BYTE $0x70; BYTE $0xd8
-TEXT _expand_key_192a<>(SB),7,$0
+TEXT _expand_key_192a<>(SB),NOSPLIT,$0
PSHUFD $0x55, X1, X1
SHUFPS $0x10, X0, X4
PXOR X4, X0
@@ -253,7 +255,7 @@ TEXT _expand_key_192a<>(SB),7,$0
ADDQ $32, BX
RET
-TEXT _expand_key_192b<>(SB),7,$0
+TEXT _expand_key_192b<>(SB),NOSPLIT,$0
PSHUFD $0x55, X1, X1
SHUFPS $0x10, X0, X4
PXOR X4, X0
@@ -271,10 +273,10 @@ TEXT _expand_key_192b<>(SB),7,$0
ADDQ $16, BX
RET
-TEXT _expand_key_256a<>(SB),7,$0
+TEXT _expand_key_256a<>(SB),NOSPLIT,$0
JMP _expand_key_128<>(SB)
-TEXT _expand_key_256b<>(SB),7,$0
+TEXT _expand_key_256b<>(SB),NOSPLIT,$0
PSHUFD $0xaa, X1, X1
SHUFPS $0x10, X2, X4
PXOR X4, X2
diff --git a/src/pkg/crypto/cipher/cbc.go b/src/pkg/crypto/cipher/cbc.go
index 913a5643f..4189677e3 100644
--- a/src/pkg/crypto/cipher/cbc.go
+++ b/src/pkg/crypto/cipher/cbc.go
@@ -61,6 +61,13 @@ func (x *cbcEncrypter) CryptBlocks(dst, src []byte) {
}
}
+func (x *cbcEncrypter) SetIV(iv []byte) {
+ if len(iv) != len(x.iv) {
+ panic("cipher: incorrect length IV")
+ }
+ copy(x.iv, iv)
+}
+
type cbcDecrypter cbc
// NewCBCDecrypter returns a BlockMode which decrypts in cipher block chaining
@@ -94,3 +101,10 @@ func (x *cbcDecrypter) CryptBlocks(dst, src []byte) {
dst = dst[x.blockSize:]
}
}
+
+func (x *cbcDecrypter) SetIV(iv []byte) {
+ if len(iv) != len(x.iv) {
+ panic("cipher: incorrect length IV")
+ }
+ copy(x.iv, iv)
+}
diff --git a/src/pkg/crypto/cipher/gcm.go b/src/pkg/crypto/cipher/gcm.go
new file mode 100644
index 000000000..2bcb46985
--- /dev/null
+++ b/src/pkg/crypto/cipher/gcm.go
@@ -0,0 +1,350 @@
+// Copyright 2013 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 cipher
+
+import (
+ "crypto/subtle"
+ "errors"
+)
+
+// AEAD is a cipher mode providing authenticated encryption with associated
+// data.
+type AEAD interface {
+ // NonceSize returns the size of the nonce that must be passed to Seal
+ // and Open.
+ NonceSize() int
+
+ // Overhead returns the maximum difference between the lengths of a
+ // plaintext and ciphertext.
+ Overhead() int
+
+ // Seal encrypts and authenticates plaintext, authenticates the
+ // additional data and appends the result to dst, returning the updated
+ // slice. The nonce must be NonceSize() bytes long and unique for all
+ // time, for a given key.
+ //
+ // The plaintext and dst may alias exactly or not at all.
+ Seal(dst, nonce, plaintext, data []byte) []byte
+
+ // Open decrypts and authenticates ciphertext, authenticates the
+ // additional data and, if successful, appends the resulting plaintext
+ // to dst, returning the updated slice and true. On error, nil and
+ // false is returned. The nonce must be NonceSize() bytes long and both
+ // it and the additional data must match the value passed to Seal.
+ //
+ // The ciphertext and dst may alias exactly or not at all.
+ Open(dst, nonce, ciphertext, data []byte) ([]byte, error)
+}
+
+// gcmFieldElement represents a value in GF(2¹²⁸). In order to reflect the GCM
+// standard and make getUint64 suitable for marshaling these values, the bits
+// are stored backwards. For example:
+// the coefficient of x⁰ can be obtained by v.low >> 63.
+// the coefficient of x⁶³ can be obtained by v.low & 1.
+// the coefficient of x⁶⁴ can be obtained by v.high >> 63.
+// the coefficient of x¹²⁷ can be obtained by v.high & 1.
+type gcmFieldElement struct {
+ low, high uint64
+}
+
+// gcm represents a Galois Counter Mode with a specific key. See
+// http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-revised-spec.pdf
+type gcm struct {
+ cipher Block
+ // productTable contains the first sixteen powers of the key, H.
+ // However, they are in bit reversed order. See NewGCM.
+ productTable [16]gcmFieldElement
+}
+
+// NewGCM returns the given 128-bit, block cipher wrapped in Galois Counter Mode.
+func NewGCM(cipher Block) (AEAD, error) {
+ if cipher.BlockSize() != gcmBlockSize {
+ return nil, errors.New("cipher: NewGCM requires 128-bit block cipher")
+ }
+
+ var key [gcmBlockSize]byte
+ cipher.Encrypt(key[:], key[:])
+
+ g := &gcm{cipher: cipher}
+
+ // We precompute 16 multiples of |key|. However, when we do lookups
+ // into this table we'll be using bits from a field element and
+ // therefore the bits will be in the reverse order. So normally one
+ // would expect, say, 4*key to be in index 4 of the table but due to
+ // this bit ordering it will actually be in index 0010 (base 2) = 2.
+ x := gcmFieldElement{
+ getUint64(key[:8]),
+ getUint64(key[8:]),
+ }
+ g.productTable[reverseBits(1)] = x
+
+ for i := 2; i < 16; i += 2 {
+ g.productTable[reverseBits(i)] = gcmDouble(&g.productTable[reverseBits(i/2)])
+ g.productTable[reverseBits(i+1)] = gcmAdd(&g.productTable[reverseBits(i)], &x)
+ }
+
+ return g, nil
+}
+
+const (
+ gcmBlockSize = 16
+ gcmTagSize = 16
+ gcmNonceSize = 12
+)
+
+func (*gcm) NonceSize() int {
+ return gcmNonceSize
+}
+
+func (*gcm) Overhead() int {
+ return gcmTagSize
+}
+
+func (g *gcm) Seal(dst, nonce, plaintext, data []byte) []byte {
+ if len(nonce) != gcmNonceSize {
+ panic("cipher: incorrect nonce length given to GCM")
+ }
+
+ ret, out := sliceForAppend(dst, len(plaintext)+gcmTagSize)
+
+ // See GCM spec, section 7.1.
+ var counter, tagMask [gcmBlockSize]byte
+ copy(counter[:], nonce)
+ counter[gcmBlockSize-1] = 1
+
+ g.cipher.Encrypt(tagMask[:], counter[:])
+ gcmInc32(&counter)
+
+ g.counterCrypt(out, plaintext, &counter)
+ g.auth(out[len(plaintext):], out[:len(plaintext)], data, &tagMask)
+
+ return ret
+}
+
+var errOpen = errors.New("cipher: message authentication failed")
+
+func (g *gcm) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
+ if len(nonce) != gcmNonceSize {
+ panic("cipher: incorrect nonce length given to GCM")
+ }
+
+ if len(ciphertext) < gcmTagSize {
+ return nil, errOpen
+ }
+ tag := ciphertext[len(ciphertext)-gcmTagSize:]
+ ciphertext = ciphertext[:len(ciphertext)-gcmTagSize]
+
+ // See GCM spec, section 7.1.
+ var counter, tagMask [gcmBlockSize]byte
+ copy(counter[:], nonce)
+ counter[gcmBlockSize-1] = 1
+
+ g.cipher.Encrypt(tagMask[:], counter[:])
+ gcmInc32(&counter)
+
+ var expectedTag [gcmTagSize]byte
+ g.auth(expectedTag[:], ciphertext, data, &tagMask)
+
+ if subtle.ConstantTimeCompare(expectedTag[:], tag) != 1 {
+ return nil, errOpen
+ }
+
+ ret, out := sliceForAppend(dst, len(ciphertext))
+ g.counterCrypt(out, ciphertext, &counter)
+
+ return ret, nil
+}
+
+// reverseBits reverses the order of the bits of 4-bit number in i.
+func reverseBits(i int) int {
+ i = ((i << 2) & 0xc) | ((i >> 2) & 0x3)
+ i = ((i << 1) & 0xa) | ((i >> 1) & 0x5)
+ return i
+}
+
+// gcmAdd adds two elements of GF(2¹²⁸) and returns the sum.
+func gcmAdd(x, y *gcmFieldElement) gcmFieldElement {
+ // Addition in a characteristic 2 field is just XOR.
+ return gcmFieldElement{x.low ^ y.low, x.high ^ y.high}
+}
+
+// gcmDouble returns the result of doubling an element of GF(2¹²⁸).
+func gcmDouble(x *gcmFieldElement) (double gcmFieldElement) {
+ msbSet := x.high&1 == 1
+
+ // Because of the bit-ordering, doubling is actually a right shift.
+ double.high = x.high >> 1
+ double.high |= x.low << 63
+ double.low = x.low >> 1
+
+ // If the most-significant bit was set before shifting then it,
+ // conceptually, becomes a term of x^128. This is greater than the
+ // irreducible polynomial so the result has to be reduced. The
+ // irreducible polynomial is 1+x+x^2+x^7+x^128. We can subtract that to
+ // eliminate the term at x^128 which also means subtracting the other
+ // four terms. In characteristic 2 fields, subtraction == addition ==
+ // XOR.
+ if msbSet {
+ double.low ^= 0xe100000000000000
+ }
+
+ return
+}
+
+var gcmReductionTable = []uint16{
+ 0x0000, 0x1c20, 0x3840, 0x2460, 0x7080, 0x6ca0, 0x48c0, 0x54e0,
+ 0xe100, 0xfd20, 0xd940, 0xc560, 0x9180, 0x8da0, 0xa9c0, 0xb5e0,
+}
+
+// mul sets y to y*H, where H is the GCM key, fixed during NewGCM.
+func (g *gcm) mul(y *gcmFieldElement) {
+ var z gcmFieldElement
+
+ for i := 0; i < 2; i++ {
+ word := y.high
+ if i == 1 {
+ word = y.low
+ }
+
+ // Multiplication works by multiplying z by 16 and adding in
+ // one of the precomputed multiples of H.
+ for j := 0; j < 64; j += 4 {
+ msw := z.high & 0xf
+ z.high >>= 4
+ z.high |= z.low << 60
+ z.low >>= 4
+ z.low ^= uint64(gcmReductionTable[msw]) << 48
+
+ // the values in |table| are ordered for
+ // little-endian bit positions. See the comment
+ // in NewGCM.
+ t := &g.productTable[word&0xf]
+
+ z.low ^= t.low
+ z.high ^= t.high
+ word >>= 4
+ }
+ }
+
+ *y = z
+}
+
+// updateBlocks extends y with more polynomial terms from blocks, based on
+// Horner's rule. There must be a multiple of gcmBlockSize bytes in blocks.
+func (g *gcm) updateBlocks(y *gcmFieldElement, blocks []byte) {
+ for len(blocks) > 0 {
+ y.low ^= getUint64(blocks)
+ y.high ^= getUint64(blocks[8:])
+ g.mul(y)
+ blocks = blocks[gcmBlockSize:]
+ }
+}
+
+// update extends y with more polynomial terms from data. If data is not a
+// multiple of gcmBlockSize bytes long then the remainder is zero padded.
+func (g *gcm) update(y *gcmFieldElement, data []byte) {
+ fullBlocks := (len(data) >> 4) << 4
+ g.updateBlocks(y, data[:fullBlocks])
+
+ if len(data) != fullBlocks {
+ var partialBlock [gcmBlockSize]byte
+ copy(partialBlock[:], data[fullBlocks:])
+ g.updateBlocks(y, partialBlock[:])
+ }
+}
+
+// gcmInc32 treats the final four bytes of counterBlock as a big-endian value
+// and increments it.
+func gcmInc32(counterBlock *[16]byte) {
+ c := 1
+ for i := gcmBlockSize - 1; i >= gcmBlockSize-4; i-- {
+ c += int(counterBlock[i])
+ counterBlock[i] = byte(c)
+ c >>= 8
+ }
+}
+
+// sliceForAppend takes a slice and a requested number of bytes. It returns a
+// slice with the contents of the given slice followed by that many bytes and a
+// second slice that aliases into it and contains only the extra bytes. If the
+// original slice has sufficient capacity then no allocation is performed.
+func sliceForAppend(in []byte, n int) (head, tail []byte) {
+ if total := len(in) + n; cap(in) >= total {
+ head = in[:total]
+ } else {
+ head = make([]byte, total)
+ copy(head, in)
+ }
+ tail = head[len(in):]
+ return
+}
+
+// counterCrypt crypts in to out using g.cipher in counter mode.
+func (g *gcm) counterCrypt(out, in []byte, counter *[gcmBlockSize]byte) {
+ var mask [gcmBlockSize]byte
+
+ for len(in) >= gcmBlockSize {
+ g.cipher.Encrypt(mask[:], counter[:])
+ gcmInc32(counter)
+
+ for i := range mask {
+ out[i] = in[i] ^ mask[i]
+ }
+ out = out[gcmBlockSize:]
+ in = in[gcmBlockSize:]
+ }
+
+ if len(in) > 0 {
+ g.cipher.Encrypt(mask[:], counter[:])
+ gcmInc32(counter)
+
+ for i := range in {
+ out[i] = in[i] ^ mask[i]
+ }
+ }
+}
+
+// auth calculates GHASH(ciphertext, additionalData), masks the result with
+// tagMask and writes the result to out.
+func (g *gcm) auth(out, ciphertext, additionalData []byte, tagMask *[gcmTagSize]byte) {
+ var y gcmFieldElement
+ g.update(&y, additionalData)
+ g.update(&y, ciphertext)
+
+ y.low ^= uint64(len(additionalData)) * 8
+ y.high ^= uint64(len(ciphertext)) * 8
+
+ g.mul(&y)
+
+ putUint64(out, y.low)
+ putUint64(out[8:], y.high)
+
+ for i := range tagMask {
+ out[i] ^= tagMask[i]
+ }
+}
+
+func getUint64(data []byte) uint64 {
+ r := uint64(data[0])<<56 |
+ uint64(data[1])<<48 |
+ uint64(data[2])<<40 |
+ uint64(data[3])<<32 |
+ uint64(data[4])<<24 |
+ uint64(data[5])<<16 |
+ uint64(data[6])<<8 |
+ uint64(data[7])
+ return r
+}
+
+func putUint64(out []byte, v uint64) {
+ out[0] = byte(v >> 56)
+ out[1] = byte(v >> 48)
+ out[2] = byte(v >> 40)
+ out[3] = byte(v >> 32)
+ out[4] = byte(v >> 24)
+ out[5] = byte(v >> 16)
+ out[6] = byte(v >> 8)
+ out[7] = byte(v)
+}
diff --git a/src/pkg/crypto/cipher/gcm_test.go b/src/pkg/crypto/cipher/gcm_test.go
new file mode 100644
index 000000000..02d421590
--- /dev/null
+++ b/src/pkg/crypto/cipher/gcm_test.go
@@ -0,0 +1,175 @@
+// Copyright 2013 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 cipher_test
+
+import (
+ "bytes"
+ "crypto/aes"
+ "crypto/cipher"
+ "encoding/hex"
+ "testing"
+)
+
+// AES-GCM test vectors taken from gcmEncryptExtIV128.rsp from
+// http://csrc.nist.gov/groups/STM/cavp/index.html.
+var aesGCMTests = []struct {
+ key, nonce, plaintext, ad, result string
+}{
+ {
+ "11754cd72aec309bf52f7687212e8957",
+ "3c819d9a9bed087615030b65",
+ "",
+ "",
+ "250327c674aaf477aef2675748cf6971",
+ },
+ {
+ "ca47248ac0b6f8372a97ac43508308ed",
+ "ffd2b598feabc9019262d2be",
+ "",
+ "",
+ "60d20404af527d248d893ae495707d1a",
+ },
+ {
+ "77be63708971c4e240d1cb79e8d77feb",
+ "e0e00f19fed7ba0136a797f3",
+ "",
+ "7a43ec1d9c0a5a78a0b16533a6213cab",
+ "209fcc8d3675ed938e9c7166709dd946",
+ },
+ {
+ "7680c5d3ca6154758e510f4d25b98820",
+ "f8f105f9c3df4965780321f8",
+ "",
+ "c94c410194c765e3dcc7964379758ed3",
+ "94dca8edfcf90bb74b153c8d48a17930",
+ },
+ {
+ "7fddb57453c241d03efbed3ac44e371c",
+ "ee283a3fc75575e33efd4887",
+ "d5de42b461646c255c87bd2962d3b9a2",
+ "",
+ "2ccda4a5415cb91e135c2a0f78c9b2fdb36d1df9b9d5e596f83e8b7f52971cb3",
+ },
+ {
+ "ab72c77b97cb5fe9a382d9fe81ffdbed",
+ "54cc7dc2c37ec006bcc6d1da",
+ "007c5e5b3e59df24a7c355584fc1518d",
+ "",
+ "0e1bde206a07a9c2c1b65300f8c649972b4401346697138c7a4891ee59867d0c",
+ },
+ {
+ "fe47fcce5fc32665d2ae399e4eec72ba",
+ "5adb9609dbaeb58cbd6e7275",
+ "7c0e88c88899a779228465074797cd4c2e1498d259b54390b85e3eef1c02df60e743f1b840382c4bccaf3bafb4ca8429bea063",
+ "88319d6e1d3ffa5f987199166c8a9b56c2aeba5a",
+ "98f4826f05a265e6dd2be82db241c0fbbbf9ffb1c173aa83964b7cf5393043736365253ddbc5db8778371495da76d269e5db3e291ef1982e4defedaa2249f898556b47",
+ },
+ {
+ "ec0c2ba17aa95cd6afffe949da9cc3a8",
+ "296bce5b50b7d66096d627ef",
+ "b85b3753535b825cbe5f632c0b843c741351f18aa484281aebec2f45bb9eea2d79d987b764b9611f6c0f8641843d5d58f3a242",
+ "f8d00f05d22bf68599bcdeb131292ad6e2df5d14",
+ "a7443d31c26bdf2a1c945e29ee4bd344a99cfaf3aa71f8b3f191f83c2adfc7a07162995506fde6309ffc19e716eddf1a828c5a890147971946b627c40016da1ecf3e77",
+ },
+ {
+ "2c1f21cf0f6fb3661943155c3e3d8492",
+ "23cb5ff362e22426984d1907",
+ "42f758836986954db44bf37c6ef5e4ac0adaf38f27252a1b82d02ea949c8a1a2dbc0d68b5615ba7c1220ff6510e259f06655d8",
+ "5d3624879d35e46849953e45a32a624d6a6c536ed9857c613b572b0333e701557a713e3f010ecdf9a6bd6c9e3e44b065208645aff4aabee611b391528514170084ccf587177f4488f33cfb5e979e42b6e1cfc0a60238982a7aec",
+ "81824f0e0d523db30d3da369fdc0d60894c7a0a20646dd015073ad2732bd989b14a222b6ad57af43e1895df9dca2a5344a62cc57a3ee28136e94c74838997ae9823f3a",
+ },
+ {
+ "d9f7d2411091f947b4d6f1e2d1f0fb2e",
+ "e1934f5db57cc983e6b180e7",
+ "73ed042327f70fe9c572a61545eda8b2a0c6e1d6c291ef19248e973aee6c312012f490c2c6f6166f4a59431e182663fcaea05a",
+ "0a8a18a7150e940c3d87b38e73baee9a5c049ee21795663e264b694a949822b639092d0e67015e86363583fcf0ca645af9f43375f05fdb4ce84f411dcbca73c2220dea03a20115d2e51398344b16bee1ed7c499b353d6c597af8",
+ "aaadbd5c92e9151ce3db7210b8714126b73e43436d242677afa50384f2149b831f1d573c7891c2a91fbc48db29967ec9542b2321b51ca862cb637cdd03b99a0f93b134",
+ },
+ {
+ "fe9bb47deb3a61e423c2231841cfd1fb",
+ "4d328eb776f500a2f7fb47aa",
+ "f1cc3818e421876bb6b8bbd6c9",
+ "",
+ "b88c5c1977b35b517b0aeae96743fd4727fe5cdb4b5b42818dea7ef8c9",
+ },
+ {
+ "6703df3701a7f54911ca72e24dca046a",
+ "12823ab601c350ea4bc2488c",
+ "793cd125b0b84a043e3ac67717",
+ "",
+ "b2051c80014f42f08735a7b0cd38e6bcd29962e5f2c13626b85a877101",
+ },
+}
+
+func TestAESGCM(t *testing.T) {
+ for i, test := range aesGCMTests {
+ key, _ := hex.DecodeString(test.key)
+ aes, err := aes.NewCipher(key)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ nonce, _ := hex.DecodeString(test.nonce)
+ plaintext, _ := hex.DecodeString(test.plaintext)
+ ad, _ := hex.DecodeString(test.ad)
+ aesgcm, err := cipher.NewGCM(aes)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ ct := aesgcm.Seal(nil, nonce, plaintext, ad)
+ if ctHex := hex.EncodeToString(ct); ctHex != test.result {
+ t.Errorf("#%d: got %s, want %s", i, ctHex, test.result)
+ continue
+ }
+
+ plaintext2, err := aesgcm.Open(nil, nonce, ct, ad)
+ if err != nil {
+ t.Errorf("#%d: Open failed", i)
+ continue
+ }
+
+ if !bytes.Equal(plaintext, plaintext2) {
+ t.Errorf("#%d: plaintext's don't match: got %x vs %x", i, plaintext2, plaintext)
+ continue
+ }
+
+ if len(ad) > 0 {
+ ad[0] ^= 0x80
+ if _, err := aesgcm.Open(nil, nonce, ct, ad); err == nil {
+ t.Errorf("#%d: Open was successful after altering additional data", i)
+ }
+ ad[0] ^= 0x80
+ }
+
+ nonce[0] ^= 0x80
+ if _, err := aesgcm.Open(nil, nonce, ct, ad); err == nil {
+ t.Errorf("#%d: Open was successful after altering nonce", i)
+ }
+ nonce[0] ^= 0x80
+
+ ct[0] ^= 0x80
+ if _, err := aesgcm.Open(nil, nonce, ct, ad); err == nil {
+ t.Errorf("#%d: Open was successful after altering ciphertext", i)
+ }
+ ct[0] ^= 0x80
+ }
+}
+
+func BenchmarkAESGCM(b *testing.B) {
+ buf := make([]byte, 1024)
+ b.SetBytes(int64(len(buf)))
+
+ var key [16]byte
+ var nonce [12]byte
+ aes, _ := aes.NewCipher(key[:])
+ aesgcm, _ := cipher.NewGCM(aes)
+ var out []byte
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ out = aesgcm.Seal(out[:0], nonce[:], buf, nonce[:])
+ }
+}
diff --git a/src/pkg/crypto/cipher/io.go b/src/pkg/crypto/cipher/io.go
index 807e8daea..3938c0a4c 100644
--- a/src/pkg/crypto/cipher/io.go
+++ b/src/pkg/crypto/cipher/io.go
@@ -25,6 +25,8 @@ func (r StreamReader) Read(dst []byte) (n int, err error) {
// StreamWriter wraps a Stream into an io.Writer. It calls XORKeyStream
// to process each slice of data which passes through. If any Write call
// returns short then the StreamWriter is out of sync and must be discarded.
+// A StreamWriter has no internal buffering; Close does not need
+// to be called to flush write data.
type StreamWriter struct {
S Stream
W io.Writer
@@ -43,8 +45,11 @@ func (w StreamWriter) Write(src []byte) (n int, err error) {
return
}
+// Close closes the underlying Writer and returns its Close return value, if the Writer
+// is also an io.Closer. Otherwise it returns nil.
func (w StreamWriter) Close() error {
- // This saves us from either requiring a WriteCloser or having a
- // StreamWriterCloser.
- return w.W.(io.Closer).Close()
+ if c, ok := w.W.(io.Closer); ok {
+ return c.Close()
+ }
+ return nil
}
diff --git a/src/pkg/crypto/crypto.go b/src/pkg/crypto/crypto.go
index ecefc6572..4b03628e6 100644
--- a/src/pkg/crypto/crypto.go
+++ b/src/pkg/crypto/crypto.go
@@ -7,6 +7,7 @@ package crypto
import (
"hash"
+ "strconv"
)
// Hash identifies a cryptographic hash function that is implemented in another
@@ -59,7 +60,7 @@ func (h Hash) New() hash.Hash {
return f()
}
}
- panic("crypto: requested hash function is unavailable")
+ panic("crypto: requested hash function #" + strconv.Itoa(int(h)) + " is unavailable")
}
// Available reports whether the given hash function is linked into the binary.
@@ -77,5 +78,8 @@ func RegisterHash(h Hash, f func() hash.Hash) {
hashes[h] = f
}
+// PublicKey represents a public key using an unspecified algorithm.
+type PublicKey interface{}
+
// PrivateKey represents a private key using an unspecified algorithm.
type PrivateKey interface{}
diff --git a/src/pkg/crypto/des/block.go b/src/pkg/crypto/des/block.go
index c11c62cd7..26355a22e 100644
--- a/src/pkg/crypto/des/block.go
+++ b/src/pkg/crypto/des/block.go
@@ -10,7 +10,7 @@ import (
func cryptBlock(subkeys []uint64, dst, src []byte, decrypt bool) {
b := binary.BigEndian.Uint64(src)
- b = permuteBlock(b, initialPermutation[:])
+ b = permuteInitialBlock(b)
left, right := uint32(b>>32), uint32(b)
var subkey uint64
@@ -25,7 +25,7 @@ func cryptBlock(subkeys []uint64, dst, src []byte, decrypt bool) {
}
// switch left & right and perform final permutation
preOutput := (uint64(right) << 32) | uint64(left)
- binary.BigEndian.PutUint64(dst, permuteBlock(preOutput, finalPermutation[:]))
+ binary.BigEndian.PutUint64(dst, permuteFinalBlock(preOutput))
}
// Encrypt one block from src into dst, using the subkeys.
@@ -40,20 +40,24 @@ func decryptBlock(subkeys []uint64, dst, src []byte) {
// DES Feistel function
func feistel(right uint32, key uint64) (result uint32) {
- sBoxLocations := key ^ permuteBlock(uint64(right), expansionFunction[:])
+ sBoxLocations := key ^ expandBlock(right)
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
+ row := (sBoxLocation & 0x1) | ((sBoxLocation & 0x20) >> 4)
column := (sBoxLocation >> 1) & 0xf
- sBoxResult |= uint32(sBoxes[i][row][column]) << (4 * (7 - i))
+ sBoxResult ^= feistelBox[i][16*row+column]
}
- return uint32(permuteBlock(uint64(sBoxResult), permutationFunction[:]))
+ return sBoxResult
}
+// feistelBox[s][16*i+j] contains the output of permutationFunction
+// for sBoxes[s][i][j] << 4*(7-s)
+var feistelBox [8][64]uint32
+
// general purpose function to perform DES block permutations
func permuteBlock(src uint64, permutation []uint8) (block uint64) {
for position, n := range permutation {
@@ -63,6 +67,127 @@ func permuteBlock(src uint64, permutation []uint8) (block uint64) {
return
}
+func init() {
+ for s := range sBoxes {
+ for i := 0; i < 4; i++ {
+ for j := 0; j < 16; j++ {
+ f := uint64(sBoxes[s][i][j]) << (4 * (7 - uint(s)))
+ f = permuteBlock(uint64(f), permutationFunction[:])
+ feistelBox[s][16*i+j] = uint32(f)
+ }
+ }
+ }
+}
+
+// expandBlock expands an input block of 32 bits,
+// producing an output block of 48 bits.
+func expandBlock(src uint32) (block uint64) {
+ // rotate the 5 highest bits to the right.
+ src = (src << 5) | (src >> 27)
+ for i := 0; i < 8; i++ {
+ block <<= 6
+ // take the 6 bits on the right
+ block |= uint64(src) & (1<<6 - 1)
+ // advance by 4 bits.
+ src = (src << 4) | (src >> 28)
+ }
+ return
+}
+
+// permuteInitialBlock is equivalent to the permutation defined
+// by initialPermutation.
+func permuteInitialBlock(block uint64) uint64 {
+ // block = b7 b6 b5 b4 b3 b2 b1 b0 (8 bytes)
+ b1 := block >> 48
+ b2 := block << 48
+ block ^= b1 ^ b2 ^ b1<<48 ^ b2>>48
+
+ // block = b1 b0 b5 b4 b3 b2 b7 b6
+ b1 = block >> 32 & 0xff00ff
+ b2 = (block & 0xff00ff00)
+ block ^= b1<<32 ^ b2 ^ b1<<8 ^ b2<<24 // exchange b0 b4 with b3 b7
+
+ // block is now b1 b3 b5 b7 b0 b2 b4 b7, the permutation:
+ // ... 8
+ // ... 24
+ // ... 40
+ // ... 56
+ // 7 6 5 4 3 2 1 0
+ // 23 22 21 20 19 18 17 16
+ // ... 32
+ // ... 48
+
+ // exchange 4,5,6,7 with 32,33,34,35 etc.
+ b1 = block & 0x0f0f00000f0f0000
+ b2 = block & 0x0000f0f00000f0f0
+ block ^= b1 ^ b2 ^ b1>>12 ^ b2<<12
+
+ // block is the permutation:
+ //
+ // [+8] [+40]
+ //
+ // 7 6 5 4
+ // 23 22 21 20
+ // 3 2 1 0
+ // 19 18 17 16 [+32]
+
+ // exchange 0,1,4,5 with 18,19,22,23
+ b1 = block & 0x3300330033003300
+ b2 = block & 0x00cc00cc00cc00cc
+ block ^= b1 ^ b2 ^ b1>>6 ^ b2<<6
+
+ // block is the permutation:
+ // 15 14
+ // 13 12
+ // 11 10
+ // 9 8
+ // 7 6
+ // 5 4
+ // 3 2
+ // 1 0 [+16] [+32] [+64]
+
+ // exchange 0,2,4,6 with 9,11,13,15:
+ b1 = block & 0xaaaaaaaa55555555
+ block ^= b1 ^ b1>>33 ^ b1<<33
+
+ // block is the permutation:
+ // 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
+ return block
+}
+
+// permuteInitialBlock is equivalent to the permutation defined
+// by finalPermutation.
+func permuteFinalBlock(block uint64) uint64 {
+ // Perform the same bit exchanges as permuteInitialBlock
+ // but in reverse order.
+ b1 := block & 0xaaaaaaaa55555555
+ block ^= b1 ^ b1>>33 ^ b1<<33
+
+ b1 = block & 0x3300330033003300
+ b2 := block & 0x00cc00cc00cc00cc
+ block ^= b1 ^ b2 ^ b1>>6 ^ b2<<6
+
+ b1 = block & 0x0f0f00000f0f0000
+ b2 = block & 0x0000f0f00000f0f0
+ block ^= b1 ^ b2 ^ b1>>12 ^ b2<<12
+
+ b1 = block >> 32 & 0xff00ff
+ b2 = (block & 0xff00ff00)
+ block ^= b1<<32 ^ b2 ^ b1<<8 ^ b2<<24
+
+ b1 = block >> 48
+ b2 = block << 48
+ block ^= b1 ^ b2 ^ b1<<48 ^ b2>>48
+ return block
+}
+
// creates 16 28-bit blocks rotated according
// to the rotation schedule
func ksRotate(in uint32) (out []uint32) {
diff --git a/src/pkg/crypto/des/des_test.go b/src/pkg/crypto/des/des_test.go
index 2e87e99b6..2bd525afe 100644
--- a/src/pkg/crypto/des/des_test.go
+++ b/src/pkg/crypto/des/des_test.go
@@ -1504,20 +1504,63 @@ func TestSubstitutionTableKnownAnswerDecrypt(t *testing.T) {
}
}
-func ExampleNewTripleDESCipher() {
- // NewTripleDESCipher can also be used when EDE2 is required by
- // duplicating the first 8 bytes of the 16-byte key.
- ede2Key := []byte("example key 1234")
+func TestInitialPermute(t *testing.T) {
+ for i := uint(0); i < 64; i++ {
+ bit := uint64(1) << i
+ got := permuteInitialBlock(bit)
+ want := uint64(1) << finalPermutation[63-i]
+ if got != want {
+ t.Errorf("permute(%x) = %x, want %x", bit, got, want)
+ }
+ }
+}
- var tripleDESKey []byte
- tripleDESKey = append(tripleDESKey, ede2Key[:16]...)
- tripleDESKey = append(tripleDESKey, ede2Key[:8]...)
+func TestFinalPermute(t *testing.T) {
+ for i := uint(0); i < 64; i++ {
+ bit := uint64(1) << i
+ got := permuteFinalBlock(bit)
+ want := uint64(1) << initialPermutation[63-i]
+ if got != want {
+ t.Errorf("permute(%x) = %x, want %x", bit, got, want)
+ }
+ }
+}
- _, err := NewTripleDESCipher(tripleDESKey)
+func TestExpandBlock(t *testing.T) {
+ for i := uint(0); i < 32; i++ {
+ bit := uint32(1) << i
+ got := expandBlock(bit)
+ want := permuteBlock(uint64(bit), expansionFunction[:])
+ if got != want {
+ t.Errorf("expand(%x) = %x, want %x", bit, got, want)
+ }
+ }
+}
+
+func BenchmarkEncrypt(b *testing.B) {
+ tt := encryptDESTests[0]
+ c, err := NewCipher(tt.key)
if err != nil {
- panic(err)
+ b.Fatal("NewCipher:", err)
+ }
+ out := make([]byte, len(tt.in))
+ b.SetBytes(int64(len(out)))
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ c.Encrypt(out, tt.in)
}
+}
- // See crypto/cipher for how to use a cipher.Block for encryption and
- // decryption.
+func BenchmarkDecrypt(b *testing.B) {
+ tt := encryptDESTests[0]
+ c, err := NewCipher(tt.key)
+ if err != nil {
+ b.Fatal("NewCipher:", err)
+ }
+ out := make([]byte, len(tt.out))
+ b.SetBytes(int64(len(out)))
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ c.Decrypt(out, tt.out)
+ }
}
diff --git a/src/pkg/crypto/des/example_test.go b/src/pkg/crypto/des/example_test.go
new file mode 100644
index 000000000..336b59375
--- /dev/null
+++ b/src/pkg/crypto/des/example_test.go
@@ -0,0 +1,25 @@
+// Copyright 2013 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_test
+
+import "crypto/des"
+
+func ExampleNewTripleDESCipher() {
+ // NewTripleDESCipher can also be used when EDE2 is required by
+ // duplicating the first 8 bytes of the 16-byte key.
+ ede2Key := []byte("example key 1234")
+
+ var tripleDESKey []byte
+ tripleDESKey = append(tripleDESKey, ede2Key[:16]...)
+ tripleDESKey = append(tripleDESKey, ede2Key[:8]...)
+
+ _, err := des.NewTripleDESCipher(tripleDESKey)
+ if err != nil {
+ panic(err)
+ }
+
+ // See crypto/cipher for how to use a cipher.Block for encryption and
+ // decryption.
+}
diff --git a/src/pkg/crypto/ecdsa/ecdsa.go b/src/pkg/crypto/ecdsa/ecdsa.go
index 255000229..d02f15c34 100644
--- a/src/pkg/crypto/ecdsa/ecdsa.go
+++ b/src/pkg/crypto/ecdsa/ecdsa.go
@@ -123,8 +123,8 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err err
return
}
-// Verify verifies the signature in r, s of hash using the public key, pub. It
-// returns true iff the signature is valid.
+// Verify verifies the signature in r, s of hash using the public key, pub. Its
+// return value records whether the signature is valid.
func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool {
// See [NSA] 3.4.2
c := pub.Curve
diff --git a/src/pkg/crypto/elliptic/elliptic.go b/src/pkg/crypto/elliptic/elliptic.go
index 7a4ff6614..ba673f80c 100644
--- a/src/pkg/crypto/elliptic/elliptic.go
+++ b/src/pkg/crypto/elliptic/elliptic.go
@@ -322,7 +322,6 @@ func Unmarshal(curve Curve, data []byte) (x, y *big.Int) {
}
var initonce sync.Once
-var p256 *CurveParams
var p384 *CurveParams
var p521 *CurveParams
@@ -333,17 +332,6 @@ func initAll() {
initP521()
}
-func initP256() {
- // See FIPS 186-3, section D.2.3
- p256 = new(CurveParams)
- 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)
- p256.BitSize = 256
-}
-
func initP384() {
// See FIPS 186-3, section D.2.4
p384 = new(CurveParams)
diff --git a/src/pkg/crypto/elliptic/elliptic_test.go b/src/pkg/crypto/elliptic/elliptic_test.go
index 58f903966..4dc27c92b 100644
--- a/src/pkg/crypto/elliptic/elliptic_test.go
+++ b/src/pkg/crypto/elliptic/elliptic_test.go
@@ -322,6 +322,52 @@ func TestGenericBaseMult(t *testing.T) {
}
}
+func TestP256BaseMult(t *testing.T) {
+ p256 := P256()
+ p256Generic := p256.Params()
+
+ scalars := make([]*big.Int, 0, len(p224BaseMultTests)+1)
+ for _, e := range p224BaseMultTests {
+ k, _ := new(big.Int).SetString(e.k, 10)
+ scalars = append(scalars, k)
+ }
+ k := new(big.Int).SetInt64(1)
+ k.Lsh(k, 500)
+ scalars = append(scalars, k)
+
+ for i, k := range scalars {
+ x, y := p256.ScalarBaseMult(k.Bytes())
+ x2, y2 := p256Generic.ScalarBaseMult(k.Bytes())
+ if x.Cmp(x2) != 0 || y.Cmp(y2) != 0 {
+ t.Errorf("#%d: got (%x, %x), want (%x, %x)", i, x, y, x2, y2)
+ }
+
+ if testing.Short() && i > 5 {
+ break
+ }
+ }
+}
+
+func TestP256Mult(t *testing.T) {
+ p256 := P256()
+ p256Generic := p256.Params()
+
+ for i, e := range p224BaseMultTests {
+ x, _ := new(big.Int).SetString(e.x, 16)
+ y, _ := new(big.Int).SetString(e.y, 16)
+ k, _ := new(big.Int).SetString(e.k, 10)
+
+ xx, yy := p256.ScalarMult(x, y, k.Bytes())
+ xx2, yy2 := p256Generic.ScalarMult(x, y, k.Bytes())
+ if xx.Cmp(xx2) != 0 || yy.Cmp(yy2) != 0 {
+ t.Errorf("#%d: got (%x, %x), want (%x, %x)", i, xx, yy, xx2, yy2)
+ }
+ if testing.Short() && i > 5 {
+ break
+ }
+ }
+}
+
func TestInfinity(t *testing.T) {
tests := []struct {
name string
@@ -371,6 +417,17 @@ func BenchmarkBaseMult(b *testing.B) {
}
}
+func BenchmarkBaseMultP256(b *testing.B) {
+ b.ResetTimer()
+ p256 := P256()
+ e := p224BaseMultTests[25]
+ k, _ := new(big.Int).SetString(e.k, 10)
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ p256.ScalarBaseMult(k.Bytes())
+ }
+}
+
func TestMarshal(t *testing.T) {
p224 := P224()
_, x, y, err := GenerateKey(p224, rand.Reader)
diff --git a/src/pkg/crypto/elliptic/p256.go b/src/pkg/crypto/elliptic/p256.go
new file mode 100644
index 000000000..82be51e62
--- /dev/null
+++ b/src/pkg/crypto/elliptic/p256.go
@@ -0,0 +1,1186 @@
+// Copyright 2013 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 elliptic
+
+// This file contains a constant-time, 32-bit implementation of P256.
+
+import (
+ "math/big"
+)
+
+type p256Curve struct {
+ *CurveParams
+}
+
+var (
+ p256 p256Curve
+ // RInverse contains 1/R mod p - the inverse of the Montgomery constant
+ // (2**257).
+ p256RInverse *big.Int
+)
+
+func initP256() {
+ // See FIPS 186-3, section D.2.3
+ p256.CurveParams = new(CurveParams)
+ 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)
+ p256.BitSize = 256
+
+ p256RInverse, _ = new(big.Int).SetString("7fffffff00000001fffffffe8000000100000000ffffffff0000000180000000", 16)
+}
+
+func (curve p256Curve) Params() *CurveParams {
+ return curve.CurveParams
+}
+
+// p256GetScalar endian-swaps the big-endian scalar value from in and writes it
+// to out. If the scalar is equal or greater than the order of the group, it's
+// reduced modulo that order.
+func p256GetScalar(out *[32]byte, in []byte) {
+ n := new(big.Int).SetBytes(in)
+ var scalarBytes []byte
+
+ if n.Cmp(p256.N) >= 0 {
+ n.Mod(n, p256.N)
+ scalarBytes = n.Bytes()
+ } else {
+ scalarBytes = in
+ }
+
+ for i, v := range scalarBytes {
+ out[len(scalarBytes)-(1+i)] = v
+ }
+}
+
+func (p256Curve) ScalarBaseMult(scalar []byte) (x, y *big.Int) {
+ var scalarReversed [32]byte
+ p256GetScalar(&scalarReversed, scalar)
+
+ var x1, y1, z1 [p256Limbs]uint32
+ p256ScalarBaseMult(&x1, &y1, &z1, &scalarReversed)
+ return p256ToAffine(&x1, &y1, &z1)
+}
+
+func (p256Curve) ScalarMult(bigX, bigY *big.Int, scalar []byte) (x, y *big.Int) {
+ var scalarReversed [32]byte
+ p256GetScalar(&scalarReversed, scalar)
+
+ var px, py, x1, y1, z1 [p256Limbs]uint32
+ p256FromBig(&px, bigX)
+ p256FromBig(&py, bigY)
+ p256ScalarMult(&x1, &y1, &z1, &px, &py, &scalarReversed)
+ return p256ToAffine(&x1, &y1, &z1)
+}
+
+// Field elements are represented as nine, unsigned 32-bit words.
+//
+// The value of an field element is:
+// x[0] + (x[1] * 2**29) + (x[2] * 2**57) + ... + (x[8] * 2**228)
+//
+// That is, each limb is alternately 29 or 28-bits wide in little-endian
+// order.
+//
+// This means that a field element hits 2**257, rather than 2**256 as we would
+// like. A 28, 29, ... pattern would cause us to hit 2**256, but that causes
+// problems when multiplying as terms end up one bit short of a limb which
+// would require much bit-shifting to correct.
+//
+// Finally, the values stored in a field element are in Montgomery form. So the
+// value |y| is stored as (y*R) mod p, where p is the P-256 prime and R is
+// 2**257.
+
+const (
+ p256Limbs = 9
+ bottom29Bits = 0x1fffffff
+)
+
+var (
+ // p256One is the number 1 as a field element.
+ p256One = [p256Limbs]uint32{2, 0, 0, 0xffff800, 0x1fffffff, 0xfffffff, 0x1fbfffff, 0x1ffffff, 0}
+ p256Zero = [p256Limbs]uint32{0, 0, 0, 0, 0, 0, 0, 0, 0}
+ // p256P is the prime modulus as a field element.
+ p256P = [p256Limbs]uint32{0x1fffffff, 0xfffffff, 0x1fffffff, 0x3ff, 0, 0, 0x200000, 0xf000000, 0xfffffff}
+ // p2562P is the twice prime modulus as a field element.
+ p2562P = [p256Limbs]uint32{0x1ffffffe, 0xfffffff, 0x1fffffff, 0x7ff, 0, 0, 0x400000, 0xe000000, 0x1fffffff}
+)
+
+// p256Precomputed contains precomputed values to aid the calculation of scalar
+// multiples of the base point, G. It's actually two, equal length, tables
+// concatenated.
+//
+// The first table contains (x,y) field element pairs for 16 multiples of the
+// base point, G.
+//
+// Index | Index (binary) | Value
+// 0 | 0000 | 0G (all zeros, omitted)
+// 1 | 0001 | G
+// 2 | 0010 | 2**64G
+// 3 | 0011 | 2**64G + G
+// 4 | 0100 | 2**128G
+// 5 | 0101 | 2**128G + G
+// 6 | 0110 | 2**128G + 2**64G
+// 7 | 0111 | 2**128G + 2**64G + G
+// 8 | 1000 | 2**192G
+// 9 | 1001 | 2**192G + G
+// 10 | 1010 | 2**192G + 2**64G
+// 11 | 1011 | 2**192G + 2**64G + G
+// 12 | 1100 | 2**192G + 2**128G
+// 13 | 1101 | 2**192G + 2**128G + G
+// 14 | 1110 | 2**192G + 2**128G + 2**64G
+// 15 | 1111 | 2**192G + 2**128G + 2**64G + G
+//
+// The second table follows the same style, but the terms are 2**32G,
+// 2**96G, 2**160G, 2**224G.
+//
+// This is ~2KB of data.
+var p256Precomputed = [p256Limbs * 2 * 15 * 2]uint32{
+ 0x11522878, 0xe730d41, 0xdb60179, 0x4afe2ff, 0x12883add, 0xcaddd88, 0x119e7edc, 0xd4a6eab, 0x3120bee,
+ 0x1d2aac15, 0xf25357c, 0x19e45cdd, 0x5c721d0, 0x1992c5a5, 0xa237487, 0x154ba21, 0x14b10bb, 0xae3fe3,
+ 0xd41a576, 0x922fc51, 0x234994f, 0x60b60d3, 0x164586ae, 0xce95f18, 0x1fe49073, 0x3fa36cc, 0x5ebcd2c,
+ 0xb402f2f, 0x15c70bf, 0x1561925c, 0x5a26704, 0xda91e90, 0xcdc1c7f, 0x1ea12446, 0xe1ade1e, 0xec91f22,
+ 0x26f7778, 0x566847e, 0xa0bec9e, 0x234f453, 0x1a31f21a, 0xd85e75c, 0x56c7109, 0xa267a00, 0xb57c050,
+ 0x98fb57, 0xaa837cc, 0x60c0792, 0xcfa5e19, 0x61bab9e, 0x589e39b, 0xa324c5, 0x7d6dee7, 0x2976e4b,
+ 0x1fc4124a, 0xa8c244b, 0x1ce86762, 0xcd61c7e, 0x1831c8e0, 0x75774e1, 0x1d96a5a9, 0x843a649, 0xc3ab0fa,
+ 0x6e2e7d5, 0x7673a2a, 0x178b65e8, 0x4003e9b, 0x1a1f11c2, 0x7816ea, 0xf643e11, 0x58c43df, 0xf423fc2,
+ 0x19633ffa, 0x891f2b2, 0x123c231c, 0x46add8c, 0x54700dd, 0x59e2b17, 0x172db40f, 0x83e277d, 0xb0dd609,
+ 0xfd1da12, 0x35c6e52, 0x19ede20c, 0xd19e0c0, 0x97d0f40, 0xb015b19, 0x449e3f5, 0xe10c9e, 0x33ab581,
+ 0x56a67ab, 0x577734d, 0x1dddc062, 0xc57b10d, 0x149b39d, 0x26a9e7b, 0xc35df9f, 0x48764cd, 0x76dbcca,
+ 0xca4b366, 0xe9303ab, 0x1a7480e7, 0x57e9e81, 0x1e13eb50, 0xf466cf3, 0x6f16b20, 0x4ba3173, 0xc168c33,
+ 0x15cb5439, 0x6a38e11, 0x73658bd, 0xb29564f, 0x3f6dc5b, 0x53b97e, 0x1322c4c0, 0x65dd7ff, 0x3a1e4f6,
+ 0x14e614aa, 0x9246317, 0x1bc83aca, 0xad97eed, 0xd38ce4a, 0xf82b006, 0x341f077, 0xa6add89, 0x4894acd,
+ 0x9f162d5, 0xf8410ef, 0x1b266a56, 0xd7f223, 0x3e0cb92, 0xe39b672, 0x6a2901a, 0x69a8556, 0x7e7c0,
+ 0x9b7d8d3, 0x309a80, 0x1ad05f7f, 0xc2fb5dd, 0xcbfd41d, 0x9ceb638, 0x1051825c, 0xda0cf5b, 0x812e881,
+ 0x6f35669, 0x6a56f2c, 0x1df8d184, 0x345820, 0x1477d477, 0x1645db1, 0xbe80c51, 0xc22be3e, 0xe35e65a,
+ 0x1aeb7aa0, 0xc375315, 0xf67bc99, 0x7fdd7b9, 0x191fc1be, 0x61235d, 0x2c184e9, 0x1c5a839, 0x47a1e26,
+ 0xb7cb456, 0x93e225d, 0x14f3c6ed, 0xccc1ac9, 0x17fe37f3, 0x4988989, 0x1a90c502, 0x2f32042, 0xa17769b,
+ 0xafd8c7c, 0x8191c6e, 0x1dcdb237, 0x16200c0, 0x107b32a1, 0x66c08db, 0x10d06a02, 0x3fc93, 0x5620023,
+ 0x16722b27, 0x68b5c59, 0x270fcfc, 0xfad0ecc, 0xe5de1c2, 0xeab466b, 0x2fc513c, 0x407f75c, 0xbaab133,
+ 0x9705fe9, 0xb88b8e7, 0x734c993, 0x1e1ff8f, 0x19156970, 0xabd0f00, 0x10469ea7, 0x3293ac0, 0xcdc98aa,
+ 0x1d843fd, 0xe14bfe8, 0x15be825f, 0x8b5212, 0xeb3fb67, 0x81cbd29, 0xbc62f16, 0x2b6fcc7, 0xf5a4e29,
+ 0x13560b66, 0xc0b6ac2, 0x51ae690, 0xd41e271, 0xf3e9bd4, 0x1d70aab, 0x1029f72, 0x73e1c35, 0xee70fbc,
+ 0xad81baf, 0x9ecc49a, 0x86c741e, 0xfe6be30, 0x176752e7, 0x23d416, 0x1f83de85, 0x27de188, 0x66f70b8,
+ 0x181cd51f, 0x96b6e4c, 0x188f2335, 0xa5df759, 0x17a77eb6, 0xfeb0e73, 0x154ae914, 0x2f3ec51, 0x3826b59,
+ 0xb91f17d, 0x1c72949, 0x1362bf0a, 0xe23fddf, 0xa5614b0, 0xf7d8f, 0x79061, 0x823d9d2, 0x8213f39,
+ 0x1128ae0b, 0xd095d05, 0xb85c0c2, 0x1ecb2ef, 0x24ddc84, 0xe35e901, 0x18411a4a, 0xf5ddc3d, 0x3786689,
+ 0x52260e8, 0x5ae3564, 0x542b10d, 0x8d93a45, 0x19952aa4, 0x996cc41, 0x1051a729, 0x4be3499, 0x52b23aa,
+ 0x109f307e, 0x6f5b6bb, 0x1f84e1e7, 0x77a0cfa, 0x10c4df3f, 0x25a02ea, 0xb048035, 0xe31de66, 0xc6ecaa3,
+ 0x28ea335, 0x2886024, 0x1372f020, 0xf55d35, 0x15e4684c, 0xf2a9e17, 0x1a4a7529, 0xcb7beb1, 0xb2a78a1,
+ 0x1ab21f1f, 0x6361ccf, 0x6c9179d, 0xb135627, 0x1267b974, 0x4408bad, 0x1cbff658, 0xe3d6511, 0xc7d76f,
+ 0x1cc7a69, 0xe7ee31b, 0x54fab4f, 0x2b914f, 0x1ad27a30, 0xcd3579e, 0xc50124c, 0x50daa90, 0xb13f72,
+ 0xb06aa75, 0x70f5cc6, 0x1649e5aa, 0x84a5312, 0x329043c, 0x41c4011, 0x13d32411, 0xb04a838, 0xd760d2d,
+ 0x1713b532, 0xbaa0c03, 0x84022ab, 0x6bcf5c1, 0x2f45379, 0x18ae070, 0x18c9e11e, 0x20bca9a, 0x66f496b,
+ 0x3eef294, 0x67500d2, 0xd7f613c, 0x2dbbeb, 0xb741038, 0xe04133f, 0x1582968d, 0xbe985f7, 0x1acbc1a,
+ 0x1a6a939f, 0x33e50f6, 0xd665ed4, 0xb4b7bd6, 0x1e5a3799, 0x6b33847, 0x17fa56ff, 0x65ef930, 0x21dc4a,
+ 0x2b37659, 0x450fe17, 0xb357b65, 0xdf5efac, 0x15397bef, 0x9d35a7f, 0x112ac15f, 0x624e62e, 0xa90ae2f,
+ 0x107eecd2, 0x1f69bbe, 0x77d6bce, 0x5741394, 0x13c684fc, 0x950c910, 0x725522b, 0xdc78583, 0x40eeabb,
+ 0x1fde328a, 0xbd61d96, 0xd28c387, 0x9e77d89, 0x12550c40, 0x759cb7d, 0x367ef34, 0xae2a960, 0x91b8bdc,
+ 0x93462a9, 0xf469ef, 0xb2e9aef, 0xd2ca771, 0x54e1f42, 0x7aaa49, 0x6316abb, 0x2413c8e, 0x5425bf9,
+ 0x1bed3e3a, 0xf272274, 0x1f5e7326, 0x6416517, 0xea27072, 0x9cedea7, 0x6e7633, 0x7c91952, 0xd806dce,
+ 0x8e2a7e1, 0xe421e1a, 0x418c9e1, 0x1dbc890, 0x1b395c36, 0xa1dc175, 0x1dc4ef73, 0x8956f34, 0xe4b5cf2,
+ 0x1b0d3a18, 0x3194a36, 0x6c2641f, 0xe44124c, 0xa2f4eaa, 0xa8c25ba, 0xf927ed7, 0x627b614, 0x7371cca,
+ 0xba16694, 0x417bc03, 0x7c0a7e3, 0x9c35c19, 0x1168a205, 0x8b6b00d, 0x10e3edc9, 0x9c19bf2, 0x5882229,
+ 0x1b2b4162, 0xa5cef1a, 0x1543622b, 0x9bd433e, 0x364e04d, 0x7480792, 0x5c9b5b3, 0xe85ff25, 0x408ef57,
+ 0x1814cfa4, 0x121b41b, 0xd248a0f, 0x3b05222, 0x39bb16a, 0xc75966d, 0xa038113, 0xa4a1769, 0x11fbc6c,
+ 0x917e50e, 0xeec3da8, 0x169d6eac, 0x10c1699, 0xa416153, 0xf724912, 0x15cd60b7, 0x4acbad9, 0x5efc5fa,
+ 0xf150ed7, 0x122b51, 0x1104b40a, 0xcb7f442, 0xfbb28ff, 0x6ac53ca, 0x196142cc, 0x7bf0fa9, 0x957651,
+ 0x4e0f215, 0xed439f8, 0x3f46bd5, 0x5ace82f, 0x110916b6, 0x6db078, 0xffd7d57, 0xf2ecaac, 0xca86dec,
+ 0x15d6b2da, 0x965ecc9, 0x1c92b4c2, 0x1f3811, 0x1cb080f5, 0x2d8b804, 0x19d1c12d, 0xf20bd46, 0x1951fa7,
+ 0xa3656c3, 0x523a425, 0xfcd0692, 0xd44ddc8, 0x131f0f5b, 0xaf80e4a, 0xcd9fc74, 0x99bb618, 0x2db944c,
+ 0xa673090, 0x1c210e1, 0x178c8d23, 0x1474383, 0x10b8743d, 0x985a55b, 0x2e74779, 0x576138, 0x9587927,
+ 0x133130fa, 0xbe05516, 0x9f4d619, 0xbb62570, 0x99ec591, 0xd9468fe, 0x1d07782d, 0xfc72e0b, 0x701b298,
+ 0x1863863b, 0x85954b8, 0x121a0c36, 0x9e7fedf, 0xf64b429, 0x9b9d71e, 0x14e2f5d8, 0xf858d3a, 0x942eea8,
+ 0xda5b765, 0x6edafff, 0xa9d18cc, 0xc65e4ba, 0x1c747e86, 0xe4ea915, 0x1981d7a1, 0x8395659, 0x52ed4e2,
+ 0x87d43b7, 0x37ab11b, 0x19d292ce, 0xf8d4692, 0x18c3053f, 0x8863e13, 0x4c146c0, 0x6bdf55a, 0x4e4457d,
+ 0x16152289, 0xac78ec2, 0x1a59c5a2, 0x2028b97, 0x71c2d01, 0x295851f, 0x404747b, 0x878558d, 0x7d29aa4,
+ 0x13d8341f, 0x8daefd7, 0x139c972d, 0x6b7ea75, 0xd4a9dde, 0xff163d8, 0x81d55d7, 0xa5bef68, 0xb7b30d8,
+ 0xbe73d6f, 0xaa88141, 0xd976c81, 0x7e7a9cc, 0x18beb771, 0xd773cbd, 0x13f51951, 0x9d0c177, 0x1c49a78,
+}
+
+// Field element operations:
+
+// nonZeroToAllOnes returns:
+// 0xffffffff for 0 < x <= 2**31
+// 0 for x == 0 or x > 2**31.
+func nonZeroToAllOnes(x uint32) uint32 {
+ return ((x - 1) >> 31) - 1
+}
+
+// p256ReduceCarry adds a multiple of p in order to cancel |carry|,
+// which is a term at 2**257.
+//
+// On entry: carry < 2**3, inout[0,2,...] < 2**29, inout[1,3,...] < 2**28.
+// On exit: inout[0,2,..] < 2**30, inout[1,3,...] < 2**29.
+func p256ReduceCarry(inout *[p256Limbs]uint32, carry uint32) {
+ carry_mask := nonZeroToAllOnes(carry)
+
+ inout[0] += carry << 1
+ inout[3] += 0x10000000 & carry_mask
+ // carry < 2**3 thus (carry << 11) < 2**14 and we added 2**28 in the
+ // previous line therefore this doesn't underflow.
+ inout[3] -= carry << 11
+ inout[4] += (0x20000000 - 1) & carry_mask
+ inout[5] += (0x10000000 - 1) & carry_mask
+ inout[6] += (0x20000000 - 1) & carry_mask
+ inout[6] -= carry << 22
+ // This may underflow if carry is non-zero but, if so, we'll fix it in the
+ // next line.
+ inout[7] -= 1 & carry_mask
+ inout[7] += carry << 25
+}
+
+// p256Sum sets out = in+in2.
+//
+// On entry, in[i]+in2[i] must not overflow a 32-bit word.
+// On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29
+func p256Sum(out, in, in2 *[p256Limbs]uint32) {
+ carry := uint32(0)
+ for i := 0; ; i++ {
+ out[i] = in[i] + in2[i]
+ out[i] += carry
+ carry = out[i] >> 29
+ out[i] &= bottom29Bits
+
+ i++
+ if i == p256Limbs {
+ break
+ }
+
+ out[i] = in[i] + in2[i]
+ out[i] += carry
+ carry = out[i] >> 28
+ out[i] &= bottom28Bits
+ }
+
+ p256ReduceCarry(out, carry)
+}
+
+const (
+ two30m2 = 1<<30 - 1<<2
+ two30p13m2 = 1<<30 + 1<<13 - 1<<2
+ two31m2 = 1<<31 - 1<<2
+ two31p24m2 = 1<<31 + 1<<24 - 1<<2
+ two30m27m2 = 1<<30 - 1<<27 - 1<<2
+)
+
+// p256Zero31 is 0 mod p.
+var p256Zero31 = [p256Limbs]uint32{two31m3, two30m2, two31m2, two30p13m2, two31m2, two30m2, two31p24m2, two30m27m2, two31m2}
+
+// p256Diff sets out = in-in2.
+//
+// On entry: in[0,2,...] < 2**30, in[1,3,...] < 2**29 and
+// in2[0,2,...] < 2**30, in2[1,3,...] < 2**29.
+// On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29.
+func p256Diff(out, in, in2 *[p256Limbs]uint32) {
+ var carry uint32
+
+ for i := 0; ; i++ {
+ out[i] = in[i] - in2[i]
+ out[i] += p256Zero31[i]
+ out[i] += carry
+ carry = out[i] >> 29
+ out[i] &= bottom29Bits
+
+ i++
+ if i == p256Limbs {
+ break
+ }
+
+ out[i] = in[i] - in2[i]
+ out[i] += p256Zero31[i]
+ out[i] += carry
+ carry = out[i] >> 28
+ out[i] &= bottom28Bits
+ }
+
+ p256ReduceCarry(out, carry)
+}
+
+// p256ReduceDegree sets out = tmp/R mod p where tmp contains 64-bit words with
+// the same 29,28,... bit positions as an field element.
+//
+// The values in field elements are in Montgomery form: x*R mod p where R =
+// 2**257. Since we just multiplied two Montgomery values together, the result
+// is x*y*R*R mod p. We wish to divide by R in order for the result also to be
+// in Montgomery form.
+//
+// On entry: tmp[i] < 2**64
+// On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29
+func p256ReduceDegree(out *[p256Limbs]uint32, tmp [17]uint64) {
+ // The following table may be helpful when reading this code:
+ //
+ // Limb number: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10...
+ // Width (bits): 29| 28| 29| 28| 29| 28| 29| 28| 29| 28| 29
+ // Start bit: 0 | 29| 57| 86|114|143|171|200|228|257|285
+ // (odd phase): 0 | 28| 57| 85|114|142|171|199|228|256|285
+ var tmp2 [18]uint32
+ var carry, x, xMask uint32
+
+ // tmp contains 64-bit words with the same 29,28,29-bit positions as an
+ // field element. So the top of an element of tmp might overlap with
+ // another element two positions down. The following loop eliminates
+ // this overlap.
+ tmp2[0] = uint32(tmp[0]) & bottom29Bits
+
+ tmp2[1] = uint32(tmp[0]) >> 29
+ tmp2[1] |= (uint32(tmp[0]>>32) << 3) & bottom28Bits
+ tmp2[1] += uint32(tmp[1]) & bottom28Bits
+ carry = tmp2[1] >> 28
+ tmp2[1] &= bottom28Bits
+
+ for i := 2; i < 17; i++ {
+ tmp2[i] = (uint32(tmp[i-2] >> 32)) >> 25
+ tmp2[i] += (uint32(tmp[i-1])) >> 28
+ tmp2[i] += (uint32(tmp[i-1]>>32) << 4) & bottom29Bits
+ tmp2[i] += uint32(tmp[i]) & bottom29Bits
+ tmp2[i] += carry
+ carry = tmp2[i] >> 29
+ tmp2[i] &= bottom29Bits
+
+ i++
+ if i == 17 {
+ break
+ }
+ tmp2[i] = uint32(tmp[i-2]>>32) >> 25
+ tmp2[i] += uint32(tmp[i-1]) >> 29
+ tmp2[i] += ((uint32(tmp[i-1] >> 32)) << 3) & bottom28Bits
+ tmp2[i] += uint32(tmp[i]) & bottom28Bits
+ tmp2[i] += carry
+ carry = tmp2[i] >> 28
+ tmp2[i] &= bottom28Bits
+ }
+
+ tmp2[17] = uint32(tmp[15]>>32) >> 25
+ tmp2[17] += uint32(tmp[16]) >> 29
+ tmp2[17] += uint32(tmp[16]>>32) << 3
+ tmp2[17] += carry
+
+ // Montgomery elimination of terms:
+ //
+ // Since R is 2**257, we can divide by R with a bitwise shift if we can
+ // ensure that the right-most 257 bits are all zero. We can make that true
+ // by adding multiplies of p without affecting the value.
+ //
+ // So we eliminate limbs from right to left. Since the bottom 29 bits of p
+ // are all ones, then by adding tmp2[0]*p to tmp2 we'll make tmp2[0] == 0.
+ // We can do that for 8 further limbs and then right shift to eliminate the
+ // extra factor of R.
+ for i := 0; ; i += 2 {
+ tmp2[i+1] += tmp2[i] >> 29
+ x = tmp2[i] & bottom29Bits
+ xMask = nonZeroToAllOnes(x)
+ tmp2[i] = 0
+
+ // The bounds calculations for this loop are tricky. Each iteration of
+ // the loop eliminates two words by adding values to words to their
+ // right.
+ //
+ // The following table contains the amounts added to each word (as an
+ // offset from the value of i at the top of the loop). The amounts are
+ // accounted for from the first and second half of the loop separately
+ // and are written as, for example, 28 to mean a value <2**28.
+ //
+ // Word: 3 4 5 6 7 8 9 10
+ // Added in top half: 28 11 29 21 29 28
+ // 28 29
+ // 29
+ // Added in bottom half: 29 10 28 21 28 28
+ // 29
+ //
+ // The value that is currently offset 7 will be offset 5 for the next
+ // iteration and then offset 3 for the iteration after that. Therefore
+ // the total value added will be the values added at 7, 5 and 3.
+ //
+ // The following table accumulates these values. The sums at the bottom
+ // are written as, for example, 29+28, to mean a value < 2**29+2**28.
+ //
+ // Word: 3 4 5 6 7 8 9 10 11 12 13
+ // 28 11 10 29 21 29 28 28 28 28 28
+ // 29 28 11 28 29 28 29 28 29 28
+ // 29 28 21 21 29 21 29 21
+ // 10 29 28 21 28 21 28
+ // 28 29 28 29 28 29 28
+ // 11 10 29 10 29 10
+ // 29 28 11 28 11
+ // 29 29
+ // --------------------------------------------
+ // 30+ 31+ 30+ 31+ 30+
+ // 28+ 29+ 28+ 29+ 21+
+ // 21+ 28+ 21+ 28+ 10
+ // 10 21+ 10 21+
+ // 11 11
+ //
+ // So the greatest amount is added to tmp2[10] and tmp2[12]. If
+ // tmp2[10/12] has an initial value of <2**29, then the maximum value
+ // will be < 2**31 + 2**30 + 2**28 + 2**21 + 2**11, which is < 2**32,
+ // as required.
+ tmp2[i+3] += (x << 10) & bottom28Bits
+ tmp2[i+4] += (x >> 18)
+
+ tmp2[i+6] += (x << 21) & bottom29Bits
+ tmp2[i+7] += x >> 8
+
+ // At position 200, which is the starting bit position for word 7, we
+ // have a factor of 0xf000000 = 2**28 - 2**24.
+ tmp2[i+7] += 0x10000000 & xMask
+ tmp2[i+8] += (x - 1) & xMask
+ tmp2[i+7] -= (x << 24) & bottom28Bits
+ tmp2[i+8] -= x >> 4
+
+ tmp2[i+8] += 0x20000000 & xMask
+ tmp2[i+8] -= x
+ tmp2[i+8] += (x << 28) & bottom29Bits
+ tmp2[i+9] += ((x >> 1) - 1) & xMask
+
+ if i+1 == p256Limbs {
+ break
+ }
+ tmp2[i+2] += tmp2[i+1] >> 28
+ x = tmp2[i+1] & bottom28Bits
+ xMask = nonZeroToAllOnes(x)
+ tmp2[i+1] = 0
+
+ tmp2[i+4] += (x << 11) & bottom29Bits
+ tmp2[i+5] += (x >> 18)
+
+ tmp2[i+7] += (x << 21) & bottom28Bits
+ tmp2[i+8] += x >> 7
+
+ // At position 199, which is the starting bit of the 8th word when
+ // dealing with a context starting on an odd word, we have a factor of
+ // 0x1e000000 = 2**29 - 2**25. Since we have not updated i, the 8th
+ // word from i+1 is i+8.
+ tmp2[i+8] += 0x20000000 & xMask
+ tmp2[i+9] += (x - 1) & xMask
+ tmp2[i+8] -= (x << 25) & bottom29Bits
+ tmp2[i+9] -= x >> 4
+
+ tmp2[i+9] += 0x10000000 & xMask
+ tmp2[i+9] -= x
+ tmp2[i+10] += (x - 1) & xMask
+ }
+
+ // We merge the right shift with a carry chain. The words above 2**257 have
+ // widths of 28,29,... which we need to correct when copying them down.
+ carry = 0
+ for i := 0; i < 8; i++ {
+ // The maximum value of tmp2[i + 9] occurs on the first iteration and
+ // is < 2**30+2**29+2**28. Adding 2**29 (from tmp2[i + 10]) is
+ // therefore safe.
+ out[i] = tmp2[i+9]
+ out[i] += carry
+ out[i] += (tmp2[i+10] << 28) & bottom29Bits
+ carry = out[i] >> 29
+ out[i] &= bottom29Bits
+
+ i++
+ out[i] = tmp2[i+9] >> 1
+ out[i] += carry
+ carry = out[i] >> 28
+ out[i] &= bottom28Bits
+ }
+
+ out[8] = tmp2[17]
+ out[8] += carry
+ carry = out[8] >> 29
+ out[8] &= bottom29Bits
+
+ p256ReduceCarry(out, carry)
+}
+
+// p256Square sets out=in*in.
+//
+// On entry: in[0,2,...] < 2**30, in[1,3,...] < 2**29.
+// On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29.
+func p256Square(out, in *[p256Limbs]uint32) {
+ var tmp [17]uint64
+
+ tmp[0] = uint64(in[0]) * uint64(in[0])
+ tmp[1] = uint64(in[0]) * (uint64(in[1]) << 1)
+ tmp[2] = uint64(in[0])*(uint64(in[2])<<1) +
+ uint64(in[1])*(uint64(in[1])<<1)
+ tmp[3] = uint64(in[0])*(uint64(in[3])<<1) +
+ uint64(in[1])*(uint64(in[2])<<1)
+ tmp[4] = uint64(in[0])*(uint64(in[4])<<1) +
+ uint64(in[1])*(uint64(in[3])<<2) +
+ uint64(in[2])*uint64(in[2])
+ tmp[5] = uint64(in[0])*(uint64(in[5])<<1) +
+ uint64(in[1])*(uint64(in[4])<<1) +
+ uint64(in[2])*(uint64(in[3])<<1)
+ tmp[6] = uint64(in[0])*(uint64(in[6])<<1) +
+ uint64(in[1])*(uint64(in[5])<<2) +
+ uint64(in[2])*(uint64(in[4])<<1) +
+ uint64(in[3])*(uint64(in[3])<<1)
+ tmp[7] = uint64(in[0])*(uint64(in[7])<<1) +
+ uint64(in[1])*(uint64(in[6])<<1) +
+ uint64(in[2])*(uint64(in[5])<<1) +
+ uint64(in[3])*(uint64(in[4])<<1)
+ // tmp[8] has the greatest value of 2**61 + 2**60 + 2**61 + 2**60 + 2**60,
+ // which is < 2**64 as required.
+ tmp[8] = uint64(in[0])*(uint64(in[8])<<1) +
+ uint64(in[1])*(uint64(in[7])<<2) +
+ uint64(in[2])*(uint64(in[6])<<1) +
+ uint64(in[3])*(uint64(in[5])<<2) +
+ uint64(in[4])*uint64(in[4])
+ tmp[9] = uint64(in[1])*(uint64(in[8])<<1) +
+ uint64(in[2])*(uint64(in[7])<<1) +
+ uint64(in[3])*(uint64(in[6])<<1) +
+ uint64(in[4])*(uint64(in[5])<<1)
+ tmp[10] = uint64(in[2])*(uint64(in[8])<<1) +
+ uint64(in[3])*(uint64(in[7])<<2) +
+ uint64(in[4])*(uint64(in[6])<<1) +
+ uint64(in[5])*(uint64(in[5])<<1)
+ tmp[11] = uint64(in[3])*(uint64(in[8])<<1) +
+ uint64(in[4])*(uint64(in[7])<<1) +
+ uint64(in[5])*(uint64(in[6])<<1)
+ tmp[12] = uint64(in[4])*(uint64(in[8])<<1) +
+ uint64(in[5])*(uint64(in[7])<<2) +
+ uint64(in[6])*uint64(in[6])
+ tmp[13] = uint64(in[5])*(uint64(in[8])<<1) +
+ uint64(in[6])*(uint64(in[7])<<1)
+ tmp[14] = uint64(in[6])*(uint64(in[8])<<1) +
+ uint64(in[7])*(uint64(in[7])<<1)
+ tmp[15] = uint64(in[7]) * (uint64(in[8]) << 1)
+ tmp[16] = uint64(in[8]) * uint64(in[8])
+
+ p256ReduceDegree(out, tmp)
+}
+
+// p256Mul sets out=in*in2.
+//
+// On entry: in[0,2,...] < 2**30, in[1,3,...] < 2**29 and
+// in2[0,2,...] < 2**30, in2[1,3,...] < 2**29.
+// On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29.
+func p256Mul(out, in, in2 *[p256Limbs]uint32) {
+ var tmp [17]uint64
+
+ tmp[0] = uint64(in[0]) * uint64(in2[0])
+ tmp[1] = uint64(in[0])*(uint64(in2[1])<<0) +
+ uint64(in[1])*(uint64(in2[0])<<0)
+ tmp[2] = uint64(in[0])*(uint64(in2[2])<<0) +
+ uint64(in[1])*(uint64(in2[1])<<1) +
+ uint64(in[2])*(uint64(in2[0])<<0)
+ tmp[3] = uint64(in[0])*(uint64(in2[3])<<0) +
+ uint64(in[1])*(uint64(in2[2])<<0) +
+ uint64(in[2])*(uint64(in2[1])<<0) +
+ uint64(in[3])*(uint64(in2[0])<<0)
+ tmp[4] = uint64(in[0])*(uint64(in2[4])<<0) +
+ uint64(in[1])*(uint64(in2[3])<<1) +
+ uint64(in[2])*(uint64(in2[2])<<0) +
+ uint64(in[3])*(uint64(in2[1])<<1) +
+ uint64(in[4])*(uint64(in2[0])<<0)
+ tmp[5] = uint64(in[0])*(uint64(in2[5])<<0) +
+ uint64(in[1])*(uint64(in2[4])<<0) +
+ uint64(in[2])*(uint64(in2[3])<<0) +
+ uint64(in[3])*(uint64(in2[2])<<0) +
+ uint64(in[4])*(uint64(in2[1])<<0) +
+ uint64(in[5])*(uint64(in2[0])<<0)
+ tmp[6] = uint64(in[0])*(uint64(in2[6])<<0) +
+ uint64(in[1])*(uint64(in2[5])<<1) +
+ uint64(in[2])*(uint64(in2[4])<<0) +
+ uint64(in[3])*(uint64(in2[3])<<1) +
+ uint64(in[4])*(uint64(in2[2])<<0) +
+ uint64(in[5])*(uint64(in2[1])<<1) +
+ uint64(in[6])*(uint64(in2[0])<<0)
+ tmp[7] = uint64(in[0])*(uint64(in2[7])<<0) +
+ uint64(in[1])*(uint64(in2[6])<<0) +
+ uint64(in[2])*(uint64(in2[5])<<0) +
+ uint64(in[3])*(uint64(in2[4])<<0) +
+ uint64(in[4])*(uint64(in2[3])<<0) +
+ uint64(in[5])*(uint64(in2[2])<<0) +
+ uint64(in[6])*(uint64(in2[1])<<0) +
+ uint64(in[7])*(uint64(in2[0])<<0)
+ // tmp[8] has the greatest value but doesn't overflow. See logic in
+ // p256Square.
+ tmp[8] = uint64(in[0])*(uint64(in2[8])<<0) +
+ uint64(in[1])*(uint64(in2[7])<<1) +
+ uint64(in[2])*(uint64(in2[6])<<0) +
+ uint64(in[3])*(uint64(in2[5])<<1) +
+ uint64(in[4])*(uint64(in2[4])<<0) +
+ uint64(in[5])*(uint64(in2[3])<<1) +
+ uint64(in[6])*(uint64(in2[2])<<0) +
+ uint64(in[7])*(uint64(in2[1])<<1) +
+ uint64(in[8])*(uint64(in2[0])<<0)
+ tmp[9] = uint64(in[1])*(uint64(in2[8])<<0) +
+ uint64(in[2])*(uint64(in2[7])<<0) +
+ uint64(in[3])*(uint64(in2[6])<<0) +
+ uint64(in[4])*(uint64(in2[5])<<0) +
+ uint64(in[5])*(uint64(in2[4])<<0) +
+ uint64(in[6])*(uint64(in2[3])<<0) +
+ uint64(in[7])*(uint64(in2[2])<<0) +
+ uint64(in[8])*(uint64(in2[1])<<0)
+ tmp[10] = uint64(in[2])*(uint64(in2[8])<<0) +
+ uint64(in[3])*(uint64(in2[7])<<1) +
+ uint64(in[4])*(uint64(in2[6])<<0) +
+ uint64(in[5])*(uint64(in2[5])<<1) +
+ uint64(in[6])*(uint64(in2[4])<<0) +
+ uint64(in[7])*(uint64(in2[3])<<1) +
+ uint64(in[8])*(uint64(in2[2])<<0)
+ tmp[11] = uint64(in[3])*(uint64(in2[8])<<0) +
+ uint64(in[4])*(uint64(in2[7])<<0) +
+ uint64(in[5])*(uint64(in2[6])<<0) +
+ uint64(in[6])*(uint64(in2[5])<<0) +
+ uint64(in[7])*(uint64(in2[4])<<0) +
+ uint64(in[8])*(uint64(in2[3])<<0)
+ tmp[12] = uint64(in[4])*(uint64(in2[8])<<0) +
+ uint64(in[5])*(uint64(in2[7])<<1) +
+ uint64(in[6])*(uint64(in2[6])<<0) +
+ uint64(in[7])*(uint64(in2[5])<<1) +
+ uint64(in[8])*(uint64(in2[4])<<0)
+ tmp[13] = uint64(in[5])*(uint64(in2[8])<<0) +
+ uint64(in[6])*(uint64(in2[7])<<0) +
+ uint64(in[7])*(uint64(in2[6])<<0) +
+ uint64(in[8])*(uint64(in2[5])<<0)
+ tmp[14] = uint64(in[6])*(uint64(in2[8])<<0) +
+ uint64(in[7])*(uint64(in2[7])<<1) +
+ uint64(in[8])*(uint64(in2[6])<<0)
+ tmp[15] = uint64(in[7])*(uint64(in2[8])<<0) +
+ uint64(in[8])*(uint64(in2[7])<<0)
+ tmp[16] = uint64(in[8]) * (uint64(in2[8]) << 0)
+
+ p256ReduceDegree(out, tmp)
+}
+
+func p256Assign(out, in *[p256Limbs]uint32) {
+ *out = *in
+}
+
+// p256Invert calculates |out| = |in|^{-1}
+//
+// Based on Fermat's Little Theorem:
+// a^p = a (mod p)
+// a^{p-1} = 1 (mod p)
+// a^{p-2} = a^{-1} (mod p)
+func p256Invert(out, in *[p256Limbs]uint32) {
+ var ftmp, ftmp2 [p256Limbs]uint32
+
+ // each e_I will hold |in|^{2^I - 1}
+ var e2, e4, e8, e16, e32, e64 [p256Limbs]uint32
+
+ p256Square(&ftmp, in) // 2^1
+ p256Mul(&ftmp, in, &ftmp) // 2^2 - 2^0
+ p256Assign(&e2, &ftmp)
+ p256Square(&ftmp, &ftmp) // 2^3 - 2^1
+ p256Square(&ftmp, &ftmp) // 2^4 - 2^2
+ p256Mul(&ftmp, &ftmp, &e2) // 2^4 - 2^0
+ p256Assign(&e4, &ftmp)
+ p256Square(&ftmp, &ftmp) // 2^5 - 2^1
+ p256Square(&ftmp, &ftmp) // 2^6 - 2^2
+ p256Square(&ftmp, &ftmp) // 2^7 - 2^3
+ p256Square(&ftmp, &ftmp) // 2^8 - 2^4
+ p256Mul(&ftmp, &ftmp, &e4) // 2^8 - 2^0
+ p256Assign(&e8, &ftmp)
+ for i := 0; i < 8; i++ {
+ p256Square(&ftmp, &ftmp)
+ } // 2^16 - 2^8
+ p256Mul(&ftmp, &ftmp, &e8) // 2^16 - 2^0
+ p256Assign(&e16, &ftmp)
+ for i := 0; i < 16; i++ {
+ p256Square(&ftmp, &ftmp)
+ } // 2^32 - 2^16
+ p256Mul(&ftmp, &ftmp, &e16) // 2^32 - 2^0
+ p256Assign(&e32, &ftmp)
+ for i := 0; i < 32; i++ {
+ p256Square(&ftmp, &ftmp)
+ } // 2^64 - 2^32
+ p256Assign(&e64, &ftmp)
+ p256Mul(&ftmp, &ftmp, in) // 2^64 - 2^32 + 2^0
+ for i := 0; i < 192; i++ {
+ p256Square(&ftmp, &ftmp)
+ } // 2^256 - 2^224 + 2^192
+
+ p256Mul(&ftmp2, &e64, &e32) // 2^64 - 2^0
+ for i := 0; i < 16; i++ {
+ p256Square(&ftmp2, &ftmp2)
+ } // 2^80 - 2^16
+ p256Mul(&ftmp2, &ftmp2, &e16) // 2^80 - 2^0
+ for i := 0; i < 8; i++ {
+ p256Square(&ftmp2, &ftmp2)
+ } // 2^88 - 2^8
+ p256Mul(&ftmp2, &ftmp2, &e8) // 2^88 - 2^0
+ for i := 0; i < 4; i++ {
+ p256Square(&ftmp2, &ftmp2)
+ } // 2^92 - 2^4
+ p256Mul(&ftmp2, &ftmp2, &e4) // 2^92 - 2^0
+ p256Square(&ftmp2, &ftmp2) // 2^93 - 2^1
+ p256Square(&ftmp2, &ftmp2) // 2^94 - 2^2
+ p256Mul(&ftmp2, &ftmp2, &e2) // 2^94 - 2^0
+ p256Square(&ftmp2, &ftmp2) // 2^95 - 2^1
+ p256Square(&ftmp2, &ftmp2) // 2^96 - 2^2
+ p256Mul(&ftmp2, &ftmp2, in) // 2^96 - 3
+
+ p256Mul(out, &ftmp2, &ftmp) // 2^256 - 2^224 + 2^192 + 2^96 - 3
+}
+
+// p256Scalar3 sets out=3*out.
+//
+// On entry: out[0,2,...] < 2**30, out[1,3,...] < 2**29.
+// On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29.
+func p256Scalar3(out *[p256Limbs]uint32) {
+ var carry uint32
+
+ for i := 0; ; i++ {
+ out[i] *= 3
+ out[i] += carry
+ carry = out[i] >> 29
+ out[i] &= bottom29Bits
+
+ i++
+ if i == p256Limbs {
+ break
+ }
+
+ out[i] *= 3
+ out[i] += carry
+ carry = out[i] >> 28
+ out[i] &= bottom28Bits
+ }
+
+ p256ReduceCarry(out, carry)
+}
+
+// p256Scalar4 sets out=4*out.
+//
+// On entry: out[0,2,...] < 2**30, out[1,3,...] < 2**29.
+// On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29.
+func p256Scalar4(out *[p256Limbs]uint32) {
+ var carry, nextCarry uint32
+
+ for i := 0; ; i++ {
+ nextCarry = out[i] >> 27
+ out[i] <<= 2
+ out[i] &= bottom29Bits
+ out[i] += carry
+ carry = nextCarry + (out[i] >> 29)
+ out[i] &= bottom29Bits
+
+ i++
+ if i == p256Limbs {
+ break
+ }
+ nextCarry = out[i] >> 26
+ out[i] <<= 2
+ out[i] &= bottom28Bits
+ out[i] += carry
+ carry = nextCarry + (out[i] >> 28)
+ out[i] &= bottom28Bits
+ }
+
+ p256ReduceCarry(out, carry)
+}
+
+// p256Scalar8 sets out=8*out.
+//
+// On entry: out[0,2,...] < 2**30, out[1,3,...] < 2**29.
+// On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29.
+func p256Scalar8(out *[p256Limbs]uint32) {
+ var carry, nextCarry uint32
+
+ for i := 0; ; i++ {
+ nextCarry = out[i] >> 26
+ out[i] <<= 3
+ out[i] &= bottom29Bits
+ out[i] += carry
+ carry = nextCarry + (out[i] >> 29)
+ out[i] &= bottom29Bits
+
+ i++
+ if i == p256Limbs {
+ break
+ }
+ nextCarry = out[i] >> 25
+ out[i] <<= 3
+ out[i] &= bottom28Bits
+ out[i] += carry
+ carry = nextCarry + (out[i] >> 28)
+ out[i] &= bottom28Bits
+ }
+
+ p256ReduceCarry(out, carry)
+}
+
+// Group operations:
+//
+// Elements of the elliptic curve group are represented in Jacobian
+// coordinates: (x, y, z). An affine point (x', y') is x'=x/z**2, y'=y/z**3 in
+// Jacobian form.
+
+// p256PointDouble sets {xOut,yOut,zOut} = 2*{x,y,z}.
+//
+// See http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l
+func p256PointDouble(xOut, yOut, zOut, x, y, z *[p256Limbs]uint32) {
+ var delta, gamma, alpha, beta, tmp, tmp2 [p256Limbs]uint32
+
+ p256Square(&delta, z)
+ p256Square(&gamma, y)
+ p256Mul(&beta, x, &gamma)
+
+ p256Sum(&tmp, x, &delta)
+ p256Diff(&tmp2, x, &delta)
+ p256Mul(&alpha, &tmp, &tmp2)
+ p256Scalar3(&alpha)
+
+ p256Sum(&tmp, y, z)
+ p256Square(&tmp, &tmp)
+ p256Diff(&tmp, &tmp, &gamma)
+ p256Diff(zOut, &tmp, &delta)
+
+ p256Scalar4(&beta)
+ p256Square(xOut, &alpha)
+ p256Diff(xOut, xOut, &beta)
+ p256Diff(xOut, xOut, &beta)
+
+ p256Diff(&tmp, &beta, xOut)
+ p256Mul(&tmp, &alpha, &tmp)
+ p256Square(&tmp2, &gamma)
+ p256Scalar8(&tmp2)
+ p256Diff(yOut, &tmp, &tmp2)
+}
+
+// p256PointAddMixed sets {xOut,yOut,zOut} = {x1,y1,z1} + {x2,y2,1}.
+// (i.e. the second point is affine.)
+//
+// See http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl
+//
+// Note that this function does not handle P+P, infinity+P nor P+infinity
+// correctly.
+func p256PointAddMixed(xOut, yOut, zOut, x1, y1, z1, x2, y2 *[p256Limbs]uint32) {
+ var z1z1, z1z1z1, s2, u2, h, i, j, r, rr, v, tmp [p256Limbs]uint32
+
+ p256Square(&z1z1, z1)
+ p256Sum(&tmp, z1, z1)
+
+ p256Mul(&u2, x2, &z1z1)
+ p256Mul(&z1z1z1, z1, &z1z1)
+ p256Mul(&s2, y2, &z1z1z1)
+ p256Diff(&h, &u2, x1)
+ p256Sum(&i, &h, &h)
+ p256Square(&i, &i)
+ p256Mul(&j, &h, &i)
+ p256Diff(&r, &s2, y1)
+ p256Sum(&r, &r, &r)
+ p256Mul(&v, x1, &i)
+
+ p256Mul(zOut, &tmp, &h)
+ p256Square(&rr, &r)
+ p256Diff(xOut, &rr, &j)
+ p256Diff(xOut, xOut, &v)
+ p256Diff(xOut, xOut, &v)
+
+ p256Diff(&tmp, &v, xOut)
+ p256Mul(yOut, &tmp, &r)
+ p256Mul(&tmp, y1, &j)
+ p256Diff(yOut, yOut, &tmp)
+ p256Diff(yOut, yOut, &tmp)
+}
+
+// p256PointAdd sets {xOut,yOut,zOut} = {x1,y1,z1} + {x2,y2,z2}.
+//
+// See http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl
+//
+// Note that this function does not handle P+P, infinity+P nor P+infinity
+// correctly.
+func p256PointAdd(xOut, yOut, zOut, x1, y1, z1, x2, y2, z2 *[p256Limbs]uint32) {
+ var z1z1, z1z1z1, z2z2, z2z2z2, s1, s2, u1, u2, h, i, j, r, rr, v, tmp [p256Limbs]uint32
+
+ p256Square(&z1z1, z1)
+ p256Square(&z2z2, z2)
+ p256Mul(&u1, x1, &z2z2)
+
+ p256Sum(&tmp, z1, z2)
+ p256Square(&tmp, &tmp)
+ p256Diff(&tmp, &tmp, &z1z1)
+ p256Diff(&tmp, &tmp, &z2z2)
+
+ p256Mul(&z2z2z2, z2, &z2z2)
+ p256Mul(&s1, y1, &z2z2z2)
+
+ p256Mul(&u2, x2, &z1z1)
+ p256Mul(&z1z1z1, z1, &z1z1)
+ p256Mul(&s2, y2, &z1z1z1)
+ p256Diff(&h, &u2, &u1)
+ p256Sum(&i, &h, &h)
+ p256Square(&i, &i)
+ p256Mul(&j, &h, &i)
+ p256Diff(&r, &s2, &s1)
+ p256Sum(&r, &r, &r)
+ p256Mul(&v, &u1, &i)
+
+ p256Mul(zOut, &tmp, &h)
+ p256Square(&rr, &r)
+ p256Diff(xOut, &rr, &j)
+ p256Diff(xOut, xOut, &v)
+ p256Diff(xOut, xOut, &v)
+
+ p256Diff(&tmp, &v, xOut)
+ p256Mul(yOut, &tmp, &r)
+ p256Mul(&tmp, &s1, &j)
+ p256Diff(yOut, yOut, &tmp)
+ p256Diff(yOut, yOut, &tmp)
+}
+
+// p256CopyConditional sets out=in if mask = 0xffffffff in constant time.
+//
+// On entry: mask is either 0 or 0xffffffff.
+func p256CopyConditional(out, in *[p256Limbs]uint32, mask uint32) {
+ for i := 0; i < p256Limbs; i++ {
+ tmp := mask & (in[i] ^ out[i])
+ out[i] ^= tmp
+ }
+}
+
+// p256SelectAffinePoint sets {out_x,out_y} to the index'th entry of table.
+// On entry: index < 16, table[0] must be zero.
+func p256SelectAffinePoint(xOut, yOut *[p256Limbs]uint32, table []uint32, index uint32) {
+ for i := range xOut {
+ xOut[i] = 0
+ }
+ for i := range yOut {
+ yOut[i] = 0
+ }
+
+ for i := uint32(1); i < 16; i++ {
+ mask := i ^ index
+ mask |= mask >> 2
+ mask |= mask >> 1
+ mask &= 1
+ mask--
+ for j := range xOut {
+ xOut[j] |= table[0] & mask
+ table = table[1:]
+ }
+ for j := range yOut {
+ yOut[j] |= table[0] & mask
+ table = table[1:]
+ }
+ }
+}
+
+// p256SelectJacobianPoint sets {out_x,out_y,out_z} to the index'th entry of
+// table.
+// On entry: index < 16, table[0] must be zero.
+func p256SelectJacobianPoint(xOut, yOut, zOut *[p256Limbs]uint32, table *[16][3][p256Limbs]uint32, index uint32) {
+ for i := range xOut {
+ xOut[i] = 0
+ }
+ for i := range yOut {
+ yOut[i] = 0
+ }
+ for i := range zOut {
+ zOut[i] = 0
+ }
+
+ // The implicit value at index 0 is all zero. We don't need to perform that
+ // iteration of the loop because we already set out_* to zero.
+ for i := uint32(1); i < 16; i++ {
+ mask := i ^ index
+ mask |= mask >> 2
+ mask |= mask >> 1
+ mask &= 1
+ mask--
+ for j := range xOut {
+ xOut[j] |= table[i][0][j] & mask
+ }
+ for j := range yOut {
+ yOut[j] |= table[i][1][j] & mask
+ }
+ for j := range zOut {
+ zOut[j] |= table[i][2][j] & mask
+ }
+ }
+}
+
+// p256GetBit returns the bit'th bit of scalar.
+func p256GetBit(scalar *[32]uint8, bit uint) uint32 {
+ return uint32(((scalar[bit>>3]) >> (bit & 7)) & 1)
+}
+
+// p256ScalarBaseMult sets {xOut,yOut,zOut} = scalar*G where scalar is a
+// little-endian number. Note that the value of scalar must be less than the
+// order of the group.
+func p256ScalarBaseMult(xOut, yOut, zOut *[p256Limbs]uint32, scalar *[32]uint8) {
+ nIsInfinityMask := ^uint32(0)
+ var pIsNoninfiniteMask, mask, tableOffset uint32
+ var px, py, tx, ty, tz [p256Limbs]uint32
+
+ for i := range xOut {
+ xOut[i] = 0
+ }
+ for i := range yOut {
+ yOut[i] = 0
+ }
+ for i := range zOut {
+ zOut[i] = 0
+ }
+
+ // The loop adds bits at positions 0, 64, 128 and 192, followed by
+ // positions 32,96,160 and 224 and does this 32 times.
+ for i := uint(0); i < 32; i++ {
+ if i != 0 {
+ p256PointDouble(xOut, yOut, zOut, xOut, yOut, zOut)
+ }
+ tableOffset = 0
+ for j := uint(0); j <= 32; j += 32 {
+ bit0 := p256GetBit(scalar, 31-i+j)
+ bit1 := p256GetBit(scalar, 95-i+j)
+ bit2 := p256GetBit(scalar, 159-i+j)
+ bit3 := p256GetBit(scalar, 223-i+j)
+ index := bit0 | (bit1 << 1) | (bit2 << 2) | (bit3 << 3)
+
+ p256SelectAffinePoint(&px, &py, p256Precomputed[tableOffset:], index)
+ tableOffset += 30 * p256Limbs
+
+ // Since scalar is less than the order of the group, we know that
+ // {xOut,yOut,zOut} != {px,py,1}, unless both are zero, which we handle
+ // below.
+ p256PointAddMixed(&tx, &ty, &tz, xOut, yOut, zOut, &px, &py)
+ // The result of pointAddMixed is incorrect if {xOut,yOut,zOut} is zero
+ // (a.k.a. the point at infinity). We handle that situation by
+ // copying the point from the table.
+ p256CopyConditional(xOut, &px, nIsInfinityMask)
+ p256CopyConditional(yOut, &py, nIsInfinityMask)
+ p256CopyConditional(zOut, &p256One, nIsInfinityMask)
+
+ // Equally, the result is also wrong if the point from the table is
+ // zero, which happens when the index is zero. We handle that by
+ // only copying from {tx,ty,tz} to {xOut,yOut,zOut} if index != 0.
+ pIsNoninfiniteMask = nonZeroToAllOnes(index)
+ mask = pIsNoninfiniteMask & ^nIsInfinityMask
+ p256CopyConditional(xOut, &tx, mask)
+ p256CopyConditional(yOut, &ty, mask)
+ p256CopyConditional(zOut, &tz, mask)
+ // If p was not zero, then n is now non-zero.
+ nIsInfinityMask &= ^pIsNoninfiniteMask
+ }
+ }
+}
+
+// p256PointToAffine converts a Jacobian point to an affine point. If the input
+// is the point at infinity then it returns (0, 0) in constant time.
+func p256PointToAffine(xOut, yOut, x, y, z *[p256Limbs]uint32) {
+ var zInv, zInvSq [p256Limbs]uint32
+
+ p256Invert(&zInv, z)
+ p256Square(&zInvSq, &zInv)
+ p256Mul(xOut, x, &zInvSq)
+ p256Mul(&zInv, &zInv, &zInvSq)
+ p256Mul(yOut, y, &zInv)
+}
+
+// p256ToAffine returns a pair of *big.Int containing the affine representation
+// of {x,y,z}.
+func p256ToAffine(x, y, z *[p256Limbs]uint32) (xOut, yOut *big.Int) {
+ var xx, yy [p256Limbs]uint32
+ p256PointToAffine(&xx, &yy, x, y, z)
+ return p256ToBig(&xx), p256ToBig(&yy)
+}
+
+// p256ScalarMult sets {xOut,yOut,zOut} = scalar*{x,y}.
+func p256ScalarMult(xOut, yOut, zOut, x, y *[p256Limbs]uint32, scalar *[32]uint8) {
+ var px, py, pz, tx, ty, tz [p256Limbs]uint32
+ var precomp [16][3][p256Limbs]uint32
+ var nIsInfinityMask, index, pIsNoninfiniteMask, mask uint32
+
+ // We precompute 0,1,2,... times {x,y}.
+ precomp[1][0] = *x
+ precomp[1][1] = *y
+ precomp[1][2] = p256One
+
+ for i := 2; i < 16; i += 2 {
+ p256PointDouble(&precomp[i][0], &precomp[i][1], &precomp[i][2], &precomp[i/2][0], &precomp[i/2][1], &precomp[i/2][2])
+ p256PointAddMixed(&precomp[i+1][0], &precomp[i+1][1], &precomp[i+1][2], &precomp[i][0], &precomp[i][1], &precomp[i][2], x, y)
+ }
+
+ for i := range xOut {
+ xOut[i] = 0
+ }
+ for i := range yOut {
+ yOut[i] = 0
+ }
+ for i := range zOut {
+ zOut[i] = 0
+ }
+ nIsInfinityMask = ^uint32(0)
+
+ // We add in a window of four bits each iteration and do this 64 times.
+ for i := 0; i < 64; i++ {
+ if i != 0 {
+ p256PointDouble(xOut, yOut, zOut, xOut, yOut, zOut)
+ p256PointDouble(xOut, yOut, zOut, xOut, yOut, zOut)
+ p256PointDouble(xOut, yOut, zOut, xOut, yOut, zOut)
+ p256PointDouble(xOut, yOut, zOut, xOut, yOut, zOut)
+ }
+
+ index = uint32(scalar[31-i/2])
+ if (i & 1) == 1 {
+ index &= 15
+ } else {
+ index >>= 4
+ }
+
+ // See the comments in scalarBaseMult about handling infinities.
+ p256SelectJacobianPoint(&px, &py, &pz, &precomp, index)
+ p256PointAdd(&tx, &ty, &tz, xOut, yOut, zOut, &px, &py, &pz)
+ p256CopyConditional(xOut, &px, nIsInfinityMask)
+ p256CopyConditional(yOut, &py, nIsInfinityMask)
+ p256CopyConditional(zOut, &pz, nIsInfinityMask)
+
+ pIsNoninfiniteMask = nonZeroToAllOnes(index)
+ mask = pIsNoninfiniteMask & ^nIsInfinityMask
+ p256CopyConditional(xOut, &tx, mask)
+ p256CopyConditional(yOut, &ty, mask)
+ p256CopyConditional(zOut, &tz, mask)
+ nIsInfinityMask &= ^pIsNoninfiniteMask
+ }
+}
+
+// p256FromBig sets out = R*in.
+func p256FromBig(out *[p256Limbs]uint32, in *big.Int) {
+ tmp := new(big.Int).Lsh(in, 257)
+ tmp.Mod(tmp, p256.P)
+
+ for i := 0; i < p256Limbs; i++ {
+ if bits := tmp.Bits(); len(bits) > 0 {
+ out[i] = uint32(bits[0]) & bottom29Bits
+ } else {
+ out[i] = 0
+ }
+ tmp.Rsh(tmp, 29)
+
+ i++
+ if i == p256Limbs {
+ break
+ }
+
+ if bits := tmp.Bits(); len(bits) > 0 {
+ out[i] = uint32(bits[0]) & bottom28Bits
+ } else {
+ out[i] = 0
+ }
+ tmp.Rsh(tmp, 28)
+ }
+}
+
+// p256ToBig returns a *big.Int containing the value of in.
+func p256ToBig(in *[p256Limbs]uint32) *big.Int {
+ result, tmp := new(big.Int), new(big.Int)
+
+ result.SetInt64(int64(in[p256Limbs-1]))
+ for i := p256Limbs - 2; i >= 0; i-- {
+ if (i & 1) == 0 {
+ result.Lsh(result, 29)
+ } else {
+ result.Lsh(result, 28)
+ }
+ tmp.SetInt64(int64(in[i]))
+ result.Add(result, tmp)
+ }
+
+ result.Mul(result, p256RInverse)
+ result.Mod(result, p256.P)
+ return result
+}
diff --git a/src/pkg/crypto/md5/example_test.go b/src/pkg/crypto/md5/example_test.go
new file mode 100644
index 000000000..28be770a7
--- /dev/null
+++ b/src/pkg/crypto/md5/example_test.go
@@ -0,0 +1,19 @@
+// Copyright 2013 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 md5_test
+
+import (
+ "crypto/md5"
+ "fmt"
+ "io"
+)
+
+func ExampleNew() {
+ h := md5.New()
+ io.WriteString(h, "The fog is getting thicker!")
+ io.WriteString(h, "And Leon's getting laaarger!")
+ fmt.Printf("%x", h.Sum(nil))
+ // Output: e2c569be17396eca2a2e3c11578123ed
+}
diff --git a/src/pkg/crypto/md5/gen.go b/src/pkg/crypto/md5/gen.go
index 275b4aeea..ccaa7c13d 100644
--- a/src/pkg/crypto/md5/gen.go
+++ b/src/pkg/crypto/md5/gen.go
@@ -164,7 +164,7 @@ var program = `
// DO NOT EDIT.
// Generate with: go run gen.go{{if .Full}} -full{{end}} | gofmt >md5block.go
-// +build !amd64
+// +build !amd64,!386,!arm
package md5
diff --git a/src/pkg/crypto/md5/md5.go b/src/pkg/crypto/md5/md5.go
index 825e5c8a2..1a1f35fab 100644
--- a/src/pkg/crypto/md5/md5.go
+++ b/src/pkg/crypto/md5/md5.go
@@ -88,7 +88,11 @@ func (d *digest) Write(p []byte) (nn int, err error) {
func (d0 *digest) Sum(in []byte) []byte {
// Make a copy of d0 so that caller can keep writing and summing.
d := *d0
+ hash := d.checkSum()
+ return append(in, hash[:]...)
+}
+func (d *digest) checkSum() [Size]byte {
// Padding. Add a 1 bit and 0 bits until 56 bytes mod 64.
len := d.len
var tmp [64]byte
@@ -118,5 +122,13 @@ func (d0 *digest) Sum(in []byte) []byte {
digest[i*4+3] = byte(s >> 24)
}
- return append(in, digest[:]...)
+ return digest
+}
+
+// Sum returns the MD5 checksum of the data.
+func Sum(data []byte) [Size]byte {
+ var d digest
+ d.Reset()
+ d.Write(data)
+ return d.checkSum()
}
diff --git a/src/pkg/crypto/md5/md5_test.go b/src/pkg/crypto/md5/md5_test.go
index 3ef4519b9..a8b7a1a52 100644
--- a/src/pkg/crypto/md5/md5_test.go
+++ b/src/pkg/crypto/md5/md5_test.go
@@ -53,6 +53,10 @@ var golden = []md5Test{
func TestGolden(t *testing.T) {
for i := 0; i < len(golden); i++ {
g := golden[i]
+ s := fmt.Sprintf("%x", Sum([]byte(g.in)))
+ if s != g.out {
+ t.Fatalf("Sum function: md5(%s) = %s want %s", g.in, s, g.out)
+ }
c := New()
buf := make([]byte, len(g.in)+4)
for j := 0; j < 3+4; j++ {
@@ -77,12 +81,28 @@ func TestGolden(t *testing.T) {
}
}
-func ExampleNew() {
- h := New()
- io.WriteString(h, "The fog is getting thicker!")
- io.WriteString(h, "And Leon's getting laaarger!")
- fmt.Printf("%x", h.Sum(nil))
- // Output: e2c569be17396eca2a2e3c11578123ed
+func TestLarge(t *testing.T) {
+ const N = 10000
+ ok := "2bb571599a4180e1d542f76904adc3df" // md5sum of "0123456789" * 1000
+ block := make([]byte, 10004)
+ c := New()
+ for offset := 0; offset < 4; offset++ {
+ for i := 0; i < N; i++ {
+ block[offset+i] = '0' + byte(i%10)
+ }
+ for blockSize := 10; blockSize <= N; blockSize *= 10 {
+ blocks := N / blockSize
+ b := block[offset : offset+blockSize]
+ c.Reset()
+ for i := 0; i < blocks; i++ {
+ c.Write(b)
+ }
+ s := fmt.Sprintf("%x", c.Sum(nil))
+ if s != ok {
+ t.Fatalf("md5 TestLarge offset=%d, blockSize=%d = %s want %s", offset, blockSize, s, ok)
+ }
+ }
+ }
}
var bench = New()
diff --git a/src/pkg/crypto/md5/md5block.go b/src/pkg/crypto/md5/md5block.go
index a376fbee9..3e739e36f 100644
--- a/src/pkg/crypto/md5/md5block.go
+++ b/src/pkg/crypto/md5/md5block.go
@@ -1,7 +1,7 @@
// DO NOT EDIT.
// Generate with: go run gen.go -full | gofmt >md5block.go
-// +build !amd64,!386
+// +build !amd64,!386,!arm
package md5
diff --git a/src/pkg/crypto/md5/md5block_386.s b/src/pkg/crypto/md5/md5block_386.s
index 3ce15e37f..e5c27ac9a 100644
--- a/src/pkg/crypto/md5/md5block_386.s
+++ b/src/pkg/crypto/md5/md5block_386.s
@@ -6,6 +6,8 @@
// #defines generating 8a assembly, and adjusted for 386,
// by the Go Authors.
+#include "../../../cmd/ld/textflag.h"
+
// MD5 optimized for AMD64.
//
// Author: Marc Bevand <bevand_m (at) epita.fr>
@@ -57,7 +59,7 @@
XORL c, BP; \
ADDL b, a
-TEXT ·block(SB),7,$24-16
+TEXT ·block(SB),NOSPLIT,$24-16
MOVL dig+0(FP), BP
MOVL p+4(FP), SI
MOVL p_len+8(FP), DX
diff --git a/src/pkg/crypto/md5/md5block_amd64.s b/src/pkg/crypto/md5/md5block_amd64.s
index e6420a28a..178e49cd8 100644
--- a/src/pkg/crypto/md5/md5block_amd64.s
+++ b/src/pkg/crypto/md5/md5block_amd64.s
@@ -5,13 +5,15 @@
// Translated from Perl generating GNU assembly into
// #defines generating 6a assembly by the Go Authors.
+#include "../../../cmd/ld/textflag.h"
+
// MD5 optimized for AMD64.
//
// Author: Marc Bevand <bevand_m (at) epita.fr>
// Licence: I hereby disclaim the copyright on this code and place it
// in the public domain.
-TEXT ·block(SB),7,$0-32
+TEXT ·block(SB),NOSPLIT,$0-32
MOVQ dig+0(FP), BP
MOVQ p+8(FP), SI
MOVQ p_len+16(FP), DX
diff --git a/src/pkg/crypto/md5/md5block_arm.s b/src/pkg/crypto/md5/md5block_arm.s
new file mode 100644
index 000000000..e644bfcd6
--- /dev/null
+++ b/src/pkg/crypto/md5/md5block_arm.s
@@ -0,0 +1,299 @@
+// Copyright 2013 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.
+//
+// ARM version of md5block.go
+
+#include "../../../cmd/ld/textflag.h"
+
+// Register definitions
+table = 0 // Pointer to MD5 constants table
+data = 1 // Pointer to data to hash
+a = 2 // MD5 accumulator
+b = 3 // MD5 accumulator
+c = 4 // MD5 accumulator
+d = 5 // MD5 accumulator
+c0 = 6 // MD5 constant
+c1 = 7 // MD5 constant
+c2 = 8 // MD5 constant
+// r9, r10 are forbidden
+// r11 is OK provided you check the assembler that no synthetic instructions use it
+c3 = 11 // MD5 constant
+t0 = 12 // temporary
+t1 = 14 // temporary
+
+// func block(dig *digest, p []byte)
+// 0(FP) is *digest
+// 4(FP) is p.array (struct Slice)
+// 8(FP) is p.len
+//12(FP) is p.cap
+//
+// Stack frame
+p_end = -4 // -4(SP) pointer to the end of data
+p_data = -8 // -8(SP) current data pointer
+buf = -8-4*16 //-72(SP) 16 words temporary buffer
+ // 3 words at 4..12(R13) for called routine parameters
+
+TEXT ·block(SB), NOSPLIT, $84-16
+ MOVW p+4(FP), R(data) // pointer to the data
+ MOVW p_len+8(FP), R(t0) // number of bytes
+ ADD R(data), R(t0)
+ MOVW R(t0), p_end(SP) // pointer to end of data
+
+loop:
+ MOVW R(data), p_data(SP) // Save R(data)
+ AND.S $3, R(data), R(t0) // TST $3, R(data) not working see issue 5921
+ BEQ aligned // aligned detected - skip copy
+
+ // Copy the unaligned source data into the aligned temporary buffer
+ // memove(to=4(R13), from=8(R13), n=12(R13)) - Corrupts all registers
+ MOVW $buf(SP), R(table) // to
+ MOVW $64, R(c0) // n
+ MOVM.IB [R(table),R(data),R(c0)], (R13)
+ BL runtime·memmove(SB)
+
+ // Point to the local aligned copy of the data
+ MOVW $buf(SP), R(data)
+
+aligned:
+ // Point to the table of constants
+ // A PC relative add would be cheaper than this
+ MOVW $·table(SB), R(table)
+
+ // Load up initial MD5 accumulator
+ MOVW dig+0(FP), R(c0)
+ MOVM.IA (R(c0)), [R(a),R(b),R(c),R(d)]
+
+// a += (((c^d)&b)^d) + X[index] + const
+// a = a<<shift | a>>(32-shift) + b
+#define ROUND1(a, b, c, d, index, shift, const) \
+ EOR R(c), R(d), R(t0) ; \
+ AND R(b), R(t0) ; \
+ EOR R(d), R(t0) ; \
+ MOVW (index<<2)(R(data)), R(t1) ; \
+ ADD R(t1), R(t0) ; \
+ ADD R(const), R(t0) ; \
+ ADD R(t0), R(a) ; \
+ ADD R(a)@>(32-shift), R(b), R(a) ;
+
+ MOVM.IA.W (R(table)), [R(c0),R(c1),R(c2),R(c3)]
+ ROUND1(a, b, c, d, 0, 7, c0)
+ ROUND1(d, a, b, c, 1, 12, c1)
+ ROUND1(c, d, a, b, 2, 17, c2)
+ ROUND1(b, c, d, a, 3, 22, c3)
+
+ MOVM.IA.W (R(table)), [R(c0),R(c1),R(c2),R(c3)]
+ ROUND1(a, b, c, d, 4, 7, c0)
+ ROUND1(d, a, b, c, 5, 12, c1)
+ ROUND1(c, d, a, b, 6, 17, c2)
+ ROUND1(b, c, d, a, 7, 22, c3)
+
+ MOVM.IA.W (R(table)), [R(c0),R(c1),R(c2),R(c3)]
+ ROUND1(a, b, c, d, 8, 7, c0)
+ ROUND1(d, a, b, c, 9, 12, c1)
+ ROUND1(c, d, a, b, 10, 17, c2)
+ ROUND1(b, c, d, a, 11, 22, c3)
+
+ MOVM.IA.W (R(table)), [R(c0),R(c1),R(c2),R(c3)]
+ ROUND1(a, b, c, d, 12, 7, c0)
+ ROUND1(d, a, b, c, 13, 12, c1)
+ ROUND1(c, d, a, b, 14, 17, c2)
+ ROUND1(b, c, d, a, 15, 22, c3)
+
+// a += (((b^c)&d)^c) + X[index] + const
+// a = a<<shift | a>>(32-shift) + b
+#define ROUND2(a, b, c, d, index, shift, const) \
+ EOR R(b), R(c), R(t0) ; \
+ AND R(d), R(t0) ; \
+ EOR R(c), R(t0) ; \
+ MOVW (index<<2)(R(data)), R(t1) ; \
+ ADD R(t1), R(t0) ; \
+ ADD R(const), R(t0) ; \
+ ADD R(t0), R(a) ; \
+ ADD R(a)@>(32-shift), R(b), R(a) ;
+
+ MOVM.IA.W (R(table)), [R(c0),R(c1),R(c2),R(c3)]
+ ROUND2(a, b, c, d, 1, 5, c0)
+ ROUND2(d, a, b, c, 6, 9, c1)
+ ROUND2(c, d, a, b, 11, 14, c2)
+ ROUND2(b, c, d, a, 0, 20, c3)
+
+ MOVM.IA.W (R(table)), [R(c0),R(c1),R(c2),R(c3)]
+ ROUND2(a, b, c, d, 5, 5, c0)
+ ROUND2(d, a, b, c, 10, 9, c1)
+ ROUND2(c, d, a, b, 15, 14, c2)
+ ROUND2(b, c, d, a, 4, 20, c3)
+
+ MOVM.IA.W (R(table)), [R(c0),R(c1),R(c2),R(c3)]
+ ROUND2(a, b, c, d, 9, 5, c0)
+ ROUND2(d, a, b, c, 14, 9, c1)
+ ROUND2(c, d, a, b, 3, 14, c2)
+ ROUND2(b, c, d, a, 8, 20, c3)
+
+ MOVM.IA.W (R(table)), [R(c0),R(c1),R(c2),R(c3)]
+ ROUND2(a, b, c, d, 13, 5, c0)
+ ROUND2(d, a, b, c, 2, 9, c1)
+ ROUND2(c, d, a, b, 7, 14, c2)
+ ROUND2(b, c, d, a, 12, 20, c3)
+
+// a += (b^c^d) + X[index] + const
+// a = a<<shift | a>>(32-shift) + b
+#define ROUND3(a, b, c, d, index, shift, const) \
+ EOR R(b), R(c), R(t0) ; \
+ EOR R(d), R(t0) ; \
+ MOVW (index<<2)(R(data)), R(t1) ; \
+ ADD R(t1), R(t0) ; \
+ ADD R(const), R(t0) ; \
+ ADD R(t0), R(a) ; \
+ ADD R(a)@>(32-shift), R(b), R(a) ;
+
+ MOVM.IA.W (R(table)), [R(c0),R(c1),R(c2),R(c3)]
+ ROUND3(a, b, c, d, 5, 4, c0)
+ ROUND3(d, a, b, c, 8, 11, c1)
+ ROUND3(c, d, a, b, 11, 16, c2)
+ ROUND3(b, c, d, a, 14, 23, c3)
+
+ MOVM.IA.W (R(table)), [R(c0),R(c1),R(c2),R(c3)]
+ ROUND3(a, b, c, d, 1, 4, c0)
+ ROUND3(d, a, b, c, 4, 11, c1)
+ ROUND3(c, d, a, b, 7, 16, c2)
+ ROUND3(b, c, d, a, 10, 23, c3)
+
+ MOVM.IA.W (R(table)), [R(c0),R(c1),R(c2),R(c3)]
+ ROUND3(a, b, c, d, 13, 4, c0)
+ ROUND3(d, a, b, c, 0, 11, c1)
+ ROUND3(c, d, a, b, 3, 16, c2)
+ ROUND3(b, c, d, a, 6, 23, c3)
+
+ MOVM.IA.W (R(table)), [R(c0),R(c1),R(c2),R(c3)]
+ ROUND3(a, b, c, d, 9, 4, c0)
+ ROUND3(d, a, b, c, 12, 11, c1)
+ ROUND3(c, d, a, b, 15, 16, c2)
+ ROUND3(b, c, d, a, 2, 23, c3)
+
+// a += (c^(b|^d)) + X[index] + const
+// a = a<<shift | a>>(32-shift) + b
+#define ROUND4(a, b, c, d, index, shift, const) \
+ MVN R(d), R(t0) ; \
+ ORR R(b), R(t0) ; \
+ EOR R(c), R(t0) ; \
+ MOVW (index<<2)(R(data)), R(t1) ; \
+ ADD R(t1), R(t0) ; \
+ ADD R(const), R(t0) ; \
+ ADD R(t0), R(a) ; \
+ ADD R(a)@>(32-shift), R(b), R(a) ;
+
+ MOVM.IA.W (R(table)), [R(c0),R(c1),R(c2),R(c3)]
+ ROUND4(a, b, c, d, 0, 6, c0)
+ ROUND4(d, a, b, c, 7, 10, c1)
+ ROUND4(c, d, a, b, 14, 15, c2)
+ ROUND4(b, c, d, a, 5, 21, c3)
+
+ MOVM.IA.W (R(table)), [R(c0),R(c1),R(c2),R(c3)]
+ ROUND4(a, b, c, d, 12, 6, c0)
+ ROUND4(d, a, b, c, 3, 10, c1)
+ ROUND4(c, d, a, b, 10, 15, c2)
+ ROUND4(b, c, d, a, 1, 21, c3)
+
+ MOVM.IA.W (R(table)), [R(c0),R(c1),R(c2),R(c3)]
+ ROUND4(a, b, c, d, 8, 6, c0)
+ ROUND4(d, a, b, c, 15, 10, c1)
+ ROUND4(c, d, a, b, 6, 15, c2)
+ ROUND4(b, c, d, a, 13, 21, c3)
+
+ MOVM.IA.W (R(table)), [R(c0),R(c1),R(c2),R(c3)]
+ ROUND4(a, b, c, d, 4, 6, c0)
+ ROUND4(d, a, b, c, 11, 10, c1)
+ ROUND4(c, d, a, b, 2, 15, c2)
+ ROUND4(b, c, d, a, 9, 21, c3)
+
+ MOVW dig+0(FP), R(t0)
+ MOVM.IA (R(t0)), [R(c0),R(c1),R(c2),R(c3)]
+
+ ADD R(c0), R(a)
+ ADD R(c1), R(b)
+ ADD R(c2), R(c)
+ ADD R(c3), R(d)
+
+ MOVM.IA [R(a),R(b),R(c),R(d)], (R(t0))
+
+ MOVW p_data(SP), R(data)
+ MOVW p_end(SP), R(t0)
+ ADD $64, R(data)
+ CMP R(t0), R(data)
+ BLO loop
+
+ RET
+
+// MD5 constants table
+
+ // Round 1
+ DATA ·table+0x00(SB)/4, $0xd76aa478
+ DATA ·table+0x04(SB)/4, $0xe8c7b756
+ DATA ·table+0x08(SB)/4, $0x242070db
+ DATA ·table+0x0c(SB)/4, $0xc1bdceee
+ DATA ·table+0x10(SB)/4, $0xf57c0faf
+ DATA ·table+0x14(SB)/4, $0x4787c62a
+ DATA ·table+0x18(SB)/4, $0xa8304613
+ DATA ·table+0x1c(SB)/4, $0xfd469501
+ DATA ·table+0x20(SB)/4, $0x698098d8
+ DATA ·table+0x24(SB)/4, $0x8b44f7af
+ DATA ·table+0x28(SB)/4, $0xffff5bb1
+ DATA ·table+0x2c(SB)/4, $0x895cd7be
+ DATA ·table+0x30(SB)/4, $0x6b901122
+ DATA ·table+0x34(SB)/4, $0xfd987193
+ DATA ·table+0x38(SB)/4, $0xa679438e
+ DATA ·table+0x3c(SB)/4, $0x49b40821
+ // Round 2
+ DATA ·table+0x40(SB)/4, $0xf61e2562
+ DATA ·table+0x44(SB)/4, $0xc040b340
+ DATA ·table+0x48(SB)/4, $0x265e5a51
+ DATA ·table+0x4c(SB)/4, $0xe9b6c7aa
+ DATA ·table+0x50(SB)/4, $0xd62f105d
+ DATA ·table+0x54(SB)/4, $0x02441453
+ DATA ·table+0x58(SB)/4, $0xd8a1e681
+ DATA ·table+0x5c(SB)/4, $0xe7d3fbc8
+ DATA ·table+0x60(SB)/4, $0x21e1cde6
+ DATA ·table+0x64(SB)/4, $0xc33707d6
+ DATA ·table+0x68(SB)/4, $0xf4d50d87
+ DATA ·table+0x6c(SB)/4, $0x455a14ed
+ DATA ·table+0x70(SB)/4, $0xa9e3e905
+ DATA ·table+0x74(SB)/4, $0xfcefa3f8
+ DATA ·table+0x78(SB)/4, $0x676f02d9
+ DATA ·table+0x7c(SB)/4, $0x8d2a4c8a
+ // Round 3
+ DATA ·table+0x80(SB)/4, $0xfffa3942
+ DATA ·table+0x84(SB)/4, $0x8771f681
+ DATA ·table+0x88(SB)/4, $0x6d9d6122
+ DATA ·table+0x8c(SB)/4, $0xfde5380c
+ DATA ·table+0x90(SB)/4, $0xa4beea44
+ DATA ·table+0x94(SB)/4, $0x4bdecfa9
+ DATA ·table+0x98(SB)/4, $0xf6bb4b60
+ DATA ·table+0x9c(SB)/4, $0xbebfbc70
+ DATA ·table+0xa0(SB)/4, $0x289b7ec6
+ DATA ·table+0xa4(SB)/4, $0xeaa127fa
+ DATA ·table+0xa8(SB)/4, $0xd4ef3085
+ DATA ·table+0xac(SB)/4, $0x04881d05
+ DATA ·table+0xb0(SB)/4, $0xd9d4d039
+ DATA ·table+0xb4(SB)/4, $0xe6db99e5
+ DATA ·table+0xb8(SB)/4, $0x1fa27cf8
+ DATA ·table+0xbc(SB)/4, $0xc4ac5665
+ // Round 4
+ DATA ·table+0xc0(SB)/4, $0xf4292244
+ DATA ·table+0xc4(SB)/4, $0x432aff97
+ DATA ·table+0xc8(SB)/4, $0xab9423a7
+ DATA ·table+0xcc(SB)/4, $0xfc93a039
+ DATA ·table+0xd0(SB)/4, $0x655b59c3
+ DATA ·table+0xd4(SB)/4, $0x8f0ccc92
+ DATA ·table+0xd8(SB)/4, $0xffeff47d
+ DATA ·table+0xdc(SB)/4, $0x85845dd1
+ DATA ·table+0xe0(SB)/4, $0x6fa87e4f
+ DATA ·table+0xe4(SB)/4, $0xfe2ce6e0
+ DATA ·table+0xe8(SB)/4, $0xa3014314
+ DATA ·table+0xec(SB)/4, $0x4e0811a1
+ DATA ·table+0xf0(SB)/4, $0xf7537e82
+ DATA ·table+0xf4(SB)/4, $0xbd3af235
+ DATA ·table+0xf8(SB)/4, $0x2ad7d2bb
+ DATA ·table+0xfc(SB)/4, $0xeb86d391
+ // Global definition
+ GLOBL ·table(SB),8,$256
diff --git a/src/pkg/crypto/md5/md5block_decl.go b/src/pkg/crypto/md5/md5block_decl.go
index 14190c6ff..c4d6aaaf0 100644
--- a/src/pkg/crypto/md5/md5block_decl.go
+++ b/src/pkg/crypto/md5/md5block_decl.go
@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build amd64 386
+// +build amd64 386 arm
package md5
+//go:noescape
+
func block(dig *digest, p []byte)
diff --git a/src/pkg/crypto/rand/example_test.go b/src/pkg/crypto/rand/example_test.go
index 5af8e46f5..8a2717300 100644
--- a/src/pkg/crypto/rand/example_test.go
+++ b/src/pkg/crypto/rand/example_test.go
@@ -8,7 +8,6 @@ import (
"bytes"
"crypto/rand"
"fmt"
- "io"
)
// This example reads 10 cryptographically secure pseudorandom numbers from
@@ -16,8 +15,8 @@ import (
func ExampleRead() {
c := 10
b := make([]byte, c)
- n, err := io.ReadFull(rand.Reader, b)
- if n != len(b) || err != nil {
+ _, err := rand.Read(b)
+ if err != nil {
fmt.Println("error:", err)
return
}
diff --git a/src/pkg/crypto/rand/rand.go b/src/pkg/crypto/rand/rand.go
index 59759038e..4da3adb70 100644
--- a/src/pkg/crypto/rand/rand.go
+++ b/src/pkg/crypto/rand/rand.go
@@ -14,5 +14,8 @@ import "io"
// On Windows systems, Reader uses the CryptGenRandom API.
var Reader io.Reader
-// Read is a helper function that calls Reader.Read.
-func Read(b []byte) (n int, err error) { return Reader.Read(b) }
+// Read is a helper function that calls Reader.Read using io.ReadFull.
+// On return, n == len(b) if and only if err == nil.
+func Read(b []byte) (n int, err error) {
+ return io.ReadFull(Reader, b)
+}
diff --git a/src/pkg/crypto/rand/rand_unix.go b/src/pkg/crypto/rand/rand_unix.go
index 18f482472..238ceee55 100644
--- a/src/pkg/crypto/rand/rand_unix.go
+++ b/src/pkg/crypto/rand/rand_unix.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.
-// +build darwin freebsd linux netbsd openbsd plan9
+// +build darwin dragonfly freebsd linux netbsd openbsd plan9
// Unix cryptographically secure pseudorandom number
// generator.
diff --git a/src/pkg/crypto/rc4/rc4_386.s b/src/pkg/crypto/rc4/rc4_386.s
index c80ef2a3a..b04fc1fb8 100644
--- a/src/pkg/crypto/rc4/rc4_386.s
+++ b/src/pkg/crypto/rc4/rc4_386.s
@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../../cmd/ld/textflag.h"
+
// func xorKeyStream(dst, src *byte, n int, state *[256]byte, i, j *uint8)
-TEXT ·xorKeyStream(SB),7,$0
+TEXT ·xorKeyStream(SB),NOSPLIT,$0
MOVL dst+0(FP), DI
MOVL src+4(FP), SI
MOVL state+12(FP), BP
diff --git a/src/pkg/crypto/rc4/rc4_amd64.s b/src/pkg/crypto/rc4/rc4_amd64.s
index 353fe3720..e3234b6c7 100644
--- a/src/pkg/crypto/rc4/rc4_amd64.s
+++ b/src/pkg/crypto/rc4/rc4_amd64.s
@@ -2,6 +2,8 @@
// http://www.zorinaq.com/papers/rc4-amd64.html
// http://www.zorinaq.com/papers/rc4-amd64.tar.bz2
+#include "../../../cmd/ld/textflag.h"
+
// Local modifications:
//
// Transliterated from GNU to 6a assembly syntax by the Go authors.
@@ -36,7 +38,7 @@
** a 1.8 GHz AMD Opteron (rev C0) processor.
*/
-TEXT ·xorKeyStream(SB),7,$0
+TEXT ·xorKeyStream(SB),NOSPLIT,$0
MOVQ n+16(FP), BX // rbx = ARG(len)
MOVQ src+8(FP), SI // in = ARG(in)
MOVQ dst+0(FP), DI // out = ARG(out)
diff --git a/src/pkg/crypto/rc4/rc4_arm.s b/src/pkg/crypto/rc4/rc4_arm.s
index 307cb7148..3aad72940 100644
--- a/src/pkg/crypto/rc4/rc4_arm.s
+++ b/src/pkg/crypto/rc4/rc4_arm.s
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../../cmd/ld/textflag.h"
+
// Registers
dst = 0
src = 1
@@ -16,7 +18,7 @@ t = 11
t2 = 12
// func xorKeyStream(dst, src *byte, n int, state *[256]byte, i, j *uint8)
-TEXT ·xorKeyStream(SB),7,$0
+TEXT ·xorKeyStream(SB),NOSPLIT,$0
MOVW 0(FP), R(dst)
MOVW 4(FP), R(src)
MOVW 8(FP), R(n)
diff --git a/src/pkg/crypto/rsa/pkcs1v15.go b/src/pkg/crypto/rsa/pkcs1v15.go
index 28ca5d73b..1a055a3d6 100644
--- a/src/pkg/crypto/rsa/pkcs1v15.go
+++ b/src/pkg/crypto/rsa/pkcs1v15.go
@@ -124,7 +124,11 @@ func decryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (valid
lookingForIndex = subtle.ConstantTimeSelect(equals0, 0, lookingForIndex)
}
- valid = firstByteIsZero & secondByteIsTwo & (^lookingForIndex & 1)
+ // The PS padding must be at least 8 bytes long, and it starts two
+ // bytes into em.
+ validPS := subtle.ConstantTimeLessOrEq(2+8, index)
+
+ valid = firstByteIsZero & secondByteIsTwo & (^lookingForIndex & 1) & validPS
msg = em[index+1:]
return
}
diff --git a/src/pkg/crypto/rsa/pkcs1v15_test.go b/src/pkg/crypto/rsa/pkcs1v15_test.go
index bf9219bae..70bb22889 100644
--- a/src/pkg/crypto/rsa/pkcs1v15_test.go
+++ b/src/pkg/crypto/rsa/pkcs1v15_test.go
@@ -197,6 +197,14 @@ func TestVerifyPKCS1v15(t *testing.T) {
}
}
+func TestOverlongMessagePKCS1v15(t *testing.T) {
+ ciphertext := decodeBase64("fjOVdirUzFoLlukv80dBllMLjXythIf22feqPrNo0YoIjzyzyoMFiLjAc/Y4krkeZ11XFThIrEvw\nkRiZcCq5ng==")
+ _, err := DecryptPKCS1v15(nil, rsaPrivateKey, ciphertext)
+ if err == nil {
+ t.Error("RSA decrypted a message that was too long.")
+ }
+}
+
// In order to generate new test vectors you'll need the PEM form of this key:
// -----BEGIN RSA PRIVATE KEY-----
// MIIBOgIBAAJBALKZD0nEffqM1ACuak0bijtqE2QrI/KLADv7l3kK3ppMyCuLKoF0
diff --git a/src/pkg/crypto/rsa/pss.go b/src/pkg/crypto/rsa/pss.go
new file mode 100644
index 000000000..f9abec394
--- /dev/null
+++ b/src/pkg/crypto/rsa/pss.go
@@ -0,0 +1,282 @@
+// Copyright 2013 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 rsa
+
+// This file implementes the PSS signature scheme [1].
+//
+// [1] http://www.rsa.com/rsalabs/pkcs/files/h11300-wp-pkcs-1v2-2-rsa-cryptography-standard.pdf
+
+import (
+ "bytes"
+ "crypto"
+ "errors"
+ "hash"
+ "io"
+ "math/big"
+)
+
+func emsaPSSEncode(mHash []byte, emBits int, salt []byte, hash hash.Hash) ([]byte, error) {
+ // See [1], section 9.1.1
+ hLen := hash.Size()
+ sLen := len(salt)
+ emLen := (emBits + 7) / 8
+
+ // 1. If the length of M is greater than the input limitation for the
+ // hash function (2^61 - 1 octets for SHA-1), output "message too
+ // long" and stop.
+ //
+ // 2. Let mHash = Hash(M), an octet string of length hLen.
+
+ if len(mHash) != hLen {
+ return nil, errors.New("crypto/rsa: input must be hashed message")
+ }
+
+ // 3. If emLen < hLen + sLen + 2, output "encoding error" and stop.
+
+ if emLen < hLen+sLen+2 {
+ return nil, errors.New("crypto/rsa: encoding error")
+ }
+
+ em := make([]byte, emLen)
+ db := em[:emLen-sLen-hLen-2+1+sLen]
+ h := em[emLen-sLen-hLen-2+1+sLen : emLen-1]
+
+ // 4. Generate a random octet string salt of length sLen; if sLen = 0,
+ // then salt is the empty string.
+ //
+ // 5. Let
+ // M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt;
+ //
+ // M' is an octet string of length 8 + hLen + sLen with eight
+ // initial zero octets.
+ //
+ // 6. Let H = Hash(M'), an octet string of length hLen.
+
+ var prefix [8]byte
+
+ hash.Write(prefix[:])
+ hash.Write(mHash)
+ hash.Write(salt)
+
+ h = hash.Sum(h[:0])
+ hash.Reset()
+
+ // 7. Generate an octet string PS consisting of emLen - sLen - hLen - 2
+ // zero octets. The length of PS may be 0.
+ //
+ // 8. Let DB = PS || 0x01 || salt; DB is an octet string of length
+ // emLen - hLen - 1.
+
+ db[emLen-sLen-hLen-2] = 0x01
+ copy(db[emLen-sLen-hLen-1:], salt)
+
+ // 9. Let dbMask = MGF(H, emLen - hLen - 1).
+ //
+ // 10. Let maskedDB = DB \xor dbMask.
+
+ mgf1XOR(db, hash, h)
+
+ // 11. Set the leftmost 8 * emLen - emBits bits of the leftmost octet in
+ // maskedDB to zero.
+
+ db[0] &= (0xFF >> uint(8*emLen-emBits))
+
+ // 12. Let EM = maskedDB || H || 0xbc.
+ em[emLen-1] = 0xBC
+
+ // 13. Output EM.
+ return em, nil
+}
+
+func emsaPSSVerify(mHash, em []byte, emBits, sLen int, hash hash.Hash) error {
+ // 1. If the length of M is greater than the input limitation for the
+ // hash function (2^61 - 1 octets for SHA-1), output "inconsistent"
+ // and stop.
+ //
+ // 2. Let mHash = Hash(M), an octet string of length hLen.
+ hLen := hash.Size()
+ if hLen != len(mHash) {
+ return ErrVerification
+ }
+
+ // 3. If emLen < hLen + sLen + 2, output "inconsistent" and stop.
+ emLen := (emBits + 7) / 8
+ if emLen < hLen+sLen+2 {
+ return ErrVerification
+ }
+
+ // 4. If the rightmost octet of EM does not have hexadecimal value
+ // 0xbc, output "inconsistent" and stop.
+ if em[len(em)-1] != 0xBC {
+ return ErrVerification
+ }
+
+ // 5. Let maskedDB be the leftmost emLen - hLen - 1 octets of EM, and
+ // let H be the next hLen octets.
+ db := em[:emLen-hLen-1]
+ h := em[emLen-hLen-1 : len(em)-1]
+
+ // 6. If the leftmost 8 * emLen - emBits bits of the leftmost octet in
+ // maskedDB are not all equal to zero, output "inconsistent" and
+ // stop.
+ if em[0]&(0xFF<<uint(8-(8*emLen-emBits))) != 0 {
+ return ErrVerification
+ }
+
+ // 7. Let dbMask = MGF(H, emLen - hLen - 1).
+ //
+ // 8. Let DB = maskedDB \xor dbMask.
+ mgf1XOR(db, hash, h)
+
+ // 9. Set the leftmost 8 * emLen - emBits bits of the leftmost octet in DB
+ // to zero.
+ db[0] &= (0xFF >> uint(8*emLen-emBits))
+
+ if sLen == PSSSaltLengthAuto {
+ FindSaltLength:
+ for sLen = emLen - (hLen + 2); sLen >= 0; sLen-- {
+ switch db[emLen-hLen-sLen-2] {
+ case 1:
+ break FindSaltLength
+ case 0:
+ continue
+ default:
+ return ErrVerification
+ }
+ }
+ if sLen < 0 {
+ return ErrVerification
+ }
+ } else {
+ // 10. If the emLen - hLen - sLen - 2 leftmost octets of DB are not zero
+ // or if the octet at position emLen - hLen - sLen - 1 (the leftmost
+ // position is "position 1") does not have hexadecimal value 0x01,
+ // output "inconsistent" and stop.
+ for _, e := range db[:emLen-hLen-sLen-2] {
+ if e != 0x00 {
+ return ErrVerification
+ }
+ }
+ if db[emLen-hLen-sLen-2] != 0x01 {
+ return ErrVerification
+ }
+ }
+
+ // 11. Let salt be the last sLen octets of DB.
+ salt := db[len(db)-sLen:]
+
+ // 12. Let
+ // M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt ;
+ // M' is an octet string of length 8 + hLen + sLen with eight
+ // initial zero octets.
+ //
+ // 13. Let H' = Hash(M'), an octet string of length hLen.
+ var prefix [8]byte
+ hash.Write(prefix[:])
+ hash.Write(mHash)
+ hash.Write(salt)
+
+ h0 := hash.Sum(nil)
+
+ // 14. If H = H', output "consistent." Otherwise, output "inconsistent."
+ if !bytes.Equal(h0, h) {
+ return ErrVerification
+ }
+ return nil
+}
+
+// signPSSWithSalt calculates the signature of hashed using PSS [1] with specified salt.
+// Note that hashed must be the result of hashing the input message using the
+// given hash funcion. salt is a random sequence of bytes whose length will be
+// later used to verify the signature.
+func signPSSWithSalt(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed, salt []byte) (s []byte, err error) {
+ nBits := priv.N.BitLen()
+ em, err := emsaPSSEncode(hashed, nBits-1, salt, hash.New())
+ if err != nil {
+ return
+ }
+ m := new(big.Int).SetBytes(em)
+ c, err := decrypt(rand, priv, m)
+ if err != nil {
+ return
+ }
+ s = make([]byte, (nBits+7)/8)
+ copyWithLeftPad(s, c.Bytes())
+ return
+}
+
+const (
+ // PSSSaltLengthAuto causes the salt in a PSS signature to be as large
+ // as possible when signing, and to be auto-detected when verifying.
+ PSSSaltLengthAuto = 0
+ // PSSSaltLengthEqualsHash causes the salt length to equal the length
+ // of the hash used in the signature.
+ PSSSaltLengthEqualsHash = -1
+)
+
+// PSSOptions contains options for creating and verifying PSS signatures.
+type PSSOptions struct {
+ // SaltLength controls the length of the salt used in the PSS
+ // signature. It can either be a number of bytes, or one of the special
+ // PSSSaltLength constants.
+ SaltLength int
+}
+
+func (opts *PSSOptions) saltLength() int {
+ if opts == nil {
+ return PSSSaltLengthAuto
+ }
+ return opts.SaltLength
+}
+
+// SignPSS calculates the signature of hashed using RSASSA-PSS [1].
+// Note that hashed must be the result of hashing the input message using the
+// given hash funcion. The opts argument may be nil, in which case sensible
+// defaults are used.
+func SignPSS(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []byte, opts *PSSOptions) (s []byte, err error) {
+ saltLength := opts.saltLength()
+ switch saltLength {
+ case PSSSaltLengthAuto:
+ saltLength = (priv.N.BitLen()+7)/8 - 2 - hash.Size()
+ case PSSSaltLengthEqualsHash:
+ saltLength = hash.Size()
+ }
+
+ salt := make([]byte, saltLength)
+ if _, err = io.ReadFull(rand, salt); err != nil {
+ return
+ }
+ return signPSSWithSalt(rand, priv, hash, hashed, salt)
+}
+
+// VerifyPSS verifies a PSS signature.
+// hashed is the result of hashing the input message using the given hash
+// function and sig is the signature. A valid signature is indicated by
+// returning a nil error. The opts argument may be nil, in which case sensible
+// defaults are used.
+func VerifyPSS(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte, opts *PSSOptions) error {
+ return verifyPSS(pub, hash, hashed, sig, opts.saltLength())
+}
+
+// verifyPSS verifies a PSS signature with the given salt length.
+func verifyPSS(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte, saltLen int) error {
+ nBits := pub.N.BitLen()
+ if len(sig) != (nBits+7)/8 {
+ return ErrVerification
+ }
+ s := new(big.Int).SetBytes(sig)
+ m := encrypt(new(big.Int), pub, s)
+ emBits := nBits - 1
+ emLen := (emBits + 7) / 8
+ if emLen < len(m.Bytes()) {
+ return ErrVerification
+ }
+ em := make([]byte, emLen)
+ copyWithLeftPad(em, m.Bytes())
+ if saltLen == PSSSaltLengthEqualsHash {
+ saltLen = hash.Size()
+ }
+ return emsaPSSVerify(hashed, em, emBits, saltLen, hash.New())
+}
diff --git a/src/pkg/crypto/rsa/pss_test.go b/src/pkg/crypto/rsa/pss_test.go
new file mode 100644
index 000000000..32e6fc39d
--- /dev/null
+++ b/src/pkg/crypto/rsa/pss_test.go
@@ -0,0 +1,249 @@
+// Copyright 2013 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 rsa
+
+import (
+ "bufio"
+ "bytes"
+ "compress/bzip2"
+ "crypto"
+ _ "crypto/md5"
+ "crypto/rand"
+ "crypto/sha1"
+ _ "crypto/sha256"
+ "encoding/hex"
+ "math/big"
+ "os"
+ "strconv"
+ "strings"
+ "testing"
+)
+
+func TestEMSAPSS(t *testing.T) {
+ // Test vector in file pss-int.txt from: ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip
+ msg := []byte{
+ 0x85, 0x9e, 0xef, 0x2f, 0xd7, 0x8a, 0xca, 0x00, 0x30, 0x8b,
+ 0xdc, 0x47, 0x11, 0x93, 0xbf, 0x55, 0xbf, 0x9d, 0x78, 0xdb,
+ 0x8f, 0x8a, 0x67, 0x2b, 0x48, 0x46, 0x34, 0xf3, 0xc9, 0xc2,
+ 0x6e, 0x64, 0x78, 0xae, 0x10, 0x26, 0x0f, 0xe0, 0xdd, 0x8c,
+ 0x08, 0x2e, 0x53, 0xa5, 0x29, 0x3a, 0xf2, 0x17, 0x3c, 0xd5,
+ 0x0c, 0x6d, 0x5d, 0x35, 0x4f, 0xeb, 0xf7, 0x8b, 0x26, 0x02,
+ 0x1c, 0x25, 0xc0, 0x27, 0x12, 0xe7, 0x8c, 0xd4, 0x69, 0x4c,
+ 0x9f, 0x46, 0x97, 0x77, 0xe4, 0x51, 0xe7, 0xf8, 0xe9, 0xe0,
+ 0x4c, 0xd3, 0x73, 0x9c, 0x6b, 0xbf, 0xed, 0xae, 0x48, 0x7f,
+ 0xb5, 0x56, 0x44, 0xe9, 0xca, 0x74, 0xff, 0x77, 0xa5, 0x3c,
+ 0xb7, 0x29, 0x80, 0x2f, 0x6e, 0xd4, 0xa5, 0xff, 0xa8, 0xba,
+ 0x15, 0x98, 0x90, 0xfc,
+ }
+ salt := []byte{
+ 0xe3, 0xb5, 0xd5, 0xd0, 0x02, 0xc1, 0xbc, 0xe5, 0x0c, 0x2b,
+ 0x65, 0xef, 0x88, 0xa1, 0x88, 0xd8, 0x3b, 0xce, 0x7e, 0x61,
+ }
+ expected := []byte{
+ 0x66, 0xe4, 0x67, 0x2e, 0x83, 0x6a, 0xd1, 0x21, 0xba, 0x24,
+ 0x4b, 0xed, 0x65, 0x76, 0xb8, 0x67, 0xd9, 0xa4, 0x47, 0xc2,
+ 0x8a, 0x6e, 0x66, 0xa5, 0xb8, 0x7d, 0xee, 0x7f, 0xbc, 0x7e,
+ 0x65, 0xaf, 0x50, 0x57, 0xf8, 0x6f, 0xae, 0x89, 0x84, 0xd9,
+ 0xba, 0x7f, 0x96, 0x9a, 0xd6, 0xfe, 0x02, 0xa4, 0xd7, 0x5f,
+ 0x74, 0x45, 0xfe, 0xfd, 0xd8, 0x5b, 0x6d, 0x3a, 0x47, 0x7c,
+ 0x28, 0xd2, 0x4b, 0xa1, 0xe3, 0x75, 0x6f, 0x79, 0x2d, 0xd1,
+ 0xdc, 0xe8, 0xca, 0x94, 0x44, 0x0e, 0xcb, 0x52, 0x79, 0xec,
+ 0xd3, 0x18, 0x3a, 0x31, 0x1f, 0xc8, 0x96, 0xda, 0x1c, 0xb3,
+ 0x93, 0x11, 0xaf, 0x37, 0xea, 0x4a, 0x75, 0xe2, 0x4b, 0xdb,
+ 0xfd, 0x5c, 0x1d, 0xa0, 0xde, 0x7c, 0xec, 0xdf, 0x1a, 0x89,
+ 0x6f, 0x9d, 0x8b, 0xc8, 0x16, 0xd9, 0x7c, 0xd7, 0xa2, 0xc4,
+ 0x3b, 0xad, 0x54, 0x6f, 0xbe, 0x8c, 0xfe, 0xbc,
+ }
+
+ hash := sha1.New()
+ hash.Write(msg)
+ hashed := hash.Sum(nil)
+
+ encoded, err := emsaPSSEncode(hashed, 1023, salt, sha1.New())
+ if err != nil {
+ t.Errorf("Error from emsaPSSEncode: %s\n", err)
+ }
+ if !bytes.Equal(encoded, expected) {
+ t.Errorf("Bad encoding. got %x, want %x", encoded, expected)
+ }
+
+ if err = emsaPSSVerify(hashed, encoded, 1023, len(salt), sha1.New()); err != nil {
+ t.Errorf("Bad verification: %s", err)
+ }
+}
+
+// TestPSSGolden tests all the test vectors in pss-vect.txt from
+// ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip
+func TestPSSGolden(t *testing.T) {
+ inFile, err := os.Open("testdata/pss-vect.txt.bz2")
+ if err != nil {
+ t.Fatalf("Failed to open input file: %s", err)
+ }
+ defer inFile.Close()
+
+ // The pss-vect.txt file contains RSA keys and then a series of
+ // signatures. A goroutine is used to preprocess the input by merging
+ // lines, removing spaces in hex values and identifying the start of
+ // new keys and signature blocks.
+ const newKeyMarker = "START NEW KEY"
+ const newSignatureMarker = "START NEW SIGNATURE"
+
+ values := make(chan string)
+
+ go func() {
+ defer close(values)
+ scanner := bufio.NewScanner(bzip2.NewReader(inFile))
+ var partialValue string
+ lastWasValue := true
+
+ for scanner.Scan() {
+ line := scanner.Text()
+ switch {
+ case len(line) == 0:
+ if len(partialValue) > 0 {
+ values <- strings.Replace(partialValue, " ", "", -1)
+ partialValue = ""
+ lastWasValue = true
+ }
+ continue
+ case strings.HasPrefix(line, "# ======") && lastWasValue:
+ values <- newKeyMarker
+ lastWasValue = false
+ case strings.HasPrefix(line, "# ------") && lastWasValue:
+ values <- newSignatureMarker
+ lastWasValue = false
+ case strings.HasPrefix(line, "#"):
+ continue
+ default:
+ partialValue += line
+ }
+ }
+ if err := scanner.Err(); err != nil {
+ panic(err)
+ }
+ }()
+
+ var key *PublicKey
+ var hashed []byte
+ hash := crypto.SHA1
+ h := hash.New()
+ opts := &PSSOptions{
+ SaltLength: PSSSaltLengthEqualsHash,
+ }
+
+ for marker := range values {
+ switch marker {
+ case newKeyMarker:
+ key = new(PublicKey)
+ nHex, ok := <-values
+ if !ok {
+ continue
+ }
+ key.N = bigFromHex(nHex)
+ key.E = intFromHex(<-values)
+ // We don't care for d, p, q, dP, dQ or qInv.
+ for i := 0; i < 6; i++ {
+ <-values
+ }
+ case newSignatureMarker:
+ msg := fromHex(<-values)
+ <-values // skip salt
+ sig := fromHex(<-values)
+
+ h.Reset()
+ h.Write(msg)
+ hashed = h.Sum(hashed[:0])
+
+ if err := VerifyPSS(key, hash, hashed, sig, opts); err != nil {
+ t.Error(err)
+ }
+ default:
+ t.Fatalf("unknown marker: " + marker)
+ }
+ }
+}
+
+// TestPSSOpenSSL ensures that we can verify a PSS signature from OpenSSL with
+// the default options. OpenSSL sets the salt length to be maximal.
+func TestPSSOpenSSL(t *testing.T) {
+ hash := crypto.SHA256
+ h := hash.New()
+ h.Write([]byte("testing"))
+ hashed := h.Sum(nil)
+
+ // Generated with `echo -n testing | openssl dgst -sign key.pem -sigopt rsa_padding_mode:pss -sha256 > sig`
+ sig := []byte{
+ 0x95, 0x59, 0x6f, 0xd3, 0x10, 0xa2, 0xe7, 0xa2, 0x92, 0x9d,
+ 0x4a, 0x07, 0x2e, 0x2b, 0x27, 0xcc, 0x06, 0xc2, 0x87, 0x2c,
+ 0x52, 0xf0, 0x4a, 0xcc, 0x05, 0x94, 0xf2, 0xc3, 0x2e, 0x20,
+ 0xd7, 0x3e, 0x66, 0x62, 0xb5, 0x95, 0x2b, 0xa3, 0x93, 0x9a,
+ 0x66, 0x64, 0x25, 0xe0, 0x74, 0x66, 0x8c, 0x3e, 0x92, 0xeb,
+ 0xc6, 0xe6, 0xc0, 0x44, 0xf3, 0xb4, 0xb4, 0x2e, 0x8c, 0x66,
+ 0x0a, 0x37, 0x9c, 0x69,
+ }
+
+ if err := VerifyPSS(&rsaPrivateKey.PublicKey, hash, hashed, sig, nil); err != nil {
+ t.Error(err)
+ }
+}
+
+func TestPSSSigning(t *testing.T) {
+ var saltLengthCombinations = []struct {
+ signSaltLength, verifySaltLength int
+ good bool
+ }{
+ {PSSSaltLengthAuto, PSSSaltLengthAuto, true},
+ {PSSSaltLengthEqualsHash, PSSSaltLengthAuto, true},
+ {PSSSaltLengthEqualsHash, PSSSaltLengthEqualsHash, true},
+ {PSSSaltLengthEqualsHash, 8, false},
+ {PSSSaltLengthAuto, PSSSaltLengthEqualsHash, false},
+ {8, 8, true},
+ }
+
+ hash := crypto.MD5
+ h := hash.New()
+ h.Write([]byte("testing"))
+ hashed := h.Sum(nil)
+ var opts PSSOptions
+
+ for i, test := range saltLengthCombinations {
+ opts.SaltLength = test.signSaltLength
+ sig, err := SignPSS(rand.Reader, rsaPrivateKey, hash, hashed, &opts)
+ if err != nil {
+ t.Errorf("#%d: error while signing: %s", i, err)
+ continue
+ }
+
+ opts.SaltLength = test.verifySaltLength
+ err = VerifyPSS(&rsaPrivateKey.PublicKey, hash, hashed, sig, &opts)
+ if (err == nil) != test.good {
+ t.Errorf("#%d: bad result, wanted: %t, got: %s", i, test.good, err)
+ }
+ }
+}
+
+func bigFromHex(hex string) *big.Int {
+ n, ok := new(big.Int).SetString(hex, 16)
+ if !ok {
+ panic("bad hex: " + hex)
+ }
+ return n
+}
+
+func intFromHex(hex string) int {
+ i, err := strconv.ParseInt(hex, 16, 32)
+ if err != nil {
+ panic(err)
+ }
+ return int(i)
+}
+
+func fromHex(hexStr string) []byte {
+ s, err := hex.DecodeString(hexStr)
+ if err != nil {
+ panic(err)
+ }
+ return s
+}
diff --git a/src/pkg/crypto/rsa/rsa.go b/src/pkg/crypto/rsa/rsa.go
index f56fb37ee..c7353ea31 100644
--- a/src/pkg/crypto/rsa/rsa.go
+++ b/src/pkg/crypto/rsa/rsa.go
@@ -5,8 +5,6 @@
// Package rsa implements RSA encryption as specified in PKCS#1.
package rsa
-// TODO(agl): Add support for PSS padding.
-
import (
"crypto/rand"
"crypto/subtle"
diff --git a/src/pkg/crypto/rsa/rsa_test.go b/src/pkg/crypto/rsa/rsa_test.go
index ffd96e62f..cf193c669 100644
--- a/src/pkg/crypto/rsa/rsa_test.go
+++ b/src/pkg/crypto/rsa/rsa_test.go
@@ -120,8 +120,10 @@ func testKeyBasics(t *testing.T, priv *PrivateKey) {
}
func fromBase10(base10 string) *big.Int {
- i := new(big.Int)
- i.SetString(base10, 10)
+ i, ok := new(big.Int).SetString(base10, 10)
+ if !ok {
+ panic("bad number: " + base10)
+ }
return i
}
diff --git a/src/pkg/crypto/rsa/testdata/pss-vect.txt.bz2 b/src/pkg/crypto/rsa/testdata/pss-vect.txt.bz2
new file mode 100644
index 000000000..ad3da1ac4
--- /dev/null
+++ b/src/pkg/crypto/rsa/testdata/pss-vect.txt.bz2
Binary files differ
diff --git a/src/pkg/crypto/sha1/example_test.go b/src/pkg/crypto/sha1/example_test.go
new file mode 100644
index 000000000..25fe5f308
--- /dev/null
+++ b/src/pkg/crypto/sha1/example_test.go
@@ -0,0 +1,18 @@
+// 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 sha1_test
+
+import (
+ "crypto/sha1"
+ "fmt"
+ "io"
+)
+
+func ExampleNew() {
+ h := sha1.New()
+ io.WriteString(h, "His money is twice tainted: 'taint yours and 'taint mine.")
+ fmt.Printf("% x", h.Sum(nil))
+ // Output: 59 7f 6a 54 00 10 f9 4c 15 d7 18 06 a9 9a 2c 87 10 e7 47 bd
+}
diff --git a/src/pkg/crypto/sha1/sha1.go b/src/pkg/crypto/sha1/sha1.go
index 7cfde47dc..8eb3f7a79 100644
--- a/src/pkg/crypto/sha1/sha1.go
+++ b/src/pkg/crypto/sha1/sha1.go
@@ -90,9 +90,13 @@ func (d *digest) Write(p []byte) (nn int, err error) {
func (d0 *digest) Sum(in []byte) []byte {
// Make a copy of d0 so that caller can keep writing and summing.
d := *d0
+ hash := d.checkSum()
+ return append(in, hash[:]...)
+}
- // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64.
+func (d *digest) checkSum() [Size]byte {
len := d.len
+ // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64.
var tmp [64]byte
tmp[0] = 0x80
if len%64 < 56 {
@@ -120,5 +124,13 @@ func (d0 *digest) Sum(in []byte) []byte {
digest[i*4+3] = byte(s)
}
- return append(in, digest[:]...)
+ return digest
+}
+
+// Sum returns the SHA1 checksum of the data.
+func Sum(data []byte) [Size]byte {
+ var d digest
+ d.Reset()
+ d.Write(data)
+ return d.checkSum()
}
diff --git a/src/pkg/crypto/sha1/sha1_test.go b/src/pkg/crypto/sha1/sha1_test.go
index 57cd4313e..c3868d702 100644
--- a/src/pkg/crypto/sha1/sha1_test.go
+++ b/src/pkg/crypto/sha1/sha1_test.go
@@ -54,6 +54,10 @@ var golden = []sha1Test{
func TestGolden(t *testing.T) {
for i := 0; i < len(golden); i++ {
g := golden[i]
+ s := fmt.Sprintf("%x", Sum([]byte(g.in)))
+ if s != g.out {
+ t.Fatalf("Sum function: sha1(%s) = %s want %s", g.in, s, g.out)
+ }
c := New()
for j := 0; j < 3; j++ {
if j < 2 {
@@ -72,13 +76,6 @@ func TestGolden(t *testing.T) {
}
}
-func ExampleNew() {
- h := New()
- io.WriteString(h, "His money is twice tainted: 'taint yours and 'taint mine.")
- fmt.Printf("% x", h.Sum(nil))
- // Output: 59 7f 6a 54 00 10 f9 4c 15 d7 18 06 a9 9a 2c 87 10 e7 47 bd
-}
-
var bench = New()
var buf = make([]byte, 8192)
diff --git a/src/pkg/crypto/sha1/sha1block_386.s b/src/pkg/crypto/sha1/sha1block_386.s
index e60a7b9b0..890b3ae81 100644
--- a/src/pkg/crypto/sha1/sha1block_386.s
+++ b/src/pkg/crypto/sha1/sha1block_386.s
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../../cmd/ld/textflag.h"
+
// SHA1 block routine. See sha1block.go for Go equivalent.
//
// There are 80 rounds of 4 types:
@@ -99,7 +101,7 @@
MIX(a, b, c, d, e, 0xCA62C1D6)
// func block(dig *digest, p []byte)
-TEXT ·block(SB),7,$92-16
+TEXT ·block(SB),NOSPLIT,$92-16
MOVL dig+0(FP), BP
MOVL p+4(FP), SI
MOVL p_len+8(FP), DX
diff --git a/src/pkg/crypto/sha1/sha1block_amd64.s b/src/pkg/crypto/sha1/sha1block_amd64.s
index 452578aa4..0bb6c204c 100644
--- a/src/pkg/crypto/sha1/sha1block_amd64.s
+++ b/src/pkg/crypto/sha1/sha1block_amd64.s
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../../cmd/ld/textflag.h"
+
// SHA1 block routine. See sha1block.go for Go equivalent.
//
// There are 80 rounds of 4 types:
@@ -87,7 +89,7 @@
FUNC4(a, b, c, d, e); \
MIX(a, b, c, d, e, 0xCA62C1D6)
-TEXT ·block(SB),7,$64-32
+TEXT ·block(SB),NOSPLIT,$64-32
MOVQ dig+0(FP), BP
MOVQ p_base+8(FP), SI
MOVQ p_len+16(FP), DX
diff --git a/src/pkg/crypto/sha1/sha1block_decl.go b/src/pkg/crypto/sha1/sha1block_decl.go
index 3512a5829..4cb157fff 100644
--- a/src/pkg/crypto/sha1/sha1block_decl.go
+++ b/src/pkg/crypto/sha1/sha1block_decl.go
@@ -6,4 +6,6 @@
package sha1
+//go:noescape
+
func block(dig *digest, p []byte)
diff --git a/src/pkg/crypto/sha256/sha256.go b/src/pkg/crypto/sha256/sha256.go
index dc0e18f50..d69ed24a3 100644
--- a/src/pkg/crypto/sha256/sha256.go
+++ b/src/pkg/crypto/sha256/sha256.go
@@ -134,9 +134,16 @@ func (d *digest) Write(p []byte) (nn int, err error) {
func (d0 *digest) Sum(in []byte) []byte {
// Make a copy of d0 so that caller can keep writing and summing.
d := *d0
+ hash := d.checkSum()
+ if d.is224 {
+ return append(in, hash[:Size224]...)
+ }
+ return append(in, hash[:]...)
+}
- // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64.
+func (d *digest) checkSum() [Size]byte {
len := d.len
+ // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64.
var tmp [64]byte
tmp[0] = 0x80
if len%64 < 56 {
@@ -157,10 +164,8 @@ func (d0 *digest) Sum(in []byte) []byte {
}
h := d.h[:]
- size := Size
if d.is224 {
h = d.h[:7]
- size = Size224
}
var digest [Size]byte
@@ -171,5 +176,24 @@ func (d0 *digest) Sum(in []byte) []byte {
digest[i*4+3] = byte(s)
}
- return append(in, digest[:size]...)
+ return digest
+}
+
+// Sum256 returns the SHA256 checksum of the data.
+func Sum256(data []byte) [Size]byte {
+ var d digest
+ d.Reset()
+ d.Write(data)
+ return d.checkSum()
+}
+
+// Sum224 returns the SHA224 checksum of the data.
+func Sum224(data []byte) (sum224 [Size224]byte) {
+ var d digest
+ d.is224 = true
+ d.Reset()
+ d.Write(data)
+ sum := d.checkSum()
+ copy(sum224[:], sum[:Size224])
+ return
}
diff --git a/src/pkg/crypto/sha256/sha256_test.go b/src/pkg/crypto/sha256/sha256_test.go
index 29bf1619a..bb1ec3b16 100644
--- a/src/pkg/crypto/sha256/sha256_test.go
+++ b/src/pkg/crypto/sha256/sha256_test.go
@@ -88,6 +88,10 @@ var golden224 = []sha256Test{
func TestGolden(t *testing.T) {
for i := 0; i < len(golden); i++ {
g := golden[i]
+ s := fmt.Sprintf("%x", Sum256([]byte(g.in)))
+ if s != g.out {
+ t.Fatalf("Sum256 function: sha256(%s) = %s want %s", g.in, s, g.out)
+ }
c := New()
for j := 0; j < 3; j++ {
if j < 2 {
@@ -106,6 +110,10 @@ func TestGolden(t *testing.T) {
}
for i := 0; i < len(golden224); i++ {
g := golden224[i]
+ s := fmt.Sprintf("%x", Sum224([]byte(g.in)))
+ if s != g.out {
+ t.Fatalf("Sum224 function: sha224(%s) = %s want %s", g.in, s, g.out)
+ }
c := New224()
for j := 0; j < 3; j++ {
if j < 2 {
diff --git a/src/pkg/crypto/sha512/sha512.go b/src/pkg/crypto/sha512/sha512.go
index 4aec52938..d2ada5137 100644
--- a/src/pkg/crypto/sha512/sha512.go
+++ b/src/pkg/crypto/sha512/sha512.go
@@ -135,7 +135,14 @@ func (d0 *digest) Sum(in []byte) []byte {
// Make a copy of d0 so that caller can keep writing and summing.
d := new(digest)
*d = *d0
+ hash := d.checkSum()
+ if d.is384 {
+ return append(in, hash[:Size384]...)
+ }
+ return append(in, hash[:]...)
+}
+func (d *digest) checkSum() [Size]byte {
// Padding. Add a 1 bit and 0 bits until 112 bytes mod 128.
len := d.len
var tmp [128]byte
@@ -158,10 +165,8 @@ func (d0 *digest) Sum(in []byte) []byte {
}
h := d.h[:]
- size := Size
if d.is384 {
h = d.h[:6]
- size = Size384
}
var digest [Size]byte
@@ -176,5 +181,24 @@ func (d0 *digest) Sum(in []byte) []byte {
digest[i*8+7] = byte(s)
}
- return append(in, digest[:size]...)
+ return digest
+}
+
+// Sum512 returns the SHA512 checksum of the data.
+func Sum512(data []byte) [Size]byte {
+ var d digest
+ d.Reset()
+ d.Write(data)
+ return d.checkSum()
+}
+
+// Sum384 returns the SHA384 checksum of the data.
+func Sum384(data []byte) (sum384 [Size384]byte) {
+ var d digest
+ d.is384 = true
+ d.Reset()
+ d.Write(data)
+ sum := d.checkSum()
+ copy(sum384[:], sum[:Size384])
+ return
}
diff --git a/src/pkg/crypto/sha512/sha512_test.go b/src/pkg/crypto/sha512/sha512_test.go
index 6eafb1b5f..167c20ad0 100644
--- a/src/pkg/crypto/sha512/sha512_test.go
+++ b/src/pkg/crypto/sha512/sha512_test.go
@@ -88,6 +88,10 @@ var golden384 = []sha512Test{
func TestGolden(t *testing.T) {
for i := 0; i < len(golden); i++ {
g := golden[i]
+ s := fmt.Sprintf("%x", Sum512([]byte(g.in)))
+ if s != g.out {
+ t.Fatalf("Sum512 function: sha512(%s) = %s want %s", g.in, s, g.out)
+ }
c := New()
for j := 0; j < 3; j++ {
if j < 2 {
@@ -106,6 +110,10 @@ func TestGolden(t *testing.T) {
}
for i := 0; i < len(golden384); i++ {
g := golden384[i]
+ s := fmt.Sprintf("%x", Sum384([]byte(g.in)))
+ if s != g.out {
+ t.Fatalf("Sum384 function: sha384(%s) = %s want %s", g.in, s, g.out)
+ }
c := New384()
for j := 0; j < 3; j++ {
if j < 2 {
diff --git a/src/pkg/crypto/subtle/constant_time.go b/src/pkg/crypto/subtle/constant_time.go
index 57dbe9db5..dfb658465 100644
--- a/src/pkg/crypto/subtle/constant_time.go
+++ b/src/pkg/crypto/subtle/constant_time.go
@@ -55,3 +55,11 @@ func ConstantTimeCopy(v int, x, y []byte) {
}
return
}
+
+// ConstantTimeLessOrEq returns 1 if x <= y and 0 otherwise.
+// Its behavior is undefined if x or y are negative or > 2**31 - 1.
+func ConstantTimeLessOrEq(x, y int) int {
+ x32 := int32(x)
+ y32 := int32(y)
+ return int(((x32 - y32 - 1) >> 31) & 1)
+}
diff --git a/src/pkg/crypto/subtle/constant_time_test.go b/src/pkg/crypto/subtle/constant_time_test.go
index adab8e2e8..d8e321ec0 100644
--- a/src/pkg/crypto/subtle/constant_time_test.go
+++ b/src/pkg/crypto/subtle/constant_time_test.go
@@ -103,3 +103,23 @@ func TestConstantTimeCopy(t *testing.T) {
t.Error(err)
}
}
+
+var lessOrEqTests = []struct {
+ x, y, result int
+}{
+ {0, 0, 1},
+ {1, 0, 0},
+ {0, 1, 1},
+ {10, 20, 1},
+ {20, 10, 0},
+ {10, 10, 1},
+}
+
+func TestConstantTimeLessOrEq(t *testing.T) {
+ for i, test := range lessOrEqTests {
+ result := ConstantTimeLessOrEq(test.x, test.y)
+ if result != test.result {
+ t.Errorf("#%d: %d <= %d gave %d, expected %d", i, test.x, test.y, result, test.result)
+ }
+ }
+}
diff --git a/src/pkg/crypto/tls/cipher_suites.go b/src/pkg/crypto/tls/cipher_suites.go
index a647e19aa..39a51459d 100644
--- a/src/pkg/crypto/tls/cipher_suites.go
+++ b/src/pkg/crypto/tls/cipher_suites.go
@@ -34,6 +34,22 @@ type keyAgreement interface {
generateClientKeyExchange(*Config, *clientHelloMsg, *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error)
}
+const (
+ // suiteECDH indicates that the cipher suite involves elliptic curve
+ // Diffie-Hellman. This means that it should only be selected when the
+ // client indicates that it supports ECC with a curve and point format
+ // that we're happy with.
+ suiteECDHE = 1 << iota
+ // suiteECDSA indicates that the cipher suite involves an ECDSA
+ // signature and therefore may only be selected when the server's
+ // certificate is ECDSA. If this is not set then the cipher suite is
+ // RSA based.
+ suiteECDSA
+ // suiteTLS12 indicates that the cipher suite should only be advertised
+ // and accepted when using TLS 1.2.
+ suiteTLS12
+)
+
// A cipherSuite is a specific combination of key agreement, cipher and MAC
// function. All cipher suites currently assume RSA key agreement.
type cipherSuite struct {
@@ -42,24 +58,30 @@ type cipherSuite struct {
keyLen int
macLen int
ivLen int
- ka func() keyAgreement
- // If elliptic is set, a server will only consider this ciphersuite if
- // the ClientHello indicated that the client supports an elliptic curve
- // and point format that we can handle.
- elliptic bool
- cipher func(key, iv []byte, isRead bool) interface{}
- mac func(version uint16, macKey []byte) macFunction
+ ka func(version uint16) keyAgreement
+ // flags is a bitmask of the suite* values, above.
+ flags int
+ cipher func(key, iv []byte, isRead bool) interface{}
+ mac func(version uint16, macKey []byte) macFunction
+ aead func(key, fixedNonce []byte) cipher.AEAD
}
var cipherSuites = []*cipherSuite{
- {TLS_RSA_WITH_RC4_128_SHA, 16, 20, 0, rsaKA, false, cipherRC4, macSHA1},
- {TLS_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, rsaKA, false, cipher3DES, macSHA1},
- {TLS_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, rsaKA, false, cipherAES, macSHA1},
- {TLS_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, rsaKA, false, cipherAES, macSHA1},
- {TLS_ECDHE_RSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheRSAKA, true, cipherRC4, macSHA1},
- {TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, ecdheRSAKA, true, cipher3DES, macSHA1},
- {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheRSAKA, true, cipherAES, macSHA1},
- {TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheRSAKA, true, cipherAES, macSHA1},
+ // Ciphersuite order is chosen so that ECDHE comes before plain RSA
+ // and RC4 comes before AES (because of the Lucky13 attack).
+ {TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadAESGCM},
+ {TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12, nil, nil, aeadAESGCM},
+ {TLS_ECDHE_RSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheRSAKA, suiteECDHE, cipherRC4, macSHA1, nil},
+ {TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheECDSAKA, suiteECDHE | suiteECDSA, cipherRC4, macSHA1, nil},
+ {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil},
+ {TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA, cipherAES, macSHA1, nil},
+ {TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil},
+ {TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA, cipherAES, macSHA1, nil},
+ {TLS_RSA_WITH_RC4_128_SHA, 16, 20, 0, rsaKA, 0, cipherRC4, macSHA1, nil},
+ {TLS_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil},
+ {TLS_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil},
+ {TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, ecdheRSAKA, suiteECDHE, cipher3DES, macSHA1, nil},
+ {TLS_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, rsaKA, 0, cipher3DES, macSHA1, nil},
}
func cipherRC4(key, iv []byte, isRead bool) interface{} {
@@ -85,7 +107,7 @@ func cipherAES(key, iv []byte, isRead bool) interface{} {
// macSHA1 returns a macFunction for the given protocol version.
func macSHA1(version uint16, key []byte) macFunction {
- if version == versionSSL30 {
+ if version == VersionSSL30 {
mac := ssl30MAC{
h: sha1.New(),
key: make([]byte, len(key)),
@@ -98,7 +120,47 @@ func macSHA1(version uint16, key []byte) macFunction {
type macFunction interface {
Size() int
- MAC(digestBuf, seq, data []byte) []byte
+ MAC(digestBuf, seq, header, data []byte) []byte
+}
+
+// fixedNonceAEAD wraps an AEAD and prefixes a fixed portion of the nonce to
+// each call.
+type fixedNonceAEAD struct {
+ // sealNonce and openNonce are buffers where the larger nonce will be
+ // constructed. Since a seal and open operation may be running
+ // concurrently, there is a separate buffer for each.
+ sealNonce, openNonce []byte
+ aead cipher.AEAD
+}
+
+func (f *fixedNonceAEAD) NonceSize() int { return 8 }
+func (f *fixedNonceAEAD) Overhead() int { return f.aead.Overhead() }
+
+func (f *fixedNonceAEAD) Seal(out, nonce, plaintext, additionalData []byte) []byte {
+ copy(f.sealNonce[len(f.sealNonce)-8:], nonce)
+ return f.aead.Seal(out, f.sealNonce, plaintext, additionalData)
+}
+
+func (f *fixedNonceAEAD) Open(out, nonce, plaintext, additionalData []byte) ([]byte, error) {
+ copy(f.openNonce[len(f.openNonce)-8:], nonce)
+ return f.aead.Open(out, f.openNonce, plaintext, additionalData)
+}
+
+func aeadAESGCM(key, fixedNonce []byte) cipher.AEAD {
+ aes, err := aes.NewCipher(key)
+ if err != nil {
+ panic(err)
+ }
+ aead, err := cipher.NewGCM(aes)
+ if err != nil {
+ panic(err)
+ }
+
+ nonce1, nonce2 := make([]byte, 12), make([]byte, 12)
+ copy(nonce1, fixedNonce)
+ copy(nonce2, fixedNonce)
+
+ return &fixedNonceAEAD{nonce1, nonce2, aead}
}
// ssl30MAC implements the SSLv3 MAC function, as defined in
@@ -116,7 +178,7 @@ var ssl30Pad1 = [48]byte{0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0
var ssl30Pad2 = [48]byte{0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c}
-func (s ssl30MAC) MAC(digestBuf, seq, record []byte) []byte {
+func (s ssl30MAC) MAC(digestBuf, seq, header, data []byte) []byte {
padLength := 48
if s.h.Size() == 20 {
padLength = 40
@@ -126,9 +188,9 @@ func (s ssl30MAC) MAC(digestBuf, seq, record []byte) []byte {
s.h.Write(s.key)
s.h.Write(ssl30Pad1[:padLength])
s.h.Write(seq)
- s.h.Write(record[:1])
- s.h.Write(record[3:5])
- s.h.Write(record[recordHeaderLen:])
+ s.h.Write(header[:1])
+ s.h.Write(header[3:5])
+ s.h.Write(data)
digestBuf = s.h.Sum(digestBuf[:0])
s.h.Reset()
@@ -147,19 +209,30 @@ func (s tls10MAC) Size() int {
return s.h.Size()
}
-func (s tls10MAC) MAC(digestBuf, seq, record []byte) []byte {
+func (s tls10MAC) MAC(digestBuf, seq, header, data []byte) []byte {
s.h.Reset()
s.h.Write(seq)
- s.h.Write(record)
+ s.h.Write(header)
+ s.h.Write(data)
return s.h.Sum(digestBuf[:0])
}
-func rsaKA() keyAgreement {
+func rsaKA(version uint16) keyAgreement {
return rsaKeyAgreement{}
}
-func ecdheRSAKA() keyAgreement {
- return new(ecdheRSAKeyAgreement)
+func ecdheECDSAKA(version uint16) keyAgreement {
+ return &ecdheKeyAgreement{
+ sigType: signatureECDSA,
+ version: version,
+ }
+}
+
+func ecdheRSAKA(version uint16) keyAgreement {
+ return &ecdheKeyAgreement{
+ sigType: signatureRSA,
+ version: version,
+ }
}
// mutualCipherSuite returns a cipherSuite given a list of supported
@@ -181,12 +254,17 @@ func mutualCipherSuite(have []uint16, want uint16) *cipherSuite {
// A list of the possible cipher suite ids. Taken from
// http://www.iana.org/assignments/tls-parameters/tls-parameters.xml
const (
- TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005
- TLS_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x000a
- TLS_RSA_WITH_AES_128_CBC_SHA uint16 = 0x002f
- TLS_RSA_WITH_AES_256_CBC_SHA uint16 = 0x0035
- TLS_ECDHE_RSA_WITH_RC4_128_SHA uint16 = 0xc011
- TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xc012
- TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0xc013
- TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA uint16 = 0xc014
+ TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005
+ TLS_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x000a
+ TLS_RSA_WITH_AES_128_CBC_SHA uint16 = 0x002f
+ TLS_RSA_WITH_AES_256_CBC_SHA uint16 = 0x0035
+ TLS_ECDHE_ECDSA_WITH_RC4_128_SHA uint16 = 0xc007
+ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA uint16 = 0xc009
+ TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA uint16 = 0xc00a
+ TLS_ECDHE_RSA_WITH_RC4_128_SHA uint16 = 0xc011
+ TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xc012
+ TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0xc013
+ TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA uint16 = 0xc014
+ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0xc02f
+ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 uint16 = 0xc02b
)
diff --git a/src/pkg/crypto/tls/common.go b/src/pkg/crypto/tls/common.go
index f86c90de7..b7229d29f 100644
--- a/src/pkg/crypto/tls/common.go
+++ b/src/pkg/crypto/tls/common.go
@@ -9,22 +9,27 @@ import (
"crypto/rand"
"crypto/x509"
"io"
+ "math/big"
"strings"
"sync"
"time"
)
const (
+ VersionSSL30 = 0x0300
+ VersionTLS10 = 0x0301
+ VersionTLS11 = 0x0302
+ VersionTLS12 = 0x0303
+)
+
+const (
maxPlaintext = 16384 // maximum plaintext payload length
maxCiphertext = 16384 + 2048 // maximum ciphertext payload length
recordHeaderLen = 5 // record header length
maxHandshake = 65536 // maximum handshake we support (protocol max is 16 MB)
- versionSSL30 = 0x0300
- versionTLS10 = 0x0301
-
- minVersion = versionSSL30
- maxVersion = versionTLS10
+ minVersion = VersionSSL30
+ maxVersion = VersionTLS12
)
// TLS record types.
@@ -60,12 +65,13 @@ const (
// TLS extension numbers
var (
- extensionServerName uint16 = 0
- extensionStatusRequest uint16 = 5
- extensionSupportedCurves uint16 = 10
- extensionSupportedPoints uint16 = 11
- extensionSessionTicket uint16 = 35
- extensionNextProtoNeg uint16 = 13172 // not IANA assigned
+ extensionServerName uint16 = 0
+ extensionStatusRequest uint16 = 5
+ extensionSupportedCurves uint16 = 10
+ extensionSupportedPoints uint16 = 11
+ extensionSignatureAlgorithms uint16 = 13
+ extensionSessionTicket uint16 = 35
+ extensionNextProtoNeg uint16 = 13172 // not IANA assigned
)
// TLS Elliptic Curves
@@ -93,25 +99,60 @@ const (
certTypeDSSSign = 2 // A certificate containing a DSA key
certTypeRSAFixedDH = 3 // A certificate containing a static DH key
certTypeDSSFixedDH = 4 // A certificate containing a static DH key
+
+ // See RFC4492 sections 3 and 5.5.
+ certTypeECDSASign = 64 // A certificate containing an ECDSA-capable public key, signed with ECDSA.
+ certTypeRSAFixedECDH = 65 // A certificate containing an ECDH-capable public key, signed with RSA.
+ certTypeECDSAFixedECDH = 66 // A certificate containing an ECDH-capable public key, signed with ECDSA.
+
// Rest of these are reserved by the TLS spec
)
+// Hash functions for TLS 1.2 (See RFC 5246, section A.4.1)
+const (
+ hashSHA1 uint8 = 2
+ hashSHA256 uint8 = 4
+)
+
+// Signature algorithms for TLS 1.2 (See RFC 5246, section A.4.1)
+const (
+ signatureRSA uint8 = 1
+ signatureECDSA uint8 = 3
+)
+
+// signatureAndHash mirrors the TLS 1.2, SignatureAndHashAlgorithm struct. See
+// RFC 5246, section A.4.1.
+type signatureAndHash struct {
+ hash, signature uint8
+}
+
+// supportedSKXSignatureAlgorithms contains the signature and hash algorithms
+// that the code advertises as supported in a TLS 1.2 ClientHello.
+var supportedSKXSignatureAlgorithms = []signatureAndHash{
+ {hashSHA256, signatureRSA},
+ {hashSHA256, signatureECDSA},
+ {hashSHA1, signatureRSA},
+ {hashSHA1, signatureECDSA},
+}
+
+// supportedClientCertSignatureAlgorithms contains the signature and hash
+// algorithms that the code advertises as supported in a TLS 1.2
+// CertificateRequest.
+var supportedClientCertSignatureAlgorithms = []signatureAndHash{
+ {hashSHA256, signatureRSA},
+ {hashSHA256, signatureECDSA},
+}
+
// ConnectionState records basic TLS details about the connection.
type ConnectionState struct {
- HandshakeComplete bool
- DidResume bool
- CipherSuite uint16
- NegotiatedProtocol string
- NegotiatedProtocolIsMutual bool
-
- // ServerName contains the server name indicated by the client, if any.
- // (Only valid for server connections.)
- ServerName string
-
- // the certificate chain that was presented by the other side
- PeerCertificates []*x509.Certificate
- // the verified certificate chains built from PeerCertificates.
- VerifiedChains [][]*x509.Certificate
+ HandshakeComplete bool // TLS handshake is complete
+ DidResume bool // connection resumes a previous TLS connection
+ CipherSuite uint16 // cipher suite in use (TLS_RSA_WITH_RC4_128_SHA, ...)
+ NegotiatedProtocol string // negotiated next protocol (from Config.NextProtos)
+ NegotiatedProtocolIsMutual bool // negotiated protocol was advertised by server
+ ServerName string // server name requested by client, if any (server side only)
+ PeerCertificates []*x509.Certificate // certificate chain presented by remote peer
+ VerifiedChains [][]*x509.Certificate // verified chains built from PeerCertificates
}
// ClientAuthType declares the policy the server will follow for
@@ -204,6 +245,15 @@ type Config struct {
// connections using that key are compromised.
SessionTicketKey [32]byte
+ // MinVersion contains the minimum SSL/TLS version that is acceptable.
+ // If zero, then SSLv3 is taken as the minimum.
+ MinVersion uint16
+
+ // MaxVersion contains the maximum SSL/TLS version that is acceptable.
+ // If zero, then the maximum version supported by this package is used,
+ // which is currently TLS 1.2.
+ MaxVersion uint16
+
serverInitOnce sync.Once // guards calling (*Config).serverInit
}
@@ -248,6 +298,35 @@ func (c *Config) cipherSuites() []uint16 {
return s
}
+func (c *Config) minVersion() uint16 {
+ if c == nil || c.MinVersion == 0 {
+ return minVersion
+ }
+ return c.MinVersion
+}
+
+func (c *Config) maxVersion() uint16 {
+ if c == nil || c.MaxVersion == 0 {
+ return maxVersion
+ }
+ return c.MaxVersion
+}
+
+// mutualVersion returns the protocol version to use given the advertised
+// version of the peer.
+func (c *Config) mutualVersion(vers uint16) (uint16, bool) {
+ minVersion := c.minVersion()
+ maxVersion := c.maxVersion()
+
+ if vers < minVersion {
+ return 0, false
+ }
+ if vers > maxVersion {
+ vers = maxVersion
+ }
+ return vers, true
+}
+
// getCertificateForName returns the best certificate for the given name,
// defaulting to the first element of c.Certificates if there are no good
// options.
@@ -304,7 +383,7 @@ func (c *Config) BuildNameToCertificate() {
// A Certificate is a chain of one or more certificates, leaf first.
type Certificate struct {
Certificate [][]byte
- PrivateKey crypto.PrivateKey // supported types: *rsa.PrivateKey
+ PrivateKey crypto.PrivateKey // supported types: *rsa.PrivateKey, *ecdsa.PrivateKey
// OCSPStaple contains an optional OCSP response which will be served
// to clients that request it.
OCSPStaple []byte
@@ -327,18 +406,13 @@ type handshakeMessage interface {
unmarshal([]byte) bool
}
-// mutualVersion returns the protocol version to use given the advertised
-// version of the peer.
-func mutualVersion(vers uint16) (uint16, bool) {
- if vers < minVersion {
- return 0, false
- }
- if vers > maxVersion {
- vers = maxVersion
- }
- return vers, true
+// TODO(jsing): Make these available to both crypto/x509 and crypto/tls.
+type dsaSignature struct {
+ R, S *big.Int
}
+type ecdsaSignature dsaSignature
+
var emptyConfig Config
func defaultConfig() *Config {
diff --git a/src/pkg/crypto/tls/conn.go b/src/pkg/crypto/tls/conn.go
index d8c2be00a..2e64b88a6 100644
--- a/src/pkg/crypto/tls/conn.go
+++ b/src/pkg/crypto/tls/conn.go
@@ -146,6 +146,9 @@ func (hc *halfConn) changeCipherSpec() error {
hc.mac = hc.nextMac
hc.nextCipher = nil
hc.nextMac = nil
+ for i := range hc.seq {
+ hc.seq[i] = 0
+ }
return nil
}
@@ -229,8 +232,16 @@ func roundUp(a, b int) int {
return a + (b-a%b)%b
}
-// decrypt checks and strips the mac and decrypts the data in b.
-func (hc *halfConn) decrypt(b *block) (bool, alert) {
+// cbcMode is an interface for block ciphers using cipher block chaining.
+type cbcMode interface {
+ cipher.BlockMode
+ SetIV([]byte)
+}
+
+// decrypt checks and strips the mac and decrypts the data in b. Returns a
+// success boolean, the number of bytes to skip from the start of the record in
+// order to get the application payload, and an optional alert value.
+func (hc *halfConn) decrypt(b *block) (ok bool, prefixLen int, alertValue alert) {
// pull out payload
payload := b.data[recordHeaderLen:]
@@ -240,26 +251,54 @@ func (hc *halfConn) decrypt(b *block) (bool, alert) {
}
paddingGood := byte(255)
+ explicitIVLen := 0
// decrypt
if hc.cipher != nil {
switch c := hc.cipher.(type) {
case cipher.Stream:
c.XORKeyStream(payload, payload)
- case cipher.BlockMode:
+ case cipher.AEAD:
+ explicitIVLen = 8
+ if len(payload) < explicitIVLen {
+ return false, 0, alertBadRecordMAC
+ }
+ nonce := payload[:8]
+ payload = payload[8:]
+
+ var additionalData [13]byte
+ copy(additionalData[:], hc.seq[:])
+ copy(additionalData[8:], b.data[:3])
+ n := len(payload) - c.Overhead()
+ additionalData[11] = byte(n >> 8)
+ additionalData[12] = byte(n)
+ var err error
+ payload, err = c.Open(payload[:0], nonce, payload, additionalData[:])
+ if err != nil {
+ return false, 0, alertBadRecordMAC
+ }
+ b.resize(recordHeaderLen + explicitIVLen + len(payload))
+ case cbcMode:
blockSize := c.BlockSize()
+ if hc.version >= VersionTLS11 {
+ explicitIVLen = blockSize
+ }
- if len(payload)%blockSize != 0 || len(payload) < roundUp(macSize+1, blockSize) {
- return false, alertBadRecordMAC
+ if len(payload)%blockSize != 0 || len(payload) < roundUp(explicitIVLen+macSize+1, blockSize) {
+ return false, 0, alertBadRecordMAC
}
+ if explicitIVLen > 0 {
+ c.SetIV(payload[:explicitIVLen])
+ payload = payload[explicitIVLen:]
+ }
c.CryptBlocks(payload, payload)
- if hc.version == versionSSL30 {
+ if hc.version == VersionSSL30 {
payload, paddingGood = removePaddingSSL30(payload)
} else {
payload, paddingGood = removePadding(payload)
}
- b.resize(recordHeaderLen + len(payload))
+ b.resize(recordHeaderLen + explicitIVLen + len(payload))
// note that we still have a timing side-channel in the
// MAC check, below. An attacker can align the record
@@ -279,25 +318,25 @@ func (hc *halfConn) decrypt(b *block) (bool, alert) {
// check, strip mac
if hc.mac != nil {
if len(payload) < macSize {
- return false, alertBadRecordMAC
+ return false, 0, alertBadRecordMAC
}
// strip mac off payload, b.data
n := len(payload) - macSize
b.data[3] = byte(n >> 8)
b.data[4] = byte(n)
- b.resize(recordHeaderLen + n)
+ b.resize(recordHeaderLen + explicitIVLen + n)
remoteMAC := payload[n:]
- localMAC := hc.mac.MAC(hc.inDigestBuf, hc.seq[0:], b.data)
- hc.incSeq()
+ localMAC := hc.mac.MAC(hc.inDigestBuf, hc.seq[0:], b.data[:recordHeaderLen], payload[:n])
if subtle.ConstantTimeCompare(localMAC, remoteMAC) != 1 || paddingGood != 255 {
- return false, alertBadRecordMAC
+ return false, 0, alertBadRecordMAC
}
hc.inDigestBuf = localMAC
}
+ hc.incSeq()
- return true, 0
+ return true, recordHeaderLen + explicitIVLen, 0
}
// padToBlockSize calculates the needed padding block, if any, for a payload.
@@ -318,11 +357,10 @@ func padToBlockSize(payload []byte, blockSize int) (prefix, finalBlock []byte) {
}
// encrypt encrypts and macs the data in b.
-func (hc *halfConn) encrypt(b *block) (bool, alert) {
+func (hc *halfConn) encrypt(b *block, explicitIVLen int) (bool, alert) {
// mac
if hc.mac != nil {
- mac := hc.mac.MAC(hc.outDigestBuf, hc.seq[0:], b.data)
- hc.incSeq()
+ mac := hc.mac.MAC(hc.outDigestBuf, hc.seq[0:], b.data[:recordHeaderLen], b.data[recordHeaderLen+explicitIVLen:])
n := len(b.data)
b.resize(n + len(mac))
@@ -337,11 +375,30 @@ func (hc *halfConn) encrypt(b *block) (bool, alert) {
switch c := hc.cipher.(type) {
case cipher.Stream:
c.XORKeyStream(payload, payload)
- case cipher.BlockMode:
- prefix, finalBlock := padToBlockSize(payload, c.BlockSize())
- b.resize(recordHeaderLen + len(prefix) + len(finalBlock))
- c.CryptBlocks(b.data[recordHeaderLen:], prefix)
- c.CryptBlocks(b.data[recordHeaderLen+len(prefix):], finalBlock)
+ case cipher.AEAD:
+ payloadLen := len(b.data) - recordHeaderLen - explicitIVLen
+ b.resize(len(b.data) + c.Overhead())
+ nonce := b.data[recordHeaderLen : recordHeaderLen+explicitIVLen]
+ payload := b.data[recordHeaderLen+explicitIVLen:]
+ payload = payload[:payloadLen]
+
+ var additionalData [13]byte
+ copy(additionalData[:], hc.seq[:])
+ copy(additionalData[8:], b.data[:3])
+ additionalData[11] = byte(payloadLen >> 8)
+ additionalData[12] = byte(payloadLen)
+
+ c.Seal(payload[:0], nonce, payload, additionalData[:])
+ case cbcMode:
+ blockSize := c.BlockSize()
+ if explicitIVLen > 0 {
+ c.SetIV(payload[:explicitIVLen])
+ payload = payload[explicitIVLen:]
+ }
+ prefix, finalBlock := padToBlockSize(payload, blockSize)
+ b.resize(recordHeaderLen + explicitIVLen + len(prefix) + len(finalBlock))
+ c.CryptBlocks(b.data[recordHeaderLen+explicitIVLen:], prefix)
+ c.CryptBlocks(b.data[recordHeaderLen+explicitIVLen+len(prefix):], finalBlock)
default:
panic("unknown cipher type")
}
@@ -351,6 +408,7 @@ func (hc *halfConn) encrypt(b *block) (bool, alert) {
n := len(b.data) - recordHeaderLen
b.data[3] = byte(n >> 8)
b.data[4] = byte(n)
+ hc.incSeq()
return true, 0
}
@@ -534,10 +592,11 @@ Again:
// Process message.
b, c.rawInput = c.in.splitBlock(b, recordHeaderLen+n)
- b.off = recordHeaderLen
- if ok, err := c.in.decrypt(b); !ok {
+ ok, off, err := c.in.decrypt(b)
+ if !ok {
return c.sendAlert(err)
}
+ b.off = off
data := b.data[b.off:]
if len(data) > maxPlaintext {
c.sendAlert(alertRecordOverflow)
@@ -637,18 +696,52 @@ func (c *Conn) writeRecord(typ recordType, data []byte) (n int, err error) {
if m > maxPlaintext {
m = maxPlaintext
}
- b.resize(recordHeaderLen + m)
+ explicitIVLen := 0
+ explicitIVIsSeq := false
+
+ var cbc cbcMode
+ if c.out.version >= VersionTLS11 {
+ var ok bool
+ if cbc, ok = c.out.cipher.(cbcMode); ok {
+ explicitIVLen = cbc.BlockSize()
+ }
+ }
+ if explicitIVLen == 0 {
+ if _, ok := c.out.cipher.(cipher.AEAD); ok {
+ explicitIVLen = 8
+ // The AES-GCM construction in TLS has an
+ // explicit nonce so that the nonce can be
+ // random. However, the nonce is only 8 bytes
+ // which is too small for a secure, random
+ // nonce. Therefore we use the sequence number
+ // as the nonce.
+ explicitIVIsSeq = true
+ }
+ }
+ b.resize(recordHeaderLen + explicitIVLen + m)
b.data[0] = byte(typ)
vers := c.vers
if vers == 0 {
- vers = maxVersion
+ // Some TLS servers fail if the record version is
+ // greater than TLS 1.0 for the initial ClientHello.
+ vers = VersionTLS10
}
b.data[1] = byte(vers >> 8)
b.data[2] = byte(vers)
b.data[3] = byte(m >> 8)
b.data[4] = byte(m)
- copy(b.data[recordHeaderLen:], data)
- c.out.encrypt(b)
+ if explicitIVLen > 0 {
+ explicitIV := b.data[recordHeaderLen : recordHeaderLen+explicitIVLen]
+ if explicitIVIsSeq {
+ copy(explicitIV, c.out.seq[:])
+ } else {
+ if _, err = io.ReadFull(c.config.rand(), explicitIV); err != nil {
+ break
+ }
+ }
+ }
+ copy(b.data[recordHeaderLen+explicitIVLen:], data)
+ c.out.encrypt(b, explicitIVLen)
_, err = c.conn.Write(b.data)
if err != nil {
break
@@ -709,7 +802,9 @@ func (c *Conn) readHandshake() (interface{}, error) {
case typeCertificate:
m = new(certificateMsg)
case typeCertificateRequest:
- m = new(certificateRequestMsg)
+ m = &certificateRequestMsg{
+ hasSignatureAndHash: c.vers >= VersionTLS12,
+ }
case typeCertificateStatus:
m = new(certificateStatusMsg)
case typeServerKeyExchange:
@@ -719,7 +814,9 @@ func (c *Conn) readHandshake() (interface{}, error) {
case typeClientKeyExchange:
m = new(clientKeyExchangeMsg)
case typeCertificateVerify:
- m = new(certificateVerifyMsg)
+ m = &certificateVerifyMsg{
+ hasSignatureAndHash: c.vers >= VersionTLS12,
+ }
case typeNextProtocol:
m = new(nextProtoMsg)
case typeFinished:
@@ -768,7 +865,7 @@ func (c *Conn) Write(b []byte) (int, error) {
// http://www.imperialviolet.org/2012/01/15/beastfollowup.html
var m int
- if len(b) > 1 && c.vers <= versionTLS10 {
+ if len(b) > 1 && c.vers <= VersionTLS10 {
if _, ok := c.out.cipher.(cipher.BlockMode); ok {
n, err := c.writeRecord(recordTypeApplicationData, b[:1])
if err != nil {
@@ -792,21 +889,32 @@ func (c *Conn) Read(b []byte) (n int, err error) {
c.in.Lock()
defer c.in.Unlock()
- for c.input == nil && c.error() == nil {
- if err := c.readRecord(recordTypeApplicationData); err != nil {
- // Soft error, like EAGAIN
+ // Some OpenSSL servers send empty records in order to randomize the
+ // CBC IV. So this loop ignores a limited number of empty records.
+ const maxConsecutiveEmptyRecords = 100
+ for emptyRecordCount := 0; emptyRecordCount <= maxConsecutiveEmptyRecords; emptyRecordCount++ {
+ for c.input == nil && c.error() == nil {
+ if err := c.readRecord(recordTypeApplicationData); err != nil {
+ // Soft error, like EAGAIN
+ return 0, err
+ }
+ }
+ if err := c.error(); err != nil {
return 0, err
}
+
+ n, err = c.input.Read(b)
+ if c.input.off >= len(c.input.data) {
+ c.in.freeBlock(c.input)
+ c.input = nil
+ }
+
+ if n != 0 || err != nil {
+ return n, err
+ }
}
- if err := c.error(); err != nil {
- return 0, err
- }
- n, err = c.input.Read(b)
- if c.input.off >= len(c.input.data) {
- c.in.freeBlock(c.input)
- c.input = nil
- }
- return n, nil
+
+ return 0, io.ErrNoProgress
}
// Close closes the connection.
diff --git a/src/pkg/crypto/tls/generate_cert.go b/src/pkg/crypto/tls/generate_cert.go
index 215644d24..b417ea464 100644
--- a/src/pkg/crypto/tls/generate_cert.go
+++ b/src/pkg/crypto/tls/generate_cert.go
@@ -30,7 +30,7 @@ var (
validFrom = flag.String("start-date", "", "Creation date formatted as Jan 1 15:04:05 2011")
validFor = flag.Duration("duration", 365*24*time.Hour, "Duration that certificate is valid for")
isCA = flag.Bool("ca", false, "whether this cert should be its own Certificate Authority")
- rsaBits = flag.Int("rsa-bits", 1024, "Size of RSA key to generate")
+ rsaBits = flag.Int("rsa-bits", 2048, "Size of RSA key to generate")
)
func main() {
diff --git a/src/pkg/crypto/tls/handshake_client.go b/src/pkg/crypto/tls/handshake_client.go
index 7db13bf70..85e4adefc 100644
--- a/src/pkg/crypto/tls/handshake_client.go
+++ b/src/pkg/crypto/tls/handshake_client.go
@@ -6,25 +6,23 @@ package tls
import (
"bytes"
- "crypto"
+ "crypto/ecdsa"
"crypto/rsa"
"crypto/subtle"
"crypto/x509"
+ "encoding/asn1"
"errors"
"io"
"strconv"
)
func (c *Conn) clientHandshake() error {
- finishedHash := newFinishedHash(versionTLS10)
-
if c.config == nil {
c.config = defaultConfig()
}
hello := &clientHelloMsg{
- vers: maxVersion,
- cipherSuites: c.config.cipherSuites(),
+ vers: c.config.maxVersion(),
compressionMethods: []uint8{compressionNone},
random: make([]byte, 32),
ocspStapling: true,
@@ -34,6 +32,25 @@ func (c *Conn) clientHandshake() error {
nextProtoNeg: len(c.config.NextProtos) > 0,
}
+ possibleCipherSuites := c.config.cipherSuites()
+ hello.cipherSuites = make([]uint16, 0, len(possibleCipherSuites))
+
+NextCipherSuite:
+ for _, suiteId := range possibleCipherSuites {
+ for _, suite := range cipherSuites {
+ if suite.id != suiteId {
+ continue
+ }
+ // Don't advertise TLS 1.2-only cipher suites unless
+ // we're attempting TLS 1.2.
+ if hello.vers < VersionTLS12 && suite.flags&suiteTLS12 != 0 {
+ continue
+ }
+ hello.cipherSuites = append(hello.cipherSuites, suiteId)
+ continue NextCipherSuite
+ }
+ }
+
t := uint32(c.config.time().Unix())
hello.random[0] = byte(t >> 24)
hello.random[1] = byte(t >> 16)
@@ -45,7 +62,10 @@ func (c *Conn) clientHandshake() error {
return errors.New("short read from Rand")
}
- finishedHash.Write(hello.marshal())
+ if hello.vers >= VersionTLS12 {
+ hello.signatureAndHashes = supportedSKXSignatureAlgorithms
+ }
+
c.writeRecord(recordTypeHandshake, hello.marshal())
msg, err := c.readHandshake()
@@ -56,16 +76,19 @@ func (c *Conn) clientHandshake() error {
if !ok {
return c.sendAlert(alertUnexpectedMessage)
}
- finishedHash.Write(serverHello.marshal())
- vers, ok := mutualVersion(serverHello.vers)
- if !ok || vers < versionTLS10 {
+ vers, ok := c.config.mutualVersion(serverHello.vers)
+ if !ok || vers < VersionTLS10 {
// TLS 1.0 is the minimum version supported as a client.
return c.sendAlert(alertProtocolVersion)
}
c.vers = vers
c.haveVers = true
+ finishedHash := newFinishedHash(c.vers)
+ finishedHash.Write(hello.marshal())
+ finishedHash.Write(serverHello.marshal())
+
if serverHello.compressionMethod != compressionNone {
return c.sendAlert(alertUnexpectedMessage)
}
@@ -121,7 +144,10 @@ func (c *Conn) clientHandshake() error {
}
}
- if _, ok := certs[0].PublicKey.(*rsa.PublicKey); !ok {
+ switch certs[0].PublicKey.(type) {
+ case *rsa.PublicKey, *ecdsa.PublicKey:
+ break
+ default:
return c.sendAlert(alertUnsupportedCertificate)
}
@@ -148,7 +174,7 @@ func (c *Conn) clientHandshake() error {
return err
}
- keyAgreement := suite.ka()
+ keyAgreement := suite.ka(c.vers)
skx, ok := msg.(*serverKeyExchangeMsg)
if ok {
@@ -165,7 +191,7 @@ func (c *Conn) clientHandshake() error {
}
}
- var certToSend *Certificate
+ var chainToSend *Certificate
var certRequested bool
certReq, ok := msg.(*certificateRequestMsg)
if ok {
@@ -184,12 +210,13 @@ func (c *Conn) clientHandshake() error {
finishedHash.Write(certReq.marshal())
- // For now, we only know how to sign challenges with RSA
- rsaAvail := false
+ var rsaAvail, ecdsaAvail bool
for _, certType := range certReq.certificateTypes {
- if certType == certTypeRSASign {
+ switch certType {
+ case certTypeRSASign:
rsaAvail = true
- break
+ case certTypeECDSASign:
+ ecdsaAvail = true
}
}
@@ -197,35 +224,42 @@ func (c *Conn) clientHandshake() error {
// where SignatureAlgorithm is RSA and the Issuer is in
// certReq.certificateAuthorities
findCert:
- for i, cert := range c.config.Certificates {
- if !rsaAvail {
+ for i, chain := range c.config.Certificates {
+ if !rsaAvail && !ecdsaAvail {
continue
}
- leaf := cert.Leaf
- if leaf == nil {
- if leaf, err = x509.ParseCertificate(cert.Certificate[0]); err != nil {
- c.sendAlert(alertInternalError)
- return errors.New("tls: failed to parse client certificate #" + strconv.Itoa(i) + ": " + err.Error())
+ for j, cert := range chain.Certificate {
+ x509Cert := chain.Leaf
+ // parse the certificate if this isn't the leaf
+ // node, or if chain.Leaf was nil
+ if j != 0 || x509Cert == nil {
+ if x509Cert, err = x509.ParseCertificate(cert); err != nil {
+ c.sendAlert(alertInternalError)
+ return errors.New("tls: failed to parse client certificate #" + strconv.Itoa(i) + ": " + err.Error())
+ }
}
- }
-
- if leaf.PublicKeyAlgorithm != x509.RSA {
- continue
- }
- if len(certReq.certificateAuthorities) == 0 {
- // they gave us an empty list, so just take the
- // first RSA cert from c.config.Certificates
- certToSend = &cert
- break
- }
+ switch {
+ case rsaAvail && x509Cert.PublicKeyAlgorithm == x509.RSA:
+ case ecdsaAvail && x509Cert.PublicKeyAlgorithm == x509.ECDSA:
+ default:
+ continue findCert
+ }
- for _, ca := range certReq.certificateAuthorities {
- if bytes.Equal(leaf.RawIssuer, ca) {
- certToSend = &cert
+ if len(certReq.certificateAuthorities) == 0 {
+ // they gave us an empty list, so just take the
+ // first RSA cert from c.config.Certificates
+ chainToSend = &chain
break findCert
}
+
+ for _, ca := range certReq.certificateAuthorities {
+ if bytes.Equal(x509Cert.RawIssuer, ca) {
+ chainToSend = &chain
+ break findCert
+ }
+ }
}
}
@@ -246,8 +280,8 @@ func (c *Conn) clientHandshake() error {
// certificate to send.
if certRequested {
certMsg = new(certificateMsg)
- if certToSend != nil {
- certMsg.certificates = certToSend.Certificate
+ if chainToSend != nil {
+ certMsg.certificates = chainToSend.Certificate
}
finishedHash.Write(certMsg.marshal())
c.writeRecord(recordTypeHandshake, certMsg.marshal())
@@ -263,12 +297,29 @@ func (c *Conn) clientHandshake() error {
c.writeRecord(recordTypeHandshake, ckx.marshal())
}
- if certToSend != nil {
- certVerify := new(certificateVerifyMsg)
- digest := make([]byte, 0, 36)
- digest = finishedHash.serverMD5.Sum(digest)
- digest = finishedHash.serverSHA1.Sum(digest)
- signed, err := rsa.SignPKCS1v15(c.config.rand(), c.config.Certificates[0].PrivateKey.(*rsa.PrivateKey), crypto.MD5SHA1, digest)
+ if chainToSend != nil {
+ var signed []byte
+ certVerify := &certificateVerifyMsg{
+ hasSignatureAndHash: c.vers >= VersionTLS12,
+ }
+
+ switch key := c.config.Certificates[0].PrivateKey.(type) {
+ case *ecdsa.PrivateKey:
+ digest, _, hashId := finishedHash.hashForClientCertificate(signatureECDSA)
+ r, s, err := ecdsa.Sign(c.config.rand(), key, digest)
+ if err == nil {
+ signed, err = asn1.Marshal(ecdsaSignature{r, s})
+ }
+ certVerify.signatureAndHash.signature = signatureECDSA
+ certVerify.signatureAndHash.hash = hashId
+ case *rsa.PrivateKey:
+ digest, hashFunc, hashId := finishedHash.hashForClientCertificate(signatureRSA)
+ signed, err = rsa.SignPKCS1v15(c.config.rand(), key, hashFunc, digest)
+ certVerify.signatureAndHash.signature = signatureRSA
+ certVerify.signatureAndHash.hash = hashId
+ default:
+ err = errors.New("unknown private key type")
+ }
if err != nil {
return c.sendAlert(alertInternalError)
}
@@ -282,8 +333,14 @@ func (c *Conn) clientHandshake() error {
clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV :=
keysFromMasterSecret(c.vers, masterSecret, hello.random, serverHello.random, suite.macLen, suite.keyLen, suite.ivLen)
- clientCipher := suite.cipher(clientKey, clientIV, false /* not for reading */)
- clientHash := suite.mac(c.vers, clientMAC)
+ var clientCipher interface{}
+ var clientHash macFunction
+ if suite.cipher != nil {
+ clientCipher = suite.cipher(clientKey, clientIV, false /* not for reading */)
+ clientHash = suite.mac(c.vers, clientMAC)
+ } else {
+ clientCipher = suite.aead(clientKey, clientIV)
+ }
c.out.prepareCipherSpec(c.vers, clientCipher, clientHash)
c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
@@ -303,8 +360,14 @@ func (c *Conn) clientHandshake() error {
finishedHash.Write(finished.marshal())
c.writeRecord(recordTypeHandshake, finished.marshal())
- serverCipher := suite.cipher(serverKey, serverIV, true /* for reading */)
- serverHash := suite.mac(c.vers, serverMAC)
+ var serverCipher interface{}
+ var serverHash macFunction
+ if suite.cipher != nil {
+ serverCipher = suite.cipher(serverKey, serverIV, true /* for reading */)
+ serverHash = suite.mac(c.vers, serverMAC)
+ } else {
+ serverCipher = suite.aead(serverKey, serverIV)
+ }
c.in.prepareCipherSpec(c.vers, serverCipher, serverHash)
c.readRecord(recordTypeChangeCipherSpec)
if err := c.error(); err != nil {
diff --git a/src/pkg/crypto/tls/handshake_client_test.go b/src/pkg/crypto/tls/handshake_client_test.go
index 9673947a4..6c564001b 100644
--- a/src/pkg/crypto/tls/handshake_client_test.go
+++ b/src/pkg/crypto/tls/handshake_client_test.go
@@ -39,16 +39,56 @@ func testClientScript(t *testing.T, name string, clientScript [][]byte, config *
}
}
-func TestHandshakeClientRC4(t *testing.T) {
+func TestHandshakeClientRSARC4(t *testing.T) {
var config = *testConfig
config.CipherSuites = []uint16{TLS_RSA_WITH_RC4_128_SHA}
- testClientScript(t, "RC4", rc4ClientScript, &config)
+ testClientScript(t, "RSA-RC4", rsaRC4ClientScript, &config)
}
-func TestHandshakeClientECDHEAES(t *testing.T) {
+func TestHandshakeClientECDHERSAAES(t *testing.T) {
var config = *testConfig
config.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA}
- testClientScript(t, "ECDHE-AES", ecdheAESClientScript, &config)
+ testClientScript(t, "ECDHE-RSA-AES", ecdheRSAAESClientScript, &config)
+}
+
+func TestHandshakeClientECDHECDSAAES(t *testing.T) {
+ var config = *testConfig
+ config.CipherSuites = []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA}
+ config.Certificates = nil
+ config.BuildNameToCertificate()
+ testClientScript(t, "ECDHE-ECDSA-AES", ecdheECDSAAESClientScript, &config)
+}
+
+func TestLongClientCerticiateChain(t *testing.T) {
+ config := *testConfig
+ cert, _ := X509KeyPair(testClientChainCertificate, testClientChainCertificate)
+ config.Certificates = []Certificate{cert}
+ testClientScript(t, "Long client certificate chains", clientChainCertificateScript, &config)
+}
+
+func TestHandshakeClientTLS11(t *testing.T) {
+ var config = *testConfig
+ config.MaxVersion = VersionTLS11
+ config.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA}
+ testClientScript(t, "TLS11-ECDHE-AES", tls11ECDHEAESClientScript, &config)
+}
+
+func TestHandshakeClientTLS12(t *testing.T) {
+ config := *testConfig
+ config.MaxVersion = VersionTLS12
+ config.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA}
+ cert, _ := X509KeyPair(testClientChainCertificate, testClientChainCertificate)
+ config.Certificates = []Certificate{cert}
+ testClientScript(t, "TLS12", clientTLS12Script, &config)
+}
+
+func TestHandshakeClientTLS12ClientCert(t *testing.T) {
+ config := *testConfig
+ config.MaxVersion = VersionTLS12
+ config.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}
+ cert, _ := X509KeyPair(testClientChainCertificate, testClientChainCertificate)
+ config.Certificates = []Certificate{cert}
+ testClientScript(t, "TLS12ClientCert", clientTLS12ClientCertScript, &config)
}
var connect = flag.Bool("connect", false, "connect to a TLS server on :10443")
@@ -79,6 +119,48 @@ func TestRunClient(t *testing.T) {
record.WriteTo(os.Stdout)
}
+func TestEmptyRecords(t *testing.T) {
+ // emptyRecordScript contains a TLS connection with an empty record as
+ // the first application data from the server. This test ensures that
+ // the empty record doesn't cause (0, nil) to be returned from
+ // Conn.Read.
+ config := *testConfig
+ config.CipherSuites = []uint16{TLS_RSA_WITH_AES_256_CBC_SHA}
+
+ c, s := net.Pipe()
+ cli := Client(c, &config)
+ go func() {
+ buf := make([]byte, 1024)
+ n, err := cli.Read(buf)
+ defer c.Close()
+ defer cli.Close()
+
+ if err != nil {
+ t.Fatalf("error reading from tls.Client: %s", err)
+ }
+ const expectedLength = 197
+ if n != expectedLength {
+ t.Fatalf("incorrect length reading from tls.Client, got %d, want %d", n, expectedLength)
+ }
+ }()
+
+ defer c.Close()
+ for i, b := range emptyRecordScript {
+ if i%2 == 1 {
+ s.Write(b)
+ continue
+ }
+ bb := make([]byte, len(b))
+ _, err := io.ReadFull(s, bb)
+ if err != nil {
+ t.Fatalf("#%d: %s", i, err)
+ }
+ if !bytes.Equal(b, bb) {
+ t.Fatalf("#%d: mismatch on read: got:%x want:%x", i, bb, b)
+ }
+ }
+}
+
// Script of interaction with gnutls implementation.
// The values for this test are obtained by building and running in client mode:
// % go test -test.run "TestRunClient" -connect
@@ -110,7 +192,7 @@ func TestRunClient(t *testing.T) {
// CSqGSIb3DQEBBQUAA0EAhTZAc8G7GtrUWZ8tonAxRnTsg26oyDxRrzms7EC86CJG
// HZnWRiok1IsFCEv7NRFukrt3uuQSu/TIXpyBqJdgTA==
// -----END CERTIFICATE-----
-var rc4ClientScript = [][]byte{
+var rsaRC4ClientScript = [][]byte{
{
0x16, 0x03, 0x01, 0x00, 0x4a, 0x01, 0x00, 0x00,
0x46, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -231,7 +313,7 @@ var rc4ClientScript = [][]byte{
},
}
-var ecdheAESClientScript = [][]byte{
+var ecdheRSAAESClientScript = [][]byte{
{
0x16, 0x03, 0x01, 0x00, 0x4a, 0x01, 0x00, 0x00,
0x46, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -406,3 +488,2563 @@ var ecdheAESClientScript = [][]byte{
0x57, 0x33, 0xc3, 0xbc, 0x3f, 0x7a, 0x4d,
},
}
+
+var emptyRecordScript = [][]byte{
+ {
+ 0x16, 0x03, 0x01, 0x00, 0x4a, 0x01, 0x00, 0x00,
+ 0x46, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x35,
+ 0x01, 0x00, 0x00, 0x1b, 0x00, 0x05, 0x00, 0x05,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00,
+ 0x08, 0x00, 0x06, 0x00, 0x17, 0x00, 0x18, 0x00,
+ 0x19, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00,
+ },
+ {
+ 0x16, 0x03, 0x01, 0x00, 0x4a, 0x02, 0x00, 0x00,
+ 0x46, 0x03, 0x01, 0x51, 0x71, 0x8e, 0x03, 0x02,
+ 0xef, 0x09, 0xf2, 0x0e, 0xf5, 0x3b, 0x29, 0x9a,
+ 0xa8, 0x8b, 0x46, 0xa3, 0xd4, 0xb4, 0xc1, 0x14,
+ 0xc3, 0x19, 0x99, 0xba, 0x3d, 0x78, 0xcf, 0x50,
+ 0xd1, 0xe7, 0x26, 0x20, 0xa0, 0x37, 0x6d, 0xc9,
+ 0xae, 0x93, 0x33, 0x81, 0x20, 0xe3, 0xc1, 0x90,
+ 0x64, 0x6e, 0x67, 0x93, 0xdb, 0xb4, 0x04, 0x16,
+ 0xc4, 0x25, 0xdd, 0x10, 0x79, 0x3c, 0x18, 0x0a,
+ 0x7c, 0xfd, 0x28, 0x65, 0x00, 0x35, 0x00, 0x16,
+ 0x03, 0x01, 0x09, 0x9e, 0x0b, 0x00, 0x09, 0x9a,
+ 0x00, 0x09, 0x97, 0x00, 0x04, 0xea, 0x30, 0x82,
+ 0x04, 0xe6, 0x30, 0x82, 0x03, 0xce, 0xa0, 0x03,
+ 0x02, 0x01, 0x02, 0x02, 0x11, 0x00, 0xff, 0xab,
+ 0x02, 0x93, 0xe0, 0x72, 0x99, 0x18, 0x6c, 0x9e,
+ 0x96, 0xb8, 0xb9, 0xf7, 0x47, 0xcb, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x41, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x46, 0x52, 0x31, 0x12, 0x30, 0x10,
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x09, 0x47,
+ 0x41, 0x4e, 0x44, 0x49, 0x20, 0x53, 0x41, 0x53,
+ 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x15, 0x47, 0x61, 0x6e, 0x64, 0x69,
+ 0x20, 0x53, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72,
+ 0x64, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41,
+ 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x30, 0x31,
+ 0x31, 0x34, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x5a, 0x17, 0x0d, 0x31, 0x34, 0x30, 0x31, 0x31,
+ 0x34, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a,
+ 0x30, 0x62, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03,
+ 0x55, 0x04, 0x0b, 0x13, 0x18, 0x44, 0x6f, 0x6d,
+ 0x61, 0x69, 0x6e, 0x20, 0x43, 0x6f, 0x6e, 0x74,
+ 0x72, 0x6f, 0x6c, 0x20, 0x56, 0x61, 0x6c, 0x69,
+ 0x64, 0x61, 0x74, 0x65, 0x64, 0x31, 0x24, 0x30,
+ 0x22, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1b,
+ 0x47, 0x61, 0x6e, 0x64, 0x69, 0x20, 0x53, 0x74,
+ 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x20, 0x57,
+ 0x69, 0x6c, 0x64, 0x63, 0x61, 0x72, 0x64, 0x20,
+ 0x53, 0x53, 0x4c, 0x31, 0x17, 0x30, 0x15, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x14, 0x0e, 0x2a, 0x2e,
+ 0x66, 0x72, 0x65, 0x65, 0x6e, 0x6f, 0x64, 0x65,
+ 0x2e, 0x6e, 0x65, 0x74, 0x30, 0x82, 0x01, 0x22,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
+ 0x02, 0x82, 0x01, 0x01, 0x00, 0xdc, 0xe3, 0xfd,
+ 0xce, 0xc1, 0x66, 0x62, 0x28, 0x8b, 0x99, 0x65,
+ 0x72, 0x52, 0x88, 0x93, 0x5b, 0x3f, 0x8d, 0xde,
+ 0x2b, 0xb0, 0xa0, 0xf4, 0xbd, 0xb4, 0x07, 0x5f,
+ 0x9e, 0x01, 0x47, 0x60, 0x57, 0x5f, 0xdf, 0xdc,
+ 0x63, 0x28, 0x1c, 0x1e, 0x5b, 0xc8, 0xe6, 0x29,
+ 0xdd, 0xeb, 0x26, 0x63, 0xd5, 0xbf, 0x83, 0xb2,
+ 0x2d, 0xcd, 0x2c, 0xa0, 0xb6, 0x91, 0xad, 0xaf,
+ 0x95, 0x21, 0x1d, 0x1f, 0x39, 0x8d, 0x3e, 0x17,
+ 0xd6, 0xbd, 0x99, 0xf5, 0x6c, 0xd4, 0xcb, 0x79,
+ 0x12, 0x3e, 0x11, 0xb9, 0x7e, 0x62, 0xbc, 0x2d,
+ 0xbf, 0xe0, 0x55, 0x1b, 0x5c, 0x1e, 0xce, 0x31,
+ 0xd9, 0xf8, 0x56, 0x68, 0x95, 0x2b, 0x15, 0x84,
+ 0x35, 0xae, 0x98, 0x2c, 0x63, 0x01, 0xb2, 0x0d,
+ 0xab, 0xa8, 0x61, 0xef, 0x7f, 0x15, 0x2c, 0x6d,
+ 0xf7, 0x67, 0x1d, 0xb8, 0x8d, 0xf6, 0xa2, 0x1c,
+ 0x4e, 0x85, 0xf0, 0xea, 0x1a, 0x2b, 0xc8, 0xac,
+ 0x70, 0x86, 0x9a, 0xbb, 0x9e, 0x9d, 0xbd, 0xc9,
+ 0x87, 0x2b, 0x9f, 0x5e, 0x40, 0x44, 0x9b, 0xba,
+ 0x96, 0x45, 0x24, 0xbc, 0x49, 0xb8, 0xfe, 0x26,
+ 0x3a, 0x1d, 0x1a, 0x0a, 0x3a, 0x90, 0x9c, 0x75,
+ 0x51, 0x59, 0x89, 0x98, 0x1a, 0x56, 0xe1, 0x3a,
+ 0x1a, 0xba, 0xff, 0xb4, 0x37, 0x7d, 0xd8, 0x99,
+ 0xe2, 0xeb, 0x45, 0x27, 0xe2, 0x42, 0x42, 0x46,
+ 0xbb, 0x00, 0x29, 0x9f, 0x30, 0xc9, 0x1e, 0x6c,
+ 0xce, 0x59, 0x0e, 0xbe, 0x16, 0x03, 0x31, 0xec,
+ 0x10, 0xc1, 0x6d, 0xca, 0x9d, 0x5f, 0x6d, 0xf1,
+ 0x26, 0x11, 0xe5, 0x50, 0xa1, 0xbb, 0x67, 0xb2,
+ 0xe0, 0x2b, 0xed, 0x76, 0x5b, 0xc7, 0x68, 0xc0,
+ 0x18, 0xad, 0x91, 0x9e, 0xb5, 0xd4, 0x4d, 0x21,
+ 0xcd, 0x98, 0xd9, 0xe0, 0x05, 0x0a, 0x4d, 0x24,
+ 0xa3, 0xe6, 0x12, 0x04, 0xdd, 0x50, 0xe6, 0xc8,
+ 0x7a, 0x69, 0xb9, 0x32, 0x43, 0x02, 0x03, 0x01,
+ 0x00, 0x01, 0xa3, 0x82, 0x01, 0xb6, 0x30, 0x82,
+ 0x01, 0xb2, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d,
+ 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xb6,
+ 0xa8, 0xff, 0xa2, 0xa8, 0x2f, 0xd0, 0xa6, 0xcd,
+ 0x4b, 0xb1, 0x68, 0xf3, 0xe7, 0x50, 0x10, 0x31,
+ 0xa7, 0x79, 0x21, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x62, 0x37,
+ 0xd4, 0x3c, 0xbf, 0xd9, 0xc2, 0x99, 0xf3, 0x28,
+ 0x3e, 0xdb, 0xca, 0xee, 0xf3, 0xb3, 0xc8, 0x73,
+ 0xb0, 0x3c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02,
+ 0x05, 0xa0, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d,
+ 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00,
+ 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04,
+ 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30,
+ 0x60, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x59,
+ 0x30, 0x57, 0x30, 0x4b, 0x06, 0x0b, 0x2b, 0x06,
+ 0x01, 0x04, 0x01, 0xb2, 0x31, 0x01, 0x02, 0x02,
+ 0x1a, 0x30, 0x3c, 0x30, 0x3a, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16,
+ 0x2e, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x77, 0x77, 0x77, 0x2e, 0x67, 0x61, 0x6e, 0x64,
+ 0x69, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x63, 0x6f,
+ 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x73, 0x2f,
+ 0x66, 0x72, 0x2f, 0x73, 0x73, 0x6c, 0x2f, 0x63,
+ 0x70, 0x73, 0x2f, 0x70, 0x64, 0x66, 0x2f, 0x30,
+ 0x08, 0x06, 0x06, 0x67, 0x81, 0x0c, 0x01, 0x02,
+ 0x01, 0x30, 0x3c, 0x06, 0x03, 0x55, 0x1d, 0x1f,
+ 0x04, 0x35, 0x30, 0x33, 0x30, 0x31, 0xa0, 0x2f,
+ 0xa0, 0x2d, 0x86, 0x2b, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x67,
+ 0x61, 0x6e, 0x64, 0x69, 0x2e, 0x6e, 0x65, 0x74,
+ 0x2f, 0x47, 0x61, 0x6e, 0x64, 0x69, 0x53, 0x74,
+ 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x53, 0x53,
+ 0x4c, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30,
+ 0x6a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x01, 0x01, 0x04, 0x5e, 0x30, 0x5c, 0x30,
+ 0x37, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x30, 0x02, 0x86, 0x2b, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x74, 0x2e,
+ 0x67, 0x61, 0x6e, 0x64, 0x69, 0x2e, 0x6e, 0x65,
+ 0x74, 0x2f, 0x47, 0x61, 0x6e, 0x64, 0x69, 0x53,
+ 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x53,
+ 0x53, 0x4c, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x74,
+ 0x30, 0x21, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x30, 0x01, 0x86, 0x15, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73,
+ 0x70, 0x2e, 0x67, 0x61, 0x6e, 0x64, 0x69, 0x2e,
+ 0x6e, 0x65, 0x74, 0x30, 0x27, 0x06, 0x03, 0x55,
+ 0x1d, 0x11, 0x04, 0x20, 0x30, 0x1e, 0x82, 0x0e,
+ 0x2a, 0x2e, 0x66, 0x72, 0x65, 0x65, 0x6e, 0x6f,
+ 0x64, 0x65, 0x2e, 0x6e, 0x65, 0x74, 0x82, 0x0c,
+ 0x66, 0x72, 0x65, 0x65, 0x6e, 0x6f, 0x64, 0x65,
+ 0x2e, 0x6e, 0x65, 0x74, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00,
+ 0x5b, 0x4a, 0x3a, 0x1d, 0x75, 0xe0, 0xc0, 0x9e,
+ 0xc9, 0x16, 0x66, 0x7f, 0x73, 0x95, 0x6e, 0x35,
+ 0xe4, 0x27, 0xfa, 0x8c, 0x9d, 0xee, 0xb1, 0x37,
+ 0x42, 0x3f, 0x54, 0x6a, 0x9d, 0x41, 0x84, 0x57,
+ 0xe1, 0x03, 0x3d, 0x69, 0x61, 0x77, 0x3b, 0x91,
+ 0xa2, 0x70, 0x94, 0xb6, 0x8e, 0x41, 0x63, 0x70,
+ 0xf2, 0x16, 0x04, 0x50, 0x05, 0x14, 0xfb, 0x59,
+ 0x7d, 0x89, 0x09, 0x3f, 0xb6, 0xef, 0xca, 0x3c,
+ 0x89, 0x88, 0x08, 0xe9, 0xa1, 0xf3, 0x33, 0x31,
+ 0x05, 0x4d, 0x70, 0xff, 0xdd, 0xa7, 0xd2, 0xe2,
+ 0xa0, 0x94, 0x3a, 0xf7, 0xc2, 0x9f, 0xad, 0x2b,
+ 0x2e, 0x20, 0xfa, 0x6c, 0xe1, 0xfc, 0xe6, 0x62,
+ 0x22, 0xa1, 0x38, 0x93, 0xec, 0x3e, 0xce, 0xfd,
+ 0x1f, 0xdd, 0xd4, 0x7c, 0x39, 0x46, 0x8b, 0xb4,
+ 0x64, 0xfa, 0xa1, 0x46, 0x87, 0x78, 0x2c, 0xd7,
+ 0x9c, 0xdd, 0x60, 0xd6, 0xda, 0x8e, 0xd8, 0x29,
+ 0x6d, 0x61, 0xa7, 0x29, 0x07, 0x76, 0xfc, 0xf9,
+ 0xbd, 0xfd, 0x14, 0xeb, 0x44, 0x70, 0xff, 0xd0,
+ 0x23, 0x99, 0x83, 0xc5, 0x5c, 0x56, 0x88, 0xaa,
+ 0x34, 0xda, 0xa6, 0xb3, 0x9a, 0xbf, 0xda, 0x58,
+ 0x1e, 0xa4, 0xb8, 0xc0, 0x40, 0x9d, 0xf0, 0xfc,
+ 0xf1, 0x23, 0xc2, 0xbc, 0x59, 0xe1, 0x82, 0xed,
+ 0x5d, 0xfb, 0x99, 0xaf, 0xf5, 0xf5, 0x15, 0xb8,
+ 0x8b, 0x59, 0xce, 0xaa, 0xca, 0xdf, 0xdc, 0x94,
+ 0x11, 0xe0, 0x96, 0xbf, 0x9f, 0x54, 0xa4, 0x9f,
+ 0x54, 0x36, 0x4a, 0xe8, 0x93, 0xda, 0xf4, 0x8c,
+ 0xb0, 0x6b, 0x8d, 0x4a, 0x9e, 0x11, 0xae, 0xcb,
+ 0xcb, 0x33, 0x8a, 0x4d, 0xcd, 0x4e, 0xa5, 0x9b,
+ 0xe9, 0x14, 0x46, 0x43, 0x9b, 0x96, 0x5f, 0x6d,
+ 0xf2, 0xea, 0x40, 0xef, 0x14, 0xc3, 0x99, 0x9f,
+ 0x23, 0x1e, 0xa5, 0x13, 0xab, 0x08, 0xea, 0x8f,
+ 0x68, 0x5b, 0x7d, 0x71, 0xdf, 0x18, 0xd1, 0x57,
+ 0x00, 0x04, 0xa7, 0x30, 0x82, 0x04, 0xa3, 0x30,
+ 0x82, 0x03, 0x8b, 0xa0, 0x03, 0x02, 0x01, 0x02,
+ 0x02, 0x10, 0x5a, 0xb6, 0x1d, 0xac, 0x1e, 0x4d,
+ 0xa2, 0x06, 0x14, 0xc7, 0x55, 0x3d, 0x3d, 0xa9,
+ 0xb2, 0xdc, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05,
+ 0x00, 0x30, 0x81, 0x97, 0x31, 0x0b, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
+ 0x04, 0x08, 0x13, 0x02, 0x55, 0x54, 0x31, 0x17,
+ 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13,
+ 0x0e, 0x53, 0x61, 0x6c, 0x74, 0x20, 0x4c, 0x61,
+ 0x6b, 0x65, 0x20, 0x43, 0x69, 0x74, 0x79, 0x31,
+ 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0a,
+ 0x13, 0x15, 0x54, 0x68, 0x65, 0x20, 0x55, 0x53,
+ 0x45, 0x52, 0x54, 0x52, 0x55, 0x53, 0x54, 0x20,
+ 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31,
+ 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x75, 0x73, 0x65,
+ 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x13, 0x16, 0x55, 0x54, 0x4e,
+ 0x2d, 0x55, 0x53, 0x45, 0x52, 0x46, 0x69, 0x72,
+ 0x73, 0x74, 0x2d, 0x48, 0x61, 0x72, 0x64, 0x77,
+ 0x61, 0x72, 0x65, 0x30, 0x1e, 0x17, 0x0d, 0x30,
+ 0x38, 0x31, 0x30, 0x32, 0x33, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x30,
+ 0x30, 0x35, 0x33, 0x30, 0x31, 0x30, 0x34, 0x38,
+ 0x33, 0x38, 0x5a, 0x30, 0x41, 0x31, 0x0b, 0x30,
+ 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x46, 0x52, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03,
+ 0x55, 0x04, 0x0a, 0x13, 0x09, 0x47, 0x41, 0x4e,
+ 0x44, 0x49, 0x20, 0x53, 0x41, 0x53, 0x31, 0x1e,
+ 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
+ 0x15, 0x47, 0x61, 0x6e, 0x64, 0x69, 0x20, 0x53,
+ 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x20,
+ 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x30, 0x82,
+ 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82,
+ 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb6,
+ 0x54, 0x3d, 0xa5, 0xdb, 0x0d, 0x22, 0x78, 0x50,
+ 0x6a, 0x5a, 0x23, 0x89, 0x3f, 0x97, 0xa1, 0xd4,
+ 0x07, 0x1a, 0xa9, 0x58, 0x08, 0x9b, 0xa0, 0x15,
+ 0xc3, 0x32, 0xb6, 0xb7, 0xf1, 0xe8, 0xb9, 0xa5,
+ 0x6f, 0xad, 0x37, 0xf6, 0x6e, 0x71, 0x1b, 0xb4,
+ 0x75, 0x2d, 0x48, 0x5e, 0x9f, 0xc6, 0x15, 0xaa,
+ 0x81, 0xef, 0xe5, 0xc4, 0x88, 0x95, 0x8a, 0x3a,
+ 0x6c, 0x77, 0xcc, 0xb5, 0xcd, 0x65, 0xe4, 0x67,
+ 0xe5, 0x73, 0xc9, 0x50, 0x52, 0x94, 0xc1, 0x27,
+ 0x49, 0x3e, 0xa0, 0x6b, 0x41, 0x16, 0x41, 0xb6,
+ 0x94, 0x99, 0x41, 0xae, 0x3e, 0xcb, 0xe2, 0x06,
+ 0x46, 0x09, 0xe9, 0x4d, 0xbe, 0xc9, 0x4c, 0x55,
+ 0xa9, 0x18, 0x7e, 0xa6, 0xdf, 0x6e, 0xfd, 0x4a,
+ 0xb2, 0xcc, 0x6c, 0x4e, 0xd9, 0xc8, 0x50, 0x15,
+ 0x93, 0xb3, 0xf2, 0xe9, 0xe3, 0xc2, 0x6a, 0xad,
+ 0x3a, 0xd5, 0xfb, 0xc3, 0x79, 0x50, 0x9f, 0x25,
+ 0x79, 0x29, 0xb2, 0x47, 0x64, 0x7c, 0x20, 0x3e,
+ 0xe2, 0x08, 0x4d, 0x93, 0x29, 0x14, 0xb6, 0x34,
+ 0x6e, 0xcf, 0x71, 0x46, 0x7e, 0x76, 0x10, 0xf4,
+ 0xfd, 0x6c, 0xaa, 0x01, 0xd2, 0xc2, 0x06, 0xde,
+ 0x92, 0x83, 0xcc, 0x58, 0x90, 0x2e, 0x92, 0xde,
+ 0x1e, 0x65, 0xb7, 0x63, 0x2f, 0x3d, 0xb2, 0xeb,
+ 0x70, 0x8c, 0x4c, 0xe0, 0xbe, 0x15, 0x9d, 0xde,
+ 0xc1, 0x4d, 0x56, 0xf8, 0x0b, 0xc6, 0x8e, 0x07,
+ 0xb9, 0x5d, 0xdf, 0x95, 0xf0, 0x7b, 0x40, 0x1f,
+ 0x1a, 0x2c, 0xd7, 0x9c, 0x2b, 0x4b, 0x76, 0xf4,
+ 0x59, 0xf5, 0x43, 0xc1, 0x2c, 0x66, 0x10, 0x9e,
+ 0x9e, 0x66, 0x96, 0x60, 0x9d, 0x1c, 0x74, 0x1b,
+ 0x4e, 0x18, 0x5c, 0x08, 0xb0, 0x6e, 0x6c, 0xca,
+ 0x69, 0x1a, 0x02, 0xe9, 0xbb, 0xca, 0x78, 0xef,
+ 0x66, 0x2e, 0xe3, 0x32, 0xfd, 0x41, 0x5c, 0x95,
+ 0x74, 0x81, 0x4d, 0xf4, 0xda, 0xfe, 0x4b, 0x02,
+ 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x3e,
+ 0x30, 0x82, 0x01, 0x3a, 0x30, 0x1f, 0x06, 0x03,
+ 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80,
+ 0x14, 0xa1, 0x72, 0x5f, 0x26, 0x1b, 0x28, 0x98,
+ 0x43, 0x95, 0x5d, 0x07, 0x37, 0xd5, 0x85, 0x96,
+ 0x9d, 0x4b, 0xd2, 0xc3, 0x45, 0x30, 0x1d, 0x06,
+ 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14,
+ 0xb6, 0xa8, 0xff, 0xa2, 0xa8, 0x2f, 0xd0, 0xa6,
+ 0xcd, 0x4b, 0xb1, 0x68, 0xf3, 0xe7, 0x50, 0x10,
+ 0x31, 0xa7, 0x79, 0x21, 0x30, 0x0e, 0x06, 0x03,
+ 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04,
+ 0x03, 0x02, 0x01, 0x06, 0x30, 0x12, 0x06, 0x03,
+ 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08,
+ 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00,
+ 0x30, 0x18, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04,
+ 0x11, 0x30, 0x0f, 0x30, 0x0d, 0x06, 0x0b, 0x2b,
+ 0x06, 0x01, 0x04, 0x01, 0xb2, 0x31, 0x01, 0x02,
+ 0x02, 0x1a, 0x30, 0x44, 0x06, 0x03, 0x55, 0x1d,
+ 0x1f, 0x04, 0x3d, 0x30, 0x3b, 0x30, 0x39, 0xa0,
+ 0x37, 0xa0, 0x35, 0x86, 0x33, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e,
+ 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73,
+ 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x55, 0x54,
+ 0x4e, 0x2d, 0x55, 0x53, 0x45, 0x52, 0x46, 0x69,
+ 0x72, 0x73, 0x74, 0x2d, 0x48, 0x61, 0x72, 0x64,
+ 0x77, 0x61, 0x72, 0x65, 0x2e, 0x63, 0x72, 0x6c,
+ 0x30, 0x74, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x01, 0x01, 0x04, 0x68, 0x30, 0x66,
+ 0x30, 0x3d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x30, 0x02, 0x86, 0x31, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x74,
+ 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75,
+ 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x55,
+ 0x54, 0x4e, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75,
+ 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
+ 0x5f, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x74, 0x30,
+ 0x25, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x30, 0x01, 0x86, 0x19, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70,
+ 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75,
+ 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x01, 0x00, 0x19, 0x53, 0xbf, 0x03, 0x3d, 0x9b,
+ 0xe2, 0x6b, 0x5a, 0xfd, 0xba, 0x49, 0x1f, 0x4f,
+ 0xec, 0xe1, 0xc6, 0x82, 0x39, 0x3c, 0xd2, 0x03,
+ 0x04, 0x0f, 0xab, 0x7b, 0x3e, 0x82, 0xa9, 0x85,
+ 0x10, 0x1f, 0xf4, 0xde, 0x32, 0xaf, 0x58, 0x3f,
+ 0xff, 0x70, 0xf3, 0x30, 0x1d, 0x97, 0x2d, 0x4c,
+ 0x9a, 0xe2, 0xec, 0x0c, 0x3e, 0x14, 0x2d, 0x2f,
+ 0x98, 0x48, 0x9d, 0xae, 0x16, 0x6a, 0xac, 0x2d,
+ 0x42, 0xaa, 0xb5, 0x64, 0xa4, 0x70, 0xbb, 0xeb,
+ 0x73, 0x94, 0x7b, 0x46, 0x4c, 0xe7, 0x7a, 0x14,
+ 0x76, 0x5b, 0x4c, 0x1d, 0x84, 0xa1, 0x20, 0x74,
+ 0x1f, 0x2e, 0x4b, 0x5c, 0x70, 0x88, 0xdc, 0xbd,
+ 0xf7, 0x19, 0x3d, 0xed, 0x59, 0x0d, 0xe2, 0x3f,
+ 0x26, 0xe2, 0x9c, 0xac, 0xa4, 0x3c, 0x95, 0x1c,
+ 0xf8, 0xbe, 0x8c, 0x03, 0xae, 0xf0, 0xe5, 0x9c,
+ 0x4d, 0xbc, 0xc7, 0x9b, 0x58, 0x00, 0xbf, 0xaf,
+ 0xad, 0xfa, 0x37, 0x6e, 0x71, 0x6d, 0x18, 0x34,
+ 0x0e, 0xc1, 0xea, 0x6a, 0xf8, 0x0d, 0xdf, 0x69,
+ 0x54, 0x56, 0x15, 0xf2, 0x28, 0xb3, 0xfe, 0xa4,
+ 0x63, 0xec, 0xc5, 0x04, 0x64, 0x60, 0xbb, 0xfe,
+ 0x2a, 0xf0, 0xf4, 0x87, 0xa1, 0xb0, 0xae, 0xbd,
+ 0xaa, 0xe4, 0x2f, 0xe3, 0x03, 0x0b, 0x2f, 0x66,
+ 0x5f, 0x85, 0xa4, 0x32, 0x7b, 0x46, 0xed, 0x25,
+ 0x0c, 0xe7, 0xf1, 0xb7, 0xe7, 0x19, 0xfd, 0x60,
+ 0xba, 0x5f, 0x87, 0x77, 0xde, 0x98, 0x07, 0x96,
+ 0xe4, 0x5e, 0xea, 0x63, 0x7d, 0xa8, 0xde, 0x55,
+ 0xda, 0x61, 0x5c, 0x3c, 0x90, 0x83, 0x43, 0x04,
+ 0x07, 0x3c, 0xdd, 0xf3, 0xf8, 0x9f, 0x06, 0x52,
+ 0x0a, 0xde, 0xc7, 0xb6, 0x7b, 0x8f, 0xe1, 0x11,
+ 0xf7, 0x04, 0x7a, 0x35, 0xff, 0x6a, 0xbc, 0x5b,
+ 0xc7, 0x50, 0x49, 0x08, 0x70, 0x6f, 0x94, 0x43,
+ 0xcd, 0x9e, 0xc7, 0x70, 0xf1, 0xdb, 0xd0, 0x6d,
+ 0xda, 0x8f, 0x16, 0x03, 0x01, 0x00, 0x0e, 0x0d,
+ 0x00, 0x00, 0x06, 0x03, 0x01, 0x02, 0x40, 0x00,
+ 0x00, 0x0e, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x16, 0x03, 0x01, 0x02, 0xbe, 0x0b, 0x00, 0x02,
+ 0xba, 0x00, 0x02, 0xb7, 0x00, 0x02, 0xb4, 0x30,
+ 0x82, 0x02, 0xb0, 0x30, 0x82, 0x02, 0x19, 0xa0,
+ 0x03, 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0x85,
+ 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
+ 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30,
+ 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a,
+ 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61,
+ 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03,
+ 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74,
+ 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69,
+ 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74,
+ 0x79, 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17,
+ 0x0d, 0x31, 0x30, 0x30, 0x34, 0x32, 0x34, 0x30,
+ 0x39, 0x30, 0x39, 0x33, 0x38, 0x5a, 0x17, 0x0d,
+ 0x31, 0x31, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39,
+ 0x30, 0x39, 0x33, 0x38, 0x5a, 0x30, 0x45, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11,
+ 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53,
+ 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74,
+ 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55,
+ 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65,
+ 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64,
+ 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79,
+ 0x20, 0x4c, 0x74, 0x64, 0x30, 0x81, 0x9f, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81,
+ 0x8d, 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81,
+ 0x00, 0xbb, 0x79, 0xd6, 0xf5, 0x17, 0xb5, 0xe5,
+ 0xbf, 0x46, 0x10, 0xd0, 0xdc, 0x69, 0xbe, 0xe6,
+ 0x2b, 0x07, 0x43, 0x5a, 0xd0, 0x03, 0x2d, 0x8a,
+ 0x7a, 0x43, 0x85, 0xb7, 0x14, 0x52, 0xe7, 0xa5,
+ 0x65, 0x4c, 0x2c, 0x78, 0xb8, 0x23, 0x8c, 0xb5,
+ 0xb4, 0x82, 0xe5, 0xde, 0x1f, 0x95, 0x3b, 0x7e,
+ 0x62, 0xa5, 0x2c, 0xa5, 0x33, 0xd6, 0xfe, 0x12,
+ 0x5c, 0x7a, 0x56, 0xfc, 0xf5, 0x06, 0xbf, 0xfa,
+ 0x58, 0x7b, 0x26, 0x3f, 0xb5, 0xcd, 0x04, 0xd3,
+ 0xd0, 0xc9, 0x21, 0x96, 0x4a, 0xc7, 0xf4, 0x54,
+ 0x9f, 0x5a, 0xbf, 0xef, 0x42, 0x71, 0x00, 0xfe,
+ 0x18, 0x99, 0x07, 0x7f, 0x7e, 0x88, 0x7d, 0x7d,
+ 0xf1, 0x04, 0x39, 0xc4, 0xa2, 0x2e, 0xdb, 0x51,
+ 0xc9, 0x7c, 0xe3, 0xc0, 0x4c, 0x3b, 0x32, 0x66,
+ 0x01, 0xcf, 0xaf, 0xb1, 0x1d, 0xb8, 0x71, 0x9a,
+ 0x1d, 0xdb, 0xdb, 0x89, 0x6b, 0xae, 0xda, 0x2d,
+ 0x79, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81,
+ 0xa7, 0x30, 0x81, 0xa4, 0x30, 0x1d, 0x06, 0x03,
+ 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb1,
+ 0xad, 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb,
+ 0x69, 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e,
+ 0x18, 0x88, 0x39, 0x30, 0x75, 0x06, 0x03, 0x55,
+ 0x1d, 0x23, 0x04, 0x6e, 0x30, 0x6c, 0x80, 0x14,
+ 0xb1, 0xad, 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28,
+ 0xdb, 0x69, 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26,
+ 0x8e, 0x18, 0x88, 0x39, 0xa1, 0x49, 0xa4, 0x47,
+ 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
+ 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31,
+ 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08,
+ 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53,
+ 0x74, 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f,
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49,
+ 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20,
+ 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20,
+ 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x82,
+ 0x09, 0x00, 0x85, 0xb0, 0xbb, 0xa4, 0x8a, 0x7f,
+ 0xb8, 0xca, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d,
+ 0x13, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03,
+ 0x81, 0x81, 0x00, 0x08, 0x6c, 0x45, 0x24, 0xc7,
+ 0x6b, 0xb1, 0x59, 0xab, 0x0c, 0x52, 0xcc, 0xf2,
+ 0xb0, 0x14, 0xd7, 0x87, 0x9d, 0x7a, 0x64, 0x75,
+ 0xb5, 0x5a, 0x95, 0x66, 0xe4, 0xc5, 0x2b, 0x8e,
+ 0xae, 0x12, 0x66, 0x1f, 0xeb, 0x4f, 0x38, 0xb3,
+ 0x6e, 0x60, 0xd3, 0x92, 0xfd, 0xf7, 0x41, 0x08,
+ 0xb5, 0x25, 0x13, 0xb1, 0x18, 0x7a, 0x24, 0xfb,
+ 0x30, 0x1d, 0xba, 0xed, 0x98, 0xb9, 0x17, 0xec,
+ 0xe7, 0xd7, 0x31, 0x59, 0xdb, 0x95, 0xd3, 0x1d,
+ 0x78, 0xea, 0x50, 0x56, 0x5c, 0xd5, 0x82, 0x5a,
+ 0x2d, 0x5a, 0x5f, 0x33, 0xc4, 0xb6, 0xd8, 0xc9,
+ 0x75, 0x90, 0x96, 0x8c, 0x0f, 0x52, 0x98, 0xb5,
+ 0xcd, 0x98, 0x1f, 0x89, 0x20, 0x5f, 0xf2, 0xa0,
+ 0x1c, 0xa3, 0x1b, 0x96, 0x94, 0xdd, 0xa9, 0xfd,
+ 0x57, 0xe9, 0x70, 0xe8, 0x26, 0x6d, 0x71, 0x99,
+ 0x9b, 0x26, 0x6e, 0x38, 0x50, 0x29, 0x6c, 0x90,
+ 0xa7, 0xbd, 0xd9, 0x16, 0x03, 0x01, 0x01, 0x06,
+ 0x10, 0x00, 0x01, 0x02, 0x01, 0x00, 0x25, 0x48,
+ 0x6c, 0x0a, 0xde, 0x9d, 0x3a, 0x57, 0xe4, 0x2e,
+ 0xb9, 0xfc, 0xb4, 0x46, 0x1f, 0x20, 0x4f, 0x58,
+ 0x4d, 0x12, 0x08, 0xb4, 0x3e, 0x4c, 0xf5, 0xa8,
+ 0xa5, 0x16, 0x40, 0x29, 0x19, 0x04, 0x4d, 0xf9,
+ 0x54, 0x3a, 0x32, 0xd7, 0x79, 0xf2, 0x0e, 0xc1,
+ 0x7b, 0x0c, 0x62, 0x71, 0xbb, 0xb4, 0x8c, 0xe7,
+ 0x84, 0xd5, 0xf8, 0x11, 0x77, 0x7f, 0x87, 0x6c,
+ 0xfc, 0x25, 0xf3, 0x2d, 0x97, 0x3d, 0x1f, 0xf5,
+ 0xfc, 0x64, 0x94, 0x9f, 0xdd, 0x90, 0x82, 0xdd,
+ 0x11, 0x74, 0x74, 0x59, 0xa2, 0x1a, 0x71, 0xb2,
+ 0x55, 0x6d, 0x18, 0xca, 0x85, 0x47, 0x8b, 0x79,
+ 0x73, 0x06, 0x24, 0x38, 0xc3, 0x34, 0x98, 0x84,
+ 0x62, 0x81, 0xd8, 0xad, 0x54, 0xad, 0x13, 0xa5,
+ 0xf4, 0xe4, 0x82, 0x85, 0xd3, 0xe3, 0x9e, 0xeb,
+ 0xb5, 0xf5, 0x95, 0x83, 0x0e, 0xb9, 0x7d, 0xb6,
+ 0xda, 0x0c, 0xf6, 0x14, 0x6a, 0x60, 0x8c, 0x75,
+ 0x56, 0xf0, 0xe9, 0x60, 0xe0, 0x4c, 0xf4, 0x4e,
+ 0x84, 0x8b, 0x4f, 0xf4, 0x2f, 0xde, 0xb7, 0xec,
+ 0x61, 0xd3, 0x77, 0x07, 0x6e, 0x41, 0x57, 0xc9,
+ 0xd9, 0x1d, 0x75, 0xee, 0x42, 0x63, 0xdc, 0x58,
+ 0xad, 0xfc, 0xc7, 0xe1, 0x77, 0x49, 0xb1, 0x58,
+ 0x21, 0x96, 0x00, 0x55, 0x90, 0x6b, 0xf6, 0x2a,
+ 0x5a, 0x19, 0x25, 0x93, 0x59, 0x9d, 0xaf, 0x79,
+ 0x9b, 0x18, 0x5d, 0xf6, 0x5d, 0x64, 0x4b, 0x9a,
+ 0xf4, 0xde, 0xf2, 0x7f, 0xbd, 0x93, 0x7e, 0x45,
+ 0x3e, 0x17, 0xae, 0xbf, 0x52, 0xe1, 0xba, 0x8e,
+ 0x0b, 0xbc, 0x1e, 0x91, 0x9d, 0xf1, 0x4e, 0x0b,
+ 0xab, 0x9e, 0x5c, 0x4c, 0x6f, 0xf7, 0xf3, 0x8d,
+ 0x8c, 0x6d, 0xeb, 0x46, 0x05, 0x36, 0x7e, 0x2f,
+ 0x9c, 0xa1, 0x86, 0x15, 0xe1, 0xe4, 0xb4, 0x20,
+ 0x06, 0x44, 0x7b, 0x3c, 0x8b, 0x13, 0x96, 0xf5,
+ 0x02, 0xb1, 0x4f, 0x3c, 0x2d, 0x4a, 0x16, 0x03,
+ 0x01, 0x00, 0x86, 0x0f, 0x00, 0x00, 0x82, 0x00,
+ 0x80, 0x52, 0xb1, 0x0d, 0xfc, 0x85, 0x34, 0x56,
+ 0xb9, 0xdf, 0xa7, 0x8e, 0xf4, 0xfd, 0x02, 0x46,
+ 0x8a, 0x23, 0xcc, 0x53, 0x3b, 0x0f, 0xa7, 0x61,
+ 0xf3, 0xb5, 0xbf, 0xfe, 0x59, 0x77, 0x10, 0xd6,
+ 0x56, 0x93, 0x19, 0x6b, 0x2c, 0xf1, 0x35, 0x71,
+ 0xe3, 0x36, 0x2f, 0xa0, 0x90, 0x4e, 0x5a, 0xdf,
+ 0x8d, 0x06, 0x88, 0xcf, 0xb1, 0x06, 0x56, 0x8b,
+ 0x74, 0x8f, 0x02, 0x8e, 0x10, 0xd2, 0xab, 0x8d,
+ 0x3f, 0x3e, 0x02, 0xf1, 0x1a, 0x80, 0x6d, 0x0f,
+ 0x9e, 0x77, 0xd8, 0xfa, 0x92, 0xb3, 0x16, 0x40,
+ 0xeb, 0x9e, 0xca, 0xd7, 0xe4, 0x31, 0xcc, 0x63,
+ 0x5f, 0xe2, 0x4c, 0x85, 0x0e, 0xf2, 0xdd, 0xd3,
+ 0xfe, 0x7e, 0xa7, 0x60, 0x1c, 0xb4, 0x00, 0xd8,
+ 0xbe, 0x4b, 0x9b, 0x66, 0x78, 0x0f, 0xfb, 0x3b,
+ 0x52, 0x30, 0x2b, 0x8b, 0xd9, 0xef, 0x82, 0x0a,
+ 0xa4, 0x18, 0x1d, 0xb0, 0xb5, 0xbf, 0x54, 0x97,
+ 0x0c, 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16,
+ 0x03, 0x01, 0x00, 0x30, 0xa1, 0x74, 0x22, 0xd8,
+ 0x86, 0x6a, 0xbe, 0x53, 0x34, 0x1d, 0xb3, 0x73,
+ 0xff, 0x51, 0xc0, 0xce, 0x8e, 0x7d, 0x9b, 0xab,
+ 0xcb, 0x8b, 0x79, 0xae, 0x04, 0x01, 0xa7, 0xf2,
+ 0x8e, 0x9d, 0xab, 0xa3, 0x73, 0x80, 0x5c, 0xff,
+ 0x96, 0x20, 0xbb, 0x8d, 0xc0, 0x02, 0x66, 0x6c,
+ 0x83, 0x4b, 0x78, 0x20,
+ },
+ {
+ 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03,
+ 0x01, 0x00, 0x30, 0x29, 0xd4, 0xfd, 0x03, 0x8b,
+ 0x30, 0x20, 0xf7, 0xca, 0xc0, 0x6c, 0x83, 0x5d,
+ 0x73, 0xcb, 0x81, 0x60, 0xe0, 0x9a, 0x09, 0xcb,
+ 0x33, 0x03, 0x80, 0x81, 0x4e, 0x84, 0x47, 0xd5,
+ 0x74, 0x6c, 0x3b, 0xb5, 0xc0, 0x48, 0x0d, 0x52,
+ 0xdd, 0xbe, 0xc2, 0x06, 0xf5, 0x79, 0x2b, 0x3e,
+ 0x99, 0x56, 0x94, 0x17, 0x03, 0x01, 0x00, 0x20,
+ 0x26, 0x46, 0x90, 0x9d, 0xef, 0x59, 0x00, 0xb6,
+ 0x70, 0xe8, 0x1e, 0x1a, 0x80, 0x8b, 0x04, 0xb2,
+ 0xfc, 0x51, 0xf8, 0x93, 0xbe, 0x00, 0x28, 0xba,
+ 0xb8, 0xdc, 0x51, 0x7e, 0x92, 0x80, 0xfa, 0xf2,
+ 0x17, 0x03, 0x01, 0x00, 0xe0, 0xb8, 0x2e, 0xc4,
+ 0x6b, 0x3f, 0xda, 0x39, 0x87, 0x7f, 0x03, 0x43,
+ 0x28, 0xdd, 0xb9, 0xf9, 0x9e, 0x16, 0xf5, 0xce,
+ 0x3f, 0x7e, 0x6a, 0x7b, 0xb3, 0x60, 0x14, 0xe1,
+ 0xea, 0x54, 0xc5, 0xe6, 0x05, 0x0a, 0x6c, 0xe0,
+ 0xef, 0x58, 0x29, 0x8a, 0x77, 0x64, 0x77, 0x5d,
+ 0x9c, 0xe2, 0xe0, 0x3c, 0x6d, 0x87, 0x82, 0xbe,
+ 0x47, 0x63, 0xd4, 0xfd, 0x0c, 0x25, 0xc4, 0xb1,
+ 0xfe, 0x29, 0x6f, 0x84, 0xfb, 0xab, 0x6e, 0xa7,
+ 0xf9, 0x22, 0x89, 0x97, 0x5b, 0x91, 0x0a, 0x07,
+ 0xe0, 0xef, 0x3d, 0x67, 0xee, 0x87, 0xa8, 0x33,
+ 0x02, 0x64, 0x33, 0xca, 0x15, 0x10, 0xb9, 0x57,
+ 0xd8, 0xe5, 0x1a, 0x4b, 0xe3, 0x45, 0xc1, 0x62,
+ 0x85, 0x50, 0xf1, 0x79, 0x54, 0xe1, 0x2e, 0x25,
+ 0x01, 0x3c, 0xdb, 0x2d, 0x39, 0x14, 0x2f, 0x9b,
+ 0xd0, 0x1d, 0xc1, 0xac, 0x73, 0x7d, 0xa4, 0xed,
+ 0x89, 0x98, 0xb1, 0xae, 0x8a, 0x9e, 0xc8, 0xa7,
+ 0xfe, 0x55, 0x27, 0xb5, 0xb5, 0xa2, 0xec, 0x7e,
+ 0xe3, 0x6b, 0x45, 0x19, 0xfa, 0x20, 0x1c, 0x33,
+ 0x83, 0x22, 0x33, 0x97, 0xd2, 0x5a, 0xc4, 0xf8,
+ 0x9a, 0x03, 0x13, 0x85, 0xf2, 0x2b, 0x04, 0x59,
+ 0x27, 0xd7, 0x0b, 0x42, 0x47, 0x9b, 0x7d, 0x4d,
+ 0xb2, 0x1a, 0x85, 0x7f, 0x97, 0xc2, 0xf2, 0x10,
+ 0xf0, 0xfa, 0x4e, 0x4b, 0x62, 0x43, 0x3a, 0x09,
+ 0x2e, 0xcd, 0x8f, 0xa8, 0xb6, 0x0b, 0x5f, 0x34,
+ 0xd7, 0x3b, 0xba, 0xd9, 0xe5, 0x01, 0x2d, 0x35,
+ 0xae, 0xc5, 0x4c, 0xab, 0x40, 0x64, 0xc2, 0xc9,
+ 0x8c, 0x69, 0x44, 0xf4, 0xb8, 0xb5, 0x3a, 0x05,
+ 0x3c, 0x29, 0x19, 0xb4, 0x09, 0x17, 0x03, 0x01,
+ 0x00, 0x20, 0xc8, 0xc5, 0xb7, 0xe3, 0xd2, 0x3e,
+ 0x27, 0xb5, 0x71, 0x8f, 0x52, 0x0b, 0xce, 0x17,
+ 0x64, 0x86, 0xa4, 0x34, 0x16, 0x1b, 0x61, 0x64,
+ 0x7c, 0xb3, 0xf2, 0xe5, 0x3e, 0xfd, 0xdd, 0xfb,
+ 0x40, 0x78, 0x17, 0x03, 0x01, 0x00, 0x50, 0x8e,
+ 0x79, 0xf0, 0x8e, 0x76, 0x5d, 0x34, 0x09, 0xdc,
+ 0xec, 0x6d, 0xc3, 0x43, 0x1d, 0xcb, 0x2d, 0xaa,
+ 0x08, 0x7a, 0x51, 0x94, 0x4e, 0xc5, 0x26, 0xe4,
+ 0x0b, 0x8e, 0x8f, 0x51, 0xf2, 0x9f, 0xeb, 0xc3,
+ 0x18, 0x43, 0x95, 0x15, 0xfc, 0x59, 0x18, 0x25,
+ 0x47, 0xb6, 0x4a, 0x6e, 0xa3, 0xa4, 0x3b, 0xa3,
+ 0x47, 0x34, 0x74, 0x6b, 0xc5, 0x3d, 0x41, 0x14,
+ 0x64, 0xd5, 0x69, 0x5f, 0x77, 0xf3, 0x7c, 0x41,
+ 0xc6, 0xed, 0x2e, 0xcf, 0xff, 0x40, 0xf2, 0xce,
+ 0xbb, 0xa7, 0x4e, 0x73, 0x88, 0x98, 0x10,
+ },
+ {
+ 0x15, 0x03, 0x01, 0x00, 0x20, 0x1a, 0xbc, 0x70,
+ 0x24, 0xf8, 0xfb, 0xf2, 0x4a, 0xf9, 0x44, 0x1e,
+ 0x58, 0xf8, 0xaa, 0x41, 0x24, 0xe8, 0x80, 0x33,
+ 0x45, 0x18, 0xa1, 0x5d, 0xee, 0x16, 0x80, 0xae,
+ 0x40, 0x41, 0x8e, 0x41, 0x9b,
+ },
+}
+
+var tls11ECDHEAESClientScript = [][]byte{
+ {
+ 0x16, 0x03, 0x01, 0x00, 0x4a, 0x01, 0x00, 0x00,
+ 0x46, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xc0, 0x13,
+ 0x01, 0x00, 0x00, 0x1b, 0x00, 0x05, 0x00, 0x05,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00,
+ 0x08, 0x00, 0x06, 0x00, 0x17, 0x00, 0x18, 0x00,
+ 0x19, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00,
+ },
+ {
+ 0x16, 0x03, 0x02, 0x00, 0x54, 0x02, 0x00, 0x00,
+ 0x50, 0x03, 0x02, 0x51, 0x9f, 0xa2, 0x21, 0x1a,
+ 0xb7, 0x75, 0x42, 0x69, 0xd3, 0x14, 0xdd, 0x05,
+ 0x1e, 0xda, 0x13, 0x71, 0x8d, 0x6a, 0x45, 0x97,
+ 0xcb, 0xee, 0x0e, 0x77, 0x01, 0x0d, 0x6e, 0xe5,
+ 0x22, 0x70, 0x16, 0x20, 0x69, 0xfc, 0xa6, 0x9a,
+ 0xe8, 0x21, 0xcc, 0x46, 0x65, 0x05, 0xb4, 0x48,
+ 0x0f, 0x34, 0x63, 0x2c, 0xac, 0xa4, 0xf5, 0x4b,
+ 0x64, 0xd1, 0x07, 0x13, 0xa7, 0xe4, 0x5b, 0xa3,
+ 0x4d, 0x31, 0x41, 0x53, 0xc0, 0x13, 0x00, 0x00,
+ 0x08, 0x00, 0x0b, 0x00, 0x04, 0x03, 0x00, 0x01,
+ 0x02, 0x16, 0x03, 0x02, 0x02, 0x39, 0x0b, 0x00,
+ 0x02, 0x35, 0x00, 0x02, 0x32, 0x00, 0x02, 0x2f,
+ 0x30, 0x82, 0x02, 0x2b, 0x30, 0x82, 0x01, 0xd5,
+ 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x09, 0x00,
+ 0xb1, 0x35, 0x13, 0x65, 0x11, 0x20, 0xc5, 0x92,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30,
+ 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
+ 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13,
+ 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
+ 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74,
+ 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06,
+ 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e,
+ 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57,
+ 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50,
+ 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e,
+ 0x17, 0x0d, 0x31, 0x32, 0x30, 0x34, 0x30, 0x36,
+ 0x31, 0x37, 0x31, 0x30, 0x31, 0x33, 0x5a, 0x17,
+ 0x0d, 0x31, 0x35, 0x30, 0x34, 0x30, 0x36, 0x31,
+ 0x37, 0x31, 0x30, 0x31, 0x33, 0x5a, 0x30, 0x45,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
+ 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30,
+ 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a,
+ 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61,
+ 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03,
+ 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74,
+ 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69,
+ 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74,
+ 0x79, 0x20, 0x4c, 0x74, 0x64, 0x30, 0x5c, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x4b,
+ 0x00, 0x30, 0x48, 0x02, 0x41, 0x00, 0x9f, 0xb3,
+ 0xc3, 0x84, 0x27, 0x95, 0xff, 0x12, 0x31, 0x52,
+ 0x0f, 0x15, 0xef, 0x46, 0x11, 0xc4, 0xad, 0x80,
+ 0xe6, 0x36, 0x5b, 0x0f, 0xdd, 0x80, 0xd7, 0x61,
+ 0x8d, 0xe0, 0xfc, 0x72, 0x45, 0x09, 0x34, 0xfe,
+ 0x55, 0x66, 0x45, 0x43, 0x4c, 0x68, 0x97, 0x6a,
+ 0xfe, 0xa8, 0xa0, 0xa5, 0xdf, 0x5f, 0x78, 0xff,
+ 0xee, 0xd7, 0x64, 0xb8, 0x3f, 0x04, 0xcb, 0x6f,
+ 0xff, 0x2a, 0xfe, 0xfe, 0xb9, 0xed, 0x02, 0x03,
+ 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, 0x30, 0x81,
+ 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e,
+ 0x04, 0x16, 0x04, 0x14, 0x78, 0xa6, 0x97, 0x9a,
+ 0x63, 0xb5, 0xc5, 0xa1, 0xa5, 0x33, 0xba, 0x22,
+ 0x7c, 0x23, 0x6e, 0x5b, 0x1b, 0x7a, 0xcc, 0x2b,
+ 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
+ 0x6e, 0x30, 0x6c, 0x80, 0x14, 0x78, 0xa6, 0x97,
+ 0x9a, 0x63, 0xb5, 0xc5, 0xa1, 0xa5, 0x33, 0xba,
+ 0x22, 0x7c, 0x23, 0x6e, 0x5b, 0x1b, 0x7a, 0xcc,
+ 0x2b, 0xa1, 0x49, 0xa4, 0x47, 0x30, 0x45, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11,
+ 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53,
+ 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74,
+ 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55,
+ 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65,
+ 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64,
+ 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79,
+ 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, 0x00, 0xb1,
+ 0x35, 0x13, 0x65, 0x11, 0x20, 0xc5, 0x92, 0x30,
+ 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05,
+ 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x05, 0x05, 0x00, 0x03, 0x41, 0x00, 0x85,
+ 0x36, 0x40, 0x73, 0xc1, 0xbb, 0x1a, 0xda, 0xd4,
+ 0x59, 0x9f, 0x2d, 0xa2, 0x70, 0x31, 0x46, 0x74,
+ 0xec, 0x83, 0x6e, 0xa8, 0xc8, 0x3c, 0x51, 0xaf,
+ 0x39, 0xac, 0xec, 0x40, 0xbc, 0xe8, 0x22, 0x46,
+ 0x1d, 0x99, 0xd6, 0x46, 0x2a, 0x24, 0xd4, 0x8b,
+ 0x05, 0x08, 0x4b, 0xfb, 0x35, 0x11, 0x6e, 0x92,
+ 0xbb, 0x77, 0xba, 0xe4, 0x12, 0xbb, 0xf4, 0xc8,
+ 0x5e, 0x9c, 0x81, 0xa8, 0x97, 0x60, 0x4c, 0x16,
+ 0x03, 0x02, 0x00, 0x8b, 0x0c, 0x00, 0x00, 0x87,
+ 0x03, 0x00, 0x17, 0x41, 0x04, 0x34, 0xde, 0x50,
+ 0x32, 0x8f, 0x25, 0x6b, 0x37, 0x2c, 0x36, 0x24,
+ 0x27, 0x0e, 0xf9, 0x67, 0xb4, 0xf8, 0x29, 0x1c,
+ 0xa5, 0xa4, 0x59, 0x9a, 0xca, 0x40, 0x26, 0x15,
+ 0x61, 0x72, 0x34, 0x4a, 0xd3, 0x0c, 0xac, 0x69,
+ 0xcb, 0x2a, 0x9e, 0xf8, 0x80, 0xfb, 0x7a, 0xc4,
+ 0xd4, 0x4b, 0x91, 0x1b, 0xbe, 0x24, 0x26, 0xad,
+ 0x19, 0x24, 0xbe, 0x32, 0x58, 0xfb, 0xc7, 0x77,
+ 0xce, 0x7e, 0x71, 0x51, 0x1a, 0x00, 0x40, 0x1a,
+ 0x0b, 0xe8, 0x91, 0x84, 0x64, 0x54, 0xb6, 0x19,
+ 0xe8, 0xd4, 0x43, 0x7c, 0x09, 0x0c, 0x2e, 0xba,
+ 0x42, 0xb9, 0x74, 0xc3, 0x6c, 0x06, 0x9b, 0xa6,
+ 0x7e, 0x92, 0xe9, 0xee, 0x7c, 0x74, 0xa9, 0xd3,
+ 0x63, 0xf0, 0x16, 0x20, 0x60, 0x71, 0x8e, 0x24,
+ 0xc7, 0x7f, 0xc5, 0x5b, 0x9c, 0x19, 0x0c, 0x80,
+ 0x15, 0x61, 0xbf, 0xb6, 0xed, 0x5b, 0x7b, 0x90,
+ 0xc5, 0x05, 0x13, 0x72, 0x45, 0x79, 0xdf, 0x16,
+ 0x03, 0x02, 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x16, 0x03, 0x02, 0x00, 0x46, 0x10, 0x00, 0x00,
+ 0x42, 0x41, 0x04, 0x1e, 0x18, 0x37, 0xef, 0x0d,
+ 0x19, 0x51, 0x88, 0x35, 0x75, 0x71, 0xb5, 0xe5,
+ 0x54, 0x5b, 0x12, 0x2e, 0x8f, 0x09, 0x67, 0xfd,
+ 0xa7, 0x24, 0x20, 0x3e, 0xb2, 0x56, 0x1c, 0xce,
+ 0x97, 0x28, 0x5e, 0xf8, 0x2b, 0x2d, 0x4f, 0x9e,
+ 0xf1, 0x07, 0x9f, 0x6c, 0x4b, 0x5b, 0x83, 0x56,
+ 0xe2, 0x32, 0x42, 0xe9, 0x58, 0xb6, 0xd7, 0x49,
+ 0xa6, 0xb5, 0x68, 0x1a, 0x41, 0x03, 0x56, 0x6b,
+ 0xdc, 0x5a, 0x89, 0x14, 0x03, 0x02, 0x00, 0x01,
+ 0x01, 0x16, 0x03, 0x02, 0x00, 0x40, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x50,
+ 0x32, 0x26, 0x51, 0xbd, 0xbd, 0x3c, 0x4f, 0x72,
+ 0xbf, 0xbc, 0x91, 0x70, 0x4b, 0x5d, 0x43, 0x4a,
+ 0x65, 0x26, 0x0d, 0xaa, 0xed, 0x00, 0x91, 0xaf,
+ 0x4f, 0x47, 0x09, 0xaa, 0x79, 0xc4, 0x47, 0x21,
+ 0x71, 0xd8, 0x2b, 0xc1, 0x51, 0xc8, 0xef, 0xed,
+ 0x67, 0xde, 0x97, 0xef, 0x18, 0x53,
+ },
+ {
+ 0x14, 0x03, 0x02, 0x00, 0x01, 0x01, 0x16, 0x03,
+ 0x02, 0x00, 0x40, 0x72, 0x20, 0xbf, 0xd1, 0xbd,
+ 0x83, 0x53, 0x57, 0xb0, 0x4e, 0xac, 0xba, 0x1a,
+ 0x2b, 0x2d, 0xeb, 0x8a, 0x48, 0x17, 0xfa, 0x69,
+ 0xf9, 0xb5, 0x94, 0x8e, 0x6f, 0x9c, 0xda, 0x59,
+ 0xba, 0x6c, 0x7c, 0x82, 0xe2, 0x53, 0xa9, 0x46,
+ 0xdc, 0x33, 0xa0, 0x9b, 0xf0, 0x1e, 0xf1, 0x53,
+ 0x83, 0x48, 0xbf, 0x5e, 0xef, 0x03, 0x2b, 0x50,
+ 0x7a, 0xa6, 0xf8, 0xc3, 0x9e, 0x24, 0x43, 0x3a,
+ 0xdf, 0x44, 0x3e,
+ },
+ {
+ 0x17, 0x03, 0x02, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x0b, 0x8f,
+ 0x6b, 0xf9, 0xd3, 0x9f, 0x2b, 0x49, 0xe0, 0x62,
+ 0x9a, 0x0b, 0x3e, 0xa2, 0x72, 0x8b, 0x96, 0x0c,
+ 0x41, 0x09, 0x95, 0x9e, 0x6b, 0x26, 0xa1, 0x46,
+ 0xca, 0xb8, 0xb6, 0xd2, 0xd4, 0x15, 0x03, 0x02,
+ 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xa0, 0xd4, 0x84, 0xc6, 0x7e, 0x1c,
+ 0x2f, 0xbd, 0x6b, 0x45, 0x31, 0x1d, 0x7d, 0x8f,
+ 0x31, 0x39, 0x5a, 0x4e, 0xaa, 0xf1, 0x0a, 0x8a,
+ 0x6c, 0x33, 0x59, 0x19, 0xd8, 0x75, 0x80, 0xab,
+ 0x93, 0x81,
+ },
+}
+
+var clientChainCertificateScript = [][]byte{
+ {
+ 0x16, 0x03, 0x01, 0x00, 0x4a, 0x01, 0x00, 0x00,
+ 0x46, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x05,
+ 0x01, 0x00, 0x00, 0x1b, 0x00, 0x05, 0x00, 0x05,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00,
+ 0x08, 0x00, 0x06, 0x00, 0x17, 0x00, 0x18, 0x00,
+ 0x19, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00,
+ },
+ {
+ 0x16, 0x03, 0x01, 0x00, 0x4a, 0x02, 0x00, 0x00,
+ 0x46, 0x03, 0x01, 0x51, 0xa2, 0x9b, 0x8b, 0xd4,
+ 0xe6, 0x33, 0xa2, 0x70, 0x38, 0x37, 0xba, 0x55,
+ 0x86, 0xcf, 0x87, 0xea, 0x6d, 0x2c, 0x3e, 0x17,
+ 0xc2, 0x09, 0xf8, 0x4d, 0xb0, 0x5d, 0x93, 0x2b,
+ 0x15, 0x99, 0x0c, 0x20, 0x5d, 0x61, 0x21, 0x2c,
+ 0xed, 0x49, 0x32, 0x29, 0x08, 0x6e, 0x21, 0x58,
+ 0x00, 0xdb, 0x34, 0xb7, 0x37, 0xcd, 0x27, 0x75,
+ 0x31, 0x1e, 0x6c, 0x74, 0xa6, 0xef, 0xa2, 0xc4,
+ 0x2b, 0x6c, 0xc3, 0x03, 0x00, 0x05, 0x00, 0x16,
+ 0x03, 0x01, 0x03, 0xef, 0x0b, 0x00, 0x03, 0xeb,
+ 0x00, 0x03, 0xe8, 0x00, 0x03, 0xe5, 0x30, 0x82,
+ 0x03, 0xe1, 0x30, 0x82, 0x02, 0xc9, 0xa0, 0x03,
+ 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0xcc, 0x22,
+ 0x4c, 0x4b, 0x98, 0xa2, 0x88, 0xfc, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81, 0x86,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
+ 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x0b, 0x30,
+ 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x02,
+ 0x4e, 0x59, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03,
+ 0x55, 0x04, 0x07, 0x0c, 0x08, 0x42, 0x72, 0x6f,
+ 0x6f, 0x6b, 0x6c, 0x79, 0x6e, 0x31, 0x21, 0x30,
+ 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x18,
+ 0x4d, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69,
+ 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41,
+ 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79,
+ 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x0c, 0x08, 0x6d, 0x79, 0x63, 0x61, 0x2e,
+ 0x6f, 0x72, 0x67, 0x31, 0x21, 0x30, 0x1f, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x09, 0x01, 0x16, 0x12, 0x6a, 0x76, 0x73, 0x68,
+ 0x61, 0x68, 0x69, 0x64, 0x40, 0x67, 0x6d, 0x61,
+ 0x69, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x1e,
+ 0x17, 0x0d, 0x31, 0x33, 0x30, 0x35, 0x32, 0x36,
+ 0x32, 0x31, 0x30, 0x35, 0x30, 0x31, 0x5a, 0x17,
+ 0x0d, 0x32, 0x33, 0x30, 0x35, 0x32, 0x34, 0x32,
+ 0x31, 0x30, 0x35, 0x30, 0x31, 0x5a, 0x30, 0x81,
+ 0x86, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
+ 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c,
+ 0x02, 0x4e, 0x59, 0x31, 0x11, 0x30, 0x0f, 0x06,
+ 0x03, 0x55, 0x04, 0x07, 0x0c, 0x08, 0x42, 0x72,
+ 0x6f, 0x6f, 0x6b, 0x6c, 0x79, 0x6e, 0x31, 0x21,
+ 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c,
+ 0x18, 0x4d, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74,
+ 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20,
+ 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74,
+ 0x79, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x0c, 0x08, 0x6d, 0x79, 0x63, 0x61,
+ 0x2e, 0x6f, 0x72, 0x67, 0x31, 0x21, 0x30, 0x1f,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x09, 0x01, 0x16, 0x12, 0x6a, 0x76, 0x73,
+ 0x68, 0x61, 0x68, 0x69, 0x64, 0x40, 0x67, 0x6d,
+ 0x61, 0x69, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x30,
+ 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
+ 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30,
+ 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00,
+ 0xf0, 0xfb, 0xad, 0x80, 0x5e, 0x37, 0xd3, 0x6d,
+ 0xee, 0x2e, 0xcc, 0xbc, 0x0c, 0xd7, 0x56, 0x4b,
+ 0x56, 0x45, 0xcd, 0x28, 0xb6, 0x22, 0xe9, 0xe2,
+ 0x0f, 0xd1, 0x87, 0x2a, 0x27, 0xce, 0x77, 0x8d,
+ 0x6e, 0x0e, 0x0f, 0xfb, 0x66, 0xe1, 0xb5, 0x0e,
+ 0x9a, 0xb6, 0x05, 0x8e, 0xb3, 0xe1, 0xc5, 0x77,
+ 0x86, 0x5b, 0x46, 0xd2, 0x0b, 0x92, 0x03, 0x1b,
+ 0x89, 0x0c, 0x1b, 0x10, 0x0e, 0x99, 0x8f, 0xe2,
+ 0x17, 0xe8, 0xc2, 0x30, 0x00, 0x47, 0xd6, 0xfc,
+ 0xf9, 0x0f, 0x3b, 0x75, 0x34, 0x8d, 0x4d, 0xb0,
+ 0x99, 0xb7, 0xa0, 0x6d, 0xa0, 0xb6, 0xad, 0xda,
+ 0x07, 0x5e, 0x38, 0x2e, 0x02, 0xe4, 0x30, 0x6d,
+ 0xae, 0x13, 0x72, 0xd4, 0xc8, 0xce, 0x14, 0x07,
+ 0xae, 0x23, 0x8c, 0x8f, 0x9e, 0x8c, 0x60, 0xd6,
+ 0x06, 0xb9, 0xef, 0x00, 0x18, 0xc0, 0x1d, 0x25,
+ 0x1e, 0xda, 0x3e, 0x2f, 0xcf, 0x2b, 0x56, 0x84,
+ 0x9e, 0x30, 0x21, 0xc7, 0x29, 0xf6, 0x03, 0x8a,
+ 0x24, 0xf9, 0x34, 0xac, 0x65, 0x9d, 0x80, 0x36,
+ 0xc8, 0x3b, 0x15, 0x10, 0xbd, 0x51, 0xe9, 0xbc,
+ 0x02, 0xe1, 0xe9, 0xb3, 0x5a, 0x9a, 0x99, 0x41,
+ 0x1b, 0x27, 0xa0, 0x4d, 0x50, 0x9e, 0x27, 0x7f,
+ 0xa1, 0x7d, 0x09, 0x87, 0xbd, 0x8a, 0xca, 0x5f,
+ 0xb1, 0xa5, 0x08, 0xb8, 0x04, 0xd4, 0x52, 0x89,
+ 0xaa, 0xe0, 0x7d, 0x42, 0x2e, 0x2f, 0x15, 0xee,
+ 0x66, 0x57, 0x0f, 0x13, 0x19, 0x45, 0xa8, 0x4b,
+ 0x5d, 0x81, 0x66, 0xcc, 0x12, 0x37, 0x94, 0x5e,
+ 0xfd, 0x3c, 0x10, 0x81, 0x51, 0x3f, 0xfa, 0x0f,
+ 0xdd, 0xa1, 0x89, 0x03, 0xa9, 0x78, 0x91, 0xf5,
+ 0x3b, 0xf3, 0xbc, 0xac, 0xbe, 0x93, 0x30, 0x2e,
+ 0xbe, 0xca, 0x7f, 0x46, 0xd3, 0x28, 0xb4, 0x4e,
+ 0x91, 0x7b, 0x5b, 0x43, 0x6c, 0xaf, 0x9b, 0x5c,
+ 0x6a, 0x6d, 0x5a, 0xdb, 0x79, 0x5e, 0x6a, 0x6b,
+ 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x50, 0x30,
+ 0x4e, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e,
+ 0x04, 0x16, 0x04, 0x14, 0x6b, 0x1e, 0x00, 0xa8,
+ 0x9f, 0xfa, 0x7d, 0x00, 0xf9, 0xe0, 0x9d, 0x0f,
+ 0x90, 0x8c, 0x90, 0xa8, 0xa1, 0x37, 0x6b, 0xda,
+ 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
+ 0x18, 0x30, 0x16, 0x80, 0x14, 0x6b, 0x1e, 0x00,
+ 0xa8, 0x9f, 0xfa, 0x7d, 0x00, 0xf9, 0xe0, 0x9d,
+ 0x0f, 0x90, 0x8c, 0x90, 0xa8, 0xa1, 0x37, 0x6b,
+ 0xda, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13,
+ 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82,
+ 0x01, 0x01, 0x00, 0xcd, 0x6f, 0x73, 0x4d, 0x56,
+ 0x0b, 0xf3, 0x2e, 0x1c, 0xe2, 0x02, 0x0c, 0x14,
+ 0xbb, 0x2f, 0xdd, 0x3c, 0x43, 0xfe, 0xdf, 0x94,
+ 0x2d, 0xa9, 0x89, 0x81, 0x51, 0xf8, 0x5f, 0xa7,
+ 0xa0, 0x13, 0xaa, 0xcc, 0xb0, 0x18, 0xe2, 0x57,
+ 0x3e, 0x0d, 0x29, 0x93, 0xe8, 0x95, 0xd5, 0x1b,
+ 0x53, 0xd2, 0x51, 0xf2, 0xbd, 0xf5, 0x9e, 0x7b,
+ 0x22, 0x65, 0x62, 0x5c, 0xc4, 0x4c, 0x1d, 0xe8,
+ 0xe9, 0xc3, 0xd4, 0x2b, 0xe7, 0x78, 0xcb, 0x10,
+ 0xf3, 0xfe, 0x06, 0x83, 0xdc, 0x3a, 0x1e, 0x62,
+ 0x10, 0xc0, 0x46, 0x77, 0xc6, 0x9d, 0x9f, 0xab,
+ 0x96, 0x25, 0x5c, 0xfb, 0x26, 0xc1, 0x15, 0x1f,
+ 0xa5, 0x33, 0xee, 0x4f, 0x9a, 0x14, 0x6a, 0x14,
+ 0x97, 0x93, 0x2b, 0x95, 0x0b, 0xdc, 0xa8, 0xd7,
+ 0x69, 0x2e, 0xf0, 0x01, 0x0e, 0xfd, 0x4e, 0xd0,
+ 0xd9, 0xa8, 0xe5, 0x65, 0xde, 0xfb, 0xca, 0xca,
+ 0x1c, 0x5f, 0xf9, 0x53, 0xa0, 0x87, 0xe7, 0x33,
+ 0x9b, 0x2f, 0xcf, 0xe4, 0x13, 0xfc, 0xec, 0x7a,
+ 0x6c, 0xb0, 0x90, 0x13, 0x9b, 0xb6, 0xc5, 0x03,
+ 0xf6, 0x0e, 0x5e, 0xe2, 0xe4, 0x26, 0xc1, 0x7e,
+ 0x53, 0xfe, 0x69, 0xa3, 0xc7, 0xd8, 0x8e, 0x6e,
+ 0x94, 0x32, 0xa0, 0xde, 0xca, 0xb6, 0xcc, 0xd6,
+ 0x01, 0xd5, 0x78, 0x40, 0x28, 0x63, 0x9b, 0xee,
+ 0xcf, 0x09, 0x3b, 0x35, 0x04, 0xf0, 0x14, 0x02,
+ 0xf6, 0x80, 0x0e, 0x90, 0xb2, 0x94, 0xd2, 0x25,
+ 0x16, 0xb8, 0x7a, 0x76, 0x87, 0x84, 0x9f, 0x84,
+ 0xc5, 0xaf, 0xc2, 0x6d, 0x68, 0x7a, 0x84, 0x9c,
+ 0xc6, 0x8a, 0x63, 0x60, 0x87, 0x6a, 0x25, 0xc1,
+ 0xa1, 0x78, 0x0f, 0xba, 0xe8, 0x5f, 0xe1, 0xba,
+ 0xac, 0xa4, 0x6f, 0xdd, 0x09, 0x3f, 0x12, 0xcb,
+ 0x1d, 0xf3, 0xcf, 0x48, 0xd7, 0xd3, 0x26, 0xe8,
+ 0x9c, 0xc3, 0x53, 0xb3, 0xba, 0xdc, 0x32, 0x99,
+ 0x98, 0x96, 0xd6, 0x16, 0x03, 0x01, 0x00, 0x99,
+ 0x0d, 0x00, 0x00, 0x91, 0x03, 0x01, 0x02, 0x40,
+ 0x00, 0x8b, 0x00, 0x89, 0x30, 0x81, 0x86, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x02, 0x4e,
+ 0x59, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55,
+ 0x04, 0x07, 0x0c, 0x08, 0x42, 0x72, 0x6f, 0x6f,
+ 0x6b, 0x6c, 0x79, 0x6e, 0x31, 0x21, 0x30, 0x1f,
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x18, 0x4d,
+ 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66,
+ 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31,
+ 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x0c, 0x08, 0x6d, 0x79, 0x63, 0x61, 0x2e, 0x6f,
+ 0x72, 0x67, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09,
+ 0x01, 0x16, 0x12, 0x6a, 0x76, 0x73, 0x68, 0x61,
+ 0x68, 0x69, 0x64, 0x40, 0x67, 0x6d, 0x61, 0x69,
+ 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x0e, 0x00, 0x00,
+ 0x00,
+ },
+ {
+ 0x16, 0x03, 0x01, 0x0a, 0xfb, 0x0b, 0x00, 0x0a,
+ 0xf7, 0x00, 0x0a, 0xf4, 0x00, 0x03, 0x7e, 0x30,
+ 0x82, 0x03, 0x7a, 0x30, 0x82, 0x02, 0x62, 0x02,
+ 0x09, 0x00, 0xb4, 0x47, 0x58, 0x57, 0x2b, 0x67,
+ 0xc8, 0xc2, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05,
+ 0x00, 0x30, 0x81, 0x80, 0x31, 0x0b, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
+ 0x04, 0x08, 0x0c, 0x02, 0x4e, 0x59, 0x31, 0x11,
+ 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c,
+ 0x08, 0x42, 0x72, 0x6f, 0x6f, 0x6b, 0x6c, 0x79,
+ 0x6e, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55,
+ 0x04, 0x0a, 0x0c, 0x0c, 0x4d, 0x79, 0x20, 0x43,
+ 0x41, 0x20, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74,
+ 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x0c, 0x0e, 0x6d, 0x79, 0x63, 0x61, 0x63,
+ 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01,
+ 0x16, 0x12, 0x6a, 0x76, 0x73, 0x68, 0x61, 0x68,
+ 0x69, 0x64, 0x40, 0x67, 0x6d, 0x61, 0x69, 0x6c,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d,
+ 0x31, 0x33, 0x30, 0x35, 0x32, 0x36, 0x32, 0x31,
+ 0x34, 0x34, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x31,
+ 0x33, 0x30, 0x36, 0x32, 0x35, 0x32, 0x31, 0x34,
+ 0x34, 0x30, 0x30, 0x5a, 0x30, 0x7d, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x55, 0x53, 0x31, 0x11, 0x30, 0x0f, 0x06,
+ 0x03, 0x55, 0x04, 0x08, 0x0c, 0x08, 0x4e, 0x65,
+ 0x77, 0x20, 0x59, 0x6f, 0x72, 0x6b, 0x31, 0x11,
+ 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c,
+ 0x08, 0x42, 0x72, 0x6f, 0x6f, 0x6b, 0x6c, 0x79,
+ 0x6e, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55,
+ 0x04, 0x0a, 0x0c, 0x07, 0x4d, 0x79, 0x20, 0x4c,
+ 0x65, 0x61, 0x66, 0x31, 0x13, 0x30, 0x11, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x0c, 0x0a, 0x6d, 0x79,
+ 0x6c, 0x65, 0x61, 0x66, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x31, 0x21, 0x30, 0x1f, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16,
+ 0x12, 0x6a, 0x76, 0x73, 0x68, 0x61, 0x68, 0x69,
+ 0x64, 0x40, 0x67, 0x6d, 0x61, 0x69, 0x6c, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x30, 0x82, 0x01, 0x22, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82,
+ 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02,
+ 0x82, 0x01, 0x01, 0x00, 0xa0, 0xa3, 0xef, 0xc1,
+ 0x44, 0x7d, 0xa2, 0xe3, 0x71, 0x98, 0x27, 0x63,
+ 0xb3, 0x1d, 0x71, 0x50, 0xa6, 0x34, 0x15, 0xcb,
+ 0xc9, 0x2a, 0xc3, 0xea, 0xe4, 0x9e, 0x9c, 0x49,
+ 0xa6, 0x01, 0x9b, 0x7e, 0xa9, 0xb5, 0x7a, 0xff,
+ 0x15, 0x92, 0x71, 0xc8, 0x97, 0x9c, 0x25, 0xb7,
+ 0x79, 0x2b, 0xff, 0xab, 0xc6, 0xb1, 0xa7, 0x00,
+ 0x90, 0xb2, 0x8b, 0xd7, 0x71, 0xd5, 0xc2, 0x3a,
+ 0xe6, 0x82, 0x42, 0x37, 0x89, 0x41, 0x04, 0xb0,
+ 0xba, 0xc7, 0x5b, 0x8a, 0x43, 0x9f, 0x97, 0x39,
+ 0x0c, 0x0f, 0xd5, 0x6d, 0x9e, 0x8d, 0xeb, 0xc0,
+ 0x26, 0xc5, 0x18, 0xe8, 0x7a, 0x3d, 0x32, 0x2e,
+ 0x38, 0x90, 0x40, 0x5b, 0x39, 0x2c, 0x07, 0xcb,
+ 0x24, 0x10, 0xc5, 0xc9, 0x3b, 0xe3, 0x66, 0x47,
+ 0x57, 0xb9, 0x6a, 0xad, 0x44, 0xf8, 0xd0, 0x70,
+ 0x62, 0x3b, 0x8e, 0xed, 0x60, 0x5f, 0x22, 0xf8,
+ 0xb8, 0x0c, 0xc9, 0x41, 0x2b, 0xc9, 0x80, 0x6e,
+ 0x4e, 0x1b, 0xe1, 0x20, 0xfc, 0x47, 0xa4, 0xac,
+ 0xc3, 0x3f, 0xe6, 0xc2, 0x81, 0x79, 0x03, 0x37,
+ 0x25, 0x89, 0xca, 0xd6, 0xa5, 0x46, 0x91, 0x63,
+ 0x41, 0xc5, 0x3e, 0xd5, 0xed, 0x7f, 0x4f, 0x8d,
+ 0x06, 0xc0, 0x89, 0x00, 0xbe, 0x37, 0x7b, 0x7e,
+ 0x73, 0xca, 0x70, 0x00, 0x14, 0x34, 0xbe, 0x47,
+ 0xbc, 0xb2, 0x6a, 0x28, 0xa5, 0x29, 0x84, 0xa8,
+ 0x9d, 0xc8, 0x1e, 0x77, 0x66, 0x1f, 0x9f, 0xaa,
+ 0x2b, 0x47, 0xdb, 0xdd, 0x6b, 0x9c, 0xa8, 0xfc,
+ 0x82, 0x36, 0x94, 0x62, 0x0d, 0x5c, 0x3f, 0xb2,
+ 0x01, 0xb4, 0xa5, 0xb8, 0xc6, 0x0e, 0x94, 0x5b,
+ 0xec, 0x5e, 0xbb, 0x7a, 0x63, 0x24, 0xf1, 0xf9,
+ 0xd6, 0x50, 0x08, 0xc1, 0xa3, 0xcc, 0x90, 0x07,
+ 0x5b, 0x04, 0x04, 0x42, 0x74, 0xcf, 0x37, 0xfa,
+ 0xf0, 0xa5, 0xd9, 0xd3, 0x86, 0x89, 0x89, 0x18,
+ 0xf3, 0x4c, 0xe2, 0x11, 0x02, 0x03, 0x01, 0x00,
+ 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x01, 0x00, 0x90, 0xbb, 0xf9,
+ 0x5e, 0xba, 0x17, 0x1f, 0xac, 0x21, 0x9f, 0x6b,
+ 0x4a, 0x46, 0xd0, 0x6d, 0x3c, 0x8f, 0x3d, 0xf8,
+ 0x5e, 0x3e, 0x72, 0xaf, 0xa0, 0x1a, 0xf3, 0xff,
+ 0x89, 0xac, 0x5b, 0x7a, 0xe2, 0x91, 0x2a, 0x23,
+ 0x85, 0xc6, 0x4d, 0x47, 0x67, 0x01, 0x08, 0xa8,
+ 0x05, 0x1d, 0x01, 0x60, 0x50, 0x5f, 0x59, 0xad,
+ 0xfe, 0x7b, 0xc6, 0x0c, 0x54, 0x90, 0x68, 0x70,
+ 0x67, 0x2e, 0xed, 0x87, 0xf8, 0x69, 0x8a, 0xac,
+ 0x32, 0xfe, 0x6f, 0x90, 0x19, 0x2a, 0x64, 0x8d,
+ 0x82, 0x66, 0x05, 0x43, 0x88, 0xee, 0xf2, 0x30,
+ 0xed, 0xa4, 0x8f, 0xbf, 0xd6, 0x57, 0x20, 0xd4,
+ 0x43, 0x1d, 0x52, 0x96, 0x6f, 0xae, 0x09, 0x96,
+ 0x01, 0x52, 0x38, 0xe3, 0xaf, 0x99, 0xd7, 0xdc,
+ 0x14, 0x99, 0xc4, 0x8b, 0x0e, 0x04, 0x0f, 0xb3,
+ 0x14, 0x14, 0xd4, 0xa5, 0x93, 0xe1, 0xc9, 0x8a,
+ 0x81, 0xef, 0x63, 0xfc, 0x36, 0x77, 0x05, 0x06,
+ 0xf0, 0x2a, 0x04, 0x0a, 0xbe, 0x2e, 0xce, 0x81,
+ 0x3d, 0x23, 0xa1, 0xda, 0xd8, 0xeb, 0xc6, 0xea,
+ 0x5e, 0xcf, 0x28, 0x36, 0x51, 0x31, 0x95, 0x5e,
+ 0x40, 0x04, 0xed, 0xac, 0xc1, 0xc8, 0x56, 0x69,
+ 0x87, 0xec, 0x3b, 0x03, 0x3e, 0x9d, 0x0f, 0x4c,
+ 0x4c, 0xeb, 0xd7, 0xba, 0x26, 0xdf, 0xe3, 0xde,
+ 0x10, 0xee, 0x93, 0x62, 0x8d, 0x73, 0x52, 0x6e,
+ 0xff, 0x37, 0x36, 0x98, 0x7b, 0x2d, 0x56, 0x4c,
+ 0xba, 0x09, 0xb8, 0xa7, 0xf0, 0x3b, 0x16, 0x81,
+ 0xca, 0xdb, 0x43, 0xab, 0xec, 0x4c, 0x6e, 0x7c,
+ 0xc1, 0x0b, 0x22, 0x22, 0x43, 0x1d, 0xb6, 0x0c,
+ 0xc1, 0xb9, 0xcf, 0xe4, 0x53, 0xee, 0x1d, 0x3e,
+ 0x88, 0xa7, 0x13, 0xbe, 0x7f, 0xbd, 0xae, 0x72,
+ 0xcf, 0xcd, 0x63, 0xd2, 0xc3, 0x18, 0x58, 0x92,
+ 0xa2, 0xad, 0xb5, 0x09, 0x9d, 0x91, 0x03, 0xdd,
+ 0x3c, 0xe2, 0x1c, 0xde, 0x78, 0x00, 0x03, 0x88,
+ 0x30, 0x82, 0x03, 0x84, 0x30, 0x82, 0x02, 0x6c,
+ 0x02, 0x09, 0x00, 0xab, 0xed, 0xa6, 0xe4, 0x4a,
+ 0x2b, 0x2b, 0xf8, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05,
+ 0x05, 0x00, 0x30, 0x81, 0x86, 0x31, 0x0b, 0x30,
+ 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
+ 0x55, 0x04, 0x08, 0x0c, 0x02, 0x4e, 0x59, 0x31,
+ 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07,
+ 0x0c, 0x08, 0x42, 0x72, 0x6f, 0x6f, 0x6b, 0x6c,
+ 0x79, 0x6e, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03,
+ 0x55, 0x04, 0x0a, 0x0c, 0x18, 0x4d, 0x79, 0x20,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
+ 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68,
+ 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x11, 0x30,
+ 0x0f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x08,
+ 0x6d, 0x79, 0x63, 0x61, 0x2e, 0x6f, 0x72, 0x67,
+ 0x31, 0x21, 0x30, 0x1f, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16,
+ 0x12, 0x6a, 0x76, 0x73, 0x68, 0x61, 0x68, 0x69,
+ 0x64, 0x40, 0x67, 0x6d, 0x61, 0x69, 0x6c, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, 0x31,
+ 0x33, 0x30, 0x35, 0x32, 0x36, 0x32, 0x31, 0x31,
+ 0x38, 0x34, 0x30, 0x5a, 0x17, 0x0d, 0x31, 0x33,
+ 0x30, 0x36, 0x32, 0x35, 0x32, 0x31, 0x31, 0x38,
+ 0x34, 0x30, 0x5a, 0x30, 0x81, 0x80, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x04, 0x08, 0x0c, 0x02, 0x4e, 0x59,
+ 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04,
+ 0x07, 0x0c, 0x08, 0x42, 0x72, 0x6f, 0x6f, 0x6b,
+ 0x6c, 0x79, 0x6e, 0x31, 0x15, 0x30, 0x13, 0x06,
+ 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0c, 0x4d, 0x79,
+ 0x20, 0x43, 0x41, 0x20, 0x43, 0x6c, 0x69, 0x65,
+ 0x6e, 0x74, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x0c, 0x0e, 0x6d, 0x79, 0x63,
+ 0x61, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x31, 0x21, 0x30, 0x1f, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x09, 0x01, 0x16, 0x12, 0x6a, 0x76, 0x73, 0x68,
+ 0x61, 0x68, 0x69, 0x64, 0x40, 0x67, 0x6d, 0x61,
+ 0x69, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x82,
+ 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82,
+ 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xce,
+ 0x13, 0xf0, 0x72, 0xb0, 0x61, 0xc8, 0x18, 0x37,
+ 0x8a, 0x41, 0x3d, 0x20, 0xa1, 0x1c, 0xcb, 0xbf,
+ 0xf6, 0x3b, 0x74, 0x26, 0x2a, 0x96, 0x11, 0xec,
+ 0x53, 0xa1, 0xcc, 0x7d, 0x77, 0x56, 0x45, 0x0f,
+ 0x36, 0xb7, 0xf2, 0x48, 0x92, 0x1a, 0x62, 0xcc,
+ 0xb6, 0xc0, 0xa1, 0x2f, 0x44, 0x2b, 0xc1, 0x89,
+ 0xcb, 0x6e, 0x1e, 0xdb, 0x57, 0x92, 0xd5, 0x97,
+ 0x60, 0x8c, 0x41, 0x2c, 0xd9, 0x20, 0xfe, 0xe9,
+ 0x1f, 0x8e, 0xfc, 0x7f, 0x02, 0x44, 0x0f, 0x28,
+ 0x81, 0xd6, 0x0c, 0xcd, 0xbc, 0xf0, 0x57, 0x6c,
+ 0xcc, 0xa7, 0xba, 0x06, 0xa0, 0xa6, 0x91, 0xda,
+ 0xef, 0x46, 0x8a, 0x60, 0x0f, 0x52, 0x6c, 0x90,
+ 0x6c, 0x8c, 0x44, 0xaf, 0xb0, 0x9d, 0x90, 0xba,
+ 0x21, 0x58, 0xa0, 0x3c, 0xee, 0x54, 0xb5, 0x29,
+ 0x26, 0x1f, 0x0a, 0xac, 0xef, 0x48, 0x68, 0x33,
+ 0xd0, 0x33, 0xd0, 0x8b, 0x1a, 0xec, 0x6e, 0x2f,
+ 0xb5, 0x4a, 0x53, 0xc2, 0x1a, 0xd2, 0xf1, 0x50,
+ 0x05, 0x59, 0x5c, 0xd9, 0xda, 0x03, 0x0a, 0x47,
+ 0xb7, 0xdd, 0xf7, 0x3a, 0x69, 0xf5, 0x4e, 0xea,
+ 0x4a, 0xc2, 0xca, 0x54, 0xb0, 0x8b, 0x76, 0xe1,
+ 0x02, 0x2d, 0x52, 0x67, 0xb9, 0xdd, 0x50, 0xc9,
+ 0x3b, 0x07, 0x24, 0x22, 0x6a, 0x00, 0x1d, 0x58,
+ 0x83, 0xa8, 0xec, 0x95, 0xf1, 0xda, 0xe2, 0x73,
+ 0xa0, 0xa1, 0x72, 0x60, 0x9e, 0x86, 0x53, 0xcb,
+ 0x45, 0xa8, 0xc2, 0xa0, 0x50, 0xa0, 0x53, 0xd6,
+ 0xfc, 0x18, 0x84, 0xb5, 0x4a, 0x26, 0xd0, 0xa2,
+ 0xaa, 0xd0, 0xff, 0xb6, 0xfe, 0x3a, 0x9c, 0xb5,
+ 0x19, 0x3b, 0x3f, 0xe1, 0x48, 0x0d, 0xa4, 0x09,
+ 0x4f, 0x83, 0xc9, 0xc0, 0xc9, 0xa6, 0x0b, 0x58,
+ 0x1f, 0x1c, 0x7b, 0xac, 0xa2, 0x42, 0xbc, 0x61,
+ 0xf4, 0x21, 0x8a, 0x00, 0xda, 0x14, 0xa0, 0x60,
+ 0x03, 0xfe, 0x93, 0x12, 0x6c, 0x56, 0xcd, 0x02,
+ 0x03, 0x01, 0x00, 0x01, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00,
+ 0x25, 0x29, 0x3b, 0x1e, 0xc3, 0x58, 0x32, 0xe6,
+ 0x23, 0xc8, 0xee, 0x18, 0xf0, 0x1d, 0x62, 0x6d,
+ 0x3b, 0x59, 0x99, 0x3a, 0xfe, 0x49, 0x72, 0x07,
+ 0x3f, 0x58, 0x93, 0xdb, 0xc0, 0xaf, 0xb0, 0xb3,
+ 0x5c, 0xd1, 0x5c, 0x98, 0xc8, 0xea, 0x4a, 0xe4,
+ 0x58, 0x73, 0x0d, 0x57, 0xc5, 0x13, 0x7c, 0x5c,
+ 0x79, 0x66, 0xda, 0x04, 0x1d, 0xe5, 0x98, 0xda,
+ 0x35, 0x47, 0x44, 0xb0, 0xd2, 0x7a, 0x66, 0x9d,
+ 0xcd, 0x41, 0xa5, 0x8f, 0xa1, 0x11, 0xb2, 0x1a,
+ 0x87, 0xc0, 0xcd, 0x55, 0xed, 0xb4, 0x7b, 0x33,
+ 0x72, 0xeb, 0xf7, 0xe3, 0x7b, 0x8b, 0x02, 0x86,
+ 0xe9, 0x2b, 0x26, 0x32, 0x9f, 0x99, 0xf1, 0xcb,
+ 0x93, 0xab, 0xb9, 0x16, 0xb3, 0x9a, 0xb2, 0x22,
+ 0x13, 0x21, 0x1f, 0x5b, 0xcc, 0xa2, 0x59, 0xbb,
+ 0x69, 0xf2, 0xb8, 0x07, 0x80, 0xce, 0x0c, 0xf7,
+ 0x98, 0x4c, 0x85, 0xc2, 0x96, 0x6a, 0x22, 0x05,
+ 0xe9, 0xbe, 0x48, 0xb0, 0x02, 0x5b, 0x69, 0x28,
+ 0x18, 0x88, 0x96, 0xe3, 0xd7, 0xc6, 0x7a, 0xd3,
+ 0xe9, 0x99, 0xff, 0x9d, 0xc3, 0x61, 0x4d, 0x9a,
+ 0x96, 0xf2, 0xc6, 0x33, 0x4d, 0xe5, 0x5d, 0x5a,
+ 0x68, 0x64, 0x5a, 0x82, 0x35, 0x65, 0x25, 0xe3,
+ 0x8c, 0x5b, 0xb0, 0xf6, 0x96, 0x56, 0xbc, 0xbf,
+ 0x97, 0x76, 0x4b, 0x66, 0x44, 0x81, 0xa4, 0xc4,
+ 0xa7, 0x31, 0xc5, 0xa1, 0x4f, 0xe8, 0xa4, 0xca,
+ 0x20, 0xf5, 0x01, 0x5b, 0x99, 0x4f, 0x5a, 0xf4,
+ 0xf0, 0x78, 0xbf, 0x71, 0x49, 0xd5, 0xf1, 0xc1,
+ 0xa2, 0x18, 0xfd, 0x72, 0x5b, 0x16, 0xe8, 0x92,
+ 0xc7, 0x37, 0x48, 0xaf, 0xee, 0x24, 0xfc, 0x35,
+ 0x0b, 0xc2, 0xdd, 0x05, 0xc7, 0x6e, 0xa3, 0x29,
+ 0xbb, 0x29, 0x7d, 0xd3, 0x2b, 0x94, 0x80, 0xc3,
+ 0x40, 0x53, 0x0e, 0x03, 0x54, 0x3d, 0x7b, 0x8b,
+ 0xce, 0xf9, 0xa4, 0x03, 0x27, 0x63, 0xec, 0x51,
+ 0x00, 0x03, 0xe5, 0x30, 0x82, 0x03, 0xe1, 0x30,
+ 0x82, 0x02, 0xc9, 0xa0, 0x03, 0x02, 0x01, 0x02,
+ 0x02, 0x09, 0x00, 0xcc, 0x22, 0x4c, 0x4b, 0x98,
+ 0xa2, 0x88, 0xfc, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05,
+ 0x05, 0x00, 0x30, 0x81, 0x86, 0x31, 0x0b, 0x30,
+ 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
+ 0x55, 0x04, 0x08, 0x0c, 0x02, 0x4e, 0x59, 0x31,
+ 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07,
+ 0x0c, 0x08, 0x42, 0x72, 0x6f, 0x6f, 0x6b, 0x6c,
+ 0x79, 0x6e, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03,
+ 0x55, 0x04, 0x0a, 0x0c, 0x18, 0x4d, 0x79, 0x20,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
+ 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68,
+ 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x11, 0x30,
+ 0x0f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x08,
+ 0x6d, 0x79, 0x63, 0x61, 0x2e, 0x6f, 0x72, 0x67,
+ 0x31, 0x21, 0x30, 0x1f, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16,
+ 0x12, 0x6a, 0x76, 0x73, 0x68, 0x61, 0x68, 0x69,
+ 0x64, 0x40, 0x67, 0x6d, 0x61, 0x69, 0x6c, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, 0x31,
+ 0x33, 0x30, 0x35, 0x32, 0x36, 0x32, 0x31, 0x30,
+ 0x35, 0x30, 0x31, 0x5a, 0x17, 0x0d, 0x32, 0x33,
+ 0x30, 0x35, 0x32, 0x34, 0x32, 0x31, 0x30, 0x35,
+ 0x30, 0x31, 0x5a, 0x30, 0x81, 0x86, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x04, 0x08, 0x0c, 0x02, 0x4e, 0x59,
+ 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04,
+ 0x07, 0x0c, 0x08, 0x42, 0x72, 0x6f, 0x6f, 0x6b,
+ 0x6c, 0x79, 0x6e, 0x31, 0x21, 0x30, 0x1f, 0x06,
+ 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x18, 0x4d, 0x79,
+ 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
+ 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74,
+ 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x11,
+ 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c,
+ 0x08, 0x6d, 0x79, 0x63, 0x61, 0x2e, 0x6f, 0x72,
+ 0x67, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01,
+ 0x16, 0x12, 0x6a, 0x76, 0x73, 0x68, 0x61, 0x68,
+ 0x69, 0x64, 0x40, 0x67, 0x6d, 0x61, 0x69, 0x6c,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x82, 0x01, 0x22,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
+ 0x02, 0x82, 0x01, 0x01, 0x00, 0xf0, 0xfb, 0xad,
+ 0x80, 0x5e, 0x37, 0xd3, 0x6d, 0xee, 0x2e, 0xcc,
+ 0xbc, 0x0c, 0xd7, 0x56, 0x4b, 0x56, 0x45, 0xcd,
+ 0x28, 0xb6, 0x22, 0xe9, 0xe2, 0x0f, 0xd1, 0x87,
+ 0x2a, 0x27, 0xce, 0x77, 0x8d, 0x6e, 0x0e, 0x0f,
+ 0xfb, 0x66, 0xe1, 0xb5, 0x0e, 0x9a, 0xb6, 0x05,
+ 0x8e, 0xb3, 0xe1, 0xc5, 0x77, 0x86, 0x5b, 0x46,
+ 0xd2, 0x0b, 0x92, 0x03, 0x1b, 0x89, 0x0c, 0x1b,
+ 0x10, 0x0e, 0x99, 0x8f, 0xe2, 0x17, 0xe8, 0xc2,
+ 0x30, 0x00, 0x47, 0xd6, 0xfc, 0xf9, 0x0f, 0x3b,
+ 0x75, 0x34, 0x8d, 0x4d, 0xb0, 0x99, 0xb7, 0xa0,
+ 0x6d, 0xa0, 0xb6, 0xad, 0xda, 0x07, 0x5e, 0x38,
+ 0x2e, 0x02, 0xe4, 0x30, 0x6d, 0xae, 0x13, 0x72,
+ 0xd4, 0xc8, 0xce, 0x14, 0x07, 0xae, 0x23, 0x8c,
+ 0x8f, 0x9e, 0x8c, 0x60, 0xd6, 0x06, 0xb9, 0xef,
+ 0x00, 0x18, 0xc0, 0x1d, 0x25, 0x1e, 0xda, 0x3e,
+ 0x2f, 0xcf, 0x2b, 0x56, 0x84, 0x9e, 0x30, 0x21,
+ 0xc7, 0x29, 0xf6, 0x03, 0x8a, 0x24, 0xf9, 0x34,
+ 0xac, 0x65, 0x9d, 0x80, 0x36, 0xc8, 0x3b, 0x15,
+ 0x10, 0xbd, 0x51, 0xe9, 0xbc, 0x02, 0xe1, 0xe9,
+ 0xb3, 0x5a, 0x9a, 0x99, 0x41, 0x1b, 0x27, 0xa0,
+ 0x4d, 0x50, 0x9e, 0x27, 0x7f, 0xa1, 0x7d, 0x09,
+ 0x87, 0xbd, 0x8a, 0xca, 0x5f, 0xb1, 0xa5, 0x08,
+ 0xb8, 0x04, 0xd4, 0x52, 0x89, 0xaa, 0xe0, 0x7d,
+ 0x42, 0x2e, 0x2f, 0x15, 0xee, 0x66, 0x57, 0x0f,
+ 0x13, 0x19, 0x45, 0xa8, 0x4b, 0x5d, 0x81, 0x66,
+ 0xcc, 0x12, 0x37, 0x94, 0x5e, 0xfd, 0x3c, 0x10,
+ 0x81, 0x51, 0x3f, 0xfa, 0x0f, 0xdd, 0xa1, 0x89,
+ 0x03, 0xa9, 0x78, 0x91, 0xf5, 0x3b, 0xf3, 0xbc,
+ 0xac, 0xbe, 0x93, 0x30, 0x2e, 0xbe, 0xca, 0x7f,
+ 0x46, 0xd3, 0x28, 0xb4, 0x4e, 0x91, 0x7b, 0x5b,
+ 0x43, 0x6c, 0xaf, 0x9b, 0x5c, 0x6a, 0x6d, 0x5a,
+ 0xdb, 0x79, 0x5e, 0x6a, 0x6b, 0x02, 0x03, 0x01,
+ 0x00, 0x01, 0xa3, 0x50, 0x30, 0x4e, 0x30, 0x1d,
+ 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04,
+ 0x14, 0x6b, 0x1e, 0x00, 0xa8, 0x9f, 0xfa, 0x7d,
+ 0x00, 0xf9, 0xe0, 0x9d, 0x0f, 0x90, 0x8c, 0x90,
+ 0xa8, 0xa1, 0x37, 0x6b, 0xda, 0x30, 0x1f, 0x06,
+ 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
+ 0x80, 0x14, 0x6b, 0x1e, 0x00, 0xa8, 0x9f, 0xfa,
+ 0x7d, 0x00, 0xf9, 0xe0, 0x9d, 0x0f, 0x90, 0x8c,
+ 0x90, 0xa8, 0xa1, 0x37, 0x6b, 0xda, 0x30, 0x0c,
+ 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, 0x30,
+ 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00,
+ 0xcd, 0x6f, 0x73, 0x4d, 0x56, 0x0b, 0xf3, 0x2e,
+ 0x1c, 0xe2, 0x02, 0x0c, 0x14, 0xbb, 0x2f, 0xdd,
+ 0x3c, 0x43, 0xfe, 0xdf, 0x94, 0x2d, 0xa9, 0x89,
+ 0x81, 0x51, 0xf8, 0x5f, 0xa7, 0xa0, 0x13, 0xaa,
+ 0xcc, 0xb0, 0x18, 0xe2, 0x57, 0x3e, 0x0d, 0x29,
+ 0x93, 0xe8, 0x95, 0xd5, 0x1b, 0x53, 0xd2, 0x51,
+ 0xf2, 0xbd, 0xf5, 0x9e, 0x7b, 0x22, 0x65, 0x62,
+ 0x5c, 0xc4, 0x4c, 0x1d, 0xe8, 0xe9, 0xc3, 0xd4,
+ 0x2b, 0xe7, 0x78, 0xcb, 0x10, 0xf3, 0xfe, 0x06,
+ 0x83, 0xdc, 0x3a, 0x1e, 0x62, 0x10, 0xc0, 0x46,
+ 0x77, 0xc6, 0x9d, 0x9f, 0xab, 0x96, 0x25, 0x5c,
+ 0xfb, 0x26, 0xc1, 0x15, 0x1f, 0xa5, 0x33, 0xee,
+ 0x4f, 0x9a, 0x14, 0x6a, 0x14, 0x97, 0x93, 0x2b,
+ 0x95, 0x0b, 0xdc, 0xa8, 0xd7, 0x69, 0x2e, 0xf0,
+ 0x01, 0x0e, 0xfd, 0x4e, 0xd0, 0xd9, 0xa8, 0xe5,
+ 0x65, 0xde, 0xfb, 0xca, 0xca, 0x1c, 0x5f, 0xf9,
+ 0x53, 0xa0, 0x87, 0xe7, 0x33, 0x9b, 0x2f, 0xcf,
+ 0xe4, 0x13, 0xfc, 0xec, 0x7a, 0x6c, 0xb0, 0x90,
+ 0x13, 0x9b, 0xb6, 0xc5, 0x03, 0xf6, 0x0e, 0x5e,
+ 0xe2, 0xe4, 0x26, 0xc1, 0x7e, 0x53, 0xfe, 0x69,
+ 0xa3, 0xc7, 0xd8, 0x8e, 0x6e, 0x94, 0x32, 0xa0,
+ 0xde, 0xca, 0xb6, 0xcc, 0xd6, 0x01, 0xd5, 0x78,
+ 0x40, 0x28, 0x63, 0x9b, 0xee, 0xcf, 0x09, 0x3b,
+ 0x35, 0x04, 0xf0, 0x14, 0x02, 0xf6, 0x80, 0x0e,
+ 0x90, 0xb2, 0x94, 0xd2, 0x25, 0x16, 0xb8, 0x7a,
+ 0x76, 0x87, 0x84, 0x9f, 0x84, 0xc5, 0xaf, 0xc2,
+ 0x6d, 0x68, 0x7a, 0x84, 0x9c, 0xc6, 0x8a, 0x63,
+ 0x60, 0x87, 0x6a, 0x25, 0xc1, 0xa1, 0x78, 0x0f,
+ 0xba, 0xe8, 0x5f, 0xe1, 0xba, 0xac, 0xa4, 0x6f,
+ 0xdd, 0x09, 0x3f, 0x12, 0xcb, 0x1d, 0xf3, 0xcf,
+ 0x48, 0xd7, 0xd3, 0x26, 0xe8, 0x9c, 0xc3, 0x53,
+ 0xb3, 0xba, 0xdc, 0x32, 0x99, 0x98, 0x96, 0xd6,
+ 0x16, 0x03, 0x01, 0x01, 0x06, 0x10, 0x00, 0x01,
+ 0x02, 0x01, 0x00, 0x6e, 0xea, 0x15, 0x6f, 0x21,
+ 0xbd, 0x2d, 0x14, 0xde, 0x9d, 0x02, 0xeb, 0xdf,
+ 0x3b, 0x09, 0x75, 0xaf, 0x32, 0x80, 0x0c, 0xe2,
+ 0xc2, 0x7b, 0x0d, 0xca, 0x24, 0x96, 0xf6, 0x3e,
+ 0xa5, 0x97, 0xba, 0x0c, 0x50, 0x7e, 0xb3, 0x68,
+ 0x58, 0xc6, 0xd8, 0xec, 0xab, 0xa9, 0xd9, 0x3a,
+ 0xb1, 0x49, 0xea, 0x2f, 0xd7, 0xdb, 0x15, 0x1b,
+ 0xb5, 0xaf, 0xec, 0xcc, 0x40, 0x5c, 0xe6, 0x0f,
+ 0xc4, 0x33, 0x71, 0xe7, 0x41, 0xc0, 0x04, 0x89,
+ 0x60, 0x3e, 0xb7, 0xe6, 0xda, 0x38, 0x62, 0x27,
+ 0x6a, 0xd9, 0xfb, 0x93, 0x94, 0x9d, 0xc1, 0x63,
+ 0x92, 0x5c, 0x88, 0x19, 0x38, 0x81, 0x79, 0x9d,
+ 0x59, 0x48, 0x5e, 0xd3, 0xc8, 0xea, 0xcb, 0x6e,
+ 0x66, 0x66, 0x03, 0xdc, 0x0c, 0x2d, 0x95, 0xb1,
+ 0x4d, 0x68, 0xc7, 0xc5, 0x6e, 0xfa, 0x94, 0x14,
+ 0xdf, 0x2c, 0x70, 0x69, 0x04, 0xf4, 0x69, 0xf1,
+ 0xf0, 0x07, 0xbd, 0x23, 0x53, 0x63, 0xb3, 0x41,
+ 0xec, 0xa7, 0x10, 0xa5, 0x04, 0x84, 0x24, 0xb5,
+ 0xf5, 0x0c, 0x0f, 0x5d, 0x02, 0x47, 0x79, 0x60,
+ 0x76, 0xbb, 0xdf, 0x60, 0xa6, 0xd7, 0x4d, 0x08,
+ 0x7d, 0xa6, 0x85, 0x4f, 0x61, 0xac, 0x96, 0x3d,
+ 0xbc, 0xaf, 0x07, 0xb0, 0x7c, 0xb6, 0x23, 0x3e,
+ 0x1f, 0x0a, 0x62, 0x77, 0x97, 0x77, 0xae, 0x33,
+ 0x55, 0x0f, 0x85, 0xdf, 0xdc, 0xbe, 0xc6, 0xe0,
+ 0xe0, 0x14, 0x83, 0x4c, 0x50, 0xf0, 0xe5, 0x2d,
+ 0xdc, 0x0b, 0x74, 0x7f, 0xc3, 0x28, 0x98, 0x16,
+ 0xda, 0x74, 0xe6, 0x40, 0xc2, 0xf0, 0xea, 0xc0,
+ 0x00, 0xd5, 0xfc, 0x16, 0xe4, 0x43, 0xa1, 0xfc,
+ 0x31, 0x19, 0x81, 0x62, 0xec, 0x2b, 0xfe, 0xcc,
+ 0xe8, 0x19, 0xed, 0xa1, 0x1e, 0x6a, 0x49, 0x73,
+ 0xde, 0xc4, 0xe9, 0x22, 0x0a, 0x21, 0xde, 0x45,
+ 0x1e, 0x55, 0x12, 0xd9, 0x44, 0xef, 0x4e, 0xaa,
+ 0x5e, 0x26, 0x57, 0x16, 0x03, 0x01, 0x01, 0x06,
+ 0x0f, 0x00, 0x01, 0x02, 0x01, 0x00, 0x23, 0xde,
+ 0xb0, 0x39, 0x60, 0xe9, 0x82, 0xb8, 0xed, 0x17,
+ 0x78, 0xd2, 0x37, 0x0e, 0x85, 0x69, 0xda, 0xcc,
+ 0x9f, 0x54, 0x4d, 0xda, 0xce, 0xe8, 0x5a, 0xeb,
+ 0x3c, 0x61, 0x4c, 0x7a, 0x84, 0x1f, 0x21, 0x03,
+ 0xb3, 0x8a, 0x74, 0x3b, 0x6a, 0x9e, 0x4f, 0x44,
+ 0xd9, 0x75, 0x0a, 0xd8, 0x7e, 0x56, 0xa3, 0xef,
+ 0x5a, 0xfe, 0x8a, 0x35, 0xce, 0x29, 0x18, 0xfe,
+ 0xa6, 0x61, 0x8e, 0x8f, 0x00, 0x90, 0x2d, 0x85,
+ 0xe3, 0x6c, 0x0e, 0x8d, 0x8c, 0x27, 0x80, 0x8c,
+ 0x9f, 0x51, 0xe9, 0xd3, 0xe6, 0x7d, 0x70, 0xe9,
+ 0xfb, 0xcb, 0xb8, 0x24, 0x94, 0x30, 0x9b, 0xba,
+ 0x01, 0x14, 0x49, 0x9f, 0xaf, 0x09, 0xd8, 0x26,
+ 0x1b, 0x23, 0xa4, 0xb8, 0xd9, 0x44, 0x0a, 0xdc,
+ 0x4e, 0x27, 0xe7, 0x32, 0xf5, 0x9c, 0xf3, 0x8d,
+ 0xa0, 0xc5, 0xc4, 0xbe, 0x92, 0x02, 0x85, 0x4f,
+ 0x33, 0x8f, 0xa7, 0xf7, 0x87, 0xa9, 0x44, 0xf3,
+ 0x64, 0xbd, 0x32, 0x04, 0xeb, 0xc5, 0xc3, 0x62,
+ 0xe9, 0xda, 0x2f, 0x95, 0x5c, 0xf7, 0x58, 0x3e,
+ 0xad, 0x35, 0xd7, 0x7e, 0xad, 0xdd, 0x32, 0x8d,
+ 0xce, 0x81, 0x08, 0xad, 0x49, 0xf7, 0xdb, 0xf7,
+ 0xaf, 0xe3, 0xc6, 0xb2, 0xdd, 0x76, 0x0c, 0xcf,
+ 0x0f, 0x87, 0x79, 0x90, 0x10, 0x79, 0xc6, 0xc8,
+ 0x7b, 0xe6, 0x23, 0xf2, 0xda, 0x33, 0xca, 0xe1,
+ 0xf0, 0x59, 0x42, 0x43, 0x03, 0x56, 0x19, 0xe3,
+ 0x8b, 0xe6, 0xa8, 0x70, 0xbc, 0x80, 0xfa, 0x24,
+ 0xae, 0x03, 0x13, 0x30, 0x0d, 0x1f, 0xab, 0xb7,
+ 0x82, 0xd9, 0x24, 0x90, 0x80, 0xbf, 0x75, 0xe1,
+ 0x0d, 0x1c, 0xb2, 0xfe, 0x92, 0x2c, 0x4d, 0x21,
+ 0xe9, 0x5d, 0xa1, 0x68, 0xf3, 0x16, 0xd8, 0x3f,
+ 0xb2, 0xc3, 0x00, 0x3e, 0xd8, 0x42, 0x25, 0x5c,
+ 0x90, 0x11, 0xc0, 0x1b, 0xd4, 0x26, 0x5c, 0x37,
+ 0x47, 0xbd, 0xf8, 0x1e, 0x34, 0xa9, 0x14, 0x03,
+ 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x01, 0x00,
+ 0x24, 0x8f, 0x94, 0x7e, 0x01, 0xee, 0xd5, 0x4f,
+ 0x83, 0x41, 0x31, 0xc0, 0x36, 0x81, 0x46, 0xc3,
+ 0xc0, 0xcc, 0x9c, 0xea, 0x0f, 0x29, 0x04, 0x10,
+ 0x43, 0x1e, 0x08, 0x6e, 0x08, 0xce, 0xb2, 0x62,
+ 0xa6, 0x0f, 0x68, 0x9f, 0x99,
+ },
+ {
+ 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03,
+ 0x01, 0x00, 0x24, 0xd9, 0x46, 0x5b, 0xbf, 0xfd,
+ 0x8a, 0xa1, 0x08, 0xd5, 0xf3, 0x0c, 0x1c, 0xd8,
+ 0xa8, 0xb3, 0xe5, 0x89, 0x83, 0x9e, 0x23, 0x47,
+ 0x81, 0x66, 0x77, 0x11, 0x98, 0xe5, 0xf4, 0xac,
+ 0x06, 0xe9, 0x4c, 0x05, 0x8b, 0xc4, 0x16,
+ },
+ {
+ 0x17, 0x03, 0x01, 0x00, 0x1a, 0xc5, 0x28, 0xfd,
+ 0x71, 0xc0, 0xe6, 0x89, 0xb8, 0x82, 0x92, 0x1b,
+ 0xdd, 0x39, 0xe5, 0xbf, 0x41, 0x82, 0x1f, 0xc1,
+ 0xbc, 0x85, 0xe5, 0x32, 0x1b, 0x93, 0x46, 0x15,
+ 0x03, 0x01, 0x00, 0x16, 0x1a, 0x8b, 0x10, 0x42,
+ 0x12, 0xb2, 0xbd, 0xd3, 0xf1, 0x74, 0x1f, 0xc2,
+ 0x10, 0x08, 0xc2, 0x79, 0x99, 0x2c, 0x55, 0xef,
+ 0x4a, 0xbd,
+ },
+}
+
+// $ openssl s_server -tls1_2 -cert server.crt -key server.key \
+// -cipher ECDHE-RSA-AES128-SHA -port 10443
+// $ go test -test.run "TestRunClient" -connect -ciphersuites=0xc013 \
+// -minversion=0x0303 -maxversion=0x0303
+var clientTLS12Script = [][]byte{
+ {
+ 0x16, 0x03, 0x01, 0x00, 0x58, 0x01, 0x00, 0x00,
+ 0x54, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xc0, 0x13,
+ 0x01, 0x00, 0x00, 0x29, 0x00, 0x05, 0x00, 0x05,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00,
+ 0x08, 0x00, 0x06, 0x00, 0x17, 0x00, 0x18, 0x00,
+ 0x19, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00,
+ 0x0d, 0x00, 0x0a, 0x00, 0x08, 0x04, 0x01, 0x04,
+ 0x03, 0x02, 0x01, 0x02, 0x03,
+ },
+ {
+ 0x16, 0x03, 0x03, 0x00, 0x54, 0x02, 0x00, 0x00,
+ 0x50, 0x03, 0x03, 0x52, 0x65, 0x67, 0xbd, 0xe8,
+ 0x72, 0x03, 0x6a, 0x52, 0x8d, 0x28, 0x2c, 0x9a,
+ 0x53, 0xff, 0xc2, 0xa1, 0x62, 0x5f, 0x54, 0xfb,
+ 0x73, 0x00, 0xcf, 0x4d, 0x28, 0x36, 0xc2, 0xee,
+ 0xfd, 0x78, 0xf0, 0x20, 0x6f, 0xbe, 0x49, 0xec,
+ 0x5b, 0x6f, 0xf9, 0x53, 0x42, 0x69, 0x0d, 0x6d,
+ 0x8b, 0x68, 0x2e, 0xca, 0x3c, 0x3c, 0x88, 0x9e,
+ 0x8b, 0xf9, 0x32, 0x65, 0x09, 0xd6, 0xa0, 0x7d,
+ 0xea, 0xc6, 0xd5, 0xc4, 0xc0, 0x13, 0x00, 0x00,
+ 0x08, 0x00, 0x0b, 0x00, 0x04, 0x03, 0x00, 0x01,
+ 0x02, 0x16, 0x03, 0x03, 0x02, 0x39, 0x0b, 0x00,
+ 0x02, 0x35, 0x00, 0x02, 0x32, 0x00, 0x02, 0x2f,
+ 0x30, 0x82, 0x02, 0x2b, 0x30, 0x82, 0x01, 0xd5,
+ 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x09, 0x00,
+ 0xb1, 0x35, 0x13, 0x65, 0x11, 0x20, 0xc5, 0x92,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30,
+ 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
+ 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13,
+ 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
+ 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74,
+ 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06,
+ 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e,
+ 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57,
+ 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50,
+ 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e,
+ 0x17, 0x0d, 0x31, 0x32, 0x30, 0x34, 0x30, 0x36,
+ 0x31, 0x37, 0x31, 0x30, 0x31, 0x33, 0x5a, 0x17,
+ 0x0d, 0x31, 0x35, 0x30, 0x34, 0x30, 0x36, 0x31,
+ 0x37, 0x31, 0x30, 0x31, 0x33, 0x5a, 0x30, 0x45,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
+ 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30,
+ 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a,
+ 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61,
+ 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03,
+ 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74,
+ 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69,
+ 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74,
+ 0x79, 0x20, 0x4c, 0x74, 0x64, 0x30, 0x5c, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x4b,
+ 0x00, 0x30, 0x48, 0x02, 0x41, 0x00, 0x9f, 0xb3,
+ 0xc3, 0x84, 0x27, 0x95, 0xff, 0x12, 0x31, 0x52,
+ 0x0f, 0x15, 0xef, 0x46, 0x11, 0xc4, 0xad, 0x80,
+ 0xe6, 0x36, 0x5b, 0x0f, 0xdd, 0x80, 0xd7, 0x61,
+ 0x8d, 0xe0, 0xfc, 0x72, 0x45, 0x09, 0x34, 0xfe,
+ 0x55, 0x66, 0x45, 0x43, 0x4c, 0x68, 0x97, 0x6a,
+ 0xfe, 0xa8, 0xa0, 0xa5, 0xdf, 0x5f, 0x78, 0xff,
+ 0xee, 0xd7, 0x64, 0xb8, 0x3f, 0x04, 0xcb, 0x6f,
+ 0xff, 0x2a, 0xfe, 0xfe, 0xb9, 0xed, 0x02, 0x03,
+ 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, 0x30, 0x81,
+ 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e,
+ 0x04, 0x16, 0x04, 0x14, 0x78, 0xa6, 0x97, 0x9a,
+ 0x63, 0xb5, 0xc5, 0xa1, 0xa5, 0x33, 0xba, 0x22,
+ 0x7c, 0x23, 0x6e, 0x5b, 0x1b, 0x7a, 0xcc, 0x2b,
+ 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
+ 0x6e, 0x30, 0x6c, 0x80, 0x14, 0x78, 0xa6, 0x97,
+ 0x9a, 0x63, 0xb5, 0xc5, 0xa1, 0xa5, 0x33, 0xba,
+ 0x22, 0x7c, 0x23, 0x6e, 0x5b, 0x1b, 0x7a, 0xcc,
+ 0x2b, 0xa1, 0x49, 0xa4, 0x47, 0x30, 0x45, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11,
+ 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53,
+ 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74,
+ 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55,
+ 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65,
+ 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64,
+ 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79,
+ 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, 0x00, 0xb1,
+ 0x35, 0x13, 0x65, 0x11, 0x20, 0xc5, 0x92, 0x30,
+ 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05,
+ 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x05, 0x05, 0x00, 0x03, 0x41, 0x00, 0x85,
+ 0x36, 0x40, 0x73, 0xc1, 0xbb, 0x1a, 0xda, 0xd4,
+ 0x59, 0x9f, 0x2d, 0xa2, 0x70, 0x31, 0x46, 0x74,
+ 0xec, 0x83, 0x6e, 0xa8, 0xc8, 0x3c, 0x51, 0xaf,
+ 0x39, 0xac, 0xec, 0x40, 0xbc, 0xe8, 0x22, 0x46,
+ 0x1d, 0x99, 0xd6, 0x46, 0x2a, 0x24, 0xd4, 0x8b,
+ 0x05, 0x08, 0x4b, 0xfb, 0x35, 0x11, 0x6e, 0x92,
+ 0xbb, 0x77, 0xba, 0xe4, 0x12, 0xbb, 0xf4, 0xc8,
+ 0x5e, 0x9c, 0x81, 0xa8, 0x97, 0x60, 0x4c, 0x16,
+ 0x03, 0x03, 0x00, 0x8d, 0x0c, 0x00, 0x00, 0x89,
+ 0x03, 0x00, 0x17, 0x41, 0x04, 0x48, 0x93, 0x62,
+ 0x6a, 0xf8, 0x7c, 0x94, 0xcc, 0xcc, 0x0a, 0x9b,
+ 0x5e, 0x11, 0xad, 0x0b, 0x30, 0xc4, 0x5d, 0xf7,
+ 0x63, 0x24, 0xc1, 0xb0, 0x40, 0x5f, 0xff, 0x9f,
+ 0x0d, 0x7e, 0xd5, 0xa5, 0xd0, 0x4f, 0x80, 0x16,
+ 0xa8, 0x66, 0x18, 0x31, 0x1f, 0x81, 0xb2, 0x9a,
+ 0x41, 0x62, 0x5b, 0xcf, 0x73, 0xac, 0x4a, 0x64,
+ 0xb5, 0xc1, 0x46, 0x4d, 0x8a, 0xac, 0x25, 0xba,
+ 0x81, 0x7f, 0xbe, 0x64, 0x68, 0x04, 0x01, 0x00,
+ 0x40, 0x4e, 0x3f, 0x1e, 0x04, 0x4c, 0xef, 0xd2,
+ 0xa6, 0x82, 0xe6, 0x7c, 0x76, 0x23, 0x17, 0xb9,
+ 0xe7, 0x52, 0x15, 0x6b, 0x3d, 0xb2, 0xb1, 0x17,
+ 0x7d, 0xe6, 0xde, 0x06, 0x87, 0x30, 0xb0, 0xb5,
+ 0x57, 0xae, 0xdf, 0xb2, 0xdc, 0x8d, 0xab, 0x76,
+ 0x9c, 0xaa, 0x45, 0x6d, 0x23, 0x5d, 0xc1, 0xa8,
+ 0x7b, 0x79, 0x79, 0xb1, 0x3c, 0xdc, 0xf5, 0x33,
+ 0x2c, 0xa1, 0x62, 0x3e, 0xbd, 0xf5, 0x5d, 0x6c,
+ 0x87, 0x16, 0x03, 0x03, 0x00, 0x04, 0x0e, 0x00,
+ 0x00, 0x00,
+ },
+ {
+ 0x16, 0x03, 0x03, 0x00, 0x46, 0x10, 0x00, 0x00,
+ 0x42, 0x41, 0x04, 0x1e, 0x18, 0x37, 0xef, 0x0d,
+ 0x19, 0x51, 0x88, 0x35, 0x75, 0x71, 0xb5, 0xe5,
+ 0x54, 0x5b, 0x12, 0x2e, 0x8f, 0x09, 0x67, 0xfd,
+ 0xa7, 0x24, 0x20, 0x3e, 0xb2, 0x56, 0x1c, 0xce,
+ 0x97, 0x28, 0x5e, 0xf8, 0x2b, 0x2d, 0x4f, 0x9e,
+ 0xf1, 0x07, 0x9f, 0x6c, 0x4b, 0x5b, 0x83, 0x56,
+ 0xe2, 0x32, 0x42, 0xe9, 0x58, 0xb6, 0xd7, 0x49,
+ 0xa6, 0xb5, 0x68, 0x1a, 0x41, 0x03, 0x56, 0x6b,
+ 0xdc, 0x5a, 0x89, 0x14, 0x03, 0x03, 0x00, 0x01,
+ 0x01, 0x16, 0x03, 0x03, 0x00, 0x40, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xee, 0x17,
+ 0x54, 0x51, 0xb6, 0x1d, 0x8e, 0xe4, 0x6b, 0xed,
+ 0x5b, 0xa1, 0x27, 0x7f, 0xdc, 0xa9, 0xa5, 0xcf,
+ 0x38, 0xe6, 0x5d, 0x17, 0x34, 0xf9, 0xc0, 0x07,
+ 0xb8, 0xbe, 0x56, 0xe6, 0xd6, 0x6a, 0xb6, 0x26,
+ 0x4e, 0x45, 0x8d, 0x48, 0xe9, 0xc6, 0xb1, 0xa1,
+ 0xea, 0xdc, 0xb1, 0x37, 0xd9, 0xf6,
+ },
+ {
+ 0x14, 0x03, 0x03, 0x00, 0x01, 0x01, 0x16, 0x03,
+ 0x03, 0x00, 0x40, 0x00, 0x68, 0xc5, 0x27, 0xd5,
+ 0x3d, 0xba, 0x04, 0xde, 0x63, 0xf1, 0x5b, 0xc3,
+ 0x86, 0xb9, 0x82, 0xc7, 0xb3, 0x90, 0x31, 0xea,
+ 0x15, 0xe1, 0x42, 0x76, 0x7d, 0x90, 0xcb, 0xc9,
+ 0xd1, 0x05, 0xe6, 0x8c, 0x76, 0xc7, 0x9a, 0x35,
+ 0x67, 0xa2, 0x70, 0x9a, 0x8a, 0x6c, 0xb5, 0x6b,
+ 0xc7, 0x87, 0xf3, 0x65, 0x0a, 0xa0, 0x98, 0xba,
+ 0x57, 0xbb, 0x31, 0x7b, 0x1f, 0x1a, 0xf7, 0x2a,
+ 0xf3, 0x12, 0xf6,
+ },
+ {
+ 0x17, 0x03, 0x03, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x10, 0x80,
+ 0x54, 0x1e, 0x72, 0xd3, 0x1a, 0x86, 0x1c, 0xc4,
+ 0x4a, 0x9b, 0xd4, 0x80, 0xd2, 0x03, 0x35, 0x0d,
+ 0xe4, 0x12, 0xc2, 0x3d, 0x79, 0x4a, 0x2c, 0xba,
+ 0xc2, 0xad, 0xf3, 0xd2, 0x16, 0x15, 0x03, 0x03,
+ 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x04, 0x9b, 0x68, 0x78, 0x92, 0x28,
+ 0x62, 0x02, 0x65, 0x87, 0x90, 0xe4, 0x32, 0xd7,
+ 0x72, 0x08, 0x70, 0xb8, 0x52, 0x32, 0x1f, 0x97,
+ 0xd4, 0x6a, 0xc6, 0x28, 0x83, 0xb0, 0x1d, 0x6e,
+ 0x16, 0xd5,
+ },
+}
+
+// $ openssl s_server -tls1_2 -cert server.crt -key server.key \
+// -port 10443 -verify 0
+// $ go test -test.run "TestRunClient" -connect -ciphersuites=0xc02f \
+// -maxversion=0x0303
+var clientTLS12ClientCertScript = [][]byte{
+ {
+ 0x16, 0x03, 0x01, 0x00, 0x58, 0x01, 0x00, 0x00,
+ 0x54, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xc0, 0x2f,
+ 0x01, 0x00, 0x00, 0x29, 0x00, 0x05, 0x00, 0x05,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00,
+ 0x08, 0x00, 0x06, 0x00, 0x17, 0x00, 0x18, 0x00,
+ 0x19, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00,
+ 0x0d, 0x00, 0x0a, 0x00, 0x08, 0x04, 0x01, 0x04,
+ 0x03, 0x02, 0x01, 0x02, 0x03,
+ },
+ {
+ 0x16, 0x03, 0x03, 0x00, 0x54, 0x02, 0x00, 0x00,
+ 0x50, 0x03, 0x03, 0x52, 0x65, 0x67, 0xe0, 0xe8,
+ 0xf1, 0x13, 0x2a, 0x83, 0x28, 0xa8, 0x2e, 0x76,
+ 0x69, 0xe6, 0x89, 0x55, 0x6c, 0x48, 0x49, 0x2e,
+ 0x00, 0xf6, 0x87, 0x6c, 0x13, 0xa1, 0xd4, 0xaa,
+ 0xd0, 0x76, 0x3b, 0x20, 0xe4, 0xd6, 0x5b, 0x1d,
+ 0x11, 0xf2, 0x42, 0xf2, 0x82, 0x0c, 0x0d, 0x66,
+ 0x6d, 0xec, 0x52, 0xf8, 0x4a, 0xd9, 0x45, 0xcf,
+ 0xe4, 0x4a, 0xba, 0x8b, 0xf1, 0xab, 0x55, 0xe4,
+ 0x57, 0x18, 0xa9, 0x36, 0xc0, 0x2f, 0x00, 0x00,
+ 0x08, 0x00, 0x0b, 0x00, 0x04, 0x03, 0x00, 0x01,
+ 0x02, 0x16, 0x03, 0x03, 0x02, 0x39, 0x0b, 0x00,
+ 0x02, 0x35, 0x00, 0x02, 0x32, 0x00, 0x02, 0x2f,
+ 0x30, 0x82, 0x02, 0x2b, 0x30, 0x82, 0x01, 0xd5,
+ 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x09, 0x00,
+ 0xb1, 0x35, 0x13, 0x65, 0x11, 0x20, 0xc5, 0x92,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30,
+ 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
+ 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13,
+ 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
+ 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74,
+ 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06,
+ 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e,
+ 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57,
+ 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50,
+ 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e,
+ 0x17, 0x0d, 0x31, 0x32, 0x30, 0x34, 0x30, 0x36,
+ 0x31, 0x37, 0x31, 0x30, 0x31, 0x33, 0x5a, 0x17,
+ 0x0d, 0x31, 0x35, 0x30, 0x34, 0x30, 0x36, 0x31,
+ 0x37, 0x31, 0x30, 0x31, 0x33, 0x5a, 0x30, 0x45,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
+ 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30,
+ 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a,
+ 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61,
+ 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03,
+ 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74,
+ 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69,
+ 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74,
+ 0x79, 0x20, 0x4c, 0x74, 0x64, 0x30, 0x5c, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x4b,
+ 0x00, 0x30, 0x48, 0x02, 0x41, 0x00, 0x9f, 0xb3,
+ 0xc3, 0x84, 0x27, 0x95, 0xff, 0x12, 0x31, 0x52,
+ 0x0f, 0x15, 0xef, 0x46, 0x11, 0xc4, 0xad, 0x80,
+ 0xe6, 0x36, 0x5b, 0x0f, 0xdd, 0x80, 0xd7, 0x61,
+ 0x8d, 0xe0, 0xfc, 0x72, 0x45, 0x09, 0x34, 0xfe,
+ 0x55, 0x66, 0x45, 0x43, 0x4c, 0x68, 0x97, 0x6a,
+ 0xfe, 0xa8, 0xa0, 0xa5, 0xdf, 0x5f, 0x78, 0xff,
+ 0xee, 0xd7, 0x64, 0xb8, 0x3f, 0x04, 0xcb, 0x6f,
+ 0xff, 0x2a, 0xfe, 0xfe, 0xb9, 0xed, 0x02, 0x03,
+ 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, 0x30, 0x81,
+ 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e,
+ 0x04, 0x16, 0x04, 0x14, 0x78, 0xa6, 0x97, 0x9a,
+ 0x63, 0xb5, 0xc5, 0xa1, 0xa5, 0x33, 0xba, 0x22,
+ 0x7c, 0x23, 0x6e, 0x5b, 0x1b, 0x7a, 0xcc, 0x2b,
+ 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
+ 0x6e, 0x30, 0x6c, 0x80, 0x14, 0x78, 0xa6, 0x97,
+ 0x9a, 0x63, 0xb5, 0xc5, 0xa1, 0xa5, 0x33, 0xba,
+ 0x22, 0x7c, 0x23, 0x6e, 0x5b, 0x1b, 0x7a, 0xcc,
+ 0x2b, 0xa1, 0x49, 0xa4, 0x47, 0x30, 0x45, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11,
+ 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53,
+ 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74,
+ 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55,
+ 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65,
+ 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64,
+ 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79,
+ 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, 0x00, 0xb1,
+ 0x35, 0x13, 0x65, 0x11, 0x20, 0xc5, 0x92, 0x30,
+ 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05,
+ 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x05, 0x05, 0x00, 0x03, 0x41, 0x00, 0x85,
+ 0x36, 0x40, 0x73, 0xc1, 0xbb, 0x1a, 0xda, 0xd4,
+ 0x59, 0x9f, 0x2d, 0xa2, 0x70, 0x31, 0x46, 0x74,
+ 0xec, 0x83, 0x6e, 0xa8, 0xc8, 0x3c, 0x51, 0xaf,
+ 0x39, 0xac, 0xec, 0x40, 0xbc, 0xe8, 0x22, 0x46,
+ 0x1d, 0x99, 0xd6, 0x46, 0x2a, 0x24, 0xd4, 0x8b,
+ 0x05, 0x08, 0x4b, 0xfb, 0x35, 0x11, 0x6e, 0x92,
+ 0xbb, 0x77, 0xba, 0xe4, 0x12, 0xbb, 0xf4, 0xc8,
+ 0x5e, 0x9c, 0x81, 0xa8, 0x97, 0x60, 0x4c, 0x16,
+ 0x03, 0x03, 0x00, 0x8d, 0x0c, 0x00, 0x00, 0x89,
+ 0x03, 0x00, 0x17, 0x41, 0x04, 0xaa, 0xf0, 0x0c,
+ 0xa3, 0x60, 0xcf, 0x69, 0x1e, 0xad, 0x16, 0x9a,
+ 0x01, 0x40, 0xc6, 0x22, 0xc4, 0xbb, 0x06, 0x3b,
+ 0x84, 0x65, 0xea, 0xc7, 0xa2, 0x96, 0x79, 0x17,
+ 0x2f, 0xc7, 0xbe, 0x56, 0x39, 0xe4, 0x79, 0xf3,
+ 0xad, 0x17, 0xf3, 0x7e, 0xe2, 0x7b, 0xa2, 0x6f,
+ 0x3f, 0x96, 0xea, 0xe5, 0x0e, 0xea, 0x39, 0x79,
+ 0x77, 0xeb, 0x14, 0x18, 0xbb, 0x7c, 0x95, 0xda,
+ 0xa7, 0x51, 0x09, 0xba, 0xd7, 0x04, 0x01, 0x00,
+ 0x40, 0x82, 0x3e, 0xce, 0xee, 0x7e, 0xba, 0x3b,
+ 0x51, 0xb1, 0xba, 0x71, 0x2e, 0x54, 0xa9, 0xb9,
+ 0xe2, 0xb1, 0x59, 0x17, 0xa1, 0xac, 0x76, 0xb4,
+ 0x4e, 0xf1, 0xae, 0x65, 0x17, 0x2b, 0x43, 0x06,
+ 0x31, 0x29, 0x0b, 0xa0, 0x1e, 0xb6, 0xfa, 0x35,
+ 0xe8, 0x63, 0x06, 0xde, 0x13, 0x89, 0x83, 0x69,
+ 0x3b, 0xc2, 0x15, 0x73, 0x1c, 0xc5, 0x07, 0xe9,
+ 0x38, 0x9b, 0x06, 0x81, 0x1b, 0x97, 0x7c, 0xa6,
+ 0x89, 0x16, 0x03, 0x03, 0x00, 0x30, 0x0d, 0x00,
+ 0x00, 0x28, 0x03, 0x01, 0x02, 0x40, 0x00, 0x20,
+ 0x06, 0x01, 0x06, 0x02, 0x06, 0x03, 0x05, 0x01,
+ 0x05, 0x02, 0x05, 0x03, 0x04, 0x01, 0x04, 0x02,
+ 0x04, 0x03, 0x03, 0x01, 0x03, 0x02, 0x03, 0x03,
+ 0x02, 0x01, 0x02, 0x02, 0x02, 0x03, 0x01, 0x01,
+ 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x16, 0x03, 0x03, 0x0a, 0xfb, 0x0b, 0x00, 0x0a,
+ 0xf7, 0x00, 0x0a, 0xf4, 0x00, 0x03, 0x7e, 0x30,
+ 0x82, 0x03, 0x7a, 0x30, 0x82, 0x02, 0x62, 0x02,
+ 0x09, 0x00, 0xb4, 0x47, 0x58, 0x57, 0x2b, 0x67,
+ 0xc8, 0xc2, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05,
+ 0x00, 0x30, 0x81, 0x80, 0x31, 0x0b, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
+ 0x04, 0x08, 0x0c, 0x02, 0x4e, 0x59, 0x31, 0x11,
+ 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c,
+ 0x08, 0x42, 0x72, 0x6f, 0x6f, 0x6b, 0x6c, 0x79,
+ 0x6e, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55,
+ 0x04, 0x0a, 0x0c, 0x0c, 0x4d, 0x79, 0x20, 0x43,
+ 0x41, 0x20, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74,
+ 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x0c, 0x0e, 0x6d, 0x79, 0x63, 0x61, 0x63,
+ 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01,
+ 0x16, 0x12, 0x6a, 0x76, 0x73, 0x68, 0x61, 0x68,
+ 0x69, 0x64, 0x40, 0x67, 0x6d, 0x61, 0x69, 0x6c,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d,
+ 0x31, 0x33, 0x30, 0x35, 0x32, 0x36, 0x32, 0x31,
+ 0x34, 0x34, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x31,
+ 0x33, 0x30, 0x36, 0x32, 0x35, 0x32, 0x31, 0x34,
+ 0x34, 0x30, 0x30, 0x5a, 0x30, 0x7d, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x55, 0x53, 0x31, 0x11, 0x30, 0x0f, 0x06,
+ 0x03, 0x55, 0x04, 0x08, 0x0c, 0x08, 0x4e, 0x65,
+ 0x77, 0x20, 0x59, 0x6f, 0x72, 0x6b, 0x31, 0x11,
+ 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c,
+ 0x08, 0x42, 0x72, 0x6f, 0x6f, 0x6b, 0x6c, 0x79,
+ 0x6e, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55,
+ 0x04, 0x0a, 0x0c, 0x07, 0x4d, 0x79, 0x20, 0x4c,
+ 0x65, 0x61, 0x66, 0x31, 0x13, 0x30, 0x11, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x0c, 0x0a, 0x6d, 0x79,
+ 0x6c, 0x65, 0x61, 0x66, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x31, 0x21, 0x30, 0x1f, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16,
+ 0x12, 0x6a, 0x76, 0x73, 0x68, 0x61, 0x68, 0x69,
+ 0x64, 0x40, 0x67, 0x6d, 0x61, 0x69, 0x6c, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x30, 0x82, 0x01, 0x22, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82,
+ 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02,
+ 0x82, 0x01, 0x01, 0x00, 0xa0, 0xa3, 0xef, 0xc1,
+ 0x44, 0x7d, 0xa2, 0xe3, 0x71, 0x98, 0x27, 0x63,
+ 0xb3, 0x1d, 0x71, 0x50, 0xa6, 0x34, 0x15, 0xcb,
+ 0xc9, 0x2a, 0xc3, 0xea, 0xe4, 0x9e, 0x9c, 0x49,
+ 0xa6, 0x01, 0x9b, 0x7e, 0xa9, 0xb5, 0x7a, 0xff,
+ 0x15, 0x92, 0x71, 0xc8, 0x97, 0x9c, 0x25, 0xb7,
+ 0x79, 0x2b, 0xff, 0xab, 0xc6, 0xb1, 0xa7, 0x00,
+ 0x90, 0xb2, 0x8b, 0xd7, 0x71, 0xd5, 0xc2, 0x3a,
+ 0xe6, 0x82, 0x42, 0x37, 0x89, 0x41, 0x04, 0xb0,
+ 0xba, 0xc7, 0x5b, 0x8a, 0x43, 0x9f, 0x97, 0x39,
+ 0x0c, 0x0f, 0xd5, 0x6d, 0x9e, 0x8d, 0xeb, 0xc0,
+ 0x26, 0xc5, 0x18, 0xe8, 0x7a, 0x3d, 0x32, 0x2e,
+ 0x38, 0x90, 0x40, 0x5b, 0x39, 0x2c, 0x07, 0xcb,
+ 0x24, 0x10, 0xc5, 0xc9, 0x3b, 0xe3, 0x66, 0x47,
+ 0x57, 0xb9, 0x6a, 0xad, 0x44, 0xf8, 0xd0, 0x70,
+ 0x62, 0x3b, 0x8e, 0xed, 0x60, 0x5f, 0x22, 0xf8,
+ 0xb8, 0x0c, 0xc9, 0x41, 0x2b, 0xc9, 0x80, 0x6e,
+ 0x4e, 0x1b, 0xe1, 0x20, 0xfc, 0x47, 0xa4, 0xac,
+ 0xc3, 0x3f, 0xe6, 0xc2, 0x81, 0x79, 0x03, 0x37,
+ 0x25, 0x89, 0xca, 0xd6, 0xa5, 0x46, 0x91, 0x63,
+ 0x41, 0xc5, 0x3e, 0xd5, 0xed, 0x7f, 0x4f, 0x8d,
+ 0x06, 0xc0, 0x89, 0x00, 0xbe, 0x37, 0x7b, 0x7e,
+ 0x73, 0xca, 0x70, 0x00, 0x14, 0x34, 0xbe, 0x47,
+ 0xbc, 0xb2, 0x6a, 0x28, 0xa5, 0x29, 0x84, 0xa8,
+ 0x9d, 0xc8, 0x1e, 0x77, 0x66, 0x1f, 0x9f, 0xaa,
+ 0x2b, 0x47, 0xdb, 0xdd, 0x6b, 0x9c, 0xa8, 0xfc,
+ 0x82, 0x36, 0x94, 0x62, 0x0d, 0x5c, 0x3f, 0xb2,
+ 0x01, 0xb4, 0xa5, 0xb8, 0xc6, 0x0e, 0x94, 0x5b,
+ 0xec, 0x5e, 0xbb, 0x7a, 0x63, 0x24, 0xf1, 0xf9,
+ 0xd6, 0x50, 0x08, 0xc1, 0xa3, 0xcc, 0x90, 0x07,
+ 0x5b, 0x04, 0x04, 0x42, 0x74, 0xcf, 0x37, 0xfa,
+ 0xf0, 0xa5, 0xd9, 0xd3, 0x86, 0x89, 0x89, 0x18,
+ 0xf3, 0x4c, 0xe2, 0x11, 0x02, 0x03, 0x01, 0x00,
+ 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x01, 0x00, 0x90, 0xbb, 0xf9,
+ 0x5e, 0xba, 0x17, 0x1f, 0xac, 0x21, 0x9f, 0x6b,
+ 0x4a, 0x46, 0xd0, 0x6d, 0x3c, 0x8f, 0x3d, 0xf8,
+ 0x5e, 0x3e, 0x72, 0xaf, 0xa0, 0x1a, 0xf3, 0xff,
+ 0x89, 0xac, 0x5b, 0x7a, 0xe2, 0x91, 0x2a, 0x23,
+ 0x85, 0xc6, 0x4d, 0x47, 0x67, 0x01, 0x08, 0xa8,
+ 0x05, 0x1d, 0x01, 0x60, 0x50, 0x5f, 0x59, 0xad,
+ 0xfe, 0x7b, 0xc6, 0x0c, 0x54, 0x90, 0x68, 0x70,
+ 0x67, 0x2e, 0xed, 0x87, 0xf8, 0x69, 0x8a, 0xac,
+ 0x32, 0xfe, 0x6f, 0x90, 0x19, 0x2a, 0x64, 0x8d,
+ 0x82, 0x66, 0x05, 0x43, 0x88, 0xee, 0xf2, 0x30,
+ 0xed, 0xa4, 0x8f, 0xbf, 0xd6, 0x57, 0x20, 0xd4,
+ 0x43, 0x1d, 0x52, 0x96, 0x6f, 0xae, 0x09, 0x96,
+ 0x01, 0x52, 0x38, 0xe3, 0xaf, 0x99, 0xd7, 0xdc,
+ 0x14, 0x99, 0xc4, 0x8b, 0x0e, 0x04, 0x0f, 0xb3,
+ 0x14, 0x14, 0xd4, 0xa5, 0x93, 0xe1, 0xc9, 0x8a,
+ 0x81, 0xef, 0x63, 0xfc, 0x36, 0x77, 0x05, 0x06,
+ 0xf0, 0x2a, 0x04, 0x0a, 0xbe, 0x2e, 0xce, 0x81,
+ 0x3d, 0x23, 0xa1, 0xda, 0xd8, 0xeb, 0xc6, 0xea,
+ 0x5e, 0xcf, 0x28, 0x36, 0x51, 0x31, 0x95, 0x5e,
+ 0x40, 0x04, 0xed, 0xac, 0xc1, 0xc8, 0x56, 0x69,
+ 0x87, 0xec, 0x3b, 0x03, 0x3e, 0x9d, 0x0f, 0x4c,
+ 0x4c, 0xeb, 0xd7, 0xba, 0x26, 0xdf, 0xe3, 0xde,
+ 0x10, 0xee, 0x93, 0x62, 0x8d, 0x73, 0x52, 0x6e,
+ 0xff, 0x37, 0x36, 0x98, 0x7b, 0x2d, 0x56, 0x4c,
+ 0xba, 0x09, 0xb8, 0xa7, 0xf0, 0x3b, 0x16, 0x81,
+ 0xca, 0xdb, 0x43, 0xab, 0xec, 0x4c, 0x6e, 0x7c,
+ 0xc1, 0x0b, 0x22, 0x22, 0x43, 0x1d, 0xb6, 0x0c,
+ 0xc1, 0xb9, 0xcf, 0xe4, 0x53, 0xee, 0x1d, 0x3e,
+ 0x88, 0xa7, 0x13, 0xbe, 0x7f, 0xbd, 0xae, 0x72,
+ 0xcf, 0xcd, 0x63, 0xd2, 0xc3, 0x18, 0x58, 0x92,
+ 0xa2, 0xad, 0xb5, 0x09, 0x9d, 0x91, 0x03, 0xdd,
+ 0x3c, 0xe2, 0x1c, 0xde, 0x78, 0x00, 0x03, 0x88,
+ 0x30, 0x82, 0x03, 0x84, 0x30, 0x82, 0x02, 0x6c,
+ 0x02, 0x09, 0x00, 0xab, 0xed, 0xa6, 0xe4, 0x4a,
+ 0x2b, 0x2b, 0xf8, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05,
+ 0x05, 0x00, 0x30, 0x81, 0x86, 0x31, 0x0b, 0x30,
+ 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
+ 0x55, 0x04, 0x08, 0x0c, 0x02, 0x4e, 0x59, 0x31,
+ 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07,
+ 0x0c, 0x08, 0x42, 0x72, 0x6f, 0x6f, 0x6b, 0x6c,
+ 0x79, 0x6e, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03,
+ 0x55, 0x04, 0x0a, 0x0c, 0x18, 0x4d, 0x79, 0x20,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
+ 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68,
+ 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x11, 0x30,
+ 0x0f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x08,
+ 0x6d, 0x79, 0x63, 0x61, 0x2e, 0x6f, 0x72, 0x67,
+ 0x31, 0x21, 0x30, 0x1f, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16,
+ 0x12, 0x6a, 0x76, 0x73, 0x68, 0x61, 0x68, 0x69,
+ 0x64, 0x40, 0x67, 0x6d, 0x61, 0x69, 0x6c, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, 0x31,
+ 0x33, 0x30, 0x35, 0x32, 0x36, 0x32, 0x31, 0x31,
+ 0x38, 0x34, 0x30, 0x5a, 0x17, 0x0d, 0x31, 0x33,
+ 0x30, 0x36, 0x32, 0x35, 0x32, 0x31, 0x31, 0x38,
+ 0x34, 0x30, 0x5a, 0x30, 0x81, 0x80, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x04, 0x08, 0x0c, 0x02, 0x4e, 0x59,
+ 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04,
+ 0x07, 0x0c, 0x08, 0x42, 0x72, 0x6f, 0x6f, 0x6b,
+ 0x6c, 0x79, 0x6e, 0x31, 0x15, 0x30, 0x13, 0x06,
+ 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0c, 0x4d, 0x79,
+ 0x20, 0x43, 0x41, 0x20, 0x43, 0x6c, 0x69, 0x65,
+ 0x6e, 0x74, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x0c, 0x0e, 0x6d, 0x79, 0x63,
+ 0x61, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x31, 0x21, 0x30, 0x1f, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x09, 0x01, 0x16, 0x12, 0x6a, 0x76, 0x73, 0x68,
+ 0x61, 0x68, 0x69, 0x64, 0x40, 0x67, 0x6d, 0x61,
+ 0x69, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x82,
+ 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82,
+ 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xce,
+ 0x13, 0xf0, 0x72, 0xb0, 0x61, 0xc8, 0x18, 0x37,
+ 0x8a, 0x41, 0x3d, 0x20, 0xa1, 0x1c, 0xcb, 0xbf,
+ 0xf6, 0x3b, 0x74, 0x26, 0x2a, 0x96, 0x11, 0xec,
+ 0x53, 0xa1, 0xcc, 0x7d, 0x77, 0x56, 0x45, 0x0f,
+ 0x36, 0xb7, 0xf2, 0x48, 0x92, 0x1a, 0x62, 0xcc,
+ 0xb6, 0xc0, 0xa1, 0x2f, 0x44, 0x2b, 0xc1, 0x89,
+ 0xcb, 0x6e, 0x1e, 0xdb, 0x57, 0x92, 0xd5, 0x97,
+ 0x60, 0x8c, 0x41, 0x2c, 0xd9, 0x20, 0xfe, 0xe9,
+ 0x1f, 0x8e, 0xfc, 0x7f, 0x02, 0x44, 0x0f, 0x28,
+ 0x81, 0xd6, 0x0c, 0xcd, 0xbc, 0xf0, 0x57, 0x6c,
+ 0xcc, 0xa7, 0xba, 0x06, 0xa0, 0xa6, 0x91, 0xda,
+ 0xef, 0x46, 0x8a, 0x60, 0x0f, 0x52, 0x6c, 0x90,
+ 0x6c, 0x8c, 0x44, 0xaf, 0xb0, 0x9d, 0x90, 0xba,
+ 0x21, 0x58, 0xa0, 0x3c, 0xee, 0x54, 0xb5, 0x29,
+ 0x26, 0x1f, 0x0a, 0xac, 0xef, 0x48, 0x68, 0x33,
+ 0xd0, 0x33, 0xd0, 0x8b, 0x1a, 0xec, 0x6e, 0x2f,
+ 0xb5, 0x4a, 0x53, 0xc2, 0x1a, 0xd2, 0xf1, 0x50,
+ 0x05, 0x59, 0x5c, 0xd9, 0xda, 0x03, 0x0a, 0x47,
+ 0xb7, 0xdd, 0xf7, 0x3a, 0x69, 0xf5, 0x4e, 0xea,
+ 0x4a, 0xc2, 0xca, 0x54, 0xb0, 0x8b, 0x76, 0xe1,
+ 0x02, 0x2d, 0x52, 0x67, 0xb9, 0xdd, 0x50, 0xc9,
+ 0x3b, 0x07, 0x24, 0x22, 0x6a, 0x00, 0x1d, 0x58,
+ 0x83, 0xa8, 0xec, 0x95, 0xf1, 0xda, 0xe2, 0x73,
+ 0xa0, 0xa1, 0x72, 0x60, 0x9e, 0x86, 0x53, 0xcb,
+ 0x45, 0xa8, 0xc2, 0xa0, 0x50, 0xa0, 0x53, 0xd6,
+ 0xfc, 0x18, 0x84, 0xb5, 0x4a, 0x26, 0xd0, 0xa2,
+ 0xaa, 0xd0, 0xff, 0xb6, 0xfe, 0x3a, 0x9c, 0xb5,
+ 0x19, 0x3b, 0x3f, 0xe1, 0x48, 0x0d, 0xa4, 0x09,
+ 0x4f, 0x83, 0xc9, 0xc0, 0xc9, 0xa6, 0x0b, 0x58,
+ 0x1f, 0x1c, 0x7b, 0xac, 0xa2, 0x42, 0xbc, 0x61,
+ 0xf4, 0x21, 0x8a, 0x00, 0xda, 0x14, 0xa0, 0x60,
+ 0x03, 0xfe, 0x93, 0x12, 0x6c, 0x56, 0xcd, 0x02,
+ 0x03, 0x01, 0x00, 0x01, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00,
+ 0x25, 0x29, 0x3b, 0x1e, 0xc3, 0x58, 0x32, 0xe6,
+ 0x23, 0xc8, 0xee, 0x18, 0xf0, 0x1d, 0x62, 0x6d,
+ 0x3b, 0x59, 0x99, 0x3a, 0xfe, 0x49, 0x72, 0x07,
+ 0x3f, 0x58, 0x93, 0xdb, 0xc0, 0xaf, 0xb0, 0xb3,
+ 0x5c, 0xd1, 0x5c, 0x98, 0xc8, 0xea, 0x4a, 0xe4,
+ 0x58, 0x73, 0x0d, 0x57, 0xc5, 0x13, 0x7c, 0x5c,
+ 0x79, 0x66, 0xda, 0x04, 0x1d, 0xe5, 0x98, 0xda,
+ 0x35, 0x47, 0x44, 0xb0, 0xd2, 0x7a, 0x66, 0x9d,
+ 0xcd, 0x41, 0xa5, 0x8f, 0xa1, 0x11, 0xb2, 0x1a,
+ 0x87, 0xc0, 0xcd, 0x55, 0xed, 0xb4, 0x7b, 0x33,
+ 0x72, 0xeb, 0xf7, 0xe3, 0x7b, 0x8b, 0x02, 0x86,
+ 0xe9, 0x2b, 0x26, 0x32, 0x9f, 0x99, 0xf1, 0xcb,
+ 0x93, 0xab, 0xb9, 0x16, 0xb3, 0x9a, 0xb2, 0x22,
+ 0x13, 0x21, 0x1f, 0x5b, 0xcc, 0xa2, 0x59, 0xbb,
+ 0x69, 0xf2, 0xb8, 0x07, 0x80, 0xce, 0x0c, 0xf7,
+ 0x98, 0x4c, 0x85, 0xc2, 0x96, 0x6a, 0x22, 0x05,
+ 0xe9, 0xbe, 0x48, 0xb0, 0x02, 0x5b, 0x69, 0x28,
+ 0x18, 0x88, 0x96, 0xe3, 0xd7, 0xc6, 0x7a, 0xd3,
+ 0xe9, 0x99, 0xff, 0x9d, 0xc3, 0x61, 0x4d, 0x9a,
+ 0x96, 0xf2, 0xc6, 0x33, 0x4d, 0xe5, 0x5d, 0x5a,
+ 0x68, 0x64, 0x5a, 0x82, 0x35, 0x65, 0x25, 0xe3,
+ 0x8c, 0x5b, 0xb0, 0xf6, 0x96, 0x56, 0xbc, 0xbf,
+ 0x97, 0x76, 0x4b, 0x66, 0x44, 0x81, 0xa4, 0xc4,
+ 0xa7, 0x31, 0xc5, 0xa1, 0x4f, 0xe8, 0xa4, 0xca,
+ 0x20, 0xf5, 0x01, 0x5b, 0x99, 0x4f, 0x5a, 0xf4,
+ 0xf0, 0x78, 0xbf, 0x71, 0x49, 0xd5, 0xf1, 0xc1,
+ 0xa2, 0x18, 0xfd, 0x72, 0x5b, 0x16, 0xe8, 0x92,
+ 0xc7, 0x37, 0x48, 0xaf, 0xee, 0x24, 0xfc, 0x35,
+ 0x0b, 0xc2, 0xdd, 0x05, 0xc7, 0x6e, 0xa3, 0x29,
+ 0xbb, 0x29, 0x7d, 0xd3, 0x2b, 0x94, 0x80, 0xc3,
+ 0x40, 0x53, 0x0e, 0x03, 0x54, 0x3d, 0x7b, 0x8b,
+ 0xce, 0xf9, 0xa4, 0x03, 0x27, 0x63, 0xec, 0x51,
+ 0x00, 0x03, 0xe5, 0x30, 0x82, 0x03, 0xe1, 0x30,
+ 0x82, 0x02, 0xc9, 0xa0, 0x03, 0x02, 0x01, 0x02,
+ 0x02, 0x09, 0x00, 0xcc, 0x22, 0x4c, 0x4b, 0x98,
+ 0xa2, 0x88, 0xfc, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05,
+ 0x05, 0x00, 0x30, 0x81, 0x86, 0x31, 0x0b, 0x30,
+ 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
+ 0x55, 0x04, 0x08, 0x0c, 0x02, 0x4e, 0x59, 0x31,
+ 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07,
+ 0x0c, 0x08, 0x42, 0x72, 0x6f, 0x6f, 0x6b, 0x6c,
+ 0x79, 0x6e, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03,
+ 0x55, 0x04, 0x0a, 0x0c, 0x18, 0x4d, 0x79, 0x20,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
+ 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68,
+ 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x11, 0x30,
+ 0x0f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x08,
+ 0x6d, 0x79, 0x63, 0x61, 0x2e, 0x6f, 0x72, 0x67,
+ 0x31, 0x21, 0x30, 0x1f, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16,
+ 0x12, 0x6a, 0x76, 0x73, 0x68, 0x61, 0x68, 0x69,
+ 0x64, 0x40, 0x67, 0x6d, 0x61, 0x69, 0x6c, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, 0x31,
+ 0x33, 0x30, 0x35, 0x32, 0x36, 0x32, 0x31, 0x30,
+ 0x35, 0x30, 0x31, 0x5a, 0x17, 0x0d, 0x32, 0x33,
+ 0x30, 0x35, 0x32, 0x34, 0x32, 0x31, 0x30, 0x35,
+ 0x30, 0x31, 0x5a, 0x30, 0x81, 0x86, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x04, 0x08, 0x0c, 0x02, 0x4e, 0x59,
+ 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04,
+ 0x07, 0x0c, 0x08, 0x42, 0x72, 0x6f, 0x6f, 0x6b,
+ 0x6c, 0x79, 0x6e, 0x31, 0x21, 0x30, 0x1f, 0x06,
+ 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x18, 0x4d, 0x79,
+ 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
+ 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74,
+ 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x11,
+ 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c,
+ 0x08, 0x6d, 0x79, 0x63, 0x61, 0x2e, 0x6f, 0x72,
+ 0x67, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01,
+ 0x16, 0x12, 0x6a, 0x76, 0x73, 0x68, 0x61, 0x68,
+ 0x69, 0x64, 0x40, 0x67, 0x6d, 0x61, 0x69, 0x6c,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x82, 0x01, 0x22,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
+ 0x02, 0x82, 0x01, 0x01, 0x00, 0xf0, 0xfb, 0xad,
+ 0x80, 0x5e, 0x37, 0xd3, 0x6d, 0xee, 0x2e, 0xcc,
+ 0xbc, 0x0c, 0xd7, 0x56, 0x4b, 0x56, 0x45, 0xcd,
+ 0x28, 0xb6, 0x22, 0xe9, 0xe2, 0x0f, 0xd1, 0x87,
+ 0x2a, 0x27, 0xce, 0x77, 0x8d, 0x6e, 0x0e, 0x0f,
+ 0xfb, 0x66, 0xe1, 0xb5, 0x0e, 0x9a, 0xb6, 0x05,
+ 0x8e, 0xb3, 0xe1, 0xc5, 0x77, 0x86, 0x5b, 0x46,
+ 0xd2, 0x0b, 0x92, 0x03, 0x1b, 0x89, 0x0c, 0x1b,
+ 0x10, 0x0e, 0x99, 0x8f, 0xe2, 0x17, 0xe8, 0xc2,
+ 0x30, 0x00, 0x47, 0xd6, 0xfc, 0xf9, 0x0f, 0x3b,
+ 0x75, 0x34, 0x8d, 0x4d, 0xb0, 0x99, 0xb7, 0xa0,
+ 0x6d, 0xa0, 0xb6, 0xad, 0xda, 0x07, 0x5e, 0x38,
+ 0x2e, 0x02, 0xe4, 0x30, 0x6d, 0xae, 0x13, 0x72,
+ 0xd4, 0xc8, 0xce, 0x14, 0x07, 0xae, 0x23, 0x8c,
+ 0x8f, 0x9e, 0x8c, 0x60, 0xd6, 0x06, 0xb9, 0xef,
+ 0x00, 0x18, 0xc0, 0x1d, 0x25, 0x1e, 0xda, 0x3e,
+ 0x2f, 0xcf, 0x2b, 0x56, 0x84, 0x9e, 0x30, 0x21,
+ 0xc7, 0x29, 0xf6, 0x03, 0x8a, 0x24, 0xf9, 0x34,
+ 0xac, 0x65, 0x9d, 0x80, 0x36, 0xc8, 0x3b, 0x15,
+ 0x10, 0xbd, 0x51, 0xe9, 0xbc, 0x02, 0xe1, 0xe9,
+ 0xb3, 0x5a, 0x9a, 0x99, 0x41, 0x1b, 0x27, 0xa0,
+ 0x4d, 0x50, 0x9e, 0x27, 0x7f, 0xa1, 0x7d, 0x09,
+ 0x87, 0xbd, 0x8a, 0xca, 0x5f, 0xb1, 0xa5, 0x08,
+ 0xb8, 0x04, 0xd4, 0x52, 0x89, 0xaa, 0xe0, 0x7d,
+ 0x42, 0x2e, 0x2f, 0x15, 0xee, 0x66, 0x57, 0x0f,
+ 0x13, 0x19, 0x45, 0xa8, 0x4b, 0x5d, 0x81, 0x66,
+ 0xcc, 0x12, 0x37, 0x94, 0x5e, 0xfd, 0x3c, 0x10,
+ 0x81, 0x51, 0x3f, 0xfa, 0x0f, 0xdd, 0xa1, 0x89,
+ 0x03, 0xa9, 0x78, 0x91, 0xf5, 0x3b, 0xf3, 0xbc,
+ 0xac, 0xbe, 0x93, 0x30, 0x2e, 0xbe, 0xca, 0x7f,
+ 0x46, 0xd3, 0x28, 0xb4, 0x4e, 0x91, 0x7b, 0x5b,
+ 0x43, 0x6c, 0xaf, 0x9b, 0x5c, 0x6a, 0x6d, 0x5a,
+ 0xdb, 0x79, 0x5e, 0x6a, 0x6b, 0x02, 0x03, 0x01,
+ 0x00, 0x01, 0xa3, 0x50, 0x30, 0x4e, 0x30, 0x1d,
+ 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04,
+ 0x14, 0x6b, 0x1e, 0x00, 0xa8, 0x9f, 0xfa, 0x7d,
+ 0x00, 0xf9, 0xe0, 0x9d, 0x0f, 0x90, 0x8c, 0x90,
+ 0xa8, 0xa1, 0x37, 0x6b, 0xda, 0x30, 0x1f, 0x06,
+ 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
+ 0x80, 0x14, 0x6b, 0x1e, 0x00, 0xa8, 0x9f, 0xfa,
+ 0x7d, 0x00, 0xf9, 0xe0, 0x9d, 0x0f, 0x90, 0x8c,
+ 0x90, 0xa8, 0xa1, 0x37, 0x6b, 0xda, 0x30, 0x0c,
+ 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, 0x30,
+ 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00,
+ 0xcd, 0x6f, 0x73, 0x4d, 0x56, 0x0b, 0xf3, 0x2e,
+ 0x1c, 0xe2, 0x02, 0x0c, 0x14, 0xbb, 0x2f, 0xdd,
+ 0x3c, 0x43, 0xfe, 0xdf, 0x94, 0x2d, 0xa9, 0x89,
+ 0x81, 0x51, 0xf8, 0x5f, 0xa7, 0xa0, 0x13, 0xaa,
+ 0xcc, 0xb0, 0x18, 0xe2, 0x57, 0x3e, 0x0d, 0x29,
+ 0x93, 0xe8, 0x95, 0xd5, 0x1b, 0x53, 0xd2, 0x51,
+ 0xf2, 0xbd, 0xf5, 0x9e, 0x7b, 0x22, 0x65, 0x62,
+ 0x5c, 0xc4, 0x4c, 0x1d, 0xe8, 0xe9, 0xc3, 0xd4,
+ 0x2b, 0xe7, 0x78, 0xcb, 0x10, 0xf3, 0xfe, 0x06,
+ 0x83, 0xdc, 0x3a, 0x1e, 0x62, 0x10, 0xc0, 0x46,
+ 0x77, 0xc6, 0x9d, 0x9f, 0xab, 0x96, 0x25, 0x5c,
+ 0xfb, 0x26, 0xc1, 0x15, 0x1f, 0xa5, 0x33, 0xee,
+ 0x4f, 0x9a, 0x14, 0x6a, 0x14, 0x97, 0x93, 0x2b,
+ 0x95, 0x0b, 0xdc, 0xa8, 0xd7, 0x69, 0x2e, 0xf0,
+ 0x01, 0x0e, 0xfd, 0x4e, 0xd0, 0xd9, 0xa8, 0xe5,
+ 0x65, 0xde, 0xfb, 0xca, 0xca, 0x1c, 0x5f, 0xf9,
+ 0x53, 0xa0, 0x87, 0xe7, 0x33, 0x9b, 0x2f, 0xcf,
+ 0xe4, 0x13, 0xfc, 0xec, 0x7a, 0x6c, 0xb0, 0x90,
+ 0x13, 0x9b, 0xb6, 0xc5, 0x03, 0xf6, 0x0e, 0x5e,
+ 0xe2, 0xe4, 0x26, 0xc1, 0x7e, 0x53, 0xfe, 0x69,
+ 0xa3, 0xc7, 0xd8, 0x8e, 0x6e, 0x94, 0x32, 0xa0,
+ 0xde, 0xca, 0xb6, 0xcc, 0xd6, 0x01, 0xd5, 0x78,
+ 0x40, 0x28, 0x63, 0x9b, 0xee, 0xcf, 0x09, 0x3b,
+ 0x35, 0x04, 0xf0, 0x14, 0x02, 0xf6, 0x80, 0x0e,
+ 0x90, 0xb2, 0x94, 0xd2, 0x25, 0x16, 0xb8, 0x7a,
+ 0x76, 0x87, 0x84, 0x9f, 0x84, 0xc5, 0xaf, 0xc2,
+ 0x6d, 0x68, 0x7a, 0x84, 0x9c, 0xc6, 0x8a, 0x63,
+ 0x60, 0x87, 0x6a, 0x25, 0xc1, 0xa1, 0x78, 0x0f,
+ 0xba, 0xe8, 0x5f, 0xe1, 0xba, 0xac, 0xa4, 0x6f,
+ 0xdd, 0x09, 0x3f, 0x12, 0xcb, 0x1d, 0xf3, 0xcf,
+ 0x48, 0xd7, 0xd3, 0x26, 0xe8, 0x9c, 0xc3, 0x53,
+ 0xb3, 0xba, 0xdc, 0x32, 0x99, 0x98, 0x96, 0xd6,
+ 0x16, 0x03, 0x03, 0x00, 0x46, 0x10, 0x00, 0x00,
+ 0x42, 0x41, 0x04, 0x1e, 0x18, 0x37, 0xef, 0x0d,
+ 0x19, 0x51, 0x88, 0x35, 0x75, 0x71, 0xb5, 0xe5,
+ 0x54, 0x5b, 0x12, 0x2e, 0x8f, 0x09, 0x67, 0xfd,
+ 0xa7, 0x24, 0x20, 0x3e, 0xb2, 0x56, 0x1c, 0xce,
+ 0x97, 0x28, 0x5e, 0xf8, 0x2b, 0x2d, 0x4f, 0x9e,
+ 0xf1, 0x07, 0x9f, 0x6c, 0x4b, 0x5b, 0x83, 0x56,
+ 0xe2, 0x32, 0x42, 0xe9, 0x58, 0xb6, 0xd7, 0x49,
+ 0xa6, 0xb5, 0x68, 0x1a, 0x41, 0x03, 0x56, 0x6b,
+ 0xdc, 0x5a, 0x89, 0x16, 0x03, 0x03, 0x01, 0x08,
+ 0x0f, 0x00, 0x01, 0x04, 0x04, 0x01, 0x01, 0x00,
+ 0x7e, 0xe4, 0x65, 0x02, 0x8e, 0xb3, 0x34, 0x6a,
+ 0x47, 0x71, 0xd1, 0xb0, 0x8d, 0x3c, 0x0c, 0xe1,
+ 0xde, 0x7e, 0x5f, 0xb4, 0x15, 0x2d, 0x32, 0x0a,
+ 0x2a, 0xdb, 0x9b, 0x40, 0xba, 0xce, 0x8b, 0xf5,
+ 0x74, 0xc1, 0x68, 0x20, 0x7c, 0x87, 0x23, 0x13,
+ 0xc3, 0x13, 0xa7, 0xdb, 0xec, 0x59, 0xa0, 0x40,
+ 0x9e, 0x64, 0x03, 0x60, 0xac, 0x76, 0xff, 0x01,
+ 0x34, 0x7b, 0x32, 0x26, 0xd9, 0x41, 0x31, 0x93,
+ 0xaa, 0x30, 0x51, 0x83, 0x85, 0x40, 0xeb, 0x4e,
+ 0x66, 0x39, 0x83, 0xb1, 0x30, 0x0d, 0x96, 0x01,
+ 0xee, 0x81, 0x53, 0x5e, 0xec, 0xa9, 0xc9, 0xdf,
+ 0x7e, 0xc1, 0x09, 0x47, 0x8b, 0x35, 0xdb, 0x10,
+ 0x15, 0xd4, 0xc7, 0x5a, 0x39, 0xe3, 0xc0, 0xf3,
+ 0x93, 0x38, 0x11, 0xdc, 0x71, 0xbb, 0xc7, 0x62,
+ 0x2b, 0x85, 0xad, 0x6b, 0x4f, 0x09, 0xb3, 0x31,
+ 0xa8, 0xe5, 0xd1, 0xb3, 0xa9, 0x21, 0x37, 0x50,
+ 0xc8, 0x7d, 0xc3, 0xd2, 0xf7, 0x00, 0xd3, 0xdb,
+ 0x0f, 0x82, 0xf2, 0x43, 0xcf, 0x36, 0x6c, 0x98,
+ 0x63, 0xd8, 0x1d, 0xb3, 0xf3, 0xde, 0x63, 0x79,
+ 0x64, 0xf0, 0xdb, 0x46, 0x04, 0xe1, 0x1c, 0x57,
+ 0x0f, 0x9e, 0x96, 0xb9, 0x93, 0x45, 0x71, 0x1c,
+ 0x8b, 0x65, 0x7d, 0x1e, 0xad, 0xbd, 0x03, 0x51,
+ 0xae, 0x44, 0xef, 0x97, 0x45, 0x0d, 0x8d, 0x41,
+ 0x5c, 0x80, 0x7b, 0xe6, 0xe0, 0xbc, 0xa6, 0x72,
+ 0x95, 0xa0, 0x97, 0xe1, 0xbb, 0xc0, 0xcc, 0xe5,
+ 0x1e, 0xc3, 0xbe, 0xd7, 0x42, 0x2a, 0xf3, 0x75,
+ 0x8a, 0x44, 0x67, 0x3c, 0xe5, 0x68, 0x78, 0xe5,
+ 0x40, 0x1f, 0xf0, 0x89, 0x57, 0xda, 0xee, 0x45,
+ 0xf4, 0x44, 0x81, 0x01, 0x77, 0xf0, 0x4a, 0x14,
+ 0xb1, 0x3f, 0x60, 0x2b, 0xeb, 0x42, 0x38, 0xa6,
+ 0xfb, 0xe5, 0x4d, 0x71, 0xdc, 0x7d, 0x0a, 0x72,
+ 0x56, 0x28, 0x9d, 0xa6, 0x8e, 0x74, 0x2d, 0xbd,
+ 0x14, 0x03, 0x03, 0x00, 0x01, 0x01, 0x16, 0x03,
+ 0x03, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x31, 0x4d, 0x58, 0x94, 0x0b,
+ 0x0b, 0x06, 0x5f, 0xae, 0x57, 0x17, 0x98, 0x86,
+ 0xaa, 0x49, 0x17, 0x7f, 0xbd, 0x41, 0x05, 0xa5,
+ 0x74, 0x1c, 0x58, 0xc8, 0x38, 0x2d, 0x99, 0x5d,
+ 0xe5, 0x12, 0x43,
+ },
+ {
+ 0x14, 0x03, 0x03, 0x00, 0x01, 0x01, 0x16, 0x03,
+ 0x03, 0x00, 0x28, 0xf2, 0x60, 0xc2, 0x75, 0x27,
+ 0x64, 0xf4, 0x05, 0x98, 0xc9, 0xd3, 0xa8, 0x00,
+ 0x4c, 0xa0, 0x49, 0x82, 0x68, 0xf1, 0x21, 0x05,
+ 0x7b, 0x4b, 0x25, 0x3e, 0xe1, 0x5f, 0x0f, 0x84,
+ 0x26, 0x2d, 0x16, 0x2e, 0xc0, 0xfd, 0xdf, 0x0a,
+ 0xf4, 0xba, 0x19,
+ },
+ {
+ 0x17, 0x03, 0x03, 0x00, 0x1e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x35, 0xef, 0x9d,
+ 0x6a, 0x86, 0x98, 0xc5, 0xca, 0x55, 0xca, 0x89,
+ 0x29, 0xb4, 0x55, 0xd4, 0x41, 0x08, 0x96, 0xe0,
+ 0xf3, 0x39, 0xfc, 0x15, 0x03, 0x03, 0x00, 0x1a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0x02, 0x63, 0x1b, 0xaa, 0xc6, 0xc9, 0x6d, 0x72,
+ 0x24, 0x10, 0x55, 0xa9, 0x8c, 0x3b, 0x23, 0xce,
+ 0xd8, 0x4a,
+ },
+}
+
+var testClientChainCertificate = fromHex(
+ "2d2d2d2d2d424547494e2050524956415445204b" +
+ "45592d2d2d2d2d0a4d494945766749424144414e" +
+ "42676b71686b6947397730424151454641415343" +
+ "424b67776767536b41674541416f494241514367" +
+ "6f2b2f4252483269343347590a4a324f7a485846" +
+ "51706a515679386b71772b726b6e70784a706747" +
+ "6266716d31657638566b6e48496c35776c74336b" +
+ "722f367647736163416b4c4b4c313348560a776a" +
+ "726d676b493369554545734c7248573470446e35" +
+ "633544412f56625a364e3638416d78526a6f656a" +
+ "30794c6a6951514673354c41664c4a4244467954" +
+ "766a0a5a6b64587557717452506a51634749376a" +
+ "75316758794c3475417a4a5153764a6747354f47" +
+ "2b45672f45656b724d4d2f35734b4265514d334a" +
+ "596e4b317156470a6b574e427854375637583950" +
+ "6a5162416951432b4e33742b6338707741425130" +
+ "766b6538736d6f6f70536d45714a3349486e646d" +
+ "48352b714b306662335775630a715079434e7052" +
+ "694456772f7367473070626a4744705262374636" +
+ "37656d4d6b38666e5755416a426f387951423173" +
+ "4542454a307a7a6636384b585a3034614a0a6952" +
+ "6a7a544f495241674d424141454367674542414a" +
+ "4b613676326b5a3144596146786e586d7369624c" +
+ "386734426f67514c6a42307362524a6d746b6b4d" +
+ "54370a685343325873537551522f446c654d7148" +
+ "664555786731784a717579597643544d44585972" +
+ "473667354a5051744d4432465a424a7239626c65" +
+ "467138386c706a0a543766514e793571354c2b4f" +
+ "682f6b62433835436e623641753641656978776d" +
+ "2b6e77665a4f3766726b6278306d35516b715975" +
+ "5739392f452b69502b454e570a76396a68773436" +
+ "76515065563236494b79717656462b4f7362722f" +
+ "6152316138707948336361566e3579594a433346" +
+ "5855756c6f5a77516331714a6b4c434c4c0a375a" +
+ "49744f525a78514c486d4d4a654d44722f5a4942" +
+ "34675467645650636145375a4d5141714d6d3066" +
+ "4c6b6d7671723149526b77642f6831455a645650" +
+ "79320a742f6b6b43413039566336663749556575" +
+ "6f67706d705a50303130564e376b6277394a6348" +
+ "75544561564543675945417a47395679426e6d62" +
+ "6858496c57764f0a71583747524f2f5231636a2b" +
+ "6b564e35377876674b54756b35592b7a4d774a48" +
+ "32626c57435945513251753974446c476854756b" +
+ "664273385746772b6e6263460a7a6f706d535245" +
+ "6c6d464d2f6141536d464733574e5a7072696a68" +
+ "504b77726338376470636b31703131635a415478" +
+ "5a413168566d43743457616343673634690a4d74" +
+ "64507a334e2f34416147664956794d2b69624949" +
+ "35332f515543675945417953693556735a356f6a" +
+ "644a795077426e6c6142554231686f2b336b7068" +
+ "70770a7264572b2b4d796b51494a345564534437" +
+ "3052486e5a315839754359713978616671746c51" +
+ "664c44395963442f436d665264706461586c5673" +
+ "5249467a5a556c0a454630557149644e77337046" +
+ "68634f4a6d6e5a3241434470434342476f763542" +
+ "6e3068302b3137686a4b376f69315833716e4542" +
+ "7857326c7462593476556a500a44394c5330666e" +
+ "4a76703043675942504a527330714c4a4a464333" +
+ "6669796b712f57574d38727474354b364a584b50" +
+ "734b674b53644144577a7463316645434d0a7a65" +
+ "2b394a6a5a376b4d77557063666a644c2b745047" +
+ "3455563048326c524375635735414131396d7058" +
+ "50367454494733713737655a6b416e65516f6163" +
+ "41340a716c3073583051476c6a5763414e30464b" +
+ "6f4759733975582b6378445a6e7265362f52392f" +
+ "3930567766443237454c57546373677734633463" +
+ "514b42675143420a6f5432326e745a5a59396d6e" +
+ "72455a36752f492f4a332f35664e396737783733" +
+ "3177746e463745745a5361575453587364597256" +
+ "466b564f6362505135494a6f0a714a6a7249372b" +
+ "474a4d69376f6a4c69642f4c45656f31764f3163" +
+ "454158334f43723236554e38612f6c7434394f5a" +
+ "69354c337348556b756c475951755671650a6737" +
+ "6e6e4632437749544c34503645486443575a4461" +
+ "7a4136626d7375524f2b6462536e335a6c567651" +
+ "4b42674859524c5a665458536c44755264776977" +
+ "746b0a513148546b6d6b57694156726c4f577864" +
+ "5858456d546130303045574c46446145797a7358" +
+ "7834424863357166776b5a4e746b634a56396e58" +
+ "63536e647441530a35767a427a676e797a4f7962" +
+ "68315878484a3966427472414f3847555878446c" +
+ "6634394457616753393449763072596e616b7656" +
+ "2f673039786875415763366e0a5365757230576b" +
+ "5376453847666653734d485149584c456b0a2d2d" +
+ "2d2d2d454e442050524956415445204b45592d2d" +
+ "2d2d2d0a2d2d2d2d2d424547494e204345525449" +
+ "4649434154452d2d2d2d2d0a4d494944656a4343" +
+ "416d494343514330523168584b326649776a414e" +
+ "42676b71686b6947397730424151554641444342" +
+ "6744454c4d416b474131554542684d430a56564d" +
+ "78437a414a42674e564241674d416b355a4d5245" +
+ "77447759445651514844416843636d3976613278" +
+ "35626a45564d424d47413155454367774d54586b" +
+ "670a51304567513278705a5735304d5263774651" +
+ "5944565151444441357465574e68593278705a57" +
+ "35304c6d4e76625445684d423847435371475349" +
+ "62334451454a0a41525953616e5a7a6147466f61" +
+ "5752415a32316861577775593239744d42345844" +
+ "54457a4d4455794e6a49784e4451774d466f5844" +
+ "54457a4d4459794e5449780a4e4451774d466f77" +
+ "6654454c4d416b474131554542684d4356564d78" +
+ "4554415042674e564241674d4345356c6479425a" +
+ "62334a724d52457744775944565151480a444168" +
+ "43636d397661327835626a45514d413447413155" +
+ "454367774854586b67544756685a6a45544d4245" +
+ "47413155454177774b62586c735a57466d4c6d4e" +
+ "760a625445684d42384743537147534962334451" +
+ "454a41525953616e5a7a6147466f615752415a32" +
+ "316861577775593239744d494942496a414e4267" +
+ "6b71686b69470a397730424151454641414f4341" +
+ "5138414d49494243674b43415145416f4b507677" +
+ "5552396f754e786d43646a73783178554b593046" +
+ "63764a4b735071354a36630a536159426d333670" +
+ "7458722f465a4a78794a65634a6264354b2f2b72" +
+ "7872476e414a43796939647831634936356f4a43" +
+ "4e346c42424c43367831754b51352b580a4f5177" +
+ "50315732656a6576414a73555936486f394d6934" +
+ "346b4542624f5377487979515178636b3734325a" +
+ "4856376c7172555434304842694f343774594638" +
+ "690a2b4c674d7955457279594275546876684950" +
+ "7848704b7a44502b624367586b444e79574a7974" +
+ "616c5270466a5163552b3165312f543430477749" +
+ "6b41766a64370a666e504b634141554e4c354876" +
+ "4c4a714b4b5570684b6964794235335a682b6671" +
+ "697448323931726e4b6a38676a61555967316350" +
+ "374942744b5734786736550a572b78657533706a" +
+ "4a504835316c41497761504d6b41646242415243" +
+ "644d38332b76436c32644f4769596b5938307a69" +
+ "45514944415141424d413047435371470a534962" +
+ "3344514542425155414134494241514351752f6c" +
+ "65756863667243476661307047304730386a7a33" +
+ "34586a357972364161382f2b4a72467436347045" +
+ "710a493458475455646e4151696f425230425946" +
+ "42665761332b6538594d564a426f634763753759" +
+ "6634615971734d7635766b426b715a4932435a67" +
+ "5644694f37790a4d4f326b6a372f575679445551" +
+ "7831536c6d2b75435a5942556a6a6a72356e5833" +
+ "42535a7849734f42412b7a46425455705a506879" +
+ "597142373250384e6e63460a427641714241712b" +
+ "4c73364250534f6832746a72787570657a796732" +
+ "55544756586b414537617a4279465a70682b7737" +
+ "417a36644430784d363965364a742f6a0a336844" +
+ "756b324b4e63314a752f7a63326d487374566b79" +
+ "364362696e384473576763726251367673544735" +
+ "3877517369496b4d6474677a4275632f6b552b34" +
+ "640a506f696e4537352f766135797a38316a3073" +
+ "4d59574a4b697262554a6e5a454433547a69484e" +
+ "35340a2d2d2d2d2d454e44204345525449464943" +
+ "4154452d2d2d2d2d0a2d2d2d2d2d424547494e20" +
+ "43455254494649434154452d2d2d2d2d0a4d4949" +
+ "4468444343416d7743435143723761626b536973" +
+ "722b44414e42676b71686b694739773042415155" +
+ "4641444342686a454c4d416b474131554542684d" +
+ "430a56564d78437a414a42674e564241674d416b" +
+ "355a4d524577447759445651514844416843636d" +
+ "397661327835626a45684d423847413155454367" +
+ "775954586b670a5132567964476c6d61574e6864" +
+ "4755675158563061473979615852354d52457744" +
+ "775944565151444441687465574e684c6d39795a" +
+ "7a45684d423847435371470a534962334451454a" +
+ "41525953616e5a7a6147466f615752415a323168" +
+ "61577775593239744d4234584454457a4d445579" +
+ "4e6a49784d5467304d466f584454457a0a4d4459" +
+ "794e5449784d5467304d466f7767594178437a41" +
+ "4a42674e5642415954416c56544d517377435159" +
+ "445651514944414a4f575445524d413847413155" +
+ "450a42777749516e4a7662327473655734784654" +
+ "415442674e5642416f4d4445313549454e424945" +
+ "4e7361575675644445584d425547413155454177" +
+ "774f62586c6a0a59574e73615756756443356a62" +
+ "3230784954416642676b71686b69473977304243" +
+ "514557456d70326332686861476c6b5147647459" +
+ "576c734c6d4e76625443430a415349774451594a" +
+ "4b6f5a496876634e415145424251414467674550" +
+ "4144434341516f4367674542414d345438484b77" +
+ "596367594e34704250534368484d752f0a396a74" +
+ "304a697157456578546f63783964315a46447a61" +
+ "33386b6953476d4c4d747343684c30517277596e" +
+ "4c6268376256354c566c32434d51537a5a495037" +
+ "700a4834373866774a454479694231677a4e7650" +
+ "4258624d796e75676167707048613730614b5941" +
+ "3953624a42736a455376734a3251756946596f44" +
+ "7a75564c55700a4a68384b724f3949614450514d" +
+ "39434c477578754c37564b553849613076465142" +
+ "566c6332646f44436b6533336663366166564f36" +
+ "6b7243796c5377693362680a416931535a376e64" +
+ "554d6b37427951696167416457494f6f374a5878" +
+ "32754a7a6f4b4679594a364755387446714d4b67" +
+ "554b425431767759684c564b4a7443690a717444" +
+ "2f747634366e4c555a4f7a2f685341326b43552b" +
+ "447963444a7067745948787837724b4a43764748" +
+ "3049596f41326853675941502b6b784a73567330" +
+ "430a417745414154414e42676b71686b69473977" +
+ "30424151554641414f43415145414a536b374873" +
+ "4e594d75596a794f3459384231696254745a6d54" +
+ "722b535849480a5031695432384376734c4e6330" +
+ "567959794f704b3546687a445666464533786365" +
+ "5762614242336c6d4e6f3152305377306e706d6e" +
+ "63314270592b68456249610a6838444e56653230" +
+ "657a4e79362f666a6534734368756b724a6a4b66" +
+ "6d66484c6b36753546724f617369495449523962" +
+ "7a4b4a5a75326e79754165417a677a330a6d4579" +
+ "4677705a7149675870766b6977416c74704b4269" +
+ "496c755058786e7254365a6e2f6e634e68545a71" +
+ "573873597a54655664576d686b576f49315a5358" +
+ "6a0a6a46757739705a57764c2b58646b746d5249" +
+ "476b784b637878614650364b544b495055425735" +
+ "6c5057765477654c397853645878776149592f58" +
+ "4a62467569530a787a6449722b346b2f44554c77" +
+ "7430467832366a4b62737066644d726c49444451" +
+ "464d4f413151396534764f2b6151444a32507355" +
+ "513d3d0a2d2d2d2d2d454e442043455254494649" +
+ "434154452d2d2d2d2d0a2d2d2d2d2d424547494e" +
+ "2043455254494649434154452d2d2d2d2d0a4d49" +
+ "49443454434341736d67417749424167494a414d" +
+ "7769544575596f6f6a384d413047435371475349" +
+ "623344514542425155414d4947474d5173774351" +
+ "59440a5651514745774a56557a454c4d416b4741" +
+ "31554543417743546c6b784554415042674e5642" +
+ "41634d43454a796232397262486c754d53457748" +
+ "7759445651514b0a4442684e655342445a584a30" +
+ "61575a70593246305a5342426458526f62334a70" +
+ "64486b784554415042674e5642414d4d43473135" +
+ "5932457562334a6e4d5345770a4877594a4b6f5a" +
+ "496876634e41516b4246684a71646e4e6f595768" +
+ "705a45426e625746706243356a62323077486863" +
+ "4e4d544d774e5449324d6a45774e5441780a5768" +
+ "634e4d6a4d774e5449304d6a45774e544178576a" +
+ "4342686a454c4d416b474131554542684d435656" +
+ "4d78437a414a42674e564241674d416b355a4d52" +
+ "45770a447759445651514844416843636d397661" +
+ "327835626a45684d423847413155454367775954" +
+ "586b675132567964476c6d61574e686447556751" +
+ "585630614739790a615852354d52457744775944" +
+ "565151444441687465574e684c6d39795a7a4568" +
+ "4d42384743537147534962334451454a41525953" +
+ "616e5a7a6147466f615752410a5a323168615777" +
+ "75593239744d494942496a414e42676b71686b69" +
+ "47397730424151454641414f43415138414d4949" +
+ "4243674b434151454138507574674634330a3032" +
+ "33754c737938444e645753315a467a5369324975" +
+ "6e69443947484b69664f6434317544672f375a75" +
+ "4731447071324259367a34635633686c74473067" +
+ "75530a4178754a4442735144706d503468666f77" +
+ "6a4141523962382b5138376454534e5462435a74" +
+ "3642746f4c6174326764654f4334433544427472" +
+ "684e79314d6a4f0a46416575493479506e6f7867" +
+ "31676135377741597742306c48746f2b4c383872" +
+ "566f53654d4348484b665944696954354e4b786c" +
+ "6e59413279447356454c31520a3662774334656d" +
+ "7a5770715a5152736e6f4531516e69642f6f5830" +
+ "4a6837324b796c2b7870516934424e5253696172" +
+ "67665549754c7858755a6c635045786c460a7145" +
+ "74646757624d456a65555876303845494652502f" +
+ "6f503361474a41366c346b665537383779737670" +
+ "4d774c72374b663062544b4c524f6b5874625132" +
+ "79760a6d31787162567262655635716177494441" +
+ "5141426f314177546a416442674e564851344546" +
+ "67515561783441714a2f3666514435344a30506b" +
+ "497951714b45330a61396f77487759445652306a" +
+ "42426777466f415561783441714a2f3666514435" +
+ "344a30506b497951714b453361396f7744415944" +
+ "5652305442415577417745420a2f7a414e42676b" +
+ "71686b6947397730424151554641414f43415145" +
+ "417a57397a5456594c387934633467494d464c73" +
+ "76335478442f742b554c616d4a675648340a5836" +
+ "65674536724d73426a69567a344e4b5a506f6c64" +
+ "556255394a52387233316e6e73695a574a637845" +
+ "7764364f6e443143766e654d7351382f34476739" +
+ "77360a486d495177455a33787032667135596c58" +
+ "50736d775255667054507554356f55616853586b" +
+ "7975564339796f31326b753841454f2f55375132" +
+ "616a6c5a6437370a79736f63582f6c546f49666e" +
+ "4d3573767a2b51542f4f7836624c435145357532" +
+ "78515032446c376935436242666c502b61615048" +
+ "324935756c444b67337371320a7a4e5942315868" +
+ "414b474f623773384a4f7a554538425143396f41" +
+ "4f6b4c4b55306955577548703268345366684d57" +
+ "76776d316f656f5363786f706a594964710a4a63" +
+ "476865412b3636462f687571796b6239304a5078" +
+ "4c4c48665050534e66544a75696377314f7a7574" +
+ "77796d5a695731673d3d0a2d2d2d2d2d454e4420" +
+ "43455254494649434154452d2d2d2d2d0a",
+)
+
+// Script of interaction with openssl implementation:
+//
+// openssl s_server -cipher ECDHE-ECDSA-AES128-SHA \
+// -key server.key -cert server.crt -port 10443
+//
+// The values for this test are obtained by building and running in client mode:
+// % go test -test.run "TestRunClient" -connect -ciphersuites=0xc009
+// The recorded bytes are written to stdout.
+//
+// The server private key is:
+//
+// -----BEGIN EC PARAMETERS-----
+// BgUrgQQAIw==
+// -----END EC PARAMETERS-----
+// -----BEGIN EC PRIVATE KEY-----
+// MIHcAgEBBEIBmIPpCa0Kyeo9M/nq5mHxeFIGlw+MqakWcvHu3Keo7xK9ZWG7JG3a
+// XfS01efjqSZJvF2DoL+Sly4A5iBn0Me9mdegBwYFK4EEACOhgYkDgYYABADEoe2+
+// mPkLSHM2fsMWVhEi8j1TwztNIT3Na3Xm9rDcmt8mwbyyh/ByMnyzZC8ckLzqaCMQ
+// fv7jJcBIOmngKG3TNwDvBGLdDaCccGKD2IHTZDGqnpcxvZawaMCbI952ZD8aXH/p
+// Eg5YWLZfcN2b2OrV1/XVzLm2nzBmW2aaIOIn5b/+Ow==
+// -----END EC PRIVATE KEY-----
+//
+// and certificate is:
+//
+// -----BEGIN CERTIFICATE-----
+// MIICADCCAWICCQC4vy1HoNLr9DAJBgcqhkjOPQQBMEUxCzAJBgNVBAYTAkFVMRMw
+// EQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0
+// eSBMdGQwHhcNMTIxMTIyMTUwNjMyWhcNMjIxMTIwMTUwNjMyWjBFMQswCQYDVQQG
+// EwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lk
+// Z2l0cyBQdHkgTHRkMIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQAxKHtvpj5C0hz
+// Nn7DFlYRIvI9U8M7TSE9zWt15vaw3JrfJsG8sofwcjJ8s2QvHJC86mgjEH7+4yXA
+// SDpp4Cht0zcA7wRi3Q2gnHBig9iB02Qxqp6XMb2WsGjAmyPedmQ/Glx/6RIOWFi2
+// X3Ddm9jq1df11cy5tp8wZltmmiDiJ+W//jswCQYHKoZIzj0EAQOBjAAwgYgCQgGI
+// ok/r4kXFSH0brPXtmJ2uR3DAXhu2L73xtk23YUDTEaLO7gt+kn7/dp3DO36lP876
+// EOJZ7EctfKzaTpcOFaBv0AJCAU38vmcTnC0FDr0/o4wlwTMTgw2UBrvUN3r27HrJ
+// hi7d1xFpf4V8Vt77MXgr5Md4Da7Lvp5ONiQxe2oPOZUSB48q
+// -----END CERTIFICATE-----
+var ecdheECDSAAESClientScript = [][]byte{
+ {
+ 0x16, 0x03, 0x01, 0x00, 0x4a, 0x01, 0x00, 0x00,
+ 0x46, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xc0, 0x09,
+ 0x01, 0x00, 0x00, 0x1b, 0x00, 0x05, 0x00, 0x05,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00,
+ 0x08, 0x00, 0x06, 0x00, 0x17, 0x00, 0x18, 0x00,
+ 0x19, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00,
+ },
+ {
+ 0x16, 0x03, 0x01, 0x00, 0x54, 0x02, 0x00, 0x00,
+ 0x50, 0x03, 0x01, 0x50, 0xd7, 0x19, 0xc9, 0x03,
+ 0xc2, 0x3a, 0xc6, 0x1f, 0x0a, 0x84, 0x9e, 0xd7,
+ 0xf4, 0x7e, 0x07, 0x6d, 0xa8, 0xe4, 0xa9, 0x4f,
+ 0x22, 0x50, 0xa2, 0x19, 0x24, 0x44, 0x42, 0x65,
+ 0xaa, 0xba, 0x3a, 0x20, 0x90, 0x70, 0xb7, 0xe5,
+ 0x57, 0xed, 0xb1, 0xb1, 0x43, 0x4b, 0xa1, 0x4e,
+ 0xee, 0x7a, 0x5b, 0x88, 0xf6, 0xa6, 0x73, 0x3b,
+ 0xcb, 0xa7, 0xbd, 0x57, 0x50, 0xf2, 0x72, 0x8c,
+ 0xbc, 0x45, 0x73, 0xaa, 0xc0, 0x09, 0x00, 0x00,
+ 0x08, 0x00, 0x0b, 0x00, 0x04, 0x03, 0x00, 0x01,
+ 0x02, 0x16, 0x03, 0x01, 0x02, 0x0e, 0x0b, 0x00,
+ 0x02, 0x0a, 0x00, 0x02, 0x07, 0x00, 0x02, 0x04,
+ 0x30, 0x82, 0x02, 0x00, 0x30, 0x82, 0x01, 0x62,
+ 0x02, 0x09, 0x00, 0xb8, 0xbf, 0x2d, 0x47, 0xa0,
+ 0xd2, 0xeb, 0xf4, 0x30, 0x09, 0x06, 0x07, 0x2a,
+ 0x86, 0x48, 0xce, 0x3d, 0x04, 0x01, 0x30, 0x45,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
+ 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30,
+ 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a,
+ 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61,
+ 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03,
+ 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74,
+ 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69,
+ 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74,
+ 0x79, 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17,
+ 0x0d, 0x31, 0x32, 0x31, 0x31, 0x32, 0x32, 0x31,
+ 0x35, 0x30, 0x36, 0x33, 0x32, 0x5a, 0x17, 0x0d,
+ 0x32, 0x32, 0x31, 0x31, 0x32, 0x30, 0x31, 0x35,
+ 0x30, 0x36, 0x33, 0x32, 0x5a, 0x30, 0x45, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11,
+ 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53,
+ 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74,
+ 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55,
+ 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65,
+ 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64,
+ 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79,
+ 0x20, 0x4c, 0x74, 0x64, 0x30, 0x81, 0x9b, 0x30,
+ 0x10, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d,
+ 0x02, 0x01, 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00,
+ 0x23, 0x03, 0x81, 0x86, 0x00, 0x04, 0x00, 0xc4,
+ 0xa1, 0xed, 0xbe, 0x98, 0xf9, 0x0b, 0x48, 0x73,
+ 0x36, 0x7e, 0xc3, 0x16, 0x56, 0x11, 0x22, 0xf2,
+ 0x3d, 0x53, 0xc3, 0x3b, 0x4d, 0x21, 0x3d, 0xcd,
+ 0x6b, 0x75, 0xe6, 0xf6, 0xb0, 0xdc, 0x9a, 0xdf,
+ 0x26, 0xc1, 0xbc, 0xb2, 0x87, 0xf0, 0x72, 0x32,
+ 0x7c, 0xb3, 0x64, 0x2f, 0x1c, 0x90, 0xbc, 0xea,
+ 0x68, 0x23, 0x10, 0x7e, 0xfe, 0xe3, 0x25, 0xc0,
+ 0x48, 0x3a, 0x69, 0xe0, 0x28, 0x6d, 0xd3, 0x37,
+ 0x00, 0xef, 0x04, 0x62, 0xdd, 0x0d, 0xa0, 0x9c,
+ 0x70, 0x62, 0x83, 0xd8, 0x81, 0xd3, 0x64, 0x31,
+ 0xaa, 0x9e, 0x97, 0x31, 0xbd, 0x96, 0xb0, 0x68,
+ 0xc0, 0x9b, 0x23, 0xde, 0x76, 0x64, 0x3f, 0x1a,
+ 0x5c, 0x7f, 0xe9, 0x12, 0x0e, 0x58, 0x58, 0xb6,
+ 0x5f, 0x70, 0xdd, 0x9b, 0xd8, 0xea, 0xd5, 0xd7,
+ 0xf5, 0xd5, 0xcc, 0xb9, 0xb6, 0x9f, 0x30, 0x66,
+ 0x5b, 0x66, 0x9a, 0x20, 0xe2, 0x27, 0xe5, 0xbf,
+ 0xfe, 0x3b, 0x30, 0x09, 0x06, 0x07, 0x2a, 0x86,
+ 0x48, 0xce, 0x3d, 0x04, 0x01, 0x03, 0x81, 0x8c,
+ 0x00, 0x30, 0x81, 0x88, 0x02, 0x42, 0x01, 0x88,
+ 0xa2, 0x4f, 0xeb, 0xe2, 0x45, 0xc5, 0x48, 0x7d,
+ 0x1b, 0xac, 0xf5, 0xed, 0x98, 0x9d, 0xae, 0x47,
+ 0x70, 0xc0, 0x5e, 0x1b, 0xb6, 0x2f, 0xbd, 0xf1,
+ 0xb6, 0x4d, 0xb7, 0x61, 0x40, 0xd3, 0x11, 0xa2,
+ 0xce, 0xee, 0x0b, 0x7e, 0x92, 0x7e, 0xff, 0x76,
+ 0x9d, 0xc3, 0x3b, 0x7e, 0xa5, 0x3f, 0xce, 0xfa,
+ 0x10, 0xe2, 0x59, 0xec, 0x47, 0x2d, 0x7c, 0xac,
+ 0xda, 0x4e, 0x97, 0x0e, 0x15, 0xa0, 0x6f, 0xd0,
+ 0x02, 0x42, 0x01, 0x4d, 0xfc, 0xbe, 0x67, 0x13,
+ 0x9c, 0x2d, 0x05, 0x0e, 0xbd, 0x3f, 0xa3, 0x8c,
+ 0x25, 0xc1, 0x33, 0x13, 0x83, 0x0d, 0x94, 0x06,
+ 0xbb, 0xd4, 0x37, 0x7a, 0xf6, 0xec, 0x7a, 0xc9,
+ 0x86, 0x2e, 0xdd, 0xd7, 0x11, 0x69, 0x7f, 0x85,
+ 0x7c, 0x56, 0xde, 0xfb, 0x31, 0x78, 0x2b, 0xe4,
+ 0xc7, 0x78, 0x0d, 0xae, 0xcb, 0xbe, 0x9e, 0x4e,
+ 0x36, 0x24, 0x31, 0x7b, 0x6a, 0x0f, 0x39, 0x95,
+ 0x12, 0x07, 0x8f, 0x2a, 0x16, 0x03, 0x01, 0x00,
+ 0xd6, 0x0c, 0x00, 0x00, 0xd2, 0x03, 0x00, 0x17,
+ 0x41, 0x04, 0x33, 0xed, 0xe1, 0x10, 0x3d, 0xe2,
+ 0xb0, 0x81, 0x5e, 0x01, 0x1b, 0x00, 0x4a, 0x7d,
+ 0xdc, 0xc5, 0x78, 0x02, 0xb1, 0x9a, 0x78, 0x92,
+ 0x34, 0xd9, 0x23, 0xcc, 0x01, 0xfb, 0x0c, 0x49,
+ 0x1c, 0x4a, 0x59, 0x8a, 0x80, 0x1b, 0x34, 0xf0,
+ 0xe8, 0x87, 0x1b, 0x7c, 0xfb, 0x72, 0xf5, 0xea,
+ 0xf9, 0xf3, 0xff, 0xa6, 0x3e, 0x4e, 0xac, 0xbc,
+ 0xee, 0x14, 0x2b, 0x87, 0xd4, 0x0b, 0xda, 0x19,
+ 0x60, 0x2b, 0x00, 0x8b, 0x30, 0x81, 0x88, 0x02,
+ 0x42, 0x01, 0x75, 0x46, 0x4f, 0x97, 0x9f, 0xc5,
+ 0xf9, 0x4c, 0x38, 0xcf, 0x3b, 0x37, 0x1a, 0x6b,
+ 0x53, 0xfc, 0x05, 0x73, 0x7d, 0x98, 0x2c, 0x5b,
+ 0x76, 0xd4, 0x37, 0x1f, 0x50, 0x6d, 0xad, 0xc6,
+ 0x0f, 0x8f, 0x7b, 0xcc, 0x60, 0x8e, 0x04, 0x00,
+ 0x21, 0x80, 0xa8, 0xa5, 0x98, 0xf2, 0x42, 0xf2,
+ 0xc3, 0xf6, 0x44, 0x50, 0xc4, 0x7a, 0xae, 0x6f,
+ 0x74, 0xa0, 0x7f, 0x07, 0x7a, 0x0b, 0xbb, 0x41,
+ 0x9e, 0x3c, 0x0b, 0x02, 0x42, 0x01, 0xbe, 0x64,
+ 0xaa, 0x12, 0x03, 0xfb, 0xd8, 0x4f, 0x93, 0xf9,
+ 0x92, 0x54, 0x0d, 0x9c, 0x9d, 0x53, 0x88, 0x19,
+ 0x69, 0x94, 0xfc, 0xd6, 0xf7, 0x60, 0xcf, 0x70,
+ 0x64, 0x15, 0x1b, 0x02, 0x22, 0x56, 0xb0, 0x2c,
+ 0xb1, 0x72, 0x4c, 0x9e, 0x7b, 0xf0, 0x53, 0x97,
+ 0x43, 0xac, 0x11, 0x62, 0xe5, 0x5a, 0xf1, 0x7e,
+ 0x87, 0x8f, 0x5c, 0x43, 0x1d, 0xae, 0x56, 0x28,
+ 0xdb, 0x76, 0x15, 0xd8, 0x1c, 0x73, 0xce, 0x16,
+ 0x03, 0x01, 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x16, 0x03, 0x01, 0x00, 0x46, 0x10, 0x00, 0x00,
+ 0x42, 0x41, 0x04, 0x1e, 0x18, 0x37, 0xef, 0x0d,
+ 0x19, 0x51, 0x88, 0x35, 0x75, 0x71, 0xb5, 0xe5,
+ 0x54, 0x5b, 0x12, 0x2e, 0x8f, 0x09, 0x67, 0xfd,
+ 0xa7, 0x24, 0x20, 0x3e, 0xb2, 0x56, 0x1c, 0xce,
+ 0x97, 0x28, 0x5e, 0xf8, 0x2b, 0x2d, 0x4f, 0x9e,
+ 0xf1, 0x07, 0x9f, 0x6c, 0x4b, 0x5b, 0x83, 0x56,
+ 0xe2, 0x32, 0x42, 0xe9, 0x58, 0xb6, 0xd7, 0x49,
+ 0xa6, 0xb5, 0x68, 0x1a, 0x41, 0x03, 0x56, 0x6b,
+ 0xdc, 0x5a, 0x89, 0x14, 0x03, 0x01, 0x00, 0x01,
+ 0x01, 0x16, 0x03, 0x01, 0x00, 0x30, 0x1a, 0x45,
+ 0x92, 0x3b, 0xac, 0x8d, 0x91, 0x89, 0xd3, 0x2c,
+ 0xf4, 0x3c, 0x5f, 0x70, 0xf1, 0x79, 0xa5, 0x6a,
+ 0xcf, 0x97, 0x8f, 0x3f, 0x73, 0x08, 0xca, 0x3f,
+ 0x55, 0xb0, 0x28, 0xd1, 0x6f, 0xcd, 0x9b, 0xca,
+ 0xb6, 0xb7, 0xd0, 0xa5, 0x21, 0x5b, 0x08, 0xf8,
+ 0x42, 0xe2, 0xdf, 0x25, 0x6a, 0x16,
+ },
+ {
+ 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03,
+ 0x01, 0x00, 0x30, 0x30, 0x83, 0xb6, 0x51, 0x8a,
+ 0x85, 0x4a, 0xee, 0xe4, 0xb6, 0xae, 0xf3, 0xc1,
+ 0xdc, 0xd2, 0x04, 0xb3, 0xd0, 0x25, 0x47, 0x5f,
+ 0xac, 0x83, 0xa3, 0x7d, 0xcf, 0x47, 0x92, 0xed,
+ 0x92, 0x6c, 0xd1, 0x6e, 0xfd, 0x63, 0xf5, 0x2d,
+ 0x89, 0xd8, 0x04, 0x8c, 0x62, 0x71, 0xae, 0x5e,
+ 0x32, 0x48, 0xf8,
+ },
+ {
+ 0x17, 0x03, 0x01, 0x00, 0x20, 0xcf, 0x5e, 0xba,
+ 0xf4, 0x47, 0x32, 0x35, 0x9b, 0x85, 0xdc, 0xb3,
+ 0xff, 0x77, 0x90, 0xd9, 0x2b, 0xbd, 0x59, 0x2a,
+ 0x33, 0xe4, 0x6e, 0x9b, 0xfc, 0x1c, 0x73, 0x3f,
+ 0x5e, 0x1e, 0xe3, 0xa4, 0xc2, 0x17, 0x03, 0x01,
+ 0x00, 0x20, 0x05, 0xdf, 0x2d, 0x9b, 0x29, 0x7f,
+ 0x97, 0xcd, 0x49, 0x04, 0x53, 0x22, 0x1a, 0xa1,
+ 0xa1, 0xe6, 0x38, 0x3a, 0x56, 0x37, 0x1f, 0xd8,
+ 0x3a, 0x12, 0x2c, 0xf0, 0xeb, 0x61, 0x35, 0x76,
+ 0xe5, 0xf0, 0x15, 0x03, 0x01, 0x00, 0x20, 0xa5,
+ 0x56, 0xb5, 0x49, 0x4b, 0xc2, 0xd4, 0x4c, 0xf6,
+ 0x95, 0x15, 0x7d, 0x41, 0x1d, 0x5c, 0x00, 0x0e,
+ 0x20, 0xb1, 0x0a, 0xbc, 0xc9, 0x2a, 0x09, 0x17,
+ 0xb4, 0xaa, 0x1c, 0x79, 0xda, 0x79, 0x27,
+ },
+}
diff --git a/src/pkg/crypto/tls/handshake_messages.go b/src/pkg/crypto/tls/handshake_messages.go
index cdd491707..83952000f 100644
--- a/src/pkg/crypto/tls/handshake_messages.go
+++ b/src/pkg/crypto/tls/handshake_messages.go
@@ -20,6 +20,7 @@ type clientHelloMsg struct {
supportedPoints []uint8
ticketSupported bool
sessionTicket []uint8
+ signatureAndHashes []signatureAndHash
}
func (m *clientHelloMsg) equal(i interface{}) bool {
@@ -40,7 +41,8 @@ func (m *clientHelloMsg) equal(i interface{}) bool {
eqUint16s(m.supportedCurves, m1.supportedCurves) &&
bytes.Equal(m.supportedPoints, m1.supportedPoints) &&
m.ticketSupported == m1.ticketSupported &&
- bytes.Equal(m.sessionTicket, m1.sessionTicket)
+ bytes.Equal(m.sessionTicket, m1.sessionTicket) &&
+ eqSignatureAndHashes(m.signatureAndHashes, m1.signatureAndHashes)
}
func (m *clientHelloMsg) marshal() []byte {
@@ -74,6 +76,10 @@ func (m *clientHelloMsg) marshal() []byte {
extensionsLength += len(m.sessionTicket)
numExtensions++
}
+ if len(m.signatureAndHashes) > 0 {
+ extensionsLength += 2 + 2*len(m.signatureAndHashes)
+ numExtensions++
+ }
if numExtensions > 0 {
extensionsLength += 4 * numExtensions
length += 2 + extensionsLength
@@ -199,6 +205,25 @@ func (m *clientHelloMsg) marshal() []byte {
copy(z, m.sessionTicket)
z = z[len(m.sessionTicket):]
}
+ if len(m.signatureAndHashes) > 0 {
+ // https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1
+ z[0] = byte(extensionSignatureAlgorithms >> 8)
+ z[1] = byte(extensionSignatureAlgorithms)
+ l := 2 + 2*len(m.signatureAndHashes)
+ z[2] = byte(l >> 8)
+ z[3] = byte(l)
+ z = z[4:]
+
+ l -= 2
+ z[0] = byte(l >> 8)
+ z[1] = byte(l)
+ z = z[2:]
+ for _, sigAndHash := range m.signatureAndHashes {
+ z[0] = sigAndHash.hash
+ z[1] = sigAndHash.signature
+ z = z[2:]
+ }
+ }
m.raw = x
@@ -249,6 +274,7 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool {
m.ocspStapling = false
m.ticketSupported = false
m.sessionTicket = nil
+ m.signatureAndHashes = nil
if len(data) == 0 {
// ClientHello is optionally followed by extension data
@@ -336,6 +362,23 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool {
// http://tools.ietf.org/html/rfc5077#section-3.2
m.ticketSupported = true
m.sessionTicket = data[:length]
+ case extensionSignatureAlgorithms:
+ // https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1
+ if length < 2 || length&1 != 0 {
+ return false
+ }
+ l := int(data[0])<<8 | int(data[1])
+ if l != length-2 {
+ return false
+ }
+ n := l / 2
+ d := data[2:]
+ m.signatureAndHashes = make([]signatureAndHash, n)
+ for i := range m.signatureAndHashes {
+ m.signatureAndHashes[i].hash = d[0]
+ m.signatureAndHashes[i].signature = d[1]
+ d = d[2:]
+ }
}
data = data[length:]
}
@@ -899,8 +942,14 @@ func (m *nextProtoMsg) unmarshal(data []byte) bool {
}
type certificateRequestMsg struct {
- raw []byte
+ raw []byte
+ // hasSignatureAndHash indicates whether this message includes a list
+ // of signature and hash functions. This change was introduced with TLS
+ // 1.2.
+ hasSignatureAndHash bool
+
certificateTypes []byte
+ signatureAndHashes []signatureAndHash
certificateAuthorities [][]byte
}
@@ -912,7 +961,8 @@ func (m *certificateRequestMsg) equal(i interface{}) bool {
return bytes.Equal(m.raw, m1.raw) &&
bytes.Equal(m.certificateTypes, m1.certificateTypes) &&
- eqByteSlices(m.certificateAuthorities, m1.certificateAuthorities)
+ eqByteSlices(m.certificateAuthorities, m1.certificateAuthorities) &&
+ eqSignatureAndHashes(m.signatureAndHashes, m1.signatureAndHashes)
}
func (m *certificateRequestMsg) marshal() (x []byte) {
@@ -928,6 +978,10 @@ func (m *certificateRequestMsg) marshal() (x []byte) {
}
length += casLength
+ if m.hasSignatureAndHash {
+ length += 2 + 2*len(m.signatureAndHashes)
+ }
+
x = make([]byte, 4+length)
x[0] = typeCertificateRequest
x[1] = uint8(length >> 16)
@@ -938,6 +992,19 @@ func (m *certificateRequestMsg) marshal() (x []byte) {
copy(x[5:], m.certificateTypes)
y := x[5+len(m.certificateTypes):]
+
+ if m.hasSignatureAndHash {
+ n := len(m.signatureAndHashes) * 2
+ y[0] = uint8(n >> 8)
+ y[1] = uint8(n)
+ y = y[2:]
+ for _, sigAndHash := range m.signatureAndHashes {
+ y[0] = sigAndHash.hash
+ y[1] = sigAndHash.signature
+ y = y[2:]
+ }
+ }
+
y[0] = uint8(casLength >> 8)
y[1] = uint8(casLength)
y = y[2:]
@@ -978,6 +1045,27 @@ func (m *certificateRequestMsg) unmarshal(data []byte) bool {
data = data[numCertTypes:]
+ if m.hasSignatureAndHash {
+ if len(data) < 2 {
+ return false
+ }
+ sigAndHashLen := uint16(data[0])<<8 | uint16(data[1])
+ data = data[2:]
+ if sigAndHashLen&1 != 0 {
+ return false
+ }
+ if len(data) < int(sigAndHashLen) {
+ return false
+ }
+ numSigAndHash := sigAndHashLen / 2
+ m.signatureAndHashes = make([]signatureAndHash, numSigAndHash)
+ for i := range m.signatureAndHashes {
+ m.signatureAndHashes[i].hash = data[0]
+ m.signatureAndHashes[i].signature = data[1]
+ data = data[2:]
+ }
+ }
+
if len(data) < 2 {
return false
}
@@ -1013,8 +1101,10 @@ func (m *certificateRequestMsg) unmarshal(data []byte) bool {
}
type certificateVerifyMsg struct {
- raw []byte
- signature []byte
+ raw []byte
+ hasSignatureAndHash bool
+ signatureAndHash signatureAndHash
+ signature []byte
}
func (m *certificateVerifyMsg) equal(i interface{}) bool {
@@ -1024,6 +1114,9 @@ func (m *certificateVerifyMsg) equal(i interface{}) bool {
}
return bytes.Equal(m.raw, m1.raw) &&
+ m.hasSignatureAndHash == m1.hasSignatureAndHash &&
+ m.signatureAndHash.hash == m1.signatureAndHash.hash &&
+ m.signatureAndHash.signature == m1.signatureAndHash.signature &&
bytes.Equal(m.signature, m1.signature)
}
@@ -1035,14 +1128,23 @@ func (m *certificateVerifyMsg) marshal() (x []byte) {
// See http://tools.ietf.org/html/rfc4346#section-7.4.8
siglength := len(m.signature)
length := 2 + siglength
+ if m.hasSignatureAndHash {
+ length += 2
+ }
x = make([]byte, 4+length)
x[0] = typeCertificateVerify
x[1] = uint8(length >> 16)
x[2] = uint8(length >> 8)
x[3] = uint8(length)
- x[4] = uint8(siglength >> 8)
- x[5] = uint8(siglength)
- copy(x[6:], m.signature)
+ y := x[4:]
+ if m.hasSignatureAndHash {
+ y[0] = m.signatureAndHash.hash
+ y[1] = m.signatureAndHash.signature
+ y = y[2:]
+ }
+ y[0] = uint8(siglength >> 8)
+ y[1] = uint8(siglength)
+ copy(y[2:], m.signature)
m.raw = x
@@ -1061,12 +1163,23 @@ func (m *certificateVerifyMsg) unmarshal(data []byte) bool {
return false
}
- siglength := int(data[4])<<8 + int(data[5])
- if len(data)-6 != siglength {
+ data = data[4:]
+ if m.hasSignatureAndHash {
+ m.signatureAndHash.hash = data[0]
+ m.signatureAndHash.signature = data[1]
+ data = data[2:]
+ }
+
+ if len(data) < 2 {
+ return false
+ }
+ siglength := int(data[0])<<8 + int(data[1])
+ data = data[2:]
+ if len(data) != siglength {
return false
}
- m.signature = data[6:]
+ m.signature = data
return true
}
@@ -1165,3 +1278,16 @@ func eqByteSlices(x, y [][]byte) bool {
}
return true
}
+
+func eqSignatureAndHashes(x, y []signatureAndHash) bool {
+ if len(x) != len(y) {
+ return false
+ }
+ for i, v := range x {
+ v2 := y[i]
+ if v.hash != v2.hash || v.signature != v2.signature {
+ return false
+ }
+ }
+ return true
+}
diff --git a/src/pkg/crypto/tls/handshake_messages_test.go b/src/pkg/crypto/tls/handshake_messages_test.go
index 3434bad9f..4f569eeb1 100644
--- a/src/pkg/crypto/tls/handshake_messages_test.go
+++ b/src/pkg/crypto/tls/handshake_messages_test.go
@@ -135,6 +135,9 @@ func (*clientHelloMsg) Generate(rand *rand.Rand, size int) reflect.Value {
m.sessionTicket = randomBytes(rand.Intn(300), rand)
}
}
+ if rand.Intn(10) > 5 {
+ m.signatureAndHashes = supportedSKXSignatureAlgorithms
+ }
return reflect.ValueOf(m)
}
diff --git a/src/pkg/crypto/tls/handshake_server.go b/src/pkg/crypto/tls/handshake_server.go
index 823730c60..c9ccf675c 100644
--- a/src/pkg/crypto/tls/handshake_server.go
+++ b/src/pkg/crypto/tls/handshake_server.go
@@ -6,9 +6,11 @@ package tls
import (
"crypto"
+ "crypto/ecdsa"
"crypto/rsa"
"crypto/subtle"
"crypto/x509"
+ "encoding/asn1"
"errors"
"io"
)
@@ -21,10 +23,12 @@ type serverHandshakeState struct {
hello *serverHelloMsg
suite *cipherSuite
ellipticOk bool
+ ecdsaOk bool
sessionState *sessionState
finishedHash finishedHash
masterSecret []byte
certsFromClient [][]byte
+ cert *Certificate
}
// serverHandshake performs a TLS handshake as a server.
@@ -98,7 +102,7 @@ func (hs *serverHandshakeState) readClientHello() (isResume bool, err error) {
if !ok {
return false, c.sendAlert(alertUnexpectedMessage)
}
- c.vers, ok = mutualVersion(hs.clientHello.vers)
+ c.vers, ok = config.mutualVersion(hs.clientHello.vers)
if !ok {
return false, c.sendAlert(alertProtocolVersion)
}
@@ -156,11 +160,25 @@ Curves:
if len(hs.clientHello.serverName) > 0 {
c.serverName = hs.clientHello.serverName
}
- if hs.clientHello.nextProtoNeg {
+ // Although sending an empty NPN extension is reasonable, Firefox has
+ // had a bug around this. Best to send nothing at all if
+ // config.NextProtos is empty. See
+ // https://code.google.com/p/go/issues/detail?id=5445.
+ if hs.clientHello.nextProtoNeg && len(config.NextProtos) > 0 {
hs.hello.nextProtoNeg = true
hs.hello.nextProtos = config.NextProtos
}
+ if len(config.Certificates) == 0 {
+ return false, c.sendAlert(alertInternalError)
+ }
+ hs.cert = &config.Certificates[0]
+ if len(hs.clientHello.serverName) > 0 {
+ hs.cert = config.getCertificateForName(hs.clientHello.serverName)
+ }
+
+ _, hs.ecdsaOk = hs.cert.PrivateKey.(*ecdsa.PrivateKey)
+
if hs.checkForResumption() {
return true, nil
}
@@ -175,7 +193,7 @@ Curves:
}
for _, id := range preferenceList {
- if hs.suite = c.tryCipherSuite(id, supportedList, hs.ellipticOk); hs.suite != nil {
+ if hs.suite = c.tryCipherSuite(id, supportedList, c.vers, hs.ellipticOk, hs.ecdsaOk); hs.suite != nil {
break
}
}
@@ -199,7 +217,7 @@ func (hs *serverHandshakeState) checkForResumption() bool {
if hs.sessionState.vers > hs.clientHello.vers {
return false
}
- if vers, ok := mutualVersion(hs.sessionState.vers); !ok || vers != hs.sessionState.vers {
+ if vers, ok := c.config.mutualVersion(hs.sessionState.vers); !ok || vers != hs.sessionState.vers {
return false
}
@@ -216,7 +234,7 @@ func (hs *serverHandshakeState) checkForResumption() bool {
}
// Check that we also support the ciphersuite from the session.
- hs.suite = c.tryCipherSuite(hs.sessionState.cipherSuite, c.config.cipherSuites(), hs.ellipticOk)
+ hs.suite = c.tryCipherSuite(hs.sessionState.cipherSuite, c.config.cipherSuites(), hs.sessionState.vers, hs.ellipticOk, hs.ecdsaOk)
if hs.suite == nil {
return false
}
@@ -258,15 +276,7 @@ func (hs *serverHandshakeState) doFullHandshake() error {
config := hs.c.config
c := hs.c
- if len(config.Certificates) == 0 {
- return c.sendAlert(alertInternalError)
- }
- cert := &config.Certificates[0]
- if len(hs.clientHello.serverName) > 0 {
- cert = config.getCertificateForName(hs.clientHello.serverName)
- }
-
- if hs.clientHello.ocspStapling && len(cert.OCSPStaple) > 0 {
+ if hs.clientHello.ocspStapling && len(hs.cert.OCSPStaple) > 0 {
hs.hello.ocspStapling = true
}
@@ -276,20 +286,20 @@ func (hs *serverHandshakeState) doFullHandshake() error {
c.writeRecord(recordTypeHandshake, hs.hello.marshal())
certMsg := new(certificateMsg)
- certMsg.certificates = cert.Certificate
+ certMsg.certificates = hs.cert.Certificate
hs.finishedHash.Write(certMsg.marshal())
c.writeRecord(recordTypeHandshake, certMsg.marshal())
if hs.hello.ocspStapling {
certStatus := new(certificateStatusMsg)
certStatus.statusType = statusTypeOCSP
- certStatus.response = cert.OCSPStaple
+ certStatus.response = hs.cert.OCSPStaple
hs.finishedHash.Write(certStatus.marshal())
c.writeRecord(recordTypeHandshake, certStatus.marshal())
}
- keyAgreement := hs.suite.ka()
- skx, err := keyAgreement.generateServerKeyExchange(config, cert, hs.clientHello, hs.hello)
+ keyAgreement := hs.suite.ka(c.vers)
+ skx, err := keyAgreement.generateServerKeyExchange(config, hs.cert, hs.clientHello, hs.hello)
if err != nil {
c.sendAlert(alertHandshakeFailure)
return err
@@ -302,7 +312,14 @@ func (hs *serverHandshakeState) doFullHandshake() error {
if config.ClientAuth >= RequestClientCert {
// Request a client certificate
certReq := new(certificateRequestMsg)
- certReq.certificateTypes = []byte{certTypeRSASign}
+ certReq.certificateTypes = []byte{
+ byte(certTypeRSASign),
+ byte(certTypeECDSASign),
+ }
+ if c.vers >= VersionTLS12 {
+ certReq.hasSignatureAndHash = true
+ certReq.signatureAndHashes = supportedClientCertSignatureAlgorithms
+ }
// An empty list of certificateAuthorities signals to
// the client that it may send any certificate in response
@@ -320,7 +337,7 @@ func (hs *serverHandshakeState) doFullHandshake() error {
hs.finishedHash.Write(helloDone.marshal())
c.writeRecord(recordTypeHandshake, helloDone.marshal())
- var pub *rsa.PublicKey // public key for client auth, if any
+ var pub crypto.PublicKey // public key for client auth, if any
msg, err := c.readHandshake()
if err != nil {
@@ -365,7 +382,7 @@ func (hs *serverHandshakeState) doFullHandshake() error {
// If we received a client cert in response to our certificate request message,
// the client will send us a certificateVerifyMsg immediately after the
- // clientKeyExchangeMsg. This message is a MD5SHA1 digest of all preceding
+ // clientKeyExchangeMsg. This message is a digest of all preceding
// handshake-layer messages that is signed using the private key corresponding
// to the client's certificate. This allows us to verify that the client is in
// possession of the private key of the certificate.
@@ -379,10 +396,25 @@ func (hs *serverHandshakeState) doFullHandshake() error {
return c.sendAlert(alertUnexpectedMessage)
}
- digest := make([]byte, 0, 36)
- digest = hs.finishedHash.serverMD5.Sum(digest)
- digest = hs.finishedHash.serverSHA1.Sum(digest)
- err = rsa.VerifyPKCS1v15(pub, crypto.MD5SHA1, digest, certVerify.signature)
+ switch key := pub.(type) {
+ case *ecdsa.PublicKey:
+ ecdsaSig := new(ecdsaSignature)
+ if _, err = asn1.Unmarshal(certVerify.signature, ecdsaSig); err != nil {
+ break
+ }
+ if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 {
+ err = errors.New("ECDSA signature contained zero or negative values")
+ break
+ }
+ digest, _, _ := hs.finishedHash.hashForClientCertificate(signatureECDSA)
+ if !ecdsa.Verify(key, digest, ecdsaSig.R, ecdsaSig.S) {
+ err = errors.New("ECDSA verification failure")
+ break
+ }
+ case *rsa.PublicKey:
+ digest, hashFunc, _ := hs.finishedHash.hashForClientCertificate(signatureRSA)
+ err = rsa.VerifyPKCS1v15(key, hashFunc, digest, certVerify.signature)
+ }
if err != nil {
c.sendAlert(alertBadCertificate)
return errors.New("could not validate signature of connection nonces: " + err.Error())
@@ -391,7 +423,7 @@ func (hs *serverHandshakeState) doFullHandshake() error {
hs.finishedHash.Write(certVerify.marshal())
}
- preMasterSecret, err := keyAgreement.processClientKeyExchange(config, cert, ckx, c.vers)
+ preMasterSecret, err := keyAgreement.processClientKeyExchange(config, hs.cert, ckx, c.vers)
if err != nil {
c.sendAlert(alertHandshakeFailure)
return err
@@ -407,12 +439,20 @@ func (hs *serverHandshakeState) establishKeys() error {
clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV :=
keysFromMasterSecret(c.vers, hs.masterSecret, hs.clientHello.random, hs.hello.random, hs.suite.macLen, hs.suite.keyLen, hs.suite.ivLen)
- clientCipher := hs.suite.cipher(clientKey, clientIV, true /* for reading */)
- clientHash := hs.suite.mac(c.vers, clientMAC)
- c.in.prepareCipherSpec(c.vers, clientCipher, clientHash)
+ var clientCipher, serverCipher interface{}
+ var clientHash, serverHash macFunction
- serverCipher := hs.suite.cipher(serverKey, serverIV, false /* not for reading */)
- serverHash := hs.suite.mac(c.vers, serverMAC)
+ if hs.suite.aead == nil {
+ clientCipher = hs.suite.cipher(clientKey, clientIV, true /* for reading */)
+ clientHash = hs.suite.mac(c.vers, clientMAC)
+ serverCipher = hs.suite.cipher(serverKey, serverIV, false /* not for reading */)
+ serverHash = hs.suite.mac(c.vers, serverMAC)
+ } else {
+ clientCipher = hs.suite.aead(clientKey, clientIV)
+ serverCipher = hs.suite.aead(serverKey, serverIV)
+ }
+
+ c.in.prepareCipherSpec(c.vers, clientCipher, clientHash)
c.out.prepareCipherSpec(c.vers, serverCipher, serverHash)
return nil
@@ -502,7 +542,7 @@ func (hs *serverHandshakeState) sendFinished() error {
// processCertsFromClient takes a chain of client certificates either from a
// Certificates message or from a sessionState and verifies them. It returns
// the public key of the leaf certificate.
-func (hs *serverHandshakeState) processCertsFromClient(certificates [][]byte) (*rsa.PublicKey, error) {
+func (hs *serverHandshakeState) processCertsFromClient(certificates [][]byte) (crypto.PublicKey, error) {
c := hs.c
hs.certsFromClient = certificates
@@ -549,8 +589,11 @@ func (hs *serverHandshakeState) processCertsFromClient(certificates [][]byte) (*
}
if len(certs) > 0 {
- pub, ok := certs[0].PublicKey.(*rsa.PublicKey)
- if !ok {
+ var pub crypto.PublicKey
+ switch key := certs[0].PublicKey.(type) {
+ case *ecdsa.PublicKey, *rsa.PublicKey:
+ pub = key
+ default:
return nil, c.sendAlert(alertUnsupportedCertificate)
}
c.peerCertificates = certs
@@ -562,7 +605,7 @@ func (hs *serverHandshakeState) processCertsFromClient(certificates [][]byte) (*
// tryCipherSuite returns a cipherSuite with the given id if that cipher suite
// is acceptable to use.
-func (c *Conn) tryCipherSuite(id uint16, supportedCipherSuites []uint16, ellipticOk bool) *cipherSuite {
+func (c *Conn) tryCipherSuite(id uint16, supportedCipherSuites []uint16, version uint16, ellipticOk, ecdsaOk bool) *cipherSuite {
for _, supported := range supportedCipherSuites {
if id == supported {
var candidate *cipherSuite
@@ -578,7 +621,13 @@ func (c *Conn) tryCipherSuite(id uint16, supportedCipherSuites []uint16, ellipti
}
// Don't select a ciphersuite which we can't
// support for this client.
- if candidate.elliptic && !ellipticOk {
+ if (candidate.flags&suiteECDHE != 0) && !ellipticOk {
+ continue
+ }
+ if (candidate.flags&suiteECDSA != 0) != ecdsaOk {
+ continue
+ }
+ if version < VersionTLS12 && candidate.flags&suiteTLS12 != 0 {
continue
}
return candidate
diff --git a/src/pkg/crypto/tls/handshake_server_test.go b/src/pkg/crypto/tls/handshake_server_test.go
index bf8cbe3ae..c08eba7f1 100644
--- a/src/pkg/crypto/tls/handshake_server_test.go
+++ b/src/pkg/crypto/tls/handshake_server_test.go
@@ -6,6 +6,8 @@ package tls
import (
"bytes"
+ "crypto/ecdsa"
+ "crypto/elliptic"
"crypto/rsa"
"crypto/x509"
"encoding/hex"
@@ -41,13 +43,15 @@ func init() {
testConfig.Time = func() time.Time { return time.Unix(0, 0) }
testConfig.Rand = zeroSource{}
testConfig.Certificates = make([]Certificate, 2)
- testConfig.Certificates[0].Certificate = [][]byte{testCertificate}
- testConfig.Certificates[0].PrivateKey = testPrivateKey
+ testConfig.Certificates[0].Certificate = [][]byte{testRSACertificate}
+ testConfig.Certificates[0].PrivateKey = testRSAPrivateKey
testConfig.Certificates[1].Certificate = [][]byte{testSNICertificate}
- testConfig.Certificates[1].PrivateKey = testPrivateKey
+ testConfig.Certificates[1].PrivateKey = testRSAPrivateKey
testConfig.BuildNameToCertificate()
testConfig.CipherSuites = []uint16{TLS_RSA_WITH_RC4_128_SHA}
testConfig.InsecureSkipVerify = true
+ testConfig.MinVersion = VersionSSL30
+ testConfig.MaxVersion = VersionTLS10
}
func testClientHelloFailure(t *testing.T, m handshakeMessage, expected error) {
@@ -100,6 +104,53 @@ func TestNoCompressionOverlap(t *testing.T) {
testClientHelloFailure(t, clientHello, alertHandshakeFailure)
}
+func TestTLS12OnlyCipherSuites(t *testing.T) {
+ // Test that a Server doesn't select a TLS 1.2-only cipher suite when
+ // the client negotiates TLS 1.1.
+ var zeros [32]byte
+
+ clientHello := &clientHelloMsg{
+ vers: VersionTLS11,
+ random: zeros[:],
+ cipherSuites: []uint16{
+ // The Server, by default, will use the client's
+ // preference order. So the GCM cipher suite
+ // will be selected unless it's excluded because
+ // of the version in this ClientHello.
+ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ TLS_RSA_WITH_RC4_128_SHA,
+ },
+ compressionMethods: []uint8{compressionNone},
+ supportedCurves: []uint16{curveP256, curveP384, curveP521},
+ supportedPoints: []uint8{pointFormatUncompressed},
+ }
+
+ c, s := net.Pipe()
+ var reply interface{}
+ var clientErr error
+ go func() {
+ cli := Client(c, testConfig)
+ cli.vers = clientHello.vers
+ cli.writeRecord(recordTypeHandshake, clientHello.marshal())
+ reply, clientErr = cli.readHandshake()
+ c.Close()
+ }()
+ config := *testConfig
+ config.CipherSuites = clientHello.cipherSuites
+ Server(s, &config).Handshake()
+ s.Close()
+ if clientErr != nil {
+ t.Fatal(clientErr)
+ }
+ serverHello, ok := reply.(*serverHelloMsg)
+ if !ok {
+ t.Fatalf("didn't get ServerHello message in reply. Got %v\n", reply)
+ }
+ if s := serverHello.cipherSuite; s != TLS_RSA_WITH_RC4_128_SHA {
+ t.Fatalf("bad cipher suite from server: %x", s)
+ }
+}
+
func TestAlertForwarding(t *testing.T) {
c, s := net.Pipe()
go func() {
@@ -110,7 +161,7 @@ func TestAlertForwarding(t *testing.T) {
err := Server(s, testConfig).Handshake()
s.Close()
if e, ok := err.(*net.OpError); !ok || e.Err != error(alertUnknownCA) {
- t.Errorf("Got error: %s; expected: %s", err, alertUnknownCA)
+ t.Errorf("Got error: %s; expected: %s", err, error(alertUnknownCA))
}
}
@@ -145,6 +196,7 @@ func TestCipherSuitePreference(t *testing.T) {
serverConfig := &Config{
CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA},
Certificates: testConfig.Certificates,
+ MaxVersion: VersionTLS11,
}
clientConfig := &Config{
CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_RC4_128_SHA},
@@ -211,22 +263,33 @@ func testServerScript(t *testing.T, name string, serverScript [][]byte, config *
}
}
-func TestHandshakeServerRC4(t *testing.T) {
- testServerScript(t, "RC4", rc4ServerScript, testConfig, nil)
+func TestHandshakeServerRSARC4(t *testing.T) {
+ testServerScript(t, "RSA-RC4", rsaRC4ServerScript, testConfig, nil)
}
-func TestHandshakeServer3DES(t *testing.T) {
+func TestHandshakeServerRSA3DES(t *testing.T) {
des3Config := new(Config)
*des3Config = *testConfig
des3Config.CipherSuites = []uint16{TLS_RSA_WITH_3DES_EDE_CBC_SHA}
- testServerScript(t, "3DES", des3ServerScript, des3Config, nil)
+ testServerScript(t, "RSA-3DES", rsaDES3ServerScript, des3Config, nil)
}
-func TestHandshakeServerAES(t *testing.T) {
+func TestHandshakeServerRSAAES(t *testing.T) {
aesConfig := new(Config)
*aesConfig = *testConfig
aesConfig.CipherSuites = []uint16{TLS_RSA_WITH_AES_128_CBC_SHA}
- testServerScript(t, "AES", aesServerScript, aesConfig, nil)
+ testServerScript(t, "RSA-AES", rsaAESServerScript, aesConfig, nil)
+}
+
+func TestHandshakeServerECDHEECDSAAES(t *testing.T) {
+ ecdsaConfig := new(Config)
+ *ecdsaConfig = *testConfig
+ ecdsaConfig.Certificates = make([]Certificate, 1)
+ ecdsaConfig.Certificates[0].Certificate = [][]byte{testECDSACertificate}
+ ecdsaConfig.Certificates[0].PrivateKey = testECDSAPrivateKey
+ ecdsaConfig.BuildNameToCertificate()
+ ecdsaConfig.CipherSuites = []uint16{TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA}
+ testServerScript(t, "ECDHE-ECDSA-AES", ecdheECDSAAESServerScript, ecdsaConfig, nil)
}
func TestHandshakeServerSSLv3(t *testing.T) {
@@ -245,6 +308,15 @@ func TestResumption(t *testing.T) {
testServerScript(t, "Resume", serverResumeTest, testConfig, nil)
}
+func TestTLS12ClientCertServer(t *testing.T) {
+ config := *testConfig
+ config.MaxVersion = VersionTLS12
+ config.ClientAuth = RequireAnyClientCert
+ config.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_RC4_128_SHA}
+
+ testServerScript(t, "TLS12", tls12ServerScript, &config, nil)
+}
+
type clientauthTest struct {
name string
clientauth ClientAuthType
@@ -252,16 +324,67 @@ type clientauthTest struct {
script [][]byte
}
-func TestClientAuth(t *testing.T) {
- for _, cat := range clientauthTests {
+func TestClientAuthRSA(t *testing.T) {
+ for _, cat := range clientauthRSATests {
+ t.Log("running", cat.name)
+ cfg := new(Config)
+ *cfg = *testConfig
+ cfg.ClientAuth = cat.clientauth
+ testServerScript(t, cat.name, cat.script, cfg, cat.peers)
+ }
+}
+
+func TestClientAuthECDSA(t *testing.T) {
+ for _, cat := range clientauthECDSATests {
t.Log("running", cat.name)
cfg := new(Config)
*cfg = *testConfig
+ cfg.Certificates = make([]Certificate, 1)
+ cfg.Certificates[0].Certificate = [][]byte{testECDSACertificate}
+ cfg.Certificates[0].PrivateKey = testECDSAPrivateKey
+ cfg.BuildNameToCertificate()
+ cfg.CipherSuites = []uint16{TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA}
cfg.ClientAuth = cat.clientauth
testServerScript(t, cat.name, cat.script, cfg, cat.peers)
}
}
+// TestCipherSuiteCertPreferance ensures that we select an RSA ciphersuite with
+// an RSA certificate and an ECDSA ciphersuite with an ECDSA certificate.
+func TestCipherSuiteCertPreferance(t *testing.T) {
+ var config = *testConfig
+ config.CipherSuites = []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA}
+ config.MaxVersion = VersionTLS11
+ config.PreferServerCipherSuites = true
+ testServerScript(t, "CipherSuiteCertPreference", tls11ECDHEAESServerScript, &config, nil)
+
+ config = *testConfig
+ config.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA}
+ config.Certificates = []Certificate{
+ Certificate{
+ Certificate: [][]byte{testECDSACertificate},
+ PrivateKey: testECDSAPrivateKey,
+ },
+ }
+ config.BuildNameToCertificate()
+ config.PreferServerCipherSuites = true
+ testServerScript(t, "CipherSuiteCertPreference2", ecdheECDSAAESServerScript, &config, nil)
+}
+
+func TestTLS11Server(t *testing.T) {
+ var config = *testConfig
+ config.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA}
+ config.MaxVersion = VersionTLS11
+ testServerScript(t, "TLS11", tls11ECDHEAESServerScript, &config, nil)
+}
+
+func TestAESGCM(t *testing.T) {
+ var config = *testConfig
+ config.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}
+ config.MaxVersion = VersionTLS12
+ testServerScript(t, "AES-GCM", aesGCMServerScript, &config, nil)
+}
+
// recordingConn is a net.Conn that records the traffic that passes through it.
// WriteTo can be used to produce Go code that contains the recorded traffic.
type recordingConn struct {
@@ -331,10 +454,28 @@ var serve = flag.Bool("serve", false, "run a TLS server on :10443")
var testCipherSuites = flag.String("ciphersuites",
"0x"+strconv.FormatInt(int64(TLS_RSA_WITH_RC4_128_SHA), 16),
"cipher suites to accept in serving mode")
+var testMinVersion = flag.String("minversion",
+ "0x"+strconv.FormatInt(int64(VersionSSL30), 16),
+ "minimum version to negotiate")
+var testMaxVersion = flag.String("maxversion",
+ "0x"+strconv.FormatInt(int64(VersionTLS10), 16),
+ "maximum version to negotiate")
var testClientAuth = flag.Int("clientauth", 0, "value for tls.Config.ClientAuth")
func GetTestConfig() *Config {
var config = *testConfig
+
+ minVersion, err := strconv.ParseUint(*testMinVersion, 0, 64)
+ if err != nil {
+ panic(err)
+ }
+ config.MinVersion = uint16(minVersion)
+ maxVersion, err := strconv.ParseUint(*testMaxVersion, 0, 64)
+ if err != nil {
+ panic(err)
+ }
+ config.MaxVersion = uint16(maxVersion)
+
suites := strings.Split(*testCipherSuites, ",")
config.CipherSuites = make([]uint16, len(suites))
for i := range suites {
@@ -345,6 +486,25 @@ func GetTestConfig() *Config {
config.CipherSuites[i] = uint16(suite)
}
+ ecdsa := false
+ for _, suite := range config.CipherSuites {
+ switch suite {
+ case TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
+ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
+ TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+ ecdsa = true
+ }
+ }
+ if ecdsa {
+ config.Certificates = nil
+ if !*connect {
+ config.Certificates = make([]Certificate, 1)
+ config.Certificates[0].Certificate = [][]byte{testECDSACertificate}
+ config.Certificates[0].PrivateKey = testECDSAPrivateKey
+ }
+ config.BuildNameToCertificate()
+ }
+
config.ClientAuth = ClientAuthType(*testClientAuth)
return &config
}
@@ -403,11 +563,13 @@ func fromHex(s string) []byte {
return b
}
-var testCertificate = fromHex("308202b030820219a00302010202090085b0bba48a7fb8ca300d06092a864886f70d01010505003045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c7464301e170d3130303432343039303933385a170d3131303432343039303933385a3045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c746430819f300d06092a864886f70d010101050003818d0030818902818100bb79d6f517b5e5bf4610d0dc69bee62b07435ad0032d8a7a4385b71452e7a5654c2c78b8238cb5b482e5de1f953b7e62a52ca533d6fe125c7a56fcf506bffa587b263fb5cd04d3d0c921964ac7f4549f5abfef427100fe1899077f7e887d7df10439c4a22edb51c97ce3c04c3b326601cfafb11db8719a1ddbdb896baeda2d790203010001a381a73081a4301d0603551d0e04160414b1ade2855acfcb28db69ce2369ded3268e18883930750603551d23046e306c8014b1ade2855acfcb28db69ce2369ded3268e188839a149a4473045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c746482090085b0bba48a7fb8ca300c0603551d13040530030101ff300d06092a864886f70d010105050003818100086c4524c76bb159ab0c52ccf2b014d7879d7a6475b55a9566e4c52b8eae12661feb4f38b36e60d392fdf74108b52513b1187a24fb301dbaed98b917ece7d73159db95d31d78ea50565cd5825a2d5a5f33c4b6d8c97590968c0f5298b5cd981f89205ff2a01ca31b9694dda9fd57e970e8266d71999b266e3850296c90a7bdd9")
+var testRSACertificate = fromHex("308202b030820219a00302010202090085b0bba48a7fb8ca300d06092a864886f70d01010505003045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c7464301e170d3130303432343039303933385a170d3131303432343039303933385a3045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c746430819f300d06092a864886f70d010101050003818d0030818902818100bb79d6f517b5e5bf4610d0dc69bee62b07435ad0032d8a7a4385b71452e7a5654c2c78b8238cb5b482e5de1f953b7e62a52ca533d6fe125c7a56fcf506bffa587b263fb5cd04d3d0c921964ac7f4549f5abfef427100fe1899077f7e887d7df10439c4a22edb51c97ce3c04c3b326601cfafb11db8719a1ddbdb896baeda2d790203010001a381a73081a4301d0603551d0e04160414b1ade2855acfcb28db69ce2369ded3268e18883930750603551d23046e306c8014b1ade2855acfcb28db69ce2369ded3268e188839a149a4473045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c746482090085b0bba48a7fb8ca300c0603551d13040530030101ff300d06092a864886f70d010105050003818100086c4524c76bb159ab0c52ccf2b014d7879d7a6475b55a9566e4c52b8eae12661feb4f38b36e60d392fdf74108b52513b1187a24fb301dbaed98b917ece7d73159db95d31d78ea50565cd5825a2d5a5f33c4b6d8c97590968c0f5298b5cd981f89205ff2a01ca31b9694dda9fd57e970e8266d71999b266e3850296c90a7bdd9")
+
+var testECDSACertificate = fromHex("3082020030820162020900b8bf2d47a0d2ebf4300906072a8648ce3d04013045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c7464301e170d3132313132323135303633325a170d3232313132303135303633325a3045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c746430819b301006072a8648ce3d020106052b81040023038186000400c4a1edbe98f90b4873367ec316561122f23d53c33b4d213dcd6b75e6f6b0dc9adf26c1bcb287f072327cb3642f1c90bcea6823107efee325c0483a69e0286dd33700ef0462dd0da09c706283d881d36431aa9e9731bd96b068c09b23de76643f1a5c7fe9120e5858b65f70dd9bd8ead5d7f5d5ccb9b69f30665b669a20e227e5bffe3b300906072a8648ce3d040103818c0030818802420188a24febe245c5487d1bacf5ed989dae4770c05e1bb62fbdf1b64db76140d311a2ceee0b7e927eff769dc33b7ea53fcefa10e259ec472d7cacda4e970e15a06fd00242014dfcbe67139c2d050ebd3fa38c25c13313830d9406bbd4377af6ec7ac9862eddd711697f857c56defb31782be4c7780daecbbe9e4e3624317b6a0f399512078f2a")
var testSNICertificate = fromHex("308201f23082015da003020102020100300b06092a864886f70d01010530283110300e060355040a130741636d6520436f311430120603550403130b736e69746573742e636f6d301e170d3132303431313137343033355a170d3133303431313137343533355a30283110300e060355040a130741636d6520436f311430120603550403130b736e69746573742e636f6d30819d300b06092a864886f70d01010103818d0030818902818100bb79d6f517b5e5bf4610d0dc69bee62b07435ad0032d8a7a4385b71452e7a5654c2c78b8238cb5b482e5de1f953b7e62a52ca533d6fe125c7a56fcf506bffa587b263fb5cd04d3d0c921964ac7f4549f5abfef427100fe1899077f7e887d7df10439c4a22edb51c97ce3c04c3b326601cfafb11db8719a1ddbdb896baeda2d790203010001a3323030300e0603551d0f0101ff0404030200a0300d0603551d0e0406040401020304300f0603551d2304083006800401020304300b06092a864886f70d0101050381810089c6455f1c1f5ef8eb1ab174ee2439059f5c4259bb1a8d86cdb1d056f56a717da40e95ab90f59e8deaf627c157995094db0802266eb34fc6842dea8a4b68d9c1389103ab84fb9e1f85d9b5d23ff2312c8670fbb540148245a4ebafe264d90c8a4cf4f85b0fac12ac2fc4a3154bad52462868af96c62c6525d652b6e31845bdcc")
-var testPrivateKey = &rsa.PrivateKey{
+var testRSAPrivateKey = &rsa.PrivateKey{
PublicKey: rsa.PublicKey{
N: bigFromString("131650079503776001033793877885499001334664249354723305978524647182322416328664556247316495448366990052837680518067798333412266673813370895702118944398081598789828837447552603077848001020611640547221687072142537202428102790818451901395596882588063427854225330436740647715202971973145151161964464812406232198521"),
E: 65537,
@@ -419,6 +581,22 @@ var testPrivateKey = &rsa.PrivateKey{
},
}
+var testECDSAPrivateKey = &ecdsa.PrivateKey{
+ PublicKey: ecdsa.PublicKey{
+ Curve: &elliptic.CurveParams{
+ P: bigFromString("6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151"),
+ N: bigFromString("6864797660130609714981900799081393217269435300143305409394463459185543183397655394245057746333217197532963996371363321113864768612440380340372808892707005449"),
+ B: bigFromString("1093849038073734274511112390766805569936207598951683748994586394495953116150735016013708737573759623248592132296706313309438452531591012912142327488478985984"),
+ Gx: bigFromString("2661740802050217063228768716723360960729859168756973147706671368418802944996427808491545080627771902352094241225065558662157113545570916814161637315895999846"),
+ Gy: bigFromString("3757180025770020463545507224491183603594455134769762486694567779615544477440556316691234405012945539562144444537289428522585666729196580810124344277578376784"),
+ BitSize: 521,
+ },
+ X: bigFromString("2636411247892461147287360222306590634450676461695221912739908880441342231985950069527906976759812296359387337367668045707086543273113073382714101597903639351"),
+ Y: bigFromString("3204695818431246682253994090650952614555094516658732116404513121125038617915183037601737180082382202488628239201196033284060130040574800684774115478859677243"),
+ },
+ D: bigFromString("5477294338614160138026852784385529180817726002953041720191098180813046231640184669647735805135001309477695746518160084669446643325196003346204701381388769751"),
+}
+
func loadPEMCert(in string) *x509.Certificate {
block, _ := pem.Decode([]byte(in))
if block.Type == "CERTIFICATE" && len(block.Headers) == 0 {
@@ -435,7 +613,7 @@ func loadPEMCert(in string) *x509.Certificate {
// The values for this test are obtained by building and running in server mode:
// % go test -test.run "TestRunServer" -serve
// The recorded bytes are written to stdout.
-var rc4ServerScript = [][]byte{
+var rsaRC4ServerScript = [][]byte{
{
0x16, 0x03, 0x01, 0x00, 0x54, 0x01, 0x00, 0x00,
0x50, 0x03, 0x01, 0x50, 0x77, 0x3d, 0xbd, 0x32,
@@ -592,7 +770,7 @@ var rc4ServerScript = [][]byte{
},
}
-var des3ServerScript = [][]byte{
+var rsaDES3ServerScript = [][]byte{
{
0x16, 0x03, 0x00, 0x00, 0xc5, 0x01, 0x00, 0x00,
0xc1, 0x03, 0x03, 0x50, 0xae, 0x5d, 0x38, 0xec,
@@ -801,7 +979,7 @@ var des3ServerScript = [][]byte{
},
}
-var aesServerScript = [][]byte{
+var rsaAESServerScript = [][]byte{
{
0x16, 0x03, 0x00, 0x00, 0xc5, 0x01, 0x00, 0x00,
0xc1, 0x03, 0x03, 0x50, 0xae, 0x5c, 0xe9, 0x5e,
@@ -1027,6 +1205,216 @@ var aesServerScript = [][]byte{
},
}
+// Generated using:
+// $ go test -test.run TestRunServer -serve -ciphersuites=0xc00a
+// $ openssl s_client -host 127.0.0.1 -port 10443 -cipher ECDHE-ECDSA-AES256-SHA
+var ecdheECDSAAESServerScript = [][]byte{
+ {
+ 0x16, 0x03, 0x01, 0x00, 0xa0, 0x01, 0x00, 0x00,
+ 0x9c, 0x03, 0x03, 0x50, 0xd7, 0x18, 0x31, 0x49,
+ 0xde, 0x19, 0x8d, 0x08, 0x5c, 0x4b, 0x60, 0x67,
+ 0x0f, 0xfe, 0xd0, 0x62, 0xf9, 0x31, 0x48, 0x17,
+ 0x9e, 0x50, 0xc1, 0xd8, 0x35, 0x24, 0x0e, 0xa6,
+ 0x09, 0x06, 0x51, 0x00, 0x00, 0x04, 0xc0, 0x0a,
+ 0x00, 0xff, 0x01, 0x00, 0x00, 0x6f, 0x00, 0x0b,
+ 0x00, 0x04, 0x03, 0x00, 0x01, 0x02, 0x00, 0x0a,
+ 0x00, 0x34, 0x00, 0x32, 0x00, 0x0e, 0x00, 0x0d,
+ 0x00, 0x19, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x18,
+ 0x00, 0x09, 0x00, 0x0a, 0x00, 0x16, 0x00, 0x17,
+ 0x00, 0x08, 0x00, 0x06, 0x00, 0x07, 0x00, 0x14,
+ 0x00, 0x15, 0x00, 0x04, 0x00, 0x05, 0x00, 0x12,
+ 0x00, 0x13, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03,
+ 0x00, 0x0f, 0x00, 0x10, 0x00, 0x11, 0x00, 0x23,
+ 0x00, 0x00, 0x00, 0x0d, 0x00, 0x22, 0x00, 0x20,
+ 0x06, 0x01, 0x06, 0x02, 0x06, 0x03, 0x05, 0x01,
+ 0x05, 0x02, 0x05, 0x03, 0x04, 0x01, 0x04, 0x02,
+ 0x04, 0x03, 0x03, 0x01, 0x03, 0x02, 0x03, 0x03,
+ 0x02, 0x01, 0x02, 0x02, 0x02, 0x03, 0x01, 0x01,
+ 0x00, 0x0f, 0x00, 0x01, 0x01,
+ },
+ {
+ 0x16, 0x03, 0x01, 0x00, 0x30, 0x02, 0x00, 0x00,
+ 0x2c, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xc0, 0x0a, 0x00, 0x00,
+ 0x04, 0x00, 0x23, 0x00, 0x00, 0x16, 0x03, 0x01,
+ 0x02, 0x0e, 0x0b, 0x00, 0x02, 0x0a, 0x00, 0x02,
+ 0x07, 0x00, 0x02, 0x04, 0x30, 0x82, 0x02, 0x00,
+ 0x30, 0x82, 0x01, 0x62, 0x02, 0x09, 0x00, 0xb8,
+ 0xbf, 0x2d, 0x47, 0xa0, 0xd2, 0xeb, 0xf4, 0x30,
+ 0x09, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d,
+ 0x04, 0x01, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41,
+ 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55,
+ 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65,
+ 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21,
+ 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
+ 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74,
+ 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74,
+ 0x64, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x32, 0x31,
+ 0x31, 0x32, 0x32, 0x31, 0x35, 0x30, 0x36, 0x33,
+ 0x32, 0x5a, 0x17, 0x0d, 0x32, 0x32, 0x31, 0x31,
+ 0x32, 0x30, 0x31, 0x35, 0x30, 0x36, 0x33, 0x32,
+ 0x5a, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, 0x55,
+ 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04,
+ 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d,
+ 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21, 0x30,
+ 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18,
+ 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74,
+ 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, 0x73,
+ 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64,
+ 0x30, 0x81, 0x9b, 0x30, 0x10, 0x06, 0x07, 0x2a,
+ 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x05,
+ 0x2b, 0x81, 0x04, 0x00, 0x23, 0x03, 0x81, 0x86,
+ 0x00, 0x04, 0x00, 0xc4, 0xa1, 0xed, 0xbe, 0x98,
+ 0xf9, 0x0b, 0x48, 0x73, 0x36, 0x7e, 0xc3, 0x16,
+ 0x56, 0x11, 0x22, 0xf2, 0x3d, 0x53, 0xc3, 0x3b,
+ 0x4d, 0x21, 0x3d, 0xcd, 0x6b, 0x75, 0xe6, 0xf6,
+ 0xb0, 0xdc, 0x9a, 0xdf, 0x26, 0xc1, 0xbc, 0xb2,
+ 0x87, 0xf0, 0x72, 0x32, 0x7c, 0xb3, 0x64, 0x2f,
+ 0x1c, 0x90, 0xbc, 0xea, 0x68, 0x23, 0x10, 0x7e,
+ 0xfe, 0xe3, 0x25, 0xc0, 0x48, 0x3a, 0x69, 0xe0,
+ 0x28, 0x6d, 0xd3, 0x37, 0x00, 0xef, 0x04, 0x62,
+ 0xdd, 0x0d, 0xa0, 0x9c, 0x70, 0x62, 0x83, 0xd8,
+ 0x81, 0xd3, 0x64, 0x31, 0xaa, 0x9e, 0x97, 0x31,
+ 0xbd, 0x96, 0xb0, 0x68, 0xc0, 0x9b, 0x23, 0xde,
+ 0x76, 0x64, 0x3f, 0x1a, 0x5c, 0x7f, 0xe9, 0x12,
+ 0x0e, 0x58, 0x58, 0xb6, 0x5f, 0x70, 0xdd, 0x9b,
+ 0xd8, 0xea, 0xd5, 0xd7, 0xf5, 0xd5, 0xcc, 0xb9,
+ 0xb6, 0x9f, 0x30, 0x66, 0x5b, 0x66, 0x9a, 0x20,
+ 0xe2, 0x27, 0xe5, 0xbf, 0xfe, 0x3b, 0x30, 0x09,
+ 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04,
+ 0x01, 0x03, 0x81, 0x8c, 0x00, 0x30, 0x81, 0x88,
+ 0x02, 0x42, 0x01, 0x88, 0xa2, 0x4f, 0xeb, 0xe2,
+ 0x45, 0xc5, 0x48, 0x7d, 0x1b, 0xac, 0xf5, 0xed,
+ 0x98, 0x9d, 0xae, 0x47, 0x70, 0xc0, 0x5e, 0x1b,
+ 0xb6, 0x2f, 0xbd, 0xf1, 0xb6, 0x4d, 0xb7, 0x61,
+ 0x40, 0xd3, 0x11, 0xa2, 0xce, 0xee, 0x0b, 0x7e,
+ 0x92, 0x7e, 0xff, 0x76, 0x9d, 0xc3, 0x3b, 0x7e,
+ 0xa5, 0x3f, 0xce, 0xfa, 0x10, 0xe2, 0x59, 0xec,
+ 0x47, 0x2d, 0x7c, 0xac, 0xda, 0x4e, 0x97, 0x0e,
+ 0x15, 0xa0, 0x6f, 0xd0, 0x02, 0x42, 0x01, 0x4d,
+ 0xfc, 0xbe, 0x67, 0x13, 0x9c, 0x2d, 0x05, 0x0e,
+ 0xbd, 0x3f, 0xa3, 0x8c, 0x25, 0xc1, 0x33, 0x13,
+ 0x83, 0x0d, 0x94, 0x06, 0xbb, 0xd4, 0x37, 0x7a,
+ 0xf6, 0xec, 0x7a, 0xc9, 0x86, 0x2e, 0xdd, 0xd7,
+ 0x11, 0x69, 0x7f, 0x85, 0x7c, 0x56, 0xde, 0xfb,
+ 0x31, 0x78, 0x2b, 0xe4, 0xc7, 0x78, 0x0d, 0xae,
+ 0xcb, 0xbe, 0x9e, 0x4e, 0x36, 0x24, 0x31, 0x7b,
+ 0x6a, 0x0f, 0x39, 0x95, 0x12, 0x07, 0x8f, 0x2a,
+ 0x16, 0x03, 0x01, 0x01, 0x1a, 0x0c, 0x00, 0x01,
+ 0x16, 0x03, 0x00, 0x19, 0x85, 0x04, 0x01, 0x39,
+ 0xdc, 0xee, 0x44, 0x17, 0x5e, 0xdb, 0xd7, 0x27,
+ 0xaf, 0xb6, 0x56, 0xd9, 0xb4, 0x43, 0x5a, 0x99,
+ 0xcf, 0xaa, 0x31, 0x37, 0x0c, 0x6f, 0x3a, 0xa0,
+ 0xf8, 0x53, 0xc4, 0x74, 0xd1, 0x91, 0x0a, 0x46,
+ 0xf5, 0x38, 0x3b, 0x5c, 0x09, 0xd8, 0x97, 0xdc,
+ 0x4b, 0xaa, 0x70, 0x26, 0x48, 0xf2, 0xd6, 0x0b,
+ 0x31, 0xc9, 0xf8, 0xd4, 0x98, 0x43, 0xe1, 0x6c,
+ 0xd5, 0xc7, 0xb2, 0x8e, 0x0b, 0x01, 0xe6, 0xb6,
+ 0x00, 0x28, 0x80, 0x7b, 0xfc, 0x96, 0x8f, 0x0d,
+ 0xa2, 0x4f, 0xb0, 0x79, 0xaf, 0xdc, 0x61, 0x28,
+ 0x63, 0x33, 0x78, 0xf6, 0x31, 0x39, 0xfd, 0x8a,
+ 0xf4, 0x15, 0x18, 0x11, 0xfe, 0xdb, 0xd5, 0x07,
+ 0xda, 0x2c, 0xed, 0x49, 0xa0, 0x23, 0xbf, 0xd0,
+ 0x3a, 0x38, 0x1d, 0x54, 0xae, 0x1c, 0x7b, 0xea,
+ 0x29, 0xee, 0xd0, 0x38, 0xc1, 0x76, 0xa7, 0x7f,
+ 0x2a, 0xf4, 0xce, 0x1e, 0xac, 0xcc, 0x94, 0x79,
+ 0x90, 0x33, 0x00, 0x8b, 0x30, 0x81, 0x88, 0x02,
+ 0x42, 0x00, 0xc6, 0x85, 0x8e, 0x06, 0xb7, 0x04,
+ 0x04, 0xe9, 0xcd, 0x9e, 0x3e, 0xcb, 0x66, 0x23,
+ 0x95, 0xb4, 0x42, 0x9c, 0x64, 0x81, 0x39, 0x05,
+ 0x3f, 0xb5, 0x21, 0xf8, 0x28, 0xaf, 0x60, 0x6b,
+ 0x4d, 0x3d, 0xba, 0xa1, 0x4b, 0x5e, 0x77, 0xef,
+ 0xe7, 0x59, 0x28, 0xfe, 0x1d, 0xc1, 0x27, 0xa2,
+ 0xff, 0xa8, 0xde, 0x33, 0x48, 0xb3, 0xc1, 0x85,
+ 0x6a, 0x42, 0x9b, 0xf9, 0x7e, 0x7e, 0x31, 0xc2,
+ 0xe5, 0xbd, 0x66, 0x02, 0x42, 0x00, 0xad, 0x7d,
+ 0x06, 0x35, 0xab, 0xec, 0x8d, 0xac, 0xd4, 0xba,
+ 0x1b, 0x49, 0x5e, 0x05, 0x5f, 0xf0, 0x97, 0x93,
+ 0x82, 0xb8, 0x2b, 0x8d, 0x91, 0x98, 0x63, 0x8e,
+ 0xb4, 0x14, 0x62, 0xdb, 0x1e, 0xc9, 0x2b, 0x30,
+ 0xf8, 0x41, 0x9b, 0xa6, 0xe6, 0xbc, 0xde, 0x0e,
+ 0x68, 0x30, 0x22, 0x50, 0xe6, 0x98, 0x97, 0x7b,
+ 0x69, 0xf7, 0x93, 0xed, 0xcd, 0x19, 0x2f, 0x44,
+ 0x6c, 0x2e, 0xdf, 0x25, 0xee, 0xcc, 0x46, 0x16,
+ 0x03, 0x01, 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x16, 0x03, 0x01, 0x00, 0x8a, 0x10, 0x00, 0x00,
+ 0x86, 0x85, 0x04, 0x00, 0x1c, 0xc5, 0xe8, 0xb3,
+ 0x42, 0xb4, 0xad, 0xca, 0x45, 0xcd, 0x42, 0x7b,
+ 0xfb, 0x0c, 0xea, 0x32, 0x26, 0xd4, 0x8a, 0xef,
+ 0xdf, 0xc9, 0xff, 0xd2, 0xe0, 0x36, 0xea, 0x4e,
+ 0xbb, 0x3e, 0xf4, 0x9c, 0x76, 0x4f, 0x44, 0xbd,
+ 0x84, 0x72, 0xdd, 0xcb, 0xe5, 0x28, 0x8d, 0x31,
+ 0x72, 0x3b, 0xd3, 0xf2, 0x9a, 0x13, 0xfb, 0x8a,
+ 0xa7, 0x72, 0xca, 0x21, 0x6c, 0xea, 0xbf, 0xe9,
+ 0x8c, 0x0a, 0xcc, 0x8f, 0xd6, 0x00, 0x20, 0x87,
+ 0xf3, 0x7d, 0x18, 0xc5, 0xfd, 0x9e, 0xdd, 0x6b,
+ 0x06, 0xdc, 0x52, 0xeb, 0x14, 0xc0, 0x67, 0x5a,
+ 0x06, 0xd8, 0x98, 0x19, 0x14, 0xe7, 0xd4, 0x36,
+ 0x32, 0xee, 0xb7, 0xfa, 0xe2, 0x85, 0x4a, 0x16,
+ 0x42, 0x0c, 0xa6, 0x21, 0xcf, 0x1f, 0xae, 0x10,
+ 0x8b, 0x28, 0x32, 0x19, 0xa4, 0x0a, 0xd7, 0xce,
+ 0xe6, 0xe1, 0x93, 0xfb, 0x5f, 0x08, 0x8b, 0x42,
+ 0xa2, 0x20, 0xed, 0x0d, 0x62, 0xca, 0xed, 0x14,
+ 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x01,
+ 0x00, 0x30, 0x2e, 0x33, 0xc0, 0x57, 0x6c, 0xb4,
+ 0x1b, 0xd2, 0x63, 0xe8, 0x67, 0x10, 0x2d, 0x87,
+ 0x71, 0x6e, 0x19, 0x60, 0xf4, 0xa4, 0x10, 0x52,
+ 0x73, 0x2d, 0x09, 0x5e, 0xdb, 0x6c, 0xdc, 0xcf,
+ 0x2d, 0xff, 0x03, 0x11, 0x95, 0x76, 0x90, 0xd7,
+ 0x87, 0x54, 0x43, 0xed, 0xc2, 0x36, 0x69, 0x14,
+ 0x72, 0x4a,
+ },
+ {
+ 0x16, 0x03, 0x01, 0x00, 0x72, 0x04, 0x00, 0x00,
+ 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65,
+ 0xe8, 0x8b, 0xde, 0xef, 0xba, 0xc5, 0x7e, 0x04,
+ 0xab, 0xfd, 0x79, 0x56, 0xf3, 0xe1, 0xa5, 0x3e,
+ 0x02, 0xdf, 0x69, 0x6d, 0x1f, 0x41, 0x9f, 0xbc,
+ 0x93, 0xe2, 0x6c, 0xf1, 0xb1, 0x38, 0xf5, 0x2b,
+ 0x8c, 0x4c, 0xf4, 0x74, 0xe1, 0x79, 0x35, 0x34,
+ 0x97, 0x9b, 0xd5, 0xba, 0xfd, 0xf7, 0x2f, 0x2d,
+ 0x9e, 0x84, 0x54, 0xee, 0x77, 0x59, 0x23, 0x8f,
+ 0xc8, 0x84, 0xb4, 0xd6, 0xea, 0x4c, 0x44, 0x8a,
+ 0xc6, 0x9c, 0xf9, 0x9b, 0x27, 0xea, 0x4f, 0x28,
+ 0x72, 0x33, 0x12, 0x20, 0x7c, 0xd7, 0x3f, 0x56,
+ 0xa6, 0x76, 0xc7, 0x48, 0xe4, 0x2d, 0x6f, 0x14,
+ 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x01,
+ 0x00, 0x30, 0x36, 0xe3, 0xd4, 0xf7, 0xb1, 0x69,
+ 0x18, 0x8d, 0x09, 0xba, 0x52, 0x1e, 0xd5, 0x7d,
+ 0x2c, 0x15, 0x3a, 0xd6, 0xe3, 0x99, 0x30, 0x2c,
+ 0x99, 0x97, 0xbc, 0x19, 0x3c, 0x63, 0xa1, 0x25,
+ 0x68, 0xbc, 0x8a, 0x16, 0x47, 0xec, 0xae, 0x13,
+ 0xa4, 0x03, 0x96, 0x29, 0x11, 0x92, 0x90, 0x1a,
+ 0xc8, 0xa4, 0x17, 0x03, 0x01, 0x00, 0x20, 0xc1,
+ 0x10, 0x1d, 0xa6, 0xf1, 0xe2, 0x8a, 0xcc, 0x37,
+ 0x7d, 0x8e, 0x05, 0x00, 0xfb, 0xd1, 0x9f, 0xc7,
+ 0x11, 0xd2, 0x00, 0xb4, 0x27, 0x0a, 0x25, 0x14,
+ 0xd9, 0x79, 0x1b, 0xcb, 0x4d, 0x81, 0x61, 0x17,
+ 0x03, 0x01, 0x00, 0x30, 0x5c, 0x7c, 0x2d, 0xc0,
+ 0x9e, 0xa6, 0xc4, 0x8e, 0xfd, 0xf4, 0xe2, 0xe5,
+ 0xe4, 0xe6, 0x56, 0x9f, 0x7d, 0x4c, 0x4c, 0x2d,
+ 0xb7, 0xa9, 0xac, 0xfa, 0x9f, 0x12, 0x7f, 0x2d,
+ 0x30, 0x57, 0xe4, 0x8e, 0x30, 0x86, 0x65, 0x59,
+ 0xcd, 0x24, 0xda, 0xe2, 0x8a, 0x7b, 0x0c, 0x5e,
+ 0x86, 0x05, 0x06, 0x2a, 0x15, 0x03, 0x01, 0x00,
+ 0x20, 0xd6, 0xb7, 0x70, 0xf8, 0x47, 0xbc, 0x0f,
+ 0xf4, 0x66, 0x98, 0x1b, 0x1e, 0x8a, 0x8c, 0x0b,
+ 0xa1, 0x4a, 0x04, 0x29, 0x60, 0x72, 0x8b, 0xc4,
+ 0x73, 0xc1, 0xd6, 0x41, 0x72, 0xb7, 0x17, 0x39,
+ 0xda,
+ },
+}
+
var sslv3ServerScript = [][]byte{
{
0x16, 0x03, 0x00, 0x00, 0x54, 0x01, 0x00, 0x00,
@@ -1559,38 +1947,115 @@ var serverResumeTest = [][]byte{
},
}
-var clientauthTests = []clientauthTest{
+var clientauthRSATests = []clientauthTest{
// Server asks for cert with empty CA list, client doesn't give it.
// go test -run "TestRunServer" -serve -clientauth 1
{"RequestClientCert, none given", RequestClientCert, nil, [][]byte{
{
- 0x16, 0x03, 0x01, 0x00, 0x54, 0x01, 0x00, 0x00,
- 0x50, 0x03, 0x01, 0x50, 0x77, 0x43, 0x9e, 0x31,
- 0xe6, 0x36, 0x5e, 0x5e, 0x24, 0xe4, 0x0d, 0x26,
- 0x34, 0xa7, 0x1c, 0x2e, 0x59, 0x6d, 0xa5, 0x3e,
- 0x72, 0xf3, 0xa3, 0x1c, 0xbc, 0xb3, 0x27, 0xaf,
- 0x92, 0x5b, 0x7d, 0x00, 0x00, 0x28, 0x00, 0x39,
- 0x00, 0x38, 0x00, 0x35, 0x00, 0x16, 0x00, 0x13,
- 0x00, 0x0a, 0x00, 0x33, 0x00, 0x32, 0x00, 0x2f,
- 0x00, 0x05, 0x00, 0x04, 0x00, 0x15, 0x00, 0x12,
- 0x00, 0x09, 0x00, 0x14, 0x00, 0x11, 0x00, 0x08,
- 0x00, 0x06, 0x00, 0x03, 0x00, 0xff, 0x02, 0x01,
- 0x00,
+ 0x16, 0x03, 0x01, 0x01, 0x1e, 0x01, 0x00, 0x01,
+ 0x1a, 0x03, 0x03, 0x51, 0xe5, 0x6c, 0xb5, 0x5a,
+ 0xc2, 0xf5, 0xf0, 0x92, 0x94, 0x8a, 0x64, 0x18,
+ 0xa4, 0x2b, 0x82, 0x07, 0xbc, 0xd9, 0xd9, 0xf9,
+ 0x7b, 0xd2, 0xd0, 0xee, 0xa2, 0x70, 0x4e, 0x23,
+ 0x88, 0x7c, 0x95, 0x00, 0x00, 0x82, 0xc0, 0x30,
+ 0xc0, 0x2c, 0xc0, 0x28, 0xc0, 0x24, 0xc0, 0x14,
+ 0xc0, 0x0a, 0x00, 0xa3, 0x00, 0x9f, 0x00, 0x6b,
+ 0x00, 0x6a, 0x00, 0x39, 0x00, 0x38, 0xc0, 0x32,
+ 0xc0, 0x2e, 0xc0, 0x2a, 0xc0, 0x26, 0xc0, 0x0f,
+ 0xc0, 0x05, 0x00, 0x9d, 0x00, 0x3d, 0x00, 0x35,
+ 0xc0, 0x12, 0xc0, 0x08, 0x00, 0x16, 0x00, 0x13,
+ 0xc0, 0x0d, 0xc0, 0x03, 0x00, 0x0a, 0xc0, 0x2f,
+ 0xc0, 0x2b, 0xc0, 0x27, 0xc0, 0x23, 0xc0, 0x13,
+ 0xc0, 0x09, 0x00, 0xa2, 0x00, 0x9e, 0x00, 0x67,
+ 0x00, 0x40, 0x00, 0x33, 0x00, 0x32, 0xc0, 0x31,
+ 0xc0, 0x2d, 0xc0, 0x29, 0xc0, 0x25, 0xc0, 0x0e,
+ 0xc0, 0x04, 0x00, 0x9c, 0x00, 0x3c, 0x00, 0x2f,
+ 0x00, 0x07, 0xc0, 0x11, 0xc0, 0x07, 0xc0, 0x0c,
+ 0xc0, 0x02, 0x00, 0x05, 0x00, 0x04, 0x00, 0x15,
+ 0x00, 0x12, 0x00, 0x09, 0x00, 0x14, 0x00, 0x11,
+ 0x00, 0x08, 0x00, 0x06, 0x00, 0x03, 0x00, 0xff,
+ 0x01, 0x00, 0x00, 0x6f, 0x00, 0x0b, 0x00, 0x04,
+ 0x03, 0x00, 0x01, 0x02, 0x00, 0x0a, 0x00, 0x34,
+ 0x00, 0x32, 0x00, 0x0e, 0x00, 0x0d, 0x00, 0x19,
+ 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x18, 0x00, 0x09,
+ 0x00, 0x0a, 0x00, 0x16, 0x00, 0x17, 0x00, 0x08,
+ 0x00, 0x06, 0x00, 0x07, 0x00, 0x14, 0x00, 0x15,
+ 0x00, 0x04, 0x00, 0x05, 0x00, 0x12, 0x00, 0x13,
+ 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x0f,
+ 0x00, 0x10, 0x00, 0x11, 0x00, 0x23, 0x00, 0x00,
+ 0x00, 0x0d, 0x00, 0x22, 0x00, 0x20, 0x06, 0x01,
+ 0x06, 0x02, 0x06, 0x03, 0x05, 0x01, 0x05, 0x02,
+ 0x05, 0x03, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03,
+ 0x03, 0x01, 0x03, 0x02, 0x03, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x02, 0x03, 0x01, 0x01, 0x00, 0x0f,
+ 0x00, 0x01, 0x01,
},
{
- 0x16, 0x03, 0x01, 0x00, 0x2a, 0x02, 0x00, 0x00,
- 0x26, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x16, 0x03, 0x01, 0x00, 0x30, 0x02, 0x00, 0x00,
+ 0x2c, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x16,
- 0x03, 0x01, 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba,
- 0x00, 0x02, 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82,
- 0x02, 0xb0, 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03,
- 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0,
- 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d,
- 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
- 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, 0x31,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00,
+ 0x04, 0x00, 0x23, 0x00, 0x00, 0x16, 0x03, 0x01,
+ 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba, 0x00, 0x02,
+ 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, 0x02, 0xb0,
+ 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, 0xbb, 0xa4,
+ 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, 0x0b, 0x30,
+ 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03,
+ 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d,
+ 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31,
+ 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a,
+ 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e,
+ 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69,
+ 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c,
+ 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30,
+ 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39,
+ 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, 0x31, 0x30,
+ 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39, 0x33,
+ 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41,
+ 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55,
+ 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65,
+ 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21,
+ 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
+ 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74,
+ 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74,
+ 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30,
+ 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xbb, 0x79,
+ 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf, 0x46, 0x10,
+ 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, 0x07, 0x43,
+ 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a, 0x43, 0x85,
+ 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, 0x4c, 0x2c,
+ 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4, 0x82, 0xe5,
+ 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, 0xa5, 0x2c,
+ 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c, 0x7a, 0x56,
+ 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, 0x7b, 0x26,
+ 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0, 0xc9, 0x21,
+ 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, 0x5a, 0xbf,
+ 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18, 0x99, 0x07,
+ 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, 0x04, 0x39,
+ 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9, 0x7c, 0xe3,
+ 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, 0xcf, 0xaf,
+ 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d, 0xdb, 0xdb,
+ 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, 0x02, 0x03,
+ 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, 0x30, 0x81,
+ 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e,
+ 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad, 0xe2, 0x85,
+ 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce, 0x23,
+ 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88, 0x39,
+ 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
+ 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1, 0xad, 0xe2,
+ 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce,
+ 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88,
+ 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, 0x45, 0x31,
0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11,
0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53,
@@ -1599,158 +2064,200 @@ var clientauthTests = []clientauthTest{
0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65,
0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64,
0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79,
- 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d,
- 0x31, 0x30, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39,
- 0x30, 0x39, 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31,
- 0x31, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30,
- 0x39, 0x33, 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b,
- 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
- 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06,
- 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f,
- 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65,
- 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04,
- 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72,
- 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67,
- 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20,
- 0x4c, 0x74, 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d,
- 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
- 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d,
- 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00,
- 0xbb, 0x79, 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf,
- 0x46, 0x10, 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b,
- 0x07, 0x43, 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a,
- 0x43, 0x85, 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65,
- 0x4c, 0x2c, 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4,
- 0x82, 0xe5, 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62,
- 0xa5, 0x2c, 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c,
- 0x7a, 0x56, 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58,
- 0x7b, 0x26, 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0,
- 0xc9, 0x21, 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f,
- 0x5a, 0xbf, 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18,
- 0x99, 0x07, 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1,
- 0x04, 0x39, 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9,
- 0x7c, 0xe3, 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01,
- 0xcf, 0xaf, 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d,
- 0xdb, 0xdb, 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79,
- 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7,
- 0x30, 0x81, 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55,
- 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad,
- 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69,
- 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18,
- 0x88, 0x39, 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d,
- 0x23, 0x04, 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1,
- 0xad, 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb,
- 0x69, 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e,
- 0x18, 0x88, 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30,
- 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
- 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13,
- 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
- 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74,
- 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06,
- 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e,
- 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57,
- 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50,
- 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09,
- 0x00, 0x85, 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8,
- 0xca, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13,
- 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30,
- 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
- 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81,
- 0x81, 0x00, 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b,
- 0xb1, 0x59, 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0,
- 0x14, 0xd7, 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5,
- 0x5a, 0x95, 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae,
- 0x12, 0x66, 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e,
- 0x60, 0xd3, 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5,
- 0x25, 0x13, 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30,
- 0x1d, 0xba, 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7,
- 0xd7, 0x31, 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78,
- 0xea, 0x50, 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d,
- 0x5a, 0x5f, 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75,
- 0x90, 0x96, 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd,
- 0x98, 0x1f, 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c,
- 0xa3, 0x1b, 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57,
- 0xe9, 0x70, 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b,
- 0x26, 0x6e, 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7,
- 0xbd, 0xd9, 0x16, 0x03, 0x01, 0x00, 0x08, 0x0d,
- 0x00, 0x00, 0x04, 0x01, 0x01, 0x00, 0x00, 0x16,
- 0x03, 0x01, 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00,
+ 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, 0x00, 0x85,
+ 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30,
+ 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05,
+ 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00,
+ 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b, 0xb1, 0x59,
+ 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, 0x14, 0xd7,
+ 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5, 0x5a, 0x95,
+ 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, 0x12, 0x66,
+ 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e, 0x60, 0xd3,
+ 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, 0x25, 0x13,
+ 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30, 0x1d, 0xba,
+ 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, 0xd7, 0x31,
+ 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78, 0xea, 0x50,
+ 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, 0x5a, 0x5f,
+ 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75, 0x90, 0x96,
+ 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, 0x98, 0x1f,
+ 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c, 0xa3, 0x1b,
+ 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, 0xe9, 0x70,
+ 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b, 0x26, 0x6e,
+ 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, 0xbd, 0xd9,
+ 0x16, 0x03, 0x01, 0x00, 0x09, 0x0d, 0x00, 0x00,
+ 0x05, 0x02, 0x01, 0x40, 0x00, 0x00, 0x16, 0x03,
+ 0x01, 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00,
},
{
0x16, 0x03, 0x01, 0x00, 0x07, 0x0b, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x16, 0x03, 0x01, 0x00,
- 0x86, 0x10, 0x00, 0x00, 0x82, 0x00, 0x80, 0x04,
- 0x58, 0x63, 0x26, 0x32, 0x1b, 0x34, 0xbe, 0x10,
- 0xe4, 0xe4, 0x3e, 0xcd, 0x36, 0x7f, 0xa8, 0xa8,
- 0xe0, 0x19, 0xe8, 0x94, 0x13, 0xd9, 0x35, 0xc4,
- 0x71, 0xb4, 0x91, 0xd4, 0xbc, 0x74, 0x57, 0x9f,
- 0x93, 0xb7, 0x5d, 0x3b, 0x9c, 0xff, 0x5d, 0x79,
- 0xdb, 0x86, 0xfc, 0xdc, 0x74, 0x1e, 0x0c, 0xc6,
- 0xe8, 0x93, 0xcf, 0xaf, 0xba, 0x1d, 0xfd, 0x8a,
- 0xeb, 0xef, 0xbf, 0xfa, 0xa6, 0xe7, 0x53, 0x98,
- 0x60, 0x4e, 0x0e, 0x60, 0x7d, 0xea, 0x40, 0x8d,
- 0x1d, 0x8f, 0xa3, 0xc6, 0x83, 0xbc, 0xef, 0xb7,
- 0x9a, 0x4a, 0xe7, 0x99, 0xee, 0x0b, 0xc7, 0x46,
- 0x75, 0x45, 0x66, 0xe8, 0x5f, 0x4b, 0x08, 0xa4,
- 0xc1, 0x36, 0xd0, 0x36, 0x2c, 0xf2, 0x9a, 0x44,
- 0x1e, 0x5f, 0x22, 0xf4, 0xbe, 0x66, 0x66, 0x17,
- 0xd8, 0xb6, 0x0a, 0x89, 0xed, 0x22, 0x80, 0xdb,
- 0xad, 0x05, 0xd1, 0xb5, 0x93, 0xa1, 0x1c, 0x14,
+ 0x86, 0x10, 0x00, 0x00, 0x82, 0x00, 0x80, 0x36,
+ 0xfc, 0xd8, 0xc8, 0xa2, 0x67, 0xc8, 0xc6, 0xf4,
+ 0x28, 0x70, 0xe1, 0x5a, 0x02, 0x8f, 0xef, 0x42,
+ 0xe0, 0xd3, 0xb8, 0xd6, 0x6b, 0xe4, 0xee, 0x5c,
+ 0xcf, 0x42, 0xc4, 0xfa, 0xcd, 0x0f, 0xfe, 0xf4,
+ 0x76, 0x76, 0x47, 0x73, 0xa8, 0x72, 0x8f, 0xa2,
+ 0x56, 0x81, 0x83, 0xb8, 0x84, 0x72, 0x67, 0xdd,
+ 0xbe, 0x05, 0x4b, 0x84, 0xd9, 0xd2, 0xb6, 0xc2,
+ 0xe7, 0x20, 0xac, 0x1f, 0x46, 0x9d, 0x05, 0x47,
+ 0x8e, 0x89, 0xc0, 0x42, 0x57, 0x4a, 0xa2, 0x98,
+ 0xe5, 0x39, 0x4f, 0xc4, 0x27, 0x6d, 0x43, 0xa8,
+ 0x83, 0x76, 0xe6, 0xad, 0xe3, 0x17, 0x68, 0x31,
+ 0xcb, 0x7e, 0xfc, 0xe7, 0x4b, 0x76, 0x3d, 0x3c,
+ 0xfa, 0x77, 0x65, 0xc9, 0x4c, 0x5b, 0xce, 0x5e,
+ 0xf7, 0x8b, 0xa8, 0xa6, 0xdd, 0xb2, 0xef, 0x0b,
+ 0x46, 0x83, 0xdf, 0x0a, 0x8c, 0x22, 0x12, 0x6e,
+ 0xe1, 0x45, 0x54, 0x88, 0xd1, 0xe8, 0xd2, 0x14,
0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x01,
- 0x00, 0x24, 0x62, 0x6f, 0x3d, 0x30, 0x56, 0x97,
- 0xde, 0x03, 0x67, 0xa9, 0x63, 0x21, 0xb6, 0xe6,
- 0x05, 0x69, 0x94, 0xfb, 0x50, 0xc1, 0x99, 0xdd,
- 0xf6, 0xe8, 0x60, 0xbd, 0xe6, 0xba, 0xe3, 0x50,
- 0x0a, 0xcd, 0xde, 0x14, 0x16, 0xc4,
+ 0x00, 0x24, 0x30, 0x8c, 0x7d, 0x40, 0xfc, 0x5e,
+ 0x80, 0x9c, 0xc4, 0x7c, 0x62, 0x01, 0xa1, 0x37,
+ 0xcf, 0x1a, 0x75, 0x28, 0x8d, 0xeb, 0x63, 0xcc,
+ 0x02, 0xa6, 0x66, 0xdf, 0x36, 0x01, 0xb3, 0x9d,
+ 0x38, 0x42, 0x16, 0x91, 0xf0, 0x02,
},
{
- 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03,
- 0x01, 0x00, 0x24, 0xf0, 0x21, 0xf6, 0x84, 0x6a,
- 0xe3, 0x6b, 0x8a, 0xc5, 0x46, 0x50, 0xca, 0x40,
- 0xea, 0x4e, 0x82, 0xc1, 0x70, 0x25, 0xd8, 0x7d,
- 0x60, 0xf5, 0x51, 0x7f, 0x64, 0x03, 0x9f, 0x53,
- 0xec, 0xfb, 0x57, 0xa9, 0xfc, 0x26, 0x15, 0x17,
- 0x03, 0x01, 0x00, 0x21, 0xa6, 0xc6, 0x94, 0x2b,
- 0xa9, 0xcb, 0x93, 0xff, 0xb6, 0xa6, 0xe7, 0xc5,
- 0x37, 0x86, 0x15, 0x37, 0x57, 0xce, 0xef, 0x54,
- 0x96, 0x5d, 0x50, 0xa0, 0x50, 0x69, 0x5e, 0x82,
- 0x61, 0x8d, 0x42, 0xfb, 0x78, 0x15, 0x03, 0x01,
- 0x00, 0x16, 0x45, 0xd1, 0x86, 0x68, 0x59, 0xc1,
- 0xaf, 0xac, 0x5c, 0x46, 0x8a, 0x68, 0x69, 0x0c,
- 0xd7, 0x67, 0xbf, 0xf0, 0x3e, 0xee, 0x45, 0x55,
+ 0x16, 0x03, 0x01, 0x00, 0x72, 0x04, 0x00, 0x00,
+ 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65,
+ 0xe8, 0x4b, 0xd1, 0xef, 0xba, 0x96, 0x9a, 0x2a,
+ 0x6c, 0x8c, 0x7e, 0x38, 0x10, 0x46, 0x86, 0x1d,
+ 0x19, 0x1d, 0x62, 0x29, 0x3f, 0x58, 0xfb, 0x6d,
+ 0x89, 0xd2, 0x81, 0x9a, 0x1c, 0xb3, 0x58, 0xb3,
+ 0x19, 0x39, 0x17, 0x47, 0x49, 0xc9, 0xfe, 0x4a,
+ 0x7a, 0x32, 0xac, 0x2c, 0x43, 0xf9, 0xa9, 0xea,
+ 0xec, 0x51, 0x46, 0xf1, 0xb8, 0x59, 0x23, 0x70,
+ 0xce, 0x7c, 0xb9, 0x47, 0x70, 0xa3, 0xc9, 0xae,
+ 0x47, 0x7b, 0x7e, 0xc7, 0xcf, 0x76, 0x12, 0x76,
+ 0x18, 0x90, 0x12, 0xcd, 0xf3, 0xd4, 0x27, 0x81,
+ 0xfc, 0x46, 0x03, 0x3e, 0x05, 0x87, 0x6f, 0x14,
+ 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x01,
+ 0x00, 0x24, 0xc3, 0xa0, 0x29, 0xb1, 0x52, 0x82,
+ 0xef, 0x85, 0xa1, 0x64, 0x0f, 0xe4, 0xa3, 0xfb,
+ 0xa7, 0x1d, 0x22, 0x4c, 0xcb, 0xd6, 0x5b, 0x18,
+ 0x61, 0xc7, 0x7c, 0xf2, 0x67, 0x4a, 0xc7, 0x11,
+ 0x9d, 0x8e, 0x0e, 0x15, 0x22, 0xcf, 0x17, 0x03,
+ 0x01, 0x00, 0x21, 0xfd, 0xbb, 0xf1, 0xa9, 0x7c,
+ 0xbf, 0x92, 0xb3, 0xfa, 0x2c, 0x08, 0x6f, 0x22,
+ 0x78, 0x80, 0xf2, 0x2e, 0x86, 0x26, 0x21, 0x36,
+ 0x3f, 0x32, 0xdf, 0xb6, 0x47, 0xa5, 0xf8, 0x27,
+ 0xc1, 0xe9, 0x53, 0x90, 0x15, 0x03, 0x01, 0x00,
+ 0x16, 0xfe, 0xef, 0x2e, 0xa0, 0x5d, 0xe0, 0xce,
+ 0x94, 0x20, 0x56, 0x61, 0x6e, 0xe5, 0x62, 0xce,
+ 0x27, 0x57, 0x3e, 0x30, 0x32, 0x77, 0x53,
},
}},
+
// Server asks for cert with empty CA list, client gives one
// go test -run "TestRunServer" -serve -clientauth 1
{"RequestClientCert, client gives it", RequestClientCert, []*x509.Certificate{clientCertificate}, [][]byte{
{
- 0x16, 0x03, 0x01, 0x00, 0x54, 0x01, 0x00, 0x00,
- 0x50, 0x03, 0x01, 0x50, 0x77, 0x43, 0x47, 0xfd,
- 0x1d, 0xb0, 0x60, 0x4c, 0x25, 0x86, 0x45, 0x4a,
- 0xe5, 0x3f, 0x80, 0x56, 0x18, 0x91, 0x5c, 0xe2,
- 0x62, 0xc5, 0x77, 0xc2, 0x92, 0xdd, 0xdc, 0x39,
- 0x23, 0x1d, 0xc5, 0x00, 0x00, 0x28, 0x00, 0x39,
- 0x00, 0x38, 0x00, 0x35, 0x00, 0x16, 0x00, 0x13,
- 0x00, 0x0a, 0x00, 0x33, 0x00, 0x32, 0x00, 0x2f,
- 0x00, 0x05, 0x00, 0x04, 0x00, 0x15, 0x00, 0x12,
- 0x00, 0x09, 0x00, 0x14, 0x00, 0x11, 0x00, 0x08,
- 0x00, 0x06, 0x00, 0x03, 0x00, 0xff, 0x02, 0x01,
- 0x00,
+ 0x16, 0x03, 0x01, 0x01, 0x1e, 0x01, 0x00, 0x01,
+ 0x1a, 0x03, 0x03, 0x51, 0xe5, 0x74, 0x0e, 0x95,
+ 0x6f, 0x4f, 0x4a, 0xbf, 0xb7, 0xc0, 0x6c, 0xac,
+ 0xd9, 0xfe, 0x7d, 0xd0, 0x51, 0x19, 0x62, 0x62,
+ 0x1c, 0x6e, 0x57, 0x77, 0xd2, 0x31, 0xaf, 0x88,
+ 0xb9, 0xc0, 0x1d, 0x00, 0x00, 0x82, 0xc0, 0x30,
+ 0xc0, 0x2c, 0xc0, 0x28, 0xc0, 0x24, 0xc0, 0x14,
+ 0xc0, 0x0a, 0x00, 0xa3, 0x00, 0x9f, 0x00, 0x6b,
+ 0x00, 0x6a, 0x00, 0x39, 0x00, 0x38, 0xc0, 0x32,
+ 0xc0, 0x2e, 0xc0, 0x2a, 0xc0, 0x26, 0xc0, 0x0f,
+ 0xc0, 0x05, 0x00, 0x9d, 0x00, 0x3d, 0x00, 0x35,
+ 0xc0, 0x12, 0xc0, 0x08, 0x00, 0x16, 0x00, 0x13,
+ 0xc0, 0x0d, 0xc0, 0x03, 0x00, 0x0a, 0xc0, 0x2f,
+ 0xc0, 0x2b, 0xc0, 0x27, 0xc0, 0x23, 0xc0, 0x13,
+ 0xc0, 0x09, 0x00, 0xa2, 0x00, 0x9e, 0x00, 0x67,
+ 0x00, 0x40, 0x00, 0x33, 0x00, 0x32, 0xc0, 0x31,
+ 0xc0, 0x2d, 0xc0, 0x29, 0xc0, 0x25, 0xc0, 0x0e,
+ 0xc0, 0x04, 0x00, 0x9c, 0x00, 0x3c, 0x00, 0x2f,
+ 0x00, 0x07, 0xc0, 0x11, 0xc0, 0x07, 0xc0, 0x0c,
+ 0xc0, 0x02, 0x00, 0x05, 0x00, 0x04, 0x00, 0x15,
+ 0x00, 0x12, 0x00, 0x09, 0x00, 0x14, 0x00, 0x11,
+ 0x00, 0x08, 0x00, 0x06, 0x00, 0x03, 0x00, 0xff,
+ 0x01, 0x00, 0x00, 0x6f, 0x00, 0x0b, 0x00, 0x04,
+ 0x03, 0x00, 0x01, 0x02, 0x00, 0x0a, 0x00, 0x34,
+ 0x00, 0x32, 0x00, 0x0e, 0x00, 0x0d, 0x00, 0x19,
+ 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x18, 0x00, 0x09,
+ 0x00, 0x0a, 0x00, 0x16, 0x00, 0x17, 0x00, 0x08,
+ 0x00, 0x06, 0x00, 0x07, 0x00, 0x14, 0x00, 0x15,
+ 0x00, 0x04, 0x00, 0x05, 0x00, 0x12, 0x00, 0x13,
+ 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x0f,
+ 0x00, 0x10, 0x00, 0x11, 0x00, 0x23, 0x00, 0x00,
+ 0x00, 0x0d, 0x00, 0x22, 0x00, 0x20, 0x06, 0x01,
+ 0x06, 0x02, 0x06, 0x03, 0x05, 0x01, 0x05, 0x02,
+ 0x05, 0x03, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03,
+ 0x03, 0x01, 0x03, 0x02, 0x03, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x02, 0x03, 0x01, 0x01, 0x00, 0x0f,
+ 0x00, 0x01, 0x01,
},
{
- 0x16, 0x03, 0x01, 0x00, 0x2a, 0x02, 0x00, 0x00,
- 0x26, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x16, 0x03, 0x01, 0x00, 0x30, 0x02, 0x00, 0x00,
+ 0x2c, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x16,
- 0x03, 0x01, 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba,
- 0x00, 0x02, 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82,
- 0x02, 0xb0, 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03,
- 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0,
- 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d,
- 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
- 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, 0x31,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00,
+ 0x04, 0x00, 0x23, 0x00, 0x00, 0x16, 0x03, 0x01,
+ 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba, 0x00, 0x02,
+ 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, 0x02, 0xb0,
+ 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, 0xbb, 0xa4,
+ 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, 0x0b, 0x30,
+ 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03,
+ 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d,
+ 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31,
+ 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a,
+ 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e,
+ 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69,
+ 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c,
+ 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30,
+ 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39,
+ 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, 0x31, 0x30,
+ 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39, 0x33,
+ 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41,
+ 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55,
+ 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65,
+ 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21,
+ 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
+ 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74,
+ 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74,
+ 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30,
+ 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xbb, 0x79,
+ 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf, 0x46, 0x10,
+ 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, 0x07, 0x43,
+ 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a, 0x43, 0x85,
+ 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, 0x4c, 0x2c,
+ 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4, 0x82, 0xe5,
+ 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, 0xa5, 0x2c,
+ 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c, 0x7a, 0x56,
+ 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, 0x7b, 0x26,
+ 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0, 0xc9, 0x21,
+ 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, 0x5a, 0xbf,
+ 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18, 0x99, 0x07,
+ 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, 0x04, 0x39,
+ 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9, 0x7c, 0xe3,
+ 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, 0xcf, 0xaf,
+ 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d, 0xdb, 0xdb,
+ 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, 0x02, 0x03,
+ 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, 0x30, 0x81,
+ 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e,
+ 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad, 0xe2, 0x85,
+ 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce, 0x23,
+ 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88, 0x39,
+ 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
+ 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1, 0xad, 0xe2,
+ 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce,
+ 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88,
+ 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, 0x45, 0x31,
0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11,
0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53,
@@ -1759,82 +2266,31 @@ var clientauthTests = []clientauthTest{
0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65,
0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64,
0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79,
- 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d,
- 0x31, 0x30, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39,
- 0x30, 0x39, 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31,
- 0x31, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30,
- 0x39, 0x33, 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b,
- 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
- 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06,
- 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f,
- 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65,
- 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04,
- 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72,
- 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67,
- 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20,
- 0x4c, 0x74, 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d,
- 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
- 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d,
- 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00,
- 0xbb, 0x79, 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf,
- 0x46, 0x10, 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b,
- 0x07, 0x43, 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a,
- 0x43, 0x85, 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65,
- 0x4c, 0x2c, 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4,
- 0x82, 0xe5, 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62,
- 0xa5, 0x2c, 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c,
- 0x7a, 0x56, 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58,
- 0x7b, 0x26, 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0,
- 0xc9, 0x21, 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f,
- 0x5a, 0xbf, 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18,
- 0x99, 0x07, 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1,
- 0x04, 0x39, 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9,
- 0x7c, 0xe3, 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01,
- 0xcf, 0xaf, 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d,
- 0xdb, 0xdb, 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79,
- 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7,
- 0x30, 0x81, 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55,
- 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad,
- 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69,
- 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18,
- 0x88, 0x39, 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d,
- 0x23, 0x04, 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1,
- 0xad, 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb,
- 0x69, 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e,
- 0x18, 0x88, 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30,
- 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
- 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13,
- 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
- 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74,
- 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06,
- 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e,
- 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57,
- 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50,
- 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09,
- 0x00, 0x85, 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8,
- 0xca, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13,
- 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30,
- 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
- 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81,
- 0x81, 0x00, 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b,
- 0xb1, 0x59, 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0,
- 0x14, 0xd7, 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5,
- 0x5a, 0x95, 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae,
- 0x12, 0x66, 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e,
- 0x60, 0xd3, 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5,
- 0x25, 0x13, 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30,
- 0x1d, 0xba, 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7,
- 0xd7, 0x31, 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78,
- 0xea, 0x50, 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d,
- 0x5a, 0x5f, 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75,
- 0x90, 0x96, 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd,
- 0x98, 0x1f, 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c,
- 0xa3, 0x1b, 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57,
- 0xe9, 0x70, 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b,
- 0x26, 0x6e, 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7,
- 0xbd, 0xd9, 0x16, 0x03, 0x01, 0x00, 0x08, 0x0d,
- 0x00, 0x00, 0x04, 0x01, 0x01, 0x00, 0x00, 0x16,
- 0x03, 0x01, 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00,
+ 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, 0x00, 0x85,
+ 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30,
+ 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05,
+ 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00,
+ 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b, 0xb1, 0x59,
+ 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, 0x14, 0xd7,
+ 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5, 0x5a, 0x95,
+ 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, 0x12, 0x66,
+ 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e, 0x60, 0xd3,
+ 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, 0x25, 0x13,
+ 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30, 0x1d, 0xba,
+ 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, 0xd7, 0x31,
+ 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78, 0xea, 0x50,
+ 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, 0x5a, 0x5f,
+ 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75, 0x90, 0x96,
+ 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, 0x98, 0x1f,
+ 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c, 0xa3, 0x1b,
+ 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, 0xe9, 0x70,
+ 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b, 0x26, 0x6e,
+ 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, 0xbd, 0xd9,
+ 0x16, 0x03, 0x01, 0x00, 0x09, 0x0d, 0x00, 0x00,
+ 0x05, 0x02, 0x01, 0x40, 0x00, 0x00, 0x16, 0x03,
+ 0x01, 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00,
},
{
0x16, 0x03, 0x01, 0x01, 0xfb, 0x0b, 0x00, 0x01,
@@ -1902,66 +2358,778 @@ var clientauthTests = []clientauthTest{
0x51, 0x8d, 0x10, 0x7e, 0x4f, 0x94, 0x67, 0xdf,
0xa3, 0x4e, 0x70, 0x73, 0x8e, 0x90, 0x91, 0x85,
0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00,
- 0x82, 0x00, 0x80, 0x81, 0x46, 0x43, 0xf9, 0xe7,
- 0xda, 0x8c, 0x92, 0x3a, 0x78, 0x1a, 0x86, 0xb3,
- 0xbe, 0x83, 0x22, 0xb6, 0xaa, 0x57, 0x37, 0x68,
- 0x9e, 0x54, 0x3f, 0xd3, 0xce, 0x4d, 0x5e, 0x2a,
- 0xdc, 0xb0, 0x49, 0x02, 0xbb, 0xc0, 0x45, 0x58,
- 0x79, 0x10, 0xc7, 0x94, 0x60, 0x9f, 0x1b, 0x5f,
- 0x18, 0x31, 0x37, 0x9c, 0xe0, 0xe6, 0xdf, 0x5e,
- 0x70, 0x44, 0xf6, 0x8b, 0xdf, 0xf1, 0xf6, 0x43,
- 0xc8, 0x2f, 0xd1, 0xce, 0xd0, 0xd6, 0x64, 0x4f,
- 0xe8, 0x2b, 0xfa, 0xd3, 0xd0, 0xd1, 0x2e, 0xaa,
- 0x9b, 0x1d, 0x13, 0x5c, 0xbe, 0x57, 0x41, 0x6c,
- 0x5e, 0x8d, 0xea, 0xa9, 0x3c, 0x58, 0xa0, 0x30,
- 0x92, 0x77, 0x7a, 0xed, 0x64, 0x58, 0xe5, 0x7f,
- 0x6a, 0x93, 0x89, 0x66, 0x3d, 0x13, 0x16, 0x56,
- 0xa0, 0xad, 0xdc, 0x68, 0x95, 0x87, 0x81, 0xd0,
- 0x90, 0x4d, 0x5f, 0xfe, 0x3e, 0x83, 0x15, 0x2e,
- 0x50, 0x3c, 0xdd, 0x16, 0x03, 0x01, 0x00, 0x86,
- 0x0f, 0x00, 0x00, 0x82, 0x00, 0x80, 0x2b, 0xf8,
- 0x56, 0x48, 0xbb, 0x02, 0x37, 0x15, 0x02, 0x74,
- 0x33, 0x53, 0x65, 0xa7, 0x7c, 0x2f, 0xc6, 0x5d,
- 0x80, 0x59, 0xc1, 0xc2, 0x3b, 0xa9, 0xde, 0x4e,
- 0x70, 0x51, 0xd2, 0xde, 0x58, 0x7f, 0xd8, 0xb9,
- 0xb6, 0x3b, 0xc8, 0xaa, 0xfc, 0x3d, 0x53, 0x2d,
- 0x61, 0x4d, 0xf5, 0x60, 0x12, 0xc2, 0xa5, 0x39,
- 0x0c, 0xa7, 0xc6, 0xac, 0x26, 0x4b, 0xf4, 0x5f,
- 0xe9, 0xf4, 0xf2, 0x73, 0x48, 0xe4, 0x3b, 0xee,
- 0xf2, 0xee, 0xc0, 0xee, 0xfb, 0x5b, 0x60, 0xc2,
- 0x74, 0xe6, 0xf6, 0x43, 0x3e, 0xa4, 0xf7, 0x97,
- 0x3d, 0xfc, 0xe9, 0x44, 0x21, 0x18, 0x46, 0x05,
- 0x33, 0xf8, 0xfe, 0x35, 0x5b, 0xe6, 0x8f, 0xef,
- 0x4d, 0x4c, 0x87, 0xf6, 0xb4, 0x6e, 0x6b, 0x39,
- 0xd8, 0xaa, 0x1b, 0x33, 0xc9, 0x1c, 0x66, 0x48,
- 0xbe, 0xfa, 0xb5, 0x92, 0x09, 0xfd, 0xb9, 0xb9,
- 0xca, 0xe6, 0x6d, 0x71, 0xc6, 0x89, 0x14, 0x03,
+ 0x82, 0x00, 0x80, 0x0a, 0x4e, 0x89, 0xdf, 0x3a,
+ 0x3f, 0xf0, 0x4f, 0xef, 0x1a, 0x90, 0xd4, 0x3c,
+ 0xaf, 0x10, 0x57, 0xb0, 0xa1, 0x5f, 0xcd, 0x62,
+ 0x01, 0xe9, 0x0c, 0x36, 0x42, 0xfd, 0xaf, 0x23,
+ 0xf9, 0x14, 0xa6, 0x72, 0x26, 0x4e, 0x01, 0xdb,
+ 0xac, 0xb7, 0x4c, 0xe6, 0xa9, 0x52, 0xe2, 0xec,
+ 0x26, 0x8c, 0x7a, 0x64, 0xf8, 0x0b, 0x4c, 0x2f,
+ 0xa9, 0xcb, 0x75, 0xaf, 0x60, 0xd4, 0xb4, 0xe6,
+ 0xe8, 0xdb, 0x78, 0x78, 0x85, 0xf6, 0x0c, 0x95,
+ 0xcc, 0xb6, 0x55, 0xb9, 0xba, 0x9e, 0x91, 0xbc,
+ 0x66, 0xdb, 0x1e, 0x28, 0xab, 0x73, 0xce, 0x8b,
+ 0xd0, 0xd3, 0xe8, 0xbc, 0xd0, 0x21, 0x28, 0xbd,
+ 0xfb, 0x74, 0x64, 0xde, 0x3b, 0x3b, 0xd3, 0x4c,
+ 0x32, 0x40, 0x82, 0xba, 0x91, 0x1e, 0xe8, 0x47,
+ 0xc2, 0x09, 0xb7, 0x16, 0xaa, 0x25, 0xa9, 0x3c,
+ 0x6c, 0xa7, 0xf8, 0xc9, 0x54, 0x84, 0xc6, 0xf7,
+ 0x56, 0x05, 0xa4, 0x16, 0x03, 0x01, 0x00, 0x86,
+ 0x0f, 0x00, 0x00, 0x82, 0x00, 0x80, 0x4b, 0xab,
+ 0xda, 0xac, 0x2a, 0xb3, 0xe6, 0x34, 0x55, 0xcd,
+ 0xf2, 0x4b, 0x67, 0xe3, 0xd3, 0xff, 0xa3, 0xf4,
+ 0x79, 0x82, 0x01, 0x47, 0x8a, 0xe3, 0x9f, 0x89,
+ 0x70, 0xbe, 0x24, 0x24, 0xb7, 0x69, 0x60, 0xed,
+ 0x55, 0xa0, 0xca, 0x72, 0xb6, 0x4a, 0xbc, 0x1d,
+ 0xe2, 0x3f, 0xb5, 0x31, 0xda, 0x02, 0xf6, 0x37,
+ 0x51, 0xf8, 0x4c, 0x88, 0x2e, 0xb3, 0x8a, 0xe8,
+ 0x7b, 0x4a, 0x90, 0x36, 0xe4, 0xa6, 0x31, 0x95,
+ 0x8b, 0xa0, 0xc6, 0x91, 0x12, 0xb9, 0x35, 0x4e,
+ 0x72, 0xeb, 0x5c, 0xa2, 0xe8, 0x4c, 0x68, 0xf9,
+ 0x69, 0xfa, 0x70, 0x60, 0x6c, 0x7f, 0x32, 0x99,
+ 0xf1, 0xc3, 0x2d, 0xb4, 0x59, 0x58, 0x87, 0xaf,
+ 0x67, 0x62, 0x90, 0xe7, 0x8d, 0xd0, 0xa3, 0x77,
+ 0x33, 0xc2, 0x9b, 0xd5, 0x9c, 0xc7, 0xea, 0x25,
+ 0x98, 0x76, 0x9c, 0xe0, 0x6a, 0x03, 0x3a, 0x10,
+ 0xfd, 0x10, 0x3d, 0x55, 0x53, 0xa0, 0x14, 0x03,
0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x01, 0x00,
- 0x24, 0xe3, 0x2b, 0xef, 0x17, 0xd5, 0xa6, 0x4c,
- 0x2e, 0x10, 0xac, 0x9c, 0xfe, 0x0f, 0x18, 0x43,
- 0x95, 0x00, 0x81, 0xf7, 0x7c, 0x00, 0x5b, 0x89,
- 0x52, 0x41, 0xe4, 0x8a, 0x8a, 0x34, 0x31, 0x09,
- 0x48, 0x7c, 0xc5, 0xc3, 0x83,
+ 0x24, 0xd5, 0x12, 0xfc, 0xb9, 0x5a, 0xe3, 0x27,
+ 0x01, 0xbe, 0xc3, 0x77, 0x17, 0x1a, 0xbb, 0x4f,
+ 0xae, 0xd5, 0xa7, 0xee, 0x56, 0x61, 0x0d, 0x40,
+ 0xf4, 0xa4, 0xb5, 0xcc, 0x76, 0xfd, 0xbd, 0x13,
+ 0x04, 0xe1, 0xb8, 0xc7, 0x36,
},
{
- 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03,
- 0x01, 0x00, 0x24, 0x24, 0xaa, 0xaa, 0x56, 0x8b,
- 0x41, 0x87, 0x01, 0xbe, 0x80, 0x05, 0x51, 0x36,
- 0x08, 0xfc, 0xaf, 0xff, 0x7f, 0xf4, 0x74, 0x84,
- 0x88, 0xdc, 0xb8, 0x8e, 0x70, 0x6c, 0x22, 0x04,
- 0xee, 0x45, 0x8d, 0xda, 0xed, 0xc6, 0x05, 0x17,
- 0x03, 0x01, 0x00, 0x21, 0x91, 0x49, 0x4b, 0xed,
- 0xa3, 0x41, 0xe9, 0x88, 0x3b, 0xa3, 0x01, 0xee,
- 0x77, 0x4e, 0x12, 0xb4, 0xcd, 0x5e, 0xcc, 0x45,
- 0x02, 0x5a, 0x20, 0xd6, 0xe8, 0xac, 0xcb, 0x60,
- 0xcb, 0x1b, 0xef, 0xf9, 0xc2, 0x15, 0x03, 0x01,
- 0x00, 0x16, 0xd4, 0xcd, 0x92, 0x3c, 0x10, 0x93,
- 0x68, 0xc3, 0xdd, 0xaf, 0xe9, 0xcb, 0x5d, 0x94,
- 0x1a, 0x06, 0x81, 0xa7, 0x78, 0x0f, 0xc3, 0x03,
+ 0x16, 0x03, 0x01, 0x02, 0x67, 0x04, 0x00, 0x02,
+ 0x63, 0x00, 0x00, 0x00, 0x00, 0x02, 0x5d, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65,
+ 0xe8, 0x4b, 0xd1, 0xef, 0xba, 0x1f, 0xe2, 0x69,
+ 0x07, 0x7f, 0x85, 0x2d, 0x4e, 0x2a, 0x2e, 0xbd,
+ 0x05, 0xe9, 0xc1, 0x6c, 0x9e, 0xbf, 0x47, 0x18,
+ 0x91, 0x77, 0xf7, 0xe8, 0xb6, 0x27, 0x37, 0xa6,
+ 0x6b, 0x87, 0x29, 0xbb, 0x3b, 0xe5, 0x68, 0x62,
+ 0x04, 0x3e, 0xad, 0x4d, 0xff, 0xad, 0xf1, 0x22,
+ 0x87, 0x8d, 0xf6, 0x04, 0x3b, 0x59, 0x22, 0xf7,
+ 0xfd, 0x88, 0x0e, 0xa4, 0x09, 0xc0, 0x0d, 0x10,
+ 0x80, 0x10, 0x79, 0xee, 0x70, 0x96, 0xdb, 0x22,
+ 0x8b, 0xb7, 0xac, 0xe0, 0x98, 0xad, 0xe9, 0xe3,
+ 0xcb, 0xea, 0x9f, 0xe6, 0x83, 0x28, 0x7c, 0x7e,
+ 0x4e, 0x9a, 0x8d, 0xd9, 0xf3, 0x86, 0xf4, 0x89,
+ 0x8b, 0x79, 0x8f, 0xbb, 0xe9, 0x74, 0x02, 0x02,
+ 0x14, 0x04, 0xea, 0xba, 0x16, 0x10, 0xa1, 0x85,
+ 0xbe, 0x4e, 0x4e, 0x92, 0xc5, 0x83, 0xf6, 0x1e,
+ 0x1f, 0xd4, 0x25, 0xc2, 0xc2, 0xb9, 0xce, 0x33,
+ 0x63, 0x66, 0x79, 0x1f, 0x54, 0x35, 0xc1, 0xe8,
+ 0x89, 0x34, 0x78, 0x94, 0x36, 0x14, 0xef, 0x01,
+ 0x1f, 0xf1, 0xbd, 0x77, 0x2c, 0x4d, 0xac, 0x5c,
+ 0x5c, 0x4a, 0xc6, 0xed, 0xd8, 0x0e, 0x72, 0x84,
+ 0x83, 0xdc, 0x56, 0x84, 0xc8, 0xf3, 0x89, 0x56,
+ 0xfd, 0x89, 0xc1, 0xc9, 0x9a, 0x29, 0x91, 0x7e,
+ 0x19, 0xe9, 0x8b, 0x5b, 0x11, 0x15, 0x4e, 0x6c,
+ 0xf4, 0x89, 0xe7, 0x6d, 0x68, 0x1e, 0xf9, 0x6c,
+ 0x23, 0x72, 0x05, 0x68, 0x82, 0x60, 0x84, 0x1f,
+ 0x83, 0x20, 0x09, 0x86, 0x10, 0x81, 0xec, 0xec,
+ 0xdc, 0x25, 0x53, 0x20, 0xfa, 0xa9, 0x41, 0x64,
+ 0xd6, 0x20, 0xf3, 0xf4, 0x52, 0xf2, 0x80, 0x62,
+ 0x83, 0xc9, 0x23, 0x66, 0x44, 0x95, 0x5a, 0x99,
+ 0x8a, 0xe1, 0x26, 0x63, 0xc1, 0x8b, 0x31, 0xf9,
+ 0x21, 0x06, 0x77, 0x04, 0x27, 0xf2, 0x0c, 0x63,
+ 0x83, 0x45, 0xa0, 0xa9, 0x7b, 0xcf, 0xdf, 0xd7,
+ 0x56, 0x75, 0xbc, 0xdd, 0x95, 0x36, 0xb1, 0x75,
+ 0x39, 0x05, 0x00, 0x3c, 0x8a, 0x79, 0xd6, 0xe9,
+ 0xf0, 0x4b, 0xdc, 0x51, 0x6b, 0x01, 0x94, 0x16,
+ 0x87, 0x12, 0x92, 0x6c, 0x07, 0xc1, 0xf5, 0x58,
+ 0xb7, 0x2a, 0x81, 0xf5, 0xa0, 0x37, 0x8b, 0xa6,
+ 0x22, 0xfe, 0x28, 0x0a, 0x7e, 0x68, 0xe2, 0xda,
+ 0x6c, 0x53, 0xee, 0x0e, 0x8d, 0x2d, 0x8b, 0x0b,
+ 0xda, 0xf8, 0x99, 0x3e, 0x0e, 0xed, 0x9f, 0xc1,
+ 0x2b, 0xf6, 0xfe, 0xe9, 0x52, 0x38, 0x7b, 0x83,
+ 0x9a, 0x50, 0xa6, 0xd7, 0x49, 0x83, 0x43, 0x7e,
+ 0x82, 0xec, 0xc7, 0x09, 0x3d, 0x3d, 0xb1, 0xee,
+ 0xe8, 0xc5, 0x6a, 0xc3, 0x3d, 0x4b, 0x4c, 0x6a,
+ 0xbb, 0x0b, 0x2c, 0x24, 0x2e, 0xdb, 0x7d, 0x57,
+ 0x87, 0xb4, 0x80, 0xa5, 0xae, 0xff, 0x54, 0xa8,
+ 0xa5, 0x27, 0x69, 0x95, 0xc8, 0xe7, 0x79, 0xc7,
+ 0x89, 0x2a, 0x73, 0x49, 0xcb, 0xf5, 0xc5, 0xbc,
+ 0x4a, 0xe0, 0x73, 0xa9, 0xbc, 0x88, 0x64, 0x96,
+ 0x98, 0xa5, 0x1e, 0xe3, 0x43, 0xc1, 0x7d, 0x78,
+ 0xc7, 0x94, 0x72, 0xd4, 0x2c, 0x6e, 0x85, 0x39,
+ 0x9a, 0xaf, 0xdb, 0xa1, 0xe9, 0xe2, 0xcb, 0x37,
+ 0x04, 0xc6, 0x8c, 0x81, 0xd3, 0x2a, 0xb7, 0xbe,
+ 0x6c, 0x07, 0x1f, 0x5e, 0xd9, 0x00, 0xd2, 0xf7,
+ 0xe1, 0xa7, 0xbc, 0x0c, 0xb6, 0x6d, 0xfb, 0x3f,
+ 0x3d, 0x24, 0xaa, 0xfb, 0x7e, 0xe1, 0xb5, 0x1b,
+ 0xff, 0x38, 0xaa, 0x69, 0x59, 0x38, 0x52, 0x9a,
+ 0x0e, 0x6d, 0xbc, 0xde, 0x4f, 0x13, 0x09, 0x17,
+ 0xc4, 0xa9, 0x05, 0x84, 0xbc, 0x50, 0xef, 0x40,
+ 0xb0, 0x4c, 0x24, 0x32, 0xed, 0x94, 0x2c, 0xdd,
+ 0xda, 0x20, 0x24, 0x67, 0xe2, 0xea, 0x71, 0x3d,
+ 0x4a, 0x04, 0x0d, 0x98, 0x29, 0x20, 0x4c, 0xeb,
+ 0x70, 0xce, 0x45, 0x9e, 0x5a, 0xaf, 0xb6, 0xa3,
+ 0x92, 0xc8, 0x28, 0xf2, 0xe3, 0xe8, 0x8a, 0x5d,
+ 0x0a, 0x33, 0x79, 0x9b, 0x6a, 0xf3, 0x30, 0x01,
+ 0x1d, 0x47, 0xbd, 0x01, 0xcc, 0x4d, 0x71, 0xc0,
+ 0x56, 0xfa, 0xfd, 0x37, 0xed, 0x0f, 0x27, 0xc0,
+ 0xbb, 0xa0, 0xee, 0xc3, 0x79, 0x8b, 0xe7, 0x41,
+ 0x8f, 0xfa, 0x3a, 0xcb, 0x45, 0x3b, 0x85, 0x9f,
+ 0x06, 0x90, 0xb2, 0x51, 0x7a, 0xc3, 0x11, 0x41,
+ 0x4b, 0xe3, 0x26, 0x94, 0x3e, 0xa2, 0xfd, 0x0a,
+ 0xda, 0x50, 0xf6, 0x50, 0x78, 0x19, 0x6c, 0x52,
+ 0xd1, 0x12, 0x76, 0xc2, 0x50, 0x2f, 0x0b, 0xca,
+ 0x33, 0xe5, 0x79, 0x93, 0x14, 0x03, 0x01, 0x00,
+ 0x01, 0x01, 0x16, 0x03, 0x01, 0x00, 0x24, 0x2b,
+ 0x51, 0x42, 0x95, 0x6b, 0xca, 0x9f, 0x42, 0x5d,
+ 0xd2, 0xd9, 0x67, 0xf9, 0x49, 0x30, 0xfd, 0x2a,
+ 0x46, 0xd3, 0x04, 0xf4, 0x86, 0xf9, 0x11, 0x34,
+ 0x82, 0xac, 0xe2, 0xc2, 0x2d, 0xc4, 0xd0, 0xfe,
+ 0xa9, 0xc9, 0x4b, 0x17, 0x03, 0x01, 0x00, 0x21,
+ 0x65, 0x1c, 0xe9, 0x5c, 0xb6, 0xe2, 0x7c, 0x8e,
+ 0x49, 0x12, 0x1b, 0xe6, 0x40, 0xd3, 0x97, 0x21,
+ 0x76, 0x01, 0xe5, 0x80, 0x5e, 0xf3, 0x11, 0x47,
+ 0x25, 0x02, 0x78, 0x8e, 0x6b, 0xae, 0xb3, 0xf3,
+ 0x59, 0x15, 0x03, 0x01, 0x00, 0x16, 0x38, 0xc1,
+ 0x99, 0x2e, 0xf8, 0x6f, 0x45, 0xa4, 0x10, 0x79,
+ 0x5b, 0xc1, 0x47, 0x9a, 0xf6, 0x5c, 0x90, 0xeb,
+ 0xa6, 0xe3, 0x1a, 0x24,
},
}},
}
+var tls11ECDHEAESServerScript = [][]byte{
+ {
+ 0x16, 0x03, 0x01, 0x01, 0x46, 0x01, 0x00, 0x01,
+ 0x42, 0x03, 0x03, 0x51, 0x9f, 0xa3, 0xb0, 0xb7,
+ 0x1d, 0x26, 0x93, 0x36, 0xc0, 0x8d, 0x7e, 0xf8,
+ 0x4f, 0x6f, 0xc9, 0x3c, 0x31, 0x1e, 0x7f, 0xb1,
+ 0xf0, 0xc1, 0x0f, 0xf9, 0x0c, 0xa2, 0xd5, 0xca,
+ 0x48, 0xe5, 0x35, 0x00, 0x00, 0xd0, 0xc0, 0x30,
+ 0xc0, 0x2c, 0xc0, 0x28, 0xc0, 0x24, 0xc0, 0x14,
+ 0xc0, 0x0a, 0xc0, 0x22, 0xc0, 0x21, 0x00, 0xa5,
+ 0x00, 0xa3, 0x00, 0xa1, 0x00, 0x9f, 0x00, 0x6b,
+ 0x00, 0x6a, 0x00, 0x69, 0x00, 0x68, 0x00, 0x39,
+ 0x00, 0x38, 0x00, 0x37, 0x00, 0x36, 0x00, 0x88,
+ 0x00, 0x87, 0x00, 0x86, 0x00, 0x85, 0xc0, 0x32,
+ 0xc0, 0x2e, 0xc0, 0x2a, 0xc0, 0x26, 0xc0, 0x0f,
+ 0xc0, 0x05, 0x00, 0x9d, 0x00, 0x3d, 0x00, 0x35,
+ 0x00, 0x84, 0xc0, 0x12, 0xc0, 0x08, 0xc0, 0x1c,
+ 0xc0, 0x1b, 0x00, 0x16, 0x00, 0x13, 0x00, 0x10,
+ 0x00, 0x0d, 0xc0, 0x0d, 0xc0, 0x03, 0x00, 0x0a,
+ 0xc0, 0x2f, 0xc0, 0x2b, 0xc0, 0x27, 0xc0, 0x23,
+ 0xc0, 0x13, 0xc0, 0x09, 0xc0, 0x1f, 0xc0, 0x1e,
+ 0x00, 0xa4, 0x00, 0xa2, 0x00, 0xa0, 0x00, 0x9e,
+ 0x00, 0x67, 0x00, 0x40, 0x00, 0x3f, 0x00, 0x3e,
+ 0x00, 0x33, 0x00, 0x32, 0x00, 0x31, 0x00, 0x30,
+ 0x00, 0x9a, 0x00, 0x99, 0x00, 0x98, 0x00, 0x97,
+ 0x00, 0x45, 0x00, 0x44, 0x00, 0x43, 0x00, 0x42,
+ 0xc0, 0x31, 0xc0, 0x2d, 0xc0, 0x29, 0xc0, 0x25,
+ 0xc0, 0x0e, 0xc0, 0x04, 0x00, 0x9c, 0x00, 0x3c,
+ 0x00, 0x2f, 0x00, 0x96, 0x00, 0x41, 0x00, 0x07,
+ 0xc0, 0x11, 0xc0, 0x07, 0xc0, 0x0c, 0xc0, 0x02,
+ 0x00, 0x05, 0x00, 0x04, 0x00, 0x15, 0x00, 0x12,
+ 0x00, 0x0f, 0x00, 0x0c, 0x00, 0x09, 0x00, 0x14,
+ 0x00, 0x11, 0x00, 0x0e, 0x00, 0x0b, 0x00, 0x08,
+ 0x00, 0x06, 0x00, 0x03, 0x00, 0xff, 0x01, 0x00,
+ 0x00, 0x49, 0x00, 0x0b, 0x00, 0x04, 0x03, 0x00,
+ 0x01, 0x02, 0x00, 0x0a, 0x00, 0x34, 0x00, 0x32,
+ 0x00, 0x0e, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x0b,
+ 0x00, 0x0c, 0x00, 0x18, 0x00, 0x09, 0x00, 0x0a,
+ 0x00, 0x16, 0x00, 0x17, 0x00, 0x08, 0x00, 0x06,
+ 0x00, 0x07, 0x00, 0x14, 0x00, 0x15, 0x00, 0x04,
+ 0x00, 0x05, 0x00, 0x12, 0x00, 0x13, 0x00, 0x01,
+ 0x00, 0x02, 0x00, 0x03, 0x00, 0x0f, 0x00, 0x10,
+ 0x00, 0x11, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0f,
+ 0x00, 0x01, 0x01,
+ },
+ {
+ 0x16, 0x03, 0x02, 0x00, 0x30, 0x02, 0x00, 0x00,
+ 0x2c, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xc0, 0x13, 0x00, 0x00,
+ 0x04, 0x00, 0x23, 0x00, 0x00, 0x16, 0x03, 0x02,
+ 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba, 0x00, 0x02,
+ 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, 0x02, 0xb0,
+ 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, 0xbb, 0xa4,
+ 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, 0x0b, 0x30,
+ 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03,
+ 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d,
+ 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31,
+ 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a,
+ 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e,
+ 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69,
+ 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c,
+ 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30,
+ 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39,
+ 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, 0x31, 0x30,
+ 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39, 0x33,
+ 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41,
+ 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55,
+ 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65,
+ 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21,
+ 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
+ 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74,
+ 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74,
+ 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30,
+ 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xbb, 0x79,
+ 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf, 0x46, 0x10,
+ 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, 0x07, 0x43,
+ 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a, 0x43, 0x85,
+ 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, 0x4c, 0x2c,
+ 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4, 0x82, 0xe5,
+ 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, 0xa5, 0x2c,
+ 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c, 0x7a, 0x56,
+ 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, 0x7b, 0x26,
+ 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0, 0xc9, 0x21,
+ 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, 0x5a, 0xbf,
+ 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18, 0x99, 0x07,
+ 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, 0x04, 0x39,
+ 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9, 0x7c, 0xe3,
+ 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, 0xcf, 0xaf,
+ 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d, 0xdb, 0xdb,
+ 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, 0x02, 0x03,
+ 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, 0x30, 0x81,
+ 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e,
+ 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad, 0xe2, 0x85,
+ 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce, 0x23,
+ 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88, 0x39,
+ 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
+ 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1, 0xad, 0xe2,
+ 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce,
+ 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88,
+ 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, 0x45, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11,
+ 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53,
+ 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74,
+ 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55,
+ 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65,
+ 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64,
+ 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79,
+ 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, 0x00, 0x85,
+ 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30,
+ 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05,
+ 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00,
+ 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b, 0xb1, 0x59,
+ 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, 0x14, 0xd7,
+ 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5, 0x5a, 0x95,
+ 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, 0x12, 0x66,
+ 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e, 0x60, 0xd3,
+ 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, 0x25, 0x13,
+ 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30, 0x1d, 0xba,
+ 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, 0xd7, 0x31,
+ 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78, 0xea, 0x50,
+ 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, 0x5a, 0x5f,
+ 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75, 0x90, 0x96,
+ 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, 0x98, 0x1f,
+ 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c, 0xa3, 0x1b,
+ 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, 0xe9, 0x70,
+ 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b, 0x26, 0x6e,
+ 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, 0xbd, 0xd9,
+ 0x16, 0x03, 0x02, 0x01, 0x0f, 0x0c, 0x00, 0x01,
+ 0x0b, 0x03, 0x00, 0x19, 0x85, 0x04, 0x01, 0x39,
+ 0xdc, 0xee, 0x44, 0x17, 0x5e, 0xdb, 0xd7, 0x27,
+ 0xaf, 0xb6, 0x56, 0xd9, 0xb4, 0x43, 0x5a, 0x99,
+ 0xcf, 0xaa, 0x31, 0x37, 0x0c, 0x6f, 0x3a, 0xa0,
+ 0xf8, 0x53, 0xc4, 0x74, 0xd1, 0x91, 0x0a, 0x46,
+ 0xf5, 0x38, 0x3b, 0x5c, 0x09, 0xd8, 0x97, 0xdc,
+ 0x4b, 0xaa, 0x70, 0x26, 0x48, 0xf2, 0xd6, 0x0b,
+ 0x31, 0xc9, 0xf8, 0xd4, 0x98, 0x43, 0xe1, 0x6c,
+ 0xd5, 0xc7, 0xb2, 0x8e, 0x0b, 0x01, 0xe6, 0xb6,
+ 0x00, 0x28, 0x80, 0x7b, 0xfc, 0x96, 0x8f, 0x0d,
+ 0xa2, 0x4f, 0xb0, 0x79, 0xaf, 0xdc, 0x61, 0x28,
+ 0x63, 0x33, 0x78, 0xf6, 0x31, 0x39, 0xfd, 0x8a,
+ 0xf4, 0x15, 0x18, 0x11, 0xfe, 0xdb, 0xd5, 0x07,
+ 0xda, 0x2c, 0xed, 0x49, 0xa0, 0x23, 0xbf, 0xd0,
+ 0x3a, 0x38, 0x1d, 0x54, 0xae, 0x1c, 0x7b, 0xea,
+ 0x29, 0xee, 0xd0, 0x38, 0xc1, 0x76, 0xa7, 0x7f,
+ 0x2a, 0xf4, 0xce, 0x1e, 0xac, 0xcc, 0x94, 0x79,
+ 0x90, 0x33, 0x00, 0x80, 0x16, 0x83, 0x9b, 0xf9,
+ 0x72, 0xdb, 0x9f, 0x55, 0x02, 0xe1, 0x04, 0xf7,
+ 0xb5, 0x3f, 0x4c, 0x71, 0x13, 0x5a, 0x91, 0xe9,
+ 0x1d, 0xeb, 0x9d, 0x9c, 0xfb, 0x88, 0xef, 0xca,
+ 0xec, 0x7d, 0x9b, 0xdd, 0xd9, 0xee, 0x2b, 0x8e,
+ 0xef, 0xf8, 0xb6, 0xc7, 0x7d, 0xfe, 0xda, 0x7f,
+ 0x90, 0x2e, 0x53, 0xf1, 0x64, 0x95, 0xfc, 0x66,
+ 0xfc, 0x87, 0x27, 0xb6, 0x9f, 0xc8, 0x3a, 0x95,
+ 0x68, 0x17, 0xe1, 0x7d, 0xf1, 0x88, 0xe8, 0x17,
+ 0x5f, 0x99, 0x90, 0x3f, 0x47, 0x47, 0x81, 0x06,
+ 0xe2, 0x8e, 0x22, 0x56, 0x8f, 0xc2, 0x14, 0xe5,
+ 0x62, 0xa7, 0x0d, 0x41, 0x3c, 0xc7, 0x4a, 0x0a,
+ 0x74, 0x4b, 0xda, 0x00, 0x8e, 0x4f, 0x90, 0xe6,
+ 0xd7, 0x68, 0xe5, 0x8b, 0xf2, 0x3f, 0x53, 0x1d,
+ 0x7a, 0xe6, 0xb3, 0xe9, 0x8a, 0xc9, 0x4d, 0x19,
+ 0xa6, 0xcf, 0xf9, 0xed, 0x5e, 0x26, 0xdc, 0x90,
+ 0x1c, 0x41, 0xad, 0x7c, 0x16, 0x03, 0x02, 0x00,
+ 0x04, 0x0e, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x16, 0x03, 0x02, 0x00, 0x8a, 0x10, 0x00, 0x00,
+ 0x86, 0x85, 0x04, 0x01, 0x11, 0xf2, 0xa4, 0x2d,
+ 0x1a, 0x75, 0x6c, 0xbc, 0x2d, 0x91, 0x95, 0x07,
+ 0xbe, 0xd6, 0x41, 0x7a, 0xbb, 0xc2, 0x7b, 0xa6,
+ 0x9b, 0xe3, 0xdc, 0x41, 0x7f, 0x1e, 0x2e, 0xcc,
+ 0x6d, 0xa3, 0x85, 0x53, 0x98, 0x9f, 0x2d, 0xe6,
+ 0x3c, 0xb9, 0x82, 0xa6, 0x80, 0x53, 0x9b, 0x71,
+ 0xfd, 0x27, 0xe5, 0xe5, 0xdf, 0x13, 0xba, 0x56,
+ 0x62, 0x30, 0x4a, 0x57, 0x27, 0xa7, 0xcc, 0x26,
+ 0x54, 0xe8, 0x65, 0x6e, 0x4d, 0x00, 0xbf, 0x8a,
+ 0xcc, 0x89, 0x6a, 0x6c, 0x88, 0xda, 0x79, 0x4f,
+ 0xc5, 0xad, 0x6d, 0x1d, 0x7c, 0x53, 0x7b, 0x1a,
+ 0x96, 0xf2, 0xf8, 0x30, 0x01, 0x0b, 0xc2, 0xf0,
+ 0x78, 0x41, 0xf4, 0x0d, 0xe0, 0xbe, 0xb9, 0x36,
+ 0xe0, 0xb7, 0xee, 0x16, 0xeb, 0x25, 0x67, 0x04,
+ 0xc0, 0x2e, 0xd8, 0x34, 0x4a, 0x65, 0xa5, 0xf1,
+ 0x95, 0x75, 0xc7, 0x39, 0xa9, 0x68, 0xa9, 0x53,
+ 0x93, 0x5b, 0xca, 0x7b, 0x7f, 0xc0, 0x63, 0x14,
+ 0x03, 0x02, 0x00, 0x01, 0x01, 0x16, 0x03, 0x02,
+ 0x00, 0x40, 0x01, 0xb1, 0xae, 0x1b, 0x8a, 0x65,
+ 0xf8, 0x37, 0x50, 0x39, 0x76, 0xef, 0xaa, 0xda,
+ 0x84, 0xc9, 0x5f, 0x80, 0xdc, 0xfa, 0xe0, 0x46,
+ 0x5a, 0xc7, 0x77, 0x9d, 0x76, 0x03, 0xa6, 0xd5,
+ 0x0e, 0xbf, 0x25, 0x30, 0x5c, 0x99, 0x7d, 0xcd,
+ 0x2b, 0xaa, 0x2e, 0x8c, 0xdd, 0xda, 0xaa, 0xd7,
+ 0xf1, 0xf6, 0x33, 0x47, 0x51, 0x1e, 0x83, 0xa1,
+ 0x83, 0x04, 0xd2, 0xb2, 0xc8, 0xbc, 0x11, 0xc5,
+ 0x1a, 0x87,
+ },
+ {
+ 0x16, 0x03, 0x02, 0x00, 0x72, 0x04, 0x00, 0x00,
+ 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65,
+ 0xeb, 0x8b, 0xc7, 0xef, 0xba, 0xe8, 0x0f, 0x69,
+ 0xfe, 0xfb, 0xc3, 0x3d, 0x90, 0x5d, 0xd7, 0xb2,
+ 0x51, 0x64, 0xac, 0xc3, 0xae, 0x33, 0x03, 0x42,
+ 0x45, 0x2d, 0xa7, 0x57, 0xbd, 0xa3, 0x85, 0x64,
+ 0xa6, 0xfe, 0x5c, 0x33, 0x04, 0x93, 0xf2, 0x7c,
+ 0x06, 0x6d, 0xd7, 0xd7, 0xcf, 0x4a, 0xaf, 0xb2,
+ 0xdd, 0x06, 0xdc, 0x28, 0x14, 0x59, 0x23, 0x02,
+ 0xef, 0x97, 0x6a, 0xe8, 0xec, 0xca, 0x10, 0x44,
+ 0xcd, 0xb8, 0x50, 0x16, 0x46, 0x5a, 0x05, 0xda,
+ 0x04, 0xb3, 0x0e, 0xe9, 0xf0, 0x74, 0xc5, 0x23,
+ 0xc2, 0x0e, 0xa1, 0x54, 0x66, 0x7b, 0xe8, 0x14,
+ 0x03, 0x02, 0x00, 0x01, 0x01, 0x16, 0x03, 0x02,
+ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x6b, 0x43, 0x1c, 0x58, 0xbc, 0x85,
+ 0xf7, 0xc1, 0x76, 0xbc, 0x72, 0x33, 0x41, 0x6b,
+ 0xb8, 0xf8, 0xfd, 0x53, 0x21, 0xc2, 0x41, 0x1b,
+ 0x72, 0x4f, 0xce, 0x97, 0xca, 0x14, 0x23, 0x4d,
+ 0xbc, 0x44, 0xd6, 0xd7, 0xfc, 0xbc, 0xfd, 0xfd,
+ 0x5d, 0x33, 0x42, 0x1b, 0x52, 0x40, 0x0a, 0x2b,
+ 0x6c, 0x98, 0x17, 0x03, 0x02, 0x00, 0x40, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d,
+ 0x31, 0xef, 0x03, 0x7d, 0xa5, 0x74, 0x92, 0x24,
+ 0x34, 0xae, 0x4e, 0xc9, 0xfc, 0x59, 0xcb, 0x64,
+ 0xf4, 0x45, 0xb1, 0xac, 0x02, 0xf2, 0x87, 0xe7,
+ 0x2f, 0xfd, 0x01, 0xca, 0x78, 0x02, 0x2e, 0x3a,
+ 0x38, 0xcd, 0xb1, 0xe0, 0xf2, 0x2e, 0xf6, 0x27,
+ 0xa0, 0xac, 0x1f, 0x91, 0x43, 0xc2, 0x3d, 0x15,
+ 0x03, 0x02, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x9f, 0x30, 0x24, 0x56,
+ 0x2c, 0xde, 0xa0, 0xe6, 0x44, 0x35, 0x30, 0x51,
+ 0xec, 0xd4, 0x69, 0x2d, 0x46, 0x64, 0x04, 0x21,
+ 0xfe, 0x7c, 0x4d, 0xc5, 0xd0, 0x8c, 0xf9, 0xd2,
+ 0x3f, 0x88, 0x69, 0xd5,
+ },
+}
+
+// $ go test -run TestRunServer -serve -clientauth 1 \
+// -ciphersuites=0xc011 -minversion=0x0303 -maxversion=0x0303
+var tls12ServerScript = [][]byte{
+ {
+ 0x16, 0x03, 0x01, 0x01, 0x1e, 0x01, 0x00, 0x01,
+ 0x1a, 0x03, 0x03, 0x51, 0xe5, 0x76, 0x84, 0x0e,
+ 0xb9, 0x17, 0xca, 0x08, 0x47, 0xd9, 0xbd, 0xd0,
+ 0x94, 0xd1, 0x97, 0xca, 0x5b, 0xe7, 0x20, 0xac,
+ 0x8e, 0xbb, 0xc7, 0x29, 0xe9, 0x26, 0xcf, 0x7d,
+ 0xb3, 0xdc, 0x99, 0x00, 0x00, 0x82, 0xc0, 0x30,
+ 0xc0, 0x2c, 0xc0, 0x28, 0xc0, 0x24, 0xc0, 0x14,
+ 0xc0, 0x0a, 0x00, 0xa3, 0x00, 0x9f, 0x00, 0x6b,
+ 0x00, 0x6a, 0x00, 0x39, 0x00, 0x38, 0xc0, 0x32,
+ 0xc0, 0x2e, 0xc0, 0x2a, 0xc0, 0x26, 0xc0, 0x0f,
+ 0xc0, 0x05, 0x00, 0x9d, 0x00, 0x3d, 0x00, 0x35,
+ 0xc0, 0x12, 0xc0, 0x08, 0x00, 0x16, 0x00, 0x13,
+ 0xc0, 0x0d, 0xc0, 0x03, 0x00, 0x0a, 0xc0, 0x2f,
+ 0xc0, 0x2b, 0xc0, 0x27, 0xc0, 0x23, 0xc0, 0x13,
+ 0xc0, 0x09, 0x00, 0xa2, 0x00, 0x9e, 0x00, 0x67,
+ 0x00, 0x40, 0x00, 0x33, 0x00, 0x32, 0xc0, 0x31,
+ 0xc0, 0x2d, 0xc0, 0x29, 0xc0, 0x25, 0xc0, 0x0e,
+ 0xc0, 0x04, 0x00, 0x9c, 0x00, 0x3c, 0x00, 0x2f,
+ 0x00, 0x07, 0xc0, 0x11, 0xc0, 0x07, 0xc0, 0x0c,
+ 0xc0, 0x02, 0x00, 0x05, 0x00, 0x04, 0x00, 0x15,
+ 0x00, 0x12, 0x00, 0x09, 0x00, 0x14, 0x00, 0x11,
+ 0x00, 0x08, 0x00, 0x06, 0x00, 0x03, 0x00, 0xff,
+ 0x01, 0x00, 0x00, 0x6f, 0x00, 0x0b, 0x00, 0x04,
+ 0x03, 0x00, 0x01, 0x02, 0x00, 0x0a, 0x00, 0x34,
+ 0x00, 0x32, 0x00, 0x0e, 0x00, 0x0d, 0x00, 0x19,
+ 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x18, 0x00, 0x09,
+ 0x00, 0x0a, 0x00, 0x16, 0x00, 0x17, 0x00, 0x08,
+ 0x00, 0x06, 0x00, 0x07, 0x00, 0x14, 0x00, 0x15,
+ 0x00, 0x04, 0x00, 0x05, 0x00, 0x12, 0x00, 0x13,
+ 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x0f,
+ 0x00, 0x10, 0x00, 0x11, 0x00, 0x23, 0x00, 0x00,
+ 0x00, 0x0d, 0x00, 0x22, 0x00, 0x20, 0x06, 0x01,
+ 0x06, 0x02, 0x06, 0x03, 0x05, 0x01, 0x05, 0x02,
+ 0x05, 0x03, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03,
+ 0x03, 0x01, 0x03, 0x02, 0x03, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x02, 0x03, 0x01, 0x01, 0x00, 0x0f,
+ 0x00, 0x01, 0x01,
+ },
+ {
+ 0x16, 0x03, 0x03, 0x00, 0x30, 0x02, 0x00, 0x00,
+ 0x2c, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xc0, 0x11, 0x00, 0x00,
+ 0x04, 0x00, 0x23, 0x00, 0x00, 0x16, 0x03, 0x03,
+ 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba, 0x00, 0x02,
+ 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, 0x02, 0xb0,
+ 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, 0xbb, 0xa4,
+ 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, 0x0b, 0x30,
+ 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03,
+ 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d,
+ 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31,
+ 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a,
+ 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e,
+ 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69,
+ 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c,
+ 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30,
+ 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39,
+ 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, 0x31, 0x30,
+ 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39, 0x33,
+ 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41,
+ 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55,
+ 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65,
+ 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21,
+ 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
+ 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74,
+ 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74,
+ 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30,
+ 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xbb, 0x79,
+ 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf, 0x46, 0x10,
+ 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, 0x07, 0x43,
+ 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a, 0x43, 0x85,
+ 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, 0x4c, 0x2c,
+ 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4, 0x82, 0xe5,
+ 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, 0xa5, 0x2c,
+ 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c, 0x7a, 0x56,
+ 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, 0x7b, 0x26,
+ 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0, 0xc9, 0x21,
+ 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, 0x5a, 0xbf,
+ 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18, 0x99, 0x07,
+ 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, 0x04, 0x39,
+ 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9, 0x7c, 0xe3,
+ 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, 0xcf, 0xaf,
+ 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d, 0xdb, 0xdb,
+ 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, 0x02, 0x03,
+ 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, 0x30, 0x81,
+ 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e,
+ 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad, 0xe2, 0x85,
+ 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce, 0x23,
+ 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88, 0x39,
+ 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
+ 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1, 0xad, 0xe2,
+ 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce,
+ 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88,
+ 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, 0x45, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11,
+ 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53,
+ 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74,
+ 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55,
+ 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65,
+ 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64,
+ 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79,
+ 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, 0x00, 0x85,
+ 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30,
+ 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05,
+ 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00,
+ 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b, 0xb1, 0x59,
+ 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, 0x14, 0xd7,
+ 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5, 0x5a, 0x95,
+ 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, 0x12, 0x66,
+ 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e, 0x60, 0xd3,
+ 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, 0x25, 0x13,
+ 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30, 0x1d, 0xba,
+ 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, 0xd7, 0x31,
+ 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78, 0xea, 0x50,
+ 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, 0x5a, 0x5f,
+ 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75, 0x90, 0x96,
+ 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, 0x98, 0x1f,
+ 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c, 0xa3, 0x1b,
+ 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, 0xe9, 0x70,
+ 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b, 0x26, 0x6e,
+ 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, 0xbd, 0xd9,
+ 0x16, 0x03, 0x03, 0x01, 0x11, 0x0c, 0x00, 0x01,
+ 0x0d, 0x03, 0x00, 0x19, 0x85, 0x04, 0x01, 0x39,
+ 0xdc, 0xee, 0x44, 0x17, 0x5e, 0xdb, 0xd7, 0x27,
+ 0xaf, 0xb6, 0x56, 0xd9, 0xb4, 0x43, 0x5a, 0x99,
+ 0xcf, 0xaa, 0x31, 0x37, 0x0c, 0x6f, 0x3a, 0xa0,
+ 0xf8, 0x53, 0xc4, 0x74, 0xd1, 0x91, 0x0a, 0x46,
+ 0xf5, 0x38, 0x3b, 0x5c, 0x09, 0xd8, 0x97, 0xdc,
+ 0x4b, 0xaa, 0x70, 0x26, 0x48, 0xf2, 0xd6, 0x0b,
+ 0x31, 0xc9, 0xf8, 0xd4, 0x98, 0x43, 0xe1, 0x6c,
+ 0xd5, 0xc7, 0xb2, 0x8e, 0x0b, 0x01, 0xe6, 0xb6,
+ 0x00, 0x28, 0x80, 0x7b, 0xfc, 0x96, 0x8f, 0x0d,
+ 0xa2, 0x4f, 0xb0, 0x79, 0xaf, 0xdc, 0x61, 0x28,
+ 0x63, 0x33, 0x78, 0xf6, 0x31, 0x39, 0xfd, 0x8a,
+ 0xf4, 0x15, 0x18, 0x11, 0xfe, 0xdb, 0xd5, 0x07,
+ 0xda, 0x2c, 0xed, 0x49, 0xa0, 0x23, 0xbf, 0xd0,
+ 0x3a, 0x38, 0x1d, 0x54, 0xae, 0x1c, 0x7b, 0xea,
+ 0x29, 0xee, 0xd0, 0x38, 0xc1, 0x76, 0xa7, 0x7f,
+ 0x2a, 0xf4, 0xce, 0x1e, 0xac, 0xcc, 0x94, 0x79,
+ 0x90, 0x33, 0x04, 0x01, 0x00, 0x80, 0x4a, 0xf9,
+ 0xf5, 0x0a, 0x61, 0x37, 0x7e, 0x4e, 0x92, 0xb5,
+ 0x1c, 0x91, 0x21, 0xb2, 0xb5, 0x17, 0x00, 0xbf,
+ 0x01, 0x5f, 0x30, 0xec, 0x62, 0x08, 0xd6, 0x9d,
+ 0x1a, 0x08, 0x05, 0x72, 0x8b, 0xf4, 0x49, 0x85,
+ 0xa7, 0xbf, 0x3f, 0x75, 0x58, 0x3e, 0x26, 0x82,
+ 0xc3, 0x28, 0x07, 0xf9, 0x41, 0x7d, 0x03, 0x14,
+ 0x3b, 0xc3, 0x05, 0x64, 0xff, 0x52, 0xf4, 0x75,
+ 0x6a, 0x87, 0xcd, 0xdf, 0x93, 0x31, 0x0a, 0x71,
+ 0x60, 0x17, 0xc6, 0x33, 0xf0, 0x79, 0xb6, 0x7b,
+ 0xd0, 0x9c, 0xa0, 0x5f, 0x74, 0x14, 0x2c, 0x5a,
+ 0xb4, 0x3f, 0x39, 0xf5, 0xe4, 0x9f, 0xbe, 0x6d,
+ 0x21, 0xd2, 0xa9, 0x42, 0xf7, 0xdc, 0xa6, 0x65,
+ 0xb7, 0x6a, 0x7e, 0x2e, 0x14, 0xd3, 0xf6, 0xf3,
+ 0x4b, 0x4c, 0x5b, 0x1a, 0x70, 0x7a, 0xbc, 0xb0,
+ 0x12, 0xf3, 0x6e, 0x0c, 0xcf, 0x43, 0x22, 0xae,
+ 0x5b, 0xba, 0x00, 0xf8, 0xfd, 0xaf, 0x16, 0x03,
+ 0x03, 0x00, 0x0f, 0x0d, 0x00, 0x00, 0x0b, 0x02,
+ 0x01, 0x40, 0x00, 0x04, 0x04, 0x01, 0x04, 0x03,
+ 0x00, 0x00, 0x16, 0x03, 0x03, 0x00, 0x04, 0x0e,
+ 0x00, 0x00, 0x00,
+ },
+ {
+ 0x16, 0x03, 0x03, 0x01, 0xfb, 0x0b, 0x00, 0x01,
+ 0xf7, 0x00, 0x01, 0xf4, 0x00, 0x01, 0xf1, 0x30,
+ 0x82, 0x01, 0xed, 0x30, 0x82, 0x01, 0x58, 0xa0,
+ 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, 0x00, 0x30,
+ 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x05, 0x30, 0x26, 0x31, 0x10,
+ 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x07, 0x41, 0x63, 0x6d, 0x65, 0x20, 0x43, 0x6f,
+ 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x09, 0x31, 0x32, 0x37, 0x2e, 0x30,
+ 0x2e, 0x30, 0x2e, 0x31, 0x30, 0x1e, 0x17, 0x0d,
+ 0x31, 0x31, 0x31, 0x32, 0x30, 0x38, 0x30, 0x37,
+ 0x35, 0x35, 0x31, 0x32, 0x5a, 0x17, 0x0d, 0x31,
+ 0x32, 0x31, 0x32, 0x30, 0x37, 0x30, 0x38, 0x30,
+ 0x30, 0x31, 0x32, 0x5a, 0x30, 0x26, 0x31, 0x10,
+ 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x07, 0x41, 0x63, 0x6d, 0x65, 0x20, 0x43, 0x6f,
+ 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x09, 0x31, 0x32, 0x37, 0x2e, 0x30,
+ 0x2e, 0x30, 0x2e, 0x31, 0x30, 0x81, 0x9c, 0x30,
+ 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x01, 0x03, 0x81, 0x8c, 0x00,
+ 0x30, 0x81, 0x88, 0x02, 0x81, 0x80, 0x4e, 0xd0,
+ 0x7b, 0x31, 0xe3, 0x82, 0x64, 0xd9, 0x59, 0xc0,
+ 0xc2, 0x87, 0xa4, 0x5e, 0x1e, 0x8b, 0x73, 0x33,
+ 0xc7, 0x63, 0x53, 0xdf, 0x66, 0x92, 0x06, 0x84,
+ 0xf6, 0x64, 0xd5, 0x8f, 0xe4, 0x36, 0xa7, 0x1d,
+ 0x2b, 0xe8, 0xb3, 0x20, 0x36, 0x45, 0x23, 0xb5,
+ 0xe3, 0x95, 0xae, 0xed, 0xe0, 0xf5, 0x20, 0x9c,
+ 0x8d, 0x95, 0xdf, 0x7f, 0x5a, 0x12, 0xef, 0x87,
+ 0xe4, 0x5b, 0x68, 0xe4, 0xe9, 0x0e, 0x74, 0xec,
+ 0x04, 0x8a, 0x7f, 0xde, 0x93, 0x27, 0xc4, 0x01,
+ 0x19, 0x7a, 0xbd, 0xf2, 0xdc, 0x3d, 0x14, 0xab,
+ 0xd0, 0x54, 0xca, 0x21, 0x0c, 0xd0, 0x4d, 0x6e,
+ 0x87, 0x2e, 0x5c, 0xc5, 0xd2, 0xbb, 0x4d, 0x4b,
+ 0x4f, 0xce, 0xb6, 0x2c, 0xf7, 0x7e, 0x88, 0xec,
+ 0x7c, 0xd7, 0x02, 0x91, 0x74, 0xa6, 0x1e, 0x0c,
+ 0x1a, 0xda, 0xe3, 0x4a, 0x5a, 0x2e, 0xde, 0x13,
+ 0x9c, 0x4c, 0x40, 0x88, 0x59, 0x93, 0x02, 0x03,
+ 0x01, 0x00, 0x01, 0xa3, 0x32, 0x30, 0x30, 0x30,
+ 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01,
+ 0xff, 0x04, 0x04, 0x03, 0x02, 0x00, 0xa0, 0x30,
+ 0x0d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x06,
+ 0x04, 0x04, 0x01, 0x02, 0x03, 0x04, 0x30, 0x0f,
+ 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x08, 0x30,
+ 0x06, 0x80, 0x04, 0x01, 0x02, 0x03, 0x04, 0x30,
+ 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x05, 0x03, 0x81, 0x81, 0x00,
+ 0x36, 0x1f, 0xb3, 0x7a, 0x0c, 0x75, 0xc9, 0x6e,
+ 0x37, 0x46, 0x61, 0x2b, 0xd5, 0xbd, 0xc0, 0xa7,
+ 0x4b, 0xcc, 0x46, 0x9a, 0x81, 0x58, 0x7c, 0x85,
+ 0x79, 0x29, 0xc8, 0xc8, 0xc6, 0x67, 0xdd, 0x32,
+ 0x56, 0x45, 0x2b, 0x75, 0xb6, 0xe9, 0x24, 0xa9,
+ 0x50, 0x9a, 0xbe, 0x1f, 0x5a, 0xfa, 0x1a, 0x15,
+ 0xd9, 0xcc, 0x55, 0x95, 0x72, 0x16, 0x83, 0xb9,
+ 0xc2, 0xb6, 0x8f, 0xfd, 0x88, 0x8c, 0x38, 0x84,
+ 0x1d, 0xab, 0x5d, 0x92, 0x31, 0x13, 0x4f, 0xfd,
+ 0x83, 0x3b, 0xc6, 0x9d, 0xf1, 0x11, 0x62, 0xb6,
+ 0x8b, 0xec, 0xab, 0x67, 0xbe, 0xc8, 0x64, 0xb0,
+ 0x11, 0x50, 0x46, 0x58, 0x17, 0x6b, 0x99, 0x1c,
+ 0xd3, 0x1d, 0xfc, 0x06, 0xf1, 0x0e, 0xe5, 0x96,
+ 0xa8, 0x0c, 0xf9, 0x78, 0x20, 0xb7, 0x44, 0x18,
+ 0x51, 0x8d, 0x10, 0x7e, 0x4f, 0x94, 0x67, 0xdf,
+ 0xa3, 0x4e, 0x70, 0x73, 0x8e, 0x90, 0x91, 0x85,
+ 0x16, 0x03, 0x03, 0x00, 0x8a, 0x10, 0x00, 0x00,
+ 0x86, 0x85, 0x04, 0x01, 0x5d, 0x3a, 0x92, 0x59,
+ 0x7f, 0x9a, 0x22, 0x36, 0x0e, 0x1b, 0x1d, 0x2a,
+ 0x05, 0xb7, 0xa4, 0xb6, 0x5d, 0xfc, 0x51, 0x6e,
+ 0x15, 0xe5, 0x89, 0x7c, 0xe2, 0xfa, 0x87, 0x38,
+ 0x05, 0x79, 0x15, 0x92, 0xb4, 0x8f, 0x88, 0x8f,
+ 0x9d, 0x5d, 0xa0, 0xaf, 0xf8, 0xce, 0xf9, 0x6f,
+ 0x83, 0xf4, 0x08, 0x69, 0xe4, 0x91, 0xc5, 0xed,
+ 0xb9, 0xc5, 0xa8, 0x1f, 0x4b, 0xec, 0xef, 0x91,
+ 0xc1, 0xa3, 0x34, 0x24, 0x18, 0x00, 0x2d, 0xcd,
+ 0xe6, 0x44, 0xef, 0x5a, 0x3e, 0x52, 0x63, 0x5b,
+ 0x36, 0x1f, 0x7e, 0xce, 0x9e, 0xaa, 0xda, 0x8d,
+ 0xb5, 0xc9, 0xea, 0xd8, 0x1b, 0xd1, 0x1c, 0x7c,
+ 0x07, 0xfc, 0x3c, 0x2d, 0x70, 0x1f, 0xf9, 0x4d,
+ 0xcb, 0xaa, 0xad, 0x07, 0xd5, 0x6d, 0xbd, 0xa6,
+ 0x61, 0xf3, 0x2f, 0xa3, 0x9c, 0x45, 0x02, 0x4a,
+ 0xac, 0x6c, 0xb6, 0x37, 0x95, 0xb1, 0x4a, 0xb5,
+ 0x0a, 0x4e, 0x60, 0x67, 0xd7, 0xe0, 0x04, 0x16,
+ 0x03, 0x03, 0x00, 0x88, 0x0f, 0x00, 0x00, 0x84,
+ 0x04, 0x01, 0x00, 0x80, 0x08, 0x83, 0x53, 0xf0,
+ 0xf8, 0x14, 0xf5, 0xc2, 0xd1, 0x8b, 0xf0, 0xa5,
+ 0xc1, 0xd8, 0x1a, 0x36, 0x4b, 0x75, 0x77, 0x02,
+ 0x19, 0xd8, 0x11, 0x3f, 0x5a, 0x36, 0xfc, 0xe9,
+ 0x2b, 0x4b, 0xf9, 0xfe, 0xda, 0x8a, 0x0f, 0x6e,
+ 0x3d, 0xd3, 0x52, 0x87, 0xf7, 0x9c, 0x78, 0x39,
+ 0xa8, 0xf1, 0xd7, 0xf7, 0x4e, 0x35, 0x33, 0xf9,
+ 0xc5, 0x76, 0xa8, 0x12, 0xc4, 0x91, 0x33, 0x1d,
+ 0x93, 0x8c, 0xbf, 0xb1, 0x83, 0x00, 0x90, 0xc5,
+ 0x52, 0x3e, 0xe0, 0x0a, 0xe8, 0x92, 0x75, 0xdf,
+ 0x54, 0x5f, 0x9f, 0x95, 0x76, 0x62, 0xb5, 0x85,
+ 0x69, 0xa4, 0x86, 0x85, 0x6c, 0xf3, 0x6b, 0x2a,
+ 0x72, 0x7b, 0x4d, 0x42, 0x33, 0x67, 0x4a, 0xce,
+ 0xb5, 0xdb, 0x9b, 0xae, 0xc0, 0xb0, 0x10, 0xeb,
+ 0x3b, 0xf4, 0xc2, 0x9a, 0x64, 0x47, 0x4c, 0x1e,
+ 0xa5, 0x91, 0x7f, 0x6d, 0xd1, 0x03, 0xf5, 0x4a,
+ 0x90, 0x69, 0x18, 0xb1, 0x14, 0x03, 0x03, 0x00,
+ 0x01, 0x01, 0x16, 0x03, 0x03, 0x00, 0x24, 0x59,
+ 0xfc, 0x7e, 0xae, 0xb3, 0xbf, 0xab, 0x4d, 0xdb,
+ 0x4e, 0xab, 0xa9, 0x6d, 0x6b, 0x4c, 0x60, 0xb6,
+ 0x16, 0xe0, 0xab, 0x7f, 0x52, 0x2d, 0xa1, 0xfc,
+ 0xe1, 0x80, 0xd2, 0x8a, 0xa1, 0xe5, 0x8f, 0xa1,
+ 0x70, 0x93, 0x23,
+ },
+ {
+ 0x16, 0x03, 0x03, 0x02, 0x67, 0x04, 0x00, 0x02,
+ 0x63, 0x00, 0x00, 0x00, 0x00, 0x02, 0x5d, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65,
+ 0xea, 0x8b, 0xc5, 0xef, 0xba, 0x64, 0xb7, 0x23,
+ 0x08, 0x86, 0x4f, 0x37, 0xe0, 0x8f, 0xbd, 0x75,
+ 0x71, 0x2b, 0xcb, 0x20, 0x75, 0x11, 0x3b, 0xa2,
+ 0x9e, 0x39, 0x3c, 0x03, 0xef, 0x6e, 0x41, 0xd7,
+ 0xcf, 0x1a, 0x2c, 0xf2, 0xfe, 0xc2, 0xd3, 0x65,
+ 0x59, 0x00, 0x9d, 0x03, 0xb4, 0xf2, 0x20, 0xe4,
+ 0x33, 0x80, 0xcd, 0xf6, 0xe4, 0x59, 0x22, 0xf7,
+ 0xfd, 0x88, 0x0e, 0xa4, 0x09, 0xc0, 0x0d, 0x10,
+ 0x80, 0x10, 0x79, 0xee, 0x70, 0x96, 0xdb, 0x22,
+ 0x8b, 0xb7, 0xac, 0xe0, 0x98, 0xad, 0xe9, 0xe3,
+ 0xcb, 0xea, 0x9f, 0xe6, 0x83, 0x28, 0x7c, 0x7e,
+ 0x4e, 0x9a, 0x8d, 0xd9, 0xf3, 0x86, 0xf4, 0x89,
+ 0x8b, 0x79, 0x8f, 0xbb, 0xe9, 0x74, 0x02, 0x02,
+ 0x14, 0x04, 0xea, 0xba, 0x16, 0x10, 0xa1, 0x85,
+ 0xbe, 0x4e, 0x4e, 0x92, 0xc5, 0x83, 0xf6, 0x1e,
+ 0x1f, 0xd4, 0x25, 0xc2, 0xc2, 0xb9, 0xce, 0x33,
+ 0x63, 0x66, 0x79, 0x1f, 0x54, 0x35, 0xc1, 0xe8,
+ 0x89, 0x34, 0x78, 0x94, 0x36, 0x14, 0xef, 0x01,
+ 0x1f, 0xf1, 0xbd, 0x77, 0x2c, 0x4d, 0xac, 0x5c,
+ 0x5c, 0x4a, 0xc6, 0xed, 0xd8, 0x0e, 0x72, 0x84,
+ 0x83, 0xdc, 0x56, 0x84, 0xc8, 0xf3, 0x89, 0x56,
+ 0xfd, 0x89, 0xc1, 0xc9, 0x9a, 0x29, 0x91, 0x7e,
+ 0x19, 0xe9, 0x8b, 0x5b, 0x11, 0x15, 0x4e, 0x6c,
+ 0xf4, 0x89, 0xe7, 0x6d, 0x68, 0x1e, 0xf9, 0x6c,
+ 0x23, 0x72, 0x05, 0x68, 0x82, 0x60, 0x84, 0x1f,
+ 0x83, 0x20, 0x09, 0x86, 0x10, 0x81, 0xec, 0xec,
+ 0xdc, 0x25, 0x53, 0x20, 0xfa, 0xa9, 0x41, 0x64,
+ 0xd6, 0x20, 0xf3, 0xf4, 0x52, 0xf2, 0x80, 0x62,
+ 0x83, 0xc9, 0x23, 0x66, 0x44, 0x95, 0x5a, 0x99,
+ 0x8a, 0xe1, 0x26, 0x63, 0xc1, 0x8b, 0x31, 0xf9,
+ 0x21, 0x06, 0x77, 0x04, 0x27, 0xf2, 0x0c, 0x63,
+ 0x83, 0x45, 0xa0, 0xa9, 0x7b, 0xcf, 0xdf, 0xd7,
+ 0x56, 0x75, 0xbc, 0xdd, 0x95, 0x36, 0xb1, 0x75,
+ 0x39, 0x05, 0x00, 0x3c, 0x8a, 0x79, 0xd6, 0xe9,
+ 0xf0, 0x4b, 0xdc, 0x51, 0x6b, 0x01, 0x94, 0x16,
+ 0x87, 0x12, 0x92, 0x6c, 0x07, 0xc1, 0xf5, 0x58,
+ 0xb7, 0x2a, 0x81, 0xf5, 0xa0, 0x37, 0x8b, 0xa6,
+ 0x22, 0xfe, 0x28, 0x0a, 0x7e, 0x68, 0xe2, 0xda,
+ 0x6c, 0x53, 0xee, 0x0e, 0x8d, 0x2d, 0x8b, 0x0b,
+ 0xda, 0xf8, 0x99, 0x3e, 0x0e, 0xed, 0x9f, 0xc1,
+ 0x2b, 0xf6, 0xfe, 0xe9, 0x52, 0x38, 0x7b, 0x83,
+ 0x9a, 0x50, 0xa6, 0xd7, 0x49, 0x83, 0x43, 0x7e,
+ 0x82, 0xec, 0xc7, 0x09, 0x3d, 0x3d, 0xb1, 0xee,
+ 0xe8, 0xc5, 0x6a, 0xc3, 0x3d, 0x4b, 0x4c, 0x6a,
+ 0xbb, 0x0b, 0x2c, 0x24, 0x2e, 0xdb, 0x7d, 0x57,
+ 0x87, 0xb4, 0x80, 0xa5, 0xae, 0xff, 0x54, 0xa8,
+ 0xa5, 0x27, 0x69, 0x95, 0xc8, 0xe7, 0x79, 0xc7,
+ 0x89, 0x2a, 0x73, 0x49, 0xcb, 0xf5, 0xc5, 0xbc,
+ 0x4a, 0xe0, 0x73, 0xa9, 0xbc, 0x88, 0x64, 0x96,
+ 0x98, 0xa5, 0x1e, 0xe3, 0x43, 0xc1, 0x7d, 0x78,
+ 0xc7, 0x94, 0x72, 0xd4, 0x2c, 0x6e, 0x85, 0x39,
+ 0x9a, 0xaf, 0xdb, 0xa1, 0xe9, 0xe2, 0xcb, 0x37,
+ 0x04, 0xc6, 0x8c, 0x81, 0xd3, 0x2a, 0xb7, 0xbe,
+ 0x6c, 0x07, 0x1f, 0x5e, 0xd9, 0x00, 0xd2, 0xf7,
+ 0xe1, 0xa7, 0xbc, 0x0c, 0xb6, 0x6d, 0xfb, 0x3f,
+ 0x3d, 0x24, 0xaa, 0xfb, 0x7e, 0xe1, 0xb5, 0x1b,
+ 0xff, 0x38, 0xaa, 0x69, 0x59, 0x38, 0x52, 0x9a,
+ 0x0e, 0x6d, 0xbc, 0xde, 0x4f, 0x13, 0x09, 0x17,
+ 0xc4, 0xa9, 0x05, 0x84, 0xbc, 0x50, 0xef, 0x40,
+ 0xb0, 0x4c, 0x24, 0x32, 0xed, 0x94, 0x2c, 0xdd,
+ 0xda, 0x20, 0x24, 0x67, 0xe2, 0xea, 0x71, 0x3d,
+ 0x4a, 0x04, 0x0d, 0x98, 0x29, 0x20, 0x4c, 0xeb,
+ 0x70, 0xce, 0x45, 0x9e, 0x5a, 0xaf, 0xb6, 0xa3,
+ 0x92, 0xc8, 0x28, 0xf2, 0xe3, 0xe8, 0x8a, 0x5d,
+ 0x0a, 0x33, 0x79, 0x9b, 0x6a, 0xf3, 0x30, 0x01,
+ 0x1d, 0x47, 0xbd, 0x01, 0xcc, 0x4d, 0x71, 0xc0,
+ 0x56, 0xfa, 0xfd, 0x37, 0xed, 0x0f, 0x27, 0xc0,
+ 0xbb, 0xa0, 0xee, 0xc3, 0x79, 0x8b, 0xe7, 0x41,
+ 0x8f, 0xfa, 0x3a, 0xcb, 0x45, 0x3b, 0x85, 0x9f,
+ 0x06, 0x90, 0xb2, 0x51, 0xc0, 0x48, 0x10, 0xac,
+ 0x2a, 0xec, 0xec, 0x48, 0x7a, 0x19, 0x47, 0xc4,
+ 0x2a, 0xeb, 0xb3, 0xa2, 0x07, 0x22, 0x32, 0x78,
+ 0xf4, 0x73, 0x5e, 0x92, 0x42, 0x15, 0xa1, 0x90,
+ 0x91, 0xd0, 0xeb, 0x12, 0x14, 0x03, 0x03, 0x00,
+ 0x01, 0x01, 0x16, 0x03, 0x03, 0x00, 0x24, 0x45,
+ 0x4b, 0x80, 0x42, 0x46, 0xde, 0xbb, 0xe7, 0x76,
+ 0xd1, 0x33, 0x92, 0xfc, 0x46, 0x17, 0x6d, 0x21,
+ 0xf6, 0x0e, 0x16, 0xca, 0x9b, 0x9b, 0x04, 0x65,
+ 0x16, 0x40, 0x44, 0x64, 0xbc, 0x58, 0xfa, 0x2a,
+ 0x49, 0xe9, 0xed, 0x17, 0x03, 0x03, 0x00, 0x21,
+ 0x89, 0x71, 0xcd, 0x56, 0x54, 0xbf, 0x73, 0xde,
+ 0xfb, 0x4b, 0x4e, 0xf1, 0x7f, 0xc6, 0x75, 0xa6,
+ 0xbd, 0x6b, 0x6c, 0xd9, 0xdc, 0x0c, 0x71, 0xb4,
+ 0xb9, 0xbb, 0x6e, 0xfa, 0x9e, 0xc7, 0xc7, 0x4c,
+ 0x24, 0x15, 0x03, 0x03, 0x00, 0x16, 0x62, 0xea,
+ 0x65, 0x69, 0x68, 0x4a, 0xce, 0xa7, 0x9e, 0xce,
+ 0xc0, 0xf1, 0x5c, 0x96, 0xd9, 0x1f, 0x49, 0xac,
+ 0x2d, 0x05, 0x89, 0x94,
+ },
+}
+
// cert.pem and key.pem were generated with generate_cert.go
// Thus, they have no ExtKeyUsage fields and trigger an error
// when verification is turned on.
@@ -1999,3 +3167,630 @@ qTdQRYlHRftgnWK1AkANibn9PRYJ7mJyJ9Dyj2QeNcSkSTzrt0tPvUMf4+meJymN
1Ntu5+S1DLLzfxlaljWG6ylW6DNxujCyuXIV2rvA
-----END RSA PRIVATE KEY-----
*/
+
+var clientECDSACertificate = loadPEMCert(`
+-----BEGIN CERTIFICATE-----
+MIIB/DCCAV4CCQCaMIRsJjXZFzAJBgcqhkjOPQQBMEUxCzAJBgNVBAYTAkFVMRMw
+EQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0
+eSBMdGQwHhcNMTIxMTE0MTMyNTUzWhcNMjIxMTEyMTMyNTUzWjBBMQswCQYDVQQG
+EwJBVTEMMAoGA1UECBMDTlNXMRAwDgYDVQQHEwdQeXJtb250MRIwEAYDVQQDEwlK
+b2VsIFNpbmcwgZswEAYHKoZIzj0CAQYFK4EEACMDgYYABACVjJF1FMBexFe01MNv
+ja5oHt1vzobhfm6ySD6B5U7ixohLZNz1MLvT/2XMW/TdtWo+PtAd3kfDdq0Z9kUs
+jLzYHQFMH3CQRnZIi4+DzEpcj0B22uCJ7B0rxE4wdihBsmKo+1vx+U56jb0JuK7q
+ixgnTy5w/hOWusPTQBbNZU6sER7m8TAJBgcqhkjOPQQBA4GMADCBiAJCAOAUxGBg
+C3JosDJdYUoCdFzCgbkWqD8pyDbHgf9stlvZcPE4O1BIKJTLCRpS8V3ujfK58PDa
+2RU6+b0DeoeiIzXsAkIBo9SKeDUcSpoj0gq+KxAxnZxfvuiRs9oa9V2jI/Umi0Vw
+jWVim34BmT0Y9hCaOGGbLlfk+syxis7iI6CH8OFnUes=
+-----END CERTIFICATE-----
+`)
+
+/* corresponding key for cert is:
+-----BEGIN EC PARAMETERS-----
+BgUrgQQAIw==
+-----END EC PARAMETERS-----
+-----BEGIN EC PRIVATE KEY-----
+MIHcAgEBBEIBkJN9X4IqZIguiEVKMqeBUP5xtRsEv4HJEtOpOGLELwO53SD78Ew8
+k+wLWoqizS3NpQyMtrU8JFdWfj+C57UNkOugBwYFK4EEACOhgYkDgYYABACVjJF1
+FMBexFe01MNvja5oHt1vzobhfm6ySD6B5U7ixohLZNz1MLvT/2XMW/TdtWo+PtAd
+3kfDdq0Z9kUsjLzYHQFMH3CQRnZIi4+DzEpcj0B22uCJ7B0rxE4wdihBsmKo+1vx
++U56jb0JuK7qixgnTy5w/hOWusPTQBbNZU6sER7m8Q==
+-----END EC PRIVATE KEY-----
+*/
+var clientauthECDSATests = []clientauthTest{
+ // Server asks for cert with empty CA list, client gives one
+ // go test -run "TestRunServer" -serve \
+ // -clientauth 1 -ciphersuites=0xc00a
+ // openssl s_client -host 127.0.0.1 -port 10443 \
+ // -cipher ECDHE-ECDSA-AES256-SHA -key client.key -cert client.crt
+ {"RequestClientCert, client gives it", RequestClientCert, []*x509.Certificate{clientECDSACertificate}, [][]byte{
+ {
+ 0x16, 0x03, 0x01, 0x00, 0xa0, 0x01, 0x00, 0x00,
+ 0x9c, 0x03, 0x03, 0x51, 0xe5, 0x73, 0xc5, 0xae,
+ 0x51, 0x94, 0xb4, 0xf2, 0xe8, 0xf6, 0x03, 0x0e,
+ 0x3b, 0x34, 0xaf, 0xf0, 0xdc, 0x1b, 0xcc, 0xd8,
+ 0x0c, 0x45, 0x82, 0xd4, 0xd6, 0x76, 0x04, 0x6e,
+ 0x4f, 0x7a, 0x24, 0x00, 0x00, 0x04, 0xc0, 0x0a,
+ 0x00, 0xff, 0x01, 0x00, 0x00, 0x6f, 0x00, 0x0b,
+ 0x00, 0x04, 0x03, 0x00, 0x01, 0x02, 0x00, 0x0a,
+ 0x00, 0x34, 0x00, 0x32, 0x00, 0x0e, 0x00, 0x0d,
+ 0x00, 0x19, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x18,
+ 0x00, 0x09, 0x00, 0x0a, 0x00, 0x16, 0x00, 0x17,
+ 0x00, 0x08, 0x00, 0x06, 0x00, 0x07, 0x00, 0x14,
+ 0x00, 0x15, 0x00, 0x04, 0x00, 0x05, 0x00, 0x12,
+ 0x00, 0x13, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03,
+ 0x00, 0x0f, 0x00, 0x10, 0x00, 0x11, 0x00, 0x23,
+ 0x00, 0x00, 0x00, 0x0d, 0x00, 0x22, 0x00, 0x20,
+ 0x06, 0x01, 0x06, 0x02, 0x06, 0x03, 0x05, 0x01,
+ 0x05, 0x02, 0x05, 0x03, 0x04, 0x01, 0x04, 0x02,
+ 0x04, 0x03, 0x03, 0x01, 0x03, 0x02, 0x03, 0x03,
+ 0x02, 0x01, 0x02, 0x02, 0x02, 0x03, 0x01, 0x01,
+ 0x00, 0x0f, 0x00, 0x01, 0x01,
+ },
+ {
+ 0x16, 0x03, 0x01, 0x00, 0x30, 0x02, 0x00, 0x00,
+ 0x2c, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xc0, 0x0a, 0x00, 0x00,
+ 0x04, 0x00, 0x23, 0x00, 0x00, 0x16, 0x03, 0x01,
+ 0x02, 0x0e, 0x0b, 0x00, 0x02, 0x0a, 0x00, 0x02,
+ 0x07, 0x00, 0x02, 0x04, 0x30, 0x82, 0x02, 0x00,
+ 0x30, 0x82, 0x01, 0x62, 0x02, 0x09, 0x00, 0xb8,
+ 0xbf, 0x2d, 0x47, 0xa0, 0xd2, 0xeb, 0xf4, 0x30,
+ 0x09, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d,
+ 0x04, 0x01, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41,
+ 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55,
+ 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65,
+ 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21,
+ 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
+ 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74,
+ 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74,
+ 0x64, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x32, 0x31,
+ 0x31, 0x32, 0x32, 0x31, 0x35, 0x30, 0x36, 0x33,
+ 0x32, 0x5a, 0x17, 0x0d, 0x32, 0x32, 0x31, 0x31,
+ 0x32, 0x30, 0x31, 0x35, 0x30, 0x36, 0x33, 0x32,
+ 0x5a, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, 0x55,
+ 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04,
+ 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d,
+ 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21, 0x30,
+ 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18,
+ 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74,
+ 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, 0x73,
+ 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64,
+ 0x30, 0x81, 0x9b, 0x30, 0x10, 0x06, 0x07, 0x2a,
+ 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x05,
+ 0x2b, 0x81, 0x04, 0x00, 0x23, 0x03, 0x81, 0x86,
+ 0x00, 0x04, 0x00, 0xc4, 0xa1, 0xed, 0xbe, 0x98,
+ 0xf9, 0x0b, 0x48, 0x73, 0x36, 0x7e, 0xc3, 0x16,
+ 0x56, 0x11, 0x22, 0xf2, 0x3d, 0x53, 0xc3, 0x3b,
+ 0x4d, 0x21, 0x3d, 0xcd, 0x6b, 0x75, 0xe6, 0xf6,
+ 0xb0, 0xdc, 0x9a, 0xdf, 0x26, 0xc1, 0xbc, 0xb2,
+ 0x87, 0xf0, 0x72, 0x32, 0x7c, 0xb3, 0x64, 0x2f,
+ 0x1c, 0x90, 0xbc, 0xea, 0x68, 0x23, 0x10, 0x7e,
+ 0xfe, 0xe3, 0x25, 0xc0, 0x48, 0x3a, 0x69, 0xe0,
+ 0x28, 0x6d, 0xd3, 0x37, 0x00, 0xef, 0x04, 0x62,
+ 0xdd, 0x0d, 0xa0, 0x9c, 0x70, 0x62, 0x83, 0xd8,
+ 0x81, 0xd3, 0x64, 0x31, 0xaa, 0x9e, 0x97, 0x31,
+ 0xbd, 0x96, 0xb0, 0x68, 0xc0, 0x9b, 0x23, 0xde,
+ 0x76, 0x64, 0x3f, 0x1a, 0x5c, 0x7f, 0xe9, 0x12,
+ 0x0e, 0x58, 0x58, 0xb6, 0x5f, 0x70, 0xdd, 0x9b,
+ 0xd8, 0xea, 0xd5, 0xd7, 0xf5, 0xd5, 0xcc, 0xb9,
+ 0xb6, 0x9f, 0x30, 0x66, 0x5b, 0x66, 0x9a, 0x20,
+ 0xe2, 0x27, 0xe5, 0xbf, 0xfe, 0x3b, 0x30, 0x09,
+ 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04,
+ 0x01, 0x03, 0x81, 0x8c, 0x00, 0x30, 0x81, 0x88,
+ 0x02, 0x42, 0x01, 0x88, 0xa2, 0x4f, 0xeb, 0xe2,
+ 0x45, 0xc5, 0x48, 0x7d, 0x1b, 0xac, 0xf5, 0xed,
+ 0x98, 0x9d, 0xae, 0x47, 0x70, 0xc0, 0x5e, 0x1b,
+ 0xb6, 0x2f, 0xbd, 0xf1, 0xb6, 0x4d, 0xb7, 0x61,
+ 0x40, 0xd3, 0x11, 0xa2, 0xce, 0xee, 0x0b, 0x7e,
+ 0x92, 0x7e, 0xff, 0x76, 0x9d, 0xc3, 0x3b, 0x7e,
+ 0xa5, 0x3f, 0xce, 0xfa, 0x10, 0xe2, 0x59, 0xec,
+ 0x47, 0x2d, 0x7c, 0xac, 0xda, 0x4e, 0x97, 0x0e,
+ 0x15, 0xa0, 0x6f, 0xd0, 0x02, 0x42, 0x01, 0x4d,
+ 0xfc, 0xbe, 0x67, 0x13, 0x9c, 0x2d, 0x05, 0x0e,
+ 0xbd, 0x3f, 0xa3, 0x8c, 0x25, 0xc1, 0x33, 0x13,
+ 0x83, 0x0d, 0x94, 0x06, 0xbb, 0xd4, 0x37, 0x7a,
+ 0xf6, 0xec, 0x7a, 0xc9, 0x86, 0x2e, 0xdd, 0xd7,
+ 0x11, 0x69, 0x7f, 0x85, 0x7c, 0x56, 0xde, 0xfb,
+ 0x31, 0x78, 0x2b, 0xe4, 0xc7, 0x78, 0x0d, 0xae,
+ 0xcb, 0xbe, 0x9e, 0x4e, 0x36, 0x24, 0x31, 0x7b,
+ 0x6a, 0x0f, 0x39, 0x95, 0x12, 0x07, 0x8f, 0x2a,
+ 0x16, 0x03, 0x01, 0x01, 0x1a, 0x0c, 0x00, 0x01,
+ 0x16, 0x03, 0x00, 0x19, 0x85, 0x04, 0x01, 0x39,
+ 0xdc, 0xee, 0x44, 0x17, 0x5e, 0xdb, 0xd7, 0x27,
+ 0xaf, 0xb6, 0x56, 0xd9, 0xb4, 0x43, 0x5a, 0x99,
+ 0xcf, 0xaa, 0x31, 0x37, 0x0c, 0x6f, 0x3a, 0xa0,
+ 0xf8, 0x53, 0xc4, 0x74, 0xd1, 0x91, 0x0a, 0x46,
+ 0xf5, 0x38, 0x3b, 0x5c, 0x09, 0xd8, 0x97, 0xdc,
+ 0x4b, 0xaa, 0x70, 0x26, 0x48, 0xf2, 0xd6, 0x0b,
+ 0x31, 0xc9, 0xf8, 0xd4, 0x98, 0x43, 0xe1, 0x6c,
+ 0xd5, 0xc7, 0xb2, 0x8e, 0x0b, 0x01, 0xe6, 0xb6,
+ 0x00, 0x28, 0x80, 0x7b, 0xfc, 0x96, 0x8f, 0x0d,
+ 0xa2, 0x4f, 0xb0, 0x79, 0xaf, 0xdc, 0x61, 0x28,
+ 0x63, 0x33, 0x78, 0xf6, 0x31, 0x39, 0xfd, 0x8a,
+ 0xf4, 0x15, 0x18, 0x11, 0xfe, 0xdb, 0xd5, 0x07,
+ 0xda, 0x2c, 0xed, 0x49, 0xa0, 0x23, 0xbf, 0xd0,
+ 0x3a, 0x38, 0x1d, 0x54, 0xae, 0x1c, 0x7b, 0xea,
+ 0x29, 0xee, 0xd0, 0x38, 0xc1, 0x76, 0xa7, 0x7f,
+ 0x2a, 0xf4, 0xce, 0x1e, 0xac, 0xcc, 0x94, 0x79,
+ 0x90, 0x33, 0x00, 0x8b, 0x30, 0x81, 0x88, 0x02,
+ 0x42, 0x00, 0xc6, 0x85, 0x8e, 0x06, 0xb7, 0x04,
+ 0x04, 0xe9, 0xcd, 0x9e, 0x3e, 0xcb, 0x66, 0x23,
+ 0x95, 0xb4, 0x42, 0x9c, 0x64, 0x81, 0x39, 0x05,
+ 0x3f, 0xb5, 0x21, 0xf8, 0x28, 0xaf, 0x60, 0x6b,
+ 0x4d, 0x3d, 0xba, 0xa1, 0x4b, 0x5e, 0x77, 0xef,
+ 0xe7, 0x59, 0x28, 0xfe, 0x1d, 0xc1, 0x27, 0xa2,
+ 0xff, 0xa8, 0xde, 0x33, 0x48, 0xb3, 0xc1, 0x85,
+ 0x6a, 0x42, 0x9b, 0xf9, 0x7e, 0x7e, 0x31, 0xc2,
+ 0xe5, 0xbd, 0x66, 0x02, 0x42, 0x00, 0xad, 0x7d,
+ 0x06, 0x35, 0xab, 0xec, 0x8d, 0xac, 0xd4, 0xba,
+ 0x1b, 0x49, 0x5e, 0x05, 0x5f, 0xf0, 0x97, 0x93,
+ 0x82, 0xb8, 0x2b, 0x8d, 0x91, 0x98, 0x63, 0x8e,
+ 0xb4, 0x14, 0x62, 0xdb, 0x1e, 0xc9, 0x2b, 0x30,
+ 0xf8, 0x41, 0x9b, 0xa6, 0xe6, 0xbc, 0xde, 0x0e,
+ 0x68, 0x30, 0x21, 0xf4, 0xa8, 0xa9, 0x1b, 0xec,
+ 0x44, 0x4f, 0x5d, 0x02, 0x2f, 0x60, 0x45, 0x60,
+ 0xba, 0xe0, 0x4e, 0xc0, 0xd4, 0x3b, 0x01, 0x16,
+ 0x03, 0x01, 0x00, 0x09, 0x0d, 0x00, 0x00, 0x05,
+ 0x02, 0x01, 0x40, 0x00, 0x00, 0x16, 0x03, 0x01,
+ 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x16, 0x03, 0x01, 0x02, 0x0a, 0x0b, 0x00, 0x02,
+ 0x06, 0x00, 0x02, 0x03, 0x00, 0x02, 0x00, 0x30,
+ 0x82, 0x01, 0xfc, 0x30, 0x82, 0x01, 0x5e, 0x02,
+ 0x09, 0x00, 0x9a, 0x30, 0x84, 0x6c, 0x26, 0x35,
+ 0xd9, 0x17, 0x30, 0x09, 0x06, 0x07, 0x2a, 0x86,
+ 0x48, 0xce, 0x3d, 0x04, 0x01, 0x30, 0x45, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11,
+ 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53,
+ 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74,
+ 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55,
+ 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65,
+ 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64,
+ 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79,
+ 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d,
+ 0x31, 0x32, 0x31, 0x31, 0x31, 0x34, 0x31, 0x33,
+ 0x32, 0x35, 0x35, 0x33, 0x5a, 0x17, 0x0d, 0x32,
+ 0x32, 0x31, 0x31, 0x31, 0x32, 0x31, 0x33, 0x32,
+ 0x35, 0x35, 0x33, 0x5a, 0x30, 0x41, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x41, 0x55, 0x31, 0x0c, 0x30, 0x0a, 0x06,
+ 0x03, 0x55, 0x04, 0x08, 0x13, 0x03, 0x4e, 0x53,
+ 0x57, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55,
+ 0x04, 0x07, 0x13, 0x07, 0x50, 0x79, 0x72, 0x6d,
+ 0x6f, 0x6e, 0x74, 0x31, 0x12, 0x30, 0x10, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x13, 0x09, 0x4a, 0x6f,
+ 0x65, 0x6c, 0x20, 0x53, 0x69, 0x6e, 0x67, 0x30,
+ 0x81, 0x9b, 0x30, 0x10, 0x06, 0x07, 0x2a, 0x86,
+ 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x05, 0x2b,
+ 0x81, 0x04, 0x00, 0x23, 0x03, 0x81, 0x86, 0x00,
+ 0x04, 0x00, 0x95, 0x8c, 0x91, 0x75, 0x14, 0xc0,
+ 0x5e, 0xc4, 0x57, 0xb4, 0xd4, 0xc3, 0x6f, 0x8d,
+ 0xae, 0x68, 0x1e, 0xdd, 0x6f, 0xce, 0x86, 0xe1,
+ 0x7e, 0x6e, 0xb2, 0x48, 0x3e, 0x81, 0xe5, 0x4e,
+ 0xe2, 0xc6, 0x88, 0x4b, 0x64, 0xdc, 0xf5, 0x30,
+ 0xbb, 0xd3, 0xff, 0x65, 0xcc, 0x5b, 0xf4, 0xdd,
+ 0xb5, 0x6a, 0x3e, 0x3e, 0xd0, 0x1d, 0xde, 0x47,
+ 0xc3, 0x76, 0xad, 0x19, 0xf6, 0x45, 0x2c, 0x8c,
+ 0xbc, 0xd8, 0x1d, 0x01, 0x4c, 0x1f, 0x70, 0x90,
+ 0x46, 0x76, 0x48, 0x8b, 0x8f, 0x83, 0xcc, 0x4a,
+ 0x5c, 0x8f, 0x40, 0x76, 0xda, 0xe0, 0x89, 0xec,
+ 0x1d, 0x2b, 0xc4, 0x4e, 0x30, 0x76, 0x28, 0x41,
+ 0xb2, 0x62, 0xa8, 0xfb, 0x5b, 0xf1, 0xf9, 0x4e,
+ 0x7a, 0x8d, 0xbd, 0x09, 0xb8, 0xae, 0xea, 0x8b,
+ 0x18, 0x27, 0x4f, 0x2e, 0x70, 0xfe, 0x13, 0x96,
+ 0xba, 0xc3, 0xd3, 0x40, 0x16, 0xcd, 0x65, 0x4e,
+ 0xac, 0x11, 0x1e, 0xe6, 0xf1, 0x30, 0x09, 0x06,
+ 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x01,
+ 0x03, 0x81, 0x8c, 0x00, 0x30, 0x81, 0x88, 0x02,
+ 0x42, 0x00, 0xe0, 0x14, 0xc4, 0x60, 0x60, 0x0b,
+ 0x72, 0x68, 0xb0, 0x32, 0x5d, 0x61, 0x4a, 0x02,
+ 0x74, 0x5c, 0xc2, 0x81, 0xb9, 0x16, 0xa8, 0x3f,
+ 0x29, 0xc8, 0x36, 0xc7, 0x81, 0xff, 0x6c, 0xb6,
+ 0x5b, 0xd9, 0x70, 0xf1, 0x38, 0x3b, 0x50, 0x48,
+ 0x28, 0x94, 0xcb, 0x09, 0x1a, 0x52, 0xf1, 0x5d,
+ 0xee, 0x8d, 0xf2, 0xb9, 0xf0, 0xf0, 0xda, 0xd9,
+ 0x15, 0x3a, 0xf9, 0xbd, 0x03, 0x7a, 0x87, 0xa2,
+ 0x23, 0x35, 0xec, 0x02, 0x42, 0x01, 0xa3, 0xd4,
+ 0x8a, 0x78, 0x35, 0x1c, 0x4a, 0x9a, 0x23, 0xd2,
+ 0x0a, 0xbe, 0x2b, 0x10, 0x31, 0x9d, 0x9c, 0x5f,
+ 0xbe, 0xe8, 0x91, 0xb3, 0xda, 0x1a, 0xf5, 0x5d,
+ 0xa3, 0x23, 0xf5, 0x26, 0x8b, 0x45, 0x70, 0x8d,
+ 0x65, 0x62, 0x9b, 0x7e, 0x01, 0x99, 0x3d, 0x18,
+ 0xf6, 0x10, 0x9a, 0x38, 0x61, 0x9b, 0x2e, 0x57,
+ 0xe4, 0xfa, 0xcc, 0xb1, 0x8a, 0xce, 0xe2, 0x23,
+ 0xa0, 0x87, 0xf0, 0xe1, 0x67, 0x51, 0xeb, 0x16,
+ 0x03, 0x01, 0x00, 0x8a, 0x10, 0x00, 0x00, 0x86,
+ 0x85, 0x04, 0x00, 0xcd, 0x1c, 0xe8, 0x66, 0x5b,
+ 0xa8, 0x9d, 0x83, 0x2f, 0x7e, 0x1d, 0x0b, 0x59,
+ 0x23, 0xbc, 0x30, 0xcf, 0xa3, 0xaf, 0x21, 0xdc,
+ 0xf2, 0x57, 0x49, 0x56, 0x30, 0x25, 0x7c, 0x84,
+ 0x5d, 0xad, 0xaa, 0x9c, 0x7b, 0x2a, 0x95, 0x58,
+ 0x3d, 0x30, 0x87, 0x01, 0x3b, 0xb7, 0xea, 0xcb,
+ 0xc4, 0xa3, 0xeb, 0x22, 0xbf, 0x2d, 0x61, 0x17,
+ 0x8c, 0x9b, 0xe8, 0x1b, 0xb2, 0x87, 0x16, 0x78,
+ 0xd5, 0xfd, 0x8b, 0xdd, 0x00, 0x0f, 0xda, 0x8e,
+ 0xfd, 0x28, 0x36, 0xeb, 0xe4, 0xc5, 0x42, 0x14,
+ 0xc7, 0xbd, 0x29, 0x5e, 0x9a, 0xed, 0x5e, 0xc1,
+ 0xf7, 0xf4, 0xbd, 0xbd, 0x15, 0x9c, 0xe8, 0x44,
+ 0x71, 0xa7, 0xb6, 0xe9, 0xfa, 0x7e, 0x97, 0xcb,
+ 0x96, 0x3e, 0x53, 0x76, 0xfb, 0x11, 0x1f, 0x36,
+ 0x8f, 0x30, 0xfb, 0x71, 0x3a, 0x75, 0x3a, 0x25,
+ 0x7b, 0xa2, 0xc1, 0xf9, 0x3e, 0x58, 0x5f, 0x07,
+ 0x16, 0xed, 0xe1, 0xf7, 0xc1, 0xb1, 0x16, 0x03,
+ 0x01, 0x00, 0x90, 0x0f, 0x00, 0x00, 0x8c, 0x00,
+ 0x8a, 0x30, 0x81, 0x87, 0x02, 0x42, 0x00, 0xb2,
+ 0xd3, 0x91, 0xe6, 0xd5, 0x9b, 0xb2, 0xb8, 0x03,
+ 0xf4, 0x85, 0x4d, 0x43, 0x79, 0x1f, 0xb6, 0x6f,
+ 0x0c, 0xcd, 0x67, 0x5f, 0x5e, 0xca, 0xee, 0xb3,
+ 0xe4, 0xab, 0x1e, 0x58, 0xc3, 0x04, 0xa9, 0x8a,
+ 0xa7, 0xcf, 0xaa, 0x33, 0x88, 0xd5, 0x35, 0xd2,
+ 0x80, 0x8f, 0xfa, 0x1b, 0x3c, 0x3d, 0xf7, 0x80,
+ 0x50, 0xde, 0x80, 0x30, 0x64, 0xee, 0xc0, 0xb3,
+ 0x91, 0x6e, 0x5d, 0x1e, 0xc0, 0xdc, 0x3a, 0x93,
+ 0x02, 0x41, 0x4e, 0xca, 0x98, 0x41, 0x8c, 0x36,
+ 0xf2, 0x12, 0xbf, 0x8e, 0x0f, 0x69, 0x8e, 0xf8,
+ 0x7b, 0x9d, 0xba, 0x9c, 0x5c, 0x48, 0x79, 0xf4,
+ 0xba, 0x3d, 0x06, 0xa5, 0xab, 0x47, 0xe0, 0x1a,
+ 0x45, 0x28, 0x3a, 0x8f, 0xbf, 0x14, 0x24, 0x36,
+ 0xd1, 0x1d, 0x29, 0xdc, 0xde, 0x72, 0x5b, 0x76,
+ 0x41, 0x67, 0xe8, 0xe5, 0x71, 0x4a, 0x77, 0xe9,
+ 0xed, 0x02, 0x19, 0xdd, 0xe4, 0xaa, 0xe9, 0x2d,
+ 0xe7, 0x47, 0x32, 0x14, 0x03, 0x01, 0x00, 0x01,
+ 0x01, 0x16, 0x03, 0x01, 0x00, 0x30, 0xfa, 0xc3,
+ 0xf2, 0x35, 0xd0, 0x6d, 0x32, 0x78, 0x6a, 0xd6,
+ 0xe6, 0x70, 0x5e, 0x00, 0x4c, 0x35, 0xf1, 0xe0,
+ 0x21, 0xcf, 0xc3, 0x78, 0xcd, 0xe0, 0x2b, 0x0b,
+ 0xf4, 0xeb, 0xf9, 0xc0, 0x38, 0xf2, 0x9a, 0x31,
+ 0x55, 0x07, 0x2b, 0x8d, 0x68, 0x40, 0x31, 0x08,
+ 0xaa, 0xe3, 0x16, 0xcf, 0x4b, 0xd4,
+ },
+ {
+ 0x16, 0x03, 0x01, 0x02, 0x76, 0x04, 0x00, 0x02,
+ 0x72, 0x00, 0x00, 0x00, 0x00, 0x02, 0x6c, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65,
+ 0xe8, 0x8b, 0xde, 0xef, 0xba, 0xf9, 0xdb, 0x95,
+ 0x24, 0xa5, 0x49, 0xb3, 0x23, 0xd8, 0x73, 0x88,
+ 0x50, 0x42, 0xed, 0xeb, 0xa3, 0xd8, 0xab, 0x31,
+ 0x9c, 0xd0, 0x00, 0x01, 0xef, 0xc0, 0xbf, 0xab,
+ 0x59, 0x55, 0xb5, 0xb9, 0xef, 0xa5, 0xa6, 0xec,
+ 0x69, 0xed, 0x00, 0x2f, 0x47, 0xdb, 0x75, 0x52,
+ 0x0c, 0xe5, 0x86, 0xb7, 0x02, 0x59, 0x22, 0xf7,
+ 0xfd, 0x8b, 0xff, 0xa4, 0x09, 0xc0, 0x1c, 0x10,
+ 0x80, 0x10, 0x7f, 0x4c, 0x7a, 0x94, 0x40, 0x10,
+ 0x0d, 0xda, 0x8a, 0xe5, 0x4a, 0xbc, 0xd0, 0xc0,
+ 0x4b, 0xa5, 0x33, 0x97, 0xc6, 0xe7, 0x40, 0x7f,
+ 0x7f, 0x8c, 0xf9, 0xf8, 0xc8, 0xb8, 0xfb, 0x8c,
+ 0xdd, 0x28, 0x81, 0xae, 0xfd, 0x37, 0x20, 0x3a,
+ 0x40, 0x37, 0x99, 0xc4, 0x21, 0x01, 0xc4, 0x91,
+ 0xb0, 0x5e, 0x11, 0xc5, 0xa9, 0xfd, 0x9a, 0x02,
+ 0x7e, 0x97, 0x6a, 0x86, 0x89, 0xb8, 0xc1, 0x32,
+ 0x4c, 0x7e, 0x6d, 0x47, 0x61, 0x0e, 0xe3, 0xc2,
+ 0xf0, 0x62, 0x3c, 0xc6, 0x71, 0x4f, 0xbb, 0x47,
+ 0x65, 0xb1, 0xd9, 0x22, 0x79, 0x15, 0xea, 0x1f,
+ 0x4b, 0x2a, 0x8a, 0xa4, 0xc8, 0x73, 0x34, 0xba,
+ 0x83, 0xe4, 0x70, 0x99, 0xc9, 0xcf, 0xbe, 0x64,
+ 0x99, 0xb9, 0xfa, 0xe9, 0xaf, 0x5d, 0xc7, 0x20,
+ 0x26, 0xde, 0xc5, 0x06, 0x12, 0x36, 0x4f, 0x4d,
+ 0xc0, 0xbb, 0x81, 0x5b, 0x5e, 0x38, 0xc3, 0x07,
+ 0x21, 0x04, 0x1a, 0x53, 0x9c, 0x59, 0xac, 0x2d,
+ 0xe6, 0xa5, 0x93, 0xa5, 0x19, 0xc6, 0xb0, 0xf7,
+ 0x56, 0x5d, 0xdf, 0xd1, 0xf4, 0xfd, 0x44, 0x6d,
+ 0xc6, 0xa2, 0x31, 0xa7, 0x35, 0x42, 0x18, 0x50,
+ 0x0c, 0x4f, 0x6e, 0xe3, 0x3b, 0xa3, 0xaa, 0x1c,
+ 0xbe, 0x41, 0x0d, 0xce, 0x6c, 0x62, 0xe1, 0x96,
+ 0x2d, 0xbd, 0x14, 0x31, 0xe3, 0xc4, 0x5b, 0xbf,
+ 0xf6, 0xde, 0xec, 0x42, 0xe8, 0xc7, 0x2a, 0x0b,
+ 0xdb, 0x2d, 0x7c, 0xf0, 0x3f, 0x45, 0x32, 0x45,
+ 0x09, 0x47, 0x09, 0x0f, 0x21, 0x22, 0x45, 0x06,
+ 0x11, 0xb8, 0xf9, 0xe6, 0x67, 0x90, 0x4b, 0x4a,
+ 0xde, 0x81, 0xfb, 0xeb, 0xe7, 0x9a, 0x08, 0x30,
+ 0xcf, 0x51, 0xe1, 0xd9, 0xfa, 0x79, 0xa3, 0xcc,
+ 0x65, 0x1a, 0x83, 0x86, 0xc9, 0x7a, 0x41, 0xf5,
+ 0xdf, 0xa0, 0x7c, 0x44, 0x23, 0x17, 0xf3, 0x62,
+ 0xe8, 0xa9, 0x31, 0x1e, 0x6b, 0x05, 0x4b, 0x4f,
+ 0x9d, 0x91, 0x46, 0x92, 0xa6, 0x25, 0x32, 0xca,
+ 0xa1, 0x75, 0xda, 0xe6, 0x80, 0x3e, 0x7f, 0xd1,
+ 0x26, 0x57, 0x07, 0x42, 0xe4, 0x91, 0xff, 0xbd,
+ 0x44, 0xae, 0x98, 0x5c, 0x1d, 0xdf, 0x11, 0xe3,
+ 0xae, 0x87, 0x5e, 0xb7, 0x69, 0xad, 0x34, 0x7f,
+ 0x3a, 0x07, 0x7c, 0xdf, 0xfc, 0x76, 0x17, 0x8b,
+ 0x62, 0xc8, 0xe1, 0x78, 0x2a, 0xc8, 0xb9, 0x8a,
+ 0xbb, 0x5c, 0xfb, 0x38, 0x74, 0x91, 0x6e, 0x12,
+ 0x0c, 0x1f, 0x8e, 0xe1, 0xc2, 0x01, 0xd8, 0x9d,
+ 0x23, 0x0f, 0xc4, 0x67, 0x5d, 0xe5, 0x67, 0x4b,
+ 0x94, 0x6e, 0x69, 0x72, 0x90, 0x2d, 0x52, 0x78,
+ 0x8e, 0x61, 0xba, 0xdf, 0x4e, 0xf5, 0xdc, 0xfb,
+ 0x73, 0xbe, 0x03, 0x70, 0xd9, 0x01, 0x30, 0xf3,
+ 0xa1, 0xbb, 0x9a, 0x5f, 0xec, 0x9e, 0xed, 0x8d,
+ 0xdd, 0x53, 0xfd, 0x60, 0xc3, 0x2b, 0x7a, 0x00,
+ 0x2c, 0xf9, 0x0a, 0x57, 0x47, 0x45, 0x43, 0xb3,
+ 0x23, 0x01, 0x9c, 0xee, 0x54, 0x4d, 0x58, 0xd3,
+ 0x71, 0x1c, 0xc9, 0xd3, 0x30, 0x9e, 0x14, 0xa5,
+ 0xf3, 0xbf, 0x4d, 0x9b, 0xb7, 0x13, 0x21, 0xae,
+ 0xd2, 0x8d, 0x6e, 0x6f, 0x1c, 0xcc, 0xb2, 0x41,
+ 0xb2, 0x64, 0x56, 0x83, 0xce, 0xd1, 0x0c, 0x79,
+ 0x32, 0x78, 0xef, 0xc5, 0x21, 0xb1, 0xe8, 0xc4,
+ 0x42, 0xa7, 0x8d, 0xc1, 0xfa, 0xa1, 0x9c, 0x3c,
+ 0x21, 0xd8, 0xe9, 0x90, 0xe2, 0x7c, 0x14, 0x26,
+ 0xfe, 0x61, 0x3e, 0xf9, 0x71, 0x1d, 0x5d, 0x49,
+ 0x3b, 0xb1, 0xb8, 0x42, 0xa1, 0xb8, 0x1c, 0x75,
+ 0x7d, 0xee, 0xed, 0xfc, 0xe6, 0x20, 0x2b, 0x9e,
+ 0x10, 0x52, 0xda, 0x56, 0x4d, 0x64, 0x6c, 0x41,
+ 0xc1, 0xf7, 0x60, 0x0c, 0x10, 0x65, 0x6f, 0xd4,
+ 0xe9, 0x9b, 0x0d, 0x83, 0x13, 0xc8, 0x5a, 0xa3,
+ 0x56, 0x2a, 0x42, 0xc6, 0x1c, 0xfe, 0xdb, 0xba,
+ 0x3d, 0x04, 0x12, 0xfd, 0x28, 0xeb, 0x78, 0xdd,
+ 0xbc, 0xc8, 0x0d, 0xa1, 0xce, 0xd4, 0x54, 0xbf,
+ 0xaf, 0xe1, 0x60, 0x0c, 0xa3, 0xc3, 0xc3, 0x62,
+ 0x58, 0xc1, 0x79, 0xa7, 0x95, 0x41, 0x09, 0x24,
+ 0xc6, 0x9a, 0x50, 0x14, 0x03, 0x01, 0x00, 0x01,
+ 0x01, 0x16, 0x03, 0x01, 0x00, 0x30, 0x4d, 0x7b,
+ 0x5f, 0x28, 0x5e, 0x68, 0x6c, 0xa3, 0x65, 0xc7,
+ 0x7e, 0x49, 0x6c, 0xb3, 0x67, 0xbb, 0xd0, 0x75,
+ 0xa2, 0x9e, 0x8c, 0x92, 0x4f, 0x8c, 0x33, 0x14,
+ 0x7c, 0x6c, 0xf1, 0x74, 0x97, 0xc3, 0xe0, 0x10,
+ 0xe9, 0x0d, 0xc2, 0x30, 0x5c, 0x23, 0xee, 0x1d,
+ 0x16, 0x2e, 0xb9, 0x96, 0x2b, 0x2d, 0x17, 0x03,
+ 0x01, 0x00, 0x20, 0xf2, 0xc8, 0xa7, 0x1b, 0x60,
+ 0x46, 0xee, 0xe5, 0x7e, 0xc9, 0x35, 0xb3, 0xf1,
+ 0x7c, 0x32, 0x0c, 0x85, 0x94, 0x59, 0x57, 0x27,
+ 0xb0, 0xbd, 0x52, 0x86, 0x90, 0xf1, 0xb7, 0x4d,
+ 0x1e, 0xc1, 0x16, 0x17, 0x03, 0x01, 0x00, 0x30,
+ 0xff, 0x85, 0x50, 0xdf, 0x3f, 0xfc, 0xa2, 0x61,
+ 0x1a, 0x12, 0xc0, 0x1e, 0x10, 0x32, 0x88, 0x50,
+ 0xa0, 0x2c, 0x80, 0xda, 0x77, 0xea, 0x09, 0x47,
+ 0xe0, 0x85, 0x07, 0x29, 0x45, 0x65, 0x19, 0xa3,
+ 0x8d, 0x99, 0xb8, 0xbf, 0xb6, 0xbc, 0x76, 0xe2,
+ 0x50, 0x24, 0x82, 0x0a, 0xfd, 0xdd, 0x35, 0x09,
+ 0x15, 0x03, 0x01, 0x00, 0x20, 0xe7, 0x36, 0xf6,
+ 0x61, 0xd2, 0x95, 0x3c, 0xb6, 0x65, 0x7b, 0xb2,
+ 0xb8, 0xdf, 0x03, 0x53, 0xeb, 0xf7, 0x16, 0xe0,
+ 0xe0, 0x15, 0x22, 0x71, 0x70, 0x62, 0x73, 0xad,
+ 0xb5, 0x1a, 0x77, 0x44, 0x57,
+ },
+ }},
+}
+
+var aesGCMServerScript = [][]byte{
+ {
+ 0x16, 0x03, 0x01, 0x01, 0x1c, 0x01, 0x00, 0x01,
+ 0x18, 0x03, 0x03, 0x52, 0x1e, 0x74, 0xf0, 0xb0,
+ 0xc1, 0x8b, 0x16, 0xf9, 0x74, 0xfc, 0x16, 0xc4,
+ 0x11, 0x18, 0x96, 0x08, 0x25, 0x38, 0x4f, 0x98,
+ 0x98, 0xbe, 0xb5, 0x61, 0xdf, 0x94, 0x15, 0xcc,
+ 0x9b, 0x61, 0xef, 0x00, 0x00, 0x80, 0xc0, 0x30,
+ 0xc0, 0x2c, 0xc0, 0x28, 0xc0, 0x24, 0xc0, 0x14,
+ 0xc0, 0x0a, 0x00, 0xa3, 0x00, 0x9f, 0x00, 0x6b,
+ 0x00, 0x6a, 0x00, 0x39, 0x00, 0x38, 0xc0, 0x32,
+ 0xc0, 0x2e, 0xc0, 0x2a, 0xc0, 0x26, 0xc0, 0x0f,
+ 0xc0, 0x05, 0x00, 0x9d, 0x00, 0x3d, 0x00, 0x35,
+ 0xc0, 0x12, 0xc0, 0x08, 0x00, 0x16, 0x00, 0x13,
+ 0xc0, 0x0d, 0xc0, 0x03, 0x00, 0x0a, 0xc0, 0x2f,
+ 0xc0, 0x2b, 0xc0, 0x27, 0xc0, 0x23, 0xc0, 0x13,
+ 0xc0, 0x09, 0x00, 0xa2, 0x00, 0x9e, 0x00, 0x67,
+ 0x00, 0x40, 0x00, 0x33, 0x00, 0x32, 0xc0, 0x31,
+ 0xc0, 0x2d, 0xc0, 0x29, 0xc0, 0x25, 0xc0, 0x0e,
+ 0xc0, 0x04, 0x00, 0x9c, 0x00, 0x3c, 0x00, 0x2f,
+ 0xc0, 0x11, 0xc0, 0x07, 0xc0, 0x0c, 0xc0, 0x02,
+ 0x00, 0x05, 0x00, 0x04, 0x00, 0x15, 0x00, 0x12,
+ 0x00, 0x09, 0x00, 0x14, 0x00, 0x11, 0x00, 0x08,
+ 0x00, 0x06, 0x00, 0x03, 0x00, 0xff, 0x01, 0x00,
+ 0x00, 0x6f, 0x00, 0x0b, 0x00, 0x04, 0x03, 0x00,
+ 0x01, 0x02, 0x00, 0x0a, 0x00, 0x34, 0x00, 0x32,
+ 0x00, 0x0e, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x0b,
+ 0x00, 0x0c, 0x00, 0x18, 0x00, 0x09, 0x00, 0x0a,
+ 0x00, 0x16, 0x00, 0x17, 0x00, 0x08, 0x00, 0x06,
+ 0x00, 0x07, 0x00, 0x14, 0x00, 0x15, 0x00, 0x04,
+ 0x00, 0x05, 0x00, 0x12, 0x00, 0x13, 0x00, 0x01,
+ 0x00, 0x02, 0x00, 0x03, 0x00, 0x0f, 0x00, 0x10,
+ 0x00, 0x11, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0d,
+ 0x00, 0x22, 0x00, 0x20, 0x06, 0x01, 0x06, 0x02,
+ 0x06, 0x03, 0x05, 0x01, 0x05, 0x02, 0x05, 0x03,
+ 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x03, 0x01,
+ 0x03, 0x02, 0x03, 0x03, 0x02, 0x01, 0x02, 0x02,
+ 0x02, 0x03, 0x01, 0x01, 0x00, 0x0f, 0x00, 0x01,
+ 0x01,
+ },
+ {
+ 0x16, 0x03, 0x03, 0x00, 0x30, 0x02, 0x00, 0x00,
+ 0x2c, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xc0, 0x2f, 0x00, 0x00,
+ 0x04, 0x00, 0x23, 0x00, 0x00, 0x16, 0x03, 0x03,
+ 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba, 0x00, 0x02,
+ 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, 0x02, 0xb0,
+ 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, 0xbb, 0xa4,
+ 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, 0x0b, 0x30,
+ 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03,
+ 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d,
+ 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31,
+ 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a,
+ 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e,
+ 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69,
+ 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c,
+ 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30,
+ 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39,
+ 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, 0x31, 0x30,
+ 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39, 0x33,
+ 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41,
+ 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55,
+ 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65,
+ 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21,
+ 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
+ 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74,
+ 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74,
+ 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30,
+ 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xbb, 0x79,
+ 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf, 0x46, 0x10,
+ 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, 0x07, 0x43,
+ 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a, 0x43, 0x85,
+ 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, 0x4c, 0x2c,
+ 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4, 0x82, 0xe5,
+ 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, 0xa5, 0x2c,
+ 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c, 0x7a, 0x56,
+ 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, 0x7b, 0x26,
+ 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0, 0xc9, 0x21,
+ 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, 0x5a, 0xbf,
+ 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18, 0x99, 0x07,
+ 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, 0x04, 0x39,
+ 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9, 0x7c, 0xe3,
+ 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, 0xcf, 0xaf,
+ 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d, 0xdb, 0xdb,
+ 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, 0x02, 0x03,
+ 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, 0x30, 0x81,
+ 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e,
+ 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad, 0xe2, 0x85,
+ 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce, 0x23,
+ 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88, 0x39,
+ 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
+ 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1, 0xad, 0xe2,
+ 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce,
+ 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88,
+ 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, 0x45, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11,
+ 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53,
+ 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74,
+ 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55,
+ 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65,
+ 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64,
+ 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79,
+ 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, 0x00, 0x85,
+ 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30,
+ 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05,
+ 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00,
+ 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b, 0xb1, 0x59,
+ 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, 0x14, 0xd7,
+ 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5, 0x5a, 0x95,
+ 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, 0x12, 0x66,
+ 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e, 0x60, 0xd3,
+ 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, 0x25, 0x13,
+ 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30, 0x1d, 0xba,
+ 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, 0xd7, 0x31,
+ 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78, 0xea, 0x50,
+ 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, 0x5a, 0x5f,
+ 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75, 0x90, 0x96,
+ 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, 0x98, 0x1f,
+ 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c, 0xa3, 0x1b,
+ 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, 0xe9, 0x70,
+ 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b, 0x26, 0x6e,
+ 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, 0xbd, 0xd9,
+ 0x16, 0x03, 0x03, 0x01, 0x11, 0x0c, 0x00, 0x01,
+ 0x0d, 0x03, 0x00, 0x19, 0x85, 0x04, 0x01, 0x39,
+ 0xdc, 0xee, 0x44, 0x17, 0x5e, 0xdb, 0xd7, 0x27,
+ 0xaf, 0xb6, 0x56, 0xd9, 0xb4, 0x43, 0x5a, 0x99,
+ 0xcf, 0xaa, 0x31, 0x37, 0x0c, 0x6f, 0x3a, 0xa0,
+ 0xf8, 0x53, 0xc4, 0x74, 0xd1, 0x91, 0x0a, 0x46,
+ 0xf5, 0x38, 0x3b, 0x5c, 0x09, 0xd8, 0x97, 0xdc,
+ 0x4b, 0xaa, 0x70, 0x26, 0x48, 0xf2, 0xd6, 0x0b,
+ 0x31, 0xc9, 0xf8, 0xd4, 0x98, 0x43, 0xe1, 0x6c,
+ 0xd5, 0xc7, 0xb2, 0x8e, 0x0b, 0x01, 0xe6, 0xb6,
+ 0x00, 0x28, 0x80, 0x7b, 0xfc, 0x96, 0x8f, 0x0d,
+ 0xa2, 0x4f, 0xb0, 0x79, 0xaf, 0xdc, 0x61, 0x28,
+ 0x63, 0x33, 0x78, 0xf6, 0x31, 0x39, 0xfd, 0x8a,
+ 0xf4, 0x15, 0x18, 0x11, 0xfe, 0xdb, 0xd5, 0x07,
+ 0xda, 0x2c, 0xed, 0x49, 0xa0, 0x23, 0xbf, 0xd0,
+ 0x3a, 0x38, 0x1d, 0x54, 0xae, 0x1c, 0x7b, 0xea,
+ 0x29, 0xee, 0xd0, 0x38, 0xc1, 0x76, 0xa7, 0x7f,
+ 0x2a, 0xf4, 0xce, 0x1e, 0xac, 0xcc, 0x94, 0x79,
+ 0x90, 0x33, 0x04, 0x01, 0x00, 0x80, 0x0d, 0x8e,
+ 0x79, 0xe6, 0x86, 0xf6, 0xb6, 0xfb, 0x6b, 0x6a,
+ 0xcc, 0x55, 0xe4, 0x80, 0x4d, 0xc5, 0x0c, 0xc6,
+ 0xa3, 0x9f, 0x1d, 0x39, 0xd2, 0x98, 0x57, 0x31,
+ 0xa2, 0x90, 0x73, 0xe8, 0xd2, 0xcd, 0xb0, 0x93,
+ 0x1a, 0x60, 0x0f, 0x38, 0x02, 0x3b, 0x1b, 0x25,
+ 0x56, 0xec, 0x44, 0xab, 0xbe, 0x2e, 0x0c, 0xc0,
+ 0x6e, 0x54, 0x91, 0x50, 0xd6, 0xb1, 0xa2, 0x98,
+ 0x14, 0xa8, 0x35, 0x62, 0x9d, 0xca, 0xfb, 0x0f,
+ 0x64, 0x2b, 0x05, 0xa0, 0xa0, 0x57, 0xef, 0xcd,
+ 0x95, 0x45, 0x13, 0x5a, 0x9b, 0x3d, 0xdb, 0x42,
+ 0x54, 0x7f, 0xb9, 0x17, 0x08, 0x7f, 0xb2, 0xf0,
+ 0xb1, 0xc3, 0xdf, 0x67, 0x95, 0xe2, 0x73, 0xf2,
+ 0x76, 0xa3, 0x97, 0xfd, 0x9c, 0x92, 0x4a, 0xdb,
+ 0x95, 0x1e, 0x91, 0x95, 0xae, 0x3d, 0xae, 0x58,
+ 0xb5, 0x03, 0x6f, 0x5c, 0x3a, 0x19, 0xab, 0x92,
+ 0xa5, 0x09, 0x6b, 0x40, 0x61, 0xb0, 0x16, 0x03,
+ 0x03, 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x16, 0x03, 0x03, 0x00, 0x8a, 0x10, 0x00, 0x00,
+ 0x86, 0x85, 0x04, 0x01, 0xba, 0xb8, 0xad, 0x69,
+ 0x20, 0x5e, 0xc1, 0x61, 0xc3, 0x0f, 0xb4, 0x30,
+ 0x64, 0x66, 0x70, 0x96, 0x33, 0x3c, 0x8e, 0x12,
+ 0x56, 0xbf, 0x6d, 0xb8, 0x6d, 0xc6, 0xba, 0xea,
+ 0xfc, 0x38, 0xc0, 0x8b, 0x87, 0xa8, 0xf3, 0x87,
+ 0xa1, 0xd5, 0xb6, 0xb0, 0x72, 0xc7, 0xd4, 0x19,
+ 0x56, 0xa0, 0x91, 0xe1, 0x45, 0xc7, 0xf1, 0x7d,
+ 0xb0, 0x1d, 0x78, 0x18, 0xf6, 0x3d, 0xbf, 0x1a,
+ 0x23, 0x93, 0x0b, 0x19, 0xb1, 0x00, 0x56, 0xc9,
+ 0x5e, 0x89, 0xd4, 0x9d, 0xd9, 0x5b, 0xe0, 0xb8,
+ 0xff, 0x2f, 0x7d, 0x93, 0xae, 0x5b, 0xa5, 0x1f,
+ 0x1f, 0x2b, 0x09, 0xe5, 0xf6, 0x07, 0x26, 0xa3,
+ 0xed, 0xcb, 0x6a, 0x1a, 0xd6, 0x14, 0x83, 0x9b,
+ 0xd3, 0x9d, 0x47, 0x1b, 0xf3, 0x72, 0x5f, 0x69,
+ 0x21, 0x8f, 0xfa, 0x09, 0x38, 0x1a, 0x6b, 0x91,
+ 0xcf, 0x19, 0x32, 0x54, 0x58, 0x8e, 0xee, 0xaf,
+ 0xeb, 0x06, 0x9b, 0x3a, 0x34, 0x16, 0x66, 0x14,
+ 0x03, 0x03, 0x00, 0x01, 0x01, 0x16, 0x03, 0x03,
+ 0x00, 0x28, 0xc6, 0x96, 0x67, 0x62, 0xcc, 0x47,
+ 0x01, 0xb5, 0xbd, 0xb7, 0x24, 0xd3, 0xb6, 0xfd,
+ 0xb8, 0x46, 0xce, 0x82, 0x6d, 0x31, 0x1f, 0x15,
+ 0x11, 0x8f, 0xed, 0x62, 0x71, 0x5f, 0xae, 0xb6,
+ 0xa9, 0x0c, 0x24, 0x1d, 0xe8, 0x26, 0x51, 0xca,
+ 0x7c, 0x42,
+ },
+ {
+ 0x16, 0x03, 0x03, 0x00, 0x72, 0x04, 0x00, 0x00,
+ 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65,
+ 0xea, 0x8b, 0xfb, 0xef, 0xba, 0xc8, 0x88, 0x94,
+ 0x44, 0x99, 0x5f, 0x02, 0x68, 0x3a, 0x12, 0x67,
+ 0x7f, 0xb9, 0x39, 0x71, 0x84, 0xe0, 0x30, 0xe6,
+ 0x90, 0x6c, 0xcf, 0x32, 0x29, 0x29, 0x5c, 0x5a,
+ 0x8b, 0x7d, 0xaa, 0x11, 0x28, 0x26, 0xb5, 0xce,
+ 0xd2, 0x88, 0xd5, 0xb0, 0x5f, 0x94, 0x37, 0xa2,
+ 0x48, 0xd9, 0x53, 0xb2, 0xab, 0x59, 0x23, 0x3d,
+ 0x81, 0x6e, 0x64, 0x89, 0xca, 0x1a, 0x84, 0x16,
+ 0xdf, 0x31, 0x10, 0xde, 0x52, 0x7f, 0x50, 0xf3,
+ 0xd9, 0x27, 0xa0, 0xe8, 0x34, 0x15, 0x9e, 0x11,
+ 0xdd, 0xba, 0xce, 0x40, 0x17, 0xf3, 0x67, 0x14,
+ 0x03, 0x03, 0x00, 0x01, 0x01, 0x16, 0x03, 0x03,
+ 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x35, 0xcb, 0x17, 0x66, 0xee, 0xfd,
+ 0x27, 0xdb, 0xb8, 0xa8, 0x8a, 0xf1, 0x56, 0x67,
+ 0x89, 0x0d, 0x13, 0xac, 0xe2, 0x31, 0xb9, 0xa2,
+ 0x26, 0xbb, 0x1c, 0xcf, 0xd1, 0xb2, 0x48, 0x1d,
+ 0x0d, 0xb1, 0x17, 0x03, 0x03, 0x00, 0x25, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc0,
+ 0x89, 0x7c, 0x58, 0x6a, 0x9b, 0x00, 0x05, 0x8c,
+ 0x7f, 0x28, 0x54, 0x61, 0x44, 0x10, 0xee, 0x85,
+ 0x26, 0xa8, 0x04, 0xcd, 0xca, 0x85, 0x60, 0xf2,
+ 0xeb, 0x22, 0xbd, 0x9e, 0x15, 0x03, 0x03, 0x00,
+ 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x10, 0xe4, 0xe5, 0xf9, 0x85, 0xe3, 0xb0,
+ 0xec, 0x84, 0x29, 0x91, 0x05, 0x7d, 0x86, 0xe3,
+ 0x97, 0xeb, 0xb2,
+ },
+}
diff --git a/src/pkg/crypto/tls/key_agreement.go b/src/pkg/crypto/tls/key_agreement.go
index b6e73fe29..7e820c1e7 100644
--- a/src/pkg/crypto/tls/key_agreement.go
+++ b/src/pkg/crypto/tls/key_agreement.go
@@ -6,11 +6,14 @@ package tls
import (
"crypto"
+ "crypto/ecdsa"
"crypto/elliptic"
"crypto/md5"
"crypto/rsa"
"crypto/sha1"
+ "crypto/sha256"
"crypto/x509"
+ "encoding/asn1"
"errors"
"io"
"math/big"
@@ -36,7 +39,7 @@ func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, cert *Certifi
}
ciphertext := ckx.ciphertext
- if version != versionSSL30 {
+ if version != VersionSSL30 {
ciphertextLen := int(ckx.ciphertext[0])<<8 | int(ckx.ciphertext[1])
if ciphertextLen != len(ckx.ciphertext)-2 {
return nil, errors.New("bad ClientKeyExchange")
@@ -82,34 +85,94 @@ func (ka rsaKeyAgreement) generateClientKeyExchange(config *Config, clientHello
return preMasterSecret, ckx, nil
}
+// sha1Hash calculates a SHA1 hash over the given byte slices.
+func sha1Hash(slices [][]byte) []byte {
+ hsha1 := sha1.New()
+ for _, slice := range slices {
+ hsha1.Write(slice)
+ }
+ return hsha1.Sum(nil)
+}
+
// md5SHA1Hash implements TLS 1.0's hybrid hash function which consists of the
// concatenation of an MD5 and SHA1 hash.
-func md5SHA1Hash(slices ...[]byte) []byte {
+func md5SHA1Hash(slices [][]byte) []byte {
md5sha1 := make([]byte, md5.Size+sha1.Size)
hmd5 := md5.New()
for _, slice := range slices {
hmd5.Write(slice)
}
copy(md5sha1, hmd5.Sum(nil))
+ copy(md5sha1[md5.Size:], sha1Hash(slices))
+ return md5sha1
+}
- hsha1 := sha1.New()
+// sha256Hash implements TLS 1.2's hash function.
+func sha256Hash(slices [][]byte) []byte {
+ h := sha256.New()
for _, slice := range slices {
- hsha1.Write(slice)
+ h.Write(slice)
}
- copy(md5sha1[md5.Size:], hsha1.Sum(nil))
- return md5sha1
+ return h.Sum(nil)
+}
+
+// hashForServerKeyExchange hashes the given slices and returns their digest
+// and the identifier of the hash function used. The hashFunc argument is only
+// used for >= TLS 1.2 and precisely identifies the hash function to use.
+func hashForServerKeyExchange(sigType, hashFunc uint8, version uint16, slices ...[]byte) ([]byte, crypto.Hash, error) {
+ if version >= VersionTLS12 {
+ switch hashFunc {
+ case hashSHA256:
+ return sha256Hash(slices), crypto.SHA256, nil
+ case hashSHA1:
+ return sha1Hash(slices), crypto.SHA1, nil
+ default:
+ return nil, crypto.Hash(0), errors.New("tls: unknown hash function used by peer")
+ }
+ }
+ if sigType == signatureECDSA {
+ return sha1Hash(slices), crypto.SHA1, nil
+ }
+ return md5SHA1Hash(slices), crypto.MD5SHA1, nil
+}
+
+// pickTLS12HashForSignature returns a TLS 1.2 hash identifier for signing a
+// ServerKeyExchange given the signature type being used and the client's
+// advertized list of supported signature and hash combinations.
+func pickTLS12HashForSignature(sigType uint8, clientSignatureAndHashes []signatureAndHash) (uint8, error) {
+ if len(clientSignatureAndHashes) == 0 {
+ // If the client didn't specify any signature_algorithms
+ // extension then we can assume that it supports SHA1. See
+ // http://tools.ietf.org/html/rfc5246#section-7.4.1.4.1
+ return hashSHA1, nil
+ }
+
+ for _, sigAndHash := range clientSignatureAndHashes {
+ if sigAndHash.signature != sigType {
+ continue
+ }
+ switch sigAndHash.hash {
+ case hashSHA1, hashSHA256:
+ return sigAndHash.hash, nil
+ }
+ }
+
+ return 0, errors.New("tls: client doesn't support any common hash functions")
}
// ecdheRSAKeyAgreement implements a TLS key agreement where the server
// generates a ephemeral EC public/private key pair and signs it. The
-// pre-master secret is then calculated using ECDH.
-type ecdheRSAKeyAgreement struct {
+// pre-master secret is then calculated using ECDH. The signature may
+// either be ECDSA or RSA.
+type ecdheKeyAgreement struct {
+ version uint16
+ sigType uint8
privateKey []byte
curve elliptic.Curve
x, y *big.Int
}
-func (ka *ecdheRSAKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
+func (ka *ecdheKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
var curveid uint16
Curve:
@@ -150,16 +213,55 @@ Curve:
serverECDHParams[3] = byte(len(ecdhePublic))
copy(serverECDHParams[4:], ecdhePublic)
- md5sha1 := md5SHA1Hash(clientHello.random, hello.random, serverECDHParams)
- sig, err := rsa.SignPKCS1v15(config.rand(), cert.PrivateKey.(*rsa.PrivateKey), crypto.MD5SHA1, md5sha1)
+ var tls12HashId uint8
+ if ka.version >= VersionTLS12 {
+ if tls12HashId, err = pickTLS12HashForSignature(ka.sigType, clientHello.signatureAndHashes); err != nil {
+ return nil, err
+ }
+ }
+
+ digest, hashFunc, err := hashForServerKeyExchange(ka.sigType, tls12HashId, ka.version, clientHello.random, hello.random, serverECDHParams)
if err != nil {
- return nil, errors.New("failed to sign ECDHE parameters: " + err.Error())
+ return nil, err
+ }
+ var sig []byte
+ switch ka.sigType {
+ case signatureECDSA:
+ privKey, ok := cert.PrivateKey.(*ecdsa.PrivateKey)
+ if !ok {
+ return nil, errors.New("ECDHE ECDSA requires an ECDSA server private key")
+ }
+ r, s, err := ecdsa.Sign(config.rand(), privKey, digest)
+ if err != nil {
+ return nil, errors.New("failed to sign ECDHE parameters: " + err.Error())
+ }
+ sig, err = asn1.Marshal(ecdsaSignature{r, s})
+ case signatureRSA:
+ privKey, ok := cert.PrivateKey.(*rsa.PrivateKey)
+ if !ok {
+ return nil, errors.New("ECDHE RSA requires a RSA server private key")
+ }
+ sig, err = rsa.SignPKCS1v15(config.rand(), privKey, hashFunc, digest)
+ if err != nil {
+ return nil, errors.New("failed to sign ECDHE parameters: " + err.Error())
+ }
+ default:
+ return nil, errors.New("unknown ECDHE signature algorithm")
}
skx := new(serverKeyExchangeMsg)
- skx.key = make([]byte, len(serverECDHParams)+2+len(sig))
+ sigAndHashLen := 0
+ if ka.version >= VersionTLS12 {
+ sigAndHashLen = 2
+ }
+ skx.key = make([]byte, len(serverECDHParams)+sigAndHashLen+2+len(sig))
copy(skx.key, serverECDHParams)
k := skx.key[len(serverECDHParams):]
+ if ka.version >= VersionTLS12 {
+ k[0] = tls12HashId
+ k[1] = ka.sigType
+ k = k[2:]
+ }
k[0] = byte(len(sig) >> 8)
k[1] = byte(len(sig))
copy(k[2:], sig)
@@ -167,7 +269,7 @@ Curve:
return skx, nil
}
-func (ka *ecdheRSAKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
+func (ka *ecdheKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
if len(ckx.ciphertext) == 0 || int(ckx.ciphertext[0]) != len(ckx.ciphertext)-1 {
return nil, errors.New("bad ClientKeyExchange")
}
@@ -185,7 +287,7 @@ func (ka *ecdheRSAKeyAgreement) processClientKeyExchange(config *Config, cert *C
var errServerKeyExchange = errors.New("invalid ServerKeyExchange")
-func (ka *ecdheRSAKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error {
+func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error {
if len(skx.key) < 4 {
return errServerKeyExchange
}
@@ -219,17 +321,62 @@ func (ka *ecdheRSAKeyAgreement) processServerKeyExchange(config *Config, clientH
if len(sig) < 2 {
return errServerKeyExchange
}
+
+ var tls12HashId uint8
+ if ka.version >= VersionTLS12 {
+ // handle SignatureAndHashAlgorithm
+ var sigAndHash []uint8
+ sigAndHash, sig = sig[:2], sig[2:]
+ if sigAndHash[1] != ka.sigType {
+ return errServerKeyExchange
+ }
+ tls12HashId = sigAndHash[0]
+ if len(sig) < 2 {
+ return errServerKeyExchange
+ }
+ }
sigLen := int(sig[0])<<8 | int(sig[1])
if sigLen+2 != len(sig) {
return errServerKeyExchange
}
sig = sig[2:]
- md5sha1 := md5SHA1Hash(clientHello.random, serverHello.random, serverECDHParams)
- return rsa.VerifyPKCS1v15(cert.PublicKey.(*rsa.PublicKey), crypto.MD5SHA1, md5sha1, sig)
+ digest, hashFunc, err := hashForServerKeyExchange(ka.sigType, tls12HashId, ka.version, clientHello.random, serverHello.random, serverECDHParams)
+ if err != nil {
+ return err
+ }
+ switch ka.sigType {
+ case signatureECDSA:
+ pubKey, ok := cert.PublicKey.(*ecdsa.PublicKey)
+ if !ok {
+ return errors.New("ECDHE ECDSA requires a ECDSA server public key")
+ }
+ ecdsaSig := new(ecdsaSignature)
+ if _, err := asn1.Unmarshal(sig, ecdsaSig); err != nil {
+ return err
+ }
+ if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 {
+ return errors.New("ECDSA signature contained zero or negative values")
+ }
+ if !ecdsa.Verify(pubKey, digest, ecdsaSig.R, ecdsaSig.S) {
+ return errors.New("ECDSA verification failure")
+ }
+ case signatureRSA:
+ pubKey, ok := cert.PublicKey.(*rsa.PublicKey)
+ if !ok {
+ return errors.New("ECDHE RSA requires a RSA server public key")
+ }
+ if err := rsa.VerifyPKCS1v15(pubKey, hashFunc, digest, sig); err != nil {
+ return err
+ }
+ default:
+ return errors.New("unknown ECDHE signature algorithm")
+ }
+
+ return nil
}
-func (ka *ecdheRSAKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) {
+func (ka *ecdheKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) {
if ka.curve == nil {
return nil, nil, errors.New("missing ServerKeyExchange message")
}
diff --git a/src/pkg/crypto/tls/prf.go b/src/pkg/crypto/tls/prf.go
index df1eaad05..fb8b3ab4d 100644
--- a/src/pkg/crypto/tls/prf.go
+++ b/src/pkg/crypto/tls/prf.go
@@ -5,9 +5,11 @@
package tls
import (
+ "crypto"
"crypto/hmac"
"crypto/md5"
"crypto/sha1"
+ "crypto/sha256"
"hash"
)
@@ -43,8 +45,8 @@ func pHash(result, secret, seed []byte, hash func() hash.Hash) {
}
}
-// pRF10 implements the TLS 1.0 pseudo-random function, as defined in RFC 2246, section 5.
-func pRF10(result, secret, label, seed []byte) {
+// prf10 implements the TLS 1.0 pseudo-random function, as defined in RFC 2246, section 5.
+func prf10(result, secret, label, seed []byte) {
hashSHA1 := sha1.New
hashMD5 := md5.New
@@ -62,9 +64,18 @@ func pRF10(result, secret, label, seed []byte) {
}
}
-// pRF30 implements the SSL 3.0 pseudo-random function, as defined in
+// prf12 implements the TLS 1.2 pseudo-random function, as defined in RFC 5246, section 5.
+func prf12(result, secret, label, seed []byte) {
+ labelAndSeed := make([]byte, len(label)+len(seed))
+ copy(labelAndSeed, label)
+ copy(labelAndSeed[len(label):], seed)
+
+ pHash(result, secret, labelAndSeed, sha256.New)
+}
+
+// prf30 implements the SSL 3.0 pseudo-random function, as defined in
// www.mozilla.org/projects/security/pki/nss/ssl/draft302.txt section 6.
-func pRF30(result, secret, label, seed []byte) {
+func prf30(result, secret, label, seed []byte) {
hashSHA1 := sha1.New()
hashMD5 := md5.New()
@@ -106,19 +117,27 @@ var keyExpansionLabel = []byte("key expansion")
var clientFinishedLabel = []byte("client finished")
var serverFinishedLabel = []byte("server finished")
+func prfForVersion(version uint16) func(result, secret, label, seed []byte) {
+ switch version {
+ case VersionSSL30:
+ return prf30
+ case VersionTLS10, VersionTLS11:
+ return prf10
+ case VersionTLS12:
+ return prf12
+ default:
+ panic("unknown version")
+ }
+}
+
// masterFromPreMasterSecret generates the master secret from the pre-master
// secret. See http://tools.ietf.org/html/rfc5246#section-8.1
func masterFromPreMasterSecret(version uint16, preMasterSecret, clientRandom, serverRandom []byte) []byte {
- prf := pRF10
- if version == versionSSL30 {
- prf = pRF30
- }
-
var seed [tlsRandomLength * 2]byte
copy(seed[0:len(clientRandom)], clientRandom)
copy(seed[len(clientRandom):], serverRandom)
masterSecret := make([]byte, masterSecretLength)
- prf(masterSecret, preMasterSecret, masterSecretLabel, seed[0:])
+ prfForVersion(version)(masterSecret, preMasterSecret, masterSecretLabel, seed[0:])
return masterSecret
}
@@ -126,18 +145,13 @@ func masterFromPreMasterSecret(version uint16, preMasterSecret, clientRandom, se
// secret, given the lengths of the MAC key, cipher key and IV, as defined in
// RFC 2246, section 6.3.
func keysFromMasterSecret(version uint16, masterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int) (clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV []byte) {
- prf := pRF10
- if version == versionSSL30 {
- prf = pRF30
- }
-
var seed [tlsRandomLength * 2]byte
copy(seed[0:len(clientRandom)], serverRandom)
copy(seed[len(serverRandom):], clientRandom)
n := 2*macLen + 2*keyLen + 2*ivLen
keyMaterial := make([]byte, n)
- prf(keyMaterial, masterSecret, keyExpansionLabel, seed[0:])
+ prfForVersion(version)(keyMaterial, masterSecret, keyExpansionLabel, seed[0:])
clientMAC = keyMaterial[:macLen]
keyMaterial = keyMaterial[macLen:]
serverMAC = keyMaterial[:macLen]
@@ -153,37 +167,34 @@ func keysFromMasterSecret(version uint16, masterSecret, clientRandom, serverRand
}
func newFinishedHash(version uint16) finishedHash {
- return finishedHash{md5.New(), sha1.New(), md5.New(), sha1.New(), version}
+ if version >= VersionTLS12 {
+ return finishedHash{sha256.New(), sha256.New(), nil, nil, version}
+ }
+ return finishedHash{sha1.New(), sha1.New(), md5.New(), md5.New(), version}
}
// A finishedHash calculates the hash of a set of handshake messages suitable
// for including in a Finished message.
type finishedHash struct {
- clientMD5 hash.Hash
- clientSHA1 hash.Hash
- serverMD5 hash.Hash
- serverSHA1 hash.Hash
- version uint16
+ client hash.Hash
+ server hash.Hash
+
+ // Prior to TLS 1.2, an additional MD5 hash is required.
+ clientMD5 hash.Hash
+ serverMD5 hash.Hash
+
+ version uint16
}
func (h finishedHash) Write(msg []byte) (n int, err error) {
- h.clientMD5.Write(msg)
- h.clientSHA1.Write(msg)
- h.serverMD5.Write(msg)
- h.serverSHA1.Write(msg)
- return len(msg), nil
-}
+ h.client.Write(msg)
+ h.server.Write(msg)
-// finishedSum10 calculates the contents of the verify_data member of a TLSv1
-// Finished message given the MD5 and SHA1 hashes of a set of handshake
-// messages.
-func finishedSum10(md5, sha1, label, masterSecret []byte) []byte {
- seed := make([]byte, len(md5)+len(sha1))
- copy(seed, md5)
- copy(seed[len(md5):], sha1)
- out := make([]byte, finishedVerifyLength)
- pRF10(out, masterSecret, label, seed)
- return out
+ if h.version < VersionTLS12 {
+ h.clientMD5.Write(msg)
+ h.serverMD5.Write(msg)
+ }
+ return len(msg), nil
}
// finishedSum30 calculates the contents of the verify_data member of a SSLv3
@@ -224,23 +235,57 @@ var ssl3ServerFinishedMagic = [4]byte{0x53, 0x52, 0x56, 0x52}
// clientSum returns the contents of the verify_data member of a client's
// Finished message.
func (h finishedHash) clientSum(masterSecret []byte) []byte {
- if h.version == versionSSL30 {
- return finishedSum30(h.clientMD5, h.clientSHA1, masterSecret, ssl3ClientFinishedMagic)
+ if h.version == VersionSSL30 {
+ return finishedSum30(h.clientMD5, h.client, masterSecret, ssl3ClientFinishedMagic)
}
- md5 := h.clientMD5.Sum(nil)
- sha1 := h.clientSHA1.Sum(nil)
- return finishedSum10(md5, sha1, clientFinishedLabel, masterSecret)
+ out := make([]byte, finishedVerifyLength)
+ if h.version >= VersionTLS12 {
+ seed := h.client.Sum(nil)
+ prf12(out, masterSecret, clientFinishedLabel, seed)
+ } else {
+ seed := make([]byte, 0, md5.Size+sha1.Size)
+ seed = h.clientMD5.Sum(seed)
+ seed = h.client.Sum(seed)
+ prf10(out, masterSecret, clientFinishedLabel, seed)
+ }
+ return out
}
// serverSum returns the contents of the verify_data member of a server's
// Finished message.
func (h finishedHash) serverSum(masterSecret []byte) []byte {
- if h.version == versionSSL30 {
- return finishedSum30(h.serverMD5, h.serverSHA1, masterSecret, ssl3ServerFinishedMagic)
+ if h.version == VersionSSL30 {
+ return finishedSum30(h.serverMD5, h.server, masterSecret, ssl3ServerFinishedMagic)
+ }
+
+ out := make([]byte, finishedVerifyLength)
+ if h.version >= VersionTLS12 {
+ seed := h.server.Sum(nil)
+ prf12(out, masterSecret, serverFinishedLabel, seed)
+ } else {
+ seed := make([]byte, 0, md5.Size+sha1.Size)
+ seed = h.serverMD5.Sum(seed)
+ seed = h.server.Sum(seed)
+ prf10(out, masterSecret, serverFinishedLabel, seed)
+ }
+ return out
+}
+
+// hashForClientCertificate returns a digest, hash function, and TLS 1.2 hash
+// id suitable for signing by a TLS client certificate.
+func (h finishedHash) hashForClientCertificate(sigType uint8) ([]byte, crypto.Hash, uint8) {
+ if h.version >= VersionTLS12 {
+ digest := h.server.Sum(nil)
+ return digest, crypto.SHA256, hashSHA256
+ }
+ if sigType == signatureECDSA {
+ digest := h.server.Sum(nil)
+ return digest, crypto.SHA1, hashSHA1
}
- md5 := h.serverMD5.Sum(nil)
- sha1 := h.serverSHA1.Sum(nil)
- return finishedSum10(md5, sha1, serverFinishedLabel, masterSecret)
+ digest := make([]byte, 0, 36)
+ digest = h.serverMD5.Sum(digest)
+ digest = h.server.Sum(digest)
+ return digest, crypto.MD5SHA1, 0 /* not specified in TLS 1.2. */
}
diff --git a/src/pkg/crypto/tls/prf_test.go b/src/pkg/crypto/tls/prf_test.go
index 773a2b2ff..a9b6c9e4c 100644
--- a/src/pkg/crypto/tls/prf_test.go
+++ b/src/pkg/crypto/tls/prf_test.go
@@ -72,7 +72,7 @@ func TestKeysFromPreMasterSecret(t *testing.T) {
// These test vectors were generated from GnuTLS using `gnutls-cli --insecure -d 9 `
var testKeysFromTests = []testKeysFromTest{
{
- versionTLS10,
+ VersionTLS10,
"0302cac83ad4b1db3b9ab49ad05957de2a504a634a386fc600889321e1a971f57479466830ac3e6f468e87f5385fa0c5",
"4ae66303755184a3917fcb44880605fcc53baa01912b22ed94473fc69cebd558",
"4ae663020ec16e6bb5130be918cfcafd4d765979a3136a5d50c593446e4e44db",
@@ -85,7 +85,7 @@ var testKeysFromTests = []testKeysFromTest{
16,
},
{
- versionTLS10,
+ VersionTLS10,
"03023f7527316bc12cbcd69e4b9e8275d62c028f27e65c745cfcddc7ce01bd3570a111378b63848127f1c36e5f9e4890",
"4ae66364b5ea56b20ce4e25555aed2d7e67f42788dd03f3fee4adae0459ab106",
"4ae66363ab815cbf6a248b87d6b556184e945e9b97fbdf247858b0bdafacfa1c",
@@ -98,7 +98,7 @@ var testKeysFromTests = []testKeysFromTest{
16,
},
{
- versionTLS10,
+ VersionTLS10,
"832d515f1d61eebb2be56ba0ef79879efb9b527504abb386fb4310ed5d0e3b1f220d3bb6b455033a2773e6d8bdf951d278a187482b400d45deb88a5d5a6bb7d6a7a1decc04eb9ef0642876cd4a82d374d3b6ff35f0351dc5d411104de431375355addc39bfb1f6329fb163b0bc298d658338930d07d313cd980a7e3d9196cac1",
"4ae663b2ee389c0de147c509d8f18f5052afc4aaf9699efe8cb05ece883d3a5e",
"4ae664d503fd4cff50cfc1fb8fc606580f87b0fcdac9554ba0e01d785bdf278e",
@@ -111,7 +111,7 @@ var testKeysFromTests = []testKeysFromTest{
16,
},
{
- versionSSL30,
+ VersionSSL30,
"832d515f1d61eebb2be56ba0ef79879efb9b527504abb386fb4310ed5d0e3b1f220d3bb6b455033a2773e6d8bdf951d278a187482b400d45deb88a5d5a6bb7d6a7a1decc04eb9ef0642876cd4a82d374d3b6ff35f0351dc5d411104de431375355addc39bfb1f6329fb163b0bc298d658338930d07d313cd980a7e3d9196cac1",
"4ae663b2ee389c0de147c509d8f18f5052afc4aaf9699efe8cb05ece883d3a5e",
"4ae664d503fd4cff50cfc1fb8fc606580f87b0fcdac9554ba0e01d785bdf278e",
diff --git a/src/pkg/crypto/tls/tls.go b/src/pkg/crypto/tls/tls.go
index 9230656d6..6c67506fc 100644
--- a/src/pkg/crypto/tls/tls.go
+++ b/src/pkg/crypto/tls/tls.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.
-// Package tls partially implements TLS 1.0, as specified in RFC 2246.
+// Package tls partially implements TLS 1.2, as specified in RFC 5246.
package tls
import (
diff --git a/src/pkg/crypto/x509/cert_pool.go b/src/pkg/crypto/x509/cert_pool.go
index 505f4d4f7..babe94d41 100644
--- a/src/pkg/crypto/x509/cert_pool.go
+++ b/src/pkg/crypto/x509/cert_pool.go
@@ -25,9 +25,10 @@ func NewCertPool() *CertPool {
}
// findVerifiedParents attempts to find certificates in s which have signed the
-// given certificate. If no such certificate can be found or the signature
-// doesn't match, it returns nil.
-func (s *CertPool) findVerifiedParents(cert *Certificate) (parents []int) {
+// given certificate. If any candidates were rejected then errCert will be set
+// to one of them, arbitrarily, and err will contain the reason that it was
+// rejected.
+func (s *CertPool) findVerifiedParents(cert *Certificate) (parents []int, errCert *Certificate, err error) {
if s == nil {
return
}
@@ -41,8 +42,10 @@ func (s *CertPool) findVerifiedParents(cert *Certificate) (parents []int) {
}
for _, c := range candidates {
- if cert.CheckSignatureFrom(s.certs[c]) == nil {
+ if err = cert.CheckSignatureFrom(s.certs[c]); err == nil {
parents = append(parents, c)
+ } else {
+ errCert = s.certs[c]
}
}
diff --git a/src/pkg/crypto/x509/pkcs1.go b/src/pkg/crypto/x509/pkcs1.go
index 873d3966e..acebe3513 100644
--- a/src/pkg/crypto/x509/pkcs1.go
+++ b/src/pkg/crypto/x509/pkcs1.go
@@ -52,7 +52,7 @@ func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err error) {
}
if priv.N.Sign() <= 0 || priv.D.Sign() <= 0 || priv.P.Sign() <= 0 || priv.Q.Sign() <= 0 {
- return nil, errors.New("private key contains zero or negative value")
+ return nil, errors.New("x509: private key contains zero or negative value")
}
key = new(rsa.PrivateKey)
@@ -67,7 +67,7 @@ func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err error) {
key.Primes[1] = priv.Q
for i, a := range priv.AdditionalPrimes {
if a.Prime.Sign() <= 0 {
- return nil, errors.New("private key contains zero or negative prime")
+ return nil, errors.New("x509: private key contains zero or negative prime")
}
key.Primes[i+2] = a.Prime
// We ignore the other two values because rsa will calculate
diff --git a/src/pkg/crypto/x509/pkcs8.go b/src/pkg/crypto/x509/pkcs8.go
index 8e1585e15..ba19989cb 100644
--- a/src/pkg/crypto/x509/pkcs8.go
+++ b/src/pkg/crypto/x509/pkcs8.go
@@ -32,7 +32,7 @@ func ParsePKCS8PrivateKey(der []byte) (key interface{}, err error) {
case privKey.Algo.Algorithm.Equal(oidPublicKeyRSA):
key, err = ParsePKCS1PrivateKey(privKey.PrivateKey)
if err != nil {
- return nil, errors.New("crypto/x509: failed to parse RSA private key embedded in PKCS#8: " + err.Error())
+ return nil, errors.New("x509: failed to parse RSA private key embedded in PKCS#8: " + err.Error())
}
return key, nil
@@ -44,11 +44,11 @@ func ParsePKCS8PrivateKey(der []byte) (key interface{}, err error) {
}
key, err = parseECPrivateKey(namedCurveOID, privKey.PrivateKey)
if err != nil {
- return nil, errors.New("crypto/x509: failed to parse EC private key embedded in PKCS#8: " + err.Error())
+ return nil, errors.New("x509: failed to parse EC private key embedded in PKCS#8: " + err.Error())
}
return key, nil
default:
- return nil, fmt.Errorf("crypto/x509: PKCS#8 wrapping contained private key with unknown algorithm: %v", privKey.Algo.Algorithm)
+ return nil, fmt.Errorf("x509: PKCS#8 wrapping contained private key with unknown algorithm: %v", privKey.Algo.Algorithm)
}
}
diff --git a/src/pkg/crypto/x509/pkix/pkix.go b/src/pkg/crypto/x509/pkix/pkix.go
index 738659011..5034946f7 100644
--- a/src/pkg/crypto/x509/pkix/pkix.go
+++ b/src/pkg/crypto/x509/pkix/pkix.go
@@ -144,7 +144,7 @@ type CertificateList struct {
SignatureValue asn1.BitString
}
-// HasExpired returns true iff now is past the expiry time of certList.
+// HasExpired reports whether now is past the expiry time of certList.
func (certList *CertificateList) HasExpired(now time.Time) bool {
return now.After(certList.TBSCertList.NextUpdate)
}
diff --git a/src/pkg/crypto/x509/root_unix.go b/src/pkg/crypto/x509/root_unix.go
index 1b25a94d0..324f855b1 100644
--- a/src/pkg/crypto/x509/root_unix.go
+++ b/src/pkg/crypto/x509/root_unix.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.
-// +build freebsd linux openbsd netbsd
+// +build dragonfly freebsd linux openbsd netbsd
package x509
@@ -10,11 +10,11 @@ import "io/ioutil"
// Possible certificate files; stop after finding one.
var certFiles = []string{
- "/etc/ssl/certs/ca-certificates.crt", // Linux etc
+ "/etc/ssl/certs/ca-certificates.crt", // Debian/Ubuntu/Gentoo etc.
"/etc/pki/tls/certs/ca-bundle.crt", // Fedora/RHEL
"/etc/ssl/ca-bundle.pem", // OpenSUSE
"/etc/ssl/cert.pem", // OpenBSD
- "/usr/local/share/certs/ca-root-nss.crt", // FreeBSD
+ "/usr/local/share/certs/ca-root-nss.crt", // FreeBSD/DragonFly
}
func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
diff --git a/src/pkg/crypto/x509/root_windows.go b/src/pkg/crypto/x509/root_windows.go
index e8f70a49d..81018b78f 100644
--- a/src/pkg/crypto/x509/root_windows.go
+++ b/src/pkg/crypto/x509/root_windows.go
@@ -89,7 +89,7 @@ func checkChainTrustStatus(c *Certificate, chainCtx *syscall.CertChainContext) e
case syscall.CERT_TRUST_IS_NOT_TIME_VALID:
return CertificateInvalidError{c, Expired}
default:
- return UnknownAuthorityError{c}
+ return UnknownAuthorityError{c, nil, nil}
}
}
return nil
@@ -129,9 +129,9 @@ func checkChainSSLServerPolicy(c *Certificate, chainCtx *syscall.CertChainContex
case syscall.CERT_E_CN_NO_MATCH:
return HostnameError{c, opts.DNSName}
case syscall.CERT_E_UNTRUSTEDROOT:
- return UnknownAuthorityError{c}
+ return UnknownAuthorityError{c, nil, nil}
default:
- return UnknownAuthorityError{c}
+ return UnknownAuthorityError{c, nil, nil}
}
}
diff --git a/src/pkg/crypto/x509/sec1.go b/src/pkg/crypto/x509/sec1.go
index 8a2840fbe..7de66754e 100644
--- a/src/pkg/crypto/x509/sec1.go
+++ b/src/pkg/crypto/x509/sec1.go
@@ -33,6 +33,20 @@ func ParseECPrivateKey(der []byte) (key *ecdsa.PrivateKey, err error) {
return parseECPrivateKey(nil, der)
}
+// MarshalECPrivateKey marshals an EC private key into ASN.1, DER format.
+func MarshalECPrivateKey(key *ecdsa.PrivateKey) ([]byte, error) {
+ oid, ok := oidFromNamedCurve(key.Curve)
+ if !ok {
+ return nil, errors.New("x509: unknown elliptic curve")
+ }
+ return asn1.Marshal(ecPrivateKey{
+ Version: 1,
+ PrivateKey: key.D.Bytes(),
+ NamedCurveOID: oid,
+ PublicKey: asn1.BitString{Bytes: elliptic.Marshal(key.Curve, key.X, key.Y)},
+ })
+}
+
// parseECPrivateKey parses an ASN.1 Elliptic Curve Private Key Structure.
// The OID for the named curve may be provided from another source (such as
// the PKCS8 container) - if it is provided then use this instead of the OID
@@ -40,10 +54,10 @@ func ParseECPrivateKey(der []byte) (key *ecdsa.PrivateKey, err error) {
func parseECPrivateKey(namedCurveOID *asn1.ObjectIdentifier, der []byte) (key *ecdsa.PrivateKey, err error) {
var privKey ecPrivateKey
if _, err := asn1.Unmarshal(der, &privKey); err != nil {
- return nil, errors.New("crypto/x509: failed to parse EC private key: " + err.Error())
+ return nil, errors.New("x509: failed to parse EC private key: " + err.Error())
}
if privKey.Version != ecPrivKeyVersion {
- return nil, fmt.Errorf("crypto/x509: unknown EC private key version %d", privKey.Version)
+ return nil, fmt.Errorf("x509: unknown EC private key version %d", privKey.Version)
}
var curve elliptic.Curve
@@ -53,12 +67,12 @@ func parseECPrivateKey(namedCurveOID *asn1.ObjectIdentifier, der []byte) (key *e
curve = namedCurveFromOID(privKey.NamedCurveOID)
}
if curve == nil {
- return nil, errors.New("crypto/x509: unknown elliptic curve")
+ return nil, errors.New("x509: unknown elliptic curve")
}
k := new(big.Int).SetBytes(privKey.PrivateKey)
if k.Cmp(curve.Params().N) >= 0 {
- return nil, errors.New("crypto/x509: invalid elliptic curve private key value")
+ return nil, errors.New("x509: invalid elliptic curve private key value")
}
priv := new(ecdsa.PrivateKey)
priv.Curve = curve
diff --git a/src/pkg/crypto/x509/sec1_test.go b/src/pkg/crypto/x509/sec1_test.go
index 7135699d2..95f18e77d 100644
--- a/src/pkg/crypto/x509/sec1_test.go
+++ b/src/pkg/crypto/x509/sec1_test.go
@@ -5,6 +5,7 @@
package x509
import (
+ "bytes"
"encoding/hex"
"testing"
)
@@ -15,8 +16,15 @@ var ecPrivateKeyHex = `3081a40201010430bdb9839c08ee793d1157886a7a758a3c8b2a17a4d
func TestParseECPrivateKey(t *testing.T) {
derBytes, _ := hex.DecodeString(ecPrivateKeyHex)
- _, err := ParseECPrivateKey(derBytes)
+ key, err := ParseECPrivateKey(derBytes)
if err != nil {
t.Errorf("failed to decode EC private key: %s", err)
}
+ serialized, err := MarshalECPrivateKey(key)
+ if err != nil {
+ t.Fatalf("failed to encode EC private key: %s", err)
+ }
+ if !bytes.Equal(serialized, derBytes) {
+ t.Fatalf("serialized key differs: got %x, want %x", serialized, derBytes)
+ }
}
diff --git a/src/pkg/crypto/x509/verify.go b/src/pkg/crypto/x509/verify.go
index b29ddbc80..8327463ca 100644
--- a/src/pkg/crypto/x509/verify.go
+++ b/src/pkg/crypto/x509/verify.go
@@ -5,6 +5,7 @@
package x509
import (
+ "fmt"
"net"
"runtime"
"strings"
@@ -91,10 +92,27 @@ func (h HostnameError) Error() string {
// UnknownAuthorityError results when the certificate issuer is unknown
type UnknownAuthorityError struct {
cert *Certificate
+ // hintErr contains an error that may be helpful in determining why an
+ // authority wasn't found.
+ hintErr error
+ // hintCert contains a possible authority certificate that was rejected
+ // because of the error in hintErr.
+ hintCert *Certificate
}
func (e UnknownAuthorityError) Error() string {
- return "x509: certificate signed by unknown authority"
+ s := "x509: certificate signed by unknown authority"
+ if e.hintErr != nil {
+ certName := e.hintCert.Subject.CommonName
+ if len(certName) == 0 {
+ if len(e.hintCert.Subject.Organization) > 0 {
+ certName = e.hintCert.Subject.Organization[0]
+ }
+ certName = "serial:" + e.hintCert.SerialNumber.String()
+ }
+ s += fmt.Sprintf(" (possibly because of %q while trying to verify candidate authority certificate %q)", e.hintErr, certName)
+ }
+ return s
}
// SystemRootsError results when we fail to load the system root certificates.
@@ -136,14 +154,18 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V
}
if len(c.PermittedDNSDomains) > 0 {
+ ok := false
for _, domain := range c.PermittedDNSDomains {
if opts.DNSName == domain ||
(strings.HasSuffix(opts.DNSName, domain) &&
len(opts.DNSName) >= 1+len(domain) &&
opts.DNSName[len(opts.DNSName)-len(domain)-1] == '.') {
- continue
+ ok = true
+ break
}
+ }
+ if !ok {
return CertificateInvalidError{c, CANotAuthorizedForThisName}
}
}
@@ -249,7 +271,8 @@ func appendToFreshChain(chain []*Certificate, cert *Certificate) []*Certificate
}
func (c *Certificate) buildChains(cache map[int][][]*Certificate, currentChain []*Certificate, opts *VerifyOptions) (chains [][]*Certificate, err error) {
- for _, rootNum := range opts.Roots.findVerifiedParents(c) {
+ possibleRoots, failedRoot, rootErr := opts.Roots.findVerifiedParents(c)
+ for _, rootNum := range possibleRoots {
root := opts.Roots.certs[rootNum]
err = root.isValid(rootCertificate, currentChain, opts)
if err != nil {
@@ -258,8 +281,9 @@ func (c *Certificate) buildChains(cache map[int][][]*Certificate, currentChain [
chains = append(chains, appendToFreshChain(currentChain, root))
}
+ possibleIntermediates, failedIntermediate, intermediateErr := opts.Intermediates.findVerifiedParents(c)
nextIntermediate:
- for _, intermediateNum := range opts.Intermediates.findVerifiedParents(c) {
+ for _, intermediateNum := range possibleIntermediates {
intermediate := opts.Intermediates.certs[intermediateNum]
for _, cert := range currentChain {
if cert == intermediate {
@@ -284,7 +308,13 @@ nextIntermediate:
}
if len(chains) == 0 && err == nil {
- err = UnknownAuthorityError{c}
+ hintErr := rootErr
+ hintCert := failedRoot
+ if hintErr == nil {
+ hintErr = intermediateErr
+ hintCert = failedIntermediate
+ }
+ err = UnknownAuthorityError{c, hintErr, hintCert}
}
return
diff --git a/src/pkg/crypto/x509/verify_test.go b/src/pkg/crypto/x509/verify_test.go
index 5103ed814..ba6c13d45 100644
--- a/src/pkg/crypto/x509/verify_test.go
+++ b/src/pkg/crypto/x509/verify_test.go
@@ -127,6 +127,18 @@ var verifyTests = []verifyTest{
},
},
{
+ leaf: googleLeafWithInvalidHash,
+ intermediates: []string{thawteIntermediate},
+ roots: []string{verisignRoot},
+ currentTime: 1302726541,
+ dnsName: "www.google.com",
+
+ // The specific error message may not occur when using system
+ // verification.
+ systemSkip: true,
+ errorCallback: expectHashError,
+ },
+ {
// The default configuration should reject an S/MIME chain.
leaf: smimeLeaf,
roots: []string{smimeIntermediate},
@@ -171,6 +183,24 @@ var verifyTests = []verifyTest{
{"mega.co.nz", "EssentialSSL CA", "COMODO Certification Authority"},
},
},
+ {
+ // Check that a name constrained intermediate works even when
+ // it lists multiple constraints.
+ leaf: nameConstraintsLeaf,
+ intermediates: []string{nameConstraintsIntermediate1, nameConstraintsIntermediate2},
+ roots: []string{globalSignRoot},
+ currentTime: 1382387896,
+ dnsName: "secure.iddl.vt.edu",
+
+ expectedChains: [][]string{
+ {
+ "Technology-enhanced Learning and Online Strategies",
+ "Virginia Tech Global Qualified Server CA",
+ "Trusted Root CA G2",
+ "GlobalSign Root CA",
+ },
+ },
+ },
}
func expectHostnameError(t *testing.T, i int, err error) (ok bool) {
@@ -213,6 +243,18 @@ func expectSystemRootsError(t *testing.T, i int, err error) bool {
return true
}
+func expectHashError(t *testing.T, i int, err error) bool {
+ if err == nil {
+ t.Errorf("#%d: no error resulted from invalid hash", i)
+ return false
+ }
+ if expected := "algorithm unimplemented"; !strings.Contains(err.Error(), expected) {
+ t.Errorf("#%d: error resulting from invalid hash didn't contain '%s', rather it was: %s", i, expected, err)
+ return false
+ }
+ return true
+}
+
func certificateFromPEM(pemBytes string) (*Certificate, error) {
block, _ := pem.Decode([]byte(pemBytes))
if block == nil {
@@ -400,6 +442,28 @@ u2ONgJd8IyAPkU0Wueru9G2Jysa9zCRo1kNbzipYvzwY4OA8Ys+WAi0oR1A04Se6
z5nRUP8pJcA2NhUzUnC+MY+f6H/nEQyNv4SgQhqAibAxWEEHXw==
-----END CERTIFICATE-----`
+// googleLeafWithInvalidHash is the same as googleLeaf, but the signature
+// algorithm in the certificate contains a nonsense OID.
+const googleLeafWithInvalidHash = `-----BEGIN CERTIFICATE-----
+MIIDITCCAoqgAwIBAgIQL9+89q6RUm0PmqPfQDQ+mjANBgkqhkiG9w0BATIFADBM
+MQswCQYDVQQGEwJaQTElMCMGA1UEChMcVGhhd3RlIENvbnN1bHRpbmcgKFB0eSkg
+THRkLjEWMBQGA1UEAxMNVGhhd3RlIFNHQyBDQTAeFw0wOTEyMTgwMDAwMDBaFw0x
+MTEyMTgyMzU5NTlaMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlh
+MRYwFAYDVQQHFA1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKFApHb29nbGUgSW5jMRcw
+FQYDVQQDFA53d3cuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC
+gYEA6PmGD5D6htffvXImttdEAoN4c9kCKO+IRTn7EOh8rqk41XXGOOsKFQebg+jN
+gtXj9xVoRaELGYW84u+E593y17iYwqG7tcFR39SDAqc9BkJb4SLD3muFXxzW2k6L
+05vuuWciKh0R73mkszeK9P4Y/bz5RiNQl/Os/CRGK1w7t0UCAwEAAaOB5zCB5DAM
+BgNVHRMBAf8EAjAAMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwudGhhd3Rl
+LmNvbS9UaGF3dGVTR0NDQS5jcmwwKAYDVR0lBCEwHwYIKwYBBQUHAwEGCCsGAQUF
+BwMCBglghkgBhvhCBAEwcgYIKwYBBQUHAQEEZjBkMCIGCCsGAQUFBzABhhZodHRw
+Oi8vb2NzcC50aGF3dGUuY29tMD4GCCsGAQUFBzAChjJodHRwOi8vd3d3LnRoYXd0
+ZS5jb20vcmVwb3NpdG9yeS9UaGF3dGVfU0dDX0NBLmNydDANBgkqhkiG9w0BAVAF
+AAOBgQCfQ89bxFApsb/isJr/aiEdLRLDLE5a+RLizrmCUi3nHX4adpaQedEkUjh5
+u2ONgJd8IyAPkU0Wueru9G2Jysa9zCRo1kNbzipYvzwY4OA8Ys+WAi0oR1A04Se6
+z5nRUP8pJcA2NhUzUnC+MY+f6H/nEQyNv4SgQhqAibAxWEEHXw==
+-----END CERTIFICATE-----`
+
const dnssecExpLeaf = `-----BEGIN CERTIFICATE-----
MIIGzTCCBbWgAwIBAgIDAdD6MA0GCSqGSIb3DQEBBQUAMIGMMQswCQYDVQQGEwJJ
TDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0
@@ -522,6 +586,50 @@ um0ABj6y6koQOdjQK/W/7HW/lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkySh
NOsF/5oirpt9P/FlUQqmMGqz9IgcgA38corog14=
-----END CERTIFICATE-----`
+const startComRootSHA256 = `-----BEGIN CERTIFICATE-----
+MIIHhzCCBW+gAwIBAgIBLTANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJJTDEW
+MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg
+Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM3WhcNMzYwOTE3MTk0NjM2WjB9
+MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi
+U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh
+cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA
+A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk
+pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf
+OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C
+Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT
+Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi
+HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM
+Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w
++2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+
+Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3
+Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B
+26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID
+AQABo4ICEDCCAgwwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD
+VR0OBBYEFE4L7xqkQFulF2mHMMo0aEPQQa7yMB8GA1UdIwQYMBaAFE4L7xqkQFul
+F2mHMMo0aEPQQa7yMIIBWgYDVR0gBIIBUTCCAU0wggFJBgsrBgEEAYG1NwEBATCC
+ATgwLgYIKwYBBQUHAgEWImh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5w
+ZGYwNAYIKwYBBQUHAgEWKGh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL2ludGVybWVk
+aWF0ZS5wZGYwgc8GCCsGAQUFBwICMIHCMCcWIFN0YXJ0IENvbW1lcmNpYWwgKFN0
+YXJ0Q29tKSBMdGQuMAMCAQEagZZMaW1pdGVkIExpYWJpbGl0eSwgcmVhZCB0aGUg
+c2VjdGlvbiAqTGVnYWwgTGltaXRhdGlvbnMqIG9mIHRoZSBTdGFydENvbSBDZXJ0
+aWZpY2F0aW9uIEF1dGhvcml0eSBQb2xpY3kgYXZhaWxhYmxlIGF0IGh0dHA6Ly93
+d3cuc3RhcnRzc2wuY29tL3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgG
+CWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1
+dGhvcml0eTANBgkqhkiG9w0BAQsFAAOCAgEAjo/n3JR5fPGFf59Jb2vKXfuM/gTF
+wWLRfUKKvFO3lANmMD+x5wqnUCBVJX92ehQN6wQOQOY+2IirByeDqXWmN3PH/UvS
+Ta0XQMhGvjt/UfzDtgUx3M2FIk5xt/JxXrAaxrqTi3iSSoX4eA+D/i+tLPfkpLst
+0OcNOrg+zvZ49q5HJMqjNTbOx8aHmNrs++myziebiMMEofYLWWivydsQD032ZGNc
+pRJvkrKTlMeIFw6Ttn5ii5B/q06f/ON1FE8qMt9bDeD1e5MNq6HPh+GlBEXoPBKl
+CcWw0bdT82AUuoVpaiF8H3VhFyAXe2w7QSlc4axa0c2Mm+tgHRns9+Ww2vl5GKVF
+P0lDV9LdJNUso/2RjSe15esUBppMeyG7Oq0wBhjA2MFrLH9ZXF2RsXAiV+uKa0hK
+1Q8p7MZAwC+ITGgBF3f0JBlPvfrhsiAhS90a2Cl9qrjeVOwhVYBsHvUwyKMQ5bLm
+KhQxw4UtjJixhlpPiVktucf3HMiKf8CdBUrmQk9io20ppB+Fq9vlgcitKj1MXVuE
+JnHEhV5xJMqlG2zYYdMa4FTbzrqpMrUi9nNBCV24F10OD5mQ1kfabwo6YigUZ4LZ
+8dCAWZvLMdibD4x3TrVoivJs9iQOLWxwxXPR3hTQcY+203sC9uO41Alua551hDnm
+fyWl8kgAwKQB2j8=
+-----END CERTIFICATE-----`
+
const smimeLeaf = `-----BEGIN CERTIFICATE-----
MIIFBjCCA+6gAwIBAgISESFvrjT8XcJTEe6rBlPptILlMA0GCSqGSIb3DQEBBQUA
MFQxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMSowKAYD
@@ -663,3 +771,168 @@ zJVSk/BwJVmcIGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5dd
BA6+C4OmF4O5MBKgxTMVBbkN+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IB
ZQ==
-----END CERTIFICATE-----`
+
+var nameConstraintsLeaf = `-----BEGIN CERTIFICATE-----
+MIIHMTCCBRmgAwIBAgIIIZaV/3ezOJkwDQYJKoZIhvcNAQEFBQAwgcsxCzAJBgNV
+BAYTAlVTMREwDwYDVQQIEwhWaXJnaW5pYTETMBEGA1UEBxMKQmxhY2tzYnVyZzEj
+MCEGA1UECxMaR2xvYmFsIFF1YWxpZmllZCBTZXJ2ZXIgQ0ExPDA6BgNVBAoTM1Zp
+cmdpbmlhIFBvbHl0ZWNobmljIEluc3RpdHV0ZSBhbmQgU3RhdGUgVW5pdmVyc2l0
+eTExMC8GA1UEAxMoVmlyZ2luaWEgVGVjaCBHbG9iYWwgUXVhbGlmaWVkIFNlcnZl
+ciBDQTAeFw0xMzA5MTkxNDM2NTVaFw0xNTA5MTkxNDM2NTVaMIHNMQswCQYDVQQG
+EwJVUzERMA8GA1UECAwIVmlyZ2luaWExEzARBgNVBAcMCkJsYWNrc2J1cmcxPDA6
+BgNVBAoMM1ZpcmdpbmlhIFBvbHl0ZWNobmljIEluc3RpdHV0ZSBhbmQgU3RhdGUg
+VW5pdmVyc2l0eTE7MDkGA1UECwwyVGVjaG5vbG9neS1lbmhhbmNlZCBMZWFybmlu
+ZyBhbmQgT25saW5lIFN0cmF0ZWdpZXMxGzAZBgNVBAMMEnNlY3VyZS5pZGRsLnZ0
+LmVkdTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkOyPpsOK/6IuPG
+WnIBlVwlHzeYf+cUlggqkLq0b0+vZbiTXgio9/VCuNQ8opSoss7J7o3ygV9to+9Y
+YwJKVC5WDT/y5JWpQey0CWILymViJnpNSwnxBc8A+Q8w5NUGDd/UhtPx/U8/hqbd
+WPDYj2hbOqyq8UlRhfS5pwtnv6BbCTaY11I6FhCLK7zttISyTuWCf9p9o/ggiipP
+ii/5oh4dkl+r5SfuSp5GPNHlYO8lWqys5NAPoDD4fc/kuflcK7Exx7XJ+Oqu0W0/
+psjEY/tES1ZgDWU/ParcxxFpFmKHbD5DXsfPOObzkVWXIY6tGMutSlE1Froy/Nn0
+OZsAOrcCAwEAAaOCAhMwggIPMIG4BggrBgEFBQcBAQSBqzCBqDBYBggrBgEFBQcw
+AoZMaHR0cDovL3d3dy5wa2kudnQuZWR1L2dsb2JhbHF1YWxpZmllZHNlcnZlci9j
+YWNlcnQvZ2xvYmFscXVhbGlmaWVkc2VydmVyLmNydDBMBggrBgEFBQcwAYZAaHR0
+cDovL3Z0Y2EtcC5lcHJvdi5zZXRpLnZ0LmVkdTo4MDgwL2VqYmNhL3B1YmxpY3dl
+Yi9zdGF0dXMvb2NzcDAdBgNVHQ4EFgQUp7xbO6iHkvtZbPE4jmndmnAbSEcwDAYD
+VR0TAQH/BAIwADAfBgNVHSMEGDAWgBS8YmAn1eM1SBfpS6tFatDIqHdxjDBqBgNV
+HSAEYzBhMA4GDCsGAQQBtGgFAgICATAOBgwrBgEEAbRoBQICAQEwPwYMKwYBBAG0
+aAUCAgMBMC8wLQYIKwYBBQUHAgEWIWh0dHA6Ly93d3cucGtpLnZ0LmVkdS9nbG9i
+YWwvY3BzLzBKBgNVHR8EQzBBMD+gPaA7hjlodHRwOi8vd3d3LnBraS52dC5lZHUv
+Z2xvYmFscXVhbGlmaWVkc2VydmVyL2NybC9jYWNybC5jcmwwDgYDVR0PAQH/BAQD
+AgTwMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAdBgNVHREEFjAUghJz
+ZWN1cmUuaWRkbC52dC5lZHUwDQYJKoZIhvcNAQEFBQADggIBAEgoYo4aUtatY3gI
+OyyKp7QlIOaLbTJZywESHqy+L5EGDdJW2DJV+mcE0LDGvqa2/1Lo+AR1ntsZwfOi
+Y718JwgVVaX/RCd5+QKP25c5/x72xI8hb/L1bgS0ED9b0YAhd7Qm1K1ot82+6mqX
+DW6WiGeDr8Z07MQ3143qQe2rBlq+QI69DYzm2GOqAIAnUIWv7tCyLUm31b4DwmrJ
+TeudVreTKUbBNB1TWRFHEPkWhjjXKZnNGRO11wHXcyBu6YekIvVZ+vmx8ePee4jJ
+3GFOi7lMuWOeq57jTVL7KOKaKLVXBb6gqo5aq+Wwt8RUD5MakrCAEeQZj7DKaFmZ
+oQCO0Pxrsl3InCGvxnGzT+bFVO9nJ/BAMj7hknFdm9Jr6Bg5q33Z+gnf909AD9QF
+ESqUSykaHu2LVdJx2MaCH1CyKnRgMw5tEwE15EXpUjCm24m8FMOYC+rNtf18pgrz
+5D8Jhh+oxK9PjcBYqXNtnioIxiMCYcV0q5d4w4BYFEh71tk7/bYB0R55CsBUVPmp
+timWNOdRd57Tfpk3USaVsumWZAf9MP3wPiC7gb4d5tYEEAG5BuDT8ruFw838wU8G
+1VvAVutSiYBg7k3NYO7AUqZ+Ax4klQX3aM9lgonmJ78Qt94UPtbptrfZ4/lSqEf8
+GBUwDrQNTb+gsXsDkjd5lcYxNx6l
+-----END CERTIFICATE-----`
+
+var nameConstraintsIntermediate1 = `-----BEGIN CERTIFICATE-----
+MIINLjCCDBagAwIBAgIRIqpyf/YoGgvHc8HiDAxAI8owDQYJKoZIhvcNAQEFBQAw
+XDELMAkGA1UEBhMCQkUxFTATBgNVBAsTDFRydXN0ZWQgUm9vdDEZMBcGA1UEChMQ
+R2xvYmFsU2lnbiBudi1zYTEbMBkGA1UEAxMSVHJ1c3RlZCBSb290IENBIEcyMB4X
+DTEyMTIxMzAwMDAwMFoXDTE3MTIxMzAwMDAwMFowgcsxCzAJBgNVBAYTAlVTMREw
+DwYDVQQIEwhWaXJnaW5pYTETMBEGA1UEBxMKQmxhY2tzYnVyZzEjMCEGA1UECxMa
+R2xvYmFsIFF1YWxpZmllZCBTZXJ2ZXIgQ0ExPDA6BgNVBAoTM1ZpcmdpbmlhIFBv
+bHl0ZWNobmljIEluc3RpdHV0ZSBhbmQgU3RhdGUgVW5pdmVyc2l0eTExMC8GA1UE
+AxMoVmlyZ2luaWEgVGVjaCBHbG9iYWwgUXVhbGlmaWVkIFNlcnZlciBDQTCCAiIw
+DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALgIZhEaptBWADBqdJ45ueFGzMXa
+GHnzNxoxR1fQIaaRQNdCg4cw3A4dWKMeEgYLtsp65ai3Xfw62Qaus0+KJ3RhgV+r
+ihqK81NUzkls78fJlADVDI4fCTlothsrE1CTOMiy97jKHai5mVTiWxmcxpmjv7fm
+5Nhc+uHgh2hIz6npryq495mD51ZrUTIaqAQN6Pw/VHfAmR524vgriTOjtp1t4lA9
+pXGWjF/vkhAKFFheOQSQ00rngo2wHgCqMla64UTN0oz70AsCYNZ3jDLx0kOP0YmM
+R3Ih91VA63kLqPXA0R6yxmmhhxLZ5bcyAy1SLjr1N302MIxLM/pSy6aquEnbELhz
+qyp9yGgRyGJay96QH7c4RJY6gtcoPDbldDcHI9nXngdAL4DrZkJ9OkDkJLyqG66W
+ZTF5q4EIs6yMdrywz0x7QP+OXPJrjYpbeFs6tGZCFnWPFfmHCRJF8/unofYrheq+
+9J7Jx3U55S/k57NXbAM1RAJOuMTlfn9Etf9Dpoac9poI4Liav6rBoUQk3N3JWqnV
+HNx/NdCyJ1/6UbKMJUZsStAVglsi6lVPo289HHOE4f7iwl3SyekizVOp01wUin3y
+cnbZB/rXmZbwapSxTTSBf0EIOr9i4EGfnnhCAVA9U5uLrI5OEB69IY8PNX0071s3
+Z2a2fio5c8m3JkdrAgMBAAGjggh5MIIIdTAOBgNVHQ8BAf8EBAMCAQYwTAYDVR0g
+BEUwQzBBBgkrBgEEAaAyATwwNDAyBggrBgEFBQcCARYmaHR0cHM6Ly93d3cuZ2xv
+YmFsc2lnbi5jb20vcmVwb3NpdG9yeS8wEgYDVR0TAQH/BAgwBgEB/wIBADCCBtAG
+A1UdHgSCBscwggbDoIIGvzASghAzZGJsYWNrc2J1cmcub3JnMBiCFmFjY2VsZXJh
+dGV2aXJnaW5pYS5jb20wGIIWYWNjZWxlcmF0ZXZpcmdpbmlhLm9yZzALgglhY3Zj
+cC5vcmcwCYIHYmV2Lm5ldDAJggdiZXYub3JnMAuCCWNsaWdzLm9yZzAMggpjbWl3
+ZWIub3JnMBeCFWVhc3Rlcm5icm9va3Ryb3V0Lm5ldDAXghVlYXN0ZXJuYnJvb2t0
+cm91dC5vcmcwEYIPZWNvcnJpZG9ycy5pbmZvMBOCEWVkZ2FycmVzZWFyY2gub3Jn
+MBKCEGdldC1lZHVjYXRlZC5jb20wE4IRZ2V0LWVkdWNhdGVkLmluZm8wEYIPZ2V0
+ZWR1Y2F0ZWQubmV0MBKCEGdldC1lZHVjYXRlZC5uZXQwEYIPZ2V0ZWR1Y2F0ZWQu
+b3JnMBKCEGdldC1lZHVjYXRlZC5vcmcwD4INaG9raWVjbHViLmNvbTAQgg5ob2tp
+ZXBob3RvLmNvbTAPgg1ob2tpZXNob3AuY29tMBGCD2hva2llc3BvcnRzLmNvbTAS
+ghBob2tpZXRpY2tldHMuY29tMBKCEGhvdGVscm9hbm9rZS5jb20wE4IRaHVtYW53
+aWxkbGlmZS5vcmcwF4IVaW5uYXR2aXJnaW5pYXRlY2guY29tMA+CDWlzY2hwMjAx
+MS5vcmcwD4INbGFuZHJlaGFiLm9yZzAggh5uYXRpb25hbHRpcmVyZXNlYXJjaGNl
+bnRlci5jb20wFYITbmV0d29ya3ZpcmdpbmlhLm5ldDAMggpwZHJjdnQuY29tMBiC
+FnBldGVkeWVyaXZlcmNvdXJzZS5jb20wDYILcmFkaW9pcS5vcmcwFYITcml2ZXJj
+b3Vyc2Vnb2xmLmNvbTALgglzZGltaS5vcmcwEIIOc292YW1vdGlvbi5jb20wHoIc
+c3VzdGFpbmFibGUtYmlvbWF0ZXJpYWxzLmNvbTAeghxzdXN0YWluYWJsZS1iaW9t
+YXRlcmlhbHMub3JnMBWCE3RoaXNpc3RoZWZ1dHVyZS5jb20wGIIWdGhpcy1pcy10
+aGUtZnV0dXJlLmNvbTAVghN0aGlzaXN0aGVmdXR1cmUubmV0MBiCFnRoaXMtaXMt
+dGhlLWZ1dHVyZS5uZXQwCoIIdmFkcy5vcmcwDIIKdmFsZWFmLm9yZzANggt2YXRl
+Y2guaW5mbzANggt2YXRlY2gubW9iaTAcghp2YXRlY2hsaWZlbG9uZ2xlYXJuaW5n
+LmNvbTAcghp2YXRlY2hsaWZlbG9uZ2xlYXJuaW5nLm5ldDAcghp2YXRlY2hsaWZl
+bG9uZ2xlYXJuaW5nLm9yZzAKggh2Y29tLmVkdTASghB2aXJnaW5pYXZpZXcubmV0
+MDSCMnZpcmdpbmlhcG9seXRlY2huaWNpbnN0aXR1dGVhbmRzdGF0ZXVuaXZlcnNp
+dHkuY29tMDWCM3ZpcmdpbmlhcG9seXRlY2huaWNpbnN0aXR1dGVhbmRzdGF0ZXVu
+aXZlcnNpdHkuaW5mbzA0gjJ2aXJnaW5pYXBvbHl0ZWNobmljaW5zdGl0dXRlYW5k
+c3RhdGV1bml2ZXJzaXR5Lm5ldDA0gjJ2aXJnaW5pYXBvbHl0ZWNobmljaW5zdGl0
+dXRlYW5kc3RhdGV1bml2ZXJzaXR5Lm9yZzAZghd2aXJnaW5pYXB1YmxpY3JhZGlv
+Lm9yZzASghB2aXJnaW5pYXRlY2guZWR1MBOCEXZpcmdpbmlhdGVjaC5tb2JpMByC
+GnZpcmdpbmlhdGVjaGZvdW5kYXRpb24ub3JnMAiCBnZ0LmVkdTALggl2dGFyYy5v
+cmcwDIIKdnQtYXJjLm9yZzALggl2dGNyYy5jb20wCoIIdnRpcC5vcmcwDIIKdnRs
+ZWFuLm9yZzAWghR2dGtub3dsZWRnZXdvcmtzLmNvbTAYghZ2dGxpZmVsb25nbGVh
+cm5pbmcuY29tMBiCFnZ0bGlmZWxvbmdsZWFybmluZy5uZXQwGIIWdnRsaWZlbG9u
+Z2xlYXJuaW5nLm9yZzATghF2dHNwb3J0c21lZGlhLmNvbTALggl2dHdlaS5jb20w
+D4INd2l3YXR3ZXJjLmNvbTAKggh3dnRmLm9yZzAIgQZ2dC5lZHUwd6R1MHMxCzAJ
+BgNVBAYTAlVTMREwDwYDVQQIEwhWaXJnaW5pYTETMBEGA1UEBxMKQmxhY2tzYnVy
+ZzE8MDoGA1UEChMzVmlyZ2luaWEgUG9seXRlY2huaWMgSW5zdGl0dXRlIGFuZCBT
+dGF0ZSBVbml2ZXJzaXR5MCcGA1UdJQQgMB4GCCsGAQUFBwMCBggrBgEFBQcDAQYI
+KwYBBQUHAwkwPQYDVR0fBDYwNDAyoDCgLoYsaHR0cDovL2NybC5nbG9iYWxzaWdu
+LmNvbS9ncy90cnVzdHJvb3RnMi5jcmwwgYQGCCsGAQUFBwEBBHgwdjAzBggrBgEF
+BQcwAYYnaHR0cDovL29jc3AyLmdsb2JhbHNpZ24uY29tL3RydXN0cm9vdGcyMD8G
+CCsGAQUFBzAChjNodHRwOi8vc2VjdXJlLmdsb2JhbHNpZ24uY29tL2NhY2VydC90
+cnVzdHJvb3RnMi5jcnQwHQYDVR0OBBYEFLxiYCfV4zVIF+lLq0Vq0Miod3GMMB8G
+A1UdIwQYMBaAFBT25YsxtkWASkxt/MKHico2w5BiMA0GCSqGSIb3DQEBBQUAA4IB
+AQAyJm/lOB2Er4tHXhc/+fSufSzgjohJgYfMkvG4LknkvnZ1BjliefR8tTXX49d2
+SCDFWfGjqyJZwavavkl/4p3oXPG/nAMDMvxh4YAT+CfEK9HH+6ICV087kD4BLegi
++aFJMj8MMdReWCzn5sLnSR1rdse2mo2arX3Uod14SW+PGrbUmTuWNyvRbz3fVmxp
+UdbGmj3laknO9YPsBGgHfv73pVVsTJkW4ZfY/7KdD/yaVv6ophpOB3coXfjl2+kd
+Z4ypn2zK+cx9IL/LSewqd/7W9cD55PCUy4X9OTbEmAccwiz3LB66mQoUGfdHdkoB
+jUY+v9vLQXmaVwI0AYL7g9LN
+-----END CERTIFICATE-----`
+
+var nameConstraintsIntermediate2 = `-----BEGIN CERTIFICATE-----
+MIIEXTCCA0WgAwIBAgILBAAAAAABNuk6OrMwDQYJKoZIhvcNAQEFBQAwVzELMAkG
+A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
+b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0xMjA0MjUxMTAw
+MDBaFw0yNzA0MjUxMTAwMDBaMFwxCzAJBgNVBAYTAkJFMRUwEwYDVQQLEwxUcnVz
+dGVkIFJvb3QxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExGzAZBgNVBAMTElRy
+dXN0ZWQgUm9vdCBDQSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+AKyuvqrtcMr7g7EuNbu4sKwxM127UsCmx1RxbxxgcArGS7rjiefpBH/w4LYrymjf
+vcw1ueyMNoqLo9nJMz/ORXupb35NNfE667prQYHa+tTjl1IiKpB7QUwt3wXPuTMF
+Ja1tXtjKzkqJyuJlNuPKT76HcjgNqgV1s9qG44MD5I2JvI12du8zI1bgdQ+l/KsX
+kTfbGjUvhOLOlVNWVQDpL+YMIrGqgBYxy5TUNgrAcRtwpNdS2KkF5otSmMweVb5k
+hoUVv3u8UxQH/WWbNhHq1RrIlg/0rBUfi/ziShYFSB7U+aLx5DxPphTFBiDquQGp
+tB+FC4JvnukDStFihZCZ1R8CAwEAAaOCASMwggEfMA4GA1UdDwEB/wQEAwIBBjAP
+BgNVHRMBAf8EBTADAQH/MEcGA1UdIARAMD4wPAYEVR0gADA0MDIGCCsGAQUFBwIB
+FiZodHRwczovL3d3dy5nbG9iYWxzaWduLmNvbS9yZXBvc2l0b3J5LzAdBgNVHQ4E
+FgQUFPblizG2RYBKTG38woeJyjbDkGIwMwYDVR0fBCwwKjAooCagJIYiaHR0cDov
+L2NybC5nbG9iYWxzaWduLm5ldC9yb290LmNybDA+BggrBgEFBQcBAQQyMDAwLgYI
+KwYBBQUHMAGGImh0dHA6Ly9vY3NwMi5nbG9iYWxzaWduLmNvbS9yb290cjEwHwYD
+VR0jBBgwFoAUYHtmGkUNl8qJUC99BM00qP/8/UswDQYJKoZIhvcNAQEFBQADggEB
+AL7IG0l+k4LkcpI+a/kvZsSRwSM4uA6zGX34e78A2oytr8RG8bJwVb8+AHMUD+Xe
+2kYdh/Uj/waQXfqR0OgxQXL9Ct4ZM+JlR1avsNKXWL5AwYXAXCOB3J5PW2XOck7H
+Zw0vRbGQhjWjQx+B4KOUFg1b3ov/z6Xkr3yaCfRQhXh7KC0Bc0RXPPG5Nv5lCW+z
+tbbg0zMm3kyfQITRusMSg6IBsDJqOnjaiaKQRcXiD0Sk43ZXb2bUKMxC7+Td3QL4
+RyHcWJbQ7YylLTS/x+jxWIcOQ0oO5/54t5PTQ14neYhOz9x4gUk2AYAW6d1vePwb
+hcC8roQwkHT7HvfYBoc74FM=
+-----END CERTIFICATE-----`
+
+var globalSignRoot = `-----BEGIN CERTIFICATE-----
+MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG
+A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
+b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw
+MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
+YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT
+aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ
+jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp
+xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp
+1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG
+snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ
+U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8
+9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E
+BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B
+AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz
+yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE
+38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP
+AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad
+DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME
+HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==
+-----END CERTIFICATE-----`
diff --git a/src/pkg/crypto/x509/x509.go b/src/pkg/crypto/x509/x509.go
index 4dfea2c94..57f68ba7e 100644
--- a/src/pkg/crypto/x509/x509.go
+++ b/src/pkg/crypto/x509/x509.go
@@ -40,38 +40,60 @@ func ParsePKIXPublicKey(derBytes []byte) (pub interface{}, err error) {
}
algo := getPublicKeyAlgorithmFromOID(pki.Algorithm.Algorithm)
if algo == UnknownPublicKeyAlgorithm {
- return nil, errors.New("ParsePKIXPublicKey: unknown public key algorithm")
+ return nil, errors.New("x509: unknown public key algorithm")
}
return parsePublicKey(algo, &pki)
}
-// MarshalPKIXPublicKey serialises a public key to DER-encoded PKIX format.
-func MarshalPKIXPublicKey(pub interface{}) ([]byte, error) {
- var pubBytes []byte
-
+func marshalPublicKey(pub interface{}) (publicKeyBytes []byte, publicKeyAlgorithm pkix.AlgorithmIdentifier, err error) {
switch pub := pub.(type) {
case *rsa.PublicKey:
- pubBytes, _ = asn1.Marshal(rsaPublicKey{
+ publicKeyBytes, err = asn1.Marshal(rsaPublicKey{
N: pub.N,
E: pub.E,
})
+ publicKeyAlgorithm.Algorithm = oidPublicKeyRSA
+ // This is a NULL parameters value which is technically
+ // superfluous, but most other code includes it and, by
+ // doing this, we match their public key hashes.
+ publicKeyAlgorithm.Parameters = asn1.RawValue{
+ Tag: 5,
+ }
+ case *ecdsa.PublicKey:
+ publicKeyBytes = elliptic.Marshal(pub.Curve, pub.X, pub.Y)
+ oid, ok := oidFromNamedCurve(pub.Curve)
+ if !ok {
+ return nil, pkix.AlgorithmIdentifier{}, errors.New("x509: unsupported elliptic curve")
+ }
+ publicKeyAlgorithm.Algorithm = oidPublicKeyECDSA
+ var paramBytes []byte
+ paramBytes, err = asn1.Marshal(oid)
+ if err != nil {
+ return
+ }
+ publicKeyAlgorithm.Parameters.FullBytes = paramBytes
default:
- return nil, errors.New("MarshalPKIXPublicKey: unknown public key type")
+ return nil, pkix.AlgorithmIdentifier{}, errors.New("x509: only RSA and ECDSA public keys supported")
+ }
+
+ return publicKeyBytes, publicKeyAlgorithm, nil
+}
+
+// MarshalPKIXPublicKey serialises a public key to DER-encoded PKIX format.
+func MarshalPKIXPublicKey(pub interface{}) ([]byte, error) {
+ var publicKeyBytes []byte
+ var publicKeyAlgorithm pkix.AlgorithmIdentifier
+ var err error
+
+ if publicKeyBytes, publicKeyAlgorithm, err = marshalPublicKey(pub); err != nil {
+ return nil, err
}
pkix := pkixPublicKey{
- Algo: pkix.AlgorithmIdentifier{
- Algorithm: []int{1, 2, 840, 113549, 1, 1, 1},
- // This is a NULL parameters value which is technically
- // superfluous, but most other code includes it and, by
- // doing this, we match their public key hashes.
- Parameters: asn1.RawValue{
- Tag: 5,
- },
- },
+ Algo: publicKeyAlgorithm,
BitString: asn1.BitString{
- Bytes: pubBytes,
- BitLength: 8 * len(pubBytes),
+ Bytes: publicKeyBytes,
+ BitLength: 8 * len(publicKeyBytes),
},
}
@@ -453,6 +475,18 @@ type Certificate struct {
NotBefore, NotAfter time.Time // Validity bounds.
KeyUsage KeyUsage
+ // Extensions contains raw X.509 extensions. When parsing certificates,
+ // this can be used to extract non-critical extensions that are not
+ // parsed by this package. When marshaling certificates, the Extensions
+ // field is ignored, see ExtraExtensions.
+ Extensions []pkix.Extension
+
+ // ExtraExtensions contains extensions to be copied, raw, into any
+ // marshaled certificates. Values override any extensions that would
+ // otherwise be produced based on the other fields. The ExtraExtensions
+ // field is not populated when parsing certificates, see Extensions.
+ ExtraExtensions []pkix.Extension
+
ExtKeyUsage []ExtKeyUsage // Sequence of extended key usages.
UnknownExtKeyUsage []asn1.ObjectIdentifier // Encountered extended key usages unknown to this package.
@@ -463,6 +497,10 @@ type Certificate struct {
SubjectKeyId []byte
AuthorityKeyId []byte
+ // RFC 5280, 4.2.2.1 (Authority Information Access)
+ OCSPServer []string
+ IssuingCertificateURL []string
+
// Subject Alternate Name values
DNSNames []string
EmailAddresses []string
@@ -472,12 +510,15 @@ type Certificate struct {
PermittedDNSDomainsCritical bool // if true then the name constraints are marked critical.
PermittedDNSDomains []string
+ // CRL Distribution Points
+ CRLDistributionPoints []string
+
PolicyIdentifiers []asn1.ObjectIdentifier
}
// ErrUnsupportedAlgorithm results from attempting to perform an operation that
// involves algorithms that are not currently implemented.
-var ErrUnsupportedAlgorithm = errors.New("crypto/x509: cannot verify signature: algorithm unimplemented")
+var ErrUnsupportedAlgorithm = errors.New("x509: cannot verify signature: algorithm unimplemented")
// ConstraintViolationError results when a requested usage is not permitted by
// a certificate. For example: checking a signature when the public key isn't a
@@ -485,7 +526,7 @@ var ErrUnsupportedAlgorithm = errors.New("crypto/x509: cannot verify signature:
type ConstraintViolationError struct{}
func (ConstraintViolationError) Error() string {
- return "crypto/x509: invalid signature: parent certificate cannot sign this kind of certificate"
+ return "x509: invalid signature: parent certificate cannot sign this kind of certificate"
}
func (c *Certificate) Equal(other *Certificate) bool {
@@ -604,10 +645,10 @@ func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature
return err
}
if dsaSig.R.Sign() <= 0 || dsaSig.S.Sign() <= 0 {
- return errors.New("DSA signature contained zero or negative values")
+ return errors.New("x509: DSA signature contained zero or negative values")
}
if !dsa.Verify(pub, digest, dsaSig.R, dsaSig.S) {
- return errors.New("DSA verification failure")
+ return errors.New("x509: DSA verification failure")
}
return
case *ecdsa.PublicKey:
@@ -616,10 +657,10 @@ func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature
return err
}
if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 {
- return errors.New("crypto/x509: ECDSA signature contained zero or negative values")
+ return errors.New("x509: ECDSA signature contained zero or negative values")
}
if !ecdsa.Verify(pub, digest, ecdsaSig.R, ecdsaSig.S) {
- return errors.New("crypto/x509: ECDSA verification failure")
+ return errors.New("x509: ECDSA verification failure")
}
return
}
@@ -635,7 +676,7 @@ func (c *Certificate) CheckCRLSignature(crl *pkix.CertificateList) (err error) {
type UnhandledCriticalExtension struct{}
func (h UnhandledCriticalExtension) Error() string {
- return "unhandled critical extension"
+ return "x509: unhandled critical extension"
}
type basicConstraints struct {
@@ -659,6 +700,24 @@ type generalSubtree struct {
Name string `asn1:"tag:2,optional,ia5"`
}
+// RFC 5280, 4.2.2.1
+type authorityInfoAccess struct {
+ Method asn1.ObjectIdentifier
+ Location asn1.RawValue
+}
+
+// RFC 5280, 4.2.1.14
+type distributionPoint struct {
+ DistributionPoint distributionPointName `asn1:"optional,tag:0"`
+ Reason asn1.BitString `asn1:"optional,tag:1"`
+ CRLIssuer asn1.RawValue `asn1:"optional,tag:2"`
+}
+
+type distributionPointName struct {
+ FullName asn1.RawValue `asn1:"optional,tag:0"`
+ RelativeName pkix.RDNSequence `asn1:"optional,tag:1"`
+}
+
func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{}, error) {
asn1Data := keyData.PublicKey.RightAlign()
switch algo {
@@ -694,7 +753,7 @@ func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{
return nil, err
}
if p.Sign() <= 0 || params.P.Sign() <= 0 || params.Q.Sign() <= 0 || params.G.Sign() <= 0 {
- return nil, errors.New("zero or negative DSA parameter")
+ return nil, errors.New("x509: zero or negative DSA parameter")
}
pub := &dsa.PublicKey{
Parameters: dsa.Parameters{
@@ -714,11 +773,11 @@ func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{
}
namedCurve := namedCurveFromOID(*namedCurveOID)
if namedCurve == nil {
- return nil, errors.New("crypto/x509: unsupported elliptic curve")
+ return nil, errors.New("x509: unsupported elliptic curve")
}
x, y := elliptic.Unmarshal(namedCurve, asn1Data)
if x == nil {
- return nil, errors.New("crypto/x509: failed to unmarshal elliptic curve point")
+ return nil, errors.New("x509: failed to unmarshal elliptic curve point")
}
pub := &ecdsa.PublicKey{
Curve: namedCurve,
@@ -752,7 +811,7 @@ func parseCertificate(in *certificate) (*Certificate, error) {
}
if in.TBSCertificate.SerialNumber.Sign() < 0 {
- return nil, errors.New("negative serial number")
+ return nil, errors.New("x509: negative serial number")
}
out.Version = in.TBSCertificate.Version + 1
@@ -773,6 +832,8 @@ func parseCertificate(in *certificate) (*Certificate, error) {
out.NotAfter = in.TBSCertificate.Validity.NotAfter
for _, e := range in.TBSCertificate.Extensions {
+ out.Extensions = append(out.Extensions, e)
+
if len(e.Id) == 4 && e.Id[0] == 2 && e.Id[1] == 5 && e.Id[2] == 29 {
switch e.Id[3] {
case 15:
@@ -896,6 +957,39 @@ func parseCertificate(in *certificate) (*Certificate, error) {
}
continue
+ case 31:
+ // RFC 5280, 4.2.1.14
+
+ // CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
+ //
+ // DistributionPoint ::= SEQUENCE {
+ // distributionPoint [0] DistributionPointName OPTIONAL,
+ // reasons [1] ReasonFlags OPTIONAL,
+ // cRLIssuer [2] GeneralNames OPTIONAL }
+ //
+ // DistributionPointName ::= CHOICE {
+ // fullName [0] GeneralNames,
+ // nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
+
+ var cdp []distributionPoint
+ _, err := asn1.Unmarshal(e.Value, &cdp)
+ if err != nil {
+ return nil, err
+ }
+
+ for _, dp := range cdp {
+ var n asn1.RawValue
+ _, err = asn1.Unmarshal(dp.DistributionPoint.FullName.Bytes, &n)
+ if err != nil {
+ return nil, err
+ }
+
+ if n.Tag == 6 {
+ out.CRLDistributionPoints = append(out.CRLDistributionPoints, string(n.Bytes))
+ }
+ }
+ continue
+
case 35:
// RFC 5280, 4.2.1.1
var a authKeyId
@@ -952,6 +1046,24 @@ func parseCertificate(in *certificate) (*Certificate, error) {
out.PolicyIdentifiers[i] = policy.Policy
}
}
+ } else if e.Id.Equal(oidExtensionAuthorityInfoAccess) {
+ // RFC 5280 4.2.2.1: Authority Information Access
+ var aia []authorityInfoAccess
+ if _, err = asn1.Unmarshal(e.Value, &aia); err != nil {
+ return nil, err
+ }
+
+ for _, v := range aia {
+ // GeneralName: uniformResourceIdentifier [6] IA5String
+ if v.Location.Tag != 6 {
+ continue
+ }
+ if v.Method.Equal(oidAuthorityInfoAccessOcsp) {
+ out.OCSPServer = append(out.OCSPServer, string(v.Location.Bytes))
+ } else if v.Method.Equal(oidAuthorityInfoAccessIssuers) {
+ out.IssuingCertificateURL = append(out.IssuingCertificateURL, string(v.Location.Bytes))
+ }
+ }
}
if e.Critical {
@@ -1011,21 +1123,40 @@ func reverseBitsInAByte(in byte) byte {
}
var (
- oidExtensionSubjectKeyId = []int{2, 5, 29, 14}
- oidExtensionKeyUsage = []int{2, 5, 29, 15}
- oidExtensionExtendedKeyUsage = []int{2, 5, 29, 37}
- oidExtensionAuthorityKeyId = []int{2, 5, 29, 35}
- oidExtensionBasicConstraints = []int{2, 5, 29, 19}
- oidExtensionSubjectAltName = []int{2, 5, 29, 17}
- oidExtensionCertificatePolicies = []int{2, 5, 29, 32}
- oidExtensionNameConstraints = []int{2, 5, 29, 30}
+ oidExtensionSubjectKeyId = []int{2, 5, 29, 14}
+ oidExtensionKeyUsage = []int{2, 5, 29, 15}
+ oidExtensionExtendedKeyUsage = []int{2, 5, 29, 37}
+ oidExtensionAuthorityKeyId = []int{2, 5, 29, 35}
+ oidExtensionBasicConstraints = []int{2, 5, 29, 19}
+ oidExtensionSubjectAltName = []int{2, 5, 29, 17}
+ oidExtensionCertificatePolicies = []int{2, 5, 29, 32}
+ oidExtensionNameConstraints = []int{2, 5, 29, 30}
+ oidExtensionCRLDistributionPoints = []int{2, 5, 29, 31}
+ oidExtensionAuthorityInfoAccess = []int{1, 3, 6, 1, 5, 5, 7, 1, 1}
+)
+
+var (
+ oidAuthorityInfoAccessOcsp = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 48, 1}
+ oidAuthorityInfoAccessIssuers = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 48, 2}
)
+// oidNotInExtensions returns whether an extension with the given oid exists in
+// extensions.
+func oidInExtensions(oid asn1.ObjectIdentifier, extensions []pkix.Extension) bool {
+ for _, e := range extensions {
+ if e.Id.Equal(oid) {
+ return true
+ }
+ }
+ return false
+}
+
func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
- ret = make([]pkix.Extension, 8 /* maximum number of elements. */)
+ ret = make([]pkix.Extension, 10 /* maximum number of elements. */)
n := 0
- if template.KeyUsage != 0 {
+ if template.KeyUsage != 0 &&
+ !oidInExtensions(oidExtensionKeyUsage, template.ExtraExtensions) {
ret[n].Id = oidExtensionKeyUsage
ret[n].Critical = true
@@ -1045,7 +1176,8 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
n++
}
- if len(template.ExtKeyUsage) > 0 || len(template.UnknownExtKeyUsage) > 0 {
+ if (len(template.ExtKeyUsage) > 0 || len(template.UnknownExtKeyUsage) > 0) &&
+ !oidInExtensions(oidExtensionExtendedKeyUsage, template.ExtraExtensions) {
ret[n].Id = oidExtensionExtendedKeyUsage
var oids []asn1.ObjectIdentifier
@@ -1066,7 +1198,7 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
n++
}
- if template.BasicConstraintsValid {
+ if template.BasicConstraintsValid && !oidInExtensions(oidExtensionBasicConstraints, template.ExtraExtensions) {
ret[n].Id = oidExtensionBasicConstraints
ret[n].Value, err = asn1.Marshal(basicConstraints{template.IsCA, template.MaxPathLen})
ret[n].Critical = true
@@ -1076,7 +1208,7 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
n++
}
- if len(template.SubjectKeyId) > 0 {
+ if len(template.SubjectKeyId) > 0 && !oidInExtensions(oidExtensionSubjectKeyId, template.ExtraExtensions) {
ret[n].Id = oidExtensionSubjectKeyId
ret[n].Value, err = asn1.Marshal(template.SubjectKeyId)
if err != nil {
@@ -1085,7 +1217,7 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
n++
}
- if len(template.AuthorityKeyId) > 0 {
+ if len(template.AuthorityKeyId) > 0 && !oidInExtensions(oidExtensionAuthorityKeyId, template.ExtraExtensions) {
ret[n].Id = oidExtensionAuthorityKeyId
ret[n].Value, err = asn1.Marshal(authKeyId{template.AuthorityKeyId})
if err != nil {
@@ -1094,7 +1226,31 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
n++
}
- if len(template.DNSNames) > 0 || len(template.EmailAddresses) > 0 || len(template.IPAddresses) > 0 {
+ if (len(template.OCSPServer) > 0 || len(template.IssuingCertificateURL) > 0) &&
+ !oidInExtensions(oidExtensionAuthorityInfoAccess, template.ExtraExtensions) {
+ ret[n].Id = oidExtensionAuthorityInfoAccess
+ var aiaValues []authorityInfoAccess
+ for _, name := range template.OCSPServer {
+ aiaValues = append(aiaValues, authorityInfoAccess{
+ Method: oidAuthorityInfoAccessOcsp,
+ Location: asn1.RawValue{Tag: 6, Class: 2, Bytes: []byte(name)},
+ })
+ }
+ for _, name := range template.IssuingCertificateURL {
+ aiaValues = append(aiaValues, authorityInfoAccess{
+ Method: oidAuthorityInfoAccessIssuers,
+ Location: asn1.RawValue{Tag: 6, Class: 2, Bytes: []byte(name)},
+ })
+ }
+ ret[n].Value, err = asn1.Marshal(aiaValues)
+ if err != nil {
+ return
+ }
+ n++
+ }
+
+ if (len(template.DNSNames) > 0 || len(template.EmailAddresses) > 0 || len(template.IPAddresses) > 0) &&
+ !oidInExtensions(oidExtensionSubjectAltName, template.ExtraExtensions) {
ret[n].Id = oidExtensionSubjectAltName
var rawValues []asn1.RawValue
for _, name := range template.DNSNames {
@@ -1118,7 +1274,8 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
n++
}
- if len(template.PolicyIdentifiers) > 0 {
+ if len(template.PolicyIdentifiers) > 0 &&
+ !oidInExtensions(oidExtensionCertificatePolicies, template.ExtraExtensions) {
ret[n].Id = oidExtensionCertificatePolicies
policies := make([]policyInformation, len(template.PolicyIdentifiers))
for i, policy := range template.PolicyIdentifiers {
@@ -1131,7 +1288,8 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
n++
}
- if len(template.PermittedDNSDomains) > 0 {
+ if len(template.PermittedDNSDomains) > 0 &&
+ !oidInExtensions(oidExtensionNameConstraints, template.ExtraExtensions) {
ret[n].Id = oidExtensionNameConstraints
ret[n].Critical = template.PermittedDNSDomainsCritical
@@ -1147,10 +1305,33 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
n++
}
+ if len(template.CRLDistributionPoints) > 0 &&
+ !oidInExtensions(oidExtensionCRLDistributionPoints, template.ExtraExtensions) {
+ ret[n].Id = oidExtensionCRLDistributionPoints
+
+ var crlDp []distributionPoint
+ for _, name := range template.CRLDistributionPoints {
+ rawFullName, _ := asn1.Marshal(asn1.RawValue{Tag: 6, Class: 2, Bytes: []byte(name)})
+
+ dp := distributionPoint{
+ DistributionPoint: distributionPointName{
+ FullName: asn1.RawValue{Tag: 0, Class: 2, Bytes: rawFullName},
+ },
+ }
+ crlDp = append(crlDp, dp)
+ }
+
+ ret[n].Value, err = asn1.Marshal(crlDp)
+ if err != nil {
+ return
+ }
+ n++
+ }
+
// Adding another extension here? Remember to update the maximum number
// of elements in the make() at the top of the function.
- return ret[0:n], nil
+ return append(ret[:n], template.ExtraExtensions...), nil
}
func subjectBytes(cert *Certificate) ([]byte, error) {
@@ -1179,28 +1360,8 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub interf
var publicKeyBytes []byte
var publicKeyAlgorithm pkix.AlgorithmIdentifier
- switch pub := pub.(type) {
- case *rsa.PublicKey:
- publicKeyBytes, err = asn1.Marshal(rsaPublicKey{
- N: pub.N,
- E: pub.E,
- })
- publicKeyAlgorithm.Algorithm = oidPublicKeyRSA
- case *ecdsa.PublicKey:
- oid, ok := oidFromNamedCurve(pub.Curve)
- if !ok {
- return nil, errors.New("x509: unknown elliptic curve")
- }
- publicKeyAlgorithm.Algorithm = oidPublicKeyECDSA
- var paramBytes []byte
- paramBytes, err = asn1.Marshal(oid)
- if err != nil {
- return
- }
- publicKeyAlgorithm.Parameters.FullBytes = paramBytes
- publicKeyBytes = elliptic.Marshal(pub.Curve, pub.X, pub.Y)
- default:
- return nil, errors.New("x509: only RSA and ECDSA public keys supported")
+ if publicKeyBytes, publicKeyAlgorithm, err = marshalPublicKey(pub); err != nil {
+ return nil, err
}
var signatureAlgorithm pkix.AlgorithmIdentifier
diff --git a/src/pkg/crypto/x509/x509_test.go b/src/pkg/crypto/x509/x509_test.go
index abd4fe84d..f1097e992 100644
--- a/src/pkg/crypto/x509/x509_test.go
+++ b/src/pkg/crypto/x509/x509_test.go
@@ -237,6 +237,11 @@ func TestCertificateParse(t *testing.T) {
if err := certs[0].VerifyHostname("mail.google.com"); err != nil {
t.Error(err)
}
+
+ const expectedExtensions = 4
+ if n := len(certs[0].Extensions); n != expectedExtensions {
+ t.Errorf("want %d extensions, got %d", expectedExtensions, n)
+ }
}
var certBytes = "308203223082028ba00302010202106edf0d9499fd4533dd1297fc42a93be1300d06092a864886" +
@@ -308,7 +313,8 @@ func TestCreateSelfSignedCertificate(t *testing.T) {
}
testExtKeyUsage := []ExtKeyUsage{ExtKeyUsageClientAuth, ExtKeyUsageServerAuth}
- testUnknownExtKeyUsage := []asn1.ObjectIdentifier{[]int{1, 2, 3}, []int{3, 2, 1}}
+ testUnknownExtKeyUsage := []asn1.ObjectIdentifier{[]int{1, 2, 3}, []int{2, 59, 1}}
+ extraExtensionData := []byte("extra extension")
for _, test := range tests {
commonName := "test.example.com"
@@ -330,12 +336,30 @@ func TestCreateSelfSignedCertificate(t *testing.T) {
BasicConstraintsValid: true,
IsCA: true,
+ OCSPServer: []string{"http://ocsp.example.com"},
+ IssuingCertificateURL: []string{"http://crt.example.com/ca1.crt"},
+
DNSNames: []string{"test.example.com"},
EmailAddresses: []string{"gopher@golang.org"},
IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1).To4(), net.ParseIP("2001:4860:0:2001::68")},
PolicyIdentifiers: []asn1.ObjectIdentifier{[]int{1, 2, 3}},
PermittedDNSDomains: []string{".example.com", "example.com"},
+
+ CRLDistributionPoints: []string{"http://crl1.example.com/ca1.crl", "http://crl2.example.com/ca1.crl"},
+
+ ExtraExtensions: []pkix.Extension{
+ {
+ Id: []int{1, 2, 3, 4},
+ Value: extraExtensionData,
+ },
+ // This extension should override the SubjectKeyId, above.
+ {
+ Id: oidExtensionSubjectKeyId,
+ Critical: false,
+ Value: []byte{0x04, 0x04, 4, 3, 2, 1},
+ },
+ },
}
derBytes, err := CreateCertificate(random, &template, &template, test.pub, test.priv)
@@ -374,6 +398,14 @@ func TestCreateSelfSignedCertificate(t *testing.T) {
t.Errorf("%s: unknown extkeyusage wasn't correctly copied from the template. Got %v, want %v", test.name, cert.UnknownExtKeyUsage, testUnknownExtKeyUsage)
}
+ if !reflect.DeepEqual(cert.OCSPServer, template.OCSPServer) {
+ t.Errorf("%s: OCSP servers differ from template. Got %v, want %v", test.name, cert.OCSPServer, template.OCSPServer)
+ }
+
+ if !reflect.DeepEqual(cert.IssuingCertificateURL, template.IssuingCertificateURL) {
+ t.Errorf("%s: Issuing certificate URLs differ from template. Got %v, want %v", test.name, cert.IssuingCertificateURL, template.IssuingCertificateURL)
+ }
+
if !reflect.DeepEqual(cert.DNSNames, template.DNSNames) {
t.Errorf("%s: SAN DNS names differ from template. Got %v, want %v", test.name, cert.DNSNames, template.DNSNames)
}
@@ -386,6 +418,18 @@ func TestCreateSelfSignedCertificate(t *testing.T) {
t.Errorf("%s: SAN IPs differ from template. Got %v, want %v", test.name, cert.IPAddresses, template.IPAddresses)
}
+ if !reflect.DeepEqual(cert.CRLDistributionPoints, template.CRLDistributionPoints) {
+ t.Errorf("%s: CRL distribution points differ from template. Got %v, want %v", test.name, cert.CRLDistributionPoints, template.CRLDistributionPoints)
+ }
+
+ if !bytes.Equal(cert.SubjectKeyId, []byte{4, 3, 2, 1}) {
+ t.Errorf("%s: ExtraExtensions didn't override SubjectKeyId", test.name)
+ }
+
+ if bytes.Index(derBytes, extraExtensionData) == -1 {
+ t.Errorf("%s: didn't find extra extension in DER output", test.name)
+ }
+
if test.checkSig {
err = cert.CheckSignatureFrom(cert)
if err != nil {
diff --git a/src/pkg/database/sql/convert_test.go b/src/pkg/database/sql/convert_test.go
index 950e24fc3..a39c2c54f 100644
--- a/src/pkg/database/sql/convert_test.go
+++ b/src/pkg/database/sql/convert_test.go
@@ -267,14 +267,14 @@ func TestValueConverters(t *testing.T) {
goterr = err.Error()
}
if goterr != tt.err {
- t.Errorf("test %d: %s(%T(%v)) error = %q; want error = %q",
+ t.Errorf("test %d: %T(%T(%v)) error = %q; want error = %q",
i, tt.c, tt.in, tt.in, goterr, tt.err)
}
if tt.err != "" {
continue
}
if !reflect.DeepEqual(out, tt.out) {
- t.Errorf("test %d: %s(%T(%v)) = %v (%T); want %v (%T)",
+ t.Errorf("test %d: %T(%T(%v)) = %v (%T); want %v (%T)",
i, tt.c, tt.in, tt.in, out, out, tt.out, tt.out)
}
}
diff --git a/src/pkg/database/sql/driver/driver.go b/src/pkg/database/sql/driver/driver.go
index d7ca94f78..0828e63c6 100644
--- a/src/pkg/database/sql/driver/driver.go
+++ b/src/pkg/database/sql/driver/driver.go
@@ -140,8 +140,8 @@ type Stmt interface {
}
// ColumnConverter may be optionally implemented by Stmt if the
-// the statement is aware of its own columns' types and can
-// convert from any type to a driver Value.
+// statement is aware of its own columns' types and can convert from
+// any type to a driver Value.
type ColumnConverter interface {
// ColumnConverter returns a ValueConverter for the provided
// column index. If the type of a specific column isn't known
diff --git a/src/pkg/database/sql/driver/types_test.go b/src/pkg/database/sql/driver/types_test.go
index ab82bca71..1ce0ff065 100644
--- a/src/pkg/database/sql/driver/types_test.go
+++ b/src/pkg/database/sql/driver/types_test.go
@@ -51,14 +51,14 @@ func TestValueConverters(t *testing.T) {
goterr = err.Error()
}
if goterr != tt.err {
- t.Errorf("test %d: %s(%T(%v)) error = %q; want error = %q",
+ t.Errorf("test %d: %T(%T(%v)) error = %q; want error = %q",
i, tt.c, tt.in, tt.in, goterr, tt.err)
}
if tt.err != "" {
continue
}
if !reflect.DeepEqual(out, tt.out) {
- t.Errorf("test %d: %s(%T(%v)) = %v (%T); want %v (%T)",
+ t.Errorf("test %d: %T(%T(%v)) = %v (%T); want %v (%T)",
i, tt.c, tt.in, tt.in, out, out, tt.out, tt.out)
}
}
diff --git a/src/pkg/database/sql/fakedb_test.go b/src/pkg/database/sql/fakedb_test.go
index d900e2ceb..a8adfdd94 100644
--- a/src/pkg/database/sql/fakedb_test.go
+++ b/src/pkg/database/sql/fakedb_test.go
@@ -38,6 +38,8 @@ type fakeDriver struct {
mu sync.Mutex // guards 3 following fields
openCount int // conn opens
closeCount int // conn closes
+ waitCh chan struct{}
+ waitingCh chan struct{}
dbs map[string]*fakeDB
}
@@ -146,6 +148,12 @@ func (d *fakeDriver) Open(dsn string) (driver.Conn, error) {
if len(parts) >= 2 && parts[1] == "badConn" {
conn.bad = true
}
+ if d.waitCh != nil {
+ d.waitingCh <- struct{}{}
+ <-d.waitCh
+ d.waitCh = nil
+ d.waitingCh = nil
+ }
return conn, nil
}
@@ -447,6 +455,10 @@ func (c *fakeConn) Prepare(query string) (driver.Stmt, error) {
return c.prepareCreate(stmt, parts)
case "INSERT":
return c.prepareInsert(stmt, parts)
+ case "NOSERT":
+ // Do all the prep-work like for an INSERT but don't actually insert the row.
+ // Used for some of the concurrent tests.
+ return c.prepareInsert(stmt, parts)
default:
stmt.Close()
return nil, errf("unsupported command type %q", cmd)
@@ -497,13 +509,20 @@ func (s *fakeStmt) Exec(args []driver.Value) (driver.Result, error) {
}
return driver.ResultNoRows, nil
case "INSERT":
- return s.execInsert(args)
+ return s.execInsert(args, true)
+ case "NOSERT":
+ // Do all the prep-work like for an INSERT but don't actually insert the row.
+ // Used for some of the concurrent tests.
+ return s.execInsert(args, false)
}
fmt.Printf("EXEC statement, cmd=%q: %#v\n", s.cmd, s)
return nil, fmt.Errorf("unimplemented statement Exec command type of %q", s.cmd)
}
-func (s *fakeStmt) execInsert(args []driver.Value) (driver.Result, error) {
+// When doInsert is true, add the row to the table.
+// When doInsert is false do prep-work and error checking, but don't
+// actually add the row to the table.
+func (s *fakeStmt) execInsert(args []driver.Value, doInsert bool) (driver.Result, error) {
db := s.c.db
if len(args) != s.placeholders {
panic("error in pkg db; should only get here if size is correct")
@@ -518,7 +537,10 @@ func (s *fakeStmt) execInsert(args []driver.Value) (driver.Result, error) {
t.mu.Lock()
defer t.mu.Unlock()
- cols := make([]interface{}, len(t.colname))
+ var cols []interface{}
+ if doInsert {
+ cols = make([]interface{}, len(t.colname))
+ }
argPos := 0
for n, colname := range s.colName {
colidx := t.columnIndex(colname)
@@ -532,10 +554,14 @@ func (s *fakeStmt) execInsert(args []driver.Value) (driver.Result, error) {
} else {
val = s.colValue[n]
}
- cols[colidx] = val
+ if doInsert {
+ cols[colidx] = val
+ }
}
- t.rows = append(t.rows, &row{cols: cols})
+ if doInsert {
+ t.rows = append(t.rows, &row{cols: cols})
+ }
return driver.RowsAffected(1), nil
}
@@ -608,9 +634,10 @@ rows:
}
cursor := &rowsCursor{
- pos: -1,
- rows: mrows,
- cols: s.colName,
+ pos: -1,
+ rows: mrows,
+ cols: s.colName,
+ errPos: -1,
}
return cursor, nil
}
@@ -635,6 +662,10 @@ type rowsCursor struct {
rows []*row
closed bool
+ // errPos and err are for making Next return early with error.
+ errPos int
+ err error
+
// a clone of slices to give out to clients, indexed by the
// the original slice's first byte address. we clone them
// just so we're able to corrupt them on close.
@@ -660,6 +691,9 @@ func (rc *rowsCursor) Next(dest []driver.Value) error {
return errors.New("fakedb: cursor is closed")
}
rc.pos++
+ if rc.pos == rc.errPos {
+ return rc.err
+ }
if rc.pos >= len(rc.rows) {
return io.EOF // per interface spec
}
diff --git a/src/pkg/database/sql/sql.go b/src/pkg/database/sql/sql.go
index a80782bfe..dddf5a3f2 100644
--- a/src/pkg/database/sql/sql.go
+++ b/src/pkg/database/sql/sql.go
@@ -7,9 +7,13 @@
//
// The sql package must be used in conjunction with a database driver.
// See http://golang.org/s/sqldrivers for a list of drivers.
+//
+// For more usage examples, see the wiki page at
+// http://golang.org/s/sqlwiki.
package sql
import (
+ "container/list"
"database/sql/driver"
"errors"
"fmt"
@@ -192,12 +196,22 @@ type DB struct {
driver driver.Driver
dsn string
- mu sync.Mutex // protects following fields
- freeConn []*driverConn
+ mu sync.Mutex // protects following fields
+ freeConn *list.List // of *driverConn
+ connRequests *list.List // of connRequest
+ numOpen int
+ pendingOpens int
+ // Used to signal the need for new connections
+ // a goroutine running connectionOpener() reads on this chan and
+ // maybeOpenNewConnections sends on the chan (one send per needed connection)
+ // It is closed during db.Close(). The close tells the connectionOpener
+ // goroutine to exit.
+ openerCh chan struct{}
closed bool
dep map[finalCloser]depSet
lastPut map[*driverConn]string // stacktrace of last conn's put; debug only
maxIdle int // zero means defaultMaxIdleConns; negative means 0
+ maxOpen int // <= 0 means unlimited
}
// driverConn wraps a driver.Conn with a mutex, to
@@ -217,6 +231,13 @@ type driverConn struct {
inUse bool
onPut []func() // code (with db.mu held) run when conn is next returned
dbmuClosed bool // same as closed, but guarded by db.mu, for connIfFree
+ // This is the Element returned by db.freeConn.PushFront(conn).
+ // It's used by connIfFree to remove the conn from the freeConn list.
+ listElem *list.Element
+}
+
+func (dc *driverConn) releaseConn(err error) {
+ dc.db.putConn(dc, err)
}
func (dc *driverConn) removeOpenStmt(si driver.Stmt) {
@@ -250,15 +271,14 @@ func (dc *driverConn) prepareLocked(query string) (driver.Stmt, error) {
}
// the dc.db's Mutex is held.
-func (dc *driverConn) closeDBLocked() error {
+func (dc *driverConn) closeDBLocked() func() error {
dc.Lock()
+ defer dc.Unlock()
if dc.closed {
- dc.Unlock()
- return errors.New("sql: duplicate driverConn close")
+ return func() error { return errors.New("sql: duplicate driverConn close") }
}
dc.closed = true
- dc.Unlock() // not defer; removeDep finalClose calls may need to lock
- return dc.db.removeDepLocked(dc, dc)()
+ return dc.db.removeDepLocked(dc, dc)
}
func (dc *driverConn) Close() error {
@@ -289,8 +309,13 @@ func (dc *driverConn) finalClose() error {
err := dc.ci.Close()
dc.ci = nil
dc.finalClosed = true
-
dc.Unlock()
+
+ dc.db.mu.Lock()
+ dc.db.numOpen--
+ dc.db.maybeOpenNewConnections()
+ dc.db.mu.Unlock()
+
return err
}
@@ -353,26 +378,36 @@ func (db *DB) removeDep(x finalCloser, dep interface{}) error {
func (db *DB) removeDepLocked(x finalCloser, dep interface{}) func() error {
//println(fmt.Sprintf("removeDep(%T %p, %T %p)", x, x, dep, dep))
- done := false
- xdep := db.dep[x]
- if xdep != nil {
- delete(xdep, dep)
- if len(xdep) == 0 {
- delete(db.dep, x)
- done = true
- }
+ xdep, ok := db.dep[x]
+ if !ok {
+ panic(fmt.Sprintf("unpaired removeDep: no deps for %T", x))
}
- if !done {
+ l0 := len(xdep)
+ delete(xdep, dep)
+
+ switch len(xdep) {
+ case l0:
+ // Nothing removed. Shouldn't happen.
+ panic(fmt.Sprintf("unpaired removeDep: no %T dep on %T", dep, x))
+ case 0:
+ // No more dependencies.
+ delete(db.dep, x)
+ return x.finalClose
+ default:
+ // Dependencies remain.
return func() error { return nil }
}
- return func() error {
- //println(fmt.Sprintf("calling final close on %T %v (%#v)", x, x, x))
- return x.finalClose()
- }
}
+// This is the size of the connectionOpener request chan (dn.openerCh).
+// This value should be larger than the maximum typical value
+// used for db.maxOpen. If maxOpen is significantly larger than
+// connectionRequestQueueSize then it is possible for ALL calls into the *DB
+// to block until the connectionOpener can satify the backlog of requests.
+var connectionRequestQueueSize = 1000000
+
// Open opens a database specified by its database driver name and a
// driver-specific data source name, usually consisting of at least a
// database name and connection information.
@@ -391,10 +426,14 @@ func Open(driverName, dataSourceName string) (*DB, error) {
return nil, fmt.Errorf("sql: unknown driver %q (forgotten import?)", driverName)
}
db := &DB{
- driver: driveri,
- dsn: dataSourceName,
- lastPut: make(map[*driverConn]string),
- }
+ driver: driveri,
+ dsn: dataSourceName,
+ openerCh: make(chan struct{}, connectionRequestQueueSize),
+ lastPut: make(map[*driverConn]string),
+ }
+ db.freeConn = list.New()
+ db.connRequests = list.New()
+ go db.connectionOpener()
return db, nil
}
@@ -415,16 +454,32 @@ func (db *DB) Ping() error {
// Close closes the database, releasing any open resources.
func (db *DB) Close() error {
db.mu.Lock()
- defer db.mu.Unlock()
+ if db.closed { // Make DB.Close idempotent
+ db.mu.Unlock()
+ return nil
+ }
+ close(db.openerCh)
var err error
- for _, dc := range db.freeConn {
- err1 := dc.closeDBLocked()
+ fns := make([]func() error, 0, db.freeConn.Len())
+ for db.freeConn.Front() != nil {
+ dc := db.freeConn.Front().Value.(*driverConn)
+ dc.listElem = nil
+ fns = append(fns, dc.closeDBLocked())
+ db.freeConn.Remove(db.freeConn.Front())
+ }
+ db.closed = true
+ for db.connRequests.Front() != nil {
+ req := db.connRequests.Front().Value.(connRequest)
+ db.connRequests.Remove(db.connRequests.Front())
+ close(req)
+ }
+ db.mu.Unlock()
+ for _, fn := range fns {
+ err1 := fn()
if err1 != nil {
err = err1
}
}
- db.freeConn = nil
- db.closed = true
return err
}
@@ -446,50 +501,168 @@ func (db *DB) maxIdleConnsLocked() int {
// SetMaxIdleConns sets the maximum number of connections in the idle
// connection pool.
//
+// If MaxOpenConns is greater than 0 but less than the new MaxIdleConns
+// then the new MaxIdleConns will be reduced to match the MaxOpenConns limit
+//
// If n <= 0, no idle connections are retained.
func (db *DB) SetMaxIdleConns(n int) {
db.mu.Lock()
- defer db.mu.Unlock()
if n > 0 {
db.maxIdle = n
} else {
// No idle connections.
db.maxIdle = -1
}
- for len(db.freeConn) > 0 && len(db.freeConn) > n {
- nfree := len(db.freeConn)
- dc := db.freeConn[nfree-1]
- db.freeConn[nfree-1] = nil
- db.freeConn = db.freeConn[:nfree-1]
- go dc.Close()
+ // Make sure maxIdle doesn't exceed maxOpen
+ if db.maxOpen > 0 && db.maxIdleConnsLocked() > db.maxOpen {
+ db.maxIdle = db.maxOpen
+ }
+ var closing []*driverConn
+ for db.freeConn.Len() > db.maxIdleConnsLocked() {
+ dc := db.freeConn.Back().Value.(*driverConn)
+ dc.listElem = nil
+ db.freeConn.Remove(db.freeConn.Back())
+ closing = append(closing, dc)
+ }
+ db.mu.Unlock()
+ for _, c := range closing {
+ c.Close()
+ }
+}
+
+// SetMaxOpenConns sets the maximum number of open connections to the database.
+//
+// If MaxIdleConns is greater than 0 and the new MaxOpenConns is less than
+// MaxIdleConns, then MaxIdleConns will be reduced to match the new
+// MaxOpenConns limit
+//
+// If n <= 0, then there is no limit on the number of open connections.
+// The default is 0 (unlimited).
+func (db *DB) SetMaxOpenConns(n int) {
+ db.mu.Lock()
+ db.maxOpen = n
+ if n < 0 {
+ db.maxOpen = 0
+ }
+ syncMaxIdle := db.maxOpen > 0 && db.maxIdleConnsLocked() > db.maxOpen
+ db.mu.Unlock()
+ if syncMaxIdle {
+ db.SetMaxIdleConns(n)
+ }
+}
+
+// Assumes db.mu is locked.
+// If there are connRequests and the connection limit hasn't been reached,
+// then tell the connectionOpener to open new connections.
+func (db *DB) maybeOpenNewConnections() {
+ numRequests := db.connRequests.Len() - db.pendingOpens
+ if db.maxOpen > 0 {
+ numCanOpen := db.maxOpen - (db.numOpen + db.pendingOpens)
+ if numRequests > numCanOpen {
+ numRequests = numCanOpen
+ }
+ }
+ for numRequests > 0 {
+ db.pendingOpens++
+ numRequests--
+ db.openerCh <- struct{}{}
+ }
+}
+
+// Runs in a seperate goroutine, opens new connections when requested.
+func (db *DB) connectionOpener() {
+ for _ = range db.openerCh {
+ db.openNewConnection()
+ }
+}
+
+// Open one new connection
+func (db *DB) openNewConnection() {
+ ci, err := db.driver.Open(db.dsn)
+ db.mu.Lock()
+ defer db.mu.Unlock()
+ if db.closed {
+ if err == nil {
+ ci.Close()
+ }
+ return
+ }
+ db.pendingOpens--
+ if err != nil {
+ db.putConnDBLocked(nil, err)
+ return
+ }
+ dc := &driverConn{
+ db: db,
+ ci: ci,
+ }
+ if db.putConnDBLocked(dc, err) {
+ db.addDepLocked(dc, dc)
+ db.numOpen++
+ } else {
+ ci.Close()
}
}
+// connRequest represents one request for a new connection
+// When there are no idle connections available, DB.conn will create
+// a new connRequest and put it on the db.connRequests list.
+type connRequest chan<- interface{} // takes either a *driverConn or an error
+
+var errDBClosed = errors.New("sql: database is closed")
+
// conn returns a newly-opened or cached *driverConn
func (db *DB) conn() (*driverConn, error) {
db.mu.Lock()
if db.closed {
db.mu.Unlock()
- return nil, errors.New("sql: database is closed")
+ return nil, errDBClosed
+ }
+
+ // If db.maxOpen > 0 and the number of open connections is over the limit
+ // or there are no free connection, then make a request and wait.
+ if db.maxOpen > 0 && (db.numOpen >= db.maxOpen || db.freeConn.Len() == 0) {
+ // Make the connRequest channel. It's buffered so that the
+ // connectionOpener doesn't block while waiting for the req to be read.
+ ch := make(chan interface{}, 1)
+ req := connRequest(ch)
+ db.connRequests.PushBack(req)
+ db.maybeOpenNewConnections()
+ db.mu.Unlock()
+ ret, ok := <-ch
+ if !ok {
+ return nil, errDBClosed
+ }
+ switch ret.(type) {
+ case *driverConn:
+ return ret.(*driverConn), nil
+ case error:
+ return nil, ret.(error)
+ default:
+ panic("sql: Unexpected type passed through connRequest.ch")
+ }
}
- if n := len(db.freeConn); n > 0 {
- conn := db.freeConn[n-1]
- db.freeConn = db.freeConn[:n-1]
+
+ if f := db.freeConn.Front(); f != nil {
+ conn := f.Value.(*driverConn)
+ conn.listElem = nil
+ db.freeConn.Remove(f)
conn.inUse = true
db.mu.Unlock()
return conn, nil
}
- db.mu.Unlock()
+ db.mu.Unlock()
ci, err := db.driver.Open(db.dsn)
if err != nil {
return nil, err
}
+ db.mu.Lock()
+ db.numOpen++
dc := &driverConn{
db: db,
ci: ci,
}
- db.mu.Lock()
db.addDepLocked(dc, dc)
dc.inUse = true
db.mu.Unlock()
@@ -511,18 +684,15 @@ var (
func (db *DB) connIfFree(wanted *driverConn) (*driverConn, error) {
db.mu.Lock()
defer db.mu.Unlock()
- if wanted.inUse {
- return nil, errConnBusy
- }
if wanted.dbmuClosed {
return nil, errConnClosed
}
- for i, conn := range db.freeConn {
- if conn != wanted {
- continue
- }
- db.freeConn[i] = db.freeConn[len(db.freeConn)-1]
- db.freeConn = db.freeConn[:len(db.freeConn)-1]
+ if wanted.inUse {
+ return nil, errConnBusy
+ }
+ if wanted.listElem != nil {
+ db.freeConn.Remove(wanted.listElem)
+ wanted.listElem = nil
wanted.inUse = true
return wanted, nil
}
@@ -582,20 +752,50 @@ func (db *DB) putConn(dc *driverConn, err error) {
if err == driver.ErrBadConn {
// Don't reuse bad connections.
+ // Since the conn is considered bad and is being discarded, treat it
+ // as closed. Don't decrement the open count here, finalClose will
+ // take care of that.
+ db.maybeOpenNewConnections()
db.mu.Unlock()
+ dc.Close()
return
}
if putConnHook != nil {
putConnHook(db, dc)
}
- if n := len(db.freeConn); !db.closed && n < db.maxIdleConnsLocked() {
- db.freeConn = append(db.freeConn, dc)
- db.mu.Unlock()
- return
- }
+ added := db.putConnDBLocked(dc, nil)
db.mu.Unlock()
- dc.Close()
+ if !added {
+ dc.Close()
+ }
+}
+
+// Satisfy a connRequest or put the driverConn in the idle pool and return true
+// or return false.
+// putConnDBLocked will satisfy a connRequest if there is one, or it will
+// return the *driverConn to the freeConn list if err != nil and the idle
+// connection limit would not be reached.
+// If err != nil, the value of dc is ignored.
+// If err == nil, then dc must not equal nil.
+// If a connRequest was fullfilled or the *driverConn was placed in the
+// freeConn list, then true is returned, otherwise false is returned.
+func (db *DB) putConnDBLocked(dc *driverConn, err error) bool {
+ if db.connRequests.Len() > 0 {
+ req := db.connRequests.Front().Value.(connRequest)
+ db.connRequests.Remove(db.connRequests.Front())
+ if err != nil {
+ req <- err
+ } else {
+ dc.inUse = true
+ req <- dc
+ }
+ return true
+ } else if err == nil && !db.closed && db.maxIdleConnsLocked() > 0 && db.maxIdleConnsLocked() > db.freeConn.Len() {
+ dc.listElem = db.freeConn.PushFront(dc)
+ return true
+ }
+ return false
}
// Prepare creates a prepared statement for later queries or executions.
@@ -710,9 +910,7 @@ func (db *DB) query(query string, args []interface{}) (*Rows, error) {
return nil, err
}
- releaseConn := func(err error) { db.putConn(ci, err) }
-
- return db.queryConn(ci, releaseConn, query, args)
+ return db.queryConn(ci, ci.releaseConn, query, args)
}
// queryConn executes a query on the given connection.
@@ -754,10 +952,10 @@ func (db *DB) queryConn(dc *driverConn, releaseConn func(error), query string, a
ds := driverStmt{dc, si}
rowsi, err := rowsiFromStatement(ds, args...)
if err != nil {
- releaseConn(err)
dc.Lock()
si.Close()
dc.Unlock()
+ releaseConn(err)
return nil, err
}
@@ -1154,8 +1352,7 @@ func (s *Stmt) connStmt() (ci *driverConn, releaseConn func(error), si driver.St
}
conn := cs.dc
- releaseConn = func(err error) { s.db.putConn(conn, err) }
- return conn, releaseConn, cs.si, nil
+ return conn, conn.releaseConn, cs.si, nil
}
// Query executes a prepared query statement with the given arguments
@@ -1245,27 +1442,32 @@ func (s *Stmt) Close() error {
return s.stickyErr
}
s.mu.Lock()
- defer s.mu.Unlock()
if s.closed {
+ s.mu.Unlock()
return nil
}
s.closed = true
if s.tx != nil {
s.txsi.Close()
+ s.mu.Unlock()
return nil
}
+ s.mu.Unlock()
return s.db.removeDep(s, s)
}
func (s *Stmt) finalClose() error {
- for _, v := range s.css {
- s.db.noteUnusedDriverStatement(v.dc, v.si)
- v.dc.removeOpenStmt(v.si)
- s.db.removeDep(v.dc, s)
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ if s.css != nil {
+ for _, v := range s.css {
+ s.db.noteUnusedDriverStatement(v.dc, v.si)
+ v.dc.removeOpenStmt(v.si)
+ }
+ s.css = nil
}
- s.css = nil
return nil
}
@@ -1289,7 +1491,7 @@ type Rows struct {
closed bool
lastcols []driver.Value
- lasterr error
+ lasterr error // non-nil only if closed is true
closeStmt driver.Stmt // if non-nil, statement to Close on close
}
@@ -1301,20 +1503,19 @@ func (rs *Rows) Next() bool {
if rs.closed {
return false
}
- if rs.lasterr != nil {
- return false
- }
if rs.lastcols == nil {
rs.lastcols = make([]driver.Value, len(rs.rowsi.Columns()))
}
rs.lasterr = rs.rowsi.Next(rs.lastcols)
- if rs.lasterr == io.EOF {
+ if rs.lasterr != nil {
rs.Close()
+ return false
}
- return rs.lasterr == nil
+ return true
}
// Err returns the error, if any, that was encountered during iteration.
+// Err may be called after an explicit or implicit Close.
func (rs *Rows) Err() error {
if rs.lasterr == io.EOF {
return nil
@@ -1349,10 +1550,7 @@ func (rs *Rows) Columns() ([]string, error) {
// is of type []byte, a copy is made and the caller owns the result.
func (rs *Rows) Scan(dest ...interface{}) error {
if rs.closed {
- return errors.New("sql: Rows closed")
- }
- if rs.lasterr != nil {
- return rs.lasterr
+ return errors.New("sql: Rows are closed")
}
if rs.lastcols == nil {
return errors.New("sql: Scan called without calling Next")
@@ -1369,15 +1567,20 @@ func (rs *Rows) Scan(dest ...interface{}) error {
return nil
}
-// Close closes the Rows, preventing further enumeration. If the
-// end is encountered, the Rows are closed automatically. Close
-// is idempotent.
+var rowsCloseHook func(*Rows, *error)
+
+// Close closes the Rows, preventing further enumeration. If Next returns
+// false, the Rows are closed automatically and it will suffice to check the
+// result of Err. Close is idempotent and does not affect the result of Err.
func (rs *Rows) Close() error {
if rs.closed {
return nil
}
rs.closed = true
err := rs.rowsi.Close()
+ if fn := rowsCloseHook; fn != nil {
+ fn(rs, &err)
+ }
if rs.closeStmt != nil {
rs.closeStmt.Close()
}
@@ -1414,13 +1617,13 @@ func (r *Row) Scan(dest ...interface{}) error {
// from Next will not be modified again." (for instance, if
// they were obtained from the network anyway) But for now we
// don't care.
+ defer r.rows.Close()
for _, dp := range dest {
if _, ok := dp.(*RawBytes); ok {
return errors.New("sql: RawBytes isn't allowed on Row.Scan")
}
}
- defer r.rows.Close()
if !r.rows.Next() {
return ErrNoRows
}
@@ -1434,7 +1637,16 @@ func (r *Row) Scan(dest ...interface{}) error {
// A Result summarizes an executed SQL command.
type Result interface {
+ // LastInsertId returns the integer generated by the database
+ // in response to a command. Typically this will be from an
+ // "auto increment" column when inserting a new row. Not all
+ // databases support this feature, and the syntax of such
+ // statements varies.
LastInsertId() (int64, error)
+
+ // RowsAffected returns the number of rows affected by an
+ // update, insert, or delete. Not every database or database
+ // driver may support this.
RowsAffected() (int64, error)
}
diff --git a/src/pkg/database/sql/sql_test.go b/src/pkg/database/sql/sql_test.go
index e6cc667fa..093c0d64c 100644
--- a/src/pkg/database/sql/sql_test.go
+++ b/src/pkg/database/sql/sql_test.go
@@ -5,7 +5,10 @@
package sql
import (
+ "database/sql/driver"
+ "errors"
"fmt"
+ "math/rand"
"reflect"
"runtime"
"strings"
@@ -21,14 +24,12 @@ func init() {
}
freedFrom := make(map[dbConn]string)
putConnHook = func(db *DB, c *driverConn) {
- for _, oc := range db.freeConn {
- if oc == c {
- // print before panic, as panic may get lost due to conflicting panic
- // (all goroutines asleep) elsewhere, since we might not unlock
- // the mutex in freeConn here.
- println("double free of conn. conflicts are:\nA) " + freedFrom[dbConn{db, c}] + "\n\nand\nB) " + stack())
- panic("double free of conn.")
- }
+ if c.listElem != nil {
+ // print before panic, as panic may get lost due to conflicting panic
+ // (all goroutines asleep) elsewhere, since we might not unlock
+ // the mutex in freeConn here.
+ println("double free of conn. conflicts are:\nA) " + freedFrom[dbConn{db, c}] + "\n\nand\nB) " + stack())
+ panic("double free of conn.")
}
freedFrom[dbConn{db, c}] = stack()
}
@@ -38,15 +39,7 @@ const fakeDBName = "foo"
var chrisBirthday = time.Unix(123456789, 0)
-type testOrBench interface {
- Fatalf(string, ...interface{})
- Errorf(string, ...interface{})
- Fatal(...interface{})
- Error(...interface{})
- Logf(string, ...interface{})
-}
-
-func newTestDB(t testOrBench, name string) *DB {
+func newTestDB(t testing.TB, name string) *DB {
db, err := Open("test", fakeDBName)
if err != nil {
t.Fatalf("Open: %v", err)
@@ -68,14 +61,14 @@ func newTestDB(t testOrBench, name string) *DB {
return db
}
-func exec(t testOrBench, db *DB, query string, args ...interface{}) {
+func exec(t testing.TB, db *DB, query string, args ...interface{}) {
_, err := db.Exec(query, args...)
if err != nil {
t.Fatalf("Exec of %q: %v", query, err)
}
}
-func closeDB(t testOrBench, db *DB) {
+func closeDB(t testing.TB, db *DB) {
if e := recover(); e != nil {
fmt.Printf("Panic: %v\n", e)
panic(e)
@@ -86,29 +79,36 @@ func closeDB(t testOrBench, db *DB) {
t.Errorf("Error closing fakeConn: %v", err)
}
})
- for i, dc := range db.freeConn {
+ for node, i := db.freeConn.Front(), 0; node != nil; node, i = node.Next(), i+1 {
+ dc := node.Value.(*driverConn)
if n := len(dc.openStmt); n > 0 {
// Just a sanity check. This is legal in
// general, but if we make the tests clean up
// their statements first, then we can safely
// verify this is always zero here, and any
// other value is a leak.
- t.Errorf("while closing db, freeConn %d/%d had %d open stmts; want 0", i, len(db.freeConn), n)
+ t.Errorf("while closing db, freeConn %d/%d had %d open stmts; want 0", i, db.freeConn.Len(), n)
}
}
err := db.Close()
if err != nil {
t.Fatalf("error closing DB: %v", err)
}
+ db.mu.Lock()
+ count := db.numOpen
+ db.mu.Unlock()
+ if count != 0 {
+ t.Fatalf("%d connections still open after closing DB", db.numOpen)
+ }
}
// numPrepares assumes that db has exactly 1 idle conn and returns
// its count of calls to Prepare
func numPrepares(t *testing.T, db *DB) int {
- if n := len(db.freeConn); n != 1 {
+ if n := db.freeConn.Len(); n != 1 {
t.Fatalf("free conns = %d; want 1", n)
}
- return db.freeConn[0].ci.(*fakeConn).numPrepare
+ return (db.freeConn.Front().Value.(*driverConn)).ci.(*fakeConn).numPrepare
}
func (db *DB) numDeps() int {
@@ -133,7 +133,7 @@ func (db *DB) numDepsPollUntil(want int, d time.Duration) int {
func (db *DB) numFreeConns() int {
db.mu.Lock()
defer db.mu.Unlock()
- return len(db.freeConn)
+ return db.freeConn.Len()
}
func (db *DB) dumpDeps(t *testing.T) {
@@ -252,6 +252,9 @@ func TestRowsColumns(t *testing.T) {
if !reflect.DeepEqual(cols, want) {
t.Errorf("got %#v; want %#v", cols, want)
}
+ if err := rows.Close(); err != nil {
+ t.Errorf("error closing rows: %s", err)
+ }
}
func TestQueryRow(t *testing.T) {
@@ -648,10 +651,10 @@ func TestQueryRowClosingStmt(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- if len(db.freeConn) != 1 {
+ if db.freeConn.Len() != 1 {
t.Fatalf("expected 1 free conn")
}
- fakeConn := db.freeConn[0].ci.(*fakeConn)
+ fakeConn := (db.freeConn.Front().Value.(*driverConn)).ci.(*fakeConn)
if made, closed := fakeConn.stmtsMade, fakeConn.stmtsClosed; made != closed {
t.Errorf("statement close mismatch: made %d, closed %d", made, closed)
}
@@ -847,13 +850,13 @@ func TestMaxIdleConns(t *testing.T) {
t.Fatal(err)
}
tx.Commit()
- if got := len(db.freeConn); got != 1 {
+ if got := db.freeConn.Len(); got != 1 {
t.Errorf("freeConns = %d; want 1", got)
}
db.SetMaxIdleConns(0)
- if got := len(db.freeConn); got != 0 {
+ if got := db.freeConn.Len(); got != 0 {
t.Errorf("freeConns after set to zero = %d; want 0", got)
}
@@ -862,11 +865,146 @@ func TestMaxIdleConns(t *testing.T) {
t.Fatal(err)
}
tx.Commit()
- if got := len(db.freeConn); got != 0 {
+ if got := db.freeConn.Len(); got != 0 {
t.Errorf("freeConns = %d; want 0", got)
}
}
+func TestMaxOpenConns(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping in short mode")
+ }
+ defer setHookpostCloseConn(nil)
+ setHookpostCloseConn(func(_ *fakeConn, err error) {
+ if err != nil {
+ t.Errorf("Error closing fakeConn: %v", err)
+ }
+ })
+
+ db := newTestDB(t, "magicquery")
+ defer closeDB(t, db)
+
+ driver := db.driver.(*fakeDriver)
+
+ // Force the number of open connections to 0 so we can get an accurate
+ // count for the test
+ db.SetMaxIdleConns(0)
+
+ if g, w := db.numFreeConns(), 0; g != w {
+ t.Errorf("free conns = %d; want %d", g, w)
+ }
+
+ if n := db.numDepsPollUntil(0, time.Second); n > 0 {
+ t.Errorf("number of dependencies = %d; expected 0", n)
+ db.dumpDeps(t)
+ }
+
+ driver.mu.Lock()
+ opens0 := driver.openCount
+ closes0 := driver.closeCount
+ driver.mu.Unlock()
+
+ db.SetMaxIdleConns(10)
+ db.SetMaxOpenConns(10)
+
+ stmt, err := db.Prepare("SELECT|magicquery|op|op=?,millis=?")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Start 50 parallel slow queries.
+ const (
+ nquery = 50
+ sleepMillis = 25
+ nbatch = 2
+ )
+ var wg sync.WaitGroup
+ for batch := 0; batch < nbatch; batch++ {
+ for i := 0; i < nquery; i++ {
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ var op string
+ if err := stmt.QueryRow("sleep", sleepMillis).Scan(&op); err != nil && err != ErrNoRows {
+ t.Error(err)
+ }
+ }()
+ }
+ // Sleep for twice the expected length of time for the
+ // batch of 50 queries above to finish before starting
+ // the next round.
+ time.Sleep(2 * sleepMillis * time.Millisecond)
+ }
+ wg.Wait()
+
+ if g, w := db.numFreeConns(), 10; g != w {
+ t.Errorf("free conns = %d; want %d", g, w)
+ }
+
+ if n := db.numDepsPollUntil(20, time.Second); n > 20 {
+ t.Errorf("number of dependencies = %d; expected <= 20", n)
+ db.dumpDeps(t)
+ }
+
+ driver.mu.Lock()
+ opens := driver.openCount - opens0
+ closes := driver.closeCount - closes0
+ driver.mu.Unlock()
+
+ if opens > 10 {
+ t.Logf("open calls = %d", opens)
+ t.Logf("close calls = %d", closes)
+ t.Errorf("db connections opened = %d; want <= 10", opens)
+ db.dumpDeps(t)
+ }
+
+ if err := stmt.Close(); err != nil {
+ t.Fatal(err)
+ }
+
+ if g, w := db.numFreeConns(), 10; g != w {
+ t.Errorf("free conns = %d; want %d", g, w)
+ }
+
+ if n := db.numDepsPollUntil(10, time.Second); n > 10 {
+ t.Errorf("number of dependencies = %d; expected <= 10", n)
+ db.dumpDeps(t)
+ }
+
+ db.SetMaxOpenConns(5)
+
+ if g, w := db.numFreeConns(), 5; g != w {
+ t.Errorf("free conns = %d; want %d", g, w)
+ }
+
+ if n := db.numDepsPollUntil(5, time.Second); n > 5 {
+ t.Errorf("number of dependencies = %d; expected 0", n)
+ db.dumpDeps(t)
+ }
+
+ db.SetMaxOpenConns(0)
+
+ if g, w := db.numFreeConns(), 5; g != w {
+ t.Errorf("free conns = %d; want %d", g, w)
+ }
+
+ if n := db.numDepsPollUntil(5, time.Second); n > 5 {
+ t.Errorf("number of dependencies = %d; expected 0", n)
+ db.dumpDeps(t)
+ }
+
+ db.SetMaxIdleConns(0)
+
+ if g, w := db.numFreeConns(), 0; g != w {
+ t.Errorf("free conns = %d; want %d", g, w)
+ }
+
+ if n := db.numDepsPollUntil(0, time.Second); n > 0 {
+ t.Errorf("number of dependencies = %d; expected 0", n)
+ db.dumpDeps(t)
+ }
+}
+
// golang.org/issue/5323
func TestStmtCloseDeps(t *testing.T) {
if testing.Short() {
@@ -932,8 +1070,8 @@ func TestStmtCloseDeps(t *testing.T) {
driver.mu.Lock()
opens := driver.openCount - opens0
closes := driver.closeCount - closes0
- driver.mu.Unlock()
openDelta := (driver.openCount - driver.closeCount) - openDelta0
+ driver.mu.Unlock()
if openDelta > 2 {
t.Logf("open calls = %d", opens)
@@ -991,10 +1129,10 @@ func TestCloseConnBeforeStmts(t *testing.T) {
t.Fatal(err)
}
- if len(db.freeConn) != 1 {
- t.Fatalf("expected 1 freeConn; got %d", len(db.freeConn))
+ if db.freeConn.Len() != 1 {
+ t.Fatalf("expected 1 freeConn; got %d", db.freeConn.Len())
}
- dc := db.freeConn[0]
+ dc := db.freeConn.Front().Value.(*driverConn)
if dc.closed {
t.Errorf("conn shouldn't be closed")
}
@@ -1046,7 +1184,393 @@ func TestRowsCloseOrder(t *testing.T) {
}
}
-func manyConcurrentQueries(t testOrBench) {
+func TestRowsImplicitClose(t *testing.T) {
+ db := newTestDB(t, "people")
+ defer closeDB(t, db)
+
+ rows, err := db.Query("SELECT|people|age,name|")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ want, fail := 2, errors.New("fail")
+ r := rows.rowsi.(*rowsCursor)
+ r.errPos, r.err = want, fail
+
+ got := 0
+ for rows.Next() {
+ got++
+ }
+ if got != want {
+ t.Errorf("got %d rows, want %d", got, want)
+ }
+ if err := rows.Err(); err != fail {
+ t.Errorf("got error %v, want %v", err, fail)
+ }
+ if !r.closed {
+ t.Errorf("r.closed is false, want true")
+ }
+}
+
+func TestStmtCloseOrder(t *testing.T) {
+ db := newTestDB(t, "people")
+ defer closeDB(t, db)
+
+ db.SetMaxIdleConns(0)
+ setStrictFakeConnClose(t)
+ defer setStrictFakeConnClose(nil)
+
+ _, err := db.Query("SELECT|non_existent|name|")
+ if err == nil {
+ t.Fatal("Quering non-existent table should fail")
+ }
+}
+
+type concurrentTest interface {
+ init(t testing.TB, db *DB)
+ finish(t testing.TB)
+ test(t testing.TB) error
+}
+
+type concurrentDBQueryTest struct {
+ db *DB
+}
+
+func (c *concurrentDBQueryTest) init(t testing.TB, db *DB) {
+ c.db = db
+}
+
+func (c *concurrentDBQueryTest) finish(t testing.TB) {
+ c.db = nil
+}
+
+func (c *concurrentDBQueryTest) test(t testing.TB) error {
+ rows, err := c.db.Query("SELECT|people|name|")
+ if err != nil {
+ t.Error(err)
+ return err
+ }
+ var name string
+ for rows.Next() {
+ rows.Scan(&name)
+ }
+ rows.Close()
+ return nil
+}
+
+type concurrentDBExecTest struct {
+ db *DB
+}
+
+func (c *concurrentDBExecTest) init(t testing.TB, db *DB) {
+ c.db = db
+}
+
+func (c *concurrentDBExecTest) finish(t testing.TB) {
+ c.db = nil
+}
+
+func (c *concurrentDBExecTest) test(t testing.TB) error {
+ _, err := c.db.Exec("NOSERT|people|name=Chris,age=?,photo=CPHOTO,bdate=?", 3, chrisBirthday)
+ if err != nil {
+ t.Error(err)
+ return err
+ }
+ return nil
+}
+
+type concurrentStmtQueryTest struct {
+ db *DB
+ stmt *Stmt
+}
+
+func (c *concurrentStmtQueryTest) init(t testing.TB, db *DB) {
+ c.db = db
+ var err error
+ c.stmt, err = db.Prepare("SELECT|people|name|")
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func (c *concurrentStmtQueryTest) finish(t testing.TB) {
+ if c.stmt != nil {
+ c.stmt.Close()
+ c.stmt = nil
+ }
+ c.db = nil
+}
+
+func (c *concurrentStmtQueryTest) test(t testing.TB) error {
+ rows, err := c.stmt.Query()
+ if err != nil {
+ t.Errorf("error on query: %v", err)
+ return err
+ }
+
+ var name string
+ for rows.Next() {
+ rows.Scan(&name)
+ }
+ rows.Close()
+ return nil
+}
+
+type concurrentStmtExecTest struct {
+ db *DB
+ stmt *Stmt
+}
+
+func (c *concurrentStmtExecTest) init(t testing.TB, db *DB) {
+ c.db = db
+ var err error
+ c.stmt, err = db.Prepare("NOSERT|people|name=Chris,age=?,photo=CPHOTO,bdate=?")
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func (c *concurrentStmtExecTest) finish(t testing.TB) {
+ if c.stmt != nil {
+ c.stmt.Close()
+ c.stmt = nil
+ }
+ c.db = nil
+}
+
+func (c *concurrentStmtExecTest) test(t testing.TB) error {
+ _, err := c.stmt.Exec(3, chrisBirthday)
+ if err != nil {
+ t.Errorf("error on exec: %v", err)
+ return err
+ }
+ return nil
+}
+
+type concurrentTxQueryTest struct {
+ db *DB
+ tx *Tx
+}
+
+func (c *concurrentTxQueryTest) init(t testing.TB, db *DB) {
+ c.db = db
+ var err error
+ c.tx, err = c.db.Begin()
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func (c *concurrentTxQueryTest) finish(t testing.TB) {
+ if c.tx != nil {
+ c.tx.Rollback()
+ c.tx = nil
+ }
+ c.db = nil
+}
+
+func (c *concurrentTxQueryTest) test(t testing.TB) error {
+ rows, err := c.db.Query("SELECT|people|name|")
+ if err != nil {
+ t.Error(err)
+ return err
+ }
+ var name string
+ for rows.Next() {
+ rows.Scan(&name)
+ }
+ rows.Close()
+ return nil
+}
+
+type concurrentTxExecTest struct {
+ db *DB
+ tx *Tx
+}
+
+func (c *concurrentTxExecTest) init(t testing.TB, db *DB) {
+ c.db = db
+ var err error
+ c.tx, err = c.db.Begin()
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func (c *concurrentTxExecTest) finish(t testing.TB) {
+ if c.tx != nil {
+ c.tx.Rollback()
+ c.tx = nil
+ }
+ c.db = nil
+}
+
+func (c *concurrentTxExecTest) test(t testing.TB) error {
+ _, err := c.tx.Exec("NOSERT|people|name=Chris,age=?,photo=CPHOTO,bdate=?", 3, chrisBirthday)
+ if err != nil {
+ t.Error(err)
+ return err
+ }
+ return nil
+}
+
+type concurrentTxStmtQueryTest struct {
+ db *DB
+ tx *Tx
+ stmt *Stmt
+}
+
+func (c *concurrentTxStmtQueryTest) init(t testing.TB, db *DB) {
+ c.db = db
+ var err error
+ c.tx, err = c.db.Begin()
+ if err != nil {
+ t.Fatal(err)
+ }
+ c.stmt, err = c.tx.Prepare("SELECT|people|name|")
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func (c *concurrentTxStmtQueryTest) finish(t testing.TB) {
+ if c.stmt != nil {
+ c.stmt.Close()
+ c.stmt = nil
+ }
+ if c.tx != nil {
+ c.tx.Rollback()
+ c.tx = nil
+ }
+ c.db = nil
+}
+
+func (c *concurrentTxStmtQueryTest) test(t testing.TB) error {
+ rows, err := c.stmt.Query()
+ if err != nil {
+ t.Errorf("error on query: %v", err)
+ return err
+ }
+
+ var name string
+ for rows.Next() {
+ rows.Scan(&name)
+ }
+ rows.Close()
+ return nil
+}
+
+type concurrentTxStmtExecTest struct {
+ db *DB
+ tx *Tx
+ stmt *Stmt
+}
+
+func (c *concurrentTxStmtExecTest) init(t testing.TB, db *DB) {
+ c.db = db
+ var err error
+ c.tx, err = c.db.Begin()
+ if err != nil {
+ t.Fatal(err)
+ }
+ c.stmt, err = c.tx.Prepare("NOSERT|people|name=Chris,age=?,photo=CPHOTO,bdate=?")
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func (c *concurrentTxStmtExecTest) finish(t testing.TB) {
+ if c.stmt != nil {
+ c.stmt.Close()
+ c.stmt = nil
+ }
+ if c.tx != nil {
+ c.tx.Rollback()
+ c.tx = nil
+ }
+ c.db = nil
+}
+
+func (c *concurrentTxStmtExecTest) test(t testing.TB) error {
+ _, err := c.stmt.Exec(3, chrisBirthday)
+ if err != nil {
+ t.Errorf("error on exec: %v", err)
+ return err
+ }
+ return nil
+}
+
+type concurrentRandomTest struct {
+ tests []concurrentTest
+}
+
+func (c *concurrentRandomTest) init(t testing.TB, db *DB) {
+ c.tests = []concurrentTest{
+ new(concurrentDBQueryTest),
+ new(concurrentDBExecTest),
+ new(concurrentStmtQueryTest),
+ new(concurrentStmtExecTest),
+ new(concurrentTxQueryTest),
+ new(concurrentTxExecTest),
+ new(concurrentTxStmtQueryTest),
+ new(concurrentTxStmtExecTest),
+ }
+ for _, ct := range c.tests {
+ ct.init(t, db)
+ }
+}
+
+func (c *concurrentRandomTest) finish(t testing.TB) {
+ for _, ct := range c.tests {
+ ct.finish(t)
+ }
+}
+
+func (c *concurrentRandomTest) test(t testing.TB) error {
+ ct := c.tests[rand.Intn(len(c.tests))]
+ return ct.test(t)
+}
+
+func doConcurrentTest(t testing.TB, ct concurrentTest) {
+ maxProcs, numReqs := 1, 500
+ if testing.Short() {
+ maxProcs, numReqs = 4, 50
+ }
+ defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(maxProcs))
+
+ db := newTestDB(t, "people")
+ defer closeDB(t, db)
+
+ ct.init(t, db)
+ defer ct.finish(t)
+
+ var wg sync.WaitGroup
+ wg.Add(numReqs)
+
+ reqs := make(chan bool)
+ defer close(reqs)
+
+ for i := 0; i < maxProcs*2; i++ {
+ go func() {
+ for _ = range reqs {
+ err := ct.test(t)
+ if err != nil {
+ wg.Done()
+ continue
+ }
+ wg.Done()
+ }
+ }()
+ }
+
+ for i := 0; i < numReqs; i++ {
+ reqs <- true
+ }
+
+ wg.Wait()
+}
+
+func manyConcurrentQueries(t testing.TB) {
maxProcs, numReqs := 16, 500
if testing.Short() {
maxProcs, numReqs = 4, 50
@@ -1096,13 +1620,174 @@ func manyConcurrentQueries(t testOrBench) {
wg.Wait()
}
+func TestIssue6081(t *testing.T) {
+ db := newTestDB(t, "people")
+ defer closeDB(t, db)
+
+ drv := db.driver.(*fakeDriver)
+ drv.mu.Lock()
+ opens0 := drv.openCount
+ closes0 := drv.closeCount
+ drv.mu.Unlock()
+
+ stmt, err := db.Prepare("SELECT|people|name|")
+ if err != nil {
+ t.Fatal(err)
+ }
+ rowsCloseHook = func(rows *Rows, err *error) {
+ *err = driver.ErrBadConn
+ }
+ defer func() { rowsCloseHook = nil }()
+ for i := 0; i < 10; i++ {
+ rows, err := stmt.Query()
+ if err != nil {
+ t.Fatal(err)
+ }
+ rows.Close()
+ }
+ if n := len(stmt.css); n > 1 {
+ t.Errorf("len(css slice) = %d; want <= 1", n)
+ }
+ stmt.Close()
+ if n := len(stmt.css); n != 0 {
+ t.Errorf("len(css slice) after Close = %d; want 0", n)
+ }
+
+ drv.mu.Lock()
+ opens := drv.openCount - opens0
+ closes := drv.closeCount - closes0
+ drv.mu.Unlock()
+ if opens < 9 {
+ t.Errorf("opens = %d; want >= 9", opens)
+ }
+ if closes < 9 {
+ t.Errorf("closes = %d; want >= 9", closes)
+ }
+}
+
func TestConcurrency(t *testing.T) {
- manyConcurrentQueries(t)
+ doConcurrentTest(t, new(concurrentDBQueryTest))
+ doConcurrentTest(t, new(concurrentDBExecTest))
+ doConcurrentTest(t, new(concurrentStmtQueryTest))
+ doConcurrentTest(t, new(concurrentStmtExecTest))
+ doConcurrentTest(t, new(concurrentTxQueryTest))
+ doConcurrentTest(t, new(concurrentTxExecTest))
+ doConcurrentTest(t, new(concurrentTxStmtQueryTest))
+ doConcurrentTest(t, new(concurrentTxStmtExecTest))
+ doConcurrentTest(t, new(concurrentRandomTest))
+}
+
+func TestConnectionLeak(t *testing.T) {
+ db := newTestDB(t, "people")
+ defer closeDB(t, db)
+ // Start by opening defaultMaxIdleConns
+ rows := make([]*Rows, defaultMaxIdleConns)
+ // We need to SetMaxOpenConns > MaxIdleConns, so the DB can open
+ // a new connection and we can fill the idle queue with the released
+ // connections.
+ db.SetMaxOpenConns(len(rows) + 1)
+ for ii := range rows {
+ r, err := db.Query("SELECT|people|name|")
+ if err != nil {
+ t.Fatal(err)
+ }
+ r.Next()
+ if err := r.Err(); err != nil {
+ t.Fatal(err)
+ }
+ rows[ii] = r
+ }
+ // Now we have defaultMaxIdleConns busy connections. Open
+ // a new one, but wait until the busy connections are released
+ // before returning control to DB.
+ drv := db.driver.(*fakeDriver)
+ drv.waitCh = make(chan struct{}, 1)
+ drv.waitingCh = make(chan struct{}, 1)
+ var wg sync.WaitGroup
+ wg.Add(1)
+ go func() {
+ r, err := db.Query("SELECT|people|name|")
+ if err != nil {
+ t.Fatal(err)
+ }
+ r.Close()
+ wg.Done()
+ }()
+ // Wait until the goroutine we've just created has started waiting.
+ <-drv.waitingCh
+ // Now close the busy connections. This provides a connection for
+ // the blocked goroutine and then fills up the idle queue.
+ for _, v := range rows {
+ v.Close()
+ }
+ // At this point we give the new connection to DB. This connection is
+ // now useless, since the idle queue is full and there are no pending
+ // requests. DB should deal with this situation without leaking the
+ // connection.
+ drv.waitCh <- struct{}{}
+ wg.Wait()
+}
+
+func BenchmarkConcurrentDBExec(b *testing.B) {
+ b.ReportAllocs()
+ ct := new(concurrentDBExecTest)
+ for i := 0; i < b.N; i++ {
+ doConcurrentTest(b, ct)
+ }
+}
+
+func BenchmarkConcurrentStmtQuery(b *testing.B) {
+ b.ReportAllocs()
+ ct := new(concurrentStmtQueryTest)
+ for i := 0; i < b.N; i++ {
+ doConcurrentTest(b, ct)
+ }
+}
+
+func BenchmarkConcurrentStmtExec(b *testing.B) {
+ b.ReportAllocs()
+ ct := new(concurrentStmtExecTest)
+ for i := 0; i < b.N; i++ {
+ doConcurrentTest(b, ct)
+ }
+}
+
+func BenchmarkConcurrentTxQuery(b *testing.B) {
+ b.ReportAllocs()
+ ct := new(concurrentTxQueryTest)
+ for i := 0; i < b.N; i++ {
+ doConcurrentTest(b, ct)
+ }
+}
+
+func BenchmarkConcurrentTxExec(b *testing.B) {
+ b.ReportAllocs()
+ ct := new(concurrentTxExecTest)
+ for i := 0; i < b.N; i++ {
+ doConcurrentTest(b, ct)
+ }
+}
+
+func BenchmarkConcurrentTxStmtQuery(b *testing.B) {
+ b.ReportAllocs()
+ ct := new(concurrentTxStmtQueryTest)
+ for i := 0; i < b.N; i++ {
+ doConcurrentTest(b, ct)
+ }
+}
+
+func BenchmarkConcurrentTxStmtExec(b *testing.B) {
+ b.ReportAllocs()
+ ct := new(concurrentTxStmtExecTest)
+ for i := 0; i < b.N; i++ {
+ doConcurrentTest(b, ct)
+ }
}
-func BenchmarkConcurrency(b *testing.B) {
+func BenchmarkConcurrentRandom(b *testing.B) {
b.ReportAllocs()
+ ct := new(concurrentRandomTest)
for i := 0; i < b.N; i++ {
- manyConcurrentQueries(b)
+ doConcurrentTest(b, ct)
}
}
diff --git a/src/pkg/debug/dwarf/const.go b/src/pkg/debug/dwarf/const.go
index ad696dc32..9d32a0af2 100644
--- a/src/pkg/debug/dwarf/const.go
+++ b/src/pkg/debug/dwarf/const.go
@@ -207,7 +207,10 @@ const (
formRef8 format = 0x14
formRefUdata format = 0x15
formIndirect format = 0x16
+ formSecOffset format = 0x17
+ formExprloc format = 0x18
formFlagPresent format = 0x19
+ formRefSig8 format = 0x20
)
// A Tag is the classification (the type) of an Entry.
diff --git a/src/pkg/debug/dwarf/entry.go b/src/pkg/debug/dwarf/entry.go
index 13d8d5ecf..c0c288992 100644
--- a/src/pkg/debug/dwarf/entry.go
+++ b/src/pkg/debug/dwarf/entry.go
@@ -10,7 +10,10 @@
package dwarf
-import "errors"
+import (
+ "errors"
+ "strconv"
+)
// a single entry's description: a sequence of attributes
type abbrev struct {
@@ -152,7 +155,7 @@ func (b *buf) entry(atab abbrevTable, ubase Offset) *Entry {
var val interface{}
switch fmt {
default:
- b.error("unknown entry attr format")
+ b.error("unknown entry attr format 0x" + strconv.FormatInt(int64(fmt), 16))
// address
case formAddr:
@@ -185,6 +188,7 @@ func (b *buf) entry(atab abbrevTable, ubase Offset) *Entry {
// flag
case formFlag:
val = b.uint8() == 1
+ // New in DWARF 4.
case formFlagPresent:
// The attribute is implicitly indicated as present, and no value is
// encoded in the debugging information entry itself.
@@ -233,6 +237,30 @@ func (b *buf) entry(atab abbrevTable, ubase Offset) *Entry {
b.err = b1.err
return nil
}
+
+ // lineptr, loclistptr, macptr, rangelistptr
+ // New in DWARF 4, but clang can generate them with -gdwarf-2.
+ // Section reference, replacing use of formData4 and formData8.
+ case formSecOffset:
+ is64, known := b.format.dwarf64()
+ if !known {
+ b.error("unknown size for DW_FORM_sec_offset")
+ } else if is64 {
+ val = int64(b.uint64())
+ } else {
+ val = int64(b.uint32())
+ }
+
+ // exprloc
+ // New in DWARF 4.
+ case formExprloc:
+ val = b.bytes(int(b.uint()))
+
+ // reference
+ // New in DWARF 4.
+ case formRefSig8:
+ // 64-bit type signature.
+ val = b.uint64()
}
e.Field[i].Val = val
}
diff --git a/src/pkg/debug/dwarf/type.go b/src/pkg/debug/dwarf/type.go
index 54000fbd7..1fbae6c14 100644
--- a/src/pkg/debug/dwarf/type.go
+++ b/src/pkg/debug/dwarf/type.go
@@ -271,24 +271,43 @@ func (d *Data) Type(off Offset) (Type, error) {
// d.Type recursively, to handle circular types correctly.
var typ Type
+ nextDepth := 0
+
// Get next child; set err if error happens.
next := func() *Entry {
if !e.Children {
return nil
}
- kid, err1 := r.Next()
- if err1 != nil {
- err = err1
- return nil
- }
- if kid == nil {
- err = DecodeError{"info", r.b.off, "unexpected end of DWARF entries"}
- return nil
- }
- if kid.Tag == 0 {
- return nil
+ // Only return direct children.
+ // Skip over composite entries that happen to be nested
+ // inside this one. Most DWARF generators wouldn't generate
+ // such a thing, but clang does.
+ // See golang.org/issue/6472.
+ for {
+ kid, err1 := r.Next()
+ if err1 != nil {
+ err = err1
+ return nil
+ }
+ if kid == nil {
+ err = DecodeError{"info", r.b.off, "unexpected end of DWARF entries"}
+ return nil
+ }
+ if kid.Tag == 0 {
+ if nextDepth > 0 {
+ nextDepth--
+ continue
+ }
+ return nil
+ }
+ if kid.Children {
+ nextDepth++
+ }
+ if nextDepth > 0 {
+ continue
+ }
+ return kid
}
- return kid
}
// Get Type referred to by Entry's AttrType field.
diff --git a/src/pkg/debug/elf/file_test.go b/src/pkg/debug/elf/file_test.go
index f9aa7265a..38b5f9e70 100644
--- a/src/pkg/debug/elf/file_test.go
+++ b/src/pkg/debug/elf/file_test.go
@@ -166,6 +166,7 @@ func TestOpen(t *testing.T) {
} else {
f, err = Open(tt.file)
}
+ defer f.Close()
if err != nil {
t.Errorf("cannot open file %s: %v", tt.file, err)
continue
diff --git a/src/pkg/debug/gosym/pclinetest.asm b/src/pkg/debug/gosym/pclinetest.asm
index 6305435b0..b9ee9c0a5 100644
--- a/src/pkg/debug/gosym/pclinetest.asm
+++ b/src/pkg/debug/gosym/pclinetest.asm
@@ -1,4 +1,4 @@
-TEXT linefrompc(SB),7,$0 // Each byte stores its line delta
+TEXT linefrompc(SB),4,$0 // Each byte stores its line delta
BYTE $2;
BYTE $1;
BYTE $1; BYTE $0;
@@ -26,9 +26,10 @@ BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0;
BYTE $2;
#include "pclinetest.h"
BYTE $2;
+BYTE $255;
-TEXT pcfromline(SB),7,$0 // Each record stores its line delta, then n, then n more bytes
-BYTE $31; BYTE $0;
+TEXT pcfromline(SB),4,$0 // Each record stores its line delta, then n, then n more bytes
+BYTE $32; BYTE $0;
BYTE $1; BYTE $1; BYTE $0;
BYTE $1; BYTE $0;
@@ -44,15 +45,14 @@ BYTE $3; BYTE $3; BYTE $0; BYTE $0; BYTE $0;
BYTE $4; BYTE $3; BYTE $0; BYTE $0; BYTE $0;
-
-TEXT main(SB),7,$0
- // Prevent GC of our test symbols
- CALL linefrompc(SB)
- CALL pcfromline(SB)
+BYTE $255;
// Keep the linker happy
-TEXT main·main(SB),7,$0
+TEXT main·main(SB),4,$0
RET
-TEXT main·init(SB),7,$0
+TEXT main·init(SB),4,$0
+ // Prevent GC of our test symbols
+ CALL linefrompc(SB)
+ CALL pcfromline(SB)
RET
diff --git a/src/pkg/debug/gosym/pclntab.go b/src/pkg/debug/gosym/pclntab.go
index 9d7b0d15f..3e6a8046b 100644
--- a/src/pkg/debug/gosym/pclntab.go
+++ b/src/pkg/debug/gosym/pclntab.go
@@ -8,16 +8,47 @@
package gosym
-import "encoding/binary"
+import (
+ "encoding/binary"
+ "sync"
+)
+// A LineTable is a data structure mapping program counters to line numbers.
+//
+// In Go 1.1 and earlier, each function (represented by a Func) had its own LineTable,
+// and the line number corresponded to a numbering of all source lines in the
+// program, across all files. That absolute line number would then have to be
+// converted separately to a file name and line number within the file.
+//
+// In Go 1.2, the format of the data changed so that there is a single LineTable
+// for the entire program, shared by all Funcs, and there are no absolute line
+// numbers, just line numbers within specific files.
+//
+// For the most part, LineTable's methods should be treated as an internal
+// detail of the package; callers should use the methods on Table instead.
type LineTable struct {
Data []byte
PC uint64
Line int
+
+ // Go 1.2 state
+ mu sync.Mutex
+ go12 int // is this in Go 1.2 format? -1 no, 0 unknown, 1 yes
+ binary binary.ByteOrder
+ quantum uint32
+ ptrsize uint32
+ functab []byte
+ nfunctab uint32
+ filetab []byte
+ nfiletab uint32
+ fileMap map[string]uint32
}
-// TODO(rsc): Need to pull in quantum from architecture definition.
-const quantum = 1
+// NOTE(rsc): This is wrong for GOARCH=arm, which uses a quantum of 4,
+// but we have no idea whether we're using arm or not. This only
+// matters in the old (pre-Go 1.2) symbol table format, so it's not worth
+// fixing.
+const oldQuantum = 1
func (t *LineTable) parse(targetPC uint64, targetLine int) (b []byte, pc uint64, line int) {
// The PC/line table can be thought of as a sequence of
@@ -46,31 +77,42 @@ func (t *LineTable) parse(targetPC uint64, targetLine int) (b []byte, pc uint64,
case code <= 128:
line -= int(code - 64)
default:
- pc += quantum * uint64(code-128)
+ pc += oldQuantum * uint64(code-128)
continue
}
- pc += quantum
+ pc += oldQuantum
}
return b, pc, line
}
func (t *LineTable) slice(pc uint64) *LineTable {
data, pc, line := t.parse(pc, -1)
- return &LineTable{data, pc, line}
+ return &LineTable{Data: data, PC: pc, Line: line}
}
+// PCToLine returns the line number for the given program counter.
+// Callers should use Table's PCToLine method instead.
func (t *LineTable) PCToLine(pc uint64) int {
+ if t.isGo12() {
+ return t.go12PCToLine(pc)
+ }
_, _, line := t.parse(pc, -1)
return line
}
+// LineToPC returns the program counter for the given line number,
+// considering only program counters before maxpc.
+// Callers should use Table's LineToPC method instead.
func (t *LineTable) LineToPC(line int, maxpc uint64) uint64 {
+ if t.isGo12() {
+ return 0
+ }
_, pc, line1 := t.parse(maxpc, line)
if line1 != line {
return 0
}
// Subtract quantum from PC to account for post-line increment
- return pc - quantum
+ return pc - oldQuantum
}
// NewLineTable returns a new PC/line table
@@ -78,5 +120,307 @@ func (t *LineTable) LineToPC(line int, maxpc uint64) uint64 {
// Text must be the start address of the
// corresponding text segment.
func NewLineTable(data []byte, text uint64) *LineTable {
- return &LineTable{data, text, 0}
+ return &LineTable{Data: data, PC: text, Line: 0}
+}
+
+// Go 1.2 symbol table format.
+// See golang.org/s/go12symtab.
+//
+// A general note about the methods here: rather than try to avoid
+// index out of bounds errors, we trust Go to detect them, and then
+// we recover from the panics and treat them as indicative of a malformed
+// or incomplete table.
+//
+// The methods called by symtab.go, which begin with "go12" prefixes,
+// are expected to have that recovery logic.
+
+// isGo12 reports whether this is a Go 1.2 (or later) symbol table.
+func (t *LineTable) isGo12() bool {
+ t.go12Init()
+ return t.go12 == 1
+}
+
+const go12magic = 0xfffffffb
+
+// uintptr returns the pointer-sized value encoded at b.
+// The pointer size is dictated by the table being read.
+func (t *LineTable) uintptr(b []byte) uint64 {
+ if t.ptrsize == 4 {
+ return uint64(t.binary.Uint32(b))
+ }
+ return t.binary.Uint64(b)
+}
+
+// go12init initializes the Go 1.2 metadata if t is a Go 1.2 symbol table.
+func (t *LineTable) go12Init() {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ if t.go12 != 0 {
+ return
+ }
+
+ defer func() {
+ // If we panic parsing, assume it's not a Go 1.2 symbol table.
+ recover()
+ }()
+
+ // Check header: 4-byte magic, two zeros, pc quantum, pointer size.
+ t.go12 = -1 // not Go 1.2 until proven otherwise
+ if len(t.Data) < 16 || t.Data[4] != 0 || t.Data[5] != 0 ||
+ (t.Data[6] != 1 && t.Data[6] != 4) || // pc quantum
+ (t.Data[7] != 4 && t.Data[7] != 8) { // pointer size
+ return
+ }
+
+ switch uint32(go12magic) {
+ case binary.LittleEndian.Uint32(t.Data):
+ t.binary = binary.LittleEndian
+ case binary.BigEndian.Uint32(t.Data):
+ t.binary = binary.BigEndian
+ default:
+ return
+ }
+
+ t.quantum = uint32(t.Data[6])
+ t.ptrsize = uint32(t.Data[7])
+
+ t.nfunctab = uint32(t.uintptr(t.Data[8:]))
+ t.functab = t.Data[8+t.ptrsize:]
+ functabsize := t.nfunctab*2*t.ptrsize + t.ptrsize
+ fileoff := t.binary.Uint32(t.functab[functabsize:])
+ t.functab = t.functab[:functabsize]
+ t.filetab = t.Data[fileoff:]
+ t.nfiletab = t.binary.Uint32(t.filetab)
+ t.filetab = t.filetab[:t.nfiletab*4]
+
+ t.go12 = 1 // so far so good
+}
+
+// findFunc returns the func corresponding to the given program counter.
+func (t *LineTable) findFunc(pc uint64) []byte {
+ if pc < t.uintptr(t.functab) || pc >= t.uintptr(t.functab[len(t.functab)-int(t.ptrsize):]) {
+ return nil
+ }
+
+ // The function table is a list of 2*nfunctab+1 uintptrs,
+ // alternating program counters and offsets to func structures.
+ f := t.functab
+ nf := t.nfunctab
+ for nf > 0 {
+ m := nf / 2
+ fm := f[2*t.ptrsize*m:]
+ if t.uintptr(fm) <= pc && pc < t.uintptr(fm[2*t.ptrsize:]) {
+ return t.Data[t.uintptr(fm[t.ptrsize:]):]
+ } else if pc < t.uintptr(fm) {
+ nf = m
+ } else {
+ f = f[(m+1)*2*t.ptrsize:]
+ nf -= m + 1
+ }
+ }
+ return nil
+}
+
+// readvarint reads, removes, and returns a varint from *pp.
+func (t *LineTable) readvarint(pp *[]byte) uint32 {
+ var v, shift uint32
+ p := *pp
+ for shift = 0; ; shift += 7 {
+ b := p[0]
+ p = p[1:]
+ v |= (uint32(b) & 0x7F) << shift
+ if b&0x80 == 0 {
+ break
+ }
+ }
+ *pp = p
+ return v
+}
+
+// string returns a Go string found at off.
+func (t *LineTable) string(off uint32) string {
+ for i := off; ; i++ {
+ if t.Data[i] == 0 {
+ return string(t.Data[off:i])
+ }
+ }
+}
+
+// step advances to the next pc, value pair in the encoded table.
+func (t *LineTable) step(p *[]byte, pc *uint64, val *int32, first bool) bool {
+ uvdelta := t.readvarint(p)
+ if uvdelta == 0 && !first {
+ return false
+ }
+ if uvdelta&1 != 0 {
+ uvdelta = ^(uvdelta >> 1)
+ } else {
+ uvdelta >>= 1
+ }
+ vdelta := int32(uvdelta)
+ pcdelta := t.readvarint(p) * t.quantum
+ *pc += uint64(pcdelta)
+ *val += vdelta
+ return true
+}
+
+// pcvalue reports the value associated with the target pc.
+// off is the offset to the beginning of the pc-value table,
+// and entry is the start PC for the corresponding function.
+func (t *LineTable) pcvalue(off uint32, entry, targetpc uint64) int32 {
+ if off == 0 {
+ return -1
+ }
+ p := t.Data[off:]
+
+ val := int32(-1)
+ pc := entry
+ for t.step(&p, &pc, &val, pc == entry) {
+ if targetpc < pc {
+ return val
+ }
+ }
+ return -1
+}
+
+// findFileLine scans one function in the binary looking for a
+// program counter in the given file on the given line.
+// It does so by running the pc-value tables mapping program counter
+// to file number. Since most functions come from a single file, these
+// are usually short and quick to scan. If a file match is found, then the
+// code goes to the expense of looking for a simultaneous line number match.
+func (t *LineTable) findFileLine(entry uint64, filetab, linetab uint32, filenum, line int32) uint64 {
+ if filetab == 0 || linetab == 0 {
+ return 0
+ }
+
+ fp := t.Data[filetab:]
+ fl := t.Data[linetab:]
+ fileVal := int32(-1)
+ filePC := entry
+ lineVal := int32(-1)
+ linePC := entry
+ fileStartPC := filePC
+ for t.step(&fp, &filePC, &fileVal, filePC == entry) {
+ if fileVal == filenum && fileStartPC < filePC {
+ // fileVal is in effect starting at fileStartPC up to
+ // but not including filePC, and it's the file we want.
+ // Run the PC table looking for a matching line number
+ // or until we reach filePC.
+ lineStartPC := linePC
+ for linePC < filePC && t.step(&fl, &linePC, &lineVal, linePC == entry) {
+ // lineVal is in effect until linePC, and lineStartPC < filePC.
+ if lineVal == line {
+ if fileStartPC <= lineStartPC {
+ return lineStartPC
+ }
+ if fileStartPC < linePC {
+ return fileStartPC
+ }
+ }
+ lineStartPC = linePC
+ }
+ }
+ fileStartPC = filePC
+ }
+ return 0
+}
+
+// go12PCToLine maps program counter to line number for the Go 1.2 pcln table.
+func (t *LineTable) go12PCToLine(pc uint64) (line int) {
+ defer func() {
+ if recover() != nil {
+ line = -1
+ }
+ }()
+
+ f := t.findFunc(pc)
+ if f == nil {
+ return -1
+ }
+ entry := t.uintptr(f)
+ linetab := t.binary.Uint32(f[t.ptrsize+5*4:])
+ return int(t.pcvalue(linetab, entry, pc))
+}
+
+// go12PCToFile maps program counter to file name for the Go 1.2 pcln table.
+func (t *LineTable) go12PCToFile(pc uint64) (file string) {
+ defer func() {
+ if recover() != nil {
+ file = ""
+ }
+ }()
+
+ f := t.findFunc(pc)
+ if f == nil {
+ return ""
+ }
+ entry := t.uintptr(f)
+ filetab := t.binary.Uint32(f[t.ptrsize+4*4:])
+ fno := t.pcvalue(filetab, entry, pc)
+ if fno <= 0 {
+ return ""
+ }
+ return t.string(t.binary.Uint32(t.filetab[4*fno:]))
+}
+
+// go12LineToPC maps a (file, line) pair to a program counter for the Go 1.2 pcln table.
+func (t *LineTable) go12LineToPC(file string, line int) (pc uint64) {
+ defer func() {
+ if recover() != nil {
+ pc = 0
+ }
+ }()
+
+ t.initFileMap()
+ filenum := t.fileMap[file]
+ if filenum == 0 {
+ return 0
+ }
+
+ // Scan all functions.
+ // If this turns out to be a bottleneck, we could build a map[int32][]int32
+ // mapping file number to a list of functions with code from that file.
+ for i := uint32(0); i < t.nfunctab; i++ {
+ f := t.Data[t.uintptr(t.functab[2*t.ptrsize*i+t.ptrsize:]):]
+ entry := t.uintptr(f)
+ filetab := t.binary.Uint32(f[t.ptrsize+4*4:])
+ linetab := t.binary.Uint32(f[t.ptrsize+5*4:])
+ pc := t.findFileLine(entry, filetab, linetab, int32(filenum), int32(line))
+ if pc != 0 {
+ return pc
+ }
+ }
+ return 0
+}
+
+// initFileMap initializes the map from file name to file number.
+func (t *LineTable) initFileMap() {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+
+ if t.fileMap != nil {
+ return
+ }
+ m := make(map[string]uint32)
+
+ for i := uint32(1); i < t.nfiletab; i++ {
+ s := t.string(t.binary.Uint32(t.filetab[4*i:]))
+ m[s] = i
+ }
+ t.fileMap = m
+}
+
+// go12MapFiles adds to m a key for every file in the Go 1.2 LineTable.
+// Every key maps to obj. That's not a very interesting map, but it provides
+// a way for callers to obtain the list of files in the program.
+func (t *LineTable) go12MapFiles(m map[string]*Obj, obj *Obj) {
+ defer func() {
+ recover()
+ }()
+
+ t.initFileMap()
+ for file := range t.fileMap {
+ m[file] = obj
+ }
}
diff --git a/src/pkg/debug/gosym/pclntab_test.go b/src/pkg/debug/gosym/pclntab_test.go
index 20acba612..35502e8c3 100644
--- a/src/pkg/debug/gosym/pclntab_test.go
+++ b/src/pkg/debug/gosym/pclntab_test.go
@@ -21,9 +21,17 @@ var (
pclinetestBinary string
)
-func dotest() bool {
- // For now, only works on ELF platforms.
- if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
+func dotest(self bool) bool {
+ // For now, only works on amd64 platforms.
+ if runtime.GOARCH != "amd64" {
+ return false
+ }
+ // Self test reads test binary; only works on Linux.
+ if self && runtime.GOOS != "linux" {
+ return false
+ }
+ // Command below expects "sh", so Unix.
+ if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
return false
}
if pclinetestBinary != "" {
@@ -41,7 +49,7 @@ func dotest() bool {
// the resulting binary looks like it was built from pclinetest.s,
// but we have renamed it to keep it away from the go tool.
pclinetestBinary = filepath.Join(pclineTempDir, "pclinetest")
- command := fmt.Sprintf("go tool 6a -o %s.6 pclinetest.asm && go tool 6l -E main -o %s %s.6",
+ command := fmt.Sprintf("go tool 6a -o %s.6 pclinetest.asm && go tool 6l -H linux -E main -o %s %s.6",
pclinetestBinary, pclinetestBinary, pclinetestBinary)
cmd := exec.Command("sh", "-c", command)
cmd.Stdout = os.Stdout
@@ -100,12 +108,16 @@ func parse(file string, f *elf.File, t *testing.T) (*elf.File, *Table) {
var goarch = os.Getenv("O")
func TestLineFromAline(t *testing.T) {
- if !dotest() {
+ if !dotest(true) {
return
}
defer endtest()
tab := getTable(t)
+ if tab.go12line != nil {
+ // aline's don't exist in the Go 1.2 table.
+ t.Skip("not relevant to Go 1.2 symbol table")
+ }
// Find the sym package
pkg := tab.LookupFunc("debug/gosym.TestLineFromAline").Obj
@@ -148,12 +160,16 @@ func TestLineFromAline(t *testing.T) {
}
func TestLineAline(t *testing.T) {
- if !dotest() {
+ if !dotest(true) {
return
}
defer endtest()
tab := getTable(t)
+ if tab.go12line != nil {
+ // aline's don't exist in the Go 1.2 table.
+ t.Skip("not relevant to Go 1.2 symbol table")
+ }
for _, o := range tab.Files {
// A source file can appear multiple times in a
@@ -190,7 +206,7 @@ func TestLineAline(t *testing.T) {
}
func TestPCLine(t *testing.T) {
- if !dotest() {
+ if !dotest(false) {
return
}
defer endtest()
@@ -206,16 +222,17 @@ func TestPCLine(t *testing.T) {
sym := tab.LookupFunc("linefrompc")
wantLine := 0
for pc := sym.Entry; pc < sym.End; pc++ {
- file, line, fn := tab.PCToLine(pc)
off := pc - text.Addr // TODO(rsc): should not need off; bug in 8g
+ if textdat[off] == 255 {
+ break
+ }
wantLine += int(textdat[off])
- t.Logf("off is %d", off)
+ t.Logf("off is %d %#x (max %d)", off, textdat[off], sym.End-pc)
+ file, line, fn := tab.PCToLine(pc)
if fn == nil {
t.Errorf("failed to get line of PC %#x", pc)
- } else if !strings.HasSuffix(file, "pclinetest.asm") {
- t.Errorf("expected %s (%s) at PC %#x, got %s (%s)", "pclinetest.asm", sym.Name, pc, file, fn.Name)
- } else if line != wantLine || fn != sym {
- t.Errorf("expected :%d (%s) at PC %#x, got :%d (%s)", wantLine, sym.Name, pc, line, fn.Name)
+ } else if !strings.HasSuffix(file, "pclinetest.asm") || line != wantLine || fn != sym {
+ t.Errorf("PCToLine(%#x) = %s:%d (%s), want %s:%d (%s)", pc, file, line, fn.Name, "pclinetest.asm", wantLine, sym.Name)
}
}
@@ -227,6 +244,9 @@ func TestPCLine(t *testing.T) {
for pc := sym.Value; pc < sym.End; pc += 2 + uint64(textdat[off]) {
file, line, fn := tab.PCToLine(pc)
off = pc - text.Addr
+ if textdat[off] == 255 {
+ break
+ }
wantLine += int(textdat[off])
if line != wantLine {
t.Errorf("expected line %d at PC %#x in pcfromline, got %d", wantLine, pc, line)
diff --git a/src/pkg/debug/gosym/symtab.go b/src/pkg/debug/gosym/symtab.go
index 81ed4fb27..9ab05bac2 100644
--- a/src/pkg/debug/gosym/symtab.go
+++ b/src/pkg/debug/gosym/symtab.go
@@ -34,7 +34,7 @@ type Sym struct {
Func *Func
}
-// Static returns whether this symbol is static (not visible outside its file).
+// Static reports whether this symbol is static (not visible outside its file).
func (s *Sym) Static() bool { return s.Type >= 'a' }
// PackageName returns the package part of the symbol name,
@@ -77,10 +77,26 @@ type Func struct {
Obj *Obj
}
-// An Obj represents a single object file.
+// An Obj represents a collection of functions in a symbol table.
+//
+// The exact method of division of a binary into separate Objs is an internal detail
+// of the symbol table format.
+//
+// In early versions of Go each source file became a different Obj.
+//
+// In Go 1 and Go 1.1, each package produced one Obj for all Go sources
+// and one Obj per C source file.
+//
+// In Go 1.2, there is a single Obj for the entire program.
type Obj struct {
+ // Funcs is a list of functions in the Obj.
Funcs []Func
- Paths []Sym
+
+ // In Go 1.1 and earlier, Paths is a list of symbols corresponding
+ // to the source file names that produced the Obj.
+ // In Go 1.2, Paths is nil.
+ // Use the keys of Table.Files to obtain a list of source files.
+ Paths []Sym // meta
}
/*
@@ -93,9 +109,10 @@ type Obj struct {
type Table struct {
Syms []Sym
Funcs []Func
- Files map[string]*Obj
- Objs []Obj
- // textEnd uint64;
+ Files map[string]*Obj // nil for Go 1.2 and later binaries
+ Objs []Obj // nil for Go 1.2 and later binaries
+
+ go12line *LineTable // Go 1.2 line number table
}
type sym struct {
@@ -105,10 +122,11 @@ type sym struct {
name []byte
}
-var littleEndianSymtab = []byte{0xFD, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00}
-var bigEndianSymtab = []byte{0xFF, 0xFF, 0xFF, 0xFD, 0x00, 0x00, 0x00}
-
-var oldLittleEndianSymtab = []byte{0xFE, 0xFF, 0xFF, 0xFF, 0x00, 0x00}
+var (
+ littleEndianSymtab = []byte{0xFD, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00}
+ bigEndianSymtab = []byte{0xFF, 0xFF, 0xFF, 0xFD, 0x00, 0x00, 0x00}
+ oldLittleEndianSymtab = []byte{0xFE, 0xFF, 0xFF, 0xFF, 0x00, 0x00}
+)
func walksymtab(data []byte, fn func(sym) error) error {
var order binary.ByteOrder = binary.BigEndian
@@ -260,6 +278,9 @@ func NewTable(symtab []byte, pcln *LineTable) (*Table, error) {
}
var t Table
+ if pcln.isGo12() {
+ t.go12line = pcln
+ }
fname := make(map[uint16]string)
t.Syms = make([]Sym, 0, n)
nf := 0
@@ -316,17 +337,29 @@ func NewTable(symtab []byte, pcln *LineTable) (*Table, error) {
}
t.Funcs = make([]Func, 0, nf)
- t.Objs = make([]Obj, 0, nz)
t.Files = make(map[string]*Obj)
+ var obj *Obj
+ if t.go12line != nil {
+ // Put all functions into one Obj.
+ t.Objs = make([]Obj, 1)
+ obj = &t.Objs[0]
+ t.go12line.go12MapFiles(t.Files, obj)
+ } else {
+ t.Objs = make([]Obj, 0, nz)
+ }
+
// Count text symbols and attach frame sizes, parameters, and
// locals to them. Also, find object file boundaries.
- var obj *Obj
lastf := 0
for i := 0; i < len(t.Syms); i++ {
sym := &t.Syms[i]
switch sym.Type {
case 'Z', 'z': // path symbol
+ if t.go12line != nil {
+ // Go 1.2 binaries have the file information elsewhere. Ignore.
+ break
+ }
// Finish the current object
if obj != nil {
obj.Funcs = t.Funcs[lastf:]
@@ -395,7 +428,12 @@ func NewTable(symtab []byte, pcln *LineTable) (*Table, error) {
fn.Sym = sym
fn.Entry = sym.Value
fn.Obj = obj
- if pcln != nil {
+ if t.go12line != nil {
+ // All functions share the same line table.
+ // It knows how to narrow down to a specific
+ // function quickly.
+ fn.LineTable = t.go12line
+ } else if pcln != nil {
fn.LineTable = pcln.slice(fn.Entry)
pcln = fn.LineTable
}
@@ -448,18 +486,32 @@ func (t *Table) PCToLine(pc uint64) (file string, line int, fn *Func) {
if fn = t.PCToFunc(pc); fn == nil {
return
}
- file, line = fn.Obj.lineFromAline(fn.LineTable.PCToLine(pc))
+ if t.go12line != nil {
+ file = t.go12line.go12PCToFile(pc)
+ line = t.go12line.go12PCToLine(pc)
+ } else {
+ file, line = fn.Obj.lineFromAline(fn.LineTable.PCToLine(pc))
+ }
return
}
// LineToPC looks up the first program counter on the given line in
-// the named file. Returns UnknownPathError or UnknownLineError if
+// the named file. It returns UnknownPathError or UnknownLineError if
// there is an error looking up this line.
func (t *Table) LineToPC(file string, line int) (pc uint64, fn *Func, err error) {
obj, ok := t.Files[file]
if !ok {
return 0, nil, UnknownFileError(file)
}
+
+ if t.go12line != nil {
+ pc := t.go12line.go12LineToPC(file, line)
+ if pc == 0 {
+ return 0, nil, &UnknownLineError{file, line}
+ }
+ return pc, t.PCToFunc(pc), nil
+ }
+
abs, err := obj.alineFromLine(file, line)
if err != nil {
return
@@ -503,9 +555,7 @@ func (t *Table) LookupFunc(name string) *Func {
}
// SymByAddr returns the text, data, or bss symbol starting at the given address.
-// TODO(rsc): Allow lookup by any address within the symbol.
func (t *Table) SymByAddr(addr uint64) *Sym {
- // TODO(austin) Maybe make a map
for i := range t.Syms {
s := &t.Syms[i]
switch s.Type {
@@ -522,6 +572,13 @@ func (t *Table) SymByAddr(addr uint64) *Sym {
* Object files
*/
+// This is legacy code for Go 1.1 and earlier, which used the
+// Plan 9 format for pc-line tables. This code was never quite
+// correct. It's probably very close, and it's usually correct, but
+// we never quite found all the corner cases.
+//
+// Go 1.2 and later use a simpler format, documented at golang.org/s/go12symtab.
+
func (o *Obj) lineFromAline(aline int) (string, int) {
type stackEnt struct {
path string
@@ -533,8 +590,6 @@ func (o *Obj) lineFromAline(aline int) (string, int) {
noPath := &stackEnt{"", 0, 0, nil}
tos := noPath
- // TODO(austin) I have no idea how 'Z' symbols work, except
- // that they pop the stack.
pathloop:
for _, s := range o.Paths {
val := int(s.Value)
diff --git a/src/pkg/encoding/asn1/asn1.go b/src/pkg/encoding/asn1/asn1.go
index cac9d64b5..992356c26 100644
--- a/src/pkg/encoding/asn1/asn1.go
+++ b/src/pkg/encoding/asn1/asn1.go
@@ -32,14 +32,14 @@ type StructuralError struct {
Msg string
}
-func (e StructuralError) Error() string { return "ASN.1 structure error: " + e.Msg }
+func (e StructuralError) Error() string { return "asn1: structure error: " + e.Msg }
// A SyntaxError suggests that the ASN.1 data is invalid.
type SyntaxError struct {
Msg string
}
-func (e SyntaxError) Error() string { return "ASN.1 syntax error: " + e.Msg }
+func (e SyntaxError) Error() string { return "asn1: syntax error: " + e.Msg }
// We start by dealing with each of the primitive types in turn.
@@ -51,7 +51,19 @@ func parseBool(bytes []byte) (ret bool, err error) {
return
}
- return bytes[0] != 0, nil
+ // DER demands that "If the encoding represents the boolean value TRUE,
+ // its single contents octet shall have all eight bits set to one."
+ // Thus only 0 and 255 are valid encoded values.
+ switch bytes[0] {
+ case 0:
+ ret = false
+ case 0xff:
+ ret = true
+ default:
+ err = SyntaxError{"invalid boolean"}
+ }
+
+ return
}
// INTEGER
@@ -171,7 +183,7 @@ func parseBitString(bytes []byte) (ret BitString, err error) {
// An ObjectIdentifier represents an ASN.1 OBJECT IDENTIFIER.
type ObjectIdentifier []int
-// Equal returns true iff oi and other represent the same identifier.
+// Equal reports whether oi and other represent the same identifier.
func (oi ObjectIdentifier) Equal(other ObjectIdentifier) bool {
if len(oi) != len(other) {
return false
@@ -198,12 +210,24 @@ func parseObjectIdentifier(bytes []byte) (s []int, err error) {
// 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
+ // The first varint is 40*value1 + value2:
+ // According to this packing, value1 can take the values 0, 1 and 2 only.
+ // When value1 = 0 or value1 = 1, then value2 is <= 39. When value1 = 2,
+ // then there are no restrictions on value2.
+ v, offset, err := parseBase128Int(bytes, 0)
+ if err != nil {
+ return
+ }
+ if v < 80 {
+ s[0] = v / 40
+ s[1] = v % 40
+ } else {
+ s[0] = 2
+ s[1] = v - 80
+ }
+
i := 2
- for offset := 1; offset < len(bytes); i++ {
- var v int
+ for ; offset < len(bytes); i++ {
v, offset, err = parseBase128Int(bytes, offset)
if err != nil {
return
@@ -573,7 +597,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
}
} else {
if fieldType != flagType {
- err = StructuralError{"Zero length explicit tag was not an asn1.Flag"}
+ err = StructuralError{"zero length explicit tag was not an asn1.Flag"}
return
}
v.SetBool(true)
diff --git a/src/pkg/encoding/asn1/asn1_test.go b/src/pkg/encoding/asn1/asn1_test.go
index 6e98dcf0b..f68804ebf 100644
--- a/src/pkg/encoding/asn1/asn1_test.go
+++ b/src/pkg/encoding/asn1/asn1_test.go
@@ -12,6 +12,32 @@ import (
"time"
)
+type boolTest struct {
+ in []byte
+ ok bool
+ out bool
+}
+
+var boolTestData = []boolTest{
+ {[]byte{0x00}, true, false},
+ {[]byte{0xff}, true, true},
+ {[]byte{0x00, 0x00}, false, false},
+ {[]byte{0xff, 0xff}, false, false},
+ {[]byte{0x01}, false, false},
+}
+
+func TestParseBool(t *testing.T) {
+ for i, test := range boolTestData {
+ ret, err := parseBool(test.in)
+ if (err == nil) != test.ok {
+ t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok)
+ }
+ if test.ok && ret != test.out {
+ t.Errorf("#%d: Bad result: %v (expected %v)", i, ret, test.out)
+ }
+ }
+}
+
type int64Test struct {
in []byte
ok bool
@@ -183,6 +209,7 @@ var objectIdentifierTestData = []objectIdentifierTest{
{[]byte{85}, true, []int{2, 5}},
{[]byte{85, 0x02}, true, []int{2, 5, 2}},
{[]byte{85, 0x02, 0xc0, 0x00}, true, []int{2, 5, 2, 0x2000}},
+ {[]byte{0x81, 0x34, 0x03}, true, []int{2, 100, 3}},
{[]byte{85, 0x02, 0xc0, 0x80, 0x80, 0x80, 0x80}, false, []int{}},
}
@@ -378,7 +405,7 @@ var unmarshalTestData = []struct {
{[]byte{0x30, 0x03, 0x81, 0x01, 0x01}, &TestContextSpecificTags{1}},
{[]byte{0x30, 0x08, 0xa1, 0x03, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02}, &TestContextSpecificTags2{1, 2}},
{[]byte{0x01, 0x01, 0x00}, newBool(false)},
- {[]byte{0x01, 0x01, 0x01}, newBool(true)},
+ {[]byte{0x01, 0x01, 0xff}, newBool(true)},
{[]byte{0x30, 0x0b, 0x13, 0x03, 0x66, 0x6f, 0x6f, 0x02, 0x01, 0x22, 0x02, 0x01, 0x33}, &TestElementsAfterString{"foo", 0x22, 0x33}},
{[]byte{0x30, 0x05, 0x02, 0x03, 0x12, 0x34, 0x56}, &TestBigInt{big.NewInt(0x123456)}},
}
diff --git a/src/pkg/encoding/asn1/marshal.go b/src/pkg/encoding/asn1/marshal.go
index adaf80dcd..ed17e41a5 100644
--- a/src/pkg/encoding/asn1/marshal.go
+++ b/src/pkg/encoding/asn1/marshal.go
@@ -240,11 +240,11 @@ func marshalBitString(out *forkableWriter, b BitString) (err error) {
}
func marshalObjectIdentifier(out *forkableWriter, oid []int) (err error) {
- if len(oid) < 2 || oid[0] > 6 || oid[1] >= 40 {
+ if len(oid) < 2 || oid[0] > 2 || (oid[0] < 2 && oid[1] >= 40) {
return StructuralError{"invalid object identifier"}
}
- err = out.WriteByte(byte(oid[0]*40 + oid[1]))
+ err = marshalBase128Int(out, int64(oid[0]*40+oid[1]))
if err != nil {
return
}
@@ -304,7 +304,7 @@ func marshalUTCTime(out *forkableWriter, t time.Time) (err error) {
case 2000 <= year && year < 2050:
err = marshalTwoDigits(out, int(year-2000))
default:
- return StructuralError{"Cannot represent time as UTCTime"}
+ return StructuralError{"cannot represent time as UTCTime"}
}
if err != nil {
return
@@ -441,11 +441,11 @@ func marshalBody(out *forkableWriter, value reflect.Value, params fieldParameter
return
}
- var params fieldParameters
+ var fp fieldParameters
for i := 0; i < v.Len(); i++ {
var pre *forkableWriter
pre, out = out.fork()
- err = marshalField(pre, v.Index(i), params)
+ err = marshalField(pre, v.Index(i), fp)
if err != nil {
return
}
@@ -501,7 +501,7 @@ func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters)
class := classUniversal
if params.stringType != 0 && tag != tagPrintableString {
- return StructuralError{"Explicit string type given to non-string member"}
+ return StructuralError{"explicit string type given to non-string member"}
}
if tag == tagPrintableString {
@@ -525,7 +525,7 @@ func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters)
if params.set {
if tag != tagSequence {
- return StructuralError{"Non sequence tagged as set"}
+ return StructuralError{"non sequence tagged as set"}
}
tag = tagSet
}
diff --git a/src/pkg/encoding/asn1/marshal_test.go b/src/pkg/encoding/asn1/marshal_test.go
index b4dbe71ef..763c86da2 100644
--- a/src/pkg/encoding/asn1/marshal_test.go
+++ b/src/pkg/encoding/asn1/marshal_test.go
@@ -87,6 +87,7 @@ var marshalTests = []marshalTest{
{BitString{[]byte{0x81, 0xf0}, 12}, "03030481f0"},
{ObjectIdentifier([]int{1, 2, 3, 4}), "06032a0304"},
{ObjectIdentifier([]int{1, 2, 840, 133549, 1, 1, 5}), "06092a864888932d010105"},
+ {ObjectIdentifier([]int{2, 100, 3}), "0603813403"},
{"test", "130474657374"},
{
"" +
diff --git a/src/pkg/encoding/binary/binary.go b/src/pkg/encoding/binary/binary.go
index edbac197d..f3466b9af 100644
--- a/src/pkg/encoding/binary/binary.go
+++ b/src/pkg/encoding/binary/binary.go
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Package binary implements translation between numbers and byte sequences
-// and encoding and decoding of varints.
+// Package binary implements simple translation between numbers and byte
+// sequences and encoding and decoding of varints.
//
// Numbers are translated by reading and writing fixed-size values.
// A fixed-size value is either a fixed-size arithmetic
@@ -13,6 +13,11 @@
// Varints are a method of encoding integers using one or more bytes;
// numbers with smaller absolute value take a smaller number of bytes.
// For a specification, see http://code.google.com/apis/protocolbuffers/docs/encoding.html.
+//
+// This package favors simplicity over efficiency. Clients that require
+// high-performance serialization, especially for large data structures,
+// should look at more advanced solutions such as the encoding/gob
+// package or protocol buffers.
package binary
import (
@@ -129,30 +134,65 @@ func (bigEndian) GoString() string { return "binary.BigEndian" }
// blank (_) field names is skipped; i.e., blank field names
// may be used for padding.
func Read(r io.Reader, order ByteOrder, data interface{}) error {
- // Fast path for basic types.
- if n := intDestSize(data); n != 0 {
+ // Fast path for basic types and slices.
+ if n := intDataSize(data); n != 0 {
var b [8]byte
- bs := b[:n]
+ var bs []byte
+ if n > len(b) {
+ bs = make([]byte, n)
+ } else {
+ bs = b[:n]
+ }
if _, err := io.ReadFull(r, bs); err != nil {
return err
}
- switch v := data.(type) {
+ switch data := data.(type) {
case *int8:
- *v = int8(b[0])
+ *data = int8(b[0])
case *uint8:
- *v = b[0]
+ *data = b[0]
case *int16:
- *v = int16(order.Uint16(bs))
+ *data = int16(order.Uint16(bs))
case *uint16:
- *v = order.Uint16(bs)
+ *data = order.Uint16(bs)
case *int32:
- *v = int32(order.Uint32(bs))
+ *data = int32(order.Uint32(bs))
case *uint32:
- *v = order.Uint32(bs)
+ *data = order.Uint32(bs)
case *int64:
- *v = int64(order.Uint64(bs))
+ *data = int64(order.Uint64(bs))
case *uint64:
- *v = order.Uint64(bs)
+ *data = order.Uint64(bs)
+ case []int8:
+ for i, x := range bs { // Easier to loop over the input for 8-bit values.
+ data[i] = int8(x)
+ }
+ case []uint8:
+ copy(data, bs)
+ case []int16:
+ for i := range data {
+ data[i] = int16(order.Uint16(bs[2*i:]))
+ }
+ case []uint16:
+ for i := range data {
+ data[i] = order.Uint16(bs[2*i:])
+ }
+ case []int32:
+ for i := range data {
+ data[i] = int32(order.Uint32(bs[4*i:]))
+ }
+ case []uint32:
+ for i := range data {
+ data[i] = order.Uint32(bs[4*i:])
+ }
+ case []int64:
+ for i := range data {
+ data[i] = int64(order.Uint64(bs[8*i:]))
+ }
+ case []uint64:
+ for i := range data {
+ data[i] = order.Uint64(bs[8*i:])
+ }
}
return nil
}
@@ -187,60 +227,95 @@ func Read(r io.Reader, order ByteOrder, data interface{}) error {
// When writing structs, zero values are written for fields
// with blank (_) field names.
func Write(w io.Writer, order ByteOrder, data interface{}) error {
- // Fast path for basic types.
- var b [8]byte
- var bs []byte
- switch v := data.(type) {
- case *int8:
- bs = b[:1]
- b[0] = byte(*v)
- case int8:
- bs = b[:1]
- b[0] = byte(v)
- case *uint8:
- bs = b[:1]
- b[0] = *v
- case uint8:
- bs = b[:1]
- b[0] = byte(v)
- case *int16:
- bs = b[:2]
- order.PutUint16(bs, uint16(*v))
- case int16:
- bs = b[:2]
- order.PutUint16(bs, uint16(v))
- case *uint16:
- bs = b[:2]
- order.PutUint16(bs, *v)
- case uint16:
- bs = b[:2]
- order.PutUint16(bs, v)
- case *int32:
- bs = b[:4]
- order.PutUint32(bs, uint32(*v))
- case int32:
- bs = b[:4]
- order.PutUint32(bs, uint32(v))
- case *uint32:
- bs = b[:4]
- order.PutUint32(bs, *v)
- case uint32:
- bs = b[:4]
- order.PutUint32(bs, v)
- case *int64:
- bs = b[:8]
- order.PutUint64(bs, uint64(*v))
- case int64:
- bs = b[:8]
- order.PutUint64(bs, uint64(v))
- case *uint64:
- bs = b[:8]
- order.PutUint64(bs, *v)
- case uint64:
- bs = b[:8]
- order.PutUint64(bs, v)
- }
- if bs != nil {
+ // Fast path for basic types and slices.
+ if n := intDataSize(data); n != 0 {
+ var b [8]byte
+ var bs []byte
+ if n > len(b) {
+ bs = make([]byte, n)
+ } else {
+ bs = b[:n]
+ }
+ switch v := data.(type) {
+ case *int8:
+ bs = b[:1]
+ b[0] = byte(*v)
+ case int8:
+ bs = b[:1]
+ b[0] = byte(v)
+ case []int8:
+ for i, x := range v {
+ bs[i] = byte(x)
+ }
+ case *uint8:
+ bs = b[:1]
+ b[0] = *v
+ case uint8:
+ bs = b[:1]
+ b[0] = byte(v)
+ case []uint8:
+ bs = v
+ case *int16:
+ bs = b[:2]
+ order.PutUint16(bs, uint16(*v))
+ case int16:
+ bs = b[:2]
+ order.PutUint16(bs, uint16(v))
+ case []int16:
+ for i, x := range v {
+ order.PutUint16(bs[2*i:], uint16(x))
+ }
+ case *uint16:
+ bs = b[:2]
+ order.PutUint16(bs, *v)
+ case uint16:
+ bs = b[:2]
+ order.PutUint16(bs, v)
+ case []uint16:
+ for i, x := range v {
+ order.PutUint16(bs[2*i:], x)
+ }
+ case *int32:
+ bs = b[:4]
+ order.PutUint32(bs, uint32(*v))
+ case int32:
+ bs = b[:4]
+ order.PutUint32(bs, uint32(v))
+ case []int32:
+ for i, x := range v {
+ order.PutUint32(bs[4*i:], uint32(x))
+ }
+ case *uint32:
+ bs = b[:4]
+ order.PutUint32(bs, *v)
+ case uint32:
+ bs = b[:4]
+ order.PutUint32(bs, v)
+ case []uint32:
+ for i, x := range v {
+ order.PutUint32(bs[4*i:], x)
+ }
+ case *int64:
+ bs = b[:8]
+ order.PutUint64(bs, uint64(*v))
+ case int64:
+ bs = b[:8]
+ order.PutUint64(bs, uint64(v))
+ case []int64:
+ for i, x := range v {
+ order.PutUint64(bs[8*i:], uint64(x))
+ }
+ case *uint64:
+ bs = b[:8]
+ order.PutUint64(bs, *v)
+ case uint64:
+ bs = b[:8]
+ order.PutUint64(bs, v)
+ case []uint64:
+ for i, x := range v {
+ order.PutUint64(bs[8*i:], x)
+ }
+ }
_, err := w.Write(bs)
return err
}
@@ -530,18 +605,34 @@ func (e *encoder) skip(v reflect.Value) {
e.buf = e.buf[n:]
}
-// intDestSize returns the size of the integer that ptrType points to,
-// or 0 if the type is not supported.
-func intDestSize(ptrType interface{}) int {
- switch ptrType.(type) {
- case *int8, *uint8:
+// intDataSize returns the size of the data required to represent the data when encoded.
+// It returns zero if the type cannot be implemented by the fast path in Read or Write.
+func intDataSize(data interface{}) int {
+ switch data := data.(type) {
+ case int8, *int8, *uint8:
return 1
- case *int16, *uint16:
+ case []int8:
+ return len(data)
+ case []uint8:
+ return len(data)
+ case int16, *int16, *uint16:
return 2
- case *int32, *uint32:
+ case []int16:
+ return 2 * len(data)
+ case []uint16:
+ return 2 * len(data)
+ case int32, *int32, *uint32:
return 4
- case *int64, *uint64:
+ case []int32:
+ return 4 * len(data)
+ case []uint32:
+ return 4 * len(data)
+ case int64, *int64, *uint64:
return 8
+ case []int64:
+ return 8 * len(data)
+ case []uint64:
+ return 8 * len(data)
}
return 0
}
diff --git a/src/pkg/encoding/binary/binary_test.go b/src/pkg/encoding/binary/binary_test.go
index 056f0998f..fdfee7d87 100644
--- a/src/pkg/encoding/binary/binary_test.go
+++ b/src/pkg/encoding/binary/binary_test.go
@@ -141,6 +141,52 @@ func TestWriteSlice(t *testing.T) {
checkResult(t, "WriteSlice", BigEndian, err, buf.Bytes(), src)
}
+// Addresses of arrays are easier to manipulate with reflection than are slices.
+var intArrays = []interface{}{
+ &[100]int8{},
+ &[100]int16{},
+ &[100]int32{},
+ &[100]int64{},
+ &[100]uint8{},
+ &[100]uint16{},
+ &[100]uint32{},
+ &[100]uint64{},
+}
+
+func TestSliceRoundTrip(t *testing.T) {
+ buf := new(bytes.Buffer)
+ for _, array := range intArrays {
+ src := reflect.ValueOf(array).Elem()
+ unsigned := false
+ switch src.Index(0).Kind() {
+ case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ unsigned = true
+ }
+ for i := 0; i < src.Len(); i++ {
+ if unsigned {
+ src.Index(i).SetUint(uint64(i * 0x07654321))
+ } else {
+ src.Index(i).SetInt(int64(i * 0x07654321))
+ }
+ }
+ buf.Reset()
+ srcSlice := src.Slice(0, src.Len())
+ err := Write(buf, BigEndian, srcSlice.Interface())
+ if err != nil {
+ t.Fatal(err)
+ }
+ dst := reflect.New(src.Type()).Elem()
+ dstSlice := dst.Slice(0, dst.Len())
+ err = Read(buf, BigEndian, dstSlice.Interface())
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !reflect.DeepEqual(src.Interface(), dst.Interface()) {
+ t.Fatal(src)
+ }
+ }
+}
+
func TestWriteT(t *testing.T) {
buf := new(bytes.Buffer)
ts := T{}
@@ -312,3 +358,16 @@ func BenchmarkWriteInts(b *testing.B) {
b.Fatalf("first half doesn't match: %x %x", buf.Bytes(), big[:30])
}
}
+
+func BenchmarkWriteSlice1000Int32s(b *testing.B) {
+ slice := make([]int32, 1000)
+ buf := new(bytes.Buffer)
+ var w io.Writer = buf
+ b.SetBytes(4 * 1000)
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ buf.Reset()
+ Write(w, BigEndian, slice)
+ }
+ b.StopTimer()
+}
diff --git a/src/pkg/encoding/binary/example_test.go b/src/pkg/encoding/binary/example_test.go
index dec12ebf5..067cf553b 100644
--- a/src/pkg/encoding/binary/example_test.go
+++ b/src/pkg/encoding/binary/example_test.go
@@ -42,7 +42,7 @@ func ExampleWrite_multi() {
func ExampleRead() {
var pi float64
b := []byte{0x18, 0x2d, 0x44, 0x54, 0xfb, 0x21, 0x09, 0x40}
- buf := bytes.NewBuffer(b)
+ buf := bytes.NewReader(b)
err := binary.Read(buf, binary.LittleEndian, &pi)
if err != nil {
fmt.Println("binary.Read failed:", err)
diff --git a/src/pkg/encoding/csv/reader.go b/src/pkg/encoding/csv/reader.go
index b099caf60..b328dcc37 100644
--- a/src/pkg/encoding/csv/reader.go
+++ b/src/pkg/encoding/csv/reader.go
@@ -72,7 +72,7 @@ func (e *ParseError) Error() string {
// These are the errors that can be returned in ParseError.Error
var (
- ErrTrailingComma = errors.New("extra delimiter at end of line")
+ ErrTrailingComma = errors.New("extra delimiter at end of line") // no longer used
ErrBareQuote = errors.New("bare \" in non-quoted-field")
ErrQuote = errors.New("extraneous \" in field")
ErrFieldCount = errors.New("wrong number of fields in line")
@@ -98,16 +98,14 @@ var (
// If LazyQuotes is true, a quote may appear in an unquoted field and a
// non-doubled quote may appear in a quoted field.
//
-// If TrailingComma is true, the last field may be an unquoted empty field.
-//
// If TrimLeadingSpace is true, leading white space in a field is ignored.
type Reader struct {
- Comma rune // Field delimiter (set to ',' by NewReader)
- Comment rune // Comment character for start of line
- FieldsPerRecord int // Number of expected fields per record
- LazyQuotes bool // Allow lazy quotes
- TrailingComma bool // Allow trailing comma
- TrimLeadingSpace bool // Trim leading space
+ Comma rune // field delimiter (set to ',' by NewReader)
+ Comment rune // comment character for start of line
+ FieldsPerRecord int // number of expected fields per record
+ LazyQuotes bool // allow lazy quotes
+ TrailingComma bool // ignored; here for backwards compatibility
+ TrimLeadingSpace bool // trim leading space
line int
column int
r *bufio.Reader
@@ -257,23 +255,15 @@ func (r *Reader) parseField() (haveField bool, delim rune, err error) {
r.field.Reset()
r1, err := r.readRune()
- if err != nil {
- // If we have EOF and are not at the start of a line
- // then we return the empty field. We have already
- // checked for trailing commas if needed.
- if err == io.EOF && r.column != 0 {
- return true, 0, err
- }
- return false, 0, err
+ for err == nil && r.TrimLeadingSpace && r1 != '\n' && unicode.IsSpace(r1) {
+ r1, err = r.readRune()
}
- if r.TrimLeadingSpace {
- for r1 != '\n' && unicode.IsSpace(r1) {
- r1, err = r.readRune()
- if err != nil {
- return false, 0, err
- }
- }
+ if err == io.EOF && r.column != 0 {
+ return true, 0, err
+ }
+ if err != nil {
+ return false, 0, err
}
switch r1 {
@@ -349,25 +339,5 @@ func (r *Reader) parseField() (haveField bool, delim rune, err error) {
return false, 0, err
}
- if !r.TrailingComma {
- // We don't allow trailing commas. See if we
- // are at the end of the line (being mindful
- // of trimming spaces).
- c := r.column
- r1, err = r.readRune()
- if r.TrimLeadingSpace {
- for r1 != '\n' && unicode.IsSpace(r1) {
- r1, err = r.readRune()
- if err != nil {
- break
- }
- }
- }
- if err == io.EOF || r1 == '\n' {
- r.column = c // report the comma
- return false, 0, r.error(ErrTrailingComma)
- }
- r.unreadRune()
- }
return true, r1, nil
}
diff --git a/src/pkg/encoding/csv/reader_test.go b/src/pkg/encoding/csv/reader_test.go
index 5fd84a76b..123df06bc 100644
--- a/src/pkg/encoding/csv/reader_test.go
+++ b/src/pkg/encoding/csv/reader_test.go
@@ -171,32 +171,32 @@ field"`,
Output: [][]string{{"a", "b", "c"}, {"d", "e"}},
},
{
- Name: "BadTrailingCommaEOF",
- Input: "a,b,c,",
- Error: "extra delimiter at end of line", Line: 1, Column: 5,
+ Name: "TrailingCommaEOF",
+ Input: "a,b,c,",
+ Output: [][]string{{"a", "b", "c", ""}},
},
{
- Name: "BadTrailingCommaEOL",
- Input: "a,b,c,\n",
- Error: "extra delimiter at end of line", Line: 1, Column: 5,
+ Name: "TrailingCommaEOL",
+ Input: "a,b,c,\n",
+ Output: [][]string{{"a", "b", "c", ""}},
},
{
- Name: "BadTrailingCommaSpaceEOF",
+ Name: "TrailingCommaSpaceEOF",
TrimLeadingSpace: true,
Input: "a,b,c, ",
- Error: "extra delimiter at end of line", Line: 1, Column: 5,
+ Output: [][]string{{"a", "b", "c", ""}},
},
{
- Name: "BadTrailingCommaSpaceEOL",
+ Name: "TrailingCommaSpaceEOL",
TrimLeadingSpace: true,
Input: "a,b,c, \n",
- Error: "extra delimiter at end of line", Line: 1, Column: 5,
+ Output: [][]string{{"a", "b", "c", ""}},
},
{
- Name: "BadTrailingCommaLine3",
+ Name: "TrailingCommaLine3",
TrimLeadingSpace: true,
Input: "a,b,c\nd,e,f\ng,hi,",
- Error: "extra delimiter at end of line", Line: 3, Column: 4,
+ Output: [][]string{{"a", "b", "c"}, {"d", "e", "f"}, {"g", "hi", ""}},
},
{
Name: "NotTrailingComma3",
@@ -231,7 +231,7 @@ x,,,
},
},
{
- Name: "Issue 2366",
+ Name: "TrailingCommaIneffective1",
TrailingComma: true,
TrimLeadingSpace: true,
Input: "a,b,\nc,d,e",
@@ -241,11 +241,14 @@ x,,,
},
},
{
- Name: "Issue 2366a",
+ Name: "TrailingCommaIneffective2",
TrailingComma: false,
TrimLeadingSpace: true,
Input: "a,b,\nc,d,e",
- Error: "extra delimiter at end of line",
+ Output: [][]string{
+ {"a", "b", ""},
+ {"c", "d", "e"},
+ },
},
}
diff --git a/src/pkg/encoding/encoding.go b/src/pkg/encoding/encoding.go
new file mode 100644
index 000000000..6d218071b
--- /dev/null
+++ b/src/pkg/encoding/encoding.go
@@ -0,0 +1,48 @@
+// Copyright 2013 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 encoding defines interfaces shared by other packages that
+// convert data to and from byte-level and textual representations.
+// Packages that check for these interfaces include encoding/gob,
+// encoding/json, and encoding/xml. As a result, implementing an
+// interface once can make a type useful in multiple encodings.
+// Standard types that implement these interfaces include time.Time and net.IP.
+// The interfaces come in pairs that produce and consume encoded data.
+package encoding
+
+// BinaryMarshaler is the interface implemented by an object that can
+// marshal itself into a binary form.
+//
+// MarshalBinary encodes the receiver into a binary form and returns the result.
+type BinaryMarshaler interface {
+ MarshalBinary() (data []byte, err error)
+}
+
+// BinaryUnmarshaler is the interface implemented by an object that can
+// unmarshal a binary representation of itself.
+//
+// UnmarshalBinary must be able to decode the form generated by MarshalBinary.
+// UnmarshalBinary must copy the data if it wishes to retain the data
+// after returning.
+type BinaryUnmarshaler interface {
+ UnmarshalBinary(data []byte) error
+}
+
+// TextMarshaler is the interface implemented by an object that can
+// marshal itself into a textual form.
+//
+// MarshalText encodes the receiver into UTF-8-encoded text and returns the result.
+type TextMarshaler interface {
+ MarshalText() (text []byte, err error)
+}
+
+// TextUnmarshaler is the interface implemented by an object that can
+// unmarshal a textual representation of itself.
+//
+// UnmarshalText must be able to decode the form generated by MarshalText.
+// UnmarshalText must copy the text if it wishes to retain the text
+// after returning.
+type TextUnmarshaler interface {
+ UnmarshalText(text []byte) error
+}
diff --git a/src/pkg/encoding/gob/codec_test.go b/src/pkg/encoding/gob/codec_test.go
index 9e38e31d5..b40f78360 100644
--- a/src/pkg/encoding/gob/codec_test.go
+++ b/src/pkg/encoding/gob/codec_test.go
@@ -1009,24 +1009,6 @@ func TestBadRecursiveType(t *testing.T) {
// Can't test decode easily because we can't encode one, so we can't pass one to a Decoder.
}
-type Bad0 struct {
- CH chan int
- C float64
-}
-
-func TestInvalidField(t *testing.T) {
- var bad0 Bad0
- bad0.CH = make(chan int)
- b := new(bytes.Buffer)
- dummyEncoder := new(Encoder) // sufficient for this purpose.
- dummyEncoder.encode(b, reflect.ValueOf(&bad0), userType(reflect.TypeOf(&bad0)))
- if err := dummyEncoder.err; err == nil {
- t.Error("expected error; got none")
- } else if strings.Index(err.Error(), "type") < 0 {
- t.Error("expected type error; got", err)
- }
-}
-
type Indirect struct {
A ***[3]int
S ***[]int
diff --git a/src/pkg/encoding/gob/debug.go b/src/pkg/encoding/gob/debug.go
index 31d1351fc..6117eb083 100644
--- a/src/pkg/encoding/gob/debug.go
+++ b/src/pkg/encoding/gob/debug.go
@@ -415,6 +415,16 @@ func (deb *debugger) typeDefinition(indent tab, id typeId) {
deb.delta(1)
com := deb.common()
wire.GobEncoderT = &gobEncoderType{com}
+ case 5: // BinaryMarshaler type, one field of {{Common}}
+ // Field number 0 is CommonType
+ deb.delta(1)
+ com := deb.common()
+ wire.BinaryMarshalerT = &gobEncoderType{com}
+ case 6: // TextMarshaler type, one field of {{Common}}
+ // Field number 0 is CommonType
+ deb.delta(1)
+ com := deb.common()
+ wire.TextMarshalerT = &gobEncoderType{com}
default:
errorf("bad field in type %d", fieldNum)
}
diff --git a/src/pkg/encoding/gob/decode.go b/src/pkg/encoding/gob/decode.go
index 7cc756540..3e76f4c90 100644
--- a/src/pkg/encoding/gob/decode.go
+++ b/src/pkg/encoding/gob/decode.go
@@ -9,6 +9,7 @@ package gob
import (
"bytes"
+ "encoding"
"errors"
"io"
"math"
@@ -450,11 +451,11 @@ type decEngine struct {
// allocate makes sure storage is available for an object of underlying type rtyp
// that is indir levels of indirection through p.
-func allocate(rtyp reflect.Type, p uintptr, indir int) uintptr {
+func allocate(rtyp reflect.Type, p unsafe.Pointer, indir int) unsafe.Pointer {
if indir == 0 {
return p
}
- up := unsafe.Pointer(p)
+ up := p
if indir > 1 {
up = decIndirect(up, indir)
}
@@ -462,13 +463,13 @@ func allocate(rtyp reflect.Type, p uintptr, indir int) uintptr {
// Allocate object.
*(*unsafe.Pointer)(up) = unsafe.Pointer(reflect.New(rtyp).Pointer())
}
- return *(*uintptr)(up)
+ return *(*unsafe.Pointer)(up)
}
// decodeSingle decodes a top-level value that is not a struct and stores it through p.
// Such values are preceded by a zero, making them have the memory layout of a
// struct field (although with an illegal field number).
-func (dec *Decoder) decodeSingle(engine *decEngine, ut *userTypeInfo, basep uintptr) {
+func (dec *Decoder) decodeSingle(engine *decEngine, ut *userTypeInfo, basep unsafe.Pointer) {
state := dec.newDecoderState(&dec.buf)
state.fieldnum = singletonField
delta := int(state.decodeUint())
@@ -479,7 +480,7 @@ func (dec *Decoder) decodeSingle(engine *decEngine, ut *userTypeInfo, basep uint
if instr.indir != ut.indir {
errorf("internal error: inconsistent indirection instr %d ut %d", instr.indir, ut.indir)
}
- ptr := unsafe.Pointer(basep) // offset will be zero
+ ptr := basep // offset will be zero
if instr.indir > 1 {
ptr = decIndirect(ptr, instr.indir)
}
@@ -492,7 +493,7 @@ func (dec *Decoder) decodeSingle(engine *decEngine, ut *userTypeInfo, basep uint
// 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) {
+func (dec *Decoder) decodeStruct(engine *decEngine, ut *userTypeInfo, p unsafe.Pointer, indir int) {
p = allocate(ut.base, p, indir)
state := dec.newDecoderState(&dec.buf)
state.fieldnum = -1
@@ -511,7 +512,7 @@ func (dec *Decoder) decodeStruct(engine *decEngine, ut *userTypeInfo, p uintptr,
break
}
instr := &engine.instr[fieldnum]
- p := unsafe.Pointer(basep + instr.offset)
+ p := unsafe.Pointer(uintptr(basep) + instr.offset)
if instr.indir > 1 {
p = decIndirect(p, instr.indir)
}
@@ -559,25 +560,25 @@ func (dec *Decoder) ignoreSingle(engine *decEngine) {
}
// decodeArrayHelper does the work for decoding arrays and slices.
-func (dec *Decoder) decodeArrayHelper(state *decoderState, p uintptr, elemOp decOp, elemWid uintptr, length, elemIndir int, ovfl error) {
+func (dec *Decoder) decodeArrayHelper(state *decoderState, p unsafe.Pointer, elemOp decOp, elemWid uintptr, length, elemIndir int, ovfl error) {
instr := &decInstr{elemOp, 0, elemIndir, 0, ovfl}
for i := 0; i < length; i++ {
if state.b.Len() == 0 {
errorf("decoding array or slice: length exceeds input size (%d elements)", length)
}
- up := unsafe.Pointer(p)
+ up := p
if elemIndir > 1 {
up = decIndirect(up, elemIndir)
}
elemOp(instr, state, up)
- p += uintptr(elemWid)
+ p = unsafe.Pointer(uintptr(p) + elemWid)
}
}
// 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.Type, state *decoderState, p uintptr, elemOp decOp, elemWid uintptr, length, indir, elemIndir int, ovfl error) {
+func (dec *Decoder) decodeArray(atyp reflect.Type, state *decoderState, p unsafe.Pointer, elemOp decOp, elemWid uintptr, length, indir, elemIndir int, ovfl error) {
if indir > 0 {
p = allocate(atyp, p, 1) // All but the last level has been allocated by dec.Indirect
}
@@ -591,7 +592,7 @@ func (dec *Decoder) decodeArray(atyp reflect.Type, state *decoderState, p uintpt
// unlike the other items we can't use a pointer directly.
func decodeIntoValue(state *decoderState, op decOp, indir int, v reflect.Value, ovfl error) reflect.Value {
instr := &decInstr{op, 0, indir, 0, ovfl}
- up := unsafe.Pointer(unsafeAddr(v))
+ up := unsafeAddr(v)
if indir > 1 {
up = decIndirect(up, indir)
}
@@ -603,7 +604,7 @@ 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.Type, state *decoderState, p uintptr, keyOp, elemOp decOp, indir, keyIndir, elemIndir int, ovfl error) {
+func (dec *Decoder) decodeMap(mtyp reflect.Type, state *decoderState, p unsafe.Pointer, keyOp, elemOp decOp, indir, keyIndir, elemIndir int, ovfl error) {
if indir > 0 {
p = allocate(mtyp, p, 1) // All but the last level has been allocated by dec.Indirect
}
@@ -673,7 +674,7 @@ func (dec *Decoder) decodeSlice(atyp reflect.Type, state *decoderState, p uintpt
hdrp.Cap = n
}
hdrp.Len = n
- dec.decodeArrayHelper(state, hdrp.Data, elemOp, elemWid, n, elemIndir, ovfl)
+ dec.decodeArrayHelper(state, unsafe.Pointer(hdrp.Data), elemOp, elemWid, n, elemIndir, ovfl)
}
// ignoreSlice skips over the data for a slice value with no destination.
@@ -693,7 +694,7 @@ func setInterfaceValue(ivalue reflect.Value, 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.Type, state *decoderState, p uintptr, indir int) {
+func (dec *Decoder) decodeInterface(ityp reflect.Type, state *decoderState, p unsafe.Pointer, indir int) {
// Create a writable interface reflect.Value. We need one even for the nil case.
ivalue := allocValue(ityp)
// Read the name of the concrete type.
@@ -767,15 +768,22 @@ func (dec *Decoder) ignoreInterface(state *decoderState) {
// decodeGobDecoder decodes something implementing the GobDecoder interface.
// The data is encoded as a byte slice.
-func (dec *Decoder) decodeGobDecoder(state *decoderState, v reflect.Value) {
+func (dec *Decoder) decodeGobDecoder(ut *userTypeInfo, state *decoderState, v reflect.Value) {
// Read the bytes for the value.
b := make([]byte, state.decodeUint())
_, err := state.b.Read(b)
if err != nil {
error_(err)
}
- // We know it's a GobDecoder, so just call the method directly.
- err = v.Interface().(GobDecoder).GobDecode(b)
+ // We know it's one of these.
+ switch ut.externalDec {
+ case xGob:
+ err = v.Interface().(GobDecoder).GobDecode(b)
+ case xBinary:
+ err = v.Interface().(encoding.BinaryUnmarshaler).UnmarshalBinary(b)
+ case xText:
+ err = v.Interface().(encoding.TextUnmarshaler).UnmarshalText(b)
+ }
if err != nil {
error_(err)
}
@@ -825,9 +833,10 @@ var decIgnoreOpMap = map[typeId]decOp{
func (dec *Decoder) decOpFor(wireId typeId, rt reflect.Type, name string, inProgress map[reflect.Type]*decOp) (*decOp, int) {
ut := userType(rt)
// If the type implements GobEncoder, we handle it without further processing.
- if ut.isGobDecoder {
+ if ut.externalDec != 0 {
return dec.gobDecodeOpFor(ut)
}
+
// If this type is already in progress, it's a recursive type (e.g. map[string]*T).
// Return the pointer to the op we're already building.
if opPtr := inProgress[rt]; opPtr != nil {
@@ -850,7 +859,7 @@ func (dec *Decoder) decOpFor(wireId typeId, rt reflect.Type, name string, inProg
elemOp, elemIndir := dec.decOpFor(elemId, t.Elem(), name, inProgress)
ovfl := overflow(name)
op = func(i *decInstr, state *decoderState, p unsafe.Pointer) {
- state.dec.decodeArray(t, state, uintptr(p), *elemOp, t.Elem().Size(), t.Len(), i.indir, elemIndir, ovfl)
+ state.dec.decodeArray(t, state, p, *elemOp, t.Elem().Size(), t.Len(), i.indir, elemIndir, ovfl)
}
case reflect.Map:
@@ -860,8 +869,7 @@ func (dec *Decoder) decOpFor(wireId typeId, rt reflect.Type, name string, inProg
elemOp, elemIndir := dec.decOpFor(elemId, t.Elem(), "element of "+name, inProgress)
ovfl := overflow(name)
op = func(i *decInstr, state *decoderState, p unsafe.Pointer) {
- up := unsafe.Pointer(p)
- state.dec.decodeMap(t, state, uintptr(up), *keyOp, *elemOp, i.indir, keyIndir, elemIndir, ovfl)
+ state.dec.decodeMap(t, state, p, *keyOp, *elemOp, i.indir, keyIndir, elemIndir, ovfl)
}
case reflect.Slice:
@@ -890,11 +898,11 @@ 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.
- dec.decodeStruct(*enginePtr, userType(typ), uintptr(p), i.indir)
+ dec.decodeStruct(*enginePtr, userType(typ), p, i.indir)
}
case reflect.Interface:
op = func(i *decInstr, state *decoderState, p unsafe.Pointer) {
- state.dec.decodeInterface(t, state, uintptr(p), i.indir)
+ state.dec.decodeInterface(t, state, p, i.indir)
}
}
}
@@ -955,7 +963,7 @@ func (dec *Decoder) decIgnoreOpFor(wireId typeId) decOp {
state.dec.ignoreStruct(*enginePtr)
}
- case wire.GobEncoderT != nil:
+ case wire.GobEncoderT != nil, wire.BinaryMarshalerT != nil, wire.TextMarshalerT != nil:
op = func(i *decInstr, state *decoderState, p unsafe.Pointer) {
state.dec.ignoreGobDecoder(state)
}
@@ -994,7 +1002,7 @@ func (dec *Decoder) gobDecodeOpFor(ut *userTypeInfo) (*decOp, int) {
} else {
v = reflect.NewAt(rcvrType, p).Elem()
}
- state.dec.decodeGobDecoder(state, v)
+ state.dec.decodeGobDecoder(ut, state, v)
}
return &op, int(ut.indir)
@@ -1011,12 +1019,18 @@ func (dec *Decoder) compatibleType(fr reflect.Type, fw typeId, inProgress map[re
inProgress[fr] = fw
ut := userType(fr)
wire, ok := dec.wireType[fw]
- // If fr is a GobDecoder, the wire type must be GobEncoder.
- // And if fr is not a GobDecoder, the wire type must not be either.
- if ut.isGobDecoder != (ok && wire.GobEncoderT != nil) { // the parentheses look odd but are correct.
+ // If wire was encoded with an encoding method, fr must have that method.
+ // And if not, it must not.
+ // At most one of the booleans in ut is set.
+ // We could possibly relax this constraint in the future in order to
+ // choose the decoding method using the data in the wireType.
+ // The parentheses look odd but are correct.
+ if (ut.externalDec == xGob) != (ok && wire.GobEncoderT != nil) ||
+ (ut.externalDec == xBinary) != (ok && wire.BinaryMarshalerT != nil) ||
+ (ut.externalDec == xText) != (ok && wire.TextMarshalerT != nil) {
return false
}
- if ut.isGobDecoder { // This test trumps all others.
+ if ut.externalDec != 0 { // This test trumps all others.
return true
}
switch t := ut.base; t.Kind() {
@@ -1115,8 +1129,7 @@ func (dec *Decoder) compileIgnoreSingle(remoteId typeId) (engine *decEngine, err
func (dec *Decoder) compileDec(remoteId typeId, ut *userTypeInfo) (engine *decEngine, err error) {
rt := ut.base
srt := rt
- if srt.Kind() != reflect.Struct ||
- ut.isGobDecoder {
+ if srt.Kind() != reflect.Struct || ut.externalDec != 0 {
return dec.compileSingle(remoteId, ut)
}
var wireStruct *structType
@@ -1224,14 +1237,14 @@ func (dec *Decoder) decodeValue(wireId typeId, val reflect.Value) {
return
}
engine := *enginePtr
- if st := base; st.Kind() == reflect.Struct && !ut.isGobDecoder {
+ if st := base; st.Kind() == reflect.Struct && ut.externalDec == 0 {
if engine.numInstr == 0 && st.NumField() > 0 && len(dec.wireType[wireId].StructT.Field) > 0 {
name := base.Name()
errorf("type mismatch: no fields matched compiling decoder for %s", name)
}
- dec.decodeStruct(engine, ut, uintptr(unsafeAddr(val)), ut.indir)
+ dec.decodeStruct(engine, ut, unsafeAddr(val), ut.indir)
} else {
- dec.decodeSingle(engine, ut, uintptr(unsafeAddr(val)))
+ dec.decodeSingle(engine, ut, unsafeAddr(val))
}
}
@@ -1283,13 +1296,13 @@ func init() {
// into existing structs or slices cannot be addressed,
// so simulate it by returning a pointer to a copy.
// Each call allocates once.
-func unsafeAddr(v reflect.Value) uintptr {
+func unsafeAddr(v reflect.Value) unsafe.Pointer {
if v.CanAddr() {
- return v.UnsafeAddr()
+ return unsafe.Pointer(v.UnsafeAddr())
}
x := reflect.New(v.Type()).Elem()
x.Set(v)
- return x.UnsafeAddr()
+ return unsafe.Pointer(x.UnsafeAddr())
}
// Gob depends on being able to take the address
diff --git a/src/pkg/encoding/gob/doc.go b/src/pkg/encoding/gob/doc.go
index 5bd61b12e..d0acaba1a 100644
--- a/src/pkg/encoding/gob/doc.go
+++ b/src/pkg/encoding/gob/doc.go
@@ -8,6 +8,12 @@ Encoder (transmitter) and a Decoder (receiver). A typical use is transporting
arguments and results of remote procedure calls (RPCs) such as those provided by
package "rpc".
+The implementation compiles a custom codec for each data type in the stream and
+is most efficient when a single Encoder is used to transmit a stream of values,
+amortizing the cost of compilation.
+
+Basics
+
A stream of gobs is self-describing. Each data item in the stream is preceded by
a specification of its type, expressed in terms of a small set of predefined
types. Pointers are not transmitted, but the things they point to are
@@ -20,6 +26,8 @@ all type information is sent before it is needed. At the receive side, a
Decoder retrieves values from the encoded stream and unpacks them into local
variables.
+Types and Values
+
The source and destination values/types need not correspond exactly. For structs,
fields (identified by name) that are in the source but absent from the receiving
variable will be ignored. Fields that are in the receiving variable but missing
@@ -67,19 +75,29 @@ point values may be received into any floating point variable. However,
the destination variable must be able to represent the value or the decode
operation will fail.
-Structs, arrays and slices are also supported. Structs encode and
-decode only exported fields. Strings and arrays of bytes are supported
-with a special, efficient representation (see below). When a slice
-is decoded, if the existing slice has capacity the slice will be
-extended in place; if not, a new array is allocated. Regardless,
-the length of the resulting slice reports the number of elements
-decoded.
+Structs, arrays and slices are also supported. Structs encode and decode only
+exported fields. Strings and arrays of bytes are supported with a special,
+efficient representation (see below). When a slice is decoded, if the existing
+slice has capacity the slice will be extended in place; if not, a new array is
+allocated. Regardless, the length of the resulting slice reports the number of
+elements decoded.
+
+Functions and channels will not be sent in a gob. Attempting to encode such a value
+at top the level will fail. A struct field of chan or func type is treated exactly
+like an unexported field and is ignored.
+
+Gob can encode a value of any type implementing the GobEncoder or
+encoding.BinaryMarshaler interfaces by calling the corresponding method,
+in that order of preference.
+
+Gob can decode a value of any type implementing the GobDecoder or
+encoding.BinaryUnmarshaler interfaces by calling the corresponding method,
+again in that order of preference.
-Functions and channels cannot be sent in a gob. Attempting
-to encode a value that contains one will fail.
+Encoding Details
-The rest of this comment documents the encoding, details that are not important
-for most users. Details are presented bottom-up.
+This section documents the encoding, details that are not important for most
+users. Details are presented bottom-up.
An unsigned integer is sent one of two ways. If it is less than 128, it is sent
as a byte with that value. Otherwise it is sent as a minimal-length big-endian
diff --git a/src/pkg/encoding/gob/encode.go b/src/pkg/encoding/gob/encode.go
index ea37a6cbd..d158b6442 100644
--- a/src/pkg/encoding/gob/encode.go
+++ b/src/pkg/encoding/gob/encode.go
@@ -6,6 +6,7 @@ package gob
import (
"bytes"
+ "encoding"
"math"
"reflect"
"unsafe"
@@ -338,14 +339,14 @@ type encEngine struct {
const singletonField = 0
// encodeSingle encodes a single top-level non-struct value.
-func (enc *Encoder) encodeSingle(b *bytes.Buffer, engine *encEngine, basep uintptr) {
+func (enc *Encoder) encodeSingle(b *bytes.Buffer, engine *encEngine, basep unsafe.Pointer) {
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.
state.sendZero = true
instr := &engine.instr[singletonField]
- p := unsafe.Pointer(basep) // offset will be zero
+ p := basep // offset will be zero
if instr.indir > 0 {
if p = encIndirect(p, instr.indir); p == nil {
return
@@ -356,12 +357,12 @@ func (enc *Encoder) encodeSingle(b *bytes.Buffer, engine *encEngine, basep uintp
}
// encodeStruct encodes a single struct value.
-func (enc *Encoder) encodeStruct(b *bytes.Buffer, engine *encEngine, basep uintptr) {
+func (enc *Encoder) encodeStruct(b *bytes.Buffer, engine *encEngine, basep unsafe.Pointer) {
state := enc.newEncoderState(b)
state.fieldnum = -1
for i := 0; i < len(engine.instr); i++ {
instr := &engine.instr[i]
- p := unsafe.Pointer(basep + instr.offset)
+ p := unsafe.Pointer(uintptr(basep) + instr.offset)
if instr.indir > 0 {
if p = encIndirect(p, instr.indir); p == nil {
continue
@@ -373,22 +374,22 @@ func (enc *Encoder) encodeStruct(b *bytes.Buffer, engine *encEngine, basep uintp
}
// 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) {
+func (enc *Encoder) encodeArray(b *bytes.Buffer, p unsafe.Pointer, op encOp, elemWid uintptr, elemIndir int, length int) {
state := enc.newEncoderState(b)
state.fieldnum = -1
state.sendZero = true
state.encodeUint(uint64(length))
for i := 0; i < length; i++ {
elemp := p
- up := unsafe.Pointer(elemp)
if elemIndir > 0 {
- if up = encIndirect(up, elemIndir); up == nil {
+ up := encIndirect(elemp, elemIndir)
+ if up == nil {
errorf("encodeArray: nil element")
}
- elemp = uintptr(up)
+ elemp = up
}
- op(nil, state, unsafe.Pointer(elemp))
- p += uintptr(elemWid)
+ op(nil, state, elemp)
+ p = unsafe.Pointer(uintptr(p) + elemWid)
}
enc.freeEncoderState(state)
}
@@ -401,7 +402,7 @@ func encodeReflectValue(state *encoderState, v reflect.Value, op encOp, indir in
if !v.IsValid() {
errorf("encodeReflectValue: nil element")
}
- op(nil, state, unsafe.Pointer(unsafeAddr(v)))
+ op(nil, state, unsafeAddr(v))
}
// encodeMap encodes a map as unsigned count followed by key:value pairs.
@@ -474,7 +475,7 @@ func (enc *Encoder) encodeInterface(b *bytes.Buffer, iv reflect.Value) {
enc.freeEncoderState(state)
}
-// isZero returns whether the value is the zero of its type.
+// isZero reports whether the value is the zero of its type.
func isZero(val reflect.Value) bool {
switch val.Kind() {
case reflect.Array:
@@ -511,10 +512,20 @@ func isZero(val reflect.Value) bool {
// encGobEncoder encodes a value that implements the GobEncoder interface.
// The data is sent as a byte array.
-func (enc *Encoder) encodeGobEncoder(b *bytes.Buffer, v reflect.Value) {
+func (enc *Encoder) encodeGobEncoder(b *bytes.Buffer, ut *userTypeInfo, v reflect.Value) {
// TODO: should we catch panics from the called method?
- // We know it's a GobEncoder, so just call the method directly.
- data, err := v.Interface().(GobEncoder).GobEncode()
+
+ var data []byte
+ var err error
+ // We know it's one of these.
+ switch ut.externalEnc {
+ case xGob:
+ data, err = v.Interface().(GobEncoder).GobEncode()
+ case xBinary:
+ data, err = v.Interface().(encoding.BinaryMarshaler).MarshalBinary()
+ case xText:
+ data, err = v.Interface().(encoding.TextMarshaler).MarshalText()
+ }
if err != nil {
error_(err)
}
@@ -550,7 +561,7 @@ var encOpTable = [...]encOp{
func (enc *Encoder) encOpFor(rt reflect.Type, inProgress map[reflect.Type]*encOp) (*encOp, int) {
ut := userType(rt)
// If the type implements GobEncoder, we handle it without further processing.
- if ut.isGobEncoder {
+ if ut.externalEnc != 0 {
return enc.gobEncodeOpFor(ut)
}
// If this type is already in progress, it's a recursive type (e.g. map[string]*T).
@@ -575,21 +586,21 @@ func (enc *Encoder) encOpFor(rt reflect.Type, inProgress map[reflect.Type]*encOp
break
}
// Slices have a header; we decode it to find the underlying array.
- elemOp, indir := enc.encOpFor(t.Elem(), inProgress)
+ elemOp, elemIndir := enc.encOpFor(t.Elem(), inProgress)
op = func(i *encInstr, state *encoderState, p unsafe.Pointer) {
slice := (*reflect.SliceHeader)(p)
if !state.sendZero && slice.Len == 0 {
return
}
state.update(i)
- state.enc.encodeArray(state.b, slice.Data, *elemOp, t.Elem().Size(), indir, int(slice.Len))
+ state.enc.encodeArray(state.b, unsafe.Pointer(slice.Data), *elemOp, t.Elem().Size(), elemIndir, int(slice.Len))
}
case reflect.Array:
// True arrays have size in the type.
- elemOp, indir := enc.encOpFor(t.Elem(), inProgress)
+ elemOp, elemIndir := 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())
+ state.enc.encodeArray(state.b, p, *elemOp, t.Elem().Size(), elemIndir, t.Len())
}
case reflect.Map:
keyOp, keyIndir := enc.encOpFor(t.Key(), inProgress)
@@ -615,7 +626,7 @@ func (enc *Encoder) encOpFor(rt reflect.Type, inProgress map[reflect.Type]*encOp
op = func(i *encInstr, state *encoderState, p unsafe.Pointer) {
state.update(i)
// indirect through info to delay evaluation for recursive structs
- state.enc.encodeStruct(state.b, info.encoder, uintptr(p))
+ state.enc.encodeStruct(state.b, info.encoder, p)
}
case reflect.Interface:
op = func(i *encInstr, state *encoderState, p unsafe.Pointer) {
@@ -661,7 +672,7 @@ func (enc *Encoder) gobEncodeOpFor(ut *userTypeInfo) (*encOp, int) {
return
}
state.update(i)
- state.enc.encodeGobEncoder(state.b, v)
+ state.enc.encodeGobEncoder(state.b, ut, v)
}
return &op, int(ut.encIndir) // encIndir: op will get called with p == address of receiver.
}
@@ -672,14 +683,13 @@ func (enc *Encoder) compileEnc(ut *userTypeInfo) *encEngine {
engine := new(encEngine)
seen := make(map[reflect.Type]*encOp)
rt := ut.base
- if ut.isGobEncoder {
+ if ut.externalEnc != 0 {
rt = ut.user
}
- if !ut.isGobEncoder &&
- srt.Kind() == reflect.Struct {
+ if ut.externalEnc == 0 && srt.Kind() == reflect.Struct {
for fieldNum, wireFieldNum := 0, 0; fieldNum < srt.NumField(); fieldNum++ {
f := srt.Field(fieldNum)
- if !isExported(f.Name) {
+ if !isSent(&f) {
continue
}
op, indir := enc.encOpFor(f.Type, seen)
@@ -736,13 +746,13 @@ func (enc *Encoder) encode(b *bytes.Buffer, value reflect.Value, ut *userTypeInf
defer catchError(&enc.err)
engine := enc.lockAndGetEncEngine(ut)
indir := ut.indir
- if ut.isGobEncoder {
+ if ut.externalEnc != 0 {
indir = int(ut.encIndir)
}
for i := 0; i < indir; i++ {
value = reflect.Indirect(value)
}
- if !ut.isGobEncoder && value.Type().Kind() == reflect.Struct {
+ if ut.externalEnc == 0 && value.Type().Kind() == reflect.Struct {
enc.encodeStruct(b, engine, unsafeAddr(value))
} else {
enc.encodeSingle(b, engine, unsafeAddr(value))
diff --git a/src/pkg/encoding/gob/encoder.go b/src/pkg/encoding/gob/encoder.go
index f669c3d5b..a3301c3bd 100644
--- a/src/pkg/encoding/gob/encoder.go
+++ b/src/pkg/encoding/gob/encoder.go
@@ -6,7 +6,6 @@ package gob
import (
"bytes"
- "errors"
"io"
"reflect"
"sync"
@@ -54,10 +53,6 @@ func (enc *Encoder) popWriter() {
enc.w = enc.w[0 : len(enc.w)-1]
}
-func (enc *Encoder) badType(rt reflect.Type) {
- enc.setError(errors.New("gob: can't encode type " + rt.String()))
-}
-
func (enc *Encoder) setError(err error) {
if enc.err == nil { // remember the first.
enc.err = err
@@ -135,7 +130,7 @@ func (enc *Encoder) sendActualType(w io.Writer, state *encoderState, ut *userTyp
// 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 {
+ if ut.externalEnc != 0 {
// The rules are different: regardless of the underlying type's representation,
// we need to tell the other side that the base type is a GobEncoder.
return enc.sendActualType(w, state, ut, ut.base)
@@ -163,8 +158,7 @@ func (enc *Encoder) sendType(w io.Writer, state *encoderState, origt reflect.Typ
// 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)
+ // If we get here, it's a field of a struct; ignore it.
return
}
@@ -184,7 +178,7 @@ func (enc *Encoder) sendTypeDescriptor(w io.Writer, state *encoderState, ut *use
// Make sure the type is known to the other side.
// First, have we already sent this type?
rt := ut.base
- if ut.isGobEncoder {
+ if ut.externalEnc != 0 {
rt = ut.user
}
if _, alreadySent := enc.sent[rt]; !alreadySent {
diff --git a/src/pkg/encoding/gob/encoder_test.go b/src/pkg/encoding/gob/encoder_test.go
index b684772c6..4ecf51d12 100644
--- a/src/pkg/encoding/gob/encoder_test.go
+++ b/src/pkg/encoding/gob/encoder_test.go
@@ -131,7 +131,7 @@ func TestBadData(t *testing.T) {
corruptDataCheck("\x03now is the time for all good men", errBadType, t)
}
-// Types not supported by the Encoder.
+// Types not supported at top level by the Encoder.
var unsupportedValues = []interface{}{
make(chan int),
func(a int) bool { return true },
@@ -662,19 +662,35 @@ func TestSequentialDecoder(t *testing.T) {
}
}
-// Should be able to have unrepresentable fields (chan, func) as long as they
-// are unexported.
+// Should be able to have unrepresentable fields (chan, func, *chan etc.); we just ignore them.
type Bug2 struct {
- A int
- b chan int
-}
-
-func TestUnexportedChan(t *testing.T) {
- b := Bug2{23, make(chan int)}
- var stream bytes.Buffer
- enc := NewEncoder(&stream)
- if err := enc.Encode(b); err != nil {
- t.Fatalf("error encoding unexported channel: %s", err)
+ A int
+ C chan int
+ CP *chan int
+ F func()
+ FPP **func()
+}
+
+func TestChanFuncIgnored(t *testing.T) {
+ c := make(chan int)
+ f := func() {}
+ fp := &f
+ b0 := Bug2{23, c, &c, f, &fp}
+ var buf bytes.Buffer
+ enc := NewEncoder(&buf)
+ if err := enc.Encode(b0); err != nil {
+ t.Fatal("error encoding:", err)
+ }
+ var b1 Bug2
+ err := NewDecoder(&buf).Decode(&b1)
+ if err != nil {
+ t.Fatal("decode:", err)
+ }
+ if b1.A != b0.A {
+ t.Fatalf("got %d want %d", b1.A, b0.A)
+ }
+ if b1.C != nil || b1.CP != nil || b1.F != nil || b1.FPP != nil {
+ t.Fatal("unexpected value for chan or func")
}
}
diff --git a/src/pkg/encoding/gob/example_encdec_test.go b/src/pkg/encoding/gob/example_encdec_test.go
new file mode 100644
index 000000000..e45ad4ccf
--- /dev/null
+++ b/src/pkg/encoding/gob/example_encdec_test.go
@@ -0,0 +1,61 @@
+// Copyright 2013 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_test
+
+import (
+ "bytes"
+ "encoding/gob"
+ "fmt"
+ "log"
+)
+
+// The Vector type has unexported fields, which the package cannot access.
+// We therefore write a BinaryMarshal/BinaryUnmarshal method pair to allow us
+// to send and receive the type with the gob package. These interfaces are
+// defined in the "encoding" package.
+// We could equivalently use the locally defined GobEncode/GobDecoder
+// interfaces.
+type Vector struct {
+ x, y, z int
+}
+
+func (v Vector) MarshalBinary() ([]byte, error) {
+ // A simple encoding: plain text.
+ var b bytes.Buffer
+ fmt.Fprintln(&b, v.x, v.y, v.z)
+ return b.Bytes(), nil
+}
+
+// UnmarshalBinary modifies the receiver so it must take a pointer receiver.
+func (v *Vector) UnmarshalBinary(data []byte) error {
+ // A simple encoding: plain text.
+ b := bytes.NewBuffer(data)
+ _, err := fmt.Fscanln(b, &v.x, &v.y, &v.z)
+ return err
+}
+
+// This example transmits a value that implements the custom encoding and decoding methods.
+func Example_encodeDecode() {
+ var network bytes.Buffer // Stand-in for the network.
+
+ // Create an encoder and send a value.
+ enc := gob.NewEncoder(&network)
+ err := enc.Encode(Vector{3, 4, 5})
+ if err != nil {
+ log.Fatal("encode:", err)
+ }
+
+ // Create a decoder and receive a value.
+ dec := gob.NewDecoder(&network)
+ var v Vector
+ err = dec.Decode(&v)
+ if err != nil {
+ log.Fatal("decode:", err)
+ }
+ fmt.Println(v)
+
+ // Output:
+ // {3 4 5}
+}
diff --git a/src/pkg/encoding/gob/example_interface_test.go b/src/pkg/encoding/gob/example_interface_test.go
new file mode 100644
index 000000000..4681e6307
--- /dev/null
+++ b/src/pkg/encoding/gob/example_interface_test.go
@@ -0,0 +1,81 @@
+// Copyright 2013 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_test
+
+import (
+ "bytes"
+ "encoding/gob"
+ "fmt"
+ "log"
+ "math"
+)
+
+type Point struct {
+ X, Y int
+}
+
+func (p Point) Hypotenuse() float64 {
+ return math.Hypot(float64(p.X), float64(p.Y))
+}
+
+type Pythagoras interface {
+ Hypotenuse() float64
+}
+
+// This example shows how to encode an interface value. The key
+// distinction from regular types is to register the concrete type that
+// implements the interface.
+func Example_interface() {
+ var network bytes.Buffer // Stand-in for the network.
+
+ // We must register the concrete type for the encoder and decoder (which would
+ // normally be on a separate machine from the encoder). On each end, this tells the
+ // engine which concrete type is being sent that implements the interface.
+ gob.Register(Point{})
+
+ // Create an encoder and send some values.
+ enc := gob.NewEncoder(&network)
+ for i := 1; i <= 3; i++ {
+ interfaceEncode(enc, Point{3 * i, 4 * i})
+ }
+
+ // Create a decoder and receive some values.
+ dec := gob.NewDecoder(&network)
+ for i := 1; i <= 3; i++ {
+ result := interfaceDecode(dec)
+ fmt.Println(result.Hypotenuse())
+ }
+
+ // Output:
+ // 5
+ // 10
+ // 15
+}
+
+// interfaceEncode encodes the interface value into the encoder.
+func interfaceEncode(enc *gob.Encoder, p Pythagoras) {
+ // The encode will fail unless the concrete type has been
+ // registered. We registered it in the calling function.
+
+ // Pass pointer to interface so Encode sees (and hence sends) a value of
+ // interface type. If we passed p directly it would see the concrete type instead.
+ // See the blog post, "The Laws of Reflection" for background.
+ err := enc.Encode(&p)
+ if err != nil {
+ log.Fatal("encode:", err)
+ }
+}
+
+// interfaceDecode decodes the next interface value from the stream and returns it.
+func interfaceDecode(dec *gob.Decoder) Pythagoras {
+ // The decode will fail unless the concrete type on the wire has been
+ // registered. We registered it in the calling function.
+ var p Pythagoras
+ err := dec.Decode(&p)
+ if err != nil {
+ log.Fatal("decode:", err)
+ }
+ return p
+}
diff --git a/src/pkg/encoding/gob/example_test.go b/src/pkg/encoding/gob/example_test.go
new file mode 100644
index 000000000..020352cee
--- /dev/null
+++ b/src/pkg/encoding/gob/example_test.go
@@ -0,0 +1,60 @@
+// Copyright 2013 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_test
+
+import (
+ "bytes"
+ "encoding/gob"
+ "fmt"
+ "log"
+)
+
+type P struct {
+ X, Y, Z int
+ Name string
+}
+
+type Q struct {
+ X, Y *int32
+ Name string
+}
+
+// This example shows the basic usage of the package: Create an encoder,
+// transmit some values, receive them with a decoder.
+func Example_basic() {
+ // Initialize the encoder and decoder. Normally enc and dec would be
+ // bound to network connections and the encoder and decoder would
+ // run in different processes.
+ var network bytes.Buffer // Stand-in for a network connection
+ enc := gob.NewEncoder(&network) // Will write to network.
+ dec := gob.NewDecoder(&network) // Will read from network.
+
+ // Encode (send) some values.
+ err := enc.Encode(P{3, 4, 5, "Pythagoras"})
+ if err != nil {
+ log.Fatal("encode error:", err)
+ }
+ err = enc.Encode(P{1782, 1841, 1922, "Treehouse"})
+ if err != nil {
+ log.Fatal("encode error:", err)
+ }
+
+ // Decode (receive) and print the values.
+ var q Q
+ err = dec.Decode(&q)
+ if err != nil {
+ log.Fatal("decode error 1:", err)
+ }
+ fmt.Printf("%q: {%d, %d}\n", q.Name, *q.X, *q.Y)
+ err = dec.Decode(&q)
+ if err != nil {
+ log.Fatal("decode error 2:", err)
+ }
+ fmt.Printf("%q: {%d, %d}\n", q.Name, *q.X, *q.Y)
+
+ // Output:
+ // "Pythagoras": {3, 4}
+ // "Treehouse": {1782, 1841}
+}
diff --git a/src/pkg/encoding/gob/gobencdec_test.go b/src/pkg/encoding/gob/gobencdec_test.go
index ddcd80b1a..0193e2b67 100644
--- a/src/pkg/encoding/gob/gobencdec_test.go
+++ b/src/pkg/encoding/gob/gobencdec_test.go
@@ -11,6 +11,7 @@ import (
"errors"
"fmt"
"io"
+ "net"
"strings"
"testing"
"time"
@@ -34,6 +35,14 @@ type Gobber int
type ValueGobber string // encodes with a value, decodes with a pointer.
+type BinaryGobber int
+
+type BinaryValueGobber string
+
+type TextGobber int
+
+type TextValueGobber string
+
// The relevant methods
func (g *ByteStruct) GobEncode() ([]byte, error) {
@@ -101,6 +110,24 @@ func (g *Gobber) GobDecode(data []byte) error {
return err
}
+func (g *BinaryGobber) MarshalBinary() ([]byte, error) {
+ return []byte(fmt.Sprintf("VALUE=%d", *g)), nil
+}
+
+func (g *BinaryGobber) UnmarshalBinary(data []byte) error {
+ _, err := fmt.Sscanf(string(data), "VALUE=%d", (*int)(g))
+ return err
+}
+
+func (g *TextGobber) MarshalText() ([]byte, error) {
+ return []byte(fmt.Sprintf("VALUE=%d", *g)), nil
+}
+
+func (g *TextGobber) UnmarshalText(data []byte) error {
+ _, err := fmt.Sscanf(string(data), "VALUE=%d", (*int)(g))
+ return err
+}
+
func (v ValueGobber) GobEncode() ([]byte, error) {
return []byte(fmt.Sprintf("VALUE=%s", v)), nil
}
@@ -110,6 +137,24 @@ func (v *ValueGobber) GobDecode(data []byte) error {
return err
}
+func (v BinaryValueGobber) MarshalBinary() ([]byte, error) {
+ return []byte(fmt.Sprintf("VALUE=%s", v)), nil
+}
+
+func (v *BinaryValueGobber) UnmarshalBinary(data []byte) error {
+ _, err := fmt.Sscanf(string(data), "VALUE=%s", (*string)(v))
+ return err
+}
+
+func (v TextValueGobber) MarshalText() ([]byte, error) {
+ return []byte(fmt.Sprintf("VALUE=%s", v)), nil
+}
+
+func (v *TextValueGobber) UnmarshalText(data []byte) error {
+ _, err := fmt.Sscanf(string(data), "VALUE=%s", (*string)(v))
+ return err
+}
+
// Structs that include GobEncodable fields.
type GobTest0 struct {
@@ -130,28 +175,42 @@ type GobTest2 struct {
type GobTest3 struct {
X int // guarantee we have something in common with GobTest*
G *Gobber
+ B *BinaryGobber
+ T *TextGobber
}
type GobTest4 struct {
- X int // guarantee we have something in common with GobTest*
- V ValueGobber
+ X int // guarantee we have something in common with GobTest*
+ V ValueGobber
+ BV BinaryValueGobber
+ TV TextValueGobber
}
type GobTest5 struct {
- X int // guarantee we have something in common with GobTest*
- V *ValueGobber
+ X int // guarantee we have something in common with GobTest*
+ V *ValueGobber
+ BV *BinaryValueGobber
+ TV *TextValueGobber
}
type GobTest6 struct {
- X int // guarantee we have something in common with GobTest*
- V ValueGobber
- W *ValueGobber
+ X int // guarantee we have something in common with GobTest*
+ V ValueGobber
+ W *ValueGobber
+ BV BinaryValueGobber
+ BW *BinaryValueGobber
+ TV TextValueGobber
+ TW *TextValueGobber
}
type GobTest7 struct {
- X int // guarantee we have something in common with GobTest*
- V *ValueGobber
- W ValueGobber
+ X int // guarantee we have something in common with GobTest*
+ V *ValueGobber
+ W ValueGobber
+ BV *BinaryValueGobber
+ BW BinaryValueGobber
+ TV *TextValueGobber
+ TW TextValueGobber
}
type GobTestIgnoreEncoder struct {
@@ -198,7 +257,9 @@ func TestGobEncoderField(t *testing.T) {
// Now a field that's not a structure.
b.Reset()
gobber := Gobber(23)
- err = enc.Encode(GobTest3{17, &gobber})
+ bgobber := BinaryGobber(24)
+ tgobber := TextGobber(25)
+ err = enc.Encode(GobTest3{17, &gobber, &bgobber, &tgobber})
if err != nil {
t.Fatal("encode error:", err)
}
@@ -207,7 +268,7 @@ func TestGobEncoderField(t *testing.T) {
if err != nil {
t.Fatal("decode error:", err)
}
- if *y.G != 23 {
+ if *y.G != 23 || *y.B != 24 || *y.T != 25 {
t.Errorf("expected '23 got %d", *y.G)
}
}
@@ -357,7 +418,7 @@ func TestGobEncoderValueEncoder(t *testing.T) {
// first, string in field to byte in field
b := new(bytes.Buffer)
enc := NewEncoder(b)
- err := enc.Encode(GobTest4{17, ValueGobber("hello")})
+ err := enc.Encode(GobTest4{17, ValueGobber("hello"), BinaryValueGobber("Καλημέρα"), TextValueGobber("こんにちは")})
if err != nil {
t.Fatal("encode error:", err)
}
@@ -367,8 +428,8 @@ func TestGobEncoderValueEncoder(t *testing.T) {
if err != nil {
t.Fatal("decode error:", err)
}
- if *x.V != "hello" {
- t.Errorf("expected `hello` got %s", x.V)
+ if *x.V != "hello" || *x.BV != "Καλημέρα" || *x.TV != "こんにちは" {
+ t.Errorf("expected `hello` got %s", *x.V)
}
}
@@ -377,13 +438,17 @@ func TestGobEncoderValueEncoder(t *testing.T) {
func TestGobEncoderValueThenPointer(t *testing.T) {
v := ValueGobber("forty-two")
w := ValueGobber("six-by-nine")
+ bv := BinaryValueGobber("1nanocentury")
+ bw := BinaryValueGobber("πseconds")
+ tv := TextValueGobber("gravitationalacceleration")
+ tw := TextValueGobber("π²ft/s²")
// this was a bug: encoding a GobEncoder by value before a GobEncoder
// pointer would cause duplicate type definitions to be sent.
b := new(bytes.Buffer)
enc := NewEncoder(b)
- if err := enc.Encode(GobTest6{42, v, &w}); err != nil {
+ if err := enc.Encode(GobTest6{42, v, &w, bv, &bw, tv, &tw}); err != nil {
t.Fatal("encode error:", err)
}
dec := NewDecoder(b)
@@ -391,6 +456,7 @@ func TestGobEncoderValueThenPointer(t *testing.T) {
if err := dec.Decode(x); err != nil {
t.Fatal("decode error:", err)
}
+
if got, want := x.V, v; got != want {
t.Errorf("v = %q, want %q", got, want)
}
@@ -399,6 +465,24 @@ func TestGobEncoderValueThenPointer(t *testing.T) {
} else if *got != want {
t.Errorf("w = %q, want %q", *got, want)
}
+
+ if got, want := x.BV, bv; got != want {
+ t.Errorf("bv = %q, want %q", got, want)
+ }
+ if got, want := x.BW, bw; got == nil {
+ t.Errorf("bw = nil, want %q", want)
+ } else if *got != want {
+ t.Errorf("bw = %q, want %q", *got, want)
+ }
+
+ if got, want := x.TV, tv; got != want {
+ t.Errorf("tv = %q, want %q", got, want)
+ }
+ if got, want := x.TW, tw; got == nil {
+ t.Errorf("tw = nil, want %q", want)
+ } else if *got != want {
+ t.Errorf("tw = %q, want %q", *got, want)
+ }
}
// Test that we can use a pointer then a value type of a GobEncoder
@@ -406,10 +490,14 @@ func TestGobEncoderValueThenPointer(t *testing.T) {
func TestGobEncoderPointerThenValue(t *testing.T) {
v := ValueGobber("forty-two")
w := ValueGobber("six-by-nine")
+ bv := BinaryValueGobber("1nanocentury")
+ bw := BinaryValueGobber("πseconds")
+ tv := TextValueGobber("gravitationalacceleration")
+ tw := TextValueGobber("π²ft/s²")
b := new(bytes.Buffer)
enc := NewEncoder(b)
- if err := enc.Encode(GobTest7{42, &v, w}); err != nil {
+ if err := enc.Encode(GobTest7{42, &v, w, &bv, bw, &tv, tw}); err != nil {
t.Fatal("encode error:", err)
}
dec := NewDecoder(b)
@@ -417,14 +505,33 @@ func TestGobEncoderPointerThenValue(t *testing.T) {
if err := dec.Decode(x); err != nil {
t.Fatal("decode error:", err)
}
+
if got, want := x.V, v; got == nil {
t.Errorf("v = nil, want %q", want)
} else if *got != want {
- t.Errorf("v = %q, want %q", got, want)
+ t.Errorf("v = %q, want %q", *got, want)
}
if got, want := x.W, w; got != want {
t.Errorf("w = %q, want %q", got, want)
}
+
+ if got, want := x.BV, bv; got == nil {
+ t.Errorf("bv = nil, want %q", want)
+ } else if *got != want {
+ t.Errorf("bv = %q, want %q", *got, want)
+ }
+ if got, want := x.BW, bw; got != want {
+ t.Errorf("bw = %q, want %q", got, want)
+ }
+
+ if got, want := x.TV, tv; got == nil {
+ t.Errorf("tv = nil, want %q", want)
+ } else if *got != want {
+ t.Errorf("tv = %q, want %q", *got, want)
+ }
+ if got, want := x.TW, tw; got != want {
+ t.Errorf("tw = %q, want %q", got, want)
+ }
}
func TestGobEncoderFieldTypeError(t *testing.T) {
@@ -521,7 +628,9 @@ func TestGobEncoderIgnoreNonStructField(t *testing.T) {
// First a field that's a structure.
enc := NewEncoder(b)
gobber := Gobber(23)
- err := enc.Encode(GobTest3{17, &gobber})
+ bgobber := BinaryGobber(24)
+ tgobber := TextGobber(25)
+ err := enc.Encode(GobTest3{17, &gobber, &bgobber, &tgobber})
if err != nil {
t.Fatal("encode error:", err)
}
@@ -659,3 +768,17 @@ func TestGobEncodePtrError(t *testing.T) {
t.Fatalf("expected nil, got %v", err2)
}
}
+
+func TestNetIP(t *testing.T) {
+ // Encoding of net.IP{1,2,3,4} in Go 1.1.
+ enc := []byte{0x07, 0x0a, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04}
+
+ var ip net.IP
+ err := NewDecoder(bytes.NewReader(enc)).Decode(&ip)
+ if err != nil {
+ t.Fatalf("decode: %v", err)
+ }
+ if ip.String() != "1.2.3.4" {
+ t.Errorf("decoded to %v, want 1.2.3.4", ip.String())
+ }
+}
diff --git a/src/pkg/encoding/gob/timing_test.go b/src/pkg/encoding/gob/timing_test.go
index f589675dd..9fbb0ac6d 100644
--- a/src/pkg/encoding/gob/timing_test.go
+++ b/src/pkg/encoding/gob/timing_test.go
@@ -6,7 +6,6 @@ package gob
import (
"bytes"
- "fmt"
"io"
"os"
"runtime"
@@ -50,6 +49,9 @@ func BenchmarkEndToEndByteBuffer(b *testing.B) {
}
func TestCountEncodeMallocs(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping malloc count in short mode")
+ }
if runtime.GOMAXPROCS(0) > 1 {
t.Skip("skipping; GOMAXPROCS>1")
}
@@ -66,10 +68,15 @@ func TestCountEncodeMallocs(t *testing.T) {
t.Fatal("encode:", err)
}
})
- fmt.Printf("mallocs per encode of type Bench: %v\n", allocs)
+ if allocs != 0 {
+ t.Fatalf("mallocs per encode of type Bench: %v; wanted 0\n", allocs)
+ }
}
func TestCountDecodeMallocs(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping malloc count in short mode")
+ }
if runtime.GOMAXPROCS(0) > 1 {
t.Skip("skipping; GOMAXPROCS>1")
}
@@ -96,5 +103,7 @@ func TestCountDecodeMallocs(t *testing.T) {
t.Fatal("decode:", err)
}
})
- fmt.Printf("mallocs per decode of type Bench: %v\n", allocs)
+ if allocs != 3 {
+ t.Fatalf("mallocs per decode of type Bench: %v; wanted 3\n", allocs)
+ }
}
diff --git a/src/pkg/encoding/gob/type.go b/src/pkg/encoding/gob/type.go
index 7fa0b499f..cad145279 100644
--- a/src/pkg/encoding/gob/type.go
+++ b/src/pkg/encoding/gob/type.go
@@ -5,6 +5,7 @@
package gob
import (
+ "encoding"
"errors"
"fmt"
"os"
@@ -18,14 +19,21 @@ import (
// 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
-}
+ 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
+ externalEnc int // xGob, xBinary, or xText
+ externalDec int // xGob, xBinary or xText
+ 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
+}
+
+// externalEncoding bits
+const (
+ xGob = 1 + iota // GobEncoder or GobDecoder
+ xBinary // encoding.BinaryMarshaler or encoding.BinaryUnmarshaler
+ xText // encoding.TextMarshaler or encoding.TextUnmarshaler
+)
var (
// Protected by an RWMutex because we read it a lot and write
@@ -75,15 +83,41 @@ func validUserType(rt reflect.Type) (ut *userTypeInfo, err error) {
}
ut.indir++
}
- ut.isGobEncoder, ut.encIndir = implementsInterface(ut.user, gobEncoderInterfaceType)
- ut.isGobDecoder, ut.decIndir = implementsInterface(ut.user, gobDecoderInterfaceType)
+
+ if ok, indir := implementsInterface(ut.user, gobEncoderInterfaceType); ok {
+ ut.externalEnc, ut.encIndir = xGob, indir
+ } else if ok, indir := implementsInterface(ut.user, binaryMarshalerInterfaceType); ok {
+ ut.externalEnc, ut.encIndir = xBinary, indir
+ }
+
+ // NOTE(rsc): Would like to allow MarshalText here, but results in incompatibility
+ // with older encodings for net.IP. See golang.org/issue/6760.
+ // } else if ok, indir := implementsInterface(ut.user, textMarshalerInterfaceType); ok {
+ // ut.externalEnc, ut.encIndir = xText, indir
+ // }
+
+ if ok, indir := implementsInterface(ut.user, gobDecoderInterfaceType); ok {
+ ut.externalDec, ut.decIndir = xGob, indir
+ } else if ok, indir := implementsInterface(ut.user, binaryUnmarshalerInterfaceType); ok {
+ ut.externalDec, ut.decIndir = xBinary, indir
+ }
+
+ // See note above.
+ // } else if ok, indir := implementsInterface(ut.user, textUnmarshalerInterfaceType); ok {
+ // ut.externalDec, ut.decIndir = xText, indir
+ // }
+
userTypeCache[rt] = ut
return
}
var (
- gobEncoderInterfaceType = reflect.TypeOf((*GobEncoder)(nil)).Elem()
- gobDecoderInterfaceType = reflect.TypeOf((*GobDecoder)(nil)).Elem()
+ gobEncoderInterfaceType = reflect.TypeOf((*GobEncoder)(nil)).Elem()
+ gobDecoderInterfaceType = reflect.TypeOf((*GobDecoder)(nil)).Elem()
+ binaryMarshalerInterfaceType = reflect.TypeOf((*encoding.BinaryMarshaler)(nil)).Elem()
+ binaryUnmarshalerInterfaceType = reflect.TypeOf((*encoding.BinaryUnmarshaler)(nil)).Elem()
+ textMarshalerInterfaceType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
+ textUnmarshalerInterfaceType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
)
// implementsInterface reports whether the type implements the
@@ -412,7 +446,7 @@ func newStructType(name string) *structType {
// works through typeIds and userTypeInfos alone.
func newTypeObject(name string, ut *userTypeInfo, rt reflect.Type) (gobType, error) {
// Does this type implement GobEncoder?
- if ut.isGobEncoder {
+ if ut.externalEnc != 0 {
return newGobEncoderType(name), nil
}
var err error
@@ -499,7 +533,7 @@ func newTypeObject(name string, ut *userTypeInfo, rt reflect.Type) (gobType, err
idToType[st.id()] = st
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
- if !isExported(f.Name) {
+ if !isSent(&f) {
continue
}
typ := userType(f.Type).base
@@ -534,6 +568,25 @@ func isExported(name string) bool {
return unicode.IsUpper(rune)
}
+// isSent reports whether this struct field is to be transmitted.
+// It will be transmitted only if it is exported and not a chan or func field
+// or pointer to chan or func.
+func isSent(field *reflect.StructField) bool {
+ if !isExported(field.Name) {
+ return false
+ }
+ // If the field is a chan or func or pointer thereto, don't send it.
+ // That is, treat it like an unexported field.
+ typ := field.Type
+ for typ.Kind() == reflect.Ptr {
+ typ = typ.Elem()
+ }
+ if typ.Kind() == reflect.Chan || typ.Kind() == reflect.Func {
+ return false
+ }
+ return true
+}
+
// 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, error) {
@@ -593,11 +646,13 @@ func bootstrapType(name string, e interface{}, expect typeId) typeId {
// 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
+ ArrayT *arrayType
+ SliceT *sliceType
+ StructT *structType
+ MapT *mapType
+ GobEncoderT *gobEncoderType
+ BinaryMarshalerT *gobEncoderType
+ TextMarshalerT *gobEncoderType
}
func (w *wireType) string() string {
@@ -616,6 +671,10 @@ func (w *wireType) string() string {
return w.MapT.Name
case w.GobEncoderT != nil:
return w.GobEncoderT.Name
+ case w.BinaryMarshalerT != nil:
+ return w.BinaryMarshalerT.Name
+ case w.TextMarshalerT != nil:
+ return w.TextMarshalerT.Name
}
return unknown
}
@@ -631,7 +690,7 @@ var typeInfoMap = make(map[reflect.Type]*typeInfo) // protected by typeLock
// typeLock must be held.
func getTypeInfo(ut *userTypeInfo) (*typeInfo, error) {
rt := ut.base
- if ut.isGobEncoder {
+ if ut.externalEnc != 0 {
// We want the user type, not the base type.
rt = ut.user
}
@@ -646,12 +705,20 @@ func getTypeInfo(ut *userTypeInfo) (*typeInfo, error) {
}
info.id = gt.id()
- if ut.isGobEncoder {
+ if ut.externalEnc != 0 {
userType, err := getType(rt.Name(), ut, rt)
if err != nil {
return nil, err
}
- info.wire = &wireType{GobEncoderT: userType.id().gobType().(*gobEncoderType)}
+ gt := userType.id().gobType().(*gobEncoderType)
+ switch ut.externalEnc {
+ case xGob:
+ info.wire = &wireType{GobEncoderT: gt}
+ case xBinary:
+ info.wire = &wireType{BinaryMarshalerT: gt}
+ case xText:
+ info.wire = &wireType{TextMarshalerT: gt}
+ }
typeInfoMap[ut.user] = info
return info, nil
}
diff --git a/src/pkg/encoding/json/decode.go b/src/pkg/encoding/json/decode.go
index 62ac294b8..458fb39ec 100644
--- a/src/pkg/encoding/json/decode.go
+++ b/src/pkg/encoding/json/decode.go
@@ -8,6 +8,7 @@
package json
import (
+ "encoding"
"encoding/base64"
"errors"
"fmt"
@@ -37,9 +38,7 @@ import (
// keys to the keys used by Marshal (either the struct field name or its tag),
// preferring an exact match but also accepting a case-insensitive match.
//
-// To unmarshal JSON into an interface value, Unmarshal unmarshals
-// the JSON into the concrete value contained in the interface value.
-// If the interface value is nil, that is, has no concrete value stored in it,
+// To unmarshal JSON into an interface value,
// Unmarshal stores one of these in the interface value:
//
// bool, for JSON booleans
@@ -293,7 +292,7 @@ func (d *decodeState) value(v reflect.Value) {
// until it gets to a non-pointer.
// if it encounters an Unmarshaler, indirect stops and returns that.
// if decodingNull is true, indirect stops at the last pointer so it can be set to nil.
-func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler, reflect.Value) {
+func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler, encoding.TextUnmarshaler, reflect.Value) {
// If v is a named type and is addressable,
// start with its address, so that if the type has pointer methods,
// we find them.
@@ -322,28 +321,38 @@ func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler,
v.Set(reflect.New(v.Type().Elem()))
}
if v.Type().NumMethod() > 0 {
- if unmarshaler, ok := v.Interface().(Unmarshaler); ok {
- return unmarshaler, reflect.Value{}
+ if u, ok := v.Interface().(Unmarshaler); ok {
+ return u, nil, reflect.Value{}
+ }
+ if u, ok := v.Interface().(encoding.TextUnmarshaler); ok {
+ return nil, u, reflect.Value{}
}
}
v = v.Elem()
}
- return nil, v
+ return nil, 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 {
+ u, ut, pv := d.indirect(v, false)
+ if u != nil {
d.off--
- err := unmarshaler.UnmarshalJSON(d.next())
+ err := u.UnmarshalJSON(d.next())
if err != nil {
d.error(err)
}
return
}
+ if ut != nil {
+ d.saveError(&UnmarshalTypeError{"array", v.Type()})
+ d.off--
+ d.next()
+ return
+ }
+
v = pv
// Check type of target.
@@ -434,15 +443,21 @@ func (d *decodeState) array(v reflect.Value) {
// 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 {
+ u, ut, pv := d.indirect(v, false)
+ if u != nil {
d.off--
- err := unmarshaler.UnmarshalJSON(d.next())
+ err := u.UnmarshalJSON(d.next())
if err != nil {
d.error(err)
}
return
}
+ if ut != nil {
+ d.saveError(&UnmarshalTypeError{"object", v.Type()})
+ d.off--
+ d.next() // skip over { } in input
+ return
+ }
v = pv
// Decoding into nil interface? Switch to non-reflect code.
@@ -611,14 +626,37 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
return
}
wantptr := item[0] == 'n' // null
- unmarshaler, pv := d.indirect(v, wantptr)
- if unmarshaler != nil {
- err := unmarshaler.UnmarshalJSON(item)
+ u, ut, pv := d.indirect(v, wantptr)
+ if u != nil {
+ err := u.UnmarshalJSON(item)
+ if err != nil {
+ d.error(err)
+ }
+ return
+ }
+ if ut != nil {
+ if item[0] != '"' {
+ if fromQuoted {
+ d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
+ } else {
+ d.saveError(&UnmarshalTypeError{"string", v.Type()})
+ }
+ }
+ s, ok := unquoteBytes(item)
+ if !ok {
+ if fromQuoted {
+ d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
+ } else {
+ d.error(errPhase)
+ }
+ }
+ err := ut.UnmarshalText(s)
if err != nil {
d.error(err)
}
return
}
+
v = pv
switch c := item[0]; c {
diff --git a/src/pkg/encoding/json/decode_test.go b/src/pkg/encoding/json/decode_test.go
index f845f69ab..22c5f89f7 100644
--- a/src/pkg/encoding/json/decode_test.go
+++ b/src/pkg/encoding/json/decode_test.go
@@ -6,6 +6,7 @@ package json
import (
"bytes"
+ "encoding"
"fmt"
"image"
"reflect"
@@ -50,8 +51,6 @@ type tx struct {
x int
}
-var txType = reflect.TypeOf((*tx)(nil)).Elem()
-
// A type that can unmarshal itself.
type unmarshaler struct {
@@ -59,7 +58,7 @@ type unmarshaler struct {
}
func (u *unmarshaler) UnmarshalJSON(b []byte) error {
- *u = unmarshaler{true} // All we need to see that UnmarshalJson is called.
+ *u = unmarshaler{true} // All we need to see that UnmarshalJSON is called.
return nil
}
@@ -67,6 +66,26 @@ type ustruct struct {
M unmarshaler
}
+type unmarshalerText struct {
+ T bool
+}
+
+// needed for re-marshaling tests
+func (u *unmarshalerText) MarshalText() ([]byte, error) {
+ return []byte(""), nil
+}
+
+func (u *unmarshalerText) UnmarshalText(b []byte) error {
+ *u = unmarshalerText{true} // All we need to see that UnmarshalText is called.
+ return nil
+}
+
+var _ encoding.TextUnmarshaler = (*unmarshalerText)(nil)
+
+type ustructText struct {
+ M unmarshalerText
+}
+
var (
um0, um1 unmarshaler // target2 of unmarshaling
ump = &um1
@@ -74,6 +93,13 @@ var (
umslice = []unmarshaler{{true}}
umslicep = new([]unmarshaler)
umstruct = ustruct{unmarshaler{true}}
+
+ um0T, um1T unmarshalerText // target2 of unmarshaling
+ umpT = &um1T
+ umtrueT = unmarshalerText{true}
+ umsliceT = []unmarshalerText{{true}}
+ umslicepT = new([]unmarshalerText)
+ umstructT = ustructText{unmarshalerText{true}}
)
// Test data structures for anonymous fields.
@@ -184,6 +210,12 @@ type Ambig struct {
Second int `json:"Hello"`
}
+type XYZ struct {
+ X interface{}
+ Y interface{}
+ Z interface{}
+}
+
var unmarshalTests = []unmarshalTest{
// basic types
{in: `true`, ptr: new(bool), out: true},
@@ -263,6 +295,13 @@ var unmarshalTests = []unmarshalTest{
{in: `[{"T":false}]`, ptr: &umslicep, out: &umslice},
{in: `{"M":{"T":false}}`, ptr: &umstruct, out: umstruct},
+ // UnmarshalText interface test
+ {in: `"X"`, ptr: &um0T, out: umtrueT}, // use "false" so test will fail if custom unmarshaler is not called
+ {in: `"X"`, ptr: &umpT, out: &umtrueT},
+ {in: `["X"]`, ptr: &umsliceT, out: umsliceT},
+ {in: `["X"]`, ptr: &umslicepT, out: &umsliceT},
+ {in: `{"M":"X"}`, ptr: &umstructT, out: umstructT},
+
{
in: `{
"Level0": 1,
@@ -391,17 +430,23 @@ func TestMarshal(t *testing.T) {
}
}
+var badUTF8 = []struct {
+ in, out string
+}{
+ {"hello\xffworld", `"hello\ufffdworld"`},
+ {"", `""`},
+ {"\xff", `"\ufffd"`},
+ {"\xff\xff", `"\ufffd\ufffd"`},
+ {"a\xffb", `"a\ufffdb"`},
+ {"\xe6\x97\xa5\xe6\x9c\xac\xff\xaa\x9e", `"日本\ufffd\ufffd\ufffd"`},
+}
+
func TestMarshalBadUTF8(t *testing.T) {
- s := "hello\xffworld"
- b, err := Marshal(s)
- if err == nil {
- t.Fatal("Marshal bad UTF8: no error")
- }
- if len(b) != 0 {
- t.Fatal("Marshal returned data")
- }
- if _, ok := err.(*InvalidUTF8Error); !ok {
- t.Fatalf("Marshal did not return InvalidUTF8Error: %T %v", err, err)
+ for _, tt := range badUTF8 {
+ b, err := Marshal(tt.in)
+ if string(b) != tt.out || err != nil {
+ t.Errorf("Marshal(%q) = %#q, %v, want %#q, nil", tt.in, b, err, tt.out)
+ }
}
}
@@ -417,6 +462,45 @@ func TestMarshalNumberZeroVal(t *testing.T) {
}
}
+func TestMarshalEmbeds(t *testing.T) {
+ top := &Top{
+ Level0: 1,
+ Embed0: Embed0{
+ Level1b: 2,
+ Level1c: 3,
+ },
+ Embed0a: &Embed0a{
+ Level1a: 5,
+ Level1b: 6,
+ },
+ Embed0b: &Embed0b{
+ Level1a: 8,
+ Level1b: 9,
+ Level1c: 10,
+ Level1d: 11,
+ Level1e: 12,
+ },
+ Loop: Loop{
+ Loop1: 13,
+ Loop2: 14,
+ },
+ Embed0p: Embed0p{
+ Point: image.Point{X: 15, Y: 16},
+ },
+ Embed0q: Embed0q{
+ Point: Point{Z: 17},
+ },
+ }
+ b, err := Marshal(top)
+ if err != nil {
+ t.Fatal(err)
+ }
+ want := "{\"Level0\":1,\"Level1b\":2,\"Level1c\":3,\"Level1a\":5,\"LEVEL1B\":6,\"e\":{\"Level1a\":8,\"Level1b\":9,\"Level1c\":10,\"Level1d\":11,\"x\":12},\"Loop1\":13,\"Loop2\":14,\"X\":15,\"Y\":16,\"Z\":17}"
+ if string(b) != want {
+ t.Errorf("Wrong marshal result.\n got: %q\nwant: %q", b, want)
+ }
+}
+
func TestUnmarshal(t *testing.T) {
for i, tt := range unmarshalTests {
var scan scanner
@@ -432,7 +516,7 @@ func TestUnmarshal(t *testing.T) {
}
// v = new(right-type)
v := reflect.New(reflect.TypeOf(tt.ptr).Elem())
- dec := NewDecoder(bytes.NewBuffer(in))
+ dec := NewDecoder(bytes.NewReader(in))
if tt.useNumber {
dec.UseNumber()
}
@@ -457,16 +541,18 @@ func TestUnmarshal(t *testing.T) {
continue
}
vv := reflect.New(reflect.TypeOf(tt.ptr).Elem())
- dec = NewDecoder(bytes.NewBuffer(enc))
+ dec = NewDecoder(bytes.NewReader(enc))
if tt.useNumber {
dec.UseNumber()
}
if err := dec.Decode(vv.Interface()); err != nil {
- t.Errorf("#%d: error re-unmarshaling: %v", i, err)
+ t.Errorf("#%d: error re-unmarshaling %#q: %v", i, enc, err)
continue
}
if !reflect.DeepEqual(v.Elem().Interface(), vv.Elem().Interface()) {
t.Errorf("#%d: mismatch\nhave: %#+v\nwant: %#+v", i, v.Elem().Interface(), vv.Elem().Interface())
+ t.Errorf(" In: %q", strings.Map(noSpace, string(in)))
+ t.Errorf("Marshal: %q", strings.Map(noSpace, string(enc)))
continue
}
}
@@ -568,14 +654,14 @@ func TestUnmarshalPtrPtr(t *testing.T) {
}
func TestEscape(t *testing.T) {
- const input = `"foobar"<html>`
- const expected = `"\"foobar\"\u003chtml\u003e"`
+ const input = `"foobar"<html>` + " [\u2028 \u2029]"
+ const expected = `"\"foobar\"\u003chtml\u003e [\u2028 \u2029]"`
b, err := Marshal(input)
if err != nil {
t.Fatalf("Marshal error: %v", err)
}
if s := string(b); s != expected {
- t.Errorf("Encoding of [%s] was [%s], want [%s]", input, s, expected)
+ t.Errorf("Encoding of [%s]:\n got [%s]\nwant [%s]", input, s, expected)
}
}
@@ -934,15 +1020,20 @@ func TestRefUnmarshal(t *testing.T) {
// Ref is defined in encode_test.go.
R0 Ref
R1 *Ref
+ R2 RefText
+ R3 *RefText
}
want := S{
R0: 12,
R1: new(Ref),
+ R2: 13,
+ R3: new(RefText),
}
*want.R1 = 12
+ *want.R3 = 13
var got S
- if err := Unmarshal([]byte(`{"R0":"ref","R1":"ref"}`), &got); err != nil {
+ if err := Unmarshal([]byte(`{"R0":"ref","R1":"ref","R2":"ref","R3":"ref"}`), &got); err != nil {
t.Fatalf("Unmarshal: %v", err)
}
if !reflect.DeepEqual(got, want) {
@@ -1064,7 +1155,6 @@ func TestUnmarshalNulls(t *testing.T) {
func TestStringKind(t *testing.T) {
type stringKind string
- type aMap map[stringKind]int
var m1, m2 map[stringKind]int
m1 = map[stringKind]int{
@@ -1191,3 +1281,38 @@ func TestSkipArrayObjects(t *testing.T) {
t.Errorf("got error %q, want nil", err)
}
}
+
+// Test semantics of pre-filled struct fields and pre-filled map fields.
+// Issue 4900.
+func TestPrefilled(t *testing.T) {
+ ptrToMap := func(m map[string]interface{}) *map[string]interface{} { return &m }
+
+ // Values here change, cannot reuse table across runs.
+ var prefillTests = []struct {
+ in string
+ ptr interface{}
+ out interface{}
+ }{
+ {
+ in: `{"X": 1, "Y": 2}`,
+ ptr: &XYZ{X: float32(3), Y: int16(4), Z: 1.5},
+ out: &XYZ{X: float64(1), Y: float64(2), Z: 1.5},
+ },
+ {
+ in: `{"X": 1, "Y": 2}`,
+ ptr: ptrToMap(map[string]interface{}{"X": float32(3), "Y": int16(4), "Z": 1.5}),
+ out: ptrToMap(map[string]interface{}{"X": float64(1), "Y": float64(2), "Z": 1.5}),
+ },
+ }
+
+ for _, tt := range prefillTests {
+ ptrstr := fmt.Sprintf("%v", tt.ptr)
+ err := Unmarshal([]byte(tt.in), tt.ptr) // tt.ptr edited here
+ if err != nil {
+ t.Errorf("Unmarshal: %v", err)
+ }
+ if !reflect.DeepEqual(tt.ptr, tt.out) {
+ t.Errorf("Unmarshal(%#q, %s): have %v, want %v", tt.in, ptrstr, tt.ptr, tt.out)
+ }
+ }
+}
diff --git a/src/pkg/encoding/json/encode.go b/src/pkg/encoding/json/encode.go
index 85727ba61..7d6c71d7a 100644
--- a/src/pkg/encoding/json/encode.go
+++ b/src/pkg/encoding/json/encode.go
@@ -12,6 +12,7 @@ package json
import (
"bytes"
+ "encoding"
"encoding/base64"
"math"
"reflect"
@@ -149,14 +150,14 @@ func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
return buf.Bytes(), nil
}
-// HTMLEscape appends to dst the JSON-encoded src with <, >, and &
-// characters inside string literals changed to \u003c, \u003e, \u0026
+// HTMLEscape appends to dst the JSON-encoded src with <, >, &, U+2028 and U+2029
+// characters inside string literals changed to \u003c, \u003e, \u0026, \u2028, \u2029
// 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,
+ // The characters can only appear in string literals,
// so just scan the string one byte at a time.
start := 0
for i, c := range src {
@@ -169,6 +170,15 @@ func HTMLEscape(dst *bytes.Buffer, src []byte) {
dst.WriteByte(hex[c&0xF])
start = i + 1
}
+ // Convert U+2028 and U+2029 (E2 80 A8 and E2 80 A9).
+ if c == 0xE2 && i+2 < len(src) && src[i+1] == 0x80 && src[i+2]&^1 == 0xA8 {
+ if start < i {
+ dst.Write(src[start:i])
+ }
+ dst.WriteString(`\u202`)
+ dst.WriteByte(hex[src[i+2]&0xF])
+ start = i + 3
+ }
}
if start < len(src) {
dst.Write(src[start:])
@@ -200,8 +210,12 @@ func (e *UnsupportedValueError) Error() string {
return "json: unsupported value: " + e.Str
}
-// An InvalidUTF8Error is returned by Marshal when attempting
-// to encode a string value with invalid UTF-8 sequences.
+// Before Go 1.2, an InvalidUTF8Error was returned by Marshal when
+// attempting to encode a string value with invalid UTF-8 sequences.
+// As of Go 1.2, Marshal instead coerces the string to valid UTF-8 by
+// replacing invalid bytes with the Unicode replacement rune U+FFFD.
+// This error is no longer generated but is kept for backwards compatibility
+// with programs that might mention it.
type InvalidUTF8Error struct {
S string // the whole string value that caused the error
}
@@ -227,12 +241,35 @@ type encodeState struct {
scratch [64]byte
}
+// TODO(bradfitz): use a sync.Cache here
+var encodeStatePool = make(chan *encodeState, 8)
+
+func newEncodeState() *encodeState {
+ select {
+ case e := <-encodeStatePool:
+ e.Reset()
+ return e
+ default:
+ return new(encodeState)
+ }
+}
+
+func putEncodeState(e *encodeState) {
+ select {
+ case encodeStatePool <- e:
+ default:
+ }
+}
+
func (e *encodeState) marshal(v interface{}) (err error) {
defer func() {
if r := recover(); r != nil {
if _, ok := r.(runtime.Error); ok {
panic(r)
}
+ if s, ok := r.(string); ok {
+ panic(s)
+ }
err = r.(error)
}
}()
@@ -265,186 +302,438 @@ func isEmptyValue(v reflect.Value) bool {
}
func (e *encodeState) reflectValue(v reflect.Value) {
- e.reflectValueQuoted(v, false)
+ valueEncoder(v)(e, v, false)
+}
+
+type encoderFunc func(e *encodeState, v reflect.Value, quoted bool)
+
+var encoderCache struct {
+ sync.RWMutex
+ m map[reflect.Type]encoderFunc
}
-// reflectValueQuoted writes the value in v to the output.
-// If quoted is true, the serialization is wrapped in a JSON string.
-func (e *encodeState) reflectValueQuoted(v reflect.Value, quoted bool) {
+func valueEncoder(v reflect.Value) encoderFunc {
if !v.IsValid() {
- e.WriteString("null")
- return
+ return invalidValueEncoder
}
+ return typeEncoder(v.Type())
+}
- m, ok := v.Interface().(Marshaler)
- if !ok {
- // T doesn't match the interface. Check against *T too.
- if v.Kind() != reflect.Ptr && v.CanAddr() {
- m, ok = v.Addr().Interface().(Marshaler)
- if ok {
- v = v.Addr()
- }
- }
+func typeEncoder(t reflect.Type) encoderFunc {
+ encoderCache.RLock()
+ f := encoderCache.m[t]
+ encoderCache.RUnlock()
+ if f != nil {
+ return f
+ }
+
+ // To deal with recursive types, populate the map with an
+ // indirect func before we build it. This type waits on the
+ // real func (f) to be ready and then calls it. This indirect
+ // func is only used for recursive types.
+ encoderCache.Lock()
+ if encoderCache.m == nil {
+ encoderCache.m = make(map[reflect.Type]encoderFunc)
+ }
+ var wg sync.WaitGroup
+ wg.Add(1)
+ encoderCache.m[t] = func(e *encodeState, v reflect.Value, quoted bool) {
+ wg.Wait()
+ f(e, v, quoted)
+ }
+ encoderCache.Unlock()
+
+ // Compute fields without lock.
+ // Might duplicate effort but won't hold other computations back.
+ f = newTypeEncoder(t, true)
+ wg.Done()
+ encoderCache.Lock()
+ encoderCache.m[t] = f
+ encoderCache.Unlock()
+ return f
+}
+
+var (
+ marshalerType = reflect.TypeOf(new(Marshaler)).Elem()
+ textMarshalerType = reflect.TypeOf(new(encoding.TextMarshaler)).Elem()
+)
+
+// newTypeEncoder constructs an encoderFunc for a type.
+// The returned encoder only checks CanAddr when allowAddr is true.
+func newTypeEncoder(t reflect.Type, allowAddr bool) encoderFunc {
+ if t.Implements(marshalerType) {
+ return marshalerEncoder
}
- if ok && (v.Kind() != reflect.Ptr || !v.IsNil()) {
- b, err := m.MarshalJSON()
- if err == nil {
- // copy JSON into buffer, checking validity.
- err = compact(&e.Buffer, b, true)
+ if t.Kind() != reflect.Ptr && allowAddr {
+ if reflect.PtrTo(t).Implements(marshalerType) {
+ return newCondAddrEncoder(addrMarshalerEncoder, newTypeEncoder(t, false))
}
- if err != nil {
- e.error(&MarshalerError{v.Type(), err})
+ }
+
+ if t.Implements(textMarshalerType) {
+ return textMarshalerEncoder
+ }
+ if t.Kind() != reflect.Ptr && allowAddr {
+ if reflect.PtrTo(t).Implements(textMarshalerType) {
+ return newCondAddrEncoder(addrTextMarshalerEncoder, newTypeEncoder(t, false))
}
+ }
+
+ switch t.Kind() {
+ case reflect.Bool:
+ return boolEncoder
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return intEncoder
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ return uintEncoder
+ case reflect.Float32:
+ return float32Encoder
+ case reflect.Float64:
+ return float64Encoder
+ case reflect.String:
+ return stringEncoder
+ case reflect.Interface:
+ return interfaceEncoder
+ case reflect.Struct:
+ return newStructEncoder(t)
+ case reflect.Map:
+ return newMapEncoder(t)
+ case reflect.Slice:
+ return newSliceEncoder(t)
+ case reflect.Array:
+ return newArrayEncoder(t)
+ case reflect.Ptr:
+ return newPtrEncoder(t)
+ default:
+ return unsupportedTypeEncoder
+ }
+}
+
+func invalidValueEncoder(e *encodeState, v reflect.Value, quoted bool) {
+ e.WriteString("null")
+}
+
+func marshalerEncoder(e *encodeState, v reflect.Value, quoted bool) {
+ if v.Kind() == reflect.Ptr && v.IsNil() {
+ e.WriteString("null")
+ return
+ }
+ m := v.Interface().(Marshaler)
+ b, err := m.MarshalJSON()
+ if err == nil {
+ // copy JSON into buffer, checking validity.
+ err = compact(&e.Buffer, b, true)
+ }
+ if err != nil {
+ e.error(&MarshalerError{v.Type(), err})
+ }
+}
+
+func addrMarshalerEncoder(e *encodeState, v reflect.Value, quoted bool) {
+ va := v.Addr()
+ if va.IsNil() {
+ e.WriteString("null")
return
}
+ m := va.Interface().(Marshaler)
+ b, err := m.MarshalJSON()
+ if err == nil {
+ // copy JSON into buffer, checking validity.
+ err = compact(&e.Buffer, b, true)
+ }
+ if err != nil {
+ e.error(&MarshalerError{v.Type(), err})
+ }
+}
- writeString := (*encodeState).WriteString
+func textMarshalerEncoder(e *encodeState, v reflect.Value, quoted bool) {
+ if v.Kind() == reflect.Ptr && v.IsNil() {
+ e.WriteString("null")
+ return
+ }
+ m := v.Interface().(encoding.TextMarshaler)
+ b, err := m.MarshalText()
+ if err == nil {
+ _, err = e.stringBytes(b)
+ }
+ if err != nil {
+ e.error(&MarshalerError{v.Type(), err})
+ }
+}
+
+func addrTextMarshalerEncoder(e *encodeState, v reflect.Value, quoted bool) {
+ va := v.Addr()
+ if va.IsNil() {
+ e.WriteString("null")
+ return
+ }
+ m := va.Interface().(encoding.TextMarshaler)
+ b, err := m.MarshalText()
+ if err == nil {
+ _, err = e.stringBytes(b)
+ }
+ if err != nil {
+ e.error(&MarshalerError{v.Type(), err})
+ }
+}
+
+func boolEncoder(e *encodeState, v reflect.Value, quoted bool) {
+ if quoted {
+ e.WriteByte('"')
+ }
+ if v.Bool() {
+ e.WriteString("true")
+ } else {
+ e.WriteString("false")
+ }
+ if quoted {
+ e.WriteByte('"')
+ }
+}
+
+func intEncoder(e *encodeState, v reflect.Value, quoted bool) {
+ b := strconv.AppendInt(e.scratch[:0], v.Int(), 10)
+ if quoted {
+ e.WriteByte('"')
+ }
+ e.Write(b)
if quoted {
- writeString = (*encodeState).string
+ e.WriteByte('"')
}
+}
- switch v.Kind() {
- case reflect.Bool:
- x := v.Bool()
- if x {
- writeString(e, "true")
- } else {
- writeString(e, "false")
- }
+func uintEncoder(e *encodeState, v reflect.Value, quoted bool) {
+ b := strconv.AppendUint(e.scratch[:0], v.Uint(), 10)
+ if quoted {
+ e.WriteByte('"')
+ }
+ e.Write(b)
+ if quoted {
+ e.WriteByte('"')
+ }
+}
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- b := strconv.AppendInt(e.scratch[:0], v.Int(), 10)
- if quoted {
- writeString(e, string(b))
- } else {
- e.Write(b)
+type floatEncoder int // number of bits
+
+func (bits floatEncoder) encode(e *encodeState, v reflect.Value, quoted bool) {
+ f := v.Float()
+ if math.IsInf(f, 0) || math.IsNaN(f) {
+ e.error(&UnsupportedValueError{v, strconv.FormatFloat(f, 'g', -1, int(bits))})
+ }
+ b := strconv.AppendFloat(e.scratch[:0], f, 'g', -1, int(bits))
+ if quoted {
+ e.WriteByte('"')
+ }
+ e.Write(b)
+ if quoted {
+ e.WriteByte('"')
+ }
+}
+
+var (
+ float32Encoder = (floatEncoder(32)).encode
+ float64Encoder = (floatEncoder(64)).encode
+)
+
+func stringEncoder(e *encodeState, v reflect.Value, quoted bool) {
+ if v.Type() == numberType {
+ numStr := v.String()
+ if numStr == "" {
+ numStr = "0" // Number's zero-val
}
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- b := strconv.AppendUint(e.scratch[:0], v.Uint(), 10)
- if quoted {
- writeString(e, string(b))
- } else {
- e.Write(b)
+ e.WriteString(numStr)
+ return
+ }
+ if quoted {
+ sb, err := Marshal(v.String())
+ if err != nil {
+ e.error(err)
}
- case reflect.Float32, reflect.Float64:
- f := v.Float()
- if math.IsInf(f, 0) || math.IsNaN(f) {
- e.error(&UnsupportedValueError{v, strconv.FormatFloat(f, 'g', -1, v.Type().Bits())})
+ e.string(string(sb))
+ } else {
+ e.string(v.String())
+ }
+}
+
+func interfaceEncoder(e *encodeState, v reflect.Value, quoted bool) {
+ if v.IsNil() {
+ e.WriteString("null")
+ return
+ }
+ e.reflectValue(v.Elem())
+}
+
+func unsupportedTypeEncoder(e *encodeState, v reflect.Value, quoted bool) {
+ e.error(&UnsupportedTypeError{v.Type()})
+}
+
+type structEncoder struct {
+ fields []field
+ fieldEncs []encoderFunc
+}
+
+func (se *structEncoder) encode(e *encodeState, v reflect.Value, quoted bool) {
+ e.WriteByte('{')
+ first := true
+ for i, f := range se.fields {
+ fv := fieldByIndex(v, f.index)
+ if !fv.IsValid() || f.omitEmpty && isEmptyValue(fv) {
+ continue
}
- b := strconv.AppendFloat(e.scratch[:0], f, 'g', -1, v.Type().Bits())
- if quoted {
- writeString(e, string(b))
+ if first {
+ first = false
} else {
- e.Write(b)
- }
- case reflect.String:
- if v.Type() == numberType {
- numStr := v.String()
- if numStr == "" {
- numStr = "0" // Number's zero-val
- }
- e.WriteString(numStr)
- break
- }
- if quoted {
- sb, err := Marshal(v.String())
- if err != nil {
- e.error(err)
- }
- e.string(string(sb))
- } else {
- e.string(v.String())
+ e.WriteByte(',')
}
+ e.string(f.name)
+ e.WriteByte(':')
+ se.fieldEncs[i](e, fv, f.quoted)
+ }
+ e.WriteByte('}')
+}
- case reflect.Struct:
- e.WriteByte('{')
- first := true
- for _, f := range cachedTypeFields(v.Type()) {
- fv := fieldByIndex(v, f.index)
- if !fv.IsValid() || f.omitEmpty && isEmptyValue(fv) {
- continue
- }
- if first {
- first = false
- } else {
- e.WriteByte(',')
- }
- e.string(f.name)
- e.WriteByte(':')
- e.reflectValueQuoted(fv, f.quoted)
- }
- e.WriteByte('}')
+func newStructEncoder(t reflect.Type) encoderFunc {
+ fields := cachedTypeFields(t)
+ se := &structEncoder{
+ fields: fields,
+ fieldEncs: make([]encoderFunc, len(fields)),
+ }
+ for i, f := range fields {
+ se.fieldEncs[i] = typeEncoder(typeByIndex(t, f.index))
+ }
+ return se.encode
+}
- 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('}')
+type mapEncoder struct {
+ elemEnc encoderFunc
+}
- case reflect.Slice:
- if v.IsNil() {
- e.WriteString("null")
- break
- }
- if v.Type().Elem().Kind() == reflect.Uint8 {
- // Byte slices get special treatment; arrays don't.
- s := v.Bytes()
- e.WriteByte('"')
- 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
- }
- // Slices can be marshalled as nil, but otherwise are handled
- // as arrays.
- fallthrough
- case reflect.Array:
- e.WriteByte('[')
- n := v.Len()
- for i := 0; i < n; i++ {
- if i > 0 {
- e.WriteByte(',')
- }
- e.reflectValue(v.Index(i))
+func (me *mapEncoder) encode(e *encodeState, v reflect.Value, _ bool) {
+ if v.IsNil() {
+ e.WriteString("null")
+ return
+ }
+ e.WriteByte('{')
+ var sv stringValues = v.MapKeys()
+ sort.Sort(sv)
+ for i, k := range sv {
+ if i > 0 {
+ e.WriteByte(',')
}
- e.WriteByte(']')
+ e.string(k.String())
+ e.WriteByte(':')
+ me.elemEnc(e, v.MapIndex(k), false)
+ }
+ e.WriteByte('}')
+}
- case reflect.Interface, reflect.Ptr:
- if v.IsNil() {
- e.WriteString("null")
- return
+func newMapEncoder(t reflect.Type) encoderFunc {
+ if t.Key().Kind() != reflect.String {
+ return unsupportedTypeEncoder
+ }
+ me := &mapEncoder{typeEncoder(t.Elem())}
+ return me.encode
+}
+
+func encodeByteSlice(e *encodeState, v reflect.Value, _ bool) {
+ if v.IsNil() {
+ e.WriteString("null")
+ return
+ }
+ s := v.Bytes()
+ e.WriteByte('"')
+ 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('"')
+}
+
+// sliceEncoder just wraps an arrayEncoder, checking to make sure the value isn't nil.
+type sliceEncoder struct {
+ arrayEnc encoderFunc
+}
+
+func (se *sliceEncoder) encode(e *encodeState, v reflect.Value, _ bool) {
+ if v.IsNil() {
+ e.WriteString("null")
+ return
+ }
+ se.arrayEnc(e, v, false)
+}
+
+func newSliceEncoder(t reflect.Type) encoderFunc {
+ // Byte slices get special treatment; arrays don't.
+ if t.Elem().Kind() == reflect.Uint8 {
+ return encodeByteSlice
+ }
+ enc := &sliceEncoder{newArrayEncoder(t)}
+ return enc.encode
+}
+
+type arrayEncoder struct {
+ elemEnc encoderFunc
+}
+
+func (ae *arrayEncoder) encode(e *encodeState, v reflect.Value, _ bool) {
+ e.WriteByte('[')
+ n := v.Len()
+ for i := 0; i < n; i++ {
+ if i > 0 {
+ e.WriteByte(',')
}
- e.reflectValue(v.Elem())
+ ae.elemEnc(e, v.Index(i), false)
+ }
+ e.WriteByte(']')
+}
- default:
- e.error(&UnsupportedTypeError{v.Type()})
+func newArrayEncoder(t reflect.Type) encoderFunc {
+ enc := &arrayEncoder{typeEncoder(t.Elem())}
+ return enc.encode
+}
+
+type ptrEncoder struct {
+ elemEnc encoderFunc
+}
+
+func (pe *ptrEncoder) encode(e *encodeState, v reflect.Value, _ bool) {
+ if v.IsNil() {
+ e.WriteString("null")
+ return
+ }
+ pe.elemEnc(e, v.Elem(), false)
+}
+
+func newPtrEncoder(t reflect.Type) encoderFunc {
+ enc := &ptrEncoder{typeEncoder(t.Elem())}
+ return enc.encode
+}
+
+type condAddrEncoder struct {
+ canAddrEnc, elseEnc encoderFunc
+}
+
+func (ce *condAddrEncoder) encode(e *encodeState, v reflect.Value, quoted bool) {
+ if v.CanAddr() {
+ ce.canAddrEnc(e, v, quoted)
+ } else {
+ ce.elseEnc(e, v, quoted)
}
- return
+}
+
+// newCondAddrEncoder returns an encoder that checks whether its value
+// CanAddr and delegates to canAddrEnc if so, else to elseEnc.
+func newCondAddrEncoder(canAddrEnc, elseEnc encoderFunc) encoderFunc {
+ enc := &condAddrEncoder{canAddrEnc: canAddrEnc, elseEnc: elseEnc}
+ return enc.encode
}
func isValidTag(s string) bool {
@@ -479,6 +768,16 @@ func fieldByIndex(v reflect.Value, index []int) reflect.Value {
return v
}
+func typeByIndex(t reflect.Type, index []int) reflect.Type {
+ for _, i := range index {
+ if t.Kind() == reflect.Ptr {
+ t = t.Elem()
+ }
+ t = t.Field(i).Type
+ }
+ return t
+}
+
// stringValues is a slice of reflect.Value holding *reflect.StringValue.
// It implements the methods to sort by string.
type stringValues []reflect.Value
@@ -488,13 +787,14 @@ 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() }
+// NOTE: keep in sync with stringBytes below.
func (e *encodeState) string(s string) (int, error) {
len0 := e.Len()
e.WriteByte('"')
start := 0
for i := 0; i < len(s); {
if b := s[i]; b < utf8.RuneSelf {
- if 0x20 <= b && b != '\\' && b != '"' && b != '<' && b != '>' {
+ if 0x20 <= b && b != '\\' && b != '"' && b != '<' && b != '>' && b != '&' {
i++
continue
}
@@ -526,7 +826,30 @@ func (e *encodeState) string(s string) (int, error) {
}
c, size := utf8.DecodeRuneInString(s[i:])
if c == utf8.RuneError && size == 1 {
- e.error(&InvalidUTF8Error{s})
+ if start < i {
+ e.WriteString(s[start:i])
+ }
+ e.WriteString(`\ufffd`)
+ i += size
+ start = i
+ continue
+ }
+ // U+2028 is LINE SEPARATOR.
+ // U+2029 is PARAGRAPH SEPARATOR.
+ // They are both technically valid characters in JSON strings,
+ // but don't work in JSONP, which has to be evaluated as JavaScript,
+ // and can lead to security holes there. It is valid JSON to
+ // escape them, so we do so unconditionally.
+ // See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion.
+ if c == '\u2028' || c == '\u2029' {
+ if start < i {
+ e.WriteString(s[start:i])
+ }
+ e.WriteString(`\u202`)
+ e.WriteByte(hex[c&0xF])
+ i += size
+ start = i
+ continue
}
i += size
}
@@ -537,6 +860,79 @@ func (e *encodeState) string(s string) (int, error) {
return e.Len() - len0, nil
}
+// NOTE: keep in sync with string above.
+func (e *encodeState) stringBytes(s []byte) (int, error) {
+ len0 := e.Len()
+ e.WriteByte('"')
+ start := 0
+ for i := 0; i < len(s); {
+ if b := s[i]; b < utf8.RuneSelf {
+ if 0x20 <= b && b != '\\' && b != '"' && b != '<' && b != '>' && b != '&' {
+ i++
+ continue
+ }
+ if start < i {
+ e.Write(s[start:i])
+ }
+ switch b {
+ case '\\', '"':
+ e.WriteByte('\\')
+ e.WriteByte(b)
+ case '\n':
+ e.WriteByte('\\')
+ e.WriteByte('n')
+ case '\r':
+ e.WriteByte('\\')
+ e.WriteByte('r')
+ default:
+ // This encodes bytes < 0x20 except for \n and \r,
+ // as well as < and >. The latter are escaped because they
+ // can lead to security holes when user-controlled strings
+ // are rendered into JSON and served to some browsers.
+ e.WriteString(`\u00`)
+ e.WriteByte(hex[b>>4])
+ e.WriteByte(hex[b&0xF])
+ }
+ i++
+ start = i
+ continue
+ }
+ c, size := utf8.DecodeRune(s[i:])
+ if c == utf8.RuneError && size == 1 {
+ if start < i {
+ e.Write(s[start:i])
+ }
+ e.WriteString(`\ufffd`)
+ i += size
+ start = i
+ continue
+ }
+ // U+2028 is LINE SEPARATOR.
+ // U+2029 is PARAGRAPH SEPARATOR.
+ // They are both technically valid characters in JSON strings,
+ // but don't work in JSONP, which has to be evaluated as JavaScript,
+ // and can lead to security holes there. It is valid JSON to
+ // escape them, so we do so unconditionally.
+ // See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion.
+ if c == '\u2028' || c == '\u2029' {
+ if start < i {
+ e.Write(s[start:i])
+ }
+ e.WriteString(`\u202`)
+ e.WriteByte(hex[c&0xF])
+ i += size
+ start = i
+ continue
+ }
+ i += size
+ }
+ if start < len(s) {
+ e.Write(s[start:])
+ }
+ e.WriteByte('"')
+ return e.Len() - len0, nil
+}
+
// A field represents a single field found in a struct.
type field struct {
name string
diff --git a/src/pkg/encoding/json/encode_test.go b/src/pkg/encoding/json/encode_test.go
index 5be0a992e..9395db7cb 100644
--- a/src/pkg/encoding/json/encode_test.go
+++ b/src/pkg/encoding/json/encode_test.go
@@ -9,6 +9,7 @@ import (
"math"
"reflect"
"testing"
+ "unicode"
)
type Optionals struct {
@@ -146,19 +147,46 @@ func (Val) MarshalJSON() ([]byte, error) {
return []byte(`"val"`), nil
}
+// RefText has Marshaler and Unmarshaler methods with pointer receiver.
+type RefText int
+
+func (*RefText) MarshalText() ([]byte, error) {
+ return []byte(`"ref"`), nil
+}
+
+func (r *RefText) UnmarshalText([]byte) error {
+ *r = 13
+ return nil
+}
+
+// ValText has Marshaler methods with value receiver.
+type ValText int
+
+func (ValText) MarshalText() ([]byte, error) {
+ return []byte(`"val"`), nil
+}
+
func TestRefValMarshal(t *testing.T) {
var s = struct {
R0 Ref
R1 *Ref
+ R2 RefText
+ R3 *RefText
V0 Val
V1 *Val
+ V2 ValText
+ V3 *ValText
}{
R0: 12,
R1: new(Ref),
+ R2: 14,
+ R3: new(RefText),
V0: 13,
V1: new(Val),
+ V2: 15,
+ V3: new(ValText),
}
- const want = `{"R0":"ref","R1":"ref","V0":"val","V1":"val"}`
+ const want = `{"R0":"ref","R1":"ref","R2":"\"ref\"","R3":"\"ref\"","V0":"val","V1":"val","V2":"\"val\"","V3":"\"val\""}`
b, err := Marshal(&s)
if err != nil {
t.Fatalf("Marshal: %v", err)
@@ -175,15 +203,32 @@ func (C) MarshalJSON() ([]byte, error) {
return []byte(`"<&>"`), nil
}
+// CText implements Marshaler and returns unescaped text.
+type CText int
+
+func (CText) MarshalText() ([]byte, error) {
+ return []byte(`"<&>"`), nil
+}
+
func TestMarshalerEscaping(t *testing.T) {
var c C
- const want = `"\u003c\u0026\u003e"`
+ want := `"\u003c\u0026\u003e"`
b, err := Marshal(c)
if err != nil {
- t.Fatalf("Marshal: %v", err)
+ t.Fatalf("Marshal(c): %v", err)
}
if got := string(b); got != want {
- t.Errorf("got %q, want %q", got, want)
+ t.Errorf("Marshal(c) = %#q, want %#q", got, want)
+ }
+
+ var ct CText
+ want = `"\"\u003c\u0026\u003e\""`
+ b, err = Marshal(ct)
+ if err != nil {
+ t.Fatalf("Marshal(ct): %v", err)
+ }
+ if got := string(b); got != want {
+ t.Errorf("Marshal(ct) = %#q, want %#q", got, want)
}
}
@@ -310,3 +355,73 @@ func TestDuplicatedFieldDisappears(t *testing.T) {
t.Fatalf("Marshal: got %s want %s", got, want)
}
}
+
+func TestStringBytes(t *testing.T) {
+ // Test that encodeState.stringBytes and encodeState.string use the same encoding.
+ es := &encodeState{}
+ var r []rune
+ for i := '\u0000'; i <= unicode.MaxRune; i++ {
+ r = append(r, i)
+ }
+ s := string(r) + "\xff\xff\xffhello" // some invalid UTF-8 too
+ _, err := es.string(s)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ esBytes := &encodeState{}
+ _, err = esBytes.stringBytes([]byte(s))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ enc := es.Buffer.String()
+ encBytes := esBytes.Buffer.String()
+ if enc != encBytes {
+ i := 0
+ for i < len(enc) && i < len(encBytes) && enc[i] == encBytes[i] {
+ i++
+ }
+ enc = enc[i:]
+ encBytes = encBytes[i:]
+ i = 0
+ for i < len(enc) && i < len(encBytes) && enc[len(enc)-i-1] == encBytes[len(encBytes)-i-1] {
+ i++
+ }
+ enc = enc[:len(enc)-i]
+ encBytes = encBytes[:len(encBytes)-i]
+
+ if len(enc) > 20 {
+ enc = enc[:20] + "..."
+ }
+ if len(encBytes) > 20 {
+ encBytes = encBytes[:20] + "..."
+ }
+
+ t.Errorf("encodings differ at %#q vs %#q", enc, encBytes)
+ }
+}
+
+func TestIssue6458(t *testing.T) {
+ type Foo struct {
+ M RawMessage
+ }
+ x := Foo{RawMessage(`"foo"`)}
+
+ b, err := Marshal(&x)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if want := `{"M":"foo"}`; string(b) != want {
+ t.Errorf("Marshal(&x) = %#q; want %#q", b, want)
+ }
+
+ b, err = Marshal(x)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if want := `{"M":"ImZvbyI="}`; string(b) != want {
+ t.Errorf("Marshal(x) = %#q; want %#q", b, want)
+ }
+}
diff --git a/src/pkg/encoding/json/example_test.go b/src/pkg/encoding/json/example_test.go
index b8d150eda..ea0bc149c 100644
--- a/src/pkg/encoding/json/example_test.go
+++ b/src/pkg/encoding/json/example_test.go
@@ -81,3 +81,49 @@ func ExampleDecoder() {
// Sam: Go fmt who?
// Ed: Go fmt yourself!
}
+
+// This example uses RawMessage to delay parsing part of a JSON message.
+func ExampleRawMessage() {
+ type Color struct {
+ Space string
+ Point json.RawMessage // delay parsing until we know the color space
+ }
+ type RGB struct {
+ R uint8
+ G uint8
+ B uint8
+ }
+ type YCbCr struct {
+ Y uint8
+ Cb int8
+ Cr int8
+ }
+
+ var j = []byte(`[
+ {"Space": "YCbCr", "Point": {"Y": 255, "Cb": 0, "Cr": -10}},
+ {"Space": "RGB", "Point": {"R": 98, "G": 218, "B": 255}}
+ ]`)
+ var colors []Color
+ err := json.Unmarshal(j, &colors)
+ if err != nil {
+ log.Fatalln("error:", err)
+ }
+
+ for _, c := range colors {
+ var dst interface{}
+ switch c.Space {
+ case "RGB":
+ dst = new(RGB)
+ case "YCbCr":
+ dst = new(YCbCr)
+ }
+ err := json.Unmarshal(c.Point, dst)
+ if err != nil {
+ log.Fatalln("error:", err)
+ }
+ fmt.Println(c.Space, dst)
+ }
+ // Output:
+ // YCbCr &{255 0 -10}
+ // RGB &{98 218 255}
+}
diff --git a/src/pkg/encoding/json/indent.go b/src/pkg/encoding/json/indent.go
index e8dfa4ec4..11ef709cc 100644
--- a/src/pkg/encoding/json/indent.go
+++ b/src/pkg/encoding/json/indent.go
@@ -27,6 +27,15 @@ func compact(dst *bytes.Buffer, src []byte, escape bool) error {
dst.WriteByte(hex[c&0xF])
start = i + 1
}
+ // Convert U+2028 and U+2029 (E2 80 A8 and E2 80 A9).
+ if c == 0xE2 && i+2 < len(src) && src[i+1] == 0x80 && src[i+2]&^1 == 0xA8 {
+ if start < i {
+ dst.Write(src[start:i])
+ }
+ dst.WriteString(`\u202`)
+ dst.WriteByte(hex[src[i+2]&0xF])
+ start = i + 3
+ }
v := scan.step(&scan, int(c))
if v >= scanSkipSpace {
if v == scanError {
diff --git a/src/pkg/encoding/json/scanner.go b/src/pkg/encoding/json/scanner.go
index 054b6b3d5..a4609c895 100644
--- a/src/pkg/encoding/json/scanner.go
+++ b/src/pkg/encoding/json/scanner.go
@@ -390,7 +390,7 @@ func stateInStringEscU123(s *scanner, c int) int {
return s.error(c, "in \\u hexadecimal character escape")
}
-// stateInStringEscU123 is the state after reading `-` during a number.
+// stateNeg is the state after reading `-` during a number.
func stateNeg(s *scanner, c int) int {
if c == '0' {
s.step = state0
diff --git a/src/pkg/encoding/json/scanner_test.go b/src/pkg/encoding/json/scanner_test.go
index 77d3455d3..90e45ff03 100644
--- a/src/pkg/encoding/json/scanner_test.go
+++ b/src/pkg/encoding/json/scanner_test.go
@@ -63,6 +63,25 @@ func TestCompact(t *testing.T) {
}
}
+func TestCompactSeparators(t *testing.T) {
+ // U+2028 and U+2029 should be escaped inside strings.
+ // They should not appear outside strings.
+ tests := []struct {
+ in, compact string
+ }{
+ {"{\"\u2028\": 1}", `{"\u2028":1}`},
+ {"{\"\u2029\" :2}", `{"\u2029":2}`},
+ }
+ for _, tt := range tests {
+ var buf bytes.Buffer
+ if err := Compact(&buf, []byte(tt.in)); err != nil {
+ t.Errorf("Compact(%q): %v", tt.in, err)
+ } else if s := buf.String(); s != tt.compact {
+ t.Errorf("Compact(%q) = %q, want %q", tt.in, s, tt.compact)
+ }
+ }
+}
+
func TestIndent(t *testing.T) {
var buf bytes.Buffer
for _, tt := range examples {
diff --git a/src/pkg/encoding/json/stream.go b/src/pkg/encoding/json/stream.go
index 00f4726cf..1928abadb 100644
--- a/src/pkg/encoding/json/stream.go
+++ b/src/pkg/encoding/json/stream.go
@@ -148,7 +148,7 @@ func NewEncoder(w io.Writer) *Encoder {
return &Encoder{w: w}
}
-// Encode writes the JSON encoding of v to the connection.
+// Encode writes the JSON encoding of v to the stream.
//
// See the documentation for Marshal for details about the
// conversion of Go values to JSON.
@@ -156,8 +156,8 @@ func (enc *Encoder) Encode(v interface{}) error {
if enc.err != nil {
return enc.err
}
- enc.e.Reset()
- err := enc.e.marshal(v)
+ e := newEncodeState()
+ err := e.marshal(v)
if err != nil {
return err
}
@@ -168,11 +168,12 @@ func (enc *Encoder) Encode(v interface{}) error {
// is required if the encoded value was a number,
// so that the reader knows there aren't more
// digits coming.
- enc.e.WriteByte('\n')
+ e.WriteByte('\n')
- if _, err = enc.w.Write(enc.e.Bytes()); err != nil {
+ if _, err = enc.w.Write(e.Bytes()); err != nil {
enc.err = err
}
+ putEncodeState(e)
return err
}
diff --git a/src/pkg/encoding/json/stream_test.go b/src/pkg/encoding/json/stream_test.go
index 07c9e1d39..b562e8769 100644
--- a/src/pkg/encoding/json/stream_test.go
+++ b/src/pkg/encoding/json/stream_test.go
@@ -191,3 +191,16 @@ func TestBlocking(t *testing.T) {
w.Close()
}
}
+
+func BenchmarkEncoderEncode(b *testing.B) {
+ b.ReportAllocs()
+ type T struct {
+ X, Y string
+ }
+ v := &T{"foo", "bar"}
+ for i := 0; i < b.N; i++ {
+ if err := NewEncoder(ioutil.Discard).Encode(v); err != nil {
+ b.Fatal(err)
+ }
+ }
+}
diff --git a/src/pkg/encoding/json/tags.go b/src/pkg/encoding/json/tags.go
index 58cda2027..c38fd5102 100644
--- a/src/pkg/encoding/json/tags.go
+++ b/src/pkg/encoding/json/tags.go
@@ -21,7 +21,7 @@ func parseTag(tag string) (string, tagOptions) {
return tag, tagOptions("")
}
-// Contains returns whether checks that a comma-separated list of options
+// Contains reports whether a comma-separated list of options
// contains a particular substr flag. substr must be surrounded by a
// string boundary or commas.
func (o tagOptions) Contains(optionName string) bool {
diff --git a/src/pkg/encoding/xml/marshal.go b/src/pkg/encoding/xml/marshal.go
index 47b001763..d9522e0b3 100644
--- a/src/pkg/encoding/xml/marshal.go
+++ b/src/pkg/encoding/xml/marshal.go
@@ -7,12 +7,12 @@ package xml
import (
"bufio"
"bytes"
+ "encoding"
"fmt"
"io"
"reflect"
"strconv"
"strings"
- "time"
)
const (
@@ -75,6 +75,41 @@ func Marshal(v interface{}) ([]byte, error) {
return b.Bytes(), nil
}
+// Marshaler is the interface implemented by objects that can marshal
+// themselves into valid XML elements.
+//
+// MarshalXML encodes the receiver as zero or more XML elements.
+// By convention, arrays or slices are typically encoded as a sequence
+// of elements, one per entry.
+// Using start as the element tag is not required, but doing so
+// will enable Unmarshal to match the XML elements to the correct
+// struct field.
+// One common implementation strategy is to construct a separate
+// value with a layout corresponding to the desired XML and then
+// to encode it using e.EncodeElement.
+// Another common strategy is to use repeated calls to e.EncodeToken
+// to generate the XML output one token at a time.
+// The sequence of encoded tokens must make up zero or more valid
+// XML elements.
+type Marshaler interface {
+ MarshalXML(e *Encoder, start StartElement) error
+}
+
+// MarshalerAttr is the interface implemented by objects that can marshal
+// themselves into valid XML attributes.
+//
+// MarshalXMLAttr returns an XML attribute with the encoded value of the receiver.
+// Using name as the attribute name is not required, but doing so
+// will enable Unmarshal to match the attribute to the correct
+// struct field.
+// If MarshalXMLAttr returns the zero attribute Attr{}, no attribute
+// will be generated in the output.
+// MarshalXMLAttr is used only for struct fields with the
+// "attr" option in the field tag.
+type MarshalerAttr interface {
+ MarshalXMLAttr(name Name) (Attr, error)
+}
+
// MarshalIndent works like Marshal, but each XML element begins on a new
// indented line that starts with prefix and is followed by one or more
// copies of indent according to the nesting depth.
@@ -90,36 +125,124 @@ func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
// An Encoder writes XML data to an output stream.
type Encoder struct {
- printer
+ p printer
}
// NewEncoder returns a new encoder that writes to w.
func NewEncoder(w io.Writer) *Encoder {
- return &Encoder{printer{Writer: bufio.NewWriter(w)}}
+ e := &Encoder{printer{Writer: bufio.NewWriter(w)}}
+ e.p.encoder = e
+ return e
}
// Indent sets the encoder to generate XML in which each element
// begins on a new indented line that starts with prefix and is followed by
// one or more copies of indent according to the nesting depth.
func (enc *Encoder) Indent(prefix, indent string) {
- enc.prefix = prefix
- enc.indent = indent
+ enc.p.prefix = prefix
+ enc.p.indent = indent
}
// Encode writes the XML encoding of v to the stream.
//
// See the documentation for Marshal for details about the conversion
// of Go values to XML.
+//
+// Encode calls Flush before returning.
func (enc *Encoder) Encode(v interface{}) error {
- err := enc.marshalValue(reflect.ValueOf(v), nil)
+ err := enc.p.marshalValue(reflect.ValueOf(v), nil, nil)
if err != nil {
return err
}
- return enc.Flush()
+ return enc.p.Flush()
+}
+
+// EncodeElement writes the XML encoding of v to the stream,
+// using start as the outermost tag in the encoding.
+//
+// See the documentation for Marshal for details about the conversion
+// of Go values to XML.
+//
+// EncodeElement calls Flush before returning.
+func (enc *Encoder) EncodeElement(v interface{}, start StartElement) error {
+ err := enc.p.marshalValue(reflect.ValueOf(v), nil, &start)
+ if err != nil {
+ return err
+ }
+ return enc.p.Flush()
+}
+
+var (
+ endComment = []byte("-->")
+ endProcInst = []byte("?>")
+ endDirective = []byte(">")
+)
+
+// EncodeToken writes the given XML token to the stream.
+// It returns an error if StartElement and EndElement tokens are not properly matched.
+//
+// EncodeToken does not call Flush, because usually it is part of a larger operation
+// such as Encode or EncodeElement (or a custom Marshaler's MarshalXML invoked
+// during those), and those will call Flush when finished.
+//
+// Callers that create an Encoder and then invoke EncodeToken directly, without
+// using Encode or EncodeElement, need to call Flush when finished to ensure
+// that the XML is written to the underlying writer.
+func (enc *Encoder) EncodeToken(t Token) error {
+ p := &enc.p
+ switch t := t.(type) {
+ case StartElement:
+ if err := p.writeStart(&t); err != nil {
+ return err
+ }
+ case EndElement:
+ if err := p.writeEnd(t.Name); err != nil {
+ return err
+ }
+ case CharData:
+ EscapeText(p, t)
+ case Comment:
+ if bytes.Contains(t, endComment) {
+ return fmt.Errorf("xml: EncodeToken of Comment containing --> marker")
+ }
+ p.WriteString("<!--")
+ p.Write(t)
+ p.WriteString("-->")
+ return p.cachedWriteError()
+ case ProcInst:
+ if t.Target == "xml" || !isNameString(t.Target) {
+ return fmt.Errorf("xml: EncodeToken of ProcInst with invalid Target")
+ }
+ if bytes.Contains(t.Inst, endProcInst) {
+ return fmt.Errorf("xml: EncodeToken of ProcInst containing ?> marker")
+ }
+ p.WriteString("<?")
+ p.WriteString(t.Target)
+ if len(t.Inst) > 0 {
+ p.WriteByte(' ')
+ p.Write(t.Inst)
+ }
+ p.WriteString("?>")
+ case Directive:
+ if bytes.Contains(t, endDirective) {
+ return fmt.Errorf("xml: EncodeToken of Directive containing > marker")
+ }
+ p.WriteString("<!")
+ p.Write(t)
+ p.WriteString(">")
+ }
+ return p.cachedWriteError()
+}
+
+// Flush flushes any buffered XML to the underlying writer.
+// See the EncodeToken documentation for details about when it is necessary.
+func (enc *Encoder) Flush() error {
+ return enc.p.Flush()
}
type printer struct {
*bufio.Writer
+ encoder *Encoder
seq int
indent string
prefix string
@@ -128,13 +251,15 @@ type printer struct {
putNewline bool
attrNS map[string]string // map prefix -> name space
attrPrefix map[string]string // map name space -> prefix
+ prefixes []string
+ tags []Name
}
// createAttrPrefix finds the name space prefix attribute to use for the given name space,
-// defining a new prefix if necessary. It returns the prefix and whether it is new.
-func (p *printer) createAttrPrefix(url string) (prefix string, isNew bool) {
- if prefix = p.attrPrefix[url]; prefix != "" {
- return prefix, false
+// defining a new prefix if necessary. It returns the prefix.
+func (p *printer) createAttrPrefix(url string) string {
+ if prefix := p.attrPrefix[url]; prefix != "" {
+ return prefix
}
// The "http://www.w3.org/XML/1998/namespace" name space is predefined as "xml"
@@ -142,7 +267,7 @@ func (p *printer) createAttrPrefix(url string) (prefix string, isNew bool) {
// (The "http://www.w3.org/2000/xmlns/" name space is also predefined as "xmlns",
// but users should not be trying to use that one directly - that's our job.)
if url == xmlURL {
- return "xml", false
+ return "xml"
}
// Need to define a new name space.
@@ -153,7 +278,7 @@ func (p *printer) createAttrPrefix(url string) (prefix string, isNew bool) {
// Pick a name. We try to use the final element of the path
// but fall back to _.
- prefix = strings.TrimRight(url, "/")
+ prefix := strings.TrimRight(url, "/")
if i := strings.LastIndex(prefix, "/"); i >= 0 {
prefix = prefix[i+1:]
}
@@ -183,7 +308,9 @@ func (p *printer) createAttrPrefix(url string) (prefix string, isNew bool) {
EscapeText(p, []byte(url))
p.WriteString(`" `)
- return prefix, true
+ p.prefixes = append(p.prefixes, prefix)
+
+ return prefix
}
// deleteAttrPrefix removes an attribute name space prefix.
@@ -192,9 +319,34 @@ func (p *printer) deleteAttrPrefix(prefix string) {
delete(p.attrNS, prefix)
}
+func (p *printer) markPrefix() {
+ p.prefixes = append(p.prefixes, "")
+}
+
+func (p *printer) popPrefix() {
+ for len(p.prefixes) > 0 {
+ prefix := p.prefixes[len(p.prefixes)-1]
+ p.prefixes = p.prefixes[:len(p.prefixes)-1]
+ if prefix == "" {
+ break
+ }
+ p.deleteAttrPrefix(prefix)
+ }
+}
+
+var (
+ marshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem()
+ marshalerAttrType = reflect.TypeOf((*MarshalerAttr)(nil)).Elem()
+ textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
+)
+
// marshalValue writes one or more XML elements representing val.
// If val was obtained from a struct field, finfo must have its details.
-func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error {
+func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplate *StartElement) error {
+ if startTemplate != nil && startTemplate.Name.Local == "" {
+ return fmt.Errorf("xml: EncodeElement of StartElement with missing name")
+ }
+
if !val.IsValid() {
return nil
}
@@ -202,21 +354,45 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error {
return nil
}
+ // Drill into interfaces and pointers.
+ // This can turn into an infinite loop given a cyclic chain,
+ // but it matches the Go 1 behavior.
+ for val.Kind() == reflect.Interface || val.Kind() == reflect.Ptr {
+ if val.IsNil() {
+ return nil
+ }
+ val = val.Elem()
+ }
+
kind := val.Kind()
typ := val.Type()
- // Drill into pointers/interfaces
- if kind == reflect.Ptr || kind == reflect.Interface {
- if val.IsNil() {
- return nil
+ // Check for marshaler.
+ if val.CanInterface() && typ.Implements(marshalerType) {
+ return p.marshalInterface(val.Interface().(Marshaler), defaultStart(typ, finfo, startTemplate))
+ }
+ if val.CanAddr() {
+ pv := val.Addr()
+ if pv.CanInterface() && pv.Type().Implements(marshalerType) {
+ return p.marshalInterface(pv.Interface().(Marshaler), defaultStart(pv.Type(), finfo, startTemplate))
+ }
+ }
+
+ // Check for text marshaler.
+ if val.CanInterface() && typ.Implements(textMarshalerType) {
+ return p.marshalTextInterface(val.Interface().(encoding.TextMarshaler), defaultStart(typ, finfo, startTemplate))
+ }
+ if val.CanAddr() {
+ pv := val.Addr()
+ if pv.CanInterface() && pv.Type().Implements(textMarshalerType) {
+ return p.marshalTextInterface(pv.Interface().(encoding.TextMarshaler), defaultStart(pv.Type(), finfo, startTemplate))
}
- return p.marshalValue(val.Elem(), finfo)
}
// Slices and arrays iterate over the elements. They do not have an enclosing tag.
if (kind == reflect.Slice || kind == reflect.Array) && typ.Elem().Kind() != reflect.Uint8 {
for i, n := 0, val.Len(); i < n; i++ {
- if err := p.marshalValue(val.Index(i), finfo); err != nil {
+ if err := p.marshalValue(val.Index(i), finfo, startTemplate); err != nil {
return err
}
}
@@ -228,40 +404,34 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error {
return err
}
+ // Create start element.
// Precedence for the XML element name is:
+ // 0. startTemplate
// 1. XMLName field in underlying struct;
// 2. field name/tag in the struct field; and
// 3. type name
- var xmlns, name string
- if tinfo.xmlname != nil {
+ var start StartElement
+
+ if startTemplate != nil {
+ start.Name = startTemplate.Name
+ start.Attr = append(start.Attr, startTemplate.Attr...)
+ } else if tinfo.xmlname != nil {
xmlname := tinfo.xmlname
if xmlname.name != "" {
- xmlns, name = xmlname.xmlns, xmlname.name
+ start.Name.Space, start.Name.Local = xmlname.xmlns, xmlname.name
} else if v, ok := xmlname.value(val).Interface().(Name); ok && v.Local != "" {
- xmlns, name = v.Space, v.Local
+ start.Name = v
}
}
- if name == "" && finfo != nil {
- xmlns, name = finfo.xmlns, finfo.name
+ if start.Name.Local == "" && finfo != nil {
+ start.Name.Space, start.Name.Local = finfo.xmlns, finfo.name
}
- if name == "" {
- name = typ.Name()
+ if start.Name.Local == "" {
+ name := typ.Name()
if name == "" {
return &UnsupportedTypeError{typ}
}
- }
-
- p.writeIndent(1)
- p.WriteByte('<')
- p.WriteString(name)
-
- if xmlns != "" {
- p.WriteString(` xmlns="`)
- // TODO: EscapeString, to avoid the allocation.
- if err := EscapeText(p, []byte(xmlns)); err != nil {
- return err
- }
- p.WriteByte('"')
+ start.Name.Local = name
}
// Attributes
@@ -271,67 +441,243 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error {
continue
}
fv := finfo.value(val)
+ name := Name{Space: finfo.xmlns, Local: finfo.name}
+
if finfo.flags&fOmitEmpty != 0 && isEmptyValue(fv) {
continue
}
- p.WriteByte(' ')
- if finfo.xmlns != "" {
- prefix, created := p.createAttrPrefix(finfo.xmlns)
- if created {
- defer p.deleteAttrPrefix(prefix)
+
+ if fv.Kind() == reflect.Interface && fv.IsNil() {
+ continue
+ }
+
+ if fv.CanInterface() && fv.Type().Implements(marshalerAttrType) {
+ attr, err := fv.Interface().(MarshalerAttr).MarshalXMLAttr(name)
+ if err != nil {
+ return err
}
- p.WriteString(prefix)
- p.WriteByte(':')
+ if attr.Name.Local != "" {
+ start.Attr = append(start.Attr, attr)
+ }
+ continue
}
- p.WriteString(finfo.name)
- p.WriteString(`="`)
- if err := p.marshalSimple(fv.Type(), fv); err != nil {
+
+ if fv.CanAddr() {
+ pv := fv.Addr()
+ if pv.CanInterface() && pv.Type().Implements(marshalerAttrType) {
+ attr, err := pv.Interface().(MarshalerAttr).MarshalXMLAttr(name)
+ if err != nil {
+ return err
+ }
+ if attr.Name.Local != "" {
+ start.Attr = append(start.Attr, attr)
+ }
+ continue
+ }
+ }
+
+ if fv.CanInterface() && fv.Type().Implements(textMarshalerType) {
+ text, err := fv.Interface().(encoding.TextMarshaler).MarshalText()
+ if err != nil {
+ return err
+ }
+ start.Attr = append(start.Attr, Attr{name, string(text)})
+ continue
+ }
+
+ if fv.CanAddr() {
+ pv := fv.Addr()
+ if pv.CanInterface() && pv.Type().Implements(textMarshalerType) {
+ text, err := pv.Interface().(encoding.TextMarshaler).MarshalText()
+ if err != nil {
+ return err
+ }
+ start.Attr = append(start.Attr, Attr{name, string(text)})
+ continue
+ }
+ }
+
+ // Dereference or skip nil pointer, interface values.
+ switch fv.Kind() {
+ case reflect.Ptr, reflect.Interface:
+ if fv.IsNil() {
+ continue
+ }
+ fv = fv.Elem()
+ }
+
+ s, b, err := p.marshalSimple(fv.Type(), fv)
+ if err != nil {
return err
}
- p.WriteByte('"')
+ if b != nil {
+ s = string(b)
+ }
+ start.Attr = append(start.Attr, Attr{name, s})
+ }
+
+ if err := p.writeStart(&start); err != nil {
+ return err
}
- p.WriteByte('>')
if val.Kind() == reflect.Struct {
err = p.marshalStruct(tinfo, val)
} else {
- err = p.marshalSimple(typ, val)
+ s, b, err1 := p.marshalSimple(typ, val)
+ if err1 != nil {
+ err = err1
+ } else if b != nil {
+ EscapeText(p, b)
+ } else {
+ p.EscapeString(s)
+ }
}
if err != nil {
return err
}
- p.writeIndent(-1)
- p.WriteByte('<')
- p.WriteByte('/')
- p.WriteString(name)
- p.WriteByte('>')
+ if err := p.writeEnd(start.Name); err != nil {
+ return err
+ }
return p.cachedWriteError()
}
-var timeType = reflect.TypeOf(time.Time{})
+// defaultStart returns the default start element to use,
+// given the reflect type, field info, and start template.
+func defaultStart(typ reflect.Type, finfo *fieldInfo, startTemplate *StartElement) StartElement {
+ var start StartElement
+ // Precedence for the XML element name is as above,
+ // except that we do not look inside structs for the first field.
+ if startTemplate != nil {
+ start.Name = startTemplate.Name
+ start.Attr = append(start.Attr, startTemplate.Attr...)
+ } else if finfo != nil && finfo.name != "" {
+ start.Name.Local = finfo.name
+ start.Name.Space = finfo.xmlns
+ } else if typ.Name() != "" {
+ start.Name.Local = typ.Name()
+ } else {
+ // Must be a pointer to a named type,
+ // since it has the Marshaler methods.
+ start.Name.Local = typ.Elem().Name()
+ }
+ return start
+}
-func (p *printer) marshalSimple(typ reflect.Type, val reflect.Value) error {
- // Normally we don't see structs, but this can happen for an attribute.
- if val.Type() == timeType {
- p.WriteString(val.Interface().(time.Time).Format(time.RFC3339Nano))
- return nil
+// marshalInterface marshals a Marshaler interface value.
+func (p *printer) marshalInterface(val Marshaler, start StartElement) error {
+ // Push a marker onto the tag stack so that MarshalXML
+ // cannot close the XML tags that it did not open.
+ p.tags = append(p.tags, Name{})
+ n := len(p.tags)
+
+ err := val.MarshalXML(p.encoder, start)
+ if err != nil {
+ return err
+ }
+
+ // Make sure MarshalXML closed all its tags. p.tags[n-1] is the mark.
+ if len(p.tags) > n {
+ return fmt.Errorf("xml: %s.MarshalXML wrote invalid XML: <%s> not closed", receiverType(val), p.tags[len(p.tags)-1].Local)
+ }
+ p.tags = p.tags[:n-1]
+ return nil
+}
+
+// marshalTextInterface marshals a TextMarshaler interface value.
+func (p *printer) marshalTextInterface(val encoding.TextMarshaler, start StartElement) error {
+ if err := p.writeStart(&start); err != nil {
+ return err
+ }
+ text, err := val.MarshalText()
+ if err != nil {
+ return err
+ }
+ EscapeText(p, text)
+ return p.writeEnd(start.Name)
+}
+
+// writeStart writes the given start element.
+func (p *printer) writeStart(start *StartElement) error {
+ if start.Name.Local == "" {
+ return fmt.Errorf("xml: start tag with no name")
+ }
+
+ p.tags = append(p.tags, start.Name)
+ p.markPrefix()
+
+ p.writeIndent(1)
+ p.WriteByte('<')
+ p.WriteString(start.Name.Local)
+
+ if start.Name.Space != "" {
+ p.WriteString(` xmlns="`)
+ p.EscapeString(start.Name.Space)
+ p.WriteByte('"')
+ }
+
+ // Attributes
+ for _, attr := range start.Attr {
+ name := attr.Name
+ if name.Local == "" {
+ continue
+ }
+ p.WriteByte(' ')
+ if name.Space != "" {
+ p.WriteString(p.createAttrPrefix(name.Space))
+ p.WriteByte(':')
+ }
+ p.WriteString(name.Local)
+ p.WriteString(`="`)
+ p.EscapeString(attr.Value)
+ p.WriteByte('"')
}
+ p.WriteByte('>')
+ return nil
+}
+
+func (p *printer) writeEnd(name Name) error {
+ if name.Local == "" {
+ return fmt.Errorf("xml: end tag with no name")
+ }
+ if len(p.tags) == 0 || p.tags[len(p.tags)-1].Local == "" {
+ return fmt.Errorf("xml: end tag </%s> without start tag", name.Local)
+ }
+ if top := p.tags[len(p.tags)-1]; top != name {
+ if top.Local != name.Local {
+ return fmt.Errorf("xml: end tag </%s> does not match start tag <%s>", name.Local, top.Local)
+ }
+ return fmt.Errorf("xml: end tag </%s> in namespace %s does not match start tag <%s> in namespace %s", name.Local, name.Space, top.Local, top.Space)
+ }
+ p.tags = p.tags[:len(p.tags)-1]
+
+ p.writeIndent(-1)
+ p.WriteByte('<')
+ p.WriteByte('/')
+ p.WriteString(name.Local)
+ p.WriteByte('>')
+ p.popPrefix()
+ return nil
+}
+
+func (p *printer) marshalSimple(typ reflect.Type, val reflect.Value) (string, []byte, error) {
switch val.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- p.WriteString(strconv.FormatInt(val.Int(), 10))
+ return strconv.FormatInt(val.Int(), 10), nil, nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- p.WriteString(strconv.FormatUint(val.Uint(), 10))
+ return strconv.FormatUint(val.Uint(), 10), nil, nil
case reflect.Float32, reflect.Float64:
- p.WriteString(strconv.FormatFloat(val.Float(), 'g', -1, val.Type().Bits()))
+ return strconv.FormatFloat(val.Float(), 'g', -1, val.Type().Bits()), nil, nil
case reflect.String:
- // TODO: Add EscapeString.
- EscapeText(p, []byte(val.String()))
+ return val.String(), nil, nil
case reflect.Bool:
- p.WriteString(strconv.FormatBool(val.Bool()))
+ return strconv.FormatBool(val.Bool()), nil, nil
case reflect.Array:
- // will be [...]byte
+ if typ.Elem().Kind() != reflect.Uint8 {
+ break
+ }
+ // [...]byte
var bytes []byte
if val.CanAddr() {
bytes = val.Slice(0, val.Len()).Bytes()
@@ -339,32 +685,57 @@ func (p *printer) marshalSimple(typ reflect.Type, val reflect.Value) error {
bytes = make([]byte, val.Len())
reflect.Copy(reflect.ValueOf(bytes), val)
}
- EscapeText(p, bytes)
+ return "", bytes, nil
case reflect.Slice:
- // will be []byte
- EscapeText(p, val.Bytes())
- default:
- return &UnsupportedTypeError{typ}
+ if typ.Elem().Kind() != reflect.Uint8 {
+ break
+ }
+ // []byte
+ return "", val.Bytes(), nil
}
- return p.cachedWriteError()
+ return "", nil, &UnsupportedTypeError{typ}
}
var ddBytes = []byte("--")
func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
- if val.Type() == timeType {
- _, err := p.WriteString(val.Interface().(time.Time).Format(time.RFC3339Nano))
- return err
- }
- s := parentStack{printer: p}
+ s := parentStack{p: p}
for i := range tinfo.fields {
finfo := &tinfo.fields[i]
if finfo.flags&fAttr != 0 {
continue
}
vf := finfo.value(val)
+
+ // Dereference or skip nil pointer, interface values.
+ switch vf.Kind() {
+ case reflect.Ptr, reflect.Interface:
+ if !vf.IsNil() {
+ vf = vf.Elem()
+ }
+ }
+
switch finfo.flags & fMode {
case fCharData:
+ if vf.CanInterface() && vf.Type().Implements(textMarshalerType) {
+ data, err := vf.Interface().(encoding.TextMarshaler).MarshalText()
+ if err != nil {
+ return err
+ }
+ Escape(p, data)
+ continue
+ }
+ if vf.CanAddr() {
+ pv := vf.Addr()
+ if pv.CanInterface() && pv.Type().Implements(textMarshalerType) {
+ data, err := pv.Interface().(encoding.TextMarshaler).MarshalText()
+ if err != nil {
+ return err
+ }
+ Escape(p, data)
+ continue
+ }
+ }
var scratch [64]byte
switch vf.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
@@ -385,10 +756,6 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
return err
}
}
- case reflect.Struct:
- if vf.Type() == timeType {
- Escape(p, []byte(vf.Interface().(time.Time).Format(time.RFC3339Nano)))
- }
}
continue
@@ -444,14 +811,18 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
}
case fElement, fElement | fAny:
- s.trim(finfo.parents)
+ if err := s.trim(finfo.parents); err != nil {
+ return err
+ }
if len(finfo.parents) > len(s.stack) {
if vf.Kind() != reflect.Ptr && vf.Kind() != reflect.Interface || !vf.IsNil() {
- s.push(finfo.parents[len(s.stack):])
+ if err := s.push(finfo.parents[len(s.stack):]); err != nil {
+ return err
+ }
}
}
}
- if err := p.marshalValue(vf, finfo); err != nil {
+ if err := p.marshalValue(vf, finfo, nil); err != nil {
return err
}
}
@@ -497,14 +868,14 @@ func (p *printer) writeIndent(depthDelta int) {
}
type parentStack struct {
- *printer
+ p *printer
stack []string
}
// trim updates the XML context to match the longest common prefix of the stack
// and the given parents. A closing tag will be written for every parent
// popped. Passing a zero slice or nil will close all the elements.
-func (s *parentStack) trim(parents []string) {
+func (s *parentStack) trim(parents []string) error {
split := 0
for ; split < len(parents) && split < len(s.stack); split++ {
if parents[split] != s.stack[split] {
@@ -512,23 +883,23 @@ func (s *parentStack) trim(parents []string) {
}
}
for i := len(s.stack) - 1; i >= split; i-- {
- s.writeIndent(-1)
- s.WriteString("</")
- s.WriteString(s.stack[i])
- s.WriteByte('>')
+ if err := s.p.writeEnd(Name{Local: s.stack[i]}); err != nil {
+ return err
+ }
}
s.stack = parents[:split]
+ return nil
}
// push adds parent elements to the stack and writes open tags.
-func (s *parentStack) push(parents []string) {
+func (s *parentStack) push(parents []string) error {
for i := 0; i < len(parents); i++ {
- s.writeIndent(1)
- s.WriteByte('<')
- s.WriteString(parents[i])
- s.WriteByte('>')
+ if err := s.p.writeStart(&StartElement{Name: Name{Local: parents[i]}}); err != nil {
+ return err
+ }
}
s.stack = append(s.stack, parents...)
+ return nil
}
// A MarshalXMLError is returned when Marshal encounters a type
diff --git a/src/pkg/encoding/xml/marshal_test.go b/src/pkg/encoding/xml/marshal_test.go
index ca14a1e53..d34118a3d 100644
--- a/src/pkg/encoding/xml/marshal_test.go
+++ b/src/pkg/encoding/xml/marshal_test.go
@@ -276,6 +276,54 @@ type Strings struct {
X []string `xml:"A>B,omitempty"`
}
+type PointerFieldsTest struct {
+ XMLName Name `xml:"dummy"`
+ Name *string `xml:"name,attr"`
+ Age *uint `xml:"age,attr"`
+ Empty *string `xml:"empty,attr"`
+ Contents *string `xml:",chardata"`
+}
+
+type ChardataEmptyTest struct {
+ XMLName Name `xml:"test"`
+ Contents *string `xml:",chardata"`
+}
+
+type MyMarshalerTest struct {
+}
+
+var _ Marshaler = (*MyMarshalerTest)(nil)
+
+func (m *MyMarshalerTest) MarshalXML(e *Encoder, start StartElement) error {
+ e.EncodeToken(start)
+ e.EncodeToken(CharData([]byte("hello world")))
+ e.EncodeToken(EndElement{start.Name})
+ return nil
+}
+
+type MyMarshalerAttrTest struct {
+}
+
+var _ MarshalerAttr = (*MyMarshalerAttrTest)(nil)
+
+func (m *MyMarshalerAttrTest) MarshalXMLAttr(name Name) (Attr, error) {
+ return Attr{name, "hello world"}, nil
+}
+
+type MarshalerStruct struct {
+ Foo MyMarshalerAttrTest `xml:",attr"`
+}
+
+func ifaceptr(x interface{}) interface{} {
+ return &x
+}
+
+var (
+ nameAttr = "Sarah"
+ ageAttr = uint(12)
+ contentsAttr = "lorem ipsum"
+)
+
// Unless explicitly stated as such (or *Plain), all of the
// tests below are two-way tests. When introducing new tests,
// please try to make them two-way as well to ensure that
@@ -312,6 +360,7 @@ var marshalTests = []struct {
{Value: &Plain{NamedType("potato")}, ExpectXML: `<Plain><V>potato</V></Plain>`},
{Value: &Plain{[]int{1, 2, 3}}, ExpectXML: `<Plain><V>1</V><V>2</V><V>3</V></Plain>`},
{Value: &Plain{[3]int{1, 2, 3}}, ExpectXML: `<Plain><V>1</V><V>2</V><V>3</V></Plain>`},
+ {Value: ifaceptr(true), MarshalOnly: true, ExpectXML: `<bool>true</bool>`},
// Test time.
{
@@ -673,6 +722,20 @@ var marshalTests = []struct {
ExpectXML: `<OmitAttrTest></OmitAttrTest>`,
},
+ // pointer fields
+ {
+ Value: &PointerFieldsTest{Name: &nameAttr, Age: &ageAttr, Contents: &contentsAttr},
+ ExpectXML: `<dummy name="Sarah" age="12">lorem ipsum</dummy>`,
+ MarshalOnly: true,
+ },
+
+ // empty chardata pointer field
+ {
+ Value: &ChardataEmptyTest{},
+ ExpectXML: `<test></test>`,
+ MarshalOnly: true,
+ },
+
// omitempty on fields
{
Value: &OmitFieldTest{
@@ -811,6 +874,15 @@ var marshalTests = []struct {
ExpectXML: `<Strings><A></A></Strings>`,
Value: &Strings{},
},
+ // Custom marshalers.
+ {
+ ExpectXML: `<MyMarshalerTest>hello world</MyMarshalerTest>`,
+ Value: &MyMarshalerTest{},
+ },
+ {
+ ExpectXML: `<MarshalerStruct Foo="hello world"></MarshalerStruct>`,
+ Value: &MarshalerStruct{},
+ },
}
func TestMarshal(t *testing.T) {
@@ -837,6 +909,10 @@ type AttrParent struct {
X string `xml:"X>Y,attr"`
}
+type BadAttr struct {
+ Name []string `xml:"name,attr"`
+}
+
var marshalErrorTests = []struct {
Value interface{}
Err string
@@ -869,6 +945,10 @@ var marshalErrorTests = []struct {
Value: &AttrParent{},
Err: `xml: X>Y chain not valid with attr flag`,
},
+ {
+ Value: BadAttr{[]string{"X", "Y"}},
+ Err: `xml: unsupported type: []string`,
+ },
}
var marshalIndentTests = []struct {
@@ -1009,6 +1089,23 @@ func TestMarshalWriteIOErrors(t *testing.T) {
}
}
+func TestMarshalFlush(t *testing.T) {
+ var buf bytes.Buffer
+ enc := NewEncoder(&buf)
+ if err := enc.EncodeToken(CharData("hello world")); err != nil {
+ t.Fatalf("enc.EncodeToken: %v", err)
+ }
+ if buf.Len() > 0 {
+ t.Fatalf("enc.EncodeToken caused actual write: %q", buf.Bytes())
+ }
+ if err := enc.Flush(); err != nil {
+ t.Fatalf("enc.Flush: %v", err)
+ }
+ if buf.String() != "hello world" {
+ t.Fatalf("after enc.Flush, buf.String() = %q, want %q", buf.String(), "hello world")
+ }
+}
+
func BenchmarkMarshal(b *testing.B) {
for i := 0; i < b.N; i++ {
Marshal(atomValue)
@@ -1021,3 +1118,34 @@ func BenchmarkUnmarshal(b *testing.B) {
Unmarshal(xml, &Feed{})
}
}
+
+// golang.org/issue/6556
+func TestStructPointerMarshal(t *testing.T) {
+ type A struct {
+ XMLName string `xml:"a"`
+ B []interface{}
+ }
+ type C struct {
+ XMLName Name
+ Value string `xml:"value"`
+ }
+
+ a := new(A)
+ a.B = append(a.B, &C{
+ XMLName: Name{Local: "c"},
+ Value: "x",
+ })
+
+ b, err := Marshal(a)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if x := string(b); x != "<a><c><value>x</value></c></a>" {
+ t.Fatal(x)
+ }
+ var v A
+ err = Unmarshal(b, &v)
+ if err != nil {
+ t.Fatal(err)
+ }
+}
diff --git a/src/pkg/encoding/xml/read.go b/src/pkg/encoding/xml/read.go
index a7a2a9655..8890508f8 100644
--- a/src/pkg/encoding/xml/read.go
+++ b/src/pkg/encoding/xml/read.go
@@ -6,11 +6,12 @@ package xml
import (
"bytes"
+ "encoding"
"errors"
+ "fmt"
"reflect"
"strconv"
"strings"
- "time"
)
// BUG(rsc): Mapping between XML elements and data structures is inherently flawed:
@@ -52,12 +53,12 @@ import (
// 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".
+// 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 comments, they are accumulated in
-// the first struct field that has tag ",comments". The struct
+// the first struct field that has tag ",comment". The struct
// field may have type []byte or string. If there is no such
// field, the comments are discarded.
//
@@ -137,6 +138,136 @@ type UnmarshalError string
func (e UnmarshalError) Error() string { return string(e) }
+// Unmarshaler is the interface implemented by objects that can unmarshal
+// an XML element description of themselves.
+//
+// UnmarshalXML decodes a single XML element
+// beginning with the given start element.
+// If it returns an error, the outer call to Unmarshal stops and
+// returns that error.
+// UnmarshalXML must consume exactly one XML element.
+// One common implementation strategy is to unmarshal into
+// a separate value with a layout matching the expected XML
+// using d.DecodeElement, and then to copy the data from
+// that value into the receiver.
+// Another common strategy is to use d.Token to process the
+// XML object one token at a time.
+// UnmarshalXML may not use d.RawToken.
+type Unmarshaler interface {
+ UnmarshalXML(d *Decoder, start StartElement) error
+}
+
+// UnmarshalerAttr is the interface implemented by objects that can unmarshal
+// an XML attribute description of themselves.
+//
+// UnmarshalXMLAttr decodes a single XML attribute.
+// If it returns an error, the outer call to Unmarshal stops and
+// returns that error.
+// UnmarshalXMLAttr is used only for struct fields with the
+// "attr" option in the field tag.
+type UnmarshalerAttr interface {
+ UnmarshalXMLAttr(attr Attr) error
+}
+
+// receiverType returns the receiver type to use in an expression like "%s.MethodName".
+func receiverType(val interface{}) string {
+ t := reflect.TypeOf(val)
+ if t.Name() != "" {
+ return t.String()
+ }
+ return "(" + t.String() + ")"
+}
+
+// unmarshalInterface unmarshals a single XML element into val.
+// start is the opening tag of the element.
+func (p *Decoder) unmarshalInterface(val Unmarshaler, start *StartElement) error {
+ // Record that decoder must stop at end tag corresponding to start.
+ p.pushEOF()
+
+ p.unmarshalDepth++
+ err := val.UnmarshalXML(p, *start)
+ p.unmarshalDepth--
+ if err != nil {
+ p.popEOF()
+ return err
+ }
+
+ if !p.popEOF() {
+ return fmt.Errorf("xml: %s.UnmarshalXML did not consume entire <%s> element", receiverType(val), start.Name.Local)
+ }
+
+ return nil
+}
+
+// unmarshalTextInterface unmarshals a single XML element into val.
+// The chardata contained in the element (but not its children)
+// is passed to the text unmarshaler.
+func (p *Decoder) unmarshalTextInterface(val encoding.TextUnmarshaler, start *StartElement) error {
+ var buf []byte
+ depth := 1
+ for depth > 0 {
+ t, err := p.Token()
+ if err != nil {
+ return err
+ }
+ switch t := t.(type) {
+ case CharData:
+ if depth == 1 {
+ buf = append(buf, t...)
+ }
+ case StartElement:
+ depth++
+ case EndElement:
+ depth--
+ }
+ }
+ return val.UnmarshalText(buf)
+}
+
+// unmarshalAttr unmarshals a single XML attribute into val.
+func (p *Decoder) unmarshalAttr(val reflect.Value, attr Attr) error {
+ if val.Kind() == reflect.Ptr {
+ if val.IsNil() {
+ val.Set(reflect.New(val.Type().Elem()))
+ }
+ val = val.Elem()
+ }
+
+ if val.CanInterface() && val.Type().Implements(unmarshalerAttrType) {
+ // This is an unmarshaler with a non-pointer receiver,
+ // so it's likely to be incorrect, but we do what we're told.
+ return val.Interface().(UnmarshalerAttr).UnmarshalXMLAttr(attr)
+ }
+ if val.CanAddr() {
+ pv := val.Addr()
+ if pv.CanInterface() && pv.Type().Implements(unmarshalerAttrType) {
+ return pv.Interface().(UnmarshalerAttr).UnmarshalXMLAttr(attr)
+ }
+ }
+
+ // Not an UnmarshalerAttr; try encoding.TextUnmarshaler.
+ if val.CanInterface() && val.Type().Implements(textUnmarshalerType) {
+ // This is an unmarshaler with a non-pointer receiver,
+ // so it's likely to be incorrect, but we do what we're told.
+ return val.Interface().(encoding.TextUnmarshaler).UnmarshalText([]byte(attr.Value))
+ }
+ if val.CanAddr() {
+ pv := val.Addr()
+ if pv.CanInterface() && pv.Type().Implements(textUnmarshalerType) {
+ return pv.Interface().(encoding.TextUnmarshaler).UnmarshalText([]byte(attr.Value))
+ }
+ }
+
+ copyValue(val, []byte(attr.Value))
+ return nil
+}
+
+var (
+ unmarshalerType = reflect.TypeOf((*Unmarshaler)(nil)).Elem()
+ unmarshalerAttrType = reflect.TypeOf((*UnmarshalerAttr)(nil)).Elem()
+ textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
+)
+
// Unmarshal a single XML element into val.
func (p *Decoder) unmarshal(val reflect.Value, start *StartElement) error {
// Find start element if we need it.
@@ -153,11 +284,35 @@ func (p *Decoder) unmarshal(val reflect.Value, start *StartElement) error {
}
}
- if pv := val; pv.Kind() == reflect.Ptr {
- if pv.IsNil() {
- pv.Set(reflect.New(pv.Type().Elem()))
+ if val.Kind() == reflect.Ptr {
+ if val.IsNil() {
+ val.Set(reflect.New(val.Type().Elem()))
+ }
+ val = val.Elem()
+ }
+
+ if val.CanInterface() && val.Type().Implements(unmarshalerType) {
+ // This is an unmarshaler with a non-pointer receiver,
+ // so it's likely to be incorrect, but we do what we're told.
+ return p.unmarshalInterface(val.Interface().(Unmarshaler), start)
+ }
+
+ if val.CanAddr() {
+ pv := val.Addr()
+ if pv.CanInterface() && pv.Type().Implements(unmarshalerType) {
+ return p.unmarshalInterface(pv.Interface().(Unmarshaler), start)
+ }
+ }
+
+ if val.CanInterface() && val.Type().Implements(textUnmarshalerType) {
+ return p.unmarshalTextInterface(val.Interface().(encoding.TextUnmarshaler), start)
+ }
+
+ if val.CanAddr() {
+ pv := val.Addr()
+ if pv.CanInterface() && pv.Type().Implements(textUnmarshalerType) {
+ return p.unmarshalTextInterface(pv.Interface().(encoding.TextUnmarshaler), start)
}
- val = pv.Elem()
}
var (
@@ -222,10 +377,6 @@ func (p *Decoder) unmarshal(val reflect.Value, start *StartElement) error {
v.Set(reflect.ValueOf(start.Name))
break
}
- if typ == timeType {
- saveData = v
- break
- }
sv = v
tinfo, err = getTypeInfo(typ)
@@ -264,7 +415,9 @@ func (p *Decoder) unmarshal(val reflect.Value, start *StartElement) error {
// Look for attribute.
for _, a := range start.Attr {
if a.Name.Local == finfo.name && (finfo.xmlns == "" || finfo.xmlns == a.Name.Space) {
- copyValue(strv, []byte(a.Value))
+ if err := p.unmarshalAttr(strv, a); err != nil {
+ return err
+ }
break
}
}
@@ -352,6 +505,23 @@ Loop:
}
}
+ if saveData.IsValid() && saveData.CanInterface() && saveData.Type().Implements(textUnmarshalerType) {
+ if err := saveData.Interface().(encoding.TextUnmarshaler).UnmarshalText(data); err != nil {
+ return err
+ }
+ saveData = reflect.Value{}
+ }
+
+ if saveData.IsValid() && saveData.CanAddr() {
+ pv := saveData.Addr()
+ if pv.CanInterface() && pv.Type().Implements(textUnmarshalerType) {
+ if err := pv.Interface().(encoding.TextUnmarshaler).UnmarshalText(data); err != nil {
+ return err
+ }
+ saveData = reflect.Value{}
+ }
+ }
+
if err := copyValue(saveData, data); err != nil {
return err
}
@@ -374,6 +544,8 @@ Loop:
}
func copyValue(dst reflect.Value, src []byte) (err error) {
+ dst0 := dst
+
if dst.Kind() == reflect.Ptr {
if dst.IsNil() {
dst.Set(reflect.New(dst.Type().Elem()))
@@ -384,9 +556,9 @@ func copyValue(dst reflect.Value, src []byte) (err error) {
// Save accumulated data.
switch dst.Kind() {
case reflect.Invalid:
- // Probably a commendst.
+ // Probably a comment.
default:
- return errors.New("cannot happen: unknown type " + dst.Type().String())
+ return errors.New("cannot unmarshal into " + dst0.Type().String())
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
itmp, err := strconv.ParseInt(string(src), 10, dst.Type().Bits())
if err != nil {
@@ -419,14 +591,6 @@ func copyValue(dst reflect.Value, src []byte) (err error) {
src = []byte{}
}
dst.SetBytes(src)
- case reflect.Struct:
- if dst.Type() == timeType {
- tv, err := time.Parse(time.RFC3339, string(src))
- if err != nil {
- return err
- }
- dst.Set(reflect.ValueOf(tv))
- }
}
return nil
}
diff --git a/src/pkg/encoding/xml/read_test.go b/src/pkg/encoding/xml/read_test.go
index 7d28c5d7d..1404c900f 100644
--- a/src/pkg/encoding/xml/read_test.go
+++ b/src/pkg/encoding/xml/read_test.go
@@ -5,6 +5,7 @@
package xml
import (
+ "io"
"reflect"
"strings"
"testing"
@@ -621,3 +622,66 @@ func TestMarshalNSAttr(t *testing.T) {
t.Errorf("Unmarshal = %q, want %q", dst, src)
}
}
+
+type MyCharData struct {
+ body string
+}
+
+func (m *MyCharData) UnmarshalXML(d *Decoder, start StartElement) error {
+ for {
+ t, err := d.Token()
+ if err == io.EOF { // found end of element
+ break
+ }
+ if err != nil {
+ return err
+ }
+ if char, ok := t.(CharData); ok {
+ m.body += string(char)
+ }
+ }
+ return nil
+}
+
+var _ Unmarshaler = (*MyCharData)(nil)
+
+func (m *MyCharData) UnmarshalXMLAttr(attr Attr) error {
+ panic("must not call")
+}
+
+type MyAttr struct {
+ attr string
+}
+
+func (m *MyAttr) UnmarshalXMLAttr(attr Attr) error {
+ m.attr = attr.Value
+ return nil
+}
+
+var _ UnmarshalerAttr = (*MyAttr)(nil)
+
+type MyStruct struct {
+ Data *MyCharData
+ Attr *MyAttr `xml:",attr"`
+
+ Data2 MyCharData
+ Attr2 MyAttr `xml:",attr"`
+}
+
+func TestUnmarshaler(t *testing.T) {
+ xml := `<?xml version="1.0" encoding="utf-8"?>
+ <MyStruct Attr="attr1" Attr2="attr2">
+ <Data>hello <!-- comment -->world</Data>
+ <Data2>howdy <!-- comment -->world</Data2>
+ </MyStruct>
+ `
+
+ var m MyStruct
+ if err := Unmarshal([]byte(xml), &m); err != nil {
+ t.Fatal(err)
+ }
+
+ if m.Data == nil || m.Attr == nil || m.Data.body != "hello world" || m.Attr.attr != "attr1" || m.Data2.body != "howdy world" || m.Attr2.attr != "attr2" {
+ t.Errorf("m=%#+v\n", m)
+ }
+}
diff --git a/src/pkg/encoding/xml/xml.go b/src/pkg/encoding/xml/xml.go
index 021f7e47d..5b9d67002 100644
--- a/src/pkg/encoding/xml/xml.go
+++ b/src/pkg/encoding/xml/xml.go
@@ -16,6 +16,7 @@ package xml
import (
"bufio"
"bytes"
+ "errors"
"fmt"
"io"
"strconv"
@@ -66,6 +67,11 @@ func (e StartElement) Copy() StartElement {
return e
}
+// End returns the corresponding XML end element.
+func (e StartElement) End() EndElement {
+ return EndElement{e.Name}
+}
+
// An EndElement represents an XML end element.
type EndElement struct {
Name Name
@@ -144,6 +150,10 @@ type Decoder struct {
// d.Entity = HTMLEntity
//
// creates a parser that can handle typical HTML.
+ //
+ // Strict mode does not enforce the requirements of the XML name spaces TR.
+ // In particular it does not reject name space tags using undefined prefixes.
+ // Such tags are recorded with the unknown prefix as the name space URL.
Strict bool
// When Strict == false, AutoClose indicates a set of elements to
@@ -174,18 +184,19 @@ type Decoder struct {
// the attribute xmlns="DefaultSpace".
DefaultSpace string
- r io.ByteReader
- buf bytes.Buffer
- saved *bytes.Buffer
- stk *stack
- free *stack
- needClose bool
- toClose Name
- nextToken Token
- nextByte int
- ns map[string]string
- err error
- line int
+ r io.ByteReader
+ buf bytes.Buffer
+ saved *bytes.Buffer
+ stk *stack
+ free *stack
+ needClose bool
+ toClose Name
+ nextToken Token
+ nextByte int
+ ns map[string]string
+ err error
+ line int
+ unmarshalDepth int
}
// NewDecoder creates a new XML parser reading from r.
@@ -223,10 +234,14 @@ func NewDecoder(r io.Reader) *Decoder {
// If Token encounters an unrecognized name space prefix,
// it uses the prefix as the Space rather than report an error.
func (d *Decoder) Token() (t Token, err error) {
+ if d.stk != nil && d.stk.kind == stkEOF {
+ err = io.EOF
+ return
+ }
if d.nextToken != nil {
t = d.nextToken
d.nextToken = nil
- } else if t, err = d.RawToken(); err != nil {
+ } else if t, err = d.rawToken(); err != nil {
return
}
@@ -322,6 +337,7 @@ type stack struct {
const (
stkStart = iota
stkNs
+ stkEOF
)
func (d *Decoder) push(kind int) *stack {
@@ -347,6 +363,43 @@ func (d *Decoder) pop() *stack {
return s
}
+// Record that after the current element is finished
+// (that element is already pushed on the stack)
+// Token should return EOF until popEOF is called.
+func (d *Decoder) pushEOF() {
+ // Walk down stack to find Start.
+ // It might not be the top, because there might be stkNs
+ // entries above it.
+ start := d.stk
+ for start.kind != stkStart {
+ start = start.next
+ }
+ // The stkNs entries below a start are associated with that
+ // element too; skip over them.
+ for start.next != nil && start.next.kind == stkNs {
+ start = start.next
+ }
+ s := d.free
+ if s != nil {
+ d.free = s.next
+ } else {
+ s = new(stack)
+ }
+ s.kind = stkEOF
+ s.next = start.next
+ start.next = s
+}
+
+// Undo a pushEOF.
+// The element must have been finished, so the EOF should be at the top of the stack.
+func (d *Decoder) popEOF() bool {
+ if d.stk == nil || d.stk.kind != stkEOF {
+ return false
+ }
+ d.pop()
+ return true
+}
+
// Record that we are starting an element with the given name.
func (d *Decoder) pushElement(name Name) {
s := d.push(stkStart)
@@ -395,9 +448,9 @@ func (d *Decoder) popElement(t *EndElement) bool {
return false
}
- // Pop stack until a Start is on the top, undoing the
+ // Pop stack until a Start or EOF is on the top, undoing the
// translations that were associated with the element we just closed.
- for d.stk != nil && d.stk.kind != stkStart {
+ for d.stk != nil && d.stk.kind != stkStart && d.stk.kind != stkEOF {
s := d.pop()
if s.ok {
d.ns[s.name.Local] = s.name.Space
@@ -429,10 +482,19 @@ func (d *Decoder) autoClose(t Token) (Token, bool) {
return nil, false
}
+var errRawToken = errors.New("xml: cannot use RawToken from UnmarshalXML method")
+
// RawToken is like Token but does not verify that
// start and end elements match and does not translate
// name space prefixes to their corresponding URLs.
func (d *Decoder) RawToken() (Token, error) {
+ if d.unmarshalDepth > 0 {
+ return nil, errRawToken
+ }
+ return d.rawToken()
+}
+
+func (d *Decoder) rawToken() (Token, error) {
if d.err != nil {
return nil, d.err
}
@@ -484,8 +546,7 @@ func (d *Decoder) RawToken() (Token, error) {
case '?':
// <?: Processing instruction.
- // TODO(rsc): Should parse the <?xml declaration to make sure
- // the version is 1.0 and the encoding is UTF-8.
+ // TODO(rsc): Should parse the <?xml declaration to make sure the version is 1.0.
var target string
if target, ok = d.name(); !ok {
if d.err == nil {
@@ -1112,6 +1173,30 @@ func isName(s []byte) bool {
return true
}
+func isNameString(s string) bool {
+ if len(s) == 0 {
+ return false
+ }
+ c, n := utf8.DecodeRuneInString(s)
+ if c == utf8.RuneError && n == 1 {
+ return false
+ }
+ if !unicode.Is(first, c) {
+ return false
+ }
+ for n < len(s) {
+ s = s[n:]
+ c, n = utf8.DecodeRuneInString(s)
+ if c == utf8.RuneError && n == 1 {
+ return false
+ }
+ if !unicode.Is(first, c) && !unicode.Is(second, c) {
+ return false
+ }
+ }
+ return true
+}
+
// These tables were generated by cut and paste from Appendix B of
// the XML spec at http://www.xml.com/axml/testaxml.htm
// and then reformatting. First corresponds to (Letter | '_' | ':')
@@ -1758,7 +1843,7 @@ func EscapeText(w io.Writer, s []byte) error {
case '\r':
esc = esc_cr
default:
- if !isInCharacterRange(r) {
+ if !isInCharacterRange(r) || (r == 0xFFFD && width == 1) {
esc = esc_fffd
break
}
@@ -1778,6 +1863,45 @@ func EscapeText(w io.Writer, s []byte) error {
return nil
}
+// EscapeString writes to p the properly escaped XML equivalent
+// of the plain text data s.
+func (p *printer) EscapeString(s string) {
+ var esc []byte
+ last := 0
+ for i := 0; i < len(s); {
+ r, width := utf8.DecodeRuneInString(s[i:])
+ i += width
+ switch r {
+ case '"':
+ esc = esc_quot
+ case '\'':
+ esc = esc_apos
+ case '&':
+ esc = esc_amp
+ case '<':
+ esc = esc_lt
+ case '>':
+ esc = esc_gt
+ case '\t':
+ esc = esc_tab
+ case '\n':
+ esc = esc_nl
+ case '\r':
+ esc = esc_cr
+ default:
+ if !isInCharacterRange(r) || (r == 0xFFFD && width == 1) {
+ esc = esc_fffd
+ break
+ }
+ continue
+ }
+ p.WriteString(s[last : i-width])
+ p.Write(esc)
+ last = i
+ }
+ p.WriteString(s[last:])
+}
+
// Escape is like EscapeText but omits the error return value.
// It is provided for backwards compatibility with Go 1.0.
// Code targeting Go 1.1 or later should use EscapeText.
diff --git a/src/pkg/encoding/xml/xml_test.go b/src/pkg/encoding/xml/xml_test.go
index eeedbe575..7723ab1c9 100644
--- a/src/pkg/encoding/xml/xml_test.go
+++ b/src/pkg/encoding/xml/xml_test.go
@@ -11,6 +11,7 @@ import (
"reflect"
"strings"
"testing"
+ "unicode/utf8"
)
const testInput = `
@@ -246,10 +247,8 @@ func (d *downCaser) Read(p []byte) (int, error) {
}
func TestRawTokenAltEncoding(t *testing.T) {
- sawEncoding := ""
d := NewDecoder(strings.NewReader(testInputAltEncoding))
d.CharsetReader = func(charset string, input io.Reader) (io.Reader, error) {
- sawEncoding = charset
if charset != "x-testing-uppercase" {
t.Fatalf("unexpected charset %q", charset)
}
@@ -714,3 +713,14 @@ func TestEscapeTextInvalidChar(t *testing.T) {
t.Errorf("have %v, want %v", text, expected)
}
}
+
+func TestIssue5880(t *testing.T) {
+ type T []byte
+ data, err := Marshal(T{192, 168, 0, 1})
+ if err != nil {
+ t.Errorf("Marshal error: %v", err)
+ }
+ if !utf8.Valid(data) {
+ t.Errorf("Marshal generated invalid UTF-8: %x", data)
+ }
+}
diff --git a/src/pkg/flag/export_test.go b/src/pkg/flag/export_test.go
index 7b190807a..56cda58b3 100644
--- a/src/pkg/flag/export_test.go
+++ b/src/pkg/flag/export_test.go
@@ -12,11 +12,6 @@ import "os"
// After calling ResetForTesting, parse errors in flag handling will not
// exit the program.
func ResetForTesting(usage func()) {
- commandLine = NewFlagSet(os.Args[0], ContinueOnError)
+ CommandLine = NewFlagSet(os.Args[0], ContinueOnError)
Usage = usage
}
-
-// CommandLine returns the default FlagSet.
-func CommandLine() *FlagSet {
- return commandLine
-}
diff --git a/src/pkg/flag/flag.go b/src/pkg/flag/flag.go
index 85dd8c3b3..e7c863ee9 100644
--- a/src/pkg/flag/flag.go
+++ b/src/pkg/flag/flag.go
@@ -89,6 +89,8 @@ func (b *boolValue) Set(s string) error {
return err
}
+func (b *boolValue) Get() interface{} { return bool(*b) }
+
func (b *boolValue) String() string { return fmt.Sprintf("%v", *b) }
func (b *boolValue) IsBoolFlag() bool { return true }
@@ -114,6 +116,8 @@ func (i *intValue) Set(s string) error {
return err
}
+func (i *intValue) Get() interface{} { return int(*i) }
+
func (i *intValue) String() string { return fmt.Sprintf("%v", *i) }
// -- int64 Value
@@ -130,6 +134,8 @@ func (i *int64Value) Set(s string) error {
return err
}
+func (i *int64Value) Get() interface{} { return int64(*i) }
+
func (i *int64Value) String() string { return fmt.Sprintf("%v", *i) }
// -- uint Value
@@ -146,6 +152,8 @@ func (i *uintValue) Set(s string) error {
return err
}
+func (i *uintValue) Get() interface{} { return uint(*i) }
+
func (i *uintValue) String() string { return fmt.Sprintf("%v", *i) }
// -- uint64 Value
@@ -162,6 +170,8 @@ func (i *uint64Value) Set(s string) error {
return err
}
+func (i *uint64Value) Get() interface{} { return uint64(*i) }
+
func (i *uint64Value) String() string { return fmt.Sprintf("%v", *i) }
// -- string Value
@@ -177,6 +187,8 @@ func (s *stringValue) Set(val string) error {
return nil
}
+func (s *stringValue) Get() interface{} { return string(*s) }
+
func (s *stringValue) String() string { return fmt.Sprintf("%s", *s) }
// -- float64 Value
@@ -193,6 +205,8 @@ func (f *float64Value) Set(s string) error {
return err
}
+func (f *float64Value) Get() interface{} { return float64(*f) }
+
func (f *float64Value) String() string { return fmt.Sprintf("%v", *f) }
// -- time.Duration Value
@@ -209,6 +223,8 @@ func (d *durationValue) Set(s string) error {
return err
}
+func (d *durationValue) Get() interface{} { return time.Duration(*d) }
+
func (d *durationValue) String() string { return (*time.Duration)(d).String() }
// Value is the interface to the dynamic value stored in a flag.
@@ -222,6 +238,15 @@ type Value interface {
Set(string) error
}
+// Getter is an interface that allows the contents of a Value to be retrieved.
+// It wraps the Value interface, rather than being part of it, because it
+// appeared after Go 1 and its compatibility rules. All Value types provided
+// by this package satisfy the Getter interface.
+type Getter interface {
+ Value
+ Get() interface{}
+}
+
// ErrorHandling defines how to handle flag parsing errors.
type ErrorHandling int
@@ -231,7 +256,8 @@ const (
PanicOnError
)
-// A FlagSet represents a set of defined flags.
+// A FlagSet represents a set of defined flags. The zero value of a FlagSet
+// has no name and has ContinueOnError error handling.
type FlagSet struct {
// Usage is the function called when an error occurs while parsing flags.
// The field is a function (not a method) that may be changed to point to
@@ -296,7 +322,7 @@ func (f *FlagSet) VisitAll(fn func(*Flag)) {
// VisitAll visits the command-line flags in lexicographical order, calling
// fn for each. It visits all flags, even those not set.
func VisitAll(fn func(*Flag)) {
- commandLine.VisitAll(fn)
+ CommandLine.VisitAll(fn)
}
// Visit visits the flags in lexicographical order, calling fn for each.
@@ -310,7 +336,7 @@ func (f *FlagSet) Visit(fn func(*Flag)) {
// Visit visits the command-line flags in lexicographical order, calling fn
// for each. It visits only those flags that have been set.
func Visit(fn func(*Flag)) {
- commandLine.Visit(fn)
+ CommandLine.Visit(fn)
}
// Lookup returns the Flag structure of the named flag, returning nil if none exists.
@@ -321,7 +347,7 @@ func (f *FlagSet) Lookup(name string) *Flag {
// Lookup returns the Flag structure of the named command-line flag,
// returning nil if none exists.
func Lookup(name string) *Flag {
- return commandLine.formal[name]
+ return CommandLine.formal[name]
}
// Set sets the value of the named flag.
@@ -343,7 +369,7 @@ func (f *FlagSet) Set(name, value string) error {
// Set sets the value of the named command-line flag.
func Set(name, value string) error {
- return commandLine.Set(name, value)
+ return CommandLine.Set(name, value)
}
// PrintDefaults prints, to standard error unless configured
@@ -361,16 +387,20 @@ func (f *FlagSet) PrintDefaults() {
// PrintDefaults prints to standard error the default values of all defined command-line flags.
func PrintDefaults() {
- commandLine.PrintDefaults()
+ CommandLine.PrintDefaults()
}
// defaultUsage is the default function to print a usage message.
func defaultUsage(f *FlagSet) {
- fmt.Fprintf(f.out(), "Usage of %s:\n", f.name)
+ if f.name == "" {
+ fmt.Fprintf(f.out(), "Usage:\n")
+ } else {
+ fmt.Fprintf(f.out(), "Usage of %s:\n", f.name)
+ }
f.PrintDefaults()
}
-// NOTE: Usage is not just defaultUsage(commandLine)
+// NOTE: Usage is not just defaultUsage(CommandLine)
// because it serves (via godoc flag Usage) as the example
// for how to write your own usage function.
@@ -385,7 +415,7 @@ var Usage = func() {
func (f *FlagSet) NFlag() int { return len(f.actual) }
// NFlag returns the number of command-line flags that have been set.
-func NFlag() int { return len(commandLine.actual) }
+func NFlag() int { return len(CommandLine.actual) }
// Arg returns the i'th argument. Arg(0) is the first remaining argument
// after flags have been processed.
@@ -399,20 +429,20 @@ func (f *FlagSet) Arg(i int) string {
// Arg returns the i'th command-line argument. Arg(0) is the first remaining argument
// after flags have been processed.
func Arg(i int) string {
- return commandLine.Arg(i)
+ return CommandLine.Arg(i)
}
// NArg is the number of arguments remaining after flags have been processed.
func (f *FlagSet) NArg() int { return len(f.args) }
// NArg is the number of arguments remaining after flags have been processed.
-func NArg() int { return len(commandLine.args) }
+func NArg() int { return len(CommandLine.args) }
// Args returns the non-flag arguments.
func (f *FlagSet) Args() []string { return f.args }
// Args returns the non-flag command-line arguments.
-func Args() []string { return commandLine.args }
+func Args() []string { return CommandLine.args }
// BoolVar defines a bool flag with specified name, default value, and usage string.
// The argument p points to a bool variable in which to store the value of the flag.
@@ -423,7 +453,7 @@ func (f *FlagSet) BoolVar(p *bool, name string, value bool, usage string) {
// BoolVar defines a bool flag with specified name, default value, and usage string.
// The argument p points to a bool variable in which to store the value of the flag.
func BoolVar(p *bool, name string, value bool, usage string) {
- commandLine.Var(newBoolValue(value, p), name, usage)
+ CommandLine.Var(newBoolValue(value, p), name, usage)
}
// Bool defines a bool flag with specified name, default value, and usage string.
@@ -437,7 +467,7 @@ func (f *FlagSet) Bool(name string, value bool, usage string) *bool {
// Bool defines a bool flag with specified name, default value, and usage string.
// The return value is the address of a bool variable that stores the value of the flag.
func Bool(name string, value bool, usage string) *bool {
- return commandLine.Bool(name, value, usage)
+ return CommandLine.Bool(name, value, usage)
}
// IntVar defines an int flag with specified name, default value, and usage string.
@@ -449,7 +479,7 @@ func (f *FlagSet) IntVar(p *int, name string, value int, usage string) {
// IntVar defines an int flag with specified name, default value, and usage string.
// The argument p points to an int variable in which to store the value of the flag.
func IntVar(p *int, name string, value int, usage string) {
- commandLine.Var(newIntValue(value, p), name, usage)
+ CommandLine.Var(newIntValue(value, p), name, usage)
}
// Int defines an int flag with specified name, default value, and usage string.
@@ -463,7 +493,7 @@ func (f *FlagSet) Int(name string, value int, usage string) *int {
// Int defines an int flag with specified name, default value, and usage string.
// The return value is the address of an int variable that stores the value of the flag.
func Int(name string, value int, usage string) *int {
- return commandLine.Int(name, value, usage)
+ return CommandLine.Int(name, value, usage)
}
// Int64Var defines an int64 flag with specified name, default value, and usage string.
@@ -475,7 +505,7 @@ func (f *FlagSet) Int64Var(p *int64, name string, value int64, usage string) {
// Int64Var defines an int64 flag with specified name, default value, and usage string.
// The argument p points to an int64 variable in which to store the value of the flag.
func Int64Var(p *int64, name string, value int64, usage string) {
- commandLine.Var(newInt64Value(value, p), name, usage)
+ CommandLine.Var(newInt64Value(value, p), name, usage)
}
// Int64 defines an int64 flag with specified name, default value, and usage string.
@@ -489,7 +519,7 @@ func (f *FlagSet) Int64(name string, value int64, usage string) *int64 {
// Int64 defines an int64 flag with specified name, default value, and usage string.
// The return value is the address of an int64 variable that stores the value of the flag.
func Int64(name string, value int64, usage string) *int64 {
- return commandLine.Int64(name, value, usage)
+ return CommandLine.Int64(name, value, usage)
}
// UintVar defines a uint flag with specified name, default value, and usage string.
@@ -501,7 +531,7 @@ func (f *FlagSet) UintVar(p *uint, name string, value uint, usage string) {
// UintVar defines a uint flag with specified name, default value, and usage string.
// The argument p points to a uint variable in which to store the value of the flag.
func UintVar(p *uint, name string, value uint, usage string) {
- commandLine.Var(newUintValue(value, p), name, usage)
+ CommandLine.Var(newUintValue(value, p), name, usage)
}
// Uint defines a uint flag with specified name, default value, and usage string.
@@ -515,7 +545,7 @@ func (f *FlagSet) Uint(name string, value uint, usage string) *uint {
// Uint defines a uint flag with specified name, default value, and usage string.
// The return value is the address of a uint variable that stores the value of the flag.
func Uint(name string, value uint, usage string) *uint {
- return commandLine.Uint(name, value, usage)
+ return CommandLine.Uint(name, value, usage)
}
// Uint64Var defines a uint64 flag with specified name, default value, and usage string.
@@ -527,7 +557,7 @@ func (f *FlagSet) Uint64Var(p *uint64, name string, value uint64, usage string)
// Uint64Var defines a uint64 flag with specified name, default value, and usage string.
// The argument p points to a uint64 variable in which to store the value of the flag.
func Uint64Var(p *uint64, name string, value uint64, usage string) {
- commandLine.Var(newUint64Value(value, p), name, usage)
+ CommandLine.Var(newUint64Value(value, p), name, usage)
}
// Uint64 defines a uint64 flag with specified name, default value, and usage string.
@@ -541,7 +571,7 @@ func (f *FlagSet) Uint64(name string, value uint64, usage string) *uint64 {
// Uint64 defines a uint64 flag with specified name, default value, and usage string.
// The return value is the address of a uint64 variable that stores the value of the flag.
func Uint64(name string, value uint64, usage string) *uint64 {
- return commandLine.Uint64(name, value, usage)
+ return CommandLine.Uint64(name, value, usage)
}
// StringVar defines a string flag with specified name, default value, and usage string.
@@ -553,7 +583,7 @@ func (f *FlagSet) StringVar(p *string, name string, value string, usage string)
// StringVar defines a string flag with specified name, default value, and usage string.
// The argument p points to a string variable in which to store the value of the flag.
func StringVar(p *string, name string, value string, usage string) {
- commandLine.Var(newStringValue(value, p), name, usage)
+ CommandLine.Var(newStringValue(value, p), name, usage)
}
// String defines a string flag with specified name, default value, and usage string.
@@ -567,7 +597,7 @@ func (f *FlagSet) String(name string, value string, usage string) *string {
// String defines a string flag with specified name, default value, and usage string.
// The return value is the address of a string variable that stores the value of the flag.
func String(name string, value string, usage string) *string {
- return commandLine.String(name, value, usage)
+ return CommandLine.String(name, value, usage)
}
// Float64Var defines a float64 flag with specified name, default value, and usage string.
@@ -579,7 +609,7 @@ func (f *FlagSet) Float64Var(p *float64, name string, value float64, usage strin
// Float64Var defines a float64 flag with specified name, default value, and usage string.
// The argument p points to a float64 variable in which to store the value of the flag.
func Float64Var(p *float64, name string, value float64, usage string) {
- commandLine.Var(newFloat64Value(value, p), name, usage)
+ CommandLine.Var(newFloat64Value(value, p), name, usage)
}
// Float64 defines a float64 flag with specified name, default value, and usage string.
@@ -593,7 +623,7 @@ func (f *FlagSet) Float64(name string, value float64, usage string) *float64 {
// Float64 defines a float64 flag with specified name, default value, and usage string.
// The return value is the address of a float64 variable that stores the value of the flag.
func Float64(name string, value float64, usage string) *float64 {
- return commandLine.Float64(name, value, usage)
+ return CommandLine.Float64(name, value, usage)
}
// DurationVar defines a time.Duration flag with specified name, default value, and usage string.
@@ -605,7 +635,7 @@ func (f *FlagSet) DurationVar(p *time.Duration, name string, value time.Duration
// DurationVar defines a time.Duration flag with specified name, default value, and usage string.
// The argument p points to a time.Duration variable in which to store the value of the flag.
func DurationVar(p *time.Duration, name string, value time.Duration, usage string) {
- commandLine.Var(newDurationValue(value, p), name, usage)
+ CommandLine.Var(newDurationValue(value, p), name, usage)
}
// Duration defines a time.Duration flag with specified name, default value, and usage string.
@@ -619,7 +649,7 @@ func (f *FlagSet) Duration(name string, value time.Duration, usage string) *time
// Duration defines a time.Duration flag with specified name, default value, and usage string.
// The return value is the address of a time.Duration variable that stores the value of the flag.
func Duration(name string, value time.Duration, usage string) *time.Duration {
- return commandLine.Duration(name, value, usage)
+ return CommandLine.Duration(name, value, usage)
}
// Var defines a flag with the specified name and usage string. The type and
@@ -633,7 +663,12 @@ func (f *FlagSet) Var(value Value, name string, usage string) {
flag := &Flag{name, usage, value, value.String()}
_, alreadythere := f.formal[name]
if alreadythere {
- msg := fmt.Sprintf("%s flag redefined: %s", f.name, name)
+ var msg string
+ if f.name == "" {
+ msg = fmt.Sprintf("flag redefined: %s", name)
+ } else {
+ msg = fmt.Sprintf("%s flag redefined: %s", f.name, name)
+ }
fmt.Fprintln(f.out(), msg)
panic(msg) // Happens only if flags are declared with identical names
}
@@ -650,7 +685,7 @@ func (f *FlagSet) Var(value Value, name string, usage string) {
// of strings by giving the slice the methods of Value; in particular, Set would
// decompose the comma-separated string into the slice.
func Var(value Value, name string, usage string) {
- commandLine.Var(value, name, usage)
+ CommandLine.Var(value, name, usage)
}
// failf prints to standard error a formatted error and usage message and
@@ -663,9 +698,9 @@ func (f *FlagSet) failf(format string, a ...interface{}) error {
}
// usage calls the Usage method for the flag set, or the usage function if
-// the flag set is commandLine.
+// the flag set is CommandLine.
func (f *FlagSet) usage() {
- if f == commandLine {
+ if f == CommandLine {
Usage()
} else if f.Usage == nil {
defaultUsage(f)
@@ -674,7 +709,7 @@ func (f *FlagSet) usage() {
}
}
-// parseOne parses one flag. It returns whether a flag was seen.
+// parseOne parses one flag. It reports whether a flag was seen.
func (f *FlagSet) parseOne() (bool, error) {
if len(f.args) == 0 {
return false, nil
@@ -781,17 +816,19 @@ func (f *FlagSet) Parsed() bool {
// Parse parses the command-line flags from os.Args[1:]. Must be called
// after all flags are defined and before flags are accessed by the program.
func Parse() {
- // Ignore errors; commandLine is set for ExitOnError.
- commandLine.Parse(os.Args[1:])
+ // Ignore errors; CommandLine is set for ExitOnError.
+ CommandLine.Parse(os.Args[1:])
}
// Parsed returns true if the command-line flags have been parsed.
func Parsed() bool {
- return commandLine.Parsed()
+ return CommandLine.Parsed()
}
-// The default set of command-line flags, parsed from os.Args.
-var commandLine = NewFlagSet(os.Args[0], ExitOnError)
+// CommandLine is the default set of command-line flags, parsed from os.Args.
+// The top-level functions such as BoolVar, Arg, and on are wrappers for the
+// methods of CommandLine.
+var CommandLine = NewFlagSet(os.Args[0], ExitOnError)
// NewFlagSet returns a new, empty flag set with the specified name and
// error handling property.
diff --git a/src/pkg/flag/flag_test.go b/src/pkg/flag/flag_test.go
index ddd54b277..2c0387269 100644
--- a/src/pkg/flag/flag_test.go
+++ b/src/pkg/flag/flag_test.go
@@ -92,10 +92,54 @@ func TestEverything(t *testing.T) {
}
}
+func TestGet(t *testing.T) {
+ ResetForTesting(nil)
+ Bool("test_bool", true, "bool value")
+ Int("test_int", 1, "int value")
+ Int64("test_int64", 2, "int64 value")
+ Uint("test_uint", 3, "uint value")
+ Uint64("test_uint64", 4, "uint64 value")
+ String("test_string", "5", "string value")
+ Float64("test_float64", 6, "float64 value")
+ Duration("test_duration", 7, "time.Duration value")
+
+ visitor := func(f *Flag) {
+ if len(f.Name) > 5 && f.Name[0:5] == "test_" {
+ g, ok := f.Value.(Getter)
+ if !ok {
+ t.Errorf("Visit: value does not satisfy Getter: %T", f.Value)
+ return
+ }
+ switch f.Name {
+ case "test_bool":
+ ok = g.Get() == true
+ case "test_int":
+ ok = g.Get() == int(1)
+ case "test_int64":
+ ok = g.Get() == int64(2)
+ case "test_uint":
+ ok = g.Get() == uint(3)
+ case "test_uint64":
+ ok = g.Get() == uint64(4)
+ case "test_string":
+ ok = g.Get() == "5"
+ case "test_float64":
+ ok = g.Get() == float64(6)
+ case "test_duration":
+ ok = g.Get() == time.Duration(7)
+ }
+ if !ok {
+ t.Errorf("Visit: bad value %T(%v) for %s", g.Get(), g.Get(), f.Name)
+ }
+ }
+ }
+ VisitAll(visitor)
+}
+
func TestUsage(t *testing.T) {
called := false
ResetForTesting(func() { called = true })
- if CommandLine().Parse([]string{"-x"}) == nil {
+ if CommandLine.Parse([]string{"-x"}) == nil {
t.Error("parse did not fail for unknown flag")
}
if !called {
@@ -171,7 +215,7 @@ func testParse(f *FlagSet, t *testing.T) {
func TestParse(t *testing.T) {
ResetForTesting(func() { t.Error("bad parse") })
- testParse(CommandLine(), t)
+ testParse(CommandLine, t)
}
func TestFlagSetParse(t *testing.T) {
@@ -267,7 +311,7 @@ func TestChangingArgs(t *testing.T) {
defer func() { os.Args = oldArgs }()
os.Args = []string{"cmd", "-before", "subcmd", "-after", "args"}
before := Bool("before", false, "")
- if err := CommandLine().Parse(os.Args[1:]); err != nil {
+ if err := CommandLine.Parse(os.Args[1:]); err != nil {
t.Fatal(err)
}
cmd := Arg(0)
diff --git a/src/pkg/fmt/doc.go b/src/pkg/fmt/doc.go
index b8dd995c7..095fd03b2 100644
--- a/src/pkg/fmt/doc.go
+++ b/src/pkg/fmt/doc.go
@@ -118,6 +118,28 @@
convert the value before recurring:
func (x X) String() string { return Sprintf("<%s>", string(x)) }
+ Explicit argument indexes:
+
+ In Printf, Sprintf, and Fprintf, the default behavior is for each
+ formatting verb to format successive arguments passed in the call.
+ However, the notation [n] immediately before the verb indicates that the
+ nth one-indexed argument is to be formatted instead. The same notation
+ before a '*' for a width or precision selects the argument index holding
+ the value. After processing a bracketed expression [n], arguments n+1,
+ n+2, etc. will be processed unless otherwise directed.
+
+ For example,
+ fmt.Sprintf("%[2]d %[1]d\n", 11, 22)
+ will yield "22, 11", while
+ fmt.Sprintf("%[3]*.[2]*[1]f", 12.0, 2, 6),
+ equivalent to
+ fmt.Sprintf("%6.2f", 12.0),
+ will yield " 12.00". Because an explicit index affects subsequent verbs,
+ this notation can be used to print the same values multiple times
+ by resetting the index for the first argument to be repeated:
+ fmt.Sprintf("%d %d %#[1]x %#x", 16, 17)
+ will yield "16 17 0x10 0x11".
+
Format errors:
If an invalid argument is given for a verb, such as providing
@@ -133,6 +155,9 @@
Non-int for width or precision: %!(BADWIDTH) or %!(BADPREC)
Printf("%*s", 4.5, "hi"): %!(BADWIDTH)hi
Printf("%.*s", 4.5, "hi"): %!(BADPREC)hi
+ Invalid or invalid use of argument index: %!(BADINDEX)
+ Printf("%*[2]d", 7): %!d(BADINDEX)
+ Printf("%.[2]d", 7): %!d(BADINDEX)
All errors begin with the string "%!" followed sometimes
by a single character (the verb) and end with a parenthesized
@@ -144,9 +169,9 @@
through the fmt package. For example, if a String method
calls panic("bad"), the resulting formatted message will look
like
- %s(PANIC=bad)
+ %!s(PANIC=bad)
- The %s just shows the print verb in use when the failure
+ The %!s just shows the print verb in use when the failure
occurred.
Scanning
@@ -190,6 +215,10 @@
stops if it does not, with the return value of the function
indicating the number of arguments scanned.
+ In all the scanning functions, a carriage return followed
+ immediately by a newline is treated as a plain newline
+ (\r\n means the same as \n).
+
In all the scanning functions, if an operand implements method
Scan (that is, it implements the Scanner interface) that
method will be used to scan the text for that operand. Also,
diff --git a/src/pkg/fmt/fmt_test.go b/src/pkg/fmt/fmt_test.go
index 20b723a99..bf50675f5 100644
--- a/src/pkg/fmt/fmt_test.go
+++ b/src/pkg/fmt/fmt_test.go
@@ -110,7 +110,7 @@ var bslice = barray[:]
var b byte
-var fmttests = []struct {
+var fmtTests = []struct {
fmt string
val interface{}
out string
@@ -227,6 +227,8 @@ var fmttests = []struct {
{"%+.3g", -1.0, "-1"},
{"% .3g", -1.0, "-1"},
{"% .3g", 1.0, " 1"},
+ {"%b", float32(1.0), "8388608p-23"},
+ {"%b", 1.0, "4503599627370496p-52"},
// complex values
{"%+.3e", 0i, "(+0.000e+00+0.000e+00i)"},
@@ -247,6 +249,8 @@ var fmttests = []struct {
{"% .3E", -1 - 2i, "(-1.000E+00-2.000E+00i)"},
{"%+.3g", complex64(1 + 2i), "(+1+2i)"},
{"%+.3g", complex128(1 + 2i), "(+1+2i)"},
+ {"%b", complex64(1 + 2i), "(8388608p-23+8388608p-22i)"},
+ {"%b", 1 + 2i, "(4503599627370496p-52+4503599627370496p-51i)"},
// erroneous formats
{"", 2, "%!(EXTRA int=2)"},
@@ -493,6 +497,17 @@ var fmttests = []struct {
// Used to crash because nByte didn't allow for a sign.
{"%b", int64(-1 << 63), "-1000000000000000000000000000000000000000000000000000000000000000"},
+ // Used to panic.
+ {"%0100d", 1, "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"},
+ {"%0100d", -1, "-000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"},
+ {"%0.100f", 1.0, "1.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"},
+ {"%0.100f", -1.0, "-1.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"},
+
+ // Zero padding floats used to put the minus sign in the middle.
+ {"%020f", -1.0, "-000000000001.000000"},
+ {"%20f", -1.0, " -1.000000"},
+ {"%0100f", -1.0, "-00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001.000000"},
+
// Complex fmt used to leave the plus flag set for future entries in the array
// causing +2+0i and +3+0i instead of 2+0i and 3+0i.
{"%v", []complex64{1, 2, 3}, "[(1+0i) (2+0i) (3+0i)]"},
@@ -503,7 +518,7 @@ var fmttests = []struct {
}
func TestSprintf(t *testing.T) {
- for _, tt := range fmttests {
+ for _, tt := range fmtTests {
s := Sprintf(tt.fmt, tt.val)
if i := strings.Index(tt.out, "PTR"); i >= 0 {
pattern := "PTR"
@@ -539,6 +554,55 @@ func TestSprintf(t *testing.T) {
}
}
+type SE []interface{} // slice of empty; notational compactness.
+
+var reorderTests = []struct {
+ fmt string
+ val SE
+ out string
+}{
+ {"%[1]d", SE{1}, "1"},
+ {"%[2]d", SE{2, 1}, "1"},
+ {"%[2]d %[1]d", SE{1, 2}, "2 1"},
+ {"%[2]*[1]d", SE{2, 5}, " 2"},
+ {"%6.2f", SE{12.0}, " 12.00"}, // Explicit version of next line.
+ {"%[3]*.[2]*[1]f", SE{12.0, 2, 6}, " 12.00"},
+ {"%[1]*.[2]*[3]f", SE{6, 2, 12.0}, " 12.00"},
+ {"%10f", SE{12.0}, " 12.000000"},
+ {"%[1]*[3]f", SE{10, 99, 12.0}, " 12.000000"},
+ {"%.6f", SE{12.0}, "12.000000"}, // Explicit version of next line.
+ {"%.[1]*[3]f", SE{6, 99, 12.0}, "12.000000"},
+ {"%6.f", SE{12.0}, " 12"}, // // Explicit version of next line; empty precision means zero.
+ {"%[1]*.[3]f", SE{6, 3, 12.0}, " 12"},
+ // An actual use! Print the same arguments twice.
+ {"%d %d %d %#[1]o %#o %#o", SE{11, 12, 13}, "11 12 13 013 014 015"},
+
+ // Erroneous cases.
+ {"%[d", SE{2, 1}, "%!d(BADINDEX)"},
+ {"%]d", SE{2, 1}, "%!](int=2)d%!(EXTRA int=1)"},
+ {"%[]d", SE{2, 1}, "%!d(BADINDEX)"},
+ {"%[-3]d", SE{2, 1}, "%!d(BADINDEX)"},
+ {"%[99]d", SE{2, 1}, "%!d(BADINDEX)"},
+ {"%[3]", SE{2, 1}, "%!(NOVERB)"},
+ {"%[1].2d", SE{5, 6}, "%!d(BADINDEX)"},
+ {"%[1]2d", SE{2, 1}, "%!d(BADINDEX)"},
+ {"%3.[2]d", SE{7}, "%!d(BADINDEX)"},
+ {"%.[2]d", SE{7}, "%!d(BADINDEX)"},
+ {"%d %d %d %#[1]o %#o %#o %#o", SE{11, 12, 13}, "11 12 13 013 014 015 %!o(MISSING)"},
+ {"%[5]d %[2]d %d", SE{1, 2, 3}, "%!d(BADINDEX) 2 3"},
+ {"%d %[3]d %d", SE{1, 2}, "1 %!d(BADINDEX) 2"}, // Erroneous index does not affect sequence.
+}
+
+func TestReorder(t *testing.T) {
+ for _, tt := range reorderTests {
+ s := Sprintf(tt.fmt, tt.val...)
+ if s != tt.out {
+ t.Errorf("Sprintf(%q, %v) = <%s> want <%s>", tt.fmt, tt.val, s, tt.out)
+ } else {
+ }
+ }
+}
+
func BenchmarkSprintfEmpty(b *testing.B) {
for i := 0; i < b.N; i++ {
Sprintf("")
@@ -605,6 +669,9 @@ var mallocTest = []struct {
var _ bytes.Buffer
func TestCountMallocs(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping malloc count in short mode")
+ }
if runtime.GOMAXPROCS(0) > 1 {
t.Skip("skipping; GOMAXPROCS>1")
}
@@ -830,16 +897,16 @@ var panictests = []struct {
}{
// String
{"%s", (*Panic)(nil), "<nil>"}, // nil pointer special case
- {"%s", Panic{io.ErrUnexpectedEOF}, "%s(PANIC=unexpected EOF)"},
- {"%s", Panic{3}, "%s(PANIC=3)"},
+ {"%s", Panic{io.ErrUnexpectedEOF}, "%!s(PANIC=unexpected EOF)"},
+ {"%s", Panic{3}, "%!s(PANIC=3)"},
// GoString
{"%#v", (*Panic)(nil), "<nil>"}, // nil pointer special case
- {"%#v", Panic{io.ErrUnexpectedEOF}, "%v(PANIC=unexpected EOF)"},
- {"%#v", Panic{3}, "%v(PANIC=3)"},
+ {"%#v", Panic{io.ErrUnexpectedEOF}, "%!v(PANIC=unexpected EOF)"},
+ {"%#v", Panic{3}, "%!v(PANIC=3)"},
// Format
{"%s", (*PanicF)(nil), "<nil>"}, // nil pointer special case
- {"%s", PanicF{io.ErrUnexpectedEOF}, "%s(PANIC=unexpected EOF)"},
- {"%s", PanicF{3}, "%s(PANIC=3)"},
+ {"%s", PanicF{io.ErrUnexpectedEOF}, "%!s(PANIC=unexpected EOF)"},
+ {"%s", PanicF{3}, "%!s(PANIC=3)"},
}
func TestPanics(t *testing.T) {
@@ -859,7 +926,7 @@ type Recur struct {
failed *bool
}
-func (r Recur) String() string {
+func (r *Recur) String() string {
if recurCount++; recurCount > 10 {
*r.failed = true
return "FAIL"
@@ -872,13 +939,13 @@ func (r Recur) String() string {
func TestBadVerbRecursion(t *testing.T) {
failed := false
- r := Recur{3, &failed}
+ r := &Recur{3, &failed}
Sprintf("recur@%p value: %d\n", &r, r.i)
if failed {
t.Error("fail with pointer")
}
failed = false
- r = Recur{4, &failed}
+ r = &Recur{4, &failed}
Sprintf("recur@%p, value: %d\n", r, r.i)
if failed {
t.Error("fail with value")
diff --git a/src/pkg/fmt/format.go b/src/pkg/fmt/format.go
index 5665db12c..2e2b0716e 100644
--- a/src/pkg/fmt/format.go
+++ b/src/pkg/fmt/format.go
@@ -24,8 +24,6 @@ const (
var padZeroBytes = make([]byte, nByte)
var padSpaceBytes = make([]byte, nByte)
-var newline = []byte{'\n'}
-
func init() {
for i := 0; i < nByte; i++ {
padZeroBytes[i] = '0'
@@ -162,6 +160,11 @@ func (f *fmt) integer(a int64, base uint64, signedness bool, digits string) {
}
var buf []byte = f.intbuf[0:]
+ if f.widPresent && f.wid > nByte {
+ // We're going to need a bigger boat.
+ buf = make([]byte, f.wid)
+ }
+
negative := signedness == signed && a < 0
if negative {
a = -a
@@ -184,7 +187,7 @@ func (f *fmt) integer(a int64, base uint64, signedness bool, digits string) {
// a is made into unsigned ua. we could make things
// marginally faster by splitting the 32-bit case out into a separate
// block but it's not worth the duplication, so ua has 64 bits.
- i := len(f.intbuf)
+ i := len(buf)
ua := uint64(a)
for ua >= base {
i--
@@ -193,7 +196,7 @@ func (f *fmt) integer(a int64, base uint64, signedness bool, digits string) {
}
i--
buf[i] = digits[ua]
- for i > 0 && prec > nByte-i {
+ for i > 0 && prec > len(buf)-i {
i--
buf[i] = '0'
}
@@ -356,6 +359,14 @@ func (f *fmt) formatFloat(v float64, verb byte, prec, n int) {
// The formatted number starts at slice[1].
switch slice[1] {
case '-', '+':
+ // If we're zero padding, want the sign before the leading zeros.
+ // Achieve this by writing the sign out and padding the postive number.
+ if f.zero && f.widPresent && f.wid > len(slice) {
+ f.buf.WriteByte(slice[1])
+ f.wid--
+ f.pad(slice[2:])
+ return
+ }
// We're set; drop the leading space.
slice = slice[1:]
default:
@@ -418,6 +429,8 @@ func (f *fmt) fmt_c64(v complex64, verb rune) {
oldPlus := f.plus
for i := 0; ; i++ {
switch verb {
+ case 'b':
+ f.fmt_fb32(r)
case 'e':
f.fmt_e32(r)
case 'E':
@@ -446,6 +459,8 @@ func (f *fmt) fmt_c128(v complex128, verb rune) {
oldPlus := f.plus
for i := 0; ; i++ {
switch verb {
+ case 'b':
+ f.fmt_fb64(r)
case 'e':
f.fmt_e64(r)
case 'E':
diff --git a/src/pkg/fmt/print.go b/src/pkg/fmt/print.go
index 5f37fd120..1ea816d6d 100644
--- a/src/pkg/fmt/print.go
+++ b/src/pkg/fmt/print.go
@@ -16,19 +16,21 @@ import (
// 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)")
- panicBytes = []byte("(PANIC=")
- extraBytes = []byte("%!(EXTRA ")
- irparenBytes = []byte("i)")
- bytesBytes = []byte("[]byte{")
- badWidthBytes = []byte("%!(BADWIDTH)")
- badPrecBytes = []byte("%!(BADPREC)")
- noVerbBytes = []byte("%!(NOVERB)")
+ commaSpaceBytes = []byte(", ")
+ nilAngleBytes = []byte("<nil>")
+ nilParenBytes = []byte("(nil)")
+ nilBytes = []byte("nil")
+ mapBytes = []byte("map[")
+ percentBangBytes = []byte("%!")
+ missingBytes = []byte("(MISSING)")
+ badIndexBytes = []byte("(BADINDEX)")
+ panicBytes = []byte("(PANIC=")
+ extraBytes = []byte("%!(EXTRA ")
+ irparenBytes = []byte("i)")
+ bytesBytes = []byte("[]byte{")
+ badWidthBytes = []byte("%!(BADWIDTH)")
+ badPrecBytes = []byte("%!(BADPREC)")
+ noVerbBytes = []byte("%!(NOVERB)")
)
// State represents the printer state passed to custom formatters.
@@ -42,7 +44,7 @@ type State interface {
// 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 reports whether the flag c, a character, has been set.
Flag(c int) bool
}
@@ -109,13 +111,17 @@ type pp struct {
panicking bool
erroring bool // printing an error condition
buf buffer
- // field holds the current item, as an interface{}.
- field interface{}
+ // arg holds the current item, as an interface{}.
+ arg interface{}
// value holds the current item, as a reflect.Value, and will be
// the zero Value if the item has not been reflected.
- value reflect.Value
- runeBuf [utf8.UTFMax]byte
- fmt fmt
+ value reflect.Value
+ // reordered records whether the format string used argument reordering.
+ reordered bool
+ // goodArgNum records whether the most recent reordering directive was valid.
+ goodArgNum bool
+ runeBuf [utf8.UTFMax]byte
+ fmt fmt
}
// A cache holds a set of reusable objects.
@@ -170,7 +176,7 @@ func (p *pp) free() {
return
}
p.buf = p.buf[:0]
- p.field = nil
+ p.arg = nil
p.value = reflect.Value{}
ppFree.put(p)
}
@@ -212,9 +218,9 @@ func (p *pp) Write(b []byte) (ret int, err error) {
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
p := newPrinter()
p.doPrintf(format, a)
- n64, err := w.Write(p.buf)
+ n, err = w.Write(p.buf)
p.free()
- return int(n64), err
+ return
}
// Printf formats according to a format specifier and writes to standard output.
@@ -246,9 +252,9 @@ func Errorf(format string, a ...interface{}) error {
func Fprint(w io.Writer, a ...interface{}) (n int, err error) {
p := newPrinter()
p.doPrint(a, false, false)
- n64, err := w.Write(p.buf)
+ n, err = w.Write(p.buf)
p.free()
- return int(n64), err
+ return
}
// Print formats using the default formats for its operands and writes to standard output.
@@ -278,9 +284,9 @@ func Sprint(a ...interface{}) string {
func Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
p := newPrinter()
p.doPrint(a, true, true)
- n64, err := w.Write(p.buf)
+ n, err = w.Write(p.buf)
p.free()
- return int(n64), err
+ return
}
// Println formats using the default formats for its operands and writes to standard output.
@@ -300,8 +306,8 @@ func Sprintln(a ...interface{}) string {
return s
}
-// getField gets the i'th arg of the struct value.
-// If the arg itself is an interface, return a value for
+// getField gets the i'th field of the struct value.
+// If the field is 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)
@@ -340,10 +346,10 @@ func (p *pp) badVerb(verb rune) {
p.add(verb)
p.add('(')
switch {
- case p.field != nil:
- p.buf.WriteString(reflect.TypeOf(p.field).String())
+ case p.arg != nil:
+ p.buf.WriteString(reflect.TypeOf(p.arg).String())
p.add('=')
- p.printField(p.field, 'v', false, false, 0)
+ p.printArg(p.arg, 'v', false, false, 0)
case p.value.IsValid():
p.buf.WriteString(p.value.Type().String())
p.add('=')
@@ -505,7 +511,7 @@ func (p *pp) fmtFloat64(v float64, verb rune) {
func (p *pp) fmtComplex64(v complex64, verb rune) {
switch verb {
- case 'e', 'E', 'f', 'F', 'g', 'G':
+ case 'b', 'e', 'E', 'f', 'F', 'g', 'G':
p.fmt.fmt_c64(v, verb)
case 'v':
p.fmt.fmt_c64(v, 'g')
@@ -516,7 +522,7 @@ func (p *pp) fmtComplex64(v complex64, verb rune) {
func (p *pp) fmtComplex128(v complex128, verb rune) {
switch verb {
- case 'e', 'E', 'f', 'F', 'g', 'G':
+ case 'b', 'e', 'E', 'f', 'F', 'g', 'G':
p.fmt.fmt_c128(v, verb)
case 'v':
p.fmt.fmt_c128(v, 'g')
@@ -566,7 +572,7 @@ func (p *pp) fmtBytes(v []byte, verb rune, goSyntax bool, typ reflect.Type, dept
p.buf.WriteByte(' ')
}
}
- p.printField(c, 'v', p.fmt.plus, goSyntax, depth+1)
+ p.printArg(c, 'v', p.fmt.plus, goSyntax, depth+1)
}
if goSyntax {
p.buf.WriteByte('}')
@@ -635,31 +641,29 @@ func (p *pp) fmtPointer(value reflect.Value, verb rune, goSyntax bool) {
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) catchPanic(field interface{}, verb rune) {
+func (p *pp) catchPanic(arg interface{}, verb rune) {
if err := recover(); err != nil {
// If it's a nil pointer, just say "<nil>". The likeliest causes are a
// Stringer that fails to guard against nil or a nil pointer for a
// value receiver, and in either case, "<nil>" is a nice result.
- if v := reflect.ValueOf(field); v.Kind() == reflect.Ptr && v.IsNil() {
+ if v := reflect.ValueOf(arg); v.Kind() == reflect.Ptr && v.IsNil() {
p.buf.Write(nilAngleBytes)
return
}
// Otherwise print a concise panic message. Most of the time the panic
// value will print itself nicely.
if p.panicking {
- // Nested panics; the recursion in printField cannot succeed.
+ // Nested panics; the recursion in printArg cannot succeed.
panic(err)
}
- p.buf.WriteByte('%')
+ p.buf.Write(percentBangBytes)
p.add(verb)
p.buf.Write(panicBytes)
p.panicking = true
- p.printField(err, 'v', false, false, 0)
+ p.printArg(err, 'v', false, false, 0)
p.panicking = false
p.buf.WriteByte(')')
}
@@ -670,10 +674,10 @@ func (p *pp) handleMethods(verb rune, plus, goSyntax bool, depth int) (wasString
return
}
// Is it a Formatter?
- if formatter, ok := p.field.(Formatter); ok {
+ if formatter, ok := p.arg.(Formatter); ok {
handled = true
wasString = false
- defer p.catchPanic(p.field, verb)
+ defer p.catchPanic(p.arg, verb)
formatter.Format(p, verb)
return
}
@@ -682,13 +686,13 @@ func (p *pp) handleMethods(verb rune, plus, goSyntax bool, depth int) (wasString
p.fmt.plus = false
}
- // If we're doing Go syntax and the field knows how to supply it, take care of it now.
+ // If we're doing Go syntax and the argument knows how to supply it, take care of it now.
if goSyntax {
p.fmt.sharp = false
- if stringer, ok := p.field.(GoStringer); ok {
+ if stringer, ok := p.arg.(GoStringer); ok {
wasString = false
handled = true
- defer p.catchPanic(p.field, verb)
+ defer p.catchPanic(p.arg, verb)
// Print the result of GoString unadorned.
p.fmtString(stringer.GoString(), 's', false)
return
@@ -703,19 +707,19 @@ func (p *pp) handleMethods(verb rune, plus, goSyntax bool, depth int) (wasString
// The duplication in the bodies is necessary:
// setting wasString and handled, and deferring catchPanic,
// must happen before calling the method.
- switch v := p.field.(type) {
+ switch v := p.arg.(type) {
case error:
wasString = false
handled = true
- defer p.catchPanic(p.field, verb)
- p.printField(v.Error(), verb, plus, false, depth)
+ defer p.catchPanic(p.arg, verb)
+ p.printArg(v.Error(), verb, plus, false, depth)
return
case Stringer:
wasString = false
handled = true
- defer p.catchPanic(p.field, verb)
- p.printField(v.String(), verb, plus, false, depth)
+ defer p.catchPanic(p.arg, verb)
+ p.printArg(v.String(), verb, plus, false, depth)
return
}
}
@@ -724,11 +728,11 @@ func (p *pp) handleMethods(verb rune, plus, goSyntax bool, depth int) (wasString
return
}
-func (p *pp) printField(field interface{}, verb rune, plus, goSyntax bool, depth int) (wasString bool) {
- p.field = field
+func (p *pp) printArg(arg interface{}, verb rune, plus, goSyntax bool, depth int) (wasString bool) {
+ p.arg = arg
p.value = reflect.Value{}
- if field == nil {
+ if arg == nil {
if verb == 'T' || verb == 'v' {
p.fmt.pad(nilAngleBytes)
} else {
@@ -741,10 +745,10 @@ func (p *pp) printField(field interface{}, verb rune, plus, goSyntax bool, depth
// %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)
+ p.printArg(reflect.TypeOf(arg).String(), 's', false, false, 0)
return false
case 'p':
- p.fmtPointer(reflect.ValueOf(field), verb, goSyntax)
+ p.fmtPointer(reflect.ValueOf(arg), verb, goSyntax)
return false
}
@@ -762,7 +766,7 @@ func (p *pp) printField(field interface{}, verb rune, plus, goSyntax bool, depth
}
// Some types can be done without reflection.
- switch f := field.(type) {
+ switch f := arg.(type) {
case bool:
p.fmtBool(f, verb)
case float32:
@@ -770,7 +774,7 @@ func (p *pp) printField(field interface{}, verb rune, plus, goSyntax bool, depth
case float64:
p.fmtFloat64(f, verb)
case complex64:
- p.fmtComplex64(complex64(f), verb)
+ p.fmtComplex64(f, verb)
case complex128:
p.fmtComplex128(f, verb)
case int:
@@ -806,17 +810,17 @@ func (p *pp) printField(field interface{}, verb rune, plus, goSyntax bool, depth
p.fmt.plus = oldPlus
p.fmt.sharp = oldSharp
// If the type is not simple, it might have methods.
- if wasString, handled := p.handleMethods(verb, plus, goSyntax, depth); handled {
- return wasString
+ if isString, handled := p.handleMethods(verb, plus, goSyntax, depth); handled {
+ return isString
}
// Need to use reflection
- return p.printReflectValue(reflect.ValueOf(field), verb, plus, goSyntax, depth)
+ return p.printReflectValue(reflect.ValueOf(arg), verb, plus, goSyntax, depth)
}
- p.field = nil
+ p.arg = nil
return
}
-// printValue is like printField but starts with a reflect value, not an interface{} value.
+// printValue is like printArg but starts with a reflect value, not an interface{} value.
func (p *pp) printValue(value reflect.Value, verb rune, plus, goSyntax bool, depth int) (wasString bool) {
if !value.IsValid() {
if verb == 'T' || verb == 'v' {
@@ -831,7 +835,7 @@ func (p *pp) printValue(value reflect.Value, verb rune, plus, goSyntax bool, dep
// %T (the value's type) and %p (its address) are special; we always do them first.
switch verb {
case 'T':
- p.printField(value.Type().String(), 's', false, false, 0)
+ p.printArg(value.Type().String(), 's', false, false, 0)
return false
case 'p':
p.fmtPointer(value, verb, goSyntax)
@@ -839,19 +843,19 @@ func (p *pp) printValue(value reflect.Value, verb rune, plus, goSyntax bool, dep
}
// Handle values with special methods.
- // Call always, even when field == nil, because handleMethods clears p.fmt.plus for us.
- p.field = nil // Make sure it's cleared, for safety.
+ // Call always, even when arg == nil, because handleMethods clears p.fmt.plus for us.
+ p.arg = nil // Make sure it's cleared, for safety.
if value.CanInterface() {
- p.field = value.Interface()
+ p.arg = value.Interface()
}
- if wasString, handled := p.handleMethods(verb, plus, goSyntax, depth); handled {
- return wasString
+ if isString, handled := p.handleMethods(verb, plus, goSyntax, depth); handled {
+ return isString
}
return p.printReflectValue(value, verb, plus, goSyntax, depth)
}
-// printReflectValue is the fallback for both printField and printValue.
+// printReflectValue is the fallback for both printArg and printValue.
// It uses reflect to print the value.
func (p *pp) printReflectValue(value reflect.Value, verb rune, plus, goSyntax bool, depth int) (wasString bool) {
oldValue := p.value
@@ -863,18 +867,18 @@ BigSwitch:
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
p.fmtInt64(f.Int(), verb)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- p.fmtUint64(uint64(f.Uint()), verb, goSyntax)
+ p.fmtUint64(f.Uint(), verb, goSyntax)
case reflect.Float32, reflect.Float64:
if f.Type().Size() == 4 {
p.fmtFloat32(float32(f.Float()), verb)
} else {
- p.fmtFloat64(float64(f.Float()), verb)
+ p.fmtFloat64(f.Float(), verb)
}
case reflect.Complex64, reflect.Complex128:
if f.Type().Size() == 8 {
p.fmtComplex64(complex64(f.Complex()), verb)
} else {
- p.fmtComplex128(complex128(f.Complex()), verb)
+ p.fmtComplex128(f.Complex(), verb)
}
case reflect.String:
p.fmtString(f.String(), verb, goSyntax)
@@ -1015,20 +1019,59 @@ BigSwitch:
return wasString
}
-// 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
+// intFromArg gets the argNumth element of a. On return, isInt reports whether the argument has type int.
+func intFromArg(a []interface{}, argNum int) (num int, isInt bool, newArgNum int) {
+ newArgNum = argNum
+ if argNum < len(a) {
+ num, isInt = a[argNum].(int)
+ newArgNum = argNum + 1
}
return
}
+// parseArgNumber returns the value of the bracketed number, minus 1
+// (explicit argument numbers are one-indexed but we want zero-indexed).
+// The opening bracket is known to be present at format[0].
+// The returned values are the index, the number of bytes to consume
+// up to the closing paren, if present, and whether the number parsed
+// ok. The bytes to consume will be 1 if no closing paren is present.
+func parseArgNumber(format string) (index int, wid int, ok bool) {
+ // Find closing bracket.
+ for i := 1; i < len(format); i++ {
+ if format[i] == ']' {
+ width, ok, newi := parsenum(format, 1, i)
+ if !ok || newi != i {
+ return 0, i + 1, false
+ }
+ return width - 1, i + 1, true // arg numbers are one-indexed and skip paren.
+ }
+ }
+ return 0, 1, false
+}
+
+// argNumber returns the next argument to evaluate, which is either the value of the passed-in
+// argNum or the value of the bracketed integer that begins format[i:]. It also returns
+// the new value of i, that is, the index of the next byte of the format to process.
+func (p *pp) argNumber(argNum int, format string, i int, numArgs int) (newArgNum, newi int, found bool) {
+ if len(format) <= i || format[i] != '[' {
+ return argNum, i, false
+ }
+ p.reordered = true
+ index, wid, ok := parseArgNumber(format[i:])
+ if ok && 0 <= index && index < numArgs {
+ return index, i + wid, true
+ }
+ p.goodArgNum = false
+ return argNum, i + wid, true
+}
+
func (p *pp) doPrintf(format string, a []interface{}) {
end := len(format)
- fieldnum := 0 // we process one field per non-trivial format
+ argNum := 0 // we process one argument per non-trivial format
+ afterIndex := false // previous item in format was an index like [3].
+ p.reordered = false
for i := 0; i < end; {
+ p.goodArgNum = true
lasti := i
for i < end && format[i] != '%' {
i++
@@ -1043,7 +1086,8 @@ func (p *pp) doPrintf(format string, a []interface{}) {
// Process one verb
i++
- // flags and widths
+
+ // Do we have flags?
p.fmt.clearflags()
F:
for ; i < end; i++ {
@@ -1062,30 +1106,52 @@ func (p *pp) doPrintf(format string, a []interface{}) {
break F
}
}
- // do we have width?
+
+ // Do we have an explicit argument index?
+ argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a))
+
+ // Do we have width?
if i < end && format[i] == '*' {
- p.fmt.wid, p.fmt.widPresent, i, fieldnum = intFromArg(a, end, i, fieldnum)
+ i++
+ p.fmt.wid, p.fmt.widPresent, argNum = intFromArg(a, argNum)
if !p.fmt.widPresent {
p.buf.Write(badWidthBytes)
}
+ afterIndex = false
} else {
p.fmt.wid, p.fmt.widPresent, i = parsenum(format, i, end)
+ if afterIndex && p.fmt.widPresent { // "%[3]2d"
+ p.goodArgNum = false
+ }
}
- // do we have precision?
+
+ // Do we have precision?
if i+1 < end && format[i] == '.' {
- if format[i+1] == '*' {
- p.fmt.prec, p.fmt.precPresent, i, fieldnum = intFromArg(a, end, i+1, fieldnum)
+ i++
+ if afterIndex { // "%[3].2d"
+ p.goodArgNum = false
+ }
+ argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a))
+ if format[i] == '*' {
+ i++
+ p.fmt.prec, p.fmt.precPresent, argNum = intFromArg(a, argNum)
if !p.fmt.precPresent {
p.buf.Write(badPrecBytes)
}
+ afterIndex = false
} else {
- p.fmt.prec, p.fmt.precPresent, i = parsenum(format, i+1, end)
+ p.fmt.prec, p.fmt.precPresent, i = parsenum(format, i, end)
if !p.fmt.precPresent {
p.fmt.prec = 0
p.fmt.precPresent = true
}
}
}
+
+ if !afterIndex {
+ argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a))
+ }
+
if i >= end {
p.buf.Write(noVerbBytes)
continue
@@ -1097,30 +1163,38 @@ func (p *pp) doPrintf(format string, a []interface{}) {
p.buf.WriteByte('%') // We ignore width and prec.
continue
}
- if fieldnum >= len(a) { // out of operands
- p.buf.WriteByte('%')
+ if !p.goodArgNum {
+ p.buf.Write(percentBangBytes)
+ p.add(c)
+ p.buf.Write(badIndexBytes)
+ continue
+ } else if argNum >= len(a) { // out of operands
+ p.buf.Write(percentBangBytes)
p.add(c)
p.buf.Write(missingBytes)
continue
}
- field := a[fieldnum]
- fieldnum++
+ arg := a[argNum]
+ argNum++
goSyntax := c == 'v' && p.fmt.sharp
plus := c == 'v' && p.fmt.plus
- p.printField(field, c, plus, goSyntax, 0)
+ p.printArg(arg, c, plus, goSyntax, 0)
}
- if fieldnum < len(a) {
+ // Check for extra arguments unless the call accessed the arguments
+ // out of order, in which case it's too expensive to detect if they've all
+ // been used and arguably OK if they're not.
+ if !p.reordered && argNum < len(a) {
p.buf.Write(extraBytes)
- for ; fieldnum < len(a); fieldnum++ {
- field := a[fieldnum]
- if field != nil {
- p.buf.WriteString(reflect.TypeOf(field).String())
+ for ; argNum < len(a); argNum++ {
+ arg := a[argNum]
+ if arg != nil {
+ p.buf.WriteString(reflect.TypeOf(arg).String())
p.buf.WriteByte('=')
}
- p.printField(field, 'v', false, false, 0)
- if fieldnum+1 < len(a) {
+ p.printArg(arg, 'v', false, false, 0)
+ if argNum+1 < len(a) {
p.buf.Write(commaSpaceBytes)
}
}
@@ -1130,17 +1204,17 @@ func (p *pp) doPrintf(format string, a []interface{}) {
func (p *pp) doPrint(a []interface{}, addspace, addnewline bool) {
prevString := false
- for fieldnum := 0; fieldnum < len(a); fieldnum++ {
+ for argNum := 0; argNum < len(a); argNum++ {
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
+ arg := a[argNum]
+ if argNum > 0 {
+ isString := arg != nil && reflect.TypeOf(arg).Kind() == reflect.String
if addspace || !isString && !prevString {
p.buf.WriteByte(' ')
}
}
- prevString = p.printField(field, 'v', false, false, 0)
+ prevString = p.printArg(arg, 'v', false, false, 0)
}
if addnewline {
p.buf.WriteByte('\n')
diff --git a/src/pkg/fmt/scan.go b/src/pkg/fmt/scan.go
index bf888c4d8..5b1be5891 100644
--- a/src/pkg/fmt/scan.go
+++ b/src/pkg/fmt/scan.go
@@ -168,12 +168,12 @@ type ss struct {
// 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.
+ 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
+ argLimit int // max value of ss.count for this arg; argLimit <= limit
+ limit int // max value of ss.count.
+ maxWid int // width of this arg.
}
// The Read method is only in ScanState so that ScanState
@@ -192,7 +192,7 @@ func (s *ss) ReadRune() (r rune, size int, err error) {
s.peekRune = -1
return
}
- if s.atEOF || s.nlIsEnd && s.prevRune == '\n' || s.count >= s.fieldLimit {
+ if s.atEOF || s.nlIsEnd && s.prevRune == '\n' || s.count >= s.argLimit {
err = io.EOF
return
}
@@ -389,7 +389,7 @@ func newScanState(r io.Reader, nlIsSpace, nlIsEnd bool) (s *ss, old ssave) {
s, ok := r.(*ss)
if ok {
old = s.ssave
- s.limit = s.fieldLimit
+ s.limit = s.argLimit
s.nlIsEnd = nlIsEnd || s.nlIsEnd
s.nlIsSpace = nlIsSpace
return
@@ -407,7 +407,7 @@ func newScanState(r io.Reader, nlIsSpace, nlIsEnd bool) (s *ss, old ssave) {
s.peekRune = -1
s.atEOF = false
s.limit = hugeWid
- s.fieldLimit = hugeWid
+ s.argLimit = hugeWid
s.maxWid = hugeWid
s.validSave = true
s.count = 0
@@ -437,6 +437,9 @@ func (s *ss) skipSpace(stopAtNewline bool) {
if r == eof {
return
}
+ if r == '\r' && s.peek("\n") {
+ continue
+ }
if r == '\n' {
if stopAtNewline {
break
@@ -476,11 +479,6 @@ func (s *ss) token(skipSpace bool, f func(rune) bool) []byte {
return s.buf
}
-// 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 = errors.New("syntax error scanning complex number")
var boolError = errors.New("syntax error scanning boolean")
@@ -781,7 +779,7 @@ func (s *ss) convertFloat(str string, n int) float64 {
}
s.error(err)
}
- n, err := strconv.Atoi(str[p+1:])
+ m, err := strconv.Atoi(str[p+1:])
if err != nil {
// Put full string into error.
if e, ok := err.(*strconv.NumError); ok {
@@ -789,7 +787,7 @@ func (s *ss) convertFloat(str string, n int) float64 {
}
s.error(err)
}
- return math.Ldexp(f, n)
+ return math.Ldexp(f, m)
}
f, err := strconv.ParseFloat(str, n)
if err != nil {
@@ -858,8 +856,7 @@ func (s *ss) quotedString() string {
// 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.
- r := s.mustReadRune()
- s.buf.WriteRune(r)
+ s.buf.WriteRune(s.mustReadRune())
} else if r == '"' {
break
}
@@ -886,7 +883,7 @@ func (s *ss) hexDigit(d rune) int {
case 'A', 'B', 'C', 'D', 'E', 'F':
return 10 + digit - 'A'
}
- s.errorString("Scan: illegal hex digit")
+ s.errorString("illegal hex digit")
return 0
}
@@ -916,7 +913,7 @@ func (s *ss) hexString() string {
s.buf.WriteByte(b)
}
if len(s.buf) == 0 {
- s.errorString("Scan: no hex data for %x string")
+ s.errorString("no hex data for %x string")
return ""
}
return string(s.buf)
@@ -927,11 +924,11 @@ 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 rune, field interface{}) {
+func (s *ss) scanOne(verb rune, arg interface{}) {
s.buf = s.buf[:0]
var err error
// If the parameter has its own Scan method, use that.
- if v, ok := field.(Scanner); ok {
+ if v, ok := arg.(Scanner); ok {
err = v.Scan(s, verb)
if err != nil {
if err == io.EOF {
@@ -942,7 +939,7 @@ func (s *ss) scanOne(verb rune, field interface{}) {
return
}
- switch v := field.(type) {
+ switch v := arg.(type) {
case *bool:
*v = s.scanBool(verb)
case *complex64:
@@ -995,7 +992,7 @@ func (s *ss) scanOne(verb rune, field interface{}) {
val := reflect.ValueOf(v)
ptr := val
if ptr.Kind() != reflect.Ptr {
- s.errorString("Scan: type not a pointer: " + val.Type().String())
+ s.errorString("type not a pointer: " + val.Type().String())
return
}
switch v := ptr.Elem(); v.Kind() {
@@ -1011,7 +1008,7 @@ func (s *ss) scanOne(verb rune, field interface{}) {
// For now, can only handle (renamed) []byte.
typ := v.Type()
if typ.Elem().Kind() != reflect.Uint8 {
- s.errorString("Scan: can't handle type: " + val.Type().String())
+ s.errorString("can't scan type: " + val.Type().String())
}
str := s.convertString(verb)
v.Set(reflect.MakeSlice(typ, len(str), len(str)))
@@ -1025,7 +1022,7 @@ func (s *ss) scanOne(verb rune, field interface{}) {
case reflect.Complex64, reflect.Complex128:
v.SetComplex(s.scanComplex(verb, v.Type().Bits()))
default:
- s.errorString("Scan: can't handle type: " + val.Type().String())
+ s.errorString("can't scan type: " + val.Type().String())
}
}
}
@@ -1046,8 +1043,8 @@ func errorHandler(errp *error) {
// doScan does the real work for scanning without a format string.
func (s *ss) doScan(a []interface{}) (numProcessed int, err error) {
defer errorHandler(&err)
- for _, field := range a {
- s.scanOne('v', field)
+ for _, arg := range a {
+ s.scanOne('v', arg)
numProcessed++
}
// Check for newline if required.
@@ -1058,7 +1055,7 @@ func (s *ss) doScan(a []interface{}) (numProcessed int, err error) {
break
}
if !isSpace(r) {
- s.errorString("Scan: expected newline")
+ s.errorString("expected newline")
break
}
}
@@ -1144,9 +1141,9 @@ func (s *ss) doScanf(format string, a []interface{}) (numProcessed int, err erro
if !widPresent {
s.maxWid = hugeWid
}
- s.fieldLimit = s.limit
- if f := s.count + s.maxWid; f < s.fieldLimit {
- s.fieldLimit = f
+ s.argLimit = s.limit
+ if f := s.count + s.maxWid; f < s.argLimit {
+ s.argLimit = f
}
c, w := utf8.DecodeRuneInString(format[i:])
@@ -1156,11 +1153,11 @@ func (s *ss) doScanf(format string, a []interface{}) (numProcessed int, err erro
s.errorString("too few operands for format %" + format[i-w:])
break
}
- field := a[numProcessed]
+ arg := a[numProcessed]
- s.scanOne(c, field)
+ s.scanOne(c, arg)
numProcessed++
- s.fieldLimit = s.limit
+ s.argLimit = s.limit
}
if numProcessed < len(a) {
s.errorString("too many operands")
diff --git a/src/pkg/fmt/scan_test.go b/src/pkg/fmt/scan_test.go
index 4e2c0feb2..d903f0c3f 100644
--- a/src/pkg/fmt/scan_test.go
+++ b/src/pkg/fmt/scan_test.go
@@ -54,7 +54,6 @@ var (
float32Val float32
float64Val float64
stringVal string
- stringVal1 string
bytesVal []byte
runeVal rune
complex64Val complex64
@@ -192,6 +191,10 @@ var scanTests = []ScanTest{
{"-.45e1-1e2i\n", &complex128Val, complex128(-.45e1 - 100i)},
{"hello\n", &stringVal, "hello"},
+ // Carriage-return followed by newline. (We treat \r\n as \n always.)
+ {"hello\r\n", &stringVal, "hello"},
+ {"27\r\n", &uint8Val, uint8(27)},
+
// Renamed types
{"true\n", &renamedBoolVal, renamedBool(true)},
{"F\n", &renamedBoolVal, renamedBool(false)},
diff --git a/src/pkg/go/ast/ast.go b/src/pkg/go/ast/ast.go
index bf533d1d2..6e635cd01 100644
--- a/src/pkg/go/ast/ast.go
+++ b/src/pkg/go/ast/ast.go
@@ -297,6 +297,8 @@ type (
Lbrack token.Pos // position of "["
Low Expr // begin of slice range; or nil
High Expr // end of slice range; or nil
+ Max Expr // maximum capacity of slice; or nil
+ Slice3 bool // true if 3-index slice (2 colons present)
Rbrack token.Pos // position of "]"
}
@@ -304,8 +306,10 @@ type (
// type assertion.
//
TypeAssertExpr struct {
- X Expr // expression
- Type Expr // asserted type; nil means type switch X.(type)
+ X Expr // expression
+ Lparen token.Pos // position of "("
+ Type Expr // asserted type; nil means type switch X.(type)
+ Rparen token.Pos // position of ")"
}
// A CallExpr node represents an expression followed by an argument list.
@@ -385,8 +389,8 @@ type (
// A FuncType node represents a function type.
FuncType struct {
- Func token.Pos // position of "func" keyword
- Params *FieldList // (incoming) parameters; or nil
+ Func token.Pos // position of "func" keyword (token.NoPos if there is no "func")
+ Params *FieldList // (incoming) parameters; non-nil
Results *FieldList // (outgoing) results; or nil
}
@@ -407,7 +411,7 @@ type (
// A ChanType node represents a channel type.
ChanType struct {
Begin token.Pos // position of "chan" keyword or "<-" (whichever comes first)
- Arrow token.Pos // position of "<-" (noPos if there is no "<-")
+ Arrow token.Pos // position of "<-" (token.NoPos if there is no "<-")
Dir ChanDir // channel direction
Value Expr // value type
}
@@ -438,10 +442,15 @@ func (x *BinaryExpr) Pos() token.Pos { return x.X.Pos() }
func (x *KeyValueExpr) Pos() token.Pos { return x.Key.Pos() }
func (x *ArrayType) Pos() token.Pos { return x.Lbrack }
func (x *StructType) Pos() token.Pos { return x.Struct }
-func (x *FuncType) Pos() token.Pos { return x.Func }
-func (x *InterfaceType) Pos() token.Pos { return x.Interface }
-func (x *MapType) Pos() token.Pos { return x.Map }
-func (x *ChanType) Pos() token.Pos { return x.Begin }
+func (x *FuncType) Pos() token.Pos {
+ if x.Func.IsValid() || x.Params == nil { // see issue 3870
+ return x.Func
+ }
+ return x.Params.Pos() // interface method declarations have no "func" keyword
+}
+func (x *InterfaceType) Pos() token.Pos { return x.Interface }
+func (x *MapType) Pos() token.Pos { return x.Map }
+func (x *ChanType) Pos() token.Pos { return x.Begin }
func (x *BadExpr) End() token.Pos { return x.To }
func (x *Ident) End() token.Pos { return token.Pos(int(x.NamePos) + len(x.Name)) }
@@ -451,26 +460,21 @@ func (x *Ellipsis) End() token.Pos {
}
return x.Ellipsis + 3 // len("...")
}
-func (x *BasicLit) End() token.Pos { return token.Pos(int(x.ValuePos) + len(x.Value)) }
-func (x *FuncLit) End() token.Pos { return x.Body.End() }
-func (x *CompositeLit) End() token.Pos { return x.Rbrace + 1 }
-func (x *ParenExpr) End() token.Pos { return x.Rparen + 1 }
-func (x *SelectorExpr) End() token.Pos { return x.Sel.End() }
-func (x *IndexExpr) End() token.Pos { return x.Rbrack + 1 }
-func (x *SliceExpr) End() token.Pos { return x.Rbrack + 1 }
-func (x *TypeAssertExpr) End() token.Pos {
- if x.Type != nil {
- return x.Type.End()
- }
- return x.X.End()
-}
-func (x *CallExpr) End() token.Pos { return x.Rparen + 1 }
-func (x *StarExpr) End() token.Pos { return x.X.End() }
-func (x *UnaryExpr) End() token.Pos { return x.X.End() }
-func (x *BinaryExpr) End() token.Pos { return x.Y.End() }
-func (x *KeyValueExpr) End() token.Pos { return x.Value.End() }
-func (x *ArrayType) End() token.Pos { return x.Elt.End() }
-func (x *StructType) End() token.Pos { return x.Fields.End() }
+func (x *BasicLit) End() token.Pos { return token.Pos(int(x.ValuePos) + len(x.Value)) }
+func (x *FuncLit) End() token.Pos { return x.Body.End() }
+func (x *CompositeLit) End() token.Pos { return x.Rbrace + 1 }
+func (x *ParenExpr) End() token.Pos { return x.Rparen + 1 }
+func (x *SelectorExpr) End() token.Pos { return x.Sel.End() }
+func (x *IndexExpr) End() token.Pos { return x.Rbrack + 1 }
+func (x *SliceExpr) End() token.Pos { return x.Rbrack + 1 }
+func (x *TypeAssertExpr) End() token.Pos { return x.Rparen + 1 }
+func (x *CallExpr) End() token.Pos { return x.Rparen + 1 }
+func (x *StarExpr) End() token.Pos { return x.X.End() }
+func (x *UnaryExpr) End() token.Pos { return x.X.End() }
+func (x *BinaryExpr) End() token.Pos { return x.Y.End() }
+func (x *KeyValueExpr) End() token.Pos { return x.Value.End() }
+func (x *ArrayType) End() token.Pos { return x.Elt.End() }
+func (x *StructType) End() token.Pos { return x.Fields.End() }
func (x *FuncType) End() token.Pos {
if x.Results != nil {
return x.Results.End()
@@ -511,23 +515,21 @@ func (*ChanType) exprNode() {}
// ----------------------------------------------------------------------------
// Convenience functions for Idents
-var noPos token.Pos
-
// NewIdent creates a new Ident without position.
// Useful for ASTs generated by code other than the Go parser.
//
-func NewIdent(name string) *Ident { return &Ident{noPos, name, nil} }
+func NewIdent(name string) *Ident { return &Ident{token.NoPos, name, nil} }
-// IsExported returns whether name is an exported Go symbol
-// (i.e., whether it begins with an uppercase letter).
+// IsExported reports whether name is an exported Go symbol
+// (that is, whether it begins with an upper-case letter).
//
func IsExported(name string) bool {
ch, _ := utf8.DecodeRuneInString(name)
return unicode.IsUpper(ch)
}
-// IsExported returns whether id is an exported Go symbol
-// (i.e., whether it begins with an uppercase letter).
+// IsExported reports whether id is an exported Go symbol
+// (that is, whether it begins with an uppercase letter).
//
func (id *Ident) IsExported() bool { return IsExported(id.Name) }
@@ -919,7 +921,7 @@ type (
Doc *CommentGroup // associated documentation; or nil
Recv *FieldList // receiver (methods); or nil (functions)
Name *Ident // function/method name
- Type *FuncType // position of Func keyword, parameters and results
+ Type *FuncType // function signature: parameters, results, and position of "func" keyword
Body *BlockStmt // function body; or nil (forward declaration)
}
)
diff --git a/src/pkg/go/ast/commentmap.go b/src/pkg/go/ast/commentmap.go
index 252d460af..1fb4867dd 100644
--- a/src/pkg/go/ast/commentmap.go
+++ b/src/pkg/go/ast/commentmap.go
@@ -129,11 +129,11 @@ func (s *nodeStack) pop(pos token.Pos) (top Node) {
//
// A comment group g is associated with a node n if:
//
-// - g starts on the same line as n ends
-// - g starts on the line immediately following n, and there is
-// at least one empty line after g and before the next node
-// - g starts before n and is not associated to the node before n
-// via the previous rules
+// - g starts on the same line as n ends
+// - g starts on the line immediately following n, and there is
+// at least one empty line after g and before the next node
+// - g starts before n and is not associated to the node before n
+// via the previous rules
//
// NewCommentMap tries to associate a comment group to the "largest"
// node possible: For instance, if the comment is a line comment
diff --git a/src/pkg/go/ast/filter.go b/src/pkg/go/ast/filter.go
index 71c9ed776..fc3eeb4a1 100644
--- a/src/pkg/go/ast/filter.go
+++ b/src/pkg/go/ast/filter.go
@@ -308,7 +308,7 @@ func nameOf(f *FuncDecl) string {
// 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, "//"}
+var separator = &Comment{token.NoPos, "//"}
// MergePackageFiles creates a file AST by merging the ASTs of the
// files belonging to a package. The mode flags control merging behavior.
diff --git a/src/pkg/go/ast/import.go b/src/pkg/go/ast/import.go
index a68a4840f..d2770d16c 100644
--- a/src/pkg/go/ast/import.go
+++ b/src/pkg/go/ast/import.go
@@ -11,6 +11,7 @@ import (
)
// SortImports sorts runs of consecutive import lines in import blocks in f.
+// It also removes duplicate imports when it is possible to do so without data loss.
func SortImports(fset *token.FileSet, f *File) {
for _, d := range f.Decls {
d, ok := d.(*GenDecl)
@@ -27,14 +28,25 @@ func SortImports(fset *token.FileSet, f *File) {
// Identify and sort runs of specs on successive lines.
i := 0
+ specs := d.Specs[:0]
for j, s := range d.Specs {
if j > i && fset.Position(s.Pos()).Line > 1+fset.Position(d.Specs[j-1].End()).Line {
// j begins a new run. End this one.
- sortSpecs(fset, f, d.Specs[i:j])
+ specs = append(specs, sortSpecs(fset, f, d.Specs[i:j])...)
i = j
}
}
- sortSpecs(fset, f, d.Specs[i:])
+ specs = append(specs, sortSpecs(fset, f, d.Specs[i:])...)
+ d.Specs = specs
+
+ // Deduping can leave a blank line before the rparen; clean that up.
+ if len(d.Specs) > 0 {
+ lastSpec := d.Specs[len(d.Specs)-1]
+ lastLine := fset.Position(lastSpec.Pos()).Line
+ if rParenLine := fset.Position(d.Rparen).Line; rParenLine > lastLine+1 {
+ fset.File(d.Rparen).MergeLine(rParenLine - 1)
+ }
+ }
}
}
@@ -46,22 +58,41 @@ func importPath(s Spec) string {
return ""
}
+func importName(s Spec) string {
+ n := s.(*ImportSpec).Name
+ if n == nil {
+ return ""
+ }
+ return n.Name
+}
+
+func importComment(s Spec) string {
+ c := s.(*ImportSpec).Comment
+ if c == nil {
+ return ""
+ }
+ return c.Text()
+}
+
+// collapse indicates whether prev may be removed, leaving only next.
+func collapse(prev, next Spec) bool {
+ if importPath(next) != importPath(prev) || importName(next) != importName(prev) {
+ return false
+ }
+ return prev.(*ImportSpec).Comment == nil
+}
+
type posSpan struct {
Start token.Pos
End token.Pos
}
-func sortSpecs(fset *token.FileSet, f *File, specs []Spec) {
- // Avoid work if already sorted (also catches < 2 entries).
- sorted := true
- for i, s := range specs {
- if i > 0 && importPath(specs[i-1]) > importPath(s) {
- sorted = false
- break
- }
- }
- if sorted {
- return
+func sortSpecs(fset *token.FileSet, f *File, specs []Spec) []Spec {
+ // Can't short-circuit here even if specs are already sorted,
+ // since they might yet need deduplication.
+ // A lone import, however, may be safely ignored.
+ if len(specs) <= 1 {
+ return specs
}
// Record positions for specs.
@@ -101,10 +132,26 @@ func sortSpecs(fset *token.FileSet, f *File, specs []Spec) {
}
// Sort the import specs by import path.
+ // Remove duplicates, when possible without data loss.
// Reassign the import paths to have the same position sequence.
// Reassign each comment to abut the end of its spec.
// Sort the comments by new position.
- sort.Sort(byImportPath(specs))
+ sort.Sort(byImportSpec(specs))
+
+ // Dedup. Thanks to our sorting, we can just consider
+ // adjacent pairs of imports.
+ deduped := specs[:0]
+ for i, s := range specs {
+ if i == len(specs)-1 || !collapse(s, specs[i+1]) {
+ deduped = append(deduped, s)
+ } else {
+ p := s.Pos()
+ fset.File(p).MergeLine(fset.Position(p).Line)
+ }
+ }
+ specs = deduped
+
+ // Fix up comment positions
for i, s := range specs {
s := s.(*ImportSpec)
if s.Name != nil {
@@ -118,14 +165,29 @@ func sortSpecs(fset *token.FileSet, f *File, specs []Spec) {
}
}
}
+
sort.Sort(byCommentPos(comments))
+
+ return specs
}
-type byImportPath []Spec // slice of *ImportSpec
+type byImportSpec []Spec // slice of *ImportSpec
-func (x byImportPath) Len() int { return len(x) }
-func (x byImportPath) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
-func (x byImportPath) Less(i, j int) bool { return importPath(x[i]) < importPath(x[j]) }
+func (x byImportSpec) Len() int { return len(x) }
+func (x byImportSpec) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
+func (x byImportSpec) Less(i, j int) bool {
+ ipath := importPath(x[i])
+ jpath := importPath(x[j])
+ if ipath != jpath {
+ return ipath < jpath
+ }
+ iname := importName(x[i])
+ jname := importName(x[j])
+ if iname != jname {
+ return iname < jname
+ }
+ return importComment(x[i]) < importComment(x[j])
+}
type byCommentPos []*CommentGroup
diff --git a/src/pkg/go/ast/walk.go b/src/pkg/go/ast/walk.go
index fef2503c3..fedffb3f2 100644
--- a/src/pkg/go/ast/walk.go
+++ b/src/pkg/go/ast/walk.go
@@ -122,6 +122,9 @@ func Walk(v Visitor, node Node) {
if n.High != nil {
Walk(v, n.High)
}
+ if n.Max != nil {
+ Walk(v, n.Max)
+ }
case *TypeAssertExpr:
Walk(v, n.X)
diff --git a/src/pkg/go/build/build.go b/src/pkg/go/build/build.go
index cc89afb21..50d2fb4ae 100644
--- a/src/pkg/go/build/build.go
+++ b/src/pkg/go/build/build.go
@@ -258,21 +258,23 @@ func (ctxt *Context) SrcDirs() []string {
var Default Context = defaultContext()
var cgoEnabled = map[string]bool{
- "darwin/386": true,
- "darwin/amd64": true,
- "freebsd/386": true,
- "freebsd/amd64": true,
- "freebsd/arm": true,
- "linux/386": true,
- "linux/amd64": true,
- "linux/arm": true,
- "netbsd/386": true,
- "netbsd/amd64": true,
- "netbsd/arm": true,
- "openbsd/386": true,
- "openbsd/amd64": true,
- "windows/386": true,
- "windows/amd64": true,
+ "darwin/386": true,
+ "darwin/amd64": true,
+ "dragonfly/386": true,
+ "dragonfly/amd64": true,
+ "freebsd/386": true,
+ "freebsd/amd64": true,
+ "freebsd/arm": true,
+ "linux/386": true,
+ "linux/amd64": true,
+ "linux/arm": true,
+ "netbsd/386": true,
+ "netbsd/amd64": true,
+ "netbsd/arm": true,
+ "openbsd/386": true,
+ "openbsd/amd64": true,
+ "windows/386": true,
+ "windows/amd64": true,
}
func defaultContext() Context {
@@ -293,7 +295,7 @@ func defaultContext() Context {
// When we reach Go 1.3 the line will read
// c.ReleaseTags = []string{"go1.1", "go1.2", "go1.3"}
// and so on.
- c.ReleaseTags = []string{"go1.1"}
+ c.ReleaseTags = []string{"go1.1", "go1.2"}
switch os.Getenv("CGO_ENABLED") {
case "1":
@@ -337,32 +339,37 @@ const (
// A Package describes the Go package found in a directory.
type Package struct {
- Dir string // directory containing package sources
- Name string // package name
- Doc string // documentation synopsis
- ImportPath string // import path of package ("" if unknown)
- Root string // root of Go tree where this package lives
- SrcRoot string // package source root directory ("" if unknown)
- PkgRoot string // package install root directory ("" if unknown)
- BinDir string // command install directory ("" if unknown)
- Goroot bool // package found in Go root
- PkgObj string // installed .a file
+ Dir string // directory containing package sources
+ Name string // package name
+ Doc string // documentation synopsis
+ ImportPath string // import path of package ("" if unknown)
+ Root string // root of Go tree where this package lives
+ SrcRoot string // package source root directory ("" if unknown)
+ PkgRoot string // package install root directory ("" if unknown)
+ BinDir string // command install directory ("" if unknown)
+ Goroot bool // package found in Go root
+ PkgObj string // installed .a file
+ AllTags []string // tags that can influence file selection in this directory
+ ConflictDir string // this directory shadows Dir in $GOPATH
// Source files
GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
CgoFiles []string // .go source files that import "C"
IgnoredGoFiles []string // .go source files ignored for this build
CFiles []string // .c source files
- HFiles []string // .h source files
+ CXXFiles []string // .cc, .cpp and .cxx source files
+ HFiles []string // .h, .hh, .hpp and .hxx source files
SFiles []string // .s source files
- SysoFiles []string // .syso system object files to add to archive
SwigFiles []string // .swig files
SwigCXXFiles []string // .swigcxx files
+ SysoFiles []string // .syso system object files to add to archive
// Cgo directives
- CgoPkgConfig []string // Cgo pkg-config directives
CgoCFLAGS []string // Cgo CFLAGS directives
+ CgoCPPFLAGS []string // Cgo CPPFLAGS directives
+ CgoCXXFLAGS []string // Cgo CXXFLAGS directives
CgoLDFLAGS []string // Cgo LDFLAGS directives
+ CgoPkgConfig []string // Cgo pkg-config directives
// Dependency information
Imports []string // imports from GoFiles, CgoFiles
@@ -391,13 +398,22 @@ func (ctxt *Context) ImportDir(dir string, mode ImportMode) (*Package, error) {
}
// NoGoError is the error used by Import to describe a directory
-// containing no Go source files.
+// containing no buildable Go source files. (It may still contain
+// test files, files hidden by build tags, and so on.)
type NoGoError struct {
Dir string
}
func (e *NoGoError) Error() string {
- return "no Go source files in " + e.Dir
+ return "no buildable Go source files in " + e.Dir
+}
+
+func nameExt(name string) string {
+ i := strings.LastIndex(name, ".")
+ if i < 0 {
+ return ""
+ }
+ return name[i:]
}
// Import returns details about the Go package named by the import path,
@@ -429,7 +445,7 @@ func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Packa
switch ctxt.Compiler {
case "gccgo":
dir, elem := pathpkg.Split(p.ImportPath)
- pkga = "pkg/gccgo/" + dir + "lib" + elem + ".a"
+ pkga = "pkg/gccgo_" + ctxt.GOOS + "_" + ctxt.GOARCH + "/" + dir + "lib" + elem + ".a"
case "gc":
suffix := ""
if ctxt.InstallSuffix != "" {
@@ -469,11 +485,13 @@ func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Packa
// else first.
if ctxt.GOROOT != "" {
if dir := ctxt.joinPath(ctxt.GOROOT, "src", "pkg", sub); ctxt.isDir(dir) {
+ p.ConflictDir = dir
goto Found
}
}
for _, earlyRoot := range all[:i] {
if dir := ctxt.joinPath(earlyRoot, "src", sub); ctxt.isDir(dir) {
+ p.ConflictDir = dir
goto Found
}
}
@@ -575,63 +593,21 @@ Found:
imported := make(map[string][]token.Position)
testImported := make(map[string][]token.Position)
xTestImported := make(map[string][]token.Position)
+ allTags := make(map[string]bool)
fset := token.NewFileSet()
for _, d := range dirs {
if d.IsDir() {
continue
}
- name := d.Name()
- if strings.HasPrefix(name, "_") ||
- strings.HasPrefix(name, ".") {
- continue
- }
-
- i := strings.LastIndex(name, ".")
- if i < 0 {
- i = len(name)
- }
- ext := name[i:]
-
- if !ctxt.UseAllFiles && !ctxt.goodOSArchFile(name) {
- if ext == ".go" {
- p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
- }
- continue
- }
- switch ext {
- case ".go", ".c", ".s", ".h", ".S", ".swig", ".swigcxx":
- // tentatively okay - read to make sure
- case ".syso":
- // binary objects to add to package archive
- // Likely of the form foo_windows.syso, but
- // the name was vetted above with goodOSArchFile.
- p.SysoFiles = append(p.SysoFiles, name)
- continue
- default:
- // skip
- continue
- }
+ name := d.Name()
+ ext := nameExt(name)
- filename := ctxt.joinPath(p.Dir, name)
- f, err := ctxt.openFile(filename)
+ match, data, filename, err := ctxt.matchFile(p.Dir, name, true, allTags)
if err != nil {
return p, err
}
-
- var data []byte
- if strings.HasSuffix(filename, ".go") {
- data, err = readImports(f, false)
- } else {
- data, err = readComments(f)
- }
- f.Close()
- if err != nil {
- return p, fmt.Errorf("read %s: %v", filename, err)
- }
-
- // Look for +build comments to accept or reject the file.
- if !ctxt.UseAllFiles && !ctxt.shouldBuild(data) {
+ if !match {
if ext == ".go" {
p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
}
@@ -643,7 +619,10 @@ Found:
case ".c":
p.CFiles = append(p.CFiles, name)
continue
- case ".h":
+ case ".cc", ".cpp", ".cxx":
+ p.CXXFiles = append(p.CXXFiles, name)
+ continue
+ case ".h", ".hh", ".hpp", ".hxx":
p.HFiles = append(p.HFiles, name)
continue
case ".s":
@@ -658,6 +637,12 @@ Found:
case ".swigcxx":
p.SwigCXXFiles = append(p.SwigCXXFiles, name)
continue
+ case ".syso":
+ // binary objects to add to package archive
+ // Likely of the form foo_windows.syso, but
+ // the name was vetted above with goodOSArchFile.
+ p.SysoFiles = append(p.SysoFiles, name)
+ continue
}
pf, err := parser.ParseFile(fset, filename, data, parser.ImportsOnly|parser.ParseComments)
@@ -730,8 +715,11 @@ Found:
}
}
if isCgo {
+ allTags["cgo"] = true
if ctxt.CgoEnabled {
p.CgoFiles = append(p.CgoFiles, name)
+ } else {
+ p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
}
} else if isXTest {
p.XTestGoFiles = append(p.XTestGoFiles, name)
@@ -741,10 +729,15 @@ Found:
p.GoFiles = append(p.GoFiles, name)
}
}
- if p.Name == "" {
+ if len(p.GoFiles)+len(p.CgoFiles)+len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 {
return p, &NoGoError{p.Dir}
}
+ for tag := range allTags {
+ p.AllTags = append(p.AllTags, tag)
+ }
+ sort.Strings(p.AllTags)
+
p.Imports, p.ImportPos = cleanImports(imported)
p.TestImports, p.TestImportPos = cleanImports(testImported)
p.XTestImports, p.XTestImportPos = cleanImports(xTestImported)
@@ -760,6 +753,79 @@ Found:
return p, pkgerr
}
+// MatchFile reports whether the file with the given name in the given directory
+// matches the context and would be included in a Package created by ImportDir
+// of that directory.
+//
+// MatchFile considers the name of the file and may use ctxt.OpenFile to
+// read some or all of the file's content.
+func (ctxt *Context) MatchFile(dir, name string) (match bool, err error) {
+ match, _, _, err = ctxt.matchFile(dir, name, false, nil)
+ return
+}
+
+// matchFile determines whether the file with the given name in the given directory
+// should be included in the package being constructed.
+// It returns the data read from the file.
+// If returnImports is true and name denotes a Go program, matchFile reads
+// until the end of the imports (and returns that data) even though it only
+// considers text until the first non-comment.
+// If allTags is non-nil, matchFile records any encountered build tag
+// by setting allTags[tag] = true.
+func (ctxt *Context) matchFile(dir, name string, returnImports bool, allTags map[string]bool) (match bool, data []byte, filename string, err error) {
+ if strings.HasPrefix(name, "_") ||
+ strings.HasPrefix(name, ".") {
+ return
+ }
+
+ i := strings.LastIndex(name, ".")
+ if i < 0 {
+ i = len(name)
+ }
+ ext := name[i:]
+
+ if !ctxt.goodOSArchFile(name, allTags) && !ctxt.UseAllFiles {
+ return
+ }
+
+ switch ext {
+ case ".go", ".c", ".cc", ".cxx", ".cpp", ".s", ".h", ".hh", ".hpp", ".hxx", ".S", ".swig", ".swigcxx":
+ // tentatively okay - read to make sure
+ case ".syso":
+ // binary, no reading
+ match = true
+ return
+ default:
+ // skip
+ return
+ }
+
+ filename = ctxt.joinPath(dir, name)
+ f, err := ctxt.openFile(filename)
+ if err != nil {
+ return
+ }
+
+ if strings.HasSuffix(filename, ".go") {
+ data, err = readImports(f, false)
+ } else {
+ data, err = readComments(f)
+ }
+ f.Close()
+ if err != nil {
+ err = fmt.Errorf("read %s: %v", filename, err)
+ return
+ }
+
+ // Look for +build comments to accept or reject the file.
+ if !ctxt.shouldBuild(data, allTags) && !ctxt.UseAllFiles {
+ return
+ }
+
+ match = true
+ return
+}
+
func cleanImports(m map[string][]token.Position) ([]string, map[string][]token.Position) {
all := make([]string, 0, len(m))
for path := range m {
@@ -794,7 +860,7 @@ var slashslash = []byte("//")
//
// marks the file as applicable only on Windows and Linux.
//
-func (ctxt *Context) shouldBuild(content []byte) bool {
+func (ctxt *Context) shouldBuild(content []byte, allTags map[string]bool) bool {
// Pass 1. Identify leading run of // comments and blank lines,
// which must be followed by a blank line.
end := 0
@@ -819,6 +885,7 @@ func (ctxt *Context) shouldBuild(content []byte) bool {
// Pass 2. Process each line in the run.
p = content
+ allok := true
for len(p) > 0 {
line := p
if i := bytes.IndexByte(line, '\n'); i >= 0 {
@@ -835,24 +902,24 @@ func (ctxt *Context) shouldBuild(content []byte) bool {
if f[0] == "+build" {
ok := false
for _, tok := range f[1:] {
- if ctxt.match(tok) {
+ if ctxt.match(tok, allTags) {
ok = true
- break
}
}
if !ok {
- return false // this one doesn't match
+ allok = false
}
}
}
}
}
- return true // everything matches
+
+ return allok
}
// saveCgo saves the information from the #cgo lines in the import "C" comment.
-// These lines set CFLAGS and LDFLAGS and pkg-config directives that affect
-// the way cgo's C code is built.
+// These lines set CFLAGS, CPPFLAGS, CXXFLAGS and LDFLAGS and pkg-config directives
+// that affect the way cgo's C code is built.
//
// TODO(rsc): This duplicates code in cgo.
// Once the dust settles, remove this code from cgo.
@@ -887,7 +954,7 @@ func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup)
if len(cond) > 0 {
ok := false
for _, c := range cond {
- if ctxt.match(c) {
+ if ctxt.match(c, nil) {
ok = true
break
}
@@ -902,7 +969,7 @@ func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup)
return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig)
}
for _, arg := range args {
- if !safeName(arg) {
+ if !safeCgoName(arg) {
return fmt.Errorf("%s: malformed #cgo argument: %s", filename, arg)
}
}
@@ -910,6 +977,10 @@ func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup)
switch verb {
case "CFLAGS":
di.CgoCFLAGS = append(di.CgoCFLAGS, args...)
+ case "CPPFLAGS":
+ di.CgoCPPFLAGS = append(di.CgoCPPFLAGS, args...)
+ case "CXXFLAGS":
+ di.CgoCXXFLAGS = append(di.CgoCXXFLAGS, args...)
case "LDFLAGS":
di.CgoLDFLAGS = append(di.CgoLDFLAGS, args...)
case "pkg-config":
@@ -921,9 +992,12 @@ func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup)
return nil
}
-var safeBytes = []byte("+-.,/0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz:")
+// NOTE: $ is not safe for the shell, but it is allowed here because of linker options like -Wl,$ORIGIN.
+// We never pass these arguments to a shell (just to programs we construct argv for), so this should be okay.
+// See golang.org/issue/6038.
+var safeBytes = []byte("+-.,/0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz:$")
-func safeName(s string) bool {
+func safeCgoName(s string) bool {
if s == "" {
return false
}
@@ -1008,19 +1082,28 @@ func splitQuoted(s string) (r []string, err error) {
// !tag (if tag is not listed in ctxt.BuildTags or ctxt.ReleaseTags)
// a comma-separated list of any of these
//
-func (ctxt *Context) match(name string) bool {
+func (ctxt *Context) match(name string, allTags map[string]bool) bool {
if name == "" {
+ if allTags != nil {
+ allTags[name] = true
+ }
return false
}
if i := strings.Index(name, ","); i >= 0 {
// comma-separated list
- return ctxt.match(name[:i]) && ctxt.match(name[i+1:])
+ ok1 := ctxt.match(name[:i], allTags)
+ ok2 := ctxt.match(name[i+1:], allTags)
+ return ok1 && ok2
}
if strings.HasPrefix(name, "!!") { // bad syntax, reject always
return false
}
if strings.HasPrefix(name, "!") { // negation
- return len(name) > 1 && !ctxt.match(name[1:])
+ return len(name) > 1 && !ctxt.match(name[1:], allTags)
+ }
+
+ if allTags != nil {
+ allTags[name] = true
}
// Tags must be letters, digits, underscores or dots.
@@ -1065,7 +1148,7 @@ func (ctxt *Context) match(name string) bool {
// name_$(GOARCH)_test.*
// name_$(GOOS)_$(GOARCH)_test.*
//
-func (ctxt *Context) goodOSArchFile(name string) bool {
+func (ctxt *Context) goodOSArchFile(name string, allTags map[string]bool) bool {
if dot := strings.Index(name, "."); dot != -1 {
name = name[:dot]
}
@@ -1075,12 +1158,22 @@ func (ctxt *Context) goodOSArchFile(name string) bool {
}
n := len(l)
if n >= 2 && knownOS[l[n-2]] && knownArch[l[n-1]] {
+ if allTags != nil {
+ allTags[l[n-2]] = true
+ allTags[l[n-1]] = true
+ }
return l[n-2] == ctxt.GOOS && l[n-1] == ctxt.GOARCH
}
if n >= 1 && knownOS[l[n-1]] {
+ if allTags != nil {
+ allTags[l[n-1]] = true
+ }
return l[n-1] == ctxt.GOOS
}
if n >= 1 && knownArch[l[n-1]] {
+ if allTags != nil {
+ allTags[l[n-1]] = true
+ }
return l[n-1] == ctxt.GOARCH
}
return true
diff --git a/src/pkg/go/build/build_test.go b/src/pkg/go/build/build_test.go
index d8cf98840..fca8d4bdb 100644
--- a/src/pkg/go/build/build_test.go
+++ b/src/pkg/go/build/build_test.go
@@ -5,38 +5,49 @@
package build
import (
+ "io"
"os"
"path/filepath"
+ "reflect"
"runtime"
+ "strings"
"testing"
)
func TestMatch(t *testing.T) {
ctxt := Default
what := "default"
- match := func(tag string) {
- if !ctxt.match(tag) {
+ match := func(tag string, want map[string]bool) {
+ m := make(map[string]bool)
+ if !ctxt.match(tag, m) {
t.Errorf("%s context should match %s, does not", what, tag)
}
+ if !reflect.DeepEqual(m, want) {
+ t.Errorf("%s tags = %v, want %v", tag, m, want)
+ }
}
- nomatch := func(tag string) {
- if ctxt.match(tag) {
+ nomatch := func(tag string, want map[string]bool) {
+ m := make(map[string]bool)
+ if ctxt.match(tag, m) {
t.Errorf("%s context should NOT match %s, does", what, tag)
}
+ if !reflect.DeepEqual(m, want) {
+ t.Errorf("%s tags = %v, want %v", tag, m, want)
+ }
}
- match(runtime.GOOS + "," + runtime.GOARCH)
- match(runtime.GOOS + "," + runtime.GOARCH + ",!foo")
- nomatch(runtime.GOOS + "," + runtime.GOARCH + ",foo")
+ match(runtime.GOOS+","+runtime.GOARCH, map[string]bool{runtime.GOOS: true, runtime.GOARCH: true})
+ match(runtime.GOOS+","+runtime.GOARCH+",!foo", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "foo": true})
+ nomatch(runtime.GOOS+","+runtime.GOARCH+",foo", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "foo": true})
what = "modified"
ctxt.BuildTags = []string{"foo"}
- match(runtime.GOOS + "," + runtime.GOARCH)
- match(runtime.GOOS + "," + runtime.GOARCH + ",foo")
- nomatch(runtime.GOOS + "," + runtime.GOARCH + ",!foo")
- match(runtime.GOOS + "," + runtime.GOARCH + ",!bar")
- nomatch(runtime.GOOS + "," + runtime.GOARCH + ",bar")
- nomatch("!")
+ match(runtime.GOOS+","+runtime.GOARCH, map[string]bool{runtime.GOOS: true, runtime.GOARCH: true})
+ match(runtime.GOOS+","+runtime.GOARCH+",foo", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "foo": true})
+ nomatch(runtime.GOOS+","+runtime.GOARCH+",!foo", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "foo": true})
+ match(runtime.GOOS+","+runtime.GOARCH+",!bar", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "bar": true})
+ nomatch(runtime.GOOS+","+runtime.GOARCH+",bar", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "bar": true})
+ nomatch("!", map[string]bool{})
}
func TestDotSlashImport(t *testing.T) {
@@ -92,28 +103,84 @@ func TestLocalDirectory(t *testing.T) {
func TestShouldBuild(t *testing.T) {
const file1 = "// +build tag1\n\n" +
"package main\n"
+ want1 := map[string]bool{"tag1": true}
const file2 = "// +build cgo\n\n" +
"// This package implements parsing of tags like\n" +
"// +build tag1\n" +
"package build"
+ want2 := map[string]bool{"cgo": true}
const file3 = "// Copyright The Go Authors.\n\n" +
"package build\n\n" +
"// shouldBuild checks tags given by lines of the form\n" +
"// +build tag\n" +
"func shouldBuild(content []byte)\n"
+ want3 := map[string]bool{}
ctx := &Context{BuildTags: []string{"tag1"}}
- if !ctx.shouldBuild([]byte(file1)) {
- t.Errorf("should not build file1, expected the contrary")
+ m := map[string]bool{}
+ if !ctx.shouldBuild([]byte(file1), m) {
+ t.Errorf("shouldBuild(file1) = false, want true")
+ }
+ if !reflect.DeepEqual(m, want1) {
+ t.Errorf("shoudBuild(file1) tags = %v, want %v", m, want1)
+ }
+
+ m = map[string]bool{}
+ if ctx.shouldBuild([]byte(file2), m) {
+ t.Errorf("shouldBuild(file2) = true, want fakse")
}
- if ctx.shouldBuild([]byte(file2)) {
- t.Errorf("should build file2, expected the contrary")
+ if !reflect.DeepEqual(m, want2) {
+ t.Errorf("shoudBuild(file2) tags = %v, want %v", m, want2)
}
+ m = map[string]bool{}
ctx = &Context{BuildTags: nil}
- if !ctx.shouldBuild([]byte(file3)) {
- t.Errorf("should not build file3, expected the contrary")
+ if !ctx.shouldBuild([]byte(file3), m) {
+ t.Errorf("shouldBuild(file3) = false, want true")
+ }
+ if !reflect.DeepEqual(m, want3) {
+ t.Errorf("shoudBuild(file3) tags = %v, want %v", m, want3)
+ }
+}
+
+type readNopCloser struct {
+ io.Reader
+}
+
+func (r readNopCloser) Close() error {
+ return nil
+}
+
+var matchFileTests = []struct {
+ name string
+ data string
+ match bool
+}{
+ {"foo_arm.go", "", true},
+ {"foo1_arm.go", "// +build linux\n\npackage main\n", false},
+ {"foo_darwin.go", "", false},
+ {"foo.go", "", true},
+ {"foo1.go", "// +build linux\n\npackage main\n", false},
+ {"foo.badsuffix", "", false},
+}
+
+func TestMatchFile(t *testing.T) {
+ for _, tt := range matchFileTests {
+ ctxt := Context{GOARCH: "arm", GOOS: "plan9"}
+ ctxt.OpenFile = func(path string) (r io.ReadCloser, err error) {
+ if path != "x+"+tt.name {
+ t.Fatalf("OpenFile asked for %q, expected %q", path, "x+"+tt.name)
+ }
+ return &readNopCloser{strings.NewReader(tt.data)}, nil
+ }
+ ctxt.JoinPath = func(elem ...string) string {
+ return strings.Join(elem, "+")
+ }
+ match, err := ctxt.MatchFile("x", tt.name)
+ if match != tt.match || err != nil {
+ t.Fatalf("MatchFile(%q) = %v, %v, want %v, nil", tt.name, match, err, tt.match)
+ }
}
}
diff --git a/src/pkg/go/build/deps_test.go b/src/pkg/go/build/deps_test.go
index 71b1bcf06..dd162c7db 100644
--- a/src/pkg/go/build/deps_test.go
+++ b/src/pkg/go/build/deps_test.go
@@ -82,24 +82,27 @@ var pkgDeps = map[string][]string{
// L3 adds reflection and some basic utility packages
// and interface definitions, but nothing that makes
// system calls.
- "crypto": {"L2", "hash"}, // interfaces
- "crypto/cipher": {"L2"}, // interfaces
- "encoding/base32": {"L2"},
- "encoding/base64": {"L2"},
- "encoding/binary": {"L2", "reflect"},
- "hash": {"L2"}, // interfaces
- "hash/adler32": {"L2", "hash"},
- "hash/crc32": {"L2", "hash"},
- "hash/crc64": {"L2", "hash"},
- "hash/fnv": {"L2", "hash"},
- "image": {"L2", "image/color"}, // interfaces
- "image/color": {"L2"}, // interfaces
- "reflect": {"L2"},
+ "crypto": {"L2", "hash"}, // interfaces
+ "crypto/cipher": {"L2", "crypto/subtle"}, // interfaces
+ "crypto/subtle": {},
+ "encoding/base32": {"L2"},
+ "encoding/base64": {"L2"},
+ "encoding/binary": {"L2", "reflect"},
+ "hash": {"L2"}, // interfaces
+ "hash/adler32": {"L2", "hash"},
+ "hash/crc32": {"L2", "hash"},
+ "hash/crc64": {"L2", "hash"},
+ "hash/fnv": {"L2", "hash"},
+ "image": {"L2", "image/color"}, // interfaces
+ "image/color": {"L2"}, // interfaces
+ "image/color/palette": {"L2", "image/color"},
+ "reflect": {"L2"},
"L3": {
"L2",
"crypto",
"crypto/cipher",
+ "crypto/subtle",
"encoding/base32",
"encoding/base64",
"encoding/binary",
@@ -110,6 +113,7 @@ var pkgDeps = map[string][]string{
"hash/fnv",
"image",
"image/color",
+ "image/color/palette",
"reflect",
},
@@ -183,26 +187,27 @@ var pkgDeps = map[string][]string{
"compress/gzip": {"L4", "compress/flate"},
"compress/lzw": {"L4"},
"compress/zlib": {"L4", "compress/flate"},
- "database/sql": {"L4", "database/sql/driver"},
+ "database/sql": {"L4", "container/list", "database/sql/driver"},
"database/sql/driver": {"L4", "time"},
"debug/dwarf": {"L4"},
"debug/elf": {"L4", "OS", "debug/dwarf"},
"debug/gosym": {"L4"},
"debug/macho": {"L4", "OS", "debug/dwarf"},
"debug/pe": {"L4", "OS", "debug/dwarf"},
+ "encoding": {"L4"},
"encoding/ascii85": {"L4"},
"encoding/asn1": {"L4", "math/big"},
"encoding/csv": {"L4"},
- "encoding/gob": {"L4", "OS"},
+ "encoding/gob": {"L4", "OS", "encoding"},
"encoding/hex": {"L4"},
- "encoding/json": {"L4"},
+ "encoding/json": {"L4", "encoding"},
"encoding/pem": {"L4"},
- "encoding/xml": {"L4"},
+ "encoding/xml": {"L4", "encoding"},
"flag": {"L4", "OS"},
"go/build": {"L4", "OS", "GOPARSER"},
"html": {"L4"},
"image/draw": {"L4"},
- "image/gif": {"L4", "compress/lzw"},
+ "image/gif": {"L4", "compress/lzw", "image/color/palette", "image/draw"},
"image/jpeg": {"L4"},
"image/png": {"L4", "compress/zlib"},
"index/suffixarray": {"L4", "regexp"},
@@ -228,7 +233,8 @@ var pkgDeps = map[string][]string{
// that shows up in programs that use cgo.
"C": {},
- "os/user": {"L4", "CGO", "syscall"},
+ // Plan 9 alone needs io/ioutil and os.
+ "os/user": {"L4", "CGO", "io/ioutil", "os", "syscall"},
// Basic networking.
// Because net must be used by any package that wants to
@@ -248,15 +254,10 @@ var pkgDeps = map[string][]string{
"net/mail": {"L4", "NET", "OS"},
"net/textproto": {"L4", "OS", "net"},
- // Support libraries for crypto that aren't L2.
- "CRYPTO-SUPPORT": {
- "crypto/subtle",
- },
-
// Core crypto.
"crypto/aes": {"L3"},
"crypto/des": {"L3"},
- "crypto/hmac": {"L3", "CRYPTO-SUPPORT"},
+ "crypto/hmac": {"L3"},
"crypto/md5": {"L3"},
"crypto/rc4": {"L3"},
"crypto/sha1": {"L3"},
@@ -264,7 +265,6 @@ var pkgDeps = map[string][]string{
"crypto/sha512": {"L3"},
"CRYPTO": {
- "CRYPTO-SUPPORT",
"crypto/aes",
"crypto/des",
"crypto/hmac",
@@ -359,7 +359,7 @@ func allowed(pkg string) map[string]bool {
}
var bools = []bool{false, true}
-var geese = []string{"darwin", "freebsd", "linux", "netbsd", "openbsd", "plan9", "windows"}
+var geese = []string{"darwin", "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "plan9", "windows"}
var goarches = []string{"386", "amd64", "arm"}
type osPkg struct {
@@ -392,6 +392,9 @@ func TestDependencies(t *testing.T) {
if allowedErrors[osPkg{ctxt.GOOS, pkg}] {
continue
}
+ if !ctxt.CgoEnabled && pkg == "runtime/cgo" {
+ continue
+ }
// Some of the combinations we try might not
// be reasonable (like arm,plan9,cgo), so ignore
// errors for the auto-generated combinations.
diff --git a/src/pkg/go/build/doc.go b/src/pkg/go/build/doc.go
index b5fc071d6..b2f04ea45 100644
--- a/src/pkg/go/build/doc.go
+++ b/src/pkg/go/build/doc.go
@@ -94,6 +94,7 @@
// - the compiler being used, either "gc" or "gccgo"
// - "cgo", if ctxt.CgoEnabled is true
// - "go1.1", from Go version 1.1 onward
+// - "go1.2", from Go version 1.2 onward
// - any additional words listed in ctxt.BuildTags
//
// If a file's name, after stripping the extension and a possible _test suffix,
diff --git a/src/pkg/go/build/syslist.go b/src/pkg/go/build/syslist.go
index ea21f3c74..e1fbf6330 100644
--- a/src/pkg/go/build/syslist.go
+++ b/src/pkg/go/build/syslist.go
@@ -4,5 +4,5 @@
package build
-const goosList = "darwin freebsd linux netbsd openbsd plan9 windows "
+const goosList = "darwin dragonfly freebsd linux netbsd openbsd plan9 windows "
const goarchList = "386 amd64 arm "
diff --git a/src/pkg/go/build/syslist_test.go b/src/pkg/go/build/syslist_test.go
index 9157faf8c..3be2928f5 100644
--- a/src/pkg/go/build/syslist_test.go
+++ b/src/pkg/go/build/syslist_test.go
@@ -55,7 +55,7 @@ var tests = []GoodFileTest{
func TestGoodOSArch(t *testing.T) {
for _, test := range tests {
- if Default.goodOSArchFile(test.name) != test.result {
+ if Default.goodOSArchFile(test.name, make(map[string]bool)) != test.result {
t.Fatalf("goodOSArchFile(%q) != %v", test.name, test.result)
}
}
diff --git a/src/pkg/go/doc/comment.go b/src/pkg/go/doc/comment.go
index c4b7e6ae6..5c8c43e0c 100644
--- a/src/pkg/go/doc/comment.go
+++ b/src/pkg/go/doc/comment.go
@@ -239,9 +239,14 @@ func anchorID(line string) string {
// 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 indented lines into a <pre> block without indent.
-// Enclose headings with header tags.
+// Each span of unindented non-blank lines is converted into
+// a single paragraph. There is one exception to the rule: a span that
+// consists of a single line, is followed by another paragraph span,
+// begins with a capital letter, and contains no punctuation
+// is formatted as a heading.
+//
+// A span of indented lines is converted into a <pre> block,
+// with the common indent prefix removed.
//
// URLs in the comment text are converted into links; if the URL also appears
// in the words map, the link is taken from the map (if the corresponding map
diff --git a/src/pkg/go/doc/doc_test.go b/src/pkg/go/doc/doc_test.go
index 8043038b4..ad8ba5378 100644
--- a/src/pkg/go/doc/doc_test.go
+++ b/src/pkg/go/doc/doc_test.go
@@ -32,6 +32,7 @@ func readTemplate(filename string) *template.Template {
t.Funcs(template.FuncMap{
"node": nodeFmt,
"synopsis": synopsisFmt,
+ "indent": indentFmt,
})
return template.Must(t.ParseFiles(filepath.Join(dataDir, filename)))
}
@@ -55,6 +56,15 @@ func synopsisFmt(s string) string {
return "// " + strings.Replace(s, "\n", " ", -1)
}
+func indentFmt(indent, s string) string {
+ end := ""
+ if strings.HasSuffix(s, "\n") {
+ end = "\n"
+ s = s[:len(s)-1]
+ }
+ return indent + strings.Replace(s, "\n", "\n"+indent, -1) + end
+}
+
func isGoFile(fi os.FileInfo) bool {
name := fi.Name()
return !fi.IsDir() &&
diff --git a/src/pkg/go/doc/example.go b/src/pkg/go/doc/example.go
index 2761083c7..2358ed389 100644
--- a/src/pkg/go/doc/example.go
+++ b/src/pkg/go/doc/example.go
@@ -265,7 +265,7 @@ func playExample(file *ast.File, body *ast.BlockStmt) *ast.File {
// Synthesize main function.
funcDecl := &ast.FuncDecl{
Name: ast.NewIdent("main"),
- Type: &ast.FuncType{},
+ Type: &ast.FuncType{Params: &ast.FieldList{}}, // FuncType.Params must be non-nil
Body: body,
}
diff --git a/src/pkg/go/doc/example_test.go b/src/pkg/go/doc/example_test.go
index e0477e3f6..e154ea8bf 100644
--- a/src/pkg/go/doc/example_test.go
+++ b/src/pkg/go/doc/example_test.go
@@ -159,8 +159,8 @@ func main() {
`
func TestExamples(t *testing.T) {
- fs := token.NewFileSet()
- file, err := parser.ParseFile(fs, "test.go", strings.NewReader(exampleTestFile), parser.ParseComments)
+ fset := token.NewFileSet()
+ file, err := parser.ParseFile(fset, "test.go", strings.NewReader(exampleTestFile), parser.ParseComments)
if err != nil {
t.Fatal(err)
}
@@ -174,11 +174,11 @@ func TestExamples(t *testing.T) {
if e.Play == nil {
g = "<nil>"
} else {
- b := new(bytes.Buffer)
- if err := format.Node(b, fs, e.Play); err != nil {
+ var buf bytes.Buffer
+ if err := format.Node(&buf, fset, e.Play); err != nil {
t.Fatal(err)
}
- g = b.String()
+ g = buf.String()
}
if g != w {
t.Errorf("%s: got Play == %q, want %q", c.Name, g, w)
diff --git a/src/pkg/go/doc/reader.go b/src/pkg/go/doc/reader.go
index 4fa6fd9d5..ed82c47cd 100644
--- a/src/pkg/go/doc/reader.go
+++ b/src/pkg/go/doc/reader.go
@@ -414,7 +414,7 @@ func (r *reader) readNote(list []*ast.Comment) {
// We remove any formatting so that we don't
// get spurious line breaks/indentation when
// showing the TODO body.
- body := clean(text[m[1]:])
+ body := clean(text[m[1]:], keepNL)
if body != "" {
marker := text[m[2]:m[3]]
r.notes[marker] = append(r.notes[marker], &Note{
diff --git a/src/pkg/go/doc/synopsis.go b/src/pkg/go/doc/synopsis.go
index 2d1817439..c90080b7c 100644
--- a/src/pkg/go/doc/synopsis.go
+++ b/src/pkg/go/doc/synopsis.go
@@ -22,19 +22,28 @@ func firstSentenceLen(s string) int {
if q == ' ' && p == '.' && (!unicode.IsUpper(pp) || unicode.IsUpper(ppp)) {
return i
}
+ if p == '。' || p == '.' {
+ return i
+ }
ppp, pp, p = pp, p, q
}
return len(s)
}
+const (
+ keepNL = 1 << iota
+)
+
// clean replaces each sequence of space, \n, \r, or \t characters
// with a single space and removes any trailing and leading spaces.
-func clean(s string) string {
+// If the keepNL flag is set, newline characters are passed through
+// instead of being change to spaces.
+func clean(s string, flags int) string {
var b []byte
p := byte(' ')
for i := 0; i < len(s); i++ {
q := s[i]
- if q == '\n' || q == '\r' || q == '\t' {
+ if (flags&keepNL) == 0 && q == '\n' || q == '\r' || q == '\t' {
q = ' '
}
if q != ' ' || p != ' ' {
@@ -57,7 +66,7 @@ func clean(s string) string {
// is the empty string.
//
func Synopsis(s string) string {
- s = clean(s[0:firstSentenceLen(s)])
+ s = clean(s[0:firstSentenceLen(s)], 0)
for _, prefix := range IllegalPrefixes {
if strings.HasPrefix(strings.ToLower(s), prefix) {
return ""
diff --git a/src/pkg/go/doc/synopsis_test.go b/src/pkg/go/doc/synopsis_test.go
index fd7081a07..59b253cb8 100644
--- a/src/pkg/go/doc/synopsis_test.go
+++ b/src/pkg/go/doc/synopsis_test.go
@@ -28,6 +28,8 @@ var tests = []struct {
{"P. Q. ", 8, "P. Q."},
{"Package Καλημέρα κόσμε.", 36, "Package Καλημέρα κόσμε."},
{"Package こんにちは 世界\n", 31, "Package こんにちは 世界"},
+ {"Package こんにちは。世界", 26, "Package こんにちは。"},
+ {"Package 안녕.世界", 17, "Package 안녕."},
{"Package foo does bar.", 21, "Package foo does bar."},
{"Copyright 2012 Google, Inc. Package foo does bar.", 27, ""},
{"All Rights reserved. Package foo does bar.", 20, ""},
diff --git a/src/pkg/go/doc/testdata/a.0.golden b/src/pkg/go/doc/testdata/a.0.golden
index cd98f4e0e..7e680b80b 100644
--- a/src/pkg/go/doc/testdata/a.0.golden
+++ b/src/pkg/go/doc/testdata/a.0.golden
@@ -9,24 +9,44 @@ FILENAMES
testdata/a1.go
BUGS .Bugs is now deprecated, please use .Notes instead
- // bug0
- // bug1
+ bug0
+
+ bug1
+
BUGS
- // bug0 (uid: uid)
- // bug1 (uid: uid)
+BUG(uid) bug0
+
+BUG(uid) bug1
+
NOTES
- // 1 of 4 - this is the first line of note 1 - note 1 continues on ... (uid: foo)
- // 2 of 4 (uid: foo)
- // 3 of 4 (uid: bar)
- // 4 of 4 - this is the last line of note 4 (uid: bar)
- // This note which contains a (parenthesized) subphrase must ... (uid: bam)
- // The ':' after the marker and uid is optional. (uid: xxx)
+NOTE(uid)
+
+NOTE(foo) 1 of 4 - this is the first line of note 1
+ - note 1 continues on this 2nd line
+ - note 1 continues on this 3rd line
+
+NOTE(foo) 2 of 4
+
+NOTE(bar) 3 of 4
+
+NOTE(bar) 4 of 4
+ - this is the last line of note 4
+
+NOTE(bam) This note which contains a (parenthesized) subphrase
+ must appear in its entirety.
+
+NOTE(xxx) The ':' after the marker and uid is optional.
+
SECBUGS
- // sec hole 0 need to fix asap (uid: uid)
+SECBUG(uid) sec hole 0
+ need to fix asap
+
TODOS
- // todo0 (uid: uid)
- // todo1 (uid: uid)
+TODO(uid) todo0
+
+TODO(uid) todo1
+
diff --git a/src/pkg/go/doc/testdata/a.1.golden b/src/pkg/go/doc/testdata/a.1.golden
index cd98f4e0e..7e680b80b 100644
--- a/src/pkg/go/doc/testdata/a.1.golden
+++ b/src/pkg/go/doc/testdata/a.1.golden
@@ -9,24 +9,44 @@ FILENAMES
testdata/a1.go
BUGS .Bugs is now deprecated, please use .Notes instead
- // bug0
- // bug1
+ bug0
+
+ bug1
+
BUGS
- // bug0 (uid: uid)
- // bug1 (uid: uid)
+BUG(uid) bug0
+
+BUG(uid) bug1
+
NOTES
- // 1 of 4 - this is the first line of note 1 - note 1 continues on ... (uid: foo)
- // 2 of 4 (uid: foo)
- // 3 of 4 (uid: bar)
- // 4 of 4 - this is the last line of note 4 (uid: bar)
- // This note which contains a (parenthesized) subphrase must ... (uid: bam)
- // The ':' after the marker and uid is optional. (uid: xxx)
+NOTE(uid)
+
+NOTE(foo) 1 of 4 - this is the first line of note 1
+ - note 1 continues on this 2nd line
+ - note 1 continues on this 3rd line
+
+NOTE(foo) 2 of 4
+
+NOTE(bar) 3 of 4
+
+NOTE(bar) 4 of 4
+ - this is the last line of note 4
+
+NOTE(bam) This note which contains a (parenthesized) subphrase
+ must appear in its entirety.
+
+NOTE(xxx) The ':' after the marker and uid is optional.
+
SECBUGS
- // sec hole 0 need to fix asap (uid: uid)
+SECBUG(uid) sec hole 0
+ need to fix asap
+
TODOS
- // todo0 (uid: uid)
- // todo1 (uid: uid)
+TODO(uid) todo0
+
+TODO(uid) todo1
+
diff --git a/src/pkg/go/doc/testdata/a.2.golden b/src/pkg/go/doc/testdata/a.2.golden
index cd98f4e0e..7e680b80b 100644
--- a/src/pkg/go/doc/testdata/a.2.golden
+++ b/src/pkg/go/doc/testdata/a.2.golden
@@ -9,24 +9,44 @@ FILENAMES
testdata/a1.go
BUGS .Bugs is now deprecated, please use .Notes instead
- // bug0
- // bug1
+ bug0
+
+ bug1
+
BUGS
- // bug0 (uid: uid)
- // bug1 (uid: uid)
+BUG(uid) bug0
+
+BUG(uid) bug1
+
NOTES
- // 1 of 4 - this is the first line of note 1 - note 1 continues on ... (uid: foo)
- // 2 of 4 (uid: foo)
- // 3 of 4 (uid: bar)
- // 4 of 4 - this is the last line of note 4 (uid: bar)
- // This note which contains a (parenthesized) subphrase must ... (uid: bam)
- // The ':' after the marker and uid is optional. (uid: xxx)
+NOTE(uid)
+
+NOTE(foo) 1 of 4 - this is the first line of note 1
+ - note 1 continues on this 2nd line
+ - note 1 continues on this 3rd line
+
+NOTE(foo) 2 of 4
+
+NOTE(bar) 3 of 4
+
+NOTE(bar) 4 of 4
+ - this is the last line of note 4
+
+NOTE(bam) This note which contains a (parenthesized) subphrase
+ must appear in its entirety.
+
+NOTE(xxx) The ':' after the marker and uid is optional.
+
SECBUGS
- // sec hole 0 need to fix asap (uid: uid)
+SECBUG(uid) sec hole 0
+ need to fix asap
+
TODOS
- // todo0 (uid: uid)
- // todo1 (uid: uid)
+TODO(uid) todo0
+
+TODO(uid) todo1
+
diff --git a/src/pkg/go/doc/testdata/bugpara.0.golden b/src/pkg/go/doc/testdata/bugpara.0.golden
new file mode 100644
index 000000000..580485950
--- /dev/null
+++ b/src/pkg/go/doc/testdata/bugpara.0.golden
@@ -0,0 +1,20 @@
+//
+PACKAGE bugpara
+
+IMPORTPATH
+ testdata/bugpara
+
+FILENAMES
+ testdata/bugpara.go
+
+BUGS .Bugs is now deprecated, please use .Notes instead
+ Sometimes bugs have multiple paragraphs.
+
+ Like this one.
+
+
+BUGS
+BUG(rsc) Sometimes bugs have multiple paragraphs.
+
+ Like this one.
+
diff --git a/src/pkg/go/doc/testdata/bugpara.1.golden b/src/pkg/go/doc/testdata/bugpara.1.golden
new file mode 100644
index 000000000..580485950
--- /dev/null
+++ b/src/pkg/go/doc/testdata/bugpara.1.golden
@@ -0,0 +1,20 @@
+//
+PACKAGE bugpara
+
+IMPORTPATH
+ testdata/bugpara
+
+FILENAMES
+ testdata/bugpara.go
+
+BUGS .Bugs is now deprecated, please use .Notes instead
+ Sometimes bugs have multiple paragraphs.
+
+ Like this one.
+
+
+BUGS
+BUG(rsc) Sometimes bugs have multiple paragraphs.
+
+ Like this one.
+
diff --git a/src/pkg/go/doc/testdata/bugpara.2.golden b/src/pkg/go/doc/testdata/bugpara.2.golden
new file mode 100644
index 000000000..580485950
--- /dev/null
+++ b/src/pkg/go/doc/testdata/bugpara.2.golden
@@ -0,0 +1,20 @@
+//
+PACKAGE bugpara
+
+IMPORTPATH
+ testdata/bugpara
+
+FILENAMES
+ testdata/bugpara.go
+
+BUGS .Bugs is now deprecated, please use .Notes instead
+ Sometimes bugs have multiple paragraphs.
+
+ Like this one.
+
+
+BUGS
+BUG(rsc) Sometimes bugs have multiple paragraphs.
+
+ Like this one.
+
diff --git a/src/pkg/go/doc/testdata/bugpara.go b/src/pkg/go/doc/testdata/bugpara.go
new file mode 100644
index 000000000..f5345a797
--- /dev/null
+++ b/src/pkg/go/doc/testdata/bugpara.go
@@ -0,0 +1,5 @@
+package bugpara
+
+// BUG(rsc): Sometimes bugs have multiple paragraphs.
+//
+// Like this one.
diff --git a/src/pkg/go/doc/testdata/template.txt b/src/pkg/go/doc/testdata/template.txt
index 26482f7c2..1b0738261 100644
--- a/src/pkg/go/doc/testdata/template.txt
+++ b/src/pkg/go/doc/testdata/template.txt
@@ -61,8 +61,8 @@ TYPES
*/}}{{with .Bugs}}
BUGS .Bugs is now deprecated, please use .Notes instead
-{{range .}} {{synopsis .}}
+{{range .}}{{indent "\t" .}}
{{end}}{{end}}{{with .Notes}}{{range $marker, $content := .}}
{{$marker}}S
-{{range $content}} {{synopsis .Body}} (uid: {{.UID}})
+{{range $content}}{{$marker}}({{.UID}}){{indent "\t" .Body}}
{{end}}{{end}}{{end}} \ No newline at end of file
diff --git a/src/pkg/go/doc/testdata/testing.0.golden b/src/pkg/go/doc/testdata/testing.0.golden
index 15a903986..f8348f1ac 100644
--- a/src/pkg/go/doc/testdata/testing.0.golden
+++ b/src/pkg/go/doc/testdata/testing.0.golden
@@ -57,7 +57,7 @@ TYPES
// FailNow marks the function as having failed and stops its ...
func (c *B) FailNow()
- // Failed returns whether the function has failed.
+ // Failed reports whether the function has failed.
func (c *B) Failed() bool
// Fatal is equivalent to Log() followed by FailNow().
@@ -136,7 +136,7 @@ TYPES
// FailNow marks the function as having failed and stops its ...
func (c *T) FailNow()
- // Failed returns whether the function has failed.
+ // Failed reports whether the function has failed.
func (c *T) Failed() bool
// Fatal is equivalent to Log() followed by FailNow().
diff --git a/src/pkg/go/doc/testdata/testing.1.golden b/src/pkg/go/doc/testdata/testing.1.golden
index ffdb5c3b5..282bb1015 100644
--- a/src/pkg/go/doc/testdata/testing.1.golden
+++ b/src/pkg/go/doc/testdata/testing.1.golden
@@ -130,7 +130,7 @@ TYPES
// FailNow marks the function as having failed and stops its ...
func (c *B) FailNow()
- // Failed returns whether the function has failed.
+ // Failed reports whether the function has failed.
func (c *B) Failed() bool
// Fatal is equivalent to Log() followed by FailNow().
@@ -232,7 +232,7 @@ TYPES
// FailNow marks the function as having failed and stops its ...
func (c *T) FailNow()
- // Failed returns whether the function has failed.
+ // Failed reports whether the function has failed.
func (c *T) Failed() bool
// Fatal is equivalent to Log() followed by FailNow().
@@ -278,7 +278,7 @@ TYPES
// FailNow marks the function as having failed and stops its ...
func (c *common) FailNow()
- // Failed returns whether the function has failed.
+ // Failed reports whether the function has failed.
func (c *common) Failed() bool
// Fatal is equivalent to Log() followed by FailNow().
diff --git a/src/pkg/go/doc/testdata/testing.2.golden b/src/pkg/go/doc/testdata/testing.2.golden
index 15a903986..f8348f1ac 100644
--- a/src/pkg/go/doc/testdata/testing.2.golden
+++ b/src/pkg/go/doc/testdata/testing.2.golden
@@ -57,7 +57,7 @@ TYPES
// FailNow marks the function as having failed and stops its ...
func (c *B) FailNow()
- // Failed returns whether the function has failed.
+ // Failed reports whether the function has failed.
func (c *B) Failed() bool
// Fatal is equivalent to Log() followed by FailNow().
@@ -136,7 +136,7 @@ TYPES
// FailNow marks the function as having failed and stops its ...
func (c *T) FailNow()
- // Failed returns whether the function has failed.
+ // Failed reports whether the function has failed.
func (c *T) Failed() bool
// Fatal is equivalent to Log() followed by FailNow().
diff --git a/src/pkg/go/doc/testdata/testing.go b/src/pkg/go/doc/testdata/testing.go
index c2499ad77..93ed494c3 100644
--- a/src/pkg/go/doc/testdata/testing.go
+++ b/src/pkg/go/doc/testdata/testing.go
@@ -130,7 +130,7 @@ type T struct {
// Fail marks the function as having failed but continues execution.
func (c *common) Fail() { c.failed = true }
-// Failed returns whether the function has failed.
+// Failed reports whether the function has failed.
func (c *common) Failed() bool { return c.failed }
// FailNow marks the function as having failed and stops its execution.
diff --git a/src/pkg/go/format/format_test.go b/src/pkg/go/format/format_test.go
index 7d7940bb5..93f099247 100644
--- a/src/pkg/go/format/format_test.go
+++ b/src/pkg/go/format/format_test.go
@@ -90,7 +90,6 @@ var tests = []string{
"\n\t\t\n\n\t\t\tx := 0\n\t\t\tconst s = `\nfoo\n`\n\n\n", // no indentation inside raw strings
// erroneous programs
- "ERRORvar x",
"ERROR1 + 2 +",
"ERRORx := 0",
}
diff --git a/src/pkg/go/parser/interface.go b/src/pkg/go/parser/interface.go
index 149257ca6..0f83ca931 100644
--- a/src/pkg/go/parser/interface.go
+++ b/src/pkg/go/parser/interface.go
@@ -15,6 +15,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
+ "strings"
)
// If src != nil, readSource converts src to a []byte if possible;
@@ -115,11 +116,13 @@ func ParseFile(fset *token.FileSet, filename string, src interface{}, mode Mode)
return
}
-// ParseDir calls ParseFile for the files in the directory specified by path and
-// returns a map of package name -> package AST with all the packages found. If
-// filter != nil, only the files with os.FileInfo entries passing through the filter
-// are considered. The mode bits are passed to ParseFile unchanged. Position
-// information is recorded in the file set fset.
+// ParseDir calls ParseFile for all files with names ending in ".go" in the
+// directory specified by path and returns a map of package name -> package
+// AST with all the packages found.
+//
+// If filter != nil, only the files with os.FileInfo entries passing through
+// the filter (and ending in ".go") are considered. The mode bits are passed
+// to ParseFile unchanged. Position information is recorded in fset.
//
// If the directory couldn't be read, a nil map and the respective error are
// returned. If a parse error occurred, a non-nil but incomplete map and the
@@ -139,7 +142,7 @@ func ParseDir(fset *token.FileSet, path string, filter func(os.FileInfo) bool, m
pkgs = make(map[string]*ast.Package)
for _, d := range list {
- if filter == nil || filter(d) {
+ if strings.HasSuffix(d.Name(), ".go") && (filter == nil || filter(d)) {
filename := filepath.Join(path, d.Name())
if src, err := ParseFile(fset, filename, nil, mode); err == nil {
name := src.Name.Name
diff --git a/src/pkg/go/parser/parser.go b/src/pkg/go/parser/parser.go
index f4a690a6f..c4523318f 100644
--- a/src/pkg/go/parser/parser.go
+++ b/src/pkg/go/parser/parser.go
@@ -64,7 +64,7 @@ type parser struct {
}
func (p *parser) init(fset *token.FileSet, filename string, src []byte, mode Mode) {
- p.file = fset.AddFile(filename, fset.Base(), len(src))
+ p.file = fset.AddFile(filename, -1, len(src))
var m scanner.Mode
if mode&ParseComments != 0 {
m = scanner.ScanComments
@@ -1150,7 +1150,7 @@ func (p *parser) parseTypeAssertion(x ast.Expr) ast.Expr {
defer un(trace(p, "TypeAssertion"))
}
- p.expect(token.LPAREN)
+ lparen := p.expect(token.LPAREN)
var typ ast.Expr
if p.tok == token.TYPE {
// type switch: typ == nil
@@ -1158,9 +1158,9 @@ func (p *parser) parseTypeAssertion(x ast.Expr) ast.Expr {
} else {
typ = p.parseType()
}
- p.expect(token.RPAREN)
+ rparen := p.expect(token.RPAREN)
- return &ast.TypeAssertExpr{X: x, Type: typ}
+ return &ast.TypeAssertExpr{X: x, Type: typ, Lparen: lparen, Rparen: rparen}
}
func (p *parser) parseIndexOrSlice(x ast.Expr) ast.Expr {
@@ -1170,25 +1170,27 @@ func (p *parser) parseIndexOrSlice(x ast.Expr) ast.Expr {
lbrack := p.expect(token.LBRACK)
p.exprLev++
- var low, high ast.Expr
- isSlice := false
+ var index [3]ast.Expr // change the 3 to 2 to disable slice expressions w/ cap
if p.tok != token.COLON {
- low = p.parseRhs()
+ index[0] = p.parseRhs()
}
- if p.tok == token.COLON {
- isSlice = true
+ ncolons := 0
+ for p.tok == token.COLON && ncolons < len(index)-1 {
p.next()
- if p.tok != token.RBRACK {
- high = p.parseRhs()
+ ncolons++
+ if p.tok != token.COLON && p.tok != token.RBRACK && p.tok != token.EOF {
+ index[ncolons] = p.parseRhs()
}
}
p.exprLev--
rbrack := p.expect(token.RBRACK)
- if isSlice {
- return &ast.SliceExpr{X: x, Lbrack: lbrack, Low: low, High: high, Rbrack: rbrack}
+ if ncolons > 0 {
+ // slice expression
+ return &ast.SliceExpr{X: x, Lbrack: lbrack, Low: index[0], High: index[1], Max: index[2], Slice3: ncolons == 2, Rbrack: rbrack}
}
- return &ast.IndexExpr{X: x, Lbrack: lbrack, Index: low, Rbrack: rbrack}
+
+ return &ast.IndexExpr{X: x, Lbrack: lbrack, Index: index[0], Rbrack: rbrack}
}
func (p *parser) parseCallOrConversion(fun ast.Expr) *ast.CallExpr {
@@ -1406,7 +1408,7 @@ L:
}
switch p.tok {
case token.IDENT:
- x = p.parseSelector(p.checkExpr(x))
+ x = p.parseSelector(p.checkExprOrType(x))
case token.LPAREN:
x = p.parseTypeAssertion(p.checkExpr(x))
default:
@@ -2145,12 +2147,13 @@ func (p *parser) parseImportSpec(doc *ast.CommentGroup, _ token.Token, _ int) as
ident = p.parseIdent()
}
- var path *ast.BasicLit
+ pos := p.pos
+ var path string
if p.tok == token.STRING {
- if !isValidImport(p.lit) {
- p.error(p.pos, "invalid import path: "+p.lit)
+ path = p.lit
+ if !isValidImport(path) {
+ p.error(pos, "invalid import path: "+path)
}
- path = &ast.BasicLit{ValuePos: p.pos, Kind: p.tok, Value: p.lit}
p.next()
} else {
p.expect(token.STRING) // use expect() error handling
@@ -2161,7 +2164,7 @@ func (p *parser) parseImportSpec(doc *ast.CommentGroup, _ token.Token, _ int) as
spec := &ast.ImportSpec{
Doc: doc,
Name: ident,
- Path: path,
+ Path: &ast.BasicLit{ValuePos: pos, Kind: token.STRING, Value: path},
Comment: p.lineComment,
}
p.imports = append(p.imports, spec)
@@ -2177,8 +2180,9 @@ func (p *parser) parseValueSpec(doc *ast.CommentGroup, keyword token.Token, iota
idents := p.parseIdentList()
typ := p.tryType()
var values []ast.Expr
- if p.tok == token.ASSIGN || keyword == token.CONST && (typ != nil || iota == 0) || keyword == token.VAR && typ == nil {
- p.expect(token.ASSIGN)
+ // always permit optional initialization for more tolerant parsing
+ if p.tok == token.ASSIGN {
+ p.next()
values = p.parseRhsList()
}
p.expectSemi() // call before accessing p.linecomment
@@ -2381,7 +2385,7 @@ func (p *parser) parseFile() *ast.File {
// Go spec: The package clause is not a declaration;
// the package name does not appear in any scope.
ident := p.parseIdent()
- if ident.Name == "_" {
+ if ident.Name == "_" && p.mode&DeclarationErrors != 0 {
p.error(p.pos, "invalid package name _")
}
p.expectSemi()
diff --git a/src/pkg/go/parser/parser_test.go b/src/pkg/go/parser/parser_test.go
index 48813d106..0a34b7e50 100644
--- a/src/pkg/go/parser/parser_test.go
+++ b/src/pkg/go/parser/parser_test.go
@@ -34,13 +34,12 @@ func TestParse(t *testing.T) {
func nameFilter(filename string) bool {
switch filename {
- case "parser.go":
- case "interface.go":
- case "parser_test.go":
- default:
- return false
+ case "parser.go", "interface.go", "parser_test.go":
+ return true
+ case "parser.go.orig":
+ return true // permit but should be ignored by ParseDir
}
- return true
+ return false
}
func dirFilter(f os.FileInfo) bool { return nameFilter(f.Name()) }
@@ -51,14 +50,17 @@ func TestParseDir(t *testing.T) {
if err != nil {
t.Fatalf("ParseDir(%s): %v", path, err)
}
- if len(pkgs) != 1 {
- t.Errorf("incorrect number of packages: %d", len(pkgs))
+ if n := len(pkgs); n != 1 {
+ t.Errorf("got %d packages; want 1", n)
}
pkg := pkgs["parser"]
if pkg == nil {
t.Errorf(`package "parser" not found`)
return
}
+ if n := len(pkg.Files); n != 3 {
+ t.Errorf("got %d package files; want 3", n)
+ }
for filename := range pkg.Files {
if !nameFilter(filename) {
t.Errorf("unexpected package file: %s", filename)
diff --git a/src/pkg/go/parser/short_test.go b/src/pkg/go/parser/short_test.go
index 62277c0d2..0ef0c560c 100644
--- a/src/pkg/go/parser/short_test.go
+++ b/src/pkg/go/parser/short_test.go
@@ -33,6 +33,8 @@ var valids = []string{
`package p; func f() { if ; true {} };`,
`package p; func f() { switch ; {} };`,
`package p; func f() { for _ = range "foo" + "bar" {} };`,
+ `package p; func f() { var s []int; g(s[:], s[i:], s[:j], s[i:j], s[i:j:k], s[:j:k]) };`,
+ `package p; var ( _ = (struct {*T}).m; _ = (interface {T}).m )`,
}
func TestValid(t *testing.T) {
@@ -46,7 +48,6 @@ var invalids = []string{
`package p; func f() { if { /* ERROR "expected operand" */ } };`,
`package p; func f() { if ; { /* ERROR "expected operand" */ } };`,
`package p; func f() { if f(); { /* ERROR "expected operand" */ } };`,
- `package p; const c; /* ERROR "expected '='" */`,
`package p; func f() { if _ /* ERROR "expected condition" */ = range x; true {} };`,
`package p; func f() { switch _ /* ERROR "expected condition" */ = range x; true {} };`,
`package p; func f() { for _ = range x ; /* ERROR "expected '{'" */ ; {} };`,
@@ -74,6 +75,9 @@ var invalids = []string{
`package p; func f() { if x := g(); x = /* ERROR "expected '=='" */ 0 {}};`,
`package p; func f() { _ = x = /* ERROR "expected '=='" */ 0 {}};`,
`package p; func f() { _ = 1 == func()int { var x bool; x = x = /* ERROR "expected '=='" */ true; return x }() };`,
+ `package p; func f() { var s []int; _ = s[] /* ERROR "expected operand" */ };`,
+ `package p; func f() { var s []int; _ = s[::: /* ERROR "expected ']'" */ ] };`,
+ `package p; func f() { var s []int; _ = s[i:j:k: /* ERROR "expected ']'" */ l] };`,
}
func TestInvalid(t *testing.T) {
diff --git a/src/pkg/go/printer/nodes.go b/src/pkg/go/printer/nodes.go
index 7cd068e22..583c6c370 100644
--- a/src/pkg/go/printer/nodes.go
+++ b/src/pkg/go/printer/nodes.go
@@ -754,13 +754,13 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) {
case *ast.TypeAssertExpr:
p.expr1(x.X, token.HighestPrec, depth)
- p.print(token.PERIOD, token.LPAREN)
+ p.print(token.PERIOD, x.Lparen, token.LPAREN)
if x.Type != nil {
p.expr(x.Type)
} else {
p.print(token.TYPE)
}
- p.print(token.RPAREN)
+ p.print(x.Rparen, token.RPAREN)
case *ast.IndexExpr:
// TODO(gri): should treat[] like parentheses and undo one level of depth
@@ -773,17 +773,25 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) {
// TODO(gri): should treat[] like parentheses and undo one level of depth
p.expr1(x.X, token.HighestPrec, 1)
p.print(x.Lbrack, token.LBRACK)
- if x.Low != nil {
- p.expr0(x.Low, depth+1)
- }
- // blanks around ":" if both sides exist and either side is a binary expression
- if depth <= 1 && x.Low != nil && x.High != nil && (isBinary(x.Low) || isBinary(x.High)) {
- p.print(blank, token.COLON, blank)
- } else {
- p.print(token.COLON)
+ indices := []ast.Expr{x.Low, x.High}
+ if x.Max != nil {
+ indices = append(indices, x.Max)
}
- if x.High != nil {
- p.expr0(x.High, depth+1)
+ for i, y := range indices {
+ if i > 0 {
+ // blanks around ":" if both sides exist and either side is a binary expression
+ // TODO(gri) once we have committed a variant of a[i:j:k] we may want to fine-
+ // tune the formatting here
+ x := indices[i-1]
+ if depth <= 1 && x != nil && y != nil && (isBinary(x) || isBinary(y)) {
+ p.print(blank, token.COLON, blank)
+ } else {
+ p.print(token.COLON)
+ }
+ }
+ if y != nil {
+ p.expr0(y, depth+1)
+ }
}
p.print(x.Rbrack, token.RBRACK)
diff --git a/src/pkg/go/printer/testdata/expressions.golden b/src/pkg/go/printer/testdata/expressions.golden
index 4291c557c..fbe8275b3 100644
--- a/src/pkg/go/printer/testdata/expressions.golden
+++ b/src/pkg/go/printer/testdata/expressions.golden
@@ -114,6 +114,23 @@ func _() {
x < y || z > 42
}
+// slice expressions with cap
+func _() {
+ _ = x[a:b:c]
+ _ = x[a:b : c+d]
+ _ = x[a : b+d : c]
+ _ = x[a : b+d : c+d]
+ _ = x[a+d : b:c]
+ _ = x[a+d : b : c+d]
+ _ = x[a+d : b+d : c]
+ _ = x[a+d : b+d : c+d]
+
+ _ = x[:b:c]
+ _ = x[:b : c+d]
+ _ = x[:b+d : c]
+ _ = x[:b+d : c+d]
+}
+
func _() {
_ = a + b
_ = a + b + c
diff --git a/src/pkg/go/printer/testdata/expressions.input b/src/pkg/go/printer/testdata/expressions.input
index 1ec12a050..f4d20fa0f 100644
--- a/src/pkg/go/printer/testdata/expressions.input
+++ b/src/pkg/go/printer/testdata/expressions.input
@@ -116,6 +116,23 @@ func _() {
}
+// slice expressions with cap
+func _() {
+ _ = x[a:b:c]
+ _ = x[a:b:c+d]
+ _ = x[a:b+d:c]
+ _ = x[a:b+d:c+d]
+ _ = x[a+d:b:c]
+ _ = x[a+d:b:c+d]
+ _ = x[a+d:b+d:c]
+ _ = x[a+d:b+d:c+d]
+
+ _ = x[:b:c]
+ _ = x[:b:c+d]
+ _ = x[:b+d:c]
+ _ = x[:b+d:c+d]
+}
+
func _() {
_ = a+b
_ = a+b+c
diff --git a/src/pkg/go/printer/testdata/expressions.raw b/src/pkg/go/printer/testdata/expressions.raw
index 062900e07..97bc81dad 100644
--- a/src/pkg/go/printer/testdata/expressions.raw
+++ b/src/pkg/go/printer/testdata/expressions.raw
@@ -114,6 +114,23 @@ func _() {
x < y || z > 42
}
+// slice expressions with cap
+func _() {
+ _ = x[a:b:c]
+ _ = x[a:b : c+d]
+ _ = x[a : b+d : c]
+ _ = x[a : b+d : c+d]
+ _ = x[a+d : b:c]
+ _ = x[a+d : b : c+d]
+ _ = x[a+d : b+d : c]
+ _ = x[a+d : b+d : c+d]
+
+ _ = x[:b:c]
+ _ = x[:b : c+d]
+ _ = x[:b+d : c]
+ _ = x[:b+d : c+d]
+}
+
func _() {
_ = a + b
_ = a + b + c
diff --git a/src/pkg/go/token/position.go b/src/pkg/go/token/position.go
index f5d999561..e6f0ae6a6 100644
--- a/src/pkg/go/token/position.go
+++ b/src/pkg/go/token/position.go
@@ -97,7 +97,7 @@ type File struct {
size int // file size as provided to AddFile
// lines and infos are protected by set.mutex
- lines []int
+ lines []int // lines contains the offset of the first character for each line (the first entry is always 0)
infos []lineInfo
}
@@ -136,6 +136,29 @@ func (f *File) AddLine(offset int) {
f.set.mutex.Unlock()
}
+// MergeLine merges a line with the following line. It is akin to replacing
+// the newline character at the end of the line with a space (to not change the
+// remaining offsets). To obtain the line number, consult e.g. Position.Line.
+// MergeLine will panic if given an invalid line number.
+//
+func (f *File) MergeLine(line int) {
+ if line <= 0 {
+ panic("illegal line number (line numbering starts at 1)")
+ }
+ f.set.mutex.Lock()
+ defer f.set.mutex.Unlock()
+ if line >= len(f.lines) {
+ panic("illegal line number")
+ }
+ // To merge the line numbered <line> with the line numbered <line+1>,
+ // we need to remove the entry in lines corresponding to the line
+ // numbered <line+1>. The entry in lines corresponding to the line
+ // numbered <line+1> is located at index <line>, since indices in lines
+ // are 0-based and line numbers are 1-based.
+ copy(f.lines[line:], f.lines[line+1:])
+ f.lines = f.lines[:len(f.lines)-1]
+}
+
// SetLines sets the line offsets for a file and returns true if successful.
// The line offsets are the offsets of the first character of each line;
// for instance for the content "ab\nc\n" the line offsets are {0, 3}.
@@ -314,7 +337,8 @@ func (s *FileSet) Base() int {
// AddFile adds a new file with a given filename, base offset, and file size
// to the file set s and returns the file. Multiple files may have the same
// name. The base offset must not be smaller than the FileSet's Base(), and
-// size must not be negative.
+// size must not be negative. As a special case, if a negative base is provided,
+// the current value of the FileSet's Base() is used instead.
//
// Adding the file will set the file set's Base() value to base + size + 1
// as the minimum base value for the next file. The following relationship
@@ -329,6 +353,9 @@ func (s *FileSet) Base() int {
func (s *FileSet) AddFile(filename string, base, size int) *File {
s.mutex.Lock()
defer s.mutex.Unlock()
+ if base < 0 {
+ base = s.base
+ }
if base < s.base || size < 0 {
panic("illegal base or size")
}
diff --git a/src/pkg/go/token/position_test.go b/src/pkg/go/token/position_test.go
index 1d36c2226..ef6cfd93c 100644
--- a/src/pkg/go/token/position_test.go
+++ b/src/pkg/go/token/position_test.go
@@ -167,7 +167,13 @@ func TestLineInfo(t *testing.T) {
func TestFiles(t *testing.T) {
fset := NewFileSet()
for i, test := range tests {
- fset.AddFile(test.filename, fset.Base(), test.size)
+ base := fset.Base()
+ if i%2 == 1 {
+ // Setting a negative base is equivalent to
+ // fset.Base(), so test some of each.
+ base = -1
+ }
+ fset.AddFile(test.filename, base, test.size)
j := 0
fset.Iterate(func(f *File) bool {
if f.Name() != tests[j].filename {
diff --git a/src/pkg/hash/crc32/crc32_amd64.s b/src/pkg/hash/crc32/crc32_amd64.s
index 826306a3e..95dc8bf41 100644
--- a/src/pkg/hash/crc32/crc32_amd64.s
+++ b/src/pkg/hash/crc32/crc32_amd64.s
@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../../cmd/ld/textflag.h"
+
// func castagnoliSSE42(crc uint32, p []byte) uint32
-TEXT ·castagnoliSSE42(SB),7,$0
+TEXT ·castagnoliSSE42(SB),NOSPLIT,$0
MOVL crc+0(FP), AX // CRC value
MOVQ p+8(FP), SI // data pointer
MOVQ p_len+16(FP), CX // len(p)
@@ -51,7 +53,7 @@ done:
RET
// func haveSSE42() bool
-TEXT ·haveSSE42(SB),7,$0
+TEXT ·haveSSE42(SB),NOSPLIT,$0
XORQ AX, AX
INCL AX
CPUID
diff --git a/src/pkg/hash/hash.go b/src/pkg/hash/hash.go
index aa895cf98..8d138d07f 100644
--- a/src/pkg/hash/hash.go
+++ b/src/pkg/hash/hash.go
@@ -9,7 +9,7 @@ import "io"
// Hash is the common interface implemented by all hash functions.
type Hash interface {
- // Write adds more data to the running hash.
+ // Write (via the embedded io.Writer interface) adds more data to the running hash.
// It never returns an error.
io.Writer
@@ -17,7 +17,7 @@ type Hash interface {
// It does not change the underlying hash state.
Sum(b []byte) []byte
- // Reset resets the hash to one with zero bytes written.
+ // Reset resets the Hash to its initial state.
Reset()
// Size returns the number of bytes Sum will return.
diff --git a/src/pkg/html/escape.go b/src/pkg/html/escape.go
index eff0384e0..dd5dfa7cd 100644
--- a/src/pkg/html/escape.go
+++ b/src/pkg/html/escape.go
@@ -187,16 +187,6 @@ func unescape(b []byte) []byte {
return b
}
-// lower lower-cases the A-Z bytes in b in-place, so that "aBc" becomes "abc".
-func lower(b []byte) []byte {
- for i, c := range b {
- if 'A' <= c && c <= 'Z' {
- b[i] = c + 'a' - 'A'
- }
- }
- return b
-}
-
const escapedChars = `&'<>"`
func escape(w writer, s string) error {
diff --git a/src/pkg/html/escape_test.go b/src/pkg/html/escape_test.go
new file mode 100644
index 000000000..b405d4b4a
--- /dev/null
+++ b/src/pkg/html/escape_test.go
@@ -0,0 +1,97 @@
+// Copyright 2013 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 html
+
+import "testing"
+
+type unescapeTest struct {
+ // A short description of the test case.
+ desc string
+ // The HTML text.
+ html string
+ // The unescaped text.
+ unescaped string
+}
+
+var unescapeTests = []unescapeTest{
+ // Handle no entities.
+ {
+ "copy",
+ "A\ttext\nstring",
+ "A\ttext\nstring",
+ },
+ // Handle simple named entities.
+ {
+ "simple",
+ "&amp; &gt; &lt;",
+ "& > <",
+ },
+ // Handle hitting the end of the string.
+ {
+ "stringEnd",
+ "&amp &amp",
+ "& &",
+ },
+ // Handle entities with two codepoints.
+ {
+ "multiCodepoint",
+ "text &gesl; blah",
+ "text \u22db\ufe00 blah",
+ },
+ // Handle decimal numeric entities.
+ {
+ "decimalEntity",
+ "Delta = &#916; ",
+ "Delta = Δ ",
+ },
+ // Handle hexadecimal numeric entities.
+ {
+ "hexadecimalEntity",
+ "Lambda = &#x3bb; = &#X3Bb ",
+ "Lambda = λ = λ ",
+ },
+ // Handle numeric early termination.
+ {
+ "numericEnds",
+ "&# &#x &#128;43 &copy = &#169f = &#xa9",
+ "&# &#x €43 © = ©f = ©",
+ },
+ // Handle numeric ISO-8859-1 entity replacements.
+ {
+ "numericReplacements",
+ "Footnote&#x87;",
+ "Footnote‡",
+ },
+}
+
+func TestUnescape(t *testing.T) {
+ for _, tt := range unescapeTests {
+ unescaped := UnescapeString(tt.html)
+ if unescaped != tt.unescaped {
+ t.Errorf("TestUnescape %s: want %q, got %q", tt.desc, tt.unescaped, unescaped)
+ }
+ }
+}
+
+func TestUnescapeEscape(t *testing.T) {
+ ss := []string{
+ ``,
+ `abc def`,
+ `a & b`,
+ `a&amp;b`,
+ `a &amp b`,
+ `&quot;`,
+ `"`,
+ `"<&>"`,
+ `&quot;&lt;&amp;&gt;&quot;`,
+ `3&5==1 && 0<1, "0&lt;1", a+acute=&aacute;`,
+ `The special characters are: <, >, &, ' and "`,
+ }
+ for _, s := range ss {
+ if got := UnescapeString(EscapeString(s)); got != s {
+ t.Errorf("got %q want %q", got, s)
+ }
+ }
+}
diff --git a/src/pkg/html/template/clone_test.go b/src/pkg/html/template/clone_test.go
index 2663cddc2..e11bff2c5 100644
--- a/src/pkg/html/template/clone_test.go
+++ b/src/pkg/html/template/clone_test.go
@@ -6,6 +6,8 @@ package template
import (
"bytes"
+ "errors"
+ "io/ioutil"
"testing"
"text/template/parse"
)
@@ -146,3 +148,41 @@ func TestCloneCrash(t *testing.T) {
Must(t1.New("t1").Parse(`{{define "foo"}}foo{{end}}`))
t1.Clone()
}
+
+// Ensure that this guarantee from the docs is upheld:
+// "Further calls to Parse in the copy will add templates
+// to the copy but not to the original."
+func TestCloneThenParse(t *testing.T) {
+ t0 := Must(New("t0").Parse(`{{define "a"}}{{template "embedded"}}{{end}}`))
+ t1 := Must(t0.Clone())
+ Must(t1.Parse(`{{define "embedded"}}t1{{end}}`))
+ if len(t0.Templates())+1 != len(t1.Templates()) {
+ t.Error("adding a template to a clone added it to the original")
+ }
+ // double check that the embedded template isn't available in the original
+ err := t0.ExecuteTemplate(ioutil.Discard, "a", nil)
+ if err == nil {
+ t.Error("expected 'no such template' error")
+ }
+}
+
+// https://code.google.com/p/go/issues/detail?id=5980
+func TestFuncMapWorksAfterClone(t *testing.T) {
+ funcs := FuncMap{"customFunc": func() (string, error) {
+ return "", errors.New("issue5980")
+ }}
+
+ // get the expected error output (no clone)
+ uncloned := Must(New("").Funcs(funcs).Parse("{{customFunc}}"))
+ wantErr := uncloned.Execute(ioutil.Discard, nil)
+
+ // toClone must be the same as uncloned. It has to be recreated from scratch,
+ // since cloning cannot occur after execution.
+ toClone := Must(New("").Funcs(funcs).Parse("{{customFunc}}"))
+ cloned := Must(toClone.Clone())
+ gotErr := cloned.Execute(ioutil.Discard, nil)
+
+ if wantErr.Error() != gotErr.Error() {
+ t.Errorf("clone error message mismatch want %q got %q", wantErr, gotErr)
+ }
+}
diff --git a/src/pkg/html/template/content.go b/src/pkg/html/template/content.go
index 9d1f74f6f..41b1116a6 100644
--- a/src/pkg/html/template/content.go
+++ b/src/pkg/html/template/content.go
@@ -74,6 +74,9 @@ const (
// indirect returns the value, after dereferencing as many times
// as necessary to reach the base type (or nil).
func indirect(a interface{}) interface{} {
+ if a == nil {
+ return nil
+ }
if t := reflect.TypeOf(a); t.Kind() != reflect.Ptr {
// Avoid creating a reflect.Value if it's not a pointer.
return a
@@ -94,6 +97,9 @@ var (
// as necessary to reach the base type (or nil) or an implementation of fmt.Stringer
// or error,
func indirectToStringerOrError(a interface{}) interface{} {
+ if a == nil {
+ return nil
+ }
v := reflect.ValueOf(a)
for !v.Type().Implements(fmtStringerType) && !v.Type().Implements(errorType) && v.Kind() == reflect.Ptr && !v.IsNil() {
v = v.Elem()
diff --git a/src/pkg/html/template/content_test.go b/src/pkg/html/template/content_test.go
index 3c32e5e89..5f3ffe2d3 100644
--- a/src/pkg/html/template/content_test.go
+++ b/src/pkg/html/template/content_test.go
@@ -123,29 +123,29 @@ func TestTypedContent(t *testing.T) {
{
`<script>alert({{.}})</script>`,
[]string{
- `"\u003cb\u003e \"foo%\" O'Reilly &bar;"`,
+ `"\u003cb\u003e \"foo%\" O'Reilly \u0026bar;"`,
`"a[href =~ \"//example.com\"]#foo"`,
- `"Hello, \u003cb\u003eWorld\u003c/b\u003e &amp;tc!"`,
+ `"Hello, \u003cb\u003eWorld\u003c/b\u003e \u0026amp;tc!"`,
`" dir=\"ltr\""`,
// Not escaped.
`c && alert("Hello, World!");`,
// Escape sequence not over-escaped.
`"Hello, World & O'Reilly\x21"`,
- `"greeting=H%69&addressee=(World)"`,
+ `"greeting=H%69\u0026addressee=(World)"`,
},
},
{
`<button onclick="alert({{.}})">`,
[]string{
- `&#34;\u003cb\u003e \&#34;foo%\&#34; O&#39;Reilly &amp;bar;&#34;`,
+ `&#34;\u003cb\u003e \&#34;foo%\&#34; O&#39;Reilly \u0026bar;&#34;`,
`&#34;a[href =~ \&#34;//example.com\&#34;]#foo&#34;`,
- `&#34;Hello, \u003cb\u003eWorld\u003c/b\u003e &amp;amp;tc!&#34;`,
+ `&#34;Hello, \u003cb\u003eWorld\u003c/b\u003e \u0026amp;tc!&#34;`,
`&#34; dir=\&#34;ltr\&#34;&#34;`,
// Not JS escaped but HTML escaped.
`c &amp;&amp; alert(&#34;Hello, World!&#34;);`,
// Escape sequence not over-escaped.
`&#34;Hello, World &amp; O&#39;Reilly\x21&#34;`,
- `&#34;greeting=H%69&amp;addressee=(World)&#34;`,
+ `&#34;greeting=H%69\u0026addressee=(World)&#34;`,
},
},
{
@@ -259,3 +259,22 @@ func TestStringer(t *testing.T) {
t.Errorf("expected %q got %q", expect, b.String())
}
}
+
+// https://code.google.com/p/go/issues/detail?id=5982
+func TestEscapingNilNonemptyInterfaces(t *testing.T) {
+ tmpl := Must(New("x").Parse("{{.E}}"))
+
+ got := new(bytes.Buffer)
+ testData := struct{ E error }{} // any non-empty interface here will do; error is just ready at hand
+ tmpl.Execute(got, testData)
+
+ // Use this data instead of just hard-coding "&lt;nil&gt;" to avoid
+ // dependencies on the html escaper and the behavior of fmt w.r.t. nil.
+ want := new(bytes.Buffer)
+ data := struct{ E string }{E: fmt.Sprint(nil)}
+ tmpl.Execute(want, data)
+
+ if !bytes.Equal(want.Bytes(), got.Bytes()) {
+ t.Errorf("expected %q got %q", string(want.Bytes()), string(got.Bytes()))
+ }
+}
diff --git a/src/pkg/html/template/context.go b/src/pkg/html/template/context.go
index 7202221b8..eb47e2be3 100644
--- a/src/pkg/html/template/context.go
+++ b/src/pkg/html/template/context.go
@@ -29,7 +29,7 @@ func (c context) String() string {
return fmt.Sprintf("{%v %v %v %v %v %v %v}", c.state, c.delim, c.urlPart, c.jsCtx, c.attr, c.element, c.err)
}
-// eq returns whether two contexts are equal.
+// eq reports whether two contexts are equal.
func (c context) eq(d context) bool {
return c.state == d.state &&
c.delim == d.delim &&
diff --git a/src/pkg/html/template/css.go b/src/pkg/html/template/css.go
index 3bcd98498..634f183f7 100644
--- a/src/pkg/html/template/css.go
+++ b/src/pkg/html/template/css.go
@@ -11,7 +11,7 @@ import (
"unicode/utf8"
)
-// endsWithCSSKeyword returns whether b ends with an ident that
+// endsWithCSSKeyword reports whether b ends with an ident that
// case-insensitively matches the lower-case kw.
func endsWithCSSKeyword(b []byte, kw string) bool {
i := len(b) - len(kw)
@@ -34,7 +34,7 @@ func endsWithCSSKeyword(b []byte, kw string) bool {
return string(bytes.ToLower(b[i:])) == kw
}
-// isCSSNmchar returns whether rune is allowed anywhere in a CSS identifier.
+// isCSSNmchar reports whether rune is allowed anywhere in a CSS identifier.
func isCSSNmchar(r rune) bool {
// Based on the CSS3 nmchar production but ignores multi-rune escape
// sequences.
@@ -99,7 +99,7 @@ func decodeCSS(s []byte) []byte {
return b
}
-// isHex returns whether the given character is a hex digit.
+// isHex reports whether the given character is a hex digit.
func isHex(c byte) bool {
return '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F'
}
@@ -144,7 +144,7 @@ func skipCSSSpace(c []byte) []byte {
return c
}
-// isCSSSpace returns whether b is a CSS space char as defined in wc.
+// isCSSSpace reports whether b is a CSS space char as defined in wc.
func isCSSSpace(b byte) bool {
switch b {
case '\t', '\n', '\f', '\r', ' ':
diff --git a/src/pkg/html/template/escape.go b/src/pkg/html/template/escape.go
index 4829bfcc4..9ae9749db 100644
--- a/src/pkg/html/template/escape.go
+++ b/src/pkg/html/template/escape.go
@@ -35,11 +35,13 @@ func escapeTemplates(tmpl *Template, names ...string) error {
for _, name := range names {
if t := tmpl.set[name]; t != nil {
t.text.Tree = nil
+ t.Tree = nil
}
}
return err
}
tmpl.escaped = true
+ tmpl.Tree = tmpl.text.Tree
}
e.commit()
return nil
@@ -301,7 +303,7 @@ func indexOfStr(s string, strs []string, eq func(a, b string) bool) int {
return -1
}
-// escFnsEq returns whether the two escaping functions are equivalent.
+// escFnsEq reports whether the two escaping functions are equivalent.
func escFnsEq(a, b string) bool {
if e := equivEscapers[a]; e != "" {
a = e
diff --git a/src/pkg/html/template/escape_test.go b/src/pkg/html/template/escape_test.go
index de3659ba8..58383a6cd 100644
--- a/src/pkg/html/template/escape_test.go
+++ b/src/pkg/html/template/escape_test.go
@@ -538,7 +538,7 @@ func TestEscape(t *testing.T) {
{
"typed HTML in script",
`<button onclick="alert({{.W}})">`,
- `<button onclick="alert(&#34;&amp;iexcl;\u003cb class=\&#34;foo\&#34;\u003eHello\u003c/b\u003e, \u003ctextarea\u003eO&#39;World\u003c/textarea\u003e!&#34;)">`,
+ `<button onclick="alert(&#34;\u0026iexcl;\u003cb class=\&#34;foo\&#34;\u003eHello\u003c/b\u003e, \u003ctextarea\u003eO&#39;World\u003c/textarea\u003e!&#34;)">`,
},
{
"typed HTML in RCDATA",
@@ -655,6 +655,11 @@ func TestEscape(t *testing.T) {
for _, test := range tests {
tmpl := New(test.name)
tmpl = Must(tmpl.Parse(test.input))
+ // Check for bug 6459: Tree field was not set in Parse.
+ if tmpl.Tree != tmpl.text.Tree {
+ t.Errorf("%s: tree not set properly", test.name)
+ continue
+ }
b := new(bytes.Buffer)
if err := tmpl.Execute(b, data); err != nil {
t.Errorf("%s: template execution failed: %s", test.name, err)
@@ -673,6 +678,10 @@ func TestEscape(t *testing.T) {
t.Errorf("%s: escaped output for pointer: want\n\t%q\ngot\n\t%q", test.name, w, g)
continue
}
+ if tmpl.Tree != tmpl.text.Tree {
+ t.Errorf("%s: tree mismatch", test.name)
+ continue
+ }
}
}
diff --git a/src/pkg/html/template/js.go b/src/pkg/html/template/js.go
index a9740931f..d594e0ad7 100644
--- a/src/pkg/html/template/js.go
+++ b/src/pkg/html/template/js.go
@@ -341,7 +341,7 @@ var jsRegexpReplacementTable = []string{
'}': `\}`,
}
-// isJSIdentPart returns whether the given rune is a JS identifier part.
+// isJSIdentPart reports whether the given rune is a JS identifier part.
// It does not handle all the non-Latin letters, joiners, and combining marks,
// but it does handle every codepoint that can occur in a numeric literal or
// a keyword.
diff --git a/src/pkg/html/template/template.go b/src/pkg/html/template/template.go
index e183898d5..11cc34a50 100644
--- a/src/pkg/html/template/template.go
+++ b/src/pkg/html/template/template.go
@@ -21,7 +21,9 @@ type Template struct {
// We could embed the text/template field, but it's safer not to because
// we need to keep our version of the name space and the underlying
// template's in sync.
- text *template.Template
+ text *template.Template
+ // The underlying template's parse tree, updated to be HTML-safe.
+ Tree *parse.Tree
*nameSpace // common to all associated templates
}
@@ -126,8 +128,10 @@ func (t *Template) Parse(src string) (*Template, error) {
if tmpl == nil {
tmpl = t.new(name)
}
+ // Restore our record of this text/template to its unescaped original state.
tmpl.escaped = false
tmpl.text = v
+ tmpl.Tree = v.Tree
}
return t, nil
}
@@ -149,6 +153,7 @@ func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error
ret := &Template{
false,
text,
+ text.Tree,
t.nameSpace,
}
t.set[name] = ret
@@ -176,6 +181,7 @@ func (t *Template) Clone() (*Template, error) {
ret := &Template{
false,
textClone,
+ textClone.Tree,
&nameSpace{
set: make(map[string]*Template),
},
@@ -186,15 +192,11 @@ func (t *Template) Clone() (*Template, error) {
if src == nil || src.escaped {
return nil, fmt.Errorf("html/template: cannot Clone %q after it has executed", t.Name())
}
- if x.Tree != nil {
- x.Tree = &parse.Tree{
- Name: x.Tree.Name,
- Root: x.Tree.Root.CopyList(),
- }
- }
+ x.Tree = x.Tree.Copy()
ret.set[name] = &Template{
false,
x,
+ x.Tree,
ret.nameSpace,
}
}
@@ -206,6 +208,7 @@ func New(name string) *Template {
tmpl := &Template{
false,
template.New(name),
+ nil,
&nameSpace{
set: make(map[string]*Template),
},
@@ -228,6 +231,7 @@ func (t *Template) new(name string) *Template {
tmpl := &Template{
false,
t.text.New(name),
+ nil,
t.nameSpace,
}
tmpl.set[name] = tmpl
diff --git a/src/pkg/html/template/transition.go b/src/pkg/html/template/transition.go
index 564eb2020..7f30a7ab8 100644
--- a/src/pkg/html/template/transition.go
+++ b/src/pkg/html/template/transition.go
@@ -504,12 +504,12 @@ var elementNameMap = map[string]element{
"title": elementTitle,
}
-// asciiAlpha returns whether c is an ASCII letter.
+// asciiAlpha reports whether c is an ASCII letter.
func asciiAlpha(c byte) bool {
return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z'
}
-// asciiAlphaNum returns whether c is an ASCII letter or digit.
+// asciiAlphaNum reports whether c is an ASCII letter or digit.
func asciiAlphaNum(c byte) bool {
return asciiAlpha(c) || '0' <= c && c <= '9'
}
diff --git a/src/pkg/image/color/color.go b/src/pkg/image/color/color.go
index 29a7b8a40..ff596a76a 100644
--- a/src/pkg/image/color/color.go
+++ b/src/pkg/image/color/color.go
@@ -253,13 +253,6 @@ func gray16Model(c Color) Color {
// Palette is a palette of colors.
type Palette []Color
-func diff(a, b uint32) uint32 {
- if a > b {
- return a - b
- }
- return b - a
-}
-
// Convert returns the palette color closest to c in Euclidean R,G,B space.
func (p Palette) Convert(c Color) Color {
if len(p) == 0 {
@@ -271,19 +264,20 @@ func (p Palette) Convert(c Color) Color {
// Index returns the index of the palette color closest to c in Euclidean
// R,G,B space.
func (p Palette) Index(c Color) int {
+ // A batch version of this computation is in image/draw/draw.go.
+
cr, cg, cb, _ := c.RGBA()
- // Shift by 1 bit to avoid potential uint32 overflow in sum-squared-difference.
- cr >>= 1
- cg >>= 1
- cb >>= 1
ret, bestSSD := 0, uint32(1<<32-1)
for i, v := range p {
vr, vg, vb, _ := v.RGBA()
- vr >>= 1
- vg >>= 1
- vb >>= 1
- dr, dg, db := diff(cr, vr), diff(cg, vg), diff(cb, vb)
- ssd := (dr * dr) + (dg * dg) + (db * db)
+ // We shift by 1 bit to avoid potential uint32 overflow in
+ // sum-squared-difference.
+ delta := (int32(cr) - int32(vr)) >> 1
+ ssd := uint32(delta * delta)
+ delta = (int32(cg) - int32(vg)) >> 1
+ ssd += uint32(delta * delta)
+ delta = (int32(cb) - int32(vb)) >> 1
+ ssd += uint32(delta * delta)
if ssd < bestSSD {
if ssd == 0 {
return i
diff --git a/src/pkg/image/color/palette/gen.go b/src/pkg/image/color/palette/gen.go
new file mode 100644
index 000000000..f20c021de
--- /dev/null
+++ b/src/pkg/image/color/palette/gen.go
@@ -0,0 +1,97 @@
+// Copyright 2013 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.
+
+// +build ignore
+
+package main
+
+// This program generates palette.go. Invoke it as
+// go run gen.go | gofmt > palette.go
+
+import (
+ "fmt"
+)
+
+func main() {
+ fmt.Println("// generated by go run gen.go; DO NOT EDIT")
+ fmt.Println()
+ fmt.Println("// Package palette provides standard color palettes.")
+ fmt.Println("package palette")
+ fmt.Println()
+ fmt.Println(`import "image/color"`)
+ fmt.Println()
+ printPlan9()
+ printWebSafe()
+}
+
+func printPlan9() {
+ c, lines := [3]int{}, [256]string{}
+ for r, i := 0, 0; r != 4; r++ {
+ for v := 0; v != 4; v, i = v+1, i+16 {
+ for g, j := 0, v-r; g != 4; g++ {
+ for b := 0; b != 4; b, j = b+1, j+1 {
+ den := r
+ if g > den {
+ den = g
+ }
+ if b > den {
+ den = b
+ }
+ if den == 0 {
+ c[0] = 0x11 * v
+ c[1] = 0x11 * v
+ c[2] = 0x11 * v
+ } else {
+ num := 17 * (4*den + v)
+ c[0] = r * num / den
+ c[1] = g * num / den
+ c[2] = b * num / den
+ }
+ lines[i+(j&0x0f)] =
+ fmt.Sprintf("\tcolor.RGBA{0x%02x, 0x%02x, 0x%02x, 0xff},", c[0], c[1], c[2])
+ }
+ }
+ }
+ }
+ fmt.Println("// Plan9 is a 256-color palette that partitions the 24-bit RGB space")
+ fmt.Println("// into 4×4×4 subdivision, with 4 shades in each subcube. Compared to the")
+ fmt.Println("// WebSafe, the idea is to reduce the color resolution by dicing the")
+ fmt.Println("// color cube into fewer cells, and to use the extra space to increase the")
+ fmt.Println("// intensity resolution. This results in 16 gray shades (4 gray subcubes with")
+ fmt.Println("// 4 samples in each), 13 shades of each primary and secondary color (3")
+ fmt.Println("// subcubes with 4 samples plus black) and a reasonable selection of colors")
+ fmt.Println("// covering the rest of the color cube. The advantage is better representation")
+ fmt.Println("// of continuous tones.")
+ fmt.Println("//")
+ fmt.Println("// This palette was used in the Plan 9 Operating System, described at")
+ fmt.Println("// http://plan9.bell-labs.com/magic/man2html/6/color")
+ fmt.Println("var Plan9 = []color.Color{")
+ for _, line := range lines {
+ fmt.Println(line)
+ }
+ fmt.Println("}")
+ fmt.Println()
+}
+
+func printWebSafe() {
+ lines := [6 * 6 * 6]string{}
+ for r := 0; r < 6; r++ {
+ for g := 0; g < 6; g++ {
+ for b := 0; b < 6; b++ {
+ lines[36*r+6*g+b] =
+ fmt.Sprintf("\tcolor.RGBA{0x%02x, 0x%02x, 0x%02x, 0xff},", 0x33*r, 0x33*g, 0x33*b)
+ }
+ }
+ }
+ fmt.Println("// WebSafe is a 216-color palette that was popularized by early versions")
+ fmt.Println("// of Netscape Navigator. It is also known as the Netscape Color Cube.")
+ fmt.Println("//")
+ fmt.Println("// See http://en.wikipedia.org/wiki/Web_colors#Web-safe_colors for details.")
+ fmt.Println("var WebSafe = []color.Color{")
+ for _, line := range lines {
+ fmt.Println(line)
+ }
+ fmt.Println("}")
+ fmt.Println()
+}
diff --git a/src/pkg/image/color/palette/palette.go b/src/pkg/image/color/palette/palette.go
new file mode 100644
index 000000000..3aba7401d
--- /dev/null
+++ b/src/pkg/image/color/palette/palette.go
@@ -0,0 +1,500 @@
+// generated by go run gen.go; DO NOT EDIT
+
+// Package palette provides standard color palettes.
+package palette
+
+import "image/color"
+
+// Plan9 is a 256-color palette that partitions the 24-bit RGB space
+// into 4×4×4 subdivision, with 4 shades in each subcube. Compared to the
+// WebSafe, the idea is to reduce the color resolution by dicing the
+// color cube into fewer cells, and to use the extra space to increase the
+// intensity resolution. This results in 16 gray shades (4 gray subcubes with
+// 4 samples in each), 13 shades of each primary and secondary color (3
+// subcubes with 4 samples plus black) and a reasonable selection of colors
+// covering the rest of the color cube. The advantage is better representation
+// of continuous tones.
+//
+// This palette was used in the Plan 9 Operating System, described at
+// http://plan9.bell-labs.com/magic/man2html/6/color
+var Plan9 = []color.Color{
+ color.RGBA{0x00, 0x00, 0x00, 0xff},
+ color.RGBA{0x00, 0x00, 0x44, 0xff},
+ color.RGBA{0x00, 0x00, 0x88, 0xff},
+ color.RGBA{0x00, 0x00, 0xcc, 0xff},
+ color.RGBA{0x00, 0x44, 0x00, 0xff},
+ color.RGBA{0x00, 0x44, 0x44, 0xff},
+ color.RGBA{0x00, 0x44, 0x88, 0xff},
+ color.RGBA{0x00, 0x44, 0xcc, 0xff},
+ color.RGBA{0x00, 0x88, 0x00, 0xff},
+ color.RGBA{0x00, 0x88, 0x44, 0xff},
+ color.RGBA{0x00, 0x88, 0x88, 0xff},
+ color.RGBA{0x00, 0x88, 0xcc, 0xff},
+ color.RGBA{0x00, 0xcc, 0x00, 0xff},
+ color.RGBA{0x00, 0xcc, 0x44, 0xff},
+ color.RGBA{0x00, 0xcc, 0x88, 0xff},
+ color.RGBA{0x00, 0xcc, 0xcc, 0xff},
+ color.RGBA{0x00, 0xdd, 0xdd, 0xff},
+ color.RGBA{0x11, 0x11, 0x11, 0xff},
+ color.RGBA{0x00, 0x00, 0x55, 0xff},
+ color.RGBA{0x00, 0x00, 0x99, 0xff},
+ color.RGBA{0x00, 0x00, 0xdd, 0xff},
+ color.RGBA{0x00, 0x55, 0x00, 0xff},
+ color.RGBA{0x00, 0x55, 0x55, 0xff},
+ color.RGBA{0x00, 0x4c, 0x99, 0xff},
+ color.RGBA{0x00, 0x49, 0xdd, 0xff},
+ color.RGBA{0x00, 0x99, 0x00, 0xff},
+ color.RGBA{0x00, 0x99, 0x4c, 0xff},
+ color.RGBA{0x00, 0x99, 0x99, 0xff},
+ color.RGBA{0x00, 0x93, 0xdd, 0xff},
+ color.RGBA{0x00, 0xdd, 0x00, 0xff},
+ color.RGBA{0x00, 0xdd, 0x49, 0xff},
+ color.RGBA{0x00, 0xdd, 0x93, 0xff},
+ color.RGBA{0x00, 0xee, 0x9e, 0xff},
+ color.RGBA{0x00, 0xee, 0xee, 0xff},
+ color.RGBA{0x22, 0x22, 0x22, 0xff},
+ color.RGBA{0x00, 0x00, 0x66, 0xff},
+ color.RGBA{0x00, 0x00, 0xaa, 0xff},
+ color.RGBA{0x00, 0x00, 0xee, 0xff},
+ color.RGBA{0x00, 0x66, 0x00, 0xff},
+ color.RGBA{0x00, 0x66, 0x66, 0xff},
+ color.RGBA{0x00, 0x55, 0xaa, 0xff},
+ color.RGBA{0x00, 0x4f, 0xee, 0xff},
+ color.RGBA{0x00, 0xaa, 0x00, 0xff},
+ color.RGBA{0x00, 0xaa, 0x55, 0xff},
+ color.RGBA{0x00, 0xaa, 0xaa, 0xff},
+ color.RGBA{0x00, 0x9e, 0xee, 0xff},
+ color.RGBA{0x00, 0xee, 0x00, 0xff},
+ color.RGBA{0x00, 0xee, 0x4f, 0xff},
+ color.RGBA{0x00, 0xff, 0x55, 0xff},
+ color.RGBA{0x00, 0xff, 0xaa, 0xff},
+ color.RGBA{0x00, 0xff, 0xff, 0xff},
+ color.RGBA{0x33, 0x33, 0x33, 0xff},
+ color.RGBA{0x00, 0x00, 0x77, 0xff},
+ color.RGBA{0x00, 0x00, 0xbb, 0xff},
+ color.RGBA{0x00, 0x00, 0xff, 0xff},
+ color.RGBA{0x00, 0x77, 0x00, 0xff},
+ color.RGBA{0x00, 0x77, 0x77, 0xff},
+ color.RGBA{0x00, 0x5d, 0xbb, 0xff},
+ color.RGBA{0x00, 0x55, 0xff, 0xff},
+ color.RGBA{0x00, 0xbb, 0x00, 0xff},
+ color.RGBA{0x00, 0xbb, 0x5d, 0xff},
+ color.RGBA{0x00, 0xbb, 0xbb, 0xff},
+ color.RGBA{0x00, 0xaa, 0xff, 0xff},
+ color.RGBA{0x00, 0xff, 0x00, 0xff},
+ color.RGBA{0x44, 0x00, 0x44, 0xff},
+ color.RGBA{0x44, 0x00, 0x88, 0xff},
+ color.RGBA{0x44, 0x00, 0xcc, 0xff},
+ color.RGBA{0x44, 0x44, 0x00, 0xff},
+ color.RGBA{0x44, 0x44, 0x44, 0xff},
+ color.RGBA{0x44, 0x44, 0x88, 0xff},
+ color.RGBA{0x44, 0x44, 0xcc, 0xff},
+ color.RGBA{0x44, 0x88, 0x00, 0xff},
+ color.RGBA{0x44, 0x88, 0x44, 0xff},
+ color.RGBA{0x44, 0x88, 0x88, 0xff},
+ color.RGBA{0x44, 0x88, 0xcc, 0xff},
+ color.RGBA{0x44, 0xcc, 0x00, 0xff},
+ color.RGBA{0x44, 0xcc, 0x44, 0xff},
+ color.RGBA{0x44, 0xcc, 0x88, 0xff},
+ color.RGBA{0x44, 0xcc, 0xcc, 0xff},
+ color.RGBA{0x44, 0x00, 0x00, 0xff},
+ color.RGBA{0x55, 0x00, 0x00, 0xff},
+ color.RGBA{0x55, 0x00, 0x55, 0xff},
+ color.RGBA{0x4c, 0x00, 0x99, 0xff},
+ color.RGBA{0x49, 0x00, 0xdd, 0xff},
+ color.RGBA{0x55, 0x55, 0x00, 0xff},
+ color.RGBA{0x55, 0x55, 0x55, 0xff},
+ color.RGBA{0x4c, 0x4c, 0x99, 0xff},
+ color.RGBA{0x49, 0x49, 0xdd, 0xff},
+ color.RGBA{0x4c, 0x99, 0x00, 0xff},
+ color.RGBA{0x4c, 0x99, 0x4c, 0xff},
+ color.RGBA{0x4c, 0x99, 0x99, 0xff},
+ color.RGBA{0x49, 0x93, 0xdd, 0xff},
+ color.RGBA{0x49, 0xdd, 0x00, 0xff},
+ color.RGBA{0x49, 0xdd, 0x49, 0xff},
+ color.RGBA{0x49, 0xdd, 0x93, 0xff},
+ color.RGBA{0x49, 0xdd, 0xdd, 0xff},
+ color.RGBA{0x4f, 0xee, 0xee, 0xff},
+ color.RGBA{0x66, 0x00, 0x00, 0xff},
+ color.RGBA{0x66, 0x00, 0x66, 0xff},
+ color.RGBA{0x55, 0x00, 0xaa, 0xff},
+ color.RGBA{0x4f, 0x00, 0xee, 0xff},
+ color.RGBA{0x66, 0x66, 0x00, 0xff},
+ color.RGBA{0x66, 0x66, 0x66, 0xff},
+ color.RGBA{0x55, 0x55, 0xaa, 0xff},
+ color.RGBA{0x4f, 0x4f, 0xee, 0xff},
+ color.RGBA{0x55, 0xaa, 0x00, 0xff},
+ color.RGBA{0x55, 0xaa, 0x55, 0xff},
+ color.RGBA{0x55, 0xaa, 0xaa, 0xff},
+ color.RGBA{0x4f, 0x9e, 0xee, 0xff},
+ color.RGBA{0x4f, 0xee, 0x00, 0xff},
+ color.RGBA{0x4f, 0xee, 0x4f, 0xff},
+ color.RGBA{0x4f, 0xee, 0x9e, 0xff},
+ color.RGBA{0x55, 0xff, 0xaa, 0xff},
+ color.RGBA{0x55, 0xff, 0xff, 0xff},
+ color.RGBA{0x77, 0x00, 0x00, 0xff},
+ color.RGBA{0x77, 0x00, 0x77, 0xff},
+ color.RGBA{0x5d, 0x00, 0xbb, 0xff},
+ color.RGBA{0x55, 0x00, 0xff, 0xff},
+ color.RGBA{0x77, 0x77, 0x00, 0xff},
+ color.RGBA{0x77, 0x77, 0x77, 0xff},
+ color.RGBA{0x5d, 0x5d, 0xbb, 0xff},
+ color.RGBA{0x55, 0x55, 0xff, 0xff},
+ color.RGBA{0x5d, 0xbb, 0x00, 0xff},
+ color.RGBA{0x5d, 0xbb, 0x5d, 0xff},
+ color.RGBA{0x5d, 0xbb, 0xbb, 0xff},
+ color.RGBA{0x55, 0xaa, 0xff, 0xff},
+ color.RGBA{0x55, 0xff, 0x00, 0xff},
+ color.RGBA{0x55, 0xff, 0x55, 0xff},
+ color.RGBA{0x88, 0x00, 0x88, 0xff},
+ color.RGBA{0x88, 0x00, 0xcc, 0xff},
+ color.RGBA{0x88, 0x44, 0x00, 0xff},
+ color.RGBA{0x88, 0x44, 0x44, 0xff},
+ color.RGBA{0x88, 0x44, 0x88, 0xff},
+ color.RGBA{0x88, 0x44, 0xcc, 0xff},
+ color.RGBA{0x88, 0x88, 0x00, 0xff},
+ color.RGBA{0x88, 0x88, 0x44, 0xff},
+ color.RGBA{0x88, 0x88, 0x88, 0xff},
+ color.RGBA{0x88, 0x88, 0xcc, 0xff},
+ color.RGBA{0x88, 0xcc, 0x00, 0xff},
+ color.RGBA{0x88, 0xcc, 0x44, 0xff},
+ color.RGBA{0x88, 0xcc, 0x88, 0xff},
+ color.RGBA{0x88, 0xcc, 0xcc, 0xff},
+ color.RGBA{0x88, 0x00, 0x00, 0xff},
+ color.RGBA{0x88, 0x00, 0x44, 0xff},
+ color.RGBA{0x99, 0x00, 0x4c, 0xff},
+ color.RGBA{0x99, 0x00, 0x99, 0xff},
+ color.RGBA{0x93, 0x00, 0xdd, 0xff},
+ color.RGBA{0x99, 0x4c, 0x00, 0xff},
+ color.RGBA{0x99, 0x4c, 0x4c, 0xff},
+ color.RGBA{0x99, 0x4c, 0x99, 0xff},
+ color.RGBA{0x93, 0x49, 0xdd, 0xff},
+ color.RGBA{0x99, 0x99, 0x00, 0xff},
+ color.RGBA{0x99, 0x99, 0x4c, 0xff},
+ color.RGBA{0x99, 0x99, 0x99, 0xff},
+ color.RGBA{0x93, 0x93, 0xdd, 0xff},
+ color.RGBA{0x93, 0xdd, 0x00, 0xff},
+ color.RGBA{0x93, 0xdd, 0x49, 0xff},
+ color.RGBA{0x93, 0xdd, 0x93, 0xff},
+ color.RGBA{0x93, 0xdd, 0xdd, 0xff},
+ color.RGBA{0x99, 0x00, 0x00, 0xff},
+ color.RGBA{0xaa, 0x00, 0x00, 0xff},
+ color.RGBA{0xaa, 0x00, 0x55, 0xff},
+ color.RGBA{0xaa, 0x00, 0xaa, 0xff},
+ color.RGBA{0x9e, 0x00, 0xee, 0xff},
+ color.RGBA{0xaa, 0x55, 0x00, 0xff},
+ color.RGBA{0xaa, 0x55, 0x55, 0xff},
+ color.RGBA{0xaa, 0x55, 0xaa, 0xff},
+ color.RGBA{0x9e, 0x4f, 0xee, 0xff},
+ color.RGBA{0xaa, 0xaa, 0x00, 0xff},
+ color.RGBA{0xaa, 0xaa, 0x55, 0xff},
+ color.RGBA{0xaa, 0xaa, 0xaa, 0xff},
+ color.RGBA{0x9e, 0x9e, 0xee, 0xff},
+ color.RGBA{0x9e, 0xee, 0x00, 0xff},
+ color.RGBA{0x9e, 0xee, 0x4f, 0xff},
+ color.RGBA{0x9e, 0xee, 0x9e, 0xff},
+ color.RGBA{0x9e, 0xee, 0xee, 0xff},
+ color.RGBA{0xaa, 0xff, 0xff, 0xff},
+ color.RGBA{0xbb, 0x00, 0x00, 0xff},
+ color.RGBA{0xbb, 0x00, 0x5d, 0xff},
+ color.RGBA{0xbb, 0x00, 0xbb, 0xff},
+ color.RGBA{0xaa, 0x00, 0xff, 0xff},
+ color.RGBA{0xbb, 0x5d, 0x00, 0xff},
+ color.RGBA{0xbb, 0x5d, 0x5d, 0xff},
+ color.RGBA{0xbb, 0x5d, 0xbb, 0xff},
+ color.RGBA{0xaa, 0x55, 0xff, 0xff},
+ color.RGBA{0xbb, 0xbb, 0x00, 0xff},
+ color.RGBA{0xbb, 0xbb, 0x5d, 0xff},
+ color.RGBA{0xbb, 0xbb, 0xbb, 0xff},
+ color.RGBA{0xaa, 0xaa, 0xff, 0xff},
+ color.RGBA{0xaa, 0xff, 0x00, 0xff},
+ color.RGBA{0xaa, 0xff, 0x55, 0xff},
+ color.RGBA{0xaa, 0xff, 0xaa, 0xff},
+ color.RGBA{0xcc, 0x00, 0xcc, 0xff},
+ color.RGBA{0xcc, 0x44, 0x00, 0xff},
+ color.RGBA{0xcc, 0x44, 0x44, 0xff},
+ color.RGBA{0xcc, 0x44, 0x88, 0xff},
+ color.RGBA{0xcc, 0x44, 0xcc, 0xff},
+ color.RGBA{0xcc, 0x88, 0x00, 0xff},
+ color.RGBA{0xcc, 0x88, 0x44, 0xff},
+ color.RGBA{0xcc, 0x88, 0x88, 0xff},
+ color.RGBA{0xcc, 0x88, 0xcc, 0xff},
+ color.RGBA{0xcc, 0xcc, 0x00, 0xff},
+ color.RGBA{0xcc, 0xcc, 0x44, 0xff},
+ color.RGBA{0xcc, 0xcc, 0x88, 0xff},
+ color.RGBA{0xcc, 0xcc, 0xcc, 0xff},
+ color.RGBA{0xcc, 0x00, 0x00, 0xff},
+ color.RGBA{0xcc, 0x00, 0x44, 0xff},
+ color.RGBA{0xcc, 0x00, 0x88, 0xff},
+ color.RGBA{0xdd, 0x00, 0x93, 0xff},
+ color.RGBA{0xdd, 0x00, 0xdd, 0xff},
+ color.RGBA{0xdd, 0x49, 0x00, 0xff},
+ color.RGBA{0xdd, 0x49, 0x49, 0xff},
+ color.RGBA{0xdd, 0x49, 0x93, 0xff},
+ color.RGBA{0xdd, 0x49, 0xdd, 0xff},
+ color.RGBA{0xdd, 0x93, 0x00, 0xff},
+ color.RGBA{0xdd, 0x93, 0x49, 0xff},
+ color.RGBA{0xdd, 0x93, 0x93, 0xff},
+ color.RGBA{0xdd, 0x93, 0xdd, 0xff},
+ color.RGBA{0xdd, 0xdd, 0x00, 0xff},
+ color.RGBA{0xdd, 0xdd, 0x49, 0xff},
+ color.RGBA{0xdd, 0xdd, 0x93, 0xff},
+ color.RGBA{0xdd, 0xdd, 0xdd, 0xff},
+ color.RGBA{0xdd, 0x00, 0x00, 0xff},
+ color.RGBA{0xdd, 0x00, 0x49, 0xff},
+ color.RGBA{0xee, 0x00, 0x4f, 0xff},
+ color.RGBA{0xee, 0x00, 0x9e, 0xff},
+ color.RGBA{0xee, 0x00, 0xee, 0xff},
+ color.RGBA{0xee, 0x4f, 0x00, 0xff},
+ color.RGBA{0xee, 0x4f, 0x4f, 0xff},
+ color.RGBA{0xee, 0x4f, 0x9e, 0xff},
+ color.RGBA{0xee, 0x4f, 0xee, 0xff},
+ color.RGBA{0xee, 0x9e, 0x00, 0xff},
+ color.RGBA{0xee, 0x9e, 0x4f, 0xff},
+ color.RGBA{0xee, 0x9e, 0x9e, 0xff},
+ color.RGBA{0xee, 0x9e, 0xee, 0xff},
+ color.RGBA{0xee, 0xee, 0x00, 0xff},
+ color.RGBA{0xee, 0xee, 0x4f, 0xff},
+ color.RGBA{0xee, 0xee, 0x9e, 0xff},
+ color.RGBA{0xee, 0xee, 0xee, 0xff},
+ color.RGBA{0xee, 0x00, 0x00, 0xff},
+ color.RGBA{0xff, 0x00, 0x00, 0xff},
+ color.RGBA{0xff, 0x00, 0x55, 0xff},
+ color.RGBA{0xff, 0x00, 0xaa, 0xff},
+ color.RGBA{0xff, 0x00, 0xff, 0xff},
+ color.RGBA{0xff, 0x55, 0x00, 0xff},
+ color.RGBA{0xff, 0x55, 0x55, 0xff},
+ color.RGBA{0xff, 0x55, 0xaa, 0xff},
+ color.RGBA{0xff, 0x55, 0xff, 0xff},
+ color.RGBA{0xff, 0xaa, 0x00, 0xff},
+ color.RGBA{0xff, 0xaa, 0x55, 0xff},
+ color.RGBA{0xff, 0xaa, 0xaa, 0xff},
+ color.RGBA{0xff, 0xaa, 0xff, 0xff},
+ color.RGBA{0xff, 0xff, 0x00, 0xff},
+ color.RGBA{0xff, 0xff, 0x55, 0xff},
+ color.RGBA{0xff, 0xff, 0xaa, 0xff},
+ color.RGBA{0xff, 0xff, 0xff, 0xff},
+}
+
+// WebSafe is a 216-color palette that was popularized by early versions
+// of Netscape Navigator. It is also known as the Netscape Color Cube.
+//
+// See http://en.wikipedia.org/wiki/Web_colors#Web-safe_colors for details.
+var WebSafe = []color.Color{
+ color.RGBA{0x00, 0x00, 0x00, 0xff},
+ color.RGBA{0x00, 0x00, 0x33, 0xff},
+ color.RGBA{0x00, 0x00, 0x66, 0xff},
+ color.RGBA{0x00, 0x00, 0x99, 0xff},
+ color.RGBA{0x00, 0x00, 0xcc, 0xff},
+ color.RGBA{0x00, 0x00, 0xff, 0xff},
+ color.RGBA{0x00, 0x33, 0x00, 0xff},
+ color.RGBA{0x00, 0x33, 0x33, 0xff},
+ color.RGBA{0x00, 0x33, 0x66, 0xff},
+ color.RGBA{0x00, 0x33, 0x99, 0xff},
+ color.RGBA{0x00, 0x33, 0xcc, 0xff},
+ color.RGBA{0x00, 0x33, 0xff, 0xff},
+ color.RGBA{0x00, 0x66, 0x00, 0xff},
+ color.RGBA{0x00, 0x66, 0x33, 0xff},
+ color.RGBA{0x00, 0x66, 0x66, 0xff},
+ color.RGBA{0x00, 0x66, 0x99, 0xff},
+ color.RGBA{0x00, 0x66, 0xcc, 0xff},
+ color.RGBA{0x00, 0x66, 0xff, 0xff},
+ color.RGBA{0x00, 0x99, 0x00, 0xff},
+ color.RGBA{0x00, 0x99, 0x33, 0xff},
+ color.RGBA{0x00, 0x99, 0x66, 0xff},
+ color.RGBA{0x00, 0x99, 0x99, 0xff},
+ color.RGBA{0x00, 0x99, 0xcc, 0xff},
+ color.RGBA{0x00, 0x99, 0xff, 0xff},
+ color.RGBA{0x00, 0xcc, 0x00, 0xff},
+ color.RGBA{0x00, 0xcc, 0x33, 0xff},
+ color.RGBA{0x00, 0xcc, 0x66, 0xff},
+ color.RGBA{0x00, 0xcc, 0x99, 0xff},
+ color.RGBA{0x00, 0xcc, 0xcc, 0xff},
+ color.RGBA{0x00, 0xcc, 0xff, 0xff},
+ color.RGBA{0x00, 0xff, 0x00, 0xff},
+ color.RGBA{0x00, 0xff, 0x33, 0xff},
+ color.RGBA{0x00, 0xff, 0x66, 0xff},
+ color.RGBA{0x00, 0xff, 0x99, 0xff},
+ color.RGBA{0x00, 0xff, 0xcc, 0xff},
+ color.RGBA{0x00, 0xff, 0xff, 0xff},
+ color.RGBA{0x33, 0x00, 0x00, 0xff},
+ color.RGBA{0x33, 0x00, 0x33, 0xff},
+ color.RGBA{0x33, 0x00, 0x66, 0xff},
+ color.RGBA{0x33, 0x00, 0x99, 0xff},
+ color.RGBA{0x33, 0x00, 0xcc, 0xff},
+ color.RGBA{0x33, 0x00, 0xff, 0xff},
+ color.RGBA{0x33, 0x33, 0x00, 0xff},
+ color.RGBA{0x33, 0x33, 0x33, 0xff},
+ color.RGBA{0x33, 0x33, 0x66, 0xff},
+ color.RGBA{0x33, 0x33, 0x99, 0xff},
+ color.RGBA{0x33, 0x33, 0xcc, 0xff},
+ color.RGBA{0x33, 0x33, 0xff, 0xff},
+ color.RGBA{0x33, 0x66, 0x00, 0xff},
+ color.RGBA{0x33, 0x66, 0x33, 0xff},
+ color.RGBA{0x33, 0x66, 0x66, 0xff},
+ color.RGBA{0x33, 0x66, 0x99, 0xff},
+ color.RGBA{0x33, 0x66, 0xcc, 0xff},
+ color.RGBA{0x33, 0x66, 0xff, 0xff},
+ color.RGBA{0x33, 0x99, 0x00, 0xff},
+ color.RGBA{0x33, 0x99, 0x33, 0xff},
+ color.RGBA{0x33, 0x99, 0x66, 0xff},
+ color.RGBA{0x33, 0x99, 0x99, 0xff},
+ color.RGBA{0x33, 0x99, 0xcc, 0xff},
+ color.RGBA{0x33, 0x99, 0xff, 0xff},
+ color.RGBA{0x33, 0xcc, 0x00, 0xff},
+ color.RGBA{0x33, 0xcc, 0x33, 0xff},
+ color.RGBA{0x33, 0xcc, 0x66, 0xff},
+ color.RGBA{0x33, 0xcc, 0x99, 0xff},
+ color.RGBA{0x33, 0xcc, 0xcc, 0xff},
+ color.RGBA{0x33, 0xcc, 0xff, 0xff},
+ color.RGBA{0x33, 0xff, 0x00, 0xff},
+ color.RGBA{0x33, 0xff, 0x33, 0xff},
+ color.RGBA{0x33, 0xff, 0x66, 0xff},
+ color.RGBA{0x33, 0xff, 0x99, 0xff},
+ color.RGBA{0x33, 0xff, 0xcc, 0xff},
+ color.RGBA{0x33, 0xff, 0xff, 0xff},
+ color.RGBA{0x66, 0x00, 0x00, 0xff},
+ color.RGBA{0x66, 0x00, 0x33, 0xff},
+ color.RGBA{0x66, 0x00, 0x66, 0xff},
+ color.RGBA{0x66, 0x00, 0x99, 0xff},
+ color.RGBA{0x66, 0x00, 0xcc, 0xff},
+ color.RGBA{0x66, 0x00, 0xff, 0xff},
+ color.RGBA{0x66, 0x33, 0x00, 0xff},
+ color.RGBA{0x66, 0x33, 0x33, 0xff},
+ color.RGBA{0x66, 0x33, 0x66, 0xff},
+ color.RGBA{0x66, 0x33, 0x99, 0xff},
+ color.RGBA{0x66, 0x33, 0xcc, 0xff},
+ color.RGBA{0x66, 0x33, 0xff, 0xff},
+ color.RGBA{0x66, 0x66, 0x00, 0xff},
+ color.RGBA{0x66, 0x66, 0x33, 0xff},
+ color.RGBA{0x66, 0x66, 0x66, 0xff},
+ color.RGBA{0x66, 0x66, 0x99, 0xff},
+ color.RGBA{0x66, 0x66, 0xcc, 0xff},
+ color.RGBA{0x66, 0x66, 0xff, 0xff},
+ color.RGBA{0x66, 0x99, 0x00, 0xff},
+ color.RGBA{0x66, 0x99, 0x33, 0xff},
+ color.RGBA{0x66, 0x99, 0x66, 0xff},
+ color.RGBA{0x66, 0x99, 0x99, 0xff},
+ color.RGBA{0x66, 0x99, 0xcc, 0xff},
+ color.RGBA{0x66, 0x99, 0xff, 0xff},
+ color.RGBA{0x66, 0xcc, 0x00, 0xff},
+ color.RGBA{0x66, 0xcc, 0x33, 0xff},
+ color.RGBA{0x66, 0xcc, 0x66, 0xff},
+ color.RGBA{0x66, 0xcc, 0x99, 0xff},
+ color.RGBA{0x66, 0xcc, 0xcc, 0xff},
+ color.RGBA{0x66, 0xcc, 0xff, 0xff},
+ color.RGBA{0x66, 0xff, 0x00, 0xff},
+ color.RGBA{0x66, 0xff, 0x33, 0xff},
+ color.RGBA{0x66, 0xff, 0x66, 0xff},
+ color.RGBA{0x66, 0xff, 0x99, 0xff},
+ color.RGBA{0x66, 0xff, 0xcc, 0xff},
+ color.RGBA{0x66, 0xff, 0xff, 0xff},
+ color.RGBA{0x99, 0x00, 0x00, 0xff},
+ color.RGBA{0x99, 0x00, 0x33, 0xff},
+ color.RGBA{0x99, 0x00, 0x66, 0xff},
+ color.RGBA{0x99, 0x00, 0x99, 0xff},
+ color.RGBA{0x99, 0x00, 0xcc, 0xff},
+ color.RGBA{0x99, 0x00, 0xff, 0xff},
+ color.RGBA{0x99, 0x33, 0x00, 0xff},
+ color.RGBA{0x99, 0x33, 0x33, 0xff},
+ color.RGBA{0x99, 0x33, 0x66, 0xff},
+ color.RGBA{0x99, 0x33, 0x99, 0xff},
+ color.RGBA{0x99, 0x33, 0xcc, 0xff},
+ color.RGBA{0x99, 0x33, 0xff, 0xff},
+ color.RGBA{0x99, 0x66, 0x00, 0xff},
+ color.RGBA{0x99, 0x66, 0x33, 0xff},
+ color.RGBA{0x99, 0x66, 0x66, 0xff},
+ color.RGBA{0x99, 0x66, 0x99, 0xff},
+ color.RGBA{0x99, 0x66, 0xcc, 0xff},
+ color.RGBA{0x99, 0x66, 0xff, 0xff},
+ color.RGBA{0x99, 0x99, 0x00, 0xff},
+ color.RGBA{0x99, 0x99, 0x33, 0xff},
+ color.RGBA{0x99, 0x99, 0x66, 0xff},
+ color.RGBA{0x99, 0x99, 0x99, 0xff},
+ color.RGBA{0x99, 0x99, 0xcc, 0xff},
+ color.RGBA{0x99, 0x99, 0xff, 0xff},
+ color.RGBA{0x99, 0xcc, 0x00, 0xff},
+ color.RGBA{0x99, 0xcc, 0x33, 0xff},
+ color.RGBA{0x99, 0xcc, 0x66, 0xff},
+ color.RGBA{0x99, 0xcc, 0x99, 0xff},
+ color.RGBA{0x99, 0xcc, 0xcc, 0xff},
+ color.RGBA{0x99, 0xcc, 0xff, 0xff},
+ color.RGBA{0x99, 0xff, 0x00, 0xff},
+ color.RGBA{0x99, 0xff, 0x33, 0xff},
+ color.RGBA{0x99, 0xff, 0x66, 0xff},
+ color.RGBA{0x99, 0xff, 0x99, 0xff},
+ color.RGBA{0x99, 0xff, 0xcc, 0xff},
+ color.RGBA{0x99, 0xff, 0xff, 0xff},
+ color.RGBA{0xcc, 0x00, 0x00, 0xff},
+ color.RGBA{0xcc, 0x00, 0x33, 0xff},
+ color.RGBA{0xcc, 0x00, 0x66, 0xff},
+ color.RGBA{0xcc, 0x00, 0x99, 0xff},
+ color.RGBA{0xcc, 0x00, 0xcc, 0xff},
+ color.RGBA{0xcc, 0x00, 0xff, 0xff},
+ color.RGBA{0xcc, 0x33, 0x00, 0xff},
+ color.RGBA{0xcc, 0x33, 0x33, 0xff},
+ color.RGBA{0xcc, 0x33, 0x66, 0xff},
+ color.RGBA{0xcc, 0x33, 0x99, 0xff},
+ color.RGBA{0xcc, 0x33, 0xcc, 0xff},
+ color.RGBA{0xcc, 0x33, 0xff, 0xff},
+ color.RGBA{0xcc, 0x66, 0x00, 0xff},
+ color.RGBA{0xcc, 0x66, 0x33, 0xff},
+ color.RGBA{0xcc, 0x66, 0x66, 0xff},
+ color.RGBA{0xcc, 0x66, 0x99, 0xff},
+ color.RGBA{0xcc, 0x66, 0xcc, 0xff},
+ color.RGBA{0xcc, 0x66, 0xff, 0xff},
+ color.RGBA{0xcc, 0x99, 0x00, 0xff},
+ color.RGBA{0xcc, 0x99, 0x33, 0xff},
+ color.RGBA{0xcc, 0x99, 0x66, 0xff},
+ color.RGBA{0xcc, 0x99, 0x99, 0xff},
+ color.RGBA{0xcc, 0x99, 0xcc, 0xff},
+ color.RGBA{0xcc, 0x99, 0xff, 0xff},
+ color.RGBA{0xcc, 0xcc, 0x00, 0xff},
+ color.RGBA{0xcc, 0xcc, 0x33, 0xff},
+ color.RGBA{0xcc, 0xcc, 0x66, 0xff},
+ color.RGBA{0xcc, 0xcc, 0x99, 0xff},
+ color.RGBA{0xcc, 0xcc, 0xcc, 0xff},
+ color.RGBA{0xcc, 0xcc, 0xff, 0xff},
+ color.RGBA{0xcc, 0xff, 0x00, 0xff},
+ color.RGBA{0xcc, 0xff, 0x33, 0xff},
+ color.RGBA{0xcc, 0xff, 0x66, 0xff},
+ color.RGBA{0xcc, 0xff, 0x99, 0xff},
+ color.RGBA{0xcc, 0xff, 0xcc, 0xff},
+ color.RGBA{0xcc, 0xff, 0xff, 0xff},
+ color.RGBA{0xff, 0x00, 0x00, 0xff},
+ color.RGBA{0xff, 0x00, 0x33, 0xff},
+ color.RGBA{0xff, 0x00, 0x66, 0xff},
+ color.RGBA{0xff, 0x00, 0x99, 0xff},
+ color.RGBA{0xff, 0x00, 0xcc, 0xff},
+ color.RGBA{0xff, 0x00, 0xff, 0xff},
+ color.RGBA{0xff, 0x33, 0x00, 0xff},
+ color.RGBA{0xff, 0x33, 0x33, 0xff},
+ color.RGBA{0xff, 0x33, 0x66, 0xff},
+ color.RGBA{0xff, 0x33, 0x99, 0xff},
+ color.RGBA{0xff, 0x33, 0xcc, 0xff},
+ color.RGBA{0xff, 0x33, 0xff, 0xff},
+ color.RGBA{0xff, 0x66, 0x00, 0xff},
+ color.RGBA{0xff, 0x66, 0x33, 0xff},
+ color.RGBA{0xff, 0x66, 0x66, 0xff},
+ color.RGBA{0xff, 0x66, 0x99, 0xff},
+ color.RGBA{0xff, 0x66, 0xcc, 0xff},
+ color.RGBA{0xff, 0x66, 0xff, 0xff},
+ color.RGBA{0xff, 0x99, 0x00, 0xff},
+ color.RGBA{0xff, 0x99, 0x33, 0xff},
+ color.RGBA{0xff, 0x99, 0x66, 0xff},
+ color.RGBA{0xff, 0x99, 0x99, 0xff},
+ color.RGBA{0xff, 0x99, 0xcc, 0xff},
+ color.RGBA{0xff, 0x99, 0xff, 0xff},
+ color.RGBA{0xff, 0xcc, 0x00, 0xff},
+ color.RGBA{0xff, 0xcc, 0x33, 0xff},
+ color.RGBA{0xff, 0xcc, 0x66, 0xff},
+ color.RGBA{0xff, 0xcc, 0x99, 0xff},
+ color.RGBA{0xff, 0xcc, 0xcc, 0xff},
+ color.RGBA{0xff, 0xcc, 0xff, 0xff},
+ color.RGBA{0xff, 0xff, 0x00, 0xff},
+ color.RGBA{0xff, 0xff, 0x33, 0xff},
+ color.RGBA{0xff, 0xff, 0x66, 0xff},
+ color.RGBA{0xff, 0xff, 0x99, 0xff},
+ color.RGBA{0xff, 0xff, 0xcc, 0xff},
+ color.RGBA{0xff, 0xff, 0xff, 0xff},
+}
diff --git a/src/pkg/image/decode_example_test.go b/src/pkg/image/decode_example_test.go
index aa5a841c0..21e90fea4 100644
--- a/src/pkg/image/decode_example_test.go
+++ b/src/pkg/image/decode_example_test.go
@@ -6,10 +6,11 @@
package image_test
import (
+ "encoding/base64"
"fmt"
"image"
"log"
- "os"
+ "strings"
// Package image/jpeg is not used explicitly in the code below,
// but is imported for its initialization side-effect, which allows
@@ -21,15 +22,15 @@ import (
)
func Example() {
- // Open the file.
- file, err := os.Open("testdata/video-001.jpeg")
- if err != nil {
- log.Fatal(err)
- }
- defer file.Close()
-
- // Decode the image.
- m, _, err := image.Decode(file)
+ // Decode the JPEG data. If reading from file, create a reader with
+ //
+ // reader, err := os.Open("testdata/video-001.q50.420.jpeg")
+ // if err != nil {
+ // log.Fatal(err)
+ // }
+ // defer reader.Close()
+ reader := base64.NewDecoder(base64.StdEncoding, strings.NewReader(data))
+ m, _, err := image.Decode(reader)
if err != nil {
log.Fatal(err)
}
@@ -60,20 +61,80 @@ func Example() {
}
// Output:
// bin red green blue alpha
- // 0x0000-0x0fff: 471 819 7596 0
- // 0x1000-0x1fff: 576 2892 726 0
- // 0x2000-0x2fff: 1038 2330 943 0
- // 0x3000-0x3fff: 883 2321 1014 0
- // 0x4000-0x4fff: 501 1295 525 0
- // 0x5000-0x5fff: 302 962 242 0
- // 0x6000-0x6fff: 219 358 150 0
- // 0x7000-0x7fff: 352 281 192 0
- // 0x8000-0x8fff: 3688 216 246 0
- // 0x9000-0x9fff: 2277 237 283 0
- // 0xa000-0xafff: 971 254 357 0
- // 0xb000-0xbfff: 317 306 429 0
- // 0xc000-0xcfff: 203 402 401 0
- // 0xd000-0xdfff: 256 394 241 0
- // 0xe000-0xefff: 378 343 173 0
- // 0xf000-0xffff: 3018 2040 1932 15450
+ // 0x0000-0x0fff: 353 759 7228 0
+ // 0x1000-0x1fff: 629 2944 1036 0
+ // 0x2000-0x2fff: 1075 2319 984 0
+ // 0x3000-0x3fff: 838 2291 988 0
+ // 0x4000-0x4fff: 540 1302 542 0
+ // 0x5000-0x5fff: 319 971 263 0
+ // 0x6000-0x6fff: 316 377 178 0
+ // 0x7000-0x7fff: 581 280 216 0
+ // 0x8000-0x8fff: 3457 228 274 0
+ // 0x9000-0x9fff: 2294 237 334 0
+ // 0xa000-0xafff: 938 283 370 0
+ // 0xb000-0xbfff: 322 338 401 0
+ // 0xc000-0xcfff: 229 386 295 0
+ // 0xd000-0xdfff: 263 416 281 0
+ // 0xe000-0xefff: 538 433 312 0
+ // 0xf000-0xffff: 2758 1886 1748 15450
}
+
+const data = `
+/9j/4AAQSkZJRgABAQIAHAAcAAD/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdA
+SFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2Nj
+Y2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wAARCABnAJYDASIAAhEBAxEB/8QA
+HwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIh
+MUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVW
+V1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXG
+x8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQF
+BgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAV
+YnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOE
+hYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq
+8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDlwKMD0pwzSiuK57QzGDxS7D6in8Y5ximnAPUfSlcq4m3ilUYp
+2OKXHvRcVxnTtS7c07HNFK4DQPakC4PNOA+tOx70XAjK/So5gBGP94fzqfvUVx/qxx/EP51UXqRP4WSE
+cmgjilP3jSEZqS0IO/NGDnpUiocDg/McDjvV6HTPOdVWYgsM5KcfzzQ2JySM2jp6VYu7SWzmMUwG4cgj
+kMPUVBjjtTGtRu0Zopw+lFFxhinrGzuqqMsxAA9yaXFSRv5cqSEcIwYj6GpuZ30O30fSLKzhUpbpNMv3
+5XGTn29BV28jt7pPLuIVljPBBFVreYx+VbqAjycgt3x14zRcNOxGyVFHQkIc/wA61exyKLbuzjdZ046d
+ftEuTEw3Rk9SPT8P8Kpbea3tchbyVae4JkjbbGpGdwOM89Af6ViFTWUtGdcXoM2+woK1JtpNtTcoZt+l
+Jt7ZqTbRtouFyPFRXI/c9D94fzqzioLsfuD/ALw/nVReqIn8LJCOTSY+tSMOTmkIpXLRu+F0t5pJxPHG
+wjjUAuBjJJz1+laD6Pai+WaK9SBX6puzn6ZP+NV/Dkdtc6ZNbyAFwxLAHDYPv6VoQ21nPNEEiQGEFRtk
+Gf0NaWTOeW7Of8QwGG4MRZnEbYXPJwRnOR0zWNXW+KrqBLUWi5EjbWCgcAA9c/gRXKYqZaGlK/LqMH0F
+FLtHvRSNiYD2pSDTgpp6p0ywUHoTULXYxcktzrdCf7Xo8LP/AKyEmMNjJ46dfbFWJ5TDGNwB9lFUvDV9
+YrbfYGbyrjcWG88S57g+vtV26ZIvMlumKwwjLZ6V0WfU54yTvYwtbubea2WNWbzg4bYQeBgj8OtYeKhj
+u4y2HQxqxOD1xzxmrWAQCCGB6EGsaikndmsJxeiYzBo280/Z7UbayuaXGY5oIp+2lx9KLjIsVDeD/Rj/
+ALy/zq1t96r3y4tT/vL/ADq4P3kRP4WSleTSFKkkKoCW4GaqNcMxIjXj1pxjKT0FKrGC1Nrw3vGrKkYz
+5kTAr6455/HH510UdwPtRgWCbzF5+YYUf4Vwun39xpmoR3qASMmQUJwGU9Rnt/8AWrpbrxhb8/ZdOmaQ
+gAGZwFH5ZJrpVKVlY5ZYhN6kXiu2eO/ikZlIljAAB5yM549OawSOOlPuLqe+umuLqTfM4OSOAo7ADsKh
+hl/cRsTuJHPv7mlKi3sVTxNtGP20VJhThgSQaK52mnZnUqsWrpkyeUrr5pABOAPU1AGaXUCWJISHGPfP
+P8qL7BiKnsMg46H3qrbzupbj5mPTPTpXVSglG551SpzSsXJ4/MBUgYIxyKpySyGBYJriV1D7kRpCVH4V
+bSeNJ4xchni3DeqnBI+td7F4b0mKIRjT45VbktJlzk455+n6VtYzv2PNwFZWBHBGKVJDGVC54/nXQeMN
+NttLNkba1jgWVWDmM8bhg4/nzXLSSbXVj6fyNKUdNRp21RtIRJGrjuM0u3FQ2DbodvcEkfQmrW2vLqLl
+k0ejCXNFMj2/jQV9qkxSYNRcsZiq2oI32N2CkhWXJxwOe9XMcVt6hoPn6dFaW0wgRpNzvKDlz6+/0rai
+ryv2Jm9LHJai+ZRGCBjnr71ErdAxAY9B611t1Y2cunbbaOQ3FvKZI3UqGlZMbiWwfcfhV231iwvLSM3U
+lt5Uq52TuZG+hGMA12xXJGxxzjzybOQtNOvb5j9ktZJhnBIHyg+5PFX38JayqK/2eLJIBUTgkDA9q7ex
+itrSHFpGsUbndhRgc+g7VNIyfZJAoJZUbb3I46CtFJMylBo8sdWhmYMuCnylc9wef5VUT7+1chc5NS7h
+sUZO5RtIPUH3pkBDOxxxmqM9TQtn+WilhHfHaik43KTG3Z4IyPyrNVjGCsZ+dmwv6V3cXhSG8sYpJLud
+JJIwxChdoJGcYx/Wkg8DafA4knvLiQr/ALqj+VQpKw3FtnFFfvbiSMgZJ6/jXp2n3d9cQRBTFsKD96EP
+oOxPU/8A68VVtbbRtMVntbePKDLTSHJH/Aj/AEqHTvE66rq72VugMMcbSGTnL4wMAfjT5n0HyW3L+s6b
+baxaJBdzN+7bcrxkAhun0rz3VNCv7e7lgigknWI43xLu6jjIHTjtXqfkpPGVYsBkghTikgsYIN/lhgXb
+cxLkknp/ShczQ7xtY8vtEmhkj8yGRBuCnehUcnHcVtmwfJ/fQ8e7f/E12txZW91C0U6b42xlST2OR/Ko
+Bo1gM/uW55/1jf41nOipu7LhV5FZHIGzI6zwj/vr/Ck+yr3uYf8Ax7/CutbQdMb71tn/ALaN/jSf8I/p
+X/PoP++2/wAan6rAr6wzkWt0II+1Rc/7Lf4Vd1eeCSKBbdZDdShYoiZNoyfY10P/AAj2lf8APmP++2/x
+oPh/SjKspsozIuNrZORjp3qo0FHYPb3OZt7ae3SzjuItsiRSAgnccl/UA+3Q1yNjKLR4ZZYY5VD7tkv3
+WwO/+e1evPp9nI257aJm6bioz1z1+tY+s6Hplnot9PbWMMcqwOFcLyOO1bJWMZSTOPHi+9w3mosrlyd2
+9lCj02g9P/1e9a3hzxAbl2ikZRcdQueHHt7j864Y8Z4I4oRzG6urFWU5BHBB7HNJxTFGbR6he6Vpmtgm
+eLy5zwZI/lb8fX8azIvBUUTHdfSFP4QsYB/HNZ+k+KEnRY75hHOvAk6K/v7H9K6yyvlnQBmDZ6GsnzR0
+N0oy1RzOtaN/Y1tHNFO06u+zYy4I4Jzx9KKveJblXuordSGES5b6n/62PzorKVdp2LjQTVyWz8UWEWlq
+jSgyxfJt6EgdDzWTdeLIZGO7zHI/hVajGmWWP+PWL8qwlAIURrhpMAHHJA71pRcZrToZzcoEuo6heakA
+GHk245CZ6/X1qPTLq40q+W5t2QybSpDAkEEc55/zilk5k2r91eKhLDzWz2rpsczbbuemeD76fUNG865I
+MiysmQMZAAwa3a5j4ftu0ByP+fh/5CulkLLG7INzhSVHqe1Fh3uOoqn9qQQxyhndmHIxwOmSR2xQ13KD
+KoiBZOV9JBnt707MVy5RWdNdy7wRGf3bfMinnO1jg+vY03WXLaJO3mhQ20b0zwpYf0qlG7S7icrJs08U
+VwumgC+YiQyeVtZH567hzj8aSL949oGhE/2v5pJCDkksQwBHC4/+vXQ8LZ2uYxxCavY7us/xCcaBfn0h
+b+VP0bnSrb94ZMJgOecj1rl/GfidUE2k2gy5+SeQjgA/wj3rlas2jdao48qrjLAGkSKPk4Gc1WMj92I+
+lIJnU8OfxPWo5inBokmtQTmM4OOh71b0q6vbFmWCbaxHyqQGAP0PT8KhSTzVyo5ocSKA5VfTOTmqsmRd
+pl99XjPzThzK3zOeOSeveirNmkgg/fIpYsTkYORxRXmzlTjJqx6EVUcU7mhkKCzdAK59QI9zYxtG1fYU
+UVtgtmY4nZEa8Ak9aqFv3rfSiiu1nMeifDv/AJF+T/r4f+QrqqKKQwzQenNFFMCOKFIgNuThdoJ5OPSk
+ubeK6t3gnXdG4wwziiii/UTKMOg6dbzJLFE4dSCP3rEdeOM8805tDsGMvySgSsS6rM6gk9eAcUUVftZt
+3uyVGNthuq3Eei6DK8H7sRR7YuMgHtXkc8rzTNLM26RyWY+p70UVnLY0iEsUipG7rhZBlDkc1HgYoorM
+0HwyBXGeRjmrcUhMg2ghezd//rUUVcTKW5s2jZtY/QDaOKKKK8ip8bPRj8KP/9k=
+`
diff --git a/src/pkg/image/draw/draw.go b/src/pkg/image/draw/draw.go
index 56d30dd6f..661230e7c 100644
--- a/src/pkg/image/draw/draw.go
+++ b/src/pkg/image/draw/draw.go
@@ -16,6 +16,19 @@ import (
// m is the maximum color value returned by image.Color.RGBA.
const m = 1<<16 - 1
+// Image is an image.Image with a Set method to change a single pixel.
+type Image interface {
+ image.Image
+ Set(x, y int, c color.Color)
+}
+
+// Quantizer produces a palette for an image.
+type Quantizer interface {
+ // Quantize appends up to cap(p) - len(p) colors to p and returns the
+ // updated palette suitable for converting m to a paletted image.
+ Quantize(p color.Palette, m image.Image) color.Palette
+}
+
// Op is a Porter-Duff compositing operator.
type Op int
@@ -26,15 +39,31 @@ const (
Src
)
-// A draw.Image is an image.Image with a Set method to change a single pixel.
-type Image interface {
- image.Image
- Set(x, y int, c color.Color)
+// Draw implements the Drawer interface by calling the Draw function with this
+// Op.
+func (op Op) Draw(dst Image, r image.Rectangle, src image.Image, sp image.Point) {
+ DrawMask(dst, r, src, sp, nil, image.Point{}, op)
}
-// Draw calls DrawMask with a nil mask.
-func Draw(dst Image, r image.Rectangle, src image.Image, sp image.Point, op Op) {
- DrawMask(dst, r, src, sp, nil, image.ZP, op)
+// Drawer contains the Draw method.
+type Drawer interface {
+ // Draw aligns r.Min in dst with sp in src and then replaces the
+ // rectangle r in dst with the result of drawing src on dst.
+ Draw(dst Image, r image.Rectangle, src image.Image, sp image.Point)
+}
+
+// FloydSteinberg is a Drawer that is the Src Op with Floyd-Steinberg error
+// diffusion.
+var FloydSteinberg Drawer = floydSteinberg{}
+
+type floydSteinberg struct{}
+
+func (floydSteinberg) Draw(dst Image, r image.Rectangle, src image.Image, sp image.Point) {
+ clip(dst, &r, src, &sp, nil, nil)
+ if r.Empty() {
+ return
+ }
+ drawPaletted(dst, r, src, sp, true)
}
// clip clips r against each image's bounds (after translating into the
@@ -58,6 +87,17 @@ func clip(dst Image, r *image.Rectangle, src image.Image, sp *image.Point, mask
(*mp).Y += dy
}
+func processBackward(dst Image, r image.Rectangle, src image.Image, sp image.Point) bool {
+ return image.Image(dst) == src &&
+ r.Overlaps(r.Add(sp.Sub(r.Min))) &&
+ (sp.Y < r.Min.Y || (sp.Y == r.Min.Y && sp.X < r.Min.X))
+}
+
+// Draw calls DrawMask with a nil mask.
+func Draw(dst Image, r image.Rectangle, src image.Image, sp image.Point, op Op) {
+ DrawMask(dst, r, src, sp, nil, image.Point{}, op)
+}
+
// DrawMask aligns r.Min in dst with sp in src and mp in mask and then replaces the rectangle r
// in dst with the result of a Porter-Duff composition. A nil mask is treated as opaque.
func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op) {
@@ -67,7 +107,8 @@ func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mas
}
// Fast paths for special cases. If none of them apply, then we fall back to a general but slow implementation.
- if dst0, ok := dst.(*image.RGBA); ok {
+ switch dst0 := dst.(type) {
+ case *image.RGBA:
if op == Over {
if mask == nil {
switch src0 := src.(type) {
@@ -113,19 +154,20 @@ func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mas
}
drawRGBA(dst0, r, src, sp, mask, mp, op)
return
+ case *image.Paletted:
+ if op == Src && mask == nil && !processBackward(dst, r, src, sp) {
+ drawPaletted(dst0, r, src, sp, false)
+ }
}
x0, x1, dx := r.Min.X, r.Max.X, 1
y0, y1, dy := r.Min.Y, r.Max.Y, 1
- if image.Image(dst) == src && r.Overlaps(r.Add(sp.Sub(r.Min))) {
- // Rectangles overlap: process backward?
- if sp.Y < r.Min.Y || sp.Y == r.Min.Y && sp.X < r.Min.X {
- x0, x1, dx = x1-1, x0-1, -1
- y0, y1, dy = y1-1, y0-1, -1
- }
+ if processBackward(dst, r, src, sp) {
+ x0, x1, dx = x1-1, x0-1, -1
+ y0, y1, dy = y1-1, y0-1, -1
}
- var out *color.RGBA64
+ var out color.RGBA64
sy := sp.Y + y0 - r.Min.Y
my := mp.Y + y0 - r.Min.Y
for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy {
@@ -147,9 +189,6 @@ func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mas
dst.Set(x, y, src.At(sx, sy))
default:
sr, sg, sb, sa := src.At(sx, sy).RGBA()
- if out == nil {
- out = new(color.RGBA64)
- }
if op == Over {
dr, dg, db, da := dst.At(x, y).RGBA()
a := m - (sa * ma / m)
@@ -163,7 +202,11 @@ func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mas
out.B = uint16(sb * ma / m)
out.A = uint16(sa * ma / m)
}
- dst.Set(x, y, out)
+ // The third argument is &out instead of out (and out is
+ // declared outside of the inner loop) to avoid the implicit
+ // conversion to color.Color here allocating memory in the
+ // inner loop if sizeof(color.RGBA64) > sizeof(uintptr).
+ dst.Set(x, y, &out)
}
}
}
@@ -500,3 +543,131 @@ func drawRGBA(dst *image.RGBA, r image.Rectangle, src image.Image, sp image.Poin
i0 += dy * dst.Stride
}
}
+
+// clamp clamps i to the interval [0, 0xffff].
+func clamp(i int32) int32 {
+ if i < 0 {
+ return 0
+ }
+ if i > 0xffff {
+ return 0xffff
+ }
+ return i
+}
+
+func drawPaletted(dst Image, r image.Rectangle, src image.Image, sp image.Point, floydSteinberg bool) {
+ // TODO(nigeltao): handle the case where the dst and src overlap.
+ // Does it even make sense to try and do Floyd-Steinberg whilst
+ // walking the image backward (right-to-left bottom-to-top)?
+
+ // If dst is an *image.Paletted, we have a fast path for dst.Set and
+ // dst.At. The dst.Set equivalent is a batch version of the algorithm
+ // used by color.Palette's Index method in image/color/color.go, plus
+ // optional Floyd-Steinberg error diffusion.
+ palette, pix, stride := [][3]int32(nil), []byte(nil), 0
+ if p, ok := dst.(*image.Paletted); ok {
+ palette = make([][3]int32, len(p.Palette))
+ for i, col := range p.Palette {
+ r, g, b, _ := col.RGBA()
+ palette[i][0] = int32(r)
+ palette[i][1] = int32(g)
+ palette[i][2] = int32(b)
+ }
+ pix, stride = p.Pix[p.PixOffset(r.Min.X, r.Min.Y):], p.Stride
+ }
+
+ // quantErrorCurr and quantErrorNext are the Floyd-Steinberg quantization
+ // errors that have been propagated to the pixels in the current and next
+ // rows. The +2 simplifies calculation near the edges.
+ var quantErrorCurr, quantErrorNext [][3]int32
+ if floydSteinberg {
+ quantErrorCurr = make([][3]int32, r.Dx()+2)
+ quantErrorNext = make([][3]int32, r.Dx()+2)
+ }
+
+ // Loop over each source pixel.
+ out := color.RGBA64{A: 0xffff}
+ for y := 0; y != r.Dy(); y++ {
+ for x := 0; x != r.Dx(); x++ {
+ // er, eg and eb are the pixel's R,G,B values plus the
+ // optional Floyd-Steinberg error.
+ sr, sg, sb, _ := src.At(sp.X+x, sp.Y+y).RGBA()
+ er, eg, eb := int32(sr), int32(sg), int32(sb)
+ if floydSteinberg {
+ er = clamp(er + quantErrorCurr[x+1][0]/16)
+ eg = clamp(eg + quantErrorCurr[x+1][1]/16)
+ eb = clamp(eb + quantErrorCurr[x+1][2]/16)
+ }
+
+ if palette != nil {
+ // Find the closest palette color in Euclidean R,G,B space: the
+ // one that minimizes sum-squared-difference. We shift by 1 bit
+ // to avoid potential uint32 overflow in sum-squared-difference.
+ // TODO(nigeltao): consider smarter algorithms.
+ bestIndex, bestSSD := 0, uint32(1<<32-1)
+ for index, p := range palette {
+ delta := (er - p[0]) >> 1
+ ssd := uint32(delta * delta)
+ delta = (eg - p[1]) >> 1
+ ssd += uint32(delta * delta)
+ delta = (eb - p[2]) >> 1
+ ssd += uint32(delta * delta)
+ if ssd < bestSSD {
+ bestIndex, bestSSD = index, ssd
+ if ssd == 0 {
+ break
+ }
+ }
+ }
+ pix[y*stride+x] = byte(bestIndex)
+
+ if !floydSteinberg {
+ continue
+ }
+ er -= int32(palette[bestIndex][0])
+ eg -= int32(palette[bestIndex][1])
+ eb -= int32(palette[bestIndex][2])
+
+ } else {
+ out.R = uint16(er)
+ out.G = uint16(eg)
+ out.B = uint16(eb)
+ // The third argument is &out instead of out (and out is
+ // declared outside of the inner loop) to avoid the implicit
+ // conversion to color.Color here allocating memory in the
+ // inner loop if sizeof(color.RGBA64) > sizeof(uintptr).
+ dst.Set(r.Min.X+x, r.Min.Y+y, &out)
+
+ if !floydSteinberg {
+ continue
+ }
+ sr, sg, sb, _ = dst.At(r.Min.X+x, r.Min.Y+y).RGBA()
+ er -= int32(sr)
+ eg -= int32(sg)
+ eb -= int32(sb)
+ }
+
+ // Propagate the Floyd-Steinberg quantization error.
+ quantErrorNext[x+0][0] += er * 3
+ quantErrorNext[x+0][1] += eg * 3
+ quantErrorNext[x+0][2] += eb * 3
+ quantErrorNext[x+1][0] += er * 5
+ quantErrorNext[x+1][1] += eg * 5
+ quantErrorNext[x+1][2] += eb * 5
+ quantErrorNext[x+2][0] += er * 1
+ quantErrorNext[x+2][1] += eg * 1
+ quantErrorNext[x+2][2] += eb * 1
+ quantErrorCurr[x+2][0] += er * 7
+ quantErrorCurr[x+2][1] += eg * 7
+ quantErrorCurr[x+2][2] += eb * 7
+ }
+
+ // Recycle the quantization error buffers.
+ if floydSteinberg {
+ quantErrorCurr, quantErrorNext = quantErrorNext, quantErrorCurr
+ for i := range quantErrorNext {
+ quantErrorNext[i] = [3]int32{}
+ }
+ }
+ }
+}
diff --git a/src/pkg/image/draw/draw_test.go b/src/pkg/image/draw/draw_test.go
index 1db75b3e3..0dd7fbd47 100644
--- a/src/pkg/image/draw/draw_test.go
+++ b/src/pkg/image/draw/draw_test.go
@@ -7,6 +7,8 @@ package draw
import (
"image"
"image/color"
+ "image/png"
+ "os"
"testing"
)
@@ -352,3 +354,76 @@ func TestFill(t *testing.T) {
check("whole")
}
}
+
+// TestFloydSteinbergCheckerboard tests that the result of Floyd-Steinberg
+// error diffusion of a uniform 50% gray source image with a black-and-white
+// palette is a checkerboard pattern.
+func TestFloydSteinbergCheckerboard(t *testing.T) {
+ b := image.Rect(0, 0, 640, 480)
+ // We can't represent 50% exactly, but 0x7fff / 0xffff is close enough.
+ src := &image.Uniform{color.Gray16{0x7fff}}
+ dst := image.NewPaletted(b, color.Palette{color.Black, color.White})
+ FloydSteinberg.Draw(dst, b, src, image.Point{})
+ nErr := 0
+ for y := b.Min.Y; y < b.Max.Y; y++ {
+ for x := b.Min.X; x < b.Max.X; x++ {
+ got := dst.Pix[dst.PixOffset(x, y)]
+ want := uint8(x+y) % 2
+ if got != want {
+ t.Errorf("at (%d, %d): got %d, want %d", x, y, got, want)
+ if nErr++; nErr == 10 {
+ t.Fatal("there may be more errors")
+ }
+ }
+ }
+ }
+}
+
+// embeddedPaletted is an Image that behaves like an *image.Paletted but whose
+// type is not *image.Paletted.
+type embeddedPaletted struct {
+ *image.Paletted
+}
+
+// TestPaletted tests that the drawPaletted function behaves the same
+// regardless of whether dst is an *image.Paletted.
+func TestPaletted(t *testing.T) {
+ f, err := os.Open("../testdata/video-001.png")
+ if err != nil {
+ t.Fatalf("open: %v", err)
+ }
+ defer f.Close()
+ src, err := png.Decode(f)
+ if err != nil {
+ t.Fatalf("decode: %v", err)
+ }
+ b := src.Bounds()
+
+ cgaPalette := color.Palette{
+ color.RGBA{0x00, 0x00, 0x00, 0xff},
+ color.RGBA{0x55, 0xff, 0xff, 0xff},
+ color.RGBA{0xff, 0x55, 0xff, 0xff},
+ color.RGBA{0xff, 0xff, 0xff, 0xff},
+ }
+ drawers := map[string]Drawer{
+ "src": Src,
+ "floyd-steinberg": FloydSteinberg,
+ }
+
+loop:
+ for dName, d := range drawers {
+ dst0 := image.NewPaletted(b, cgaPalette)
+ dst1 := image.NewPaletted(b, cgaPalette)
+ d.Draw(dst0, b, src, image.Point{})
+ d.Draw(embeddedPaletted{dst1}, b, src, image.Point{})
+ for y := b.Min.Y; y < b.Max.Y; y++ {
+ for x := b.Min.X; x < b.Max.X; x++ {
+ if !eq(dst0.At(x, y), dst1.At(x, y)) {
+ t.Errorf("%s: at (%d, %d), %v versus %v",
+ dName, x, y, dst0.At(x, y), dst1.At(x, y))
+ continue loop
+ }
+ }
+ }
+ }
+}
diff --git a/src/pkg/image/format.go b/src/pkg/image/format.go
index 36635bcc5..3668de4e6 100644
--- a/src/pkg/image/format.go
+++ b/src/pkg/image/format.go
@@ -47,7 +47,7 @@ func asReader(r io.Reader) reader {
return bufio.NewReader(r)
}
-// Match returns whether magic matches b. Magic may contain "?" wildcards.
+// Match reports whether magic matches b. Magic may contain "?" wildcards.
func match(magic string, b []byte) bool {
if len(magic) != len(b) {
return false
@@ -73,7 +73,7 @@ func sniff(r reader) format {
// Decode decodes an image that has been encoded in a registered format.
// The string returned is the format name used during format registration.
-// Format registration is typically done by the init method of the codec-
+// Format registration is typically done by an init function in the codec-
// specific package.
func Decode(r io.Reader) (Image, string, error) {
rr := asReader(r)
@@ -88,7 +88,7 @@ func Decode(r io.Reader) (Image, string, error) {
// DecodeConfig decodes the color model and dimensions of an image that has
// been encoded in a registered format. The string returned is the format name
// used during format registration. Format registration is typically done by
-// the init method of the codec-specific package.
+// an init function in the codec-specific package.
func DecodeConfig(r io.Reader) (Config, string, error) {
rr := asReader(r)
f := sniff(rr)
diff --git a/src/pkg/image/geom.go b/src/pkg/image/geom.go
index e12348331..6ebaf67da 100644
--- a/src/pkg/image/geom.go
+++ b/src/pkg/image/geom.go
@@ -38,7 +38,7 @@ func (p Point) Div(k int) Point {
return Point{p.X / k, p.Y / k}
}
-// In returns whether p is in r.
+// In reports whether p is in r.
func (p Point) In(r Rectangle) bool {
return r.Min.X <= p.X && p.X < r.Max.X &&
r.Min.Y <= p.Y && p.Y < r.Max.Y
@@ -60,7 +60,7 @@ func (p Point) Mod(r Rectangle) Point {
return p.Add(r.Min)
}
-// Eq returns whether p and q are equal.
+// Eq reports whether p and q are equal.
func (p Point) Eq(q Point) bool {
return p.X == q.X && p.Y == q.Y
}
@@ -179,24 +179,24 @@ func (r Rectangle) Union(s Rectangle) Rectangle {
return r
}
-// Empty returns whether the rectangle contains no points.
+// Empty reports whether the rectangle contains no points.
func (r Rectangle) Empty() bool {
return r.Min.X >= r.Max.X || r.Min.Y >= r.Max.Y
}
-// Eq returns whether r and s are equal.
+// Eq reports whether r and s are equal.
func (r Rectangle) Eq(s Rectangle) bool {
return r.Min.X == s.Min.X && r.Min.Y == s.Min.Y &&
r.Max.X == s.Max.X && r.Max.Y == s.Max.Y
}
-// Overlaps returns whether r and s have a non-empty intersection.
+// Overlaps reports whether r and s have a non-empty intersection.
func (r Rectangle) Overlaps(s Rectangle) bool {
return r.Min.X < s.Max.X && s.Min.X < r.Max.X &&
r.Min.Y < s.Max.Y && s.Min.Y < r.Max.Y
}
-// In returns whether every point in r is in s.
+// In reports whether every point in r is in s.
func (r Rectangle) In(s Rectangle) bool {
if r.Empty() {
return true
diff --git a/src/pkg/image/gif/reader.go b/src/pkg/image/gif/reader.go
index 8e8531f9b..8b0298a29 100644
--- a/src/pkg/image/gif/reader.go
+++ b/src/pkg/image/gif/reader.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.
-// Package gif implements a GIF image decoder.
+// Package gif implements a GIF image decoder and encoder.
//
// The GIF specification is at http://www.w3.org/Graphics/GIF/spec-gif89a.txt.
package gif
@@ -20,6 +20,7 @@ import (
var (
errNotEnough = errors.New("gif: not enough image data")
errTooMuch = errors.New("gif: too much image data")
+ errBadPixel = errors.New("gif: invalid pixel value")
)
// If the io.Reader does not also have ReadByte, then decode will introduce its own buffering.
@@ -189,6 +190,7 @@ func (d *decoder) decode(r io.Reader, configOnly bool) error {
// A wonderfully Go-like piece of magic.
br := &blockReader{r: d.r}
lzwr := lzw.NewReader(br, lzw.LSB, int(litWidth))
+ defer lzwr.Close()
if _, err = io.ReadFull(lzwr, m.Pix); err != nil {
if err != io.ErrUnexpectedEOF {
return err
@@ -210,6 +212,15 @@ func (d *decoder) decode(r io.Reader, configOnly bool) error {
return errTooMuch
}
+ // Check that the color indexes are inside the palette.
+ if len(m.Palette) < 256 {
+ for _, pixel := range m.Pix {
+ if int(pixel) >= len(m.Palette) {
+ return errBadPixel
+ }
+ }
+ }
+
// Undo the interlacing if necessary.
if d.imageFields&ifInterlace != 0 {
uninterlace(m)
diff --git a/src/pkg/image/gif/reader_test.go b/src/pkg/image/gif/reader_test.go
index dcc6c6dd3..09867132d 100644
--- a/src/pkg/image/gif/reader_test.go
+++ b/src/pkg/image/gif/reader_test.go
@@ -9,16 +9,16 @@ import (
"testing"
)
-func TestDecode(t *testing.T) {
- // header and trailer are parts of a valid 2x1 GIF image.
- const (
- header = "GIF89a" +
- "\x02\x00\x01\x00" + // width=2, height=1
- "\x80\x00\x00" + // headerFields=(a color map of 2 pixels), backgroundIndex, aspect
- "\x10\x20\x30\x40\x50\x60" // the color map, also known as a palette
- trailer = "\x3b"
- )
+// header, palette and trailer are parts of a valid 2x1 GIF image.
+const (
+ headerStr = "GIF89a" +
+ "\x02\x00\x01\x00" + // width=2, height=1
+ "\x80\x00\x00" // headerFields=(a color map of 2 pixels), backgroundIndex, aspect
+ paletteStr = "\x10\x20\x30\x40\x50\x60" // the color map, also known as a palette
+ trailerStr = "\x3b"
+)
+func TestDecode(t *testing.T) {
// lzwEncode returns an LZW encoding (with 2-bit literals) of n zeroes.
lzwEncode := func(n int) []byte {
b := &bytes.Buffer{}
@@ -41,7 +41,8 @@ func TestDecode(t *testing.T) {
}
for _, tc := range testCases {
b := &bytes.Buffer{}
- b.WriteString(header)
+ b.WriteString(headerStr)
+ b.WriteString(paletteStr)
// Write an image with bounds 2x1 but tc.nPix pixels. If tc.nPix != 2
// then this should result in an invalid GIF image. First, write a
// magic 0x2c (image descriptor) byte, bounds=(0,0)-(2,1), a flags
@@ -60,7 +61,7 @@ func TestDecode(t *testing.T) {
b.WriteString("\x01\x02") // A 1-byte payload with an 0x02 byte.
}
b.WriteByte(0x00) // An empty block signifies the end of the image data.
- b.WriteString(trailer)
+ b.WriteString(trailerStr)
got, err := Decode(b)
if err != tc.wantErr {
@@ -114,7 +115,7 @@ func try(t *testing.T, b []byte, want string) {
}
func TestBounds(t *testing.T) {
- // make a local copy of testGIF
+ // Make a local copy of testGIF.
gif := make([]byte, len(testGIF))
copy(gif, testGIF)
// Make the bounds too big, just by one.
@@ -136,3 +137,61 @@ func TestBounds(t *testing.T) {
}
try(t, gif, want)
}
+
+func TestNoPalette(t *testing.T) {
+ b := &bytes.Buffer{}
+
+ // Manufacture a GIF with no palette, so any pixel at all
+ // will be invalid.
+ b.WriteString(headerStr[:len(headerStr)-3])
+ b.WriteString("\x00\x00\x00") // No global palette.
+
+ // Image descriptor: 2x1, no local palette.
+ b.WriteString("\x2c\x00\x00\x00\x00\x02\x00\x01\x00\x00\x02")
+
+ // Encode the pixels: neither is in range, because there is no palette.
+ pix := []byte{0, 128}
+ enc := &bytes.Buffer{}
+ w := lzw.NewWriter(enc, lzw.LSB, 2)
+ w.Write(pix)
+ w.Close()
+ b.WriteByte(byte(len(enc.Bytes())))
+ b.Write(enc.Bytes())
+ b.WriteByte(0x00) // An empty block signifies the end of the image data.
+
+ b.WriteString(trailerStr)
+
+ try(t, b.Bytes(), "gif: invalid pixel value")
+}
+
+func TestPixelOutsidePaletteRange(t *testing.T) {
+ for _, pval := range []byte{0, 1, 2, 3, 255} {
+ b := &bytes.Buffer{}
+
+ // Manufacture a GIF with a 2 color palette.
+ b.WriteString(headerStr)
+ b.WriteString(paletteStr)
+
+ // Image descriptor: 2x1, no local palette.
+ b.WriteString("\x2c\x00\x00\x00\x00\x02\x00\x01\x00\x00\x02")
+
+ // Encode the pixels; some pvals trigger the expected error.
+ pix := []byte{pval, pval}
+ enc := &bytes.Buffer{}
+ w := lzw.NewWriter(enc, lzw.LSB, 2)
+ w.Write(pix)
+ w.Close()
+ b.WriteByte(byte(len(enc.Bytes())))
+ b.Write(enc.Bytes())
+ b.WriteByte(0x00) // An empty block signifies the end of the image data.
+
+ b.WriteString(trailerStr)
+
+ // No error expected, unless the pixels are beyond the 2 color palette.
+ want := ""
+ if pval >= 2 {
+ want = "gif: invalid pixel value"
+ }
+ try(t, b.Bytes(), want)
+ }
+}
diff --git a/src/pkg/image/gif/writer.go b/src/pkg/image/gif/writer.go
new file mode 100644
index 000000000..15cd40fad
--- /dev/null
+++ b/src/pkg/image/gif/writer.go
@@ -0,0 +1,323 @@
+// Copyright 2013 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 gif
+
+import (
+ "bufio"
+ "compress/lzw"
+ "errors"
+ "image"
+ "image/color"
+ "image/color/palette"
+ "image/draw"
+ "io"
+)
+
+// Graphic control extension fields.
+const (
+ gcLabel = 0xF9
+ gcBlockSize = 0x04
+)
+
+var log2Lookup = [8]int{2, 4, 8, 16, 32, 64, 128, 256}
+
+func log2(x int) int {
+ for i, v := range log2Lookup {
+ if x <= v {
+ return i
+ }
+ }
+ return -1
+}
+
+// Little-endian.
+func writeUint16(b []uint8, u uint16) {
+ b[0] = uint8(u)
+ b[1] = uint8(u >> 8)
+}
+
+// writer is a buffered writer.
+type writer interface {
+ Flush() error
+ io.Writer
+ io.ByteWriter
+}
+
+// encoder encodes an image to the GIF format.
+type encoder struct {
+ // w is the writer to write to. err is the first error encountered during
+ // writing. All attempted writes after the first error become no-ops.
+ w writer
+ err error
+ // g is a reference to the data that is being encoded.
+ g *GIF
+ // buf is a scratch buffer. It must be at least 768 so we can write the color map.
+ buf [1024]byte
+}
+
+// blockWriter writes the block structure of GIF image data, which
+// comprises (n, (n bytes)) blocks, with 1 <= n <= 255. It is the
+// writer given to the LZW encoder, which is thus immune to the
+// blocking.
+type blockWriter struct {
+ e *encoder
+}
+
+func (b blockWriter) Write(data []byte) (int, error) {
+ if b.e.err != nil {
+ return 0, b.e.err
+ }
+ if len(data) == 0 {
+ return 0, nil
+ }
+ total := 0
+ for total < len(data) {
+ n := copy(b.e.buf[1:256], data[total:])
+ total += n
+ b.e.buf[0] = uint8(n)
+
+ n, b.e.err = b.e.w.Write(b.e.buf[:n+1])
+ if b.e.err != nil {
+ return 0, b.e.err
+ }
+ }
+ return total, b.e.err
+}
+
+func (e *encoder) flush() {
+ if e.err != nil {
+ return
+ }
+ e.err = e.w.Flush()
+}
+
+func (e *encoder) write(p []byte) {
+ if e.err != nil {
+ return
+ }
+ _, e.err = e.w.Write(p)
+}
+
+func (e *encoder) writeByte(b byte) {
+ if e.err != nil {
+ return
+ }
+ e.err = e.w.WriteByte(b)
+}
+
+func (e *encoder) writeHeader() {
+ if e.err != nil {
+ return
+ }
+ _, e.err = io.WriteString(e.w, "GIF89a")
+ if e.err != nil {
+ return
+ }
+
+ pm := e.g.Image[0]
+ // Logical screen width and height.
+ writeUint16(e.buf[0:2], uint16(pm.Bounds().Dx()))
+ writeUint16(e.buf[2:4], uint16(pm.Bounds().Dy()))
+ e.write(e.buf[:4])
+
+ // All frames have a local color table, so a global color table
+ // is not needed.
+ e.buf[0] = 0x00
+ e.buf[1] = 0x00 // Background Color Index.
+ e.buf[2] = 0x00 // Pixel Aspect Ratio.
+ e.write(e.buf[:3])
+
+ // Add animation info if necessary.
+ if len(e.g.Image) > 1 {
+ e.buf[0] = 0x21 // Extension Introducer.
+ e.buf[1] = 0xff // Application Label.
+ e.buf[2] = 0x0b // Block Size.
+ e.write(e.buf[:3])
+ _, e.err = io.WriteString(e.w, "NETSCAPE2.0") // Application Identifier.
+ if e.err != nil {
+ return
+ }
+ e.buf[0] = 0x03 // Block Size.
+ e.buf[1] = 0x01 // Sub-block Index.
+ writeUint16(e.buf[2:4], uint16(e.g.LoopCount))
+ e.buf[4] = 0x00 // Block Terminator.
+ e.write(e.buf[:5])
+ }
+}
+
+func (e *encoder) writeColorTable(p color.Palette, size int) {
+ if e.err != nil {
+ return
+ }
+
+ for i := 0; i < log2Lookup[size]; i++ {
+ if i < len(p) {
+ r, g, b, _ := p[i].RGBA()
+ e.buf[3*i+0] = uint8(r >> 8)
+ e.buf[3*i+1] = uint8(g >> 8)
+ e.buf[3*i+2] = uint8(b >> 8)
+ } else {
+ // Pad with black.
+ e.buf[3*i+0] = 0x00
+ e.buf[3*i+1] = 0x00
+ e.buf[3*i+2] = 0x00
+ }
+ }
+ e.write(e.buf[:3*log2Lookup[size]])
+}
+
+func (e *encoder) writeImageBlock(pm *image.Paletted, delay int) {
+ if e.err != nil {
+ return
+ }
+
+ if len(pm.Palette) == 0 {
+ e.err = errors.New("gif: cannot encode image block with empty palette")
+ return
+ }
+
+ b := pm.Bounds()
+ if b.Dx() >= 1<<16 || b.Dy() >= 1<<16 || b.Min.X < 0 || b.Min.X >= 1<<16 || b.Min.Y < 0 || b.Min.Y >= 1<<16 {
+ e.err = errors.New("gif: image block is too large to encode")
+ return
+ }
+
+ transparentIndex := -1
+ for i, c := range pm.Palette {
+ if _, _, _, a := c.RGBA(); a == 0 {
+ transparentIndex = i
+ break
+ }
+ }
+
+ if delay > 0 || transparentIndex != -1 {
+ e.buf[0] = sExtension // Extension Introducer.
+ e.buf[1] = gcLabel // Graphic Control Label.
+ e.buf[2] = gcBlockSize // Block Size.
+ if transparentIndex != -1 {
+ e.buf[3] = 0x01
+ } else {
+ e.buf[3] = 0x00
+ }
+ writeUint16(e.buf[4:6], uint16(delay)) // Delay Time (1/100ths of a second)
+
+ // Transparent color index.
+ if transparentIndex != -1 {
+ e.buf[6] = uint8(transparentIndex)
+ } else {
+ e.buf[6] = 0x00
+ }
+ e.buf[7] = 0x00 // Block Terminator.
+ e.write(e.buf[:8])
+ }
+ e.buf[0] = sImageDescriptor
+ writeUint16(e.buf[1:3], uint16(b.Min.X))
+ writeUint16(e.buf[3:5], uint16(b.Min.Y))
+ writeUint16(e.buf[5:7], uint16(b.Dx()))
+ writeUint16(e.buf[7:9], uint16(b.Dy()))
+ e.write(e.buf[:9])
+
+ paddedSize := log2(len(pm.Palette)) // Size of Local Color Table: 2^(1+n).
+ // Interlacing is not supported.
+ e.writeByte(0x80 | uint8(paddedSize))
+
+ // Local Color Table.
+ e.writeColorTable(pm.Palette, paddedSize)
+
+ litWidth := paddedSize + 1
+ if litWidth < 2 {
+ litWidth = 2
+ }
+ e.writeByte(uint8(litWidth)) // LZW Minimum Code Size.
+
+ lzww := lzw.NewWriter(blockWriter{e: e}, lzw.LSB, litWidth)
+ _, e.err = lzww.Write(pm.Pix)
+ if e.err != nil {
+ lzww.Close()
+ return
+ }
+ lzww.Close()
+ e.writeByte(0x00) // Block Terminator.
+}
+
+// Options are the encoding parameters.
+type Options struct {
+ // NumColors is the maximum number of colors used in the image.
+ // It ranges from 1 to 256.
+ NumColors int
+
+ // Quantizer is used to produce a palette with size NumColors.
+ // palette.Plan9 is used in place of a nil Quantizer.
+ Quantizer draw.Quantizer
+
+ // Drawer is used to convert the source image to the desired palette.
+ // draw.FloydSteinberg is used in place of a nil Drawer.
+ Drawer draw.Drawer
+}
+
+// EncodeAll writes the images in g to w in GIF format with the
+// given loop count and delay between frames.
+func EncodeAll(w io.Writer, g *GIF) error {
+ if len(g.Image) == 0 {
+ return errors.New("gif: must provide at least one image")
+ }
+
+ if len(g.Image) != len(g.Delay) {
+ return errors.New("gif: mismatched image and delay lengths")
+ }
+ if g.LoopCount < 0 {
+ g.LoopCount = 0
+ }
+
+ e := encoder{g: g}
+ if ww, ok := w.(writer); ok {
+ e.w = ww
+ } else {
+ e.w = bufio.NewWriter(w)
+ }
+
+ e.writeHeader()
+ for i, pm := range g.Image {
+ e.writeImageBlock(pm, g.Delay[i])
+ }
+ e.writeByte(sTrailer)
+ e.flush()
+ return e.err
+}
+
+// Encode writes the Image m to w in GIF format.
+func Encode(w io.Writer, m image.Image, o *Options) error {
+ // Check for bounds and size restrictions.
+ b := m.Bounds()
+ if b.Dx() >= 1<<16 || b.Dy() >= 1<<16 {
+ return errors.New("gif: image is too large to encode")
+ }
+
+ opts := Options{}
+ if o != nil {
+ opts = *o
+ }
+ if opts.NumColors < 1 || 256 < opts.NumColors {
+ opts.NumColors = 256
+ }
+ if opts.Drawer == nil {
+ opts.Drawer = draw.FloydSteinberg
+ }
+
+ pm, ok := m.(*image.Paletted)
+ if !ok || len(pm.Palette) > opts.NumColors {
+ // TODO: Pick a better sub-sample of the Plan 9 palette.
+ pm = image.NewPaletted(b, palette.Plan9[:opts.NumColors])
+ if opts.Quantizer != nil {
+ pm.Palette = opts.Quantizer.Quantize(make(color.Palette, 0, opts.NumColors), m)
+ }
+ opts.Drawer.Draw(pm, b, m, image.ZP)
+ }
+
+ return EncodeAll(w, &GIF{
+ Image: []*image.Paletted{pm},
+ Delay: []int{0},
+ })
+}
diff --git a/src/pkg/image/gif/writer_test.go b/src/pkg/image/gif/writer_test.go
new file mode 100644
index 000000000..c1ada769c
--- /dev/null
+++ b/src/pkg/image/gif/writer_test.go
@@ -0,0 +1,204 @@
+// Copyright 2013 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 gif
+
+import (
+ "bytes"
+ "image"
+ "image/color"
+ _ "image/png"
+ "io/ioutil"
+ "math/rand"
+ "os"
+ "testing"
+)
+
+func readImg(filename string) (image.Image, error) {
+ f, err := os.Open(filename)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+ m, _, err := image.Decode(f)
+ return m, err
+}
+
+func readGIF(filename string) (*GIF, error) {
+ f, err := os.Open(filename)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+ return DecodeAll(f)
+}
+
+func delta(u0, u1 uint32) int64 {
+ d := int64(u0) - int64(u1)
+ if d < 0 {
+ return -d
+ }
+ return d
+}
+
+// averageDelta returns the average delta in RGB space. The two images must
+// have the same bounds.
+func averageDelta(m0, m1 image.Image) int64 {
+ b := m0.Bounds()
+ var sum, n int64
+ for y := b.Min.Y; y < b.Max.Y; y++ {
+ for x := b.Min.X; x < b.Max.X; x++ {
+ c0 := m0.At(x, y)
+ c1 := m1.At(x, y)
+ r0, g0, b0, _ := c0.RGBA()
+ r1, g1, b1, _ := c1.RGBA()
+ sum += delta(r0, r1)
+ sum += delta(g0, g1)
+ sum += delta(b0, b1)
+ n += 3
+ }
+ }
+ return sum / n
+}
+
+var testCase = []struct {
+ filename string
+ tolerance int64
+}{
+ {"../testdata/video-001.png", 1 << 12},
+ {"../testdata/video-001.gif", 0},
+ {"../testdata/video-001.interlaced.gif", 0},
+}
+
+func TestWriter(t *testing.T) {
+ for _, tc := range testCase {
+ m0, err := readImg(tc.filename)
+ if err != nil {
+ t.Error(tc.filename, err)
+ continue
+ }
+ var buf bytes.Buffer
+ err = Encode(&buf, m0, nil)
+ if err != nil {
+ t.Error(tc.filename, err)
+ continue
+ }
+ m1, err := Decode(&buf)
+ if err != nil {
+ t.Error(tc.filename, err)
+ continue
+ }
+ if m0.Bounds() != m1.Bounds() {
+ t.Errorf("%s, bounds differ: %v and %v", tc.filename, m0.Bounds(), m1.Bounds())
+ continue
+ }
+ // Compare the average delta to the tolerance level.
+ avgDelta := averageDelta(m0, m1)
+ if avgDelta > tc.tolerance {
+ t.Errorf("%s: average delta is too high. expected: %d, got %d", tc.filename, tc.tolerance, avgDelta)
+ continue
+ }
+ }
+}
+
+var frames = []string{
+ "../testdata/video-001.gif",
+ "../testdata/video-005.gray.gif",
+}
+
+func TestEncodeAll(t *testing.T) {
+ g0 := &GIF{
+ Image: make([]*image.Paletted, len(frames)),
+ Delay: make([]int, len(frames)),
+ LoopCount: 5,
+ }
+ for i, f := range frames {
+ m, err := readGIF(f)
+ if err != nil {
+ t.Error(f, err)
+ }
+ g0.Image[i] = m.Image[0]
+ }
+ var buf bytes.Buffer
+ if err := EncodeAll(&buf, g0); err != nil {
+ t.Fatal("EncodeAll:", err)
+ }
+ g1, err := DecodeAll(&buf)
+ if err != nil {
+ t.Fatal("DecodeAll:", err)
+ }
+ if g0.LoopCount != g1.LoopCount {
+ t.Errorf("loop counts differ: %d and %d", g0.LoopCount, g1.LoopCount)
+ }
+ for i := range g0.Image {
+ m0, m1 := g0.Image[i], g1.Image[i]
+ if m0.Bounds() != m1.Bounds() {
+ t.Errorf("%s, bounds differ: %v and %v", frames[i], m0.Bounds(), m1.Bounds())
+ }
+ d0, d1 := g0.Delay[i], g1.Delay[i]
+ if d0 != d1 {
+ t.Errorf("%s: delay values differ: %d and %d", frames[i], d0, d1)
+ }
+ }
+
+ g1.Delay = make([]int, 1)
+ if err := EncodeAll(ioutil.Discard, g1); err == nil {
+ t.Error("expected error from mismatched delay and image slice lengths")
+ }
+ if err := EncodeAll(ioutil.Discard, &GIF{}); err == nil {
+ t.Error("expected error from providing empty gif")
+ }
+}
+
+func BenchmarkEncode(b *testing.B) {
+ b.StopTimer()
+
+ bo := image.Rect(0, 0, 640, 480)
+ rnd := rand.New(rand.NewSource(123))
+
+ // Restrict to a 256-color paletted image to avoid quantization path.
+ palette := make(color.Palette, 256)
+ for i := range palette {
+ palette[i] = color.RGBA{
+ uint8(rnd.Intn(256)),
+ uint8(rnd.Intn(256)),
+ uint8(rnd.Intn(256)),
+ 255,
+ }
+ }
+ img := image.NewPaletted(image.Rect(0, 0, 640, 480), palette)
+ for y := bo.Min.Y; y < bo.Max.Y; y++ {
+ for x := bo.Min.X; x < bo.Max.X; x++ {
+ img.Set(x, y, palette[rnd.Intn(256)])
+ }
+ }
+
+ b.SetBytes(640 * 480 * 4)
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ Encode(ioutil.Discard, img, nil)
+ }
+}
+
+func BenchmarkQuantizedEncode(b *testing.B) {
+ b.StopTimer()
+ img := image.NewRGBA(image.Rect(0, 0, 640, 480))
+ bo := img.Bounds()
+ rnd := rand.New(rand.NewSource(123))
+ for y := bo.Min.Y; y < bo.Max.Y; y++ {
+ for x := bo.Min.X; x < bo.Max.X; x++ {
+ img.SetRGBA(x, y, color.RGBA{
+ uint8(rnd.Intn(256)),
+ uint8(rnd.Intn(256)),
+ uint8(rnd.Intn(256)),
+ 255,
+ })
+ }
+ }
+ b.SetBytes(640 * 480 * 4)
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ Encode(ioutil.Discard, img, nil)
+ }
+}
diff --git a/src/pkg/image/image.go b/src/pkg/image/image.go
index 03ac60606..32a89ef34 100644
--- a/src/pkg/image/image.go
+++ b/src/pkg/image/image.go
@@ -126,7 +126,7 @@ func (p *RGBA) SubImage(r Rectangle) Image {
}
}
-// Opaque scans the entire image and returns whether or not it is fully opaque.
+// Opaque scans the entire image and reports whether it is fully opaque.
func (p *RGBA) Opaque() bool {
if p.Rect.Empty() {
return true
@@ -234,7 +234,7 @@ func (p *RGBA64) SubImage(r Rectangle) Image {
}
}
-// Opaque scans the entire image and returns whether or not it is fully opaque.
+// Opaque scans the entire image and reports whether it is fully opaque.
func (p *RGBA64) Opaque() bool {
if p.Rect.Empty() {
return true
@@ -329,7 +329,7 @@ func (p *NRGBA) SubImage(r Rectangle) Image {
}
}
-// Opaque scans the entire image and returns whether or not it is fully opaque.
+// Opaque scans the entire image and reports whether it is fully opaque.
func (p *NRGBA) Opaque() bool {
if p.Rect.Empty() {
return true
@@ -437,7 +437,7 @@ func (p *NRGBA64) SubImage(r Rectangle) Image {
}
}
-// Opaque scans the entire image and returns whether or not it is fully opaque.
+// Opaque scans the entire image and reports whether it is fully opaque.
func (p *NRGBA64) Opaque() bool {
if p.Rect.Empty() {
return true
@@ -525,7 +525,7 @@ func (p *Alpha) SubImage(r Rectangle) Image {
}
}
-// Opaque scans the entire image and returns whether or not it is fully opaque.
+// Opaque scans the entire image and reports whether it is fully opaque.
func (p *Alpha) Opaque() bool {
if p.Rect.Empty() {
return true
@@ -616,7 +616,7 @@ func (p *Alpha16) SubImage(r Rectangle) Image {
}
}
-// Opaque scans the entire image and returns whether or not it is fully opaque.
+// Opaque scans the entire image and reports whether it is fully opaque.
func (p *Alpha16) Opaque() bool {
if p.Rect.Empty() {
return true
@@ -704,7 +704,7 @@ func (p *Gray) SubImage(r Rectangle) Image {
}
}
-// Opaque scans the entire image and returns whether or not it is fully opaque.
+// Opaque scans the entire image and reports whether it is fully opaque.
func (p *Gray) Opaque() bool {
return true
}
@@ -782,7 +782,7 @@ func (p *Gray16) SubImage(r Rectangle) Image {
}
}
-// Opaque scans the entire image and returns whether or not it is fully opaque.
+// Opaque scans the entire image and reports whether it is fully opaque.
func (p *Gray16) Opaque() bool {
return true
}
@@ -873,7 +873,7 @@ func (p *Paletted) SubImage(r Rectangle) Image {
}
}
-// Opaque scans the entire image and returns whether or not it is fully opaque.
+// Opaque scans the entire image and reports whether it is fully opaque.
func (p *Paletted) Opaque() bool {
var present [256]bool
i0, i1 := 0, p.Rect.Dx()
diff --git a/src/pkg/image/jpeg/dct_test.go b/src/pkg/image/jpeg/dct_test.go
index 7389f7e4f..845e75887 100644
--- a/src/pkg/image/jpeg/dct_test.go
+++ b/src/pkg/image/jpeg/dct_test.go
@@ -90,7 +90,7 @@ func TestDCT(t *testing.T) {
}
}
-// differ returns whether any pair-wise elements in b0 and b1 differ by 2 or
+// differ reports whether any pair-wise elements in b0 and b1 differ by 2 or
// more. That tolerance is because there isn't a single definitive decoding of
// a given JPEG image, even before the YCbCr to RGB conversion; implementations
// can have different IDCT rounding errors.
diff --git a/src/pkg/image/jpeg/reader.go b/src/pkg/image/jpeg/reader.go
index 862d8dc1b..356d56220 100644
--- a/src/pkg/image/jpeg/reader.go
+++ b/src/pkg/image/jpeg/reader.go
@@ -174,10 +174,10 @@ func (d *decoder) processSOF(n int) error {
// values for the Cr and Cb components must be (1, 1).
if i == 0 {
if hv != 0x11 && hv != 0x21 && hv != 0x22 && hv != 0x12 {
- return UnsupportedError("luma downsample ratio")
+ return UnsupportedError("luma/chroma downsample ratio")
}
} else if hv != 0x11 {
- return UnsupportedError("chroma downsample ratio")
+ return UnsupportedError("luma/chroma downsample ratio")
}
}
return nil
diff --git a/src/pkg/image/names.go b/src/pkg/image/names.go
index 04ee2cfb4..8985f4921 100644
--- a/src/pkg/image/names.go
+++ b/src/pkg/image/names.go
@@ -41,7 +41,7 @@ func (c *Uniform) Bounds() Rectangle { return Rectangle{Point{-1e9, -1e9}, Point
func (c *Uniform) At(x, y int) color.Color { return c.C }
-// Opaque scans the entire image and returns whether or not it is fully opaque.
+// Opaque scans the entire image and reports whether it is fully opaque.
func (c *Uniform) Opaque() bool {
_, _, _, a := c.C.RGBA()
return a == 0xffff
diff --git a/src/pkg/image/testdata/video-005.gray.gif b/src/pkg/image/testdata/video-005.gray.gif
new file mode 100644
index 000000000..23350d6dc
--- /dev/null
+++ b/src/pkg/image/testdata/video-005.gray.gif
Binary files differ
diff --git a/src/pkg/io/io.go b/src/pkg/io/io.go
index ec2cd6056..f7073ffc0 100644
--- a/src/pkg/io/io.go
+++ b/src/pkg/io/io.go
@@ -91,10 +91,14 @@ type Closer interface {
// Seek sets the offset for the next Read or Write to offset,
// interpreted according to whence: 0 means relative to the origin of
// the file, 1 means relative to the current offset, and 2 means
-// relative to the end. Seek returns the new offset and an Error, if
+// relative to the end. Seek returns the new offset and an error, if
// any.
+//
+// Seeking to a negative offset is an error. Seeking to any positive
+// offset is legal, but the behavior of subsequent I/O operations on
+// the underlying object is implementation-dependent.
type Seeker interface {
- Seek(offset int64, whence int) (ret int64, err error)
+ Seek(offset int64, whence int) (int64, error)
}
// ReadWriter is the interface that groups the basic Read and Write methods.
@@ -329,20 +333,20 @@ func CopyN(dst Writer, src Reader, n int64) (written int64, err error) {
// Because Copy is defined to read from src until EOF, it does
// not treat an EOF from Read as an error to be reported.
//
-// If dst implements the ReaderFrom interface,
-// the copy is implemented by calling dst.ReadFrom(src).
-// Otherwise, if src implements the WriterTo interface,
+// If src implements the WriterTo interface,
// the copy is implemented by calling src.WriteTo(dst).
+// Otherwise, if dst implements the ReaderFrom interface,
+// the copy is implemented by calling dst.ReadFrom(src).
func Copy(dst Writer, src Reader) (written int64, err error) {
- // If the writer has a ReadFrom method, use it to do the copy.
+ // If the reader has a WriteTo method, use it to do the copy.
// Avoids an allocation and a copy.
- if rt, ok := dst.(ReaderFrom); ok {
- return rt.ReadFrom(src)
- }
- // Similarly, if the reader has a WriteTo method, use it to do the copy.
if wt, ok := src.(WriterTo); ok {
return wt.WriteTo(dst)
}
+ // Similarly, if the writer has a ReadFrom method, use it to do the copy.
+ if rt, ok := dst.(ReaderFrom); ok {
+ return rt.ReadFrom(src)
+ }
buf := make([]byte, 32*1024)
for {
nr, er := src.Read(buf)
@@ -426,7 +430,7 @@ func (s *SectionReader) Read(p []byte) (n int, err error) {
var errWhence = errors.New("Seek: invalid whence")
var errOffset = errors.New("Seek: invalid offset")
-func (s *SectionReader) Seek(offset int64, whence int) (ret int64, err error) {
+func (s *SectionReader) Seek(offset int64, whence int) (int64, error) {
switch whence {
default:
return 0, errWhence
@@ -437,7 +441,7 @@ func (s *SectionReader) Seek(offset int64, whence int) (ret int64, err error) {
case 2:
offset += s.limit
}
- if offset < s.base || offset > s.limit {
+ if offset < s.base {
return 0, errOffset
}
s.off = offset
diff --git a/src/pkg/io/io_test.go b/src/pkg/io/io_test.go
index 1bc451e44..bd7a82f17 100644
--- a/src/pkg/io/io_test.go
+++ b/src/pkg/io/io_test.go
@@ -52,6 +52,32 @@ func TestCopyWriteTo(t *testing.T) {
}
}
+// Version of bytes.Buffer that checks whether WriteTo was called or not
+type writeToChecker struct {
+ bytes.Buffer
+ writeToCalled bool
+}
+
+func (wt *writeToChecker) WriteTo(w Writer) (int64, error) {
+ wt.writeToCalled = true
+ return wt.Buffer.WriteTo(w)
+}
+
+// It's preferable to choose WriterTo over ReaderFrom, since a WriterTo can issue one large write,
+// while the ReaderFrom must read until EOF, potentially allocating when running out of buffer.
+// Make sure that we choose WriterTo when both are implemented.
+func TestCopyPriority(t *testing.T) {
+ rb := new(writeToChecker)
+ wb := new(bytes.Buffer)
+ rb.WriteString("hello, world.")
+ Copy(wb, rb)
+ if wb.String() != "hello, world." {
+ t.Errorf("Copy did not work properly")
+ } else if !rb.writeToCalled {
+ t.Errorf("WriteTo was not prioritized over ReadFrom")
+ }
+}
+
func TestCopyN(t *testing.T) {
rb := new(Buffer)
wb := new(Buffer)
@@ -234,7 +260,7 @@ func TestTeeReader(t *testing.T) {
}
}
-func TestSectionReader_ReadAt(tst *testing.T) {
+func TestSectionReader_ReadAt(t *testing.T) {
dat := "a long sample data, 1234567890"
tests := []struct {
data string
@@ -256,12 +282,40 @@ func TestSectionReader_ReadAt(tst *testing.T) {
{data: dat, off: 3, n: len(dat) / 2, bufLen: len(dat)/2 - 2, at: 2, exp: dat[5 : 5+len(dat)/2-2], err: nil},
{data: dat, off: 3, n: len(dat) / 2, bufLen: len(dat)/2 + 2, at: 2, exp: dat[5 : 5+len(dat)/2-2], err: EOF},
}
- for i, t := range tests {
- r := strings.NewReader(t.data)
- s := NewSectionReader(r, int64(t.off), int64(t.n))
- buf := make([]byte, t.bufLen)
- if n, err := s.ReadAt(buf, int64(t.at)); n != len(t.exp) || string(buf[:n]) != t.exp || err != t.err {
- tst.Fatalf("%d: ReadAt(%d) = %q, %v; expected %q, %v", i, t.at, buf[:n], err, t.exp, t.err)
+ for i, tt := range tests {
+ r := strings.NewReader(tt.data)
+ s := NewSectionReader(r, int64(tt.off), int64(tt.n))
+ buf := make([]byte, tt.bufLen)
+ if n, err := s.ReadAt(buf, int64(tt.at)); n != len(tt.exp) || string(buf[:n]) != tt.exp || err != tt.err {
+ t.Fatalf("%d: ReadAt(%d) = %q, %v; expected %q, %v", i, tt.at, buf[:n], err, tt.exp, tt.err)
}
}
}
+
+func TestSectionReader_Seek(t *testing.T) {
+ // Verifies that NewSectionReader's Seeker behaves like bytes.NewReader (which is like strings.NewReader)
+ br := bytes.NewReader([]byte("foo"))
+ sr := NewSectionReader(br, 0, int64(len("foo")))
+
+ for whence := 0; whence <= 2; whence++ {
+ for offset := int64(-3); offset <= 4; offset++ {
+ brOff, brErr := br.Seek(offset, whence)
+ srOff, srErr := sr.Seek(offset, whence)
+ if (brErr != nil) != (srErr != nil) || brOff != srOff {
+ t.Errorf("For whence %d, offset %d: bytes.Reader.Seek = (%v, %v) != SectionReader.Seek = (%v, %v)",
+ whence, offset, brOff, brErr, srErr, srOff)
+ }
+ }
+ }
+
+ // And verify we can just seek past the end and get an EOF
+ got, err := sr.Seek(100, 0)
+ if err != nil || got != 100 {
+ t.Errorf("Seek = %v, %v; want 100, nil", got, err)
+ }
+
+ n, err := sr.Read(make([]byte, 10))
+ if n != 0 || err != EOF {
+ t.Errorf("Read = %v, %v; want 0, EOF", n, err)
+ }
+}
diff --git a/src/pkg/io/ioutil/ioutil.go b/src/pkg/io/ioutil/ioutil.go
index 6b395c69b..b2508b789 100644
--- a/src/pkg/io/ioutil/ioutil.go
+++ b/src/pkg/io/ioutil/ioutil.go
@@ -78,10 +78,12 @@ func WriteFile(filename string, data []byte, perm os.FileMode) error {
return err
}
n, err := f.Write(data)
- f.Close()
if err == nil && n < len(data) {
err = io.ErrShortWrite
}
+ if err1 := f.Close(); err == nil {
+ err = err1
+ }
return err
}
@@ -130,6 +132,10 @@ func (devNull) Write(p []byte) (int, error) {
return len(p), nil
}
+func (devNull) WriteString(s string) (int, error) {
+ return len(s), nil
+}
+
func (devNull) ReadFrom(r io.Reader) (n int64, err error) {
buf := blackHole()
defer blackHolePut(buf)
diff --git a/src/pkg/io/pipe.go b/src/pkg/io/pipe.go
index f3f0f1757..f65354a7f 100644
--- a/src/pkg/io/pipe.go
+++ b/src/pkg/io/pipe.go
@@ -74,6 +74,10 @@ func (p *pipe) write(b []byte) (n int, err error) {
p.l.Lock()
defer p.l.Unlock()
+ if p.werr != nil {
+ err = ErrClosedPipe
+ return
+ }
p.data = b
p.rwait.Signal()
for {
diff --git a/src/pkg/io/pipe_test.go b/src/pkg/io/pipe_test.go
index 7718151b0..b16e65306 100644
--- a/src/pkg/io/pipe_test.go
+++ b/src/pkg/io/pipe_test.go
@@ -268,3 +268,35 @@ func TestWriteNil(t *testing.T) {
ReadFull(r, b[0:2])
r.Close()
}
+
+func TestWriteAfterWriterClose(t *testing.T) {
+ r, w := Pipe()
+
+ done := make(chan bool)
+ var writeErr error
+ go func() {
+ _, err := w.Write([]byte("hello"))
+ if err != nil {
+ t.Errorf("got error: %q; expected none", err)
+ }
+ w.Close()
+ _, writeErr = w.Write([]byte("world"))
+ done <- true
+ }()
+
+ buf := make([]byte, 100)
+ var result string
+ n, err := ReadFull(r, buf)
+ if err != nil && err != ErrUnexpectedEOF {
+ t.Fatalf("got: %q; want: %q", err, ErrUnexpectedEOF)
+ }
+ result = string(buf[0:n])
+ <-done
+
+ if result != "hello" {
+ t.Errorf("got: %q; want: %q", result, "hello")
+ }
+ if writeErr != ErrClosedPipe {
+ t.Errorf("got: %q; want: %q", writeErr, ErrClosedPipe)
+ }
+}
diff --git a/src/pkg/log/syslog/syslog.go b/src/pkg/log/syslog/syslog.go
index 8bdd9825e..0cbfa9011 100644
--- a/src/pkg/log/syslog/syslog.go
+++ b/src/pkg/log/syslog/syslog.go
@@ -88,7 +88,23 @@ type Writer struct {
raddr string
mu sync.Mutex // guards conn
- conn net.Conn
+ conn serverConn
+}
+
+// This interface and the separate syslog_unix.go file exist for
+// Solaris support as implemented by gccgo. On Solaris you can not
+// simply open a TCP connection to the syslog daemon. The gccgo
+// sources have a syslog_solaris.go file that implements unixSyslog to
+// return a type that satisfies this interface and simply calls the C
+// library syslog function.
+type serverConn interface {
+ writeString(p Priority, hostname, tag, s, nl string) error
+ close() error
+}
+
+type netConn struct {
+ local bool
+ conn net.Conn
}
// New establishes a new connection to the system log daemon. Each
@@ -135,7 +151,7 @@ func Dial(network, raddr string, priority Priority, tag string) (*Writer, error)
func (w *Writer) connect() (err error) {
if w.conn != nil {
// ignore err from close, it makes sense to continue anyway
- w.conn.Close()
+ w.conn.close()
w.conn = nil
}
@@ -148,7 +164,7 @@ func (w *Writer) connect() (err error) {
var c net.Conn
c, err = net.Dial(w.network, w.raddr)
if err == nil {
- w.conn = c
+ w.conn = &netConn{conn: c}
if w.hostname == "" {
w.hostname = c.LocalAddr().String()
}
@@ -168,7 +184,7 @@ func (w *Writer) Close() error {
defer w.mu.Unlock()
if w.conn != nil {
- err := w.conn.Close()
+ err := w.conn.close()
w.conn = nil
return err
}
@@ -203,7 +219,7 @@ func (w *Writer) Err(m string) (err error) {
return err
}
-// Wanring logs a message with severity LOG_WARNING, ignoring the
+// Warning logs a message with severity LOG_WARNING, ignoring the
// severity passed to New.
func (w *Writer) Warning(m string) (err error) {
_, err = w.writeAndRetry(LOG_WARNING, m)
@@ -257,13 +273,38 @@ func (w *Writer) write(p Priority, msg string) (int, error) {
nl = "\n"
}
- timestamp := time.Now().Format(time.RFC3339)
- fmt.Fprintf(w.conn, "<%d>%s %s %s[%d]: %s%s",
- p, timestamp, w.hostname,
- w.tag, os.Getpid(), msg, nl)
+ err := w.conn.writeString(p, w.hostname, w.tag, msg, nl)
+ if err != nil {
+ return 0, err
+ }
+ // Note: return the length of the input, not the number of
+ // bytes printed by Fprintf, because this must behave like
+ // an io.Writer.
return len(msg), nil
}
+func (n *netConn) writeString(p Priority, hostname, tag, msg, nl string) error {
+ if n.local {
+ // Compared to the network form below, the changes are:
+ // 1. Use time.Stamp instead of time.RFC3339.
+ // 2. Drop the hostname field from the Fprintf.
+ timestamp := time.Now().Format(time.Stamp)
+ _, err := fmt.Fprintf(n.conn, "<%d>%s %s[%d]: %s%s",
+ p, timestamp,
+ tag, os.Getpid(), msg, nl)
+ return err
+ }
+ timestamp := time.Now().Format(time.RFC3339)
+ _, err := fmt.Fprintf(n.conn, "<%d>%s %s %s[%d]: %s%s",
+ p, timestamp, hostname,
+ tag, os.Getpid(), msg, nl)
+ return err
+}
+
+func (n *netConn) close() error {
+ return n.conn.Close()
+}
+
// NewLogger creates a log.Logger whose output is written to
// the system log service with the specified priority. The logFlag
// argument is the flag set passed through to log.New to create
diff --git a/src/pkg/log/syslog/syslog_test.go b/src/pkg/log/syslog/syslog_test.go
index ec4525190..760a5c7d1 100644
--- a/src/pkg/log/syslog/syslog_test.go
+++ b/src/pkg/log/syslog/syslog_test.go
@@ -122,7 +122,9 @@ func TestWithSimulated(t *testing.T) {
for _, tr := range transport {
done := make(chan string)
- addr, _, _ := startServer(tr, "", done)
+ addr, sock, srvWG := startServer(tr, "", done)
+ defer srvWG.Wait()
+ defer sock.Close()
if tr == "unix" || tr == "unixgram" {
defer os.Remove(addr)
}
@@ -142,7 +144,8 @@ func TestWithSimulated(t *testing.T) {
func TestFlap(t *testing.T) {
net := "unix"
done := make(chan string)
- addr, sock, _ := startServer(net, "", done)
+ addr, sock, srvWG := startServer(net, "", done)
+ defer srvWG.Wait()
defer os.Remove(addr)
defer sock.Close()
@@ -158,7 +161,8 @@ func TestFlap(t *testing.T) {
check(t, msg, <-done)
// restart the server
- _, sock2, _ := startServer(net, addr, done)
+ _, sock2, srvWG2 := startServer(net, addr, done)
+ defer srvWG2.Wait()
defer sock2.Close()
// and try retransmitting
@@ -249,12 +253,14 @@ func TestWrite(t *testing.T) {
} else {
for _, test := range tests {
done := make(chan string)
- addr, sock, _ := startServer("udp", "", done)
+ addr, sock, srvWG := startServer("udp", "", done)
+ defer srvWG.Wait()
defer sock.Close()
l, err := Dial("udp", addr, test.pri, test.pre)
if err != nil {
t.Fatalf("syslog.Dial() failed: %v", err)
}
+ defer l.Close()
_, err = io.WriteString(l, test.msg)
if err != nil {
t.Fatalf("WriteString() failed: %v", err)
@@ -271,7 +277,8 @@ func TestWrite(t *testing.T) {
}
func TestConcurrentWrite(t *testing.T) {
- addr, sock, _ := startServer("udp", "", make(chan string))
+ addr, sock, srvWG := startServer("udp", "", make(chan string, 1))
+ defer srvWG.Wait()
defer sock.Close()
w, err := Dial("udp", addr, LOG_USER|LOG_ERR, "how's it going?")
if err != nil {
@@ -281,12 +288,12 @@ func TestConcurrentWrite(t *testing.T) {
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
+ defer wg.Done()
err := w.Info("test")
if err != nil {
t.Errorf("Info() failed: %v", err)
return
}
- wg.Done()
}()
}
wg.Wait()
@@ -296,8 +303,10 @@ func TestConcurrentReconnect(t *testing.T) {
crashy = true
defer func() { crashy = false }()
+ const N = 10
+ const M = 100
net := "unix"
- done := make(chan string)
+ done := make(chan string, N*M)
addr, sock, srvWG := startServer(net, "", done)
defer os.Remove(addr)
@@ -310,7 +319,7 @@ func TestConcurrentReconnect(t *testing.T) {
// we are looking for 500 out of 1000 events
// here because lots of log messages are lost
// in buffers (kernel and/or bufio)
- if ct > 500 {
+ if ct > N*M/2 {
break
}
}
@@ -318,21 +327,22 @@ func TestConcurrentReconnect(t *testing.T) {
}()
var wg sync.WaitGroup
- for i := 0; i < 10; i++ {
- wg.Add(1)
+ wg.Add(N)
+ for i := 0; i < N; i++ {
go func() {
+ defer wg.Done()
w, err := Dial(net, addr, LOG_USER|LOG_ERR, "tag")
if err != nil {
t.Fatalf("syslog.Dial() failed: %v", err)
}
- for i := 0; i < 100; i++ {
+ defer w.Close()
+ for i := 0; i < M; i++ {
err := w.Info("test")
if err != nil {
t.Errorf("Info() failed: %v", err)
return
}
}
- wg.Done()
}()
}
wg.Wait()
diff --git a/src/pkg/log/syslog/syslog_unix.go b/src/pkg/log/syslog/syslog_unix.go
index a0001ccae..28a294af9 100644
--- a/src/pkg/log/syslog/syslog_unix.go
+++ b/src/pkg/log/syslog/syslog_unix.go
@@ -14,7 +14,7 @@ import (
// unixSyslog opens a connection to the syslog daemon running on the
// local machine using a Unix domain socket.
-func unixSyslog() (conn net.Conn, err error) {
+func unixSyslog() (conn serverConn, err error) {
logTypes := []string{"unixgram", "unix"}
logPaths := []string{"/dev/log", "/var/run/syslog"}
for _, network := range logTypes {
@@ -23,7 +23,7 @@ func unixSyslog() (conn net.Conn, err error) {
if err != nil {
continue
} else {
- return conn, nil
+ return &netConn{conn: conn, local: true}, nil
}
}
}
diff --git a/src/pkg/math/abs_386.s b/src/pkg/math/abs_386.s
index 574676475..3490cf66c 100644
--- a/src/pkg/math/abs_386.s
+++ b/src/pkg/math/abs_386.s
@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
// func Abs(x float64) float64
-TEXT ·Abs(SB),7,$0
+TEXT ·Abs(SB),NOSPLIT,$0
FMOVD x+0(FP), F0 // F0=x
FABS // F0=|x|
FMOVDP F0, ret+8(FP)
diff --git a/src/pkg/math/abs_amd64.s b/src/pkg/math/abs_amd64.s
index 119346045..779c8f548 100644
--- a/src/pkg/math/abs_amd64.s
+++ b/src/pkg/math/abs_amd64.s
@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
// func Abs(x float64) float64
-TEXT ·Abs(SB),7,$0
+TEXT ·Abs(SB),NOSPLIT,$0
MOVQ $(1<<63), BX
MOVQ BX, X0 // movsd $(-0.0), x0
MOVSD x+0(FP), X1
diff --git a/src/pkg/math/abs_arm.s b/src/pkg/math/abs_arm.s
index 929e1ce67..b5117ab39 100644
--- a/src/pkg/math/abs_arm.s
+++ b/src/pkg/math/abs_arm.s
@@ -2,7 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT ·Abs(SB),7,$0
+#include "../../cmd/ld/textflag.h"
+
+TEXT ·Abs(SB),NOSPLIT,$0
MOVW x_lo+0(FP), R0
MOVW x_hi+4(FP), R1
AND $((1<<31)-1), R1
diff --git a/src/pkg/math/asin.go b/src/pkg/math/asin.go
index 00bf61ee4..88b851e55 100644
--- a/src/pkg/math/asin.go
+++ b/src/pkg/math/asin.go
@@ -11,7 +11,7 @@ package math
after appropriate range reduction.
*/
-// Asin returns the arcsine of x.
+// Asin returns the arcsine, in radians, of x.
//
// Special cases are:
// Asin(±0) = ±0
@@ -44,7 +44,7 @@ func asin(x float64) float64 {
return temp
}
-// Acos returns the arccosine of x.
+// Acos returns the arccosine, in radians, of x.
//
// Special case is:
// Acos(x) = NaN if x < -1 or x > 1
diff --git a/src/pkg/math/asin_386.s b/src/pkg/math/asin_386.s
index cd3f9cd9b..2c1d27094 100644
--- a/src/pkg/math/asin_386.s
+++ b/src/pkg/math/asin_386.s
@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
// func Asin(x float64) float64
-TEXT ·Asin(SB),7,$0
+TEXT ·Asin(SB),NOSPLIT,$0
FMOVD x+0(FP), F0 // F0=sin(x)
FMOVD F0, F1 // F0=sin(x), F1=sin(x)
FMULD F0, F0 // F0=sin(x)*sin(x), F1=sin(x)
@@ -15,7 +17,7 @@ TEXT ·Asin(SB),7,$0
RET
// func Acos(x float64) float64
-TEXT ·Acos(SB),7,$0
+TEXT ·Acos(SB),NOSPLIT,$0
FMOVD x+0(FP), F0 // F0=cos(x)
FMOVD F0, F1 // F0=cos(x), F1=cos(x)
FMULD F0, F0 // F0=cos(x)*cos(x), F1=cos(x)
diff --git a/src/pkg/math/asin_amd64.s b/src/pkg/math/asin_amd64.s
index 42151f1e9..ea48104ac 100644
--- a/src/pkg/math/asin_amd64.s
+++ b/src/pkg/math/asin_amd64.s
@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT ·Asin(SB),7,$0
+#include "../../cmd/ld/textflag.h"
+
+TEXT ·Asin(SB),NOSPLIT,$0
JMP ·asin(SB)
-TEXT ·Acos(SB),7,$0
+TEXT ·Acos(SB),NOSPLIT,$0
JMP ·acos(SB)
diff --git a/src/pkg/math/asin_arm.s b/src/pkg/math/asin_arm.s
index d27213fad..b90526003 100644
--- a/src/pkg/math/asin_arm.s
+++ b/src/pkg/math/asin_arm.s
@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT ·Asin(SB),7,$0
+#include "../../cmd/ld/textflag.h"
+
+TEXT ·Asin(SB),NOSPLIT,$0
B ·asin(SB)
-TEXT ·Acos(SB),7,$0
+TEXT ·Acos(SB),NOSPLIT,$0
B ·acos(SB)
diff --git a/src/pkg/math/atan.go b/src/pkg/math/atan.go
index c107d388d..7fcc90b8b 100644
--- a/src/pkg/math/atan.go
+++ b/src/pkg/math/atan.go
@@ -87,7 +87,7 @@ func satan(x float64) float64 {
return Pi/4 + xatan((x-1)/(x+1)) + 0.5*Morebits
}
-// Atan returns the arctangent of x.
+// Atan returns the arctangent, in radians, of x.
//
// Special cases are:
// Atan(±0) = ±0
diff --git a/src/pkg/math/atan2_386.s b/src/pkg/math/atan2_386.s
index 1bf301c4c..fb649316a 100644
--- a/src/pkg/math/atan2_386.s
+++ b/src/pkg/math/atan2_386.s
@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
// func Atan2(y, x float64) float64 // =atan(y/x)
-TEXT ·Atan2(SB),7,$0
+TEXT ·Atan2(SB),NOSPLIT,$0
FMOVD y+0(FP), F0 // F0=y
FMOVD x+8(FP), F0 // F0=x, F1=y
FPATAN // F0=atan(F1/F0)
diff --git a/src/pkg/math/atan2_amd64.s b/src/pkg/math/atan2_amd64.s
index 1c5b038c2..f7a5a11d4 100644
--- a/src/pkg/math/atan2_amd64.s
+++ b/src/pkg/math/atan2_amd64.s
@@ -2,5 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT ·Atan2(SB),7,$0
+#include "../../cmd/ld/textflag.h"
+
+TEXT ·Atan2(SB),NOSPLIT,$0
JMP ·atan2(SB)
diff --git a/src/pkg/math/atan2_arm.s b/src/pkg/math/atan2_arm.s
index c2edafae1..24bff2c03 100644
--- a/src/pkg/math/atan2_arm.s
+++ b/src/pkg/math/atan2_arm.s
@@ -2,5 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT ·Atan2(SB),7,$0
+#include "../../cmd/ld/textflag.h"
+
+TEXT ·Atan2(SB),NOSPLIT,$0
B ·atan2(SB)
diff --git a/src/pkg/math/atan_386.s b/src/pkg/math/atan_386.s
index c988705be..aad8ffcec 100644
--- a/src/pkg/math/atan_386.s
+++ b/src/pkg/math/atan_386.s
@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
// func Atan(x float64) float64
-TEXT ·Atan(SB),7,$0
+TEXT ·Atan(SB),NOSPLIT,$0
FMOVD x+0(FP), F0 // F0=x
FLD1 // F0=1, F1=x
FPATAN // F0=atan(F1/F0)
diff --git a/src/pkg/math/atan_amd64.s b/src/pkg/math/atan_amd64.s
index 206072b93..fc4a91b0d 100644
--- a/src/pkg/math/atan_amd64.s
+++ b/src/pkg/math/atan_amd64.s
@@ -2,5 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT ·Atan(SB),7,$0
+#include "../../cmd/ld/textflag.h"
+
+TEXT ·Atan(SB),NOSPLIT,$0
JMP ·atan(SB)
diff --git a/src/pkg/math/atan_arm.s b/src/pkg/math/atan_arm.s
index ed492ab46..defa93a1e 100644
--- a/src/pkg/math/atan_arm.s
+++ b/src/pkg/math/atan_arm.s
@@ -2,5 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT ·Atan(SB),7,$0
+#include "../../cmd/ld/textflag.h"
+
+TEXT ·Atan(SB),NOSPLIT,$0
B ·atan(SB)
diff --git a/src/pkg/math/big/arith_386.s b/src/pkg/math/big/arith_386.s
index f0118ec0d..15b036c65 100644
--- a/src/pkg/math/big/arith_386.s
+++ b/src/pkg/math/big/arith_386.s
@@ -2,11 +2,13 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../../cmd/ld/textflag.h"
+
// This file provides fast assembly versions for the elementary
// arithmetic operations on vectors implemented in arith.go.
// func mulWW(x, y Word) (z1, z0 Word)
-TEXT ·mulWW(SB),7,$0
+TEXT ·mulWW(SB),NOSPLIT,$0
MOVL x+0(FP), AX
MULL y+4(FP)
MOVL DX, z1+8(FP)
@@ -15,7 +17,7 @@ TEXT ·mulWW(SB),7,$0
// func divWW(x1, x0, y Word) (q, r Word)
-TEXT ·divWW(SB),7,$0
+TEXT ·divWW(SB),NOSPLIT,$0
MOVL x1+0(FP), DX
MOVL x0+4(FP), AX
DIVL y+8(FP)
@@ -25,7 +27,7 @@ TEXT ·divWW(SB),7,$0
// func addVV(z, x, y []Word) (c Word)
-TEXT ·addVV(SB),7,$0
+TEXT ·addVV(SB),NOSPLIT,$0
MOVL z+0(FP), DI
MOVL x+12(FP), SI
MOVL y+24(FP), CX
@@ -50,7 +52,7 @@ E1: CMPL BX, BP // i < n
// func subVV(z, x, y []Word) (c Word)
// (same as addVV except for SBBL instead of ADCL and label names)
-TEXT ·subVV(SB),7,$0
+TEXT ·subVV(SB),NOSPLIT,$0
MOVL z+0(FP), DI
MOVL x+12(FP), SI
MOVL y+24(FP), CX
@@ -74,7 +76,7 @@ E2: CMPL BX, BP // i < n
// func addVW(z, x []Word, y Word) (c Word)
-TEXT ·addVW(SB),7,$0
+TEXT ·addVW(SB),NOSPLIT,$0
MOVL z+0(FP), DI
MOVL x+12(FP), SI
MOVL y+24(FP), AX // c = y
@@ -96,7 +98,7 @@ E3: CMPL BX, BP // i < n
// func subVW(z, x []Word, y Word) (c Word)
-TEXT ·subVW(SB),7,$0
+TEXT ·subVW(SB),NOSPLIT,$0
MOVL z+0(FP), DI
MOVL x+12(FP), SI
MOVL y+24(FP), AX // c = y
@@ -119,7 +121,7 @@ E4: CMPL BX, BP // i < n
// func shlVU(z, x []Word, s uint) (c Word)
-TEXT ·shlVU(SB),7,$0
+TEXT ·shlVU(SB),NOSPLIT,$0
MOVL z_len+4(FP), BX // i = z
SUBL $1, BX // i--
JL X8b // i < 0 (n <= 0)
@@ -154,7 +156,7 @@ X8b: MOVL $0, c+28(FP)
// func shrVU(z, x []Word, s uint) (c Word)
-TEXT ·shrVU(SB),7,$0
+TEXT ·shrVU(SB),NOSPLIT,$0
MOVL z_len+4(FP), BP
SUBL $1, BP // n--
JL X9b // n < 0 (n <= 0)
@@ -191,7 +193,7 @@ X9b: MOVL $0, c+28(FP)
// func mulAddVWW(z, x []Word, y, r Word) (c Word)
-TEXT ·mulAddVWW(SB),7,$0
+TEXT ·mulAddVWW(SB),NOSPLIT,$0
MOVL z+0(FP), DI
MOVL x+12(FP), SI
MOVL y+24(FP), BP
@@ -218,7 +220,7 @@ E5: CMPL BX, $0 // i < 0
// func addMulVVW(z, x []Word, y Word) (c Word)
-TEXT ·addMulVVW(SB),7,$0
+TEXT ·addMulVVW(SB),NOSPLIT,$0
MOVL z+0(FP), DI
MOVL x+12(FP), SI
MOVL y+24(FP), BP
@@ -246,7 +248,7 @@ E6: CMPL BX, $0 // i < 0
// func divWVW(z* Word, xn Word, x []Word, y Word) (r Word)
-TEXT ·divWVW(SB),7,$0
+TEXT ·divWVW(SB),NOSPLIT,$0
MOVL z+0(FP), DI
MOVL xn+12(FP), DX // r = xn
MOVL x+16(FP), SI
@@ -265,7 +267,7 @@ E7: SUBL $1, BX // i--
RET
// func bitLen(x Word) (n int)
-TEXT ·bitLen(SB),7,$0
+TEXT ·bitLen(SB),NOSPLIT,$0
BSRL x+0(FP), AX
JZ Z1
INCL AX
diff --git a/src/pkg/math/big/arith_amd64.s b/src/pkg/math/big/arith_amd64.s
index 62da65030..e2113a7e3 100644
--- a/src/pkg/math/big/arith_amd64.s
+++ b/src/pkg/math/big/arith_amd64.s
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../../cmd/ld/textflag.h"
+
// This file provides fast assembly versions for the elementary
// arithmetic operations on vectors implemented in arith.go.
@@ -16,7 +18,7 @@
BYTE $0x00
// func mulWW(x, y Word) (z1, z0 Word)
-TEXT ·mulWW(SB),7,$0
+TEXT ·mulWW(SB),NOSPLIT,$0
MOVQ x+0(FP), AX
MULQ y+8(FP)
MOVQ DX, z1+16(FP)
@@ -25,7 +27,7 @@ TEXT ·mulWW(SB),7,$0
// func divWW(x1, x0, y Word) (q, r Word)
-TEXT ·divWW(SB),7,$0
+TEXT ·divWW(SB),NOSPLIT,$0
MOVQ x1+0(FP), DX
MOVQ x0+8(FP), AX
DIVQ y+16(FP)
@@ -35,7 +37,7 @@ TEXT ·divWW(SB),7,$0
// func addVV(z, x, y []Word) (c Word)
-TEXT ·addVV(SB),7,$0
+TEXT ·addVV(SB),NOSPLIT,$0
MOVQ z_len+8(FP), DI
MOVQ x+24(FP), R8
MOVQ y+48(FP), R9
@@ -89,7 +91,7 @@ E1: MOVQ CX, c+72(FP) // return c
// func subVV(z, x, y []Word) (c Word)
// (same as addVV except for SBBQ instead of ADCQ and label names)
-TEXT ·subVV(SB),7,$0
+TEXT ·subVV(SB),NOSPLIT,$0
MOVQ z_len+8(FP), DI
MOVQ x+24(FP), R8
MOVQ y+48(FP), R9
@@ -142,7 +144,7 @@ E2: MOVQ CX, c+72(FP) // return c
// func addVW(z, x []Word, y Word) (c Word)
-TEXT ·addVW(SB),7,$0
+TEXT ·addVW(SB),NOSPLIT,$0
MOVQ z_len+8(FP), DI
MOVQ x+24(FP), R8
MOVQ y+48(FP), CX // c = y
@@ -194,7 +196,7 @@ E3: MOVQ CX, c+56(FP) // return c
// func subVW(z, x []Word, y Word) (c Word)
// (same as addVW except for SUBQ/SBBQ instead of ADDQ/ADCQ and label names)
-TEXT ·subVW(SB),7,$0
+TEXT ·subVW(SB),NOSPLIT,$0
MOVQ z_len+8(FP), DI
MOVQ x+24(FP), R8
MOVQ y+48(FP), CX // c = y
@@ -246,7 +248,7 @@ E4: MOVQ CX, c+56(FP) // return c
// func shlVU(z, x []Word, s uint) (c Word)
-TEXT ·shlVU(SB),7,$0
+TEXT ·shlVU(SB),NOSPLIT,$0
MOVQ z_len+8(FP), BX // i = z
SUBQ $1, BX // i--
JL X8b // i < 0 (n <= 0)
@@ -281,7 +283,7 @@ X8b: MOVQ $0, c+56(FP)
// func shrVU(z, x []Word, s uint) (c Word)
-TEXT ·shrVU(SB),7,$0
+TEXT ·shrVU(SB),NOSPLIT,$0
MOVQ z_len+8(FP), R11
SUBQ $1, R11 // n--
JL X9b // n < 0 (n <= 0)
@@ -318,7 +320,7 @@ X9b: MOVQ $0, c+56(FP)
// func mulAddVWW(z, x []Word, y, r Word) (c Word)
-TEXT ·mulAddVWW(SB),7,$0
+TEXT ·mulAddVWW(SB),NOSPLIT,$0
MOVQ z+0(FP), R10
MOVQ x+24(FP), R8
MOVQ y+48(FP), R9
@@ -343,7 +345,7 @@ E5: CMPQ BX, R11 // i < n
// func addMulVVW(z, x []Word, y Word) (c Word)
-TEXT ·addMulVVW(SB),7,$0
+TEXT ·addMulVVW(SB),NOSPLIT,$0
MOVQ z+0(FP), R10
MOVQ x+24(FP), R8
MOVQ y+48(FP), R9
@@ -369,7 +371,7 @@ E6: CMPQ BX, R11 // i < n
// func divWVW(z []Word, xn Word, x []Word, y Word) (r Word)
-TEXT ·divWVW(SB),7,$0
+TEXT ·divWVW(SB),NOSPLIT,$0
MOVQ z+0(FP), R10
MOVQ xn+24(FP), DX // r = xn
MOVQ x+32(FP), R8
@@ -388,7 +390,7 @@ E7: SUBQ $1, BX // i--
RET
// func bitLen(x Word) (n int)
-TEXT ·bitLen(SB),7,$0
+TEXT ·bitLen(SB),NOSPLIT,$0
BSRQ x+0(FP), AX
JZ Z1
ADDQ $1, AX
diff --git a/src/pkg/math/big/arith_arm.s b/src/pkg/math/big/arith_arm.s
index 6e2d23d33..ecf55b344 100644
--- a/src/pkg/math/big/arith_arm.s
+++ b/src/pkg/math/big/arith_arm.s
@@ -2,13 +2,15 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../../cmd/ld/textflag.h"
+
// This file provides fast assembly versions for the elementary
// arithmetic operations on vectors implemented in arith.go.
#define CFLAG 29 // bit position of carry flag
// func addVV(z, x, y []Word) (c Word)
-TEXT ·addVV(SB),7,$0
+TEXT ·addVV(SB),NOSPLIT,$0
MOVW $0, R0
MOVW z+0(FP), R1
MOVW x+12(FP), R2
@@ -36,7 +38,7 @@ E1:
// func subVV(z, x, y []Word) (c Word)
// (same as addVV except for SBC instead of ADC and label names)
-TEXT ·subVV(SB),7,$0
+TEXT ·subVV(SB),NOSPLIT,$0
MOVW $(1<<CFLAG), R0
MOVW z+0(FP), R1
MOVW x+12(FP), R2
@@ -64,7 +66,7 @@ E2:
// func addVW(z, x []Word, y Word) (c Word)
-TEXT ·addVW(SB),7,$0
+TEXT ·addVW(SB),NOSPLIT,$0
MOVW z+0(FP), R1
MOVW x+12(FP), R2
MOVW y+24(FP), R3
@@ -98,7 +100,7 @@ E3:
// func subVW(z, x []Word, y Word) (c Word)
-TEXT ·subVW(SB),7,$0
+TEXT ·subVW(SB),NOSPLIT,$0
MOVW z+0(FP), R1
MOVW x+12(FP), R2
MOVW y+24(FP), R3
@@ -133,7 +135,7 @@ E4:
// func shlVU(z, x []Word, s uint) (c Word)
-TEXT ·shlVU(SB),7,$0
+TEXT ·shlVU(SB),NOSPLIT,$0
MOVW z_len+4(FP), R5
CMP $0, R5
BEQ X7
@@ -182,7 +184,7 @@ X7:
// func shrVU(z, x []Word, s uint) (c Word)
-TEXT ·shrVU(SB),7,$0
+TEXT ·shrVU(SB),NOSPLIT,$0
MOVW z_len+4(FP), R5
CMP $0, R5
BEQ X6
@@ -232,7 +234,7 @@ X6:
// func mulAddVWW(z, x []Word, y, r Word) (c Word)
-TEXT ·mulAddVWW(SB),7,$0
+TEXT ·mulAddVWW(SB),NOSPLIT,$0
MOVW $0, R0
MOVW z+0(FP), R1
MOVW x+12(FP), R2
@@ -260,7 +262,7 @@ E8:
// func addMulVVW(z, x []Word, y Word) (c Word)
-TEXT ·addMulVVW(SB),7,$0
+TEXT ·addMulVVW(SB),NOSPLIT,$0
MOVW $0, R0
MOVW z+0(FP), R1
MOVW x+12(FP), R2
@@ -291,19 +293,19 @@ E9:
// func divWVW(z* Word, xn Word, x []Word, y Word) (r Word)
-TEXT ·divWVW(SB),7,$0
+TEXT ·divWVW(SB),NOSPLIT,$0
// ARM has no multiword division, so use portable code.
B ·divWVW_g(SB)
// func divWW(x1, x0, y Word) (q, r Word)
-TEXT ·divWW(SB),7,$0
+TEXT ·divWW(SB),NOSPLIT,$0
// ARM has no multiword division, so use portable code.
B ·divWW_g(SB)
// func mulWW(x, y Word) (z1, z0 Word)
-TEXT ·mulWW(SB),7,$0
+TEXT ·mulWW(SB),NOSPLIT,$0
MOVW x+0(FP), R1
MOVW y+4(FP), R2
MULLU R1, R2, (R4, R3)
@@ -312,7 +314,7 @@ TEXT ·mulWW(SB),7,$0
RET
// func bitLen(x Word) (n int)
-TEXT ·bitLen(SB),7,$0
+TEXT ·bitLen(SB),NOSPLIT,$0
MOVW x+0(FP), R0
CLZ R0, R0
MOVW $32, R1
diff --git a/src/pkg/math/big/int.go b/src/pkg/math/big/int.go
index d1b5602d6..7bbb152d7 100644
--- a/src/pkg/math/big/int.go
+++ b/src/pkg/math/big/int.go
@@ -563,13 +563,13 @@ func (z *Int) SetBytes(buf []byte) *Int {
return z
}
-// Bytes returns the absolute value of z as a big-endian byte slice.
+// Bytes returns the absolute value of x as a big-endian byte slice.
func (x *Int) Bytes() []byte {
buf := make([]byte, len(x.abs)*_S)
return buf[x.abs.bytes(buf):]
}
-// BitLen returns the length of the absolute value of z in bits.
+// BitLen returns the length of the absolute value of x in bits.
// The bit length of 0 is 0.
func (x *Int) BitLen() int {
return x.abs.bitLen()
@@ -703,14 +703,15 @@ func (z *Int) binaryGCD(a, b *Int) *Int {
// reduce t
t.Rsh(t, t.abs.trailingZeroBits())
if t.neg {
- v.Neg(t)
+ v, t = t, v
+ v.neg = len(v.abs) > 0 && !v.neg // 0 has no sign
} else {
- u.Set(t)
+ u, t = t, u
}
t.Sub(u, v)
}
- return u.Lsh(u, k)
+ return z.Lsh(u, k)
}
// ProbablyPrime performs n Miller-Rabin tests to check whether x is prime.
@@ -951,6 +952,9 @@ const intGobVersion byte = 1
// GobEncode implements the gob.GobEncoder interface.
func (x *Int) GobEncode() ([]byte, error) {
+ if x == nil {
+ return nil, nil
+ }
buf := make([]byte, 1+len(x.abs)*_S) // extra byte for version and sign bit
i := x.abs.bytes(buf) - 1 // i >= 0
b := intGobVersion << 1 // make space for sign bit
@@ -964,7 +968,9 @@ func (x *Int) GobEncode() ([]byte, error) {
// GobDecode implements the gob.GobDecoder interface.
func (z *Int) GobDecode(buf []byte) error {
if len(buf) == 0 {
- return errors.New("Int.GobDecode: no data")
+ // Other side sent a nil or default value.
+ *z = Int{}
+ return nil
}
b := buf[0]
if b>>1 != intGobVersion {
diff --git a/src/pkg/math/big/int_test.go b/src/pkg/math/big/int_test.go
index 6c981e775..87b975d5c 100644
--- a/src/pkg/math/big/int_test.go
+++ b/src/pkg/math/big/int_test.go
@@ -89,7 +89,7 @@ func testFunZZ(t *testing.T, msg string, f funZZ, a argZZ) {
var z Int
f(&z, a.x, a.y)
if !isNormalized(&z) {
- t.Errorf("%s%v is not normalized", z, msg)
+ t.Errorf("%s%v is not normalized", msg, z)
}
if (&z).Cmp(a.z) != 0 {
t.Errorf("%s%+v\n\tgot z = %v; want %v", msg, a, &z, a.z)
@@ -1484,6 +1484,32 @@ func TestIntGobEncoding(t *testing.T) {
}
}
+// Sending a nil Int pointer (inside a slice) on a round trip through gob should yield a zero.
+// TODO: top-level nils.
+func TestGobEncodingNilIntInSlice(t *testing.T) {
+ buf := new(bytes.Buffer)
+ enc := gob.NewEncoder(buf)
+ dec := gob.NewDecoder(buf)
+
+ var in = make([]*Int, 1)
+ err := enc.Encode(&in)
+ if err != nil {
+ t.Errorf("gob encode failed: %q", err)
+ }
+ var out []*Int
+ err = dec.Decode(&out)
+ if err != nil {
+ t.Fatalf("gob decode failed: %q", err)
+ }
+ if len(out) != 1 {
+ t.Fatalf("wrong len; want 1 got %d", len(out))
+ }
+ var zero Int
+ if out[0].Cmp(&zero) != 0 {
+ t.Errorf("transmission of (*Int)(nill) failed: got %s want 0", out)
+ }
+}
+
func TestIntJSONEncoding(t *testing.T) {
for _, test := range encodingTests {
var tx Int
diff --git a/src/pkg/math/big/nat_test.go b/src/pkg/math/big/nat_test.go
index 2dd7bf639..1d4dfe80d 100644
--- a/src/pkg/math/big/nat_test.go
+++ b/src/pkg/math/big/nat_test.go
@@ -685,7 +685,7 @@ func runModWTests(t *testing.T, tests []modWTest) {
r := in.abs.modW(d.abs[0])
if r != out.abs[0] {
- t.Errorf("#%d failed: got %s want %s", i, r, out)
+ t.Errorf("#%d failed: got %d want %s", i, r, out)
}
}
}
diff --git a/src/pkg/math/big/rat.go b/src/pkg/math/big/rat.go
index 75d044fe2..7faee61a4 100644
--- a/src/pkg/math/big/rat.go
+++ b/src/pkg/math/big/rat.go
@@ -164,8 +164,9 @@ func quotToFloat(a, b nat) (f float64, exact bool) {
}
// Float64 returns the nearest float64 value for x and a bool indicating
-// whether f represents x exactly. The sign of f always matches the sign
-// of x, even if f == 0.
+// whether f represents x exactly. If the magnitude of x is too large to
+// be represented by a float64, f is an infinity and exact is false.
+// The sign of f always matches the sign of x, even if f == 0.
func (x *Rat) Float64() (f float64, exact bool) {
b := x.b.abs
if len(b) == 0 {
@@ -545,6 +546,9 @@ const ratGobVersion byte = 1
// GobEncode implements the gob.GobEncoder interface.
func (x *Rat) GobEncode() ([]byte, error) {
+ if x == nil {
+ return nil, nil
+ }
buf := make([]byte, 1+4+(len(x.a.abs)+len(x.b.abs))*_S) // extra bytes for version and sign bit (1), and numerator length (4)
i := x.b.abs.bytes(buf)
j := x.a.abs.bytes(buf[0:i])
@@ -566,7 +570,9 @@ func (x *Rat) GobEncode() ([]byte, error) {
// GobDecode implements the gob.GobDecoder interface.
func (z *Rat) GobDecode(buf []byte) error {
if len(buf) == 0 {
- return errors.New("Rat.GobDecode: no data")
+ // Other side sent a nil or default value.
+ *z = Rat{}
+ return nil
}
b := buf[0]
if b>>1 != ratGobVersion {
diff --git a/src/pkg/math/big/rat_test.go b/src/pkg/math/big/rat_test.go
index 1c2c64237..0d432637b 100644
--- a/src/pkg/math/big/rat_test.go
+++ b/src/pkg/math/big/rat_test.go
@@ -407,6 +407,32 @@ func TestRatGobEncoding(t *testing.T) {
}
}
+// Sending a nil Rat pointer (inside a slice) on a round trip through gob should yield a zero.
+// TODO: top-level nils.
+func TestGobEncodingNilRatInSlice(t *testing.T) {
+ buf := new(bytes.Buffer)
+ enc := gob.NewEncoder(buf)
+ dec := gob.NewDecoder(buf)
+
+ var in = make([]*Rat, 1)
+ err := enc.Encode(&in)
+ if err != nil {
+ t.Errorf("gob encode failed: %q", err)
+ }
+ var out []*Rat
+ err = dec.Decode(&out)
+ if err != nil {
+ t.Fatalf("gob decode failed: %q", err)
+ }
+ if len(out) != 1 {
+ t.Fatalf("wrong len; want 1 got %d", len(out))
+ }
+ var zero Rat
+ if out[0].Cmp(&zero) != 0 {
+ t.Errorf("transmission of (*Int)(nill) failed: got %s want 0", out)
+ }
+}
+
func TestIssue2379(t *testing.T) {
// 1) no aliasing
q := NewRat(3, 2)
diff --git a/src/pkg/math/bits.go b/src/pkg/math/bits.go
index 0df0b1cc9..d85ee9cb1 100644
--- a/src/pkg/math/bits.go
+++ b/src/pkg/math/bits.go
@@ -27,7 +27,7 @@ func Inf(sign int) float64 {
// NaN returns an IEEE 754 ``not-a-number'' value.
func NaN() float64 { return Float64frombits(uvnan) }
-// IsNaN returns whether f is an IEEE 754 ``not-a-number'' value.
+// IsNaN reports whether f is an IEEE 754 ``not-a-number'' value.
func IsNaN(f float64) (is bool) {
// IEEE 754 says that only NaNs satisfy f != f.
// To avoid the floating-point hardware, could use:
@@ -36,10 +36,10 @@ func IsNaN(f float64) (is bool) {
return f != f
}
-// IsInf returns whether f is an infinity, according to sign.
-// If sign > 0, IsInf returns whether f is positive infinity.
-// If sign < 0, IsInf returns whether f is negative infinity.
-// If sign == 0, IsInf returns whether f is either infinity.
+// IsInf reports whether f is an infinity, according to sign.
+// If sign > 0, IsInf reports whether f is positive infinity.
+// If sign < 0, IsInf reports whether f is negative infinity.
+// If sign == 0, IsInf reports whether f is either infinity.
func IsInf(f float64, sign int) bool {
// Test for infinity by comparing against maximum float.
// To avoid the floating-point hardware, could use:
diff --git a/src/pkg/math/dim_386.s b/src/pkg/math/dim_386.s
index 6a31c7540..f715114c4 100644
--- a/src/pkg/math/dim_386.s
+++ b/src/pkg/math/dim_386.s
@@ -2,11 +2,13 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT ·Dim(SB),7,$0
+#include "../../cmd/ld/textflag.h"
+
+TEXT ·Dim(SB),NOSPLIT,$0
JMP ·dim(SB)
-TEXT ·Max(SB),7,$0
+TEXT ·Max(SB),NOSPLIT,$0
JMP ·max(SB)
-TEXT ·Min(SB),7,$0
+TEXT ·Min(SB),NOSPLIT,$0
JMP ·min(SB)
diff --git a/src/pkg/math/dim_amd64.s b/src/pkg/math/dim_amd64.s
index 0ae8ad196..38423ef02 100644
--- a/src/pkg/math/dim_amd64.s
+++ b/src/pkg/math/dim_amd64.s
@@ -2,12 +2,14 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
#define PosInf 0x7FF0000000000000
#define NaN 0x7FF8000000000001
#define NegInf 0xFFF0000000000000
// func Dim(x, y float64) float64
-TEXT ·Dim(SB),7,$0
+TEXT ·Dim(SB),NOSPLIT,$0
// (+Inf, +Inf) special case
MOVQ x+0(FP), BX
MOVQ y+8(FP), CX
@@ -45,7 +47,7 @@ isDimNaN:
RET
// func ·Max(x, y float64) float64
-TEXT ·Max(SB),7,$0
+TEXT ·Max(SB),NOSPLIT,$0
// +Inf special cases
MOVQ $PosInf, AX
MOVQ x+0(FP), R8
@@ -98,7 +100,7 @@ isMaxZero:
*/
// func Min(x, y float64) float64
-TEXT ·Min(SB),7,$0
+TEXT ·Min(SB),NOSPLIT,$0
// -Inf special cases
MOVQ $NegInf, AX
MOVQ x+0(FP), R8
diff --git a/src/pkg/math/dim_arm.s b/src/pkg/math/dim_arm.s
index 304fa78cd..162f08cda 100644
--- a/src/pkg/math/dim_arm.s
+++ b/src/pkg/math/dim_arm.s
@@ -2,11 +2,13 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT ·Dim(SB),7,$0
+#include "../../cmd/ld/textflag.h"
+
+TEXT ·Dim(SB),NOSPLIT,$0
B ·dim(SB)
-TEXT ·Min(SB),7,$0
+TEXT ·Min(SB),NOSPLIT,$0
B ·min(SB)
-TEXT ·Max(SB),7,$0
+TEXT ·Max(SB),NOSPLIT,$0
B ·max(SB)
diff --git a/src/pkg/math/exp2_386.s b/src/pkg/math/exp2_386.s
index 153762631..71959d94d 100644
--- a/src/pkg/math/exp2_386.s
+++ b/src/pkg/math/exp2_386.s
@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
// func Exp2(x float64) float64
-TEXT ·Exp2(SB),7,$0
+TEXT ·Exp2(SB),NOSPLIT,$0
// test bits for not-finite
MOVL x_hi+4(FP), AX
ANDL $0x7ff00000, AX
diff --git a/src/pkg/math/exp2_amd64.s b/src/pkg/math/exp2_amd64.s
index 7bb44f78a..77a53dad4 100644
--- a/src/pkg/math/exp2_amd64.s
+++ b/src/pkg/math/exp2_amd64.s
@@ -2,5 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT ·Exp2(SB),7,$0
+#include "../../cmd/ld/textflag.h"
+
+TEXT ·Exp2(SB),NOSPLIT,$0
JMP ·exp2(SB)
diff --git a/src/pkg/math/exp2_arm.s b/src/pkg/math/exp2_arm.s
index 41b63bfaf..fe51f2522 100644
--- a/src/pkg/math/exp2_arm.s
+++ b/src/pkg/math/exp2_arm.s
@@ -2,5 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT ·Exp2(SB),7,$0
+#include "../../cmd/ld/textflag.h"
+
+TEXT ·Exp2(SB),NOSPLIT,$0
B ·exp2(SB)
diff --git a/src/pkg/math/exp_386.s b/src/pkg/math/exp_386.s
index aeceb3cad..af2d680d5 100644
--- a/src/pkg/math/exp_386.s
+++ b/src/pkg/math/exp_386.s
@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
// func Exp(x float64) float64
-TEXT ·Exp(SB),7,$0
+TEXT ·Exp(SB),NOSPLIT,$0
// test bits for not-finite
MOVL x_hi+4(FP), AX
ANDL $0x7ff00000, AX
diff --git a/src/pkg/math/exp_amd64.s b/src/pkg/math/exp_amd64.s
index eb6fb0432..070b45264 100644
--- a/src/pkg/math/exp_amd64.s
+++ b/src/pkg/math/exp_amd64.s
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
// The method is based on a paper by Naoki Shibata: "Efficient evaluation
// methods of elementary functions suitable for SIMD computation", Proc.
// of International Supercomputing Conference 2010 (ISC'10), pp. 25 -- 32
@@ -31,7 +33,7 @@
#define NegInf 0xFFF0000000000000
// func Exp(x float64) float64
-TEXT ·Exp(SB),7,$0
+TEXT ·Exp(SB),NOSPLIT,$0
// test bits for not-finite
MOVQ x+0(FP), BX
MOVQ $~(1<<63), AX // sign bit mask
diff --git a/src/pkg/math/exp_arm.s b/src/pkg/math/exp_arm.s
index a95fa9342..130b502ac 100644
--- a/src/pkg/math/exp_arm.s
+++ b/src/pkg/math/exp_arm.s
@@ -2,5 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT ·Exp(SB),7,$0
+#include "../../cmd/ld/textflag.h"
+
+TEXT ·Exp(SB),NOSPLIT,$0
B ·exp(SB)
diff --git a/src/pkg/math/expm1_386.s b/src/pkg/math/expm1_386.s
index 0ff9c4ab0..b268c58c6 100644
--- a/src/pkg/math/expm1_386.s
+++ b/src/pkg/math/expm1_386.s
@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
// func Expm1(x float64) float64
-TEXT ·Expm1(SB),7,$0
+TEXT ·Expm1(SB),NOSPLIT,$0
FLDLN2 // F0=log(2) = 1/log2(e) ~ 0.693147
FMOVD x+0(FP), F0 // F0=x, F1=1/log2(e)
FABS // F0=|x|, F1=1/log2(e)
diff --git a/src/pkg/math/expm1_amd64.s b/src/pkg/math/expm1_amd64.s
index a3b09e2f6..66a75b3d5 100644
--- a/src/pkg/math/expm1_amd64.s
+++ b/src/pkg/math/expm1_amd64.s
@@ -2,5 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT ·Expm1(SB),7,$0
+#include "../../cmd/ld/textflag.h"
+
+TEXT ·Expm1(SB),NOSPLIT,$0
JMP ·expm1(SB)
diff --git a/src/pkg/math/expm1_arm.s b/src/pkg/math/expm1_arm.s
index e4e40441b..838744447 100644
--- a/src/pkg/math/expm1_arm.s
+++ b/src/pkg/math/expm1_arm.s
@@ -2,5 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT ·Expm1(SB),7,$0
+#include "../../cmd/ld/textflag.h"
+
+TEXT ·Expm1(SB),NOSPLIT,$0
B ·expm1(SB)
diff --git a/src/pkg/math/floor_386.s b/src/pkg/math/floor_386.s
index 9aa71c043..37d5a4559 100644
--- a/src/pkg/math/floor_386.s
+++ b/src/pkg/math/floor_386.s
@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
// func Ceil(x float64) float64
-TEXT ·Ceil(SB),7,$0
+TEXT ·Ceil(SB),NOSPLIT,$0
FMOVD x+0(FP), F0 // F0=x
FSTCW -2(SP) // save old Control Word
MOVW -2(SP), AX
@@ -17,7 +19,7 @@ TEXT ·Ceil(SB),7,$0
RET
// func Floor(x float64) float64
-TEXT ·Floor(SB),7,$0
+TEXT ·Floor(SB),NOSPLIT,$0
FMOVD x+0(FP), F0 // F0=x
FSTCW -2(SP) // save old Control Word
MOVW -2(SP), AX
@@ -31,7 +33,7 @@ TEXT ·Floor(SB),7,$0
RET
// func Trunc(x float64) float64
-TEXT ·Trunc(SB),7,$0
+TEXT ·Trunc(SB),NOSPLIT,$0
FMOVD x+0(FP), F0 // F0=x
FSTCW -2(SP) // save old Control Word
MOVW -2(SP), AX
diff --git a/src/pkg/math/floor_amd64.s b/src/pkg/math/floor_amd64.s
index bb1a2fd22..2fd31c4fa 100644
--- a/src/pkg/math/floor_amd64.s
+++ b/src/pkg/math/floor_amd64.s
@@ -2,10 +2,12 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
#define Big 0x4330000000000000 // 2**52
// func Floor(x float64) float64
-TEXT ·Floor(SB),7,$0
+TEXT ·Floor(SB),NOSPLIT,$0
MOVQ x+0(FP), AX
MOVQ $~(1<<63), DX // sign bit mask
ANDQ AX,DX // DX = |x|
@@ -27,7 +29,7 @@ isBig_floor:
RET
// func Ceil(x float64) float64
-TEXT ·Ceil(SB),7,$0
+TEXT ·Ceil(SB),NOSPLIT,$0
MOVQ x+0(FP), AX
MOVQ $~(1<<63), DX // sign bit mask
MOVQ AX, BX // BX = copy of x
@@ -53,7 +55,7 @@ isBig_ceil:
RET
// func Trunc(x float64) float64
-TEXT ·Trunc(SB),7,$0
+TEXT ·Trunc(SB),NOSPLIT,$0
MOVQ x+0(FP), AX
MOVQ $~(1<<63), DX // sign bit mask
MOVQ AX, BX // BX = copy of x
diff --git a/src/pkg/math/floor_arm.s b/src/pkg/math/floor_arm.s
index e3ae53f52..cb3b98e95 100644
--- a/src/pkg/math/floor_arm.s
+++ b/src/pkg/math/floor_arm.s
@@ -2,11 +2,13 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT ·Floor(SB),7,$0
+#include "../../cmd/ld/textflag.h"
+
+TEXT ·Floor(SB),NOSPLIT,$0
B ·floor(SB)
-TEXT ·Ceil(SB),7,$0
+TEXT ·Ceil(SB),NOSPLIT,$0
B ·ceil(SB)
-TEXT ·Trunc(SB),7,$0
+TEXT ·Trunc(SB),NOSPLIT,$0
B ·trunc(SB)
diff --git a/src/pkg/math/fltasm_amd64.s b/src/pkg/math/fltasm_amd64.s
deleted file mode 100644
index 66442cd30..000000000
--- a/src/pkg/math/fltasm_amd64.s
+++ /dev/null
@@ -1,67 +0,0 @@
-// Derived from Inferno's libkern/getfcr-amd64.s
-// http://code.google.com/p/inferno-os/source/browse/libkern/getfcr-amd64.s
-//
-// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
-// Revisions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com). All rights reserved.
-// Portions Copyright 2009 The Go Authors. All rights reserved.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-TEXT ·SetFPControl(SB), 7, $8
- // Set new
- MOVL p+0(FP), DI
- XORL $(0x3F<<7), DI
- ANDL $0xFFC0, DI
- WAIT
- STMXCSR 0(SP)
- MOVL 0(SP), AX
- ANDL $~0x3F, AX
- ORL DI, AX
- MOVL AX, 0(SP)
- LDMXCSR 0(SP)
- RET
-
-TEXT ·GetFPControl(SB), 7, $0
- WAIT
- STMXCSR 0(SP)
- MOVWLZX 0(SP), AX
- ANDL $0xFFC0, AX
- XORL $(0x3F<<7), AX
- MOVL AX, ret+0(FP)
- RET
-
-TEXT ·SetFPStatus(SB), $0
- MOVL p+0(FP), DI
- ANDL $0x3F, DI
- WAIT
- STMXCSR 0(SP)
- MOVL 0(SP), AX
- ANDL $~0x3F, AX
- ORL DI, AX
- MOVL AX, 0(SP)
- LDMXCSR 0(SP)
- RET
-
-TEXT ·GetFPStatus(SB), $0
- WAIT
- STMXCSR 0(SP)
- MOVL 0(SP), AX
- ANDL $0x3F, AX
- MOVL AX, ret+0(FP)
- RET
diff --git a/src/pkg/math/frexp_386.s b/src/pkg/math/frexp_386.s
index 95e50de02..c6d0a80ed 100644
--- a/src/pkg/math/frexp_386.s
+++ b/src/pkg/math/frexp_386.s
@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
// func Frexp(f float64) (frac float64, exp int)
-TEXT ·Frexp(SB),7,$0
+TEXT ·Frexp(SB),NOSPLIT,$0
FMOVD f+0(FP), F0 // F0=f
FXAM
FSTSW AX
diff --git a/src/pkg/math/frexp_amd64.s b/src/pkg/math/frexp_amd64.s
index bc52b79ab..03d101243 100644
--- a/src/pkg/math/frexp_amd64.s
+++ b/src/pkg/math/frexp_amd64.s
@@ -2,5 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT ·Frexp(SB),7,$0
+#include "../../cmd/ld/textflag.h"
+
+TEXT ·Frexp(SB),NOSPLIT,$0
JMP ·frexp(SB)
diff --git a/src/pkg/math/frexp_arm.s b/src/pkg/math/frexp_arm.s
index cfd5d0b52..9d40ae46a 100644
--- a/src/pkg/math/frexp_arm.s
+++ b/src/pkg/math/frexp_arm.s
@@ -2,5 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT ·Frexp(SB),7,$0
+#include "../../cmd/ld/textflag.h"
+
+TEXT ·Frexp(SB),NOSPLIT,$0
B ·frexp(SB)
diff --git a/src/pkg/math/hypot_386.s b/src/pkg/math/hypot_386.s
index 8edfe064f..eec1bf554 100644
--- a/src/pkg/math/hypot_386.s
+++ b/src/pkg/math/hypot_386.s
@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
// func Hypot(p, q float64) float64
-TEXT ·Hypot(SB),7,$0
+TEXT ·Hypot(SB),NOSPLIT,$0
// test bits for not-finite
MOVL p_hi+4(FP), AX // high word p
ANDL $0x7ff00000, AX
diff --git a/src/pkg/math/hypot_amd64.s b/src/pkg/math/hypot_amd64.s
index 40ba6f41d..5c0ff4dd8 100644
--- a/src/pkg/math/hypot_amd64.s
+++ b/src/pkg/math/hypot_amd64.s
@@ -2,11 +2,13 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
#define PosInf 0x7FF0000000000000
#define NaN 0x7FF8000000000001
// func Hypot(p, q float64) float64
-TEXT ·Hypot(SB),7,$0
+TEXT ·Hypot(SB),NOSPLIT,$0
// test bits for special cases
MOVQ p+0(FP), BX
MOVQ $~(1<<63), AX
diff --git a/src/pkg/math/hypot_arm.s b/src/pkg/math/hypot_arm.s
index 2c599fd55..2562aa830 100644
--- a/src/pkg/math/hypot_arm.s
+++ b/src/pkg/math/hypot_arm.s
@@ -2,5 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT ·Hypot(SB),7,$0
+#include "../../cmd/ld/textflag.h"
+
+TEXT ·Hypot(SB),NOSPLIT,$0
B ·hypot(SB)
diff --git a/src/pkg/math/ldexp_386.s b/src/pkg/math/ldexp_386.s
index 566245dc2..baf377ead 100644
--- a/src/pkg/math/ldexp_386.s
+++ b/src/pkg/math/ldexp_386.s
@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
// func Ldexp(frac float64, exp int) float64
-TEXT ·Ldexp(SB),7,$0
+TEXT ·Ldexp(SB),NOSPLIT,$0
FMOVL exp+8(FP), F0 // F0=exp
FMOVD frac+0(FP), F0 // F0=frac, F1=e
FSCALE // F0=x*2**e, F1=e
diff --git a/src/pkg/math/ldexp_amd64.s b/src/pkg/math/ldexp_amd64.s
index a8d458322..c7fc226ef 100644
--- a/src/pkg/math/ldexp_amd64.s
+++ b/src/pkg/math/ldexp_amd64.s
@@ -2,5 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT ·Ldexp(SB),7,$0
+#include "../../cmd/ld/textflag.h"
+
+TEXT ·Ldexp(SB),NOSPLIT,$0
JMP ·ldexp(SB)
diff --git a/src/pkg/math/ldexp_arm.s b/src/pkg/math/ldexp_arm.s
index 3c42f515e..16744ea57 100644
--- a/src/pkg/math/ldexp_arm.s
+++ b/src/pkg/math/ldexp_arm.s
@@ -2,5 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT ·Ldexp(SB),7,$0
+#include "../../cmd/ld/textflag.h"
+
+TEXT ·Ldexp(SB),NOSPLIT,$0
B ·ldexp(SB)
diff --git a/src/pkg/math/log10_386.s b/src/pkg/math/log10_386.s
index d4f94235e..4ae069da6 100644
--- a/src/pkg/math/log10_386.s
+++ b/src/pkg/math/log10_386.s
@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
// func Log10(x float64) float64
-TEXT ·Log10(SB),7,$0
+TEXT ·Log10(SB),NOSPLIT,$0
FLDLG2 // F0=log10(2)
FMOVD x+0(FP), F0 // F0=x, F1=log10(2)
FYL2X // F0=log10(x)=log2(x)*log10(2)
@@ -11,7 +13,7 @@ TEXT ·Log10(SB),7,$0
RET
// func Log2(x float64) float64
-TEXT ·Log2(SB),7,$0
+TEXT ·Log2(SB),NOSPLIT,$0
FLD1 // F0=1
FMOVD x+0(FP), F0 // F0=x, F1=1
FYL2X // F0=log2(x)
diff --git a/src/pkg/math/log10_amd64.s b/src/pkg/math/log10_amd64.s
index 86a3b0577..b9ae83268 100644
--- a/src/pkg/math/log10_amd64.s
+++ b/src/pkg/math/log10_amd64.s
@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT ·Log10(SB),7,$0
+#include "../../cmd/ld/textflag.h"
+
+TEXT ·Log10(SB),NOSPLIT,$0
JMP ·log10(SB)
-TEXT ·Log2(SB),7,$0
+TEXT ·Log2(SB),NOSPLIT,$0
JMP ·log2(SB)
diff --git a/src/pkg/math/log10_arm.s b/src/pkg/math/log10_arm.s
index 619b0fe1e..fa7580f6f 100644
--- a/src/pkg/math/log10_arm.s
+++ b/src/pkg/math/log10_arm.s
@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT ·Log10(SB),7,$0
+#include "../../cmd/ld/textflag.h"
+
+TEXT ·Log10(SB),NOSPLIT,$0
B ·log10(SB)
-TEXT ·Log2(SB),7,$0
+TEXT ·Log2(SB),NOSPLIT,$0
B ·log2(SB)
diff --git a/src/pkg/math/log1p_386.s b/src/pkg/math/log1p_386.s
index 30dc8033d..3b30fd5b7 100644
--- a/src/pkg/math/log1p_386.s
+++ b/src/pkg/math/log1p_386.s
@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
// func Log1p(x float64) float64
-TEXT ·Log1p(SB),7,$0
+TEXT ·Log1p(SB),NOSPLIT,$0
FMOVD $(2.928932188134524e-01), F0
FMOVD x+0(FP), F0 // F0=x, F1=1-sqrt(2)/2 = 0.29289321881345247559915564
FABS // F0=|x|, F1=1-sqrt(2)/2
diff --git a/src/pkg/math/log1p_amd64.s b/src/pkg/math/log1p_amd64.s
index 65c93adad..48c24f41f 100644
--- a/src/pkg/math/log1p_amd64.s
+++ b/src/pkg/math/log1p_amd64.s
@@ -2,5 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT ·Log1p(SB),7,$0
+#include "../../cmd/ld/textflag.h"
+
+TEXT ·Log1p(SB),NOSPLIT,$0
JMP ·log1p(SB)
diff --git a/src/pkg/math/log1p_arm.s b/src/pkg/math/log1p_arm.s
index 0e599aaff..fd2740d0d 100644
--- a/src/pkg/math/log1p_arm.s
+++ b/src/pkg/math/log1p_arm.s
@@ -2,5 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT ·Log1p(SB),7,$0
+#include "../../cmd/ld/textflag.h"
+
+TEXT ·Log1p(SB),NOSPLIT,$0
B ·log1p(SB)
diff --git a/src/pkg/math/log_386.s b/src/pkg/math/log_386.s
index 7a6f2c052..21a0633b1 100644
--- a/src/pkg/math/log_386.s
+++ b/src/pkg/math/log_386.s
@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
// func Log(x float64) float64
-TEXT ·Log(SB),7,$0
+TEXT ·Log(SB),NOSPLIT,$0
FLDLN2 // F0=log(2)
FMOVD x+0(FP), F0 // F0=x, F1=log(2)
FYL2X // F0=log(x)=log2(x)*log(2)
diff --git a/src/pkg/math/log_amd64.s b/src/pkg/math/log_amd64.s
index 6ae5fbc95..dffc5aec8 100644
--- a/src/pkg/math/log_amd64.s
+++ b/src/pkg/math/log_amd64.s
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
#define HSqrt2 7.07106781186547524401e-01 // sqrt(2)/2
#define Ln2Hi 6.93147180369123816490e-01 // 0x3fe62e42fee00000
#define Ln2Lo 1.90821492927058770002e-10 // 0x3dea39ef35793c76
@@ -17,7 +19,7 @@
#define PosInf 0x7FF0000000000000
// func Log(x float64) float64
-TEXT ·Log(SB),7,$0
+TEXT ·Log(SB),NOSPLIT,$0
// test bits for special cases
MOVQ x+0(FP), BX
MOVQ $~(1<<63), AX // sign bit mask
diff --git a/src/pkg/math/log_arm.s b/src/pkg/math/log_arm.s
index 3dce1e9d4..28448aeef 100644
--- a/src/pkg/math/log_arm.s
+++ b/src/pkg/math/log_arm.s
@@ -2,5 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT ·Log(SB),7,$0
+#include "../../cmd/ld/textflag.h"
+
+TEXT ·Log(SB),NOSPLIT,$0
B ·log(SB)
diff --git a/src/pkg/math/mod_386.s b/src/pkg/math/mod_386.s
index bcb451b5d..9b3b6bd06 100644
--- a/src/pkg/math/mod_386.s
+++ b/src/pkg/math/mod_386.s
@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
// func Mod(x, y float64) float64
-TEXT ·Mod(SB),7,$0
+TEXT ·Mod(SB),NOSPLIT,$0
FMOVD y+8(FP), F0 // F0=y
FMOVD x+0(FP), F0 // F0=x, F1=y
FPREM // F0=reduced_x, F1=y
diff --git a/src/pkg/math/mod_amd64.s b/src/pkg/math/mod_amd64.s
index 33b86be40..bef83fcf7 100644
--- a/src/pkg/math/mod_amd64.s
+++ b/src/pkg/math/mod_amd64.s
@@ -2,5 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT ·Mod(SB),7,$0
+#include "../../cmd/ld/textflag.h"
+
+TEXT ·Mod(SB),NOSPLIT,$0
JMP ·mod(SB)
diff --git a/src/pkg/math/mod_arm.s b/src/pkg/math/mod_arm.s
index 47c564bf1..1f51588f8 100644
--- a/src/pkg/math/mod_arm.s
+++ b/src/pkg/math/mod_arm.s
@@ -2,5 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT ·Mod(SB),7,$0
+#include "../../cmd/ld/textflag.h"
+
+TEXT ·Mod(SB),NOSPLIT,$0
B ·mod(SB)
diff --git a/src/pkg/math/modf_386.s b/src/pkg/math/modf_386.s
index f5dc415c3..07a0dc5cf 100644
--- a/src/pkg/math/modf_386.s
+++ b/src/pkg/math/modf_386.s
@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
// func Modf(f float64) (int float64, frac float64)
-TEXT ·Modf(SB),7,$0
+TEXT ·Modf(SB),NOSPLIT,$0
FMOVD f+0(FP), F0 // F0=f
FMOVD F0, F1 // F0=f, F1=f
FSTCW -2(SP) // save old Control Word
diff --git a/src/pkg/math/modf_amd64.s b/src/pkg/math/modf_amd64.s
index 2a6d7ea04..05feb4bf9 100644
--- a/src/pkg/math/modf_amd64.s
+++ b/src/pkg/math/modf_amd64.s
@@ -2,5 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT ·Modf(SB),7,$0
+#include "../../cmd/ld/textflag.h"
+
+TEXT ·Modf(SB),NOSPLIT,$0
JMP ·modf(SB)
diff --git a/src/pkg/math/modf_arm.s b/src/pkg/math/modf_arm.s
index 6cef18734..e6bd26d53 100644
--- a/src/pkg/math/modf_arm.s
+++ b/src/pkg/math/modf_arm.s
@@ -2,5 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT ·Modf(SB),7,$0
+#include "../../cmd/ld/textflag.h"
+
+TEXT ·Modf(SB),NOSPLIT,$0
B ·modf(SB)
diff --git a/src/pkg/math/rand/example_test.go b/src/pkg/math/rand/example_test.go
index 4fe207d85..f42991453 100644
--- a/src/pkg/math/rand/example_test.go
+++ b/src/pkg/math/rand/example_test.go
@@ -11,12 +11,40 @@ import (
"text/tabwriter"
)
-// This test serves as an example but also makes sure we don't change
+// These tests serve as an example but also make sure we don't change
// the output of the random number generator when given a fixed seed.
+func Example() {
+ rand.Seed(42) // Try changing this number!
+ answers := []string{
+ "It is certain",
+ "It is decidedly so",
+ "Without a doubt",
+ "Yes definitely",
+ "You may rely on it",
+ "As I see it yes",
+ "Most likely",
+ "Outlook good",
+ "Yes",
+ "Signs point to yes",
+ "Reply hazy try again",
+ "Ask again later",
+ "Better not tell you now",
+ "Cannot predict now",
+ "Concentrate and ask again",
+ "Don't count on it",
+ "My reply is no",
+ "My sources say no",
+ "Outlook not so good",
+ "Very doubtful",
+ }
+ fmt.Println("Magic 8-Ball says:", answers[rand.Intn(len(answers))])
+ // Output: Magic 8-Ball says: As I see it yes
+}
+
// This example shows the use of each of the methods on a *Rand.
// The use of the global functions is the same, without the receiver.
-func Example() {
+func Example_rand() {
// Create and seed the generator.
// Typically a non-fixed seed should be used, such as time.Now().UnixNano().
// Using a fixed seed will produce the same output on every run.
diff --git a/src/pkg/math/rand/rand.go b/src/pkg/math/rand/rand.go
index 94f84a85f..2157cdb46 100644
--- a/src/pkg/math/rand/rand.go
+++ b/src/pkg/math/rand/rand.go
@@ -3,6 +3,12 @@
// license that can be found in the LICENSE file.
// Package rand implements pseudo-random number generators.
+//
+// Random numbers are generated by a Source. Top-level functions, such as
+// Float64 and Int, use a default shared Source that produces a deterministic
+// sequence of values each time a program is run. Use the Seed function to
+// initialize the default Source if different behavior is required for each run.
+// The default Source is safe for concurrent use by multiple goroutines.
package rand
import "sync"
@@ -113,47 +119,57 @@ func (r *Rand) Perm(n int) []int {
var globalRand = New(&lockedSource{src: NewSource(1)})
-// Seed uses the provided seed value to initialize the generator to a
+// Seed uses the provided seed value to initialize the default Source to a
// deterministic state. If Seed is not called, the generator behaves as
// if seeded by Seed(1).
func Seed(seed int64) { globalRand.Seed(seed) }
-// Int63 returns a non-negative pseudo-random 63-bit integer as an int64.
+// Int63 returns a non-negative pseudo-random 63-bit integer as an int64
+// from the default Source.
func Int63() int64 { return globalRand.Int63() }
-// Uint32 returns a pseudo-random 32-bit value as a uint32.
+// Uint32 returns a pseudo-random 32-bit value as a uint32
+// from the default Source.
func Uint32() uint32 { return globalRand.Uint32() }
-// Int31 returns a non-negative pseudo-random 31-bit integer as an int32.
+// Int31 returns a non-negative pseudo-random 31-bit integer as an int32
+// from the default Source.
func Int31() int32 { return globalRand.Int31() }
-// Int returns a non-negative pseudo-random int.
+// Int returns a non-negative pseudo-random int from the default Source.
func Int() int { return globalRand.Int() }
-// Int63n returns, as an int64, a non-negative pseudo-random number in [0,n).
+// Int63n returns, as an int64, a non-negative pseudo-random number in [0,n)
+// from the default Source.
// It panics if n <= 0.
func Int63n(n int64) int64 { return globalRand.Int63n(n) }
-// Int31n returns, as an int32, a non-negative pseudo-random number in [0,n).
+// Int31n returns, as an int32, a non-negative pseudo-random number in [0,n)
+// from the default Source.
// It panics if n <= 0.
func Int31n(n int32) int32 { return globalRand.Int31n(n) }
-// Intn returns, as an int, a non-negative pseudo-random number in [0,n).
+// Intn returns, as an int, a non-negative pseudo-random number in [0,n)
+// from the default Source.
// It panics if n <= 0.
func Intn(n int) int { return globalRand.Intn(n) }
-// Float64 returns, as a float64, a pseudo-random number in [0.0,1.0).
+// Float64 returns, as a float64, a pseudo-random number in [0.0,1.0)
+// from the default Source.
func Float64() float64 { return globalRand.Float64() }
-// Float32 returns, as a float32, a pseudo-random number in [0.0,1.0).
+// Float32 returns, as a float32, a pseudo-random number in [0.0,1.0)
+// from the default Source.
func Float32() float32 { return globalRand.Float32() }
-// Perm returns, as a slice of n ints, a pseudo-random permutation of the integers [0,n).
+// Perm returns, as a slice of n ints, a pseudo-random permutation of the integers [0,n)
+// from the default Source.
func Perm(n int) []int { return globalRand.Perm(n) }
// NormFloat64 returns a normally distributed float64 in the range
// [-math.MaxFloat64, +math.MaxFloat64] with
-// standard normal distribution (mean = 0, stddev = 1).
+// standard normal distribution (mean = 0, stddev = 1)
+// from the default Source.
// To produce a different normal distribution, callers can
// adjust the output using:
//
@@ -163,7 +179,7 @@ func NormFloat64() float64 { return globalRand.NormFloat64() }
// ExpFloat64 returns an exponentially distributed float64 in the range
// (0, +math.MaxFloat64] with an exponential distribution whose rate parameter
-// (lambda) is 1 and whose mean is 1/lambda (1).
+// (lambda) is 1 and whose mean is 1/lambda (1) from the default Source.
// To produce a distribution with a different rate parameter,
// callers can adjust the output using:
//
diff --git a/src/pkg/math/remainder_386.s b/src/pkg/math/remainder_386.s
index 2238aba49..bbe13a0d9 100644
--- a/src/pkg/math/remainder_386.s
+++ b/src/pkg/math/remainder_386.s
@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
// func Remainder(x, y float64) float64
-TEXT ·Remainder(SB),7,$0
+TEXT ·Remainder(SB),NOSPLIT,$0
FMOVD y+8(FP), F0 // F0=y
FMOVD x+0(FP), F0 // F0=x, F1=y
FPREM1 // F0=reduced_x, F1=y
diff --git a/src/pkg/math/remainder_amd64.s b/src/pkg/math/remainder_amd64.s
index f04bd3de7..e5e23c7ce 100644
--- a/src/pkg/math/remainder_amd64.s
+++ b/src/pkg/math/remainder_amd64.s
@@ -2,5 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT ·Remainder(SB),7,$0
+#include "../../cmd/ld/textflag.h"
+
+TEXT ·Remainder(SB),NOSPLIT,$0
JMP ·remainder(SB)
diff --git a/src/pkg/math/remainder_arm.s b/src/pkg/math/remainder_arm.s
index 642af6a85..8728afe93 100644
--- a/src/pkg/math/remainder_arm.s
+++ b/src/pkg/math/remainder_arm.s
@@ -2,5 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT ·Remainder(SB),7,$0
+#include "../../cmd/ld/textflag.h"
+
+TEXT ·Remainder(SB),NOSPLIT,$0
B ·remainder(SB)
diff --git a/src/pkg/math/sin.go b/src/pkg/math/sin.go
index 8beb8bbe3..ed85f21be 100644
--- a/src/pkg/math/sin.go
+++ b/src/pkg/math/sin.go
@@ -109,7 +109,7 @@ var _cos = [...]float64{
4.16666666666665929218E-2, // 0x3fa555555555554b
}
-// Cos returns the cosine of x.
+// Cos returns the cosine of the radian argument x.
//
// Special cases are:
// Cos(±Inf) = NaN
@@ -165,7 +165,7 @@ func cos(x float64) float64 {
return y
}
-// Sin returns the sine of x.
+// Sin returns the sine of the radian argument x.
//
// Special cases are:
// Sin(±0) = ±0
diff --git a/src/pkg/math/sin_386.s b/src/pkg/math/sin_386.s
index b2a836eb1..09271c035 100644
--- a/src/pkg/math/sin_386.s
+++ b/src/pkg/math/sin_386.s
@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
// func Cos(x float64) float64
-TEXT ·Cos(SB),7,$0
+TEXT ·Cos(SB),NOSPLIT,$0
FMOVD x+0(FP), F0 // F0=x
FCOS // F0=cos(x) if -2**63 < x < 2**63
FSTSW AX // AX=status word
@@ -24,7 +26,7 @@ TEXT ·Cos(SB),7,$0
RET
// func Sin(x float64) float64
-TEXT ·Sin(SB),7,$0
+TEXT ·Sin(SB),NOSPLIT,$0
FMOVD x+0(FP), F0 // F0=x
FSIN // F0=sin(x) if -2**63 < x < 2**63
FSTSW AX // AX=status word
diff --git a/src/pkg/math/sin_amd64.s b/src/pkg/math/sin_amd64.s
index c9c99e5b3..008bf4be5 100644
--- a/src/pkg/math/sin_amd64.s
+++ b/src/pkg/math/sin_amd64.s
@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT ·Sin(SB),7,$0
+#include "../../cmd/ld/textflag.h"
+
+TEXT ·Sin(SB),NOSPLIT,$0
JMP ·sin(SB)
-TEXT ·Cos(SB),7,$0
+TEXT ·Cos(SB),NOSPLIT,$0
JMP ·cos(SB)
diff --git a/src/pkg/math/sin_arm.s b/src/pkg/math/sin_arm.s
index 9447ca2eb..a057b4fc9 100644
--- a/src/pkg/math/sin_arm.s
+++ b/src/pkg/math/sin_arm.s
@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT ·Sin(SB),7,$0
+#include "../../cmd/ld/textflag.h"
+
+TEXT ·Sin(SB),NOSPLIT,$0
B ·sin(SB)
-TEXT ·Cos(SB),7,$0
+TEXT ·Cos(SB),NOSPLIT,$0
B ·cos(SB)
diff --git a/src/pkg/math/sincos_386.s b/src/pkg/math/sincos_386.s
index 8f5e0f8d1..bf964b168 100644
--- a/src/pkg/math/sincos_386.s
+++ b/src/pkg/math/sincos_386.s
@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
// func Sincos(x float64) (sin, cos float64)
-TEXT ·Sincos(SB),7,$0
+TEXT ·Sincos(SB),NOSPLIT,$0
FMOVD x+0(FP), F0 // F0=x
FSINCOS // F0=cos(x), F1=sin(x) if -2**63 < x < 2**63
FSTSW AX // AX=status word
diff --git a/src/pkg/math/sincos_amd64.s b/src/pkg/math/sincos_amd64.s
index c9dea0916..bccc1ade1 100644
--- a/src/pkg/math/sincos_amd64.s
+++ b/src/pkg/math/sincos_amd64.s
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
// The method is based on a paper by Naoki Shibata: "Efficient evaluation
// methods of elementary functions suitable for SIMD computation", Proc.
// of International Supercomputing Conference 2010 (ISC'10), pp. 25 -- 32
@@ -31,7 +33,7 @@
#define T4 5.51146384479717813051146e-07 // (+1.0/1814400)
// func Sincos(d float64) (sin, cos float64)
-TEXT ·Sincos(SB),7,$0
+TEXT ·Sincos(SB),NOSPLIT,$0
// test for special cases
MOVQ $~(1<<63), DX // sign bit mask
MOVQ x+0(FP), BX
diff --git a/src/pkg/math/sincos_arm.s b/src/pkg/math/sincos_arm.s
index 3e2b0e4e0..b6866af54 100644
--- a/src/pkg/math/sincos_arm.s
+++ b/src/pkg/math/sincos_arm.s
@@ -2,5 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT ·Sincos(SB),7,$0
+#include "../../cmd/ld/textflag.h"
+
+TEXT ·Sincos(SB),NOSPLIT,$0
B ·sincos(SB)
diff --git a/src/pkg/math/sqrt_386.s b/src/pkg/math/sqrt_386.s
index 824fa634c..2d0c786d0 100644
--- a/src/pkg/math/sqrt_386.s
+++ b/src/pkg/math/sqrt_386.s
@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
// func Sqrt(x float64) float64
-TEXT ·Sqrt(SB),7,$0
+TEXT ·Sqrt(SB),NOSPLIT,$0
FMOVD x+0(FP),F0
FSQRT
FMOVDP F0,ret+8(FP)
diff --git a/src/pkg/math/sqrt_amd64.s b/src/pkg/math/sqrt_amd64.s
index 553c4e01b..1508944c9 100644
--- a/src/pkg/math/sqrt_amd64.s
+++ b/src/pkg/math/sqrt_amd64.s
@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
// func Sqrt(x float64) float64
-TEXT ·Sqrt(SB),7,$0
+TEXT ·Sqrt(SB),NOSPLIT,$0
SQRTSD x+0(FP), X0
MOVSD X0, ret+8(FP)
RET
diff --git a/src/pkg/math/sqrt_arm.s b/src/pkg/math/sqrt_arm.s
index b965b4845..f731ee976 100644
--- a/src/pkg/math/sqrt_arm.s
+++ b/src/pkg/math/sqrt_arm.s
@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
// func Sqrt(x float64) float64
-TEXT ·Sqrt(SB),7,$0
+TEXT ·Sqrt(SB),NOSPLIT,$0
MOVD x+0(FP),F0
SQRTD F0,F0
MOVD F0,ret+8(FP)
diff --git a/src/pkg/math/tan.go b/src/pkg/math/tan.go
index b2f29cc3b..285eff1ab 100644
--- a/src/pkg/math/tan.go
+++ b/src/pkg/math/tan.go
@@ -73,7 +73,7 @@ var _tanQ = [...]float64{
-5.38695755929454629881E7, //0xc189afe03cbe5a31
}
-// Tan returns the tangent of x.
+// Tan returns the tangent of the radian argument x.
//
// Special cases are:
// Tan(±0) = ±0
diff --git a/src/pkg/math/tan_386.s b/src/pkg/math/tan_386.s
index f3ad33907..2320326e3 100644
--- a/src/pkg/math/tan_386.s
+++ b/src/pkg/math/tan_386.s
@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
// func Tan(x float64) float64
-TEXT ·Tan(SB),7,$0
+TEXT ·Tan(SB),NOSPLIT,$0
FMOVD x+0(FP), F0 // F0=x
FPTAN // F0=1, F1=tan(x) if -2**63 < x < 2**63
FSTSW AX // AX=status word
diff --git a/src/pkg/math/tan_amd64.s b/src/pkg/math/tan_amd64.s
index 823ceb254..9fa5f148e 100644
--- a/src/pkg/math/tan_amd64.s
+++ b/src/pkg/math/tan_amd64.s
@@ -2,5 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT ·Tan(SB),7,$0
+#include "../../cmd/ld/textflag.h"
+
+TEXT ·Tan(SB),NOSPLIT,$0
JMP ·tan(SB)
diff --git a/src/pkg/math/tan_arm.s b/src/pkg/math/tan_arm.s
index 4be35c38b..68fea318d 100644
--- a/src/pkg/math/tan_arm.s
+++ b/src/pkg/math/tan_arm.s
@@ -2,5 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT ·Tan(SB),7,$0
+#include "../../cmd/ld/textflag.h"
+
+TEXT ·Tan(SB),NOSPLIT,$0
B ·tan(SB)
diff --git a/src/pkg/mime/grammar.go b/src/pkg/mime/grammar.go
index 09e941e3e..2347324aa 100644
--- a/src/pkg/mime/grammar.go
+++ b/src/pkg/mime/grammar.go
@@ -30,16 +30,3 @@ func isToken(s string) bool {
}
return strings.IndexFunc(s, isNotTokenChar) < 0
}
-
-// isQText returns true if rune is in 'qtext' as defined by RFC 822.
-func isQText(r int) bool {
- // CHAR = <any ASCII character> ; ( 0-177, 0.-127.)
- // qtext = <any CHAR excepting <">, ; => may be folded
- // "\" & CR, and including
- // linear-white-space>
- switch r {
- case '"', '\\', '\r':
- return false
- }
- return r < 0x80
-}
diff --git a/src/pkg/mime/mediatype.go b/src/pkg/mime/mediatype.go
index 8396c0a15..608f759da 100644
--- a/src/pkg/mime/mediatype.go
+++ b/src/pkg/mime/mediatype.go
@@ -47,7 +47,7 @@ func FormatMediaType(t string, param map[string]string) string {
b.WriteByte('"')
offset := 0
for index, character := range value {
- if character == '"' || character == '\r' {
+ if character == '"' || character == '\\' {
b.WriteString(value[offset:index])
offset = index
b.WriteByte('\\')
diff --git a/src/pkg/mime/mediatype_test.go b/src/pkg/mime/mediatype_test.go
index e41ead237..29511445b 100644
--- a/src/pkg/mime/mediatype_test.go
+++ b/src/pkg/mime/mediatype_test.go
@@ -282,8 +282,17 @@ type formatTest struct {
var formatTests = []formatTest{
{"noslash", nil, ""},
+ {"foo bar/baz", nil, ""},
+ {"foo/bar baz", nil, ""},
{"foo/BAR", nil, "foo/bar"},
{"foo/BAR", map[string]string{"X": "Y"}, "foo/bar; x=Y"},
+ {"foo/BAR", map[string]string{"space": "With space"}, `foo/bar; space="With space"`},
+ {"foo/BAR", map[string]string{"quote": `With "quote`}, `foo/bar; quote="With \"quote"`},
+ {"foo/BAR", map[string]string{"bslash": `With \backslash`}, `foo/bar; bslash="With \\backslash"`},
+ {"foo/BAR", map[string]string{"both": `With \backslash and "quote`}, `foo/bar; both="With \\backslash and \"quote"`},
+ {"foo/BAR", map[string]string{"": "empty attribute"}, ""},
+ {"foo/BAR", map[string]string{"bad attribute": "baz"}, ""},
+ {"foo/BAR", map[string]string{"nonascii": "not an ascii character: ä"}, ""},
}
func TestFormatMediaType(t *testing.T) {
diff --git a/src/pkg/mime/multipart/multipart.go b/src/pkg/mime/multipart/multipart.go
index 2c862a647..2b4f5b433 100644
--- a/src/pkg/mime/multipart/multipart.go
+++ b/src/pkg/mime/multipart/multipart.go
@@ -272,7 +272,7 @@ func (r *Reader) NextPart() (*Part, error) {
}
}
-// isFinalBoundary returns whether line is the final boundary line
+// isFinalBoundary reports whether line is the final boundary line
// indicating that all parts are over.
// It matches `^--boundary--[ \t]*(\r\n)?$`
func (mr *Reader) isFinalBoundary(line []byte) bool {
@@ -307,8 +307,8 @@ func (mr *Reader) isBoundaryDelimiterLine(line []byte) (ret bool) {
return bytes.Equal(rest, mr.nl)
}
-// peekBufferIsEmptyPart returns whether the provided peek-ahead
-// buffer represents an empty part. This is only called if we've not
+// peekBufferIsEmptyPart reports whether the provided peek-ahead
+// buffer represents an empty part. It is called only if we've not
// already read any bytes in this part and checks for the case of MIME
// software not writing the \r\n on empty parts. Some does, some
// doesn't.
diff --git a/src/pkg/mime/testdata/test.types.plan9 b/src/pkg/mime/testdata/test.types.plan9
new file mode 100644
index 000000000..19dbf41cc
--- /dev/null
+++ b/src/pkg/mime/testdata/test.types.plan9
@@ -0,0 +1,8 @@
+# Copyright 2013 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.
+
+
+ # mime package test
+.t1 application test - y # Simple test
+.t2 text test - y # Text test
diff --git a/src/pkg/mime/type_plan9.go b/src/pkg/mime/type_plan9.go
new file mode 100644
index 000000000..b8f0511ee
--- /dev/null
+++ b/src/pkg/mime/type_plan9.go
@@ -0,0 +1,53 @@
+// Copyright 2013 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 mime
+
+import (
+ "bufio"
+ "os"
+ "strings"
+)
+
+var typeFiles = []string{
+ "/sys/lib/mimetypes",
+}
+
+func loadMimeFile(filename string) {
+ f, err := os.Open(filename)
+ if err != nil {
+ return
+ }
+ defer f.Close()
+
+ scanner := bufio.NewScanner(f)
+ for scanner.Scan() {
+ fields := strings.Fields(scanner.Text())
+ if len(fields) <= 2 || fields[0][0] != '.' {
+ continue
+ }
+ if fields[1] == "-" || fields[2] == "-" {
+ continue
+ }
+ setExtensionType(fields[0], fields[1]+"/"+fields[2])
+ }
+ if err := scanner.Err(); err != nil {
+ panic(err)
+ }
+}
+
+func initMime() {
+ for _, filename := range typeFiles {
+ loadMimeFile(filename)
+ }
+}
+
+func initMimeForTests() map[string]string {
+ typeFiles = []string{"testdata/test.types.plan9"}
+ return map[string]string{
+ ".t1": "application/test",
+ ".t2": "text/test; charset=utf-8",
+ ".png": "image/png",
+ }
+}
diff --git a/src/pkg/mime/type_unix.go b/src/pkg/mime/type_unix.go
index 857a0ab67..713e301cd 100644
--- a/src/pkg/mime/type_unix.go
+++ b/src/pkg/mime/type_unix.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.
-// +build darwin freebsd linux netbsd openbsd plan9
+// +build darwin dragonfly freebsd linux netbsd openbsd
package mime
diff --git a/src/pkg/net/cgo_bsd.go b/src/pkg/net/cgo_bsd.go
index 3b38e3d83..388eab4fe 100644
--- a/src/pkg/net/cgo_bsd.go
+++ b/src/pkg/net/cgo_bsd.go
@@ -2,7 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin freebsd
+// +build !netgo
+// +build darwin dragonfly freebsd
package net
diff --git a/src/pkg/net/cgo_linux.go b/src/pkg/net/cgo_linux.go
index f6cefa89a..693aef03d 100644
--- a/src/pkg/net/cgo_linux.go
+++ b/src/pkg/net/cgo_linux.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build cgo,!netgo
+
package net
/*
diff --git a/src/pkg/net/cgo_netbsd.go b/src/pkg/net/cgo_netbsd.go
index aeaf8e568..09c5ad2d9 100644
--- a/src/pkg/net/cgo_netbsd.go
+++ b/src/pkg/net/cgo_netbsd.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build cgo,!netgo
+
package net
/*
diff --git a/src/pkg/net/cgo_openbsd.go b/src/pkg/net/cgo_openbsd.go
index aeaf8e568..09c5ad2d9 100644
--- a/src/pkg/net/cgo_openbsd.go
+++ b/src/pkg/net/cgo_openbsd.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build cgo,!netgo
+
package net
/*
diff --git a/src/pkg/net/cgo_stub.go b/src/pkg/net/cgo_stub.go
index 52e57d740..f533c1421 100644
--- a/src/pkg/net/cgo_stub.go
+++ b/src/pkg/net/cgo_stub.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.
-// +build !cgo
+// +build !cgo netgo
// Stub cgo routines for systems that do not use cgo to do network lookups.
diff --git a/src/pkg/net/cgo_unix.go b/src/pkg/net/cgo_unix.go
index 7476140eb..1f366ee5c 100644
--- a/src/pkg/net/cgo_unix.go
+++ b/src/pkg/net/cgo_unix.go
@@ -2,7 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin freebsd linux netbsd openbsd
+// +build !netgo
+// +build darwin dragonfly freebsd linux netbsd openbsd
package net
@@ -31,6 +32,9 @@ func cgoLookupHost(name string) (addrs []string, err error, completed bool) {
}
func cgoLookupPort(net, service string) (port int, err error, completed bool) {
+ acquireThread()
+ defer releaseThread()
+
var res *C.struct_addrinfo
var hints C.struct_addrinfo
@@ -78,10 +82,14 @@ func cgoLookupPort(net, service string) (port int, err error, completed bool) {
}
func cgoLookupIPCNAME(name string) (addrs []IP, cname string, err error, completed bool) {
+ acquireThread()
+ defer releaseThread()
+
var res *C.struct_addrinfo
var hints C.struct_addrinfo
hints.ai_flags = cgoAddrInfoFlags()
+ hints.ai_socktype = C.SOCK_STREAM
h := C.CString(name)
defer C.free(unsafe.Pointer(h))
@@ -91,6 +99,16 @@ func cgoLookupIPCNAME(name string) (addrs []IP, cname string, err error, complet
if gerrno == C.EAI_NONAME {
str = noSuchHost
} else if gerrno == C.EAI_SYSTEM {
+ if err == nil {
+ // err should not be nil, but sometimes getaddrinfo returns
+ // gerrno == C.EAI_SYSTEM with err == nil on Linux.
+ // The report claims that it happens when we have too many
+ // open files, so use syscall.EMFILE (too many open files in system).
+ // Most system calls would return ENFILE (too many open files),
+ // so at the least EMFILE should be easy to recognize if this
+ // comes up again. golang.org/issue/6232.
+ err = syscall.EMFILE
+ }
str = err.Error()
} else {
str = C.GoString(C.gai_strerror(gerrno))
@@ -108,7 +126,7 @@ func cgoLookupIPCNAME(name string) (addrs []IP, cname string, err error, complet
}
}
for r := res; r != nil; r = r.ai_next {
- // Everything comes back twice, once for UDP and once for TCP.
+ // We only asked for SOCK_STREAM, but check anyhow.
if r.ai_socktype != C.SOCK_STREAM {
continue
}
@@ -137,6 +155,9 @@ func cgoLookupCNAME(name string) (cname string, err error, completed bool) {
}
func copyIP(x IP) IP {
+ if len(x) < 16 {
+ return x.To16()
+ }
y := make(IP, len(x))
copy(y, x)
return y
diff --git a/src/pkg/net/dial.go b/src/pkg/net/dial.go
index b18d28362..6304818bf 100644
--- a/src/pkg/net/dial.go
+++ b/src/pkg/net/dial.go
@@ -37,6 +37,13 @@ type Dialer struct {
// network being dialed.
// If nil, a local address is automatically chosen.
LocalAddr Addr
+
+ // DualStack allows a single dial to attempt to establish
+ // multiple IPv4 and IPv6 connections and to return the first
+ // established connection when the network is "tcp" and the
+ // destination is a host name that has multiple address family
+ // DNS records.
+ DualStack bool
}
// Return either now+Timeout or Deadline, whichever comes first.
@@ -82,13 +89,13 @@ func parseNetwork(net string) (afnet string, proto int, err error) {
return "", 0, UnknownNetworkError(net)
}
-func resolveAddr(op, net, addr string, deadline time.Time) (Addr, error) {
+func resolveAddr(op, net, addr string, deadline time.Time) (netaddr, error) {
afnet, _, err := parseNetwork(net)
if err != nil {
- return nil, &OpError{op, net, nil, err}
+ return nil, err
}
if op == "dial" && addr == "" {
- return nil, &OpError{op, net, nil, errMissingAddress}
+ return nil, errMissingAddress
}
switch afnet {
case "unix", "unixgram", "unixpacket":
@@ -143,12 +150,74 @@ func DialTimeout(network, address string, timeout time.Duration) (Conn, error) {
// See func Dial for a description of the network and address
// parameters.
func (d *Dialer) Dial(network, address string) (Conn, error) {
- return resolveAndDial(network, address, d.LocalAddr, d.deadline())
+ ra, err := resolveAddr("dial", network, address, d.deadline())
+ if err != nil {
+ return nil, &OpError{Op: "dial", Net: network, Addr: nil, Err: err}
+ }
+ dialer := func(deadline time.Time) (Conn, error) {
+ return dialSingle(network, address, d.LocalAddr, ra.toAddr(), deadline)
+ }
+ if ras, ok := ra.(addrList); ok && d.DualStack && network == "tcp" {
+ dialer = func(deadline time.Time) (Conn, error) {
+ return dialMulti(network, address, d.LocalAddr, ras, deadline)
+ }
+ }
+ return dial(network, ra.toAddr(), dialer, d.deadline())
}
-func dial(net, addr string, la, ra Addr, deadline time.Time) (c Conn, err error) {
+// dialMulti attempts to establish connections to each destination of
+// the list of addresses. It will return the first established
+// connection and close the other connections. Otherwise it returns
+// error on the last attempt.
+func dialMulti(net, addr string, la Addr, ras addrList, deadline time.Time) (Conn, error) {
+ type racer struct {
+ Conn
+ Addr
+ error
+ }
+ // Sig controls the flow of dial results on lane. It passes a
+ // token to the next racer and also indicates the end of flow
+ // by using closed channel.
+ sig := make(chan bool, 1)
+ lane := make(chan racer, 1)
+ for _, ra := range ras {
+ go func(ra Addr) {
+ c, err := dialSingle(net, addr, la, ra, deadline)
+ if _, ok := <-sig; ok {
+ lane <- racer{c, ra, err}
+ } else if err == nil {
+ // We have to return the resources
+ // that belong to the other
+ // connections here for avoiding
+ // unnecessary resource starvation.
+ c.Close()
+ }
+ }(ra.toAddr())
+ }
+ defer close(sig)
+ var failAddr Addr
+ lastErr := errTimeout
+ nracers := len(ras)
+ for nracers > 0 {
+ sig <- true
+ select {
+ case racer := <-lane:
+ if racer.error == nil {
+ return racer.Conn, nil
+ }
+ failAddr = racer.Addr
+ lastErr = racer.error
+ nracers--
+ }
+ }
+ return nil, &OpError{Op: "dial", Net: net, Addr: failAddr, Err: lastErr}
+}
+
+// dialSingle attempts to establish and returns a single connection to
+// the destination address.
+func dialSingle(net, addr string, la, ra Addr, deadline time.Time) (c Conn, err error) {
if la != nil && la.Network() != ra.Network() {
- return nil, &OpError{"dial", net, ra, errors.New("mismatched local addr type " + la.Network())}
+ return nil, &OpError{Op: "dial", Net: net, Addr: ra, Err: errors.New("mismatched local address type " + la.Network())}
}
switch ra := ra.(type) {
case *TCPAddr:
@@ -164,21 +233,14 @@ func dial(net, addr string, la, ra Addr, deadline time.Time) (c Conn, err error)
la, _ := la.(*UnixAddr)
c, err = dialUnix(net, la, ra, deadline)
default:
- err = &OpError{"dial", net + " " + addr, ra, UnknownNetworkError(net)}
+ return nil, &OpError{Op: "dial", Net: net, Addr: ra, Err: &AddrError{Err: "unexpected address type", Addr: addr}}
}
if err != nil {
- return nil, err
+ return nil, err // c is non-nil interface containing nil pointer
}
- return
-}
-
-type stringAddr struct {
- net, addr string
+ return c, nil
}
-func (a stringAddr) Network() string { return a.net }
-func (a stringAddr) String() string { return a.addr }
-
// Listen announces on the local network address laddr.
// The network net must be a stream-oriented network: "tcp", "tcp4",
// "tcp6", "unix" or "unixpacket".
@@ -186,15 +248,21 @@ func (a stringAddr) String() string { return a.addr }
func Listen(net, laddr string) (Listener, error) {
la, err := resolveAddr("listen", net, laddr, noDeadline)
if err != nil {
- return nil, err
+ return nil, &OpError{Op: "listen", Net: net, Addr: nil, Err: err}
}
- switch la := la.(type) {
+ var l Listener
+ switch la := la.toAddr().(type) {
case *TCPAddr:
- return ListenTCP(net, la)
+ l, err = ListenTCP(net, la)
case *UnixAddr:
- return ListenUnix(net, la)
+ l, err = ListenUnix(net, la)
+ default:
+ return nil, &OpError{Op: "listen", Net: net, Addr: la, Err: &AddrError{Err: "unexpected address type", Addr: laddr}}
}
- return nil, UnknownNetworkError(net)
+ if err != nil {
+ return nil, err // l is non-nil interface containing nil pointer
+ }
+ return l, nil
}
// ListenPacket announces on the local network address laddr.
@@ -204,15 +272,21 @@ func Listen(net, laddr string) (Listener, error) {
func ListenPacket(net, laddr string) (PacketConn, error) {
la, err := resolveAddr("listen", net, laddr, noDeadline)
if err != nil {
- return nil, err
+ return nil, &OpError{Op: "listen", Net: net, Addr: nil, Err: err}
}
- switch la := la.(type) {
+ var l PacketConn
+ switch la := la.toAddr().(type) {
case *UDPAddr:
- return ListenUDP(net, la)
+ l, err = ListenUDP(net, la)
case *IPAddr:
- return ListenIP(net, la)
+ l, err = ListenIP(net, la)
case *UnixAddr:
- return ListenUnixgram(net, la)
+ l, err = ListenUnixgram(net, la)
+ default:
+ return nil, &OpError{Op: "listen", Net: net, Addr: la, Err: &AddrError{Err: "unexpected address type", Addr: laddr}}
+ }
+ if err != nil {
+ return nil, err // l is non-nil interface containing nil pointer
}
- return nil, UnknownNetworkError(net)
+ return l, nil
}
diff --git a/src/pkg/net/dial_gen.go b/src/pkg/net/dial_gen.go
index 19f868168..ada623300 100644
--- a/src/pkg/net/dial_gen.go
+++ b/src/pkg/net/dial_gen.go
@@ -12,62 +12,35 @@ import (
var testingIssue5349 bool // used during tests
-// resolveAndDialChannel is the simple pure-Go implementation of
-// resolveAndDial, still used on operating systems where the deadline
-// hasn't been pushed down into the pollserver. (Plan 9 and some old
-// versions of Windows)
-func resolveAndDialChannel(net, addr string, localAddr Addr, deadline time.Time) (Conn, error) {
+// dialChannel is the simple pure-Go implementation of dial, still
+// used on operating systems where the deadline hasn't been pushed
+// down into the pollserver. (Plan 9 and some old versions of Windows)
+func dialChannel(net string, ra Addr, dialer func(time.Time) (Conn, error), deadline time.Time) (Conn, error) {
var timeout time.Duration
if !deadline.IsZero() {
timeout = deadline.Sub(time.Now())
}
if timeout <= 0 {
- ra, err := resolveAddr("dial", net, addr, noDeadline)
- if err != nil {
- return nil, err
- }
- return dial(net, addr, localAddr, ra, noDeadline)
+ return dialer(noDeadline)
}
t := time.NewTimer(timeout)
defer t.Stop()
- type pair struct {
+ type racer struct {
Conn
error
}
- ch := make(chan pair, 1)
- resolvedAddr := make(chan Addr, 1)
+ ch := make(chan racer, 1)
go func() {
if testingIssue5349 {
time.Sleep(time.Millisecond)
}
- ra, err := resolveAddr("dial", net, addr, noDeadline)
- if err != nil {
- ch <- pair{nil, err}
- return
- }
- resolvedAddr <- ra // in case we need it for OpError
- c, err := dial(net, addr, localAddr, ra, noDeadline)
- ch <- pair{c, err}
+ c, err := dialer(noDeadline)
+ ch <- racer{c, err}
}()
select {
case <-t.C:
- // Try to use the real Addr in our OpError, if we resolved it
- // before the timeout. Otherwise we just use stringAddr.
- var ra Addr
- select {
- case a := <-resolvedAddr:
- ra = a
- default:
- ra = &stringAddr{net, addr}
- }
- err := &OpError{
- Op: "dial",
- Net: net,
- Addr: ra,
- Err: &timeoutError{},
- }
- return nil, err
- case p := <-ch:
- return p.Conn, p.error
+ return nil, &OpError{Op: "dial", Net: net, Addr: ra, Err: errTimeout}
+ case racer := <-ch:
+ return racer.Conn, racer.error
}
}
diff --git a/src/pkg/net/dial_test.go b/src/pkg/net/dial_test.go
index e24fecc8d..f1d813f41 100644
--- a/src/pkg/net/dial_test.go
+++ b/src/pkg/net/dial_test.go
@@ -5,13 +5,17 @@
package net
import (
+ "bytes"
"flag"
"fmt"
"io"
"os"
+ "os/exec"
"reflect"
"regexp"
"runtime"
+ "strconv"
+ "sync"
"testing"
"time"
)
@@ -137,7 +141,7 @@ func TestSelfConnect(t *testing.T) {
n = 1000
}
switch runtime.GOOS {
- case "darwin", "freebsd", "netbsd", "openbsd", "plan9", "windows":
+ case "darwin", "dragonfly", "freebsd", "netbsd", "openbsd", "plan9", "windows":
// Non-Linux systems take a long time to figure
// out that there is nothing listening on localhost.
n = 100
@@ -314,6 +318,96 @@ func TestDialTimeoutFDLeak(t *testing.T) {
}
}
+func numTCP() (ntcp, nopen, nclose int, err error) {
+ lsof, err := exec.Command("lsof", "-n", "-p", strconv.Itoa(os.Getpid())).Output()
+ if err != nil {
+ return 0, 0, 0, err
+ }
+ ntcp += bytes.Count(lsof, []byte("TCP"))
+ for _, state := range []string{"LISTEN", "SYN_SENT", "SYN_RECEIVED", "ESTABLISHED"} {
+ nopen += bytes.Count(lsof, []byte(state))
+ }
+ for _, state := range []string{"CLOSED", "CLOSE_WAIT", "LAST_ACK", "FIN_WAIT_1", "FIN_WAIT_2", "CLOSING", "TIME_WAIT"} {
+ nclose += bytes.Count(lsof, []byte(state))
+ }
+ return ntcp, nopen, nclose, nil
+}
+
+func TestDialMultiFDLeak(t *testing.T) {
+ if !supportsIPv4 || !supportsIPv6 {
+ t.Skip("neither ipv4 nor ipv6 is supported")
+ }
+
+ halfDeadServer := func(dss *dualStackServer, ln Listener) {
+ for {
+ if c, err := ln.Accept(); err != nil {
+ return
+ } else {
+ // It just keeps established
+ // connections like a half-dead server
+ // does.
+ dss.putConn(c)
+ }
+ }
+ }
+ dss, err := newDualStackServer([]streamListener{
+ {net: "tcp4", addr: "127.0.0.1"},
+ {net: "tcp6", addr: "[::1]"},
+ })
+ if err != nil {
+ t.Fatalf("newDualStackServer failed: %v", err)
+ }
+ defer dss.teardown()
+ if err := dss.buildup(halfDeadServer); err != nil {
+ t.Fatalf("dualStackServer.buildup failed: %v", err)
+ }
+
+ _, before, _, err := numTCP()
+ if err != nil {
+ t.Skipf("skipping test; error finding or running lsof: %v", err)
+ }
+
+ var wg sync.WaitGroup
+ portnum, _, _ := dtoi(dss.port, 0)
+ ras := addrList{
+ // Losers that will fail to connect, see RFC 6890.
+ &TCPAddr{IP: IPv4(198, 18, 0, 254), Port: portnum},
+ &TCPAddr{IP: ParseIP("2001:2::254"), Port: portnum},
+
+ // Winner candidates of this race.
+ &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: portnum},
+ &TCPAddr{IP: IPv6loopback, Port: portnum},
+
+ // Losers that will have established connections.
+ &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: portnum},
+ &TCPAddr{IP: IPv6loopback, Port: portnum},
+ }
+ const T1 = 10 * time.Millisecond
+ const T2 = 2 * T1
+ const N = 10
+ for i := 0; i < N; i++ {
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ if c, err := dialMulti("tcp", "fast failover test", nil, ras, time.Now().Add(T1)); err == nil {
+ c.Close()
+ }
+ }()
+ }
+ wg.Wait()
+ time.Sleep(T2)
+
+ ntcp, after, nclose, err := numTCP()
+ if err != nil {
+ t.Skipf("skipping test; error finding or running lsof: %v", err)
+ }
+ t.Logf("tcp sessions: %v, open sessions: %v, closing sessions: %v", ntcp, after, nclose)
+
+ if after != before {
+ t.Fatalf("got %v open sessions; expected %v", after, before)
+ }
+}
+
func numFD() int {
if runtime.GOOS == "linux" {
f, err := os.Open("/proc/self/fd")
@@ -331,17 +425,22 @@ func numFD() int {
panic("numFDs not implemented on " + runtime.GOOS)
}
-var testPoller = flag.Bool("poller", false, "platform supports runtime-integrated poller")
-
// Assert that a failed Dial attempt does not leak
// runtime.PollDesc structures
func TestDialFailPDLeak(t *testing.T) {
- if !*testPoller {
- t.Skip("test disabled; use -poller to enable")
+ if testing.Short() {
+ t.Skip("skipping test in short mode")
+ }
+ if runtime.GOOS == "windows" && runtime.GOARCH == "386" {
+ // Just skip the test because it takes too long.
+ t.Skipf("skipping test on %q/%q", runtime.GOOS, runtime.GOARCH)
}
- const loops = 10
- const count = 20000
+ maxprocs := runtime.GOMAXPROCS(0)
+ loops := 10 + maxprocs
+ // 500 is enough to turn over the chunk of pollcache.
+ // See allocPollDesc in runtime/netpoll.goc.
+ const count = 500
var old runtime.MemStats // used by sysdelta
runtime.ReadMemStats(&old)
sysdelta := func() uint64 {
@@ -354,19 +453,26 @@ func TestDialFailPDLeak(t *testing.T) {
d := &Dialer{Timeout: time.Nanosecond} // don't bother TCP with handshaking
failcount := 0
for i := 0; i < loops; i++ {
+ var wg sync.WaitGroup
for i := 0; i < count; i++ {
- conn, err := d.Dial("tcp", "127.0.0.1:1")
- if err == nil {
- t.Error("dial should not succeed")
- conn.Close()
- t.FailNow()
- }
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ if c, err := d.Dial("tcp", "127.0.0.1:1"); err == nil {
+ t.Error("dial should not succeed")
+ c.Close()
+ }
+ }()
+ }
+ wg.Wait()
+ if t.Failed() {
+ t.FailNow()
}
if delta := sysdelta(); delta > 0 {
failcount++
}
// there are always some allocations on the first loop
- if failcount > 3 {
+ if failcount > maxprocs+2 {
t.Error("detected possible memory leak in runtime")
t.FailNow()
}
@@ -381,7 +487,6 @@ func TestDialer(t *testing.T) {
defer ln.Close()
ch := make(chan error, 1)
go func() {
- var err error
c, err := ln.Accept()
if err != nil {
ch <- fmt.Errorf("Accept failed: %v", err)
@@ -407,3 +512,46 @@ func TestDialer(t *testing.T) {
t.Error(err)
}
}
+
+func TestDialDualStackLocalhost(t *testing.T) {
+ if ips, err := LookupIP("localhost"); err != nil {
+ t.Fatalf("LookupIP failed: %v", err)
+ } else if len(ips) < 2 || !supportsIPv4 || !supportsIPv6 {
+ t.Skip("localhost doesn't have a pair of different address family IP addresses")
+ }
+
+ touchAndByeServer := func(dss *dualStackServer, ln Listener) {
+ for {
+ if c, err := ln.Accept(); err != nil {
+ return
+ } else {
+ c.Close()
+ }
+ }
+ }
+ dss, err := newDualStackServer([]streamListener{
+ {net: "tcp4", addr: "127.0.0.1"},
+ {net: "tcp6", addr: "[::1]"},
+ })
+ if err != nil {
+ t.Fatalf("newDualStackServer failed: %v", err)
+ }
+ defer dss.teardown()
+ if err := dss.buildup(touchAndByeServer); err != nil {
+ t.Fatalf("dualStackServer.buildup failed: %v", err)
+ }
+
+ d := &Dialer{DualStack: true}
+ for _ = range dss.lns {
+ if c, err := d.Dial("tcp", "localhost:"+dss.port); err != nil {
+ t.Errorf("Dial failed: %v", err)
+ } else {
+ if addr := c.LocalAddr().(*TCPAddr); addr.IP.To4() != nil {
+ dss.teardownNetwork("tcp4")
+ } else if addr.IP.To16() != nil && addr.IP.To4() == nil {
+ dss.teardownNetwork("tcp6")
+ }
+ c.Close()
+ }
+ }
+}
diff --git a/src/pkg/net/dialgoogle_test.go b/src/pkg/net/dialgoogle_test.go
index 73a94f5bf..b4ebad0e0 100644
--- a/src/pkg/net/dialgoogle_test.go
+++ b/src/pkg/net/dialgoogle_test.go
@@ -16,6 +16,59 @@ import (
// If an IPv6 tunnel is running, we can try dialing a real IPv6 address.
var testIPv6 = flag.Bool("ipv6", false, "assume ipv6 tunnel is present")
+func TestResolveGoogle(t *testing.T) {
+ if testing.Short() || !*testExternal {
+ t.Skip("skipping test to avoid external network")
+ }
+
+ for _, network := range []string{"tcp", "tcp4", "tcp6"} {
+ addr, err := ResolveTCPAddr(network, "www.google.com:http")
+ if err != nil {
+ if (network == "tcp" || network == "tcp4") && !supportsIPv4 {
+ t.Logf("ipv4 is not supported: %v", err)
+ } else if network == "tcp6" && !supportsIPv6 {
+ t.Logf("ipv6 is not supported: %v", err)
+ } else {
+ t.Errorf("ResolveTCPAddr failed: %v", err)
+ }
+ continue
+ }
+ if (network == "tcp" || network == "tcp4") && addr.IP.To4() == nil {
+ t.Errorf("got %v; expected an IPv4 address on %v", addr, network)
+ } else if network == "tcp6" && (addr.IP.To16() == nil || addr.IP.To4() != nil) {
+ t.Errorf("got %v; expected an IPv6 address on %v", addr, network)
+ }
+ }
+}
+
+func TestDialGoogle(t *testing.T) {
+ if testing.Short() || !*testExternal {
+ t.Skip("skipping test to avoid external network")
+ }
+
+ d := &Dialer{DualStack: true}
+ for _, network := range []string{"tcp", "tcp4", "tcp6"} {
+ if network == "tcp" && !supportsIPv4 && !supportsIPv6 {
+ t.Logf("skipping test; both ipv4 and ipv6 are not supported")
+ continue
+ } else if network == "tcp4" && !supportsIPv4 {
+ t.Logf("skipping test; ipv4 is not supported")
+ continue
+ } else if network == "tcp6" && !supportsIPv6 {
+ t.Logf("skipping test; ipv6 is not supported")
+ continue
+ } else if network == "tcp6" && !*testIPv6 {
+ t.Logf("test disabled; use -ipv6 to enable")
+ continue
+ }
+ if c, err := d.Dial(network, "www.google.com:http"); err != nil {
+ t.Errorf("Dial failed: %v", err)
+ } else {
+ c.Close()
+ }
+ }
+}
+
// fd is already connected to the destination, port 80.
// Run an HTTP request to fetch the appropriate page.
func fetchGoogle(t *testing.T, fd Conn, network, addr string) {
@@ -54,6 +107,30 @@ var googleaddrsipv4 = []string{
"[0:0:0:0:0:ffff::%d.%d.%d.%d]:80",
}
+func TestDNSThreadLimit(t *testing.T) {
+ if testing.Short() || !*testExternal {
+ t.Skip("skipping test to avoid external network")
+ }
+
+ const N = 10000
+ c := make(chan int, N)
+ for i := 0; i < N; i++ {
+ go func(i int) {
+ LookupIP(fmt.Sprintf("%d.net-test.golang.org", i))
+ c <- 1
+ }(i)
+ }
+ // Don't bother waiting for the stragglers; stop at 0.9 N.
+ for i := 0; i < N*9/10; i++ {
+ if i%100 == 0 {
+ //println("TestDNSThreadLimit:", i)
+ }
+ <-c
+ }
+
+ // If we're still here, it worked.
+}
+
func TestDialGoogleIPv4(t *testing.T) {
if testing.Short() || !*testExternal {
t.Skip("skipping test to avoid external network")
diff --git a/src/pkg/net/dnsclient.go b/src/pkg/net/dnsclient.go
index 76b192645..01db43729 100644
--- a/src/pkg/net/dnsclient.go
+++ b/src/pkg/net/dnsclient.go
@@ -122,12 +122,9 @@ func isDomainName(s string) bool {
if len(s) > 255 {
return false
}
- if s[len(s)-1] != '.' { // simplify checking loop: make name end in dot
- s += "."
- }
last := byte('.')
- ok := false // ok once we've seen a letter
+ ok := false // Ok once we've seen a letter.
partlen := 0
for i := 0; i < len(s); i++ {
c := s[i]
@@ -141,13 +138,13 @@ func isDomainName(s string) bool {
// fine
partlen++
case c == '-':
- // byte before dash cannot be dot
+ // Byte before dash cannot be dot.
if last == '.' {
return false
}
partlen++
case c == '.':
- // byte before dot cannot be dot, dash
+ // Byte before dot cannot be dot, dash.
if last == '.' || last == '-' {
return false
}
@@ -158,6 +155,9 @@ func isDomainName(s string) bool {
}
last = c
}
+ if last == '-' || partlen > 63 {
+ return false
+ }
return ok
}
diff --git a/src/pkg/net/dnsclient_unix.go b/src/pkg/net/dnsclient_unix.go
index 9e21bb4a0..16cf420dc 100644
--- a/src/pkg/net/dnsclient_unix.go
+++ b/src/pkg/net/dnsclient_unix.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.
-// +build darwin freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd
// DNS client: see RFC 1035.
// Has to be linked into package net for Dial.
@@ -17,6 +17,7 @@
package net
import (
+ "io"
"math/rand"
"sync"
"time"
@@ -25,6 +26,7 @@ import (
// Send a request on the connection and hope for a reply.
// Up to cfg.attempts attempts.
func exchange(cfg *dnsConfig, c Conn, name string, qtype uint16) (*dnsMsg, error) {
+ _, useTCP := c.(*TCPConn)
if len(name) >= 256 {
return nil, &DNSError{Err: "name too long", Name: name}
}
@@ -38,7 +40,10 @@ func exchange(cfg *dnsConfig, c Conn, name string, qtype uint16) (*dnsMsg, error
if !ok {
return nil, &DNSError{Err: "internal error - cannot pack message", Name: name}
}
-
+ if useTCP {
+ mlen := uint16(len(msg))
+ msg = append([]byte{byte(mlen >> 8), byte(mlen)}, msg...)
+ }
for attempt := 0; attempt < cfg.attempts; attempt++ {
n, err := c.Write(msg)
if err != nil {
@@ -46,20 +51,33 @@ func exchange(cfg *dnsConfig, c Conn, name string, qtype uint16) (*dnsMsg, error
}
if cfg.timeout == 0 {
- c.SetReadDeadline(time.Time{})
+ c.SetReadDeadline(noDeadline)
} else {
c.SetReadDeadline(time.Now().Add(time.Duration(cfg.timeout) * time.Second))
}
-
- buf := make([]byte, 2000) // More than enough.
- n, err = c.Read(buf)
+ buf := make([]byte, 2000)
+ if useTCP {
+ n, err = io.ReadFull(c, buf[:2])
+ if err != nil {
+ if e, ok := err.(Error); ok && e.Timeout() {
+ continue
+ }
+ }
+ mlen := int(buf[0])<<8 | int(buf[1])
+ if mlen > len(buf) {
+ buf = make([]byte, mlen)
+ }
+ n, err = io.ReadFull(c, buf[:mlen])
+ } else {
+ n, err = c.Read(buf)
+ }
if err != nil {
if e, ok := err.(Error); ok && e.Timeout() {
continue
}
return nil, err
}
- buf = buf[0:n]
+ buf = buf[:n]
in := new(dnsMsg)
if !in.Unpack(buf) || in.id != out.id {
continue
@@ -98,6 +116,19 @@ func tryOneName(cfg *dnsConfig, name string, qtype uint16) (cname string, addrs
err = merr
continue
}
+ if msg.truncated { // see RFC 5966
+ c, cerr = Dial("tcp", server)
+ if cerr != nil {
+ err = cerr
+ continue
+ }
+ msg, merr = exchange(cfg, c, name, qtype)
+ c.Close()
+ if merr != nil {
+ err = merr
+ continue
+ }
+ }
cname, addrs, err = answer(name, server, msg, qtype)
if err == nil || err.(*DNSError).Err == noSuchHost {
break
@@ -180,6 +211,12 @@ func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err error)
if err == nil {
return
}
+ if e, ok := err.(*DNSError); ok {
+ // Show original name passed to lookup, not suffixed one.
+ // In general we might have tried many suffixes; showing
+ // just one is misleading. See also golang.org/issue/6324.
+ e.Name = name
+ }
return
}
diff --git a/src/pkg/net/dnsclient_unix_test.go b/src/pkg/net/dnsclient_unix_test.go
new file mode 100644
index 000000000..47dcb563b
--- /dev/null
+++ b/src/pkg/net/dnsclient_unix_test.go
@@ -0,0 +1,27 @@
+// Copyright 2013 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.
+
+// +build darwin dragonfly freebsd linux netbsd openbsd
+
+package net
+
+import (
+ "testing"
+)
+
+func TestTCPLookup(t *testing.T) {
+ if testing.Short() || !*testExternal {
+ t.Skip("skipping test to avoid external network")
+ }
+ c, err := Dial("tcp", "8.8.8.8:53")
+ if err != nil {
+ t.Fatalf("Dial failed: %v", err)
+ }
+ defer c.Close()
+ cfg := &dnsConfig{timeout: 10, attempts: 3}
+ _, err = exchange(cfg, c, "com.", dnsTypeALL)
+ if err != nil {
+ t.Fatalf("exchange failed: %v", err)
+ }
+}
diff --git a/src/pkg/net/dnsconfig_unix.go b/src/pkg/net/dnsconfig_unix.go
index bb46cc900..2f0f6c031 100644
--- a/src/pkg/net/dnsconfig_unix.go
+++ b/src/pkg/net/dnsconfig_unix.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.
-// +build darwin freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd
// Read system DNS config from /etc/resolv.conf
diff --git a/src/pkg/net/dnsname_test.go b/src/pkg/net/dnsname_test.go
index 70df693f7..57dd25fe4 100644
--- a/src/pkg/net/dnsname_test.go
+++ b/src/pkg/net/dnsname_test.go
@@ -5,6 +5,7 @@
package net
import (
+ "strings"
"testing"
)
@@ -16,7 +17,6 @@ type testCase struct {
var tests = []testCase{
// RFC2181, section 11.
{"_xmpp-server._tcp.google.com", true},
- {"_xmpp-server._tcp.google.com", true},
{"foo.com", true},
{"1foo.com", true},
{"26.0.0.73.com", true},
@@ -24,6 +24,10 @@ var tests = []testCase{
{"fo1o.com", true},
{"foo1.com", true},
{"a.b..com", false},
+ {"a.b-.com", false},
+ {"a.b.com-", false},
+ {"a.b..", false},
+ {"b.com.", true},
}
func getTestCases(ch chan<- testCase) {
@@ -63,3 +67,17 @@ func TestDNSNames(t *testing.T) {
}
}
}
+
+func BenchmarkDNSNames(b *testing.B) {
+ benchmarks := append(tests, []testCase{
+ {strings.Repeat("a", 63), true},
+ {strings.Repeat("a", 64), false},
+ }...)
+ for n := 0; n < b.N; n++ {
+ for _, tc := range benchmarks {
+ if isDomainName(tc.name) != tc.result {
+ b.Errorf("isDomainName(%q) = %v; want %v", tc.name, !tc.result, tc.result)
+ }
+ }
+ }
+}
diff --git a/src/pkg/net/fd_bsd.go b/src/pkg/net/fd_bsd.go
deleted file mode 100644
index 8bb1ae538..000000000
--- a/src/pkg/net/fd_bsd.go
+++ /dev/null
@@ -1,123 +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.
-
-// +build freebsd netbsd openbsd
-
-// Waiting for FDs via kqueue/kevent.
-
-package net
-
-import (
- "os"
- "syscall"
-)
-
-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 error) {
- p = new(pollster)
- if p.kq, err = syscall.Kqueue(); err != nil {
- return nil, os.NewSyscallError("kqueue", err)
- }
- syscall.CloseOnExec(p.kq)
- p.events = p.eventbuf[0:0]
- return p, nil
-}
-
-// First return value is whether the pollServer should be woken up.
-// This version always returns false.
-func (p *pollster) AddFD(fd int, mode int, repeat bool) (bool, error) {
- // pollServer is locked.
-
- var kmode int
- if mode == 'r' {
- kmode = syscall.EVFILT_READ
- } else {
- kmode = syscall.EVFILT_WRITE
- }
- 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
- if !repeat {
- flags |= syscall.EV_ONESHOT
- }
- syscall.SetKevent(ev, fd, kmode, flags)
-
- n, err := syscall.Kevent(p.kq, p.kbuf[:], nil, nil)
- if err != nil {
- return false, os.NewSyscallError("kevent", err)
- }
- if n != 1 || (ev.Flags&syscall.EV_ERROR) == 0 || int(ev.Ident) != fd || int(ev.Filter) != kmode {
- return false, os.NewSyscallError("kqueue phase error", err)
- }
- if ev.Data != 0 {
- return false, syscall.Errno(int(ev.Data))
- }
- return false, nil
-}
-
-// Return value is whether the pollServer should be woken up.
-// This version always returns false.
-func (p *pollster) DelFD(fd int, mode int) bool {
- // pollServer is locked.
-
- var kmode int
- if mode == 'r' {
- kmode = syscall.EVFILT_READ
- } else {
- kmode = syscall.EVFILT_WRITE
- }
- ev := &p.kbuf[0]
- // EV_DELETE - delete event from kqueue list
- syscall.SetKevent(ev, fd, kmode, syscall.EV_DELETE)
- syscall.Kevent(p.kq, p.kbuf[:], nil, nil)
- return false
-}
-
-func (p *pollster) WaitFD(s *pollServer, nsec int64) (fd int, mode int, err error) {
- var t *syscall.Timespec
- for len(p.events) == 0 {
- if nsec > 0 {
- if t == nil {
- t = new(syscall.Timespec)
- }
- *t = syscall.NsecToTimespec(nsec)
- }
-
- s.Unlock()
- n, err := syscall.Kevent(p.kq, nil, p.eventbuf[:], t)
- s.Lock()
-
- if err != nil {
- if err == syscall.EINTR {
- continue
- }
- return -1, 0, os.NewSyscallError("kevent", err)
- }
- if n == 0 {
- return -1, 0, nil
- }
- p.events = p.eventbuf[:n]
- }
- ev := &p.events[0]
- p.events = p.events[1:]
- fd = int(ev.Ident)
- if ev.Filter == syscall.EVFILT_READ {
- mode = 'r'
- } else {
- mode = 'w'
- }
- return fd, mode, nil
-}
-
-func (p *pollster) Close() error { return os.NewSyscallError("close", syscall.Close(p.kq)) }
diff --git a/src/pkg/net/fd_mutex.go b/src/pkg/net/fd_mutex.go
new file mode 100644
index 000000000..6d5509d7f
--- /dev/null
+++ b/src/pkg/net/fd_mutex.go
@@ -0,0 +1,184 @@
+// Copyright 2013 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 "sync/atomic"
+
+// fdMutex is a specialized synchronization primitive
+// that manages lifetime of an fd and serializes access
+// to Read and Write methods on netFD.
+type fdMutex struct {
+ state uint64
+ rsema uint32
+ wsema uint32
+}
+
+// fdMutex.state is organized as follows:
+// 1 bit - whether netFD is closed, if set all subsequent lock operations will fail.
+// 1 bit - lock for read operations.
+// 1 bit - lock for write operations.
+// 20 bits - total number of references (read+write+misc).
+// 20 bits - number of outstanding read waiters.
+// 20 bits - number of outstanding write waiters.
+const (
+ mutexClosed = 1 << 0
+ mutexRLock = 1 << 1
+ mutexWLock = 1 << 2
+ mutexRef = 1 << 3
+ mutexRefMask = (1<<20 - 1) << 3
+ mutexRWait = 1 << 23
+ mutexRMask = (1<<20 - 1) << 23
+ mutexWWait = 1 << 43
+ mutexWMask = (1<<20 - 1) << 43
+)
+
+// Read operations must do RWLock(true)/RWUnlock(true).
+// Write operations must do RWLock(false)/RWUnlock(false).
+// Misc operations must do Incref/Decref. Misc operations include functions like
+// setsockopt and setDeadline. They need to use Incref/Decref to ensure that
+// they operate on the correct fd in presence of a concurrent Close call
+// (otherwise fd can be closed under their feet).
+// Close operation must do IncrefAndClose/Decref.
+
+// RWLock/Incref return whether fd is open.
+// RWUnlock/Decref return whether fd is closed and there are no remaining references.
+
+func (mu *fdMutex) Incref() bool {
+ for {
+ old := atomic.LoadUint64(&mu.state)
+ if old&mutexClosed != 0 {
+ return false
+ }
+ new := old + mutexRef
+ if new&mutexRefMask == 0 {
+ panic("net: inconsistent fdMutex")
+ }
+ if atomic.CompareAndSwapUint64(&mu.state, old, new) {
+ return true
+ }
+ }
+}
+
+func (mu *fdMutex) IncrefAndClose() bool {
+ for {
+ old := atomic.LoadUint64(&mu.state)
+ if old&mutexClosed != 0 {
+ return false
+ }
+ // Mark as closed and acquire a reference.
+ new := (old | mutexClosed) + mutexRef
+ if new&mutexRefMask == 0 {
+ panic("net: inconsistent fdMutex")
+ }
+ // Remove all read and write waiters.
+ new &^= mutexRMask | mutexWMask
+ if atomic.CompareAndSwapUint64(&mu.state, old, new) {
+ // Wake all read and write waiters,
+ // they will observe closed flag after wakeup.
+ for old&mutexRMask != 0 {
+ old -= mutexRWait
+ runtime_Semrelease(&mu.rsema)
+ }
+ for old&mutexWMask != 0 {
+ old -= mutexWWait
+ runtime_Semrelease(&mu.wsema)
+ }
+ return true
+ }
+ }
+}
+
+func (mu *fdMutex) Decref() bool {
+ for {
+ old := atomic.LoadUint64(&mu.state)
+ if old&mutexRefMask == 0 {
+ panic("net: inconsistent fdMutex")
+ }
+ new := old - mutexRef
+ if atomic.CompareAndSwapUint64(&mu.state, old, new) {
+ return new&(mutexClosed|mutexRefMask) == mutexClosed
+ }
+ }
+}
+
+func (mu *fdMutex) RWLock(read bool) bool {
+ var mutexBit, mutexWait, mutexMask uint64
+ var mutexSema *uint32
+ if read {
+ mutexBit = mutexRLock
+ mutexWait = mutexRWait
+ mutexMask = mutexRMask
+ mutexSema = &mu.rsema
+ } else {
+ mutexBit = mutexWLock
+ mutexWait = mutexWWait
+ mutexMask = mutexWMask
+ mutexSema = &mu.wsema
+ }
+ for {
+ old := atomic.LoadUint64(&mu.state)
+ if old&mutexClosed != 0 {
+ return false
+ }
+ var new uint64
+ if old&mutexBit == 0 {
+ // Lock is free, acquire it.
+ new = (old | mutexBit) + mutexRef
+ if new&mutexRefMask == 0 {
+ panic("net: inconsistent fdMutex")
+ }
+ } else {
+ // Wait for lock.
+ new = old + mutexWait
+ if new&mutexMask == 0 {
+ panic("net: inconsistent fdMutex")
+ }
+ }
+ if atomic.CompareAndSwapUint64(&mu.state, old, new) {
+ if old&mutexBit == 0 {
+ return true
+ }
+ runtime_Semacquire(mutexSema)
+ // The signaller has subtracted mutexWait.
+ }
+ }
+}
+
+func (mu *fdMutex) RWUnlock(read bool) bool {
+ var mutexBit, mutexWait, mutexMask uint64
+ var mutexSema *uint32
+ if read {
+ mutexBit = mutexRLock
+ mutexWait = mutexRWait
+ mutexMask = mutexRMask
+ mutexSema = &mu.rsema
+ } else {
+ mutexBit = mutexWLock
+ mutexWait = mutexWWait
+ mutexMask = mutexWMask
+ mutexSema = &mu.wsema
+ }
+ for {
+ old := atomic.LoadUint64(&mu.state)
+ if old&mutexBit == 0 || old&mutexRefMask == 0 {
+ panic("net: inconsistent fdMutex")
+ }
+ // Drop lock, drop reference and wake read waiter if present.
+ new := (old &^ mutexBit) - mutexRef
+ if old&mutexMask != 0 {
+ new -= mutexWait
+ }
+ if atomic.CompareAndSwapUint64(&mu.state, old, new) {
+ if old&mutexMask != 0 {
+ runtime_Semrelease(mutexSema)
+ }
+ return new&(mutexClosed|mutexRefMask) == mutexClosed
+ }
+ }
+}
+
+// Implemented in runtime package.
+func runtime_Semacquire(sema *uint32)
+func runtime_Semrelease(sema *uint32)
diff --git a/src/pkg/net/fd_mutex_test.go b/src/pkg/net/fd_mutex_test.go
new file mode 100644
index 000000000..8383084b7
--- /dev/null
+++ b/src/pkg/net/fd_mutex_test.go
@@ -0,0 +1,186 @@
+// Copyright 2013 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 (
+ "math/rand"
+ "runtime"
+ "testing"
+ "time"
+)
+
+func TestMutexLock(t *testing.T) {
+ var mu fdMutex
+
+ if !mu.Incref() {
+ t.Fatal("broken")
+ }
+ if mu.Decref() {
+ t.Fatal("broken")
+ }
+
+ if !mu.RWLock(true) {
+ t.Fatal("broken")
+ }
+ if mu.RWUnlock(true) {
+ t.Fatal("broken")
+ }
+
+ if !mu.RWLock(false) {
+ t.Fatal("broken")
+ }
+ if mu.RWUnlock(false) {
+ t.Fatal("broken")
+ }
+}
+
+func TestMutexClose(t *testing.T) {
+ var mu fdMutex
+ if !mu.IncrefAndClose() {
+ t.Fatal("broken")
+ }
+
+ if mu.Incref() {
+ t.Fatal("broken")
+ }
+ if mu.RWLock(true) {
+ t.Fatal("broken")
+ }
+ if mu.RWLock(false) {
+ t.Fatal("broken")
+ }
+ if mu.IncrefAndClose() {
+ t.Fatal("broken")
+ }
+}
+
+func TestMutexCloseUnblock(t *testing.T) {
+ c := make(chan bool)
+ var mu fdMutex
+ mu.RWLock(true)
+ for i := 0; i < 4; i++ {
+ go func() {
+ if mu.RWLock(true) {
+ t.Fatal("broken")
+ }
+ c <- true
+ }()
+ }
+ // Concurrent goroutines must not be able to read lock the mutex.
+ time.Sleep(time.Millisecond)
+ select {
+ case <-c:
+ t.Fatal("broken")
+ default:
+ }
+ mu.IncrefAndClose() // Must unblock the readers.
+ for i := 0; i < 4; i++ {
+ select {
+ case <-c:
+ case <-time.After(10 * time.Second):
+ t.Fatal("broken")
+ }
+ }
+ if mu.Decref() {
+ t.Fatal("broken")
+ }
+ if !mu.RWUnlock(true) {
+ t.Fatal("broken")
+ }
+}
+
+func TestMutexPanic(t *testing.T) {
+ ensurePanics := func(f func()) {
+ defer func() {
+ if recover() == nil {
+ t.Fatal("does not panic")
+ }
+ }()
+ f()
+ }
+
+ var mu fdMutex
+ ensurePanics(func() { mu.Decref() })
+ ensurePanics(func() { mu.RWUnlock(true) })
+ ensurePanics(func() { mu.RWUnlock(false) })
+
+ ensurePanics(func() { mu.Incref(); mu.Decref(); mu.Decref() })
+ ensurePanics(func() { mu.RWLock(true); mu.RWUnlock(true); mu.RWUnlock(true) })
+ ensurePanics(func() { mu.RWLock(false); mu.RWUnlock(false); mu.RWUnlock(false) })
+
+ // ensure that it's still not broken
+ mu.Incref()
+ mu.Decref()
+ mu.RWLock(true)
+ mu.RWUnlock(true)
+ mu.RWLock(false)
+ mu.RWUnlock(false)
+}
+
+func TestMutexStress(t *testing.T) {
+ P := 8
+ N := int(1e6)
+ if testing.Short() {
+ P = 4
+ N = 1e4
+ }
+ defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(P))
+ done := make(chan bool)
+ var mu fdMutex
+ var readState [2]uint64
+ var writeState [2]uint64
+ for p := 0; p < P; p++ {
+ go func() {
+ r := rand.New(rand.NewSource(rand.Int63()))
+ for i := 0; i < N; i++ {
+ switch r.Intn(3) {
+ case 0:
+ if !mu.Incref() {
+ t.Fatal("broken")
+ }
+ if mu.Decref() {
+ t.Fatal("broken")
+ }
+ case 1:
+ if !mu.RWLock(true) {
+ t.Fatal("broken")
+ }
+ // Ensure that it provides mutual exclusion for readers.
+ if readState[0] != readState[1] {
+ t.Fatal("broken")
+ }
+ readState[0]++
+ readState[1]++
+ if mu.RWUnlock(true) {
+ t.Fatal("broken")
+ }
+ case 2:
+ if !mu.RWLock(false) {
+ t.Fatal("broken")
+ }
+ // Ensure that it provides mutual exclusion for writers.
+ if writeState[0] != writeState[1] {
+ t.Fatal("broken")
+ }
+ writeState[0]++
+ writeState[1]++
+ if mu.RWUnlock(false) {
+ t.Fatal("broken")
+ }
+ }
+ }
+ done <- true
+ }()
+ }
+ for p := 0; p < P; p++ {
+ <-done
+ }
+ if !mu.IncrefAndClose() {
+ t.Fatal("broken")
+ }
+ if !mu.Decref() {
+ t.Fatal("broken")
+ }
+}
diff --git a/src/pkg/net/fd_plan9.go b/src/pkg/net/fd_plan9.go
index e9527a374..acc829402 100644
--- a/src/pkg/net/fd_plan9.go
+++ b/src/pkg/net/fd_plan9.go
@@ -18,15 +18,13 @@ type netFD struct {
laddr, raddr Addr
}
-var canCancelIO = true // used for testing current package
-
func sysInit() {
}
-func resolveAndDial(net, addr string, localAddr Addr, deadline time.Time) (Conn, error) {
+func dial(net string, ra Addr, dialer func(time.Time) (Conn, error), deadline time.Time) (Conn, error) {
// On plan9, use the relatively inefficient
// goroutine-racing implementation.
- return resolveAndDialChannel(net, addr, localAddr, deadline)
+ return dialChannel(net, ra, dialer, deadline)
}
func newFD(proto, name string, ctl, data *os.File, laddr, raddr Addr) *netFD {
@@ -108,15 +106,15 @@ func (fd *netFD) file(f *os.File, s string) (*os.File, error) {
return os.NewFile(uintptr(dfd), s), nil
}
-func setDeadline(fd *netFD, t time.Time) error {
+func (fd *netFD) setDeadline(t time.Time) error {
return syscall.EPLAN9
}
-func setReadDeadline(fd *netFD, t time.Time) error {
+func (fd *netFD) setReadDeadline(t time.Time) error {
return syscall.EPLAN9
}
-func setWriteDeadline(fd *netFD, t time.Time) error {
+func (fd *netFD) setWriteDeadline(t time.Time) error {
return syscall.EPLAN9
}
@@ -127,3 +125,7 @@ func setReadBuffer(fd *netFD, bytes int) error {
func setWriteBuffer(fd *netFD, bytes int) error {
return syscall.EPLAN9
}
+
+func skipRawSocketTests() (skip bool, skipmsg string, err error) {
+ return true, "skipping test on plan9", nil
+}
diff --git a/src/pkg/net/fd_poll_runtime.go b/src/pkg/net/fd_poll_runtime.go
index e3b4f7e46..e2b276886 100644
--- a/src/pkg/net/fd_poll_runtime.go
+++ b/src/pkg/net/fd_poll_runtime.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.
-// +build darwin linux
+// +build darwin dragonfly freebsd linux netbsd openbsd windows
package net
@@ -13,27 +13,23 @@ import (
)
func runtime_pollServerInit()
-func runtime_pollOpen(fd int) (uintptr, int)
+func runtime_pollOpen(fd uintptr) (uintptr, int)
func runtime_pollClose(ctx uintptr)
func runtime_pollWait(ctx uintptr, mode int) int
+func runtime_pollWaitCanceled(ctx uintptr, mode int) int
func runtime_pollReset(ctx uintptr, mode int) int
func runtime_pollSetDeadline(ctx uintptr, d int64, mode int)
func runtime_pollUnblock(ctx uintptr)
-var canCancelIO = true // used for testing current package
-
type pollDesc struct {
runtimeCtx uintptr
}
var serverInit sync.Once
-func sysInit() {
-}
-
func (pd *pollDesc) Init(fd *netFD) error {
serverInit.Do(runtime_pollServerInit)
- ctx, errno := runtime_pollOpen(fd.sysfd)
+ ctx, errno := runtime_pollOpen(uintptr(fd.sysfd))
if errno != 0 {
return syscall.Errno(errno)
}
@@ -42,7 +38,11 @@ func (pd *pollDesc) Init(fd *netFD) error {
}
func (pd *pollDesc) Close() {
+ if pd.runtimeCtx == 0 {
+ return
+ }
runtime_pollClose(pd.runtimeCtx)
+ pd.runtimeCtx = 0
}
func (pd *pollDesc) Lock() {
@@ -57,28 +57,49 @@ func (pd *pollDesc) Wakeup() {
// Evict evicts fd from the pending list, unblocking any I/O running on fd.
// Return value is whether the pollServer should be woken up.
func (pd *pollDesc) Evict() bool {
+ if pd.runtimeCtx == 0 {
+ return false
+ }
runtime_pollUnblock(pd.runtimeCtx)
return false
}
-func (pd *pollDesc) PrepareRead() error {
- res := runtime_pollReset(pd.runtimeCtx, 'r')
+func (pd *pollDesc) Prepare(mode int) error {
+ res := runtime_pollReset(pd.runtimeCtx, mode)
return convertErr(res)
}
+func (pd *pollDesc) PrepareRead() error {
+ return pd.Prepare('r')
+}
+
func (pd *pollDesc) PrepareWrite() error {
- res := runtime_pollReset(pd.runtimeCtx, 'w')
+ return pd.Prepare('w')
+}
+
+func (pd *pollDesc) Wait(mode int) error {
+ res := runtime_pollWait(pd.runtimeCtx, mode)
return convertErr(res)
}
func (pd *pollDesc) WaitRead() error {
- res := runtime_pollWait(pd.runtimeCtx, 'r')
- return convertErr(res)
+ return pd.Wait('r')
}
func (pd *pollDesc) WaitWrite() error {
- res := runtime_pollWait(pd.runtimeCtx, 'w')
- return convertErr(res)
+ return pd.Wait('w')
+}
+
+func (pd *pollDesc) WaitCanceled(mode int) {
+ runtime_pollWaitCanceled(pd.runtimeCtx, mode)
+}
+
+func (pd *pollDesc) WaitCanceledRead() {
+ pd.WaitCanceled('r')
+}
+
+func (pd *pollDesc) WaitCanceledWrite() {
+ pd.WaitCanceled('w')
}
func convertErr(res int) error {
@@ -90,19 +111,20 @@ func convertErr(res int) error {
case 2:
return errTimeout
}
+ println("unreachable: ", res)
panic("unreachable")
}
-func setReadDeadline(fd *netFD, t time.Time) error {
- return setDeadlineImpl(fd, t, 'r')
+func (fd *netFD) setDeadline(t time.Time) error {
+ return setDeadlineImpl(fd, t, 'r'+'w')
}
-func setWriteDeadline(fd *netFD, t time.Time) error {
- return setDeadlineImpl(fd, t, 'w')
+func (fd *netFD) setReadDeadline(t time.Time) error {
+ return setDeadlineImpl(fd, t, 'r')
}
-func setDeadline(fd *netFD, t time.Time) error {
- return setDeadlineImpl(fd, t, 'r'+'w')
+func (fd *netFD) setWriteDeadline(t time.Time) error {
+ return setDeadlineImpl(fd, t, 'w')
}
func setDeadlineImpl(fd *netFD, t time.Time, mode int) error {
@@ -110,7 +132,7 @@ func setDeadlineImpl(fd *netFD, t time.Time, mode int) error {
if t.IsZero() {
d = 0
}
- if err := fd.incref(false); err != nil {
+ if err := fd.incref(); err != nil {
return err
}
runtime_pollSetDeadline(fd.pd.runtimeCtx, d, mode)
diff --git a/src/pkg/net/fd_poll_unix.go b/src/pkg/net/fd_poll_unix.go
deleted file mode 100644
index 307e577e9..000000000
--- a/src/pkg/net/fd_poll_unix.go
+++ /dev/null
@@ -1,360 +0,0 @@
-// Copyright 2013 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.
-
-// +build freebsd netbsd openbsd
-
-package net
-
-import (
- "os"
- "runtime"
- "sync"
- "syscall"
- "time"
-)
-
-// A pollServer helps FDs determine when to retry a non-blocking
-// read or write after they get EAGAIN. When an FD needs to wait,
-// call s.WaitRead() or s.WaitWrite() to pass the request to the poll server.
-// When the pollServer finds that i/o on FD should be possible
-// again, it will send on fd.cr/fd.cw to wake any waiting goroutines.
-//
-// To avoid races in closing, all fd operations are locked and
-// refcounted. when netFD.Close() is called, it calls syscall.Shutdown
-// and sets a closing flag. Only when the last reference is removed
-// will the fd be closed.
-
-type pollServer struct {
- pr, pw *os.File
- poll *pollster // low-level OS hooks
- sync.Mutex // controls pending and deadline
- pending map[int]*pollDesc
- deadline int64 // next deadline (nsec since 1970)
-}
-
-// A pollDesc contains netFD state related to pollServer.
-type pollDesc struct {
- // immutable after Init()
- pollServer *pollServer
- sysfd int
- cr, cw chan error
-
- // mutable, protected by pollServer mutex
- closing bool
- ncr, ncw int
-
- // mutable, safe for concurrent access
- rdeadline, wdeadline deadline
-}
-
-func newPollServer() (s *pollServer, err error) {
- s = new(pollServer)
- if s.pr, s.pw, err = os.Pipe(); err != nil {
- return nil, err
- }
- if err = syscall.SetNonblock(int(s.pr.Fd()), true); err != nil {
- goto Errno
- }
- if err = syscall.SetNonblock(int(s.pw.Fd()), true); err != nil {
- goto Errno
- }
- if s.poll, err = newpollster(); err != nil {
- goto Error
- }
- if _, err = s.poll.AddFD(int(s.pr.Fd()), 'r', true); err != nil {
- s.poll.Close()
- goto Error
- }
- s.pending = make(map[int]*pollDesc)
- go s.Run()
- return s, nil
-
-Errno:
- err = &os.PathError{
- Op: "setnonblock",
- Path: s.pr.Name(),
- Err: err,
- }
-Error:
- s.pr.Close()
- s.pw.Close()
- return nil, err
-}
-
-func (s *pollServer) AddFD(pd *pollDesc, mode int) error {
- s.Lock()
- intfd := pd.sysfd
- if intfd < 0 || pd.closing {
- // fd closed underfoot
- s.Unlock()
- return errClosing
- }
-
- var t int64
- key := intfd << 1
- if mode == 'r' {
- pd.ncr++
- t = pd.rdeadline.value()
- } else {
- pd.ncw++
- key++
- t = pd.wdeadline.value()
- }
- s.pending[key] = pd
- doWakeup := false
- if t > 0 && (s.deadline == 0 || t < s.deadline) {
- s.deadline = t
- doWakeup = true
- }
-
- wake, err := s.poll.AddFD(intfd, mode, false)
- s.Unlock()
- if err != nil {
- return err
- }
- if wake || doWakeup {
- s.Wakeup()
- }
- return nil
-}
-
-// Evict evicts pd from the pending list, unblocking
-// any I/O running on pd. The caller must have locked
-// pollserver.
-// Return value is whether the pollServer should be woken up.
-func (s *pollServer) Evict(pd *pollDesc) bool {
- pd.closing = true
- doWakeup := false
- if s.pending[pd.sysfd<<1] == pd {
- s.WakeFD(pd, 'r', errClosing)
- if s.poll.DelFD(pd.sysfd, 'r') {
- doWakeup = true
- }
- delete(s.pending, pd.sysfd<<1)
- }
- if s.pending[pd.sysfd<<1|1] == pd {
- s.WakeFD(pd, 'w', errClosing)
- if s.poll.DelFD(pd.sysfd, 'w') {
- doWakeup = true
- }
- delete(s.pending, pd.sysfd<<1|1)
- }
- return doWakeup
-}
-
-var wakeupbuf [1]byte
-
-func (s *pollServer) Wakeup() { s.pw.Write(wakeupbuf[0:]) }
-
-func (s *pollServer) LookupFD(fd int, mode int) *pollDesc {
- key := fd << 1
- if mode == 'w' {
- key++
- }
- netfd, ok := s.pending[key]
- if !ok {
- return nil
- }
- delete(s.pending, key)
- return netfd
-}
-
-func (s *pollServer) WakeFD(pd *pollDesc, mode int, err error) {
- if mode == 'r' {
- for pd.ncr > 0 {
- pd.ncr--
- pd.cr <- err
- }
- } else {
- for pd.ncw > 0 {
- pd.ncw--
- pd.cw <- err
- }
- }
-}
-
-func (s *pollServer) CheckDeadlines() {
- now := time.Now().UnixNano()
- // TODO(rsc): This will need to be handled more efficiently,
- // probably with a heap indexed by wakeup time.
-
- var nextDeadline int64
- for key, pd := range s.pending {
- var t int64
- var mode int
- if key&1 == 0 {
- mode = 'r'
- } else {
- mode = 'w'
- }
- if mode == 'r' {
- t = pd.rdeadline.value()
- } else {
- t = pd.wdeadline.value()
- }
- if t > 0 {
- if t <= now {
- delete(s.pending, key)
- s.poll.DelFD(pd.sysfd, mode)
- s.WakeFD(pd, mode, errTimeout)
- } else if nextDeadline == 0 || t < nextDeadline {
- nextDeadline = t
- }
- }
- }
- s.deadline = nextDeadline
-}
-
-func (s *pollServer) Run() {
- var scratch [100]byte
- s.Lock()
- defer s.Unlock()
- for {
- var timeout int64 // nsec to wait for or 0 for none
- if s.deadline > 0 {
- timeout = s.deadline - time.Now().UnixNano()
- if timeout <= 0 {
- s.CheckDeadlines()
- continue
- }
- }
- fd, mode, err := s.poll.WaitFD(s, timeout)
- if err != nil {
- print("pollServer WaitFD: ", err.Error(), "\n")
- return
- }
- if fd < 0 {
- // Timeout happened.
- s.CheckDeadlines()
- continue
- }
- if fd == int(s.pr.Fd()) {
- // 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 {
- pd := s.LookupFD(fd, mode)
- if pd == nil {
- // This can happen because the WaitFD runs without
- // holding s's lock, so there might be a pending wakeup
- // for an fd that has been evicted. No harm done.
- continue
- }
- s.WakeFD(pd, mode, nil)
- }
- }
-}
-
-func (pd *pollDesc) Close() {
-}
-
-func (pd *pollDesc) Lock() {
- pd.pollServer.Lock()
-}
-
-func (pd *pollDesc) Unlock() {
- pd.pollServer.Unlock()
-}
-
-func (pd *pollDesc) Wakeup() {
- pd.pollServer.Wakeup()
-}
-
-func (pd *pollDesc) PrepareRead() error {
- if pd.rdeadline.expired() {
- return errTimeout
- }
- return nil
-}
-
-func (pd *pollDesc) PrepareWrite() error {
- if pd.wdeadline.expired() {
- return errTimeout
- }
- return nil
-}
-
-func (pd *pollDesc) WaitRead() error {
- err := pd.pollServer.AddFD(pd, 'r')
- if err == nil {
- err = <-pd.cr
- }
- return err
-}
-
-func (pd *pollDesc) WaitWrite() error {
- err := pd.pollServer.AddFD(pd, 'w')
- if err == nil {
- err = <-pd.cw
- }
- return err
-}
-
-func (pd *pollDesc) Evict() bool {
- return pd.pollServer.Evict(pd)
-}
-
-// Spread network FDs over several pollServers.
-
-var pollMaxN int
-var pollservers []*pollServer
-var startServersOnce []func()
-
-var canCancelIO = true // used for testing current package
-
-func sysInit() {
- pollMaxN = runtime.NumCPU()
- if pollMaxN > 8 {
- pollMaxN = 8 // No improvement then.
- }
- pollservers = make([]*pollServer, pollMaxN)
- startServersOnce = make([]func(), pollMaxN)
- for i := 0; i < pollMaxN; i++ {
- k := i
- once := new(sync.Once)
- startServersOnce[i] = func() { once.Do(func() { startServer(k) }) }
- }
-}
-
-func startServer(k int) {
- p, err := newPollServer()
- if err != nil {
- panic(err)
- }
- pollservers[k] = p
-}
-
-func (pd *pollDesc) Init(fd *netFD) error {
- pollN := runtime.GOMAXPROCS(0)
- if pollN > pollMaxN {
- pollN = pollMaxN
- }
- k := fd.sysfd % pollN
- startServersOnce[k]()
- pd.sysfd = fd.sysfd
- pd.pollServer = pollservers[k]
- pd.cr = make(chan error, 1)
- pd.cw = make(chan error, 1)
- return nil
-}
-
-// TODO(dfc) these unused error returns could be removed
-
-func setReadDeadline(fd *netFD, t time.Time) error {
- fd.pd.rdeadline.setTime(t)
- return nil
-}
-
-func setWriteDeadline(fd *netFD, t time.Time) error {
- fd.pd.wdeadline.setTime(t)
- return nil
-}
-
-func setDeadline(fd *netFD, t time.Time) error {
- setReadDeadline(fd, t)
- setWriteDeadline(fd, t)
- return nil
-}
diff --git a/src/pkg/net/fd_posix_test.go b/src/pkg/net/fd_posix_test.go
deleted file mode 100644
index 8be0335d6..000000000
--- a/src/pkg/net/fd_posix_test.go
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2012 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.
-
-// +build darwin freebsd linux netbsd openbsd windows
-
-package net
-
-import (
- "testing"
- "time"
-)
-
-var deadlineSetTimeTests = []struct {
- input time.Time
- expected int64
-}{
- {time.Time{}, 0},
- {time.Date(2009, 11, 10, 23, 00, 00, 00, time.UTC), 1257894000000000000}, // 2009-11-10 23:00:00 +0000 UTC
-}
-
-func TestDeadlineSetTime(t *testing.T) {
- for _, tt := range deadlineSetTimeTests {
- var d deadline
- d.setTime(tt.input)
- actual := d.value()
- expected := int64(0)
- if !tt.input.IsZero() {
- expected = tt.input.UnixNano()
- }
- if actual != expected {
- t.Errorf("set/value failed: expected %v, actual %v", expected, actual)
- }
- }
-}
-
-var deadlineExpiredTests = []struct {
- deadline time.Time
- expired bool
-}{
- // note, times are relative to the start of the test run, not
- // the start of TestDeadlineExpired
- {time.Now().Add(5 * time.Minute), false},
- {time.Now().Add(-5 * time.Minute), true},
- {time.Time{}, false}, // no deadline set
-}
-
-func TestDeadlineExpired(t *testing.T) {
- for _, tt := range deadlineExpiredTests {
- var d deadline
- d.set(tt.deadline.UnixNano())
- expired := d.expired()
- if expired != tt.expired {
- t.Errorf("expire failed: expected %v, actual %v", tt.expired, expired)
- }
- }
-}
diff --git a/src/pkg/net/fd_unix.go b/src/pkg/net/fd_unix.go
index 8c59bff98..9ed4f7536 100644
--- a/src/pkg/net/fd_unix.go
+++ b/src/pkg/net/fd_unix.go
@@ -2,70 +2,59 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd
package net
import (
"io"
"os"
- "sync"
+ "runtime"
+ "sync/atomic"
"syscall"
"time"
)
// Network file descriptor.
type netFD struct {
- // locking/lifetime of sysfd
- sysmu sync.Mutex
- sysref int
-
- // must lock both sysmu and pollDesc to write
- // can lock either to read
- closing bool
+ // locking/lifetime of sysfd + serialize access to Read and Write methods
+ fdmu fdMutex
// immutable until Close
sysfd int
family int
sotype int
isConnected bool
- sysfile *os.File
net string
laddr Addr
raddr Addr
- // serialize access to Read and Write methods
- rio, wio sync.Mutex
-
// wait server
pd pollDesc
}
-func resolveAndDial(net, addr string, localAddr Addr, deadline time.Time) (Conn, error) {
- ra, err := resolveAddr("dial", net, addr, deadline)
- if err != nil {
- return nil, err
- }
- return dial(net, addr, localAddr, ra, deadline)
+func sysInit() {
}
-func newFD(fd, family, sotype int, net string) (*netFD, error) {
- netfd := &netFD{
- sysfd: fd,
- family: family,
- sotype: sotype,
- net: net,
- }
- if err := netfd.pd.Init(netfd); err != nil {
- return nil, err
+func dial(network string, ra Addr, dialer func(time.Time) (Conn, error), deadline time.Time) (Conn, error) {
+ return dialer(deadline)
+}
+
+func newFD(sysfd, family, sotype int, net string) (*netFD, error) {
+ return &netFD{sysfd: sysfd, family: family, sotype: sotype, net: net}, nil
+}
+
+func (fd *netFD) init() error {
+ if err := fd.pd.Init(fd); err != nil {
+ return err
}
- return netfd, nil
+ return nil
}
func (fd *netFD) setAddr(laddr, raddr Addr) {
fd.laddr = laddr
fd.raddr = raddr
- fd.sysfile = os.NewFile(uintptr(fd.sysfd), fd.net)
+ runtime.SetFinalizer(fd, (*netFD).Close)
}
func (fd *netFD) name() string {
@@ -80,8 +69,9 @@ func (fd *netFD) name() string {
}
func (fd *netFD) connect(la, ra syscall.Sockaddr) error {
- fd.wio.Lock()
- defer fd.wio.Unlock()
+ // Do not need to call fd.writeLock here,
+ // because fd is not yet accessible to user,
+ // so no concurrent operations are possible.
if err := fd.pd.PrepareWrite(); err != nil {
return err
}
@@ -100,48 +90,69 @@ func (fd *netFD) connect(la, ra syscall.Sockaddr) error {
return nil
}
+func (fd *netFD) destroy() {
+ // Poller may want to unregister fd in readiness notification mechanism,
+ // so this must be executed before closesocket.
+ fd.pd.Close()
+ closesocket(fd.sysfd)
+ fd.sysfd = -1
+ runtime.SetFinalizer(fd, nil)
+}
+
// Add a reference to this fd.
-// If closing==true, pollDesc must be locked; mark the fd as closing.
// Returns an error if the fd cannot be used.
-func (fd *netFD) incref(closing bool) error {
- fd.sysmu.Lock()
- if fd.closing {
- fd.sysmu.Unlock()
+func (fd *netFD) incref() error {
+ if !fd.fdmu.Incref() {
return errClosing
}
- fd.sysref++
- if closing {
- fd.closing = true
- }
- fd.sysmu.Unlock()
return nil
}
-// Remove a reference to this FD and close if we've been asked to do so (and
-// there are no references left.
+// Remove a reference to this FD and close if we've been asked to do so
+// (and there are no references left).
func (fd *netFD) decref() {
- fd.sysmu.Lock()
- fd.sysref--
- if fd.closing && fd.sysref == 0 {
- // Poller may want to unregister fd in readiness notification mechanism,
- // so this must be executed before sysfile.Close().
- fd.pd.Close()
- if fd.sysfile != nil {
- fd.sysfile.Close()
- fd.sysfile = nil
- } else {
- closesocket(fd.sysfd)
- }
- fd.sysfd = -1
+ if fd.fdmu.Decref() {
+ fd.destroy()
+ }
+}
+
+// Add a reference to this fd and lock for reading.
+// Returns an error if the fd cannot be used.
+func (fd *netFD) readLock() error {
+ if !fd.fdmu.RWLock(true) {
+ return errClosing
+ }
+ return nil
+}
+
+// Unlock for reading and remove a reference to this FD.
+func (fd *netFD) readUnlock() {
+ if fd.fdmu.RWUnlock(true) {
+ fd.destroy()
+ }
+}
+
+// Add a reference to this fd and lock for writing.
+// Returns an error if the fd cannot be used.
+func (fd *netFD) writeLock() error {
+ if !fd.fdmu.RWLock(false) {
+ return errClosing
+ }
+ return nil
+}
+
+// Unlock for writing and remove a reference to this FD.
+func (fd *netFD) writeUnlock() {
+ if fd.fdmu.RWUnlock(false) {
+ fd.destroy()
}
- fd.sysmu.Unlock()
}
func (fd *netFD) Close() error {
fd.pd.Lock() // needed for both fd.incref(true) and pollDesc.Evict
- if err := fd.incref(true); err != nil {
+ if !fd.fdmu.IncrefAndClose() {
fd.pd.Unlock()
- return err
+ return errClosing
}
// Unblock any I/O. Once it all unblocks and returns,
// so that it cannot be referring to fd.sysfd anymore,
@@ -158,7 +169,7 @@ func (fd *netFD) Close() error {
}
func (fd *netFD) shutdown(how int) error {
- if err := fd.incref(false); err != nil {
+ if err := fd.incref(); err != nil {
return err
}
defer fd.decref()
@@ -178,12 +189,10 @@ func (fd *netFD) CloseWrite() error {
}
func (fd *netFD) Read(p []byte) (n int, err error) {
- fd.rio.Lock()
- defer fd.rio.Unlock()
- if err := fd.incref(false); err != nil {
+ if err := fd.readLock(); err != nil {
return 0, err
}
- defer fd.decref()
+ defer fd.readUnlock()
if err := fd.pd.PrepareRead(); err != nil {
return 0, &OpError{"read", fd.net, fd.raddr, err}
}
@@ -207,12 +216,10 @@ func (fd *netFD) Read(p []byte) (n int, err error) {
}
func (fd *netFD) ReadFrom(p []byte) (n int, sa syscall.Sockaddr, err error) {
- fd.rio.Lock()
- defer fd.rio.Unlock()
- if err := fd.incref(false); err != nil {
+ if err := fd.readLock(); err != nil {
return 0, nil, err
}
- defer fd.decref()
+ defer fd.readUnlock()
if err := fd.pd.PrepareRead(); err != nil {
return 0, nil, &OpError{"read", fd.net, fd.laddr, err}
}
@@ -236,12 +243,10 @@ func (fd *netFD) ReadFrom(p []byte) (n int, sa syscall.Sockaddr, err error) {
}
func (fd *netFD) ReadMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.Sockaddr, err error) {
- fd.rio.Lock()
- defer fd.rio.Unlock()
- if err := fd.incref(false); err != nil {
+ if err := fd.readLock(); err != nil {
return 0, 0, 0, nil, err
}
- defer fd.decref()
+ defer fd.readUnlock()
if err := fd.pd.PrepareRead(); err != nil {
return 0, 0, 0, nil, &OpError{"read", fd.net, fd.laddr, err}
}
@@ -272,12 +277,10 @@ func chkReadErr(n int, err error, fd *netFD) error {
}
func (fd *netFD) Write(p []byte) (nn int, err error) {
- fd.wio.Lock()
- defer fd.wio.Unlock()
- if err := fd.incref(false); err != nil {
+ if err := fd.writeLock(); err != nil {
return 0, err
}
- defer fd.decref()
+ defer fd.writeUnlock()
if err := fd.pd.PrepareWrite(); err != nil {
return 0, &OpError{"write", fd.net, fd.raddr, err}
}
@@ -311,12 +314,10 @@ func (fd *netFD) Write(p []byte) (nn int, err error) {
}
func (fd *netFD) WriteTo(p []byte, sa syscall.Sockaddr) (n int, err error) {
- fd.wio.Lock()
- defer fd.wio.Unlock()
- if err := fd.incref(false); err != nil {
+ if err := fd.writeLock(); err != nil {
return 0, err
}
- defer fd.decref()
+ defer fd.writeUnlock()
if err := fd.pd.PrepareWrite(); err != nil {
return 0, &OpError{"write", fd.net, fd.raddr, err}
}
@@ -338,12 +339,10 @@ func (fd *netFD) WriteTo(p []byte, sa syscall.Sockaddr) (n int, err error) {
}
func (fd *netFD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oobn int, err error) {
- fd.wio.Lock()
- defer fd.wio.Unlock()
- if err := fd.incref(false); err != nil {
+ if err := fd.writeLock(); err != nil {
return 0, 0, err
}
- defer fd.decref()
+ defer fd.writeUnlock()
if err := fd.pd.PrepareWrite(); err != nil {
return 0, 0, &OpError{"write", fd.net, fd.raddr, err}
}
@@ -366,12 +365,10 @@ func (fd *netFD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oob
}
func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (netfd *netFD, err error) {
- fd.rio.Lock()
- defer fd.rio.Unlock()
- if err := fd.incref(false); err != nil {
+ if err := fd.readLock(); err != nil {
return nil, err
}
- defer fd.decref()
+ defer fd.readUnlock()
var s int
var rsa syscall.Sockaddr
@@ -399,20 +396,68 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (netfd *netFD, err e
closesocket(s)
return nil, err
}
+ if err = netfd.init(); err != nil {
+ fd.Close()
+ return nil, err
+ }
lsa, _ := syscall.Getsockname(netfd.sysfd)
netfd.setAddr(toAddr(lsa), toAddr(rsa))
return netfd, nil
}
-func (fd *netFD) dup() (f *os.File, err error) {
+// tryDupCloexec indicates whether F_DUPFD_CLOEXEC should be used.
+// If the kernel doesn't support it, this is set to 0.
+var tryDupCloexec = int32(1)
+
+func dupCloseOnExec(fd int) (newfd int, err error) {
+ if atomic.LoadInt32(&tryDupCloexec) == 1 {
+ r0, _, e1 := syscall.Syscall(syscall.SYS_FCNTL, uintptr(fd), syscall.F_DUPFD_CLOEXEC, 0)
+ if runtime.GOOS == "darwin" && e1 == syscall.EBADF {
+ // On OS X 10.6 and below (but we only support
+ // >= 10.6), F_DUPFD_CLOEXEC is unsupported
+ // and fcntl there falls back (undocumented)
+ // to doing an ioctl instead, returning EBADF
+ // in this case because fd is not of the
+ // expected device fd type. Treat it as
+ // EINVAL instead, so we fall back to the
+ // normal dup path.
+ // TODO: only do this on 10.6 if we can detect 10.6
+ // cheaply.
+ e1 = syscall.EINVAL
+ }
+ switch e1 {
+ case 0:
+ return int(r0), nil
+ case syscall.EINVAL:
+ // Old kernel. Fall back to the portable way
+ // from now on.
+ atomic.StoreInt32(&tryDupCloexec, 0)
+ default:
+ return -1, e1
+ }
+ }
+ return dupCloseOnExecOld(fd)
+}
+
+// dupCloseOnExecUnixOld is the traditional way to dup an fd and
+// set its O_CLOEXEC bit, using two system calls.
+func dupCloseOnExecOld(fd int) (newfd int, err error) {
syscall.ForkLock.RLock()
- ns, err := syscall.Dup(fd.sysfd)
+ defer syscall.ForkLock.RUnlock()
+ newfd, err = syscall.Dup(fd)
+ if err != nil {
+ return -1, err
+ }
+ syscall.CloseOnExec(newfd)
+ return
+}
+
+func (fd *netFD) dup() (f *os.File, err error) {
+ ns, err := dupCloseOnExec(fd.sysfd)
if err != nil {
syscall.ForkLock.RUnlock()
return nil, &OpError{"dup", fd.net, fd.laddr, err}
}
- syscall.CloseOnExec(ns)
- syscall.ForkLock.RUnlock()
// We want blocking mode for the new fd, hence the double negative.
// This also puts the old fd into blocking mode, meaning that
@@ -428,3 +473,10 @@ func (fd *netFD) dup() (f *os.File, err error) {
func closesocket(s int) error {
return syscall.Close(s)
}
+
+func skipRawSocketTests() (skip bool, skipmsg string, err error) {
+ if os.Getuid() != 0 {
+ return true, "skipping test; must be root", nil
+ }
+ return false, "", nil
+}
diff --git a/src/pkg/net/fd_unix_test.go b/src/pkg/net/fd_unix_test.go
index 664ef1bf1..65d3e69a7 100644
--- a/src/pkg/net/fd_unix_test.go
+++ b/src/pkg/net/fd_unix_test.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.
-// +build darwin freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd
package net
diff --git a/src/pkg/net/fd_windows.go b/src/pkg/net/fd_windows.go
index fefd174ba..64d56c73e 100644
--- a/src/pkg/net/fd_windows.go
+++ b/src/pkg/net/fd_windows.go
@@ -15,7 +15,10 @@ import (
"unsafe"
)
-var initErr error
+var (
+ initErr error
+ ioSync uint64
+)
// CancelIo Windows API cancels all outstanding IO for a particular
// socket on current thread. To overcome that limitation, we run
@@ -27,7 +30,11 @@ var initErr error
// package uses CancelIoEx API, if present, otherwise it fallback
// to CancelIo.
-var canCancelIO bool // determines if CancelIoEx API is present
+var (
+ canCancelIO bool // determines if CancelIoEx API is present
+ skipSyncNotif bool
+ hasLoadSetFileCompletionNotificationModes bool
+)
func sysInit() {
var d syscall.WSAData
@@ -40,6 +47,27 @@ func sysInit() {
lookupPort = newLookupPort
lookupIP = newLookupIP
}
+
+ hasLoadSetFileCompletionNotificationModes = syscall.LoadSetFileCompletionNotificationModes() == nil
+ if hasLoadSetFileCompletionNotificationModes {
+ // It's not safe to use FILE_SKIP_COMPLETION_PORT_ON_SUCCESS if non IFS providers are installed:
+ // http://support.microsoft.com/kb/2568167
+ skipSyncNotif = true
+ protos := [2]int32{syscall.IPPROTO_TCP, 0}
+ var buf [32]syscall.WSAProtocolInfo
+ len := uint32(unsafe.Sizeof(buf))
+ n, err := syscall.WSAEnumProtocols(&protos[0], &buf[0], &len)
+ if err != nil {
+ skipSyncNotif = false
+ } else {
+ for i := int32(0); i < n; i++ {
+ if buf[i].ServiceFlags1&syscall.XP1_IFS_HANDLES == 0 {
+ skipSyncNotif = false
+ break
+ }
+ }
+ }
+ }
}
func closesocket(s syscall.Handle) error {
@@ -47,128 +75,62 @@ func closesocket(s syscall.Handle) error {
}
func canUseConnectEx(net string) bool {
- if net == "udp" || net == "udp4" || net == "udp6" {
+ switch net {
+ case "udp", "udp4", "udp6", "ip", "ip4", "ip6":
// ConnectEx windows API does not support connectionless sockets.
return false
}
return syscall.LoadConnectEx() == nil
}
-func resolveAndDial(net, addr string, localAddr Addr, deadline time.Time) (Conn, error) {
+func dial(net string, ra Addr, dialer func(time.Time) (Conn, error), deadline time.Time) (Conn, error) {
if !canUseConnectEx(net) {
// Use the relatively inefficient goroutine-racing
// implementation of DialTimeout.
- return resolveAndDialChannel(net, addr, localAddr, deadline)
- }
- ra, err := resolveAddr("dial", net, addr, deadline)
- if err != nil {
- return nil, err
+ return dialChannel(net, ra, dialer, deadline)
}
- return dial(net, addr, localAddr, ra, deadline)
+ return dialer(deadline)
}
-// Interface for all IO operations.
-type anOpIface interface {
- Op() *anOp
- Name() string
- Submit() error
-}
-
-// IO completion result parameters.
-type ioResult struct {
- qty uint32
- err error
-}
-
-// anOp implements functionality common to all IO operations.
-type anOp struct {
+// operation contains superset of data necessary to perform all async IO.
+type operation struct {
// Used by IOCP interface, it must be first field
// of the struct, as our code rely on it.
o syscall.Overlapped
- resultc chan ioResult
- errnoc chan error
- fd *netFD
-}
+ // fields used by runtime.netpoll
+ runtimeCtx uintptr
+ mode int32
+ errno int32
+ qty uint32
-func (o *anOp) Init(fd *netFD, mode int) {
- o.fd = fd
- var i int
- if mode == 'r' {
- i = 0
- } else {
- i = 1
- }
- if fd.resultc[i] == nil {
- fd.resultc[i] = make(chan ioResult, 1)
- }
- o.resultc = fd.resultc[i]
- if fd.errnoc[i] == nil {
- fd.errnoc[i] = make(chan error)
- }
- o.errnoc = fd.errnoc[i]
+ // fields used only by net package
+ fd *netFD
+ errc chan error
+ buf syscall.WSABuf
+ sa syscall.Sockaddr
+ rsa *syscall.RawSockaddrAny
+ rsan int32
+ handle syscall.Handle
+ flags uint32
}
-func (o *anOp) Op() *anOp {
- return o
-}
-
-// bufOp is used by IO operations that read / write
-// data from / to client buffer.
-type bufOp struct {
- anOp
- buf syscall.WSABuf
-}
-
-func (o *bufOp) Init(fd *netFD, buf []byte, mode int) {
- o.anOp.Init(fd, mode)
+func (o *operation) InitBuf(buf []byte) {
o.buf.Len = uint32(len(buf))
- if len(buf) == 0 {
- o.buf.Buf = nil
- } else {
+ o.buf.Buf = nil
+ if len(buf) != 0 {
o.buf.Buf = (*byte)(unsafe.Pointer(&buf[0]))
}
}
-// resultSrv will retrieve all IO completion results from
-// iocp and send them to the correspondent waiting client
-// goroutine via channel supplied in the request.
-type resultSrv struct {
- iocp syscall.Handle
-}
-
-func runtime_blockingSyscallHint()
-
-func (s *resultSrv) Run() {
- var o *syscall.Overlapped
- var key uint32
- var r ioResult
- for {
- r.err = syscall.GetQueuedCompletionStatus(s.iocp, &(r.qty), &key, &o, 0)
- if r.err == syscall.Errno(syscall.WAIT_TIMEOUT) && o == nil {
- runtime_blockingSyscallHint()
- r.err = syscall.GetQueuedCompletionStatus(s.iocp, &(r.qty), &key, &o, syscall.INFINITE)
- }
- switch {
- case r.err == nil:
- // Dequeued successfully completed IO packet.
- case r.err == syscall.Errno(syscall.WAIT_TIMEOUT) && o == nil:
- // Wait has timed out (should not happen now, but might be used in the future).
- panic("GetQueuedCompletionStatus timed out")
- case o == nil:
- // Failed to dequeue anything -> report the error.
- panic("GetQueuedCompletionStatus failed " + r.err.Error())
- default:
- // Dequeued failed IO packet.
- }
- (*anOp)(unsafe.Pointer(o)).resultc <- r
- }
-}
-
// ioSrv executes net IO requests.
type ioSrv struct {
- submchan chan anOpIface // submit IO requests
- canchan chan anOpIface // cancel IO requests
+ req chan ioSrvReq
+}
+
+type ioSrvReq struct {
+ o *operation
+ submit func(o *operation) error // if nil, cancel the operation
}
// ProcessRemoteIO will execute submit IO requests on behalf
@@ -179,192 +141,182 @@ type ioSrv struct {
func (s *ioSrv) ProcessRemoteIO() {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
- for {
- select {
- case o := <-s.submchan:
- o.Op().errnoc <- o.Submit()
- case o := <-s.canchan:
- o.Op().errnoc <- syscall.CancelIo(syscall.Handle(o.Op().fd.sysfd))
+ for r := range s.req {
+ if r.submit != nil {
+ r.o.errc <- r.submit(r.o)
+ } else {
+ r.o.errc <- syscall.CancelIo(r.o.fd.sysfd)
}
}
}
-// ExecIO executes a single IO operation oi. It submits and cancels
+// ExecIO executes a single IO operation o. It submits and cancels
// IO in the current thread for systems where Windows CancelIoEx API
// is available. Alternatively, it passes the request onto
-// a special goroutine and waits for completion or cancels request.
-// deadline is unix nanos.
-func (s *ioSrv) ExecIO(oi anOpIface, deadline int64) (int, error) {
- var err error
- o := oi.Op()
- // Calculate timeout delta.
- var delta int64
- if deadline != 0 {
- delta = deadline - time.Now().UnixNano()
- if delta <= 0 {
- return 0, &OpError{oi.Name(), o.fd.net, o.fd.laddr, errTimeout}
- }
+// runtime netpoll and waits for completion or cancels request.
+func (s *ioSrv) ExecIO(o *operation, name string, submit func(o *operation) error) (int, error) {
+ fd := o.fd
+ // Notify runtime netpoll about starting IO.
+ err := fd.pd.Prepare(int(o.mode))
+ if err != nil {
+ return 0, &OpError{name, fd.net, fd.laddr, err}
}
// Start IO.
if canCancelIO {
- err = oi.Submit()
+ err = submit(o)
} else {
// Send request to a special dedicated thread,
// so it can stop the IO with CancelIO later.
- s.submchan <- oi
- err = <-o.errnoc
+ s.req <- ioSrvReq{o, submit}
+ err = <-o.errc
}
switch err {
case nil:
- // IO completed immediately, but we need to get our completion message anyway.
+ // IO completed immediately
+ if o.fd.skipSyncNotif {
+ // No completion message will follow, so return immediately.
+ return int(o.qty), nil
+ }
+ // Need to get our completion message anyway.
case syscall.ERROR_IO_PENDING:
// IO started, and we have to wait for its completion.
err = nil
default:
- return 0, &OpError{oi.Name(), o.fd.net, o.fd.laddr, err}
- }
- // Setup timer, if deadline is given.
- var timer <-chan time.Time
- if delta > 0 {
- t := time.NewTimer(time.Duration(delta) * time.Nanosecond)
- defer t.Stop()
- timer = t.C
+ return 0, &OpError{name, fd.net, fd.laddr, err}
}
// Wait for our request to complete.
- var r ioResult
- var cancelled, timeout bool
- select {
- case r = <-o.resultc:
- case <-timer:
- cancelled = true
- timeout = true
- case <-o.fd.closec:
- cancelled = true
- }
- if cancelled {
- // Cancel it.
- if canCancelIO {
- err := syscall.CancelIoEx(syscall.Handle(o.Op().fd.sysfd), &o.o)
- // Assuming ERROR_NOT_FOUND is returned, if IO is completed.
- if err != nil && err != syscall.ERROR_NOT_FOUND {
- // TODO(brainman): maybe do something else, but panic.
- panic(err)
- }
- } else {
- s.canchan <- oi
- <-o.errnoc
- }
- // Wait for IO to be canceled or complete successfully.
- r = <-o.resultc
- if r.err == syscall.ERROR_OPERATION_ABORTED { // IO Canceled
- if timeout {
- r.err = errTimeout
- } else {
- r.err = errClosing
- }
+ err = fd.pd.Wait(int(o.mode))
+ if err == nil {
+ // All is good. Extract our IO results and return.
+ if o.errno != 0 {
+ err = syscall.Errno(o.errno)
+ return 0, &OpError{name, fd.net, fd.laddr, err}
}
+ return int(o.qty), nil
+ }
+ // IO is interrupted by "close" or "timeout"
+ netpollErr := err
+ switch netpollErr {
+ case errClosing, errTimeout:
+ // will deal with those.
+ default:
+ panic("net: unexpected runtime.netpoll error: " + netpollErr.Error())
}
- if r.err != nil {
- err = &OpError{oi.Name(), o.fd.net, o.fd.laddr, r.err}
+ // Cancel our request.
+ if canCancelIO {
+ err := syscall.CancelIoEx(fd.sysfd, &o.o)
+ // Assuming ERROR_NOT_FOUND is returned, if IO is completed.
+ if err != nil && err != syscall.ERROR_NOT_FOUND {
+ // TODO(brainman): maybe do something else, but panic.
+ panic(err)
+ }
+ } else {
+ s.req <- ioSrvReq{o, nil}
+ <-o.errc
+ }
+ // Wait for cancellation to complete.
+ fd.pd.WaitCanceled(int(o.mode))
+ if o.errno != 0 {
+ err = syscall.Errno(o.errno)
+ if err == syscall.ERROR_OPERATION_ABORTED { // IO Canceled
+ err = netpollErr
+ }
+ return 0, &OpError{name, fd.net, fd.laddr, err}
}
- return int(r.qty), err
+ // We issued cancellation request. But, it seems, IO operation succeeded
+ // before cancellation request run. We need to treat IO operation as
+ // succeeded (the bytes are actually sent/recv from network).
+ return int(o.qty), nil
}
// Start helper goroutines.
-var resultsrv *resultSrv
-var iosrv *ioSrv
+var rsrv, wsrv *ioSrv
var onceStartServer sync.Once
func startServer() {
- resultsrv = new(resultSrv)
- var err error
- resultsrv.iocp, err = syscall.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 1)
- if err != nil {
- panic("CreateIoCompletionPort: " + err.Error())
- }
- go resultsrv.Run()
-
- iosrv = new(ioSrv)
+ rsrv = new(ioSrv)
+ wsrv = new(ioSrv)
if !canCancelIO {
- // Only CancelIo API is available. Lets start special goroutine
- // locked to an OS thread, that both starts and cancels IO.
- iosrv.submchan = make(chan anOpIface)
- iosrv.canchan = make(chan anOpIface)
- go iosrv.ProcessRemoteIO()
+ // Only CancelIo API is available. Lets start two special goroutines
+ // locked to an OS thread, that both starts and cancels IO. One will
+ // process read requests, while other will do writes.
+ rsrv.req = make(chan ioSrvReq)
+ go rsrv.ProcessRemoteIO()
+ wsrv.req = make(chan ioSrvReq)
+ go wsrv.ProcessRemoteIO()
}
}
// Network file descriptor.
type netFD struct {
- // locking/lifetime of sysfd
- sysmu sync.Mutex
- sysref int
- closing bool
+ // locking/lifetime of sysfd + serialize access to Read and Write methods
+ fdmu fdMutex
// immutable until Close
- sysfd syscall.Handle
- family int
- sotype int
- isConnected bool
- net string
- laddr Addr
- raddr Addr
- resultc [2]chan ioResult // read/write completion results
- errnoc [2]chan error // read/write submit or cancel operation errors
- closec chan bool // used by Close to cancel pending IO
+ sysfd syscall.Handle
+ family int
+ sotype int
+ isConnected bool
+ skipSyncNotif bool
+ net string
+ laddr Addr
+ raddr Addr
- // serialize access to Read and Write methods
- rio, wio sync.Mutex
+ rop operation // read operation
+ wop operation // write operation
- // read and write deadlines
- rdeadline, wdeadline deadline
+ // wait server
+ pd pollDesc
}
-func allocFD(fd syscall.Handle, family, sotype int, net string) *netFD {
- netfd := &netFD{
- sysfd: fd,
- family: family,
- sotype: sotype,
- net: net,
- closec: make(chan bool),
- }
- return netfd
-}
-
-func newFD(fd syscall.Handle, family, proto int, net string) (*netFD, error) {
+func newFD(sysfd syscall.Handle, family, sotype int, net string) (*netFD, error) {
if initErr != nil {
return nil, initErr
}
onceStartServer.Do(startServer)
- // Associate our socket with resultsrv.iocp.
- if _, err := syscall.CreateIoCompletionPort(syscall.Handle(fd), resultsrv.iocp, 0, 0); err != nil {
- return nil, err
+ return &netFD{sysfd: sysfd, family: family, sotype: sotype, net: net}, nil
+}
+
+func (fd *netFD) init() error {
+ if err := fd.pd.Init(fd); err != nil {
+ return err
+ }
+ if hasLoadSetFileCompletionNotificationModes {
+ // We do not use events, so we can skip them always.
+ flags := uint8(syscall.FILE_SKIP_SET_EVENT_ON_HANDLE)
+ // It's not safe to skip completion notifications for UDP:
+ // http://blogs.technet.com/b/winserverperformance/archive/2008/06/26/designing-applications-for-high-performance-part-iii.aspx
+ if skipSyncNotif && fd.net == "tcp" {
+ flags |= syscall.FILE_SKIP_COMPLETION_PORT_ON_SUCCESS
+ }
+ err := syscall.SetFileCompletionNotificationModes(fd.sysfd, flags)
+ if err == nil && flags&syscall.FILE_SKIP_COMPLETION_PORT_ON_SUCCESS != 0 {
+ fd.skipSyncNotif = true
+ }
}
- return allocFD(fd, family, proto, net), nil
+ fd.rop.mode = 'r'
+ fd.wop.mode = 'w'
+ fd.rop.fd = fd
+ fd.wop.fd = fd
+ fd.rop.runtimeCtx = fd.pd.runtimeCtx
+ fd.wop.runtimeCtx = fd.pd.runtimeCtx
+ if !canCancelIO {
+ fd.rop.errc = make(chan error)
+ fd.wop.errc = make(chan error)
+ }
+ return nil
}
func (fd *netFD) setAddr(laddr, raddr Addr) {
fd.laddr = laddr
fd.raddr = raddr
- runtime.SetFinalizer(fd, (*netFD).closesocket)
-}
-
-// Make new connection.
-
-type connectOp struct {
- anOp
- ra syscall.Sockaddr
-}
-
-func (o *connectOp) Submit() error {
- return syscall.ConnectEx(o.fd.sysfd, o.ra, nil, 0, nil, &o.o)
-}
-
-func (o *connectOp) Name() string {
- return "ConnectEx"
+ runtime.SetFinalizer(fd, (*netFD).Close)
}
func (fd *netFD) connect(la, ra syscall.Sockaddr) error {
+ // Do not need to call fd.writeLock here,
+ // because fd is not yet accessible to user,
+ // so no concurrent operations are possible.
if !canUseConnectEx(fd.net) {
return syscall.Connect(fd.sysfd, ra)
}
@@ -383,10 +335,11 @@ func (fd *netFD) connect(la, ra syscall.Sockaddr) error {
}
}
// Call ConnectEx API.
- var o connectOp
- o.Init(fd, 'w')
- o.ra = ra
- _, err := iosrv.ExecIO(&o, fd.wdeadline.value())
+ o := &fd.wop
+ o.sa = ra
+ _, err := wsrv.ExecIO(o, "ConnectEx", func(o *operation) error {
+ return syscall.ConnectEx(o.fd.sysfd, o.sa, nil, 0, nil, &o.o)
+ })
if err != nil {
return err
}
@@ -394,61 +347,80 @@ func (fd *netFD) connect(la, ra syscall.Sockaddr) error {
return syscall.Setsockopt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_UPDATE_CONNECT_CONTEXT, (*byte)(unsafe.Pointer(&fd.sysfd)), int32(unsafe.Sizeof(fd.sysfd)))
}
+func (fd *netFD) destroy() {
+ if fd.sysfd == syscall.InvalidHandle {
+ return
+ }
+ // Poller may want to unregister fd in readiness notification mechanism,
+ // so this must be executed before closesocket.
+ fd.pd.Close()
+ closesocket(fd.sysfd)
+ fd.sysfd = syscall.InvalidHandle
+ // no need for a finalizer anymore
+ runtime.SetFinalizer(fd, nil)
+}
+
// Add a reference to this fd.
-// If closing==true, mark the fd as closing.
// Returns an error if the fd cannot be used.
-func (fd *netFD) incref(closing bool) error {
- if fd == nil {
+func (fd *netFD) incref() error {
+ if !fd.fdmu.Incref() {
return errClosing
}
- fd.sysmu.Lock()
- if fd.closing {
- fd.sysmu.Unlock()
- return errClosing
+ return nil
+}
+
+// Remove a reference to this FD and close if we've been asked to do so
+// (and there are no references left).
+func (fd *netFD) decref() {
+ if fd.fdmu.Decref() {
+ fd.destroy()
}
- fd.sysref++
- if closing {
- fd.closing = true
+}
+
+// Add a reference to this fd and lock for reading.
+// Returns an error if the fd cannot be used.
+func (fd *netFD) readLock() error {
+ if !fd.fdmu.RWLock(true) {
+ return errClosing
}
- closing = fd.closing
- fd.sysmu.Unlock()
return nil
}
-// Remove a reference to this FD and close if we've been asked to do so (and
-// there are no references left.
-func (fd *netFD) decref() {
- if fd == nil {
- return
+// Unlock for reading and remove a reference to this FD.
+func (fd *netFD) readUnlock() {
+ if fd.fdmu.RWUnlock(true) {
+ fd.destroy()
+ }
+}
+
+// Add a reference to this fd and lock for writing.
+// Returns an error if the fd cannot be used.
+func (fd *netFD) writeLock() error {
+ if !fd.fdmu.RWLock(false) {
+ return errClosing
}
- fd.sysmu.Lock()
- fd.sysref--
- if fd.closing && fd.sysref == 0 && fd.sysfd != syscall.InvalidHandle {
- closesocket(fd.sysfd)
- fd.sysfd = syscall.InvalidHandle
- // no need for a finalizer anymore
- runtime.SetFinalizer(fd, nil)
+ return nil
+}
+
+// Unlock for writing and remove a reference to this FD.
+func (fd *netFD) writeUnlock() {
+ if fd.fdmu.RWUnlock(false) {
+ fd.destroy()
}
- fd.sysmu.Unlock()
}
func (fd *netFD) Close() error {
- if err := fd.incref(true); err != nil {
- return err
+ if !fd.fdmu.IncrefAndClose() {
+ return errClosing
}
- defer fd.decref()
// unblock pending reader and writer
- close(fd.closec)
- // wait for both reader and writer to exit
- fd.rio.Lock()
- defer fd.rio.Unlock()
- fd.wio.Lock()
- defer fd.wio.Unlock()
+ fd.pd.Evict()
+ fd.decref()
return nil
}
func (fd *netFD) shutdown(how int) error {
- if err := fd.incref(false); err != nil {
+ if err := fd.incref(); err != nil {
return err
}
defer fd.decref()
@@ -467,72 +439,42 @@ func (fd *netFD) CloseWrite() error {
return fd.shutdown(syscall.SHUT_WR)
}
-func (fd *netFD) closesocket() error {
- return closesocket(fd.sysfd)
-}
-
-// Read from network.
-
-type readOp struct {
- bufOp
-}
-
-func (o *readOp) Submit() error {
- var d, f uint32
- return syscall.WSARecv(syscall.Handle(o.fd.sysfd), &o.buf, 1, &d, &f, &o.o, nil)
-}
-
-func (o *readOp) Name() string {
- return "WSARecv"
-}
-
func (fd *netFD) Read(buf []byte) (int, error) {
- if err := fd.incref(false); err != nil {
+ if err := fd.readLock(); err != nil {
return 0, err
}
- defer fd.decref()
- fd.rio.Lock()
- defer fd.rio.Unlock()
- var o readOp
- o.Init(fd, buf, 'r')
- n, err := iosrv.ExecIO(&o, fd.rdeadline.value())
+ defer fd.readUnlock()
+ o := &fd.rop
+ o.InitBuf(buf)
+ n, err := rsrv.ExecIO(o, "WSARecv", func(o *operation) error {
+ return syscall.WSARecv(o.fd.sysfd, &o.buf, 1, &o.qty, &o.flags, &o.o, nil)
+ })
if err == nil && n == 0 {
err = io.EOF
}
+ if raceenabled {
+ raceAcquire(unsafe.Pointer(&ioSync))
+ }
return n, err
}
-// ReadFrom from network.
-
-type readFromOp struct {
- bufOp
- rsa syscall.RawSockaddrAny
- rsan int32
-}
-
-func (o *readFromOp) Submit() error {
- var d, f uint32
- return syscall.WSARecvFrom(o.fd.sysfd, &o.buf, 1, &d, &f, &o.rsa, &o.rsan, &o.o, nil)
-}
-
-func (o *readFromOp) Name() string {
- return "WSARecvFrom"
-}
-
func (fd *netFD) ReadFrom(buf []byte) (n int, sa syscall.Sockaddr, err error) {
if len(buf) == 0 {
return 0, nil, nil
}
- if err := fd.incref(false); err != nil {
+ if err := fd.readLock(); err != nil {
return 0, nil, err
}
- defer fd.decref()
- fd.rio.Lock()
- defer fd.rio.Unlock()
- var o readFromOp
- o.Init(fd, buf, 'r')
- o.rsan = int32(unsafe.Sizeof(o.rsa))
- n, err = iosrv.ExecIO(&o, fd.rdeadline.value())
+ defer fd.readUnlock()
+ o := &fd.rop
+ o.InitBuf(buf)
+ n, err = rsrv.ExecIO(o, "WSARecvFrom", func(o *operation) error {
+ if o.rsa == nil {
+ o.rsa = new(syscall.RawSockaddrAny)
+ }
+ o.rsan = int32(unsafe.Sizeof(*o.rsa))
+ return syscall.WSARecvFrom(o.fd.sysfd, &o.buf, 1, &o.qty, &o.flags, o.rsa, &o.rsan, &o.o, nil)
+ })
if err != nil {
return 0, nil, err
}
@@ -540,89 +482,42 @@ func (fd *netFD) ReadFrom(buf []byte) (n int, sa syscall.Sockaddr, err error) {
return
}
-// Write to network.
-
-type writeOp struct {
- bufOp
-}
-
-func (o *writeOp) Submit() error {
- var d uint32
- return syscall.WSASend(o.fd.sysfd, &o.buf, 1, &d, 0, &o.o, nil)
-}
-
-func (o *writeOp) Name() string {
- return "WSASend"
-}
-
func (fd *netFD) Write(buf []byte) (int, error) {
- if err := fd.incref(false); err != nil {
+ if err := fd.writeLock(); err != nil {
return 0, err
}
- defer fd.decref()
- fd.wio.Lock()
- defer fd.wio.Unlock()
- var o writeOp
- o.Init(fd, buf, 'w')
- return iosrv.ExecIO(&o, fd.wdeadline.value())
-}
-
-// WriteTo to network.
-
-type writeToOp struct {
- bufOp
- sa syscall.Sockaddr
-}
-
-func (o *writeToOp) Submit() error {
- var d uint32
- return syscall.WSASendto(o.fd.sysfd, &o.buf, 1, &d, 0, o.sa, &o.o, nil)
-}
-
-func (o *writeToOp) Name() string {
- return "WSASendto"
+ defer fd.writeUnlock()
+ if raceenabled {
+ raceReleaseMerge(unsafe.Pointer(&ioSync))
+ }
+ o := &fd.wop
+ o.InitBuf(buf)
+ return wsrv.ExecIO(o, "WSASend", func(o *operation) error {
+ return syscall.WSASend(o.fd.sysfd, &o.buf, 1, &o.qty, 0, &o.o, nil)
+ })
}
func (fd *netFD) WriteTo(buf []byte, sa syscall.Sockaddr) (int, error) {
if len(buf) == 0 {
return 0, nil
}
- if err := fd.incref(false); err != nil {
+ if err := fd.writeLock(); err != nil {
return 0, err
}
- defer fd.decref()
- fd.wio.Lock()
- defer fd.wio.Unlock()
- var o writeToOp
- o.Init(fd, buf, 'w')
+ defer fd.writeUnlock()
+ o := &fd.wop
+ o.InitBuf(buf)
o.sa = sa
- return iosrv.ExecIO(&o, fd.wdeadline.value())
-}
-
-// Accept new network connections.
-
-type acceptOp struct {
- anOp
- newsock syscall.Handle
- attrs [2]syscall.RawSockaddrAny // space for local and remote address only
-}
-
-func (o *acceptOp) Submit() error {
- var d uint32
- l := uint32(unsafe.Sizeof(o.attrs[0]))
- return syscall.AcceptEx(o.fd.sysfd, o.newsock,
- (*byte)(unsafe.Pointer(&o.attrs[0])), 0, l, l, &d, &o.o)
-}
-
-func (o *acceptOp) Name() string {
- return "AcceptEx"
+ return wsrv.ExecIO(o, "WSASendto", func(o *operation) error {
+ return syscall.WSASendto(o.fd.sysfd, &o.buf, 1, &o.qty, 0, o.sa, &o.o, nil)
+ })
}
func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (*netFD, error) {
- if err := fd.incref(false); err != nil {
+ if err := fd.readLock(); err != nil {
return nil, err
}
- defer fd.decref()
+ defer fd.readUnlock()
// Get new socket.
s, err := sysSocket(fd.family, fd.sotype, 0)
@@ -631,43 +526,67 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (*netFD, error) {
}
// Associate our new socket with IOCP.
- onceStartServer.Do(startServer)
- if _, err := syscall.CreateIoCompletionPort(s, resultsrv.iocp, 0, 0); err != nil {
+ netfd, err := newFD(s, fd.family, fd.sotype, fd.net)
+ if err != nil {
closesocket(s)
- return nil, &OpError{"CreateIoCompletionPort", fd.net, fd.laddr, err}
+ return nil, &OpError{"accept", fd.net, fd.laddr, err}
+ }
+ if err := netfd.init(); err != nil {
+ fd.Close()
+ return nil, err
}
// Submit accept request.
- var o acceptOp
- o.Init(fd, 'r')
- o.newsock = s
- _, err = iosrv.ExecIO(&o, fd.rdeadline.value())
+ o := &fd.rop
+ o.handle = s
+ var rawsa [2]syscall.RawSockaddrAny
+ o.rsan = int32(unsafe.Sizeof(rawsa[0]))
+ _, err = rsrv.ExecIO(o, "AcceptEx", func(o *operation) error {
+ return syscall.AcceptEx(o.fd.sysfd, o.handle, (*byte)(unsafe.Pointer(&rawsa[0])), 0, uint32(o.rsan), uint32(o.rsan), &o.qty, &o.o)
+ })
if err != nil {
- closesocket(s)
+ netfd.Close()
return nil, err
}
// Inherit properties of the listening socket.
err = syscall.Setsockopt(s, syscall.SOL_SOCKET, syscall.SO_UPDATE_ACCEPT_CONTEXT, (*byte)(unsafe.Pointer(&fd.sysfd)), int32(unsafe.Sizeof(fd.sysfd)))
if err != nil {
- closesocket(s)
+ netfd.Close()
return nil, &OpError{"Setsockopt", fd.net, fd.laddr, err}
}
// Get local and peer addr out of AcceptEx buffer.
var lrsa, rrsa *syscall.RawSockaddrAny
var llen, rlen int32
- l := uint32(unsafe.Sizeof(*lrsa))
- syscall.GetAcceptExSockaddrs((*byte)(unsafe.Pointer(&o.attrs[0])),
- 0, l, l, &lrsa, &llen, &rrsa, &rlen)
+ syscall.GetAcceptExSockaddrs((*byte)(unsafe.Pointer(&rawsa[0])),
+ 0, uint32(o.rsan), uint32(o.rsan), &lrsa, &llen, &rrsa, &rlen)
lsa, _ := lrsa.Sockaddr()
rsa, _ := rrsa.Sockaddr()
- netfd := allocFD(s, fd.family, fd.sotype, fd.net)
netfd.setAddr(toAddr(lsa), toAddr(rsa))
return netfd, nil
}
+func skipRawSocketTests() (skip bool, skipmsg string, err error) {
+ // From http://msdn.microsoft.com/en-us/library/windows/desktop/ms740548.aspx:
+ // Note: To use a socket of type SOCK_RAW requires administrative privileges.
+ // Users running Winsock applications that use raw sockets must be a member of
+ // the Administrators group on the local computer, otherwise raw socket calls
+ // will fail with an error code of WSAEACCES. On Windows Vista and later, access
+ // for raw sockets is enforced at socket creation. In earlier versions of Windows,
+ // access for raw sockets is enforced during other socket operations.
+ s, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, 0)
+ if err == syscall.WSAEACCES {
+ return true, "skipping test; no access to raw socket allowed", nil
+ }
+ if err != nil {
+ return true, "", err
+ }
+ defer syscall.Closesocket(s)
+ return false, "", nil
+}
+
// Unimplemented functions.
func (fd *netFD) dup() (*os.File, error) {
diff --git a/src/pkg/net/file_unix.go b/src/pkg/net/file_unix.go
index 4c8403e40..8fe1b0eb0 100644
--- a/src/pkg/net/file_unix.go
+++ b/src/pkg/net/file_unix.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.
-// +build darwin freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd
package net
@@ -12,14 +12,11 @@ import (
)
func newFileFD(f *os.File) (*netFD, error) {
- syscall.ForkLock.RLock()
- fd, err := syscall.Dup(int(f.Fd()))
+ fd, err := dupCloseOnExec(int(f.Fd()))
if err != nil {
- syscall.ForkLock.RUnlock()
return nil, os.NewSyscallError("dup", err)
}
- syscall.CloseOnExec(fd)
- syscall.ForkLock.RUnlock()
+
if err = syscall.SetNonblock(fd, true); err != nil {
closesocket(fd)
return nil, err
@@ -70,6 +67,10 @@ func newFileFD(f *os.File) (*netFD, error) {
closesocket(fd)
return nil, err
}
+ if err := netfd.init(); err != nil {
+ netfd.Close()
+ return nil, err
+ }
netfd.setAddr(laddr, raddr)
return netfd, nil
}
diff --git a/src/pkg/net/hosts_test.go b/src/pkg/net/hosts_test.go
index 064e7e432..b07ed0baa 100644
--- a/src/pkg/net/hosts_test.go
+++ b/src/pkg/net/hosts_test.go
@@ -53,6 +53,19 @@ func TestLookupStaticHost(t *testing.T) {
hostsPath = p
}
+// https://code.google.com/p/go/issues/detail?id=6646
+func TestSingleLineHostsFile(t *testing.T) {
+ p := hostsPath
+ hostsPath = "testdata/hosts_singleline"
+
+ ips := lookupStaticHost("odin")
+ if len(ips) != 1 || ips[0] != "127.0.0.2" {
+ t.Errorf("lookupStaticHost = %v, want %v", ips, []string{"127.0.0.2"})
+ }
+
+ hostsPath = p
+}
+
func TestLookupHost(t *testing.T) {
// Can't depend on this to return anything in particular,
// but if it does return something, make sure it doesn't
diff --git a/src/pkg/net/http/cgi/child.go b/src/pkg/net/http/cgi/child.go
index 100b8b777..45fc2e57c 100644
--- a/src/pkg/net/http/cgi/child.go
+++ b/src/pkg/net/http/cgi/child.go
@@ -100,10 +100,21 @@ func RequestFromMap(params map[string]string) (*http.Request, error) {
uriStr += "?" + s
}
}
+
+ // There's apparently a de-facto standard for this.
+ // http://docstore.mik.ua/orelly/linux/cgi/ch03_02.htm#ch03-35636
+ if s := params["HTTPS"]; s == "on" || s == "ON" || s == "1" {
+ r.TLS = &tls.ConnectionState{HandshakeComplete: true}
+ }
+
if r.Host != "" {
- // Hostname is provided, so we can reasonably construct a URL,
- // even if we have to assume 'http' for the scheme.
- rawurl := "http://" + r.Host + uriStr
+ // Hostname is provided, so we can reasonably construct a URL.
+ rawurl := r.Host + uriStr
+ if r.TLS == nil {
+ rawurl = "http://" + rawurl
+ } else {
+ rawurl = "https://" + rawurl
+ }
url, err := url.Parse(rawurl)
if err != nil {
return nil, errors.New("cgi: failed to parse host and REQUEST_URI into a URL: " + rawurl)
@@ -120,12 +131,6 @@ func RequestFromMap(params map[string]string) (*http.Request, error) {
r.URL = url
}
- // There's apparently a de-facto standard for this.
- // http://docstore.mik.ua/orelly/linux/cgi/ch03_02.htm#ch03-35636
- if s := params["HTTPS"]; s == "on" || s == "ON" || s == "1" {
- r.TLS = &tls.ConnectionState{HandshakeComplete: true}
- }
-
// Request.RemoteAddr has its port set by Go's standard http
// server, so we do here too. We don't have one, though, so we
// use a dummy one.
diff --git a/src/pkg/net/http/cgi/child_test.go b/src/pkg/net/http/cgi/child_test.go
index 74e068014..075d8411b 100644
--- a/src/pkg/net/http/cgi/child_test.go
+++ b/src/pkg/net/http/cgi/child_test.go
@@ -21,7 +21,6 @@ func TestRequest(t *testing.T) {
"REQUEST_URI": "/path?a=b",
"CONTENT_LENGTH": "123",
"CONTENT_TYPE": "text/xml",
- "HTTPS": "1",
"REMOTE_ADDR": "5.6.7.8",
}
req, err := RequestFromMap(env)
@@ -58,14 +57,37 @@ func TestRequest(t *testing.T) {
if req.Trailer == nil {
t.Errorf("unexpected nil Trailer")
}
- if req.TLS == nil {
- t.Errorf("expected non-nil TLS")
+ if req.TLS != nil {
+ t.Errorf("expected nil TLS")
}
if e, g := "5.6.7.8:0", req.RemoteAddr; e != g {
t.Errorf("RemoteAddr: got %q; want %q", g, e)
}
}
+func TestRequestWithTLS(t *testing.T) {
+ env := map[string]string{
+ "SERVER_PROTOCOL": "HTTP/1.1",
+ "REQUEST_METHOD": "GET",
+ "HTTP_HOST": "example.com",
+ "HTTP_REFERER": "elsewhere",
+ "REQUEST_URI": "/path?a=b",
+ "CONTENT_TYPE": "text/xml",
+ "HTTPS": "1",
+ "REMOTE_ADDR": "5.6.7.8",
+ }
+ req, err := RequestFromMap(env)
+ if err != nil {
+ t.Fatalf("RequestFromMap: %v", err)
+ }
+ if g, e := req.URL.String(), "https://example.com/path?a=b"; e != g {
+ t.Errorf("expected URL %q; got %q", e, g)
+ }
+ if req.TLS == nil {
+ t.Errorf("expected non-nil TLS")
+ }
+}
+
func TestRequestWithoutHost(t *testing.T) {
env := map[string]string{
"SERVER_PROTOCOL": "HTTP/1.1",
diff --git a/src/pkg/net/http/client.go b/src/pkg/net/http/client.go
index a34d47be1..22f2e865c 100644
--- a/src/pkg/net/http/client.go
+++ b/src/pkg/net/http/client.go
@@ -74,8 +74,8 @@ type RoundTripper interface {
// authentication, or cookies.
//
// RoundTrip should not modify the request, except for
- // consuming the Body. The request's URL and Header fields
- // are guaranteed to be initialized.
+ // consuming and closing the Body. The request's URL and
+ // Header fields are guaranteed to be initialized.
RoundTrip(*Request) (*Response, error)
}
@@ -161,7 +161,9 @@ func send(req *Request, t RoundTripper) (resp *Response, err error) {
}
if u := req.URL.User; u != nil {
- req.Header.Set("Authorization", "Basic "+base64.URLEncoding.EncodeToString([]byte(u.String())))
+ username := u.Username()
+ password, _ := u.Password()
+ req.Header.Set("Authorization", "Basic "+basicAuth(username, password))
}
resp, err = t.RoundTrip(req)
if err != nil {
@@ -173,6 +175,16 @@ func send(req *Request, t RoundTripper) (resp *Response, err error) {
return resp, nil
}
+// See 2 (end of page 4) http://www.ietf.org/rfc/rfc2617.txt
+// "To receive authorization, the client sends the userid and password,
+// separated by a single colon (":") character, within a base64
+// encoded string in the credentials."
+// It is not meant to be urlencoded.
+func basicAuth(username, password string) string {
+ auth := username + ":" + password
+ return base64.StdEncoding.EncodeToString([]byte(auth))
+}
+
// True if the specified HTTP status code is one for which the Get utility should
// automatically redirect.
func shouldRedirectGet(statusCode int) bool {
@@ -335,6 +347,9 @@ func Post(url string, bodyType string, body io.Reader) (resp *Response, err erro
// Post issues a POST to the specified URL.
//
// Caller should close resp.Body when done reading from it.
+//
+// If the provided body is also an io.Closer, it is closed after the
+// body is successfully written to the server.
func (c *Client) Post(url string, bodyType string, body io.Reader) (resp *Response, err error) {
req, err := NewRequest("POST", url, body)
if err != nil {
diff --git a/src/pkg/net/http/client_test.go b/src/pkg/net/http/client_test.go
index 73f1fe3c1..997d04151 100644
--- a/src/pkg/net/http/client_test.go
+++ b/src/pkg/net/http/client_test.go
@@ -10,6 +10,7 @@ import (
"bytes"
"crypto/tls"
"crypto/x509"
+ "encoding/base64"
"errors"
"fmt"
"io"
@@ -665,6 +666,36 @@ func TestClientWithIncorrectTLSServerName(t *testing.T) {
}
}
+// Test for golang.org/issue/5829; the Transport should respect TLSClientConfig.ServerName
+// when not empty.
+//
+// tls.Config.ServerName (non-empty, set to "example.com") takes
+// precedence over "some-other-host.tld" which previously incorrectly
+// took precedence. We don't actually connect to (or even resolve)
+// "some-other-host.tld", though, because of the Transport.Dial hook.
+//
+// The httptest.Server has a cert with "example.com" as its name.
+func TestTransportUsesTLSConfigServerName(t *testing.T) {
+ defer afterTest(t)
+ ts := httptest.NewTLSServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ w.Write([]byte("Hello"))
+ }))
+ defer ts.Close()
+
+ tr := newTLSTransport(t, ts)
+ tr.TLSClientConfig.ServerName = "example.com" // one of httptest's Server cert names
+ tr.Dial = func(netw, addr string) (net.Conn, error) {
+ return net.Dial(netw, ts.Listener.Addr().String())
+ }
+ defer tr.CloseIdleConnections()
+ c := &Client{Transport: tr}
+ res, err := c.Get("https://some-other-host.tld/")
+ if err != nil {
+ t.Fatal(err)
+ }
+ res.Body.Close()
+}
+
// Verify Response.ContentLength is populated. http://golang.org/issue/4126
func TestClientHeadContentLength(t *testing.T) {
defer afterTest(t)
@@ -700,3 +731,71 @@ func TestClientHeadContentLength(t *testing.T) {
}
}
}
+
+func TestEmptyPasswordAuth(t *testing.T) {
+ defer afterTest(t)
+ gopher := "gopher"
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ auth := r.Header.Get("Authorization")
+ if strings.HasPrefix(auth, "Basic ") {
+ encoded := auth[6:]
+ decoded, err := base64.StdEncoding.DecodeString(encoded)
+ if err != nil {
+ t.Fatal(err)
+ }
+ expected := gopher + ":"
+ s := string(decoded)
+ if expected != s {
+ t.Errorf("Invalid Authorization header. Got %q, wanted %q", s, expected)
+ }
+ } else {
+ t.Errorf("Invalid auth %q", auth)
+ }
+ }))
+ defer ts.Close()
+ c := &Client{}
+ req, err := NewRequest("GET", ts.URL, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ req.URL.User = url.User(gopher)
+ resp, err := c.Do(req)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer resp.Body.Close()
+}
+
+func TestBasicAuth(t *testing.T) {
+ defer afterTest(t)
+ tr := &recordingTransport{}
+ client := &Client{Transport: tr}
+
+ url := "http://My%20User:My%20Pass@dummy.faketld/"
+ expected := "My User:My Pass"
+ client.Get(url)
+
+ if tr.req.Method != "GET" {
+ t.Errorf("got method %q, want %q", tr.req.Method, "GET")
+ }
+ if tr.req.URL.String() != url {
+ t.Errorf("got URL %q, want %q", tr.req.URL.String(), url)
+ }
+ if tr.req.Header == nil {
+ t.Fatalf("expected non-nil request Header")
+ }
+ auth := tr.req.Header.Get("Authorization")
+ if strings.HasPrefix(auth, "Basic ") {
+ encoded := auth[6:]
+ decoded, err := base64.StdEncoding.DecodeString(encoded)
+ if err != nil {
+ t.Fatal(err)
+ }
+ s := string(decoded)
+ if expected != s {
+ t.Errorf("Invalid Authorization header. Got %q, wanted %q", s, expected)
+ }
+ } else {
+ t.Errorf("Invalid auth %q", auth)
+ }
+}
diff --git a/src/pkg/net/http/cookie.go b/src/pkg/net/http/cookie.go
index 155b09223..8b01c508e 100644
--- a/src/pkg/net/http/cookie.go
+++ b/src/pkg/net/http/cookie.go
@@ -7,6 +7,8 @@ package http
import (
"bytes"
"fmt"
+ "log"
+ "net"
"strconv"
"strings"
"time"
@@ -139,12 +141,25 @@ func SetCookie(w ResponseWriter, cookie *Cookie) {
// header (if other fields are set).
func (c *Cookie) String() string {
var b bytes.Buffer
- fmt.Fprintf(&b, "%s=%s", sanitizeName(c.Name), sanitizeValue(c.Value))
+ fmt.Fprintf(&b, "%s=%s", sanitizeCookieName(c.Name), sanitizeCookieValue(c.Value))
if len(c.Path) > 0 {
- fmt.Fprintf(&b, "; Path=%s", sanitizeValue(c.Path))
+ fmt.Fprintf(&b, "; Path=%s", sanitizeCookiePath(c.Path))
}
if len(c.Domain) > 0 {
- fmt.Fprintf(&b, "; Domain=%s", sanitizeValue(c.Domain))
+ if validCookieDomain(c.Domain) {
+ // A c.Domain containing illegal characters is not
+ // sanitized but simply dropped which turns the cookie
+ // into a host-only cookie. A leading dot is okay
+ // but won't be sent.
+ d := c.Domain
+ if d[0] == '.' {
+ d = d[1:]
+ }
+ fmt.Fprintf(&b, "; Domain=%s", d)
+ } else {
+ log.Printf("net/http: invalid Cookie.Domain %q; dropping domain attribute",
+ c.Domain)
+ }
}
if c.Expires.Unix() > 0 {
fmt.Fprintf(&b, "; Expires=%s", c.Expires.UTC().Format(time.RFC1123))
@@ -207,16 +222,122 @@ func readCookies(h Header, filter string) []*Cookie {
return cookies
}
+// validCookieDomain returns wheter v is a valid cookie domain-value.
+func validCookieDomain(v string) bool {
+ if isCookieDomainName(v) {
+ return true
+ }
+ if net.ParseIP(v) != nil && !strings.Contains(v, ":") {
+ return true
+ }
+ return false
+}
+
+// isCookieDomainName returns whether s is a valid domain name or a valid
+// domain name with a leading dot '.'. It is almost a direct copy of
+// package net's isDomainName.
+func isCookieDomainName(s string) bool {
+ if len(s) == 0 {
+ return false
+ }
+ if len(s) > 255 {
+ return false
+ }
+
+ if s[0] == '.' {
+ // A cookie a domain attribute may start with a leading dot.
+ s = s[1:]
+ }
+ last := byte('.')
+ ok := false // Ok once we've seen a letter.
+ partlen := 0
+ for i := 0; i < len(s); i++ {
+ c := s[i]
+ switch {
+ default:
+ return false
+ case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z':
+ // No '_' allowed here (in contrast to package net).
+ ok = true
+ partlen++
+ case '0' <= c && c <= '9':
+ // fine
+ partlen++
+ case c == '-':
+ // Byte before dash cannot be dot.
+ if last == '.' {
+ return false
+ }
+ partlen++
+ case c == '.':
+ // Byte before dot cannot be dot, dash.
+ if last == '.' || last == '-' {
+ return false
+ }
+ if partlen > 63 || partlen == 0 {
+ return false
+ }
+ partlen = 0
+ }
+ last = c
+ }
+ if last == '-' || partlen > 63 {
+ return false
+ }
+
+ return ok
+}
+
var cookieNameSanitizer = strings.NewReplacer("\n", "-", "\r", "-")
-func sanitizeName(n string) string {
+func sanitizeCookieName(n string) string {
return cookieNameSanitizer.Replace(n)
}
-var cookieValueSanitizer = strings.NewReplacer("\n", " ", "\r", " ", ";", " ")
+// http://tools.ietf.org/html/rfc6265#section-4.1.1
+// cookie-value = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE )
+// cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
+// ; US-ASCII characters excluding CTLs,
+// ; whitespace DQUOTE, comma, semicolon,
+// ; and backslash
+func sanitizeCookieValue(v string) string {
+ return sanitizeOrWarn("Cookie.Value", validCookieValueByte, v)
+}
+
+func validCookieValueByte(b byte) bool {
+ return 0x20 < b && b < 0x7f && b != '"' && b != ',' && b != ';' && b != '\\'
+}
+
+// path-av = "Path=" path-value
+// path-value = <any CHAR except CTLs or ";">
+func sanitizeCookiePath(v string) string {
+ return sanitizeOrWarn("Cookie.Path", validCookiePathByte, v)
+}
-func sanitizeValue(v string) string {
- return cookieValueSanitizer.Replace(v)
+func validCookiePathByte(b byte) bool {
+ return 0x20 <= b && b < 0x7f && b != ';'
+}
+
+func sanitizeOrWarn(fieldName string, valid func(byte) bool, v string) string {
+ ok := true
+ for i := 0; i < len(v); i++ {
+ if valid(v[i]) {
+ continue
+ }
+ log.Printf("net/http: invalid byte %q in %s; dropping invalid bytes", v[i], fieldName)
+ ok = false
+ break
+ }
+ if ok {
+ return v
+ }
+ buf := make([]byte, 0, len(v))
+ for i := 0; i < len(v); i++ {
+ if b := v[i]; valid(b) {
+ buf = append(buf, b)
+ }
+ }
+ return string(buf)
}
func unquoteCookieValue(v string) string {
diff --git a/src/pkg/net/http/cookie_test.go b/src/pkg/net/http/cookie_test.go
index f84f73936..11b01cc57 100644
--- a/src/pkg/net/http/cookie_test.go
+++ b/src/pkg/net/http/cookie_test.go
@@ -26,12 +26,28 @@ var writeSetCookiesTests = []struct {
},
{
&Cookie{Name: "cookie-3", Value: "three", Domain: ".example.com"},
- "cookie-3=three; Domain=.example.com",
+ "cookie-3=three; Domain=example.com",
},
{
&Cookie{Name: "cookie-4", Value: "four", Path: "/restricted/"},
"cookie-4=four; Path=/restricted/",
},
+ {
+ &Cookie{Name: "cookie-5", Value: "five", Domain: "wrong;bad.abc"},
+ "cookie-5=five",
+ },
+ {
+ &Cookie{Name: "cookie-6", Value: "six", Domain: "bad-.abc"},
+ "cookie-6=six",
+ },
+ {
+ &Cookie{Name: "cookie-7", Value: "seven", Domain: "127.0.0.1"},
+ "cookie-7=seven; Domain=127.0.0.1",
+ },
+ {
+ &Cookie{Name: "cookie-8", Value: "eight", Domain: "::1"},
+ "cookie-8=eight",
+ },
}
func TestWriteSetCookies(t *testing.T) {
@@ -226,3 +242,34 @@ func TestReadCookies(t *testing.T) {
}
}
}
+
+func TestCookieSanitizeValue(t *testing.T) {
+ tests := []struct {
+ in, want string
+ }{
+ {"foo", "foo"},
+ {"foo bar", "foobar"},
+ {"\x00\x7e\x7f\x80", "\x7e"},
+ {`"withquotes"`, "withquotes"},
+ }
+ for _, tt := range tests {
+ if got := sanitizeCookieValue(tt.in); got != tt.want {
+ t.Errorf("sanitizeCookieValue(%q) = %q; want %q", tt.in, got, tt.want)
+ }
+ }
+}
+
+func TestCookieSanitizePath(t *testing.T) {
+ tests := []struct {
+ in, want string
+ }{
+ {"/path", "/path"},
+ {"/path with space/", "/path with space/"},
+ {"/just;no;semicolon\x00orstuff/", "/justnosemicolonorstuff/"},
+ }
+ for _, tt := range tests {
+ if got := sanitizeCookiePath(tt.in); got != tt.want {
+ t.Errorf("sanitizeCookiePath(%q) = %q; want %q", tt.in, got, tt.want)
+ }
+ }
+}
diff --git a/src/pkg/net/http/cookiejar/jar.go b/src/pkg/net/http/cookiejar/jar.go
index 5977d48b6..389ab58e4 100644
--- a/src/pkg/net/http/cookiejar/jar.go
+++ b/src/pkg/net/http/cookiejar/jar.go
@@ -142,7 +142,7 @@ func (e *entry) pathMatch(requestPath string) bool {
return false
}
-// hasDotSuffix returns whether s ends in "."+suffix.
+// hasDotSuffix reports whether s ends in "."+suffix.
func hasDotSuffix(s, suffix string) bool {
return len(s) > len(suffix) && s[len(s)-len(suffix)-1] == '.' && s[len(s)-len(suffix):] == suffix
}
@@ -316,7 +316,7 @@ func canonicalHost(host string) (string, error) {
return toASCII(host)
}
-// hasPort returns whether host contains a port number. host may be a host
+// hasPort reports whether host contains a port number. host may be a host
// name, an IPv4 or an IPv6 address.
func hasPort(host string) bool {
colons := strings.Count(host, ":")
@@ -357,7 +357,7 @@ func jarKey(host string, psl PublicSuffixList) string {
return host[prevDot+1:]
}
-// isIP returns whether host is an IP address.
+// isIP reports whether host is an IP address.
func isIP(host string) bool {
return net.ParseIP(host) != nil
}
@@ -380,7 +380,7 @@ func defaultPath(path string) string {
// is compared to c.Expires to determine deletion of c. defPath and host are the
// default-path and the canonical host name of the URL c was received from.
//
-// remove is whether the jar should delete this cookie, as it has already
+// remove records whether the jar should delete this cookie, as it has already
// expired with respect to now. In this case, e may be incomplete, but it will
// be valid to call e.id (which depends on e's Name, Domain and Path).
//
diff --git a/src/pkg/net/http/doc.go b/src/pkg/net/http/doc.go
index b6ae8b87a..b1216e8da 100644
--- a/src/pkg/net/http/doc.go
+++ b/src/pkg/net/http/doc.go
@@ -5,7 +5,7 @@
/*
Package http provides HTTP client and server implementations.
-Get, Head, Post, and PostForm make HTTP requests:
+Get, Head, Post, and PostForm make HTTP (or HTTPS) requests:
resp, err := http.Get("http://example.com/")
...
diff --git a/src/pkg/net/http/example_test.go b/src/pkg/net/http/example_test.go
index bc60df7f2..88b97d9e3 100644
--- a/src/pkg/net/http/example_test.go
+++ b/src/pkg/net/http/example_test.go
@@ -68,3 +68,21 @@ func ExampleStripPrefix() {
// URL's path before the FileServer sees it:
http.Handle("/tmpfiles/", http.StripPrefix("/tmpfiles/", http.FileServer(http.Dir("/tmp"))))
}
+
+type apiHandler struct{}
+
+func (apiHandler) ServeHTTP(http.ResponseWriter, *http.Request) {}
+
+func ExampleServeMux_Handle() {
+ mux := http.NewServeMux()
+ mux.Handle("/api/", apiHandler{})
+ mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
+ // The "/" pattern matches everything, so we need to check
+ // that we're at the root here.
+ if req.URL.Path != "/" {
+ http.NotFound(w, req)
+ return
+ }
+ fmt.Fprintf(w, "Welcome to the home page!")
+ })
+}
diff --git a/src/pkg/net/http/export_test.go b/src/pkg/net/http/export_test.go
index 3fc245326..22b7f2796 100644
--- a/src/pkg/net/http/export_test.go
+++ b/src/pkg/net/http/export_test.go
@@ -16,6 +16,8 @@ func NewLoggingConn(baseName string, c net.Conn) net.Conn {
return newLoggingConn(baseName, c)
}
+var ExportAppendTime = appendTime
+
func (t *Transport) NumPendingRequestsForTesting() int {
t.reqMu.Lock()
defer t.reqMu.Unlock()
@@ -48,6 +50,12 @@ func (t *Transport) IdleConnCountForTesting(cacheKey string) int {
return len(conns)
}
+func (t *Transport) IdleConnChMapSizeForTesting() int {
+ t.idleMu.Lock()
+ defer t.idleMu.Unlock()
+ return len(t.idleConnCh)
+}
+
func NewTestTimeoutHandler(handler Handler, ch <-chan time.Time) Handler {
f := func() <-chan time.Time {
return ch
diff --git a/src/pkg/net/http/fs.go b/src/pkg/net/http/fs.go
index b6bea0dfa..8b32ca1d0 100644
--- a/src/pkg/net/http/fs.go
+++ b/src/pkg/net/http/fs.go
@@ -105,23 +105,31 @@ func dirList(w ResponseWriter, f File) {
//
// Note that *os.File implements the io.ReadSeeker interface.
func ServeContent(w ResponseWriter, req *Request, name string, modtime time.Time, content io.ReadSeeker) {
- size, err := content.Seek(0, os.SEEK_END)
- if err != nil {
- Error(w, "seeker can't seek", StatusInternalServerError)
- return
- }
- _, err = content.Seek(0, os.SEEK_SET)
- if err != nil {
- Error(w, "seeker can't seek", StatusInternalServerError)
- return
+ sizeFunc := func() (int64, error) {
+ size, err := content.Seek(0, os.SEEK_END)
+ if err != nil {
+ return 0, errSeeker
+ }
+ _, err = content.Seek(0, os.SEEK_SET)
+ if err != nil {
+ return 0, errSeeker
+ }
+ return size, nil
}
- serveContent(w, req, name, modtime, size, content)
+ serveContent(w, req, name, modtime, sizeFunc, content)
}
+// errSeeker is returned by ServeContent's sizeFunc when the content
+// doesn't seek properly. The underlying Seeker's error text isn't
+// included in the sizeFunc reply so it's not sent over HTTP to end
+// users.
+var errSeeker = errors.New("seeker can't seek")
+
// if name is empty, filename is unknown. (used for mime type, before sniffing)
// if modtime.IsZero(), modtime is unknown.
// content must be seeked to the beginning of the file.
-func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time, size int64, content io.ReadSeeker) {
+// The sizeFunc is called at most once. Its error, if any, is sent in the HTTP response.
+func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time, sizeFunc func() (int64, error), content io.ReadSeeker) {
if checkLastModified(w, r, modtime) {
return
}
@@ -132,16 +140,17 @@ func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time,
code := StatusOK
- // If Content-Type isn't set, use the file's extension to find it.
- ctype := w.Header().Get("Content-Type")
- if ctype == "" {
+ // If Content-Type isn't set, use the file's extension to find it, but
+ // if the Content-Type is unset explicitly, do not sniff the type.
+ ctypes, haveType := w.Header()["Content-Type"]
+ var ctype string
+ if !haveType {
ctype = mime.TypeByExtension(filepath.Ext(name))
if ctype == "" {
// read a chunk to decide between utf-8 text and binary
- var buf [1024]byte
+ var buf [sniffLen]byte
n, _ := io.ReadFull(content, buf[:])
- b := buf[:n]
- ctype = DetectContentType(b)
+ ctype = DetectContentType(buf[:n])
_, err := content.Seek(0, os.SEEK_SET) // rewind to output whole file
if err != nil {
Error(w, "seeker can't seek", StatusInternalServerError)
@@ -149,6 +158,14 @@ func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time,
}
}
w.Header().Set("Content-Type", ctype)
+ } else if len(ctypes) > 0 {
+ ctype = ctypes[0]
+ }
+
+ size, err := sizeFunc()
+ if err != nil {
+ Error(w, err.Error(), StatusInternalServerError)
+ return
}
// handle Content-Range header.
@@ -160,7 +177,7 @@ func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time,
Error(w, err.Error(), StatusRequestedRangeNotSatisfiable)
return
}
- if sumRangesSize(ranges) >= size {
+ if sumRangesSize(ranges) > size {
// The total number of bytes in all the ranges
// is larger than the size of the file by
// itself, so this is probably an attack, or a
@@ -378,7 +395,8 @@ func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirec
}
// serverContent will check modification time
- serveContent(w, r, d.Name(), d.ModTime(), d.Size(), f)
+ sizeFunc := func() (int64, error) { return d.Size(), nil }
+ serveContent(w, r, d.Name(), d.ModTime(), sizeFunc, f)
}
// localRedirect gives a Moved Permanently response.
diff --git a/src/pkg/net/http/fs_test.go b/src/pkg/net/http/fs_test.go
index 2c3737653..ae54edf0c 100644
--- a/src/pkg/net/http/fs_test.go
+++ b/src/pkg/net/http/fs_test.go
@@ -20,8 +20,10 @@ import (
"os/exec"
"path"
"path/filepath"
+ "reflect"
"regexp"
"runtime"
+ "strconv"
"strings"
"testing"
"time"
@@ -36,6 +38,8 @@ type wantRange struct {
start, end int64 // range [start,end)
}
+var itoa = strconv.Itoa
+
var ServeFileRangeTests = []struct {
r string
code int
@@ -50,7 +54,11 @@ var ServeFileRangeTests = []struct {
{r: "bytes=0-0,-2", code: StatusPartialContent, ranges: []wantRange{{0, 1}, {testFileLen - 2, testFileLen}}},
{r: "bytes=0-1,5-8", code: StatusPartialContent, ranges: []wantRange{{0, 2}, {5, 9}}},
{r: "bytes=0-1,5-", code: StatusPartialContent, ranges: []wantRange{{0, 2}, {5, testFileLen}}},
+ {r: "bytes=5-1000", code: StatusPartialContent, ranges: []wantRange{{5, testFileLen}}},
{r: "bytes=0-,1-,2-,3-,4-", code: StatusOK}, // ignore wasteful range request
+ {r: "bytes=0-" + itoa(testFileLen-2), code: StatusPartialContent, ranges: []wantRange{{0, testFileLen - 1}}},
+ {r: "bytes=0-" + itoa(testFileLen-1), code: StatusPartialContent, ranges: []wantRange{{0, testFileLen}}},
+ {r: "bytes=0-" + itoa(testFileLen), code: StatusPartialContent, ranges: []wantRange{{0, testFileLen}}},
}
func TestServeFile(t *testing.T) {
@@ -259,6 +267,9 @@ func TestFileServerImplicitLeadingSlash(t *testing.T) {
}
func TestDirJoin(t *testing.T) {
+ if runtime.GOOS == "windows" {
+ t.Skip("skipping test on windows")
+ }
wfi, err := os.Stat("/etc/hosts")
if err != nil {
t.Skip("skipping test; no /etc/hosts file")
@@ -309,24 +320,29 @@ func TestServeFileContentType(t *testing.T) {
defer afterTest(t)
const ctype = "icecream/chocolate"
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
- if r.FormValue("override") == "1" {
+ switch r.FormValue("override") {
+ case "1":
w.Header().Set("Content-Type", ctype)
+ case "2":
+ // Explicitly inhibit sniffing.
+ w.Header()["Content-Type"] = []string{}
}
ServeFile(w, r, "testdata/file")
}))
defer ts.Close()
- get := func(override, want string) {
+ get := func(override string, want []string) {
resp, err := Get(ts.URL + "?override=" + override)
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)
+ if h := resp.Header["Content-Type"]; !reflect.DeepEqual(h, want) {
+ t.Errorf("Content-Type mismatch: got %v, want %v", h, want)
}
resp.Body.Close()
}
- get("0", "text/plain; charset=utf-8")
- get("1", ctype)
+ get("0", []string{"text/plain; charset=utf-8"})
+ get("1", []string{ctype})
+ get("2", nil)
}
func TestServeFileMimeType(t *testing.T) {
@@ -567,7 +583,10 @@ func TestServeContent(t *testing.T) {
defer ts.Close()
type testCase struct {
- file string
+ // One of file or content must be set:
+ file string
+ content io.ReadSeeker
+
modtime time.Time
serveETag string // optional
serveContentType string // optional
@@ -615,6 +634,14 @@ func TestServeContent(t *testing.T) {
},
wantStatus: 304,
},
+ "not_modified_etag_no_seek": {
+ content: panicOnSeek{nil}, // should never be called
+ serveETag: `"foo"`,
+ reqHeader: map[string]string{
+ "If-None-Match": `"foo"`,
+ },
+ wantStatus: 304,
+ },
"range_good": {
file: "testdata/style.css",
serveETag: `"A"`,
@@ -638,15 +665,21 @@ func TestServeContent(t *testing.T) {
},
}
for testName, tt := range tests {
- f, err := os.Open(tt.file)
- if err != nil {
- t.Fatalf("test %q: %v", testName, err)
+ var content io.ReadSeeker
+ if tt.file != "" {
+ f, err := os.Open(tt.file)
+ if err != nil {
+ t.Fatalf("test %q: %v", testName, err)
+ }
+ defer f.Close()
+ content = f
+ } else {
+ content = tt.content
}
- defer f.Close()
servec <- serveParam{
name: filepath.Base(tt.file),
- content: f,
+ content: content,
modtime: tt.modtime,
etag: tt.serveETag,
contentType: tt.serveContentType,
@@ -763,3 +796,5 @@ func TestLinuxSendfileChild(*testing.T) {
panic(err)
}
}
+
+type panicOnSeek struct{ io.ReadSeeker }
diff --git a/src/pkg/net/http/header.go b/src/pkg/net/http/header.go
index 6374237fb..ca1ae07c2 100644
--- a/src/pkg/net/http/header.go
+++ b/src/pkg/net/http/header.go
@@ -173,7 +173,7 @@ func (h Header) WriteSubset(w io.Writer, exclude map[string]bool) error {
// canonical key for "accept-encoding" is "Accept-Encoding".
func CanonicalHeaderKey(s string) string { return textproto.CanonicalMIMEHeaderKey(s) }
-// hasToken returns whether token appears with v, ASCII
+// hasToken reports whether token appears with v, ASCII
// case-insensitive, with space or comma boundaries.
// token must be all lowercase.
// v may contain mixed cased.
diff --git a/src/pkg/net/http/header_test.go b/src/pkg/net/http/header_test.go
index a2b82a701..9fd9837a5 100644
--- a/src/pkg/net/http/header_test.go
+++ b/src/pkg/net/http/header_test.go
@@ -193,6 +193,9 @@ func BenchmarkHeaderWriteSubset(b *testing.B) {
}
func TestHeaderWriteSubsetMallocs(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping malloc count in short mode")
+ }
if runtime.GOMAXPROCS(0) > 1 {
t.Skip("skipping; GOMAXPROCS>1")
}
@@ -201,6 +204,6 @@ func TestHeaderWriteSubsetMallocs(t *testing.T) {
testHeader.WriteSubset(&buf, nil)
})
if n > 0 {
- t.Errorf("mallocs = %d; want 0", n)
+ t.Errorf("mallocs = %g; want 0", n)
}
}
diff --git a/src/pkg/net/http/httputil/dump.go b/src/pkg/net/http/httputil/dump.go
index 0b0035661..265499fb0 100644
--- a/src/pkg/net/http/httputil/dump.go
+++ b/src/pkg/net/http/httputil/dump.go
@@ -45,13 +45,27 @@ func (c *dumpConn) SetDeadline(t time.Time) error { return nil }
func (c *dumpConn) SetReadDeadline(t time.Time) error { return nil }
func (c *dumpConn) SetWriteDeadline(t time.Time) error { return nil }
+type neverEnding byte
+
+func (b neverEnding) Read(p []byte) (n int, err error) {
+ for i := range p {
+ p[i] = byte(b)
+ }
+ return len(p), nil
+}
+
// DumpRequestOut is like DumpRequest but includes
// headers that the standard http.Transport adds,
// such as User-Agent.
func DumpRequestOut(req *http.Request, body bool) ([]byte, error) {
save := req.Body
+ dummyBody := false
if !body || req.Body == nil {
req.Body = nil
+ if req.ContentLength != 0 {
+ req.Body = ioutil.NopCloser(io.LimitReader(neverEnding('x'), req.ContentLength))
+ dummyBody = true
+ }
} else {
var err error
save, req.Body, err = drainBody(req.Body)
@@ -99,7 +113,19 @@ func DumpRequestOut(req *http.Request, body bool) ([]byte, error) {
if err != nil {
return nil, err
}
- return buf.Bytes(), nil
+ dump := buf.Bytes()
+
+ // If we used a dummy body above, remove it now.
+ // TODO: if the req.ContentLength is large, we allocate memory
+ // unnecessarily just to slice it off here. But this is just
+ // a debug function, so this is acceptable for now. We could
+ // discard the body earlier if this matters.
+ if dummyBody {
+ if i := bytes.Index(dump, []byte("\r\n\r\n")); i >= 0 {
+ dump = dump[:i+4]
+ }
+ }
+ return dump, nil
}
// delegateReader is a reader that delegates to another reader,
diff --git a/src/pkg/net/http/httputil/dump_test.go b/src/pkg/net/http/httputil/dump_test.go
index 3e87c27bc..987a82048 100644
--- a/src/pkg/net/http/httputil/dump_test.go
+++ b/src/pkg/net/http/httputil/dump_test.go
@@ -20,6 +20,7 @@ type dumpTest struct {
WantDump string
WantDumpOut string
+ NoBody bool // if true, set DumpRequest{,Out} body to false
}
var dumpTests = []dumpTest{
@@ -83,6 +84,31 @@ var dumpTests = []dumpTest{
"User-Agent: Go 1.1 package http\r\n" +
"Accept-Encoding: gzip\r\n\r\n",
},
+
+ // Request with Body, but Dump requested without it.
+ {
+ Req: http.Request{
+ Method: "POST",
+ URL: &url.URL{
+ Scheme: "http",
+ Host: "post.tld",
+ Path: "/",
+ },
+ ContentLength: 6,
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ },
+
+ Body: []byte("abcdef"),
+
+ WantDumpOut: "POST / HTTP/1.1\r\n" +
+ "Host: post.tld\r\n" +
+ "User-Agent: Go 1.1 package http\r\n" +
+ "Content-Length: 6\r\n" +
+ "Accept-Encoding: gzip\r\n\r\n",
+
+ NoBody: true,
+ },
}
func TestDumpRequest(t *testing.T) {
@@ -105,7 +131,7 @@ func TestDumpRequest(t *testing.T) {
if tt.WantDump != "" {
setBody()
- dump, err := DumpRequest(&tt.Req, true)
+ dump, err := DumpRequest(&tt.Req, !tt.NoBody)
if err != nil {
t.Errorf("DumpRequest #%d: %s", i, err)
continue
@@ -118,7 +144,7 @@ func TestDumpRequest(t *testing.T) {
if tt.WantDumpOut != "" {
setBody()
- dump, err := DumpRequestOut(&tt.Req, true)
+ dump, err := DumpRequestOut(&tt.Req, !tt.NoBody)
if err != nil {
t.Errorf("DumpRequestOut #%d: %s", i, err)
continue
diff --git a/src/pkg/net/http/request.go b/src/pkg/net/http/request.go
index 6d4569146..57b5d0948 100644
--- a/src/pkg/net/http/request.go
+++ b/src/pkg/net/http/request.go
@@ -10,7 +10,6 @@ import (
"bufio"
"bytes"
"crypto/tls"
- "encoding/base64"
"errors"
"fmt"
"io"
@@ -106,7 +105,16 @@ type Request struct {
// following a hyphen uppercase and the rest lowercase.
Header Header
- // The message body.
+ // Body is the request's body.
+ //
+ // For client requests, a nil body means the request has no
+ // body, such as a GET request. The HTTP Client's Transport
+ // is responsible for calling the Close method.
+ //
+ // For server requests, the Request Body is always non-nil
+ // but will return EOF immediately when no body is present.
+ // The Server will close the request body. The ServeHTTP
+ // Handler does not need to.
Body io.ReadCloser
// ContentLength records the length of the associated content.
@@ -183,7 +191,7 @@ type Request struct {
TLS *tls.ConnectionState
}
-// ProtoAtLeast returns whether the HTTP protocol used
+// ProtoAtLeast reports whether the HTTP protocol used
// in the request is at least major.minor.
func (r *Request) ProtoAtLeast(major, minor int) bool {
return r.ProtoMajor > major ||
@@ -216,7 +224,7 @@ func (r *Request) Cookie(name string) (*Cookie, error) {
// means all cookies, if any, are written into the same line,
// separated by semicolon.
func (r *Request) AddCookie(c *Cookie) {
- s := fmt.Sprintf("%s=%s", sanitizeName(c.Name), sanitizeValue(c.Value))
+ s := fmt.Sprintf("%s=%s", sanitizeCookieName(c.Name), sanitizeCookieValue(c.Value))
if c := r.Header.Get("Cookie"); c != "" {
r.Header.Set("Cookie", c+"; "+s)
} else {
@@ -283,6 +291,11 @@ func valueOrDefault(value, def string) string {
return def
}
+// NOTE: This is not intended to reflect the actual Go version being used.
+// It was changed from "Go http package" to "Go 1.1 package http" at the
+// time of the Go 1.1 release because the former User-Agent had ended up
+// on a blacklist for some intrusion detection systems.
+// See https://codereview.appspot.com/7532043.
const defaultUserAgent = "Go 1.1 package http"
// Write writes an HTTP/1.1 request -- header and body -- in wire format.
@@ -424,6 +437,10 @@ func ParseHTTPVersion(vers string) (major, minor int, ok bool) {
}
// NewRequest returns a new Request given a method, URL, and optional body.
+//
+// If the provided body is also an io.Closer, the returned
+// Request.Body is set to body and will be closed by the Client
+// methods Do, Post, and PostForm, and Transport.RoundTrip.
func NewRequest(method, urlStr string, body io.Reader) (*Request, error) {
u, err := url.Parse(urlStr)
if err != nil {
@@ -463,8 +480,7 @@ func NewRequest(method, urlStr string, body io.Reader) (*Request, error) {
// With HTTP Basic Authentication the provided username and password
// are not encrypted.
func (r *Request) SetBasicAuth(username, password string) {
- s := username + ":" + password
- r.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(s)))
+ r.Header.Set("Authorization", "Basic "+basicAuth(username, password))
}
// parseRequestLine parses "GET /foo HTTP/1.1" into its three parts.
diff --git a/src/pkg/net/http/request_test.go b/src/pkg/net/http/request_test.go
index 692485c49..89303c336 100644
--- a/src/pkg/net/http/request_test.go
+++ b/src/pkg/net/http/request_test.go
@@ -332,7 +332,7 @@ func TestRequestWriteBufferedWriter(t *testing.T) {
func testMissingFile(t *testing.T, req *Request) {
f, fh, err := req.FormFile("missing")
if f != nil {
- t.Errorf("FormFile file = %q, want nil", f)
+ t.Errorf("FormFile file = %v, want nil", f)
}
if fh != nil {
t.Errorf("FormFile file header = %q, want nil", fh)
diff --git a/src/pkg/net/http/response.go b/src/pkg/net/http/response.go
index 9a7e4e319..35d0ba3bb 100644
--- a/src/pkg/net/http/response.go
+++ b/src/pkg/net/http/response.go
@@ -32,7 +32,7 @@ type Response struct {
ProtoMinor int // e.g. 0
// Header maps header keys to values. If the response had multiple
- // headers with the same key, they will be concatenated, with comma
+ // headers with the same key, they may be concatenated, with comma
// delimiters. (Section 4.2 of RFC 2616 requires that multiple headers
// be semantically equivalent to a comma-delimited sequence.) Values
// duplicated by other fields in this struct (e.g., ContentLength) are
@@ -98,18 +98,17 @@ func (r *Response) Location() (*url.URL, error) {
return url.Parse(lv)
}
-// ReadResponse reads and returns an HTTP response from r. The
-// req parameter specifies the Request that corresponds to
-// this Response. Clients must call resp.Body.Close when finished
-// reading resp.Body. After that call, clients can inspect
-// resp.Trailer to find key/value pairs included in the response
-// trailer.
-func ReadResponse(r *bufio.Reader, req *Request) (resp *Response, err error) {
-
+// ReadResponse reads and returns an HTTP response from r.
+// The req parameter optionally specifies the Request that corresponds
+// to this Response. If nil, a GET request is assumed.
+// Clients must call resp.Body.Close when finished reading resp.Body.
+// After that call, clients can inspect resp.Trailer to find key/value
+// pairs included in the response trailer.
+func ReadResponse(r *bufio.Reader, req *Request) (*Response, error) {
tp := textproto.NewReader(r)
- resp = new(Response)
-
- resp.Request = req
+ resp := &Response{
+ Request: req,
+ }
// Parse the first line of the response.
line, err := tp.ReadLine()
@@ -168,7 +167,7 @@ func fixPragmaCacheControl(header Header) {
}
}
-// ProtoAtLeast returns whether the HTTP protocol used
+// ProtoAtLeast reports whether the HTTP protocol used
// in the response is at least major.minor.
func (r *Response) ProtoAtLeast(major, minor int) bool {
return r.ProtoMajor > major ||
diff --git a/src/pkg/net/http/response_test.go b/src/pkg/net/http/response_test.go
index 02796e88b..5044306a8 100644
--- a/src/pkg/net/http/response_test.go
+++ b/src/pkg/net/http/response_test.go
@@ -348,6 +348,29 @@ some body`,
"some body",
},
+
+ // Unchunked response without Content-Length, Request is nil
+ {
+ "HTTP/1.0 200 OK\r\n" +
+ "Connection: close\r\n" +
+ "\r\n" +
+ "Body here\n",
+
+ Response{
+ Status: "200 OK",
+ StatusCode: 200,
+ Proto: "HTTP/1.0",
+ ProtoMajor: 1,
+ ProtoMinor: 0,
+ Header: Header{
+ "Connection": {"close"}, // TODO(rsc): Delete?
+ },
+ Close: true,
+ ContentLength: -1,
+ },
+
+ "Body here\n",
+ },
}
func TestReadResponse(t *testing.T) {
@@ -565,3 +588,42 @@ func TestResponseStatusStutter(t *testing.T) {
t.Errorf("stutter in status: %s", buf.String())
}
}
+
+func TestResponseContentLengthShortBody(t *testing.T) {
+ const shortBody = "Short body, not 123 bytes."
+ br := bufio.NewReader(strings.NewReader("HTTP/1.1 200 OK\r\n" +
+ "Content-Length: 123\r\n" +
+ "\r\n" +
+ shortBody))
+ res, err := ReadResponse(br, &Request{Method: "GET"})
+ if err != nil {
+ t.Fatal(err)
+ }
+ if res.ContentLength != 123 {
+ t.Fatalf("Content-Length = %d; want 123", res.ContentLength)
+ }
+ var buf bytes.Buffer
+ n, err := io.Copy(&buf, res.Body)
+ if n != int64(len(shortBody)) {
+ t.Errorf("Copied %d bytes; want %d, len(%q)", n, len(shortBody), shortBody)
+ }
+ if buf.String() != shortBody {
+ t.Errorf("Read body %q; want %q", buf.String(), shortBody)
+ }
+ if err != io.ErrUnexpectedEOF {
+ t.Errorf("io.Copy error = %#v; want io.ErrUnexpectedEOF", err)
+ }
+}
+
+func TestNeedsSniff(t *testing.T) {
+ // needsSniff returns true with an empty response.
+ r := &response{}
+ if got, want := r.needsSniff(), true; got != want {
+ t.Errorf("needsSniff = %t; want %t", got, want)
+ }
+ // needsSniff returns false when Content-Type = nil.
+ r.handlerHeader = Header{"Content-Type": nil}
+ if got, want := r.needsSniff(), false; got != want {
+ t.Errorf("needsSniff empty Content-Type = %t; want %t", got, want)
+ }
+}
diff --git a/src/pkg/net/http/serve_test.go b/src/pkg/net/http/serve_test.go
index d7b321597..955112bc2 100644
--- a/src/pkg/net/http/serve_test.go
+++ b/src/pkg/net/http/serve_test.go
@@ -116,6 +116,34 @@ func (c *testConn) Close() error {
return nil
}
+// reqBytes treats req as a request (with \n delimiters) and returns it with \r\n delimiters,
+// ending in \r\n\r\n
+func reqBytes(req string) []byte {
+ return []byte(strings.Replace(strings.TrimSpace(req), "\n", "\r\n", -1) + "\r\n\r\n")
+}
+
+type handlerTest struct {
+ handler Handler
+}
+
+func newHandlerTest(h Handler) handlerTest {
+ return handlerTest{h}
+}
+
+func (ht handlerTest) rawResponse(req string) string {
+ reqb := reqBytes(req)
+ var output bytes.Buffer
+ conn := &rwTestConn{
+ Reader: bytes.NewReader(reqb),
+ Writer: &output,
+ closec: make(chan bool, 1),
+ }
+ ln := &oneConnListener{conn: conn}
+ go Serve(ln, ht.handler)
+ <-conn.closec
+ return output.String()
+}
+
func TestConsumingBodyOnNextConn(t *testing.T) {
conn := new(testConn)
for i := 0; i < 2; i++ {
@@ -241,6 +269,152 @@ func TestHostHandlers(t *testing.T) {
}
}
+var serveMuxRegister = []struct {
+ pattern string
+ h Handler
+}{
+ {"/dir/", serve(200)},
+ {"/search", serve(201)},
+ {"codesearch.google.com/search", serve(202)},
+ {"codesearch.google.com/", serve(203)},
+ {"example.com/", HandlerFunc(checkQueryStringHandler)},
+}
+
+// serve returns a handler that sends a response with the given code.
+func serve(code int) HandlerFunc {
+ return func(w ResponseWriter, r *Request) {
+ w.WriteHeader(code)
+ }
+}
+
+// checkQueryStringHandler checks if r.URL.RawQuery has the same value
+// as the URL excluding the scheme and the query string and sends 200
+// response code if it is, 500 otherwise.
+func checkQueryStringHandler(w ResponseWriter, r *Request) {
+ u := *r.URL
+ u.Scheme = "http"
+ u.Host = r.Host
+ u.RawQuery = ""
+ if "http://"+r.URL.RawQuery == u.String() {
+ w.WriteHeader(200)
+ } else {
+ w.WriteHeader(500)
+ }
+}
+
+var serveMuxTests = []struct {
+ method string
+ host string
+ path string
+ code int
+ pattern string
+}{
+ {"GET", "google.com", "/", 404, ""},
+ {"GET", "google.com", "/dir", 301, "/dir/"},
+ {"GET", "google.com", "/dir/", 200, "/dir/"},
+ {"GET", "google.com", "/dir/file", 200, "/dir/"},
+ {"GET", "google.com", "/search", 201, "/search"},
+ {"GET", "google.com", "/search/", 404, ""},
+ {"GET", "google.com", "/search/foo", 404, ""},
+ {"GET", "codesearch.google.com", "/search", 202, "codesearch.google.com/search"},
+ {"GET", "codesearch.google.com", "/search/", 203, "codesearch.google.com/"},
+ {"GET", "codesearch.google.com", "/search/foo", 203, "codesearch.google.com/"},
+ {"GET", "codesearch.google.com", "/", 203, "codesearch.google.com/"},
+ {"GET", "images.google.com", "/search", 201, "/search"},
+ {"GET", "images.google.com", "/search/", 404, ""},
+ {"GET", "images.google.com", "/search/foo", 404, ""},
+ {"GET", "google.com", "/../search", 301, "/search"},
+ {"GET", "google.com", "/dir/..", 301, ""},
+ {"GET", "google.com", "/dir/..", 301, ""},
+ {"GET", "google.com", "/dir/./file", 301, "/dir/"},
+
+ // The /foo -> /foo/ redirect applies to CONNECT requests
+ // but the path canonicalization does not.
+ {"CONNECT", "google.com", "/dir", 301, "/dir/"},
+ {"CONNECT", "google.com", "/../search", 404, ""},
+ {"CONNECT", "google.com", "/dir/..", 200, "/dir/"},
+ {"CONNECT", "google.com", "/dir/..", 200, "/dir/"},
+ {"CONNECT", "google.com", "/dir/./file", 200, "/dir/"},
+}
+
+func TestServeMuxHandler(t *testing.T) {
+ mux := NewServeMux()
+ for _, e := range serveMuxRegister {
+ mux.Handle(e.pattern, e.h)
+ }
+
+ for _, tt := range serveMuxTests {
+ r := &Request{
+ Method: tt.method,
+ Host: tt.host,
+ URL: &url.URL{
+ Path: tt.path,
+ },
+ }
+ h, pattern := mux.Handler(r)
+ rr := httptest.NewRecorder()
+ h.ServeHTTP(rr, r)
+ if pattern != tt.pattern || rr.Code != tt.code {
+ t.Errorf("%s %s %s = %d, %q, want %d, %q", tt.method, tt.host, tt.path, rr.Code, pattern, tt.code, tt.pattern)
+ }
+ }
+}
+
+var serveMuxTests2 = []struct {
+ method string
+ host string
+ url string
+ code int
+ redirOk bool
+}{
+ {"GET", "google.com", "/", 404, false},
+ {"GET", "example.com", "/test/?example.com/test/", 200, false},
+ {"GET", "example.com", "test/?example.com/test/", 200, true},
+}
+
+// TestServeMuxHandlerRedirects tests that automatic redirects generated by
+// mux.Handler() shouldn't clear the request's query string.
+func TestServeMuxHandlerRedirects(t *testing.T) {
+ mux := NewServeMux()
+ for _, e := range serveMuxRegister {
+ mux.Handle(e.pattern, e.h)
+ }
+
+ for _, tt := range serveMuxTests2 {
+ tries := 1
+ turl := tt.url
+ for tries > 0 {
+ u, e := url.Parse(turl)
+ if e != nil {
+ t.Fatal(e)
+ }
+ r := &Request{
+ Method: tt.method,
+ Host: tt.host,
+ URL: u,
+ }
+ h, _ := mux.Handler(r)
+ rr := httptest.NewRecorder()
+ h.ServeHTTP(rr, r)
+ if rr.Code != 301 {
+ if rr.Code != tt.code {
+ t.Errorf("%s %s %s = %d, want %d", tt.method, tt.host, tt.url, rr.Code, tt.code)
+ }
+ break
+ }
+ if !tt.redirOk {
+ t.Errorf("%s %s %s, unexpected redirect", tt.method, tt.host, tt.url)
+ break
+ }
+ turl = rr.HeaderMap.Get("Location")
+ tries--
+ }
+ if tries < 0 {
+ t.Errorf("%s %s %s, too many redirects", tt.method, tt.host, tt.url)
+ }
+ }
+}
+
// Tests for http://code.google.com/p/go/issues/detail?id=900
func TestMuxRedirectLeadingSlashes(t *testing.T) {
paths := []string{"//foo.txt", "///foo.txt", "/../../foo.txt"}
@@ -626,22 +800,20 @@ func Test304Responses(t *testing.T) {
}
}
-// TestHeadResponses verifies that responses to HEAD requests don't
-// declare that they're chunking in their response headers, aren't
-// allowed to produce output, and don't set a Content-Type since
-// the real type of the body data cannot be inferred.
+// TestHeadResponses verifies that all MIME type sniffing and Content-Length
+// counting of GET requests also happens on HEAD requests.
func TestHeadResponses(t *testing.T) {
defer afterTest(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)
+ _, err := w.Write([]byte("<html>"))
+ if err != nil {
+ t.Errorf("ResponseWriter.Write: %v", err)
}
// Also exercise the ReaderFrom path
- _, err = io.Copy(w, strings.NewReader("Ignored body"))
- if err != ErrBodyNotAllowed {
- t.Errorf("on Copy, expected ErrBodyNotAllowed, got %v", err)
+ _, err = io.Copy(w, strings.NewReader("789a"))
+ if err != nil {
+ t.Errorf("Copy(ResponseWriter, ...): %v", err)
}
}))
defer ts.Close()
@@ -652,9 +824,11 @@ func TestHeadResponses(t *testing.T) {
if len(res.TransferEncoding) > 0 {
t.Errorf("expected no TransferEncoding; got %v", res.TransferEncoding)
}
- ct := res.Header.Get("Content-Type")
- if ct != "" {
- t.Errorf("expected no Content-Type; got %s", ct)
+ if ct := res.Header.Get("Content-Type"); ct != "text/html; charset=utf-8" {
+ t.Errorf("Content-Type: %q; want text/html; charset=utf-8", ct)
+ }
+ if v := res.ContentLength; v != 10 {
+ t.Errorf("Content-Length: %d; want 10", v)
}
body, err := ioutil.ReadAll(res.Body)
if err != nil {
@@ -975,6 +1149,23 @@ func TestRedirectMunging(t *testing.T) {
}
}
+func TestRedirectBadPath(t *testing.T) {
+ // This used to crash. It's not valid input (bad path), but it
+ // shouldn't crash.
+ rr := httptest.NewRecorder()
+ req := &Request{
+ Method: "GET",
+ URL: &url.URL{
+ Scheme: "http",
+ Path: "not-empty-but-no-leading-slash", // bogus
+ },
+ }
+ Redirect(rr, req, "", 304)
+ if rr.Code != 304 {
+ t.Errorf("Code = %d; want 304", rr.Code)
+ }
+}
+
// TestZeroLengthPostAndResponse exercises an optimization done by the Transport:
// when there is no body (either because the method doesn't permit a body, or an
// explicit Content-Length of zero is present), then the transport can re-use the
@@ -1408,10 +1599,7 @@ For:
func TestCloseNotifierChanLeak(t *testing.T) {
defer afterTest(t)
- req := []byte(strings.Replace(`GET / HTTP/1.0
-Host: golang.org
-
-`, "\n", "\r\n", -1))
+ req := reqBytes("GET / HTTP/1.0\nHost: golang.org")
for i := 0; i < 20; i++ {
var output bytes.Buffer
conn := &rwTestConn{
@@ -1493,11 +1681,6 @@ func TestOptions(t *testing.T) {
// ones, even if the handler modifies them (~erroneously) after the
// first Write.
func TestHeaderToWire(t *testing.T) {
- req := []byte(strings.Replace(`GET / HTTP/1.1
-Host: golang.org
-
-`, "\n", "\r\n", -1))
-
tests := []struct {
name string
handler func(ResponseWriter, *Request)
@@ -1660,17 +1843,10 @@ Host: golang.org
},
}
for _, tc := range tests {
- var output bytes.Buffer
- conn := &rwTestConn{
- Reader: bytes.NewReader(req),
- Writer: &output,
- closec: make(chan bool, 1),
- }
- ln := &oneConnListener{conn: conn}
- go Serve(ln, HandlerFunc(tc.handler))
- <-conn.closec
- if err := tc.check(output.String()); err != nil {
- t.Errorf("%s: %v\nGot response:\n%s", tc.name, err, output.Bytes())
+ ht := newHandlerTest(HandlerFunc(tc.handler))
+ got := ht.rawResponse("GET / HTTP/1.1\nHost: golang.org")
+ if err := tc.check(got); err != nil {
+ t.Errorf("%s: %v\nGot response:\n%s", tc.name, err, got)
}
}
}
@@ -1726,7 +1902,199 @@ func TestAcceptMaxFds(t *testing.T) {
}
}
+func TestWriteAfterHijack(t *testing.T) {
+ req := reqBytes("GET / HTTP/1.1\nHost: golang.org")
+ var buf bytes.Buffer
+ wrotec := make(chan bool, 1)
+ conn := &rwTestConn{
+ Reader: bytes.NewReader(req),
+ Writer: &buf,
+ closec: make(chan bool, 1),
+ }
+ handler := HandlerFunc(func(rw ResponseWriter, r *Request) {
+ conn, bufrw, err := rw.(Hijacker).Hijack()
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ go func() {
+ bufrw.Write([]byte("[hijack-to-bufw]"))
+ bufrw.Flush()
+ conn.Write([]byte("[hijack-to-conn]"))
+ conn.Close()
+ wrotec <- true
+ }()
+ })
+ ln := &oneConnListener{conn: conn}
+ go Serve(ln, handler)
+ <-conn.closec
+ <-wrotec
+ if g, w := buf.String(), "[hijack-to-bufw][hijack-to-conn]"; g != w {
+ t.Errorf("wrote %q; want %q", g, w)
+ }
+}
+
+// http://code.google.com/p/go/issues/detail?id=5955
+// Note that this does not test the "request too large"
+// exit path from the http server. This is intentional;
+// not sending Connection: close is just a minor wire
+// optimization and is pointless if dealing with a
+// badly behaved client.
+func TestHTTP10ConnectionHeader(t *testing.T) {
+ defer afterTest(t)
+
+ mux := NewServeMux()
+ mux.Handle("/", HandlerFunc(func(resp ResponseWriter, req *Request) {}))
+ ts := httptest.NewServer(mux)
+ defer ts.Close()
+
+ // net/http uses HTTP/1.1 for requests, so write requests manually
+ tests := []struct {
+ req string // raw http request
+ expect []string // expected Connection header(s)
+ }{
+ {
+ req: "GET / HTTP/1.0\r\n\r\n",
+ expect: nil,
+ },
+ {
+ req: "OPTIONS * HTTP/1.0\r\n\r\n",
+ expect: nil,
+ },
+ {
+ req: "GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n",
+ expect: []string{"keep-alive"},
+ },
+ }
+
+ for _, tt := range tests {
+ conn, err := net.Dial("tcp", ts.Listener.Addr().String())
+ if err != nil {
+ t.Fatal("dial err:", err)
+ }
+
+ _, err = fmt.Fprint(conn, tt.req)
+ if err != nil {
+ t.Fatal("conn write err:", err)
+ }
+
+ resp, err := ReadResponse(bufio.NewReader(conn), &Request{Method: "GET"})
+ if err != nil {
+ t.Fatal("ReadResponse err:", err)
+ }
+ conn.Close()
+ resp.Body.Close()
+
+ got := resp.Header["Connection"]
+ if !reflect.DeepEqual(got, tt.expect) {
+ t.Errorf("wrong Connection headers for request %q. Got %q expect %q", tt.req, got, tt.expect)
+ }
+ }
+}
+
+// See golang.org/issue/5660
+func TestServerReaderFromOrder(t *testing.T) {
+ defer afterTest(t)
+ pr, pw := io.Pipe()
+ const size = 3 << 20
+ ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) {
+ rw.Header().Set("Content-Type", "text/plain") // prevent sniffing path
+ done := make(chan bool)
+ go func() {
+ io.Copy(rw, pr)
+ close(done)
+ }()
+ time.Sleep(25 * time.Millisecond) // give Copy a chance to break things
+ n, err := io.Copy(ioutil.Discard, req.Body)
+ if err != nil {
+ t.Errorf("handler Copy: %v", err)
+ return
+ }
+ if n != size {
+ t.Errorf("handler Copy = %d; want %d", n, size)
+ }
+ pw.Write([]byte("hi"))
+ pw.Close()
+ <-done
+ }))
+ defer ts.Close()
+
+ req, err := NewRequest("POST", ts.URL, io.LimitReader(neverEnding('a'), size))
+ if err != nil {
+ t.Fatal(err)
+ }
+ res, err := DefaultClient.Do(req)
+ if err != nil {
+ t.Fatal(err)
+ }
+ all, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ t.Fatal(err)
+ }
+ res.Body.Close()
+ if string(all) != "hi" {
+ t.Errorf("Body = %q; want hi", all)
+ }
+}
+
+// Issue 6157
+func TestNoContentTypeOnNotModified(t *testing.T) {
+ ht := newHandlerTest(HandlerFunc(func(w ResponseWriter, r *Request) {
+ if r.URL.Path == "/header" {
+ w.Header().Set("Content-Length", "123")
+ }
+ w.WriteHeader(StatusNotModified)
+ if r.URL.Path == "/more" {
+ w.Write([]byte("stuff"))
+ }
+ }))
+ for _, req := range []string{
+ "GET / HTTP/1.0",
+ "GET /header HTTP/1.0",
+ "GET /more HTTP/1.0",
+ "GET / HTTP/1.1",
+ "GET /header HTTP/1.1",
+ "GET /more HTTP/1.1",
+ } {
+ got := ht.rawResponse(req)
+ if !strings.Contains(got, "304 Not Modified") {
+ t.Errorf("Non-304 Not Modified for %q: %s", req, got)
+ } else if strings.Contains(got, "Content-Length") {
+ t.Errorf("Got a Content-Length from %q: %s", req, got)
+ }
+ }
+}
+
+func TestResponseWriterWriteStringAllocs(t *testing.T) {
+ ht := newHandlerTest(HandlerFunc(func(w ResponseWriter, r *Request) {
+ if r.URL.Path == "/s" {
+ io.WriteString(w, "Hello world")
+ } else {
+ w.Write([]byte("Hello world"))
+ }
+ }))
+ before := testing.AllocsPerRun(25, func() { ht.rawResponse("GET / HTTP/1.0") })
+ after := testing.AllocsPerRun(25, func() { ht.rawResponse("GET /s HTTP/1.0") })
+ if int(after) >= int(before) {
+ t.Errorf("WriteString allocs of %v >= Write allocs of %v", after, before)
+ }
+}
+
+func TestAppendTime(t *testing.T) {
+ var b [len(TimeFormat)]byte
+ t1 := time.Date(2013, 9, 21, 15, 41, 0, 0, time.FixedZone("CEST", 2*60*60))
+ res := ExportAppendTime(b[:0], t1)
+ t2, err := ParseTime(string(res))
+ if err != nil {
+ t.Fatalf("Error parsing time: %s", err)
+ }
+ if !t1.Equal(t2) {
+ t.Fatalf("Times differ; expected: %v, got %v (%s)", t1, t2, string(res))
+ }
+}
+
func BenchmarkClientServer(b *testing.B) {
+ b.ReportAllocs()
b.StopTimer()
ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, r *Request) {
fmt.Fprintf(rw, "Hello world.\n")
@@ -1761,6 +2129,7 @@ func BenchmarkClientServerParallel64(b *testing.B) {
}
func benchmarkClientServerParallel(b *testing.B, conc int) {
+ b.ReportAllocs()
b.StopTimer()
ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, r *Request) {
fmt.Fprintf(rw, "Hello world.\n")
@@ -1805,6 +2174,7 @@ func benchmarkClientServerParallel(b *testing.B, conc int) {
// $ go tool pprof http.test http.prof
// (pprof) web
func BenchmarkServer(b *testing.B) {
+ b.ReportAllocs()
// Child process mode;
if url := os.Getenv("TEST_BENCH_SERVER_URL"); url != "" {
n, err := strconv.Atoi(os.Getenv("TEST_BENCH_CLIENT_N"))
@@ -1851,15 +2221,14 @@ func BenchmarkServer(b *testing.B) {
func BenchmarkServerFakeConnNoKeepAlive(b *testing.B) {
b.ReportAllocs()
- req := []byte(strings.Replace(`GET / HTTP/1.0
+ req := reqBytes(`GET / HTTP/1.0
Host: golang.org
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
-
-`, "\n", "\r\n", -1))
+`)
res := []byte("Hello world!\n")
conn := &testConn{
@@ -1905,15 +2274,14 @@ func (r *repeatReader) Read(p []byte) (n int, err error) {
func BenchmarkServerFakeConnWithKeepAlive(b *testing.B) {
b.ReportAllocs()
- req := []byte(strings.Replace(`GET / HTTP/1.1
+ req := reqBytes(`GET / HTTP/1.1
Host: golang.org
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
-
-`, "\n", "\r\n", -1))
+`)
res := []byte("Hello world!\n")
conn := &rwTestConn{
@@ -1940,10 +2308,9 @@ Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
func BenchmarkServerFakeConnWithKeepAliveLite(b *testing.B) {
b.ReportAllocs()
- req := []byte(strings.Replace(`GET / HTTP/1.1
+ req := reqBytes(`GET / HTTP/1.1
Host: golang.org
-
-`, "\n", "\r\n", -1))
+`)
res := []byte("Hello world!\n")
conn := &rwTestConn{
@@ -2003,10 +2370,9 @@ func BenchmarkServerHandlerNoHeader(b *testing.B) {
func benchmarkHandler(b *testing.B, h Handler) {
b.ReportAllocs()
- req := []byte(strings.Replace(`GET / HTTP/1.1
+ req := reqBytes(`GET / HTTP/1.1
Host: golang.org
-
-`, "\n", "\r\n", -1))
+`)
conn := &rwTestConn{
Reader: &repeatReader{content: req, count: b.N},
Writer: ioutil.Discard,
diff --git a/src/pkg/net/http/server.go b/src/pkg/net/http/server.go
index b25960705..0e46863d5 100644
--- a/src/pkg/net/http/server.go
+++ b/src/pkg/net/http/server.go
@@ -16,6 +16,7 @@ import (
"log"
"net"
"net/url"
+ "os"
"path"
"runtime"
"strconv"
@@ -109,8 +110,6 @@ type conn struct {
sr liveSwitchReader // where the LimitReader reads from; usually the rwc
lr *io.LimitedReader // io.LimitReader(sr)
buf *bufio.ReadWriter // buffered(lr,rwc), reading from bufio->limitReader->sr->rwc
- bufswr *switchReader // the *switchReader io.Reader source of buf
- bufsww *switchWriter // the *switchWriter io.Writer dest of buf
tlsState *tls.ConnectionState // or nil when not using TLS
mu sync.Mutex // guards the following
@@ -246,6 +245,10 @@ func (cw *chunkWriter) Write(p []byte) (n int, err error) {
if !cw.wroteHeader {
cw.writeHeader(p)
}
+ if cw.res.req.Method == "HEAD" {
+ // Eat writes.
+ return len(p), nil
+ }
if cw.chunking {
_, err = fmt.Fprintf(cw.res.conn.buf, "%x\r\n", len(p))
if err != nil {
@@ -278,7 +281,7 @@ func (cw *chunkWriter) close() {
// zero EOF chunk, trailer key/value pairs (currently
// unsupported in Go's server), followed by a blank
// line.
- io.WriteString(cw.res.conn.buf, "0\r\n\r\n")
+ cw.res.conn.buf.WriteString("0\r\n\r\n")
}
}
@@ -320,6 +323,10 @@ type response struct {
requestBodyLimitHit bool
handlerDone bool // set true when the handler exits
+
+ // Buffers for Date and Content-Length
+ dateBuf [len(TimeFormat)]byte
+ clenBuf [10]byte
}
// requestTooLarge is called by maxBytesReader when too much input has
@@ -332,16 +339,50 @@ func (w *response) requestTooLarge() {
}
}
-// needsSniff returns whether a Content-Type still needs to be sniffed.
+// needsSniff reports whether a Content-Type still needs to be sniffed.
func (w *response) needsSniff() bool {
- return !w.cw.wroteHeader && w.handlerHeader.Get("Content-Type") == "" && w.written < sniffLen
+ _, haveType := w.handlerHeader["Content-Type"]
+ return !w.cw.wroteHeader && !haveType && w.written < sniffLen
}
+// writerOnly hides an io.Writer value's optional ReadFrom method
+// from io.Copy.
type writerOnly struct {
io.Writer
}
+func srcIsRegularFile(src io.Reader) (isRegular bool, err error) {
+ switch v := src.(type) {
+ case *os.File:
+ fi, err := v.Stat()
+ if err != nil {
+ return false, err
+ }
+ return fi.Mode().IsRegular(), nil
+ case *io.LimitedReader:
+ return srcIsRegularFile(v.R)
+ default:
+ return
+ }
+}
+
+// ReadFrom is here to optimize copying from an *os.File regular file
+// to a *net.TCPConn with sendfile.
func (w *response) ReadFrom(src io.Reader) (n int64, err error) {
+ // Our underlying w.conn.rwc is usually a *TCPConn (with its
+ // own ReadFrom method). If not, or if our src isn't a regular
+ // file, just fall back to the normal copy method.
+ rf, ok := w.conn.rwc.(io.ReaderFrom)
+ regFile, err := srcIsRegularFile(src)
+ if err != nil {
+ return 0, err
+ }
+ if !ok || !regFile {
+ return io.Copy(writerOnly{w}, src)
+ }
+
+ // sendfile path:
+
if !w.wroteHeader {
w.WriteHeader(StatusOK)
}
@@ -359,16 +400,12 @@ func (w *response) ReadFrom(src io.Reader) (n int64, err error) {
// Now that cw has been flushed, its chunking field is guaranteed initialized.
if !w.cw.chunking && w.bodyAllowed() {
- if rf, ok := w.conn.rwc.(io.ReaderFrom); ok {
- n0, err := rf.ReadFrom(src)
- n += n0
- w.written += n0
- return n, err
- }
+ n0, err := rf.ReadFrom(src)
+ n += n0
+ w.written += n0
+ return n, err
}
- // Fall back to default io.Copy implementation.
- // Use wrapper to hide w.ReadFrom from io.Copy.
n0, err := io.Copy(writerOnly{w}, src)
n += n0
return n, err
@@ -392,34 +429,20 @@ func (srv *Server) newConn(rwc net.Conn) (c *conn, err error) {
}
c.sr = liveSwitchReader{r: c.rwc}
c.lr = io.LimitReader(&c.sr, noLimit).(*io.LimitedReader)
- br, sr := newBufioReader(c.lr)
- bw, sw := newBufioWriterSize(c.rwc, 4<<10)
+ br := newBufioReader(c.lr)
+ bw := newBufioWriterSize(c.rwc, 4<<10)
c.buf = bufio.NewReadWriter(br, bw)
- c.bufswr = sr
- c.bufsww = sw
return c, nil
}
-// TODO: remove this, if issue 5100 is fixed
-type bufioReaderPair struct {
- br *bufio.Reader
- sr *switchReader // from which the bufio.Reader is reading
-}
-
-// TODO: remove this, if issue 5100 is fixed
-type bufioWriterPair struct {
- bw *bufio.Writer
- sw *switchWriter // to which the bufio.Writer is writing
-}
-
// TODO: use a sync.Cache instead
var (
- bufioReaderCache = make(chan bufioReaderPair, 4)
- bufioWriterCache2k = make(chan bufioWriterPair, 4)
- bufioWriterCache4k = make(chan bufioWriterPair, 4)
+ bufioReaderCache = make(chan *bufio.Reader, 4)
+ bufioWriterCache2k = make(chan *bufio.Writer, 4)
+ bufioWriterCache4k = make(chan *bufio.Writer, 4)
)
-func bufioWriterCache(size int) chan bufioWriterPair {
+func bufioWriterCache(size int) chan *bufio.Writer {
switch size {
case 2 << 10:
return bufioWriterCache2k
@@ -429,55 +452,38 @@ func bufioWriterCache(size int) chan bufioWriterPair {
return nil
}
-func newBufioReader(r io.Reader) (*bufio.Reader, *switchReader) {
+func newBufioReader(r io.Reader) *bufio.Reader {
select {
case p := <-bufioReaderCache:
- p.sr.Reader = r
- return p.br, p.sr
+ p.Reset(r)
+ return p
default:
- sr := &switchReader{r}
- return bufio.NewReader(sr), sr
+ return bufio.NewReader(r)
}
}
-func putBufioReader(br *bufio.Reader, sr *switchReader) {
- if n := br.Buffered(); n > 0 {
- io.CopyN(ioutil.Discard, br, int64(n))
- }
- br.Read(nil) // clears br.err
- sr.Reader = nil
+func putBufioReader(br *bufio.Reader) {
+ br.Reset(nil)
select {
- case bufioReaderCache <- bufioReaderPair{br, sr}:
+ case bufioReaderCache <- br:
default:
}
}
-func newBufioWriterSize(w io.Writer, size int) (*bufio.Writer, *switchWriter) {
+func newBufioWriterSize(w io.Writer, size int) *bufio.Writer {
select {
case p := <-bufioWriterCache(size):
- p.sw.Writer = w
- return p.bw, p.sw
+ p.Reset(w)
+ return p
default:
- sw := &switchWriter{w}
- return bufio.NewWriterSize(sw, size), sw
+ return bufio.NewWriterSize(w, size)
}
}
-func putBufioWriter(bw *bufio.Writer, sw *switchWriter) {
- if bw.Buffered() > 0 {
- // It must have failed to flush to its target
- // earlier. We can't reuse this bufio.Writer.
- return
- }
- if err := bw.Flush(); err != nil {
- // Its sticky error field is set, which is returned by
- // Flush even when there's no data buffered. This
- // bufio Writer is dead to us. Don't reuse it.
- return
- }
- sw.Writer = nil
+func putBufioWriter(bw *bufio.Writer) {
+ bw.Reset(nil)
select {
- case bufioWriterCache(bw.Available()) <- bufioWriterPair{bw, sw}:
+ case bufioWriterCache(bw.Available()) <- bw:
default:
}
}
@@ -508,7 +514,7 @@ func (ecr *expectContinueReader) Read(p []byte) (n int, err error) {
}
if !ecr.resp.wroteContinue && !ecr.resp.conn.hijacked() {
ecr.resp.wroteContinue = true
- io.WriteString(ecr.resp.conn.buf, "HTTP/1.1 100 Continue\r\n\r\n")
+ ecr.resp.conn.buf.WriteString("HTTP/1.1 100 Continue\r\n\r\n")
ecr.resp.conn.buf.Flush()
}
return ecr.readCloser.Read(p)
@@ -525,6 +531,28 @@ func (ecr *expectContinueReader) Close() error {
// It is like time.RFC1123 but hard codes GMT as the time zone.
const TimeFormat = "Mon, 02 Jan 2006 15:04:05 GMT"
+// appendTime is a non-allocating version of []byte(t.UTC().Format(TimeFormat))
+func appendTime(b []byte, t time.Time) []byte {
+ const days = "SunMonTueWedThuFriSat"
+ const months = "JanFebMarAprMayJunJulAugSepOctNovDec"
+
+ t = t.UTC()
+ yy, mm, dd := t.Date()
+ hh, mn, ss := t.Clock()
+ day := days[3*t.Weekday():]
+ mon := months[3*(mm-1):]
+
+ return append(b,
+ day[0], day[1], day[2], ',', ' ',
+ byte('0'+dd/10), byte('0'+dd%10), ' ',
+ mon[0], mon[1], mon[2], ' ',
+ byte('0'+yy/1000), byte('0'+(yy/100)%10), byte('0'+(yy/10)%10), byte('0'+yy%10), ' ',
+ byte('0'+hh/10), byte('0'+hh%10), ':',
+ byte('0'+mn/10), byte('0'+mn%10), ':',
+ byte('0'+ss/10), byte('0'+ss%10), ' ',
+ 'G', 'M', 'T')
+}
+
var errTooLarge = errors.New("http: request too large")
// Read next request from connection.
@@ -562,7 +590,7 @@ func (c *conn) readRequest() (w *response, err error) {
contentLength: -1,
}
w.cw.res = w
- w.w, w.sw = newBufioWriterSize(&w.cw, bufferBeforeChunkingSize)
+ w.w = newBufioWriterSize(&w.cw, bufferBeforeChunkingSize)
return w, nil
}
@@ -620,27 +648,45 @@ func (w *response) WriteHeader(code int) {
// the response Header map and all its 1-element slices.
type extraHeader struct {
contentType string
- contentLength string
connection string
- date string
transferEncoding string
+ date []byte // written if not nil
+ contentLength []byte // written if not nil
}
// Sorted the same as extraHeader.Write's loop.
var extraHeaderKeys = [][]byte{
- []byte("Content-Type"), []byte("Content-Length"),
- []byte("Connection"), []byte("Date"), []byte("Transfer-Encoding"),
+ []byte("Content-Type"),
+ []byte("Connection"),
+ []byte("Transfer-Encoding"),
}
-// The value receiver, despite copying 5 strings to the stack,
-// prevents an extra allocation. The escape analysis isn't smart
-// enough to realize this doesn't mutate h.
-func (h extraHeader) Write(w io.Writer) {
- for i, v := range []string{h.contentType, h.contentLength, h.connection, h.date, h.transferEncoding} {
+var (
+ headerContentLength = []byte("Content-Length: ")
+ headerDate = []byte("Date: ")
+)
+
+// Write writes the headers described in h to w.
+//
+// This method has a value receiver, despite the somewhat large size
+// of h, because it prevents an allocation. The escape analysis isn't
+// smart enough to realize this function doesn't mutate h.
+func (h extraHeader) Write(w *bufio.Writer) {
+ if h.date != nil {
+ w.Write(headerDate)
+ w.Write(h.date)
+ w.Write(crlf)
+ }
+ if h.contentLength != nil {
+ w.Write(headerContentLength)
+ w.Write(h.contentLength)
+ w.Write(crlf)
+ }
+ for i, v := range []string{h.contentType, h.connection, h.transferEncoding} {
if v != "" {
w.Write(extraHeaderKeys[i])
w.Write(colonSpace)
- io.WriteString(w, v)
+ w.WriteString(v)
w.Write(crlf)
}
}
@@ -661,6 +707,7 @@ func (cw *chunkWriter) writeHeader(p []byte) {
cw.wroteHeader = true
w := cw.res
+ isHEAD := w.req.Method == "HEAD"
// header is written out to w.conn.buf below. Depending on the
// state of the handler, we either own the map or not. If we
@@ -692,9 +739,17 @@ func (cw *chunkWriter) writeHeader(p []byte) {
// response header and this is our first (and last) write, set
// it, even to zero. This helps HTTP/1.0 clients keep their
// "keep-alive" connections alive.
- if w.handlerDone && header.get("Content-Length") == "" && w.req.Method != "HEAD" {
+ // Exceptions: 304 responses never get Content-Length, and if
+ // it was a HEAD request, we don't know the difference between
+ // 0 actual bytes and 0 bytes because the handler noticed it
+ // was a HEAD request and chose not to write anything. So for
+ // HEAD, the handler should either write the Content-Length or
+ // write non-zero bytes. If it's actually 0 bytes and the
+ // handler never looked at the Request.Method, we just don't
+ // send a Content-Length header.
+ if w.handlerDone && w.status != StatusNotModified && header.get("Content-Length") == "" && (!isHEAD || len(p) > 0) {
w.contentLength = int64(len(p))
- setHeader.contentLength = strconv.Itoa(len(p))
+ setHeader.contentLength = strconv.AppendInt(cw.res.clenBuf[:0], int64(len(p)), 10)
}
// If this was an HTTP/1.0 request with keep-alive and we sent a
@@ -709,7 +764,7 @@ func (cw *chunkWriter) writeHeader(p []byte) {
// Check for a explicit (and valid) Content-Length header.
hasCL := w.contentLength != -1
- if w.req.wantsHttp10KeepAlive() && (w.req.Method == "HEAD" || hasCL) {
+ if w.req.wantsHttp10KeepAlive() && (isHEAD || hasCL) {
_, connectionHeaderSet := header["Connection"]
if !connectionHeaderSet {
setHeader.connection = "keep-alive"
@@ -749,13 +804,14 @@ func (cw *chunkWriter) writeHeader(p []byte) {
}
} else {
// If no content type, apply sniffing algorithm to body.
- if header.get("Content-Type") == "" && w.req.Method != "HEAD" {
+ _, haveType := header["Content-Type"]
+ if !haveType {
setHeader.contentType = DetectContentType(p)
}
}
if _, ok := header["Date"]; !ok {
- setHeader.date = time.Now().UTC().Format(TimeFormat)
+ setHeader.date = appendTime(cw.res.dateBuf[:0], time.Now())
}
te := header.get("Transfer-Encoding")
@@ -801,12 +857,14 @@ func (cw *chunkWriter) writeHeader(p []byte) {
if w.closeAfterReply && !hasToken(cw.header.get("Connection"), "close") {
delHeader("Connection")
- setHeader.connection = "close"
+ if w.req.ProtoAtLeast(1, 1) {
+ setHeader.connection = "close"
+ }
}
- io.WriteString(w.conn.buf, statusLine(w.req, code))
+ w.conn.buf.WriteString(statusLine(w.req, code))
cw.header.WriteSubset(w.conn.buf, excludeHeader)
- setHeader.Write(w.conn.buf)
+ setHeader.Write(w.conn.buf.Writer)
w.conn.buf.Write(crlf)
}
@@ -861,7 +919,7 @@ func (w *response) bodyAllowed() bool {
if !w.wroteHeader {
panic("")
}
- return w.status != StatusNotModified && w.req.Method != "HEAD"
+ return w.status != StatusNotModified
}
// The Life Of A Write is like this:
@@ -897,6 +955,15 @@ func (w *response) bodyAllowed() bool {
// bufferBeforeChunkingSize smaller and having bufio's fast-paths deal
// with this instead.
func (w *response) Write(data []byte) (n int, err error) {
+ return w.write(len(data), data, "")
+}
+
+func (w *response) WriteString(data string) (n int, err error) {
+ return w.write(len(data), nil, data)
+}
+
+// either dataB or dataS is non-zero.
+func (w *response) write(lenData int, dataB []byte, dataS string) (n int, err error) {
if w.conn.hijacked() {
log.Print("http: response.Write on hijacked connection")
return 0, ErrHijacked
@@ -904,18 +971,22 @@ func (w *response) Write(data []byte) (n int, err error) {
if !w.wroteHeader {
w.WriteHeader(StatusOK)
}
- if len(data) == 0 {
+ if lenData == 0 {
return 0, nil
}
if !w.bodyAllowed() {
return 0, ErrBodyNotAllowed
}
- w.written += int64(len(data)) // ignoring errors, for errorKludge
+ w.written += int64(lenData) // ignoring errors, for errorKludge
if w.contentLength != -1 && w.written > w.contentLength {
return 0, ErrContentLength
}
- return w.w.Write(data)
+ if dataB != nil {
+ return w.w.Write(dataB)
+ } else {
+ return w.w.WriteString(dataS)
+ }
}
func (w *response) finishRequest() {
@@ -926,7 +997,7 @@ func (w *response) finishRequest() {
}
w.w.Flush()
- putBufioWriter(w.w, w.sw)
+ putBufioWriter(w.w)
w.cw.close()
w.conn.buf.Flush()
@@ -939,7 +1010,7 @@ func (w *response) finishRequest() {
w.req.MultipartForm.RemoveAll()
}
- if w.contentLength != -1 && w.bodyAllowed() && w.contentLength != w.written {
+ if w.req.Method != "HEAD" && w.contentLength != -1 && w.bodyAllowed() && w.contentLength != w.written {
// Did not write enough. Avoid getting out of sync.
w.closeAfterReply = true
}
@@ -959,11 +1030,11 @@ func (c *conn) finalFlush() {
// Steal the bufio.Reader (~4KB worth of memory) and its associated
// reader for a future connection.
- putBufioReader(c.buf.Reader, c.bufswr)
+ putBufioReader(c.buf.Reader)
// Steal the bufio.Writer (~4KB worth of memory) and its associated
// writer for a future connection.
- putBufioWriter(c.buf.Writer, c.bufsww)
+ putBufioWriter(c.buf.Writer)
c.buf = nil
}
@@ -1001,7 +1072,7 @@ func (c *conn) closeWriteAndWait() {
time.Sleep(rstAvoidanceDelay)
}
-// validNPN returns whether the proto is not a blacklisted Next
+// validNPN reports whether the proto is not a blacklisted Next
// Protocol Negotiation protocol. Empty and built-in protocol types
// are blacklisted and can't be overridden with alternate
// implementations.
@@ -1152,6 +1223,7 @@ func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
// Helper handlers
// Error replies to the request with the specified error message and HTTP code.
+// The error message should be plain text.
func Error(w ResponseWriter, error string, code int) {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.WriteHeader(code)
@@ -1288,6 +1360,10 @@ func RedirectHandler(url string, code int) Handler {
// former will receive requests for any other paths in the
// "/images/" subtree.
//
+// Note that since a pattern ending in a slash names a rooted subtree,
+// the pattern "/" matches all paths not matched by other registered
+// patterns, not just the URL with Path == "/".
+//
// Patterns may optionally begin with a host name, restricting matches to
// URLs on that host only. Host-specific patterns take precedence over
// general patterns, so that a handler might register for the two patterns
@@ -1378,7 +1454,9 @@ func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
if r.Method != "CONNECT" {
if p := cleanPath(r.URL.Path); p != r.URL.Path {
_, pattern = mux.handler(r.Host, p)
- return RedirectHandler(p, StatusMovedPermanently), pattern
+ url := *r.URL
+ url.Path = p
+ return RedirectHandler(url.String(), StatusMovedPermanently), pattern
}
}
@@ -1408,7 +1486,9 @@ func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
// pattern most closely matches the request URL.
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
if r.RequestURI == "*" {
- w.Header().Set("Connection", "close")
+ if r.ProtoAtLeast(1, 1) {
+ w.Header().Set("Connection", "close")
+ }
w.WriteHeader(StatusBadRequest)
return
}
@@ -1771,7 +1851,15 @@ func (globalOptionsHandler) ServeHTTP(w ResponseWriter, r *Request) {
}
// eofReader is a non-nil io.ReadCloser that always returns EOF.
-var eofReader = ioutil.NopCloser(strings.NewReader(""))
+// It embeds a *strings.Reader so it still has a WriteTo method
+// and io.Copy won't need a buffer.
+var eofReader = &struct {
+ *strings.Reader
+ io.Closer
+}{
+ strings.NewReader(""),
+ ioutil.NopCloser(nil),
+}
// initNPNRequest is an HTTP handler that initializes certain
// uninitialized fields in its *Request. Such partially-initialized
diff --git a/src/pkg/net/http/server_test.go b/src/pkg/net/http/server_test.go
deleted file mode 100644
index e8b69f76c..000000000
--- a/src/pkg/net/http/server_test.go
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright 2012 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 http_test
-
-import (
- . "net/http"
- "net/http/httptest"
- "net/url"
- "testing"
-)
-
-var serveMuxRegister = []struct {
- pattern string
- h Handler
-}{
- {"/dir/", serve(200)},
- {"/search", serve(201)},
- {"codesearch.google.com/search", serve(202)},
- {"codesearch.google.com/", serve(203)},
-}
-
-// serve returns a handler that sends a response with the given code.
-func serve(code int) HandlerFunc {
- return func(w ResponseWriter, r *Request) {
- w.WriteHeader(code)
- }
-}
-
-var serveMuxTests = []struct {
- method string
- host string
- path string
- code int
- pattern string
-}{
- {"GET", "google.com", "/", 404, ""},
- {"GET", "google.com", "/dir", 301, "/dir/"},
- {"GET", "google.com", "/dir/", 200, "/dir/"},
- {"GET", "google.com", "/dir/file", 200, "/dir/"},
- {"GET", "google.com", "/search", 201, "/search"},
- {"GET", "google.com", "/search/", 404, ""},
- {"GET", "google.com", "/search/foo", 404, ""},
- {"GET", "codesearch.google.com", "/search", 202, "codesearch.google.com/search"},
- {"GET", "codesearch.google.com", "/search/", 203, "codesearch.google.com/"},
- {"GET", "codesearch.google.com", "/search/foo", 203, "codesearch.google.com/"},
- {"GET", "codesearch.google.com", "/", 203, "codesearch.google.com/"},
- {"GET", "images.google.com", "/search", 201, "/search"},
- {"GET", "images.google.com", "/search/", 404, ""},
- {"GET", "images.google.com", "/search/foo", 404, ""},
- {"GET", "google.com", "/../search", 301, "/search"},
- {"GET", "google.com", "/dir/..", 301, ""},
- {"GET", "google.com", "/dir/..", 301, ""},
- {"GET", "google.com", "/dir/./file", 301, "/dir/"},
-
- // The /foo -> /foo/ redirect applies to CONNECT requests
- // but the path canonicalization does not.
- {"CONNECT", "google.com", "/dir", 301, "/dir/"},
- {"CONNECT", "google.com", "/../search", 404, ""},
- {"CONNECT", "google.com", "/dir/..", 200, "/dir/"},
- {"CONNECT", "google.com", "/dir/..", 200, "/dir/"},
- {"CONNECT", "google.com", "/dir/./file", 200, "/dir/"},
-}
-
-func TestServeMuxHandler(t *testing.T) {
- mux := NewServeMux()
- for _, e := range serveMuxRegister {
- mux.Handle(e.pattern, e.h)
- }
-
- for _, tt := range serveMuxTests {
- r := &Request{
- Method: tt.method,
- Host: tt.host,
- URL: &url.URL{
- Path: tt.path,
- },
- }
- h, pattern := mux.Handler(r)
- rr := httptest.NewRecorder()
- h.ServeHTTP(rr, r)
- if pattern != tt.pattern || rr.Code != tt.code {
- t.Errorf("%s %s %s = %d, %q, want %d, %q", tt.method, tt.host, tt.path, rr.Code, pattern, tt.code, tt.pattern)
- }
- }
-}
-
-func TestServerRedirect(t *testing.T) {
- // This used to crash. It's not valid input (bad path), but it
- // shouldn't crash.
- rr := httptest.NewRecorder()
- req := &Request{
- Method: "GET",
- URL: &url.URL{
- Scheme: "http",
- Path: "not-empty-but-no-leading-slash", // bogus
- },
- }
- Redirect(rr, req, "", 304)
- if rr.Code != 304 {
- t.Errorf("Code = %d; want 304", rr.Code)
- }
-}
diff --git a/src/pkg/net/http/sniff_test.go b/src/pkg/net/http/sniff_test.go
index 106d94ec1..24ca27afc 100644
--- a/src/pkg/net/http/sniff_test.go
+++ b/src/pkg/net/http/sniff_test.go
@@ -12,6 +12,7 @@ import (
"log"
. "net/http"
"net/http/httptest"
+ "reflect"
"strconv"
"strings"
"testing"
@@ -84,6 +85,29 @@ func TestServerContentType(t *testing.T) {
}
}
+// Issue 5953: shouldn't sniff if the handler set a Content-Type header,
+// even if it's the empty string.
+func TestServerIssue5953(t *testing.T) {
+ defer afterTest(t)
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ w.Header()["Content-Type"] = []string{""}
+ fmt.Fprintf(w, "<html><head></head><body>hi</body></html>")
+ }))
+ defer ts.Close()
+
+ resp, err := Get(ts.URL)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ got := resp.Header["Content-Type"]
+ want := []string{""}
+ if !reflect.DeepEqual(got, want) {
+ t.Errorf("Content-Type = %q; want %q", got, want)
+ }
+ resp.Body.Close()
+}
+
func TestContentTypeWithCopy(t *testing.T) {
defer afterTest(t)
diff --git a/src/pkg/net/http/transfer.go b/src/pkg/net/http/transfer.go
index 53569bcc2..bacd83732 100644
--- a/src/pkg/net/http/transfer.go
+++ b/src/pkg/net/http/transfer.go
@@ -238,7 +238,7 @@ type transferReader struct {
Trailer Header
}
-// bodyAllowedForStatus returns whether a given response status code
+// bodyAllowedForStatus reports whether a given response status code
// permits a body. See RFC2616, section 4.4.
func bodyAllowedForStatus(status int) bool {
switch {
@@ -254,7 +254,7 @@ func bodyAllowedForStatus(status int) bool {
// msg is *Request or *Response.
func readTransfer(msg interface{}, r *bufio.Reader) (err error) {
- t := &transferReader{}
+ t := &transferReader{RequestMethod: "GET"}
// Unify input
isResponse := false
@@ -262,11 +262,13 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) {
case *Response:
t.Header = rr.Header
t.StatusCode = rr.StatusCode
- t.RequestMethod = rr.Request.Method
t.ProtoMajor = rr.ProtoMajor
t.ProtoMinor = rr.ProtoMinor
t.Close = shouldClose(t.ProtoMajor, t.ProtoMinor, t.Header)
isResponse = true
+ if rr.Request != nil {
+ t.RequestMethod = rr.Request.Method
+ }
case *Request:
t.Header = rr.Header
t.ProtoMajor = rr.ProtoMajor
@@ -274,7 +276,6 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) {
// Transfer semantics for Requests are exactly like those for
// Responses with status code 200, responding to a GET method
t.StatusCode = 200
- t.RequestMethod = "GET"
default:
panic("unexpected type")
}
@@ -328,12 +329,12 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) {
switch {
case chunked(t.TransferEncoding):
if noBodyExpected(t.RequestMethod) {
- t.Body = &body{Reader: eofReader, closing: t.Close}
+ t.Body = eofReader
} else {
t.Body = &body{Reader: newChunkedReader(r), hdr: msg, r: r, closing: t.Close}
}
case realLength == 0:
- t.Body = &body{Reader: eofReader, closing: t.Close}
+ t.Body = eofReader
case realLength > 0:
t.Body = &body{Reader: io.LimitReader(r, realLength), closing: t.Close}
default:
@@ -343,7 +344,7 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) {
t.Body = &body{Reader: r, closing: t.Close}
} else {
// Persistent connection (i.e. HTTP/1.1)
- t.Body = &body{Reader: eofReader, closing: t.Close}
+ t.Body = eofReader
}
}
@@ -518,8 +519,6 @@ type body struct {
r *bufio.Reader // underlying wire-format reader for the trailer
closing bool // is the connection to be closed after reading body?
closed bool
-
- res *response // response writer for server requests, else nil
}
// ErrBodyReadAfterClose is returned when reading a Request or Response
@@ -534,13 +533,22 @@ func (b *body) Read(p []byte) (n int, err error) {
}
n, err = b.Reader.Read(p)
- // Read the final trailer once we hit EOF.
- if err == io.EOF && b.hdr != nil {
- if e := b.readTrailer(); e != nil {
- err = e
+ if err == io.EOF {
+ // Chunked case. Read the trailer.
+ if b.hdr != nil {
+ if e := b.readTrailer(); e != nil {
+ err = e
+ }
+ b.hdr = nil
+ } else {
+ // If the server declared the Content-Length, our body is a LimitedReader
+ // and we need to check whether this EOF arrived early.
+ if lr, ok := b.Reader.(*io.LimitedReader); ok && lr.N > 0 {
+ err = io.ErrUnexpectedEOF
+ }
}
- b.hdr = nil
}
+
return n, err
}
@@ -618,14 +626,6 @@ func (b *body) Close() error {
case b.hdr == nil && b.closing:
// no trailer and closing the connection next.
// no point in reading to EOF.
- case b.res != nil && b.res.requestBodyLimitHit:
- // In a server request, don't continue reading from the client
- // if we've already hit the maximum body size set by the
- // handler. If this is set, that also means the TCP connection
- // is about to be closed, so getting to the next HTTP request
- // in the stream is not necessary.
- case b.Reader == eofReader:
- // Nothing to read. No need to io.Copy from it.
default:
// Fully consume the body, which will also lead to us reading
// the trailer headers after the body, if present.
diff --git a/src/pkg/net/http/transport.go b/src/pkg/net/http/transport.go
index 4cd0533ff..f6871afac 100644
--- a/src/pkg/net/http/transport.go
+++ b/src/pkg/net/http/transport.go
@@ -13,7 +13,6 @@ import (
"bufio"
"compress/gzip"
"crypto/tls"
- "encoding/base64"
"errors"
"fmt"
"io"
@@ -109,9 +108,11 @@ func ProxyFromEnvironment(req *Request) (*url.URL, error) {
}
proxyURL, err := url.Parse(proxy)
if err != nil || !strings.HasPrefix(proxyURL.Scheme, "http") {
- if u, err := url.Parse("http://" + proxy); err == nil {
- proxyURL = u
- err = nil
+ // proxy was bogus. Try prepending "http://" to it and
+ // see if that parses correctly. If not, we fall
+ // through and complain about the original one.
+ if proxyURL, err := url.Parse("http://" + proxy); err == nil {
+ return proxyURL, nil
}
}
if err != nil {
@@ -215,6 +216,7 @@ func (t *Transport) CloseIdleConnections() {
t.idleMu.Lock()
m := t.idleConn
t.idleConn = nil
+ t.idleConnCh = nil
t.idleMu.Unlock()
if m == nil {
return
@@ -270,7 +272,9 @@ func (cm *connectMethod) proxyAuth() string {
return ""
}
if u := cm.proxyURL.User; u != nil {
- return "Basic " + base64.URLEncoding.EncodeToString([]byte(u.String()))
+ username := u.Username()
+ password, _ := u.Password()
+ return "Basic " + basicAuth(username, password)
}
return ""
}
@@ -293,8 +297,10 @@ func (t *Transport) putIdleConn(pconn *persistConn) bool {
max = DefaultMaxIdleConnsPerHost
}
t.idleMu.Lock()
+
+ waitingDialer := t.idleConnCh[key]
select {
- case t.idleConnCh[key] <- pconn:
+ case waitingDialer <- pconn:
// We're done with this pconn and somebody else is
// currently waiting for a conn of this type (they're
// actively dialing, but this conn is ready
@@ -303,6 +309,11 @@ func (t *Transport) putIdleConn(pconn *persistConn) bool {
t.idleMu.Unlock()
return true
default:
+ if waitingDialer != nil {
+ // They had populated this, but their dial won
+ // first, so we can clean up this map entry.
+ delete(t.idleConnCh, key)
+ }
}
if t.idleConn == nil {
t.idleConn = make(map[string][]*persistConn)
@@ -322,7 +333,13 @@ func (t *Transport) putIdleConn(pconn *persistConn) bool {
return true
}
+// getIdleConnCh returns a channel to receive and return idle
+// persistent connection for the given connectMethod.
+// It may return nil, if persistent connections are not being used.
func (t *Transport) getIdleConnCh(cm *connectMethod) chan *persistConn {
+ if t.DisableKeepAlives {
+ return nil
+ }
key := cm.key()
t.idleMu.Lock()
defer t.idleMu.Unlock()
@@ -498,8 +515,8 @@ func (t *Transport) dialConn(cm *connectMethod) (*persistConn, error) {
if err = conn.(*tls.Conn).Handshake(); err != nil {
return nil, err
}
- if t.TLSClientConfig == nil || !t.TLSClientConfig.InsecureSkipVerify {
- if err = conn.(*tls.Conn).VerifyHostname(cm.tlsHost()); err != nil {
+ if !cfg.InsecureSkipVerify {
+ if err = conn.(*tls.Conn).VerifyHostname(cfg.ServerName); err != nil {
return nil, err
}
}
@@ -831,10 +848,15 @@ func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err err
// uncompress the gzip stream if we were the layer that
// requested it.
requestedGzip := false
- if !pc.t.DisableCompression && req.Header.Get("Accept-Encoding") == "" {
+ if !pc.t.DisableCompression && req.Header.Get("Accept-Encoding") == "" && req.Method != "HEAD" {
// Request gzip only, not deflate. Deflate is ambiguous and
// not as universally supported anyway.
// See: http://www.gzip.org/zlib/zlib_faq.html#faq38
+ //
+ // Note that we don't request this for HEAD requests,
+ // due to a bug in nginx:
+ // http://trac.nginx.org/nginx/ticket/358
+ // http://golang.org/issue/5522
requestedGzip = true
req.extraHeaders().Set("Accept-Encoding", "gzip")
}
diff --git a/src/pkg/net/http/transport_test.go b/src/pkg/net/http/transport_test.go
index 9f64a6e4b..e4df30a98 100644
--- a/src/pkg/net/http/transport_test.go
+++ b/src/pkg/net/http/transport_test.go
@@ -15,6 +15,7 @@ import (
"io"
"io/ioutil"
"net"
+ "net/http"
. "net/http"
"net/http/httptest"
"net/url"
@@ -469,6 +470,7 @@ func TestTransportHeadResponses(t *testing.T) {
res, err := c.Head(ts.URL)
if err != nil {
t.Errorf("error on loop %d: %v", i, err)
+ continue
}
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)
@@ -476,6 +478,11 @@ func TestTransportHeadResponses(t *testing.T) {
if e, g := int64(123), res.ContentLength; e != g {
t.Errorf("loop %d: expected res.ContentLength of %v, got %v", i, e, g)
}
+ if all, err := ioutil.ReadAll(res.Body); err != nil {
+ t.Errorf("loop %d: Body ReadAll: %v", i, err)
+ } else if len(all) != 0 {
+ t.Errorf("Bogus body %q", all)
+ }
}
}
@@ -553,12 +560,13 @@ func TestRoundTripGzip(t *testing.T) {
res, err := DefaultTransport.RoundTrip(req)
var body []byte
if test.compressed {
- gzip, err := gzip.NewReader(res.Body)
+ var r *gzip.Reader
+ r, err = gzip.NewReader(res.Body)
if err != nil {
t.Errorf("%d. gzip NewReader: %v", i, err)
continue
}
- body, err = ioutil.ReadAll(gzip)
+ body, err = ioutil.ReadAll(r)
res.Body.Close()
} else {
body, err = ioutil.ReadAll(res.Body)
@@ -585,13 +593,16 @@ func TestTransportGzip(t *testing.T) {
const testString = "The test string aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
const nRandBytes = 1024 * 1024
ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) {
+ if req.Method == "HEAD" {
+ if g := req.Header.Get("Accept-Encoding"); g != "" {
+ t.Errorf("HEAD request sent with Accept-Encoding of %q; want none", g)
+ }
+ return
+ }
if g, e := req.Header.Get("Accept-Encoding"), "gzip"; g != e {
t.Errorf("Accept-Encoding = %q, want %q", g, e)
}
rw.Header().Set("Content-Encoding", "gzip")
- if req.Method == "HEAD" {
- return
- }
var w io.Writer = rw
var buf bytes.Buffer
@@ -819,7 +830,7 @@ func TestTransportPersistConnLeakShortBody(t *testing.T) {
}
nhigh := runtime.NumGoroutine()
tr.CloseIdleConnections()
- time.Sleep(50 * time.Millisecond)
+ time.Sleep(400 * time.Millisecond)
runtime.GC()
nfinal := runtime.NumGoroutine()
@@ -1571,6 +1582,77 @@ func TestProxyFromEnvironment(t *testing.T) {
}
}
+func TestIdleConnChannelLeak(t *testing.T) {
+ var mu sync.Mutex
+ var n int
+
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ mu.Lock()
+ n++
+ mu.Unlock()
+ }))
+ defer ts.Close()
+
+ tr := &Transport{
+ Dial: func(netw, addr string) (net.Conn, error) {
+ return net.Dial(netw, ts.Listener.Addr().String())
+ },
+ }
+ defer tr.CloseIdleConnections()
+
+ c := &Client{Transport: tr}
+
+ // First, without keep-alives.
+ for _, disableKeep := range []bool{true, false} {
+ tr.DisableKeepAlives = disableKeep
+ for i := 0; i < 5; i++ {
+ _, err := c.Get(fmt.Sprintf("http://foo-host-%d.tld/", i))
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+ if got := tr.IdleConnChMapSizeForTesting(); got != 0 {
+ t.Fatalf("ForDisableKeepAlives = %v, map size = %d; want 0", disableKeep, got)
+ }
+ }
+}
+
+// Verify the status quo: that the Client.Post function coerces its
+// body into a ReadCloser if it's a Closer, and that the Transport
+// then closes it.
+func TestTransportClosesRequestBody(t *testing.T) {
+ defer afterTest(t)
+ ts := httptest.NewServer(http.HandlerFunc(func(w ResponseWriter, r *Request) {
+ io.Copy(ioutil.Discard, r.Body)
+ }))
+ defer ts.Close()
+
+ tr := &Transport{}
+ defer tr.CloseIdleConnections()
+ cl := &Client{Transport: tr}
+
+ closes := 0
+
+ res, err := cl.Post(ts.URL, "text/plain", countCloseReader{&closes, strings.NewReader("hello")})
+ if err != nil {
+ t.Fatal(err)
+ }
+ res.Body.Close()
+ if closes != 1 {
+ t.Errorf("closes = %d; want 1", closes)
+ }
+}
+
+type countCloseReader struct {
+ n *int
+ io.Reader
+}
+
+func (cr countCloseReader) Close() error {
+ (*cr.n)++
+ return nil
+}
+
// rgz is a gzip quine that uncompresses to itself.
var rgz = []byte{
0x1f, 0x8b, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00,
diff --git a/src/pkg/net/http/z_last_test.go b/src/pkg/net/http/z_last_test.go
index 2161db736..5a0cc1198 100644
--- a/src/pkg/net/http/z_last_test.go
+++ b/src/pkg/net/http/z_last_test.go
@@ -23,7 +23,6 @@ func interestingGoroutines() (gs []string) {
}
stack := strings.TrimSpace(sl[1])
if stack == "" ||
- strings.Contains(stack, "created by net.newPollServer") ||
strings.Contains(stack, "created by net.startServer") ||
strings.Contains(stack, "created by testing.RunTests") ||
strings.Contains(stack, "closeWriteAndWait") ||
diff --git a/src/pkg/net/interface_bsd.go b/src/pkg/net/interface_bsd.go
index 716b60a97..16775579d 100644
--- a/src/pkg/net/interface_bsd.go
+++ b/src/pkg/net/interface_bsd.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.
-// +build darwin freebsd netbsd openbsd
+// +build darwin dragonfly freebsd netbsd openbsd
package net
diff --git a/src/pkg/net/interface_bsd_test.go b/src/pkg/net/interface_bsd_test.go
index aa1141903..88daf7393 100644
--- a/src/pkg/net/interface_bsd_test.go
+++ b/src/pkg/net/interface_bsd_test.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.
-// +build darwin freebsd netbsd openbsd
+// +build darwin dragonfly freebsd netbsd openbsd
package net
diff --git a/src/pkg/net/interface_dragonfly.go b/src/pkg/net/interface_dragonfly.go
new file mode 100644
index 000000000..c9ce5a7ac
--- /dev/null
+++ b/src/pkg/net/interface_dragonfly.go
@@ -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.
+
+package net
+
+// interfaceMulticastAddrTable returns addresses for a specific
+// interface.
+func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) {
+ // TODO(mikio): Implement this like other platforms.
+ return nil, nil
+}
diff --git a/src/pkg/net/interface_linux_test.go b/src/pkg/net/interface_linux_test.go
index 085d3de9d..d8800bd0c 100644
--- a/src/pkg/net/interface_linux_test.go
+++ b/src/pkg/net/interface_linux_test.go
@@ -78,7 +78,7 @@ var (
func TestParseProcNet(t *testing.T) {
defer func() {
if p := recover(); p != nil {
- t.Fatalf("panicked")
+ t.Fatalf("parseProcNetIGMP or parseProtNetIGMP6 panicked: %v", p)
}
}()
diff --git a/src/pkg/net/interface_test.go b/src/pkg/net/interface_test.go
index e31894abf..efabb5f3c 100644
--- a/src/pkg/net/interface_test.go
+++ b/src/pkg/net/interface_test.go
@@ -108,12 +108,23 @@ func testInterfaceMulticastAddrs(t *testing.T, ifi *Interface) {
func testAddrs(t *testing.T, ifat []Addr) {
for _, ifa := range ifat {
switch ifa := ifa.(type) {
- case *IPAddr, *IPNet:
- if ifa == nil {
- t.Errorf("\tunexpected value: %v", ifa)
+ case *IPAddr:
+ if ifa == nil || ifa.IP == nil {
+ t.Errorf("\tunexpected value: %v, %v", ifa, ifa.IP)
} else {
t.Logf("\tinterface address %q", ifa.String())
}
+ case *IPNet:
+ if ifa == nil || ifa.IP == nil || ifa.Mask == nil {
+ t.Errorf("\tunexpected value: %v, %v, %v", ifa, ifa.IP, ifa.Mask)
+ } else {
+ _, prefixLen := ifa.Mask.Size()
+ if ifa.IP.To4() != nil && prefixLen != 8*IPv4len || ifa.IP.To16() != nil && ifa.IP.To4() == nil && prefixLen != 8*IPv6len {
+ t.Errorf("\tunexpected value: %v, %v, %v, %v", ifa, ifa.IP, ifa.Mask, prefixLen)
+ } else {
+ t.Logf("\tinterface address %q", ifa.String())
+ }
+ }
default:
t.Errorf("\tunexpected type: %T", ifa)
}
diff --git a/src/pkg/net/interface_unix_test.go b/src/pkg/net/interface_unix_test.go
index 0a453c095..01f609f15 100644
--- a/src/pkg/net/interface_unix_test.go
+++ b/src/pkg/net/interface_unix_test.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.
-// +build darwin freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd
package net
diff --git a/src/pkg/net/ip.go b/src/pkg/net/ip.go
index 0e42da216..fd6a7d4ee 100644
--- a/src/pkg/net/ip.go
+++ b/src/pkg/net/ip.go
@@ -12,6 +12,8 @@
package net
+import "errors"
+
// IP address lengths (bytes).
const (
IPv4len = 4
@@ -310,6 +312,43 @@ func (ip IP) String() string {
return s
}
+// ipEmptyString is like ip.String except that it returns
+// an empty string when ip is unset.
+func ipEmptyString(ip IP) string {
+ if len(ip) == 0 {
+ return ""
+ }
+ return ip.String()
+}
+
+// MarshalText implements the encoding.TextMarshaler interface.
+// The encoding is the same as returned by String.
+func (ip IP) MarshalText() ([]byte, error) {
+ if len(ip) == 0 {
+ return []byte(""), nil
+ }
+ if len(ip) != IPv4len && len(ip) != IPv6len {
+ return nil, errors.New("invalid IP address")
+ }
+ return []byte(ip.String()), nil
+}
+
+// UnmarshalText implements the encoding.TextUnmarshaler interface.
+// The IP address is expected in a form accepted by ParseIP.
+func (ip *IP) UnmarshalText(text []byte) error {
+ if len(text) == 0 {
+ *ip = nil
+ return nil
+ }
+ s := string(text)
+ x := ParseIP(s)
+ if x == nil {
+ return &ParseError{"IP address", s}
+ }
+ *ip = x
+ return nil
+}
+
// 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.
diff --git a/src/pkg/net/ip_test.go b/src/pkg/net/ip_test.go
index 16f30d446..26b53729b 100644
--- a/src/pkg/net/ip_test.go
+++ b/src/pkg/net/ip_test.go
@@ -32,6 +32,32 @@ func TestParseIP(t *testing.T) {
if out := ParseIP(tt.in); !reflect.DeepEqual(out, tt.out) {
t.Errorf("ParseIP(%q) = %v, want %v", tt.in, out, tt.out)
}
+ if tt.in == "" {
+ // Tested in TestMarshalEmptyIP below.
+ continue
+ }
+ var out IP
+ if err := out.UnmarshalText([]byte(tt.in)); !reflect.DeepEqual(out, tt.out) || (tt.out == nil) != (err != nil) {
+ t.Errorf("IP.UnmarshalText(%q) = %v, %v, want %v", tt.in, out, err, tt.out)
+ }
+ }
+}
+
+// Issue 6339
+func TestMarshalEmptyIP(t *testing.T) {
+ for _, in := range [][]byte{nil, []byte("")} {
+ var out = IP{1, 2, 3, 4}
+ if err := out.UnmarshalText(in); err != nil || out != nil {
+ t.Errorf("UnmarshalText(%v) = %v, %v; want nil, nil", in, out, err)
+ }
+ }
+ var ip IP
+ got, err := ip.MarshalText()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !reflect.DeepEqual(got, []byte("")) {
+ t.Errorf(`got %#v, want []byte("")`, got)
}
}
@@ -47,13 +73,19 @@ var ipStringTests = []struct {
{IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0}, "2001:db8:0:0:1::"},
{IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0x1}, "2001:db8::1:0:0:1"},
{IP{0x20, 0x1, 0xD, 0xB8, 0, 0, 0, 0, 0, 0xA, 0, 0xB, 0, 0xC, 0, 0xD}, "2001:db8::a:b:c:d"},
- {nil, "<nil>"},
+ {IPv4(192, 168, 0, 1), "192.168.0.1"},
+ {nil, ""},
}
func TestIPString(t *testing.T) {
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)
+ if tt.in != nil {
+ if out := tt.in.String(); out != tt.out {
+ t.Errorf("IP.String(%v) = %q, want %q", tt.in, out, tt.out)
+ }
+ }
+ if out, err := tt.in.MarshalText(); string(out) != tt.out || err != nil {
+ t.Errorf("IP.MarshalText(%v) = %q, %v, want %q, nil", tt.in, out, err, tt.out)
}
}
}
diff --git a/src/pkg/net/ipraw_test.go b/src/pkg/net/ipraw_test.go
index 12c199d1c..ea183f1d3 100644
--- a/src/pkg/net/ipraw_test.go
+++ b/src/pkg/net/ipraw_test.go
@@ -6,19 +6,19 @@ package net
import (
"bytes"
- "errors"
"fmt"
"os"
"reflect"
+ "runtime"
"testing"
"time"
)
type resolveIPAddrTest struct {
- net string
- litAddr string
- addr *IPAddr
- err error
+ net string
+ litAddrOrName string
+ addr *IPAddr
+ err error
}
var resolveIPAddrTests = []resolveIPAddrTest{
@@ -29,6 +29,7 @@ var resolveIPAddrTests = []resolveIPAddrTest{
{"ip", "::1", &IPAddr{IP: ParseIP("::1")}, nil},
{"ip6", "::1", &IPAddr{IP: ParseIP("::1")}, nil},
{"ip6:ipv6-icmp", "::1", &IPAddr{IP: ParseIP("::1")}, nil},
+ {"ip6:IPv6-ICMP", "::1", &IPAddr{IP: ParseIP("::1")}, nil},
{"ip", "::1%en0", &IPAddr{IP: ParseIP("::1"), Zone: "en0"}, nil},
{"ip6", "::1%911", &IPAddr{IP: ParseIP("::1"), Zone: "911"}, nil},
@@ -49,13 +50,28 @@ func init() {
{"ip6", "fe80::1%" + index, &IPAddr{IP: ParseIP("fe80::1"), Zone: index}, nil},
}...)
}
+ if ips, err := LookupIP("localhost"); err == nil && len(ips) > 1 && supportsIPv4 && supportsIPv6 {
+ resolveIPAddrTests = append(resolveIPAddrTests, []resolveIPAddrTest{
+ {"ip", "localhost", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil},
+ {"ip4", "localhost", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil},
+ {"ip6", "localhost", &IPAddr{IP: IPv6loopback}, nil},
+ }...)
+ }
+}
+
+func skipRawSocketTest(t *testing.T) (skip bool, skipmsg string) {
+ skip, skipmsg, err := skipRawSocketTests()
+ if err != nil {
+ t.Fatal(err)
+ }
+ return skip, skipmsg
}
func TestResolveIPAddr(t *testing.T) {
for _, tt := range resolveIPAddrTests {
- addr, err := ResolveIPAddr(tt.net, tt.litAddr)
+ addr, err := ResolveIPAddr(tt.net, tt.litAddrOrName)
if err != tt.err {
- condFatalf(t, "ResolveIPAddr(%v, %v) failed: %v", tt.net, tt.litAddr, err)
+ t.Fatalf("ResolveIPAddr(%v, %v) failed: %v", tt.net, tt.litAddrOrName, err)
} else if !reflect.DeepEqual(addr, tt.addr) {
t.Fatalf("got %#v; expected %#v", addr, tt.addr)
}
@@ -72,8 +88,8 @@ var icmpEchoTests = []struct {
}
func TestConnICMPEcho(t *testing.T) {
- if os.Getuid() != 0 {
- t.Skip("skipping test; must be root")
+ if skip, skipmsg := skipRawSocketTest(t); skip {
+ t.Skip(skipmsg)
}
for i, tt := range icmpEchoTests {
@@ -97,7 +113,7 @@ func TestConnICMPEcho(t *testing.T) {
typ = icmpv6EchoRequest
}
xid, xseq := os.Getpid()&0xffff, i+1
- b, err := (&icmpMessage{
+ wb, err := (&icmpMessage{
Type: typ, Code: 0,
Body: &icmpEcho{
ID: xid, Seq: xseq,
@@ -107,18 +123,19 @@ func TestConnICMPEcho(t *testing.T) {
if err != nil {
t.Fatalf("icmpMessage.Marshal failed: %v", err)
}
- if _, err := c.Write(b); err != nil {
+ if _, err := c.Write(wb); err != nil {
t.Fatalf("Conn.Write failed: %v", err)
}
var m *icmpMessage
+ rb := make([]byte, 20+len(wb))
for {
- if _, err := c.Read(b); err != nil {
+ if _, err := c.Read(rb); err != nil {
t.Fatalf("Conn.Read failed: %v", err)
}
if net == "ip4" {
- b = ipv4Payload(b)
+ rb = ipv4Payload(rb)
}
- if m, err = parseICMPMessage(b); err != nil {
+ if m, err = parseICMPMessage(rb); err != nil {
t.Fatalf("parseICMPMessage failed: %v", err)
}
switch m.Type {
@@ -139,8 +156,8 @@ func TestConnICMPEcho(t *testing.T) {
}
func TestPacketConnICMPEcho(t *testing.T) {
- if os.Getuid() != 0 {
- t.Skip("skipping test; must be root")
+ if skip, skipmsg := skipRawSocketTest(t); skip {
+ t.Skip(skipmsg)
}
for i, tt := range icmpEchoTests {
@@ -168,7 +185,7 @@ func TestPacketConnICMPEcho(t *testing.T) {
typ = icmpv6EchoRequest
}
xid, xseq := os.Getpid()&0xffff, i+1
- b, err := (&icmpMessage{
+ wb, err := (&icmpMessage{
Type: typ, Code: 0,
Body: &icmpEcho{
ID: xid, Seq: xseq,
@@ -178,19 +195,20 @@ func TestPacketConnICMPEcho(t *testing.T) {
if err != nil {
t.Fatalf("icmpMessage.Marshal failed: %v", err)
}
- if _, err := c.WriteTo(b, ra); err != nil {
+ if _, err := c.WriteTo(wb, ra); err != nil {
t.Fatalf("PacketConn.WriteTo failed: %v", err)
}
var m *icmpMessage
+ rb := make([]byte, 20+len(wb))
for {
- if _, _, err := c.ReadFrom(b); err != nil {
+ if _, _, err := c.ReadFrom(rb); err != nil {
t.Fatalf("PacketConn.ReadFrom failed: %v", err)
}
- // TODO: fix issue 3944
+ // See BUG section.
//if net == "ip4" {
- // b = ipv4Payload(b)
+ // rb = ipv4Payload(rb)
//}
- if m, err = parseICMPMessage(b); err != nil {
+ if m, err = parseICMPMessage(rb); err != nil {
t.Fatalf("parseICMPMessage failed: %v", err)
}
switch m.Type {
@@ -218,115 +236,6 @@ func ipv4Payload(b []byte) []byte {
return b[hdrlen:]
}
-const (
- icmpv4EchoRequest = 8
- icmpv4EchoReply = 0
- icmpv6EchoRequest = 128
- icmpv6EchoReply = 129
-)
-
-// icmpMessage represents an ICMP message.
-type icmpMessage struct {
- Type int // type
- Code int // code
- Checksum int // checksum
- Body icmpMessageBody // body
-}
-
-// icmpMessageBody represents an ICMP message body.
-type icmpMessageBody interface {
- Len() int
- Marshal() ([]byte, error)
-}
-
-// Marshal returns the binary enconding of the ICMP echo request or
-// reply message m.
-func (m *icmpMessage) Marshal() ([]byte, error) {
- b := []byte{byte(m.Type), byte(m.Code), 0, 0}
- if m.Body != nil && m.Body.Len() != 0 {
- mb, err := m.Body.Marshal()
- if err != nil {
- return nil, err
- }
- b = append(b, mb...)
- }
- switch m.Type {
- case icmpv6EchoRequest, icmpv6EchoReply:
- return b, nil
- }
- csumcv := len(b) - 1 // checksum coverage
- s := uint32(0)
- for i := 0; i < csumcv; i += 2 {
- s += uint32(b[i+1])<<8 | uint32(b[i])
- }
- if csumcv&1 == 0 {
- s += uint32(b[csumcv])
- }
- s = s>>16 + s&0xffff
- s = s + s>>16
- // Place checksum back in header; using ^= avoids the
- // assumption the checksum bytes are zero.
- b[2] ^= byte(^s & 0xff)
- b[3] ^= byte(^s >> 8)
- return b, nil
-}
-
-// parseICMPMessage parses b as an ICMP message.
-func parseICMPMessage(b []byte) (*icmpMessage, error) {
- msglen := len(b)
- if msglen < 4 {
- return nil, errors.New("message too short")
- }
- m := &icmpMessage{Type: int(b[0]), Code: int(b[1]), Checksum: int(b[2])<<8 | int(b[3])}
- if msglen > 4 {
- var err error
- switch m.Type {
- case icmpv4EchoRequest, icmpv4EchoReply, icmpv6EchoRequest, icmpv6EchoReply:
- m.Body, err = parseICMPEcho(b[4:])
- if err != nil {
- return nil, err
- }
- }
- }
- return m, nil
-}
-
-// imcpEcho represenets an ICMP echo request or reply message body.
-type icmpEcho struct {
- ID int // identifier
- Seq int // sequence number
- Data []byte // data
-}
-
-func (p *icmpEcho) Len() int {
- if p == nil {
- return 0
- }
- return 4 + len(p.Data)
-}
-
-// Marshal returns the binary enconding of the ICMP echo request or
-// reply message body p.
-func (p *icmpEcho) Marshal() ([]byte, error) {
- b := make([]byte, 4+len(p.Data))
- b[0], b[1] = byte(p.ID>>8), byte(p.ID&0xff)
- b[2], b[3] = byte(p.Seq>>8), byte(p.Seq&0xff)
- copy(b[4:], p.Data)
- return b, nil
-}
-
-// parseICMPEcho parses b as an ICMP echo request or reply message
-// body.
-func parseICMPEcho(b []byte) (*icmpEcho, error) {
- bodylen := len(b)
- p := &icmpEcho{ID: int(b[0])<<8 | int(b[1]), Seq: int(b[2])<<8 | int(b[3])}
- if bodylen > 4 {
- p.Data = make([]byte, bodylen-4)
- copy(p.Data, b[4:])
- }
- return p, nil
-}
-
var ipConnLocalNameTests = []struct {
net string
laddr *IPAddr
@@ -337,8 +246,13 @@ var ipConnLocalNameTests = []struct {
}
func TestIPConnLocalName(t *testing.T) {
- if os.Getuid() != 0 {
- t.Skip("skipping test; must be root")
+ switch runtime.GOOS {
+ case "plan9", "windows":
+ t.Skipf("skipping test on %q", runtime.GOOS)
+ default:
+ if os.Getuid() != 0 {
+ t.Skip("skipping test; must be root")
+ }
}
for _, tt := range ipConnLocalNameTests {
@@ -354,8 +268,13 @@ func TestIPConnLocalName(t *testing.T) {
}
func TestIPConnRemoteName(t *testing.T) {
- if os.Getuid() != 0 {
- t.Skip("skipping test; must be root")
+ switch runtime.GOOS {
+ case "plan9", "windows":
+ t.Skipf("skipping test on %q", runtime.GOOS)
+ default:
+ if os.Getuid() != 0 {
+ t.Skip("skipping test; must be root")
+ }
}
raddr := &IPAddr{IP: IPv4(127, 0, 0, 10).To4()}
diff --git a/src/pkg/net/iprawsock.go b/src/pkg/net/iprawsock.go
index 0be94eb70..5cc361390 100644
--- a/src/pkg/net/iprawsock.go
+++ b/src/pkg/net/iprawsock.go
@@ -23,6 +23,13 @@ func (a *IPAddr) String() string {
return a.IP.String()
}
+func (a *IPAddr) toAddr() Addr {
+ if a == nil {
+ return nil
+ }
+ return a
+}
+
// ResolveIPAddr parses addr as an IP address of the form "host" or
// "ipv6-host%zone" and resolves the domain name on the network net,
// which must be "ip", "ip4" or "ip6".
@@ -43,5 +50,5 @@ func ResolveIPAddr(net, addr string) (*IPAddr, error) {
if err != nil {
return nil, err
}
- return a.(*IPAddr), nil
+ return a.toAddr().(*IPAddr), nil
}
diff --git a/src/pkg/net/iprawsock_posix.go b/src/pkg/net/iprawsock_posix.go
index caeeb4653..722853257 100644
--- a/src/pkg/net/iprawsock_posix.go
+++ b/src/pkg/net/iprawsock_posix.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.
-// +build darwin freebsd linux netbsd openbsd windows
+// +build darwin dragonfly freebsd linux netbsd openbsd windows
package net
@@ -11,6 +11,18 @@ import (
"time"
)
+// BUG(mikio): On every POSIX platform, reads from the "ip4" network
+// using the ReadFrom or ReadFromIP method might not return a complete
+// IPv4 packet, including its header, even if there is space
+// available. This can occur even in cases where Read or ReadMsgIP
+// could return a complete packet. For this reason, it is recommended
+// that you do not uses these methods if it is important to receive a
+// full packet.
+//
+// The Go 1 compatibliity guidelines make it impossible for us to
+// change the behavior of these methods; use Read or ReadMsgIP
+// instead.
+
func sockaddrToIP(sa syscall.Sockaddr) Addr {
switch sa := sa.(type) {
case *syscall.SockaddrInet4:
@@ -39,14 +51,10 @@ func (a *IPAddr) isWildcard() bool {
}
func (a *IPAddr) sockaddr(family int) (syscall.Sockaddr, error) {
- return ipToSockaddr(family, a.IP, 0, a.Zone)
-}
-
-func (a *IPAddr) toAddr() sockaddr {
- if a == nil { // nil *IPAddr
- return nil // nil interface
+ if a == nil {
+ return nil, nil
}
- return a
+ return ipToSockaddr(family, a.IP, 0, a.Zone)
}
// IPConn is the implementation of the Conn and PacketConn interfaces
@@ -125,6 +133,9 @@ func (c *IPConn) WriteToIP(b []byte, addr *IPAddr) (int, error) {
if !c.ok() {
return 0, syscall.EINVAL
}
+ if addr == nil {
+ return 0, &OpError{Op: "write", Net: c.fd.net, Addr: nil, Err: errMissingAddress}
+ }
sa, err := addr.sockaddr(c.fd.family)
if err != nil {
return 0, &OpError{"write", c.fd.net, addr, err}
@@ -151,6 +162,9 @@ func (c *IPConn) WriteMsgIP(b, oob []byte, addr *IPAddr) (n, oobn int, err error
if !c.ok() {
return 0, 0, syscall.EINVAL
}
+ if addr == nil {
+ return 0, 0, &OpError{Op: "write", Net: c.fd.net, Addr: nil, Err: errMissingAddress}
+ }
sa, err := addr.sockaddr(c.fd.family)
if err != nil {
return 0, 0, &OpError{"write", c.fd.net, addr, err}
@@ -168,19 +182,19 @@ func DialIP(netProto string, laddr, raddr *IPAddr) (*IPConn, error) {
func dialIP(netProto string, laddr, raddr *IPAddr, deadline time.Time) (*IPConn, error) {
net, proto, err := parseNetwork(netProto)
if err != nil {
- return nil, err
+ return nil, &OpError{Op: "dial", Net: netProto, Addr: raddr, Err: err}
}
switch net {
case "ip", "ip4", "ip6":
default:
- return nil, UnknownNetworkError(netProto)
+ return nil, &OpError{Op: "dial", Net: netProto, Addr: raddr, Err: UnknownNetworkError(netProto)}
}
if raddr == nil {
- return nil, &OpError{"dial", netProto, nil, errMissingAddress}
+ return nil, &OpError{Op: "dial", Net: netProto, Addr: nil, Err: errMissingAddress}
}
- fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), deadline, syscall.SOCK_RAW, proto, "dial", sockaddrToIP)
+ fd, err := internetSocket(net, laddr, raddr, deadline, syscall.SOCK_RAW, proto, "dial", sockaddrToIP)
if err != nil {
- return nil, err
+ return nil, &OpError{Op: "dial", Net: netProto, Addr: raddr, Err: err}
}
return newIPConn(fd), nil
}
@@ -192,16 +206,16 @@ func dialIP(netProto string, laddr, raddr *IPAddr, deadline time.Time) (*IPConn,
func ListenIP(netProto string, laddr *IPAddr) (*IPConn, error) {
net, proto, err := parseNetwork(netProto)
if err != nil {
- return nil, err
+ return nil, &OpError{Op: "dial", Net: netProto, Addr: laddr, Err: err}
}
switch net {
case "ip", "ip4", "ip6":
default:
- return nil, UnknownNetworkError(netProto)
+ return nil, &OpError{Op: "listen", Net: netProto, Addr: laddr, Err: UnknownNetworkError(netProto)}
}
- fd, err := internetSocket(net, laddr.toAddr(), nil, noDeadline, syscall.SOCK_RAW, proto, "listen", sockaddrToIP)
+ fd, err := internetSocket(net, laddr, nil, noDeadline, syscall.SOCK_RAW, proto, "listen", sockaddrToIP)
if err != nil {
- return nil, err
+ return nil, &OpError{Op: "listen", Net: netProto, Addr: laddr, Err: err}
}
return newIPConn(fd), nil
}
diff --git a/src/pkg/net/ipsock.go b/src/pkg/net/ipsock.go
index d93059587..8b586ef7c 100644
--- a/src/pkg/net/ipsock.go
+++ b/src/pkg/net/ipsock.go
@@ -6,68 +6,135 @@
package net
-import "time"
+import (
+ "errors"
+ "time"
+)
-var supportsIPv6, supportsIPv4map bool
+var (
+ // supportsIPv4 reports whether the platform supports IPv4
+ // networking functionality.
+ supportsIPv4 bool
+
+ // supportsIPv6 reports whether the platfrom supports IPv6
+ // networking functionality.
+ supportsIPv6 bool
+
+ // supportsIPv4map reports whether the platform supports
+ // mapping an IPv4 address inside an IPv6 address at transport
+ // layer protocols. See RFC 4291, RFC 4038 and RFC 3493.
+ supportsIPv4map bool
+)
func init() {
sysInit()
+ supportsIPv4 = probeIPv4Stack()
supportsIPv6, supportsIPv4map = probeIPv6Stack()
}
-func firstFavoriteAddr(filter func(IP) IP, addrs []string) (addr IP) {
- if filter == nil {
- // We'll take any IP address, but since the dialing code
- // does not yet try multiple addresses, prefer to use
- // an IPv4 address if possible. This is especially relevant
- // if localhost resolves to [ipv6-localhost, ipv4-localhost].
- // Too much code assumes localhost == ipv4-localhost.
- addr = firstSupportedAddr(ipv4only, addrs)
- if addr == nil {
- addr = firstSupportedAddr(anyaddr, addrs)
- }
- } else {
- addr = firstSupportedAddr(filter, addrs)
+// A netaddr represents a network endpoint address or a list of
+// network endpoint addresses.
+type netaddr interface {
+ // toAddr returns the address represented in Addr interface.
+ // It returns a nil interface when the address is nil.
+ toAddr() Addr
+}
+
+// An addrList represents a list of network endpoint addresses.
+type addrList []netaddr
+
+func (al addrList) toAddr() Addr {
+ switch len(al) {
+ case 0:
+ return nil
+ case 1:
+ return al[0].toAddr()
+ default:
+ // For now, we'll roughly pick first one without
+ // considering dealing with any preferences such as
+ // DNS TTL, transport path quality, network routing
+ // information.
+ return al[0].toAddr()
}
- return
}
-func firstSupportedAddr(filter func(IP) IP, addrs []string) IP {
- for _, s := range addrs {
- if addr := filter(ParseIP(s)); addr != nil {
- return addr
+var errNoSuitableAddress = errors.New("no suitable address found")
+
+// firstFavoriteAddr returns an address or a list of addresses that
+// implement the netaddr interface. Known filters are nil, ipv4only
+// and ipv6only. It returns any address when filter is nil. The result
+// contains at least one address when error is nil.
+func firstFavoriteAddr(filter func(IP) IP, ips []IP, inetaddr func(IP) netaddr) (netaddr, error) {
+ if filter != nil {
+ return firstSupportedAddr(filter, ips, inetaddr)
+ }
+ var (
+ ipv4, ipv6, swap bool
+ list addrList
+ )
+ for _, ip := range ips {
+ // We'll take any IP address, but since the dialing
+ // code does not yet try multiple addresses
+ // effectively, prefer to use an IPv4 address if
+ // possible. This is especially relevant if localhost
+ // resolves to [ipv6-localhost, ipv4-localhost]. Too
+ // much code assumes localhost == ipv4-localhost.
+ if ip4 := ipv4only(ip); ip4 != nil && !ipv4 {
+ list = append(list, inetaddr(ip4))
+ ipv4 = true
+ if ipv6 {
+ swap = true
+ }
+ } else if ip6 := ipv6only(ip); ip6 != nil && !ipv6 {
+ list = append(list, inetaddr(ip6))
+ ipv6 = true
+ }
+ if ipv4 && ipv6 {
+ if swap {
+ list[0], list[1] = list[1], list[0]
+ }
+ break
}
}
- return nil
+ switch len(list) {
+ case 0:
+ return nil, errNoSuitableAddress
+ case 1:
+ return list[0], nil
+ default:
+ return list, nil
+ }
}
-func anyaddr(x IP) IP {
- if x4 := x.To4(); x4 != nil {
- return x4
+func firstSupportedAddr(filter func(IP) IP, ips []IP, inetaddr func(IP) netaddr) (netaddr, error) {
+ for _, ip := range ips {
+ if ip := filter(ip); ip != nil {
+ return inetaddr(ip), nil
+ }
}
- if supportsIPv6 {
- return x
+ return nil, errNoSuitableAddress
+}
+
+// ipv4only returns IPv4 addresses that we can use with the kernel's
+// IPv4 addressing modes. If ip is an IPv4 address, ipv4only returns ip.
+// Otherwise it returns nil.
+func ipv4only(ip IP) IP {
+ if supportsIPv4 && ip.To4() != nil {
+ return ip
}
return nil
}
-func ipv4only(x IP) IP { return x.To4() }
-
-func ipv6only(x IP) IP {
- // Only return addresses that we can use
- // with the kernel's IPv6 addressing modes.
- if len(x) == IPv6len && x.To4() == nil && supportsIPv6 {
- return x
+// ipv6only returns IPv6 addresses that we can use with the kernel's
+// IPv6 addressing modes. It returns IPv4-mapped IPv6 addresses as
+// nils and returns other IPv6 address types as IPv6 addresses.
+func ipv6only(ip IP) IP {
+ if supportsIPv6 && len(ip) == IPv6len && ip.To4() == nil {
+ return ip
}
return nil
}
-type InvalidAddrError string
-
-func (e InvalidAddrError) Error() string { return string(e) }
-func (e InvalidAddrError) Timeout() bool { return false }
-func (e InvalidAddrError) Temporary() bool { return false }
-
// SplitHostPort splits a network address of the form "host:port",
// "[host]:port" or "[ipv6-host%zone]:port" into host or
// ipv6-host%zone and port. A literal address or host name for IPv6
@@ -161,7 +228,13 @@ func JoinHostPort(host, port string) string {
return host + ":" + port
}
-func resolveInternetAddr(net, addr string, deadline time.Time) (Addr, error) {
+// resolveInternetAddr resolves addr that is either a literal IP
+// address or a DNS name and returns an internet protocol family
+// address. It returns a list that contains a pair of different
+// address family addresses when addr is a DNS name and the name has
+// mutiple address family records. The result contains at least one
+// address when error is nil.
+func resolveInternetAddr(net, addr string, deadline time.Time) (netaddr, error) {
var (
err error
host, port, zone string
@@ -184,30 +257,32 @@ func resolveInternetAddr(net, addr string, deadline time.Time) (Addr, error) {
default:
return nil, UnknownNetworkError(net)
}
- inetaddr := func(net string, ip IP, port int, zone string) Addr {
+ inetaddr := func(ip IP) netaddr {
switch net {
case "tcp", "tcp4", "tcp6":
- return &TCPAddr{IP: ip, Port: port, Zone: zone}
+ return &TCPAddr{IP: ip, Port: portnum, Zone: zone}
case "udp", "udp4", "udp6":
- return &UDPAddr{IP: ip, Port: port, Zone: zone}
+ return &UDPAddr{IP: ip, Port: portnum, Zone: zone}
case "ip", "ip4", "ip6":
return &IPAddr{IP: ip, Zone: zone}
+ default:
+ panic("unexpected network: " + net)
}
- return nil
}
if host == "" {
- return inetaddr(net, nil, portnum, zone), nil
+ return inetaddr(nil), nil
}
- // Try as an IP address.
- if ip := parseIPv4(host); ip != nil {
- return inetaddr(net, ip, portnum, zone), nil
+ // Try as a literal IP address.
+ var ip IP
+ if ip = parseIPv4(host); ip != nil {
+ return inetaddr(ip), nil
}
- if ip, zone := parseIPv6(host, true); ip != nil {
- return inetaddr(net, ip, portnum, zone), nil
+ if ip, zone = parseIPv6(host, true); ip != nil {
+ return inetaddr(ip), nil
}
- // Try as a domain name.
+ // Try as a DNS name.
host, zone = splitHostZone(host)
- addrs, err := lookupHostDeadline(host, deadline)
+ ips, err := lookupIPDeadline(host, deadline)
if err != nil {
return nil, err
}
@@ -218,12 +293,7 @@ func resolveInternetAddr(net, addr string, deadline time.Time) (Addr, error) {
if net != "" && net[len(net)-1] == '6' || zone != "" {
filter = ipv6only
}
- ip := firstFavoriteAddr(filter, addrs)
- if ip == nil {
- // should not happen
- return nil, &AddrError{"LookupHost returned no suitable address", addrs[0]}
- }
- return inetaddr(net, ip, portnum, zone), nil
+ return firstFavoriteAddr(filter, ips, inetaddr)
}
func zoneToString(zone int) string {
diff --git a/src/pkg/net/ipsock_plan9.go b/src/pkg/net/ipsock_plan9.go
index c7d542dab..fcec4164f 100644
--- a/src/pkg/net/ipsock_plan9.go
+++ b/src/pkg/net/ipsock_plan9.go
@@ -12,13 +12,18 @@ import (
"syscall"
)
-// /sys/include/ape/sys/socket.h:/SOMAXCONN
-var listenerBacklog = 5
+func probeIPv4Stack() bool {
+ // TODO(mikio): implement this when Plan 9 supports IPv6-only
+ // kernel.
+ return true
+}
// probeIPv6Stack returns two boolean values. If the first boolean
// value is true, kernel supports basic IPv6 functionality. If the
// second boolean value is true, kernel supports IPv6 IPv4-mapping.
func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) {
+ // TODO(mikio): implement this once Plan 9 gets an IPv6
+ // protocol stack implementation.
return false, false
}
diff --git a/src/pkg/net/ipsock_posix.go b/src/pkg/net/ipsock_posix.go
index 4c37616ec..a83e52561 100644
--- a/src/pkg/net/ipsock_posix.go
+++ b/src/pkg/net/ipsock_posix.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.
-// +build darwin freebsd linux netbsd openbsd windows
+// +build darwin dragonfly freebsd linux netbsd openbsd windows
// Internet protocol family sockets for POSIX
@@ -13,6 +13,17 @@ import (
"time"
)
+func probeIPv4Stack() bool {
+ s, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
+ switch err {
+ case syscall.EAFNOSUPPORT, syscall.EPROTONOSUPPORT:
+ return false
+ case nil:
+ closesocket(s)
+ }
+ return true
+}
+
// Should we try to use the IPv4 socket interface if we're
// only dealing with IPv4 sockets? As long as the host system
// understands IPv6, it's okay to pass IPv4 addresses to the IPv6
@@ -28,8 +39,8 @@ import (
// boolean value is true, kernel supports IPv6 IPv4-mapping.
func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) {
var probes = []struct {
- la TCPAddr
- ok bool
+ laddr TCPAddr
+ ok bool
}{
// IPv6 communication capability
{TCPAddr{IP: ParseIP("::1")}, false},
@@ -44,12 +55,11 @@ func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) {
}
defer closesocket(s)
syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 0)
- sa, err := probes[i].la.toAddr().sockaddr(syscall.AF_INET6)
+ sa, err := probes[i].laddr.sockaddr(syscall.AF_INET6)
if err != nil {
continue
}
- err = syscall.Bind(s, sa)
- if err != nil {
+ if err := syscall.Bind(s, sa); err != nil {
continue
}
probes[i].ok = true
@@ -121,40 +131,9 @@ func favoriteAddrFamily(net string, laddr, raddr sockaddr, mode string) (family
// Internet sockets (TCP, UDP, IP)
-// A sockaddr represents a TCP, UDP or IP network address that can
-// be converted into a syscall.Sockaddr.
-type sockaddr interface {
- Addr
- family() int
- isWildcard() bool
- sockaddr(family int) (syscall.Sockaddr, error)
-}
-
func internetSocket(net string, laddr, raddr sockaddr, deadline time.Time, sotype, proto int, mode string, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) {
- var la, ra syscall.Sockaddr
family, ipv6only := favoriteAddrFamily(net, laddr, raddr, mode)
- if laddr != nil {
- if la, err = laddr.sockaddr(family); err != nil {
- goto Error
- }
- }
- if raddr != nil {
- if ra, err = raddr.sockaddr(family); err != nil {
- goto Error
- }
- }
- fd, err = socket(net, family, sotype, proto, ipv6only, la, ra, deadline, toAddr)
- if err != nil {
- goto Error
- }
- return fd, nil
-
-Error:
- addr := raddr
- if mode == "listen" {
- addr = laddr
- }
- return nil, &OpError{mode, net, addr, err}
+ return socket(net, family, sotype, proto, ipv6only, laddr, raddr, deadline, toAddr)
}
func ipToSockaddr(family int, ip IP, port int, zone string) (syscall.Sockaddr, error) {
diff --git a/src/pkg/net/ipsock_test.go b/src/pkg/net/ipsock_test.go
new file mode 100644
index 000000000..9ecaaec69
--- /dev/null
+++ b/src/pkg/net/ipsock_test.go
@@ -0,0 +1,193 @@
+// Copyright 2013 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 (
+ "reflect"
+ "testing"
+)
+
+var testInetaddr = func(ip IP) netaddr { return &TCPAddr{IP: ip, Port: 5682} }
+
+var firstFavoriteAddrTests = []struct {
+ filter func(IP) IP
+ ips []IP
+ inetaddr func(IP) netaddr
+ addr netaddr
+ err error
+}{
+ {
+ nil,
+ []IP{
+ IPv4(127, 0, 0, 1),
+ IPv6loopback,
+ },
+ testInetaddr,
+ addrList{
+ &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
+ &TCPAddr{IP: IPv6loopback, Port: 5682},
+ },
+ nil,
+ },
+ {
+ nil,
+ []IP{
+ IPv6loopback,
+ IPv4(127, 0, 0, 1),
+ },
+ testInetaddr,
+ addrList{
+ &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
+ &TCPAddr{IP: IPv6loopback, Port: 5682},
+ },
+ nil,
+ },
+ {
+ nil,
+ []IP{
+ IPv4(127, 0, 0, 1),
+ IPv4(192, 168, 0, 1),
+ },
+ testInetaddr,
+ &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
+ nil,
+ },
+ {
+ nil,
+ []IP{
+ IPv6loopback,
+ ParseIP("fe80::1"),
+ },
+ testInetaddr,
+ &TCPAddr{IP: IPv6loopback, Port: 5682},
+ nil,
+ },
+ {
+ nil,
+ []IP{
+ IPv4(127, 0, 0, 1),
+ IPv4(192, 168, 0, 1),
+ IPv6loopback,
+ ParseIP("fe80::1"),
+ },
+ testInetaddr,
+ addrList{
+ &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
+ &TCPAddr{IP: IPv6loopback, Port: 5682},
+ },
+ nil,
+ },
+ {
+ nil,
+ []IP{
+ IPv6loopback,
+ ParseIP("fe80::1"),
+ IPv4(127, 0, 0, 1),
+ IPv4(192, 168, 0, 1),
+ },
+ testInetaddr,
+ addrList{
+ &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
+ &TCPAddr{IP: IPv6loopback, Port: 5682},
+ },
+ nil,
+ },
+ {
+ nil,
+ []IP{
+ IPv4(127, 0, 0, 1),
+ IPv6loopback,
+ IPv4(192, 168, 0, 1),
+ ParseIP("fe80::1"),
+ },
+ testInetaddr,
+ addrList{
+ &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
+ &TCPAddr{IP: IPv6loopback, Port: 5682},
+ },
+ nil,
+ },
+ {
+ nil,
+ []IP{
+ IPv6loopback,
+ IPv4(127, 0, 0, 1),
+ ParseIP("fe80::1"),
+ IPv4(192, 168, 0, 1),
+ },
+ testInetaddr,
+ addrList{
+ &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
+ &TCPAddr{IP: IPv6loopback, Port: 5682},
+ },
+ nil,
+ },
+
+ {
+ ipv4only,
+ []IP{
+ IPv4(127, 0, 0, 1),
+ IPv6loopback,
+ },
+ testInetaddr,
+ &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
+ nil,
+ },
+ {
+ ipv4only,
+ []IP{
+ IPv6loopback,
+ IPv4(127, 0, 0, 1),
+ },
+ testInetaddr,
+ &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
+ nil,
+ },
+
+ {
+ ipv6only,
+ []IP{
+ IPv4(127, 0, 0, 1),
+ IPv6loopback,
+ },
+ testInetaddr,
+ &TCPAddr{IP: IPv6loopback, Port: 5682},
+ nil,
+ },
+ {
+ ipv6only,
+ []IP{
+ IPv6loopback,
+ IPv4(127, 0, 0, 1),
+ },
+ testInetaddr,
+ &TCPAddr{IP: IPv6loopback, Port: 5682},
+ nil,
+ },
+
+ {nil, nil, testInetaddr, nil, errNoSuitableAddress},
+
+ {ipv4only, nil, testInetaddr, nil, errNoSuitableAddress},
+ {ipv4only, []IP{IPv6loopback}, testInetaddr, nil, errNoSuitableAddress},
+
+ {ipv6only, nil, testInetaddr, nil, errNoSuitableAddress},
+ {ipv6only, []IP{IPv4(127, 0, 0, 1)}, testInetaddr, nil, errNoSuitableAddress},
+}
+
+func TestFirstFavoriteAddr(t *testing.T) {
+ if !supportsIPv4 || !supportsIPv6 {
+ t.Skip("ipv4 or ipv6 is not supported")
+ }
+
+ for i, tt := range firstFavoriteAddrTests {
+ addr, err := firstFavoriteAddr(tt.filter, tt.ips, tt.inetaddr)
+ if err != tt.err {
+ t.Errorf("#%v: got %v; expected %v", i, err, tt.err)
+ }
+ if !reflect.DeepEqual(addr, tt.addr) {
+ t.Errorf("#%v: got %v; expected %v", i, addr, tt.addr)
+ }
+ }
+}
diff --git a/src/pkg/net/lookup.go b/src/pkg/net/lookup.go
index bec93ec08..20f20578c 100644
--- a/src/pkg/net/lookup.go
+++ b/src/pkg/net/lookup.go
@@ -4,9 +4,20 @@
package net
-import (
- "time"
-)
+import "time"
+
+// protocols contains minimal mappings between internet protocol
+// names and numbers for platforms that don't have a complete list of
+// protocol numbers.
+//
+// See http://www.iana.org/assignments/protocol-numbers
+var protocols = map[string]int{
+ "icmp": 1, "ICMP": 1,
+ "igmp": 2, "IGMP": 2,
+ "tcp": 6, "TCP": 6,
+ "udp": 17, "UDP": 17,
+ "ipv6-icmp": 58, "IPV6-ICMP": 58, "IPv6-ICMP": 58,
+}
// LookupHost looks up the given host using the local resolver.
// It returns an array of that host's addresses.
@@ -14,9 +25,36 @@ func LookupHost(host string) (addrs []string, err error) {
return lookupHost(host)
}
-func lookupHostDeadline(host string, deadline time.Time) (addrs []string, err error) {
+// 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 error) {
+ return lookupIPMerge(host)
+}
+
+var lookupGroup singleflight
+
+// lookupIPMerge wraps lookupIP, but makes sure that for any given
+// host, only one lookup is in-flight at a time. The returned memory
+// is always owned by the caller.
+func lookupIPMerge(host string) (addrs []IP, err error) {
+ addrsi, err, shared := lookupGroup.Do(host, func() (interface{}, error) {
+ return lookupIP(host)
+ })
+ if err != nil {
+ return nil, err
+ }
+ addrs = addrsi.([]IP)
+ if shared {
+ clone := make([]IP, len(addrs))
+ copy(clone, addrs)
+ addrs = clone
+ }
+ return addrs, nil
+}
+
+func lookupIPDeadline(host string, deadline time.Time) (addrs []IP, err error) {
if deadline.IsZero() {
- return lookupHost(host)
+ return lookupIPMerge(host)
}
// TODO(bradfitz): consider pushing the deadline down into the
@@ -34,12 +72,12 @@ func lookupHostDeadline(host string, deadline time.Time) (addrs []string, err er
t := time.NewTimer(timeout)
defer t.Stop()
type res struct {
- addrs []string
+ addrs []IP
err error
}
resc := make(chan res, 1)
go func() {
- a, err := lookupHost(host)
+ a, err := lookupIPMerge(host)
resc <- res{a, err}
}()
select {
@@ -51,12 +89,6 @@ func lookupHostDeadline(host string, deadline time.Time) (addrs []string, err er
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 error) {
- return lookupIP(host)
-}
-
// LookupPort looks up the port for the given network and service.
func LookupPort(network, service string) (port int, err error) {
return lookupPort(network, service)
diff --git a/src/pkg/net/lookup_plan9.go b/src/pkg/net/lookup_plan9.go
index 94c553328..f1204a99f 100644
--- a/src/pkg/net/lookup_plan9.go
+++ b/src/pkg/net/lookup_plan9.go
@@ -186,9 +186,9 @@ func lookupSRV(service, proto, name string) (cname string, addrs []*SRV, err err
if len(f) < 6 {
continue
}
- port, _, portOk := dtoi(f[2], 0)
+ port, _, portOk := dtoi(f[4], 0)
priority, _, priorityOk := dtoi(f[3], 0)
- weight, _, weightOk := dtoi(f[4], 0)
+ weight, _, weightOk := dtoi(f[2], 0)
if !(portOk && priorityOk && weightOk) {
continue
}
@@ -224,10 +224,10 @@ func lookupNS(name string) (ns []*NS, err error) {
}
for _, line := range lines {
f := getFields(line)
- if len(f) < 4 {
+ if len(f) < 3 {
continue
}
- ns = append(ns, &NS{f[3]})
+ ns = append(ns, &NS{f[2]})
}
return
}
diff --git a/src/pkg/net/lookup_unix.go b/src/pkg/net/lookup_unix.go
index fa98eed5f..59e9f6321 100644
--- a/src/pkg/net/lookup_unix.go
+++ b/src/pkg/net/lookup_unix.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.
-// +build darwin freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd
package net
@@ -11,15 +11,11 @@ import (
"sync"
)
-var (
- protocols map[string]int
- onceReadProtocols sync.Once
-)
+var onceReadProtocols sync.Once
// readProtocols loads contents of /etc/protocols into protocols map
// for quick access.
func readProtocols() {
- protocols = make(map[string]int)
if file, err := open("/etc/protocols"); err == nil {
for line, ok := file.readLine(); ok; line, ok = file.readLine() {
// tcp 6 TCP # transmission control protocol
@@ -31,9 +27,13 @@ func readProtocols() {
continue
}
if proto, _, ok := dtoi(f[1], 0); ok {
- protocols[f[0]] = proto
+ if _, ok := protocols[f[0]]; !ok {
+ protocols[f[0]] = proto
+ }
for _, alias := range f[2:] {
- protocols[alias] = proto
+ if _, ok := protocols[alias]; !ok {
+ protocols[alias] = proto
+ }
}
}
}
diff --git a/src/pkg/net/lookup_windows.go b/src/pkg/net/lookup_windows.go
index 3b29724f2..130364231 100644
--- a/src/pkg/net/lookup_windows.go
+++ b/src/pkg/net/lookup_windows.go
@@ -34,12 +34,19 @@ func lookupProtocol(name string) (proto int, err error) {
}
ch := make(chan result)
go func() {
+ acquireThread()
+ defer releaseThread()
runtime.LockOSThread()
defer runtime.UnlockOSThread()
proto, err := getprotobyname(name)
ch <- result{proto: proto, err: err}
}()
r := <-ch
+ if r.err != nil {
+ if proto, ok := protocols[name]; ok {
+ return proto, nil
+ }
+ }
return r.proto, r.err
}
@@ -56,6 +63,7 @@ func lookupHost(name string) (addrs []string, err error) {
}
func gethostbyname(name string) (addrs []IP, err error) {
+ // caller already acquired thread
h, err := syscall.GetHostByName(name)
if err != nil {
return nil, os.NewSyscallError("GetHostByName", err)
@@ -83,6 +91,8 @@ func oldLookupIP(name string) (addrs []IP, err error) {
}
ch := make(chan result)
go func() {
+ acquireThread()
+ defer releaseThread()
runtime.LockOSThread()
defer runtime.UnlockOSThread()
addrs, err := gethostbyname(name)
@@ -93,6 +103,8 @@ func oldLookupIP(name string) (addrs []IP, err error) {
}
func newLookupIP(name string) (addrs []IP, err error) {
+ acquireThread()
+ defer releaseThread()
hints := syscall.AddrinfoW{
Family: syscall.AF_UNSPEC,
Socktype: syscall.SOCK_STREAM,
@@ -122,6 +134,8 @@ func newLookupIP(name string) (addrs []IP, err error) {
}
func getservbyname(network, service string) (port int, err error) {
+ acquireThread()
+ defer releaseThread()
switch network {
case "tcp4", "tcp6":
network = "tcp"
@@ -144,6 +158,8 @@ func oldLookupPort(network, service string) (port int, err error) {
}
ch := make(chan result)
go func() {
+ acquireThread()
+ defer releaseThread()
runtime.LockOSThread()
defer runtime.UnlockOSThread()
port, err := getservbyname(network, service)
@@ -154,6 +170,8 @@ func oldLookupPort(network, service string) (port int, err error) {
}
func newLookupPort(network, service string) (port int, err error) {
+ acquireThread()
+ defer releaseThread()
var stype int32
switch network {
case "tcp4", "tcp6":
@@ -188,6 +206,8 @@ func newLookupPort(network, service string) (port int, err error) {
}
func lookupCNAME(name string) (cname string, err error) {
+ acquireThread()
+ defer releaseThread()
var r *syscall.DNSRecord
e := syscall.DnsQuery(name, syscall.DNS_TYPE_CNAME, 0, nil, &r, nil)
if e != nil {
@@ -202,6 +222,8 @@ func lookupCNAME(name string) (cname string, err error) {
}
func lookupSRV(service, proto, name string) (cname string, addrs []*SRV, err error) {
+ acquireThread()
+ defer releaseThread()
var target string
if service == "" && proto == "" {
target = name
@@ -224,6 +246,8 @@ func lookupSRV(service, proto, name string) (cname string, addrs []*SRV, err err
}
func lookupMX(name string) (mx []*MX, err error) {
+ acquireThread()
+ defer releaseThread()
var r *syscall.DNSRecord
e := syscall.DnsQuery(name, syscall.DNS_TYPE_MX, 0, nil, &r, nil)
if e != nil {
@@ -240,6 +264,8 @@ func lookupMX(name string) (mx []*MX, err error) {
}
func lookupNS(name string) (ns []*NS, err error) {
+ acquireThread()
+ defer releaseThread()
var r *syscall.DNSRecord
e := syscall.DnsQuery(name, syscall.DNS_TYPE_NS, 0, nil, &r, nil)
if e != nil {
@@ -255,6 +281,8 @@ func lookupNS(name string) (ns []*NS, err error) {
}
func lookupTXT(name string) (txt []string, err error) {
+ acquireThread()
+ defer releaseThread()
var r *syscall.DNSRecord
e := syscall.DnsQuery(name, syscall.DNS_TYPE_TEXT, 0, nil, &r, nil)
if e != nil {
@@ -273,6 +301,8 @@ func lookupTXT(name string) (txt []string, err error) {
}
func lookupAddr(addr string) (name []string, err error) {
+ acquireThread()
+ defer releaseThread()
arpa, err := reverseaddr(addr)
if err != nil {
return nil, err
diff --git a/src/pkg/net/mail/message.go b/src/pkg/net/mail/message.go
index 96c796e78..dc2ab44da 100644
--- a/src/pkg/net/mail/message.go
+++ b/src/pkg/net/mail/message.go
@@ -342,7 +342,9 @@ func (p *addrParser) consumePhrase() (phrase string, err error) {
word, err = p.consumeQuotedString()
} else {
// atom
- word, err = p.consumeAtom(false)
+ // We actually parse dot-atom here to be more permissive
+ // than what RFC 5322 specifies.
+ word, err = p.consumeAtom(true)
}
// RFC 2047 encoded-word starts with =?, ends with ?=, and has two other ?s.
@@ -519,7 +521,7 @@ func isAtext(c byte, dot bool) bool {
return bytes.IndexByte(atextChars, c) >= 0
}
-// isQtext returns true if c is an RFC 5322 qtest character.
+// isQtext returns true if c is an RFC 5322 qtext character.
func isQtext(c byte) bool {
// Printable US-ASCII, excluding backslash or quote.
if c == '\\' || c == '"' {
diff --git a/src/pkg/net/mail/message_test.go b/src/pkg/net/mail/message_test.go
index 2e746f4a7..3c037f383 100644
--- a/src/pkg/net/mail/message_test.go
+++ b/src/pkg/net/mail/message_test.go
@@ -225,6 +225,16 @@ func TestAddressParsing(t *testing.T) {
},
},
},
+ // Custom example with "." in name. For issue 4938
+ {
+ `Asem H. <noreply@example.com>`,
+ []*Address{
+ {
+ Name: `Asem H.`,
+ Address: "noreply@example.com",
+ },
+ },
+ },
}
for _, test := range tests {
if len(test.exp) == 1 {
diff --git a/src/pkg/net/mockicmp_test.go b/src/pkg/net/mockicmp_test.go
new file mode 100644
index 000000000..e742365ea
--- /dev/null
+++ b/src/pkg/net/mockicmp_test.go
@@ -0,0 +1,116 @@
+// 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 net
+
+import "errors"
+
+const (
+ icmpv4EchoRequest = 8
+ icmpv4EchoReply = 0
+ icmpv6EchoRequest = 128
+ icmpv6EchoReply = 129
+)
+
+// icmpMessage represents an ICMP message.
+type icmpMessage struct {
+ Type int // type
+ Code int // code
+ Checksum int // checksum
+ Body icmpMessageBody // body
+}
+
+// icmpMessageBody represents an ICMP message body.
+type icmpMessageBody interface {
+ Len() int
+ Marshal() ([]byte, error)
+}
+
+// Marshal returns the binary enconding of the ICMP echo request or
+// reply message m.
+func (m *icmpMessage) Marshal() ([]byte, error) {
+ b := []byte{byte(m.Type), byte(m.Code), 0, 0}
+ if m.Body != nil && m.Body.Len() != 0 {
+ mb, err := m.Body.Marshal()
+ if err != nil {
+ return nil, err
+ }
+ b = append(b, mb...)
+ }
+ switch m.Type {
+ case icmpv6EchoRequest, icmpv6EchoReply:
+ return b, nil
+ }
+ csumcv := len(b) - 1 // checksum coverage
+ s := uint32(0)
+ for i := 0; i < csumcv; i += 2 {
+ s += uint32(b[i+1])<<8 | uint32(b[i])
+ }
+ if csumcv&1 == 0 {
+ s += uint32(b[csumcv])
+ }
+ s = s>>16 + s&0xffff
+ s = s + s>>16
+ // Place checksum back in header; using ^= avoids the
+ // assumption the checksum bytes are zero.
+ b[2] ^= byte(^s)
+ b[3] ^= byte(^s >> 8)
+ return b, nil
+}
+
+// parseICMPMessage parses b as an ICMP message.
+func parseICMPMessage(b []byte) (*icmpMessage, error) {
+ msglen := len(b)
+ if msglen < 4 {
+ return nil, errors.New("message too short")
+ }
+ m := &icmpMessage{Type: int(b[0]), Code: int(b[1]), Checksum: int(b[2])<<8 | int(b[3])}
+ if msglen > 4 {
+ var err error
+ switch m.Type {
+ case icmpv4EchoRequest, icmpv4EchoReply, icmpv6EchoRequest, icmpv6EchoReply:
+ m.Body, err = parseICMPEcho(b[4:])
+ if err != nil {
+ return nil, err
+ }
+ }
+ }
+ return m, nil
+}
+
+// imcpEcho represenets an ICMP echo request or reply message body.
+type icmpEcho struct {
+ ID int // identifier
+ Seq int // sequence number
+ Data []byte // data
+}
+
+func (p *icmpEcho) Len() int {
+ if p == nil {
+ return 0
+ }
+ return 4 + len(p.Data)
+}
+
+// Marshal returns the binary enconding of the ICMP echo request or
+// reply message body p.
+func (p *icmpEcho) Marshal() ([]byte, error) {
+ b := make([]byte, 4+len(p.Data))
+ b[0], b[1] = byte(p.ID>>8), byte(p.ID)
+ b[2], b[3] = byte(p.Seq>>8), byte(p.Seq)
+ copy(b[4:], p.Data)
+ return b, nil
+}
+
+// parseICMPEcho parses b as an ICMP echo request or reply message
+// body.
+func parseICMPEcho(b []byte) (*icmpEcho, error) {
+ bodylen := len(b)
+ p := &icmpEcho{ID: int(b[0])<<8 | int(b[1]), Seq: int(b[2])<<8 | int(b[3])}
+ if bodylen > 4 {
+ p.Data = make([]byte, bodylen-4)
+ copy(p.Data, b[4:])
+ }
+ return p, nil
+}
diff --git a/src/pkg/net/mockserver_test.go b/src/pkg/net/mockserver_test.go
new file mode 100644
index 000000000..68ded5d75
--- /dev/null
+++ b/src/pkg/net/mockserver_test.go
@@ -0,0 +1,82 @@
+// Copyright 2013 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 "sync"
+
+type streamListener struct {
+ net, addr string
+ ln Listener
+}
+
+type dualStackServer struct {
+ lnmu sync.RWMutex
+ lns []streamListener
+ port string
+
+ cmu sync.RWMutex
+ cs []Conn // established connections at the passive open side
+}
+
+func (dss *dualStackServer) buildup(server func(*dualStackServer, Listener)) error {
+ for i := range dss.lns {
+ go server(dss, dss.lns[i].ln)
+ }
+ return nil
+}
+
+func (dss *dualStackServer) putConn(c Conn) error {
+ dss.cmu.Lock()
+ dss.cs = append(dss.cs, c)
+ dss.cmu.Unlock()
+ return nil
+}
+
+func (dss *dualStackServer) teardownNetwork(net string) error {
+ dss.lnmu.Lock()
+ for i := range dss.lns {
+ if net == dss.lns[i].net && dss.lns[i].ln != nil {
+ dss.lns[i].ln.Close()
+ dss.lns[i].ln = nil
+ }
+ }
+ dss.lnmu.Unlock()
+ return nil
+}
+
+func (dss *dualStackServer) teardown() error {
+ dss.lnmu.Lock()
+ for i := range dss.lns {
+ if dss.lns[i].ln != nil {
+ dss.lns[i].ln.Close()
+ }
+ }
+ dss.lnmu.Unlock()
+ dss.cmu.Lock()
+ for _, c := range dss.cs {
+ c.Close()
+ }
+ dss.cmu.Unlock()
+ return nil
+}
+
+func newDualStackServer(lns []streamListener) (*dualStackServer, error) {
+ dss := &dualStackServer{lns: lns, port: "0"}
+ for i := range dss.lns {
+ ln, err := Listen(dss.lns[i].net, dss.lns[i].addr+":"+dss.port)
+ if err != nil {
+ dss.teardown()
+ return nil, err
+ }
+ dss.lns[i].ln = ln
+ if dss.port == "0" {
+ if _, dss.port, err = SplitHostPort(ln.Addr().String()); err != nil {
+ dss.teardown()
+ return nil, err
+ }
+ }
+ }
+ return dss, nil
+}
diff --git a/src/pkg/net/multicast_test.go b/src/pkg/net/multicast_test.go
index 8ff02a3c9..5660fd42f 100644
--- a/src/pkg/net/multicast_test.go
+++ b/src/pkg/net/multicast_test.go
@@ -158,7 +158,7 @@ func checkMulticastListener(c *UDPConn, ip IP) error {
func multicastRIBContains(ip IP) (bool, error) {
switch runtime.GOOS {
- case "netbsd", "openbsd", "plan9", "solaris", "windows":
+ case "dragonfly", "netbsd", "openbsd", "plan9", "solaris", "windows":
return true, nil // not implemented yet
case "linux":
if runtime.GOARCH == "arm" || runtime.GOARCH == "alpha" {
diff --git a/src/pkg/net/net.go b/src/pkg/net/net.go
index 72b2b646c..2e6db5551 100644
--- a/src/pkg/net/net.go
+++ b/src/pkg/net/net.go
@@ -46,7 +46,6 @@ import (
"errors"
"io"
"os"
- "sync"
"syscall"
"time"
)
@@ -160,7 +159,7 @@ func (c *conn) SetDeadline(t time.Time) error {
if !c.ok() {
return syscall.EINVAL
}
- return setDeadline(c.fd, t)
+ return c.fd.setDeadline(t)
}
// SetReadDeadline implements the Conn SetReadDeadline method.
@@ -168,7 +167,7 @@ func (c *conn) SetReadDeadline(t time.Time) error {
if !c.ok() {
return syscall.EINVAL
}
- return setReadDeadline(c.fd, t)
+ return c.fd.setReadDeadline(t)
}
// SetWriteDeadline implements the Conn SetWriteDeadline method.
@@ -176,7 +175,7 @@ func (c *conn) SetWriteDeadline(t time.Time) error {
if !c.ok() {
return syscall.EINVAL
}
- return setWriteDeadline(c.fd, t)
+ return c.fd.setWriteDeadline(t)
}
// SetReadBuffer sets the size of the operating system's
@@ -259,6 +258,8 @@ type PacketConn interface {
SetWriteDeadline(t time.Time) error
}
+var listenerBacklog = maxListenerBacklog()
+
// A Listener is a generic network listener for stream-oriented protocols.
//
// Multiple goroutines may invoke methods on a Listener simultaneously.
@@ -370,6 +371,12 @@ func (e UnknownNetworkError) Error() string { return "unknown network " + stri
func (e UnknownNetworkError) Temporary() bool { return false }
func (e UnknownNetworkError) Timeout() bool { return false }
+type InvalidAddrError string
+
+func (e InvalidAddrError) Error() string { return string(e) }
+func (e InvalidAddrError) Timeout() bool { return false }
+func (e InvalidAddrError) Temporary() bool { return false }
+
// DNSConfigError represents an error reading the machine's DNS configuration.
type DNSConfigError struct {
Err error
@@ -393,35 +400,22 @@ func genericReadFrom(w io.Writer, r io.Reader) (n int64, err error) {
return io.Copy(writerOnly{w}, r)
}
-// deadline is an atomically-accessed number of nanoseconds since 1970
-// or 0, if no deadline is set.
-type deadline struct {
- sync.Mutex
- val int64
-}
+// Limit the number of concurrent cgo-using goroutines, because
+// each will block an entire operating system thread. The usual culprit
+// is resolving many DNS names in separate goroutines but the DNS
+// server is not responding. Then the many lookups each use a different
+// thread, and the system or the program runs out of threads.
-func (d *deadline) expired() bool {
- t := d.value()
- return t > 0 && time.Now().UnixNano() >= t
-}
+var threadLimit = make(chan struct{}, 500)
-func (d *deadline) value() (v int64) {
- d.Lock()
- v = d.val
- d.Unlock()
- return
-}
+// Using send for acquire is fine here because we are not using this
+// to protect any memory. All we care about is the number of goroutines
+// making calls at a time.
-func (d *deadline) set(v int64) {
- d.Lock()
- d.val = v
- d.Unlock()
+func acquireThread() {
+ threadLimit <- struct{}{}
}
-func (d *deadline) setTime(t time.Time) {
- if t.IsZero() {
- d.set(0)
- } else {
- d.set(t.UnixNano())
- }
+func releaseThread() {
+ <-threadLimit
}
diff --git a/src/pkg/net/net_test.go b/src/pkg/net/net_test.go
index 1a512a5b1..1320096df 100644
--- a/src/pkg/net/net_test.go
+++ b/src/pkg/net/net_test.go
@@ -25,6 +25,7 @@ func TestShutdown(t *testing.T) {
}
go func() {
+ defer ln.Close()
c, err := ln.Accept()
if err != nil {
t.Fatalf("Accept: %v", err)
@@ -75,7 +76,10 @@ func TestShutdownUnix(t *testing.T) {
if err != nil {
t.Fatalf("ListenUnix on %s: %s", tmpname, err)
}
- defer os.Remove(tmpname)
+ defer func() {
+ ln.Close()
+ os.Remove(tmpname)
+ }()
go func() {
c, err := ln.Accept()
@@ -214,3 +218,41 @@ func TestTCPClose(t *testing.T) {
t.Fatal(err)
}
}
+
+func TestErrorNil(t *testing.T) {
+ c, err := Dial("tcp", "127.0.0.1:65535")
+ if err == nil {
+ t.Fatal("Dial 127.0.0.1:65535 succeeded")
+ }
+ if c != nil {
+ t.Fatalf("Dial returned non-nil interface %T(%v) with err != nil", c, c)
+ }
+
+ // Make Listen fail by relistening on the same address.
+ l, err := Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ t.Fatal("Listen 127.0.0.1:0: %v", err)
+ }
+ defer l.Close()
+ l1, err := Listen("tcp", l.Addr().String())
+ if err == nil {
+ t.Fatal("second Listen %v: %v", l.Addr(), err)
+ }
+ if l1 != nil {
+ t.Fatalf("Listen returned non-nil interface %T(%v) with err != nil", l1, l1)
+ }
+
+ // Make ListenPacket fail by relistening on the same address.
+ lp, err := ListenPacket("udp", "127.0.0.1:0")
+ if err != nil {
+ t.Fatal("Listen 127.0.0.1:0: %v", err)
+ }
+ defer lp.Close()
+ lp1, err := ListenPacket("udp", lp.LocalAddr().String())
+ if err == nil {
+ t.Fatal("second Listen %v: %v", lp.LocalAddr(), err)
+ }
+ if lp1 != nil {
+ t.Fatalf("ListenPacket returned non-nil interface %T(%v) with err != nil", lp1, lp1)
+ }
+}
diff --git a/src/pkg/net/packetconn_test.go b/src/pkg/net/packetconn_test.go
index ec5dd710f..945003f67 100644
--- a/src/pkg/net/packetconn_test.go
+++ b/src/pkg/net/packetconn_test.go
@@ -21,6 +21,45 @@ func strfunc(s string) func() string {
}
}
+func packetConnTestData(t *testing.T, net string, i int) ([]byte, func()) {
+ switch net {
+ case "udp":
+ return []byte("UDP PACKETCONN TEST"), nil
+ case "ip":
+ if skip, skipmsg := skipRawSocketTest(t); skip {
+ return nil, func() {
+ t.Logf(skipmsg)
+ }
+ }
+ b, err := (&icmpMessage{
+ Type: icmpv4EchoRequest, Code: 0,
+ Body: &icmpEcho{
+ ID: os.Getpid() & 0xffff, Seq: i + 1,
+ Data: []byte("IP PACKETCONN TEST"),
+ },
+ }).Marshal()
+ if err != nil {
+ return nil, func() {
+ t.Fatalf("icmpMessage.Marshal failed: %v", err)
+ }
+ }
+ return b, nil
+ case "unixgram":
+ switch runtime.GOOS {
+ case "plan9", "windows":
+ return nil, func() {
+ t.Logf("skipping %q test on %q", net, runtime.GOOS)
+ }
+ default:
+ return []byte("UNIXGRAM PACKETCONN TEST"), nil
+ }
+ default:
+ return nil, func() {
+ t.Logf("skipping %q test", net)
+ }
+ }
+}
+
var packetConnTests = []struct {
net string
addr1 func() string
@@ -42,37 +81,10 @@ func TestPacketConn(t *testing.T) {
}
for i, tt := range packetConnTests {
- var wb []byte
netstr := strings.Split(tt.net, ":")
- switch netstr[0] {
- case "udp":
- wb = []byte("UDP PACKETCONN TEST")
- case "ip":
- switch runtime.GOOS {
- case "plan9":
- continue
- }
- if os.Getuid() != 0 {
- continue
- }
- var err error
- wb, err = (&icmpMessage{
- Type: icmpv4EchoRequest, Code: 0,
- Body: &icmpEcho{
- ID: os.Getpid() & 0xffff, Seq: i + 1,
- Data: []byte("IP PACKETCONN TEST"),
- },
- }).Marshal()
- if err != nil {
- t.Fatalf("icmpMessage.Marshal failed: %v", err)
- }
- case "unixgram":
- switch runtime.GOOS {
- case "plan9", "windows":
- continue
- }
- wb = []byte("UNIXGRAM PACKETCONN TEST")
- default:
+ wb, skipOrFatalFn := packetConnTestData(t, netstr[0], i)
+ if skipOrFatalFn != nil {
+ skipOrFatalFn()
continue
}
@@ -127,35 +139,9 @@ func TestConnAndPacketConn(t *testing.T) {
for i, tt := range packetConnTests {
var wb []byte
netstr := strings.Split(tt.net, ":")
- switch netstr[0] {
- case "udp":
- wb = []byte("UDP PACKETCONN TEST")
- case "ip":
- switch runtime.GOOS {
- case "plan9":
- continue
- }
- if os.Getuid() != 0 {
- continue
- }
- var err error
- wb, err = (&icmpMessage{
- Type: icmpv4EchoRequest, Code: 0,
- Body: &icmpEcho{
- ID: os.Getpid() & 0xffff, Seq: i + 1,
- Data: []byte("IP PACKETCONN TEST"),
- },
- }).Marshal()
- if err != nil {
- t.Fatalf("icmpMessage.Marshal failed: %v", err)
- }
- case "unixgram":
- switch runtime.GOOS {
- case "plan9", "windows":
- continue
- }
- wb = []byte("UNIXGRAM PACKETCONN TEST")
- default:
+ wb, skipOrFatalFn := packetConnTestData(t, netstr[0], i)
+ if skipOrFatalFn != nil {
+ skipOrFatalFn()
continue
}
@@ -186,7 +172,7 @@ func TestConnAndPacketConn(t *testing.T) {
}
rb1 := make([]byte, 128)
if _, _, err := c1.ReadFrom(rb1); err != nil {
- t.Fatalf("PacetConn.ReadFrom failed: %v", err)
+ t.Fatalf("PacketConn.ReadFrom failed: %v", err)
}
var dst Addr
switch netstr[0] {
diff --git a/src/pkg/net/parse.go b/src/pkg/net/parse.go
index 7c87b42f6..6056de248 100644
--- a/src/pkg/net/parse.go
+++ b/src/pkg/net/parse.go
@@ -54,7 +54,7 @@ func (f *file) readLine() (s string, ok bool) {
if n >= 0 {
f.data = f.data[0 : ln+n]
}
- if err == io.EOF {
+ if err == io.EOF || err == io.ErrUnexpectedEOF {
f.atEOF = true
}
}
diff --git a/src/pkg/net/parse_test.go b/src/pkg/net/parse_test.go
index 9df0c534b..b86bc3288 100644
--- a/src/pkg/net/parse_test.go
+++ b/src/pkg/net/parse_test.go
@@ -23,12 +23,14 @@ func TestReadLine(t *testing.T) {
if err != nil {
t.Fatalf("open %s: %v", filename, err)
}
+ defer fd.Close()
br := bufio.NewReader(fd)
file, err := open(filename)
if file == nil {
t.Fatalf("net.open(%s) = nil", filename)
}
+ defer file.close()
lineno := 1
byteno := 0
diff --git a/src/pkg/net/port_unix.go b/src/pkg/net/port_unix.go
index 16780da11..3cd9ca2aa 100644
--- a/src/pkg/net/port_unix.go
+++ b/src/pkg/net/port_unix.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.
-// +build darwin freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd
// Read system port mappings from /etc/services
diff --git a/src/pkg/net/protoconn_test.go b/src/pkg/net/protoconn_test.go
index b59925e01..5a8958b08 100644
--- a/src/pkg/net/protoconn_test.go
+++ b/src/pkg/net/protoconn_test.go
@@ -103,6 +103,7 @@ func TestTCPConnSpecificMethods(t *testing.T) {
}
defer c.Close()
c.SetKeepAlive(false)
+ c.SetKeepAlivePeriod(3 * time.Second)
c.SetLinger(0)
c.SetNoDelay(false)
c.LocalAddr()
@@ -160,15 +161,20 @@ func TestUDPConnSpecificMethods(t *testing.T) {
} else {
f.Close()
}
+
+ defer func() {
+ if p := recover(); p != nil {
+ t.Fatalf("UDPConn.WriteToUDP or WriteMsgUDP panicked: %v", p)
+ }
+ }()
+
+ c.WriteToUDP(wb, nil)
+ c.WriteMsgUDP(wb, nil, nil)
}
func TestIPConnSpecificMethods(t *testing.T) {
- switch runtime.GOOS {
- case "plan9":
- t.Skipf("skipping test on %q", runtime.GOOS)
- }
- if os.Getuid() != 0 {
- t.Skipf("skipping test; must be root")
+ if skip, skipmsg := skipRawSocketTest(t); skip {
+ t.Skip(skipmsg)
}
la, err := ResolveIPAddr("ip4", "127.0.0.1")
@@ -198,7 +204,7 @@ func TestIPConnSpecificMethods(t *testing.T) {
if err != nil {
t.Fatalf("icmpMessage.Marshal failed: %v", err)
}
- rb := make([]byte, 20+128)
+ rb := make([]byte, 20+len(wb))
if _, err := c.WriteToIP(wb, c.LocalAddr().(*IPAddr)); err != nil {
t.Fatalf("IPConn.WriteToIP failed: %v", err)
}
@@ -217,6 +223,15 @@ func TestIPConnSpecificMethods(t *testing.T) {
} else {
f.Close()
}
+
+ defer func() {
+ if p := recover(); p != nil {
+ t.Fatalf("IPConn.WriteToIP or WriteMsgIP panicked: %v", p)
+ }
+ }()
+
+ c.WriteToIP(wb, nil)
+ c.WriteMsgIP(wb, nil, nil)
}
func TestUnixListenerSpecificMethods(t *testing.T) {
@@ -357,4 +372,15 @@ func TestUnixConnSpecificMethods(t *testing.T) {
} else {
f.Close()
}
+
+ defer func() {
+ if p := recover(); p != nil {
+ t.Fatalf("UnixConn.WriteToUnix or WriteMsgUnix panicked: %v", p)
+ }
+ }()
+
+ c1.WriteToUnix(wb, nil)
+ c1.WriteMsgUnix(wb, nil, nil)
+ c3.WriteToUnix(wb, nil)
+ c3.WriteMsgUnix(wb, nil, nil)
}
diff --git a/src/pkg/net/race.go b/src/pkg/net/race.go
new file mode 100644
index 000000000..2f02a6c22
--- /dev/null
+++ b/src/pkg/net/race.go
@@ -0,0 +1,31 @@
+// Copyright 2013 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.
+
+// +build race
+// +build windows
+
+package net
+
+import (
+ "runtime"
+ "unsafe"
+)
+
+const raceenabled = true
+
+func raceAcquire(addr unsafe.Pointer) {
+ runtime.RaceAcquire(addr)
+}
+
+func raceReleaseMerge(addr unsafe.Pointer) {
+ runtime.RaceReleaseMerge(addr)
+}
+
+func raceReadRange(addr unsafe.Pointer, len int) {
+ runtime.RaceReadRange(addr, len)
+}
+
+func raceWriteRange(addr unsafe.Pointer, len int) {
+ runtime.RaceWriteRange(addr, len)
+}
diff --git a/src/pkg/net/race0.go b/src/pkg/net/race0.go
new file mode 100644
index 000000000..f50429779
--- /dev/null
+++ b/src/pkg/net/race0.go
@@ -0,0 +1,26 @@
+// Copyright 2013 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.
+
+// +build !race
+// +build windows
+
+package net
+
+import (
+ "unsafe"
+)
+
+const raceenabled = false
+
+func raceAcquire(addr unsafe.Pointer) {
+}
+
+func raceReleaseMerge(addr unsafe.Pointer) {
+}
+
+func raceReadRange(addr unsafe.Pointer, len int) {
+}
+
+func raceWriteRange(addr unsafe.Pointer, len int) {
+}
diff --git a/src/pkg/net/rpc/client.go b/src/pkg/net/rpc/client.go
index 4b0c9c3bb..c524d0a0a 100644
--- a/src/pkg/net/rpc/client.go
+++ b/src/pkg/net/rpc/client.go
@@ -58,6 +58,7 @@ type Client struct {
// argument to force the body of the response to be read and then
// discarded.
type ClientCodec interface {
+ // WriteRequest must be safe for concurrent use by multiple goroutines.
WriteRequest(*Request, interface{}) error
ReadResponseHeader(*Response) error
ReadResponseBody(interface{}) error
@@ -160,7 +161,7 @@ func (client *Client) input() {
}
client.mutex.Unlock()
client.sending.Unlock()
- if err != io.EOF && !closing {
+ if debugLog && err != io.EOF && !closing {
log.Println("rpc: client protocol error:", err)
}
}
@@ -172,7 +173,9 @@ func (call *Call) done() {
default:
// We don't want to block here. It is the caller's responsibility to make
// sure the channel has enough buffer space. See comment in Go().
- log.Println("rpc: discarding Call reply due to insufficient Done chan capacity")
+ if debugLog {
+ log.Println("rpc: discarding Call reply due to insufficient Done chan capacity")
+ }
}
}
diff --git a/src/pkg/net/rpc/debug.go b/src/pkg/net/rpc/debug.go
index 663663fe9..926466d62 100644
--- a/src/pkg/net/rpc/debug.go
+++ b/src/pkg/net/rpc/debug.go
@@ -38,6 +38,9 @@ const debugText = `<html>
var debug = template.Must(template.New("RPC debug").Parse(debugText))
+// If set, print log statements for internal and I/O errors.
+var debugLog = false
+
type debugMethod struct {
Type *methodType
Name string
diff --git a/src/pkg/net/rpc/jsonrpc/server.go b/src/pkg/net/rpc/jsonrpc/server.go
index 5bc05fd0a..16ec0fe9a 100644
--- a/src/pkg/net/rpc/jsonrpc/server.go
+++ b/src/pkg/net/rpc/jsonrpc/server.go
@@ -20,8 +20,7 @@ type serverCodec struct {
c io.Closer
// temporary work space
- req serverRequest
- resp serverResponse
+ req serverRequest
// JSON-RPC clients can use arbitrary json values as request IDs.
// Package rpc expects uint64 request IDs.
diff --git a/src/pkg/net/rpc/server.go b/src/pkg/net/rpc/server.go
index e71b6fb1a..7eb2dcf5a 100644
--- a/src/pkg/net/rpc/server.go
+++ b/src/pkg/net/rpc/server.go
@@ -247,10 +247,12 @@ func (server *Server) register(rcvr interface{}, name string, useName bool) erro
sname = name
}
if sname == "" {
- log.Fatal("rpc: no service name for type", s.typ.String())
+ s := "rpc.Register: no service name for type " + s.typ.String()
+ log.Print(s)
+ return errors.New(s)
}
if !isExported(sname) && !useName {
- s := "rpc Register: type " + sname + " is not exported"
+ s := "rpc.Register: type " + sname + " is not exported"
log.Print(s)
return errors.New(s)
}
@@ -258,13 +260,13 @@ func (server *Server) register(rcvr interface{}, name string, useName bool) erro
return errors.New("rpc: service already defined: " + sname)
}
s.name = sname
- s.method = make(map[string]*methodType)
// Install the methods
s.method = suitableMethods(s.typ, true)
if len(s.method) == 0 {
str := ""
+
// To help the user, see if a pointer receiver would work.
method := suitableMethods(reflect.PtrTo(s.typ), false)
if len(method) != 0 {
@@ -356,7 +358,7 @@ func (server *Server) sendResponse(sending *sync.Mutex, req *Request, reply inte
resp.Seq = req.Seq
sending.Lock()
err := codec.WriteResponse(resp, reply)
- if err != nil {
+ if debugLog && err != nil {
log.Println("rpc: writing response:", err)
}
sending.Unlock()
@@ -434,7 +436,7 @@ func (server *Server) ServeCodec(codec ServerCodec) {
for {
service, mtype, req, argv, replyv, keepReading, err := server.readRequest(codec)
if err != nil {
- if err != io.EOF {
+ if debugLog && err != io.EOF {
log.Println("rpc:", err)
}
if !keepReading {
@@ -560,20 +562,23 @@ func (server *Server) readRequestHeader(codec ServerCodec) (service *service, mt
// we can still recover and move on to the next request.
keepReading = true
- serviceMethod := strings.Split(req.ServiceMethod, ".")
- if len(serviceMethod) != 2 {
+ dot := strings.LastIndex(req.ServiceMethod, ".")
+ if dot < 0 {
err = errors.New("rpc: service/method request ill-formed: " + req.ServiceMethod)
return
}
+ serviceName := req.ServiceMethod[:dot]
+ methodName := req.ServiceMethod[dot+1:]
+
// Look up the request.
server.mu.RLock()
- service = server.serviceMap[serviceMethod[0]]
+ service = server.serviceMap[serviceName]
server.mu.RUnlock()
if service == nil {
err = errors.New("rpc: can't find service " + req.ServiceMethod)
return
}
- mtype = service.method[serviceMethod[1]]
+ mtype = service.method[methodName]
if mtype == nil {
err = errors.New("rpc: can't find method " + req.ServiceMethod)
}
@@ -612,6 +617,7 @@ func RegisterName(name string, rcvr interface{}) error {
type ServerCodec interface {
ReadRequestHeader(*Request) error
ReadRequestBody(interface{}) error
+ // WriteResponse must be safe for concurrent use by multiple goroutines.
WriteResponse(*Response, interface{}) error
Close() error
diff --git a/src/pkg/net/rpc/server_test.go b/src/pkg/net/rpc/server_test.go
index eb17210ab..3b9a88380 100644
--- a/src/pkg/net/rpc/server_test.go
+++ b/src/pkg/net/rpc/server_test.go
@@ -84,6 +84,7 @@ func listenTCP() (net.Listener, string) {
func startServer() {
Register(new(Arith))
+ RegisterName("net.rpc.Arith", new(Arith))
var l net.Listener
l, serverAddr = listenTCP()
@@ -97,11 +98,13 @@ func startServer() {
func startNewServer() {
newServer = NewServer()
newServer.Register(new(Arith))
+ newServer.RegisterName("net.rpc.Arith", new(Arith))
+ newServer.RegisterName("newServer.Arith", new(Arith))
var l net.Listener
l, newServerAddr = listenTCP()
log.Println("NewServer test RPC server listening on", newServerAddr)
- go Accept(l)
+ go newServer.Accept(l)
newServer.HandleHTTP(newHttpPath, "/bar")
httpOnce.Do(startHttpServer)
@@ -118,6 +121,7 @@ func TestRPC(t *testing.T) {
testRPC(t, serverAddr)
newOnce.Do(startNewServer)
testRPC(t, newServerAddr)
+ testNewServerRPC(t, newServerAddr)
}
func testRPC(t *testing.T, addr string) {
@@ -125,6 +129,7 @@ func testRPC(t *testing.T, addr string) {
if err != nil {
t.Fatal("dialing", err)
}
+ defer client.Close()
// Synchronous calls
args := &Args{7, 8}
@@ -233,6 +238,36 @@ func testRPC(t *testing.T, addr string) {
if reply.C != args.A*args.B {
t.Errorf("Mul: expected %d got %d", reply.C, args.A*args.B)
}
+
+ // ServiceName contain "." character
+ args = &Args{7, 8}
+ reply = new(Reply)
+ err = client.Call("net.rpc.Arith.Add", args, reply)
+ if err != nil {
+ t.Errorf("Add: expected no error but got string %q", err.Error())
+ }
+ if reply.C != args.A+args.B {
+ t.Errorf("Add: expected %d got %d", reply.C, args.A+args.B)
+ }
+}
+
+func testNewServerRPC(t *testing.T, addr string) {
+ client, err := Dial("tcp", addr)
+ if err != nil {
+ t.Fatal("dialing", err)
+ }
+ defer client.Close()
+
+ // Synchronous calls
+ args := &Args{7, 8}
+ reply := new(Reply)
+ err = client.Call("newServer.Arith.Add", args, reply)
+ if err != nil {
+ t.Errorf("Add: expected no error but got string %q", err.Error())
+ }
+ if reply.C != args.A+args.B {
+ t.Errorf("Add: expected %d got %d", reply.C, args.A+args.B)
+ }
}
func TestHTTP(t *testing.T) {
@@ -253,6 +288,7 @@ func testHTTPRPC(t *testing.T, path string) {
if err != nil {
t.Fatal("dialing", err)
}
+ defer client.Close()
// Synchronous calls
args := &Args{7, 8}
@@ -329,6 +365,7 @@ func TestServeRequest(t *testing.T) {
func testServeRequest(t *testing.T, server *Server) {
client := CodecEmulator{server: server}
+ defer client.Close()
args := &Args{7, 8}
reply := new(Reply)
@@ -411,6 +448,7 @@ func (WriteFailCodec) Close() error {
func TestSendDeadlock(t *testing.T) {
client := NewClientWithCodec(WriteFailCodec(0))
+ defer client.Close()
done := make(chan bool)
go func() {
@@ -449,6 +487,8 @@ func countMallocs(dial func() (*Client, error), t *testing.T) float64 {
if err != nil {
t.Fatal("error dialing", err)
}
+ defer client.Close()
+
args := &Args{7, 8}
reply := new(Reply)
return testing.AllocsPerRun(100, func() {
@@ -463,6 +503,9 @@ func countMallocs(dial func() (*Client, error), t *testing.T) float64 {
}
func TestCountMallocs(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping malloc count in short mode")
+ }
if runtime.GOMAXPROCS(0) > 1 {
t.Skip("skipping; GOMAXPROCS>1")
}
@@ -470,6 +513,9 @@ func TestCountMallocs(t *testing.T) {
}
func TestCountMallocsOverHTTP(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping malloc count in short mode")
+ }
if runtime.GOMAXPROCS(0) > 1 {
t.Skip("skipping; GOMAXPROCS>1")
}
@@ -496,6 +542,8 @@ func (writeCrasher) Write(p []byte) (int, error) {
func TestClientWriteError(t *testing.T) {
w := &writeCrasher{done: make(chan bool)}
c := NewClient(w)
+ defer c.Close()
+
res := false
err := c.Call("foo", 1, &res)
if err == nil {
@@ -552,6 +600,7 @@ func benchmarkEndToEnd(dial func() (*Client, error), b *testing.B) {
if err != nil {
b.Fatal("error dialing:", err)
}
+ defer client.Close()
// Synchronous calls
args := &Args{7, 8}
@@ -587,6 +636,7 @@ func benchmarkEndToEndAsync(dial func() (*Client, error), b *testing.B) {
if err != nil {
b.Fatal("error dialing:", err)
}
+ defer client.Close()
// Asynchronous calls
args := &Args{7, 8}
diff --git a/src/pkg/net/sendfile_dragonfly.go b/src/pkg/net/sendfile_dragonfly.go
new file mode 100644
index 000000000..a2219c163
--- /dev/null
+++ b/src/pkg/net/sendfile_dragonfly.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 net
+
+import (
+ "io"
+ "os"
+ "syscall"
+)
+
+// maxSendfileSize is the largest chunk size we ask the kernel to copy
+// at a time.
+const maxSendfileSize int = 4 << 20
+
+// sendFile copies the contents of r to c using the sendfile
+// system call to minimize copies.
+//
+// if handled == true, sendFile returns the number of bytes copied and any
+// non-EOF error.
+//
+// if handled == false, sendFile performed no work.
+func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
+ // DragonFly uses 0 as the "until EOF" value. If you pass in more bytes than the
+ // file contains, it will loop back to the beginning ad nauseum until it's sent
+ // exactly the number of bytes told to. As such, we need to know exactly how many
+ // bytes to send.
+ var remain int64 = 0
+
+ lr, ok := r.(*io.LimitedReader)
+ if ok {
+ remain, r = lr.N, lr.R
+ if remain <= 0 {
+ return 0, nil, true
+ }
+ }
+ f, ok := r.(*os.File)
+ if !ok {
+ return 0, nil, false
+ }
+
+ if remain == 0 {
+ fi, err := f.Stat()
+ if err != nil {
+ return 0, err, false
+ }
+
+ remain = fi.Size()
+ }
+
+ // The other quirk with DragonFly's sendfile implementation is that it doesn't
+ // use the current position of the file -- if you pass it offset 0, it starts
+ // from offset 0. There's no way to tell it "start from current position", so
+ // we have to manage that explicitly.
+ pos, err := f.Seek(0, os.SEEK_CUR)
+ if err != nil {
+ return 0, err, false
+ }
+
+ if err := c.writeLock(); err != nil {
+ return 0, err, true
+ }
+ defer c.writeUnlock()
+
+ dst := c.sysfd
+ src := int(f.Fd())
+ for remain > 0 {
+ n := maxSendfileSize
+ if int64(n) > remain {
+ n = int(remain)
+ }
+ pos1 := pos
+ n, err1 := syscall.Sendfile(dst, src, &pos1, n)
+ if n > 0 {
+ pos += int64(n)
+ written += int64(n)
+ remain -= int64(n)
+ }
+ if n == 0 && err1 == nil {
+ break
+ }
+ if err1 == syscall.EAGAIN {
+ if err1 = c.pd.WaitWrite(); err1 == nil {
+ continue
+ }
+ }
+ if err1 == syscall.EINTR {
+ continue
+ }
+ if err1 != nil {
+ // This includes syscall.ENOSYS (no kernel
+ // support) and syscall.EINVAL (fd types which
+ // don't implement sendfile together)
+ err = &OpError{"sendfile", c.net, c.raddr, err1}
+ break
+ }
+ }
+ if lr != nil {
+ lr.N = remain
+ }
+ return written, err, written > 0
+}
diff --git a/src/pkg/net/sendfile_freebsd.go b/src/pkg/net/sendfile_freebsd.go
index dc5b76755..42fe799ef 100644
--- a/src/pkg/net/sendfile_freebsd.go
+++ b/src/pkg/net/sendfile_freebsd.go
@@ -58,12 +58,10 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
return 0, err, false
}
- c.wio.Lock()
- defer c.wio.Unlock()
- if err := c.incref(false); err != nil {
+ if err := c.writeLock(); err != nil {
return 0, err, true
}
- defer c.decref()
+ defer c.writeUnlock()
dst := c.sysfd
src := int(f.Fd())
diff --git a/src/pkg/net/sendfile_linux.go b/src/pkg/net/sendfile_linux.go
index 6f1323b3d..5e117636a 100644
--- a/src/pkg/net/sendfile_linux.go
+++ b/src/pkg/net/sendfile_linux.go
@@ -36,12 +36,10 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
return 0, nil, false
}
- c.wio.Lock()
- defer c.wio.Unlock()
- if err := c.incref(false); err != nil {
+ if err := c.writeLock(); err != nil {
return 0, err, true
}
- defer c.decref()
+ defer c.writeUnlock()
dst := c.sysfd
src := int(f.Fd())
diff --git a/src/pkg/net/sendfile_windows.go b/src/pkg/net/sendfile_windows.go
index 2d64f2f5b..b128ba27b 100644
--- a/src/pkg/net/sendfile_windows.go
+++ b/src/pkg/net/sendfile_windows.go
@@ -10,20 +10,6 @@ import (
"syscall"
)
-type sendfileOp struct {
- anOp
- src syscall.Handle // source
- n uint32
-}
-
-func (o *sendfileOp) Submit() (err error) {
- return syscall.TransmitFile(o.fd.sysfd, o.src, o.n, 0, &o.o, nil, syscall.TF_WRITE_BEHIND)
-}
-
-func (o *sendfileOp) Name() string {
- return "TransmitFile"
-}
-
// sendFile copies the contents of r to c using the TransmitFile
// system call to minimize copies.
//
@@ -33,7 +19,7 @@ func (o *sendfileOp) Name() string {
// if handled == false, sendFile performed no work.
//
// Note that sendfile for windows does not suppport >2GB file.
-func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
+func sendFile(fd *netFD, r io.Reader) (written int64, err error, handled bool) {
var n int64 = 0 // by default, copy until EOF
lr, ok := r.(*io.LimitedReader)
@@ -48,18 +34,17 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
return 0, nil, false
}
- if err := c.incref(false); err != nil {
+ if err := fd.writeLock(); err != nil {
return 0, err, true
}
- defer c.decref()
- c.wio.Lock()
- defer c.wio.Unlock()
-
- var o sendfileOp
- o.Init(c, 'w')
- o.n = uint32(n)
- o.src = syscall.Handle(f.Fd())
- done, err := iosrv.ExecIO(&o, 0)
+ defer fd.writeUnlock()
+
+ o := &fd.wop
+ o.qty = uint32(n)
+ o.handle = syscall.Handle(f.Fd())
+ done, err := wsrv.ExecIO(o, "TransmitFile", func(o *operation) error {
+ return syscall.TransmitFile(o.fd.sysfd, o.handle, o.qty, 0, &o.o, nil, syscall.TF_WRITE_BEHIND)
+ })
if err != nil {
return 0, err, false
}
diff --git a/src/pkg/net/singleflight.go b/src/pkg/net/singleflight.go
new file mode 100644
index 000000000..dc58affda
--- /dev/null
+++ b/src/pkg/net/singleflight.go
@@ -0,0 +1,53 @@
+// Copyright 2013 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 "sync"
+
+// call is an in-flight or completed singleflight.Do call
+type call struct {
+ wg sync.WaitGroup
+ val interface{}
+ err error
+ dups int
+}
+
+// singleflight represents a class of work and forms a namespace in
+// which units of work can be executed with duplicate suppression.
+type singleflight struct {
+ mu sync.Mutex // protects m
+ m map[string]*call // lazily initialized
+}
+
+// Do executes and returns the results of the given function, making
+// sure that only one execution is in-flight for a given key at a
+// time. If a duplicate comes in, the duplicate caller waits for the
+// original to complete and receives the same results.
+// The return value shared indicates whether v was given to multiple callers.
+func (g *singleflight) Do(key string, fn func() (interface{}, error)) (v interface{}, err error, shared bool) {
+ g.mu.Lock()
+ if g.m == nil {
+ g.m = make(map[string]*call)
+ }
+ if c, ok := g.m[key]; ok {
+ c.dups++
+ g.mu.Unlock()
+ c.wg.Wait()
+ return c.val, c.err, true
+ }
+ c := new(call)
+ c.wg.Add(1)
+ g.m[key] = c
+ g.mu.Unlock()
+
+ c.val, c.err = fn()
+ c.wg.Done()
+
+ g.mu.Lock()
+ delete(g.m, key)
+ g.mu.Unlock()
+
+ return c.val, c.err, c.dups > 0
+}
diff --git a/src/pkg/net/smtp/smtp.go b/src/pkg/net/smtp/smtp.go
index 4b9177877..a0a478a85 100644
--- a/src/pkg/net/smtp/smtp.go
+++ b/src/pkg/net/smtp/smtp.go
@@ -41,12 +41,13 @@ type Client struct {
}
// Dial returns a new Client connected to an SMTP server at addr.
+// The addr must include a port number.
func Dial(addr string) (*Client, error) {
conn, err := net.Dial("tcp", addr)
if err != nil {
return nil, err
}
- host := addr[:strings.Index(addr, ":")]
+ host, _, _ := net.SplitHostPort(addr)
return NewClient(conn, host)
}
@@ -63,6 +64,11 @@ func NewClient(conn net.Conn, host string) (*Client, error) {
return c, nil
}
+// Close closes the connection.
+func (c *Client) Close() error {
+ return c.Text.Close()
+}
+
// hello runs a hello exchange if needed.
func (c *Client) hello() error {
if !c.didHello {
@@ -190,7 +196,9 @@ func (c *Client) Auth(a Auth) error {
default:
err = &textproto.Error{Code: code, Msg: msg64}
}
- resp, err = a.Next(msg, code == 334)
+ if err == nil {
+ resp, err = a.Next(msg, code == 334)
+ }
if err != nil {
// abort the AUTH
c.cmd(501, "*")
@@ -256,15 +264,17 @@ func (c *Client) Data() (io.WriteCloser, error) {
return &dataCloser{c, c.Text.DotWriter()}, nil
}
-// SendMail connects to the server at addr, switches to TLS if possible,
-// authenticates with mechanism a if possible, and then sends an email from
-// address from, to addresses to, with message msg.
+// SendMail connects to the server at addr, switches to TLS if
+// possible, authenticates with the optional mechanism a if possible,
+// and then sends an email from address from, to addresses to, with
+// message msg.
func SendMail(addr string, a Auth, from string, to []string, msg []byte) error {
c, err := Dial(addr)
if err != nil {
return err
}
- if err := c.hello(); err != nil {
+ defer c.Close()
+ if err = c.hello(); err != nil {
return err
}
if ok, _ := c.Extension("STARTTLS"); ok {
diff --git a/src/pkg/net/smtp/smtp_test.go b/src/pkg/net/smtp/smtp_test.go
index c190b32c0..2133dc7c7 100644
--- a/src/pkg/net/smtp/smtp_test.go
+++ b/src/pkg/net/smtp/smtp_test.go
@@ -238,6 +238,7 @@ func TestNewClient(t *testing.T) {
if err != nil {
t.Fatalf("NewClient: %v\n(after %v)", err, out())
}
+ defer c.Close()
if ok, args := c.Extension("aUtH"); !ok || args != "LOGIN PLAIN" {
t.Fatalf("Expected AUTH supported")
}
@@ -278,6 +279,7 @@ func TestNewClient2(t *testing.T) {
if err != nil {
t.Fatalf("NewClient: %v", err)
}
+ defer c.Close()
if ok, _ := c.Extension("DSN"); ok {
t.Fatalf("Shouldn't support DSN")
}
@@ -323,6 +325,7 @@ func TestHello(t *testing.T) {
if err != nil {
t.Fatalf("NewClient: %v", err)
}
+ defer c.Close()
c.localName = "customhost"
err = nil
@@ -501,3 +504,47 @@ SendMail is working for me.
.
QUIT
`
+
+func TestAuthFailed(t *testing.T) {
+ server := strings.Join(strings.Split(authFailedServer, "\n"), "\r\n")
+ client := strings.Join(strings.Split(authFailedClient, "\n"), "\r\n")
+ var cmdbuf bytes.Buffer
+ bcmdbuf := bufio.NewWriter(&cmdbuf)
+ var fake faker
+ fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(server)), bcmdbuf)
+ c, err := NewClient(fake, "fake.host")
+ if err != nil {
+ t.Fatalf("NewClient: %v", err)
+ }
+ defer c.Close()
+
+ c.tls = true
+ c.serverName = "smtp.google.com"
+ err = c.Auth(PlainAuth("", "user", "pass", "smtp.google.com"))
+
+ if err == nil {
+ t.Error("Auth: expected error; got none")
+ } else if err.Error() != "535 Invalid credentials\nplease see www.example.com" {
+ t.Errorf("Auth: got error: %v, want: %s", err, "535 Invalid credentials\nplease see www.example.com")
+ }
+
+ bcmdbuf.Flush()
+ actualcmds := cmdbuf.String()
+ if client != actualcmds {
+ t.Errorf("Got:\n%s\nExpected:\n%s", actualcmds, client)
+ }
+}
+
+var authFailedServer = `220 hello world
+250-mx.google.com at your service
+250 AUTH LOGIN PLAIN
+535-Invalid credentials
+535 please see www.example.com
+221 Goodbye
+`
+
+var authFailedClient = `EHLO localhost
+AUTH PLAIN AHVzZXIAcGFzcw==
+*
+QUIT
+`
diff --git a/src/pkg/net/sock_bsd.go b/src/pkg/net/sock_bsd.go
index d99349265..6c37109f5 100644
--- a/src/pkg/net/sock_bsd.go
+++ b/src/pkg/net/sock_bsd.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.
-// +build darwin freebsd netbsd openbsd
+// +build darwin dragonfly freebsd netbsd openbsd
package net
diff --git a/src/pkg/net/sock_plan9.go b/src/pkg/net/sock_plan9.go
new file mode 100644
index 000000000..88d9ed15c
--- /dev/null
+++ b/src/pkg/net/sock_plan9.go
@@ -0,0 +1,10 @@
+// Copyright 2013 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
+
+func maxListenerBacklog() int {
+ // /sys/include/ape/sys/socket.h:/SOMAXCONN
+ return 5
+}
diff --git a/src/pkg/net/sock_posix.go b/src/pkg/net/sock_posix.go
index be89c26db..c2d343c58 100644
--- a/src/pkg/net/sock_posix.go
+++ b/src/pkg/net/sock_posix.go
@@ -2,78 +2,197 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin freebsd linux netbsd openbsd windows
+// +build darwin dragonfly freebsd linux netbsd openbsd windows
package net
import (
+ "os"
"syscall"
"time"
)
-var listenerBacklog = maxListenerBacklog()
+// A sockaddr represents a TCP, UDP, IP or Unix network endpoint
+// address that can be converted into a syscall.Sockaddr.
+type sockaddr interface {
+ Addr
-// Generic POSIX socket creation.
-func socket(net string, f, t, p int, ipv6only bool, ulsa, ursa syscall.Sockaddr, deadline time.Time, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) {
- s, err := sysSocket(f, t, p)
+ netaddr
+
+ // family returns the platform-dependent address family
+ // identifier.
+ family() int
+
+ // isWildcard reports whether the address is a wildcard
+ // address.
+ isWildcard() bool
+
+ // sockaddr returns the address converted into a syscall
+ // sockaddr type that implements syscall.Sockaddr
+ // interface. It returns a nil interface when the address is
+ // nil.
+ sockaddr(family int) (syscall.Sockaddr, error)
+}
+
+// socket returns a network file descriptor that is ready for
+// asynchronous I/O using the network poller.
+func socket(net string, family, sotype, proto int, ipv6only bool, laddr, raddr sockaddr, deadline time.Time, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) {
+ s, err := sysSocket(family, sotype, proto)
if err != nil {
return nil, err
}
-
- if err = setDefaultSockopts(s, f, t, ipv6only); err != nil {
+ if err = setDefaultSockopts(s, family, sotype, ipv6only); err != nil {
closesocket(s)
return nil, err
}
-
- // This socket is used by a listener.
- if ulsa != nil && ursa == nil {
- // We provide a socket that listens to a wildcard
- // address with reusable UDP port when the given ulsa
- // is an appropriate UDP multicast address prefix.
- // This makes it possible for a single UDP listener
- // to join multiple different group addresses, for
- // multiple UDP listeners that listen on the same UDP
- // port to join the same group address.
- if ulsa, err = listenerSockaddr(s, f, ulsa, toAddr); err != nil {
- closesocket(s)
- return nil, err
- }
+ if fd, err = newFD(s, family, sotype, net); err != nil {
+ closesocket(s)
+ return nil, err
}
- if ulsa != nil {
- if err = syscall.Bind(s, ulsa); err != nil {
- closesocket(s)
- return nil, err
+ // This function makes a network file descriptor for the
+ // following applications:
+ //
+ // - An endpoint holder that opens a passive stream
+ // connenction, known as a stream listener
+ //
+ // - An endpoint holder that opens a destination-unspecific
+ // datagram connection, known as a datagram listener
+ //
+ // - An endpoint holder that opens an active stream or a
+ // destination-specific datagram connection, known as a
+ // dialer
+ //
+ // - An endpoint holder that opens the other connection, such
+ // as talking to the protocol stack inside the kernel
+ //
+ // For stream and datagram listeners, they will only require
+ // named sockets, so we can assume that it's just a request
+ // from stream or datagram listeners when laddr is not nil but
+ // raddr is nil. Otherwise we assume it's just for dialers or
+ // the other connection holders.
+
+ if laddr != nil && raddr == nil {
+ switch sotype {
+ case syscall.SOCK_STREAM, syscall.SOCK_SEQPACKET:
+ if err := fd.listenStream(laddr, listenerBacklog, toAddr); err != nil {
+ fd.Close()
+ return nil, err
+ }
+ return fd, nil
+ case syscall.SOCK_DGRAM:
+ if err := fd.listenDatagram(laddr, toAddr); err != nil {
+ fd.Close()
+ return nil, err
+ }
+ return fd, nil
}
}
-
- if fd, err = newFD(s, f, t, net); err != nil {
- closesocket(s)
+ if err := fd.dial(laddr, raddr, deadline, toAddr); err != nil {
+ fd.Close()
return nil, err
}
+ return fd, nil
+}
- // This socket is used by a dialer.
- if ursa != nil {
- if !deadline.IsZero() {
- setWriteDeadline(fd, deadline)
+func (fd *netFD) dial(laddr, raddr sockaddr, deadline time.Time, toAddr func(syscall.Sockaddr) Addr) error {
+ var err error
+ var lsa syscall.Sockaddr
+ if laddr != nil {
+ if lsa, err = laddr.sockaddr(fd.family); err != nil {
+ return err
+ } else if lsa != nil {
+ if err := syscall.Bind(fd.sysfd, lsa); err != nil {
+ return os.NewSyscallError("bind", err)
+ }
}
- if err = fd.connect(ulsa, ursa); err != nil {
- fd.Close()
- return nil, err
+ }
+ if err := fd.init(); err != nil {
+ return err
+ }
+ var rsa syscall.Sockaddr
+ if raddr != nil {
+ if rsa, err = raddr.sockaddr(fd.family); err != nil {
+ return err
+ } else if rsa != nil {
+ if !deadline.IsZero() {
+ fd.setWriteDeadline(deadline)
+ }
+ if err := fd.connect(lsa, rsa); err != nil {
+ return err
+ }
+ fd.isConnected = true
+ if !deadline.IsZero() {
+ fd.setWriteDeadline(noDeadline)
+ }
}
- fd.isConnected = true
- if !deadline.IsZero() {
- setWriteDeadline(fd, time.Time{})
+ }
+ lsa, _ = syscall.Getsockname(fd.sysfd)
+ if rsa, _ = syscall.Getpeername(fd.sysfd); rsa != nil {
+ fd.setAddr(toAddr(lsa), toAddr(rsa))
+ } else {
+ fd.setAddr(toAddr(lsa), raddr)
+ }
+ return nil
+}
+
+func (fd *netFD) listenStream(laddr sockaddr, backlog int, toAddr func(syscall.Sockaddr) Addr) error {
+ if err := setDefaultListenerSockopts(fd.sysfd); err != nil {
+ return err
+ }
+ if lsa, err := laddr.sockaddr(fd.family); err != nil {
+ return err
+ } else if lsa != nil {
+ if err := syscall.Bind(fd.sysfd, lsa); err != nil {
+ return os.NewSyscallError("bind", err)
}
}
+ if err := syscall.Listen(fd.sysfd, backlog); err != nil {
+ return os.NewSyscallError("listen", err)
+ }
+ if err := fd.init(); err != nil {
+ return err
+ }
+ lsa, _ := syscall.Getsockname(fd.sysfd)
+ fd.setAddr(toAddr(lsa), nil)
+ return nil
+}
- lsa, _ := syscall.Getsockname(s)
- laddr := toAddr(lsa)
- rsa, _ := syscall.Getpeername(s)
- if rsa == nil {
- rsa = ursa
- }
- raddr := toAddr(rsa)
- fd.setAddr(laddr, raddr)
- return fd, nil
+func (fd *netFD) listenDatagram(laddr sockaddr, toAddr func(syscall.Sockaddr) Addr) error {
+ switch addr := laddr.(type) {
+ case *UDPAddr:
+ // We provide a socket that listens to a wildcard
+ // address with reusable UDP port when the given laddr
+ // is an appropriate UDP multicast address prefix.
+ // This makes it possible for a single UDP listener to
+ // join multiple different group addresses, for
+ // multiple UDP listeners that listen on the same UDP
+ // port to join the same group address.
+ if addr.IP != nil && addr.IP.IsMulticast() {
+ if err := setDefaultMulticastSockopts(fd.sysfd); err != nil {
+ return err
+ }
+ addr := *addr
+ switch fd.family {
+ case syscall.AF_INET:
+ addr.IP = IPv4zero
+ case syscall.AF_INET6:
+ addr.IP = IPv6unspecified
+ }
+ laddr = &addr
+ }
+ }
+ if lsa, err := laddr.sockaddr(fd.family); err != nil {
+ return err
+ } else if lsa != nil {
+ if err := syscall.Bind(fd.sysfd, lsa); err != nil {
+ return os.NewSyscallError("bind", err)
+ }
+ }
+ if err := fd.init(); err != nil {
+ return err
+ }
+ lsa, _ := syscall.Getsockname(fd.sysfd)
+ fd.setAddr(toAddr(lsa), nil)
+ return nil
}
diff --git a/src/pkg/net/sock_unix.go b/src/pkg/net/sock_unix.go
deleted file mode 100644
index b0d6d4900..000000000
--- a/src/pkg/net/sock_unix.go
+++ /dev/null
@@ -1,36 +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.
-
-// +build darwin freebsd linux netbsd openbsd
-
-package net
-
-import "syscall"
-
-func listenerSockaddr(s, f int, la syscall.Sockaddr, toAddr func(syscall.Sockaddr) Addr) (syscall.Sockaddr, error) {
- a := toAddr(la)
- if a == nil {
- return la, nil
- }
- switch a := a.(type) {
- case *TCPAddr, *UnixAddr:
- if err := setDefaultListenerSockopts(s); err != nil {
- return nil, err
- }
- case *UDPAddr:
- if a.IP.IsMulticast() {
- if err := setDefaultMulticastSockopts(s); err != nil {
- return nil, err
- }
- switch f {
- case syscall.AF_INET:
- a.IP = IPv4zero
- case syscall.AF_INET6:
- a.IP = IPv6unspecified
- }
- return a.sockaddr(f)
- }
- }
- return la, nil
-}
diff --git a/src/pkg/net/sock_windows.go b/src/pkg/net/sock_windows.go
index 41368d39e..6ccde3a24 100644
--- a/src/pkg/net/sock_windows.go
+++ b/src/pkg/net/sock_windows.go
@@ -12,33 +12,6 @@ func maxListenerBacklog() int {
return syscall.SOMAXCONN
}
-func listenerSockaddr(s syscall.Handle, f int, la syscall.Sockaddr, toAddr func(syscall.Sockaddr) Addr) (syscall.Sockaddr, error) {
- a := toAddr(la)
- if a == nil {
- return la, nil
- }
- switch a := a.(type) {
- case *TCPAddr, *UnixAddr:
- if err := setDefaultListenerSockopts(s); err != nil {
- return nil, err
- }
- case *UDPAddr:
- if a.IP.IsMulticast() {
- if err := setDefaultMulticastSockopts(s); err != nil {
- return nil, err
- }
- switch f {
- case syscall.AF_INET:
- a.IP = IPv4zero
- case syscall.AF_INET6:
- a.IP = IPv6unspecified
- }
- return a.sockaddr(f)
- }
- }
- return la, nil
-}
-
func sysSocket(f, t, p int) (syscall.Handle, error) {
// See ../syscall/exec_unix.go for description of ForkLock.
syscall.ForkLock.RLock()
diff --git a/src/pkg/net/sockopt_bsd.go b/src/pkg/net/sockopt_bsd.go
index fff65f362..ef6eb8505 100644
--- a/src/pkg/net/sockopt_bsd.go
+++ b/src/pkg/net/sockopt_bsd.go
@@ -2,9 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin freebsd netbsd openbsd
-
-// Socket options for BSD variants
+// +build darwin dragonfly freebsd netbsd openbsd
package net
@@ -13,49 +11,31 @@ import (
"syscall"
)
-func setDefaultSockopts(s, f, t int, ipv6only bool) error {
- switch f {
- case syscall.AF_INET6:
- if ipv6only {
- syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 1)
- } else {
- // Allow both IP versions even if the OS default
- // is otherwise. Note that some operating systems
- // never admit this option.
- syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 0)
- }
+func setDefaultSockopts(s, family, sotype int, ipv6only bool) error {
+ if family == syscall.AF_INET6 && sotype != syscall.SOCK_RAW {
+ // Allow both IP versions even if the OS default
+ // is otherwise. Note that some operating systems
+ // never admit this option.
+ syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, boolint(ipv6only))
}
// Allow broadcast.
- err := syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
+ return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1))
}
func setDefaultListenerSockopts(s int) error {
// Allow reuse of recently-used addresses.
- err := syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
+ return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1))
}
func setDefaultMulticastSockopts(s int) error {
// Allow multicast UDP and raw IP datagram sockets to listen
// concurrently across multiple listeners.
- err := syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
- if err != nil {
+ if err := syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil {
return os.NewSyscallError("setsockopt", err)
}
// Allow reuse of recently-used ports.
// This option is supported only in descendants of 4.4BSD,
// to make an effective multicast application that requires
// quick draw possible.
- err = syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEPORT, 1)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
+ return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEPORT, 1))
}
diff --git a/src/pkg/net/sockopt_linux.go b/src/pkg/net/sockopt_linux.go
index 0f47538c5..54c20b140 100644
--- a/src/pkg/net/sockopt_linux.go
+++ b/src/pkg/net/sockopt_linux.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.
-// Socket options for Linux
-
package net
import (
@@ -11,41 +9,24 @@ import (
"syscall"
)
-func setDefaultSockopts(s, f, t int, ipv6only bool) error {
- switch f {
- case syscall.AF_INET6:
- if ipv6only {
- syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 1)
- } else {
- // Allow both IP versions even if the OS default
- // is otherwise. Note that some operating systems
- // never admit this option.
- syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 0)
- }
+func setDefaultSockopts(s, family, sotype int, ipv6only bool) error {
+ if family == syscall.AF_INET6 && sotype != syscall.SOCK_RAW {
+ // Allow both IP versions even if the OS default
+ // is otherwise. Note that some operating systems
+ // never admit this option.
+ syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, boolint(ipv6only))
}
// Allow broadcast.
- err := syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
+ return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1))
}
func setDefaultListenerSockopts(s int) error {
// Allow reuse of recently-used addresses.
- err := syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
+ return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1))
}
func setDefaultMulticastSockopts(s int) error {
// Allow multicast UDP and raw IP datagram sockets to listen
// concurrently across multiple listeners.
- err := syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
+ return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1))
}
diff --git a/src/pkg/net/sockopt_posix.go b/src/pkg/net/sockopt_posix.go
index 1590f4e98..ff3bc6899 100644
--- a/src/pkg/net/sockopt_posix.go
+++ b/src/pkg/net/sockopt_posix.go
@@ -2,9 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin freebsd linux netbsd openbsd windows
-
-// Socket options
+// +build darwin dragonfly freebsd linux netbsd openbsd windows
package net
@@ -103,7 +101,7 @@ done:
}
func setReadBuffer(fd *netFD, bytes int) error {
- if err := fd.incref(false); err != nil {
+ if err := fd.incref(); err != nil {
return err
}
defer fd.decref()
@@ -111,7 +109,7 @@ func setReadBuffer(fd *netFD, bytes int) error {
}
func setWriteBuffer(fd *netFD, bytes int) error {
- if err := fd.incref(false); err != nil {
+ if err := fd.incref(); err != nil {
return err
}
defer fd.decref()
@@ -119,21 +117,13 @@ func setWriteBuffer(fd *netFD, bytes int) error {
}
func setKeepAlive(fd *netFD, keepalive bool) error {
- if err := fd.incref(false); err != nil {
+ if err := fd.incref(); err != nil {
return err
}
defer fd.decref()
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, boolint(keepalive)))
}
-func setNoDelay(fd *netFD, noDelay bool) error {
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_NODELAY, boolint(noDelay)))
-}
-
func setLinger(fd *netFD, sec int) error {
var l syscall.Linger
if sec >= 0 {
@@ -143,7 +133,7 @@ func setLinger(fd *netFD, sec int) error {
l.Onoff = 0
l.Linger = 0
}
- if err := fd.incref(false); err != nil {
+ if err := fd.incref(); err != nil {
return err
}
defer fd.decref()
diff --git a/src/pkg/net/sockopt_windows.go b/src/pkg/net/sockopt_windows.go
index 0861fe8f4..cb64a40c6 100644
--- a/src/pkg/net/sockopt_windows.go
+++ b/src/pkg/net/sockopt_windows.go
@@ -2,27 +2,19 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Socket options for Windows
-
package net
import (
"os"
"syscall"
- "time"
)
-func setDefaultSockopts(s syscall.Handle, f, t int, ipv6only bool) error {
- switch f {
- case syscall.AF_INET6:
- if ipv6only {
- syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 1)
- } else {
- // Allow both IP versions even if the OS default
- // is otherwise. Note that some operating systems
- // never admit this option.
- syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 0)
- }
+func setDefaultSockopts(s syscall.Handle, family, sotype int, ipv6only bool) error {
+ if family == syscall.AF_INET6 && sotype != syscall.SOCK_RAW {
+ // Allow both IP versions even if the OS default
+ // is otherwise. Note that some operating systems
+ // never admit this option.
+ syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, boolint(ipv6only))
}
// Allow broadcast.
syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1)
@@ -42,27 +34,5 @@ func setDefaultListenerSockopts(s syscall.Handle) error {
func setDefaultMulticastSockopts(s syscall.Handle) error {
// Allow multicast UDP and raw IP datagram sockets to listen
// concurrently across multiple listeners.
- err := syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
-}
-
-// TODO(dfc) these unused error returns could be removed
-
-func setReadDeadline(fd *netFD, t time.Time) error {
- fd.rdeadline.setTime(t)
- return nil
-}
-
-func setWriteDeadline(fd *netFD, t time.Time) error {
- fd.wdeadline.setTime(t)
- return nil
-}
-
-func setDeadline(fd *netFD, t time.Time) error {
- setReadDeadline(fd, t)
- setWriteDeadline(fd, t)
- return nil
+ return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1))
}
diff --git a/src/pkg/net/sockoptip_bsd.go b/src/pkg/net/sockoptip_bsd.go
index 263f85521..2199e480d 100644
--- a/src/pkg/net/sockoptip_bsd.go
+++ b/src/pkg/net/sockoptip_bsd.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.
-// +build darwin freebsd netbsd openbsd
+// +build darwin dragonfly freebsd netbsd openbsd
package net
@@ -18,25 +18,17 @@ func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error {
}
var a [4]byte
copy(a[:], ip.To4())
- if err := fd.incref(false); err != nil {
+ if err := fd.incref(); err != nil {
return err
}
defer fd.decref()
- err = syscall.SetsockoptInet4Addr(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, a)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
+ return os.NewSyscallError("setsockopt", syscall.SetsockoptInet4Addr(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, a))
}
func setIPv4MulticastLoopback(fd *netFD, v bool) error {
- if err := fd.incref(false); err != nil {
+ if err := fd.incref(); err != nil {
return err
}
defer fd.decref()
- err := syscall.SetsockoptByte(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, byte(boolint(v)))
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
+ return os.NewSyscallError("setsockopt", syscall.SetsockoptByte(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, byte(boolint(v))))
}
diff --git a/src/pkg/net/sockoptip_linux.go b/src/pkg/net/sockoptip_linux.go
index 225fb0c4c..a69b778e4 100644
--- a/src/pkg/net/sockoptip_linux.go
+++ b/src/pkg/net/sockoptip_linux.go
@@ -15,25 +15,17 @@ func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error {
v = int32(ifi.Index)
}
mreq := &syscall.IPMreqn{Ifindex: v}
- if err := fd.incref(false); err != nil {
+ if err := fd.incref(); err != nil {
return err
}
defer fd.decref()
- err := syscall.SetsockoptIPMreqn(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, mreq)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
+ return os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreqn(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, mreq))
}
func setIPv4MulticastLoopback(fd *netFD, v bool) error {
- if err := fd.incref(false); err != nil {
+ if err := fd.incref(); err != nil {
return err
}
defer fd.decref()
- err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, boolint(v))
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
+ return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, boolint(v)))
}
diff --git a/src/pkg/net/sockoptip_posix.go b/src/pkg/net/sockoptip_posix.go
index e4c56a0e4..c2579be91 100644
--- a/src/pkg/net/sockoptip_posix.go
+++ b/src/pkg/net/sockoptip_posix.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.
-// +build darwin freebsd linux netbsd openbsd windows
+// +build darwin dragonfly freebsd linux netbsd openbsd windows
package net
@@ -16,15 +16,11 @@ func joinIPv4Group(fd *netFD, ifi *Interface, ip IP) error {
if err := setIPv4MreqToInterface(mreq, ifi); err != nil {
return err
}
- if err := fd.incref(false); err != nil {
+ if err := fd.incref(); err != nil {
return err
}
defer fd.decref()
- err := syscall.SetsockoptIPMreq(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_ADD_MEMBERSHIP, mreq)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
+ return os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_ADD_MEMBERSHIP, mreq))
}
func setIPv6MulticastInterface(fd *netFD, ifi *Interface) error {
@@ -32,27 +28,19 @@ func setIPv6MulticastInterface(fd *netFD, ifi *Interface) error {
if ifi != nil {
v = ifi.Index
}
- if err := fd.incref(false); err != nil {
+ if err := fd.incref(); err != nil {
return err
}
defer fd.decref()
- err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_IF, v)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
+ return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_IF, v))
}
func setIPv6MulticastLoopback(fd *netFD, v bool) error {
- if err := fd.incref(false); err != nil {
+ if err := fd.incref(); err != nil {
return err
}
defer fd.decref()
- err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_LOOP, boolint(v))
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
+ return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_LOOP, boolint(v)))
}
func joinIPv6Group(fd *netFD, ifi *Interface, ip IP) error {
@@ -61,13 +49,9 @@ func joinIPv6Group(fd *netFD, ifi *Interface, ip IP) error {
if ifi != nil {
mreq.Interface = uint32(ifi.Index)
}
- if err := fd.incref(false); err != nil {
+ if err := fd.incref(); err != nil {
return err
}
defer fd.decref()
- err := syscall.SetsockoptIPv6Mreq(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_JOIN_GROUP, mreq)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
+ return os.NewSyscallError("setsockopt", syscall.SetsockoptIPv6Mreq(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_JOIN_GROUP, mreq))
}
diff --git a/src/pkg/net/sockoptip_windows.go b/src/pkg/net/sockoptip_windows.go
index 3e248441a..7b11f207a 100644
--- a/src/pkg/net/sockoptip_windows.go
+++ b/src/pkg/net/sockoptip_windows.go
@@ -17,26 +17,17 @@ func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error {
}
var a [4]byte
copy(a[:], ip.To4())
- if err := fd.incref(false); err != nil {
+ if err := fd.incref(); err != nil {
return err
}
defer fd.decref()
- err = syscall.Setsockopt(fd.sysfd, int32(syscall.IPPROTO_IP), int32(syscall.IP_MULTICAST_IF), (*byte)(unsafe.Pointer(&a[0])), 4)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
+ return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, (*byte)(unsafe.Pointer(&a[0])), 4))
}
func setIPv4MulticastLoopback(fd *netFD, v bool) error {
- if err := fd.incref(false); err != nil {
+ if err := fd.incref(); err != nil {
return err
}
defer fd.decref()
- vv := int32(boolint(v))
- err := syscall.Setsockopt(fd.sysfd, int32(syscall.IPPROTO_IP), int32(syscall.IP_MULTICAST_LOOP), (*byte)(unsafe.Pointer(&vv)), 4)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
+ return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, boolint(v)))
}
diff --git a/src/pkg/net/sys_cloexec.go b/src/pkg/net/sys_cloexec.go
index 17e874908..bbfcc1a4f 100644
--- a/src/pkg/net/sys_cloexec.go
+++ b/src/pkg/net/sys_cloexec.go
@@ -5,7 +5,7 @@
// This file implements sysSocket and accept for platforms that do not
// provide a fast path for setting SetNonblock and CloseOnExec.
-// +build darwin freebsd netbsd openbsd
+// +build darwin dragonfly freebsd netbsd openbsd
package net
diff --git a/src/pkg/net/tcp_test.go b/src/pkg/net/tcp_test.go
index a71b02b47..62fd99f5c 100644
--- a/src/pkg/net/tcp_test.go
+++ b/src/pkg/net/tcp_test.go
@@ -6,8 +6,10 @@ package net
import (
"fmt"
+ "io"
"reflect"
"runtime"
+ "sync"
"testing"
"time"
)
@@ -59,7 +61,7 @@ func BenchmarkTCP6PersistentTimeout(b *testing.B) {
func benchmarkTCP(b *testing.B, persistent, timeout bool, laddr string) {
const msgLen = 512
conns := b.N
- numConcurrent := runtime.GOMAXPROCS(-1) * 16
+ numConcurrent := runtime.GOMAXPROCS(-1) * 2
msgs := 1
if persistent {
conns = numConcurrent
@@ -147,11 +149,134 @@ func benchmarkTCP(b *testing.B, persistent, timeout bool, laddr string) {
}
}
+func BenchmarkTCP4ConcurrentReadWrite(b *testing.B) {
+ benchmarkTCPConcurrentReadWrite(b, "127.0.0.1:0")
+}
+
+func BenchmarkTCP6ConcurrentReadWrite(b *testing.B) {
+ if !supportsIPv6 {
+ b.Skip("ipv6 is not supported")
+ }
+ benchmarkTCPConcurrentReadWrite(b, "[::1]:0")
+}
+
+func benchmarkTCPConcurrentReadWrite(b *testing.B, laddr string) {
+ // The benchmark creates GOMAXPROCS client/server pairs.
+ // Each pair creates 4 goroutines: client reader/writer and server reader/writer.
+ // The benchmark stresses concurrent reading and writing to the same connection.
+ // Such pattern is used in net/http and net/rpc.
+
+ b.StopTimer()
+
+ P := runtime.GOMAXPROCS(0)
+ N := b.N / P
+ W := 1000
+
+ // Setup P client/server connections.
+ clients := make([]Conn, P)
+ servers := make([]Conn, P)
+ ln, err := Listen("tcp", laddr)
+ if err != nil {
+ b.Fatalf("Listen failed: %v", err)
+ }
+ defer ln.Close()
+ done := make(chan bool)
+ go func() {
+ for p := 0; p < P; p++ {
+ s, err := ln.Accept()
+ if err != nil {
+ b.Fatalf("Accept failed: %v", err)
+ }
+ servers[p] = s
+ }
+ done <- true
+ }()
+ for p := 0; p < P; p++ {
+ c, err := Dial("tcp", ln.Addr().String())
+ if err != nil {
+ b.Fatalf("Dial failed: %v", err)
+ }
+ clients[p] = c
+ }
+ <-done
+
+ b.StartTimer()
+
+ var wg sync.WaitGroup
+ wg.Add(4 * P)
+ for p := 0; p < P; p++ {
+ // Client writer.
+ go func(c Conn) {
+ defer wg.Done()
+ var buf [1]byte
+ for i := 0; i < N; i++ {
+ v := byte(i)
+ for w := 0; w < W; w++ {
+ v *= v
+ }
+ buf[0] = v
+ _, err := c.Write(buf[:])
+ if err != nil {
+ b.Fatalf("Write failed: %v", err)
+ }
+ }
+ }(clients[p])
+
+ // Pipe between server reader and server writer.
+ pipe := make(chan byte, 128)
+
+ // Server reader.
+ go func(s Conn) {
+ defer wg.Done()
+ var buf [1]byte
+ for i := 0; i < N; i++ {
+ _, err := s.Read(buf[:])
+ if err != nil {
+ b.Fatalf("Read failed: %v", err)
+ }
+ pipe <- buf[0]
+ }
+ }(servers[p])
+
+ // Server writer.
+ go func(s Conn) {
+ defer wg.Done()
+ var buf [1]byte
+ for i := 0; i < N; i++ {
+ v := <-pipe
+ for w := 0; w < W; w++ {
+ v *= v
+ }
+ buf[0] = v
+ _, err := s.Write(buf[:])
+ if err != nil {
+ b.Fatalf("Write failed: %v", err)
+ }
+ }
+ s.Close()
+ }(servers[p])
+
+ // Client reader.
+ go func(c Conn) {
+ defer wg.Done()
+ var buf [1]byte
+ for i := 0; i < N; i++ {
+ _, err := c.Read(buf[:])
+ if err != nil {
+ b.Fatalf("Read failed: %v", err)
+ }
+ }
+ c.Close()
+ }(clients[p])
+ }
+ wg.Wait()
+}
+
type resolveTCPAddrTest struct {
- net string
- litAddr string
- addr *TCPAddr
- err error
+ net string
+ litAddrOrName string
+ addr *TCPAddr
+ err error
}
var resolveTCPAddrTests = []resolveTCPAddrTest{
@@ -167,6 +292,8 @@ var resolveTCPAddrTests = []resolveTCPAddrTest{
{"", "127.0.0.1:0", &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 0}, nil}, // Go 1.0 behavior
{"", "[::1]:0", &TCPAddr{IP: ParseIP("::1"), Port: 0}, nil}, // Go 1.0 behavior
+ {"tcp", ":12345", &TCPAddr{Port: 12345}, nil},
+
{"http", "127.0.0.1:0", nil, UnknownNetworkError("http")},
}
@@ -178,16 +305,33 @@ func init() {
{"tcp6", "[fe80::1%" + index + "]:4", &TCPAddr{IP: ParseIP("fe80::1"), Port: 4, Zone: index}, nil},
}...)
}
+ if ips, err := LookupIP("localhost"); err == nil && len(ips) > 1 && supportsIPv4 && supportsIPv6 {
+ resolveTCPAddrTests = append(resolveTCPAddrTests, []resolveTCPAddrTest{
+ {"tcp", "localhost:5", &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5}, nil},
+ {"tcp4", "localhost:6", &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 6}, nil},
+ {"tcp6", "localhost:7", &TCPAddr{IP: IPv6loopback, Port: 7}, nil},
+ }...)
+ }
}
func TestResolveTCPAddr(t *testing.T) {
for _, tt := range resolveTCPAddrTests {
- addr, err := ResolveTCPAddr(tt.net, tt.litAddr)
+ addr, err := ResolveTCPAddr(tt.net, tt.litAddrOrName)
if err != tt.err {
- t.Fatalf("ResolveTCPAddr(%v, %v) failed: %v", tt.net, tt.litAddr, err)
+ t.Fatalf("ResolveTCPAddr(%q, %q) failed: %v", tt.net, tt.litAddrOrName, err)
}
if !reflect.DeepEqual(addr, tt.addr) {
- t.Fatalf("got %#v; expected %#v", addr, tt.addr)
+ t.Fatalf("ResolveTCPAddr(%q, %q) = %#v, want %#v", tt.net, tt.litAddrOrName, addr, tt.addr)
+ }
+ if err == nil {
+ str := addr.String()
+ addr1, err := ResolveTCPAddr(tt.net, str)
+ if err != nil {
+ t.Fatalf("ResolveTCPAddr(%q, %q) [from %q]: %v", tt.net, str, tt.litAddrOrName, err)
+ }
+ if !reflect.DeepEqual(addr1, addr) {
+ t.Fatalf("ResolveTCPAddr(%q, %q) [from %q] = %#v, want %#v", tt.net, str, tt.litAddrOrName, addr1, addr)
+ }
}
}
}
@@ -294,3 +438,153 @@ func TestIPv6LinkLocalUnicastTCP(t *testing.T) {
<-done
}
}
+
+func TestTCPConcurrentAccept(t *testing.T) {
+ defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
+ ln, err := Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ t.Fatalf("Listen failed: %v", err)
+ }
+ const N = 10
+ var wg sync.WaitGroup
+ wg.Add(N)
+ for i := 0; i < N; i++ {
+ go func() {
+ for {
+ c, err := ln.Accept()
+ if err != nil {
+ break
+ }
+ c.Close()
+ }
+ wg.Done()
+ }()
+ }
+ for i := 0; i < 10*N; i++ {
+ c, err := Dial("tcp", ln.Addr().String())
+ if err != nil {
+ t.Fatalf("Dial failed: %v", err)
+ }
+ c.Close()
+ }
+ ln.Close()
+ wg.Wait()
+}
+
+func TestTCPReadWriteMallocs(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping malloc count in short mode")
+ }
+ ln, err := Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ t.Fatalf("Listen failed: %v", err)
+ }
+ defer ln.Close()
+ var server Conn
+ errc := make(chan error)
+ go func() {
+ var err error
+ server, err = ln.Accept()
+ errc <- err
+ }()
+ client, err := Dial("tcp", ln.Addr().String())
+ if err != nil {
+ t.Fatalf("Dial failed: %v", err)
+ }
+ if err := <-errc; err != nil {
+ t.Fatalf("Accept failed: %v", err)
+ }
+ defer server.Close()
+ var buf [128]byte
+ mallocs := testing.AllocsPerRun(1000, func() {
+ _, err := server.Write(buf[:])
+ if err != nil {
+ t.Fatalf("Write failed: %v", err)
+ }
+ _, err = io.ReadFull(client, buf[:])
+ if err != nil {
+ t.Fatalf("Read failed: %v", err)
+ }
+ })
+ if mallocs > 0 {
+ t.Fatalf("Got %v allocs, want 0", mallocs)
+ }
+}
+
+func TestTCPStress(t *testing.T) {
+ const conns = 2
+ const msgLen = 512
+ msgs := int(1e4)
+ if testing.Short() {
+ msgs = 1e2
+ }
+
+ sendMsg := func(c Conn, buf []byte) bool {
+ n, err := c.Write(buf)
+ if n != len(buf) || err != nil {
+ t.Logf("Write failed: %v", err)
+ return false
+ }
+ return true
+ }
+ recvMsg := func(c Conn, buf []byte) bool {
+ for read := 0; read != len(buf); {
+ n, err := c.Read(buf)
+ read += n
+ if err != nil {
+ t.Logf("Read failed: %v", err)
+ return false
+ }
+ }
+ return true
+ }
+
+ ln, err := Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ t.Fatalf("Listen failed: %v", err)
+ }
+ defer ln.Close()
+ // Acceptor.
+ go func() {
+ for {
+ c, err := ln.Accept()
+ if err != nil {
+ break
+ }
+ // Server connection.
+ go func(c Conn) {
+ defer c.Close()
+ var buf [msgLen]byte
+ for m := 0; m < msgs; m++ {
+ if !recvMsg(c, buf[:]) || !sendMsg(c, buf[:]) {
+ break
+ }
+ }
+ }(c)
+ }
+ }()
+ done := make(chan bool)
+ for i := 0; i < conns; i++ {
+ // Client connection.
+ go func() {
+ defer func() {
+ done <- true
+ }()
+ c, err := Dial("tcp", ln.Addr().String())
+ if err != nil {
+ t.Logf("Dial failed: %v", err)
+ return
+ }
+ defer c.Close()
+ var buf [msgLen]byte
+ for m := 0; m < msgs; m++ {
+ if !sendMsg(c, buf[:]) || !recvMsg(c, buf[:]) {
+ break
+ }
+ }
+ }()
+ }
+ for i := 0; i < conns; i++ {
+ <-done
+ }
+}
diff --git a/src/pkg/net/tcpsock.go b/src/pkg/net/tcpsock.go
index 4d9ebd214..f3dfbd23d 100644
--- a/src/pkg/net/tcpsock.go
+++ b/src/pkg/net/tcpsock.go
@@ -18,10 +18,18 @@ func (a *TCPAddr) String() string {
if a == nil {
return "<nil>"
}
+ ip := ipEmptyString(a.IP)
if a.Zone != "" {
- return JoinHostPort(a.IP.String()+"%"+a.Zone, itoa(a.Port))
+ return JoinHostPort(ip+"%"+a.Zone, itoa(a.Port))
}
- return JoinHostPort(a.IP.String(), itoa(a.Port))
+ return JoinHostPort(ip, itoa(a.Port))
+}
+
+func (a *TCPAddr) toAddr() Addr {
+ if a == nil {
+ return nil
+ }
+ return a
}
// ResolveTCPAddr parses addr as a TCP address of the form "host:port"
@@ -42,5 +50,5 @@ func ResolveTCPAddr(net, addr string) (*TCPAddr, error) {
if err != nil {
return nil, err
}
- return a.(*TCPAddr), nil
+ return a.toAddr().(*TCPAddr), nil
}
diff --git a/src/pkg/net/tcpsock_plan9.go b/src/pkg/net/tcpsock_plan9.go
index 48334fed7..cf9c0f890 100644
--- a/src/pkg/net/tcpsock_plan9.go
+++ b/src/pkg/net/tcpsock_plan9.go
@@ -65,6 +65,11 @@ func (c *TCPConn) SetKeepAlive(keepalive bool) error {
return syscall.EPLAN9
}
+// SetKeepAlivePeriod sets period between keep alives.
+func (c *TCPConn) SetKeepAlivePeriod(d time.Duration) error {
+ return syscall.EPLAN9
+}
+
// SetNoDelay controls whether the operating system should delay
// packet transmission in hopes of sending fewer packets (Nagle's
// algorithm). The default is true (no delay), meaning that data is
@@ -106,7 +111,7 @@ type TCPListener struct {
}
// AcceptTCP accepts the next incoming call and returns the new
-// connection and the remote address.
+// connection.
func (l *TCPListener) AcceptTCP() (*TCPConn, error) {
if l == nil || l.fd == nil || l.fd.ctl == nil {
return nil, syscall.EINVAL
@@ -153,7 +158,7 @@ func (l *TCPListener) SetDeadline(t time.Time) error {
if l == nil || l.fd == nil || l.fd.ctl == nil {
return syscall.EINVAL
}
- return setDeadline(l.fd, t)
+ return l.fd.setDeadline(t)
}
// File returns a copy of the underlying os.File, set to blocking
diff --git a/src/pkg/net/tcpsock_posix.go b/src/pkg/net/tcpsock_posix.go
index 876edb101..00c692e42 100644
--- a/src/pkg/net/tcpsock_posix.go
+++ b/src/pkg/net/tcpsock_posix.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.
-// +build darwin freebsd linux netbsd openbsd windows
+// +build darwin dragonfly freebsd linux netbsd openbsd windows
package net
@@ -46,14 +46,10 @@ func (a *TCPAddr) isWildcard() bool {
}
func (a *TCPAddr) sockaddr(family int) (syscall.Sockaddr, error) {
- return ipToSockaddr(family, a.IP, a.Port, a.Zone)
-}
-
-func (a *TCPAddr) toAddr() sockaddr {
- if a == nil { // nil *TCPAddr
- return nil // nil interface
+ if a == nil {
+ return nil, nil
}
- return a
+ return ipToSockaddr(family, a.IP, a.Port, a.Zone)
}
// TCPConn is an implementation of the Conn interface for TCP network
@@ -121,6 +117,14 @@ func (c *TCPConn) SetKeepAlive(keepalive bool) error {
return setKeepAlive(c.fd, keepalive)
}
+// SetKeepAlivePeriod sets period between keep alives.
+func (c *TCPConn) SetKeepAlivePeriod(d time.Duration) error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ return setKeepAlivePeriod(c.fd, d)
+}
+
// SetNoDelay controls whether the operating system should delay
// packet transmission in hopes of sending fewer packets (Nagle's
// algorithm). The default is true (no delay), meaning that data is
@@ -139,16 +143,16 @@ func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
switch net {
case "tcp", "tcp4", "tcp6":
default:
- return nil, UnknownNetworkError(net)
+ return nil, &OpError{Op: "dial", Net: net, Addr: raddr, Err: UnknownNetworkError(net)}
}
if raddr == nil {
- return nil, &OpError{"dial", net, nil, errMissingAddress}
+ return nil, &OpError{Op: "dial", Net: net, Addr: nil, Err: errMissingAddress}
}
return dialTCP(net, laddr, raddr, noDeadline)
}
func dialTCP(net string, laddr, raddr *TCPAddr, deadline time.Time) (*TCPConn, error) {
- fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), deadline, syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP)
+ fd, err := internetSocket(net, laddr, raddr, deadline, syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP)
// TCP has a rarely used mechanism called a 'simultaneous connection' in
// which Dial("tcp", addr1, addr2) run on the machine at addr1 can
@@ -178,11 +182,11 @@ func dialTCP(net string, laddr, raddr *TCPAddr, deadline time.Time) (*TCPConn, e
if err == nil {
fd.Close()
}
- fd, err = internetSocket(net, laddr.toAddr(), raddr.toAddr(), deadline, syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP)
+ fd, err = internetSocket(net, laddr, raddr, deadline, syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP)
}
if err != nil {
- return nil, err
+ return nil, &OpError{Op: "dial", Net: net, Addr: raddr, Err: err}
}
return newTCPConn(fd), nil
}
@@ -221,7 +225,7 @@ type TCPListener struct {
}
// AcceptTCP accepts the next incoming call and returns the new
-// connection and the remote address.
+// connection.
func (l *TCPListener) AcceptTCP() (*TCPConn, error) {
if l == nil || l.fd == nil {
return nil, syscall.EINVAL
@@ -261,7 +265,7 @@ func (l *TCPListener) SetDeadline(t time.Time) error {
if l == nil || l.fd == nil {
return syscall.EINVAL
}
- return setDeadline(l.fd, t)
+ return l.fd.setDeadline(t)
}
// File returns a copy of the underlying os.File, set to blocking
@@ -281,19 +285,14 @@ func ListenTCP(net string, laddr *TCPAddr) (*TCPListener, error) {
switch net {
case "tcp", "tcp4", "tcp6":
default:
- return nil, UnknownNetworkError(net)
+ return nil, &OpError{Op: "listen", Net: net, Addr: laddr, Err: UnknownNetworkError(net)}
}
if laddr == nil {
laddr = &TCPAddr{}
}
- fd, err := internetSocket(net, laddr.toAddr(), nil, noDeadline, syscall.SOCK_STREAM, 0, "listen", sockaddrToTCP)
- if err != nil {
- return nil, err
- }
- err = syscall.Listen(fd.sysfd, listenerBacklog)
+ fd, err := internetSocket(net, laddr, nil, noDeadline, syscall.SOCK_STREAM, 0, "listen", sockaddrToTCP)
if err != nil {
- fd.Close()
- return nil, &OpError{"listen", net, laddr, err}
+ return nil, &OpError{Op: "listen", Net: net, Addr: laddr, Err: err}
}
return &TCPListener{fd}, nil
}
diff --git a/src/pkg/net/tcpsockopt_darwin.go b/src/pkg/net/tcpsockopt_darwin.go
new file mode 100644
index 000000000..33140849c
--- /dev/null
+++ b/src/pkg/net/tcpsockopt_darwin.go
@@ -0,0 +1,27 @@
+// 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.
+
+// TCP socket options for darwin
+
+package net
+
+import (
+ "os"
+ "syscall"
+ "time"
+)
+
+// Set keep alive period.
+func setKeepAlivePeriod(fd *netFD, d time.Duration) error {
+ if err := fd.incref(); err != nil {
+ return err
+ }
+ defer fd.decref()
+
+ // The kernel expects seconds so round to next highest second.
+ d += (time.Second - time.Nanosecond)
+ secs := int(d.Seconds())
+
+ return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_KEEPALIVE, secs))
+}
diff --git a/src/pkg/net/tcpsockopt_openbsd.go b/src/pkg/net/tcpsockopt_openbsd.go
new file mode 100644
index 000000000..3480f932c
--- /dev/null
+++ b/src/pkg/net/tcpsockopt_openbsd.go
@@ -0,0 +1,27 @@
+// 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.
+
+// TCP socket options for openbsd
+
+package net
+
+import (
+ "os"
+ "syscall"
+ "time"
+)
+
+// Set keep alive period.
+func setKeepAlivePeriod(fd *netFD, d time.Duration) error {
+ if err := fd.incref(); err != nil {
+ return err
+ }
+ defer fd.decref()
+
+ // The kernel expects seconds so round to next highest second.
+ d += (time.Second - time.Nanosecond)
+ secs := int(d.Seconds())
+
+ return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.SO_KEEPALIVE, secs))
+}
diff --git a/src/pkg/net/tcpsockopt_posix.go b/src/pkg/net/tcpsockopt_posix.go
new file mode 100644
index 000000000..e03476ac6
--- /dev/null
+++ b/src/pkg/net/tcpsockopt_posix.go
@@ -0,0 +1,20 @@
+// 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.
+
+// +build darwin dragonfly freebsd linux netbsd openbsd windows
+
+package net
+
+import (
+ "os"
+ "syscall"
+)
+
+func setNoDelay(fd *netFD, noDelay bool) error {
+ if err := fd.incref(); err != nil {
+ return err
+ }
+ defer fd.decref()
+ return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_NODELAY, boolint(noDelay)))
+}
diff --git a/src/pkg/net/tcpsockopt_unix.go b/src/pkg/net/tcpsockopt_unix.go
new file mode 100644
index 000000000..89d9143b5
--- /dev/null
+++ b/src/pkg/net/tcpsockopt_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.
+
+// +build dragonfly freebsd linux netbsd
+
+package net
+
+import (
+ "os"
+ "syscall"
+ "time"
+)
+
+// Set keep alive period.
+func setKeepAlivePeriod(fd *netFD, d time.Duration) error {
+ if err := fd.incref(); err != nil {
+ return err
+ }
+ defer fd.decref()
+
+ // The kernel expects seconds so round to next highest second.
+ d += (time.Second - time.Nanosecond)
+ secs := int(d.Seconds())
+
+ err := os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, secs))
+ if err != nil {
+ return err
+ }
+ return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, secs))
+}
diff --git a/src/pkg/net/tcpsockopt_windows.go b/src/pkg/net/tcpsockopt_windows.go
new file mode 100644
index 000000000..0bf4312f2
--- /dev/null
+++ b/src/pkg/net/tcpsockopt_windows.go
@@ -0,0 +1,21 @@
+// 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.
+
+// TCP socket options for windows
+
+package net
+
+import (
+ "time"
+)
+
+func setKeepAlivePeriod(fd *netFD, d time.Duration) error {
+ if err := fd.incref(); err != nil {
+ return err
+ }
+ defer fd.decref()
+
+ // We can't actually set this per connection. Act as a noop rather than an error.
+ return nil
+}
diff --git a/src/pkg/net/testdata/hosts_singleline b/src/pkg/net/testdata/hosts_singleline
new file mode 100644
index 000000000..5f5f74a3f
--- /dev/null
+++ b/src/pkg/net/testdata/hosts_singleline
@@ -0,0 +1 @@
+127.0.0.2 odin \ No newline at end of file
diff --git a/src/pkg/net/textproto/reader.go b/src/pkg/net/textproto/reader.go
index 5bd26ac8d..b0c07413c 100644
--- a/src/pkg/net/textproto/reader.go
+++ b/src/pkg/net/textproto/reader.go
@@ -203,7 +203,7 @@ func parseCodeLine(line string, expectCode int) (code int, continued bool, messa
// ReadCodeLine reads a response code line of the form
// code message
-// where code is a 3-digit status code and the message
+// where code is a three-digit status code and the message
// extends to the rest of the line. An example of such a line is:
// 220 plan9.bell-labs.com ESMTP
//
@@ -231,7 +231,7 @@ func (r *Reader) ReadCodeLine(expectCode int) (code int, message string, err err
// ...
// code message line n
//
-// where code is a 3-digit status code. The first line starts with the
+// where code is a three-digit status code. The first line starts with the
// code and a hyphen. The response is terminated by a line that starts
// with the same code followed by a space. Each line in message is
// separated by a newline (\n).
@@ -456,7 +456,16 @@ func (r *Reader) ReadDotLines() ([]string, error) {
// }
//
func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) {
- m := make(MIMEHeader, 4)
+ // Avoid lots of small slice allocations later by allocating one
+ // large one ahead of time which we'll cut up into smaller
+ // slices. If this isn't big enough later, we allocate small ones.
+ var strs []string
+ hint := r.upcomingHeaderNewlines()
+ if hint > 0 {
+ strs = make([]string, hint)
+ }
+
+ m := make(MIMEHeader, hint)
for {
kv, err := r.readContinuedLineSlice()
if len(kv) == 0 {
@@ -483,7 +492,18 @@ func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) {
}
value := string(kv[i:])
- m[key] = append(m[key], value)
+ vv := m[key]
+ if vv == nil && len(strs) > 0 {
+ // More than likely this will be a single-element key.
+ // Most headers aren't multi-valued.
+ // Set the capacity on strs[0] to 1, so any future append
+ // won't extend the slice into the other strings.
+ vv, strs = strs[:1:1], strs[1:]
+ vv[0] = value
+ m[key] = vv
+ } else {
+ m[key] = append(vv, value)
+ }
if err != nil {
return m, err
@@ -491,6 +511,29 @@ func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) {
}
}
+// upcomingHeaderNewlines returns an approximation of the number of newlines
+// that will be in this header. If it gets confused, it returns 0.
+func (r *Reader) upcomingHeaderNewlines() (n int) {
+ // Try to determine the 'hint' size.
+ r.R.Peek(1) // force a buffer load if empty
+ s := r.R.Buffered()
+ if s == 0 {
+ return
+ }
+ peek, _ := r.R.Peek(s)
+ for len(peek) > 0 {
+ i := bytes.IndexByte(peek, '\n')
+ if i < 3 {
+ // Not present (-1) or found within the next few bytes,
+ // implying we're at the end ("\r\n\r\n" or "\n\n")
+ return
+ }
+ n++
+ peek = peek[i+1:]
+ }
+ return
+}
+
// CanonicalMIMEHeaderKey returns the canonical format of the
// MIME header key s. The canonicalization converts the first
// letter and any letter following a hyphen to upper case;
@@ -531,13 +574,10 @@ func canonicalMIMEHeaderKey(a []byte) string {
// and upper case after each dash.
// (Host, User-Agent, If-Modified-Since).
// MIME headers are ASCII only, so no Unicode issues.
- if a[i] == ' ' {
- a[i] = '-'
- upper = true
- continue
- }
c := a[i]
- if upper && 'a' <= c && c <= 'z' {
+ if c == ' ' {
+ c = '-'
+ } else if upper && 'a' <= c && c <= 'z' {
c -= toLower
} else if !upper && 'A' <= c && c <= 'Z' {
c += toLower
diff --git a/src/pkg/net/textproto/reader_test.go b/src/pkg/net/textproto/reader_test.go
index f27042d4e..cc12912b6 100644
--- a/src/pkg/net/textproto/reader_test.go
+++ b/src/pkg/net/textproto/reader_test.go
@@ -25,6 +25,10 @@ var canonicalHeaderKeyTests = []canonicalHeaderKeyTest{
{"user-agent", "User-Agent"},
{"USER-AGENT", "User-Agent"},
{"üser-agenT", "üser-Agent"}, // non-ASCII unchanged
+
+ // This caused a panic due to mishandling of a space:
+ {"C Ontent-Transfer-Encoding", "C-Ontent-Transfer-Encoding"},
+ {"foo bar", "Foo-Bar"},
}
func TestCanonicalMIMEHeaderKey(t *testing.T) {
diff --git a/src/pkg/net/textproto/textproto.go b/src/pkg/net/textproto/textproto.go
index eb6ced1c5..026eb026b 100644
--- a/src/pkg/net/textproto/textproto.go
+++ b/src/pkg/net/textproto/textproto.go
@@ -105,7 +105,7 @@ func Dial(network, addr string) (*Conn, error) {
// if _, _, err = c.ReadCodeLine(110); err != nil {
// return nil, err
// }
-// text, err := c.ReadDotAll()
+// text, err := c.ReadDotBytes()
// if err != nil {
// return nil, err
// }
diff --git a/src/pkg/net/timeout_test.go b/src/pkg/net/timeout_test.go
index 2e92147b8..35d427a69 100644
--- a/src/pkg/net/timeout_test.go
+++ b/src/pkg/net/timeout_test.go
@@ -325,9 +325,6 @@ func TestReadWriteDeadline(t *testing.T) {
t.Skipf("skipping test on %q", runtime.GOOS)
}
- if !canCancelIO {
- t.Skip("skipping test on this system")
- }
const (
readTimeout = 50 * time.Millisecond
writeTimeout = 250 * time.Millisecond
@@ -496,7 +493,10 @@ func testVariousDeadlines(t *testing.T, maxProcs int) {
clientc <- copyRes{n, err, d}
}()
- const tooLong = 2000 * time.Millisecond
+ tooLong := 2 * time.Second
+ if runtime.GOOS == "windows" {
+ tooLong = 5 * time.Second
+ }
select {
case res := <-clientc:
if isTimeout(res.err) {
@@ -549,7 +549,7 @@ func TestReadDeadlineDataAvailable(t *testing.T) {
}
defer c.Close()
if res := <-servec; res.err != nil || res.n != int64(len(msg)) {
- t.Fatalf("unexpected server Write: n=%d, err=%d; want n=%d, err=nil", res.n, res.err, len(msg))
+ t.Fatalf("unexpected server Write: n=%d, err=%v; want n=%d, err=nil", res.n, res.err, len(msg))
}
c.SetReadDeadline(time.Now().Add(-5 * time.Second)) // in the psat.
buf := make([]byte, len(msg)/2)
@@ -703,3 +703,40 @@ func TestProlongTimeout(t *testing.T) {
c.Write(buf[:])
}
}
+
+func TestDeadlineRace(t *testing.T) {
+ switch runtime.GOOS {
+ case "plan9":
+ t.Skipf("skipping test on %q", runtime.GOOS)
+ }
+
+ N := 1000
+ if testing.Short() {
+ N = 50
+ }
+ defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
+ ln := newLocalListener(t)
+ defer ln.Close()
+ c, err := Dial("tcp", ln.Addr().String())
+ if err != nil {
+ t.Fatalf("Dial: %v", err)
+ }
+ defer c.Close()
+ done := make(chan bool)
+ go func() {
+ t := time.NewTicker(2 * time.Microsecond).C
+ for i := 0; i < N; i++ {
+ if err := c.SetDeadline(time.Now().Add(2 * time.Microsecond)); err != nil {
+ break
+ }
+ <-t
+ }
+ done <- true
+ }()
+ var buf [1]byte
+ for i := 0; i < N; i++ {
+ c.Read(buf[:]) // ignore possible timeout errors
+ }
+ c.Close()
+ <-done
+}
diff --git a/src/pkg/net/udp_test.go b/src/pkg/net/udp_test.go
index 4278f6dd4..6f4d2152c 100644
--- a/src/pkg/net/udp_test.go
+++ b/src/pkg/net/udp_test.go
@@ -5,53 +5,31 @@
package net
import (
- "fmt"
"reflect"
"runtime"
+ "strings"
"testing"
)
-type resolveUDPAddrTest struct {
- net string
- litAddr string
- addr *UDPAddr
- err error
-}
-
-var resolveUDPAddrTests = []resolveUDPAddrTest{
- {"udp", "127.0.0.1:0", &UDPAddr{IP: IPv4(127, 0, 0, 1), Port: 0}, nil},
- {"udp4", "127.0.0.1:65535", &UDPAddr{IP: IPv4(127, 0, 0, 1), Port: 65535}, nil},
-
- {"udp", "[::1]:1", &UDPAddr{IP: ParseIP("::1"), Port: 1}, nil},
- {"udp6", "[::1]:65534", &UDPAddr{IP: ParseIP("::1"), Port: 65534}, nil},
-
- {"udp", "[::1%en0]:1", &UDPAddr{IP: ParseIP("::1"), Port: 1, Zone: "en0"}, nil},
- {"udp6", "[::1%911]:2", &UDPAddr{IP: ParseIP("::1"), Port: 2, Zone: "911"}, nil},
-
- {"", "127.0.0.1:0", &UDPAddr{IP: IPv4(127, 0, 0, 1), Port: 0}, nil}, // Go 1.0 behavior
- {"", "[::1]:0", &UDPAddr{IP: ParseIP("::1"), Port: 0}, nil}, // Go 1.0 behavior
-
- {"sip", "127.0.0.1:0", nil, UnknownNetworkError("sip")},
-}
-
-func init() {
- if ifi := loopbackInterface(); ifi != nil {
- index := fmt.Sprintf("%v", ifi.Index)
- resolveUDPAddrTests = append(resolveUDPAddrTests, []resolveUDPAddrTest{
- {"udp6", "[fe80::1%" + ifi.Name + "]:3", &UDPAddr{IP: ParseIP("fe80::1"), Port: 3, Zone: zoneToString(ifi.Index)}, nil},
- {"udp6", "[fe80::1%" + index + "]:4", &UDPAddr{IP: ParseIP("fe80::1"), Port: 4, Zone: index}, nil},
- }...)
- }
-}
-
func TestResolveUDPAddr(t *testing.T) {
- for _, tt := range resolveUDPAddrTests {
- addr, err := ResolveUDPAddr(tt.net, tt.litAddr)
+ for _, tt := range resolveTCPAddrTests {
+ net := strings.Replace(tt.net, "tcp", "udp", -1)
+ addr, err := ResolveUDPAddr(net, tt.litAddrOrName)
if err != tt.err {
- t.Fatalf("ResolveUDPAddr(%v, %v) failed: %v", tt.net, tt.litAddr, err)
+ t.Fatalf("ResolveUDPAddr(%q, %q) failed: %v", net, tt.litAddrOrName, err)
}
- if !reflect.DeepEqual(addr, tt.addr) {
- t.Fatalf("got %#v; expected %#v", addr, tt.addr)
+ if !reflect.DeepEqual(addr, (*UDPAddr)(tt.addr)) {
+ t.Fatalf("ResolveUDPAddr(%q, %q) = %#v, want %#v", net, tt.litAddrOrName, addr, tt.addr)
+ }
+ if err == nil {
+ str := addr.String()
+ addr1, err := ResolveUDPAddr(net, str)
+ if err != nil {
+ t.Fatalf("ResolveUDPAddr(%q, %q) [from %q]: %v", net, str, tt.litAddrOrName, err)
+ }
+ if !reflect.DeepEqual(addr1, addr) {
+ t.Fatalf("ResolveUDPAddr(%q, %q) [from %q] = %#v, want %#v", net, str, tt.litAddrOrName, addr1, addr)
+ }
}
}
}
@@ -224,7 +202,7 @@ func TestIPv6LinkLocalUnicastUDP(t *testing.T) {
{"udp6", "[" + laddr + "%" + ifi.Name + "]:0", false},
}
switch runtime.GOOS {
- case "darwin", "freebsd", "openbsd", "netbsd":
+ case "darwin", "dragonfly", "freebsd", "openbsd", "netbsd":
tests = append(tests, []test{
{"udp", "[localhost%" + ifi.Name + "]:0", true},
{"udp6", "[localhost%" + ifi.Name + "]:0", true},
diff --git a/src/pkg/net/udpsock.go b/src/pkg/net/udpsock.go
index 5ce7d6bea..0dd0dbd71 100644
--- a/src/pkg/net/udpsock.go
+++ b/src/pkg/net/udpsock.go
@@ -22,10 +22,18 @@ func (a *UDPAddr) String() string {
if a == nil {
return "<nil>"
}
+ ip := ipEmptyString(a.IP)
if a.Zone != "" {
- return JoinHostPort(a.IP.String()+"%"+a.Zone, itoa(a.Port))
+ return JoinHostPort(ip+"%"+a.Zone, itoa(a.Port))
}
- return JoinHostPort(a.IP.String(), itoa(a.Port))
+ return JoinHostPort(ip, itoa(a.Port))
+}
+
+func (a *UDPAddr) toAddr() Addr {
+ if a == nil {
+ return nil
+ }
+ return a
}
// ResolveUDPAddr parses addr as a UDP address of the form "host:port"
@@ -46,5 +54,5 @@ func ResolveUDPAddr(net, addr string) (*UDPAddr, error) {
if err != nil {
return nil, err
}
- return a.(*UDPAddr), nil
+ return a.toAddr().(*UDPAddr), nil
}
diff --git a/src/pkg/net/udpsock_plan9.go b/src/pkg/net/udpsock_plan9.go
index 12a348399..73621706d 100644
--- a/src/pkg/net/udpsock_plan9.go
+++ b/src/pkg/net/udpsock_plan9.go
@@ -73,6 +73,9 @@ func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (int, error) {
if !c.ok() || c.fd.data == nil {
return 0, syscall.EINVAL
}
+ if addr == nil {
+ return 0, &OpError{Op: "write", Net: c.fd.dir, Addr: nil, Err: errMissingAddress}
+ }
h := new(udpHeader)
h.raddr = addr.IP.To16()
h.laddr = c.fd.laddr.(*UDPAddr).IP.To16()
diff --git a/src/pkg/net/udpsock_posix.go b/src/pkg/net/udpsock_posix.go
index b90cb030d..142da8186 100644
--- a/src/pkg/net/udpsock_posix.go
+++ b/src/pkg/net/udpsock_posix.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.
-// +build darwin freebsd linux netbsd openbsd windows
+// +build darwin dragonfly freebsd linux netbsd openbsd windows
package net
@@ -39,14 +39,10 @@ func (a *UDPAddr) isWildcard() bool {
}
func (a *UDPAddr) sockaddr(family int) (syscall.Sockaddr, error) {
- return ipToSockaddr(family, a.IP, a.Port, a.Zone)
-}
-
-func (a *UDPAddr) toAddr() sockaddr {
- if a == nil { // nil *UDPAddr
- return nil // nil interface
+ if a == nil {
+ return nil, nil
}
- return a
+ return ipToSockaddr(family, a.IP, a.Port, a.Zone)
}
// UDPConn is the implementation of the Conn and PacketConn interfaces
@@ -121,6 +117,9 @@ func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (int, error) {
if c.fd.isConnected {
return 0, &OpError{"write", c.fd.net, addr, ErrWriteToConnected}
}
+ if addr == nil {
+ return 0, &OpError{Op: "write", Net: c.fd.net, Addr: nil, Err: errMissingAddress}
+ }
sa, err := addr.sockaddr(c.fd.family)
if err != nil {
return 0, &OpError{"write", c.fd.net, addr, err}
@@ -150,6 +149,9 @@ func (c *UDPConn) WriteMsgUDP(b, oob []byte, addr *UDPAddr) (n, oobn int, err er
if c.fd.isConnected {
return 0, 0, &OpError{"write", c.fd.net, addr, ErrWriteToConnected}
}
+ if addr == nil {
+ return 0, 0, &OpError{Op: "write", Net: c.fd.net, Addr: nil, Err: errMissingAddress}
+ }
sa, err := addr.sockaddr(c.fd.family)
if err != nil {
return 0, 0, &OpError{"write", c.fd.net, addr, err}
@@ -161,21 +163,21 @@ func (c *UDPConn) WriteMsgUDP(b, oob []byte, addr *UDPAddr) (n, oobn int, err er
// which must be "udp", "udp4", or "udp6". If laddr is not nil, it is
// used as the local address for the connection.
func DialUDP(net string, laddr, raddr *UDPAddr) (*UDPConn, error) {
- return dialUDP(net, laddr, raddr, noDeadline)
-}
-
-func dialUDP(net string, laddr, raddr *UDPAddr, deadline time.Time) (*UDPConn, error) {
switch net {
case "udp", "udp4", "udp6":
default:
- return nil, UnknownNetworkError(net)
+ return nil, &OpError{Op: "dial", Net: net, Addr: raddr, Err: UnknownNetworkError(net)}
}
if raddr == nil {
- return nil, &OpError{"dial", net, nil, errMissingAddress}
+ return nil, &OpError{Op: "dial", Net: net, Addr: nil, Err: errMissingAddress}
}
- fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), deadline, syscall.SOCK_DGRAM, 0, "dial", sockaddrToUDP)
+ return dialUDP(net, laddr, raddr, noDeadline)
+}
+
+func dialUDP(net string, laddr, raddr *UDPAddr, deadline time.Time) (*UDPConn, error) {
+ fd, err := internetSocket(net, laddr, raddr, deadline, syscall.SOCK_DGRAM, 0, "dial", sockaddrToUDP)
if err != nil {
- return nil, err
+ return nil, &OpError{Op: "dial", Net: net, Addr: raddr, Err: err}
}
return newUDPConn(fd), nil
}
@@ -191,14 +193,14 @@ func ListenUDP(net string, laddr *UDPAddr) (*UDPConn, error) {
switch net {
case "udp", "udp4", "udp6":
default:
- return nil, UnknownNetworkError(net)
+ return nil, &OpError{Op: "listen", Net: net, Addr: laddr, Err: UnknownNetworkError(net)}
}
if laddr == nil {
laddr = &UDPAddr{}
}
- fd, err := internetSocket(net, laddr.toAddr(), nil, noDeadline, syscall.SOCK_DGRAM, 0, "listen", sockaddrToUDP)
+ fd, err := internetSocket(net, laddr, nil, noDeadline, syscall.SOCK_DGRAM, 0, "listen", sockaddrToUDP)
if err != nil {
- return nil, err
+ return nil, &OpError{Op: "listen", Net: net, Addr: laddr, Err: err}
}
return newUDPConn(fd), nil
}
@@ -211,25 +213,25 @@ func ListenMulticastUDP(net string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, e
switch net {
case "udp", "udp4", "udp6":
default:
- return nil, UnknownNetworkError(net)
+ return nil, &OpError{Op: "listen", Net: net, Addr: gaddr, Err: UnknownNetworkError(net)}
}
if gaddr == nil || gaddr.IP == nil {
- return nil, &OpError{"listen", net, nil, errMissingAddress}
+ return nil, &OpError{Op: "listen", Net: net, Addr: nil, Err: errMissingAddress}
}
- fd, err := internetSocket(net, gaddr.toAddr(), nil, noDeadline, syscall.SOCK_DGRAM, 0, "listen", sockaddrToUDP)
+ fd, err := internetSocket(net, gaddr, nil, noDeadline, syscall.SOCK_DGRAM, 0, "listen", sockaddrToUDP)
if err != nil {
- return nil, err
+ return nil, &OpError{Op: "listen", Net: net, Addr: gaddr, Err: err}
}
c := newUDPConn(fd)
if ip4 := gaddr.IP.To4(); ip4 != nil {
if err := listenIPv4MulticastUDP(c, ifi, ip4); err != nil {
c.Close()
- return nil, &OpError{"listen", net, &IPAddr{IP: ip4}, err}
+ return nil, &OpError{Op: "listen", Net: net, Addr: &IPAddr{IP: ip4}, Err: err}
}
} else {
if err := listenIPv6MulticastUDP(c, ifi, gaddr.IP); err != nil {
c.Close()
- return nil, &OpError{"listen", net, &IPAddr{IP: gaddr.IP}, err}
+ return nil, &OpError{Op: "listen", Net: net, Addr: &IPAddr{IP: gaddr.IP}, Err: err}
}
}
return c, nil
diff --git a/src/pkg/net/unicast_posix_test.go b/src/pkg/net/unicast_posix_test.go
index b0588f4e5..5deb8f47c 100644
--- a/src/pkg/net/unicast_posix_test.go
+++ b/src/pkg/net/unicast_posix_test.go
@@ -349,12 +349,16 @@ func checkDualStackSecondListener(t *testing.T, net, laddr string, xerr, err err
if xerr == nil && err != nil || xerr != nil && err == nil {
t.Fatalf("Second Listen(%q, %q) returns %v, expected %v", net, laddr, err, xerr)
}
- l.(*TCPListener).Close()
+ if err == nil {
+ l.(*TCPListener).Close()
+ }
case "udp", "udp4", "udp6":
if xerr == nil && err != nil || xerr != nil && err == nil {
t.Fatalf("Second ListenPacket(%q, %q) returns %v, expected %v", net, laddr, err, xerr)
}
- l.(*UDPConn).Close()
+ if err == nil {
+ l.(*UDPConn).Close()
+ }
default:
t.Fatalf("Unexpected network: %q", net)
}
@@ -436,8 +440,8 @@ func TestWildWildcardListener(t *testing.T) {
}
defer func() {
- if recover() != nil {
- t.Fatalf("panicked")
+ if p := recover(); p != nil {
+ t.Fatalf("Listen, ListenPacket or protocol-specific Listen panicked: %v", p)
}
}()
diff --git a/src/pkg/net/unix_test.go b/src/pkg/net/unix_test.go
index 5e63e9d9d..91df3ff88 100644
--- a/src/pkg/net/unix_test.go
+++ b/src/pkg/net/unix_test.go
@@ -107,7 +107,7 @@ func TestReadUnixgramWithZeroBytesBuffer(t *testing.T) {
}
}
-func TestUnixAutobind(t *testing.T) {
+func TestUnixgramAutobind(t *testing.T) {
if runtime.GOOS != "linux" {
t.Skip("skipping: autobind is linux only")
}
@@ -139,8 +139,21 @@ func TestUnixAutobind(t *testing.T) {
}
}
+func TestUnixAutobindClose(t *testing.T) {
+ if runtime.GOOS != "linux" {
+ t.Skip("skipping: autobind is linux only")
+ }
+ laddr := &UnixAddr{Name: "", Net: "unix"}
+ ln, err := ListenUnix("unix", laddr)
+ if err != nil {
+ t.Fatalf("ListenUnix failed: %v", err)
+ }
+ ln.Close()
+}
+
func TestUnixConnLocalAndRemoteNames(t *testing.T) {
for _, laddr := range []string{"", testUnixAddr()} {
+ laddr := laddr
taddr := testUnixAddr()
ta, err := ResolveUnixAddr("unix", taddr)
if err != nil {
@@ -196,6 +209,7 @@ func TestUnixConnLocalAndRemoteNames(t *testing.T) {
func TestUnixgramConnLocalAndRemoteNames(t *testing.T) {
for _, laddr := range []string{"", testUnixAddr()} {
+ laddr := laddr
taddr := testUnixAddr()
ta, err := ResolveUnixAddr("unixgram", taddr)
if err != nil {
@@ -212,7 +226,6 @@ func TestUnixgramConnLocalAndRemoteNames(t *testing.T) {
var la *UnixAddr
if laddr != "" {
- var err error
if la, err = ResolveUnixAddr("unixgram", laddr); err != nil {
t.Fatalf("ResolveUnixAddr failed: %v", err)
}
diff --git a/src/pkg/net/unixsock.go b/src/pkg/net/unixsock.go
index 21a19eca2..85955845b 100644
--- a/src/pkg/net/unixsock.go
+++ b/src/pkg/net/unixsock.go
@@ -24,8 +24,8 @@ func (a *UnixAddr) String() string {
}
func (a *UnixAddr) toAddr() Addr {
- if a == nil { // nil *UnixAddr
- return nil // nil interface
+ if a == nil {
+ return nil
}
return a
}
diff --git a/src/pkg/net/unixsock_plan9.go b/src/pkg/net/unixsock_plan9.go
index 8a1281fb1..c60c1d83b 100644
--- a/src/pkg/net/unixsock_plan9.go
+++ b/src/pkg/net/unixsock_plan9.go
@@ -97,7 +97,7 @@ func ListenUnix(net string, laddr *UnixAddr) (*UnixListener, error) {
}
// AcceptUnix accepts the next incoming call and returns the new
-// connection and the remote address.
+// connection.
func (l *UnixListener) AcceptUnix() (*UnixConn, error) {
return nil, syscall.EPLAN9
}
diff --git a/src/pkg/net/unixsock_posix.go b/src/pkg/net/unixsock_posix.go
index 5db30df95..b82f3cee0 100644
--- a/src/pkg/net/unixsock_posix.go
+++ b/src/pkg/net/unixsock_posix.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.
-// +build darwin freebsd linux netbsd openbsd windows
+// +build darwin dragonfly freebsd linux netbsd openbsd windows
package net
@@ -13,14 +13,7 @@ import (
"time"
)
-func (a *UnixAddr) isUnnamed() bool {
- if a == nil || a.Name == "" {
- return true
- }
- return false
-}
-
-func unixSocket(net string, laddr, raddr *UnixAddr, mode string, deadline time.Time) (*netFD, error) {
+func unixSocket(net string, laddr, raddr sockaddr, mode string, deadline time.Time) (*netFD, error) {
var sotype int
switch net {
case "unix":
@@ -33,19 +26,18 @@ func unixSocket(net string, laddr, raddr *UnixAddr, mode string, deadline time.T
return nil, UnknownNetworkError(net)
}
- var la, ra syscall.Sockaddr
switch mode {
case "dial":
- if !laddr.isUnnamed() {
- la = &syscall.SockaddrUnix{Name: laddr.Name}
+ if laddr != nil && laddr.isWildcard() {
+ laddr = nil
}
- if raddr != nil {
- ra = &syscall.SockaddrUnix{Name: raddr.Name}
- } else if sotype != syscall.SOCK_DGRAM || laddr.isUnnamed() {
- return nil, &OpError{Op: mode, Net: net, Err: errMissingAddress}
+ if raddr != nil && raddr.isWildcard() {
+ raddr = nil
+ }
+ if raddr == nil && (sotype != syscall.SOCK_DGRAM || laddr == nil) {
+ return nil, errMissingAddress
}
case "listen":
- la = &syscall.SockaddrUnix{Name: laddr.Name}
default:
return nil, errors.New("unknown mode: " + mode)
}
@@ -57,19 +49,11 @@ func unixSocket(net string, laddr, raddr *UnixAddr, mode string, deadline time.T
f = sockaddrToUnixpacket
}
- fd, err := socket(net, syscall.AF_UNIX, sotype, 0, false, la, ra, deadline, f)
+ fd, err := socket(net, syscall.AF_UNIX, sotype, 0, false, laddr, raddr, deadline, f)
if err != nil {
- goto error
+ return nil, err
}
return fd, nil
-
-error:
- addr := raddr
- switch mode {
- case "listen":
- addr = laddr
- }
- return nil, &OpError{Op: mode, Net: net, Addr: addr, Err: err}
}
func sockaddrToUnix(sa syscall.Sockaddr) Addr {
@@ -106,6 +90,21 @@ func sotypeToNet(sotype int) string {
}
}
+func (a *UnixAddr) family() int {
+ return syscall.AF_UNIX
+}
+
+func (a *UnixAddr) isWildcard() bool {
+ return a == nil || a.Name == ""
+}
+
+func (a *UnixAddr) sockaddr(family int) (syscall.Sockaddr, error) {
+ if a == nil {
+ return nil, nil
+ }
+ return &syscall.SockaddrUnix{Name: a.Name}, nil
+}
+
// UnixConn is an implementation of the Conn interface for connections
// to Unix domain sockets.
type UnixConn struct {
@@ -172,6 +171,9 @@ func (c *UnixConn) WriteToUnix(b []byte, addr *UnixAddr) (n int, err error) {
if !c.ok() {
return 0, syscall.EINVAL
}
+ if addr == nil {
+ return 0, &OpError{Op: "write", Net: c.fd.net, Addr: nil, Err: errMissingAddress}
+ }
if addr.Net != sotypeToNet(c.fd.sotype) {
return 0, syscall.EAFNOSUPPORT
}
@@ -230,18 +232,18 @@ func (c *UnixConn) CloseWrite() error {
// which must be "unix", "unixgram" or "unixpacket". If laddr is not
// nil, it is used as the local address for the connection.
func DialUnix(net string, laddr, raddr *UnixAddr) (*UnixConn, error) {
- return dialUnix(net, laddr, raddr, noDeadline)
-}
-
-func dialUnix(net string, laddr, raddr *UnixAddr, deadline time.Time) (*UnixConn, error) {
switch net {
case "unix", "unixgram", "unixpacket":
default:
- return nil, UnknownNetworkError(net)
+ return nil, &OpError{Op: "dial", Net: net, Addr: raddr, Err: UnknownNetworkError(net)}
}
+ return dialUnix(net, laddr, raddr, noDeadline)
+}
+
+func dialUnix(net string, laddr, raddr *UnixAddr, deadline time.Time) (*UnixConn, error) {
fd, err := unixSocket(net, laddr, raddr, "dial", deadline)
if err != nil {
- return nil, err
+ return nil, &OpError{Op: "dial", Net: net, Addr: raddr, Err: err}
}
return newUnixConn(fd), nil
}
@@ -260,25 +262,20 @@ func ListenUnix(net string, laddr *UnixAddr) (*UnixListener, error) {
switch net {
case "unix", "unixpacket":
default:
- return nil, UnknownNetworkError(net)
+ return nil, &OpError{Op: "listen", Net: net, Addr: laddr, Err: UnknownNetworkError(net)}
}
if laddr == nil {
- return nil, &OpError{"listen", net, nil, errMissingAddress}
+ return nil, &OpError{Op: "listen", Net: net, Addr: nil, Err: errMissingAddress}
}
fd, err := unixSocket(net, laddr, nil, "listen", noDeadline)
if err != nil {
- return nil, err
- }
- err = syscall.Listen(fd.sysfd, listenerBacklog)
- if err != nil {
- fd.Close()
return nil, &OpError{Op: "listen", Net: net, Addr: laddr, Err: err}
}
- return &UnixListener{fd, laddr.Name}, nil
+ return &UnixListener{fd, fd.laddr.String()}, nil
}
// AcceptUnix accepts the next incoming call and returns the new
-// connection and the remote address.
+// connection.
func (l *UnixListener) AcceptUnix() (*UnixConn, error) {
if l == nil || l.fd == nil {
return nil, syscall.EINVAL
@@ -333,7 +330,7 @@ func (l *UnixListener) SetDeadline(t time.Time) (err error) {
if l == nil || l.fd == nil {
return syscall.EINVAL
}
- return setDeadline(l.fd, t)
+ return l.fd.setDeadline(t)
}
// File returns a copy of the underlying os.File, set to blocking
@@ -353,14 +350,14 @@ func ListenUnixgram(net string, laddr *UnixAddr) (*UnixConn, error) {
switch net {
case "unixgram":
default:
- return nil, UnknownNetworkError(net)
+ return nil, &OpError{Op: "listen", Net: net, Addr: laddr, Err: UnknownNetworkError(net)}
}
if laddr == nil {
- return nil, &OpError{"listen", net, nil, errMissingAddress}
+ return nil, &OpError{Op: "listen", Net: net, Addr: nil, Err: errMissingAddress}
}
fd, err := unixSocket(net, laddr, nil, "listen", noDeadline)
if err != nil {
- return nil, err
+ return nil, &OpError{Op: "listen", Net: net, Addr: laddr, Err: err}
}
return newUnixConn(fd), nil
}
diff --git a/src/pkg/net/url/url.go b/src/pkg/net/url/url.go
index 459dc473c..3b3787202 100644
--- a/src/pkg/net/url/url.go
+++ b/src/pkg/net/url/url.go
@@ -451,14 +451,17 @@ func (u *URL) String() string {
} else {
if u.Scheme != "" || u.Host != "" || u.User != nil {
buf.WriteString("//")
- if u := u.User; u != nil {
- buf.WriteString(u.String())
+ if ui := u.User; ui != nil {
+ buf.WriteString(ui.String())
buf.WriteByte('@')
}
if h := u.Host; h != "" {
buf.WriteString(h)
}
}
+ if u.Path != "" && u.Path[0] != '/' && u.Host != "" {
+ buf.WriteByte('/')
+ }
buf.WriteString(escape(u.Path, encodePath))
}
if u.RawQuery != "" {
@@ -555,8 +558,8 @@ func parseQuery(m Values, query string) (err error) {
return err
}
-// Encode encodes the values into ``URL encoded'' form.
-// e.g. "foo=bar&bar=baz"
+// Encode encodes the values into ``URL encoded'' form
+// ("bar=baz&foo=quux") sorted by key.
func (v Values) Encode() string {
if v == nil {
return ""
diff --git a/src/pkg/net/url/url_test.go b/src/pkg/net/url/url_test.go
index 9d81289ce..7578eb15b 100644
--- a/src/pkg/net/url/url_test.go
+++ b/src/pkg/net/url/url_test.go
@@ -260,6 +260,14 @@ var urltests = []URLTest{
},
"mailto:webmaster@golang.org",
},
+ // Relative path
+ {
+ "a/b/c",
+ &URL{
+ Path: "a/b/c",
+ },
+ "a/b/c",
+ },
}
// more useful string for debugging than fmt's struct printer
@@ -372,6 +380,22 @@ func DoTestString(t *testing.T, parse func(string) (*URL, error), name string, t
func TestURLString(t *testing.T) {
DoTestString(t, Parse, "Parse", urltests)
+
+ // no leading slash on path should prepend
+ // slash on String() call
+ noslash := URLTest{
+ "http://www.google.com/search",
+ &URL{
+ Scheme: "http",
+ Host: "www.google.com",
+ Path: "search",
+ },
+ "",
+ }
+ s := noslash.out.String()
+ if s != noslash.in {
+ t.Errorf("Expected %s; go %s", noslash.in, s)
+ }
}
type EscapeTest struct {
diff --git a/src/pkg/os/dir_unix.go b/src/pkg/os/dir_unix.go
index f41f939a9..9fa7ad664 100644
--- a/src/pkg/os/dir_unix.go
+++ b/src/pkg/os/dir_unix.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.
-// +build darwin freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd
package os
diff --git a/src/pkg/os/doc.go b/src/pkg/os/doc.go
index 2cc17530c..a954e313d 100644
--- a/src/pkg/os/doc.go
+++ b/src/pkg/os/doc.go
@@ -58,7 +58,7 @@ func (p *ProcessState) SystemTime() time.Duration {
return p.systemTime()
}
-// Exited returns whether the program has exited.
+// Exited reports whether the program has exited.
func (p *ProcessState) Exited() bool {
return p.exited()
}
@@ -106,6 +106,9 @@ func Hostname() (name string, err error) {
// directory, Readdir returns the FileInfo read until that point
// and a non-nil error.
func (f *File) Readdir(n int) (fi []FileInfo, err error) {
+ if f == nil {
+ return nil, ErrInvalid
+ }
return f.readdir(n)
}
@@ -122,5 +125,8 @@ func (f *File) Readdir(n int) (fi []FileInfo, err error) {
// directory, Readdirnames returns the names read until that point and
// a non-nil error.
func (f *File) Readdirnames(n int) (names []string, err error) {
+ if f == nil {
+ return nil, ErrInvalid
+ }
return f.readdirnames(n)
}
diff --git a/src/pkg/os/env_unix_test.go b/src/pkg/os/env_unix_test.go
index 7eb4dc0ff..e16d71a64 100644
--- a/src/pkg/os/env_unix_test.go
+++ b/src/pkg/os/env_unix_test.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.
-// +build darwin freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd
package os_test
diff --git a/src/pkg/os/error.go b/src/pkg/os/error.go
index a7977ff19..8810e6930 100644
--- a/src/pkg/os/error.go
+++ b/src/pkg/os/error.go
@@ -43,20 +43,23 @@ func NewSyscallError(syscall string, err error) error {
return &SyscallError{syscall, err}
}
-// IsExist returns whether the error is known to report that a file or directory
-// already exists. It is satisfied by ErrExist as well as some syscall errors.
+// IsExist returns a boolean indicating whether the error is known to report
+// that a file or directory already exists. It is satisfied by ErrExist as
+// well as some syscall errors.
func IsExist(err error) bool {
return isExist(err)
}
-// IsNotExist returns whether the error is known to report that a file or directory
-// does not exist. It is satisfied by ErrNotExist as well as some syscall errors.
+// IsNotExist returns a boolean indicating whether the error is known to
+// report that a file or directory does not exist. It is satisfied by
+// ErrNotExist as well as some syscall errors.
func IsNotExist(err error) bool {
return isNotExist(err)
}
-// IsPermission returns whether the error is known to report that permission is denied.
-// It is satisfied by ErrPermission as well as some syscall errors.
+// IsPermission returns a boolean indicating whether the error is known to
+// report that permission is denied. It is satisfied by ErrPermission as well
+// as some syscall errors.
func IsPermission(err error) bool {
return isPermission(err)
}
diff --git a/src/pkg/os/error_posix.go b/src/pkg/os/error_unix.go
index 81b626aec..6250349e5 100644
--- a/src/pkg/os/error_posix.go
+++ b/src/pkg/os/error_unix.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.
-// +build darwin freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd
package os
diff --git a/src/pkg/os/exec/exec.go b/src/pkg/os/exec/exec.go
index a3bbcf300..491cc242b 100644
--- a/src/pkg/os/exec/exec.go
+++ b/src/pkg/os/exec/exec.go
@@ -13,6 +13,7 @@ import (
"io"
"os"
"strconv"
+ "sync"
"syscall"
)
@@ -357,6 +358,10 @@ func (c *Cmd) CombinedOutput() ([]byte, error) {
// StdinPipe returns a pipe that will be connected to the command's
// standard input when the command starts.
+// The pipe will be closed automatically after Wait sees the command exit.
+// A caller need only call Close to force the pipe to close sooner.
+// For example, if the command being run will not exit until standard input
+// is closed, the caller must close the pipe.
func (c *Cmd) StdinPipe() (io.WriteCloser, error) {
if c.Stdin != nil {
return nil, errors.New("exec: Stdin already set")
@@ -370,13 +375,33 @@ func (c *Cmd) StdinPipe() (io.WriteCloser, error) {
}
c.Stdin = pr
c.closeAfterStart = append(c.closeAfterStart, pr)
- c.closeAfterWait = append(c.closeAfterWait, pw)
- return pw, nil
+ wc := &closeOnce{File: pw}
+ c.closeAfterWait = append(c.closeAfterWait, wc)
+ return wc, nil
+}
+
+type closeOnce struct {
+ *os.File
+
+ close sync.Once
+ closeErr error
+}
+
+func (c *closeOnce) Close() error {
+ c.close.Do(func() {
+ c.closeErr = c.File.Close()
+ })
+ return c.closeErr
}
// StdoutPipe returns a pipe that will be connected to the command's
// standard output when the command starts.
-// The pipe will be closed automatically after Wait sees the command exit.
+//
+// Wait will close the pipe after seeing the command exit, so most callers
+// need not close the pipe themselves; however, an implication is that
+// it is incorrect to call Wait before all reads from the pipe have completed.
+// For the same reason, it is incorrect to call Run when using StdoutPipe.
+// See the example for idiomatic usage.
func (c *Cmd) StdoutPipe() (io.ReadCloser, error) {
if c.Stdout != nil {
return nil, errors.New("exec: Stdout already set")
@@ -396,7 +421,12 @@ func (c *Cmd) StdoutPipe() (io.ReadCloser, error) {
// StderrPipe returns a pipe that will be connected to the command's
// standard error when the command starts.
-// The pipe will be closed automatically after Wait sees the command exit.
+//
+// Wait will close the pipe after seeing the command exit, so most callers
+// need not close the pipe themselves; however, an implication is that
+// it is incorrect to call Wait before all reads from the pipe have completed.
+// For the same reason, it is incorrect to use Run when using StderrPipe.
+// See the StdoutPipe example for idiomatic usage.
func (c *Cmd) StderrPipe() (io.ReadCloser, error) {
if c.Stderr != nil {
return nil, errors.New("exec: Stderr already set")
diff --git a/src/pkg/os/exec/exec_test.go b/src/pkg/os/exec/exec_test.go
index 6f5860e95..c380d6506 100644
--- a/src/pkg/os/exec/exec_test.go
+++ b/src/pkg/os/exec/exec_test.go
@@ -152,6 +152,34 @@ func TestPipes(t *testing.T) {
check("Wait", err)
}
+const stdinCloseTestString = "Some test string."
+
+// Issue 6270.
+func TestStdinClose(t *testing.T) {
+ check := func(what string, err error) {
+ if err != nil {
+ t.Fatalf("%s: %v", what, err)
+ }
+ }
+ cmd := helperCommand("stdinClose")
+ stdin, err := cmd.StdinPipe()
+ check("StdinPipe", err)
+ // Check that we can access methods of the underlying os.File.`
+ if _, ok := stdin.(interface {
+ Fd() uintptr
+ }); !ok {
+ t.Error("can't access methods of underlying *os.File")
+ }
+ check("Start", cmd.Start())
+ go func() {
+ _, err := io.Copy(stdin, strings.NewReader(stdinCloseTestString))
+ check("Copy", err)
+ // Before the fix, this next line would race with cmd.Wait.
+ check("Close", stdin.Close())
+ }()
+ check("Wait", cmd.Wait())
+}
+
// Issue 5071
func TestPipeLookPathLeak(t *testing.T) {
fd0 := numOpenFDS(t)
@@ -195,8 +223,29 @@ func basefds() uintptr {
return n
}
+func closeUnexpectedFds(t *testing.T, m string) {
+ for fd := basefds(); fd <= 101; fd++ {
+ err := os.NewFile(fd, "").Close()
+ if err == nil {
+ t.Logf("%s: Something already leaked - closed fd %d", m, fd)
+ }
+ }
+}
+
func TestExtraFilesFDShuffle(t *testing.T) {
- t.Skip("TODO: TestExtraFilesFDShuffle is too non-portable; skipping")
+ t.Skip("flaky test; see http://golang.org/issue/5780")
+ switch runtime.GOOS {
+ case "darwin":
+ // TODO(cnicolaou): http://golang.org/issue/2603
+ // leads to leaked file descriptors in this test when it's
+ // run from a builder.
+ closeUnexpectedFds(t, "TestExtraFilesFDShuffle")
+ case "netbsd":
+ // http://golang.org/issue/3955
+ closeUnexpectedFds(t, "TestExtraFilesFDShuffle")
+ case "windows":
+ t.Skip("no operating system support; skipping")
+ }
// syscall.StartProcess maps all the FDs passed to it in
// ProcAttr.Files (the concatenation of stdin,stdout,stderr and
@@ -296,12 +345,7 @@ func TestExtraFiles(t *testing.T) {
// our environment.
if !testedAlreadyLeaked {
testedAlreadyLeaked = true
- for fd := basefds(); fd <= 101; fd++ {
- err := os.NewFile(fd, "").Close()
- if err == nil {
- t.Logf("Something already leaked - closed fd %d", fd)
- }
- }
+ closeUnexpectedFds(t, "TestExtraFiles")
}
// Force network usage, to verify the epoll (or whatever) fd
@@ -430,7 +474,7 @@ func TestHelperProcess(*testing.T) {
// Determine which command to use to display open files.
ofcmd := "lsof"
switch runtime.GOOS {
- case "freebsd", "netbsd", "openbsd":
+ case "dragonfly", "freebsd", "netbsd", "openbsd":
ofcmd = "fstat"
}
@@ -491,6 +535,17 @@ func TestHelperProcess(*testing.T) {
os.Exit(1)
}
}
+ case "stdinClose":
+ b, err := ioutil.ReadAll(os.Stdin)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error: %v\n", err)
+ os.Exit(1)
+ }
+ if s := string(b); s != stdinCloseTestString {
+ fmt.Fprintf(os.Stderr, "Error: Read %q, want %q", s, stdinCloseTestString)
+ os.Exit(1)
+ }
+ os.Exit(0)
case "read3": // read fd 3
fd3 := os.NewFile(3, "fd3")
bs, err := ioutil.ReadAll(fd3)
@@ -499,6 +554,9 @@ func TestHelperProcess(*testing.T) {
os.Exit(1)
}
switch runtime.GOOS {
+ case "dragonfly":
+ // TODO(jsing): Determine why DragonFly is leaking
+ // file descriptors...
case "darwin":
// TODO(bradfitz): broken? Sometimes.
// http://golang.org/issue/2603
@@ -540,13 +598,11 @@ func TestHelperProcess(*testing.T) {
n, _ := strconv.Atoi(args[0])
os.Exit(n)
case "describefiles":
- for fd := uintptr(3); fd < 25; fd++ {
- f := os.NewFile(fd, fmt.Sprintf("fd-%d", fd))
- ln, err := net.FileListener(f)
- if err == nil {
- fmt.Printf("fd%d: listener %s\n", fd, ln.Addr())
- ln.Close()
- }
+ f := os.NewFile(3, fmt.Sprintf("fd3"))
+ ln, err := net.FileListener(f)
+ if err == nil {
+ fmt.Printf("fd3: listener %s\n", ln.Addr())
+ ln.Close()
}
os.Exit(0)
case "extraFilesAndPipes":
diff --git a/src/pkg/os/exec/lp_plan9.go b/src/pkg/os/exec/lp_plan9.go
index 6846a35c8..5aa8a54ed 100644
--- a/src/pkg/os/exec/lp_plan9.go
+++ b/src/pkg/os/exec/lp_plan9.go
@@ -28,6 +28,7 @@ func findExecutable(file string) error {
// in the directories named by the path environment variable.
// If file begins with "/", "#", "./", or "../", it is tried
// directly and the path is not consulted.
+// The result may be an absolute path or a path relative to the current directory.
func LookPath(file string) (string, error) {
// skip the path lookup for these prefixes
skip := []string{"/", "#", "./", "../"}
diff --git a/src/pkg/os/exec/lp_unix.go b/src/pkg/os/exec/lp_unix.go
index 1d1ec07da..7ff2d201b 100644
--- a/src/pkg/os/exec/lp_unix.go
+++ b/src/pkg/os/exec/lp_unix.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.
-// +build darwin freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd
package exec
@@ -29,6 +29,7 @@ func findExecutable(file string) error {
// LookPath searches for an executable binary named file
// in the directories named by the PATH environment variable.
// If file contains a slash, it is tried directly and the PATH is not consulted.
+// The result may be an absolute path or a path relative to the current directory.
func LookPath(file string) (string, error) {
// NOTE(rsc): I wish we could use the Plan 9 behavior here
// (only bypass the path if file begins with / or ./ or ../)
diff --git a/src/pkg/os/exec/lp_unix_test.go b/src/pkg/os/exec/lp_unix_test.go
index 625d78486..f1ab6deff 100644
--- a/src/pkg/os/exec/lp_unix_test.go
+++ b/src/pkg/os/exec/lp_unix_test.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.
-// +build darwin freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd
package exec
diff --git a/src/pkg/os/exec/lp_windows.go b/src/pkg/os/exec/lp_windows.go
index 7c7289bce..c3efd67e9 100644
--- a/src/pkg/os/exec/lp_windows.go
+++ b/src/pkg/os/exec/lp_windows.go
@@ -24,14 +24,21 @@ func chkStat(file string) error {
return nil
}
+func hasExt(file string) bool {
+ i := strings.LastIndex(file, ".")
+ if i < 0 {
+ return false
+ }
+ return strings.LastIndexAny(file, `:\/`) < i
+}
+
func findExecutable(file string, exts []string) (string, error) {
if len(exts) == 0 {
return file, chkStat(file)
}
- f := strings.ToLower(file)
- for _, e := range exts {
- if strings.HasSuffix(f, e) {
- return file, chkStat(file)
+ if hasExt(file) {
+ if chkStat(file) == nil {
+ return file, nil
}
}
for _, e := range exts {
@@ -47,6 +54,7 @@ func findExecutable(file string, exts []string) (string, error) {
// If file contains a slash, it is tried directly and the PATH is not consulted.
// LookPath also uses PATHEXT environment variable to match
// a suitable candidate.
+// The result may be an absolute path or a path relative to the current directory.
func LookPath(file string) (f string, err error) {
x := os.Getenv(`PATHEXT`)
if x == `` {
diff --git a/src/pkg/os/exec/lp_windows_test.go b/src/pkg/os/exec/lp_windows_test.go
new file mode 100644
index 000000000..385dd331b
--- /dev/null
+++ b/src/pkg/os/exec/lp_windows_test.go
@@ -0,0 +1,391 @@
+// Copyright 2013 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 exec
+
+import (
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strconv"
+ "strings"
+ "testing"
+ "text/template"
+)
+
+type lookPathTest struct {
+ PATH string
+ PATHEXT string
+ files []string
+ searchFor string
+ fails bool // test is expected to fail
+}
+
+// PrefixPATH returns p.PATH with every element prefixed by prefix.
+func (t lookPathTest) PrefixPATH(prefix string) string {
+ a := strings.SplitN(t.PATH, ";", -1)
+ for i := range a {
+ a[i] = filepath.Join(prefix, a[i])
+ }
+ return strings.Join(a, ";")
+}
+
+var lookPathTests = []lookPathTest{
+ {
+ PATHEXT: `.COM;.EXE;.BAT`,
+ PATH: `p1;p2`,
+ files: []string{`p1\a.exe`, `p2\a.exe`, `p2\a`},
+ searchFor: `a`,
+ },
+ {
+ PATHEXT: `.COM;.EXE;.BAT`,
+ PATH: `p1.dir;p2.dir`,
+ files: []string{`p1.dir\a`, `p2.dir\a.exe`},
+ searchFor: `a`,
+ },
+ {
+ PATHEXT: `.COM;.EXE;.BAT`,
+ PATH: `p1;p2`,
+ files: []string{`p1\a.exe`, `p2\a.exe`},
+ searchFor: `a.exe`,
+ },
+ {
+ PATHEXT: `.COM;.EXE;.BAT`,
+ PATH: `p1;p2`,
+ files: []string{`p1\a.exe`, `p2\b.exe`},
+ searchFor: `b`,
+ },
+ {
+ PATHEXT: `.COM;.EXE;.BAT`,
+ PATH: `p1;p2`,
+ files: []string{`p1\b`, `p2\a`},
+ searchFor: `a`,
+ fails: true, // TODO(brainman): do not know why this fails
+ },
+ // If the command name specifies a path, the shell searches
+ // the specified path for an executable file matching
+ // the command name. If a match is found, the external
+ // command (the executable file) executes.
+ {
+ PATHEXT: `.COM;.EXE;.BAT`,
+ PATH: `p1;p2`,
+ files: []string{`p1\a.exe`, `p2\a.exe`},
+ searchFor: `p2\a`,
+ },
+ // If the command name specifies a path, the shell searches
+ // the specified path for an executable file matching the command
+ // name. ... If no match is found, the shell reports an error
+ // and command processing completes.
+ {
+ PATHEXT: `.COM;.EXE;.BAT`,
+ PATH: `p1;p2`,
+ files: []string{`p1\b.exe`, `p2\a.exe`},
+ searchFor: `p2\b`,
+ fails: true,
+ },
+ // If the command name does not specify a path, the shell
+ // searches the current directory for an executable file
+ // matching the command name. If a match is found, the external
+ // command (the executable file) executes.
+ {
+ PATHEXT: `.COM;.EXE;.BAT`,
+ PATH: `p1;p2`,
+ files: []string{`a`, `p1\a.exe`, `p2\a.exe`},
+ searchFor: `a`,
+ },
+ // The shell now searches each directory specified by the
+ // PATH environment variable, in the order listed, for an
+ // executable file matching the command name. If a match
+ // is found, the external command (the executable file) executes.
+ {
+ PATHEXT: `.COM;.EXE;.BAT`,
+ PATH: `p1;p2`,
+ files: []string{`p1\a.exe`, `p2\a.exe`},
+ searchFor: `a`,
+ },
+ // The shell now searches each directory specified by the
+ // PATH environment variable, in the order listed, for an
+ // executable file matching the command name. If no match
+ // is found, the shell reports an error and command processing
+ // completes.
+ {
+ PATHEXT: `.COM;.EXE;.BAT`,
+ PATH: `p1;p2`,
+ files: []string{`p1\a.exe`, `p2\a.exe`},
+ searchFor: `b`,
+ fails: true,
+ },
+ // If the command name includes a file extension, the shell
+ // searches each directory for the exact file name specified
+ // by the command name.
+ {
+ PATHEXT: `.COM;.EXE;.BAT`,
+ PATH: `p1;p2`,
+ files: []string{`p1\a.exe`, `p2\a.exe`},
+ searchFor: `a.exe`,
+ },
+ {
+ PATHEXT: `.COM;.EXE;.BAT`,
+ PATH: `p1;p2`,
+ files: []string{`p1\a.exe`, `p2\a.exe`},
+ searchFor: `a.com`,
+ fails: true, // includes extension and not exact file name match
+ },
+ {
+ PATHEXT: `.COM;.EXE;.BAT`,
+ PATH: `p1`,
+ files: []string{`p1\a.exe.exe`},
+ searchFor: `a.exe`,
+ },
+ {
+ PATHEXT: `.COM;.BAT`,
+ PATH: `p1;p2`,
+ files: []string{`p1\a.exe`, `p2\a.exe`},
+ searchFor: `a.exe`,
+ },
+ // If the command name does not include a file extension, the shell
+ // adds the extensions listed in the PATHEXT environment variable,
+ // one by one, and searches the directory for that file name. Note
+ // that the shell tries all possible file extensions in a specific
+ // directory before moving on to search the next directory
+ // (if there is one).
+ {
+ PATHEXT: `.COM;.EXE`,
+ PATH: `p1;p2`,
+ files: []string{`p1\a.bat`, `p2\a.exe`},
+ searchFor: `a`,
+ },
+ {
+ PATHEXT: `.COM;.EXE;.BAT`,
+ PATH: `p1;p2`,
+ files: []string{`p1\a.bat`, `p2\a.exe`},
+ searchFor: `a`,
+ },
+ {
+ PATHEXT: `.COM;.EXE;.BAT`,
+ PATH: `p1;p2`,
+ files: []string{`p1\a.bat`, `p1\a.exe`, `p2\a.bat`, `p2\a.exe`},
+ searchFor: `a`,
+ },
+ {
+ PATHEXT: `.COM`,
+ PATH: `p1;p2`,
+ files: []string{`p1\a.bat`, `p2\a.exe`},
+ searchFor: `a`,
+ fails: true, // tried all extensions in PATHEXT, but none matches
+ },
+}
+
+func updateEnv(env []string, name, value string) []string {
+ for i, e := range env {
+ if strings.HasPrefix(strings.ToUpper(e), name+"=") {
+ env[i] = name + "=" + value
+ return env
+ }
+ }
+ return append(env, name+"="+value)
+}
+
+func installExe(t *testing.T, dest, src string) {
+ fsrc, err := os.Open(src)
+ if err != nil {
+ t.Fatal("os.Open failed: ", err)
+ }
+ defer fsrc.Close()
+ fdest, err := os.Create(dest)
+ if err != nil {
+ t.Fatal("os.Create failed: ", err)
+ }
+ defer fdest.Close()
+ _, err = io.Copy(fdest, fsrc)
+ if err != nil {
+ t.Fatal("io.Copy failed: ", err)
+ }
+}
+
+func installBat(t *testing.T, dest string) {
+ f, err := os.Create(dest)
+ if err != nil {
+ t.Fatalf("failed to create batch file: %v", err)
+ }
+ defer f.Close()
+ fmt.Fprintf(f, "@echo %s\n", dest)
+}
+
+func installProg(t *testing.T, dest, srcExe string) {
+ err := os.MkdirAll(filepath.Dir(dest), 0700)
+ if err != nil {
+ t.Fatal("os.MkdirAll failed: ", err)
+ }
+ if strings.ToLower(filepath.Ext(dest)) == ".bat" {
+ installBat(t, dest)
+ return
+ }
+ installExe(t, dest, srcExe)
+}
+
+func runProg(t *testing.T, test lookPathTest, env []string, dir string, args ...string) (string, error) {
+ cmd := Command(args[0], args[1:]...)
+ cmd.Env = env
+ cmd.Dir = dir
+ args[0] = filepath.Base(args[0])
+ cmdText := fmt.Sprintf("%q command", strings.Join(args, " "))
+ out, err := cmd.CombinedOutput()
+ if (err != nil) != test.fails {
+ if test.fails {
+ t.Fatalf("test=%+v: %s succeeded, but expected to fail", test, cmdText)
+ }
+ t.Fatalf("test=%+v: %s failed, but expected to succeed: %v - %v", test, cmdText, err, string(out))
+ }
+ if err != nil {
+ return "", fmt.Errorf("test=%+v: %s failed: %v - %v", test, cmdText, err, string(out))
+ }
+ // normalise program output
+ p := string(out)
+ // trim terminating \r and \n that batch file outputs
+ for len(p) > 0 && (p[len(p)-1] == '\n' || p[len(p)-1] == '\r') {
+ p = p[:len(p)-1]
+ }
+ if !filepath.IsAbs(p) {
+ return p, nil
+ }
+ if p[:len(dir)] != dir {
+ t.Fatalf("test=%+v: %s output is wrong: %q must have %q prefix", test, cmdText, p, dir)
+ }
+ return p[len(dir)+1:], nil
+}
+
+func testLookPath(t *testing.T, test lookPathTest, tmpdir, lookpathExe, printpathExe string) {
+ // Create files listed in test.files in tmp directory.
+ for i := range test.files {
+ installProg(t, filepath.Join(tmpdir, test.files[i]), printpathExe)
+ }
+ // Create environment with test.PATH and test.PATHEXT set.
+ env := os.Environ()
+ env = updateEnv(env, "PATH", test.PrefixPATH(tmpdir))
+ env = updateEnv(env, "PATHEXT", test.PATHEXT)
+ // Run "cmd.exe /c test.searchFor" with new environment and
+ // work directory set. All candidates are copies of printpath.exe.
+ // These will output their program paths when run.
+ should, errCmd := runProg(t, test, env, tmpdir, "cmd", "/c", test.searchFor)
+ // Run the lookpath program with new environment and work directory set.
+ have, errLP := runProg(t, test, env, tmpdir, lookpathExe, test.searchFor)
+ // Compare results.
+ if errCmd == nil && errLP == nil {
+ // both succeeded
+ if should != have {
+ // t.Fatalf("test=%+v failed: expected to find %v, but found %v", test, should, have)
+ t.Fatalf("test=%+v failed: expected to find %q, but found %q", test, should, have)
+ }
+ return
+ }
+ if errCmd != nil && errLP != nil {
+ // both failed -> continue
+ return
+ }
+ if errCmd != nil {
+ t.Fatal(errCmd)
+ }
+ if errLP != nil {
+ t.Fatal(errLP)
+ }
+}
+
+func buildExe(t *testing.T, templ, dir, name string) string {
+ srcname := name + ".go"
+ f, err := os.Create(filepath.Join(dir, srcname))
+ if err != nil {
+ t.Fatalf("failed to create source: %v", err)
+ }
+ err = template.Must(template.New("template").Parse(templ)).Execute(f, nil)
+ f.Close()
+ if err != nil {
+ t.Fatalf("failed to execute template: %v", err)
+ }
+ outname := name + ".exe"
+ cmd := Command("go", "build", "-o", outname, srcname)
+ cmd.Dir = dir
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("failed to build executable: %v - %v", err, string(out))
+ }
+ return filepath.Join(dir, outname)
+}
+
+func TestLookPath(t *testing.T) {
+ tmp, err := ioutil.TempDir("", "TestLookPath")
+ if err != nil {
+ t.Fatal("TempDir failed: ", err)
+ }
+ defer os.RemoveAll(tmp)
+
+ // Create a Go program that uses LookPath to find executable passed as command line parameter.
+ lookpathExe := buildExe(t, lookpathSrc, tmp, "lookpath")
+
+ // Create a Go program that prints its own path.
+ printpathExe := buildExe(t, printpathSrc, tmp, "printpath")
+
+ // Run all tests.
+ for i, test := range lookPathTests {
+ dir := filepath.Join(tmp, "d"+strconv.Itoa(i))
+ err := os.Mkdir(dir, 0700)
+ if err != nil {
+ t.Fatal("Mkdir failed: ", err)
+ }
+ testLookPath(t, test, dir, lookpathExe, printpathExe)
+ }
+}
+
+const lookpathSrc = `
+package main
+
+import (
+ "fmt"
+ "os"
+ "os/exec"
+)
+
+func main() {
+ p, err := exec.LookPath(os.Args[1])
+ if err != nil {
+ fmt.Printf("LookPath failed: %v\n", err)
+ os.Exit(1)
+ }
+ fmt.Print(p)
+}
+`
+
+const printpathSrc = `
+package main
+
+import (
+ "fmt"
+ "os"
+ "syscall"
+ "unicode/utf16"
+ "unsafe"
+)
+
+func getMyName() (string, error) {
+ var sysproc = syscall.MustLoadDLL("kernel32.dll").MustFindProc("GetModuleFileNameW")
+ b := make([]uint16, syscall.MAX_PATH)
+ r, _, err := sysproc.Call(0, uintptr(unsafe.Pointer(&b[0])), uintptr(len(b)))
+ n := uint32(r)
+ if n == 0 {
+ return "", err
+ }
+ return string(utf16.Decode(b[0:n])), nil
+}
+
+func main() {
+ path, err := getMyName()
+ if err != nil {
+ fmt.Printf("getMyName failed: %v\n", err)
+ os.Exit(1)
+ }
+ fmt.Print(path)
+}
+`
diff --git a/src/pkg/os/exec_posix.go b/src/pkg/os/exec_posix.go
index f7b10f3c6..fb123aefb 100644
--- a/src/pkg/os/exec_posix.go
+++ b/src/pkg/os/exec_posix.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.
-// +build darwin freebsd linux netbsd openbsd windows
+// +build darwin dragonfly freebsd linux netbsd openbsd windows
package os
diff --git a/src/pkg/os/exec_unix.go b/src/pkg/os/exec_unix.go
index fa3ba8a19..5572e628e 100644
--- a/src/pkg/os/exec_unix.go
+++ b/src/pkg/os/exec_unix.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.
-// +build darwin freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd
package os
diff --git a/src/pkg/os/exec_windows.go b/src/pkg/os/exec_windows.go
index 4aa2ade63..c4f3d4f85 100644
--- a/src/pkg/os/exec_windows.go
+++ b/src/pkg/os/exec_windows.go
@@ -42,13 +42,22 @@ func (p *Process) wait() (ps *ProcessState, err error) {
return &ProcessState{p.Pid, syscall.WaitStatus{ExitCode: ec}, &u}, nil
}
+func terminateProcess(pid, exitcode int) error {
+ h, e := syscall.OpenProcess(syscall.PROCESS_TERMINATE, false, uint32(pid))
+ if e != nil {
+ return NewSyscallError("OpenProcess", e)
+ }
+ defer syscall.CloseHandle(h)
+ e = syscall.TerminateProcess(h, uint32(exitcode))
+ return NewSyscallError("TerminateProcess", e)
+}
+
func (p *Process) signal(sig Signal) error {
if p.done() {
return errors.New("os: process already finished")
}
if sig == Kill {
- e := syscall.TerminateProcess(syscall.Handle(p.handle), 1)
- return NewSyscallError("TerminateProcess", e)
+ return terminateProcess(p.Pid, 1)
}
// TODO(rsc): Handle Interrupt too?
return syscall.Errno(syscall.EWINDOWS)
diff --git a/src/pkg/os/export_test.go b/src/pkg/os/export_test.go
index 9c6ef4297..9fa7936ae 100644
--- a/src/pkg/os/export_test.go
+++ b/src/pkg/os/export_test.go
@@ -7,3 +7,4 @@ package os
// Export for testing.
var Atime = atime
+var LstatP = &lstat
diff --git a/src/pkg/os/file.go b/src/pkg/os/file.go
index 32cac6d89..2dd1fcf28 100644
--- a/src/pkg/os/file.go
+++ b/src/pkg/os/file.go
@@ -174,6 +174,9 @@ func (f *File) WriteAt(b []byte, off int64) (n int, err error) {
// relative to the current offset, and 2 means relative to the end.
// It returns the new offset and an error, if any.
func (f *File) Seek(offset int64, whence int) (ret int64, err error) {
+ if f == nil {
+ return 0, ErrInvalid
+ }
r, e := f.seek(offset, whence)
if e == nil && f.dirinfo != nil && r != 0 {
e = syscall.EISDIR
@@ -216,6 +219,9 @@ func Chdir(dir string) error {
// which must be a directory.
// If there is an error, it will be of type *PathError.
func (f *File) Chdir() error {
+ if f == nil {
+ return ErrInvalid
+ }
if e := syscall.Fchdir(f.fd); e != nil {
return &PathError{"chdir", f.name, e}
}
@@ -238,3 +244,6 @@ func Open(name string) (file *File, err error) {
func Create(name string) (file *File, err error) {
return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
}
+
+// lstat is overridden in tests.
+var lstat = Lstat
diff --git a/src/pkg/os/file_plan9.go b/src/pkg/os/file_plan9.go
index d6d39a899..708163ee1 100644
--- a/src/pkg/os/file_plan9.go
+++ b/src/pkg/os/file_plan9.go
@@ -133,6 +133,9 @@ func OpenFile(name string, flag int, perm FileMode) (file *File, err error) {
// Close closes the File, rendering it unusable for I/O.
// It returns an error, if any.
func (f *File) Close() error {
+ if f == nil {
+ return ErrInvalid
+ }
return f.file.close()
}
@@ -156,6 +159,9 @@ func (file *file) close() error {
// Stat returns the FileInfo structure describing file.
// If there is an error, it will be of type *PathError.
func (f *File) Stat() (fi FileInfo, err error) {
+ if f == nil {
+ return nil, ErrInvalid
+ }
d, err := dirstat(f)
if err != nil {
return nil, err
@@ -167,8 +173,11 @@ func (f *File) Stat() (fi FileInfo, err error) {
// It does not change the I/O offset.
// If there is an error, it will be of type *PathError.
func (f *File) Truncate(size int64) error {
- var d syscall.Dir
+ if f == nil {
+ return ErrInvalid
+ }
+ var d syscall.Dir
d.Null()
d.Length = size
@@ -188,6 +197,9 @@ const chmodMask = uint32(syscall.DMAPPEND | syscall.DMEXCL | syscall.DMTMP | Mod
// Chmod changes the mode of the file to mode.
// If there is an error, it will be of type *PathError.
func (f *File) Chmod(mode FileMode) error {
+ if f == nil {
+ return ErrInvalid
+ }
var d syscall.Dir
odir, e := dirstat(f)
@@ -419,6 +431,9 @@ func Lchown(name string, uid, gid int) error {
// Chown changes the numeric uid and gid of the named file.
// If there is an error, it will be of type *PathError.
func (f *File) Chown(uid, gid int) error {
+ if f == nil {
+ return ErrInvalid
+ }
return &PathError{"chown", f.name, syscall.EPLAN9}
}
diff --git a/src/pkg/os/file_posix.go b/src/pkg/os/file_posix.go
index 3df43feaa..a8bef359b 100644
--- a/src/pkg/os/file_posix.go
+++ b/src/pkg/os/file_posix.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.
-// +build darwin freebsd linux netbsd openbsd windows
+// +build darwin dragonfly freebsd linux netbsd openbsd windows
package os
@@ -86,6 +86,9 @@ func Chmod(name string, mode FileMode) error {
// Chmod changes the mode of the file to mode.
// If there is an error, it will be of type *PathError.
func (f *File) Chmod(mode FileMode) error {
+ if f == nil {
+ return ErrInvalid
+ }
if e := syscall.Fchmod(f.fd, syscallMode(mode)); e != nil {
return &PathError{"chmod", f.name, e}
}
@@ -115,6 +118,9 @@ func Lchown(name string, uid, gid int) error {
// Chown changes the numeric uid and gid of the named file.
// If there is an error, it will be of type *PathError.
func (f *File) Chown(uid, gid int) error {
+ if f == nil {
+ return ErrInvalid
+ }
if e := syscall.Fchown(f.fd, uid, gid); e != nil {
return &PathError{"chown", f.name, e}
}
@@ -125,6 +131,9 @@ func (f *File) Chown(uid, gid int) error {
// It does not change the I/O offset.
// If there is an error, it will be of type *PathError.
func (f *File) Truncate(size int64) error {
+ if f == nil {
+ return ErrInvalid
+ }
if e := syscall.Ftruncate(f.fd, size); e != nil {
return &PathError{"truncate", f.name, e}
}
diff --git a/src/pkg/os/file_unix.go b/src/pkg/os/file_unix.go
index 898e7634a..ff1a597e7 100644
--- a/src/pkg/os/file_unix.go
+++ b/src/pkg/os/file_unix.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.
-// +build darwin freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd
package os
@@ -96,6 +96,9 @@ func OpenFile(name string, flag int, perm FileMode) (file *File, err error) {
// Close closes the File, rendering it unusable for I/O.
// It returns an error, if any.
func (f *File) Close() error {
+ if f == nil {
+ return ErrInvalid
+ }
return f.file.close()
}
@@ -117,6 +120,9 @@ func (file *file) close() error {
// Stat returns the FileInfo structure describing file.
// If there is an error, it will be of type *PathError.
func (f *File) Stat() (fi FileInfo, err error) {
+ if f == nil {
+ return nil, ErrInvalid
+ }
var stat syscall.Stat_t
err = syscall.Fstat(f.fd, &stat)
if err != nil {
@@ -158,12 +164,12 @@ func (f *File) readdir(n int) (fi []FileInfo, err error) {
names, err := f.Readdirnames(n)
fi = make([]FileInfo, len(names))
for i, filename := range names {
- fip, err := Lstat(dirname + filename)
- if err == nil {
- fi[i] = fip
- } else {
+ fip, lerr := lstat(dirname + filename)
+ if lerr != nil {
fi[i] = &fileStat{name: filename}
+ continue
}
+ fi[i] = fip
}
return fi, err
}
diff --git a/src/pkg/os/file_windows.go b/src/pkg/os/file_windows.go
index 82af756d8..fab7de342 100644
--- a/src/pkg/os/file_windows.go
+++ b/src/pkg/os/file_windows.go
@@ -153,6 +153,9 @@ func OpenFile(name string, flag int, perm FileMode) (file *File, err error) {
// Close closes the File, rendering it unusable for I/O.
// It returns an error, if any.
func (file *File) Close() error {
+ if file == nil {
+ return ErrInvalid
+ }
return file.file.close()
}
@@ -251,8 +254,14 @@ func (f *File) readConsole(b []byte) (n int, err error) {
return 0, nil
}
if len(f.readbuf) == 0 {
+ // syscall.ReadConsole seems to fail, if given large buffer.
+ // So limit the buffer to 16000 characters.
+ numBytes := len(b)
+ if numBytes > 16000 {
+ numBytes = 16000
+ }
// get more input data from os
- wchars := make([]uint16, len(b))
+ wchars := make([]uint16, numBytes)
var p *uint16
if len(b) > 0 {
p = &wchars[0]
@@ -306,6 +315,10 @@ func (f *File) pread(b []byte, off int64) (n int, err error) {
var done uint32
e = syscall.ReadFile(syscall.Handle(f.fd), b, &done, &o)
if e != nil {
+ if e == syscall.ERROR_HANDLE_EOF {
+ // end of file
+ return 0, nil
+ }
return 0, e
}
return int(done), nil
diff --git a/src/pkg/os/getwd.go b/src/pkg/os/getwd.go
index 0235c5d77..8c5ff7fca 100644
--- a/src/pkg/os/getwd.go
+++ b/src/pkg/os/getwd.go
@@ -14,6 +14,10 @@ var getwdCache struct {
dir string
}
+// useSyscallwd determines whether to use the return value of
+// syscall.Getwd based on its error.
+var useSyscallwd = func(error) bool { return true }
+
// Getwd returns a rooted path name corresponding to the
// current directory. If the current directory can be
// reached via multiple paths (due to symbolic links),
@@ -22,7 +26,9 @@ func Getwd() (pwd string, err error) {
// If the operating system provides a Getwd call, use it.
if syscall.ImplementsGetwd {
s, e := syscall.Getwd()
- return s, NewSyscallError("getwd", e)
+ if useSyscallwd(e) {
+ return s, NewSyscallError("getwd", e)
+ }
}
// Otherwise, we're trying to find our way back to ".".
diff --git a/src/pkg/os/getwd_darwin.go b/src/pkg/os/getwd_darwin.go
new file mode 100644
index 000000000..e51ffcd5e
--- /dev/null
+++ b/src/pkg/os/getwd_darwin.go
@@ -0,0 +1,15 @@
+// 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"
+
+func init() {
+ useSyscallwd = useSyscallwdDarwin
+}
+
+func useSyscallwdDarwin(err error) bool {
+ return err != syscall.ENOTSUP
+}
diff --git a/src/pkg/os/os_test.go b/src/pkg/os/os_test.go
index 29706015d..9462ebd42 100644
--- a/src/pkg/os/os_test.go
+++ b/src/pkg/os/os_test.go
@@ -11,11 +11,13 @@ import (
"io"
"io/ioutil"
. "os"
+ osexec "os/exec"
"path/filepath"
"runtime"
"strings"
"syscall"
"testing"
+ "text/template"
"time"
)
@@ -297,6 +299,7 @@ func TestReaddirnamesOneAtATime(t *testing.T) {
if err2 != nil {
t.Fatalf("open %q failed: %v", dir, err2)
}
+ defer file1.Close()
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))
@@ -524,6 +527,7 @@ func exec(t *testing.T, dir, cmd string, args []string, expect string) {
if err != nil {
t.Fatalf("Pipe: %v", err)
}
+ defer r.Close()
attr := &ProcAttr{Dir: dir, Files: []*File{nil, w, Stderr}}
p, err := StartProcess(cmd, args, attr)
if err != nil {
@@ -821,9 +825,16 @@ func TestOpenError(t *testing.T) {
if !strings.HasSuffix(syscallErrStr, expectedErrStr) {
t.Errorf("Open(%q, %d) = _, %q; want suffix %q", tt.path, tt.mode, syscallErrStr, expectedErrStr)
}
- } else {
- t.Errorf("Open(%q, %d) = _, %q; want %q", tt.path, tt.mode, perr.Err.Error(), tt.error.Error())
+ continue
+ }
+ if runtime.GOOS == "dragonfly" {
+ // DragonFly incorrectly returns EACCES rather
+ // EISDIR when a directory is opened for write.
+ if tt.error == syscall.EISDIR && perr.Err == syscall.EACCES {
+ continue
+ }
}
+ t.Errorf("Open(%q, %d) = _, %q; want %q", tt.path, tt.mode, perr.Err.Error(), tt.error.Error())
}
}
}
@@ -842,6 +853,7 @@ func run(t *testing.T, cmd []string) string {
if err != nil {
t.Fatal(err)
}
+ defer r.Close()
p, err := StartProcess("/bin/hostname", []string{"hostname"}, &ProcAttr{Files: []*File{nil, w, Stderr}})
if err != nil {
t.Fatal(err)
@@ -1114,3 +1126,88 @@ func TestStatDirModeExec(t *testing.T) {
t.Errorf("Stat %q: mode %#o want %#o", path, dir.Mode()&mode, mode)
}
}
+
+func TestReadAtEOF(t *testing.T) {
+ f := newFile("TestReadAtEOF", t)
+ defer Remove(f.Name())
+ defer f.Close()
+
+ _, err := f.ReadAt(make([]byte, 10), 0)
+ switch err {
+ case io.EOF:
+ // all good
+ case nil:
+ t.Fatalf("ReadAt succeeded")
+ default:
+ t.Fatalf("ReadAt failed: %s", err)
+ }
+}
+
+func testKillProcess(t *testing.T, processKiller func(p *Process)) {
+ dir, err := ioutil.TempDir("", "go-build")
+ if err != nil {
+ t.Fatalf("Failed to create temp directory: %v", err)
+ }
+ defer RemoveAll(dir)
+
+ src := filepath.Join(dir, "main.go")
+ f, err := Create(src)
+ if err != nil {
+ t.Fatalf("Failed to create %v: %v", src, err)
+ }
+ st := template.Must(template.New("source").Parse(`
+package main
+import "time"
+func main() {
+ time.Sleep(time.Second)
+}
+`))
+ err = st.Execute(f, nil)
+ if err != nil {
+ f.Close()
+ t.Fatalf("Failed to execute template: %v", err)
+ }
+ f.Close()
+
+ exe := filepath.Join(dir, "main.exe")
+ output, err := osexec.Command("go", "build", "-o", exe, src).CombinedOutput()
+ if err != nil {
+ t.Fatalf("Failed to build exe %v: %v %v", exe, err, string(output))
+ }
+
+ cmd := osexec.Command(exe)
+ err = cmd.Start()
+ if err != nil {
+ t.Fatalf("Failed to start test process: %v", err)
+ }
+ go func() {
+ time.Sleep(100 * time.Millisecond)
+ processKiller(cmd.Process)
+ }()
+ err = cmd.Wait()
+ if err == nil {
+ t.Errorf("Test process succeeded, but expected to fail")
+ }
+}
+
+func TestKillStartProcess(t *testing.T) {
+ testKillProcess(t, func(p *Process) {
+ err := p.Kill()
+ if err != nil {
+ t.Fatalf("Failed to kill test process: %v", err)
+ }
+ })
+}
+
+func TestKillFindProcess(t *testing.T) {
+ testKillProcess(t, func(p *Process) {
+ p2, err := FindProcess(p.Pid)
+ if err != nil {
+ t.Fatalf("Failed to find test process: %v", err)
+ }
+ err = p2.Kill()
+ if err != nil {
+ t.Fatalf("Failed to kill test process: %v", err)
+ }
+ })
+}
diff --git a/src/pkg/os/os_unix_test.go b/src/pkg/os/os_unix_test.go
index f8e330beb..b0fc0256d 100644
--- a/src/pkg/os/os_unix_test.go
+++ b/src/pkg/os/os_unix_test.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.
-// +build darwin freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd
package os_test
@@ -28,7 +28,7 @@ func checkUidGid(t *testing.T, path string, uid, gid int) {
}
func TestChown(t *testing.T) {
- // Chown is not supported under windows or Plan 9.
+ // Chown is not supported under windows os Plan 9.
// Plan9 provides a native ChownPlan9 version instead.
if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
return
@@ -74,3 +74,41 @@ func TestChown(t *testing.T) {
checkUidGid(t, f.Name(), int(sys.Uid), gid)
}
}
+
+func TestReaddirWithBadLstat(t *testing.T) {
+ handle, err := Open(sfdir)
+ failfile := sfdir + "/" + sfname
+ if err != nil {
+ t.Fatalf("Couldn't open %s: %s", sfdir, err)
+ }
+
+ *LstatP = func(file string) (FileInfo, error) {
+ if file == failfile {
+ var fi FileInfo
+ return fi, ErrInvalid
+ }
+ return Lstat(file)
+ }
+ defer func() { *LstatP = Lstat }()
+
+ dirs, err := handle.Readdir(-1)
+ if err != nil {
+ t.Fatalf("Expected Readdir to return no error, got %v", err)
+ }
+ foundfail := false
+ for _, dir := range dirs {
+ if dir.Name() == sfname {
+ foundfail = true
+ if dir.Sys() != nil {
+ t.Errorf("Expected Readdir for %s should not contain Sys", failfile)
+ }
+ } else {
+ if dir.Sys() == nil {
+ t.Errorf("Readdir for every file other than %s should contain Sys, but %s/%s didn't either", failfile, sfdir, dir.Name())
+ }
+ }
+ }
+ if !foundfail {
+ t.Fatalf("Expected %s from Readdir, but didn't find it", failfile)
+ }
+}
diff --git a/src/pkg/os/path_test.go b/src/pkg/os/path_test.go
index 16c4120dc..27abf5982 100644
--- a/src/pkg/os/path_test.go
+++ b/src/pkg/os/path_test.go
@@ -91,7 +91,7 @@ func TestRemoveAll(t *testing.T) {
if err = RemoveAll(path); err != nil {
t.Fatalf("RemoveAll %q (first): %s", path, err)
}
- if _, err := Lstat(path); err == nil {
+ if _, err = Lstat(path); err == nil {
t.Fatalf("Lstat %q succeeded after RemoveAll (first)", path)
}
@@ -153,7 +153,7 @@ func TestRemoveAll(t *testing.T) {
Chmod(dpath, 0777)
for _, s := range []string{fpath, path + "/zzz"} {
- if _, err := Lstat(s); err == nil {
+ if _, err = Lstat(s); err == nil {
t.Fatalf("Lstat %q succeeded after partial RemoveAll", s)
}
}
@@ -161,7 +161,7 @@ func TestRemoveAll(t *testing.T) {
if err = RemoveAll(path); err != nil {
t.Fatalf("RemoveAll %q after partial RemoveAll: %s", path, err)
}
- if _, err := Lstat(path); err == nil {
+ if _, err = Lstat(path); err == nil {
t.Fatalf("Lstat %q succeeded after RemoveAll (final)", path)
}
}
diff --git a/src/pkg/os/path_unix.go b/src/pkg/os/path_unix.go
index 30a167b1a..3bf63bf80 100644
--- a/src/pkg/os/path_unix.go
+++ b/src/pkg/os/path_unix.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.
-// +build darwin freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd
package os
diff --git a/src/pkg/os/pipe_bsd.go b/src/pkg/os/pipe_bsd.go
index a2ce9a39f..73d35b4d5 100644
--- a/src/pkg/os/pipe_bsd.go
+++ b/src/pkg/os/pipe_bsd.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.
-// +build darwin freebsd netbsd openbsd
+// +build darwin dragonfly freebsd netbsd openbsd
package os
diff --git a/src/pkg/os/signal/sig.s b/src/pkg/os/signal/sig.s
index df4855de8..888823cf4 100644
--- a/src/pkg/os/signal/sig.s
+++ b/src/pkg/os/signal/sig.s
@@ -6,16 +6,18 @@
// +build amd64 arm 386
+#include "../../../cmd/ld/textflag.h"
+
#ifdef GOARCH_arm
#define JMP B
#endif
-TEXT ·signal_disable(SB),7,$0
+TEXT ·signal_disable(SB),NOSPLIT,$0
JMP runtime·signal_disable(SB)
-TEXT ·signal_enable(SB),7,$0
+TEXT ·signal_enable(SB),NOSPLIT,$0
JMP runtime·signal_enable(SB)
-TEXT ·signal_recv(SB),7,$0
+TEXT ·signal_recv(SB),NOSPLIT,$0
JMP runtime·signal_recv(SB)
diff --git a/src/pkg/os/signal/signal_test.go b/src/pkg/os/signal/signal_test.go
index d13833306..741f2a0ed 100644
--- a/src/pkg/os/signal/signal_test.go
+++ b/src/pkg/os/signal/signal_test.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.
-// +build darwin freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd
package signal
@@ -36,8 +36,8 @@ func TestSignal(t *testing.T) {
Notify(c, syscall.SIGHUP)
defer Stop(c)
- t.Logf("sighup...")
// Send this process a SIGHUP
+ t.Logf("sighup...")
syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
waitSig(t, c, syscall.SIGHUP)
@@ -45,18 +45,18 @@ func TestSignal(t *testing.T) {
c1 := make(chan os.Signal, 1)
Notify(c1)
- t.Logf("sigwinch...")
// Send this process a SIGWINCH
+ t.Logf("sigwinch...")
syscall.Kill(syscall.Getpid(), syscall.SIGWINCH)
waitSig(t, c1, syscall.SIGWINCH)
// Send two more SIGHUPs, to make sure that
// they get delivered on c1 and that not reading
// from c does not block everything.
- t.Logf("sigwinch...")
+ t.Logf("sighup...")
syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
waitSig(t, c1, syscall.SIGHUP)
- t.Logf("sigwinch...")
+ t.Logf("sighup...")
syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
waitSig(t, c1, syscall.SIGHUP)
diff --git a/src/pkg/os/signal/signal_unix.go b/src/pkg/os/signal/signal_unix.go
index 6b4c8ab66..318488dc0 100644
--- a/src/pkg/os/signal/signal_unix.go
+++ b/src/pkg/os/signal/signal_unix.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.
-// +build darwin freebsd linux netbsd openbsd windows
+// +build darwin dragonfly freebsd linux netbsd openbsd windows
package signal
diff --git a/src/pkg/os/stat_dragonfly.go b/src/pkg/os/stat_dragonfly.go
new file mode 100644
index 000000000..605c1d9b6
--- /dev/null
+++ b/src/pkg/os/stat_dragonfly.go
@@ -0,0 +1,61 @@
+// 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"
+ "time"
+)
+
+func sameFile(fs1, fs2 *fileStat) bool {
+ stat1 := fs1.sys.(*syscall.Stat_t)
+ stat2 := fs2.sys.(*syscall.Stat_t)
+ return stat1.Dev == stat2.Dev && stat1.Ino == stat2.Ino
+}
+
+func fileInfoFromStat(st *syscall.Stat_t, name string) FileInfo {
+ fs := &fileStat{
+ name: basename(name),
+ size: int64(st.Size),
+ modTime: timespecToTime(st.Mtim),
+ sys: st,
+ }
+ fs.mode = FileMode(st.Mode & 0777)
+ switch st.Mode & syscall.S_IFMT {
+ case syscall.S_IFBLK:
+ fs.mode |= ModeDevice
+ case syscall.S_IFCHR:
+ fs.mode |= ModeDevice | ModeCharDevice
+ case syscall.S_IFDIR:
+ fs.mode |= ModeDir
+ case syscall.S_IFIFO:
+ fs.mode |= ModeNamedPipe
+ case syscall.S_IFLNK:
+ fs.mode |= ModeSymlink
+ case syscall.S_IFREG:
+ // nothing to do
+ case syscall.S_IFSOCK:
+ fs.mode |= ModeSocket
+ }
+ if st.Mode&syscall.S_ISGID != 0 {
+ fs.mode |= ModeSetgid
+ }
+ if st.Mode&syscall.S_ISUID != 0 {
+ fs.mode |= ModeSetuid
+ }
+ if st.Mode&syscall.S_ISVTX != 0 {
+ fs.mode |= ModeSticky
+ }
+ return fs
+}
+
+func timespecToTime(ts syscall.Timespec) time.Time {
+ return time.Unix(int64(ts.Sec), int64(ts.Nsec))
+}
+
+// For testing.
+func atime(fi FileInfo) time.Time {
+ return timespecToTime(fi.Sys().(*syscall.Stat_t).Atim)
+}
diff --git a/src/pkg/os/stat_windows.go b/src/pkg/os/stat_windows.go
index 8394c2b32..6dc386685 100644
--- a/src/pkg/os/stat_windows.go
+++ b/src/pkg/os/stat_windows.go
@@ -12,6 +12,9 @@ import (
// Stat returns the FileInfo structure describing file.
// If there is an error, it will be of type *PathError.
func (file *File) Stat() (fi FileInfo, err error) {
+ if file == nil {
+ return nil, ErrInvalid
+ }
if file == nil || file.fd < 0 {
return nil, syscall.EINVAL
}
diff --git a/src/pkg/os/sys_bsd.go b/src/pkg/os/sys_bsd.go
index 0f263f1c1..9ad2f8546 100644
--- a/src/pkg/os/sys_bsd.go
+++ b/src/pkg/os/sys_bsd.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.
-// +build darwin freebsd netbsd openbsd
+// +build darwin dragonfly freebsd netbsd openbsd
// os code shared between *BSD systems including OS X (Darwin)
// and FreeBSD.
diff --git a/src/pkg/os/user/lookup_plan9.go b/src/pkg/os/user/lookup_plan9.go
new file mode 100644
index 000000000..f7ef3482b
--- /dev/null
+++ b/src/pkg/os/user/lookup_plan9.go
@@ -0,0 +1,46 @@
+// Copyright 2013 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 user
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "syscall"
+)
+
+// Partial os/user support on Plan 9.
+// Supports Current(), but not Lookup()/LookupId().
+// The latter two would require parsing /adm/users.
+const (
+ userFile = "/dev/user"
+)
+
+func current() (*User, error) {
+ ubytes, err := ioutil.ReadFile(userFile)
+ if err != nil {
+ return nil, fmt.Errorf("user: %s", err)
+ }
+
+ uname := string(ubytes)
+
+ u := &User{
+ Uid: uname,
+ Gid: uname,
+ Username: uname,
+ Name: uname,
+ HomeDir: os.Getenv("home"),
+ }
+
+ return u, nil
+}
+
+func lookup(username string) (*User, error) {
+ return nil, syscall.EPLAN9
+}
+
+func lookupId(uid string) (*User, error) {
+ return nil, syscall.EPLAN9
+}
diff --git a/src/pkg/os/user/lookup_stubs.go b/src/pkg/os/user/lookup_stubs.go
index ad06907b5..86f0e6e64 100644
--- a/src/pkg/os/user/lookup_stubs.go
+++ b/src/pkg/os/user/lookup_stubs.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.
-// +build !cgo,!windows
+// +build !cgo,!windows,!plan9
package user
diff --git a/src/pkg/os/user/lookup_unix.go b/src/pkg/os/user/lookup_unix.go
index 609542263..5459268fa 100644
--- a/src/pkg/os/user/lookup_unix.go
+++ b/src/pkg/os/user/lookup_unix.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.
-// +build darwin freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd
// +build cgo
package user
@@ -50,10 +50,10 @@ func lookupUnix(uid int, username string, lookupByName bool) (*User, error) {
var result *C.struct_passwd
var bufSize C.long
- if runtime.GOOS == "freebsd" {
- // FreeBSD doesn't have _SC_GETPW_R_SIZE_MAX
- // and just returns -1. So just use the same
- // size that Linux returns
+ if runtime.GOOS == "dragonfly" || runtime.GOOS == "freebsd" {
+ // DragonFly and FreeBSD do not have _SC_GETPW_R_SIZE_MAX
+ // and just return -1. So just use the same
+ // size that Linux returns.
bufSize = 1024
} else {
bufSize = C.sysconf(C._SC_GETPW_R_SIZE_MAX)
diff --git a/src/pkg/os/user/lookup_windows.go b/src/pkg/os/user/lookup_windows.go
index a0a8a4ec1..99c325ff0 100644
--- a/src/pkg/os/user/lookup_windows.go
+++ b/src/pkg/os/user/lookup_windows.go
@@ -10,37 +10,63 @@ import (
"unsafe"
)
-func lookupFullName(domain, username, domainAndUser string) (string, error) {
- // try domain controller first
- name, e := syscall.TranslateAccountName(domainAndUser,
+func isDomainJoined() (bool, error) {
+ var domain *uint16
+ var status uint32
+ err := syscall.NetGetJoinInformation(nil, &domain, &status)
+ if err != nil {
+ return false, err
+ }
+ syscall.NetApiBufferFree((*byte)(unsafe.Pointer(domain)))
+ return status == syscall.NetSetupDomainName, nil
+}
+
+func lookupFullNameDomain(domainAndUser string) (string, error) {
+ return syscall.TranslateAccountName(domainAndUser,
syscall.NameSamCompatible, syscall.NameDisplay, 50)
+}
+
+func lookupFullNameServer(servername, username string) (string, error) {
+ s, e := syscall.UTF16PtrFromString(servername)
if e != nil {
- // domain lookup failed, perhaps this pc is not part of domain
- d, e := syscall.UTF16PtrFromString(domain)
- if e != nil {
- return "", e
- }
- u, e := syscall.UTF16PtrFromString(username)
- if e != nil {
- return "", e
- }
- var p *byte
- e = syscall.NetUserGetInfo(d, u, 10, &p)
- if e != nil {
- // path executed when a domain user is disconnected from the domain
- // pretend username is fullname
- return username, nil
- }
- defer syscall.NetApiBufferFree(p)
- i := (*syscall.UserInfo10)(unsafe.Pointer(p))
- if i.FullName == nil {
- return "", nil
- }
- name = syscall.UTF16ToString((*[1024]uint16)(unsafe.Pointer(i.FullName))[:])
+ return "", e
}
+ u, e := syscall.UTF16PtrFromString(username)
+ if e != nil {
+ return "", e
+ }
+ var p *byte
+ e = syscall.NetUserGetInfo(s, u, 10, &p)
+ if e != nil {
+ return "", e
+ }
+ defer syscall.NetApiBufferFree(p)
+ i := (*syscall.UserInfo10)(unsafe.Pointer(p))
+ if i.FullName == nil {
+ return "", nil
+ }
+ name := syscall.UTF16ToString((*[1024]uint16)(unsafe.Pointer(i.FullName))[:])
return name, nil
}
+func lookupFullName(domain, username, domainAndUser string) (string, error) {
+ joined, err := isDomainJoined()
+ if err == nil && joined {
+ name, err := lookupFullNameDomain(domainAndUser)
+ if err == nil {
+ return name, nil
+ }
+ }
+ name, err := lookupFullNameServer(domain, username)
+ if err == nil {
+ return name, nil
+ }
+ // domain worked neigher as a domain nor as a server
+ // could be domain server unavailable
+ // pretend username is fullname
+ return username, nil
+}
+
func newUser(usid *syscall.SID, gid, dir string) (*User, error) {
username, domain, t, e := usid.LookupAccount("")
if e != nil {
@@ -73,6 +99,7 @@ func current() (*User, error) {
if e != nil {
return nil, e
}
+ defer t.Close()
u, e := t.GetTokenUser()
if e != nil {
return nil, e
diff --git a/src/pkg/os/user/user.go b/src/pkg/os/user/user.go
index 841f2263f..e8680fe54 100644
--- a/src/pkg/os/user/user.go
+++ b/src/pkg/os/user/user.go
@@ -16,6 +16,8 @@ var implemented = true // set to false by lookup_stubs.go's init
// On posix systems Uid and Gid contain a decimal number
// representing uid and gid. On windows Uid and Gid
// contain security identifier (SID) in a string format.
+// On Plan 9, Uid, Gid, Username, and Name will be the
+// contents of /dev/user.
type User struct {
Uid string // user id
Gid string // primary group id
diff --git a/src/pkg/os/user/user_test.go b/src/pkg/os/user/user_test.go
index 444a9aacd..9d9420e80 100644
--- a/src/pkg/os/user/user_test.go
+++ b/src/pkg/os/user/user_test.go
@@ -13,12 +13,6 @@ func check(t *testing.T) {
if !implemented {
t.Skip("user: not implemented; skipping tests")
}
- switch runtime.GOOS {
- case "linux", "freebsd", "darwin", "windows":
- // test supported
- default:
- t.Skipf("user: Lookup not implemented on %q; skipping test", runtime.GOOS)
- }
}
func TestCurrent(t *testing.T) {
@@ -61,6 +55,10 @@ func compare(t *testing.T, want, got *User) {
func TestLookup(t *testing.T) {
check(t)
+ if runtime.GOOS == "plan9" {
+ t.Skipf("Lookup not implemented on %q", runtime.GOOS)
+ }
+
want, err := Current()
if err != nil {
t.Fatalf("Current: %v", err)
@@ -75,6 +73,10 @@ func TestLookup(t *testing.T) {
func TestLookupId(t *testing.T) {
check(t)
+ if runtime.GOOS == "plan9" {
+ t.Skipf("LookupId not implemented on %q", runtime.GOOS)
+ }
+
want, err := Current()
if err != nil {
t.Fatalf("Current: %v", err)
diff --git a/src/pkg/path/filepath/match.go b/src/pkg/path/filepath/match.go
index db8b0260c..3d84145d7 100644
--- a/src/pkg/path/filepath/match.go
+++ b/src/pkg/path/filepath/match.go
@@ -132,6 +132,12 @@ func matchChunk(chunk, s string) (rest string, ok bool, err error) {
r, n := utf8.DecodeRuneInString(s)
s = s[n:]
chunk = chunk[1:]
+ // We can't end right after '[', we're expecting at least
+ // a closing bracket and possibly a caret.
+ if len(chunk) == 0 {
+ err = ErrBadPattern
+ return
+ }
// possibly negated
negated := chunk[0] == '^'
if negated {
diff --git a/src/pkg/path/filepath/match_test.go b/src/pkg/path/filepath/match_test.go
index f1bc60e35..13108ce1e 100644
--- a/src/pkg/path/filepath/match_test.go
+++ b/src/pkg/path/filepath/match_test.go
@@ -65,6 +65,11 @@ var matchTests = []MatchTest{
{"[-x]", "a", false, ErrBadPattern},
{"\\", "a", false, ErrBadPattern},
{"[a-b-c]", "a", false, ErrBadPattern},
+ {"[", "a", false, ErrBadPattern},
+ {"[^", "a", false, ErrBadPattern},
+ {"[^bc", "a", false, ErrBadPattern},
+ {"a[", "a", false, nil},
+ {"a[", "ab", false, ErrBadPattern},
{"*x", "xxx", true, nil},
}
diff --git a/src/pkg/path/filepath/path_test.go b/src/pkg/path/filepath/path_test.go
index c4d73602f..d32b70d6e 100644
--- a/src/pkg/path/filepath/path_test.go
+++ b/src/pkg/path/filepath/path_test.go
@@ -107,6 +107,9 @@ func TestClean(t *testing.T) {
}
}
+ if testing.Short() {
+ t.Skip("skipping malloc count in short mode")
+ }
if runtime.GOMAXPROCS(0) > 1 {
t.Log("skipping AllocsPerRun checks; GOMAXPROCS>1")
return
@@ -630,6 +633,10 @@ func simpleJoin(dir, path string) string {
}
func TestEvalSymlinks(t *testing.T) {
+ if runtime.GOOS == "plan9" {
+ t.Skip("Skipping test: symlinks don't exist under Plan 9")
+ }
+
tmpDir, err := ioutil.TempDir("", "evalsymlink")
if err != nil {
t.Fatal("creating temp dir:", err)
@@ -920,27 +927,32 @@ func TestDriveLetterInEvalSymlinks(t *testing.T) {
}
func TestBug3486(t *testing.T) { // http://code.google.com/p/go/issues/detail?id=3486
- root, err := filepath.EvalSymlinks(runtime.GOROOT())
+ root, err := filepath.EvalSymlinks(runtime.GOROOT() + "/test")
if err != nil {
t.Fatal(err)
}
- lib := filepath.Join(root, "lib")
- src := filepath.Join(root, "src")
- seenSrc := false
+ bugs := filepath.Join(root, "bugs")
+ ken := filepath.Join(root, "ken")
+ seenBugs := false
+ seenKen := false
filepath.Walk(root, func(pth string, info os.FileInfo, err error) error {
if err != nil {
t.Fatal(err)
}
switch pth {
- case lib:
+ case bugs:
+ seenBugs = true
return filepath.SkipDir
- case src:
- seenSrc = true
+ case ken:
+ if !seenBugs {
+ t.Fatal("filepath.Walk out of order - ken before bugs")
+ }
+ seenKen = true
}
return nil
})
- if !seenSrc {
- t.Fatalf("%q not seen", src)
+ if !seenKen {
+ t.Fatalf("%q not seen", ken)
}
}
diff --git a/src/pkg/path/filepath/path_unix.go b/src/pkg/path/filepath/path_unix.go
index cff7b2c65..d927b342b 100644
--- a/src/pkg/path/filepath/path_unix.go
+++ b/src/pkg/path/filepath/path_unix.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.
-// +build darwin freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd
package filepath
diff --git a/src/pkg/path/match_test.go b/src/pkg/path/match_test.go
index 730b6b903..6b0676f81 100644
--- a/src/pkg/path/match_test.go
+++ b/src/pkg/path/match_test.go
@@ -61,6 +61,11 @@ var matchTests = []MatchTest{
{"[-x]", "a", false, ErrBadPattern},
{"\\", "a", false, ErrBadPattern},
{"[a-b-c]", "a", false, ErrBadPattern},
+ {"[", "a", false, ErrBadPattern},
+ {"[^", "a", false, ErrBadPattern},
+ {"[^bc", "a", false, ErrBadPattern},
+ {"a[", "a", false, nil},
+ {"a[", "ab", false, ErrBadPattern},
{"*x", "xxx", true, nil},
}
diff --git a/src/pkg/path/path_test.go b/src/pkg/path/path_test.go
index 69caa80e4..13b585223 100644
--- a/src/pkg/path/path_test.go
+++ b/src/pkg/path/path_test.go
@@ -72,7 +72,12 @@ func TestClean(t *testing.T) {
t.Errorf("Clean(%q) = %q, want %q", test.result, s, test.result)
}
}
+}
+func TestCleanMallocs(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping malloc count in short mode")
+ }
if runtime.GOMAXPROCS(0) > 1 {
t.Log("skipping AllocsPerRun checks; GOMAXPROCS>1")
return
diff --git a/src/pkg/reflect/all_test.go b/src/pkg/reflect/all_test.go
index a61f66308..e9a20963f 100644
--- a/src/pkg/reflect/all_test.go
+++ b/src/pkg/reflect/all_test.go
@@ -169,16 +169,20 @@ var typeTests = []pair{
}
var valueTests = []pair{
+ {new(int), "132"},
{new(int8), "8"},
{new(int16), "16"},
{new(int32), "32"},
{new(int64), "64"},
+ {new(uint), "132"},
{new(uint8), "8"},
{new(uint16), "16"},
{new(uint32), "32"},
{new(uint64), "64"},
{new(float32), "256.25"},
{new(float64), "512.125"},
+ {new(complex64), "532.125+10i"},
+ {new(complex128), "564.25+1i"},
{new(string), "stringy cheese"},
{new(bool), "true"},
{new(*int8), "*int8(0)"},
@@ -944,7 +948,7 @@ func TestMap(t *testing.T) {
newm := newmap.Interface().(map[string]int)
if len(newm) != len(m) {
- t.Errorf("length after copy: newm=%d, m=%d", newm, m)
+ t.Errorf("length after copy: newm=%d, m=%d", len(newm), len(m))
}
for k, v := range newm {
@@ -1448,6 +1452,24 @@ func TestMakeFunc(t *testing.T) {
}
}
+func TestMakeFuncInterface(t *testing.T) {
+ fn := func(i int) int { return i }
+ incr := func(in []Value) []Value {
+ return []Value{ValueOf(int(in[0].Int() + 1))}
+ }
+ fv := MakeFunc(TypeOf(fn), incr)
+ ValueOf(&fn).Elem().Set(fv)
+ if r := fn(2); r != 3 {
+ t.Errorf("Call returned %d, want 3", r)
+ }
+ if r := fv.Call([]Value{ValueOf(14)})[0].Int(); r != 15 {
+ t.Errorf("Call returned %d, want 15", r)
+ }
+ if r := fv.Interface().(func(int) int)(26); r != 27 {
+ t.Errorf("Call returned %d, want 27", r)
+ }
+}
+
type Point struct {
x, y int
}
@@ -1598,6 +1620,25 @@ func TestMethodValue(t *testing.T) {
t.Errorf("Pointer Value MethodByName returned %d; want 325", i)
}
+ // Curried method of pointer to pointer.
+ pp := &p
+ v = ValueOf(&pp).Elem().Method(1)
+ if tt := v.Type(); tt != tfunc {
+ t.Errorf("Pointer Pointer Value Method Type is %s; want %s", tt, tfunc)
+ }
+ i = ValueOf(v.Interface()).Call([]Value{ValueOf(14)})[0].Int()
+ if i != 350 {
+ t.Errorf("Pointer Pointer Value Method returned %d; want 350", i)
+ }
+ v = ValueOf(&pp).Elem().MethodByName("Dist")
+ if tt := v.Type(); tt != tfunc {
+ t.Errorf("Pointer Pointer Value MethodByName Type is %s; want %s", tt, tfunc)
+ }
+ i = ValueOf(v.Interface()).Call([]Value{ValueOf(15)})[0].Int()
+ if i != 375 {
+ t.Errorf("Pointer Pointer Value MethodByName returned %d; want 375", i)
+ }
+
// Curried method of interface value.
// Have to wrap interface value in a struct to get at it.
// Passing it to ValueOf directly would
@@ -1612,17 +1653,17 @@ func TestMethodValue(t *testing.T) {
if tt := v.Type(); tt != tfunc {
t.Errorf("Interface Method Type is %s; want %s", tt, tfunc)
}
- i = ValueOf(v.Interface()).Call([]Value{ValueOf(14)})[0].Int()
- if i != 350 {
- t.Errorf("Interface Method returned %d; want 350", i)
+ i = ValueOf(v.Interface()).Call([]Value{ValueOf(16)})[0].Int()
+ if i != 400 {
+ t.Errorf("Interface Method returned %d; want 400", i)
}
v = pv.MethodByName("Dist")
if tt := v.Type(); tt != tfunc {
t.Errorf("Interface MethodByName Type is %s; want %s", tt, tfunc)
}
- i = ValueOf(v.Interface()).Call([]Value{ValueOf(15)})[0].Int()
- if i != 375 {
- t.Errorf("Interface MethodByName returned %d; want 375", i)
+ i = ValueOf(v.Interface()).Call([]Value{ValueOf(17)})[0].Int()
+ if i != 425 {
+ t.Errorf("Interface MethodByName returned %d; want 425", i)
}
}
@@ -2287,6 +2328,9 @@ func TestAddr(t *testing.T) {
}
func noAlloc(t *testing.T, n int, f func(int)) {
+ if testing.Short() {
+ t.Skip("skipping malloc count in short mode")
+ }
if runtime.GOMAXPROCS(0) > 1 {
t.Skip("skipping; GOMAXPROCS>1")
}
@@ -2368,6 +2412,74 @@ func TestSlice(t *testing.T) {
}
}
+func TestSlice3(t *testing.T) {
+ xs := []int{1, 2, 3, 4, 5, 6, 7, 8}
+ v := ValueOf(xs).Slice3(3, 5, 7).Interface().([]int)
+ if len(v) != 2 {
+ t.Errorf("len(xs.Slice3(3, 5, 7)) = %d", len(v))
+ }
+ if cap(v) != 4 {
+ t.Errorf("cap(xs.Slice3(3, 5, 7)) = %d", cap(v))
+ }
+ if !DeepEqual(v[0:4], xs[3:7:7]) {
+ t.Errorf("xs.Slice3(3, 5, 7)[0:4] = %v", v[0:4])
+ }
+ rv := ValueOf(&xs).Elem()
+ shouldPanic(func() { rv.Slice3(1, 2, 1) })
+ shouldPanic(func() { rv.Slice3(1, 1, 11) })
+ shouldPanic(func() { rv.Slice3(2, 2, 1) })
+
+ xa := [8]int{10, 20, 30, 40, 50, 60, 70, 80}
+ v = ValueOf(&xa).Elem().Slice3(2, 5, 6).Interface().([]int)
+ if len(v) != 3 {
+ t.Errorf("len(xa.Slice(2, 5, 6)) = %d", len(v))
+ }
+ if cap(v) != 4 {
+ t.Errorf("cap(xa.Slice(2, 5, 6)) = %d", cap(v))
+ }
+ if !DeepEqual(v[0:4], xa[2:6:6]) {
+ t.Errorf("xs.Slice(2, 5, 6)[0:4] = %v", v[0:4])
+ }
+ rv = ValueOf(&xa).Elem()
+ shouldPanic(func() { rv.Slice3(1, 2, 1) })
+ shouldPanic(func() { rv.Slice3(1, 1, 11) })
+ shouldPanic(func() { rv.Slice3(2, 2, 1) })
+
+ s := "hello world"
+ rv = ValueOf(&s).Elem()
+ shouldPanic(func() { rv.Slice3(1, 2, 3) })
+}
+
+func TestSetLenCap(t *testing.T) {
+ xs := []int{1, 2, 3, 4, 5, 6, 7, 8}
+ xa := [8]int{10, 20, 30, 40, 50, 60, 70, 80}
+
+ vs := ValueOf(&xs).Elem()
+ shouldPanic(func() { vs.SetLen(10) })
+ shouldPanic(func() { vs.SetCap(10) })
+ shouldPanic(func() { vs.SetLen(-1) })
+ shouldPanic(func() { vs.SetCap(-1) })
+ shouldPanic(func() { vs.SetCap(6) }) // smaller than len
+ vs.SetLen(5)
+ if len(xs) != 5 || cap(xs) != 8 {
+ t.Errorf("after SetLen(5), len, cap = %d, %d, want 5, 8", len(xs), cap(xs))
+ }
+ vs.SetCap(6)
+ if len(xs) != 5 || cap(xs) != 6 {
+ t.Errorf("after SetCap(6), len, cap = %d, %d, want 5, 6", len(xs), cap(xs))
+ }
+ vs.SetCap(5)
+ if len(xs) != 5 || cap(xs) != 5 {
+ t.Errorf("after SetCap(5), len, cap = %d, %d, want 5, 5", len(xs), cap(xs))
+ }
+ shouldPanic(func() { vs.SetCap(4) }) // smaller than len
+ shouldPanic(func() { vs.SetLen(6) }) // bigger than cap
+
+ va := ValueOf(&xa).Elem()
+ shouldPanic(func() { va.SetLen(8) })
+ shouldPanic(func() { va.SetCap(8) })
+}
+
func TestVariadic(t *testing.T) {
var b bytes.Buffer
V := ValueOf
@@ -2385,6 +2497,15 @@ func TestVariadic(t *testing.T) {
}
}
+func TestFuncArg(t *testing.T) {
+ f1 := func(i int, f func(int) int) int { return f(i) }
+ f2 := func(i int) int { return i + 1 }
+ r := ValueOf(f1).Call([]Value{ValueOf(100), ValueOf(f2)})
+ if r[0].Int() != 101 {
+ t.Errorf("function returned %d, want 101", r[0].Int())
+ }
+}
+
var tagGetTests = []struct {
Tag StructTag
Key string
@@ -2904,17 +3025,28 @@ func TestConvert(t *testing.T) {
all[t2] = true
canConvert[[2]Type{t1, t2}] = true
+ // vout1 represents the in value converted to the in type.
v1 := tt.in
vout1 := v1.Convert(t1)
out1 := vout1.Interface()
if vout1.Type() != tt.in.Type() || !DeepEqual(out1, tt.in.Interface()) {
- t.Errorf("ValueOf(%T(%v)).Convert(%s) = %T(%v), want %T(%v)", tt.in.Interface(), tt.in.Interface(), t1, out1, out1, tt.in.Interface(), tt.in.Interface())
+ t.Errorf("ValueOf(%T(%[1]v)).Convert(%s) = %T(%[3]v), want %T(%[4]v)", tt.in.Interface(), t1, out1, tt.in.Interface())
}
- vout := v1.Convert(t2)
- out := vout.Interface()
- if vout.Type() != tt.out.Type() || !DeepEqual(out, tt.out.Interface()) {
- t.Errorf("ValueOf(%T(%v)).Convert(%s) = %T(%v), want %T(%v)", tt.in.Interface(), tt.in.Interface(), t2, out, out, tt.out.Interface(), tt.out.Interface())
+ // vout2 represents the in value converted to the out type.
+ vout2 := v1.Convert(t2)
+ out2 := vout2.Interface()
+ if vout2.Type() != tt.out.Type() || !DeepEqual(out2, tt.out.Interface()) {
+ t.Errorf("ValueOf(%T(%[1]v)).Convert(%s) = %T(%[3]v), want %T(%[4]v)", tt.in.Interface(), t2, out2, tt.out.Interface())
+ }
+
+ // vout3 represents a new value of the out type, set to vout2. This makes
+ // sure the converted value vout2 is really usable as a regular value.
+ vout3 := New(t2).Elem()
+ vout3.Set(vout2)
+ out3 := vout3.Interface()
+ if vout3.Type() != tt.out.Type() || !DeepEqual(out3, tt.out.Interface()) {
+ t.Errorf("Set(ValueOf(%T(%[1]v)).Convert(%s)) = %T(%[3]v), want %T(%[4]v)", tt.in.Interface(), t2, out3, tt.out.Interface())
}
if IsRO(v1) {
@@ -2923,8 +3055,11 @@ func TestConvert(t *testing.T) {
if IsRO(vout1) {
t.Errorf("self-conversion output %v is RO, should not be", vout1)
}
- if IsRO(vout) {
- t.Errorf("conversion output %v is RO, should not be", vout)
+ if IsRO(vout2) {
+ t.Errorf("conversion output %v is RO, should not be", vout2)
+ }
+ if IsRO(vout3) {
+ t.Errorf("set(conversion output) %v is RO, should not be", vout3)
}
if !IsRO(MakeRO(v1).Convert(t1)) {
t.Errorf("RO self-conversion output %v is not RO, should be", v1)
@@ -3351,6 +3486,46 @@ func BenchmarkFieldByName3(b *testing.B) {
}
}
+type S struct {
+ i1 int64
+ i2 int64
+}
+
+func BenchmarkInterfaceBig(b *testing.B) {
+ v := ValueOf(S{})
+ for i := 0; i < b.N; i++ {
+ v.Interface()
+ }
+ b.StopTimer()
+}
+
+func TestAllocsInterfaceBig(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping malloc count in short mode")
+ }
+ v := ValueOf(S{})
+ if allocs := testing.AllocsPerRun(100, func() { v.Interface() }); allocs > 0 {
+ t.Error("allocs:", allocs)
+ }
+}
+
+func BenchmarkInterfaceSmall(b *testing.B) {
+ v := ValueOf(int64(0))
+ for i := 0; i < b.N; i++ {
+ v.Interface()
+ }
+}
+
+func TestAllocsInterfaceSmall(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping malloc count in short mode")
+ }
+ v := ValueOf(int64(0))
+ if allocs := testing.AllocsPerRun(100, func() { v.Interface() }); allocs > 0 {
+ t.Error("allocs:", allocs)
+ }
+}
+
// An exhaustive is a mechanism for writing exhaustive or stochastic tests.
// The basic usage is:
//
diff --git a/src/pkg/reflect/asm_386.s b/src/pkg/reflect/asm_386.s
index bbd068d98..75413c752 100644
--- a/src/pkg/reflect/asm_386.s
+++ b/src/pkg/reflect/asm_386.s
@@ -2,10 +2,13 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
// makeFuncStub is the code half of the function returned by MakeFunc.
// See the comment on the declaration of makeFuncStub in makefunc.go
// for more details.
-TEXT ·makeFuncStub(SB),7,$8
+// No argsize here, gc generates argsize info at call site.
+TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$8
MOVL DX, 0(SP)
LEAL argframe+0(FP), CX
MOVL CX, 4(SP)
@@ -15,7 +18,8 @@ TEXT ·makeFuncStub(SB),7,$8
// methodValueCall is the code half of the function returned by makeMethodValue.
// See the comment on the declaration of methodValueCall in makefunc.go
// for more details.
-TEXT ·methodValueCall(SB),7,$8
+// No argsize here, gc generates argsize info at call site.
+TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$8
MOVL DX, 0(SP)
LEAL argframe+0(FP), CX
MOVL CX, 4(SP)
diff --git a/src/pkg/reflect/asm_amd64.s b/src/pkg/reflect/asm_amd64.s
index 2e7fce55d..712959843 100644
--- a/src/pkg/reflect/asm_amd64.s
+++ b/src/pkg/reflect/asm_amd64.s
@@ -2,10 +2,13 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
// makeFuncStub is the code half of the function returned by MakeFunc.
// See the comment on the declaration of makeFuncStub in makefunc.go
// for more details.
-TEXT ·makeFuncStub(SB),7,$16
+// No argsize here, gc generates argsize info at call site.
+TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$16
MOVQ DX, 0(SP)
LEAQ argframe+0(FP), CX
MOVQ CX, 8(SP)
@@ -15,7 +18,8 @@ TEXT ·makeFuncStub(SB),7,$16
// methodValueCall is the code half of the function returned by makeMethodValue.
// See the comment on the declaration of methodValueCall in makefunc.go
// for more details.
-TEXT ·methodValueCall(SB),7,$16
+// No argsize here, gc generates argsize info at call site.
+TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$16
MOVQ DX, 0(SP)
LEAQ argframe+0(FP), CX
MOVQ CX, 8(SP)
diff --git a/src/pkg/reflect/asm_arm.s b/src/pkg/reflect/asm_arm.s
index fb1dddebe..68ded4ac6 100644
--- a/src/pkg/reflect/asm_arm.s
+++ b/src/pkg/reflect/asm_arm.s
@@ -2,10 +2,13 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
// makeFuncStub is jumped to by the code generated by MakeFunc.
// See the comment on the declaration of makeFuncStub in makefunc.go
// for more details.
-TEXT ·makeFuncStub(SB),7,$8
+// No argsize here, gc generates argsize info at call site.
+TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$8
MOVW R7, 4(R13)
MOVW $argframe+0(FP), R1
MOVW R1, 8(R13)
@@ -15,7 +18,8 @@ TEXT ·makeFuncStub(SB),7,$8
// methodValueCall is the code half of the function returned by makeMethodValue.
// See the comment on the declaration of methodValueCall in makefunc.go
// for more details.
-TEXT ·methodValueCall(SB),7,$8
+// No argsize here, gc generates argsize info at call site.
+TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$8
MOVW R7, 4(R13)
MOVW $argframe+0(FP), R1
MOVW R1, 8(R13)
diff --git a/src/pkg/reflect/deepequal.go b/src/pkg/reflect/deepequal.go
index 915afed4c..e3bf3dcac 100644
--- a/src/pkg/reflect/deepequal.go
+++ b/src/pkg/reflect/deepequal.go
@@ -9,18 +9,17 @@ package reflect
// During deepValueEqual, must keep track of checks that are
// in progress. The comparison algorithm assumes that all
// checks in progress are true when it reencounters them.
-// Visited are stored in a map indexed by 17 * a1 + a2;
+// Visited comparisons are stored in a map indexed by visit.
type visit struct {
- a1 uintptr
- a2 uintptr
- typ Type
- next *visit
+ a1 uintptr
+ a2 uintptr
+ typ Type
}
// Tests for deep equality using reflected types. The map argument tracks
// comparisons that have already been seen, which allows short circuiting on
// recursive types.
-func deepValueEqual(v1, v2 Value, visited map[uintptr]*visit, depth int) (b bool) {
+func deepValueEqual(v1, v2 Value, visited map[visit]bool, depth int) bool {
if !v1.IsValid() || !v2.IsValid() {
return v1.IsValid() == v2.IsValid()
}
@@ -29,8 +28,15 @@ func deepValueEqual(v1, v2 Value, visited map[uintptr]*visit, depth int) (b bool
}
// if depth > 10 { panic("deepValueEqual") } // for debugging
+ hard := func(k Kind) bool {
+ switch k {
+ case Array, Map, Slice, Struct:
+ return true
+ }
+ return false
+ }
- if v1.CanAddr() && v2.CanAddr() {
+ if v1.CanAddr() && v2.CanAddr() && hard(v1.Kind()) {
addr1 := v1.UnsafeAddr()
addr2 := v2.UnsafeAddr()
if addr1 > addr2 {
@@ -44,17 +50,14 @@ func deepValueEqual(v1, v2 Value, visited map[uintptr]*visit, depth int) (b bool
}
// ... or already seen
- h := 17*addr1 + addr2
- seen := visited[h]
typ := v1.Type()
- for p := seen; p != nil; p = p.next {
- if p.a1 == addr1 && p.a2 == addr2 && p.typ == typ {
- return true
- }
+ v := visit{addr1, addr2, typ}
+ if visited[v] {
+ return true
}
// Remember for later.
- visited[h] = &visit{addr1, addr2, typ, seen}
+ visited[v] = true
}
switch v1.Kind() {
@@ -75,6 +78,9 @@ func deepValueEqual(v1, v2 Value, visited map[uintptr]*visit, depth int) (b bool
if v1.Len() != v2.Len() {
return false
}
+ if v1.Pointer() == v2.Pointer() {
+ return true
+ }
for i := 0; i < v1.Len(); i++ {
if !deepValueEqual(v1.Index(i), v2.Index(i), visited, depth+1) {
return false
@@ -102,6 +108,9 @@ func deepValueEqual(v1, v2 Value, visited map[uintptr]*visit, depth int) (b bool
if v1.Len() != v2.Len() {
return false
}
+ if v1.Pointer() == v2.Pointer() {
+ return true
+ }
for _, k := range v1.MapKeys() {
if !deepValueEqual(v1.MapIndex(k), v2.MapIndex(k), visited, depth+1) {
return false
@@ -135,5 +144,5 @@ func DeepEqual(a1, a2 interface{}) bool {
if v1.Type() != v2.Type() {
return false
}
- return deepValueEqual(v1, v2, make(map[uintptr]*visit), 0)
+ return deepValueEqual(v1, v2, make(map[visit]bool), 0)
}
diff --git a/src/pkg/reflect/example_test.go b/src/pkg/reflect/example_test.go
index 62455c00a..cca28eeec 100644
--- a/src/pkg/reflect/example_test.go
+++ b/src/pkg/reflect/example_test.go
@@ -50,3 +50,17 @@ func ExampleMakeFunc() {
// 1 0
// 3.14 2.72
}
+
+func ExampleStructTag() {
+ type S struct {
+ F string `species:"gopher" color:"blue"`
+ }
+
+ s := S{}
+ st := reflect.TypeOf(s)
+ field := st.Field(0)
+ fmt.Println(field.Tag.Get("color"), field.Tag.Get("species"))
+
+ // Output:
+ // blue gopher
+}
diff --git a/src/pkg/reflect/makefunc.go b/src/pkg/reflect/makefunc.go
index ccdd683a0..e1608ea6c 100644
--- a/src/pkg/reflect/makefunc.go
+++ b/src/pkg/reflect/makefunc.go
@@ -22,7 +22,7 @@ type makeFuncImpl struct {
// that wraps the function fn. When called, that new function
// does the following:
//
-// - converts its arguments to a list of Values args.
+// - converts its arguments to a slice of Values.
// - runs results := fn(args).
// - returns the results as a slice of Values, one per formal result.
//
diff --git a/src/pkg/reflect/type.go b/src/pkg/reflect/type.go
index b513fee90..7afb7defe 100644
--- a/src/pkg/reflect/type.go
+++ b/src/pkg/reflect/type.go
@@ -188,6 +188,14 @@ type Type interface {
uncommon() *uncommonType
}
+// BUG(rsc): FieldByName and related functions consider struct field names to be equal
+// if the names are equal, even if they are unexported names originating
+// in different packages. The practical effect of this is that the result of
+// t.FieldByName("x") is not well defined if the struct type t contains
+// multiple fields named x (embedded from different packages).
+// FieldByName may return one of the fields named x or may report that there are none.
+// See golang.org/issue/4876 for more details.
+
/*
* These data structures are known to the compiler (../../cmd/gc/reflect.c).
* A few are known to ../runtime/type.go to convey to debuggers.
@@ -313,9 +321,11 @@ type interfaceType struct {
// mapType represents a map type.
type mapType struct {
- rtype `reflect:"map"`
- key *rtype // map key type
- elem *rtype // map element (value) type
+ rtype `reflect:"map"`
+ key *rtype // map key type
+ elem *rtype // map element (value) type
+ bucket *rtype // internal bucket structure
+ hmap *rtype // internal map header
}
// ptrType represents a pointer type.
@@ -354,7 +364,6 @@ const (
_GC_ARRAY_START
_GC_ARRAY_NEXT
_GC_CALL
- _GC_MAP_PTR
_GC_CHAN_PTR
_GC_STRING
_GC_EFACE
@@ -1400,11 +1409,11 @@ func cachePut(k cacheKey, t *rtype) Type {
return t
}
-// garbage collection bytecode program for chan or map.
+// garbage collection bytecode program for chan.
// See ../../cmd/gc/reflect.c:/^dgcsym1 and :/^dgcsym.
-type chanMapGC struct {
+type chanGC struct {
width uintptr // sizeof(map)
- op uintptr // _GC_MAP_PTR or _GC_CHAN_PTR
+ op uintptr // _GC_CHAN_PTR
off uintptr // 0
typ *rtype // map type
end uintptr // _GC_END
@@ -1467,7 +1476,7 @@ func ChanOf(dir ChanDir, t Type) Type {
ch.uncommonType = nil
ch.ptrToThis = nil
- ch.gc = unsafe.Pointer(&chanMapGC{
+ ch.gc = unsafe.Pointer(&chanGC{
width: ch.size,
op: _GC_CHAN_PTR,
off: 0,
@@ -1521,17 +1530,11 @@ func MapOf(key, elem Type) Type {
mt.hash = fnv1(etyp.hash, 'm', byte(ktyp.hash>>24), byte(ktyp.hash>>16), byte(ktyp.hash>>8), byte(ktyp.hash))
mt.key = ktyp
mt.elem = etyp
+ mt.bucket = bucketOf(ktyp, etyp)
+ mt.hmap = hMapOf(mt.bucket)
mt.uncommonType = nil
mt.ptrToThis = nil
- mt.gc = unsafe.Pointer(&chanMapGC{
- width: mt.size,
- op: _GC_MAP_PTR,
- off: 0,
- typ: &mt.rtype,
- end: _GC_END,
- })
-
// INCORRECT. Uncomment to check that TestMapOfGC and TestMapOfGCValues
// fail when mt.gc is wrong.
//mt.gc = unsafe.Pointer(&badGC{width: mt.size, end: _GC_END})
@@ -1539,6 +1542,117 @@ func MapOf(key, elem Type) Type {
return cachePut(ckey, &mt.rtype)
}
+// Make sure these routines stay in sync with ../../pkg/runtime/hashmap.c!
+// These types exist only for GC, so we only fill out GC relevant info.
+// Currently, that's just size and the GC program. We also fill in string
+// for possible debugging use.
+const (
+ _BUCKETSIZE = 8
+ _MAXKEYSIZE = 128
+ _MAXVALSIZE = 128
+)
+
+func bucketOf(ktyp, etyp *rtype) *rtype {
+ if ktyp.size > _MAXKEYSIZE {
+ ktyp = PtrTo(ktyp).(*rtype)
+ }
+ if etyp.size > _MAXVALSIZE {
+ etyp = PtrTo(etyp).(*rtype)
+ }
+ ptrsize := unsafe.Sizeof(uintptr(0))
+
+ gc := make([]uintptr, 1) // first entry is size, filled in at the end
+ offset := _BUCKETSIZE * unsafe.Sizeof(uint8(0)) // topbits
+ gc = append(gc, _GC_PTR, offset, 0 /*self pointer set below*/) // overflow
+ offset += ptrsize
+
+ // keys
+ if ktyp.kind&kindNoPointers == 0 {
+ gc = append(gc, _GC_ARRAY_START, offset, _BUCKETSIZE, ktyp.size)
+ gc = appendGCProgram(gc, ktyp)
+ gc = append(gc, _GC_ARRAY_NEXT)
+ }
+ offset += _BUCKETSIZE * ktyp.size
+
+ // values
+ if etyp.kind&kindNoPointers == 0 {
+ gc = append(gc, _GC_ARRAY_START, offset, _BUCKETSIZE, etyp.size)
+ gc = appendGCProgram(gc, etyp)
+ gc = append(gc, _GC_ARRAY_NEXT)
+ }
+ offset += _BUCKETSIZE * etyp.size
+
+ gc = append(gc, _GC_END)
+ gc[0] = offset
+ gc[3] = uintptr(unsafe.Pointer(&gc[0])) // set self pointer
+
+ b := new(rtype)
+ b.size = offset
+ b.gc = unsafe.Pointer(&gc[0])
+ s := "bucket(" + *ktyp.string + "," + *etyp.string + ")"
+ b.string = &s
+ return b
+}
+
+// Take the GC program for "t" and append it to the GC program "gc".
+func appendGCProgram(gc []uintptr, t *rtype) []uintptr {
+ p := t.gc
+ p = unsafe.Pointer(uintptr(p) + unsafe.Sizeof(uintptr(0))) // skip size
+loop:
+ for {
+ var argcnt int
+ switch *(*uintptr)(p) {
+ case _GC_END:
+ // Note: _GC_END not included in append
+ break loop
+ case _GC_ARRAY_NEXT:
+ argcnt = 0
+ case _GC_APTR, _GC_STRING, _GC_EFACE, _GC_IFACE:
+ argcnt = 1
+ case _GC_PTR, _GC_CALL, _GC_CHAN_PTR, _GC_SLICE:
+ argcnt = 2
+ case _GC_ARRAY_START, _GC_REGION:
+ argcnt = 3
+ default:
+ panic("unknown GC program op for " + *t.string + ": " + strconv.FormatUint(*(*uint64)(p), 10))
+ }
+ for i := 0; i < argcnt+1; i++ {
+ gc = append(gc, *(*uintptr)(p))
+ p = unsafe.Pointer(uintptr(p) + unsafe.Sizeof(uintptr(0)))
+ }
+ }
+ return gc
+}
+func hMapOf(bucket *rtype) *rtype {
+ ptrsize := unsafe.Sizeof(uintptr(0))
+
+ // make gc program & compute hmap size
+ gc := make([]uintptr, 1) // first entry is size, filled in at the end
+ offset := unsafe.Sizeof(uint(0)) // count
+ offset += unsafe.Sizeof(uint32(0)) // flags
+ offset += unsafe.Sizeof(uint32(0)) // hash0
+ offset += unsafe.Sizeof(uint8(0)) // B
+ offset += unsafe.Sizeof(uint8(0)) // keysize
+ offset += unsafe.Sizeof(uint8(0)) // valuesize
+ offset = (offset + 1) / 2 * 2
+ offset += unsafe.Sizeof(uint16(0)) // bucketsize
+ offset = (offset + ptrsize - 1) / ptrsize * ptrsize
+ gc = append(gc, _GC_PTR, offset, uintptr(bucket.gc)) // buckets
+ offset += ptrsize
+ gc = append(gc, _GC_PTR, offset, uintptr(bucket.gc)) // oldbuckets
+ offset += ptrsize
+ offset += ptrsize // nevacuate
+ gc = append(gc, _GC_END)
+ gc[0] = offset
+
+ h := new(rtype)
+ h.size = offset
+ h.gc = unsafe.Pointer(&gc[0])
+ s := "hmap(" + *bucket.string + ")"
+ h.string = &s
+ return h
+}
+
// garbage collection bytecode program for slice of non-zero-length values.
// See ../../cmd/gc/reflect.c:/^dgcsym1 and :/^dgcsym.
type sliceGC struct {
diff --git a/src/pkg/reflect/value.go b/src/pkg/reflect/value.go
index 80aa85723..df549f5e1 100644
--- a/src/pkg/reflect/value.go
+++ b/src/pkg/reflect/value.go
@@ -446,11 +446,11 @@ func (v Value) call(op string, in []Value) []Value {
// For now make everything look like a pointer by allocating
// a []unsafe.Pointer.
args := make([]unsafe.Pointer, size/ptrSize)
- ptr := uintptr(unsafe.Pointer(&args[0]))
+ ptr := unsafe.Pointer(&args[0])
off := uintptr(0)
if v.flag&flagMethod != 0 {
// Hard-wired first argument.
- *(*iword)(unsafe.Pointer(ptr)) = rcvr
+ *(*iword)(ptr) = rcvr
off = ptrSize
}
for i, v := range in {
@@ -459,7 +459,7 @@ func (v Value) call(op string, in []Value) []Value {
a := uintptr(targ.align)
off = (off + a - 1) &^ (a - 1)
n := targ.size
- addr := unsafe.Pointer(ptr + off)
+ addr := unsafe.Pointer(uintptr(ptr) + off)
v = v.assignTo("reflect.Value.Call", targ, (*interface{})(addr))
if v.flag&flagIndir == 0 {
storeIword(addr, iword(v.val), n)
@@ -471,7 +471,7 @@ func (v Value) call(op string, in []Value) []Value {
off = (off + ptrSize - 1) &^ (ptrSize - 1)
// Call.
- call(fn, unsafe.Pointer(ptr), uint32(size))
+ call(fn, ptr, uint32(size))
// Copy return values out of args.
//
@@ -482,7 +482,7 @@ func (v Value) call(op string, in []Value) []Value {
a := uintptr(tv.Align())
off = (off + a - 1) &^ (a - 1)
fl := flagIndir | flag(tv.Kind())<<flagKindShift
- ret[i] = Value{tv.common(), unsafe.Pointer(ptr + off), fl}
+ ret[i] = Value{tv.common(), unsafe.Pointer(uintptr(ptr) + off), fl}
off += tv.Size()
}
@@ -497,6 +497,10 @@ func (v Value) call(op string, in []Value) []Value {
// frame into a call using Values.
// It is in this file so that it can be next to the call method above.
// The remainder of the MakeFunc implementation is in makefunc.go.
+//
+// NOTE: This function must be marked as a "wrapper" in the generated code,
+// so that the linker can make it work correctly for panic and recover.
+// The gc compilers know to do that for the name "reflect.callReflect".
func callReflect(ctxt *makeFuncImpl, frame unsafe.Pointer) {
ftyp := ctxt.typ
f := ctxt.fn
@@ -650,6 +654,10 @@ func frameSize(t *rtype, rcvr bool) (total, in, outOffset, out uintptr) {
// to deal with individual Values for each argument.
// It is in this file so that it can be next to the two similar functions above.
// The remainder of the makeMethodValue implementation is in makefunc.go.
+//
+// NOTE: This function must be marked as a "wrapper" in the generated code,
+// so that the linker can make it work correctly for panic and recover.
+// The gc compilers know to do that for the name "reflect.callMethod".
func callMethod(ctxt *methodValue, frame unsafe.Pointer) {
t, fn, rcvr := methodReceiver("call", ctxt.rcvr, ctxt.method)
total, in, outOffset, out := frameSize(t, true)
@@ -963,10 +971,7 @@ func (v Value) CanInterface() bool {
// Interface returns v's current value as an interface{}.
// It is equivalent to:
// var i interface{} = (v's underlying value)
-// If v is a method obtained by invoking Value.Method
-// (as opposed to Type.Method), Interface cannot return an
-// interface value, so it panics.
-// It also panics if the Value was obtained by accessing
+// It panics if the Value was obtained by accessing
// unexported struct fields.
func (v Value) Interface() (i interface{}) {
return valueInterface(v, true)
@@ -1004,7 +1009,8 @@ func valueInterface(v Value, safe bool) interface{} {
eface.typ = v.typ
eface.word = v.iword()
- if v.flag&flagIndir != 0 && v.typ.size > ptrSize {
+ // Don't need to allocate if v is not addressable or fits in one word.
+ if v.flag&flagAddr != 0 && v.typ.size > ptrSize {
// eface.word is a pointer to the actual data,
// which might be changed. We need to return
// a pointer to unchanging data, so make a copy.
@@ -1475,6 +1481,19 @@ func (v Value) SetLen(n int) {
s.Len = n
}
+// SetCap sets v's capacity to n.
+// It panics if v's Kind is not Slice or if n is smaller than the length or
+// greater than the capacity of the slice.
+func (v Value) SetCap(n int) {
+ v.mustBeAssignable()
+ v.mustBe(Slice)
+ s := (*SliceHeader)(v.val)
+ if n < int(s.Len) || n > int(s.Cap) {
+ panic("reflect: slice capacity out of range in SetCap")
+ }
+ s.Cap = n
+}
+
// 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.
@@ -1531,17 +1550,18 @@ func (v Value) SetString(x string) {
*(*string)(v.val) = x
}
-// Slice returns a slice of v.
-// It panics if v's Kind is not Array, Slice or String, or if v is an unaddressable array.
-func (v Value) Slice(beg, end int) Value {
+// Slice returns v[i:j].
+// It panics if v's Kind is not Array, Slice or String, or if v is an unaddressable array,
+// or if the indexes are out of bounds.
+func (v Value) Slice(i, j int) Value {
var (
cap int
typ *sliceType
base unsafe.Pointer
)
- switch k := v.kind(); k {
+ switch kind := v.kind(); kind {
default:
- panic(&ValueError{"reflect.Value.Slice", k})
+ panic(&ValueError{"reflect.Value.Slice", kind})
case Array:
if v.flag&flagAddr == 0 {
@@ -1560,17 +1580,17 @@ func (v Value) Slice(beg, end int) Value {
case String:
s := (*StringHeader)(v.val)
- if beg < 0 || end < beg || end > s.Len {
+ if i < 0 || j < i || j > s.Len {
panic("reflect.Value.Slice: string slice index out of bounds")
}
var x string
val := (*StringHeader)(unsafe.Pointer(&x))
- val.Data = s.Data + uintptr(beg)
- val.Len = end - beg
+ val.Data = s.Data + uintptr(i)
+ val.Len = j - i
return Value{v.typ, unsafe.Pointer(&x), v.flag}
}
- if beg < 0 || end < beg || end > cap {
+ if i < 0 || j < i || j > cap {
panic("reflect.Value.Slice: slice index out of bounds")
}
@@ -1579,9 +1599,56 @@ func (v Value) Slice(beg, end int) Value {
// Reinterpret as *SliceHeader to edit.
s := (*SliceHeader)(unsafe.Pointer(&x))
- s.Data = uintptr(base) + uintptr(beg)*typ.elem.Size()
- s.Len = end - beg
- s.Cap = cap - beg
+ s.Data = uintptr(base) + uintptr(i)*typ.elem.Size()
+ s.Len = j - i
+ s.Cap = cap - i
+
+ fl := v.flag&flagRO | flagIndir | flag(Slice)<<flagKindShift
+ return Value{typ.common(), unsafe.Pointer(&x), fl}
+}
+
+// Slice3 is the 3-index form of the slice operation: it returns v[i:j:k].
+// It panics if v's Kind is not Array or Slice, or if v is an unaddressable array,
+// or if the indexes are out of bounds.
+func (v Value) Slice3(i, j, k int) Value {
+ var (
+ cap int
+ typ *sliceType
+ base unsafe.Pointer
+ )
+ switch kind := v.kind(); kind {
+ default:
+ panic(&ValueError{"reflect.Value.Slice3", kind})
+
+ case Array:
+ if v.flag&flagAddr == 0 {
+ panic("reflect.Value.Slice: slice of unaddressable array")
+ }
+ tt := (*arrayType)(unsafe.Pointer(v.typ))
+ cap = int(tt.len)
+ typ = (*sliceType)(unsafe.Pointer(tt.slice))
+ base = v.val
+
+ case Slice:
+ typ = (*sliceType)(unsafe.Pointer(v.typ))
+ s := (*SliceHeader)(v.val)
+ base = unsafe.Pointer(s.Data)
+ cap = s.Cap
+ }
+
+ if i < 0 || j < i || k < j || k > cap {
+ panic("reflect.Value.Slice3: slice index out of bounds")
+ }
+
+ // Declare slice so that the garbage collector
+ // can see the base pointer in it.
+ var x []unsafe.Pointer
+
+ // Reinterpret as *SliceHeader to edit.
+ s := (*SliceHeader)(unsafe.Pointer(&x))
+ s.Data = uintptr(base) + uintptr(i)*typ.elem.Size()
+ s.Len = j - i
+ s.Cap = k - i
fl := v.flag&flagRO | flagIndir | flag(Slice)<<flagKindShift
return Value{typ.common(), unsafe.Pointer(&x), fl}
@@ -2236,7 +2303,7 @@ func makeInt(f flag, bits uint64, t Type) Value {
// Assume ptrSize >= 4, so this must be uint64.
ptr := unsafe_New(typ)
*(*uint64)(unsafe.Pointer(ptr)) = bits
- return Value{typ, ptr, f | flag(typ.Kind())<<flagKindShift}
+ return Value{typ, ptr, f | flagIndir | flag(typ.Kind())<<flagKindShift}
}
var w iword
switch typ.size {
@@ -2260,7 +2327,7 @@ func makeFloat(f flag, v float64, t Type) Value {
// Assume ptrSize >= 4, so this must be float64.
ptr := unsafe_New(typ)
*(*float64)(unsafe.Pointer(ptr)) = v
- return Value{typ, ptr, f | flag(typ.Kind())<<flagKindShift}
+ return Value{typ, ptr, f | flagIndir | flag(typ.Kind())<<flagKindShift}
}
var w iword
@@ -2285,7 +2352,7 @@ func makeComplex(f flag, v complex128, t Type) Value {
case 16:
*(*complex128)(unsafe.Pointer(ptr)) = v
}
- return Value{typ, ptr, f | flag(typ.Kind())<<flagKindShift}
+ return Value{typ, ptr, f | flagIndir | flag(typ.Kind())<<flagKindShift}
}
// Assume ptrSize <= 8 so this must be complex64.
diff --git a/src/pkg/regexp/all_test.go b/src/pkg/regexp/all_test.go
index 9c4d64f58..e914a7ccb 100644
--- a/src/pkg/regexp/all_test.go
+++ b/src/pkg/regexp/all_test.go
@@ -308,14 +308,14 @@ func TestReplaceAllFunc(t *testing.T) {
}
actual := re.ReplaceAllStringFunc(tc.input, tc.replacement)
if actual != tc.output {
- t.Errorf("%q.ReplaceFunc(%q,%q) = %q; want %q",
- tc.pattern, tc.input, tc.replacement, actual, tc.output)
+ t.Errorf("%q.ReplaceFunc(%q,fn) = %q; want %q",
+ tc.pattern, tc.input, actual, tc.output)
}
// now try bytes
actual = string(re.ReplaceAllFunc([]byte(tc.input), func(s []byte) []byte { return []byte(tc.replacement(string(s))) }))
if actual != tc.output {
- t.Errorf("%q.ReplaceFunc(%q,%q) = %q; want %q",
- tc.pattern, tc.input, tc.replacement, actual, tc.output)
+ t.Errorf("%q.ReplaceFunc(%q,fn) = %q; want %q",
+ tc.pattern, tc.input, actual, tc.output)
}
}
}
diff --git a/src/pkg/regexp/exec2_test.go b/src/pkg/regexp/exec2_test.go
new file mode 100644
index 000000000..7b86b4115
--- /dev/null
+++ b/src/pkg/regexp/exec2_test.go
@@ -0,0 +1,20 @@
+// Copyright 2013 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.
+
+// +build !race
+
+package regexp
+
+import (
+ "testing"
+)
+
+// This test is excluded when running under the race detector because
+// it is a very expensive test and takes too long.
+func TestRE2Exhaustive(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping TestRE2Exhaustive during short test")
+ }
+ testRE2(t, "testdata/re2-exhaustive.txt.bz2")
+}
diff --git a/src/pkg/regexp/exec_test.go b/src/pkg/regexp/exec_test.go
index 9dfaed713..70d069c06 100644
--- a/src/pkg/regexp/exec_test.go
+++ b/src/pkg/regexp/exec_test.go
@@ -9,7 +9,6 @@ import (
"compress/bzip2"
"fmt"
"io"
- "math/rand"
"os"
"path/filepath"
"regexp/syntax"
@@ -67,13 +66,6 @@ func TestRE2Search(t *testing.T) {
testRE2(t, "testdata/re2-search.txt")
}
-func TestRE2Exhaustive(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping TestRE2Exhaustive during short test")
- }
- testRE2(t, "testdata/re2-exhaustive.txt.bz2")
-}
-
func testRE2(t *testing.T, file string) {
f, err := os.Open(file)
if err != nil {
@@ -650,11 +642,17 @@ func makeText(n int) []byte {
return text[:n]
}
text = make([]byte, n)
+ x := ^uint32(0)
for i := range text {
- if rand.Intn(30) == 0 {
+ x += x
+ x ^= 1
+ if int32(x) < 0 {
+ x ^= 0x88888eef
+ }
+ if x%31 == 0 {
text[i] = '\n'
} else {
- text[i] = byte(rand.Intn(0x7E+1-0x20) + 0x20)
+ text[i] = byte(x%(0x7E+1-0x20) + 0x20)
}
}
return text
@@ -691,7 +689,7 @@ func BenchmarkMatchEasy1_1K(b *testing.B) { benchmark(b, easy1, 1<<10) }
func BenchmarkMatchEasy1_32K(b *testing.B) { benchmark(b, easy1, 32<<10) }
func BenchmarkMatchEasy1_1M(b *testing.B) { benchmark(b, easy1, 1<<20) }
func BenchmarkMatchEasy1_32M(b *testing.B) { benchmark(b, easy1, 32<<20) }
-func BenchmarkMatchMedium_32(b *testing.B) { benchmark(b, medium, 1<<0) }
+func BenchmarkMatchMedium_32(b *testing.B) { benchmark(b, medium, 32<<0) }
func BenchmarkMatchMedium_1K(b *testing.B) { benchmark(b, medium, 1<<10) }
func BenchmarkMatchMedium_32K(b *testing.B) { benchmark(b, medium, 32<<10) }
func BenchmarkMatchMedium_1M(b *testing.B) { benchmark(b, medium, 1<<20) }
diff --git a/src/pkg/regexp/regexp.go b/src/pkg/regexp/regexp.go
index c392b376f..0046026ea 100644
--- a/src/pkg/regexp/regexp.go
+++ b/src/pkg/regexp/regexp.go
@@ -375,21 +375,18 @@ func (re *Regexp) LiteralPrefix() (prefix string, complete bool) {
return re.prefix, re.prefixComplete
}
-// MatchReader returns whether the Regexp matches the text read by the
-// RuneReader. The return value is a boolean: true for match, false for no
-// match.
+// MatchReader reports whether the Regexp matches the text read by the
+// RuneReader.
func (re *Regexp) MatchReader(r io.RuneReader) bool {
return re.doExecute(r, nil, "", 0, 0) != nil
}
-// MatchString returns whether the Regexp matches the string s.
-// The return value is a boolean: true for match, false for no match.
+// MatchString reports whether the Regexp matches the string s.
func (re *Regexp) MatchString(s string) bool {
return re.doExecute(nil, nil, s, 0, 0) != nil
}
-// Match returns whether the Regexp matches the byte slice b.
-// The return value is a boolean: true for match, false for no match.
+// Match reports whether the Regexp matches the byte slice b.
func (re *Regexp) Match(b []byte) bool {
return re.doExecute(nil, b, "", 0, 0) != nil
}
diff --git a/src/pkg/regexp/syntax/doc.go b/src/pkg/regexp/syntax/doc.go
index bcb5d051b..e52632ef7 100644
--- a/src/pkg/regexp/syntax/doc.go
+++ b/src/pkg/regexp/syntax/doc.go
@@ -64,8 +64,8 @@ Empty strings:
^ at beginning of text or line (flag m=true)
$ at end of text (like \z not \Z) or line (flag m=true)
\A at beginning of text
- \b at word boundary (\w on one side and \W, \A, or \z on the other)
- \B not a word boundary
+ \b at ASCII word boundary (\w on one side and \W, \A, or \z on the other)
+ \B not an ASCII word boundary
\z at end of text
Escape sequences:
@@ -104,8 +104,8 @@ Perl character classes:
\D not digits (== [^0-9])
\s whitespace (== [\t\n\f\r ])
\S not whitespace (== [^\t\n\f\r ])
- \w word characters (== [0-9A-Za-z_])
- \W not word characters (== [^0-9A-Za-z_])
+ \w ASCII word characters (== [0-9A-Za-z_])
+ \W not ASCII word characters (== [^0-9A-Za-z_])
ASCII character classes:
[:alnum:] alphanumeric (== [0-9A-Za-z])
diff --git a/src/pkg/regexp/syntax/parse.go b/src/pkg/regexp/syntax/parse.go
index 30e0e8b7f..42d0bf4a1 100644
--- a/src/pkg/regexp/syntax/parse.go
+++ b/src/pkg/regexp/syntax/parse.go
@@ -651,7 +651,7 @@ func literalRegexp(s string, flags Flags) *Regexp {
// Parse parses a regular expression string s, controlled by the specified
// Flags, and returns a regular expression parse tree. The syntax is
-// described in the top-level comment for package regexp.
+// described in the top-level comment.
func Parse(s string, flags Flags) (*Regexp, error) {
if flags&Literal != 0 {
// Trivial parser for literal string.
diff --git a/src/pkg/regexp/syntax/parse_test.go b/src/pkg/regexp/syntax/parse_test.go
index 81fd9dc01..269d6c3b8 100644
--- a/src/pkg/regexp/syntax/parse_test.go
+++ b/src/pkg/regexp/syntax/parse_test.go
@@ -542,7 +542,7 @@ func TestToStringEquivalentParse(t *testing.T) {
// but "{" is a shorter equivalent in some contexts.
nre, err := Parse(s, testFlags)
if err != nil {
- t.Errorf("Parse(%#q.String() = %#q): %v", tt.Regexp, t, err)
+ t.Errorf("Parse(%#q.String() = %#q): %v", tt.Regexp, s, err)
continue
}
nd := dump(nre)
diff --git a/src/pkg/regexp/syntax/prog.go b/src/pkg/regexp/syntax/prog.go
index 902d3b3a5..a482a82f2 100644
--- a/src/pkg/regexp/syntax/prog.go
+++ b/src/pkg/regexp/syntax/prog.go
@@ -56,23 +56,26 @@ const (
// Passing r2 == -1 indicates that the position is
// at the end of the text.
func EmptyOpContext(r1, r2 rune) EmptyOp {
- var op EmptyOp
- if r1 < 0 {
- op |= EmptyBeginText | EmptyBeginLine
- }
- if r1 == '\n' {
+ var op EmptyOp = EmptyNoWordBoundary
+ var boundary byte
+ switch {
+ case IsWordChar(r1):
+ boundary = 1
+ case r1 == '\n':
op |= EmptyBeginLine
+ case r1 < 0:
+ op |= EmptyBeginText | EmptyBeginLine
}
- if r2 < 0 {
- op |= EmptyEndText | EmptyEndLine
- }
- if r2 == '\n' {
+ switch {
+ case IsWordChar(r2):
+ boundary ^= 1
+ case r2 == '\n':
op |= EmptyEndLine
+ case r2 < 0:
+ op |= EmptyEndText | EmptyEndLine
}
- if IsWordChar(r1) != IsWordChar(r2) {
- op |= EmptyWordBoundary
- } else {
- op |= EmptyNoWordBoundary
+ if boundary != 0 { // IsWordChar(r1) != IsWordChar(r2)
+ op ^= (EmptyWordBoundary | EmptyNoWordBoundary)
}
return op
}
diff --git a/src/pkg/regexp/syntax/prog_test.go b/src/pkg/regexp/syntax/prog_test.go
index 663d5a8d7..cd71abc2a 100644
--- a/src/pkg/regexp/syntax/prog_test.go
+++ b/src/pkg/regexp/syntax/prog_test.go
@@ -103,3 +103,14 @@ func TestCompile(t *testing.T) {
}
}
}
+
+func BenchmarkEmptyOpContext(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ var r1 rune = -1
+ for _, r2 := range "foo, bar, baz\nsome input text.\n" {
+ EmptyOpContext(r1, r2)
+ r1 = r2
+ }
+ EmptyOpContext(r1, -1)
+ }
+}
diff --git a/src/pkg/runtime/alg.c b/src/pkg/runtime/alg.c
index a78d9780c..c3a839695 100644
--- a/src/pkg/runtime/alg.c
+++ b/src/pkg/runtime/alg.c
@@ -4,6 +4,7 @@
#include "runtime.h"
#include "type.h"
+#include "../../cmd/ld/textflag.h"
#define M0 (sizeof(uintptr)==4 ? 2860486313UL : 33054211828000289ULL)
#define M1 (sizeof(uintptr)==4 ? 3267000013UL : 23344194077549503ULL)
@@ -499,7 +500,7 @@ runtime·hashinit(void)
}
// func equal(t *Type, x T, y T) (ret bool)
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·equal(Type *t, ...)
{
@@ -512,3 +513,29 @@ runtime·equal(Type *t, ...)
ret = ROUND(ret, Structrnd);
t->alg->equal((bool*)ret, t->size, x, y);
}
+
+// Testing adapters for hash quality tests (see hash_test.go)
+void runtime·haveGoodHash(bool res) {
+ res = use_aeshash;
+ FLUSH(&res);
+}
+void runtime·stringHash(String s, uintptr seed, uintptr res) {
+ runtime·algarray[ASTRING].hash(&seed, sizeof(String), &s);
+ res = seed;
+ FLUSH(&res);
+}
+void runtime·bytesHash(Slice s, uintptr seed, uintptr res) {
+ runtime·algarray[AMEM].hash(&seed, s.len, s.array);
+ res = seed;
+ FLUSH(&res);
+}
+void runtime·int32Hash(uint32 i, uintptr seed, uintptr res) {
+ runtime·algarray[AMEM32].hash(&seed, sizeof(uint32), &i);
+ res = seed;
+ FLUSH(&res);
+}
+void runtime·int64Hash(uint64 i, uintptr seed, uintptr res) {
+ runtime·algarray[AMEM64].hash(&seed, sizeof(uint64), &i);
+ res = seed;
+ FLUSH(&res);
+}
diff --git a/src/pkg/runtime/append_test.go b/src/pkg/runtime/append_test.go
index 36390181e..937c8259f 100644
--- a/src/pkg/runtime/append_test.go
+++ b/src/pkg/runtime/append_test.go
@@ -38,10 +38,18 @@ func BenchmarkAppend4Bytes(b *testing.B) {
benchmarkAppendBytes(b, 4)
}
+func BenchmarkAppend7Bytes(b *testing.B) {
+ benchmarkAppendBytes(b, 7)
+}
+
func BenchmarkAppend8Bytes(b *testing.B) {
benchmarkAppendBytes(b, 8)
}
+func BenchmarkAppend15Bytes(b *testing.B) {
+ benchmarkAppendBytes(b, 15)
+}
+
func BenchmarkAppend16Bytes(b *testing.B) {
benchmarkAppendBytes(b, 16)
}
@@ -121,3 +129,43 @@ func TestAppendOverlap(t *testing.T) {
t.Errorf("overlap failed: got %q want %q", got, want)
}
}
+
+func benchmarkCopySlice(b *testing.B, l int) {
+ s := make([]byte, l)
+ buf := make([]byte, 4096)
+ var n int
+ for i := 0; i < b.N; i++ {
+ n = copy(buf, s)
+ }
+ b.SetBytes(int64(n))
+}
+
+func benchmarkCopyStr(b *testing.B, l int) {
+ s := string(make([]byte, l))
+ buf := make([]byte, 4096)
+ var n int
+ for i := 0; i < b.N; i++ {
+ n = copy(buf, s)
+ }
+ b.SetBytes(int64(n))
+}
+
+func BenchmarkCopy1Byte(b *testing.B) { benchmarkCopySlice(b, 1) }
+func BenchmarkCopy2Byte(b *testing.B) { benchmarkCopySlice(b, 2) }
+func BenchmarkCopy4Byte(b *testing.B) { benchmarkCopySlice(b, 4) }
+func BenchmarkCopy8Byte(b *testing.B) { benchmarkCopySlice(b, 8) }
+func BenchmarkCopy12Byte(b *testing.B) { benchmarkCopySlice(b, 12) }
+func BenchmarkCopy16Byte(b *testing.B) { benchmarkCopySlice(b, 16) }
+func BenchmarkCopy32Byte(b *testing.B) { benchmarkCopySlice(b, 32) }
+func BenchmarkCopy128Byte(b *testing.B) { benchmarkCopySlice(b, 128) }
+func BenchmarkCopy1024Byte(b *testing.B) { benchmarkCopySlice(b, 1024) }
+
+func BenchmarkCopy1String(b *testing.B) { benchmarkCopyStr(b, 1) }
+func BenchmarkCopy2String(b *testing.B) { benchmarkCopyStr(b, 2) }
+func BenchmarkCopy4String(b *testing.B) { benchmarkCopyStr(b, 4) }
+func BenchmarkCopy8String(b *testing.B) { benchmarkCopyStr(b, 8) }
+func BenchmarkCopy12String(b *testing.B) { benchmarkCopyStr(b, 12) }
+func BenchmarkCopy16String(b *testing.B) { benchmarkCopyStr(b, 16) }
+func BenchmarkCopy32String(b *testing.B) { benchmarkCopyStr(b, 32) }
+func BenchmarkCopy128String(b *testing.B) { benchmarkCopyStr(b, 128) }
+func BenchmarkCopy1024String(b *testing.B) { benchmarkCopyStr(b, 1024) }
diff --git a/src/pkg/runtime/arch_386.h b/src/pkg/runtime/arch_386.h
index 62ed11b40..ebdb3ff4e 100644
--- a/src/pkg/runtime/arch_386.h
+++ b/src/pkg/runtime/arch_386.h
@@ -6,5 +6,6 @@ enum {
thechar = '8',
BigEndian = 0,
CacheLineSize = 64,
- appendCrossover = 16
+ RuntimeGogoBytes = 64,
+ PCQuantum = 1
};
diff --git a/src/pkg/runtime/arch_amd64.h b/src/pkg/runtime/arch_amd64.h
index a5e43ca8d..2bddf0796 100644
--- a/src/pkg/runtime/arch_amd64.h
+++ b/src/pkg/runtime/arch_amd64.h
@@ -6,5 +6,6 @@ enum {
thechar = '6',
BigEndian = 0,
CacheLineSize = 64,
- appendCrossover = 16
+ RuntimeGogoBytes = 64,
+ PCQuantum = 1
};
diff --git a/src/pkg/runtime/arch_arm.h b/src/pkg/runtime/arch_arm.h
index 27c70c105..e5da01c60 100644
--- a/src/pkg/runtime/arch_arm.h
+++ b/src/pkg/runtime/arch_arm.h
@@ -6,5 +6,6 @@ enum {
thechar = '5',
BigEndian = 0,
CacheLineSize = 32,
- appendCrossover = 8
+ RuntimeGogoBytes = 80,
+ PCQuantum = 4
};
diff --git a/src/pkg/runtime/asm_386.s b/src/pkg/runtime/asm_386.s
index 531057ff8..5c642c0ed 100644
--- a/src/pkg/runtime/asm_386.s
+++ b/src/pkg/runtime/asm_386.s
@@ -3,8 +3,10 @@
// license that can be found in the LICENSE file.
#include "zasm_GOOS_GOARCH.h"
+#include "funcdata.h"
+#include "../../cmd/ld/textflag.h"
-TEXT _rt0_386(SB),7,$0
+TEXT _rt0_go(SB),NOSPLIT,$0
// copy arguments forward on an even stack
MOVL argc+0(FP), AX
MOVL argv+4(FP), BX
@@ -18,6 +20,7 @@ TEXT _rt0_386(SB),7,$0
MOVL $runtime·g0(SB), BP
LEAL (-64*1024+104)(SP), BX
MOVL BX, g_stackguard(BP)
+ MOVL BX, g_stackguard0(BP)
MOVL SP, g_stackbase(BP)
// find out information about the processor we're on
@@ -41,6 +44,10 @@ nocpuinfo:
MOVL BX, 4(SP)
MOVL BP, 0(SP)
CALL AX
+ // update stackguard after _cgo_init
+ MOVL $runtime·g0(SB), CX
+ MOVL g_stackguard0(CX), AX
+ MOVL AX, g_stackguard(CX)
// skip runtime·ldt0setup(SB) and tls test after _cgo_init for non-windows
CMPL runtime·iswindows(SB), $0
JEQ ok
@@ -90,7 +97,9 @@ ok:
// create a new goroutine to start program
PUSHL $runtime·main·f(SB) // entry
PUSHL $0 // arg size
+ ARGSIZE(8)
CALL runtime·newproc(SB)
+ ARGSIZE(-1)
POPL AX
POPL AX
@@ -101,13 +110,13 @@ ok:
RET
DATA runtime·main·f+0(SB)/4,$runtime·main(SB)
-GLOBL runtime·main·f(SB),8,$4
+GLOBL runtime·main·f(SB),RODATA,$4
-TEXT runtime·breakpoint(SB),7,$0
+TEXT runtime·breakpoint(SB),NOSPLIT,$0-0
INT $3
RET
-TEXT runtime·asminit(SB),7,$0
+TEXT runtime·asminit(SB),NOSPLIT,$0-0
// Linux and MinGW start the FPU in extended double precision.
// Other operating systems use double precision.
// Change to double precision to match them,
@@ -123,73 +132,45 @@ TEXT runtime·asminit(SB),7,$0
// void gosave(Gobuf*)
// save state in Gobuf; setjmp
-TEXT runtime·gosave(SB), 7, $0
+TEXT runtime·gosave(SB), NOSPLIT, $0-4
MOVL 4(SP), AX // gobuf
LEAL 4(SP), BX // caller's SP
MOVL BX, gobuf_sp(AX)
MOVL 0(SP), BX // caller's PC
MOVL BX, gobuf_pc(AX)
+ MOVL $0, gobuf_ret(AX)
+ MOVL $0, gobuf_ctxt(AX)
get_tls(CX)
MOVL g(CX), BX
MOVL BX, gobuf_g(AX)
RET
-// void gogo(Gobuf*, uintptr)
+// void gogo(Gobuf*)
// restore state from Gobuf; longjmp
-TEXT runtime·gogo(SB), 7, $0
- MOVL 8(SP), AX // return 2nd arg
+TEXT runtime·gogo(SB), NOSPLIT, $0-4
MOVL 4(SP), BX // gobuf
MOVL gobuf_g(BX), DX
MOVL 0(DX), CX // make sure g != nil
get_tls(CX)
MOVL DX, g(CX)
MOVL gobuf_sp(BX), SP // restore SP
+ MOVL gobuf_ret(BX), AX
+ MOVL gobuf_ctxt(BX), DX
+ MOVL $0, gobuf_sp(BX) // clear to help garbage collector
+ MOVL $0, gobuf_ret(BX)
+ MOVL $0, gobuf_ctxt(BX)
MOVL gobuf_pc(BX), BX
JMP BX
-// void gogocall(Gobuf*, void (*fn)(void), uintptr r0)
-// restore state from Gobuf but then call fn.
-// (call fn, returning to state in Gobuf)
-TEXT runtime·gogocall(SB), 7, $0
- MOVL 12(SP), DX // context
- MOVL 8(SP), AX // fn
- MOVL 4(SP), BX // gobuf
- MOVL gobuf_g(BX), DI
- get_tls(CX)
- MOVL DI, g(CX)
- MOVL 0(DI), CX // make sure g != nil
- MOVL gobuf_sp(BX), SP // restore SP
- MOVL gobuf_pc(BX), BX
- PUSHL BX
- JMP AX
- POPL BX // not reached
-
-// void gogocallfn(Gobuf*, FuncVal*)
-// restore state from Gobuf but then call fn.
-// (call fn, returning to state in Gobuf)
-TEXT runtime·gogocallfn(SB), 7, $0
- MOVL 8(SP), DX // fn
- MOVL 4(SP), BX // gobuf
- MOVL gobuf_g(BX), DI
- get_tls(CX)
- MOVL DI, g(CX)
- MOVL 0(DI), CX // make sure g != nil
- MOVL gobuf_sp(BX), SP // restore SP
- MOVL gobuf_pc(BX), BX
- PUSHL BX
- MOVL 0(DX), BX
- JMP BX
- 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->sched)
// to keep running g.
-TEXT runtime·mcall(SB), 7, $0
+TEXT runtime·mcall(SB), NOSPLIT, $0-4
MOVL fn+0(FP), DI
get_tls(CX)
- MOVL g(CX), AX // save state in g->gobuf
+ MOVL g(CX), AX // save state in g->sched
MOVL 0(SP), BX // caller's PC
MOVL BX, (g_sched+gobuf_pc)(AX)
LEAL 4(SP), BX // caller's SP
@@ -200,14 +181,16 @@ TEXT runtime·mcall(SB), 7, $0
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)
+ JNE 3(PC)
+ MOVL $runtime·badmcall(SB), AX
+ JMP AX
MOVL SI, g(CX) // g = m->g0
- MOVL (g_sched+gobuf_sp)(SI), SP // sp = m->g0->gobuf.sp
+ MOVL (g_sched+gobuf_sp)(SI), SP // sp = m->g0->sched.sp
PUSHL AX
CALL DI
POPL AX
- CALL runtime·badmcall2(SB)
+ MOVL $runtime·badmcall2(SB), AX
+ JMP AX
RET
/*
@@ -215,7 +198,12 @@ TEXT runtime·mcall(SB), 7, $0
*/
// Called during function prolog when more stack is needed.
-TEXT runtime·morestack(SB),7,$0
+//
+// The traceback routines see morestack on a g0 as being
+// the top of a stack (for example, morestack calling newstack
+// calling the scheduler calling newm calling gc), so we must
+// record an argument size. For that purpose, it has no arguments.
+TEXT runtime·morestack(SB),NOSPLIT,$0-0
// Cannot grow scheduler stack (m->g0).
get_tls(CX)
MOVL m(CX), BX
@@ -223,8 +211,6 @@ TEXT runtime·morestack(SB),7,$0
CMPL g(CX), SI
JNE 2(PC)
INT $3
-
- MOVL DX, m_cret(BX)
// frame size in DI
// arg size in AX
@@ -243,9 +229,13 @@ TEXT runtime·morestack(SB),7,$0
MOVL g(CX), SI
MOVL SI, (m_morebuf+gobuf_g)(BX)
- // Set m->morepc to f's PC.
- MOVL 0(SP), AX
- MOVL AX, m_morepc(BX)
+ // Set g->sched to context in f.
+ MOVL 0(SP), AX // f's PC
+ MOVL AX, (g_sched+gobuf_pc)(SI)
+ MOVL SI, (g_sched+gobuf_g)(SI)
+ LEAL 4(SP), AX // f's SP
+ MOVL AX, (g_sched+gobuf_sp)(SI)
+ MOVL DX, (g_sched+gobuf_ctxt)(SI)
// Call newstack on m->g0's stack.
MOVL m_g0(BX), BP
@@ -257,12 +247,12 @@ TEXT runtime·morestack(SB),7,$0
MOVL $0, 0x1003 // crash if newstack returns
RET
-// Called from reflection library. Mimics morestack,
+// Called from panic. Mimics morestack,
// reuses stack growth code to create a frame
// with the desired args running the desired function.
//
// func call(fn *byte, arg *byte, argsize uint32).
-TEXT reflect·call(SB), 7, $0
+TEXT runtime·newstackcall(SB), NOSPLIT, $0-12
get_tls(CX)
MOVL m(CX), BX
@@ -275,9 +265,14 @@ TEXT reflect·call(SB), 7, $0
MOVL g(CX), AX
MOVL AX, (m_morebuf+gobuf_g)(BX)
+ // Save our own state as the PC and SP to restore
+ // if this goroutine needs to be restarted.
+ MOVL $runtime·newstackcall(SB), (g_sched+gobuf_pc)(AX)
+ MOVL SP, (g_sched+gobuf_sp)(AX)
+
// Set up morestack arguments to call f on a new stack.
// We set f's frame size to 1, as a hint to newstack
- // that this is a call from reflect·call.
+ // that this is a call from runtime·newstackcall.
// If it turns out that f needs a larger frame than
// the default stack, f's usual stack growth prolog will
// allocate a new segment (and recopy the arguments).
@@ -285,7 +280,7 @@ TEXT reflect·call(SB), 7, $0
MOVL 8(SP), DX // arg frame
MOVL 12(SP), CX // arg size
- MOVL AX, m_morepc(BX) // f's PC
+ MOVL AX, m_cret(BX) // f's PC
MOVL DX, m_moreargp(BX) // f's argument pointer
MOVL CX, m_moreargsize(BX) // f's argument size
MOVL $1, m_moreframesize(BX) // f's frame size
@@ -299,9 +294,101 @@ TEXT reflect·call(SB), 7, $0
MOVL $0, 0x1103 // crash if newstack returns
RET
+// reflect·call: call a function with the given argument list
+// func call(f *FuncVal, arg *byte, argsize uint32).
+// we don't have variable-sized frames, so we use a small number
+// of constant-sized-frame functions to encode a few bits of size in the pc.
+// Caution: ugly multiline assembly macros in your future!
+
+#define DISPATCH(NAME,MAXSIZE) \
+ CMPL CX, $MAXSIZE; \
+ JA 3(PC); \
+ MOVL $runtime·NAME(SB), AX; \
+ JMP AX
+// Note: can't just "JMP runtime·NAME(SB)" - bad inlining results.
+
+TEXT reflect·call(SB), NOSPLIT, $0-12
+ MOVL argsize+8(FP), CX
+ DISPATCH(call16, 16)
+ DISPATCH(call32, 32)
+ DISPATCH(call64, 64)
+ DISPATCH(call128, 128)
+ DISPATCH(call256, 256)
+ DISPATCH(call512, 512)
+ DISPATCH(call1024, 1024)
+ DISPATCH(call2048, 2048)
+ DISPATCH(call4096, 4096)
+ DISPATCH(call8192, 8192)
+ DISPATCH(call16384, 16384)
+ DISPATCH(call32768, 32768)
+ DISPATCH(call65536, 65536)
+ DISPATCH(call131072, 131072)
+ DISPATCH(call262144, 262144)
+ DISPATCH(call524288, 524288)
+ DISPATCH(call1048576, 1048576)
+ DISPATCH(call2097152, 2097152)
+ DISPATCH(call4194304, 4194304)
+ DISPATCH(call8388608, 8388608)
+ DISPATCH(call16777216, 16777216)
+ DISPATCH(call33554432, 33554432)
+ DISPATCH(call67108864, 67108864)
+ DISPATCH(call134217728, 134217728)
+ DISPATCH(call268435456, 268435456)
+ DISPATCH(call536870912, 536870912)
+ DISPATCH(call1073741824, 1073741824)
+ MOVL $runtime·badreflectcall(SB), AX
+ JMP AX
+
+#define CALLFN(NAME,MAXSIZE) \
+TEXT runtime·NAME(SB), WRAPPER, $MAXSIZE-12; \
+ /* copy arguments to stack */ \
+ MOVL argptr+4(FP), SI; \
+ MOVL argsize+8(FP), CX; \
+ MOVL SP, DI; \
+ REP;MOVSB; \
+ /* call function */ \
+ MOVL f+0(FP), DX; \
+ CALL (DX); \
+ /* copy return values back */ \
+ MOVL argptr+4(FP), DI; \
+ MOVL argsize+8(FP), CX; \
+ MOVL SP, SI; \
+ REP;MOVSB; \
+ RET
+
+CALLFN(call16, 16)
+CALLFN(call32, 32)
+CALLFN(call64, 64)
+CALLFN(call128, 128)
+CALLFN(call256, 256)
+CALLFN(call512, 512)
+CALLFN(call1024, 1024)
+CALLFN(call2048, 2048)
+CALLFN(call4096, 4096)
+CALLFN(call8192, 8192)
+CALLFN(call16384, 16384)
+CALLFN(call32768, 32768)
+CALLFN(call65536, 65536)
+CALLFN(call131072, 131072)
+CALLFN(call262144, 262144)
+CALLFN(call524288, 524288)
+CALLFN(call1048576, 1048576)
+CALLFN(call2097152, 2097152)
+CALLFN(call4194304, 4194304)
+CALLFN(call8388608, 8388608)
+CALLFN(call16777216, 16777216)
+CALLFN(call33554432, 33554432)
+CALLFN(call67108864, 67108864)
+CALLFN(call134217728, 134217728)
+CALLFN(call268435456, 268435456)
+CALLFN(call536870912, 536870912)
+CALLFN(call1073741824, 1073741824)
// Return point when leaving stack.
-TEXT runtime·lessstack(SB), 7, $0
+//
+// Lessstack can appear in stack traces for the same reason
+// as morestack; in that context, it has 0 arguments.
+TEXT runtime·lessstack(SB), NOSPLIT, $0-0
// Save return value in m->cret
get_tls(CX)
MOVL m(CX), BX
@@ -323,7 +410,7 @@ TEXT runtime·lessstack(SB), 7, $0
// return 1;
// }else
// return 0;
-TEXT runtime·cas(SB), 7, $0
+TEXT runtime·cas(SB), NOSPLIT, $0-12
MOVL 4(SP), BX
MOVL 8(SP), AX
MOVL 12(SP), CX
@@ -335,30 +422,26 @@ TEXT runtime·cas(SB), 7, $0
MOVL $1, AX
RET
-// bool runtime·cas64(uint64 *val, uint64 *old, uint64 new)
+// bool runtime·cas64(uint64 *val, uint64 old, uint64 new)
// Atomically:
// if(*val == *old){
// *val = new;
// return 1;
// } else {
-// *old = *val
// return 0;
// }
-TEXT runtime·cas64(SB), 7, $0
+TEXT runtime·cas64(SB), NOSPLIT, $0-20
MOVL 4(SP), BP
- MOVL 8(SP), SI
- MOVL 0(SI), AX
- MOVL 4(SI), DX
- MOVL 12(SP), BX
- MOVL 16(SP), CX
+ MOVL 8(SP), AX
+ MOVL 12(SP), DX
+ MOVL 16(SP), BX
+ MOVL 20(SP), CX
LOCK
CMPXCHG8B 0(BP)
JNZ cas64_fail
MOVL $1, AX
RET
cas64_fail:
- MOVL AX, 0(SI)
- MOVL DX, 4(SI)
MOVL $0, AX
RET
@@ -369,7 +452,7 @@ cas64_fail:
// return 1;
// }else
// return 0;
-TEXT runtime·casp(SB), 7, $0
+TEXT runtime·casp(SB), NOSPLIT, $0-12
MOVL 4(SP), BX
MOVL 8(SP), AX
MOVL 12(SP), CX
@@ -385,7 +468,7 @@ TEXT runtime·casp(SB), 7, $0
// Atomically:
// *val += delta;
// return *val;
-TEXT runtime·xadd(SB), 7, $0
+TEXT runtime·xadd(SB), NOSPLIT, $0-8
MOVL 4(SP), BX
MOVL 8(SP), AX
MOVL AX, CX
@@ -394,13 +477,13 @@ TEXT runtime·xadd(SB), 7, $0
ADDL CX, AX
RET
-TEXT runtime·xchg(SB), 7, $0
+TEXT runtime·xchg(SB), NOSPLIT, $0-8
MOVL 4(SP), BX
MOVL 8(SP), AX
XCHGL AX, 0(BX)
RET
-TEXT runtime·procyield(SB),7,$0
+TEXT runtime·procyield(SB),NOSPLIT,$0-0
MOVL 4(SP), AX
again:
PAUSE
@@ -408,13 +491,13 @@ again:
JNZ again
RET
-TEXT runtime·atomicstorep(SB), 7, $0
+TEXT runtime·atomicstorep(SB), NOSPLIT, $0-8
MOVL 4(SP), BX
MOVL 8(SP), AX
XCHGL AX, 0(BX)
RET
-TEXT runtime·atomicstore(SB), 7, $0
+TEXT runtime·atomicstore(SB), NOSPLIT, $0-8
MOVL 4(SP), BX
MOVL 8(SP), AX
XCHGL AX, 0(BX)
@@ -423,8 +506,8 @@ TEXT runtime·atomicstore(SB), 7, $0
// uint64 atomicload64(uint64 volatile* addr);
// so actually
// void atomicload64(uint64 *res, uint64 volatile *addr);
-TEXT runtime·atomicload64(SB), 7, $0
- MOVL 4(SP), BX
+TEXT runtime·atomicload64(SB), NOSPLIT, $0-8
+ MOVL 4(SP), BX
MOVL 8(SP), AX
// MOVQ (%EAX), %MM0
BYTE $0x0f; BYTE $0x6f; BYTE $0x00
@@ -435,7 +518,7 @@ TEXT runtime·atomicload64(SB), 7, $0
RET
// void runtime·atomicstore64(uint64 volatile* addr, uint64 v);
-TEXT runtime·atomicstore64(SB), 7, $0
+TEXT runtime·atomicstore64(SB), NOSPLIT, $0-12
MOVL 4(SP), AX
// MOVQ and EMMS were introduced on the Pentium MMX.
// MOVQ 0x8(%ESP), %MM0
@@ -456,7 +539,7 @@ TEXT runtime·atomicstore64(SB), 7, $0
// 1. pop the caller
// 2. sub 5 bytes from the callers return
// 3. jmp to the argument
-TEXT runtime·jmpdefer(SB), 7, $0
+TEXT runtime·jmpdefer(SB), NOSPLIT, $0-8
MOVL 4(SP), DX // fn
MOVL 8(SP), BX // caller sp
LEAL -4(BX), SP // caller sp after CALL
@@ -464,18 +547,27 @@ TEXT runtime·jmpdefer(SB), 7, $0
MOVL 0(DX), BX
JMP BX // 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
+// Save state of caller into g->sched.
+TEXT gosave<>(SB),NOSPLIT,$0
+ PUSHL AX
+ PUSHL BX
+ get_tls(BX)
+ MOVL g(BX), BX
+ LEAL arg+0(FP), AX
+ MOVL AX, (g_sched+gobuf_sp)(BX)
+ MOVL -4(AX), AX
+ MOVL AX, (g_sched+gobuf_pc)(BX)
+ MOVL $0, (g_sched+gobuf_ret)(BX)
+ MOVL $0, (g_sched+gobuf_ctxt)(BX)
+ POPL BX
+ POPL AX
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
+TEXT runtime·asmcgocall(SB),NOSPLIT,$0-8
MOVL fn+0(FP), AX
MOVL arg+4(FP), BX
MOVL SP, DX
@@ -488,10 +580,8 @@ TEXT runtime·asmcgocall(SB),7,$0
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)
+ JEQ 4(PC)
+ CALL gosave<>(SB)
MOVL SI, g(CX)
MOVL (g_sched+gobuf_sp)(SI), SP
@@ -513,7 +603,7 @@ TEXT runtime·asmcgocall(SB),7,$0
// cgocallback(void (*fn)(void*), void *frame, uintptr framesize)
// Turn the fn into a Go func (by taking its address) and call
// cgocallback_gofunc.
-TEXT runtime·cgocallback(SB),7,$12
+TEXT runtime·cgocallback(SB),NOSPLIT,$12-12
LEAL fn+0(FP), AX
MOVL AX, 0(SP)
MOVL frame+4(FP), AX
@@ -526,7 +616,7 @@ TEXT runtime·cgocallback(SB),7,$12
// cgocallback_gofunc(FuncVal*, void *frame, uintptr framesize)
// See cgocall.c for more details.
-TEXT runtime·cgocallback_gofunc(SB),7,$12
+TEXT runtime·cgocallback_gofunc(SB),NOSPLIT,$12-12
// If m is nil, Go did not create the current thread.
// Call needm to obtain one for temporary use.
// In this case, we're running on the thread stack, so there's
@@ -534,18 +624,19 @@ TEXT runtime·cgocallback_gofunc(SB),7,$12
// the linker analysis by using an indirect call through AX.
get_tls(CX)
#ifdef GOOS_windows
+ MOVL $0, BP
CMPL CX, $0
- JNE 3(PC)
- PUSHL $0
- JMP needm
+ JEQ 2(PC)
#endif
MOVL m(CX), BP
- PUSHL BP
+ MOVL BP, DX // saved copy of oldm
CMPL BP, $0
JNE havem
needm:
+ MOVL DX, 0(SP)
MOVL $runtime·needm(SB), AX
CALL AX
+ MOVL 0(SP), DX
get_tls(CX)
MOVL m(CX), BP
@@ -554,50 +645,40 @@ havem:
// Save current m->g0->sched.sp on stack and then set it to SP.
// Save current sp in m->g0->sched.sp in preparation for
// switch back to m->curg stack.
+ // NOTE: unwindm knows that the saved g->sched.sp is at 0(SP).
+ // On Windows, the SEH is at 4(SP) and 8(SP).
MOVL m_g0(BP), SI
- PUSHL (g_sched+gobuf_sp)(SI)
+ MOVL (g_sched+gobuf_sp)(SI), AX
+ MOVL AX, 0(SP)
MOVL SP, (g_sched+gobuf_sp)(SI)
- // Switch to m->curg stack and call runtime.cgocallbackg
- // 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
+ // Switch to m->curg stack and call runtime.cgocallbackg.
+ // 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->sched) so we can restore it.
+ // We can restore m->curg->sched.sp easily, because calling
// runtime.cgocallbackg leaves SP unchanged upon return.
- // To save m->curg->gobuf.pc, we push it onto the stack.
+ // To save m->curg->sched.pc, we push it onto the stack.
// This has the added benefit that it looks to the traceback
// routine like cgocallbackg is going to return to that
- // PC (because we defined cgocallbackg to have
- // a frame size of 12, the same amount that we use below),
+ // PC (because the frame we allocate below has the same
+ // size as cgocallback_gofunc's frame declared above)
// so that the traceback will seamlessly trace back into
// the earlier calls.
- MOVL fn+0(FP), AX
- MOVL frame+4(FP), BX
- MOVL framesize+8(FP), DX
-
+ //
+ // In the new goroutine, 0(SP) holds the saved oldm (DX) register.
+ // 4(SP) and 8(SP) are unused.
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_sp)(SI), DI // prepare stack as DI
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
+ MOVL BP, -4(DI)
+ LEAL -(4+12)(DI), SP
+ MOVL DX, 0(SP)
CALL runtime·cgocallbackg(SB)
+ MOVL 0(SP), DX
- // Restore g->gobuf (== m->curg->gobuf) from saved values.
+ // Restore g->sched (== m->curg->sched) from saved values.
get_tls(CX)
MOVL g(CX), SI
MOVL 12(SP), BP
@@ -612,12 +693,12 @@ havem:
MOVL m_g0(BP), SI
MOVL SI, g(CX)
MOVL (g_sched+gobuf_sp)(SI), SP
- POPL (g_sched+gobuf_sp)(SI)
+ MOVL 0(SP), AX
+ MOVL AX, (g_sched+gobuf_sp)(SI)
// If the m on entry was nil, we called needm above to borrow an m
// for the duration of the call. Since the call is over, return it with dropm.
- POPL BP
- CMPL BP, $0
+ CMPL DX, $0
JNE 3(PC)
MOVL $runtime·dropm(SB), AX
CALL AX
@@ -626,7 +707,7 @@ havem:
RET
// void setmg(M*, G*); set m and g. for use by needm.
-TEXT runtime·setmg(SB), 7, $0
+TEXT runtime·setmg(SB), NOSPLIT, $0-8
#ifdef GOOS_windows
MOVL mm+0(FP), AX
CMPL AX, $0
@@ -646,7 +727,7 @@ settls:
RET
// void setmg_gcc(M*, G*); set m and g. for use by gcc
-TEXT setmg_gcc<>(SB), 7, $0
+TEXT setmg_gcc<>(SB), NOSPLIT, $0
get_tls(AX)
MOVL mm+0(FP), DX
MOVL DX, m(AX)
@@ -655,7 +736,7 @@ TEXT setmg_gcc<>(SB), 7, $0
RET
// check that SP is in range [g->stackbase, g->stackguard)
-TEXT runtime·stackcheck(SB), 7, $0
+TEXT runtime·stackcheck(SB), NOSPLIT, $0-0
get_tls(CX)
MOVL g(CX), AX
CMPL g_stackbase(AX), SP
@@ -666,7 +747,7 @@ TEXT runtime·stackcheck(SB), 7, $0
INT $3
RET
-TEXT runtime·memclr(SB),7,$0
+TEXT runtime·memclr(SB),NOSPLIT,$0-8
MOVL 4(SP), DI // arg 1 addr
MOVL 8(SP), CX // arg 2 count
MOVL CX, BX
@@ -681,31 +762,31 @@ TEXT runtime·memclr(SB),7,$0
STOSB
RET
-TEXT runtime·getcallerpc(SB),7,$0
+TEXT runtime·getcallerpc(SB),NOSPLIT,$0-4
MOVL x+0(FP),AX // addr of first arg
MOVL -4(AX),AX // get calling pc
RET
-TEXT runtime·setcallerpc(SB),7,$0
+TEXT runtime·setcallerpc(SB),NOSPLIT,$0-8
MOVL x+0(FP),AX // addr of first arg
MOVL x+4(FP), BX
MOVL BX, -4(AX) // set calling pc
RET
-TEXT runtime·getcallersp(SB), 7, $0
+TEXT runtime·getcallersp(SB), NOSPLIT, $0-4
MOVL sp+0(FP), AX
RET
// int64 runtime·cputicks(void), so really
// void runtime·cputicks(int64 *ticks)
-TEXT runtime·cputicks(SB),7,$0
+TEXT runtime·cputicks(SB),NOSPLIT,$0-4
RDTSC
MOVL ret+0(FP), DI
MOVL AX, 0(DI)
MOVL DX, 4(DI)
RET
-TEXT runtime·ldt0setup(SB),7,$16
+TEXT runtime·ldt0setup(SB),NOSPLIT,$16-0
// set up ldt 7 to point at tls0
// ldt 1 would be fine on Linux, but on OS X, 7 is as low as we can go.
// the entry number is just a hint. setldt will set up GS with what it used.
@@ -716,13 +797,13 @@ TEXT runtime·ldt0setup(SB),7,$16
CALL runtime·setldt(SB)
RET
-TEXT runtime·emptyfunc(SB),0,$0
+TEXT runtime·emptyfunc(SB),0,$0-0
RET
-TEXT runtime·abort(SB),7,$0
+TEXT runtime·abort(SB),NOSPLIT,$0-0
INT $0x3
-TEXT runtime·stackguard(SB),7,$0
+TEXT runtime·stackguard(SB),NOSPLIT,$0-8
MOVL SP, DX
MOVL DX, sp+0(FP)
get_tls(CX)
@@ -734,13 +815,13 @@ TEXT runtime·stackguard(SB),7,$0
GLOBL runtime·tls0(SB), $32
// hash function using AES hardware instructions
-TEXT runtime·aeshash(SB),7,$0
+TEXT runtime·aeshash(SB),NOSPLIT,$0-12
MOVL 4(SP), DX // ptr to hash value
MOVL 8(SP), CX // size
MOVL 12(SP), AX // ptr to data
JMP runtime·aeshashbody(SB)
-TEXT runtime·aeshashstr(SB),7,$0
+TEXT runtime·aeshashstr(SB),NOSPLIT,$0-12
MOVL 4(SP), DX // ptr to hash value
MOVL 12(SP), AX // ptr to string struct
MOVL 4(AX), CX // length of string
@@ -750,41 +831,49 @@ TEXT runtime·aeshashstr(SB),7,$0
// AX: data
// CX: length
// DX: ptr to seed input / hash output
-TEXT runtime·aeshashbody(SB),7,$0
+TEXT runtime·aeshashbody(SB),NOSPLIT,$0-12
MOVL (DX), X0 // seed to low 32 bits of xmm0
PINSRD $1, CX, X0 // size to next 32 bits of xmm0
MOVO runtime·aeskeysched+0(SB), X2
MOVO runtime·aeskeysched+16(SB), X3
+ CMPL CX, $16
+ JB aessmall
aesloop:
CMPL CX, $16
- JB aesloopend
+ JBE aesloopend
MOVOU (AX), X1
AESENC X2, X0
AESENC X1, X0
SUBL $16, CX
ADDL $16, AX
JMP aesloop
+// 1-16 bytes remaining
aesloopend:
+ // This load may overlap with the previous load above.
+ // We'll hash some bytes twice, but that's ok.
+ MOVOU -16(AX)(CX*1), X1
+ JMP partial
+// 0-15 bytes
+aessmall:
TESTL CX, CX
- JE finalize // no partial block
+ JE finalize // 0 bytes
- TESTL $16, AX
- JNE highpartial
+ CMPB AX, $0xf0
+ JA highpartial
- // address ends in 0xxxx. 16 bytes loaded
- // at this address won't cross a page boundary, so
- // we can load it directly.
+ // 16 bytes loaded at this address won't cross
+ // a page boundary, so we can load it directly.
MOVOU (AX), X1
ADDL CX, CX
- PAND masks(SB)(CX*8), X1
+ PAND masks<>(SB)(CX*8), X1
JMP partial
highpartial:
- // address ends in 1xxxx. Might be up against
+ // address ends in 1111xxxx. Might be up against
// a page boundary, so load ending at last byte.
// Then shift bytes down using pshufb.
MOVOU -16(AX)(CX*1), X1
ADDL CX, CX
- PSHUFB shifts(SB)(CX*8), X1
+ PSHUFB shifts<>(SB)(CX*8), X1
partial:
// incorporate partial block into hash
AESENC X3, X0
@@ -797,7 +886,7 @@ finalize:
MOVL X0, (DX)
RET
-TEXT runtime·aeshash32(SB),7,$0
+TEXT runtime·aeshash32(SB),NOSPLIT,$0-12
MOVL 4(SP), DX // ptr to hash value
MOVL 12(SP), AX // ptr to data
MOVL (DX), X0 // seed
@@ -808,7 +897,7 @@ TEXT runtime·aeshash32(SB),7,$0
MOVL X0, (DX)
RET
-TEXT runtime·aeshash64(SB),7,$0
+TEXT runtime·aeshash64(SB),NOSPLIT,$0-12
MOVL 4(SP), DX // ptr to hash value
MOVL 12(SP), AX // ptr to data
MOVQ (AX), X0 // data
@@ -819,181 +908,181 @@ TEXT runtime·aeshash64(SB),7,$0
MOVL X0, (DX)
RET
-
// simple mask to get rid of data in the high part of the register.
-TEXT masks(SB),7,$0
- LONG $0x00000000
- LONG $0x00000000
- LONG $0x00000000
- LONG $0x00000000
+DATA masks<>+0x00(SB)/4, $0x00000000
+DATA masks<>+0x04(SB)/4, $0x00000000
+DATA masks<>+0x08(SB)/4, $0x00000000
+DATA masks<>+0x0c(SB)/4, $0x00000000
- LONG $0x000000ff
- LONG $0x00000000
- LONG $0x00000000
- LONG $0x00000000
+DATA masks<>+0x10(SB)/4, $0x000000ff
+DATA masks<>+0x14(SB)/4, $0x00000000
+DATA masks<>+0x18(SB)/4, $0x00000000
+DATA masks<>+0x1c(SB)/4, $0x00000000
- LONG $0x0000ffff
- LONG $0x00000000
- LONG $0x00000000
- LONG $0x00000000
+DATA masks<>+0x20(SB)/4, $0x0000ffff
+DATA masks<>+0x24(SB)/4, $0x00000000
+DATA masks<>+0x28(SB)/4, $0x00000000
+DATA masks<>+0x2c(SB)/4, $0x00000000
- LONG $0x00ffffff
- LONG $0x00000000
- LONG $0x00000000
- LONG $0x00000000
+DATA masks<>+0x30(SB)/4, $0x00ffffff
+DATA masks<>+0x34(SB)/4, $0x00000000
+DATA masks<>+0x38(SB)/4, $0x00000000
+DATA masks<>+0x3c(SB)/4, $0x00000000
- LONG $0xffffffff
- LONG $0x00000000
- LONG $0x00000000
- LONG $0x00000000
+DATA masks<>+0x40(SB)/4, $0xffffffff
+DATA masks<>+0x44(SB)/4, $0x00000000
+DATA masks<>+0x48(SB)/4, $0x00000000
+DATA masks<>+0x4c(SB)/4, $0x00000000
- LONG $0xffffffff
- LONG $0x000000ff
- LONG $0x00000000
- LONG $0x00000000
+DATA masks<>+0x50(SB)/4, $0xffffffff
+DATA masks<>+0x54(SB)/4, $0x000000ff
+DATA masks<>+0x58(SB)/4, $0x00000000
+DATA masks<>+0x5c(SB)/4, $0x00000000
- LONG $0xffffffff
- LONG $0x0000ffff
- LONG $0x00000000
- LONG $0x00000000
+DATA masks<>+0x60(SB)/4, $0xffffffff
+DATA masks<>+0x64(SB)/4, $0x0000ffff
+DATA masks<>+0x68(SB)/4, $0x00000000
+DATA masks<>+0x6c(SB)/4, $0x00000000
- LONG $0xffffffff
- LONG $0x00ffffff
- LONG $0x00000000
- LONG $0x00000000
+DATA masks<>+0x70(SB)/4, $0xffffffff
+DATA masks<>+0x74(SB)/4, $0x00ffffff
+DATA masks<>+0x78(SB)/4, $0x00000000
+DATA masks<>+0x7c(SB)/4, $0x00000000
- LONG $0xffffffff
- LONG $0xffffffff
- LONG $0x00000000
- LONG $0x00000000
+DATA masks<>+0x80(SB)/4, $0xffffffff
+DATA masks<>+0x84(SB)/4, $0xffffffff
+DATA masks<>+0x88(SB)/4, $0x00000000
+DATA masks<>+0x8c(SB)/4, $0x00000000
- LONG $0xffffffff
- LONG $0xffffffff
- LONG $0x000000ff
- LONG $0x00000000
+DATA masks<>+0x90(SB)/4, $0xffffffff
+DATA masks<>+0x94(SB)/4, $0xffffffff
+DATA masks<>+0x98(SB)/4, $0x000000ff
+DATA masks<>+0x9c(SB)/4, $0x00000000
- LONG $0xffffffff
- LONG $0xffffffff
- LONG $0x0000ffff
- LONG $0x00000000
+DATA masks<>+0xa0(SB)/4, $0xffffffff
+DATA masks<>+0xa4(SB)/4, $0xffffffff
+DATA masks<>+0xa8(SB)/4, $0x0000ffff
+DATA masks<>+0xac(SB)/4, $0x00000000
- LONG $0xffffffff
- LONG $0xffffffff
- LONG $0x00ffffff
- LONG $0x00000000
+DATA masks<>+0xb0(SB)/4, $0xffffffff
+DATA masks<>+0xb4(SB)/4, $0xffffffff
+DATA masks<>+0xb8(SB)/4, $0x00ffffff
+DATA masks<>+0xbc(SB)/4, $0x00000000
- LONG $0xffffffff
- LONG $0xffffffff
- LONG $0xffffffff
- LONG $0x00000000
+DATA masks<>+0xc0(SB)/4, $0xffffffff
+DATA masks<>+0xc4(SB)/4, $0xffffffff
+DATA masks<>+0xc8(SB)/4, $0xffffffff
+DATA masks<>+0xcc(SB)/4, $0x00000000
- LONG $0xffffffff
- LONG $0xffffffff
- LONG $0xffffffff
- LONG $0x000000ff
+DATA masks<>+0xd0(SB)/4, $0xffffffff
+DATA masks<>+0xd4(SB)/4, $0xffffffff
+DATA masks<>+0xd8(SB)/4, $0xffffffff
+DATA masks<>+0xdc(SB)/4, $0x000000ff
- LONG $0xffffffff
- LONG $0xffffffff
- LONG $0xffffffff
- LONG $0x0000ffff
+DATA masks<>+0xe0(SB)/4, $0xffffffff
+DATA masks<>+0xe4(SB)/4, $0xffffffff
+DATA masks<>+0xe8(SB)/4, $0xffffffff
+DATA masks<>+0xec(SB)/4, $0x0000ffff
- LONG $0xffffffff
- LONG $0xffffffff
- LONG $0xffffffff
- LONG $0x00ffffff
-
- // these are arguments to pshufb. They move data down from
- // the high bytes of the register to the low bytes of the register.
- // index is how many bytes to move.
-TEXT shifts(SB),7,$0
- LONG $0x00000000
- LONG $0x00000000
- LONG $0x00000000
- LONG $0x00000000
+DATA masks<>+0xf0(SB)/4, $0xffffffff
+DATA masks<>+0xf4(SB)/4, $0xffffffff
+DATA masks<>+0xf8(SB)/4, $0xffffffff
+DATA masks<>+0xfc(SB)/4, $0x00ffffff
+
+GLOBL masks<>(SB),RODATA,$256
+
+// these are arguments to pshufb. They move data down from
+// the high bytes of the register to the low bytes of the register.
+// index is how many bytes to move.
+DATA shifts<>+0x00(SB)/4, $0x00000000
+DATA shifts<>+0x04(SB)/4, $0x00000000
+DATA shifts<>+0x08(SB)/4, $0x00000000
+DATA shifts<>+0x0c(SB)/4, $0x00000000
- LONG $0xffffff0f
- LONG $0xffffffff
- LONG $0xffffffff
- LONG $0xffffffff
+DATA shifts<>+0x10(SB)/4, $0xffffff0f
+DATA shifts<>+0x14(SB)/4, $0xffffffff
+DATA shifts<>+0x18(SB)/4, $0xffffffff
+DATA shifts<>+0x1c(SB)/4, $0xffffffff
- LONG $0xffff0f0e
- LONG $0xffffffff
- LONG $0xffffffff
- LONG $0xffffffff
+DATA shifts<>+0x20(SB)/4, $0xffff0f0e
+DATA shifts<>+0x24(SB)/4, $0xffffffff
+DATA shifts<>+0x28(SB)/4, $0xffffffff
+DATA shifts<>+0x2c(SB)/4, $0xffffffff
- LONG $0xff0f0e0d
- LONG $0xffffffff
- LONG $0xffffffff
- LONG $0xffffffff
+DATA shifts<>+0x30(SB)/4, $0xff0f0e0d
+DATA shifts<>+0x34(SB)/4, $0xffffffff
+DATA shifts<>+0x38(SB)/4, $0xffffffff
+DATA shifts<>+0x3c(SB)/4, $0xffffffff
- LONG $0x0f0e0d0c
- LONG $0xffffffff
- LONG $0xffffffff
- LONG $0xffffffff
+DATA shifts<>+0x40(SB)/4, $0x0f0e0d0c
+DATA shifts<>+0x44(SB)/4, $0xffffffff
+DATA shifts<>+0x48(SB)/4, $0xffffffff
+DATA shifts<>+0x4c(SB)/4, $0xffffffff
- LONG $0x0e0d0c0b
- LONG $0xffffff0f
- LONG $0xffffffff
- LONG $0xffffffff
+DATA shifts<>+0x50(SB)/4, $0x0e0d0c0b
+DATA shifts<>+0x54(SB)/4, $0xffffff0f
+DATA shifts<>+0x58(SB)/4, $0xffffffff
+DATA shifts<>+0x5c(SB)/4, $0xffffffff
- LONG $0x0d0c0b0a
- LONG $0xffff0f0e
- LONG $0xffffffff
- LONG $0xffffffff
+DATA shifts<>+0x60(SB)/4, $0x0d0c0b0a
+DATA shifts<>+0x64(SB)/4, $0xffff0f0e
+DATA shifts<>+0x68(SB)/4, $0xffffffff
+DATA shifts<>+0x6c(SB)/4, $0xffffffff
- LONG $0x0c0b0a09
- LONG $0xff0f0e0d
- LONG $0xffffffff
- LONG $0xffffffff
+DATA shifts<>+0x70(SB)/4, $0x0c0b0a09
+DATA shifts<>+0x74(SB)/4, $0xff0f0e0d
+DATA shifts<>+0x78(SB)/4, $0xffffffff
+DATA shifts<>+0x7c(SB)/4, $0xffffffff
- LONG $0x0b0a0908
- LONG $0x0f0e0d0c
- LONG $0xffffffff
- LONG $0xffffffff
+DATA shifts<>+0x80(SB)/4, $0x0b0a0908
+DATA shifts<>+0x84(SB)/4, $0x0f0e0d0c
+DATA shifts<>+0x88(SB)/4, $0xffffffff
+DATA shifts<>+0x8c(SB)/4, $0xffffffff
- LONG $0x0a090807
- LONG $0x0e0d0c0b
- LONG $0xffffff0f
- LONG $0xffffffff
+DATA shifts<>+0x90(SB)/4, $0x0a090807
+DATA shifts<>+0x94(SB)/4, $0x0e0d0c0b
+DATA shifts<>+0x98(SB)/4, $0xffffff0f
+DATA shifts<>+0x9c(SB)/4, $0xffffffff
- LONG $0x09080706
- LONG $0x0d0c0b0a
- LONG $0xffff0f0e
- LONG $0xffffffff
+DATA shifts<>+0xa0(SB)/4, $0x09080706
+DATA shifts<>+0xa4(SB)/4, $0x0d0c0b0a
+DATA shifts<>+0xa8(SB)/4, $0xffff0f0e
+DATA shifts<>+0xac(SB)/4, $0xffffffff
- LONG $0x08070605
- LONG $0x0c0b0a09
- LONG $0xff0f0e0d
- LONG $0xffffffff
+DATA shifts<>+0xb0(SB)/4, $0x08070605
+DATA shifts<>+0xb4(SB)/4, $0x0c0b0a09
+DATA shifts<>+0xb8(SB)/4, $0xff0f0e0d
+DATA shifts<>+0xbc(SB)/4, $0xffffffff
- LONG $0x07060504
- LONG $0x0b0a0908
- LONG $0x0f0e0d0c
- LONG $0xffffffff
+DATA shifts<>+0xc0(SB)/4, $0x07060504
+DATA shifts<>+0xc4(SB)/4, $0x0b0a0908
+DATA shifts<>+0xc8(SB)/4, $0x0f0e0d0c
+DATA shifts<>+0xcc(SB)/4, $0xffffffff
- LONG $0x06050403
- LONG $0x0a090807
- LONG $0x0e0d0c0b
- LONG $0xffffff0f
+DATA shifts<>+0xd0(SB)/4, $0x06050403
+DATA shifts<>+0xd4(SB)/4, $0x0a090807
+DATA shifts<>+0xd8(SB)/4, $0x0e0d0c0b
+DATA shifts<>+0xdc(SB)/4, $0xffffff0f
- LONG $0x05040302
- LONG $0x09080706
- LONG $0x0d0c0b0a
- LONG $0xffff0f0e
+DATA shifts<>+0xe0(SB)/4, $0x05040302
+DATA shifts<>+0xe4(SB)/4, $0x09080706
+DATA shifts<>+0xe8(SB)/4, $0x0d0c0b0a
+DATA shifts<>+0xec(SB)/4, $0xffff0f0e
- LONG $0x04030201
- LONG $0x08070605
- LONG $0x0c0b0a09
- LONG $0xff0f0e0d
+DATA shifts<>+0xf0(SB)/4, $0x04030201
+DATA shifts<>+0xf4(SB)/4, $0x08070605
+DATA shifts<>+0xf8(SB)/4, $0x0c0b0a09
+DATA shifts<>+0xfc(SB)/4, $0xff0f0e0d
+
+GLOBL shifts<>(SB),RODATA,$256
-TEXT runtime·memeq(SB),7,$0
+TEXT runtime·memeq(SB),NOSPLIT,$0-12
MOVL a+0(FP), SI
MOVL b+4(FP), DI
MOVL count+8(FP), BX
JMP runtime·memeqbody(SB)
-
-TEXT bytes·Equal(SB),7,$0
+TEXT bytes·Equal(SB),NOSPLIT,$0-25
MOVL a_len+4(FP), BX
MOVL b_len+16(FP), CX
XORL AX, AX
@@ -1009,7 +1098,7 @@ eqret:
// a in SI
// b in DI
// count in BX
-TEXT runtime·memeqbody(SB),7,$0
+TEXT runtime·memeqbody(SB),NOSPLIT,$0-0
XORL AX, AX
CMPL BX, $4
@@ -1101,3 +1190,166 @@ di_finish:
equal:
SETEQ AX
RET
+
+TEXT runtime·cmpstring(SB),NOSPLIT,$0-20
+ MOVL s1+0(FP), SI
+ MOVL s1+4(FP), BX
+ MOVL s2+8(FP), DI
+ MOVL s2+12(FP), DX
+ CALL runtime·cmpbody(SB)
+ MOVL AX, res+16(FP)
+ RET
+
+TEXT bytes·Compare(SB),NOSPLIT,$0-28
+ MOVL s1+0(FP), SI
+ MOVL s1+4(FP), BX
+ MOVL s2+12(FP), DI
+ MOVL s2+16(FP), DX
+ CALL runtime·cmpbody(SB)
+ MOVL AX, res+24(FP)
+ RET
+
+TEXT bytes·IndexByte(SB),NOSPLIT,$0
+ MOVL s+0(FP), SI
+ MOVL s_len+4(FP), CX
+ MOVB c+12(FP), AL
+ MOVL SI, DI
+ CLD; REPN; SCASB
+ JZ 3(PC)
+ MOVL $-1, ret+16(FP)
+ RET
+ SUBL SI, DI
+ SUBL $1, DI
+ MOVL DI, ret+16(FP)
+ RET
+
+TEXT strings·IndexByte(SB),NOSPLIT,$0
+ MOVL s+0(FP), SI
+ MOVL s_len+4(FP), CX
+ MOVB c+8(FP), AL
+ MOVL SI, DI
+ CLD; REPN; SCASB
+ JZ 3(PC)
+ MOVL $-1, ret+12(FP)
+ RET
+ SUBL SI, DI
+ SUBL $1, DI
+ MOVL DI, ret+12(FP)
+ RET
+
+// input:
+// SI = a
+// DI = b
+// BX = alen
+// DX = blen
+// output:
+// AX = 1/0/-1
+TEXT runtime·cmpbody(SB),NOSPLIT,$0-0
+ CMPL SI, DI
+ JEQ cmp_allsame
+ CMPL BX, DX
+ MOVL DX, BP
+ CMOVLLT BX, BP // BP = min(alen, blen)
+ CMPL BP, $4
+ JB cmp_small
+ TESTL $0x4000000, runtime·cpuid_edx(SB) // check for sse2
+ JE cmp_mediumloop
+cmp_largeloop:
+ CMPL BP, $16
+ JB cmp_mediumloop
+ MOVOU (SI), X0
+ MOVOU (DI), X1
+ PCMPEQB X0, X1
+ PMOVMSKB X1, AX
+ XORL $0xffff, AX // convert EQ to NE
+ JNE cmp_diff16 // branch if at least one byte is not equal
+ ADDL $16, SI
+ ADDL $16, DI
+ SUBL $16, BP
+ JMP cmp_largeloop
+
+cmp_diff16:
+ BSFL AX, BX // index of first byte that differs
+ XORL AX, AX
+ MOVB (SI)(BX*1), CX
+ CMPB CX, (DI)(BX*1)
+ SETHI AX
+ LEAL -1(AX*2), AX // convert 1/0 to +1/-1
+ RET
+
+cmp_mediumloop:
+ CMPL BP, $4
+ JBE cmp_0through4
+ MOVL (SI), AX
+ MOVL (DI), CX
+ CMPL AX, CX
+ JNE cmp_diff4
+ ADDL $4, SI
+ ADDL $4, DI
+ SUBL $4, BP
+ JMP cmp_mediumloop
+
+cmp_0through4:
+ MOVL -4(SI)(BP*1), AX
+ MOVL -4(DI)(BP*1), CX
+ CMPL AX, CX
+ JEQ cmp_allsame
+
+cmp_diff4:
+ BSWAPL AX // reverse order of bytes
+ BSWAPL CX
+ XORL AX, CX // find bit differences
+ BSRL CX, CX // index of highest bit difference
+ SHRL CX, AX // move a's bit to bottom
+ ANDL $1, AX // mask bit
+ LEAL -1(AX*2), AX // 1/0 => +1/-1
+ RET
+
+ // 0-3 bytes in common
+cmp_small:
+ LEAL (BP*8), CX
+ NEGL CX
+ JEQ cmp_allsame
+
+ // load si
+ CMPB SI, $0xfc
+ JA cmp_si_high
+ MOVL (SI), SI
+ JMP cmp_si_finish
+cmp_si_high:
+ MOVL -4(SI)(BP*1), SI
+ SHRL CX, SI
+cmp_si_finish:
+ SHLL CX, SI
+
+ // same for di
+ CMPB DI, $0xfc
+ JA cmp_di_high
+ MOVL (DI), DI
+ JMP cmp_di_finish
+cmp_di_high:
+ MOVL -4(DI)(BP*1), DI
+ SHRL CX, DI
+cmp_di_finish:
+ SHLL CX, DI
+
+ BSWAPL SI // reverse order of bytes
+ BSWAPL DI
+ XORL SI, DI // find bit differences
+ JEQ cmp_allsame
+ BSRL DI, CX // index of highest bit difference
+ SHRL CX, SI // move a's bit to bottom
+ ANDL $1, SI // mask bit
+ LEAL -1(SI*2), AX // 1/0 => +1/-1
+ RET
+
+ // all the bytes in common are the same, so we just need
+ // to compare the lengths.
+cmp_allsame:
+ XORL AX, AX
+ XORL CX, CX
+ CMPL BX, DX
+ SETGT AX // 1 if alen > blen
+ SETEQ CX // 1 if alen == blen
+ LEAL -1(CX)(AX*2), AX // 1,0,-1 result
+ RET
diff --git a/src/pkg/runtime/asm_amd64.s b/src/pkg/runtime/asm_amd64.s
index 0dee1556d..2c2ffedd1 100644
--- a/src/pkg/runtime/asm_amd64.s
+++ b/src/pkg/runtime/asm_amd64.s
@@ -3,8 +3,10 @@
// license that can be found in the LICENSE file.
#include "zasm_GOOS_GOARCH.h"
+#include "funcdata.h"
+#include "../../cmd/ld/textflag.h"
-TEXT _rt0_amd64(SB),7,$-8
+TEXT _rt0_go(SB),NOSPLIT,$0
// copy arguments forward on an even stack
MOVQ DI, AX // argc
MOVQ SI, BX // argv
@@ -18,6 +20,7 @@ TEXT _rt0_amd64(SB),7,$-8
MOVQ $runtime·g0(SB), DI
LEAQ (-64*1024+104)(SP), BX
MOVQ BX, g_stackguard(DI)
+ MOVQ BX, g_stackguard0(DI)
MOVQ SP, g_stackbase(DI)
// find out information about the processor we're on
@@ -39,6 +42,10 @@ nocpuinfo:
MOVQ DI, CX // Win64 uses CX for first parameter
MOVQ $setmg_gcc<>(SB), SI
CALL AX
+ // update stackguard after _cgo_init
+ MOVQ $runtime·g0(SB), CX
+ MOVQ g_stackguard0(CX), AX
+ MOVQ AX, g_stackguard(CX)
CMPL runtime·iswindows(SB), $0
JEQ ok
@@ -83,7 +90,9 @@ ok:
// create a new goroutine to start program
PUSHQ $runtime·main·f(SB) // entry
PUSHQ $0 // arg size
+ ARGSIZE(16)
CALL runtime·newproc(SB)
+ ARGSIZE(-1)
POPQ AX
POPQ AX
@@ -94,13 +103,13 @@ ok:
RET
DATA runtime·main·f+0(SB)/8,$runtime·main(SB)
-GLOBL runtime·main·f(SB),8,$8
+GLOBL runtime·main·f(SB),RODATA,$8
-TEXT runtime·breakpoint(SB),7,$0
+TEXT runtime·breakpoint(SB),NOSPLIT,$0-0
BYTE $0xcc
RET
-TEXT runtime·asminit(SB),7,$0
+TEXT runtime·asminit(SB),NOSPLIT,$0-0
// No per-thread init.
RET
@@ -110,73 +119,45 @@ TEXT runtime·asminit(SB),7,$0
// void gosave(Gobuf*)
// save state in Gobuf; setjmp
-TEXT runtime·gosave(SB), 7, $0
+TEXT runtime·gosave(SB), NOSPLIT, $0-8
MOVQ 8(SP), AX // gobuf
LEAQ 8(SP), BX // caller's SP
MOVQ BX, gobuf_sp(AX)
MOVQ 0(SP), BX // caller's PC
MOVQ BX, gobuf_pc(AX)
+ MOVQ $0, gobuf_ret(AX)
+ MOVQ $0, gobuf_ctxt(AX)
get_tls(CX)
MOVQ g(CX), BX
MOVQ BX, gobuf_g(AX)
RET
-// void gogo(Gobuf*, uintptr)
+// void gogo(Gobuf*)
// restore state from Gobuf; longjmp
-TEXT runtime·gogo(SB), 7, $0
- MOVQ 16(SP), AX // return 2nd arg
+TEXT runtime·gogo(SB), NOSPLIT, $0-8
MOVQ 8(SP), BX // gobuf
MOVQ gobuf_g(BX), DX
MOVQ 0(DX), CX // make sure g != nil
get_tls(CX)
MOVQ DX, g(CX)
MOVQ gobuf_sp(BX), SP // restore SP
+ MOVQ gobuf_ret(BX), AX
+ MOVQ gobuf_ctxt(BX), DX
+ MOVQ $0, gobuf_sp(BX) // clear to help garbage collector
+ MOVQ $0, gobuf_ret(BX)
+ MOVQ $0, gobuf_ctxt(BX)
MOVQ gobuf_pc(BX), BX
JMP BX
-// void gogocall(Gobuf*, void (*fn)(void), uintptr r0)
-// restore state from Gobuf but then call fn.
-// (call fn, returning to state in Gobuf)
-TEXT runtime·gogocall(SB), 7, $0
- MOVQ 24(SP), DX // context
- MOVQ 16(SP), AX // fn
- MOVQ 8(SP), BX // gobuf
- MOVQ gobuf_g(BX), DI
- get_tls(CX)
- MOVQ DI, g(CX)
- MOVQ 0(DI), CX // make sure g != nil
- MOVQ gobuf_sp(BX), SP // restore SP
- MOVQ gobuf_pc(BX), BX
- PUSHQ BX
- JMP AX
- POPQ BX // not reached
-
-// void gogocallfn(Gobuf*, FuncVal*)
-// restore state from Gobuf but then call fn.
-// (call fn, returning to state in Gobuf)
-TEXT runtime·gogocallfn(SB), 7, $0
- MOVQ 16(SP), DX // fn
- MOVQ 8(SP), BX // gobuf
- MOVQ gobuf_g(BX), AX
- get_tls(CX)
- MOVQ AX, g(CX)
- MOVQ 0(AX), CX // make sure g != nil
- MOVQ gobuf_sp(BX), SP // restore SP
- MOVQ gobuf_pc(BX), BX
- PUSHQ BX
- MOVQ 0(DX), BX
- JMP BX
- 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->sched)
// to keep running g.
-TEXT runtime·mcall(SB), 7, $0
+TEXT runtime·mcall(SB), NOSPLIT, $0-8
MOVQ fn+0(FP), DI
get_tls(CX)
- MOVQ g(CX), AX // save state in g->gobuf
+ MOVQ g(CX), AX // save state in g->sched
MOVQ 0(SP), BX // caller's PC
MOVQ BX, (g_sched+gobuf_pc)(AX)
LEAQ 8(SP), BX // caller's SP
@@ -187,14 +168,17 @@ TEXT runtime·mcall(SB), 7, $0
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)
+ JNE 3(PC)
+ MOVQ $runtime·badmcall(SB), AX
+ JMP AX
MOVQ SI, g(CX) // g = m->g0
- MOVQ (g_sched+gobuf_sp)(SI), SP // sp = m->g0->gobuf.sp
+ MOVQ (g_sched+gobuf_sp)(SI), SP // sp = m->g0->sched.sp
PUSHQ AX
+ ARGSIZE(8)
CALL DI
POPQ AX
- CALL runtime·badmcall2(SB)
+ MOVQ $runtime·badmcall2(SB), AX
+ JMP AX
RET
/*
@@ -203,14 +187,17 @@ TEXT runtime·mcall(SB), 7, $0
// Called during function prolog when more stack is needed.
// Caller has already done get_tls(CX); MOVQ m(CX), BX.
-TEXT runtime·morestack(SB),7,$0
+//
+// The traceback routines see morestack on a g0 as being
+// the top of a stack (for example, morestack calling newstack
+// calling the scheduler calling newm calling gc), so we must
+// record an argument size. For that purpose, it has no arguments.
+TEXT runtime·morestack(SB),NOSPLIT,$0-0
// Cannot grow scheduler stack (m->g0).
MOVQ m_g0(BX), SI
CMPQ g(CX), SI
JNE 2(PC)
INT $3
-
- MOVQ DX, m_cret(BX)
// Called from f.
// Set m->morebuf to f's caller.
@@ -223,9 +210,13 @@ TEXT runtime·morestack(SB),7,$0
MOVQ g(CX), SI
MOVQ SI, (m_morebuf+gobuf_g)(BX)
- // Set m->morepc to f's PC.
- MOVQ 0(SP), AX
- MOVQ AX, m_morepc(BX)
+ // Set g->sched to context in f.
+ MOVQ 0(SP), AX // f's PC
+ MOVQ AX, (g_sched+gobuf_pc)(SI)
+ MOVQ SI, (g_sched+gobuf_g)(SI)
+ LEAQ 8(SP), AX // f's SP
+ MOVQ AX, (g_sched+gobuf_sp)(SI)
+ MOVQ DX, (g_sched+gobuf_ctxt)(SI)
// Call newstack on m->g0's stack.
MOVQ m_g0(BX), BP
@@ -235,12 +226,12 @@ TEXT runtime·morestack(SB),7,$0
MOVQ $0, 0x1003 // crash if newstack returns
RET
-// Called from reflection library. Mimics morestack,
+// Called from panic. Mimics morestack,
// reuses stack growth code to create a frame
// with the desired args running the desired function.
//
// func call(fn *byte, arg *byte, argsize uint32).
-TEXT reflect·call(SB), 7, $0
+TEXT runtime·newstackcall(SB), NOSPLIT, $0-20
get_tls(CX)
MOVQ m(CX), BX
@@ -252,10 +243,15 @@ TEXT reflect·call(SB), 7, $0
MOVQ AX, (m_morebuf+gobuf_sp)(BX)
MOVQ g(CX), AX
MOVQ AX, (m_morebuf+gobuf_g)(BX)
+
+ // Save our own state as the PC and SP to restore
+ // if this goroutine needs to be restarted.
+ MOVQ $runtime·newstackcall(SB), (g_sched+gobuf_pc)(AX)
+ MOVQ SP, (g_sched+gobuf_sp)(AX)
// Set up morestack arguments to call f on a new stack.
// We set f's frame size to 1, as a hint to newstack
- // that this is a call from reflect·call.
+ // that this is a call from runtime·newstackcall.
// If it turns out that f needs a larger frame than
// the default stack, f's usual stack growth prolog will
// allocate a new segment (and recopy the arguments).
@@ -263,7 +259,7 @@ TEXT reflect·call(SB), 7, $0
MOVQ 16(SP), DX // arg frame
MOVL 24(SP), CX // arg size
- MOVQ AX, m_morepc(BX) // f's PC
+ MOVQ AX, m_cret(BX) // f's PC
MOVQ DX, m_moreargp(BX) // argument frame pointer
MOVL CX, m_moreargsize(BX) // f's argument size
MOVL $1, m_moreframesize(BX) // f's frame size
@@ -277,8 +273,101 @@ TEXT reflect·call(SB), 7, $0
MOVQ $0, 0x1103 // crash if newstack returns
RET
+// reflect·call: call a function with the given argument list
+// func call(f *FuncVal, arg *byte, argsize uint32).
+// we don't have variable-sized frames, so we use a small number
+// of constant-sized-frame functions to encode a few bits of size in the pc.
+// Caution: ugly multiline assembly macros in your future!
+
+#define DISPATCH(NAME,MAXSIZE) \
+ CMPQ CX, $MAXSIZE; \
+ JA 3(PC); \
+ MOVQ $runtime·NAME(SB), AX; \
+ JMP AX
+// Note: can't just "JMP runtime·NAME(SB)" - bad inlining results.
+
+TEXT reflect·call(SB), NOSPLIT, $0-20
+ MOVLQZX argsize+16(FP), CX
+ DISPATCH(call16, 16)
+ DISPATCH(call32, 32)
+ DISPATCH(call64, 64)
+ DISPATCH(call128, 128)
+ DISPATCH(call256, 256)
+ DISPATCH(call512, 512)
+ DISPATCH(call1024, 1024)
+ DISPATCH(call2048, 2048)
+ DISPATCH(call4096, 4096)
+ DISPATCH(call8192, 8192)
+ DISPATCH(call16384, 16384)
+ DISPATCH(call32768, 32768)
+ DISPATCH(call65536, 65536)
+ DISPATCH(call131072, 131072)
+ DISPATCH(call262144, 262144)
+ DISPATCH(call524288, 524288)
+ DISPATCH(call1048576, 1048576)
+ DISPATCH(call2097152, 2097152)
+ DISPATCH(call4194304, 4194304)
+ DISPATCH(call8388608, 8388608)
+ DISPATCH(call16777216, 16777216)
+ DISPATCH(call33554432, 33554432)
+ DISPATCH(call67108864, 67108864)
+ DISPATCH(call134217728, 134217728)
+ DISPATCH(call268435456, 268435456)
+ DISPATCH(call536870912, 536870912)
+ DISPATCH(call1073741824, 1073741824)
+ MOVQ $runtime·badreflectcall(SB), AX
+ JMP AX
+
+#define CALLFN(NAME,MAXSIZE) \
+TEXT runtime·NAME(SB), WRAPPER, $MAXSIZE-20; \
+ /* copy arguments to stack */ \
+ MOVQ argptr+8(FP), SI; \
+ MOVLQZX argsize+16(FP), CX; \
+ MOVQ SP, DI; \
+ REP;MOVSB; \
+ /* call function */ \
+ MOVQ f+0(FP), DX; \
+ CALL (DX); \
+ /* copy return values back */ \
+ MOVQ argptr+8(FP), DI; \
+ MOVLQZX argsize+16(FP), CX; \
+ MOVQ SP, SI; \
+ REP;MOVSB; \
+ RET
+
+CALLFN(call16, 16)
+CALLFN(call32, 32)
+CALLFN(call64, 64)
+CALLFN(call128, 128)
+CALLFN(call256, 256)
+CALLFN(call512, 512)
+CALLFN(call1024, 1024)
+CALLFN(call2048, 2048)
+CALLFN(call4096, 4096)
+CALLFN(call8192, 8192)
+CALLFN(call16384, 16384)
+CALLFN(call32768, 32768)
+CALLFN(call65536, 65536)
+CALLFN(call131072, 131072)
+CALLFN(call262144, 262144)
+CALLFN(call524288, 524288)
+CALLFN(call1048576, 1048576)
+CALLFN(call2097152, 2097152)
+CALLFN(call4194304, 4194304)
+CALLFN(call8388608, 8388608)
+CALLFN(call16777216, 16777216)
+CALLFN(call33554432, 33554432)
+CALLFN(call67108864, 67108864)
+CALLFN(call134217728, 134217728)
+CALLFN(call268435456, 268435456)
+CALLFN(call536870912, 536870912)
+CALLFN(call1073741824, 1073741824)
+
// Return point when leaving stack.
-TEXT runtime·lessstack(SB), 7, $0
+//
+// Lessstack can appear in stack traces for the same reason
+// as morestack; in that context, it has 0 arguments.
+TEXT runtime·lessstack(SB), NOSPLIT, $0-0
// Save return value in m->cret
get_tls(CX)
MOVQ m(CX), BX
@@ -293,7 +382,7 @@ TEXT runtime·lessstack(SB), 7, $0
RET
// morestack trampolines
-TEXT runtime·morestack00(SB),7,$0
+TEXT runtime·morestack00(SB),NOSPLIT,$0
get_tls(CX)
MOVQ m(CX), BX
MOVQ $0, AX
@@ -301,7 +390,7 @@ TEXT runtime·morestack00(SB),7,$0
MOVQ $runtime·morestack(SB), AX
JMP AX
-TEXT runtime·morestack01(SB),7,$0
+TEXT runtime·morestack01(SB),NOSPLIT,$0
get_tls(CX)
MOVQ m(CX), BX
SHLQ $32, AX
@@ -309,7 +398,7 @@ TEXT runtime·morestack01(SB),7,$0
MOVQ $runtime·morestack(SB), AX
JMP AX
-TEXT runtime·morestack10(SB),7,$0
+TEXT runtime·morestack10(SB),NOSPLIT,$0
get_tls(CX)
MOVQ m(CX), BX
MOVLQZX AX, AX
@@ -317,7 +406,7 @@ TEXT runtime·morestack10(SB),7,$0
MOVQ $runtime·morestack(SB), AX
JMP AX
-TEXT runtime·morestack11(SB),7,$0
+TEXT runtime·morestack11(SB),NOSPLIT,$0
get_tls(CX)
MOVQ m(CX), BX
MOVQ AX, m_moreframesize(BX)
@@ -326,42 +415,41 @@ TEXT runtime·morestack11(SB),7,$0
// subcases of morestack01
// with const of 8,16,...48
-TEXT runtime·morestack8(SB),7,$0
- PUSHQ $1
+TEXT runtime·morestack8(SB),NOSPLIT,$0
+ MOVQ $1, R8
MOVQ $morestack<>(SB), AX
JMP AX
-TEXT runtime·morestack16(SB),7,$0
- PUSHQ $2
+TEXT runtime·morestack16(SB),NOSPLIT,$0
+ MOVQ $2, R8
MOVQ $morestack<>(SB), AX
JMP AX
-TEXT runtime·morestack24(SB),7,$0
- PUSHQ $3
+TEXT runtime·morestack24(SB),NOSPLIT,$0
+ MOVQ $3, R8
MOVQ $morestack<>(SB), AX
JMP AX
-TEXT runtime·morestack32(SB),7,$0
- PUSHQ $4
+TEXT runtime·morestack32(SB),NOSPLIT,$0
+ MOVQ $4, R8
MOVQ $morestack<>(SB), AX
JMP AX
-TEXT runtime·morestack40(SB),7,$0
- PUSHQ $5
+TEXT runtime·morestack40(SB),NOSPLIT,$0
+ MOVQ $5, R8
MOVQ $morestack<>(SB), AX
JMP AX
-TEXT runtime·morestack48(SB),7,$0
- PUSHQ $6
+TEXT runtime·morestack48(SB),NOSPLIT,$0
+ MOVQ $6, R8
MOVQ $morestack<>(SB), AX
JMP AX
-TEXT morestack<>(SB),7,$0
+TEXT morestack<>(SB),NOSPLIT,$0
get_tls(CX)
MOVQ m(CX), BX
- POPQ AX
- SHLQ $35, AX
- MOVQ AX, m_moreframesize(BX)
+ SHLQ $35, R8
+ MOVQ R8, m_moreframesize(BX)
MOVQ $runtime·morestack(SB), AX
JMP AX
@@ -372,7 +460,7 @@ TEXT morestack<>(SB),7,$0
// return 1;
// } else
// return 0;
-TEXT runtime·cas(SB), 7, $0
+TEXT runtime·cas(SB), NOSPLIT, $0-16
MOVQ 8(SP), BX
MOVL 16(SP), AX
MOVL 20(SP), CX
@@ -384,19 +472,17 @@ TEXT runtime·cas(SB), 7, $0
MOVL $1, AX
RET
-// bool runtime·cas64(uint64 *val, uint64 *old, uint64 new)
+// bool runtime·cas64(uint64 *val, uint64 old, uint64 new)
// Atomically:
// if(*val == *old){
// *val = new;
// return 1;
// } else {
-// *old = *val
// return 0;
// }
-TEXT runtime·cas64(SB), 7, $0
+TEXT runtime·cas64(SB), NOSPLIT, $0-24
MOVQ 8(SP), BX
- MOVQ 16(SP), BP
- MOVQ 0(BP), AX
+ MOVQ 16(SP), AX
MOVQ 24(SP), CX
LOCK
CMPXCHGQ CX, 0(BX)
@@ -404,7 +490,6 @@ TEXT runtime·cas64(SB), 7, $0
MOVL $1, AX
RET
cas64_fail:
- MOVQ AX, 0(BP)
MOVL $0, AX
RET
@@ -415,7 +500,7 @@ cas64_fail:
// return 1;
// } else
// return 0;
-TEXT runtime·casp(SB), 7, $0
+TEXT runtime·casp(SB), NOSPLIT, $0-24
MOVQ 8(SP), BX
MOVQ 16(SP), AX
MOVQ 24(SP), CX
@@ -431,7 +516,7 @@ TEXT runtime·casp(SB), 7, $0
// Atomically:
// *val += delta;
// return *val;
-TEXT runtime·xadd(SB), 7, $0
+TEXT runtime·xadd(SB), NOSPLIT, $0-12
MOVQ 8(SP), BX
MOVL 16(SP), AX
MOVL AX, CX
@@ -440,7 +525,7 @@ TEXT runtime·xadd(SB), 7, $0
ADDL CX, AX
RET
-TEXT runtime·xadd64(SB), 7, $0
+TEXT runtime·xadd64(SB), NOSPLIT, $0-16
MOVQ 8(SP), BX
MOVQ 16(SP), AX
MOVQ AX, CX
@@ -449,19 +534,19 @@ TEXT runtime·xadd64(SB), 7, $0
ADDQ CX, AX
RET
-TEXT runtime·xchg(SB), 7, $0
+TEXT runtime·xchg(SB), NOSPLIT, $0-12
MOVQ 8(SP), BX
MOVL 16(SP), AX
XCHGL AX, 0(BX)
RET
-TEXT runtime·xchg64(SB), 7, $0
+TEXT runtime·xchg64(SB), NOSPLIT, $0-16
MOVQ 8(SP), BX
MOVQ 16(SP), AX
XCHGQ AX, 0(BX)
RET
-TEXT runtime·procyield(SB),7,$0
+TEXT runtime·procyield(SB),NOSPLIT,$0-0
MOVL 8(SP), AX
again:
PAUSE
@@ -469,19 +554,19 @@ again:
JNZ again
RET
-TEXT runtime·atomicstorep(SB), 7, $0
+TEXT runtime·atomicstorep(SB), NOSPLIT, $0-16
MOVQ 8(SP), BX
MOVQ 16(SP), AX
XCHGQ AX, 0(BX)
RET
-TEXT runtime·atomicstore(SB), 7, $0
+TEXT runtime·atomicstore(SB), NOSPLIT, $0-12
MOVQ 8(SP), BX
MOVL 16(SP), AX
XCHGL AX, 0(BX)
RET
-TEXT runtime·atomicstore64(SB), 7, $0
+TEXT runtime·atomicstore64(SB), NOSPLIT, $0-16
MOVQ 8(SP), BX
MOVQ 16(SP), AX
XCHGQ AX, 0(BX)
@@ -492,7 +577,7 @@ TEXT runtime·atomicstore64(SB), 7, $0
// 1. pop the caller
// 2. sub 5 bytes from the callers return
// 3. jmp to the argument
-TEXT runtime·jmpdefer(SB), 7, $0
+TEXT runtime·jmpdefer(SB), NOSPLIT, $0-16
MOVQ 8(SP), DX // fn
MOVQ 16(SP), BX // caller sp
LEAQ -8(BX), SP // caller sp after CALL
@@ -500,18 +585,23 @@ TEXT runtime·jmpdefer(SB), 7, $0
MOVQ 0(DX), BX
JMP BX // 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
+// Save state of caller into g->sched. Smashes R8, R9.
+TEXT gosave<>(SB),NOSPLIT,$0
+ get_tls(R8)
+ MOVQ g(R8), R8
+ MOVQ 0(SP), R9
+ MOVQ R9, (g_sched+gobuf_pc)(R8)
+ LEAQ 8(SP), R9
+ MOVQ R9, (g_sched+gobuf_sp)(R8)
+ MOVQ $0, (g_sched+gobuf_ret)(R8)
+ MOVQ $0, (g_sched+gobuf_ctxt)(R8)
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
+TEXT runtime·asmcgocall(SB),NOSPLIT,$0-16
MOVQ fn+0(FP), AX
MOVQ arg+8(FP), BX
MOVQ SP, DX
@@ -524,10 +614,8 @@ TEXT runtime·asmcgocall(SB),7,$0
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)
+ JEQ 4(PC)
+ CALL gosave<>(SB)
MOVQ SI, g(CX)
MOVQ (g_sched+gobuf_sp)(SI), SP
@@ -552,7 +640,7 @@ TEXT runtime·asmcgocall(SB),7,$0
// cgocallback(void (*fn)(void*), void *frame, uintptr framesize)
// Turn the fn into a Go func (by taking its address) and call
// cgocallback_gofunc.
-TEXT runtime·cgocallback(SB),7,$24
+TEXT runtime·cgocallback(SB),NOSPLIT,$24-24
LEAQ fn+0(FP), AX
MOVQ AX, 0(SP)
MOVQ frame+8(FP), AX
@@ -565,7 +653,7 @@ TEXT runtime·cgocallback(SB),7,$24
// cgocallback_gofunc(FuncVal*, void *frame, uintptr framesize)
// See cgocall.c for more details.
-TEXT runtime·cgocallback_gofunc(SB),7,$24
+TEXT runtime·cgocallback_gofunc(SB),NOSPLIT,$8-24
// If m is nil, Go did not create the current thread.
// Call needm to obtain one for temporary use.
// In this case, we're running on the thread stack, so there's
@@ -573,18 +661,19 @@ TEXT runtime·cgocallback_gofunc(SB),7,$24
// the linker analysis by using an indirect call through AX.
get_tls(CX)
#ifdef GOOS_windows
+ MOVL $0, BP
CMPQ CX, $0
- JNE 3(PC)
- PUSHQ $0
- JMP needm
+ JEQ 2(PC)
#endif
MOVQ m(CX), BP
- PUSHQ BP
+ MOVQ BP, R8 // holds oldm until end of function
CMPQ BP, $0
JNE havem
needm:
+ MOVQ R8, 0(SP)
MOVQ $runtime·needm(SB), AX
CALL AX
+ MOVQ 0(SP), R8
get_tls(CX)
MOVQ m(CX), BP
@@ -593,55 +682,43 @@ havem:
// Save current m->g0->sched.sp on stack and then set it to SP.
// Save current sp in m->g0->sched.sp in preparation for
// switch back to m->curg stack.
+ // NOTE: unwindm knows that the saved g->sched.sp is at 0(SP).
MOVQ m_g0(BP), SI
- PUSHQ (g_sched+gobuf_sp)(SI)
+ MOVQ (g_sched+gobuf_sp)(SI), AX
+ MOVQ AX, 0(SP)
MOVQ SP, (g_sched+gobuf_sp)(SI)
- // Switch to m->curg stack and call runtime.cgocallbackg
- // 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
+ // Switch to m->curg stack and call runtime.cgocallbackg.
+ // 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->sched) so we can restore it.
+ // We can restore m->curg->sched.sp easily, because calling
// runtime.cgocallbackg leaves SP unchanged upon return.
- // To save m->curg->gobuf.pc, we push it onto the stack.
+ // To save m->curg->sched.pc, we push it onto the stack.
// This has the added benefit that it looks to the traceback
// routine like cgocallbackg is going to return to that
- // PC (because we defined cgocallbackg to have
- // a frame size of 24, the same amount that we use below),
+ // PC (because the frame we allocate below has the same
+ // size as cgocallback_gofunc's frame declared above)
// so that the traceback will seamlessly trace back into
// the earlier calls.
- MOVQ fn+0(FP), AX
- MOVQ frame+8(FP), BX
- MOVQ framesize+16(FP), DX
-
+ //
+ // In the new goroutine, 0(SP) holds the saved R8.
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
+ MOVQ BP, -8(DI)
+ LEAQ -(8+8)(DI), SP
+ MOVQ R8, 0(SP)
CALL runtime·cgocallbackg(SB)
+ MOVQ 0(SP), R8
- // Restore g->gobuf (== m->curg->gobuf) from saved values.
+ // Restore g->sched (== m->curg->sched) from saved values.
get_tls(CX)
MOVQ g(CX), SI
- MOVQ 24(SP), BP
+ MOVQ 8(SP), BP
MOVQ BP, (g_sched+gobuf_pc)(SI)
- LEAQ (24+8)(SP), DI
+ LEAQ (8+8)(SP), DI
MOVQ DI, (g_sched+gobuf_sp)(SI)
// Switch back to m->g0's stack and restore m->g0->sched.sp.
@@ -651,12 +728,12 @@ havem:
MOVQ m_g0(BP), SI
MOVQ SI, g(CX)
MOVQ (g_sched+gobuf_sp)(SI), SP
- POPQ (g_sched+gobuf_sp)(SI)
+ MOVQ 0(SP), AX
+ MOVQ AX, (g_sched+gobuf_sp)(SI)
// If the m on entry was nil, we called needm above to borrow an m
// for the duration of the call. Since the call is over, return it with dropm.
- POPQ BP
- CMPQ BP, $0
+ CMPQ R8, $0
JNE 3(PC)
MOVQ $runtime·dropm(SB), AX
CALL AX
@@ -665,7 +742,7 @@ havem:
RET
// void setmg(M*, G*); set m and g. for use by needm.
-TEXT runtime·setmg(SB), 7, $0
+TEXT runtime·setmg(SB), NOSPLIT, $0-16
MOVQ mm+0(FP), AX
#ifdef GOOS_windows
CMPQ AX, $0
@@ -684,14 +761,14 @@ settls:
RET
// void setmg_gcc(M*, G*); set m and g called from gcc.
-TEXT setmg_gcc<>(SB),7,$0
+TEXT setmg_gcc<>(SB),NOSPLIT,$0
get_tls(AX)
MOVQ DI, m(AX)
MOVQ SI, g(AX)
RET
// check that SP is in range [g->stackbase, g->stackguard)
-TEXT runtime·stackcheck(SB), 7, $0
+TEXT runtime·stackcheck(SB), NOSPLIT, $0-0
get_tls(CX)
MOVQ g(CX), AX
CMPQ g_stackbase(AX), SP
@@ -702,7 +779,7 @@ TEXT runtime·stackcheck(SB), 7, $0
INT $3
RET
-TEXT runtime·memclr(SB),7,$0
+TEXT runtime·memclr(SB),NOSPLIT,$0-16
MOVQ 8(SP), DI // arg 1 addr
MOVQ 16(SP), CX // arg 2 count
MOVQ CX, BX
@@ -717,29 +794,29 @@ TEXT runtime·memclr(SB),7,$0
STOSB
RET
-TEXT runtime·getcallerpc(SB),7,$0
+TEXT runtime·getcallerpc(SB),NOSPLIT,$0-8
MOVQ x+0(FP),AX // addr of first arg
MOVQ -8(AX),AX // get calling pc
RET
-TEXT runtime·setcallerpc(SB),7,$0
+TEXT runtime·setcallerpc(SB),NOSPLIT,$0-16
MOVQ x+0(FP),AX // addr of first arg
MOVQ x+8(FP), BX
MOVQ BX, -8(AX) // set calling pc
RET
-TEXT runtime·getcallersp(SB),7,$0
+TEXT runtime·getcallersp(SB),NOSPLIT,$0-8
MOVQ sp+0(FP), AX
RET
// int64 runtime·cputicks(void)
-TEXT runtime·cputicks(SB),7,$0
+TEXT runtime·cputicks(SB),NOSPLIT,$0-0
RDTSC
SHLQ $32, DX
ADDQ DX, AX
RET
-TEXT runtime·stackguard(SB),7,$0
+TEXT runtime·stackguard(SB),NOSPLIT,$0-16
MOVQ SP, DX
MOVQ DX, sp+0(FP)
get_tls(CX)
@@ -751,13 +828,13 @@ TEXT runtime·stackguard(SB),7,$0
GLOBL runtime·tls0(SB), $64
// hash function using AES hardware instructions
-TEXT runtime·aeshash(SB),7,$0
+TEXT runtime·aeshash(SB),NOSPLIT,$0-24
MOVQ 8(SP), DX // ptr to hash value
MOVQ 16(SP), CX // size
MOVQ 24(SP), AX // ptr to data
JMP runtime·aeshashbody(SB)
-TEXT runtime·aeshashstr(SB),7,$0
+TEXT runtime·aeshashstr(SB),NOSPLIT,$0-24
MOVQ 8(SP), DX // ptr to hash value
MOVQ 24(SP), AX // ptr to string struct
MOVQ 8(AX), CX // length of string
@@ -767,41 +844,49 @@ TEXT runtime·aeshashstr(SB),7,$0
// AX: data
// CX: length
// DX: ptr to seed input / hash output
-TEXT runtime·aeshashbody(SB),7,$0
+TEXT runtime·aeshashbody(SB),NOSPLIT,$0-24
MOVQ (DX), X0 // seed to low 64 bits of xmm0
PINSRQ $1, CX, X0 // size to high 64 bits of xmm0
MOVO runtime·aeskeysched+0(SB), X2
MOVO runtime·aeskeysched+16(SB), X3
+ CMPQ CX, $16
+ JB aessmall
aesloop:
CMPQ CX, $16
- JB aesloopend
+ JBE aesloopend
MOVOU (AX), X1
AESENC X2, X0
AESENC X1, X0
SUBQ $16, CX
ADDQ $16, AX
JMP aesloop
+// 1-16 bytes remaining
aesloopend:
+ // This load may overlap with the previous load above.
+ // We'll hash some bytes twice, but that's ok.
+ MOVOU -16(AX)(CX*1), X1
+ JMP partial
+// 0-15 bytes
+aessmall:
TESTQ CX, CX
- JE finalize // no partial block
+ JE finalize // 0 bytes
- TESTQ $16, AX
- JNE highpartial
+ CMPB AX, $0xf0
+ JA highpartial
- // address ends in 0xxxx. 16 bytes loaded
- // at this address won't cross a page boundary, so
- // we can load it directly.
+ // 16 bytes loaded at this address won't cross
+ // a page boundary, so we can load it directly.
MOVOU (AX), X1
ADDQ CX, CX
- PAND masks(SB)(CX*8), X1
+ PAND masks<>(SB)(CX*8), X1
JMP partial
highpartial:
- // address ends in 1xxxx. Might be up against
+ // address ends in 1111xxxx. Might be up against
// a page boundary, so load ending at last byte.
// Then shift bytes down using pshufb.
MOVOU -16(AX)(CX*1), X1
ADDQ CX, CX
- PSHUFB shifts(SB)(CX*8), X1
+ PSHUFB shifts<>(SB)(CX*8), X1
partial:
// incorporate partial block into hash
AESENC X3, X0
@@ -814,7 +899,7 @@ finalize:
MOVQ X0, (DX)
RET
-TEXT runtime·aeshash32(SB),7,$0
+TEXT runtime·aeshash32(SB),NOSPLIT,$0-24
MOVQ 8(SP), DX // ptr to hash value
MOVQ 24(SP), AX // ptr to data
MOVQ (DX), X0 // seed
@@ -825,7 +910,7 @@ TEXT runtime·aeshash32(SB),7,$0
MOVQ X0, (DX)
RET
-TEXT runtime·aeshash64(SB),7,$0
+TEXT runtime·aeshash64(SB),NOSPLIT,$0-24
MOVQ 8(SP), DX // ptr to hash value
MOVQ 24(SP), AX // ptr to data
MOVQ (DX), X0 // seed
@@ -837,101 +922,87 @@ TEXT runtime·aeshash64(SB),7,$0
RET
// simple mask to get rid of data in the high part of the register.
-TEXT masks(SB),7,$0
- QUAD $0x0000000000000000
- QUAD $0x0000000000000000
- QUAD $0x00000000000000ff
- QUAD $0x0000000000000000
- QUAD $0x000000000000ffff
- QUAD $0x0000000000000000
- QUAD $0x0000000000ffffff
- QUAD $0x0000000000000000
- QUAD $0x00000000ffffffff
- QUAD $0x0000000000000000
- QUAD $0x000000ffffffffff
- QUAD $0x0000000000000000
- QUAD $0x0000ffffffffffff
- QUAD $0x0000000000000000
- QUAD $0x00ffffffffffffff
- QUAD $0x0000000000000000
- QUAD $0xffffffffffffffff
- QUAD $0x0000000000000000
- QUAD $0xffffffffffffffff
- QUAD $0x00000000000000ff
- QUAD $0xffffffffffffffff
- QUAD $0x000000000000ffff
- QUAD $0xffffffffffffffff
- QUAD $0x0000000000ffffff
- QUAD $0xffffffffffffffff
- QUAD $0x00000000ffffffff
- QUAD $0xffffffffffffffff
- QUAD $0x000000ffffffffff
- QUAD $0xffffffffffffffff
- QUAD $0x0000ffffffffffff
- QUAD $0xffffffffffffffff
- QUAD $0x00ffffffffffffff
-
- // these are arguments to pshufb. They move data down from
- // the high bytes of the register to the low bytes of the register.
- // index is how many bytes to move.
-TEXT shifts(SB),7,$0
- QUAD $0x0000000000000000
- QUAD $0x0000000000000000
- QUAD $0xffffffffffffff0f
- QUAD $0xffffffffffffffff
- QUAD $0xffffffffffff0f0e
- QUAD $0xffffffffffffffff
- QUAD $0xffffffffff0f0e0d
- QUAD $0xffffffffffffffff
- QUAD $0xffffffff0f0e0d0c
- QUAD $0xffffffffffffffff
- QUAD $0xffffff0f0e0d0c0b
- QUAD $0xffffffffffffffff
- QUAD $0xffff0f0e0d0c0b0a
- QUAD $0xffffffffffffffff
- QUAD $0xff0f0e0d0c0b0a09
- QUAD $0xffffffffffffffff
- QUAD $0x0f0e0d0c0b0a0908
- QUAD $0xffffffffffffffff
- QUAD $0x0e0d0c0b0a090807
- QUAD $0xffffffffffffff0f
- QUAD $0x0d0c0b0a09080706
- QUAD $0xffffffffffff0f0e
- QUAD $0x0c0b0a0908070605
- QUAD $0xffffffffff0f0e0d
- QUAD $0x0b0a090807060504
- QUAD $0xffffffff0f0e0d0c
- QUAD $0x0a09080706050403
- QUAD $0xffffff0f0e0d0c0b
- QUAD $0x0908070605040302
- QUAD $0xffff0f0e0d0c0b0a
- QUAD $0x0807060504030201
- QUAD $0xff0f0e0d0c0b0a09
-
-TEXT runtime·memeq(SB),7,$0
+DATA masks<>+0x00(SB)/8, $0x0000000000000000
+DATA masks<>+0x08(SB)/8, $0x0000000000000000
+DATA masks<>+0x10(SB)/8, $0x00000000000000ff
+DATA masks<>+0x18(SB)/8, $0x0000000000000000
+DATA masks<>+0x20(SB)/8, $0x000000000000ffff
+DATA masks<>+0x28(SB)/8, $0x0000000000000000
+DATA masks<>+0x30(SB)/8, $0x0000000000ffffff
+DATA masks<>+0x38(SB)/8, $0x0000000000000000
+DATA masks<>+0x40(SB)/8, $0x00000000ffffffff
+DATA masks<>+0x48(SB)/8, $0x0000000000000000
+DATA masks<>+0x50(SB)/8, $0x000000ffffffffff
+DATA masks<>+0x58(SB)/8, $0x0000000000000000
+DATA masks<>+0x60(SB)/8, $0x0000ffffffffffff
+DATA masks<>+0x68(SB)/8, $0x0000000000000000
+DATA masks<>+0x70(SB)/8, $0x00ffffffffffffff
+DATA masks<>+0x78(SB)/8, $0x0000000000000000
+DATA masks<>+0x80(SB)/8, $0xffffffffffffffff
+DATA masks<>+0x88(SB)/8, $0x0000000000000000
+DATA masks<>+0x90(SB)/8, $0xffffffffffffffff
+DATA masks<>+0x98(SB)/8, $0x00000000000000ff
+DATA masks<>+0xa0(SB)/8, $0xffffffffffffffff
+DATA masks<>+0xa8(SB)/8, $0x000000000000ffff
+DATA masks<>+0xb0(SB)/8, $0xffffffffffffffff
+DATA masks<>+0xb8(SB)/8, $0x0000000000ffffff
+DATA masks<>+0xc0(SB)/8, $0xffffffffffffffff
+DATA masks<>+0xc8(SB)/8, $0x00000000ffffffff
+DATA masks<>+0xd0(SB)/8, $0xffffffffffffffff
+DATA masks<>+0xd8(SB)/8, $0x000000ffffffffff
+DATA masks<>+0xe0(SB)/8, $0xffffffffffffffff
+DATA masks<>+0xe8(SB)/8, $0x0000ffffffffffff
+DATA masks<>+0xf0(SB)/8, $0xffffffffffffffff
+DATA masks<>+0xf8(SB)/8, $0x00ffffffffffffff
+GLOBL masks<>(SB),RODATA,$256
+
+// these are arguments to pshufb. They move data down from
+// the high bytes of the register to the low bytes of the register.
+// index is how many bytes to move.
+DATA shifts<>+0x00(SB)/8, $0x0000000000000000
+DATA shifts<>+0x08(SB)/8, $0x0000000000000000
+DATA shifts<>+0x10(SB)/8, $0xffffffffffffff0f
+DATA shifts<>+0x18(SB)/8, $0xffffffffffffffff
+DATA shifts<>+0x20(SB)/8, $0xffffffffffff0f0e
+DATA shifts<>+0x28(SB)/8, $0xffffffffffffffff
+DATA shifts<>+0x30(SB)/8, $0xffffffffff0f0e0d
+DATA shifts<>+0x38(SB)/8, $0xffffffffffffffff
+DATA shifts<>+0x40(SB)/8, $0xffffffff0f0e0d0c
+DATA shifts<>+0x48(SB)/8, $0xffffffffffffffff
+DATA shifts<>+0x50(SB)/8, $0xffffff0f0e0d0c0b
+DATA shifts<>+0x58(SB)/8, $0xffffffffffffffff
+DATA shifts<>+0x60(SB)/8, $0xffff0f0e0d0c0b0a
+DATA shifts<>+0x68(SB)/8, $0xffffffffffffffff
+DATA shifts<>+0x70(SB)/8, $0xff0f0e0d0c0b0a09
+DATA shifts<>+0x78(SB)/8, $0xffffffffffffffff
+DATA shifts<>+0x80(SB)/8, $0x0f0e0d0c0b0a0908
+DATA shifts<>+0x88(SB)/8, $0xffffffffffffffff
+DATA shifts<>+0x90(SB)/8, $0x0e0d0c0b0a090807
+DATA shifts<>+0x98(SB)/8, $0xffffffffffffff0f
+DATA shifts<>+0xa0(SB)/8, $0x0d0c0b0a09080706
+DATA shifts<>+0xa8(SB)/8, $0xffffffffffff0f0e
+DATA shifts<>+0xb0(SB)/8, $0x0c0b0a0908070605
+DATA shifts<>+0xb8(SB)/8, $0xffffffffff0f0e0d
+DATA shifts<>+0xc0(SB)/8, $0x0b0a090807060504
+DATA shifts<>+0xc8(SB)/8, $0xffffffff0f0e0d0c
+DATA shifts<>+0xd0(SB)/8, $0x0a09080706050403
+DATA shifts<>+0xd8(SB)/8, $0xffffff0f0e0d0c0b
+DATA shifts<>+0xe0(SB)/8, $0x0908070605040302
+DATA shifts<>+0xe8(SB)/8, $0xffff0f0e0d0c0b0a
+DATA shifts<>+0xf0(SB)/8, $0x0807060504030201
+DATA shifts<>+0xf8(SB)/8, $0xff0f0e0d0c0b0a09
+GLOBL shifts<>(SB),RODATA,$256
+
+TEXT runtime·memeq(SB),NOSPLIT,$0-24
MOVQ a+0(FP), SI
MOVQ b+8(FP), DI
MOVQ count+16(FP), BX
JMP runtime·memeqbody(SB)
-
-TEXT bytes·Equal(SB),7,$0
- MOVQ a_len+8(FP), BX
- MOVQ b_len+32(FP), CX
- XORQ AX, AX
- CMPQ BX, CX
- JNE eqret
- MOVQ a+0(FP), SI
- MOVQ b+24(FP), DI
- CALL runtime·memeqbody(SB)
-eqret:
- MOVB AX, ret+48(FP)
- RET
-
// a in SI
// b in DI
// count in BX
-TEXT runtime·memeqbody(SB),7,$0
+TEXT runtime·memeqbody(SB),NOSPLIT,$0-0
XORQ AX, AX
CMPQ BX, $8
@@ -1019,3 +1090,253 @@ di_finish:
equal:
SETEQ AX
RET
+
+TEXT runtime·cmpstring(SB),NOSPLIT,$0-40
+ MOVQ s1+0(FP), SI
+ MOVQ s1+8(FP), BX
+ MOVQ s2+16(FP), DI
+ MOVQ s2+24(FP), DX
+ CALL runtime·cmpbody(SB)
+ MOVQ AX, res+32(FP)
+ RET
+
+TEXT bytes·Compare(SB),NOSPLIT,$0-56
+ MOVQ s1+0(FP), SI
+ MOVQ s1+8(FP), BX
+ MOVQ s2+24(FP), DI
+ MOVQ s2+32(FP), DX
+ CALL runtime·cmpbody(SB)
+ MOVQ AX, res+48(FP)
+ RET
+
+// input:
+// SI = a
+// DI = b
+// BX = alen
+// DX = blen
+// output:
+// AX = 1/0/-1
+TEXT runtime·cmpbody(SB),NOSPLIT,$0-0
+ CMPQ SI, DI
+ JEQ cmp_allsame
+ CMPQ BX, DX
+ MOVQ DX, BP
+ CMOVQLT BX, BP // BP = min(alen, blen) = # of bytes to compare
+ CMPQ BP, $8
+ JB cmp_small
+
+cmp_loop:
+ CMPQ BP, $16
+ JBE cmp_0through16
+ MOVOU (SI), X0
+ MOVOU (DI), X1
+ PCMPEQB X0, X1
+ PMOVMSKB X1, AX
+ XORQ $0xffff, AX // convert EQ to NE
+ JNE cmp_diff16 // branch if at least one byte is not equal
+ ADDQ $16, SI
+ ADDQ $16, DI
+ SUBQ $16, BP
+ JMP cmp_loop
+
+ // AX = bit mask of differences
+cmp_diff16:
+ BSFQ AX, BX // index of first byte that differs
+ XORQ AX, AX
+ MOVB (SI)(BX*1), CX
+ CMPB CX, (DI)(BX*1)
+ SETHI AX
+ LEAQ -1(AX*2), AX // convert 1/0 to +1/-1
+ RET
+
+ // 0 through 16 bytes left, alen>=8, blen>=8
+cmp_0through16:
+ CMPQ BP, $8
+ JBE cmp_0through8
+ MOVQ (SI), AX
+ MOVQ (DI), CX
+ CMPQ AX, CX
+ JNE cmp_diff8
+cmp_0through8:
+ MOVQ -8(SI)(BP*1), AX
+ MOVQ -8(DI)(BP*1), CX
+ CMPQ AX, CX
+ JEQ cmp_allsame
+
+ // AX and CX contain parts of a and b that differ.
+cmp_diff8:
+ BSWAPQ AX // reverse order of bytes
+ BSWAPQ CX
+ XORQ AX, CX
+ BSRQ CX, CX // index of highest bit difference
+ SHRQ CX, AX // move a's bit to bottom
+ ANDQ $1, AX // mask bit
+ LEAQ -1(AX*2), AX // 1/0 => +1/-1
+ RET
+
+ // 0-7 bytes in common
+cmp_small:
+ LEAQ (BP*8), CX // bytes left -> bits left
+ NEGQ CX // - bits lift (== 64 - bits left mod 64)
+ JEQ cmp_allsame
+
+ // load bytes of a into high bytes of AX
+ CMPB SI, $0xf8
+ JA cmp_si_high
+ MOVQ (SI), SI
+ JMP cmp_si_finish
+cmp_si_high:
+ MOVQ -8(SI)(BP*1), SI
+ SHRQ CX, SI
+cmp_si_finish:
+ SHLQ CX, SI
+
+ // load bytes of b in to high bytes of BX
+ CMPB DI, $0xf8
+ JA cmp_di_high
+ MOVQ (DI), DI
+ JMP cmp_di_finish
+cmp_di_high:
+ MOVQ -8(DI)(BP*1), DI
+ SHRQ CX, DI
+cmp_di_finish:
+ SHLQ CX, DI
+
+ BSWAPQ SI // reverse order of bytes
+ BSWAPQ DI
+ XORQ SI, DI // find bit differences
+ JEQ cmp_allsame
+ BSRQ DI, CX // index of highest bit difference
+ SHRQ CX, SI // move a's bit to bottom
+ ANDQ $1, SI // mask bit
+ LEAQ -1(SI*2), AX // 1/0 => +1/-1
+ RET
+
+cmp_allsame:
+ XORQ AX, AX
+ XORQ CX, CX
+ CMPQ BX, DX
+ SETGT AX // 1 if alen > blen
+ SETEQ CX // 1 if alen == blen
+ LEAQ -1(CX)(AX*2), AX // 1,0,-1 result
+ RET
+
+TEXT bytes·IndexByte(SB),NOSPLIT,$0
+ MOVQ s+0(FP), SI
+ MOVQ s_len+8(FP), BX
+ MOVB c+24(FP), AL
+ CALL runtime·indexbytebody(SB)
+ MOVQ AX, ret+32(FP)
+ RET
+
+TEXT strings·IndexByte(SB),NOSPLIT,$0
+ MOVQ s+0(FP), SI
+ MOVQ s_len+8(FP), BX
+ MOVB c+16(FP), AL
+ CALL runtime·indexbytebody(SB)
+ MOVQ AX, ret+24(FP)
+ RET
+
+// input:
+// SI: data
+// BX: data len
+// AL: byte sought
+// output:
+// AX
+TEXT runtime·indexbytebody(SB),NOSPLIT,$0
+ MOVQ SI, DI
+
+ CMPQ BX, $16
+ JLT indexbyte_small
+
+ // round up to first 16-byte boundary
+ TESTQ $15, SI
+ JZ aligned
+ MOVQ SI, CX
+ ANDQ $~15, CX
+ ADDQ $16, CX
+
+ // search the beginning
+ SUBQ SI, CX
+ REPN; SCASB
+ JZ success
+
+// DI is 16-byte aligned; get ready to search using SSE instructions
+aligned:
+ // round down to last 16-byte boundary
+ MOVQ BX, R11
+ ADDQ SI, R11
+ ANDQ $~15, R11
+
+ // shuffle X0 around so that each byte contains c
+ MOVD AX, X0
+ PUNPCKLBW X0, X0
+ PUNPCKLBW X0, X0
+ PSHUFL $0, X0, X0
+ JMP condition
+
+sse:
+ // move the next 16-byte chunk of the buffer into X1
+ MOVO (DI), X1
+ // compare bytes in X0 to X1
+ PCMPEQB X0, X1
+ // take the top bit of each byte in X1 and put the result in DX
+ PMOVMSKB X1, DX
+ TESTL DX, DX
+ JNZ ssesuccess
+ ADDQ $16, DI
+
+condition:
+ CMPQ DI, R11
+ JLT sse
+
+ // search the end
+ MOVQ SI, CX
+ ADDQ BX, CX
+ SUBQ R11, CX
+ // if CX == 0, the zero flag will be set and we'll end up
+ // returning a false success
+ JZ failure
+ REPN; SCASB
+ JZ success
+
+failure:
+ MOVQ $-1, AX
+ RET
+
+// handle for lengths < 16
+indexbyte_small:
+ MOVQ BX, CX
+ REPN; SCASB
+ JZ success
+ MOVQ $-1, AX
+ RET
+
+// we've found the chunk containing the byte
+// now just figure out which specific byte it is
+ssesuccess:
+ // get the index of the least significant set bit
+ BSFW DX, DX
+ SUBQ SI, DI
+ ADDQ DI, DX
+ MOVQ DX, AX
+ RET
+
+success:
+ SUBQ SI, DI
+ SUBL $1, DI
+ MOVQ DI, AX
+ RET
+
+TEXT bytes·Equal(SB),NOSPLIT,$0-49
+ MOVQ a_len+8(FP), BX
+ MOVQ b_len+32(FP), CX
+ XORQ AX, AX
+ CMPQ BX, CX
+ JNE eqret
+ MOVQ a+0(FP), SI
+ MOVQ b+24(FP), DI
+ CALL runtime·memeqbody(SB)
+eqret:
+ MOVB AX, ret+48(FP)
+ RET
diff --git a/src/pkg/runtime/asm_arm.s b/src/pkg/runtime/asm_arm.s
index fed9b3021..f483e6fc8 100644
--- a/src/pkg/runtime/asm_arm.s
+++ b/src/pkg/runtime/asm_arm.s
@@ -3,15 +3,17 @@
// license that can be found in the LICENSE file.
#include "zasm_GOOS_GOARCH.h"
+#include "funcdata.h"
+#include "../../cmd/ld/textflag.h"
// using frame size $-4 means do not save LR on stack.
-TEXT _rt0_arm(SB),7,$-4
+TEXT _rt0_go(SB),NOSPLIT,$-4
MOVW $0xcafebabe, R12
// copy arguments forward on an even stack
// use R13 instead of SP to avoid linker rewriting the offsets
MOVW 0(R13), R0 // argc
- MOVW $4(R13), R1 // argv
+ MOVW 4(R13), R1 // argv
SUB $64, R13 // plenty of scratch
AND $~7, R13
MOVW R0, 60(R13) // save argc, argv away
@@ -28,14 +30,23 @@ TEXT _rt0_arm(SB),7,$-4
// create istack out of the OS stack
MOVW $(-8192+104)(R13), R0
MOVW R0, g_stackguard(g) // (w 104b guard)
+ MOVW R0, g_stackguard0(g)
MOVW R13, g_stackbase(g)
BL runtime·emptyfunc(SB) // fault if stack check is wrong
// if there is an _cgo_init, call it.
- MOVW _cgo_init(SB), R2
- CMP $0, R2
- MOVW.NE g, R0 // first argument of _cgo_init is g
- BL.NE (R2) // will clobber R0-R3
+ MOVW _cgo_init(SB), R4
+ CMP $0, R4
+ B.EQ nocgo
+ BL runtime·save_gm(SB);
+ MOVW g, R0 // first argument of _cgo_init is g
+ MOVW $setmg_gcc<>(SB), R1 // second argument is address of save_gm
+ BL (R4) // will clobber R0-R3
+
+nocgo:
+ // update stackguard after _cgo_init
+ MOVW g_stackguard0(g), R0
+ MOVW R0, g_stackguard(g)
BL runtime·checkgoarm(SB)
BL runtime·check(SB)
@@ -57,7 +68,9 @@ TEXT _rt0_arm(SB),7,$-4
MOVW.W R0, -4(R13)
MOVW $0, R0
MOVW.W R0, -4(R13) // push $0 as guard
+ ARGSIZE(12)
BL runtime·newproc(SB)
+ ARGSIZE(-1)
MOVW $12(R13), R13 // pop args and LR
// start this M
@@ -68,23 +81,24 @@ TEXT _rt0_arm(SB),7,$-4
MOVW R0, (R1) // fail hard
DATA runtime·main·f+0(SB)/4,$runtime·main(SB)
-GLOBL runtime·main·f(SB),8,$4
+GLOBL runtime·main·f(SB),RODATA,$4
-TEXT runtime·breakpoint(SB),7,$0
+TEXT runtime·breakpoint(SB),NOSPLIT,$0-0
// gdb won't skip this breakpoint instruction automatically,
// so you must manually "set $pc+=4" to skip it and continue.
- WORD $0xe1200071 // BKPT 0x0001
+ WORD $0xe1200071 // BKPT 0x0001
RET
GLOBL runtime·goarm(SB), $4
-TEXT runtime·asminit(SB),7,$0
+
+TEXT runtime·asminit(SB),NOSPLIT,$0-0
// disable runfast (flush-to-zero) mode of vfp if runtime.goarm > 5
- MOVW runtime·goarm(SB), R11
- CMP $5, R11
- BLE 4(PC)
- WORD $0xeef1ba10 // vmrs r11, fpscr
- BIC $(1<<24), R11
- WORD $0xeee1ba10 // vmsr fpscr, r11
+ MOVW runtime·goarm(SB), R11
+ CMP $5, R11
+ BLE 4(PC)
+ WORD $0xeef1ba10 // vmrs r11, fpscr
+ BIC $(1<<24), R11
+ WORD $0xeee1ba10 // vmsr fpscr, r11
RET
/*
@@ -93,82 +107,63 @@ TEXT runtime·asminit(SB),7,$0
// void gosave(Gobuf*)
// save state in Gobuf; setjmp
-TEXT runtime·gosave(SB), 7, $-4
+TEXT runtime·gosave(SB), NOSPLIT, $-4-4
MOVW 0(FP), R0 // gobuf
MOVW SP, gobuf_sp(R0)
MOVW LR, gobuf_pc(R0)
MOVW g, gobuf_g(R0)
+ MOVW $0, R11
+ MOVW R11, gobuf_lr(R0)
+ MOVW R11, gobuf_ret(R0)
+ MOVW R11, gobuf_ctxt(R0)
RET
-// void gogo(Gobuf*, uintptr)
+// void gogo(Gobuf*)
// restore state from Gobuf; longjmp
-TEXT runtime·gogo(SB), 7, $-4
+TEXT runtime·gogo(SB), NOSPLIT, $-4-4
MOVW 0(FP), R1 // gobuf
MOVW gobuf_g(R1), g
MOVW 0(g), R2 // make sure g != nil
- MOVW _cgo_save_gm(SB), R2
+ MOVB runtime·iscgo(SB), R2
CMP $0, R2 // if in Cgo, we have to save g and m
- BL.NE (R2) // this call will clobber R0
- MOVW 4(FP), R0 // return 2nd arg
+ BL.NE runtime·save_gm(SB) // this call will clobber R0
MOVW gobuf_sp(R1), SP // restore SP
+ MOVW gobuf_lr(R1), LR
+ MOVW gobuf_ret(R1), R0
+ MOVW gobuf_ctxt(R1), R7
+ MOVW $0, R11
+ MOVW R11, gobuf_sp(R1) // clear to help garbage collector
+ MOVW R11, gobuf_ret(R1)
+ MOVW R11, gobuf_lr(R1)
+ MOVW R11, gobuf_ctxt(R1)
+ CMP R11, R11 // set condition codes for == test, needed by stack split
MOVW gobuf_pc(R1), PC
-// void gogocall(Gobuf*, void (*fn)(void), uintptr r7)
-// restore state from Gobuf but then call fn.
-// (call fn, returning to state in Gobuf)
-// using frame size $-4 means do not save LR on stack.
-TEXT runtime·gogocall(SB), 7, $-4
- MOVW 0(FP), R3 // gobuf
- MOVW 4(FP), R1 // fn
- MOVW gobuf_g(R3), g
- MOVW 0(g), R0 // make sure g != nil
- MOVW _cgo_save_gm(SB), R0
- CMP $0, R0 // if in Cgo, we have to save g and m
- BL.NE (R0) // this call will clobber R0
- MOVW 8(FP), R7 // context
- MOVW gobuf_sp(R3), SP // restore SP
- MOVW gobuf_pc(R3), LR
- MOVW R1, PC
-
-// void gogocallfn(Gobuf*, FuncVal*)
-// restore state from Gobuf but then call fn.
-// (call fn, returning to state in Gobuf)
-// using frame size $-4 means do not save LR on stack.
-TEXT runtime·gogocallfn(SB), 7, $-4
- MOVW 0(FP), R3 // gobuf
- MOVW 4(FP), R1 // fn
- MOVW gobuf_g(R3), g
- MOVW 0(g), R0 // make sure g != nil
- MOVW _cgo_save_gm(SB), R0
- CMP $0, R0 // if in Cgo, we have to save g and m
- BL.NE (R0) // this call will clobber R0
- MOVW gobuf_sp(R3), SP // restore SP
- MOVW gobuf_pc(R3), LR
- MOVW R1, R7
- MOVW 0(R1), PC
-
// void mcall(void (*fn)(G*))
// Switch to m->g0's stack, call fn(g).
// Fn must never return. It should gogo(&g->sched)
// to keep running g.
-TEXT runtime·mcall(SB), 7, $-4
+TEXT runtime·mcall(SB), NOSPLIT, $-4-4
MOVW fn+0(FP), R0
- // Save caller state in g->gobuf.
+ // Save caller state in g->sched.
MOVW SP, (g_sched+gobuf_sp)(g)
MOVW LR, (g_sched+gobuf_pc)(g)
+ MOVW $0, R11
+ MOVW R11, (g_sched+gobuf_lr)(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)
+ B.NE 2(PC)
+ B runtime·badmcall(SB)
MOVW (g_sched+gobuf_sp)(g), SP
SUB $8, SP
MOVW R1, 4(SP)
BL (R0)
- BL runtime·badmcall2(SB)
+ B runtime·badmcall2(SB)
RET
/*
@@ -182,18 +177,28 @@ TEXT runtime·mcall(SB), 7, $-4
// NB. we do not save R0 because we've forced 5c to pass all arguments
// on the stack.
// using frame size $-4 means do not save LR on stack.
-TEXT runtime·morestack(SB),7,$-4
+//
+// The traceback routines see morestack on a g0 as being
+// the top of a stack (for example, morestack calling newstack
+// calling the scheduler calling newm calling gc), so we must
+// record an argument size. For that purpose, it has no arguments.
+TEXT runtime·morestack(SB),NOSPLIT,$-4-0
// Cannot grow scheduler stack (m->g0).
MOVW m_g0(m), R4
CMP g, R4
BL.EQ runtime·abort(SB)
- // Save in m.
- MOVW R7, m_cret(m) // function context
MOVW R1, m_moreframesize(m)
MOVW R2, m_moreargsize(m)
// Called from f.
+ // Set g->sched to context in f.
+ MOVW R7, (g_sched+gobuf_ctxt)(g)
+ MOVW SP, (g_sched+gobuf_sp)(g)
+ MOVW LR, (g_sched+gobuf_pc)(g)
+ MOVW R3, (g_sched+gobuf_lr)(g)
+
+ // Called from f.
// Set m->morebuf to f's caller.
MOVW R3, (m_morebuf+gobuf_pc)(m) // f's caller's PC
MOVW SP, (m_morebuf+gobuf_sp)(m) // f's caller's SP
@@ -201,29 +206,37 @@ TEXT runtime·morestack(SB),7,$-4
MOVW R3, m_moreargp(m)
MOVW g, (m_morebuf+gobuf_g)(m)
- // Set m->morepc to f's PC.
- MOVW LR, m_morepc(m)
-
// Call newstack on m->g0's stack.
MOVW m_g0(m), g
MOVW (g_sched+gobuf_sp)(g), SP
- B runtime·newstack(SB)
+ BL runtime·newstack(SB)
-// Called from reflection library. Mimics morestack,
+ // Not reached, but make sure the return PC from the call to newstack
+ // is still in this function, and not the beginning of the next.
+ RET
+
+// Called from panic. Mimics morestack,
// reuses stack growth code to create a frame
// with the desired args running the desired function.
//
// func call(fn *byte, arg *byte, argsize uint32).
-TEXT reflect·call(SB), 7, $-4
+TEXT runtime·newstackcall(SB), NOSPLIT, $-4-12
// Save our caller's state as the PC and SP to
// restore when returning from f.
MOVW LR, (m_morebuf+gobuf_pc)(m) // our caller's PC
MOVW SP, (m_morebuf+gobuf_sp)(m) // our caller's SP
MOVW g, (m_morebuf+gobuf_g)(m)
+ // Save our own state as the PC and SP to restore
+ // if this goroutine needs to be restarted.
+ MOVW $runtime·newstackcall(SB), R11
+ MOVW R11, (g_sched+gobuf_pc)(g)
+ MOVW LR, (g_sched+gobuf_lr)(g)
+ MOVW SP, (g_sched+gobuf_sp)(g)
+
// Set up morestack arguments to call f on a new stack.
// We set f's frame size to 1, as a hint to newstack
- // that this is a call from reflect·call.
+ // that this is a call from runtime·newstackcall.
// If it turns out that f needs a larger frame than
// the default stack, f's usual stack growth prolog will
// allocate a new segment (and recopy the arguments).
@@ -231,7 +244,7 @@ TEXT reflect·call(SB), 7, $-4
MOVW 8(SP), R1 // arg frame
MOVW 12(SP), R2 // arg size
- MOVW R0, m_morepc(m) // f's PC
+ MOVW R0, m_cret(m) // f's PC
MOVW R1, m_moreargp(m) // f's argument pointer
MOVW R2, m_moreargsize(m) // f's argument size
MOVW $1, R3
@@ -242,23 +255,125 @@ TEXT reflect·call(SB), 7, $-4
MOVW (g_sched+gobuf_sp)(g), SP
B runtime·newstack(SB)
+// reflect·call: call a function with the given argument list
+// func call(f *FuncVal, arg *byte, argsize uint32).
+// we don't have variable-sized frames, so we use a small number
+// of constant-sized-frame functions to encode a few bits of size in the pc.
+// Caution: ugly multiline assembly macros in your future!
+
+#define DISPATCH(NAME,MAXSIZE) \
+ CMP $MAXSIZE, R0; \
+ B.HI 3(PC); \
+ MOVW $runtime·NAME(SB), R1; \
+ B (R1)
+
+TEXT reflect·call(SB), NOSPLIT, $-4-12
+ MOVW argsize+8(FP), R0
+ DISPATCH(call16, 16)
+ DISPATCH(call32, 32)
+ DISPATCH(call64, 64)
+ DISPATCH(call128, 128)
+ DISPATCH(call256, 256)
+ DISPATCH(call512, 512)
+ DISPATCH(call1024, 1024)
+ DISPATCH(call2048, 2048)
+ DISPATCH(call4096, 4096)
+ DISPATCH(call8192, 8192)
+ DISPATCH(call16384, 16384)
+ DISPATCH(call32768, 32768)
+ DISPATCH(call65536, 65536)
+ DISPATCH(call131072, 131072)
+ DISPATCH(call262144, 262144)
+ DISPATCH(call524288, 524288)
+ DISPATCH(call1048576, 1048576)
+ DISPATCH(call2097152, 2097152)
+ DISPATCH(call4194304, 4194304)
+ DISPATCH(call8388608, 8388608)
+ DISPATCH(call16777216, 16777216)
+ DISPATCH(call33554432, 33554432)
+ DISPATCH(call67108864, 67108864)
+ DISPATCH(call134217728, 134217728)
+ DISPATCH(call268435456, 268435456)
+ DISPATCH(call536870912, 536870912)
+ DISPATCH(call1073741824, 1073741824)
+ MOVW $runtime·badreflectcall(SB), R1
+ B (R1)
+
+#define CALLFN(NAME,MAXSIZE) \
+TEXT runtime·NAME(SB), WRAPPER, $MAXSIZE-12; \
+ /* copy arguments to stack */ \
+ MOVW argptr+4(FP), R0; \
+ MOVW argsize+8(FP), R2; \
+ ADD $4, SP, R1; \
+ CMP $0, R2; \
+ B.EQ 5(PC); \
+ MOVBU.P 1(R0), R5; \
+ MOVBU.P R5, 1(R1); \
+ SUB $1, R2, R2; \
+ B -5(PC); \
+ /* call function */ \
+ MOVW f+0(FP), R7; \
+ MOVW (R7), R0; \
+ BL (R0); \
+ /* copy return values back */ \
+ MOVW argptr+4(FP), R0; \
+ MOVW argsize+8(FP), R2; \
+ ADD $4, SP, R1; \
+ CMP $0, R2; \
+ RET.EQ ; \
+ MOVBU.P 1(R1), R5; \
+ MOVBU.P R5, 1(R0); \
+ SUB $1, R2, R2; \
+ B -5(PC) \
+
+CALLFN(call16, 16)
+CALLFN(call32, 32)
+CALLFN(call64, 64)
+CALLFN(call128, 128)
+CALLFN(call256, 256)
+CALLFN(call512, 512)
+CALLFN(call1024, 1024)
+CALLFN(call2048, 2048)
+CALLFN(call4096, 4096)
+CALLFN(call8192, 8192)
+CALLFN(call16384, 16384)
+CALLFN(call32768, 32768)
+CALLFN(call65536, 65536)
+CALLFN(call131072, 131072)
+CALLFN(call262144, 262144)
+CALLFN(call524288, 524288)
+CALLFN(call1048576, 1048576)
+CALLFN(call2097152, 2097152)
+CALLFN(call4194304, 4194304)
+CALLFN(call8388608, 8388608)
+CALLFN(call16777216, 16777216)
+CALLFN(call33554432, 33554432)
+CALLFN(call67108864, 67108864)
+CALLFN(call134217728, 134217728)
+CALLFN(call268435456, 268435456)
+CALLFN(call536870912, 536870912)
+CALLFN(call1073741824, 1073741824)
+
// Return point when leaving stack.
// using frame size $-4 means do not save LR on stack.
-TEXT runtime·lessstack(SB), 7, $-4
+//
+// Lessstack can appear in stack traces for the same reason
+// as morestack; in that context, it has 0 arguments.
+TEXT runtime·lessstack(SB), NOSPLIT, $-4-0
// Save return value in m->cret
MOVW R0, m_cret(m)
// Call oldstack on m->g0's stack.
MOVW m_g0(m), g
MOVW (g_sched+gobuf_sp)(g), SP
- B runtime·oldstack(SB)
+ BL runtime·oldstack(SB)
// void jmpdefer(fn, sp);
// called from deferreturn.
// 1. grab stored LR for caller
// 2. sub 4 bytes to get back to BL deferreturn
// 3. B to fn
-TEXT runtime·jmpdefer(SB), 7, $0
+TEXT runtime·jmpdefer(SB), NOSPLIT, $0-8
MOVW 0(SP), LR
MOVW $-4(LR), LR // BL deferreturn
MOVW fn+0(FP), R7
@@ -267,18 +382,21 @@ TEXT runtime·jmpdefer(SB), 7, $0
MOVW 0(R7), R1
B (R1)
-// 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
+// Save state of caller into g->sched. Smashes R11.
+TEXT gosave<>(SB),NOSPLIT,$0
+ MOVW LR, (g_sched+gobuf_pc)(g)
+ MOVW R13, (g_sched+gobuf_sp)(g)
+ MOVW $0, R11
+ MOVW R11, (g_sched+gobuf_lr)(g)
+ MOVW R11, (g_sched+gobuf_ret)(g)
+ MOVW R11, (g_sched+gobuf_ctxt)(g)
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
+TEXT runtime·asmcgocall(SB),NOSPLIT,$0-8
MOVW fn+0(FP), R1
MOVW arg+4(FP), R0
MOVW R13, R2
@@ -289,11 +407,8 @@ TEXT runtime·asmcgocall(SB),7,$0
// come in on the m->g0 stack already.
MOVW m_g0(m), R3
CMP R3, g
- BEQ 7(PC)
- MOVW R13, (g_sched + gobuf_sp)(g)
- MOVW $return<>(SB), R4
- MOVW R4, (g_sched+gobuf_pc)(g)
- MOVW g, (g_sched+gobuf_g)(g)
+ BEQ 4(PC)
+ BL gosave<>(SB)
MOVW R3, g
MOVW (g_sched+gobuf_sp)(g), R13
@@ -313,7 +428,7 @@ TEXT runtime·asmcgocall(SB),7,$0
// cgocallback(void (*fn)(void*), void *frame, uintptr framesize)
// Turn the fn into a Go func (by taking its address) and call
// cgocallback_gofunc.
-TEXT runtime·cgocallback(SB),7,$12
+TEXT runtime·cgocallback(SB),NOSPLIT,$12-12
MOVW $fn+0(FP), R0
MOVW R0, 4(R13)
MOVW frame+4(FP), R0
@@ -326,20 +441,20 @@ TEXT runtime·cgocallback(SB),7,$12
// cgocallback_gofunc(void (*fn)(void*), void *frame, uintptr framesize)
// See cgocall.c for more details.
-TEXT runtime·cgocallback_gofunc(SB),7,$12
+TEXT runtime·cgocallback_gofunc(SB),NOSPLIT,$8-12
// Load m and g from thread-local storage.
- MOVW _cgo_load_gm(SB), R0
+ MOVB runtime·iscgo(SB), R0
CMP $0, R0
- BL.NE (R0)
+ BL.NE runtime·load_gm(SB)
// If m is nil, Go did not create the current thread.
// Call needm to obtain one for temporary use.
// In this case, we're running on the thread stack, so there's
// lots of space, but the linker doesn't know. Hide the call from
// the linker analysis by using an indirect call.
- MOVW m, savedm-12(SP)
+ MOVW m, savedm-4(SP)
CMP $0, m
- B.NE havem
+ B.NE havem
MOVW $runtime·needm(SB), R0
BL (R0)
@@ -348,51 +463,41 @@ havem:
// Save current m->g0->sched.sp on stack and then set it to SP.
// Save current sp in m->g0->sched.sp in preparation for
// switch back to m->curg stack.
+ // NOTE: unwindm knows that the saved g->sched.sp is at 4(R13) aka savedsp-8(SP).
MOVW m_g0(m), R3
MOVW (g_sched+gobuf_sp)(R3), R4
- MOVW.W R4, -4(R13)
+ MOVW R4, savedsp-8(SP)
MOVW R13, (g_sched+gobuf_sp)(R3)
- // Switch to m->curg stack and call runtime.cgocallbackg
- // 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
+ // Switch to m->curg stack and call runtime.cgocallbackg.
+ // 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->sched) so we can restore it.
+ // We can restore m->curg->sched.sp easily, because calling
// runtime.cgocallbackg leaves SP unchanged upon return.
- // To save m->curg->gobuf.pc, we push it onto the stack.
+ // To save m->curg->sched.pc, we push it onto the stack.
// This has the added benefit that it looks to the traceback
// routine like cgocallbackg is going to return to that
- // PC (because we defined cgocallbackg to have
- // a frame size of 12, the same amount that we use below),
+ // PC (because the frame we allocate below has the same
+ // size as cgocallback_gofunc's frame declared above)
// so that the traceback will seamlessly trace back into
// the earlier calls.
+ //
+ // In the new goroutine, -8(SP) and -4(SP) are unused.
MOVW fn+4(FP), R0
MOVW frame+8(FP), R1
MOVW framesize+12(FP), R2
-
MOVW m_curg(m), g
MOVW (g_sched+gobuf_sp)(g), R4 // prepare stack as R4
-
- // Push gobuf.pc
MOVW (g_sched+gobuf_pc)(g), R5
- MOVW.W R5, -16(R4)
-
- // Push arguments to cgocallbackg.
- // Frame size here must match the frame size above
- // to trick traceback routines into doing the right thing.
- MOVW R0, 4(R4)
- MOVW R1, 8(R4)
- MOVW R2, 12(R4)
-
- // Switch stack and make the call.
- MOVW R4, R13
+ MOVW R5, -12(R4)
+ MOVW $-12(R4), R13
BL runtime·cgocallbackg(SB)
- // Restore g->gobuf (== m->curg->gobuf) from saved values.
+ // Restore g->sched (== m->curg->sched) from saved values.
MOVW 0(R13), R5
MOVW R5, (g_sched+gobuf_pc)(g)
- ADD $(12+4), R13, R4
+ MOVW $12(R13), R4
MOVW R4, (g_sched+gobuf_sp)(g)
// Switch back to m->g0's stack and restore m->g0->sched.sp.
@@ -400,14 +505,12 @@ havem:
// so we do not have to restore it.)
MOVW m_g0(m), g
MOVW (g_sched+gobuf_sp)(g), R13
- // POP R6
- MOVW 0(R13), R6
- ADD $4, R13
- MOVW R6, (g_sched+gobuf_sp)(g)
+ MOVW savedsp-8(SP), R4
+ MOVW R4, (g_sched+gobuf_sp)(g)
// If the m on entry was nil, we called needm above to borrow an m
// for the duration of the call. Since the call is over, return it with dropm.
- MOVW savedm-12(SP), R6
+ MOVW savedm-4(SP), R6
CMP $0, R6
B.NE 3(PC)
MOVW $runtime·dropm(SB), R0
@@ -417,35 +520,35 @@ havem:
RET
// void setmg(M*, G*); set m and g. for use by needm.
-TEXT runtime·setmg(SB), 7, $0
+TEXT runtime·setmg(SB), NOSPLIT, $0-8
MOVW mm+0(FP), m
MOVW gg+4(FP), g
// Save m and g to thread-local storage.
- MOVW _cgo_save_gm(SB), R0
+ MOVB runtime·iscgo(SB), R0
CMP $0, R0
- BL.NE (R0)
+ BL.NE runtime·save_gm(SB)
RET
-TEXT runtime·getcallerpc(SB),7,$-4
+TEXT runtime·getcallerpc(SB),NOSPLIT,$-4-4
MOVW 0(SP), R0
RET
-TEXT runtime·setcallerpc(SB),7,$-4
+TEXT runtime·setcallerpc(SB),NOSPLIT,$-4-8
MOVW x+4(FP), R0
MOVW R0, 0(SP)
RET
-TEXT runtime·getcallersp(SB),7,$-4
+TEXT runtime·getcallersp(SB),NOSPLIT,$-4-4
MOVW 0(FP), R0
MOVW $-4(R0), R0
RET
-TEXT runtime·emptyfunc(SB),0,$0
+TEXT runtime·emptyfunc(SB),0,$0-0
RET
-TEXT runtime·abort(SB),7,$-4
+TEXT runtime·abort(SB),NOSPLIT,$-4-0
MOVW $0, R0
MOVW (R0), R1
@@ -460,27 +563,27 @@ TEXT runtime·abort(SB),7,$-4
// To implement runtime·cas in sys_$GOOS_arm.s
// using the native instructions, use:
//
-// TEXT runtime·cas(SB),7,$0
+// TEXT runtime·cas(SB),NOSPLIT,$0
// B runtime·armcas(SB)
//
-TEXT runtime·armcas(SB),7,$0
+TEXT runtime·armcas(SB),NOSPLIT,$0-12
MOVW valptr+0(FP), R1
MOVW old+4(FP), R2
MOVW new+8(FP), R3
casl:
LDREX (R1), R0
- CMP R0, R2
- BNE casfail
+ CMP R0, R2
+ BNE casfail
STREX R3, (R1), R0
- CMP $0, R0
- BNE casl
+ CMP $0, R0
+ BNE casl
MOVW $1, R0
RET
casfail:
MOVW $0, R0
RET
-TEXT runtime·stackguard(SB),7,$0
+TEXT runtime·stackguard(SB),NOSPLIT,$0-8
MOVW R13, R1
MOVW g_stackguard(g), R2
MOVW R1, sp+0(FP)
@@ -488,20 +591,20 @@ TEXT runtime·stackguard(SB),7,$0
RET
// AES hashing not implemented for ARM
-TEXT runtime·aeshash(SB),7,$-4
+TEXT runtime·aeshash(SB),NOSPLIT,$-4-0
MOVW $0, R0
MOVW (R0), R1
-TEXT runtime·aeshash32(SB),7,$-4
+TEXT runtime·aeshash32(SB),NOSPLIT,$-4-0
MOVW $0, R0
MOVW (R0), R1
-TEXT runtime·aeshash64(SB),7,$-4
+TEXT runtime·aeshash64(SB),NOSPLIT,$-4-0
MOVW $0, R0
MOVW (R0), R1
-TEXT runtime·aeshashstr(SB),7,$-4
+TEXT runtime·aeshashstr(SB),NOSPLIT,$-4-0
MOVW $0, R0
MOVW (R0), R1
-TEXT runtime·memeq(SB),7,$-4
+TEXT runtime·memeq(SB),NOSPLIT,$-4-12
MOVW a+0(FP), R1
MOVW b+4(FP), R2
MOVW n+8(FP), R3
@@ -517,3 +620,109 @@ _next:
MOVW $0, R0
RET
+
+// We have to resort to TLS variable to save g(R10) and
+// m(R9). One reason is that external code might trigger
+// SIGSEGV, and our runtime.sigtramp don't even know we
+// are in external code, and will continue to use R10/R9,
+// this might as well result in another SIGSEGV.
+// Note: all three functions will clobber R0, and the last
+// two can be called from 5c ABI code.
+
+// g (R10) at 8(TP), m (R9) at 12(TP)
+TEXT runtime·save_gm(SB),NOSPLIT,$0
+ MRC 15, 0, R0, C13, C0, 3 // Fetch TLS register
+ MOVW g, 8(R0)
+ MOVW m, 12(R0)
+ RET
+
+TEXT runtime·load_gm(SB),NOSPLIT,$0
+ MRC 15, 0, R0, C13, C0, 3 // Fetch TLS register
+ MOVW 8(R0), g
+ MOVW 12(R0), m
+ RET
+
+// void setmg_gcc(M*, G*); set m and g called from gcc.
+TEXT setmg_gcc<>(SB),NOSPLIT,$0
+ MOVW R0, m
+ MOVW R1, g
+ B runtime·save_gm(SB)
+
+
+// TODO: share code with memeq?
+TEXT bytes·Equal(SB),NOSPLIT,$0
+ MOVW a_len+4(FP), R1
+ MOVW b_len+16(FP), R3
+
+ CMP R1, R3 // unequal lengths are not equal
+ B.NE _notequal
+
+ MOVW a+0(FP), R0
+ MOVW b+12(FP), R2
+ ADD R0, R1 // end
+
+_byteseq_next:
+ CMP R0, R1
+ B.EQ _equal // reached the end
+ MOVBU.P 1(R0), R4
+ MOVBU.P 1(R2), R5
+ CMP R4, R5
+ B.EQ _byteseq_next
+
+_notequal:
+ MOVW $0, R0
+ MOVBU R0, ret+24(FP)
+ RET
+
+_equal:
+ MOVW $1, R0
+ MOVBU R0, ret+24(FP)
+ RET
+
+TEXT bytes·IndexByte(SB),NOSPLIT,$0
+ MOVW s+0(FP), R0
+ MOVW s_len+4(FP), R1
+ MOVBU c+12(FP), R2 // byte to find
+ MOVW R0, R4 // store base for later
+ ADD R0, R1 // end
+
+_loop:
+ CMP R0, R1
+ B.EQ _notfound
+ MOVBU.P 1(R0), R3
+ CMP R2, R3
+ B.NE _loop
+
+ SUB $1, R0 // R0 will be one beyond the position we want
+ SUB R4, R0 // remove base
+ MOVW R0, ret+16(FP)
+ RET
+
+_notfound:
+ MOVW $-1, R0
+ MOVW R0, ret+16(FP)
+ RET
+
+TEXT strings·IndexByte(SB),NOSPLIT,$0
+ MOVW s+0(FP), R0
+ MOVW s_len+4(FP), R1
+ MOVBU c+8(FP), R2 // byte to find
+ MOVW R0, R4 // store base for later
+ ADD R0, R1 // end
+
+_sib_loop:
+ CMP R0, R1
+ B.EQ _sib_notfound
+ MOVBU.P 1(R0), R3
+ CMP R2, R3
+ B.NE _sib_loop
+
+ SUB $1, R0 // R0 will be one beyond the position we want
+ SUB R4, R0 // remove base
+ MOVW R0, ret+12(FP)
+ RET
+
+_sib_notfound:
+ MOVW $-1, R0
+ MOVW R0, ret+12(FP)
+ RET
diff --git a/src/pkg/runtime/atomic_386.c b/src/pkg/runtime/atomic_386.c
index 1046eb81e..d7162a1b8 100644
--- a/src/pkg/runtime/atomic_386.c
+++ b/src/pkg/runtime/atomic_386.c
@@ -3,43 +3,44 @@
// license that can be found in the LICENSE file.
#include "runtime.h"
+#include "../../cmd/ld/textflag.h"
-#pragma textflag 7
+#pragma textflag NOSPLIT
uint32
runtime·atomicload(uint32 volatile* addr)
{
return *addr;
}
-#pragma textflag 7
+#pragma textflag NOSPLIT
void*
runtime·atomicloadp(void* volatile* addr)
{
return *addr;
}
-#pragma textflag 7
+#pragma textflag NOSPLIT
uint64
runtime·xadd64(uint64 volatile* addr, int64 v)
{
uint64 old;
- old = *addr;
- while(!runtime·cas64(addr, &old, old+v)) {
- // nothing
- }
+ do
+ old = *addr;
+ while(!runtime·cas64(addr, old, old+v));
+
return old+v;
}
-#pragma textflag 7
+#pragma textflag NOSPLIT
uint64
runtime·xchg64(uint64 volatile* addr, uint64 v)
{
uint64 old;
- old = *addr;
- while(!runtime·cas64(addr, &old, v)) {
- // nothing
- }
+ do
+ old = *addr;
+ while(!runtime·cas64(addr, old, v));
+
return old;
}
diff --git a/src/pkg/runtime/atomic_amd64.c b/src/pkg/runtime/atomic_amd64.c
index e92d8ec21..0bd4d906b 100644
--- a/src/pkg/runtime/atomic_amd64.c
+++ b/src/pkg/runtime/atomic_amd64.c
@@ -3,22 +3,23 @@
// license that can be found in the LICENSE file.
#include "runtime.h"
+#include "../../cmd/ld/textflag.h"
-#pragma textflag 7
+#pragma textflag NOSPLIT
uint32
runtime·atomicload(uint32 volatile* addr)
{
return *addr;
}
-#pragma textflag 7
+#pragma textflag NOSPLIT
uint64
runtime·atomicload64(uint64 volatile* addr)
{
return *addr;
}
-#pragma textflag 7
+#pragma textflag NOSPLIT
void*
runtime·atomicloadp(void* volatile* addr)
{
diff --git a/src/pkg/runtime/atomic_arm.c b/src/pkg/runtime/atomic_arm.c
index a78b1dfe2..b1e97b27d 100644
--- a/src/pkg/runtime/atomic_arm.c
+++ b/src/pkg/runtime/atomic_arm.c
@@ -4,6 +4,7 @@
#include "runtime.h"
#include "arch_GOARCH.h"
+#include "../../cmd/ld/textflag.h"
static struct {
Lock l;
@@ -13,7 +14,7 @@ static struct {
#define LOCK(addr) (&locktab[((uintptr)(addr)>>3)%nelem(locktab)].l)
// Atomic add and return new value.
-#pragma textflag 7
+#pragma textflag NOSPLIT
uint32
runtime·xadd(uint32 volatile *val, int32 delta)
{
@@ -27,7 +28,7 @@ runtime·xadd(uint32 volatile *val, int32 delta)
}
}
-#pragma textflag 7
+#pragma textflag NOSPLIT
uint32
runtime·xchg(uint32 volatile* addr, uint32 v)
{
@@ -40,7 +41,7 @@ runtime·xchg(uint32 volatile* addr, uint32 v)
}
}
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·procyield(uint32 cnt)
{
@@ -50,21 +51,21 @@ runtime·procyield(uint32 cnt)
}
}
-#pragma textflag 7
+#pragma textflag NOSPLIT
uint32
runtime·atomicload(uint32 volatile* addr)
{
return runtime·xadd(addr, 0);
}
-#pragma textflag 7
+#pragma textflag NOSPLIT
void*
runtime·atomicloadp(void* volatile* addr)
{
return (void*)runtime·xadd((uint32 volatile*)addr, 0);
}
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·atomicstorep(void* volatile* addr, void* v)
{
@@ -77,7 +78,7 @@ runtime·atomicstorep(void* volatile* addr, void* v)
}
}
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·atomicstore(uint32 volatile* addr, uint32 v)
{
@@ -90,25 +91,24 @@ runtime·atomicstore(uint32 volatile* addr, uint32 v)
}
}
-#pragma textflag 7
+#pragma textflag NOSPLIT
bool
-runtime·cas64(uint64 volatile *addr, uint64 *old, uint64 new)
+runtime·cas64(uint64 volatile *addr, uint64 old, uint64 new)
{
bool res;
runtime·lock(LOCK(addr));
- if(*addr == *old) {
+ if(*addr == old) {
*addr = new;
res = true;
} else {
- *old = *addr;
res = false;
}
runtime·unlock(LOCK(addr));
return res;
}
-#pragma textflag 7
+#pragma textflag NOSPLIT
uint64
runtime·xadd64(uint64 volatile *addr, int64 delta)
{
@@ -121,7 +121,7 @@ runtime·xadd64(uint64 volatile *addr, int64 delta)
return res;
}
-#pragma textflag 7
+#pragma textflag NOSPLIT
uint64
runtime·xchg64(uint64 volatile *addr, uint64 v)
{
@@ -134,7 +134,7 @@ runtime·xchg64(uint64 volatile *addr, uint64 v)
return res;
}
-#pragma textflag 7
+#pragma textflag NOSPLIT
uint64
runtime·atomicload64(uint64 volatile *addr)
{
@@ -146,7 +146,7 @@ runtime·atomicload64(uint64 volatile *addr)
return res;
}
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·atomicstore64(uint64 volatile *addr, uint64 v)
{
diff --git a/src/pkg/runtime/callback_windows_386.c b/src/pkg/runtime/callback_windows.c
index 880588da6..88ee53bb5 100644
--- a/src/pkg/runtime/callback_windows_386.c
+++ b/src/pkg/runtime/callback_windows.c
@@ -7,24 +7,19 @@
#include "typekind.h"
#include "defs_GOOS_GOARCH.h"
#include "os_GOOS.h"
-
-// Will keep all callbacks in a linked list, so they don't get garbage collected.
-typedef struct Callback Callback;
-struct Callback {
- Callback* link;
- void* gobody;
- byte asmbody;
-};
+#include "zasm_GOOS_GOARCH.h"
typedef struct Callbacks Callbacks;
struct Callbacks {
Lock;
- Callback* link;
- int32 n;
+ WinCallbackContext* ctxt[cb_max];
+ int32 n;
};
static Callbacks cbs;
+WinCallbackContext** runtime·cbctxts; // to simplify access to cbs.ctxt in sys_windows_*.s
+
// Call back from windows dll into go.
byte *
runtime·compilecallback(Eface fn, bool cleanstack)
@@ -32,8 +27,7 @@ runtime·compilecallback(Eface fn, bool cleanstack)
FuncType *ft;
Type *t;
int32 argsize, i, n;
- byte *p;
- Callback *c;
+ WinCallbackContext *c;
if(fn.type == nil || fn.type->kind != KindFunc)
runtime·panicstring("compilecallback: not a function");
@@ -50,59 +44,33 @@ runtime·compilecallback(Eface fn, bool cleanstack)
argsize += sizeof(uintptr);
}
- // compute size of new fn.
- // must match code laid out below.
- n = 1+4; // MOVL fn, AX
- n += 1+4; // MOVL argsize, DX
- n += 1+4; // MOVL callbackasm, CX
- n += 2; // CALL CX
- n += 1; // RET
- if(cleanstack && argsize!=0)
- n += 2; // ... argsize
-
runtime·lock(&cbs);
- for(c = cbs.link; c != nil; c = c->link) {
- if(c->gobody == fn.data) {
+ if(runtime·cbctxts == nil)
+ runtime·cbctxts = &(cbs.ctxt[0]);
+ n = cbs.n;
+ for(i=0; i<n; i++) {
+ if(cbs.ctxt[i]->gobody == fn.data) {
runtime·unlock(&cbs);
- return &c->asmbody;
+ // runtime·callbackasm is just a series of CALL instructions
+ // (each is 5 bytes long), and we want callback to arrive at
+ // correspondent call instruction instead of start of
+ // runtime·callbackasm.
+ return (byte*)runtime·callbackasm + i * 5;
}
}
- if(cbs.n >= 2000)
+ if(n >= cb_max)
runtime·throw("too many callback functions");
- c = runtime·mal(sizeof *c + n);
+ c = runtime·mal(sizeof *c);
c->gobody = fn.data;
- c->link = cbs.link;
- cbs.link = c;
+ c->argsize = argsize;
+ if(cleanstack && argsize!=0)
+ c->restorestack = argsize;
+ else
+ c->restorestack = 0;
+ cbs.ctxt[n] = c;
cbs.n++;
runtime·unlock(&cbs);
- p = &c->asmbody;
-
- // MOVL fn, AX
- *p++ = 0xb8;
- *(uint32*)p = (uint32)(fn.data);
- p += 4;
-
- // MOVL argsize, DX
- *p++ = 0xba;
- *(uint32*)p = argsize;
- p += 4;
-
- // MOVL callbackasm, CX
- *p++ = 0xb9;
- *(uint32*)p = (uint32)runtime·callbackasm;
- p += 4;
-
- // CALL CX
- *p++ = 0xff;
- *p++ = 0xd1;
-
- // RET argsize?
- if(cleanstack && argsize!=0) {
- *p++ = 0xc2;
- *(uint16*)p = argsize;
- } else
- *p = 0xc3;
-
- return &c->asmbody;
+ // as before
+ return (byte*)runtime·callbackasm + n * 5;
}
diff --git a/src/pkg/runtime/callback_windows_amd64.c b/src/pkg/runtime/callback_windows_amd64.c
deleted file mode 100644
index 1a4779291..000000000
--- a/src/pkg/runtime/callback_windows_amd64.c
+++ /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.
-
-#include "runtime.h"
-#include "type.h"
-#include "typekind.h"
-#include "defs_GOOS_GOARCH.h"
-#include "os_GOOS.h"
-
-// Will keep all callbacks in a linked list, so they don't get garbage collected.
-typedef struct Callback Callback;
-struct Callback {
- Callback* link;
- void* gobody;
- byte asmbody;
-};
-
-typedef struct Callbacks Callbacks;
-struct Callbacks {
- Lock;
- Callback* link;
- int32 n;
-};
-
-static Callbacks cbs;
-
-// Call back from windows dll into go.
-byte *
-runtime·compilecallback(Eface fn, bool /*cleanstack*/)
-{
- FuncType *ft;
- Type *t;
- int32 argsize, i, n;
- byte *p;
- Callback *c;
-
- if(fn.type == nil || fn.type->kind != KindFunc)
- runtime·panicstring("compilecallback: not a function");
- ft = (FuncType*)fn.type;
- if(ft->out.len != 1)
- runtime·panicstring("compilecallback: function must have one output parameter");
- if(((Type**)ft->out.array)[0]->size != sizeof(uintptr))
- runtime·panicstring("compilecallback: output parameter size is wrong");
- argsize = 0;
- for(i=0; i<ft->in.len; i++) {
- t = ((Type**)ft->in.array)[i];
- if(t->size > sizeof(uintptr))
- runtime·panicstring("compilecallback: input parameter size is wrong");
- argsize += sizeof(uintptr);
- }
-
- // compute size of new fn.
- // must match code laid out below.
- n = 2+8+1; // MOVQ fn, AX / PUSHQ AX
- n += 2+8+1; // MOVQ argsize, AX / PUSHQ AX
- n += 2+8; // MOVQ callbackasm, AX
- n += 2; // JMP AX
-
- runtime·lock(&cbs);
- for(c = cbs.link; c != nil; c = c->link) {
- if(c->gobody == fn.data) {
- runtime·unlock(&cbs);
- return &c->asmbody;
- }
- }
- if(cbs.n >= 2000)
- runtime·throw("too many callback functions");
- c = runtime·mal(sizeof *c + n);
- c->gobody = fn.data;
- c->link = cbs.link;
- cbs.link = c;
- cbs.n++;
- runtime·unlock(&cbs);
-
- p = &c->asmbody;
-
- // MOVQ fn, AX
- *p++ = 0x48;
- *p++ = 0xb8;
- *(uint64*)p = (uint64)(fn.data);
- p += 8;
- // PUSH AX
- *p++ = 0x50;
-
- // MOVQ argsize, AX
- *p++ = 0x48;
- *p++ = 0xb8;
- *(uint64*)p = argsize;
- p += 8;
- // PUSH AX
- *p++ = 0x50;
-
- // MOVQ callbackasm, AX
- *p++ = 0x48;
- *p++ = 0xb8;
- *(uint64*)p = (uint64)runtime·callbackasm;
- p += 8;
-
- // JMP AX
- *p++ = 0xFF;
- *p = 0xE0;
-
- return &c->asmbody;
-}
diff --git a/src/pkg/runtime/cgo/asm_386.s b/src/pkg/runtime/cgo/asm_386.s
index 7faaa4097..ab2f1d17a 100644
--- a/src/pkg/runtime/cgo/asm_386.s
+++ b/src/pkg/runtime/cgo/asm_386.s
@@ -2,11 +2,13 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../../cmd/ld/textflag.h"
+
/*
* void crosscall2(void (*fn)(void*, int32), void*, int32)
* Save registers and call fn with two arguments.
*/
-TEXT crosscall2(SB),7,$0
+TEXT crosscall2(SB),NOSPLIT,$0
PUSHL BP
MOVL SP, BP
PUSHL BX
diff --git a/src/pkg/runtime/cgo/asm_amd64.s b/src/pkg/runtime/cgo/asm_amd64.s
index 53f7148a2..64f719ab1 100644
--- a/src/pkg/runtime/cgo/asm_amd64.s
+++ b/src/pkg/runtime/cgo/asm_amd64.s
@@ -2,11 +2,13 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../../cmd/ld/textflag.h"
+
/*
* void crosscall2(void (*fn)(void*, int32), void*, int32)
* Save registers and call fn with two arguments.
*/
-TEXT crosscall2(SB),7,$0
+TEXT crosscall2(SB),NOSPLIT,$0
SUBQ $0x58, SP /* keeps stack pointer 32-byte aligned */
MOVQ BX, 0x10(SP)
MOVQ BP, 0x18(SP)
diff --git a/src/pkg/runtime/cgo/asm_arm.s b/src/pkg/runtime/cgo/asm_arm.s
index a6ea0dc07..850b1c6b6 100644
--- a/src/pkg/runtime/cgo/asm_arm.s
+++ b/src/pkg/runtime/cgo/asm_arm.s
@@ -2,22 +2,23 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../../cmd/ld/textflag.h"
+
/*
* void crosscall2(void (*fn)(void*, int32), void*, int32)
* Save registers and call fn with two arguments.
*/
-TEXT crosscall2(SB),7,$-4
+TEXT crosscall2(SB),NOSPLIT,$-4
/*
* We still need to save all callee save register as before, and then
* push 2 args for fn (R1 and R2).
* Also note that at procedure entry in 5c/5g world, 4(R13) will be the
* first arg, so we must push another dummy reg (R0) for 0(R13).
- * Additionally, cgo_tls_set_gm will clobber R0, so we need to save R0
+ * Additionally, runtime·load_gm will clobber R0, so we need to save R0
* nevertheless.
*/
- MOVM.WP [R0, R1, R2, R4, R5, R6, R7, R8, R9, R10, R11, R12, R14], (R13)
- MOVW _cgo_load_gm(SB), R0
- BL (R0)
+ MOVM.WP [R0, R1, R2, R4, R5, R6, R7, R8, m, g, R11, R12, R14], (R13)
+ BL runtime·load_gm(SB)
MOVW PC, R14
MOVW 0(R13), PC
- MOVM.IAW (R13), [R0, R1, R2, R4, R5, R6, R7, R8, R9, R10, R11, R12, PC]
+ MOVM.IAW (R13), [R0, R1, R2, R4, R5, R6, R7, R8, m, g, R11, R12, PC]
diff --git a/src/pkg/runtime/cgo/callbacks.c b/src/pkg/runtime/cgo/callbacks.c
index 524f30428..e91c8bf8a 100644
--- a/src/pkg/runtime/cgo/callbacks.c
+++ b/src/pkg/runtime/cgo/callbacks.c
@@ -4,6 +4,7 @@
#include "../runtime.h"
#include "../cgocall.h"
+#include "../../../cmd/ld/textflag.h"
// These utility functions are available to be called from code
// compiled with gcc via crosscall2.
@@ -47,6 +48,7 @@ _cgo_allocate_internal(uintptr len, byte *ret)
#pragma cgo_export_static _cgo_allocate
#pragma cgo_export_dynamic _cgo_allocate
+#pragma textflag NOSPLIT
void
_cgo_allocate(void *a, int32 n)
{
@@ -76,6 +78,7 @@ _cgo_panic_internal(byte *p)
#pragma cgo_export_static _cgo_panic
#pragma cgo_export_dynamic _cgo_panic
+#pragma textflag NOSPLIT
void
_cgo_panic(void *a, int32 n)
{
diff --git a/src/pkg/runtime/cgo/cgo.go b/src/pkg/runtime/cgo/cgo.go
index e0d538668..258b6fba1 100644
--- a/src/pkg/runtime/cgo/cgo.go
+++ b/src/pkg/runtime/cgo/cgo.go
@@ -12,6 +12,7 @@ package cgo
/*
#cgo darwin LDFLAGS: -lpthread
+#cgo dragonfly LDFLAGS: -lpthread
#cgo freebsd LDFLAGS: -lpthread
#cgo linux LDFLAGS: -lpthread
#cgo netbsd LDFLAGS: -lpthread
diff --git a/src/pkg/runtime/cgo/cgo_arm.c b/src/pkg/runtime/cgo/cgo_arm.c
deleted file mode 100644
index d23f53e77..000000000
--- a/src/pkg/runtime/cgo/cgo_arm.c
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright 2013 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.
-
-#pragma cgo_import_static x_cgo_load_gm
-extern void x_cgo_load_gm(void);
-void (*_cgo_load_gm)(void) = x_cgo_load_gm;
-
-#pragma cgo_import_static x_cgo_save_gm
-extern void x_cgo_save_gm(void);
-void (*_cgo_save_gm)(void) = x_cgo_save_gm;
-
diff --git a/src/pkg/runtime/cgo/dragonfly.c b/src/pkg/runtime/cgo/dragonfly.c
new file mode 100644
index 000000000..acf53e265
--- /dev/null
+++ b/src/pkg/runtime/cgo/dragonfly.c
@@ -0,0 +1,13 @@
+// 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.
+
+// Supply environ and __progname, because we don't
+// link against the standard DragonFly crt0.o and the
+// libc dynamic library needs them.
+
+char *environ[1];
+char *__progname;
+
+#pragma dynexport environ environ
+#pragma dynexport __progname __progname
diff --git a/src/pkg/runtime/cgo/gcc_arm.S b/src/pkg/runtime/cgo/gcc_arm.S
index 3ec6e5d97..17e98d91f 100644
--- a/src/pkg/runtime/cgo/gcc_arm.S
+++ b/src/pkg/runtime/cgo/gcc_arm.S
@@ -12,7 +12,14 @@
#endif
/*
- * void crosscall_arm2(void (*fn)(void), void *g, void *m)
+ * Because the assembler might target an earlier revision of the ISA
+ * by default, we must explicitly specify the ISA revision to ensure
+ * BLX is recognized as a valid instruction.
+ */
+.arch armv5t
+
+/*
+ * void crosscall_arm2(void (*fn)(void), void (*setmg_gcc)(void *m, void *g), void *m, void *g)
*
* Calling into the 5c tool chain, where all registers are caller save.
* Called from standard ARM EABI, where r4-r11 are callee-save, so they
@@ -21,12 +28,12 @@
.globl EXT(crosscall_arm2)
EXT(crosscall_arm2):
push {r4, r5, r6, r7, r8, r9, r10, r11, ip, lr}
- mov r10, r1 // g
- mov r9, r2 // m
- mov r3, r0 // save r0, cgo_tls_set_gm will clobber it
- bl EXT(x_cgo_save_gm) // save current g and m into TLS variable
- mov lr, pc
- mov pc, r3
+ mov r4, r0
+ mov r5, r1
+ mov r0, r2
+ mov r1, r3
+ blx r5 // setmg(m, g)
+ blx r4 // fn()
pop {r4, r5, r6, r7, r8, r9, r10, r11, ip, pc}
.globl EXT(__stack_chk_fail_local)
diff --git a/src/pkg/runtime/cgo/gcc_dragonfly_386.c b/src/pkg/runtime/cgo/gcc_dragonfly_386.c
new file mode 100644
index 000000000..6797824c6
--- /dev/null
+++ b/src/pkg/runtime/cgo/gcc_dragonfly_386.c
@@ -0,0 +1,77 @@
+// 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.
+
+#include <sys/types.h>
+#include <sys/signalvar.h>
+#include <pthread.h>
+#include <signal.h>
+#include <string.h>
+#include "libcgo.h"
+
+static void* threadentry(void*);
+static void (*setmg_gcc)(void*, void*);
+
+void
+x_cgo_init(G *g, void (*setmg)(void*, void*))
+{
+ pthread_attr_t attr;
+ size_t size;
+
+ setmg_gcc = setmg;
+ pthread_attr_init(&attr);
+ pthread_attr_getstacksize(&attr, &size);
+ g->stackguard = (uintptr)&attr - size + 4096;
+ pthread_attr_destroy(&attr);
+}
+
+
+void
+_cgo_sys_thread_start(ThreadStart *ts)
+{
+ pthread_attr_t attr;
+ sigset_t ign, oset;
+ pthread_t p;
+ size_t size;
+ int err;
+
+ SIGFILLSET(ign);
+ sigprocmask(SIG_SETMASK, &ign, &oset);
+
+ pthread_attr_init(&attr);
+ pthread_attr_getstacksize(&attr, &size);
+ ts->g->stackguard = size;
+ err = pthread_create(&p, &attr, threadentry, ts);
+
+ sigprocmask(SIG_SETMASK, &oset, nil);
+
+ if (err != 0) {
+ fprintf(stderr, "runtime/cgo: pthread_create failed: %s\n", strerror(err));
+ abort();
+ }
+}
+
+static void*
+threadentry(void *v)
+{
+ ThreadStart ts;
+
+ ts = *(ThreadStart*)v;
+ free(v);
+
+ ts.g->stackbase = (uintptr)&ts;
+
+ /*
+ * _cgo_sys_thread_start set stackguard to stack size;
+ * change to actual guard pointer.
+ */
+ ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096;
+
+ /*
+ * Set specific keys.
+ */
+ setmg_gcc((void*)ts.m, (void*)ts.g);
+
+ crosscall_386(ts.fn);
+ return nil;
+}
diff --git a/src/pkg/runtime/cgo/gcc_dragonfly_amd64.c b/src/pkg/runtime/cgo/gcc_dragonfly_amd64.c
new file mode 100644
index 000000000..eb342a2ff
--- /dev/null
+++ b/src/pkg/runtime/cgo/gcc_dragonfly_amd64.c
@@ -0,0 +1,77 @@
+// 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.
+
+#include <sys/types.h>
+#include <sys/signalvar.h>
+#include <pthread.h>
+#include <signal.h>
+#include <string.h>
+#include "libcgo.h"
+
+static void* threadentry(void*);
+static void (*setmg_gcc)(void*, void*);
+
+void
+x_cgo_init(G *g, void (*setmg)(void*, void*))
+{
+ pthread_attr_t attr;
+ size_t size;
+
+ setmg_gcc = setmg;
+ pthread_attr_init(&attr);
+ pthread_attr_getstacksize(&attr, &size);
+ g->stackguard = (uintptr)&attr - size + 4096;
+ pthread_attr_destroy(&attr);
+}
+
+void
+_cgo_sys_thread_start(ThreadStart *ts)
+{
+ pthread_attr_t attr;
+ sigset_t ign, oset;
+ pthread_t p;
+ size_t size;
+ int err;
+
+ SIGFILLSET(ign);
+ sigprocmask(SIG_SETMASK, &ign, &oset);
+
+ pthread_attr_init(&attr);
+ pthread_attr_getstacksize(&attr, &size);
+
+ ts->g->stackguard = size;
+ err = pthread_create(&p, &attr, threadentry, ts);
+
+ sigprocmask(SIG_SETMASK, &oset, nil);
+
+ if (err != 0) {
+ fprintf(stderr, "runtime/cgo: pthread_create failed: %s\n", strerror(err));
+ abort();
+ }
+}
+
+static void*
+threadentry(void *v)
+{
+ ThreadStart ts;
+
+ ts = *(ThreadStart*)v;
+ free(v);
+
+ ts.g->stackbase = (uintptr)&ts;
+
+ /*
+ * _cgo_sys_thread_start set stackguard to stack size;
+ * change to actual guard pointer.
+ */
+ ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096;
+
+ /*
+ * Set specific keys.
+ */
+ setmg_gcc((void*)ts.m, (void*)ts.g);
+
+ crosscall_amd64(ts.fn);
+ return nil;
+}
diff --git a/src/pkg/runtime/cgo/gcc_freebsd_arm.c b/src/pkg/runtime/cgo/gcc_freebsd_arm.c
index 73c990c28..211dca75c 100644
--- a/src/pkg/runtime/cgo/gcc_freebsd_arm.c
+++ b/src/pkg/runtime/cgo/gcc_freebsd_arm.c
@@ -8,72 +8,26 @@
#include <string.h>
#include "libcgo.h"
-static void *threadentry(void*);
-
-// We have to resort to TLS variable to save g(R10) and
-// m(R9). One reason is that external code might trigger
-// SIGSEGV, and our runtime.sigtramp don't even know we
-// are in external code, and will continue to use R10/R9,
-// this might as well result in another SIGSEGV.
-// Note: all three functions will clobber R0, and the last
-// two can be called from 5c ABI code.
-void __aeabi_read_tp(void) __attribute__((naked));
-void x_cgo_save_gm(void) __attribute__((naked));
-void x_cgo_load_gm(void) __attribute__((naked));
-
-void
-__aeabi_read_tp(void)
-{
- __asm__ __volatile__ (
#ifdef ARM_TP_ADDRESS
- // ARM_TP_ADDRESS is (ARM_VECTORS_HIGH + 0x1000) or 0xffff1000
- // GCC inline asm doesn't provide a way to provide a constant
- // to "ldr r0, =??" pseudo instruction, so we hardcode the value
- // and check it with cpp.
+// ARM_TP_ADDRESS is (ARM_VECTORS_HIGH + 0x1000) or 0xffff1000
+// and is known to runtime.read_tls_fallback. Verify it with
+// cpp.
#if ARM_TP_ADDRESS != 0xffff1000
#error Wrong ARM_TP_ADDRESS!
#endif
- "ldr r0, =0xffff1000\n\t"
- "ldr r0, [r0]\n\t"
-#else
- "mrc p15, 0, r0, c13, c0, 3\n\t"
#endif
- "mov pc, lr\n\t"
- );
-}
-// g (R10) at 8(TP), m (R9) at 12(TP)
-void
-x_cgo_load_gm(void)
-{
- __asm__ __volatile__ (
- "push {lr}\n\t"
- "bl __aeabi_read_tp\n\t"
- "ldr r10, [r0, #8]\n\t"
- "ldr r9, [r0, #12]\n\t"
- "pop {pc}\n\t"
- );
-}
+static void *threadentry(void*);
-void
-x_cgo_save_gm(void)
-{
- __asm__ __volatile__ (
- "push {lr}\n\t"
- "bl __aeabi_read_tp\n\t"
- "str r10, [r0, #8]\n\t"
- "str r9, [r0, #12]\n\t"
- "pop {pc}\n\t"
- );
-}
+static void (*setmg_gcc)(void*, void*);
void
-x_cgo_init(G *g)
+x_cgo_init(G *g, void (*setmg)(void*, void*))
{
pthread_attr_t attr;
size_t size;
- x_cgo_save_gm(); // save g and m for the initial thread
+ setmg_gcc = setmg;
pthread_attr_init(&attr);
pthread_attr_getstacksize(&attr, &size);
g->stackguard = (uintptr)&attr - size + 4096;
@@ -104,7 +58,7 @@ _cgo_sys_thread_start(ThreadStart *ts)
}
}
-extern void crosscall_arm2(void (*fn)(void), void *g, void *m);
+extern void crosscall_arm2(void (*fn)(void), void (*setmg_gcc)(void*, void*), void *g, void *m);
static void*
threadentry(void *v)
{
@@ -121,6 +75,6 @@ threadentry(void *v)
*/
ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096 * 2;
- crosscall_arm2(ts.fn, (void *)ts.g, (void *)ts.m);
+ crosscall_arm2(ts.fn, setmg_gcc, (void*)ts.m, (void*)ts.g);
return nil;
}
diff --git a/src/pkg/runtime/cgo/gcc_linux_arm.c b/src/pkg/runtime/cgo/gcc_linux_arm.c
index 46a1126ad..9a6e58594 100644
--- a/src/pkg/runtime/cgo/gcc_linux_arm.c
+++ b/src/pkg/runtime/cgo/gcc_linux_arm.c
@@ -8,60 +8,15 @@
static void *threadentry(void*);
-// We have to resort to TLS variable to save g(R10) and
-// m(R9). One reason is that external code might trigger
-// SIGSEGV, and our runtime.sigtramp don't even know we
-// are in external code, and will continue to use R10/R9,
-// this might as well result in another SIGSEGV.
-// Note: all three functions will clobber R0, and the last
-// two can be called from 5c ABI code.
-void __aeabi_read_tp(void) __attribute__((naked));
-void x_cgo_save_gm(void) __attribute__((naked));
-void x_cgo_load_gm(void) __attribute__((naked));
+static void (*setmg_gcc)(void*, void*);
void
-__aeabi_read_tp(void)
-{
- // b __kuser_get_tls @ 0xffff0fe0
- __asm__ __volatile__ (
- "mvn r0, #0xf000\n\t"
- "sub pc, r0, #31\n\t"
- "nop\n\tnop\n\t"
- );
-}
-
-// g (R10) at 8(TP), m (R9) at 12(TP)
-void
-x_cgo_load_gm(void)
-{
- __asm__ __volatile__ (
- "push {lr}\n\t"
- "bl __aeabi_read_tp\n\t"
- "ldr r10, [r0, #8]\n\t"
- "ldr r9, [r0, #12]\n\t"
- "pop {pc}\n\t"
- );
-}
-
-void
-x_cgo_save_gm(void)
-{
- __asm__ __volatile__ (
- "push {lr}\n\t"
- "bl __aeabi_read_tp\n\t"
- "str r10, [r0, #8]\n\t"
- "str r9, [r0, #12]\n\t"
- "pop {pc}\n\t"
- );
-}
-
-void
-x_cgo_init(G *g)
+x_cgo_init(G *g, void (*setmg)(void*, void*))
{
pthread_attr_t attr;
size_t size;
- x_cgo_save_gm(); // save g and m for the initial thread
+ setmg_gcc = setmg;
pthread_attr_init(&attr);
pthread_attr_getstacksize(&attr, &size);
g->stackguard = (uintptr)&attr - size + 4096;
@@ -92,7 +47,7 @@ _cgo_sys_thread_start(ThreadStart *ts)
}
}
-extern void crosscall_arm2(void (*fn)(void), void *g, void *m);
+extern void crosscall_arm2(void (*fn)(void), void (*setmg_gcc)(void*, void*), void*, void*);
static void*
threadentry(void *v)
{
@@ -109,6 +64,6 @@ threadentry(void *v)
*/
ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096 * 2;
- crosscall_arm2(ts.fn, (void *)ts.g, (void *)ts.m);
+ crosscall_arm2(ts.fn, setmg_gcc, (void*)ts.m, (void*)ts.g);
return nil;
}
diff --git a/src/pkg/runtime/cgo/gcc_netbsd_arm.c b/src/pkg/runtime/cgo/gcc_netbsd_arm.c
index d93b531e7..68c8b6e71 100644
--- a/src/pkg/runtime/cgo/gcc_netbsd_arm.c
+++ b/src/pkg/runtime/cgo/gcc_netbsd_arm.c
@@ -10,64 +10,15 @@
static void *threadentry(void*);
-// We have to resort to TLS variable to save g(R10) and
-// m(R9). One reason is that external code might trigger
-// SIGSEGV, and our runtime.sigtramp don't even know we
-// are in external code, and will continue to use R10/R9,
-// this might as well result in another SIGSEGV.
-// Note: all three functions will clobber R0, and the last
-// two can be called from 5c ABI code.
-void __aeabi_read_tp(void) __attribute__((naked));
-void x_cgo_save_gm(void) __attribute__((naked));
-void x_cgo_load_gm(void) __attribute__((naked));
+static void (*setmg_gcc)(void*, void*);
void
-__aeabi_read_tp(void)
-{
- // this function is only allowed to clobber r0
- __asm__ __volatile__ (
- "mrc p15, 0, r0, c13, c0, 3\n\t"
- "cmp r0, #0\n\t"
- "movne pc, lr\n\t"
- "push {r1,r2,r3,r12}\n\t"
- "svc 0x00a0013c\n\t" // _lwp_getprivate
- "pop {r1,r2,r3,r12}\n\t"
- "mov pc, lr\n\t"
- );
-}
-
-// g (R10) at 8(TP), m (R9) at 12(TP)
-void
-x_cgo_load_gm(void)
-{
- __asm__ __volatile__ (
- "push {lr}\n\t"
- "bl __aeabi_read_tp\n\t"
- "ldr r10, [r0, #8]\n\t"
- "ldr r9, [r0, #12]\n\t"
- "pop {pc}\n\t"
- );
-}
-
-void
-x_cgo_save_gm(void)
-{
- __asm__ __volatile__ (
- "push {lr}\n\t"
- "bl __aeabi_read_tp\n\t"
- "str r10, [r0, #8]\n\t"
- "str r9, [r0, #12]\n\t"
- "pop {pc}\n\t"
- );
-}
-
-void
-x_cgo_init(G *g)
+x_cgo_init(G *g, void (*setmg)(void*, void*))
{
pthread_attr_t attr;
size_t size;
- x_cgo_save_gm(); // save g and m for the initial thread
+ setmg_gcc = setmg;
pthread_attr_init(&attr);
pthread_attr_getstacksize(&attr, &size);
g->stackguard = (uintptr)&attr - size + 4096;
@@ -100,7 +51,7 @@ _cgo_sys_thread_start(ThreadStart *ts)
}
}
-extern void crosscall_arm2(void (*fn)(void), void *g, void *m);
+extern void crosscall_arm2(void (*fn)(void), void (*setmg_gcc)(void*, void*), void *g, void *m);
static void*
threadentry(void *v)
{
@@ -117,6 +68,6 @@ threadentry(void *v)
*/
ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096 * 2;
- crosscall_arm2(ts.fn, (void *)ts.g, (void *)ts.m);
+ crosscall_arm2(ts.fn, setmg_gcc, (void*)ts.m, (void*)ts.g);
return nil;
}
diff --git a/src/pkg/runtime/cgo/gcc_setenv.c b/src/pkg/runtime/cgo/gcc_setenv.c
index a0938166d..8b128b946 100644
--- a/src/pkg/runtime/cgo/gcc_setenv.c
+++ b/src/pkg/runtime/cgo/gcc_setenv.c
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd
#include "libcgo.h"
diff --git a/src/pkg/runtime/cgo/gcc_util.c b/src/pkg/runtime/cgo/gcc_util.c
index 20913d736..143734e94 100644
--- a/src/pkg/runtime/cgo/gcc_util.c
+++ b/src/pkg/runtime/cgo/gcc_util.c
@@ -14,6 +14,8 @@ x_cgo_malloc(void *p)
} *a = p;
a->ret = malloc(a->n);
+ if(a->ret == NULL && a->n == 0)
+ a->ret = malloc(1);
}
/* Stub for calling free from Go */
diff --git a/src/pkg/runtime/cgo/iscgo.c b/src/pkg/runtime/cgo/iscgo.c
index eb6f5c09d..0907a1958 100644
--- a/src/pkg/runtime/cgo/iscgo.c
+++ b/src/pkg/runtime/cgo/iscgo.c
@@ -12,3 +12,4 @@
#include "../runtime.h"
bool runtime·iscgo = 1;
+uint32 runtime·needextram = 1; // create an extra M on first cgo call
diff --git a/src/pkg/runtime/cgo/setenv.c b/src/pkg/runtime/cgo/setenv.c
index 4c47cdb00..2d03db09f 100644
--- a/src/pkg/runtime/cgo/setenv.c
+++ b/src/pkg/runtime/cgo/setenv.c
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd
#pragma cgo_import_static x_cgo_setenv
diff --git a/src/pkg/runtime/cgocall.c b/src/pkg/runtime/cgocall.c
index b82966546..2a04453fd 100644
--- a/src/pkg/runtime/cgocall.c
+++ b/src/pkg/runtime/cgocall.c
@@ -7,6 +7,7 @@
#include "stack.h"
#include "cgocall.h"
#include "race.h"
+#include "../../cmd/ld/textflag.h"
// Cgo call and callback support.
//
@@ -86,11 +87,6 @@
void *_cgo_init; /* filled in by dynamic linker when Cgo is available */
static int64 cgosync; /* represents possible synchronization in C code */
-// These two are only used by the architecture where TLS based storage isn't
-// the default for g and m (e.g., ARM)
-void *_cgo_load_gm; /* filled in by dynamic linker when Cgo is available */
-void *_cgo_save_gm; /* filled in by dynamic linker when Cgo is available */
-
static void unwindm(void);
// Call from Go to C.
@@ -98,15 +94,6 @@ static void unwindm(void);
static void endcgo(void);
static FuncVal endcgoV = { endcgo };
-// Gives a hint that the next syscall
-// executed by the current goroutine will block.
-// Currently used only on windows.
-void
-net·runtime_blockingSyscallHint(void)
-{
- g->blockingsyscall = true;
-}
-
void
runtime·cgocall(void (*fn)(void*), void *arg)
{
@@ -126,6 +113,10 @@ runtime·cgocall(void (*fn)(void*), void *arg)
if(raceenabled)
runtime·racereleasemerge(&cgosync);
+ // Create an extra M for callbacks on threads not created by Go on first cgo call.
+ if(runtime·needextram && runtime·cas(&runtime·needextram, 1, 0))
+ runtime·newextram();
+
m->ncgocall++;
/*
@@ -154,11 +145,7 @@ runtime·cgocall(void (*fn)(void*), void *arg)
* so it is safe to call while "in a system call", outside
* the $GOMAXPROCS accounting.
*/
- if(g->blockingsyscall) {
- g->blockingsyscall = false;
- runtime·entersyscallblock();
- } else
- runtime·entersyscall();
+ runtime·entersyscall();
runtime·asmcgocall(fn, arg);
runtime·exitsyscall();
@@ -211,6 +198,8 @@ runtime·cmalloc(uintptr n)
a.n = n;
a.ret = nil;
runtime·cgocall(_cgo_malloc, &a);
+ if(a.ret == nil)
+ runtime·throw("runtime: C malloc failed");
return a.ret;
}
@@ -224,20 +213,66 @@ runtime·cfree(void *p)
static FuncVal unwindmf = {unwindm};
+typedef struct CallbackArgs CallbackArgs;
+struct CallbackArgs
+{
+ FuncVal *fn;
+ void *arg;
+ uintptr argsize;
+};
+
+// Location of callback arguments depends on stack frame layout
+// and size of stack frame of cgocallback_gofunc.
+
+// On arm, stack frame is two words and there's a saved LR between
+// SP and the stack frame and between the stack frame and the arguments.
+#ifdef GOARCH_arm
+#define CBARGS (CallbackArgs*)((byte*)m->g0->sched.sp+4*sizeof(void*))
+#endif
+
+// On amd64, stack frame is one word, plus caller PC.
+#ifdef GOARCH_amd64
+#define CBARGS (CallbackArgs*)((byte*)m->g0->sched.sp+2*sizeof(void*))
+#endif
+
+// On 386, stack frame is three words, plus caller PC.
+#ifdef GOARCH_386
+#define CBARGS (CallbackArgs*)((byte*)m->g0->sched.sp+4*sizeof(void*))
+#endif
+
+void runtime·cgocallbackg1(void);
+
+#pragma textflag NOSPLIT
void
-runtime·cgocallbackg(FuncVal *fn, void *arg, uintptr argsize)
+runtime·cgocallbackg(void)
{
- Defer d;
+ if(g != m->curg) {
+ runtime·prints("runtime: bad g in cgocallback");
+ runtime·exit(2);
+ }
if(m->racecall) {
- reflect·call(fn, arg, argsize);
- return;
+ // We were not in syscall, so no need to call runtime·exitsyscall.
+ // However we must set m->locks for the following reason.
+ // Race detector runtime makes __tsan_symbolize cgo callback
+ // holding internal mutexes. The mutexes are not cooperative with Go scheduler.
+ // So if we deschedule a goroutine that holds race detector internal mutex
+ // (e.g. preempt it), another goroutine will deadlock trying to acquire the same mutex.
+ m->locks++;
+ runtime·cgocallbackg1();
+ m->locks--;
+ } else {
+ runtime·exitsyscall(); // coming out of cgo call
+ runtime·cgocallbackg1();
+ runtime·entersyscall(); // going back to cgo call
}
+}
- if(g != m->curg)
- runtime·throw("runtime: bad g in cgocallback");
-
- runtime·exitsyscall(); // coming out of cgo call
+void
+runtime·cgocallbackg1(void)
+{
+ CallbackArgs *cb;
+ Defer d;
if(m->needextram) {
m->needextram = 0;
@@ -253,13 +288,14 @@ runtime·cgocallbackg(FuncVal *fn, void *arg, uintptr argsize)
d.free = false;
g->defer = &d;
- if(raceenabled)
+ if(raceenabled && !m->racecall)
runtime·raceacquire(&cgosync);
// Invoke callback.
- reflect·call(fn, arg, argsize);
+ cb = CBARGS;
+ runtime·newstackcall(cb->fn, cb->arg, cb->argsize);
- if(raceenabled)
+ if(raceenabled && !m->racecall)
runtime·racereleasemerge(&cgosync);
// Pop defer.
@@ -268,8 +304,6 @@ runtime·cgocallbackg(FuncVal *fn, void *arg, uintptr argsize)
if(g->defer != &d || d.fn != &unwindmf)
runtime·throw("runtime: bad defer entry in cgocallback");
g->defer = d.link;
-
- runtime·entersyscall(); // going back to cgo call
}
static void
@@ -282,9 +316,11 @@ unwindm(void)
runtime·throw("runtime: unwindm not implemented");
case '8':
case '6':
- case '5':
m->g0->sched.sp = *(uintptr*)m->g0->sched.sp;
break;
+ case '5':
+ m->g0->sched.sp = *(uintptr*)((byte*)m->g0->sched.sp + 4);
+ break;
}
}
diff --git a/src/pkg/runtime/chan.c b/src/pkg/runtime/chan.c
index fba36a4c3..48cc41e20 100644
--- a/src/pkg/runtime/chan.c
+++ b/src/pkg/runtime/chan.c
@@ -7,12 +7,11 @@
#include "type.h"
#include "race.h"
#include "malloc.h"
+#include "../../cmd/ld/textflag.h"
-#define MAXALIGN 7
+#define MAXALIGN 8
#define NOSELGEN 1
-static int32 debug = 0;
-
typedef struct WaitQ WaitQ;
typedef struct SudoG SudoG;
typedef struct Select Select;
@@ -40,8 +39,8 @@ struct Hchan
uintgo qcount; // total data in the q
uintgo dataqsiz; // size of the circular q
uint16 elemsize;
+ uint16 pad; // ensures proper alignment of the buffer that follows Hchan in memory
bool closed;
- uint8 elemalign;
Alg* elemalg; // interface for element type
uintgo sendx; // send index
uintgo recvx; // receive index
@@ -58,6 +57,8 @@ uint32 runtime·Hchansize = sizeof(Hchan);
enum
{
+ debug = 0,
+
// Scase.kind
CaseRecv,
CaseSend,
@@ -93,7 +94,6 @@ Hchan*
runtime·makechan_c(ChanType *t, int64 hint)
{
Hchan *c;
- uintptr n;
Type *elem;
elem = t->elem;
@@ -101,26 +101,21 @@ runtime·makechan_c(ChanType *t, int64 hint)
// compiler checks this but be safe.
if(elem->size >= (1<<16))
runtime·throw("makechan: invalid channel element type");
+ if((sizeof(*c)%MAXALIGN) != 0 || elem->align > MAXALIGN)
+ runtime·throw("makechan: bad alignment");
if(hint < 0 || (intgo)hint != hint || (elem->size > 0 && hint > MaxMem / elem->size))
runtime·panicstring("makechan: size out of range");
- // calculate rounded size of Hchan
- n = sizeof(*c);
- while(n & MAXALIGN)
- n++;
-
// allocate memory in one call
- c = (Hchan*)runtime·mal(n + hint*elem->size);
+ c = (Hchan*)runtime·mallocgc(sizeof(*c) + hint*elem->size, (uintptr)t | TypeInfo_Chan, 0);
c->elemsize = elem->size;
c->elemalg = elem->alg;
- c->elemalign = elem->align;
c->dataqsiz = hint;
- runtime·settype(c, (uintptr)t | TypeInfo_Chan);
if(debug)
- runtime·printf("makechan: chan=%p; elemsize=%D; elemalg=%p; elemalign=%d; dataqsiz=%D\n",
- c, (int64)elem->size, elem->alg, elem->align, (int64)c->dataqsiz);
+ runtime·printf("makechan: chan=%p; elemsize=%D; elemalg=%p; dataqsiz=%D\n",
+ c, (int64)elem->size, elem->alg, (int64)c->dataqsiz);
return c;
}
@@ -174,9 +169,6 @@ runtime·chansend(ChanType *t, Hchan *c, byte *ep, bool *pres, void *pc)
return; // not reached
}
- if(runtime·gcwaiting)
- runtime·gosched();
-
if(debug) {
runtime·printf("chansend: chan=%p; elem=", c);
c->elemalg->print(c->elemsize, ep);
@@ -191,7 +183,6 @@ runtime·chansend(ChanType *t, Hchan *c, byte *ep, bool *pres, void *pc)
}
runtime·lock(c);
- // TODO(dvyukov): add similar instrumentation to select.
if(raceenabled)
runtime·racereadpc(c, pc, runtime·chansend);
if(c->closed)
@@ -301,9 +292,6 @@ runtime·chanrecv(ChanType *t, Hchan* c, byte *ep, bool *selected, bool *receive
G *gp;
int64 t0;
- if(runtime·gcwaiting)
- runtime·gosched();
-
if(debug)
runtime·printf("chanrecv: chan=%p\n", c);
@@ -443,7 +431,7 @@ closed:
}
// chansend1(hchan *chan any, elem any);
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·chansend1(ChanType *t, Hchan* c, ...)
{
@@ -451,7 +439,7 @@ runtime·chansend1(ChanType *t, Hchan* c, ...)
}
// chanrecv1(hchan *chan any) (elem any);
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·chanrecv1(ChanType *t, Hchan* c, ...)
{
@@ -459,7 +447,7 @@ runtime·chanrecv1(ChanType *t, Hchan* c, ...)
}
// chanrecv2(hchan *chan any) (elem any, received bool);
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·chanrecv2(ChanType *t, Hchan* c, ...)
{
@@ -489,7 +477,7 @@ runtime·chanrecv2(ChanType *t, Hchan* c, ...)
// ... bar
// }
//
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·selectnbsend(ChanType *t, Hchan *c, ...)
{
@@ -519,7 +507,7 @@ runtime·selectnbsend(ChanType *t, Hchan *c, ...)
// ... bar
// }
//
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·selectnbrecv(ChanType *t, byte *v, Hchan *c, bool selected)
{
@@ -545,7 +533,7 @@ runtime·selectnbrecv(ChanType *t, byte *v, Hchan *c, bool selected)
// ... bar
// }
//
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·selectnbrecv2(ChanType *t, byte *v, bool *received, Hchan *c, bool selected)
{
@@ -559,7 +547,7 @@ runtime·selectnbrecv2(ChanType *t, byte *v, bool *received, Hchan *c, bool sele
//
// The "uintptr selected" is really "bool selected" but saying
// uintptr gets us the right alignment for the output parameter block.
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
reflect·chansend(ChanType *t, Hchan *c, uintptr val, bool nb, uintptr selected)
{
@@ -615,7 +603,7 @@ reflect·chanrecv(ChanType *t, Hchan *c, bool nb, uintptr val, bool selected, bo
static void newselect(int32, Select**);
// newselect(size uint32) (sel *byte);
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·newselect(int32 size, ...)
{
@@ -660,7 +648,7 @@ newselect(int32 size, Select **selp)
static void selectsend(Select *sel, Hchan *c, void *pc, void *elem, int32 so);
// selectsend(sel *byte, hchan *chan any, elem *any) (selected bool);
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·selectsend(Select *sel, Hchan *c, void *elem, bool selected)
{
@@ -701,7 +689,7 @@ selectsend(Select *sel, Hchan *c, void *pc, void *elem, int32 so)
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
+#pragma textflag NOSPLIT
void
runtime·selectrecv(Select *sel, Hchan *c, void *elem, bool selected)
{
@@ -716,7 +704,7 @@ runtime·selectrecv(Select *sel, Hchan *c, void *elem, bool selected)
}
// selectrecv2(sel *byte, hchan *chan any, elem *any, received *bool) (selected bool);
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·selectrecv2(Select *sel, Hchan *c, void *elem, bool *received, bool selected)
{
@@ -758,7 +746,7 @@ selectrecv(Select *sel, Hchan *c, void *pc, void *elem, bool *received, int32 so
static void selectdefault(Select*, void*, int32);
// selectdefault(sel *byte) (selected bool);
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·selectdefault(Select *sel, bool selected)
{
@@ -845,7 +833,7 @@ static void* selectgo(Select**);
//
// overwrites return pc on stack to signal which case of the select
// to run, so cannot appear at the top of a split stack.
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·selectgo(Select *sel)
{
@@ -857,6 +845,7 @@ selectgo(Select **selp)
{
Select *sel;
uint32 o, i, j, k;
+ int64 t0;
Scase *cas, *dfl;
Hchan *c;
SudoG *sg;
@@ -865,12 +854,17 @@ selectgo(Select **selp)
void *pc;
sel = *selp;
- if(runtime·gcwaiting)
- runtime·gosched();
if(debug)
runtime·printf("select: sel=%p\n", sel);
+ t0 = 0;
+ if(runtime·blockprofilerate > 0) {
+ t0 = runtime·cputicks();
+ for(i=0; i<sel->ncase; i++)
+ sel->scase[i].sg.releasetime = -1;
+ }
+
// The compiler rewrites selects that statically have
// only 0 or 1 cases plus default into simpler constructs.
// The only way we can end up with such small sel->ncase
@@ -951,6 +945,8 @@ loop:
break;
case CaseSend:
+ if(raceenabled)
+ runtime·racereadpc(c, cas->pc, runtime·chansend);
if(c->closed)
goto sclose;
if(c->dataqsiz > 0) {
@@ -1052,6 +1048,8 @@ asyncrecv:
if(sg != nil) {
gp = sg->g;
selunlock(sel);
+ if(sg->releasetime)
+ sg->releasetime = runtime·cputicks();
runtime·ready(gp);
} else {
selunlock(sel);
@@ -1070,6 +1068,8 @@ asyncsend:
if(sg != nil) {
gp = sg->g;
selunlock(sel);
+ if(sg->releasetime)
+ sg->releasetime = runtime·cputicks();
runtime·ready(gp);
} else {
selunlock(sel);
@@ -1089,6 +1089,8 @@ syncrecv:
c->elemalg->copy(c->elemsize, cas->sg.elem, sg->elem);
gp = sg->g;
gp->param = sg;
+ if(sg->releasetime)
+ sg->releasetime = runtime·cputicks();
runtime·ready(gp);
goto retc;
@@ -1114,6 +1116,8 @@ syncsend:
c->elemalg->copy(c->elemsize, sg->elem, cas->sg.elem);
gp = sg->g;
gp->param = sg;
+ if(sg->releasetime)
+ sg->releasetime = runtime·cputicks();
runtime·ready(gp);
retc:
@@ -1127,6 +1131,8 @@ retc:
as = (byte*)selp + cas->so;
*as = true;
}
+ if(cas->sg.releasetime > 0)
+ runtime·blockevent(cas->sg.releasetime - t0, 2);
runtime·free(sel);
return pc;
@@ -1218,20 +1224,34 @@ reflect·rselect(Slice cases, intgo chosen, uintptr word, bool recvOK)
FLUSH(&recvOK);
}
+static void closechan(Hchan *c, void *pc);
+
// closechan(sel *byte);
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·closechan(Hchan *c)
{
+ closechan(c, runtime·getcallerpc(&c));
+}
+
+// For reflect
+// func chanclose(c chan)
+#pragma textflag NOSPLIT
+void
+reflect·chanclose(Hchan *c)
+{
+ closechan(c, runtime·getcallerpc(&c));
+}
+
+static void
+closechan(Hchan *c, void *pc)
+{
SudoG *sg;
G* gp;
if(c == nil)
runtime·panicstring("close of nil channel");
- if(runtime·gcwaiting)
- runtime·gosched();
-
runtime·lock(c);
if(c->closed) {
runtime·unlock(c);
@@ -1239,7 +1259,7 @@ runtime·closechan(Hchan *c)
}
if(raceenabled) {
- runtime·racewritepc(c, runtime·getcallerpc(&c), runtime·closechan);
+ runtime·racewritepc(c, pc, runtime·closechan);
runtime·racerelease(c);
}
@@ -1252,6 +1272,8 @@ runtime·closechan(Hchan *c)
break;
gp = sg->g;
gp->param = nil;
+ if(sg->releasetime)
+ sg->releasetime = runtime·cputicks();
runtime·ready(gp);
}
@@ -1262,6 +1284,8 @@ runtime·closechan(Hchan *c)
break;
gp = sg->g;
gp->param = nil;
+ if(sg->releasetime)
+ sg->releasetime = runtime·cputicks();
runtime·ready(gp);
}
@@ -1269,14 +1293,6 @@ runtime·closechan(Hchan *c)
}
// For reflect
-// func chanclose(c chan)
-void
-reflect·chanclose(Hchan *c)
-{
- runtime·closechan(c);
-}
-
-// For reflect
// func chanlen(c chan) (len int)
void
reflect·chanlen(Hchan *c, intgo len)
diff --git a/src/pkg/runtime/cpuprof.c b/src/pkg/runtime/cpuprof.c
index 9a0606a22..1c34b9e6f 100644
--- a/src/pkg/runtime/cpuprof.c
+++ b/src/pkg/runtime/cpuprof.c
@@ -127,10 +127,6 @@ runtime·SetCPUProfileRate(intgo hz)
{
uintptr *p;
uintptr n;
-
- // Call findfunc now so that it won't have to
- // build tables during the signal handler.
- runtime·findfunc(0);
// Clamp hz to something reasonable.
if(hz < 0)
@@ -141,7 +137,7 @@ runtime·SetCPUProfileRate(intgo hz)
runtime·lock(&lk);
if(hz > 0) {
if(prof == nil) {
- prof = runtime·SysAlloc(sizeof *prof);
+ prof = runtime·SysAlloc(sizeof *prof, &mstats.other_sys);
if(prof == nil) {
runtime·printf("runtime: cpu profiling cannot allocate memory\n");
runtime·unlock(&lk);
@@ -335,7 +331,7 @@ getprofile(Profile *p)
if(p->wholding) {
// Release previous log to signal handling side.
- // Loop because we are racing against setprofile(off).
+ // Loop because we are racing against SetCPUProfileRate(0).
for(;;) {
n = p->handoff;
if(n == 0) {
@@ -362,9 +358,7 @@ getprofile(Profile *p)
return ret;
// Wait for new log.
- runtime·entersyscallblock();
- runtime·notesleep(&p->wait);
- runtime·exitsyscall();
+ runtime·notetsleepg(&p->wait, -1);
runtime·noteclear(&p->wait);
n = p->handoff;
diff --git a/src/pkg/runtime/crash_cgo_test.go b/src/pkg/runtime/crash_cgo_test.go
index 8ccea8f37..4ff0084c2 100644
--- a/src/pkg/runtime/crash_cgo_test.go
+++ b/src/pkg/runtime/crash_cgo_test.go
@@ -7,6 +7,7 @@
package runtime_test
import (
+ "runtime"
"testing"
)
@@ -15,6 +16,9 @@ func TestCgoCrashHandler(t *testing.T) {
}
func TestCgoSignalDeadlock(t *testing.T) {
+ if testing.Short() && runtime.GOOS == "windows" {
+ t.Skip("Skipping in short mode") // takes up to 64 seconds
+ }
got := executeTest(t, cgoSignalDeadlockSource, nil)
want := "OK\n"
if got != want {
@@ -22,6 +26,14 @@ func TestCgoSignalDeadlock(t *testing.T) {
}
}
+func TestCgoTraceback(t *testing.T) {
+ got := executeTest(t, cgoTracebackSource, nil)
+ want := "OK\n"
+ if got != want {
+ t.Fatalf("expected %q, but got %q", want, got)
+ }
+}
+
const cgoSignalDeadlockSource = `
package main
@@ -86,3 +98,22 @@ func main() {
fmt.Printf("OK\n")
}
`
+
+const cgoTracebackSource = `
+package main
+
+/* void foo(void) {} */
+import "C"
+
+import (
+ "fmt"
+ "runtime"
+)
+
+func main() {
+ C.foo()
+ buf := make([]byte, 1)
+ runtime.Stack(buf, true)
+ fmt.Printf("OK\n")
+}
+`
diff --git a/src/pkg/runtime/crash_test.go b/src/pkg/runtime/crash_test.go
index 929d4a963..5476924bb 100644
--- a/src/pkg/runtime/crash_test.go
+++ b/src/pkg/runtime/crash_test.go
@@ -14,7 +14,7 @@ import (
"text/template"
)
-// testEnv excludes GOGCTRACE from the environment
+// testEnv excludes GODEBUG from the environment
// to prevent its output from breaking tests that
// are trying to parse other command output.
func testEnv(cmd *exec.Cmd) *exec.Cmd {
@@ -22,7 +22,7 @@ func testEnv(cmd *exec.Cmd) *exec.Cmd {
panic("environment already set")
}
for _, env := range os.Environ() {
- if strings.HasPrefix(env, "GOGCTRACE=") {
+ if strings.HasPrefix(env, "GODEBUG=") {
continue
}
cmd.Env = append(cmd.Env, env)
@@ -44,14 +44,16 @@ func executeTest(t *testing.T, templ string, data interface{}) string {
src := filepath.Join(dir, "main.go")
f, err := os.Create(src)
if err != nil {
- t.Fatalf("failed to create %v: %v", src, err)
+ t.Fatalf("failed to create file: %v", err)
}
err = st.Execute(f, data)
if err != nil {
f.Close()
t.Fatalf("failed to execute template: %v", err)
}
- f.Close()
+ if err := f.Close(); err != nil {
+ t.Fatalf("failed to close file: %v", err)
+ }
got, _ := testEnv(exec.Command("go", "run", src)).CombinedOutput()
return string(got)
@@ -72,10 +74,10 @@ func testCrashHandler(t *testing.T, cgo bool) {
type crashTest struct {
Cgo bool
}
- got := executeTest(t, crashSource, &crashTest{Cgo: cgo})
+ output := executeTest(t, crashSource, &crashTest{Cgo: cgo})
want := "main: recovered done\nnew-thread: recovered done\nsecond-new-thread: recovered done\nmain-again: recovered done\n"
- if got != want {
- t.Fatalf("expected %q, but got %q", want, got)
+ if output != want {
+ t.Fatalf("output:\n%s\n\nwanted:\n%s", output, want)
}
}
@@ -84,10 +86,10 @@ func TestCrashHandler(t *testing.T) {
}
func testDeadlock(t *testing.T, source string) {
- got := executeTest(t, source, nil)
+ output := executeTest(t, source, nil)
want := "fatal error: all goroutines are asleep - deadlock!\n"
- if !strings.HasPrefix(got, want) {
- t.Fatalf("expected %q, but got %q", want, got)
+ if !strings.HasPrefix(output, want) {
+ t.Fatalf("output does not start with %q:\n%s", want, output)
}
}
@@ -108,10 +110,25 @@ func TestLockedDeadlock2(t *testing.T) {
}
func TestGoexitDeadlock(t *testing.T) {
- got := executeTest(t, goexitDeadlockSource, nil)
- want := ""
- if got != want {
- t.Fatalf("expected %q, but got %q", want, got)
+ output := executeTest(t, goexitDeadlockSource, nil)
+ if output != "" {
+ t.Fatalf("expected no output, got:\n%s", output)
+ }
+}
+
+func TestStackOverflow(t *testing.T) {
+ output := executeTest(t, stackOverflowSource, nil)
+ want := "runtime: goroutine stack exceeds 4194304-byte limit\nfatal error: stack overflow"
+ if !strings.HasPrefix(output, want) {
+ t.Fatalf("output does not start with %q:\n%s", want, output)
+ }
+}
+
+func TestThreadExhaustion(t *testing.T) {
+ output := executeTest(t, threadExhaustionSource, nil)
+ want := "runtime: program exceeds 10-thread limit\nfatal error: thread exhaustion"
+ if !strings.HasPrefix(output, want) {
+ t.Fatalf("output does not start with %q:\n%s", want, output)
}
}
@@ -217,3 +234,41 @@ func main() {
runtime.Goexit()
}
`
+
+const stackOverflowSource = `
+package main
+
+import "runtime/debug"
+
+func main() {
+ debug.SetMaxStack(4<<20)
+ f(make([]byte, 10))
+}
+
+func f(x []byte) byte {
+ var buf [64<<10]byte
+ return x[0] + f(buf[:])
+}
+`
+
+const threadExhaustionSource = `
+package main
+
+import (
+ "runtime"
+ "runtime/debug"
+)
+
+func main() {
+ debug.SetMaxThreads(10)
+ c := make(chan int)
+ for i := 0; i < 100; i++ {
+ go func() {
+ runtime.LockOSThread()
+ c <- 0
+ select{}
+ }()
+ <-c
+ }
+}
+`
diff --git a/src/pkg/runtime/debug/garbage.go b/src/pkg/runtime/debug/garbage.go
index 8f3026426..8337d5d5b 100644
--- a/src/pkg/runtime/debug/garbage.go
+++ b/src/pkg/runtime/debug/garbage.go
@@ -24,6 +24,8 @@ func readGCStats(*[]time.Duration)
func enableGC(bool) bool
func setGCPercent(int) int
func freeOSMemory()
+func setMaxStack(int) int
+func setMaxThreads(int) int
// ReadGCStats reads statistics about garbage collection into stats.
// The number of entries in the pause history is system-dependent;
@@ -99,3 +101,35 @@ func SetGCPercent(percent int) int {
func FreeOSMemory() {
freeOSMemory()
}
+
+// SetMaxStack sets the maximum amount of memory that
+// can be used by a single goroutine stack.
+// If any goroutine exceeds this limit while growing its stack,
+// the program crashes.
+// SetMaxStack returns the previous setting.
+// The initial setting is 1 GB on 64-bit systems, 250 MB on 32-bit systems.
+//
+// SetMaxStack is useful mainly for limiting the damage done by
+// goroutines that enter an infinite recursion. It only limits future
+// stack growth.
+func SetMaxStack(bytes int) int {
+ return setMaxStack(bytes)
+}
+
+// SetMaxThreads sets the maximum number of operating system
+// threads that the Go program can use. If it attempts to use more than
+// this many, the program crashes.
+// SetMaxThreads returns the previous setting.
+// The initial setting is 10,000 threads.
+//
+// The limit controls the number of operating system threads, not the number
+// of goroutines. A Go program creates a new thread only when a goroutine
+// is ready to run but all the existing threads are blocked in system calls, cgo calls,
+// or are locked to other goroutines due to use of runtime.LockOSThread.
+//
+// SetMaxThreads is useful mainly for limiting the damage done by
+// programs that create an unbounded number of threads. The idea is
+// to take down the program before it takes down the operating system.
+func SetMaxThreads(threads int) int {
+ return setMaxThreads(threads)
+}
diff --git a/src/pkg/runtime/defs_dragonfly.go b/src/pkg/runtime/defs_dragonfly.go
new file mode 100644
index 000000000..8ebc3a9e1
--- /dev/null
+++ b/src/pkg/runtime/defs_dragonfly.go
@@ -0,0 +1,126 @@
+// 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.
+
+// +build ignore
+
+/*
+Input to cgo.
+
+GOARCH=amd64 go tool cgo -cdefs defs_dragonfly.go >defs_dragonfly_amd64.h
+GOARCH=386 go tool cgo -cdefs defs_dragonfly.go >defs_dragonfly_386.h
+*/
+
+package runtime
+
+/*
+#include <sys/user.h>
+#include <sys/time.h>
+#include <sys/event.h>
+#include <sys/mman.h>
+#include <sys/ucontext.h>
+#include <sys/rtprio.h>
+#include <sys/signal.h>
+#include <sys/unistd.h>
+#include <errno.h>
+#include <signal.h>
+*/
+import "C"
+
+const (
+ EINTR = C.EINTR
+ EFAULT = C.EFAULT
+ EBUSY = C.EBUSY
+ EAGAIN = C.EAGAIN
+
+ PROT_NONE = C.PROT_NONE
+ PROT_READ = C.PROT_READ
+ PROT_WRITE = C.PROT_WRITE
+ PROT_EXEC = C.PROT_EXEC
+
+ MAP_ANON = C.MAP_ANON
+ MAP_PRIVATE = C.MAP_PRIVATE
+ MAP_FIXED = C.MAP_FIXED
+
+ MADV_FREE = C.MADV_FREE
+
+ SA_SIGINFO = C.SA_SIGINFO
+ SA_RESTART = C.SA_RESTART
+ SA_ONSTACK = C.SA_ONSTACK
+
+ SIGHUP = C.SIGHUP
+ SIGINT = C.SIGINT
+ SIGQUIT = C.SIGQUIT
+ SIGILL = C.SIGILL
+ SIGTRAP = C.SIGTRAP
+ SIGABRT = C.SIGABRT
+ SIGEMT = C.SIGEMT
+ SIGFPE = C.SIGFPE
+ SIGKILL = C.SIGKILL
+ SIGBUS = C.SIGBUS
+ SIGSEGV = C.SIGSEGV
+ SIGSYS = C.SIGSYS
+ SIGPIPE = C.SIGPIPE
+ SIGALRM = C.SIGALRM
+ SIGTERM = C.SIGTERM
+ SIGURG = C.SIGURG
+ SIGSTOP = C.SIGSTOP
+ SIGTSTP = C.SIGTSTP
+ SIGCONT = C.SIGCONT
+ SIGCHLD = C.SIGCHLD
+ SIGTTIN = C.SIGTTIN
+ SIGTTOU = C.SIGTTOU
+ SIGIO = C.SIGIO
+ SIGXCPU = C.SIGXCPU
+ SIGXFSZ = C.SIGXFSZ
+ SIGVTALRM = C.SIGVTALRM
+ SIGPROF = C.SIGPROF
+ SIGWINCH = C.SIGWINCH
+ SIGINFO = C.SIGINFO
+ SIGUSR1 = C.SIGUSR1
+ SIGUSR2 = C.SIGUSR2
+
+ FPE_INTDIV = C.FPE_INTDIV
+ FPE_INTOVF = C.FPE_INTOVF
+ FPE_FLTDIV = C.FPE_FLTDIV
+ FPE_FLTOVF = C.FPE_FLTOVF
+ FPE_FLTUND = C.FPE_FLTUND
+ FPE_FLTRES = C.FPE_FLTRES
+ FPE_FLTINV = C.FPE_FLTINV
+ FPE_FLTSUB = C.FPE_FLTSUB
+
+ BUS_ADRALN = C.BUS_ADRALN
+ BUS_ADRERR = C.BUS_ADRERR
+ BUS_OBJERR = C.BUS_OBJERR
+
+ SEGV_MAPERR = C.SEGV_MAPERR
+ SEGV_ACCERR = C.SEGV_ACCERR
+
+ ITIMER_REAL = C.ITIMER_REAL
+ ITIMER_VIRTUAL = C.ITIMER_VIRTUAL
+ ITIMER_PROF = C.ITIMER_PROF
+
+ EV_ADD = C.EV_ADD
+ EV_DELETE = C.EV_DELETE
+ EV_CLEAR = C.EV_CLEAR
+ EV_ERROR = C.EV_ERROR
+ EVFILT_READ = C.EVFILT_READ
+ EVFILT_WRITE = C.EVFILT_WRITE
+)
+
+type Rtprio C.struct_rtprio
+type Lwpparams C.struct_lwp_params
+type Sigaltstack C.struct_sigaltstack
+type Sigset C.struct___sigset
+type StackT C.stack_t
+
+type Siginfo C.siginfo_t
+
+type Mcontext C.mcontext_t
+type Ucontext C.ucontext_t
+
+type Timespec C.struct_timespec
+type Timeval C.struct_timeval
+type Itimerval C.struct_itimerval
+
+type Kevent C.struct_kevent
diff --git a/src/pkg/runtime/defs_dragonfly_386.h b/src/pkg/runtime/defs_dragonfly_386.h
new file mode 100644
index 000000000..696dcd887
--- /dev/null
+++ b/src/pkg/runtime/defs_dragonfly_386.h
@@ -0,0 +1,198 @@
+// Created by cgo -cdefs - DO NOT EDIT
+// cgo -cdefs defs_dragonfly.go
+
+
+enum {
+ EINTR = 0x4,
+ EFAULT = 0xe,
+ EBUSY = 0x10,
+ EAGAIN = 0x23,
+
+ PROT_NONE = 0x0,
+ PROT_READ = 0x1,
+ PROT_WRITE = 0x2,
+ PROT_EXEC = 0x4,
+
+ MAP_ANON = 0x1000,
+ MAP_PRIVATE = 0x2,
+ MAP_FIXED = 0x10,
+
+ MADV_FREE = 0x5,
+
+ SA_SIGINFO = 0x40,
+ SA_RESTART = 0x2,
+ SA_ONSTACK = 0x1,
+
+ SIGHUP = 0x1,
+ SIGINT = 0x2,
+ SIGQUIT = 0x3,
+ SIGILL = 0x4,
+ SIGTRAP = 0x5,
+ SIGABRT = 0x6,
+ SIGEMT = 0x7,
+ SIGFPE = 0x8,
+ SIGKILL = 0x9,
+ SIGBUS = 0xa,
+ SIGSEGV = 0xb,
+ SIGSYS = 0xc,
+ SIGPIPE = 0xd,
+ SIGALRM = 0xe,
+ SIGTERM = 0xf,
+ SIGURG = 0x10,
+ SIGSTOP = 0x11,
+ SIGTSTP = 0x12,
+ SIGCONT = 0x13,
+ SIGCHLD = 0x14,
+ SIGTTIN = 0x15,
+ SIGTTOU = 0x16,
+ SIGIO = 0x17,
+ SIGXCPU = 0x18,
+ SIGXFSZ = 0x19,
+ SIGVTALRM = 0x1a,
+ SIGPROF = 0x1b,
+ SIGWINCH = 0x1c,
+ SIGINFO = 0x1d,
+ SIGUSR1 = 0x1e,
+ SIGUSR2 = 0x1f,
+
+ FPE_INTDIV = 0x2,
+ FPE_INTOVF = 0x1,
+ FPE_FLTDIV = 0x3,
+ FPE_FLTOVF = 0x4,
+ FPE_FLTUND = 0x5,
+ FPE_FLTRES = 0x6,
+ FPE_FLTINV = 0x7,
+ FPE_FLTSUB = 0x8,
+
+ BUS_ADRALN = 0x1,
+ BUS_ADRERR = 0x2,
+ BUS_OBJERR = 0x3,
+
+ SEGV_MAPERR = 0x1,
+ SEGV_ACCERR = 0x2,
+
+ ITIMER_REAL = 0x0,
+ ITIMER_VIRTUAL = 0x1,
+ ITIMER_PROF = 0x2,
+
+ EV_ADD = 0x1,
+ EV_DELETE = 0x2,
+ EV_CLEAR = 0x20,
+ EV_ERROR = 0x4000,
+ EVFILT_READ = -0x1,
+ EVFILT_WRITE = -0x2,
+};
+
+typedef struct Rtprio Rtprio;
+typedef struct Lwpparams Lwpparams;
+typedef struct Sigaltstack Sigaltstack;
+typedef struct Sigset Sigset;
+typedef struct StackT StackT;
+typedef struct Siginfo Siginfo;
+typedef struct Mcontext Mcontext;
+typedef struct Ucontext Ucontext;
+typedef struct Timespec Timespec;
+typedef struct Timeval Timeval;
+typedef struct Itimerval Itimerval;
+typedef struct Kevent Kevent;
+
+#pragma pack on
+
+struct Rtprio {
+ uint16 type;
+ uint16 prio;
+};
+struct Lwpparams {
+ void *func;
+ byte *arg;
+ byte *stack;
+ int32 *tid1;
+ int32 *tid2;
+};
+struct Sigaltstack {
+ int8 *ss_sp;
+ uint32 ss_size;
+ int32 ss_flags;
+};
+struct Sigset {
+ uint32 __bits[4];
+};
+struct StackT {
+ int8 *ss_sp;
+ uint32 ss_size;
+ int32 ss_flags;
+};
+
+struct Siginfo {
+ int32 si_signo;
+ int32 si_errno;
+ int32 si_code;
+ int32 si_pid;
+ uint32 si_uid;
+ int32 si_status;
+ byte *si_addr;
+ byte si_value[4];
+ int32 si_band;
+ int32 __spare__[7];
+};
+
+struct Mcontext {
+ int32 mc_onstack;
+ int32 mc_gs;
+ int32 mc_fs;
+ int32 mc_es;
+ int32 mc_ds;
+ int32 mc_edi;
+ int32 mc_esi;
+ int32 mc_ebp;
+ int32 mc_isp;
+ int32 mc_ebx;
+ int32 mc_edx;
+ int32 mc_ecx;
+ int32 mc_eax;
+ int32 mc_xflags;
+ int32 mc_trapno;
+ int32 mc_err;
+ int32 mc_eip;
+ int32 mc_cs;
+ int32 mc_eflags;
+ int32 mc_esp;
+ int32 mc_ss;
+ int32 mc_len;
+ int32 mc_fpformat;
+ int32 mc_ownedfp;
+ int32 mc_fpregs[128];
+ int32 __spare__[16];
+};
+struct Ucontext {
+ Sigset uc_sigmask;
+ Mcontext uc_mcontext;
+ Ucontext *uc_link;
+ StackT uc_stack;
+ int32 __spare__[8];
+};
+
+struct Timespec {
+ int32 tv_sec;
+ int32 tv_nsec;
+};
+struct Timeval {
+ int32 tv_sec;
+ int32 tv_usec;
+};
+struct Itimerval {
+ Timeval it_interval;
+ Timeval it_value;
+};
+
+struct Kevent {
+ uint32 ident;
+ int16 filter;
+ uint16 flags;
+ uint32 fflags;
+ int32 data;
+ byte *udata;
+};
+
+
+#pragma pack off
diff --git a/src/pkg/runtime/defs_dragonfly_amd64.h b/src/pkg/runtime/defs_dragonfly_amd64.h
new file mode 100644
index 000000000..74581cc94
--- /dev/null
+++ b/src/pkg/runtime/defs_dragonfly_amd64.h
@@ -0,0 +1,208 @@
+// Created by cgo -cdefs - DO NOT EDIT
+// cgo -cdefs defs_dragonfly.go
+
+
+enum {
+ EINTR = 0x4,
+ EFAULT = 0xe,
+ EBUSY = 0x10,
+ EAGAIN = 0x23,
+
+ PROT_NONE = 0x0,
+ PROT_READ = 0x1,
+ PROT_WRITE = 0x2,
+ PROT_EXEC = 0x4,
+
+ MAP_ANON = 0x1000,
+ MAP_PRIVATE = 0x2,
+ MAP_FIXED = 0x10,
+
+ MADV_FREE = 0x5,
+
+ SA_SIGINFO = 0x40,
+ SA_RESTART = 0x2,
+ SA_ONSTACK = 0x1,
+
+ SIGHUP = 0x1,
+ SIGINT = 0x2,
+ SIGQUIT = 0x3,
+ SIGILL = 0x4,
+ SIGTRAP = 0x5,
+ SIGABRT = 0x6,
+ SIGEMT = 0x7,
+ SIGFPE = 0x8,
+ SIGKILL = 0x9,
+ SIGBUS = 0xa,
+ SIGSEGV = 0xb,
+ SIGSYS = 0xc,
+ SIGPIPE = 0xd,
+ SIGALRM = 0xe,
+ SIGTERM = 0xf,
+ SIGURG = 0x10,
+ SIGSTOP = 0x11,
+ SIGTSTP = 0x12,
+ SIGCONT = 0x13,
+ SIGCHLD = 0x14,
+ SIGTTIN = 0x15,
+ SIGTTOU = 0x16,
+ SIGIO = 0x17,
+ SIGXCPU = 0x18,
+ SIGXFSZ = 0x19,
+ SIGVTALRM = 0x1a,
+ SIGPROF = 0x1b,
+ SIGWINCH = 0x1c,
+ SIGINFO = 0x1d,
+ SIGUSR1 = 0x1e,
+ SIGUSR2 = 0x1f,
+
+ FPE_INTDIV = 0x2,
+ FPE_INTOVF = 0x1,
+ FPE_FLTDIV = 0x3,
+ FPE_FLTOVF = 0x4,
+ FPE_FLTUND = 0x5,
+ FPE_FLTRES = 0x6,
+ FPE_FLTINV = 0x7,
+ FPE_FLTSUB = 0x8,
+
+ BUS_ADRALN = 0x1,
+ BUS_ADRERR = 0x2,
+ BUS_OBJERR = 0x3,
+
+ SEGV_MAPERR = 0x1,
+ SEGV_ACCERR = 0x2,
+
+ ITIMER_REAL = 0x0,
+ ITIMER_VIRTUAL = 0x1,
+ ITIMER_PROF = 0x2,
+
+ EV_ADD = 0x1,
+ EV_DELETE = 0x2,
+ EV_CLEAR = 0x20,
+ EV_ERROR = 0x4000,
+ EVFILT_READ = -0x1,
+ EVFILT_WRITE = -0x2,
+};
+
+typedef struct Rtprio Rtprio;
+typedef struct Lwpparams Lwpparams;
+typedef struct Sigaltstack Sigaltstack;
+typedef struct Sigset Sigset;
+typedef struct StackT StackT;
+typedef struct Siginfo Siginfo;
+typedef struct Mcontext Mcontext;
+typedef struct Ucontext Ucontext;
+typedef struct Timespec Timespec;
+typedef struct Timeval Timeval;
+typedef struct Itimerval Itimerval;
+typedef struct Kevent Kevent;
+
+#pragma pack on
+
+struct Rtprio {
+ uint16 type;
+ uint16 prio;
+};
+struct Lwpparams {
+ void *func;
+ byte *arg;
+ byte *stack;
+ int32 *tid1;
+ int32 *tid2;
+};
+struct Sigaltstack {
+ int8 *ss_sp;
+ uint64 ss_size;
+ int32 ss_flags;
+ byte Pad_cgo_0[4];
+};
+struct Sigset {
+ uint32 __bits[4];
+};
+struct StackT {
+ int8 *ss_sp;
+ uint64 ss_size;
+ int32 ss_flags;
+ byte Pad_cgo_0[4];
+};
+
+struct Siginfo {
+ int32 si_signo;
+ int32 si_errno;
+ int32 si_code;
+ int32 si_pid;
+ uint32 si_uid;
+ int32 si_status;
+ byte *si_addr;
+ byte si_value[8];
+ int64 si_band;
+ int32 __spare__[7];
+ byte Pad_cgo_0[4];
+};
+
+struct Mcontext {
+ int64 mc_onstack;
+ int64 mc_rdi;
+ int64 mc_rsi;
+ int64 mc_rdx;
+ int64 mc_rcx;
+ int64 mc_r8;
+ int64 mc_r9;
+ int64 mc_rax;
+ int64 mc_rbx;
+ int64 mc_rbp;
+ int64 mc_r10;
+ int64 mc_r11;
+ int64 mc_r12;
+ int64 mc_r13;
+ int64 mc_r14;
+ int64 mc_r15;
+ int64 mc_xflags;
+ int64 mc_trapno;
+ int64 mc_addr;
+ int64 mc_flags;
+ int64 mc_err;
+ int64 mc_rip;
+ int64 mc_cs;
+ int64 mc_rflags;
+ int64 mc_rsp;
+ int64 mc_ss;
+ uint32 mc_len;
+ uint32 mc_fpformat;
+ uint32 mc_ownedfp;
+ uint32 mc_reserved;
+ uint32 mc_unused[8];
+ int32 mc_fpregs[256];
+};
+struct Ucontext {
+ Sigset uc_sigmask;
+ byte Pad_cgo_0[48];
+ Mcontext uc_mcontext;
+ Ucontext *uc_link;
+ StackT uc_stack;
+ int32 __spare__[8];
+};
+
+struct Timespec {
+ int64 tv_sec;
+ int64 tv_nsec;
+};
+struct Timeval {
+ int64 tv_sec;
+ int64 tv_usec;
+};
+struct Itimerval {
+ Timeval it_interval;
+ Timeval it_value;
+};
+
+struct Kevent {
+ uint64 ident;
+ int16 filter;
+ uint16 flags;
+ uint32 fflags;
+ int64 data;
+ byte *udata;
+};
+
+
+#pragma pack off
diff --git a/src/pkg/runtime/defs_freebsd.go b/src/pkg/runtime/defs_freebsd.go
index 93f0703e3..dad20f16d 100644
--- a/src/pkg/runtime/defs_freebsd.go
+++ b/src/pkg/runtime/defs_freebsd.go
@@ -19,6 +19,7 @@ package runtime
#include <sys/time.h>
#include <signal.h>
#include <errno.h>
+#include <sys/event.h>
#include <sys/mman.h>
#include <sys/ucontext.h>
#include <sys/umtx.h>
@@ -30,6 +31,9 @@ package runtime
import "C"
const (
+ EINTR = C.EINTR
+ EFAULT = C.EFAULT
+
PROT_NONE = C.PROT_NONE
PROT_READ = C.PROT_READ
PROT_WRITE = C.PROT_WRITE
@@ -48,8 +52,6 @@ const (
UMTX_OP_WAIT_UINT = C.UMTX_OP_WAIT_UINT
UMTX_OP_WAKE = C.UMTX_OP_WAKE
- EINTR = C.EINTR
-
SIGHUP = C.SIGHUP
SIGINT = C.SIGINT
SIGQUIT = C.SIGQUIT
@@ -101,6 +103,14 @@ const (
ITIMER_REAL = C.ITIMER_REAL
ITIMER_VIRTUAL = C.ITIMER_VIRTUAL
ITIMER_PROF = C.ITIMER_PROF
+
+ EV_ADD = C.EV_ADD
+ EV_DELETE = C.EV_DELETE
+ EV_CLEAR = C.EV_CLEAR
+ EV_RECEIPT = C.EV_RECEIPT
+ EV_ERROR = C.EV_ERROR
+ EVFILT_READ = C.EVFILT_READ
+ EVFILT_WRITE = C.EVFILT_WRITE
)
type Rtprio C.struct_rtprio
@@ -117,3 +127,5 @@ type Ucontext C.ucontext_t
type Timespec C.struct_timespec
type Timeval C.struct_timeval
type Itimerval C.struct_itimerval
+
+type Kevent C.struct_kevent
diff --git a/src/pkg/runtime/defs_freebsd_386.h b/src/pkg/runtime/defs_freebsd_386.h
index 8fa37c5d6..cf9c76eb1 100644
--- a/src/pkg/runtime/defs_freebsd_386.h
+++ b/src/pkg/runtime/defs_freebsd_386.h
@@ -3,6 +3,9 @@
enum {
+ EINTR = 0x4,
+ EFAULT = 0xe,
+
PROT_NONE = 0x0,
PROT_READ = 0x1,
PROT_WRITE = 0x2,
@@ -21,8 +24,6 @@ enum {
UMTX_OP_WAIT_UINT = 0xb,
UMTX_OP_WAKE = 0x3,
- EINTR = 0x4,
-
SIGHUP = 0x1,
SIGINT = 0x2,
SIGQUIT = 0x3,
@@ -74,6 +75,14 @@ enum {
ITIMER_REAL = 0x0,
ITIMER_VIRTUAL = 0x1,
ITIMER_PROF = 0x2,
+
+ EV_ADD = 0x1,
+ EV_DELETE = 0x2,
+ EV_CLEAR = 0x20,
+ EV_RECEIPT = 0x40,
+ EV_ERROR = 0x4000,
+ EVFILT_READ = -0x1,
+ EVFILT_WRITE = -0x2,
};
typedef struct Rtprio Rtprio;
@@ -87,6 +96,7 @@ typedef struct Ucontext Ucontext;
typedef struct Timespec Timespec;
typedef struct Timeval Timeval;
typedef struct Itimerval Itimerval;
+typedef struct Kevent Kevent;
#pragma pack on
@@ -157,11 +167,13 @@ struct Mcontext {
int32 mc_len;
int32 mc_fpformat;
int32 mc_ownedfp;
- int32 mc_spare1[1];
+ int32 mc_flags;
int32 mc_fpstate[128];
int32 mc_fsbase;
int32 mc_gsbase;
- int32 mc_spare2[6];
+ int32 mc_xfpustate;
+ int32 mc_xfpustate_len;
+ int32 mc_spare2[4];
};
struct Ucontext {
Sigset uc_sigmask;
@@ -186,5 +198,14 @@ struct Itimerval {
Timeval it_value;
};
+struct Kevent {
+ uint32 ident;
+ int16 filter;
+ uint16 flags;
+ uint32 fflags;
+ int32 data;
+ byte *udata;
+};
+
#pragma pack off
diff --git a/src/pkg/runtime/defs_freebsd_amd64.h b/src/pkg/runtime/defs_freebsd_amd64.h
index 56d849bce..3fb33f38a 100644
--- a/src/pkg/runtime/defs_freebsd_amd64.h
+++ b/src/pkg/runtime/defs_freebsd_amd64.h
@@ -3,6 +3,9 @@
enum {
+ EINTR = 0x4,
+ EFAULT = 0xe,
+
PROT_NONE = 0x0,
PROT_READ = 0x1,
PROT_WRITE = 0x2,
@@ -21,8 +24,6 @@ enum {
UMTX_OP_WAIT_UINT = 0xb,
UMTX_OP_WAKE = 0x3,
- EINTR = 0x4,
-
SIGHUP = 0x1,
SIGINT = 0x2,
SIGQUIT = 0x3,
@@ -74,6 +75,14 @@ enum {
ITIMER_REAL = 0x0,
ITIMER_VIRTUAL = 0x1,
ITIMER_PROF = 0x2,
+
+ EV_ADD = 0x1,
+ EV_DELETE = 0x2,
+ EV_CLEAR = 0x20,
+ EV_RECEIPT = 0x40,
+ EV_ERROR = 0x4000,
+ EVFILT_READ = -0x1,
+ EVFILT_WRITE = -0x2,
};
typedef struct Rtprio Rtprio;
@@ -87,6 +96,7 @@ typedef struct Ucontext Ucontext;
typedef struct Timespec Timespec;
typedef struct Timeval Timeval;
typedef struct Itimerval Itimerval;
+typedef struct Kevent Kevent;
#pragma pack on
@@ -172,7 +182,9 @@ struct Mcontext {
int64 mc_fpstate[64];
int64 mc_fsbase;
int64 mc_gsbase;
- int64 mc_spare[6];
+ int64 mc_xfpustate;
+ int64 mc_xfpustate_len;
+ int64 mc_spare[4];
};
struct Ucontext {
Sigset uc_sigmask;
@@ -197,5 +209,14 @@ struct Itimerval {
Timeval it_value;
};
+struct Kevent {
+ uint64 ident;
+ int16 filter;
+ uint16 flags;
+ uint32 fflags;
+ int64 data;
+ byte *udata;
+};
+
#pragma pack off
diff --git a/src/pkg/runtime/defs_freebsd_arm.h b/src/pkg/runtime/defs_freebsd_arm.h
index 334652eec..d321f4249 100644
--- a/src/pkg/runtime/defs_freebsd_arm.h
+++ b/src/pkg/runtime/defs_freebsd_arm.h
@@ -3,6 +3,9 @@
enum {
+ EINTR = 0x4,
+ EFAULT = 0xe,
+
PROT_NONE = 0x0,
PROT_READ = 0x1,
PROT_WRITE = 0x2,
@@ -21,8 +24,6 @@ enum {
UMTX_OP_WAIT_UINT = 0xb,
UMTX_OP_WAKE = 0x3,
- EINTR = 0x4,
-
SIGHUP = 0x1,
SIGINT = 0x2,
SIGQUIT = 0x3,
@@ -74,6 +75,14 @@ enum {
ITIMER_REAL = 0x0,
ITIMER_VIRTUAL = 0x1,
ITIMER_PROF = 0x2,
+
+ EV_ADD = 0x1,
+ EV_DELETE = 0x2,
+ EV_CLEAR = 0x20,
+ EV_RECEIPT = 0x40,
+ EV_ERROR = 0x4000,
+ EVFILT_READ = -0x1,
+ EVFILT_WRITE = -0x2,
};
typedef struct Rtprio Rtprio;
@@ -87,6 +96,7 @@ typedef struct Ucontext Ucontext;
typedef struct Timespec Timespec;
typedef struct Timeval Timeval;
typedef struct Itimerval Itimerval;
+typedef struct Kevent Kevent;
#pragma pack on
@@ -159,5 +169,14 @@ struct Itimerval {
Timeval it_value;
};
+struct Kevent {
+ uint32 ident;
+ int16 filter;
+ uint16 flags;
+ uint32 fflags;
+ int32 data;
+ byte *udata;
+};
+
#pragma pack off
diff --git a/src/pkg/runtime/defs_netbsd.go b/src/pkg/runtime/defs_netbsd.go
index c543593fa..d1c4cbe96 100644
--- a/src/pkg/runtime/defs_netbsd.go
+++ b/src/pkg/runtime/defs_netbsd.go
@@ -20,6 +20,7 @@ package runtime
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/signal.h>
+#include <sys/event.h>
#include <sys/time.h>
#include <sys/ucontext.h>
#include <sys/unistd.h>
@@ -29,6 +30,9 @@ package runtime
import "C"
const (
+ EINTR = C.EINTR
+ EFAULT = C.EFAULT
+
PROT_NONE = C.PROT_NONE
PROT_READ = C.PROT_READ
PROT_WRITE = C.PROT_WRITE
@@ -44,8 +48,6 @@ const (
SA_RESTART = C.SA_RESTART
SA_ONSTACK = C.SA_ONSTACK
- EINTR = C.EINTR
-
SIGHUP = C.SIGHUP
SIGINT = C.SIGINT
SIGQUIT = C.SIGQUIT
@@ -97,6 +99,14 @@ const (
ITIMER_REAL = C.ITIMER_REAL
ITIMER_VIRTUAL = C.ITIMER_VIRTUAL
ITIMER_PROF = C.ITIMER_PROF
+
+ EV_ADD = C.EV_ADD
+ EV_DELETE = C.EV_DELETE
+ EV_CLEAR = C.EV_CLEAR
+ EV_RECEIPT = 0
+ EV_ERROR = C.EV_ERROR
+ EVFILT_READ = C.EVFILT_READ
+ EVFILT_WRITE = C.EVFILT_WRITE
)
type Sigaltstack C.struct_sigaltstack
@@ -111,3 +121,5 @@ type Itimerval C.struct_itimerval
type McontextT C.mcontext_t
type UcontextT C.ucontext_t
+
+type Kevent C.struct_kevent
diff --git a/src/pkg/runtime/defs_netbsd_386.h b/src/pkg/runtime/defs_netbsd_386.h
index 04c380e3f..7fd66959f 100644
--- a/src/pkg/runtime/defs_netbsd_386.h
+++ b/src/pkg/runtime/defs_netbsd_386.h
@@ -3,6 +3,9 @@
enum {
+ EINTR = 0x4,
+ EFAULT = 0xe,
+
PROT_NONE = 0x0,
PROT_READ = 0x1,
PROT_WRITE = 0x2,
@@ -18,8 +21,6 @@ enum {
SA_RESTART = 0x2,
SA_ONSTACK = 0x1,
- EINTR = 0x4,
-
SIGHUP = 0x1,
SIGINT = 0x2,
SIGQUIT = 0x3,
@@ -71,6 +72,14 @@ enum {
ITIMER_REAL = 0x0,
ITIMER_VIRTUAL = 0x1,
ITIMER_PROF = 0x2,
+
+ EV_ADD = 0x1,
+ EV_DELETE = 0x2,
+ EV_CLEAR = 0x20,
+ EV_RECEIPT = 0,
+ EV_ERROR = 0x4000,
+ EVFILT_READ = 0x0,
+ EVFILT_WRITE = 0x1,
};
typedef struct Sigaltstack Sigaltstack;
@@ -82,6 +91,7 @@ typedef struct Timeval Timeval;
typedef struct Itimerval Itimerval;
typedef struct McontextT McontextT;
typedef struct UcontextT UcontextT;
+typedef struct Kevent Kevent;
#pragma pack on
@@ -133,6 +143,15 @@ struct UcontextT {
int32 __uc_pad[4];
};
+struct Kevent {
+ uint32 ident;
+ uint32 filter;
+ uint32 flags;
+ uint32 fflags;
+ int64 data;
+ int32 udata;
+};
+
#pragma pack off
// Created by cgo -cdefs - DO NOT EDIT
diff --git a/src/pkg/runtime/defs_netbsd_amd64.h b/src/pkg/runtime/defs_netbsd_amd64.h
index 3d3f576d3..972af165b 100644
--- a/src/pkg/runtime/defs_netbsd_amd64.h
+++ b/src/pkg/runtime/defs_netbsd_amd64.h
@@ -3,6 +3,9 @@
enum {
+ EINTR = 0x4,
+ EFAULT = 0xe,
+
PROT_NONE = 0x0,
PROT_READ = 0x1,
PROT_WRITE = 0x2,
@@ -18,8 +21,6 @@ enum {
SA_RESTART = 0x2,
SA_ONSTACK = 0x1,
- EINTR = 0x4,
-
SIGHUP = 0x1,
SIGINT = 0x2,
SIGQUIT = 0x3,
@@ -71,6 +72,14 @@ enum {
ITIMER_REAL = 0x0,
ITIMER_VIRTUAL = 0x1,
ITIMER_PROF = 0x2,
+
+ EV_ADD = 0x1,
+ EV_DELETE = 0x2,
+ EV_CLEAR = 0x20,
+ EV_RECEIPT = 0,
+ EV_ERROR = 0x4000,
+ EVFILT_READ = 0x0,
+ EVFILT_WRITE = 0x1,
};
typedef struct Sigaltstack Sigaltstack;
@@ -82,6 +91,7 @@ typedef struct Timeval Timeval;
typedef struct Itimerval Itimerval;
typedef struct McontextT McontextT;
typedef struct UcontextT UcontextT;
+typedef struct Kevent Kevent;
#pragma pack on
@@ -137,6 +147,16 @@ struct UcontextT {
McontextT uc_mcontext;
};
+struct Kevent {
+ uint64 ident;
+ uint32 filter;
+ uint32 flags;
+ uint32 fflags;
+ byte Pad_cgo_0[4];
+ int64 data;
+ int64 udata;
+};
+
#pragma pack off
// Created by cgo -cdefs - DO NOT EDIT
diff --git a/src/pkg/runtime/defs_netbsd_arm.h b/src/pkg/runtime/defs_netbsd_arm.h
index 26b55222e..c6f5b1c47 100644
--- a/src/pkg/runtime/defs_netbsd_arm.h
+++ b/src/pkg/runtime/defs_netbsd_arm.h
@@ -3,6 +3,9 @@
enum {
+ EINTR = 0x4,
+ EFAULT = 0xe,
+
PROT_NONE = 0x0,
PROT_READ = 0x1,
PROT_WRITE = 0x2,
@@ -18,8 +21,6 @@ enum {
SA_RESTART = 0x2,
SA_ONSTACK = 0x1,
- EINTR = 0x4,
-
SIGHUP = 0x1,
SIGINT = 0x2,
SIGQUIT = 0x3,
@@ -71,6 +72,14 @@ enum {
ITIMER_REAL = 0x0,
ITIMER_VIRTUAL = 0x1,
ITIMER_PROF = 0x2,
+
+ EV_ADD = 0x1,
+ EV_DELETE = 0x2,
+ EV_CLEAR = 0x20,
+ EV_RECEIPT = 0,
+ EV_ERROR = 0x4000,
+ EVFILT_READ = 0x0,
+ EVFILT_WRITE = 0x1,
};
typedef struct Sigaltstack Sigaltstack;
@@ -82,6 +91,7 @@ typedef struct Timeval Timeval;
typedef struct Itimerval Itimerval;
typedef struct McontextT McontextT;
typedef struct UcontextT UcontextT;
+typedef struct Kevent Kevent;
#pragma pack on
@@ -137,6 +147,16 @@ struct UcontextT {
int32 __uc_pad[2];
};
+struct Kevent {
+ uint32 ident;
+ uint32 filter;
+ uint32 flags;
+ uint32 fflags;
+ int64 data;
+ int32 udata;
+};
+
+
#pragma pack off
// Created by cgo -cdefs - DO NOT EDIT
// cgo -cdefs defs_netbsd.go defs_netbsd_arm.go
diff --git a/src/pkg/runtime/defs_openbsd.go b/src/pkg/runtime/defs_openbsd.go
index ff94b9405..4a705796f 100644
--- a/src/pkg/runtime/defs_openbsd.go
+++ b/src/pkg/runtime/defs_openbsd.go
@@ -15,6 +15,7 @@ package runtime
/*
#include <sys/types.h>
+#include <sys/event.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <sys/unistd.h>
@@ -25,6 +26,9 @@ package runtime
import "C"
const (
+ EINTR = C.EINTR
+ EFAULT = C.EFAULT
+
PROT_NONE = C.PROT_NONE
PROT_READ = C.PROT_READ
PROT_WRITE = C.PROT_WRITE
@@ -40,8 +44,6 @@ const (
SA_RESTART = C.SA_RESTART
SA_ONSTACK = C.SA_ONSTACK
- EINTR = C.EINTR
-
SIGHUP = C.SIGHUP
SIGINT = C.SIGINT
SIGQUIT = C.SIGQUIT
@@ -93,6 +95,13 @@ const (
ITIMER_REAL = C.ITIMER_REAL
ITIMER_VIRTUAL = C.ITIMER_VIRTUAL
ITIMER_PROF = C.ITIMER_PROF
+
+ EV_ADD = C.EV_ADD
+ EV_DELETE = C.EV_DELETE
+ EV_CLEAR = C.EV_CLEAR
+ EV_ERROR = C.EV_ERROR
+ EVFILT_READ = C.EVFILT_READ
+ EVFILT_WRITE = C.EVFILT_WRITE
)
type Tfork C.struct___tfork
@@ -108,3 +117,5 @@ type StackT C.stack_t
type Timespec C.struct_timespec
type Timeval C.struct_timeval
type Itimerval C.struct_itimerval
+
+type Kevent C.struct_kevent
diff --git a/src/pkg/runtime/defs_openbsd_386.h b/src/pkg/runtime/defs_openbsd_386.h
index 323bb084a..a5b7f04b5 100644
--- a/src/pkg/runtime/defs_openbsd_386.h
+++ b/src/pkg/runtime/defs_openbsd_386.h
@@ -3,6 +3,9 @@
enum {
+ EINTR = 0x4,
+ EFAULT = 0xe,
+
PROT_NONE = 0x0,
PROT_READ = 0x1,
PROT_WRITE = 0x2,
@@ -18,8 +21,6 @@ enum {
SA_RESTART = 0x2,
SA_ONSTACK = 0x1,
- EINTR = 0x4,
-
SIGHUP = 0x1,
SIGINT = 0x2,
SIGQUIT = 0x3,
@@ -71,6 +72,13 @@ enum {
ITIMER_REAL = 0x0,
ITIMER_VIRTUAL = 0x1,
ITIMER_PROF = 0x2,
+
+ EV_ADD = 0x1,
+ EV_DELETE = 0x2,
+ EV_CLEAR = 0x20,
+ EV_ERROR = 0x4000,
+ EVFILT_READ = -0x1,
+ EVFILT_WRITE = -0x2,
};
typedef struct Tfork Tfork;
@@ -81,6 +89,7 @@ typedef struct StackT StackT;
typedef struct Timespec Timespec;
typedef struct Timeval Timeval;
typedef struct Itimerval Itimerval;
+typedef struct Kevent Kevent;
#pragma pack on
@@ -146,5 +155,14 @@ struct Itimerval {
Timeval it_value;
};
+struct Kevent {
+ uint32 ident;
+ int16 filter;
+ uint16 flags;
+ uint32 fflags;
+ int32 data;
+ byte *udata;
+};
+
#pragma pack off
diff --git a/src/pkg/runtime/defs_openbsd_amd64.h b/src/pkg/runtime/defs_openbsd_amd64.h
index 429cc99f0..eb47ec892 100644
--- a/src/pkg/runtime/defs_openbsd_amd64.h
+++ b/src/pkg/runtime/defs_openbsd_amd64.h
@@ -3,6 +3,9 @@
enum {
+ EINTR = 0x4,
+ EFAULT = 0xe,
+
PROT_NONE = 0x0,
PROT_READ = 0x1,
PROT_WRITE = 0x2,
@@ -18,8 +21,6 @@ enum {
SA_RESTART = 0x2,
SA_ONSTACK = 0x1,
- EINTR = 0x4,
-
SIGHUP = 0x1,
SIGINT = 0x2,
SIGQUIT = 0x3,
@@ -71,6 +72,13 @@ enum {
ITIMER_REAL = 0x0,
ITIMER_VIRTUAL = 0x1,
ITIMER_PROF = 0x2,
+
+ EV_ADD = 0x1,
+ EV_DELETE = 0x2,
+ EV_CLEAR = 0x20,
+ EV_ERROR = 0x4000,
+ EVFILT_READ = -0x1,
+ EVFILT_WRITE = -0x2,
};
typedef struct Tfork Tfork;
@@ -81,6 +89,7 @@ typedef struct StackT StackT;
typedef struct Timespec Timespec;
typedef struct Timeval Timeval;
typedef struct Itimerval Itimerval;
+typedef struct Kevent Kevent;
#pragma pack on
@@ -158,5 +167,14 @@ struct Itimerval {
Timeval it_value;
};
+struct Kevent {
+ uint32 ident;
+ int16 filter;
+ uint16 flags;
+ uint32 fflags;
+ int32 data;
+ byte *udata;
+};
+
#pragma pack off
diff --git a/src/pkg/runtime/defs_windows.go b/src/pkg/runtime/defs_windows.go
index 0d525b932..01aea92de 100644
--- a/src/pkg/runtime/defs_windows.go
+++ b/src/pkg/runtime/defs_windows.go
@@ -7,8 +7,8 @@
/*
Input to cgo.
-GOARCH=amd64 cgo -cdefs defs.go >amd64/defs.h
-GOARCH=386 cgo -cdefs defs.go >386/defs.h
+GOARCH=amd64 go tool cgo -cdefs defs_windows.go > defs_windows_amd64.h
+GOARCH=386 go tool cgo -cdefs defs_windows.go > defs_windows_386.h
*/
package runtime
@@ -57,6 +57,9 @@ const (
EXCEPTION_FLT_UNDERFLOW = C.STATUS_FLOAT_UNDERFLOW
EXCEPTION_INT_DIVIDE_BY_ZERO = C.STATUS_INTEGER_DIVIDE_BY_ZERO
EXCEPTION_INT_OVERFLOW = C.STATUS_INTEGER_OVERFLOW
+
+ INFINITE = C.INFINITE
+ WAIT_TIMEOUT = C.WAIT_TIMEOUT
)
type SystemInfo C.SYSTEM_INFO
@@ -64,3 +67,4 @@ type ExceptionRecord C.EXCEPTION_RECORD
type FloatingSaveArea C.FLOATING_SAVE_AREA
type M128a C.M128A
type Context C.CONTEXT
+type Overlapped C.OVERLAPPED
diff --git a/src/pkg/runtime/defs_windows_386.h b/src/pkg/runtime/defs_windows_386.h
index e64a82faf..db3629a1d 100644
--- a/src/pkg/runtime/defs_windows_386.h
+++ b/src/pkg/runtime/defs_windows_386.h
@@ -1,99 +1,113 @@
-// c:\Users\Hector\Code\go\bin\godefs.exe defs.c
+// Created by cgo -cdefs - DO NOT EDIT
+// cgo -cdefs defs_windows.go
-// MACHINE GENERATED - DO NOT EDIT.
-// Constants
enum {
- PROT_NONE = 0,
- PROT_READ = 0x1,
- PROT_WRITE = 0x2,
- PROT_EXEC = 0x4,
- MAP_ANON = 0x1,
- MAP_PRIVATE = 0x2,
- DUPLICATE_SAME_ACCESS = 0x2,
- THREAD_PRIORITY_HIGHEST = 0x2,
- SIGINT = 0x2,
- CTRL_C_EVENT = 0,
- CTRL_BREAK_EVENT = 0x1,
- CONTEXT_CONTROL = 0x10001,
- CONTEXT_FULL = 0x10007,
- EXCEPTION_ACCESS_VIOLATION = 0xc0000005,
- EXCEPTION_BREAKPOINT = 0x80000003,
- EXCEPTION_FLT_DENORMAL_OPERAND = 0xc000008d,
- EXCEPTION_FLT_DIVIDE_BY_ZERO = 0xc000008e,
- EXCEPTION_FLT_INEXACT_RESULT = 0xc000008f,
- EXCEPTION_FLT_OVERFLOW = 0xc0000091,
- EXCEPTION_FLT_UNDERFLOW = 0xc0000093,
- EXCEPTION_INT_DIVIDE_BY_ZERO = 0xc0000094,
- EXCEPTION_INT_OVERFLOW = 0xc0000095,
+ PROT_NONE = 0,
+ PROT_READ = 1,
+ PROT_WRITE = 2,
+ PROT_EXEC = 4,
+
+ MAP_ANON = 1,
+ MAP_PRIVATE = 2,
+
+ DUPLICATE_SAME_ACCESS = 0x2,
+ THREAD_PRIORITY_HIGHEST = 0x2,
+
+ SIGINT = 0x2,
+ CTRL_C_EVENT = 0x0,
+ CTRL_BREAK_EVENT = 0x1,
+
+ CONTEXT_CONTROL = 0x10001,
+ CONTEXT_FULL = 0x10007,
+
+ EXCEPTION_ACCESS_VIOLATION = 0xc0000005,
+ EXCEPTION_BREAKPOINT = 0x80000003,
+ EXCEPTION_FLT_DENORMAL_OPERAND = 0xc000008d,
+ EXCEPTION_FLT_DIVIDE_BY_ZERO = 0xc000008e,
+ EXCEPTION_FLT_INEXACT_RESULT = 0xc000008f,
+ EXCEPTION_FLT_OVERFLOW = 0xc0000091,
+ EXCEPTION_FLT_UNDERFLOW = 0xc0000093,
+ EXCEPTION_INT_DIVIDE_BY_ZERO = 0xc0000094,
+ EXCEPTION_INT_OVERFLOW = 0xc0000095,
+
+ INFINITE = 0xffffffff,
+ WAIT_TIMEOUT = 0x102,
};
-// Types
+typedef struct SystemInfo SystemInfo;
+typedef struct ExceptionRecord ExceptionRecord;
+typedef struct FloatingSaveArea FloatingSaveArea;
+typedef struct M128a M128a;
+typedef struct Context Context;
+typedef struct Overlapped Overlapped;
+
#pragma pack on
-typedef struct SystemInfo SystemInfo;
struct SystemInfo {
- byte Pad_godefs_0[4];
- uint32 dwPageSize;
- void *lpMinimumApplicationAddress;
- void *lpMaximumApplicationAddress;
- uint32 dwActiveProcessorMask;
- uint32 dwNumberOfProcessors;
- uint32 dwProcessorType;
- uint32 dwAllocationGranularity;
- uint16 wProcessorLevel;
- uint16 wProcessorRevision;
+ byte anon0[4];
+ uint32 dwPageSize;
+ byte *lpMinimumApplicationAddress;
+ byte *lpMaximumApplicationAddress;
+ uint32 dwActiveProcessorMask;
+ uint32 dwNumberOfProcessors;
+ uint32 dwProcessorType;
+ uint32 dwAllocationGranularity;
+ uint16 wProcessorLevel;
+ uint16 wProcessorRevision;
};
-
-typedef struct ExceptionRecord ExceptionRecord;
struct ExceptionRecord {
- uint32 ExceptionCode;
- uint32 ExceptionFlags;
- ExceptionRecord *ExceptionRecord;
- void *ExceptionAddress;
- uint32 NumberParameters;
- uint32 ExceptionInformation[15];
+ uint32 ExceptionCode;
+ uint32 ExceptionFlags;
+ ExceptionRecord *ExceptionRecord;
+ byte *ExceptionAddress;
+ uint32 NumberParameters;
+ uint32 ExceptionInformation[15];
};
-
-typedef struct FloatingSaveArea FloatingSaveArea;
struct FloatingSaveArea {
- uint32 ControlWord;
- uint32 StatusWord;
- uint32 TagWord;
- uint32 ErrorOffset;
- uint32 ErrorSelector;
- uint32 DataOffset;
- uint32 DataSelector;
- uint8 RegisterArea[80];
- uint32 Cr0NpxState;
+ uint32 ControlWord;
+ uint32 StatusWord;
+ uint32 TagWord;
+ uint32 ErrorOffset;
+ uint32 ErrorSelector;
+ uint32 DataOffset;
+ uint32 DataSelector;
+ uint8 RegisterArea[80];
+ uint32 Cr0NpxState;
};
-
-typedef struct Context Context;
struct Context {
- uint32 ContextFlags;
- uint32 Dr0;
- uint32 Dr1;
- uint32 Dr2;
- uint32 Dr3;
- uint32 Dr6;
- uint32 Dr7;
- FloatingSaveArea FloatSave;
- uint32 SegGs;
- uint32 SegFs;
- uint32 SegEs;
- uint32 SegDs;
- uint32 Edi;
- uint32 Esi;
- uint32 Ebx;
- uint32 Edx;
- uint32 Ecx;
- uint32 Eax;
- uint32 Ebp;
- uint32 Eip;
- uint32 SegCs;
- uint32 EFlags;
- uint32 Esp;
- uint32 SegSs;
- uint8 ExtendedRegisters[512];
+ uint32 ContextFlags;
+ uint32 Dr0;
+ uint32 Dr1;
+ uint32 Dr2;
+ uint32 Dr3;
+ uint32 Dr6;
+ uint32 Dr7;
+ FloatingSaveArea FloatSave;
+ uint32 SegGs;
+ uint32 SegFs;
+ uint32 SegEs;
+ uint32 SegDs;
+ uint32 Edi;
+ uint32 Esi;
+ uint32 Ebx;
+ uint32 Edx;
+ uint32 Ecx;
+ uint32 Eax;
+ uint32 Ebp;
+ uint32 Eip;
+ uint32 SegCs;
+ uint32 EFlags;
+ uint32 Esp;
+ uint32 SegSs;
+ uint8 ExtendedRegisters[512];
+};
+struct Overlapped {
+ uint32 Internal;
+ uint32 InternalHigh;
+ byte anon0[8];
+ byte *hEvent;
};
+
+
#pragma pack off
diff --git a/src/pkg/runtime/defs_windows_amd64.h b/src/pkg/runtime/defs_windows_amd64.h
index da4c19d90..fe26f5a84 100644
--- a/src/pkg/runtime/defs_windows_amd64.h
+++ b/src/pkg/runtime/defs_windows_amd64.h
@@ -1,114 +1,128 @@
-// c:\go\bin\godefs.exe -f -m64 defs.c
+// Created by cgo -cdefs - DO NOT EDIT
+// cgo -cdefs defs_windows.go
-// MACHINE GENERATED - DO NOT EDIT.
-// Constants
enum {
- PROT_NONE = 0,
- PROT_READ = 0x1,
- PROT_WRITE = 0x2,
- PROT_EXEC = 0x4,
- MAP_ANON = 0x1,
- MAP_PRIVATE = 0x2,
- DUPLICATE_SAME_ACCESS = 0x2,
- THREAD_PRIORITY_HIGHEST = 0x2,
- SIGINT = 0x2,
- CTRL_C_EVENT = 0,
- CTRL_BREAK_EVENT = 0x1,
- CONTEXT_CONTROL = 0x100001,
- CONTEXT_FULL = 0x10000b,
- EXCEPTION_ACCESS_VIOLATION = 0xc0000005,
- EXCEPTION_BREAKPOINT = 0x80000003,
- EXCEPTION_FLT_DENORMAL_OPERAND = 0xc000008d,
- EXCEPTION_FLT_DIVIDE_BY_ZERO = 0xc000008e,
- EXCEPTION_FLT_INEXACT_RESULT = 0xc000008f,
- EXCEPTION_FLT_OVERFLOW = 0xc0000091,
- EXCEPTION_FLT_UNDERFLOW = 0xc0000093,
- EXCEPTION_INT_DIVIDE_BY_ZERO = 0xc0000094,
- EXCEPTION_INT_OVERFLOW = 0xc0000095,
+ PROT_NONE = 0,
+ PROT_READ = 1,
+ PROT_WRITE = 2,
+ PROT_EXEC = 4,
+
+ MAP_ANON = 1,
+ MAP_PRIVATE = 2,
+
+ DUPLICATE_SAME_ACCESS = 0x2,
+ THREAD_PRIORITY_HIGHEST = 0x2,
+
+ SIGINT = 0x2,
+ CTRL_C_EVENT = 0x0,
+ CTRL_BREAK_EVENT = 0x1,
+
+ CONTEXT_CONTROL = 0x100001,
+ CONTEXT_FULL = 0x10000b,
+
+ EXCEPTION_ACCESS_VIOLATION = 0xc0000005,
+ EXCEPTION_BREAKPOINT = 0x80000003,
+ EXCEPTION_FLT_DENORMAL_OPERAND = 0xc000008d,
+ EXCEPTION_FLT_DIVIDE_BY_ZERO = 0xc000008e,
+ EXCEPTION_FLT_INEXACT_RESULT = 0xc000008f,
+ EXCEPTION_FLT_OVERFLOW = 0xc0000091,
+ EXCEPTION_FLT_UNDERFLOW = 0xc0000093,
+ EXCEPTION_INT_DIVIDE_BY_ZERO = 0xc0000094,
+ EXCEPTION_INT_OVERFLOW = 0xc0000095,
+
+ INFINITE = 0xffffffff,
+ WAIT_TIMEOUT = 0x102,
};
-// Types
+typedef struct SystemInfo SystemInfo;
+typedef struct ExceptionRecord ExceptionRecord;
+typedef struct FloatingSaveArea FloatingSaveArea;
+typedef struct M128a M128a;
+typedef struct Context Context;
+typedef struct Overlapped Overlapped;
+
#pragma pack on
-typedef struct SystemInfo SystemInfo;
struct SystemInfo {
- byte Pad_godefs_0[4];
- uint32 dwPageSize;
- void *lpMinimumApplicationAddress;
- void *lpMaximumApplicationAddress;
- uint64 dwActiveProcessorMask;
- uint32 dwNumberOfProcessors;
- uint32 dwProcessorType;
- uint32 dwAllocationGranularity;
- uint16 wProcessorLevel;
- uint16 wProcessorRevision;
+ byte anon0[4];
+ uint32 dwPageSize;
+ byte *lpMinimumApplicationAddress;
+ byte *lpMaximumApplicationAddress;
+ uint64 dwActiveProcessorMask;
+ uint32 dwNumberOfProcessors;
+ uint32 dwProcessorType;
+ uint32 dwAllocationGranularity;
+ uint16 wProcessorLevel;
+ uint16 wProcessorRevision;
};
-
-typedef struct ExceptionRecord ExceptionRecord;
struct ExceptionRecord {
- uint32 ExceptionCode;
- uint32 ExceptionFlags;
- ExceptionRecord *ExceptionRecord;
- void *ExceptionAddress;
- uint32 NumberParameters;
- byte pad_godefs_0[4];
- uint64 ExceptionInformation[15];
+ uint32 ExceptionCode;
+ uint32 ExceptionFlags;
+ ExceptionRecord *ExceptionRecord;
+ byte *ExceptionAddress;
+ uint32 NumberParameters;
+ byte Pad_cgo_0[4];
+ uint64 ExceptionInformation[15];
};
-
-typedef struct M128a M128a;
struct M128a {
- uint64 Low;
- int64 High;
+ uint64 Low;
+ int64 High;
};
-
-typedef struct Context Context;
struct Context {
- uint64 P1Home;
- uint64 P2Home;
- uint64 P3Home;
- uint64 P4Home;
- uint64 P5Home;
- uint64 P6Home;
- uint32 ContextFlags;
- uint32 MxCsr;
- uint16 SegCs;
- uint16 SegDs;
- uint16 SegEs;
- uint16 SegFs;
- uint16 SegGs;
- uint16 SegSs;
- uint32 EFlags;
- uint64 Dr0;
- uint64 Dr1;
- uint64 Dr2;
- uint64 Dr3;
- uint64 Dr6;
- uint64 Dr7;
- uint64 Rax;
- uint64 Rcx;
- uint64 Rdx;
- uint64 Rbx;
- uint64 Rsp;
- uint64 Rbp;
- uint64 Rsi;
- uint64 Rdi;
- uint64 R8;
- uint64 R9;
- uint64 R10;
- uint64 R11;
- uint64 R12;
- uint64 R13;
- uint64 R14;
- uint64 R15;
- uint64 Rip;
- byte Pad_godefs_0[512];
- M128a VectorRegister[26];
- uint64 VectorControl;
- uint64 DebugControl;
- uint64 LastBranchToRip;
- uint64 LastBranchFromRip;
- uint64 LastExceptionToRip;
- uint64 LastExceptionFromRip;
+ uint64 P1Home;
+ uint64 P2Home;
+ uint64 P3Home;
+ uint64 P4Home;
+ uint64 P5Home;
+ uint64 P6Home;
+ uint32 ContextFlags;
+ uint32 MxCsr;
+ uint16 SegCs;
+ uint16 SegDs;
+ uint16 SegEs;
+ uint16 SegFs;
+ uint16 SegGs;
+ uint16 SegSs;
+ uint32 EFlags;
+ uint64 Dr0;
+ uint64 Dr1;
+ uint64 Dr2;
+ uint64 Dr3;
+ uint64 Dr6;
+ uint64 Dr7;
+ uint64 Rax;
+ uint64 Rcx;
+ uint64 Rdx;
+ uint64 Rbx;
+ uint64 Rsp;
+ uint64 Rbp;
+ uint64 Rsi;
+ uint64 Rdi;
+ uint64 R8;
+ uint64 R9;
+ uint64 R10;
+ uint64 R11;
+ uint64 R12;
+ uint64 R13;
+ uint64 R14;
+ uint64 R15;
+ uint64 Rip;
+ byte anon0[512];
+ M128a VectorRegister[26];
+ uint64 VectorControl;
+ uint64 DebugControl;
+ uint64 LastBranchToRip;
+ uint64 LastBranchFromRip;
+ uint64 LastExceptionToRip;
+ uint64 LastExceptionFromRip;
+};
+struct Overlapped {
+ uint64 Internal;
+ uint64 InternalHigh;
+ byte anon0[8];
+ byte *hEvent;
};
+
+
#pragma pack off
diff --git a/src/pkg/runtime/env_plan9.c b/src/pkg/runtime/env_plan9.c
index 0483d7eef..599319c75 100644
--- a/src/pkg/runtime/env_plan9.c
+++ b/src/pkg/runtime/env_plan9.c
@@ -8,7 +8,8 @@
byte*
runtime·getenv(int8 *s)
{
- int32 fd, len, n, r;
+ int32 fd, n, r;
+ intgo len;
byte file[128];
byte *p;
diff --git a/src/pkg/runtime/env_posix.c b/src/pkg/runtime/env_posix.c
index 8333811fb..00ce577d0 100644
--- a/src/pkg/runtime/env_posix.c
+++ b/src/pkg/runtime/env_posix.c
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin freebsd linux netbsd openbsd windows
+// +build darwin dragonfly freebsd linux netbsd openbsd windows
#include "runtime.h"
@@ -11,7 +11,8 @@ Slice syscall·envs;
byte*
runtime·getenv(int8 *s)
{
- int32 i, j, len;
+ int32 i, j;
+ intgo len;
byte *v, *bs;
String* envv;
int32 envc;
diff --git a/src/pkg/runtime/error.go b/src/pkg/runtime/error.go
index b6b520cf2..bd7090883 100644
--- a/src/pkg/runtime/error.go
+++ b/src/pkg/runtime/error.go
@@ -74,6 +74,22 @@ func newErrorString(s string, ret *interface{}) {
*ret = errorString(s)
}
+// An errorCString represents a runtime error described by a single C string.
+type errorCString uintptr
+
+func (e errorCString) RuntimeError() {}
+
+func cstringToGo(uintptr) string
+
+func (e errorCString) Error() string {
+ return "runtime error: " + cstringToGo(uintptr(e))
+}
+
+// For calling from C.
+func newErrorCString(s uintptr, ret *interface{}) {
+ *ret = errorCString(s)
+}
+
type stringer interface {
String() string
}
diff --git a/src/pkg/runtime/export_futex_test.go b/src/pkg/runtime/export_futex_test.go
index bcab60fbe..1477828a7 100644
--- a/src/pkg/runtime/export_futex_test.go
+++ b/src/pkg/runtime/export_futex_test.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.
-// +build linux freebsd
+// +build dragonfly freebsd linux
package runtime
diff --git a/src/pkg/runtime/export_test.c b/src/pkg/runtime/export_test.c
new file mode 100644
index 000000000..5ad1a7007
--- /dev/null
+++ b/src/pkg/runtime/export_test.c
@@ -0,0 +1,13 @@
+// Copyright 2013 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 "runtime.h"
+#include "arch_GOARCH.h"
+
+void
+·GogoBytes(int32 x)
+{
+ x = RuntimeGogoBytes;
+ FLUSH(&x);
+}
diff --git a/src/pkg/runtime/export_test.go b/src/pkg/runtime/export_test.go
index 062aea248..d170fa72a 100644
--- a/src/pkg/runtime/export_test.go
+++ b/src/pkg/runtime/export_test.go
@@ -67,3 +67,20 @@ func testSchedLocalQueueSteal()
var TestSchedLocalQueue1 = testSchedLocalQueue
var TestSchedLocalQueueSteal1 = testSchedLocalQueueSteal
+
+func haveGoodHash() bool
+func stringHash(s string, seed uintptr) uintptr
+func bytesHash(b []byte, seed uintptr) uintptr
+func int32Hash(i uint32, seed uintptr) uintptr
+func int64Hash(i uint64, seed uintptr) uintptr
+
+var HaveGoodHash = haveGoodHash
+var StringHash = stringHash
+var BytesHash = bytesHash
+var Int32Hash = int32Hash
+var Int64Hash = int64Hash
+
+func GogoBytes() int32
+
+var hashLoad float64 // declared in hashmap.c
+var HashLoad = &hashLoad
diff --git a/src/pkg/runtime/extern.go b/src/pkg/runtime/extern.go
index 20f234253..527e9cdf8 100644
--- a/src/pkg/runtime/extern.go
+++ b/src/pkg/runtime/extern.go
@@ -21,11 +21,20 @@ is GOGC=100. Setting GOGC=off disables the garbage collector entirely.
The runtime/debug package's SetGCPercent function allows changing this
percentage at run time. See http://golang.org/pkg/runtime/debug/#SetGCPercent.
-The GOGCTRACE variable controls debug output from the garbage collector.
-Setting GOGCTRACE=1 causes the garbage collector to emit a single line to standard
-error at each collection, summarizing the amount of memory collected and the
-length of the pause. Setting GOGCTRACE=2 emits the same summary but also
-repeats each collection.
+The GODEBUG variable controls debug output from the runtime. GODEBUG value is
+a comma-separated list of name=val pairs. Supported names are:
+
+ gctrace: setting gctrace=1 causes the garbage collector to emit a single line to standard
+ error at each collection, summarizing the amount of memory collected and the
+ length of the pause. Setting gctrace=2 emits the same summary but also
+ repeats each collection.
+
+ schedtrace: setting schedtrace=X causes the scheduler to emit a single line to standard
+ error every X milliseconds, summarizing the scheduler state.
+
+ scheddetail: setting schedtrace=X and scheddetail=1 causes the scheduler to emit
+ detailed multiline info every X milliseconds, describing state of the scheduler,
+ processors, threads and goroutines.
The GOMAXPROCS variable limits the number of operating system threads that
can execute user-level Go code simultaneously. There is no limit to the number of threads
@@ -77,17 +86,8 @@ func Caller(skip int) (pc uintptr, file string, line int, ok bool)
// It returns the number of entries written to pc.
func Callers(skip int, pc []uintptr) int
-type Func struct { // Keep in sync with runtime.h:struct Func
- name string
- typ string // go type string
- src string // src file name
- pcln []byte // pc/ln tab for this func
- entry uintptr // entry pc
- pc0 uintptr // starting pc, ln for table
- ln0 int32
- frame int32 // stack frame size
- args int32 // in/out args size
- locals int32 // locals size
+type Func struct {
+ opaque struct{} // unexported field to disallow conversions
}
// FuncForPC returns a *Func describing the function that contains the
@@ -95,10 +95,14 @@ type Func struct { // Keep in sync with runtime.h:struct Func
func FuncForPC(pc uintptr) *Func
// Name returns the name of the function.
-func (f *Func) Name() string { return f.name }
+func (f *Func) Name() string {
+ return funcname_go(f)
+}
// Entry returns the entry address of the function.
-func (f *Func) Entry() uintptr { return f.entry }
+func (f *Func) Entry() uintptr {
+ return funcentry_go(f)
+}
// FileLine returns the file name and line number of the
// source code corresponding to the program counter pc.
@@ -110,9 +114,8 @@ func (f *Func) FileLine(pc uintptr) (file string, line int) {
// implemented in symtab.c
func funcline_go(*Func, uintptr) (string, int)
-
-// mid returns the current OS thread (m) id.
-func mid() uint32
+func funcname_go(*Func) string
+func funcentry_go(*Func) uintptr
// SetFinalizer sets the finalizer associated with x to f.
// When the garbage collector finds an unreachable block
@@ -127,8 +130,9 @@ func mid() uint32
// The argument x must be a pointer to an object allocated by
// calling new or by taking the address of a composite literal.
// The argument f must be a function that takes a single argument
-// of x's type and can have arbitrary ignored return values.
-// If either of these is not true, SetFinalizer aborts the program.
+// to which x's type can be assigned, and can have arbitrary ignored return
+// values. If either of these is not true, SetFinalizer aborts the
+// program.
//
// Finalizers are run in dependency order: if A points at B, both have
// finalizers, and they are otherwise unreachable, only the finalizer
diff --git a/src/pkg/runtime/funcdata.h b/src/pkg/runtime/funcdata.h
new file mode 100644
index 000000000..166263ef9
--- /dev/null
+++ b/src/pkg/runtime/funcdata.h
@@ -0,0 +1,22 @@
+// Copyright 2013 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 defines the IDs for PCDATA and FUNCDATA instructions
+// in Go binaries. It is included by both C and assembly, so it must
+// be written using #defines. It is included by the runtime package
+// as well as the compilers.
+
+#define PCDATA_ArgSize 0 /* argument size at CALL instruction */
+
+#define FUNCDATA_GCArgs 0 /* garbage collector blocks */
+#define FUNCDATA_GCLocals 1
+
+// To be used in assembly.
+#define ARGSIZE(n) PCDATA $PCDATA_ArgSize, $n
+
+// ArgsSizeUnknown is set in Func.argsize to mark all functions
+// whose argument size is unknown (C vararg functions, and
+// assembly code without an explicit specification).
+// This value is generated by the compiler, assembler, or linker.
+#define ArgsSizeUnknown 0x80000000
diff --git a/src/pkg/runtime/futex_test.go b/src/pkg/runtime/futex_test.go
index 51f4d0f12..f4054b7e7 100644
--- a/src/pkg/runtime/futex_test.go
+++ b/src/pkg/runtime/futex_test.go
@@ -2,7 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build linux freebsd
+// Futex is only available on Dragonfly, FreeBSD and Linux.
+// The race detector emits calls to split stack functions so it breaks the test.
+// +build dragonfly freebsd linux
+// +build !race
package runtime_test
diff --git a/src/pkg/runtime/gc_test.go b/src/pkg/runtime/gc_test.go
index a3c731ccb..dbd68c1c7 100644
--- a/src/pkg/runtime/gc_test.go
+++ b/src/pkg/runtime/gc_test.go
@@ -136,7 +136,9 @@ func TestGcRescan(t *testing.T) {
for i := 0; i < 10; i++ {
p := &Y{}
p.c = make(chan error)
- p.nextx = &head.X
+ if head != nil {
+ p.nextx = &head.X
+ }
p.nexty = head
p.p = new(int)
*p.p = 42
diff --git a/src/pkg/runtime/hash_test.go b/src/pkg/runtime/hash_test.go
new file mode 100644
index 000000000..312c4be8e
--- /dev/null
+++ b/src/pkg/runtime/hash_test.go
@@ -0,0 +1,512 @@
+// Copyright 2013 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_test
+
+import (
+ "fmt"
+ "math"
+ "math/rand"
+ . "runtime"
+ "strings"
+ "testing"
+)
+
+// Smhasher is a torture test for hash functions.
+// https://code.google.com/p/smhasher/
+// This code is a port of some of the Smhasher tests to Go.
+//
+// The current AES hash function passes Smhasher. Our fallback
+// hash functions don't, so we only enable the difficult tests when
+// we know the AES implementation is available.
+
+// Sanity checks.
+// hash should not depend on values outside key.
+// hash should not depend on alignment.
+func TestSmhasherSanity(t *testing.T) {
+ r := rand.New(rand.NewSource(1234))
+ const REP = 10
+ const KEYMAX = 128
+ const PAD = 16
+ const OFFMAX = 16
+ for k := 0; k < REP; k++ {
+ for n := 0; n < KEYMAX; n++ {
+ for i := 0; i < OFFMAX; i++ {
+ var b [KEYMAX + OFFMAX + 2*PAD]byte
+ var c [KEYMAX + OFFMAX + 2*PAD]byte
+ randBytes(r, b[:])
+ randBytes(r, c[:])
+ copy(c[PAD+i:PAD+i+n], b[PAD:PAD+n])
+ if BytesHash(b[PAD:PAD+n], 0) != BytesHash(c[PAD+i:PAD+i+n], 0) {
+ t.Errorf("hash depends on bytes outside key")
+ }
+ }
+ }
+ }
+}
+
+type HashSet struct {
+ m map[uintptr]struct{} // set of hashes added
+ n int // number of hashes added
+}
+
+func newHashSet() *HashSet {
+ return &HashSet{make(map[uintptr]struct{}), 0}
+}
+func (s *HashSet) add(h uintptr) {
+ s.m[h] = struct{}{}
+ s.n++
+}
+func (s *HashSet) addS(x string) {
+ s.add(StringHash(x, 0))
+}
+func (s *HashSet) addB(x []byte) {
+ s.add(BytesHash(x, 0))
+}
+func (s *HashSet) addS_seed(x string, seed uintptr) {
+ s.add(StringHash(x, seed))
+}
+func (s *HashSet) check(t *testing.T) {
+ const SLOP = 10.0
+ collisions := s.n - len(s.m)
+ //fmt.Printf("%d/%d\n", len(s.m), s.n)
+ pairs := int64(s.n) * int64(s.n-1) / 2
+ expected := float64(pairs) / math.Pow(2.0, float64(hashSize))
+ stddev := math.Sqrt(expected)
+ if float64(collisions) > expected+SLOP*3*stddev {
+ t.Errorf("unexpected number of collisions: got=%d mean=%f stddev=%f", collisions, expected, stddev)
+ }
+}
+
+// a string plus adding zeros must make distinct hashes
+func TestSmhasherAppendedZeros(t *testing.T) {
+ s := "hello" + strings.Repeat("\x00", 256)
+ h := newHashSet()
+ for i := 0; i <= len(s); i++ {
+ h.addS(s[:i])
+ }
+ h.check(t)
+}
+
+// All 0-3 byte strings have distinct hashes.
+func TestSmhasherSmallKeys(t *testing.T) {
+ h := newHashSet()
+ var b [3]byte
+ for i := 0; i < 256; i++ {
+ b[0] = byte(i)
+ h.addB(b[:1])
+ for j := 0; j < 256; j++ {
+ b[1] = byte(j)
+ h.addB(b[:2])
+ if !testing.Short() {
+ for k := 0; k < 256; k++ {
+ b[2] = byte(k)
+ h.addB(b[:3])
+ }
+ }
+ }
+ }
+ h.check(t)
+}
+
+// Different length strings of all zeros have distinct hashes.
+func TestSmhasherZeros(t *testing.T) {
+ N := 256 * 1024
+ if testing.Short() {
+ N = 1024
+ }
+ h := newHashSet()
+ b := make([]byte, N)
+ for i := 0; i <= N; i++ {
+ h.addB(b[:i])
+ }
+ h.check(t)
+}
+
+// Strings with up to two nonzero bytes all have distinct hashes.
+func TestSmhasherTwoNonzero(t *testing.T) {
+ if testing.Short() {
+ t.Skip("Skipping in short mode")
+ }
+ h := newHashSet()
+ for n := 2; n <= 16; n++ {
+ twoNonZero(h, n)
+ }
+ h.check(t)
+}
+func twoNonZero(h *HashSet, n int) {
+ b := make([]byte, n)
+
+ // all zero
+ h.addB(b[:])
+
+ // one non-zero byte
+ for i := 0; i < n; i++ {
+ for x := 1; x < 256; x++ {
+ b[i] = byte(x)
+ h.addB(b[:])
+ b[i] = 0
+ }
+ }
+
+ // two non-zero bytes
+ for i := 0; i < n; i++ {
+ for x := 1; x < 256; x++ {
+ b[i] = byte(x)
+ for j := i + 1; j < n; j++ {
+ for y := 1; y < 256; y++ {
+ b[j] = byte(y)
+ h.addB(b[:])
+ b[j] = 0
+ }
+ }
+ b[i] = 0
+ }
+ }
+}
+
+// Test strings with repeats, like "abcdabcdabcdabcd..."
+func TestSmhasherCyclic(t *testing.T) {
+ if testing.Short() {
+ t.Skip("Skipping in short mode")
+ }
+ if !HaveGoodHash() {
+ t.Skip("fallback hash not good enough for this test")
+ }
+ r := rand.New(rand.NewSource(1234))
+ const REPEAT = 8
+ const N = 1000000
+ for n := 4; n <= 12; n++ {
+ h := newHashSet()
+ b := make([]byte, REPEAT*n)
+ for i := 0; i < N; i++ {
+ b[0] = byte(i * 79 % 97)
+ b[1] = byte(i * 43 % 137)
+ b[2] = byte(i * 151 % 197)
+ b[3] = byte(i * 199 % 251)
+ randBytes(r, b[4:n])
+ for j := n; j < n*REPEAT; j++ {
+ b[j] = b[j-n]
+ }
+ h.addB(b)
+ }
+ h.check(t)
+ }
+}
+
+// Test strings with only a few bits set
+func TestSmhasherSparse(t *testing.T) {
+ if testing.Short() {
+ t.Skip("Skipping in short mode")
+ }
+ sparse(t, 32, 6)
+ sparse(t, 40, 6)
+ sparse(t, 48, 5)
+ sparse(t, 56, 5)
+ sparse(t, 64, 5)
+ sparse(t, 96, 4)
+ sparse(t, 256, 3)
+ sparse(t, 2048, 2)
+}
+func sparse(t *testing.T, n int, k int) {
+ b := make([]byte, n/8)
+ h := newHashSet()
+ setbits(h, b, 0, k)
+ h.check(t)
+}
+
+// set up to k bits at index i and greater
+func setbits(h *HashSet, b []byte, i int, k int) {
+ h.addB(b)
+ if k == 0 {
+ return
+ }
+ for j := i; j < len(b)*8; j++ {
+ b[j/8] |= byte(1 << uint(j&7))
+ setbits(h, b, j+1, k-1)
+ b[j/8] &= byte(^(1 << uint(j&7)))
+ }
+}
+
+// Test all possible combinations of n blocks from the set s.
+// "permutation" is a bad name here, but it is what Smhasher uses.
+func TestSmhasherPermutation(t *testing.T) {
+ if testing.Short() {
+ t.Skip("Skipping in short mode")
+ }
+ if !HaveGoodHash() {
+ t.Skip("fallback hash not good enough for this test")
+ }
+ permutation(t, []uint32{0, 1, 2, 3, 4, 5, 6, 7}, 8)
+ permutation(t, []uint32{0, 1 << 29, 2 << 29, 3 << 29, 4 << 29, 5 << 29, 6 << 29, 7 << 29}, 8)
+ permutation(t, []uint32{0, 1}, 20)
+ permutation(t, []uint32{0, 1 << 31}, 20)
+ permutation(t, []uint32{0, 1, 2, 3, 4, 5, 6, 7, 1 << 29, 2 << 29, 3 << 29, 4 << 29, 5 << 29, 6 << 29, 7 << 29}, 6)
+}
+func permutation(t *testing.T, s []uint32, n int) {
+ b := make([]byte, n*4)
+ h := newHashSet()
+ genPerm(h, b, s, 0)
+ h.check(t)
+}
+func genPerm(h *HashSet, b []byte, s []uint32, n int) {
+ h.addB(b[:n])
+ if n == len(b) {
+ return
+ }
+ for _, v := range s {
+ b[n] = byte(v)
+ b[n+1] = byte(v >> 8)
+ b[n+2] = byte(v >> 16)
+ b[n+3] = byte(v >> 24)
+ genPerm(h, b, s, n+4)
+ }
+}
+
+type Key interface {
+ clear() // set bits all to 0
+ random(r *rand.Rand) // set key to something random
+ bits() int // how many bits key has
+ flipBit(i int) // flip bit i of the key
+ hash() uintptr // hash the key
+ name() string // for error reporting
+}
+
+type BytesKey struct {
+ b []byte
+}
+
+func (k *BytesKey) clear() {
+ for i := range k.b {
+ k.b[i] = 0
+ }
+}
+func (k *BytesKey) random(r *rand.Rand) {
+ randBytes(r, k.b)
+}
+func (k *BytesKey) bits() int {
+ return len(k.b) * 8
+}
+func (k *BytesKey) flipBit(i int) {
+ k.b[i>>3] ^= byte(1 << uint(i&7))
+}
+func (k *BytesKey) hash() uintptr {
+ return BytesHash(k.b, 0)
+}
+func (k *BytesKey) name() string {
+ return fmt.Sprintf("bytes%d", len(k.b))
+}
+
+type Int32Key struct {
+ i uint32
+}
+
+func (k *Int32Key) clear() {
+ k.i = 0
+}
+func (k *Int32Key) random(r *rand.Rand) {
+ k.i = r.Uint32()
+}
+func (k *Int32Key) bits() int {
+ return 32
+}
+func (k *Int32Key) flipBit(i int) {
+ k.i ^= 1 << uint(i)
+}
+func (k *Int32Key) hash() uintptr {
+ return Int32Hash(k.i, 0)
+}
+func (k *Int32Key) name() string {
+ return "int32"
+}
+
+type Int64Key struct {
+ i uint64
+}
+
+func (k *Int64Key) clear() {
+ k.i = 0
+}
+func (k *Int64Key) random(r *rand.Rand) {
+ k.i = uint64(r.Uint32()) + uint64(r.Uint32())<<32
+}
+func (k *Int64Key) bits() int {
+ return 64
+}
+func (k *Int64Key) flipBit(i int) {
+ k.i ^= 1 << uint(i)
+}
+func (k *Int64Key) hash() uintptr {
+ return Int64Hash(k.i, 0)
+}
+func (k *Int64Key) name() string {
+ return "int64"
+}
+
+// Flipping a single bit of a key should flip each output bit with 50% probability.
+func TestSmhasherAvalanche(t *testing.T) {
+ if !HaveGoodHash() {
+ t.Skip("fallback hash not good enough for this test")
+ }
+ if testing.Short() {
+ t.Skip("Skipping in short mode")
+ }
+ avalancheTest1(t, &BytesKey{make([]byte, 2)})
+ avalancheTest1(t, &BytesKey{make([]byte, 4)})
+ avalancheTest1(t, &BytesKey{make([]byte, 8)})
+ avalancheTest1(t, &BytesKey{make([]byte, 16)})
+ avalancheTest1(t, &BytesKey{make([]byte, 32)})
+ avalancheTest1(t, &BytesKey{make([]byte, 200)})
+ avalancheTest1(t, &Int32Key{})
+ avalancheTest1(t, &Int64Key{})
+}
+func avalancheTest1(t *testing.T, k Key) {
+ const REP = 100000
+ r := rand.New(rand.NewSource(1234))
+ n := k.bits()
+
+ // grid[i][j] is a count of whether flipping
+ // input bit i affects output bit j.
+ grid := make([][hashSize]int, n)
+
+ for z := 0; z < REP; z++ {
+ // pick a random key, hash it
+ k.random(r)
+ h := k.hash()
+
+ // flip each bit, hash & compare the results
+ for i := 0; i < n; i++ {
+ k.flipBit(i)
+ d := h ^ k.hash()
+ k.flipBit(i)
+
+ // record the effects of that bit flip
+ g := &grid[i]
+ for j := 0; j < hashSize; j++ {
+ g[j] += int(d & 1)
+ d >>= 1
+ }
+ }
+ }
+
+ // Each entry in the grid should be about REP/2.
+ // More precisely, we did N = k.bits() * hashSize experiments where
+ // each is the sum of REP coin flips. We want to find bounds on the
+ // sum of coin flips such that a truly random experiment would have
+ // all sums inside those bounds with 99% probability.
+ N := n * hashSize
+ var c float64
+ // find c such that Prob(mean-c*stddev < x < mean+c*stddev)^N > .99
+ for c = 0.0; math.Pow(math.Erf(c/math.Sqrt(2)), float64(N)) < .99; c += .1 {
+ }
+ c *= 2.0 // allowed slack - we don't need to be perfectly random
+ mean := .5 * REP
+ stddev := .5 * math.Sqrt(REP)
+ low := int(mean - c*stddev)
+ high := int(mean + c*stddev)
+ for i := 0; i < n; i++ {
+ for j := 0; j < hashSize; j++ {
+ x := grid[i][j]
+ if x < low || x > high {
+ t.Errorf("bad bias for %s bit %d -> bit %d: %d/%d\n", k.name(), i, j, x, REP)
+ }
+ }
+ }
+}
+
+// All bit rotations of a set of distinct keys
+func TestSmhasherWindowed(t *testing.T) {
+ windowed(t, &Int32Key{})
+ windowed(t, &Int64Key{})
+ windowed(t, &BytesKey{make([]byte, 128)})
+}
+func windowed(t *testing.T, k Key) {
+ if testing.Short() {
+ t.Skip("Skipping in short mode")
+ }
+ const BITS = 16
+
+ for r := 0; r < k.bits(); r++ {
+ h := newHashSet()
+ for i := 0; i < 1<<BITS; i++ {
+ k.clear()
+ for j := 0; j < BITS; j++ {
+ if i>>uint(j)&1 != 0 {
+ k.flipBit((j + r) % k.bits())
+ }
+ }
+ h.add(k.hash())
+ }
+ h.check(t)
+ }
+}
+
+// All keys of the form prefix + [A-Za-z0-9]*N + suffix.
+func TestSmhasherText(t *testing.T) {
+ if testing.Short() {
+ t.Skip("Skipping in short mode")
+ }
+ text(t, "Foo", "Bar")
+ text(t, "FooBar", "")
+ text(t, "", "FooBar")
+}
+func text(t *testing.T, prefix, suffix string) {
+ const N = 4
+ const S = "ABCDEFGHIJKLMNOPQRSTabcdefghijklmnopqrst0123456789"
+ const L = len(S)
+ b := make([]byte, len(prefix)+N+len(suffix))
+ copy(b, prefix)
+ copy(b[len(prefix)+N:], suffix)
+ h := newHashSet()
+ c := b[len(prefix):]
+ for i := 0; i < L; i++ {
+ c[0] = S[i]
+ for j := 0; j < L; j++ {
+ c[1] = S[j]
+ for k := 0; k < L; k++ {
+ c[2] = S[k]
+ for x := 0; x < L; x++ {
+ c[3] = S[x]
+ h.addB(b)
+ }
+ }
+ }
+ }
+ h.check(t)
+}
+
+// Make sure different seed values generate different hashes.
+func TestSmhasherSeed(t *testing.T) {
+ h := newHashSet()
+ const N = 100000
+ s := "hello"
+ for i := 0; i < N; i++ {
+ h.addS_seed(s, uintptr(i))
+ }
+ h.check(t)
+}
+
+// size of the hash output (32 or 64 bits)
+const hashSize = 32 + int(^uintptr(0)>>63<<5)
+
+func randBytes(r *rand.Rand, b []byte) {
+ for i := range b {
+ b[i] = byte(r.Uint32())
+ }
+}
+
+func benchmarkHash(b *testing.B, n int) {
+ s := strings.Repeat("A", n)
+
+ for i := 0; i < b.N; i++ {
+ StringHash(s, 0)
+ }
+ b.SetBytes(int64(n))
+}
+
+func BenchmarkHash5(b *testing.B) { benchmarkHash(b, 5) }
+func BenchmarkHash16(b *testing.B) { benchmarkHash(b, 16) }
+func BenchmarkHash64(b *testing.B) { benchmarkHash(b, 64) }
+func BenchmarkHash1024(b *testing.B) { benchmarkHash(b, 1024) }
+func BenchmarkHash65536(b *testing.B) { benchmarkHash(b, 65536) }
diff --git a/src/pkg/runtime/hashmap.c b/src/pkg/runtime/hashmap.c
index 892f0a170..6d2ab2168 100644
--- a/src/pkg/runtime/hashmap.c
+++ b/src/pkg/runtime/hashmap.c
@@ -5,9 +5,9 @@
#include "runtime.h"
#include "arch_GOARCH.h"
#include "malloc.h"
-#include "hashmap.h"
#include "type.h"
#include "race.h"
+#include "../../cmd/ld/textflag.h"
// This file contains the implementation of Go's map type.
//
@@ -74,6 +74,8 @@
typedef struct Bucket Bucket;
struct Bucket
{
+ // Note: the format of the Bucket is encoded in ../../cmd/gc/reflect.c and
+ // ../reflect/type.go. Don't change this structure without also changing that code!
uint8 tophash[BUCKETSIZE]; // top 8 bits of hash of each entry (0 = empty)
Bucket *overflow; // overflow bucket, if any
byte data[1]; // BUCKETSIZE keys followed by BUCKETSIZE values
@@ -89,19 +91,18 @@ struct Bucket
#define evacuated(b) (((uintptr)(b)->overflow & 1) != 0)
#define overflowptr(b) ((Bucket*)((uintptr)(b)->overflow & ~(uintptr)1))
-// Initialize bucket to the empty state. This only works if BUCKETSIZE==8!
-#define clearbucket(b) { *(uint64*)((b)->tophash) = 0; (b)->overflow = nil; }
-
struct Hmap
{
+ // Note: the format of the Hmap is encoded in ../../cmd/gc/reflect.c and
+ // ../reflect/type.go. Don't change this structure without also changing that code!
uintgo count; // # live cells == size of map. Must be first (used by len() builtin)
uint32 flags;
+ uint32 hash0; // hash seed
uint8 B; // log_2 of # of buckets (can hold up to LOAD * 2^B items)
uint8 keysize; // key size in bytes
uint8 valuesize; // value size in bytes
uint16 bucketsize; // bucket size in bytes
- uintptr hash0; // hash seed
byte *buckets; // array of 2^B Buckets. may be nil if count==0.
byte *oldbuckets; // previous bucket array of half the size, non-nil only when growing
uintptr nevacuate; // progress counter for evacuation (buckets less than this have been evacuated)
@@ -114,8 +115,6 @@ enum
IndirectValue = 2, // storing pointers to values
Iterator = 4, // there may be an iterator using buckets
OldIterator = 8, // there may be an iterator using oldbuckets
- CanFreeBucket = 16, // ok to free buckets
- CanFreeKey = 32, // keys are indirect and ok to free keys
};
// Macros for dereferencing indirect keys
@@ -208,17 +207,15 @@ hash_init(MapType *t, Hmap *h, uint32 hint)
{
uint8 B;
byte *buckets;
- uintptr i;
uintptr keysize, valuesize, bucketsize;
uint8 flags;
- Bucket *b;
- flags = CanFreeBucket;
+ flags = 0;
// figure out how big we have to make everything
keysize = t->key->size;
if(keysize > MAXKEYSIZE) {
- flags |= IndirectKey | CanFreeKey;
+ flags |= IndirectKey;
keysize = sizeof(byte*);
}
valuesize = t->elem->size;
@@ -240,8 +237,6 @@ hash_init(MapType *t, Hmap *h, uint32 hint)
runtime·throw("value size not a multiple of value align");
if(BUCKETSIZE < 8)
runtime·throw("bucketsize too small for proper alignment");
- if(BUCKETSIZE != 8)
- runtime·throw("must redo clearbucket");
if(sizeof(void*) == 4 && t->key->align > 4)
runtime·throw("need padding in bucket (key)");
if(sizeof(void*) == 4 && t->elem->align > 4)
@@ -259,16 +254,10 @@ hash_init(MapType *t, Hmap *h, uint32 hint)
// done lazily later.
buckets = nil;
} else {
- buckets = runtime·mallocgc(bucketsize << B, 0, 1, 0);
- for(i = 0; i < (uintptr)1 << B; i++) {
- b = (Bucket*)(buckets + i * bucketsize);
- clearbucket(b);
- }
+ buckets = runtime·cnewarray(t->bucket, (uintptr)1 << B);
}
// initialize Hmap
- // Note: we save all these stores to the end so gciter doesn't see
- // a partially initialized map.
h->count = 0;
h->B = B;
h->flags = flags;
@@ -299,19 +288,18 @@ evacuate(MapType *t, Hmap *h, uintptr oldbucket)
uintptr i;
byte *k, *v;
byte *xk, *yk, *xv, *yv;
- byte *ob;
+ uint8 top;
+ bool eq;
b = (Bucket*)(h->oldbuckets + oldbucket * h->bucketsize);
newbit = (uintptr)1 << (h->B - 1);
if(!evacuated(b)) {
// TODO: reuse overflow buckets instead of using new ones, if there
- // is no iterator using the old buckets. (If CanFreeBuckets and !OldIterator.)
+ // is no iterator using the old buckets. (If !OldIterator.)
x = (Bucket*)(h->buckets + oldbucket * h->bucketsize);
y = (Bucket*)(h->buckets + (oldbucket + newbit) * h->bucketsize);
- clearbucket(x);
- clearbucket(y);
xi = 0;
yi = 0;
xk = x->data;
@@ -320,25 +308,49 @@ evacuate(MapType *t, Hmap *h, uintptr oldbucket)
yv = yk + h->keysize * BUCKETSIZE;
do {
for(i = 0, k = b->data, v = k + h->keysize * BUCKETSIZE; i < BUCKETSIZE; i++, k += h->keysize, v += h->valuesize) {
- if(b->tophash[i] == 0)
+ top = b->tophash[i];
+ if(top == 0)
continue;
+
+ // Compute hash to make our evacuation decision (whether we need
+ // to send this key/value to bucket x or bucket y).
hash = h->hash0;
t->key->alg->hash(&hash, t->key->size, IK(h, k));
- // NOTE: if key != key, then this hash could be (and probably will be)
- // entirely different from the old hash. We effectively only update
- // the B'th bit of the hash in this case.
+ if((h->flags & Iterator) != 0) {
+ t->key->alg->equal(&eq, t->key->size, IK(h, k), IK(h, k));
+ if(!eq) {
+ // If key != key (NaNs), then the hash could be (and probably
+ // will be) entirely different from the old hash. Moreover,
+ // it isn't reproducible. Reproducibility is required in the
+ // presence of iterators, as our evacuation decision must
+ // match whatever decision the iterator made.
+ // Fortunately, we have the freedom to send these keys either
+ // way. Also, tophash is meaningless for these kinds of keys.
+ // We let the low bit of tophash drive the evacuation decision.
+ // We recompute a new random tophash for the next level so
+ // these keys will get evenly distributed across all buckets
+ // after multiple grows.
+ if((top & 1) != 0)
+ hash |= newbit;
+ else
+ hash &= ~newbit;
+ top = hash >> (8*sizeof(uintptr)-8);
+ if(top == 0)
+ top = 1;
+ }
+ }
+
if((hash & newbit) == 0) {
if(xi == BUCKETSIZE) {
if(checkgc) mstats.next_gc = mstats.heap_alloc;
- newx = runtime·mallocgc(h->bucketsize, 0, 1, 0);
- clearbucket(newx);
+ newx = runtime·cnew(t->bucket);
x->overflow = newx;
x = newx;
xi = 0;
xk = x->data;
xv = xk + h->keysize * BUCKETSIZE;
}
- x->tophash[xi] = b->tophash[i];
+ x->tophash[xi] = top;
if((h->flags & IndirectKey) != 0) {
*(byte**)xk = *(byte**)k; // copy pointer
} else {
@@ -355,15 +367,14 @@ evacuate(MapType *t, Hmap *h, uintptr oldbucket)
} else {
if(yi == BUCKETSIZE) {
if(checkgc) mstats.next_gc = mstats.heap_alloc;
- newy = runtime·mallocgc(h->bucketsize, 0, 1, 0);
- clearbucket(newy);
+ newy = runtime·cnew(t->bucket);
y->overflow = newy;
y = newy;
yi = 0;
yk = y->data;
yv = yk + h->keysize * BUCKETSIZE;
}
- y->tophash[yi] = b->tophash[i];
+ y->tophash[yi] = top;
if((h->flags & IndirectKey) != 0) {
*(byte**)yk = *(byte**)k;
} else {
@@ -388,35 +399,18 @@ evacuate(MapType *t, Hmap *h, uintptr oldbucket)
b = nextb;
} while(b != nil);
- // Free old overflow buckets as much as we can.
- if((h->flags & OldIterator) == 0) {
- b = (Bucket*)(h->oldbuckets + oldbucket * h->bucketsize);
- if((h->flags & CanFreeBucket) != 0) {
- while((nextb = overflowptr(b)) != nil) {
- b->overflow = nextb->overflow;
- runtime·free(nextb);
- }
- } else {
- // can't explicitly free overflow buckets, but at least
- // we can unlink them.
- b->overflow = (Bucket*)1;
- }
- }
+ // Unlink the overflow buckets to help GC.
+ b = (Bucket*)(h->oldbuckets + oldbucket * h->bucketsize);
+ if((h->flags & OldIterator) == 0)
+ b->overflow = (Bucket*)1;
}
// advance evacuation mark
if(oldbucket == h->nevacuate) {
h->nevacuate = oldbucket + 1;
- if(oldbucket + 1 == newbit) { // newbit == # of oldbuckets
+ if(oldbucket + 1 == newbit) // newbit == # of oldbuckets
// free main bucket array
- if((h->flags & (OldIterator | CanFreeBucket)) == CanFreeBucket) {
- ob = h->oldbuckets;
- h->oldbuckets = nil;
- runtime·free(ob);
- } else {
- h->oldbuckets = nil;
- }
- }
+ h->oldbuckets = nil;
}
if(docheck)
check(t, h);
@@ -451,14 +445,10 @@ hash_grow(MapType *t, Hmap *h)
old_buckets = h->buckets;
// NOTE: this could be a big malloc, but since we don't need zeroing it is probably fast.
if(checkgc) mstats.next_gc = mstats.heap_alloc;
- new_buckets = runtime·mallocgc((uintptr)h->bucketsize << (h->B + 1), 0, 1, 0);
+ new_buckets = runtime·cnewarray(t->bucket, (uintptr)1 << (h->B + 1));
flags = (h->flags & ~(Iterator | OldIterator));
- if((h->flags & Iterator) != 0) {
+ if((h->flags & Iterator) != 0)
flags |= OldIterator;
- // We can't free indirect keys any more, as
- // they are potentially aliased across buckets.
- flags &= ~CanFreeKey;
- }
// commit the grow (atomic wrt gc)
h->B++;
@@ -524,6 +514,7 @@ hash_lookup(MapType *t, Hmap *h, byte **keyp)
}
// When an item is not found, fast versions return a pointer to this zeroed memory.
+#pragma dataflag RODATA
static uint8 empty_value[MAXVALUESIZE];
// Specialized versions of mapaccess1 for specific types.
@@ -532,48 +523,65 @@ static uint8 empty_value[MAXVALUESIZE];
#define HASH_LOOKUP2 runtime·mapaccess2_fast32
#define KEYTYPE uint32
#define HASHFUNC runtime·algarray[AMEM32].hash
-#define EQFUNC(x,y) ((x) == (y))
-#define EQMAYBE(x,y) ((x) == (y))
-#define HASMAYBE false
-#define QUICKEQ(x) true
+#define FASTKEY(x) true
+#define QUICK_NE(x,y) ((x) != (y))
+#define QUICK_EQ(x,y) true
+#define SLOW_EQ(x,y) true
+#define MAYBE_EQ(x,y) true
#include "hashmap_fast.c"
#undef HASH_LOOKUP1
#undef HASH_LOOKUP2
#undef KEYTYPE
#undef HASHFUNC
-#undef EQFUNC
-#undef EQMAYBE
-#undef HASMAYBE
-#undef QUICKEQ
+#undef FASTKEY
+#undef QUICK_NE
+#undef QUICK_EQ
+#undef SLOW_EQ
+#undef MAYBE_EQ
#define HASH_LOOKUP1 runtime·mapaccess1_fast64
#define HASH_LOOKUP2 runtime·mapaccess2_fast64
#define KEYTYPE uint64
#define HASHFUNC runtime·algarray[AMEM64].hash
-#define EQFUNC(x,y) ((x) == (y))
-#define EQMAYBE(x,y) ((x) == (y))
-#define HASMAYBE false
-#define QUICKEQ(x) true
+#define FASTKEY(x) true
+#define QUICK_NE(x,y) ((x) != (y))
+#define QUICK_EQ(x,y) true
+#define SLOW_EQ(x,y) true
+#define MAYBE_EQ(x,y) true
#include "hashmap_fast.c"
#undef HASH_LOOKUP1
#undef HASH_LOOKUP2
#undef KEYTYPE
#undef HASHFUNC
-#undef EQFUNC
-#undef EQMAYBE
-#undef HASMAYBE
-#undef QUICKEQ
+#undef FASTKEY
+#undef QUICK_NE
+#undef QUICK_EQ
+#undef SLOW_EQ
+#undef MAYBE_EQ
+
+#ifdef GOARCH_amd64
+#define CHECKTYPE uint64
+#endif
+#ifdef GOARCH_386
+#define CHECKTYPE uint32
+#endif
+#ifdef GOARCH_arm
+// can't use uint32 on arm because our loads aren't aligned.
+// TODO: use uint32 for arm v6+?
+#define CHECKTYPE uint8
+#endif
#define HASH_LOOKUP1 runtime·mapaccess1_faststr
#define HASH_LOOKUP2 runtime·mapaccess2_faststr
#define KEYTYPE String
#define HASHFUNC runtime·algarray[ASTRING].hash
-#define EQFUNC(x,y) ((x).len == (y).len && ((x).str == (y).str || runtime·memeq((x).str, (y).str, (x).len)))
-#define EQMAYBE(x,y) ((x).len == (y).len)
-#define HASMAYBE true
-#define QUICKEQ(x) ((x).len < 32)
+#define FASTKEY(x) ((x).len < 32)
+#define QUICK_NE(x,y) ((x).len != (y).len)
+#define QUICK_EQ(x,y) ((x).str == (y).str)
+#define SLOW_EQ(x,y) runtime·memeq((x).str, (y).str, (x).len)
+#define MAYBE_EQ(x,y) (*(CHECKTYPE*)(x).str == *(CHECKTYPE*)(y).str && *(CHECKTYPE*)((x).str + (x).len - sizeof(CHECKTYPE)) == *(CHECKTYPE*)((y).str + (x).len - sizeof(CHECKTYPE)))
#include "hashmap_fast.c"
static void
@@ -595,11 +603,8 @@ hash_insert(MapType *t, Hmap *h, void *key, void *value)
check(t, h);
hash = h->hash0;
t->key->alg->hash(&hash, t->key->size, key);
- if(h->buckets == nil) {
- h->buckets = runtime·mallocgc(h->bucketsize, 0, 1, 0);
- b = (Bucket*)(h->buckets);
- clearbucket(b);
- }
+ if(h->buckets == nil)
+ h->buckets = runtime·cnewarray(t->bucket, 1);
again:
bucket = hash & (((uintptr)1 << h->B) - 1);
@@ -609,7 +614,7 @@ hash_insert(MapType *t, Hmap *h, void *key, void *value)
top = hash >> (sizeof(uintptr)*8 - 8);
if(top == 0)
top = 1;
- inserti = 0;
+ inserti = nil;
insertk = nil;
insertv = nil;
while(true) {
@@ -646,8 +651,7 @@ hash_insert(MapType *t, Hmap *h, void *key, void *value)
if(inserti == nil) {
// all current buckets are full, allocate a new one.
if(checkgc) mstats.next_gc = mstats.heap_alloc;
- newb = runtime·mallocgc(h->bucketsize, 0, 1, 0);
- clearbucket(newb);
+ newb = runtime·cnew(t->bucket);
b->overflow = newb;
inserti = newb->tophash;
insertk = newb->data;
@@ -657,13 +661,13 @@ hash_insert(MapType *t, Hmap *h, void *key, void *value)
// store new key/value at insert position
if((h->flags & IndirectKey) != 0) {
if(checkgc) mstats.next_gc = mstats.heap_alloc;
- kmem = runtime·mallocgc(t->key->size, 0, 1, 0);
+ kmem = runtime·cnew(t->key);
*(byte**)insertk = kmem;
insertk = kmem;
}
if((h->flags & IndirectValue) != 0) {
if(checkgc) mstats.next_gc = mstats.heap_alloc;
- vmem = runtime·mallocgc(t->elem->size, 0, 1, 0);
+ vmem = runtime·cnew(t->elem);
*(byte**)insertv = vmem;
insertv = vmem;
}
@@ -707,22 +711,20 @@ hash_remove(MapType *t, Hmap *h, void *key)
if(!eq)
continue;
- if((h->flags & CanFreeKey) != 0) {
- k = *(byte**)k;
+ if((h->flags & IndirectKey) != 0) {
+ *(byte**)k = nil;
+ } else {
+ t->key->alg->copy(t->key->size, k, nil);
}
if((h->flags & IndirectValue) != 0) {
- v = *(byte**)v;
+ *(byte**)v = nil;
+ } else {
+ t->elem->alg->copy(t->elem->size, v, nil);
}
b->tophash[i] = 0;
h->count--;
- if((h->flags & CanFreeKey) != 0) {
- runtime·free(k);
- }
- if((h->flags & IndirectValue) != 0) {
- runtime·free(v);
- }
// TODO: consolidate buckets if they are mostly empty
// can only consolidate if there are no live iterators at this size.
if(docheck)
@@ -785,7 +787,7 @@ hash_iter_init(MapType *t, Hmap *h, struct hash_iter *it)
it->bptr = nil;
// Remember we have an iterator.
- // Can run concurrently with another hash_iter_init() and with reflect·mapiterinit().
+ // Can run concurrently with another hash_iter_init().
for(;;) {
old = h->flags;
if((old&(Iterator|OldIterator)) == (Iterator|OldIterator))
@@ -863,18 +865,12 @@ next:
if(check_bucket >= 0) {
// Special case: iterator was started during a grow and the
// grow is not done yet. We're working on a bucket whose
- // oldbucket has not been evacuated yet. So we iterate
+ // oldbucket has not been evacuated yet. So we're iterating
// through the oldbucket, skipping any keys that will go
// to the other new bucket (each oldbucket expands to two
// buckets during a grow).
t->key->alg->equal(&eq, t->key->size, IK(h, k), IK(h, k));
- if(!eq) {
- // Hash is meaningless if k != k (NaNs). Return all
- // NaNs during the first of the two new buckets.
- if(bucket >= ((uintptr)1 << (it->B - 1))) {
- continue;
- }
- } else {
+ if(eq) {
// If the item in the oldbucket is not destined for
// the current new bucket in the iteration, skip it.
hash = h->hash0;
@@ -882,6 +878,14 @@ next:
if((hash & (((uintptr)1 << it->B) - 1)) != check_bucket) {
continue;
}
+ } else {
+ // Hash isn't repeatable if k != k (NaNs). We need a
+ // repeatable and randomish choice of which direction
+ // to send NaNs during evacuation. We'll use the low
+ // bit of tophash to decide which way NaNs go.
+ if(check_bucket >> (it->B - 1) != (b->tophash[i] & 1)) {
+ continue;
+ }
}
}
if(!evacuated(b)) {
@@ -925,157 +929,6 @@ next:
goto next;
}
-
-#define PHASE_BUCKETS 0
-#define PHASE_OLD_BUCKETS 1
-#define PHASE_TABLE 2
-#define PHASE_OLD_TABLE 3
-#define PHASE_DONE 4
-
-// Initialize the iterator.
-// Returns false if Hmap contains no pointers (in which case the iterator is not initialized).
-bool
-hash_gciter_init (Hmap *h, struct hash_gciter *it)
-{
- // GC during map initialization or on an empty map.
- if(h->buckets == nil)
- return false;
-
- it->h = h;
- it->phase = PHASE_BUCKETS;
- it->bucket = 0;
- it->b = nil;
-
- // TODO: finish evacuating oldbuckets so that we can collect
- // oldbuckets? We don't want to keep a partially evacuated
- // table around forever, so each gc could make at least some
- // evacuation progress. Need to be careful about concurrent
- // access if we do concurrent gc. Even if not, we don't want
- // to make the gc pause any longer than it has to be.
-
- return true;
-}
-
-// Returns true and fills *data with internal structure/key/value data,
-// or returns false if the iterator has terminated.
-// Ugh, this interface is really annoying. I want a callback fn!
-bool
-hash_gciter_next(struct hash_gciter *it, struct hash_gciter_data *data)
-{
- Hmap *h;
- uintptr bucket, oldbucket;
- Bucket *b, *oldb;
- uintptr i;
- byte *k, *v;
-
- h = it->h;
- bucket = it->bucket;
- b = it->b;
- i = it->i;
-
- data->st = nil;
- data->key_data = nil;
- data->val_data = nil;
- data->indirectkey = (h->flags & IndirectKey) != 0;
- data->indirectval = (h->flags & IndirectValue) != 0;
-
-next:
- switch (it->phase) {
- case PHASE_BUCKETS:
- if(b != nil) {
- k = b->data + h->keysize * i;
- v = b->data + h->keysize * BUCKETSIZE + h->valuesize * i;
- for(; i < BUCKETSIZE; i++, k += h->keysize, v += h->valuesize) {
- if(b->tophash[i] != 0) {
- data->key_data = k;
- data->val_data = v;
- it->bucket = bucket;
- it->b = b;
- it->i = i + 1;
- return true;
- }
- }
- b = b->overflow;
- if(b != nil) {
- data->st = (byte*)b;
- it->bucket = bucket;
- it->b = b;
- it->i = 0;
- return true;
- }
- }
- while(bucket < ((uintptr)1 << h->B)) {
- if(h->oldbuckets != nil) {
- oldbucket = bucket & (((uintptr)1 << (h->B - 1)) - 1);
- oldb = (Bucket*)(h->oldbuckets + oldbucket * h->bucketsize);
- if(!evacuated(oldb)) {
- // new bucket isn't valid yet
- bucket++;
- continue;
- }
- }
- b = (Bucket*)(h->buckets + bucket * h->bucketsize);
- i = 0;
- bucket++;
- goto next;
- }
- it->phase = PHASE_OLD_BUCKETS;
- bucket = 0;
- b = nil;
- goto next;
- case PHASE_OLD_BUCKETS:
- if(h->oldbuckets == nil) {
- it->phase = PHASE_TABLE;
- goto next;
- }
- if(b != nil) {
- k = b->data + h->keysize * i;
- v = b->data + h->keysize * BUCKETSIZE + h->valuesize * i;
- for(; i < BUCKETSIZE; i++, k += h->keysize, v += h->valuesize) {
- if(b->tophash[i] != 0) {
- data->key_data = k;
- data->val_data = v;
- it->bucket = bucket;
- it->b = b;
- it->i = i + 1;
- return true;
- }
- }
- b = overflowptr(b);
- if(b != nil) {
- data->st = (byte*)b;
- it->bucket = bucket;
- it->b = b;
- it->i = 0;
- return true;
- }
- }
- if(bucket < ((uintptr)1 << (h->B - 1))) {
- b = (Bucket*)(h->oldbuckets + bucket * h->bucketsize);
- bucket++;
- i = 0;
- goto next;
- }
- it->phase = PHASE_TABLE;
- goto next;
- case PHASE_TABLE:
- it->phase = PHASE_OLD_TABLE;
- data->st = h->buckets;
- return true;
- case PHASE_OLD_TABLE:
- it->phase = PHASE_DONE;
- if(h->oldbuckets != nil) {
- data->st = h->oldbuckets;
- return true;
- } else {
- goto next;
- }
- }
- if(it->phase != PHASE_DONE)
- runtime·throw("bad phase at done");
- return false;
-}
-
//
/// interfaces to go runtime
//
@@ -1094,6 +947,9 @@ runtime·makemap_c(MapType *typ, int64 hint)
Type *key;
key = typ->key;
+
+ if(sizeof(Hmap) > 48)
+ runtime·panicstring("hmap too large");
if(hint < 0 || (int32)hint != hint)
runtime·panicstring("makemap: size out of range");
@@ -1101,15 +957,7 @@ runtime·makemap_c(MapType *typ, int64 hint)
if(key->alg->hash == runtime·nohash)
runtime·throw("runtime.makemap: unsupported map key type");
- h = runtime·mal(sizeof(*h));
-
- if(UseSpanType) {
- if(false) {
- runtime·printf("makemap %S: %p\n", *typ->string, h);
- }
- runtime·settype(h, (uintptr)typ | TypeInfo_Map);
- }
-
+ h = runtime·cnew(typ->hmap);
hash_init(typ, h, hint);
// these calculations are compiler dependent.
@@ -1153,9 +1001,6 @@ runtime·mapaccess(MapType *t, Hmap *h, byte *ak, byte *av, bool *pres)
return;
}
- if(runtime·gcwaiting)
- runtime·gosched();
-
res = hash_lookup(t, h, &ak);
if(res != nil) {
@@ -1168,7 +1013,7 @@ runtime·mapaccess(MapType *t, Hmap *h, byte *ak, byte *av, bool *pres)
}
// mapaccess1(hmap *map[any]any, key any) (val any);
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·mapaccess1(MapType *t, Hmap *h, ...)
{
@@ -1200,7 +1045,7 @@ runtime·mapaccess1(MapType *t, Hmap *h, ...)
}
// mapaccess2(hmap *map[any]any, key any) (val any, pres bool);
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·mapaccess2(MapType *t, Hmap *h, ...)
{
@@ -1263,9 +1108,6 @@ runtime·mapassign(MapType *t, Hmap *h, byte *ak, byte *av)
if(h == nil)
runtime·panicstring("assignment to entry in nil map");
- if(runtime·gcwaiting)
- runtime·gosched();
-
if(av == nil) {
hash_remove(t, h, ak);
} else {
@@ -1278,13 +1120,16 @@ runtime·mapassign(MapType *t, Hmap *h, byte *ak, byte *av)
runtime·prints("; key=");
t->key->alg->print(t->key->size, ak);
runtime·prints("; val=");
- t->elem->alg->print(t->elem->size, av);
+ if(av)
+ t->elem->alg->print(t->elem->size, av);
+ else
+ runtime·prints("nil");
runtime·prints("\n");
}
}
// mapassign1(mapType *type, hmap *map[any]any, key any, val any);
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·mapassign1(MapType *t, Hmap *h, ...)
{
@@ -1302,7 +1147,7 @@ runtime·mapassign1(MapType *t, Hmap *h, ...)
}
// mapdelete(mapType *type, hmap *map[any]any, key any)
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·mapdelete(MapType *t, Hmap *h, ...)
{
@@ -1355,7 +1200,7 @@ reflect·mapassign(MapType *t, Hmap *h, uintptr key, uintptr val, bool pres)
void
runtime·mapiterinit(MapType *t, Hmap *h, struct hash_iter *it)
{
- if(h == nil) {
+ if(h == nil || h->count == 0) {
it->key = nil;
return;
}
@@ -1379,26 +1224,6 @@ runtime·mapiterinit(MapType *t, Hmap *h, struct hash_iter *it)
void
reflect·mapiterinit(MapType *t, Hmap *h, struct hash_iter *it)
{
- uint32 old, new;
-
- if(h != nil && t->key->size > sizeof(void*)) {
- // reflect·mapiterkey returns pointers to key data,
- // and reflect holds them, so we cannot free key data
- // eagerly anymore.
- // Can run concurrently with another reflect·mapiterinit() and with hash_iter_init().
- for(;;) {
- old = h->flags;
- if(old & IndirectKey)
- new = old & ~CanFreeKey;
- else
- new = old & ~CanFreeBucket;
- if(new == old)
- break;
- if(runtime·cas(&h->flags, old, new))
- break;
- }
- }
-
it = runtime·mal(sizeof *it);
FLUSH(&it);
runtime·mapiterinit(t, h, it);
@@ -1410,8 +1235,6 @@ runtime·mapiternext(struct hash_iter *it)
{
if(raceenabled)
runtime·racereadpc(it->h, runtime·getcallerpc(&it), runtime·mapiternext);
- if(runtime·gcwaiting)
- runtime·gosched();
hash_next(it);
if(debug) {
@@ -1432,7 +1255,7 @@ reflect·mapiternext(struct hash_iter *it)
}
// mapiter1(hiter *any) (key any);
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·mapiter1(struct hash_iter *it, ...)
{
@@ -1484,12 +1307,8 @@ reflect·mapiterkey(struct hash_iter *it, uintptr key, bool ok)
key = 0;
ok = false;
res = it->key;
- if(res == nil) {
- key = 0;
- ok = false;
- } else {
+ if(res != nil) {
tkey = it->t->key;
- key = 0;
if(tkey->size <= sizeof(key))
tkey->alg->copy(tkey->size, (byte*)&key, res);
else
@@ -1517,7 +1336,7 @@ reflect·maplen(Hmap *h, intgo len)
}
// mapiter2(hiter *any) (key any, val any);
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·mapiter2(struct hash_iter *it, ...)
{
@@ -1543,3 +1362,6 @@ runtime·mapiter2(struct hash_iter *it, ...)
runtime·prints("\n");
}
}
+
+// exported value for testing
+float64 runtime·hashLoad = LOAD;
diff --git a/src/pkg/runtime/hashmap.h b/src/pkg/runtime/hashmap.h
deleted file mode 100644
index 2988417f6..000000000
--- a/src/pkg/runtime/hashmap.h
+++ /dev/null
@@ -1,33 +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.
-
-struct Hmap; /* opaque */
-
-/* Used by the garbage collector */
-struct hash_gciter
-{
- Hmap *h;
- int32 phase;
- uintptr bucket;
- struct Bucket *b;
- uintptr i;
-};
-
-// this data is used by the garbage collector to keep the map's
-// internal structures from being reclaimed. The iterator must
-// return in st every live object (ones returned by mallocgc) so
-// that those objects won't be collected, and it must return
-// every key & value in key_data/val_data so they can get scanned
-// for pointers they point to. Note that if you malloc storage
-// for keys and values, you need to do both.
-struct hash_gciter_data
-{
- uint8 *st; /* internal structure, or nil */
- uint8 *key_data; /* key data, or nil */
- uint8 *val_data; /* value data, or nil */
- bool indirectkey; /* storing pointers to keys */
- bool indirectval; /* storing pointers to values */
-};
-bool hash_gciter_init (struct Hmap *h, struct hash_gciter *it);
-bool hash_gciter_next (struct hash_gciter *it, struct hash_gciter_data *data);
diff --git a/src/pkg/runtime/hashmap_fast.c b/src/pkg/runtime/hashmap_fast.c
index afff7b1aa..796582e2d 100644
--- a/src/pkg/runtime/hashmap_fast.c
+++ b/src/pkg/runtime/hashmap_fast.c
@@ -12,19 +12,16 @@
// +build ignore
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
HASH_LOOKUP1(MapType *t, Hmap *h, KEYTYPE key, byte *value)
{
- uintptr hash;
- uintptr bucket, oldbucket;
+ uintptr bucket, i;
Bucket *b;
- uintptr i;
KEYTYPE *k;
byte *v;
uint8 top;
int8 keymaybe;
- bool quickkey;
if(debug) {
runtime·prints("runtime.mapaccess1_fastXXX: map=");
@@ -45,59 +42,76 @@ HASH_LOOKUP1(MapType *t, Hmap *h, KEYTYPE key, byte *value)
if(h->B == 0) {
// One-bucket table. Don't hash, just check each bucket entry.
- if(HASMAYBE) {
- keymaybe = -1;
- }
- quickkey = QUICKEQ(key);
b = (Bucket*)h->buckets;
- for(i = 0, k = (KEYTYPE*)b->data, v = (byte*)(k + BUCKETSIZE); i < BUCKETSIZE; i++, k++, v += h->valuesize) {
- if(b->tophash[i] != 0) {
- if(quickkey && EQFUNC(key, *k)) {
+ if(FASTKEY(key)) {
+ for(i = 0, k = (KEYTYPE*)b->data, v = (byte*)(k + BUCKETSIZE); i < BUCKETSIZE; i++, k++, v += h->valuesize) {
+ if(b->tophash[i] == 0)
+ continue;
+ if(QUICK_NE(key, *k))
+ continue;
+ if(QUICK_EQ(key, *k) || SLOW_EQ(key, *k)) {
+ value = v;
+ FLUSH(&value);
+ return;
+ }
+ }
+ } else {
+ keymaybe = -1;
+ for(i = 0, k = (KEYTYPE*)b->data, v = (byte*)(k + BUCKETSIZE); i < BUCKETSIZE; i++, k++, v += h->valuesize) {
+ if(b->tophash[i] == 0)
+ continue;
+ if(QUICK_NE(key, *k))
+ continue;
+ if(QUICK_EQ(key, *k)) {
value = v;
FLUSH(&value);
return;
}
- if(HASMAYBE && EQMAYBE(key, *k)) {
- // TODO: check if key.str matches. Add EQFUNCFAST?
+ if(MAYBE_EQ(key, *k)) {
if(keymaybe >= 0) {
// Two same-length strings in this bucket.
// use slow path.
- // TODO: keep track of more than just 1. Especially
- // if doing the TODO above.
+ // TODO: keep track of more than just 1. We could
+ // afford about 3 equals calls before it would be more
+ // expensive than 1 hash + 1 equals.
goto dohash;
}
keymaybe = i;
}
}
- }
- if(HASMAYBE && keymaybe >= 0) {
- k = (KEYTYPE*)b->data + keymaybe;
- if(EQFUNC(key, *k)) {
- value = (byte*)((KEYTYPE*)b->data + BUCKETSIZE) + keymaybe * h->valuesize;
- FLUSH(&value);
- return;
+ if(keymaybe >= 0) {
+ k = (KEYTYPE*)b->data + keymaybe;
+ if(SLOW_EQ(key, *k)) {
+ value = (byte*)((KEYTYPE*)b->data + BUCKETSIZE) + keymaybe * h->valuesize;
+ FLUSH(&value);
+ return;
+ }
}
}
} else {
dohash:
- hash = h->hash0;
- HASHFUNC(&hash, sizeof(KEYTYPE), &key);
- bucket = hash & (((uintptr)1 << h->B) - 1);
+ bucket = h->hash0;
+ HASHFUNC(&bucket, sizeof(KEYTYPE), &key);
+ top = bucket >> (sizeof(uintptr)*8 - 8);
+ if(top == 0)
+ top = 1;
+ bucket &= (((uintptr)1 << h->B) - 1);
if(h->oldbuckets != nil) {
- oldbucket = bucket & (((uintptr)1 << (h->B - 1)) - 1);
- b = (Bucket*)(h->oldbuckets + oldbucket * h->bucketsize);
+ i = bucket & (((uintptr)1 << (h->B - 1)) - 1);
+ b = (Bucket*)(h->oldbuckets + i * h->bucketsize);
if(evacuated(b)) {
b = (Bucket*)(h->buckets + bucket * h->bucketsize);
}
} else {
b = (Bucket*)(h->buckets + bucket * h->bucketsize);
}
- top = hash >> (sizeof(uintptr)*8 - 8);
- if(top == 0)
- top = 1;
do {
for(i = 0, k = (KEYTYPE*)b->data, v = (byte*)(k + BUCKETSIZE); i < BUCKETSIZE; i++, k++, v += h->valuesize) {
- if(b->tophash[i] == top && EQFUNC(key, *k)) {
+ if(b->tophash[i] != top)
+ continue;
+ if(QUICK_NE(key, *k))
+ continue;
+ if(QUICK_EQ(key, *k) || SLOW_EQ(key, *k)) {
value = v;
FLUSH(&value);
return;
@@ -110,19 +124,16 @@ dohash:
FLUSH(&value);
}
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
HASH_LOOKUP2(MapType *t, Hmap *h, KEYTYPE key, byte *value, bool res)
{
- uintptr hash;
- uintptr bucket, oldbucket;
+ uintptr bucket, i;
Bucket *b;
- uintptr i;
KEYTYPE *k;
byte *v;
uint8 top;
int8 keymaybe;
- bool quickkey;
if(debug) {
runtime·prints("runtime.mapaccess2_fastXXX: map=");
@@ -144,64 +155,83 @@ HASH_LOOKUP2(MapType *t, Hmap *h, KEYTYPE key, byte *value, bool res)
check(t, h);
if(h->B == 0) {
- // One-bucket table. Don't hash, just check each bucket entry.
- if(HASMAYBE) {
- keymaybe = -1;
- }
- quickkey = QUICKEQ(key);
+ // One-bucket table. Don't hash, just check each bucket entry.
b = (Bucket*)h->buckets;
- for(i = 0, k = (KEYTYPE*)b->data, v = (byte*)(k + BUCKETSIZE); i < BUCKETSIZE; i++, k++, v += h->valuesize) {
- if(b->tophash[i] != 0) {
- if(quickkey && EQFUNC(key, *k)) {
+ if(FASTKEY(key)) {
+ for(i = 0, k = (KEYTYPE*)b->data, v = (byte*)(k + BUCKETSIZE); i < BUCKETSIZE; i++, k++, v += h->valuesize) {
+ if(b->tophash[i] == 0)
+ continue;
+ if(QUICK_NE(key, *k))
+ continue;
+ if(QUICK_EQ(key, *k) || SLOW_EQ(key, *k)) {
+ value = v;
+ res = true;
+ FLUSH(&value);
+ FLUSH(&res);
+ return;
+ }
+ }
+ } else {
+ keymaybe = -1;
+ for(i = 0, k = (KEYTYPE*)b->data, v = (byte*)(k + BUCKETSIZE); i < BUCKETSIZE; i++, k++, v += h->valuesize) {
+ if(b->tophash[i] == 0)
+ continue;
+ if(QUICK_NE(key, *k))
+ continue;
+ if(QUICK_EQ(key, *k)) {
value = v;
res = true;
FLUSH(&value);
FLUSH(&res);
return;
}
- if(HASMAYBE && EQMAYBE(key, *k)) {
- // TODO: check if key.str matches. Add EQFUNCFAST?
+ if(MAYBE_EQ(key, *k)) {
if(keymaybe >= 0) {
// Two same-length strings in this bucket.
// use slow path.
- // TODO: keep track of more than just 1. Especially
- // if doing the TODO above.
+ // TODO: keep track of more than just 1. We could
+ // afford about 3 equals calls before it would be more
+ // expensive than 1 hash + 1 equals.
goto dohash;
}
keymaybe = i;
}
}
- }
- if(HASMAYBE && keymaybe >= 0) {
- k = (KEYTYPE*)b->data + keymaybe;
- if(EQFUNC(key, *k)) {
- value = (byte*)((KEYTYPE*)b->data + BUCKETSIZE) + keymaybe * h->valuesize;
- res = true;
- FLUSH(&value);
- FLUSH(&res);
- return;
+ if(keymaybe >= 0) {
+ k = (KEYTYPE*)b->data + keymaybe;
+ if(SLOW_EQ(key, *k)) {
+ value = (byte*)((KEYTYPE*)b->data + BUCKETSIZE) + keymaybe * h->valuesize;
+ res = true;
+ FLUSH(&value);
+ FLUSH(&res);
+ return;
+ }
}
}
} else {
dohash:
- hash = h->hash0;
- HASHFUNC(&hash, sizeof(KEYTYPE), &key);
- bucket = hash & (((uintptr)1 << h->B) - 1);
+ bucket = h->hash0;
+ HASHFUNC(&bucket, sizeof(KEYTYPE), &key);
+ top = bucket >> (sizeof(uintptr)*8 - 8);
+ if(top == 0)
+ top = 1;
+ bucket &= (((uintptr)1 << h->B) - 1);
if(h->oldbuckets != nil) {
- oldbucket = bucket & (((uintptr)1 << (h->B - 1)) - 1);
- b = (Bucket*)(h->oldbuckets + oldbucket * h->bucketsize);
+ i = bucket & (((uintptr)1 << (h->B - 1)) - 1);
+ b = (Bucket*)(h->oldbuckets + i * h->bucketsize);
if(evacuated(b)) {
b = (Bucket*)(h->buckets + bucket * h->bucketsize);
}
} else {
b = (Bucket*)(h->buckets + bucket * h->bucketsize);
}
- top = hash >> (sizeof(uintptr)*8 - 8);
- if(top == 0)
- top = 1;
do {
for(i = 0, k = (KEYTYPE*)b->data, v = (byte*)(k + BUCKETSIZE); i < BUCKETSIZE; i++, k++, v += h->valuesize) {
- if(b->tophash[i] == top && EQFUNC(key, *k)) {
+ if(b->tophash[i] != top)
+ continue;
+ if(QUICK_NE(key, *k))
+ continue;
+ if(QUICK_EQ(key, *k) || SLOW_EQ(key, *k)) {
value = v;
res = true;
FLUSH(&value);
diff --git a/src/pkg/runtime/iface.c b/src/pkg/runtime/iface.c
index 5973d6d03..ecbdcc707 100644
--- a/src/pkg/runtime/iface.c
+++ b/src/pkg/runtime/iface.c
@@ -7,6 +7,7 @@
#include "type.h"
#include "typekind.h"
#include "malloc.h"
+#include "../../cmd/ld/textflag.h"
void
runtime·printiface(Iface i)
@@ -85,7 +86,7 @@ itab(InterfaceType *inter, Type *type, int32 canfail)
}
ni = inter->mhdr.len;
- m = runtime·malloc(sizeof(*m) + ni*sizeof m->fun[0]);
+ m = runtime·persistentalloc(sizeof(*m) + ni*sizeof m->fun[0], 0, &mstats.other_sys);
m->inter = inter;
m->type = type;
@@ -170,7 +171,7 @@ copyout(Type *t, void **src, void *dst)
alg->copy(size, dst, *src);
}
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·typ2Itab(Type *t, InterfaceType *inter, Itab **cache, Itab *ret)
{
@@ -183,7 +184,7 @@ runtime·typ2Itab(Type *t, InterfaceType *inter, Itab **cache, Itab *ret)
}
// func convT2I(typ *byte, typ2 *byte, cache **byte, elem any) (ret any)
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·convT2I(Type *t, InterfaceType *inter, Itab **cache, ...)
{
@@ -205,7 +206,7 @@ runtime·convT2I(Type *t, InterfaceType *inter, Itab **cache, ...)
}
// func convT2E(typ *byte, elem any) (ret any)
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·convT2E(Type *t, ...)
{
@@ -223,7 +224,7 @@ runtime·convT2E(Type *t, ...)
static void assertI2Tret(Type *t, Iface i, byte *ret);
// func ifaceI2T(typ *byte, iface any) (ret any)
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·assertI2T(Type *t, Iface i, ...)
{
@@ -256,7 +257,7 @@ assertI2Tret(Type *t, Iface i, byte *ret)
}
// func ifaceI2T2(typ *byte, iface any) (ret any, ok bool)
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·assertI2T2(Type *t, Iface i, ...)
{
@@ -288,7 +289,7 @@ runtime·assertI2TOK(Type *t, Iface i, bool ok)
static void assertE2Tret(Type *t, Eface e, byte *ret);
// func ifaceE2T(typ *byte, iface any) (ret any)
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·assertE2T(Type *t, Eface e, ...)
{
@@ -319,7 +320,7 @@ assertE2Tret(Type *t, Eface e, byte *ret)
}
// func ifaceE2T2(sigt *byte, iface any) (ret any, ok bool);
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·assertE2T2(Type *t, Eface e, ...)
{
@@ -481,6 +482,16 @@ runtime·ifaceE2I(InterfaceType *inter, Eface e, Iface *ret)
ret->tab = itab(inter, t, 0);
}
+bool
+runtime·ifaceE2I2(InterfaceType *inter, Eface e, Iface *ret)
+{
+ ret->tab = itab(inter, e.type, 1);
+ if(ret->tab == nil)
+ return false;
+ ret->data = e.data;
+ return true;
+}
+
// For reflect
// func ifaceE2I(t *InterfaceType, e interface{}, dst *Iface)
void
diff --git a/src/pkg/runtime/lfstack.c b/src/pkg/runtime/lfstack.c
index 1d48491aa..140384d3d 100644
--- a/src/pkg/runtime/lfstack.c
+++ b/src/pkg/runtime/lfstack.c
@@ -29,10 +29,10 @@ runtime·lfstackpush(uint64 *head, LFNode *node)
node->pushcnt++;
new = (uint64)(uintptr)node|(((uint64)node->pushcnt&CNT_MASK)<<PTR_BITS);
- old = runtime·atomicload64(head);
for(;;) {
+ old = runtime·atomicload64(head);
node->next = (LFNode*)(uintptr)(old&PTR_MASK);
- if(runtime·cas64(head, &old, new))
+ if(runtime·cas64(head, old, new))
break;
}
}
@@ -43,8 +43,8 @@ runtime·lfstackpop(uint64 *head)
LFNode *node, *node2;
uint64 old, new;
- old = runtime·atomicload64(head);
for(;;) {
+ old = runtime·atomicload64(head);
if(old == 0)
return nil;
node = (LFNode*)(uintptr)(old&PTR_MASK);
@@ -52,7 +52,7 @@ runtime·lfstackpop(uint64 *head)
new = 0;
if(node2 != nil)
new = (uint64)(uintptr)node2|(((uint64)node2->pushcnt&CNT_MASK)<<PTR_BITS);
- if(runtime·cas64(head, &old, new))
+ if(runtime·cas64(head, old, new))
return node;
}
}
diff --git a/src/pkg/runtime/lock_futex.c b/src/pkg/runtime/lock_futex.c
index 3c2ef4ede..e6e9be923 100644
--- a/src/pkg/runtime/lock_futex.c
+++ b/src/pkg/runtime/lock_futex.c
@@ -2,9 +2,11 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build freebsd linux
+// +build dragonfly freebsd linux
#include "runtime.h"
+#include "stack.h"
+#include "../../cmd/ld/textflag.h"
// This implementation depends on OS-specific implementations of
//
@@ -91,14 +93,16 @@ runtime·unlock(Lock *l)
{
uint32 v;
- if(--m->locks < 0)
- runtime·throw("runtime·unlock: lock count");
-
v = runtime·xchg((uint32*)&l->key, MUTEX_UNLOCKED);
if(v == MUTEX_UNLOCKED)
runtime·throw("unlock of unlocked lock");
if(v == MUTEX_SLEEPING)
runtime·futexwakeup((uint32*)&l->key, 1);
+
+ if(--m->locks < 0)
+ runtime·throw("runtime·unlock: lock count");
+ if(m->locks == 0 && g->preempt) // restore the preemption request in case we've cleared it in newstack
+ g->stackguard0 = StackPreempt;
}
// One-time notifications.
@@ -111,37 +115,42 @@ runtime·noteclear(Note *n)
void
runtime·notewakeup(Note *n)
{
- if(runtime·xchg((uint32*)&n->key, 1))
+ uint32 old;
+
+ old = runtime·xchg((uint32*)&n->key, 1);
+ if(old != 0) {
+ runtime·printf("notewakeup - double wakeup (%d)\n", old);
runtime·throw("notewakeup - double wakeup");
+ }
runtime·futexwakeup((uint32*)&n->key, 1);
}
void
runtime·notesleep(Note *n)
{
- if(m->profilehz > 0)
- runtime·setprof(false);
+ if(g != m->g0)
+ runtime·throw("notesleep not on g0");
while(runtime·atomicload((uint32*)&n->key) == 0)
runtime·futexsleep((uint32*)&n->key, 0, -1);
- if(m->profilehz > 0)
- runtime·setprof(true);
}
-void
-runtime·notetsleep(Note *n, int64 ns)
+#pragma textflag NOSPLIT
+static bool
+notetsleep(Note *n, int64 ns, int64 deadline, int64 now)
{
- int64 deadline, now;
+ // Conceptually, deadline and now are local variables.
+ // They are passed as arguments so that the space for them
+ // does not count against our nosplit stack sequence.
if(ns < 0) {
- runtime·notesleep(n);
- return;
+ while(runtime·atomicload((uint32*)&n->key) == 0)
+ runtime·futexsleep((uint32*)&n->key, 0, -1);
+ return true;
}
if(runtime·atomicload((uint32*)&n->key) != 0)
- return;
+ return true;
- if(m->profilehz > 0)
- runtime·setprof(false);
deadline = runtime·nanotime() + ns;
for(;;) {
runtime·futexsleep((uint32*)&n->key, 0, ns);
@@ -152,6 +161,33 @@ runtime·notetsleep(Note *n, int64 ns)
break;
ns = deadline - now;
}
- if(m->profilehz > 0)
- runtime·setprof(true);
+ return runtime·atomicload((uint32*)&n->key) != 0;
+}
+
+bool
+runtime·notetsleep(Note *n, int64 ns)
+{
+ bool res;
+
+ if(g != m->g0 && !m->gcing)
+ runtime·throw("notetsleep not on g0");
+
+ res = notetsleep(n, ns, 0, 0);
+ return res;
+}
+
+// same as runtime·notetsleep, but called on user g (not g0)
+// calls only nosplit functions between entersyscallblock/exitsyscall
+bool
+runtime·notetsleepg(Note *n, int64 ns)
+{
+ bool res;
+
+ if(g == m->g0)
+ runtime·throw("notetsleepg on g0");
+
+ runtime·entersyscallblock();
+ res = notetsleep(n, ns, 0, 0);
+ runtime·exitsyscall();
+ return res;
}
diff --git a/src/pkg/runtime/lock_sema.c b/src/pkg/runtime/lock_sema.c
index ec4b15a98..3d58cc87f 100644
--- a/src/pkg/runtime/lock_sema.c
+++ b/src/pkg/runtime/lock_sema.c
@@ -5,6 +5,8 @@
// +build darwin netbsd openbsd plan9 windows
#include "runtime.h"
+#include "stack.h"
+#include "../../cmd/ld/textflag.h"
// This implementation depends on OS-specific implementations of
//
@@ -93,9 +95,6 @@ runtime·unlock(Lock *l)
uintptr v;
M *mp;
- if(--m->locks < 0)
- runtime·throw("runtime·unlock: lock count");
-
for(;;) {
v = (uintptr)runtime·atomicloadp((void**)&l->key);
if(v == LOCKED) {
@@ -112,6 +111,11 @@ runtime·unlock(Lock *l)
}
}
}
+
+ if(--m->locks < 0)
+ runtime·throw("runtime·unlock: lock count");
+ if(m->locks == 0 && g->preempt) // restore the preemption request in case we've cleared it in newstack
+ g->stackguard0 = StackPreempt;
}
// One-time notifications.
@@ -146,6 +150,9 @@ runtime·notewakeup(Note *n)
void
runtime·notesleep(Note *n)
{
+ if(g != m->g0)
+ runtime·throw("notesleep not on g0");
+
if(m->waitsema == 0)
m->waitsema = runtime·semacreate();
if(!runtime·casp((void**)&n->key, nil, m)) { // must be LOCKED (got wakeup)
@@ -154,59 +161,46 @@ runtime·notesleep(Note *n)
return;
}
// Queued. Sleep.
- if(m->profilehz > 0)
- runtime·setprof(false);
runtime·semasleep(-1);
- if(m->profilehz > 0)
- runtime·setprof(true);
}
-void
-runtime·notetsleep(Note *n, int64 ns)
+#pragma textflag NOSPLIT
+static bool
+notetsleep(Note *n, int64 ns, int64 deadline, M *mp)
{
- M *mp;
- int64 deadline, now;
-
- if(ns < 0) {
- runtime·notesleep(n);
- return;
- }
-
- if(m->waitsema == 0)
- m->waitsema = runtime·semacreate();
+ // Conceptually, deadline and mp are local variables.
+ // They are passed as arguments so that the space for them
+ // does not count against our nosplit stack sequence.
// Register for wakeup on n->waitm.
if(!runtime·casp((void**)&n->key, nil, m)) { // must be LOCKED (got wakeup already)
if(n->key != LOCKED)
runtime·throw("notetsleep - waitm out of sync");
- return;
+ return true;
+ }
+
+ if(ns < 0) {
+ // Queued. Sleep.
+ runtime·semasleep(-1);
+ return true;
}
- if(m->profilehz > 0)
- runtime·setprof(false);
deadline = runtime·nanotime() + ns;
for(;;) {
// Registered. Sleep.
if(runtime·semasleep(ns) >= 0) {
// Acquired semaphore, semawakeup unregistered us.
// Done.
- if(m->profilehz > 0)
- runtime·setprof(true);
- return;
+ return true;
}
// Interrupted or timed out. Still registered. Semaphore not acquired.
- now = runtime·nanotime();
- if(now >= deadline)
+ ns = deadline - runtime·nanotime();
+ if(ns <= 0)
break;
-
// Deadline hasn't arrived. Keep sleeping.
- ns = deadline - now;
}
- if(m->profilehz > 0)
- runtime·setprof(true);
-
// Deadline arrived. Still registered. Semaphore not acquired.
// Want to give up and return, but have to unregister first,
// so that any notewakeup racing with the return does not
@@ -216,15 +210,48 @@ runtime·notetsleep(Note *n, int64 ns)
if(mp == m) {
// No wakeup yet; unregister if possible.
if(runtime·casp((void**)&n->key, mp, nil))
- return;
+ return false;
} else if(mp == (M*)LOCKED) {
// Wakeup happened so semaphore is available.
// Grab it to avoid getting out of sync.
if(runtime·semasleep(-1) < 0)
runtime·throw("runtime: unable to acquire - semaphore out of sync");
- return;
- } else {
+ return true;
+ } else
runtime·throw("runtime: unexpected waitm - semaphore out of sync");
- }
}
}
+
+bool
+runtime·notetsleep(Note *n, int64 ns)
+{
+ bool res;
+
+ if(g != m->g0 && !m->gcing)
+ runtime·throw("notetsleep not on g0");
+
+ if(m->waitsema == 0)
+ m->waitsema = runtime·semacreate();
+
+ res = notetsleep(n, ns, 0, nil);
+ return res;
+}
+
+// same as runtime·notetsleep, but called on user g (not g0)
+// calls only nosplit functions between entersyscallblock/exitsyscall
+bool
+runtime·notetsleepg(Note *n, int64 ns)
+{
+ bool res;
+
+ if(g == m->g0)
+ runtime·throw("notetsleepg on g0");
+
+ if(m->waitsema == 0)
+ m->waitsema = runtime·semacreate();
+
+ runtime·entersyscallblock();
+ res = notetsleep(n, ns, 0, nil);
+ runtime·exitsyscall();
+ return res;
+}
diff --git a/src/pkg/runtime/malloc.goc b/src/pkg/runtime/malloc.goc
index ef755f25c..c3ede4abd 100644
--- a/src/pkg/runtime/malloc.goc
+++ b/src/pkg/runtime/malloc.goc
@@ -13,8 +13,12 @@ package runtime
#include "type.h"
#include "typekind.h"
#include "race.h"
+#include "stack.h"
+#include "../../cmd/ld/textflag.h"
-MHeap *runtime·mheap;
+// Mark mheap as 'no pointers', it does not contain interesting pointers but occupies ~45K.
+#pragma dataflag NOPTR
+MHeap runtime·mheap;
int32 runtime·checking;
@@ -25,39 +29,56 @@ extern volatile intgo runtime·MemProfileRate;
// Allocate an object of at least size bytes.
// Small objects are allocated from the per-thread cache's free lists.
// Large objects (> 32 kB) are allocated straight from the heap.
+// If the block will be freed with runtime·free(), typ must be 0.
void*
-runtime·mallocgc(uintptr size, uint32 flag, int32 dogc, int32 zeroed)
+runtime·mallocgc(uintptr size, uintptr typ, uint32 flag)
{
int32 sizeclass;
intgo rate;
MCache *c;
+ MCacheList *l;
uintptr npages;
MSpan *s;
- void *v;
+ MLink *v;
- if(runtime·gcwaiting && g != m->g0 && m->locks == 0 && dogc)
- runtime·gosched();
+ if(size == 0) {
+ // All 0-length allocations use this pointer.
+ // The language does not require the allocations to
+ // have distinct values.
+ return &runtime·zerobase;
+ }
if(m->mallocing)
runtime·throw("malloc/free - deadlock");
+ // Disable preemption during settype_flush.
+ // We can not use m->mallocing for this, because settype_flush calls mallocgc.
+ m->locks++;
m->mallocing = 1;
- if(size == 0)
- size = 1;
if(DebugTypeAtBlockEnd)
size += sizeof(uintptr);
c = m->mcache;
- c->local_nmalloc++;
if(size <= MaxSmallSize) {
// Allocate from mcache free lists.
- sizeclass = runtime·SizeToClass(size);
+ // Inlined version of SizeToClass().
+ if(size <= 1024-8)
+ sizeclass = runtime·size_to_class8[(size+7)>>3];
+ else
+ sizeclass = runtime·size_to_class128[(size-1024+127) >> 7];
size = runtime·class_to_size[sizeclass];
- v = runtime·MCache_Alloc(c, sizeclass, size, zeroed);
- if(v == nil)
- runtime·throw("out of memory");
- c->local_alloc += size;
- c->local_total_alloc += size;
- c->local_by_size[sizeclass].nmalloc++;
+ l = &c->list[sizeclass];
+ if(l->list == nil)
+ runtime·MCache_Refill(c, sizeclass);
+ v = l->list;
+ l->list = v->next;
+ l->nlist--;
+ if(!(flag & FlagNoZero)) {
+ v->next = nil;
+ // block is zeroed iff second word is zero ...
+ if(size > sizeof(uintptr) && ((uintptr*)v)[1] != 0)
+ runtime·memclr((byte*)v, size);
+ }
+ c->local_cachealloc += size;
} else {
// TODO(rsc): Report tracebacks for very large allocations.
@@ -65,32 +86,41 @@ runtime·mallocgc(uintptr size, uint32 flag, int32 dogc, int32 zeroed)
npages = size >> PageShift;
if((size & PageMask) != 0)
npages++;
- s = runtime·MHeap_Alloc(runtime·mheap, npages, 0, 1, zeroed);
+ s = runtime·MHeap_Alloc(&runtime·mheap, npages, 0, 1, !(flag & FlagNoZero));
if(s == nil)
runtime·throw("out of memory");
+ s->limit = (byte*)(s->start<<PageShift) + size;
size = npages<<PageShift;
- c->local_alloc += size;
- c->local_total_alloc += size;
v = (void*)(s->start << PageShift);
// setup for mark sweep
runtime·markspan(v, 0, 0, true);
}
- if (sizeof(void*) == 4 && c->local_total_alloc >= (1<<30)) {
- // purge cache stats to prevent overflow
- runtime·lock(runtime·mheap);
- runtime·purgecachedstats(c);
- runtime·unlock(runtime·mheap);
- }
-
if(!(flag & FlagNoGC))
- runtime·markallocated(v, size, (flag&FlagNoPointers) != 0);
+ runtime·markallocated(v, size, (flag&FlagNoScan) != 0);
if(DebugTypeAtBlockEnd)
- *(uintptr*)((uintptr)v+size-sizeof(uintptr)) = 0;
+ *(uintptr*)((uintptr)v+size-sizeof(uintptr)) = typ;
+
+ // TODO: save type even if FlagNoScan? Potentially expensive but might help
+ // heap profiling/tracing.
+ if(UseSpanType && !(flag & FlagNoScan) && typ != 0) {
+ uintptr *buf, i;
+
+ buf = m->settype_buf;
+ i = m->settype_bufsize;
+ buf[i++] = (uintptr)v;
+ buf[i++] = typ;
+ m->settype_bufsize = i;
+ }
m->mallocing = 0;
+ if(UseSpanType && !(flag & FlagNoScan) && typ != 0 && m->settype_bufsize == nelem(m->settype_buf))
+ runtime·settype_flush(m);
+ m->locks--;
+ if(m->locks == 0 && g->preempt) // restore the preemption request in case we've cleared it in newstack
+ g->stackguard0 = StackPreempt;
if(!(flag & FlagNoProfiling) && (rate = runtime·MemProfileRate) > 0) {
if(size >= rate)
@@ -109,20 +139,18 @@ runtime·mallocgc(uintptr size, uint32 flag, int32 dogc, int32 zeroed)
}
}
- if(dogc && mstats.heap_alloc >= mstats.next_gc)
+ if(!(flag & FlagNoInvokeGC) && mstats.heap_alloc >= mstats.next_gc)
runtime·gc(0);
- if(raceenabled) {
- runtime·racemalloc(v, size, m->racepc);
- m->racepc = nil;
- }
+ if(raceenabled)
+ runtime·racemalloc(v, size);
return v;
}
void*
runtime·malloc(uintptr size)
{
- return runtime·mallocgc(size, 0, 0, 1);
+ return runtime·mallocgc(size, 0, FlagNoInvokeGC);
}
// Free the object whose base pointer is v.
@@ -165,7 +193,9 @@ runtime·free(void *v)
// they might coalesce v into other spans and change the bitmap further.
runtime·markfreed(v, size);
runtime·unmarkspan(v, 1<<PageShift);
- runtime·MHeap_Free(runtime·mheap, s, 1);
+ runtime·MHeap_Free(&runtime·mheap, s, 1);
+ c->local_nlargefree++;
+ c->local_largefree += size;
} else {
// Small object.
size = runtime·class_to_size[sizeclass];
@@ -175,11 +205,9 @@ runtime·free(void *v)
// it might coalesce v and other blocks into a bigger span
// and change the bitmap further.
runtime·markfreed(v, size);
- c->local_by_size[sizeclass].nfree++;
+ c->local_nsmallfree[sizeclass]++;
runtime·MCache_Free(c, v, sizeclass, size);
}
- c->local_nfree++;
- c->local_alloc -= size;
if(prof)
runtime·MProf_Free(v, size);
m->mallocing = 0;
@@ -195,12 +223,12 @@ runtime·mlookup(void *v, byte **base, uintptr *size, MSpan **sp)
m->mcache->local_nlookup++;
if (sizeof(void*) == 4 && m->mcache->local_nlookup >= (1<<30)) {
// purge cache stats to prevent overflow
- runtime·lock(runtime·mheap);
+ runtime·lock(&runtime·mheap);
runtime·purgecachedstats(m->mcache);
- runtime·unlock(runtime·mheap);
+ runtime·unlock(&runtime·mheap);
}
- s = runtime·MHeap_LookupMaybe(runtime·mheap, v);
+ s = runtime·MHeap_LookupMaybe(&runtime·mheap, v);
if(sp)
*sp = s;
if(s == nil) {
@@ -222,11 +250,6 @@ runtime·mlookup(void *v, byte **base, uintptr *size, MSpan **sp)
return 1;
}
- if((byte*)v >= (byte*)s->limit) {
- // pointers past the last block do not count as pointers.
- return 0;
- }
-
n = s->elemsize;
if(base) {
i = ((byte*)v - p)/n;
@@ -244,11 +267,9 @@ runtime·allocmcache(void)
intgo rate;
MCache *c;
- runtime·lock(runtime·mheap);
- c = runtime·FixAlloc_Alloc(&runtime·mheap->cachealloc);
- mstats.mcache_inuse = runtime·mheap->cachealloc.inuse;
- mstats.mcache_sys = runtime·mheap->cachealloc.sys;
- runtime·unlock(runtime·mheap);
+ runtime·lock(&runtime·mheap);
+ c = runtime·FixAlloc_Alloc(&runtime·mheap.cachealloc);
+ runtime·unlock(&runtime·mheap);
runtime·memclr((byte*)c, sizeof(*c));
// Set first allocation sample size.
@@ -265,30 +286,32 @@ void
runtime·freemcache(MCache *c)
{
runtime·MCache_ReleaseAll(c);
- runtime·lock(runtime·mheap);
+ runtime·lock(&runtime·mheap);
runtime·purgecachedstats(c);
- runtime·FixAlloc_Free(&runtime·mheap->cachealloc, c);
- runtime·unlock(runtime·mheap);
+ runtime·FixAlloc_Free(&runtime·mheap.cachealloc, c);
+ runtime·unlock(&runtime·mheap);
}
void
runtime·purgecachedstats(MCache *c)
{
+ MHeap *h;
+ int32 i;
+
// Protected by either heap or GC lock.
+ h = &runtime·mheap;
mstats.heap_alloc += c->local_cachealloc;
c->local_cachealloc = 0;
- mstats.heap_objects += c->local_objects;
- c->local_objects = 0;
- mstats.nmalloc += c->local_nmalloc;
- c->local_nmalloc = 0;
- mstats.nfree += c->local_nfree;
- c->local_nfree = 0;
mstats.nlookup += c->local_nlookup;
c->local_nlookup = 0;
- mstats.alloc += c->local_alloc;
- c->local_alloc= 0;
- mstats.total_alloc += c->local_total_alloc;
- c->local_total_alloc= 0;
+ h->largefree += c->local_largefree;
+ c->local_largefree = 0;
+ h->nlargefree += c->local_nlargefree;
+ c->local_nlargefree = 0;
+ for(i=0; i<nelem(c->local_nsmallfree); i++) {
+ h->nsmallfree[i] += c->local_nsmallfree[i];
+ c->local_nsmallfree[i] = 0;
+ }
}
uintptr runtime·sizeof_C_MStats = sizeof(MStats);
@@ -299,22 +322,22 @@ void
runtime·mallocinit(void)
{
byte *p;
- uintptr arena_size, bitmap_size;
+ uintptr arena_size, bitmap_size, spans_size;
extern byte end[];
byte *want;
uintptr limit;
+ uint64 i;
p = nil;
arena_size = 0;
bitmap_size = 0;
-
+ spans_size = 0;
+
// for 64-bit build
USED(p);
USED(arena_size);
USED(bitmap_size);
-
- if((runtime·mheap = runtime·SysAlloc(sizeof(*runtime·mheap))) == nil)
- runtime·throw("runtime: cannot allocate heap metadata");
+ USED(spans_size);
runtime·InitSizes();
@@ -331,15 +354,17 @@ runtime·mallocinit(void)
// 128 GB (MaxMem) should be big enough for now.
//
// The code will work with the reservation at any address, but ask
- // SysReserve to use 0x000000c000000000 if possible.
+ // SysReserve to use 0x0000XXc000000000 if possible (XX=00...7f).
// Allocating a 128 GB region takes away 37 bits, and the amd64
// doesn't let us choose the top 17 bits, so that leaves the 11 bits
// in the middle of 0x00c0 for us to choose. Choosing 0x00c0 means
- // that the valid memory addresses will begin 0x00c0, 0x00c1, ..., 0x0x00df.
+ // that the valid memory addresses will begin 0x00c0, 0x00c1, ..., 0x00df.
// In little-endian, that's c0 00, c1 00, ..., df 00. None of those are valid
// UTF-8 sequences, and they are otherwise as far away from
- // ff (likely a common byte) as possible. An earlier attempt to use 0x11f8
- // caused out of memory errors on OS X during thread allocations.
+ // ff (likely a common byte) as possible. If that fails, we try other 0xXXc0
+ // addresses. An earlier attempt to use 0x11f8 caused out of memory errors
+ // on OS X during thread allocations. 0x00c0 causes conflicts with
+ // AddressSanitizer which reserves all memory up to 0x0100.
// These choices are both for debuggability and to reduce the
// odds of the conservative garbage collector not collecting memory
// because some non-pointer block of memory had a bit pattern
@@ -351,7 +376,14 @@ runtime·mallocinit(void)
// If this fails we fall back to the 32 bit memory mechanism
arena_size = MaxMem;
bitmap_size = arena_size / (sizeof(void*)*8/4);
- p = runtime·SysReserve((void*)(0x00c0ULL<<32), bitmap_size + arena_size);
+ spans_size = arena_size / PageSize * sizeof(runtime·mheap.spans[0]);
+ spans_size = ROUND(spans_size, PageSize);
+ for(i = 0; i <= 0x7f; i++) {
+ p = (void*)(i<<40 | 0x00c0ULL<<32);
+ p = runtime·SysReserve(p, bitmap_size + spans_size + arena_size);
+ if(p != nil)
+ break;
+ }
}
if (p == nil) {
// On a 32-bit machine, we can't typically get away
@@ -373,11 +405,14 @@ runtime·mallocinit(void)
// of address space, which is probably too much in a 32-bit world.
bitmap_size = MaxArena32 / (sizeof(void*)*8/4);
arena_size = 512<<20;
- if(limit > 0 && arena_size+bitmap_size > limit) {
+ spans_size = MaxArena32 / PageSize * sizeof(runtime·mheap.spans[0]);
+ if(limit > 0 && arena_size+bitmap_size+spans_size > limit) {
bitmap_size = (limit / 9) & ~((1<<PageShift) - 1);
arena_size = bitmap_size * 8;
+ spans_size = arena_size / PageSize * sizeof(runtime·mheap.spans[0]);
}
-
+ spans_size = ROUND(spans_size, PageSize);
+
// SysReserve treats the address we ask for, end, as a hint,
// not as an absolute requirement. If we ask for the end
// of the data segment but the operating system requires
@@ -387,23 +422,25 @@ runtime·mallocinit(void)
// So adjust it upward a little bit ourselves: 1/4 MB to get
// away from the running binary image and then round up
// to a MB boundary.
- want = (byte*)(((uintptr)end + (1<<18) + (1<<20) - 1)&~((1<<20)-1));
- p = runtime·SysReserve(want, bitmap_size + arena_size);
+ want = (byte*)ROUND((uintptr)end + (1<<18), 1<<20);
+ p = runtime·SysReserve(want, bitmap_size + spans_size + arena_size);
if(p == nil)
runtime·throw("runtime: cannot reserve arena virtual address space");
if((uintptr)p & (((uintptr)1<<PageShift)-1))
- runtime·printf("runtime: SysReserve returned unaligned address %p; asked for %p", p, bitmap_size+arena_size);
+ runtime·printf("runtime: SysReserve returned unaligned address %p; asked for %p", p,
+ bitmap_size+spans_size+arena_size);
}
if((uintptr)p & (((uintptr)1<<PageShift)-1))
runtime·throw("runtime: SysReserve returned unaligned address");
- runtime·mheap->bitmap = p;
- runtime·mheap->arena_start = p + bitmap_size;
- runtime·mheap->arena_used = runtime·mheap->arena_start;
- runtime·mheap->arena_end = runtime·mheap->arena_start + arena_size;
+ runtime·mheap.spans = (MSpan**)p;
+ runtime·mheap.bitmap = p + spans_size;
+ runtime·mheap.arena_start = p + spans_size + bitmap_size;
+ runtime·mheap.arena_used = runtime·mheap.arena_start;
+ runtime·mheap.arena_end = runtime·mheap.arena_start + arena_size;
// Initialize the rest of the allocator.
- runtime·MHeap_Init(runtime·mheap, runtime·SysAlloc);
+ runtime·MHeap_Init(&runtime·mheap);
m->mcache = runtime·allocmcache();
// See if it works.
@@ -422,8 +459,7 @@ runtime·MHeap_SysAlloc(MHeap *h, uintptr n)
uintptr needed;
needed = (uintptr)h->arena_used + n - (uintptr)h->arena_end;
- // Round wanted arena size to a multiple of 256MB.
- needed = (needed + (256<<20) - 1) & ~((256<<20)-1);
+ needed = ROUND(needed, 256<<20);
new_end = h->arena_end + needed;
if(new_end <= h->arena_start + MaxArena32) {
p = runtime·SysReserve(h->arena_end, new_end - h->arena_end);
@@ -434,9 +470,10 @@ runtime·MHeap_SysAlloc(MHeap *h, uintptr n)
if(n <= h->arena_end - h->arena_used) {
// Keep taking from our reservation.
p = h->arena_used;
- runtime·SysMap(p, n);
+ runtime·SysMap(p, n, &mstats.heap_sys);
h->arena_used += n;
runtime·MHeap_MapBits(h);
+ runtime·MHeap_MapSpans(h);
if(raceenabled)
runtime·racemapshadow(p, n);
return p;
@@ -449,14 +486,14 @@ runtime·MHeap_SysAlloc(MHeap *h, uintptr n)
// On 32-bit, once the reservation is gone we can
// try to get memory at a location chosen by the OS
// and hope that it is in the range we allocated bitmap for.
- p = runtime·SysAlloc(n);
+ p = runtime·SysAlloc(n, &mstats.heap_sys);
if(p == nil)
return nil;
if(p < h->arena_start || p+n - h->arena_start >= MaxArena32) {
runtime·printf("runtime: memory allocated by OS (%p) not in usable range [%p,%p)\n",
p, h->arena_start, h->arena_start+MaxArena32);
- runtime·SysFree(p, n);
+ runtime·SysFree(p, n, &mstats.heap_sys);
return nil;
}
@@ -465,6 +502,7 @@ runtime·MHeap_SysAlloc(MHeap *h, uintptr n)
if(h->arena_used > h->arena_end)
h->arena_end = h->arena_used;
runtime·MHeap_MapBits(h);
+ runtime·MHeap_MapSpans(h);
if(raceenabled)
runtime·racemapshadow(p, n);
}
@@ -472,17 +510,68 @@ runtime·MHeap_SysAlloc(MHeap *h, uintptr n)
return p;
}
+static struct
+{
+ Lock;
+ byte* pos;
+ byte* end;
+} persistent;
+
+enum
+{
+ PersistentAllocChunk = 256<<10,
+ PersistentAllocMaxBlock = 64<<10, // VM reservation granularity is 64K on windows
+};
+
+// Wrapper around SysAlloc that can allocate small chunks.
+// There is no associated free operation.
+// Intended for things like function/type/debug-related persistent data.
+// If align is 0, uses default align (currently 8).
+void*
+runtime·persistentalloc(uintptr size, uintptr align, uint64 *stat)
+{
+ byte *p;
+
+ if(align != 0) {
+ if(align&(align-1))
+ runtime·throw("persistentalloc: align is now a power of 2");
+ if(align > PageSize)
+ runtime·throw("persistentalloc: align is too large");
+ } else
+ align = 8;
+ if(size >= PersistentAllocMaxBlock)
+ return runtime·SysAlloc(size, stat);
+ runtime·lock(&persistent);
+ persistent.pos = (byte*)ROUND((uintptr)persistent.pos, align);
+ if(persistent.pos + size > persistent.end) {
+ persistent.pos = runtime·SysAlloc(PersistentAllocChunk, &mstats.other_sys);
+ if(persistent.pos == nil) {
+ runtime·unlock(&persistent);
+ runtime·throw("runtime: cannot allocate memory");
+ }
+ persistent.end = persistent.pos + PersistentAllocChunk;
+ }
+ p = persistent.pos;
+ persistent.pos += size;
+ runtime·unlock(&persistent);
+ if(stat != &mstats.other_sys) {
+ // reaccount the allocation against provided stat
+ runtime·xadd64(stat, size);
+ runtime·xadd64(&mstats.other_sys, -(uint64)size);
+ }
+ return p;
+}
+
static Lock settype_lock;
void
-runtime·settype_flush(M *mp, bool sysalloc)
+runtime·settype_flush(M *mp)
{
uintptr *buf, *endbuf;
uintptr size, ofs, j, t;
uintptr ntypes, nbytes2, nbytes3;
uintptr *data2;
byte *data3;
- bool sysalloc3;
void *v;
uintptr typ, p;
MSpan *s;
@@ -501,8 +590,8 @@ runtime·settype_flush(M *mp, bool sysalloc)
// (Manually inlined copy of runtime·MHeap_Lookup)
p = (uintptr)v>>PageShift;
if(sizeof(void*) == 8)
- p -= (uintptr)runtime·mheap->arena_start >> PageShift;
- s = runtime·mheap->map[p];
+ p -= (uintptr)runtime·mheap.arena_start >> PageShift;
+ s = runtime·mheap.spans[p];
if(s->sizeclass == 0) {
s->types.compression = MTypes_Single;
@@ -517,20 +606,9 @@ runtime·settype_flush(M *mp, bool sysalloc)
case MTypes_Empty:
ntypes = (s->npages << PageShift) / size;
nbytes3 = 8*sizeof(uintptr) + 1*ntypes;
-
- if(!sysalloc) {
- data3 = runtime·mallocgc(nbytes3, FlagNoProfiling|FlagNoPointers, 0, 1);
- } else {
- data3 = runtime·SysAlloc(nbytes3);
- if(data3 == nil)
- runtime·throw("runtime: cannot allocate memory");
- if(0) runtime·printf("settype(0->3): SysAlloc(%x) --> %p\n", (uint32)nbytes3, data3);
- }
-
+ data3 = runtime·mallocgc(nbytes3, 0, FlagNoProfiling|FlagNoScan|FlagNoInvokeGC);
s->types.compression = MTypes_Bytes;
- s->types.sysalloc = sysalloc;
s->types.data = (uintptr)data3;
-
((uintptr*)data3)[1] = typ;
data3[8*sizeof(uintptr) + ofs] = 1;
break;
@@ -555,20 +633,8 @@ runtime·settype_flush(M *mp, bool sysalloc)
} else {
ntypes = (s->npages << PageShift) / size;
nbytes2 = ntypes * sizeof(uintptr);
-
- if(!sysalloc) {
- data2 = runtime·mallocgc(nbytes2, FlagNoProfiling|FlagNoPointers, 0, 1);
- } else {
- data2 = runtime·SysAlloc(nbytes2);
- if(data2 == nil)
- runtime·throw("runtime: cannot allocate memory");
- if(0) runtime·printf("settype.(3->2): SysAlloc(%x) --> %p\n", (uint32)nbytes2, data2);
- }
-
- sysalloc3 = s->types.sysalloc;
-
+ data2 = runtime·mallocgc(nbytes2, 0, FlagNoProfiling|FlagNoScan|FlagNoInvokeGC);
s->types.compression = MTypes_Words;
- s->types.sysalloc = sysalloc;
s->types.data = (uintptr)data2;
// Move the contents of data3 to data2. Then deallocate data3.
@@ -577,12 +643,6 @@ runtime·settype_flush(M *mp, bool sysalloc)
t = ((uintptr*)data3)[t];
data2[j] = t;
}
- if(sysalloc3) {
- nbytes3 = 8*sizeof(uintptr) + 1*ntypes;
- if(0) runtime·printf("settype.(3->2): SysFree(%p,%x)\n", data3, (uint32)nbytes3);
- runtime·SysFree(data3, nbytes3);
- }
-
data2[ofs] = typ;
}
break;
@@ -593,64 +653,6 @@ runtime·settype_flush(M *mp, bool sysalloc)
mp->settype_bufsize = 0;
}
-// It is forbidden to use this function if it is possible that
-// explicit deallocation via calling runtime·free(v) may happen.
-void
-runtime·settype(void *v, uintptr t)
-{
- M *mp;
- uintptr *buf;
- uintptr i;
- MSpan *s;
-
- if(t == 0)
- runtime·throw("settype: zero type");
-
- mp = m;
- buf = mp->settype_buf;
- i = mp->settype_bufsize;
- buf[i+0] = (uintptr)v;
- buf[i+1] = t;
- i += 2;
- mp->settype_bufsize = i;
-
- if(i == nelem(mp->settype_buf)) {
- runtime·settype_flush(mp, false);
- }
-
- if(DebugTypeAtBlockEnd) {
- s = runtime·MHeap_Lookup(runtime·mheap, v);
- *(uintptr*)((uintptr)v+s->elemsize-sizeof(uintptr)) = t;
- }
-}
-
-void
-runtime·settype_sysfree(MSpan *s)
-{
- uintptr ntypes, nbytes;
-
- if(!s->types.sysalloc)
- return;
-
- nbytes = (uintptr)-1;
-
- switch (s->types.compression) {
- case MTypes_Words:
- ntypes = (s->npages << PageShift) / s->elemsize;
- nbytes = ntypes * sizeof(uintptr);
- break;
- case MTypes_Bytes:
- ntypes = (s->npages << PageShift) / s->elemsize;
- nbytes = 8*sizeof(uintptr) + 1*ntypes;
- break;
- }
-
- if(nbytes != (uintptr)-1) {
- if(0) runtime·printf("settype: SysFree(%p,%x)\n", (void*)s->types.data, (uint32)nbytes);
- runtime·SysFree((void*)s->types.data, nbytes);
- }
-}
-
uintptr
runtime·gettype(void *v)
{
@@ -658,7 +660,7 @@ runtime·gettype(void *v)
uintptr t, ofs;
byte *data;
- s = runtime·MHeap_LookupMaybe(runtime·mheap, v);
+ s = runtime·MHeap_LookupMaybe(&runtime·mheap, v);
if(s != nil) {
t = 0;
switch(s->types.compression) {
@@ -695,61 +697,25 @@ runtime·gettype(void *v)
void*
runtime·mal(uintptr n)
{
- return runtime·mallocgc(n, 0, 1, 1);
+ return runtime·mallocgc(n, 0, 0);
}
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·new(Type *typ, uint8 *ret)
{
- uint32 flag;
-
- if(raceenabled)
- m->racepc = runtime·getcallerpc(&typ);
-
- if(typ->size == 0) {
- // All 0-length allocations use this pointer.
- // The language does not require the allocations to
- // have distinct values.
- ret = (uint8*)&runtime·zerobase;
- } else {
- flag = typ->kind&KindNoPointers ? FlagNoPointers : 0;
- ret = runtime·mallocgc(typ->size, flag, 1, 1);
-
- if(UseSpanType && !flag) {
- if(false)
- runtime·printf("new %S: %p\n", *typ->string, ret);
- runtime·settype(ret, (uintptr)typ | TypeInfo_SingleObject);
- }
- }
-
+ ret = runtime·mallocgc(typ->size, (uintptr)typ | TypeInfo_SingleObject, typ->kind&KindNoPointers ? FlagNoScan : 0);
FLUSH(&ret);
}
static void*
cnew(Type *typ, intgo n, int32 objtyp)
{
- uint32 flag;
- void *ret;
-
if((objtyp&(PtrSize-1)) != objtyp)
runtime·throw("runtime: invalid objtyp");
if(n < 0 || (typ->size > 0 && n > MaxMem/typ->size))
runtime·panicstring("runtime: allocation size out of range");
- if(typ->size == 0 || n == 0) {
- // All 0-length allocations use this pointer.
- // The language does not require the allocations to
- // have distinct values.
- return &runtime·zerobase;
- }
- flag = typ->kind&KindNoPointers ? FlagNoPointers : 0;
- ret = runtime·mallocgc(typ->size*n, flag, 1, 1);
- if(UseSpanType && !flag) {
- if(false)
- runtime·printf("cnew [%D]%S: %p\n", (int64)n, *typ->string, ret);
- runtime·settype(ret, (uintptr)typ | objtyp);
- }
- return ret;
+ return runtime·mallocgc(typ->size*n, (uintptr)typ | objtyp, typ->kind&KindNoPointers ? FlagNoScan : 0);
}
// same as runtime·new, but callable from C
@@ -776,6 +742,9 @@ func SetFinalizer(obj Eface, finalizer Eface) {
int32 i;
uintptr nret;
Type *t;
+ Type *fint;
+ PtrType *ot;
+ Iface iface;
if(obj.type == nil) {
runtime·printf("runtime.SetFinalizer: first argument is nil interface\n");
@@ -790,30 +759,43 @@ func SetFinalizer(obj Eface, finalizer Eface) {
goto throw;
}
nret = 0;
+ ot = (PtrType*)obj.type;
+ fint = nil;
if(finalizer.type != nil) {
if(finalizer.type->kind != KindFunc)
goto badfunc;
ft = (FuncType*)finalizer.type;
- if(ft->dotdotdot || ft->in.len != 1 || *(Type**)ft->in.array != obj.type)
+ if(ft->dotdotdot || ft->in.len != 1)
+ goto badfunc;
+ fint = *(Type**)ft->in.array;
+ if(fint == obj.type) {
+ // ok - same type
+ } else if(fint->kind == KindPtr && (fint->x == nil || fint->x->name == nil || obj.type->x == nil || obj.type->x->name == nil) && ((PtrType*)fint)->elem == ((PtrType*)obj.type)->elem) {
+ // ok - not same type, but both pointers,
+ // one or the other is unnamed, and same element type, so assignable.
+ } else if(fint->kind == KindInterface && ((InterfaceType*)fint)->mhdr.len == 0) {
+ // ok - satisfies empty interface
+ } else if(fint->kind == KindInterface && runtime·ifaceE2I2((InterfaceType*)fint, obj, &iface)) {
+ // ok - satisfies non-empty interface
+ } else
goto badfunc;
// compute size needed for return parameters
for(i=0; i<ft->out.len; i++) {
t = ((Type**)ft->out.array)[i];
- nret = (nret + t->align - 1) & ~(t->align - 1);
- nret += t->size;
+ nret = ROUND(nret, t->align) + t->size;
}
- nret = (nret + sizeof(void*)-1) & ~(sizeof(void*)-1);
+ nret = ROUND(nret, sizeof(void*));
}
- if(!runtime·addfinalizer(obj.data, finalizer.data, nret)) {
+ if(!runtime·addfinalizer(obj.data, finalizer.data, nret, fint, ot)) {
runtime·printf("runtime.SetFinalizer: finalizer already set\n");
goto throw;
}
return;
badfunc:
- runtime·printf("runtime.SetFinalizer: second argument is %S, not func(%S)\n", *finalizer.type->string, *obj.type->string);
+ runtime·printf("runtime.SetFinalizer: cannot pass %S to finalizer %S\n", *obj.type->string, *finalizer.type->string);
throw:
runtime·throw("runtime.SetFinalizer");
}
diff --git a/src/pkg/runtime/malloc.h b/src/pkg/runtime/malloc.h
index 7bbc1b2b3..2c66c6fa7 100644
--- a/src/pkg/runtime/malloc.h
+++ b/src/pkg/runtime/malloc.h
@@ -108,9 +108,7 @@ enum
// Tunable constants.
MaxSmallSize = 32<<10,
- FixAllocChunk = 128<<10, // Chunk size for FixAlloc
- MaxMCacheListLen = 256, // Maximum objects on MCacheList
- MaxMCacheSize = 2<<20, // Maximum bytes in one MCache
+ FixAllocChunk = 16<<10, // Chunk size for FixAlloc
MaxMHeapList = 1<<(20 - PageShift), // Maximum page length for fixed-size list in MHeap.
HeapAllocChunk = 1<<20, // Chunk size for heap growth
@@ -155,13 +153,13 @@ struct MLink
// SysAlloc obtains a large chunk of zeroed memory from the
// operating system, typically on the order of a hundred kilobytes
-// or a megabyte. If the pointer argument is non-nil, the caller
-// wants a mapping there or nowhere.
+// or a megabyte.
//
// SysUnused notifies the operating system that the contents
// of the memory region are no longer needed and can be reused
-// for other purposes. The program reserves the right to start
-// accessing those pages in the future.
+// for other purposes.
+// SysUsed notifies the operating system that the contents
+// of the memory region are needed again.
//
// SysFree returns it unconditionally; this is only used if
// an out-of-memory error has been detected midway through
@@ -174,10 +172,11 @@ struct MLink
//
// SysMap maps previously reserved address space for use.
-void* runtime·SysAlloc(uintptr nbytes);
-void runtime·SysFree(void *v, uintptr nbytes);
+void* runtime·SysAlloc(uintptr nbytes, uint64 *stat);
+void runtime·SysFree(void *v, uintptr nbytes, uint64 *stat);
void runtime·SysUnused(void *v, uintptr nbytes);
-void runtime·SysMap(void *v, uintptr nbytes);
+void runtime·SysUsed(void *v, uintptr nbytes);
+void runtime·SysMap(void *v, uintptr nbytes, uint64 *stat);
void* runtime·SysReserve(void *v, uintptr nbytes);
// FixAlloc is a simple free-list allocator for fixed size objects.
@@ -190,18 +189,17 @@ void* runtime·SysReserve(void *v, uintptr nbytes);
// smashed by freeing and reallocating.
struct FixAlloc
{
- uintptr size;
- void *(*alloc)(uintptr);
- void (*first)(void *arg, byte *p); // called first time p is returned
- void *arg;
- MLink *list;
- byte *chunk;
- uint32 nchunk;
- uintptr inuse; // in-use bytes now
- uintptr sys; // bytes obtained from system
+ uintptr size;
+ void (*first)(void *arg, byte *p); // called first time p is returned
+ void* arg;
+ MLink* list;
+ byte* chunk;
+ uint32 nchunk;
+ uintptr inuse; // in-use bytes now
+ uint64* stat;
};
-void runtime·FixAlloc_Init(FixAlloc *f, uintptr size, void *(*alloc)(uintptr), void (*first)(void*, byte*), void *arg);
+void runtime·FixAlloc_Init(FixAlloc *f, uintptr size, void (*first)(void*, byte*), void *arg, uint64 *stat);
void* runtime·FixAlloc_Alloc(FixAlloc *f);
void runtime·FixAlloc_Free(FixAlloc *f, void *p);
@@ -236,6 +234,8 @@ struct MStats
uint64 mcache_inuse; // MCache structures
uint64 mcache_sys;
uint64 buckhash_sys; // profiling bucket hash table
+ uint64 gc_sys;
+ uint64 other_sys;
// Statistics about garbage collector.
// Protected by mheap or stopping the world during GC.
@@ -267,14 +267,12 @@ extern MStats mstats;
// class_to_size[i] = largest size in class i
// class_to_allocnpages[i] = number of pages to allocate when
// making new objects in class i
-// class_to_transfercount[i] = number of objects to move when
-// taking a bunch of objects out of the central lists
-// and putting them in the thread free list.
int32 runtime·SizeToClass(int32);
extern int32 runtime·class_to_size[NumSizeClasses];
extern int32 runtime·class_to_allocnpages[NumSizeClasses];
-extern int32 runtime·class_to_transfercount[NumSizeClasses];
+extern int8 runtime·size_to_class8[1024/8 + 1];
+extern int8 runtime·size_to_class128[(MaxSmallSize-1024)/128 + 1];
extern void runtime·InitSizes(void);
@@ -285,30 +283,24 @@ struct MCacheList
{
MLink *list;
uint32 nlist;
- uint32 nlistmin;
};
struct MCache
{
- MCacheList list[NumSizeClasses];
- uintptr size;
+ // The following members are accessed on every malloc,
+ // so they are grouped here for better caching.
+ int32 next_sample; // trigger heap sample after allocating this many bytes
intptr local_cachealloc; // bytes allocated (or freed) from cache since last lock of heap
- intptr local_objects; // objects allocated (or freed) from cache since last lock of heap
- intptr local_alloc; // bytes allocated (or freed) since last lock of heap
- uintptr local_total_alloc; // bytes allocated (even if freed) since last lock of heap
- uintptr local_nmalloc; // number of mallocs since last lock of heap
- uintptr local_nfree; // number of frees since last lock of heap
- uintptr local_nlookup; // number of pointer lookups since last lock of heap
- int32 next_sample; // trigger heap sample after allocating this many bytes
- // Statistics about allocation size classes since last lock of heap
- struct {
- uintptr nmalloc;
- uintptr nfree;
- } local_by_size[NumSizeClasses];
-
+ // The rest is not accessed on every malloc.
+ MCacheList list[NumSizeClasses];
+ // Local allocator stats, flushed during GC.
+ uintptr local_nlookup; // number of pointer lookups
+ uintptr local_largefree; // bytes freed for large objects (>MaxSmallSize)
+ uintptr local_nlargefree; // number of frees for large objects (>MaxSmallSize)
+ uintptr local_nsmallfree[NumSizeClasses]; // number of frees for small objects (<=MaxSmallSize)
};
-void* runtime·MCache_Alloc(MCache *c, int32 sizeclass, uintptr size, int32 zeroed);
+void runtime·MCache_Refill(MCache *c, int32 sizeclass);
void runtime·MCache_Free(MCache *c, void *p, int32 sizeclass, uintptr size);
void runtime·MCache_ReleaseAll(MCache *c);
@@ -346,7 +338,6 @@ enum
struct MTypes
{
byte compression; // one of MTypes_*
- bool sysalloc; // whether (void*)data is from runtime·SysAlloc
uintptr data;
};
@@ -397,8 +388,8 @@ struct MCentral
};
void runtime·MCentral_Init(MCentral *c, int32 sizeclass);
-int32 runtime·MCentral_AllocList(MCentral *c, int32 n, MLink **first);
-void runtime·MCentral_FreeList(MCentral *c, int32 n, MLink *first);
+int32 runtime·MCentral_AllocList(MCentral *c, MLink **first);
+void runtime·MCentral_FreeList(MCentral *c, MLink *first);
void runtime·MCentral_FreeSpan(MCentral *c, MSpan *s, int32 n, MLink *start, MLink *end);
// Main malloc heap.
@@ -414,7 +405,8 @@ struct MHeap
uint32 nspancap;
// span lookup
- MSpan *map[1<<MHeapMap_Bits];
+ MSpan** spans;
+ uintptr spans_mapped;
// range of addresses we might see in the heap
byte *bitmap;
@@ -434,10 +426,15 @@ struct MHeap
FixAlloc spanalloc; // allocator for Span*
FixAlloc cachealloc; // allocator for MCache*
+
+ // Malloc stats.
+ uint64 largefree; // bytes freed for large objects (>MaxSmallSize)
+ uint64 nlargefree; // number of frees for large objects (>MaxSmallSize)
+ uint64 nsmallfree[NumSizeClasses]; // number of frees for small objects (<=MaxSmallSize)
};
-extern MHeap *runtime·mheap;
+extern MHeap runtime·mheap;
-void runtime·MHeap_Init(MHeap *h, void *(*allocator)(uintptr));
+void runtime·MHeap_Init(MHeap *h);
MSpan* runtime·MHeap_Alloc(MHeap *h, uintptr npage, int32 sizeclass, int32 acct, int32 zeroed);
void runtime·MHeap_Free(MHeap *h, MSpan *s, int32 acct);
MSpan* runtime·MHeap_Lookup(MHeap *h, void *v);
@@ -445,9 +442,11 @@ MSpan* runtime·MHeap_LookupMaybe(MHeap *h, void *v);
void runtime·MGetSizeClassInfo(int32 sizeclass, uintptr *size, int32 *npages, int32 *nobj);
void* runtime·MHeap_SysAlloc(MHeap *h, uintptr n);
void runtime·MHeap_MapBits(MHeap *h);
+void runtime·MHeap_MapSpans(MHeap *h);
void runtime·MHeap_Scavenger(void);
-void* runtime·mallocgc(uintptr size, uint32 flag, int32 dogc, int32 zeroed);
+void* runtime·mallocgc(uintptr size, uintptr typ, uint32 flag);
+void* runtime·persistentalloc(uintptr size, uintptr align, uint64 *stat);
int32 runtime·mlookup(void *v, byte **base, uintptr *size, MSpan **s);
void runtime·gc(int32 force);
void runtime·markallocated(void *v, uintptr n, bool noptr);
@@ -463,17 +462,18 @@ void runtime·purgecachedstats(MCache*);
void* runtime·cnew(Type*);
void* runtime·cnewarray(Type*, intgo);
-void runtime·settype(void*, uintptr);
-void runtime·settype_flush(M*, bool);
+void runtime·settype_flush(M*);
void runtime·settype_sysfree(MSpan*);
uintptr runtime·gettype(void*);
enum
{
// flags to malloc
- FlagNoPointers = 1<<0, // no pointers here
- FlagNoProfiling = 1<<1, // must not profile
- FlagNoGC = 1<<2, // must not free or scan for pointers
+ FlagNoScan = 1<<0, // GC doesn't have to scan object
+ FlagNoProfiling = 1<<1, // must not profile
+ FlagNoGC = 1<<2, // must not free or scan for pointers
+ FlagNoZero = 1<<3, // don't zero memory
+ FlagNoInvokeGC = 1<<4, // don't invoke GC
};
void runtime·MProf_Malloc(void*, uintptr);
@@ -483,15 +483,13 @@ int32 runtime·gcprocs(void);
void runtime·helpgc(int32 nproc);
void runtime·gchelper(void);
-bool runtime·getfinalizer(void *p, bool del, FuncVal **fn, uintptr *nret);
void runtime·walkfintab(void (*fn)(void*));
enum
{
TypeInfo_SingleObject = 0,
TypeInfo_Array = 1,
- TypeInfo_Map = 2,
- TypeInfo_Chan = 3,
+ TypeInfo_Chan = 2,
// Enables type information at the end of blocks allocated from heap
DebugTypeAtBlockEnd = 0,
diff --git a/src/pkg/runtime/malloc_test.go b/src/pkg/runtime/malloc_test.go
new file mode 100644
index 000000000..128ec098c
--- /dev/null
+++ b/src/pkg/runtime/malloc_test.go
@@ -0,0 +1,156 @@
+// Copyright 2013 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_test
+
+import (
+ "flag"
+ . "runtime"
+ "testing"
+ "time"
+ "unsafe"
+)
+
+func TestMemStats(t *testing.T) {
+ // Test that MemStats has sane values.
+ st := new(MemStats)
+ ReadMemStats(st)
+ if st.HeapSys == 0 || st.StackSys == 0 || st.MSpanSys == 0 || st.MCacheSys == 0 ||
+ st.BuckHashSys == 0 || st.GCSys == 0 || st.OtherSys == 0 {
+ t.Fatalf("Zero sys value: %+v", *st)
+ }
+ if st.Sys != st.HeapSys+st.StackSys+st.MSpanSys+st.MCacheSys+
+ st.BuckHashSys+st.GCSys+st.OtherSys {
+ t.Fatalf("Bad sys value: %+v", *st)
+ }
+}
+
+var mallocSink uintptr
+
+func BenchmarkMalloc8(b *testing.B) {
+ var x uintptr
+ for i := 0; i < b.N; i++ {
+ p := new(int64)
+ x ^= uintptr(unsafe.Pointer(p))
+ }
+ mallocSink = x
+}
+
+func BenchmarkMalloc16(b *testing.B) {
+ var x uintptr
+ for i := 0; i < b.N; i++ {
+ p := new([2]int64)
+ x ^= uintptr(unsafe.Pointer(p))
+ }
+ mallocSink = x
+}
+
+func BenchmarkMallocTypeInfo8(b *testing.B) {
+ var x uintptr
+ for i := 0; i < b.N; i++ {
+ p := new(struct {
+ p [8 / unsafe.Sizeof(uintptr(0))]*int
+ })
+ x ^= uintptr(unsafe.Pointer(p))
+ }
+ mallocSink = x
+}
+
+func BenchmarkMallocTypeInfo16(b *testing.B) {
+ var x uintptr
+ for i := 0; i < b.N; i++ {
+ p := new(struct {
+ p [16 / unsafe.Sizeof(uintptr(0))]*int
+ })
+ x ^= uintptr(unsafe.Pointer(p))
+ }
+ mallocSink = x
+}
+
+var n = flag.Int("n", 1000, "number of goroutines")
+
+func BenchmarkGoroutineSelect(b *testing.B) {
+ quit := make(chan struct{})
+ read := func(ch chan struct{}) {
+ for {
+ select {
+ case _, ok := <-ch:
+ if !ok {
+ return
+ }
+ case <-quit:
+ return
+ }
+ }
+ }
+ benchHelper(b, *n, read)
+}
+
+func BenchmarkGoroutineBlocking(b *testing.B) {
+ read := func(ch chan struct{}) {
+ for {
+ if _, ok := <-ch; !ok {
+ return
+ }
+ }
+ }
+ benchHelper(b, *n, read)
+}
+
+func BenchmarkGoroutineForRange(b *testing.B) {
+ read := func(ch chan struct{}) {
+ for _ = range ch {
+ }
+ }
+ benchHelper(b, *n, read)
+}
+
+func benchHelper(b *testing.B, n int, read func(chan struct{})) {
+ m := make([]chan struct{}, n)
+ for i := range m {
+ m[i] = make(chan struct{}, 1)
+ go read(m[i])
+ }
+ b.StopTimer()
+ b.ResetTimer()
+ GC()
+
+ for i := 0; i < b.N; i++ {
+ for _, ch := range m {
+ if ch != nil {
+ ch <- struct{}{}
+ }
+ }
+ time.Sleep(10 * time.Millisecond)
+ b.StartTimer()
+ GC()
+ b.StopTimer()
+ }
+
+ for _, ch := range m {
+ close(ch)
+ }
+ time.Sleep(10 * time.Millisecond)
+}
+
+func BenchmarkGoroutineIdle(b *testing.B) {
+ quit := make(chan struct{})
+ fn := func() {
+ <-quit
+ }
+ for i := 0; i < *n; i++ {
+ go fn()
+ }
+
+ GC()
+ b.ResetTimer()
+
+ for i := 0; i < b.N; i++ {
+ GC()
+ }
+
+ b.StopTimer()
+ close(quit)
+ time.Sleep(10 * time.Millisecond)
+}
diff --git a/src/pkg/runtime/map_test.go b/src/pkg/runtime/map_test.go
index 9f9c40d15..a221cb28c 100644
--- a/src/pkg/runtime/map_test.go
+++ b/src/pkg/runtime/map_test.go
@@ -371,3 +371,41 @@ func testMapLookups(t *testing.T, m map[string]string) {
}
}
}
+
+// Tests whether the iterator returns the right elements when
+// started in the middle of a grow, when the keys are NaNs.
+func TestMapNanGrowIterator(t *testing.T) {
+ m := make(map[float64]int)
+ nan := math.NaN()
+ const nBuckets = 16
+ // To fill nBuckets buckets takes LOAD * nBuckets keys.
+ nKeys := int(nBuckets * *runtime.HashLoad)
+
+ // Get map to full point with nan keys.
+ for i := 0; i < nKeys; i++ {
+ m[nan] = i
+ }
+ // Trigger grow
+ m[1.0] = 1
+ delete(m, 1.0)
+
+ // Run iterator
+ found := make(map[int]struct{})
+ for _, v := range m {
+ if v != -1 {
+ if _, repeat := found[v]; repeat {
+ t.Fatalf("repeat of value %d", v)
+ }
+ found[v] = struct{}{}
+ }
+ if len(found) == nKeys/2 {
+ // Halfway through iteration, finish grow.
+ for i := 0; i < nBuckets; i++ {
+ delete(m, 1.0)
+ }
+ }
+ }
+ if len(found) != nKeys {
+ t.Fatalf("missing value")
+ }
+}
diff --git a/src/pkg/runtime/mapspeed_test.go b/src/pkg/runtime/mapspeed_test.go
index 3b7fbfd63..d643d9898 100644
--- a/src/pkg/runtime/mapspeed_test.go
+++ b/src/pkg/runtime/mapspeed_test.go
@@ -32,6 +32,33 @@ func BenchmarkHashStringSpeed(b *testing.B) {
}
}
+type chunk [17]byte
+
+func BenchmarkHashBytesSpeed(b *testing.B) {
+ // a bunch of chunks, each with a different alignment mod 16
+ var chunks [size]chunk
+ // initialize each to a different value
+ for i := 0; i < size; i++ {
+ chunks[i][0] = byte(i)
+ }
+ // put into a map
+ m := make(map[chunk]int, size)
+ for i, c := range chunks {
+ m[c] = i
+ }
+ idx := 0
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ if m[chunks[idx]] != idx {
+ b.Error("bad map entry for chunk")
+ }
+ idx++
+ if idx == size {
+ idx = 0
+ }
+ }
+}
+
func BenchmarkHashInt32Speed(b *testing.B) {
ints := make([]int32, size)
for i := 0; i < size; i++ {
@@ -206,3 +233,38 @@ func BenchmarkNewEmptyMap(b *testing.B) {
_ = make(map[int]int)
}
}
+
+func BenchmarkMapIter(b *testing.B) {
+ m := make(map[int]bool)
+ for i := 0; i < 8; i++ {
+ m[i] = true
+ }
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ for _, _ = range m {
+ }
+ }
+}
+
+func BenchmarkMapIterEmpty(b *testing.B) {
+ m := make(map[int]bool)
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ for _, _ = range m {
+ }
+ }
+}
+
+func BenchmarkSameLengthMap(b *testing.B) {
+ // long strings, same length, differ in first few
+ // and last few bytes.
+ m := make(map[string]bool)
+ s1 := "foo" + strings.Repeat("-", 100) + "bar"
+ s2 := "goo" + strings.Repeat("-", 100) + "ber"
+ m[s1] = true
+ m[s2] = true
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _ = m[s1]
+ }
+}
diff --git a/src/pkg/runtime/mcache.c b/src/pkg/runtime/mcache.c
index 64803e703..863030e74 100644
--- a/src/pkg/runtime/mcache.c
+++ b/src/pkg/runtime/mcache.c
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Per-thread (in Go, per-M) malloc cache for small objects.
+// Per-P malloc cache for small objects.
//
// See malloc.h for an overview.
@@ -10,48 +10,23 @@
#include "arch_GOARCH.h"
#include "malloc.h"
-void*
-runtime·MCache_Alloc(MCache *c, int32 sizeclass, uintptr size, int32 zeroed)
+void
+runtime·MCache_Refill(MCache *c, int32 sizeclass)
{
MCacheList *l;
- MLink *first, *v;
- int32 n;
- // Allocate from list.
+ // Replenish using central lists.
l = &c->list[sizeclass];
- if(l->list == nil) {
- // Replenish using central lists.
- n = runtime·MCentral_AllocList(&runtime·mheap->central[sizeclass],
- runtime·class_to_transfercount[sizeclass], &first);
- if(n == 0)
- runtime·throw("out of memory");
- l->list = first;
- l->nlist = n;
- c->size += n*size;
- }
- v = l->list;
- l->list = v->next;
- l->nlist--;
- if(l->nlist < l->nlistmin)
- l->nlistmin = l->nlist;
- c->size -= size;
-
- // v is zeroed except for the link pointer
- // that we used above; zero that.
- v->next = nil;
- if(zeroed) {
- // block is zeroed iff second word is zero ...
- if(size > sizeof(uintptr) && ((uintptr*)v)[1] != 0)
- runtime·memclr((byte*)v, size);
- }
- c->local_cachealloc += size;
- c->local_objects++;
- return v;
+ if(l->list)
+ runtime·throw("MCache_Refill: the list is not empty");
+ l->nlist = runtime·MCentral_AllocList(&runtime·mheap.central[sizeclass], &l->list);
+ if(l->list == nil)
+ runtime·throw("out of memory");
}
// Take n elements off l and return them to the central free list.
static void
-ReleaseN(MCache *c, MCacheList *l, int32 n, int32 sizeclass)
+ReleaseN(MCacheList *l, int32 n, int32 sizeclass)
{
MLink *first, **lp;
int32 i;
@@ -64,18 +39,14 @@ ReleaseN(MCache *c, MCacheList *l, int32 n, int32 sizeclass)
l->list = *lp;
*lp = nil;
l->nlist -= n;
- if(l->nlist < l->nlistmin)
- l->nlistmin = l->nlist;
- c->size -= n*runtime·class_to_size[sizeclass];
// Return them to central free list.
- runtime·MCentral_FreeList(&runtime·mheap->central[sizeclass], n, first);
+ runtime·MCentral_FreeList(&runtime·mheap.central[sizeclass], first);
}
void
runtime·MCache_Free(MCache *c, void *v, int32 sizeclass, uintptr size)
{
- int32 i, n;
MCacheList *l;
MLink *p;
@@ -85,34 +56,12 @@ runtime·MCache_Free(MCache *c, void *v, int32 sizeclass, uintptr size)
p->next = l->list;
l->list = p;
l->nlist++;
- c->size += size;
c->local_cachealloc -= size;
- c->local_objects--;
-
- if(l->nlist >= MaxMCacheListLen) {
- // Release a chunk back.
- ReleaseN(c, l, runtime·class_to_transfercount[sizeclass], sizeclass);
- }
-
- if(c->size >= MaxMCacheSize) {
- // Scavenge.
- for(i=0; i<NumSizeClasses; i++) {
- l = &c->list[i];
- n = l->nlistmin;
- // n is the minimum number of elements we've seen on
- // the list since the last scavenge. If n > 0, it means that
- // we could have gotten by with n fewer elements
- // without needing to consult the central free list.
- // Move toward that situation by releasing n/2 of them.
- if(n > 0) {
- if(n > 1)
- n /= 2;
- ReleaseN(c, l, n, i);
- }
- l->nlistmin = l->nlist;
- }
- }
+ // We transfer span at a time from MCentral to MCache,
+ // if we have 2 times more than that, release a half back.
+ if(l->nlist >= 2*(runtime·class_to_allocnpages[sizeclass]<<PageShift)/size)
+ ReleaseN(l, l->nlist/2, sizeclass);
}
void
@@ -123,7 +72,10 @@ runtime·MCache_ReleaseAll(MCache *c)
for(i=0; i<NumSizeClasses; i++) {
l = &c->list[i];
- ReleaseN(c, l, l->nlist, i);
- l->nlistmin = 0;
+ if(l->list) {
+ runtime·MCentral_FreeList(&runtime·mheap.central[i], l->list);
+ l->list = nil;
+ l->nlist = 0;
+ }
}
}
diff --git a/src/pkg/runtime/mcentral.c b/src/pkg/runtime/mcentral.c
index ec2a91ad5..735a7e6a9 100644
--- a/src/pkg/runtime/mcentral.c
+++ b/src/pkg/runtime/mcentral.c
@@ -30,16 +30,15 @@ runtime·MCentral_Init(MCentral *c, int32 sizeclass)
runtime·MSpanList_Init(&c->empty);
}
-// Allocate up to n objects from the central free list.
+// Allocate a list of objects from the central free list.
// Return the number of objects allocated.
// The objects are linked together by their first words.
-// On return, *pstart points at the first object.
+// On return, *pfirst points at the first object.
int32
-runtime·MCentral_AllocList(MCentral *c, int32 n, MLink **pfirst)
+runtime·MCentral_AllocList(MCentral *c, MLink **pfirst)
{
MSpan *s;
- MLink *first, *last;
- int32 cap, avail, i;
+ int32 cap, n;
runtime·lock(c);
// Replenish central list if empty.
@@ -52,49 +51,27 @@ runtime·MCentral_AllocList(MCentral *c, int32 n, MLink **pfirst)
}
s = c->nonempty.next;
cap = (s->npages << PageShift) / s->elemsize;
- avail = cap - s->ref;
- if(avail < n)
- n = avail;
-
- // First one is guaranteed to work, because we just grew the list.
- first = s->freelist;
- last = first;
- for(i=1; i<n; i++) {
- last = last->next;
- }
- s->freelist = last->next;
- last->next = nil;
+ n = cap - s->ref;
+ *pfirst = s->freelist;
+ s->freelist = nil;
s->ref += n;
c->nfree -= n;
-
- if(n == avail) {
- if(s->freelist != nil || s->ref != cap) {
- runtime·throw("invalid freelist");
- }
- runtime·MSpanList_Remove(s);
- runtime·MSpanList_Insert(&c->empty, s);
- }
-
+ runtime·MSpanList_Remove(s);
+ runtime·MSpanList_Insert(&c->empty, s);
runtime·unlock(c);
- *pfirst = first;
return n;
}
-// Free n objects back into the central free list.
+// Free the list of objects back into the central free list.
void
-runtime·MCentral_FreeList(MCentral *c, int32 n, MLink *start)
+runtime·MCentral_FreeList(MCentral *c, MLink *start)
{
- MLink *v, *next;
-
- // Assume next == nil marks end of list.
- // n and end would be useful if we implemented
- // the transfer cache optimization in the TODO above.
- USED(n);
+ MLink *next;
runtime·lock(c);
- for(v=start; v; v=next) {
- next = v->next;
- MCentral_Free(c, v);
+ for(; start != nil; start = next) {
+ next = start->next;
+ MCentral_Free(c, start);
}
runtime·unlock(c);
}
@@ -108,7 +85,7 @@ MCentral_Free(MCentral *c, void *v)
int32 size;
// Find span for v.
- s = runtime·MHeap_Lookup(runtime·mheap, v);
+ s = runtime·MHeap_Lookup(&runtime·mheap, v);
if(s == nil || s->ref == 0)
runtime·throw("invalid free");
@@ -133,7 +110,7 @@ MCentral_Free(MCentral *c, void *v)
s->freelist = nil;
c->nfree -= (s->npages << PageShift) / size;
runtime·unlock(c);
- runtime·MHeap_Free(runtime·mheap, s, 0);
+ runtime·MHeap_Free(&runtime·mheap, s, 0);
runtime·lock(c);
}
}
@@ -168,7 +145,7 @@ runtime·MCentral_FreeSpan(MCentral *c, MSpan *s, int32 n, MLink *start, MLink *
c->nfree -= (s->npages << PageShift) / size;
runtime·unlock(c);
runtime·unmarkspan((byte*)(s->start<<PageShift), s->npages<<PageShift);
- runtime·MHeap_Free(runtime·mheap, s, 0);
+ runtime·MHeap_Free(&runtime·mheap, s, 0);
} else {
runtime·unlock(c);
}
@@ -200,7 +177,7 @@ MCentral_Grow(MCentral *c)
runtime·unlock(c);
runtime·MGetSizeClassInfo(c->sizeclass, &size, &npages, &n);
- s = runtime·MHeap_Alloc(runtime·mheap, npages, c->sizeclass, 0, 1);
+ s = runtime·MHeap_Alloc(&runtime·mheap, npages, c->sizeclass, 0, 1);
if(s == nil) {
// TODO(rsc): Log out of memory
runtime·lock(c);
diff --git a/src/pkg/runtime/mem.go b/src/pkg/runtime/mem.go
index 79edc5a60..dc735e4a6 100644
--- a/src/pkg/runtime/mem.go
+++ b/src/pkg/runtime/mem.go
@@ -14,7 +14,7 @@ type MemStats struct {
// General statistics.
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)
+ Sys uint64 // bytes obtained from system (sum of XxxSys below)
Lookups uint64 // number of pointer lookups
Mallocs uint64 // number of mallocs
Frees uint64 // number of frees
@@ -37,6 +37,8 @@ type MemStats struct {
MCacheInuse uint64 // mcache structures
MCacheSys uint64
BuckHashSys uint64 // profiling bucket hash table
+ GCSys uint64 // GC metadata
+ OtherSys uint64 // other system allocations
// Garbage collector statistics.
NextGC uint64 // next run in HeapAlloc time (bytes)
diff --git a/src/pkg/runtime/mem_darwin.c b/src/pkg/runtime/mem_darwin.c
index 7aa607f8e..a75c46d9d 100644
--- a/src/pkg/runtime/mem_darwin.c
+++ b/src/pkg/runtime/mem_darwin.c
@@ -9,14 +9,14 @@
#include "malloc.h"
void*
-runtime·SysAlloc(uintptr n)
+runtime·SysAlloc(uintptr n, uint64 *stat)
{
void *v;
- mstats.sys += n;
v = runtime·mmap(nil, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0);
if(v < (void*)4096)
return nil;
+ runtime·xadd64(stat, n);
return v;
}
@@ -28,9 +28,16 @@ runtime·SysUnused(void *v, uintptr n)
}
void
-runtime·SysFree(void *v, uintptr n)
+runtime·SysUsed(void *v, uintptr n)
{
- mstats.sys -= n;
+ USED(v);
+ USED(n);
+}
+
+void
+runtime·SysFree(void *v, uintptr n, uint64 *stat)
+{
+ runtime·xadd64(stat, -(uint64)n);
runtime·munmap(v, n);
}
@@ -51,11 +58,11 @@ enum
};
void
-runtime·SysMap(void *v, uintptr n)
+runtime·SysMap(void *v, uintptr n, uint64 *stat)
{
void *p;
- mstats.sys += n;
+ runtime·xadd64(stat, n);
p = runtime·mmap(v, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0);
if(p == (void*)ENOMEM)
runtime·throw("runtime: out of memory");
diff --git a/src/pkg/runtime/mem_dragonfly.c b/src/pkg/runtime/mem_dragonfly.c
new file mode 100644
index 000000000..025b62ea6
--- /dev/null
+++ b/src/pkg/runtime/mem_dragonfly.c
@@ -0,0 +1,94 @@
+// 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 "runtime.h"
+#include "arch_GOARCH.h"
+#include "defs_GOOS_GOARCH.h"
+#include "os_GOOS.h"
+#include "malloc.h"
+
+enum
+{
+ ENOMEM = 12,
+};
+
+void*
+runtime·SysAlloc(uintptr n, uint64 *stat)
+{
+ void *v;
+
+ v = runtime·mmap(nil, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0);
+ if(v < (void*)4096)
+ return nil;
+ runtime·xadd64(stat, n);
+ return v;
+}
+
+void
+runtime·SysUnused(void *v, uintptr n)
+{
+ runtime·madvise(v, n, MADV_FREE);
+}
+
+void
+runtime·SysUsed(void *v, uintptr n)
+{
+ USED(v);
+ USED(n);
+}
+
+void
+runtime·SysFree(void *v, uintptr n, uint64 *stat)
+{
+ runtime·xadd64(stat, -(uint64)n);
+ runtime·munmap(v, n);
+}
+
+void*
+runtime·SysReserve(void *v, uintptr n)
+{
+ void *p;
+
+ // On 64-bit, people with ulimit -v set complain if we reserve too
+ // much address space. Instead, assume that the reservation is okay
+ // and check the assumption in SysMap.
+ if(sizeof(void*) == 8)
+ return v;
+
+ p = runtime·mmap(v, n, PROT_NONE, MAP_ANON|MAP_PRIVATE, -1, 0);
+ if(p < (void*)4096)
+ return nil;
+ return p;
+}
+
+void
+runtime·SysMap(void *v, uintptr n, uint64 *stat)
+{
+ void *p;
+
+ runtime·xadd64(stat, n);
+
+ // On 64-bit, we don't actually have v reserved, so tread carefully.
+ if(sizeof(void*) == 8) {
+ // TODO(jsing): For some reason DragonFly seems to return
+ // memory at a different address than we requested, even when
+ // there should be no reason for it to do so. This can be
+ // avoided by using MAP_FIXED, but I'm not sure we should need
+ // to do this - we do not on other platforms.
+ p = runtime·mmap(v, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0);
+ if(p == (void*)ENOMEM)
+ runtime·throw("runtime: out of memory");
+ if(p != v) {
+ runtime·printf("runtime: address space conflict: map(%p) = %p\n", v, p);
+ runtime·throw("runtime: address space conflict");
+ }
+ return;
+ }
+
+ p = runtime·mmap(v, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0);
+ if(p == (void*)ENOMEM)
+ runtime·throw("runtime: out of memory");
+ if(p != v)
+ runtime·throw("runtime: cannot map pages in arena address space");
+}
diff --git a/src/pkg/runtime/mem_freebsd.c b/src/pkg/runtime/mem_freebsd.c
index 805e74cff..1ee2a555e 100644
--- a/src/pkg/runtime/mem_freebsd.c
+++ b/src/pkg/runtime/mem_freebsd.c
@@ -14,14 +14,14 @@ enum
};
void*
-runtime·SysAlloc(uintptr n)
+runtime·SysAlloc(uintptr n, uint64 *stat)
{
void *v;
- mstats.sys += n;
v = runtime·mmap(nil, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0);
if(v < (void*)4096)
return nil;
+ runtime·xadd64(stat, n);
return v;
}
@@ -32,9 +32,16 @@ runtime·SysUnused(void *v, uintptr n)
}
void
-runtime·SysFree(void *v, uintptr n)
+runtime·SysUsed(void *v, uintptr n)
{
- mstats.sys -= n;
+ USED(v);
+ USED(n);
+}
+
+void
+runtime·SysFree(void *v, uintptr n, uint64 *stat)
+{
+ runtime·xadd64(stat, -(uint64)n);
runtime·munmap(v, n);
}
@@ -56,11 +63,11 @@ runtime·SysReserve(void *v, uintptr n)
}
void
-runtime·SysMap(void *v, uintptr n)
+runtime·SysMap(void *v, uintptr n, uint64 *stat)
{
void *p;
- mstats.sys += n;
+ runtime·xadd64(stat, n);
// On 64-bit, we don't actually have v reserved, so tread carefully.
if(sizeof(void*) == 8) {
diff --git a/src/pkg/runtime/mem_linux.c b/src/pkg/runtime/mem_linux.c
index 1bae755fa..b0f295633 100644
--- a/src/pkg/runtime/mem_linux.c
+++ b/src/pkg/runtime/mem_linux.c
@@ -50,11 +50,10 @@ mmap_fixed(byte *v, uintptr n, int32 prot, int32 flags, int32 fd, uint32 offset)
}
void*
-runtime·SysAlloc(uintptr n)
+runtime·SysAlloc(uintptr n, uint64 *stat)
{
void *p;
- mstats.sys += n;
p = runtime·mmap(nil, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0);
if(p < (void*)4096) {
if(p == (void*)EACCES) {
@@ -68,6 +67,7 @@ runtime·SysAlloc(uintptr n)
}
return nil;
}
+ runtime·xadd64(stat, n);
return p;
}
@@ -78,9 +78,16 @@ runtime·SysUnused(void *v, uintptr n)
}
void
-runtime·SysFree(void *v, uintptr n)
+runtime·SysUsed(void *v, uintptr n)
{
- mstats.sys -= n;
+ USED(v);
+ USED(n);
+}
+
+void
+runtime·SysFree(void *v, uintptr n, uint64 *stat)
+{
+ runtime·xadd64(stat, -(uint64)n);
runtime·munmap(v, n);
}
@@ -95,24 +102,27 @@ runtime·SysReserve(void *v, uintptr n)
// Only user-mode Linux (UML) rejects these requests.
if(sizeof(void*) == 8 && (uintptr)v >= 0xffffffffU) {
p = mmap_fixed(v, 64<<10, PROT_NONE, MAP_ANON|MAP_PRIVATE, -1, 0);
- if (p != v)
+ if (p != v) {
+ if(p >= (void*)4096)
+ runtime·munmap(p, 64<<10);
return nil;
+ }
runtime·munmap(p, 64<<10);
return v;
}
-
+
p = runtime·mmap(v, n, PROT_NONE, MAP_ANON|MAP_PRIVATE, -1, 0);
- if((uintptr)p < 4096 || -(uintptr)p < 4096)
+ if((uintptr)p < 4096)
return nil;
return p;
}
void
-runtime·SysMap(void *v, uintptr n)
+runtime·SysMap(void *v, uintptr n, uint64 *stat)
{
void *p;
- mstats.sys += n;
+ runtime·xadd64(stat, n);
// On 64-bit, we don't actually have v reserved, so tread carefully.
if(sizeof(void*) == 8 && (uintptr)v >= 0xffffffffU) {
diff --git a/src/pkg/runtime/mem_netbsd.c b/src/pkg/runtime/mem_netbsd.c
index e5bdac0ef..91e36eb60 100644
--- a/src/pkg/runtime/mem_netbsd.c
+++ b/src/pkg/runtime/mem_netbsd.c
@@ -14,14 +14,14 @@ enum
};
void*
-runtime·SysAlloc(uintptr n)
+runtime·SysAlloc(uintptr n, uint64 *stat)
{
void *v;
- mstats.sys += n;
v = runtime·mmap(nil, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0);
if(v < (void*)4096)
return nil;
+ runtime·xadd64(stat, n);
return v;
}
@@ -32,9 +32,16 @@ runtime·SysUnused(void *v, uintptr n)
}
void
-runtime·SysFree(void *v, uintptr n)
+runtime·SysUsed(void *v, uintptr n)
{
- mstats.sys -= n;
+ USED(v);
+ USED(n);
+}
+
+void
+runtime·SysFree(void *v, uintptr n, uint64 *stat)
+{
+ runtime·xadd64(stat, -(uint64)n);
runtime·munmap(v, n);
}
@@ -56,11 +63,11 @@ runtime·SysReserve(void *v, uintptr n)
}
void
-runtime·SysMap(void *v, uintptr n)
+runtime·SysMap(void *v, uintptr n, uint64 *stat)
{
void *p;
- mstats.sys += n;
+ runtime·xadd64(stat, n);
// On 64-bit, we don't actually have v reserved, so tread carefully.
if(sizeof(void*) == 8) {
diff --git a/src/pkg/runtime/mem_openbsd.c b/src/pkg/runtime/mem_openbsd.c
index e5bdac0ef..91e36eb60 100644
--- a/src/pkg/runtime/mem_openbsd.c
+++ b/src/pkg/runtime/mem_openbsd.c
@@ -14,14 +14,14 @@ enum
};
void*
-runtime·SysAlloc(uintptr n)
+runtime·SysAlloc(uintptr n, uint64 *stat)
{
void *v;
- mstats.sys += n;
v = runtime·mmap(nil, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0);
if(v < (void*)4096)
return nil;
+ runtime·xadd64(stat, n);
return v;
}
@@ -32,9 +32,16 @@ runtime·SysUnused(void *v, uintptr n)
}
void
-runtime·SysFree(void *v, uintptr n)
+runtime·SysUsed(void *v, uintptr n)
{
- mstats.sys -= n;
+ USED(v);
+ USED(n);
+}
+
+void
+runtime·SysFree(void *v, uintptr n, uint64 *stat)
+{
+ runtime·xadd64(stat, -(uint64)n);
runtime·munmap(v, n);
}
@@ -56,11 +63,11 @@ runtime·SysReserve(void *v, uintptr n)
}
void
-runtime·SysMap(void *v, uintptr n)
+runtime·SysMap(void *v, uintptr n, uint64 *stat)
{
void *p;
- mstats.sys += n;
+ runtime·xadd64(stat, n);
// On 64-bit, we don't actually have v reserved, so tread carefully.
if(sizeof(void*) == 8) {
diff --git a/src/pkg/runtime/mem_plan9.c b/src/pkg/runtime/mem_plan9.c
index 26ca367f1..edf970b2f 100644
--- a/src/pkg/runtime/mem_plan9.c
+++ b/src/pkg/runtime/mem_plan9.c
@@ -18,12 +18,11 @@ enum
};
void*
-runtime·SysAlloc(uintptr nbytes)
+runtime·SysAlloc(uintptr nbytes, uint64 *stat)
{
uintptr bl;
runtime·lock(&memlock);
- mstats.sys += nbytes;
// Plan 9 sbrk from /sys/src/libc/9sys/sbrk.c
bl = ((uintptr)bloc + Round) & ~Round;
if(runtime·brk_((void*)(bl + nbytes)) < 0) {
@@ -32,20 +31,21 @@ runtime·SysAlloc(uintptr nbytes)
}
bloc = (byte*)bl + nbytes;
runtime·unlock(&memlock);
+ runtime·xadd64(stat, nbytes);
return (void*)bl;
}
void
-runtime·SysFree(void *v, uintptr nbytes)
+runtime·SysFree(void *v, uintptr nbytes, uint64 *stat)
{
+ runtime·xadd64(stat, -(uint64)nbytes);
runtime·lock(&memlock);
- mstats.sys -= nbytes;
// from tiny/mem.c
// Push pointer back if this is a free
// of the most recent SysAlloc.
nbytes += (nbytes + Round) & ~Round;
if(bloc == (byte*)v+nbytes)
- bloc -= nbytes;
+ bloc -= nbytes;
runtime·unlock(&memlock);
}
@@ -56,14 +56,20 @@ runtime·SysUnused(void *v, uintptr nbytes)
}
void
-runtime·SysMap(void *v, uintptr nbytes)
+runtime·SysUsed(void *v, uintptr nbytes)
{
USED(v, nbytes);
}
+void
+runtime·SysMap(void *v, uintptr nbytes, uint64 *stat)
+{
+ USED(v, nbytes, stat);
+}
+
void*
runtime·SysReserve(void *v, uintptr nbytes)
{
USED(v);
- return runtime·SysAlloc(nbytes);
+ return runtime·SysAlloc(nbytes, &mstats.heap_sys);
}
diff --git a/src/pkg/runtime/mem_windows.c b/src/pkg/runtime/mem_windows.c
index 7840daa22..abdc72ad8 100644
--- a/src/pkg/runtime/mem_windows.c
+++ b/src/pkg/runtime/mem_windows.c
@@ -11,9 +11,10 @@
enum {
MEM_COMMIT = 0x1000,
MEM_RESERVE = 0x2000,
+ MEM_DECOMMIT = 0x4000,
MEM_RELEASE = 0x8000,
- PAGE_EXECUTE_READWRITE = 0x40,
+ PAGE_READWRITE = 0x0004,
};
#pragma dynimport runtime·VirtualAlloc VirtualAlloc "kernel32.dll"
@@ -22,25 +23,38 @@ extern void *runtime·VirtualAlloc;
extern void *runtime·VirtualFree;
void*
-runtime·SysAlloc(uintptr n)
+runtime·SysAlloc(uintptr n, uint64 *stat)
{
- mstats.sys += n;
- return runtime·stdcall(runtime·VirtualAlloc, 4, nil, n, (uintptr)(MEM_COMMIT|MEM_RESERVE), (uintptr)PAGE_EXECUTE_READWRITE);
+ runtime·xadd64(stat, n);
+ return runtime·stdcall(runtime·VirtualAlloc, 4, nil, n, (uintptr)(MEM_COMMIT|MEM_RESERVE), (uintptr)PAGE_READWRITE);
}
void
runtime·SysUnused(void *v, uintptr n)
{
- USED(v);
- USED(n);
+ void *r;
+
+ r = runtime·stdcall(runtime·VirtualFree, 3, v, n, (uintptr)MEM_DECOMMIT);
+ if(r == nil)
+ runtime·throw("runtime: failed to decommit pages");
+}
+
+void
+runtime·SysUsed(void *v, uintptr n)
+{
+ void *r;
+
+ r = runtime·stdcall(runtime·VirtualAlloc, 4, v, n, (uintptr)MEM_COMMIT, (uintptr)PAGE_READWRITE);
+ if(r != v)
+ runtime·throw("runtime: failed to commit pages");
}
void
-runtime·SysFree(void *v, uintptr n)
+runtime·SysFree(void *v, uintptr n, uint64 *stat)
{
uintptr r;
- mstats.sys -= n;
+ runtime·xadd64(stat, -(uint64)n);
r = (uintptr)runtime·stdcall(runtime·VirtualFree, 3, v, (uintptr)0, (uintptr)MEM_RELEASE);
if(r == 0)
runtime·throw("runtime: failed to release pages");
@@ -51,21 +65,21 @@ runtime·SysReserve(void *v, uintptr n)
{
// v is just a hint.
// First try at v.
- v = runtime·stdcall(runtime·VirtualAlloc, 4, v, n, (uintptr)MEM_RESERVE, (uintptr)PAGE_EXECUTE_READWRITE);
+ v = runtime·stdcall(runtime·VirtualAlloc, 4, v, n, (uintptr)MEM_RESERVE, (uintptr)PAGE_READWRITE);
if(v != nil)
return v;
// Next let the kernel choose the address.
- return runtime·stdcall(runtime·VirtualAlloc, 4, nil, n, (uintptr)MEM_RESERVE, (uintptr)PAGE_EXECUTE_READWRITE);
+ return runtime·stdcall(runtime·VirtualAlloc, 4, nil, n, (uintptr)MEM_RESERVE, (uintptr)PAGE_READWRITE);
}
void
-runtime·SysMap(void *v, uintptr n)
+runtime·SysMap(void *v, uintptr n, uint64 *stat)
{
void *p;
- mstats.sys += n;
- p = runtime·stdcall(runtime·VirtualAlloc, 4, v, n, (uintptr)MEM_COMMIT, (uintptr)PAGE_EXECUTE_READWRITE);
+ runtime·xadd64(stat, n);
+ p = runtime·stdcall(runtime·VirtualAlloc, 4, v, n, (uintptr)MEM_COMMIT, (uintptr)PAGE_READWRITE);
if(p != v)
runtime·throw("runtime: cannot map pages in arena address space");
}
diff --git a/src/pkg/runtime/memclr_arm.s b/src/pkg/runtime/memclr_arm.s
index afc529d90..d5ff75d7a 100644
--- a/src/pkg/runtime/memclr_arm.s
+++ b/src/pkg/runtime/memclr_arm.s
@@ -23,12 +23,14 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#include "../../cmd/ld/textflag.h"
+
TO = 8
TOE = 11
N = 12
TMP = 12 /* N and TMP don't overlap */
-TEXT runtime·memclr(SB),7,$0
+TEXT runtime·memclr(SB),NOSPLIT,$0-8
MOVW ptr+0(FP), R(TO)
MOVW n+4(FP), R(N)
MOVW $0, R(0)
diff --git a/src/pkg/runtime/memmove_386.s b/src/pkg/runtime/memmove_386.s
index 203a8187c..13d575973 100644
--- a/src/pkg/runtime/memmove_386.s
+++ b/src/pkg/runtime/memmove_386.s
@@ -23,10 +23,40 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-TEXT runtime·memmove(SB), 7, $0
+#include "../../cmd/ld/textflag.h"
+
+TEXT runtime·memmove(SB), NOSPLIT, $0-12
MOVL to+0(FP), DI
MOVL fr+4(FP), SI
MOVL n+8(FP), BX
+
+ // REP instructions have a high startup cost, so we handle small sizes
+ // with some straightline code. The REP MOVSL instruction is really fast
+ // for large sizes. The cutover is approximately 1K. We implement up to
+ // 128 because that is the maximum SSE register load (loading all data
+ // into registers lets us ignore copy direction).
+tail:
+ TESTL BX, BX
+ JEQ move_0
+ CMPL BX, $2
+ JBE move_1or2
+ CMPL BX, $4
+ JBE move_3or4
+ CMPL BX, $8
+ JBE move_5through8
+ CMPL BX, $16
+ JBE move_9through16
+ TESTL $0x4000000, runtime·cpuid_edx(SB) // check for sse2
+ JEQ nosse2
+ CMPL BX, $32
+ JBE move_17through32
+ CMPL BX, $64
+ JBE move_33through64
+ CMPL BX, $128
+ JBE move_65through128
+ // TODO: use branch table and BSR to make this just a single dispatch
+
+nosse2:
/*
* check and set for backwards
*/
@@ -42,11 +72,7 @@ forward:
ANDL $3, BX
REP; MOVSL
- MOVL BX, CX
- REP; MOVSB
-
- MOVL to+0(FP),AX
- RET
+ JMP tail
/*
* check overlap
*/
@@ -75,12 +101,73 @@ back:
SUBL $4, SI
REP; MOVSL
- ADDL $3, DI
- ADDL $3, SI
- MOVL BX, CX
- REP; MOVSB
-
CLD
- MOVL to+0(FP),AX
- RET
+ ADDL $4, DI
+ ADDL $4, SI
+ SUBL BX, DI
+ SUBL BX, SI
+ JMP tail
+move_1or2:
+ MOVB (SI), AX
+ MOVB -1(SI)(BX*1), CX
+ MOVB AX, (DI)
+ MOVB CX, -1(DI)(BX*1)
+move_0:
+ RET
+move_3or4:
+ MOVW (SI), AX
+ MOVW -2(SI)(BX*1), CX
+ MOVW AX, (DI)
+ MOVW CX, -2(DI)(BX*1)
+ RET
+move_5through8:
+ MOVL (SI), AX
+ MOVL -4(SI)(BX*1), CX
+ MOVL AX, (DI)
+ MOVL CX, -4(DI)(BX*1)
+ RET
+move_9through16:
+ MOVL (SI), AX
+ MOVL 4(SI), CX
+ MOVL -8(SI)(BX*1), DX
+ MOVL -4(SI)(BX*1), BP
+ MOVL AX, (DI)
+ MOVL CX, 4(DI)
+ MOVL DX, -8(DI)(BX*1)
+ MOVL BP, -4(DI)(BX*1)
+ RET
+move_17through32:
+ MOVOU (SI), X0
+ MOVOU -16(SI)(BX*1), X1
+ MOVOU X0, (DI)
+ MOVOU X1, -16(DI)(BX*1)
+ RET
+move_33through64:
+ MOVOU (SI), X0
+ MOVOU 16(SI), X1
+ MOVOU -32(SI)(BX*1), X2
+ MOVOU -16(SI)(BX*1), X3
+ MOVOU X0, (DI)
+ MOVOU X1, 16(DI)
+ MOVOU X2, -32(DI)(BX*1)
+ MOVOU X3, -16(DI)(BX*1)
+ RET
+move_65through128:
+ MOVOU (SI), X0
+ MOVOU 16(SI), X1
+ MOVOU 32(SI), X2
+ MOVOU 48(SI), X3
+ MOVOU -64(SI)(BX*1), X4
+ MOVOU -48(SI)(BX*1), X5
+ MOVOU -32(SI)(BX*1), X6
+ MOVOU -16(SI)(BX*1), X7
+ MOVOU X0, (DI)
+ MOVOU X1, 16(DI)
+ MOVOU X2, 32(DI)
+ MOVOU X3, 48(DI)
+ MOVOU X4, -64(DI)(BX*1)
+ MOVOU X5, -48(DI)(BX*1)
+ MOVOU X6, -32(DI)(BX*1)
+ MOVOU X7, -16(DI)(BX*1)
+ RET
diff --git a/src/pkg/runtime/memmove_amd64.s b/src/pkg/runtime/memmove_amd64.s
index 6174407e3..f1641cdb2 100644
--- a/src/pkg/runtime/memmove_amd64.s
+++ b/src/pkg/runtime/memmove_amd64.s
@@ -23,13 +23,41 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#include "../../cmd/ld/textflag.h"
+
// void runtime·memmove(void*, void*, uintptr)
-TEXT runtime·memmove(SB), 7, $0
+TEXT runtime·memmove(SB), NOSPLIT, $0-24
MOVQ to+0(FP), DI
MOVQ fr+8(FP), SI
MOVQ n+16(FP), BX
+ // REP instructions have a high startup cost, so we handle small sizes
+ // with some straightline code. The REP MOVSQ instruction is really fast
+ // for large sizes. The cutover is approximately 1K. We implement up to
+ // 256 because that is the maximum SSE register load (loading all data
+ // into registers lets us ignore copy direction).
+tail:
+ TESTQ BX, BX
+ JEQ move_0
+ CMPQ BX, $2
+ JBE move_1or2
+ CMPQ BX, $4
+ JBE move_3or4
+ CMPQ BX, $8
+ JBE move_5through8
+ CMPQ BX, $16
+ JBE move_9through16
+ CMPQ BX, $32
+ JBE move_17through32
+ CMPQ BX, $64
+ JBE move_33through64
+ CMPQ BX, $128
+ JBE move_65through128
+ CMPQ BX, $256
+ JBE move_129through256
+ // TODO: use branch table and BSR to make this just a single dispatch
+
/*
* check and set for backwards
*/
@@ -45,11 +73,8 @@ forward:
ANDQ $7, BX
REP; MOVSQ
- MOVQ BX, CX
- REP; MOVSB
+ JMP tail
- MOVQ to+0(FP),AX
- RET
back:
/*
* check overlap
@@ -78,12 +103,103 @@ back:
SUBQ $8, SI
REP; MOVSQ
- ADDQ $7, DI
- ADDQ $7, SI
- MOVQ BX, CX
- REP; MOVSB
-
CLD
- MOVQ to+0(FP),AX
- RET
+ ADDQ $8, DI
+ ADDQ $8, SI
+ SUBQ BX, DI
+ SUBQ BX, SI
+ JMP tail
+move_1or2:
+ MOVB (SI), AX
+ MOVB -1(SI)(BX*1), CX
+ MOVB AX, (DI)
+ MOVB CX, -1(DI)(BX*1)
+move_0:
+ RET
+move_3or4:
+ MOVW (SI), AX
+ MOVW -2(SI)(BX*1), CX
+ MOVW AX, (DI)
+ MOVW CX, -2(DI)(BX*1)
+ RET
+move_5through8:
+ MOVL (SI), AX
+ MOVL -4(SI)(BX*1), CX
+ MOVL AX, (DI)
+ MOVL CX, -4(DI)(BX*1)
+ RET
+move_9through16:
+ MOVQ (SI), AX
+ MOVQ -8(SI)(BX*1), CX
+ MOVQ AX, (DI)
+ MOVQ CX, -8(DI)(BX*1)
+ RET
+move_17through32:
+ MOVOU (SI), X0
+ MOVOU -16(SI)(BX*1), X1
+ MOVOU X0, (DI)
+ MOVOU X1, -16(DI)(BX*1)
+ RET
+move_33through64:
+ MOVOU (SI), X0
+ MOVOU 16(SI), X1
+ MOVOU -32(SI)(BX*1), X2
+ MOVOU -16(SI)(BX*1), X3
+ MOVOU X0, (DI)
+ MOVOU X1, 16(DI)
+ MOVOU X2, -32(DI)(BX*1)
+ MOVOU X3, -16(DI)(BX*1)
+ RET
+move_65through128:
+ MOVOU (SI), X0
+ MOVOU 16(SI), X1
+ MOVOU 32(SI), X2
+ MOVOU 48(SI), X3
+ MOVOU -64(SI)(BX*1), X4
+ MOVOU -48(SI)(BX*1), X5
+ MOVOU -32(SI)(BX*1), X6
+ MOVOU -16(SI)(BX*1), X7
+ MOVOU X0, (DI)
+ MOVOU X1, 16(DI)
+ MOVOU X2, 32(DI)
+ MOVOU X3, 48(DI)
+ MOVOU X4, -64(DI)(BX*1)
+ MOVOU X5, -48(DI)(BX*1)
+ MOVOU X6, -32(DI)(BX*1)
+ MOVOU X7, -16(DI)(BX*1)
+ RET
+move_129through256:
+ MOVOU (SI), X0
+ MOVOU 16(SI), X1
+ MOVOU 32(SI), X2
+ MOVOU 48(SI), X3
+ MOVOU 64(SI), X4
+ MOVOU 80(SI), X5
+ MOVOU 96(SI), X6
+ MOVOU 112(SI), X7
+ MOVOU -128(SI)(BX*1), X8
+ MOVOU -112(SI)(BX*1), X9
+ MOVOU -96(SI)(BX*1), X10
+ MOVOU -80(SI)(BX*1), X11
+ MOVOU -64(SI)(BX*1), X12
+ MOVOU -48(SI)(BX*1), X13
+ MOVOU -32(SI)(BX*1), X14
+ MOVOU -16(SI)(BX*1), X15
+ MOVOU X0, (DI)
+ MOVOU X1, 16(DI)
+ MOVOU X2, 32(DI)
+ MOVOU X3, 48(DI)
+ MOVOU X4, 64(DI)
+ MOVOU X5, 80(DI)
+ MOVOU X6, 96(DI)
+ MOVOU X7, 112(DI)
+ MOVOU X8, -128(DI)(BX*1)
+ MOVOU X9, -112(DI)(BX*1)
+ MOVOU X10, -96(DI)(BX*1)
+ MOVOU X11, -80(DI)(BX*1)
+ MOVOU X12, -64(DI)(BX*1)
+ MOVOU X13, -48(DI)(BX*1)
+ MOVOU X14, -32(DI)(BX*1)
+ MOVOU X15, -16(DI)(BX*1)
+ RET
diff --git a/src/pkg/runtime/memmove_arm.s b/src/pkg/runtime/memmove_arm.s
index c5d7e9d70..9701dc99f 100644
--- a/src/pkg/runtime/memmove_arm.s
+++ b/src/pkg/runtime/memmove_arm.s
@@ -23,6 +23,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#include "../../cmd/ld/textflag.h"
+
// TE or TS are spilled to the stack during bulk register moves.
TS = 0
TE = 8
@@ -56,7 +58,7 @@ FR2 = 4
FW3 = 4
FR3 = 8 /* shared with TE */
-TEXT runtime·memmove(SB), 7, $4
+TEXT runtime·memmove(SB), NOSPLIT, $4-12
_memmove:
MOVW to+0(FP), R(TS)
MOVW from+4(FP), R(FROM)
@@ -85,7 +87,7 @@ _b4aligned: /* is source now aligned? */
BNE _bunaligned
ADD $31, R(TS), R(TMP) /* do 32-byte chunks if possible */
- MOVW R(TS), savedts+4(SP)
+ MOVW R(TS), savedts-4(SP)
_b32loop:
CMP R(TMP), R(TE)
BLS _b4tail
@@ -95,7 +97,7 @@ _b32loop:
B _b32loop
_b4tail: /* do remaining words if possible */
- MOVW savedts+4(SP), R(TS)
+ MOVW savedts-4(SP), R(TS)
ADD $3, R(TS), R(TMP)
_b4loop:
CMP R(TMP), R(TE)
@@ -130,7 +132,7 @@ _f4aligned: /* is source now aligned? */
BNE _funaligned
SUB $31, R(TE), R(TMP) /* do 32-byte chunks if possible */
- MOVW R(TE), savedte+4(SP)
+ MOVW R(TE), savedte-4(SP)
_f32loop:
CMP R(TMP), R(TS)
BHS _f4tail
@@ -140,7 +142,7 @@ _f32loop:
B _f32loop
_f4tail:
- MOVW savedte+4(SP), R(TE)
+ MOVW savedte-4(SP), R(TE)
SUB $3, R(TE), R(TMP) /* do remaining words if possible */
_f4loop:
CMP R(TMP), R(TS)
@@ -182,7 +184,7 @@ _bunaligned:
BLS _b1tail
BIC $3, R(FROM) /* align source */
- MOVW R(TS), savedts+4(SP)
+ MOVW R(TS), savedts-4(SP)
MOVW (R(FROM)), R(BR0) /* prime first block register */
_bu16loop:
@@ -206,7 +208,7 @@ _bu16loop:
B _bu16loop
_bu1tail:
- MOVW savedts+4(SP), R(TS)
+ MOVW savedts-4(SP), R(TS)
ADD R(OFFSET), R(FROM)
B _b1tail
@@ -230,7 +232,7 @@ _funaligned:
BHS _f1tail
BIC $3, R(FROM) /* align source */
- MOVW R(TE), savedte+4(SP)
+ MOVW R(TE), savedte-4(SP)
MOVW.P 4(R(FROM)), R(FR3) /* prime last block register, implicit write back */
_fu16loop:
@@ -254,6 +256,6 @@ _fu16loop:
B _fu16loop
_fu1tail:
- MOVW savedte+4(SP), R(TE)
+ MOVW savedte-4(SP), R(TE)
SUB R(OFFSET), R(FROM)
B _f1tail
diff --git a/src/pkg/runtime/memmove_test.go b/src/pkg/runtime/memmove_test.go
new file mode 100644
index 000000000..9525f0682
--- /dev/null
+++ b/src/pkg/runtime/memmove_test.go
@@ -0,0 +1,116 @@
+// Copyright 2013 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_test
+
+import (
+ "testing"
+)
+
+func TestMemmove(t *testing.T) {
+ size := 256
+ if testing.Short() {
+ size = 128 + 16
+ }
+ src := make([]byte, size)
+ dst := make([]byte, size)
+ for i := 0; i < size; i++ {
+ src[i] = byte(128 + (i & 127))
+ }
+ for i := 0; i < size; i++ {
+ dst[i] = byte(i & 127)
+ }
+ for n := 0; n <= size; n++ {
+ for x := 0; x <= size-n; x++ { // offset in src
+ for y := 0; y <= size-n; y++ { // offset in dst
+ copy(dst[y:y+n], src[x:x+n])
+ for i := 0; i < y; i++ {
+ if dst[i] != byte(i&127) {
+ t.Fatalf("prefix dst[%d] = %d", i, dst[i])
+ }
+ }
+ for i := y; i < y+n; i++ {
+ if dst[i] != byte(128+((i-y+x)&127)) {
+ t.Fatalf("copied dst[%d] = %d", i, dst[i])
+ }
+ dst[i] = byte(i & 127) // reset dst
+ }
+ for i := y + n; i < size; i++ {
+ if dst[i] != byte(i&127) {
+ t.Fatalf("suffix dst[%d] = %d", i, dst[i])
+ }
+ }
+ }
+ }
+ }
+}
+
+func TestMemmoveAlias(t *testing.T) {
+ size := 256
+ if testing.Short() {
+ size = 128 + 16
+ }
+ buf := make([]byte, size)
+ for i := 0; i < size; i++ {
+ buf[i] = byte(i)
+ }
+ for n := 0; n <= size; n++ {
+ for x := 0; x <= size-n; x++ { // src offset
+ for y := 0; y <= size-n; y++ { // dst offset
+ copy(buf[y:y+n], buf[x:x+n])
+ for i := 0; i < y; i++ {
+ if buf[i] != byte(i) {
+ t.Fatalf("prefix buf[%d] = %d", i, buf[i])
+ }
+ }
+ for i := y; i < y+n; i++ {
+ if buf[i] != byte(i-y+x) {
+ t.Fatalf("copied buf[%d] = %d", i, buf[i])
+ }
+ buf[i] = byte(i) // reset buf
+ }
+ for i := y + n; i < size; i++ {
+ if buf[i] != byte(i) {
+ t.Fatalf("suffix buf[%d] = %d", i, buf[i])
+ }
+ }
+ }
+ }
+ }
+}
+
+func bmMemmove(n int, b *testing.B) {
+ x := make([]byte, n)
+ y := make([]byte, n)
+ b.SetBytes(int64(n))
+ for i := 0; i < b.N; i++ {
+ copy(x, y)
+ }
+}
+
+func BenchmarkMemmove0(b *testing.B) { bmMemmove(0, b) }
+func BenchmarkMemmove1(b *testing.B) { bmMemmove(1, b) }
+func BenchmarkMemmove2(b *testing.B) { bmMemmove(2, b) }
+func BenchmarkMemmove3(b *testing.B) { bmMemmove(3, b) }
+func BenchmarkMemmove4(b *testing.B) { bmMemmove(4, b) }
+func BenchmarkMemmove5(b *testing.B) { bmMemmove(5, b) }
+func BenchmarkMemmove6(b *testing.B) { bmMemmove(6, b) }
+func BenchmarkMemmove7(b *testing.B) { bmMemmove(7, b) }
+func BenchmarkMemmove8(b *testing.B) { bmMemmove(8, b) }
+func BenchmarkMemmove9(b *testing.B) { bmMemmove(9, b) }
+func BenchmarkMemmove10(b *testing.B) { bmMemmove(10, b) }
+func BenchmarkMemmove11(b *testing.B) { bmMemmove(11, b) }
+func BenchmarkMemmove12(b *testing.B) { bmMemmove(12, b) }
+func BenchmarkMemmove13(b *testing.B) { bmMemmove(13, b) }
+func BenchmarkMemmove14(b *testing.B) { bmMemmove(14, b) }
+func BenchmarkMemmove15(b *testing.B) { bmMemmove(15, b) }
+func BenchmarkMemmove16(b *testing.B) { bmMemmove(16, b) }
+func BenchmarkMemmove32(b *testing.B) { bmMemmove(32, b) }
+func BenchmarkMemmove64(b *testing.B) { bmMemmove(64, b) }
+func BenchmarkMemmove128(b *testing.B) { bmMemmove(128, b) }
+func BenchmarkMemmove256(b *testing.B) { bmMemmove(256, b) }
+func BenchmarkMemmove512(b *testing.B) { bmMemmove(512, b) }
+func BenchmarkMemmove1024(b *testing.B) { bmMemmove(1024, b) }
+func BenchmarkMemmove2048(b *testing.B) { bmMemmove(2048, b) }
+func BenchmarkMemmove4096(b *testing.B) { bmMemmove(4096, b) }
diff --git a/src/pkg/runtime/mfinal.c b/src/pkg/runtime/mfinal.c
index 2f5e4277d..3e524d3e0 100644
--- a/src/pkg/runtime/mfinal.c
+++ b/src/pkg/runtime/mfinal.c
@@ -5,6 +5,7 @@
#include "runtime.h"
#include "arch_GOARCH.h"
#include "malloc.h"
+#include "type.h"
enum { debug = 0 };
@@ -13,6 +14,8 @@ struct Fin
{
FuncVal *fn;
uintptr nret;
+ Type *fint;
+ PtrType *ot;
};
// Finalizer hash table. Direct hash, linear scan, at most 3/4 full.
@@ -42,7 +45,7 @@ static struct {
} fintab[TABSZ];
static void
-addfintab(Fintab *t, void *k, FuncVal *fn, uintptr nret)
+addfintab(Fintab *t, void *k, FuncVal *fn, uintptr nret, Type *fint, PtrType *ot)
{
int32 i, j;
@@ -67,6 +70,8 @@ ret:
t->key[i] = k;
t->val[i].fn = fn;
t->val[i].nret = nret;
+ t->val[i].fint = fint;
+ t->val[i].ot = ot;
}
static bool
@@ -87,6 +92,7 @@ lookfintab(Fintab *t, void *k, bool del, Fin *f)
t->key[i] = (void*)-1;
t->val[i].fn = nil;
t->val[i].nret = 0;
+ t->val[i].ot = nil;
t->ndead++;
}
return true;
@@ -117,13 +123,13 @@ resizefintab(Fintab *tab)
newtab.max *= 3;
}
- newtab.key = runtime·mallocgc(newtab.max*sizeof newtab.key[0], FlagNoPointers, 0, 1);
- newtab.val = runtime·mallocgc(newtab.max*sizeof newtab.val[0], 0, 0, 1);
+ newtab.key = runtime·mallocgc(newtab.max*sizeof newtab.key[0], 0, FlagNoInvokeGC|FlagNoScan);
+ newtab.val = runtime·mallocgc(newtab.max*sizeof newtab.val[0], 0, FlagNoInvokeGC);
for(i=0; i<tab->max; i++) {
k = tab->key[i];
if(k != nil && k != (void*)-1)
- addfintab(&newtab, k, tab->val[i].fn, tab->val[i].nret);
+ addfintab(&newtab, k, tab->val[i].fn, tab->val[i].nret, tab->val[i].fint, tab->val[i].ot);
}
runtime·free(tab->key);
@@ -137,7 +143,7 @@ resizefintab(Fintab *tab)
}
bool
-runtime·addfinalizer(void *p, FuncVal *f, uintptr nret)
+runtime·addfinalizer(void *p, FuncVal *f, uintptr nret, Type *fint, PtrType *ot)
{
Fintab *tab;
byte *base;
@@ -166,7 +172,7 @@ runtime·addfinalizer(void *p, FuncVal *f, uintptr nret)
resizefintab(tab);
}
- addfintab(tab, p, f, nret);
+ addfintab(tab, p, f, nret, fint, ot);
runtime·setblockspecial(p, true);
runtime·unlock(tab);
return true;
@@ -175,7 +181,7 @@ runtime·addfinalizer(void *p, FuncVal *f, uintptr nret)
// get finalizer; if del, delete finalizer.
// caller is responsible for updating RefHasFinalizer (special) bit.
bool
-runtime·getfinalizer(void *p, bool del, FuncVal **fn, uintptr *nret)
+runtime·getfinalizer(void *p, bool del, FuncVal **fn, uintptr *nret, Type **fint, PtrType **ot)
{
Fintab *tab;
bool res;
@@ -189,6 +195,8 @@ runtime·getfinalizer(void *p, bool del, FuncVal **fn, uintptr *nret)
return false;
*fn = f.fn;
*nret = f.nret;
+ *fint = f.fint;
+ *ot = f.ot;
return true;
}
diff --git a/src/pkg/runtime/mfinal_test.go b/src/pkg/runtime/mfinal_test.go
index de632717a..6efef9bb0 100644
--- a/src/pkg/runtime/mfinal_test.go
+++ b/src/pkg/runtime/mfinal_test.go
@@ -9,8 +9,94 @@ import (
"sync"
"sync/atomic"
"testing"
+ "time"
)
+type Tintptr *int // assignable to *int
+type Tint int // *Tint implements Tinter, interface{}
+
+func (t *Tint) m() {}
+
+type Tinter interface {
+ m()
+}
+
+func TestFinalizerType(t *testing.T) {
+ if runtime.GOARCH != "amd64" {
+ t.Skipf("Skipping on non-amd64 machine")
+ }
+
+ ch := make(chan bool, 10)
+ finalize := func(x *int) {
+ if *x != 97531 {
+ t.Errorf("finalizer %d, want %d", *x, 97531)
+ }
+ ch <- true
+ }
+
+ var finalizerTests = []struct {
+ convert func(*int) interface{}
+ finalizer interface{}
+ }{
+ {func(x *int) interface{} { return x }, func(v *int) { finalize(v) }},
+ {func(x *int) interface{} { return Tintptr(x) }, func(v Tintptr) { finalize(v) }},
+ {func(x *int) interface{} { return Tintptr(x) }, func(v *int) { finalize(v) }},
+ {func(x *int) interface{} { return (*Tint)(x) }, func(v *Tint) { finalize((*int)(v)) }},
+ {func(x *int) interface{} { return (*Tint)(x) }, func(v Tinter) { finalize((*int)(v.(*Tint))) }},
+ }
+
+ for _, tt := range finalizerTests {
+ go func() {
+ v := new(int)
+ *v = 97531
+ runtime.SetFinalizer(tt.convert(v), tt.finalizer)
+ v = nil
+ }()
+ time.Sleep(1 * time.Second)
+ runtime.GC()
+ select {
+ case <-ch:
+ case <-time.After(time.Second * 4):
+ t.Errorf("finalizer for type %T didn't run", tt.finalizer)
+ }
+ }
+}
+
+type bigValue struct {
+ fill uint64
+ it bool
+ up string
+}
+
+func TestFinalizerInterfaceBig(t *testing.T) {
+ if runtime.GOARCH != "amd64" {
+ t.Skipf("Skipping on non-amd64 machine")
+ }
+ ch := make(chan bool)
+ go func() {
+ v := &bigValue{0xDEADBEEFDEADBEEF, true, "It matters not how strait the gate"}
+ old := *v
+ runtime.SetFinalizer(v, func(v interface{}) {
+ i, ok := v.(*bigValue)
+ if !ok {
+ t.Errorf("finalizer called with type %T, want *bigValue", v)
+ }
+ if *i != old {
+ t.Errorf("finalizer called with %+v, want %+v", *i, old)
+ }
+ close(ch)
+ })
+ v = nil
+ }()
+ time.Sleep(1 * time.Second)
+ runtime.GC()
+ select {
+ case <-ch:
+ case <-time.After(4 * time.Second):
+ t.Errorf("finalizer for type *bigValue didn't run")
+ }
+}
+
func fin(v *int) {
}
diff --git a/src/pkg/runtime/mfixalloc.c b/src/pkg/runtime/mfixalloc.c
index c7dab8aea..d670629da 100644
--- a/src/pkg/runtime/mfixalloc.c
+++ b/src/pkg/runtime/mfixalloc.c
@@ -13,17 +13,16 @@
// Initialize f to allocate objects of the given size,
// using the allocator to obtain chunks of memory.
void
-runtime·FixAlloc_Init(FixAlloc *f, uintptr size, void *(*alloc)(uintptr), void (*first)(void*, byte*), void *arg)
+runtime·FixAlloc_Init(FixAlloc *f, uintptr size, void (*first)(void*, byte*), void *arg, uint64 *stat)
{
f->size = size;
- f->alloc = alloc;
f->first = first;
f->arg = arg;
f->list = nil;
f->chunk = nil;
f->nchunk = 0;
f->inuse = 0;
- f->sys = 0;
+ f->stat = stat;
}
void*
@@ -43,10 +42,7 @@ runtime·FixAlloc_Alloc(FixAlloc *f)
return v;
}
if(f->nchunk < f->size) {
- f->sys += FixAllocChunk;
- f->chunk = f->alloc(FixAllocChunk);
- if(f->chunk == nil)
- runtime·throw("out of memory (FixAlloc)");
+ f->chunk = runtime·persistentalloc(FixAllocChunk, 0, f->stat);
f->nchunk = FixAllocChunk;
}
v = f->chunk;
diff --git a/src/pkg/runtime/mgc0.c b/src/pkg/runtime/mgc0.c
index c83f1892c..4b2108ba7 100644
--- a/src/pkg/runtime/mgc0.c
+++ b/src/pkg/runtime/mgc0.c
@@ -12,7 +12,8 @@
#include "race.h"
#include "type.h"
#include "typekind.h"
-#include "hashmap.h"
+#include "funcdata.h"
+#include "../../cmd/ld/textflag.h"
enum {
Debug = 0,
@@ -32,6 +33,13 @@ enum {
PRECISE = 1,
LOOP = 2,
PC_BITS = PRECISE | LOOP,
+
+ // Pointer map
+ BitsPerPointer = 2,
+ BitsNoPointer = 0,
+ BitsPointer = 1,
+ BitsIface = 2,
+ BitsEface = 3,
};
// Bits in per-word bitmap.
@@ -43,7 +51,7 @@ enum {
// The bits in the word are packed together by type first, then by
// heap location, so each 64-bit bitmap word consists of, from top to bottom,
// the 16 bitSpecial bits for the corresponding heap words, then the 16 bitMarked bits,
-// then the 16 bitNoPointers/bitBlockBoundary bits, then the 16 bitAllocated bits.
+// then the 16 bitNoScan/bitBlockBoundary bits, then the 16 bitAllocated bits.
// This layout makes it easier to iterate over the bits of a given type.
//
// The bitmap starts at mheap.arena_start and extends *backward* from
@@ -60,7 +68,7 @@ enum {
// /* then test bits & bitAllocated, bits & bitMarked, etc. */
//
#define bitAllocated ((uintptr)1<<(bitShift*0))
-#define bitNoPointers ((uintptr)1<<(bitShift*1)) /* when bitAllocated is set */
+#define bitNoScan ((uintptr)1<<(bitShift*1)) /* when bitAllocated is set */
#define bitMarked ((uintptr)1<<(bitShift*2)) /* when bitAllocated is set */
#define bitSpecial ((uintptr)1<<(bitShift*3)) /* when bitAllocated is set - has finalizer or being profiled */
#define bitBlockBoundary ((uintptr)1<<(bitShift*1)) /* when bitAllocated is NOT set */
@@ -82,8 +90,6 @@ enum {
//
uint32 runtime·worldsema = 1;
-static int32 gctrace;
-
typedef struct Obj Obj;
struct Obj
{
@@ -110,6 +116,8 @@ struct Finalizer
FuncVal *fn;
void *arg;
uintptr nret;
+ Type *fint;
+ PtrType *ot;
};
typedef struct FinBlock FinBlock;
@@ -167,7 +175,6 @@ static struct {
enum {
GC_DEFAULT_PTR = GC_NUM_INSTR,
- GC_MAP_NEXT,
GC_CHAN,
GC_NUM_INSTR2
@@ -190,6 +197,16 @@ static struct {
uint64 instr[GC_NUM_INSTR2];
uint64 putempty;
uint64 getfull;
+ struct {
+ uint64 foundbit;
+ uint64 foundword;
+ uint64 foundspan;
+ } flushptrbuf;
+ struct {
+ uint64 foundbit;
+ uint64 foundword;
+ uint64 foundspan;
+ } markonly;
} gcstats;
// markonly marks an object. It returns true if the object
@@ -199,12 +216,12 @@ static bool
markonly(void *obj)
{
byte *p;
- uintptr *bitp, bits, shift, x, xbits, off;
+ uintptr *bitp, bits, shift, x, xbits, off, j;
MSpan *s;
PageID k;
// Words outside the arena cannot be pointers.
- if(obj < runtime·mheap->arena_start || obj >= runtime·mheap->arena_used)
+ if(obj < runtime·mheap.arena_start || obj >= runtime·mheap.arena_used)
return false;
// obj may be a pointer to a live object.
@@ -214,42 +231,57 @@ markonly(void *obj)
obj = (void*)((uintptr)obj & ~((uintptr)PtrSize-1));
// Find bits for this word.
- off = (uintptr*)obj - (uintptr*)runtime·mheap->arena_start;
- bitp = (uintptr*)runtime·mheap->arena_start - off/wordsPerBitmapWord - 1;
+ off = (uintptr*)obj - (uintptr*)runtime·mheap.arena_start;
+ bitp = (uintptr*)runtime·mheap.arena_start - off/wordsPerBitmapWord - 1;
shift = off % wordsPerBitmapWord;
xbits = *bitp;
bits = xbits >> shift;
// Pointing at the beginning of a block?
- if((bits & (bitAllocated|bitBlockBoundary)) != 0)
+ if((bits & (bitAllocated|bitBlockBoundary)) != 0) {
+ if(CollectStats)
+ runtime·xadd64(&gcstats.markonly.foundbit, 1);
goto found;
+ }
+
+ // Pointing just past the beginning?
+ // Scan backward a little to find a block boundary.
+ for(j=shift; j-->0; ) {
+ if(((xbits>>j) & (bitAllocated|bitBlockBoundary)) != 0) {
+ shift = j;
+ bits = xbits>>shift;
+ if(CollectStats)
+ runtime·xadd64(&gcstats.markonly.foundword, 1);
+ goto found;
+ }
+ }
// Otherwise consult span table to find beginning.
// (Manually inlined copy of MHeap_LookupMaybe.)
k = (uintptr)obj>>PageShift;
x = k;
if(sizeof(void*) == 8)
- x -= (uintptr)runtime·mheap->arena_start>>PageShift;
- s = runtime·mheap->map[x];
- if(s == nil || k < s->start || k - s->start >= s->npages || s->state != MSpanInUse)
+ x -= (uintptr)runtime·mheap.arena_start>>PageShift;
+ s = runtime·mheap.spans[x];
+ if(s == nil || k < s->start || obj >= s->limit || s->state != MSpanInUse)
return false;
p = (byte*)((uintptr)s->start<<PageShift);
if(s->sizeclass == 0) {
obj = p;
} else {
- if((byte*)obj >= (byte*)s->limit)
- return false;
uintptr size = s->elemsize;
int32 i = ((byte*)obj - p)/size;
obj = p+i*size;
}
// Now that we know the object header, reload bits.
- off = (uintptr*)obj - (uintptr*)runtime·mheap->arena_start;
- bitp = (uintptr*)runtime·mheap->arena_start - off/wordsPerBitmapWord - 1;
+ off = (uintptr*)obj - (uintptr*)runtime·mheap.arena_start;
+ bitp = (uintptr*)runtime·mheap.arena_start - off/wordsPerBitmapWord - 1;
shift = off % wordsPerBitmapWord;
xbits = *bitp;
bits = xbits >> shift;
+ if(CollectStats)
+ runtime·xadd64(&gcstats.markonly.foundspan, 1);
found:
// Now we have bits, bitp, and shift correct for
@@ -293,7 +325,7 @@ struct BufferList
uint32 busy;
byte pad[CacheLineSize];
};
-#pragma dataflag 16 // no pointers
+#pragma dataflag NOPTR
static BufferList bufferList[MaxGcproc];
static Type *itabtype;
@@ -328,7 +360,7 @@ flushptrbuf(PtrTarget *ptrbuf, PtrTarget **ptrbufpos, Obj **_wp, Workbuf **_wbuf
Workbuf *wbuf;
PtrTarget *ptrbuf_end;
- arena_start = runtime·mheap->arena_start;
+ arena_start = runtime·mheap.arena_start;
wp = *_wp;
wbuf = *_wbuf;
@@ -367,7 +399,7 @@ flushptrbuf(PtrTarget *ptrbuf, PtrTarget **ptrbufpos, Obj **_wp, Workbuf **_wbuf
// obj belongs to interval [mheap.arena_start, mheap.arena_used).
if(Debug > 1) {
- if(obj < runtime·mheap->arena_start || obj >= runtime·mheap->arena_used)
+ if(obj < runtime·mheap.arena_start || obj >= runtime·mheap.arena_used)
runtime·throw("object is outside of mheap");
}
@@ -388,8 +420,11 @@ flushptrbuf(PtrTarget *ptrbuf, PtrTarget **ptrbufpos, Obj **_wp, Workbuf **_wbuf
bits = xbits >> shift;
// Pointing at the beginning of a block?
- if((bits & (bitAllocated|bitBlockBoundary)) != 0)
+ if((bits & (bitAllocated|bitBlockBoundary)) != 0) {
+ if(CollectStats)
+ runtime·xadd64(&gcstats.flushptrbuf.foundbit, 1);
goto found;
+ }
ti = 0;
@@ -400,6 +435,8 @@ flushptrbuf(PtrTarget *ptrbuf, PtrTarget **ptrbufpos, Obj **_wp, Workbuf **_wbuf
obj = (byte*)obj - (shift-j)*PtrSize;
shift = j;
bits = xbits>>shift;
+ if(CollectStats)
+ runtime·xadd64(&gcstats.flushptrbuf.foundword, 1);
goto found;
}
}
@@ -410,15 +447,13 @@ flushptrbuf(PtrTarget *ptrbuf, PtrTarget **ptrbufpos, Obj **_wp, Workbuf **_wbuf
x = k;
if(sizeof(void*) == 8)
x -= (uintptr)arena_start>>PageShift;
- s = runtime·mheap->map[x];
- if(s == nil || k < s->start || k - s->start >= s->npages || s->state != MSpanInUse)
+ s = runtime·mheap.spans[x];
+ if(s == nil || k < s->start || obj >= s->limit || s->state != MSpanInUse)
continue;
p = (byte*)((uintptr)s->start<<PageShift);
if(s->sizeclass == 0) {
obj = p;
} else {
- if((byte*)obj >= (byte*)s->limit)
- continue;
size = s->elemsize;
int32 i = ((byte*)obj - p)/size;
obj = p+i*size;
@@ -430,6 +465,8 @@ flushptrbuf(PtrTarget *ptrbuf, PtrTarget **ptrbufpos, Obj **_wp, Workbuf **_wbuf
shift = off % wordsPerBitmapWord;
xbits = *bitp;
bits = xbits >> shift;
+ if(CollectStats)
+ runtime·xadd64(&gcstats.flushptrbuf.foundspan, 1);
found:
// Now we have bits, bitp, and shift correct for
@@ -450,7 +487,7 @@ flushptrbuf(PtrTarget *ptrbuf, PtrTarget **ptrbufpos, Obj **_wp, Workbuf **_wbuf
}
// If object has no pointers, don't need to scan further.
- if((bits & bitNoPointers) != 0)
+ if((bits & bitNoScan) != 0)
continue;
// Ask span about size class.
@@ -458,7 +495,7 @@ flushptrbuf(PtrTarget *ptrbuf, PtrTarget **ptrbufpos, Obj **_wp, Workbuf **_wbuf
x = (uintptr)obj >> PageShift;
if(sizeof(void*) == 8)
x -= (uintptr)arena_start>>PageShift;
- s = runtime·mheap->map[x];
+ s = runtime·mheap.spans[x];
PREFETCH(obj);
@@ -541,9 +578,6 @@ flushobjbuf(Obj *objbuf, Obj **objbufpos, Obj **_wp, Workbuf **_wbuf, uintptr *_
// Program that scans the whole block and treats every block element as a potential pointer
static uintptr defaultProg[2] = {PtrSize, GC_DEFAULT_PTR};
-// Hashmap iterator program
-static uintptr mapProg[2] = {0, GC_MAP_NEXT};
-
// Hchan program
static uintptr chanProg[2] = {0, GC_CHAN};
@@ -566,7 +600,7 @@ checkptr(void *obj, uintptr objti)
if(!Debug)
runtime·throw("checkptr is debug only");
- if(obj < runtime·mheap->arena_start || obj >= runtime·mheap->arena_used)
+ if(obj < runtime·mheap.arena_start || obj >= runtime·mheap.arena_used)
return;
type = runtime·gettype(obj);
t = (Type*)(type & ~(uintptr)(PtrSize-1));
@@ -574,8 +608,8 @@ checkptr(void *obj, uintptr objti)
return;
x = (uintptr)obj >> PageShift;
if(sizeof(void*) == 8)
- x -= (uintptr)(runtime·mheap->arena_start)>>PageShift;
- s = runtime·mheap->map[x];
+ x -= (uintptr)(runtime·mheap.arena_start)>>PageShift;
+ s = runtime·mheap.spans[x];
objstart = (byte*)((uintptr)s->start<<PageShift);
if(s->sizeclass != 0) {
i = ((byte*)obj - objstart)/s->elemsize;
@@ -583,8 +617,11 @@ checkptr(void *obj, uintptr objti)
}
tisize = *(uintptr*)objti;
// Sanity check for object size: it should fit into the memory block.
- if((byte*)obj + tisize > objstart + s->elemsize)
+ if((byte*)obj + tisize > objstart + s->elemsize) {
+ runtime·printf("object of type '%S' at %p/%p does not fit in block %p/%p\n",
+ *t->string, obj, tisize, objstart, s->elemsize);
runtime·throw("invalid gc type info");
+ }
if(obj != objstart)
return;
// If obj points to the beginning of the memory block,
@@ -600,7 +637,7 @@ checkptr(void *obj, uintptr objti)
for(j = 1; pc1[j] != GC_END && pc2[j] != GC_END; j++) {
if(pc1[j] != pc2[j]) {
runtime·printf("invalid gc type info for '%s' at %p, type info %p, block info %p\n",
- t->string ? (int8*)t->string->str : (int8*)"?", j, pc1[j], pc2[j]);
+ t->string ? (int8*)t->string->str : (int8*)"?", j, pc1[j], pc2[j]);
runtime·throw("invalid gc type info");
}
}
@@ -623,7 +660,7 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking)
byte *b, *arena_start, *arena_used;
uintptr n, i, end_b, elemsize, size, ti, objti, count, type;
uintptr *pc, precise_type, nominal_size;
- uintptr *map_ret, mapkey_size, mapval_size, mapkey_ti, mapval_ti, *chan_ret, chancap;
+ uintptr *chan_ret, chancap;
void *obj;
Type *t;
Slice *sliceptr;
@@ -633,11 +670,6 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking)
Obj *objbuf, *objbuf_end, *objbufpos;
Eface *eface;
Iface *iface;
- Hmap *hmap;
- MapType *maptype;
- bool mapkey_kind, mapval_kind;
- struct hash_gciter map_iter;
- struct hash_gciter_data d;
Hchan *chan;
ChanType *chantype;
@@ -645,8 +677,8 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking)
runtime·throw("scanblock: size of Workbuf is suboptimal");
// Memory arena parameters.
- arena_start = runtime·mheap->arena_start;
- arena_used = runtime·mheap->arena_used;
+ arena_start = runtime·mheap.arena_start;
+ arena_used = runtime·mheap.arena_used;
stack_ptr = stack+nelem(stack)-1;
@@ -666,10 +698,6 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking)
objbufpos = objbuf;
// (Silence the compiler)
- map_ret = nil;
- mapkey_size = mapval_size = 0;
- mapkey_kind = mapval_kind = false;
- mapkey_ti = mapval_ti = 0;
chan = nil;
chantype = nil;
chan_ret = nil;
@@ -738,23 +766,6 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking)
stack_top.elemsize = pc[0];
stack_top.loop_or_ret = pc+1;
break;
- case TypeInfo_Map:
- hmap = (Hmap*)b;
- maptype = (MapType*)t;
- if(hash_gciter_init(hmap, &map_iter)) {
- mapkey_size = maptype->key->size;
- mapkey_kind = maptype->key->kind;
- mapkey_ti = (uintptr)maptype->key->gc | PRECISE;
- mapval_size = maptype->elem->size;
- mapval_kind = maptype->elem->kind;
- mapval_ti = (uintptr)maptype->elem->gc | PRECISE;
-
- map_ret = nil;
- pc = mapProg;
- } else {
- goto next_block;
- }
- break;
case TypeInfo_Chan:
chan = (Hchan*)b;
chantype = (ChanType*)t;
@@ -955,77 +966,6 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking)
pc = (uintptr*)((byte*)pc + *(int32*)(pc+2)); // target of the CALL instruction
continue;
- case GC_MAP_PTR:
- hmap = *(Hmap**)(stack_top.b + pc[1]);
- if(hmap == nil) {
- pc += 3;
- continue;
- }
- if(markonly(hmap)) {
- maptype = (MapType*)pc[2];
- if(hash_gciter_init(hmap, &map_iter)) {
- mapkey_size = maptype->key->size;
- mapkey_kind = maptype->key->kind;
- mapkey_ti = (uintptr)maptype->key->gc | PRECISE;
- mapval_size = maptype->elem->size;
- mapval_kind = maptype->elem->kind;
- mapval_ti = (uintptr)maptype->elem->gc | PRECISE;
-
- // Start mapProg.
- map_ret = pc+3;
- pc = mapProg+1;
- } else {
- pc += 3;
- }
- } else {
- pc += 3;
- }
- continue;
-
- case GC_MAP_NEXT:
- // Add all keys and values to buffers, mark all subtables.
- while(hash_gciter_next(&map_iter, &d)) {
- // buffers: reserve space for 2 objects.
- if(ptrbufpos+2 >= ptrbuf_end)
- flushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj);
- if(objbufpos+2 >= objbuf_end)
- flushobjbuf(objbuf, &objbufpos, &wp, &wbuf, &nobj);
-
- if(d.st != nil)
- markonly(d.st);
-
- if(d.key_data != nil) {
- if(!(mapkey_kind & KindNoPointers) || d.indirectkey) {
- if(!d.indirectkey)
- *objbufpos++ = (Obj){d.key_data, mapkey_size, mapkey_ti};
- else {
- if(Debug) {
- obj = *(void**)d.key_data;
- if(!(arena_start <= obj && obj < arena_used))
- runtime·throw("scanblock: inconsistent hashmap");
- }
- *ptrbufpos++ = (PtrTarget){*(void**)d.key_data, mapkey_ti};
- }
- }
- if(!(mapval_kind & KindNoPointers) || d.indirectval) {
- if(!d.indirectval)
- *objbufpos++ = (Obj){d.val_data, mapval_size, mapval_ti};
- else {
- if(Debug) {
- obj = *(void**)d.val_data;
- if(!(arena_start <= obj && obj < arena_used))
- runtime·throw("scanblock: inconsistent hashmap");
- }
- *ptrbufpos++ = (PtrTarget){*(void**)d.val_data, mapval_ti};
- }
- }
- }
- }
- if(map_ret == nil)
- goto next_block;
- pc = map_ret;
- continue;
-
case GC_REGION:
obj = (void*)(stack_top.b + pc[1]);
size = pc[2];
@@ -1038,7 +978,6 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking)
continue;
case GC_CHAN_PTR:
- // Similar to GC_MAP_PTR
chan = *(Hchan**)(stack_top.b + pc[1]);
if(chan == nil) {
pc += 3;
@@ -1157,14 +1096,14 @@ debug_scanblock(byte *b, uintptr n)
obj = (byte*)vp[i];
// Words outside the arena cannot be pointers.
- if((byte*)obj < runtime·mheap->arena_start || (byte*)obj >= runtime·mheap->arena_used)
+ if((byte*)obj < runtime·mheap.arena_start || (byte*)obj >= runtime·mheap.arena_used)
continue;
// Round down to word boundary.
obj = (void*)((uintptr)obj & ~((uintptr)PtrSize-1));
// Consult span table to find beginning.
- s = runtime·MHeap_LookupMaybe(runtime·mheap, obj);
+ s = runtime·MHeap_LookupMaybe(&runtime·mheap, obj);
if(s == nil)
continue;
@@ -1173,15 +1112,13 @@ debug_scanblock(byte *b, uintptr n)
if(s->sizeclass == 0) {
obj = p;
} else {
- if((byte*)obj >= (byte*)s->limit)
- continue;
int32 i = ((byte*)obj - p)/size;
obj = p+i*size;
}
// Now that we know the object header, reload bits.
- off = (uintptr*)obj - (uintptr*)runtime·mheap->arena_start;
- bitp = (uintptr*)runtime·mheap->arena_start - off/wordsPerBitmapWord - 1;
+ off = (uintptr*)obj - (uintptr*)runtime·mheap.arena_start;
+ bitp = (uintptr*)runtime·mheap.arena_start - off/wordsPerBitmapWord - 1;
shift = off % wordsPerBitmapWord;
xbits = *bitp;
bits = xbits >> shift;
@@ -1196,7 +1133,7 @@ debug_scanblock(byte *b, uintptr n)
runtime·printf("found unmarked block %p in %p\n", obj, vp+i);
// If object has no pointers, don't need to scan further.
- if((bits & bitNoPointers) != 0)
+ if((bits & bitNoScan) != 0)
continue;
debug_scanblock(obj, size);
@@ -1286,7 +1223,7 @@ getempty(Workbuf *b)
runtime·lock(&work);
if(work.nchunk < sizeof *b) {
work.nchunk = 1<<20;
- work.chunk = runtime·SysAlloc(work.nchunk);
+ work.chunk = runtime·SysAlloc(work.nchunk, &mstats.gc_sys);
if(work.chunk == nil)
runtime·throw("runtime: cannot allocate memory");
}
@@ -1377,12 +1314,12 @@ addroot(Obj obj)
cap = PageSize/sizeof(Obj);
if(cap < 2*work.rootcap)
cap = 2*work.rootcap;
- new = (Obj*)runtime·SysAlloc(cap*sizeof(Obj));
+ new = (Obj*)runtime·SysAlloc(cap*sizeof(Obj), &mstats.gc_sys);
if(new == nil)
runtime·throw("runtime: cannot allocate memory");
if(work.roots != nil) {
runtime·memmove(new, work.roots, work.rootcap*sizeof(Obj));
- runtime·SysFree(work.roots, work.rootcap*sizeof(Obj));
+ runtime·SysFree(work.roots, work.rootcap*sizeof(Obj), &mstats.gc_sys);
}
work.roots = new;
work.rootcap = cap;
@@ -1391,26 +1328,106 @@ addroot(Obj obj)
work.nroot++;
}
-// Scan a stack frame. The doframe parameter is a signal that the previously
-// scanned activation has an unknown argument size. When *doframe is true the
-// current activation must have its entire frame scanned. Otherwise, only the
-// locals need to be scanned.
+extern byte pclntab[]; // base for f->ptrsoff
+
+typedef struct BitVector BitVector;
+struct BitVector
+{
+ int32 n;
+ uint32 data[];
+};
+
+// Scans an interface data value when the interface type indicates
+// that it is a pointer.
static void
-addframeroots(Func *f, byte*, byte *sp, void *doframe)
+scaninterfacedata(uintptr bits, byte *scanp, bool afterprologue)
{
- uintptr outs;
-
- if(thechar == '5')
- sp += sizeof(uintptr);
- if(f->locals == 0 || *(bool*)doframe == true)
- addroot((Obj){sp, f->frame - sizeof(uintptr), 0});
- else if(f->locals > 0) {
- outs = f->frame - sizeof(uintptr) - f->locals;
- addroot((Obj){sp + outs, f->locals, 0});
+ Itab *tab;
+ Type *type;
+
+ if(runtime·precisestack && afterprologue) {
+ if(bits == BitsIface) {
+ tab = *(Itab**)scanp;
+ if(tab->type->size <= sizeof(void*) && (tab->type->kind & KindNoPointers))
+ return;
+ } else { // bits == BitsEface
+ type = *(Type**)scanp;
+ if(type->size <= sizeof(void*) && (type->kind & KindNoPointers))
+ return;
+ }
+ }
+ addroot((Obj){scanp+PtrSize, PtrSize, 0});
+}
+
+// Starting from scanp, scans words corresponding to set bits.
+static void
+scanbitvector(byte *scanp, BitVector *bv, bool afterprologue)
+{
+ uintptr word, bits;
+ uint32 *wordp;
+ int32 i, remptrs;
+
+ wordp = bv->data;
+ for(remptrs = bv->n; remptrs > 0; remptrs -= 32) {
+ word = *wordp++;
+ if(remptrs < 32)
+ i = remptrs;
+ else
+ i = 32;
+ i /= BitsPerPointer;
+ for(; i > 0; i--) {
+ bits = word & 3;
+ if(bits != BitsNoPointer && *(void**)scanp != nil)
+ if(bits == BitsPointer)
+ addroot((Obj){scanp, PtrSize, 0});
+ else
+ scaninterfacedata(bits, scanp, afterprologue);
+ word >>= BitsPerPointer;
+ scanp += PtrSize;
+ }
}
- if(f->args > 0)
- addroot((Obj){sp + f->frame, f->args, 0});
- *(bool*)doframe = (f->args == ArgsSizeUnknown);
+}
+
+// Scan a stack frame: local variables and function arguments/results.
+static void
+addframeroots(Stkframe *frame, void*)
+{
+ Func *f;
+ BitVector *args, *locals;
+ uintptr size;
+ bool afterprologue;
+
+ f = frame->fn;
+
+ // Scan local variables if stack frame has been allocated.
+ // Use pointer information if known.
+ afterprologue = (frame->varp > (byte*)frame->sp);
+ if(afterprologue) {
+ locals = runtime·funcdata(f, FUNCDATA_GCLocals);
+ if(locals == nil) {
+ // No locals information, scan everything.
+ size = frame->varp - (byte*)frame->sp;
+ addroot((Obj){frame->varp - size, size, 0});
+ } else if(locals->n < 0) {
+ // Locals size information, scan just the
+ // locals.
+ size = -locals->n;
+ addroot((Obj){frame->varp - size, size, 0});
+ } else if(locals->n > 0) {
+ // Locals bitmap information, scan just the
+ // pointers in locals.
+ size = (locals->n*PtrSize) / BitsPerPointer;
+ scanbitvector(frame->varp - size, locals, afterprologue);
+ }
+ }
+
+ // Scan arguments.
+ // Use pointer information if known.
+ args = runtime·funcdata(f, FUNCDATA_GCArgs);
+ if(args != nil && args->n > 0)
+ scanbitvector(frame->argp, args, false);
+ else
+ addroot((Obj){frame->argp, frame->arglen, 0});
}
static void
@@ -1419,62 +1436,54 @@ addstackroots(G *gp)
M *mp;
int32 n;
Stktop *stk;
- byte *sp, *guard, *pc;
- Func *f;
- bool doframe;
+ uintptr sp, guard, pc, lr;
+ void *base;
+ uintptr size;
stk = (Stktop*)gp->stackbase;
- guard = (byte*)gp->stackguard;
-
- if(gp == g) {
- // Scanning our own stack: start at &gp.
- sp = runtime·getcallersp(&gp);
- pc = runtime·getcallerpc(&gp);
- } else if((mp = gp->m) != nil && mp->helpgc) {
- // gchelper's stack is in active use and has no interesting pointers.
- return;
- } else if(gp->gcstack != (uintptr)nil) {
+ guard = gp->stackguard;
+
+ if(gp == g)
+ runtime·throw("can't scan our own stack");
+ if((mp = gp->m) != nil && mp->helpgc)
+ runtime·throw("can't scan gchelper stack");
+ if(gp->syscallstack != (uintptr)nil) {
// Scanning another goroutine that is about to enter or might
// have just exited a system call. It may be executing code such
// as schedlock and may have needed to start a new stack segment.
// Use the stack segment and stack pointer at the time of
// the system call instead, since that won't change underfoot.
- sp = (byte*)gp->gcsp;
- pc = gp->gcpc;
- stk = (Stktop*)gp->gcstack;
- guard = (byte*)gp->gcguard;
+ sp = gp->syscallsp;
+ pc = gp->syscallpc;
+ lr = 0;
+ stk = (Stktop*)gp->syscallstack;
+ guard = gp->syscallguard;
} else {
// Scanning another goroutine's stack.
// The goroutine is usually asleep (the world is stopped).
- sp = (byte*)gp->sched.sp;
+ sp = gp->sched.sp;
pc = gp->sched.pc;
- if(ScanStackByFrames && pc == (byte*)runtime·goexit && gp->fnstart != nil) {
- // The goroutine has not started. However, its incoming
- // arguments are live at the top of the stack and must
- // be scanned. No other live values should be on the
- // stack.
- f = runtime·findfunc((uintptr)gp->fnstart->fn);
- if(f->args > 0) {
- if(thechar == '5')
- sp += sizeof(uintptr);
- addroot((Obj){sp, f->args, 0});
- }
- return;
- }
+ lr = gp->sched.lr;
+
+ // For function about to start, context argument is a root too.
+ if(gp->sched.ctxt != 0 && runtime·mlookup(gp->sched.ctxt, &base, &size, nil))
+ addroot((Obj){base, size, 0});
}
- if (ScanStackByFrames) {
- doframe = false;
- runtime·gentraceback(pc, sp, nil, gp, 0, nil, 0x7fffffff, addframeroots, &doframe);
+ if(ScanStackByFrames) {
+ USED(stk);
+ USED(guard);
+ runtime·gentraceback(pc, sp, lr, gp, 0, nil, 0x7fffffff, addframeroots, nil, false);
} else {
+ USED(lr);
USED(pc);
n = 0;
while(stk) {
- if(sp < guard-StackGuard || (byte*)stk < sp) {
+ if(sp < guard-StackGuard || (uintptr)stk < sp) {
runtime·printf("scanstack inconsistent: g%D#%d sp=%p not in [%p,%p]\n", gp->goid, n, sp, guard-StackGuard, stk);
runtime·throw("scanstack");
}
- addroot((Obj){sp, (byte*)stk - sp, (uintptr)defaultProg | PRECISE | LOOP});
- sp = (byte*)stk->gobuf.sp;
+ addroot((Obj){(byte*)sp, (uintptr)stk - sp, (uintptr)defaultProg | PRECISE | LOOP});
+ sp = stk->gobuf.sp;
guard = stk->stackguard;
stk = (Stktop*)stk->stackbase;
n++;
@@ -1512,8 +1521,8 @@ addroots(void)
addroot((Obj){bss, ebss - bss, (uintptr)gcbss});
// MSpan.types
- allspans = runtime·mheap->allspans;
- for(spanidx=0; spanidx<runtime·mheap->nspan; spanidx++) {
+ allspans = runtime·mheap.allspans;
+ for(spanidx=0; spanidx<runtime·mheap.nspan; spanidx++) {
s = allspans[spanidx];
if(s->state == MSpanInUse) {
// The garbage collector ignores type pointers stored in MSpan.types:
@@ -1541,10 +1550,7 @@ addroots(void)
case Gdead:
break;
case Grunning:
- if(gp != g)
- runtime·throw("mark - world not stopped");
- addstackroots(gp);
- break;
+ runtime·throw("mark - world not stopped");
case Grunnable:
case Gsyscall:
case Gwaiting:
@@ -1564,10 +1570,12 @@ handlespecial(byte *p, uintptr size)
{
FuncVal *fn;
uintptr nret;
+ PtrType *ot;
+ Type *fint;
FinBlock *block;
Finalizer *f;
- if(!runtime·getfinalizer(p, true, &fn, &nret)) {
+ if(!runtime·getfinalizer(p, true, &fn, &nret, &fint, &ot)) {
runtime·setblockspecial(p, false);
runtime·MProf_Free(p, size);
return false;
@@ -1576,9 +1584,7 @@ handlespecial(byte *p, uintptr size)
runtime·lock(&finlock);
if(finq == nil || finq->cnt == finq->cap) {
if(finc == nil) {
- finc = runtime·SysAlloc(PageSize);
- if(finc == nil)
- runtime·throw("runtime: cannot allocate memory");
+ finc = runtime·persistentalloc(PageSize, 0, &mstats.gc_sys);
finc->cap = (PageSize - sizeof(FinBlock)) / sizeof(Finalizer) + 1;
finc->alllink = allfin;
allfin = finc;
@@ -1592,6 +1598,8 @@ handlespecial(byte *p, uintptr size)
finq->cnt++;
f->fn = fn;
f->nret = nret;
+ f->fint = fint;
+ f->ot = ot;
f->arg = p;
runtime·unlock(&finlock);
return true;
@@ -1615,10 +1623,10 @@ sweepspan(ParFor *desc, uint32 idx)
MSpan *s;
USED(&desc);
- s = runtime·mheap->allspans[idx];
+ s = runtime·mheap.allspans[idx];
if(s->state != MSpanInUse)
return;
- arena_start = runtime·mheap->arena_start;
+ arena_start = runtime·mheap.arena_start;
p = (byte*)(s->start << PageShift);
cl = s->sizeclass;
size = s->elemsize;
@@ -1682,9 +1690,9 @@ sweepspan(ParFor *desc, uint32 idx)
// Free large span.
runtime·unmarkspan(p, 1<<PageShift);
*(uintptr*)p = (uintptr)0xdeaddeaddeaddeadll; // needs zeroing
- runtime·MHeap_Free(runtime·mheap, s, 1);
- c->local_alloc -= size;
- c->local_nfree++;
+ runtime·MHeap_Free(&runtime·mheap, s, 1);
+ c->local_nlargefree++;
+ c->local_largefree += size;
} else {
// Free small object.
switch(compression) {
@@ -1705,12 +1713,9 @@ sweepspan(ParFor *desc, uint32 idx)
}
if(nfree) {
- c->local_by_size[cl].nfree += nfree;
- c->local_alloc -= size * nfree;
- c->local_nfree += nfree;
+ c->local_nsmallfree[cl] += nfree;
c->local_cachealloc -= nfree * size;
- c->local_objects -= nfree;
- runtime·MCentral_FreeSpan(&runtime·mheap->central[cl], s, nfree, head.next, end);
+ runtime·MCentral_FreeSpan(&runtime·mheap.central[cl], s, nfree, head.next, end);
}
}
@@ -1724,10 +1729,10 @@ dumpspan(uint32 idx)
MSpan *s;
bool allocated, special;
- s = runtime·mheap->allspans[idx];
+ s = runtime·mheap.allspans[idx];
if(s->state != MSpanInUse)
return;
- arena_start = runtime·mheap->arena_start;
+ arena_start = runtime·mheap.arena_start;
p = (byte*)(s->start << PageShift);
sizeclass = s->sizeclass;
size = s->elemsize;
@@ -1785,7 +1790,7 @@ runtime·memorydump(void)
{
uint32 spanidx;
- for(spanidx=0; spanidx<runtime·mheap->nspan; spanidx++) {
+ for(spanidx=0; spanidx<runtime·mheap.nspan; spanidx++) {
dumpspan(spanidx);
}
}
@@ -1827,13 +1832,28 @@ runtime·gchelper(void)
static int32 gcpercent = GcpercentUnknown;
static void
-cachestats(GCStats *stats)
+cachestats(void)
+{
+ MCache *c;
+ P *p, **pp;
+
+ for(pp=runtime·allp; p=*pp; pp++) {
+ c = p->mcache;
+ if(c==nil)
+ continue;
+ runtime·purgecachedstats(c);
+ }
+}
+
+static void
+updatememstats(GCStats *stats)
{
M *mp;
+ MSpan *s;
MCache *c;
P *p, **pp;
int32 i;
- uint64 stacks_inuse;
+ uint64 stacks_inuse, smallfree;
uint64 *src, *dst;
if(stats)
@@ -1849,29 +1869,80 @@ cachestats(GCStats *stats)
runtime·memclr((byte*)&mp->gcstats, sizeof(mp->gcstats));
}
}
+ mstats.stacks_inuse = stacks_inuse;
+ mstats.mcache_inuse = runtime·mheap.cachealloc.inuse;
+ mstats.mspan_inuse = runtime·mheap.spanalloc.inuse;
+ mstats.sys = mstats.heap_sys + mstats.stacks_sys + mstats.mspan_sys +
+ mstats.mcache_sys + mstats.buckhash_sys + mstats.gc_sys + mstats.other_sys;
+
+ // Calculate memory allocator stats.
+ // During program execution we only count number of frees and amount of freed memory.
+ // Current number of alive object in the heap and amount of alive heap memory
+ // are calculated by scanning all spans.
+ // Total number of mallocs is calculated as number of frees plus number of alive objects.
+ // Similarly, total amount of allocated memory is calculated as amount of freed memory
+ // plus amount of alive heap memory.
+ mstats.alloc = 0;
+ mstats.total_alloc = 0;
+ mstats.nmalloc = 0;
+ mstats.nfree = 0;
+ for(i = 0; i < nelem(mstats.by_size); i++) {
+ mstats.by_size[i].nmalloc = 0;
+ mstats.by_size[i].nfree = 0;
+ }
+
+ // Flush MCache's to MCentral.
for(pp=runtime·allp; p=*pp; pp++) {
c = p->mcache;
if(c==nil)
continue;
- runtime·purgecachedstats(c);
- for(i=0; i<nelem(c->local_by_size); i++) {
- mstats.by_size[i].nmalloc += c->local_by_size[i].nmalloc;
- c->local_by_size[i].nmalloc = 0;
- mstats.by_size[i].nfree += c->local_by_size[i].nfree;
- c->local_by_size[i].nfree = 0;
+ runtime·MCache_ReleaseAll(c);
+ }
+
+ // Aggregate local stats.
+ cachestats();
+
+ // Scan all spans and count number of alive objects.
+ for(i = 0; i < runtime·mheap.nspan; i++) {
+ s = runtime·mheap.allspans[i];
+ if(s->state != MSpanInUse)
+ continue;
+ if(s->sizeclass == 0) {
+ mstats.nmalloc++;
+ mstats.alloc += s->elemsize;
+ } else {
+ mstats.nmalloc += s->ref;
+ mstats.by_size[s->sizeclass].nmalloc += s->ref;
+ mstats.alloc += s->ref*s->elemsize;
}
}
- mstats.stacks_inuse = stacks_inuse;
+
+ // Aggregate by size class.
+ smallfree = 0;
+ mstats.nfree = runtime·mheap.nlargefree;
+ for(i = 0; i < nelem(mstats.by_size); i++) {
+ mstats.nfree += runtime·mheap.nsmallfree[i];
+ mstats.by_size[i].nfree = runtime·mheap.nsmallfree[i];
+ mstats.by_size[i].nmalloc += runtime·mheap.nsmallfree[i];
+ smallfree += runtime·mheap.nsmallfree[i] * runtime·class_to_size[i];
+ }
+ mstats.nmalloc += mstats.nfree;
+
+ // Calculate derived stats.
+ mstats.total_alloc = mstats.alloc + runtime·mheap.largefree + smallfree;
+ mstats.heap_alloc = mstats.alloc;
+ mstats.heap_objects = mstats.nmalloc - mstats.nfree;
}
// Structure of arguments passed to function gc().
-// This allows the arguments to be passed via reflect·call.
+// This allows the arguments to be passed via runtime·mcall.
struct gc_args
{
- int32 force;
+ int64 start_time; // start time of GC in ns (just before stoptheworld)
};
static void gc(struct gc_args *args);
+static void mgc(G *gp);
static int32
readgogc(void)
@@ -1886,12 +1957,13 @@ readgogc(void)
return runtime·atoi(p);
}
+static FuncVal runfinqv = {runfinq};
+
void
runtime·gc(int32 force)
{
- byte *p;
- struct gc_args a, *ap;
- FuncVal gcv;
+ struct gc_args a;
+ int32 i;
// The atomic operations are not atomic if the uint64s
// are not aligned on uint64 boundaries. This has been
@@ -1909,34 +1981,77 @@ runtime·gc(int32 force)
// problems, don't bother trying to run gc
// while holding a lock. The next mallocgc
// without a lock will do the gc instead.
- if(!mstats.enablegc || m->locks > 0 || runtime·panicking)
+ if(!mstats.enablegc || g == m->g0 || m->locks > 0 || runtime·panicking)
return;
if(gcpercent == GcpercentUnknown) { // first time through
- gcpercent = readgogc();
-
- p = runtime·getenv("GOGCTRACE");
- if(p != nil)
- gctrace = runtime·atoi(p);
+ runtime·lock(&runtime·mheap);
+ if(gcpercent == GcpercentUnknown)
+ gcpercent = readgogc();
+ runtime·unlock(&runtime·mheap);
}
if(gcpercent < 0)
return;
- // Run gc on a bigger stack to eliminate
- // a potentially large number of calls to runtime·morestack.
- a.force = force;
- ap = &a;
- m->moreframesize_minalloc = StackBig;
- gcv.fn = (void*)gc;
- reflect·call(&gcv, (byte*)&ap, sizeof(ap));
-
- if(gctrace > 1 && !force) {
- a.force = 1;
- gc(&a);
+ runtime·semacquire(&runtime·worldsema, false);
+ if(!force && mstats.heap_alloc < mstats.next_gc) {
+ // typically threads which lost the race to grab
+ // worldsema exit here when gc is done.
+ runtime·semrelease(&runtime·worldsema);
+ return;
}
+
+ // Ok, we're doing it! Stop everybody else
+ a.start_time = runtime·nanotime();
+ m->gcing = 1;
+ runtime·stoptheworld();
+
+ // Run gc on the g0 stack. We do this so that the g stack
+ // we're currently running on will no longer change. Cuts
+ // the root set down a bit (g0 stacks are not scanned, and
+ // we don't need to scan gc's internal state). Also an
+ // enabler for copyable stacks.
+ for(i = 0; i < (runtime·debug.gctrace > 1 ? 2 : 1); i++) {
+ // switch to g0, call gc(&a), then switch back
+ g->param = &a;
+ g->status = Gwaiting;
+ g->waitreason = "garbage collection";
+ runtime·mcall(mgc);
+ // record a new start time in case we're going around again
+ a.start_time = runtime·nanotime();
+ }
+
+ // all done
+ m->gcing = 0;
+ m->locks++;
+ runtime·semrelease(&runtime·worldsema);
+ runtime·starttheworld();
+ m->locks--;
+
+ // now that gc is done, kick off finalizer thread if needed
+ if(finq != nil) {
+ runtime·lock(&finlock);
+ // kick off or wake up goroutine to run queued finalizers
+ if(fing == nil)
+ fing = runtime·newproc1(&runfinqv, nil, 0, 0, runtime·gc);
+ else if(fingwait) {
+ fingwait = 0;
+ runtime·ready(fing);
+ }
+ runtime·unlock(&finlock);
+ }
+ // give the queued finalizers, if any, a chance to run
+ runtime·gosched();
}
-static FuncVal runfinqv = {runfinq};
+static void
+mgc(G *gp)
+{
+ gc(gp->param);
+ gp->param = nil;
+ gp->status = Grunning;
+ runtime·gogo(&gp->sched);
+}
static void
gc(struct gc_args *args)
@@ -1948,27 +2063,18 @@ gc(struct gc_args *args)
uint32 i;
Eface eface;
- runtime·semacquire(&runtime·worldsema);
- if(!args->force && mstats.heap_alloc < mstats.next_gc) {
- runtime·semrelease(&runtime·worldsema);
- return;
- }
-
- t0 = runtime·nanotime();
-
- m->gcing = 1;
- runtime·stoptheworld();
+ t0 = args->start_time;
if(CollectStats)
runtime·memclr((byte*)&gcstats, sizeof(gcstats));
for(mp=runtime·allm; mp; mp=mp->alllink)
- runtime·settype_flush(mp, false);
+ runtime·settype_flush(mp);
heap0 = 0;
obj0 = 0;
- if(gctrace) {
- cachestats(nil);
+ if(runtime·debug.gctrace) {
+ updatememstats(nil);
heap0 = mstats.heap_alloc;
obj0 = mstats.nmalloc - mstats.nfree;
}
@@ -1992,7 +2098,7 @@ gc(struct gc_args *args)
work.nproc = runtime·gcprocs();
addroots();
runtime·parforsetup(work.markfor, work.nproc, work.nroot, nil, false, markroot);
- runtime·parforsetup(work.sweepfor, work.nproc, runtime·mheap->nspan, nil, true, sweepspan);
+ runtime·parforsetup(work.sweepfor, work.nproc, runtime·mheap.nspan, nil, true, sweepspan);
if(work.nproc > 1) {
runtime·noteclear(&work.alldone);
runtime·helpgc(work.nproc);
@@ -2018,29 +2124,8 @@ gc(struct gc_args *args)
if(work.nproc > 1)
runtime·notesleep(&work.alldone);
- cachestats(&stats);
-
- stats.nprocyield += work.sweepfor->nprocyield;
- stats.nosyield += work.sweepfor->nosyield;
- stats.nsleep += work.sweepfor->nsleep;
-
+ cachestats();
mstats.next_gc = mstats.heap_alloc+mstats.heap_alloc*gcpercent/100;
- m->gcing = 0;
-
- if(finq != nil) {
- m->locks++; // disable gc during the mallocs in newproc
- // kick off or wake up goroutine to run queued finalizers
- if(fing == nil)
- fing = runtime·newproc1(&runfinqv, nil, 0, 0, runtime·gc);
- else if(fingwait) {
- fingwait = 0;
- runtime·ready(fing);
- }
- m->locks--;
- }
-
- heap1 = mstats.heap_alloc;
- obj1 = mstats.nmalloc - mstats.nfree;
t4 = runtime·nanotime();
mstats.last_gc = t4;
@@ -2050,7 +2135,15 @@ gc(struct gc_args *args)
if(mstats.debuggc)
runtime·printf("pause %D\n", t4-t0);
- if(gctrace) {
+ if(runtime·debug.gctrace) {
+ updatememstats(&stats);
+ heap1 = mstats.heap_alloc;
+ obj1 = mstats.nmalloc - mstats.nfree;
+
+ stats.nprocyield += work.sweepfor->nprocyield;
+ stats.nosyield += work.sweepfor->nosyield;
+ stats.nsleep += work.sweepfor->nsleep;
+
runtime·printf("gc%d(%d): %D+%D+%D ms, %D -> %D MB %D -> %D (%D-%D) objects,"
" %D(%D) handoff, %D(%D) steal, %D/%D/%D yields\n",
mstats.numgc, work.nproc, (t2-t1)/1000000, (t3-t2)/1000000, (t1-t0+t4-t3)/1000000,
@@ -2079,16 +2172,13 @@ gc(struct gc_args *args)
runtime·printf("\ttotal:\t%D\n", ninstr);
runtime·printf("putempty: %D, getfull: %D\n", gcstats.putempty, gcstats.getfull);
+
+ runtime·printf("markonly base lookup: bit %D word %D span %D\n", gcstats.markonly.foundbit, gcstats.markonly.foundword, gcstats.markonly.foundspan);
+ runtime·printf("flushptrbuf base lookup: bit %D word %D span %D\n", gcstats.flushptrbuf.foundbit, gcstats.flushptrbuf.foundword, gcstats.flushptrbuf.foundspan);
}
}
runtime·MProf_GC();
- runtime·semrelease(&runtime·worldsema);
- runtime·starttheworld();
-
- // give the queued finalizers, if any, a chance to run
- if(finq != nil)
- runtime·gosched();
}
void
@@ -2098,14 +2188,16 @@ runtime·ReadMemStats(MStats *stats)
// because stoptheworld can only be used by
// one goroutine at a time, and there might be
// a pending garbage collection already calling it.
- runtime·semacquire(&runtime·worldsema);
+ runtime·semacquire(&runtime·worldsema, false);
m->gcing = 1;
runtime·stoptheworld();
- cachestats(nil);
+ updatememstats(nil);
*stats = mstats;
m->gcing = 0;
+ m->locks++;
runtime·semrelease(&runtime·worldsema);
runtime·starttheworld();
+ m->locks--;
}
void
@@ -2120,7 +2212,7 @@ runtime∕debug·readGCStats(Slice *pauses)
// Pass back: pauses, last gc (absolute time), number of gc, total pause ns.
p = (uint64*)pauses->array;
- runtime·lock(runtime·mheap);
+ runtime·lock(&runtime·mheap);
n = mstats.numgc;
if(n > nelem(mstats.pause_ns))
n = nelem(mstats.pause_ns);
@@ -2135,21 +2227,21 @@ runtime∕debug·readGCStats(Slice *pauses)
p[n] = mstats.last_gc;
p[n+1] = mstats.numgc;
p[n+2] = mstats.pause_total_ns;
- runtime·unlock(runtime·mheap);
+ runtime·unlock(&runtime·mheap);
pauses->len = n+3;
}
void
runtime∕debug·setGCPercent(intgo in, intgo out)
{
- runtime·lock(runtime·mheap);
+ runtime·lock(&runtime·mheap);
if(gcpercent == GcpercentUnknown)
gcpercent = readgogc();
out = gcpercent;
if(in < 0)
in = -1;
gcpercent = in;
- runtime·unlock(runtime·mheap);
+ runtime·unlock(&runtime·mheap);
FLUSH(&out);
}
@@ -2160,6 +2252,8 @@ gchelperstart(void)
runtime·throw("gchelperstart: bad m->helpgc");
if(runtime·xchg(&bufferList[m->helpgc].busy, 1))
runtime·throw("gchelperstart: already busy");
+ if(g != m->g0)
+ runtime·throw("gchelper not running on g0 stack");
}
static void
@@ -2169,39 +2263,57 @@ runfinq(void)
FinBlock *fb, *next;
byte *frame;
uint32 framesz, framecap, i;
+ Eface *ef, ef1;
frame = nil;
framecap = 0;
for(;;) {
- // There's no need for a lock in this section
- // because it only conflicts with the garbage
- // collector, and the garbage collector only
- // runs when everyone else is stopped, and
- // runfinq only stops at the gosched() or
- // during the calls in the for loop.
+ runtime·lock(&finlock);
fb = finq;
finq = nil;
if(fb == nil) {
fingwait = 1;
- runtime·park(nil, nil, "finalizer wait");
+ runtime·park(runtime·unlock, &finlock, "finalizer wait");
continue;
}
+ runtime·unlock(&finlock);
if(raceenabled)
runtime·racefingo();
for(; fb; fb=next) {
next = fb->next;
for(i=0; i<fb->cnt; i++) {
f = &fb->fin[i];
- framesz = sizeof(uintptr) + f->nret;
+ framesz = sizeof(Eface) + f->nret;
if(framecap < framesz) {
runtime·free(frame);
- frame = runtime·mal(framesz);
+ // The frame does not contain pointers interesting for GC,
+ // all not yet finalized objects are stored in finc.
+ // If we do not mark it as FlagNoScan,
+ // the last finalized object is not collected.
+ frame = runtime·mallocgc(framesz, 0, FlagNoScan|FlagNoInvokeGC);
framecap = framesz;
}
- *(void**)frame = f->arg;
- reflect·call(f->fn, frame, sizeof(uintptr) + f->nret);
+ if(f->fint == nil)
+ runtime·throw("missing type in runfinq");
+ if(f->fint->kind == KindPtr) {
+ // direct use of pointer
+ *(void**)frame = f->arg;
+ } else if(((InterfaceType*)f->fint)->mhdr.len == 0) {
+ // convert to empty interface
+ ef = (Eface*)frame;
+ ef->type = f->ot;
+ ef->data = f->arg;
+ } else {
+ // convert to interface with methods, via empty interface.
+ ef1.type = f->ot;
+ ef1.data = f->arg;
+ if(!runtime·ifaceE2I2((InterfaceType*)f->fint, ef1, (Iface*)frame))
+ runtime·throw("invalid type conversion in runfinq");
+ }
+ reflect·call(f->fn, frame, framesz);
f->fn = nil;
f->arg = nil;
+ f->ot = nil;
}
fb->cnt = 0;
fb->next = finc;
@@ -2212,28 +2324,28 @@ runfinq(void)
}
// mark the block at v of size n as allocated.
-// If noptr is true, mark it as having no pointers.
+// If noscan is true, mark it as not needing scanning.
void
-runtime·markallocated(void *v, uintptr n, bool noptr)
+runtime·markallocated(void *v, uintptr n, bool noscan)
{
uintptr *b, obits, bits, off, shift;
if(0)
runtime·printf("markallocated %p+%p\n", v, n);
- if((byte*)v+n > (byte*)runtime·mheap->arena_used || (byte*)v < runtime·mheap->arena_start)
+ if((byte*)v+n > (byte*)runtime·mheap.arena_used || (byte*)v < runtime·mheap.arena_start)
runtime·throw("markallocated: bad pointer");
- off = (uintptr*)v - (uintptr*)runtime·mheap->arena_start; // word offset
- b = (uintptr*)runtime·mheap->arena_start - off/wordsPerBitmapWord - 1;
+ off = (uintptr*)v - (uintptr*)runtime·mheap.arena_start; // word offset
+ b = (uintptr*)runtime·mheap.arena_start - off/wordsPerBitmapWord - 1;
shift = off % wordsPerBitmapWord;
for(;;) {
obits = *b;
bits = (obits & ~(bitMask<<shift)) | (bitAllocated<<shift);
- if(noptr)
- bits |= bitNoPointers<<shift;
- if(runtime·singleproc) {
+ if(noscan)
+ bits |= bitNoScan<<shift;
+ if(runtime·gomaxprocs == 1) {
*b = bits;
break;
} else {
@@ -2251,19 +2363,19 @@ runtime·markfreed(void *v, uintptr n)
uintptr *b, obits, bits, off, shift;
if(0)
- runtime·printf("markallocated %p+%p\n", v, n);
+ runtime·printf("markfreed %p+%p\n", v, n);
- if((byte*)v+n > (byte*)runtime·mheap->arena_used || (byte*)v < runtime·mheap->arena_start)
- runtime·throw("markallocated: bad pointer");
+ if((byte*)v+n > (byte*)runtime·mheap.arena_used || (byte*)v < runtime·mheap.arena_start)
+ runtime·throw("markfreed: bad pointer");
- off = (uintptr*)v - (uintptr*)runtime·mheap->arena_start; // word offset
- b = (uintptr*)runtime·mheap->arena_start - off/wordsPerBitmapWord - 1;
+ off = (uintptr*)v - (uintptr*)runtime·mheap.arena_start; // word offset
+ b = (uintptr*)runtime·mheap.arena_start - off/wordsPerBitmapWord - 1;
shift = off % wordsPerBitmapWord;
for(;;) {
obits = *b;
bits = (obits & ~(bitMask<<shift)) | (bitBlockBoundary<<shift);
- if(runtime·singleproc) {
+ if(runtime·gomaxprocs == 1) {
*b = bits;
break;
} else {
@@ -2283,11 +2395,11 @@ runtime·checkfreed(void *v, uintptr n)
if(!runtime·checking)
return;
- if((byte*)v+n > (byte*)runtime·mheap->arena_used || (byte*)v < runtime·mheap->arena_start)
+ if((byte*)v+n > (byte*)runtime·mheap.arena_used || (byte*)v < runtime·mheap.arena_start)
return; // not allocated, so okay
- off = (uintptr*)v - (uintptr*)runtime·mheap->arena_start; // word offset
- b = (uintptr*)runtime·mheap->arena_start - off/wordsPerBitmapWord - 1;
+ off = (uintptr*)v - (uintptr*)runtime·mheap.arena_start; // word offset
+ b = (uintptr*)runtime·mheap.arena_start - off/wordsPerBitmapWord - 1;
shift = off % wordsPerBitmapWord;
bits = *b>>shift;
@@ -2306,7 +2418,7 @@ runtime·markspan(void *v, uintptr size, uintptr n, bool leftover)
uintptr *b, off, shift;
byte *p;
- if((byte*)v+size*n > (byte*)runtime·mheap->arena_used || (byte*)v < runtime·mheap->arena_start)
+ if((byte*)v+size*n > (byte*)runtime·mheap.arena_used || (byte*)v < runtime·mheap.arena_start)
runtime·throw("markspan: bad pointer");
p = v;
@@ -2317,8 +2429,8 @@ runtime·markspan(void *v, uintptr size, uintptr n, bool leftover)
// the entire span, and each bitmap word has bits for only
// one span, so no other goroutines are changing these
// bitmap words.
- off = (uintptr*)p - (uintptr*)runtime·mheap->arena_start; // word offset
- b = (uintptr*)runtime·mheap->arena_start - off/wordsPerBitmapWord - 1;
+ off = (uintptr*)p - (uintptr*)runtime·mheap.arena_start; // word offset
+ b = (uintptr*)runtime·mheap.arena_start - off/wordsPerBitmapWord - 1;
shift = off % wordsPerBitmapWord;
*b = (*b & ~(bitMask<<shift)) | (bitBlockBoundary<<shift);
}
@@ -2330,14 +2442,14 @@ runtime·unmarkspan(void *v, uintptr n)
{
uintptr *p, *b, off;
- if((byte*)v+n > (byte*)runtime·mheap->arena_used || (byte*)v < runtime·mheap->arena_start)
+ if((byte*)v+n > (byte*)runtime·mheap.arena_used || (byte*)v < runtime·mheap.arena_start)
runtime·throw("markspan: bad pointer");
p = v;
- off = p - (uintptr*)runtime·mheap->arena_start; // word offset
+ off = p - (uintptr*)runtime·mheap.arena_start; // word offset
if(off % wordsPerBitmapWord != 0)
runtime·throw("markspan: unaligned pointer");
- b = (uintptr*)runtime·mheap->arena_start - off/wordsPerBitmapWord - 1;
+ b = (uintptr*)runtime·mheap.arena_start - off/wordsPerBitmapWord - 1;
n /= PtrSize;
if(n%wordsPerBitmapWord != 0)
runtime·throw("unmarkspan: unaligned length");
@@ -2358,8 +2470,8 @@ runtime·blockspecial(void *v)
if(DebugMark)
return true;
- off = (uintptr*)v - (uintptr*)runtime·mheap->arena_start;
- b = (uintptr*)runtime·mheap->arena_start - off/wordsPerBitmapWord - 1;
+ off = (uintptr*)v - (uintptr*)runtime·mheap.arena_start;
+ b = (uintptr*)runtime·mheap.arena_start - off/wordsPerBitmapWord - 1;
shift = off % wordsPerBitmapWord;
return (*b & (bitSpecial<<shift)) != 0;
@@ -2373,8 +2485,8 @@ runtime·setblockspecial(void *v, bool s)
if(DebugMark)
return;
- off = (uintptr*)v - (uintptr*)runtime·mheap->arena_start;
- b = (uintptr*)runtime·mheap->arena_start - off/wordsPerBitmapWord - 1;
+ off = (uintptr*)v - (uintptr*)runtime·mheap.arena_start;
+ b = (uintptr*)runtime·mheap.arena_start - off/wordsPerBitmapWord - 1;
shift = off % wordsPerBitmapWord;
for(;;) {
@@ -2383,7 +2495,7 @@ runtime·setblockspecial(void *v, bool s)
bits = obits | (bitSpecial<<shift);
else
bits = obits & ~(bitSpecial<<shift);
- if(runtime·singleproc) {
+ if(runtime·gomaxprocs == 1) {
*b = bits;
break;
} else {
@@ -2406,10 +2518,10 @@ runtime·MHeap_MapBits(MHeap *h)
uintptr n;
n = (h->arena_used - h->arena_start) / wordsPerBitmapWord;
- n = (n+bitmapChunk-1) & ~(bitmapChunk-1);
+ n = ROUND(n, bitmapChunk);
if(h->bitmap_mapped >= n)
return;
- runtime·SysMap(h->arena_start - n, n - h->bitmap_mapped);
+ runtime·SysMap(h->arena_start - n, n - h->bitmap_mapped, &mstats.gc_sys);
h->bitmap_mapped = n;
}
diff --git a/src/pkg/runtime/mgc0.h b/src/pkg/runtime/mgc0.h
index d14fb37c2..f8abe6c9c 100644
--- a/src/pkg/runtime/mgc0.h
+++ b/src/pkg/runtime/mgc0.h
@@ -26,7 +26,6 @@ enum {
GC_ARRAY_START, // Start an array with a fixed length. Args: (off, len, elemsize)
GC_ARRAY_NEXT, // The next element of an array. Args: none
GC_CALL, // Call a subroutine. Args: (off, objgcrel)
- GC_MAP_PTR, // Go map. Args: (off, MapType*)
GC_CHAN_PTR, // Go channel. Args: (off, ChanType*)
GC_STRING, // Go string. Args: (off)
GC_EFACE, // interface{}. Args: (off)
diff --git a/src/pkg/runtime/mheap.c b/src/pkg/runtime/mheap.c
index f4fbbee7a..fc80c2600 100644
--- a/src/pkg/runtime/mheap.c
+++ b/src/pkg/runtime/mheap.c
@@ -36,12 +36,12 @@ RecordSpan(void *vh, byte *p)
cap = 64*1024/sizeof(all[0]);
if(cap < h->nspancap*3/2)
cap = h->nspancap*3/2;
- all = (MSpan**)runtime·SysAlloc(cap*sizeof(all[0]));
+ all = (MSpan**)runtime·SysAlloc(cap*sizeof(all[0]), &mstats.other_sys);
if(all == nil)
runtime·throw("runtime: cannot allocate memory");
if(h->allspans) {
runtime·memmove(all, h->allspans, h->nspancap*sizeof(all[0]));
- runtime·SysFree(h->allspans, h->nspancap*sizeof(all[0]));
+ runtime·SysFree(h->allspans, h->nspancap*sizeof(all[0]), &mstats.other_sys);
}
h->allspans = all;
h->nspancap = cap;
@@ -51,12 +51,12 @@ RecordSpan(void *vh, byte *p)
// Initialize the heap; fetch memory using alloc.
void
-runtime·MHeap_Init(MHeap *h, void *(*alloc)(uintptr))
+runtime·MHeap_Init(MHeap *h)
{
uint32 i;
- runtime·FixAlloc_Init(&h->spanalloc, sizeof(MSpan), alloc, RecordSpan, h);
- runtime·FixAlloc_Init(&h->cachealloc, sizeof(MCache), alloc, nil, nil);
+ runtime·FixAlloc_Init(&h->spanalloc, sizeof(MSpan), RecordSpan, h, &mstats.mspan_sys);
+ runtime·FixAlloc_Init(&h->cachealloc, sizeof(MCache), nil, nil, &mstats.mcache_sys);
// h->mapcache needs no init
for(i=0; i<nelem(h->free); i++)
runtime·MSpanList_Init(&h->free[i]);
@@ -65,6 +65,23 @@ runtime·MHeap_Init(MHeap *h, void *(*alloc)(uintptr))
runtime·MCentral_Init(&h->central[i], i);
}
+void
+runtime·MHeap_MapSpans(MHeap *h)
+{
+ uintptr n;
+
+ // Map spans array, PageSize at a time.
+ n = (uintptr)h->arena_used;
+ if(sizeof(void*) == 8)
+ n -= (uintptr)h->arena_start;
+ n = n / PageSize * sizeof(h->spans[0]);
+ n = ROUND(n, PageSize);
+ if(h->spans_mapped >= n)
+ return;
+ runtime·SysMap((byte*)h->spans + h->spans_mapped, n - h->spans_mapped, &mstats.other_sys);
+ h->spans_mapped = n;
+}
+
// Allocate a new span of npage pages from the heap
// and record its size class in the HeapMap and HeapMapCache.
MSpan*
@@ -73,7 +90,8 @@ runtime·MHeap_Alloc(MHeap *h, uintptr npage, int32 sizeclass, int32 acct, int32
MSpan *s;
runtime·lock(h);
- runtime·purgecachedstats(m->mcache);
+ mstats.heap_alloc += m->mcache->local_cachealloc;
+ m->mcache->local_cachealloc = 0;
s = MHeap_AllocLocked(h, npage, sizeclass);
if(s != nil) {
mstats.heap_inuse += npage<<PageShift;
@@ -138,6 +156,7 @@ HaveSpan:
// is just a unique constant not seen elsewhere in the
// runtime, as a clue in case it turns up unexpectedly in
// memory or in a stack trace.
+ runtime·SysUsed((void*)(s->start<<PageShift), s->npages<<PageShift);
*(uintptr*)(s->start<<PageShift) = (uintptr)0xbeadbeadbeadbeadULL;
}
s->npreleased = 0;
@@ -145,17 +164,15 @@ HaveSpan:
if(s->npages > npage) {
// Trim extra and put it back in the heap.
t = runtime·FixAlloc_Alloc(&h->spanalloc);
- mstats.mspan_inuse = h->spanalloc.inuse;
- mstats.mspan_sys = h->spanalloc.sys;
runtime·MSpan_Init(t, s->start + npage, s->npages - npage);
s->npages = npage;
p = t->start;
if(sizeof(void*) == 8)
p -= ((uintptr)h->arena_start>>PageShift);
if(p > 0)
- h->map[p-1] = s;
- h->map[p] = t;
- h->map[p+t->npages-1] = t;
+ h->spans[p-1] = s;
+ h->spans[p] = t;
+ h->spans[p+t->npages-1] = t;
*(uintptr*)(t->start<<PageShift) = *(uintptr*)(s->start<<PageShift); // copy "needs zeroing" mark
t->state = MSpanInUse;
MHeap_FreeLocked(h, t);
@@ -172,7 +189,7 @@ HaveSpan:
if(sizeof(void*) == 8)
p -= ((uintptr)h->arena_start>>PageShift);
for(n=0; n<npage; n++)
- h->map[p+n] = s;
+ h->spans[p+n] = s;
return s;
}
@@ -232,19 +249,16 @@ MHeap_Grow(MHeap *h, uintptr npage)
return false;
}
}
- mstats.heap_sys += ask;
// Create a fake "in use" span and free it, so that the
// right coalescing happens.
s = runtime·FixAlloc_Alloc(&h->spanalloc);
- mstats.mspan_inuse = h->spanalloc.inuse;
- mstats.mspan_sys = h->spanalloc.sys;
runtime·MSpan_Init(s, (uintptr)v>>PageShift, ask>>PageShift);
p = s->start;
if(sizeof(void*) == 8)
p -= ((uintptr)h->arena_start>>PageShift);
- h->map[p] = s;
- h->map[p + s->npages - 1] = s;
+ h->spans[p] = s;
+ h->spans[p + s->npages - 1] = s;
s->state = MSpanInUse;
MHeap_FreeLocked(h, s);
return true;
@@ -261,7 +275,7 @@ runtime·MHeap_Lookup(MHeap *h, void *v)
p = (uintptr)v;
if(sizeof(void*) == 8)
p -= (uintptr)h->arena_start;
- return h->map[p >> PageShift];
+ return h->spans[p >> PageShift];
}
// Look up the span at the given address.
@@ -283,10 +297,8 @@ runtime·MHeap_LookupMaybe(MHeap *h, void *v)
q = p;
if(sizeof(void*) == 8)
q -= (uintptr)h->arena_start >> PageShift;
- s = h->map[q];
- if(s == nil || p < s->start || p - s->start >= s->npages)
- return nil;
- if(s->state != MSpanInUse)
+ s = h->spans[q];
+ if(s == nil || p < s->start || v >= s->limit || s->state != MSpanInUse)
return nil;
return s;
}
@@ -296,7 +308,8 @@ void
runtime·MHeap_Free(MHeap *h, MSpan *s, int32 acct)
{
runtime·lock(h);
- runtime·purgecachedstats(m->mcache);
+ mstats.heap_alloc += m->mcache->local_cachealloc;
+ m->mcache->local_cachealloc = 0;
mstats.heap_inuse -= s->npages<<PageShift;
if(acct) {
mstats.heap_alloc -= s->npages<<PageShift;
@@ -313,8 +326,6 @@ MHeap_FreeLocked(MHeap *h, MSpan *s)
MSpan *t;
PageID p;
- if(s->types.sysalloc)
- runtime·settype_sysfree(s);
s->types.compression = MTypes_Empty;
if(s->state != MSpanInUse || s->ref != 0) {
@@ -334,31 +345,31 @@ MHeap_FreeLocked(MHeap *h, MSpan *s)
p = s->start;
if(sizeof(void*) == 8)
p -= (uintptr)h->arena_start >> PageShift;
- if(p > 0 && (t = h->map[p-1]) != nil && t->state != MSpanInUse) {
- tp = (uintptr*)(t->start<<PageShift);
- *tp |= *sp; // propagate "needs zeroing" mark
+ if(p > 0 && (t = h->spans[p-1]) != nil && t->state != MSpanInUse) {
+ if(t->npreleased == 0) { // cant't touch this otherwise
+ tp = (uintptr*)(t->start<<PageShift);
+ *tp |= *sp; // propagate "needs zeroing" mark
+ }
s->start = t->start;
s->npages += t->npages;
s->npreleased = t->npreleased; // absorb released pages
p -= t->npages;
- h->map[p] = s;
+ h->spans[p] = s;
runtime·MSpanList_Remove(t);
t->state = MSpanDead;
runtime·FixAlloc_Free(&h->spanalloc, t);
- mstats.mspan_inuse = h->spanalloc.inuse;
- mstats.mspan_sys = h->spanalloc.sys;
}
- if(p+s->npages < nelem(h->map) && (t = h->map[p+s->npages]) != nil && t->state != MSpanInUse) {
- tp = (uintptr*)(t->start<<PageShift);
- *sp |= *tp; // propagate "needs zeroing" mark
+ if((p+s->npages)*sizeof(h->spans[0]) < h->spans_mapped && (t = h->spans[p+s->npages]) != nil && t->state != MSpanInUse) {
+ if(t->npreleased == 0) { // cant't touch this otherwise
+ tp = (uintptr*)(t->start<<PageShift);
+ *sp |= *tp; // propagate "needs zeroing" mark
+ }
s->npages += t->npages;
s->npreleased += t->npreleased;
- h->map[p + s->npages - 1] = s;
+ h->spans[p + s->npages - 1] = s;
runtime·MSpanList_Remove(t);
t->state = MSpanDead;
runtime·FixAlloc_Free(&h->spanalloc, t);
- mstats.mspan_inuse = h->spanalloc.inuse;
- mstats.mspan_sys = h->spanalloc.sys;
}
// Insert s into appropriate list.
@@ -386,7 +397,7 @@ scavengelist(MSpan *list, uint64 now, uint64 limit)
sumreleased = 0;
for(s=list->next; s != list; s=s->next) {
- if((now - s->unusedsince) > limit) {
+ if((now - s->unusedsince) > limit && s->npreleased != s->npages) {
released = (s->npages - s->npreleased) << PageShift;
mstats.heap_released += released;
sumreleased += released;
@@ -397,19 +408,26 @@ scavengelist(MSpan *list, uint64 now, uint64 limit)
return sumreleased;
}
-static uintptr
-scavenge(uint64 now, uint64 limit)
+static void
+scavenge(int32 k, uint64 now, uint64 limit)
{
uint32 i;
uintptr sumreleased;
MHeap *h;
- h = runtime·mheap;
+ h = &runtime·mheap;
sumreleased = 0;
for(i=0; i < nelem(h->free); i++)
sumreleased += scavengelist(&h->free[i], now, limit);
sumreleased += scavengelist(&h->large, now, limit);
- return sumreleased;
+
+ if(runtime·debug.gctrace > 0) {
+ if(sumreleased > 0)
+ runtime·printf("scvg%d: %D MB released\n", k, (uint64)sumreleased>>20);
+ runtime·printf("scvg%d: inuse: %D, idle: %D, sys: %D, released: %D, consumed: %D (MB)\n",
+ k, mstats.heap_inuse>>20, mstats.heap_idle>>20, mstats.heap_sys>>20,
+ mstats.heap_released>>20, (mstats.heap_sys - mstats.heap_released)>>20);
+ }
}
static FuncVal forcegchelperv = {(void(*)(void))forcegchelper};
@@ -422,10 +440,7 @@ runtime·MHeap_Scavenger(void)
{
MHeap *h;
uint64 tick, now, forcegc, limit;
- uint32 k;
- uintptr sumreleased;
- byte *env;
- bool trace;
+ int32 k;
Note note, *notep;
g->issystem = true;
@@ -442,17 +457,10 @@ runtime·MHeap_Scavenger(void)
else
tick = limit/2;
- trace = false;
- env = runtime·getenv("GOGCTRACE");
- if(env != nil)
- trace = runtime·atoi(env) > 0;
-
- h = runtime·mheap;
+ h = &runtime·mheap;
for(k=0;; k++) {
runtime·noteclear(&note);
- runtime·entersyscallblock();
- runtime·notetsleep(&note, tick);
- runtime·exitsyscall();
+ runtime·notetsleepg(&note, tick);
runtime·lock(h);
now = runtime·nanotime();
@@ -464,24 +472,14 @@ runtime·MHeap_Scavenger(void)
runtime·noteclear(&note);
notep = &note;
runtime·newproc1(&forcegchelperv, (byte*)&notep, sizeof(notep), 0, runtime·MHeap_Scavenger);
- runtime·entersyscallblock();
- runtime·notesleep(&note);
- runtime·exitsyscall();
- if(trace)
+ runtime·notetsleepg(&note, -1);
+ if(runtime·debug.gctrace > 0)
runtime·printf("scvg%d: GC forced\n", k);
runtime·lock(h);
now = runtime·nanotime();
}
- sumreleased = scavenge(now, limit);
+ scavenge(k, now, limit);
runtime·unlock(h);
-
- if(trace) {
- if(sumreleased > 0)
- runtime·printf("scvg%d: %p MB released\n", k, sumreleased>>20);
- runtime·printf("scvg%d: inuse: %D, idle: %D, sys: %D, released: %D, consumed: %D (MB)\n",
- k, mstats.heap_inuse>>20, mstats.heap_idle>>20, mstats.heap_sys>>20,
- mstats.heap_released>>20, (mstats.heap_sys - mstats.heap_released)>>20);
- }
}
}
@@ -489,9 +487,9 @@ void
runtime∕debug·freeOSMemory(void)
{
runtime·gc(1);
- runtime·lock(runtime·mheap);
- scavenge(~(uintptr)0, 0);
- runtime·unlock(runtime·mheap);
+ runtime·lock(&runtime·mheap);
+ scavenge(-1, ~(uintptr)0, 0);
+ runtime·unlock(&runtime·mheap);
}
// Initialize a new span with the given start and npages.
diff --git a/src/pkg/runtime/mkversion.c b/src/pkg/runtime/mkversion.c
deleted file mode 100644
index 94ad0d9e5..000000000
--- a/src/pkg/runtime/mkversion.c
+++ /dev/null
@@ -1,21 +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.
-
-// +build ignore
-
-#include <u.h>
-#include <libc.h>
-
-char *template =
- "// AUTO-GENERATED by autogen.sh; DO NOT EDIT\n\n"
- "package runtime\n"
- "const defaultGoroot = `%s`\n"
- "const theVersion = \"%s\"\n";
-
-void
-main(void)
-{
- print(template, getgoroot(), getgoversion());
- exits(0);
-}
diff --git a/src/pkg/runtime/mprof.goc b/src/pkg/runtime/mprof.goc
index 63334e704..5b92cec95 100644
--- a/src/pkg/runtime/mprof.goc
+++ b/src/pkg/runtime/mprof.goc
@@ -13,44 +13,11 @@ package runtime
#include "type.h"
// NOTE(rsc): Everything here could use cas if contention became an issue.
-static Lock proflock, alloclock;
+static Lock proflock;
// All memory allocations are local and do not escape outside of the profiler.
// The profiler is forbidden from referring to garbage-collected memory.
-static byte *pool; // memory allocation pool
-static uintptr poolfree; // number of bytes left in the pool
-enum {
- Chunk = 32*PageSize, // initial size of the pool
-};
-
-// Memory allocation local to this file.
-// There is no way to return the allocated memory back to the OS.
-static void*
-allocate(uintptr size)
-{
- void *v;
-
- if(size == 0)
- return nil;
-
- if(size >= Chunk/2)
- return runtime·SysAlloc(size);
-
- runtime·lock(&alloclock);
- if(size > poolfree) {
- pool = runtime·SysAlloc(Chunk);
- if(pool == nil)
- runtime·throw("runtime: cannot allocate memory");
- poolfree = Chunk;
- }
- v = pool;
- pool += size;
- poolfree -= size;
- runtime·unlock(&alloclock);
- return v;
-}
-
enum { MProf, BProf }; // profile types
// Per-call-stack profiling information.
@@ -103,10 +70,9 @@ stkbucket(int32 typ, uintptr *stk, int32 nstk, bool alloc)
Bucket *b;
if(buckhash == nil) {
- buckhash = runtime·SysAlloc(BuckHashSize*sizeof buckhash[0]);
+ buckhash = runtime·SysAlloc(BuckHashSize*sizeof buckhash[0], &mstats.buckhash_sys);
if(buckhash == nil)
runtime·throw("runtime: cannot allocate memory");
- mstats.buckhash_sys += BuckHashSize*sizeof buckhash[0];
}
// Hash stack.
@@ -128,9 +94,7 @@ stkbucket(int32 typ, uintptr *stk, int32 nstk, bool alloc)
if(!alloc)
return nil;
- b = allocate(sizeof *b + nstk*sizeof stk[0]);
- if(b == nil)
- runtime·throw("runtime: cannot allocate memory");
+ b = runtime·persistentalloc(sizeof *b + nstk*sizeof stk[0], 0, &mstats.buckhash_sys);
bucketmem += sizeof *b + nstk*sizeof stk[0];
runtime·memmove(b->stk, stk, nstk*sizeof stk[0]);
b->typ = typ;
@@ -232,7 +196,7 @@ setaddrbucket(uintptr addr, Bucket *b)
if(ah->addr == (addr>>AddrHashShift))
goto found;
- ah = allocate(sizeof *ah);
+ ah = runtime·persistentalloc(sizeof *ah, 0, &mstats.buckhash_sys);
addrmem += sizeof *ah;
ah->next = addrhash[h];
ah->addr = addr>>AddrHashShift;
@@ -240,7 +204,7 @@ setaddrbucket(uintptr addr, Bucket *b)
found:
if((e = addrfree) == nil) {
- e = allocate(64*sizeof *e);
+ e = runtime·persistentalloc(64*sizeof *e, 0, &mstats.buckhash_sys);
addrmem += 64*sizeof *e;
for(i=0; i+1<64; i++)
e[i].next = &e[i+1];
@@ -291,10 +255,6 @@ runtime·MProf_Malloc(void *p, uintptr size)
uintptr stk[32];
Bucket *b;
- if(m->nomemprof > 0)
- return;
-
- m->nomemprof++;
nstk = runtime·callers(1, stk, 32);
runtime·lock(&proflock);
b = stkbucket(MProf, stk, nstk, true);
@@ -302,7 +262,6 @@ runtime·MProf_Malloc(void *p, uintptr size)
b->recent_alloc_bytes += size;
setaddrbucket((uintptr)p, b);
runtime·unlock(&proflock);
- m->nomemprof--;
}
// Called when freeing a profiled block.
@@ -311,10 +270,6 @@ runtime·MProf_Free(void *p, uintptr size)
{
Bucket *b;
- if(m->nomemprof > 0)
- return;
-
- m->nomemprof++;
runtime·lock(&proflock);
b = getaddrbucket((uintptr)p);
if(b != nil) {
@@ -322,7 +277,6 @@ runtime·MProf_Free(void *p, uintptr size)
b->recent_free_bytes += size;
}
runtime·unlock(&proflock);
- m->nomemprof--;
}
int64 runtime·blockprofilerate; // in CPU ticks
@@ -330,7 +284,17 @@ int64 runtime·blockprofilerate; // in CPU ticks
void
runtime·SetBlockProfileRate(intgo rate)
{
- runtime·atomicstore64((uint64*)&runtime·blockprofilerate, rate * runtime·tickspersecond() / (1000*1000*1000));
+ int64 r;
+
+ if(rate <= 0)
+ r = 0; // disable profiling
+ else {
+ // convert ns to cycles, use float64 to prevent overflow during multiplication
+ r = (float64)rate*runtime·tickspersecond()/(1000*1000*1000);
+ if(r == 0)
+ r = 1;
+ }
+ runtime·atomicstore64((uint64*)&runtime·blockprofilerate, r);
}
void
@@ -476,13 +440,13 @@ func ThreadCreateProfile(p Slice) (n int, ok bool) {
}
func Stack(b Slice, all bool) (n int) {
- byte *pc, *sp;
+ uintptr pc, sp;
sp = runtime·getcallersp(&b);
- pc = runtime·getcallerpc(&b);
+ pc = (uintptr)runtime·getcallerpc(&b);
if(all) {
- runtime·semacquire(&runtime·worldsema);
+ runtime·semacquire(&runtime·worldsema, false);
m->gcing = 1;
runtime·stoptheworld();
}
@@ -509,27 +473,27 @@ func Stack(b Slice, all bool) (n int) {
}
static void
-saveg(byte *pc, byte *sp, G *gp, TRecord *r)
+saveg(uintptr pc, uintptr sp, G *gp, TRecord *r)
{
int32 n;
- n = runtime·gentraceback(pc, sp, 0, gp, 0, r->stk, nelem(r->stk), nil, nil);
+ n = runtime·gentraceback((uintptr)pc, (uintptr)sp, 0, gp, 0, r->stk, nelem(r->stk), nil, nil, false);
if(n < nelem(r->stk))
r->stk[n] = 0;
}
func GoroutineProfile(b Slice) (n int, ok bool) {
- byte *pc, *sp;
+ uintptr pc, sp;
TRecord *r;
G *gp;
sp = runtime·getcallersp(&b);
- pc = runtime·getcallerpc(&b);
+ pc = (uintptr)runtime·getcallerpc(&b);
ok = false;
n = runtime·gcount();
if(n <= b.len) {
- runtime·semacquire(&runtime·worldsema);
+ runtime·semacquire(&runtime·worldsema, false);
m->gcing = 1;
runtime·stoptheworld();
@@ -541,7 +505,7 @@ func GoroutineProfile(b Slice) (n int, ok bool) {
for(gp = runtime·allg; gp != nil; gp = gp->alllink) {
if(gp == g || gp->status == Gdead)
continue;
- saveg(gp->sched.pc, (byte*)gp->sched.sp, gp, r++);
+ saveg(gp->sched.pc, gp->sched.sp, gp, r++);
}
}
@@ -554,5 +518,5 @@ func GoroutineProfile(b Slice) (n int, ok bool) {
void
runtime·mprofinit(void)
{
- addrhash = allocate((1<<AddrHashBits)*sizeof *addrhash);
+ addrhash = runtime·persistentalloc((1<<AddrHashBits)*sizeof *addrhash, 0, &mstats.buckhash_sys);
}
diff --git a/src/pkg/runtime/msize.c b/src/pkg/runtime/msize.c
index e6cfcdb02..50b372b61 100644
--- a/src/pkg/runtime/msize.c
+++ b/src/pkg/runtime/msize.c
@@ -31,7 +31,6 @@
int32 runtime·class_to_size[NumSizeClasses];
int32 runtime·class_to_allocnpages[NumSizeClasses];
-int32 runtime·class_to_transfercount[NumSizeClasses];
// The SizeToClass lookup is implemented using two arrays,
// one mapping sizes <= 1024 to their class and one mapping
@@ -42,17 +41,17 @@ int32 runtime·class_to_transfercount[NumSizeClasses];
// size divided by 128 (rounded up). The arrays are filled in
// by InitSizes.
-static int32 size_to_class8[1024/8 + 1];
-static int32 size_to_class128[(MaxSmallSize-1024)/128 + 1];
+int8 runtime·size_to_class8[1024/8 + 1];
+int8 runtime·size_to_class128[(MaxSmallSize-1024)/128 + 1];
-int32
-runtime·SizeToClass(int32 size)
+static int32
+SizeToClass(int32 size)
{
if(size > MaxSmallSize)
runtime·throw("SizeToClass - invalid size");
if(size > 1024-8)
- return size_to_class128[(size-1024+127) >> 7];
- return size_to_class8[(size+7)>>3];
+ return runtime·size_to_class128[(size-1024+127) >> 7];
+ return runtime·size_to_class8[(size+7)>>3];
}
void
@@ -111,16 +110,16 @@ runtime·InitSizes(void)
nextsize = 0;
for (sizeclass = 1; sizeclass < NumSizeClasses; sizeclass++) {
for(; nextsize < 1024 && nextsize <= runtime·class_to_size[sizeclass]; nextsize+=8)
- size_to_class8[nextsize/8] = sizeclass;
+ runtime·size_to_class8[nextsize/8] = sizeclass;
if(nextsize >= 1024)
for(; nextsize <= runtime·class_to_size[sizeclass]; nextsize += 128)
- size_to_class128[(nextsize-1024)/128] = sizeclass;
+ runtime·size_to_class128[(nextsize-1024)/128] = sizeclass;
}
// Double-check SizeToClass.
if(0) {
for(n=0; n < MaxSmallSize; n++) {
- sizeclass = runtime·SizeToClass(n);
+ sizeclass = SizeToClass(n);
if(sizeclass < 1 || sizeclass >= NumSizeClasses || runtime·class_to_size[sizeclass] < n) {
runtime·printf("size=%d sizeclass=%d runtime·class_to_size=%d\n", n, sizeclass, runtime·class_to_size[sizeclass]);
runtime·printf("incorrect SizeToClass");
@@ -137,16 +136,6 @@ runtime·InitSizes(void)
// Copy out for statistics table.
for(i=0; i<nelem(runtime·class_to_size); i++)
mstats.by_size[i].size = runtime·class_to_size[i];
-
- // Initialize the runtime·class_to_transfercount table.
- for(sizeclass = 1; sizeclass < NumSizeClasses; sizeclass++) {
- n = 64*1024 / runtime·class_to_size[sizeclass];
- if(n < 2)
- n = 2;
- if(n > 32)
- n = 32;
- runtime·class_to_transfercount[sizeclass] = n;
- }
return;
dump:
@@ -157,12 +146,14 @@ dump:
runtime·printf(" %d", runtime·class_to_size[sizeclass]);
runtime·printf("\n\n");
runtime·printf("size_to_class8:");
- for(i=0; i<nelem(size_to_class8); i++)
- runtime·printf(" %d=>%d(%d)\n", i*8, size_to_class8[i], runtime·class_to_size[size_to_class8[i]]);
+ for(i=0; i<nelem(runtime·size_to_class8); i++)
+ runtime·printf(" %d=>%d(%d)\n", i*8, runtime·size_to_class8[i],
+ runtime·class_to_size[runtime·size_to_class8[i]]);
runtime·printf("\n");
runtime·printf("size_to_class128:");
- for(i=0; i<nelem(size_to_class128); i++)
- runtime·printf(" %d=>%d(%d)\n", i*128, size_to_class128[i], runtime·class_to_size[size_to_class128[i]]);
+ for(i=0; i<nelem(runtime·size_to_class128); i++)
+ runtime·printf(" %d=>%d(%d)\n", i*128, runtime·size_to_class128[i],
+ runtime·class_to_size[runtime·size_to_class128[i]]);
runtime·printf("\n");
}
runtime·throw("InitSizes failed");
diff --git a/src/pkg/runtime/netpoll.goc b/src/pkg/runtime/netpoll.goc
index 06b6d6172..d27bef167 100644
--- a/src/pkg/runtime/netpoll.goc
+++ b/src/pkg/runtime/netpoll.goc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin linux
+// +build darwin dragonfly freebsd linux netbsd openbsd windows
package net
@@ -14,7 +14,7 @@ package net
// Integrated network poller (platform-independent part).
// A particular implementation (epoll/kqueue) must define the following functions:
// void runtime·netpollinit(void); // to initialize the poller
-// int32 runtime·netpollopen(int32 fd, PollDesc *pd); // to arm edge-triggered notifications
+// int32 runtime·netpollopen(uintptr fd, PollDesc *pd); // to arm edge-triggered notifications
// and associate fd with pd.
// An implementation must call the following function to denote that the pd is ready.
// void runtime·netpollready(G **gpp, PollDesc *pd, int32 mode);
@@ -25,7 +25,7 @@ struct PollDesc
{
PollDesc* link; // in pollcache, protected by pollcache.Lock
Lock; // protectes the following fields
- int32 fd;
+ uintptr fd;
bool closing;
uintptr seq; // protects from stale timers and ready notifications
G* rg; // G waiting for read or READY (binary semaphore)
@@ -47,8 +47,8 @@ static struct
// seq is incremented when deadlines are changed or descriptor is reused.
} pollcache;
-static void netpollblock(PollDesc*, int32);
-static G* netpollunblock(PollDesc*, int32);
+static bool netpollblock(PollDesc*, int32);
+static G* netpollunblock(PollDesc*, int32, bool);
static void deadline(int64, Eface);
static void readDeadline(int64, Eface);
static void writeDeadline(int64, Eface);
@@ -63,7 +63,7 @@ func runtime_pollServerInit() {
runtime·netpollinit();
}
-func runtime_pollOpen(fd int) (pd *PollDesc, errno int) {
+func runtime_pollOpen(fd uintptr) (pd *PollDesc, errno int) {
pd = allocPollDesc();
runtime·lock(pd);
if(pd->wg != nil && pd->wg != READY)
@@ -112,18 +112,35 @@ ret:
func runtime_pollWait(pd *PollDesc, mode int) (err int) {
runtime·lock(pd);
err = checkerr(pd, mode);
- if(err)
- goto ret;
- netpollblock(pd, mode);
- err = checkerr(pd, mode);
-ret:
+ if(err == 0) {
+ while(!netpollblock(pd, mode)) {
+ err = checkerr(pd, mode);
+ if(err != 0)
+ break;
+ // Can happen if timeout has fired and unblocked us,
+ // but before we had a chance to run, timeout has been reset.
+ // Pretend it has not happened and retry.
+ }
+ }
+ runtime·unlock(pd);
+}
+
+func runtime_pollWaitCanceled(pd *PollDesc, mode int) {
+ runtime·lock(pd);
+ // wait for ioready, ignore closing or timeouts.
+ while(!netpollblock(pd, mode))
+ ;
runtime·unlock(pd);
}
func runtime_pollSetDeadline(pd *PollDesc, d int64, mode int) {
+ G *rg, *wg;
+
runtime·lock(pd);
- if(pd->closing)
- goto ret;
+ if(pd->closing) {
+ runtime·unlock(pd);
+ return;
+ }
pd->seq++; // invalidate current timers
// Reset current timers.
if(pd->rt.fv) {
@@ -135,9 +152,8 @@ func runtime_pollSetDeadline(pd *PollDesc, d int64, mode int) {
pd->wt.fv = nil;
}
// Setup new timers.
- if(d != 0 && d <= runtime·nanotime()) {
+ if(d != 0 && d <= runtime·nanotime())
d = -1;
- }
if(mode == 'r' || mode == 'r'+'w')
pd->rd = d;
if(mode == 'w' || mode == 'r'+'w')
@@ -167,8 +183,18 @@ func runtime_pollSetDeadline(pd *PollDesc, d int64, mode int) {
runtime·addtimer(&pd->wt);
}
}
-ret:
+ // If we set the new deadline in the past, unblock currently pending IO if any.
+ rg = nil;
+ wg = nil;
+ if(pd->rd < 0)
+ rg = netpollunblock(pd, 'r', false);
+ if(pd->wd < 0)
+ wg = netpollunblock(pd, 'w', false);
runtime·unlock(pd);
+ if(rg)
+ runtime·ready(rg);
+ if(wg)
+ runtime·ready(wg);
}
func runtime_pollUnblock(pd *PollDesc) {
@@ -179,8 +205,8 @@ func runtime_pollUnblock(pd *PollDesc) {
runtime·throw("runtime_pollUnblock: already closing");
pd->closing = true;
pd->seq++;
- rg = netpollunblock(pd, 'r');
- wg = netpollunblock(pd, 'w');
+ rg = netpollunblock(pd, 'r', false);
+ wg = netpollunblock(pd, 'w', false);
if(pd->rt.fv) {
runtime·deltimer(&pd->rt);
pd->rt.fv = nil;
@@ -196,6 +222,12 @@ func runtime_pollUnblock(pd *PollDesc) {
runtime·ready(wg);
}
+uintptr
+runtime·netpollfd(PollDesc *pd)
+{
+ return pd->fd;
+}
+
// make pd ready, newly runnable goroutines (if any) are enqueued info gpp list
void
runtime·netpollready(G **gpp, PollDesc *pd, int32 mode)
@@ -205,9 +237,9 @@ runtime·netpollready(G **gpp, PollDesc *pd, int32 mode)
rg = wg = nil;
runtime·lock(pd);
if(mode == 'r' || mode == 'r'+'w')
- rg = netpollunblock(pd, 'r');
+ rg = netpollunblock(pd, 'r', true);
if(mode == 'w' || mode == 'r'+'w')
- wg = netpollunblock(pd, 'w');
+ wg = netpollunblock(pd, 'w', true);
runtime·unlock(pd);
if(rg) {
rg->schedlink = *gpp;
@@ -229,7 +261,8 @@ checkerr(PollDesc *pd, int32 mode)
return 0;
}
-static void
+// returns true if IO is ready, or false if timedout or closed
+static bool
netpollblock(PollDesc *pd, int32 mode)
{
G **gpp;
@@ -239,17 +272,20 @@ netpollblock(PollDesc *pd, int32 mode)
gpp = &pd->wg;
if(*gpp == READY) {
*gpp = nil;
- return;
+ return true;
}
if(*gpp != nil)
- runtime·throw("epoll: double wait");
+ runtime·throw("netpollblock: double wait");
*gpp = g;
runtime·park(runtime·unlock, &pd->Lock, "IO wait");
runtime·lock(pd);
+ if(g->param)
+ return true;
+ return false;
}
static G*
-netpollunblock(PollDesc *pd, int32 mode)
+netpollunblock(PollDesc *pd, int32 mode, bool ioready)
{
G **gpp, *old;
@@ -259,10 +295,15 @@ netpollunblock(PollDesc *pd, int32 mode)
if(*gpp == READY)
return nil;
if(*gpp == nil) {
- *gpp = READY;
+ // Only set READY for ioready. runtime_pollWait
+ // will check for timeout/cancel before waiting.
+ if(ioready)
+ *gpp = READY;
return nil;
}
old = *gpp;
+ // pass unblock reason onto blocked g
+ old->param = (void*)ioready;
*gpp = nil;
return old;
}
@@ -291,14 +332,14 @@ deadlineimpl(int64 now, Eface arg, bool read, bool write)
runtime·throw("deadlineimpl: inconsistent read deadline");
pd->rd = -1;
pd->rt.fv = nil;
- rg = netpollunblock(pd, 'r');
+ rg = netpollunblock(pd, 'r', false);
}
if(write) {
if(pd->wd <= 0 || (pd->wt.fv == nil && !read))
runtime·throw("deadlineimpl: inconsistent write deadline");
pd->wd = -1;
pd->wt.fv = nil;
- wg = netpollunblock(pd, 'w');
+ wg = netpollunblock(pd, 'w', false);
}
runtime·unlock(pd);
if(rg)
@@ -338,7 +379,7 @@ allocPollDesc(void)
n = 1;
// Must be in non-GC memory because can be referenced
// only from epoll/kqueue internals.
- pd = runtime·SysAlloc(n*sizeof(*pd));
+ pd = runtime·persistentalloc(n*sizeof(*pd), 0, &mstats.other_sys);
for(i = 0; i < n; i++) {
pd[i].link = pollcache.first;
pollcache.first = &pd[i];
diff --git a/src/pkg/runtime/netpoll_epoll.c b/src/pkg/runtime/netpoll_epoll.c
index 9b5980700..885ac5e4d 100644
--- a/src/pkg/runtime/netpoll_epoll.c
+++ b/src/pkg/runtime/netpoll_epoll.c
@@ -31,24 +31,24 @@ runtime·netpollinit(void)
}
int32
-runtime·netpollopen(int32 fd, PollDesc *pd)
+runtime·netpollopen(uintptr fd, PollDesc *pd)
{
EpollEvent ev;
int32 res;
ev.events = EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLET;
ev.data = (uint64)pd;
- res = runtime·epollctl(epfd, EPOLL_CTL_ADD, fd, &ev);
+ res = runtime·epollctl(epfd, EPOLL_CTL_ADD, (int32)fd, &ev);
return -res;
}
int32
-runtime·netpollclose(int32 fd)
+runtime·netpollclose(uintptr fd)
{
EpollEvent ev;
int32 res;
- res = runtime·epollctl(epfd, EPOLL_CTL_DEL, fd, &ev);
+ res = runtime·epollctl(epfd, EPOLL_CTL_DEL, (int32)fd, &ev);
return -res;
}
diff --git a/src/pkg/runtime/netpoll_kqueue.c b/src/pkg/runtime/netpoll_kqueue.c
index 0ed03d31f..afc8d6859 100644
--- a/src/pkg/runtime/netpoll_kqueue.c
+++ b/src/pkg/runtime/netpoll_kqueue.c
@@ -2,10 +2,11 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin
+// +build darwin dragonfly freebsd netbsd openbsd
#include "runtime.h"
#include "defs_GOOS_GOARCH.h"
+#include "os_GOOS.h"
// Integrated network poller (kqueue-based implementation).
@@ -27,7 +28,7 @@ runtime·netpollinit(void)
}
int32
-runtime·netpollopen(int32 fd, PollDesc *pd)
+runtime·netpollopen(uintptr fd, PollDesc *pd)
{
Kevent ev[2];
int32 n;
@@ -35,30 +36,22 @@ runtime·netpollopen(int32 fd, PollDesc *pd)
// Arm both EVFILT_READ and EVFILT_WRITE in edge-triggered mode (EV_CLEAR)
// for the whole fd lifetime. The notifications are automatically unregistered
// when fd is closed.
- ev[0].ident = fd;
+ ev[0].ident = (uint32)fd;
ev[0].filter = EVFILT_READ;
- ev[0].flags = EV_ADD|EV_RECEIPT|EV_CLEAR;
+ ev[0].flags = EV_ADD|EV_CLEAR;
ev[0].fflags = 0;
ev[0].data = 0;
- ev[0].udata = (byte*)pd;
+ ev[0].udata = (kevent_udata)pd;
ev[1] = ev[0];
ev[1].filter = EVFILT_WRITE;
- n = runtime·kevent(kq, ev, 2, ev, 2, nil);
+ n = runtime·kevent(kq, ev, 2, nil, 0, nil);
if(n < 0)
return -n;
- if(n != 2 ||
- (ev[0].flags&EV_ERROR) == 0 || ev[0].ident != fd || ev[0].filter != EVFILT_READ ||
- (ev[1].flags&EV_ERROR) == 0 || ev[1].ident != fd || ev[1].filter != EVFILT_WRITE)
- return EFAULT; // just to mark out from other errors
- if(ev[0].data != 0)
- return ev[0].data;
- if(ev[1].data != 0)
- return ev[1].data;
return 0;
}
int32
-runtime·netpollclose(int32 fd)
+runtime·netpollclose(uintptr fd)
{
// Don't need to unregister because calling close()
// on fd will remove any kevents that reference the descriptor.
@@ -74,7 +67,7 @@ runtime·netpoll(bool block)
static int32 lasterr;
Kevent events[64], *ev;
Timespec ts, *tp;
- int32 n, i;
+ int32 n, i, mode;
G *gp;
if(kq == -1)
@@ -97,10 +90,13 @@ retry:
}
for(i = 0; i < n; i++) {
ev = &events[i];
+ mode = 0;
if(ev->filter == EVFILT_READ)
- runtime·netpollready(&gp, (PollDesc*)ev->udata, 'r');
+ mode += 'r';
if(ev->filter == EVFILT_WRITE)
- runtime·netpollready(&gp, (PollDesc*)ev->udata, 'w');
+ mode += 'w';
+ if(mode)
+ runtime·netpollready(&gp, (PollDesc*)ev->udata, mode);
}
if(block && gp == nil)
goto retry;
diff --git a/src/pkg/runtime/netpoll_stub.c b/src/pkg/runtime/netpoll_stub.c
index 39d19a4ce..b7a8f2944 100644
--- a/src/pkg/runtime/netpoll_stub.c
+++ b/src/pkg/runtime/netpoll_stub.c
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build freebsd netbsd openbsd plan9 windows
+// +build plan9
#include "runtime.h"
diff --git a/src/pkg/runtime/netpoll_windows.c b/src/pkg/runtime/netpoll_windows.c
new file mode 100644
index 000000000..b510a41e2
--- /dev/null
+++ b/src/pkg/runtime/netpoll_windows.c
@@ -0,0 +1,148 @@
+// Copyright 2013 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 "runtime.h"
+#include "defs_GOOS_GOARCH.h"
+#include "os_GOOS.h"
+
+#define DWORD_MAX 0xffffffff
+
+#pragma dynimport runtime·CreateIoCompletionPort CreateIoCompletionPort "kernel32.dll"
+#pragma dynimport runtime·GetQueuedCompletionStatus GetQueuedCompletionStatus "kernel32.dll"
+#pragma dynimport runtime·WSAGetOverlappedResult WSAGetOverlappedResult "ws2_32.dll"
+
+extern void *runtime·CreateIoCompletionPort;
+extern void *runtime·GetQueuedCompletionStatus;
+extern void *runtime·WSAGetOverlappedResult;
+
+#define INVALID_HANDLE_VALUE ((uintptr)-1)
+
+// net_op must be the same as beginning of net.operation. Keep these in sync.
+typedef struct net_op net_op;
+struct net_op
+{
+ // used by windows
+ Overlapped o;
+ // used by netpoll
+ PollDesc* pd;
+ int32 mode;
+ int32 errno;
+ uint32 qty;
+};
+
+typedef struct OverlappedEntry OverlappedEntry;
+struct OverlappedEntry
+{
+ uintptr key;
+ net_op* op; // In reality it's Overlapped*, but we cast it to net_op* anyway.
+ uintptr internal;
+ uint32 qty;
+};
+
+static void handlecompletion(G **gpp, net_op *o, int32 errno, uint32 qty);
+
+static uintptr iocphandle = INVALID_HANDLE_VALUE; // completion port io handle
+
+void
+runtime·netpollinit(void)
+{
+ iocphandle = (uintptr)runtime·stdcall(runtime·CreateIoCompletionPort, 4, INVALID_HANDLE_VALUE, (uintptr)0, (uintptr)0, (uintptr)DWORD_MAX);
+ if(iocphandle == 0) {
+ runtime·printf("netpoll: failed to create iocp handle (errno=%d)\n", runtime·getlasterror());
+ runtime·throw("netpoll: failed to create iocp handle");
+ }
+ return;
+}
+
+int32
+runtime·netpollopen(uintptr fd, PollDesc *pd)
+{
+ USED(pd);
+ if(runtime·stdcall(runtime·CreateIoCompletionPort, 4, fd, iocphandle, (uintptr)0, (uintptr)0) == 0)
+ return -runtime·getlasterror();
+ return 0;
+}
+
+int32
+runtime·netpollclose(uintptr fd)
+{
+ // nothing to do
+ USED(fd);
+ return 0;
+}
+
+// Polls for completed network IO.
+// Returns list of goroutines that become runnable.
+G*
+runtime·netpoll(bool block)
+{
+ OverlappedEntry entries[64];
+ uint32 wait, qty, key, flags, n, i;
+ int32 errno;
+ net_op *op;
+ G *gp;
+
+ if(iocphandle == INVALID_HANDLE_VALUE)
+ return nil;
+ gp = nil;
+ wait = 0;
+ if(block)
+ wait = INFINITE;
+retry:
+ if(runtime·GetQueuedCompletionStatusEx != nil) {
+ n = nelem(entries) / runtime·gomaxprocs;
+ if(n < 8)
+ n = 8;
+ if(runtime·stdcall(runtime·GetQueuedCompletionStatusEx, 6, iocphandle, entries, (uintptr)n, &n, (uintptr)wait, (uintptr)0) == 0) {
+ errno = runtime·getlasterror();
+ if(!block && errno == WAIT_TIMEOUT)
+ return nil;
+ runtime·printf("netpoll: GetQueuedCompletionStatusEx failed (errno=%d)\n", errno);
+ runtime·throw("netpoll: GetQueuedCompletionStatusEx failed");
+ }
+ for(i = 0; i < n; i++) {
+ op = entries[i].op;
+ errno = 0;
+ qty = 0;
+ if(runtime·stdcall(runtime·WSAGetOverlappedResult, 5, runtime·netpollfd(op->pd), op, &qty, (uintptr)0, (uintptr)&flags) == 0)
+ errno = runtime·getlasterror();
+ handlecompletion(&gp, op, errno, qty);
+ }
+ } else {
+ op = nil;
+ errno = 0;
+ qty = 0;
+ if(runtime·stdcall(runtime·GetQueuedCompletionStatus, 5, iocphandle, &qty, &key, &op, (uintptr)wait) == 0) {
+ errno = runtime·getlasterror();
+ if(!block && errno == WAIT_TIMEOUT)
+ return nil;
+ if(op == nil) {
+ runtime·printf("netpoll: GetQueuedCompletionStatus failed (errno=%d)\n", errno);
+ runtime·throw("netpoll: GetQueuedCompletionStatus failed");
+ }
+ // dequeued failed IO packet, so report that
+ }
+ handlecompletion(&gp, op, errno, qty);
+ }
+ if(block && gp == nil)
+ goto retry;
+ return gp;
+}
+
+static void
+handlecompletion(G **gpp, net_op *op, int32 errno, uint32 qty)
+{
+ int32 mode;
+
+ if(op == nil)
+ runtime·throw("netpoll: GetQueuedCompletionStatus returned op == nil");
+ mode = op->mode;
+ if(mode != 'r' && mode != 'w') {
+ runtime·printf("netpoll: GetQueuedCompletionStatus returned invalid mode=%d\n", mode);
+ runtime·throw("netpoll: GetQueuedCompletionStatus returned invalid mode");
+ }
+ op->errno = errno;
+ op->qty = qty;
+ runtime·netpollready(gpp, op->pd, mode);
+}
diff --git a/src/pkg/runtime/noasm_arm.goc b/src/pkg/runtime/noasm_arm.goc
new file mode 100644
index 000000000..fe3591e8a
--- /dev/null
+++ b/src/pkg/runtime/noasm_arm.goc
@@ -0,0 +1,74 @@
+// Copyright 2013 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.
+
+// Routines that are implemented in assembly in asm_{amd64,386}.s
+// but are implemented in C for arm.
+
+package runtime
+#include "runtime.h"
+#include "../../cmd/ld/textflag.h"
+
+#pragma textflag NOSPLIT
+func cmpstring(s1 String, s2 String) (v int) {
+ uintgo i, l;
+ byte c1, c2;
+
+ l = s1.len;
+ if(s2.len < l)
+ l = s2.len;
+ for(i=0; i<l; i++) {
+ c1 = s1.str[i];
+ c2 = s2.str[i];
+ if(c1 < c2) {
+ v = -1;
+ goto done;
+ }
+ if(c1 > c2) {
+ v = +1;
+ goto done;
+ }
+ }
+ if(s1.len < s2.len) {
+ v = -1;
+ goto done;
+ }
+ if(s1.len > s2.len) {
+ v = +1;
+ goto done;
+ }
+ v = 0;
+ done:;
+}
+
+#pragma textflag NOSPLIT
+func bytes·Compare(s1 Slice, s2 Slice) (v int) {
+ uintgo i, l;
+ byte c1, c2;
+
+ l = s1.len;
+ if(s2.len < l)
+ l = s2.len;
+ for(i=0; i<l; i++) {
+ c1 = s1.array[i];
+ c2 = s2.array[i];
+ if(c1 < c2) {
+ v = -1;
+ goto done;
+ }
+ if(c1 > c2) {
+ v = +1;
+ goto done;
+ }
+ }
+ if(s1.len < s2.len) {
+ v = -1;
+ goto done;
+ }
+ if(s1.len > s2.len) {
+ v = +1;
+ goto done;
+ }
+ v = 0;
+ done:;
+}
diff --git a/src/pkg/runtime/norace_test.go b/src/pkg/runtime/norace_test.go
new file mode 100644
index 000000000..a3d5b0086
--- /dev/null
+++ b/src/pkg/runtime/norace_test.go
@@ -0,0 +1,58 @@
+// Copyright 2013 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 file contains tests that can not run under race detector for some reason.
+// +build !race
+
+package runtime_test
+
+import (
+ "runtime"
+ "sync/atomic"
+ "testing"
+)
+
+// Syscall tests split stack between Entersyscall and Exitsyscall under race detector.
+func BenchmarkSyscall(b *testing.B) {
+ benchmarkSyscall(b, 0, 1)
+}
+
+func BenchmarkSyscallWork(b *testing.B) {
+ benchmarkSyscall(b, 100, 1)
+}
+
+func BenchmarkSyscallExcess(b *testing.B) {
+ benchmarkSyscall(b, 0, 4)
+}
+
+func BenchmarkSyscallExcessWork(b *testing.B) {
+ benchmarkSyscall(b, 100, 4)
+}
+
+func benchmarkSyscall(b *testing.B, work, excess int) {
+ const CallsPerSched = 1000
+ procs := runtime.GOMAXPROCS(-1) * excess
+ N := int32(b.N / CallsPerSched)
+ c := make(chan bool, procs)
+ for p := 0; p < procs; p++ {
+ go func() {
+ foo := 42
+ for atomic.AddInt32(&N, -1) >= 0 {
+ runtime.Gosched()
+ for g := 0; g < CallsPerSched; g++ {
+ runtime.Entersyscall()
+ for i := 0; i < work; i++ {
+ foo *= 2
+ foo /= 2
+ }
+ runtime.Exitsyscall()
+ }
+ }
+ c <- foo == 42
+ }()
+ }
+ for p := 0; p < procs; p++ {
+ <-c
+ }
+}
diff --git a/src/pkg/runtime/os_darwin.c b/src/pkg/runtime/os_darwin.c
index 276362a97..9eb1b4626 100644
--- a/src/pkg/runtime/os_darwin.c
+++ b/src/pkg/runtime/os_darwin.c
@@ -7,12 +7,12 @@
#include "os_GOOS.h"
#include "signal_unix.h"
#include "stack.h"
+#include "../../cmd/ld/textflag.h"
extern SigTab runtime·sigtab[];
static Sigset sigset_none;
static Sigset sigset_all = ~(Sigset)0;
-static Sigset sigset_prof = 1<<(SIGPROF-1);
static void
unimplemented(int8 *name)
@@ -22,19 +22,6 @@ unimplemented(int8 *name)
*(int32*)1231 = 1231;
}
-int32
-runtime·semasleep(int64 ns)
-{
- int32 v;
-
- if(m->profilehz > 0)
- runtime·setprof(false);
- v = runtime·mach_semacquire(m->waitsema, ns);
- if(m->profilehz > 0)
- runtime·setprof(true);
- return v;
-}
-
void
runtime·semawakeup(M *mp)
{
@@ -142,7 +129,6 @@ runtime·minit(void)
runtime·signalstack((byte*)m->gsignal->stackguard - StackGuard, 32*1024);
runtime·sigprocmask(SIG_SETMASK, &sigset_none, nil);
- runtime·setprof(m->profilehz > 0);
}
// Called from dropm to undo the effect of an minit.
@@ -155,10 +141,15 @@ runtime·unminit(void)
// Mach IPC, to get at semaphores
// Definitions are in /usr/include/mach on a Mac.
+#pragma textflag NOSPLIT
static void
macherror(int32 r, int8 *fn)
{
- runtime·printf("mach error %s: %d\n", fn, r);
+ runtime·prints("mach error ");
+ runtime·prints(fn);
+ runtime·prints(": ");
+ runtime·printint(r);
+ runtime·prints("\n");
runtime·throw("mach error");
}
@@ -405,25 +396,22 @@ int32 runtime·mach_semaphore_timedwait(uint32 sema, uint32 sec, uint32 nsec);
int32 runtime·mach_semaphore_signal(uint32 sema);
int32 runtime·mach_semaphore_signal_all(uint32 sema);
+#pragma textflag NOSPLIT
int32
-runtime·mach_semacquire(uint32 sem, int64 ns)
+runtime·semasleep(int64 ns)
{
- int32 r;
- int64 secs;
+ int32 r, secs, nsecs;
if(ns >= 0) {
- secs = ns/1000000000LL;
- // Avoid overflow
- if(secs > 1LL<<30)
- secs = 1LL<<30;
- r = runtime·mach_semaphore_timedwait(sem, secs, ns%1000000000LL);
+ secs = runtime·timediv(ns, 1000000000, &nsecs);
+ r = runtime·mach_semaphore_timedwait(m->waitsema, secs, nsecs);
if(r == KERN_ABORTED || r == KERN_OPERATION_TIMED_OUT)
return -1;
if(r != 0)
macherror(r, "semaphore_wait");
return 0;
}
- while((r = runtime·mach_semaphore_wait(sem)) != 0) {
+ while((r = runtime·mach_semaphore_wait(m->waitsema)) != 0) {
if(r == KERN_ABORTED) // interrupted
continue;
macherror(r, "semaphore_wait");
@@ -475,7 +463,7 @@ runtime·sigpanic(void)
runtime·panicstring(runtime·sigtab[g->sig].name);
}
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·osyield(void)
{
@@ -492,70 +480,6 @@ runtime·memlimit(void)
return 0;
}
-// NOTE(rsc): On OS X, when the CPU profiling timer expires, the SIGPROF
-// signal is not guaranteed to be sent to the thread that was executing to
-// cause it to expire. It can and often does go to a sleeping thread, which is
-// not interesting for our profile. This is filed Apple Bug Report #9177434,
-// copied to http://code.google.com/p/go/source/detail?r=35b716c94225.
-// To work around this bug, we disable receipt of the profiling signal on
-// a thread while in blocking system calls. This forces the kernel to deliver
-// the profiling signal to an executing thread.
-//
-// The workaround fails on OS X machines using a 64-bit Snow Leopard kernel.
-// In that configuration, the kernel appears to want to deliver SIGPROF to the
-// sleeping threads regardless of signal mask and, worse, does not deliver
-// the signal until the thread wakes up on its own.
-//
-// If necessary, we can switch to using ITIMER_REAL for OS X and handle
-// the kernel-generated SIGALRM by generating our own SIGALRMs to deliver
-// to all the running threads. SIGALRM does not appear to be affected by
-// the 64-bit Snow Leopard bug. However, as of this writing Mountain Lion
-// is in preview, making Snow Leopard two versions old, so it is unclear how
-// much effort we need to spend on one buggy kernel.
-
-// Control whether profiling signal can be delivered to this thread.
-void
-runtime·setprof(bool on)
-{
- if(on)
- runtime·sigprocmask(SIG_UNBLOCK, &sigset_prof, nil);
- else
- runtime·sigprocmask(SIG_BLOCK, &sigset_prof, nil);
-}
-
-static int8 badcallback[] = "runtime: cgo callback on thread not created by Go.\n";
-
-// This runs on a foreign stack, without an m or a g. No stack split.
-#pragma textflag 7
-void
-runtime·badcallback(void)
-{
- runtime·write(2, badcallback, sizeof badcallback - 1);
-}
-
-static int8 badsignal[] = "runtime: signal received on thread not created by Go: ";
-
-// This runs on a foreign stack, without an m or a g. No stack split.
-#pragma textflag 7
-void
-runtime·badsignal(int32 sig)
-{
- int32 len;
-
- if (sig == SIGPROF) {
- return; // Ignore SIGPROFs intended for a non-Go thread.
- }
- runtime·write(2, badsignal, sizeof badsignal - 1);
- if (0 <= sig && sig < NSIG) {
- // Can't call findnull() because it will split stack.
- for(len = 0; runtime·sigtab[sig].name[len]; len++)
- ;
- runtime·write(2, runtime·sigtab[sig].name, len);
- }
- runtime·write(2, "\n", 1);
- runtime·exit(1);
-}
-
void
runtime·setsig(int32 i, GoSighandler *fn, bool restart)
{
diff --git a/src/pkg/runtime/os_darwin.h b/src/pkg/runtime/os_darwin.h
index 802410975..b4f49e023 100644
--- a/src/pkg/runtime/os_darwin.h
+++ b/src/pkg/runtime/os_darwin.h
@@ -4,6 +4,8 @@
#define SS_DISABLE 4
+typedef byte* kevent_udata;
+
int32 runtime·bsdthread_create(void*, M*, G*, void(*)(void));
int32 runtime·bsdthread_register(void);
int32 runtime·mach_msg_trap(MachHeader*, int32, uint32, uint32, uint32, uint32, uint32);
diff --git a/src/pkg/runtime/os_dragonfly.c b/src/pkg/runtime/os_dragonfly.c
new file mode 100644
index 000000000..cf427b78c
--- /dev/null
+++ b/src/pkg/runtime/os_dragonfly.c
@@ -0,0 +1,282 @@
+// 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 "runtime.h"
+#include "defs_GOOS_GOARCH.h"
+#include "os_GOOS.h"
+#include "signal_unix.h"
+#include "stack.h"
+#include "../../cmd/ld/textflag.h"
+
+extern SigTab runtime·sigtab[];
+extern int32 runtime·sys_umtx_sleep(uint32*, int32, int32);
+extern int32 runtime·sys_umtx_wakeup(uint32*, int32);
+
+// From DragonFly's <sys/sysctl.h>
+#define CTL_HW 6
+#define HW_NCPU 3
+
+static Sigset sigset_none;
+static Sigset sigset_all = { ~(uint32)0, ~(uint32)0, ~(uint32)0, ~(uint32)0, };
+
+static int32
+getncpu(void)
+{
+ uint32 mib[2];
+ uint32 out;
+ int32 ret;
+ uintptr nout;
+
+ // Fetch hw.ncpu via sysctl.
+ mib[0] = CTL_HW;
+ mib[1] = HW_NCPU;
+ nout = sizeof out;
+ out = 0;
+ ret = runtime·sysctl(mib, 2, (byte*)&out, &nout, nil, 0);
+ if(ret >= 0)
+ return out;
+ else
+ return 1;
+}
+
+#pragma textflag NOSPLIT
+void
+runtime·futexsleep(uint32 *addr, uint32 val, int64 ns)
+{
+ int32 timeout = 0;
+ int32 ret;
+
+ if(ns >= 0) {
+ // The timeout is specified in microseconds - ensure that we
+ // do not end up dividing to zero, which would put us to sleep
+ // indefinitely...
+ timeout = runtime·timediv(ns, 1000, nil);
+ if(timeout == 0)
+ timeout = 1;
+ }
+
+ // sys_umtx_sleep will return EWOULDBLOCK (EAGAIN) when the timeout
+ // expires or EBUSY if the mutex value does not match.
+ ret = runtime·sys_umtx_sleep(addr, val, timeout);
+ if(ret >= 0 || ret == -EINTR || ret == -EAGAIN || ret == -EBUSY)
+ return;
+
+ runtime·prints("umtx_wait addr=");
+ runtime·printpointer(addr);
+ runtime·prints(" val=");
+ runtime·printint(val);
+ runtime·prints(" ret=");
+ runtime·printint(ret);
+ runtime·prints("\n");
+ *(int32*)0x1005 = 0x1005;
+}
+
+void
+runtime·futexwakeup(uint32 *addr, uint32 cnt)
+{
+ int32 ret;
+
+ ret = runtime·sys_umtx_wakeup(addr, cnt);
+ if(ret >= 0)
+ return;
+
+ runtime·printf("umtx_wake addr=%p ret=%d\n", addr, ret);
+ *(int32*)0x1006 = 0x1006;
+}
+
+void runtime·lwp_start(void*);
+
+void
+runtime·newosproc(M *mp, void *stk)
+{
+ Lwpparams params;
+ Sigset oset;
+
+ if(0){
+ runtime·printf("newosproc stk=%p m=%p g=%p id=%d/%d ostk=%p\n",
+ stk, mp, mp->g0, mp->id, (int32)mp->tls[0], &mp);
+ }
+
+ runtime·sigprocmask(&sigset_all, &oset);
+ runtime·memclr((byte*)&params, sizeof params);
+
+ params.func = runtime·lwp_start;
+ params.arg = (byte*)mp;
+ params.stack = (byte*)stk;
+ params.tid1 = (int32*)&mp->procid;
+ params.tid2 = nil;
+
+ mp->tls[0] = mp->id; // so 386 asm can find it
+
+ runtime·lwp_create(&params);
+ runtime·sigprocmask(&oset, nil);
+}
+
+void
+runtime·osinit(void)
+{
+ runtime·ncpu = getncpu();
+}
+
+void
+runtime·get_random_data(byte **rnd, int32 *rnd_len)
+{
+ static byte urandom_data[HashRandomBytes];
+ int32 fd;
+ fd = runtime·open("/dev/urandom", 0 /* O_RDONLY */, 0);
+ if(runtime·read(fd, urandom_data, HashRandomBytes) == HashRandomBytes) {
+ *rnd = urandom_data;
+ *rnd_len = HashRandomBytes;
+ } else {
+ *rnd = nil;
+ *rnd_len = 0;
+ }
+ runtime·close(fd);
+}
+
+void
+runtime·goenvs(void)
+{
+ runtime·goenvs_unix();
+}
+
+// Called to initialize a new m (including the bootstrap m).
+// Called on the parent thread (main thread in case of bootstrap), can allocate memory.
+void
+runtime·mpreinit(M *mp)
+{
+ mp->gsignal = runtime·malg(32*1024);
+}
+
+// Called to initialize a new m (including the bootstrap m).
+// Called on the new thread, can not allocate memory.
+void
+runtime·minit(void)
+{
+ // Initialize signal handling
+ runtime·signalstack((byte*)m->gsignal->stackguard - StackGuard, 32*1024);
+ runtime·sigprocmask(&sigset_none, nil);
+}
+
+// Called from dropm to undo the effect of an minit.
+void
+runtime·unminit(void)
+{
+ runtime·signalstack(nil, 0);
+}
+
+void
+runtime·sigpanic(void)
+{
+ switch(g->sig) {
+ case SIGBUS:
+ if(g->sigcode0 == BUS_ADRERR && g->sigcode1 < 0x1000) {
+ if(g->sigpc == 0)
+ runtime·panicstring("call of nil func value");
+ runtime·panicstring("invalid memory address or nil pointer dereference");
+ }
+ runtime·printf("unexpected fault address %p\n", g->sigcode1);
+ runtime·throw("fault");
+ case SIGSEGV:
+ if((g->sigcode0 == 0 || g->sigcode0 == SEGV_MAPERR || g->sigcode0 == SEGV_ACCERR) && g->sigcode1 < 0x1000) {
+ if(g->sigpc == 0)
+ runtime·panicstring("call of nil func value");
+ runtime·panicstring("invalid memory address or nil pointer dereference");
+ }
+ runtime·printf("unexpected fault address %p\n", g->sigcode1);
+ runtime·throw("fault");
+ case SIGFPE:
+ switch(g->sigcode0) {
+ case FPE_INTDIV:
+ runtime·panicstring("integer divide by zero");
+ case FPE_INTOVF:
+ runtime·panicstring("integer overflow");
+ }
+ runtime·panicstring("floating point error");
+ }
+ runtime·panicstring(runtime·sigtab[g->sig].name);
+}
+
+uintptr
+runtime·memlimit(void)
+{
+ Rlimit rl;
+ extern byte text[], end[];
+ uintptr used;
+
+ if(runtime·getrlimit(RLIMIT_AS, &rl) != 0)
+ return 0;
+ if(rl.rlim_cur >= 0x7fffffff)
+ return 0;
+
+ // Estimate our VM footprint excluding the heap.
+ // Not an exact science: use size of binary plus
+ // some room for thread stacks.
+ used = end - text + (64<<20);
+ if(used >= rl.rlim_cur)
+ return 0;
+
+ // If there's not at least 16 MB left, we're probably
+ // not going to be able to do much. Treat as no limit.
+ rl.rlim_cur -= used;
+ if(rl.rlim_cur < (16<<20))
+ return 0;
+
+ return rl.rlim_cur - used;
+}
+
+extern void runtime·sigtramp(void);
+
+typedef struct sigaction {
+ union {
+ void (*__sa_handler)(int32);
+ void (*__sa_sigaction)(int32, Siginfo*, void *);
+ } __sigaction_u; /* signal handler */
+ int32 sa_flags; /* see signal options below */
+ Sigset sa_mask; /* signal mask to apply */
+} Sigaction;
+
+void
+runtime·setsig(int32 i, GoSighandler *fn, 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.__bits[0] = ~(uint32)0;
+ sa.sa_mask.__bits[1] = ~(uint32)0;
+ sa.sa_mask.__bits[2] = ~(uint32)0;
+ sa.sa_mask.__bits[3] = ~(uint32)0;
+ if(fn == runtime·sighandler)
+ fn = (void*)runtime·sigtramp;
+ sa.__sigaction_u.__sa_sigaction = (void*)fn;
+ runtime·sigaction(i, &sa, nil);
+}
+
+GoSighandler*
+runtime·getsig(int32 i)
+{
+ Sigaction sa;
+
+ runtime·memclr((byte*)&sa, sizeof sa);
+ runtime·sigaction(i, nil, &sa);
+ if((void*)sa.__sigaction_u.__sa_sigaction == runtime·sigtramp)
+ return runtime·sighandler;
+ return (void*)sa.__sigaction_u.__sa_sigaction;
+}
+
+void
+runtime·signalstack(byte *p, int32 n)
+{
+ StackT st;
+
+ st.ss_sp = (void*)p;
+ st.ss_size = n;
+ st.ss_flags = 0;
+ if(p == nil)
+ st.ss_flags = SS_DISABLE;
+ runtime·sigaltstack(&st, nil);
+}
diff --git a/src/pkg/runtime/os_dragonfly.h b/src/pkg/runtime/os_dragonfly.h
new file mode 100644
index 000000000..ebbd0eb15
--- /dev/null
+++ b/src/pkg/runtime/os_dragonfly.h
@@ -0,0 +1,28 @@
+// 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.
+
+#define SS_DISABLE 4
+
+typedef byte* kevent_udata;
+
+int32 runtime·lwp_create(Lwpparams*);
+void runtime·sigpanic(void);
+void runtime·sigaltstack(Sigaltstack*, Sigaltstack*);
+struct sigaction;
+void runtime·sigaction(int32, struct sigaction*, struct sigaction*);
+void runtime·sigprocmask(Sigset *, Sigset *);
+void runtime·setitimer(int32, Itimerval*, Itimerval*);
+int32 runtime·sysctl(uint32*, uint32, byte*, uintptr*, byte*, uintptr);
+
+
+#define NSIG 33
+#define SI_USER 0x10001
+
+#define RLIMIT_AS 10
+typedef struct Rlimit Rlimit;
+struct Rlimit {
+ int64 rlim_cur;
+ int64 rlim_max;
+};
+int32 runtime·getrlimit(int32, Rlimit*);
diff --git a/src/pkg/runtime/os_freebsd.c b/src/pkg/runtime/os_freebsd.c
index f454ab349..042097bdd 100644
--- a/src/pkg/runtime/os_freebsd.c
+++ b/src/pkg/runtime/os_freebsd.c
@@ -7,6 +7,7 @@
#include "os_GOOS.h"
#include "signal_unix.h"
#include "stack.h"
+#include "../../cmd/ld/textflag.h"
extern SigTab runtime·sigtab[];
extern int32 runtime·sys_umtx_op(uint32*, int32, uint32, void*, void*);
@@ -41,30 +42,34 @@ getncpu(void)
// FreeBSD's umtx_op syscall is effectively the same as Linux's futex, and
// thus the code is largely similar. See linux/thread.c and lock_futex.c for comments.
+#pragma textflag NOSPLIT
void
runtime·futexsleep(uint32 *addr, uint32 val, int64 ns)
{
int32 ret;
- Timespec ts, *tsp;
- int64 secs;
-
- if(ns < 0)
- tsp = nil;
- else {
- secs = ns / 1000000000LL;
- // Avoid overflow
- if(secs > 1LL<<30)
- secs = 1LL<<30;
- ts.tv_sec = secs;
- ts.tv_nsec = ns % 1000000000LL;
- tsp = &ts;
- }
+ Timespec ts;
- ret = runtime·sys_umtx_op(addr, UMTX_OP_WAIT_UINT, val, nil, tsp);
+ if(ns < 0) {
+ ret = runtime·sys_umtx_op(addr, UMTX_OP_WAIT_UINT, val, nil, nil);
+ if(ret >= 0 || ret == -EINTR)
+ return;
+ goto fail;
+ }
+ // NOTE: tv_nsec is int64 on amd64, so this assumes a little-endian system.
+ ts.tv_nsec = 0;
+ ts.tv_sec = runtime·timediv(ns, 1000000000, (int32*)&ts.tv_nsec);
+ ret = runtime·sys_umtx_op(addr, UMTX_OP_WAIT_UINT, val, nil, &ts);
if(ret >= 0 || ret == -EINTR)
return;
- runtime·printf("umtx_wait addr=%p val=%d ret=%d\n", addr, val, ret);
+fail:
+ runtime·prints("umtx_wait addr=");
+ runtime·printpointer(addr);
+ runtime·prints(" val=");
+ runtime·printint(val);
+ runtime·prints(" ret=");
+ runtime·printint(ret);
+ runtime·prints("\n");
*(int32*)0x1005 = 0x1005;
}
@@ -229,45 +234,6 @@ runtime·memlimit(void)
return rl.rlim_cur - used;
}
-void
-runtime·setprof(bool on)
-{
- USED(on);
-}
-
-static int8 badcallback[] = "runtime: cgo callback on thread not created by Go.\n";
-
-// This runs on a foreign stack, without an m or a g. No stack split.
-#pragma textflag 7
-void
-runtime·badcallback(void)
-{
- runtime·write(2, badcallback, sizeof badcallback - 1);
-}
-
-static int8 badsignal[] = "runtime: signal received on thread not created by Go: ";
-
-// This runs on a foreign stack, without an m or a g. No stack split.
-#pragma textflag 7
-void
-runtime·badsignal(int32 sig)
-{
- int32 len;
-
- if (sig == SIGPROF) {
- return; // Ignore SIGPROFs intended for a non-Go thread.
- }
- runtime·write(2, badsignal, sizeof badsignal - 1);
- if (0 <= sig && sig < NSIG) {
- // Can't call findnull() because it will split stack.
- for(len = 0; runtime·sigtab[sig].name[len]; len++)
- ;
- runtime·write(2, runtime·sigtab[sig].name, len);
- }
- runtime·write(2, "\n", 1);
- runtime·exit(1);
-}
-
extern void runtime·sigtramp(void);
typedef struct sigaction {
diff --git a/src/pkg/runtime/os_freebsd.h b/src/pkg/runtime/os_freebsd.h
index e9be1362c..c1853e65d 100644
--- a/src/pkg/runtime/os_freebsd.h
+++ b/src/pkg/runtime/os_freebsd.h
@@ -4,6 +4,8 @@
#define SS_DISABLE 4
+typedef byte* kevent_udata;
+
int32 runtime·thr_new(ThrParam*, int32);
void runtime·sigpanic(void);
void runtime·sigaltstack(Sigaltstack*, Sigaltstack*);
diff --git a/src/pkg/runtime/os_freebsd_arm.c b/src/pkg/runtime/os_freebsd_arm.c
index 7eaa45c44..1fa235b01 100644
--- a/src/pkg/runtime/os_freebsd_arm.c
+++ b/src/pkg/runtime/os_freebsd_arm.c
@@ -5,6 +5,7 @@
#include "runtime.h"
#include "defs_GOOS_GOARCH.h"
#include "os_GOOS.h"
+#include "../../cmd/ld/textflag.h"
void
runtime·checkgoarm(void)
@@ -12,7 +13,7 @@ runtime·checkgoarm(void)
// TODO(minux)
}
-#pragma textflag 7
+#pragma textflag NOSPLIT
int64
runtime·cputicks(void)
{
diff --git a/src/pkg/runtime/os_linux.c b/src/pkg/runtime/os_linux.c
index 6b86d2b17..cb45fe8ce 100644
--- a/src/pkg/runtime/os_linux.c
+++ b/src/pkg/runtime/os_linux.c
@@ -7,6 +7,7 @@
#include "os_GOOS.h"
#include "signal_unix.h"
#include "stack.h"
+#include "../../cmd/ld/textflag.h"
extern SigTab runtime·sigtab[];
@@ -32,30 +33,26 @@ enum
// if(*addr == val) sleep
// Might be woken up spuriously; that's allowed.
// Don't sleep longer than ns; ns < 0 means forever.
+#pragma textflag NOSPLIT
void
runtime·futexsleep(uint32 *addr, uint32 val, int64 ns)
{
- Timespec ts, *tsp;
- int64 secs;
-
- if(ns < 0)
- tsp = nil;
- else {
- secs = ns/1000000000LL;
- // Avoid overflow
- if(secs > 1LL<<30)
- secs = 1LL<<30;
- ts.tv_sec = secs;
- ts.tv_nsec = ns%1000000000LL;
- tsp = &ts;
- }
+ Timespec ts;
// Some Linux kernels have a bug where futex of
// FUTEX_WAIT returns an internal error code
// as an errno. Libpthread ignores the return value
// here, and so can we: as it says a few lines up,
// spurious wakeups are allowed.
- runtime·futex(addr, FUTEX_WAIT, val, tsp, nil, 0);
+
+ if(ns < 0) {
+ runtime·futex(addr, FUTEX_WAIT, val, nil, nil, 0);
+ return;
+ }
+ // NOTE: tv_nsec is int64 on amd64, so this assumes a little-endian system.
+ ts.tv_nsec = 0;
+ ts.tv_sec = runtime·timediv(ns, 1000000000LL, (int32*)&ts.tv_nsec);
+ runtime·futex(addr, FUTEX_WAIT, val, &ts, nil, 0);
}
// If any procs are sleeping on addr, wake up at most cnt.
@@ -172,6 +169,7 @@ runtime·get_random_data(byte **rnd, int32 *rnd_len)
*rnd = runtime·startup_random_data;
*rnd_len = runtime·startup_random_data_len;
} else {
+ #pragma dataflag NOPTR
static byte urandom_data[HashRandomBytes];
int32 fd;
fd = runtime·open("/dev/urandom", 0 /* O_RDONLY */, 0);
@@ -277,45 +275,6 @@ runtime·memlimit(void)
return rl.rlim_cur - used;
}
-void
-runtime·setprof(bool on)
-{
- USED(on);
-}
-
-static int8 badcallback[] = "runtime: cgo callback on thread not created by Go.\n";
-
-// This runs on a foreign stack, without an m or a g. No stack split.
-#pragma textflag 7
-void
-runtime·badcallback(void)
-{
- runtime·write(2, badcallback, sizeof badcallback - 1);
-}
-
-static int8 badsignal[] = "runtime: signal received on thread not created by Go: ";
-
-// This runs on a foreign stack, without an m or a g. No stack split.
-#pragma textflag 7
-void
-runtime·badsignal(int32 sig)
-{
- int32 len;
-
- if (sig == SIGPROF) {
- return; // Ignore SIGPROFs intended for a non-Go thread.
- }
- runtime·write(2, badsignal, sizeof badsignal - 1);
- if (0 <= sig && sig < NSIG) {
- // Can't call findnull() because it will split stack.
- for(len = 0; runtime·sigtab[sig].name[len]; len++)
- ;
- runtime·write(2, runtime·sigtab[sig].name, len);
- }
- runtime·write(2, "\n", 1);
- runtime·exit(1);
-}
-
#ifdef GOARCH_386
#define sa_handler k_sa_handler
#endif
diff --git a/src/pkg/runtime/os_linux_386.c b/src/pkg/runtime/os_linux_386.c
index 18becb6e6..ad7281464 100644
--- a/src/pkg/runtime/os_linux_386.c
+++ b/src/pkg/runtime/os_linux_386.c
@@ -5,13 +5,14 @@
#include "runtime.h"
#include "defs_GOOS_GOARCH.h"
#include "os_GOOS.h"
+#include "../../cmd/ld/textflag.h"
#define AT_NULL 0
#define AT_RANDOM 25
#define AT_SYSINFO 32
extern uint32 runtime·_vdso;
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·linux_setup_vdso(int32 argc, byte **argv)
{
diff --git a/src/pkg/runtime/os_linux_arm.c b/src/pkg/runtime/os_linux_arm.c
index dd0fa9415..570b3f0be 100644
--- a/src/pkg/runtime/os_linux_arm.c
+++ b/src/pkg/runtime/os_linux_arm.c
@@ -5,6 +5,7 @@
#include "runtime.h"
#include "defs_GOOS_GOARCH.h"
#include "os_GOOS.h"
+#include "../../cmd/ld/textflag.h"
#define AT_NULL 0
#define AT_PLATFORM 15 // introduced in at least 2.6.11
@@ -32,18 +33,15 @@ runtime·checkgoarm(void)
}
}
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
-runtime·setup_auxv(int32 argc, void *argv_list)
+runtime·setup_auxv(int32 argc, byte **argv)
{
- byte **argv;
byte **envp;
byte *rnd;
uint32 *auxv;
uint32 t;
- argv = &argv_list;
-
// skip envp to get to ELF auxiliary vector.
for(envp = &argv[argc+1]; *envp != nil; envp++)
;
@@ -71,7 +69,7 @@ runtime·setup_auxv(int32 argc, void *argv_list)
}
}
-#pragma textflag 7
+#pragma textflag NOSPLIT
int64
runtime·cputicks(void)
{
diff --git a/src/pkg/runtime/os_netbsd.c b/src/pkg/runtime/os_netbsd.c
index 7679ec255..a49dca295 100644
--- a/src/pkg/runtime/os_netbsd.c
+++ b/src/pkg/runtime/os_netbsd.c
@@ -7,6 +7,7 @@
#include "os_GOOS.h"
#include "signal_unix.h"
#include "stack.h"
+#include "../../cmd/ld/textflag.h"
enum
{
@@ -62,6 +63,7 @@ runtime·semacreate(void)
return 1;
}
+#pragma textflag NOSPLIT
int32
runtime·semasleep(int64 ns)
{
@@ -93,9 +95,10 @@ runtime·semasleep(int64 ns)
runtime·atomicstore(&m->waitsemalock, 0);
runtime·lwp_park(nil, 0, &m->waitsemacount, nil);
} else {
- ns += runtime·nanotime();
- ts.tv_sec = ns/1000000000LL;
- ts.tv_nsec = ns%1000000000LL;
+ ns = ns + runtime·nanotime();
+ // NOTE: tv_nsec is int64 on amd64, so this assumes a little-endian system.
+ ts.tv_nsec = 0;
+ ts.tv_sec = runtime·timediv(ns, 1000000000, (int32*)&ts.tv_nsec);
// TODO(jsing) - potential deadlock!
// See above for details.
runtime·atomicstore(&m->waitsemalock, 0);
@@ -269,45 +272,6 @@ runtime·memlimit(void)
return 0;
}
-void
-runtime·setprof(bool on)
-{
- USED(on);
-}
-
-static int8 badcallback[] = "runtime: cgo callback on thread not created by Go.\n";
-
-// This runs on a foreign stack, without an m or a g. No stack split.
-#pragma textflag 7
-void
-runtime·badcallback(void)
-{
- runtime·write(2, badcallback, sizeof badcallback - 1);
-}
-
-static int8 badsignal[] = "runtime: signal received on thread not created by Go: ";
-
-// This runs on a foreign stack, without an m or a g. No stack split.
-#pragma textflag 7
-void
-runtime·badsignal(int32 sig)
-{
- int32 len;
-
- if (sig == SIGPROF) {
- return; // Ignore SIGPROFs intended for a non-Go thread.
- }
- runtime·write(2, badsignal, sizeof badsignal - 1);
- if (0 <= sig && sig < NSIG) {
- // Can't call findnull() because it will split stack.
- for(len = 0; runtime·sigtab[sig].name[len]; len++)
- ;
- runtime·write(2, runtime·sigtab[sig].name, len);
- }
- runtime·write(2, "\n", 1);
- runtime·exit(1);
-}
-
extern void runtime·sigtramp(void);
typedef struct sigaction {
diff --git a/src/pkg/runtime/os_netbsd.h b/src/pkg/runtime/os_netbsd.h
index c193ae0b4..55743c8d5 100644
--- a/src/pkg/runtime/os_netbsd.h
+++ b/src/pkg/runtime/os_netbsd.h
@@ -8,6 +8,8 @@
#define SIG_UNBLOCK 2
#define SIG_SETMASK 3
+typedef uintptr kevent_udata;
+
struct sigaction;
void runtime·sigpanic(void);
diff --git a/src/pkg/runtime/os_netbsd_arm.c b/src/pkg/runtime/os_netbsd_arm.c
index 385e6406d..e440e7def 100644
--- a/src/pkg/runtime/os_netbsd_arm.c
+++ b/src/pkg/runtime/os_netbsd_arm.c
@@ -6,6 +6,7 @@
#include "defs_GOOS_GOARCH.h"
#include "os_GOOS.h"
#include "signal_GOOS_GOARCH.h"
+#include "../../cmd/ld/textflag.h"
void
runtime·lwp_mcontext_init(McontextT *mc, void *stack, M *mp, G *gp, void (*fn)(void))
@@ -23,7 +24,7 @@ runtime·checkgoarm(void)
// TODO(minux)
}
-#pragma textflag 7
+#pragma textflag NOSPLIT
int64
runtime·cputicks() {
// Currently cputicks() is used in blocking profiler and to seed runtime·fastrand1().
diff --git a/src/pkg/runtime/os_openbsd.c b/src/pkg/runtime/os_openbsd.c
index 4ce102ec2..18377a047 100644
--- a/src/pkg/runtime/os_openbsd.c
+++ b/src/pkg/runtime/os_openbsd.c
@@ -7,6 +7,7 @@
#include "os_GOOS.h"
#include "signal_unix.h"
#include "stack.h"
+#include "../../cmd/ld/textflag.h"
enum
{
@@ -59,11 +60,11 @@ runtime·semacreate(void)
return 1;
}
+#pragma textflag NOSPLIT
int32
runtime·semasleep(int64 ns)
{
Timespec ts;
- int64 secs;
// spin-mutex lock
while(runtime·xchg(&m->waitsemalock, 1))
@@ -78,12 +79,9 @@ runtime·semasleep(int64 ns)
runtime·thrsleep(&m->waitsemacount, 0, nil, &m->waitsemalock, nil);
else {
ns += runtime·nanotime();
- secs = ns/1000000000LL;
- // Avoid overflow
- if(secs >= 1LL<<31)
- secs = (1LL<<31) - 1;
- ts.tv_sec = secs;
- ts.tv_nsec = ns%1000000000LL;
+ // NOTE: tv_nsec is int64 on amd64, so this assumes a little-endian system.
+ ts.tv_nsec = 0;
+ ts.tv_sec = runtime·timediv(ns, 1000000000, (int32*)&ts.tv_nsec);
runtime·thrsleep(&m->waitsemacount, CLOCK_REALTIME, &ts, &m->waitsemalock, nil);
}
// reacquire lock
@@ -251,45 +249,6 @@ runtime·memlimit(void)
return 0;
}
-void
-runtime·setprof(bool on)
-{
- USED(on);
-}
-
-static int8 badcallback[] = "runtime: cgo callback on thread not created by Go.\n";
-
-// This runs on a foreign stack, without an m or a g. No stack split.
-#pragma textflag 7
-void
-runtime·badcallback(void)
-{
- runtime·write(2, badcallback, sizeof badcallback - 1);
-}
-
-static int8 badsignal[] = "runtime: signal received on thread not created by Go: ";
-
-// This runs on a foreign stack, without an m or a g. No stack split.
-#pragma textflag 7
-void
-runtime·badsignal(int32 sig)
-{
- int32 len;
-
- if (sig == SIGPROF) {
- return; // Ignore SIGPROFs intended for a non-Go thread.
- }
- runtime·write(2, badsignal, sizeof badsignal - 1);
- if (0 <= sig && sig < NSIG) {
- // Can't call findnull() because it will split stack.
- for(len = 0; runtime·sigtab[sig].name[len]; len++)
- ;
- runtime·write(2, runtime·sigtab[sig].name, len);
- }
- runtime·write(2, "\n", 1);
- runtime·exit(1);
-}
-
extern void runtime·sigtramp(void);
typedef struct sigaction {
diff --git a/src/pkg/runtime/os_openbsd.h b/src/pkg/runtime/os_openbsd.h
index dbfa4b69f..4746b314f 100644
--- a/src/pkg/runtime/os_openbsd.h
+++ b/src/pkg/runtime/os_openbsd.h
@@ -8,6 +8,8 @@
#define SIG_UNBLOCK 2
#define SIG_SETMASK 3
+typedef byte* kevent_udata;
+
struct sigaction;
void runtime·sigpanic(void);
diff --git a/src/pkg/runtime/os_plan9.c b/src/pkg/runtime/os_plan9.c
index c7ed59fc9..07db2c305 100644
--- a/src/pkg/runtime/os_plan9.c
+++ b/src/pkg/runtime/os_plan9.c
@@ -5,6 +5,7 @@
#include "runtime.h"
#include "os_GOOS.h"
#include "arch_GOARCH.h"
+#include "../../cmd/ld/textflag.h"
int8 *goos = "plan9";
extern SigTab runtime·sigtab[];
@@ -115,13 +116,14 @@ runtime·initsig(void)
{
}
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·osyield(void)
{
runtime·sleep(0);
}
+#pragma textflag NOSPLIT
void
runtime·usleep(uint32 µs)
{
@@ -193,7 +195,8 @@ runtime·goexitsall(int8 *status)
int32
runtime·postnote(int32 pid, int8* msg)
{
- int32 fd, len;
+ int32 fd;
+ intgo len;
uint8 buf[128];
uint8 tmp[16];
uint8 *p, *q;
@@ -259,6 +262,7 @@ runtime·semacreate(void)
return 1;
}
+#pragma textflag NOSPLIT
int32
runtime·semasleep(int64 ns)
{
@@ -266,10 +270,7 @@ runtime·semasleep(int64 ns)
int32 ms;
if(ns >= 0) {
- if(ns/1000000 > 0x7fffffffll)
- ms = 0x7fffffff;
- else
- ms = ns/1000000;
+ ms = runtime·timediv(ns, 1000000, nil);
ret = runtime·plan9_tsemacquire(&m->waitsemacount, ms);
if(ret == 1)
return 0; // success
@@ -323,28 +324,13 @@ runtime·memlimit(void)
return 0;
}
-void
-runtime·setprof(bool on)
-{
- USED(on);
-}
-
-static int8 badcallback[] = "runtime: cgo callback on thread not created by Go.\n";
-
-// This runs on a foreign stack, without an m or a g. No stack split.
-#pragma textflag 7
-void
-runtime·badcallback(void)
-{
- runtime·pwrite(2, badcallback, sizeof badcallback - 1, -1LL);
-}
-
+#pragma dataflag NOPTR
static int8 badsignal[] = "runtime: signal received on thread not created by Go.\n";
// This runs on a foreign stack, without an m or a g. No stack split.
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
-runtime·badsignal(void)
+runtime·badsignal2(void)
{
runtime·pwrite(2, badsignal, sizeof badsignal - 1, -1LL);
runtime·exits(badsignal);
diff --git a/src/pkg/runtime/os_plan9_386.c b/src/pkg/runtime/os_plan9_386.c
index 3396e44e7..0844d726b 100644
--- a/src/pkg/runtime/os_plan9_386.c
+++ b/src/pkg/runtime/os_plan9_386.c
@@ -32,7 +32,7 @@ runtime·sighandler(void *v, int8 *s, G *gp)
Ureg *ureg;
uintptr *sp;
SigTab *sig, *nsig;
- int32 len, i;
+ intgo len, i;
if(!s)
return NCONT;
@@ -88,6 +88,8 @@ runtime·sighandler(void *v, int8 *s, G *gp)
return NDFLT;
Throw:
+ m->throwing = 1;
+ m->caughtsig = gp;
runtime·startpanic();
runtime·printf("%s\n", s);
@@ -95,7 +97,7 @@ Throw:
runtime·printf("\n");
if(runtime·gotraceback(&crash)) {
- runtime·traceback((void*)ureg->pc, (void*)ureg->sp, 0, gp);
+ runtime·traceback(ureg->pc, ureg->sp, 0, gp);
runtime·tracebackothers(gp);
runtime·dumpregs(ureg);
}
diff --git a/src/pkg/runtime/os_plan9_amd64.c b/src/pkg/runtime/os_plan9_amd64.c
index cf0a82b6b..58822ff84 100644
--- a/src/pkg/runtime/os_plan9_amd64.c
+++ b/src/pkg/runtime/os_plan9_amd64.c
@@ -40,7 +40,7 @@ runtime·sighandler(void *v, int8 *s, G *gp)
Ureg *ureg;
uintptr *sp;
SigTab *sig, *nsig;
- int32 len, i;
+ intgo i, len;
if(!s)
return NCONT;
@@ -96,6 +96,8 @@ runtime·sighandler(void *v, int8 *s, G *gp)
return NDFLT;
Throw:
+ m->throwing = 1;
+ m->caughtsig = gp;
runtime·startpanic();
runtime·printf("%s\n", s);
@@ -103,7 +105,7 @@ Throw:
runtime·printf("\n");
if(runtime·gotraceback(&crash)) {
- runtime·traceback((void*)ureg->ip, (void*)ureg->sp, 0, gp);
+ runtime·traceback(ureg->ip, ureg->sp, 0, gp);
runtime·tracebackothers(gp);
runtime·dumpregs(ureg);
}
diff --git a/src/pkg/runtime/os_windows.c b/src/pkg/runtime/os_windows.c
index b28affe31..c3e296aa6 100644
--- a/src/pkg/runtime/os_windows.c
+++ b/src/pkg/runtime/os_windows.c
@@ -6,6 +6,7 @@
#include "type.h"
#include "defs_GOOS_GOARCH.h"
#include "os_GOOS.h"
+#include "../../cmd/ld/textflag.h"
#pragma dynimport runtime·CloseHandle CloseHandle "kernel32.dll"
#pragma dynimport runtime·CreateEvent CreateEventA "kernel32.dll"
@@ -24,6 +25,7 @@
#pragma dynimport runtime·GetSystemTimeAsFileTime GetSystemTimeAsFileTime "kernel32.dll"
#pragma dynimport runtime·GetThreadContext GetThreadContext "kernel32.dll"
#pragma dynimport runtime·LoadLibrary LoadLibraryW "kernel32.dll"
+#pragma dynimport runtime·LoadLibraryA LoadLibraryA "kernel32.dll"
#pragma dynimport runtime·ResumeThread ResumeThread "kernel32.dll"
#pragma dynimport runtime·SetConsoleCtrlHandler SetConsoleCtrlHandler "kernel32.dll"
#pragma dynimport runtime·SetEvent SetEvent "kernel32.dll"
@@ -55,6 +57,7 @@ extern void *runtime·GetSystemInfo;
extern void *runtime·GetSystemTimeAsFileTime;
extern void *runtime·GetThreadContext;
extern void *runtime·LoadLibrary;
+extern void *runtime·LoadLibraryA;
extern void *runtime·ResumeThread;
extern void *runtime·SetConsoleCtrlHandler;
extern void *runtime·SetEvent;
@@ -66,6 +69,8 @@ extern void *runtime·timeBeginPeriod;
extern void *runtime·WaitForSingleObject;
extern void *runtime·WriteFile;
+void *runtime·GetQueuedCompletionStatusEx;
+
static int32
getproccount(void)
{
@@ -78,6 +83,9 @@ getproccount(void)
void
runtime·osinit(void)
{
+ void *kernel32;
+ void *SetProcessPriorityBoost;
+
// -1 = current process, -2 = current thread
runtime·stdcall(runtime·DuplicateHandle, 7,
(uintptr)-1, (uintptr)-2, (uintptr)-1, &m->thread,
@@ -85,6 +93,18 @@ runtime·osinit(void)
runtime·stdcall(runtime·SetConsoleCtrlHandler, 2, runtime·ctrlhandler, (uintptr)1);
runtime·stdcall(runtime·timeBeginPeriod, 1, (uintptr)1);
runtime·ncpu = getproccount();
+
+ kernel32 = runtime·stdcall(runtime·LoadLibraryA, 1, "kernel32.dll");
+ if(kernel32 != nil) {
+ // Windows dynamic priority boosting assumes that a process has different types
+ // of dedicated threads -- GUI, IO, computational, etc. Go processes use
+ // equivalent threads that all do a mix of GUI, IO, computations, etc.
+ // In such context dynamic priority boosting does nothing but harm, so we turn it off.
+ SetProcessPriorityBoost = runtime·stdcall(runtime·GetProcAddress, 2, kernel32, "SetProcessPriorityBoost");
+ if(SetProcessPriorityBoost != nil) // supported since Windows XP
+ runtime·stdcall(SetProcessPriorityBoost, 2, (uintptr)-1, (uintptr)1);
+ runtime·GetQueuedCompletionStatusEx = runtime·stdcall(runtime·GetProcAddress, 2, kernel32, "GetQueuedCompletionStatusEx");
+ }
}
void
@@ -164,21 +184,19 @@ runtime·write(int32 fd, void *buf, int32 n)
#define INFINITE ((uintptr)0xFFFFFFFF)
+#pragma textflag NOSPLIT
int32
runtime·semasleep(int64 ns)
{
- uintptr ms;
-
+ // store ms in ns to save stack space
if(ns < 0)
- ms = INFINITE;
- else if(ns/1000000 > 0x7fffffffLL)
- ms = 0x7fffffff;
+ ns = INFINITE;
else {
- ms = ns/1000000;
- if(ms == 0)
- ms = 1;
+ ns = runtime·timediv(ns, 1000000, nil);
+ if(ns == 0)
+ ns = 1;
}
- if(runtime·stdcall(runtime·WaitForSingleObject, 2, m->waitsema, ms) != 0)
+ if(runtime·stdcall(runtime·WaitForSingleObject, 2, m->waitsema, (uintptr)ns) != 0)
return -1; // timeout
return 0;
}
@@ -237,6 +255,7 @@ runtime·unminit(void)
runtime·remove_exception_handler();
}
+#pragma textflag NOSPLIT
int64
runtime·nanotime(void)
{
@@ -262,17 +281,32 @@ time·now(int64 sec, int32 usec)
}
// Calling stdcall on os stack.
-#pragma textflag 7
+#pragma textflag NOSPLIT
void *
runtime·stdcall(void *fn, int32 count, ...)
{
- WinCall c;
+ m->wincall.fn = fn;
+ m->wincall.n = count;
+ m->wincall.args = (uintptr*)&count + 1;
+ runtime·asmcgocall(runtime·asmstdcall, &m->wincall);
+ return (void*)m->wincall.r1;
+}
+
+extern void runtime·usleep1(uint32);
+
+#pragma textflag NOSPLIT
+void
+runtime·osyield(void)
+{
+ runtime·usleep1(1);
+}
- c.fn = fn;
- c.n = count;
- c.args = (uintptr*)&count + 1;
- runtime·asmcgocall(runtime·asmstdcall, &c);
- return (void*)c.r1;
+#pragma textflag NOSPLIT
+void
+runtime·usleep(uint32 us)
+{
+ // Have 1us units; want 100ns units.
+ runtime·usleep1(10*us);
}
uint32
@@ -444,15 +478,7 @@ runtime·memlimit(void)
return 0;
}
-void
-runtime·setprof(bool on)
-{
- USED(on);
-}
-
-int8 runtime·badcallbackmsg[] = "runtime: cgo callback on thread not created by Go.\n";
-int32 runtime·badcallbacklen = sizeof runtime·badcallbackmsg - 1;
-
+#pragma dataflag NOPTR
int8 runtime·badsignalmsg[] = "runtime: signal received on thread not created by Go.\n";
int32 runtime·badsignallen = sizeof runtime·badsignalmsg - 1;
diff --git a/src/pkg/runtime/os_windows.h b/src/pkg/runtime/os_windows.h
index cf0ecb68e..b64fa8873 100644
--- a/src/pkg/runtime/os_windows.h
+++ b/src/pkg/runtime/os_windows.h
@@ -4,6 +4,7 @@
extern void *runtime·LoadLibrary;
extern void *runtime·GetProcAddress;
+extern void *runtime·GetQueuedCompletionStatusEx;
// Call a Windows function with stdcall conventions,
// and switch to os stack during the call.
diff --git a/src/pkg/runtime/os_windows_386.c b/src/pkg/runtime/os_windows_386.c
index 20fbea13d..c377e5b6c 100644
--- a/src/pkg/runtime/os_windows_386.c
+++ b/src/pkg/runtime/os_windows_386.c
@@ -76,7 +76,7 @@ runtime·sighandler(ExceptionRecord *info, Context *r, G *gp)
runtime·printf("\n");
if(runtime·gotraceback(&crash)){
- runtime·traceback((void*)r->Eip, (void*)r->Esp, 0, gp);
+ runtime·traceback(r->Eip, r->Esp, 0, gp);
runtime·tracebackothers(gp);
runtime·dumpregs(r);
}
diff --git a/src/pkg/runtime/os_windows_amd64.c b/src/pkg/runtime/os_windows_amd64.c
index 881c73c93..97c48feb0 100644
--- a/src/pkg/runtime/os_windows_amd64.c
+++ b/src/pkg/runtime/os_windows_amd64.c
@@ -83,7 +83,7 @@ runtime·sighandler(ExceptionRecord *info, Context *r, G *gp)
runtime·printf("\n");
if(runtime·gotraceback(&crash)){
- runtime·traceback((void*)r->Rip, (void*)r->Rsp, 0, gp);
+ runtime·traceback(r->Rip, r->Rsp, 0, gp);
runtime·tracebackothers(gp);
runtime·dumpregs(r);
}
diff --git a/src/pkg/runtime/panic.c b/src/pkg/runtime/panic.c
index d0cf3ad6f..8227a444d 100644
--- a/src/pkg/runtime/panic.c
+++ b/src/pkg/runtime/panic.c
@@ -6,6 +6,7 @@
#include "arch_GOARCH.h"
#include "stack.h"
#include "malloc.h"
+#include "../../cmd/ld/textflag.h"
// Code related to defer, panic and recover.
@@ -104,11 +105,15 @@ popdefer(void)
static void
freedefer(Defer *d)
{
+ int32 total;
+
if(d->special) {
if(d->free)
runtime·free(d);
} else {
- runtime·memclr((byte*)d->args, d->siz);
+ // Wipe out any possible pointers in argp/pc/fn/args.
+ total = sizeof(*d) + ROUND(d->siz, sizeof(uintptr)) - sizeof(d->args);
+ runtime·memclr((byte*)d, total);
}
}
@@ -118,7 +123,7 @@ freedefer(Defer *d)
// are available sequentially after &fn; they would not be
// copied if a stack split occurred. It's OK for this to call
// functions that split the stack.
-#pragma textflag 7
+#pragma textflag NOSPLIT
uintptr
runtime·deferproc(int32 siz, FuncVal *fn, ...)
{
@@ -152,9 +157,14 @@ runtime·deferproc(int32 siz, FuncVal *fn, ...)
// is called again and again until there are no more deferred functions.
// Cannot split the stack because we reuse the caller's frame to
// call the deferred function.
-#pragma textflag 7
+//
+// The ... in the prototype keeps the compiler from declaring
+// an argument frame size. deferreturn is a very special function,
+// and if the runtime ever asks for its frame size, that means
+// the traceback routines are probably broken.
+#pragma textflag NOSPLIT
void
-runtime·deferreturn(uintptr arg0)
+runtime·deferreturn(uintptr arg0, ...)
{
Defer *d;
byte *argp;
@@ -166,10 +176,19 @@ runtime·deferreturn(uintptr arg0)
argp = (byte*)&arg0;
if(d->argp != argp)
return;
+
+ // Moving arguments around.
+ // Do not allow preemption here, because the garbage collector
+ // won't know the form of the arguments until the jmpdefer can
+ // flip the PC over to fn.
+ m->locks++;
runtime·memmove(argp, d->args, d->siz);
fn = d->fn;
popdefer();
freedefer(d);
+ m->locks--;
+ if(m->locks == 0 && g->preempt)
+ g->stackguard0 = StackPreempt;
runtime·jmpdefer(fn, argp);
}
@@ -214,7 +233,7 @@ runtime·panic(Eface e)
p = runtime·mal(sizeof *p);
p->arg = e;
p->link = g->panic;
- p->stackbase = (byte*)g->stackbase;
+ p->stackbase = g->stackbase;
g->panic = p;
for(;;) {
@@ -223,10 +242,10 @@ runtime·panic(Eface e)
break;
// take defer off list in case of recursive panic
popdefer();
- g->ispanic = true; // rock for newstack, where reflect.call ends up
+ g->ispanic = true; // rock for newstack, where reflect.newstackcall ends up
argp = d->argp;
pc = d->pc;
- reflect·call(d->fn, (byte*)d->args, d->siz);
+ runtime·newstackcall(d->fn, (byte*)d->args, d->siz);
freedefer(d);
if(p->recovered) {
g->panic = p->link;
@@ -254,11 +273,11 @@ static void
recovery(G *gp)
{
void *argp;
- void *pc;
+ uintptr pc;
// Info about defer passed in G struct.
argp = (void*)gp->sigcode0;
- pc = (void*)gp->sigcode1;
+ pc = (uintptr)gp->sigcode1;
// Unwind to the stack frame with d's arguments in it.
runtime·unwindstack(gp, argp);
@@ -277,7 +296,9 @@ recovery(G *gp)
else
gp->sched.sp = (uintptr)argp - 2*sizeof(uintptr);
gp->sched.pc = pc;
- runtime·gogo(&gp->sched, 1);
+ gp->sched.lr = 0;
+ gp->sched.ret = 1;
+ runtime·gogo(&gp->sched);
}
// Free stack frames until we hit the last one
@@ -292,14 +313,17 @@ runtime·unwindstack(G *gp, byte *sp)
if(g == gp)
runtime·throw("unwindstack on self");
- while((top = (Stktop*)gp->stackbase) != nil && top->stackbase != nil) {
+ while((top = (Stktop*)gp->stackbase) != 0 && top->stackbase != 0) {
stk = (byte*)gp->stackguard - StackGuard;
if(stk <= sp && sp < (byte*)gp->stackbase)
break;
- gp->stackbase = (uintptr)top->stackbase;
- gp->stackguard = (uintptr)top->stackguard;
- if(top->free != 0)
+ gp->stackbase = top->stackbase;
+ gp->stackguard = top->stackguard;
+ gp->stackguard0 = gp->stackguard;
+ if(top->free != 0) {
+ gp->stacksize -= top->free;
runtime·stackfree(stk, top->free);
+ }
}
if(sp != nil && (sp < (byte*)gp->stackguard - StackGuard || (byte*)gp->stackbase < sp)) {
@@ -311,80 +335,38 @@ runtime·unwindstack(G *gp, byte *sp)
// The implementation of the predeclared function recover.
// Cannot split the stack because it needs to reliably
// find the stack segment of its caller.
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·recover(byte *argp, Eface ret)
{
- Stktop *top, *oldtop;
Panic *p;
+ Stktop *top;
- // Must be a panic going on.
- if((p = g->panic) == nil || p->recovered)
- goto nomatch;
-
- // Frame must be at the top of the stack segment,
- // because each deferred call starts a new stack
- // segment as a side effect of using reflect.call.
- // (There has to be some way to remember the
- // variable argument frame size, and the segment
- // code already takes care of that for us, so we
- // reuse it.)
- //
- // As usual closures complicate things: the fp that
- // the closure implementation function claims to have
- // is where the explicit arguments start, after the
- // implicit pointer arguments and PC slot.
- // If we're on the first new segment for a closure,
- // then fp == top - top->args is correct, but if
- // the closure has its own big argument frame and
- // allocated a second segment (see below),
- // the fp is slightly above top - top->args.
- // That condition can't happen normally though
- // (stack pointers go down, not up), so we can accept
- // any fp between top and top - top->args as
- // indicating the top of the segment.
+ // Must be an unrecovered panic in progress.
+ // Must be on a stack segment created for a deferred call during a panic.
+ // Must be at the top of that segment, meaning the deferred call itself
+ // and not something it called. The top frame in the segment will have
+ // argument pointer argp == top - top->argsize.
+ // The subtraction of g->panicwrap allows wrapper functions that
+ // do not count as official calls to adjust what we consider the top frame
+ // while they are active on the stack. The linker emits adjustments of
+ // g->panicwrap in the prologue and epilogue of functions marked as wrappers.
top = (Stktop*)g->stackbase;
- if(argp < (byte*)top - top->argsize || (byte*)top < argp)
- goto nomatch;
-
- // The deferred call makes a new segment big enough
- // for the argument frame but not necessarily big
- // enough for the function's local frame (size unknown
- // at the time of the call), so the function might have
- // made its own segment immediately. If that's the
- // case, back top up to the older one, the one that
- // reflect.call would have made for the panic.
- //
- // The fp comparison here checks that the argument
- // frame that was copied during the split (the top->args
- // bytes above top->fp) abuts the old top of stack.
- // This is a correct test for both closure and non-closure code.
- oldtop = (Stktop*)top->stackbase;
- if(oldtop != nil && top->argp == (byte*)oldtop - top->argsize)
- top = oldtop;
-
- // Now we have the segment that was created to
- // run this call. It must have been marked as a panic segment.
- if(!top->panic)
- goto nomatch;
-
- // Okay, this is the top frame of a deferred call
- // in response to a panic. It can see the panic argument.
- p->recovered = 1;
- ret = p->arg;
- FLUSH(&ret);
- return;
-
-nomatch:
- ret.type = nil;
- ret.data = nil;
+ p = g->panic;
+ if(p != nil && !p->recovered && top->panic && argp == (byte*)top - top->argsize - g->panicwrap) {
+ p->recovered = 1;
+ ret = p->arg;
+ } else {
+ ret.type = nil;
+ ret.data = nil;
+ }
FLUSH(&ret);
}
void
runtime·startpanic(void)
{
- if(runtime·mheap == 0 || runtime·mheap->cachealloc.size == 0) { // very early
+ if(runtime·mheap.cachealloc.size == 0) { // very early
runtime·printf("runtime: panic before malloc heap initialized\n");
m->mallocing = 1; // tell rest of panic not to try to malloc
} else if(m->mcache == nil) // can happen if called from signal handler or throw
@@ -394,8 +376,13 @@ runtime·startpanic(void)
runtime·exit(3);
}
m->dying = 1;
+ if(g != nil)
+ g->writebuf = nil;
runtime·xadd(&runtime·panicking, 1);
runtime·lock(&paniclk);
+ if(runtime·debug.schedtrace > 0 || runtime·debug.scheddetail > 0)
+ runtime·schedtrace(true);
+ runtime·freezetheworld();
}
void
@@ -403,16 +390,20 @@ runtime·dopanic(int32 unused)
{
static bool didothers;
bool crash;
+ int32 t;
if(g->sig != 0)
runtime·printf("[signal %x code=%p addr=%p pc=%p]\n",
g->sig, g->sigcode0, g->sigcode1, g->sigpc);
- if(runtime·gotraceback(&crash)){
+ if((t = runtime·gotraceback(&crash)) > 0){
if(g != m->g0) {
runtime·printf("\n");
runtime·goroutineheader(g);
- runtime·traceback(runtime·getcallerpc(&unused), runtime·getcallersp(&unused), 0, g);
+ runtime·traceback((uintptr)runtime·getcallerpc(&unused), (uintptr)runtime·getcallersp(&unused), 0, g);
+ } else if(t >= 2 || m->throwing > 0) {
+ runtime·printf("\nruntime stack:\n");
+ runtime·traceback((uintptr)runtime·getcallerpc(&unused), (uintptr)runtime·getcallersp(&unused), 0, g);
}
if(!didothers) {
didothers = true;
@@ -479,11 +470,15 @@ runtime·panicstring(int8 *s)
{
Eface err;
+ if(m->mallocing) {
+ runtime·printf("panic: %s\n", s);
+ runtime·throw("panic during malloc");
+ }
if(m->gcing) {
runtime·printf("panic: %s\n", s);
runtime·throw("panic during gc");
}
- runtime·newErrorString(runtime·gostringnocopy((byte*)s), &err);
+ runtime·newErrorCString(s, &err);
runtime·panic(err);
}
diff --git a/src/pkg/runtime/parfor.c b/src/pkg/runtime/parfor.c
index a4468c2af..ceaac8bc9 100644
--- a/src/pkg/runtime/parfor.c
+++ b/src/pkg/runtime/parfor.c
@@ -144,9 +144,9 @@ runtime·parfordo(ParFor *desc)
if(victim >= tid)
victim++;
victimpos = &desc->thr[victim].pos;
- pos = runtime·atomicload64(victimpos);
for(;;) {
// See if it has any work.
+ pos = runtime·atomicload64(victimpos);
begin = (uint32)pos;
end = (uint32)(pos>>32);
if(begin+1 >= end) {
@@ -159,7 +159,7 @@ runtime·parfordo(ParFor *desc)
}
begin2 = begin + (end-begin)/2;
newpos = (uint64)begin | (uint64)begin2<<32;
- if(runtime·cas64(victimpos, &pos, newpos)) {
+ if(runtime·cas64(victimpos, pos, newpos)) {
begin = begin2;
break;
}
diff --git a/src/pkg/runtime/parfor_test.go b/src/pkg/runtime/parfor_test.go
index 4c69a68ce..de64285b8 100644
--- a/src/pkg/runtime/parfor_test.go
+++ b/src/pkg/runtime/parfor_test.go
@@ -102,11 +102,6 @@ func TestParForSetup(t *testing.T) {
// Test parallel parallelfor.
func TestParForParallel(t *testing.T) {
- if GOARCH != "amd64" {
- t.Log("temporarily disabled, see http://golang.org/issue/4155")
- return
- }
-
N := uint64(1e7)
if testing.Short() {
N /= 10
diff --git a/src/pkg/runtime/pprof/pprof.go b/src/pkg/runtime/pprof/pprof.go
index 32c1098b9..3b8428519 100644
--- a/src/pkg/runtime/pprof/pprof.go
+++ b/src/pkg/runtime/pprof/pprof.go
@@ -20,8 +20,8 @@ import (
"text/tabwriter"
)
-// BUG(rsc): A bug in the OS X Snow Leopard 64-bit kernel prevents
-// CPU profiling from giving accurate results on that system.
+// BUG(rsc): Profiles are incomplete and inaccuate on NetBSD, OpenBSD, and OS X.
+// See http://golang.org/issue/6047 for details.
// A Profile is a collection of stack traces showing the call sequences
// that led to instances of a particular event, such as allocation.
@@ -666,7 +666,7 @@ func writeBlock(w io.Writer, debug int) error {
}
fmt.Fprint(w, "\n")
if debug > 0 {
- printStackRecord(w, r.Stack(), false)
+ printStackRecord(w, r.Stack(), true)
}
}
diff --git a/src/pkg/runtime/pprof/pprof_test.go b/src/pkg/runtime/pprof/pprof_test.go
index 6d5764f4a..eb76b93c4 100644
--- a/src/pkg/runtime/pprof/pprof_test.go
+++ b/src/pkg/runtime/pprof/pprof_test.go
@@ -6,54 +6,67 @@ package pprof_test
import (
"bytes"
+ "fmt"
"hash/crc32"
+ "math/big"
"os/exec"
+ "regexp"
"runtime"
. "runtime/pprof"
"strings"
+ "sync"
"testing"
+ "time"
"unsafe"
)
func TestCPUProfile(t *testing.T) {
- switch runtime.GOOS {
- case "darwin":
- out, err := exec.Command("uname", "-a").CombinedOutput()
- if err != nil {
- t.Fatal(err)
- }
- vers := string(out)
- t.Logf("uname -a: %v", vers)
- // Lion uses "Darwin Kernel Version 11".
- if strings.Contains(vers, "Darwin Kernel Version 10") && strings.Contains(vers, "RELEASE_X86_64") {
- t.Skip("skipping test on known-broken kernel (64-bit Leopard / Snow Leopard)")
+ buf := make([]byte, 100000)
+ testCPUProfile(t, []string{"crc32.ChecksumIEEE"}, func() {
+ // 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)
}
- case "plan9":
- // unimplemented
- return
- }
+ })
+}
+func TestCPUProfileMultithreaded(t *testing.T) {
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()
+ defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2))
+ testCPUProfile(t, []string{"crc32.ChecksumIEEE", "crc32.Update"}, func() {
+ c := make(chan int)
+ go func() {
+ for i := 0; i < 2000; i++ {
+ crc32.Update(0, crc32.IEEETable, buf)
+ }
+ c <- 1
+ }()
+ // 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 < 2000; i++ {
+ crc32.ChecksumIEEE(buf)
+ }
+ <-c
+ })
+}
+func parseProfile(t *testing.T, bytes []byte, f func(uintptr, []uintptr)) {
// Convert []byte to []uintptr.
- bytes := prof.Bytes()
l := len(bytes) / int(unsafe.Sizeof(uintptr(0)))
val := *(*[]uintptr)(unsafe.Pointer(&bytes))
val = val[:l]
- if l < 13 {
- t.Fatalf("profile too short: %#x", val)
+ // 5 for the header, 2 for the per-sample header on at least one sample, 3 for the trailer.
+ if l < 5+2+3 {
+ t.Logf("profile too short: %#x", val)
+ if badOS[runtime.GOOS] {
+ t.Skipf("ignoring failure on %s; see golang.org/issue/6047", runtime.GOOS)
+ return
+ }
+ t.FailNow()
}
hd, val, tl := val[:5], val[5:l-3], val[l-3:]
@@ -65,25 +78,323 @@ func TestCPUProfile(t *testing.T) {
t.Fatalf("malformed end-of-data marker %#x", tl)
}
- // Check that profile is well formed and contains ChecksumIEEE.
- found := false
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(val[0], val[2:2+val[1]])
+ val = val[2+val[1]:]
+ }
+}
+
+func testCPUProfile(t *testing.T, need []string, f func()) {
+ switch runtime.GOOS {
+ case "darwin":
+ out, err := exec.Command("uname", "-a").CombinedOutput()
+ if err != nil {
+ t.Fatal(err)
+ }
+ vers := string(out)
+ t.Logf("uname -a: %v", vers)
+ case "plan9":
+ // unimplemented
+ return
+ }
+
+ var prof bytes.Buffer
+ if err := StartCPUProfile(&prof); err != nil {
+ t.Fatal(err)
+ }
+ f()
+ StopCPUProfile()
+
+ // Check that profile is well formed and contains ChecksumIEEE.
+ have := make([]uintptr, len(need))
+ parseProfile(t, prof.Bytes(), func(count uintptr, stk []uintptr) {
+ for _, pc := range stk {
f := runtime.FuncForPC(pc)
if f == nil {
continue
}
- if strings.Contains(f.Name(), "ChecksumIEEE") {
- found = true
+ for i, name := range need {
+ if strings.Contains(f.Name(), name) {
+ have[i] += count
+ }
}
}
- val = val[2+val[1]:]
+ })
+
+ if len(need) == 0 {
+ return
+ }
+
+ var total uintptr
+ for i, name := range need {
+ total += have[i]
+ t.Logf("%s: %d\n", name, have[i])
+ }
+ ok := true
+ if total == 0 {
+ t.Logf("no CPU profile samples collected")
+ ok = false
+ }
+ min := total / uintptr(len(have)) / 3
+ for i, name := range need {
+ if have[i] < min {
+ t.Logf("%s has %d samples out of %d, want at least %d, ideally %d", name, have[i], total, min, total/uintptr(len(have)))
+ ok = false
+ }
}
- if !found {
- t.Fatal("did not find ChecksumIEEE in the profile")
+ if !ok {
+ if badOS[runtime.GOOS] {
+ t.Skipf("ignoring failure on %s; see golang.org/issue/6047", runtime.GOOS)
+ return
+ }
+ t.FailNow()
+ }
+}
+
+func TestCPUProfileWithFork(t *testing.T) {
+ // Fork can hang if preempted with signals frequently enough (see issue 5517).
+ // Ensure that we do not do this.
+ heap := 1 << 30
+ if testing.Short() {
+ heap = 100 << 20
+ }
+ // This makes fork slower.
+ garbage := make([]byte, heap)
+ // Need to touch the slice, otherwise it won't be paged in.
+ done := make(chan bool)
+ go func() {
+ for i := range garbage {
+ garbage[i] = 42
+ }
+ done <- true
+ }()
+ <-done
+
+ var prof bytes.Buffer
+ if err := StartCPUProfile(&prof); err != nil {
+ t.Fatal(err)
+ }
+ defer StopCPUProfile()
+
+ for i := 0; i < 10; i++ {
+ exec.Command("go").CombinedOutput()
+ }
+}
+
+// Test that profiler does not observe runtime.gogo as "user" goroutine execution.
+// If it did, it would see inconsistent state and would either record an incorrect stack
+// or crash because the stack was malformed.
+func TestGoroutineSwitch(t *testing.T) {
+ if runtime.GOOS == "windows" {
+ t.Skip("flaky test; see http://golang.org/issue/6417")
}
+ // How much to try. These defaults take about 1 seconds
+ // on a 2012 MacBook Pro. The ones in short mode take
+ // about 0.1 seconds.
+ tries := 10
+ count := 1000000
+ if testing.Short() {
+ tries = 1
+ }
+ for try := 0; try < tries; try++ {
+ var prof bytes.Buffer
+ if err := StartCPUProfile(&prof); err != nil {
+ t.Fatal(err)
+ }
+ for i := 0; i < count; i++ {
+ runtime.Gosched()
+ }
+ StopCPUProfile()
+
+ // Read profile to look for entries for runtime.gogo with an attempt at a traceback.
+ // The special entry
+ parseProfile(t, prof.Bytes(), func(count uintptr, stk []uintptr) {
+ // An entry with two frames with 'System' in its top frame
+ // exists to record a PC without a traceback. Those are okay.
+ if len(stk) == 2 {
+ f := runtime.FuncForPC(stk[1])
+ if f != nil && f.Name() == "System" {
+ return
+ }
+ }
+
+ // Otherwise, should not see runtime.gogo.
+ // The place we'd see it would be the inner most frame.
+ f := runtime.FuncForPC(stk[0])
+ if f != nil && f.Name() == "runtime.gogo" {
+ var buf bytes.Buffer
+ for _, pc := range stk {
+ f := runtime.FuncForPC(pc)
+ if f == nil {
+ fmt.Fprintf(&buf, "%#x ?:0\n", pc)
+ } else {
+ file, line := f.FileLine(pc)
+ fmt.Fprintf(&buf, "%#x %s:%d\n", pc, file, line)
+ }
+ }
+ t.Fatalf("found profile entry for runtime.gogo:\n%s", buf.String())
+ }
+ })
+ }
+}
+
+// Test that profiling of division operations is okay, especially on ARM. See issue 6681.
+func TestMathBigDivide(t *testing.T) {
+ testCPUProfile(t, nil, func() {
+ t := time.After(5 * time.Second)
+ pi := new(big.Int)
+ for {
+ for i := 0; i < 100; i++ {
+ n := big.NewInt(2646693125139304345)
+ d := big.NewInt(842468587426513207)
+ pi.Div(n, d)
+ }
+ select {
+ case <-t:
+ return
+ default:
+ }
+ }
+ })
+}
+
+// Operating systems that are expected to fail the tests. See issue 6047.
+var badOS = map[string]bool{
+ "darwin": true,
+ "netbsd": true,
+ "openbsd": true,
+}
+
+func TestBlockProfile(t *testing.T) {
+ type TestCase struct {
+ name string
+ f func()
+ re string
+ }
+ tests := [...]TestCase{
+ {"chan recv", blockChanRecv, `
+[0-9]+ [0-9]+ @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+
+# 0x[0-9,a-f]+ runtime\.chanrecv1\+0x[0-9,a-f]+ .*/src/pkg/runtime/chan.c:[0-9]+
+# 0x[0-9,a-f]+ runtime/pprof_test\.blockChanRecv\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
+# 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
+`},
+ {"chan send", blockChanSend, `
+[0-9]+ [0-9]+ @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+
+# 0x[0-9,a-f]+ runtime\.chansend1\+0x[0-9,a-f]+ .*/src/pkg/runtime/chan.c:[0-9]+
+# 0x[0-9,a-f]+ runtime/pprof_test\.blockChanSend\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
+# 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
+`},
+ {"chan close", blockChanClose, `
+[0-9]+ [0-9]+ @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+
+# 0x[0-9,a-f]+ runtime\.chanrecv1\+0x[0-9,a-f]+ .*/src/pkg/runtime/chan.c:[0-9]+
+# 0x[0-9,a-f]+ runtime/pprof_test\.blockChanClose\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
+# 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
+`},
+ {"select recv async", blockSelectRecvAsync, `
+[0-9]+ [0-9]+ @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+
+# 0x[0-9,a-f]+ runtime\.selectgo\+0x[0-9,a-f]+ .*/src/pkg/runtime/chan.c:[0-9]+
+# 0x[0-9,a-f]+ runtime/pprof_test\.blockSelectRecvAsync\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
+# 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
+`},
+ {"select send sync", blockSelectSendSync, `
+[0-9]+ [0-9]+ @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+
+# 0x[0-9,a-f]+ runtime\.selectgo\+0x[0-9,a-f]+ .*/src/pkg/runtime/chan.c:[0-9]+
+# 0x[0-9,a-f]+ runtime/pprof_test\.blockSelectSendSync\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
+# 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
+`},
+ {"mutex", blockMutex, `
+[0-9]+ [0-9]+ @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+
+# 0x[0-9,a-f]+ sync\.\(\*Mutex\)\.Lock\+0x[0-9,a-f]+ .*/src/pkg/sync/mutex\.go:[0-9]+
+# 0x[0-9,a-f]+ runtime/pprof_test\.blockMutex\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
+# 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
+`},
+ }
+
+ runtime.SetBlockProfileRate(1)
+ defer runtime.SetBlockProfileRate(0)
+ for _, test := range tests {
+ test.f()
+ }
+ var w bytes.Buffer
+ Lookup("block").WriteTo(&w, 1)
+ prof := w.String()
+
+ if !strings.HasPrefix(prof, "--- contention:\ncycles/second=") {
+ t.Fatalf("Bad profile header:\n%v", prof)
+ }
+
+ for _, test := range tests {
+ if !regexp.MustCompile(test.re).MatchString(prof) {
+ t.Fatalf("Bad %v entry, expect:\n%v\ngot:\n%v", test.name, test.re, prof)
+ }
+ }
+}
+
+const blockDelay = 10 * time.Millisecond
+
+func blockChanRecv() {
+ c := make(chan bool)
+ go func() {
+ time.Sleep(blockDelay)
+ c <- true
+ }()
+ <-c
+}
+
+func blockChanSend() {
+ c := make(chan bool)
+ go func() {
+ time.Sleep(blockDelay)
+ <-c
+ }()
+ c <- true
+}
+
+func blockChanClose() {
+ c := make(chan bool)
+ go func() {
+ time.Sleep(blockDelay)
+ close(c)
+ }()
+ <-c
+}
+
+func blockSelectRecvAsync() {
+ c := make(chan bool, 1)
+ c2 := make(chan bool, 1)
+ go func() {
+ time.Sleep(blockDelay)
+ c <- true
+ }()
+ select {
+ case <-c:
+ case <-c2:
+ }
+}
+
+func blockSelectSendSync() {
+ c := make(chan bool)
+ c2 := make(chan bool)
+ go func() {
+ time.Sleep(blockDelay)
+ <-c
+ }()
+ select {
+ case c <- true:
+ case c2 <- true:
+ }
+}
+
+func blockMutex() {
+ var mu sync.Mutex
+ mu.Lock()
+ go func() {
+ time.Sleep(blockDelay)
+ mu.Unlock()
+ }()
+ mu.Lock()
}
diff --git a/src/pkg/runtime/print.c b/src/pkg/runtime/print.c
index 5b601599b..8de3ae4fa 100644
--- a/src/pkg/runtime/print.c
+++ b/src/pkg/runtime/print.c
@@ -4,6 +4,7 @@
#include "runtime.h"
#include "type.h"
+#include "../../cmd/ld/textflag.h"
//static Lock debuglock;
@@ -12,7 +13,7 @@ static void vprintf(int8*, byte*);
// write to goroutine-local buffer if diverting output,
// or else standard error.
static void
-gwrite(void *v, int32 n)
+gwrite(void *v, intgo n)
{
if(g == nil || g->writebuf == nil) {
runtime·write(2, v, n);
@@ -52,7 +53,7 @@ runtime·prints(int8 *s)
gwrite(s, runtime·findnull((byte*)s));
}
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·printf(int8 *s, ...)
{
@@ -179,7 +180,7 @@ vprintf(int8 *s, byte *base)
//runtime·unlock(&debuglock);
}
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·goprintf(String s, ...)
{
@@ -350,8 +351,6 @@ runtime·printpointer(void *p)
void
runtime·printstring(String v)
{
- extern uint32 runtime·maxstring;
-
if(v.len > runtime·maxstring) {
gwrite("[string too long]", 17);
return;
diff --git a/src/pkg/runtime/proc.c b/src/pkg/runtime/proc.c
index 5734509e0..de26c72d3 100644
--- a/src/pkg/runtime/proc.c
+++ b/src/pkg/runtime/proc.c
@@ -4,10 +4,12 @@
#include "runtime.h"
#include "arch_GOARCH.h"
+#include "zaexperiment.h"
#include "malloc.h"
#include "stack.h"
#include "race.h"
#include "type.h"
+#include "../../cmd/ld/textflag.h"
// Goroutine scheduler
// The scheduler's job is to distribute ready-to-run goroutines over worker threads.
@@ -29,8 +31,9 @@ struct Sched {
M* midle; // idle m's waiting for work
int32 nmidle; // number of idle m's waiting for work
- int32 mlocked; // number of locked m's waiting for work
+ int32 nmidlelocked; // number of locked m's waiting for work
int32 mcount; // number of m's that have been created
+ int32 maxmcount; // maximum number of m's allowed (or die)
P* pidle; // idle P's
uint32 npidle;
@@ -45,6 +48,7 @@ struct Sched {
Lock gflock;
G* gfree;
+ uint32 gcwaiting; // gc is waiting to run
int32 stopwait;
Note stopnote;
uint32 sysmonwait;
@@ -60,9 +64,8 @@ enum { MaxGomaxprocs = 1<<8 };
Sched runtime·sched;
int32 runtime·gomaxprocs;
-bool runtime·singleproc;
+uint32 runtime·needextram;
bool runtime·iscgo;
-uint32 runtime·gcwaiting;
M runtime·m0;
G runtime·g0; // idle goroutine for m0
G* runtime·allg;
@@ -86,7 +89,6 @@ static void procresize(int32);
static void acquirep(P*);
static P* releasep(void);
static void newm(void(*)(void), P*);
-static void goidle(void);
static void stopm(void);
static void startm(P*, bool);
static void handoffp(P*);
@@ -94,21 +96,24 @@ static void wakep(void);
static void stoplockedm(void);
static void startlockedm(G*);
static void sysmon(void);
-static uint32 retake(uint32*);
-static void inclocked(int32);
+static uint32 retake(int64);
+static void incidlelocked(int32);
static void checkdead(void);
static void exitsyscall0(G*);
static void park0(G*);
-static void gosched0(G*);
static void goexit0(G*);
static void gfput(P*, G*);
static G* gfget(P*);
static void gfpurge(P*);
static void globrunqput(G*);
-static G* globrunqget(P*);
+static G* globrunqget(P*, int32);
static P* pidleget(void);
static void pidleput(P*);
static void injectglist(G*);
+static bool preemptall(void);
+static bool preemptone(P*);
+static bool exitsyscallfast(void);
+static bool haveexperiment(int8*);
// The bootstrap sequence is:
//
@@ -123,19 +128,27 @@ runtime·schedinit(void)
{
int32 n, procs;
byte *p;
+ Eface i;
+
+ runtime·sched.maxmcount = 10000;
+ runtime·precisestack = haveexperiment("precisestack");
- m->nomemprof++;
runtime·mprofinit();
runtime·mallocinit();
mcommoninit(m);
+
+ // Initialize the itable value for newErrorCString,
+ // so that the next time it gets called, possibly
+ // in a fault during a garbage collection, it will not
+ // need to allocated memory.
+ runtime·newErrorCString(0, &i);
runtime·goargs();
runtime·goenvs();
+ runtime·parsedebugvars();
- // For debugging:
- // Allocate internal symbol table representation now,
- // so that we don't need to call malloc when we crash.
- // runtime·findfunc(0);
+ // Allocate internal symbol table representation now, we need it for GC anyway.
+ runtime·symtabinit();
runtime·sched.lastpoll = runtime·nanotime();
procs = 1;
@@ -149,7 +162,6 @@ runtime·schedinit(void)
procresize(procs);
mstats.enablegc = 1;
- m->nomemprof--;
if(raceenabled)
g->racectx = runtime·raceinit();
@@ -160,10 +172,22 @@ extern void main·main(void);
static FuncVal scavenger = {runtime·MHeap_Scavenger};
+static FuncVal initDone = { runtime·unlockOSThread };
+
// The main goroutine.
void
runtime·main(void)
{
+ Defer d;
+
+ // Max stack size is 1 GB on 64-bit, 250 MB on 32-bit.
+ // Using decimal instead of binary GB and MB because
+ // they look nicer in the stack overflow failure message.
+ if(sizeof(void*) == 8)
+ runtime·maxstacksize = 1000000000;
+ else
+ runtime·maxstacksize = 250000000;
+
newm(sysmon, nil);
// Lock the main goroutine onto this, the main OS thread,
@@ -173,10 +197,24 @@ runtime·main(void)
// by calling runtime.LockOSThread during initialization
// to preserve the lock.
runtime·lockOSThread();
+
+ // Defer unlock so that runtime.Goexit during init does the unlock too.
+ d.fn = &initDone;
+ d.siz = 0;
+ d.link = g->defer;
+ d.argp = (void*)-1;
+ d.special = true;
+ d.free = false;
+ g->defer = &d;
+
if(m != &runtime·m0)
runtime·throw("runtime·main not on m0");
runtime·newproc1(&scavenger, nil, 0, 0, runtime·main);
main·init();
+
+ if(g->defer != &d || d.fn != &initDone)
+ runtime·throw("runtime: bad defer entry after init");
+ g->defer = d.link;
runtime·unlockOSThread();
main·main();
@@ -233,14 +271,36 @@ runtime·tracebackothers(G *me)
int32 traceback;
traceback = runtime·gotraceback(nil);
+
+ // Show the current goroutine first, if we haven't already.
+ if((gp = m->curg) != nil && gp != me) {
+ runtime·printf("\n");
+ runtime·goroutineheader(gp);
+ runtime·traceback(gp->sched.pc, gp->sched.sp, gp->sched.lr, gp);
+ }
+
for(gp = runtime·allg; gp != nil; gp = gp->alllink) {
- if(gp == me || gp->status == Gdead)
+ if(gp == me || gp == m->curg || gp->status == Gdead)
continue;
if(gp->issystem && traceback < 2)
continue;
runtime·printf("\n");
runtime·goroutineheader(gp);
- runtime·traceback(gp->sched.pc, (byte*)gp->sched.sp, 0, gp);
+ if(gp->status == Grunning) {
+ runtime·printf("\tgoroutine running on other thread; stack unavailable\n");
+ runtime·printcreatedby(gp);
+ } else
+ runtime·traceback(gp->sched.pc, gp->sched.sp, gp->sched.lr, gp);
+ }
+}
+
+static void
+checkmcount(void)
+{
+ // sched lock is held
+ if(runtime·sched.mcount > runtime·sched.maxmcount) {
+ runtime·printf("runtime: program exceeds %d-thread limit\n", runtime·sched.maxmcount);
+ runtime·throw("thread exhaustion");
}
}
@@ -256,7 +316,7 @@ mcommoninit(M *mp)
runtime·lock(&runtime·sched);
mp->id = runtime·sched.mcount++;
-
+ checkmcount();
runtime·mpreinit(mp);
// Add to runtime·allm so garbage collector doesn't free m
@@ -273,6 +333,7 @@ void
runtime·ready(G *gp)
{
// Mark runnable.
+ m->locks++; // disable preemption because it can be holding p in a local var
if(gp->status != Gwaiting) {
runtime·printf("goroutine %D has status %d\n", gp->goid, gp->status);
runtime·throw("bad g->status in ready");
@@ -281,6 +342,9 @@ runtime·ready(G *gp)
runqput(m->p, gp);
if(runtime·atomicload(&runtime·sched.npidle) != 0 && runtime·atomicload(&runtime·sched.nmspinning) == 0) // TODO: fast atomic
wakep();
+ m->locks--;
+ if(m->locks == 0 && g->preempt) // restore the preemption request in case we've cleared it in newstack
+ g->stackguard0 = StackPreempt;
}
int32
@@ -340,6 +404,34 @@ runtime·helpgc(int32 nproc)
runtime·unlock(&runtime·sched);
}
+// Similar to stoptheworld but best-effort and can be called several times.
+// There is no reverse operation, used during crashing.
+// This function must not lock any mutexes.
+void
+runtime·freezetheworld(void)
+{
+ int32 i;
+
+ if(runtime·gomaxprocs == 1)
+ return;
+ // stopwait and preemption requests can be lost
+ // due to races with concurrently executing threads,
+ // so try several times
+ for(i = 0; i < 5; i++) {
+ // this should tell the scheduler to not start any new goroutines
+ runtime·sched.stopwait = 0x7fffffff;
+ runtime·atomicstore((uint32*)&runtime·sched.gcwaiting, 1);
+ // this should stop running goroutines
+ if(!preemptall())
+ break; // no running goroutines
+ runtime·usleep(1000);
+ }
+ // to be sure
+ runtime·usleep(1000);
+ preemptall();
+ runtime·usleep(1000);
+}
+
void
runtime·stoptheworld(void)
{
@@ -350,7 +442,8 @@ runtime·stoptheworld(void)
runtime·lock(&runtime·sched);
runtime·sched.stopwait = runtime·gomaxprocs;
- runtime·atomicstore((uint32*)&runtime·gcwaiting, 1);
+ runtime·atomicstore((uint32*)&runtime·sched.gcwaiting, 1);
+ preemptall();
// stop current P
m->p->status = Pgcstop;
runtime·sched.stopwait--;
@@ -369,10 +462,16 @@ runtime·stoptheworld(void)
wait = runtime·sched.stopwait > 0;
runtime·unlock(&runtime·sched);
- // wait for remaining P's to stop voluntary
+ // wait for remaining P's to stop voluntarily
if(wait) {
- runtime·notesleep(&runtime·sched.stopnote);
- runtime·noteclear(&runtime·sched.stopnote);
+ for(;;) {
+ // wait for 100us, then try to re-preempt in case of any races
+ if(runtime·notetsleep(&runtime·sched.stopnote, 100*1000)) {
+ runtime·noteclear(&runtime·sched.stopnote);
+ break;
+ }
+ preemptall();
+ }
}
if(runtime·sched.stopwait)
runtime·throw("stoptheworld: not stopped");
@@ -397,6 +496,7 @@ runtime·starttheworld(void)
G *gp;
bool add;
+ m->locks++; // disable preemption because it can be holding p in a local var
gp = runtime·netpoll(false); // non-blocking
injectglist(gp);
add = needaddgcproc();
@@ -406,7 +506,7 @@ runtime·starttheworld(void)
newprocs = 0;
} else
procresize(runtime·gomaxprocs);
- runtime·gcwaiting = 0;
+ runtime·sched.gcwaiting = 0;
p1 = nil;
while(p = pidleget()) {
@@ -416,16 +516,9 @@ runtime·starttheworld(void)
pidleput(p);
break;
}
- mp = mget();
- if(mp == nil) {
- p->link = p1;
- p1 = p;
- continue;
- }
- if(mp->nextp)
- runtime·throw("starttheworld: inconsistent mp->nextp");
- mp->nextp = p;
- runtime·notewakeup(&mp->park);
+ p->m = mget();
+ p->link = p1;
+ p1 = p;
}
if(runtime·sched.sysmonwait) {
runtime·sched.sysmonwait = false;
@@ -436,8 +529,18 @@ runtime·starttheworld(void)
while(p1) {
p = p1;
p1 = p1->link;
- add = false;
- newm(nil, p);
+ if(p->m) {
+ mp = p->m;
+ p->m = nil;
+ if(mp->nextp)
+ runtime·throw("starttheworld: inconsistent mp->nextp");
+ mp->nextp = p;
+ runtime·notewakeup(&mp->park);
+ } else {
+ // Start M to run P. Do not start another M below.
+ newm(nil, p);
+ add = false;
+ }
}
if(add) {
@@ -450,16 +553,23 @@ runtime·starttheworld(void)
// the maximum number of procs.
newm(mhelpgc, nil);
}
+ m->locks--;
+ if(m->locks == 0 && g->preempt) // restore the preemption request in case we've cleared it in newstack
+ g->stackguard0 = StackPreempt;
}
// Called to start an M.
void
runtime·mstart(void)
{
+#ifdef GOOS_windows
+#ifdef GOARCH_386
// It is used by windows-386 only. Unfortunately, seh needs
// to be located on os stack, and mstart runs on os stack
// for both m0 and m.
SEH seh;
+#endif
+#endif
if(g != m->g0)
runtime·throw("bad runtime·mstart");
@@ -468,18 +578,20 @@ runtime·mstart(void)
// 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
+ m->g0->sched.pc = (uintptr)-1; // make sure it is never used
+ m->g0->stackguard = m->g0->stackguard0; // cgo sets only stackguard0, copy it to stackguard
+#ifdef GOOS_windows
+#ifdef GOARCH_386
m->seh = &seh;
+#endif
+#endif
runtime·asminit();
runtime·minit();
// Install signal handlers; after minit so that minit can
// prepare the thread to be able to handle the signals.
- if(m == &runtime·m0) {
+ if(m == &runtime·m0)
runtime·initsig();
- if(runtime·iscgo)
- runtime·newextram();
- }
if(m->mstartfn)
m->mstartfn();
@@ -541,6 +653,8 @@ runtime·allocm(P *p)
if(p == m->p)
releasep();
m->locks--;
+ if(m->locks == 0 && g->preempt) // restore the preemption request in case we've cleared it in newstack
+ g->stackguard0 = StackPreempt;
return mp;
}
@@ -581,12 +695,20 @@ static void unlockextra(M*);
//
// When the callback is done with the m, it calls dropm to
// put the m back on the list.
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·needm(byte x)
{
M *mp;
+ if(runtime·needextram) {
+ // Can happen if C/C++ code calls Go from a global ctor.
+ // Can not throw, because scheduler is not initialized yet.
+ runtime·write(2, "fatal error: cgo callback before cgo call\n",
+ sizeof("fatal error: cgo callback before cgo call\n")-1);
+ runtime·exit(1);
+ }
+
// Lock extra list, take head, unlock popped list.
// nilokay=false is safe here because of the invariant above,
// that the extra list always contains or will soon contain
@@ -611,12 +733,17 @@ runtime·needm(byte x)
runtime·setmg(mp, mp->g0);
g->stackbase = (uintptr)(&x + 1024);
g->stackguard = (uintptr)(&x - 32*1024);
+ g->stackguard0 = g->stackguard;
+#ifdef GOOS_windows
+#ifdef GOARCH_386
// On windows/386, we need to put an SEH frame (two words)
- // somewhere on the current stack. We are called
- // from needm, and we know there is some available
- // space one word into the argument frame. Use that.
+ // somewhere on the current stack. We are called from cgocallback_gofunc
+ // and we know that it will leave two unused words below m->curg->sched.sp.
+ // Use those.
m->seh = (SEH*)((uintptr*)&x + 1);
+#endif
+#endif
// Initialize this thread to use the m.
runtime·asminit();
@@ -639,14 +766,22 @@ runtime·newextram(void)
// the goroutine stack ends.
mp = runtime·allocm(nil);
gp = runtime·malg(4096);
- gp->sched.pc = (void*)runtime·goexit;
+ gp->sched.pc = (uintptr)runtime·goexit;
gp->sched.sp = gp->stackbase;
+ gp->sched.lr = 0;
gp->sched.g = gp;
+ gp->syscallpc = gp->sched.pc;
+ gp->syscallsp = gp->sched.sp;
+ gp->syscallstack = gp->stackbase;
+ gp->syscallguard = gp->stackguard;
gp->status = Gsyscall;
mp->curg = gp;
mp->locked = LockInternal;
mp->lockedg = gp;
gp->lockedm = mp;
+ gp->goid = runtime·xadd64(&runtime·sched.goidgen, 1);
+ if(raceenabled)
+ gp->racectx = runtime·racegostart(runtime·newextram);
// put on allg for garbage collector
runtime·lock(&runtime·sched);
if(runtime·lastg == nil)
@@ -655,9 +790,6 @@ runtime·newextram(void)
runtime·lastg->alllink = gp;
runtime·lastg = gp;
runtime·unlock(&runtime·sched);
- gp->goid = runtime·xadd64(&runtime·sched.goidgen, 1);
- if(raceenabled)
- gp->racectx = runtime·racegostart(runtime·newextram);
// Add m to the extra list.
mnext = lockextra(true);
@@ -695,7 +827,12 @@ runtime·dropm(void)
// Undo whatever initialization minit did during needm.
runtime·unminit();
+
+#ifdef GOOS_windows
+#ifdef GOARCH_386
m->seh = nil; // reset dangling typed pointer
+#endif
+#endif
// Clear m and g, and return m to the extra list.
// After the call to setmg we can only call nosplit functions.
@@ -714,7 +851,7 @@ runtime·dropm(void)
// to runtime.extram. If nilokay is true, then lockextra will
// return a nil list head if that's what it finds. If nilokay is false,
// lockextra will keep waiting until the list head is no longer nil.
-#pragma textflag 7
+#pragma textflag NOSPLIT
static M*
lockextra(bool nilokay)
{
@@ -742,7 +879,7 @@ lockextra(bool nilokay)
return mp;
}
-#pragma textflag 7
+#pragma textflag NOSPLIT
static void
unlockextra(M *mp)
{
@@ -863,7 +1000,7 @@ handoffp(P *p)
return;
}
runtime·lock(&runtime·sched);
- if(runtime·gcwaiting) {
+ if(runtime·sched.gcwaiting) {
p->status = Pgcstop;
if(--runtime·sched.stopwait == 0)
runtime·notewakeup(&runtime·sched.stopnote);
@@ -911,7 +1048,7 @@ stoplockedm(void)
p = releasep();
handoffp(p);
}
- inclocked(1);
+ incidlelocked(1);
// Wait until another thread schedules lockedg again.
runtime·notesleep(&m->park);
runtime·noteclear(&m->park);
@@ -934,7 +1071,7 @@ startlockedm(G *gp)
if(mp->nextp)
runtime·throw("startlockedm: m has p");
// directly handoff current P to the locked m
- inclocked(-1);
+ incidlelocked(-1);
p = releasep();
mp->nextp = p;
runtime·notewakeup(&mp->park);
@@ -948,7 +1085,7 @@ gcstopm(void)
{
P *p;
- if(!runtime·gcwaiting)
+ if(!runtime·sched.gcwaiting)
runtime·throw("gcstopm: not waiting for gc");
if(m->spinning) {
m->spinning = false;
@@ -975,7 +1112,9 @@ execute(G *gp)
runtime·throw("execute: bad g status");
}
gp->status = Grunning;
- m->p->tick++;
+ gp->preempt = false;
+ gp->stackguard0 = gp->stackguard;
+ m->p->schedtick++;
m->curg = gp;
gp->m = m;
@@ -984,9 +1123,7 @@ execute(G *gp)
if(m->profilehz != hz)
runtime·resetcpuprofiler(hz);
- if(gp->sched.pc == (byte*)runtime·goexit) // kickoff
- runtime·gogocallfn(&gp->sched, gp->fnstart);
- runtime·gogo(&gp->sched, 0);
+ runtime·gogo(&gp->sched);
}
// Finds a runnable goroutine to execute.
@@ -999,7 +1136,7 @@ findrunnable(void)
int32 i;
top:
- if(runtime·gcwaiting) {
+ if(runtime·sched.gcwaiting) {
gcstopm();
goto top;
}
@@ -1010,7 +1147,7 @@ top:
// global runq
if(runtime·sched.runqsize) {
runtime·lock(&runtime·sched);
- gp = globrunqget(m->p);
+ gp = globrunqget(m->p, 0);
runtime·unlock(&runtime·sched);
if(gp)
return gp;
@@ -1033,7 +1170,7 @@ top:
}
// random steal from other P's
for(i = 0; i < 2*runtime·gomaxprocs; i++) {
- if(runtime·gcwaiting)
+ if(runtime·sched.gcwaiting)
goto top;
p = runtime·allp[runtime·fastrand1()%runtime·gomaxprocs];
if(p == m->p)
@@ -1046,12 +1183,12 @@ top:
stop:
// return P and block
runtime·lock(&runtime·sched);
- if(runtime·gcwaiting) {
+ if(runtime·sched.gcwaiting) {
runtime·unlock(&runtime·sched);
goto top;
}
if(runtime·sched.runqsize) {
- gp = globrunqget(m->p);
+ gp = globrunqget(m->p, 0);
runtime·unlock(&runtime·sched);
return gp;
}
@@ -1101,6 +1238,25 @@ stop:
goto top;
}
+static void
+resetspinning(void)
+{
+ int32 nmspinning;
+
+ if(m->spinning) {
+ m->spinning = false;
+ nmspinning = runtime·xadd(&runtime·sched.nmspinning, -1);
+ if(nmspinning < 0)
+ runtime·throw("findrunnable: negative nmspinning");
+ } else
+ nmspinning = runtime·atomicload(&runtime·sched.nmspinning);
+
+ // M wakeup policy is deliberately somewhat conservative (see nmspinning handling),
+ // so see if we need to wakeup another P here.
+ if (nmspinning == 0 && runtime·atomicload(&runtime·sched.npidle) > 0)
+ wakep();
+}
+
// Injects the list of runnable G's into the scheduler.
// Can run concurrently with GC.
static void
@@ -1130,33 +1286,44 @@ static void
schedule(void)
{
G *gp;
+ uint32 tick;
if(m->locks)
runtime·throw("schedule: holding locks");
top:
- if(runtime·gcwaiting) {
+ if(runtime·sched.gcwaiting) {
gcstopm();
goto top;
}
- gp = runqget(m->p);
- if(gp == nil)
- gp = findrunnable();
-
- if(m->spinning) {
- m->spinning = false;
- runtime·xadd(&runtime·sched.nmspinning, -1);
+ gp = nil;
+ // Check the global runnable queue once in a while to ensure fairness.
+ // Otherwise two goroutines can completely occupy the local runqueue
+ // by constantly respawning each other.
+ tick = m->p->schedtick;
+ // This is a fancy way to say tick%61==0,
+ // it uses 2 MUL instructions instead of a single DIV and so is faster on modern processors.
+ if(tick - (((uint64)tick*0x4325c53fu)>>36)*61 == 0 && runtime·sched.runqsize > 0) {
+ runtime·lock(&runtime·sched);
+ gp = globrunqget(m->p, 1);
+ runtime·unlock(&runtime·sched);
+ if(gp)
+ resetspinning();
+ }
+ if(gp == nil) {
+ gp = runqget(m->p);
+ if(gp && m->spinning)
+ runtime·throw("schedule: spinning with local work");
+ }
+ if(gp == nil) {
+ gp = findrunnable(); // blocks until work is available
+ resetspinning();
}
-
- // M wakeup policy is deliberately somewhat conservative (see nmspinning handling),
- // so see if we need to wakeup another M here.
- if (m->p->runqhead != m->p->runqtail &&
- runtime·atomicload(&runtime·sched.nmspinning) == 0 &&
- runtime·atomicload(&runtime·sched.npidle) > 0) // TODO: fast atomic
- wakep();
if(gp->lockedm) {
+ // Hands off own p to the locked m,
+ // then blocks waiting for a new p.
startlockedm(gp);
goto top;
}
@@ -1198,12 +1365,12 @@ park0(G *gp)
void
runtime·gosched(void)
{
- runtime·mcall(gosched0);
+ runtime·mcall(runtime·gosched0);
}
// runtime·gosched continuation on g0.
-static void
-gosched0(G *gp)
+void
+runtime·gosched0(G *gp)
{
gp->status = Grunnable;
gp->m = nil;
@@ -1219,6 +1386,10 @@ gosched0(G *gp)
}
// Finishes execution of the current goroutine.
+// Need to mark it as nosplit, because it runs with sp > stackbase (as runtime·lessstack).
+// Since it does not return it does not matter. But if it is preempted
+// at the split stack check, GC will complain about inconsistent sp.
+#pragma textflag NOSPLIT
void
runtime·goexit(void)
{
@@ -1232,13 +1403,12 @@ static void
goexit0(G *gp)
{
gp->status = Gdead;
- gp->fnstart = nil;
gp->m = nil;
gp->lockedm = nil;
m->curg = nil;
m->lockedg = nil;
if(m->locked & ~LockExternal) {
- runtime·printf("invalid m->locked = %d", m->locked);
+ runtime·printf("invalid m->locked = %d\n", m->locked);
runtime·throw("internal lockOSThread error");
}
m->locked = 0;
@@ -1247,6 +1417,18 @@ goexit0(G *gp)
schedule();
}
+#pragma textflag NOSPLIT
+static void
+save(void *pc, uintptr sp)
+{
+ g->sched.pc = (uintptr)pc;
+ g->sched.sp = sp;
+ g->sched.lr = 0;
+ g->sched.ret = 0;
+ g->sched.ctxt = 0;
+ g->sched.g = g;
+}
+
// The goroutine g is about to enter a system call.
// Record that it's not using the cpu anymore.
// This is called only from the go syscall library and cgocall,
@@ -1255,25 +1437,24 @@ goexit0(G *gp)
// Entersyscall cannot split the stack: the runtime·gosave must
// make g->sched refer to the caller's stack segment, because
// entersyscall is going to return immediately after.
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
·entersyscall(int32 dummy)
{
- if(m->profilehz > 0)
- runtime·setprof(false);
+ // Disable preemption because during this function g is in Gsyscall status,
+ // but can have inconsistent g->sched, do not let GC observe it.
+ m->locks++;
- // Leave SP around for gc and traceback.
- g->sched.sp = (uintptr)runtime·getcallersp(&dummy);
- g->sched.pc = runtime·getcallerpc(&dummy);
- g->sched.g = g;
- g->gcsp = g->sched.sp;
- g->gcpc = g->sched.pc;
- g->gcstack = g->stackbase;
- g->gcguard = g->stackguard;
+ // Leave SP around for GC and traceback.
+ save(runtime·getcallerpc(&dummy), runtime·getcallersp(&dummy));
+ g->syscallsp = g->sched.sp;
+ g->syscallpc = g->sched.pc;
+ g->syscallstack = g->stackbase;
+ g->syscallguard = g->stackguard;
g->status = Gsyscall;
- if(g->gcsp < g->gcguard-StackGuard || g->gcstack < g->gcsp) {
+ if(g->syscallsp < g->syscallguard-StackGuard || g->syscallstack < g->syscallsp) {
// runtime·printf("entersyscall inconsistent %p [%p,%p]\n",
- // g->gcsp, g->gcguard-StackGuard, g->gcstack);
+ // g->syscallsp, g->syscallguard-StackGuard, g->syscallstack);
runtime·throw("entersyscall");
}
@@ -1284,100 +1465,96 @@ void
runtime·notewakeup(&runtime·sched.sysmonnote);
}
runtime·unlock(&runtime·sched);
- runtime·gosave(&g->sched); // re-save for traceback
+ save(runtime·getcallerpc(&dummy), runtime·getcallersp(&dummy));
}
m->mcache = nil;
- m->p->tick++;
m->p->m = nil;
runtime·atomicstore(&m->p->status, Psyscall);
- if(runtime·gcwaiting) {
+ if(runtime·sched.gcwaiting) {
runtime·lock(&runtime·sched);
if (runtime·sched.stopwait > 0 && runtime·cas(&m->p->status, Psyscall, Pgcstop)) {
if(--runtime·sched.stopwait == 0)
runtime·notewakeup(&runtime·sched.stopnote);
}
runtime·unlock(&runtime·sched);
- runtime·gosave(&g->sched); // re-save for traceback
+ save(runtime·getcallerpc(&dummy), runtime·getcallersp(&dummy));
}
+
+ // Goroutines must not split stacks in Gsyscall status (it would corrupt g->sched).
+ // We set stackguard to StackPreempt so that first split stack check calls morestack.
+ // Morestack detects this case and throws.
+ g->stackguard0 = StackPreempt;
+ m->locks--;
}
// The same as runtime·entersyscall(), but with a hint that the syscall is blocking.
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
·entersyscallblock(int32 dummy)
{
P *p;
- if(m->profilehz > 0)
- runtime·setprof(false);
+ m->locks++; // see comment in entersyscall
- // Leave SP around for gc and traceback.
- g->sched.sp = (uintptr)runtime·getcallersp(&dummy);
- g->sched.pc = runtime·getcallerpc(&dummy);
- g->sched.g = g;
- g->gcsp = g->sched.sp;
- g->gcpc = g->sched.pc;
- g->gcstack = g->stackbase;
- g->gcguard = g->stackguard;
+ // Leave SP around for GC and traceback.
+ save(runtime·getcallerpc(&dummy), runtime·getcallersp(&dummy));
+ g->syscallsp = g->sched.sp;
+ g->syscallpc = g->sched.pc;
+ g->syscallstack = g->stackbase;
+ g->syscallguard = g->stackguard;
g->status = Gsyscall;
- if(g->gcsp < g->gcguard-StackGuard || g->gcstack < g->gcsp) {
- // runtime·printf("entersyscallblock inconsistent %p [%p,%p]\n",
- // g->gcsp, g->gcguard-StackGuard, g->gcstack);
+ if(g->syscallsp < g->syscallguard-StackGuard || g->syscallstack < g->syscallsp) {
+ // runtime·printf("entersyscall inconsistent %p [%p,%p]\n",
+ // g->syscallsp, g->syscallguard-StackGuard, g->syscallstack);
runtime·throw("entersyscallblock");
}
p = releasep();
handoffp(p);
if(g->isbackground) // do not consider blocked scavenger for deadlock detection
- inclocked(1);
- runtime·gosave(&g->sched); // re-save for traceback
+ incidlelocked(1);
+
+ // Resave for traceback during blocked call.
+ save(runtime·getcallerpc(&dummy), runtime·getcallersp(&dummy));
+
+ g->stackguard0 = StackPreempt; // see comment in entersyscall
+ m->locks--;
}
// The goroutine g exited its system call.
// Arrange for it to run on a cpu again.
// This is called only from the go syscall library, not
// from the low-level system calls used by the runtime.
+#pragma textflag NOSPLIT
void
runtime·exitsyscall(void)
{
- P *p;
+ m->locks++; // see comment in entersyscall
- // Check whether the profiler needs to be turned on.
- if(m->profilehz > 0)
- runtime·setprof(true);
+ if(g->isbackground) // do not consider blocked scavenger for deadlock detection
+ incidlelocked(-1);
- // Try to re-acquire the last P.
- if(m->p && m->p->status == Psyscall && runtime·cas(&m->p->status, Psyscall, Prunning)) {
+ if(exitsyscallfast()) {
// There's a cpu for us, so we can run.
- m->mcache = m->p->mcache;
- m->p->m = m;
- m->p->tick++;
+ m->p->syscalltick++;
g->status = Grunning;
// Garbage collector isn't running (since we are),
// so okay to clear gcstack and gcsp.
- g->gcstack = (uintptr)nil;
- g->gcsp = (uintptr)nil;
+ g->syscallstack = (uintptr)nil;
+ g->syscallsp = (uintptr)nil;
+ m->locks--;
+ if(g->preempt) {
+ // restore the preemption request in case we've cleared it in newstack
+ g->stackguard0 = StackPreempt;
+ } else {
+ // otherwise restore the real stackguard, we've spoiled it in entersyscall/entersyscallblock
+ g->stackguard0 = g->stackguard;
+ }
return;
}
- if(g->isbackground) // do not consider blocked scavenger for deadlock detection
- inclocked(-1);
- // Try to get any other idle P.
- m->p = nil;
- if(runtime·sched.pidle) {
- runtime·lock(&runtime·sched);
- p = pidleget();
- runtime·unlock(&runtime·sched);
- if(p) {
- acquirep(p);
- m->p->tick++;
- g->status = Grunning;
- g->gcstack = (uintptr)nil;
- g->gcsp = (uintptr)nil;
- return;
- }
- }
+ m->locks--;
// Call the scheduler.
runtime·mcall(exitsyscall0);
@@ -1388,8 +1565,46 @@ runtime·exitsyscall(void)
// Must wait until now because until gosched returns
// we don't know for sure that the garbage collector
// is not running.
- g->gcstack = (uintptr)nil;
- g->gcsp = (uintptr)nil;
+ g->syscallstack = (uintptr)nil;
+ g->syscallsp = (uintptr)nil;
+ m->p->syscalltick++;
+}
+
+#pragma textflag NOSPLIT
+static bool
+exitsyscallfast(void)
+{
+ P *p;
+
+ // Freezetheworld sets stopwait but does not retake P's.
+ if(runtime·sched.stopwait) {
+ m->p = nil;
+ return false;
+ }
+
+ // Try to re-acquire the last P.
+ if(m->p && m->p->status == Psyscall && runtime·cas(&m->p->status, Psyscall, Prunning)) {
+ // There's a cpu for us, so we can run.
+ m->mcache = m->p->mcache;
+ m->p->m = m;
+ return true;
+ }
+ // Try to get any other idle P.
+ m->p = nil;
+ if(runtime·sched.pidle) {
+ runtime·lock(&runtime·sched);
+ p = pidleget();
+ if(p && runtime·atomicload(&runtime·sched.sysmonwait)) {
+ runtime·atomicstore(&runtime·sched.sysmonwait, 0);
+ runtime·notewakeup(&runtime·sched.sysmonnote);
+ }
+ runtime·unlock(&runtime·sched);
+ if(p) {
+ acquirep(p);
+ return true;
+ }
+ }
+ return false;
}
// runtime·exitsyscall slow path on g0.
@@ -1406,6 +1621,10 @@ exitsyscall0(G *gp)
p = pidleget();
if(p == nil)
globrunqput(gp);
+ else if(runtime·atomicload(&runtime·sched.sysmonwait)) {
+ runtime·atomicstore(&runtime·sched.sysmonwait, 0);
+ runtime·notewakeup(&runtime·sched.sysmonnote);
+ }
runtime·unlock(&runtime·sched);
if(p) {
acquirep(p);
@@ -1420,6 +1639,29 @@ exitsyscall0(G *gp)
schedule(); // Never returns.
}
+// Called from syscall package before fork.
+void
+syscall·runtime_BeforeFork(void)
+{
+ // Fork can hang if preempted with signals frequently enough (see issue 5517).
+ // Ensure that we stay on the same M where we disable profiling.
+ m->locks++;
+ if(m->profilehz != 0)
+ runtime·resetcpuprofiler(0);
+}
+
+// Called from syscall package after fork in parent.
+void
+syscall·runtime_AfterFork(void)
+{
+ int32 hz;
+
+ hz = runtime·sched.profilehz;
+ if(hz != 0)
+ runtime·resetcpuprofiler(hz);
+ m->locks--;
+}
+
// Hook used by runtime·malg to call runtime·stackalloc on the
// scheduler stack. This exists because runtime·stackalloc insists
// on being called on the scheduler stack, to avoid trying to grow
@@ -1428,7 +1670,7 @@ static void
mstackalloc(G *gp)
{
gp->param = runtime·stackalloc((uintptr)gp->param);
- runtime·gogo(&gp->sched, 0);
+ runtime·gogo(&gp->sched);
}
// Allocate a new g, with a stack big enough for stacksize bytes.
@@ -1455,8 +1697,10 @@ runtime·malg(int32 stacksize)
stk = g->param;
g->param = nil;
}
+ newg->stacksize = StackSystem + stacksize;
newg->stack0 = (uintptr)stk;
newg->stackguard = (uintptr)stk + StackGuard;
+ newg->stackguard0 = newg->stackguard;
newg->stackbase = (uintptr)stk + StackSystem + stacksize - sizeof(Stktop);
runtime·memclr((byte*)newg->stackbase, sizeof(Stktop));
}
@@ -1470,7 +1714,7 @@ runtime·malg(int32 stacksize)
// are available sequentially after &fn; they would not be
// copied if a stack split occurred. It's OK for this to call
// functions that split the stack.
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·newproc(int32 siz, FuncVal* fn, ...)
{
@@ -1494,7 +1738,8 @@ runtime·newproc1(FuncVal *fn, byte *argp, int32 narg, int32 nret, void *callerp
G *newg;
int32 siz;
-//printf("newproc1 %p %p narg=%d nret=%d\n", fn, argp, narg, nret);
+//runtime·printf("newproc1 %p %p narg=%d nret=%d\n", fn->fn, argp, narg, nret);
+ m->locks++; // disable preemption because it can be holding p in a local var
siz = narg + nret;
siz = (siz+7) & ~7;
@@ -1528,19 +1773,24 @@ runtime·newproc1(FuncVal *fn, byte *argp, int32 narg, int32 nret, void *callerp
*(void**)sp = nil;
}
+ runtime·memclr((byte*)&newg->sched, sizeof newg->sched);
newg->sched.sp = (uintptr)sp;
- newg->sched.pc = (byte*)runtime·goexit;
+ newg->sched.pc = (uintptr)runtime·goexit;
newg->sched.g = newg;
- newg->fnstart = fn;
+ runtime·gostartcallfn(&newg->sched, fn);
newg->gopc = (uintptr)callerpc;
newg->status = Grunnable;
newg->goid = runtime·xadd64(&runtime·sched.goidgen, 1);
+ newg->panicwrap = 0;
if(raceenabled)
- newg->racectx = runtime·racegostart(callerpc);
+ newg->racectx = runtime·racegostart((void*)callerpc);
runqput(m->p, newg);
if(runtime·atomicload(&runtime·sched.npidle) != 0 && runtime·atomicload(&runtime·sched.nmspinning) == 0 && fn->fn != runtime·main) // TODO: fast atomic
wakep();
+ m->locks--;
+ if(m->locks == 0 && g->preempt) // restore the preemption request in case we've cleared it in newstack
+ g->stackguard0 = StackPreempt;
return newg;
}
@@ -1641,7 +1891,7 @@ runtime·gomaxprocsfunc(int32 n)
}
runtime·unlock(&runtime·sched);
- runtime·semacquire(&runtime·worldsema);
+ runtime·semacquire(&runtime·worldsema, false);
m->gcing = 1;
runtime·stoptheworld();
newprocs = n;
@@ -1652,8 +1902,12 @@ runtime·gomaxprocsfunc(int32 n)
return ret;
}
+// lockOSThread is called by runtime.LockOSThread and runtime.lockOSThread below
+// after they modify m->locked. Do not allow preemption during this call,
+// or else the m might be different in this function than in the caller.
+#pragma textflag NOSPLIT
static void
-LockOSThread(void)
+lockOSThread(void)
{
m->lockedg = g;
g->lockedm = m;
@@ -1663,18 +1917,23 @@ void
runtime·LockOSThread(void)
{
m->locked |= LockExternal;
- LockOSThread();
+ lockOSThread();
}
void
runtime·lockOSThread(void)
{
m->locked += LockInternal;
- LockOSThread();
+ lockOSThread();
}
+
+// unlockOSThread is called by runtime.UnlockOSThread and runtime.unlockOSThread below
+// after they update m->locked. Do not allow preemption during this call,
+// or else the m might be in different in this function than in the caller.
+#pragma textflag NOSPLIT
static void
-UnlockOSThread(void)
+unlockOSThread(void)
{
if(m->locked != 0)
return;
@@ -1686,7 +1945,7 @@ void
runtime·UnlockOSThread(void)
{
m->locked &= ~LockExternal;
- UnlockOSThread();
+ unlockOSThread();
}
void
@@ -1695,7 +1954,7 @@ runtime·unlockOSThread(void)
if(m->locked < LockInternal)
runtime·throw("runtime: internal error: misuse of lockOSThread/unlockOSThread");
m->locked -= LockInternal;
- UnlockOSThread();
+ unlockOSThread();
}
bool
@@ -1712,14 +1971,6 @@ runtime·golockedOSThread(bool ret)
FLUSH(&ret);
}
-// for testing of wire, unwire
-void
-runtime·mid(uint32 ret)
-{
- ret = m->id;
- FLUSH(&ret);
-}
-
void
runtime·NumGoroutine(intgo ret)
{
@@ -1755,17 +2006,25 @@ runtime·mcount(void)
}
void
-runtime·badmcall(void) // called from assembly
+runtime·badmcall(void (*fn)(G*)) // called from assembly
{
+ USED(fn); // TODO: print fn?
runtime·throw("runtime: mcall called on m->g0 stack");
}
void
-runtime·badmcall2(void) // called from assembly
+runtime·badmcall2(void (*fn)(G*)) // called from assembly
{
+ USED(fn);
runtime·throw("runtime: mcall function returned");
}
+void
+runtime·badreflectcall(void) // called from assembly
+{
+ runtime·panicstring("runtime: arg size to reflect.call more than 1GB");
+}
+
static struct {
Lock;
void (*fn)(uintptr*, int32);
@@ -1773,26 +2032,126 @@ static struct {
uintptr pcbuf[100];
} prof;
+static void
+System(void)
+{
+}
+
// Called if we receive a SIGPROF signal.
void
runtime·sigprof(uint8 *pc, uint8 *sp, uint8 *lr, G *gp)
{
int32 n;
+ bool traceback;
- // Windows does profiling in a dedicated thread w/o m.
- if(!Windows && (m == nil || m->mcache == nil))
- return;
if(prof.fn == nil || prof.hz == 0)
return;
+ traceback = true;
+ // Windows does profiling in a dedicated thread w/o m.
+ if(!Windows && (m == nil || m->mcache == nil))
+ traceback = false;
+
+ // Define that a "user g" is a user-created goroutine, and a "system g"
+ // is one that is m->g0 or m->gsignal. We've only made sure that we
+ // can unwind user g's, so exclude the system g's.
+ //
+ // It is not quite as easy as testing gp == m->curg (the current user g)
+ // because we might be interrupted for profiling halfway through a
+ // goroutine switch. The switch involves updating three (or four) values:
+ // g, PC, SP, and (on arm) LR. The PC must be the last to be updated,
+ // because once it gets updated the new g is running.
+ //
+ // When switching from a user g to a system g, LR is not considered live,
+ // so the update only affects g, SP, and PC. Since PC must be last, there
+ // the possible partial transitions in ordinary execution are (1) g alone is updated,
+ // (2) both g and SP are updated, and (3) SP alone is updated.
+ // If g is updated, we'll see a system g and not look closer.
+ // If SP alone is updated, we can detect the partial transition by checking
+ // whether the SP is within g's stack bounds. (We could also require that SP
+ // be changed only after g, but the stack bounds check is needed by other
+ // cases, so there is no need to impose an additional requirement.)
+ //
+ // There is one exceptional transition to a system g, not in ordinary execution.
+ // When a signal arrives, the operating system starts the signal handler running
+ // with an updated PC and SP. The g is updated last, at the beginning of the
+ // handler. There are two reasons this is okay. First, until g is updated the
+ // g and SP do not match, so the stack bounds check detects the partial transition.
+ // Second, signal handlers currently run with signals disabled, so a profiling
+ // signal cannot arrive during the handler.
+ //
+ // When switching from a system g to a user g, there are three possibilities.
+ //
+ // First, it may be that the g switch has no PC update, because the SP
+ // either corresponds to a user g throughout (as in runtime.asmcgocall)
+ // or because it has been arranged to look like a user g frame
+ // (as in runtime.cgocallback_gofunc). In this case, since the entire
+ // transition is a g+SP update, a partial transition updating just one of
+ // those will be detected by the stack bounds check.
+ //
+ // Second, when returning from a signal handler, the PC and SP updates
+ // are performed by the operating system in an atomic update, so the g
+ // update must be done before them. The stack bounds check detects
+ // the partial transition here, and (again) signal handlers run with signals
+ // disabled, so a profiling signal cannot arrive then anyway.
+ //
+ // Third, the common case: it may be that the switch updates g, SP, and PC
+ // separately, as in runtime.gogo.
+ //
+ // Because runtime.gogo is the only instance, we check whether the PC lies
+ // within that function, and if so, not ask for a traceback. This approach
+ // requires knowing the size of the runtime.gogo function, which we
+ // record in arch_*.h and check in runtime_test.go.
+ //
+ // There is another apparently viable approach, recorded here in case
+ // the "PC within runtime.gogo" check turns out not to be usable.
+ // It would be possible to delay the update of either g or SP until immediately
+ // before the PC update instruction. Then, because of the stack bounds check,
+ // the only problematic interrupt point is just before that PC update instruction,
+ // and the sigprof handler can detect that instruction and simulate stepping past
+ // it in order to reach a consistent state. On ARM, the update of g must be made
+ // in two places (in R10 and also in a TLS slot), so the delayed update would
+ // need to be the SP update. The sigprof handler must read the instruction at
+ // the current PC and if it was the known instruction (for example, JMP BX or
+ // MOV R2, PC), use that other register in place of the PC value.
+ // The biggest drawback to this solution is that it requires that we can tell
+ // whether it's safe to read from the memory pointed at by PC.
+ // In a correct program, we can test PC == nil and otherwise read,
+ // but if a profiling signal happens at the instant that a program executes
+ // a bad jump (before the program manages to handle the resulting fault)
+ // the profiling handler could fault trying to read nonexistent memory.
+ //
+ // To recap, there are no constraints on the assembly being used for the
+ // transition. We simply require that g and SP match and that the PC is not
+ // in runtime.gogo.
+ //
+ // On Windows, one m is sending reports about all the g's, so gp == m->curg
+ // is not a useful comparison. The profilem function in os_windows.c has
+ // already checked that gp is a user g.
+ if(gp == nil ||
+ (!Windows && gp != m->curg) ||
+ (uintptr)sp < gp->stackguard - StackGuard || gp->stackbase < (uintptr)sp ||
+ ((uint8*)runtime·gogo <= pc && pc < (uint8*)runtime·gogo + RuntimeGogoBytes))
+ traceback = false;
+
+ // Race detector calls asmcgocall w/o entersyscall/exitsyscall,
+ // we can not currently unwind through asmcgocall.
+ if(m != nil && m->racecall)
+ traceback = false;
runtime·lock(&prof);
if(prof.fn == nil) {
runtime·unlock(&prof);
return;
}
- n = runtime·gentraceback(pc, sp, lr, gp, 0, prof.pcbuf, nelem(prof.pcbuf), nil, nil);
- if(n > 0)
- prof.fn(prof.pcbuf, n);
+ n = 0;
+ if(traceback)
+ n = runtime·gentraceback((uintptr)pc, (uintptr)sp, (uintptr)lr, gp, 0, prof.pcbuf, nelem(prof.pcbuf), nil, nil, false);
+ if(!traceback || n <= 0) {
+ n = 2;
+ prof.pcbuf[0] = (uintptr)pc;
+ prof.pcbuf[1] = (uintptr)System + 1;
+ }
+ prof.fn(prof.pcbuf, n);
runtime·unlock(&prof);
}
@@ -1808,7 +2167,11 @@ runtime·setcpuprofilerate(void (*fn)(uintptr*, int32), int32 hz)
if(fn == nil)
hz = 0;
- // Stop profiler on this cpu so that it is safe to lock prof.
+ // Disable preemption, otherwise we can be rescheduled to another thread
+ // that has profiling enabled.
+ m->locks++;
+
+ // Stop profiler on this thread 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);
@@ -1823,6 +2186,8 @@ runtime·setcpuprofilerate(void (*fn)(uintptr*, int32), int32 hz)
if(hz != 0)
runtime·resetcpuprofiler(hz);
+
+ m->locks--;
}
// Change number of processors. The world is stopped, sched is locked.
@@ -1840,7 +2205,8 @@ procresize(int32 new)
for(i = 0; i < new; i++) {
p = runtime·allp[i];
if(p == nil) {
- p = (P*)runtime·mallocgc(sizeof(*p), 0, 0, 1);
+ p = (P*)runtime·mallocgc(sizeof(*p), 0, FlagNoInvokeGC);
+ p->id = i;
p->status = Pgcstop;
runtime·atomicstorep(&runtime·allp[i], p);
}
@@ -1852,7 +2218,7 @@ procresize(int32 new)
}
if(p->runq == nil) {
p->runqsize = 128;
- p->runq = (G**)runtime·mallocgc(p->runqsize*sizeof(G*), 0, 0, 1);
+ p->runq = (G**)runtime·mallocgc(p->runqsize*sizeof(G*), 0, FlagNoInvokeGC);
}
}
@@ -1895,7 +2261,6 @@ procresize(int32 new)
p->status = Pidle;
pidleput(p);
}
- runtime·singleproc = new == 1;
runtime·atomicstore((uint32*)&runtime·gomaxprocs, new);
}
@@ -1937,10 +2302,10 @@ releasep(void)
}
static void
-inclocked(int32 v)
+incidlelocked(int32 v)
{
runtime·lock(&runtime·sched);
- runtime·sched.mlocked += v;
+ runtime·sched.nmidlelocked += v;
if(v > 0)
checkdead();
runtime·unlock(&runtime·sched);
@@ -1955,12 +2320,12 @@ checkdead(void)
int32 run, grunning, s;
// -1 for sysmon
- run = runtime·sched.mcount - runtime·sched.nmidle - runtime·sched.mlocked - 1;
+ run = runtime·sched.mcount - runtime·sched.nmidle - runtime·sched.nmidlelocked - 1;
if(run > 0)
return;
if(run < 0) {
- runtime·printf("checkdead: nmidle=%d mlocked=%d mcount=%d\n",
- runtime·sched.nmidle, runtime·sched.mlocked, runtime·sched.mcount);
+ runtime·printf("checkdead: nmidle=%d nmidlelocked=%d mcount=%d\n",
+ runtime·sched.nmidle, runtime·sched.nmidlelocked, runtime·sched.mcount);
runtime·throw("checkdead: inconsistent counts");
}
grunning = 0;
@@ -1985,10 +2350,10 @@ static void
sysmon(void)
{
uint32 idle, delay;
- int64 now, lastpoll;
+ int64 now, lastpoll, lasttrace;
G *gp;
- uint32 ticks[MaxGomaxprocs];
+ lasttrace = 0;
idle = 0; // how many cycles in succession we had not wokeup somebody
delay = 0;
for(;;) {
@@ -1999,9 +2364,10 @@ sysmon(void)
if(delay > 10*1000) // up to 10ms
delay = 10*1000;
runtime·usleep(delay);
- if(runtime·gcwaiting || runtime·atomicload(&runtime·sched.npidle) == runtime·gomaxprocs) { // TODO: fast atomic
+ if(runtime·debug.schedtrace <= 0 &&
+ (runtime·sched.gcwaiting || runtime·atomicload(&runtime·sched.npidle) == runtime·gomaxprocs)) { // TODO: fast atomic
runtime·lock(&runtime·sched);
- if(runtime·atomicload(&runtime·gcwaiting) || runtime·atomicload(&runtime·sched.npidle) == runtime·gomaxprocs) {
+ if(runtime·atomicload(&runtime·sched.gcwaiting) || runtime·atomicload(&runtime·sched.npidle) == runtime·gomaxprocs) {
runtime·atomicstore(&runtime·sched.sysmonwait, 1);
runtime·unlock(&runtime·sched);
runtime·notesleep(&runtime·sched.sysmonnote);
@@ -2014,51 +2380,231 @@ sysmon(void)
// poll network if not polled for more than 10ms
lastpoll = runtime·atomicload64(&runtime·sched.lastpoll);
now = runtime·nanotime();
- if(lastpoll != 0 && lastpoll + 10*1000*1000 > now) {
+ if(lastpoll != 0 && lastpoll + 10*1000*1000 < now) {
+ runtime·cas64(&runtime·sched.lastpoll, lastpoll, now);
gp = runtime·netpoll(false); // non-blocking
- injectglist(gp);
+ if(gp) {
+ // Need to decrement number of idle locked M's
+ // (pretending that one more is running) before injectglist.
+ // Otherwise it can lead to the following situation:
+ // injectglist grabs all P's but before it starts M's to run the P's,
+ // another M returns from syscall, finishes running its G,
+ // observes that there is no work to do and no other running M's
+ // and reports deadlock.
+ incidlelocked(-1);
+ injectglist(gp);
+ incidlelocked(1);
+ }
}
// retake P's blocked in syscalls
- if(retake(ticks))
+ // and preempt long running G's
+ if(retake(now))
idle = 0;
else
idle++;
+
+ if(runtime·debug.schedtrace > 0 && lasttrace + runtime·debug.schedtrace*1000000ll <= now) {
+ lasttrace = now;
+ runtime·schedtrace(runtime·debug.scheddetail);
+ }
}
}
+typedef struct Pdesc Pdesc;
+struct Pdesc
+{
+ uint32 schedtick;
+ int64 schedwhen;
+ uint32 syscalltick;
+ int64 syscallwhen;
+};
+static Pdesc pdesc[MaxGomaxprocs];
+
static uint32
-retake(uint32 *ticks)
+retake(int64 now)
{
uint32 i, s, n;
int64 t;
P *p;
+ Pdesc *pd;
n = 0;
for(i = 0; i < runtime·gomaxprocs; i++) {
p = runtime·allp[i];
if(p==nil)
continue;
- t = p->tick;
- if(ticks[i] != t) {
- ticks[i] = t;
- continue;
- }
+ pd = &pdesc[i];
s = p->status;
- if(s != Psyscall)
+ if(s == Psyscall) {
+ // Retake P from syscall if it's there for more than 1 sysmon tick (20us).
+ // But only if there is other work to do.
+ t = p->syscalltick;
+ if(pd->syscalltick != t) {
+ pd->syscalltick = t;
+ pd->syscallwhen = now;
+ continue;
+ }
+ if(p->runqhead == p->runqtail &&
+ runtime·atomicload(&runtime·sched.nmspinning) + runtime·atomicload(&runtime·sched.npidle) > 0)
+ continue;
+ // Need to decrement number of idle locked M's
+ // (pretending that one more is running) before the CAS.
+ // Otherwise the M from which we retake can exit the syscall,
+ // increment nmidle and report deadlock.
+ incidlelocked(-1);
+ if(runtime·cas(&p->status, s, Pidle)) {
+ n++;
+ handoffp(p);
+ }
+ incidlelocked(1);
+ } else if(s == Prunning) {
+ // Preempt G if it's running for more than 10ms.
+ t = p->schedtick;
+ if(pd->schedtick != t) {
+ pd->schedtick = t;
+ pd->schedwhen = now;
+ continue;
+ }
+ if(pd->schedwhen + 10*1000*1000 > now)
+ continue;
+ preemptone(p);
+ }
+ }
+ return n;
+}
+
+// Tell all goroutines that they have been preempted and they should stop.
+// This function is purely best-effort. It can fail to inform a goroutine if a
+// processor just started running it.
+// No locks need to be held.
+// Returns true if preemption request was issued to at least one goroutine.
+static bool
+preemptall(void)
+{
+ P *p;
+ int32 i;
+ bool res;
+
+ res = false;
+ for(i = 0; i < runtime·gomaxprocs; i++) {
+ p = runtime·allp[i];
+ if(p == nil || p->status != Prunning)
continue;
- if(p->runqhead == p->runqtail && runtime·atomicload(&runtime·sched.nmspinning) + runtime·atomicload(&runtime·sched.npidle) > 0) // TODO: fast atomic
+ res |= preemptone(p);
+ }
+ return res;
+}
+
+// Tell the goroutine running on processor P to stop.
+// This function is purely best-effort. It can incorrectly fail to inform the
+// goroutine. It can send inform the wrong goroutine. Even if it informs the
+// correct goroutine, that goroutine might ignore the request if it is
+// simultaneously executing runtime·newstack.
+// No lock needs to be held.
+// Returns true if preemption request was issued.
+static bool
+preemptone(P *p)
+{
+ M *mp;
+ G *gp;
+
+ mp = p->m;
+ if(mp == nil || mp == m)
+ return false;
+ gp = mp->curg;
+ if(gp == nil || gp == mp->g0)
+ return false;
+ gp->preempt = true;
+ gp->stackguard0 = StackPreempt;
+ return true;
+}
+
+void
+runtime·schedtrace(bool detailed)
+{
+ static int64 starttime;
+ int64 now;
+ int64 id1, id2, id3;
+ int32 i, q, t, h, s;
+ int8 *fmt;
+ M *mp, *lockedm;
+ G *gp, *lockedg;
+ P *p;
+
+ now = runtime·nanotime();
+ if(starttime == 0)
+ starttime = now;
+
+ runtime·lock(&runtime·sched);
+ runtime·printf("SCHED %Dms: gomaxprocs=%d idleprocs=%d threads=%d idlethreads=%d runqueue=%d",
+ (now-starttime)/1000000, runtime·gomaxprocs, runtime·sched.npidle, runtime·sched.mcount,
+ runtime·sched.nmidle, runtime·sched.runqsize);
+ if(detailed) {
+ runtime·printf(" gcwaiting=%d nmidlelocked=%d nmspinning=%d stopwait=%d sysmonwait=%d\n",
+ runtime·sched.gcwaiting, runtime·sched.nmidlelocked, runtime·sched.nmspinning,
+ runtime·sched.stopwait, runtime·sched.sysmonwait);
+ }
+ // We must be careful while reading data from P's, M's and G's.
+ // Even if we hold schedlock, most data can be changed concurrently.
+ // E.g. (p->m ? p->m->id : -1) can crash if p->m changes from non-nil to nil.
+ for(i = 0; i < runtime·gomaxprocs; i++) {
+ p = runtime·allp[i];
+ if(p == nil)
continue;
- // Need to increment number of locked M's before the CAS.
- // Otherwise the M from which we retake can exit the syscall,
- // increment nmidle and report deadlock.
- inclocked(-1);
- if(runtime·cas(&p->status, s, Pidle)) {
- n++;
- handoffp(p);
+ mp = p->m;
+ t = p->runqtail;
+ h = p->runqhead;
+ s = p->runqsize;
+ q = t - h;
+ if(q < 0)
+ q += s;
+ if(detailed)
+ runtime·printf(" P%d: status=%d schedtick=%d syscalltick=%d m=%d runqsize=%d/%d gfreecnt=%d\n",
+ i, p->status, p->schedtick, p->syscalltick, mp ? mp->id : -1, q, s, p->gfreecnt);
+ else {
+ // In non-detailed mode format lengths of per-P run queues as:
+ // [len1 len2 len3 len4]
+ fmt = " %d";
+ if(runtime·gomaxprocs == 1)
+ fmt = " [%d]\n";
+ else if(i == 0)
+ fmt = " [%d";
+ else if(i == runtime·gomaxprocs-1)
+ fmt = " %d]\n";
+ runtime·printf(fmt, q);
}
- inclocked(1);
}
- return n;
+ if(!detailed) {
+ runtime·unlock(&runtime·sched);
+ return;
+ }
+ for(mp = runtime·allm; mp; mp = mp->alllink) {
+ p = mp->p;
+ gp = mp->curg;
+ lockedg = mp->lockedg;
+ id1 = -1;
+ if(p)
+ id1 = p->id;
+ id2 = -1;
+ if(gp)
+ id2 = gp->goid;
+ id3 = -1;
+ if(lockedg)
+ id3 = lockedg->goid;
+ runtime·printf(" M%d: p=%D curg=%D mallocing=%d throwing=%d gcing=%d"
+ " locks=%d dying=%d helpgc=%d spinning=%d lockedg=%D\n",
+ mp->id, id1, id2,
+ mp->mallocing, mp->throwing, mp->gcing, mp->locks, mp->dying, mp->helpgc,
+ mp->spinning, id3);
+ }
+ for(gp = runtime·allg; gp; gp = gp->alllink) {
+ mp = gp->m;
+ lockedm = gp->lockedm;
+ runtime·printf(" G%D: status=%d(%s) m=%d lockedm=%d\n",
+ gp->goid, gp->status, gp->waitreason, mp ? mp->id : -1,
+ lockedm ? lockedm->id : -1);
+ }
+ runtime·unlock(&runtime·sched);
}
// Put mp on midle list.
@@ -2103,7 +2649,7 @@ globrunqput(G *gp)
// Try get a batch of G's from the global runnable queue.
// Sched must be locked.
static G*
-globrunqget(P *p)
+globrunqget(P *p, int32 max)
{
G *gp, *gp1;
int32 n;
@@ -2113,6 +2659,8 @@ globrunqget(P *p)
n = runtime·sched.runqsize/runtime·gomaxprocs+1;
if(n > runtime·sched.runqsize)
n = runtime·sched.runqsize;
+ if(max > 0 && n > max)
+ n = max;
runtime·sched.runqsize -= n;
if(runtime·sched.runqsize == 0)
runtime·sched.runqtail = nil;
@@ -2365,3 +2913,48 @@ runtime·testSchedLocalQueueSteal(void)
}
}
+extern void runtime·morestack(void);
+
+// Does f mark the top of a goroutine stack?
+bool
+runtime·topofstack(Func *f)
+{
+ return f->entry == (uintptr)runtime·goexit ||
+ f->entry == (uintptr)runtime·mstart ||
+ f->entry == (uintptr)runtime·mcall ||
+ f->entry == (uintptr)runtime·morestack ||
+ f->entry == (uintptr)runtime·lessstack ||
+ f->entry == (uintptr)_rt0_go;
+}
+
+void
+runtime∕debug·setMaxThreads(intgo in, intgo out)
+{
+ runtime·lock(&runtime·sched);
+ out = runtime·sched.maxmcount;
+ runtime·sched.maxmcount = in;
+ checkmcount();
+ runtime·unlock(&runtime·sched);
+ FLUSH(&out);
+}
+
+static int8 experiment[] = GOEXPERIMENT; // defined in zaexperiment.h
+
+static bool
+haveexperiment(int8 *name)
+{
+ int32 i, j;
+
+ for(i=0; i<sizeof(experiment); i++) {
+ if((i == 0 || experiment[i-1] == ',') && experiment[i] == name[0]) {
+ for(j=0; name[j]; j++)
+ if(experiment[i+j] != name[j])
+ goto nomatch;
+ if(experiment[i+j] != '\0' && experiment[i+j] != ',')
+ goto nomatch;
+ return 1;
+ }
+ nomatch:;
+ }
+ return 0;
+}
diff --git a/src/pkg/runtime/proc_test.go b/src/pkg/runtime/proc_test.go
index 21fb9c2f7..dd70ed97d 100644
--- a/src/pkg/runtime/proc_test.go
+++ b/src/pkg/runtime/proc_test.go
@@ -8,6 +8,7 @@ import (
"math"
"runtime"
"sync/atomic"
+ "syscall"
"testing"
"time"
)
@@ -92,6 +93,35 @@ func TestYieldLocked(t *testing.T) {
<-c
}
+func TestGoroutineParallelism(t *testing.T) {
+ P := 4
+ N := 10
+ if testing.Short() {
+ P = 3
+ N = 3
+ }
+ defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(P))
+ for try := 0; try < N; try++ {
+ done := make(chan bool)
+ x := uint32(0)
+ for p := 0; p < P; p++ {
+ // Test that all P goroutines are scheduled at the same time
+ go func(p int) {
+ for i := 0; i < 3; i++ {
+ expected := uint32(P*i + p)
+ for atomic.LoadUint32(&x) != expected {
+ }
+ atomic.StoreUint32(&x, expected+1)
+ }
+ done <- true
+ }(p)
+ }
+ for p := 0; p < P; p++ {
+ <-done
+ }
+ }
+}
+
func TestBlockLocked(t *testing.T) {
const N = 10
c := make(chan bool)
@@ -107,86 +137,209 @@ func TestBlockLocked(t *testing.T) {
}
}
-func stackGrowthRecursive(i int) {
- var pad [128]uint64
- if i != 0 && pad[0] == 0 {
- stackGrowthRecursive(i - 1)
+func TestTimerFairness(t *testing.T) {
+ done := make(chan bool)
+ c := make(chan bool)
+ for i := 0; i < 2; i++ {
+ go func() {
+ for {
+ select {
+ case c <- true:
+ case <-done:
+ return
+ }
+ }
+ }()
+ }
+
+ timer := time.After(20 * time.Millisecond)
+ for {
+ select {
+ case <-c:
+ case <-timer:
+ close(done)
+ return
+ }
}
}
-func TestSchedLocalQueue(t *testing.T) {
- runtime.TestSchedLocalQueue1()
+func TestTimerFairness2(t *testing.T) {
+ done := make(chan bool)
+ c := make(chan bool)
+ for i := 0; i < 2; i++ {
+ go func() {
+ timer := time.After(20 * time.Millisecond)
+ var buf [1]byte
+ for {
+ syscall.Read(0, buf[0:0])
+ select {
+ case c <- true:
+ case <-c:
+ case <-timer:
+ done <- true
+ return
+ }
+ }
+ }()
+ }
+ <-done
+ <-done
}
-func TestSchedLocalQueueSteal(t *testing.T) {
- runtime.TestSchedLocalQueueSteal1()
+// The function is used to test preemption at split stack checks.
+// Declaring a var avoids inlining at the call site.
+var preempt = func() int {
+ var a [128]int
+ sum := 0
+ for _, v := range a {
+ sum += v
+ }
+ return sum
}
-func benchmarkStackGrowth(b *testing.B, rec int) {
- const CallsPerSched = 1000
- procs := runtime.GOMAXPROCS(-1)
- N := int32(b.N / CallsPerSched)
- c := make(chan bool, procs)
- for p := 0; p < procs; p++ {
- go func() {
- for atomic.AddInt32(&N, -1) >= 0 {
- runtime.Gosched()
- for g := 0; g < CallsPerSched; g++ {
- stackGrowthRecursive(rec)
+func TestPreemption(t *testing.T) {
+ // Test that goroutines are preempted at function calls.
+ N := 5
+ if testing.Short() {
+ N = 2
+ }
+ c := make(chan bool)
+ var x uint32
+ for g := 0; g < 2; g++ {
+ go func(g int) {
+ for i := 0; i < N; i++ {
+ for atomic.LoadUint32(&x) != uint32(g) {
+ preempt()
}
+ atomic.StoreUint32(&x, uint32(1-g))
}
c <- true
+ }(g)
+ }
+ <-c
+ <-c
+}
+
+func TestPreemptionGC(t *testing.T) {
+ // Test that pending GC preempts running goroutines.
+ P := 5
+ N := 10
+ if testing.Short() {
+ P = 3
+ N = 2
+ }
+ defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(P + 1))
+ var stop uint32
+ for i := 0; i < P; i++ {
+ go func() {
+ for atomic.LoadUint32(&stop) == 0 {
+ preempt()
+ }
}()
}
- for p := 0; p < procs; p++ {
- <-c
+ for i := 0; i < N; i++ {
+ runtime.Gosched()
+ runtime.GC()
}
+ atomic.StoreUint32(&stop, 1)
}
-func BenchmarkStackGrowth(b *testing.B) {
- benchmarkStackGrowth(b, 10)
+func stackGrowthRecursive(i int) {
+ var pad [128]uint64
+ if i != 0 && pad[0] == 0 {
+ stackGrowthRecursive(i - 1)
+ }
}
-func BenchmarkStackGrowthDeep(b *testing.B) {
- benchmarkStackGrowth(b, 1024)
+func TestPreemptSplitBig(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping in -short mode")
+ }
+ defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2))
+ stop := make(chan int)
+ go big(stop)
+ for i := 0; i < 3; i++ {
+ time.Sleep(10 * time.Microsecond) // let big start running
+ runtime.GC()
+ }
+ close(stop)
+}
+
+func big(stop chan int) int {
+ n := 0
+ for {
+ // delay so that gc is sure to have asked for a preemption
+ for i := 0; i < 1e9; i++ {
+ n++
+ }
+
+ // call bigframe, which used to miss the preemption in its prologue.
+ bigframe(stop)
+
+ // check if we've been asked to stop.
+ select {
+ case <-stop:
+ return n
+ }
+ }
+}
+
+func bigframe(stop chan int) int {
+ // not splitting the stack will overflow.
+ // small will notice that it needs a stack split and will
+ // catch the overflow.
+ var x [8192]byte
+ return small(stop, &x)
}
-func BenchmarkSyscall(b *testing.B) {
- benchmarkSyscall(b, 0, 1)
+func small(stop chan int, x *[8192]byte) int {
+ for i := range x {
+ x[i] = byte(i)
+ }
+ sum := 0
+ for i := range x {
+ sum += int(x[i])
+ }
+
+ // keep small from being a leaf function, which might
+ // make it not do any stack check at all.
+ nonleaf(stop)
+
+ return sum
}
-func BenchmarkSyscallWork(b *testing.B) {
- benchmarkSyscall(b, 100, 1)
+func nonleaf(stop chan int) bool {
+ // do something that won't be inlined:
+ select {
+ case <-stop:
+ return true
+ default:
+ return false
+ }
}
-func BenchmarkSyscallExcess(b *testing.B) {
- benchmarkSyscall(b, 0, 4)
+func TestSchedLocalQueue(t *testing.T) {
+ runtime.TestSchedLocalQueue1()
}
-func BenchmarkSyscallExcessWork(b *testing.B) {
- benchmarkSyscall(b, 100, 4)
+func TestSchedLocalQueueSteal(t *testing.T) {
+ runtime.TestSchedLocalQueueSteal1()
}
-func benchmarkSyscall(b *testing.B, work, excess int) {
+func benchmarkStackGrowth(b *testing.B, rec int) {
const CallsPerSched = 1000
- procs := runtime.GOMAXPROCS(-1) * excess
+ procs := runtime.GOMAXPROCS(-1)
N := int32(b.N / CallsPerSched)
c := make(chan bool, procs)
for p := 0; p < procs; p++ {
go func() {
- foo := 42
for atomic.AddInt32(&N, -1) >= 0 {
runtime.Gosched()
for g := 0; g < CallsPerSched; g++ {
- runtime.Entersyscall()
- for i := 0; i < work; i++ {
- foo *= 2
- foo /= 2
- }
- runtime.Exitsyscall()
+ stackGrowthRecursive(rec)
}
}
- c <- foo == 42
+ c <- true
}()
}
for p := 0; p < procs; p++ {
@@ -194,6 +347,14 @@ func benchmarkSyscall(b *testing.B, work, excess int) {
}
}
+func BenchmarkStackGrowth(b *testing.B) {
+ benchmarkStackGrowth(b, 10)
+}
+
+func BenchmarkStackGrowthDeep(b *testing.B) {
+ benchmarkStackGrowth(b, 1024)
+}
+
func BenchmarkCreateGoroutines(b *testing.B) {
benchmarkCreateGoroutines(b, 1)
}
diff --git a/src/pkg/runtime/race.c b/src/pkg/runtime/race.c
index ce250b5b6..6ee55beff 100644
--- a/src/pkg/runtime/race.c
+++ b/src/pkg/runtime/race.c
@@ -9,6 +9,7 @@
#include "arch_GOARCH.h"
#include "malloc.h"
#include "race.h"
+#include "../../cmd/ld/textflag.h"
void runtime∕race·Initialize(uintptr *racectx);
void runtime∕race·MapShadow(void *addr, uintptr size);
@@ -16,8 +17,8 @@ void runtime∕race·Finalize(void);
void runtime∕race·FinalizerGoroutine(uintptr racectx);
void runtime∕race·Read(uintptr racectx, void *addr, void *pc);
void runtime∕race·Write(uintptr racectx, void *addr, void *pc);
-void runtime∕race·ReadRange(uintptr racectx, void *addr, uintptr sz, uintptr step, void *pc);
-void runtime∕race·WriteRange(uintptr racectx, void *addr, uintptr sz, uintptr step, void *pc);
+void runtime∕race·ReadRange(uintptr racectx, void *addr, uintptr sz, void *pc);
+void runtime∕race·WriteRange(uintptr racectx, void *addr, uintptr sz, void *pc);
void runtime∕race·FuncEnter(uintptr racectx, void *pc);
void runtime∕race·FuncExit(uintptr racectx);
void runtime∕race·Malloc(uintptr racectx, void *p, uintptr sz, void *pc);
@@ -33,17 +34,23 @@ extern byte enoptrbss[];
static bool onstack(uintptr argp);
+// We set m->racecall around all calls into race library to trigger fast path in cgocall.
+// Also we increment m->locks to disable preemption and potential rescheduling
+// to ensure that we reset m->racecall on the correct m.
+
uintptr
runtime·raceinit(void)
{
uintptr racectx, start, size;
m->racecall = true;
+ m->locks++;
runtime∕race·Initialize(&racectx);
// Round data segment to page boundaries, because it's used in mmap().
start = (uintptr)noptrdata & ~(PageSize-1);
size = ROUND((uintptr)enoptrbss - start, PageSize);
runtime∕race·MapShadow((void*)start, size);
+ m->locks--;
m->racecall = false;
return racectx;
}
@@ -52,7 +59,9 @@ void
runtime·racefini(void)
{
m->racecall = true;
+ m->locks++;
runtime∕race·Finalize();
+ m->locks--;
m->racecall = false;
}
@@ -60,38 +69,70 @@ void
runtime·racemapshadow(void *addr, uintptr size)
{
m->racecall = true;
+ m->locks++;
runtime∕race·MapShadow(addr, size);
+ m->locks--;
m->racecall = false;
}
// Called from instrumented code.
// If we split stack, getcallerpc() can return runtime·lessstack().
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·racewrite(uintptr addr)
{
if(!onstack(addr)) {
m->racecall = true;
+ m->locks++;
runtime∕race·Write(g->racectx, (void*)addr, runtime·getcallerpc(&addr));
+ m->locks--;
+ m->racecall = false;
+ }
+}
+
+#pragma textflag NOSPLIT
+void
+runtime·racewriterange(uintptr addr, uintptr sz)
+{
+ if(!onstack(addr)) {
+ m->racecall = true;
+ m->locks++;
+ runtime∕race·WriteRange(g->racectx, (void*)addr, sz, runtime·getcallerpc(&addr));
+ m->locks--;
m->racecall = false;
}
}
// Called from instrumented code.
// If we split stack, getcallerpc() can return runtime·lessstack().
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·raceread(uintptr addr)
{
if(!onstack(addr)) {
m->racecall = true;
+ m->locks++;
runtime∕race·Read(g->racectx, (void*)addr, runtime·getcallerpc(&addr));
+ m->locks--;
+ m->racecall = false;
+ }
+}
+
+#pragma textflag NOSPLIT
+void
+runtime·racereadrange(uintptr addr, uintptr sz)
+{
+ if(!onstack(addr)) {
+ m->racecall = true;
+ m->locks++;
+ runtime∕race·ReadRange(g->racectx, (void*)addr, sz, runtime·getcallerpc(&addr));
+ m->locks--;
m->racecall = false;
}
}
// Called from runtime·racefuncenter (assembly).
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·racefuncenter1(uintptr pc)
{
@@ -101,28 +142,34 @@ runtime·racefuncenter1(uintptr pc)
runtime·callers(2, &pc, 1);
m->racecall = true;
+ m->locks++;
runtime∕race·FuncEnter(g->racectx, (void*)pc);
+ m->locks--;
m->racecall = false;
}
// Called from instrumented code.
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·racefuncexit(void)
{
m->racecall = true;
+ m->locks++;
runtime∕race·FuncExit(g->racectx);
+ m->locks--;
m->racecall = false;
}
void
-runtime·racemalloc(void *p, uintptr sz, void *pc)
+runtime·racemalloc(void *p, uintptr sz)
{
// use m->curg because runtime·stackalloc() is called from g0
if(m->curg == nil)
return;
m->racecall = true;
- runtime∕race·Malloc(m->curg->racectx, p, sz, pc);
+ m->locks++;
+ runtime∕race·Malloc(m->curg->racectx, p, sz, /* unused pc */ 0);
+ m->locks--;
m->racecall = false;
}
@@ -130,7 +177,9 @@ void
runtime·racefree(void *p)
{
m->racecall = true;
+ m->locks++;
runtime∕race·Free(p);
+ m->locks--;
m->racecall = false;
}
@@ -140,7 +189,9 @@ runtime·racegostart(void *pc)
uintptr racectx;
m->racecall = true;
+ m->locks++;
runtime∕race·GoStart(g->racectx, &racectx, pc);
+ m->locks--;
m->racecall = false;
return racectx;
}
@@ -149,7 +200,9 @@ void
runtime·racegoend(void)
{
m->racecall = true;
+ m->locks++;
runtime∕race·GoEnd(g->racectx);
+ m->locks--;
m->racecall = false;
}
@@ -160,6 +213,7 @@ memoryaccess(void *addr, uintptr callpc, uintptr pc, bool write)
if(!onstack((uintptr)addr)) {
m->racecall = true;
+ m->locks++;
racectx = g->racectx;
if(callpc) {
if(callpc == (uintptr)runtime·lessstack)
@@ -172,6 +226,7 @@ memoryaccess(void *addr, uintptr callpc, uintptr pc, bool write)
runtime∕race·Read(racectx, addr, (void*)pc);
if(callpc)
runtime∕race·FuncExit(racectx);
+ m->locks--;
m->racecall = false;
}
}
@@ -189,12 +244,13 @@ runtime·racereadpc(void *addr, void *callpc, void *pc)
}
static void
-rangeaccess(void *addr, uintptr size, uintptr step, uintptr callpc, uintptr pc, bool write)
+rangeaccess(void *addr, uintptr size, uintptr callpc, uintptr pc, bool write)
{
uintptr racectx;
if(!onstack((uintptr)addr)) {
m->racecall = true;
+ m->locks++;
racectx = g->racectx;
if(callpc) {
if(callpc == (uintptr)runtime·lessstack)
@@ -202,25 +258,26 @@ rangeaccess(void *addr, uintptr size, uintptr step, uintptr callpc, uintptr pc,
runtime∕race·FuncEnter(racectx, (void*)callpc);
}
if(write)
- runtime∕race·WriteRange(racectx, addr, size, step, (void*)pc);
+ runtime∕race·WriteRange(racectx, addr, size, (void*)pc);
else
- runtime∕race·ReadRange(racectx, addr, size, step, (void*)pc);
+ runtime∕race·ReadRange(racectx, addr, size, (void*)pc);
if(callpc)
runtime∕race·FuncExit(racectx);
+ m->locks--;
m->racecall = false;
}
}
void
-runtime·racewriterangepc(void *addr, uintptr sz, uintptr step, void *callpc, void *pc)
+runtime·racewriterangepc(void *addr, uintptr sz, void *callpc, void *pc)
{
- rangeaccess(addr, sz, step, (uintptr)callpc, (uintptr)pc, true);
+ rangeaccess(addr, sz, (uintptr)callpc, (uintptr)pc, true);
}
void
-runtime·racereadrangepc(void *addr, uintptr sz, uintptr step, void *callpc, void *pc)
+runtime·racereadrangepc(void *addr, uintptr sz, void *callpc, void *pc)
{
- rangeaccess(addr, sz, step, (uintptr)callpc, (uintptr)pc, false);
+ rangeaccess(addr, sz, (uintptr)callpc, (uintptr)pc, false);
}
void
@@ -235,7 +292,9 @@ runtime·raceacquireg(G *gp, void *addr)
if(g->raceignore)
return;
m->racecall = true;
+ m->locks++;
runtime∕race·Acquire(gp->racectx, addr);
+ m->locks--;
m->racecall = false;
}
@@ -251,7 +310,9 @@ runtime·racereleaseg(G *gp, void *addr)
if(g->raceignore)
return;
m->racecall = true;
+ m->locks++;
runtime∕race·Release(gp->racectx, addr);
+ m->locks--;
m->racecall = false;
}
@@ -267,7 +328,9 @@ runtime·racereleasemergeg(G *gp, void *addr)
if(g->raceignore)
return;
m->racecall = true;
+ m->locks++;
runtime∕race·ReleaseMerge(gp->racectx, addr);
+ m->locks--;
m->racecall = false;
}
@@ -275,7 +338,9 @@ void
runtime·racefingo(void)
{
m->racecall = true;
+ m->locks++;
runtime∕race·FinalizerGoroutine(g->racectx);
+ m->locks--;
m->racecall = false;
}
@@ -301,19 +366,21 @@ runtime·RaceReleaseMerge(void *addr)
}
// func RaceSemacquire(s *uint32)
-void runtime·RaceSemacquire(uint32 *s)
+void
+runtime·RaceSemacquire(uint32 *s)
{
- runtime·semacquire(s);
+ runtime·semacquire(s, false);
}
// func RaceSemrelease(s *uint32)
-void runtime·RaceSemrelease(uint32 *s)
+void
+runtime·RaceSemrelease(uint32 *s)
{
runtime·semrelease(s);
}
// func RaceRead(addr unsafe.Pointer)
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·RaceRead(void *addr)
{
@@ -321,21 +388,39 @@ runtime·RaceRead(void *addr)
}
// func RaceWrite(addr unsafe.Pointer)
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·RaceWrite(void *addr)
{
memoryaccess(addr, 0, (uintptr)runtime·getcallerpc(&addr), true);
}
+// func RaceReadRange(addr unsafe.Pointer, len int)
+#pragma textflag NOSPLIT
+void
+runtime·RaceReadRange(void *addr, intgo len)
+{
+ rangeaccess(addr, len, 0, (uintptr)runtime·getcallerpc(&addr), false);
+}
+
+// func RaceWriteRange(addr unsafe.Pointer, len int)
+#pragma textflag NOSPLIT
+void
+runtime·RaceWriteRange(void *addr, intgo len)
+{
+ rangeaccess(addr, len, 0, (uintptr)runtime·getcallerpc(&addr), true);
+}
+
// func RaceDisable()
-void runtime·RaceDisable(void)
+void
+runtime·RaceDisable(void)
{
g->raceignore++;
}
// func RaceEnable()
-void runtime·RaceEnable(void)
+void
+runtime·RaceEnable(void)
{
g->raceignore--;
}
@@ -347,7 +432,7 @@ onstack(uintptr argp)
// the layout is in ../../cmd/ld/data.c
if((byte*)argp >= noptrdata && (byte*)argp < enoptrbss)
return false;
- if((byte*)argp >= runtime·mheap->arena_start && (byte*)argp < runtime·mheap->arena_used)
+ if((byte*)argp >= runtime·mheap.arena_start && (byte*)argp < runtime·mheap.arena_used)
return false;
return true;
}
diff --git a/src/pkg/runtime/race.go b/src/pkg/runtime/race.go
index 1d64ba389..2a9124d64 100644
--- a/src/pkg/runtime/race.go
+++ b/src/pkg/runtime/race.go
@@ -24,6 +24,8 @@ func RaceReleaseMerge(addr unsafe.Pointer)
func RaceRead(addr unsafe.Pointer)
func RaceWrite(addr unsafe.Pointer)
+func RaceReadRange(addr unsafe.Pointer, len int)
+func RaceWriteRange(addr unsafe.Pointer, len int)
func RaceSemacquire(s *uint32)
func RaceSemrelease(s *uint32)
diff --git a/src/pkg/runtime/race.h b/src/pkg/runtime/race.h
index 432a8a97d..f7aa99dc2 100644
--- a/src/pkg/runtime/race.h
+++ b/src/pkg/runtime/race.h
@@ -16,14 +16,14 @@ uintptr runtime·raceinit(void);
void runtime·racefini(void);
void runtime·racemapshadow(void *addr, uintptr size);
-void runtime·racemalloc(void *p, uintptr sz, void *pc);
+void runtime·racemalloc(void *p, uintptr sz);
void runtime·racefree(void *p);
uintptr runtime·racegostart(void *pc);
void runtime·racegoend(void);
void runtime·racewritepc(void *addr, void *callpc, void *pc);
void runtime·racereadpc(void *addr, void *callpc, void *pc);
-void runtime·racewriterangepc(void *addr, uintptr sz, uintptr step, void *callpc, void *pc);
-void runtime·racereadrangepc(void *addr, uintptr sz, uintptr step, void *callpc, void *pc);
+void runtime·racewriterangepc(void *addr, uintptr sz, void *callpc, void *pc);
+void runtime·racereadrangepc(void *addr, uintptr sz, void *callpc, void *pc);
void runtime·racefingo(void);
void runtime·raceacquire(void *addr);
void runtime·raceacquireg(G *gp, void *addr);
diff --git a/src/pkg/runtime/race/README b/src/pkg/runtime/race/README
index 8bedb09cd..0b73bd857 100644
--- a/src/pkg/runtime/race/README
+++ b/src/pkg/runtime/race/README
@@ -9,3 +9,4 @@ $ ./buildgo.sh
Tested with gcc 4.6.1 and 4.7.0. On Windows it's built with 64-bit MinGW.
+Current runtime is built on rev 191161.
diff --git a/src/pkg/runtime/race/output_test.go b/src/pkg/runtime/race/output_test.go
new file mode 100644
index 000000000..d2303f7af
--- /dev/null
+++ b/src/pkg/runtime/race/output_test.go
@@ -0,0 +1,156 @@
+// Copyright 2013 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.
+
+// +build race
+
+package race_test
+
+import (
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "regexp"
+ "strings"
+ "testing"
+)
+
+func TestOutput(t *testing.T) {
+ for _, test := range tests {
+ dir, err := ioutil.TempDir("", "go-build")
+ if err != nil {
+ t.Fatalf("failed to create temp directory: %v", err)
+ }
+ defer os.RemoveAll(dir)
+ src := filepath.Join(dir, "main.go")
+ f, err := os.Create(src)
+ if err != nil {
+ t.Fatalf("failed to create file: %v", err)
+ }
+ _, err = f.WriteString(test.source)
+ if err != nil {
+ f.Close()
+ t.Fatalf("failed to write: %v", err)
+ }
+ if err := f.Close(); err != nil {
+ t.Fatalf("failed to close file: %v", err)
+ }
+ // Pass -l to the compiler to test stack traces.
+ cmd := exec.Command("go", "run", "-race", "-gcflags=-l", src)
+ // GODEBUG spoils program output, GOMAXPROCS makes it flaky.
+ for _, env := range os.Environ() {
+ if strings.HasPrefix(env, "GODEBUG=") ||
+ strings.HasPrefix(env, "GOMAXPROCS=") ||
+ strings.HasPrefix(env, "GORACE=") {
+ continue
+ }
+ cmd.Env = append(cmd.Env, env)
+ }
+ cmd.Env = append(cmd.Env, "GORACE="+test.gorace)
+ got, _ := cmd.CombinedOutput()
+ if !regexp.MustCompile(test.re).MatchString(string(got)) {
+ t.Fatalf("failed test case %v, expect:\n%v\ngot:\n%s",
+ test.name, test.re, got)
+ }
+ }
+}
+
+var tests = []struct {
+ name string
+ gorace string
+ source string
+ re string
+}{
+ {"simple", "atexit_sleep_ms=0", `
+package main
+import "time"
+func main() {
+ done := make(chan bool)
+ x := 0
+ startRacer(&x, done)
+ store(&x, 43)
+ <-done
+}
+func store(x *int, v int) {
+ *x = v
+}
+func startRacer(x *int, done chan bool) {
+ go racer(x, done)
+}
+func racer(x *int, done chan bool) {
+ time.Sleep(10*time.Millisecond)
+ store(x, 42)
+ done <- true
+}
+`, `==================
+WARNING: DATA RACE
+Write by goroutine [0-9]:
+ main\.store\(\)
+ .+/main\.go:12 \+0x[0-9,a-f]+
+ main\.racer\(\)
+ .+/main\.go:19 \+0x[0-9,a-f]+
+
+Previous write by main goroutine:
+ main\.store\(\)
+ .+/main\.go:12 \+0x[0-9,a-f]+
+ main\.main\(\)
+ .+/main\.go:8 \+0x[0-9,a-f]+
+
+Goroutine [0-9] \(running\) created at:
+ main\.startRacer\(\)
+ .+/main\.go:15 \+0x[0-9,a-f]+
+ main\.main\(\)
+ .+/main\.go:7 \+0x[0-9,a-f]+
+==================
+Found 1 data race\(s\)
+exit status 66
+`},
+
+ {"exitcode", "atexit_sleep_ms=0 exitcode=13", `
+package main
+func main() {
+ done := make(chan bool)
+ x := 0
+ go func() {
+ x = 42
+ done <- true
+ }()
+ x = 43
+ <-done
+}
+`, `exit status 13`},
+
+ {"strip_path_prefix", "atexit_sleep_ms=0 strip_path_prefix=/main.", `
+package main
+func main() {
+ done := make(chan bool)
+ x := 0
+ go func() {
+ x = 42
+ done <- true
+ }()
+ x = 43
+ <-done
+}
+`, `
+ go:7 \+0x[0-9,a-f]+
+`},
+
+ {"halt_on_error", "atexit_sleep_ms=0 halt_on_error=1", `
+package main
+func main() {
+ done := make(chan bool)
+ x := 0
+ go func() {
+ x = 42
+ done <- true
+ }()
+ x = 43
+ <-done
+}
+`, `
+==================
+exit status 66
+`},
+}
diff --git a/src/pkg/runtime/race/race.go b/src/pkg/runtime/race/race.go
index ee13057e3..5b44bde83 100644
--- a/src/pkg/runtime/race/race.go
+++ b/src/pkg/runtime/race/race.go
@@ -56,14 +56,14 @@ func Write(racectx uintptr, addr, pc uintptr) {
C.__tsan_write(unsafe.Pointer(racectx), unsafe.Pointer(addr), unsafe.Pointer(pc))
}
-func ReadRange(racectx uintptr, addr, sz, step, pc uintptr) {
+func ReadRange(racectx uintptr, addr, sz, pc uintptr) {
C.__tsan_read_range(unsafe.Pointer(racectx), unsafe.Pointer(addr),
- C.long(sz), C.long(step), unsafe.Pointer(pc))
+ C.long(sz), 0 /*step is unused*/, unsafe.Pointer(pc))
}
-func WriteRange(racectx uintptr, addr, sz, step, pc uintptr) {
+func WriteRange(racectx uintptr, addr, sz, pc uintptr) {
C.__tsan_write_range(unsafe.Pointer(racectx), unsafe.Pointer(addr),
- C.long(sz), C.long(step), unsafe.Pointer(pc))
+ C.long(sz), 0 /*step is unused*/, unsafe.Pointer(pc))
}
func FuncEnter(racectx uintptr, pc uintptr) {
diff --git a/src/pkg/runtime/race/race_darwin_amd64.syso b/src/pkg/runtime/race/race_darwin_amd64.syso
index 24a00497c..96a43c9a9 100644
--- a/src/pkg/runtime/race/race_darwin_amd64.syso
+++ b/src/pkg/runtime/race/race_darwin_amd64.syso
Binary files differ
diff --git a/src/pkg/runtime/race/race_linux_amd64.syso b/src/pkg/runtime/race/race_linux_amd64.syso
index b15091ba8..50bde9648 100644
--- a/src/pkg/runtime/race/race_linux_amd64.syso
+++ b/src/pkg/runtime/race/race_linux_amd64.syso
Binary files differ
diff --git a/src/pkg/runtime/race/race_test.go b/src/pkg/runtime/race/race_test.go
index 47d34d7ad..4776ae22d 100644
--- a/src/pkg/runtime/race/race_test.go
+++ b/src/pkg/runtime/race/race_test.go
@@ -147,7 +147,7 @@ func runTests() ([]byte, error) {
// It is required because the tests contain a lot of data races on the same addresses
// (the tests are simple and the memory is constantly reused).
for _, env := range os.Environ() {
- if strings.HasPrefix(env, "GOMAXPROCS=") || strings.HasPrefix(env, "GOGCTRACE=") {
+ if strings.HasPrefix(env, "GOMAXPROCS=") || strings.HasPrefix(env, "GODEBUG=") {
continue
}
cmd.Env = append(cmd.Env, env)
diff --git a/src/pkg/runtime/race/race_windows_amd64.syso b/src/pkg/runtime/race/race_windows_amd64.syso
index 0a3a58354..46eb1274f 100644
--- a/src/pkg/runtime/race/race_windows_amd64.syso
+++ b/src/pkg/runtime/race/race_windows_amd64.syso
Binary files differ
diff --git a/src/pkg/runtime/race/testdata/chan_test.go b/src/pkg/runtime/race/testdata/chan_test.go
index 2332f097e..614ba4a4e 100644
--- a/src/pkg/runtime/race/testdata/chan_test.go
+++ b/src/pkg/runtime/race/testdata/chan_test.go
@@ -311,12 +311,35 @@ func TestRaceChanSendClose(t *testing.T) {
go func() {
defer func() {
recover()
+ compl <- true
}()
c <- 1
+ }()
+ go func() {
+ time.Sleep(10 * time.Millisecond)
+ close(c)
compl <- true
}()
+ <-compl
+ <-compl
+}
+
+func TestRaceChanSendSelectClose(t *testing.T) {
+ compl := make(chan bool, 2)
+ c := make(chan int, 1)
+ c1 := make(chan int)
+ go func() {
+ defer func() {
+ recover()
+ compl <- true
+ }()
+ time.Sleep(10 * time.Millisecond)
+ select {
+ case c <- 1:
+ case <-c1:
+ }
+ }()
go func() {
- time.Sleep(1e7)
close(c)
compl <- true
}()
diff --git a/src/pkg/runtime/race/testdata/comp_test.go b/src/pkg/runtime/race/testdata/comp_test.go
index 754e4db6d..27b2d0081 100644
--- a/src/pkg/runtime/race/testdata/comp_test.go
+++ b/src/pkg/runtime/race/testdata/comp_test.go
@@ -83,6 +83,60 @@ func TestRaceCompArray(t *testing.T) {
<-c
}
+type P2 P
+type S2 S
+
+func TestRaceConv1(t *testing.T) {
+ c := make(chan bool, 1)
+ var p P2
+ go func() {
+ p.x = 1
+ c <- true
+ }()
+ _ = P(p).x
+ <-c
+}
+
+func TestRaceConv2(t *testing.T) {
+ c := make(chan bool, 1)
+ var p P2
+ go func() {
+ p.x = 1
+ c <- true
+ }()
+ ptr := &p
+ _ = P(*ptr).x
+ <-c
+}
+
+func TestRaceConv3(t *testing.T) {
+ c := make(chan bool, 1)
+ var s S2
+ go func() {
+ s.s1.x = 1
+ c <- true
+ }()
+ _ = P2(S(s).s1).x
+ <-c
+}
+
+type X struct {
+ V [4]P
+}
+
+type X2 X
+
+func TestRaceConv4(t *testing.T) {
+ c := make(chan bool, 1)
+ var x X2
+ go func() {
+ x.V[1].x = 1
+ c <- true
+ }()
+ _ = P2(X(x).V[1]).x
+ <-c
+}
+
type Ptr struct {
s1, s2 *P
}
diff --git a/src/pkg/runtime/race/testdata/mop_test.go b/src/pkg/runtime/race/testdata/mop_test.go
index fa7abe0ef..b0b66562c 100644
--- a/src/pkg/runtime/race/testdata/mop_test.go
+++ b/src/pkg/runtime/race/testdata/mop_test.go
@@ -5,8 +5,12 @@
package race_test
import (
+ "bytes"
+ "crypto/sha1"
"errors"
"fmt"
+ "io"
+ "os"
"runtime"
"sync"
"testing"
@@ -227,6 +231,22 @@ func TestRaceCaseFallthrough(t *testing.T) {
<-ch
}
+func TestRaceCaseIssue6418(t *testing.T) {
+ m := map[string]map[string]string{
+ "a": map[string]string{
+ "b": "c",
+ },
+ }
+ ch := make(chan int)
+ go func() {
+ m["a"]["x"] = "y"
+ ch <- 1
+ }()
+ switch m["a"]["b"] {
+ }
+ <-ch
+}
+
func TestRaceCaseType(t *testing.T) {
var x, y int
var i interface{} = x
@@ -258,6 +278,25 @@ func TestRaceCaseTypeBody(t *testing.T) {
<-c
}
+func TestRaceCaseTypeIssue5890(t *testing.T) {
+ // spurious extra instrumentation of the initial interface
+ // value.
+ var x, y int
+ m := make(map[int]map[int]interface{})
+ m[0] = make(map[int]interface{})
+ c := make(chan int, 1)
+ go func() {
+ switch i := m[0][1].(type) {
+ case nil:
+ case *int:
+ *i = x
+ }
+ c <- 1
+ }()
+ m[0][1] = y
+ <-c
+}
+
func TestNoRaceRange(t *testing.T) {
ch := make(chan int, 3)
a := [...]int{1, 2, 3}
@@ -267,6 +306,19 @@ func TestNoRaceRange(t *testing.T) {
close(ch)
}
+func TestNoRaceRangeIssue5446(t *testing.T) {
+ ch := make(chan int, 3)
+ a := []int{1, 2, 3}
+ b := []int{4}
+ // used to insert a spurious instrumentation of a[i]
+ // and crash.
+ i := 1
+ for i, a[i] = range b {
+ ch <- i
+ }
+ close(ch)
+}
+
func TestRaceRange(t *testing.T) {
const N = 2
var a [N]int
@@ -289,6 +341,94 @@ func TestRaceRange(t *testing.T) {
}
}
+func TestRaceForInit(t *testing.T) {
+ c := make(chan int)
+ x := 0
+ go func() {
+ c <- x
+ }()
+ for x = 42; false; {
+ }
+ <-c
+}
+
+func TestNoRaceForInit(t *testing.T) {
+ done := make(chan bool)
+ c := make(chan bool)
+ x := 0
+ go func() {
+ for {
+ _, ok := <-c
+ if !ok {
+ done <- true
+ return
+ }
+ x++
+ }
+ }()
+ i := 0
+ for x = 42; i < 10; i++ {
+ c <- true
+ }
+ close(c)
+ <-done
+}
+
+func TestRaceForTest(t *testing.T) {
+ done := make(chan bool)
+ c := make(chan bool)
+ stop := false
+ go func() {
+ for {
+ _, ok := <-c
+ if !ok {
+ done <- true
+ return
+ }
+ stop = true
+ }
+ }()
+ for !stop {
+ c <- true
+ }
+ close(c)
+ <-done
+}
+
+func TestRaceForIncr(t *testing.T) {
+ done := make(chan bool)
+ c := make(chan bool)
+ x := 0
+ go func() {
+ for {
+ _, ok := <-c
+ if !ok {
+ done <- true
+ return
+ }
+ x++
+ }
+ }()
+ for i := 0; i < 10; x++ {
+ i++
+ c <- true
+ }
+ close(c)
+ <-done
+}
+
+func TestNoRaceForIncr(t *testing.T) {
+ done := make(chan bool)
+ x := 0
+ go func() {
+ x++
+ done <- true
+ }()
+ for i := 0; i < 0; x++ {
+ }
+ <-done
+}
+
func TestRacePlus(t *testing.T) {
var x, y, z int
ch := make(chan int, 2)
@@ -495,8 +635,7 @@ func TestRaceSprint(t *testing.T) {
<-ch
}
-// Not implemented.
-func TestRaceFailingArrayCopy(t *testing.T) {
+func TestRaceArrayCopy(t *testing.T) {
ch := make(chan bool, 1)
var a [5]int
go func() {
@@ -507,6 +646,24 @@ func TestRaceFailingArrayCopy(t *testing.T) {
<-ch
}
+// Blows up a naive compiler.
+func TestRaceNestedArrayCopy(t *testing.T) {
+ ch := make(chan bool, 1)
+ type (
+ Point32 [2][2][2][2][2]Point
+ Point1024 [2][2][2][2][2]Point32
+ Point32k [2][2][2][2][2]Point1024
+ Point1M [2][2][2][2][2]Point32k
+ )
+ var a, b Point1M
+ go func() {
+ a[0][1][0][1][0][1][0][1][0][1][0][1][0][1][0][1][0][1][0][1].y = 1
+ ch <- true
+ }()
+ a = b
+ <-ch
+}
+
func TestRaceStructRW(t *testing.T) {
p := Point{0, 0}
ch := make(chan bool, 1)
@@ -1461,8 +1618,7 @@ func TestRaceSliceString(t *testing.T) {
<-c
}
-// http://golang.org/issue/4453
-func TestRaceFailingSliceStruct(t *testing.T) {
+func TestRaceSliceStruct(t *testing.T) {
type X struct {
x, y int
}
@@ -1477,7 +1633,7 @@ func TestRaceFailingSliceStruct(t *testing.T) {
<-c
}
-func TestRaceFailingAppendSliceStruct(t *testing.T) {
+func TestRaceAppendSliceStruct(t *testing.T) {
type X struct {
x, y int
}
@@ -1614,3 +1770,166 @@ func TestRaceNestedStruct(t *testing.T) {
y.x.y = 42
<-c
}
+
+func TestRaceIssue5567(t *testing.T) {
+ defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
+ in := make(chan []byte)
+ res := make(chan error)
+ go func() {
+ var err error
+ defer func() {
+ close(in)
+ res <- err
+ }()
+ path := "mop_test.go"
+ f, err := os.Open(path)
+ if err != nil {
+ return
+ }
+ defer f.Close()
+ var n, total int
+ b := make([]byte, 17) // the race is on b buffer
+ for err == nil {
+ n, err = f.Read(b)
+ total += n
+ if n > 0 {
+ in <- b[:n]
+ }
+ }
+ if err == io.EOF {
+ err = nil
+ }
+ }()
+ h := sha1.New()
+ for b := range in {
+ h.Write(b)
+ }
+ _ = h.Sum(nil)
+ err := <-res
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestRaceIssue5654(t *testing.T) {
+ text := `Friends, Romans, countrymen, lend me your ears;
+I come to bury Caesar, not to praise him.
+The evil that men do lives after them;
+The good is oft interred with their bones;
+So let it be with Caesar. The noble Brutus
+Hath told you Caesar was ambitious:
+If it were so, it was a grievous fault,
+And grievously hath Caesar answer'd it.
+Here, under leave of Brutus and the rest -
+For Brutus is an honourable man;
+So are they all, all honourable men -
+Come I to speak in Caesar's funeral.
+He was my friend, faithful and just to me:
+But Brutus says he was ambitious;
+And Brutus is an honourable man.`
+
+ data := bytes.NewBufferString(text)
+ in := make(chan []byte)
+
+ go func() {
+ buf := make([]byte, 16)
+ var n int
+ var err error
+ for ; err == nil; n, err = data.Read(buf) {
+ in <- buf[:n]
+ }
+ close(in)
+ }()
+ res := ""
+ for s := range in {
+ res += string(s)
+ }
+ _ = res
+}
+
+type Base int
+
+func (b *Base) Foo() int {
+ return 42
+}
+
+func (b Base) Bar() int {
+ return int(b)
+}
+
+func TestNoRaceMethodThunk(t *testing.T) {
+ type Derived struct {
+ pad int
+ Base
+ }
+ var d Derived
+ done := make(chan bool)
+ go func() {
+ _ = d.Foo()
+ done <- true
+ }()
+ d = Derived{}
+ <-done
+}
+
+func TestRaceMethodThunk(t *testing.T) {
+ type Derived struct {
+ pad int
+ *Base
+ }
+ var d Derived
+ done := make(chan bool)
+ go func() {
+ _ = d.Foo()
+ done <- true
+ }()
+ d = Derived{}
+ <-done
+}
+
+func TestRaceMethodThunk2(t *testing.T) {
+ type Derived struct {
+ pad int
+ Base
+ }
+ var d Derived
+ done := make(chan bool)
+ go func() {
+ _ = d.Bar()
+ done <- true
+ }()
+ d = Derived{}
+ <-done
+}
+
+func TestRaceMethodThunk3(t *testing.T) {
+ type Derived struct {
+ pad int
+ *Base
+ }
+ var d Derived
+ d.Base = new(Base)
+ done := make(chan bool)
+ go func() {
+ _ = d.Bar()
+ done <- true
+ }()
+ d.Base = new(Base)
+ <-done
+}
+
+func TestRaceMethodThunk4(t *testing.T) {
+ type Derived struct {
+ pad int
+ *Base
+ }
+ var d Derived
+ d.Base = new(Base)
+ done := make(chan bool)
+ go func() {
+ _ = d.Bar()
+ done <- true
+ }()
+ *(*int)(d.Base) = 42
+ <-done
+}
diff --git a/src/pkg/runtime/race/testdata/regression_test.go b/src/pkg/runtime/race/testdata/regression_test.go
index f08ee3ed3..d461269d9 100644
--- a/src/pkg/runtime/race/testdata/regression_test.go
+++ b/src/pkg/runtime/race/testdata/regression_test.go
@@ -160,3 +160,35 @@ func noRaceReturn(c chan int) (a, b int) {
}()
return a, 10
}
+
+func issue5431() {
+ var p **inltype
+ if inlinetest(p).x && inlinetest(p).y {
+ } else if inlinetest(p).x || inlinetest(p).y {
+ }
+}
+
+type inltype struct {
+ x, y bool
+}
+
+func inlinetest(p **inltype) *inltype {
+ return *p
+}
+
+type iface interface {
+ Foo() *struct{ b bool }
+}
+
+type Int int
+
+func (i Int) Foo() *struct{ b bool } {
+ return &struct{ b bool }{false}
+}
+
+func TestNoRaceForInfiniteLoop(t *testing.T) {
+ var x Int
+ // interface conversion causes nodes to be put on init list
+ for iface(x).Foo().b {
+ }
+}
diff --git a/src/pkg/runtime/race/testdata/waitgroup_test.go b/src/pkg/runtime/race/testdata/waitgroup_test.go
index 7ea21fa7e..ff152b0ab 100644
--- a/src/pkg/runtime/race/testdata/waitgroup_test.go
+++ b/src/pkg/runtime/race/testdata/waitgroup_test.go
@@ -102,22 +102,22 @@ func TestRaceWaitGroupWrongWait(t *testing.T) {
<-c
}
-// A common WaitGroup misuse that can potentially be caught be the race detector.
-// For this simple case we must emulate Add() as read on &wg and Wait() as write on &wg.
-// However it will have false positives if there are several concurrent Wait() calls.
-func TestRaceFailingWaitGroupWrongAdd(t *testing.T) {
+func TestRaceWaitGroupWrongAdd(t *testing.T) {
c := make(chan bool, 2)
var wg sync.WaitGroup
go func() {
wg.Add(1)
+ time.Sleep(100 * time.Millisecond)
wg.Done()
c <- true
}()
go func() {
wg.Add(1)
+ time.Sleep(100 * time.Millisecond)
wg.Done()
c <- true
}()
+ time.Sleep(50 * time.Millisecond)
wg.Wait()
<-c
<-c
@@ -158,6 +158,32 @@ func TestNoRaceWaitGroupMultipleWait2(t *testing.T) {
<-c
}
+func TestNoRaceWaitGroupMultipleWait3(t *testing.T) {
+ const P = 3
+ var data [P]int
+ done := make(chan bool, P)
+ var wg sync.WaitGroup
+ wg.Add(P)
+ for p := 0; p < P; p++ {
+ go func(p int) {
+ data[p] = 42
+ wg.Done()
+ }(p)
+ }
+ for p := 0; p < P; p++ {
+ go func() {
+ wg.Wait()
+ for p1 := 0; p1 < P; p1++ {
+ _ = data[p1]
+ }
+ done <- true
+ }()
+ }
+ for p := 0; p < P; p++ {
+ <-done
+ }
+}
+
// Correct usage but still a race
func TestRaceWaitGroup2(t *testing.T) {
var x int
@@ -230,3 +256,97 @@ func TestNoRaceWaitGroupTransitive(t *testing.T) {
_ = x
_ = y
}
+
+func TestNoRaceWaitGroupReuse(t *testing.T) {
+ const P = 3
+ var data [P]int
+ var wg sync.WaitGroup
+ for try := 0; try < 3; try++ {
+ wg.Add(P)
+ for p := 0; p < P; p++ {
+ go func(p int) {
+ data[p]++
+ wg.Done()
+ }(p)
+ }
+ wg.Wait()
+ for p := 0; p < P; p++ {
+ data[p]++
+ }
+ }
+}
+
+func TestNoRaceWaitGroupReuse2(t *testing.T) {
+ const P = 3
+ var data [P]int
+ var wg sync.WaitGroup
+ for try := 0; try < 3; try++ {
+ wg.Add(P)
+ for p := 0; p < P; p++ {
+ go func(p int) {
+ data[p]++
+ wg.Done()
+ }(p)
+ }
+ done := make(chan bool)
+ go func() {
+ wg.Wait()
+ for p := 0; p < P; p++ {
+ data[p]++
+ }
+ done <- true
+ }()
+ wg.Wait()
+ <-done
+ for p := 0; p < P; p++ {
+ data[p]++
+ }
+ }
+}
+
+func TestRaceWaitGroupReuse(t *testing.T) {
+ const P = 3
+ const T = 3
+ done := make(chan bool, T)
+ var wg sync.WaitGroup
+ for try := 0; try < T; try++ {
+ var data [P]int
+ wg.Add(P)
+ for p := 0; p < P; p++ {
+ go func(p int) {
+ time.Sleep(50 * time.Millisecond)
+ data[p]++
+ wg.Done()
+ }(p)
+ }
+ go func() {
+ wg.Wait()
+ for p := 0; p < P; p++ {
+ data[p]++
+ }
+ done <- true
+ }()
+ time.Sleep(100 * time.Millisecond)
+ wg.Wait()
+ }
+ for try := 0; try < T; try++ {
+ <-done
+ }
+}
+
+func TestNoRaceWaitGroupConcurrentAdd(t *testing.T) {
+ const P = 4
+ waiting := make(chan bool, P)
+ var wg sync.WaitGroup
+ for p := 0; p < P; p++ {
+ go func() {
+ wg.Add(1)
+ waiting <- true
+ wg.Done()
+ }()
+ }
+ for p := 0; p < P; p++ {
+ <-waiting
+ }
+ wg.Wait()
+}
diff --git a/src/pkg/runtime/race0.c b/src/pkg/runtime/race0.c
index 1c5f05a7e..b74b03583 100644
--- a/src/pkg/runtime/race0.c
+++ b/src/pkg/runtime/race0.c
@@ -43,21 +43,19 @@ runtime·racereadpc(void *addr, void *callpc, void *pc)
}
void
-runtime·racewriterangepc(void *addr, uintptr sz, uintptr step, void *callpc, void *pc)
+runtime·racewriterangepc(void *addr, uintptr sz, void *callpc, void *pc)
{
USED(addr);
USED(sz);
- USED(step);
USED(callpc);
USED(pc);
}
void
-runtime·racereadrangepc(void *addr, uintptr sz, uintptr step, void *callpc, void *pc)
+runtime·racereadrangepc(void *addr, uintptr sz, void *callpc, void *pc)
{
USED(addr);
USED(sz);
- USED(step);
USED(callpc);
USED(pc);
}
@@ -107,11 +105,10 @@ runtime·racefingo(void)
}
void
-runtime·racemalloc(void *p, uintptr sz, void *pc)
+runtime·racemalloc(void *p, uintptr sz)
{
USED(p);
USED(sz);
- USED(pc);
}
void
diff --git a/src/pkg/runtime/race_amd64.s b/src/pkg/runtime/race_amd64.s
index 83e300905..a33b77a50 100644
--- a/src/pkg/runtime/race_amd64.s
+++ b/src/pkg/runtime/race_amd64.s
@@ -4,8 +4,10 @@
// +build race
+#include "../../cmd/ld/textflag.h"
+
// func runtime·racefuncenter(pc uintptr)
-TEXT runtime·racefuncenter(SB), 7, $16
+TEXT runtime·racefuncenter(SB), NOSPLIT, $16-8
MOVQ DX, saved-8(SP) // save function entry context (for closures)
MOVQ pc+0(FP), DX
MOVQ DX, arg-16(SP)
diff --git a/src/pkg/runtime/rt0_darwin_386.s b/src/pkg/runtime/rt0_darwin_386.s
index 4b4c1f294..4f85250c2 100644
--- a/src/pkg/runtime/rt0_darwin_386.s
+++ b/src/pkg/runtime/rt0_darwin_386.s
@@ -2,7 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT _rt0_386_darwin(SB),7,$8
+#include "../../cmd/ld/textflag.h"
+
+TEXT _rt0_386_darwin(SB),NOSPLIT,$8
MOVL 8(SP), AX
LEAL 12(SP), BX
MOVL AX, 0(SP)
@@ -10,5 +12,5 @@ TEXT _rt0_386_darwin(SB),7,$8
CALL main(SB)
INT $3
-TEXT main(SB),7,$0
- JMP _rt0_386(SB)
+TEXT main(SB),NOSPLIT,$0
+ JMP _rt0_go(SB)
diff --git a/src/pkg/runtime/rt0_darwin_amd64.s b/src/pkg/runtime/rt0_darwin_amd64.s
index 45e69a015..8d2962b03 100644
--- a/src/pkg/runtime/rt0_darwin_amd64.s
+++ b/src/pkg/runtime/rt0_darwin_amd64.s
@@ -2,12 +2,14 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT _rt0_amd64_darwin(SB),7,$-8
+#include "../../cmd/ld/textflag.h"
+
+TEXT _rt0_amd64_darwin(SB),NOSPLIT,$-8
LEAQ 8(SP), SI // argv
MOVQ 0(SP), DI // argc
MOVQ $main(SB), AX
JMP AX
-TEXT main(SB),7,$-8
- MOVQ $_rt0_amd64(SB), AX
+TEXT main(SB),NOSPLIT,$-8
+ MOVQ $_rt0_go(SB), AX
JMP AX
diff --git a/src/pkg/runtime/rt0_dragonfly_386.s b/src/pkg/runtime/rt0_dragonfly_386.s
new file mode 100644
index 000000000..b857f6039
--- /dev/null
+++ b/src/pkg/runtime/rt0_dragonfly_386.s
@@ -0,0 +1,16 @@
+// 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.
+
+#include "../../cmd/ld/textflag.h"
+
+TEXT _rt0_386_dragonfly(SB),NOSPLIT,$8
+ MOVL 8(SP), AX
+ LEAL 12(SP), BX
+ MOVL AX, 0(SP)
+ MOVL BX, 4(SP)
+ CALL main(SB)
+ INT $3
+
+TEXT main(SB),NOSPLIT,$0
+ JMP _rt0_go(SB)
diff --git a/src/pkg/runtime/rt0_dragonfly_amd64.s b/src/pkg/runtime/rt0_dragonfly_amd64.s
new file mode 100644
index 000000000..fc7e74598
--- /dev/null
+++ b/src/pkg/runtime/rt0_dragonfly_amd64.s
@@ -0,0 +1,15 @@
+// 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.
+
+#include "../../cmd/ld/textflag.h"
+
+TEXT _rt0_amd64_dragonfly(SB),NOSPLIT,$-8
+ LEAQ 8(DI), SI // argv
+ MOVQ 0(DI), DI // argc
+ MOVQ $main(SB), AX
+ JMP AX
+
+TEXT main(SB),NOSPLIT,$-8
+ MOVQ $_rt0_go(SB), AX
+ JMP AX
diff --git a/src/pkg/runtime/rt0_freebsd_386.s b/src/pkg/runtime/rt0_freebsd_386.s
index c84482cdb..758f7d268 100644
--- a/src/pkg/runtime/rt0_freebsd_386.s
+++ b/src/pkg/runtime/rt0_freebsd_386.s
@@ -2,7 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT _rt0_386_freebsd(SB),7,$8
+#include "../../cmd/ld/textflag.h"
+
+TEXT _rt0_386_freebsd(SB),NOSPLIT,$8
MOVL 8(SP), AX
LEAL 12(SP), BX
MOVL AX, 0(SP)
@@ -10,5 +12,5 @@ TEXT _rt0_386_freebsd(SB),7,$8
CALL main(SB)
INT $3
-TEXT main(SB),7,$0
- JMP _rt0_386(SB)
+TEXT main(SB),NOSPLIT,$0
+ JMP _rt0_go(SB)
diff --git a/src/pkg/runtime/rt0_freebsd_amd64.s b/src/pkg/runtime/rt0_freebsd_amd64.s
index e6c6fb9ca..3cf7163b5 100644
--- a/src/pkg/runtime/rt0_freebsd_amd64.s
+++ b/src/pkg/runtime/rt0_freebsd_amd64.s
@@ -2,12 +2,14 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT _rt0_amd64_freebsd(SB),7,$-8
+#include "../../cmd/ld/textflag.h"
+
+TEXT _rt0_amd64_freebsd(SB),NOSPLIT,$-8
LEAQ 8(DI), SI // argv
MOVQ 0(DI), DI // argc
MOVQ $main(SB), AX
JMP AX
-TEXT main(SB),7,$-8
- MOVQ $_rt0_amd64(SB), AX
+TEXT main(SB),NOSPLIT,$-8
+ MOVQ $_rt0_go(SB), AX
JMP AX
diff --git a/src/pkg/runtime/rt0_freebsd_arm.s b/src/pkg/runtime/rt0_freebsd_arm.s
index 085fccf9d..d11087639 100644
--- a/src/pkg/runtime/rt0_freebsd_arm.s
+++ b/src/pkg/runtime/rt0_freebsd_arm.s
@@ -2,7 +2,12 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
// FreeBSD and Linux use the same linkage to main
-TEXT _rt0_arm_freebsd(SB),7,$-4
- B _rt0_arm(SB)
+TEXT _rt0_arm_freebsd(SB),NOSPLIT,$-4
+ MOVW (R13), R0 // argc
+ MOVW $4(R13), R1 // argv
+ MOVM.DB.W [R0-R1], (R13)
+ B _rt0_go(SB)
diff --git a/src/pkg/runtime/rt0_linux_386.s b/src/pkg/runtime/rt0_linux_386.s
index 73cca5d98..c6f4159ce 100644
--- a/src/pkg/runtime/rt0_linux_386.s
+++ b/src/pkg/runtime/rt0_linux_386.s
@@ -2,7 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT _rt0_386_linux(SB),7,$8
+#include "../../cmd/ld/textflag.h"
+
+TEXT _rt0_386_linux(SB),NOSPLIT,$8
MOVL 8(SP), AX
LEAL 12(SP), BX
MOVL AX, 0(SP)
@@ -11,10 +13,10 @@ TEXT _rt0_386_linux(SB),7,$8
CALL main(SB)
INT $3
-TEXT main(SB),7,$0
- JMP _rt0_386(SB)
+TEXT main(SB),NOSPLIT,$0
+ JMP _rt0_go(SB)
-TEXT _fallback_vdso(SB),7,$0
+TEXT _fallback_vdso(SB),NOSPLIT,$0
INT $0x80
RET
diff --git a/src/pkg/runtime/rt0_linux_amd64.s b/src/pkg/runtime/rt0_linux_amd64.s
index dfc9c0421..a887ced8f 100644
--- a/src/pkg/runtime/rt0_linux_amd64.s
+++ b/src/pkg/runtime/rt0_linux_amd64.s
@@ -2,12 +2,14 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT _rt0_amd64_linux(SB),7,$-8
+#include "../../cmd/ld/textflag.h"
+
+TEXT _rt0_amd64_linux(SB),NOSPLIT,$-8
LEAQ 8(SP), SI // argv
MOVQ 0(SP), DI // argc
MOVQ $main(SB), AX
JMP AX
-TEXT main(SB),7,$-8
- MOVQ $_rt0_amd64(SB), AX
+TEXT main(SB),NOSPLIT,$-8
+ MOVQ $_rt0_go(SB), AX
JMP AX
diff --git a/src/pkg/runtime/rt0_linux_arm.s b/src/pkg/runtime/rt0_linux_arm.s
index a648160cf..309fa2f79 100644
--- a/src/pkg/runtime/rt0_linux_arm.s
+++ b/src/pkg/runtime/rt0_linux_arm.s
@@ -2,7 +2,15 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT _rt0_arm_linux(SB),7,$-4
+#include "../../cmd/ld/textflag.h"
+
+TEXT _rt0_arm_linux(SB),NOSPLIT,$-4
+ MOVW (R13), R0 // argc
+ MOVW $4(R13), R1 // argv
+ MOVW $_rt0_arm_linux1(SB), R4
+ B (R4)
+
+TEXT _rt0_arm_linux1(SB),NOSPLIT,$-4
// We first need to detect the kernel ABI, and warn the user
// if the system only supports OABI
// The strategy here is to call some EABI syscall to see if
@@ -12,38 +20,45 @@ TEXT _rt0_arm_linux(SB),7,$-4
// we don't know the kernel ABI... Oh, not really, we can do
// syscall in Thumb mode.
- // set up sa_handler
- MOVW $bad_abi<>(SB), R0 // sa_handler
- MOVW $0, R1 // sa_flags
- MOVW $0, R2 // sa_restorer
- MOVW $0, R3 // sa_mask
- MOVM.DB.W [R0-R3], (R13)
- MOVW $4, R0 // SIGILL
- MOVW R13, R1 // sa
- SUB $16, R13
- MOVW R13, R2 // old_sa
- MOVW $8, R3 // c
- MOVW $174, R7 // sys_sigaction
- BL oabi_syscall<>(SB)
+ // Save argc and argv
+ MOVM.DB.W [R0-R1], (R13)
+
+ // Thumb mode OABI check disabled because there are some
+ // EABI systems that do not support Thumb execution.
+ // We can run on them except for this check!
+
+ // // set up sa_handler
+ // MOVW $bad_abi<>(SB), R0 // sa_handler
+ // MOVW $0, R1 // sa_flags
+ // MOVW $0, R2 // sa_restorer
+ // MOVW $0, R3 // sa_mask
+ // MOVM.DB.W [R0-R3], (R13)
+ // MOVW $4, R0 // SIGILL
+ // MOVW R13, R1 // sa
+ // SUB $16, R13
+ // MOVW R13, R2 // old_sa
+ // MOVW $8, R3 // c
+ // MOVW $174, R7 // sys_sigaction
+ // BL oabi_syscall<>(SB)
// do an EABI syscall
MOVW $20, R7 // sys_getpid
SWI $0 // this will trigger SIGILL on OABI systems
- MOVW $4, R0 // SIGILL
- MOVW R13, R1 // sa
- MOVW $0, R2 // old_sa
- MOVW $8, R3 // c
- MOVW $174, R7 // sys_sigaction
- SWI $0 // restore signal handler
- ADD $32, R13
+ // MOVW $4, R0 // SIGILL
+ // MOVW R13, R1 // sa
+ // MOVW $0, R2 // old_sa
+ // MOVW $8, R3 // c
+ // MOVW $174, R7 // sys_sigaction
+ // SWI $0 // restore signal handler
+ // ADD $32, R13
SUB $4, R13 // fake a stack frame for runtime·setup_auxv
BL runtime·setup_auxv(SB)
ADD $4, R13
- B _rt0_arm(SB)
+ B _rt0_go(SB)
-TEXT bad_abi<>(SB),7,$-4
+TEXT bad_abi<>(SB),NOSPLIT,$-4
// give diagnosis and exit
MOVW $2, R0 // stderr
MOVW $bad_abi_msg(SB), R1 // data
@@ -64,9 +79,13 @@ DATA bad_abi_msg+0x28(SB)/4, $"nels"
DATA bad_abi_msg+0x2c(SB)/1, $0xa
GLOBL bad_abi_msg(SB), $45
-TEXT oabi_syscall<>(SB),7,$-4
+TEXT oabi_syscall<>(SB),NOSPLIT,$-4
ADD $1, PC, R4
WORD $0xe12fff14 //BX (R4) // enter thumb mode
// TODO(minux): only supports little-endian CPUs
WORD $0x4770df01 // swi $1; bx lr
+TEXT main(SB),NOSPLIT,$-4
+ MOVW $_rt0_arm_linux1(SB), R4
+ B (R4)
+
diff --git a/src/pkg/runtime/rt0_netbsd_386.s b/src/pkg/runtime/rt0_netbsd_386.s
index b4c029c53..eb348fcee 100644
--- a/src/pkg/runtime/rt0_netbsd_386.s
+++ b/src/pkg/runtime/rt0_netbsd_386.s
@@ -2,7 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT _rt0_386_netbsd(SB),7,$8
+#include "../../cmd/ld/textflag.h"
+
+TEXT _rt0_386_netbsd(SB),NOSPLIT,$8
MOVL 8(SP), AX
LEAL 12(SP), BX
MOVL AX, 0(SP)
@@ -10,5 +12,5 @@ TEXT _rt0_386_netbsd(SB),7,$8
CALL main(SB)
INT $3
-TEXT main(SB),7,$0
- JMP _rt0_386(SB)
+TEXT main(SB),NOSPLIT,$0
+ JMP _rt0_go(SB)
diff --git a/src/pkg/runtime/rt0_netbsd_amd64.s b/src/pkg/runtime/rt0_netbsd_amd64.s
index 9e7b78edc..c8e3fb18c 100644
--- a/src/pkg/runtime/rt0_netbsd_amd64.s
+++ b/src/pkg/runtime/rt0_netbsd_amd64.s
@@ -2,12 +2,14 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT _rt0_amd64_netbsd(SB),7,$-8
+#include "../../cmd/ld/textflag.h"
+
+TEXT _rt0_amd64_netbsd(SB),NOSPLIT,$-8
LEAQ 8(SP), SI // argv
MOVQ 0(SP), DI // argc
MOVQ $main(SB), AX
JMP AX
-TEXT main(SB),7,$-8
- MOVQ $_rt0_amd64(SB), AX
+TEXT main(SB),NOSPLIT,$-8
+ MOVQ $_rt0_go(SB), AX
JMP AX
diff --git a/src/pkg/runtime/rt0_netbsd_arm.s b/src/pkg/runtime/rt0_netbsd_arm.s
index 8c1588f2e..36effc3c5 100644
--- a/src/pkg/runtime/rt0_netbsd_arm.s
+++ b/src/pkg/runtime/rt0_netbsd_arm.s
@@ -2,7 +2,12 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
// FreeBSD/NetBSD and Linux use the same linkage to main
-TEXT _rt0_arm_netbsd(SB),7,$-4
- B _rt0_arm(SB)
+TEXT _rt0_arm_netbsd(SB),NOSPLIT,$-4
+ MOVW (R13), R0 // argc
+ MOVW $4(R13), R1 // argv
+ MOVM.DB.W [R0-R1], (R13)
+ B _rt0_go(SB)
diff --git a/src/pkg/runtime/rt0_openbsd_386.s b/src/pkg/runtime/rt0_openbsd_386.s
index 9c00a7334..9e80f69be 100644
--- a/src/pkg/runtime/rt0_openbsd_386.s
+++ b/src/pkg/runtime/rt0_openbsd_386.s
@@ -2,7 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT _rt0_386_openbsd(SB),7,$8
+#include "../../cmd/ld/textflag.h"
+
+TEXT _rt0_386_openbsd(SB),NOSPLIT,$8
MOVL 8(SP), AX
LEAL 12(SP), BX
MOVL AX, 0(SP)
@@ -10,5 +12,5 @@ TEXT _rt0_386_openbsd(SB),7,$8
CALL main(SB)
INT $3
-TEXT main(SB),7,$0
- JMP _rt0_386(SB)
+TEXT main(SB),NOSPLIT,$0
+ JMP _rt0_go(SB)
diff --git a/src/pkg/runtime/rt0_openbsd_amd64.s b/src/pkg/runtime/rt0_openbsd_amd64.s
index 245a4c0f9..b1ad403b7 100644
--- a/src/pkg/runtime/rt0_openbsd_amd64.s
+++ b/src/pkg/runtime/rt0_openbsd_amd64.s
@@ -2,12 +2,14 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT _rt0_amd64_openbsd(SB),7,$-8
+#include "../../cmd/ld/textflag.h"
+
+TEXT _rt0_amd64_openbsd(SB),NOSPLIT,$-8
LEAQ 8(SP), SI // argv
MOVQ 0(SP), DI // argc
MOVQ $main(SB), AX
JMP AX
-TEXT main(SB),7,$-8
- MOVQ $_rt0_amd64(SB), AX
+TEXT main(SB),NOSPLIT,$-8
+ MOVQ $_rt0_go(SB), AX
JMP AX
diff --git a/src/pkg/runtime/rt0_plan9_386.s b/src/pkg/runtime/rt0_plan9_386.s
index 7af1eae7c..dad75c84d 100644
--- a/src/pkg/runtime/rt0_plan9_386.s
+++ b/src/pkg/runtime/rt0_plan9_386.s
@@ -2,7 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT _rt0_386_plan9(SB),7, $0
+#include "../../cmd/ld/textflag.h"
+
+TEXT _rt0_386_plan9(SB),NOSPLIT, $0
MOVL AX, _tos(SB)
// move arguments down to make room for
@@ -33,7 +35,7 @@ argv_fix:
PUSHL AX
PUSHL $-1
- JMP _rt0_386(SB)
+ JMP _rt0_go(SB)
DATA runtime·isplan9(SB)/4, $1
GLOBL runtime·isplan9(SB), $4
diff --git a/src/pkg/runtime/rt0_plan9_amd64.s b/src/pkg/runtime/rt0_plan9_amd64.s
index 16e5e82b7..79a7c92fc 100644
--- a/src/pkg/runtime/rt0_plan9_amd64.s
+++ b/src/pkg/runtime/rt0_plan9_amd64.s
@@ -2,10 +2,12 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT _rt0_amd64_plan9(SB),7,$-8
+#include "../../cmd/ld/textflag.h"
+
+TEXT _rt0_amd64_plan9(SB),NOSPLIT,$-8
LEAQ 8(SP), SI // argv
MOVQ 0(SP), DI // argc
- MOVQ $_rt0_amd64(SB), AX
+ MOVQ $_rt0_go(SB), AX
JMP AX
DATA runtime·isplan9(SB)/4, $1
diff --git a/src/pkg/runtime/rt0_windows_386.s b/src/pkg/runtime/rt0_windows_386.s
index 6e34c6c17..594e2cd34 100644
--- a/src/pkg/runtime/rt0_windows_386.s
+++ b/src/pkg/runtime/rt0_windows_386.s
@@ -2,7 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-TEXT _rt0_386_windows(SB),7,$12
+#include "../../cmd/ld/textflag.h"
+
+TEXT _rt0_386_windows(SB),NOSPLIT,$12
MOVL 12(SP), AX
LEAL 16(SP), BX
MOVL AX, 4(SP)
@@ -10,8 +12,8 @@ TEXT _rt0_386_windows(SB),7,$12
MOVL $-1, 0(SP) // return PC for main
JMP main(SB)
-TEXT main(SB),7,$0
- JMP _rt0_386(SB)
+TEXT main(SB),NOSPLIT,$0
+ JMP _rt0_go(SB)
DATA runtime·iswindows(SB)/4, $1
diff --git a/src/pkg/runtime/rt0_windows_amd64.s b/src/pkg/runtime/rt0_windows_amd64.s
index b48c05570..32e18b02b 100644
--- a/src/pkg/runtime/rt0_windows_amd64.s
+++ b/src/pkg/runtime/rt0_windows_amd64.s
@@ -3,15 +3,16 @@
// license that can be found in the LICENSE file.
#include "zasm_GOOS_GOARCH.h"
+#include "../../cmd/ld/textflag.h"
-TEXT _rt0_amd64_windows(SB),7,$-8
+TEXT _rt0_amd64_windows(SB),NOSPLIT,$-8
LEAQ 8(SP), SI // argv
MOVQ 0(SP), DI // argc
MOVQ $main(SB), AX
JMP AX
-TEXT main(SB),7,$-8
- MOVQ $_rt0_amd64(SB), AX
+TEXT main(SB),NOSPLIT,$-8
+ MOVQ $_rt0_go(SB), AX
JMP AX
DATA runtime·iswindows(SB)/4, $1
diff --git a/src/pkg/runtime/runtime-gdb.py b/src/pkg/runtime/runtime-gdb.py
index cb70ca028..e704f4c4b 100644
--- a/src/pkg/runtime/runtime-gdb.py
+++ b/src/pkg/runtime/runtime-gdb.py
@@ -436,6 +436,9 @@ class GoIfaceCmd(gdb.Command):
#
# Register all convenience functions and CLI commands
#
-for k in vars().values():
- if hasattr(k, 'invoke'):
- k()
+GoLenFunc()
+GoCapFunc()
+DTypeFunc()
+GoroutinesCmd()
+GoroutineCmd()
+GoIfaceCmd()
diff --git a/src/pkg/runtime/runtime.c b/src/pkg/runtime/runtime.c
index d62408118..ab9fed805 100644
--- a/src/pkg/runtime/runtime.c
+++ b/src/pkg/runtime/runtime.c
@@ -4,6 +4,7 @@
#include "runtime.h"
#include "arch_GOARCH.h"
+#include "../../cmd/ld/textflag.h"
enum {
maxround = sizeof(uintptr),
@@ -157,11 +158,12 @@ TestAtomic64(void)
z64 = 42;
x64 = 0;
PREFETCH(&z64);
- if(runtime·cas64(&z64, &x64, 1))
+ if(runtime·cas64(&z64, x64, 1))
runtime·throw("cas64 failed");
- if(x64 != 42)
+ if(x64 != 0)
runtime·throw("cas64 failed");
- if(!runtime·cas64(&z64, &x64, 1))
+ x64 = 42;
+ if(!runtime·cas64(&z64, x64, 1))
runtime·throw("cas64 failed");
if(x64 != 42 || z64 != 1)
runtime·throw("cas64 failed");
@@ -193,7 +195,7 @@ runtime·check(void)
uint64 h;
float32 i, i1;
float64 j, j1;
- void* k;
+ byte *k, *k1;
uint16* l;
struct x1 {
byte x;
@@ -219,6 +221,9 @@ runtime·check(void)
if(offsetof(struct y1, y) != 1) runtime·throw("bad offsetof y1.y");
if(sizeof(struct y1) != 2) runtime·throw("bad sizeof y1");
+ if(runtime·timediv(12345LL*1000000000+54321, 1000000000, &e) != 12345 || e != 54321)
+ runtime·throw("bad timediv");
+
uint32 z;
z = 1;
if(!runtime·cas(&z, 1, 2))
@@ -232,6 +237,17 @@ runtime·check(void)
if(z != 4)
runtime·throw("cas4");
+ k = (byte*)0xfedcb123;
+ if(sizeof(void*) == 8)
+ k = (byte*)((uintptr)k<<10);
+ if(runtime·casp((void**)&k, nil, nil))
+ runtime·throw("casp1");
+ k1 = k+1;
+ if(!runtime·casp((void**)&k, k, k1))
+ runtime·throw("casp2");
+ if(k != k1)
+ runtime·throw("casp3");
+
*(uint64*)&j = ~0ULL;
if(j == j)
runtime·throw("float64nan");
@@ -282,12 +298,11 @@ runtime·Caller(intgo skip, uintptr retpc, String retfile, intgo retline, bool r
retbool = true; // have retpc at least
} else {
retpc = rpc[1];
- retfile = f->src;
pc = retpc;
g = runtime·findfunc(rpc[0]);
if(pc > f->entry && (g == nil || g->entry != (uintptr)runtime·sigpanic))
pc--;
- retline = runtime·funcline(f, pc);
+ retline = runtime·funcline(f, pc, &retfile);
retbool = true;
}
FLUSH(&retpc);
@@ -365,3 +380,63 @@ runtime∕pprof·runtime_cyclesPerSecond(int64 res)
res = runtime·tickspersecond();
FLUSH(&res);
}
+
+DebugVars runtime·debug;
+
+static struct {
+ int8* name;
+ int32* value;
+} dbgvar[] = {
+ {"gctrace", &runtime·debug.gctrace},
+ {"schedtrace", &runtime·debug.schedtrace},
+ {"scheddetail", &runtime·debug.scheddetail},
+};
+
+void
+runtime·parsedebugvars(void)
+{
+ byte *p;
+ intgo i, n;
+
+ p = runtime·getenv("GODEBUG");
+ if(p == nil)
+ return;
+ for(;;) {
+ for(i=0; i<nelem(dbgvar); i++) {
+ n = runtime·findnull((byte*)dbgvar[i].name);
+ if(runtime·mcmp(p, (byte*)dbgvar[i].name, n) == 0 && p[n] == '=')
+ *dbgvar[i].value = runtime·atoi(p+n+1);
+ }
+ p = runtime·strstr(p, (byte*)",");
+ if(p == nil)
+ break;
+ p++;
+ }
+}
+
+// Poor mans 64-bit division.
+// This is a very special function, do not use it if you are not sure what you are doing.
+// int64 division is lowered into _divv() call on 386, which does not fit into nosplit functions.
+// Handles overflow in a time-specific manner.
+#pragma textflag NOSPLIT
+int32
+runtime·timediv(int64 v, int32 div, int32 *rem)
+{
+ int32 res, bit;
+
+ if(v >= (int64)div*0x7fffffffLL) {
+ if(rem != nil)
+ *rem = 0;
+ return 0x7fffffff;
+ }
+ res = 0;
+ for(bit = 30; bit >= 0; bit--) {
+ if(v >= ((int64)div<<bit)) {
+ v = v - ((int64)div<<bit);
+ res += 1<<bit;
+ }
+ }
+ if(rem != nil)
+ *rem = v;
+ return res;
+}
diff --git a/src/pkg/runtime/runtime.h b/src/pkg/runtime/runtime.h
index cb72b92d6..f7c2adb12 100644
--- a/src/pkg/runtime/runtime.h
+++ b/src/pkg/runtime/runtime.h
@@ -53,7 +53,6 @@ typedef struct Gobuf Gobuf;
typedef struct Lock Lock;
typedef struct M M;
typedef struct P P;
-typedef struct Mem Mem;
typedef struct Note Note;
typedef struct Slice Slice;
typedef struct Stktop Stktop;
@@ -78,6 +77,7 @@ typedef struct Complex64 Complex64;
typedef struct Complex128 Complex128;
typedef struct WinCall WinCall;
typedef struct SEH SEH;
+typedef struct WinCallbackContext WinCallbackContext;
typedef struct Timers Timers;
typedef struct Timer Timer;
typedef struct GCStats GCStats;
@@ -86,6 +86,7 @@ typedef struct ParFor ParFor;
typedef struct ParForThread ParForThread;
typedef struct CgoMal CgoMal;
typedef struct PollDesc PollDesc;
+typedef struct DebugVars DebugVars;
/*
* Per-CPU declaration.
@@ -147,12 +148,6 @@ enum
// Global <-> per-M stack segment cache transfer batch size.
StackCacheBatch = 16,
};
-enum
-{
- // This value is generated by the linker and should be kept in
- // sync with cmd/ld/lib.h
- ArgsSizeUnknown = 0x80000000,
-};
/*
* structures
*/
@@ -209,10 +204,13 @@ struct Slice
};
struct Gobuf
{
- // The offsets of these fields are known to (hard-coded in) libmach.
+ // The offsets of sp, pc, and g are known to (hard-coded in) libmach.
uintptr sp;
- byte* pc;
+ uintptr pc;
G* g;
+ uintptr ret;
+ void* ctxt;
+ uintptr lr;
};
struct GCStats
{
@@ -224,50 +222,74 @@ struct GCStats
uint64 nosyield;
uint64 nsleep;
};
+
+struct WinCall
+{
+ void (*fn)(void*);
+ uintptr n; // number of parameters
+ void* args; // parameters
+ uintptr r1; // return values
+ uintptr r2;
+ uintptr err; // error number
+};
+struct SEH
+{
+ void* prev;
+ void* handler;
+};
+// describes how to handle callback
+struct WinCallbackContext
+{
+ void* gobody; // Go function to call
+ uintptr argsize; // callback arguments size (in bytes)
+ uintptr restorestack; // adjust stack on return by (in bytes) (386 only)
+};
+
struct G
{
- uintptr stackguard; // cannot move - also known to linker, libmach, runtime/cgo
+ // stackguard0 can be set to StackPreempt as opposed to stackguard
+ uintptr stackguard0; // cannot move - also known to linker, libmach, runtime/cgo
uintptr stackbase; // cannot move - also known to libmach, runtime/cgo
+ uint32 panicwrap; // cannot move - also known to linker
+ uint32 selgen; // valid sudog pointer
Defer* defer;
Panic* panic;
Gobuf sched;
- uintptr gcstack; // if status==Gsyscall, gcstack = stackbase to use during gc
- uintptr gcsp; // if status==Gsyscall, gcsp = sched.sp to use during gc
- byte* gcpc; // if status==Gsyscall, gcpc = sched.pc to use during gc
- uintptr gcguard; // if status==Gsyscall, gcguard = stackguard to use during gc
+ uintptr syscallstack; // if status==Gsyscall, syscallstack = stackbase to use during gc
+ uintptr syscallsp; // if status==Gsyscall, syscallsp = sched.sp to use during gc
+ uintptr syscallpc; // if status==Gsyscall, syscallpc = sched.pc to use during gc
+ uintptr syscallguard; // if status==Gsyscall, syscallguard = stackguard to use during gc
+ uintptr stackguard; // same as stackguard0, but not set to StackPreempt
uintptr stack0;
- FuncVal* fnstart; // initial function
+ uintptr stacksize;
G* alllink; // on allg
void* param; // passed parameter on wakeup
int16 status;
int64 goid;
- uint32 selgen; // valid sudog pointer
int8* waitreason; // if status==Gwaiting
G* schedlink;
bool ispanic;
bool issystem; // do not output in stack dump
bool isbackground; // ignore in deadlock detector
- bool blockingsyscall; // hint that the next syscall will block
+ bool preempt; // preemption signal, duplicates stackguard0 = StackPreempt
int8 raceignore; // ignore race detection events
M* m; // for debuggers, but offset not hard-coded
M* lockedm;
int32 sig;
int32 writenbuf;
byte* writebuf;
- DeferChunk *dchunk;
- DeferChunk *dchunknext;
+ DeferChunk* dchunk;
+ DeferChunk* dchunknext;
uintptr sigcode0;
uintptr sigcode1;
uintptr sigpc;
- uintptr gopc; // pc of go statement that created this goroutine
+ uintptr gopc; // pc of go statement that created this goroutine
uintptr racectx;
uintptr end[];
};
struct M
{
- // The offsets of these fields are known to (hard-coded in) libmach.
G* g0; // goroutine with scheduling stack
- void (*morepc)(void);
void* moreargp; // argument pointer for more stack
Gobuf morebuf; // gobuf arg to morestack
@@ -280,6 +302,7 @@ struct M
uintptr tls[4]; // thread-local storage (for x86 extern register)
void (*mstartfn)(void);
G* curg; // current running goroutine
+ G* caughtsig; // goroutine running during fatal signal
P* p; // attached P for executing Go code (nil if not executing Go code)
P* nextp;
int32 id;
@@ -287,11 +310,9 @@ struct M
int32 throwing;
int32 gcing;
int32 locks;
- int32 nomemprof;
int32 dying;
int32 profilehz;
int32 helpgc;
- bool blockingsyscall;
bool spinning;
uint32 fastrand;
uint64 ncgocall; // number of cgo calls in total
@@ -301,17 +322,17 @@ struct M
M* alllink; // on allm
M* schedlink;
uint32 machport; // Return address for Mach IPC (OS X)
- MCache *mcache;
+ MCache* mcache;
int32 stackinuse;
uint32 stackcachepos;
uint32 stackcachecnt;
void* stackcache[StackCacheSize];
G* lockedg;
- uintptr createstack[32]; // Stack that created this thread.
+ uintptr createstack[32];// Stack that created this thread.
uint32 freglo[16]; // D[i] lsb and F[i]
uint32 freghi[16]; // D[i] msb and F[i+16]
uint32 fflag; // floating point compare flags
- uint32 locked; // tracking for LockOSThread
+ uint32 locked; // tracking for LockOSThread
M* nextwaitm; // next M waiting for lock
uintptr waitsema; // semaphore for parking on locks
uint32 waitsemacount;
@@ -319,19 +340,18 @@ struct M
GCStats gcstats;
bool racecall;
bool needextram;
- void* racepc;
void (*waitunlockf)(Lock*);
void* waitlock;
- uint32 moreframesize_minalloc;
uintptr settype_buf[1024];
uintptr settype_bufsize;
#ifdef GOOS_windows
void* thread; // thread handle
+ WinCall wincall;
#endif
#ifdef GOOS_plan9
- int8* notesig;
+ int8* notesig;
byte* errstr;
#endif
SEH* seh;
@@ -342,10 +362,12 @@ struct P
{
Lock;
- uint32 status; // one of Pidle/Prunning/...
+ int32 id;
+ uint32 status; // one of Pidle/Prunning/...
P* link;
- uint32 tick; // incremented on every scheduler or system call
- M* m; // back-link to associated M (nil if idle)
+ uint32 schedtick; // incremented on every scheduler call
+ uint32 syscalltick; // incremented on every system call
+ M* m; // back-link to associated M (nil if idle)
MCache* mcache;
// Queue of runnable goroutines.
@@ -361,9 +383,13 @@ struct P
byte pad[64];
};
-// The m->locked word holds a single bit saying whether
-// external calls to LockOSThread are in effect, and then a counter
-// of the internal nesting depth of lockOSThread / unlockOSThread.
+// The m->locked word holds two pieces of state counting active calls to LockOSThread/lockOSThread.
+// The low bit (LockExternal) is a boolean reporting whether any LockOSThread call is active.
+// External locks are not recursive; a second lock is silently ignored.
+// The upper bits of m->lockedcount record the nesting depth of calls to lockOSThread
+// (counting up by LockInternal), popped by unlockOSThread (counting down by LockInternal).
+// Internal locks can be recursive. For instance, a lock for cgo can occur while the main
+// goroutine is holding the lock during the initialization phase.
enum
{
LockExternal = 1,
@@ -373,10 +399,11 @@ enum
struct Stktop
{
// The offsets of these fields are known to (hard-coded in) libmach.
- uint8* stackguard;
- uint8* stackbase;
+ uintptr stackguard;
+ uintptr stackbase;
Gobuf gobuf;
uint32 argsize;
+ uint32 panicwrap;
uint8* argp; // pointer to arguments in old frame
uintptr free; // if free>0, call stackfree using free as size
@@ -398,23 +425,27 @@ enum
SigIgnored = 1<<6, // the signal was ignored before we registered for it
};
-// NOTE(rsc): keep in sync with extern.go:/type.Func.
-// Eventually, the loaded symbol table should be closer to this form.
+// Layout of in-memory per-function information prepared by linker
+// See http://golang.org/s/go12symtab.
+// Keep in sync with linker and with ../../libmach/sym.c
+// and with package debug/gosym.
struct Func
{
- String name;
- String type; // go type string
- String src; // src file name
- Slice pcln; // pc/ln tab for this func
- uintptr entry; // entry pc
- uintptr pc0; // starting pc, ln for table
- int32 ln0;
- int32 frame; // stack frame size
+ uintptr entry; // start pc
+ int32 nameoff;// function name
+
int32 args; // in/out args size
- int32 locals; // locals size
+ int32 frame; // legacy frame size; use pcsp if possible
+
+ int32 pcsp;
+ int32 pcfile;
+ int32 pcln;
+ int32 npcdata;
+ int32 nfuncdata;
};
// layout of Itab known to compilers
+// allocated in non-garbage-collected memory
struct Itab
{
InterfaceType* inter;
@@ -425,21 +456,6 @@ struct Itab
void (*fun[])(void);
};
-struct WinCall
-{
- void (*fn)(void*);
- uintptr n; // number of parameters
- void* args; // parameters
- uintptr r1; // return values
- uintptr r2;
- uintptr err; // error number
-};
-struct SEH
-{
- void* prev;
- void* handler;
-};
-
#ifdef GOOS_windows
enum {
Windows = 1
@@ -466,7 +482,7 @@ struct Timers
// If this struct changes, adjust ../time/sleep.go:/runtimeTimer.
struct Timer
{
- int32 i; // heap index
+ int32 i; // heap index
// Timer wakes up at when, and then at when+period, ... (period > 0 only)
// each time calling f(now, arg) in the timer goroutine, so f must be
@@ -514,6 +530,16 @@ struct CgoMal
void *alloc;
};
+// Holds variables parsed from GODEBUG env var.
+struct DebugVars
+{
+ int32 gctrace;
+ int32 schedtrace;
+ int32 scheddetail;
+};
+
+extern bool runtime·precisestack;
+
/*
* defined macros
* you need super-gopher-guru privilege
@@ -621,9 +647,9 @@ void runtime·nilintercopy(uintptr, void*, void*);
struct Defer
{
int32 siz;
- bool special; // not part of defer frame
- bool free; // if special, free when done
- byte* argp; // where args were copied from
+ bool special; // not part of defer frame
+ bool free; // if special, free when done
+ byte* argp; // where args were copied from
byte* pc;
FuncVal* fn;
Defer* link;
@@ -642,12 +668,34 @@ struct DeferChunk
struct Panic
{
Eface arg; // argument to panic
- byte* stackbase; // g->stackbase in panic
+ uintptr stackbase; // g->stackbase in panic
Panic* link; // link to earlier panic
bool recovered; // whether this panic is over
};
/*
+ * stack traces
+ */
+typedef struct Stkframe Stkframe;
+struct Stkframe
+{
+ Func* fn; // function being run
+ uintptr pc; // program counter within fn
+ uintptr lr; // program counter at caller aka link register
+ uintptr sp; // stack pointer at pc
+ uintptr fp; // stack pointer at caller aka frame pointer
+ byte* varp; // top of local variables
+ byte* argp; // pointer to function arguments
+ uintptr arglen; // number of bytes at argp
+};
+
+int32 runtime·gentraceback(uintptr, uintptr, uintptr, G*, int32, uintptr*, int32, void(*)(Stkframe*, void*), void*, bool);
+void runtime·traceback(uintptr pc, uintptr sp, uintptr lr, G* gp);
+void runtime·tracebackothers(G*);
+bool runtime·haszeroargs(uintptr pc);
+bool runtime·topofstack(Func*);
+
+/*
* external data
*/
extern String runtime·emptystring;
@@ -657,25 +705,26 @@ extern G* runtime·lastg;
extern M* runtime·allm;
extern P** runtime·allp;
extern int32 runtime·gomaxprocs;
-extern bool runtime·singleproc;
+extern uint32 runtime·needextram;
extern uint32 runtime·panicking;
-extern uint32 runtime·gcwaiting; // gc is waiting to run
extern int8* runtime·goos;
extern int32 runtime·ncpu;
extern bool runtime·iscgo;
extern void (*runtime·sysargs)(int32, uint8**);
-extern uint32 runtime·maxstring;
+extern uintptr runtime·maxstring;
extern uint32 runtime·Hchansize;
extern uint32 runtime·cpuid_ecx;
extern uint32 runtime·cpuid_edx;
+extern DebugVars runtime·debug;
+extern uintptr runtime·maxstacksize;
/*
* common functions and data
*/
int32 runtime·strcmp(byte*, byte*);
byte* runtime·strstr(byte*, byte*);
-int32 runtime·findnull(byte*);
-int32 runtime·findnullw(uint16*);
+intgo runtime·findnull(byte*);
+intgo runtime·findnullw(uint16*);
void runtime·dump(byte*, int32);
int32 runtime·runetochar(byte*, int32);
int32 runtime·charntorune(int32*, uint8*, int32);
@@ -685,9 +734,9 @@ int32 runtime·charntorune(int32*, uint8*, int32);
*/
#define FLUSH(x) USED(x)
-void runtime·gogo(Gobuf*, uintptr);
-void runtime·gogocall(Gobuf*, void(*)(void), uintptr);
-void runtime·gogocallfn(Gobuf*, FuncVal*);
+void runtime·gogo(Gobuf*);
+void runtime·gostartcall(Gobuf*, void(*)(void), void*);
+void runtime·gostartcallfn(Gobuf*, FuncVal*);
void runtime·gosave(Gobuf*);
void runtime·lessstack(void);
void runtime·goargs(void);
@@ -713,15 +762,13 @@ void runtime·sigenable(uint32 sig);
void runtime·sigdisable(uint32 sig);
int32 runtime·gotraceback(bool *crash);
void runtime·goroutineheader(G*);
-void runtime·traceback(uint8 *pc, uint8 *sp, uint8 *lr, G* gp);
-void runtime·tracebackothers(G*);
int32 runtime·open(int8*, int32, int32);
int32 runtime·read(int32, void*, int32);
int32 runtime·write(int32, void*, int32);
int32 runtime·close(int32);
int32 runtime·mincore(void*, uintptr, byte*);
bool runtime·cas(uint32*, uint32, uint32);
-bool runtime·cas64(uint64*, uint64*, uint64);
+bool runtime·cas64(uint64*, uint64, uint64);
bool runtime·casp(void**, void*, void*);
// Don't confuse with XADD x86 instruction,
// this one is actually 'addx', that is, add-and-fetch.
@@ -748,8 +795,13 @@ void runtime·mpreinit(M*);
void runtime·minit(void);
void runtime·unminit(void);
void runtime·signalstack(byte*, int32);
+void runtime·symtabinit(void);
Func* runtime·findfunc(uintptr);
-int32 runtime·funcline(Func*, uintptr);
+int32 runtime·funcline(Func*, uintptr, String*);
+int32 runtime·funcarglen(Func*, uintptr);
+int32 runtime·funcspdelta(Func*, uintptr);
+int8* runtime·funcname(Func*);
+int32 runtime·pcdatavalue(Func*, int32, uintptr);
void* runtime·stackalloc(uint32);
void runtime·stackfree(void*, uintptr);
MCache* runtime·allocmcache(void);
@@ -762,19 +814,22 @@ uintptr runtime·ifacehash(Iface, uintptr);
uintptr runtime·efacehash(Eface, uintptr);
void* runtime·malloc(uintptr size);
void runtime·free(void *v);
-bool runtime·addfinalizer(void*, FuncVal *fn, uintptr);
void runtime·runpanic(Panic*);
-void* runtime·getcallersp(void*);
+uintptr runtime·getcallersp(void*);
int32 runtime·mcount(void);
int32 runtime·gcount(void);
void runtime·mcall(void(*)(G*));
uint32 runtime·fastrand1(void);
+void runtime·rewindmorestack(Gobuf*);
+int32 runtime·timediv(int64, int32, int32*);
void runtime·setmg(M*, G*);
void runtime·newextram(void);
void runtime·exit(int32);
void runtime·breakpoint(void);
void runtime·gosched(void);
+void runtime·gosched0(G*);
+void runtime·schedtrace(bool);
void runtime·park(void(*)(Lock*), Lock*, int8*);
void runtime·tsleep(int64, int8*);
M* runtime·newm(void);
@@ -786,10 +841,10 @@ void runtime·exitsyscall(void);
G* runtime·newproc1(FuncVal*, byte*, int32, int32, void*);
bool runtime·sigsend(int32 sig);
int32 runtime·callers(int32, uintptr*, int32);
-int32 runtime·gentraceback(byte*, byte*, byte*, G*, int32, uintptr*, int32, void (*)(Func*, byte*, byte*, void*), void*);
int64 runtime·nanotime(void);
void runtime·dopanic(int32);
void runtime·startpanic(void);
+void runtime·freezetheworld(void);
void runtime·unwindstack(G*, byte*);
void runtime·sigprof(uint8 *pc, uint8 *sp, uint8 *lr, G *gp);
void runtime·resetcpuprofiler(int32);
@@ -803,12 +858,17 @@ void runtime·addtimer(Timer*);
bool runtime·deltimer(Timer*);
G* runtime·netpoll(bool);
void runtime·netpollinit(void);
-int32 runtime·netpollopen(int32, PollDesc*);
-int32 runtime·netpollclose(int32);
+int32 runtime·netpollopen(uintptr, PollDesc*);
+int32 runtime·netpollclose(uintptr);
void runtime·netpollready(G**, PollDesc*, int32);
+uintptr runtime·netpollfd(PollDesc*);
void runtime·crash(void);
+void runtime·parsedebugvars(void);
+void _rt0_go(void);
+void* runtime·funcdata(Func*, int32);
#pragma varargck argpos runtime·printf 1
+#pragma varargck type "c" int32
#pragma varargck type "d" int32
#pragma varargck type "d" uint32
#pragma varargck type "D" int64
@@ -854,11 +914,15 @@ void runtime·unlock(Lock*);
* wake up early, it must wait to call noteclear until it
* can be sure that no other goroutine is calling
* notewakeup.
+ *
+ * notesleep/notetsleep are generally called on g0,
+ * notetsleepg is similar to notetsleep but is called on user g.
*/
void runtime·noteclear(Note*);
void runtime·notesleep(Note*);
void runtime·notewakeup(Note*);
-void runtime·notetsleep(Note*, int64);
+bool runtime·notetsleep(Note*, int64); // false - timeout
+bool runtime·notetsleepg(Note*, int64); // false - timeout
/*
* low-level synchronization for implementing the above
@@ -927,6 +991,7 @@ void runtime·printuint(uint64);
void runtime·printhex(uint64);
void runtime·printslice(Slice);
void runtime·printcomplex(Complex128);
+void runtime·newstackcall(FuncVal*, byte*, uint32);
void reflect·call(FuncVal*, byte*, uint32);
void runtime·panic(Eface);
void runtime·panicindex(void);
@@ -938,6 +1003,7 @@ void runtime·panicslice(void);
void runtime·printany(Eface);
void runtime·newTypeAssertionError(String*, String*, String*, String*, Eface*);
void runtime·newErrorString(String, Eface*);
+void runtime·newErrorCString(int8*, Eface*);
void runtime·fadd64c(uint64, uint64, uint64*);
void runtime·fsub64c(uint64, uint64, uint64*);
void runtime·fmul64c(uint64, uint64, uint64*);
@@ -963,7 +1029,7 @@ bool runtime·isInf(float64 f, int32 sign);
bool runtime·isNaN(float64 f);
float64 runtime·ldexp(float64 d, int32 e);
float64 runtime·modf(float64 d, float64 *ip);
-void runtime·semacquire(uint32*);
+void runtime·semacquire(uint32*, bool);
void runtime·semrelease(uint32*);
int32 runtime·gomaxprocsfunc(int32 n);
void runtime·procyield(uint32);
@@ -980,22 +1046,13 @@ Hmap* runtime·makemap_c(MapType*, int64);
Hchan* runtime·makechan_c(ChanType*, int64);
void runtime·chansend(ChanType*, Hchan*, byte*, bool*, void*);
void runtime·chanrecv(ChanType*, Hchan*, byte*, bool*, bool*);
-bool runtime·showframe(Func*, bool);
+bool runtime·showframe(Func*, G*);
+void runtime·printcreatedby(G*);
void runtime·ifaceE2I(InterfaceType*, Eface, Iface*);
-
+bool runtime·ifaceE2I2(InterfaceType*, Eface, Iface*);
uintptr runtime·memlimit(void);
-// If appropriate, ask the operating system to control whether this
-// thread should receive profiling signals. This is only necessary on OS X.
-// An operating system should not deliver a profiling signal to a
-// thread that is not actually executing (what good is that?), but that's
-// what OS X prefers to do. When profiling is turned on, we mask
-// away the profiling signal when threads go to sleep, so that OS X
-// is forced to deliver the signal to a thread that's actually running.
-// This is a no-op on other systems.
-void runtime·setprof(bool);
-
// float.c
extern float64 runtime·nan;
extern float64 runtime·posinf;
diff --git a/src/pkg/runtime/runtime_test.go b/src/pkg/runtime/runtime_test.go
index e45879349..de6e5498e 100644
--- a/src/pkg/runtime/runtime_test.go
+++ b/src/pkg/runtime/runtime_test.go
@@ -6,6 +6,12 @@ package runtime_test
import (
"io"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ . "runtime"
+ "strconv"
+ "strings"
"testing"
)
@@ -79,3 +85,40 @@ func BenchmarkDeferMany(b *testing.B) {
}(1, 2, 3)
}
}
+
+// The profiling signal handler needs to know whether it is executing runtime.gogo.
+// The constant RuntimeGogoBytes in arch_*.h gives the size of the function;
+// we don't have a way to obtain it from the linker (perhaps someday).
+// Test that the constant matches the size determined by 'go tool nm -S'.
+// The value reported will include the padding between runtime.gogo and the
+// next function in memory. That's fine.
+func TestRuntimeGogoBytes(t *testing.T) {
+ dir, err := ioutil.TempDir("", "go-build")
+ if err != nil {
+ t.Fatalf("failed to create temp directory: %v", err)
+ }
+ defer os.RemoveAll(dir)
+
+ out, err := exec.Command("go", "build", "-o", dir+"/hello", "../../../test/helloworld.go").CombinedOutput()
+ if err != nil {
+ t.Fatalf("building hello world: %v\n%s", err, out)
+ }
+
+ out, err = exec.Command("go", "tool", "nm", "-S", dir+"/hello").CombinedOutput()
+ if err != nil {
+ t.Fatalf("go tool nm: %v\n%s", err, out)
+ }
+
+ for _, line := range strings.Split(string(out), "\n") {
+ f := strings.Fields(line)
+ if len(f) == 4 && f[3] == "runtime.gogo" {
+ size, _ := strconv.Atoi(f[1])
+ if GogoBytes() != int32(size) {
+ t.Fatalf("RuntimeGogoBytes = %d, should be %d", GogoBytes(), size)
+ }
+ return
+ }
+ }
+
+ t.Fatalf("go tool nm did not report size for runtime.gogo")
+}
diff --git a/src/pkg/runtime/sema.goc b/src/pkg/runtime/sema.goc
index 4df01fc4e..57f32a0dd 100644
--- a/src/pkg/runtime/sema.goc
+++ b/src/pkg/runtime/sema.goc
@@ -20,23 +20,25 @@
package sync
#include "runtime.h"
#include "arch_GOARCH.h"
+#include "../../cmd/ld/textflag.h"
-typedef struct Sema Sema;
-struct Sema
+typedef struct SemaWaiter SemaWaiter;
+struct SemaWaiter
{
uint32 volatile* addr;
G* g;
int64 releasetime;
- Sema* prev;
- Sema* next;
+ int32 nrelease; // -1 for acquire
+ SemaWaiter* prev;
+ SemaWaiter* next;
};
typedef struct SemaRoot SemaRoot;
struct SemaRoot
{
Lock;
- Sema* head;
- Sema* tail;
+ SemaWaiter* head;
+ SemaWaiter* tail;
// Number of waiters. Read w/o the lock.
uint32 volatile nwait;
};
@@ -49,7 +51,7 @@ struct semtable
SemaRoot;
uint8 pad[CacheLineSize-sizeof(SemaRoot)];
};
-#pragma dataflag 16 /* mark semtable as 'no pointers', hiding from garbage collector */
+#pragma dataflag NOPTR /* mark semtable as 'no pointers', hiding from garbage collector */
static struct semtable semtable[SEMTABLESZ];
static SemaRoot*
@@ -59,7 +61,7 @@ semroot(uint32 *addr)
}
static void
-semqueue(SemaRoot *root, uint32 volatile *addr, Sema *s)
+semqueue(SemaRoot *root, uint32 volatile *addr, SemaWaiter *s)
{
s->g = g;
s->addr = addr;
@@ -73,7 +75,7 @@ semqueue(SemaRoot *root, uint32 volatile *addr, Sema *s)
}
static void
-semdequeue(SemaRoot *root, Sema *s)
+semdequeue(SemaRoot *root, SemaWaiter *s)
{
if(s->next)
s->next->prev = s->prev;
@@ -98,10 +100,10 @@ cansemacquire(uint32 *addr)
return 0;
}
-static void
-semacquireimpl(uint32 volatile *addr, int32 profile)
+void
+runtime·semacquire(uint32 volatile *addr, bool profile)
{
- Sema s; // Needs to be allocated on stack, otherwise garbage collector could deallocate it
+ SemaWaiter s; // Needs to be allocated on stack, otherwise garbage collector could deallocate it
SemaRoot *root;
int64 t0;
@@ -145,15 +147,9 @@ semacquireimpl(uint32 volatile *addr, int32 profile)
}
void
-runtime·semacquire(uint32 volatile *addr)
-{
- semacquireimpl(addr, 0);
-}
-
-void
runtime·semrelease(uint32 volatile *addr)
{
- Sema *s;
+ SemaWaiter *s;
SemaRoot *root;
root = semroot(addr);
@@ -188,10 +184,111 @@ runtime·semrelease(uint32 volatile *addr)
}
}
+// TODO(dvyukov): move to netpoll.goc once it's used by all OSes.
+void net·runtime_Semacquire(uint32 *addr)
+{
+ runtime·semacquire(addr, true);
+}
+
+void net·runtime_Semrelease(uint32 *addr)
+{
+ runtime·semrelease(addr);
+}
+
func runtime_Semacquire(addr *uint32) {
- semacquireimpl(addr, 1);
+ runtime·semacquire(addr, true);
}
func runtime_Semrelease(addr *uint32) {
runtime·semrelease(addr);
}
+
+typedef struct SyncSema SyncSema;
+struct SyncSema
+{
+ Lock;
+ SemaWaiter* head;
+ SemaWaiter* tail;
+};
+
+func runtime_Syncsemcheck(size uintptr) {
+ if(size != sizeof(SyncSema)) {
+ runtime·printf("bad SyncSema size: sync:%D runtime:%D\n", (int64)size, (int64)sizeof(SyncSema));
+ runtime·throw("bad SyncSema size");
+ }
+}
+
+// Syncsemacquire waits for a pairing Syncsemrelease on the same semaphore s.
+func runtime_Syncsemacquire(s *SyncSema) {
+ SemaWaiter w, *wake;
+ int64 t0;
+
+ w.g = g;
+ w.nrelease = -1;
+ w.next = nil;
+ w.releasetime = 0;
+ t0 = 0;
+ if(runtime·blockprofilerate > 0) {
+ t0 = runtime·cputicks();
+ w.releasetime = -1;
+ }
+
+ runtime·lock(s);
+ if(s->head && s->head->nrelease > 0) {
+ // have pending release, consume it
+ wake = nil;
+ s->head->nrelease--;
+ if(s->head->nrelease == 0) {
+ wake = s->head;
+ s->head = wake->next;
+ if(s->head == nil)
+ s->tail = nil;
+ }
+ runtime·unlock(s);
+ if(wake)
+ runtime·ready(wake->g);
+ } else {
+ // enqueue itself
+ if(s->tail == nil)
+ s->head = &w;
+ else
+ s->tail->next = &w;
+ s->tail = &w;
+ runtime·park(runtime·unlock, s, "semacquire");
+ if(t0)
+ runtime·blockevent(w.releasetime - t0, 2);
+ }
+}
+
+// Syncsemrelease waits for n pairing Syncsemacquire on the same semaphore s.
+func runtime_Syncsemrelease(s *SyncSema, n uint32) {
+ SemaWaiter w, *wake;
+
+ w.g = g;
+ w.nrelease = (int32)n;
+ w.next = nil;
+ w.releasetime = 0;
+
+ runtime·lock(s);
+ while(w.nrelease > 0 && s->head && s->head->nrelease < 0) {
+ // have pending acquire, satisfy it
+ wake = s->head;
+ s->head = wake->next;
+ if(s->head == nil)
+ s->tail = nil;
+ if(wake->releasetime)
+ wake->releasetime = runtime·cputicks();
+ runtime·ready(wake->g);
+ w.nrelease--;
+ }
+ if(w.nrelease > 0) {
+ // enqueue itself
+ if(s->tail == nil)
+ s->head = &w;
+ else
+ s->tail->next = &w;
+ s->tail = &w;
+ runtime·park(runtime·unlock, s, "semarelease");
+ } else
+ runtime·unlock(s);
+}
diff --git a/src/pkg/runtime/signal_386.c b/src/pkg/runtime/signal_386.c
index 72b4a66f8..5a913c646 100644
--- a/src/pkg/runtime/signal_386.c
+++ b/src/pkg/runtime/signal_386.c
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd
#include "runtime.h"
#include "defs_GOOS_GOARCH.h"
@@ -39,8 +39,7 @@ runtime·sighandler(int32 sig, Siginfo *info, void *ctxt, G *gp)
bool crash;
if(sig == SIGPROF) {
- if(gp != m->g0 && gp != m->gsignal)
- runtime·sigprof((byte*)SIG_EIP(info, ctxt), (byte*)SIG_ESP(info, ctxt), nil, gp);
+ runtime·sigprof((byte*)SIG_EIP(info, ctxt), (byte*)SIG_ESP(info, ctxt), nil, gp);
return;
}
@@ -96,6 +95,8 @@ runtime·sighandler(int32 sig, Siginfo *info, void *ctxt, G *gp)
return;
Throw:
+ m->throwing = 1;
+ m->caughtsig = gp;
runtime·startpanic();
if(sig < 0 || sig >= NSIG)
@@ -111,8 +112,9 @@ Throw:
runtime·printf("\n");
if(runtime·gotraceback(&crash)){
- runtime·traceback((void*)SIG_EIP(info, ctxt), (void*)SIG_ESP(info, ctxt), 0, gp);
+ runtime·traceback(SIG_EIP(info, ctxt), SIG_ESP(info, ctxt), 0, gp);
runtime·tracebackothers(gp);
+ runtime·printf("\n");
runtime·dumpregs(info, ctxt);
}
diff --git a/src/pkg/runtime/signal_amd64.c b/src/pkg/runtime/signal_amd64.c
index ce17bf36d..f0cbb1f8c 100644
--- a/src/pkg/runtime/signal_amd64.c
+++ b/src/pkg/runtime/signal_amd64.c
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd
#include "runtime.h"
#include "defs_GOOS_GOARCH.h"
@@ -47,8 +47,7 @@ runtime·sighandler(int32 sig, Siginfo *info, void *ctxt, G *gp)
bool crash;
if(sig == SIGPROF) {
- if(gp != m->g0 && gp != m->gsignal)
- runtime·sigprof((byte*)SIG_RIP(info, ctxt), (byte*)SIG_RSP(info, ctxt), nil, gp);
+ runtime·sigprof((byte*)SIG_RIP(info, ctxt), (byte*)SIG_RSP(info, ctxt), nil, gp);
return;
}
@@ -106,6 +105,8 @@ runtime·sighandler(int32 sig, Siginfo *info, void *ctxt, G *gp)
return;
Throw:
+ m->throwing = 1;
+ m->caughtsig = gp;
runtime·startpanic();
if(sig < 0 || sig >= NSIG)
@@ -121,8 +122,9 @@ Throw:
runtime·printf("\n");
if(runtime·gotraceback(&crash)){
- runtime·traceback((void*)SIG_RIP(info, ctxt), (void*)SIG_RSP(info, ctxt), 0, gp);
+ runtime·traceback(SIG_RIP(info, ctxt), SIG_RSP(info, ctxt), 0, gp);
runtime·tracebackothers(gp);
+ runtime·printf("\n");
runtime·dumpregs(info, ctxt);
}
diff --git a/src/pkg/runtime/signal_arm.c b/src/pkg/runtime/signal_arm.c
index adf61de6b..a6e239601 100644
--- a/src/pkg/runtime/signal_arm.c
+++ b/src/pkg/runtime/signal_arm.c
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd
#include "runtime.h"
#include "defs_GOOS_GOARCH.h"
@@ -46,8 +46,7 @@ runtime·sighandler(int32 sig, Siginfo *info, void *ctxt, G *gp)
bool crash;
if(sig == SIGPROF) {
- if(gp != m->g0 && gp != m->gsignal)
- runtime·sigprof((uint8*)SIG_PC(info, ctxt), (uint8*)SIG_SP(info, ctxt), (uint8*)SIG_LR(info, ctxt), gp);
+ runtime·sigprof((uint8*)SIG_PC(info, ctxt), (uint8*)SIG_SP(info, ctxt), (uint8*)SIG_LR(info, ctxt), gp);
return;
}
@@ -94,6 +93,8 @@ runtime·sighandler(int32 sig, Siginfo *info, void *ctxt, G *gp)
return;
Throw:
+ m->throwing = 1;
+ m->caughtsig = gp;
if(runtime·panicking) // traceback already printed
runtime·exit(2);
runtime·panicking = 1;
@@ -111,7 +112,7 @@ Throw:
runtime·printf("\n");
if(runtime·gotraceback(&crash)){
- runtime·traceback((void*)SIG_PC(info, ctxt), (void*)SIG_SP(info, ctxt), (void*)SIG_LR(info, ctxt), gp);
+ runtime·traceback(SIG_PC(info, ctxt), SIG_SP(info, ctxt), SIG_LR(info, ctxt), gp);
runtime·tracebackothers(gp);
runtime·printf("\n");
runtime·dumpregs(info, ctxt);
diff --git a/src/pkg/runtime/signal_dragonfly_386.h b/src/pkg/runtime/signal_dragonfly_386.h
new file mode 100644
index 000000000..a24f1ee96
--- /dev/null
+++ b/src/pkg/runtime/signal_dragonfly_386.h
@@ -0,0 +1,23 @@
+// Copyright 2013 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.
+
+#define SIG_REGS(ctxt) (((Ucontext*)(ctxt))->uc_mcontext)
+
+#define SIG_EAX(info, ctxt) (SIG_REGS(ctxt).mc_eax)
+#define SIG_EBX(info, ctxt) (SIG_REGS(ctxt).mc_ebx)
+#define SIG_ECX(info, ctxt) (SIG_REGS(ctxt).mc_ecx)
+#define SIG_EDX(info, ctxt) (SIG_REGS(ctxt).mc_edx)
+#define SIG_EDI(info, ctxt) (SIG_REGS(ctxt).mc_edi)
+#define SIG_ESI(info, ctxt) (SIG_REGS(ctxt).mc_esi)
+#define SIG_EBP(info, ctxt) (SIG_REGS(ctxt).mc_ebp)
+#define SIG_ESP(info, ctxt) (SIG_REGS(ctxt).mc_esp)
+#define SIG_EIP(info, ctxt) (SIG_REGS(ctxt).mc_eip)
+#define SIG_EFLAGS(info, ctxt) (SIG_REGS(ctxt).mc_eflags)
+
+#define SIG_CS(info, ctxt) (SIG_REGS(ctxt).mc_cs)
+#define SIG_FS(info, ctxt) (SIG_REGS(ctxt).mc_fs)
+#define SIG_GS(info, ctxt) (SIG_REGS(ctxt).mc_gs)
+
+#define SIG_CODE0(info, ctxt) ((info)->si_code)
+#define SIG_CODE1(info, ctxt) ((uintptr)(info)->si_addr)
diff --git a/src/pkg/runtime/signal_dragonfly_amd64.h b/src/pkg/runtime/signal_dragonfly_amd64.h
new file mode 100644
index 000000000..5b4f97782
--- /dev/null
+++ b/src/pkg/runtime/signal_dragonfly_amd64.h
@@ -0,0 +1,31 @@
+// Copyright 2013 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.
+
+#define SIG_REGS(ctxt) (((Ucontext*)(ctxt))->uc_mcontext)
+
+#define SIG_RAX(info, ctxt) (SIG_REGS(ctxt).mc_rax)
+#define SIG_RBX(info, ctxt) (SIG_REGS(ctxt).mc_rbx)
+#define SIG_RCX(info, ctxt) (SIG_REGS(ctxt).mc_rcx)
+#define SIG_RDX(info, ctxt) (SIG_REGS(ctxt).mc_rdx)
+#define SIG_RDI(info, ctxt) (SIG_REGS(ctxt).mc_rdi)
+#define SIG_RSI(info, ctxt) (SIG_REGS(ctxt).mc_rsi)
+#define SIG_RBP(info, ctxt) (SIG_REGS(ctxt).mc_rbp)
+#define SIG_RSP(info, ctxt) (SIG_REGS(ctxt).mc_rsp)
+#define SIG_R8(info, ctxt) (SIG_REGS(ctxt).mc_r8)
+#define SIG_R9(info, ctxt) (SIG_REGS(ctxt).mc_r9)
+#define SIG_R10(info, ctxt) (SIG_REGS(ctxt).mc_r10)
+#define SIG_R11(info, ctxt) (SIG_REGS(ctxt).mc_r11)
+#define SIG_R12(info, ctxt) (SIG_REGS(ctxt).mc_r12)
+#define SIG_R13(info, ctxt) (SIG_REGS(ctxt).mc_r13)
+#define SIG_R14(info, ctxt) (SIG_REGS(ctxt).mc_r14)
+#define SIG_R15(info, ctxt) (SIG_REGS(ctxt).mc_r15)
+#define SIG_RIP(info, ctxt) (SIG_REGS(ctxt).mc_rip)
+#define SIG_RFLAGS(info, ctxt) (SIG_REGS(ctxt).mc_rflags)
+
+#define SIG_CS(info, ctxt) (SIG_REGS(ctxt).mc_cs)
+#define SIG_FS(info, ctxt) (SIG_REGS(ctxt).mc_ss)
+#define SIG_GS(info, ctxt) (SIG_REGS(ctxt).mc_ss)
+
+#define SIG_CODE0(info, ctxt) ((info)->si_code)
+#define SIG_CODE1(info, ctxt) ((uintptr)(info)->si_addr)
diff --git a/src/pkg/runtime/signal_unix.c b/src/pkg/runtime/signal_unix.c
index 54e461f99..4d14b2208 100644
--- a/src/pkg/runtime/signal_unix.c
+++ b/src/pkg/runtime/signal_unix.c
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin freebsd linux openbsd netbsd
+// +build darwin dragonfly freebsd linux openbsd netbsd
#include "runtime.h"
#include "defs_GOOS_GOARCH.h"
@@ -83,13 +83,11 @@ runtime·resetcpuprofiler(int32 hz)
runtime·memclr((byte*)&it, sizeof it);
if(hz == 0) {
runtime·setitimer(ITIMER_PROF, &it, nil);
- runtime·setprof(false);
} else {
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);
- runtime·setprof(true);
}
m->profilehz = hz;
}
diff --git a/src/pkg/runtime/signals_dragonfly.h b/src/pkg/runtime/signals_dragonfly.h
new file mode 100644
index 000000000..4d27e050d
--- /dev/null
+++ b/src/pkg/runtime/signals_dragonfly.h
@@ -0,0 +1,51 @@
+// 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.
+
+#define N SigNotify
+#define K SigKill
+#define T SigThrow
+#define P SigPanic
+#define D SigDefault
+
+SigTab runtime·sigtab[] = {
+ /* 0 */ 0, "SIGNONE: no trap",
+ /* 1 */ N+K, "SIGHUP: terminal line hangup",
+ /* 2 */ N+K, "SIGINT: interrupt",
+ /* 3 */ N+T, "SIGQUIT: quit",
+ /* 4 */ T, "SIGILL: illegal instruction",
+ /* 5 */ T, "SIGTRAP: trace trap",
+ /* 6 */ N+T, "SIGABRT: abort",
+ /* 7 */ T, "SIGEMT: emulate instruction executed",
+ /* 8 */ P, "SIGFPE: floating-point exception",
+ /* 9 */ 0, "SIGKILL: kill",
+ /* 10 */ P, "SIGBUS: bus error",
+ /* 11 */ P, "SIGSEGV: segmentation violation",
+ /* 12 */ T, "SIGSYS: bad system call",
+ /* 13 */ N, "SIGPIPE: write to broken pipe",
+ /* 14 */ N, "SIGALRM: alarm clock",
+ /* 15 */ N+K, "SIGTERM: termination",
+ /* 16 */ N, "SIGURG: urgent condition on socket",
+ /* 17 */ 0, "SIGSTOP: stop",
+ /* 18 */ N+D, "SIGTSTP: keyboard stop",
+ /* 19 */ 0, "SIGCONT: continue after stop",
+ /* 20 */ N, "SIGCHLD: child status has changed",
+ /* 21 */ N+D, "SIGTTIN: background read from tty",
+ /* 22 */ N+D, "SIGTTOU: background write to tty",
+ /* 23 */ N, "SIGIO: i/o now possible",
+ /* 24 */ N, "SIGXCPU: cpu limit exceeded",
+ /* 25 */ N, "SIGXFSZ: file size limit exceeded",
+ /* 26 */ N, "SIGVTALRM: virtual alarm clock",
+ /* 27 */ N, "SIGPROF: profiling alarm clock",
+ /* 28 */ N, "SIGWINCH: window size change",
+ /* 29 */ N, "SIGINFO: status request from keyboard",
+ /* 30 */ N, "SIGUSR1: user-defined signal 1",
+ /* 31 */ N, "SIGUSR2: user-defined signal 2",
+ /* 32 */ N, "SIGTHR: reserved",
+};
+
+#undef N
+#undef K
+#undef T
+#undef P
+#undef D
diff --git a/src/pkg/runtime/sigqueue.goc b/src/pkg/runtime/sigqueue.goc
index 7e083685d..e08bf98aa 100644
--- a/src/pkg/runtime/sigqueue.goc
+++ b/src/pkg/runtime/sigqueue.goc
@@ -28,6 +28,8 @@ package runtime
#include "runtime.h"
#include "defs_GOOS_GOARCH.h"
#include "os_GOOS.h"
+#include "cgocall.h"
+#include "../../cmd/ld/textflag.h"
static struct {
Note;
@@ -105,9 +107,7 @@ func signal_recv() (m uint32) {
new = HASWAITER;
if(runtime·cas(&sig.state, old, new)) {
if (new == HASWAITER) {
- runtime·entersyscallblock();
- runtime·notesleep(&sig);
- runtime·exitsyscall();
+ runtime·notetsleepg(&sig, -1);
runtime·noteclear(&sig);
}
break;
@@ -155,3 +155,11 @@ func signal_disable(s uint32) {
sig.wanted[s/32] &= ~(1U<<(s&31));
runtime·sigdisable(s);
}
+
+// This runs on a foreign stack, without an m or a g. No stack split.
+#pragma textflag NOSPLIT
+void
+runtime·badsignal(uintptr sig)
+{
+ runtime·cgocallback((void (*)(void))runtime·sigsend, &sig, sizeof(sig));
+}
diff --git a/src/pkg/runtime/slice.c b/src/pkg/runtime/slice.c
index dd92a59a9..ef8ab7fe0 100644
--- a/src/pkg/runtime/slice.c
+++ b/src/pkg/runtime/slice.c
@@ -8,8 +8,12 @@
#include "typekind.h"
#include "malloc.h"
#include "race.h"
+#include "../../cmd/ld/textflag.h"
-static bool debug = 0;
+enum
+{
+ debug = 0
+};
static void makeslice1(SliceType*, intgo, intgo, Slice*);
static void growslice1(SliceType*, Slice, intgo, Slice *);
@@ -53,109 +57,6 @@ makeslice1(SliceType *t, intgo len, intgo cap, Slice *ret)
ret->array = runtime·cnewarray(t->elem, cap);
}
-// appendslice(type *Type, x, y, []T) []T
-#pragma textflag 7
-void
-runtime·appendslice(SliceType *t, Slice x, Slice y, Slice ret)
-{
- intgo m;
- uintptr w;
- void *pc;
- uint8 *p, *q;
-
- m = x.len+y.len;
- w = t->elem->size;
-
- if(m < x.len)
- runtime·throw("append: slice overflow");
-
- if(m > x.cap)
- growslice1(t, x, m, &ret);
- else
- ret = x;
-
- if(raceenabled) {
- // Don't mark read/writes on the newly allocated slice.
- pc = runtime·getcallerpc(&t);
- // read x[:len]
- if(m > x.cap)
- runtime·racereadrangepc(x.array, x.len*w, w, pc, runtime·appendslice);
- // read y
- runtime·racereadrangepc(y.array, y.len*w, w, pc, runtime·appendslice);
- // write x[len(x):len(x)+len(y)]
- if(m <= x.cap)
- runtime·racewriterangepc(ret.array+ret.len*w, y.len*w, w, pc, runtime·appendslice);
- }
-
- // A very common case is appending bytes. Small appends can avoid the overhead of memmove.
- // We can generalize a bit here, and just pick small-sized appends.
- p = ret.array+ret.len*w;
- q = y.array;
- w *= y.len;
- if(w <= appendCrossover) {
- if(p <= q || w <= p-q) // No overlap.
- while(w-- > 0)
- *p++ = *q++;
- else {
- p += w;
- q += w;
- while(w-- > 0)
- *--p = *--q;
- }
- } else {
- runtime·memmove(p, q, w);
- }
- ret.len += y.len;
- FLUSH(&ret);
-}
-
-
-// appendstr([]byte, string) []byte
-#pragma textflag 7
-void
-runtime·appendstr(SliceType *t, Slice x, String y, Slice ret)
-{
- intgo m;
- void *pc;
- uintptr w;
- uint8 *p, *q;
-
- m = x.len+y.len;
-
- if(m < x.len)
- runtime·throw("append: string overflow");
-
- if(m > x.cap)
- growslice1(t, x, m, &ret);
- else
- ret = x;
-
- if(raceenabled) {
- // Don't mark read/writes on the newly allocated slice.
- pc = runtime·getcallerpc(&t);
- // read x[:len]
- if(m > x.cap)
- runtime·racereadrangepc(x.array, x.len, 1, pc, runtime·appendstr);
- // write x[len(x):len(x)+len(y)]
- if(m <= x.cap)
- runtime·racewriterangepc(ret.array+ret.len, y.len, 1, pc, runtime·appendstr);
- }
-
- // Small appends can avoid the overhead of memmove.
- w = y.len;
- p = ret.array+ret.len;
- q = y.str;
- if(w <= appendCrossover) {
- while(w-- > 0)
- *p++ = *q++;
- } else {
- runtime·memmove(p, q, w);
- }
- ret.len += y.len;
- FLUSH(&ret);
-}
-
-
// growslice(type *Type, x, []T, n int64) []T
void
runtime·growslice(SliceType *t, Slice old, int64 n, Slice ret)
@@ -173,7 +74,7 @@ runtime·growslice(SliceType *t, Slice old, int64 n, Slice ret)
if(raceenabled) {
pc = runtime·getcallerpc(&t);
- runtime·racereadrangepc(old.array, old.len*t->elem->size, t->elem->size, pc, runtime·growslice);
+ runtime·racereadrangepc(old.array, old.len*t->elem->size, pc, runtime·growslice);
}
growslice1(t, old, cap, &ret);
@@ -214,7 +115,7 @@ growslice1(SliceType *t, Slice x, intgo newcap, Slice *ret)
}
// copy(to any, fr any, wid uintptr) int
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·copy(Slice to, Slice fm, uintptr width, intgo ret)
{
@@ -231,8 +132,8 @@ runtime·copy(Slice to, Slice fm, uintptr width, intgo ret)
if(raceenabled) {
pc = runtime·getcallerpc(&to);
- runtime·racewriterangepc(to.array, ret*width, width, pc, runtime·copy);
- runtime·racereadrangepc(fm.array, ret*width, width, pc, runtime·copy);
+ runtime·racewriterangepc(to.array, ret*width, pc, runtime·copy);
+ runtime·racereadrangepc(fm.array, ret*width, pc, runtime·copy);
}
if(ret == 1 && width == 1) { // common case worth about 2x to do here
@@ -257,7 +158,7 @@ out:
}
}
-#pragma textflag 7
+#pragma textflag NOSPLIT
void
runtime·slicestringcopy(Slice to, String fm, intgo ret)
{
@@ -274,7 +175,7 @@ runtime·slicestringcopy(Slice to, String fm, intgo ret)
if(raceenabled) {
pc = runtime·getcallerpc(&to);
- runtime·racewriterangepc(to.array, ret, 1, pc, runtime·slicestringcopy);
+ runtime·racewriterangepc(to.array, ret, pc, runtime·slicestringcopy);
}
runtime·memmove(to.array, fm.str, ret);
diff --git a/src/pkg/runtime/softfloat_arm.c b/src/pkg/runtime/softfloat_arm.c
index 9a5440630..f5801dde4 100644
--- a/src/pkg/runtime/softfloat_arm.c
+++ b/src/pkg/runtime/softfloat_arm.c
@@ -7,6 +7,7 @@
// It uses true little-endian doubles, while the 7500 used mixed-endian.
#include "runtime.h"
+#include "../../cmd/ld/textflag.h"
#define CPSR 14
#define FLAGS_N (1U << 31)
@@ -576,20 +577,44 @@ done:
return 0;
}
-#pragma textflag 7
+typedef struct Sfregs Sfregs;
+
+// NOTE: These are all recorded as pointers because they are possibly live registers,
+// and we don't know what they contain. Recording them as pointers should be
+// safer than not.
+struct Sfregs
+{
+ uint32 *r0;
+ uint32 *r1;
+ uint32 *r2;
+ uint32 *r3;
+ uint32 *r4;
+ uint32 *r5;
+ uint32 *r6;
+ uint32 *r7;
+ uint32 *r8;
+ uint32 *r9;
+ uint32 *r10;
+ uint32 *r11;
+ uint32 *r12;
+ uint32 *r13;
+ uint32 cspr;
+};
+
+#pragma textflag NOSPLIT
uint32*
-runtime·_sfloat2(uint32 *lr, uint32 r0)
+runtime·_sfloat2(uint32 *lr, Sfregs regs)
{
uint32 skip;
- skip = stepflt(lr, &r0);
+ skip = stepflt(lr, (uint32*)&regs.r0);
if(skip == 0) {
runtime·printf("sfloat2 %p %x\n", lr, *lr);
fabort(); // not ok to fail first instruction
}
lr += skip;
- while(skip = stepflt(lr, &r0))
+ while(skip = stepflt(lr, (uint32*)&regs.r0))
lr += skip;
return lr;
}
diff --git a/src/pkg/runtime/stack.c b/src/pkg/runtime/stack.c
index e9a35672f..634706051 100644
--- a/src/pkg/runtime/stack.c
+++ b/src/pkg/runtime/stack.c
@@ -7,6 +7,11 @@
#include "malloc.h"
#include "stack.h"
+enum
+{
+ StackDebug = 0,
+};
+
typedef struct StackCacheNode StackCacheNode;
struct StackCacheNode
{
@@ -31,10 +36,9 @@ stackcacherefill(void)
stackcache = n->next;
runtime·unlock(&stackcachemu);
if(n == nil) {
- n = (StackCacheNode*)runtime·SysAlloc(FixedStack*StackCacheBatch);
+ n = (StackCacheNode*)runtime·SysAlloc(FixedStack*StackCacheBatch, &mstats.stacks_sys);
if(n == nil)
runtime·throw("out of memory (stackcacherefill)");
- runtime·xadd64(&mstats.stacks_sys, FixedStack*StackCacheBatch);
for(i = 0; i < StackCacheBatch-1; i++)
n->batch[i] = (byte*)n + (i+1)*FixedStack;
}
@@ -81,13 +85,10 @@ runtime·stackalloc(uint32 n)
if(g != m->g0)
runtime·throw("stackalloc not on scheduler stack");
- // Stack allocator uses malloc/free most of the time,
- // but if we're in the middle of malloc and need stack,
- // we have to do something else to avoid deadlock.
- // In that case, we fall back on a fixed-size free-list
- // allocator, assuming that inside malloc all the stack
- // frames are small, so that all the stack allocations
- // will be a single size, the minimum (right now, 5k).
+ // Stacks are usually allocated with a fixed-size free-list allocator,
+ // but if we need a stack of non-standard size, we fall back on malloc
+ // (assuming that inside malloc and GC all the stack frames are small,
+ // so that we do not deadlock).
if(n == FixedStack || m->mallocing || m->gcing) {
if(n != FixedStack) {
runtime·printf("stackalloc: in malloc, size=%d want %d\n", FixedStack, n);
@@ -103,7 +104,7 @@ runtime·stackalloc(uint32 n)
m->stackinuse++;
return v;
}
- return runtime·mallocgc(n, FlagNoProfiling|FlagNoGC, 0, 0);
+ return runtime·mallocgc(n, 0, FlagNoProfiling|FlagNoGC|FlagNoZero|FlagNoInvokeGC);
}
void
@@ -131,21 +132,34 @@ void
runtime·oldstack(void)
{
Stktop *top;
- Gobuf label;
uint32 argsize;
- uintptr cret;
byte *sp, *old;
uintptr *src, *dst, *dstend;
G *gp;
int64 goid;
-
-//printf("oldstack m->cret=%p\n", m->cret);
+ int32 oldstatus;
gp = m->curg;
top = (Stktop*)gp->stackbase;
old = (byte*)gp->stackguard - StackGuard;
sp = (byte*)top;
argsize = top->argsize;
+
+ if(StackDebug) {
+ runtime·printf("runtime: oldstack gobuf={pc:%p sp:%p lr:%p} cret=%p argsize=%p\n",
+ top->gobuf.pc, top->gobuf.sp, top->gobuf.lr, m->cret, (uintptr)argsize);
+ }
+
+ // gp->status is usually Grunning, but it could be Gsyscall if a stack split
+ // happens during a function call inside entersyscall.
+ oldstatus = gp->status;
+
+ gp->sched = top->gobuf;
+ gp->sched.ret = m->cret;
+ m->cret = 0; // drop reference
+ gp->status = Gwaiting;
+ gp->waitreason = "stack unsplit";
+
if(argsize > 0) {
sp -= argsize;
dst = (uintptr*)top->argp;
@@ -157,18 +171,23 @@ runtime·oldstack(void)
goid = top->gobuf.g->goid; // fault if g is bad, before gogo
USED(goid);
- label = top->gobuf;
- gp->stackbase = (uintptr)top->stackbase;
- gp->stackguard = (uintptr)top->stackguard;
- if(top->free != 0)
+ gp->stackbase = top->stackbase;
+ gp->stackguard = top->stackguard;
+ gp->stackguard0 = gp->stackguard;
+ gp->panicwrap = top->panicwrap;
+
+ if(top->free != 0) {
+ gp->stacksize -= top->free;
runtime·stackfree(old, top->free);
+ }
- cret = m->cret;
- m->cret = 0; // drop reference
- runtime·gogo(&label, cret);
+ gp->status = oldstatus;
+ runtime·gogo(&gp->sched);
}
-// Called from reflect·call or from runtime·morestack when a new
+uintptr runtime·maxstacksize = 1<<20; // enough until runtime.main sets it for real
+
+// Called from runtime·newstackcall or from runtime·morestack when a new
// stack segment is needed. Allocate a new stack big enough for
// m->moreframesize bytes, copy m->moreargsize bytes to the new frame,
// and then act as though runtime·lessstack called the function at
@@ -176,42 +195,86 @@ runtime·oldstack(void)
void
runtime·newstack(void)
{
- int32 framesize, minalloc, argsize;
- Stktop *top;
- byte *stk, *sp;
+ int32 framesize, argsize, oldstatus;
+ Stktop *top, *oldtop;
+ byte *stk;
+ uintptr sp;
uintptr *src, *dst, *dstend;
G *gp;
Gobuf label;
- bool reflectcall;
+ bool newstackcall;
uintptr free;
+ if(m->morebuf.g != m->curg) {
+ runtime·printf("runtime: newstack called from g=%p\n"
+ "\tm=%p m->curg=%p m->g0=%p m->gsignal=%p\n",
+ m->morebuf.g, m, m->curg, m->g0, m->gsignal);
+ runtime·throw("runtime: wrong goroutine in newstack");
+ }
+
+ // gp->status is usually Grunning, but it could be Gsyscall if a stack split
+ // happens during a function call inside entersyscall.
+ gp = m->curg;
+ oldstatus = gp->status;
+
framesize = m->moreframesize;
argsize = m->moreargsize;
- gp = m->curg;
+ gp->status = Gwaiting;
+ gp->waitreason = "stack split";
+ newstackcall = framesize==1;
+ if(newstackcall)
+ framesize = 0;
- if(m->morebuf.sp < gp->stackguard - StackGuard) {
- runtime·printf("runtime: split stack overflow: %p < %p\n", m->morebuf.sp, gp->stackguard - StackGuard);
+ // For newstackcall the context already points to beginning of runtime·newstackcall.
+ if(!newstackcall)
+ runtime·rewindmorestack(&gp->sched);
+
+ sp = gp->sched.sp;
+ if(thechar == '6' || thechar == '8') {
+ // The call to morestack cost a word.
+ sp -= sizeof(uintptr);
+ }
+ if(StackDebug || sp < gp->stackguard - StackGuard) {
+ runtime·printf("runtime: newstack framesize=%p argsize=%p sp=%p stack=[%p, %p]\n"
+ "\tmorebuf={pc:%p sp:%p lr:%p}\n"
+ "\tsched={pc:%p sp:%p lr:%p ctxt:%p}\n",
+ (uintptr)framesize, (uintptr)argsize, sp, gp->stackguard - StackGuard, gp->stackbase,
+ m->morebuf.pc, m->morebuf.sp, m->morebuf.lr,
+ gp->sched.pc, gp->sched.sp, gp->sched.lr, gp->sched.ctxt);
+ }
+ if(sp < gp->stackguard - StackGuard) {
+ runtime·printf("runtime: split stack overflow: %p < %p\n", sp, gp->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");
}
- minalloc = 0;
- reflectcall = framesize==1;
- if(reflectcall) {
- framesize = 0;
- // moreframesize_minalloc is only set in runtime·gc(),
- // that calls newstack via reflect·call().
- minalloc = m->moreframesize_minalloc;
- m->moreframesize_minalloc = 0;
- if(framesize < minalloc)
- framesize = minalloc;
+ if(gp->stackguard0 == (uintptr)StackPreempt) {
+ if(gp == m->g0)
+ runtime·throw("runtime: preempt g0");
+ if(oldstatus == Grunning && m->p == nil && m->locks == 0)
+ runtime·throw("runtime: g is running but p is not");
+ if(oldstatus == Gsyscall && m->locks == 0)
+ runtime·throw("runtime: stack split during syscall");
+ // Be conservative about where we preempt.
+ // We are interested in preempting user Go code, not runtime code.
+ if(oldstatus != Grunning || m->locks || m->mallocing || m->gcing || m->p->status != Prunning) {
+ // Let the goroutine keep running for now.
+ // gp->preempt is set, so it will be preempted next time.
+ gp->stackguard0 = gp->stackguard;
+ gp->status = oldstatus;
+ runtime·gogo(&gp->sched); // never return
+ }
+ // Act like goroutine called runtime.Gosched.
+ gp->status = oldstatus;
+ runtime·gosched0(gp); // never return
}
- if(reflectcall && minalloc == 0 && m->morebuf.sp - sizeof(Stktop) - argsize - 32 > gp->stackguard) {
- // special case: called from reflect.call (framesize==1)
+ if(newstackcall && m->morebuf.sp - sizeof(Stktop) - argsize - 32 > gp->stackguard) {
+ // special case: called from runtime.newstackcall (framesize==1)
// to call code with an arbitrary argument size,
// and we have enough space on the current stack.
// the new Stktop* is necessary to unwind, but
@@ -226,34 +289,50 @@ runtime·newstack(void)
if(framesize < StackMin)
framesize = StackMin;
framesize += StackSystem;
+ gp->stacksize += framesize;
+ if(gp->stacksize > runtime·maxstacksize) {
+ runtime·printf("runtime: goroutine stack exceeds %D-byte limit\n", (uint64)runtime·maxstacksize);
+ runtime·throw("stack overflow");
+ }
stk = runtime·stackalloc(framesize);
top = (Stktop*)(stk+framesize-sizeof(*top));
free = framesize;
}
- if(0) {
- runtime·printf("newstack framesize=%d argsize=%d morepc=%p moreargp=%p gobuf=%p, %p top=%p old=%p\n",
- framesize, argsize, m->morepc, m->moreargp, m->morebuf.pc, m->morebuf.sp, top, gp->stackbase);
+ if(StackDebug) {
+ runtime·printf("\t-> new stack [%p, %p]\n", stk, top);
}
- top->stackbase = (byte*)gp->stackbase;
- top->stackguard = (byte*)gp->stackguard;
+ top->stackbase = gp->stackbase;
+ top->stackguard = gp->stackguard;
top->gobuf = m->morebuf;
top->argp = m->moreargp;
top->argsize = argsize;
top->free = free;
m->moreargp = nil;
- m->morebuf.pc = nil;
+ m->morebuf.pc = (uintptr)nil;
+ m->morebuf.lr = (uintptr)nil;
m->morebuf.sp = (uintptr)nil;
// copy flag from panic
top->panic = gp->ispanic;
gp->ispanic = false;
+
+ // if this isn't a panic, maybe we're splitting the stack for a panic.
+ // if we're splitting in the top frame, propagate the panic flag
+ // forward so that recover will know we're in a panic.
+ oldtop = (Stktop*)top->stackbase;
+ if(oldtop != nil && oldtop->panic && top->argp == (byte*)oldtop - oldtop->argsize - gp->panicwrap)
+ top->panic = true;
+
+ top->panicwrap = gp->panicwrap;
+ gp->panicwrap = 0;
gp->stackbase = (uintptr)top;
gp->stackguard = (uintptr)stk + StackGuard;
+ gp->stackguard0 = gp->stackguard;
- sp = (byte*)top;
+ sp = (uintptr)top;
if(argsize > 0) {
sp -= argsize;
dst = (uintptr*)sp;
@@ -270,13 +349,34 @@ runtime·newstack(void)
// Continue as if lessstack had just called m->morepc
// (the PC that decided to grow the stack).
- label.sp = (uintptr)sp;
- label.pc = (byte*)runtime·lessstack;
+ runtime·memclr((byte*)&label, sizeof label);
+ label.sp = sp;
+ label.pc = (uintptr)runtime·lessstack;
label.g = m->curg;
- if(reflectcall)
- runtime·gogocallfn(&label, (FuncVal*)m->morepc);
- else
- runtime·gogocall(&label, m->morepc, m->cret);
+ if(newstackcall)
+ runtime·gostartcallfn(&label, (FuncVal*)m->cret);
+ else {
+ runtime·gostartcall(&label, (void(*)(void))gp->sched.pc, gp->sched.ctxt);
+ gp->sched.ctxt = nil;
+ }
+ gp->status = oldstatus;
+ runtime·gogo(&label);
*(int32*)345 = 123; // never return
}
+
+// adjust Gobuf as if it executed a call to fn
+// and then did an immediate gosave.
+void
+runtime·gostartcallfn(Gobuf *gobuf, FuncVal *fv)
+{
+ runtime·gostartcall(gobuf, fv->fn, fv);
+}
+
+void
+runtime∕debug·setMaxStack(intgo in, intgo out)
+{
+ out = runtime·maxstacksize;
+ runtime·maxstacksize = in;
+ FLUSH(&out);
+}
diff --git a/src/pkg/runtime/stack.h b/src/pkg/runtime/stack.h
index 06b0c568c..296eb688d 100644
--- a/src/pkg/runtime/stack.h
+++ b/src/pkg/runtime/stack.h
@@ -71,21 +71,19 @@ enum {
// The amount of extra stack to allocate beyond the size
// needed for the single frame that triggered the split.
- StackExtra = 1024,
+ StackExtra = 2048,
// The minimum stack segment size to allocate.
// If the amount needed for the splitting frame + StackExtra
// is less than this number, the stack will have this size instead.
- StackMin = 4096,
+ StackMin = 8192,
FixedStack = StackMin + StackSystem,
- // Functions that need frames bigger than this call morestack
- // unconditionally. That is, on entry to a function it is assumed
- // that the amount of space available in the current stack segment
- // couldn't possibly be bigger than StackBig. If stack segments
- // do run with more space than StackBig, the space may not be
- // used efficiently. As a result, StackBig should not be significantly
- // smaller than StackMin or StackExtra.
+ // Functions that need frames bigger than this use an extra
+ // instruction to do the stack split check, to avoid overflow
+ // in case SP - framesize wraps below zero.
+ // This value can be no bigger than the size of the unmapped
+ // space at zero.
StackBig = 4096,
// The stack guard is a pointer this many bytes above the
@@ -104,5 +102,11 @@ enum {
// The assumed size of the top-of-stack data block.
// The actual size can be smaller than this but cannot be larger.
// Checked in proc.c's runtime.malg.
- StackTop = 72,
+ StackTop = 96,
};
+
+// Goroutine preemption request.
+// Stored into g->stackguard0 to cause split stack check failure.
+// Must be greater than any real sp.
+// 0xfffffade in hex.
+#define StackPreempt ((uint64)-1314)
diff --git a/src/pkg/runtime/stack_test.go b/src/pkg/runtime/stack_test.go
index da0181a66..00c2d0e06 100644
--- a/src/pkg/runtime/stack_test.go
+++ b/src/pkg/runtime/stack_test.go
@@ -49,7 +49,7 @@ func TestStackSplit(t *testing.T) {
sp, guard := f()
bottom := guard - StackGuard
if sp < bottom+StackLimit {
- fun := FuncForPC(*(*uintptr)(unsafe.Pointer(&f)))
+ fun := FuncForPC(**(**uintptr)(unsafe.Pointer(&f)))
t.Errorf("after %s: sp=%#x < limit=%#x (guard=%#x, bottom=%#x)",
fun.Name(), sp, bottom+StackLimit, guard, bottom)
}
diff --git a/src/pkg/runtime/string.goc b/src/pkg/runtime/string.goc
index 49bf1148b..b79acbe1c 100644
--- a/src/pkg/runtime/string.goc
+++ b/src/pkg/runtime/string.goc
@@ -7,13 +7,15 @@ package runtime
#include "arch_GOARCH.h"
#include "malloc.h"
#include "race.h"
+#include "../../cmd/ld/textflag.h"
String runtime·emptystring;
-int32
+#pragma textflag NOSPLIT
+intgo
runtime·findnull(byte *s)
{
- int32 l;
+ intgo l;
if(s == nil)
return 0;
@@ -22,10 +24,10 @@ runtime·findnull(byte *s)
return l;
}
-int32
+intgo
runtime·findnullw(uint16 *s)
{
- int32 l;
+ intgo l;
if(s == nil)
return 0;
@@ -34,23 +36,23 @@ runtime·findnullw(uint16 *s)
return l;
}
-uint32 runtime·maxstring = 256; // a hint for print
+uintptr runtime·maxstring = 256; // a hint for print
static String
gostringsize(intgo l)
{
String s;
- uint32 ms;
+ uintptr ms;
if(l == 0)
return runtime·emptystring;
// leave room for NUL for C runtime (e.g., callers of getenv)
- s.str = runtime·mallocgc(l+1, FlagNoPointers, 1, 0);
+ s.str = runtime·mallocgc(l+1, 0, FlagNoScan|FlagNoZero);
s.len = l;
s.str[l] = 0;
for(;;) {
ms = runtime·maxstring;
- if((uint32)l <= ms || runtime·cas(&runtime·maxstring, ms, (uint32)l))
+ if((uintptr)l <= ms || runtime·casp((void**)&runtime·maxstring, (void*)ms, (void*)l))
break;
}
return s;
@@ -83,7 +85,7 @@ runtime·gobytes(byte *p, intgo n)
{
Slice sl;
- sl.array = runtime·mallocgc(n, FlagNoPointers, 1, 0);
+ sl.array = runtime·mallocgc(n, 0, FlagNoScan|FlagNoZero);
sl.len = n;
sl.cap = n;
runtime·memmove(sl.array, p, n);
@@ -100,6 +102,13 @@ runtime·gostringnocopy(byte *str)
return s;
}
+void
+runtime·cstringToGo(byte *str, String s)
+{
+ s = runtime·gostringnocopy(str);
+ FLUSH(&s);
+}
+
String
runtime·gostringw(uint16 *str)
{
@@ -170,39 +179,14 @@ concatstring(intgo n, String *s)
return out;
}
-#pragma textflag 7
-// s1 is the first of n strings.
-// the output string follows.
-func concatstring(n int, s1 String) {
- (&s1)[n] = concatstring(n, &s1);
-}
-
-static int32
-cmpstring(String s1, String s2)
+// NOTE: Cannot use func syntax, because we need the ...,
+// to signal to the garbage collector that this function does
+// not have a fixed size argument count.
+#pragma textflag NOSPLIT
+void
+runtime·concatstring(intgo n, String s1, ...)
{
- uintgo i, l;
- byte c1, c2;
-
- l = s1.len;
- if(s2.len < l)
- l = s2.len;
- for(i=0; i<l; i++) {
- c1 = s1.str[i];
- c2 = s2.str[i];
- if(c1 < c2)
- return -1;
- if(c1 > c2)
- return +1;
- }
- if(s1.len < s2.len)
- return -1;
- if(s1.len > s2.len)
- return +1;
- return 0;
-}
-
-func cmpstring(s1 String, s2 String) (v int) {
- v = cmpstring(s1, s2);
+ (&s1)[n] = concatstring(n, &s1);
}
func eqstring(s1 String, s2 String) (v bool) {
@@ -268,14 +252,14 @@ func slicebytetostring(b Slice) (s String) {
if(raceenabled) {
pc = runtime·getcallerpc(&b);
- runtime·racereadrangepc(b.array, b.len, 1, pc, runtime·slicebytetostring);
+ runtime·racereadrangepc(b.array, b.len, pc, runtime·slicebytetostring);
}
s = gostringsize(b.len);
runtime·memmove(s.str, b.array, s.len);
}
func stringtoslicebyte(s String) (b Slice) {
- b.array = runtime·mallocgc(s.len, FlagNoPointers, 1, 0);
+ b.array = runtime·mallocgc(s.len, 0, FlagNoScan|FlagNoZero);
b.len = s.len;
b.cap = s.len;
runtime·memmove(b.array, s.str, s.len);
@@ -289,7 +273,7 @@ func slicerunetostring(b Slice) (s String) {
if(raceenabled) {
pc = runtime·getcallerpc(&b);
- runtime·racereadrangepc(b.array, b.len*sizeof(*a), sizeof(*a), pc, runtime·slicerunetostring);
+ runtime·racereadrangepc(b.array, b.len*sizeof(*a), pc, runtime·slicerunetostring);
}
a = (int32*)b.array;
siz1 = 0;
@@ -324,7 +308,7 @@ func stringtoslicerune(s String) (b Slice) {
n++;
}
- b.array = runtime·mallocgc(n*sizeof(r[0]), FlagNoPointers, 1, 0);
+ b.array = runtime·mallocgc(n*sizeof(r[0]), 0, FlagNoScan|FlagNoZero);
b.len = n;
b.cap = n;
p = s.str;
diff --git a/src/pkg/runtime/symtab.c b/src/pkg/runtime/symtab.c
index 578406247..dd0015aee 100644
--- a/src/pkg/runtime/symtab.c
+++ b/src/pkg/runtime/symtab.c
@@ -3,619 +3,268 @@
// license that can be found in the LICENSE file.
// Runtime symbol table parsing.
-//
-// The Go tools use a symbol table derived from the Plan 9 symbol table
-// format. The symbol table is kept in its own section treated as
-// read-only memory when the binary is running: the binary consults the
-// table.
-//
-// The format used by Go 1.0 was basically the Plan 9 format. Each entry
-// is variable sized but had this format:
-//
-// 4-byte value, big endian
-// 1-byte type ([A-Za-z] + 0x80)
-// name, NUL terminated (or for 'z' and 'Z' entries, double-NUL terminated)
-// 4-byte Go type address, big endian (new in Go)
-//
-// In order to support greater interoperation with standard toolchains,
-// Go 1.1 uses a more flexible yet smaller encoding of the entries.
-// The overall structure is unchanged from Go 1.0 and, for that matter,
-// from Plan 9.
-//
-// The Go 1.1 table is a re-encoding of the data in a Go 1.0 table.
-// To identify a new table as new, it begins one of two eight-byte
-// sequences:
-//
-// FF FF FF FD 00 00 00 xx - big endian new table
-// FD FF FF FF 00 00 00 xx - little endian new table
-//
-// This sequence was chosen because old tables stop at an entry with type
-// 0, so old code reading a new table will see only an empty table. The
-// first four bytes are the target-endian encoding of 0xfffffffd. The
-// final xx gives AddrSize, the width of a full-width address.
-//
-// After that header, each entry is encoded as follows.
-//
-// 1-byte type (0-51 + two flag bits)
-// AddrSize-byte value, host-endian OR varint-encoded value
-// AddrSize-byte Go type address OR nothing
-// [n] name, terminated as before
-//
-// The type byte comes first, but 'A' encodes as 0 and 'a' as 26, so that
-// the type itself is only in the low 6 bits. The upper two bits specify
-// the format of the next two fields. If the 0x40 bit is set, the value
-// is encoded as an full-width 4- or 8-byte target-endian word. Otherwise
-// the value is a varint-encoded number. If the 0x80 bit is set, the Go
-// type is present, again as a 4- or 8-byte target-endian word. If not,
-// there is no Go type in this entry. The NUL-terminated name ends the
-// entry.
+// See http://golang.org/s/go12symtab for an overview.
#include "runtime.h"
#include "defs_GOOS_GOARCH.h"
#include "os_GOOS.h"
#include "arch_GOARCH.h"
#include "malloc.h"
+#include "funcdata.h"
-extern byte pclntab[], epclntab[], symtab[], esymtab[];
-
-typedef struct Sym Sym;
-struct Sym
+typedef struct Ftab Ftab;
+struct Ftab
{
- uintptr value;
- byte symtype;
- byte *name;
-// byte *gotype;
+ uintptr entry;
+ uintptr funcoff;
};
-static uintptr mainoffset;
+extern byte pclntab[];
-// A dynamically allocated string containing multiple substrings.
-// Individual strings are slices of hugestring.
-static String hugestring;
-static int32 hugestring_len;
+static Ftab *ftab;
+static uintptr nftab;
+static uint32 *filetab;
+static uint32 nfiletab;
-extern void main·main(void);
+static String end = { (uint8*)"end", 3 };
-static uintptr
-readword(byte **pp, byte *ep)
+void
+runtime·symtabinit(void)
{
- byte *p;
-
- p = *pp;
- if(ep - p < sizeof(void*)) {
- *pp = ep;
- return 0;
+ int32 i, j;
+ Func *f1, *f2;
+
+ // See golang.org/s/go12symtab for header: 0xfffffffb,
+ // two zero bytes, a byte giving the PC quantum,
+ // and a byte giving the pointer width in bytes.
+ if(*(uint32*)pclntab != 0xfffffffb || pclntab[4] != 0 || pclntab[5] != 0 || pclntab[6] != PCQuantum || pclntab[7] != sizeof(void*)) {
+ runtime·printf("runtime: function symbol table header: 0x%x 0x%x\n", *(uint32*)pclntab, *(uint32*)(pclntab+4));
+ runtime·throw("invalid function symbol table\n");
}
- *pp = p + sizeof(void*);
- // Hairy, but only one of these four cases gets compiled.
- if(sizeof(void*) == 8) {
- if(BigEndian) {
- return ((uint64)p[0]<<56) | ((uint64)p[1]<<48) | ((uint64)p[2]<<40) | ((uint64)p[3]<<32) |
- ((uint64)p[4]<<24) | ((uint64)p[5]<<16) | ((uint64)p[6]<<8) | ((uint64)p[7]);
+ nftab = *(uintptr*)(pclntab+8);
+ ftab = (Ftab*)(pclntab+8+sizeof(void*));
+ for(i=0; i<nftab; i++) {
+ // NOTE: ftab[nftab].entry is legal; it is the address beyond the final function.
+ if(ftab[i].entry > ftab[i+1].entry) {
+ f1 = (Func*)(pclntab + ftab[i].funcoff);
+ f2 = (Func*)(pclntab + ftab[i+1].funcoff);
+ runtime·printf("function symbol table not sorted by program counter: %p %s > %p %s", ftab[i].entry, runtime·funcname(f1), ftab[i+1].entry, i+1 == nftab ? "end" : runtime·funcname(f2));
+ for(j=0; j<=i; j++)
+ runtime·printf("\t%p %s\n", ftab[j].entry, runtime·funcname((Func*)(pclntab + ftab[j].funcoff)));
+ runtime·throw("invalid runtime symbol table");
}
- return ((uint64)p[7]<<56) | ((uint64)p[6]<<48) | ((uint64)p[5]<<40) | ((uint64)p[4]<<32) |
- ((uint64)p[3]<<24) | ((uint64)p[2]<<16) | ((uint64)p[1]<<8) | ((uint64)p[0]);
- }
- if(BigEndian) {
- return ((uint32)p[0]<<24) | ((uint32)p[1]<<16) | ((uint32)p[2]<<8) | ((uint32)p[3]);
}
- return ((uint32)p[3]<<24) | ((uint32)p[2]<<16) | ((uint32)p[1]<<8) | ((uint32)p[0]);
+
+ filetab = (uint32*)(pclntab + *(uint32*)&ftab[nftab].funcoff);
+ nfiletab = filetab[0];
}
-// Walk over symtab, calling fn(&s) for each symbol.
-static void
-walksymtab(void (*fn)(Sym*))
+static uint32
+readvarint(byte **pp)
{
- byte *p, *ep, *q;
- Sym s;
- int32 widevalue, havetype, shift;
-
- p = symtab;
- ep = esymtab;
-
- // Table must begin with correct magic number.
- if(ep - p < 8 || p[4] != 0x00 || p[5] != 0x00 || p[6] != 0x00 || p[7] != sizeof(void*))
- return;
- if(BigEndian) {
- if(p[0] != 0xff || p[1] != 0xff || p[2] != 0xff || p[3] != 0xfd)
- return;
- } else {
- if(p[0] != 0xfd || p[1] != 0xff || p[2] != 0xff || p[3] != 0xff)
- return;
- }
- p += 8;
-
- while(p < ep) {
- s.symtype = p[0]&0x3F;
- widevalue = p[0]&0x40;
- havetype = p[0]&0x80;
- if(s.symtype < 26)
- s.symtype += 'A';
- else
- s.symtype += 'a' - 26;
- p++;
-
- // Value, either full-width or varint-encoded.
- if(widevalue) {
- s.value = readword(&p, ep);
- } else {
- s.value = 0;
- shift = 0;
- while(p < ep && (p[0]&0x80) != 0) {
- s.value |= (uintptr)(p[0]&0x7F)<<shift;
- shift += 7;
- p++;
- }
- if(p >= ep)
- break;
- s.value |= (uintptr)p[0]<<shift;
- p++;
- }
-
- // Go type, if present. Ignored but must skip over.
- if(havetype)
- readword(&p, ep);
-
- // Name.
- if(ep - p < 2)
- break;
-
- s.name = p;
- if(s.symtype == 'z' || s.symtype == 'Z') {
- // path reference string - skip first byte,
- // then 2-byte pairs ending at two zeros.
- q = p+1;
- for(;;) {
- if(q+2 > ep)
- return;
- if(q[0] == '\0' && q[1] == '\0')
- break;
- q += 2;
- }
- p = q+2;
- }else{
- q = runtime·mchr(p, '\0', ep);
- if(q == nil)
- break;
- p = q+1;
- }
+ byte *p;
+ uint32 v;
+ int32 shift;
- fn(&s);
+ v = 0;
+ p = *pp;
+ for(shift = 0;; shift += 7) {
+ v |= (*p & 0x7F) << shift;
+ if(!(*p++ & 0x80))
+ break;
}
+ *pp = p;
+ return v;
}
-// Symtab walker; accumulates info about functions.
-
-static Func *func;
-static int32 nfunc;
-
-static byte **fname;
-static int32 nfname;
-
-static uint32 funcinit;
-static Lock funclock;
-static uintptr lastvalue;
-
-static void
-dofunc(Sym *sym)
+void*
+runtime·funcdata(Func *f, int32 i)
{
- Func *f;
-
- switch(sym->symtype) {
- case 't':
- case 'T':
- case 'l':
- case 'L':
- if(runtime·strcmp(sym->name, (byte*)"etext") == 0)
- break;
- if(sym->value < lastvalue) {
- runtime·printf("symbols out of order: %p before %p\n", lastvalue, sym->value);
- runtime·throw("malformed symbol table");
- }
- lastvalue = sym->value;
- if(func == nil) {
- nfunc++;
- break;
- }
- f = &func[nfunc++];
- f->name = runtime·gostringnocopy(sym->name);
- f->entry = sym->value;
- if(sym->symtype == 'L' || sym->symtype == 'l')
- f->frame = -sizeof(uintptr);
- break;
- case 'm':
- if(nfunc <= 0 || func == nil)
- break;
- if(runtime·strcmp(sym->name, (byte*)".frame") == 0)
- func[nfunc-1].frame = sym->value;
- else if(runtime·strcmp(sym->name, (byte*)".locals") == 0)
- func[nfunc-1].locals = sym->value;
- else if(runtime·strcmp(sym->name, (byte*)".args") == 0)
- func[nfunc-1].args = sym->value;
- else {
- runtime·printf("invalid 'm' symbol named '%s'\n", sym->name);
- runtime·throw("mangled symbol table");
- }
- break;
- case 'f':
- if(fname == nil) {
- if(sym->value >= nfname) {
- if(sym->value >= 0x10000) {
- runtime·printf("runtime: invalid symbol file index %p\n", sym->value);
- runtime·throw("mangled symbol table");
- }
- nfname = sym->value+1;
- }
- break;
- }
- fname[sym->value] = sym->name;
- break;
- }
+ byte *p;
+
+ if(i < 0 || i >= f->nfuncdata)
+ return nil;
+ p = (byte*)&f->nfuncdata + 4 + f->npcdata*4;
+ if(sizeof(void*) == 8 && ((uintptr)p & 4))
+ p += 4;
+ return ((void**)p)[i];
}
-// put together the path name for a z entry.
-// the f entries have been accumulated into fname already.
-// returns the length of the path name.
-static int32
-makepath(byte *buf, int32 nbuf, byte *path)
+static bool
+step(byte **pp, uintptr *pc, int32 *value, bool first)
{
- int32 n, len;
- byte *p, *ep, *q;
+ uint32 uvdelta, pcdelta;
+ int32 vdelta;
- if(nbuf <= 0)
+ uvdelta = readvarint(pp);
+ if(uvdelta == 0 && !first)
return 0;
-
- p = buf;
- ep = buf + nbuf;
- *p = '\0';
- for(;;) {
- if(path[0] == 0 && path[1] == 0)
- break;
- n = (path[0]<<8) | path[1];
- path += 2;
- if(n >= nfname)
- break;
- q = fname[n];
- len = runtime·findnull(q);
- if(p+1+len >= ep)
- break;
- if(p > buf && p[-1] != '/')
- *p++ = '/';
- runtime·memmove(p, q, len+1);
- p += len;
- }
- return p - buf;
+ if(uvdelta&1)
+ uvdelta = ~(uvdelta>>1);
+ else
+ uvdelta >>= 1;
+ vdelta = (int32)uvdelta;
+ pcdelta = readvarint(pp) * PCQuantum;
+ *value += vdelta;
+ *pc += pcdelta;
+ return 1;
}
-// appends p to hugestring
-static String
-gostringn(byte *p, int32 l)
+// Return associated data value for targetpc in func f.
+// (Source file is f->src.)
+static int32
+pcvalue(Func *f, int32 off, uintptr targetpc, bool strict)
{
- String s;
+ byte *p;
+ uintptr pc;
+ int32 value;
+
+ enum {
+ debug = 0
+ };
- if(l == 0)
- return runtime·emptystring;
- if(hugestring.str == nil) {
- hugestring_len += l;
- return runtime·emptystring;
+ // The table is a delta-encoded sequence of (value, pc) pairs.
+ // Each pair states the given value is in effect up to pc.
+ // The value deltas are signed, zig-zag encoded.
+ // The pc deltas are unsigned.
+ // The starting value is -1, the starting pc is the function entry.
+ // The table ends at a value delta of 0 except in the first pair.
+ if(off == 0)
+ return -1;
+ p = pclntab + off;
+ pc = f->entry;
+ value = -1;
+
+ if(debug && !runtime·panicking)
+ runtime·printf("pcvalue start f=%s [%p] pc=%p targetpc=%p value=%d tab=%p\n",
+ runtime·funcname(f), f, pc, targetpc, value, p);
+
+ while(step(&p, &pc, &value, pc == f->entry)) {
+ if(debug)
+ runtime·printf("\tvalue=%d until pc=%p\n", value, pc);
+ if(targetpc < pc)
+ return value;
}
- s.str = hugestring.str + hugestring.len;
- s.len = l;
- hugestring.len += s.len;
- runtime·memmove(s.str, p, l);
- return s;
+
+ // If there was a table, it should have covered all program counters.
+ // If not, something is wrong.
+ if(runtime·panicking || !strict)
+ return -1;
+ runtime·printf("runtime: invalid pc-encoded table f=%s pc=%p targetpc=%p tab=%p\n",
+ runtime·funcname(f), pc, targetpc, p);
+ p = (byte*)f + off;
+ pc = f->entry;
+ value = -1;
+
+ while(step(&p, &pc, &value, pc == f->entry))
+ runtime·printf("\tvalue=%d until pc=%p\n", value, pc);
+
+ runtime·throw("invalid runtime symbol table");
+ return -1;
}
-// walk symtab accumulating path names for use by pc/ln table.
-// don't need the full generality of the z entry history stack because
-// there are no includes in go (and only sensible includes in our c);
-// assume code only appear in top-level files.
-static void
-dosrcline(Sym *sym)
+static String unknown = { (uint8*)"?", 1 };
+
+int8*
+runtime·funcname(Func *f)
{
- static byte srcbuf[1000];
- static struct {
- String srcstring;
- int32 aline;
- int32 delta;
- } files[200];
- static int32 incstart;
- static int32 nfunc, nfile, nhist;
- Func *f;
- int32 i, l;
-
- switch(sym->symtype) {
- case 't':
- case 'T':
- if(hugestring.str == nil)
- break;
- if(runtime·strcmp(sym->name, (byte*)"etext") == 0)
- break;
- f = &func[nfunc++];
- // find source file
- for(i = 0; i < nfile - 1; i++) {
- if (files[i+1].aline > f->ln0)
- break;
- }
- f->src = files[i].srcstring;
- f->ln0 -= files[i].delta;
- break;
- case 'z':
- if(sym->value == 1) {
- // entry for main source file for a new object.
- l = makepath(srcbuf, sizeof srcbuf, sym->name+1);
- nhist = 0;
- nfile = 0;
- if(nfile == nelem(files))
- return;
- files[nfile].srcstring = gostringn(srcbuf, l);
- files[nfile].aline = 0;
- files[nfile++].delta = 0;
- } else {
- // push or pop of included file.
- l = makepath(srcbuf, sizeof srcbuf, sym->name+1);
- if(srcbuf[0] != '\0') {
- if(nhist++ == 0)
- incstart = sym->value;
- if(nhist == 0 && nfile < nelem(files)) {
- // new top-level file
- files[nfile].srcstring = gostringn(srcbuf, l);
- files[nfile].aline = sym->value;
- // this is "line 0"
- files[nfile++].delta = sym->value - 1;
- }
- }else{
- if(--nhist == 0)
- files[nfile-1].delta += sym->value - incstart;
- }
- }
- }
+ if(f == nil || f->nameoff == 0)
+ return nil;
+ return (int8*)(pclntab + f->nameoff);
}
-// Interpret pc/ln table, saving the subpiece for each func.
-static void
-splitpcln(void)
+static int32
+funcline(Func *f, uintptr targetpc, String *file, bool strict)
{
int32 line;
- uintptr pc;
- byte *p, *ep;
- Func *f, *ef;
- int32 pcquant;
-
- if(pclntab == epclntab || nfunc == 0)
- return;
-
- switch(thechar) {
- case '5':
- pcquant = 4;
- break;
- default: // 6, 8
- pcquant = 1;
- break;
- }
-
- // pc/ln table bounds
- p = pclntab;
- ep = epclntab;
-
- f = func;
- ef = func + nfunc;
- pc = func[0].entry; // text base
- f->pcln.array = p;
- f->pc0 = pc;
- line = 0;
- for(;;) {
- while(p < ep && *p > 128)
- pc += pcquant * (*p++ - 128);
- // runtime·printf("pc<%p targetpc=%p line=%d\n", pc, targetpc, line);
- if(*p == 0) {
- if(p+5 > ep)
- break;
- // 4 byte add to line
- line += (p[1]<<24) | (p[2]<<16) | (p[3]<<8) | p[4];
- p += 5;
- } else if(*p <= 64)
- line += *p++;
- else
- line -= *p++ - 64;
-
- // pc, line now match.
- // Because the state machine begins at pc==entry and line==0,
- // it can happen - just at the beginning! - that the update may
- // have updated line but left pc alone, to tell us the true line
- // number for pc==entry. In that case, update f->ln0.
- // Having the correct initial line number is important for choosing
- // the correct file in dosrcline above.
- if(f == func && pc == f->pc0) {
- f->pcln.array = p;
- f->pc0 = pc + pcquant;
- f->ln0 = line;
- }
-
- if(f < ef && pc >= (f+1)->entry) {
- f->pcln.len = p - f->pcln.array;
- f->pcln.cap = f->pcln.len;
- do
- f++;
- while(f < ef && pc >= (f+1)->entry);
- f->pcln.array = p;
- // pc0 and ln0 are the starting values for
- // the loop over f->pcln, so pc must be
- // adjusted by the same pcquant update
- // that we're going to do as we continue our loop.
- f->pc0 = pc + pcquant;
- f->ln0 = line;
- }
+ int32 fileno;
- pc += pcquant;
- }
- if(f < ef) {
- f->pcln.len = p - f->pcln.array;
- f->pcln.cap = f->pcln.len;
+ *file = unknown;
+ fileno = pcvalue(f, f->pcfile, targetpc, strict);
+ line = pcvalue(f, f->pcln, targetpc, strict);
+ if(fileno == -1 || line == -1 || fileno >= nfiletab) {
+ // runtime·printf("looking for %p in %S got file=%d line=%d\n", targetpc, *f->name, fileno, line);
+ return 0;
}
+ *file = runtime·gostringnocopy(pclntab + filetab[fileno]);
+ return line;
}
-
-// Return actual file line number for targetpc in func f.
-// (Source file is f->src.)
-// NOTE(rsc): If you edit this function, also edit extern.go:/FileLine
int32
-runtime·funcline(Func *f, uintptr targetpc)
+runtime·funcline(Func *f, uintptr targetpc, String *file)
{
- byte *p, *ep;
- uintptr pc;
- int32 line;
- int32 pcquant;
-
- enum {
- debug = 0
- };
+ return funcline(f, targetpc, file, true);
+}
- switch(thechar) {
- case '5':
- pcquant = 4;
- break;
- default: // 6, 8
- pcquant = 1;
- break;
- }
+int32
+runtime·funcspdelta(Func *f, uintptr targetpc)
+{
+ int32 x;
+
+ x = pcvalue(f, f->pcsp, targetpc, true);
+ if(x&(sizeof(void*)-1))
+ runtime·printf("invalid spdelta %d %d\n", f->pcsp, x);
+ return x;
+}
- p = f->pcln.array;
- ep = p + f->pcln.len;
- pc = f->pc0;
- line = f->ln0;
- if(debug && !runtime·panicking)
- runtime·printf("funcline start pc=%p targetpc=%p line=%d tab=%p+%d\n",
- pc, targetpc, line, p, (int32)f->pcln.len);
- for(;;) {
- // Table is a sequence of updates.
-
- // Each update says first how to adjust the pc,
- // in possibly multiple instructions...
- while(p < ep && *p > 128)
- pc += pcquant * (*p++ - 128);
-
- if(debug && !runtime·panicking)
- runtime·printf("pc<%p targetpc=%p line=%d\n", pc, targetpc, line);
-
- // If the pc has advanced too far or we're out of data,
- // stop and the last known line number.
- if(pc > targetpc || p >= ep)
- break;
+int32
+runtime·pcdatavalue(Func *f, int32 table, uintptr targetpc)
+{
+ if(table < 0 || table >= f->npcdata)
+ return -1;
+ return pcvalue(f, (&f->nfuncdata)[1+table], targetpc, true);
+}
- // ... and then how to adjust the line number,
- // in a single instruction.
- if(*p == 0) {
- if(p+5 > ep)
- break;
- line += (p[1]<<24) | (p[2]<<16) | (p[3]<<8) | p[4];
- p += 5;
- } else if(*p <= 64)
- line += *p++;
- else
- line -= *p++ - 64;
- // Now pc, line pair is consistent.
- if(debug && !runtime·panicking)
- runtime·printf("pc=%p targetpc=%p line=%d\n", pc, targetpc, line);
-
- // PC increments implicitly on each iteration.
- pc += pcquant;
- }
- return line;
+int32
+runtime·funcarglen(Func *f, uintptr targetpc)
+{
+ if(targetpc == f->entry)
+ return 0;
+ return runtime·pcdatavalue(f, PCDATA_ArgSize, targetpc-PCQuantum);
}
void
runtime·funcline_go(Func *f, uintptr targetpc, String retfile, intgo retline)
{
- retfile = f->src;
- retline = runtime·funcline(f, targetpc);
- FLUSH(&retfile);
+ // Pass strict=false here, because anyone can call this function,
+ // and they might just be wrong about targetpc belonging to f.
+ retline = funcline(f, targetpc, &retfile, false);
FLUSH(&retline);
}
-static void
-buildfuncs(void)
+void
+runtime·funcname_go(Func *f, String ret)
+{
+ ret = runtime·gostringnocopy((uint8*)runtime·funcname(f));
+ FLUSH(&ret);
+}
+
+void
+runtime·funcentry_go(Func *f, uintptr ret)
{
- extern byte etext[];
-
- if(func != nil)
- return;
-
- // Memory profiling uses this code;
- // can deadlock if the profiler ends
- // up back here.
- m->nomemprof++;
-
- // count funcs, fnames
- nfunc = 0;
- nfname = 0;
- lastvalue = 0;
- walksymtab(dofunc);
-
- // Initialize tables.
- // Can use FlagNoPointers - all pointers either point into sections of the executable
- // or point into hugestring.
- func = runtime·mallocgc((nfunc+1)*sizeof func[0], FlagNoPointers, 0, 1);
- func[nfunc].entry = (uint64)etext;
- fname = runtime·mallocgc(nfname*sizeof fname[0], FlagNoPointers, 0, 1);
- nfunc = 0;
- lastvalue = 0;
- walksymtab(dofunc);
-
- // split pc/ln table by func
- splitpcln();
-
- // record src file and line info for each func
- walksymtab(dosrcline); // pass 1: determine hugestring_len
- hugestring.str = runtime·mallocgc(hugestring_len, FlagNoPointers, 0, 0);
- hugestring.len = 0;
- walksymtab(dosrcline); // pass 2: fill and use hugestring
-
- if(hugestring.len != hugestring_len)
- runtime·throw("buildfunc: problem in initialization procedure");
-
- m->nomemprof--;
+ ret = f->entry;
+ FLUSH(&ret);
}
Func*
runtime·findfunc(uintptr addr)
{
- Func *f;
+ Ftab *f;
int32 nf, n;
- // Use atomic double-checked locking,
- // because when called from pprof signal
- // handler, findfunc must run without
- // grabbing any locks.
- // (Before enabling the signal handler,
- // SetCPUProfileRate calls findfunc to trigger
- // the initialization outside the handler.)
- // Avoid deadlock on fault during malloc
- // by not calling buildfuncs if we're already in malloc.
- if(!m->mallocing && !m->gcing) {
- if(runtime·atomicload(&funcinit) == 0) {
- runtime·lock(&funclock);
- if(funcinit == 0) {
- buildfuncs();
- runtime·atomicstore(&funcinit, 1);
- }
- runtime·unlock(&funclock);
- }
- }
-
- if(nfunc == 0)
+ if(nftab == 0)
return nil;
- if(addr < func[0].entry || addr >= func[nfunc].entry)
+ if(addr < ftab[0].entry || addr >= ftab[nftab].entry)
return nil;
// binary search to find func with entry <= addr.
- f = func;
- nf = nfunc;
+ f = ftab;
+ nf = nftab;
while(nf > 0) {
n = nf/2;
if(f[n].entry <= addr && addr < f[n+1].entry)
- return &f[n];
+ return (Func*)(pclntab + f[n].funcoff);
else if(addr < f[n].entry)
nf = n;
else {
@@ -663,13 +312,22 @@ contains(String s, int8 *p)
}
bool
-runtime·showframe(Func *f, bool current)
+runtime·showframe(Func *f, G *gp)
{
static int32 traceback = -1;
+ String name;
- if(current && m->throwing > 0)
+ if(m->throwing > 0 && gp != nil && (gp == m->curg || gp == m->caughtsig))
return 1;
if(traceback < 0)
traceback = runtime·gotraceback(nil);
- return traceback > 1 || f != nil && contains(f->name, ".") && !hasprefix(f->name, "runtime.");
+ name = runtime·gostringnocopy((uint8*)runtime·funcname(f));
+
+ // Special case: always show runtime.panic frame, so that we can
+ // see where a panic started in the middle of a stack trace.
+ // See golang.org/issue/5832.
+ if(name.len == 7+1+5 && hasprefix(name, "runtime.panic"))
+ return 1;
+
+ return traceback > 1 || f != nil && contains(name, ".") && !hasprefix(name, "runtime.");
}
diff --git a/src/pkg/runtime/sys_arm.c b/src/pkg/runtime/sys_arm.c
new file mode 100644
index 000000000..a65560e5b
--- /dev/null
+++ b/src/pkg/runtime/sys_arm.c
@@ -0,0 +1,35 @@
+// Copyright 2013 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 "runtime.h"
+
+// adjust Gobuf as if it executed a call to fn with context ctxt
+// and then did an immediate Gosave.
+void
+runtime·gostartcall(Gobuf *gobuf, void (*fn)(void), void *ctxt)
+{
+ if(gobuf->lr != 0)
+ runtime·throw("invalid use of gostartcall");
+ gobuf->lr = gobuf->pc;
+ gobuf->pc = (uintptr)fn;
+ gobuf->ctxt = ctxt;
+}
+
+// Called to rewind context saved during morestack back to beginning of function.
+// To help us, the linker emits a jmp back to the beginning right after the
+// call to morestack. We just have to decode and apply that jump.
+void
+runtime·rewindmorestack(Gobuf *gobuf)
+{
+ uint32 inst;
+
+ inst = *(uint32*)gobuf->pc;
+ if((gobuf->pc&3) == 0 && (inst>>24) == 0x9a) {
+ //runtime·printf("runtime: rewind pc=%p to pc=%p\n", gobuf->pc, gobuf->pc + ((int32)(inst<<8)>>6) + 8);
+ gobuf->pc += ((int32)(inst<<8)>>6) + 8;
+ return;
+ }
+ runtime·printf("runtime: pc=%p %x\n", gobuf->pc, inst);
+ runtime·throw("runtime: misuse of rewindmorestack");
+}
diff --git a/src/pkg/runtime/sys_darwin_386.s b/src/pkg/runtime/sys_darwin_386.s
index 59bb9d80d..c2a259e5b 100644
--- a/src/pkg/runtime/sys_darwin_386.s
+++ b/src/pkg/runtime/sys_darwin_386.s
@@ -7,9 +7,10 @@
// or /usr/include/sys/syscall.h (on a Mac) for system call numbers.
#include "zasm_GOOS_GOARCH.h"
+#include "../../cmd/ld/textflag.h"
// Exit the entire program (like C exit)
-TEXT runtime·exit(SB),7,$0
+TEXT runtime·exit(SB),NOSPLIT,$0
MOVL $1, AX
INT $0x80
MOVL $0xf1, 0xf1 // crash
@@ -17,34 +18,34 @@ TEXT runtime·exit(SB),7,$0
// Exit this OS thread (like pthread_exit, which eventually
// calls __bsdthread_terminate).
-TEXT runtime·exit1(SB),7,$0
+TEXT runtime·exit1(SB),NOSPLIT,$0
MOVL $361, AX
INT $0x80
JAE 2(PC)
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·open(SB),7,$0
+TEXT runtime·open(SB),NOSPLIT,$0
MOVL $5, AX
INT $0x80
RET
-TEXT runtime·close(SB),7,$0
+TEXT runtime·close(SB),NOSPLIT,$0
MOVL $6, AX
INT $0x80
RET
-TEXT runtime·read(SB),7,$0
+TEXT runtime·read(SB),NOSPLIT,$0
MOVL $3, AX
INT $0x80
RET
-TEXT runtime·write(SB),7,$0
+TEXT runtime·write(SB),NOSPLIT,$0
MOVL $4, AX
INT $0x80
RET
-TEXT runtime·raise(SB),7,$16
+TEXT runtime·raise(SB),NOSPLIT,$16
MOVL $20, AX // getpid
INT $0x80
MOVL AX, 4(SP) // pid
@@ -55,25 +56,25 @@ TEXT runtime·raise(SB),7,$16
INT $0x80
RET
-TEXT runtime·mmap(SB),7,$0
+TEXT runtime·mmap(SB),NOSPLIT,$0
MOVL $197, AX
INT $0x80
RET
-TEXT runtime·madvise(SB),7,$0
+TEXT runtime·madvise(SB),NOSPLIT,$0
MOVL $75, AX
INT $0x80
// ignore failure - maybe pages are locked
RET
-TEXT runtime·munmap(SB),7,$0
+TEXT runtime·munmap(SB),NOSPLIT,$0
MOVL $73, AX
INT $0x80
JAE 2(PC)
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·setitimer(SB),7,$0
+TEXT runtime·setitimer(SB),NOSPLIT,$0
MOVL $83, AX
INT $0x80
RET
@@ -94,7 +95,7 @@ TEXT runtime·setitimer(SB),7,$0
// 64-bit unix nanoseconds returned in DX:AX.
// I'd much rather write this in C but we need
// assembly for the 96-bit multiply and RDTSC.
-TEXT runtime·now(SB),7,$40
+TEXT runtime·now(SB),NOSPLIT,$40
MOVL $0xffff0000, BP /* comm page base */
// Test for slow CPU. If so, the math is completely
@@ -192,7 +193,7 @@ systime:
RET
// func now() (sec int64, nsec int32)
-TEXT time·now(SB),7,$0
+TEXT time·now(SB),NOSPLIT,$0
CALL runtime·now(SB)
MOVL $1000000000, CX
DIVL CX
@@ -203,21 +204,21 @@ TEXT time·now(SB),7,$0
// int64 nanotime(void) so really
// void nanotime(int64 *nsec)
-TEXT runtime·nanotime(SB),7,$0
+TEXT runtime·nanotime(SB),NOSPLIT,$0
CALL runtime·now(SB)
MOVL ret+0(FP), DI
MOVL AX, 0(DI)
MOVL DX, 4(DI)
RET
-TEXT runtime·sigprocmask(SB),7,$0
+TEXT runtime·sigprocmask(SB),NOSPLIT,$0
MOVL $329, AX // pthread_sigmask (on OS X, sigprocmask==entire process)
INT $0x80
JAE 2(PC)
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·sigaction(SB),7,$0
+TEXT runtime·sigaction(SB),NOSPLIT,$0
MOVL $46, AX
INT $0x80
JAE 2(PC)
@@ -232,17 +233,18 @@ TEXT runtime·sigaction(SB),7,$0
// 12(FP) siginfo style
// 16(FP) siginfo
// 20(FP) context
-TEXT runtime·sigtramp(SB),7,$40
+TEXT runtime·sigtramp(SB),NOSPLIT,$40
get_tls(CX)
// check that m exists
MOVL m(CX), BP
CMPL BP, $0
- JNE 5(PC)
+ JNE 6(PC)
MOVL sig+8(FP), BX
MOVL BX, 0(SP)
- CALL runtime·badsignal(SB)
- RET
+ MOVL $runtime·badsignal(SB), AX
+ CALL AX
+ JMP sigtramp_ret
// save g
MOVL g(CX), DI
@@ -269,6 +271,7 @@ TEXT runtime·sigtramp(SB),7,$40
MOVL 20(SP), DI
MOVL DI, g(CX)
+sigtramp_ret:
// call sigreturn
MOVL context+16(FP), CX
MOVL style+4(FP), BX
@@ -280,14 +283,14 @@ TEXT runtime·sigtramp(SB),7,$40
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·sigaltstack(SB),7,$0
+TEXT runtime·sigaltstack(SB),NOSPLIT,$0
MOVL $53, AX
INT $0x80
JAE 2(PC)
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·usleep(SB),7,$32
+TEXT runtime·usleep(SB),NOSPLIT,$32
MOVL $0, DX
MOVL usec+0(FP), AX
MOVL $1000000, CX
@@ -309,7 +312,7 @@ TEXT runtime·usleep(SB),7,$32
// void bsdthread_create(void *stk, M *mp, G *gp, void (*fn)(void))
// System call args are: func arg stack pthread flags.
-TEXT runtime·bsdthread_create(SB),7,$32
+TEXT runtime·bsdthread_create(SB),NOSPLIT,$32
MOVL $360, AX
// 0(SP) is where the caller PC would be; kernel skips it
MOVL func+12(FP), BX
@@ -338,7 +341,7 @@ TEXT runtime·bsdthread_create(SB),7,$32
// DI = stack top
// SI = flags (= 0x1000000)
// SP = stack - C_32_STK_ALIGN
-TEXT runtime·bsdthread_start(SB),7,$0
+TEXT runtime·bsdthread_start(SB),NOSPLIT,$0
// set up ldt 7+id to point at m->tls.
// m->tls is at m+40. newosproc left
// the m->id in tls[0].
@@ -369,7 +372,7 @@ TEXT runtime·bsdthread_start(SB),7,$0
// void bsdthread_register(void)
// registers callbacks for threadstart (see bsdthread_create above
// and wqthread and pthsize (not used). returns 0 on success.
-TEXT runtime·bsdthread_register(SB),7,$40
+TEXT runtime·bsdthread_register(SB),NOSPLIT,$40
MOVL $366, AX
// 0(SP) is where kernel expects caller PC; ignored
MOVL $runtime·bsdthread_start(SB), 4(SP) // threadstart
@@ -396,23 +399,23 @@ TEXT runtime·bsdthread_register(SB),7,$40
// in the high 16 bits that seems to be the
// argument count in bytes but is not always.
// INT $0x80 works fine for those.
-TEXT runtime·sysenter(SB),7,$0
+TEXT runtime·sysenter(SB),NOSPLIT,$0
POPL DX
MOVL SP, CX
BYTE $0x0F; BYTE $0x34; // SYSENTER
// returns to DX with SP set to CX
-TEXT runtime·mach_msg_trap(SB),7,$0
+TEXT runtime·mach_msg_trap(SB),NOSPLIT,$0
MOVL $-31, AX
CALL runtime·sysenter(SB)
RET
-TEXT runtime·mach_reply_port(SB),7,$0
+TEXT runtime·mach_reply_port(SB),NOSPLIT,$0
MOVL $-26, AX
CALL runtime·sysenter(SB)
RET
-TEXT runtime·mach_task_self(SB),7,$0
+TEXT runtime·mach_task_self(SB),NOSPLIT,$0
MOVL $-28, AX
CALL runtime·sysenter(SB)
RET
@@ -421,32 +424,32 @@ TEXT runtime·mach_task_self(SB),7,$0
// instead of requiring the use of RPC.
// uint32 mach_semaphore_wait(uint32)
-TEXT runtime·mach_semaphore_wait(SB),7,$0
+TEXT runtime·mach_semaphore_wait(SB),NOSPLIT,$0
MOVL $-36, AX
CALL runtime·sysenter(SB)
RET
// uint32 mach_semaphore_timedwait(uint32, uint32, uint32)
-TEXT runtime·mach_semaphore_timedwait(SB),7,$0
+TEXT runtime·mach_semaphore_timedwait(SB),NOSPLIT,$0
MOVL $-38, AX
CALL runtime·sysenter(SB)
RET
// uint32 mach_semaphore_signal(uint32)
-TEXT runtime·mach_semaphore_signal(SB),7,$0
+TEXT runtime·mach_semaphore_signal(SB),NOSPLIT,$0
MOVL $-33, AX
CALL runtime·sysenter(SB)
RET
// uint32 mach_semaphore_signal_all(uint32)
-TEXT runtime·mach_semaphore_signal_all(SB),7,$0
+TEXT runtime·mach_semaphore_signal_all(SB),NOSPLIT,$0
MOVL $-34, AX
CALL runtime·sysenter(SB)
RET
// setldt(int entry, int address, int limit)
// entry and limit are ignored.
-TEXT runtime·setldt(SB),7,$32
+TEXT runtime·setldt(SB),NOSPLIT,$32
MOVL address+4(FP), BX // aka base
/*
@@ -481,7 +484,7 @@ TEXT runtime·setldt(SB),7,$32
MOVW GS, AX
RET
-TEXT runtime·sysctl(SB),7,$0
+TEXT runtime·sysctl(SB),NOSPLIT,$0
MOVL $202, AX
INT $0x80
JAE 3(PC)
@@ -491,7 +494,7 @@ TEXT runtime·sysctl(SB),7,$0
RET
// int32 runtime·kqueue(void);
-TEXT runtime·kqueue(SB),7,$0
+TEXT runtime·kqueue(SB),NOSPLIT,$0
MOVL $362, AX
INT $0x80
JAE 2(PC)
@@ -499,7 +502,7 @@ TEXT runtime·kqueue(SB),7,$0
RET
// int32 runtime·kevent(int kq, Kevent *changelist, int nchanges, Kevent *eventlist, int nevents, Timespec *timeout);
-TEXT runtime·kevent(SB),7,$0
+TEXT runtime·kevent(SB),NOSPLIT,$0
MOVL $363, AX
INT $0x80
JAE 2(PC)
@@ -507,7 +510,7 @@ TEXT runtime·kevent(SB),7,$0
RET
// int32 runtime·closeonexec(int32 fd);
-TEXT runtime·closeonexec(SB),7,$32
+TEXT runtime·closeonexec(SB),NOSPLIT,$32
MOVL $92, AX // fcntl
// 0(SP) is where the caller PC would be; kernel skips it
MOVL fd+0(FP), BX
diff --git a/src/pkg/runtime/sys_darwin_amd64.s b/src/pkg/runtime/sys_darwin_amd64.s
index b324a0424..a0c81b5d2 100644
--- a/src/pkg/runtime/sys_darwin_amd64.s
+++ b/src/pkg/runtime/sys_darwin_amd64.s
@@ -12,9 +12,10 @@
//
#include "zasm_GOOS_GOARCH.h"
+#include "../../cmd/ld/textflag.h"
// Exit the entire program (like C exit)
-TEXT runtime·exit(SB),7,$0
+TEXT runtime·exit(SB),NOSPLIT,$0
MOVL 8(SP), DI // arg 1 exit status
MOVL $(0x2000000+1), AX // syscall entry
SYSCALL
@@ -23,14 +24,14 @@ TEXT runtime·exit(SB),7,$0
// Exit this OS thread (like pthread_exit, which eventually
// calls __bsdthread_terminate).
-TEXT runtime·exit1(SB),7,$0
+TEXT runtime·exit1(SB),NOSPLIT,$0
MOVL 8(SP), DI // arg 1 exit status
MOVL $(0x2000000+361), AX // syscall entry
SYSCALL
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·open(SB),7,$0
+TEXT runtime·open(SB),NOSPLIT,$0
MOVQ 8(SP), DI // arg 1 pathname
MOVL 16(SP), SI // arg 2 flags
MOVL 20(SP), DX // arg 3 mode
@@ -38,13 +39,13 @@ TEXT runtime·open(SB),7,$0
SYSCALL
RET
-TEXT runtime·close(SB),7,$0
+TEXT runtime·close(SB),NOSPLIT,$0
MOVL 8(SP), DI // arg 1 fd
MOVL $(0x2000000+6), AX // syscall entry
SYSCALL
RET
-TEXT runtime·read(SB),7,$0
+TEXT runtime·read(SB),NOSPLIT,$0
MOVL 8(SP), DI // arg 1 fd
MOVQ 16(SP), SI // arg 2 buf
MOVL 24(SP), DX // arg 3 count
@@ -52,7 +53,7 @@ TEXT runtime·read(SB),7,$0
SYSCALL
RET
-TEXT runtime·write(SB),7,$0
+TEXT runtime·write(SB),NOSPLIT,$0
MOVL 8(SP), DI // arg 1 fd
MOVQ 16(SP), SI // arg 2 buf
MOVL 24(SP), DX // arg 3 count
@@ -60,7 +61,7 @@ TEXT runtime·write(SB),7,$0
SYSCALL
RET
-TEXT runtime·raise(SB),7,$24
+TEXT runtime·raise(SB),NOSPLIT,$24
MOVL $(0x2000000+20), AX // getpid
SYSCALL
MOVQ AX, DI // arg 1 - pid
@@ -70,7 +71,7 @@ TEXT runtime·raise(SB),7,$24
SYSCALL
RET
-TEXT runtime·setitimer(SB), 7, $0
+TEXT runtime·setitimer(SB), NOSPLIT, $0
MOVL 8(SP), DI
MOVQ 16(SP), SI
MOVQ 24(SP), DX
@@ -78,7 +79,7 @@ TEXT runtime·setitimer(SB), 7, $0
SYSCALL
RET
-TEXT runtime·madvise(SB), 7, $0
+TEXT runtime·madvise(SB), NOSPLIT, $0
MOVQ 8(SP), DI // arg 1 addr
MOVQ 16(SP), SI // arg 2 len
MOVL 24(SP), DX // arg 3 advice
@@ -99,7 +100,7 @@ TEXT runtime·madvise(SB), 7, $0
#define gtod_sec_base 0x78
// int64 nanotime(void)
-TEXT runtime·nanotime(SB), 7, $32
+TEXT runtime·nanotime(SB), NOSPLIT, $32
MOVQ $0x7fffffe00000, BP /* comm page base */
// Loop trying to take a consistent snapshot
// of the time parameters.
@@ -149,7 +150,7 @@ systime:
RET
// func now() (sec int64, nsec int32)
-TEXT time·now(SB),7,$0
+TEXT time·now(SB),NOSPLIT,$0
CALL runtime·nanotime(SB)
// generated code for
@@ -167,7 +168,7 @@ TEXT time·now(SB),7,$0
MOVL CX, nsec+8(FP)
RET
-TEXT runtime·sigprocmask(SB),7,$0
+TEXT runtime·sigprocmask(SB),NOSPLIT,$0
MOVL 8(SP), DI
MOVQ 16(SP), SI
MOVQ 24(SP), DX
@@ -177,7 +178,7 @@ TEXT runtime·sigprocmask(SB),7,$0
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·sigaction(SB),7,$0
+TEXT runtime·sigaction(SB),NOSPLIT,$0
MOVL 8(SP), DI // arg 1 sig
MOVQ 16(SP), SI // arg 2 act
MOVQ 24(SP), DX // arg 3 oact
@@ -189,16 +190,20 @@ TEXT runtime·sigaction(SB),7,$0
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·sigtramp(SB),7,$64
+TEXT runtime·sigtramp(SB),NOSPLIT,$64
get_tls(BX)
+ MOVQ R8, 32(SP) // save ucontext
+ MOVQ SI, 40(SP) // save infostyle
+
// check that m exists
MOVQ m(BX), BP
CMPQ BP, $0
- JNE 4(PC)
+ JNE 5(PC)
MOVL DX, 0(SP)
- CALL runtime·badsignal(SB)
- RET
+ MOVQ $runtime·badsignal(SB), AX
+ CALL AX
+ JMP sigtramp_ret
// save g
MOVQ g(BX), R10
@@ -213,8 +218,6 @@ TEXT runtime·sigtramp(SB),7,$64
MOVQ R8, 16(SP)
MOVQ R10, 24(SP)
- MOVQ R8, 32(SP) // save ucontext
- MOVQ SI, 40(SP) // save infostyle
CALL DI
// restore g
@@ -222,6 +225,7 @@ TEXT runtime·sigtramp(SB),7,$64
MOVQ 48(SP), R10
MOVQ R10, g(BX)
+sigtramp_ret:
// call sigreturn
MOVL $(0x2000000+184), AX // sigreturn(ucontext, infostyle)
MOVQ 32(SP), DI // saved ucontext
@@ -229,7 +233,7 @@ TEXT runtime·sigtramp(SB),7,$64
SYSCALL
INT $3 // not reached
-TEXT runtime·mmap(SB),7,$0
+TEXT runtime·mmap(SB),NOSPLIT,$0
MOVQ 8(SP), DI // arg 1 addr
MOVQ 16(SP), SI // arg 2 len
MOVL 24(SP), DX // arg 3 prot
@@ -240,7 +244,7 @@ TEXT runtime·mmap(SB),7,$0
SYSCALL
RET
-TEXT runtime·munmap(SB),7,$0
+TEXT runtime·munmap(SB),NOSPLIT,$0
MOVQ 8(SP), DI // arg 1 addr
MOVQ 16(SP), SI // arg 2 len
MOVL $(0x2000000+73), AX // syscall entry
@@ -249,7 +253,7 @@ TEXT runtime·munmap(SB),7,$0
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·sigaltstack(SB),7,$0
+TEXT runtime·sigaltstack(SB),NOSPLIT,$0
MOVQ new+8(SP), DI
MOVQ old+16(SP), SI
MOVQ $(0x2000000+53), AX
@@ -258,7 +262,7 @@ TEXT runtime·sigaltstack(SB),7,$0
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·usleep(SB),7,$16
+TEXT runtime·usleep(SB),NOSPLIT,$16
MOVL $0, DX
MOVL usec+0(FP), AX
MOVL $1000000, CX
@@ -277,7 +281,7 @@ TEXT runtime·usleep(SB),7,$16
RET
// void bsdthread_create(void *stk, M *mp, G *gp, void (*fn)(void))
-TEXT runtime·bsdthread_create(SB),7,$0
+TEXT runtime·bsdthread_create(SB),NOSPLIT,$0
// Set up arguments to bsdthread_create system call.
// The ones in quotes pass through to the thread callback
// uninterpreted, so we can put whatever we want there.
@@ -305,7 +309,7 @@ TEXT runtime·bsdthread_create(SB),7,$0
// R8 = stack
// R9 = flags (= 0)
// SP = stack - C_64_REDZONE_LEN (= stack - 128)
-TEXT runtime·bsdthread_start(SB),7,$0
+TEXT runtime·bsdthread_start(SB),NOSPLIT,$0
MOVQ R8, SP // empirically, SP is very wrong but R8 is right
PUSHQ DX
@@ -333,7 +337,7 @@ TEXT runtime·bsdthread_start(SB),7,$0
// void bsdthread_register(void)
// registers callbacks for threadstart (see bsdthread_create above
// and wqthread and pthsize (not used). returns 0 on success.
-TEXT runtime·bsdthread_register(SB),7,$0
+TEXT runtime·bsdthread_register(SB),NOSPLIT,$0
MOVQ $runtime·bsdthread_start(SB), DI // threadstart
MOVQ $0, SI // wqthread, not used by us
MOVQ $0, DX // pthsize, not used by us
@@ -351,7 +355,7 @@ TEXT runtime·bsdthread_register(SB),7,$0
// Mach system calls use 0x1000000 instead of the BSD's 0x2000000.
// uint32 mach_msg_trap(void*, uint32, uint32, uint32, uint32, uint32, uint32)
-TEXT runtime·mach_msg_trap(SB),7,$0
+TEXT runtime·mach_msg_trap(SB),NOSPLIT,$0
MOVQ 8(SP), DI
MOVL 16(SP), SI
MOVL 20(SP), DX
@@ -365,17 +369,17 @@ TEXT runtime·mach_msg_trap(SB),7,$0
POPQ R11
RET
-TEXT runtime·mach_task_self(SB),7,$0
+TEXT runtime·mach_task_self(SB),NOSPLIT,$0
MOVL $(0x1000000+28), AX // task_self_trap
SYSCALL
RET
-TEXT runtime·mach_thread_self(SB),7,$0
+TEXT runtime·mach_thread_self(SB),NOSPLIT,$0
MOVL $(0x1000000+27), AX // thread_self_trap
SYSCALL
RET
-TEXT runtime·mach_reply_port(SB),7,$0
+TEXT runtime·mach_reply_port(SB),NOSPLIT,$0
MOVL $(0x1000000+26), AX // mach_reply_port
SYSCALL
RET
@@ -384,14 +388,14 @@ TEXT runtime·mach_reply_port(SB),7,$0
// instead of requiring the use of RPC.
// uint32 mach_semaphore_wait(uint32)
-TEXT runtime·mach_semaphore_wait(SB),7,$0
+TEXT runtime·mach_semaphore_wait(SB),NOSPLIT,$0
MOVL 8(SP), DI
MOVL $(0x1000000+36), AX // semaphore_wait_trap
SYSCALL
RET
// uint32 mach_semaphore_timedwait(uint32, uint32, uint32)
-TEXT runtime·mach_semaphore_timedwait(SB),7,$0
+TEXT runtime·mach_semaphore_timedwait(SB),NOSPLIT,$0
MOVL 8(SP), DI
MOVL 12(SP), SI
MOVL 16(SP), DX
@@ -400,21 +404,21 @@ TEXT runtime·mach_semaphore_timedwait(SB),7,$0
RET
// uint32 mach_semaphore_signal(uint32)
-TEXT runtime·mach_semaphore_signal(SB),7,$0
+TEXT runtime·mach_semaphore_signal(SB),NOSPLIT,$0
MOVL 8(SP), DI
MOVL $(0x1000000+33), AX // semaphore_signal_trap
SYSCALL
RET
// uint32 mach_semaphore_signal_all(uint32)
-TEXT runtime·mach_semaphore_signal_all(SB),7,$0
+TEXT runtime·mach_semaphore_signal_all(SB),NOSPLIT,$0
MOVL 8(SP), DI
MOVL $(0x1000000+34), AX // semaphore_signal_all_trap
SYSCALL
RET
// set tls base to DI
-TEXT runtime·settls(SB),7,$32
+TEXT runtime·settls(SB),NOSPLIT,$32
/*
* Same as in sys_darwin_386.s:/ugliness, different constant.
* See cgo/gcc_darwin_amd64.c for the derivation
@@ -426,7 +430,7 @@ TEXT runtime·settls(SB),7,$32
SYSCALL
RET
-TEXT runtime·sysctl(SB),7,$0
+TEXT runtime·sysctl(SB),NOSPLIT,$0
MOVQ 8(SP), DI
MOVL 16(SP), SI
MOVQ 24(SP), DX
@@ -442,7 +446,7 @@ TEXT runtime·sysctl(SB),7,$0
RET
// int32 runtime·kqueue(void);
-TEXT runtime·kqueue(SB),7,$0
+TEXT runtime·kqueue(SB),NOSPLIT,$0
MOVQ $0, DI
MOVQ $0, SI
MOVQ $0, DX
@@ -453,7 +457,7 @@ TEXT runtime·kqueue(SB),7,$0
RET
// int32 runtime·kevent(int kq, Kevent *changelist, int nchanges, Kevent *eventlist, int nevents, Timespec *timeout);
-TEXT runtime·kevent(SB),7,$0
+TEXT runtime·kevent(SB),NOSPLIT,$0
MOVL 8(SP), DI
MOVQ 16(SP), SI
MOVL 24(SP), DX
@@ -467,7 +471,7 @@ TEXT runtime·kevent(SB),7,$0
RET
// void runtime·closeonexec(int32 fd);
-TEXT runtime·closeonexec(SB),7,$0
+TEXT runtime·closeonexec(SB),NOSPLIT,$0
MOVL 8(SP), DI // fd
MOVQ $2, SI // F_SETFD
MOVQ $1, DX // FD_CLOEXEC
diff --git a/src/pkg/runtime/sys_dragonfly_386.s b/src/pkg/runtime/sys_dragonfly_386.s
new file mode 100644
index 000000000..9085ded6f
--- /dev/null
+++ b/src/pkg/runtime/sys_dragonfly_386.s
@@ -0,0 +1,369 @@
+// 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 calls and other sys.stuff for 386, FreeBSD
+// /usr/src/sys/kern/syscalls.master for syscall numbers.
+//
+
+#include "zasm_GOOS_GOARCH.h"
+#include "../../cmd/ld/textflag.h"
+
+TEXT runtime·sys_umtx_sleep(SB),NOSPLIT,$-4
+ MOVL $469, AX // umtx_sleep
+ INT $0x80
+ JAE 2(PC)
+ NEGL AX
+ RET
+
+TEXT runtime·sys_umtx_wakeup(SB),NOSPLIT,$-4
+ MOVL $470, AX // umtx_wakeup
+ INT $0x80
+ JAE 2(PC)
+ NEGL AX
+ RET
+
+TEXT runtime·lwp_create(SB),NOSPLIT,$-4
+ MOVL $495, AX // lwp_create
+ INT $0x80
+ RET
+
+TEXT runtime·lwp_start(SB),NOSPLIT,$0
+
+ // Set GS to point at m->tls.
+ MOVL mm+0(FP), BX
+ MOVL m_g0(BX), DX
+ LEAL m_tls(BX), BP
+ PUSHAL
+ PUSHL BP
+ CALL runtime·settls(SB)
+ POPL AX
+ POPAL
+
+ // Now segment is established. Initialize m, g.
+ get_tls(CX)
+ MOVL BX, m(CX)
+ MOVL DX, g(CX)
+
+ CALL runtime·stackcheck(SB) // smashes AX, CX
+ MOVL 0(DX), DX // paranoia; check they are not nil
+ MOVL 0(BX), BX
+
+ // More paranoia; check that stack splitting code works.
+ PUSHAL
+ CALL runtime·emptyfunc(SB)
+ POPAL
+
+ CALL runtime·mstart(SB)
+
+ CALL runtime·exit1(SB)
+ MOVL $0x1234, 0x1005
+ RET
+
+// Exit the entire program (like C exit)
+TEXT runtime·exit(SB),NOSPLIT,$-4
+ MOVL $1, AX
+ INT $0x80
+ MOVL $0xf1, 0xf1 // crash
+ RET
+
+TEXT runtime·exit1(SB),NOSPLIT,$16
+ MOVL $0, 0(SP) // syscall gap
+ MOVL $0x10000, 4(SP) // arg 1 - how (EXTEXIT_LWP)
+ MOVL $0, 8(SP) // arg 2 - status
+ MOVL $0, 12(SP) // arg 3 - addr
+ MOVL $494, AX
+ INT $0x80
+ JAE 2(PC)
+ MOVL $0xf1, 0xf1 // crash
+ RET
+
+TEXT runtime·open(SB),NOSPLIT,$-4
+ MOVL $5, AX
+ INT $0x80
+ RET
+
+TEXT runtime·close(SB),NOSPLIT,$-4
+ MOVL $6, AX
+ INT $0x80
+ RET
+
+TEXT runtime·read(SB),NOSPLIT,$-4
+ MOVL $3, AX
+ INT $0x80
+ RET
+
+TEXT runtime·write(SB),NOSPLIT,$-4
+ MOVL $4, AX
+ INT $0x80
+ RET
+
+TEXT runtime·getrlimit(SB),NOSPLIT,$-4
+ MOVL $194, AX
+ INT $0x80
+ RET
+
+TEXT runtime·raise(SB),NOSPLIT,$16
+ MOVL $496, AX // lwp_gettid
+ INT $0x80
+ MOVL $0, 0(SP)
+ MOVL $-1, 4(SP) // arg 1 - pid
+ MOVL AX, 8(SP) // arg 2 - tid
+ MOVL sig+0(FP), AX
+ MOVL AX, 8(SP) // arg 3 - signum
+ MOVL $497, AX // lwp_kill
+ INT $0x80
+ RET
+
+TEXT runtime·mmap(SB),NOSPLIT,$36
+ LEAL arg0+0(FP), SI
+ LEAL 4(SP), DI
+ CLD
+ MOVSL // arg 1 - addr
+ MOVSL // arg 2 - len
+ MOVSL // arg 3 - prot
+ MOVSL // arg 4 - flags
+ MOVSL // arg 5 - fd
+ MOVL $0, AX
+ STOSL // arg 6 - pad
+ MOVSL // arg 7 - offset
+ MOVL $0, AX // top 32 bits of file offset
+ STOSL
+ MOVL $197, AX // sys_mmap
+ INT $0x80
+ RET
+
+TEXT runtime·munmap(SB),NOSPLIT,$-4
+ MOVL $73, AX
+ INT $0x80
+ JAE 2(PC)
+ MOVL $0xf1, 0xf1 // crash
+ RET
+
+TEXT runtime·madvise(SB),NOSPLIT,$-4
+ MOVL $75, AX // madvise
+ INT $0x80
+ // ignore failure - maybe pages are locked
+ RET
+
+TEXT runtime·setitimer(SB), NOSPLIT, $-4
+ MOVL $83, AX
+ INT $0x80
+ RET
+
+// func now() (sec int64, nsec int32)
+TEXT time·now(SB), NOSPLIT, $32
+ MOVL $232, AX
+ LEAL 12(SP), BX
+ MOVL $0, 4(SP)
+ MOVL BX, 8(SP)
+ INT $0x80
+ MOVL 12(SP), AX // sec
+ MOVL 16(SP), BX // nsec
+
+ // sec is in AX, nsec in BX
+ MOVL AX, sec+0(FP)
+ MOVL $0, sec+4(FP)
+ MOVL BX, nsec+8(FP)
+ RET
+
+// int64 nanotime(void) so really
+// void nanotime(int64 *nsec)
+TEXT runtime·nanotime(SB), NOSPLIT, $32
+ MOVL $232, AX
+ LEAL 12(SP), BX
+ MOVL $0, 4(SP)
+ MOVL BX, 8(SP)
+ INT $0x80
+ MOVL 12(SP), AX // sec
+ MOVL 16(SP), BX // nsec
+
+ // sec is in AX, nsec in BX
+ // convert to DX:AX nsec
+ MOVL $1000000000, CX
+ MULL CX
+ ADDL BX, AX
+ ADCL $0, DX
+
+ MOVL ret+0(FP), DI
+ MOVL AX, 0(DI)
+ MOVL DX, 4(DI)
+ RET
+
+
+TEXT runtime·sigaction(SB),NOSPLIT,$-4
+ MOVL $342, AX
+ INT $0x80
+ JAE 2(PC)
+ MOVL $0xf1, 0xf1 // crash
+ RET
+
+TEXT runtime·sigtramp(SB),NOSPLIT,$44
+ get_tls(CX)
+
+ // check that m exists
+ MOVL m(CX), BX
+ CMPL BX, $0
+ JNE 6(PC)
+ MOVL signo+0(FP), BX
+ MOVL BX, 0(SP)
+ MOVL $runtime·badsignal(SB), AX
+ CALL AX
+ JMP sigtramp_ret
+
+ // save g
+ MOVL g(CX), DI
+ MOVL DI, 20(SP)
+
+ // g = m->gsignal
+ MOVL m_gsignal(BX), BX
+ MOVL BX, g(CX)
+
+ // copy arguments for call to sighandler
+ MOVL signo+0(FP), BX
+ MOVL BX, 0(SP)
+ MOVL info+4(FP), BX
+ MOVL BX, 4(SP)
+ MOVL context+8(FP), BX
+ MOVL BX, 8(SP)
+ MOVL DI, 12(SP)
+
+ CALL runtime·sighandler(SB)
+
+ // restore g
+ get_tls(CX)
+ MOVL 20(SP), BX
+ MOVL BX, g(CX)
+
+sigtramp_ret:
+ // call sigreturn
+ MOVL context+8(FP), AX
+ MOVL $0, 0(SP) // syscall gap
+ MOVL AX, 4(SP)
+ MOVL $344, AX // sigreturn(ucontext)
+ INT $0x80
+ MOVL $0xf1, 0xf1 // crash
+ RET
+
+TEXT runtime·sigaltstack(SB),NOSPLIT,$0
+ MOVL $53, AX
+ INT $0x80
+ JAE 2(PC)
+ MOVL $0xf1, 0xf1 // crash
+ RET
+
+TEXT runtime·usleep(SB),NOSPLIT,$20
+ MOVL $0, DX
+ MOVL usec+0(FP), AX
+ MOVL $1000000, CX
+ DIVL CX
+ MOVL AX, 12(SP) // tv_sec
+ MOVL $1000, AX
+ MULL DX
+ MOVL AX, 16(SP) // tv_nsec
+
+ MOVL $0, 0(SP)
+ LEAL 12(SP), AX
+ MOVL AX, 4(SP) // arg 1 - rqtp
+ MOVL $0, 8(SP) // arg 2 - rmtp
+ MOVL $240, AX // sys_nanosleep
+ INT $0x80
+ RET
+
+TEXT runtime·setldt(SB),NOSPLIT,$4
+ // Under DragonFly we set the GS base instead of messing with the LDT.
+ MOVL tls0+4(FP), AX
+ MOVL AX, 0(SP)
+ CALL runtime·settls(SB)
+ RET
+
+TEXT runtime·settls(SB),NOSPLIT,$24
+ // adjust for ELF: wants to use -8(GS) and -4(GS) for g and m
+ MOVL tlsbase+0(FP), CX
+ ADDL $8, CX
+
+ // Set up a struct tls_info - a size of -1 maps the whole address
+ // space and is required for direct-tls access of variable data
+ // via negative offsets.
+ LEAL 16(SP), BX
+ MOVL CX, 16(SP) // base
+ MOVL $-1, 20(SP) // size
+
+ // set_tls_area returns the descriptor that needs to be loaded into GS.
+ MOVL $0, 0(SP) // syscall gap
+ MOVL $0, 4(SP) // arg 1 - which
+ MOVL BX, 8(SP) // arg 2 - tls_info
+ MOVL $8, 12(SP) // arg 3 - infosize
+ MOVL $472, AX // set_tls_area
+ INT $0x80
+ JCC 2(PC)
+ MOVL $0xf1, 0xf1 // crash
+ MOVW AX, GS
+ RET
+
+TEXT runtime·sysctl(SB),NOSPLIT,$28
+ LEAL arg0+0(FP), SI
+ LEAL 4(SP), DI
+ CLD
+ MOVSL // arg 1 - name
+ MOVSL // arg 2 - namelen
+ MOVSL // arg 3 - oldp
+ MOVSL // arg 4 - oldlenp
+ MOVSL // arg 5 - newp
+ MOVSL // arg 6 - newlen
+ MOVL $202, AX // sys___sysctl
+ INT $0x80
+ JCC 3(PC)
+ NEGL AX
+ RET
+ MOVL $0, AX
+ RET
+
+TEXT runtime·osyield(SB),NOSPLIT,$-4
+ MOVL $331, AX // sys_sched_yield
+ INT $0x80
+ RET
+
+TEXT runtime·sigprocmask(SB),NOSPLIT,$16
+ MOVL $0, 0(SP) // syscall gap
+ MOVL $3, 4(SP) // arg 1 - how (SIG_SETMASK)
+ MOVL args+0(FP), AX
+ MOVL AX, 8(SP) // arg 2 - set
+ MOVL args+4(FP), AX
+ MOVL AX, 12(SP) // arg 3 - oset
+ MOVL $340, AX // sys_sigprocmask
+ INT $0x80
+ JAE 2(PC)
+ MOVL $0xf1, 0xf1 // crash
+ RET
+
+// int32 runtime·kqueue(void);
+TEXT runtime·kqueue(SB),NOSPLIT,$0
+ MOVL $362, AX
+ INT $0x80
+ JAE 2(PC)
+ NEGL AX
+ RET
+
+// int32 runtime·kevent(int kq, Kevent *changelist, int nchanges, Kevent *eventlist, int nevents, Timespec *timeout);
+TEXT runtime·kevent(SB),NOSPLIT,$0
+ MOVL $363, AX
+ INT $0x80
+ JAE 2(PC)
+ NEGL AX
+ RET
+
+// int32 runtime·closeonexec(int32 fd);
+TEXT runtime·closeonexec(SB),NOSPLIT,$32
+ MOVL $92, AX // fcntl
+ // 0(SP) is where the caller PC would be; kernel skips it
+ MOVL fd+0(FP), BX
+ MOVL BX, 4(SP) // fd
+ MOVL $2, 8(SP) // F_SETFD
+ MOVL $1, 12(SP) // FD_CLOEXEC
+ INT $0x80
+ JAE 2(PC)
+ NEGL AX
+ RET
+
+GLOBL runtime·tlsoffset(SB),$4
diff --git a/src/pkg/runtime/sys_dragonfly_amd64.s b/src/pkg/runtime/sys_dragonfly_amd64.s
new file mode 100644
index 000000000..2fa97f207
--- /dev/null
+++ b/src/pkg/runtime/sys_dragonfly_amd64.s
@@ -0,0 +1,330 @@
+// 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 calls and other sys.stuff for AMD64, FreeBSD
+// /usr/src/sys/kern/syscalls.master for syscall numbers.
+//
+
+#include "zasm_GOOS_GOARCH.h"
+#include "../../cmd/ld/textflag.h"
+
+TEXT runtime·sys_umtx_sleep(SB),NOSPLIT,$0
+ MOVQ 8(SP), DI // arg 1 - ptr
+ MOVL 16(SP), SI // arg 2 - value
+ MOVL 20(SP), DX // arg 3 - timeout
+ MOVL $469, AX // umtx_sleep
+ SYSCALL
+ JCC 2(PC)
+ NEGQ AX
+ RET
+
+TEXT runtime·sys_umtx_wakeup(SB),NOSPLIT,$0
+ MOVQ 8(SP), DI // arg 1 - ptr
+ MOVL 16(SP), SI // arg 2 - count
+ MOVL $470, AX // umtx_wakeup
+ SYSCALL
+ JCC 2(PC)
+ NEGQ AX
+ RET
+
+TEXT runtime·lwp_create(SB),NOSPLIT,$0
+ MOVQ 8(SP), DI // arg 1 - params
+ MOVL $495, AX // lwp_create
+ SYSCALL
+ RET
+
+TEXT runtime·lwp_start(SB),NOSPLIT,$0
+ MOVQ DI, R13 // m
+
+ // set up FS to point at m->tls
+ LEAQ m_tls(R13), DI
+ CALL runtime·settls(SB) // smashes DI
+
+ // set up m, g
+ get_tls(CX)
+ MOVQ R13, m(CX)
+ MOVQ m_g0(R13), DI
+ MOVQ DI, g(CX)
+
+ CALL runtime·stackcheck(SB)
+ CALL runtime·mstart(SB)
+
+ MOVQ 0, AX // crash (not reached)
+
+// Exit the entire program (like C exit)
+TEXT runtime·exit(SB),NOSPLIT,$-8
+ MOVL 8(SP), DI // arg 1 exit status
+ MOVL $1, AX
+ SYSCALL
+ MOVL $0xf1, 0xf1 // crash
+ RET
+
+TEXT runtime·exit1(SB),NOSPLIT,$-8
+ MOVQ 8(SP), DI // arg 1 exit status
+ MOVL $431, AX
+ SYSCALL
+ MOVL $0xf1, 0xf1 // crash
+ RET
+
+TEXT runtime·open(SB),NOSPLIT,$-8
+ MOVQ 8(SP), DI // arg 1 pathname
+ MOVL 16(SP), SI // arg 2 flags
+ MOVL 20(SP), DX // arg 3 mode
+ MOVL $5, AX
+ SYSCALL
+ RET
+
+TEXT runtime·close(SB),NOSPLIT,$-8
+ MOVL 8(SP), DI // arg 1 fd
+ MOVL $6, AX
+ SYSCALL
+ RET
+
+TEXT runtime·read(SB),NOSPLIT,$-8
+ MOVL 8(SP), DI // arg 1 fd
+ MOVQ 16(SP), SI // arg 2 buf
+ MOVL 24(SP), DX // arg 3 count
+ MOVL $3, AX
+ SYSCALL
+ RET
+
+TEXT runtime·write(SB),NOSPLIT,$-8
+ MOVL 8(SP), DI // arg 1 fd
+ MOVQ 16(SP), SI // arg 2 buf
+ MOVL 24(SP), DX // arg 3 count
+ MOVL $4, AX
+ SYSCALL
+ RET
+
+TEXT runtime·getrlimit(SB),NOSPLIT,$-8
+ MOVL 8(SP), DI
+ MOVQ 16(SP), SI
+ MOVL $194, AX
+ SYSCALL
+ RET
+
+TEXT runtime·raise(SB),NOSPLIT,$16
+ MOVL $496, AX // lwp_gettid
+ SYSCALL
+ MOVQ $-1, DI // arg 1 - pid
+ MOVQ 8(SP), DI // arg 2 - tid
+ MOVL sig+0(FP), SI // arg 3 - signum
+ MOVL $497, AX // lwp_kill
+ SYSCALL
+ RET
+
+TEXT runtime·setitimer(SB), NOSPLIT, $-8
+ MOVL 8(SP), DI
+ MOVQ 16(SP), SI
+ MOVQ 24(SP), DX
+ MOVL $83, AX
+ SYSCALL
+ RET
+
+// func now() (sec int64, nsec int32)
+TEXT time·now(SB), NOSPLIT, $32
+ MOVL $232, AX
+ MOVQ $0, DI
+ LEAQ 8(SP), SI
+ SYSCALL
+ MOVQ 8(SP), AX // sec
+ MOVQ 16(SP), DX // nsec
+
+ // sec is in AX, nsec in DX
+ MOVQ AX, sec+0(FP)
+ MOVL DX, nsec+8(FP)
+ RET
+
+TEXT runtime·nanotime(SB), NOSPLIT, $32
+ MOVL $232, AX
+ MOVQ $0, DI
+ LEAQ 8(SP), SI
+ SYSCALL
+ MOVQ 8(SP), AX // sec
+ MOVQ 16(SP), DX // nsec
+
+ // sec is in AX, nsec in DX
+ // return nsec in AX
+ IMULQ $1000000000, AX
+ ADDQ DX, AX
+ RET
+
+TEXT runtime·sigaction(SB),NOSPLIT,$-8
+ MOVL 8(SP), DI // arg 1 sig
+ MOVQ 16(SP), SI // arg 2 act
+ MOVQ 24(SP), DX // arg 3 oact
+ MOVL $342, AX
+ SYSCALL
+ JCC 2(PC)
+ MOVL $0xf1, 0xf1 // crash
+ RET
+
+TEXT runtime·sigtramp(SB),NOSPLIT,$64
+ get_tls(BX)
+
+ // check that m exists
+ MOVQ m(BX), BP
+ CMPQ BP, $0
+ JNE 5(PC)
+ MOVQ DI, 0(SP)
+ MOVQ $runtime·badsignal(SB), AX
+ CALL AX
+ RET
+
+ // save g
+ MOVQ g(BX), R10
+ MOVQ R10, 40(SP)
+
+ // g = m->signal
+ MOVQ m_gsignal(BP), BP
+ MOVQ BP, g(BX)
+
+ MOVQ DI, 0(SP)
+ MOVQ SI, 8(SP)
+ MOVQ DX, 16(SP)
+ MOVQ R10, 24(SP)
+
+ CALL runtime·sighandler(SB)
+
+ // restore g
+ get_tls(BX)
+ MOVQ 40(SP), R10
+ MOVQ R10, g(BX)
+ RET
+
+TEXT runtime·mmap(SB),NOSPLIT,$0
+ MOVQ 8(SP), DI // arg 1 - addr
+ MOVQ 16(SP), SI // arg 2 - len
+ MOVL 24(SP), DX // arg 3 - prot
+ MOVL 28(SP), R10 // arg 4 - flags
+ MOVL 32(SP), R8 // arg 5 - fd
+ MOVL 36(SP), R9
+ SUBQ $16, SP
+ MOVQ R9, 8(SP) // arg 7 - offset (passed on stack)
+ MOVQ $0, R9 // arg 6 - pad
+ MOVL $197, AX
+ SYSCALL
+ ADDQ $16, SP
+ RET
+
+TEXT runtime·munmap(SB),NOSPLIT,$0
+ MOVQ 8(SP), DI // arg 1 addr
+ MOVQ 16(SP), SI // arg 2 len
+ MOVL $73, AX
+ SYSCALL
+ JCC 2(PC)
+ MOVL $0xf1, 0xf1 // crash
+ RET
+
+TEXT runtime·madvise(SB),NOSPLIT,$0
+ MOVQ 8(SP), DI
+ MOVQ 16(SP), SI
+ MOVQ 24(SP), DX
+ MOVQ $75, AX // madvise
+ SYSCALL
+ // ignore failure - maybe pages are locked
+ RET
+
+TEXT runtime·sigaltstack(SB),NOSPLIT,$-8
+ MOVQ new+8(SP), DI
+ MOVQ old+16(SP), SI
+ MOVQ $53, AX
+ SYSCALL
+ JCC 2(PC)
+ MOVL $0xf1, 0xf1 // crash
+ RET
+
+TEXT runtime·usleep(SB),NOSPLIT,$16
+ MOVL $0, DX
+ MOVL usec+0(FP), AX
+ MOVL $1000000, CX
+ DIVL CX
+ MOVQ AX, 0(SP) // tv_sec
+ MOVL $1000, AX
+ MULL DX
+ MOVQ AX, 8(SP) // tv_nsec
+
+ MOVQ SP, DI // arg 1 - rqtp
+ MOVQ $0, SI // arg 2 - rmtp
+ MOVL $240, AX // sys_nanosleep
+ SYSCALL
+ RET
+
+// set tls base to DI
+TEXT runtime·settls(SB),NOSPLIT,$16
+ ADDQ $16, DI // adjust for ELF: wants to use -16(FS) and -8(FS) for g and m
+ MOVQ DI, 0(SP)
+ MOVQ $16, 8(SP)
+ MOVQ $0, DI // arg 1 - which
+ MOVQ SP, SI // arg 2 - tls_info
+ MOVQ $16, DX // arg 3 - infosize
+ MOVQ $472, AX // set_tls_area
+ SYSCALL
+ JCC 2(PC)
+ MOVL $0xf1, 0xf1 // crash
+ RET
+
+TEXT runtime·sysctl(SB),NOSPLIT,$0
+ MOVQ 8(SP), DI // arg 1 - name
+ MOVL 16(SP), SI // arg 2 - namelen
+ MOVQ 24(SP), DX // arg 3 - oldp
+ MOVQ 32(SP), R10 // arg 4 - oldlenp
+ MOVQ 40(SP), R8 // arg 5 - newp
+ MOVQ 48(SP), R9 // arg 6 - newlen
+ MOVQ $202, AX // sys___sysctl
+ SYSCALL
+ JCC 3(PC)
+ NEGQ AX
+ RET
+ MOVL $0, AX
+ RET
+
+TEXT runtime·osyield(SB),NOSPLIT,$-4
+ MOVL $331, AX // sys_sched_yield
+ SYSCALL
+ RET
+
+TEXT runtime·sigprocmask(SB),NOSPLIT,$0
+ MOVL $3, DI // arg 1 - how (SIG_SETMASK)
+ MOVQ 8(SP), SI // arg 2 - set
+ MOVQ 16(SP), DX // arg 3 - oset
+ MOVL $340, AX // sys_sigprocmask
+ SYSCALL
+ JAE 2(PC)
+ MOVL $0xf1, 0xf1 // crash
+ RET
+
+// int32 runtime·kqueue(void);
+TEXT runtime·kqueue(SB),NOSPLIT,$0
+ MOVQ $0, DI
+ MOVQ $0, SI
+ MOVQ $0, DX
+ MOVL $362, AX
+ SYSCALL
+ JCC 2(PC)
+ NEGQ AX
+ RET
+
+// int32 runtime·kevent(int kq, Kevent *changelist, int nchanges, Kevent *eventlist, int nevents, Timespec *timeout);
+TEXT runtime·kevent(SB),NOSPLIT,$0
+ MOVL 8(SP), DI
+ MOVQ 16(SP), SI
+ MOVL 24(SP), DX
+ MOVQ 32(SP), R10
+ MOVL 40(SP), R8
+ MOVQ 48(SP), R9
+ MOVL $363, AX
+ SYSCALL
+ JCC 2(PC)
+ NEGQ AX
+ RET
+
+// void runtime·closeonexec(int32 fd);
+TEXT runtime·closeonexec(SB),NOSPLIT,$0
+ MOVL 8(SP), DI // fd
+ MOVQ $2, SI // F_SETFD
+ MOVQ $1, DX // FD_CLOEXEC
+ MOVL $92, AX // fcntl
+ SYSCALL
+ RET
diff --git a/src/pkg/runtime/sys_freebsd_386.s b/src/pkg/runtime/sys_freebsd_386.s
index d960663cb..8b4d2317d 100644
--- a/src/pkg/runtime/sys_freebsd_386.s
+++ b/src/pkg/runtime/sys_freebsd_386.s
@@ -7,18 +7,19 @@
//
#include "zasm_GOOS_GOARCH.h"
+#include "../../cmd/ld/textflag.h"
-TEXT runtime·sys_umtx_op(SB),7,$-4
+TEXT runtime·sys_umtx_op(SB),NOSPLIT,$-4
MOVL $454, AX
INT $0x80
RET
-TEXT runtime·thr_new(SB),7,$-4
+TEXT runtime·thr_new(SB),NOSPLIT,$-4
MOVL $455, AX
INT $0x80
RET
-TEXT runtime·thr_start(SB),7,$0
+TEXT runtime·thr_start(SB),NOSPLIT,$0
MOVL mm+0(FP), AX
MOVL m_g0(AX), BX
LEAL m_tls(AX), BP
@@ -43,45 +44,45 @@ TEXT runtime·thr_start(SB),7,$0
MOVL 0, AX // crash (not reached)
// Exit the entire program (like C exit)
-TEXT runtime·exit(SB),7,$-4
+TEXT runtime·exit(SB),NOSPLIT,$-4
MOVL $1, AX
INT $0x80
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·exit1(SB),7,$-4
+TEXT runtime·exit1(SB),NOSPLIT,$-4
MOVL $431, AX
INT $0x80
JAE 2(PC)
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·open(SB),7,$-4
+TEXT runtime·open(SB),NOSPLIT,$-4
MOVL $5, AX
INT $0x80
RET
-TEXT runtime·close(SB),7,$-4
+TEXT runtime·close(SB),NOSPLIT,$-4
MOVL $6, AX
INT $0x80
RET
-TEXT runtime·read(SB),7,$-4
+TEXT runtime·read(SB),NOSPLIT,$-4
MOVL $3, AX
INT $0x80
RET
-TEXT runtime·write(SB),7,$-4
+TEXT runtime·write(SB),NOSPLIT,$-4
MOVL $4, AX
INT $0x80
RET
-TEXT runtime·getrlimit(SB),7,$-4
+TEXT runtime·getrlimit(SB),NOSPLIT,$-4
MOVL $194, AX
INT $0x80
RET
-TEXT runtime·raise(SB),7,$16
+TEXT runtime·raise(SB),NOSPLIT,$16
// thr_self(&8(SP))
LEAL 8(SP), AX
MOVL AX, 4(SP)
@@ -96,7 +97,7 @@ TEXT runtime·raise(SB),7,$16
INT $0x80
RET
-TEXT runtime·mmap(SB),7,$32
+TEXT runtime·mmap(SB),NOSPLIT,$32
LEAL arg0+0(FP), SI
LEAL 4(SP), DI
CLD
@@ -112,26 +113,26 @@ TEXT runtime·mmap(SB),7,$32
INT $0x80
RET
-TEXT runtime·munmap(SB),7,$-4
+TEXT runtime·munmap(SB),NOSPLIT,$-4
MOVL $73, AX
INT $0x80
JAE 2(PC)
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·madvise(SB),7,$-4
+TEXT runtime·madvise(SB),NOSPLIT,$-4
MOVL $75, AX // madvise
INT $0x80
// ignore failure - maybe pages are locked
RET
-TEXT runtime·setitimer(SB), 7, $-4
+TEXT runtime·setitimer(SB), NOSPLIT, $-4
MOVL $83, AX
INT $0x80
RET
// func now() (sec int64, nsec int32)
-TEXT time·now(SB), 7, $32
+TEXT time·now(SB), NOSPLIT, $32
MOVL $232, AX
LEAL 12(SP), BX
MOVL $0, 4(SP)
@@ -148,7 +149,7 @@ TEXT time·now(SB), 7, $32
// int64 nanotime(void) so really
// void nanotime(int64 *nsec)
-TEXT runtime·nanotime(SB), 7, $32
+TEXT runtime·nanotime(SB), NOSPLIT, $32
MOVL $232, AX
LEAL 12(SP), BX
MOVL $0, 4(SP)
@@ -170,24 +171,25 @@ TEXT runtime·nanotime(SB), 7, $32
RET
-TEXT runtime·sigaction(SB),7,$-4
+TEXT runtime·sigaction(SB),NOSPLIT,$-4
MOVL $416, AX
INT $0x80
JAE 2(PC)
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·sigtramp(SB),7,$44
+TEXT runtime·sigtramp(SB),NOSPLIT,$44
get_tls(CX)
// check that m exists
MOVL m(CX), BX
CMPL BX, $0
- JNE 5(PC)
+ JNE 6(PC)
MOVL signo+0(FP), BX
MOVL BX, 0(SP)
- CALL runtime·badsignal(SB)
- RET
+ MOVL $runtime·badsignal(SB), AX
+ CALL AX
+ JMP sigtramp_ret
// save g
MOVL g(CX), DI
@@ -212,7 +214,8 @@ TEXT runtime·sigtramp(SB),7,$44
get_tls(CX)
MOVL 20(SP), BX
MOVL BX, g(CX)
-
+
+sigtramp_ret:
// call sigreturn
MOVL context+8(FP), AX
MOVL $0, 0(SP) // syscall gap
@@ -222,14 +225,14 @@ TEXT runtime·sigtramp(SB),7,$44
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·sigaltstack(SB),7,$0
+TEXT runtime·sigaltstack(SB),NOSPLIT,$0
MOVL $53, AX
INT $0x80
JAE 2(PC)
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·usleep(SB),7,$20
+TEXT runtime·usleep(SB),NOSPLIT,$20
MOVL $0, DX
MOVL usec+0(FP), AX
MOVL $1000000, CX
@@ -264,7 +267,7 @@ int i386_set_ldt(int, const union ldt_entry *, int);
*/
// setldt(int entry, int address, int limit)
-TEXT runtime·setldt(SB),7,$32
+TEXT runtime·setldt(SB),NOSPLIT,$32
MOVL address+4(FP), BX // aka base
// see comment in sys_linux_386.s; freebsd is similar
ADDL $0x8, BX
@@ -297,7 +300,7 @@ TEXT runtime·setldt(SB),7,$32
MOVW AX, GS
RET
-TEXT runtime·i386_set_ldt(SB),7,$16
+TEXT runtime·i386_set_ldt(SB),NOSPLIT,$16
LEAL args+0(FP), AX // 0(FP) == 4(SP) before SP got moved
MOVL $0, 0(SP) // syscall gap
MOVL $1, 4(SP)
@@ -309,7 +312,7 @@ TEXT runtime·i386_set_ldt(SB),7,$16
INT $3
RET
-TEXT runtime·sysctl(SB),7,$28
+TEXT runtime·sysctl(SB),NOSPLIT,$28
LEAL arg0+0(FP), SI
LEAL 4(SP), DI
CLD
@@ -327,12 +330,12 @@ TEXT runtime·sysctl(SB),7,$28
MOVL $0, AX
RET
-TEXT runtime·osyield(SB),7,$-4
+TEXT runtime·osyield(SB),NOSPLIT,$-4
MOVL $331, AX // sys_sched_yield
INT $0x80
RET
-TEXT runtime·sigprocmask(SB),7,$16
+TEXT runtime·sigprocmask(SB),NOSPLIT,$16
MOVL $0, 0(SP) // syscall gap
MOVL $3, 4(SP) // arg 1 - how (SIG_SETMASK)
MOVL args+0(FP), AX
@@ -345,4 +348,33 @@ TEXT runtime·sigprocmask(SB),7,$16
MOVL $0xf1, 0xf1 // crash
RET
+// int32 runtime·kqueue(void);
+TEXT runtime·kqueue(SB),NOSPLIT,$0
+ MOVL $362, AX
+ INT $0x80
+ JAE 2(PC)
+ NEGL AX
+ RET
+
+// int32 runtime·kevent(int kq, Kevent *changelist, int nchanges, Kevent *eventlist, int nevents, Timespec *timeout);
+TEXT runtime·kevent(SB),NOSPLIT,$0
+ MOVL $363, AX
+ INT $0x80
+ JAE 2(PC)
+ NEGL AX
+ RET
+
+// int32 runtime·closeonexec(int32 fd);
+TEXT runtime·closeonexec(SB),NOSPLIT,$32
+ MOVL $92, AX // fcntl
+ // 0(SP) is where the caller PC would be; kernel skips it
+ MOVL fd+0(FP), BX
+ MOVL BX, 4(SP) // fd
+ MOVL $2, 8(SP) // F_SETFD
+ MOVL $1, 12(SP) // FD_CLOEXEC
+ INT $0x80
+ JAE 2(PC)
+ NEGL AX
+ RET
+
GLOBL runtime·tlsoffset(SB),$4
diff --git a/src/pkg/runtime/sys_freebsd_amd64.s b/src/pkg/runtime/sys_freebsd_amd64.s
index cfa33d4fb..63cd3ac07 100644
--- a/src/pkg/runtime/sys_freebsd_amd64.s
+++ b/src/pkg/runtime/sys_freebsd_amd64.s
@@ -7,8 +7,34 @@
//
#include "zasm_GOOS_GOARCH.h"
+#include "../../cmd/ld/textflag.h"
+
+// FreeBSD 8, FreeBSD 9, and older versions that I have checked
+// do not restore R10 on exit from a "restarted" system call
+// if you use the SYSCALL instruction. This means that, for example,
+// if a signal arrives while the wait4 system call is executing,
+// the wait4 internally returns ERESTART, which makes the kernel
+// back up the PC to execute the SYSCALL instruction a second time.
+// However, since the kernel does not restore R10, the fourth
+// argument to the system call has been lost. (FreeBSD 9 also fails
+// to restore the fifth and sixth arguments, R8 and R9, although
+// some earlier versions did restore those correctly.)
+// The broken code is in fast_syscall in FreeBSD's amd64/amd64/exception.S.
+// It restores only DI, SI, DX, AX, and RFLAGS on system call return.
+// http://fxr.watson.org/fxr/source/amd64/amd64/exception.S?v=FREEBSD91#L399
+//
+// The INT $0x80 system call path (int0x80_syscall in FreeBSD's
+// amd64/ia32/ia32_exception.S) does not have this problem,
+// but it expects the third argument in R10. Instead of rewriting
+// all the assembly in this file, #define SYSCALL to a safe simulation
+// using INT $0x80.
+//
+// INT $0x80 is a little slower than SYSCALL, but correctness wins.
+//
+// See golang.org/issue/6372.
+#define SYSCALL MOVQ R10, CX; INT $0x80
-TEXT runtime·sys_umtx_op(SB),7,$0
+TEXT runtime·sys_umtx_op(SB),NOSPLIT,$0
MOVQ 8(SP), DI
MOVL 16(SP), SI
MOVL 20(SP), DX
@@ -18,14 +44,14 @@ TEXT runtime·sys_umtx_op(SB),7,$0
SYSCALL
RET
-TEXT runtime·thr_new(SB),7,$0
+TEXT runtime·thr_new(SB),NOSPLIT,$0
MOVQ 8(SP), DI
MOVQ 16(SP), SI
MOVL $455, AX
SYSCALL
RET
-TEXT runtime·thr_start(SB),7,$0
+TEXT runtime·thr_start(SB),NOSPLIT,$0
MOVQ DI, R13 // m
// set up FS to point at m->tls
@@ -44,21 +70,21 @@ TEXT runtime·thr_start(SB),7,$0
MOVQ 0, AX // crash (not reached)
// Exit the entire program (like C exit)
-TEXT runtime·exit(SB),7,$-8
+TEXT runtime·exit(SB),NOSPLIT,$-8
MOVL 8(SP), DI // arg 1 exit status
MOVL $1, AX
SYSCALL
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·exit1(SB),7,$-8
+TEXT runtime·exit1(SB),NOSPLIT,$-8
MOVQ 8(SP), DI // arg 1 exit status
MOVL $431, AX
SYSCALL
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·open(SB),7,$-8
+TEXT runtime·open(SB),NOSPLIT,$-8
MOVQ 8(SP), DI // arg 1 pathname
MOVL 16(SP), SI // arg 2 flags
MOVL 20(SP), DX // arg 3 mode
@@ -66,13 +92,13 @@ TEXT runtime·open(SB),7,$-8
SYSCALL
RET
-TEXT runtime·close(SB),7,$-8
+TEXT runtime·close(SB),NOSPLIT,$-8
MOVL 8(SP), DI // arg 1 fd
MOVL $6, AX
SYSCALL
RET
-TEXT runtime·read(SB),7,$-8
+TEXT runtime·read(SB),NOSPLIT,$-8
MOVL 8(SP), DI // arg 1 fd
MOVQ 16(SP), SI // arg 2 buf
MOVL 24(SP), DX // arg 3 count
@@ -80,7 +106,7 @@ TEXT runtime·read(SB),7,$-8
SYSCALL
RET
-TEXT runtime·write(SB),7,$-8
+TEXT runtime·write(SB),NOSPLIT,$-8
MOVL 8(SP), DI // arg 1 fd
MOVQ 16(SP), SI // arg 2 buf
MOVL 24(SP), DX // arg 3 count
@@ -88,14 +114,14 @@ TEXT runtime·write(SB),7,$-8
SYSCALL
RET
-TEXT runtime·getrlimit(SB),7,$-8
+TEXT runtime·getrlimit(SB),NOSPLIT,$-8
MOVL 8(SP), DI
MOVQ 16(SP), SI
MOVL $194, AX
SYSCALL
RET
-TEXT runtime·raise(SB),7,$16
+TEXT runtime·raise(SB),NOSPLIT,$16
// thr_self(&8(SP))
LEAQ 8(SP), DI // arg 1 &8(SP)
MOVL $432, AX
@@ -107,7 +133,7 @@ TEXT runtime·raise(SB),7,$16
SYSCALL
RET
-TEXT runtime·setitimer(SB), 7, $-8
+TEXT runtime·setitimer(SB), NOSPLIT, $-8
MOVL 8(SP), DI
MOVQ 16(SP), SI
MOVQ 24(SP), DX
@@ -116,7 +142,7 @@ TEXT runtime·setitimer(SB), 7, $-8
RET
// func now() (sec int64, nsec int32)
-TEXT time·now(SB), 7, $32
+TEXT time·now(SB), NOSPLIT, $32
MOVL $232, AX
MOVQ $0, DI
LEAQ 8(SP), SI
@@ -129,7 +155,7 @@ TEXT time·now(SB), 7, $32
MOVL DX, nsec+8(FP)
RET
-TEXT runtime·nanotime(SB), 7, $32
+TEXT runtime·nanotime(SB), NOSPLIT, $32
MOVL $232, AX
MOVQ $0, DI
LEAQ 8(SP), SI
@@ -143,7 +169,7 @@ TEXT runtime·nanotime(SB), 7, $32
ADDQ DX, AX
RET
-TEXT runtime·sigaction(SB),7,$-8
+TEXT runtime·sigaction(SB),NOSPLIT,$-8
MOVL 8(SP), DI // arg 1 sig
MOVQ 16(SP), SI // arg 2 act
MOVQ 24(SP), DX // arg 3 oact
@@ -153,15 +179,16 @@ TEXT runtime·sigaction(SB),7,$-8
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·sigtramp(SB),7,$64
+TEXT runtime·sigtramp(SB),NOSPLIT,$64
get_tls(BX)
-
+
// check that m exists
MOVQ m(BX), BP
CMPQ BP, $0
- JNE 4(PC)
+ JNE 5(PC)
MOVQ DI, 0(SP)
- CALL runtime·badsignal(SB)
+ MOVQ $runtime·badsignal(SB), AX
+ CALL AX
RET
// save g
@@ -176,7 +203,7 @@ TEXT runtime·sigtramp(SB),7,$64
MOVQ SI, 8(SP)
MOVQ DX, 16(SP)
MOVQ R10, 24(SP)
-
+
CALL runtime·sighandler(SB)
// restore g
@@ -185,7 +212,7 @@ TEXT runtime·sigtramp(SB),7,$64
MOVQ R10, g(BX)
RET
-TEXT runtime·mmap(SB),7,$0
+TEXT runtime·mmap(SB),NOSPLIT,$0
MOVQ 8(SP), DI // arg 1 addr
MOVQ 16(SP), SI // arg 2 len
MOVL 24(SP), DX // arg 3 prot
@@ -196,7 +223,7 @@ TEXT runtime·mmap(SB),7,$0
SYSCALL
RET
-TEXT runtime·munmap(SB),7,$0
+TEXT runtime·munmap(SB),NOSPLIT,$0
MOVQ 8(SP), DI // arg 1 addr
MOVQ 16(SP), SI // arg 2 len
MOVL $73, AX
@@ -205,7 +232,7 @@ TEXT runtime·munmap(SB),7,$0
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·madvise(SB),7,$0
+TEXT runtime·madvise(SB),NOSPLIT,$0
MOVQ 8(SP), DI
MOVQ 16(SP), SI
MOVQ 24(SP), DX
@@ -214,7 +241,7 @@ TEXT runtime·madvise(SB),7,$0
// ignore failure - maybe pages are locked
RET
-TEXT runtime·sigaltstack(SB),7,$-8
+TEXT runtime·sigaltstack(SB),NOSPLIT,$-8
MOVQ new+8(SP), DI
MOVQ old+16(SP), SI
MOVQ $53, AX
@@ -223,7 +250,7 @@ TEXT runtime·sigaltstack(SB),7,$-8
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·usleep(SB),7,$16
+TEXT runtime·usleep(SB),NOSPLIT,$16
MOVL $0, DX
MOVL usec+0(FP), AX
MOVL $1000000, CX
@@ -240,7 +267,7 @@ TEXT runtime·usleep(SB),7,$16
RET
// set tls base to DI
-TEXT runtime·settls(SB),7,$8
+TEXT runtime·settls(SB),NOSPLIT,$8
ADDQ $16, DI // adjust for ELF: wants to use -16(FS) and -8(FS) for g and m
MOVQ DI, 0(SP)
MOVQ SP, SI
@@ -251,7 +278,7 @@ TEXT runtime·settls(SB),7,$8
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·sysctl(SB),7,$0
+TEXT runtime·sysctl(SB),NOSPLIT,$0
MOVQ 8(SP), DI // arg 1 - name
MOVL 16(SP), SI // arg 2 - namelen
MOVQ 24(SP), DX // arg 3 - oldp
@@ -266,12 +293,12 @@ TEXT runtime·sysctl(SB),7,$0
MOVL $0, AX
RET
-TEXT runtime·osyield(SB),7,$-4
+TEXT runtime·osyield(SB),NOSPLIT,$-4
MOVL $331, AX // sys_sched_yield
SYSCALL
RET
-TEXT runtime·sigprocmask(SB),7,$0
+TEXT runtime·sigprocmask(SB),NOSPLIT,$0
MOVL $3, DI // arg 1 - how (SIG_SETMASK)
MOVQ 8(SP), SI // arg 2 - set
MOVQ 16(SP), DX // arg 3 - oset
@@ -280,3 +307,37 @@ TEXT runtime·sigprocmask(SB),7,$0
JAE 2(PC)
MOVL $0xf1, 0xf1 // crash
RET
+
+// int32 runtime·kqueue(void);
+TEXT runtime·kqueue(SB),NOSPLIT,$0
+ MOVQ $0, DI
+ MOVQ $0, SI
+ MOVQ $0, DX
+ MOVL $362, AX
+ SYSCALL
+ JCC 2(PC)
+ NEGQ AX
+ RET
+
+// int32 runtime·kevent(int kq, Kevent *changelist, int nchanges, Kevent *eventlist, int nevents, Timespec *timeout);
+TEXT runtime·kevent(SB),NOSPLIT,$0
+ MOVL 8(SP), DI
+ MOVQ 16(SP), SI
+ MOVL 24(SP), DX
+ MOVQ 32(SP), R10
+ MOVL 40(SP), R8
+ MOVQ 48(SP), R9
+ MOVL $363, AX
+ SYSCALL
+ JCC 2(PC)
+ NEGQ AX
+ RET
+
+// void runtime·closeonexec(int32 fd);
+TEXT runtime·closeonexec(SB),NOSPLIT,$0
+ MOVL 8(SP), DI // fd
+ MOVQ $2, SI // F_SETFD
+ MOVQ $1, DX // FD_CLOEXEC
+ MOVL $92, AX // fcntl
+ SYSCALL
+ RET
diff --git a/src/pkg/runtime/sys_freebsd_arm.s b/src/pkg/runtime/sys_freebsd_arm.s
index 5531936ff..106d72799 100644
--- a/src/pkg/runtime/sys_freebsd_arm.s
+++ b/src/pkg/runtime/sys_freebsd_arm.s
@@ -7,108 +7,148 @@
//
#include "zasm_GOOS_GOARCH.h"
+#include "../../cmd/ld/textflag.h"
+
+// for EABI, as we don't support OABI
+#define SYS_BASE 0x0
+
+#define SYS_exit (SYS_BASE + 1)
+#define SYS_read (SYS_BASE + 3)
+#define SYS_write (SYS_BASE + 4)
+#define SYS_open (SYS_BASE + 5)
+#define SYS_close (SYS_BASE + 6)
+#define SYS_sigaltstack (SYS_BASE + 53)
+#define SYS_munmap (SYS_BASE + 73)
+#define SYS_madvise (SYS_BASE + 75)
+#define SYS_setitimer (SYS_BASE + 83)
+#define SYS_fcntl (SYS_BASE + 92)
+#define SYS_getrlimit (SYS_BASE + 194)
+#define SYS___sysctl (SYS_BASE + 202)
+#define SYS_nanosleep (SYS_BASE + 240)
+#define SYS_clock_gettime (SYS_BASE + 232)
+#define SYS_sched_yield (SYS_BASE + 331)
+#define SYS_sigprocmask (SYS_BASE + 340)
+#define SYS_kqueue (SYS_BASE + 362)
+#define SYS_kevent (SYS_BASE + 363)
+#define SYS_sigaction (SYS_BASE + 416)
+#define SYS_thr_exit (SYS_BASE + 431)
+#define SYS_thr_self (SYS_BASE + 432)
+#define SYS_thr_kill (SYS_BASE + 433)
+#define SYS__umtx_op (SYS_BASE + 454)
+#define SYS_thr_new (SYS_BASE + 455)
+#define SYS_mmap (SYS_BASE + 477)
-TEXT runtime·sys_umtx_op(SB),7,$0
+TEXT runtime·sys_umtx_op(SB),NOSPLIT,$0
MOVW 0(FP), R0
MOVW 4(FP), R1
MOVW 8(FP), R2
MOVW 12(FP), R3
ADD $20, R13 // arg 5 is passed on stack
- SWI $454
+ MOVW $SYS__umtx_op, R7
+ SWI $0
SUB $20, R13
// BCS error
RET
-TEXT runtime·thr_new(SB),7,$0
+TEXT runtime·thr_new(SB),NOSPLIT,$0
MOVW 0(FP), R0
MOVW 4(FP), R1
- SWI $455
+ MOVW $SYS_thr_new, R7
+ SWI $0
RET
-TEXT runtime·thr_start(SB),7,$0
- MOVW R0, R9 // m
-
- // TODO(minux): set up TLS?
+TEXT runtime·thr_start(SB),NOSPLIT,$0
+ MOVW R0, m
// set up g
- MOVW m_g0(R9), R10
+ MOVW m_g0(m), g
BL runtime·emptyfunc(SB) // fault if stack check is wrong
BL runtime·mstart(SB)
- MOVW $2, R9 // crash (not reached)
- MOVW R9, (R9)
+ MOVW $2, R8 // crash (not reached)
+ MOVW R8, (R8)
RET
// Exit the entire program (like C exit)
-TEXT runtime·exit(SB),7,$-8
+TEXT runtime·exit(SB),NOSPLIT,$-8
MOVW 0(FP), R0 // arg 1 exit status
- SWI $1
- MOVW.CS $0, R9 // crash on syscall failure
- MOVW.CS R9, (R9)
+ MOVW $SYS_exit, R7
+ SWI $0
+ MOVW.CS $0, R8 // crash on syscall failure
+ MOVW.CS R8, (R8)
RET
-TEXT runtime·exit1(SB),7,$-8
+TEXT runtime·exit1(SB),NOSPLIT,$-8
MOVW 0(FP), R0 // arg 1 exit status
- SWI $431
- MOVW.CS $0, R9 // crash on syscall failure
- MOVW.CS R9, (R9)
+ MOVW $SYS_thr_exit, R7
+ SWI $0
+ MOVW.CS $0, R8 // crash on syscall failure
+ MOVW.CS R8, (R8)
RET
-TEXT runtime·open(SB),7,$-8
+TEXT runtime·open(SB),NOSPLIT,$-8
MOVW 0(FP), R0 // arg 1 name
MOVW 4(FP), R1 // arg 2 mode
MOVW 8(FP), R2 // arg 3 perm
- SWI $5
+ MOVW $SYS_open, R7
+ SWI $0
RET
-TEXT runtime·read(SB),7,$-8
+TEXT runtime·read(SB),NOSPLIT,$-8
MOVW 0(FP), R0 // arg 1 fd
MOVW 4(FP), R1 // arg 2 buf
MOVW 8(FP), R2 // arg 3 count
- SWI $3
+ MOVW $SYS_read, R7
+ SWI $0
RET
-TEXT runtime·write(SB),7,$-8
+TEXT runtime·write(SB),NOSPLIT,$-8
MOVW 0(FP), R0 // arg 1 fd
MOVW 4(FP), R1 // arg 2 buf
MOVW 8(FP), R2 // arg 3 count
- SWI $4
+ MOVW $SYS_write, R7
+ SWI $0
RET
-TEXT runtime·close(SB),7,$-8
+TEXT runtime·close(SB),NOSPLIT,$-8
MOVW 0(FP), R0 // arg 1 fd
- SWI $6
+ MOVW $SYS_close, R7
+ SWI $0
RET
-TEXT runtime·getrlimit(SB),7,$-8
+TEXT runtime·getrlimit(SB),NOSPLIT,$-8
MOVW 0(FP), R0
MOVW 4(FP), R1
- MOVW 8(FP), R2
- SWI $194
+ MOVW $SYS_getrlimit, R7
+ SWI $0
RET
-TEXT runtime·raise(SB),7,$8
+TEXT runtime·raise(SB),NOSPLIT,$8
// thr_self(&4(R13))
MOVW $4(R13), R0 // arg 1 &4(R13)
- SWI $432
+ MOVW $SYS_thr_self, R7
+ SWI $0
// thr_kill(self, SIGPIPE)
MOVW 4(R13), R0 // arg 1 id
MOVW sig+0(FP), R1 // arg 2 - signal
- SWI $433
+ MOVW $SYS_thr_kill, R7
+ SWI $0
RET
-TEXT runtime·setitimer(SB), 7, $-8
+TEXT runtime·setitimer(SB), NOSPLIT, $-8
MOVW 0(FP), R0
MOVW 4(FP), R1
MOVW 8(FP), R2
- SWI $83
+ MOVW $SYS_setitimer, R7
+ SWI $0
RET
// func now() (sec int64, nsec int32)
-TEXT time·now(SB), 7, $32
+TEXT time·now(SB), NOSPLIT, $32
MOVW $0, R0 // CLOCK_REALTIME
MOVW $8(R13), R1
- SWI $232 // clock_gettime
+ MOVW $SYS_clock_gettime, R7
+ SWI $0
MOVW 8(R13), R0 // sec.low
MOVW 12(R13), R1 // sec.high
@@ -121,10 +161,11 @@ TEXT time·now(SB), 7, $32
// int64 nanotime(void) so really
// void nanotime(int64 *nsec)
-TEXT runtime·nanotime(SB), 7, $32
+TEXT runtime·nanotime(SB), NOSPLIT, $32
MOVW $0, R0 // CLOCK_REALTIME
MOVW $8(R13), R1
- SWI $232 // clock_gettime
+ MOVW $SYS_clock_gettime, R7
+ SWI $0
MOVW 8(R13), R0 // sec.low
MOVW 12(R13), R4 // sec.high
@@ -141,31 +182,38 @@ TEXT runtime·nanotime(SB), 7, $32
MOVW R1, 4(R3)
RET
-TEXT runtime·sigaction(SB),7,$-8
+TEXT runtime·sigaction(SB),NOSPLIT,$-8
MOVW 0(FP), R0 // arg 1 sig
MOVW 4(FP), R1 // arg 2 act
MOVW 8(FP), R2 // arg 3 oact
- SWI $416
- MOVW.CS $0, R9 // crash on syscall failure
- MOVW.CS R9, (R9)
+ MOVW $SYS_sigaction, R7
+ SWI $0
+ MOVW.CS $0, R8 // crash on syscall failure
+ MOVW.CS R8, (R8)
RET
-TEXT runtime·sigtramp(SB),7,$24
+TEXT runtime·sigtramp(SB),NOSPLIT,$24
// this might be called in external code context,
// where g and m are not set.
- // first save R0, because _cgo_load_gm will clobber it
- // TODO(adonovan): call runtime·badsignal if m=0, like other platforms?
+ // first save R0, because runtime·load_gm will clobber it
MOVW R0, 4(R13) // signum
- MOVW _cgo_load_gm(SB), R0
+ MOVB runtime·iscgo(SB), R0
CMP $0, R0
- BL.NE (R0)
+ BL.NE runtime·load_gm(SB)
+
+ CMP $0, m
+ BNE 4(PC)
+ // signal number is already prepared in 4(R13)
+ MOVW $runtime·badsignal(SB), R11
+ BL (R11)
+ RET
// save g
- MOVW R10, R4
- MOVW R10, 20(R13)
+ MOVW g, R4
+ MOVW g, 20(R13)
// g = m->signal
- MOVW m_gsignal(R9), R10
+ MOVW m_gsignal(m), g
// R0 is already saved
MOVW R1, 8(R13) // info
@@ -175,10 +223,10 @@ TEXT runtime·sigtramp(SB),7,$24
BL runtime·sighandler(SB)
// restore g
- MOVW 20(R13), R10
+ MOVW 20(R13), g
RET
-TEXT runtime·mmap(SB),7,$12
+TEXT runtime·mmap(SB),NOSPLIT,$16
MOVW 0(FP), R0 // arg 1 addr
MOVW 4(FP), R1 // arg 2 len
MOVW 8(FP), R2 // arg 3 prot
@@ -188,39 +236,45 @@ TEXT runtime·mmap(SB),7,$12
MOVW 16(FP), R4 // arg 5
MOVW R4, 4(R13)
MOVW 20(FP), R5 // arg 6 lower 32-bit
- MOVW R5, 8(R13)
- MOVW $0, R6 // higher 32-bit for arg 6
- MOVW R6, 12(R13)
- ADD $4, R13 // pass arg 5 and arg 6 on stack
- SWI $477
+ // the word at 8(R13) is skipped due to 64-bit argument alignment.
+ MOVW R5, 12(R13)
+ MOVW $0, R6 // higher 32-bit for arg 6
+ MOVW R6, 16(R13)
+ ADD $4, R13
+ MOVW $SYS_mmap, R7
+ SWI $0
SUB $4, R13
+ // TODO(dfc) error checking ?
RET
-TEXT runtime·munmap(SB),7,$0
+TEXT runtime·munmap(SB),NOSPLIT,$0
MOVW 0(FP), R0 // arg 1 addr
MOVW 4(FP), R1 // arg 2 len
- SWI $73
- MOVW.CS $0, R9 // crash on syscall failure
- MOVW.CS R9, (R9)
+ MOVW $SYS_munmap, R7
+ SWI $0
+ MOVW.CS $0, R8 // crash on syscall failure
+ MOVW.CS R8, (R8)
RET
-TEXT runtime·madvise(SB),7,$0
+TEXT runtime·madvise(SB),NOSPLIT,$0
MOVW 0(FP), R0 // arg 1 addr
MOVW 4(FP), R1 // arg 2 len
MOVW 8(FP), R2 // arg 3 flags
- SWI $75
+ MOVW $SYS_madvise, R7
+ SWI $0
// ignore failure - maybe pages are locked
RET
-TEXT runtime·sigaltstack(SB),7,$-8
+TEXT runtime·sigaltstack(SB),NOSPLIT,$-8
MOVW new+0(FP), R0
MOVW old+4(FP), R1
- SWI $53
- MOVW.CS $0, R9 // crash on syscall failure
- MOVW.CS R9, (R9)
+ MOVW $SYS_sigaltstack, R7
+ SWI $0
+ MOVW.CS $0, R8 // crash on syscall failure
+ MOVW.CS R8, (R8)
RET
-TEXT runtime·usleep(SB),7,$16
+TEXT runtime·usleep(SB),NOSPLIT,$16
MOVW usec+0(FP), R0
MOVW R0, R2
MOVW $1000000, R1
@@ -236,35 +290,68 @@ TEXT runtime·usleep(SB),7,$16
MOVW $4(R13), R0 // arg 1 - rqtp
MOVW $0, R1 // arg 2 - rmtp
- SWI $240 // sys_nanosleep
+ MOVW $SYS_nanosleep, R7
+ SWI $0
RET
-TEXT runtime·sysctl(SB),7,$0
+TEXT runtime·sysctl(SB),NOSPLIT,$0
MOVW 0(FP), R0 // arg 1 - name
MOVW 4(FP), R1 // arg 2 - namelen
- MOVW 8(FP), R2 // arg 3 - oldp
+ MOVW 8(FP), R2 // arg 3 - old
MOVW 12(FP), R3 // arg 4 - oldlenp
// arg 5 (newp) and arg 6 (newlen) are passed on stack
ADD $20, R13
- SWI $202 // sys___sysctl
+ MOVW $SYS___sysctl, R7
+ SWI $0
SUB.CS $0, R0, R0
SUB $20, R13
RET
-TEXT runtime·osyield(SB),7,$-4
- SWI $331 // sys_sched_yield
+TEXT runtime·osyield(SB),NOSPLIT,$-4
+ MOVW $SYS_sched_yield, R7
+ SWI $0
RET
-TEXT runtime·sigprocmask(SB),7,$0
+TEXT runtime·sigprocmask(SB),NOSPLIT,$0
MOVW $3, R0 // arg 1 - how (SIG_SETMASK)
MOVW 0(FP), R1 // arg 2 - set
MOVW 4(FP), R2 // arg 3 - oset
- SWI $340 // sys_sigprocmask
- MOVW.CS $0, R9 // crash on syscall failure
- MOVW.CS R9, (R9)
+ MOVW $SYS_sigprocmask, R7
+ SWI $0
+ MOVW.CS $0, R8 // crash on syscall failure
+ MOVW.CS R8, (R8)
+ RET
+
+// int32 runtime·kqueue(void)
+TEXT runtime·kqueue(SB),NOSPLIT,$0
+ MOVW $SYS_kqueue, R7
+ SWI $0
+ RSB.CS $0, R0
RET
-TEXT runtime·casp(SB),7,$0
+// int32 runtime·kevent(int kq, Kevent *changelist, int nchanges, Kevent *eventlist, int nevents, Timespec *timeout)
+TEXT runtime·kevent(SB),NOSPLIT,$0
+ MOVW 0(FP), R0 // kq
+ MOVW 4(FP), R1 // changelist
+ MOVW 8(FP), R2 // nchanges
+ MOVW 12(FP), R3 // eventlist
+ ADD $20, R13 // pass arg 5 and 6 on stack
+ MOVW $SYS_kevent, R7
+ SWI $0
+ RSB.CS $0, R0
+ SUB $20, R13
+ RET
+
+// void runtime·closeonexec(int32 fd)
+TEXT runtime·closeonexec(SB),NOSPLIT,$0
+ MOVW 0(FP), R0 // fd
+ MOVW $2, R1 // F_SETFD
+ MOVW $1, R2 // FD_CLOEXEC
+ MOVW $SYS_fcntl, R7
+ SWI $0
+ RET
+
+TEXT runtime·casp(SB),NOSPLIT,$0
B runtime·cas(SB)
// TODO(minux): this is only valid for ARMv6+
@@ -275,5 +362,9 @@ TEXT runtime·casp(SB),7,$0
// return 1;
// }else
// return 0;
-TEXT runtime·cas(SB),7,$0
+TEXT runtime·cas(SB),NOSPLIT,$0
B runtime·armcas(SB)
+
+TEXT runtime·read_tls_fallback(SB),NOSPLIT,$-4
+ MOVW $0xffff1000, R0
+ MOVW (R0), R0
diff --git a/src/pkg/runtime/sys_linux_386.s b/src/pkg/runtime/sys_linux_386.s
index 76ebe3dcf..fcda739db 100644
--- a/src/pkg/runtime/sys_linux_386.s
+++ b/src/pkg/runtime/sys_linux_386.s
@@ -7,22 +7,23 @@
//
#include "zasm_GOOS_GOARCH.h"
+#include "../../cmd/ld/textflag.h"
-TEXT runtime·exit(SB),7,$0
+TEXT runtime·exit(SB),NOSPLIT,$0
MOVL $252, AX // syscall number
MOVL 4(SP), BX
CALL *runtime·_vdso(SB)
INT $3 // not reached
RET
-TEXT runtime·exit1(SB),7,$0
+TEXT runtime·exit1(SB),NOSPLIT,$0
MOVL $1, AX // exit - exit the current os thread
MOVL 4(SP), BX
CALL *runtime·_vdso(SB)
INT $3 // not reached
RET
-TEXT runtime·open(SB),7,$0
+TEXT runtime·open(SB),NOSPLIT,$0
MOVL $5, AX // syscall - open
MOVL 4(SP), BX
MOVL 8(SP), CX
@@ -30,13 +31,13 @@ TEXT runtime·open(SB),7,$0
CALL *runtime·_vdso(SB)
RET
-TEXT runtime·close(SB),7,$0
+TEXT runtime·close(SB),NOSPLIT,$0
MOVL $6, AX // syscall - close
MOVL 4(SP), BX
CALL *runtime·_vdso(SB)
RET
-TEXT runtime·write(SB),7,$0
+TEXT runtime·write(SB),NOSPLIT,$0
MOVL $4, AX // syscall - write
MOVL 4(SP), BX
MOVL 8(SP), CX
@@ -44,7 +45,7 @@ TEXT runtime·write(SB),7,$0
CALL *runtime·_vdso(SB)
RET
-TEXT runtime·read(SB),7,$0
+TEXT runtime·read(SB),NOSPLIT,$0
MOVL $3, AX // syscall - read
MOVL 4(SP), BX
MOVL 8(SP), CX
@@ -52,14 +53,14 @@ TEXT runtime·read(SB),7,$0
CALL *runtime·_vdso(SB)
RET
-TEXT runtime·getrlimit(SB),7,$0
+TEXT runtime·getrlimit(SB),NOSPLIT,$0
MOVL $191, AX // syscall - ugetrlimit
MOVL 4(SP), BX
MOVL 8(SP), CX
CALL *runtime·_vdso(SB)
RET
-TEXT runtime·usleep(SB),7,$8
+TEXT runtime·usleep(SB),NOSPLIT,$8
MOVL $0, DX
MOVL usec+0(FP), AX
MOVL $1000000, CX
@@ -77,7 +78,7 @@ TEXT runtime·usleep(SB),7,$8
CALL *runtime·_vdso(SB)
RET
-TEXT runtime·raise(SB),7,$12
+TEXT runtime·raise(SB),NOSPLIT,$12
MOVL $224, AX // syscall - gettid
CALL *runtime·_vdso(SB)
MOVL AX, BX // arg 1 tid
@@ -86,7 +87,7 @@ TEXT runtime·raise(SB),7,$12
CALL *runtime·_vdso(SB)
RET
-TEXT runtime·setitimer(SB),7,$0-24
+TEXT runtime·setitimer(SB),NOSPLIT,$0-24
MOVL $104, AX // syscall - setitimer
MOVL 4(SP), BX
MOVL 8(SP), CX
@@ -94,7 +95,7 @@ TEXT runtime·setitimer(SB),7,$0-24
CALL *runtime·_vdso(SB)
RET
-TEXT runtime·mincore(SB),7,$0-24
+TEXT runtime·mincore(SB),NOSPLIT,$0-24
MOVL $218, AX // syscall - mincore
MOVL 4(SP), BX
MOVL 8(SP), CX
@@ -103,7 +104,7 @@ TEXT runtime·mincore(SB),7,$0-24
RET
// func now() (sec int64, nsec int32)
-TEXT time·now(SB), 7, $32
+TEXT time·now(SB), NOSPLIT, $32
MOVL $265, AX // syscall - clock_gettime
MOVL $0, BX
LEAL 8(SP), CX
@@ -120,7 +121,7 @@ TEXT time·now(SB), 7, $32
// int64 nanotime(void) so really
// void nanotime(int64 *nsec)
-TEXT runtime·nanotime(SB), 7, $32
+TEXT runtime·nanotime(SB), NOSPLIT, $32
MOVL $265, AX // syscall - clock_gettime
MOVL $0, BX
LEAL 8(SP), CX
@@ -141,7 +142,7 @@ TEXT runtime·nanotime(SB), 7, $32
MOVL DX, 4(DI)
RET
-TEXT runtime·rtsigprocmask(SB),7,$0
+TEXT runtime·rtsigprocmask(SB),NOSPLIT,$0
MOVL $175, AX // syscall entry
MOVL 4(SP), BX
MOVL 8(SP), CX
@@ -153,7 +154,7 @@ TEXT runtime·rtsigprocmask(SB),7,$0
INT $3
RET
-TEXT runtime·rt_sigaction(SB),7,$0
+TEXT runtime·rt_sigaction(SB),NOSPLIT,$0
MOVL $174, AX // syscall - rt_sigaction
MOVL 4(SP), BX
MOVL 8(SP), CX
@@ -162,16 +163,17 @@ TEXT runtime·rt_sigaction(SB),7,$0
CALL *runtime·_vdso(SB)
RET
-TEXT runtime·sigtramp(SB),7,$44
+TEXT runtime·sigtramp(SB),NOSPLIT,$44
get_tls(CX)
// check that m exists
MOVL m(CX), BX
CMPL BX, $0
- JNE 5(PC)
+ JNE 6(PC)
MOVL sig+0(FP), BX
MOVL BX, 0(SP)
- CALL runtime·badsignal(SB)
+ MOVL $runtime·badsignal(SB), AX
+ CALL AX
RET
// save g
@@ -201,7 +203,7 @@ TEXT runtime·sigtramp(SB),7,$44
RET
-TEXT runtime·sigreturn(SB),7,$0
+TEXT runtime·sigreturn(SB),NOSPLIT,$0
MOVL $173, AX // rt_sigreturn
// Sigreturn expects same SP as signal handler,
// so cannot CALL *runtime._vsdo(SB) here.
@@ -209,7 +211,7 @@ TEXT runtime·sigreturn(SB),7,$0
INT $3 // not reached
RET
-TEXT runtime·mmap(SB),7,$0
+TEXT runtime·mmap(SB),NOSPLIT,$0
MOVL $192, AX // mmap2
MOVL 4(SP), BX
MOVL 8(SP), CX
@@ -225,7 +227,7 @@ TEXT runtime·mmap(SB),7,$0
INCL AX
RET
-TEXT runtime·munmap(SB),7,$0
+TEXT runtime·munmap(SB),NOSPLIT,$0
MOVL $91, AX // munmap
MOVL 4(SP), BX
MOVL 8(SP), CX
@@ -235,7 +237,7 @@ TEXT runtime·munmap(SB),7,$0
INT $3
RET
-TEXT runtime·madvise(SB),7,$0
+TEXT runtime·madvise(SB),NOSPLIT,$0
MOVL $219, AX // madvise
MOVL 4(SP), BX
MOVL 8(SP), CX
@@ -246,7 +248,7 @@ TEXT runtime·madvise(SB),7,$0
// int32 futex(int32 *uaddr, int32 op, int32 val,
// struct timespec *timeout, int32 *uaddr2, int32 val2);
-TEXT runtime·futex(SB),7,$0
+TEXT runtime·futex(SB),NOSPLIT,$0
MOVL $240, AX // futex
MOVL 4(SP), BX
MOVL 8(SP), CX
@@ -258,7 +260,7 @@ TEXT runtime·futex(SB),7,$0
RET
// int32 clone(int32 flags, void *stack, M *mp, G *gp, void (*fn)(void));
-TEXT runtime·clone(SB),7,$0
+TEXT runtime·clone(SB),NOSPLIT,$0
MOVL $120, AX // clone
MOVL flags+4(SP), BX
MOVL stack+8(SP), CX
@@ -338,7 +340,7 @@ TEXT runtime·clone(SB),7,$0
MOVL $0x1234, 0x1005
RET
-TEXT runtime·sigaltstack(SB),7,$-8
+TEXT runtime·sigaltstack(SB),NOSPLIT,$-8
MOVL $186, AX // sigaltstack
MOVL new+4(SP), BX
MOVL old+8(SP), CX
@@ -371,7 +373,7 @@ TEXT runtime·sigaltstack(SB),7,$-8
#define USEABLE 0x40
// setldt(int entry, int address, int limit)
-TEXT runtime·setldt(SB),7,$32
+TEXT runtime·setldt(SB),NOSPLIT,$32
MOVL entry+0(FP), BX // entry
MOVL address+4(FP), CX // base address
@@ -418,12 +420,12 @@ TEXT runtime·setldt(SB),7,$32
RET
-TEXT runtime·osyield(SB),7,$0
+TEXT runtime·osyield(SB),NOSPLIT,$0
MOVL $158, AX
CALL *runtime·_vdso(SB)
RET
-TEXT runtime·sched_getaffinity(SB),7,$0
+TEXT runtime·sched_getaffinity(SB),NOSPLIT,$0
MOVL $242, AX // syscall - sched_getaffinity
MOVL 4(SP), BX
MOVL 8(SP), CX
@@ -432,21 +434,21 @@ TEXT runtime·sched_getaffinity(SB),7,$0
RET
// int32 runtime·epollcreate(int32 size);
-TEXT runtime·epollcreate(SB),7,$0
+TEXT runtime·epollcreate(SB),NOSPLIT,$0
MOVL $254, AX
MOVL 4(SP), BX
CALL *runtime·_vdso(SB)
RET
// int32 runtime·epollcreate1(int32 flags);
-TEXT runtime·epollcreate1(SB),7,$0
+TEXT runtime·epollcreate1(SB),NOSPLIT,$0
MOVL $329, AX
MOVL 4(SP), BX
CALL *runtime·_vdso(SB)
RET
// int32 runtime·epollctl(int32 epfd, int32 op, int32 fd, EpollEvent *ev);
-TEXT runtime·epollctl(SB),7,$0
+TEXT runtime·epollctl(SB),NOSPLIT,$0
MOVL $255, AX
MOVL 4(SP), BX
MOVL 8(SP), CX
@@ -456,7 +458,7 @@ TEXT runtime·epollctl(SB),7,$0
RET
// int32 runtime·epollwait(int32 epfd, EpollEvent *ev, int32 nev, int32 timeout);
-TEXT runtime·epollwait(SB),7,$0
+TEXT runtime·epollwait(SB),NOSPLIT,$0
MOVL $256, AX
MOVL 4(SP), BX
MOVL 8(SP), CX
@@ -466,7 +468,7 @@ TEXT runtime·epollwait(SB),7,$0
RET
// void runtime·closeonexec(int32 fd);
-TEXT runtime·closeonexec(SB),7,$0
+TEXT runtime·closeonexec(SB),NOSPLIT,$0
MOVL $55, AX // fcntl
MOVL 4(SP), BX // fd
MOVL $2, CX // F_SETFD
diff --git a/src/pkg/runtime/sys_linux_amd64.s b/src/pkg/runtime/sys_linux_amd64.s
index 2d802abb6..481841a67 100644
--- a/src/pkg/runtime/sys_linux_amd64.s
+++ b/src/pkg/runtime/sys_linux_amd64.s
@@ -7,20 +7,21 @@
//
#include "zasm_GOOS_GOARCH.h"
+#include "../../cmd/ld/textflag.h"
-TEXT runtime·exit(SB),7,$0-8
+TEXT runtime·exit(SB),NOSPLIT,$0-8
MOVL 8(SP), DI
MOVL $231, AX // exitgroup - force all os threads to exit
SYSCALL
RET
-TEXT runtime·exit1(SB),7,$0-8
+TEXT runtime·exit1(SB),NOSPLIT,$0-8
MOVL 8(SP), DI
MOVL $60, AX // exit - exit the current os thread
SYSCALL
RET
-TEXT runtime·open(SB),7,$0-16
+TEXT runtime·open(SB),NOSPLIT,$0-16
MOVQ 8(SP), DI
MOVL 16(SP), SI
MOVL 20(SP), DX
@@ -28,13 +29,13 @@ TEXT runtime·open(SB),7,$0-16
SYSCALL
RET
-TEXT runtime·close(SB),7,$0-16
+TEXT runtime·close(SB),NOSPLIT,$0-16
MOVL 8(SP), DI
MOVL $3, AX // syscall entry
SYSCALL
RET
-TEXT runtime·write(SB),7,$0-24
+TEXT runtime·write(SB),NOSPLIT,$0-24
MOVL 8(SP), DI
MOVQ 16(SP), SI
MOVL 24(SP), DX
@@ -42,7 +43,7 @@ TEXT runtime·write(SB),7,$0-24
SYSCALL
RET
-TEXT runtime·read(SB),7,$0-24
+TEXT runtime·read(SB),NOSPLIT,$0-24
MOVL 8(SP), DI
MOVQ 16(SP), SI
MOVL 24(SP), DX
@@ -50,14 +51,14 @@ TEXT runtime·read(SB),7,$0-24
SYSCALL
RET
-TEXT runtime·getrlimit(SB),7,$0-24
+TEXT runtime·getrlimit(SB),NOSPLIT,$0-24
MOVL 8(SP), DI
MOVQ 16(SP), SI
MOVL $97, AX // syscall entry
SYSCALL
RET
-TEXT runtime·usleep(SB),7,$16
+TEXT runtime·usleep(SB),NOSPLIT,$16
MOVL $0, DX
MOVL usec+0(FP), AX
MOVL $1000000, CX
@@ -75,7 +76,7 @@ TEXT runtime·usleep(SB),7,$16
SYSCALL
RET
-TEXT runtime·raise(SB),7,$12
+TEXT runtime·raise(SB),NOSPLIT,$12
MOVL $186, AX // syscall - gettid
SYSCALL
MOVL AX, DI // arg 1 tid
@@ -84,7 +85,7 @@ TEXT runtime·raise(SB),7,$12
SYSCALL
RET
-TEXT runtime·setitimer(SB),7,$0-24
+TEXT runtime·setitimer(SB),NOSPLIT,$0-24
MOVL 8(SP), DI
MOVQ 16(SP), SI
MOVQ 24(SP), DX
@@ -92,7 +93,7 @@ TEXT runtime·setitimer(SB),7,$0-24
SYSCALL
RET
-TEXT runtime·mincore(SB),7,$0-24
+TEXT runtime·mincore(SB),NOSPLIT,$0-24
MOVQ 8(SP), DI
MOVQ 16(SP), SI
MOVQ 24(SP), DX
@@ -101,7 +102,7 @@ TEXT runtime·mincore(SB),7,$0-24
RET
// func now() (sec int64, nsec int32)
-TEXT time·now(SB),7,$16
+TEXT time·now(SB),NOSPLIT,$16
// Be careful. We're calling a function with gcc calling convention here.
// We're guaranteed 128 bytes on entry, and we've taken 16, and the
// call uses another 8.
@@ -129,7 +130,7 @@ fallback_gtod:
MOVL DX, nsec+8(FP)
RET
-TEXT runtime·nanotime(SB),7,$16
+TEXT runtime·nanotime(SB),NOSPLIT,$16
// Duplicate time.now here to avoid using up precious stack space.
// See comment above in time.now.
MOVQ runtime·__vdso_clock_gettime_sym(SB), AX
@@ -159,7 +160,7 @@ fallback_gtod_nt:
ADDQ DX, AX
RET
-TEXT runtime·rtsigprocmask(SB),7,$0-32
+TEXT runtime·rtsigprocmask(SB),NOSPLIT,$0-32
MOVL 8(SP), DI
MOVQ 16(SP), SI
MOVQ 24(SP), DX
@@ -171,7 +172,7 @@ TEXT runtime·rtsigprocmask(SB),7,$0-32
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·rt_sigaction(SB),7,$0-32
+TEXT runtime·rt_sigaction(SB),NOSPLIT,$0-32
MOVL 8(SP), DI
MOVQ 16(SP), SI
MOVQ 24(SP), DX
@@ -180,15 +181,16 @@ TEXT runtime·rt_sigaction(SB),7,$0-32
SYSCALL
RET
-TEXT runtime·sigtramp(SB),7,$64
+TEXT runtime·sigtramp(SB),NOSPLIT,$64
get_tls(BX)
// check that m exists
MOVQ m(BX), BP
CMPQ BP, $0
- JNE 4(PC)
+ JNE 5(PC)
MOVQ DI, 0(SP)
- CALL runtime·badsignal(SB)
+ MOVQ $runtime·badsignal(SB), AX
+ CALL AX
RET
// save g
@@ -212,12 +214,12 @@ TEXT runtime·sigtramp(SB),7,$64
MOVQ R10, g(BX)
RET
-TEXT runtime·sigreturn(SB),7,$0
+TEXT runtime·sigreturn(SB),NOSPLIT,$0
MOVL $15, AX // rt_sigreturn
SYSCALL
INT $3 // not reached
-TEXT runtime·mmap(SB),7,$0
+TEXT runtime·mmap(SB),NOSPLIT,$0
MOVQ 8(SP), DI
MOVQ $0, SI
MOVQ 16(SP), SI
@@ -234,7 +236,7 @@ TEXT runtime·mmap(SB),7,$0
INCQ AX
RET
-TEXT runtime·munmap(SB),7,$0
+TEXT runtime·munmap(SB),NOSPLIT,$0
MOVQ 8(SP), DI
MOVQ 16(SP), SI
MOVQ $11, AX // munmap
@@ -244,7 +246,7 @@ TEXT runtime·munmap(SB),7,$0
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·madvise(SB),7,$0
+TEXT runtime·madvise(SB),NOSPLIT,$0
MOVQ 8(SP), DI
MOVQ 16(SP), SI
MOVQ 24(SP), DX
@@ -255,7 +257,7 @@ TEXT runtime·madvise(SB),7,$0
// int64 futex(int32 *uaddr, int32 op, int32 val,
// struct timespec *timeout, int32 *uaddr2, int32 val2);
-TEXT runtime·futex(SB),7,$0
+TEXT runtime·futex(SB),NOSPLIT,$0
MOVQ 8(SP), DI
MOVL 16(SP), SI
MOVL 20(SP), DX
@@ -267,7 +269,7 @@ TEXT runtime·futex(SB),7,$0
RET
// int64 clone(int32 flags, void *stack, M *mp, G *gp, void (*fn)(void));
-TEXT runtime·clone(SB),7,$0
+TEXT runtime·clone(SB),NOSPLIT,$0
MOVL flags+8(SP), DI
MOVQ stack+16(SP), SI
@@ -312,7 +314,7 @@ TEXT runtime·clone(SB),7,$0
SYSCALL
JMP -3(PC) // keep exiting
-TEXT runtime·sigaltstack(SB),7,$-8
+TEXT runtime·sigaltstack(SB),NOSPLIT,$-8
MOVQ new+8(SP), DI
MOVQ old+16(SP), SI
MOVQ $131, AX
@@ -323,7 +325,7 @@ TEXT runtime·sigaltstack(SB),7,$-8
RET
// set tls base to DI
-TEXT runtime·settls(SB),7,$32
+TEXT runtime·settls(SB),NOSPLIT,$32
ADDQ $16, DI // ELF wants to use -16(FS), -8(FS)
MOVQ DI, SI
@@ -335,12 +337,12 @@ TEXT runtime·settls(SB),7,$32
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·osyield(SB),7,$0
+TEXT runtime·osyield(SB),NOSPLIT,$0
MOVL $24, AX
SYSCALL
RET
-TEXT runtime·sched_getaffinity(SB),7,$0
+TEXT runtime·sched_getaffinity(SB),NOSPLIT,$0
MOVQ 8(SP), DI
MOVL 16(SP), SI
MOVQ 24(SP), DX
@@ -349,21 +351,21 @@ TEXT runtime·sched_getaffinity(SB),7,$0
RET
// int32 runtime·epollcreate(int32 size);
-TEXT runtime·epollcreate(SB),7,$0
+TEXT runtime·epollcreate(SB),NOSPLIT,$0
MOVL 8(SP), DI
MOVL $213, AX // syscall entry
SYSCALL
RET
// int32 runtime·epollcreate1(int32 flags);
-TEXT runtime·epollcreate1(SB),7,$0
+TEXT runtime·epollcreate1(SB),NOSPLIT,$0
MOVL 8(SP), DI
MOVL $291, AX // syscall entry
SYSCALL
RET
// int32 runtime·epollctl(int32 epfd, int32 op, int32 fd, EpollEvent *ev);
-TEXT runtime·epollctl(SB),7,$0
+TEXT runtime·epollctl(SB),NOSPLIT,$0
MOVL 8(SP), DI
MOVL 12(SP), SI
MOVL 16(SP), DX
@@ -373,7 +375,7 @@ TEXT runtime·epollctl(SB),7,$0
RET
// int32 runtime·epollwait(int32 epfd, EpollEvent *ev, int32 nev, int32 timeout);
-TEXT runtime·epollwait(SB),7,$0
+TEXT runtime·epollwait(SB),NOSPLIT,$0
MOVL 8(SP), DI
MOVQ 16(SP), SI
MOVL 24(SP), DX
@@ -383,7 +385,7 @@ TEXT runtime·epollwait(SB),7,$0
RET
// void runtime·closeonexec(int32 fd);
-TEXT runtime·closeonexec(SB),7,$0
+TEXT runtime·closeonexec(SB),NOSPLIT,$0
MOVL 8(SP), DI // fd
MOVQ $2, SI // F_SETFD
MOVQ $1, DX // FD_CLOEXEC
diff --git a/src/pkg/runtime/sys_linux_arm.s b/src/pkg/runtime/sys_linux_arm.s
index 7f813482d..42aef56a7 100644
--- a/src/pkg/runtime/sys_linux_arm.s
+++ b/src/pkg/runtime/sys_linux_arm.s
@@ -7,6 +7,7 @@
//
#include "zasm_GOOS_GOARCH.h"
+#include "../../cmd/ld/textflag.h"
// for EABI, as we don't support OABI
#define SYS_BASE 0x0
@@ -44,7 +45,7 @@
#define ARM_BASE (SYS_BASE + 0x0f0000)
-TEXT runtime·open(SB),7,$0
+TEXT runtime·open(SB),NOSPLIT,$0
MOVW 0(FP), R0
MOVW 4(FP), R1
MOVW 8(FP), R2
@@ -52,13 +53,13 @@ TEXT runtime·open(SB),7,$0
SWI $0
RET
-TEXT runtime·close(SB),7,$0
+TEXT runtime·close(SB),NOSPLIT,$0
MOVW 0(FP), R0
MOVW $SYS_close, R7
SWI $0
RET
-TEXT runtime·write(SB),7,$0
+TEXT runtime·write(SB),NOSPLIT,$0
MOVW 0(FP), R0
MOVW 4(FP), R1
MOVW 8(FP), R2
@@ -66,7 +67,7 @@ TEXT runtime·write(SB),7,$0
SWI $0
RET
-TEXT runtime·read(SB),7,$0
+TEXT runtime·read(SB),NOSPLIT,$0
MOVW 0(FP), R0
MOVW 4(FP), R1
MOVW 8(FP), R2
@@ -74,14 +75,14 @@ TEXT runtime·read(SB),7,$0
SWI $0
RET
-TEXT runtime·getrlimit(SB),7,$0
+TEXT runtime·getrlimit(SB),NOSPLIT,$0
MOVW 0(FP), R0
MOVW 4(FP), R1
MOVW $SYS_ugetrlimit, R7
SWI $0
RET
-TEXT runtime·exit(SB),7,$-4
+TEXT runtime·exit(SB),NOSPLIT,$-4
MOVW 0(FP), R0
MOVW $SYS_exit_group, R7
SWI $0
@@ -89,7 +90,7 @@ TEXT runtime·exit(SB),7,$-4
MOVW $1002, R1
MOVW R0, (R1) // fail hard
-TEXT runtime·exit1(SB),7,$-4
+TEXT runtime·exit1(SB),NOSPLIT,$-4
MOVW 0(FP), R0
MOVW $SYS_exit, R7
SWI $0
@@ -97,7 +98,7 @@ TEXT runtime·exit1(SB),7,$-4
MOVW $1003, R1
MOVW R0, (R1) // fail hard
-TEXT runtime·raise(SB),7,$-4
+TEXT runtime·raise(SB),NOSPLIT,$-4
MOVW $SYS_gettid, R7
SWI $0
// arg 1 tid already in R0 from gettid
@@ -106,7 +107,7 @@ TEXT runtime·raise(SB),7,$-4
SWI $0
RET
-TEXT runtime·mmap(SB),7,$0
+TEXT runtime·mmap(SB),NOSPLIT,$0
MOVW 0(FP), R0
MOVW 4(FP), R1
MOVW 8(FP), R2
@@ -120,18 +121,18 @@ TEXT runtime·mmap(SB),7,$0
RSB.HI $0, R0
RET
-TEXT runtime·munmap(SB),7,$0
+TEXT runtime·munmap(SB),NOSPLIT,$0
MOVW 0(FP), R0
MOVW 4(FP), R1
MOVW $SYS_munmap, R7
SWI $0
MOVW $0xfffff001, R6
CMP R6, R0
- MOVW.HI $0, R9 // crash on syscall failure
- MOVW.HI R9, (R9)
+ MOVW.HI $0, R8 // crash on syscall failure
+ MOVW.HI R8, (R8)
RET
-TEXT runtime·madvise(SB),7,$0
+TEXT runtime·madvise(SB),NOSPLIT,$0
MOVW 0(FP), R0
MOVW 4(FP), R1
MOVW 8(FP), R2
@@ -140,7 +141,7 @@ TEXT runtime·madvise(SB),7,$0
// ignore failure - maybe pages are locked
RET
-TEXT runtime·setitimer(SB),7,$0
+TEXT runtime·setitimer(SB),NOSPLIT,$0
MOVW 0(FP), R0
MOVW 4(FP), R1
MOVW 8(FP), R2
@@ -148,7 +149,7 @@ TEXT runtime·setitimer(SB),7,$0
SWI $0
RET
-TEXT runtime·mincore(SB),7,$0
+TEXT runtime·mincore(SB),NOSPLIT,$0
MOVW 0(FP), R0
MOVW 4(FP), R1
MOVW 8(FP), R2
@@ -156,7 +157,7 @@ TEXT runtime·mincore(SB),7,$0
SWI $0
RET
-TEXT time·now(SB), 7, $32
+TEXT time·now(SB), NOSPLIT, $32
MOVW $0, R0 // CLOCK_REALTIME
MOVW $8(R13), R1 // timespec
MOVW $SYS_clock_gettime, R7
@@ -173,7 +174,7 @@ TEXT time·now(SB), 7, $32
// int64 nanotime(void) so really
// void nanotime(int64 *nsec)
-TEXT runtime·nanotime(SB),7,$32
+TEXT runtime·nanotime(SB),NOSPLIT,$32
MOVW $0, R0 // CLOCK_REALTIME
MOVW $8(R13), R1 // timespec
MOVW $SYS_clock_gettime, R7
@@ -195,7 +196,7 @@ TEXT runtime·nanotime(SB),7,$32
// int32 futex(int32 *uaddr, int32 op, int32 val,
// struct timespec *timeout, int32 *uaddr2, int32 val2);
-TEXT runtime·futex(SB),7,$0
+TEXT runtime·futex(SB),NOSPLIT,$0
MOVW 4(SP), R0
MOVW 8(SP), R1
MOVW 12(SP), R2
@@ -208,7 +209,7 @@ TEXT runtime·futex(SB),7,$0
// int32 clone(int32 flags, void *stack, M *mp, G *gp, void (*fn)(void));
-TEXT runtime·clone(SB),7,$0
+TEXT runtime·clone(SB),NOSPLIT,$0
MOVW flags+0(FP), R0
MOVW stack+4(FP), R1
MOVW $0, R2 // parent tid ptr
@@ -271,26 +272,32 @@ TEXT runtime·clone(SB),7,$0
MOVW $1005, R1
MOVW R0, (R1)
-TEXT runtime·sigaltstack(SB),7,$0
+TEXT runtime·sigaltstack(SB),NOSPLIT,$0
MOVW 0(FP), R0
MOVW 4(FP), R1
MOVW $SYS_sigaltstack, R7
SWI $0
MOVW $0xfffff001, R6
CMP R6, R0
- MOVW.HI $0, R9 // crash on syscall failure
- MOVW.HI R9, (R9)
+ MOVW.HI $0, R8 // crash on syscall failure
+ MOVW.HI R8, (R8)
RET
-TEXT runtime·sigtramp(SB),7,$24
+TEXT runtime·sigtramp(SB),NOSPLIT,$24
// this might be called in external code context,
// where g and m are not set.
- // first save R0, because _cgo_load_gm will clobber it
- // TODO(adonovan): call runtime·badsignal if m=0, like other platforms?
+ // first save R0, because runtime·load_gm will clobber it
MOVW R0, 4(R13)
- MOVW _cgo_load_gm(SB), R0
+ MOVB runtime·iscgo(SB), R0
CMP $0, R0
- BL.NE (R0)
+ BL.NE runtime·load_gm(SB)
+
+ CMP $0, m
+ BNE 4(PC)
+ // signal number is already prepared in 4(R13)
+ MOVW $runtime·badsignal(SB), R11
+ BL (R11)
+ RET
// save g
MOVW g, R3
@@ -312,7 +319,7 @@ TEXT runtime·sigtramp(SB),7,$24
RET
-TEXT runtime·rtsigprocmask(SB),7,$0
+TEXT runtime·rtsigprocmask(SB),NOSPLIT,$0
MOVW 0(FP), R0
MOVW 4(FP), R1
MOVW 8(FP), R2
@@ -321,7 +328,7 @@ TEXT runtime·rtsigprocmask(SB),7,$0
SWI $0
RET
-TEXT runtime·rt_sigaction(SB),7,$0
+TEXT runtime·rt_sigaction(SB),NOSPLIT,$0
MOVW 0(FP), R0
MOVW 4(FP), R1
MOVW 8(FP), R2
@@ -330,12 +337,12 @@ TEXT runtime·rt_sigaction(SB),7,$0
SWI $0
RET
-TEXT runtime·sigreturn(SB),7,$0
+TEXT runtime·sigreturn(SB),NOSPLIT,$0
MOVW $SYS_rt_sigreturn, R7
SWI $0
RET
-TEXT runtime·usleep(SB),7,$12
+TEXT runtime·usleep(SB),NOSPLIT,$12
MOVW usec+0(FP), R0
MOVW R0, R1
MOVW $1000000, R2
@@ -354,17 +361,17 @@ TEXT runtime·usleep(SB),7,$12
// Use kernel version instead of native armcas in asm_arm.s.
// See ../sync/atomic/asm_linux_arm.s for details.
-TEXT cas<>(SB),7,$0
+TEXT cas<>(SB),NOSPLIT,$0
MOVW $0xffff0fc0, PC
-TEXT runtime·cas(SB),7,$0
+TEXT runtime·cas(SB),NOSPLIT,$0
MOVW valptr+0(FP), R2
MOVW old+4(FP), R0
casagain:
MOVW new+8(FP), R1
BL cas<>(SB)
BCC cascheck
- MOVW $1, R0
+ MOVW $1, R0
RET
cascheck:
// Kernel lies; double-check.
@@ -373,18 +380,18 @@ cascheck:
MOVW 0(R2), R3
CMP R0, R3
BEQ casagain
- MOVW $0, R0
+ MOVW $0, R0
RET
-TEXT runtime·casp(SB),7,$0
+TEXT runtime·casp(SB),NOSPLIT,$0
B runtime·cas(SB)
-TEXT runtime·osyield(SB),7,$0
+TEXT runtime·osyield(SB),NOSPLIT,$0
MOVW $SYS_sched_yield, R7
SWI $0
RET
-TEXT runtime·sched_getaffinity(SB),7,$0
+TEXT runtime·sched_getaffinity(SB),NOSPLIT,$0
MOVW 0(FP), R0
MOVW 4(FP), R1
MOVW 8(FP), R2
@@ -393,21 +400,21 @@ TEXT runtime·sched_getaffinity(SB),7,$0
RET
// int32 runtime·epollcreate(int32 size)
-TEXT runtime·epollcreate(SB),7,$0
+TEXT runtime·epollcreate(SB),NOSPLIT,$0
MOVW 0(FP), R0
MOVW $SYS_epoll_create, R7
SWI $0
RET
// int32 runtime·epollcreate1(int32 flags)
-TEXT runtime·epollcreate1(SB),7,$0
+TEXT runtime·epollcreate1(SB),NOSPLIT,$0
MOVW 0(FP), R0
MOVW $SYS_epoll_create1, R7
SWI $0
RET
// int32 runtime·epollctl(int32 epfd, int32 op, int32 fd, EpollEvent *ev)
-TEXT runtime·epollctl(SB),7,$0
+TEXT runtime·epollctl(SB),NOSPLIT,$0
MOVW 0(FP), R0
MOVW 4(FP), R1
MOVW 8(FP), R2
@@ -417,7 +424,7 @@ TEXT runtime·epollctl(SB),7,$0
RET
// int32 runtime·epollwait(int32 epfd, EpollEvent *ev, int32 nev, int32 timeout)
-TEXT runtime·epollwait(SB),7,$0
+TEXT runtime·epollwait(SB),NOSPLIT,$0
MOVW 0(FP), R0
MOVW 4(FP), R1
MOVW 8(FP), R2
@@ -427,10 +434,15 @@ TEXT runtime·epollwait(SB),7,$0
RET
// void runtime·closeonexec(int32 fd)
-TEXT runtime·closeonexec(SB),7,$0
+TEXT runtime·closeonexec(SB),NOSPLIT,$0
MOVW 0(FP), R0 // fd
MOVW $2, R1 // F_SETFD
MOVW $1, R2 // FD_CLOEXEC
MOVW $SYS_fcntl, R7
- SWI $0
+ SWI $0
RET
+
+// b __kuser_get_tls @ 0xffff0fe0
+TEXT runtime·read_tls_fallback(SB),NOSPLIT,$-4
+ MOVW $0xffff0fe0, R0
+ B (R0)
diff --git a/src/pkg/runtime/sys_netbsd_386.s b/src/pkg/runtime/sys_netbsd_386.s
index 992eba77d..05de55e93 100644
--- a/src/pkg/runtime/sys_netbsd_386.s
+++ b/src/pkg/runtime/sys_netbsd_386.s
@@ -7,42 +7,43 @@
//
#include "zasm_GOOS_GOARCH.h"
+#include "../../cmd/ld/textflag.h"
// Exit the entire program (like C exit)
-TEXT runtime·exit(SB),7,$-4
+TEXT runtime·exit(SB),NOSPLIT,$-4
MOVL $1, AX
INT $0x80
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·exit1(SB),7,$-4
+TEXT runtime·exit1(SB),NOSPLIT,$-4
MOVL $310, AX // sys__lwp_exit
INT $0x80
JAE 2(PC)
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·open(SB),7,$-4
+TEXT runtime·open(SB),NOSPLIT,$-4
MOVL $5, AX
INT $0x80
RET
-TEXT runtime·close(SB),7,$-4
+TEXT runtime·close(SB),NOSPLIT,$-4
MOVL $6, AX
INT $0x80
RET
-TEXT runtime·read(SB),7,$-4
+TEXT runtime·read(SB),NOSPLIT,$-4
MOVL $3, AX
INT $0x80
RET
-TEXT runtime·write(SB),7,$-4
+TEXT runtime·write(SB),NOSPLIT,$-4
MOVL $4, AX // sys_write
INT $0x80
RET
-TEXT runtime·usleep(SB),7,$24
+TEXT runtime·usleep(SB),NOSPLIT,$24
MOVL $0, DX
MOVL usec+0(FP), AX
MOVL $1000000, CX
@@ -61,7 +62,7 @@ TEXT runtime·usleep(SB),7,$24
INT $0x80
RET
-TEXT runtime·raise(SB),7,$12
+TEXT runtime·raise(SB),NOSPLIT,$12
MOVL $311, AX // sys__lwp_self
INT $0x80
MOVL $0, 0(SP)
@@ -72,7 +73,7 @@ TEXT runtime·raise(SB),7,$12
INT $0x80
RET
-TEXT runtime·mmap(SB),7,$36
+TEXT runtime·mmap(SB),NOSPLIT,$36
LEAL arg0+0(FP), SI
LEAL 4(SP), DI
CLD
@@ -90,26 +91,26 @@ TEXT runtime·mmap(SB),7,$36
INT $0x80
RET
-TEXT runtime·munmap(SB),7,$-4
+TEXT runtime·munmap(SB),NOSPLIT,$-4
MOVL $73, AX // sys_munmap
INT $0x80
JAE 2(PC)
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·madvise(SB),7,$-4
+TEXT runtime·madvise(SB),NOSPLIT,$-4
MOVL $75, AX // sys_madvise
INT $0x80
// ignore failure - maybe pages are locked
RET
-TEXT runtime·setitimer(SB),7,$-4
+TEXT runtime·setitimer(SB),NOSPLIT,$-4
MOVL $425, AX // sys_setitimer
INT $0x80
RET
// func now() (sec int64, nsec int32)
-TEXT time·now(SB), 7, $32
+TEXT time·now(SB), NOSPLIT, $32
LEAL 12(SP), BX
MOVL $0, 4(SP) // arg 1 - clock_id
MOVL BX, 8(SP) // arg 2 - tp
@@ -127,7 +128,7 @@ TEXT time·now(SB), 7, $32
// int64 nanotime(void) so really
// void nanotime(int64 *nsec)
-TEXT runtime·nanotime(SB),7,$32
+TEXT runtime·nanotime(SB),NOSPLIT,$32
LEAL 12(SP), BX
MOVL $0, 4(SP) // arg 1 - clock_id
MOVL BX, 8(SP) // arg 2 - tp
@@ -150,21 +151,21 @@ TEXT runtime·nanotime(SB),7,$32
MOVL DX, 4(DI)
RET
-TEXT runtime·getcontext(SB),7,$-4
+TEXT runtime·getcontext(SB),NOSPLIT,$-4
MOVL $307, AX // sys_getcontext
INT $0x80
JAE 2(PC)
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·sigprocmask(SB),7,$-4
+TEXT runtime·sigprocmask(SB),NOSPLIT,$-4
MOVL $293, AX // sys_sigprocmask
INT $0x80
JAE 2(PC)
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·sigreturn_tramp(SB),7,$0
+TEXT runtime·sigreturn_tramp(SB),NOSPLIT,$0
LEAL 140(SP), AX // Load address of ucontext
MOVL AX, 4(SP)
MOVL $308, AX // sys_setcontext
@@ -173,7 +174,7 @@ TEXT runtime·sigreturn_tramp(SB),7,$0
MOVL $1, AX // sys_exit
INT $0x80
-TEXT runtime·sigaction(SB),7,$24
+TEXT runtime·sigaction(SB),NOSPLIT,$24
LEAL arg0+0(FP), SI
LEAL 4(SP), DI
CLD
@@ -190,16 +191,17 @@ TEXT runtime·sigaction(SB),7,$24
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·sigtramp(SB),7,$44
+TEXT runtime·sigtramp(SB),NOSPLIT,$44
get_tls(CX)
// check that m exists
MOVL m(CX), BX
CMPL BX, $0
- JNE 5(PC)
+ JNE 6(PC)
MOVL signo+0(FP), BX
MOVL BX, 0(SP)
- CALL runtime·badsignal(SB)
+ MOVL $runtime·badsignal(SB), AX
+ CALL AX
RET
// save g
@@ -228,7 +230,7 @@ TEXT runtime·sigtramp(SB),7,$44
RET
// int32 lwp_create(void *context, uintptr flags, void *lwpid);
-TEXT runtime·lwp_create(SB),7,$16
+TEXT runtime·lwp_create(SB),NOSPLIT,$16
MOVL $0, 0(SP)
MOVL context+0(FP), AX
MOVL AX, 4(SP) // arg 1 - context
@@ -242,7 +244,7 @@ TEXT runtime·lwp_create(SB),7,$16
NEGL AX
RET
-TEXT runtime·lwp_tramp(SB),7,$0
+TEXT runtime·lwp_tramp(SB),NOSPLIT,$0
// Set FS to point at m->tls
LEAL m_tls(BX), BP
@@ -273,7 +275,7 @@ TEXT runtime·lwp_tramp(SB),7,$0
MOVL $0x1234, 0x1005
RET
-TEXT runtime·sigaltstack(SB),7,$-8
+TEXT runtime·sigaltstack(SB),NOSPLIT,$-8
MOVL $281, AX // sys___sigaltstack14
MOVL new+4(SP), BX
MOVL old+8(SP), CX
@@ -283,14 +285,14 @@ TEXT runtime·sigaltstack(SB),7,$-8
INT $3
RET
-TEXT runtime·setldt(SB),7,$8
+TEXT runtime·setldt(SB),NOSPLIT,$8
// Under NetBSD we set the GS base instead of messing with the LDT.
MOVL 16(SP), AX // tls0
MOVL AX, 0(SP)
CALL runtime·settls(SB)
RET
-TEXT runtime·settls(SB),7,$16
+TEXT runtime·settls(SB),NOSPLIT,$16
// adjust for ELF: wants to use -8(GS) and -4(GS) for g and m
MOVL base+0(FP), CX
ADDL $8, CX
@@ -302,27 +304,27 @@ TEXT runtime·settls(SB),7,$16
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·osyield(SB),7,$-4
+TEXT runtime·osyield(SB),NOSPLIT,$-4
MOVL $350, AX // sys_sched_yield
INT $0x80
RET
-TEXT runtime·lwp_park(SB),7,$-4
+TEXT runtime·lwp_park(SB),NOSPLIT,$-4
MOVL $434, AX // sys__lwp_park
INT $0x80
RET
-TEXT runtime·lwp_unpark(SB),7,$-4
+TEXT runtime·lwp_unpark(SB),NOSPLIT,$-4
MOVL $321, AX // sys__lwp_unpark
INT $0x80
RET
-TEXT runtime·lwp_self(SB),7,$-4
+TEXT runtime·lwp_self(SB),NOSPLIT,$-4
MOVL $311, AX // sys__lwp_self
INT $0x80
RET
-TEXT runtime·sysctl(SB),7,$28
+TEXT runtime·sysctl(SB),NOSPLIT,$28
LEAL arg0+0(FP), SI
LEAL 4(SP), DI
CLD
@@ -341,3 +343,32 @@ TEXT runtime·sysctl(SB),7,$28
RET
GLOBL runtime·tlsoffset(SB),$4
+
+// int32 runtime·kqueue(void)
+TEXT runtime·kqueue(SB),NOSPLIT,$0
+ MOVL $344, AX
+ INT $0x80
+ JAE 2(PC)
+ NEGL AX
+ RET
+
+// int32 runtime·kevent(int kq, Kevent *changelist, int nchanges, Kevent *eventlist, int nevents, Timespec *timeout)
+TEXT runtime·kevent(SB),NOSPLIT,$0
+ MOVL $435, AX
+ INT $0x80
+ JAE 2(PC)
+ NEGL AX
+ RET
+
+// int32 runtime·closeonexec(int32 fd)
+TEXT runtime·closeonexec(SB),NOSPLIT,$32
+ MOVL $92, AX // fcntl
+ // 0(SP) is where the caller PC would be; kernel skips it
+ MOVL fd+0(FP), BX
+ MOVL BX, 4(SP) // fd
+ MOVL $2, 8(SP) // F_SETFD
+ MOVL $1, 12(SP) // FD_CLOEXEC
+ INT $0x80
+ JAE 2(PC)
+ NEGL AX
+ RET
diff --git a/src/pkg/runtime/sys_netbsd_amd64.s b/src/pkg/runtime/sys_netbsd_amd64.s
index 574d8a91b..fcbced548 100644
--- a/src/pkg/runtime/sys_netbsd_amd64.s
+++ b/src/pkg/runtime/sys_netbsd_amd64.s
@@ -7,9 +7,10 @@
//
#include "zasm_GOOS_GOARCH.h"
+#include "../../cmd/ld/textflag.h"
// int32 lwp_create(void *context, uintptr flags, void *lwpid)
-TEXT runtime·lwp_create(SB),7,$0
+TEXT runtime·lwp_create(SB),NOSPLIT,$0
MOVQ context+0(FP), DI
MOVQ flags+8(FP), SI
MOVQ lwpid+16(FP), DX
@@ -19,7 +20,7 @@ TEXT runtime·lwp_create(SB),7,$0
NEGQ AX
RET
-TEXT runtime·lwp_tramp(SB),7,$0
+TEXT runtime·lwp_tramp(SB),NOSPLIT,$0
// Set FS to point at m->tls.
LEAQ m_tls(R8), DI
@@ -39,12 +40,12 @@ TEXT runtime·lwp_tramp(SB),7,$0
SYSCALL
JMP -3(PC) // keep exiting
-TEXT runtime·osyield(SB),7,$0
+TEXT runtime·osyield(SB),NOSPLIT,$0
MOVL $350, AX // sys_sched_yield
SYSCALL
RET
-TEXT runtime·lwp_park(SB),7,$0
+TEXT runtime·lwp_park(SB),NOSPLIT,$0
MOVQ 8(SP), DI // arg 1 - abstime
MOVL 16(SP), SI // arg 2 - unpark
MOVQ 24(SP), DX // arg 3 - hint
@@ -53,33 +54,33 @@ TEXT runtime·lwp_park(SB),7,$0
SYSCALL
RET
-TEXT runtime·lwp_unpark(SB),7,$0
+TEXT runtime·lwp_unpark(SB),NOSPLIT,$0
MOVQ 8(SP), DI // arg 1 - lwp
MOVL 16(SP), SI // arg 2 - hint
MOVL $321, AX // sys__lwp_unpark
SYSCALL
RET
-TEXT runtime·lwp_self(SB),7,$0
+TEXT runtime·lwp_self(SB),NOSPLIT,$0
MOVL $311, AX // sys__lwp_self
SYSCALL
RET
// Exit the entire program (like C exit)
-TEXT runtime·exit(SB),7,$-8
+TEXT runtime·exit(SB),NOSPLIT,$-8
MOVL 8(SP), DI // arg 1 - exit status
MOVL $1, AX // sys_exit
SYSCALL
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·exit1(SB),7,$-8
+TEXT runtime·exit1(SB),NOSPLIT,$-8
MOVL $310, AX // sys__lwp_exit
SYSCALL
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·open(SB),7,$-8
+TEXT runtime·open(SB),NOSPLIT,$-8
MOVQ 8(SP), DI // arg 1 pathname
MOVL 16(SP), SI // arg 2 flags
MOVL 20(SP), DX // arg 3 mode
@@ -87,13 +88,13 @@ TEXT runtime·open(SB),7,$-8
SYSCALL
RET
-TEXT runtime·close(SB),7,$-8
+TEXT runtime·close(SB),NOSPLIT,$-8
MOVL 8(SP), DI // arg 1 fd
MOVL $6, AX
SYSCALL
RET
-TEXT runtime·read(SB),7,$-8
+TEXT runtime·read(SB),NOSPLIT,$-8
MOVL 8(SP), DI // arg 1 fd
MOVQ 16(SP), SI // arg 2 buf
MOVL 24(SP), DX // arg 3 count
@@ -101,7 +102,7 @@ TEXT runtime·read(SB),7,$-8
SYSCALL
RET
-TEXT runtime·write(SB),7,$-8
+TEXT runtime·write(SB),NOSPLIT,$-8
MOVL 8(SP), DI // arg 1 - fd
MOVQ 16(SP), SI // arg 2 - buf
MOVL 24(SP), DX // arg 3 - nbyte
@@ -109,7 +110,7 @@ TEXT runtime·write(SB),7,$-8
SYSCALL
RET
-TEXT runtime·usleep(SB),7,$16
+TEXT runtime·usleep(SB),NOSPLIT,$16
MOVL $0, DX
MOVL usec+0(FP), AX
MOVL $1000000, CX
@@ -125,7 +126,7 @@ TEXT runtime·usleep(SB),7,$16
SYSCALL
RET
-TEXT runtime·raise(SB),7,$16
+TEXT runtime·raise(SB),NOSPLIT,$16
MOVL $311, AX // sys__lwp_self
SYSCALL
MOVQ AX, DI // arg 1 - target
@@ -134,7 +135,7 @@ TEXT runtime·raise(SB),7,$16
SYSCALL
RET
-TEXT runtime·setitimer(SB),7,$-8
+TEXT runtime·setitimer(SB),NOSPLIT,$-8
MOVL 8(SP), DI // arg 1 - which
MOVQ 16(SP), SI // arg 2 - itv
MOVQ 24(SP), DX // arg 3 - oitv
@@ -143,7 +144,7 @@ TEXT runtime·setitimer(SB),7,$-8
RET
// func now() (sec int64, nsec int32)
-TEXT time·now(SB), 7, $32
+TEXT time·now(SB), NOSPLIT, $32
MOVQ $0, DI // arg 1 - clock_id
LEAQ 8(SP), SI // arg 2 - tp
MOVL $427, AX // sys_clock_gettime
@@ -156,7 +157,7 @@ TEXT time·now(SB), 7, $32
MOVL DX, nsec+8(FP)
RET
-TEXT runtime·nanotime(SB),7,$32
+TEXT runtime·nanotime(SB),NOSPLIT,$32
MOVQ $0, DI // arg 1 - clock_id
LEAQ 8(SP), SI // arg 2 - tp
MOVL $427, AX // sys_clock_gettime
@@ -170,7 +171,7 @@ TEXT runtime·nanotime(SB),7,$32
ADDQ DX, AX
RET
-TEXT runtime·getcontext(SB),7,$-8
+TEXT runtime·getcontext(SB),NOSPLIT,$-8
MOVQ 8(SP), DI // arg 1 - context
MOVL $307, AX // sys_getcontext
SYSCALL
@@ -178,7 +179,7 @@ TEXT runtime·getcontext(SB),7,$-8
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·sigprocmask(SB),7,$0
+TEXT runtime·sigprocmask(SB),NOSPLIT,$0
MOVL 8(SP), DI // arg 1 - how
MOVQ 16(SP), SI // arg 2 - set
MOVQ 24(SP), DX // arg 3 - oset
@@ -188,7 +189,7 @@ TEXT runtime·sigprocmask(SB),7,$0
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·sigreturn_tramp(SB),7,$-8
+TEXT runtime·sigreturn_tramp(SB),NOSPLIT,$-8
MOVQ R15, DI // Load address of ucontext
MOVQ $308, AX // sys_setcontext
SYSCALL
@@ -196,7 +197,7 @@ TEXT runtime·sigreturn_tramp(SB),7,$-8
MOVL $1, AX // sys_exit
SYSCALL
-TEXT runtime·sigaction(SB),7,$-8
+TEXT runtime·sigaction(SB),NOSPLIT,$-8
MOVL 8(SP), DI // arg 1 - signum
MOVQ 16(SP), SI // arg 2 - nsa
MOVQ 24(SP), DX // arg 3 - osa
@@ -209,15 +210,16 @@ TEXT runtime·sigaction(SB),7,$-8
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·sigtramp(SB),7,$64
+TEXT runtime·sigtramp(SB),NOSPLIT,$64
get_tls(BX)
// check that m exists
MOVQ m(BX), BP
CMPQ BP, $0
- JNE 4(PC)
+ JNE 5(PC)
MOVQ DI, 0(SP)
- CALL runtime·badsignal(SB)
+ MOVQ $runtime·badsignal(SB), AX
+ CALL AX
RET
// save g
@@ -241,7 +243,7 @@ TEXT runtime·sigtramp(SB),7,$64
MOVQ R10, g(BX)
RET
-TEXT runtime·mmap(SB),7,$0
+TEXT runtime·mmap(SB),NOSPLIT,$0
MOVQ 8(SP), DI // arg 1 - addr
MOVQ 16(SP), SI // arg 2 - len
MOVL 24(SP), DX // arg 3 - prot
@@ -256,7 +258,7 @@ TEXT runtime·mmap(SB),7,$0
ADDQ $16, SP
RET
-TEXT runtime·munmap(SB),7,$0
+TEXT runtime·munmap(SB),NOSPLIT,$0
MOVQ 8(SP), DI // arg 1 - addr
MOVQ 16(SP), SI // arg 2 - len
MOVL $73, AX // sys_munmap
@@ -266,7 +268,7 @@ TEXT runtime·munmap(SB),7,$0
RET
-TEXT runtime·madvise(SB),7,$0
+TEXT runtime·madvise(SB),NOSPLIT,$0
MOVQ addr+0(FP), DI // arg 1 - addr
MOVQ len+8(FP), SI // arg 2 - len
MOVQ behav+16(FP), DX // arg 3 - behav
@@ -275,7 +277,7 @@ TEXT runtime·madvise(SB),7,$0
// ignore failure - maybe pages are locked
RET
-TEXT runtime·sigaltstack(SB),7,$-8
+TEXT runtime·sigaltstack(SB),NOSPLIT,$-8
MOVQ new+8(SP), DI // arg 1 - nss
MOVQ old+16(SP), SI // arg 2 - oss
MOVQ $281, AX // sys___sigaltstack14
@@ -285,7 +287,7 @@ TEXT runtime·sigaltstack(SB),7,$-8
RET
// set tls base to DI
-TEXT runtime·settls(SB),7,$8
+TEXT runtime·settls(SB),NOSPLIT,$8
// adjust for ELF: wants to use -16(FS) and -8(FS) for g and m
ADDQ $16, DI // arg 1 - ptr
MOVQ $317, AX // sys__lwp_setprivate
@@ -294,7 +296,7 @@ TEXT runtime·settls(SB),7,$8
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·sysctl(SB),7,$0
+TEXT runtime·sysctl(SB),NOSPLIT,$0
MOVQ 8(SP), DI // arg 1 - name
MOVL 16(SP), SI // arg 2 - namelen
MOVQ 24(SP), DX // arg 3 - oldp
@@ -309,3 +311,34 @@ TEXT runtime·sysctl(SB),7,$0
MOVL $0, AX
RET
+// int32 runtime·kqueue(void)
+TEXT runtime·kqueue(SB),NOSPLIT,$0
+ MOVQ $0, DI
+ MOVL $344, AX
+ SYSCALL
+ JCC 2(PC)
+ NEGQ AX
+ RET
+
+// int32 runtime·kevent(int kq, Kevent *changelist, int nchanges, Kevent *eventlist, int nevents, Timespec *timeout)
+TEXT runtime·kevent(SB),NOSPLIT,$0
+ MOVL 8(SP), DI
+ MOVQ 16(SP), SI
+ MOVL 24(SP), DX
+ MOVQ 32(SP), R10
+ MOVL 40(SP), R8
+ MOVQ 48(SP), R9
+ MOVL $435, AX
+ SYSCALL
+ JCC 2(PC)
+ NEGQ AX
+ RET
+
+// void runtime·closeonexec(int32 fd)
+TEXT runtime·closeonexec(SB),NOSPLIT,$0
+ MOVL 8(SP), DI // fd
+ MOVQ $2, SI // F_SETFD
+ MOVQ $1, DX // FD_CLOEXEC
+ MOVL $92, AX // fcntl
+ SYSCALL
+ RET
diff --git a/src/pkg/runtime/sys_netbsd_arm.s b/src/pkg/runtime/sys_netbsd_arm.s
index 3ff335f4d..b2eb74ee3 100644
--- a/src/pkg/runtime/sys_netbsd_arm.s
+++ b/src/pkg/runtime/sys_netbsd_arm.s
@@ -7,41 +7,42 @@
//
#include "zasm_GOOS_GOARCH.h"
+#include "../../cmd/ld/textflag.h"
// Exit the entire program (like C exit)
-TEXT runtime·exit(SB),7,$-4
+TEXT runtime·exit(SB),NOSPLIT,$-4
MOVW 0(FP), R0 // arg 1 exit status
SWI $0xa00001
- MOVW.CS $0, R9 // crash on syscall failure
- MOVW.CS R9, (R9)
+ MOVW.CS $0, R8 // crash on syscall failure
+ MOVW.CS R8, (R8)
RET
-TEXT runtime·exit1(SB),7,$-4
+TEXT runtime·exit1(SB),NOSPLIT,$-4
SWI $0xa00136 // sys__lwp_exit
- MOVW $1, R9 // crash
- MOVW R9, (R9)
+ MOVW $1, R8 // crash
+ MOVW R8, (R8)
RET
-TEXT runtime·open(SB),7,$-8
+TEXT runtime·open(SB),NOSPLIT,$-8
MOVW 0(FP), R0
MOVW 4(FP), R1
MOVW 8(FP), R2
SWI $0xa00005
RET
-TEXT runtime·close(SB),7,$-8
+TEXT runtime·close(SB),NOSPLIT,$-8
MOVW 0(FP), R0
SWI $0xa00006
RET
-TEXT runtime·read(SB),7,$-8
+TEXT runtime·read(SB),NOSPLIT,$-8
MOVW 0(FP), R0
MOVW 4(FP), R1
MOVW 8(FP), R2
SWI $0xa00003
RET
-TEXT runtime·write(SB),7,$-4
+TEXT runtime·write(SB),NOSPLIT,$-4
MOVW 0(FP), R0 // arg 1 - fd
MOVW 4(FP), R1 // arg 2 - buf
MOVW 8(FP), R2 // arg 3 - nbyte
@@ -49,18 +50,18 @@ TEXT runtime·write(SB),7,$-4
RET
// int32 lwp_create(void *context, uintptr flags, void *lwpid)
-TEXT runtime·lwp_create(SB),7,$0
+TEXT runtime·lwp_create(SB),NOSPLIT,$0
MOVW context+0(FP), R0
MOVW flags+4(FP), R1
MOVW lwpid+8(FP), R2
SWI $0xa00135 // sys__lwp_create
RET
-TEXT runtime·osyield(SB),7,$0
+TEXT runtime·osyield(SB),NOSPLIT,$0
SWI $0xa0015e // sys_sched_yield
RET
-TEXT runtime·lwp_park(SB),7,$0
+TEXT runtime·lwp_park(SB),NOSPLIT,$0
MOVW 0(FP), R0 // arg 1 - abstime
MOVW 4(FP), R1 // arg 2 - unpark
MOVW 8(FP), R2 // arg 3 - hint
@@ -68,27 +69,27 @@ TEXT runtime·lwp_park(SB),7,$0
SWI $0xa001b2 // sys__lwp_park
RET
-TEXT runtime·lwp_unpark(SB),7,$0
+TEXT runtime·lwp_unpark(SB),NOSPLIT,$0
MOVW 0(FP), R0 // arg 1 - lwp
MOVW 4(FP), R1 // arg 2 - hint
SWI $0xa00141 // sys__lwp_unpark
RET
-TEXT runtime·lwp_self(SB),7,$0
+TEXT runtime·lwp_self(SB),NOSPLIT,$0
SWI $0xa00137 // sys__lwp_self
RET
-TEXT runtime·lwp_tramp(SB),7,$0
- MOVW R0, R9 // m
- MOVW R1, R10 // g
+TEXT runtime·lwp_tramp(SB),NOSPLIT,$0
+ MOVW R0, m
+ MOVW R1, g
BL runtime·emptyfunc(SB) // fault if stack check is wrong
BL (R2)
- MOVW $2, R9 // crash (not reached)
- MOVW R9, (R9)
+ MOVW $2, R8 // crash (not reached)
+ MOVW R8, (R8)
RET
-TEXT runtime·usleep(SB),7,$16
+TEXT runtime·usleep(SB),NOSPLIT,$16
MOVW usec+0(FP), R0
MOVW R0, R2
MOVW $1000000, R1
@@ -107,13 +108,13 @@ TEXT runtime·usleep(SB),7,$16
SWI $0xa001ae // sys_nanosleep
RET
-TEXT runtime·raise(SB),7,$16
+TEXT runtime·raise(SB),NOSPLIT,$16
SWI $0xa00137 // sys__lwp_self, the returned R0 is arg 1
MOVW sig+0(FP), R1 // arg 2 - signal
SWI $0xa0013e // sys__lwp_kill
RET
-TEXT runtime·setitimer(SB),7,$-4
+TEXT runtime·setitimer(SB),NOSPLIT,$-4
MOVW 0(FP), R0 // arg 1 - which
MOVW 4(FP), R1 // arg 2 - itv
MOVW 8(FP), R2 // arg 3 - oitv
@@ -121,7 +122,7 @@ TEXT runtime·setitimer(SB),7,$-4
RET
// func now() (sec int64, nsec int32)
-TEXT time·now(SB), 7, $32
+TEXT time·now(SB), NOSPLIT, $32
MOVW $0, R0 // CLOCK_REALTIME
MOVW $8(R13), R1
SWI $0xa001ab // clock_gettime
@@ -137,7 +138,7 @@ TEXT time·now(SB), 7, $32
// int64 nanotime(void) so really
// void nanotime(int64 *nsec)
-TEXT runtime·nanotime(SB), 7, $32
+TEXT runtime·nanotime(SB), NOSPLIT, $32
MOVW $0, R0 // CLOCK_REALTIME
MOVW $8(R13), R1
SWI $0xa001ab // clock_gettime
@@ -157,33 +158,33 @@ TEXT runtime·nanotime(SB), 7, $32
MOVW R1, 4(R3)
RET
-TEXT runtime·getcontext(SB),7,$-4
+TEXT runtime·getcontext(SB),NOSPLIT,$-4
MOVW 0(FP), R0 // arg 1 - context
SWI $0xa00133 // sys_getcontext
- MOVW.CS $0, R9 // crash on syscall failure
- MOVW.CS R9, (R9)
+ MOVW.CS $0, R8 // crash on syscall failure
+ MOVW.CS R8, (R8)
RET
-TEXT runtime·sigprocmask(SB),7,$0
+TEXT runtime·sigprocmask(SB),NOSPLIT,$0
MOVW 0(FP), R0 // arg 1 - how
MOVW 4(FP), R1 // arg 2 - set
MOVW 8(FP), R2 // arg 3 - oset
SWI $0xa00125 // sys_sigprocmask
- MOVW.CS $0, R9 // crash on syscall failure
- MOVW.CS R9, (R9)
+ MOVW.CS $0, R8 // crash on syscall failure
+ MOVW.CS R8, (R8)
RET
-TEXT runtime·sigreturn_tramp(SB),7,$-4
- // in runtime·sigtramp, we saved ucontext into m->tls[0],
- // here we just load it and call sys_setcontext
- MOVW m_tls(m), R0
+TEXT runtime·sigreturn_tramp(SB),NOSPLIT,$-4
+ // on entry, SP points to siginfo, we add sizeof(ucontext)
+ // to SP to get a pointer to ucontext.
+ ADD $0x80, R13, R0 // 0x80 == sizeof(UcontextT)
SWI $0xa00134 // sys_setcontext
// something failed, we have to exit
MOVW $0x4242, R0 // magic return number
SWI $0xa00001 // sys_exit
B -2(PC) // continue exit
-TEXT runtime·sigaction(SB),7,$4
+TEXT runtime·sigaction(SB),NOSPLIT,$4
MOVW 0(FP), R0 // arg 1 - signum
MOVW 4(FP), R1 // arg 2 - nsa
MOVW 8(FP), R2 // arg 3 - osa
@@ -193,42 +194,45 @@ TEXT runtime·sigaction(SB),7,$4
ADD $4, R13 // pass arg 5 on stack
SWI $0xa00154 // sys___sigaction_sigtramp
SUB $4, R13
- MOVW.CS $3, R9 // crash on syscall failure
- MOVW.CS R9, (R9)
+ MOVW.CS $3, R8 // crash on syscall failure
+ MOVW.CS R8, (R8)
RET
-TEXT runtime·sigtramp(SB),7,$24
+TEXT runtime·sigtramp(SB),NOSPLIT,$24
// this might be called in external code context,
// where g and m are not set.
- // first save R0, because _cgo_load_gm will clobber it
- // TODO(adonovan): call runtime·badsignal if m=0, like other platforms?
+ // first save R0, because runtime·load_gm will clobber it
MOVW R0, 4(R13) // signum
- MOVW _cgo_load_gm(SB), R0
+ MOVB runtime·iscgo(SB), R0
CMP $0, R0
- BL.NE (R0)
+ BL.NE runtime·load_gm(SB)
+
+ CMP $0, m
+ BNE 4(PC)
+ // signal number is already prepared in 4(R13)
+ MOVW $runtime·badsignal(SB), R11
+ BL (R11)
+ RET
// save g
- MOVW R10, R4
- MOVW R10, 20(R13)
+ MOVW g, R4
+ MOVW g, 20(R13)
// g = m->signal
- MOVW m_gsignal(R9), R10
+ MOVW m_gsignal(m), g
// R0 is already saved
MOVW R1, 8(R13) // info
MOVW R2, 12(R13) // context
MOVW R4, 16(R13) // gp
- // we also save the ucontext into m->tls[0] for easy
- // signal return
- MOVW R2, m_tls(m)
BL runtime·sighandler(SB)
// restore g
- MOVW 20(R13), R10
+ MOVW 20(R13), g
RET
-TEXT runtime·mmap(SB),7,$12
+TEXT runtime·mmap(SB),NOSPLIT,$12
MOVW 0(FP), R0 // arg 1 - addr
MOVW 4(FP), R1 // arg 2 - len
MOVW 8(FP), R2 // arg 3 - prot
@@ -246,15 +250,15 @@ TEXT runtime·mmap(SB),7,$12
SUB $4, R13
RET
-TEXT runtime·munmap(SB),7,$0
+TEXT runtime·munmap(SB),NOSPLIT,$0
MOVW 0(FP), R0 // arg 1 - addr
MOVW 4(FP), R1 // arg 2 - len
SWI $0xa00049 // sys_munmap
- MOVW.CS $0, R9 // crash on syscall failure
- MOVW.CS R9, (R9)
+ MOVW.CS $0, R8 // crash on syscall failure
+ MOVW.CS R8, (R8)
RET
-TEXT runtime·madvise(SB),7,$0
+TEXT runtime·madvise(SB),NOSPLIT,$0
MOVW 0(FP), R0 // arg 1 - addr
MOVW 4(FP), R1 // arg 2 - len
MOVW 8(FP), R2 // arg 3 - behav
@@ -262,15 +266,15 @@ TEXT runtime·madvise(SB),7,$0
// ignore failure - maybe pages are locked
RET
-TEXT runtime·sigaltstack(SB),7,$-4
+TEXT runtime·sigaltstack(SB),NOSPLIT,$-4
MOVW 0(FP), R0 // arg 1 - nss
MOVW 4(FP), R1 // arg 2 - oss
SWI $0xa00119 // sys___sigaltstack14
- MOVW.CS $0, R9 // crash on syscall failure
- MOVW.CS R9, (R9)
+ MOVW.CS $0, R8 // crash on syscall failure
+ MOVW.CS R8, (R8)
RET
-TEXT runtime·sysctl(SB),7,$8
+TEXT runtime·sysctl(SB),NOSPLIT,$8
MOVW 0(FP), R0 // arg 1 - name
MOVW 4(FP), R1 // arg 2 - namelen
MOVW 8(FP), R2 // arg 3 - oldp
@@ -284,7 +288,37 @@ TEXT runtime·sysctl(SB),7,$8
SUB $4, R13
RET
-TEXT runtime·casp(SB),7,$0
+// int32 runtime·kqueue(void)
+TEXT runtime·kqueue(SB),NOSPLIT,$0
+ SWI $0xa00158 // sys_kqueue
+ RSB.CS $0, R0
+ RET
+
+// int32 runtime·kevent(int kq, Kevent *changelist, int nchanges, Kevent *eventlist, int nevents, Timespec *timeout)
+TEXT runtime·kevent(SB),NOSPLIT,$8
+ MOVW 0(FP), R0 // kq
+ MOVW 4(FP), R1 // changelist
+ MOVW 8(FP), R2 // nchanges
+ MOVW 12(FP), R3 // eventlist
+ MOVW 16(FP), R4 // nevents
+ MOVW R4, 4(R13)
+ MOVW 20(FP), R4 // timeout
+ MOVW R4, 8(R13)
+ ADD $4, R13 // pass arg 5 and 6 on stack
+ SWI $0xa001b3 // sys___kevent50
+ RSB.CS $0, R0
+ SUB $4, R13
+ RET
+
+// void runtime·closeonexec(int32 fd)
+TEXT runtime·closeonexec(SB),NOSPLIT,$0
+ MOVW 0(FP), R0 // fd
+ MOVW $2, R1 // F_SETFD
+ MOVW $1, R2 // FD_CLOEXEC
+ SWI $0xa0005c // sys_fcntl
+ RET
+
+TEXT runtime·casp(SB),NOSPLIT,$0
B runtime·cas(SB)
// TODO(minux): this is only valid for ARMv6+
@@ -295,5 +329,11 @@ TEXT runtime·casp(SB),7,$0
// return 1;
// }else
// return 0;
-TEXT runtime·cas(SB),7,$0
+TEXT runtime·cas(SB),NOSPLIT,$0
B runtime·armcas(SB)
+
+TEXT runtime·read_tls_fallback(SB),NOSPLIT,$-4
+ MOVM.WP [R1, R2, R3, R12], (R13)
+ SWI $0x00a0013c // _lwp_getprivate
+ MOVM.IAW (R13), [R1, R2, R3, R12]
+ RET
diff --git a/src/pkg/runtime/sys_openbsd_386.s b/src/pkg/runtime/sys_openbsd_386.s
index 37b6ff215..e1ec5337a 100644
--- a/src/pkg/runtime/sys_openbsd_386.s
+++ b/src/pkg/runtime/sys_openbsd_386.s
@@ -7,15 +7,16 @@
//
#include "zasm_GOOS_GOARCH.h"
+#include "../../cmd/ld/textflag.h"
// Exit the entire program (like C exit)
-TEXT runtime·exit(SB),7,$-4
+TEXT runtime·exit(SB),NOSPLIT,$-4
MOVL $1, AX
INT $0x80
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·exit1(SB),7,$8
+TEXT runtime·exit1(SB),NOSPLIT,$8
MOVL $0, 0(SP)
MOVL $0, 4(SP) // arg 1 - notdead
MOVL $302, AX // sys___threxit
@@ -24,27 +25,27 @@ TEXT runtime·exit1(SB),7,$8
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·open(SB),7,$-4
+TEXT runtime·open(SB),NOSPLIT,$-4
MOVL $5, AX
INT $0x80
RET
-TEXT runtime·close(SB),7,$-4
+TEXT runtime·close(SB),NOSPLIT,$-4
MOVL $6, AX
INT $0x80
RET
-TEXT runtime·read(SB),7,$-4
+TEXT runtime·read(SB),NOSPLIT,$-4
MOVL $3, AX
INT $0x80
RET
-TEXT runtime·write(SB),7,$-4
+TEXT runtime·write(SB),NOSPLIT,$-4
MOVL $4, AX // sys_write
INT $0x80
RET
-TEXT runtime·usleep(SB),7,$20
+TEXT runtime·usleep(SB),NOSPLIT,$20
MOVL $0, DX
MOVL usec+0(FP), AX
MOVL $1000000, CX
@@ -62,7 +63,7 @@ TEXT runtime·usleep(SB),7,$20
INT $0x80
RET
-TEXT runtime·raise(SB),7,$12
+TEXT runtime·raise(SB),NOSPLIT,$12
MOVL $299, AX // sys_getthrid
INT $0x80
MOVL $0, 0(SP)
@@ -73,7 +74,7 @@ TEXT runtime·raise(SB),7,$12
INT $0x80
RET
-TEXT runtime·mmap(SB),7,$36
+TEXT runtime·mmap(SB),NOSPLIT,$36
LEAL arg0+0(FP), SI
LEAL 4(SP), DI
CLD
@@ -91,27 +92,27 @@ TEXT runtime·mmap(SB),7,$36
INT $0x80
RET
-TEXT runtime·munmap(SB),7,$-4
+TEXT runtime·munmap(SB),NOSPLIT,$-4
MOVL $73, AX // sys_munmap
INT $0x80
JAE 2(PC)
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·madvise(SB),7,$-4
+TEXT runtime·madvise(SB),NOSPLIT,$-4
MOVL $75, AX // sys_madvise
INT $0x80
JAE 2(PC)
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·setitimer(SB),7,$-4
+TEXT runtime·setitimer(SB),NOSPLIT,$-4
MOVL $83, AX
INT $0x80
RET
// func now() (sec int64, nsec int32)
-TEXT time·now(SB), 7, $32
+TEXT time·now(SB), NOSPLIT, $32
MOVL $232, AX
LEAL 12(SP), BX
MOVL $0, 4(SP)
@@ -128,7 +129,7 @@ TEXT time·now(SB), 7, $32
// int64 nanotime(void) so really
// void nanotime(int64 *nsec)
-TEXT runtime·nanotime(SB),7,$32
+TEXT runtime·nanotime(SB),NOSPLIT,$32
MOVL $232, AX
LEAL 12(SP), BX
MOVL $0, 4(SP)
@@ -149,14 +150,14 @@ TEXT runtime·nanotime(SB),7,$32
MOVL DX, 4(DI)
RET
-TEXT runtime·sigaction(SB),7,$-4
+TEXT runtime·sigaction(SB),NOSPLIT,$-4
MOVL $46, AX // sys_sigaction
INT $0x80
JAE 2(PC)
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·sigprocmask(SB),7,$-4
+TEXT runtime·sigprocmask(SB),NOSPLIT,$-4
MOVL $48, AX // sys_sigprocmask
INT $0x80
JAE 2(PC)
@@ -164,17 +165,18 @@ TEXT runtime·sigprocmask(SB),7,$-4
MOVL AX, oset+0(FP)
RET
-TEXT runtime·sigtramp(SB),7,$44
+TEXT runtime·sigtramp(SB),NOSPLIT,$44
get_tls(CX)
// check that m exists
MOVL m(CX), BX
CMPL BX, $0
- JNE 5(PC)
+ JNE 6(PC)
MOVL signo+0(FP), BX
MOVL BX, 0(SP)
- CALL runtime·badsignal(SB)
- RET
+ MOVL $runtime·badsignal(SB), AX
+ CALL AX
+ JMP sigtramp_ret
// save g
MOVL g(CX), DI
@@ -199,7 +201,8 @@ TEXT runtime·sigtramp(SB),7,$44
get_tls(CX)
MOVL 20(SP), BX
MOVL BX, g(CX)
-
+
+sigtramp_ret:
// call sigreturn
MOVL context+8(FP), AX
MOVL $0, 0(SP) // syscall gap
@@ -210,7 +213,7 @@ TEXT runtime·sigtramp(SB),7,$44
RET
// int32 tfork(void *param, uintptr psize, M *mp, G *gp, void (*fn)(void));
-TEXT runtime·tfork(SB),7,$12
+TEXT runtime·tfork(SB),NOSPLIT,$12
// Copy mp, gp and fn from the parent stack onto the child stack.
MOVL params+4(FP), AX
@@ -287,7 +290,7 @@ TEXT runtime·tfork(SB),7,$12
MOVL $0x1234, 0x1005
RET
-TEXT runtime·sigaltstack(SB),7,$-8
+TEXT runtime·sigaltstack(SB),NOSPLIT,$-8
MOVL $288, AX // sys_sigaltstack
MOVL new+4(SP), BX
MOVL old+8(SP), CX
@@ -297,14 +300,14 @@ TEXT runtime·sigaltstack(SB),7,$-8
INT $3
RET
-TEXT runtime·setldt(SB),7,$4
+TEXT runtime·setldt(SB),NOSPLIT,$4
// Under OpenBSD we set the GS base instead of messing with the LDT.
MOVL tls0+4(FP), AX
MOVL AX, 0(SP)
CALL runtime·settls(SB)
RET
-TEXT runtime·settls(SB),7,$8
+TEXT runtime·settls(SB),NOSPLIT,$8
// adjust for ELF: wants to use -8(GS) and -4(GS) for g and m
MOVL tlsbase+0(FP), CX
ADDL $8, CX
@@ -316,22 +319,22 @@ TEXT runtime·settls(SB),7,$8
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·osyield(SB),7,$-4
+TEXT runtime·osyield(SB),NOSPLIT,$-4
MOVL $298, AX // sys_sched_yield
INT $0x80
RET
-TEXT runtime·thrsleep(SB),7,$-4
+TEXT runtime·thrsleep(SB),NOSPLIT,$-4
MOVL $300, AX // sys___thrsleep
INT $0x80
RET
-TEXT runtime·thrwakeup(SB),7,$-4
+TEXT runtime·thrwakeup(SB),NOSPLIT,$-4
MOVL $301, AX // sys___thrwakeup
INT $0x80
RET
-TEXT runtime·sysctl(SB),7,$28
+TEXT runtime·sysctl(SB),NOSPLIT,$28
LEAL arg0+0(FP), SI
LEAL 4(SP), DI
CLD
@@ -349,4 +352,33 @@ TEXT runtime·sysctl(SB),7,$28
MOVL $0, AX
RET
+// int32 runtime·kqueue(void);
+TEXT runtime·kqueue(SB),NOSPLIT,$0
+ MOVL $269, AX
+ INT $0x80
+ JAE 2(PC)
+ NEGL AX
+ RET
+
+// int32 runtime·kevent(int kq, Kevent *changelist, int nchanges, Kevent *eventlist, int nevents, Timespec *timeout);
+TEXT runtime·kevent(SB),NOSPLIT,$0
+ MOVL $270, AX
+ INT $0x80
+ JAE 2(PC)
+ NEGL AX
+ RET
+
+// int32 runtime·closeonexec(int32 fd);
+TEXT runtime·closeonexec(SB),NOSPLIT,$32
+ MOVL $92, AX // fcntl
+ // 0(SP) is where the caller PC would be; kernel skips it
+ MOVL fd+0(FP), BX
+ MOVL BX, 4(SP) // fd
+ MOVL $2, 8(SP) // F_SETFD
+ MOVL $1, 12(SP) // FD_CLOEXEC
+ INT $0x80
+ JAE 2(PC)
+ NEGL AX
+ RET
+
GLOBL runtime·tlsoffset(SB),$4
diff --git a/src/pkg/runtime/sys_openbsd_amd64.s b/src/pkg/runtime/sys_openbsd_amd64.s
index cbd2c2f76..26dc61f71 100644
--- a/src/pkg/runtime/sys_openbsd_amd64.s
+++ b/src/pkg/runtime/sys_openbsd_amd64.s
@@ -7,9 +7,10 @@
//
#include "zasm_GOOS_GOARCH.h"
+#include "../../cmd/ld/textflag.h"
// int64 tfork(void *param, uintptr psize, M *mp, G *gp, void (*fn)(void));
-TEXT runtime·tfork(SB),7,$32
+TEXT runtime·tfork(SB),NOSPLIT,$32
// Copy mp, gp and fn off parent stack for use by child.
MOVQ mm+16(FP), R8
@@ -50,12 +51,12 @@ TEXT runtime·tfork(SB),7,$32
SYSCALL
JMP -3(PC) // keep exiting
-TEXT runtime·osyield(SB),7,$0
+TEXT runtime·osyield(SB),NOSPLIT,$0
MOVL $298, AX // sys_sched_yield
SYSCALL
RET
-TEXT runtime·thrsleep(SB),7,$0
+TEXT runtime·thrsleep(SB),NOSPLIT,$0
MOVQ 8(SP), DI // arg 1 - ident
MOVL 16(SP), SI // arg 2 - clock_id
MOVQ 24(SP), DX // arg 3 - tp
@@ -65,7 +66,7 @@ TEXT runtime·thrsleep(SB),7,$0
SYSCALL
RET
-TEXT runtime·thrwakeup(SB),7,$0
+TEXT runtime·thrwakeup(SB),NOSPLIT,$0
MOVQ 8(SP), DI // arg 1 - ident
MOVL 16(SP), SI // arg 2 - n
MOVL $301, AX // sys___thrwakeup
@@ -73,21 +74,21 @@ TEXT runtime·thrwakeup(SB),7,$0
RET
// Exit the entire program (like C exit)
-TEXT runtime·exit(SB),7,$-8
+TEXT runtime·exit(SB),NOSPLIT,$-8
MOVL 8(SP), DI // arg 1 - exit status
MOVL $1, AX // sys_exit
SYSCALL
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·exit1(SB),7,$-8
+TEXT runtime·exit1(SB),NOSPLIT,$-8
MOVQ $0, DI // arg 1 - notdead
MOVL $302, AX // sys___threxit
SYSCALL
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·open(SB),7,$-8
+TEXT runtime·open(SB),NOSPLIT,$-8
MOVQ 8(SP), DI // arg 1 pathname
MOVL 16(SP), SI // arg 2 flags
MOVL 20(SP), DX // arg 3 mode
@@ -95,13 +96,13 @@ TEXT runtime·open(SB),7,$-8
SYSCALL
RET
-TEXT runtime·close(SB),7,$-8
+TEXT runtime·close(SB),NOSPLIT,$-8
MOVL 8(SP), DI // arg 1 fd
MOVL $6, AX
SYSCALL
RET
-TEXT runtime·read(SB),7,$-8
+TEXT runtime·read(SB),NOSPLIT,$-8
MOVL 8(SP), DI // arg 1 fd
MOVQ 16(SP), SI // arg 2 buf
MOVL 24(SP), DX // arg 3 count
@@ -109,7 +110,7 @@ TEXT runtime·read(SB),7,$-8
SYSCALL
RET
-TEXT runtime·write(SB),7,$-8
+TEXT runtime·write(SB),NOSPLIT,$-8
MOVL 8(SP), DI // arg 1 - fd
MOVQ 16(SP), SI // arg 2 - buf
MOVL 24(SP), DX // arg 3 - nbyte
@@ -117,7 +118,7 @@ TEXT runtime·write(SB),7,$-8
SYSCALL
RET
-TEXT runtime·usleep(SB),7,$16
+TEXT runtime·usleep(SB),NOSPLIT,$16
MOVL $0, DX
MOVL usec+0(FP), AX
MOVL $1000000, CX
@@ -133,7 +134,7 @@ TEXT runtime·usleep(SB),7,$16
SYSCALL
RET
-TEXT runtime·raise(SB),7,$16
+TEXT runtime·raise(SB),NOSPLIT,$16
MOVL $299, AX // sys_getthrid
SYSCALL
MOVQ AX, DI // arg 1 - pid
@@ -142,7 +143,7 @@ TEXT runtime·raise(SB),7,$16
SYSCALL
RET
-TEXT runtime·setitimer(SB),7,$-8
+TEXT runtime·setitimer(SB),NOSPLIT,$-8
MOVL 8(SP), DI // arg 1 - which
MOVQ 16(SP), SI // arg 2 - itv
MOVQ 24(SP), DX // arg 3 - oitv
@@ -151,7 +152,7 @@ TEXT runtime·setitimer(SB),7,$-8
RET
// func now() (sec int64, nsec int32)
-TEXT time·now(SB), 7, $32
+TEXT time·now(SB), NOSPLIT, $32
MOVQ $0, DI // arg 1 - clock_id
LEAQ 8(SP), SI // arg 2 - tp
MOVL $232, AX // sys_clock_gettime
@@ -164,7 +165,7 @@ TEXT time·now(SB), 7, $32
MOVL DX, nsec+8(FP)
RET
-TEXT runtime·nanotime(SB),7,$32
+TEXT runtime·nanotime(SB),NOSPLIT,$24
MOVQ $0, DI // arg 1 - clock_id
LEAQ 8(SP), SI // arg 2 - tp
MOVL $232, AX // sys_clock_gettime
@@ -178,7 +179,7 @@ TEXT runtime·nanotime(SB),7,$32
ADDQ DX, AX
RET
-TEXT runtime·sigaction(SB),7,$-8
+TEXT runtime·sigaction(SB),NOSPLIT,$-8
MOVL 8(SP), DI // arg 1 - signum
MOVQ 16(SP), SI // arg 2 - nsa
MOVQ 24(SP), DX // arg 3 - osa
@@ -188,7 +189,7 @@ TEXT runtime·sigaction(SB),7,$-8
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·sigprocmask(SB),7,$0
+TEXT runtime·sigprocmask(SB),NOSPLIT,$0
MOVL 8(SP), DI // arg 1 - how
MOVL 12(SP), SI // arg 2 - set
MOVL $48, AX // sys_sigprocmask
@@ -198,15 +199,16 @@ TEXT runtime·sigprocmask(SB),7,$0
MOVL AX, oset+0(FP) // Return oset
RET
-TEXT runtime·sigtramp(SB),7,$64
+TEXT runtime·sigtramp(SB),NOSPLIT,$64
get_tls(BX)
// check that m exists
MOVQ m(BX), BP
CMPQ BP, $0
- JNE 4(PC)
+ JNE 5(PC)
MOVQ DI, 0(SP)
- CALL runtime·badsignal(SB)
+ MOVQ $runtime·badsignal(SB), AX
+ CALL AX
RET
// save g
@@ -230,7 +232,7 @@ TEXT runtime·sigtramp(SB),7,$64
MOVQ R10, g(BX)
RET
-TEXT runtime·mmap(SB),7,$0
+TEXT runtime·mmap(SB),NOSPLIT,$0
MOVQ 8(SP), DI // arg 1 - addr
MOVQ 16(SP), SI // arg 2 - len
MOVL 24(SP), DX // arg 3 - prot
@@ -245,7 +247,7 @@ TEXT runtime·mmap(SB),7,$0
ADDQ $16, SP
RET
-TEXT runtime·munmap(SB),7,$0
+TEXT runtime·munmap(SB),NOSPLIT,$0
MOVQ 8(SP), DI // arg 1 - addr
MOVQ 16(SP), SI // arg 2 - len
MOVL $73, AX // sys_munmap
@@ -254,7 +256,7 @@ TEXT runtime·munmap(SB),7,$0
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·madvise(SB),7,$0
+TEXT runtime·madvise(SB),NOSPLIT,$0
MOVQ addr+0(FP), DI // arg 1 - addr
MOVQ len+8(FP), SI // arg 2 - len
MOVQ behav+16(FP), DX // arg 3 - behav
@@ -263,7 +265,7 @@ TEXT runtime·madvise(SB),7,$0
// ignore failure - maybe pages are locked
RET
-TEXT runtime·sigaltstack(SB),7,$-8
+TEXT runtime·sigaltstack(SB),NOSPLIT,$-8
MOVQ new+8(SP), DI // arg 1 - nss
MOVQ old+16(SP), SI // arg 2 - oss
MOVQ $288, AX // sys_sigaltstack
@@ -273,7 +275,7 @@ TEXT runtime·sigaltstack(SB),7,$-8
RET
// set tls base to DI
-TEXT runtime·settls(SB),7,$0
+TEXT runtime·settls(SB),NOSPLIT,$0
// adjust for ELF: wants to use -16(FS) and -8(FS) for g and m
ADDQ $16, DI
MOVQ $329, AX // sys___settcb
@@ -282,7 +284,7 @@ TEXT runtime·settls(SB),7,$0
MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·sysctl(SB),7,$0
+TEXT runtime·sysctl(SB),NOSPLIT,$0
MOVQ 8(SP), DI // arg 1 - name
MOVL 16(SP), SI // arg 2 - namelen
MOVQ 24(SP), DX // arg 3 - oldp
@@ -297,3 +299,36 @@ TEXT runtime·sysctl(SB),7,$0
MOVL $0, AX
RET
+// int32 runtime·kqueue(void);
+TEXT runtime·kqueue(SB),NOSPLIT,$0
+ MOVQ $0, DI
+ MOVQ $0, SI
+ MOVQ $0, DX
+ MOVL $269, AX
+ SYSCALL
+ JCC 2(PC)
+ NEGQ AX
+ RET
+
+// int32 runtime·kevent(int kq, Kevent *changelist, int nchanges, Kevent *eventlist, int nevents, Timespec *timeout);
+TEXT runtime·kevent(SB),NOSPLIT,$0
+ MOVL 8(SP), DI
+ MOVQ 16(SP), SI
+ MOVL 24(SP), DX
+ MOVQ 32(SP), R10
+ MOVL 40(SP), R8
+ MOVQ 48(SP), R9
+ MOVL $270, AX
+ SYSCALL
+ JCC 2(PC)
+ NEGQ AX
+ RET
+
+// void runtime·closeonexec(int32 fd);
+TEXT runtime·closeonexec(SB),NOSPLIT,$0
+ MOVL 8(SP), DI // fd
+ MOVQ $2, SI // F_SETFD
+ MOVQ $1, DX // FD_CLOEXEC
+ MOVL $92, AX // fcntl
+ SYSCALL
+ RET
diff --git a/src/pkg/runtime/sys_plan9_386.s b/src/pkg/runtime/sys_plan9_386.s
index 1f860a961..bed0f7ebe 100644
--- a/src/pkg/runtime/sys_plan9_386.s
+++ b/src/pkg/runtime/sys_plan9_386.s
@@ -3,27 +3,28 @@
// license that can be found in the LICENSE file.
#include "zasm_GOOS_GOARCH.h"
+#include "../../cmd/ld/textflag.h"
// setldt(int entry, int address, int limit)
-TEXT runtime·setldt(SB),7,$0
+TEXT runtime·setldt(SB),NOSPLIT,$0
RET
-TEXT runtime·open(SB),7,$0
+TEXT runtime·open(SB),NOSPLIT,$0
MOVL $14, AX
INT $64
RET
-TEXT runtime·pread(SB),7,$0
+TEXT runtime·pread(SB),NOSPLIT,$0
MOVL $50, AX
INT $64
RET
-TEXT runtime·pwrite(SB),7,$0
+TEXT runtime·pwrite(SB),NOSPLIT,$0
MOVL $51, AX
INT $64
RET
-TEXT runtime·seek(SB),7,$0
+TEXT runtime·seek(SB),NOSPLIT,$0
MOVL $39, AX
INT $64
CMPL AX, $-1
@@ -33,52 +34,52 @@ TEXT runtime·seek(SB),7,$0
MOVL AX, 4(CX)
RET
-TEXT runtime·close(SB),7,$0
+TEXT runtime·close(SB),NOSPLIT,$0
MOVL $4, AX
INT $64
RET
-TEXT runtime·exits(SB),7,$0
+TEXT runtime·exits(SB),NOSPLIT,$0
MOVL $8, AX
INT $64
RET
-TEXT runtime·brk_(SB),7,$0
+TEXT runtime·brk_(SB),NOSPLIT,$0
MOVL $24, AX
INT $64
RET
-TEXT runtime·sleep(SB),7,$0
+TEXT runtime·sleep(SB),NOSPLIT,$0
MOVL $17, AX
INT $64
RET
-TEXT runtime·plan9_semacquire(SB),7,$0
+TEXT runtime·plan9_semacquire(SB),NOSPLIT,$0
MOVL $37, AX
INT $64
RET
-TEXT runtime·plan9_tsemacquire(SB),7,$0
+TEXT runtime·plan9_tsemacquire(SB),NOSPLIT,$0
MOVL $52, AX
INT $64
RET
-TEXT runtime·notify(SB),7,$0
+TEXT runtime·notify(SB),NOSPLIT,$0
MOVL $28, AX
INT $64
RET
-TEXT runtime·noted(SB),7,$0
+TEXT runtime·noted(SB),NOSPLIT,$0
MOVL $29, AX
INT $64
RET
-TEXT runtime·plan9_semrelease(SB),7,$0
+TEXT runtime·plan9_semrelease(SB),NOSPLIT,$0
MOVL $38, AX
INT $64
RET
-TEXT runtime·rfork(SB),7,$0
+TEXT runtime·rfork(SB),NOSPLIT,$0
MOVL $19, AX // rfork
INT $64
@@ -120,14 +121,14 @@ TEXT runtime·rfork(SB),7,$0
RET
// void sigtramp(void *ureg, int8 *note)
-TEXT runtime·sigtramp(SB),7,$0
+TEXT runtime·sigtramp(SB),NOSPLIT,$0
get_tls(AX)
// check that m exists
MOVL m(AX), BX
CMPL BX, $0
JNE 3(PC)
- CALL runtime·badsignal(SB) // will exit
+ CALL runtime·badsignal2(SB) // will exit
RET
// save args
@@ -168,7 +169,7 @@ TEXT runtime·sigtramp(SB),7,$0
RET
// Only used by the 64-bit runtime.
-TEXT runtime·setfpmasks(SB),7,$0
+TEXT runtime·setfpmasks(SB),NOSPLIT,$0
RET
#define ERRMAX 128 /* from os_plan9.h */
@@ -179,7 +180,7 @@ TEXT runtime·setfpmasks(SB),7,$0
// in entersyscall mode, without going
// through the allocator (issue 4994).
// See ../syscall/asm_plan9_386.s:/·Syscall/
-TEXT runtime·errstr(SB),7,$0
+TEXT runtime·errstr(SB),NOSPLIT,$0
get_tls(AX)
MOVL m(AX), BX
MOVL m_errstr(BX), CX
diff --git a/src/pkg/runtime/sys_plan9_amd64.s b/src/pkg/runtime/sys_plan9_amd64.s
index c0c896ebc..d6702e865 100644
--- a/src/pkg/runtime/sys_plan9_amd64.s
+++ b/src/pkg/runtime/sys_plan9_amd64.s
@@ -3,38 +3,39 @@
// license that can be found in the LICENSE file.
#include "zasm_GOOS_GOARCH.h"
+#include "../../cmd/ld/textflag.h"
// setldt(int entry, int address, int limit)
-TEXT runtime·setldt(SB),7,$0
+TEXT runtime·setldt(SB),NOSPLIT,$0
RET
-TEXT runtime·open(SB),7,$0
+TEXT runtime·open(SB),NOSPLIT,$0
MOVQ $0x8000, AX
MOVQ $14, BP
SYSCALL
RET
-TEXT runtime·pread(SB),7,$0
+TEXT runtime·pread(SB),NOSPLIT,$0
MOVQ $0x8000, AX
MOVQ $50, BP
SYSCALL
RET
-TEXT runtime·pwrite(SB),7,$0
+TEXT runtime·pwrite(SB),NOSPLIT,$0
MOVQ $0x8000, AX
MOVQ $51, BP
SYSCALL
RET
// int32 _seek(int64*, int32, int64, int32)
-TEXT _seek<>(SB),7,$0
+TEXT _seek<>(SB),NOSPLIT,$0
MOVQ $0x8000, AX
MOVQ $39, BP
SYSCALL
RET
// int64 seek(int32, int64, int32)
-TEXT runtime·seek(SB),7,$56
+TEXT runtime·seek(SB),NOSPLIT,$56
LEAQ new+48(SP), CX
MOVQ CX, 0(SP)
MOVQ fd+0(FP), CX
@@ -50,67 +51,67 @@ TEXT runtime·seek(SB),7,$56
MOVQ new+48(SP), AX
RET
-TEXT runtime·close(SB),7,$0
+TEXT runtime·close(SB),NOSPLIT,$0
MOVQ $0x8000, AX
MOVQ $4, BP
SYSCALL
RET
-TEXT runtime·exits(SB),7,$0
+TEXT runtime·exits(SB),NOSPLIT,$0
MOVQ $0x8000, AX
MOVQ $8, BP
SYSCALL
RET
-TEXT runtime·brk_(SB),7,$0
+TEXT runtime·brk_(SB),NOSPLIT,$0
MOVQ $0x8000, AX
MOVQ $24, BP
SYSCALL
RET
-TEXT runtime·sleep(SB),7,$0
+TEXT runtime·sleep(SB),NOSPLIT,$0
MOVQ $0x8000, AX
MOVQ $17, BP
SYSCALL
RET
-TEXT runtime·plan9_semacquire(SB),7,$0
+TEXT runtime·plan9_semacquire(SB),NOSPLIT,$0
MOVQ $0x8000, AX
MOVQ $37, BP
SYSCALL
RET
-TEXT runtime·plan9_tsemacquire(SB),7,$0
+TEXT runtime·plan9_tsemacquire(SB),NOSPLIT,$0
MOVQ $0x8000, AX
MOVQ $52, BP
SYSCALL
RET
-TEXT runtime·notify(SB),7,$0
+TEXT runtime·notify(SB),NOSPLIT,$0
MOVQ $0x8000, AX
MOVQ $28, BP
SYSCALL
RET
-TEXT runtime·noted(SB),7,$0
+TEXT runtime·noted(SB),NOSPLIT,$0
MOVQ $0x8000, AX
MOVQ $29, BP
SYSCALL
RET
-TEXT runtime·plan9_semrelease(SB),7,$0
+TEXT runtime·plan9_semrelease(SB),NOSPLIT,$0
MOVQ $0x8000, AX
MOVQ $38, BP
SYSCALL
RET
-TEXT runtime·nanotime(SB),7,$0
+TEXT runtime·nanotime(SB),NOSPLIT,$0
MOVQ $0x8000, AX
MOVQ $60, BP
SYSCALL
RET
-TEXT runtime·rfork(SB),7,$0
+TEXT runtime·rfork(SB),NOSPLIT,$0
MOVQ $0x8000, AX
MOVQ $19, BP // rfork
SYSCALL
@@ -148,18 +149,18 @@ TEXT runtime·rfork(SB),7,$0
RET
// This is needed by asm_amd64.s
-TEXT runtime·settls(SB),7,$0
+TEXT runtime·settls(SB),NOSPLIT,$0
RET
// void sigtramp(void *ureg, int8 *note)
-TEXT runtime·sigtramp(SB),7,$0
+TEXT runtime·sigtramp(SB),NOSPLIT,$0
get_tls(AX)
// check that m exists
MOVQ m(AX), BX
CMPQ BX, $0
JNE 3(PC)
- CALL runtime·badsignal(SB) // will exit
+ CALL runtime·badsignal2(SB) // will exit
RET
// save args
@@ -198,7 +199,7 @@ TEXT runtime·sigtramp(SB),7,$0
CALL runtime·noted(SB)
RET
-TEXT runtime·setfpmasks(SB),7,$8
+TEXT runtime·setfpmasks(SB),NOSPLIT,$8
STMXCSR 0(SP)
MOVL 0(SP), AX
ANDL $~0x3F, AX
@@ -215,7 +216,7 @@ TEXT runtime·setfpmasks(SB),7,$8
// in entersyscall mode, without going
// through the allocator (issue 4994).
// See ../syscall/asm_plan9_386.s:/·Syscall/
-TEXT runtime·errstr(SB),7,$0
+TEXT runtime·errstr(SB),NOSPLIT,$0
get_tls(AX)
MOVQ m(AX), BX
MOVQ m_errstr(BX), CX
diff --git a/src/pkg/runtime/sys_windows_386.s b/src/pkg/runtime/sys_windows_386.s
index 206cdccc4..20753638e 100644
--- a/src/pkg/runtime/sys_windows_386.s
+++ b/src/pkg/runtime/sys_windows_386.s
@@ -3,9 +3,10 @@
// license that can be found in the LICENSE file.
#include "zasm_GOOS_GOARCH.h"
+#include "../../cmd/ld/textflag.h"
// void runtime·asmstdcall(void *c);
-TEXT runtime·asmstdcall(SB),7,$0
+TEXT runtime·asmstdcall(SB),NOSPLIT,$0
MOVL c+0(FP), BX
// SetLastError(0).
@@ -38,27 +39,7 @@ TEXT runtime·asmstdcall(SB),7,$0
RET
-TEXT runtime·badcallback(SB),7,$24
- // stderr
- MOVL $-12, 0(SP)
- MOVL SP, BP
- CALL *runtime·GetStdHandle(SB)
- MOVL BP, SP
-
- MOVL AX, 0(SP) // handle
- MOVL $runtime·badcallbackmsg(SB), DX // pointer
- MOVL DX, 4(SP)
- MOVL runtime·badcallbacklen(SB), DX // count
- MOVL DX, 8(SP)
- LEAL 20(SP), DX // written count
- MOVL $0, 0(DX)
- MOVL DX, 12(SP)
- MOVL $0, 16(SP) // overlapped
- CALL *runtime·WriteFile(SB)
- MOVL BP, SI
- RET
-
-TEXT runtime·badsignal(SB),7,$24
+TEXT runtime·badsignal2(SB),NOSPLIT,$24
// stderr
MOVL $-12, 0(SP)
MOVL SP, BP
@@ -79,16 +60,16 @@ TEXT runtime·badsignal(SB),7,$24
RET
// faster get/set last error
-TEXT runtime·getlasterror(SB),7,$0
+TEXT runtime·getlasterror(SB),NOSPLIT,$0
MOVL 0x34(FS), AX
RET
-TEXT runtime·setlasterror(SB),7,$0
+TEXT runtime·setlasterror(SB),NOSPLIT,$0
MOVL err+0(FP), AX
MOVL AX, 0x34(FS)
RET
-TEXT runtime·sigtramp(SB),7,$28
+TEXT runtime·sigtramp(SB),NOSPLIT,$28
// unwinding?
MOVL info+0(FP), CX
TESTL $6, 4(CX) // exception flags
@@ -106,7 +87,7 @@ TEXT runtime·sigtramp(SB),7,$28
MOVL m(CX), AX
CMPL AX, $0
JNE 2(PC)
- CALL runtime·badsignal(SB)
+ CALL runtime·badsignal2(SB)
MOVL g(CX), CX
MOVL CX, 8(SP)
@@ -126,21 +107,21 @@ TEXT runtime·sigtramp(SB),7,$28
sigdone:
RET
-TEXT runtime·ctrlhandler(SB),7,$0
+TEXT runtime·ctrlhandler(SB),NOSPLIT,$0
PUSHL $runtime·ctrlhandler1(SB)
CALL runtime·externalthreadhandler(SB)
MOVL 4(SP), CX
ADDL $12, SP
JMP CX
-TEXT runtime·profileloop(SB),7,$0
+TEXT runtime·profileloop(SB),NOSPLIT,$0
PUSHL $runtime·profileloop1(SB)
CALL runtime·externalthreadhandler(SB)
MOVL 4(SP), CX
ADDL $12, SP
JMP CX
-TEXT runtime·externalthreadhandler(SB),7,$0
+TEXT runtime·externalthreadhandler(SB),NOSPLIT,$0
PUSHL BP
MOVL SP, BP
PUSHL BX
@@ -184,19 +165,16 @@ TEXT runtime·externalthreadhandler(SB),7,$0
POPL BP
RET
-// Called from dynamic function created by ../thread.c compilecallback,
-// running on Windows stack (not Go stack).
-// BX, BP, SI, DI registers and DF flag are preserved
-// as required by windows callback convention.
-// AX = address of go func we need to call
-// DX = total size of arguments
-//
-TEXT runtime·callbackasm+0(SB),7,$0
- // preserve whatever's at the memory location that
- // the callback will use to store the return value
- LEAL 8(SP), CX
- PUSHL 0(CX)(DX*1)
- ADDL $4, DX // extend argsize by size of return value
+GLOBL runtime·cbctxts(SB), $4
+
+TEXT runtime·callbackasm1+0(SB),NOSPLIT,$0
+ MOVL 0(SP), AX // will use to find our callback context
+
+ // remove return address from stack, we are not returning there
+ ADDL $4, SP
+
+ // address to callback parameters into CX
+ LEAL 4(SP), CX
// save registers as required for windows callback
PUSHL DI
@@ -209,19 +187,51 @@ TEXT runtime·callbackasm+0(SB),7,$0
PUSHL 0(FS)
MOVL SP, 0(FS)
- // callback parameters
- PUSHL DX
- PUSHL CX
- PUSHL AX
+ // determine index into runtime·cbctxts table
+ SUBL $runtime·callbackasm(SB), AX
+ MOVL $0, DX
+ MOVL $5, BX // divide by 5 because each call instruction in runtime·callbacks is 5 bytes long
+ DIVL BX,
- CLD
+ // find correspondent runtime·cbctxts table entry
+ MOVL runtime·cbctxts(SB), BX
+ MOVL -4(BX)(AX*4), BX
- CALL runtime·cgocallback_gofunc(SB)
+ // extract callback context
+ MOVL cbctxt_gobody(BX), AX
+ MOVL cbctxt_argsize(BX), DX
+
+ // preserve whatever's at the memory location that
+ // the callback will use to store the return value
+ PUSHL 0(CX)(DX*1)
+
+ // extend argsize by size of return value
+ ADDL $4, DX
+
+ // remember how to restore stack on return
+ MOVL cbctxt_restorestack(BX), BX
+ PUSHL BX
+ // call target Go function
+ PUSHL DX // argsize (including return value)
+ PUSHL CX // callback parameters
+ PUSHL AX // address of target Go function
+ CLD
+ CALL runtime·cgocallback_gofunc(SB)
POPL AX
POPL CX
POPL DX
+ // how to restore stack on return
+ POPL BX
+
+ // return value into AX (as per Windows spec)
+ // and restore previously preserved value
+ MOVL -4(CX)(DX*1), AX
+ POPL -4(CX)(DX*1)
+
+ MOVL BX, CX // cannot use BX anymore
+
// pop SEH frame
POPL 0(FS)
POPL BX
@@ -232,14 +242,17 @@ TEXT runtime·callbackasm+0(SB),7,$0
POPL SI
POPL DI
+ // remove callback parameters before return (as per Windows spec)
+ POPL DX
+ ADDL CX, SP
+ PUSHL DX
+
CLD
- MOVL -4(CX)(DX*1), AX
- POPL -4(CX)(DX*1)
RET
// void tstart(M *newm);
-TEXT runtime·tstart(SB),7,$0
+TEXT runtime·tstart(SB),NOSPLIT,$0
MOVL newm+4(SP), CX // m
MOVL m_g0(CX), DX // g
@@ -264,7 +277,7 @@ TEXT runtime·tstart(SB),7,$0
RET
// uint32 tstart_stdcall(M *newm);
-TEXT runtime·tstart_stdcall(SB),7,$0
+TEXT runtime·tstart_stdcall(SB),NOSPLIT,$0
MOVL newm+4(SP), BX
PUSHL BX
@@ -281,13 +294,13 @@ TEXT runtime·tstart_stdcall(SB),7,$0
RET
// setldt(int entry, int address, int limit)
-TEXT runtime·setldt(SB),7,$0
+TEXT runtime·setldt(SB),NOSPLIT,$0
MOVL address+4(FP), CX
MOVL CX, 0x14(FS)
RET
// void install_exception_handler()
-TEXT runtime·install_exception_handler(SB),7,$0
+TEXT runtime·install_exception_handler(SB),NOSPLIT,$0
get_tls(CX)
MOVL m(CX), CX // m
@@ -304,7 +317,7 @@ TEXT runtime·install_exception_handler(SB),7,$0
RET
// void remove_exception_handler()
-TEXT runtime·remove_exception_handler(SB),7,$0
+TEXT runtime·remove_exception_handler(SB),NOSPLIT,$0
get_tls(CX)
MOVL m(CX), CX // m
@@ -315,28 +328,39 @@ TEXT runtime·remove_exception_handler(SB),7,$0
RET
-TEXT runtime·osyield(SB),7,$20
- // Tried NtYieldExecution but it doesn't yield hard enough.
- // NtWaitForSingleObject being used here as Sleep(0).
- MOVL runtime·NtWaitForSingleObject(SB), AX
- MOVL $-1, hi-4(SP)
- MOVL $-1, lo-8(SP)
- LEAL lo-8(SP), BX
- MOVL BX, ptime-12(SP)
- MOVL $0, alertable-16(SP)
- MOVL $-1, handle-20(SP)
- MOVL SP, BP
- CALL checkstack4<>(SB)
+// Sleep duration is in 100ns units.
+TEXT runtime·usleep1(SB),NOSPLIT,$0
+ MOVL duration+0(FP), BX
+ MOVL $runtime·usleep2(SB), AX // to hide from 8l
+
+ // Execute call on m->g0 stack, in case we are not actually
+ // calling a system call wrapper, like when running under WINE.
+ get_tls(CX)
+ CMPL CX, $0
+ JNE 3(PC)
+ // Not a Go-managed thread. Do not switch stack.
CALL AX
- MOVL BP, SP
RET
-TEXT runtime·usleep(SB),7,$20
- MOVL runtime·NtWaitForSingleObject(SB), AX
- // Have 1us units; need negative 100ns units.
- // Assume multiply by 10 will not overflow 32-bit word.
- MOVL usec+0(FP), BX
- IMULL $10, BX
+ MOVL m(CX), BP
+ MOVL m_g0(BP), SI
+ CMPL g(CX), SI
+ JNE 3(PC)
+ // executing on m->g0 already
+ CALL AX
+ RET
+
+ // Switch to m->g0 stack and back.
+ MOVL (g_sched+gobuf_sp)(SI), SI
+ MOVL SP, -4(SI)
+ LEAL -4(SI), SP
+ CALL AX
+ MOVL 0(SP), SP
+ RET
+
+// Runs on OS stack. duration (in 100ns units) is in BX.
+TEXT runtime·usleep2(SB),NOSPLIT,$20
+ // Want negative 100ns units.
NEGL BX
MOVL $-1, hi-4(SP)
MOVL BX, lo-8(SP)
@@ -345,15 +369,7 @@ TEXT runtime·usleep(SB),7,$20
MOVL $0, alertable-16(SP)
MOVL $-1, handle-20(SP)
MOVL SP, BP
- CALL checkstack4<>(SB)
+ MOVL runtime·NtWaitForSingleObject(SB), AX
CALL AX
MOVL BP, SP
RET
-
-// This function requires 4 bytes of stack,
-// to simulate what calling NtWaitForSingleObject will use.
-// (It is just a CALL to the system call dispatch.)
-// If the linker okays the call to checkstack4 (a NOSPLIT function)
-// then the call to NtWaitForSingleObject is okay too.
-TEXT checkstack4<>(SB),7,$4
- RET
diff --git a/src/pkg/runtime/sys_windows_amd64.s b/src/pkg/runtime/sys_windows_amd64.s
index c20a268b1..8c4caf867 100644
--- a/src/pkg/runtime/sys_windows_amd64.s
+++ b/src/pkg/runtime/sys_windows_amd64.s
@@ -3,13 +3,14 @@
// license that can be found in the LICENSE file.
#include "zasm_GOOS_GOARCH.h"
+#include "../../cmd/ld/textflag.h"
// maxargs should be divisible by 2, as Windows stack
// must be kept 16-byte aligned on syscall entry.
#define maxargs 16
// void runtime·asmstdcall(void *c);
-TEXT runtime·asmstdcall(SB),7,$0
+TEXT runtime·asmstdcall(SB),NOSPLIT,$0
// asmcgocall will put first argument into CX.
PUSHQ CX // save for later
MOVQ wincall_fn(CX), AX
@@ -60,34 +61,7 @@ loadregs:
RET
-// This should be called on a system stack,
-// so we don't need to concern about split stack.
-TEXT runtime·badcallback(SB),7,$0
- SUBQ $48, SP
-
- // stderr
- MOVQ $-12, CX // stderr
- MOVQ CX, 0(SP)
- MOVQ runtime·GetStdHandle(SB), AX
- CALL AX
-
- MOVQ AX, CX // handle
- MOVQ CX, 0(SP)
- MOVQ $runtime·badcallbackmsg(SB), DX // pointer
- MOVQ DX, 8(SP)
- MOVL $runtime·badcallbacklen(SB), R8 // count
- MOVQ R8, 16(SP)
- LEAQ 40(SP), R9 // written count
- MOVQ $0, 0(R9)
- MOVQ R9, 24(SP)
- MOVQ $0, 32(SP) // overlapped
- MOVQ runtime·WriteFile(SB), AX
- CALL AX
-
- ADDQ $48, SP
- RET
-
-TEXT runtime·badsignal(SB),7,$48
+TEXT runtime·badsignal2(SB),NOSPLIT,$48
// stderr
MOVQ $-12, CX // stderr
MOVQ CX, 0(SP)
@@ -110,18 +84,18 @@ TEXT runtime·badsignal(SB),7,$48
RET
// faster get/set last error
-TEXT runtime·getlasterror(SB),7,$0
+TEXT runtime·getlasterror(SB),NOSPLIT,$0
MOVQ 0x30(GS), AX
MOVL 0x68(AX), AX
RET
-TEXT runtime·setlasterror(SB),7,$0
+TEXT runtime·setlasterror(SB),NOSPLIT,$0
MOVL err+0(FP), AX
MOVQ 0x30(GS), CX
MOVL AX, 0x68(CX)
RET
-TEXT runtime·sigtramp(SB),7,$0
+TEXT runtime·sigtramp(SB),NOSPLIT,$0
// CX: exception record
// R8: context
@@ -145,7 +119,7 @@ TEXT runtime·sigtramp(SB),7,$0
MOVQ m(CX), AX
CMPQ AX, $0
JNE 2(PC)
- CALL runtime·badsignal(SB)
+ CALL runtime·badsignal2(SB)
MOVQ g(CX), CX
MOVQ CX, 16(SP)
@@ -166,20 +140,20 @@ TEXT runtime·sigtramp(SB),7,$0
sigdone:
RET
-TEXT runtime·ctrlhandler(SB),7,$8
+TEXT runtime·ctrlhandler(SB),NOSPLIT,$8
MOVQ CX, 16(SP) // spill
MOVQ $runtime·ctrlhandler1(SB), CX
MOVQ CX, 0(SP)
CALL runtime·externalthreadhandler(SB)
RET
-TEXT runtime·profileloop(SB),7,$8
+TEXT runtime·profileloop(SB),NOSPLIT,$8
MOVQ $runtime·profileloop1(SB), CX
MOVQ CX, 0(SP)
CALL runtime·externalthreadhandler(SB)
RET
-TEXT runtime·externalthreadhandler(SB),7,$0
+TEXT runtime·externalthreadhandler(SB),NOSPLIT,$0
PUSHQ BP
MOVQ SP, BP
PUSHQ BX
@@ -223,32 +197,37 @@ TEXT runtime·externalthreadhandler(SB),7,$0
POPQ BP
RET
-// Continuation of thunk function created for each callback by ../thread.c compilecallback,
-// runs on Windows stack (not Go stack).
-// Thunk code designed to have minimal size for it is copied many (up to thousands) times.
-//
-// thunk:
-// MOVQ $fn, AX
-// PUSHQ AX
-// MOVQ $argsize, AX
-// PUSHQ AX
-// MOVQ $runtime·callbackasm, AX
-// JMP AX
-TEXT runtime·callbackasm(SB),7,$0
+GLOBL runtime·cbctxts(SB), $8
+
+TEXT runtime·callbackasm1(SB),NOSPLIT,$0
// Construct args vector for cgocallback().
// By windows/amd64 calling convention first 4 args are in CX, DX, R8, R9
// args from the 5th on are on the stack.
// In any case, even if function has 0,1,2,3,4 args, there is reserved
// but uninitialized "shadow space" for the first 4 args.
// The values are in registers.
- MOVQ CX, (24+0)(SP)
- MOVQ DX, (24+8)(SP)
- MOVQ R8, (24+16)(SP)
- MOVQ R9, (24+24)(SP)
- // 6l does not accept writing POPQs here issuing a warning "unbalanced PUSH/POP"
- MOVQ 0(SP), DX // POPQ DX
- MOVQ 8(SP), AX // POPQ AX
- ADDQ $16, SP
+ MOVQ CX, (16+0)(SP)
+ MOVQ DX, (16+8)(SP)
+ MOVQ R8, (16+16)(SP)
+ MOVQ R9, (16+24)(SP)
+
+ // remove return address from stack, we are not returning there
+ MOVQ 0(SP), AX
+ ADDQ $8, SP
+
+ // determine index into runtime·cbctxts table
+ SUBQ $runtime·callbackasm(SB), AX
+ MOVQ $0, DX
+ MOVQ $5, CX // divide by 5 because each call instruction in runtime·callbacks is 5 bytes long
+ DIVL CX,
+
+ // find correspondent runtime·cbctxts table entry
+ MOVQ runtime·cbctxts(SB), CX
+ MOVQ -8(CX)(AX*8), AX
+
+ // extract callback context
+ MOVQ cbctxt_argsize(AX), DX
+ MOVQ cbctxt_gobody(AX), AX
// preserve whatever's at the memory location that
// the callback will use to store the return value
@@ -258,8 +237,6 @@ TEXT runtime·callbackasm(SB),7,$0
// DI SI BP BX R12 R13 R14 R15 registers and DF flag are preserved
// as required by windows callback convention.
- // 6l does not allow writing many PUSHQs here issuing a warning "nosplit stack overflow"
- // the warning has no sense as this code uses os thread stack
PUSHFQ
SUBQ $64, SP
MOVQ DI, 56(SP)
@@ -274,18 +251,17 @@ TEXT runtime·callbackasm(SB),7,$0
// prepare call stack. use SUBQ to hide from stack frame checks
// cgocallback(Go func, void *frame, uintptr framesize)
SUBQ $24, SP
- MOVQ DX, 16(SP) // uintptr framesize
- MOVQ CX, 8(SP) // void *frame
- MOVQ AX, 0(SP) // Go func
+ MOVQ DX, 16(SP) // argsize (including return value)
+ MOVQ CX, 8(SP) // callback parameters
+ MOVQ AX, 0(SP) // address of target Go function
CLD
- CALL runtime·cgocallback_gofunc(SB)
+ CALL runtime·cgocallback_gofunc(SB)
MOVQ 0(SP), AX
MOVQ 8(SP), CX
MOVQ 16(SP), DX
ADDQ $24, SP
// restore registers as required for windows callback
- // 6l does not allow writing many POPs here issuing a warning "nosplit stack overflow"
MOVQ 0(SP), R15
MOVQ 8(SP), R14
MOVQ 16(SP), R13
@@ -301,7 +277,7 @@ TEXT runtime·callbackasm(SB),7,$0
POPQ -8(CX)(DX*1) // restore bytes just after the args
RET
-TEXT runtime·setstacklimits(SB),7,$0
+TEXT runtime·setstacklimits(SB),NOSPLIT,$0
MOVQ 0x30(GS), CX
MOVQ $0, 0x10(CX)
MOVQ $0xffffffffffff, AX
@@ -309,7 +285,7 @@ TEXT runtime·setstacklimits(SB),7,$0
RET
// uint32 tstart_stdcall(M *newm);
-TEXT runtime·tstart_stdcall(SB),7,$0
+TEXT runtime·tstart_stdcall(SB),NOSPLIT,$0
// CX contains first arg newm
MOVQ m_g0(CX), DX // g
@@ -335,46 +311,56 @@ TEXT runtime·tstart_stdcall(SB),7,$0
RET
// set tls base to DI
-TEXT runtime·settls(SB),7,$0
+TEXT runtime·settls(SB),NOSPLIT,$0
MOVQ DI, 0x28(GS)
RET
// void install_exception_handler()
-TEXT runtime·install_exception_handler(SB),7,$0
+TEXT runtime·install_exception_handler(SB),NOSPLIT,$0
CALL runtime·setstacklimits(SB)
RET
-TEXT runtime·remove_exception_handler(SB),7,$0
+TEXT runtime·remove_exception_handler(SB),NOSPLIT,$0
RET
-TEXT runtime·osyield(SB),7,$8
- // Tried NtYieldExecution but it doesn't yield hard enough.
- // NtWaitForSingleObject being used here as Sleep(0).
- // The CALL is safe because NtXxx is a system call wrapper:
- // it puts the right system call number in AX, then does
- // a SYSENTER and a RET.
- MOVQ runtime·NtWaitForSingleObject(SB), AX
- MOVQ $1, BX
- NEGQ BX
- MOVQ SP, R8 // ptime
- MOVQ BX, (R8)
- MOVQ $-1, CX // handle
- MOVQ $0, DX // alertable
+// Sleep duration is in 100ns units.
+TEXT runtime·usleep1(SB),NOSPLIT,$0
+ MOVL duration+0(FP), BX
+ MOVQ $runtime·usleep2(SB), AX // to hide from 6l
+
+ // Execute call on m->g0 stack, in case we are not actually
+ // calling a system call wrapper, like when running under WINE.
+ get_tls(R15)
+ CMPQ R15, $0
+ JNE 3(PC)
+ // Not a Go-managed thread. Do not switch stack.
CALL AX
RET
-TEXT runtime·usleep(SB),7,$8
- // The CALL is safe because NtXxx is a system call wrapper:
- // it puts the right system call number in AX, then does
- // a SYSENTER and a RET.
- MOVQ runtime·NtWaitForSingleObject(SB), AX
- // Have 1us units; want negative 100ns units.
- MOVL usec+0(FP), BX
- IMULQ $10, BX
+ MOVQ m(R15), R14
+ MOVQ m_g0(R14), R14
+ CMPQ g(R15), R14
+ JNE 3(PC)
+ // executing on m->g0 already
+ CALL AX
+ RET
+
+ // Switch to m->g0 stack and back.
+ MOVQ (g_sched+gobuf_sp)(R14), R14
+ MOVQ SP, -8(R14)
+ LEAQ -8(R14), SP
+ CALL AX
+ MOVQ 0(SP), SP
+ RET
+
+// Runs on OS stack. duration (in 100ns units) is in BX.
+TEXT runtime·usleep2(SB),NOSPLIT,$8
+ // Want negative 100ns units.
NEGQ BX
MOVQ SP, R8 // ptime
MOVQ BX, (R8)
MOVQ $-1, CX // handle
MOVQ $0, DX // alertable
+ MOVQ runtime·NtWaitForSingleObject(SB), AX
CALL AX
RET
diff --git a/src/pkg/runtime/sys_x86.c b/src/pkg/runtime/sys_x86.c
new file mode 100644
index 000000000..e68ff514a
--- /dev/null
+++ b/src/pkg/runtime/sys_x86.c
@@ -0,0 +1,42 @@
+// Copyright 2013 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.
+
+// +build amd64 386
+
+#include "runtime.h"
+
+// adjust Gobuf as it if executed a call to fn with context ctxt
+// and then did an immediate gosave.
+void
+runtime·gostartcall(Gobuf *gobuf, void (*fn)(void), void *ctxt)
+{
+ uintptr *sp;
+
+ sp = (uintptr*)gobuf->sp;
+ *--sp = (uintptr)gobuf->pc;
+ gobuf->sp = (uintptr)sp;
+ gobuf->pc = (uintptr)fn;
+ gobuf->ctxt = ctxt;
+}
+
+// Called to rewind context saved during morestack back to beginning of function.
+// To help us, the linker emits a jmp back to the beginning right after the
+// call to morestack. We just have to decode and apply that jump.
+void
+runtime·rewindmorestack(Gobuf *gobuf)
+{
+ byte *pc;
+
+ pc = (byte*)gobuf->pc;
+ if(pc[0] == 0xe9) { // jmp 4-byte offset
+ gobuf->pc = gobuf->pc + 5 + *(int32*)(pc+1);
+ return;
+ }
+ if(pc[0] == 0xeb) { // jmp 1-byte offset
+ gobuf->pc = gobuf->pc + 2 + *(int8*)(pc+1);
+ return;
+ }
+ runtime·printf("runtime: pc=%p %x %x %x %x %x\n", pc, pc[0], pc[1], pc[2], pc[3], pc[4]);
+ runtime·throw("runtime: misuse of rewindmorestack");
+}
diff --git a/src/pkg/runtime/syscall_windows.goc b/src/pkg/runtime/syscall_windows.goc
index 781ec908d..173d3ed6a 100644
--- a/src/pkg/runtime/syscall_windows.goc
+++ b/src/pkg/runtime/syscall_windows.goc
@@ -40,9 +40,14 @@ func NewCallback(fn Eface) (code uintptr) {
code = (uintptr)runtime·compilecallback(fn, true);
}
+/*
+ * If this is needed, uncomment here and add a declaration in package syscall
+ * next to the NewCallback declaration.
+ *
func NewCallbackCDecl(fn Eface) (code uintptr) {
code = (uintptr)runtime·compilecallback(fn, false);
}
+ */
func Syscall(fn uintptr, nargs uintptr, a1 uintptr, a2 uintptr, a3 uintptr) (r1 uintptr, r2 uintptr, err uintptr) {
WinCall c;
diff --git a/src/pkg/runtime/time.goc b/src/pkg/runtime/time.goc
index be0c1f83d..b575696f7 100644
--- a/src/pkg/runtime/time.goc
+++ b/src/pkg/runtime/time.goc
@@ -13,8 +13,13 @@ package time
#include "malloc.h"
#include "race.h"
+enum {
+ debug = 0,
+};
+
static Timers timers;
static void addtimer(Timer*);
+static void dumptimers(int8*);
// Package time APIs.
// Godoc uses the comments in package time, not these.
@@ -92,6 +97,11 @@ addtimer(Timer *t)
int32 n;
Timer **nt;
+ // when must never be negative; otherwise timerproc will overflow
+ // during its delta calculation and never expire other timers.
+ if(t->when < 0)
+ t->when = (1LL<<63)-1;
+
if(timers.len >= timers.cap) {
// Grow slice.
n = 16;
@@ -121,6 +131,8 @@ addtimer(Timer *t)
timers.timerproc = runtime·newproc1(&timerprocv, nil, 0, 0, addtimer);
timers.timerproc->issystem = true;
}
+ if(debug)
+ dumptimers("addtimer");
}
// Delete timer t from the heap.
@@ -157,6 +169,8 @@ runtime·deltimer(Timer *t)
siftup(i);
siftdown(i);
}
+ if(debug)
+ dumptimers("deltimer");
runtime·unlock(&timers);
return true;
}
@@ -175,6 +189,7 @@ timerproc(void)
for(;;) {
runtime·lock(&timers);
+ timers.sleeping = false;
now = runtime·nanotime();
for(;;) {
if(timers.len == 0) {
@@ -214,9 +229,7 @@ timerproc(void)
timers.sleeping = true;
runtime·noteclear(&timers.waitnote);
runtime·unlock(&timers);
- runtime·entersyscallblock();
- runtime·notetsleep(&timers.waitnote, delta);
- runtime·exitsyscall();
+ runtime·notetsleepg(&timers.waitnote, delta);
}
}
@@ -226,18 +239,20 @@ static void
siftup(int32 i)
{
int32 p;
+ int64 when;
Timer **t, *tmp;
t = timers.t;
+ when = t[i]->when;
+ tmp = t[i];
while(i > 0) {
- p = (i-1)/2; // parent
- if(t[i]->when >= t[p]->when)
+ p = (i-1)/4; // parent
+ if(when >= t[p]->when)
break;
- tmp = t[i];
t[i] = t[p];
- t[p] = tmp;
t[i]->i = i;
- t[p]->i = p;
+ t[p] = tmp;
+ tmp->i = p;
i = p;
}
}
@@ -245,25 +260,57 @@ siftup(int32 i)
static void
siftdown(int32 i)
{
- int32 c, len;
+ int32 c, c3, len;
+ int64 when, w, w3;
Timer **t, *tmp;
t = timers.t;
len = timers.len;
+ when = t[i]->when;
+ tmp = t[i];
for(;;) {
- c = i*2 + 1; // left child
+ c = i*4 + 1; // left child
+ c3 = c + 2; // mid child
if(c >= len) {
break;
}
- if(c+1 < len && t[c+1]->when < t[c]->when)
+ w = t[c]->when;
+ if(c+1 < len && t[c+1]->when < w) {
+ w = t[c+1]->when;
c++;
- if(t[c]->when >= t[i]->when)
+ }
+ if(c3 < len) {
+ w3 = t[c3]->when;
+ if(c3+1 < len && t[c3+1]->when < w3) {
+ w3 = t[c3+1]->when;
+ c3++;
+ }
+ if(w3 < w) {
+ w = w3;
+ c = c3;
+ }
+ }
+ if(w >= when)
break;
- tmp = t[i];
t[i] = t[c];
- t[c] = tmp;
t[i]->i = i;
- t[c]->i = c;
+ t[c] = tmp;
+ tmp->i = c;
i = c;
}
}
+
+static void
+dumptimers(int8 *msg)
+{
+ Timer *t;
+ int32 i;
+
+ runtime·printf("timers: %s\n", msg);
+ for(i = 0; i < timers.len; i++) {
+ t = timers.t[i];
+ runtime·printf("\t%d\t%p:\ti %d when %D period %D fn %p\n",
+ i, t, t->i, t->when, t->period, t->fv->fn);
+ }
+ runtime·printf("\n");
+}
diff --git a/src/pkg/runtime/time_plan9_386.c b/src/pkg/runtime/time_plan9_386.c
index fc08a90d6..71d54b764 100644
--- a/src/pkg/runtime/time_plan9_386.c
+++ b/src/pkg/runtime/time_plan9_386.c
@@ -4,7 +4,9 @@
#include "runtime.h"
#include "os_GOOS.h"
+#include "../../cmd/ld/textflag.h"
+#pragma textflag NOSPLIT
int64
runtime·nanotime(void)
{
diff --git a/src/pkg/runtime/traceback_arm.c b/src/pkg/runtime/traceback_arm.c
index ae2d3241f..341aa2058 100644
--- a/src/pkg/runtime/traceback_arm.c
+++ b/src/pkg/runtime/traceback_arm.c
@@ -5,44 +5,45 @@
#include "runtime.h"
#include "arch_GOARCH.h"
#include "malloc.h"
+#include "funcdata.h"
-void runtime·deferproc(void);
-void runtime·newproc(void);
-void runtime·newstack(void);
-void runtime·morestack(void);
void runtime·sigpanic(void);
-void _div(void);
-void _mod(void);
-void _divu(void);
-void _modu(void);
int32
-runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *gp, int32 skip, uintptr *pcbuf, int32 max, void (*fn)(Func*, byte*, byte*, void*), void *arg)
+runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip, uintptr *pcbuf, int32 max, void (*callback)(Stkframe*, void*), void *v, bool printall)
{
- int32 i, n;
- uintptr pc, lr, tracepc, x;
- byte *fp;
- bool waspanic;
+ int32 i, n, nprint, line;
+ uintptr x, tracepc;
+ bool waspanic, printing;
+ Func *f, *flr;
+ Stkframe frame;
Stktop *stk;
- Func *f;
+ String file;
- pc = (uintptr)pc0;
- lr = (uintptr)lr0;
- fp = nil;
+ nprint = 0;
+ runtime·memclr((byte*)&frame, sizeof frame);
+ frame.pc = pc0;
+ frame.lr = lr0;
+ frame.sp = sp0;
waspanic = false;
-
- // If the PC is goexit, the goroutine hasn't started yet.
- if(pc == (uintptr)runtime·goexit && gp->fnstart != nil) {
- pc = (uintptr)gp->fnstart->fn;
- lr = (uintptr)runtime·goexit;
- }
+ printing = pcbuf==nil && callback==nil;
// If the PC is zero, it's likely a nil function call.
// Start in the caller's frame.
- if(pc == 0) {
- pc = lr;
- lr = 0;
+ if(frame.pc == 0) {
+ frame.pc = frame.lr;
+ frame.lr = 0;
+ }
+
+ f = runtime·findfunc(frame.pc);
+ if(f == nil) {
+ if(callback != nil) {
+ runtime·printf("runtime: unknown pc %p\n", frame.pc);
+ runtime·throw("unknown pc");
+ }
+ return 0;
}
+ frame.fn = f;
n = 0;
stk = (Stktop*)gp->stackbase;
@@ -54,159 +55,192 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *gp, int32 skip, uintptr
// stk is the stack containing sp.
// The caller's program counter is lr, unless lr is zero, in which case it is *(uintptr*)sp.
- if(pc == (uintptr)runtime·lessstack) {
+ if(frame.pc == (uintptr)runtime·lessstack) {
// Hit top of stack segment. Unwind to next segment.
- pc = (uintptr)stk->gobuf.pc;
- sp = (byte*)stk->gobuf.sp;
- lr = 0;
- fp = nil;
- if(pcbuf == nil && fn == nil && runtime·showframe(nil, gp == m->curg))
+ frame.pc = stk->gobuf.pc;
+ frame.sp = stk->gobuf.sp;
+ frame.lr = 0;
+ frame.fp = 0;
+ if(printing && runtime·showframe(nil, gp))
runtime·printf("----- stack segment boundary -----\n");
stk = (Stktop*)stk->stackbase;
+
+ f = runtime·findfunc(frame.pc);
+ if(f == nil) {
+ runtime·printf("runtime: unknown pc %p after stack split\n", frame.pc);
+ if(callback != nil)
+ runtime·throw("unknown pc");
+ }
+ frame.fn = f;
continue;
}
-
- if(pc <= 0x1000 || (f = runtime·findfunc(pc)) == nil) {
- if(fn != nil)
- runtime·throw("unknown pc");
- break;
- }
+ f = frame.fn;
// Found an actual function.
- if(lr == 0)
- lr = *(uintptr*)sp;
- if(fp == nil) {
- fp = sp;
- if(pc > f->entry && f->frame >= sizeof(uintptr))
- fp += f->frame - sizeof(uintptr);
- fp += sizeof(uintptr);
+ // Derive frame pointer and link register.
+ if(frame.fp == 0)
+ frame.fp = frame.sp + runtime·funcspdelta(f, frame.pc);
+ if(runtime·topofstack(f)) {
+ frame.lr = 0;
+ flr = nil;
+ } else {
+ if((n == 0 && frame.sp < frame.fp) || frame.lr == 0)
+ frame.lr = *(uintptr*)frame.sp;
+ flr = runtime·findfunc(frame.lr);
+ if(flr == nil) {
+ runtime·printf("runtime: unexpected return pc for %s called from %p\n", runtime·funcname(f), frame.lr);
+ if(callback != nil)
+ runtime·throw("unknown caller pc");
+ }
}
- if(skip > 0)
+ frame.varp = (byte*)frame.fp;
+
+ // Derive size of arguments.
+ // Most functions have a fixed-size argument block,
+ // so we can use metadata about the function f.
+ // Not all, though: there are some variadic functions
+ // in package runtime and reflect, and for those we use call-specific
+ // metadata recorded by f's caller.
+ if(callback != nil || printing) {
+ frame.argp = (byte*)frame.fp + sizeof(uintptr);
+ if(f->args != ArgsSizeUnknown)
+ frame.arglen = f->args;
+ else if(flr == nil)
+ frame.arglen = 0;
+ else if(frame.lr == (uintptr)runtime·lessstack)
+ frame.arglen = stk->argsize;
+ else if((i = runtime·funcarglen(flr, frame.lr)) >= 0)
+ frame.arglen = i;
+ else {
+ runtime·printf("runtime: unknown argument frame size for %s called from %p [%s]\n",
+ runtime·funcname(f), frame.lr, flr ? runtime·funcname(flr) : "?");
+ if(callback != nil)
+ runtime·throw("invalid stack");
+ frame.arglen = 0;
+ }
+ }
+
+ if(skip > 0) {
skip--;
- else if(pcbuf != nil)
- pcbuf[n++] = pc;
- else if(fn != nil)
- (*fn)(f, (byte*)pc, sp, arg);
- else {
- if(runtime·showframe(f, gp == m->curg)) {
+ goto skipped;
+ }
+
+ if(pcbuf != nil)
+ pcbuf[n] = frame.pc;
+ if(callback != nil)
+ callback(&frame, v);
+ if(printing) {
+ if(printall || runtime·showframe(f, gp)) {
// Print during crash.
// main(0x1, 0x2, 0x3)
// /home/rsc/go/src/runtime/x.go:23 +0xf
- tracepc = pc; // back up to CALL instruction for funcline.
- if(n > 0 && pc > f->entry && !waspanic)
+ tracepc = frame.pc; // back up to CALL instruction for funcline.
+ if(n > 0 && frame.pc > f->entry && !waspanic)
tracepc -= sizeof(uintptr);
- if(m->throwing && gp == m->curg)
- runtime·printf("[fp=%p] ", fp);
- runtime·printf("%S(", f->name);
- for(i = 0; i < f->args/sizeof(uintptr); i++) {
- if(i != 0)
- runtime·prints(", ");
- runtime·printhex(((uintptr*)fp)[1+i]);
- if(i >= 4) {
+ runtime·printf("%s(", runtime·funcname(f));
+ for(i = 0; i < frame.arglen/sizeof(uintptr); i++) {
+ if(i >= 5) {
runtime·prints(", ...");
break;
}
+ if(i != 0)
+ runtime·prints(", ");
+ runtime·printhex(((uintptr*)frame.argp)[i]);
}
runtime·prints(")\n");
- runtime·printf("\t%S:%d", f->src, runtime·funcline(f, tracepc));
- if(pc > f->entry)
- runtime·printf(" +%p", (uintptr)(pc - f->entry));
+ line = runtime·funcline(f, tracepc, &file);
+ runtime·printf("\t%S:%d", file, line);
+ if(frame.pc > f->entry)
+ runtime·printf(" +%p", (uintptr)(frame.pc - f->entry));
+ if(m->throwing > 0 && gp == m->curg)
+ runtime·printf(" fp=%p", frame.fp);
runtime·printf("\n");
+ nprint++;
}
- n++;
}
+ n++;
+ skipped:
waspanic = f->entry == (uintptr)runtime·sigpanic;
- if(pcbuf == nil && fn == nil && f->entry == (uintptr)runtime·newstack && gp == m->g0) {
- runtime·printf("----- newstack called from goroutine %D -----\n", m->curg->goid);
- pc = (uintptr)m->morepc;
- sp = (byte*)m->moreargp - sizeof(void*);
- lr = (uintptr)m->morebuf.pc;
- fp = (byte*)m->morebuf.sp;
- gp = m->curg;
- stk = (Stktop*)gp->stackbase;
- continue;
- }
-
- if(pcbuf == nil && fn == nil && f->entry == (uintptr)runtime·lessstack && gp == m->g0) {
- runtime·printf("----- lessstack called from goroutine %D -----\n", m->curg->goid);
- gp = m->curg;
- stk = (Stktop*)gp->stackbase;
- sp = (byte*)stk->gobuf.sp;
- pc = (uintptr)stk->gobuf.pc;
- fp = nil;
- lr = 0;
- continue;
- }
-
// Do not unwind past the bottom of the stack.
- if(pc == (uintptr)runtime·goexit)
+ if(flr == nil)
break;
// Unwind to next frame.
- pc = lr;
- lr = 0;
- sp = fp;
- fp = nil;
-
- // If this was div or divu or mod or modu, the caller had
- // an extra 8 bytes on its stack. Adjust sp.
- if(f->entry == (uintptr)_div || f->entry == (uintptr)_divu || f->entry == (uintptr)_mod || f->entry == (uintptr)_modu)
- sp += 8;
-
- // If this was deferproc or newproc, the caller had an extra 12.
- if(f->entry == (uintptr)runtime·deferproc || f->entry == (uintptr)runtime·newproc)
- sp += 12;
-
+ frame.pc = frame.lr;
+ frame.fn = flr;
+ frame.lr = 0;
+ frame.sp = frame.fp;
+ frame.fp = 0;
+
// sighandler saves the lr on stack before faking a call to sigpanic
if(waspanic) {
- x = *(uintptr *)sp;
- sp += 4;
- f = runtime·findfunc(pc);
- if (f == nil) {
- pc = x;
- } else if (f->frame == 0)
- lr = x;
+ x = *(uintptr*)frame.sp;
+ frame.sp += 4;
+ frame.fn = f = runtime·findfunc(frame.pc);
+ if(f == nil)
+ frame.pc = x;
+ else if(f->frame == 0)
+ frame.lr = x;
}
}
- if(pcbuf == nil && fn == nil && (pc = gp->gopc) != 0 && (f = runtime·findfunc(pc)) != nil
- && runtime·showframe(f, gp == m->curg) && gp->goid != 1) {
- runtime·printf("created by %S\n", f->name);
+ if(pcbuf == nil && callback == nil)
+ n = nprint;
+
+ return n;
+}
+
+void
+runtime·printcreatedby(G *gp)
+{
+ int32 line;
+ uintptr pc, tracepc;
+ Func *f;
+ String file;
+
+ // Show what created goroutine, except main goroutine (goid 1).
+ if((pc = gp->gopc) != 0 && (f = runtime·findfunc(pc)) != nil &&
+ runtime·showframe(f, gp) && gp->goid != 1) {
+ runtime·printf("created by %s\n", runtime·funcname(f));
tracepc = pc; // back up to CALL instruction for funcline.
- if(n > 0 && pc > f->entry)
- tracepc -= sizeof(uintptr);
- runtime·printf("\t%S:%d", f->src, runtime·funcline(f, tracepc));
+ if(pc > f->entry)
+ tracepc -= PCQuantum;
+ line = runtime·funcline(f, tracepc, &file);
+ runtime·printf("\t%S:%d", file, line);
if(pc > f->entry)
runtime·printf(" +%p", (uintptr)(pc - f->entry));
runtime·printf("\n");
}
-
- return n;
}
void
-runtime·traceback(byte *pc0, byte *sp, byte *lr, G *gp)
+runtime·traceback(uintptr pc, uintptr sp, uintptr lr, G *gp)
{
if(gp->status == Gsyscall) {
// Override signal registers if blocked in system call.
- pc0 = gp->sched.pc;
- sp = (byte*)gp->sched.sp;
- lr = nil;
+ pc = gp->syscallpc;
+ sp = gp->syscallsp;
+ lr = 0;
}
- runtime·gentraceback(pc0, sp, lr, gp, 0, nil, 100, nil, nil);
+
+ // Print traceback. By default, omits runtime frames.
+ // If that means we print nothing at all, repeat forcing all frames printed.
+ if(runtime·gentraceback(pc, sp, lr, gp, 0, nil, 100, nil, nil, false) == 0)
+ runtime·gentraceback(pc, sp, lr, gp, 0, nil, 100, nil, nil, true);
+ runtime·printcreatedby(gp);
}
// func caller(n int) (pc uintptr, file string, line int, ok bool)
int32
runtime·callers(int32 skip, uintptr *pcbuf, int32 m)
{
- byte *pc, *sp;
+ uintptr pc, sp;
sp = runtime·getcallersp(&skip);
- pc = runtime·getcallerpc(&skip);
+ pc = (uintptr)runtime·getcallerpc(&skip);
- return runtime·gentraceback(pc, sp, 0, g, skip, pcbuf, m, nil, nil);
+ return runtime·gentraceback(pc, sp, 0, g, skip, pcbuf, m, nil, nil, false);
}
diff --git a/src/pkg/runtime/traceback_x86.c b/src/pkg/runtime/traceback_x86.c
index ce52df870..d658e8f11 100644
--- a/src/pkg/runtime/traceback_x86.c
+++ b/src/pkg/runtime/traceback_x86.c
@@ -7,11 +7,8 @@
#include "runtime.h"
#include "arch_GOARCH.h"
#include "malloc.h"
+#include "funcdata.h"
-void runtime·deferproc(void);
-void runtime·newproc(void);
-void runtime·newstack(void);
-void runtime·morestack(void);
void runtime·sigpanic(void);
// This code is also used for the 386 tracebacks.
@@ -19,40 +16,46 @@ void runtime·sigpanic(void);
// Generic traceback. Handles runtime stack prints (pcbuf == nil),
// the runtime.Callers function (pcbuf != nil), as well as the garbage
-// collector (fn != nil). A little clunky to merge the two but avoids
+// collector (callback != nil). A little clunky to merge these, but avoids
// duplicating the code and all its subtlety.
int32
-runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *gp, int32 skip, uintptr *pcbuf, int32 max, void (*fn)(Func*, byte*, byte*, void*), void *arg)
+runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip, uintptr *pcbuf, int32 max, void (*callback)(Stkframe*, void*), void *v, bool printall)
{
- int32 i, n, sawnewstack;
- uintptr pc, lr, tracepc;
- byte *fp;
+ int32 i, n, nprint, line;
+ uintptr tracepc;
+ bool waspanic, printing;
+ Func *f, *flr;
+ Stkframe frame;
Stktop *stk;
- Func *f;
- bool waspanic;
+ String file;
USED(lr0);
- pc = (uintptr)pc0;
- lr = 0;
- fp = nil;
+
+ nprint = 0;
+ runtime·memclr((byte*)&frame, sizeof frame);
+ frame.pc = pc0;
+ frame.sp = sp0;
waspanic = false;
-
- // If the PC is goexit, the goroutine hasn't started yet.
- if(pc0 == gp->sched.pc && sp == (byte*)gp->sched.sp && pc0 == (byte*)runtime·goexit && gp->fnstart != nil) {
- fp = sp;
- lr = pc;
- pc = (uintptr)gp->fnstart->fn;
- }
+ printing = pcbuf==nil && callback==nil;
// If the PC is zero, it's likely a nil function call.
// Start in the caller's frame.
- if(pc == 0) {
- pc = *(uintptr*)sp;
- sp += sizeof(uintptr);
+ if(frame.pc == 0) {
+ frame.pc = *(uintptr*)frame.sp;
+ frame.sp += sizeof(uintptr);
+ }
+
+ f = runtime·findfunc(frame.pc);
+ if(f == nil) {
+ if(callback != nil) {
+ runtime·printf("runtime: unknown pc %p\n", frame.pc);
+ runtime·throw("unknown pc");
+ }
+ return 0;
}
+ frame.fn = f;
n = 0;
- sawnewstack = 0;
stk = (Stktop*)gp->stackbase;
while(n < max) {
// Typically:
@@ -62,151 +65,185 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *gp, int32 skip, uintptr
// stk is the stack containing sp.
// The caller's program counter is lr, unless lr is zero, in which case it is *(uintptr*)sp.
- if(pc == (uintptr)runtime·lessstack) {
+ if(frame.pc == (uintptr)runtime·lessstack) {
// Hit top of stack segment. Unwind to next segment.
- pc = (uintptr)stk->gobuf.pc;
- sp = (byte*)stk->gobuf.sp;
- lr = 0;
- fp = nil;
- if(pcbuf == nil && fn == nil && runtime·showframe(nil, gp == m->curg))
+ frame.pc = stk->gobuf.pc;
+ frame.sp = stk->gobuf.sp;
+ frame.lr = 0;
+ frame.fp = 0;
+ frame.fn = nil;
+ if(printing && runtime·showframe(nil, gp))
runtime·printf("----- stack segment boundary -----\n");
stk = (Stktop*)stk->stackbase;
+
+ f = runtime·findfunc(frame.pc);
+ if(f == nil) {
+ runtime·printf("runtime: unknown pc %p after stack split\n", frame.pc);
+ if(callback != nil)
+ runtime·throw("unknown pc");
+ }
+ frame.fn = f;
continue;
}
- if(pc <= 0x1000 || (f = runtime·findfunc(pc)) == nil) {
- if(fn != nil)
- runtime·throw("unknown pc");
- break;
- }
+ f = frame.fn;
// Found an actual function.
- if(fp == nil) {
- fp = sp;
- if(pc > f->entry && f->frame >= sizeof(uintptr))
- fp += f->frame - sizeof(uintptr);
- if(lr == 0)
- lr = *(uintptr*)fp;
- fp += sizeof(uintptr);
- } else if(lr == 0)
- lr = *(uintptr*)fp;
-
- if(skip > 0)
+ // Derive frame pointer and link register.
+ if(frame.fp == 0) {
+ frame.fp = frame.sp + runtime·funcspdelta(f, frame.pc);
+ frame.fp += sizeof(uintptr); // caller PC
+ }
+ if(runtime·topofstack(f)) {
+ frame.lr = 0;
+ flr = nil;
+ } else {
+ if(frame.lr == 0)
+ frame.lr = ((uintptr*)frame.fp)[-1];
+ flr = runtime·findfunc(frame.lr);
+ if(flr == nil) {
+ runtime·printf("runtime: unexpected return pc for %s called from %p\n", runtime·funcname(f), frame.lr);
+ if(callback != nil)
+ runtime·throw("unknown caller pc");
+ }
+ }
+
+ frame.varp = (byte*)frame.fp - sizeof(uintptr);
+
+ // Derive size of arguments.
+ // Most functions have a fixed-size argument block,
+ // so we can use metadata about the function f.
+ // Not all, though: there are some variadic functions
+ // in package runtime and reflect, and for those we use call-specific
+ // metadata recorded by f's caller.
+ if(callback != nil || printing) {
+ frame.argp = (byte*)frame.fp;
+ if(f->args != ArgsSizeUnknown)
+ frame.arglen = f->args;
+ else if(flr == nil)
+ frame.arglen = 0;
+ else if(frame.lr == (uintptr)runtime·lessstack)
+ frame.arglen = stk->argsize;
+ else if((i = runtime·funcarglen(flr, frame.lr)) >= 0)
+ frame.arglen = i;
+ else {
+ runtime·printf("runtime: unknown argument frame size for %s called from %p [%s]\n",
+ runtime·funcname(f), frame.lr, flr ? runtime·funcname(flr) : "?");
+ if(callback != nil)
+ runtime·throw("invalid stack");
+ frame.arglen = 0;
+ }
+ }
+
+ if(skip > 0) {
skip--;
- else if(pcbuf != nil)
- pcbuf[n++] = pc;
- else if(fn != nil)
- (*fn)(f, (byte*)pc, sp, arg);
- else {
- if(runtime·showframe(f, gp == m->curg)) {
+ goto skipped;
+ }
+
+ if(pcbuf != nil)
+ pcbuf[n] = frame.pc;
+ if(callback != nil)
+ callback(&frame, v);
+ if(printing) {
+ if(printall || runtime·showframe(f, gp)) {
// Print during crash.
// main(0x1, 0x2, 0x3)
// /home/rsc/go/src/runtime/x.go:23 +0xf
//
- tracepc = pc; // back up to CALL instruction for funcline.
- if(n > 0 && pc > f->entry && !waspanic)
+ tracepc = frame.pc; // back up to CALL instruction for funcline.
+ if(n > 0 && frame.pc > f->entry && !waspanic)
tracepc--;
- if(m->throwing && gp == m->curg)
- runtime·printf("[fp=%p] ", fp);
- runtime·printf("%S(", f->name);
- for(i = 0; i < f->args/sizeof(uintptr); i++) {
- if(i != 0)
- runtime·prints(", ");
- runtime·printhex(((uintptr*)fp)[i]);
- if(i >= 4) {
+ runtime·printf("%s(", runtime·funcname(f));
+ for(i = 0; i < frame.arglen/sizeof(uintptr); i++) {
+ if(i >= 5) {
runtime·prints(", ...");
break;
}
+ if(i != 0)
+ runtime·prints(", ");
+ runtime·printhex(((uintptr*)frame.argp)[i]);
}
runtime·prints(")\n");
- runtime·printf("\t%S:%d", f->src, runtime·funcline(f, tracepc));
- if(pc > f->entry)
- runtime·printf(" +%p", (uintptr)(pc - f->entry));
+ line = runtime·funcline(f, tracepc, &file);
+ runtime·printf("\t%S:%d", file, line);
+ if(frame.pc > f->entry)
+ runtime·printf(" +%p", (uintptr)(frame.pc - f->entry));
+ if(m->throwing > 0 && gp == m->curg)
+ runtime·printf(" fp=%p", frame.fp);
runtime·printf("\n");
+ nprint++;
}
- n++;
}
-
+ n++;
+
+ skipped:
waspanic = f->entry == (uintptr)runtime·sigpanic;
- if(f->entry == (uintptr)runtime·deferproc || f->entry == (uintptr)runtime·newproc)
- fp += 2*sizeof(uintptr);
-
- if(f->entry == (uintptr)runtime·newstack)
- sawnewstack = 1;
-
- if(pcbuf == nil && fn == nil && f->entry == (uintptr)runtime·morestack && gp == m->g0 && sawnewstack) {
- // The fact that we saw newstack means that morestack
- // has managed to record its information in m, so we can
- // use it to keep unwinding the stack.
- runtime·printf("----- morestack called from goroutine %D -----\n", m->curg->goid);
- pc = (uintptr)m->morepc;
- sp = (byte*)m->morebuf.sp - sizeof(void*);
- lr = (uintptr)m->morebuf.pc;
- fp = (byte*)m->morebuf.sp;
- sawnewstack = 0;
- gp = m->curg;
- stk = (Stktop*)gp->stackbase;
- continue;
- }
-
- if(pcbuf == nil && fn == nil && f->entry == (uintptr)runtime·lessstack && gp == m->g0) {
- // Lessstack is running on scheduler stack. Switch to original goroutine.
- runtime·printf("----- lessstack called from goroutine %D -----\n", m->curg->goid);
- gp = m->curg;
- stk = (Stktop*)gp->stackbase;
- sp = (byte*)stk->gobuf.sp;
- pc = (uintptr)stk->gobuf.pc;
- fp = nil;
- lr = 0;
- continue;
- }
-
// Do not unwind past the bottom of the stack.
- if(pc == (uintptr)runtime·goexit)
+ if(flr == nil)
break;
// Unwind to next frame.
- pc = lr;
- lr = 0;
- sp = fp;
- fp = nil;
+ frame.fn = flr;
+ frame.pc = frame.lr;
+ frame.lr = 0;
+ frame.sp = frame.fp;
+ frame.fp = 0;
}
+ if(pcbuf == nil && callback == nil)
+ n = nprint;
+
+ return n;
+}
+
+void
+runtime·printcreatedby(G *gp)
+{
+ int32 line;
+ uintptr pc, tracepc;
+ Func *f;
+ String file;
+
// Show what created goroutine, except main goroutine (goid 1).
- if(pcbuf == nil && fn == nil && (pc = gp->gopc) != 0 && (f = runtime·findfunc(pc)) != nil
- && runtime·showframe(f, gp == m->curg) && gp->goid != 1) {
- runtime·printf("created by %S\n", f->name);
+ if((pc = gp->gopc) != 0 && (f = runtime·findfunc(pc)) != nil &&
+ runtime·showframe(f, gp) && gp->goid != 1) {
+ runtime·printf("created by %s\n", runtime·funcname(f));
tracepc = pc; // back up to CALL instruction for funcline.
- if(n > 0 && pc > f->entry)
- tracepc--;
- runtime·printf("\t%S:%d", f->src, runtime·funcline(f, tracepc));
+ if(pc > f->entry)
+ tracepc -= PCQuantum;
+ line = runtime·funcline(f, tracepc, &file);
+ runtime·printf("\t%S:%d", file, line);
if(pc > f->entry)
runtime·printf(" +%p", (uintptr)(pc - f->entry));
runtime·printf("\n");
}
-
- return n;
}
void
-runtime·traceback(byte *pc0, byte *sp, byte*, G *gp)
+runtime·traceback(uintptr pc, uintptr sp, uintptr lr, G *gp)
{
+ USED(lr);
+
if(gp->status == Gsyscall) {
// Override signal registers if blocked in system call.
- pc0 = gp->sched.pc;
- sp = (byte*)gp->sched.sp;
+ pc = gp->syscallpc;
+ sp = gp->syscallsp;
}
- runtime·gentraceback(pc0, sp, nil, gp, 0, nil, 100, nil, nil);
+
+ // Print traceback. By default, omits runtime frames.
+ // If that means we print nothing at all, repeat forcing all frames printed.
+ if(runtime·gentraceback(pc, sp, 0, gp, 0, nil, 100, nil, nil, false) == 0)
+ runtime·gentraceback(pc, sp, 0, gp, 0, nil, 100, nil, nil, true);
+ runtime·printcreatedby(gp);
}
int32
runtime·callers(int32 skip, uintptr *pcbuf, int32 m)
{
- byte *pc, *sp;
+ uintptr pc, sp;
- // our caller's pc, sp.
- sp = (byte*)&skip;
- pc = runtime·getcallerpc(&skip);
+ sp = runtime·getcallersp(&skip);
+ pc = (uintptr)runtime·getcallerpc(&skip);
- return runtime·gentraceback(pc, sp, nil, g, skip, pcbuf, m, nil, nil);
+ return runtime·gentraceback(pc, sp, 0, g, skip, pcbuf, m, nil, nil, false);
}
diff --git a/src/pkg/runtime/type.h b/src/pkg/runtime/type.h
index 769a8071b..30936046c 100644
--- a/src/pkg/runtime/type.h
+++ b/src/pkg/runtime/type.h
@@ -70,6 +70,8 @@ struct MapType
Type;
Type *key;
Type *elem;
+ Type *bucket; // internal type representing a hash bucket
+ Type *hmap; // internal type representing a Hmap
};
struct ChanType
@@ -98,3 +100,7 @@ struct PtrType
Type;
Type *elem;
};
+
+// Here instead of in runtime.h because it uses the type names.
+bool runtime·addfinalizer(void*, FuncVal *fn, uintptr, Type*, PtrType*);
+bool runtime·getfinalizer(void *p, bool del, FuncVal **fn, uintptr *nret, Type**, PtrType**);
diff --git a/src/pkg/runtime/vlop_386.s b/src/pkg/runtime/vlop_386.s
index 28f6da82d..9783fdc93 100644
--- a/src/pkg/runtime/vlop_386.s
+++ b/src/pkg/runtime/vlop_386.s
@@ -23,11 +23,15 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#include "../../cmd/ld/textflag.h"
+
/*
* C runtime for 64-bit divide.
*/
-TEXT _mul64by32(SB), 7, $0
+// _mul64x32(r *uint64, a uint64, b uint32)
+// sets *r = low 64 bits of 96-bit product a*b; returns high 32 bits.
+TEXT _mul64by32(SB), NOSPLIT, $0
MOVL r+0(FP), CX
MOVL a+4(FP), AX
MULL b+12(FP)
@@ -36,10 +40,12 @@ TEXT _mul64by32(SB), 7, $0
MOVL a+8(FP), AX
MULL b+12(FP)
ADDL AX, BX
+ ADCL $0, DX
MOVL BX, 4(CX)
+ MOVL DX, AX
RET
-TEXT _div64by32(SB), 7, $0
+TEXT _div64by32(SB), NOSPLIT, $0
MOVL r+12(FP), CX
MOVL a+0(FP), AX
MOVL a+4(FP), DX
diff --git a/src/pkg/runtime/vlop_arm.s b/src/pkg/runtime/vlop_arm.s
index 0dedc316a..d7c566afb 100644
--- a/src/pkg/runtime/vlop_arm.s
+++ b/src/pkg/runtime/vlop_arm.s
@@ -23,11 +23,14 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#include "zasm_GOOS_GOARCH.h"
+#include "../../cmd/ld/textflag.h"
+
arg=0
/* replaced use of R10 by R11 because the former can be the data segment base register */
-TEXT _mulv(SB), $0
+TEXT _mulv(SB), NOSPLIT, $0
MOVW 0(FP), R0
MOVW 4(FP), R2 /* l0 */
MOVW 8(FP), R11 /* h0 */
@@ -45,7 +48,7 @@ TEXT _mulv(SB), $0
// trampoline for _sfloat2. passes LR as arg0 and
// saves registers R0-R13 and CPSR on the stack. R0-R12 and CPSR flags can
// be changed by _sfloat2.
-TEXT _sfloat(SB), 7, $64 // 4 arg + 14*4 saved regs + cpsr
+TEXT _sfloat(SB), NOSPLIT, $64-0 // 4 arg + 14*4 saved regs + cpsr
MOVW R14, 4(R13)
MOVW R0, 8(R13)
MOVW $12(R13), R0
@@ -54,12 +57,41 @@ TEXT _sfloat(SB), 7, $64 // 4 arg + 14*4 saved regs + cpsr
MOVW R1, 60(R13)
WORD $0xe10f1000 // mrs r1, cpsr
MOVW R1, 64(R13)
+ // Disable preemption of this goroutine during _sfloat2 by
+ // m->locks++ and m->locks-- around the call.
+ // Rescheduling this goroutine may cause the loss of the
+ // contents of the software floating point registers in
+ // m->freghi, m->freglo, m->fflag, if the goroutine is moved
+ // to a different m or another goroutine runs on this m.
+ // Rescheduling at ordinary function calls is okay because
+ // all registers are caller save, but _sfloat2 and the things
+ // that it runs are simulating the execution of individual
+ // program instructions, and those instructions do not expect
+ // the floating point registers to be lost.
+ // An alternative would be to move the software floating point
+ // registers into G, but they do not need to be kept at the
+ // usual places a goroutine reschedules (at function calls),
+ // so it would be a waste of 132 bytes per G.
+ MOVW m_locks(m), R1
+ ADD $1, R1
+ MOVW R1, m_locks(m)
BL runtime·_sfloat2(SB)
+ MOVW m_locks(m), R1
+ SUB $1, R1
+ MOVW R1, m_locks(m)
MOVW R0, 0(R13)
MOVW 64(R13), R1
WORD $0xe128f001 // msr cpsr_f, r1
MOVW $12(R13), R0
- MOVM.IA.W (R0), [R1-R12]
+ // Restore R1-R8 and R11-R12, but ignore the saved R9 (m) and R10 (g).
+ // Both are maintained by the runtime and always have correct values,
+ // so there is no need to restore old values here.
+ // The g should not have changed, but m may have, if we were preempted
+ // and restarted on a different thread, in which case restoring the old
+ // value is incorrect and will cause serious confusion in the runtime.
+ MOVM.IA.W (R0), [R1-R8]
+ MOVW $52(R13), R0
+ MOVM.IA.W (R0), [R11-R12]
MOVW 8(R13), R0
RET
@@ -70,48 +102,43 @@ TEXT _sfloat(SB), 7, $64 // 4 arg + 14*4 saved regs + cpsr
q = 0 // input d, output q
r = 1 // input n, output r
s = 2 // three temporary variables
-m = 3
+M = 3
a = 11
-// Please be careful when changing this, it is pretty fragile:
-// 1, don't use unconditional branch as the linker is free to reorder the blocks;
-// 2. if a == 11, beware that the linker will use R11 if you use certain instructions.
-TEXT udiv<>(SB),7,$-4
+// Be careful: R(a) == R11 will be used by the linker for synthesized instructions.
+TEXT udiv<>(SB),NOSPLIT,$-4
CLZ R(q), R(s) // find normalizing shift
MOVW.S R(q)<<R(s), R(a)
- ADD R(a)>>25, PC, R(a) // most significant 7 bits of divisor
- MOVBU.NE (4*36-64)(R(a)), R(a) // 36 == number of inst. between fast_udiv_tab and begin
+ MOVW $fast_udiv_tab<>-64(SB), R(M)
+ MOVBU.NE R(a)>>25(R(M)), R(a) // index by most significant 7 bits of divisor
-begin:
SUB.S $7, R(s)
- RSB $0, R(q), R(m) // m = -q
+ RSB $0, R(q), R(M) // M = -q
MOVW.PL R(a)<<R(s), R(q)
// 1st Newton iteration
- MUL.PL R(m), R(q), R(a) // a = -q*d
+ MUL.PL R(M), R(q), R(a) // a = -q*d
BMI udiv_by_large_d
MULAWT R(a), R(q), R(q), R(q) // q approx q-(q*q*d>>32)
- TEQ R(m)->1, R(m) // check for d=0 or d=1
+ TEQ R(M)->1, R(M) // check for d=0 or d=1
// 2nd Newton iteration
- MUL.NE R(m), R(q), R(a)
+ MUL.NE R(M), R(q), R(a)
MOVW.NE $0, R(s)
MULAL.NE R(q), R(a), (R(q),R(s))
BEQ udiv_by_0_or_1
// q now accurate enough for a remainder r, 0<=r<3*d
MULLU R(q), R(r), (R(q),R(s)) // q = (r * q) >> 32
- ADD R(m), R(r), R(r) // r = n - d
- MULA R(m), R(q), R(r), R(r) // r = n - (q+1)*d
+ ADD R(M), R(r), R(r) // r = n - d
+ MULA R(M), R(q), R(r), R(r) // r = n - (q+1)*d
// since 0 <= n-q*d < 3*d; thus -d <= r < 2*d
- CMN R(m), R(r) // t = r-d
- SUB.CS R(m), R(r), R(r) // if (t<-d || t>=0) r=r+d
+ CMN R(M), R(r) // t = r-d
+ SUB.CS R(M), R(r), R(r) // if (t<-d || t>=0) r=r+d
ADD.CC $1, R(q)
- ADD.PL R(m)<<1, R(r)
+ ADD.PL R(M)<<1, R(r)
ADD.PL $2, R(q)
-
- // return, can't use RET here or fast_udiv_tab will be dropped during linking
- MOVW R14, R15
+ RET
udiv_by_large_d:
// at this point we know d>=2^(31-6)=2^25
@@ -119,29 +146,43 @@ udiv_by_large_d:
RSB $0, R(s), R(s)
MOVW R(a)>>R(s), R(q)
MULLU R(q), R(r), (R(q),R(s))
- MULA R(m), R(q), R(r), R(r)
+ MULA R(M), R(q), R(r), R(r)
// q now accurate enough for a remainder r, 0<=r<4*d
- CMN R(r)>>1, R(m) // if(r/2 >= d)
- ADD.CS R(m)<<1, R(r)
+ CMN R(r)>>1, R(M) // if(r/2 >= d)
+ ADD.CS R(M)<<1, R(r)
ADD.CS $2, R(q)
- CMN R(r), R(m)
- ADD.CS R(m), R(r)
+ CMN R(r), R(M)
+ ADD.CS R(M), R(r)
ADD.CS $1, R(q)
-
- // return, can't use RET here or fast_udiv_tab will be dropped during linking
- MOVW R14, R15
+ RET
udiv_by_0_or_1:
// carry set if d==1, carry clear if d==0
- MOVW.CS R(r), R(q)
- MOVW.CS $0, R(r)
- BL.CC runtime·panicdivide(SB) // no way back
+ BCC udiv_by_0
+ MOVW R(r), R(q)
+ MOVW $0, R(r)
+ RET
- // return, can't use RET here or fast_udiv_tab will be dropped during linking
- MOVW R14, R15
+udiv_by_0:
+ // The ARM toolchain expects it can emit references to DIV and MOD
+ // instructions. The linker rewrites each pseudo-instruction into
+ // a sequence that pushes two values onto the stack and then calls
+ // _divu, _modu, _div, or _mod (below), all of which have a 16-byte
+ // frame plus the saved LR. The traceback routine knows the expanded
+ // stack frame size at the pseudo-instruction call site, but it
+ // doesn't know that the frame has a non-standard layout. In particular,
+ // it expects to find a saved LR in the bottom word of the frame.
+ // Unwind the stack back to the pseudo-instruction call site, copy the
+ // saved LR where the traceback routine will look for it, and make it
+ // appear that panicdivide was called from that PC.
+ MOVW 0(R13), LR
+ ADD $20, R13
+ MOVW 8(R13), R1 // actual saved LR
+ MOVW R1, 0(R13) // expected here for traceback
+ B runtime·panicdivide(SB)
-fast_udiv_tab:
+TEXT fast_udiv_tab<>(SB),NOSPLIT,$-4
// var tab [64]byte
// tab[0] = 255; for i := 1; i <= 63; i++ { tab[i] = (1<<14)/(64+i) }
// laid out here as little-endian uint32s
@@ -166,11 +207,11 @@ fast_udiv_tab:
// expects the result in R(TMP)
TMP = 11
-TEXT _divu(SB), 7, $16
+TEXT _divu(SB), NOSPLIT, $16
MOVW R(q), 4(R13)
MOVW R(r), 8(R13)
MOVW R(s), 12(R13)
- MOVW R(m), 16(R13)
+ MOVW R(M), 16(R13)
MOVW R(TMP), R(r) /* numerator */
MOVW 0(FP), R(q) /* denominator */
@@ -179,14 +220,14 @@ TEXT _divu(SB), 7, $16
MOVW 4(R13), R(q)
MOVW 8(R13), R(r)
MOVW 12(R13), R(s)
- MOVW 16(R13), R(m)
+ MOVW 16(R13), R(M)
RET
-TEXT _modu(SB), 7, $16
+TEXT _modu(SB), NOSPLIT, $16
MOVW R(q), 4(R13)
MOVW R(r), 8(R13)
MOVW R(s), 12(R13)
- MOVW R(m), 16(R13)
+ MOVW R(M), 16(R13)
MOVW R(TMP), R(r) /* numerator */
MOVW 0(FP), R(q) /* denominator */
@@ -195,14 +236,14 @@ TEXT _modu(SB), 7, $16
MOVW 4(R13), R(q)
MOVW 8(R13), R(r)
MOVW 12(R13), R(s)
- MOVW 16(R13), R(m)
+ MOVW 16(R13), R(M)
RET
-TEXT _div(SB),7,$16
+TEXT _div(SB),NOSPLIT,$16
MOVW R(q), 4(R13)
MOVW R(r), 8(R13)
MOVW R(s), 12(R13)
- MOVW R(m), 16(R13)
+ MOVW R(M), 16(R13)
MOVW R(TMP), R(r) /* numerator */
MOVW 0(FP), R(q) /* denominator */
CMP $0, R(r)
@@ -224,11 +265,11 @@ d2:
RSB $0, R(q), R(TMP)
B out
-TEXT _mod(SB),7,$16
+TEXT _mod(SB),NOSPLIT,$16
MOVW R(q), 4(R13)
MOVW R(r), 8(R13)
MOVW R(s), 12(R13)
- MOVW R(m), 16(R13)
+ MOVW R(M), 16(R13)
MOVW R(TMP), R(r) /* numerator */
MOVW 0(FP), R(q) /* denominator */
CMP $0, R(q)
@@ -246,5 +287,5 @@ out:
MOVW 4(R13), R(q)
MOVW 8(R13), R(r)
MOVW 12(R13), R(s)
- MOVW 16(R13), R(m)
+ MOVW 16(R13), R(M)
RET
diff --git a/src/pkg/runtime/vlrt_386.c b/src/pkg/runtime/vlrt_386.c
index 1631dbe10..8d965c086 100644
--- a/src/pkg/runtime/vlrt_386.c
+++ b/src/pkg/runtime/vlrt_386.c
@@ -23,6 +23,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#include "../../cmd/ld/textflag.h"
+
/*
* C runtime for 64-bit divide, others.
*
@@ -145,7 +147,7 @@ _v2f(Vlong x)
}
ulong _div64by32(Vlong, ulong, ulong*);
-void _mul64by32(Vlong*, Vlong, ulong);
+int _mul64by32(Vlong*, Vlong, ulong);
static void
slowdodiv(Vlong num, Vlong den, Vlong *q, Vlong *r)
@@ -230,8 +232,7 @@ dodiv(Vlong num, Vlong den, Vlong *qp, Vlong *rp)
if(den.hi != 0){
q.hi = 0;
n = num.hi/den.hi;
- _mul64by32(&x, den, n);
- if(x.hi > num.hi || (x.hi == num.hi && x.lo > num.lo))
+ if(_mul64by32(&x, den, n) || x.hi > num.hi || (x.hi == num.hi && x.lo > num.lo))
slowdodiv(num, den, &q, &r);
else {
q.lo = n;
@@ -423,6 +424,7 @@ _rshlv(Vlong *r, Vlong a, int b)
r->lo = (t << (32-b)) | (a.lo >> b);
}
+#pragma textflag NOSPLIT
void
_lshv(Vlong *r, Vlong a, int b)
{
diff --git a/src/pkg/runtime/vlrt_arm.c b/src/pkg/runtime/vlrt_arm.c
index ab8050177..219163c60 100644
--- a/src/pkg/runtime/vlrt_arm.c
+++ b/src/pkg/runtime/vlrt_arm.c
@@ -23,6 +23,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#include "../../cmd/ld/textflag.h"
+
// declared here to avoid include of runtime.h
void runtime·panicstring(char*);
@@ -62,30 +64,24 @@ struct Vlong
void runtime·abort(void);
+#pragma textflag NOSPLIT
void
_addv(Vlong *r, Vlong a, Vlong b)
{
- ulong lo, hi;
-
- lo = a.lo + b.lo;
- hi = a.hi + b.hi;
- if(lo < a.lo)
- hi++;
- r->lo = lo;
- r->hi = hi;
+ r->lo = a.lo + b.lo;
+ r->hi = a.hi + b.hi;
+ if(r->lo < a.lo)
+ r->hi++;
}
+#pragma textflag NOSPLIT
void
_subv(Vlong *r, Vlong a, Vlong b)
{
- ulong lo, hi;
-
- lo = a.lo - b.lo;
- hi = a.hi - b.hi;
- if(lo > a.lo)
- hi--;
- r->lo = lo;
- r->hi = hi;
+ r->lo = a.lo - b.lo;
+ r->hi = a.hi - b.hi;
+ if(r->lo > a.lo)
+ r->hi--;
}
void
@@ -427,12 +423,10 @@ _rshlv(Vlong *r, Vlong a, int b)
r->lo = (t << (32-b)) | (a.lo >> b);
}
+#pragma textflag NOSPLIT
void
_lshv(Vlong *r, Vlong a, int b)
{
- ulong t;
-
- t = a.lo;
if(b >= 32) {
r->lo = 0;
if(b >= 64) {
@@ -440,16 +434,16 @@ _lshv(Vlong *r, Vlong a, int b)
r->hi = 0;
return;
}
- r->hi = t << (b-32);
+ r->hi = a.lo << (b-32);
return;
}
if(b <= 0) {
- r->lo = t;
+ r->lo = a.lo;
r->hi = a.hi;
return;
}
- r->lo = t << b;
- r->hi = (t >> (32-b)) | (a.hi << b);
+ r->lo = a.lo << b;
+ r->hi = (a.lo >> (32-b)) | (a.hi << b);
}
void
@@ -623,14 +617,12 @@ _ul2v(Vlong *ret, ulong ul)
ret->hi = 0;
}
+#pragma textflag NOSPLIT
void
_si2v(Vlong *ret, int si)
{
- long t;
-
- t = si;
- ret->lo = t;
- ret->hi = t >> 31;
+ ret->lo = (long)si;
+ ret->hi = (long)si >> 31;
}
void
@@ -729,6 +721,7 @@ _v2ul(Vlong rv)
return rv.lo;
}
+#pragma textflag NOSPLIT
long
_v2si(Vlong rv)
{
@@ -782,6 +775,7 @@ _gtv(Vlong lv, Vlong rv)
(lv.hi == rv.hi && lv.lo > rv.lo);
}
+#pragma textflag NOSPLIT
int
_gev(Vlong lv, Vlong rv)
{
diff --git a/src/pkg/sort/example_interface_test.go b/src/pkg/sort/example_interface_test.go
index 4c88821be..442204ea9 100644
--- a/src/pkg/sort/example_interface_test.go
+++ b/src/pkg/sort/example_interface_test.go
@@ -9,69 +9,36 @@ import (
"sort"
)
-type Grams int
-
-func (g Grams) String() string { return fmt.Sprintf("%dg", int(g)) }
-
-type Organ struct {
- Name string
- Weight Grams
+type Person struct {
+ Name string
+ Age int
}
-type Organs []*Organ
-
-func (s Organs) Len() int { return len(s) }
-func (s Organs) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
-
-// ByName implements sort.Interface by providing Less and using the Len and
-// Swap methods of the embedded Organs value.
-type ByName struct{ Organs }
-
-func (s ByName) Less(i, j int) bool { return s.Organs[i].Name < s.Organs[j].Name }
+func (p Person) String() string {
+ return fmt.Sprintf("%s: %d", p.Name, p.Age)
+}
-// ByWeight implements sort.Interface by providing Less and using the Len and
-// Swap methods of the embedded Organs value.
-type ByWeight struct{ Organs }
+// ByAge implements sort.Interface for []Person based on
+// the Age field.
+type ByAge []Person
-func (s ByWeight) Less(i, j int) bool { return s.Organs[i].Weight < s.Organs[j].Weight }
+func (a ByAge) Len() int { return len(a) }
+func (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }
-func ExampleInterface() {
- s := []*Organ{
- {"brain", 1340},
- {"heart", 290},
- {"liver", 1494},
- {"pancreas", 131},
- {"prostate", 62},
- {"spleen", 162},
+func Example() {
+ people := []Person{
+ {"Bob", 31},
+ {"John", 42},
+ {"Michael", 17},
+ {"Jenny", 26},
}
- sort.Sort(ByWeight{s})
- fmt.Println("Organs by weight:")
- printOrgans(s)
-
- sort.Sort(ByName{s})
- fmt.Println("Organs by name:")
- printOrgans(s)
+ fmt.Println(people)
+ sort.Sort(ByAge(people))
+ fmt.Println(people)
// Output:
- // Organs by weight:
- // prostate (62g)
- // pancreas (131g)
- // spleen (162g)
- // heart (290g)
- // brain (1340g)
- // liver (1494g)
- // Organs by name:
- // brain (1340g)
- // heart (290g)
- // liver (1494g)
- // pancreas (131g)
- // prostate (62g)
- // spleen (162g)
-}
-
-func printOrgans(s []*Organ) {
- for _, o := range s {
- fmt.Printf("%-8s (%v)\n", o.Name, o.Weight)
- }
+ // [Bob: 31 John: 42 Michael: 17 Jenny: 26]
+ // [Michael: 17 Jenny: 26 Bob: 31 John: 42]
}
diff --git a/src/pkg/sort/example_multi_test.go b/src/pkg/sort/example_multi_test.go
index d0a9e7dc3..ac316540f 100644
--- a/src/pkg/sort/example_multi_test.go
+++ b/src/pkg/sort/example_multi_test.go
@@ -26,6 +26,7 @@ type multiSorter struct {
// Sort sorts the argument slice according to the less functions passed to OrderedBy.
func (ms *multiSorter) Sort(changes []Change) {
+ ms.changes = changes
sort.Sort(ms)
}
@@ -33,8 +34,7 @@ func (ms *multiSorter) Sort(changes []Change) {
// Call its Sort method to sort the data.
func OrderedBy(less ...lessFunc) *multiSorter {
return &multiSorter{
- changes: changes,
- less: less,
+ less: less,
}
}
@@ -108,11 +108,10 @@ func Example_sortMultiKeys() {
OrderedBy(user).Sort(changes)
fmt.Println("By user:", changes)
- // multiSorter implements the Sort interface, so we can also do this.
- sort.Sort(OrderedBy(user, increasingLines))
+ // More examples.
+ OrderedBy(user, increasingLines).Sort(changes)
fmt.Println("By user,<lines:", changes)
- // More examples.
OrderedBy(user, decreasingLines).Sort(changes)
fmt.Println("By user,>lines:", changes)
@@ -123,10 +122,10 @@ func Example_sortMultiKeys() {
fmt.Println("By language,<lines,user:", changes)
// Output:
- //By user: [{dmr C 100} {glenda Go 200} {gri Smalltalk 80} {gri Go 100} {ken Go 200} {ken C 150} {r Go 100} {r C 150} {rsc Go 200}]
- //By user,<lines: [{dmr C 100} {glenda Go 200} {gri Smalltalk 80} {gri Go 100} {ken C 150} {ken Go 200} {r Go 100} {r C 150} {rsc Go 200}]
- //By user,>lines: [{dmr C 100} {glenda Go 200} {gri Go 100} {gri Smalltalk 80} {ken Go 200} {ken C 150} {r C 150} {r Go 100} {rsc Go 200}]
- //By language,<lines: [{dmr C 100} {ken C 150} {r C 150} {gri Go 100} {r Go 100} {ken Go 200} {glenda Go 200} {rsc Go 200} {gri Smalltalk 80}]
- //By language,<lines,user: [{dmr C 100} {ken C 150} {r C 150} {gri Go 100} {r Go 100} {glenda Go 200} {ken Go 200} {rsc Go 200} {gri Smalltalk 80}]
+ // By user: [{dmr C 100} {glenda Go 200} {gri Smalltalk 80} {gri Go 100} {ken Go 200} {ken C 150} {r Go 100} {r C 150} {rsc Go 200}]
+ // By user,<lines: [{dmr C 100} {glenda Go 200} {gri Smalltalk 80} {gri Go 100} {ken C 150} {ken Go 200} {r Go 100} {r C 150} {rsc Go 200}]
+ // By user,>lines: [{dmr C 100} {glenda Go 200} {gri Go 100} {gri Smalltalk 80} {ken Go 200} {ken C 150} {r C 150} {r Go 100} {rsc Go 200}]
+ // By language,<lines: [{dmr C 100} {ken C 150} {r C 150} {gri Go 100} {r Go 100} {ken Go 200} {glenda Go 200} {rsc Go 200} {gri Smalltalk 80}]
+ // By language,<lines,user: [{dmr C 100} {ken C 150} {r C 150} {gri Go 100} {r Go 100} {glenda Go 200} {ken Go 200} {rsc Go 200} {gri Smalltalk 80}]
}
diff --git a/src/pkg/sort/example_wrapper_test.go b/src/pkg/sort/example_wrapper_test.go
new file mode 100644
index 000000000..cf6d74cf7
--- /dev/null
+++ b/src/pkg/sort/example_wrapper_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 sort_test
+
+import (
+ "fmt"
+ "sort"
+)
+
+type Grams int
+
+func (g Grams) String() string { return fmt.Sprintf("%dg", int(g)) }
+
+type Organ struct {
+ Name string
+ Weight Grams
+}
+
+type Organs []*Organ
+
+func (s Organs) Len() int { return len(s) }
+func (s Organs) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+
+// ByName implements sort.Interface by providing Less and using the Len and
+// Swap methods of the embedded Organs value.
+type ByName struct{ Organs }
+
+func (s ByName) Less(i, j int) bool { return s.Organs[i].Name < s.Organs[j].Name }
+
+// ByWeight implements sort.Interface by providing Less and using the Len and
+// Swap methods of the embedded Organs value.
+type ByWeight struct{ Organs }
+
+func (s ByWeight) Less(i, j int) bool { return s.Organs[i].Weight < s.Organs[j].Weight }
+
+func Example_sortWrapper() {
+ s := []*Organ{
+ {"brain", 1340},
+ {"heart", 290},
+ {"liver", 1494},
+ {"pancreas", 131},
+ {"prostate", 62},
+ {"spleen", 162},
+ }
+
+ sort.Sort(ByWeight{s})
+ fmt.Println("Organs by weight:")
+ printOrgans(s)
+
+ sort.Sort(ByName{s})
+ fmt.Println("Organs by name:")
+ printOrgans(s)
+
+ // Output:
+ // Organs by weight:
+ // prostate (62g)
+ // pancreas (131g)
+ // spleen (162g)
+ // heart (290g)
+ // brain (1340g)
+ // liver (1494g)
+ // Organs by name:
+ // brain (1340g)
+ // heart (290g)
+ // liver (1494g)
+ // pancreas (131g)
+ // prostate (62g)
+ // spleen (162g)
+}
+
+func printOrgans(s []*Organ) {
+ for _, o := range s {
+ fmt.Printf("%-8s (%v)\n", o.Name, o.Weight)
+ }
+}
diff --git a/src/pkg/sort/search_test.go b/src/pkg/sort/search_test.go
index ee95c663c..29b8d62df 100644
--- a/src/pkg/sort/search_test.go
+++ b/src/pkg/sort/search_test.go
@@ -128,6 +128,9 @@ func runSearchWrappers() {
}
func TestSearchWrappersDontAlloc(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping malloc count in short mode")
+ }
if runtime.GOMAXPROCS(0) > 1 {
t.Skip("skipping; GOMAXPROCS>1")
}
diff --git a/src/pkg/sort/sort.go b/src/pkg/sort/sort.go
index d3092e801..f06eb3827 100644
--- a/src/pkg/sort/sort.go
+++ b/src/pkg/sort/sort.go
@@ -12,8 +12,8 @@ package sort
type Interface interface {
// Len is the number of elements in the collection.
Len() int
- // Less returns whether the element with index i should sort
- // before the element with index j.
+ // Less reports whether the element with
+ // index i should sort before the element with index j.
Less(i, j int) bool
// Swap swaps the elements with indexes i and j.
Swap(i, j int)
@@ -283,3 +283,192 @@ func Float64sAreSorted(a []float64) bool { return IsSorted(Float64Slice(a)) }
// StringsAreSorted tests whether a slice of strings is sorted in increasing order.
func StringsAreSorted(a []string) bool { return IsSorted(StringSlice(a)) }
+
+// Notes on stable sorting:
+// The used algorithms are simple and provable correct on all input and use
+// only logarithmic additional stack space. They perform well if compared
+// experimentaly to other stable in-place sorting algorithms.
+//
+// Remarks on other algoritms evaluated:
+// - GCC's 4.6.3 stable_sort with merge_without_buffer from libstdc++:
+// Not faster.
+// - GCC's __rotate for block rotations: Not faster.
+// - "Practical in-place mergesort" from Jyrki Katajainen, Tomi A. Pasanen
+// and Jukka Teuhola; Nordic Journal of Computing 3,1 (1996), 27-40:
+// The given algorithms are in-place, number of Swap and Assignments
+// grow as n log n but the algorithm is not stable.
+// - "Fast Stable In-Plcae Sorting with O(n) Data Moves" J.I. Munro and
+// V. Raman in Algorithmica (1996) 16, 115-160:
+// This algorithm either needs additional 2n bits or works only if there
+// are enough different elements available to encode some permutations
+// which have to be undone later (so not stable an any input).
+// - All the optimal in-place sorting/merging algorithms I found are either
+// unstable or rely on enough different elements in each step to encode the
+// performed block rearrangements. See also "In-Place Merging Algorithms",
+// Denham Coates-Evely, Department of Computer Science, Kings College,
+// January 2004 and the reverences in there.
+// - Often "optimal" algorithms are optimal in the number of assignments
+// but Interface has only Swap as operation.
+
+// Stable sorts data while keeping the original order of equal elements.
+//
+// It makes one call to data.Len to determine n, O(n*log(n)) calls to
+// data.Less and O(n*log(n)*log(n)) calls to data.Swap.
+func Stable(data Interface) {
+ n := data.Len()
+ blockSize := 20
+ a, b := 0, blockSize
+ for b <= n {
+ insertionSort(data, a, b)
+ a = b
+ b += blockSize
+ }
+ insertionSort(data, a, n)
+
+ for blockSize < n {
+ a, b = 0, 2*blockSize
+ for b <= n {
+ symMerge(data, a, a+blockSize, b)
+ a = b
+ b += 2 * blockSize
+ }
+ symMerge(data, a, a+blockSize, n)
+ blockSize *= 2
+ }
+}
+
+// SymMerge merges the two sorted subsequences data[a:m] and data[m:b] using
+// the SymMerge algorithm from Pok-Son Kim and Arne Kutzner, "Stable Minimum
+// Storage Merging by Symmetric Comparisons", in Susanne Albers and Tomasz
+// Radzik, editors, Algorithms - ESA 2004, volume 3221 of Lecture Notes in
+// Computer Science, pages 714-723. Springer, 2004.
+//
+// Let M = m-a and N = b-n. Wolog M < N.
+// The recursion depth is bound by ceil(log(N+M)).
+// The algorithm needs O(M*log(N/M + 1)) calls to data.Less.
+// The algorithm needs O((M+N)*log(M)) calls to data.Swap.
+//
+// The paper gives O((M+N)*log(M)) as the number of assignments assuming a
+// rotation algorithm wich uses O(M+N+gcd(M+N)) assignments. The argumentation
+// in the paper carries through for Swap operations, especially as the block
+// swapping rotate uses only O(M+N) Swaps.
+func symMerge(data Interface, a, m, b int) {
+ if a >= m || m >= b {
+ return
+ }
+
+ mid := a + (b-a)/2
+ n := mid + m
+ start := 0
+ if m > mid {
+ start = n - b
+ r, p := mid, n-1
+ for start < r {
+ c := start + (r-start)/2
+ if !data.Less(p-c, c) {
+ start = c + 1
+ } else {
+ r = c
+ }
+ }
+ } else {
+ start = a
+ r, p := m, n-1
+ for start < r {
+ c := start + (r-start)/2
+ if !data.Less(p-c, c) {
+ start = c + 1
+ } else {
+ r = c
+ }
+ }
+ }
+ end := n - start
+ rotate(data, start, m, end)
+ symMerge(data, a, start, mid)
+ symMerge(data, mid, end, b)
+}
+
+// Rotate two consecutives blocks u = data[a:m] and v = data[m:b] in data:
+// Data of the form 'x u v y' is changed to 'x v u y'.
+// Rotate performs at most b-a many calls to data.Swap.
+func rotate(data Interface, a, m, b int) {
+ i := m - a
+ if i == 0 {
+ return
+ }
+ j := b - m
+ if j == 0 {
+ return
+ }
+
+ if i == j {
+ swapRange(data, a, m, i)
+ return
+ }
+
+ p := a + i
+ for i != j {
+ if i > j {
+ swapRange(data, p-i, p, j)
+ i -= j
+ } else {
+ swapRange(data, p-i, p+j-i, i)
+ j -= i
+ }
+ }
+ swapRange(data, p-i, p, i)
+}
+
+/*
+Complexity of Stable Sorting
+
+
+Complexity of block swapping rotation
+
+Each Swap puts one new element into its correct, final position.
+Elements which reach their final position are no longer moved.
+Thus block swapping rotation needs |u|+|v| calls to Swaps.
+This is best possible as each element might need a move.
+
+Pay attention when comparing to other optimal algorithms which
+typically count the number of assignments instead of swaps:
+E.g. the optimal algorithm of Dudzinski and Dydek for in-place
+rotations uses O(u + v + gcd(u,v)) assignments which is
+better than our O(3 * (u+v)) as gcd(u,v) <= u.
+
+
+Stable sorting by SymMerge and BlockSwap rotations
+
+SymMerg complexity for same size input M = N:
+Calls to Less: O(M*log(N/M+1)) = O(N*log(2)) = O(N)
+Calls to Swap: O((M+N)*log(M)) = O(2*N*log(N)) = O(N*log(N))
+
+(The following argument does not fuzz over a missing -1 or
+other stuff which does not impact the final result).
+
+Let n = data.Len(). Assume n = 2^k.
+
+Plain merge sort performs log(n) = k iterations.
+On iteration i the algorithm merges 2^(k-i) blocks, each of size 2^i.
+
+Thus iteration i of merge sort performs:
+Calls to Less O(2^(k-i) * 2^i) = O(2^k) = O(2^log(n)) = O(n)
+Calls to Swap O(2^(k-i) * 2^i * log(2^i)) = O(2^k * i) = O(n*i)
+
+In total k = log(n) iterations are performed; so in total:
+Calls to Less O(log(n) * n)
+Calls to Swap O(n + 2*n + 3*n + ... + (k-1)*n + k*n)
+ = O((k/2) * k * n) = O(n * k^2) = O(n * log^2(n))
+
+
+Above results should generalize to arbitrary n = 2^k + p
+and should not be influenced by the initial insertion sort phase:
+Insertion sort is O(n^2) on Swap and Less, thus O(bs^2) per block of
+size bs at n/bs blocks: O(bs*n) Swaps and Less during insertion sort.
+Merge sort iterations start at i = log(bs). With t = log(bs) constant:
+Calls to Less O((log(n)-t) * n + bs*n) = O(log(n)*n + (bs-t)*n)
+ = O(n * log(n))
+Calls to Swap O(n * log^2(n) - (t^2+t)/2*n) = O(n * log^2(n))
+
+*/
diff --git a/src/pkg/sort/sort_test.go b/src/pkg/sort/sort_test.go
index 5daf8482b..6c36f30e0 100644
--- a/src/pkg/sort/sort_test.go
+++ b/src/pkg/sort/sort_test.go
@@ -122,6 +122,19 @@ func BenchmarkSortString1K(b *testing.B) {
}
}
+func BenchmarkStableString1K(b *testing.B) {
+ b.StopTimer()
+ for i := 0; i < b.N; i++ {
+ data := make([]string, 1<<10)
+ for i := 0; i < len(data); i++ {
+ data[i] = strconv.Itoa(i ^ 0x2cc)
+ }
+ b.StartTimer()
+ Stable(StringSlice(data))
+ b.StopTimer()
+ }
+}
+
func BenchmarkSortInt1K(b *testing.B) {
b.StopTimer()
for i := 0; i < b.N; i++ {
@@ -135,6 +148,19 @@ func BenchmarkSortInt1K(b *testing.B) {
}
}
+func BenchmarkStableInt1K(b *testing.B) {
+ b.StopTimer()
+ for i := 0; i < b.N; i++ {
+ data := make([]int, 1<<10)
+ for i := 0; i < len(data); i++ {
+ data[i] = i ^ 0x2cc
+ }
+ b.StartTimer()
+ Stable(IntSlice(data))
+ b.StopTimer()
+ }
+}
+
func BenchmarkSortInt64K(b *testing.B) {
b.StopTimer()
for i := 0; i < b.N; i++ {
@@ -148,6 +174,19 @@ func BenchmarkSortInt64K(b *testing.B) {
}
}
+func BenchmarkStableInt64K(b *testing.B) {
+ b.StopTimer()
+ for i := 0; i < b.N; i++ {
+ data := make([]int, 1<<16)
+ for i := 0; i < len(data); i++ {
+ data[i] = i ^ 0xcccc
+ }
+ b.StartTimer()
+ Stable(IntSlice(data))
+ b.StopTimer()
+ }
+}
+
const (
_Sawtooth = iota
_Rand
@@ -204,7 +243,7 @@ func lg(n int) int {
return i
}
-func testBentleyMcIlroy(t *testing.T, sort func(Interface)) {
+func testBentleyMcIlroy(t *testing.T, sort func(Interface), maxswap func(int) int) {
sizes := []int{100, 1023, 1024, 1025}
if testing.Short() {
sizes = []int{100, 127, 128, 129}
@@ -278,7 +317,7 @@ func testBentleyMcIlroy(t *testing.T, sort func(Interface)) {
}
desc := fmt.Sprintf("n=%d m=%d dist=%s mode=%s", n, m, dists[dist], modes[mode])
- d := &testingData{desc: desc, t: t, data: mdata[0:n], maxswap: n * lg(n) * 12 / 10}
+ d := &testingData{desc: desc, t: t, data: mdata[0:n], maxswap: maxswap(n)}
sort(d)
// Uncomment if you are trying to improve the number of compares/swaps.
//t.Logf("%s: ncmp=%d, nswp=%d", desc, d.ncmp, d.nswap)
@@ -303,11 +342,15 @@ func testBentleyMcIlroy(t *testing.T, sort func(Interface)) {
}
func TestSortBM(t *testing.T) {
- testBentleyMcIlroy(t, Sort)
+ testBentleyMcIlroy(t, Sort, func(n int) int { return n * lg(n) * 12 / 10 })
}
func TestHeapsortBM(t *testing.T) {
- testBentleyMcIlroy(t, Heapsort)
+ testBentleyMcIlroy(t, Heapsort, func(n int) int { return n * lg(n) * 12 / 10 })
+}
+
+func TestStableBM(t *testing.T) {
+ testBentleyMcIlroy(t, Stable, func(n int) int { return n * lg(n) * lg(n) / 3 })
}
// This is based on the "antiquicksort" implementation by M. Douglas McIlroy.
@@ -357,3 +400,154 @@ func TestAdversary(t *testing.T) {
d := &adversaryTestingData{data, make(map[int]int), 0}
Sort(d) // This should degenerate to heapsort.
}
+
+func TestStableInts(t *testing.T) {
+ data := ints
+ Stable(IntSlice(data[0:]))
+ if !IntsAreSorted(data[0:]) {
+ t.Errorf("nsorted %v\n got %v", ints, data)
+ }
+}
+
+type intPairs []struct {
+ a, b int
+}
+
+// IntPairs compare on a only.
+func (d intPairs) Len() int { return len(d) }
+func (d intPairs) Less(i, j int) bool { return d[i].a < d[j].a }
+func (d intPairs) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
+
+// Record initial order in B.
+func (d intPairs) initB() {
+ for i := range d {
+ d[i].b = i
+ }
+}
+
+// InOrder checks if a-equal elements were not reordered.
+func (d intPairs) inOrder() bool {
+ lastA, lastB := -1, 0
+ for i := 0; i < len(d); i++ {
+ if lastA != d[i].a {
+ lastA = d[i].a
+ lastB = d[i].b
+ continue
+ }
+ if d[i].b <= lastB {
+ return false
+ }
+ lastB = d[i].b
+ }
+ return true
+}
+
+func TestStability(t *testing.T) {
+ n, m := 100000, 1000
+ if testing.Short() {
+ n, m = 1000, 100
+ }
+ data := make(intPairs, n)
+
+ // random distribution
+ for i := 0; i < len(data); i++ {
+ data[i].a = rand.Intn(m)
+ }
+ if IsSorted(data) {
+ t.Fatalf("terrible rand.rand")
+ }
+ data.initB()
+ Stable(data)
+ if !IsSorted(data) {
+ t.Errorf("Stable didn't sort %d ints", n)
+ }
+ if !data.inOrder() {
+ t.Errorf("Stable wasn't stable on %d ints", n)
+ }
+
+ // already sorted
+ data.initB()
+ Stable(data)
+ if !IsSorted(data) {
+ t.Errorf("Stable shuffeled sorted %d ints (order)", n)
+ }
+ if !data.inOrder() {
+ t.Errorf("Stable shuffeled sorted %d ints (stability)", n)
+ }
+
+ // sorted reversed
+ for i := 0; i < len(data); i++ {
+ data[i].a = len(data) - i
+ }
+ data.initB()
+ Stable(data)
+ if !IsSorted(data) {
+ t.Errorf("Stable didn't sort %d ints", n)
+ }
+ if !data.inOrder() {
+ t.Errorf("Stable wasn't stable on %d ints", n)
+ }
+}
+
+var countOpsSizes = []int{1e2, 3e2, 1e3, 3e3, 1e4, 3e4, 1e5, 3e5, 1e6}
+
+func countOps(t *testing.T, algo func(Interface), name string) {
+ sizes := countOpsSizes
+ if testing.Short() {
+ sizes = sizes[:5]
+ }
+ if !testing.Verbose() {
+ t.Skip("Counting skipped as non-verbose mode.")
+ }
+ for _, n := range sizes {
+ td := testingData{
+ desc: name,
+ t: t,
+ data: make([]int, n),
+ maxswap: 1<<31 - 1,
+ }
+ for i := 0; i < n; i++ {
+ td.data[i] = rand.Intn(n / 5)
+ }
+ algo(&td)
+ t.Logf("%s %8d elements: %11d Swap, %10d Less", name, n, td.nswap, td.ncmp)
+ }
+}
+
+func TestCountStableOps(t *testing.T) { countOps(t, Stable, "Stable") }
+func TestCountSortOps(t *testing.T) { countOps(t, Sort, "Sort ") }
+
+func bench(b *testing.B, size int, algo func(Interface), name string) {
+ b.StopTimer()
+ data := make(intPairs, size)
+ x := ^uint32(0)
+ for i := 0; i < b.N; i++ {
+ for n := size - 3; n <= size+3; n++ {
+ for i := 0; i < len(data); i++ {
+ x += x
+ x ^= 1
+ if int32(x) < 0 {
+ x ^= 0x88888eef
+ }
+ data[i].a = int(x % uint32(n/5))
+ }
+ data.initB()
+ b.StartTimer()
+ algo(data)
+ b.StopTimer()
+ if !IsSorted(data) {
+ b.Errorf("%s did not sort %d ints", name, n)
+ }
+ if name == "Stable" && !data.inOrder() {
+ b.Errorf("%s unstable on %d ints", name, n)
+ }
+ }
+ }
+}
+
+func BenchmarkSort1e2(b *testing.B) { bench(b, 1e2, Sort, "Sort") }
+func BenchmarkStable1e2(b *testing.B) { bench(b, 1e2, Stable, "Stable") }
+func BenchmarkSort1e4(b *testing.B) { bench(b, 1e4, Sort, "Sort") }
+func BenchmarkStable1e4(b *testing.B) { bench(b, 1e4, Stable, "Stable") }
+func BenchmarkSort1e6(b *testing.B) { bench(b, 1e6, Sort, "Sort") }
+func BenchmarkStable1e6(b *testing.B) { bench(b, 1e6, Stable, "Stable") }
diff --git a/src/pkg/strconv/atof.go b/src/pkg/strconv/atof.go
index b4fe97d12..1b3f8fb33 100644
--- a/src/pkg/strconv/atof.go
+++ b/src/pkg/strconv/atof.go
@@ -536,11 +536,11 @@ func atof64(s string) (f float64, err error) {
// The errors that ParseFloat returns have concrete type *NumError
// and include err.Num = s.
//
-// If s is not syntactically well-formed, ParseFloat returns err.Error = ErrSyntax.
+// If s is not syntactically well-formed, ParseFloat returns err.Err = ErrSyntax.
//
// If s is syntactically well-formed but is more than 1/2 ULP
// away from the largest floating point number of the given size,
-// ParseFloat returns f = ±Inf, err.Error = ErrRange.
+// ParseFloat returns f = ±Inf, err.Err = ErrRange.
func ParseFloat(s string, bitSize int) (f float64, err error) {
if bitSize == 32 {
f1, err1 := atof32(s)
diff --git a/src/pkg/strconv/atoi.go b/src/pkg/strconv/atoi.go
index 21c690096..2d0db7155 100644
--- a/src/pkg/strconv/atoi.go
+++ b/src/pkg/strconv/atoi.go
@@ -33,7 +33,8 @@ func rangeError(fn, str string) *NumError {
const intSize = 32 << uint(^uint(0)>>63)
-const IntSize = intSize // number of bits in int, uint (32 or 64)
+// IntSize is the size in bits of an int or uint value.
+const IntSize = intSize
// Return the first number n such that n*base >= 1<<64.
func cutoff64(base int) uint64 {
@@ -141,9 +142,9 @@ Error:
//
// The errors that ParseInt returns have concrete type *NumError
// and include err.Num = s. If s is empty or contains invalid
-// digits, err.Error = ErrSyntax; if the value corresponding
+// digits, err.Err = ErrSyntax; if the value corresponding
// to s cannot be represented by a signed integer of the
-// given size, err.Error = ErrRange.
+// given size, err.Err = ErrRange.
func ParseInt(s string, base int, bitSize int) (i int64, err error) {
const fnParseInt = "ParseInt"
diff --git a/src/pkg/strconv/quote.go b/src/pkg/strconv/quote.go
index 8cbef88b5..7d6cdcf0b 100644
--- a/src/pkg/strconv/quote.go
+++ b/src/pkg/strconv/quote.go
@@ -133,7 +133,7 @@ func QuoteRuneToASCII(r rune) string {
return quoteWith(string(r), '\'', true)
}
-// AppendQuoteRune appends a single-quoted Go character literal representing the rune,
+// AppendQuoteRuneToASCII appends a single-quoted Go character literal representing the rune,
// as generated by QuoteRuneToASCII, to dst and returns the extended buffer.
func AppendQuoteRuneToASCII(dst []byte, r rune) []byte {
return append(dst, QuoteRuneToASCII(r)...)
diff --git a/src/pkg/strconv/strconv_test.go b/src/pkg/strconv/strconv_test.go
index 40ab4ce6a..9a007dde4 100644
--- a/src/pkg/strconv/strconv_test.go
+++ b/src/pkg/strconv/strconv_test.go
@@ -42,6 +42,9 @@ var (
)
func TestCountMallocs(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping malloc count in short mode")
+ }
if runtime.GOMAXPROCS(0) > 1 {
t.Skip("skipping; GOMAXPROCS>1")
}
diff --git a/src/pkg/strings/replace.go b/src/pkg/strings/replace.go
index f63b1792c..54c9323e0 100644
--- a/src/pkg/strings/replace.go
+++ b/src/pkg/strings/replace.go
@@ -364,17 +364,18 @@ func makeSingleStringReplacer(pattern string, value string) *singleStringReplace
func (r *singleStringReplacer) Replace(s string) string {
var buf []byte
- i := 0
+ i, matched := 0, false
for {
match := r.finder.next(s[i:])
if match == -1 {
break
}
+ matched = true
buf = append(buf, s[i:i+match]...)
buf = append(buf, r.value...)
i += match + len(r.finder.pattern)
}
- if buf == nil {
+ if !matched {
return s
}
buf = append(buf, s[i:]...)
diff --git a/src/pkg/strings/replace_test.go b/src/pkg/strings/replace_test.go
index d33dea95b..82e4b6ef0 100644
--- a/src/pkg/strings/replace_test.go
+++ b/src/pkg/strings/replace_test.go
@@ -261,10 +261,21 @@ func TestReplacer(t *testing.T) {
testCases = append(testCases,
testCase{abcMatcher, "", ""},
testCase{abcMatcher, "ab", "ab"},
+ testCase{abcMatcher, "abc", "[match]"},
testCase{abcMatcher, "abcd", "[match]d"},
testCase{abcMatcher, "cabcabcdabca", "c[match][match]d[match]a"},
)
+ // Issue 6659 cases (more single string replacer)
+
+ noHello := NewReplacer("Hello", "")
+ testCases = append(testCases,
+ testCase{noHello, "Hello", ""},
+ testCase{noHello, "Hellox", "x"},
+ testCase{noHello, "xHello", "x"},
+ testCase{noHello, "xHellox", "xx"},
+ )
+
// No-arg test cases.
nop := NewReplacer()
diff --git a/src/pkg/strings/strings.go b/src/pkg/strings/strings.go
index 986f6d61e..5d46211d8 100644
--- a/src/pkg/strings/strings.go
+++ b/src/pkg/strings/strings.go
@@ -130,14 +130,7 @@ func Index(s, sep string) int {
case n == 0:
return 0
case n == 1:
- c := sep[0]
- // special case worth making fast
- for i := 0; i < len(s); i++ {
- if s[i] == c {
- return i
- }
- }
- return -1
+ return IndexByte(s, sep[0])
case n == len(s):
if sep == s {
return 0
@@ -432,10 +425,7 @@ func Repeat(s string, count int) string {
b := make([]byte, len(s)*count)
bp := 0
for i := 0; i < count; i++ {
- for j := 0; j < len(s); j++ {
- b[bp] = s[j]
- bp++
- }
+ bp += copy(b[bp:], s)
}
return string(b)
}
diff --git a/src/pkg/strings/strings.s b/src/pkg/strings/strings.s
new file mode 100644
index 000000000..55103bae0
--- /dev/null
+++ b/src/pkg/strings/strings.s
@@ -0,0 +1,5 @@
+// Copyright 2013 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 here just to make the go tool happy.
diff --git a/src/pkg/strings/strings_decl.go b/src/pkg/strings/strings_decl.go
new file mode 100644
index 000000000..810a696af
--- /dev/null
+++ b/src/pkg/strings/strings_decl.go
@@ -0,0 +1,8 @@
+// Copyright 2013 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 strings
+
+// IndexByte returns the index of the first instance of c in s, or -1 if c is not present in s.
+func IndexByte(s string, c byte) int // ../runtime/asm_$GOARCH.s
diff --git a/src/pkg/strings/strings_test.go b/src/pkg/strings/strings_test.go
index 68b658ca4..df0dd7165 100644
--- a/src/pkg/strings/strings_test.go
+++ b/src/pkg/strings/strings_test.go
@@ -168,6 +168,15 @@ func BenchmarkIndex(b *testing.B) {
}
}
+func BenchmarkIndexByte(b *testing.B) {
+ if got := IndexByte(benchmarkString, 'v'); got != 17 {
+ b.Fatalf("wrong index: expected 17, got=%d", got)
+ }
+ for i := 0; i < b.N; i++ {
+ IndexByte(benchmarkString, 'v')
+ }
+}
+
var explodetests = []struct {
s string
n int
@@ -1001,6 +1010,30 @@ func TestEqualFold(t *testing.T) {
}
}
+var CountTests = []struct {
+ s, sep string
+ num int
+}{
+ {"", "", 1},
+ {"", "notempty", 0},
+ {"notempty", "", 9},
+ {"smaller", "not smaller", 0},
+ {"12345678987654321", "6", 2},
+ {"611161116", "6", 3},
+ {"notequal", "NotEqual", 0},
+ {"equal", "equal", 1},
+ {"abc1231231123q", "123", 3},
+ {"11111", "11", 2},
+}
+
+func TestCount(t *testing.T) {
+ for _, tt := range CountTests {
+ if num := Count(tt.s, tt.sep); num != tt.num {
+ t.Errorf("Count(\"%s\", \"%s\") = %d, want %d", tt.s, tt.sep, num, tt.num)
+ }
+ }
+}
+
func makeBenchInputHard() string {
tokens := [...]string{
"<a>", "<p>", "<b>", "<strong>",
diff --git a/src/pkg/sync/atomic/64bit_arm.go b/src/pkg/sync/atomic/64bit_arm.go
index f070e78bd..c08f214c7 100644
--- a/src/pkg/sync/atomic/64bit_arm.go
+++ b/src/pkg/sync/atomic/64bit_arm.go
@@ -34,3 +34,13 @@ func addUint64(val *uint64, delta uint64) (new uint64) {
}
return
}
+
+func swapUint64(addr *uint64, new uint64) (old uint64) {
+ for {
+ old = *addr
+ if CompareAndSwapUint64(addr, old, new) {
+ break
+ }
+ }
+ return
+}
diff --git a/src/pkg/sync/atomic/asm_386.s b/src/pkg/sync/atomic/asm_386.s
index 8c02f106f..eaa72eabb 100644
--- a/src/pkg/sync/atomic/asm_386.s
+++ b/src/pkg/sync/atomic/asm_386.s
@@ -4,10 +4,59 @@
// +build !race
-TEXT ·CompareAndSwapInt32(SB),7,$0
+#include "../../../cmd/ld/textflag.h"
+
+TEXT ·SwapInt32(SB),NOSPLIT,$0-12
+ JMP ·SwapUint32(SB)
+
+TEXT ·SwapUint32(SB),NOSPLIT,$0-12
+ MOVL addr+0(FP), BP
+ MOVL new+4(FP), AX
+ XCHGL AX, 0(BP)
+ MOVL AX, new+8(FP)
+ RET
+
+TEXT ·SwapInt64(SB),NOSPLIT,$0-20
+ JMP ·SwapUint64(SB)
+
+TEXT ·SwapUint64(SB),NOSPLIT,$0-20
+ // no XCHGQ so use CMPXCHG8B loop
+ MOVL addr+0(FP), BP
+ TESTL $7, BP
+ JZ 2(PC)
+ MOVL 0, AX // crash with nil ptr deref
+ // CX:BX = new
+ MOVL new_lo+4(FP), BX
+ MOVL new_hi+8(FP), CX
+ // DX:AX = *addr
+ MOVL 0(BP), AX
+ MOVL 4(BP), DX
+swaploop:
+ // if *addr == DX:AX
+ // *addr = CX:BX
+ // else
+ // DX:AX = *addr
+ // all in one instruction
+ LOCK
+ CMPXCHG8B 0(BP)
+ JNZ swaploop
+
+ // success
+ // return DX:AX
+ MOVL AX, new_lo+12(FP)
+ MOVL DX, new_hi+16(FP)
+ RET
+
+TEXT ·SwapUintptr(SB),NOSPLIT,$0-12
+ JMP ·SwapUint32(SB)
+
+TEXT ·SwapPointer(SB),NOSPLIT,$0-12
+ JMP ·SwapUint32(SB)
+
+TEXT ·CompareAndSwapInt32(SB),NOSPLIT,$0-13
JMP ·CompareAndSwapUint32(SB)
-TEXT ·CompareAndSwapUint32(SB),7,$0
+TEXT ·CompareAndSwapUint32(SB),NOSPLIT,$0-13
MOVL addr+0(FP), BP
MOVL old+4(FP), AX
MOVL new+8(FP), CX
@@ -17,16 +66,16 @@ TEXT ·CompareAndSwapUint32(SB),7,$0
SETEQ swapped+12(FP)
RET
-TEXT ·CompareAndSwapUintptr(SB),7,$0
+TEXT ·CompareAndSwapUintptr(SB),NOSPLIT,$0-13
JMP ·CompareAndSwapUint32(SB)
-TEXT ·CompareAndSwapPointer(SB),7,$0
+TEXT ·CompareAndSwapPointer(SB),NOSPLIT,$0-13
JMP ·CompareAndSwapUint32(SB)
-TEXT ·CompareAndSwapInt64(SB),7,$0
+TEXT ·CompareAndSwapInt64(SB),NOSPLIT,$0-21
JMP ·CompareAndSwapUint64(SB)
-TEXT ·CompareAndSwapUint64(SB),7,$0
+TEXT ·CompareAndSwapUint64(SB),NOSPLIT,$0-21
MOVL addr+0(FP), BP
TESTL $7, BP
JZ 2(PC)
@@ -41,10 +90,10 @@ TEXT ·CompareAndSwapUint64(SB),7,$0
SETEQ swapped+20(FP)
RET
-TEXT ·AddInt32(SB),7,$0
+TEXT ·AddInt32(SB),NOSPLIT,$0-12
JMP ·AddUint32(SB)
-TEXT ·AddUint32(SB),7,$0
+TEXT ·AddUint32(SB),NOSPLIT,$0-12
MOVL addr+0(FP), BP
MOVL delta+4(FP), AX
MOVL AX, CX
@@ -55,13 +104,13 @@ TEXT ·AddUint32(SB),7,$0
MOVL CX, new+8(FP)
RET
-TEXT ·AddUintptr(SB),7,$0
+TEXT ·AddUintptr(SB),NOSPLIT,$0-12
JMP ·AddUint32(SB)
-TEXT ·AddInt64(SB),7,$0
+TEXT ·AddInt64(SB),NOSPLIT,$0-20
JMP ·AddUint64(SB)
-TEXT ·AddUint64(SB),7,$0
+TEXT ·AddUint64(SB),NOSPLIT,$0-20
// no XADDQ so use CMPXCHG8B loop
MOVL addr+0(FP), BP
TESTL $7, BP
@@ -97,19 +146,19 @@ addloop:
MOVL CX, new_hi+16(FP)
RET
-TEXT ·LoadInt32(SB),7,$0
+TEXT ·LoadInt32(SB),NOSPLIT,$0-8
JMP ·LoadUint32(SB)
-TEXT ·LoadUint32(SB),7,$0
+TEXT ·LoadUint32(SB),NOSPLIT,$0-8
MOVL addr+0(FP), AX
MOVL 0(AX), AX
MOVL AX, val+4(FP)
RET
-TEXT ·LoadInt64(SB),7,$0
+TEXT ·LoadInt64(SB),NOSPLIT,$0-16
JMP ·LoadUint64(SB)
-TEXT ·LoadUint64(SB),7,$0
+TEXT ·LoadUint64(SB),NOSPLIT,$0-16
MOVL addr+0(FP), AX
TESTL $7, AX
JZ 2(PC)
@@ -122,25 +171,25 @@ TEXT ·LoadUint64(SB),7,$0
EMMS
RET
-TEXT ·LoadUintptr(SB),7,$0
+TEXT ·LoadUintptr(SB),NOSPLIT,$0-8
JMP ·LoadUint32(SB)
-TEXT ·LoadPointer(SB),7,$0
+TEXT ·LoadPointer(SB),NOSPLIT,$0-8
JMP ·LoadUint32(SB)
-TEXT ·StoreInt32(SB),7,$0
+TEXT ·StoreInt32(SB),NOSPLIT,$0-8
JMP ·StoreUint32(SB)
-TEXT ·StoreUint32(SB),7,$0
+TEXT ·StoreUint32(SB),NOSPLIT,$0-8
MOVL addr+0(FP), BP
MOVL val+4(FP), AX
XCHGL AX, 0(BP)
RET
-TEXT ·StoreInt64(SB),7,$0
+TEXT ·StoreInt64(SB),NOSPLIT,$0-16
JMP ·StoreUint64(SB)
-TEXT ·StoreUint64(SB),7,$0
+TEXT ·StoreUint64(SB),NOSPLIT,$0-16
MOVL addr+0(FP), AX
TESTL $7, AX
JZ 2(PC)
@@ -158,8 +207,8 @@ TEXT ·StoreUint64(SB),7,$0
XADDL AX, (SP)
RET
-TEXT ·StoreUintptr(SB),7,$0
+TEXT ·StoreUintptr(SB),NOSPLIT,$0-8
JMP ·StoreUint32(SB)
-TEXT ·StorePointer(SB),7,$0
+TEXT ·StorePointer(SB),NOSPLIT,$0-8
JMP ·StoreUint32(SB)
diff --git a/src/pkg/sync/atomic/asm_amd64.s b/src/pkg/sync/atomic/asm_amd64.s
index 58bda9e4f..0900492dc 100644
--- a/src/pkg/sync/atomic/asm_amd64.s
+++ b/src/pkg/sync/atomic/asm_amd64.s
@@ -4,10 +4,38 @@
// +build !race
-TEXT ·CompareAndSwapInt32(SB),7,$0
+#include "../../../cmd/ld/textflag.h"
+
+TEXT ·SwapInt32(SB),NOSPLIT,$0-20
+ JMP ·SwapUint32(SB)
+
+TEXT ·SwapUint32(SB),NOSPLIT,$0-20
+ MOVQ addr+0(FP), BP
+ MOVL new+8(FP), AX
+ XCHGL AX, 0(BP)
+ MOVL AX, new+16(FP)
+ RET
+
+TEXT ·SwapInt64(SB),NOSPLIT,$0-24
+ JMP ·SwapUint64(SB)
+
+TEXT ·SwapUint64(SB),NOSPLIT,$0-24
+ MOVQ addr+0(FP), BP
+ MOVQ new+8(FP), AX
+ XCHGQ AX, 0(BP)
+ MOVQ AX, new+16(FP)
+ RET
+
+TEXT ·SwapUintptr(SB),NOSPLIT,$0-24
+ JMP ·SwapUint64(SB)
+
+TEXT ·SwapPointer(SB),NOSPLIT,$0-24
+ JMP ·SwapUint64(SB)
+
+TEXT ·CompareAndSwapInt32(SB),NOSPLIT,$0-17
JMP ·CompareAndSwapUint32(SB)
-TEXT ·CompareAndSwapUint32(SB),7,$0
+TEXT ·CompareAndSwapUint32(SB),NOSPLIT,$0-17
MOVQ addr+0(FP), BP
MOVL old+8(FP), AX
MOVL new+12(FP), CX
@@ -16,16 +44,16 @@ TEXT ·CompareAndSwapUint32(SB),7,$0
SETEQ swapped+16(FP)
RET
-TEXT ·CompareAndSwapUintptr(SB),7,$0
+TEXT ·CompareAndSwapUintptr(SB),NOSPLIT,$0-25
JMP ·CompareAndSwapUint64(SB)
-TEXT ·CompareAndSwapPointer(SB),7,$0
+TEXT ·CompareAndSwapPointer(SB),NOSPLIT,$0-25
JMP ·CompareAndSwapUint64(SB)
-TEXT ·CompareAndSwapInt64(SB),7,$0
+TEXT ·CompareAndSwapInt64(SB),NOSPLIT,$0-25
JMP ·CompareAndSwapUint64(SB)
-TEXT ·CompareAndSwapUint64(SB),7,$0
+TEXT ·CompareAndSwapUint64(SB),NOSPLIT,$0-25
MOVQ addr+0(FP), BP
MOVQ old+8(FP), AX
MOVQ new+16(FP), CX
@@ -34,10 +62,10 @@ TEXT ·CompareAndSwapUint64(SB),7,$0
SETEQ swapped+24(FP)
RET
-TEXT ·AddInt32(SB),7,$0
+TEXT ·AddInt32(SB),NOSPLIT,$0-20
JMP ·AddUint32(SB)
-TEXT ·AddUint32(SB),7,$0
+TEXT ·AddUint32(SB),NOSPLIT,$0-20
MOVQ addr+0(FP), BP
MOVL delta+8(FP), AX
MOVL AX, CX
@@ -47,13 +75,13 @@ TEXT ·AddUint32(SB),7,$0
MOVL CX, new+16(FP)
RET
-TEXT ·AddUintptr(SB),7,$0
+TEXT ·AddUintptr(SB),NOSPLIT,$0-24
JMP ·AddUint64(SB)
-TEXT ·AddInt64(SB),7,$0
+TEXT ·AddInt64(SB),NOSPLIT,$0-24
JMP ·AddUint64(SB)
-TEXT ·AddUint64(SB),7,$0
+TEXT ·AddUint64(SB),NOSPLIT,$0-24
MOVQ addr+0(FP), BP
MOVQ delta+8(FP), AX
MOVQ AX, CX
@@ -63,55 +91,55 @@ TEXT ·AddUint64(SB),7,$0
MOVQ CX, new+16(FP)
RET
-TEXT ·LoadInt32(SB),7,$0
+TEXT ·LoadInt32(SB),NOSPLIT,$0-12
JMP ·LoadUint32(SB)
-TEXT ·LoadUint32(SB),7,$0
+TEXT ·LoadUint32(SB),NOSPLIT,$0-12
MOVQ addr+0(FP), AX
MOVL 0(AX), AX
MOVL AX, val+8(FP)
RET
-TEXT ·LoadInt64(SB),7,$0
+TEXT ·LoadInt64(SB),NOSPLIT,$0-16
JMP ·LoadUint64(SB)
-TEXT ·LoadUint64(SB),7,$0
+TEXT ·LoadUint64(SB),NOSPLIT,$0-16
MOVQ addr+0(FP), AX
MOVQ 0(AX), AX
MOVQ AX, val+8(FP)
RET
-TEXT ·LoadUintptr(SB),7,$0
+TEXT ·LoadUintptr(SB),NOSPLIT,$0-16
JMP ·LoadPointer(SB)
-TEXT ·LoadPointer(SB),7,$0
+TEXT ·LoadPointer(SB),NOSPLIT,$0-16
MOVQ addr+0(FP), AX
MOVQ 0(AX), AX
MOVQ AX, val+8(FP)
RET
-TEXT ·StoreInt32(SB),7,$0
+TEXT ·StoreInt32(SB),NOSPLIT,$0-12
JMP ·StoreUint32(SB)
-TEXT ·StoreUint32(SB),7,$0
+TEXT ·StoreUint32(SB),NOSPLIT,$0-12
MOVQ addr+0(FP), BP
MOVL val+8(FP), AX
XCHGL AX, 0(BP)
RET
-TEXT ·StoreInt64(SB),7,$0
+TEXT ·StoreInt64(SB),NOSPLIT,$0-16
JMP ·StoreUint64(SB)
-TEXT ·StoreUint64(SB),7,$0
+TEXT ·StoreUint64(SB),NOSPLIT,$0-16
MOVQ addr+0(FP), BP
MOVQ val+8(FP), AX
XCHGQ AX, 0(BP)
RET
-TEXT ·StoreUintptr(SB),7,$0
+TEXT ·StoreUintptr(SB),NOSPLIT,$0-16
JMP ·StorePointer(SB)
-TEXT ·StorePointer(SB),7,$0
+TEXT ·StorePointer(SB),NOSPLIT,$0-16
MOVQ addr+0(FP), BP
MOVQ val+8(FP), AX
XCHGQ AX, 0(BP)
diff --git a/src/pkg/sync/atomic/asm_arm.s b/src/pkg/sync/atomic/asm_arm.s
index a0525881e..7c8620a51 100644
--- a/src/pkg/sync/atomic/asm_arm.s
+++ b/src/pkg/sync/atomic/asm_arm.s
@@ -4,14 +4,16 @@
// +build !race
+#include "../../../cmd/ld/textflag.h"
+
// ARM atomic operations, for use by asm_$(GOOS)_arm.s.
-TEXT ·armCompareAndSwapUint32(SB),7,$0
+TEXT ·armCompareAndSwapUint32(SB),NOSPLIT,$0-13
MOVW addr+0(FP), R1
MOVW old+4(FP), R2
MOVW new+8(FP), R3
casloop:
- // LDREX and STREX were introduced in ARM 6.
+ // LDREX and STREX were introduced in ARMv6.
LDREX (R1), R0
CMP R0, R2
BNE casfail
@@ -26,7 +28,7 @@ casfail:
MOVBU R0, ret+12(FP)
RET
-TEXT ·armCompareAndSwapUint64(SB),7,$0
+TEXT ·armCompareAndSwapUint64(SB),NOSPLIT,$0-21
BL fastCheck64<>(SB)
MOVW addr+0(FP), R1
// make unaligned atomic access panic
@@ -38,7 +40,7 @@ TEXT ·armCompareAndSwapUint64(SB),7,$0
MOVW newlo+12(FP), R4
MOVW newhi+16(FP), R5
cas64loop:
- // LDREXD and STREXD were introduced in ARM 11.
+ // LDREXD and STREXD were introduced in ARMv6k.
LDREXD (R1), R6 // loads R6 and R7
CMP R2, R6
BNE cas64fail
@@ -55,11 +57,11 @@ cas64fail:
MOVBU R0, ret+20(FP)
RET
-TEXT ·armAddUint32(SB),7,$0
+TEXT ·armAddUint32(SB),NOSPLIT,$0-12
MOVW addr+0(FP), R1
MOVW delta+4(FP), R2
addloop:
- // LDREX and STREX were introduced in ARM 6.
+ // LDREX and STREX were introduced in ARMv6.
LDREX (R1), R3
ADD R2, R3
STREX R3, (R1), R0
@@ -68,7 +70,7 @@ addloop:
MOVW R3, ret+8(FP)
RET
-TEXT ·armAddUint64(SB),7,$0
+TEXT ·armAddUint64(SB),NOSPLIT,$0-20
BL fastCheck64<>(SB)
MOVW addr+0(FP), R1
// make unaligned atomic access panic
@@ -78,7 +80,7 @@ TEXT ·armAddUint64(SB),7,$0
MOVW deltalo+4(FP), R2
MOVW deltahi+8(FP), R3
add64loop:
- // LDREXD and STREXD were introduced in ARM 11.
+ // LDREXD and STREXD were introduced in ARMv6k.
LDREXD (R1), R4 // loads R4 and R5
ADD.S R2, R4
ADC R3, R5
@@ -89,7 +91,38 @@ add64loop:
MOVW R5, rethi+16(FP)
RET
-TEXT ·armLoadUint64(SB),7,$0
+TEXT ·armSwapUint32(SB),NOSPLIT,$0-12
+ MOVW addr+0(FP), R1
+ MOVW new+4(FP), R2
+swaploop:
+ // LDREX and STREX were introduced in ARMv6.
+ LDREX (R1), R3
+ STREX R2, (R1), R0
+ CMP $0, R0
+ BNE swaploop
+ MOVW R3, old+8(FP)
+ RET
+
+TEXT ·armSwapUint64(SB),NOSPLIT,$0-20
+ BL fastCheck64<>(SB)
+ MOVW addr+0(FP), R1
+ // make unaligned atomic access panic
+ AND.S $7, R1, R2
+ BEQ 2(PC)
+ MOVW R2, (R2)
+ MOVW newlo+4(FP), R2
+ MOVW newhi+8(FP), R3
+swap64loop:
+ // LDREXD and STREXD were introduced in ARMv6k.
+ LDREXD (R1), R4 // loads R4 and R5
+ STREXD R2, (R1), R0 // stores R2 and R3
+ CMP $0, R0
+ BNE swap64loop
+ MOVW R4, oldlo+12(FP)
+ MOVW R5, oldhi+16(FP)
+ RET
+
+TEXT ·armLoadUint64(SB),NOSPLIT,$0-12
BL fastCheck64<>(SB)
MOVW addr+0(FP), R1
// make unaligned atomic access panic
@@ -105,7 +138,7 @@ load64loop:
MOVW R3, valhi+8(FP)
RET
-TEXT ·armStoreUint64(SB),7,$0
+TEXT ·armStoreUint64(SB),NOSPLIT,$0-12
BL fastCheck64<>(SB)
MOVW addr+0(FP), R1
// make unaligned atomic access panic
@@ -129,7 +162,7 @@ store64loop:
// 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,$16
+TEXT check64<>(SB),NOSPLIT,$16-0
MOVW $10, R1
// 8-aligned stack address scratch space.
MOVW $8(R13), R5
@@ -148,13 +181,13 @@ ok:
RET
// Fast, cached version of check. No frame, just MOVW CMP RET after first time.
-TEXT fastCheck64<>(SB),7,$-4
+TEXT fastCheck64<>(SB),NOSPLIT,$-4
MOVW ok64<>(SB), R0
CMP $0, R0 // have we been here before?
RET.NE
B slowCheck64<>(SB)
-TEXT slowCheck64<>(SB),7,$0
+TEXT slowCheck64<>(SB),NOSPLIT,$0-0
BL check64<>(SB)
// Still here, must be okay.
MOVW $1, R0
diff --git a/src/pkg/sync/atomic/asm_freebsd_arm.s b/src/pkg/sync/atomic/asm_freebsd_arm.s
index 6590921b0..db37f73bc 100644
--- a/src/pkg/sync/atomic/asm_freebsd_arm.s
+++ b/src/pkg/sync/atomic/asm_freebsd_arm.s
@@ -2,46 +2,66 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../../cmd/ld/textflag.h"
+
// FreeBSD/ARM atomic operations.
// TODO(minux): this only supports ARMv6K or higher.
-TEXT ·CompareAndSwapInt32(SB),7,$0
+TEXT ·CompareAndSwapInt32(SB),NOSPLIT,$0
B ·CompareAndSwapUint32(SB)
-TEXT ·CompareAndSwapUint32(SB),7,$0
+TEXT ·CompareAndSwapUint32(SB),NOSPLIT,$0
B ·armCompareAndSwapUint32(SB)
-TEXT ·CompareAndSwapUintptr(SB),7,$0
+TEXT ·CompareAndSwapUintptr(SB),NOSPLIT,$0
B ·CompareAndSwapUint32(SB)
-TEXT ·CompareAndSwapPointer(SB),7,$0
+TEXT ·CompareAndSwapPointer(SB),NOSPLIT,$0
B ·CompareAndSwapUint32(SB)
-TEXT ·AddInt32(SB),7,$0
+TEXT ·AddInt32(SB),NOSPLIT,$0
B ·AddUint32(SB)
-TEXT ·AddUint32(SB),7,$0
+TEXT ·AddUint32(SB),NOSPLIT,$0
B ·armAddUint32(SB)
-TEXT ·AddUintptr(SB),7,$0
+TEXT ·AddUintptr(SB),NOSPLIT,$0
B ·AddUint32(SB)
-TEXT ·CompareAndSwapInt64(SB),7,$0
+TEXT ·SwapInt32(SB),NOSPLIT,$0
+ B ·SwapUint32(SB)
+
+TEXT ·SwapUint32(SB),NOSPLIT,$0
+ B ·armSwapUint32(SB)
+
+TEXT ·SwapUintptr(SB),NOSPLIT,$0
+ B ·SwapUint32(SB)
+
+TEXT ·SwapPointer(SB),NOSPLIT,$0
+ B ·SwapUint32(SB)
+
+TEXT ·CompareAndSwapInt64(SB),NOSPLIT,$0
B ·CompareAndSwapUint64(SB)
-TEXT ·CompareAndSwapUint64(SB),7,$-4
+TEXT ·CompareAndSwapUint64(SB),NOSPLIT,$-4
B ·armCompareAndSwapUint64(SB)
-TEXT ·AddInt64(SB),7,$0
+TEXT ·AddInt64(SB),NOSPLIT,$0
B ·addUint64(SB)
-TEXT ·AddUint64(SB),7,$0
+TEXT ·AddUint64(SB),NOSPLIT,$0
B ·addUint64(SB)
-TEXT ·LoadInt32(SB),7,$0
+TEXT ·SwapInt64(SB),NOSPLIT,$0
+ B ·swapUint64(SB)
+
+TEXT ·SwapUint64(SB),NOSPLIT,$0
+ B ·swapUint64(SB)
+
+TEXT ·LoadInt32(SB),NOSPLIT,$0
B ·LoadUint32(SB)
-TEXT ·LoadUint32(SB),7,$0
+TEXT ·LoadUint32(SB),NOSPLIT,$0-8
MOVW addr+0(FP), R1
load32loop:
LDREX (R1), R2 // loads R2
@@ -51,22 +71,22 @@ load32loop:
MOVW R2, val+4(FP)
RET
-TEXT ·LoadInt64(SB),7,$0
+TEXT ·LoadInt64(SB),NOSPLIT,$0
B ·loadUint64(SB)
-TEXT ·LoadUint64(SB),7,$0
+TEXT ·LoadUint64(SB),NOSPLIT,$0
B ·loadUint64(SB)
-TEXT ·LoadUintptr(SB),7,$0
+TEXT ·LoadUintptr(SB),NOSPLIT,$0
B ·LoadUint32(SB)
-TEXT ·LoadPointer(SB),7,$0
+TEXT ·LoadPointer(SB),NOSPLIT,$0
B ·LoadUint32(SB)
-TEXT ·StoreInt32(SB),7,$0
+TEXT ·StoreInt32(SB),NOSPLIT,$0
B ·StoreUint32(SB)
-TEXT ·StoreUint32(SB),7,$0
+TEXT ·StoreUint32(SB),NOSPLIT,$0-8
MOVW addr+0(FP), R1
MOVW val+4(FP), R2
storeloop:
@@ -76,14 +96,14 @@ storeloop:
BNE storeloop
RET
-TEXT ·StoreInt64(SB),7,$0
+TEXT ·StoreInt64(SB),NOSPLIT,$0
B ·storeUint64(SB)
-TEXT ·StoreUint64(SB),7,$0
+TEXT ·StoreUint64(SB),NOSPLIT,$0
B ·storeUint64(SB)
-TEXT ·StoreUintptr(SB),7,$0
+TEXT ·StoreUintptr(SB),NOSPLIT,$0
B ·StoreUint32(SB)
-TEXT ·StorePointer(SB),7,$0
+TEXT ·StorePointer(SB),NOSPLIT,$0
B ·StoreUint32(SB)
diff --git a/src/pkg/sync/atomic/asm_linux_arm.s b/src/pkg/sync/atomic/asm_linux_arm.s
index 5b16894b9..b85ca0a13 100644
--- a/src/pkg/sync/atomic/asm_linux_arm.s
+++ b/src/pkg/sync/atomic/asm_linux_arm.s
@@ -4,6 +4,8 @@
// +build !race
+#include "../../../cmd/ld/textflag.h"
+
// Linux/ARM atomic operations.
// Because there is so much variation in ARM devices,
@@ -21,19 +23,22 @@
//
// http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=b49c0f24cf6744a3f4fd09289fe7cade349dead5
//
-TEXT cas<>(SB),7,$0
+TEXT cas<>(SB),NOSPLIT,$0
MOVW $0xffff0fc0, PC
-TEXT ·CompareAndSwapInt32(SB),7,$0
+TEXT ·CompareAndSwapInt32(SB),NOSPLIT,$0
B ·CompareAndSwapUint32(SB)
// Implement using kernel cas for portability.
-TEXT ·CompareAndSwapUint32(SB),7,$0
+TEXT ·CompareAndSwapUint32(SB),NOSPLIT,$0-13
MOVW addr+0(FP), R2
+ // trigger potential paging fault here,
+ // because we don't know how to traceback through __kuser_cmpxchg
+ MOVW (R2), R0
MOVW old+4(FP), R0
casagain:
MOVW new+8(FP), R1
- BL cas<>(SB)
+ BL cas<>(SB)
BCC cascheck
MOVW $1, R0
casret:
@@ -49,17 +54,17 @@ cascheck:
MOVW $0, R0
B casret
-TEXT ·CompareAndSwapUintptr(SB),7,$0
+TEXT ·CompareAndSwapUintptr(SB),NOSPLIT,$0
B ·CompareAndSwapUint32(SB)
-TEXT ·CompareAndSwapPointer(SB),7,$0
+TEXT ·CompareAndSwapPointer(SB),NOSPLIT,$0
B ·CompareAndSwapUint32(SB)
-TEXT ·AddInt32(SB),7,$0
+TEXT ·AddInt32(SB),NOSPLIT,$0
B ·AddUint32(SB)
// Implement using kernel cas for portability.
-TEXT ·AddUint32(SB),7,$0
+TEXT ·AddUint32(SB),NOSPLIT,$0-12
MOVW addr+0(FP), R2
MOVW delta+4(FP), R4
addloop1:
@@ -71,48 +76,77 @@ addloop1:
MOVW R1, ret+8(FP)
RET
-TEXT ·AddUintptr(SB),7,$0
+TEXT ·AddUintptr(SB),NOSPLIT,$0
B ·AddUint32(SB)
-TEXT cas64<>(SB),7,$0
+TEXT ·SwapInt32(SB),NOSPLIT,$0
+ B ·SwapUint32(SB)
+
+// Implement using kernel cas for portability.
+TEXT ·SwapUint32(SB),NOSPLIT,$0-12
+ MOVW addr+0(FP), R2
+ MOVW new+4(FP), R1
+swaploop1:
+ MOVW 0(R2), R0
+ MOVW R0, R4 // cas smashes R0
+ BL cas<>(SB)
+ BCC swaploop1
+ MOVW R4, old+8(FP)
+ RET
+
+TEXT ·SwapUintptr(SB),NOSPLIT,$0
+ B ·SwapUint32(SB)
+
+TEXT ·SwapPointer(SB),NOSPLIT,$0
+ B ·SwapUint32(SB)
+
+TEXT cas64<>(SB),NOSPLIT,$0
MOVW $0xffff0f60, PC // __kuser_cmpxchg64: Linux-3.1 and above
-TEXT kernelCAS64<>(SB),7,$0
+TEXT kernelCAS64<>(SB),NOSPLIT,$0-21
// int (*__kuser_cmpxchg64_t)(const int64_t *oldval, const int64_t *newval, volatile int64_t *ptr);
MOVW addr+0(FP), R2 // ptr
+ // trigger potential paging fault here,
+ // because we don't know how to traceback through __kuser_cmpxchg64
+ MOVW (R2), R0
// make unaligned atomic access panic
AND.S $7, R2, R1
BEQ 2(PC)
MOVW R1, (R1)
MOVW $4(FP), R0 // oldval
MOVW $12(FP), R1 // newval
- BL cas64<>(SB)
+ BL cas64<>(SB)
MOVW.CS $1, R0 // C is set if the kernel has changed *ptr
MOVW.CC $0, R0
MOVW R0, 20(FP)
RET
-TEXT generalCAS64<>(SB),7,$20
- // bool runtime·cas64(uint64 volatile *addr, uint64 *old, uint64 new)
+TEXT ·generalCAS64(SB),NOSPLIT,$20-21
+ // bool runtime·cas64(uint64 volatile *addr, uint64 old, uint64 new)
MOVW addr+0(FP), R0
+ // trigger potential paging fault here,
+ // because a fault in runtime.cas64 will hang.
+ MOVW (R0), R2
// make unaligned atomic access panic
AND.S $7, R0, R1
BEQ 2(PC)
MOVW R1, (R1)
MOVW R0, 4(R13)
- MOVW $4(FP), R1 // oldval
+ MOVW oldlo+4(FP), R1
MOVW R1, 8(R13)
+ MOVW oldhi+8(FP), R1
+ MOVW R1, 12(R13)
MOVW newlo+12(FP), R2
- MOVW R2, 12(R13)
+ MOVW R2, 16(R13)
MOVW newhi+16(FP), R3
- MOVW R3, 16(R13)
+ MOVW R3, 20(R13)
BL runtime·cas64(SB)
- MOVW R0, 20(FP)
+ MOVB R0, ret+20(FP)
RET
GLOBL armCAS64(SB), $4
-TEXT setupAndCallCAS64<>(SB),7,$-4
+TEXT setupAndCallCAS64<>(SB),NOSPLIT,$-4-21
MOVW $0xffff0ffc, R0 // __kuser_helper_version
MOVW (R0), R0
// __kuser_cmpxchg64 only present if helper version >= 5
@@ -122,34 +156,40 @@ TEXT setupAndCallCAS64<>(SB),7,$-4
MOVW.CS R1, PC
MOVB runtime·armArch(SB), R0
// LDREXD, STREXD only present on ARMv6K or higher
- CMP $6, R0 // TODO(minux): how to differentiate ARMv6 with ARMv6K?
+ CMP $6, R0 // TODO(minux): how to differentiate ARMv6 with ARMv6K?
MOVW.CS $·armCompareAndSwapUint64(SB), R1
MOVW.CS R1, armCAS64(SB)
MOVW.CS R1, PC
// we are out of luck, can only use runtime's emulated 64-bit cas
- MOVW $generalCAS64<>(SB), R1
+ MOVW $·generalCAS64(SB), R1
MOVW R1, armCAS64(SB)
MOVW R1, PC
-TEXT ·CompareAndSwapInt64(SB),7,$0
+TEXT ·CompareAndSwapInt64(SB),NOSPLIT,$0
B ·CompareAndSwapUint64(SB)
-TEXT ·CompareAndSwapUint64(SB),7,$-4
+TEXT ·CompareAndSwapUint64(SB),NOSPLIT,$-4-21
MOVW armCAS64(SB), R0
CMP $0, R0
MOVW.NE R0, PC
- B setupAndCallCAS64<>(SB)
+ B setupAndCallCAS64<>(SB)
-TEXT ·AddInt64(SB),7,$0
+TEXT ·AddInt64(SB),NOSPLIT,$0
B ·addUint64(SB)
-TEXT ·AddUint64(SB),7,$0
+TEXT ·AddUint64(SB),NOSPLIT,$0
B ·addUint64(SB)
-TEXT ·LoadInt32(SB),7,$0
+TEXT ·SwapInt64(SB),NOSPLIT,$0
+ B ·swapUint64(SB)
+
+TEXT ·SwapUint64(SB),NOSPLIT,$0
+ B ·swapUint64(SB)
+
+TEXT ·LoadInt32(SB),NOSPLIT,$0
B ·LoadUint32(SB)
-TEXT ·LoadUint32(SB),7,$0
+TEXT ·LoadUint32(SB),NOSPLIT,$0-8
MOVW addr+0(FP), R2
loadloop1:
MOVW 0(R2), R0
@@ -159,22 +199,22 @@ loadloop1:
MOVW R1, val+4(FP)
RET
-TEXT ·LoadInt64(SB),7,$0
+TEXT ·LoadInt64(SB),NOSPLIT,$0
B ·loadUint64(SB)
-TEXT ·LoadUint64(SB),7,$0
+TEXT ·LoadUint64(SB),NOSPLIT,$0
B ·loadUint64(SB)
-TEXT ·LoadUintptr(SB),7,$0
+TEXT ·LoadUintptr(SB),NOSPLIT,$0
B ·LoadUint32(SB)
-TEXT ·LoadPointer(SB),7,$0
+TEXT ·LoadPointer(SB),NOSPLIT,$0
B ·LoadUint32(SB)
-TEXT ·StoreInt32(SB),7,$0
+TEXT ·StoreInt32(SB),NOSPLIT,$0
B ·StoreUint32(SB)
-TEXT ·StoreUint32(SB),7,$0
+TEXT ·StoreUint32(SB),NOSPLIT,$0-8
MOVW addr+0(FP), R2
MOVW val+4(FP), R1
storeloop1:
@@ -183,14 +223,14 @@ storeloop1:
BCC storeloop1
RET
-TEXT ·StoreInt64(SB),7,$0
+TEXT ·StoreInt64(SB),NOSPLIT,$0
B ·storeUint64(SB)
-TEXT ·StoreUint64(SB),7,$0
+TEXT ·StoreUint64(SB),NOSPLIT,$0
B ·storeUint64(SB)
-TEXT ·StoreUintptr(SB),7,$0
+TEXT ·StoreUintptr(SB),NOSPLIT,$0
B ·StoreUint32(SB)
-TEXT ·StorePointer(SB),7,$0
+TEXT ·StorePointer(SB),NOSPLIT,$0
B ·StoreUint32(SB)
diff --git a/src/pkg/sync/atomic/asm_netbsd_arm.s b/src/pkg/sync/atomic/asm_netbsd_arm.s
index 677f3daaa..64f4dbe71 100644
--- a/src/pkg/sync/atomic/asm_netbsd_arm.s
+++ b/src/pkg/sync/atomic/asm_netbsd_arm.s
@@ -2,46 +2,66 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../../cmd/ld/textflag.h"
+
// NetBSD/ARM atomic operations.
// TODO(minux): this only supports ARMv6K or higher.
-TEXT ·CompareAndSwapInt32(SB),7,$0
+TEXT ·CompareAndSwapInt32(SB),NOSPLIT,$0
B ·CompareAndSwapUint32(SB)
-TEXT ·CompareAndSwapUint32(SB),7,$0
+TEXT ·CompareAndSwapUint32(SB),NOSPLIT,$0
B ·armCompareAndSwapUint32(SB)
-TEXT ·CompareAndSwapUintptr(SB),7,$0
+TEXT ·CompareAndSwapUintptr(SB),NOSPLIT,$0
B ·CompareAndSwapUint32(SB)
-TEXT ·CompareAndSwapPointer(SB),7,$0
+TEXT ·CompareAndSwapPointer(SB),NOSPLIT,$0
B ·CompareAndSwapUint32(SB)
-TEXT ·AddInt32(SB),7,$0
+TEXT ·AddInt32(SB),NOSPLIT,$0
B ·AddUint32(SB)
-TEXT ·AddUint32(SB),7,$0
+TEXT ·AddUint32(SB),NOSPLIT,$0
B ·armAddUint32(SB)
-TEXT ·AddUintptr(SB),7,$0
+TEXT ·AddUintptr(SB),NOSPLIT,$0
B ·AddUint32(SB)
-TEXT ·CompareAndSwapInt64(SB),7,$0
+TEXT ·SwapInt32(SB),NOSPLIT,$0
+ B ·SwapUint32(SB)
+
+TEXT ·SwapUint32(SB),NOSPLIT,$0
+ B ·armSwapUint32(SB)
+
+TEXT ·SwapUintptr(SB),NOSPLIT,$0
+ B ·SwapUint32(SB)
+
+TEXT ·SwapPointer(SB),NOSPLIT,$0
+ B ·SwapUint32(SB)
+
+TEXT ·CompareAndSwapInt64(SB),NOSPLIT,$0
B ·CompareAndSwapUint64(SB)
-TEXT ·CompareAndSwapUint64(SB),7,$-4
+TEXT ·CompareAndSwapUint64(SB),NOSPLIT,$-4
B ·armCompareAndSwapUint64(SB)
-TEXT ·AddInt64(SB),7,$0
+TEXT ·AddInt64(SB),NOSPLIT,$0
B ·addUint64(SB)
-TEXT ·AddUint64(SB),7,$0
+TEXT ·AddUint64(SB),NOSPLIT,$0
B ·addUint64(SB)
-TEXT ·LoadInt32(SB),7,$0
+TEXT ·SwapInt64(SB),NOSPLIT,$0
+ B ·swapUint64(SB)
+
+TEXT ·SwapUint64(SB),NOSPLIT,$0
+ B ·swapUint64(SB)
+
+TEXT ·LoadInt32(SB),NOSPLIT,$0
B ·LoadUint32(SB)
-TEXT ·LoadUint32(SB),7,$0
+TEXT ·LoadUint32(SB),NOSPLIT,$0-8
MOVW addr+0(FP), R1
load32loop:
LDREX (R1), R2 // loads R2
@@ -51,22 +71,22 @@ load32loop:
MOVW R2, val+4(FP)
RET
-TEXT ·LoadInt64(SB),7,$0
+TEXT ·LoadInt64(SB),NOSPLIT,$0
B ·loadUint64(SB)
-TEXT ·LoadUint64(SB),7,$0
+TEXT ·LoadUint64(SB),NOSPLIT,$0
B ·loadUint64(SB)
-TEXT ·LoadUintptr(SB),7,$0
+TEXT ·LoadUintptr(SB),NOSPLIT,$0
B ·LoadUint32(SB)
-TEXT ·LoadPointer(SB),7,$0
+TEXT ·LoadPointer(SB),NOSPLIT,$0
B ·LoadUint32(SB)
-TEXT ·StoreInt32(SB),7,$0
+TEXT ·StoreInt32(SB),NOSPLIT,$0
B ·StoreUint32(SB)
-TEXT ·StoreUint32(SB),7,$0
+TEXT ·StoreUint32(SB),NOSPLIT,$0-8
MOVW addr+0(FP), R1
MOVW val+4(FP), R2
storeloop:
@@ -76,14 +96,14 @@ storeloop:
BNE storeloop
RET
-TEXT ·StoreInt64(SB),7,$0
+TEXT ·StoreInt64(SB),NOSPLIT,$0
B ·storeUint64(SB)
-TEXT ·StoreUint64(SB),7,$0
+TEXT ·StoreUint64(SB),NOSPLIT,$0
B ·storeUint64(SB)
-TEXT ·StoreUintptr(SB),7,$0
+TEXT ·StoreUintptr(SB),NOSPLIT,$0
B ·StoreUint32(SB)
-TEXT ·StorePointer(SB),7,$0
+TEXT ·StorePointer(SB),NOSPLIT,$0
B ·StoreUint32(SB)
diff --git a/src/pkg/sync/atomic/atomic_linux_arm_test.go b/src/pkg/sync/atomic/atomic_linux_arm_test.go
new file mode 100644
index 000000000..b6965b99b
--- /dev/null
+++ b/src/pkg/sync/atomic/atomic_linux_arm_test.go
@@ -0,0 +1,14 @@
+// Copyright 2013 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 atomic_test
+
+import (
+ . "sync/atomic"
+ "testing"
+)
+
+func TestGeneralCAS64(t *testing.T) {
+ testCompareAndSwapUint64(t, GeneralCAS64)
+}
diff --git a/src/pkg/sync/atomic/atomic_test.go b/src/pkg/sync/atomic/atomic_test.go
index b392df595..e10effe7e 100644
--- a/src/pkg/sync/atomic/atomic_test.go
+++ b/src/pkg/sync/atomic/atomic_test.go
@@ -5,7 +5,9 @@
package atomic_test
import (
+ "fmt"
"runtime"
+ "strings"
. "sync/atomic"
"testing"
"unsafe"
@@ -38,6 +40,142 @@ var test64err = func() (err interface{}) {
return nil
}()
+func TestSwapInt32(t *testing.T) {
+ var x struct {
+ before int32
+ i int32
+ after int32
+ }
+ x.before = magic32
+ x.after = magic32
+ var j int32
+ for delta := int32(1); delta+delta > delta; delta += delta {
+ k := SwapInt32(&x.i, delta)
+ if x.i != delta || k != j {
+ t.Fatalf("delta=%d i=%d j=%d k=%d", delta, x.i, j, k)
+ }
+ j = delta
+ }
+ if x.before != magic32 || x.after != magic32 {
+ t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magic32, magic32)
+ }
+}
+
+func TestSwapUint32(t *testing.T) {
+ var x struct {
+ before uint32
+ i uint32
+ after uint32
+ }
+ x.before = magic32
+ x.after = magic32
+ var j uint32
+ for delta := uint32(1); delta+delta > delta; delta += delta {
+ k := SwapUint32(&x.i, delta)
+ if x.i != delta || k != j {
+ t.Fatalf("delta=%d i=%d j=%d k=%d", delta, x.i, j, k)
+ }
+ j = delta
+ }
+ if x.before != magic32 || x.after != magic32 {
+ t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magic32, magic32)
+ }
+}
+
+func TestSwapInt64(t *testing.T) {
+ if test64err != nil {
+ t.Skipf("Skipping 64-bit tests: %v", test64err)
+ }
+ var x struct {
+ before int64
+ i int64
+ after int64
+ }
+ x.before = magic64
+ x.after = magic64
+ var j int64
+ for delta := int64(1); delta+delta > delta; delta += delta {
+ k := SwapInt64(&x.i, delta)
+ if x.i != delta || k != j {
+ t.Fatalf("delta=%d i=%d j=%d k=%d", delta, x.i, j, k)
+ }
+ j = delta
+ }
+ if x.before != magic64 || x.after != magic64 {
+ t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, uint64(magic64), uint64(magic64))
+ }
+}
+
+func TestSwapUint64(t *testing.T) {
+ if test64err != nil {
+ t.Skipf("Skipping 64-bit tests: %v", test64err)
+ }
+ var x struct {
+ before uint64
+ i uint64
+ after uint64
+ }
+ x.before = magic64
+ x.after = magic64
+ var j uint64
+ for delta := uint64(1); delta+delta > delta; delta += delta {
+ k := SwapUint64(&x.i, delta)
+ if x.i != delta || k != j {
+ t.Fatalf("delta=%d i=%d j=%d k=%d", delta, x.i, j, k)
+ }
+ j = delta
+ }
+ if x.before != magic64 || x.after != magic64 {
+ t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, uint64(magic64), uint64(magic64))
+ }
+}
+
+func TestSwapUintptr(t *testing.T) {
+ var x struct {
+ before uintptr
+ i uintptr
+ after uintptr
+ }
+ var m uint64 = magic64
+ magicptr := uintptr(m)
+ x.before = magicptr
+ x.after = magicptr
+ var j uintptr
+ for delta := uintptr(1); delta+delta > delta; delta += delta {
+ k := SwapUintptr(&x.i, delta)
+ if x.i != delta || k != j {
+ t.Fatalf("delta=%d i=%d j=%d k=%d", delta, x.i, j, k)
+ }
+ j = delta
+ }
+ if x.before != magicptr || x.after != magicptr {
+ t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magicptr, magicptr)
+ }
+}
+
+func TestSwapPointer(t *testing.T) {
+ var x struct {
+ before uintptr
+ i unsafe.Pointer
+ after uintptr
+ }
+ var m uint64 = magic64
+ magicptr := uintptr(m)
+ x.before = magicptr
+ x.after = magicptr
+ var j uintptr
+ for delta := uintptr(1); delta+delta > delta; delta += delta {
+ k := SwapPointer(&x.i, unsafe.Pointer(delta))
+ if uintptr(x.i) != delta || uintptr(k) != j {
+ t.Fatalf("delta=%d i=%d j=%d k=%d", delta, x.i, j, k)
+ }
+ j = delta
+ }
+ if x.before != magicptr || x.after != magicptr {
+ t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magicptr, magicptr)
+ }
+}
+
func TestAddInt32(t *testing.T) {
var x struct {
before int32
@@ -241,7 +379,7 @@ func TestCompareAndSwapInt64(t *testing.T) {
}
}
-func TestCompareAndSwapUint64(t *testing.T) {
+func testCompareAndSwapUint64(t *testing.T, cas func(*uint64, uint64, uint64) bool) {
if test64err != nil {
t.Skipf("Skipping 64-bit tests: %v", test64err)
}
@@ -254,14 +392,14 @@ func TestCompareAndSwapUint64(t *testing.T) {
x.after = magic64
for val := uint64(1); val+val > val; val += val {
x.i = val
- if !CompareAndSwapUint64(&x.i, val, val+1) {
+ if !cas(&x.i, val, val+1) {
t.Fatalf("should have swapped %#x %#x", val, val+1)
}
if x.i != val+1 {
t.Fatalf("wrong x.i after swap: x.i=%#x val+1=%#x", x.i, val+1)
}
x.i = val + 1
- if CompareAndSwapUint64(&x.i, val, val+2) {
+ if cas(&x.i, val, val+2) {
t.Fatalf("should not have swapped %#x %#x", val, val+2)
}
if x.i != val+1 {
@@ -273,6 +411,10 @@ func TestCompareAndSwapUint64(t *testing.T) {
}
}
+func TestCompareAndSwapUint64(t *testing.T) {
+ testCompareAndSwapUint64(t, CompareAndSwapUint64)
+}
+
func TestCompareAndSwapUintptr(t *testing.T) {
var x struct {
before uintptr
@@ -608,27 +750,85 @@ func TestStorePointer(t *testing.T) {
// uses the atomic operation to add 1 to a value. After running
// multiple hammers in parallel, check that we end with the correct
// total.
-
-var hammer32 = []struct {
- name string
- f func(*uint32, int)
-}{
- {"AddInt32", hammerAddInt32},
- {"AddUint32", hammerAddUint32},
- {"AddUintptr", hammerAddUintptr32},
- {"CompareAndSwapInt32", hammerCompareAndSwapInt32},
- {"CompareAndSwapUint32", hammerCompareAndSwapUint32},
- {"CompareAndSwapUintptr", hammerCompareAndSwapUintptr32},
- {"CompareAndSwapPointer", hammerCompareAndSwapPointer32},
+// Swap can't add 1, so it uses a different scheme.
+// The functions repeatedly generate a pseudo-random number such that
+// low bits are equal to high bits, swap, check that the old value
+// has low and high bits equal.
+
+var hammer32 = map[string]func(*uint32, int){
+ "SwapInt32": hammerSwapInt32,
+ "SwapUint32": hammerSwapUint32,
+ "SwapUintptr": hammerSwapUintptr32,
+ "SwapPointer": hammerSwapPointer32,
+ "AddInt32": hammerAddInt32,
+ "AddUint32": hammerAddUint32,
+ "AddUintptr": hammerAddUintptr32,
+ "CompareAndSwapInt32": hammerCompareAndSwapInt32,
+ "CompareAndSwapUint32": hammerCompareAndSwapUint32,
+ "CompareAndSwapUintptr": hammerCompareAndSwapUintptr32,
+ "CompareAndSwapPointer": hammerCompareAndSwapPointer32,
}
func init() {
var v uint64 = 1 << 50
if uintptr(v) != 0 {
// 64-bit system; clear uintptr tests
- hammer32[2].f = nil
- hammer32[5].f = nil
- hammer32[6].f = nil
+ delete(hammer32, "SwapUintptr")
+ delete(hammer32, "SwapPointer")
+ delete(hammer32, "AddUintptr")
+ delete(hammer32, "CompareAndSwapUintptr")
+ delete(hammer32, "CompareAndSwapPointer")
+ }
+}
+
+func hammerSwapInt32(uaddr *uint32, count int) {
+ addr := (*int32)(unsafe.Pointer(uaddr))
+ seed := int(uintptr(unsafe.Pointer(&count)))
+ for i := 0; i < count; i++ {
+ new := uint32(seed+i)<<16 | uint32(seed+i)<<16>>16
+ old := uint32(SwapInt32(addr, int32(new)))
+ if old>>16 != old<<16>>16 {
+ panic(fmt.Sprintf("SwapInt32 is not atomic: %v", old))
+ }
+ }
+}
+
+func hammerSwapUint32(addr *uint32, count int) {
+ seed := int(uintptr(unsafe.Pointer(&count)))
+ for i := 0; i < count; i++ {
+ new := uint32(seed+i)<<16 | uint32(seed+i)<<16>>16
+ old := SwapUint32(addr, new)
+ if old>>16 != old<<16>>16 {
+ panic(fmt.Sprintf("SwapUint32 is not atomic: %v", old))
+ }
+ }
+}
+
+func hammerSwapUintptr32(uaddr *uint32, count int) {
+ // only safe when uintptr is 32-bit.
+ // not called on 64-bit systems.
+ addr := (*uintptr)(unsafe.Pointer(uaddr))
+ seed := int(uintptr(unsafe.Pointer(&count)))
+ for i := 0; i < count; i++ {
+ new := uintptr(seed+i)<<16 | uintptr(seed+i)<<16>>16
+ old := SwapUintptr(addr, new)
+ if old>>16 != old<<16>>16 {
+ panic(fmt.Sprintf("SwapUintptr is not atomic: %v", old))
+ }
+ }
+}
+
+func hammerSwapPointer32(uaddr *uint32, count int) {
+ // only safe when uintptr is 32-bit.
+ // not called on 64-bit systems.
+ addr := (*unsafe.Pointer)(unsafe.Pointer(uaddr))
+ seed := int(uintptr(unsafe.Pointer(&count)))
+ for i := 0; i < count; i++ {
+ new := uintptr(seed+i)<<16 | uintptr(seed+i)<<16>>16
+ old := uintptr(SwapPointer(addr, unsafe.Pointer(new)))
+ if old>>16 != old<<16>>16 {
+ panic(fmt.Sprintf("SwapPointer is not atomic: %v", old))
+ }
}
}
@@ -713,47 +913,103 @@ func TestHammer32(t *testing.T) {
}
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(p))
- for _, tt := range hammer32 {
- if tt.f == nil {
- continue
- }
+ for name, testf := range hammer32 {
c := make(chan int)
var val uint32
for i := 0; i < p; i++ {
go func() {
- tt.f(&val, n)
- c <- 1
+ defer func() {
+ if err := recover(); err != nil {
+ t.Error(err.(string))
+ }
+ c <- 1
+ }()
+ testf(&val, n)
}()
}
for i := 0; i < p; i++ {
<-c
}
- if val != uint32(n)*p {
- t.Fatalf("%s: val=%d want %d", tt.name, val, n*p)
+ if !strings.HasPrefix(name, "Swap") && val != uint32(n)*p {
+ t.Fatalf("%s: val=%d want %d", name, val, n*p)
}
}
}
-var hammer64 = []struct {
- name string
- f func(*uint64, int)
-}{
- {"AddInt64", hammerAddInt64},
- {"AddUint64", hammerAddUint64},
- {"AddUintptr", hammerAddUintptr64},
- {"CompareAndSwapInt64", hammerCompareAndSwapInt64},
- {"CompareAndSwapUint64", hammerCompareAndSwapUint64},
- {"CompareAndSwapUintptr", hammerCompareAndSwapUintptr64},
- {"CompareAndSwapPointer", hammerCompareAndSwapPointer64},
+var hammer64 = map[string]func(*uint64, int){
+ "SwapInt64": hammerSwapInt64,
+ "SwapUint64": hammerSwapUint64,
+ "SwapUintptr": hammerSwapUintptr64,
+ "SwapPointer": hammerSwapPointer64,
+ "AddInt64": hammerAddInt64,
+ "AddUint64": hammerAddUint64,
+ "AddUintptr": hammerAddUintptr64,
+ "CompareAndSwapInt64": hammerCompareAndSwapInt64,
+ "CompareAndSwapUint64": hammerCompareAndSwapUint64,
+ "CompareAndSwapUintptr": hammerCompareAndSwapUintptr64,
+ "CompareAndSwapPointer": hammerCompareAndSwapPointer64,
}
func init() {
var v uint64 = 1 << 50
if uintptr(v) == 0 {
// 32-bit system; clear uintptr tests
- hammer64[2].f = nil
- hammer64[5].f = nil
- hammer64[6].f = nil
+ delete(hammer64, "SwapUintptr")
+ delete(hammer64, "SwapPointer")
+ delete(hammer64, "AddUintptr")
+ delete(hammer64, "CompareAndSwapUintptr")
+ delete(hammer64, "CompareAndSwapPointer")
+ }
+}
+
+func hammerSwapInt64(uaddr *uint64, count int) {
+ addr := (*int64)(unsafe.Pointer(uaddr))
+ seed := int(uintptr(unsafe.Pointer(&count)))
+ for i := 0; i < count; i++ {
+ new := uint64(seed+i)<<32 | uint64(seed+i)<<32>>32
+ old := uint64(SwapInt64(addr, int64(new)))
+ if old>>32 != old<<32>>32 {
+ panic(fmt.Sprintf("SwapInt64 is not atomic: %v", old))
+ }
+ }
+}
+
+func hammerSwapUint64(addr *uint64, count int) {
+ seed := int(uintptr(unsafe.Pointer(&count)))
+ for i := 0; i < count; i++ {
+ new := uint64(seed+i)<<32 | uint64(seed+i)<<32>>32
+ old := SwapUint64(addr, new)
+ if old>>32 != old<<32>>32 {
+ panic(fmt.Sprintf("SwapUint64 is not atomic: %v", old))
+ }
+ }
+}
+
+func hammerSwapUintptr64(uaddr *uint64, count int) {
+ // only safe when uintptr is 64-bit.
+ // not called on 32-bit systems.
+ addr := (*uintptr)(unsafe.Pointer(uaddr))
+ seed := int(uintptr(unsafe.Pointer(&count)))
+ for i := 0; i < count; i++ {
+ new := uintptr(seed+i)<<32 | uintptr(seed+i)<<32>>32
+ old := SwapUintptr(addr, new)
+ if old>>32 != old<<32>>32 {
+ panic(fmt.Sprintf("SwapUintptr is not atomic: %v", old))
+ }
+ }
+}
+
+func hammerSwapPointer64(uaddr *uint64, count int) {
+ // only safe when uintptr is 64-bit.
+ // not called on 32-bit systems.
+ addr := (*unsafe.Pointer)(unsafe.Pointer(uaddr))
+ seed := int(uintptr(unsafe.Pointer(&count)))
+ for i := 0; i < count; i++ {
+ new := uintptr(seed+i)<<32 | uintptr(seed+i)<<32>>32
+ old := uintptr(SwapPointer(addr, unsafe.Pointer(new)))
+ if old>>32 != old<<32>>32 {
+ panic(fmt.Sprintf("SwapPointer is not atomic: %v", old))
+ }
}
}
@@ -841,23 +1097,25 @@ func TestHammer64(t *testing.T) {
}
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(p))
- for _, tt := range hammer64 {
- if tt.f == nil {
- continue
- }
+ for name, testf := range hammer64 {
c := make(chan int)
var val uint64
for i := 0; i < p; i++ {
go func() {
- tt.f(&val, n)
- c <- 1
+ defer func() {
+ if err := recover(); err != nil {
+ t.Error(err.(string))
+ }
+ c <- 1
+ }()
+ testf(&val, n)
}()
}
for i := 0; i < p; i++ {
<-c
}
- if val != uint64(n)*p {
- t.Fatalf("%s: val=%d want %d", tt.name, val, n*p)
+ if !strings.HasPrefix(name, "Swap") && val != uint64(n)*p {
+ t.Fatalf("%s: val=%d want %d", name, val, n*p)
}
}
}
@@ -1203,3 +1461,46 @@ func TestUnaligned64(t *testing.T) {
shouldPanic(t, "CompareAndSwapUint64", func() { CompareAndSwapUint64(p, 1, 2) })
shouldPanic(t, "AddUint64", func() { AddUint64(p, 3) })
}
+
+func TestNilDeref(t *testing.T) {
+ funcs := [...]func(){
+ func() { CompareAndSwapInt32(nil, 0, 0) },
+ func() { CompareAndSwapInt64(nil, 0, 0) },
+ func() { CompareAndSwapUint32(nil, 0, 0) },
+ func() { CompareAndSwapUint64(nil, 0, 0) },
+ func() { CompareAndSwapUintptr(nil, 0, 0) },
+ func() { CompareAndSwapPointer(nil, nil, nil) },
+ func() { SwapInt32(nil, 0) },
+ func() { SwapUint32(nil, 0) },
+ func() { SwapInt64(nil, 0) },
+ func() { SwapUint64(nil, 0) },
+ func() { SwapUintptr(nil, 0) },
+ func() { SwapPointer(nil, nil) },
+ func() { AddInt32(nil, 0) },
+ func() { AddUint32(nil, 0) },
+ func() { AddInt64(nil, 0) },
+ func() { AddUint64(nil, 0) },
+ func() { AddUintptr(nil, 0) },
+ func() { LoadInt32(nil) },
+ func() { LoadInt64(nil) },
+ func() { LoadUint32(nil) },
+ func() { LoadUint64(nil) },
+ func() { LoadUintptr(nil) },
+ func() { LoadPointer(nil) },
+ func() { StoreInt32(nil, 0) },
+ func() { StoreInt64(nil, 0) },
+ func() { StoreUint32(nil, 0) },
+ func() { StoreUint64(nil, 0) },
+ func() { StoreUintptr(nil, 0) },
+ func() { StorePointer(nil, nil) },
+ }
+ for _, f := range funcs {
+ func() {
+ defer func() {
+ runtime.GC()
+ recover()
+ }()
+ f()
+ }()
+ }
+}
diff --git a/src/pkg/sync/atomic/doc.go b/src/pkg/sync/atomic/doc.go
index 27a12c984..17ba72fa1 100644
--- a/src/pkg/sync/atomic/doc.go
+++ b/src/pkg/sync/atomic/doc.go
@@ -13,6 +13,13 @@
// Share memory by communicating;
// don't communicate by sharing memory.
//
+// The swap operation, implemented by the SwapT functions, is the atomic
+// equivalent of:
+//
+// old = *addr
+// *addr = new
+// return old
+//
// The compare-and-swap operation, implemented by the CompareAndSwapT
// functions, is the atomic equivalent of:
//
@@ -40,11 +47,31 @@ import (
// BUG(rsc): On x86-32, the 64-bit functions use instructions unavailable before the Pentium MMX.
//
+// On non-Linux ARM, the 64-bit functions use instructions unavailable before the ARMv6k core.
+//
// On both ARM and x86-32, it is the caller's responsibility to arrange for 64-bit
// alignment of 64-bit words accessed atomically. The first word in a global
// variable or in an allocated struct or slice can be relied upon to be
// 64-bit aligned.
+// SwapInt32 atomically stores new into *addr and returns the previous *addr value.
+func SwapInt32(addr *int32, new int32) (old int32)
+
+// SwapInt64 atomically stores new into *addr and returns the previous *addr value.
+func SwapInt64(addr *int64, new int64) (old int64)
+
+// SwapUint32 atomically stores new into *addr and returns the previous *addr value.
+func SwapUint32(addr *uint32, new uint32) (old uint32)
+
+// SwapUint64 atomically stores new into *addr and returns the previous *addr value.
+func SwapUint64(addr *uint64, new uint64) (old uint64)
+
+// SwapUintptr atomically stores new into *addr and returns the previous *addr value.
+func SwapUintptr(addr *uintptr, new uintptr) (old uintptr)
+
+// SwapPointer atomically stores new into *addr and returns the previous *addr value.
+func SwapPointer(addr *unsafe.Pointer, new unsafe.Pointer) (old unsafe.Pointer)
+
// CompareAndSwapInt32 executes the compare-and-swap operation for an int32 value.
func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool)
@@ -67,12 +94,16 @@ func CompareAndSwapPointer(addr *unsafe.Pointer, old, new unsafe.Pointer) (swapp
func AddInt32(addr *int32, delta int32) (new int32)
// AddUint32 atomically adds delta to *addr and returns the new value.
+// To subtract a signed positive constant value c from x, do AddUint32(&x, ^uint32(c-1)).
+// In particular, to decrement x, do AddUint32(&x, ^uint32(0)).
func AddUint32(addr *uint32, delta uint32) (new uint32)
// AddInt64 atomically adds delta to *addr and returns the new value.
func AddInt64(addr *int64, delta int64) (new int64)
// AddUint64 atomically adds delta to *addr and returns the new value.
+// To subtract a signed positive constant value c from x, do AddUint64(&x, ^uint64(c-1)).
+// In particular, to decrement x, do AddUint64(&x, ^uint64(0)).
func AddUint64(addr *uint64, delta uint64) (new uint64)
// AddUintptr atomically adds delta to *addr and returns the new value.
diff --git a/src/pkg/sync/atomic/export_linux_arm_test.go b/src/pkg/sync/atomic/export_linux_arm_test.go
new file mode 100644
index 000000000..8c0b5a75c
--- /dev/null
+++ b/src/pkg/sync/atomic/export_linux_arm_test.go
@@ -0,0 +1,9 @@
+// Copyright 2013 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 atomic
+
+func generalCAS64(*uint64, uint64, uint64) bool
+
+var GeneralCAS64 = generalCAS64
diff --git a/src/pkg/sync/atomic/race.go b/src/pkg/sync/atomic/race.go
index 2320b5707..6cbbf12cb 100644
--- a/src/pkg/sync/atomic/race.go
+++ b/src/pkg/sync/atomic/race.go
@@ -20,6 +20,54 @@ import (
var mtx uint32 = 1 // same for all
+func SwapInt32(addr *int32, new int32) (old int32) {
+ return int32(SwapUint32((*uint32)(unsafe.Pointer(addr)), uint32(new)))
+}
+
+func SwapUint32(addr *uint32, new uint32) (old uint32) {
+ _ = *addr
+ runtime.RaceSemacquire(&mtx)
+ runtime.RaceRead(unsafe.Pointer(addr))
+ runtime.RaceAcquire(unsafe.Pointer(addr))
+ old = *addr
+ *addr = new
+ runtime.RaceReleaseMerge(unsafe.Pointer(addr))
+ runtime.RaceSemrelease(&mtx)
+ return
+}
+
+func SwapInt64(addr *int64, new int64) (old int64) {
+ return int64(SwapUint64((*uint64)(unsafe.Pointer(addr)), uint64(new)))
+}
+
+func SwapUint64(addr *uint64, new uint64) (old uint64) {
+ _ = *addr
+ runtime.RaceSemacquire(&mtx)
+ runtime.RaceRead(unsafe.Pointer(addr))
+ runtime.RaceAcquire(unsafe.Pointer(addr))
+ old = *addr
+ *addr = new
+ runtime.RaceReleaseMerge(unsafe.Pointer(addr))
+ runtime.RaceSemrelease(&mtx)
+ return
+}
+
+func SwapUintptr(addr *uintptr, new uintptr) (old uintptr) {
+ return uintptr(SwapPointer((*unsafe.Pointer)(unsafe.Pointer(addr)), unsafe.Pointer(new)))
+}
+
+func SwapPointer(addr *unsafe.Pointer, new unsafe.Pointer) (old unsafe.Pointer) {
+ _ = *addr
+ runtime.RaceSemacquire(&mtx)
+ runtime.RaceRead(unsafe.Pointer(addr))
+ runtime.RaceAcquire(unsafe.Pointer(addr))
+ old = *addr
+ *addr = new
+ runtime.RaceReleaseMerge(unsafe.Pointer(addr))
+ runtime.RaceSemrelease(&mtx)
+ return
+}
+
func CompareAndSwapInt32(val *int32, old, new int32) bool {
return CompareAndSwapUint32((*uint32)(unsafe.Pointer(val)), uint32(old), uint32(new))
}
diff --git a/src/pkg/sync/cond.go b/src/pkg/sync/cond.go
index 13547a8a1..9e6bc170f 100644
--- a/src/pkg/sync/cond.go
+++ b/src/pkg/sync/cond.go
@@ -4,6 +4,11 @@
package sync
+import (
+ "sync/atomic"
+ "unsafe"
+)
+
// Cond implements a condition variable, a rendezvous point
// for goroutines waiting for or announcing the occurrence
// of an event.
@@ -11,27 +16,16 @@ package sync
// Each Cond has an associated Locker L (often a *Mutex or *RWMutex),
// which must be held when changing the condition and
// when calling the Wait method.
+//
+// A Cond can be created as part of other structures.
+// A Cond must not be copied after first use.
type Cond struct {
- L Locker // held while observing or changing the condition
- m Mutex // held to avoid internal races
-
- // We must be careful to make sure that when Signal
- // releases a semaphore, the corresponding acquire is
- // executed by a goroutine that was already waiting at
- // the time of the call to Signal, not one that arrived later.
- // To ensure this, we segment waiting goroutines into
- // generations punctuated by calls to Signal. Each call to
- // Signal begins another generation if there are no goroutines
- // left in older generations for it to wake. Because of this
- // optimization (only begin another generation if there
- // are no older goroutines left), we only need to keep track
- // of the two most recent generations, which we call old
- // and new.
- oldWaiters int // number of waiters in old generation...
- oldSema *uint32 // ... waiting on this semaphore
+ // L is held while observing or changing the condition
+ L Locker
- newWaiters int // number of waiters in new generation...
- newSema *uint32 // ... waiting on this semaphore
+ sema syncSema
+ waiters uint32 // number of waiters
+ checker copyChecker
}
// NewCond returns a new Cond with Locker l.
@@ -56,22 +50,16 @@ func NewCond(l Locker) *Cond {
// c.L.Unlock()
//
func (c *Cond) Wait() {
+ c.checker.check()
if raceenabled {
- _ = c.m.state
raceDisable()
}
- c.m.Lock()
- if c.newSema == nil {
- c.newSema = new(uint32)
- }
- s := c.newSema
- c.newWaiters++
- c.m.Unlock()
+ atomic.AddUint32(&c.waiters, 1)
if raceenabled {
raceEnable()
}
c.L.Unlock()
- runtime_Semacquire(s)
+ runtime_Syncsemacquire(&c.sema)
c.L.Lock()
}
@@ -80,26 +68,7 @@ func (c *Cond) Wait() {
// It is allowed but not required for the caller to hold c.L
// during the call.
func (c *Cond) Signal() {
- if raceenabled {
- _ = c.m.state
- raceDisable()
- }
- c.m.Lock()
- if c.oldWaiters == 0 && c.newWaiters > 0 {
- // Retire old generation; rename new to old.
- c.oldWaiters = c.newWaiters
- c.oldSema = c.newSema
- c.newWaiters = 0
- c.newSema = nil
- }
- if c.oldWaiters > 0 {
- c.oldWaiters--
- runtime_Semrelease(c.oldSema)
- }
- c.m.Unlock()
- if raceenabled {
- raceEnable()
- }
+ c.signalImpl(false)
}
// Broadcast wakes all goroutines waiting on c.
@@ -107,27 +76,43 @@ func (c *Cond) Signal() {
// It is allowed but not required for the caller to hold c.L
// during the call.
func (c *Cond) Broadcast() {
+ c.signalImpl(true)
+}
+
+func (c *Cond) signalImpl(all bool) {
+ c.checker.check()
if raceenabled {
- _ = c.m.state
raceDisable()
}
- c.m.Lock()
- // Wake both generations.
- if c.oldWaiters > 0 {
- for i := 0; i < c.oldWaiters; i++ {
- runtime_Semrelease(c.oldSema)
+ for {
+ old := atomic.LoadUint32(&c.waiters)
+ if old == 0 {
+ if raceenabled {
+ raceEnable()
+ }
+ return
}
- c.oldWaiters = 0
- }
- if c.newWaiters > 0 {
- for i := 0; i < c.newWaiters; i++ {
- runtime_Semrelease(c.newSema)
+ new := old - 1
+ if all {
+ new = 0
+ }
+ if atomic.CompareAndSwapUint32(&c.waiters, old, new) {
+ if raceenabled {
+ raceEnable()
+ }
+ runtime_Syncsemrelease(&c.sema, old-new)
+ return
}
- c.newWaiters = 0
- c.newSema = nil
}
- c.m.Unlock()
- if raceenabled {
- raceEnable()
+}
+
+// copyChecker holds back pointer to itself to detect object copying.
+type copyChecker uintptr
+
+func (c *copyChecker) check() {
+ if uintptr(*c) != uintptr(unsafe.Pointer(c)) &&
+ !atomic.CompareAndSwapUintptr((*uintptr)(c), 0, uintptr(unsafe.Pointer(c))) &&
+ uintptr(*c) != uintptr(unsafe.Pointer(c)) {
+ panic("sync.Cond is copied")
}
}
diff --git a/src/pkg/sync/cond_test.go b/src/pkg/sync/cond_test.go
index cefacb184..467c80621 100644
--- a/src/pkg/sync/cond_test.go
+++ b/src/pkg/sync/cond_test.go
@@ -5,6 +5,8 @@ package sync_test
import (
. "sync"
+
+ "runtime"
"testing"
)
@@ -124,3 +126,130 @@ func TestCondBroadcast(t *testing.T) {
}
c.Broadcast()
}
+
+func TestRace(t *testing.T) {
+ x := 0
+ c := NewCond(&Mutex{})
+ done := make(chan bool)
+ go func() {
+ c.L.Lock()
+ x = 1
+ c.Wait()
+ if x != 2 {
+ t.Fatal("want 2")
+ }
+ x = 3
+ c.Signal()
+ c.L.Unlock()
+ done <- true
+ }()
+ go func() {
+ c.L.Lock()
+ for {
+ if x == 1 {
+ x = 2
+ c.Signal()
+ break
+ }
+ c.L.Unlock()
+ runtime.Gosched()
+ c.L.Lock()
+ }
+ c.L.Unlock()
+ done <- true
+ }()
+ go func() {
+ c.L.Lock()
+ for {
+ if x == 2 {
+ c.Wait()
+ if x != 3 {
+ t.Fatal("want 3")
+ }
+ break
+ }
+ if x == 3 {
+ break
+ }
+ c.L.Unlock()
+ runtime.Gosched()
+ c.L.Lock()
+ }
+ c.L.Unlock()
+ done <- true
+ }()
+ <-done
+ <-done
+ <-done
+}
+
+func TestCondCopy(t *testing.T) {
+ defer func() {
+ err := recover()
+ if err == nil || err.(string) != "sync.Cond is copied" {
+ t.Fatalf("got %v, expect sync.Cond is copied", err)
+ }
+ }()
+ c := Cond{L: &Mutex{}}
+ c.Signal()
+ c2 := c
+ c2.Signal()
+}
+
+func BenchmarkCond1(b *testing.B) {
+ benchmarkCond(b, 1)
+}
+
+func BenchmarkCond2(b *testing.B) {
+ benchmarkCond(b, 2)
+}
+
+func BenchmarkCond4(b *testing.B) {
+ benchmarkCond(b, 4)
+}
+
+func BenchmarkCond8(b *testing.B) {
+ benchmarkCond(b, 8)
+}
+
+func BenchmarkCond16(b *testing.B) {
+ benchmarkCond(b, 16)
+}
+
+func BenchmarkCond32(b *testing.B) {
+ benchmarkCond(b, 32)
+}
+
+func benchmarkCond(b *testing.B, waiters int) {
+ c := NewCond(&Mutex{})
+ done := make(chan bool)
+ id := 0
+
+ for routine := 0; routine < waiters+1; routine++ {
+ go func() {
+ for i := 0; i < b.N; i++ {
+ c.L.Lock()
+ if id == -1 {
+ c.L.Unlock()
+ break
+ }
+ id++
+ if id == waiters+1 {
+ id = 0
+ c.Broadcast()
+ } else {
+ c.Wait()
+ }
+ c.L.Unlock()
+ }
+ c.L.Lock()
+ id = -1
+ c.Broadcast()
+ c.L.Unlock()
+ done <- true
+ }()
+ }
+ for routine := 0; routine < waiters+1; routine++ {
+ <-done
+ }
+}
diff --git a/src/pkg/sync/example_test.go b/src/pkg/sync/example_test.go
index 031c87f03..bdd3af6fe 100644
--- a/src/pkg/sync/example_test.go
+++ b/src/pkg/sync/example_test.go
@@ -6,10 +6,15 @@ package sync_test
import (
"fmt"
- "net/http"
"sync"
)
+type httpPkg struct{}
+
+func (httpPkg) Get(url string) {}
+
+var http httpPkg
+
// This example fetches several URLs concurrently,
// using a WaitGroup to block until all the fetches are complete.
func ExampleWaitGroup() {
diff --git a/src/pkg/sync/once.go b/src/pkg/sync/once.go
index 1699e86a9..161ae3b3e 100644
--- a/src/pkg/sync/once.go
+++ b/src/pkg/sync/once.go
@@ -14,8 +14,8 @@ type Once struct {
done uint32
}
-// Do calls the function f if and only if the method is being called for the
-// first time with this receiver. In other words, given
+// Do calls the function f if and only if Do is being called for the
+// first time for this instance of Once. In other words, given
// var once Once
// if once.Do(f) is called multiple times, only the first call will invoke f,
// even if f has a different value in each invocation. A new instance of
diff --git a/src/pkg/sync/race.go b/src/pkg/sync/race.go
index d9431af6f..fd0277dcc 100644
--- a/src/pkg/sync/race.go
+++ b/src/pkg/sync/race.go
@@ -32,3 +32,11 @@ func raceDisable() {
func raceEnable() {
runtime.RaceEnable()
}
+
+func raceRead(addr unsafe.Pointer) {
+ runtime.RaceRead(addr)
+}
+
+func raceWrite(addr unsafe.Pointer) {
+ runtime.RaceWrite(addr)
+}
diff --git a/src/pkg/sync/race0.go b/src/pkg/sync/race0.go
index bef14f974..65ada1c5d 100644
--- a/src/pkg/sync/race0.go
+++ b/src/pkg/sync/race0.go
@@ -26,3 +26,9 @@ func raceDisable() {
func raceEnable() {
}
+
+func raceRead(addr unsafe.Pointer) {
+}
+
+func raceWrite(addr unsafe.Pointer) {
+}
diff --git a/src/pkg/sync/runtime.go b/src/pkg/sync/runtime.go
index e99599c11..3bf47ea52 100644
--- a/src/pkg/sync/runtime.go
+++ b/src/pkg/sync/runtime.go
@@ -4,6 +4,8 @@
package sync
+import "unsafe"
+
// defined in package runtime
// Semacquire waits until *s > 0 and then atomically decrements it.
@@ -16,3 +18,19 @@ func runtime_Semacquire(s *uint32)
// It is intended as a simple wakeup primitive for use by the synchronization
// library and should not be used directly.
func runtime_Semrelease(s *uint32)
+
+// Opaque representation of SyncSema in runtime/sema.goc.
+type syncSema [3]uintptr
+
+// Syncsemacquire waits for a pairing Syncsemrelease on the same semaphore s.
+func runtime_Syncsemacquire(s *syncSema)
+
+// Syncsemrelease waits for n pairing Syncsemacquire on the same semaphore s.
+func runtime_Syncsemrelease(s *syncSema, n uint32)
+
+// Ensure that sync and runtime agree on size of syncSema.
+func runtime_Syncsemcheck(size uintptr)
+func init() {
+ var s syncSema
+ runtime_Syncsemcheck(unsafe.Sizeof(s))
+}
diff --git a/src/pkg/sync/waitgroup.go b/src/pkg/sync/waitgroup.go
index ca3883783..22681115c 100644
--- a/src/pkg/sync/waitgroup.go
+++ b/src/pkg/sync/waitgroup.go
@@ -43,12 +43,23 @@ type WaitGroup struct {
// other event to be waited for. See the WaitGroup example.
func (wg *WaitGroup) Add(delta int) {
if raceenabled {
- _ = wg.m.state
- raceReleaseMerge(unsafe.Pointer(wg))
+ _ = wg.m.state // trigger nil deref early
+ if delta < 0 {
+ // Synchronize decrements with Wait.
+ raceReleaseMerge(unsafe.Pointer(wg))
+ }
raceDisable()
defer raceEnable()
}
v := atomic.AddInt32(&wg.counter, int32(delta))
+ if raceenabled {
+ if delta > 0 && v == int32(delta) {
+ // The first increment must be synchronized with Wait.
+ // Need to model this as a read, because there can be
+ // several concurrent wg.counter transitions from 0.
+ raceRead(unsafe.Pointer(&wg.sema))
+ }
+ }
if v < 0 {
panic("sync: negative WaitGroup counter")
}
@@ -72,7 +83,7 @@ func (wg *WaitGroup) Done() {
// Wait blocks until the WaitGroup counter is zero.
func (wg *WaitGroup) Wait() {
if raceenabled {
- _ = wg.m.state
+ _ = wg.m.state // trigger nil deref early
raceDisable()
}
if atomic.LoadInt32(&wg.counter) == 0 {
@@ -83,7 +94,7 @@ func (wg *WaitGroup) Wait() {
return
}
wg.m.Lock()
- atomic.AddInt32(&wg.waiters, 1)
+ w := atomic.AddInt32(&wg.waiters, 1)
// This code is racing with the unlocked path in Add above.
// The code above modifies counter and then reads waiters.
// We must modify waiters and then read counter (the opposite order)
@@ -101,6 +112,13 @@ func (wg *WaitGroup) Wait() {
}
return
}
+ if raceenabled && w == 1 {
+ // Wait must be synchronized with the first Add.
+ // Need to model this is as a write to race with the read in Add.
+ // As a consequence, can do the write only for the first waiter,
+ // otherwise concurrent Waits will race with each other.
+ raceWrite(unsafe.Pointer(&wg.sema))
+ }
if wg.sema == nil {
wg.sema = new(uint32)
}
diff --git a/src/pkg/syscall/asm_darwin_386.s b/src/pkg/syscall/asm_darwin_386.s
index 20cd809c7..2ddfb3bbd 100644
--- a/src/pkg/syscall/asm_darwin_386.s
+++ b/src/pkg/syscall/asm_darwin_386.s
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
//
// System call support for 386, Darwin
//
@@ -10,7 +12,7 @@
// func Syscall6(trap int32, a1, a2, a3, a4, a5, a6 int32) (r1, r2, err int32);
// Trap # in AX, args on stack above caller pc.
-TEXT ·Syscall(SB),7,$0
+TEXT ·Syscall(SB),NOSPLIT,$0-32
CALL runtime·entersyscall(SB)
MOVL 4(SP), AX // syscall entry
// slide args down on top of system call number
@@ -34,7 +36,7 @@ ok:
CALL runtime·exitsyscall(SB)
RET
-TEXT ·Syscall6(SB),7,$0
+TEXT ·Syscall6(SB),NOSPLIT,$0-44
CALL runtime·entersyscall(SB)
MOVL 4(SP), AX // syscall entry
// slide args down on top of system call number
@@ -61,7 +63,7 @@ ok6:
CALL runtime·exitsyscall(SB)
RET
-TEXT ·Syscall9(SB),7,$0
+TEXT ·Syscall9(SB),NOSPLIT,$0-56
CALL runtime·entersyscall(SB)
MOVL 4(SP), AX // syscall entry
// slide args down on top of system call number
@@ -91,7 +93,7 @@ ok9:
CALL runtime·exitsyscall(SB)
RET
-TEXT ·RawSyscall(SB),7,$0
+TEXT ·RawSyscall(SB),NOSPLIT,$0-32
MOVL 4(SP), AX // syscall entry
// slide args down on top of system call number
LEAL 8(SP), SI
@@ -112,7 +114,7 @@ ok1:
MOVL $0, 28(SP) // errno
RET
-TEXT ·RawSyscall6(SB),7,$0
+TEXT ·RawSyscall6(SB),NOSPLIT,$0-44
MOVL 4(SP), AX // syscall entry
// slide args down on top of system call number
LEAL 8(SP), SI
diff --git a/src/pkg/syscall/asm_darwin_amd64.s b/src/pkg/syscall/asm_darwin_amd64.s
index 1613622aa..c1970b71d 100644
--- a/src/pkg/syscall/asm_darwin_amd64.s
+++ b/src/pkg/syscall/asm_darwin_amd64.s
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
//
// System call support for AMD64, Darwin
//
@@ -10,7 +12,7 @@
// func Syscall6(trap int64, a1, a2, a3, a4, a5, a6 int64) (r1, r2, err int64);
// Trap # in AX, args in DI SI DX, return in AX DX
-TEXT ·Syscall(SB),7,$0
+TEXT ·Syscall(SB),NOSPLIT,$0-64
CALL runtime·entersyscall(SB)
MOVQ 16(SP), DI
MOVQ 24(SP), SI
@@ -34,7 +36,7 @@ ok:
CALL runtime·exitsyscall(SB)
RET
-TEXT ·Syscall6(SB),7,$0
+TEXT ·Syscall6(SB),NOSPLIT,$0-88
CALL runtime·entersyscall(SB)
MOVQ 16(SP), DI
MOVQ 24(SP), SI
@@ -58,7 +60,7 @@ ok6:
CALL runtime·exitsyscall(SB)
RET
-TEXT ·RawSyscall(SB),7,$0
+TEXT ·RawSyscall(SB),NOSPLIT,$0-64
MOVQ 16(SP), DI
MOVQ 24(SP), SI
MOVQ 32(SP), DX
@@ -79,7 +81,7 @@ ok1:
MOVQ $0, 56(SP) // errno
RET
-TEXT ·RawSyscall6(SB),7,$0
+TEXT ·RawSyscall6(SB),NOSPLIT,$0-88
MOVQ 16(SP), DI
MOVQ 24(SP), SI
MOVQ 32(SP), DX
diff --git a/src/pkg/syscall/asm_dragonfly_386.s b/src/pkg/syscall/asm_dragonfly_386.s
new file mode 100644
index 000000000..d24216fdd
--- /dev/null
+++ b/src/pkg/syscall/asm_dragonfly_386.s
@@ -0,0 +1,139 @@
+// 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.
+
+#include "../../cmd/ld/textflag.h"
+
+//
+// System call support for 386, FreeBSD
+//
+
+// func Syscall(trap int32, a1, a2, a3 int32) (r1, r2, err int32);
+// func Syscall6(trap int32, a1, a2, a3, a4, a5, a6 int32) (r1, r2, err int32);
+// Trap # in AX, args on stack above caller pc.
+
+TEXT ·Syscall(SB),NOSPLIT,$0-32
+ 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 $0x80
+ JAE ok
+ MOVL $-1, 20(SP) // r1
+ MOVL $-1, 24(SP) // r2
+ MOVL AX, 28(SP) // errno
+ CALL runtime·exitsyscall(SB)
+ RET
+ok:
+ MOVL AX, 20(SP) // r1
+ MOVL DX, 24(SP) // r2
+ MOVL $0, 28(SP) // errno
+ CALL runtime·exitsyscall(SB)
+ RET
+
+TEXT ·Syscall6(SB),NOSPLIT,$0-44
+ 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 $0x80
+ JAE ok6
+ MOVL $-1, 32(SP) // r1
+ MOVL $-1, 36(SP) // r2
+ MOVL AX, 40(SP) // errno
+ CALL runtime·exitsyscall(SB)
+ RET
+ok6:
+ MOVL AX, 32(SP) // r1
+ MOVL DX, 36(SP) // r2
+ MOVL $0, 40(SP) // errno
+ CALL runtime·exitsyscall(SB)
+ RET
+
+TEXT ·Syscall9(SB),NOSPLIT,$0-56
+ 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),NOSPLIT,$0-32
+ 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 $0x80
+ JAE ok1
+ MOVL $-1, 20(SP) // r1
+ MOVL $-1, 24(SP) // r2
+ MOVL AX, 28(SP) // errno
+ RET
+ok1:
+ MOVL AX, 20(SP) // r1
+ MOVL DX, 24(SP) // r2
+ MOVL $0, 28(SP) // errno
+ RET
+
+TEXT ·RawSyscall6(SB),NOSPLIT,$0-44
+ 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_dragonfly_amd64.s b/src/pkg/syscall/asm_dragonfly_amd64.s
new file mode 100644
index 000000000..31d107490
--- /dev/null
+++ b/src/pkg/syscall/asm_dragonfly_amd64.s
@@ -0,0 +1,133 @@
+// 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.
+
+#include "../../cmd/ld/textflag.h"
+
+//
+// System call support for AMD64, DragonFly
+//
+
+// func Syscall(trap int64, a1, a2, a3 int64) (r1, r2, err int64);
+// func Syscall6(trap int64, a1, a2, a3, a4, a5, a6 int64) (r1, r2, err int64);
+// func Syscall9(trap int64, a1, a2, a3, a4, a5, a6, a7, a8, a9 int64) (r1, r2, err int64)
+// Trap # in AX, args in DI SI DX, return in AX DX
+
+TEXT ·Syscall(SB),NOSPLIT,$0-64
+ CALL runtime·entersyscall(SB)
+ MOVQ 16(SP), DI
+ MOVQ 24(SP), SI
+ MOVQ 32(SP), DX
+ MOVQ $0, R10
+ MOVQ $0, R8
+ MOVQ $0, R9
+ MOVQ 8(SP), AX // syscall entry
+ SYSCALL
+ JCC ok
+ MOVQ $-1, 40(SP) // r1
+ MOVQ $0, 48(SP) // r2
+ MOVQ AX, 56(SP) // errno
+ CALL runtime·exitsyscall(SB)
+ RET
+ok:
+ MOVQ AX, 40(SP) // r1
+ MOVQ DX, 48(SP) // r2
+ MOVQ $0, 56(SP) // errno
+ CALL runtime·exitsyscall(SB)
+ RET
+
+TEXT ·Syscall6(SB),NOSPLIT,$0-88
+ CALL runtime·entersyscall(SB)
+ 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 ok6
+ MOVQ $-1, 64(SP) // r1
+ MOVQ $0, 72(SP) // r2
+ MOVQ AX, 80(SP) // errno
+ CALL runtime·exitsyscall(SB)
+ RET
+ok6:
+ MOVQ AX, 64(SP) // r1
+ MOVQ DX, 72(SP) // r2
+ MOVQ $0, 80(SP) // errno
+ CALL runtime·exitsyscall(SB)
+ RET
+
+TEXT ·Syscall9(SB),NOSPLIT,$0-112
+ CALL runtime·entersyscall(SB)
+ MOVQ 8(SP), AX
+ MOVQ 16(SP), DI
+ MOVQ 24(SP), SI
+ MOVQ 32(SP), DX
+ MOVQ 40(SP), R10
+ MOVQ 48(SP), R8
+ MOVQ 56(SP), R9
+
+ // shift around the last three arguments so they're at the
+ // top of the stack when the syscall is called.
+ MOVQ 64(SP), R11 // arg 7
+ MOVQ R11, 8(SP)
+ MOVQ 72(SP), R11 // arg 8
+ MOVQ R11, 16(SP)
+ MOVQ 80(SP), R11 // arg 9
+ MOVQ R11, 24(SP)
+
+ SYSCALL
+ JCC ok9
+ MOVQ $-1, 88(SP) // r1
+ MOVQ $0, 96(SP) // r2
+ MOVQ AX, 104(SP) // errno
+ CALL runtime·exitsyscall(SB)
+ RET
+ok9:
+ MOVQ AX, 88(SP) // r1
+ MOVQ DX, 96(SP) // r2
+ MOVQ $0, 104(SP) // errno
+ CALL runtime·exitsyscall(SB)
+ RET
+
+TEXT ·RawSyscall(SB),NOSPLIT,$0-64
+ MOVQ 16(SP), DI
+ MOVQ 24(SP), SI
+ MOVQ 32(SP), DX
+ MOVQ $0, R10
+ MOVQ $0, R8
+ MOVQ $0, R9
+ MOVQ 8(SP), AX // syscall entry
+ SYSCALL
+ JCC ok1
+ MOVQ $-1, 40(SP) // r1
+ MOVQ $0, 48(SP) // r2
+ MOVQ AX, 56(SP) // errno
+ RET
+ok1:
+ MOVQ AX, 40(SP) // r1
+ MOVQ DX, 48(SP) // r2
+ MOVQ $0, 56(SP) // errno
+ RET
+
+TEXT ·RawSyscall6(SB),NOSPLIT,$0-88
+ 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_freebsd_386.s b/src/pkg/syscall/asm_freebsd_386.s
index f2d4438a1..d24216fdd 100644
--- a/src/pkg/syscall/asm_freebsd_386.s
+++ b/src/pkg/syscall/asm_freebsd_386.s
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
//
// System call support for 386, FreeBSD
//
@@ -10,7 +12,7 @@
// func Syscall6(trap int32, a1, a2, a3, a4, a5, a6 int32) (r1, r2, err int32);
// Trap # in AX, args on stack above caller pc.
-TEXT ·Syscall(SB),7,$0
+TEXT ·Syscall(SB),NOSPLIT,$0-32
CALL runtime·entersyscall(SB)
MOVL 4(SP), AX // syscall entry
// slide args down on top of system call number
@@ -34,7 +36,7 @@ ok:
CALL runtime·exitsyscall(SB)
RET
-TEXT ·Syscall6(SB),7,$0
+TEXT ·Syscall6(SB),NOSPLIT,$0-44
CALL runtime·entersyscall(SB)
MOVL 4(SP), AX // syscall entry
// slide args down on top of system call number
@@ -61,7 +63,7 @@ ok6:
CALL runtime·exitsyscall(SB)
RET
-TEXT ·Syscall9(SB),7,$0
+TEXT ·Syscall9(SB),NOSPLIT,$0-56
CALL runtime·entersyscall(SB)
MOVL 4(SP), AX // syscall entry
// slide args down on top of system call number
@@ -91,7 +93,7 @@ ok9:
CALL runtime·exitsyscall(SB)
RET
-TEXT ·RawSyscall(SB),7,$0
+TEXT ·RawSyscall(SB),NOSPLIT,$0-32
MOVL 4(SP), AX // syscall entry
// slide args down on top of system call number
LEAL 8(SP), SI
@@ -112,7 +114,7 @@ ok1:
MOVL $0, 28(SP) // errno
RET
-TEXT ·RawSyscall6(SB),7,$0
+TEXT ·RawSyscall6(SB),NOSPLIT,$0-44
MOVL 4(SP), AX // syscall entry
// slide args down on top of system call number
LEAL 8(SP), SI
diff --git a/src/pkg/syscall/asm_freebsd_amd64.s b/src/pkg/syscall/asm_freebsd_amd64.s
index fbf917804..fca7f371e 100644
--- a/src/pkg/syscall/asm_freebsd_amd64.s
+++ b/src/pkg/syscall/asm_freebsd_amd64.s
@@ -2,16 +2,23 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
//
// System call support for AMD64, FreeBSD
//
+// The SYSCALL variant for invoking system calls is broken in FreeBSD.
+// See comment at top of ../runtime/sys_freebsd_amd64.c and
+// golang.org/issue/6372.
+#define SYSCALL MOVQ R10, CX; INT $0x80
+
// func Syscall(trap int64, a1, a2, a3 int64) (r1, r2, err int64);
// func Syscall6(trap int64, a1, a2, a3, a4, a5, a6 int64) (r1, r2, err int64);
// func Syscall9(trap int64, a1, a2, a3, a4, a5, a6, a7, a8, a9 int64) (r1, r2, err int64)
// Trap # in AX, args in DI SI DX, return in AX DX
-TEXT ·Syscall(SB),7,$0
+TEXT ·Syscall(SB),NOSPLIT,$0-64
CALL runtime·entersyscall(SB)
MOVQ 16(SP), DI
MOVQ 24(SP), SI
@@ -34,7 +41,7 @@ ok:
CALL runtime·exitsyscall(SB)
RET
-TEXT ·Syscall6(SB),7,$0
+TEXT ·Syscall6(SB),NOSPLIT,$0-88
CALL runtime·entersyscall(SB)
MOVQ 16(SP), DI
MOVQ 24(SP), SI
@@ -57,7 +64,7 @@ ok6:
CALL runtime·exitsyscall(SB)
RET
-TEXT ·Syscall9(SB),7,$0
+TEXT ·Syscall9(SB),NOSPLIT,$0-112
CALL runtime·entersyscall(SB)
MOVQ 8(SP), AX
MOVQ 16(SP), DI
@@ -90,7 +97,7 @@ ok9:
CALL runtime·exitsyscall(SB)
RET
-TEXT ·RawSyscall(SB),7,$0
+TEXT ·RawSyscall(SB),NOSPLIT,$0-64
MOVQ 16(SP), DI
MOVQ 24(SP), SI
MOVQ 32(SP), DX
@@ -110,7 +117,7 @@ ok1:
MOVQ $0, 56(SP) // errno
RET
-TEXT ·RawSyscall6(SB),7,$0
+TEXT ·RawSyscall6(SB),NOSPLIT,$0-88
MOVQ 16(SP), DI
MOVQ 24(SP), SI
MOVQ 32(SP), DX
diff --git a/src/pkg/syscall/asm_freebsd_arm.s b/src/pkg/syscall/asm_freebsd_arm.s
index ab08ab5f5..9283d079b 100644
--- a/src/pkg/syscall/asm_freebsd_arm.s
+++ b/src/pkg/syscall/asm_freebsd_arm.s
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
//
// System call support for ARM, FreeBSD
//
@@ -10,12 +12,12 @@
// func Syscall6(trap int32, a1, a2, a3, a4, a5, a6 int32) (r1, r2, err int32);
// func Syscall9(trap int32, a1, a2, a3, a4, a5, a6, a7, a8, a9 int64) (r1, r2, err int32)
-TEXT ·Syscall(SB),7,$0
+TEXT ·Syscall(SB),NOSPLIT,$0-28
BL runtime·entersyscall(SB)
- MOVW 0(FP), R0 // sigcall num
- MOVW 4(FP), R1 // a1
- MOVW 8(FP), R2 // a2
- MOVW 12(FP), R3 // a3
+ MOVW 0(FP), R7 // sigcall num
+ MOVW 4(FP), R0 // a1
+ MOVW 8(FP), R1 // a2
+ MOVW 12(FP), R2 // a3
SWI $0 // syscall
MOVW $0, R2
BCS error
@@ -32,16 +34,16 @@ error:
BL runtime·exitsyscall(SB)
RET
-TEXT ·Syscall6(SB),7,$0
+TEXT ·Syscall6(SB),NOSPLIT,$0-40
BL runtime·entersyscall(SB)
- MOVW 0(FP), R0 // sigcall num
- MOVW 4(FP), R1 // a1
- MOVW 8(FP), R2 // a2
- MOVW 12(FP), R3 // a3
- MOVW R13, R4
- MOVW $16(FP), R13 // a4 to a6 are passed on stack
+ MOVW 0(FP), R7 // sigcall num
+ MOVW 4(FP), R0 // a1
+ MOVW 8(FP), R1 // a2
+ MOVW 12(FP), R2 // a3
+ MOVW 16(FP), R3 // a4
+ ADD $24, R13 // a5 to a6 are passed on stack
SWI $0 // syscall
- MOVW R4, R13
+ SUB $24, R13
MOVW $0, R2
BCS error6
MOVW R0, 28(FP) // r1
@@ -57,16 +59,16 @@ error6:
BL runtime·exitsyscall(SB)
RET
-TEXT ·Syscall9(SB),7,$0
+TEXT ·Syscall9(SB),NOSPLIT,$0-52
BL runtime·entersyscall(SB)
- MOVW 0(FP), R0 // sigcall num
- MOVW 4(FP), R1 // a1
- MOVW 8(FP), R2 // a2
- MOVW 12(FP), R3 // a3
- MOVW R13, R4
- MOVW $16(FP), R13 // a4 to a9 are passed on stack
+ MOVW 0(FP), R7 // sigcall num
+ MOVW 4(FP), R0 // a1
+ MOVW 8(FP), R1 // a2
+ MOVW 12(FP), R2 // a3
+ MOVW 16(FP), R3 // a4
+ ADD $24, R13 // a5 to a9 are passed on stack
SWI $0 // syscall
- MOVW R4, R13
+ SUB $24, R13
MOVW $0, R2
BCS error9
MOVW R0, 40(FP) // r1
@@ -82,11 +84,11 @@ error9:
BL runtime·exitsyscall(SB)
RET
-TEXT ·RawSyscall(SB),7,$0
- MOVW 0(FP), R0 // sigcall num
- MOVW 4(FP), R1 // a1
- MOVW 8(FP), R2 // a2
- MOVW 12(FP), R3 // a3
+TEXT ·RawSyscall(SB),NOSPLIT,$0-28
+ MOVW 0(FP), R7 // sigcall num
+ MOVW 4(FP), R0 // a1
+ MOVW 8(FP), R1 // a2
+ MOVW 12(FP), R2 // a3
SWI $0 // syscall
MOVW $0, R2
BCS errorr
@@ -101,15 +103,15 @@ errorr:
MOVW R0, 24(FP) // err
RET
-TEXT ·RawSyscall6(SB),7,$0
- MOVW 0(FP), R0 // sigcall num
- MOVW 4(FP), R1 // a1
- MOVW 8(FP), R2 // a2
- MOVW 12(FP), R3 // a3
- MOVW R13, R4
- MOVW $16(FP), R13 // a4 to a9 are passed on stack
+TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
+ MOVW 0(FP), R7 // sigcall num
+ MOVW 4(FP), R0 // a1
+ MOVW 8(FP), R1 // a2
+ MOVW 12(FP), R2 // a3
+ MOVW 16(FP), R3 // a4
+ ADD $24, R13 // a5 to a6 are passed on stack
SWI $0 // syscall
- MOVW R4, R13
+ SUB $24, R13
MOVW $0, R2
BCS errorr6
MOVW R0, 28(FP) // r1
diff --git a/src/pkg/syscall/asm_linux_386.s b/src/pkg/syscall/asm_linux_386.s
index 22e00e45b..cf2ab02ab 100644
--- a/src/pkg/syscall/asm_linux_386.s
+++ b/src/pkg/syscall/asm_linux_386.s
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
//
// System calls for 386, Linux
//
@@ -9,7 +11,7 @@
// func Syscall(trap uintptr, a1, a2, a3 uintptr) (r1, r2, err uintptr);
// Trap # in AX, args in BX CX DX SI DI, return in AX
-TEXT ·Syscall(SB),7,$0
+TEXT ·Syscall(SB),NOSPLIT,$0-32
CALL runtime·entersyscall(SB)
MOVL 4(SP), AX // syscall entry
MOVL 8(SP), BX
@@ -34,7 +36,7 @@ ok:
RET
// func Syscall6(trap uintptr, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr);
-TEXT ·Syscall6(SB),7,$0
+TEXT ·Syscall6(SB),NOSPLIT,$0-44
CALL runtime·entersyscall(SB)
MOVL 4(SP), AX // syscall entry
MOVL 8(SP), BX
@@ -60,7 +62,7 @@ ok6:
RET
// func RawSyscall(trap uintptr, a1, a2, a3 uintptr) (r1, r2, err uintptr);
-TEXT ·RawSyscall(SB),7,$0
+TEXT ·RawSyscall(SB),NOSPLIT,$0-32
MOVL 4(SP), AX // syscall entry
MOVL 8(SP), BX
MOVL 12(SP), CX
@@ -82,7 +84,7 @@ ok1:
RET
// func RawSyscall6(trap uintptr, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr);
-TEXT ·RawSyscall6(SB),7,$0
+TEXT ·RawSyscall6(SB),NOSPLIT,$0-44
MOVL 4(SP), AX // syscall entry
MOVL 8(SP), BX
MOVL 12(SP), CX
@@ -108,7 +110,7 @@ ok2:
// func socketcall(call int, a0, a1, a2, a3, a4, a5 uintptr) (n int, errno int)
// Kernel interface gets call sub-number and pointer to a0.
-TEXT ·socketcall(SB),7,$0
+TEXT ·socketcall(SB),NOSPLIT,$0-40
CALL runtime·entersyscall(SB)
MOVL $SYS_SOCKETCALL, AX // syscall entry
MOVL 4(SP), BX // socket call number
@@ -132,7 +134,7 @@ oksock:
// 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
+TEXT ·rawsocketcall(SB),NOSPLIT,$0-40
MOVL $SYS_SOCKETCALL, AX // syscall entry
MOVL 4(SP), BX // socket call number
LEAL 8(SP), CX // pointer to call arguments
@@ -157,7 +159,7 @@ oksock1:
// taking the address of the return value newoffset.
// Underlying system call is
// llseek(int fd, int offhi, int offlo, int64 *result, int whence)
-TEXT ·Seek(SB),7,$0
+TEXT ·Seek(SB),NOSPLIT,$0-32
CALL runtime·entersyscall(SB)
MOVL $SYS__LLSEEK, AX // syscall entry
MOVL 4(SP), BX // fd
diff --git a/src/pkg/syscall/asm_linux_amd64.s b/src/pkg/syscall/asm_linux_amd64.s
index 1a1fdb06a..28a2a5809 100644
--- a/src/pkg/syscall/asm_linux_amd64.s
+++ b/src/pkg/syscall/asm_linux_amd64.s
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
//
// System calls for AMD64, Linux
//
@@ -11,7 +13,7 @@
// Note that this differs from "standard" ABI convention, which
// would pass 4th arg in CX, not R10.
-TEXT ·Syscall(SB),7,$0
+TEXT ·Syscall(SB),NOSPLIT,$0-64
CALL runtime·entersyscall(SB)
MOVQ 16(SP), DI
MOVQ 24(SP), SI
@@ -36,7 +38,7 @@ ok:
CALL runtime·exitsyscall(SB)
RET
-TEXT ·Syscall6(SB),7,$0
+TEXT ·Syscall6(SB),NOSPLIT,$0-88
CALL runtime·entersyscall(SB)
MOVQ 16(SP), DI
MOVQ 24(SP), SI
@@ -61,7 +63,7 @@ ok6:
CALL runtime·exitsyscall(SB)
RET
-TEXT ·RawSyscall(SB),7,$0
+TEXT ·RawSyscall(SB),NOSPLIT,$0-64
MOVQ 16(SP), DI
MOVQ 24(SP), SI
MOVQ 32(SP), DX
@@ -83,7 +85,7 @@ ok1:
MOVQ $0, 56(SP) // errno
RET
-TEXT ·RawSyscall6(SB),7,$0
+TEXT ·RawSyscall6(SB),NOSPLIT,$0-88
MOVQ 16(SP), DI
MOVQ 24(SP), SI
MOVQ 32(SP), DX
@@ -105,7 +107,7 @@ ok2:
MOVQ $0, 80(SP) // errno
RET
-TEXT ·Gettimeofday(SB),7,$0
+TEXT ·Gettimeofday(SB),NOSPLIT,$0-24
MOVQ 8(SP), DI
MOVQ $0, SI
MOVQ runtime·__vdso_gettimeofday_sym(SB), AX
@@ -120,7 +122,7 @@ ok7:
MOVQ $0, 16(SP) // errno
RET
-TEXT ·Time(SB),7,$0
+TEXT ·Time(SB),NOSPLIT,$0-32
MOVQ 8(SP), DI
MOVQ runtime·__vdso_time_sym(SB), AX
CALL AX
diff --git a/src/pkg/syscall/asm_linux_arm.s b/src/pkg/syscall/asm_linux_arm.s
index 2651b7284..bf54b4fe6 100644
--- a/src/pkg/syscall/asm_linux_arm.s
+++ b/src/pkg/syscall/asm_linux_arm.s
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
//
// System calls for arm, Linux
//
@@ -10,7 +12,7 @@
// func Syscall(syscall uintptr, a1, a2, a3 uintptr) (r1, r2, err uintptr);
-TEXT ·Syscall(SB),7,$0
+TEXT ·Syscall(SB),NOSPLIT,$0-28
BL runtime·entersyscall(SB)
MOVW 4(SP), R7
MOVW 8(SP), R0
@@ -38,7 +40,7 @@ ok:
// 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
+TEXT ·Syscall6(SB),NOSPLIT,$0-40
BL runtime·entersyscall(SB)
MOVW 4(SP), R7 // syscall entry
MOVW 8(SP), R0
@@ -69,7 +71,7 @@ ok6:
// 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
+TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
MOVW 4(SP), R7 // syscall entry
MOVW 8(SP), R0
MOVW 12(SP), R1
@@ -101,7 +103,7 @@ ok2:
// taking the address of the return value newoffset.
// Underlying system call is
// llseek(int fd, int offhi, int offlo, int64 *result, int whence)
-TEXT ·Seek(SB),7,$0
+TEXT ·Seek(SB),NOSPLIT,$0-32
BL runtime·entersyscall(SB)
MOVW $SYS__LLSEEK, R7 // syscall entry
MOVW 4(SP), R0 // fd
@@ -128,7 +130,7 @@ okseek:
RET
// func RawSyscall(trap uintptr, a1, a2, a3 uintptr) (r1, r2, err uintptr);
-TEXT ·RawSyscall(SB),7,$0
+TEXT ·RawSyscall(SB),NOSPLIT,$0-28
MOVW 4(SP), R7 // syscall entry
MOVW 8(SP), R0
MOVW 12(SP), R1
diff --git a/src/pkg/syscall/asm_netbsd_386.s b/src/pkg/syscall/asm_netbsd_386.s
index ec2065fc7..8caade255 100644
--- a/src/pkg/syscall/asm_netbsd_386.s
+++ b/src/pkg/syscall/asm_netbsd_386.s
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
//
// System call support for 386, NetBSD
//
@@ -10,7 +12,7 @@
// func Syscall6(trap int32, a1, a2, a3, a4, a5, a6 int32) (r1, r2, err int32);
// Trap # in AX, args on stack above caller pc.
-TEXT ·Syscall(SB),7,$0
+TEXT ·Syscall(SB),NOSPLIT,$0-32
CALL runtime·entersyscall(SB)
MOVL 4(SP), AX // syscall entry
// slide args down on top of system call number
@@ -34,7 +36,7 @@ ok:
CALL runtime·exitsyscall(SB)
RET
-TEXT ·Syscall6(SB),7,$0
+TEXT ·Syscall6(SB),NOSPLIT,$0-44
CALL runtime·entersyscall(SB)
MOVL 4(SP), AX // syscall entry
// slide args down on top of system call number
@@ -61,7 +63,7 @@ ok6:
CALL runtime·exitsyscall(SB)
RET
-TEXT ·Syscall9(SB),7,$0
+TEXT ·Syscall9(SB),NOSPLIT,$0-56
CALL runtime·entersyscall(SB)
MOVL 4(SP), AX // syscall entry
// slide args down on top of system call number
@@ -91,7 +93,7 @@ ok9:
CALL runtime·exitsyscall(SB)
RET
-TEXT ·RawSyscall(SB),7,$0
+TEXT ·RawSyscall(SB),NOSPLIT,$0-32
MOVL 4(SP), AX // syscall entry
// slide args down on top of system call number
LEAL 8(SP), SI
@@ -112,7 +114,7 @@ ok1:
MOVL $0, 28(SP) // errno
RET
-TEXT ·RawSyscall6(SB),7,$0
+TEXT ·RawSyscall6(SB),NOSPLIT,$0-44
MOVL 4(SP), AX // syscall entry
// slide args down on top of system call number
LEAL 8(SP), SI
diff --git a/src/pkg/syscall/asm_netbsd_amd64.s b/src/pkg/syscall/asm_netbsd_amd64.s
index 240e41c8f..e0b8b3cb8 100644
--- a/src/pkg/syscall/asm_netbsd_amd64.s
+++ b/src/pkg/syscall/asm_netbsd_amd64.s
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
//
// System call support for AMD64, NetBSD
//
@@ -11,7 +13,7 @@
// func Syscall9(trap int64, a1, a2, a3, a4, a5, a6, a7, a8, a9 int64) (r1, r2, err int64);
// Trap # in AX, args in DI SI DX, return in AX DX
-TEXT ·Syscall(SB),7,$0
+TEXT ·Syscall(SB),NOSPLIT,$0-64
CALL runtime·entersyscall(SB)
MOVQ 8(SP), AX // syscall entry
MOVQ 16(SP), DI
@@ -34,7 +36,7 @@ ok:
CALL runtime·exitsyscall(SB)
RET
-TEXT ·Syscall6(SB),7,$0
+TEXT ·Syscall6(SB),NOSPLIT,$0-88
CALL runtime·entersyscall(SB)
MOVQ 8(SP), AX // syscall entry
MOVQ 16(SP), DI
@@ -57,7 +59,7 @@ ok6:
CALL runtime·exitsyscall(SB)
RET
-TEXT ·Syscall9(SB),7,$0
+TEXT ·Syscall9(SB),NOSPLIT,$0-112
CALL runtime·entersyscall(SB)
MOVQ 8(SP), AX // syscall entry
MOVQ 16(SP), DI
@@ -76,20 +78,20 @@ TEXT ·Syscall9(SB),7,$0
SYSCALL
JCC ok9
ADDQ $32, SP
- MOVQ $-1, 64(SP) // r1
- MOVQ $0, 72(SP) // r2
- MOVQ AX, 80(SP) // errno
+ MOVQ $-1, 88(SP) // r1
+ MOVQ $0, 96(SP) // r2
+ MOVQ AX, 104(SP) // errno
CALL runtime·exitsyscall(SB)
RET
ok9:
ADDQ $32, SP
- MOVQ AX, 64(SP) // r1
- MOVQ DX, 72(SP) // r2
- MOVQ $0, 80(SP) // errno
+ MOVQ AX, 88(SP) // r1
+ MOVQ DX, 96(SP) // r2
+ MOVQ $0, 104(SP) // errno
CALL runtime·exitsyscall(SB)
RET
-TEXT ·RawSyscall(SB),7,$0
+TEXT ·RawSyscall(SB),NOSPLIT,$0-64
MOVQ 16(SP), DI
MOVQ 24(SP), SI
MOVQ 32(SP), DX
@@ -109,7 +111,7 @@ ok1:
MOVQ $0, 56(SP) // errno
RET
-TEXT ·RawSyscall6(SB),7,$0
+TEXT ·RawSyscall6(SB),NOSPLIT,$0-88
MOVQ 16(SP), DI
MOVQ 24(SP), SI
MOVQ 32(SP), DX
diff --git a/src/pkg/syscall/asm_netbsd_arm.s b/src/pkg/syscall/asm_netbsd_arm.s
index e42da334a..2c0d65401 100644
--- a/src/pkg/syscall/asm_netbsd_arm.s
+++ b/src/pkg/syscall/asm_netbsd_arm.s
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
//
// System call support for ARM, NetBSD
//
@@ -10,7 +12,7 @@
// func Syscall6(trap int32, a1, a2, a3, a4, a5, a6 int32) (r1, r2, err int32);
// func Syscall9(trap int32, a1, a2, a3, a4, a5, a6, a7, a8, a9 int64) (r1, r2, err int32)
-TEXT ·Syscall(SB),7,$0
+TEXT ·Syscall(SB),NOSPLIT,$0-28
BL runtime·entersyscall(SB)
MOVW 0(FP), R0 // sigcall num
MOVW 4(FP), R1 // a1
@@ -32,7 +34,7 @@ error:
BL runtime·exitsyscall(SB)
RET
-TEXT ·Syscall6(SB),7,$0
+TEXT ·Syscall6(SB),NOSPLIT,$0-40
BL runtime·entersyscall(SB)
MOVW 0(FP), R0 // sigcall num
MOVW 4(FP), R1 // a1
@@ -57,7 +59,7 @@ error6:
BL runtime·exitsyscall(SB)
RET
-TEXT ·Syscall9(SB),7,$0
+TEXT ·Syscall9(SB),NOSPLIT,$0-52
BL runtime·entersyscall(SB)
MOVW 0(FP), R0 // sigcall num
MOVW 4(FP), R1 // a1
@@ -82,7 +84,7 @@ error9:
BL runtime·exitsyscall(SB)
RET
-TEXT ·RawSyscall(SB),7,$0
+TEXT ·RawSyscall(SB),NOSPLIT,$0-28
MOVW 0(FP), R0 // sigcall num
MOVW 4(FP), R1 // a1
MOVW 8(FP), R2 // a2
@@ -101,7 +103,7 @@ errorr:
MOVW R0, 24(FP) // err
RET
-TEXT ·RawSyscall6(SB),7,$0
+TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
MOVW 0(FP), R0 // sigcall num
MOVW 4(FP), R1 // a1
MOVW 8(FP), R2 // a2
diff --git a/src/pkg/syscall/asm_openbsd_386.s b/src/pkg/syscall/asm_openbsd_386.s
index daa115f26..a38349661 100644
--- a/src/pkg/syscall/asm_openbsd_386.s
+++ b/src/pkg/syscall/asm_openbsd_386.s
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
//
// System call support for 386, OpenBSD
//
@@ -10,7 +12,7 @@
// func Syscall6(trap int32, a1, a2, a3, a4, a5, a6 int32) (r1, r2, err int32);
// Trap # in AX, args on stack above caller pc.
-TEXT ·Syscall(SB),7,$0
+TEXT ·Syscall(SB),NOSPLIT,$0-32
CALL runtime·entersyscall(SB)
MOVL 4(SP), AX // syscall entry
// slide args down on top of system call number
@@ -34,7 +36,7 @@ ok:
CALL runtime·exitsyscall(SB)
RET
-TEXT ·Syscall6(SB),7,$0
+TEXT ·Syscall6(SB),NOSPLIT,$0-44
CALL runtime·entersyscall(SB)
MOVL 4(SP), AX // syscall entry
// slide args down on top of system call number
@@ -61,7 +63,7 @@ ok6:
CALL runtime·exitsyscall(SB)
RET
-TEXT ·Syscall9(SB),7,$0
+TEXT ·Syscall9(SB),NOSPLIT,$0-56
CALL runtime·entersyscall(SB)
MOVL 4(SP), AX // syscall entry
// slide args down on top of system call number
@@ -91,7 +93,7 @@ ok9:
CALL runtime·exitsyscall(SB)
RET
-TEXT ·RawSyscall(SB),7,$0
+TEXT ·RawSyscall(SB),NOSPLIT,$0-32
MOVL 4(SP), AX // syscall entry
// slide args down on top of system call number
LEAL 8(SP), SI
@@ -112,7 +114,7 @@ ok1:
MOVL $0, 28(SP) // errno
RET
-TEXT ·RawSyscall6(SB),7,$0
+TEXT ·RawSyscall6(SB),NOSPLIT,$0-44
MOVL 4(SP), AX // syscall entry
// slide args down on top of system call number
LEAL 8(SP), SI
diff --git a/src/pkg/syscall/asm_openbsd_amd64.s b/src/pkg/syscall/asm_openbsd_amd64.s
index e19c6a9a6..1bf25f1db 100644
--- a/src/pkg/syscall/asm_openbsd_amd64.s
+++ b/src/pkg/syscall/asm_openbsd_amd64.s
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
//
// System call support for AMD64, OpenBSD
//
@@ -11,7 +13,7 @@
// func Syscall9(trap int64, a1, a2, a3, a4, a5, a6, a7, a8, a9 int64) (r1, r2, err int64);
// Trap # in AX, args in DI SI DX, return in AX DX
-TEXT ·Syscall(SB),7,$0
+TEXT ·Syscall(SB),NOSPLIT,$0-64
CALL runtime·entersyscall(SB)
MOVQ 8(SP), AX // syscall entry
MOVQ 16(SP), DI
@@ -34,7 +36,7 @@ ok:
CALL runtime·exitsyscall(SB)
RET
-TEXT ·Syscall6(SB),7,$0
+TEXT ·Syscall6(SB),NOSPLIT,$0-88
CALL runtime·entersyscall(SB)
MOVQ 8(SP), AX // syscall entry
MOVQ 16(SP), DI
@@ -57,7 +59,7 @@ ok6:
CALL runtime·exitsyscall(SB)
RET
-TEXT ·Syscall9(SB),7,$0
+TEXT ·Syscall9(SB),NOSPLIT,$0-112
CALL runtime·entersyscall(SB)
MOVQ 8(SP), AX // syscall entry
MOVQ 16(SP), DI
@@ -76,20 +78,20 @@ TEXT ·Syscall9(SB),7,$0
SYSCALL
JCC ok9
ADDQ $32, SP
- MOVQ $-1, 64(SP) // r1
- MOVQ $0, 72(SP) // r2
- MOVQ AX, 80(SP) // errno
+ MOVQ $-1, 88(SP) // r1
+ MOVQ $0, 96(SP) // r2
+ MOVQ AX, 104(SP) // errno
CALL runtime·exitsyscall(SB)
RET
ok9:
ADDQ $32, SP
- MOVQ AX, 64(SP) // r1
- MOVQ DX, 72(SP) // r2
- MOVQ $0, 80(SP) // errno
+ MOVQ AX, 88(SP) // r1
+ MOVQ DX, 96(SP) // r2
+ MOVQ $0, 104(SP) // errno
CALL runtime·exitsyscall(SB)
RET
-TEXT ·RawSyscall(SB),7,$0
+TEXT ·RawSyscall(SB),NOSPLIT,$0-64
MOVQ 16(SP), DI
MOVQ 24(SP), SI
MOVQ 32(SP), DX
@@ -109,7 +111,7 @@ ok1:
MOVQ $0, 56(SP) // errno
RET
-TEXT ·RawSyscall6(SB),7,$0
+TEXT ·RawSyscall6(SB),NOSPLIT,$0-88
MOVQ 16(SP), DI
MOVQ 24(SP), SI
MOVQ 32(SP), DX
diff --git a/src/pkg/syscall/asm_plan9_386.s b/src/pkg/syscall/asm_plan9_386.s
index 0ae20f568..7ebd20690 100644
--- a/src/pkg/syscall/asm_plan9_386.s
+++ b/src/pkg/syscall/asm_plan9_386.s
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
//
// System call support for 386, Plan 9
//
@@ -12,7 +14,7 @@
//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
+TEXT ·Syscall(SB),NOSPLIT,$0-32
CALL runtime·entersyscall(SB)
MOVL 4(SP), AX // syscall entry
// slide args down on top of system call number
@@ -47,7 +49,7 @@ copyresult3:
CALL runtime·exitsyscall(SB)
RET
-TEXT ·Syscall6(SB),7,$0
+TEXT ·Syscall6(SB),NOSPLIT,$0-44
CALL runtime·entersyscall(SB)
MOVL 4(SP), AX // syscall entry
// slide args down on top of system call number
@@ -85,7 +87,7 @@ copyresult4:
CALL runtime·exitsyscall(SB)
RET
-TEXT ·RawSyscall(SB),7,$0
+TEXT ·RawSyscall(SB),NOSPLIT,$0-32
MOVL 4(SP), AX // syscall entry
// slide args down on top of system call number
LEAL 8(SP), SI
@@ -100,7 +102,7 @@ TEXT ·RawSyscall(SB),7,$0
MOVL AX, err+28(SP)
RET
-TEXT ·RawSyscall6(SB),7,$0
+TEXT ·RawSyscall6(SB),NOSPLIT,$0-44
MOVL 4(SP), AX // syscall entry
// slide args down on top of system call number
LEAL 8(SP), SI
@@ -121,7 +123,7 @@ TEXT ·RawSyscall6(SB),7,$0
#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
+TEXT ·seek(SB),NOSPLIT,$0-40
LEAL newoffset+24(SP), AX
MOVL AX, placeholder+4(SP)
@@ -152,7 +154,7 @@ copyresult6:
//func exit(code int)
// Import runtime·exit for cleanly exiting.
-TEXT ·exit(SB),7,$4
+TEXT ·exit(SB),NOSPLIT,$4-4
MOVL code+0(FP), AX
MOVL AX, 0(SP)
CALL runtime·exit(SB)
diff --git a/src/pkg/syscall/asm_plan9_amd64.s b/src/pkg/syscall/asm_plan9_amd64.s
index 40cc12642..880bf7c6f 100644
--- a/src/pkg/syscall/asm_plan9_amd64.s
+++ b/src/pkg/syscall/asm_plan9_amd64.s
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "../../cmd/ld/textflag.h"
+
//
// System call support for Plan 9
//
@@ -14,7 +16,7 @@
// Trap # in BP, args on stack above caller pc.
// NxM requires that Plan 9 system calls be
// marked with $0x8000 in AX.
-TEXT ·Syscall(SB),7,$0
+TEXT ·Syscall(SB),NOSPLIT,$0-64
CALL runtime·entersyscall(SB)
MOVQ $0x8000, AX // for NxM
MOVQ 8(SP), BP // syscall entry
@@ -50,7 +52,7 @@ copyresult3:
CALL runtime·exitsyscall(SB)
RET
-TEXT ·Syscall6(SB),7,$0
+TEXT ·Syscall6(SB),NOSPLIT,$0-88
CALL runtime·entersyscall(SB)
MOVQ $0x8000, AX // for NxM
MOVQ 8(SP), BP // syscall entry
@@ -89,7 +91,7 @@ copyresult4:
CALL runtime·exitsyscall(SB)
RET
-TEXT ·RawSyscall(SB),7,$0
+TEXT ·RawSyscall(SB),NOSPLIT,$0-64
MOVQ $0x8000, AX // for NxM
MOVQ 8(SP), BP // syscall entry
// slide args down on top of system call number
@@ -105,7 +107,7 @@ TEXT ·RawSyscall(SB),7,$0
MOVQ AX, err+56(SP)
RET
-TEXT ·RawSyscall6(SB),7,$0
+TEXT ·RawSyscall6(SB),NOSPLIT,$0-88
MOVQ $0x8000, AX // for NxM
MOVQ 8(SP), BP // syscall entry
// slide args down on top of system call number
@@ -127,7 +129,7 @@ TEXT ·RawSyscall6(SB),7,$0
#define SYS_SEEK 39 /* from zsysnum_plan9_amd64.go */
//func seek(placeholder uintptr, fd int, offset int64, whence int) (newoffset int64, err string)
-TEXT ·seek(SB),7,$0
+TEXT ·seek(SB),NOSPLIT,$0-64
LEAQ newoffset+40(SP), AX
MOVQ AX, placeholder+8(SP)
@@ -158,7 +160,7 @@ copyresult6:
//func exit(code int)
// Import runtime·exit for cleanly exiting.
-TEXT ·exit(SB),7,$8
+TEXT ·exit(SB),NOSPLIT,$8-4
MOVQ code+0(FP), AX
MOVQ AX, 0(SP)
CALL runtime·exit(SB)
diff --git a/src/pkg/syscall/bpf_bsd.go b/src/pkg/syscall/bpf_bsd.go
index f98036c42..cc6c1e77c 100644
--- a/src/pkg/syscall/bpf_bsd.go
+++ b/src/pkg/syscall/bpf_bsd.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.
-// +build darwin freebsd netbsd openbsd
+// +build darwin dragonfly freebsd netbsd openbsd
// Berkeley packet filter for BSD variants
diff --git a/src/pkg/syscall/consistency_unix_test.go b/src/pkg/syscall/consistency_unix_test.go
new file mode 100644
index 000000000..73630bc61
--- /dev/null
+++ b/src/pkg/syscall/consistency_unix_test.go
@@ -0,0 +1,34 @@
+// Copyright 2013 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.
+
+// +build freebsd dragonfly darwin linux netbsd openbsd
+
+// This file tests that some basic syscalls are consistent across
+// all Unixes.
+
+package syscall_test
+
+import "syscall"
+
+// {Set,Get}priority and needed constants for them
+func _() {
+ var (
+ _ func(int, int, int) error = syscall.Setpriority
+ _ func(int, int) (int, error) = syscall.Getpriority
+ )
+ const (
+ _ int = syscall.PRIO_USER
+ _ int = syscall.PRIO_PROCESS
+ _ int = syscall.PRIO_PGRP
+ )
+}
+
+// termios functions and constants
+func _() {
+ const (
+ _ int = syscall.TCIFLUSH
+ _ int = syscall.TCIOFLUSH
+ _ int = syscall.TCOFLUSH
+ )
+}
diff --git a/src/pkg/syscall/env_unix.go b/src/pkg/syscall/env_unix.go
index 8573d79c7..5970df8fc 100644
--- a/src/pkg/syscall/env_unix.go
+++ b/src/pkg/syscall/env_unix.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.
-// +build darwin freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd
// Unix environment variables.
diff --git a/src/pkg/syscall/env_windows.go b/src/pkg/syscall/env_windows.go
index 39bd5022e..420b38724 100644
--- a/src/pkg/syscall/env_windows.go
+++ b/src/pkg/syscall/env_windows.go
@@ -28,20 +28,13 @@ func Getenv(key string) (value string, found bool) {
n = 0
}
}
- if n == 0 {
- return "", false
- }
return string(utf16.Decode(b[0:n])), true
}
func Setenv(key, value string) error {
- var v *uint16
- var err error
- if len(value) > 0 {
- v, err = UTF16PtrFromString(value)
- if err != nil {
- return err
- }
+ v, err := UTF16PtrFromString(value)
+ if err != nil {
+ return err
}
keyp, err := UTF16PtrFromString(key)
if err != nil {
diff --git a/src/pkg/syscall/exec_bsd.go b/src/pkg/syscall/exec_bsd.go
index 5d3d57813..ff78f197f 100644
--- a/src/pkg/syscall/exec_bsd.go
+++ b/src/pkg/syscall/exec_bsd.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.
-// +build darwin freebsd netbsd openbsd
+// +build darwin dragonfly freebsd netbsd openbsd
package syscall
@@ -21,12 +21,17 @@ type SysProcAttr struct {
Noctty bool // Detach fd 0 from controlling terminal
}
+// Implemented in runtime package.
+func runtime_BeforeFork()
+func runtime_AfterFork()
+
// Fork, dup fd onto 0..len(fd), and exec(argv0, argvv, envv) in child.
// If a dup or exec fails, write the errno error to pipe.
// (Pipe 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.
+// For the same reason compiler does not race instrument it.
// The calls to RawSyscall are okay because they are assembly
// functions that do not grow the stack.
func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) {
@@ -56,8 +61,10 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
// About to call fork.
// No more allocation or calls of non-assembly functions.
+ runtime_BeforeFork()
r1, r2, err1 = RawSyscall(SYS_FORK, 0, 0, 0)
if err1 != 0 {
+ runtime_AfterFork()
return 0, err1
}
@@ -71,6 +78,7 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
if r1 != 0 {
// parent; return PID
+ runtime_AfterFork()
return int(r1), 0
}
diff --git a/src/pkg/syscall/exec_linux.go b/src/pkg/syscall/exec_linux.go
index ddd946ed2..a1656e8dc 100644
--- a/src/pkg/syscall/exec_linux.go
+++ b/src/pkg/syscall/exec_linux.go
@@ -20,14 +20,20 @@ type SysProcAttr struct {
Noctty bool // Detach fd 0 from controlling terminal
Ctty int // Controlling TTY fd (Linux only)
Pdeathsig Signal // Signal that the process will get when its parent dies (Linux only)
+ Cloneflags uintptr // Flags for clone calls (Linux only)
}
+// Implemented in runtime package.
+func runtime_BeforeFork()
+func runtime_AfterFork()
+
// Fork, dup fd onto 0..len(fd), and exec(argv0, argvv, envv) in child.
// If a dup or exec fails, write the errno error to pipe.
// (Pipe 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.
+// For the same reason compiler does not race instrument it.
// The calls to RawSyscall are okay because they are assembly
// functions that do not grow the stack.
func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) {
@@ -55,13 +61,16 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
// About to call fork.
// No more allocation or calls of non-assembly functions.
- r1, _, err1 = RawSyscall(SYS_FORK, 0, 0, 0)
+ runtime_BeforeFork()
+ r1, _, err1 = RawSyscall6(SYS_CLONE, uintptr(SIGCHLD)|sys.Cloneflags, 0, 0, 0, 0, 0)
if err1 != 0 {
+ runtime_AfterFork()
return 0, err1
}
if r1 != 0 {
// parent; return PID
+ runtime_AfterFork()
return int(r1), 0
}
@@ -233,11 +242,6 @@ childerror:
for {
RawSyscall(SYS_EXIT, 253, 0, 0)
}
-
- // Calling panic is not actually safe,
- // but the for loop above won't break
- // and this shuts up the compiler.
- panic("unreached")
}
// Try to open a pipe with O_CLOEXEC set on both file descriptors.
diff --git a/src/pkg/syscall/exec_unix.go b/src/pkg/syscall/exec_unix.go
index 01b76c106..b82e39701 100644
--- a/src/pkg/syscall/exec_unix.go
+++ b/src/pkg/syscall/exec_unix.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.
-// +build darwin freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd
// Fork, exec, wait, etc.
diff --git a/src/pkg/syscall/mkall.sh b/src/pkg/syscall/mkall.sh
index 3900a93ab..a3139d603 100755
--- a/src/pkg/syscall/mkall.sh
+++ b/src/pkg/syscall/mkall.sh
@@ -121,6 +121,18 @@ darwin_amd64)
mksysnum="./mksysnum_darwin.pl /usr/include/sys/syscall.h"
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
;;
+dragonfly_386)
+ mkerrors="$mkerrors -m32"
+ mksyscall="./mksyscall.pl -l32 -dragonfly"
+ mksysnum="curl -s 'http://gitweb.dragonflybsd.org/dragonfly.git/blob_plain/HEAD:/sys/kern/syscalls.master' | ./mksysnum_dragonfly.pl"
+ mktypes="GOARCH=$GOARCH go tool cgo -godefs"
+ ;;
+dragonfly_amd64)
+ mkerrors="$mkerrors -m64"
+ mksyscall="./mksyscall.pl -dragonfly"
+ mksysnum="curl -s 'http://gitweb.dragonflybsd.org/dragonfly.git/blob_plain/HEAD:/sys/kern/syscalls.master' | ./mksysnum_dragonfly.pl"
+ mktypes="GOARCH=$GOARCH go tool cgo -godefs"
+ ;;
freebsd_386)
mkerrors="$mkerrors -m32"
mksyscall="./mksyscall.pl -l32"
@@ -157,7 +169,7 @@ linux_amd64)
linux_arm)
mkerrors="$mkerrors"
mksyscall="./mksyscall.pl -l32 -arm"
- mksysnum="curl -s 'http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob_plain;f=arch/arm/include/asm/unistd.h;hb=HEAD' | ./mksysnum_linux.pl"
+ mksysnum="curl -s 'http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/plain/arch/arm/include/uapi/asm/unistd.h' | ./mksysnum_linux.pl"
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
;;
netbsd_386)
@@ -218,7 +230,7 @@ esac
if [ -n "$mkerrors" ]; then echo "$mkerrors |gofmt >$zerrors"; fi
syscall_goos="syscall_$GOOS.go"
case "$GOOS" in
- darwin | freebsd | netbsd | openbsd)
+ darwin | dragonfly | freebsd | netbsd | openbsd)
syscall_goos="syscall_bsd.go $syscall_goos"
;;
windows)
diff --git a/src/pkg/syscall/mkerrors.sh b/src/pkg/syscall/mkerrors.sh
index 5a39d707b..20b2b9875 100755
--- a/src/pkg/syscall/mkerrors.sh
+++ b/src/pkg/syscall/mkerrors.sh
@@ -37,6 +37,25 @@ includes_Darwin='
#include <termios.h>
'
+includes_DragonFly='
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <net/bpf.h>
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <termios.h>
+#include <netinet/ip.h>
+#include <net/ip_mroute/ip_mroute.h>
+'
+
includes_FreeBSD='
#include <sys/types.h>
#include <sys/event.h>
@@ -71,6 +90,8 @@ includes_Linux='
#include <sys/prctl.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
#include <linux/if_addr.h>
#include <linux/if_ether.h>
#include <linux/if_tun.h>
@@ -79,11 +100,14 @@ includes_Linux='
#include <linux/reboot.h>
#include <linux/rtnetlink.h>
#include <linux/ptrace.h>
+#include <linux/sched.h>
#include <linux/wait.h>
+#include <linux/icmpv6.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <net/route.h>
#include <netpacket/packet.h>
+#include <termios.h>
#ifndef MSG_FASTOPEN
#define MSG_FASTOPEN 0x20000000
@@ -200,7 +224,8 @@ ccflags="$@"
$2 ~ /^O[CNPFP][A-Z]+[^_][A-Z]+$/ ||
$2 ~ /^IN_/ ||
$2 ~ /^LOCK_(SH|EX|NB|UN)$/ ||
- $2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|TCP|EVFILT|NOTE|EV|SHUT|PROT|MAP|PACKET|MSG|SCM|MCL|DT|MADV|PR)_/ ||
+ $2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|ICMP6|TCP|EVFILT|NOTE|EV|SHUT|PROT|MAP|PACKET|MSG|SCM|MCL|DT|MADV|PR)_/ ||
+ $2 == "ICMPV6_FILTER" ||
$2 == "SOMAXCONN" ||
$2 == "NAME_MAX" ||
$2 == "IFNAMSIZ" ||
@@ -219,6 +244,8 @@ ccflags="$@"
$2 ~ /^BIOC/ ||
$2 ~ /^RUSAGE_(SELF|CHILDREN|THREAD)/ ||
$2 ~ /^RLIMIT_(AS|CORE|CPU|DATA|FSIZE|NOFILE|STACK)|RLIM_INFINITY/ ||
+ $2 ~ /^PRIO_(PROCESS|PGRP|USER)/ ||
+ $2 ~ /^CLONE_[A-Z_]+/ ||
$2 !~ /^(BPF_TIMEVAL)$/ &&
$2 ~ /^(BPF|DLT)_/ ||
$2 !~ "WMESGLEN" &&
diff --git a/src/pkg/syscall/mksyscall.pl b/src/pkg/syscall/mksyscall.pl
index df47958ea..b4ece9a54 100755
--- a/src/pkg/syscall/mksyscall.pl
+++ b/src/pkg/syscall/mksyscall.pl
@@ -1,4 +1,4 @@
-#!/usr/bin/perl
+#!/usr/bin/env perl
# 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.
@@ -27,6 +27,7 @@ my $_32bit = "";
my $plan9 = 0;
my $openbsd = 0;
my $netbsd = 0;
+my $dragonfly = 0;
my $arm = 0; # 64-bit value should use (even, odd)-pair
if($ARGV[0] eq "-b32") {
@@ -48,6 +49,10 @@ if($ARGV[0] eq "-netbsd") {
$netbsd = 1;
shift;
}
+if($ARGV[0] eq "-dragonfly") {
+ $dragonfly = 1;
+ shift;
+}
if($ARGV[0] eq "-arm") {
$arm = 1;
shift;
@@ -159,6 +164,17 @@ while(<>) {
} else {
push @args, "uintptr($name)";
}
+ } elsif($type eq "int64" && $dragonfly) {
+ if ($func !~ /^extp(read|write)/i) {
+ push @args, "0";
+ }
+ if($_32bit eq "big-endian") {
+ push @args, "uintptr($name>>32)", "uintptr($name)";
+ } elsif($_32bit eq "little-endian") {
+ push @args, "uintptr($name)", "uintptr($name>>32)";
+ } else {
+ push @args, "uintptr($name)";
+ }
} elsif($type eq "int64" && $_32bit ne "") {
if(@args % 2 && $arm) {
# arm abi specifies 64-bit argument uses
diff --git a/src/pkg/syscall/mksyscall_windows.pl b/src/pkg/syscall/mksyscall_windows.pl
index 0e015cb70..65d6efc20 100755
--- a/src/pkg/syscall/mksyscall_windows.pl
+++ b/src/pkg/syscall/mksyscall_windows.pl
@@ -1,4 +1,4 @@
-#!/usr/bin/perl
+#!/usr/bin/env perl
# 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.
diff --git a/src/pkg/syscall/mksysctl_openbsd.pl b/src/pkg/syscall/mksysctl_openbsd.pl
index 8e5ccaac2..c2e2ea925 100755
--- a/src/pkg/syscall/mksysctl_openbsd.pl
+++ b/src/pkg/syscall/mksysctl_openbsd.pl
@@ -1,4 +1,4 @@
-#!/usr/bin/perl
+#!/usr/bin/env perl
# Copyright 2011 The Go Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
diff --git a/src/pkg/syscall/mksysnum_darwin.pl b/src/pkg/syscall/mksysnum_darwin.pl
index fd4375b2f..e3470435d 100755
--- a/src/pkg/syscall/mksysnum_darwin.pl
+++ b/src/pkg/syscall/mksysnum_darwin.pl
@@ -1,4 +1,4 @@
-#!/usr/bin/perl
+#!/usr/bin/env perl
# 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.
diff --git a/src/pkg/syscall/mksysnum_dragonfly.pl b/src/pkg/syscall/mksysnum_dragonfly.pl
new file mode 100755
index 000000000..769c29ea7
--- /dev/null
+++ b/src/pkg/syscall/mksysnum_dragonfly.pl
@@ -0,0 +1,43 @@
+#!/usr/bin/env perl
+# 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.
+#
+# Generate system call table for DragonFly from master list
+# (for example, /usr/src/sys/kern/syscalls.master).
+
+use strict;
+
+my $command = "mksysnum_dragonfly.pl " . join(' ', @ARGV);
+
+print <<EOF;
+// $command
+// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
+
+package syscall
+
+const (
+EOF
+
+while(<>){
+ if(/^([0-9]+)\s+STD\s+\S+\s+({ \S+\s+(\w+).*)$/){
+ my $num = $1;
+ my $proto = $2;
+ my $name = "SYS_$3";
+ $name =~ y/a-z/A-Z/;
+
+ # There are multiple entries for enosys and nosys, so comment them out.
+ if($name =~ /^SYS_E?NOSYS$/){
+ $name = "// $name";
+ }
+ if($name eq 'SYS_SYS_EXIT'){
+ $name = 'SYS_EXIT';
+ }
+
+ print " $name = $num; // $proto\n";
+ }
+}
+
+print <<EOF;
+)
+EOF
diff --git a/src/pkg/syscall/mksysnum_freebsd.pl b/src/pkg/syscall/mksysnum_freebsd.pl
index 54872b2f4..5c156338e 100755
--- a/src/pkg/syscall/mksysnum_freebsd.pl
+++ b/src/pkg/syscall/mksysnum_freebsd.pl
@@ -1,4 +1,4 @@
-#!/usr/bin/perl
+#!/usr/bin/env perl
# 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.
diff --git a/src/pkg/syscall/mksysnum_linux.pl b/src/pkg/syscall/mksysnum_linux.pl
index d11666a1d..c7e5cf73a 100755
--- a/src/pkg/syscall/mksysnum_linux.pl
+++ b/src/pkg/syscall/mksysnum_linux.pl
@@ -1,4 +1,4 @@
-#!/usr/bin/perl
+#!/usr/bin/env perl
# 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.
diff --git a/src/pkg/syscall/mksysnum_netbsd.pl b/src/pkg/syscall/mksysnum_netbsd.pl
index a300810ba..f1534ed58 100755
--- a/src/pkg/syscall/mksysnum_netbsd.pl
+++ b/src/pkg/syscall/mksysnum_netbsd.pl
@@ -1,4 +1,4 @@
-#!/usr/bin/perl
+#!/usr/bin/env perl
# 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.
diff --git a/src/pkg/syscall/mksysnum_openbsd.pl b/src/pkg/syscall/mksysnum_openbsd.pl
index e041888ea..ad1ccc12a 100755
--- a/src/pkg/syscall/mksysnum_openbsd.pl
+++ b/src/pkg/syscall/mksysnum_openbsd.pl
@@ -1,4 +1,4 @@
-#!/usr/bin/perl
+#!/usr/bin/env perl
# 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.
diff --git a/src/pkg/syscall/passfd_test.go b/src/pkg/syscall/passfd_test.go
index e16debae1..53c7a1ffa 100644
--- a/src/pkg/syscall/passfd_test.go
+++ b/src/pkg/syscall/passfd_test.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.
-// +build linux darwin freebsd netbsd openbsd
+// +build linux dragonfly darwin freebsd netbsd openbsd
package syscall_test
@@ -13,6 +13,7 @@ import (
"net"
"os"
"os/exec"
+ "runtime"
"syscall"
"testing"
"time"
@@ -26,6 +27,10 @@ import (
// "-test.run=^TestPassFD$" and an environment variable used to signal
// that the test should become the child process instead.
func TestPassFD(t *testing.T) {
+ if runtime.GOOS == "dragonfly" {
+ // TODO(jsing): Figure out why sendmsg is returning EINVAL.
+ t.Skip("Skipping test on dragonfly")
+ }
if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
passFDChild()
return
diff --git a/src/pkg/syscall/race.go b/src/pkg/syscall/race.go
index 81778846f..e69c1119a 100644
--- a/src/pkg/syscall/race.go
+++ b/src/pkg/syscall/race.go
@@ -20,3 +20,11 @@ func raceAcquire(addr unsafe.Pointer) {
func raceReleaseMerge(addr unsafe.Pointer) {
runtime.RaceReleaseMerge(addr)
}
+
+func raceReadRange(addr unsafe.Pointer, len int) {
+ runtime.RaceReadRange(addr, len)
+}
+
+func raceWriteRange(addr unsafe.Pointer, len int) {
+ runtime.RaceWriteRange(addr, len)
+}
diff --git a/src/pkg/syscall/race0.go b/src/pkg/syscall/race0.go
index e94fb47af..b02f882fd 100644
--- a/src/pkg/syscall/race0.go
+++ b/src/pkg/syscall/race0.go
@@ -17,3 +17,9 @@ func raceAcquire(addr unsafe.Pointer) {
func raceReleaseMerge(addr unsafe.Pointer) {
}
+
+func raceReadRange(addr unsafe.Pointer, len int) {
+}
+
+func raceWriteRange(addr unsafe.Pointer, len int) {
+}
diff --git a/src/pkg/syscall/route_bsd.go b/src/pkg/syscall/route_bsd.go
index 62c5ce1a3..638073592 100644
--- a/src/pkg/syscall/route_bsd.go
+++ b/src/pkg/syscall/route_bsd.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.
-// +build darwin freebsd netbsd openbsd
+// +build darwin dragonfly freebsd netbsd openbsd
// Routing sockets and messages
@@ -13,10 +13,14 @@ import "unsafe"
// Round the length of a raw sockaddr up to align it properly.
func rsaAlignOf(salen int) int {
salign := sizeofPtr
- // NOTE: It seems like 64-bit Darwin kernel still requires 32-bit
- // aligned access to BSD subsystem.
- if darwinAMD64 {
+ // NOTE: It seems like 64-bit Darwin kernel still requires
+ // 32-bit aligned access to BSD subsystem. Also NetBSD 6
+ // kernel and beyond require 64-bit aligned access to routing
+ // facilities.
+ if darwin64Bit {
salign = 4
+ } else if netbsd32Bit {
+ salign = 8
}
if salen == 0 {
return salign
@@ -142,6 +146,12 @@ func (m *InterfaceAddrMessage) sockaddr() (sas []Sockaddr) {
return nil
}
b := m.Data[:]
+ // We still see AF_UNSPEC in socket addresses on some
+ // platforms. To identify each address family correctly, we
+ // will use the address family of RTAX_NETMASK as a preferred
+ // one on the 32-bit NetBSD kernel, also use the length of
+ // RTAX_NETMASK socket address on the FreeBSD kernel.
+ preferredFamily := uint8(AF_UNSPEC)
for i := uint(0); i < RTAX_MAX; i++ {
if m.Header.Addrs&rtaIfaMask&(1<<i) == 0 {
continue
@@ -149,14 +159,29 @@ func (m *InterfaceAddrMessage) sockaddr() (sas []Sockaddr) {
rsa := (*RawSockaddr)(unsafe.Pointer(&b[0]))
switch i {
case RTAX_IFA:
+ if rsa.Family == AF_UNSPEC {
+ rsa.Family = preferredFamily
+ }
sa, err := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(rsa)))
if err != nil {
return nil
}
sas = append(sas, sa)
case RTAX_NETMASK:
- if rsa.Family == AF_UNSPEC {
- rsa.Family = AF_INET // an old fasion, AF_UNSPEC means AF_INET
+ switch rsa.Family {
+ case AF_UNSPEC:
+ switch rsa.Len {
+ case SizeofSockaddrInet4:
+ rsa.Family = AF_INET
+ case SizeofSockaddrInet6:
+ rsa.Family = AF_INET6
+ default:
+ rsa.Family = AF_INET // an old fashion, AF_UNSPEC means AF_INET
+ }
+ case AF_INET, AF_INET6:
+ preferredFamily = rsa.Family
+ default:
+ return nil
}
sa, err := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(rsa)))
if err != nil {
diff --git a/src/pkg/syscall/route_dragonfly.go b/src/pkg/syscall/route_dragonfly.go
new file mode 100644
index 000000000..acad7a2be
--- /dev/null
+++ b/src/pkg/syscall/route_dragonfly.go
@@ -0,0 +1,72 @@
+// 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.
+
+// Routing sockets and messages for Dragonfly
+
+package syscall
+
+import "unsafe"
+
+func (any *anyMessage) toRoutingMessage(b []byte) RoutingMessage {
+ switch any.Type {
+ case RTM_ADD, RTM_DELETE, RTM_CHANGE, RTM_GET, RTM_LOSING, RTM_REDIRECT, RTM_MISS, RTM_LOCK, RTM_RESOLVE:
+ p := (*RouteMessage)(unsafe.Pointer(any))
+ return &RouteMessage{Header: p.Header, Data: b[SizeofRtMsghdr:any.Msglen]}
+ case RTM_IFINFO:
+ p := (*InterfaceMessage)(unsafe.Pointer(any))
+ return &InterfaceMessage{Header: p.Header, Data: b[SizeofIfMsghdr:any.Msglen]}
+ case RTM_IFANNOUNCE:
+ p := (*InterfaceAnnounceMessage)(unsafe.Pointer(any))
+ return &InterfaceAnnounceMessage{Header: p.Header}
+ case RTM_NEWADDR, RTM_DELADDR:
+ p := (*InterfaceAddrMessage)(unsafe.Pointer(any))
+ return &InterfaceAddrMessage{Header: p.Header, Data: b[SizeofIfaMsghdr:any.Msglen]}
+ case RTM_NEWMADDR, RTM_DELMADDR:
+ p := (*InterfaceMulticastAddrMessage)(unsafe.Pointer(any))
+ return &InterfaceMulticastAddrMessage{Header: p.Header, Data: b[SizeofIfmaMsghdr:any.Msglen]}
+ }
+ return nil
+}
+
+// InterfaceAnnounceMessage represents a routing message containing
+// network interface arrival and depature information.
+type InterfaceAnnounceMessage struct {
+ Header IfAnnounceMsghdr
+}
+
+func (m *InterfaceAnnounceMessage) sockaddr() (sas []Sockaddr) { return nil }
+
+// InterfaceMulticastAddrMessage represents a routing message
+// containing network interface address entries.
+type InterfaceMulticastAddrMessage struct {
+ Header IfmaMsghdr
+ Data []byte
+}
+
+const rtaIfmaMask = RTA_GATEWAY | RTA_IFP | RTA_IFA
+
+func (m *InterfaceMulticastAddrMessage) sockaddr() (sas []Sockaddr) {
+ if m.Header.Addrs&rtaIfmaMask == 0 {
+ return nil
+ }
+ b := m.Data[:]
+ for i := uint(0); i < RTAX_MAX; i++ {
+ if m.Header.Addrs&rtaIfmaMask&(1<<i) == 0 {
+ continue
+ }
+ rsa := (*RawSockaddr)(unsafe.Pointer(&b[0]))
+ switch i {
+ case RTAX_IFA:
+ sa, e := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(rsa)))
+ if e != nil {
+ return nil
+ }
+ sas = append(sas, sa)
+ case RTAX_GATEWAY, RTAX_IFP:
+ // nothing to do
+ }
+ b = b[rsaAlignOf(int(rsa.Len)):]
+ }
+ return sas
+}
diff --git a/src/pkg/syscall/security_windows.go b/src/pkg/syscall/security_windows.go
index 017b27014..b22ecf578 100644
--- a/src/pkg/syscall/security_windows.go
+++ b/src/pkg/syscall/security_windows.go
@@ -58,6 +58,14 @@ func TranslateAccountName(username string, from, to uint32, initSize int) (strin
return UTF16ToString(b), nil
}
+const (
+ // do not reorder
+ NetSetupUnknownStatus = iota
+ NetSetupUnjoined
+ NetSetupWorkgroupName
+ NetSetupDomainName
+)
+
type UserInfo10 struct {
Name *uint16
Comment *uint16
@@ -66,6 +74,7 @@ type UserInfo10 struct {
}
//sys NetUserGetInfo(serverName *uint16, userName *uint16, level uint32, buf **byte) (neterr error) = netapi32.NetUserGetInfo
+//sys NetGetJoinInformation(server *uint16, name **uint16, bufType *uint32) (neterr error) = netapi32.NetGetJoinInformation
//sys NetApiBufferFree(buf *byte) (neterr error) = netapi32.NetApiBufferFree
const (
diff --git a/src/pkg/syscall/sockcmsg_unix.go b/src/pkg/syscall/sockcmsg_unix.go
index bc7cf2098..a2d234f21 100644
--- a/src/pkg/syscall/sockcmsg_unix.go
+++ b/src/pkg/syscall/sockcmsg_unix.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.
-// +build darwin freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd
// Socket control messages
@@ -15,7 +15,7 @@ func cmsgAlignOf(salen int) int {
salign := sizeofPtr
// NOTE: It seems like 64-bit Darwin kernel still requires 32-bit
// aligned access to BSD subsystem.
- if darwinAMD64 {
+ if darwin64Bit {
salign = 4
}
return (salen + salign - 1) & ^(salign - 1)
diff --git a/src/pkg/syscall/syscall_bsd.go b/src/pkg/syscall/syscall_bsd.go
index 560409a26..76b1f41b4 100644
--- a/src/pkg/syscall/syscall_bsd.go
+++ b/src/pkg/syscall/syscall_bsd.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.
-// +build darwin freebsd netbsd openbsd
+// +build darwin dragonfly freebsd netbsd openbsd
// BSD system call wrappers shared by *BSD based systems
// including OS X (Darwin) and FreeBSD. Like the other
@@ -18,17 +18,6 @@ import (
)
/*
- * Pseudo-system calls
- */
-
-// The const provides a compile-time constant so clients
-// can adjust to whether there is a working Getwd and avoid
-// even linking this function into the binary. See ../os/getwd.go.
-const ImplementsGetwd = false
-
-func Getwd() (string, error) { return "", ENOTSUP }
-
-/*
* Wrapped
*/
@@ -151,20 +140,6 @@ func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int,
//sysnb getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error)
//sys Shutdown(s int, how int) (err error)
-// For testing: clients can set this flag to force
-// creation of IPv6 sockets to return EAFNOSUPPORT.
-var SocketDisableIPv6 bool
-
-type Sockaddr interface {
- sockaddr() (ptr uintptr, len _Socklen, err error) // lowercase; only we can define Sockaddrs
-}
-
-type SockaddrInet4 struct {
- Port int
- Addr [4]byte
- raw RawSockaddrInet4
-}
-
func (sa *SockaddrInet4) sockaddr() (uintptr, _Socklen, error) {
if sa.Port < 0 || sa.Port > 0xFFFF {
return 0, 0, EINVAL
@@ -180,13 +155,6 @@ func (sa *SockaddrInet4) sockaddr() (uintptr, _Socklen, error) {
return uintptr(unsafe.Pointer(&sa.raw)), _Socklen(sa.raw.Len), nil
}
-type SockaddrInet6 struct {
- Port int
- ZoneId uint32
- Addr [16]byte
- raw RawSockaddrInet6
-}
-
func (sa *SockaddrInet6) sockaddr() (uintptr, _Socklen, error) {
if sa.Port < 0 || sa.Port > 0xFFFF {
return 0, 0, EINVAL
@@ -203,11 +171,6 @@ func (sa *SockaddrInet6) sockaddr() (uintptr, _Socklen, error) {
return uintptr(unsafe.Pointer(&sa.raw)), _Socklen(sa.raw.Len), nil
}
-type SockaddrUnix struct {
- Name string
- raw RawSockaddrUnix
-}
-
func (sa *SockaddrUnix) sockaddr() (uintptr, _Socklen, error) {
name := sa.Name
n := len(name)
@@ -328,58 +291,17 @@ func Getsockname(fd int) (sa Sockaddr, err error) {
return
}
// TODO(jsing): Remove after OpenBSD 5.4 is released (see issue 3349).
- if runtime.GOOS == "openbsd" && rsa.Addr.Family == AF_UNSPEC && rsa.Addr.Len == 0 {
+ // TODO(jsing): Apparently dragonfly has the same "bug", which should
+ // be reported upstream.
+ if (runtime.GOOS == "dragonfly" || runtime.GOOS == "openbsd") && rsa.Addr.Family == AF_UNSPEC && rsa.Addr.Len == 0 {
rsa.Addr.Family = AF_UNIX
rsa.Addr.Len = SizeofSockaddrUnix
}
return anyToSockaddr(&rsa)
}
-func Getpeername(fd int) (sa Sockaddr, err error) {
- var rsa RawSockaddrAny
- var len _Socklen = SizeofSockaddrAny
- if err = getpeername(fd, &rsa, &len); err != nil {
- return
- }
- return anyToSockaddr(&rsa)
-}
-
-func Bind(fd int, sa Sockaddr) (err error) {
- ptr, n, err := sa.sockaddr()
- if err != nil {
- return err
- }
- return bind(fd, ptr, n)
-}
-
-func Connect(fd int, sa Sockaddr) (err error) {
- ptr, n, err := sa.sockaddr()
- if err != nil {
- return err
- }
- return connect(fd, ptr, n)
-}
-
-func Socket(domain, typ, proto int) (fd int, err error) {
- if domain == AF_INET6 && SocketDisableIPv6 {
- return -1, EAFNOSUPPORT
- }
- fd, err = socket(domain, typ, proto)
- return
-}
-
//sysnb socketpair(domain int, typ int, proto int, fd *[2]int32) (err error)
-func Socketpair(domain, typ, proto int) (fd [2]int, err error) {
- var fdx [2]int32
- err = socketpair(domain, typ, proto, &fdx)
- if err == nil {
- fd[0] = int(fdx[0])
- fd[1] = int(fdx[1])
- }
- return
-}
-
func GetsockoptByte(fd, level, opt int) (value byte, err error) {
var n byte
vallen := _Socklen(1)
@@ -387,13 +309,6 @@ func GetsockoptByte(fd, level, opt int) (value byte, err error) {
return n, err
}
-func GetsockoptInt(fd, level, opt int) (value int, err error) {
- var n int32
- vallen := _Socklen(4)
- err = getsockopt(fd, level, opt, uintptr(unsafe.Pointer(&n)), &vallen)
- return int(n), err
-}
-
func GetsockoptInet4Addr(fd, level, opt int) (value [4]byte, err error) {
vallen := _Socklen(4)
err = getsockopt(fd, level, opt, uintptr(unsafe.Pointer(&value[0])), &vallen)
@@ -414,64 +329,22 @@ func GetsockoptIPv6Mreq(fd, level, opt int) (*IPv6Mreq, error) {
return &value, err
}
-func SetsockoptByte(fd, level, opt int, value byte) (err error) {
- var n = byte(value)
- return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(&n)), 1)
-}
-
-func SetsockoptInt(fd, level, opt int, value int) (err error) {
- var n = int32(value)
- return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(&n)), 4)
-}
-
-func SetsockoptInet4Addr(fd, level, opt int, value [4]byte) (err error) {
- return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(&value[0])), 4)
-}
-
-func SetsockoptTimeval(fd, level, opt int, tv *Timeval) (err error) {
- return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(tv)), unsafe.Sizeof(*tv))
-}
-
-func SetsockoptLinger(fd, level, opt int, l *Linger) (err error) {
- return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(l)), unsafe.Sizeof(*l))
-}
-
-func SetsockoptIPMreq(fd, level, opt int, mreq *IPMreq) (err error) {
- return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(mreq)), unsafe.Sizeof(*mreq))
-}
-
-func SetsockoptIPv6Mreq(fd, level, opt int, mreq *IPv6Mreq) (err error) {
- return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(mreq)), unsafe.Sizeof(*mreq))
-}
-
-func SetsockoptString(fd, level, opt int, s string) (err error) {
- return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(&[]byte(s)[0])), uintptr(len(s)))
-}
-
-//sys recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (n int, err error)
-
-func Recvfrom(fd int, p []byte, flags int) (n int, from Sockaddr, err error) {
- var rsa RawSockaddrAny
- var len _Socklen = SizeofSockaddrAny
- if n, err = recvfrom(fd, p, flags, &rsa, &len); err != nil {
- return
- }
- if rsa.Addr.Family != AF_UNSPEC {
- from, err = anyToSockaddr(&rsa)
- }
- return
+func GetsockoptIPv6MTUInfo(fd, level, opt int) (*IPv6MTUInfo, error) {
+ var value IPv6MTUInfo
+ vallen := _Socklen(SizeofIPv6MTUInfo)
+ err := getsockopt(fd, level, opt, uintptr(unsafe.Pointer(&value)), &vallen)
+ return &value, err
}
-//sys sendto(s int, buf []byte, flags int, to uintptr, addrlen _Socklen) (err error)
-
-func Sendto(fd int, p []byte, flags int, to Sockaddr) (err error) {
- ptr, n, err := to.sockaddr()
- if err != nil {
- return err
- }
- return sendto(fd, p, flags, ptr, n)
+func GetsockoptICMPv6Filter(fd, level, opt int) (*ICMPv6Filter, error) {
+ var value ICMPv6Filter
+ vallen := _Socklen(SizeofICMPv6Filter)
+ err := getsockopt(fd, level, opt, uintptr(unsafe.Pointer(&value)), &vallen)
+ return &value, err
}
+//sys recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (n int, err error)
+//sys sendto(s int, buf []byte, flags int, to uintptr, addrlen _Socklen) (err error)
//sys recvmsg(s int, msg *Msghdr, flags int) (n int, err error)
func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) {
@@ -514,10 +387,9 @@ func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error) {
var ptr uintptr
var salen _Socklen
if to != nil {
- var err error
ptr, salen, err = to.sockaddr()
if err != nil {
- return err
+ return
}
}
var msg Msghdr
diff --git a/src/pkg/syscall/syscall_darwin.go b/src/pkg/syscall/syscall_darwin.go
index 75ef8f0a9..bd929ff99 100644
--- a/src/pkg/syscall/syscall_darwin.go
+++ b/src/pkg/syscall/syscall_darwin.go
@@ -12,7 +12,28 @@
package syscall
-import "unsafe"
+import (
+ errorspkg "errors"
+ "unsafe"
+)
+
+const ImplementsGetwd = true
+
+func Getwd() (string, error) {
+ buf := make([]byte, 2048)
+ attrs, err := getAttrList(".", attrList{CommonAttr: attrCmnFullpath}, buf, 0)
+ if err == nil && len(attrs) == 1 && len(attrs[0]) >= 2 {
+ wd := string(attrs[0])
+ // Sanity check that it's an absolute path and ends
+ // in a null byte, which we then strip.
+ if wd[0] == '/' && wd[len(wd)-1] == 0 {
+ return wd[:len(wd)-1], nil
+ }
+ }
+ // If pkg/os/getwd.go gets ENOTSUP, it will fall back to the
+ // slow algorithm.
+ return "", ENOTSUP
+}
type SockaddrDatalink struct {
Len uint8
@@ -86,6 +107,76 @@ func ParseDirent(buf []byte, max int, names []string) (consumed int, count int,
func PtraceAttach(pid int) (err error) { return ptrace(PT_ATTACH, pid, 0, 0) }
func PtraceDetach(pid int) (err error) { return ptrace(PT_DETACH, pid, 0, 0) }
+const (
+ attrBitMapCount = 5
+ attrCmnFullpath = 0x08000000
+)
+
+type attrList struct {
+ bitmapCount uint16
+ _ uint16
+ CommonAttr uint32
+ VolAttr uint32
+ DirAttr uint32
+ FileAttr uint32
+ Forkattr uint32
+}
+
+func getAttrList(path string, attrList attrList, attrBuf []byte, options uint) (attrs [][]byte, err error) {
+ if len(attrBuf) < 4 {
+ return nil, errorspkg.New("attrBuf too small")
+ }
+ attrList.bitmapCount = attrBitMapCount
+
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return nil, err
+ }
+
+ _, _, e1 := Syscall6(
+ SYS_GETATTRLIST,
+ uintptr(unsafe.Pointer(_p0)),
+ uintptr(unsafe.Pointer(&attrList)),
+ uintptr(unsafe.Pointer(&attrBuf[0])),
+ uintptr(len(attrBuf)),
+ uintptr(options),
+ 0,
+ )
+ if e1 != 0 {
+ return nil, e1
+ }
+ size := *(*uint32)(unsafe.Pointer(&attrBuf[0]))
+
+ // dat is the section of attrBuf that contains valid data,
+ // without the 4 byte length header. All attribute offsets
+ // are relative to dat.
+ dat := attrBuf
+ if int(size) < len(attrBuf) {
+ dat = dat[:size]
+ }
+ dat = dat[4:] // remove length prefix
+
+ for i := uint32(0); int(i) < len(dat); {
+ header := dat[i:]
+ if len(header) < 8 {
+ return attrs, errorspkg.New("truncated attribute header")
+ }
+ datOff := *(*int32)(unsafe.Pointer(&header[0]))
+ attrLen := *(*uint32)(unsafe.Pointer(&header[4]))
+ if datOff < 0 || uint32(datOff)+attrLen > uint32(len(dat)) {
+ return attrs, errorspkg.New("truncated results; attrBuf too small")
+ }
+ end := uint32(datOff) + attrLen
+ attrs = append(attrs, dat[datOff:end])
+ i = end
+ if r := i % 4; r != 0 {
+ i += (4 - r)
+ }
+ }
+ return
+}
+
//sysnb pipe() (r int, w int, err error)
func Pipe(p []int) (err error) {
@@ -96,11 +187,6 @@ func Pipe(p []int) (err error) {
return
}
-// TODO
-func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
- return -1, ENOSYS
-}
-
/*
* Wrapped
*/
diff --git a/src/pkg/syscall/syscall_darwin_386.go b/src/pkg/syscall/syscall_darwin_386.go
index 9a8a97de5..2074e7ac2 100644
--- a/src/pkg/syscall/syscall_darwin_386.go
+++ b/src/pkg/syscall/syscall_darwin_386.go
@@ -4,6 +4,8 @@
package syscall
+import "unsafe"
+
func Getpagesize() int { return 4096 }
func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }
@@ -52,4 +54,17 @@ func (cmsg *Cmsghdr) SetLen(length int) {
cmsg.Len = uint32(length)
}
+func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
+ var length = uint64(count)
+
+ _, _, e1 := Syscall9(SYS_SENDFILE, uintptr(infd), uintptr(outfd), uintptr(*offset), uintptr(*offset>>32), uintptr(unsafe.Pointer(&length)), 0, 0, 0, 0)
+
+ written = int(length)
+
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno) // sic
diff --git a/src/pkg/syscall/syscall_darwin_amd64.go b/src/pkg/syscall/syscall_darwin_amd64.go
index 2d25e591e..81b1fd3d2 100644
--- a/src/pkg/syscall/syscall_darwin_amd64.go
+++ b/src/pkg/syscall/syscall_darwin_amd64.go
@@ -4,6 +4,8 @@
package syscall
+import "unsafe"
+
func Getpagesize() int { return 4096 }
func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }
@@ -51,3 +53,18 @@ func (msghdr *Msghdr) SetControllen(length int) {
func (cmsg *Cmsghdr) SetLen(length int) {
cmsg.Len = uint32(length)
}
+
+func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
+ var length = uint64(count)
+
+ _, _, e1 := Syscall6(SYS_SENDFILE, uintptr(infd), uintptr(outfd), uintptr(*offset), uintptr(unsafe.Pointer(&length)), 0, 0)
+
+ written = int(length)
+
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno) // sic
diff --git a/src/pkg/syscall/syscall_dragonfly.go b/src/pkg/syscall/syscall_dragonfly.go
new file mode 100644
index 000000000..e19a9cee3
--- /dev/null
+++ b/src/pkg/syscall/syscall_dragonfly.go
@@ -0,0 +1,394 @@
+// Copyright 2009,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.
+
+// FreeBSD 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, either here or in
+// syscall_bsd.go or syscall_unix.go.
+
+package syscall
+
+import "unsafe"
+
+type SockaddrDatalink struct {
+ Len uint8
+ Family uint8
+ Index uint16
+ Type uint8
+ Nlen uint8
+ Alen uint8
+ Slen uint8
+ Data [12]int8
+ Rcf uint16
+ Route [16]uint16
+ raw RawSockaddrDatalink
+}
+
+// Translate "kern.hostname" to []_C_int{0,1,2,3}.
+func nametomib(name string) (mib []_C_int, err error) {
+ const siz = unsafe.Sizeof(mib[0])
+
+ // NOTE(rsc): It seems strange to set the buffer to have
+ // size CTL_MAXNAME+2 but use only CTL_MAXNAME
+ // as the size. I don't know why the +2 is here, but the
+ // kernel uses +2 for its own implementation of this function.
+ // I am scared that if we don't include the +2 here, the kernel
+ // will silently write 2 words farther than we specify
+ // and we'll get memory corruption.
+ var buf [CTL_MAXNAME + 2]_C_int
+ n := uintptr(CTL_MAXNAME) * siz
+
+ p := (*byte)(unsafe.Pointer(&buf[0]))
+ bytes, err := ByteSliceFromString(name)
+ if err != nil {
+ return nil, err
+ }
+
+ // Magic sysctl: "setting" 0.3 to a string name
+ // lets you read back the array of integers form.
+ if err = sysctl([]_C_int{0, 3}, p, &n, &bytes[0], uintptr(len(name))); err != nil {
+ return nil, err
+ }
+ return buf[0 : n/siz], nil
+}
+
+// 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]))
+ reclen := int(16+dirent.Namlen+1+7) & ^7
+ buf = buf[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
+}
+
+//sysnb pipe() (r int, w int, err error)
+
+func Pipe(p []int) (err error) {
+ if len(p) != 2 {
+ return EINVAL
+ }
+ p[0], p[1], err = pipe()
+ return
+}
+
+//sys extpread(fd int, p []byte, flags int, offset int64) (n int, err error)
+func Pread(fd int, p []byte, offset int64) (n int, err error) {
+ return extpread(fd, p, 0, offset)
+}
+
+//sys extpwrite(fd int, p []byte, flags int, offset int64) (n int, err error)
+func Pwrite(fd int, p []byte, offset int64) (n int, err error) {
+ return extpwrite(fd, p, 0, offset)
+}
+
+/*
+ * Exposed directly
+ */
+//sys Access(path string, mode uint32) (err error)
+//sys Adjtime(delta *Timeval, olddelta *Timeval) (err error)
+//sys Chdir(path string) (err error)
+//sys Chflags(path string, flags int) (err error)
+//sys Chmod(path string, mode uint32) (err error)
+//sys Chown(path string, uid int, gid int) (err error)
+//sys Chroot(path string) (err error)
+//sys Close(fd int) (err error)
+//sysnb Dup(fd int) (nfd int, err error)
+//sysnb Dup2(from int, to int) (err error)
+//sys Exit(code int)
+//sys Fchdir(fd int) (err error)
+//sys Fchflags(fd int, flags int) (err error)
+//sys Fchmod(fd int, mode uint32) (err error)
+//sys Fchown(fd int, uid int, gid int) (err error)
+//sys Flock(fd int, how int) (err error)
+//sys Fpathconf(fd int, name int) (val int, err error)
+//sys Fstat(fd int, stat *Stat_t) (err error)
+//sys Fstatfs(fd int, stat *Statfs_t) (err error)
+//sys Fsync(fd int) (err error)
+//sys Ftruncate(fd int, length int64) (err error)
+//sys Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error)
+//sys Getdtablesize() (size int)
+//sysnb Getegid() (egid int)
+//sysnb Geteuid() (uid int)
+//sys Getfsstat(buf []Statfs_t, flags int) (n int, err error)
+//sysnb Getgid() (gid int)
+//sysnb Getpgid(pid int) (pgid int, err error)
+//sysnb Getpgrp() (pgrp int)
+//sysnb Getpid() (pid int)
+//sysnb Getppid() (ppid int)
+//sys Getpriority(which int, who int) (prio int, err error)
+//sysnb Getrlimit(which int, lim *Rlimit) (err error)
+//sysnb Getrusage(who int, rusage *Rusage) (err error)
+//sysnb Getsid(pid int) (sid int, err error)
+//sysnb Gettimeofday(tv *Timeval) (err error)
+//sysnb Getuid() (uid int)
+//sys Issetugid() (tainted bool)
+//sys Kill(pid int, signum Signal) (err error)
+//sys Kqueue() (fd int, err error)
+//sys Lchown(path string, uid int, gid int) (err error)
+//sys Link(path string, link string) (err error)
+//sys Listen(s int, backlog int) (err error)
+//sys Lstat(path string, stat *Stat_t) (err error)
+//sys Mkdir(path string, mode uint32) (err error)
+//sys Mkfifo(path string, mode uint32) (err error)
+//sys Mknod(path string, mode uint32, dev int) (err error)
+//sys Nanosleep(time *Timespec, leftover *Timespec) (err error)
+//sys Open(path string, mode int, perm uint32) (fd int, err error)
+//sys Pathconf(path string, name int) (val int, err error)
+//sys read(fd int, p []byte) (n int, err error)
+//sys Readlink(path string, buf []byte) (n int, err error)
+//sys Rename(from string, to string) (err error)
+//sys Revoke(path string) (err error)
+//sys Rmdir(path string) (err error)
+//sys Seek(fd int, offset int64, whence int) (newoffset int64, err error) = SYS_LSEEK
+//sys Select(n int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (err error)
+//sysnb Setegid(egid int) (err error)
+//sysnb Seteuid(euid int) (err error)
+//sysnb Setgid(gid int) (err error)
+//sys Setlogin(name string) (err error)
+//sysnb Setpgid(pid int, pgid int) (err error)
+//sys Setpriority(which int, who int, prio int) (err error)
+//sysnb Setregid(rgid int, egid int) (err error)
+//sysnb Setreuid(ruid int, euid int) (err error)
+//sysnb Setrlimit(which int, lim *Rlimit) (err error)
+//sysnb Setsid() (pid int, err error)
+//sysnb Settimeofday(tp *Timeval) (err error)
+//sysnb Setuid(uid int) (err error)
+//sys Stat(path string, stat *Stat_t) (err error)
+//sys Statfs(path string, stat *Statfs_t) (err error)
+//sys Symlink(path string, link string) (err error)
+//sys Sync() (err error)
+//sys Truncate(path string, length int64) (err error)
+//sys Umask(newmask int) (oldmask int)
+//sys Undelete(path string) (err error)
+//sys Unlink(path string) (err error)
+//sys Unmount(path string, flags int) (err error)
+//sys write(fd int, p []byte) (n int, err error)
+//sys mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, err error)
+//sys munmap(addr uintptr, length uintptr) (err error)
+//sys readlen(fd int, buf *byte, nbuf int) (n int, err error) = SYS_READ
+//sys writelen(fd int, buf *byte, nbuf int) (n int, err error) = SYS_WRITE
+
+/*
+ * Unimplemented
+ * TODO(jsing): Update this list for DragonFly.
+ */
+// Profil
+// Sigaction
+// Sigprocmask
+// Getlogin
+// Sigpending
+// Sigaltstack
+// Ioctl
+// Reboot
+// Execve
+// Vfork
+// Sbrk
+// Sstk
+// Ovadvise
+// Mincore
+// Setitimer
+// Swapon
+// Select
+// Sigsuspend
+// Readv
+// Writev
+// Nfssvc
+// Getfh
+// Quotactl
+// Mount
+// Csops
+// Waitid
+// Add_profil
+// Kdebug_trace
+// Sigreturn
+// Mmap
+// Mlock
+// Munlock
+// Atsocket
+// Kqueue_from_portset_np
+// Kqueue_portset
+// Getattrlist
+// Setattrlist
+// Getdirentriesattr
+// Searchfs
+// Delete
+// Copyfile
+// Poll
+// Watchevent
+// Waitevent
+// Modwatch
+// Getxattr
+// Fgetxattr
+// Setxattr
+// Fsetxattr
+// Removexattr
+// Fremovexattr
+// Listxattr
+// Flistxattr
+// Fsctl
+// Initgroups
+// Posix_spawn
+// Nfsclnt
+// Fhopen
+// Minherit
+// Semsys
+// Msgsys
+// Shmsys
+// Semctl
+// Semget
+// Semop
+// Msgctl
+// Msgget
+// Msgsnd
+// Msgrcv
+// Shmat
+// Shmctl
+// Shmdt
+// Shmget
+// Shm_open
+// Shm_unlink
+// Sem_open
+// Sem_close
+// Sem_unlink
+// Sem_wait
+// Sem_trywait
+// Sem_post
+// Sem_getvalue
+// Sem_init
+// Sem_destroy
+// Open_extended
+// Umask_extended
+// Stat_extended
+// Lstat_extended
+// Fstat_extended
+// Chmod_extended
+// Fchmod_extended
+// Access_extended
+// Settid
+// Gettid
+// Setsgroups
+// Getsgroups
+// Setwgroups
+// Getwgroups
+// Mkfifo_extended
+// Mkdir_extended
+// Identitysvc
+// Shared_region_check_np
+// Shared_region_map_np
+// __pthread_mutex_destroy
+// __pthread_mutex_init
+// __pthread_mutex_lock
+// __pthread_mutex_trylock
+// __pthread_mutex_unlock
+// __pthread_cond_init
+// __pthread_cond_destroy
+// __pthread_cond_broadcast
+// __pthread_cond_signal
+// Setsid_with_pid
+// __pthread_cond_timedwait
+// Aio_fsync
+// Aio_return
+// Aio_suspend
+// Aio_cancel
+// Aio_error
+// Aio_read
+// Aio_write
+// Lio_listio
+// __pthread_cond_wait
+// Iopolicysys
+// Mlockall
+// Munlockall
+// __pthread_kill
+// __pthread_sigmask
+// __sigwait
+// __disable_threadsignal
+// __pthread_markcancel
+// __pthread_canceled
+// __semwait_signal
+// Proc_info
+// Stat64_extended
+// Lstat64_extended
+// Fstat64_extended
+// __pthread_chdir
+// __pthread_fchdir
+// Audit
+// Auditon
+// Getauid
+// Setauid
+// Getaudit
+// Setaudit
+// Getaudit_addr
+// Setaudit_addr
+// Auditctl
+// Bsdthread_create
+// Bsdthread_terminate
+// Stack_snapshot
+// Bsdthread_register
+// Workq_open
+// Workq_ops
+// __mac_execve
+// __mac_syscall
+// __mac_get_file
+// __mac_set_file
+// __mac_get_link
+// __mac_set_link
+// __mac_get_proc
+// __mac_set_proc
+// __mac_get_fd
+// __mac_set_fd
+// __mac_get_pid
+// __mac_get_lcid
+// __mac_get_lctx
+// __mac_set_lctx
+// Setlcid
+// Read_nocancel
+// Write_nocancel
+// Open_nocancel
+// Close_nocancel
+// Wait4_nocancel
+// Recvmsg_nocancel
+// Sendmsg_nocancel
+// Recvfrom_nocancel
+// Accept_nocancel
+// Msync_nocancel
+// Fcntl_nocancel
+// Select_nocancel
+// Fsync_nocancel
+// Connect_nocancel
+// Sigsuspend_nocancel
+// Readv_nocancel
+// Writev_nocancel
+// Sendto_nocancel
+// Pread_nocancel
+// Pwrite_nocancel
+// Waitid_nocancel
+// Poll_nocancel
+// Msgsnd_nocancel
+// Msgrcv_nocancel
+// Sem_wait_nocancel
+// Aio_suspend_nocancel
+// __sigwait_nocancel
+// __semwait_signal_nocancel
+// __mac_mount
+// __mac_get_mount
+// __mac_getfsstat
diff --git a/src/pkg/syscall/syscall_dragonfly_386.go b/src/pkg/syscall/syscall_dragonfly_386.go
new file mode 100644
index 000000000..ebd3d4c9c
--- /dev/null
+++ b/src/pkg/syscall/syscall_dragonfly_386.go
@@ -0,0 +1,58 @@
+// 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
+
+import "unsafe"
+
+func Getpagesize() int { return 4096 }
+
+func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }
+
+func NsecToTimespec(nsec int64) (ts Timespec) {
+ ts.Sec = int32(nsec / 1e9)
+ ts.Nsec = int32(nsec % 1e9)
+ return
+}
+
+func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3 }
+
+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 SetKevent(k *Kevent_t, fd, mode, flags int) {
+ k.Ident = uint32(fd)
+ k.Filter = int16(mode)
+ k.Flags = uint16(flags)
+}
+
+func (iov *Iovec) SetLen(length int) {
+ iov.Len = uint32(length)
+}
+
+func (msghdr *Msghdr) SetControllen(length int) {
+ msghdr.Controllen = uint32(length)
+}
+
+func (cmsg *Cmsghdr) SetLen(length int) {
+ cmsg.Len = uint32(length)
+}
+
+func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
+ var writtenOut uint64 = 0
+ _, _, e1 := Syscall9(SYS_SENDFILE, uintptr(infd), uintptr(outfd), uintptr(*offset), uintptr((*offset)>>32), uintptr(count), 0, uintptr(unsafe.Pointer(&writtenOut)), 0, 0)
+
+ written = int(writtenOut)
+
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno) // sic
diff --git a/src/pkg/syscall/syscall_dragonfly_amd64.go b/src/pkg/syscall/syscall_dragonfly_amd64.go
new file mode 100644
index 000000000..70c2ffb03
--- /dev/null
+++ b/src/pkg/syscall/syscall_dragonfly_amd64.go
@@ -0,0 +1,58 @@
+// 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
+
+import "unsafe"
+
+func Getpagesize() int { return 4096 }
+
+func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }
+
+func NsecToTimespec(nsec int64) (ts Timespec) {
+ ts.Sec = nsec / 1e9
+ ts.Nsec = nsec % 1e9
+ return
+}
+
+func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3 }
+
+func NsecToTimeval(nsec int64) (tv Timeval) {
+ nsec += 999 // round up to microsecond
+ tv.Usec = nsec % 1e9 / 1e3
+ tv.Sec = int64(nsec / 1e9)
+ return
+}
+
+func SetKevent(k *Kevent_t, fd, mode, flags int) {
+ k.Ident = uint64(fd)
+ k.Filter = int16(mode)
+ k.Flags = uint16(flags)
+}
+
+func (iov *Iovec) SetLen(length int) {
+ iov.Len = uint64(length)
+}
+
+func (msghdr *Msghdr) SetControllen(length int) {
+ msghdr.Controllen = uint32(length)
+}
+
+func (cmsg *Cmsghdr) SetLen(length int) {
+ cmsg.Len = uint32(length)
+}
+
+func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
+ var writtenOut uint64 = 0
+ _, _, e1 := Syscall9(SYS_SENDFILE, uintptr(infd), uintptr(outfd), uintptr(*offset), uintptr(count), 0, uintptr(unsafe.Pointer(&writtenOut)), 0, 0, 0)
+
+ written = int(writtenOut)
+
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno)
diff --git a/src/pkg/syscall/syscall_linux.go b/src/pkg/syscall/syscall_linux.go
index 4f458be73..79c1fda68 100644
--- a/src/pkg/syscall/syscall_linux.go
+++ b/src/pkg/syscall/syscall_linux.go
@@ -230,20 +230,6 @@ func Mkfifo(path string, mode uint32) (err error) {
return Mknod(path, mode|S_IFIFO, 0)
}
-// For testing: clients can set this flag to force
-// creation of IPv6 sockets to return EAFNOSUPPORT.
-var SocketDisableIPv6 bool
-
-type Sockaddr interface {
- sockaddr() (ptr uintptr, len _Socklen, err error) // lowercase; only we can define Sockaddrs
-}
-
-type SockaddrInet4 struct {
- Port int
- Addr [4]byte
- raw RawSockaddrInet4
-}
-
func (sa *SockaddrInet4) sockaddr() (uintptr, _Socklen, error) {
if sa.Port < 0 || sa.Port > 0xFFFF {
return 0, 0, EINVAL
@@ -258,13 +244,6 @@ func (sa *SockaddrInet4) sockaddr() (uintptr, _Socklen, error) {
return uintptr(unsafe.Pointer(&sa.raw)), SizeofSockaddrInet4, nil
}
-type SockaddrInet6 struct {
- Port int
- ZoneId uint32
- Addr [16]byte
- raw RawSockaddrInet6
-}
-
func (sa *SockaddrInet6) sockaddr() (uintptr, _Socklen, error) {
if sa.Port < 0 || sa.Port > 0xFFFF {
return 0, 0, EINVAL
@@ -280,11 +259,6 @@ func (sa *SockaddrInet6) sockaddr() (uintptr, _Socklen, error) {
return uintptr(unsafe.Pointer(&sa.raw)), SizeofSockaddrInet6, nil
}
-type SockaddrUnix struct {
- Name string
- raw RawSockaddrUnix
-}
-
func (sa *SockaddrUnix) sockaddr() (uintptr, _Socklen, error) {
name := sa.Name
n := len(name)
@@ -463,56 +437,6 @@ func Getsockname(fd int) (sa Sockaddr, err error) {
return anyToSockaddr(&rsa)
}
-func Getpeername(fd int) (sa Sockaddr, err error) {
- var rsa RawSockaddrAny
- var len _Socklen = SizeofSockaddrAny
- if err = getpeername(fd, &rsa, &len); err != nil {
- return
- }
- return anyToSockaddr(&rsa)
-}
-
-func Bind(fd int, sa Sockaddr) (err error) {
- ptr, n, err := sa.sockaddr()
- if err != nil {
- return err
- }
- return bind(fd, ptr, n)
-}
-
-func Connect(fd int, sa Sockaddr) (err error) {
- ptr, n, err := sa.sockaddr()
- if err != nil {
- return err
- }
- return connect(fd, ptr, n)
-}
-
-func Socket(domain, typ, proto int) (fd int, err error) {
- if domain == AF_INET6 && SocketDisableIPv6 {
- return -1, EAFNOSUPPORT
- }
- fd, err = socket(domain, typ, proto)
- return
-}
-
-func Socketpair(domain, typ, proto int) (fd [2]int, err error) {
- var fdx [2]int32
- err = socketpair(domain, typ, proto, &fdx)
- if err == nil {
- fd[0] = int(fdx[0])
- fd[1] = int(fdx[1])
- }
- return
-}
-
-func GetsockoptInt(fd, level, opt int) (value int, err error) {
- var n int32
- vallen := _Socklen(4)
- err = getsockopt(fd, level, opt, uintptr(unsafe.Pointer(&n)), &vallen)
- return int(n), err
-}
-
func GetsockoptInet4Addr(fd, level, opt int) (value [4]byte, err error) {
vallen := _Socklen(4)
err = getsockopt(fd, level, opt, uintptr(unsafe.Pointer(&value[0])), &vallen)
@@ -540,66 +464,31 @@ func GetsockoptIPv6Mreq(fd, level, opt int) (*IPv6Mreq, error) {
return &value, err
}
-func GetsockoptUcred(fd, level, opt int) (*Ucred, error) {
- var value Ucred
- vallen := _Socklen(SizeofUcred)
+func GetsockoptIPv6MTUInfo(fd, level, opt int) (*IPv6MTUInfo, error) {
+ var value IPv6MTUInfo
+ vallen := _Socklen(SizeofIPv6MTUInfo)
err := getsockopt(fd, level, opt, uintptr(unsafe.Pointer(&value)), &vallen)
return &value, err
}
-func SetsockoptInt(fd, level, opt int, value int) (err error) {
- var n = int32(value)
- return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(&n)), 4)
-}
-
-func SetsockoptInet4Addr(fd, level, opt int, value [4]byte) (err error) {
- return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(&value[0])), 4)
-}
-
-func SetsockoptTimeval(fd, level, opt int, tv *Timeval) (err error) {
- return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(tv)), unsafe.Sizeof(*tv))
-}
-
-func SetsockoptLinger(fd, level, opt int, l *Linger) (err error) {
- return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(l)), unsafe.Sizeof(*l))
+func GetsockoptICMPv6Filter(fd, level, opt int) (*ICMPv6Filter, error) {
+ var value ICMPv6Filter
+ vallen := _Socklen(SizeofICMPv6Filter)
+ err := getsockopt(fd, level, opt, uintptr(unsafe.Pointer(&value)), &vallen)
+ return &value, err
}
-func SetsockoptIPMreq(fd, level, opt int, mreq *IPMreq) (err error) {
- return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(mreq)), unsafe.Sizeof(*mreq))
+func GetsockoptUcred(fd, level, opt int) (*Ucred, error) {
+ var value Ucred
+ vallen := _Socklen(SizeofUcred)
+ err := getsockopt(fd, level, opt, uintptr(unsafe.Pointer(&value)), &vallen)
+ return &value, err
}
func SetsockoptIPMreqn(fd, level, opt int, mreq *IPMreqn) (err error) {
return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(mreq)), unsafe.Sizeof(*mreq))
}
-func SetsockoptIPv6Mreq(fd, level, opt int, mreq *IPv6Mreq) (err error) {
- return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(mreq)), unsafe.Sizeof(*mreq))
-}
-
-func SetsockoptString(fd, level, opt int, s string) (err error) {
- return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(&[]byte(s)[0])), uintptr(len(s)))
-}
-
-func Recvfrom(fd int, p []byte, flags int) (n int, from Sockaddr, err error) {
- var rsa RawSockaddrAny
- var len _Socklen = SizeofSockaddrAny
- if n, err = recvfrom(fd, p, flags, &rsa, &len); err != nil {
- return
- }
- if rsa.Addr.Family != AF_UNSPEC {
- from, err = anyToSockaddr(&rsa)
- }
- return
-}
-
-func Sendto(fd int, p []byte, flags int, to Sockaddr) (err error) {
- ptr, n, err := to.sockaddr()
- if err != nil {
- return err
- }
- return sendto(fd, p, flags, ptr, n)
-}
-
func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) {
var msg Msghdr
var rsa RawSockaddrAny
@@ -889,6 +778,7 @@ func Mount(source string, target string, fstype string, flags uintptr, data stri
//sys Creat(path string, mode uint32) (fd int, err error)
//sysnb Dup(oldfd int) (fd int, err error)
//sysnb Dup2(oldfd int, newfd int) (err error)
+//sysnb Dup3(oldfd int, newfd int, flags int) (err error)
//sysnb EpollCreate(size int) (fd int, err error)
//sysnb EpollCreate1(flag int) (fd int, err error)
//sysnb EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error)
@@ -909,6 +799,7 @@ func Mount(source string, target string, fstype string, flags uintptr, data stri
//sysnb Getpgrp() (pid int)
//sysnb Getpid() (pid int)
//sysnb Getppid() (ppid int)
+//sys Getpriority(which int, who int) (prio int, err error)
//sysnb Getrusage(who int, rusage *Rusage) (err error)
//sysnb Gettid() (tid int)
//sys Getxattr(path string, attr string, dest []byte) (sz int, err error)
@@ -940,6 +831,7 @@ func Mount(source string, target string, fstype string, flags uintptr, data stri
//sysnb Setsid() (pid int, err error)
//sysnb Settimeofday(tv *Timeval) (err error)
//sysnb Setuid(uid int) (err error)
+//sys Setpriority(which int, who int, prio int) (err error)
//sys Setxattr(path string, attr string, data []byte, flags int) (err error)
//sys Symlink(oldpath string, newpath string) (err error)
//sys Sync()
@@ -1019,7 +911,6 @@ func Munmap(b []byte) (err error) {
// GetThreadArea
// Getitimer
// Getpmsg
-// Getpriority
// IoCancel
// IoDestroy
// IoGetevents
@@ -1095,7 +986,6 @@ func Munmap(b []byte) (err error) {
// SetRobustList
// SetThreadArea
// SetTidAddress
-// Setpriority
// Shmat
// Shmctl
// Shmdt
diff --git a/src/pkg/syscall/syscall_no_getwd.go b/src/pkg/syscall/syscall_no_getwd.go
new file mode 100644
index 000000000..0080c5ca0
--- /dev/null
+++ b/src/pkg/syscall/syscall_no_getwd.go
@@ -0,0 +1,11 @@
+// Copyright 2013 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.
+
+// +build dragonfly freebsd netbsd openbsd
+
+package syscall
+
+const ImplementsGetwd = false
+
+func Getwd() (string, error) { return "", ENOTSUP }
diff --git a/src/pkg/syscall/syscall_plan9.go b/src/pkg/syscall/syscall_plan9.go
index bc2505758..2e1c064c4 100644
--- a/src/pkg/syscall/syscall_plan9.go
+++ b/src/pkg/syscall/syscall_plan9.go
@@ -120,17 +120,10 @@ func Getppid() (ppid int) {
}
func Read(fd int, p []byte) (n int, err error) {
- n, err = Pread(fd, p, -1)
- if raceenabled && err == nil {
- raceAcquire(unsafe.Pointer(&ioSync))
- }
- return
+ return Pread(fd, p, -1)
}
func Write(fd int, p []byte) (n int, err error) {
- if raceenabled {
- raceReleaseMerge(unsafe.Pointer(&ioSync))
- }
return Pwrite(fd, p, -1)
}
diff --git a/src/pkg/syscall/syscall_test.go b/src/pkg/syscall/syscall_test.go
new file mode 100644
index 000000000..2a39b54f1
--- /dev/null
+++ b/src/pkg/syscall/syscall_test.go
@@ -0,0 +1,30 @@
+// Copyright 2013 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_test
+
+import (
+ "syscall"
+ "testing"
+)
+
+func testSetGetenv(t *testing.T, key, value string) {
+ err := syscall.Setenv(key, value)
+ if err != nil {
+ t.Fatalf("Setenv failed to set %q: %v", value, err)
+ }
+ newvalue, found := syscall.Getenv(key)
+ if !found {
+ t.Fatalf("Getenv failed to find %v variable (want value %q)", key, value)
+ }
+ if newvalue != value {
+ t.Fatalf("Getenv(%v) = %q; want %q", key, newvalue, value)
+ }
+}
+
+func TestEnv(t *testing.T) {
+ testSetGetenv(t, "TESTENV", "AVALUE")
+ // make sure TESTENV gets set to "", not deleted
+ testSetGetenv(t, "TESTENV", "")
+}
diff --git a/src/pkg/syscall/syscall_unix.go b/src/pkg/syscall/syscall_unix.go
index fee1fc491..6455dc29c 100644
--- a/src/pkg/syscall/syscall_unix.go
+++ b/src/pkg/syscall/syscall_unix.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.
-// +build darwin freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd
package syscall
@@ -18,7 +18,10 @@ var (
Stderr = 2
)
-const darwinAMD64 = runtime.GOOS == "darwin" && runtime.GOARCH == "amd64"
+const (
+ darwin64Bit = runtime.GOOS == "darwin" && sizeofPtr == 8
+ netbsd32Bit = runtime.GOOS == "netbsd" && sizeofPtr == 4
+)
func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
@@ -130,8 +133,13 @@ func (s Signal) String() string {
func Read(fd int, p []byte) (n int, err error) {
n, err = read(fd, p)
- if raceenabled && err == nil {
- raceAcquire(unsafe.Pointer(&ioSync))
+ if raceenabled {
+ if n > 0 {
+ raceWriteRange(unsafe.Pointer(&p[0]), n)
+ }
+ if err == nil {
+ raceAcquire(unsafe.Pointer(&ioSync))
+ }
}
return
}
@@ -140,7 +148,144 @@ func Write(fd int, p []byte) (n int, err error) {
if raceenabled {
raceReleaseMerge(unsafe.Pointer(&ioSync))
}
- return write(fd, p)
+ n, err = write(fd, p)
+ if raceenabled && n > 0 {
+ raceReadRange(unsafe.Pointer(&p[0]), n)
+ }
+ return
+}
+
+// For testing: clients can set this flag to force
+// creation of IPv6 sockets to return EAFNOSUPPORT.
+var SocketDisableIPv6 bool
+
+type Sockaddr interface {
+ sockaddr() (ptr uintptr, len _Socklen, err error) // lowercase; only we can define Sockaddrs
+}
+
+type SockaddrInet4 struct {
+ Port int
+ Addr [4]byte
+ raw RawSockaddrInet4
+}
+
+type SockaddrInet6 struct {
+ Port int
+ ZoneId uint32
+ Addr [16]byte
+ raw RawSockaddrInet6
+}
+
+type SockaddrUnix struct {
+ Name string
+ raw RawSockaddrUnix
+}
+
+func Bind(fd int, sa Sockaddr) (err error) {
+ ptr, n, err := sa.sockaddr()
+ if err != nil {
+ return err
+ }
+ return bind(fd, ptr, n)
+}
+
+func Connect(fd int, sa Sockaddr) (err error) {
+ ptr, n, err := sa.sockaddr()
+ if err != nil {
+ return err
+ }
+ return connect(fd, ptr, n)
+}
+
+func Getpeername(fd int) (sa Sockaddr, err error) {
+ var rsa RawSockaddrAny
+ var len _Socklen = SizeofSockaddrAny
+ if err = getpeername(fd, &rsa, &len); err != nil {
+ return
+ }
+ return anyToSockaddr(&rsa)
+}
+
+func GetsockoptInt(fd, level, opt int) (value int, err error) {
+ var n int32
+ vallen := _Socklen(4)
+ err = getsockopt(fd, level, opt, uintptr(unsafe.Pointer(&n)), &vallen)
+ return int(n), err
+}
+
+func Recvfrom(fd int, p []byte, flags int) (n int, from Sockaddr, err error) {
+ var rsa RawSockaddrAny
+ var len _Socklen = SizeofSockaddrAny
+ if n, err = recvfrom(fd, p, flags, &rsa, &len); err != nil {
+ return
+ }
+ if rsa.Addr.Family != AF_UNSPEC {
+ from, err = anyToSockaddr(&rsa)
+ }
+ return
+}
+
+func Sendto(fd int, p []byte, flags int, to Sockaddr) (err error) {
+ ptr, n, err := to.sockaddr()
+ if err != nil {
+ return err
+ }
+ return sendto(fd, p, flags, ptr, n)
+}
+
+func SetsockoptByte(fd, level, opt int, value byte) (err error) {
+ return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(&value)), 1)
+}
+
+func SetsockoptInt(fd, level, opt int, value int) (err error) {
+ var n = int32(value)
+ return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(&n)), 4)
+}
+
+func SetsockoptInet4Addr(fd, level, opt int, value [4]byte) (err error) {
+ return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(&value[0])), 4)
+}
+
+func SetsockoptIPMreq(fd, level, opt int, mreq *IPMreq) (err error) {
+ return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(mreq)), SizeofIPMreq)
+}
+
+func SetsockoptIPv6Mreq(fd, level, opt int, mreq *IPv6Mreq) (err error) {
+ return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(mreq)), SizeofIPv6Mreq)
+}
+
+func SetsockoptICMPv6Filter(fd, level, opt int, filter *ICMPv6Filter) error {
+ return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(filter)), SizeofICMPv6Filter)
+}
+
+func SetsockoptLinger(fd, level, opt int, l *Linger) (err error) {
+ return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(l)), SizeofLinger)
+}
+
+func SetsockoptString(fd, level, opt int, s string) (err error) {
+ return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(&[]byte(s)[0])), uintptr(len(s)))
+}
+
+func SetsockoptTimeval(fd, level, opt int, tv *Timeval) (err error) {
+ return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(tv)), unsafe.Sizeof(*tv))
+}
+
+func Socket(domain, typ, proto int) (fd int, err error) {
+ if domain == AF_INET6 && SocketDisableIPv6 {
+ return -1, EAFNOSUPPORT
+ }
+ fd, err = socket(domain, typ, proto)
+ return
+}
+
+func Socketpair(domain, typ, proto int) (fd [2]int, err error) {
+ var fdx [2]int32
+ err = socketpair(domain, typ, proto, &fdx)
+ if err == nil {
+ fd[0] = int(fdx[0])
+ fd[1] = int(fdx[1])
+ }
+ return
}
func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
diff --git a/src/pkg/syscall/syscall_windows.go b/src/pkg/syscall/syscall_windows.go
index d7c3265a1..3d78b6823 100644
--- a/src/pkg/syscall/syscall_windows.go
+++ b/src/pkg/syscall/syscall_windows.go
@@ -272,6 +272,9 @@ func Read(fd Handle, p []byte) (n int, err error) {
return 0, e
}
if raceenabled {
+ if done > 0 {
+ raceWriteRange(unsafe.Pointer(&p[0]), int(done))
+ }
raceAcquire(unsafe.Pointer(&ioSync))
}
return int(done), nil
@@ -286,6 +289,9 @@ func Write(fd Handle, p []byte) (n int, err error) {
if e != nil {
return 0, e
}
+ if raceenabled && done > 0 {
+ raceReadRange(unsafe.Pointer(&p[0]), int(done))
+ }
return int(done), nil
}
@@ -502,6 +508,10 @@ func LoadCancelIoEx() error {
return procCancelIoEx.Find()
}
+func LoadSetFileCompletionNotificationModes() error {
+ return procSetFileCompletionNotificationModes.Find()
+}
+
// net api calls
const socket_error = uintptr(^uint32(0))
@@ -535,6 +545,8 @@ const socket_error = uintptr(^uint32(0))
//sys FreeAddrInfoW(addrinfo *AddrinfoW) = ws2_32.FreeAddrInfoW
//sys GetIfEntry(pIfRow *MibIfRow) (errcode error) = iphlpapi.GetIfEntry
//sys GetAdaptersInfo(ai *IpAdapterInfo, ol *uint32) (errcode error) = iphlpapi.GetAdaptersInfo
+//sys SetFileCompletionNotificationModes(handle Handle, flags uint8) (err error) = kernel32.SetFileCompletionNotificationModes
+//sys WSAEnumProtocols(protocols *int32, protocolBuffer *WSAProtocolInfo, bufferLength *uint32) (n int32, err error) [failretval==-1] = ws2_32.WSAEnumProtocolsW
// For testing: clients can set this flag to force
// creation of IPv6 sockets to return EAFNOSUPPORT.
diff --git a/src/pkg/syscall/types_darwin.go b/src/pkg/syscall/types_darwin.go
index 098bbff6f..a043071f2 100644
--- a/src/pkg/syscall/types_darwin.go
+++ b/src/pkg/syscall/types_darwin.go
@@ -45,6 +45,7 @@ package syscall
#include <net/if_var.h>
#include <net/route.h>
#include <netinet/in.h>
+#include <netinet/icmp6.h>
#include <netinet/tcp.h>
enum {
@@ -154,6 +155,10 @@ type Inet4Pktinfo C.struct_in_pktinfo
type Inet6Pktinfo C.struct_in6_pktinfo
+type IPv6MTUInfo C.struct_ip6_mtuinfo
+
+type ICMPv6Filter C.struct_icmp6_filter
+
const (
SizeofSockaddrInet4 = C.sizeof_struct_sockaddr_in
SizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
@@ -167,6 +172,8 @@ const (
SizeofCmsghdr = C.sizeof_struct_cmsghdr
SizeofInet4Pktinfo = C.sizeof_struct_in_pktinfo
SizeofInet6Pktinfo = C.sizeof_struct_in6_pktinfo
+ SizeofIPv6MTUInfo = C.sizeof_struct_ip6_mtuinfo
+ SizeofICMPv6Filter = C.sizeof_struct_icmp6_filter
)
// Ptrace requests
diff --git a/src/pkg/syscall/types_dragonfly.go b/src/pkg/syscall/types_dragonfly.go
new file mode 100644
index 000000000..009b8f045
--- /dev/null
+++ b/src/pkg/syscall/types_dragonfly.go
@@ -0,0 +1,241 @@
+// 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.
+
+// +build ignore
+
+/*
+Input to cgo -godefs. See also mkerrors.sh and mkall.sh
+*/
+
+// +godefs map struct_in_addr [4]byte /* in_addr */
+// +godefs map struct_in6_addr [16]byte /* in6_addr */
+
+package syscall
+
+/*
+#define KERNEL
+#include <dirent.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/event.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/param.h>
+#include <sys/ptrace.h>
+#include <sys/resource.h>
+#include <sys/select.h>
+#include <sys/signal.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#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>
+#include <netinet/in.h>
+#include <netinet/icmp6.h>
+#include <netinet/tcp.h>
+
+enum {
+ sizeofPtr = sizeof(void*),
+};
+
+union sockaddr_all {
+ struct sockaddr s1; // this one gets used for fields
+ struct sockaddr_in s2; // these pad it out
+ struct sockaddr_in6 s3;
+ struct sockaddr_un s4;
+ struct sockaddr_dl s5;
+};
+
+struct sockaddr_any {
+ struct sockaddr addr;
+ char pad[sizeof(union sockaddr_all) - sizeof(struct sockaddr)];
+};
+
+*/
+import "C"
+
+// Machine characteristics; for internal use.
+
+const (
+ sizeofPtr = C.sizeofPtr
+ sizeofShort = C.sizeof_short
+ sizeofInt = C.sizeof_int
+ sizeofLong = C.sizeof_long
+ sizeofLongLong = C.sizeof_longlong
+)
+
+// Basic types
+
+type (
+ _C_short C.short
+ _C_int C.int
+ _C_long C.long
+ _C_long_long C.longlong
+)
+
+// Time
+
+type Timespec C.struct_timespec
+
+type Timeval C.struct_timeval
+
+// Processes
+
+type Rusage C.struct_rusage
+
+type Rlimit C.struct_rlimit
+
+type _Gid_t C.gid_t
+
+// Files
+
+const (
+ F_DUPFD_CLOEXEC = 0 // not supported
+)
+
+const ( // Directory mode bits
+ S_IFMT = C.S_IFMT
+ S_IFIFO = C.S_IFIFO
+ S_IFCHR = C.S_IFCHR
+ S_IFDIR = C.S_IFDIR
+ S_IFBLK = C.S_IFBLK
+ S_IFREG = C.S_IFREG
+ S_IFLNK = C.S_IFLNK
+ S_IFSOCK = C.S_IFSOCK
+ S_ISUID = C.S_ISUID
+ S_ISGID = C.S_ISGID
+ S_ISVTX = C.S_ISVTX
+ S_IRUSR = C.S_IRUSR
+ S_IWUSR = C.S_IWUSR
+ S_IXUSR = C.S_IXUSR
+)
+
+type Stat_t C.struct_stat
+
+type Statfs_t C.struct_statfs
+
+type Flock_t C.struct_flock
+
+type Dirent C.struct_dirent
+
+type Fsid C.struct_fsid
+
+// Sockets
+
+type RawSockaddrInet4 C.struct_sockaddr_in
+
+type RawSockaddrInet6 C.struct_sockaddr_in6
+
+type RawSockaddrUnix C.struct_sockaddr_un
+
+type RawSockaddrDatalink C.struct_sockaddr_dl
+
+type RawSockaddr C.struct_sockaddr
+
+type RawSockaddrAny C.struct_sockaddr_any
+
+type _Socklen C.socklen_t
+
+type Linger C.struct_linger
+
+type Iovec C.struct_iovec
+
+type IPMreq C.struct_ip_mreq
+
+type IPv6Mreq C.struct_ipv6_mreq
+
+type Msghdr C.struct_msghdr
+
+type Cmsghdr C.struct_cmsghdr
+
+type Inet6Pktinfo C.struct_in6_pktinfo
+
+type IPv6MTUInfo C.struct_ip6_mtuinfo
+
+type ICMPv6Filter C.struct_icmp6_filter
+
+const (
+ SizeofSockaddrInet4 = C.sizeof_struct_sockaddr_in
+ SizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
+ SizeofSockaddrAny = C.sizeof_struct_sockaddr_any
+ SizeofSockaddrUnix = C.sizeof_struct_sockaddr_un
+ SizeofSockaddrDatalink = C.sizeof_struct_sockaddr_dl
+ SizeofLinger = C.sizeof_struct_linger
+ SizeofIPMreq = C.sizeof_struct_ip_mreq
+ SizeofIPv6Mreq = C.sizeof_struct_ipv6_mreq
+ SizeofMsghdr = C.sizeof_struct_msghdr
+ SizeofCmsghdr = C.sizeof_struct_cmsghdr
+ SizeofInet6Pktinfo = C.sizeof_struct_in6_pktinfo
+ SizeofIPv6MTUInfo = C.sizeof_struct_ip6_mtuinfo
+ SizeofICMPv6Filter = C.sizeof_struct_icmp6_filter
+)
+
+// Ptrace requests
+
+const (
+ PTRACE_TRACEME = C.PT_TRACE_ME
+ PTRACE_CONT = C.PT_CONTINUE
+ PTRACE_KILL = C.PT_KILL
+)
+
+// Events (kqueue, kevent)
+
+type Kevent_t C.struct_kevent
+
+// Select
+
+type FdSet C.fd_set
+
+// Routing and interface messages
+
+const (
+ SizeofIfMsghdr = C.sizeof_struct_if_msghdr
+ SizeofIfData = C.sizeof_struct_if_data
+ SizeofIfaMsghdr = C.sizeof_struct_ifa_msghdr
+ SizeofIfmaMsghdr = C.sizeof_struct_ifma_msghdr
+ SizeofIfAnnounceMsghdr = C.sizeof_struct_if_announcemsghdr
+ SizeofRtMsghdr = C.sizeof_struct_rt_msghdr
+ SizeofRtMetrics = C.sizeof_struct_rt_metrics
+)
+
+type IfMsghdr C.struct_if_msghdr
+
+type IfData C.struct_if_data
+
+type IfaMsghdr C.struct_ifa_msghdr
+
+type IfmaMsghdr C.struct_ifma_msghdr
+
+type IfAnnounceMsghdr C.struct_if_announcemsghdr
+
+type RtMsghdr C.struct_rt_msghdr
+
+type RtMetrics C.struct_rt_metrics
+
+// Berkeley packet filter
+
+const (
+ SizeofBpfVersion = C.sizeof_struct_bpf_version
+ SizeofBpfStat = C.sizeof_struct_bpf_stat
+ SizeofBpfProgram = C.sizeof_struct_bpf_program
+ SizeofBpfInsn = C.sizeof_struct_bpf_insn
+ SizeofBpfHdr = C.sizeof_struct_bpf_hdr
+)
+
+type BpfVersion C.struct_bpf_version
+
+type BpfStat C.struct_bpf_stat
+
+type BpfProgram C.struct_bpf_program
+
+type BpfInsn C.struct_bpf_insn
+
+type BpfHdr C.struct_bpf_hdr
diff --git a/src/pkg/syscall/types_freebsd.go b/src/pkg/syscall/types_freebsd.go
index 7d4923d5a..ccf53d0ad 100644
--- a/src/pkg/syscall/types_freebsd.go
+++ b/src/pkg/syscall/types_freebsd.go
@@ -39,6 +39,7 @@ package syscall
#include <net/if_dl.h>
#include <net/route.h>
#include <netinet/in.h>
+#include <netinet/icmp6.h>
#include <netinet/tcp.h>
enum {
@@ -159,6 +160,10 @@ type Cmsghdr C.struct_cmsghdr
type Inet6Pktinfo C.struct_in6_pktinfo
+type IPv6MTUInfo C.struct_ip6_mtuinfo
+
+type ICMPv6Filter C.struct_icmp6_filter
+
const (
SizeofSockaddrInet4 = C.sizeof_struct_sockaddr_in
SizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
@@ -172,6 +177,8 @@ const (
SizeofMsghdr = C.sizeof_struct_msghdr
SizeofCmsghdr = C.sizeof_struct_cmsghdr
SizeofInet6Pktinfo = C.sizeof_struct_in6_pktinfo
+ SizeofIPv6MTUInfo = C.sizeof_struct_ip6_mtuinfo
+ SizeofICMPv6Filter = C.sizeof_struct_icmp6_filter
)
// Ptrace requests
diff --git a/src/pkg/syscall/types_linux.go b/src/pkg/syscall/types_linux.go
index 1514cbc95..fea09d1d7 100644
--- a/src/pkg/syscall/types_linux.go
+++ b/src/pkg/syscall/types_linux.go
@@ -49,6 +49,7 @@ package syscall
#include <linux/filter.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
+#include <linux/icmpv6.h>
#include <termios.h>
#include <time.h>
#include <unistd.h>
@@ -193,6 +194,10 @@ type Inet4Pktinfo C.struct_in_pktinfo
type Inet6Pktinfo C.struct_in6_pktinfo
+type IPv6MTUInfo C.struct_ip6_mtuinfo
+
+type ICMPv6Filter C.struct_icmp6_filter
+
type Ucred C.struct_ucred
type TCPInfo C.struct_tcp_info
@@ -212,6 +217,8 @@ const (
SizeofCmsghdr = C.sizeof_struct_cmsghdr
SizeofInet4Pktinfo = C.sizeof_struct_in_pktinfo
SizeofInet6Pktinfo = C.sizeof_struct_in6_pktinfo
+ SizeofIPv6MTUInfo = C.sizeof_struct_ip6_mtuinfo
+ SizeofICMPv6Filter = C.sizeof_struct_icmp6_filter
SizeofUcred = C.sizeof_struct_ucred
SizeofTCPInfo = C.sizeof_struct_tcp_info
)
diff --git a/src/pkg/syscall/types_netbsd.go b/src/pkg/syscall/types_netbsd.go
index 4906a99ef..badaa1049 100644
--- a/src/pkg/syscall/types_netbsd.go
+++ b/src/pkg/syscall/types_netbsd.go
@@ -41,6 +41,7 @@ package syscall
#include <net/if_dl.h>
#include <net/route.h>
#include <netinet/in.h>
+#include <netinet/icmp6.h>
#include <netinet/tcp.h>
enum {
@@ -138,6 +139,10 @@ type Cmsghdr C.struct_cmsghdr
type Inet6Pktinfo C.struct_in6_pktinfo
+type IPv6MTUInfo C.struct_ip6_mtuinfo
+
+type ICMPv6Filter C.struct_icmp6_filter
+
const (
SizeofSockaddrInet4 = C.sizeof_struct_sockaddr_in
SizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
@@ -150,6 +155,8 @@ const (
SizeofMsghdr = C.sizeof_struct_msghdr
SizeofCmsghdr = C.sizeof_struct_cmsghdr
SizeofInet6Pktinfo = C.sizeof_struct_in6_pktinfo
+ SizeofIPv6MTUInfo = C.sizeof_struct_ip6_mtuinfo
+ SizeofICMPv6Filter = C.sizeof_struct_icmp6_filter
)
// Ptrace requests
diff --git a/src/pkg/syscall/types_openbsd.go b/src/pkg/syscall/types_openbsd.go
index 2430a166b..6fe2af6e0 100644
--- a/src/pkg/syscall/types_openbsd.go
+++ b/src/pkg/syscall/types_openbsd.go
@@ -40,6 +40,7 @@ package syscall
#include <net/if_dl.h>
#include <net/route.h>
#include <netinet/in.h>
+#include <netinet/icmp6.h>
#include <netinet/tcp.h>
enum {
@@ -154,6 +155,10 @@ type Cmsghdr C.struct_cmsghdr
type Inet6Pktinfo C.struct_in6_pktinfo
+type IPv6MTUInfo C.struct_ip6_mtuinfo
+
+type ICMPv6Filter C.struct_icmp6_filter
+
const (
SizeofSockaddrInet4 = C.sizeof_struct_sockaddr_in
SizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
@@ -166,6 +171,8 @@ const (
SizeofMsghdr = C.sizeof_struct_msghdr
SizeofCmsghdr = C.sizeof_struct_cmsghdr
SizeofInet6Pktinfo = C.sizeof_struct_in6_pktinfo
+ SizeofIPv6MTUInfo = C.sizeof_struct_ip6_mtuinfo
+ SizeofICMPv6Filter = C.sizeof_struct_icmp6_filter
)
// Ptrace requests
diff --git a/src/pkg/syscall/zerrors_darwin_386.go b/src/pkg/syscall/zerrors_darwin_386.go
index cdb860543..bb3a1610c 100644
--- a/src/pkg/syscall/zerrors_darwin_386.go
+++ b/src/pkg/syscall/zerrors_darwin_386.go
@@ -273,6 +273,7 @@ const (
F_WRLCK = 0x3
HUPCL = 0x4000
ICANON = 0x100
+ ICMP6_FILTER = 0x12
ICRNL = 0x100
IEXTEN = 0x400
IFF_ALLMULTI = 0x200
@@ -746,6 +747,9 @@ const (
PARMRK = 0x8
PARODD = 0x2000
PENDIN = 0x20000000
+ PRIO_PGRP = 0x1
+ PRIO_PROCESS = 0x0
+ PRIO_USER = 0x2
PROT_EXEC = 0x4
PROT_NONE = 0x0
PROT_READ = 0x1
diff --git a/src/pkg/syscall/zerrors_darwin_amd64.go b/src/pkg/syscall/zerrors_darwin_amd64.go
index 8ffcbcc04..05ab48ee3 100644
--- a/src/pkg/syscall/zerrors_darwin_amd64.go
+++ b/src/pkg/syscall/zerrors_darwin_amd64.go
@@ -273,6 +273,7 @@ const (
F_WRLCK = 0x3
HUPCL = 0x4000
ICANON = 0x100
+ ICMP6_FILTER = 0x12
ICRNL = 0x100
IEXTEN = 0x400
IFF_ALLMULTI = 0x200
@@ -746,6 +747,9 @@ const (
PARMRK = 0x8
PARODD = 0x2000
PENDIN = 0x20000000
+ PRIO_PGRP = 0x1
+ PRIO_PROCESS = 0x0
+ PRIO_USER = 0x2
PROT_EXEC = 0x4
PROT_NONE = 0x0
PROT_READ = 0x1
diff --git a/src/pkg/syscall/zerrors_dragonfly_386.go b/src/pkg/syscall/zerrors_dragonfly_386.go
new file mode 100644
index 000000000..a2eb926ee
--- /dev/null
+++ b/src/pkg/syscall/zerrors_dragonfly_386.go
@@ -0,0 +1,1523 @@
+// mkerrors.sh -m32
+// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs -- -m32 _const.go
+
+package syscall
+
+const (
+ AF_APPLETALK = 0x10
+ AF_ATM = 0x1e
+ AF_BLUETOOTH = 0x21
+ 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 = 0x23
+ 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 = 0x24
+ AF_MPLS = 0x22
+ AF_NATM = 0x1d
+ AF_NETGRAPH = 0x20
+ AF_NS = 0x6
+ AF_OSI = 0x7
+ AF_PUP = 0x4
+ AF_ROUTE = 0x11
+ AF_SIP = 0x18
+ AF_SNA = 0xb
+ AF_UNIX = 0x1
+ AF_UNSPEC = 0x0
+ B0 = 0x0
+ B110 = 0x6e
+ B115200 = 0x1c200
+ B1200 = 0x4b0
+ B134 = 0x86
+ B14400 = 0x3840
+ B150 = 0x96
+ B1800 = 0x708
+ B19200 = 0x4b00
+ B200 = 0xc8
+ B230400 = 0x38400
+ B2400 = 0x960
+ B28800 = 0x7080
+ B300 = 0x12c
+ B38400 = 0x9600
+ B4800 = 0x12c0
+ B50 = 0x32
+ B57600 = 0xe100
+ B600 = 0x258
+ B7200 = 0x1c20
+ B75 = 0x4b
+ B76800 = 0x12c00
+ B9600 = 0x2580
+ BIOCFLUSH = 0x20004268
+ BIOCGBLEN = 0x40044266
+ BIOCGDLT = 0x4004426a
+ BIOCGDLTLIST = 0xc0084279
+ BIOCGETIF = 0x4020426b
+ BIOCGHDRCMPLT = 0x40044274
+ BIOCGRSIG = 0x40044272
+ BIOCGRTIMEOUT = 0x4008426e
+ BIOCGSEESENT = 0x40044276
+ BIOCGSTATS = 0x4008426f
+ BIOCIMMEDIATE = 0x80044270
+ BIOCLOCK = 0x2000427a
+ BIOCPROMISC = 0x20004269
+ BIOCSBLEN = 0xc0044266
+ BIOCSDLT = 0x80044278
+ BIOCSETF = 0x80084267
+ BIOCSETIF = 0x8020426c
+ BIOCSETWF = 0x8008427b
+ BIOCSHDRCMPLT = 0x80044275
+ BIOCSRSIG = 0x80044273
+ BIOCSRTIMEOUT = 0x8008426d
+ BIOCSSEESENT = 0x80044277
+ BIOCVERSION = 0x40044271
+ BPF_A = 0x10
+ BPF_ABS = 0x20
+ BPF_ADD = 0x0
+ BPF_ALIGNMENT = 0x4
+ BPF_ALU = 0x4
+ BPF_AND = 0x50
+ BPF_B = 0x10
+ BPF_DEFAULTBUFSIZE = 0x1000
+ BPF_DIV = 0x30
+ BPF_H = 0x8
+ BPF_IMM = 0x0
+ BPF_IND = 0x40
+ BPF_JA = 0x0
+ BPF_JEQ = 0x10
+ BPF_JGE = 0x30
+ BPF_JGT = 0x20
+ BPF_JMP = 0x5
+ BPF_JSET = 0x40
+ BPF_K = 0x0
+ BPF_LD = 0x0
+ BPF_LDX = 0x1
+ BPF_LEN = 0x80
+ BPF_LSH = 0x60
+ BPF_MAJOR_VERSION = 0x1
+ BPF_MAXBUFSIZE = 0x80000
+ BPF_MAXINSNS = 0x200
+ BPF_MAX_CLONES = 0x80
+ 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 = 0x0
+ BPF_TXA = 0x80
+ BPF_W = 0x0
+ BPF_X = 0x8
+ BRKINT = 0x2
+ CFLUSH = 0xf
+ CLOCAL = 0x8000
+ CREAD = 0x800
+ CS5 = 0x0
+ CS6 = 0x100
+ CS7 = 0x200
+ CS8 = 0x300
+ CSIZE = 0x300
+ CSTART = 0x11
+ CSTATUS = 0x14
+ CSTOP = 0x13
+ CSTOPB = 0x400
+ CSUSP = 0x1a
+ CTL_MAXNAME = 0xc
+ 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_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 = 0x0
+ DLT_PCI_EXP = 0x7d
+ DLT_PFLOG = 0x75
+ DLT_PFSYNC = 0x12
+ 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_PRISM_HEADER = 0x77
+ DLT_PRONET = 0x4
+ DLT_RAIF1 = 0xc6
+ DLT_RAW = 0xc
+ DLT_REDBACK_SMARTEDGE = 0x20
+ 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_X2E_SERIAL = 0xd5
+ DLT_X2E_XORAYA = 0xd6
+ DT_BLK = 0x6
+ DT_CHR = 0x2
+ DT_DBF = 0xf
+ DT_DIR = 0x4
+ DT_FIFO = 0x1
+ DT_LNK = 0xa
+ DT_REG = 0x8
+ DT_SOCK = 0xc
+ DT_UNKNOWN = 0x0
+ DT_WHT = 0xe
+ ECHO = 0x8
+ ECHOCTL = 0x40
+ ECHOE = 0x2
+ ECHOK = 0x4
+ ECHOKE = 0x1
+ ECHONL = 0x10
+ ECHOPRT = 0x20
+ EVFILT_AIO = -0x3
+ EVFILT_EXCEPT = -0x8
+ EVFILT_MARKER = 0xf
+ EVFILT_PROC = -0x5
+ EVFILT_READ = -0x1
+ EVFILT_SIGNAL = -0x6
+ EVFILT_SYSCOUNT = 0x8
+ EVFILT_TIMER = -0x7
+ EVFILT_VNODE = -0x4
+ EVFILT_WRITE = -0x2
+ EV_ADD = 0x1
+ EV_CLEAR = 0x20
+ EV_DELETE = 0x2
+ EV_DISABLE = 0x8
+ EV_ENABLE = 0x4
+ EV_EOF = 0x8000
+ EV_ERROR = 0x4000
+ EV_FLAG1 = 0x2000
+ EV_NODATA = 0x1000
+ EV_ONESHOT = 0x10
+ EV_SYSFLAGS = 0xf000
+ EXTA = 0x4b00
+ EXTB = 0x9600
+ EXTEXIT_LWP = 0x10000
+ EXTEXIT_PROC = 0x0
+ EXTEXIT_SETINT = 0x1
+ EXTEXIT_SIMPLE = 0x0
+ EXTPROC = 0x800
+ FD_CLOEXEC = 0x1
+ FD_SETSIZE = 0x400
+ FLUSHO = 0x800000
+ F_DUPFD = 0x0
+ F_GETFD = 0x1
+ F_GETFL = 0x3
+ F_GETLK = 0x7
+ F_GETOWN = 0x5
+ F_OK = 0x0
+ F_RDLCK = 0x1
+ F_SETFD = 0x2
+ F_SETFL = 0x4
+ F_SETLK = 0x8
+ F_SETLKW = 0x9
+ F_SETOWN = 0x6
+ F_UNLCK = 0x2
+ F_WRLCK = 0x3
+ HUPCL = 0x4000
+ ICANON = 0x100
+ ICMP6_FILTER = 0x12
+ ICRNL = 0x100
+ IEXTEN = 0x400
+ IFAN_ARRIVAL = 0x0
+ IFAN_DEPARTURE = 0x1
+ IFF_ALLMULTI = 0x200
+ IFF_ALTPHYS = 0x4000
+ IFF_BROADCAST = 0x2
+ IFF_CANTCHANGE = 0x118e72
+ IFF_DEBUG = 0x4
+ IFF_LINK0 = 0x1000
+ IFF_LINK1 = 0x2000
+ IFF_LINK2 = 0x4000
+ IFF_LOOPBACK = 0x8
+ IFF_MONITOR = 0x40000
+ IFF_MULTICAST = 0x8000
+ IFF_NOARP = 0x80
+ IFF_NPOLLING = 0x100000
+ IFF_OACTIVE = 0x400
+ IFF_OACTIVE_COMPAT = 0x400
+ IFF_POINTOPOINT = 0x10
+ IFF_POLLING = 0x10000
+ IFF_POLLING_COMPAT = 0x10000
+ IFF_PPROMISC = 0x20000
+ IFF_PROMISC = 0x100
+ IFF_RUNNING = 0x40
+ IFF_SIMPLEX = 0x800
+ IFF_SMART = 0x20
+ IFF_STATICARP = 0x80000
+ IFF_UP = 0x1
+ IFNAMSIZ = 0x10
+ IFT_1822 = 0x2
+ IFT_A12MPPSWITCH = 0x82
+ IFT_AAL2 = 0xbb
+ IFT_AAL5 = 0x31
+ IFT_ADSL = 0x5e
+ IFT_AFLANE8023 = 0x3b
+ IFT_AFLANE8025 = 0x3c
+ IFT_ARAP = 0x58
+ IFT_ARCNET = 0x23
+ IFT_ARCNETPLUS = 0x24
+ IFT_ASYNC = 0x54
+ IFT_ATM = 0x25
+ IFT_ATMDXI = 0x69
+ IFT_ATMFUNI = 0x6a
+ IFT_ATMIMA = 0x6b
+ IFT_ATMLOGICAL = 0x50
+ IFT_ATMRADIO = 0xbd
+ IFT_ATMSUBINTERFACE = 0x86
+ IFT_ATMVCIENDPT = 0xc2
+ IFT_ATMVIRTUAL = 0x95
+ IFT_BGPPOLICYACCOUNTING = 0xa2
+ IFT_BRIDGE = 0xd1
+ IFT_BSC = 0x53
+ IFT_CARP = 0xf8
+ IFT_CCTEMUL = 0x3d
+ IFT_CEPT = 0x13
+ IFT_CES = 0x85
+ IFT_CHANNEL = 0x46
+ IFT_CNR = 0x55
+ IFT_COFFEE = 0x84
+ IFT_COMPOSITELINK = 0x9b
+ IFT_DCN = 0x8d
+ IFT_DIGITALPOWERLINE = 0x8a
+ IFT_DIGITALWRAPPEROVERHEADCHANNEL = 0xba
+ IFT_DLSW = 0x4a
+ IFT_DOCSCABLEDOWNSTREAM = 0x80
+ IFT_DOCSCABLEMACLAYER = 0x7f
+ IFT_DOCSCABLEUPSTREAM = 0x81
+ IFT_DS0 = 0x51
+ IFT_DS0BUNDLE = 0x52
+ IFT_DS1FDL = 0xaa
+ IFT_DS3 = 0x1e
+ IFT_DTM = 0x8c
+ IFT_DVBASILN = 0xac
+ IFT_DVBASIOUT = 0xad
+ IFT_DVBRCCDOWNSTREAM = 0x93
+ IFT_DVBRCCMACLAYER = 0x92
+ IFT_DVBRCCUPSTREAM = 0x94
+ IFT_ENC = 0xf4
+ IFT_EON = 0x19
+ IFT_EPLRS = 0x57
+ IFT_ESCON = 0x49
+ IFT_ETHER = 0x6
+ IFT_FAITH = 0xf2
+ IFT_FAST = 0x7d
+ IFT_FASTETHER = 0x3e
+ IFT_FASTETHERFX = 0x45
+ IFT_FDDI = 0xf
+ IFT_FIBRECHANNEL = 0x38
+ IFT_FRAMERELAYINTERCONNECT = 0x3a
+ IFT_FRAMERELAYMPI = 0x5c
+ IFT_FRDLCIENDPT = 0xc1
+ IFT_FRELAY = 0x20
+ IFT_FRELAYDCE = 0x2c
+ IFT_FRF16MFRBUNDLE = 0xa3
+ IFT_FRFORWARD = 0x9e
+ IFT_G703AT2MB = 0x43
+ IFT_G703AT64K = 0x42
+ IFT_GIF = 0xf0
+ IFT_GIGABITETHERNET = 0x75
+ IFT_GR303IDT = 0xb2
+ IFT_GR303RDT = 0xb1
+ IFT_H323GATEKEEPER = 0xa4
+ IFT_H323PROXY = 0xa5
+ IFT_HDH1822 = 0x3
+ IFT_HDLC = 0x76
+ IFT_HDSL2 = 0xa8
+ IFT_HIPERLAN2 = 0xb7
+ IFT_HIPPI = 0x2f
+ IFT_HIPPIINTERFACE = 0x39
+ IFT_HOSTPAD = 0x5a
+ IFT_HSSI = 0x2e
+ IFT_HY = 0xe
+ IFT_IBM370PARCHAN = 0x48
+ IFT_IDSL = 0x9a
+ IFT_IEEE1394 = 0x90
+ IFT_IEEE80211 = 0x47
+ IFT_IEEE80212 = 0x37
+ IFT_IEEE8023ADLAG = 0xa1
+ IFT_IFGSN = 0x91
+ IFT_IMT = 0xbe
+ IFT_INTERLEAVE = 0x7c
+ IFT_IP = 0x7e
+ IFT_IPFORWARD = 0x8e
+ IFT_IPOVERATM = 0x72
+ IFT_IPOVERCDLC = 0x6d
+ IFT_IPOVERCLAW = 0x6e
+ IFT_IPSWITCH = 0x4e
+ IFT_ISDN = 0x3f
+ IFT_ISDNBASIC = 0x14
+ IFT_ISDNPRIMARY = 0x15
+ IFT_ISDNS = 0x4b
+ IFT_ISDNU = 0x4c
+ IFT_ISO88022LLC = 0x29
+ IFT_ISO88023 = 0x7
+ IFT_ISO88024 = 0x8
+ IFT_ISO88025 = 0x9
+ IFT_ISO88025CRFPINT = 0x62
+ IFT_ISO88025DTR = 0x56
+ IFT_ISO88025FIBER = 0x73
+ IFT_ISO88026 = 0xa
+ IFT_ISUP = 0xb3
+ IFT_L2VLAN = 0x87
+ IFT_L3IPVLAN = 0x88
+ IFT_L3IPXVLAN = 0x89
+ IFT_LAPB = 0x10
+ IFT_LAPD = 0x4d
+ IFT_LAPF = 0x77
+ IFT_LOCALTALK = 0x2a
+ IFT_LOOP = 0x18
+ IFT_MEDIAMAILOVERIP = 0x8b
+ IFT_MFSIGLINK = 0xa7
+ IFT_MIOX25 = 0x26
+ IFT_MODEM = 0x30
+ IFT_MPC = 0x71
+ IFT_MPLS = 0xa6
+ IFT_MPLSTUNNEL = 0x96
+ IFT_MSDSL = 0x8f
+ IFT_MVL = 0xbf
+ IFT_MYRINET = 0x63
+ IFT_NFAS = 0xaf
+ IFT_NSIP = 0x1b
+ IFT_OPTICALCHANNEL = 0xc3
+ IFT_OPTICALTRANSPORT = 0xc4
+ IFT_OTHER = 0x1
+ IFT_P10 = 0xc
+ IFT_P80 = 0xd
+ IFT_PARA = 0x22
+ IFT_PFLOG = 0xf5
+ IFT_PFSYNC = 0xf6
+ IFT_PLC = 0xae
+ IFT_POS = 0xab
+ IFT_PPP = 0x17
+ IFT_PPPMULTILINKBUNDLE = 0x6c
+ IFT_PROPBWAP2MP = 0xb8
+ IFT_PROPCNLS = 0x59
+ IFT_PROPDOCSWIRELESSDOWNSTREAM = 0xb5
+ IFT_PROPDOCSWIRELESSMACLAYER = 0xb4
+ IFT_PROPDOCSWIRELESSUPSTREAM = 0xb6
+ IFT_PROPMUX = 0x36
+ IFT_PROPVIRTUAL = 0x35
+ IFT_PROPWIRELESSP2P = 0x9d
+ IFT_PTPSERIAL = 0x16
+ IFT_PVC = 0xf1
+ IFT_QLLC = 0x44
+ IFT_RADIOMAC = 0xbc
+ IFT_RADSL = 0x5f
+ IFT_REACHDSL = 0xc0
+ IFT_RFC1483 = 0x9f
+ IFT_RS232 = 0x21
+ IFT_RSRB = 0x4f
+ IFT_SDLC = 0x11
+ IFT_SDSL = 0x60
+ IFT_SHDSL = 0xa9
+ IFT_SIP = 0x1f
+ IFT_SLIP = 0x1c
+ IFT_SMDSDXI = 0x2b
+ IFT_SMDSICIP = 0x34
+ IFT_SONET = 0x27
+ IFT_SONETOVERHEADCHANNEL = 0xb9
+ IFT_SONETPATH = 0x32
+ IFT_SONETVT = 0x33
+ IFT_SRP = 0x97
+ IFT_SS7SIGLINK = 0x9c
+ IFT_STACKTOSTACK = 0x6f
+ IFT_STARLAN = 0xb
+ IFT_STF = 0xf3
+ IFT_T1 = 0x12
+ IFT_TDLC = 0x74
+ IFT_TERMPAD = 0x5b
+ IFT_TR008 = 0xb0
+ IFT_TRANSPHDLC = 0x7b
+ IFT_TUNNEL = 0x83
+ IFT_ULTRA = 0x1d
+ IFT_USB = 0xa0
+ IFT_V11 = 0x40
+ IFT_V35 = 0x2d
+ IFT_V36 = 0x41
+ IFT_V37 = 0x78
+ IFT_VDSL = 0x61
+ IFT_VIRTUALIPADDRESS = 0x70
+ IFT_VOICEEM = 0x64
+ IFT_VOICEENCAP = 0x67
+ IFT_VOICEFXO = 0x65
+ IFT_VOICEFXS = 0x66
+ IFT_VOICEOVERATM = 0x98
+ IFT_VOICEOVERFRAMERELAY = 0x99
+ IFT_VOICEOVERIP = 0x68
+ IFT_X213 = 0x5d
+ IFT_X25 = 0x5
+ IFT_X25DDN = 0x4
+ IFT_X25HUNTGROUP = 0x7a
+ IFT_X25MLP = 0x79
+ IFT_X25PLE = 0x28
+ IFT_XETHER = 0x1a
+ IGNBRK = 0x1
+ IGNCR = 0x80
+ IGNPAR = 0x4
+ IMAXBEL = 0x2000
+ INLCR = 0x40
+ INPCK = 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 = 0xfe
+ 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 = 0x0
+ 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 = 0x0
+ 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_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_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_UNKNOWN = 0x102
+ 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_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_MAXPACKET = 0xffff
+ 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_PKTOPTIONS = 0x34
+ IPV6_PORTRANGE = 0xe
+ IPV6_PORTRANGE_DEFAULT = 0x0
+ 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 = 0x0
+ IPV6_RTHDR_STRICT = 0x1
+ IPV6_RTHDR_TYPE_0 = 0x0
+ 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_DEFAULT_MULTICAST_LOOP = 0x1
+ IP_DEFAULT_MULTICAST_TTL = 0x1
+ IP_DF = 0x4000
+ IP_DROP_MEMBERSHIP = 0xd
+ IP_DUMMYNET_CONFIGURE = 0x3c
+ IP_DUMMYNET_DEL = 0x3d
+ IP_DUMMYNET_FLUSH = 0x3e
+ IP_DUMMYNET_GET = 0x40
+ IP_FAITH = 0x16
+ IP_FW_ADD = 0x32
+ IP_FW_DEL = 0x33
+ IP_FW_FLUSH = 0x34
+ IP_FW_GET = 0x36
+ IP_FW_RESETLOG = 0x37
+ IP_FW_ZERO = 0x35
+ IP_HDRINCL = 0x2
+ IP_IPSEC_POLICY = 0x15
+ IP_MAXPACKET = 0xffff
+ IP_MAX_MEMBERSHIPS = 0x14
+ IP_MF = 0x2000
+ IP_MINTTL = 0x42
+ IP_MSS = 0x240
+ IP_MULTICAST_IF = 0x9
+ IP_MULTICAST_LOOP = 0xb
+ IP_MULTICAST_TTL = 0xa
+ IP_MULTICAST_VIF = 0xe
+ IP_OFFMASK = 0x1fff
+ IP_OPTIONS = 0x1
+ IP_PORTRANGE = 0x13
+ IP_PORTRANGE_DEFAULT = 0x0
+ 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_TOS = 0x3
+ IP_TTL = 0x4
+ ISIG = 0x80
+ ISTRIP = 0x20
+ IXANY = 0x800
+ IXOFF = 0x400
+ IXON = 0x200
+ LOCK_EX = 0x2
+ LOCK_NB = 0x4
+ LOCK_SH = 0x1
+ LOCK_UN = 0x8
+ MADV_AUTOSYNC = 0x7
+ MADV_CONTROL_END = 0xb
+ MADV_CONTROL_START = 0xa
+ MADV_CORE = 0x9
+ MADV_DONTNEED = 0x4
+ MADV_FREE = 0x5
+ MADV_INVAL = 0xa
+ MADV_NOCORE = 0x8
+ MADV_NORMAL = 0x0
+ MADV_NOSYNC = 0x6
+ MADV_RANDOM = 0x1
+ MADV_SEQUENTIAL = 0x2
+ MADV_SETMAP = 0xb
+ MADV_WILLNEED = 0x3
+ MAP_ANON = 0x1000
+ MAP_COPY = 0x2
+ MAP_FILE = 0x0
+ MAP_FIXED = 0x10
+ MAP_HASSEMAPHORE = 0x200
+ MAP_INHERIT = 0x80
+ MAP_NOCORE = 0x20000
+ MAP_NOEXTEND = 0x100
+ MAP_NORESERVE = 0x40
+ MAP_NOSYNC = 0x800
+ MAP_PRIVATE = 0x2
+ MAP_RENAME = 0x20
+ MAP_SHARED = 0x1
+ MAP_SIZEALIGN = 0x40000
+ MAP_STACK = 0x400
+ MAP_TRYFIXED = 0x10000
+ MAP_VPAGETABLE = 0x2000
+ MCL_CURRENT = 0x1
+ MCL_FUTURE = 0x2
+ MSG_CTRUNC = 0x20
+ MSG_DONTROUTE = 0x4
+ MSG_DONTWAIT = 0x80
+ MSG_EOF = 0x100
+ MSG_EOR = 0x8
+ MSG_FBLOCKING = 0x10000
+ MSG_FMASK = 0xffff0000
+ MSG_FNONBLOCKING = 0x20000
+ MSG_NOSIGNAL = 0x400
+ MSG_NOTIFICATION = 0x200
+ MSG_OOB = 0x1
+ MSG_PEEK = 0x2
+ MSG_SYNC = 0x800
+ MSG_TRUNC = 0x10
+ MSG_WAITALL = 0x40
+ MS_ASYNC = 0x1
+ MS_INVALIDATE = 0x2
+ MS_SYNC = 0x0
+ NAME_MAX = 0xff
+ NET_RT_DUMP = 0x1
+ NET_RT_FLAGS = 0x2
+ NET_RT_IFLIST = 0x3
+ NET_RT_MAXID = 0x4
+ NOFLSH = 0x80000000
+ NOTE_ATTRIB = 0x8
+ NOTE_CHILD = 0x4
+ NOTE_DELETE = 0x1
+ NOTE_EXEC = 0x20000000
+ NOTE_EXIT = 0x80000000
+ NOTE_EXTEND = 0x4
+ NOTE_FORK = 0x40000000
+ NOTE_LINK = 0x10
+ NOTE_LOWAT = 0x1
+ NOTE_OOB = 0x2
+ NOTE_PCTRLMASK = 0xf0000000
+ NOTE_PDATAMASK = 0xfffff
+ NOTE_RENAME = 0x20
+ NOTE_REVOKE = 0x40
+ NOTE_TRACK = 0x1
+ NOTE_TRACKERR = 0x2
+ NOTE_WRITE = 0x2
+ OCRNL = 0x10
+ ONLCR = 0x2
+ ONLRET = 0x40
+ ONOCR = 0x20
+ ONOEOT = 0x8
+ OPOST = 0x1
+ O_ACCMODE = 0x3
+ O_APPEND = 0x8
+ O_ASYNC = 0x40
+ O_CLOEXEC = 0x20000
+ O_CREAT = 0x200
+ O_DIRECT = 0x10000
+ O_DIRECTORY = 0x8000000
+ O_EXCL = 0x800
+ O_EXLOCK = 0x20
+ O_FAPPEND = 0x100000
+ O_FASYNCWRITE = 0x800000
+ O_FBLOCKING = 0x40000
+ O_FBUFFERED = 0x2000000
+ O_FMASK = 0x7fc0000
+ O_FNONBLOCKING = 0x80000
+ O_FOFFSET = 0x200000
+ O_FSYNC = 0x80
+ O_FSYNCWRITE = 0x400000
+ O_FUNBUFFERED = 0x1000000
+ O_MAPONREAD = 0x4000000
+ O_NDELAY = 0x4
+ O_NOCTTY = 0x8000
+ O_NOFOLLOW = 0x100
+ O_NONBLOCK = 0x4
+ O_RDONLY = 0x0
+ O_RDWR = 0x2
+ O_SHLOCK = 0x10
+ O_SYNC = 0x80
+ O_TRUNC = 0x400
+ O_WRONLY = 0x1
+ PARENB = 0x1000
+ PARMRK = 0x8
+ PARODD = 0x2000
+ PENDIN = 0x20000000
+ PRIO_PGRP = 0x1
+ PRIO_PROCESS = 0x0
+ PRIO_USER = 0x2
+ PROT_EXEC = 0x4
+ PROT_NONE = 0x0
+ PROT_READ = 0x1
+ PROT_WRITE = 0x2
+ RLIMIT_AS = 0xa
+ RLIMIT_CORE = 0x4
+ RLIMIT_CPU = 0x0
+ RLIMIT_DATA = 0x2
+ RLIMIT_FSIZE = 0x1
+ RLIMIT_NOFILE = 0x8
+ RLIMIT_STACK = 0x3
+ RLIM_INFINITY = 0x7fffffffffffffff
+ RTAX_AUTHOR = 0x6
+ RTAX_BRD = 0x7
+ RTAX_DST = 0x0
+ RTAX_GATEWAY = 0x1
+ RTAX_GENMASK = 0x3
+ RTAX_IFA = 0x5
+ RTAX_IFP = 0x4
+ RTAX_MAX = 0xb
+ RTAX_MPLS1 = 0x8
+ RTAX_MPLS2 = 0x9
+ RTAX_MPLS3 = 0xa
+ 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_MPLS1 = 0x100
+ RTA_MPLS2 = 0x200
+ RTA_MPLS3 = 0x400
+ RTA_NETMASK = 0x4
+ RTF_BLACKHOLE = 0x1000
+ RTF_BROADCAST = 0x400000
+ RTF_CLONING = 0x100
+ RTF_DONE = 0x40
+ RTF_DYNAMIC = 0x10
+ RTF_GATEWAY = 0x2
+ RTF_HOST = 0x4
+ RTF_LLINFO = 0x400
+ RTF_LOCAL = 0x200000
+ RTF_MODIFIED = 0x20
+ RTF_MPLSOPS = 0x1000000
+ RTF_MULTICAST = 0x800000
+ RTF_PINNED = 0x100000
+ RTF_PRCLONING = 0x10000
+ RTF_PROTO1 = 0x8000
+ RTF_PROTO2 = 0x4000
+ RTF_PROTO3 = 0x40000
+ RTF_REJECT = 0x8
+ RTF_STATIC = 0x800
+ RTF_UP = 0x1
+ RTF_WASCLONED = 0x20000
+ 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 = 0x6
+ RTV_EXPIRE = 0x4
+ RTV_HOPCOUNT = 0x2
+ RTV_IWCAPSEGS = 0x400
+ RTV_IWMAXSEGS = 0x200
+ RTV_MSL = 0x100
+ RTV_MTU = 0x1
+ RTV_RPIPE = 0x8
+ RTV_RTT = 0x40
+ RTV_RTTVAR = 0x80
+ RTV_SPIPE = 0x10
+ RTV_SSTHRESH = 0x20
+ RUSAGE_CHILDREN = -0x1
+ RUSAGE_SELF = 0x0
+ SCM_CREDS = 0x3
+ SCM_RIGHTS = 0x1
+ SCM_TIMESTAMP = 0x2
+ SHUT_RD = 0x0
+ SHUT_RDWR = 0x2
+ SHUT_WR = 0x1
+ SIOCADDMULTI = 0x80206931
+ SIOCADDRT = 0x8030720a
+ SIOCAIFADDR = 0x8040691a
+ SIOCALIFADDR = 0x8118691b
+ SIOCATMARK = 0x40047307
+ SIOCDELMULTI = 0x80206932
+ SIOCDELRT = 0x8030720b
+ SIOCDIFADDR = 0x80206919
+ SIOCDIFPHYADDR = 0x80206949
+ SIOCDLIFADDR = 0x8118691d
+ SIOCGDRVSPEC = 0xc01c697b
+ SIOCGETSGCNT = 0xc0147210
+ SIOCGETVIFCNT = 0xc014720f
+ SIOCGHIWAT = 0x40047301
+ SIOCGIFADDR = 0xc0206921
+ SIOCGIFBRDADDR = 0xc0206923
+ SIOCGIFCAP = 0xc020691f
+ SIOCGIFCONF = 0xc0086924
+ SIOCGIFDATA = 0xc0206926
+ SIOCGIFDSTADDR = 0xc0206922
+ SIOCGIFFLAGS = 0xc0206911
+ SIOCGIFGENERIC = 0xc020693a
+ SIOCGIFGMEMB = 0xc024698a
+ SIOCGIFINDEX = 0xc0206920
+ SIOCGIFMEDIA = 0xc0286938
+ SIOCGIFMETRIC = 0xc0206917
+ SIOCGIFMTU = 0xc0206933
+ SIOCGIFNETMASK = 0xc0206925
+ SIOCGIFPDSTADDR = 0xc0206948
+ SIOCGIFPHYS = 0xc0206935
+ SIOCGIFPOLLCPU = 0xc020697e
+ SIOCGIFPSRCADDR = 0xc0206947
+ SIOCGIFSTATUS = 0xc331693b
+ SIOCGIFTSOLEN = 0xc0206980
+ 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
+ SIOCSIFDSTADDR = 0x8020690e
+ SIOCSIFFLAGS = 0x80206910
+ SIOCSIFGENERIC = 0x80206939
+ SIOCSIFLLADDR = 0x8020693c
+ SIOCSIFMEDIA = 0xc0206937
+ SIOCSIFMETRIC = 0x80206918
+ SIOCSIFMTU = 0x80206934
+ SIOCSIFNAME = 0x80206928
+ SIOCSIFNETMASK = 0x80206916
+ SIOCSIFPHYADDR = 0x80406946
+ SIOCSIFPHYS = 0x80206936
+ SIOCSIFPOLLCPU = 0x8020697d
+ SIOCSIFTSOLEN = 0x8020697f
+ 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_BROADCAST = 0x20
+ SO_DEBUG = 0x1
+ SO_DONTROUTE = 0x10
+ SO_ERROR = 0x1007
+ SO_KEEPALIVE = 0x8
+ SO_LINGER = 0x80
+ SO_NOSIGPIPE = 0x800
+ SO_OOBINLINE = 0x100
+ SO_RCVBUF = 0x1002
+ SO_RCVLOWAT = 0x1004
+ SO_RCVTIMEO = 0x1006
+ SO_REUSEADDR = 0x4
+ SO_REUSEPORT = 0x200
+ SO_SNDBUF = 0x1001
+ SO_SNDLOWAT = 0x1003
+ SO_SNDSPACE = 0x100a
+ SO_SNDTIMEO = 0x1005
+ SO_TIMESTAMP = 0x400
+ SO_TYPE = 0x1008
+ SO_USELOOPBACK = 0x40
+ TCIFLUSH = 0x1
+ TCIOFLUSH = 0x3
+ TCOFLUSH = 0x2
+ TCP_FASTKEEP = 0x80
+ TCP_KEEPCNT = 0x400
+ TCP_KEEPIDLE = 0x100
+ TCP_KEEPINIT = 0x20
+ TCP_KEEPINTVL = 0x200
+ TCP_MAXBURST = 0x4
+ TCP_MAXHLEN = 0x3c
+ TCP_MAXOLEN = 0x28
+ TCP_MAXSEG = 0x2
+ TCP_MAXWIN = 0xffff
+ TCP_MAX_WINSHIFT = 0xe
+ TCP_MINMSS = 0x100
+ TCP_MIN_WINSHIFT = 0x5
+ TCP_MSS = 0x200
+ TCP_NODELAY = 0x1
+ TCP_NOOPT = 0x8
+ TCP_NOPUSH = 0x4
+ TCP_SIGNATURE_ENABLE = 0x10
+ TCSAFLUSH = 0x2
+ TIOCCBRK = 0x2000747a
+ TIOCCDTR = 0x20007478
+ TIOCCONS = 0x80047462
+ TIOCDCDTIMESTAMP = 0x40087458
+ TIOCDRAIN = 0x2000745e
+ TIOCEXCL = 0x2000740d
+ TIOCEXT = 0x80047460
+ TIOCFLUSH = 0x80047410
+ TIOCGDRAINWAIT = 0x40047456
+ TIOCGETA = 0x402c7413
+ TIOCGETD = 0x4004741a
+ TIOCGPGRP = 0x40047477
+ TIOCGSID = 0x40047463
+ TIOCGSIZE = 0x40087468
+ TIOCGWINSZ = 0x40087468
+ TIOCISPTMASTER = 0x20007455
+ TIOCMBIC = 0x8004746b
+ TIOCMBIS = 0x8004746c
+ TIOCMGDTRWAIT = 0x4004745a
+ TIOCMGET = 0x4004746a
+ TIOCMODG = 0x40047403
+ TIOCMODS = 0x80047404
+ TIOCMSDTRWAIT = 0x8004745b
+ TIOCMSET = 0x8004746d
+ TIOCM_CAR = 0x40
+ TIOCM_CD = 0x40
+ TIOCM_CTS = 0x20
+ TIOCM_DSR = 0x100
+ TIOCM_DTR = 0x2
+ TIOCM_LE = 0x1
+ TIOCM_RI = 0x80
+ TIOCM_RNG = 0x80
+ TIOCM_RTS = 0x4
+ TIOCM_SR = 0x10
+ TIOCM_ST = 0x8
+ TIOCNOTTY = 0x20007471
+ TIOCNXCL = 0x2000740e
+ TIOCOUTQ = 0x40047473
+ TIOCPKT = 0x80047470
+ TIOCPKT_DATA = 0x0
+ TIOCPKT_DOSTOP = 0x20
+ TIOCPKT_FLUSHREAD = 0x1
+ TIOCPKT_FLUSHWRITE = 0x2
+ TIOCPKT_IOCTL = 0x40
+ TIOCPKT_NOSTOP = 0x10
+ TIOCPKT_START = 0x8
+ TIOCPKT_STOP = 0x4
+ TIOCREMOTE = 0x80047469
+ TIOCSBRK = 0x2000747b
+ TIOCSCTTY = 0x20007461
+ TIOCSDRAINWAIT = 0x80047457
+ TIOCSDTR = 0x20007479
+ TIOCSETA = 0x802c7414
+ TIOCSETAF = 0x802c7416
+ TIOCSETAW = 0x802c7415
+ TIOCSETD = 0x8004741b
+ TIOCSIG = 0x2000745f
+ TIOCSPGRP = 0x80047476
+ TIOCSSIZE = 0x80087467
+ TIOCSTART = 0x2000746e
+ TIOCSTAT = 0x20007465
+ TIOCSTI = 0x80017472
+ TIOCSTOP = 0x2000746f
+ TIOCSWINSZ = 0x80087467
+ TIOCTIMESTAMP = 0x40087459
+ TIOCUCNTL = 0x80047466
+ TOSTOP = 0x400000
+ VCHECKPT = 0x13
+ VDISCARD = 0xf
+ VDSUSP = 0xb
+ VEOF = 0x0
+ VEOL = 0x1
+ VEOL2 = 0x2
+ VERASE = 0x3
+ VERASE2 = 0x7
+ VINTR = 0x8
+ VKILL = 0x5
+ VLNEXT = 0xe
+ VMIN = 0x10
+ VQUIT = 0x9
+ VREPRINT = 0x6
+ VSTART = 0xc
+ VSTATUS = 0x12
+ VSTOP = 0xd
+ VSUSP = 0xa
+ VTIME = 0x11
+ VWERASE = 0x4
+ WCONTINUED = 0x4
+ WCOREFLAG = 0x80
+ WLINUXCLONE = 0x80000000
+ WNOHANG = 0x1
+ WSTOPPED = 0x7f
+ WUNTRACED = 0x2
+)
+
+// Errors
+const (
+ E2BIG = Errno(0x7)
+ EACCES = Errno(0xd)
+ EADDRINUSE = Errno(0x30)
+ EADDRNOTAVAIL = Errno(0x31)
+ EAFNOSUPPORT = Errno(0x2f)
+ EAGAIN = Errno(0x23)
+ EALREADY = Errno(0x25)
+ EASYNC = Errno(0x63)
+ EAUTH = Errno(0x50)
+ EBADF = Errno(0x9)
+ EBADMSG = Errno(0x59)
+ EBADRPC = Errno(0x48)
+ EBUSY = Errno(0x10)
+ ECANCELED = Errno(0x55)
+ ECHILD = Errno(0xa)
+ ECONNABORTED = Errno(0x35)
+ ECONNREFUSED = Errno(0x3d)
+ ECONNRESET = Errno(0x36)
+ EDEADLK = Errno(0xb)
+ EDESTADDRREQ = Errno(0x27)
+ EDOM = Errno(0x21)
+ EDOOFUS = Errno(0x58)
+ EDQUOT = Errno(0x45)
+ EEXIST = Errno(0x11)
+ EFAULT = Errno(0xe)
+ EFBIG = Errno(0x1b)
+ EFTYPE = Errno(0x4f)
+ EHOSTDOWN = Errno(0x40)
+ EHOSTUNREACH = Errno(0x41)
+ EIDRM = Errno(0x52)
+ EILSEQ = Errno(0x56)
+ EINPROGRESS = Errno(0x24)
+ EINTR = Errno(0x4)
+ EINVAL = Errno(0x16)
+ EIO = Errno(0x5)
+ EISCONN = Errno(0x38)
+ EISDIR = Errno(0x15)
+ ELAST = Errno(0x63)
+ ELOOP = Errno(0x3e)
+ EMFILE = Errno(0x18)
+ EMLINK = Errno(0x1f)
+ EMSGSIZE = Errno(0x28)
+ EMULTIHOP = Errno(0x5a)
+ ENAMETOOLONG = Errno(0x3f)
+ ENEEDAUTH = Errno(0x51)
+ ENETDOWN = Errno(0x32)
+ ENETRESET = Errno(0x34)
+ ENETUNREACH = Errno(0x33)
+ ENFILE = Errno(0x17)
+ ENOATTR = Errno(0x57)
+ ENOBUFS = Errno(0x37)
+ ENODEV = Errno(0x13)
+ ENOENT = Errno(0x2)
+ ENOEXEC = Errno(0x8)
+ ENOLCK = Errno(0x4d)
+ ENOLINK = Errno(0x5b)
+ ENOMEDIUM = Errno(0x5d)
+ ENOMEM = Errno(0xc)
+ ENOMSG = Errno(0x53)
+ ENOPROTOOPT = Errno(0x2a)
+ ENOSPC = Errno(0x1c)
+ ENOSYS = Errno(0x4e)
+ ENOTBLK = Errno(0xf)
+ ENOTCONN = Errno(0x39)
+ ENOTDIR = Errno(0x14)
+ ENOTEMPTY = Errno(0x42)
+ ENOTSOCK = Errno(0x26)
+ ENOTSUP = Errno(0x2d)
+ ENOTTY = Errno(0x19)
+ ENXIO = Errno(0x6)
+ EOPNOTSUPP = Errno(0x2d)
+ EOVERFLOW = Errno(0x54)
+ EPERM = Errno(0x1)
+ EPFNOSUPPORT = Errno(0x2e)
+ EPIPE = Errno(0x20)
+ EPROCLIM = Errno(0x43)
+ EPROCUNAVAIL = Errno(0x4c)
+ EPROGMISMATCH = Errno(0x4b)
+ EPROGUNAVAIL = Errno(0x4a)
+ EPROTO = Errno(0x5c)
+ EPROTONOSUPPORT = Errno(0x2b)
+ EPROTOTYPE = Errno(0x29)
+ ERANGE = Errno(0x22)
+ EREMOTE = Errno(0x47)
+ EROFS = Errno(0x1e)
+ ERPCMISMATCH = Errno(0x49)
+ ESHUTDOWN = Errno(0x3a)
+ ESOCKTNOSUPPORT = Errno(0x2c)
+ ESPIPE = Errno(0x1d)
+ ESRCH = Errno(0x3)
+ ESTALE = Errno(0x46)
+ ETIMEDOUT = Errno(0x3c)
+ ETOOMANYREFS = Errno(0x3b)
+ ETXTBSY = Errno(0x1a)
+ EUNUSED94 = Errno(0x5e)
+ EUNUSED95 = Errno(0x5f)
+ EUNUSED96 = Errno(0x60)
+ EUNUSED97 = Errno(0x61)
+ EUNUSED98 = Errno(0x62)
+ EUSERS = Errno(0x44)
+ EWOULDBLOCK = Errno(0x23)
+ EXDEV = Errno(0x12)
+)
+
+// Signals
+const (
+ SIGABRT = Signal(0x6)
+ SIGALRM = Signal(0xe)
+ SIGBUS = Signal(0xa)
+ SIGCHLD = Signal(0x14)
+ SIGCKPT = Signal(0x21)
+ SIGCKPTEXIT = Signal(0x22)
+ SIGCONT = Signal(0x13)
+ SIGEMT = Signal(0x7)
+ SIGFPE = Signal(0x8)
+ SIGHUP = Signal(0x1)
+ SIGILL = Signal(0x4)
+ SIGINFO = Signal(0x1d)
+ SIGINT = Signal(0x2)
+ SIGIO = Signal(0x17)
+ SIGIOT = Signal(0x6)
+ SIGKILL = Signal(0x9)
+ SIGPIPE = Signal(0xd)
+ SIGPROF = Signal(0x1b)
+ SIGQUIT = Signal(0x3)
+ SIGSEGV = Signal(0xb)
+ SIGSTOP = Signal(0x11)
+ SIGSYS = Signal(0xc)
+ SIGTERM = Signal(0xf)
+ SIGTHR = Signal(0x20)
+ SIGTRAP = Signal(0x5)
+ SIGTSTP = Signal(0x12)
+ SIGTTIN = Signal(0x15)
+ SIGTTOU = Signal(0x16)
+ SIGURG = Signal(0x10)
+ SIGUSR1 = Signal(0x1e)
+ SIGUSR2 = Signal(0x1f)
+ SIGVTALRM = Signal(0x1a)
+ SIGWINCH = Signal(0x1c)
+ SIGXCPU = Signal(0x18)
+ SIGXFSZ = Signal(0x19)
+)
+
+// Error table
+var errors = [...]string{
+ 1: "operation not permitted",
+ 2: "no such file or directory",
+ 3: "no such process",
+ 4: "interrupted system call",
+ 5: "input/output error",
+ 6: "device not configured",
+ 7: "argument list too long",
+ 8: "exec format error",
+ 9: "bad file descriptor",
+ 10: "no child processes",
+ 11: "resource deadlock avoided",
+ 12: "cannot allocate memory",
+ 13: "permission denied",
+ 14: "bad address",
+ 15: "block device required",
+ 16: "device busy",
+ 17: "file exists",
+ 18: "cross-device link",
+ 19: "operation not supported by device",
+ 20: "not a directory",
+ 21: "is a directory",
+ 22: "invalid argument",
+ 23: "too many open files in system",
+ 24: "too many open files",
+ 25: "inappropriate ioctl for device",
+ 26: "text file busy",
+ 27: "file too large",
+ 28: "no space left on device",
+ 29: "illegal seek",
+ 30: "read-only file system",
+ 31: "too many links",
+ 32: "broken pipe",
+ 33: "numerical argument out of domain",
+ 34: "result too large",
+ 35: "resource temporarily unavailable",
+ 36: "operation now in progress",
+ 37: "operation already in progress",
+ 38: "socket operation on non-socket",
+ 39: "destination address required",
+ 40: "message too long",
+ 41: "protocol wrong type for socket",
+ 42: "protocol not available",
+ 43: "protocol not supported",
+ 44: "socket type not supported",
+ 45: "operation not supported",
+ 46: "protocol family not supported",
+ 47: "address family not supported by protocol family",
+ 48: "address already in use",
+ 49: "can't assign requested address",
+ 50: "network is down",
+ 51: "network is unreachable",
+ 52: "network dropped connection on reset",
+ 53: "software caused connection abort",
+ 54: "connection reset by peer",
+ 55: "no buffer space available",
+ 56: "socket is already connected",
+ 57: "socket is not connected",
+ 58: "can't send after socket shutdown",
+ 59: "too many references: can't splice",
+ 60: "operation timed out",
+ 61: "connection refused",
+ 62: "too many levels of symbolic links",
+ 63: "file name too long",
+ 64: "host is down",
+ 65: "no route to host",
+ 66: "directory not empty",
+ 67: "too many processes",
+ 68: "too many users",
+ 69: "disc quota exceeded",
+ 70: "stale NFS file handle",
+ 71: "too many levels of remote in path",
+ 72: "RPC struct is bad",
+ 73: "RPC version wrong",
+ 74: "RPC prog. not avail",
+ 75: "program version wrong",
+ 76: "bad procedure for program",
+ 77: "no locks available",
+ 78: "function not implemented",
+ 79: "inappropriate file type or format",
+ 80: "authentication error",
+ 81: "need authenticator",
+ 82: "identifier removed",
+ 83: "no message of desired type",
+ 84: "value too large to be stored in data type",
+ 85: "operation canceled",
+ 86: "illegal byte sequence",
+ 87: "attribute not found",
+ 88: "programming error",
+ 89: "bad message",
+ 90: "multihop attempted",
+ 91: "link has been severed",
+ 92: "protocol error",
+ 93: "unknown error: 93",
+ 94: "unknown error: 94",
+ 95: "unknown error: 95",
+ 96: "unknown error: 96",
+ 97: "unknown error: 97",
+ 98: "unknown error: 98",
+ 99: "unknown error: 99",
+}
+
+// Signal table
+var signals = [...]string{
+ 1: "hangup",
+ 2: "interrupt",
+ 3: "quit",
+ 4: "illegal instruction",
+ 5: "trace/BPT trap",
+ 6: "abort trap",
+ 7: "EMT trap",
+ 8: "floating point exception",
+ 9: "killed",
+ 10: "bus error",
+ 11: "segmentation fault",
+ 12: "bad system call",
+ 13: "broken pipe",
+ 14: "alarm clock",
+ 15: "terminated",
+ 16: "urgent I/O condition",
+ 17: "suspended (signal)",
+ 18: "suspended",
+ 19: "continued",
+ 20: "child exited",
+ 21: "stopped (tty input)",
+ 22: "stopped (tty output)",
+ 23: "I/O possible",
+ 24: "cputime limit exceeded",
+ 25: "filesize limit exceeded",
+ 26: "virtual timer expired",
+ 27: "profiling timer expired",
+ 28: "window size changes",
+ 29: "information request",
+ 30: "user defined signal 1",
+ 31: "user defined signal 2",
+ 32: "thread Scheduler",
+ 33: "checkPoint",
+ 34: "checkPointExit",
+}
diff --git a/src/pkg/syscall/zerrors_dragonfly_amd64.go b/src/pkg/syscall/zerrors_dragonfly_amd64.go
new file mode 100644
index 000000000..d2fe97c68
--- /dev/null
+++ b/src/pkg/syscall/zerrors_dragonfly_amd64.go
@@ -0,0 +1,1523 @@
+// mkerrors.sh -m64
+// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs -- -m64 _const.go
+
+package syscall
+
+const (
+ AF_APPLETALK = 0x10
+ AF_ATM = 0x1e
+ AF_BLUETOOTH = 0x21
+ 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 = 0x23
+ 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 = 0x24
+ AF_MPLS = 0x22
+ AF_NATM = 0x1d
+ AF_NETGRAPH = 0x20
+ AF_NS = 0x6
+ AF_OSI = 0x7
+ AF_PUP = 0x4
+ AF_ROUTE = 0x11
+ AF_SIP = 0x18
+ AF_SNA = 0xb
+ AF_UNIX = 0x1
+ AF_UNSPEC = 0x0
+ B0 = 0x0
+ B110 = 0x6e
+ B115200 = 0x1c200
+ B1200 = 0x4b0
+ B134 = 0x86
+ B14400 = 0x3840
+ B150 = 0x96
+ B1800 = 0x708
+ B19200 = 0x4b00
+ B200 = 0xc8
+ B230400 = 0x38400
+ B2400 = 0x960
+ B28800 = 0x7080
+ B300 = 0x12c
+ B38400 = 0x9600
+ B4800 = 0x12c0
+ B50 = 0x32
+ B57600 = 0xe100
+ B600 = 0x258
+ B7200 = 0x1c20
+ B75 = 0x4b
+ B76800 = 0x12c00
+ B9600 = 0x2580
+ BIOCFLUSH = 0x20004268
+ BIOCGBLEN = 0x40044266
+ BIOCGDLT = 0x4004426a
+ BIOCGDLTLIST = 0xc0104279
+ BIOCGETIF = 0x4020426b
+ BIOCGHDRCMPLT = 0x40044274
+ BIOCGRSIG = 0x40044272
+ BIOCGRTIMEOUT = 0x4010426e
+ BIOCGSEESENT = 0x40044276
+ BIOCGSTATS = 0x4008426f
+ BIOCIMMEDIATE = 0x80044270
+ BIOCLOCK = 0x2000427a
+ BIOCPROMISC = 0x20004269
+ BIOCSBLEN = 0xc0044266
+ BIOCSDLT = 0x80044278
+ BIOCSETF = 0x80104267
+ BIOCSETIF = 0x8020426c
+ BIOCSETWF = 0x8010427b
+ BIOCSHDRCMPLT = 0x80044275
+ BIOCSRSIG = 0x80044273
+ BIOCSRTIMEOUT = 0x8010426d
+ BIOCSSEESENT = 0x80044277
+ BIOCVERSION = 0x40044271
+ BPF_A = 0x10
+ BPF_ABS = 0x20
+ BPF_ADD = 0x0
+ BPF_ALIGNMENT = 0x8
+ BPF_ALU = 0x4
+ BPF_AND = 0x50
+ BPF_B = 0x10
+ BPF_DEFAULTBUFSIZE = 0x1000
+ BPF_DIV = 0x30
+ BPF_H = 0x8
+ BPF_IMM = 0x0
+ BPF_IND = 0x40
+ BPF_JA = 0x0
+ BPF_JEQ = 0x10
+ BPF_JGE = 0x30
+ BPF_JGT = 0x20
+ BPF_JMP = 0x5
+ BPF_JSET = 0x40
+ BPF_K = 0x0
+ BPF_LD = 0x0
+ BPF_LDX = 0x1
+ BPF_LEN = 0x80
+ BPF_LSH = 0x60
+ BPF_MAJOR_VERSION = 0x1
+ BPF_MAXBUFSIZE = 0x80000
+ BPF_MAXINSNS = 0x200
+ BPF_MAX_CLONES = 0x80
+ 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 = 0x0
+ BPF_TXA = 0x80
+ BPF_W = 0x0
+ BPF_X = 0x8
+ BRKINT = 0x2
+ CFLUSH = 0xf
+ CLOCAL = 0x8000
+ CREAD = 0x800
+ CS5 = 0x0
+ CS6 = 0x100
+ CS7 = 0x200
+ CS8 = 0x300
+ CSIZE = 0x300
+ CSTART = 0x11
+ CSTATUS = 0x14
+ CSTOP = 0x13
+ CSTOPB = 0x400
+ CSUSP = 0x1a
+ CTL_MAXNAME = 0xc
+ 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_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 = 0x0
+ DLT_PCI_EXP = 0x7d
+ DLT_PFLOG = 0x75
+ DLT_PFSYNC = 0x12
+ 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_PRISM_HEADER = 0x77
+ DLT_PRONET = 0x4
+ DLT_RAIF1 = 0xc6
+ DLT_RAW = 0xc
+ DLT_REDBACK_SMARTEDGE = 0x20
+ 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_X2E_SERIAL = 0xd5
+ DLT_X2E_XORAYA = 0xd6
+ DT_BLK = 0x6
+ DT_CHR = 0x2
+ DT_DBF = 0xf
+ DT_DIR = 0x4
+ DT_FIFO = 0x1
+ DT_LNK = 0xa
+ DT_REG = 0x8
+ DT_SOCK = 0xc
+ DT_UNKNOWN = 0x0
+ DT_WHT = 0xe
+ ECHO = 0x8
+ ECHOCTL = 0x40
+ ECHOE = 0x2
+ ECHOK = 0x4
+ ECHOKE = 0x1
+ ECHONL = 0x10
+ ECHOPRT = 0x20
+ EVFILT_AIO = -0x3
+ EVFILT_EXCEPT = -0x8
+ EVFILT_MARKER = 0xf
+ EVFILT_PROC = -0x5
+ EVFILT_READ = -0x1
+ EVFILT_SIGNAL = -0x6
+ EVFILT_SYSCOUNT = 0x8
+ EVFILT_TIMER = -0x7
+ EVFILT_VNODE = -0x4
+ EVFILT_WRITE = -0x2
+ EV_ADD = 0x1
+ EV_CLEAR = 0x20
+ EV_DELETE = 0x2
+ EV_DISABLE = 0x8
+ EV_ENABLE = 0x4
+ EV_EOF = 0x8000
+ EV_ERROR = 0x4000
+ EV_FLAG1 = 0x2000
+ EV_NODATA = 0x1000
+ EV_ONESHOT = 0x10
+ EV_SYSFLAGS = 0xf000
+ EXTA = 0x4b00
+ EXTB = 0x9600
+ EXTEXIT_LWP = 0x10000
+ EXTEXIT_PROC = 0x0
+ EXTEXIT_SETINT = 0x1
+ EXTEXIT_SIMPLE = 0x0
+ EXTPROC = 0x800
+ FD_CLOEXEC = 0x1
+ FD_SETSIZE = 0x400
+ FLUSHO = 0x800000
+ F_DUPFD = 0x0
+ F_GETFD = 0x1
+ F_GETFL = 0x3
+ F_GETLK = 0x7
+ F_GETOWN = 0x5
+ F_OK = 0x0
+ F_RDLCK = 0x1
+ F_SETFD = 0x2
+ F_SETFL = 0x4
+ F_SETLK = 0x8
+ F_SETLKW = 0x9
+ F_SETOWN = 0x6
+ F_UNLCK = 0x2
+ F_WRLCK = 0x3
+ HUPCL = 0x4000
+ ICANON = 0x100
+ ICMP6_FILTER = 0x12
+ ICRNL = 0x100
+ IEXTEN = 0x400
+ IFAN_ARRIVAL = 0x0
+ IFAN_DEPARTURE = 0x1
+ IFF_ALLMULTI = 0x200
+ IFF_ALTPHYS = 0x4000
+ IFF_BROADCAST = 0x2
+ IFF_CANTCHANGE = 0x118e72
+ IFF_DEBUG = 0x4
+ IFF_LINK0 = 0x1000
+ IFF_LINK1 = 0x2000
+ IFF_LINK2 = 0x4000
+ IFF_LOOPBACK = 0x8
+ IFF_MONITOR = 0x40000
+ IFF_MULTICAST = 0x8000
+ IFF_NOARP = 0x80
+ IFF_NPOLLING = 0x100000
+ IFF_OACTIVE = 0x400
+ IFF_OACTIVE_COMPAT = 0x400
+ IFF_POINTOPOINT = 0x10
+ IFF_POLLING = 0x10000
+ IFF_POLLING_COMPAT = 0x10000
+ IFF_PPROMISC = 0x20000
+ IFF_PROMISC = 0x100
+ IFF_RUNNING = 0x40
+ IFF_SIMPLEX = 0x800
+ IFF_SMART = 0x20
+ IFF_STATICARP = 0x80000
+ IFF_UP = 0x1
+ IFNAMSIZ = 0x10
+ IFT_1822 = 0x2
+ IFT_A12MPPSWITCH = 0x82
+ IFT_AAL2 = 0xbb
+ IFT_AAL5 = 0x31
+ IFT_ADSL = 0x5e
+ IFT_AFLANE8023 = 0x3b
+ IFT_AFLANE8025 = 0x3c
+ IFT_ARAP = 0x58
+ IFT_ARCNET = 0x23
+ IFT_ARCNETPLUS = 0x24
+ IFT_ASYNC = 0x54
+ IFT_ATM = 0x25
+ IFT_ATMDXI = 0x69
+ IFT_ATMFUNI = 0x6a
+ IFT_ATMIMA = 0x6b
+ IFT_ATMLOGICAL = 0x50
+ IFT_ATMRADIO = 0xbd
+ IFT_ATMSUBINTERFACE = 0x86
+ IFT_ATMVCIENDPT = 0xc2
+ IFT_ATMVIRTUAL = 0x95
+ IFT_BGPPOLICYACCOUNTING = 0xa2
+ IFT_BRIDGE = 0xd1
+ IFT_BSC = 0x53
+ IFT_CARP = 0xf8
+ IFT_CCTEMUL = 0x3d
+ IFT_CEPT = 0x13
+ IFT_CES = 0x85
+ IFT_CHANNEL = 0x46
+ IFT_CNR = 0x55
+ IFT_COFFEE = 0x84
+ IFT_COMPOSITELINK = 0x9b
+ IFT_DCN = 0x8d
+ IFT_DIGITALPOWERLINE = 0x8a
+ IFT_DIGITALWRAPPEROVERHEADCHANNEL = 0xba
+ IFT_DLSW = 0x4a
+ IFT_DOCSCABLEDOWNSTREAM = 0x80
+ IFT_DOCSCABLEMACLAYER = 0x7f
+ IFT_DOCSCABLEUPSTREAM = 0x81
+ IFT_DS0 = 0x51
+ IFT_DS0BUNDLE = 0x52
+ IFT_DS1FDL = 0xaa
+ IFT_DS3 = 0x1e
+ IFT_DTM = 0x8c
+ IFT_DVBASILN = 0xac
+ IFT_DVBASIOUT = 0xad
+ IFT_DVBRCCDOWNSTREAM = 0x93
+ IFT_DVBRCCMACLAYER = 0x92
+ IFT_DVBRCCUPSTREAM = 0x94
+ IFT_ENC = 0xf4
+ IFT_EON = 0x19
+ IFT_EPLRS = 0x57
+ IFT_ESCON = 0x49
+ IFT_ETHER = 0x6
+ IFT_FAITH = 0xf2
+ IFT_FAST = 0x7d
+ IFT_FASTETHER = 0x3e
+ IFT_FASTETHERFX = 0x45
+ IFT_FDDI = 0xf
+ IFT_FIBRECHANNEL = 0x38
+ IFT_FRAMERELAYINTERCONNECT = 0x3a
+ IFT_FRAMERELAYMPI = 0x5c
+ IFT_FRDLCIENDPT = 0xc1
+ IFT_FRELAY = 0x20
+ IFT_FRELAYDCE = 0x2c
+ IFT_FRF16MFRBUNDLE = 0xa3
+ IFT_FRFORWARD = 0x9e
+ IFT_G703AT2MB = 0x43
+ IFT_G703AT64K = 0x42
+ IFT_GIF = 0xf0
+ IFT_GIGABITETHERNET = 0x75
+ IFT_GR303IDT = 0xb2
+ IFT_GR303RDT = 0xb1
+ IFT_H323GATEKEEPER = 0xa4
+ IFT_H323PROXY = 0xa5
+ IFT_HDH1822 = 0x3
+ IFT_HDLC = 0x76
+ IFT_HDSL2 = 0xa8
+ IFT_HIPERLAN2 = 0xb7
+ IFT_HIPPI = 0x2f
+ IFT_HIPPIINTERFACE = 0x39
+ IFT_HOSTPAD = 0x5a
+ IFT_HSSI = 0x2e
+ IFT_HY = 0xe
+ IFT_IBM370PARCHAN = 0x48
+ IFT_IDSL = 0x9a
+ IFT_IEEE1394 = 0x90
+ IFT_IEEE80211 = 0x47
+ IFT_IEEE80212 = 0x37
+ IFT_IEEE8023ADLAG = 0xa1
+ IFT_IFGSN = 0x91
+ IFT_IMT = 0xbe
+ IFT_INTERLEAVE = 0x7c
+ IFT_IP = 0x7e
+ IFT_IPFORWARD = 0x8e
+ IFT_IPOVERATM = 0x72
+ IFT_IPOVERCDLC = 0x6d
+ IFT_IPOVERCLAW = 0x6e
+ IFT_IPSWITCH = 0x4e
+ IFT_ISDN = 0x3f
+ IFT_ISDNBASIC = 0x14
+ IFT_ISDNPRIMARY = 0x15
+ IFT_ISDNS = 0x4b
+ IFT_ISDNU = 0x4c
+ IFT_ISO88022LLC = 0x29
+ IFT_ISO88023 = 0x7
+ IFT_ISO88024 = 0x8
+ IFT_ISO88025 = 0x9
+ IFT_ISO88025CRFPINT = 0x62
+ IFT_ISO88025DTR = 0x56
+ IFT_ISO88025FIBER = 0x73
+ IFT_ISO88026 = 0xa
+ IFT_ISUP = 0xb3
+ IFT_L2VLAN = 0x87
+ IFT_L3IPVLAN = 0x88
+ IFT_L3IPXVLAN = 0x89
+ IFT_LAPB = 0x10
+ IFT_LAPD = 0x4d
+ IFT_LAPF = 0x77
+ IFT_LOCALTALK = 0x2a
+ IFT_LOOP = 0x18
+ IFT_MEDIAMAILOVERIP = 0x8b
+ IFT_MFSIGLINK = 0xa7
+ IFT_MIOX25 = 0x26
+ IFT_MODEM = 0x30
+ IFT_MPC = 0x71
+ IFT_MPLS = 0xa6
+ IFT_MPLSTUNNEL = 0x96
+ IFT_MSDSL = 0x8f
+ IFT_MVL = 0xbf
+ IFT_MYRINET = 0x63
+ IFT_NFAS = 0xaf
+ IFT_NSIP = 0x1b
+ IFT_OPTICALCHANNEL = 0xc3
+ IFT_OPTICALTRANSPORT = 0xc4
+ IFT_OTHER = 0x1
+ IFT_P10 = 0xc
+ IFT_P80 = 0xd
+ IFT_PARA = 0x22
+ IFT_PFLOG = 0xf5
+ IFT_PFSYNC = 0xf6
+ IFT_PLC = 0xae
+ IFT_POS = 0xab
+ IFT_PPP = 0x17
+ IFT_PPPMULTILINKBUNDLE = 0x6c
+ IFT_PROPBWAP2MP = 0xb8
+ IFT_PROPCNLS = 0x59
+ IFT_PROPDOCSWIRELESSDOWNSTREAM = 0xb5
+ IFT_PROPDOCSWIRELESSMACLAYER = 0xb4
+ IFT_PROPDOCSWIRELESSUPSTREAM = 0xb6
+ IFT_PROPMUX = 0x36
+ IFT_PROPVIRTUAL = 0x35
+ IFT_PROPWIRELESSP2P = 0x9d
+ IFT_PTPSERIAL = 0x16
+ IFT_PVC = 0xf1
+ IFT_QLLC = 0x44
+ IFT_RADIOMAC = 0xbc
+ IFT_RADSL = 0x5f
+ IFT_REACHDSL = 0xc0
+ IFT_RFC1483 = 0x9f
+ IFT_RS232 = 0x21
+ IFT_RSRB = 0x4f
+ IFT_SDLC = 0x11
+ IFT_SDSL = 0x60
+ IFT_SHDSL = 0xa9
+ IFT_SIP = 0x1f
+ IFT_SLIP = 0x1c
+ IFT_SMDSDXI = 0x2b
+ IFT_SMDSICIP = 0x34
+ IFT_SONET = 0x27
+ IFT_SONETOVERHEADCHANNEL = 0xb9
+ IFT_SONETPATH = 0x32
+ IFT_SONETVT = 0x33
+ IFT_SRP = 0x97
+ IFT_SS7SIGLINK = 0x9c
+ IFT_STACKTOSTACK = 0x6f
+ IFT_STARLAN = 0xb
+ IFT_STF = 0xf3
+ IFT_T1 = 0x12
+ IFT_TDLC = 0x74
+ IFT_TERMPAD = 0x5b
+ IFT_TR008 = 0xb0
+ IFT_TRANSPHDLC = 0x7b
+ IFT_TUNNEL = 0x83
+ IFT_ULTRA = 0x1d
+ IFT_USB = 0xa0
+ IFT_V11 = 0x40
+ IFT_V35 = 0x2d
+ IFT_V36 = 0x41
+ IFT_V37 = 0x78
+ IFT_VDSL = 0x61
+ IFT_VIRTUALIPADDRESS = 0x70
+ IFT_VOICEEM = 0x64
+ IFT_VOICEENCAP = 0x67
+ IFT_VOICEFXO = 0x65
+ IFT_VOICEFXS = 0x66
+ IFT_VOICEOVERATM = 0x98
+ IFT_VOICEOVERFRAMERELAY = 0x99
+ IFT_VOICEOVERIP = 0x68
+ IFT_X213 = 0x5d
+ IFT_X25 = 0x5
+ IFT_X25DDN = 0x4
+ IFT_X25HUNTGROUP = 0x7a
+ IFT_X25MLP = 0x79
+ IFT_X25PLE = 0x28
+ IFT_XETHER = 0x1a
+ IGNBRK = 0x1
+ IGNCR = 0x80
+ IGNPAR = 0x4
+ IMAXBEL = 0x2000
+ INLCR = 0x40
+ INPCK = 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 = 0xfe
+ 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 = 0x0
+ 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 = 0x0
+ 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_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_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_UNKNOWN = 0x102
+ 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_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_MAXPACKET = 0xffff
+ 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_PKTOPTIONS = 0x34
+ IPV6_PORTRANGE = 0xe
+ IPV6_PORTRANGE_DEFAULT = 0x0
+ 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 = 0x0
+ IPV6_RTHDR_STRICT = 0x1
+ IPV6_RTHDR_TYPE_0 = 0x0
+ 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_DEFAULT_MULTICAST_LOOP = 0x1
+ IP_DEFAULT_MULTICAST_TTL = 0x1
+ IP_DF = 0x4000
+ IP_DROP_MEMBERSHIP = 0xd
+ IP_DUMMYNET_CONFIGURE = 0x3c
+ IP_DUMMYNET_DEL = 0x3d
+ IP_DUMMYNET_FLUSH = 0x3e
+ IP_DUMMYNET_GET = 0x40
+ IP_FAITH = 0x16
+ IP_FW_ADD = 0x32
+ IP_FW_DEL = 0x33
+ IP_FW_FLUSH = 0x34
+ IP_FW_GET = 0x36
+ IP_FW_RESETLOG = 0x37
+ IP_FW_ZERO = 0x35
+ IP_HDRINCL = 0x2
+ IP_IPSEC_POLICY = 0x15
+ IP_MAXPACKET = 0xffff
+ IP_MAX_MEMBERSHIPS = 0x14
+ IP_MF = 0x2000
+ IP_MINTTL = 0x42
+ IP_MSS = 0x240
+ IP_MULTICAST_IF = 0x9
+ IP_MULTICAST_LOOP = 0xb
+ IP_MULTICAST_TTL = 0xa
+ IP_MULTICAST_VIF = 0xe
+ IP_OFFMASK = 0x1fff
+ IP_OPTIONS = 0x1
+ IP_PORTRANGE = 0x13
+ IP_PORTRANGE_DEFAULT = 0x0
+ 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_TOS = 0x3
+ IP_TTL = 0x4
+ ISIG = 0x80
+ ISTRIP = 0x20
+ IXANY = 0x800
+ IXOFF = 0x400
+ IXON = 0x200
+ LOCK_EX = 0x2
+ LOCK_NB = 0x4
+ LOCK_SH = 0x1
+ LOCK_UN = 0x8
+ MADV_AUTOSYNC = 0x7
+ MADV_CONTROL_END = 0xb
+ MADV_CONTROL_START = 0xa
+ MADV_CORE = 0x9
+ MADV_DONTNEED = 0x4
+ MADV_FREE = 0x5
+ MADV_INVAL = 0xa
+ MADV_NOCORE = 0x8
+ MADV_NORMAL = 0x0
+ MADV_NOSYNC = 0x6
+ MADV_RANDOM = 0x1
+ MADV_SEQUENTIAL = 0x2
+ MADV_SETMAP = 0xb
+ MADV_WILLNEED = 0x3
+ MAP_ANON = 0x1000
+ MAP_COPY = 0x2
+ MAP_FILE = 0x0
+ MAP_FIXED = 0x10
+ MAP_HASSEMAPHORE = 0x200
+ MAP_INHERIT = 0x80
+ MAP_NOCORE = 0x20000
+ MAP_NOEXTEND = 0x100
+ MAP_NORESERVE = 0x40
+ MAP_NOSYNC = 0x800
+ MAP_PRIVATE = 0x2
+ MAP_RENAME = 0x20
+ MAP_SHARED = 0x1
+ MAP_SIZEALIGN = 0x40000
+ MAP_STACK = 0x400
+ MAP_TRYFIXED = 0x10000
+ MAP_VPAGETABLE = 0x2000
+ MCL_CURRENT = 0x1
+ MCL_FUTURE = 0x2
+ MSG_CTRUNC = 0x20
+ MSG_DONTROUTE = 0x4
+ MSG_DONTWAIT = 0x80
+ MSG_EOF = 0x100
+ MSG_EOR = 0x8
+ MSG_FBLOCKING = 0x10000
+ MSG_FMASK = 0xffff0000
+ MSG_FNONBLOCKING = 0x20000
+ MSG_NOSIGNAL = 0x400
+ MSG_NOTIFICATION = 0x200
+ MSG_OOB = 0x1
+ MSG_PEEK = 0x2
+ MSG_SYNC = 0x800
+ MSG_TRUNC = 0x10
+ MSG_WAITALL = 0x40
+ MS_ASYNC = 0x1
+ MS_INVALIDATE = 0x2
+ MS_SYNC = 0x0
+ NAME_MAX = 0xff
+ NET_RT_DUMP = 0x1
+ NET_RT_FLAGS = 0x2
+ NET_RT_IFLIST = 0x3
+ NET_RT_MAXID = 0x4
+ NOFLSH = 0x80000000
+ NOTE_ATTRIB = 0x8
+ NOTE_CHILD = 0x4
+ NOTE_DELETE = 0x1
+ NOTE_EXEC = 0x20000000
+ NOTE_EXIT = 0x80000000
+ NOTE_EXTEND = 0x4
+ NOTE_FORK = 0x40000000
+ NOTE_LINK = 0x10
+ NOTE_LOWAT = 0x1
+ NOTE_OOB = 0x2
+ NOTE_PCTRLMASK = 0xf0000000
+ NOTE_PDATAMASK = 0xfffff
+ NOTE_RENAME = 0x20
+ NOTE_REVOKE = 0x40
+ NOTE_TRACK = 0x1
+ NOTE_TRACKERR = 0x2
+ NOTE_WRITE = 0x2
+ OCRNL = 0x10
+ ONLCR = 0x2
+ ONLRET = 0x40
+ ONOCR = 0x20
+ ONOEOT = 0x8
+ OPOST = 0x1
+ O_ACCMODE = 0x3
+ O_APPEND = 0x8
+ O_ASYNC = 0x40
+ O_CLOEXEC = 0x20000
+ O_CREAT = 0x200
+ O_DIRECT = 0x10000
+ O_DIRECTORY = 0x8000000
+ O_EXCL = 0x800
+ O_EXLOCK = 0x20
+ O_FAPPEND = 0x100000
+ O_FASYNCWRITE = 0x800000
+ O_FBLOCKING = 0x40000
+ O_FBUFFERED = 0x2000000
+ O_FMASK = 0x7fc0000
+ O_FNONBLOCKING = 0x80000
+ O_FOFFSET = 0x200000
+ O_FSYNC = 0x80
+ O_FSYNCWRITE = 0x400000
+ O_FUNBUFFERED = 0x1000000
+ O_MAPONREAD = 0x4000000
+ O_NDELAY = 0x4
+ O_NOCTTY = 0x8000
+ O_NOFOLLOW = 0x100
+ O_NONBLOCK = 0x4
+ O_RDONLY = 0x0
+ O_RDWR = 0x2
+ O_SHLOCK = 0x10
+ O_SYNC = 0x80
+ O_TRUNC = 0x400
+ O_WRONLY = 0x1
+ PARENB = 0x1000
+ PARMRK = 0x8
+ PARODD = 0x2000
+ PENDIN = 0x20000000
+ PRIO_PGRP = 0x1
+ PRIO_PROCESS = 0x0
+ PRIO_USER = 0x2
+ PROT_EXEC = 0x4
+ PROT_NONE = 0x0
+ PROT_READ = 0x1
+ PROT_WRITE = 0x2
+ RLIMIT_AS = 0xa
+ RLIMIT_CORE = 0x4
+ RLIMIT_CPU = 0x0
+ RLIMIT_DATA = 0x2
+ RLIMIT_FSIZE = 0x1
+ RLIMIT_NOFILE = 0x8
+ RLIMIT_STACK = 0x3
+ RLIM_INFINITY = 0x7fffffffffffffff
+ RTAX_AUTHOR = 0x6
+ RTAX_BRD = 0x7
+ RTAX_DST = 0x0
+ RTAX_GATEWAY = 0x1
+ RTAX_GENMASK = 0x3
+ RTAX_IFA = 0x5
+ RTAX_IFP = 0x4
+ RTAX_MAX = 0xb
+ RTAX_MPLS1 = 0x8
+ RTAX_MPLS2 = 0x9
+ RTAX_MPLS3 = 0xa
+ 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_MPLS1 = 0x100
+ RTA_MPLS2 = 0x200
+ RTA_MPLS3 = 0x400
+ RTA_NETMASK = 0x4
+ RTF_BLACKHOLE = 0x1000
+ RTF_BROADCAST = 0x400000
+ RTF_CLONING = 0x100
+ RTF_DONE = 0x40
+ RTF_DYNAMIC = 0x10
+ RTF_GATEWAY = 0x2
+ RTF_HOST = 0x4
+ RTF_LLINFO = 0x400
+ RTF_LOCAL = 0x200000
+ RTF_MODIFIED = 0x20
+ RTF_MPLSOPS = 0x1000000
+ RTF_MULTICAST = 0x800000
+ RTF_PINNED = 0x100000
+ RTF_PRCLONING = 0x10000
+ RTF_PROTO1 = 0x8000
+ RTF_PROTO2 = 0x4000
+ RTF_PROTO3 = 0x40000
+ RTF_REJECT = 0x8
+ RTF_STATIC = 0x800
+ RTF_UP = 0x1
+ RTF_WASCLONED = 0x20000
+ 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 = 0x6
+ RTV_EXPIRE = 0x4
+ RTV_HOPCOUNT = 0x2
+ RTV_IWCAPSEGS = 0x400
+ RTV_IWMAXSEGS = 0x200
+ RTV_MSL = 0x100
+ RTV_MTU = 0x1
+ RTV_RPIPE = 0x8
+ RTV_RTT = 0x40
+ RTV_RTTVAR = 0x80
+ RTV_SPIPE = 0x10
+ RTV_SSTHRESH = 0x20
+ RUSAGE_CHILDREN = -0x1
+ RUSAGE_SELF = 0x0
+ SCM_CREDS = 0x3
+ SCM_RIGHTS = 0x1
+ SCM_TIMESTAMP = 0x2
+ SHUT_RD = 0x0
+ SHUT_RDWR = 0x2
+ SHUT_WR = 0x1
+ SIOCADDMULTI = 0x80206931
+ SIOCADDRT = 0x8040720a
+ SIOCAIFADDR = 0x8040691a
+ SIOCALIFADDR = 0x8118691b
+ SIOCATMARK = 0x40047307
+ SIOCDELMULTI = 0x80206932
+ SIOCDELRT = 0x8040720b
+ SIOCDIFADDR = 0x80206919
+ SIOCDIFPHYADDR = 0x80206949
+ SIOCDLIFADDR = 0x8118691d
+ SIOCGDRVSPEC = 0xc028697b
+ SIOCGETSGCNT = 0xc0207210
+ SIOCGETVIFCNT = 0xc028720f
+ SIOCGHIWAT = 0x40047301
+ SIOCGIFADDR = 0xc0206921
+ SIOCGIFBRDADDR = 0xc0206923
+ SIOCGIFCAP = 0xc020691f
+ SIOCGIFCONF = 0xc0106924
+ SIOCGIFDATA = 0xc0206926
+ SIOCGIFDSTADDR = 0xc0206922
+ SIOCGIFFLAGS = 0xc0206911
+ SIOCGIFGENERIC = 0xc020693a
+ SIOCGIFGMEMB = 0xc028698a
+ SIOCGIFINDEX = 0xc0206920
+ SIOCGIFMEDIA = 0xc0306938
+ SIOCGIFMETRIC = 0xc0206917
+ SIOCGIFMTU = 0xc0206933
+ SIOCGIFNETMASK = 0xc0206925
+ SIOCGIFPDSTADDR = 0xc0206948
+ SIOCGIFPHYS = 0xc0206935
+ SIOCGIFPOLLCPU = 0xc020697e
+ SIOCGIFPSRCADDR = 0xc0206947
+ SIOCGIFSTATUS = 0xc331693b
+ SIOCGIFTSOLEN = 0xc0206980
+ 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
+ SIOCSIFDSTADDR = 0x8020690e
+ SIOCSIFFLAGS = 0x80206910
+ SIOCSIFGENERIC = 0x80206939
+ SIOCSIFLLADDR = 0x8020693c
+ SIOCSIFMEDIA = 0xc0206937
+ SIOCSIFMETRIC = 0x80206918
+ SIOCSIFMTU = 0x80206934
+ SIOCSIFNAME = 0x80206928
+ SIOCSIFNETMASK = 0x80206916
+ SIOCSIFPHYADDR = 0x80406946
+ SIOCSIFPHYS = 0x80206936
+ SIOCSIFPOLLCPU = 0x8020697d
+ SIOCSIFTSOLEN = 0x8020697f
+ 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_BROADCAST = 0x20
+ SO_DEBUG = 0x1
+ SO_DONTROUTE = 0x10
+ SO_ERROR = 0x1007
+ SO_KEEPALIVE = 0x8
+ SO_LINGER = 0x80
+ SO_NOSIGPIPE = 0x800
+ SO_OOBINLINE = 0x100
+ SO_RCVBUF = 0x1002
+ SO_RCVLOWAT = 0x1004
+ SO_RCVTIMEO = 0x1006
+ SO_REUSEADDR = 0x4
+ SO_REUSEPORT = 0x200
+ SO_SNDBUF = 0x1001
+ SO_SNDLOWAT = 0x1003
+ SO_SNDSPACE = 0x100a
+ SO_SNDTIMEO = 0x1005
+ SO_TIMESTAMP = 0x400
+ SO_TYPE = 0x1008
+ SO_USELOOPBACK = 0x40
+ TCIFLUSH = 0x1
+ TCIOFLUSH = 0x3
+ TCOFLUSH = 0x2
+ TCP_FASTKEEP = 0x80
+ TCP_KEEPCNT = 0x400
+ TCP_KEEPIDLE = 0x100
+ TCP_KEEPINIT = 0x20
+ TCP_KEEPINTVL = 0x200
+ TCP_MAXBURST = 0x4
+ TCP_MAXHLEN = 0x3c
+ TCP_MAXOLEN = 0x28
+ TCP_MAXSEG = 0x2
+ TCP_MAXWIN = 0xffff
+ TCP_MAX_WINSHIFT = 0xe
+ TCP_MINMSS = 0x100
+ TCP_MIN_WINSHIFT = 0x5
+ TCP_MSS = 0x200
+ TCP_NODELAY = 0x1
+ TCP_NOOPT = 0x8
+ TCP_NOPUSH = 0x4
+ TCP_SIGNATURE_ENABLE = 0x10
+ TCSAFLUSH = 0x2
+ TIOCCBRK = 0x2000747a
+ TIOCCDTR = 0x20007478
+ TIOCCONS = 0x80047462
+ TIOCDCDTIMESTAMP = 0x40107458
+ TIOCDRAIN = 0x2000745e
+ TIOCEXCL = 0x2000740d
+ TIOCEXT = 0x80047460
+ TIOCFLUSH = 0x80047410
+ TIOCGDRAINWAIT = 0x40047456
+ TIOCGETA = 0x402c7413
+ TIOCGETD = 0x4004741a
+ TIOCGPGRP = 0x40047477
+ TIOCGSID = 0x40047463
+ TIOCGSIZE = 0x40087468
+ TIOCGWINSZ = 0x40087468
+ TIOCISPTMASTER = 0x20007455
+ TIOCMBIC = 0x8004746b
+ TIOCMBIS = 0x8004746c
+ TIOCMGDTRWAIT = 0x4004745a
+ TIOCMGET = 0x4004746a
+ TIOCMODG = 0x40047403
+ TIOCMODS = 0x80047404
+ TIOCMSDTRWAIT = 0x8004745b
+ TIOCMSET = 0x8004746d
+ TIOCM_CAR = 0x40
+ TIOCM_CD = 0x40
+ TIOCM_CTS = 0x20
+ TIOCM_DSR = 0x100
+ TIOCM_DTR = 0x2
+ TIOCM_LE = 0x1
+ TIOCM_RI = 0x80
+ TIOCM_RNG = 0x80
+ TIOCM_RTS = 0x4
+ TIOCM_SR = 0x10
+ TIOCM_ST = 0x8
+ TIOCNOTTY = 0x20007471
+ TIOCNXCL = 0x2000740e
+ TIOCOUTQ = 0x40047473
+ TIOCPKT = 0x80047470
+ TIOCPKT_DATA = 0x0
+ TIOCPKT_DOSTOP = 0x20
+ TIOCPKT_FLUSHREAD = 0x1
+ TIOCPKT_FLUSHWRITE = 0x2
+ TIOCPKT_IOCTL = 0x40
+ TIOCPKT_NOSTOP = 0x10
+ TIOCPKT_START = 0x8
+ TIOCPKT_STOP = 0x4
+ TIOCREMOTE = 0x80047469
+ TIOCSBRK = 0x2000747b
+ TIOCSCTTY = 0x20007461
+ TIOCSDRAINWAIT = 0x80047457
+ TIOCSDTR = 0x20007479
+ TIOCSETA = 0x802c7414
+ TIOCSETAF = 0x802c7416
+ TIOCSETAW = 0x802c7415
+ TIOCSETD = 0x8004741b
+ TIOCSIG = 0x2000745f
+ TIOCSPGRP = 0x80047476
+ TIOCSSIZE = 0x80087467
+ TIOCSTART = 0x2000746e
+ TIOCSTAT = 0x20007465
+ TIOCSTI = 0x80017472
+ TIOCSTOP = 0x2000746f
+ TIOCSWINSZ = 0x80087467
+ TIOCTIMESTAMP = 0x40107459
+ TIOCUCNTL = 0x80047466
+ TOSTOP = 0x400000
+ VCHECKPT = 0x13
+ VDISCARD = 0xf
+ VDSUSP = 0xb
+ VEOF = 0x0
+ VEOL = 0x1
+ VEOL2 = 0x2
+ VERASE = 0x3
+ VERASE2 = 0x7
+ VINTR = 0x8
+ VKILL = 0x5
+ VLNEXT = 0xe
+ VMIN = 0x10
+ VQUIT = 0x9
+ VREPRINT = 0x6
+ VSTART = 0xc
+ VSTATUS = 0x12
+ VSTOP = 0xd
+ VSUSP = 0xa
+ VTIME = 0x11
+ VWERASE = 0x4
+ WCONTINUED = 0x4
+ WCOREFLAG = 0x80
+ WLINUXCLONE = 0x80000000
+ WNOHANG = 0x1
+ WSTOPPED = 0x7f
+ WUNTRACED = 0x2
+)
+
+// Errors
+const (
+ E2BIG = Errno(0x7)
+ EACCES = Errno(0xd)
+ EADDRINUSE = Errno(0x30)
+ EADDRNOTAVAIL = Errno(0x31)
+ EAFNOSUPPORT = Errno(0x2f)
+ EAGAIN = Errno(0x23)
+ EALREADY = Errno(0x25)
+ EASYNC = Errno(0x63)
+ EAUTH = Errno(0x50)
+ EBADF = Errno(0x9)
+ EBADMSG = Errno(0x59)
+ EBADRPC = Errno(0x48)
+ EBUSY = Errno(0x10)
+ ECANCELED = Errno(0x55)
+ ECHILD = Errno(0xa)
+ ECONNABORTED = Errno(0x35)
+ ECONNREFUSED = Errno(0x3d)
+ ECONNRESET = Errno(0x36)
+ EDEADLK = Errno(0xb)
+ EDESTADDRREQ = Errno(0x27)
+ EDOM = Errno(0x21)
+ EDOOFUS = Errno(0x58)
+ EDQUOT = Errno(0x45)
+ EEXIST = Errno(0x11)
+ EFAULT = Errno(0xe)
+ EFBIG = Errno(0x1b)
+ EFTYPE = Errno(0x4f)
+ EHOSTDOWN = Errno(0x40)
+ EHOSTUNREACH = Errno(0x41)
+ EIDRM = Errno(0x52)
+ EILSEQ = Errno(0x56)
+ EINPROGRESS = Errno(0x24)
+ EINTR = Errno(0x4)
+ EINVAL = Errno(0x16)
+ EIO = Errno(0x5)
+ EISCONN = Errno(0x38)
+ EISDIR = Errno(0x15)
+ ELAST = Errno(0x63)
+ ELOOP = Errno(0x3e)
+ EMFILE = Errno(0x18)
+ EMLINK = Errno(0x1f)
+ EMSGSIZE = Errno(0x28)
+ EMULTIHOP = Errno(0x5a)
+ ENAMETOOLONG = Errno(0x3f)
+ ENEEDAUTH = Errno(0x51)
+ ENETDOWN = Errno(0x32)
+ ENETRESET = Errno(0x34)
+ ENETUNREACH = Errno(0x33)
+ ENFILE = Errno(0x17)
+ ENOATTR = Errno(0x57)
+ ENOBUFS = Errno(0x37)
+ ENODEV = Errno(0x13)
+ ENOENT = Errno(0x2)
+ ENOEXEC = Errno(0x8)
+ ENOLCK = Errno(0x4d)
+ ENOLINK = Errno(0x5b)
+ ENOMEDIUM = Errno(0x5d)
+ ENOMEM = Errno(0xc)
+ ENOMSG = Errno(0x53)
+ ENOPROTOOPT = Errno(0x2a)
+ ENOSPC = Errno(0x1c)
+ ENOSYS = Errno(0x4e)
+ ENOTBLK = Errno(0xf)
+ ENOTCONN = Errno(0x39)
+ ENOTDIR = Errno(0x14)
+ ENOTEMPTY = Errno(0x42)
+ ENOTSOCK = Errno(0x26)
+ ENOTSUP = Errno(0x2d)
+ ENOTTY = Errno(0x19)
+ ENXIO = Errno(0x6)
+ EOPNOTSUPP = Errno(0x2d)
+ EOVERFLOW = Errno(0x54)
+ EPERM = Errno(0x1)
+ EPFNOSUPPORT = Errno(0x2e)
+ EPIPE = Errno(0x20)
+ EPROCLIM = Errno(0x43)
+ EPROCUNAVAIL = Errno(0x4c)
+ EPROGMISMATCH = Errno(0x4b)
+ EPROGUNAVAIL = Errno(0x4a)
+ EPROTO = Errno(0x5c)
+ EPROTONOSUPPORT = Errno(0x2b)
+ EPROTOTYPE = Errno(0x29)
+ ERANGE = Errno(0x22)
+ EREMOTE = Errno(0x47)
+ EROFS = Errno(0x1e)
+ ERPCMISMATCH = Errno(0x49)
+ ESHUTDOWN = Errno(0x3a)
+ ESOCKTNOSUPPORT = Errno(0x2c)
+ ESPIPE = Errno(0x1d)
+ ESRCH = Errno(0x3)
+ ESTALE = Errno(0x46)
+ ETIMEDOUT = Errno(0x3c)
+ ETOOMANYREFS = Errno(0x3b)
+ ETXTBSY = Errno(0x1a)
+ EUNUSED94 = Errno(0x5e)
+ EUNUSED95 = Errno(0x5f)
+ EUNUSED96 = Errno(0x60)
+ EUNUSED97 = Errno(0x61)
+ EUNUSED98 = Errno(0x62)
+ EUSERS = Errno(0x44)
+ EWOULDBLOCK = Errno(0x23)
+ EXDEV = Errno(0x12)
+)
+
+// Signals
+const (
+ SIGABRT = Signal(0x6)
+ SIGALRM = Signal(0xe)
+ SIGBUS = Signal(0xa)
+ SIGCHLD = Signal(0x14)
+ SIGCKPT = Signal(0x21)
+ SIGCKPTEXIT = Signal(0x22)
+ SIGCONT = Signal(0x13)
+ SIGEMT = Signal(0x7)
+ SIGFPE = Signal(0x8)
+ SIGHUP = Signal(0x1)
+ SIGILL = Signal(0x4)
+ SIGINFO = Signal(0x1d)
+ SIGINT = Signal(0x2)
+ SIGIO = Signal(0x17)
+ SIGIOT = Signal(0x6)
+ SIGKILL = Signal(0x9)
+ SIGPIPE = Signal(0xd)
+ SIGPROF = Signal(0x1b)
+ SIGQUIT = Signal(0x3)
+ SIGSEGV = Signal(0xb)
+ SIGSTOP = Signal(0x11)
+ SIGSYS = Signal(0xc)
+ SIGTERM = Signal(0xf)
+ SIGTHR = Signal(0x20)
+ SIGTRAP = Signal(0x5)
+ SIGTSTP = Signal(0x12)
+ SIGTTIN = Signal(0x15)
+ SIGTTOU = Signal(0x16)
+ SIGURG = Signal(0x10)
+ SIGUSR1 = Signal(0x1e)
+ SIGUSR2 = Signal(0x1f)
+ SIGVTALRM = Signal(0x1a)
+ SIGWINCH = Signal(0x1c)
+ SIGXCPU = Signal(0x18)
+ SIGXFSZ = Signal(0x19)
+)
+
+// Error table
+var errors = [...]string{
+ 1: "operation not permitted",
+ 2: "no such file or directory",
+ 3: "no such process",
+ 4: "interrupted system call",
+ 5: "input/output error",
+ 6: "device not configured",
+ 7: "argument list too long",
+ 8: "exec format error",
+ 9: "bad file descriptor",
+ 10: "no child processes",
+ 11: "resource deadlock avoided",
+ 12: "cannot allocate memory",
+ 13: "permission denied",
+ 14: "bad address",
+ 15: "block device required",
+ 16: "device busy",
+ 17: "file exists",
+ 18: "cross-device link",
+ 19: "operation not supported by device",
+ 20: "not a directory",
+ 21: "is a directory",
+ 22: "invalid argument",
+ 23: "too many open files in system",
+ 24: "too many open files",
+ 25: "inappropriate ioctl for device",
+ 26: "text file busy",
+ 27: "file too large",
+ 28: "no space left on device",
+ 29: "illegal seek",
+ 30: "read-only file system",
+ 31: "too many links",
+ 32: "broken pipe",
+ 33: "numerical argument out of domain",
+ 34: "result too large",
+ 35: "resource temporarily unavailable",
+ 36: "operation now in progress",
+ 37: "operation already in progress",
+ 38: "socket operation on non-socket",
+ 39: "destination address required",
+ 40: "message too long",
+ 41: "protocol wrong type for socket",
+ 42: "protocol not available",
+ 43: "protocol not supported",
+ 44: "socket type not supported",
+ 45: "operation not supported",
+ 46: "protocol family not supported",
+ 47: "address family not supported by protocol family",
+ 48: "address already in use",
+ 49: "can't assign requested address",
+ 50: "network is down",
+ 51: "network is unreachable",
+ 52: "network dropped connection on reset",
+ 53: "software caused connection abort",
+ 54: "connection reset by peer",
+ 55: "no buffer space available",
+ 56: "socket is already connected",
+ 57: "socket is not connected",
+ 58: "can't send after socket shutdown",
+ 59: "too many references: can't splice",
+ 60: "operation timed out",
+ 61: "connection refused",
+ 62: "too many levels of symbolic links",
+ 63: "file name too long",
+ 64: "host is down",
+ 65: "no route to host",
+ 66: "directory not empty",
+ 67: "too many processes",
+ 68: "too many users",
+ 69: "disc quota exceeded",
+ 70: "stale NFS file handle",
+ 71: "too many levels of remote in path",
+ 72: "RPC struct is bad",
+ 73: "RPC version wrong",
+ 74: "RPC prog. not avail",
+ 75: "program version wrong",
+ 76: "bad procedure for program",
+ 77: "no locks available",
+ 78: "function not implemented",
+ 79: "inappropriate file type or format",
+ 80: "authentication error",
+ 81: "need authenticator",
+ 82: "identifier removed",
+ 83: "no message of desired type",
+ 84: "value too large to be stored in data type",
+ 85: "operation canceled",
+ 86: "illegal byte sequence",
+ 87: "attribute not found",
+ 88: "programming error",
+ 89: "bad message",
+ 90: "multihop attempted",
+ 91: "link has been severed",
+ 92: "protocol error",
+ 93: "unknown error: 93",
+ 94: "unknown error: 94",
+ 95: "unknown error: 95",
+ 96: "unknown error: 96",
+ 97: "unknown error: 97",
+ 98: "unknown error: 98",
+ 99: "unknown error: 99",
+}
+
+// Signal table
+var signals = [...]string{
+ 1: "hangup",
+ 2: "interrupt",
+ 3: "quit",
+ 4: "illegal instruction",
+ 5: "trace/BPT trap",
+ 6: "abort trap",
+ 7: "EMT trap",
+ 8: "floating point exception",
+ 9: "killed",
+ 10: "bus error",
+ 11: "segmentation fault",
+ 12: "bad system call",
+ 13: "broken pipe",
+ 14: "alarm clock",
+ 15: "terminated",
+ 16: "urgent I/O condition",
+ 17: "suspended (signal)",
+ 18: "suspended",
+ 19: "continued",
+ 20: "child exited",
+ 21: "stopped (tty input)",
+ 22: "stopped (tty output)",
+ 23: "I/O possible",
+ 24: "cputime limit exceeded",
+ 25: "filesize limit exceeded",
+ 26: "virtual timer expired",
+ 27: "profiling timer expired",
+ 28: "window size changes",
+ 29: "information request",
+ 30: "user defined signal 1",
+ 31: "user defined signal 2",
+ 32: "thread Scheduler",
+ 33: "checkPoint",
+ 34: "checkPointExit",
+}
diff --git a/src/pkg/syscall/zerrors_freebsd_386.go b/src/pkg/syscall/zerrors_freebsd_386.go
index 048687361..43d7c5969 100644
--- a/src/pkg/syscall/zerrors_freebsd_386.go
+++ b/src/pkg/syscall/zerrors_freebsd_386.go
@@ -438,7 +438,9 @@ const (
FLUSHO = 0x800000
F_CANCEL = 0x5
F_DUP2FD = 0xa
+ F_DUP2FD_CLOEXEC = 0x12
F_DUPFD = 0x0
+ F_DUPFD_CLOEXEC = 0x11
F_GETFD = 0x1
F_GETFL = 0x3
F_GETLK = 0xb
@@ -461,6 +463,7 @@ const (
F_WRLCK = 0x3
HUPCL = 0x4000
ICANON = 0x100
+ ICMP6_FILTER = 0x12
ICRNL = 0x100
IEXTEN = 0x400
IFAN_ARRIVAL = 0x0
@@ -1088,6 +1091,9 @@ const (
PARMRK = 0x8
PARODD = 0x2000
PENDIN = 0x20000000
+ PRIO_PGRP = 0x1
+ PRIO_PROCESS = 0x0
+ PRIO_USER = 0x2
PROT_EXEC = 0x4
PROT_NONE = 0x0
PROT_READ = 0x1
diff --git a/src/pkg/syscall/zerrors_freebsd_amd64.go b/src/pkg/syscall/zerrors_freebsd_amd64.go
index a45d7f495..8e03f45e2 100644
--- a/src/pkg/syscall/zerrors_freebsd_amd64.go
+++ b/src/pkg/syscall/zerrors_freebsd_amd64.go
@@ -438,7 +438,9 @@ const (
FLUSHO = 0x800000
F_CANCEL = 0x5
F_DUP2FD = 0xa
+ F_DUP2FD_CLOEXEC = 0x12
F_DUPFD = 0x0
+ F_DUPFD_CLOEXEC = 0x11
F_GETFD = 0x1
F_GETFL = 0x3
F_GETLK = 0xb
@@ -461,6 +463,7 @@ const (
F_WRLCK = 0x3
HUPCL = 0x4000
ICANON = 0x100
+ ICMP6_FILTER = 0x12
ICRNL = 0x100
IEXTEN = 0x400
IFAN_ARRIVAL = 0x0
@@ -1088,6 +1091,9 @@ const (
PARMRK = 0x8
PARODD = 0x2000
PENDIN = 0x20000000
+ PRIO_PGRP = 0x1
+ PRIO_PROCESS = 0x0
+ PRIO_USER = 0x2
PROT_EXEC = 0x4
PROT_NONE = 0x0
PROT_READ = 0x1
diff --git a/src/pkg/syscall/zerrors_freebsd_arm.go b/src/pkg/syscall/zerrors_freebsd_arm.go
index 82f30abc5..269f179b3 100644
--- a/src/pkg/syscall/zerrors_freebsd_arm.go
+++ b/src/pkg/syscall/zerrors_freebsd_arm.go
@@ -466,6 +466,7 @@ const (
F_WRLCK = 0x3
HUPCL = 0x4000
ICANON = 0x100
+ ICMP6_FILTER = 0x12
ICRNL = 0x100
IEXTEN = 0x400
IFAN_ARRIVAL = 0x0
@@ -1093,6 +1094,9 @@ const (
PARMRK = 0x8
PARODD = 0x2000
PENDIN = 0x20000000
+ PRIO_PGRP = 0x1
+ PRIO_PROCESS = 0x0
+ PRIO_USER = 0x2
PROT_EXEC = 0x4
PROT_NONE = 0x0
PROT_READ = 0x1
diff --git a/src/pkg/syscall/zerrors_linux_386.go b/src/pkg/syscall/zerrors_linux_386.go
index a689942b8..7aa8ff07a 100644
--- a/src/pkg/syscall/zerrors_linux_386.go
+++ b/src/pkg/syscall/zerrors_linux_386.go
@@ -4,12 +4,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs -- -m32 _const.go
-//line _const.go:1
package syscall
-//line _const.go:51
-
-//line _const.go:50
const (
AF_ALG = 0x26
AF_APPLETALK = 0x5
@@ -150,6 +146,28 @@ const (
BPF_TXA = 0x80
BPF_W = 0x0
BPF_X = 0x8
+ CLONE_CHILD_CLEARTID = 0x200000
+ CLONE_CHILD_SETTID = 0x1000000
+ CLONE_DETACHED = 0x400000
+ CLONE_FILES = 0x400
+ CLONE_FS = 0x200
+ CLONE_IO = 0x80000000
+ CLONE_NEWIPC = 0x8000000
+ CLONE_NEWNET = 0x40000000
+ CLONE_NEWNS = 0x20000
+ CLONE_NEWPID = 0x20000000
+ CLONE_NEWUSER = 0x10000000
+ CLONE_NEWUTS = 0x4000000
+ CLONE_PARENT = 0x8000
+ CLONE_PARENT_SETTID = 0x100000
+ CLONE_PTRACE = 0x2000
+ CLONE_SETTLS = 0x80000
+ CLONE_SIGHAND = 0x800
+ CLONE_SYSVSEM = 0x40000
+ CLONE_THREAD = 0x10000
+ CLONE_UNTRACED = 0x800000
+ CLONE_VFORK = 0x4000
+ CLONE_VM = 0x100
DT_BLK = 0x6
DT_CHR = 0x2
DT_DIR = 0x4
@@ -276,6 +294,7 @@ const (
F_ULOCK = 0x0
F_UNLCK = 0x2
F_WRLCK = 0x1
+ ICMPV6_FILTER = 0x1
IFA_F_DADFAILED = 0x8
IFA_F_DEPRECATED = 0x20
IFA_F_HOMEADDRESS = 0x10
@@ -657,6 +676,9 @@ const (
PACKET_RECV_OUTPUT = 0x3
PACKET_RX_RING = 0x5
PACKET_STATISTICS = 0x6
+ PRIO_PGRP = 0x1
+ PRIO_PROCESS = 0x0
+ PRIO_USER = 0x2
PROT_EXEC = 0x4
PROT_GROWSDOWN = 0x1000000
PROT_GROWSUP = 0x2000000
@@ -1062,6 +1084,9 @@ const (
S_IXGRP = 0x8
S_IXOTH = 0x1
S_IXUSR = 0x40
+ TCIFLUSH = 0x0
+ TCIOFLUSH = 0x2
+ TCOFLUSH = 0x1
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 a1ac1773b..94d051d8a 100644
--- a/src/pkg/syscall/zerrors_linux_amd64.go
+++ b/src/pkg/syscall/zerrors_linux_amd64.go
@@ -4,12 +4,8 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs -- -m64 _const.go
-//line _const.go:1
package syscall
-//line _const.go:51
-
-//line _const.go:50
const (
AF_ALG = 0x26
AF_APPLETALK = 0x5
@@ -150,6 +146,28 @@ const (
BPF_TXA = 0x80
BPF_W = 0x0
BPF_X = 0x8
+ CLONE_CHILD_CLEARTID = 0x200000
+ CLONE_CHILD_SETTID = 0x1000000
+ CLONE_DETACHED = 0x400000
+ CLONE_FILES = 0x400
+ CLONE_FS = 0x200
+ CLONE_IO = 0x80000000
+ CLONE_NEWIPC = 0x8000000
+ CLONE_NEWNET = 0x40000000
+ CLONE_NEWNS = 0x20000
+ CLONE_NEWPID = 0x20000000
+ CLONE_NEWUSER = 0x10000000
+ CLONE_NEWUTS = 0x4000000
+ CLONE_PARENT = 0x8000
+ CLONE_PARENT_SETTID = 0x100000
+ CLONE_PTRACE = 0x2000
+ CLONE_SETTLS = 0x80000
+ CLONE_SIGHAND = 0x800
+ CLONE_SYSVSEM = 0x40000
+ CLONE_THREAD = 0x10000
+ CLONE_UNTRACED = 0x800000
+ CLONE_VFORK = 0x4000
+ CLONE_VM = 0x100
DT_BLK = 0x6
DT_CHR = 0x2
DT_DIR = 0x4
@@ -276,6 +294,7 @@ const (
F_ULOCK = 0x0
F_UNLCK = 0x2
F_WRLCK = 0x1
+ ICMPV6_FILTER = 0x1
IFA_F_DADFAILED = 0x8
IFA_F_DEPRECATED = 0x20
IFA_F_HOMEADDRESS = 0x10
@@ -657,6 +676,9 @@ const (
PACKET_RECV_OUTPUT = 0x3
PACKET_RX_RING = 0x5
PACKET_STATISTICS = 0x6
+ PRIO_PGRP = 0x1
+ PRIO_PROCESS = 0x0
+ PRIO_USER = 0x2
PROT_EXEC = 0x4
PROT_GROWSDOWN = 0x1000000
PROT_GROWSUP = 0x2000000
@@ -1063,6 +1085,9 @@ const (
S_IXGRP = 0x8
S_IXOTH = 0x1
S_IXUSR = 0x40
+ TCIFLUSH = 0x0
+ TCIOFLUSH = 0x2
+ TCOFLUSH = 0x1
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 0730f2140..dcaaef742 100644
--- a/src/pkg/syscall/zerrors_linux_arm.go
+++ b/src/pkg/syscall/zerrors_linux_arm.go
@@ -146,6 +146,28 @@ const (
BPF_TXA = 0x80
BPF_W = 0x0
BPF_X = 0x8
+ CLONE_CHILD_CLEARTID = 0x200000
+ CLONE_CHILD_SETTID = 0x1000000
+ CLONE_DETACHED = 0x400000
+ CLONE_FILES = 0x400
+ CLONE_FS = 0x200
+ CLONE_IO = 0x80000000
+ CLONE_NEWIPC = 0x8000000
+ CLONE_NEWNET = 0x40000000
+ CLONE_NEWNS = 0x20000
+ CLONE_NEWPID = 0x20000000
+ CLONE_NEWUSER = 0x10000000
+ CLONE_NEWUTS = 0x4000000
+ CLONE_PARENT = 0x8000
+ CLONE_PARENT_SETTID = 0x100000
+ CLONE_PTRACE = 0x2000
+ CLONE_SETTLS = 0x80000
+ CLONE_SIGHAND = 0x800
+ CLONE_SYSVSEM = 0x40000
+ CLONE_THREAD = 0x10000
+ CLONE_UNTRACED = 0x800000
+ CLONE_VFORK = 0x4000
+ CLONE_VM = 0x100
DT_BLK = 0x6
DT_CHR = 0x2
DT_DIR = 0x4
@@ -274,6 +296,7 @@ const (
F_ULOCK = 0x0
F_UNLCK = 0x2
F_WRLCK = 0x1
+ ICMPV6_FILTER = 0x1
IFA_F_DADFAILED = 0x8
IFA_F_DEPRECATED = 0x20
IFA_F_HOMEADDRESS = 0x10
@@ -653,6 +676,9 @@ const (
PACKET_RECV_OUTPUT = 0x3
PACKET_RX_RING = 0x5
PACKET_STATISTICS = 0x6
+ PRIO_PGRP = 0x1
+ PRIO_PROCESS = 0x0
+ PRIO_USER = 0x2
PROT_EXEC = 0x4
PROT_GROWSDOWN = 0x1000000
PROT_GROWSUP = 0x2000000
@@ -1069,6 +1095,9 @@ const (
S_IXGRP = 0x8
S_IXOTH = 0x1
S_IXUSR = 0x40
+ TCIFLUSH = 0x0
+ TCIOFLUSH = 0x2
+ TCOFLUSH = 0x1
TCP_CONGESTION = 0xd
TCP_CORK = 0x3
TCP_DEFER_ACCEPT = 0x9
diff --git a/src/pkg/syscall/zerrors_netbsd_386.go b/src/pkg/syscall/zerrors_netbsd_386.go
index 68051f9f8..9b93f5a15 100644
--- a/src/pkg/syscall/zerrors_netbsd_386.go
+++ b/src/pkg/syscall/zerrors_netbsd_386.go
@@ -570,6 +570,7 @@ const (
F_WRLCK = 0x3
HUPCL = 0x4000
ICANON = 0x100
+ ICMP6_FILTER = 0x12
ICRNL = 0x100
IEXTEN = 0x400
IFAN_ARRIVAL = 0x0
@@ -1039,6 +1040,9 @@ const (
PARODD = 0x2000
PENDIN = 0x20000000
PRI_IOFLUSH = 0x7c
+ PRIO_PGRP = 0x1
+ PRIO_PROCESS = 0x0
+ PRIO_USER = 0x2
RLIMIT_AS = 0xa
RLIMIT_CORE = 0x4
RLIMIT_CPU = 0x0
diff --git a/src/pkg/syscall/zerrors_netbsd_amd64.go b/src/pkg/syscall/zerrors_netbsd_amd64.go
index f719528a7..4db30fa5c 100644
--- a/src/pkg/syscall/zerrors_netbsd_amd64.go
+++ b/src/pkg/syscall/zerrors_netbsd_amd64.go
@@ -560,6 +560,7 @@ const (
F_WRLCK = 0x3
HUPCL = 0x4000
ICANON = 0x100
+ ICMP6_FILTER = 0x12
ICRNL = 0x100
IEXTEN = 0x400
IFAN_ARRIVAL = 0x0
@@ -1029,6 +1030,9 @@ const (
PARODD = 0x2000
PENDIN = 0x20000000
PRI_IOFLUSH = 0x7c
+ PRIO_PGRP = 0x1
+ PRIO_PROCESS = 0x0
+ PRIO_USER = 0x2
RLIMIT_AS = 0xa
RLIMIT_CORE = 0x4
RLIMIT_CPU = 0x0
diff --git a/src/pkg/syscall/zerrors_netbsd_arm.go b/src/pkg/syscall/zerrors_netbsd_arm.go
index 3899f6ba6..9262d5afb 100644
--- a/src/pkg/syscall/zerrors_netbsd_arm.go
+++ b/src/pkg/syscall/zerrors_netbsd_arm.go
@@ -560,6 +560,7 @@ const (
F_WRLCK = 0x3
HUPCL = 0x4000
ICANON = 0x100
+ ICMP6_FILTER = 0x12
ICRNL = 0x100
IEXTEN = 0x400
IFAN_ARRIVAL = 0x0
@@ -1029,6 +1030,9 @@ const (
PARODD = 0x2000
PENDIN = 0x20000000
PRI_IOFLUSH = 0x7c
+ PRIO_PGRP = 0x1
+ PRIO_PROCESS = 0x0
+ PRIO_USER = 0x2
RLIMIT_AS = 0xa
RLIMIT_CORE = 0x4
RLIMIT_CPU = 0x0
diff --git a/src/pkg/syscall/zerrors_openbsd_386.go b/src/pkg/syscall/zerrors_openbsd_386.go
index 5330d15b1..e546243b0 100644
--- a/src/pkg/syscall/zerrors_openbsd_386.go
+++ b/src/pkg/syscall/zerrors_openbsd_386.go
@@ -439,6 +439,7 @@ const (
F_WRLCK = 0x3
HUPCL = 0x4000
ICANON = 0x100
+ ICMP6_FILTER = 0x12
ICRNL = 0x100
IEXTEN = 0x400
IFAN_ARRIVAL = 0x0
@@ -926,6 +927,9 @@ const (
PENDIN = 0x20000000
PF_FLUSH = 0x1
PT_MASK = 0x3ff000
+ PRIO_PGRP = 0x1
+ PRIO_PROCESS = 0x0
+ PRIO_USER = 0x2
RLIMIT_CORE = 0x4
RLIMIT_CPU = 0x0
RLIMIT_DATA = 0x2
diff --git a/src/pkg/syscall/zerrors_openbsd_amd64.go b/src/pkg/syscall/zerrors_openbsd_amd64.go
index 5a7edd341..411b51a68 100644
--- a/src/pkg/syscall/zerrors_openbsd_amd64.go
+++ b/src/pkg/syscall/zerrors_openbsd_amd64.go
@@ -445,6 +445,7 @@ const (
F_WRLCK = 0x3
HUPCL = 0x4000
ICANON = 0x100
+ ICMP6_FILTER = 0x12
ICRNL = 0x100
IEXTEN = 0x400
IFAN_ARRIVAL = 0x0
@@ -932,6 +933,9 @@ const (
PENDIN = 0x20000000
PF_FLUSH = 0x1
PMC5_PIPELINE_FLUSH = 0x15
+ PRIO_PGRP = 0x1
+ PRIO_PROCESS = 0x0
+ PRIO_USER = 0x2
RLIMIT_CORE = 0x4
RLIMIT_CPU = 0x0
RLIMIT_DATA = 0x2
diff --git a/src/pkg/syscall/zsyscall_dragonfly_386.go b/src/pkg/syscall/zsyscall_dragonfly_386.go
new file mode 100644
index 000000000..5c3fe0713
--- /dev/null
+++ b/src/pkg/syscall/zsyscall_dragonfly_386.go
@@ -0,0 +1,1303 @@
+// mksyscall.pl -l32 -dragonfly syscall_bsd.go syscall_dragonfly.go syscall_dragonfly_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 getgroups(ngid int, gid *_Gid_t) (n int, err error) {
+ r0, _, e1 := RawSyscall(SYS_GETGROUPS, uintptr(ngid), uintptr(unsafe.Pointer(gid)), 0)
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func setgroups(ngid int, gid *_Gid_t) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETGROUPS, uintptr(ngid), uintptr(unsafe.Pointer(gid)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func wait4(pid int, wstatus *_C_int, options int, rusage *Rusage) (wpid int, err error) {
+ r0, _, e1 := Syscall6(SYS_WAIT4, uintptr(pid), uintptr(unsafe.Pointer(wstatus)), uintptr(options), uintptr(unsafe.Pointer(rusage)), 0, 0)
+ wpid = int(r0)
+ if e1 != 0 {
+ err = 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, err error) {
+ r0, _, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
+ fd = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func bind(s int, addr uintptr, addrlen _Socklen) (err error) {
+ _, _, e1 := Syscall(SYS_BIND, uintptr(s), uintptr(addr), uintptr(addrlen))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func connect(s int, addr uintptr, addrlen _Socklen) (err error) {
+ _, _, e1 := Syscall(SYS_CONNECT, uintptr(s), uintptr(addr), uintptr(addrlen))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func socket(domain int, typ int, proto int) (fd int, err error) {
+ r0, _, e1 := RawSyscall(SYS_SOCKET, uintptr(domain), uintptr(typ), uintptr(proto))
+ fd = int(r0)
+ if e1 != 0 {
+ err = 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) (err error) {
+ _, _, e1 := Syscall6(SYS_GETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(unsafe.Pointer(vallen)), 0)
+ if e1 != 0 {
+ err = 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 uintptr) (err error) {
+ _, _, e1 := Syscall6(SYS_SETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(vallen), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) {
+ _, _, e1 := RawSyscall(SYS_GETPEERNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) {
+ _, _, e1 := RawSyscall(SYS_GETSOCKNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Shutdown(s int, how int) (err error) {
+ _, _, e1 := Syscall(SYS_SHUTDOWN, uintptr(s), uintptr(how), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) {
+ _, _, e1 := RawSyscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (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_RECVFROM, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(flags), uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(fromlen)))
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func sendto(s int, buf []byte, flags int, to uintptr, addrlen _Socklen) (err error) {
+ var _p0 unsafe.Pointer
+ if len(buf) > 0 {
+ _p0 = unsafe.Pointer(&buf[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ _, _, e1 := Syscall6(SYS_SENDTO, uintptr(s), uintptr(_p0), uintptr(len(buf)), uintptr(flags), uintptr(to), uintptr(addrlen))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func recvmsg(s int, msg *Msghdr, flags int) (n int, err error) {
+ r0, _, e1 := Syscall(SYS_RECVMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags))
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func sendmsg(s int, msg *Msghdr, flags int) (err error) {
+ _, _, e1 := Syscall(SYS_SENDMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func kevent(kq int, change uintptr, nchange int, event uintptr, nevent int, timeout *Timespec) (n int, err error) {
+ r0, _, e1 := Syscall6(SYS_KEVENT, uintptr(kq), uintptr(change), uintptr(nchange), uintptr(event), uintptr(nevent), uintptr(unsafe.Pointer(timeout)))
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) {
+ var _p0 unsafe.Pointer
+ if len(mib) > 0 {
+ _p0 = unsafe.Pointer(&mib[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ _, _, e1 := Syscall6(SYS___SYSCTL, uintptr(_p0), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func utimes(path string, timeval *[2]Timeval) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(timeval)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func futimes(fd int, timeval *[2]Timeval) (err error) {
+ _, _, e1 := Syscall(SYS_FUTIMES, uintptr(fd), uintptr(unsafe.Pointer(timeval)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func fcntl(fd int, cmd int, arg int) (val int, err error) {
+ r0, _, e1 := Syscall(SYS_FCNTL, uintptr(fd), uintptr(cmd), uintptr(arg))
+ val = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func pipe() (r int, w int, err error) {
+ r0, r1, e1 := RawSyscall(SYS_PIPE, 0, 0, 0)
+ r = int(r0)
+ w = int(r1)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func extpread(fd int, p []byte, flags int, 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_EXTPREAD, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(flags), uintptr(offset), uintptr(offset>>32))
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func extpwrite(fd int, p []byte, flags int, 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_EXTPWRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(flags), uintptr(offset), uintptr(offset>>32))
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Access(path string, mode uint32) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Adjtime(delta *Timeval, olddelta *Timeval) (err error) {
+ _, _, e1 := Syscall(SYS_ADJTIME, uintptr(unsafe.Pointer(delta)), uintptr(unsafe.Pointer(olddelta)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Chdir(path string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Chflags(path string, flags int) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Chmod(path string, mode uint32) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Chown(path string, uid int, gid int) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Chroot(path string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(_p0)), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Close(fd int) (err error) {
+ _, _, e1 := Syscall(SYS_CLOSE, uintptr(fd), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Dup(fd int) (nfd int, err error) {
+ r0, _, e1 := RawSyscall(SYS_DUP, uintptr(fd), 0, 0)
+ nfd = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Dup2(from int, to int) (err error) {
+ _, _, e1 := RawSyscall(SYS_DUP2, uintptr(from), uintptr(to), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Exit(code int) {
+ Syscall(SYS_EXIT, uintptr(code), 0, 0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fchdir(fd int) (err error) {
+ _, _, e1 := Syscall(SYS_FCHDIR, uintptr(fd), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fchflags(fd int, flags int) (err error) {
+ _, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(fd), uintptr(flags), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fchmod(fd int, mode uint32) (err error) {
+ _, _, e1 := Syscall(SYS_FCHMOD, uintptr(fd), uintptr(mode), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fchown(fd int, uid int, gid int) (err error) {
+ _, _, e1 := Syscall(SYS_FCHOWN, uintptr(fd), uintptr(uid), uintptr(gid))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Flock(fd int, how int) (err error) {
+ _, _, e1 := Syscall(SYS_FLOCK, uintptr(fd), uintptr(how), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fpathconf(fd int, name int) (val int, err error) {
+ r0, _, e1 := Syscall(SYS_FPATHCONF, uintptr(fd), uintptr(name), 0)
+ val = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fstat(fd int, stat *Stat_t) (err error) {
+ _, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fstatfs(fd int, stat *Statfs_t) (err error) {
+ _, _, e1 := Syscall(SYS_FSTATFS, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fsync(fd int) (err error) {
+ _, _, e1 := Syscall(SYS_FSYNC, uintptr(fd), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Ftruncate(fd int, length int64) (err error) {
+ _, _, e1 := Syscall6(SYS_FTRUNCATE, uintptr(fd), 0, uintptr(length), uintptr(length>>32), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(buf) > 0 {
+ _p0 = unsafe.Pointer(&buf[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall6(SYS_GETDIRENTRIES, uintptr(fd), uintptr(_p0), uintptr(len(buf)), uintptr(unsafe.Pointer(basep)), 0, 0)
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getdtablesize() (size int) {
+ r0, _, _ := Syscall(SYS_GETDTABLESIZE, 0, 0, 0)
+ size = int(r0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getegid() (egid int) {
+ r0, _, _ := RawSyscall(SYS_GETEGID, 0, 0, 0)
+ egid = int(r0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Geteuid() (uid int) {
+ r0, _, _ := RawSyscall(SYS_GETEUID, 0, 0, 0)
+ uid = int(r0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getfsstat(buf []Statfs_t, flags int) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(buf) > 0 {
+ _p0 = unsafe.Pointer(&buf[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_GETFSSTAT, uintptr(_p0), uintptr(len(buf)), uintptr(flags))
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getgid() (gid int) {
+ r0, _, _ := RawSyscall(SYS_GETGID, 0, 0, 0)
+ gid = int(r0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getpgid(pid int) (pgid int, err error) {
+ r0, _, e1 := RawSyscall(SYS_GETPGID, uintptr(pid), 0, 0)
+ pgid = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getpgrp() (pgrp int) {
+ r0, _, _ := RawSyscall(SYS_GETPGRP, 0, 0, 0)
+ pgrp = int(r0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getpid() (pid int) {
+ r0, _, _ := RawSyscall(SYS_GETPID, 0, 0, 0)
+ pid = int(r0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getppid() (ppid int) {
+ r0, _, _ := RawSyscall(SYS_GETPPID, 0, 0, 0)
+ ppid = int(r0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getpriority(which int, who int) (prio int, err error) {
+ r0, _, e1 := Syscall(SYS_GETPRIORITY, uintptr(which), uintptr(who), 0)
+ prio = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getrlimit(which int, lim *Rlimit) (err error) {
+ _, _, e1 := RawSyscall(SYS_GETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getrusage(who int, rusage *Rusage) (err error) {
+ _, _, e1 := RawSyscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getsid(pid int) (sid int, err error) {
+ r0, _, e1 := RawSyscall(SYS_GETSID, uintptr(pid), 0, 0)
+ sid = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Gettimeofday(tv *Timeval) (err error) {
+ _, _, e1 := RawSyscall(SYS_GETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getuid() (uid int) {
+ r0, _, _ := RawSyscall(SYS_GETUID, 0, 0, 0)
+ uid = int(r0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Issetugid() (tainted bool) {
+ r0, _, _ := Syscall(SYS_ISSETUGID, 0, 0, 0)
+ tainted = bool(r0 != 0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Kill(pid int, signum Signal) (err error) {
+ _, _, e1 := Syscall(SYS_KILL, uintptr(pid), uintptr(signum), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Kqueue() (fd int, err error) {
+ r0, _, e1 := Syscall(SYS_KQUEUE, 0, 0, 0)
+ fd = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Lchown(path string, uid int, gid int) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Link(path string, link string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(link)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Listen(s int, backlog int) (err error) {
+ _, _, e1 := Syscall(SYS_LISTEN, uintptr(s), uintptr(backlog), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Lstat(path string, stat *Stat_t) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Mkdir(path string, mode uint32) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Mkfifo(path string, mode uint32) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Mknod(path string, mode uint32, dev int) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Nanosleep(time *Timespec, leftover *Timespec) (err error) {
+ _, _, e1 := Syscall(SYS_NANOSLEEP, uintptr(unsafe.Pointer(time)), uintptr(unsafe.Pointer(leftover)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Open(path string, mode int, perm uint32) (fd int, err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm))
+ fd = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Pathconf(path string, name int) (val int, err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(_p0)), uintptr(name), 0)
+ val = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func read(fd int, p []byte) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(p) > 0 {
+ _p0 = unsafe.Pointer(&p[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(_p0), uintptr(len(p)))
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Readlink(path string, buf []byte) (n int, err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 unsafe.Pointer
+ if len(buf) > 0 {
+ _p1 = unsafe.Pointer(&buf[0])
+ } else {
+ _p1 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(buf)))
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Rename(from string, to string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(from)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(to)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Revoke(path string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(_p0)), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Rmdir(path string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Seek(fd int, offset int64, whence int) (newoffset int64, err error) {
+ r0, r1, e1 := Syscall6(SYS_LSEEK, uintptr(fd), 0, uintptr(offset), uintptr(offset>>32), uintptr(whence), 0)
+ newoffset = int64(int64(r1)<<32 | int64(r0))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Select(n int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (err error) {
+ _, _, e1 := Syscall6(SYS_SELECT, uintptr(n), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setegid(egid int) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETEGID, uintptr(egid), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Seteuid(euid int) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETEUID, uintptr(euid), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setgid(gid int) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETGID, uintptr(gid), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setlogin(name string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(name)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_SETLOGIN, uintptr(unsafe.Pointer(_p0)), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setpgid(pid int, pgid int) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETPGID, uintptr(pid), uintptr(pgid), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setpriority(which int, who int, prio int) (err error) {
+ _, _, e1 := Syscall(SYS_SETPRIORITY, uintptr(which), uintptr(who), uintptr(prio))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setregid(rgid int, egid int) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETREGID, uintptr(rgid), uintptr(egid), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setreuid(ruid int, euid int) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETREUID, uintptr(ruid), uintptr(euid), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setrlimit(which int, lim *Rlimit) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setsid() (pid int, err error) {
+ r0, _, e1 := RawSyscall(SYS_SETSID, 0, 0, 0)
+ pid = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Settimeofday(tp *Timeval) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tp)), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setuid(uid int) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETUID, uintptr(uid), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Stat(path string, stat *Stat_t) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Statfs(path string, stat *Statfs_t) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_STATFS, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Symlink(path string, link string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(link)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Sync() (err error) {
+ _, _, e1 := Syscall(SYS_SYNC, 0, 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Truncate(path string, length int64) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall6(SYS_TRUNCATE, uintptr(unsafe.Pointer(_p0)), 0, uintptr(length), uintptr(length>>32), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Umask(newmask int) (oldmask int) {
+ r0, _, _ := Syscall(SYS_UMASK, uintptr(newmask), 0, 0)
+ oldmask = int(r0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Undelete(path string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UNDELETE, uintptr(unsafe.Pointer(_p0)), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Unlink(path string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(_p0)), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Unmount(path string, flags int) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func write(fd int, p []byte) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(p) > 0 {
+ _p0 = unsafe.Pointer(&p[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)))
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// 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, err error) {
+ r0, _, e1 := Syscall9(SYS_MMAP, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flag), uintptr(fd), 0, uintptr(pos), uintptr(pos>>32), 0)
+ ret = uintptr(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func munmap(addr uintptr, length uintptr) (err error) {
+ _, _, e1 := Syscall(SYS_MUNMAP, uintptr(addr), uintptr(length), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func readlen(fd int, buf *byte, nbuf int) (n int, err error) {
+ r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf))
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func writelen(fd int, buf *byte, nbuf int) (n int, err error) {
+ r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf))
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
diff --git a/src/pkg/syscall/zsyscall_dragonfly_amd64.go b/src/pkg/syscall/zsyscall_dragonfly_amd64.go
new file mode 100644
index 000000000..c7766a8c3
--- /dev/null
+++ b/src/pkg/syscall/zsyscall_dragonfly_amd64.go
@@ -0,0 +1,1303 @@
+// mksyscall.pl -dragonfly syscall_bsd.go syscall_dragonfly.go syscall_dragonfly_amd64.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 getgroups(ngid int, gid *_Gid_t) (n int, err error) {
+ r0, _, e1 := RawSyscall(SYS_GETGROUPS, uintptr(ngid), uintptr(unsafe.Pointer(gid)), 0)
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func setgroups(ngid int, gid *_Gid_t) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETGROUPS, uintptr(ngid), uintptr(unsafe.Pointer(gid)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func wait4(pid int, wstatus *_C_int, options int, rusage *Rusage) (wpid int, err error) {
+ r0, _, e1 := Syscall6(SYS_WAIT4, uintptr(pid), uintptr(unsafe.Pointer(wstatus)), uintptr(options), uintptr(unsafe.Pointer(rusage)), 0, 0)
+ wpid = int(r0)
+ if e1 != 0 {
+ err = 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, err error) {
+ r0, _, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
+ fd = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func bind(s int, addr uintptr, addrlen _Socklen) (err error) {
+ _, _, e1 := Syscall(SYS_BIND, uintptr(s), uintptr(addr), uintptr(addrlen))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func connect(s int, addr uintptr, addrlen _Socklen) (err error) {
+ _, _, e1 := Syscall(SYS_CONNECT, uintptr(s), uintptr(addr), uintptr(addrlen))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func socket(domain int, typ int, proto int) (fd int, err error) {
+ r0, _, e1 := RawSyscall(SYS_SOCKET, uintptr(domain), uintptr(typ), uintptr(proto))
+ fd = int(r0)
+ if e1 != 0 {
+ err = 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) (err error) {
+ _, _, e1 := Syscall6(SYS_GETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(unsafe.Pointer(vallen)), 0)
+ if e1 != 0 {
+ err = 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 uintptr) (err error) {
+ _, _, e1 := Syscall6(SYS_SETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(vallen), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) {
+ _, _, e1 := RawSyscall(SYS_GETPEERNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) {
+ _, _, e1 := RawSyscall(SYS_GETSOCKNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Shutdown(s int, how int) (err error) {
+ _, _, e1 := Syscall(SYS_SHUTDOWN, uintptr(s), uintptr(how), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) {
+ _, _, e1 := RawSyscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (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_RECVFROM, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(flags), uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(fromlen)))
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func sendto(s int, buf []byte, flags int, to uintptr, addrlen _Socklen) (err error) {
+ var _p0 unsafe.Pointer
+ if len(buf) > 0 {
+ _p0 = unsafe.Pointer(&buf[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ _, _, e1 := Syscall6(SYS_SENDTO, uintptr(s), uintptr(_p0), uintptr(len(buf)), uintptr(flags), uintptr(to), uintptr(addrlen))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func recvmsg(s int, msg *Msghdr, flags int) (n int, err error) {
+ r0, _, e1 := Syscall(SYS_RECVMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags))
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func sendmsg(s int, msg *Msghdr, flags int) (err error) {
+ _, _, e1 := Syscall(SYS_SENDMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func kevent(kq int, change uintptr, nchange int, event uintptr, nevent int, timeout *Timespec) (n int, err error) {
+ r0, _, e1 := Syscall6(SYS_KEVENT, uintptr(kq), uintptr(change), uintptr(nchange), uintptr(event), uintptr(nevent), uintptr(unsafe.Pointer(timeout)))
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) {
+ var _p0 unsafe.Pointer
+ if len(mib) > 0 {
+ _p0 = unsafe.Pointer(&mib[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ _, _, e1 := Syscall6(SYS___SYSCTL, uintptr(_p0), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func utimes(path string, timeval *[2]Timeval) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(timeval)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func futimes(fd int, timeval *[2]Timeval) (err error) {
+ _, _, e1 := Syscall(SYS_FUTIMES, uintptr(fd), uintptr(unsafe.Pointer(timeval)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func fcntl(fd int, cmd int, arg int) (val int, err error) {
+ r0, _, e1 := Syscall(SYS_FCNTL, uintptr(fd), uintptr(cmd), uintptr(arg))
+ val = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func pipe() (r int, w int, err error) {
+ r0, r1, e1 := RawSyscall(SYS_PIPE, 0, 0, 0)
+ r = int(r0)
+ w = int(r1)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func extpread(fd int, p []byte, flags int, 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_EXTPREAD, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(flags), uintptr(offset), 0)
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func extpwrite(fd int, p []byte, flags int, 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_EXTPWRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(flags), uintptr(offset), 0)
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Access(path string, mode uint32) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Adjtime(delta *Timeval, olddelta *Timeval) (err error) {
+ _, _, e1 := Syscall(SYS_ADJTIME, uintptr(unsafe.Pointer(delta)), uintptr(unsafe.Pointer(olddelta)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Chdir(path string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Chflags(path string, flags int) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Chmod(path string, mode uint32) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Chown(path string, uid int, gid int) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Chroot(path string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(_p0)), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Close(fd int) (err error) {
+ _, _, e1 := Syscall(SYS_CLOSE, uintptr(fd), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Dup(fd int) (nfd int, err error) {
+ r0, _, e1 := RawSyscall(SYS_DUP, uintptr(fd), 0, 0)
+ nfd = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Dup2(from int, to int) (err error) {
+ _, _, e1 := RawSyscall(SYS_DUP2, uintptr(from), uintptr(to), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Exit(code int) {
+ Syscall(SYS_EXIT, uintptr(code), 0, 0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fchdir(fd int) (err error) {
+ _, _, e1 := Syscall(SYS_FCHDIR, uintptr(fd), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fchflags(fd int, flags int) (err error) {
+ _, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(fd), uintptr(flags), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fchmod(fd int, mode uint32) (err error) {
+ _, _, e1 := Syscall(SYS_FCHMOD, uintptr(fd), uintptr(mode), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fchown(fd int, uid int, gid int) (err error) {
+ _, _, e1 := Syscall(SYS_FCHOWN, uintptr(fd), uintptr(uid), uintptr(gid))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Flock(fd int, how int) (err error) {
+ _, _, e1 := Syscall(SYS_FLOCK, uintptr(fd), uintptr(how), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fpathconf(fd int, name int) (val int, err error) {
+ r0, _, e1 := Syscall(SYS_FPATHCONF, uintptr(fd), uintptr(name), 0)
+ val = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fstat(fd int, stat *Stat_t) (err error) {
+ _, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fstatfs(fd int, stat *Statfs_t) (err error) {
+ _, _, e1 := Syscall(SYS_FSTATFS, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fsync(fd int) (err error) {
+ _, _, e1 := Syscall(SYS_FSYNC, uintptr(fd), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Ftruncate(fd int, length int64) (err error) {
+ _, _, e1 := Syscall(SYS_FTRUNCATE, uintptr(fd), 0, uintptr(length))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(buf) > 0 {
+ _p0 = unsafe.Pointer(&buf[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall6(SYS_GETDIRENTRIES, uintptr(fd), uintptr(_p0), uintptr(len(buf)), uintptr(unsafe.Pointer(basep)), 0, 0)
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getdtablesize() (size int) {
+ r0, _, _ := Syscall(SYS_GETDTABLESIZE, 0, 0, 0)
+ size = int(r0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getegid() (egid int) {
+ r0, _, _ := RawSyscall(SYS_GETEGID, 0, 0, 0)
+ egid = int(r0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Geteuid() (uid int) {
+ r0, _, _ := RawSyscall(SYS_GETEUID, 0, 0, 0)
+ uid = int(r0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getfsstat(buf []Statfs_t, flags int) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(buf) > 0 {
+ _p0 = unsafe.Pointer(&buf[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_GETFSSTAT, uintptr(_p0), uintptr(len(buf)), uintptr(flags))
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getgid() (gid int) {
+ r0, _, _ := RawSyscall(SYS_GETGID, 0, 0, 0)
+ gid = int(r0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getpgid(pid int) (pgid int, err error) {
+ r0, _, e1 := RawSyscall(SYS_GETPGID, uintptr(pid), 0, 0)
+ pgid = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getpgrp() (pgrp int) {
+ r0, _, _ := RawSyscall(SYS_GETPGRP, 0, 0, 0)
+ pgrp = int(r0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getpid() (pid int) {
+ r0, _, _ := RawSyscall(SYS_GETPID, 0, 0, 0)
+ pid = int(r0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getppid() (ppid int) {
+ r0, _, _ := RawSyscall(SYS_GETPPID, 0, 0, 0)
+ ppid = int(r0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getpriority(which int, who int) (prio int, err error) {
+ r0, _, e1 := Syscall(SYS_GETPRIORITY, uintptr(which), uintptr(who), 0)
+ prio = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getrlimit(which int, lim *Rlimit) (err error) {
+ _, _, e1 := RawSyscall(SYS_GETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getrusage(who int, rusage *Rusage) (err error) {
+ _, _, e1 := RawSyscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getsid(pid int) (sid int, err error) {
+ r0, _, e1 := RawSyscall(SYS_GETSID, uintptr(pid), 0, 0)
+ sid = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Gettimeofday(tv *Timeval) (err error) {
+ _, _, e1 := RawSyscall(SYS_GETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getuid() (uid int) {
+ r0, _, _ := RawSyscall(SYS_GETUID, 0, 0, 0)
+ uid = int(r0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Issetugid() (tainted bool) {
+ r0, _, _ := Syscall(SYS_ISSETUGID, 0, 0, 0)
+ tainted = bool(r0 != 0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Kill(pid int, signum Signal) (err error) {
+ _, _, e1 := Syscall(SYS_KILL, uintptr(pid), uintptr(signum), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Kqueue() (fd int, err error) {
+ r0, _, e1 := Syscall(SYS_KQUEUE, 0, 0, 0)
+ fd = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Lchown(path string, uid int, gid int) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Link(path string, link string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(link)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Listen(s int, backlog int) (err error) {
+ _, _, e1 := Syscall(SYS_LISTEN, uintptr(s), uintptr(backlog), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Lstat(path string, stat *Stat_t) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Mkdir(path string, mode uint32) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Mkfifo(path string, mode uint32) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Mknod(path string, mode uint32, dev int) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Nanosleep(time *Timespec, leftover *Timespec) (err error) {
+ _, _, e1 := Syscall(SYS_NANOSLEEP, uintptr(unsafe.Pointer(time)), uintptr(unsafe.Pointer(leftover)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Open(path string, mode int, perm uint32) (fd int, err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm))
+ fd = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Pathconf(path string, name int) (val int, err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(_p0)), uintptr(name), 0)
+ val = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func read(fd int, p []byte) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(p) > 0 {
+ _p0 = unsafe.Pointer(&p[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(_p0), uintptr(len(p)))
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Readlink(path string, buf []byte) (n int, err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 unsafe.Pointer
+ if len(buf) > 0 {
+ _p1 = unsafe.Pointer(&buf[0])
+ } else {
+ _p1 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(buf)))
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Rename(from string, to string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(from)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(to)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Revoke(path string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(_p0)), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Rmdir(path string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Seek(fd int, offset int64, whence int) (newoffset int64, err error) {
+ r0, _, e1 := Syscall6(SYS_LSEEK, uintptr(fd), 0, uintptr(offset), uintptr(whence), 0, 0)
+ newoffset = int64(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Select(n int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (err error) {
+ _, _, e1 := Syscall6(SYS_SELECT, uintptr(n), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setegid(egid int) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETEGID, uintptr(egid), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Seteuid(euid int) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETEUID, uintptr(euid), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setgid(gid int) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETGID, uintptr(gid), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setlogin(name string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(name)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_SETLOGIN, uintptr(unsafe.Pointer(_p0)), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setpgid(pid int, pgid int) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETPGID, uintptr(pid), uintptr(pgid), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setpriority(which int, who int, prio int) (err error) {
+ _, _, e1 := Syscall(SYS_SETPRIORITY, uintptr(which), uintptr(who), uintptr(prio))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setregid(rgid int, egid int) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETREGID, uintptr(rgid), uintptr(egid), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setreuid(ruid int, euid int) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETREUID, uintptr(ruid), uintptr(euid), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setrlimit(which int, lim *Rlimit) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setsid() (pid int, err error) {
+ r0, _, e1 := RawSyscall(SYS_SETSID, 0, 0, 0)
+ pid = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Settimeofday(tp *Timeval) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tp)), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setuid(uid int) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETUID, uintptr(uid), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Stat(path string, stat *Stat_t) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Statfs(path string, stat *Statfs_t) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_STATFS, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Symlink(path string, link string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(link)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Sync() (err error) {
+ _, _, e1 := Syscall(SYS_SYNC, 0, 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Truncate(path string, length int64) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_TRUNCATE, uintptr(unsafe.Pointer(_p0)), 0, uintptr(length))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Umask(newmask int) (oldmask int) {
+ r0, _, _ := Syscall(SYS_UMASK, uintptr(newmask), 0, 0)
+ oldmask = int(r0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Undelete(path string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UNDELETE, uintptr(unsafe.Pointer(_p0)), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Unlink(path string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(_p0)), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Unmount(path string, flags int) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func write(fd int, p []byte) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(p) > 0 {
+ _p0 = unsafe.Pointer(&p[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)))
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// 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, err error) {
+ r0, _, e1 := Syscall9(SYS_MMAP, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flag), uintptr(fd), 0, uintptr(pos), 0, 0)
+ ret = uintptr(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func munmap(addr uintptr, length uintptr) (err error) {
+ _, _, e1 := Syscall(SYS_MUNMAP, uintptr(addr), uintptr(length), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func readlen(fd int, buf *byte, nbuf int) (n int, err error) {
+ r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf))
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func writelen(fd int, buf *byte, nbuf int) (n int, err error) {
+ r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf))
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
diff --git a/src/pkg/syscall/zsyscall_linux_386.go b/src/pkg/syscall/zsyscall_linux_386.go
index 3f0470c98..6eeb11828 100644
--- a/src/pkg/syscall/zsyscall_linux_386.go
+++ b/src/pkg/syscall/zsyscall_linux_386.go
@@ -310,6 +310,16 @@ func Dup2(oldfd int, newfd int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func Dup3(oldfd int, newfd int, flags int) (err error) {
+ _, _, e1 := RawSyscall(SYS_DUP3, uintptr(oldfd), uintptr(newfd), uintptr(flags))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func EpollCreate(size int) (fd int, err error) {
r0, _, e1 := RawSyscall(SYS_EPOLL_CREATE, uintptr(size), 0, 0)
fd = int(r0)
@@ -534,6 +544,17 @@ func Getppid() (ppid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func Getpriority(which int, who int) (prio int, err error) {
+ r0, _, e1 := Syscall(SYS_GETPRIORITY, uintptr(which), uintptr(who), 0)
+ prio = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func Getrusage(who int, rusage *Rusage) (err error) {
_, _, e1 := RawSyscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0)
if e1 != 0 {
@@ -994,6 +1015,16 @@ func Setuid(uid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func Setpriority(which int, who int, prio int) (err error) {
+ _, _, e1 := Syscall(SYS_SETPRIORITY, uintptr(which), uintptr(who), uintptr(prio))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func Setxattr(path string, attr string, data []byte, flags int) (err error) {
var _p0 *byte
_p0, err = BytePtrFromString(path)
diff --git a/src/pkg/syscall/zsyscall_linux_amd64.go b/src/pkg/syscall/zsyscall_linux_amd64.go
index 43f24e773..c1c3d4511 100644
--- a/src/pkg/syscall/zsyscall_linux_amd64.go
+++ b/src/pkg/syscall/zsyscall_linux_amd64.go
@@ -310,6 +310,16 @@ func Dup2(oldfd int, newfd int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func Dup3(oldfd int, newfd int, flags int) (err error) {
+ _, _, e1 := RawSyscall(SYS_DUP3, uintptr(oldfd), uintptr(newfd), uintptr(flags))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func EpollCreate(size int) (fd int, err error) {
r0, _, e1 := RawSyscall(SYS_EPOLL_CREATE, uintptr(size), 0, 0)
fd = int(r0)
@@ -534,6 +544,17 @@ func Getppid() (ppid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func Getpriority(which int, who int) (prio int, err error) {
+ r0, _, e1 := Syscall(SYS_GETPRIORITY, uintptr(which), uintptr(who), 0)
+ prio = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func Getrusage(who int, rusage *Rusage) (err error) {
_, _, e1 := RawSyscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0)
if e1 != 0 {
@@ -994,6 +1015,16 @@ func Setuid(uid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func Setpriority(which int, who int, prio int) (err error) {
+ _, _, e1 := Syscall(SYS_SETPRIORITY, uintptr(which), uintptr(who), uintptr(prio))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func Setxattr(path string, attr string, data []byte, flags int) (err error) {
var _p0 *byte
_p0, err = BytePtrFromString(path)
diff --git a/src/pkg/syscall/zsyscall_linux_arm.go b/src/pkg/syscall/zsyscall_linux_arm.go
index 804821eb3..3380714a6 100644
--- a/src/pkg/syscall/zsyscall_linux_arm.go
+++ b/src/pkg/syscall/zsyscall_linux_arm.go
@@ -310,6 +310,16 @@ func Dup2(oldfd int, newfd int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func Dup3(oldfd int, newfd int, flags int) (err error) {
+ _, _, e1 := RawSyscall(SYS_DUP3, uintptr(oldfd), uintptr(newfd), uintptr(flags))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func EpollCreate(size int) (fd int, err error) {
r0, _, e1 := RawSyscall(SYS_EPOLL_CREATE, uintptr(size), 0, 0)
fd = int(r0)
@@ -534,6 +544,17 @@ func Getppid() (ppid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func Getpriority(which int, who int) (prio int, err error) {
+ r0, _, e1 := Syscall(SYS_GETPRIORITY, uintptr(which), uintptr(who), 0)
+ prio = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func Getrusage(who int, rusage *Rusage) (err error) {
_, _, e1 := RawSyscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0)
if e1 != 0 {
@@ -994,6 +1015,16 @@ func Setuid(uid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func Setpriority(which int, who int, prio int) (err error) {
+ _, _, e1 := Syscall(SYS_SETPRIORITY, uintptr(which), uintptr(who), uintptr(prio))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func Setxattr(path string, attr string, data []byte, flags int) (err error) {
var _p0 *byte
_p0, err = BytePtrFromString(path)
diff --git a/src/pkg/syscall/zsyscall_windows_386.go b/src/pkg/syscall/zsyscall_windows_386.go
index e5c48488b..3cd12dd47 100644
--- a/src/pkg/syscall/zsyscall_windows_386.go
+++ b/src/pkg/syscall/zsyscall_windows_386.go
@@ -18,138 +18,141 @@ var (
modnetapi32 = NewLazyDLL("netapi32.dll")
moduserenv = NewLazyDLL("userenv.dll")
- procGetLastError = modkernel32.NewProc("GetLastError")
- procLoadLibraryW = modkernel32.NewProc("LoadLibraryW")
- procFreeLibrary = modkernel32.NewProc("FreeLibrary")
- procGetProcAddress = modkernel32.NewProc("GetProcAddress")
- procGetVersion = modkernel32.NewProc("GetVersion")
- procFormatMessageW = modkernel32.NewProc("FormatMessageW")
- procExitProcess = modkernel32.NewProc("ExitProcess")
- procCreateFileW = modkernel32.NewProc("CreateFileW")
- procReadFile = modkernel32.NewProc("ReadFile")
- procWriteFile = modkernel32.NewProc("WriteFile")
- procSetFilePointer = modkernel32.NewProc("SetFilePointer")
- procCloseHandle = modkernel32.NewProc("CloseHandle")
- procGetStdHandle = modkernel32.NewProc("GetStdHandle")
- procFindFirstFileW = modkernel32.NewProc("FindFirstFileW")
- procFindNextFileW = modkernel32.NewProc("FindNextFileW")
- procFindClose = modkernel32.NewProc("FindClose")
- procGetFileInformationByHandle = modkernel32.NewProc("GetFileInformationByHandle")
- procGetCurrentDirectoryW = modkernel32.NewProc("GetCurrentDirectoryW")
- procSetCurrentDirectoryW = modkernel32.NewProc("SetCurrentDirectoryW")
- procCreateDirectoryW = modkernel32.NewProc("CreateDirectoryW")
- procRemoveDirectoryW = modkernel32.NewProc("RemoveDirectoryW")
- procDeleteFileW = modkernel32.NewProc("DeleteFileW")
- procMoveFileW = modkernel32.NewProc("MoveFileW")
- procGetComputerNameW = modkernel32.NewProc("GetComputerNameW")
- procSetEndOfFile = modkernel32.NewProc("SetEndOfFile")
- procGetSystemTimeAsFileTime = modkernel32.NewProc("GetSystemTimeAsFileTime")
- procGetTimeZoneInformation = modkernel32.NewProc("GetTimeZoneInformation")
- procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort")
- procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
- procPostQueuedCompletionStatus = modkernel32.NewProc("PostQueuedCompletionStatus")
- procCancelIo = modkernel32.NewProc("CancelIo")
- procCancelIoEx = modkernel32.NewProc("CancelIoEx")
- procCreateProcessW = modkernel32.NewProc("CreateProcessW")
- procOpenProcess = modkernel32.NewProc("OpenProcess")
- procTerminateProcess = modkernel32.NewProc("TerminateProcess")
- procGetExitCodeProcess = modkernel32.NewProc("GetExitCodeProcess")
- procGetStartupInfoW = modkernel32.NewProc("GetStartupInfoW")
- procGetCurrentProcess = modkernel32.NewProc("GetCurrentProcess")
- procGetProcessTimes = modkernel32.NewProc("GetProcessTimes")
- procDuplicateHandle = modkernel32.NewProc("DuplicateHandle")
- procWaitForSingleObject = modkernel32.NewProc("WaitForSingleObject")
- procGetTempPathW = modkernel32.NewProc("GetTempPathW")
- procCreatePipe = modkernel32.NewProc("CreatePipe")
- procGetFileType = modkernel32.NewProc("GetFileType")
- procCryptAcquireContextW = modadvapi32.NewProc("CryptAcquireContextW")
- procCryptReleaseContext = modadvapi32.NewProc("CryptReleaseContext")
- procCryptGenRandom = modadvapi32.NewProc("CryptGenRandom")
- procGetEnvironmentStringsW = modkernel32.NewProc("GetEnvironmentStringsW")
- procFreeEnvironmentStringsW = modkernel32.NewProc("FreeEnvironmentStringsW")
- procGetEnvironmentVariableW = modkernel32.NewProc("GetEnvironmentVariableW")
- procSetEnvironmentVariableW = modkernel32.NewProc("SetEnvironmentVariableW")
- procSetFileTime = modkernel32.NewProc("SetFileTime")
- procGetFileAttributesW = modkernel32.NewProc("GetFileAttributesW")
- procSetFileAttributesW = modkernel32.NewProc("SetFileAttributesW")
- procGetFileAttributesExW = modkernel32.NewProc("GetFileAttributesExW")
- procGetCommandLineW = modkernel32.NewProc("GetCommandLineW")
- procCommandLineToArgvW = modshell32.NewProc("CommandLineToArgvW")
- procLocalFree = modkernel32.NewProc("LocalFree")
- procSetHandleInformation = modkernel32.NewProc("SetHandleInformation")
- procFlushFileBuffers = modkernel32.NewProc("FlushFileBuffers")
- procGetFullPathNameW = modkernel32.NewProc("GetFullPathNameW")
- procGetLongPathNameW = modkernel32.NewProc("GetLongPathNameW")
- procGetShortPathNameW = modkernel32.NewProc("GetShortPathNameW")
- procCreateFileMappingW = modkernel32.NewProc("CreateFileMappingW")
- procMapViewOfFile = modkernel32.NewProc("MapViewOfFile")
- procUnmapViewOfFile = modkernel32.NewProc("UnmapViewOfFile")
- procFlushViewOfFile = modkernel32.NewProc("FlushViewOfFile")
- procVirtualLock = modkernel32.NewProc("VirtualLock")
- procVirtualUnlock = modkernel32.NewProc("VirtualUnlock")
- procTransmitFile = modmswsock.NewProc("TransmitFile")
- procReadDirectoryChangesW = modkernel32.NewProc("ReadDirectoryChangesW")
- procCertOpenSystemStoreW = modcrypt32.NewProc("CertOpenSystemStoreW")
- procCertOpenStore = modcrypt32.NewProc("CertOpenStore")
- procCertEnumCertificatesInStore = modcrypt32.NewProc("CertEnumCertificatesInStore")
- procCertAddCertificateContextToStore = modcrypt32.NewProc("CertAddCertificateContextToStore")
- procCertCloseStore = modcrypt32.NewProc("CertCloseStore")
- procCertGetCertificateChain = modcrypt32.NewProc("CertGetCertificateChain")
- procCertFreeCertificateChain = modcrypt32.NewProc("CertFreeCertificateChain")
- procCertCreateCertificateContext = modcrypt32.NewProc("CertCreateCertificateContext")
- procCertFreeCertificateContext = modcrypt32.NewProc("CertFreeCertificateContext")
- procCertVerifyCertificateChainPolicy = modcrypt32.NewProc("CertVerifyCertificateChainPolicy")
- procRegOpenKeyExW = modadvapi32.NewProc("RegOpenKeyExW")
- procRegCloseKey = modadvapi32.NewProc("RegCloseKey")
- procRegQueryInfoKeyW = modadvapi32.NewProc("RegQueryInfoKeyW")
- procRegEnumKeyExW = modadvapi32.NewProc("RegEnumKeyExW")
- procRegQueryValueExW = modadvapi32.NewProc("RegQueryValueExW")
- procGetCurrentProcessId = modkernel32.NewProc("GetCurrentProcessId")
- procGetConsoleMode = modkernel32.NewProc("GetConsoleMode")
- procWriteConsoleW = modkernel32.NewProc("WriteConsoleW")
- procReadConsoleW = modkernel32.NewProc("ReadConsoleW")
- procWSAStartup = modws2_32.NewProc("WSAStartup")
- procWSACleanup = modws2_32.NewProc("WSACleanup")
- procWSAIoctl = modws2_32.NewProc("WSAIoctl")
- procsocket = modws2_32.NewProc("socket")
- procsetsockopt = modws2_32.NewProc("setsockopt")
- procgetsockopt = modws2_32.NewProc("getsockopt")
- procbind = modws2_32.NewProc("bind")
- procconnect = modws2_32.NewProc("connect")
- procgetsockname = modws2_32.NewProc("getsockname")
- procgetpeername = modws2_32.NewProc("getpeername")
- proclisten = modws2_32.NewProc("listen")
- procshutdown = modws2_32.NewProc("shutdown")
- procclosesocket = modws2_32.NewProc("closesocket")
- procAcceptEx = modmswsock.NewProc("AcceptEx")
- procGetAcceptExSockaddrs = modmswsock.NewProc("GetAcceptExSockaddrs")
- procWSARecv = modws2_32.NewProc("WSARecv")
- procWSASend = modws2_32.NewProc("WSASend")
- procWSARecvFrom = modws2_32.NewProc("WSARecvFrom")
- procWSASendTo = modws2_32.NewProc("WSASendTo")
- procgethostbyname = modws2_32.NewProc("gethostbyname")
- procgetservbyname = modws2_32.NewProc("getservbyname")
- procntohs = modws2_32.NewProc("ntohs")
- procgetprotobyname = modws2_32.NewProc("getprotobyname")
- procDnsQuery_W = moddnsapi.NewProc("DnsQuery_W")
- procDnsRecordListFree = moddnsapi.NewProc("DnsRecordListFree")
- procGetAddrInfoW = modws2_32.NewProc("GetAddrInfoW")
- procFreeAddrInfoW = modws2_32.NewProc("FreeAddrInfoW")
- procGetIfEntry = modiphlpapi.NewProc("GetIfEntry")
- procGetAdaptersInfo = modiphlpapi.NewProc("GetAdaptersInfo")
- procTranslateNameW = modsecur32.NewProc("TranslateNameW")
- procGetUserNameExW = modsecur32.NewProc("GetUserNameExW")
- procNetUserGetInfo = modnetapi32.NewProc("NetUserGetInfo")
- procNetApiBufferFree = modnetapi32.NewProc("NetApiBufferFree")
- procLookupAccountSidW = modadvapi32.NewProc("LookupAccountSidW")
- procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW")
- procConvertSidToStringSidW = modadvapi32.NewProc("ConvertSidToStringSidW")
- procConvertStringSidToSidW = modadvapi32.NewProc("ConvertStringSidToSidW")
- procGetLengthSid = modadvapi32.NewProc("GetLengthSid")
- procCopySid = modadvapi32.NewProc("CopySid")
- procOpenProcessToken = modadvapi32.NewProc("OpenProcessToken")
- procGetTokenInformation = modadvapi32.NewProc("GetTokenInformation")
- procGetUserProfileDirectoryW = moduserenv.NewProc("GetUserProfileDirectoryW")
+ procGetLastError = modkernel32.NewProc("GetLastError")
+ procLoadLibraryW = modkernel32.NewProc("LoadLibraryW")
+ procFreeLibrary = modkernel32.NewProc("FreeLibrary")
+ procGetProcAddress = modkernel32.NewProc("GetProcAddress")
+ procGetVersion = modkernel32.NewProc("GetVersion")
+ procFormatMessageW = modkernel32.NewProc("FormatMessageW")
+ procExitProcess = modkernel32.NewProc("ExitProcess")
+ procCreateFileW = modkernel32.NewProc("CreateFileW")
+ procReadFile = modkernel32.NewProc("ReadFile")
+ procWriteFile = modkernel32.NewProc("WriteFile")
+ procSetFilePointer = modkernel32.NewProc("SetFilePointer")
+ procCloseHandle = modkernel32.NewProc("CloseHandle")
+ procGetStdHandle = modkernel32.NewProc("GetStdHandle")
+ procFindFirstFileW = modkernel32.NewProc("FindFirstFileW")
+ procFindNextFileW = modkernel32.NewProc("FindNextFileW")
+ procFindClose = modkernel32.NewProc("FindClose")
+ procGetFileInformationByHandle = modkernel32.NewProc("GetFileInformationByHandle")
+ procGetCurrentDirectoryW = modkernel32.NewProc("GetCurrentDirectoryW")
+ procSetCurrentDirectoryW = modkernel32.NewProc("SetCurrentDirectoryW")
+ procCreateDirectoryW = modkernel32.NewProc("CreateDirectoryW")
+ procRemoveDirectoryW = modkernel32.NewProc("RemoveDirectoryW")
+ procDeleteFileW = modkernel32.NewProc("DeleteFileW")
+ procMoveFileW = modkernel32.NewProc("MoveFileW")
+ procGetComputerNameW = modkernel32.NewProc("GetComputerNameW")
+ procSetEndOfFile = modkernel32.NewProc("SetEndOfFile")
+ procGetSystemTimeAsFileTime = modkernel32.NewProc("GetSystemTimeAsFileTime")
+ procGetTimeZoneInformation = modkernel32.NewProc("GetTimeZoneInformation")
+ procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort")
+ procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
+ procPostQueuedCompletionStatus = modkernel32.NewProc("PostQueuedCompletionStatus")
+ procCancelIo = modkernel32.NewProc("CancelIo")
+ procCancelIoEx = modkernel32.NewProc("CancelIoEx")
+ procCreateProcessW = modkernel32.NewProc("CreateProcessW")
+ procOpenProcess = modkernel32.NewProc("OpenProcess")
+ procTerminateProcess = modkernel32.NewProc("TerminateProcess")
+ procGetExitCodeProcess = modkernel32.NewProc("GetExitCodeProcess")
+ procGetStartupInfoW = modkernel32.NewProc("GetStartupInfoW")
+ procGetCurrentProcess = modkernel32.NewProc("GetCurrentProcess")
+ procGetProcessTimes = modkernel32.NewProc("GetProcessTimes")
+ procDuplicateHandle = modkernel32.NewProc("DuplicateHandle")
+ procWaitForSingleObject = modkernel32.NewProc("WaitForSingleObject")
+ procGetTempPathW = modkernel32.NewProc("GetTempPathW")
+ procCreatePipe = modkernel32.NewProc("CreatePipe")
+ procGetFileType = modkernel32.NewProc("GetFileType")
+ procCryptAcquireContextW = modadvapi32.NewProc("CryptAcquireContextW")
+ procCryptReleaseContext = modadvapi32.NewProc("CryptReleaseContext")
+ procCryptGenRandom = modadvapi32.NewProc("CryptGenRandom")
+ procGetEnvironmentStringsW = modkernel32.NewProc("GetEnvironmentStringsW")
+ procFreeEnvironmentStringsW = modkernel32.NewProc("FreeEnvironmentStringsW")
+ procGetEnvironmentVariableW = modkernel32.NewProc("GetEnvironmentVariableW")
+ procSetEnvironmentVariableW = modkernel32.NewProc("SetEnvironmentVariableW")
+ procSetFileTime = modkernel32.NewProc("SetFileTime")
+ procGetFileAttributesW = modkernel32.NewProc("GetFileAttributesW")
+ procSetFileAttributesW = modkernel32.NewProc("SetFileAttributesW")
+ procGetFileAttributesExW = modkernel32.NewProc("GetFileAttributesExW")
+ procGetCommandLineW = modkernel32.NewProc("GetCommandLineW")
+ procCommandLineToArgvW = modshell32.NewProc("CommandLineToArgvW")
+ procLocalFree = modkernel32.NewProc("LocalFree")
+ procSetHandleInformation = modkernel32.NewProc("SetHandleInformation")
+ procFlushFileBuffers = modkernel32.NewProc("FlushFileBuffers")
+ procGetFullPathNameW = modkernel32.NewProc("GetFullPathNameW")
+ procGetLongPathNameW = modkernel32.NewProc("GetLongPathNameW")
+ procGetShortPathNameW = modkernel32.NewProc("GetShortPathNameW")
+ procCreateFileMappingW = modkernel32.NewProc("CreateFileMappingW")
+ procMapViewOfFile = modkernel32.NewProc("MapViewOfFile")
+ procUnmapViewOfFile = modkernel32.NewProc("UnmapViewOfFile")
+ procFlushViewOfFile = modkernel32.NewProc("FlushViewOfFile")
+ procVirtualLock = modkernel32.NewProc("VirtualLock")
+ procVirtualUnlock = modkernel32.NewProc("VirtualUnlock")
+ procTransmitFile = modmswsock.NewProc("TransmitFile")
+ procReadDirectoryChangesW = modkernel32.NewProc("ReadDirectoryChangesW")
+ procCertOpenSystemStoreW = modcrypt32.NewProc("CertOpenSystemStoreW")
+ procCertOpenStore = modcrypt32.NewProc("CertOpenStore")
+ procCertEnumCertificatesInStore = modcrypt32.NewProc("CertEnumCertificatesInStore")
+ procCertAddCertificateContextToStore = modcrypt32.NewProc("CertAddCertificateContextToStore")
+ procCertCloseStore = modcrypt32.NewProc("CertCloseStore")
+ procCertGetCertificateChain = modcrypt32.NewProc("CertGetCertificateChain")
+ procCertFreeCertificateChain = modcrypt32.NewProc("CertFreeCertificateChain")
+ procCertCreateCertificateContext = modcrypt32.NewProc("CertCreateCertificateContext")
+ procCertFreeCertificateContext = modcrypt32.NewProc("CertFreeCertificateContext")
+ procCertVerifyCertificateChainPolicy = modcrypt32.NewProc("CertVerifyCertificateChainPolicy")
+ procRegOpenKeyExW = modadvapi32.NewProc("RegOpenKeyExW")
+ procRegCloseKey = modadvapi32.NewProc("RegCloseKey")
+ procRegQueryInfoKeyW = modadvapi32.NewProc("RegQueryInfoKeyW")
+ procRegEnumKeyExW = modadvapi32.NewProc("RegEnumKeyExW")
+ procRegQueryValueExW = modadvapi32.NewProc("RegQueryValueExW")
+ procGetCurrentProcessId = modkernel32.NewProc("GetCurrentProcessId")
+ procGetConsoleMode = modkernel32.NewProc("GetConsoleMode")
+ procWriteConsoleW = modkernel32.NewProc("WriteConsoleW")
+ procReadConsoleW = modkernel32.NewProc("ReadConsoleW")
+ procWSAStartup = modws2_32.NewProc("WSAStartup")
+ procWSACleanup = modws2_32.NewProc("WSACleanup")
+ procWSAIoctl = modws2_32.NewProc("WSAIoctl")
+ procsocket = modws2_32.NewProc("socket")
+ procsetsockopt = modws2_32.NewProc("setsockopt")
+ procgetsockopt = modws2_32.NewProc("getsockopt")
+ procbind = modws2_32.NewProc("bind")
+ procconnect = modws2_32.NewProc("connect")
+ procgetsockname = modws2_32.NewProc("getsockname")
+ procgetpeername = modws2_32.NewProc("getpeername")
+ proclisten = modws2_32.NewProc("listen")
+ procshutdown = modws2_32.NewProc("shutdown")
+ procclosesocket = modws2_32.NewProc("closesocket")
+ procAcceptEx = modmswsock.NewProc("AcceptEx")
+ procGetAcceptExSockaddrs = modmswsock.NewProc("GetAcceptExSockaddrs")
+ procWSARecv = modws2_32.NewProc("WSARecv")
+ procWSASend = modws2_32.NewProc("WSASend")
+ procWSARecvFrom = modws2_32.NewProc("WSARecvFrom")
+ procWSASendTo = modws2_32.NewProc("WSASendTo")
+ procgethostbyname = modws2_32.NewProc("gethostbyname")
+ procgetservbyname = modws2_32.NewProc("getservbyname")
+ procntohs = modws2_32.NewProc("ntohs")
+ procgetprotobyname = modws2_32.NewProc("getprotobyname")
+ procDnsQuery_W = moddnsapi.NewProc("DnsQuery_W")
+ procDnsRecordListFree = moddnsapi.NewProc("DnsRecordListFree")
+ procGetAddrInfoW = modws2_32.NewProc("GetAddrInfoW")
+ procFreeAddrInfoW = modws2_32.NewProc("FreeAddrInfoW")
+ procGetIfEntry = modiphlpapi.NewProc("GetIfEntry")
+ procGetAdaptersInfo = modiphlpapi.NewProc("GetAdaptersInfo")
+ procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes")
+ procWSAEnumProtocolsW = modws2_32.NewProc("WSAEnumProtocolsW")
+ procTranslateNameW = modsecur32.NewProc("TranslateNameW")
+ procGetUserNameExW = modsecur32.NewProc("GetUserNameExW")
+ procNetUserGetInfo = modnetapi32.NewProc("NetUserGetInfo")
+ procNetGetJoinInformation = modnetapi32.NewProc("NetGetJoinInformation")
+ procNetApiBufferFree = modnetapi32.NewProc("NetApiBufferFree")
+ procLookupAccountSidW = modadvapi32.NewProc("LookupAccountSidW")
+ procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW")
+ procConvertSidToStringSidW = modadvapi32.NewProc("ConvertSidToStringSidW")
+ procConvertStringSidToSidW = modadvapi32.NewProc("ConvertStringSidToSidW")
+ procGetLengthSid = modadvapi32.NewProc("GetLengthSid")
+ procCopySid = modadvapi32.NewProc("CopySid")
+ procOpenProcessToken = modadvapi32.NewProc("OpenProcessToken")
+ procGetTokenInformation = modadvapi32.NewProc("GetTokenInformation")
+ procGetUserProfileDirectoryW = moduserenv.NewProc("GetUserProfileDirectoryW")
)
func GetLastError() (lasterr error) {
@@ -1581,6 +1584,31 @@ func GetAdaptersInfo(ai *IpAdapterInfo, ol *uint32) (errcode error) {
return
}
+func SetFileCompletionNotificationModes(handle Handle, flags uint8) (err error) {
+ r1, _, e1 := Syscall(procSetFileCompletionNotificationModes.Addr(), 2, uintptr(handle), uintptr(flags), 0)
+ if r1 == 0 {
+ if e1 != 0 {
+ err = error(e1)
+ } else {
+ err = EINVAL
+ }
+ }
+ return
+}
+
+func WSAEnumProtocols(protocols *int32, protocolBuffer *WSAProtocolInfo, bufferLength *uint32) (n int32, err error) {
+ r0, _, e1 := Syscall(procWSAEnumProtocolsW.Addr(), 3, uintptr(unsafe.Pointer(protocols)), uintptr(unsafe.Pointer(protocolBuffer)), uintptr(unsafe.Pointer(bufferLength)))
+ n = int32(r0)
+ if n == -1 {
+ if e1 != 0 {
+ err = error(e1)
+ } else {
+ err = EINVAL
+ }
+ }
+ return
+}
+
func TranslateName(accName *uint16, accNameFormat uint32, desiredNameFormat uint32, translatedName *uint16, nSize *uint32) (err error) {
r1, _, e1 := Syscall6(procTranslateNameW.Addr(), 5, uintptr(unsafe.Pointer(accName)), uintptr(accNameFormat), uintptr(desiredNameFormat), uintptr(unsafe.Pointer(translatedName)), uintptr(unsafe.Pointer(nSize)), 0)
if r1&0xff == 0 {
@@ -1613,6 +1641,14 @@ func NetUserGetInfo(serverName *uint16, userName *uint16, level uint32, buf **by
return
}
+func NetGetJoinInformation(server *uint16, name **uint16, bufType *uint32) (neterr error) {
+ r0, _, _ := Syscall(procNetGetJoinInformation.Addr(), 3, uintptr(unsafe.Pointer(server)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(bufType)))
+ if r0 != 0 {
+ neterr = Errno(r0)
+ }
+ return
+}
+
func NetApiBufferFree(buf *byte) (neterr error) {
r0, _, _ := Syscall(procNetApiBufferFree.Addr(), 1, uintptr(unsafe.Pointer(buf)), 0, 0)
if r0 != 0 {
diff --git a/src/pkg/syscall/zsyscall_windows_amd64.go b/src/pkg/syscall/zsyscall_windows_amd64.go
index 465b509ae..d23c2311a 100644
--- a/src/pkg/syscall/zsyscall_windows_amd64.go
+++ b/src/pkg/syscall/zsyscall_windows_amd64.go
@@ -18,138 +18,141 @@ var (
modnetapi32 = NewLazyDLL("netapi32.dll")
moduserenv = NewLazyDLL("userenv.dll")
- procGetLastError = modkernel32.NewProc("GetLastError")
- procLoadLibraryW = modkernel32.NewProc("LoadLibraryW")
- procFreeLibrary = modkernel32.NewProc("FreeLibrary")
- procGetProcAddress = modkernel32.NewProc("GetProcAddress")
- procGetVersion = modkernel32.NewProc("GetVersion")
- procFormatMessageW = modkernel32.NewProc("FormatMessageW")
- procExitProcess = modkernel32.NewProc("ExitProcess")
- procCreateFileW = modkernel32.NewProc("CreateFileW")
- procReadFile = modkernel32.NewProc("ReadFile")
- procWriteFile = modkernel32.NewProc("WriteFile")
- procSetFilePointer = modkernel32.NewProc("SetFilePointer")
- procCloseHandle = modkernel32.NewProc("CloseHandle")
- procGetStdHandle = modkernel32.NewProc("GetStdHandle")
- procFindFirstFileW = modkernel32.NewProc("FindFirstFileW")
- procFindNextFileW = modkernel32.NewProc("FindNextFileW")
- procFindClose = modkernel32.NewProc("FindClose")
- procGetFileInformationByHandle = modkernel32.NewProc("GetFileInformationByHandle")
- procGetCurrentDirectoryW = modkernel32.NewProc("GetCurrentDirectoryW")
- procSetCurrentDirectoryW = modkernel32.NewProc("SetCurrentDirectoryW")
- procCreateDirectoryW = modkernel32.NewProc("CreateDirectoryW")
- procRemoveDirectoryW = modkernel32.NewProc("RemoveDirectoryW")
- procDeleteFileW = modkernel32.NewProc("DeleteFileW")
- procMoveFileW = modkernel32.NewProc("MoveFileW")
- procGetComputerNameW = modkernel32.NewProc("GetComputerNameW")
- procSetEndOfFile = modkernel32.NewProc("SetEndOfFile")
- procGetSystemTimeAsFileTime = modkernel32.NewProc("GetSystemTimeAsFileTime")
- procGetTimeZoneInformation = modkernel32.NewProc("GetTimeZoneInformation")
- procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort")
- procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
- procPostQueuedCompletionStatus = modkernel32.NewProc("PostQueuedCompletionStatus")
- procCancelIo = modkernel32.NewProc("CancelIo")
- procCancelIoEx = modkernel32.NewProc("CancelIoEx")
- procCreateProcessW = modkernel32.NewProc("CreateProcessW")
- procOpenProcess = modkernel32.NewProc("OpenProcess")
- procTerminateProcess = modkernel32.NewProc("TerminateProcess")
- procGetExitCodeProcess = modkernel32.NewProc("GetExitCodeProcess")
- procGetStartupInfoW = modkernel32.NewProc("GetStartupInfoW")
- procGetCurrentProcess = modkernel32.NewProc("GetCurrentProcess")
- procGetProcessTimes = modkernel32.NewProc("GetProcessTimes")
- procDuplicateHandle = modkernel32.NewProc("DuplicateHandle")
- procWaitForSingleObject = modkernel32.NewProc("WaitForSingleObject")
- procGetTempPathW = modkernel32.NewProc("GetTempPathW")
- procCreatePipe = modkernel32.NewProc("CreatePipe")
- procGetFileType = modkernel32.NewProc("GetFileType")
- procCryptAcquireContextW = modadvapi32.NewProc("CryptAcquireContextW")
- procCryptReleaseContext = modadvapi32.NewProc("CryptReleaseContext")
- procCryptGenRandom = modadvapi32.NewProc("CryptGenRandom")
- procGetEnvironmentStringsW = modkernel32.NewProc("GetEnvironmentStringsW")
- procFreeEnvironmentStringsW = modkernel32.NewProc("FreeEnvironmentStringsW")
- procGetEnvironmentVariableW = modkernel32.NewProc("GetEnvironmentVariableW")
- procSetEnvironmentVariableW = modkernel32.NewProc("SetEnvironmentVariableW")
- procSetFileTime = modkernel32.NewProc("SetFileTime")
- procGetFileAttributesW = modkernel32.NewProc("GetFileAttributesW")
- procSetFileAttributesW = modkernel32.NewProc("SetFileAttributesW")
- procGetFileAttributesExW = modkernel32.NewProc("GetFileAttributesExW")
- procGetCommandLineW = modkernel32.NewProc("GetCommandLineW")
- procCommandLineToArgvW = modshell32.NewProc("CommandLineToArgvW")
- procLocalFree = modkernel32.NewProc("LocalFree")
- procSetHandleInformation = modkernel32.NewProc("SetHandleInformation")
- procFlushFileBuffers = modkernel32.NewProc("FlushFileBuffers")
- procGetFullPathNameW = modkernel32.NewProc("GetFullPathNameW")
- procGetLongPathNameW = modkernel32.NewProc("GetLongPathNameW")
- procGetShortPathNameW = modkernel32.NewProc("GetShortPathNameW")
- procCreateFileMappingW = modkernel32.NewProc("CreateFileMappingW")
- procMapViewOfFile = modkernel32.NewProc("MapViewOfFile")
- procUnmapViewOfFile = modkernel32.NewProc("UnmapViewOfFile")
- procFlushViewOfFile = modkernel32.NewProc("FlushViewOfFile")
- procVirtualLock = modkernel32.NewProc("VirtualLock")
- procVirtualUnlock = modkernel32.NewProc("VirtualUnlock")
- procTransmitFile = modmswsock.NewProc("TransmitFile")
- procReadDirectoryChangesW = modkernel32.NewProc("ReadDirectoryChangesW")
- procCertOpenSystemStoreW = modcrypt32.NewProc("CertOpenSystemStoreW")
- procCertOpenStore = modcrypt32.NewProc("CertOpenStore")
- procCertEnumCertificatesInStore = modcrypt32.NewProc("CertEnumCertificatesInStore")
- procCertAddCertificateContextToStore = modcrypt32.NewProc("CertAddCertificateContextToStore")
- procCertCloseStore = modcrypt32.NewProc("CertCloseStore")
- procCertGetCertificateChain = modcrypt32.NewProc("CertGetCertificateChain")
- procCertFreeCertificateChain = modcrypt32.NewProc("CertFreeCertificateChain")
- procCertCreateCertificateContext = modcrypt32.NewProc("CertCreateCertificateContext")
- procCertFreeCertificateContext = modcrypt32.NewProc("CertFreeCertificateContext")
- procCertVerifyCertificateChainPolicy = modcrypt32.NewProc("CertVerifyCertificateChainPolicy")
- procRegOpenKeyExW = modadvapi32.NewProc("RegOpenKeyExW")
- procRegCloseKey = modadvapi32.NewProc("RegCloseKey")
- procRegQueryInfoKeyW = modadvapi32.NewProc("RegQueryInfoKeyW")
- procRegEnumKeyExW = modadvapi32.NewProc("RegEnumKeyExW")
- procRegQueryValueExW = modadvapi32.NewProc("RegQueryValueExW")
- procGetCurrentProcessId = modkernel32.NewProc("GetCurrentProcessId")
- procGetConsoleMode = modkernel32.NewProc("GetConsoleMode")
- procWriteConsoleW = modkernel32.NewProc("WriteConsoleW")
- procReadConsoleW = modkernel32.NewProc("ReadConsoleW")
- procWSAStartup = modws2_32.NewProc("WSAStartup")
- procWSACleanup = modws2_32.NewProc("WSACleanup")
- procWSAIoctl = modws2_32.NewProc("WSAIoctl")
- procsocket = modws2_32.NewProc("socket")
- procsetsockopt = modws2_32.NewProc("setsockopt")
- procgetsockopt = modws2_32.NewProc("getsockopt")
- procbind = modws2_32.NewProc("bind")
- procconnect = modws2_32.NewProc("connect")
- procgetsockname = modws2_32.NewProc("getsockname")
- procgetpeername = modws2_32.NewProc("getpeername")
- proclisten = modws2_32.NewProc("listen")
- procshutdown = modws2_32.NewProc("shutdown")
- procclosesocket = modws2_32.NewProc("closesocket")
- procAcceptEx = modmswsock.NewProc("AcceptEx")
- procGetAcceptExSockaddrs = modmswsock.NewProc("GetAcceptExSockaddrs")
- procWSARecv = modws2_32.NewProc("WSARecv")
- procWSASend = modws2_32.NewProc("WSASend")
- procWSARecvFrom = modws2_32.NewProc("WSARecvFrom")
- procWSASendTo = modws2_32.NewProc("WSASendTo")
- procgethostbyname = modws2_32.NewProc("gethostbyname")
- procgetservbyname = modws2_32.NewProc("getservbyname")
- procntohs = modws2_32.NewProc("ntohs")
- procgetprotobyname = modws2_32.NewProc("getprotobyname")
- procDnsQuery_W = moddnsapi.NewProc("DnsQuery_W")
- procDnsRecordListFree = moddnsapi.NewProc("DnsRecordListFree")
- procGetAddrInfoW = modws2_32.NewProc("GetAddrInfoW")
- procFreeAddrInfoW = modws2_32.NewProc("FreeAddrInfoW")
- procGetIfEntry = modiphlpapi.NewProc("GetIfEntry")
- procGetAdaptersInfo = modiphlpapi.NewProc("GetAdaptersInfo")
- procTranslateNameW = modsecur32.NewProc("TranslateNameW")
- procGetUserNameExW = modsecur32.NewProc("GetUserNameExW")
- procNetUserGetInfo = modnetapi32.NewProc("NetUserGetInfo")
- procNetApiBufferFree = modnetapi32.NewProc("NetApiBufferFree")
- procLookupAccountSidW = modadvapi32.NewProc("LookupAccountSidW")
- procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW")
- procConvertSidToStringSidW = modadvapi32.NewProc("ConvertSidToStringSidW")
- procConvertStringSidToSidW = modadvapi32.NewProc("ConvertStringSidToSidW")
- procGetLengthSid = modadvapi32.NewProc("GetLengthSid")
- procCopySid = modadvapi32.NewProc("CopySid")
- procOpenProcessToken = modadvapi32.NewProc("OpenProcessToken")
- procGetTokenInformation = modadvapi32.NewProc("GetTokenInformation")
- procGetUserProfileDirectoryW = moduserenv.NewProc("GetUserProfileDirectoryW")
+ procGetLastError = modkernel32.NewProc("GetLastError")
+ procLoadLibraryW = modkernel32.NewProc("LoadLibraryW")
+ procFreeLibrary = modkernel32.NewProc("FreeLibrary")
+ procGetProcAddress = modkernel32.NewProc("GetProcAddress")
+ procGetVersion = modkernel32.NewProc("GetVersion")
+ procFormatMessageW = modkernel32.NewProc("FormatMessageW")
+ procExitProcess = modkernel32.NewProc("ExitProcess")
+ procCreateFileW = modkernel32.NewProc("CreateFileW")
+ procReadFile = modkernel32.NewProc("ReadFile")
+ procWriteFile = modkernel32.NewProc("WriteFile")
+ procSetFilePointer = modkernel32.NewProc("SetFilePointer")
+ procCloseHandle = modkernel32.NewProc("CloseHandle")
+ procGetStdHandle = modkernel32.NewProc("GetStdHandle")
+ procFindFirstFileW = modkernel32.NewProc("FindFirstFileW")
+ procFindNextFileW = modkernel32.NewProc("FindNextFileW")
+ procFindClose = modkernel32.NewProc("FindClose")
+ procGetFileInformationByHandle = modkernel32.NewProc("GetFileInformationByHandle")
+ procGetCurrentDirectoryW = modkernel32.NewProc("GetCurrentDirectoryW")
+ procSetCurrentDirectoryW = modkernel32.NewProc("SetCurrentDirectoryW")
+ procCreateDirectoryW = modkernel32.NewProc("CreateDirectoryW")
+ procRemoveDirectoryW = modkernel32.NewProc("RemoveDirectoryW")
+ procDeleteFileW = modkernel32.NewProc("DeleteFileW")
+ procMoveFileW = modkernel32.NewProc("MoveFileW")
+ procGetComputerNameW = modkernel32.NewProc("GetComputerNameW")
+ procSetEndOfFile = modkernel32.NewProc("SetEndOfFile")
+ procGetSystemTimeAsFileTime = modkernel32.NewProc("GetSystemTimeAsFileTime")
+ procGetTimeZoneInformation = modkernel32.NewProc("GetTimeZoneInformation")
+ procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort")
+ procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
+ procPostQueuedCompletionStatus = modkernel32.NewProc("PostQueuedCompletionStatus")
+ procCancelIo = modkernel32.NewProc("CancelIo")
+ procCancelIoEx = modkernel32.NewProc("CancelIoEx")
+ procCreateProcessW = modkernel32.NewProc("CreateProcessW")
+ procOpenProcess = modkernel32.NewProc("OpenProcess")
+ procTerminateProcess = modkernel32.NewProc("TerminateProcess")
+ procGetExitCodeProcess = modkernel32.NewProc("GetExitCodeProcess")
+ procGetStartupInfoW = modkernel32.NewProc("GetStartupInfoW")
+ procGetCurrentProcess = modkernel32.NewProc("GetCurrentProcess")
+ procGetProcessTimes = modkernel32.NewProc("GetProcessTimes")
+ procDuplicateHandle = modkernel32.NewProc("DuplicateHandle")
+ procWaitForSingleObject = modkernel32.NewProc("WaitForSingleObject")
+ procGetTempPathW = modkernel32.NewProc("GetTempPathW")
+ procCreatePipe = modkernel32.NewProc("CreatePipe")
+ procGetFileType = modkernel32.NewProc("GetFileType")
+ procCryptAcquireContextW = modadvapi32.NewProc("CryptAcquireContextW")
+ procCryptReleaseContext = modadvapi32.NewProc("CryptReleaseContext")
+ procCryptGenRandom = modadvapi32.NewProc("CryptGenRandom")
+ procGetEnvironmentStringsW = modkernel32.NewProc("GetEnvironmentStringsW")
+ procFreeEnvironmentStringsW = modkernel32.NewProc("FreeEnvironmentStringsW")
+ procGetEnvironmentVariableW = modkernel32.NewProc("GetEnvironmentVariableW")
+ procSetEnvironmentVariableW = modkernel32.NewProc("SetEnvironmentVariableW")
+ procSetFileTime = modkernel32.NewProc("SetFileTime")
+ procGetFileAttributesW = modkernel32.NewProc("GetFileAttributesW")
+ procSetFileAttributesW = modkernel32.NewProc("SetFileAttributesW")
+ procGetFileAttributesExW = modkernel32.NewProc("GetFileAttributesExW")
+ procGetCommandLineW = modkernel32.NewProc("GetCommandLineW")
+ procCommandLineToArgvW = modshell32.NewProc("CommandLineToArgvW")
+ procLocalFree = modkernel32.NewProc("LocalFree")
+ procSetHandleInformation = modkernel32.NewProc("SetHandleInformation")
+ procFlushFileBuffers = modkernel32.NewProc("FlushFileBuffers")
+ procGetFullPathNameW = modkernel32.NewProc("GetFullPathNameW")
+ procGetLongPathNameW = modkernel32.NewProc("GetLongPathNameW")
+ procGetShortPathNameW = modkernel32.NewProc("GetShortPathNameW")
+ procCreateFileMappingW = modkernel32.NewProc("CreateFileMappingW")
+ procMapViewOfFile = modkernel32.NewProc("MapViewOfFile")
+ procUnmapViewOfFile = modkernel32.NewProc("UnmapViewOfFile")
+ procFlushViewOfFile = modkernel32.NewProc("FlushViewOfFile")
+ procVirtualLock = modkernel32.NewProc("VirtualLock")
+ procVirtualUnlock = modkernel32.NewProc("VirtualUnlock")
+ procTransmitFile = modmswsock.NewProc("TransmitFile")
+ procReadDirectoryChangesW = modkernel32.NewProc("ReadDirectoryChangesW")
+ procCertOpenSystemStoreW = modcrypt32.NewProc("CertOpenSystemStoreW")
+ procCertOpenStore = modcrypt32.NewProc("CertOpenStore")
+ procCertEnumCertificatesInStore = modcrypt32.NewProc("CertEnumCertificatesInStore")
+ procCertAddCertificateContextToStore = modcrypt32.NewProc("CertAddCertificateContextToStore")
+ procCertCloseStore = modcrypt32.NewProc("CertCloseStore")
+ procCertGetCertificateChain = modcrypt32.NewProc("CertGetCertificateChain")
+ procCertFreeCertificateChain = modcrypt32.NewProc("CertFreeCertificateChain")
+ procCertCreateCertificateContext = modcrypt32.NewProc("CertCreateCertificateContext")
+ procCertFreeCertificateContext = modcrypt32.NewProc("CertFreeCertificateContext")
+ procCertVerifyCertificateChainPolicy = modcrypt32.NewProc("CertVerifyCertificateChainPolicy")
+ procRegOpenKeyExW = modadvapi32.NewProc("RegOpenKeyExW")
+ procRegCloseKey = modadvapi32.NewProc("RegCloseKey")
+ procRegQueryInfoKeyW = modadvapi32.NewProc("RegQueryInfoKeyW")
+ procRegEnumKeyExW = modadvapi32.NewProc("RegEnumKeyExW")
+ procRegQueryValueExW = modadvapi32.NewProc("RegQueryValueExW")
+ procGetCurrentProcessId = modkernel32.NewProc("GetCurrentProcessId")
+ procGetConsoleMode = modkernel32.NewProc("GetConsoleMode")
+ procWriteConsoleW = modkernel32.NewProc("WriteConsoleW")
+ procReadConsoleW = modkernel32.NewProc("ReadConsoleW")
+ procWSAStartup = modws2_32.NewProc("WSAStartup")
+ procWSACleanup = modws2_32.NewProc("WSACleanup")
+ procWSAIoctl = modws2_32.NewProc("WSAIoctl")
+ procsocket = modws2_32.NewProc("socket")
+ procsetsockopt = modws2_32.NewProc("setsockopt")
+ procgetsockopt = modws2_32.NewProc("getsockopt")
+ procbind = modws2_32.NewProc("bind")
+ procconnect = modws2_32.NewProc("connect")
+ procgetsockname = modws2_32.NewProc("getsockname")
+ procgetpeername = modws2_32.NewProc("getpeername")
+ proclisten = modws2_32.NewProc("listen")
+ procshutdown = modws2_32.NewProc("shutdown")
+ procclosesocket = modws2_32.NewProc("closesocket")
+ procAcceptEx = modmswsock.NewProc("AcceptEx")
+ procGetAcceptExSockaddrs = modmswsock.NewProc("GetAcceptExSockaddrs")
+ procWSARecv = modws2_32.NewProc("WSARecv")
+ procWSASend = modws2_32.NewProc("WSASend")
+ procWSARecvFrom = modws2_32.NewProc("WSARecvFrom")
+ procWSASendTo = modws2_32.NewProc("WSASendTo")
+ procgethostbyname = modws2_32.NewProc("gethostbyname")
+ procgetservbyname = modws2_32.NewProc("getservbyname")
+ procntohs = modws2_32.NewProc("ntohs")
+ procgetprotobyname = modws2_32.NewProc("getprotobyname")
+ procDnsQuery_W = moddnsapi.NewProc("DnsQuery_W")
+ procDnsRecordListFree = moddnsapi.NewProc("DnsRecordListFree")
+ procGetAddrInfoW = modws2_32.NewProc("GetAddrInfoW")
+ procFreeAddrInfoW = modws2_32.NewProc("FreeAddrInfoW")
+ procGetIfEntry = modiphlpapi.NewProc("GetIfEntry")
+ procGetAdaptersInfo = modiphlpapi.NewProc("GetAdaptersInfo")
+ procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes")
+ procWSAEnumProtocolsW = modws2_32.NewProc("WSAEnumProtocolsW")
+ procTranslateNameW = modsecur32.NewProc("TranslateNameW")
+ procGetUserNameExW = modsecur32.NewProc("GetUserNameExW")
+ procNetUserGetInfo = modnetapi32.NewProc("NetUserGetInfo")
+ procNetGetJoinInformation = modnetapi32.NewProc("NetGetJoinInformation")
+ procNetApiBufferFree = modnetapi32.NewProc("NetApiBufferFree")
+ procLookupAccountSidW = modadvapi32.NewProc("LookupAccountSidW")
+ procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW")
+ procConvertSidToStringSidW = modadvapi32.NewProc("ConvertSidToStringSidW")
+ procConvertStringSidToSidW = modadvapi32.NewProc("ConvertStringSidToSidW")
+ procGetLengthSid = modadvapi32.NewProc("GetLengthSid")
+ procCopySid = modadvapi32.NewProc("CopySid")
+ procOpenProcessToken = modadvapi32.NewProc("OpenProcessToken")
+ procGetTokenInformation = modadvapi32.NewProc("GetTokenInformation")
+ procGetUserProfileDirectoryW = moduserenv.NewProc("GetUserProfileDirectoryW")
)
func GetLastError() (lasterr error) {
@@ -1581,6 +1584,31 @@ func GetAdaptersInfo(ai *IpAdapterInfo, ol *uint32) (errcode error) {
return
}
+func SetFileCompletionNotificationModes(handle Handle, flags uint8) (err error) {
+ r1, _, e1 := Syscall(procSetFileCompletionNotificationModes.Addr(), 2, uintptr(handle), uintptr(flags), 0)
+ if r1 == 0 {
+ if e1 != 0 {
+ err = error(e1)
+ } else {
+ err = EINVAL
+ }
+ }
+ return
+}
+
+func WSAEnumProtocols(protocols *int32, protocolBuffer *WSAProtocolInfo, bufferLength *uint32) (n int32, err error) {
+ r0, _, e1 := Syscall(procWSAEnumProtocolsW.Addr(), 3, uintptr(unsafe.Pointer(protocols)), uintptr(unsafe.Pointer(protocolBuffer)), uintptr(unsafe.Pointer(bufferLength)))
+ n = int32(r0)
+ if n == -1 {
+ if e1 != 0 {
+ err = error(e1)
+ } else {
+ err = EINVAL
+ }
+ }
+ return
+}
+
func TranslateName(accName *uint16, accNameFormat uint32, desiredNameFormat uint32, translatedName *uint16, nSize *uint32) (err error) {
r1, _, e1 := Syscall6(procTranslateNameW.Addr(), 5, uintptr(unsafe.Pointer(accName)), uintptr(accNameFormat), uintptr(desiredNameFormat), uintptr(unsafe.Pointer(translatedName)), uintptr(unsafe.Pointer(nSize)), 0)
if r1&0xff == 0 {
@@ -1613,6 +1641,14 @@ func NetUserGetInfo(serverName *uint16, userName *uint16, level uint32, buf **by
return
}
+func NetGetJoinInformation(server *uint16, name **uint16, bufType *uint32) (neterr error) {
+ r0, _, _ := Syscall(procNetGetJoinInformation.Addr(), 3, uintptr(unsafe.Pointer(server)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(bufType)))
+ if r0 != 0 {
+ neterr = Errno(r0)
+ }
+ return
+}
+
func NetApiBufferFree(buf *byte) (neterr error) {
r0, _, _ := Syscall(procNetApiBufferFree.Addr(), 1, uintptr(unsafe.Pointer(buf)), 0, 0)
if r0 != 0 {
diff --git a/src/pkg/syscall/zsysnum_dragonfly_386.go b/src/pkg/syscall/zsysnum_dragonfly_386.go
new file mode 100644
index 000000000..68eeb32ac
--- /dev/null
+++ b/src/pkg/syscall/zsysnum_dragonfly_386.go
@@ -0,0 +1,303 @@
+// mksysnum_dragonfly.pl
+// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
+
+package syscall
+
+const (
+ // SYS_NOSYS = 0; // { int nosys(void); } syscall nosys_args int
+ SYS_EXIT = 1 // { void exit(int rval); }
+ SYS_FORK = 2 // { int fork(void); }
+ SYS_READ = 3 // { ssize_t read(int fd, void *buf, size_t nbyte); }
+ SYS_WRITE = 4 // { ssize_t write(int fd, const void *buf, size_t nbyte); }
+ SYS_OPEN = 5 // { int open(char *path, int flags, int mode); }
+ SYS_CLOSE = 6 // { int close(int fd); }
+ SYS_WAIT4 = 7 // { int wait4(int pid, int *status, int options, \
+ SYS_LINK = 9 // { int link(char *path, char *link); }
+ SYS_UNLINK = 10 // { int unlink(char *path); }
+ SYS_CHDIR = 12 // { int chdir(char *path); }
+ SYS_FCHDIR = 13 // { int fchdir(int fd); }
+ SYS_MKNOD = 14 // { int mknod(char *path, int mode, int dev); }
+ SYS_CHMOD = 15 // { int chmod(char *path, int mode); }
+ SYS_CHOWN = 16 // { int chown(char *path, int uid, int gid); }
+ SYS_OBREAK = 17 // { int obreak(char *nsize); } break obreak_args int
+ SYS_GETFSSTAT = 18 // { int getfsstat(struct statfs *buf, long bufsize, \
+ SYS_GETPID = 20 // { pid_t getpid(void); }
+ SYS_MOUNT = 21 // { int mount(char *type, char *path, int flags, \
+ SYS_UNMOUNT = 22 // { int unmount(char *path, int flags); }
+ SYS_SETUID = 23 // { int setuid(uid_t uid); }
+ SYS_GETUID = 24 // { uid_t getuid(void); }
+ SYS_GETEUID = 25 // { uid_t geteuid(void); }
+ SYS_PTRACE = 26 // { int ptrace(int req, pid_t pid, caddr_t addr, \
+ SYS_RECVMSG = 27 // { int recvmsg(int s, struct msghdr *msg, int flags); }
+ SYS_SENDMSG = 28 // { int sendmsg(int s, caddr_t msg, int flags); }
+ SYS_RECVFROM = 29 // { int recvfrom(int s, caddr_t buf, size_t len, \
+ SYS_ACCEPT = 30 // { int accept(int s, caddr_t name, int *anamelen); }
+ SYS_GETPEERNAME = 31 // { int getpeername(int fdes, caddr_t asa, int *alen); }
+ SYS_GETSOCKNAME = 32 // { int getsockname(int fdes, caddr_t asa, int *alen); }
+ SYS_ACCESS = 33 // { int access(char *path, int flags); }
+ SYS_CHFLAGS = 34 // { int chflags(char *path, int flags); }
+ SYS_FCHFLAGS = 35 // { int fchflags(int fd, int flags); }
+ SYS_SYNC = 36 // { int sync(void); }
+ SYS_KILL = 37 // { int kill(int pid, int signum); }
+ SYS_GETPPID = 39 // { pid_t getppid(void); }
+ SYS_DUP = 41 // { int dup(u_int fd); }
+ SYS_PIPE = 42 // { int pipe(void); }
+ SYS_GETEGID = 43 // { gid_t getegid(void); }
+ SYS_PROFIL = 44 // { int profil(caddr_t samples, size_t size, \
+ SYS_KTRACE = 45 // { int ktrace(const char *fname, int ops, int facs, \
+ SYS_GETGID = 47 // { gid_t getgid(void); }
+ SYS_GETLOGIN = 49 // { int getlogin(char *namebuf, u_int namelen); }
+ SYS_SETLOGIN = 50 // { int setlogin(char *namebuf); }
+ SYS_ACCT = 51 // { int acct(char *path); }
+ SYS_SIGALTSTACK = 53 // { int sigaltstack(stack_t *ss, stack_t *oss); }
+ SYS_IOCTL = 54 // { int ioctl(int fd, u_long com, caddr_t data); }
+ SYS_REBOOT = 55 // { int reboot(int opt); }
+ SYS_REVOKE = 56 // { int revoke(char *path); }
+ SYS_SYMLINK = 57 // { int symlink(char *path, char *link); }
+ SYS_READLINK = 58 // { int readlink(char *path, char *buf, int count); }
+ SYS_EXECVE = 59 // { int execve(char *fname, char **argv, char **envv); }
+ SYS_UMASK = 60 // { int umask(int newmask); } umask umask_args int
+ SYS_CHROOT = 61 // { int chroot(char *path); }
+ SYS_MSYNC = 65 // { int msync(void *addr, size_t len, int flags); }
+ SYS_VFORK = 66 // { pid_t vfork(void); }
+ SYS_SBRK = 69 // { int sbrk(int incr); }
+ SYS_SSTK = 70 // { int sstk(int incr); }
+ SYS_MUNMAP = 73 // { int munmap(void *addr, size_t len); }
+ SYS_MPROTECT = 74 // { int mprotect(void *addr, size_t len, int prot); }
+ SYS_MADVISE = 75 // { int madvise(void *addr, size_t len, int behav); }
+ SYS_MINCORE = 78 // { int mincore(const void *addr, size_t len, \
+ SYS_GETGROUPS = 79 // { int getgroups(u_int gidsetsize, gid_t *gidset); }
+ SYS_SETGROUPS = 80 // { int setgroups(u_int gidsetsize, gid_t *gidset); }
+ SYS_GETPGRP = 81 // { int getpgrp(void); }
+ SYS_SETPGID = 82 // { int setpgid(int pid, int pgid); }
+ SYS_SETITIMER = 83 // { int setitimer(u_int which, struct itimerval *itv, \
+ SYS_SWAPON = 85 // { int swapon(char *name); }
+ SYS_GETITIMER = 86 // { int getitimer(u_int which, struct itimerval *itv); }
+ SYS_GETDTABLESIZE = 89 // { int getdtablesize(void); }
+ SYS_DUP2 = 90 // { int dup2(u_int from, u_int to); }
+ SYS_FCNTL = 92 // { int fcntl(int fd, int cmd, long arg); }
+ SYS_SELECT = 93 // { int select(int nd, fd_set *in, fd_set *ou, \
+ SYS_FSYNC = 95 // { int fsync(int fd); }
+ SYS_SETPRIORITY = 96 // { int setpriority(int which, int who, int prio); }
+ SYS_SOCKET = 97 // { int socket(int domain, int type, int protocol); }
+ SYS_CONNECT = 98 // { int connect(int s, caddr_t name, int namelen); }
+ SYS_GETPRIORITY = 100 // { int getpriority(int which, int who); }
+ SYS_BIND = 104 // { int bind(int s, caddr_t name, int namelen); }
+ SYS_SETSOCKOPT = 105 // { int setsockopt(int s, int level, int name, \
+ SYS_LISTEN = 106 // { int listen(int s, int backlog); }
+ SYS_GETTIMEOFDAY = 116 // { int gettimeofday(struct timeval *tp, \
+ SYS_GETRUSAGE = 117 // { int getrusage(int who, struct rusage *rusage); }
+ SYS_GETSOCKOPT = 118 // { int getsockopt(int s, int level, int name, \
+ SYS_READV = 120 // { int readv(int fd, struct iovec *iovp, u_int iovcnt); }
+ SYS_WRITEV = 121 // { int writev(int fd, struct iovec *iovp, \
+ SYS_SETTIMEOFDAY = 122 // { int settimeofday(struct timeval *tv, \
+ SYS_FCHOWN = 123 // { int fchown(int fd, int uid, int gid); }
+ SYS_FCHMOD = 124 // { int fchmod(int fd, int mode); }
+ SYS_SETREUID = 126 // { int setreuid(int ruid, int euid); }
+ SYS_SETREGID = 127 // { int setregid(int rgid, int egid); }
+ SYS_RENAME = 128 // { int rename(char *from, char *to); }
+ SYS_FLOCK = 131 // { int flock(int fd, int how); }
+ SYS_MKFIFO = 132 // { int mkfifo(char *path, int mode); }
+ SYS_SENDTO = 133 // { int sendto(int s, caddr_t buf, size_t len, \
+ SYS_SHUTDOWN = 134 // { int shutdown(int s, int how); }
+ SYS_SOCKETPAIR = 135 // { int socketpair(int domain, int type, int protocol, \
+ SYS_MKDIR = 136 // { int mkdir(char *path, int mode); }
+ SYS_RMDIR = 137 // { int rmdir(char *path); }
+ SYS_UTIMES = 138 // { int utimes(char *path, struct timeval *tptr); }
+ SYS_ADJTIME = 140 // { int adjtime(struct timeval *delta, \
+ SYS_SETSID = 147 // { int setsid(void); }
+ SYS_QUOTACTL = 148 // { int quotactl(char *path, int cmd, int uid, \
+ SYS_STATFS = 157 // { int statfs(char *path, struct statfs *buf); }
+ SYS_FSTATFS = 158 // { int fstatfs(int fd, struct statfs *buf); }
+ SYS_GETFH = 161 // { int getfh(char *fname, struct fhandle *fhp); }
+ SYS_GETDOMAINNAME = 162 // { int getdomainname(char *domainname, int len); }
+ SYS_SETDOMAINNAME = 163 // { int setdomainname(char *domainname, int len); }
+ SYS_UNAME = 164 // { int uname(struct utsname *name); }
+ SYS_SYSARCH = 165 // { int sysarch(int op, char *parms); }
+ SYS_RTPRIO = 166 // { int rtprio(int function, pid_t pid, \
+ SYS_SEMSYS = 169 // { int semsys(int which, int a2, int a3, int a4, \
+ SYS_MSGSYS = 170 // { int msgsys(int which, int a2, int a3, int a4, \
+ SYS_SHMSYS = 171 // { int shmsys(int which, int a2, int a3, int a4); }
+ SYS_EXTPREAD = 173 // { ssize_t extpread(int fd, void *buf, \
+ SYS_EXTPWRITE = 174 // { ssize_t extpwrite(int fd, const void *buf, \
+ SYS_NTP_ADJTIME = 176 // { int ntp_adjtime(struct timex *tp); }
+ SYS_SETGID = 181 // { int setgid(gid_t gid); }
+ SYS_SETEGID = 182 // { int setegid(gid_t egid); }
+ SYS_SETEUID = 183 // { int seteuid(uid_t euid); }
+ SYS_PATHCONF = 191 // { int pathconf(char *path, int name); }
+ SYS_FPATHCONF = 192 // { int fpathconf(int fd, int name); }
+ SYS_GETRLIMIT = 194 // { int getrlimit(u_int which, \
+ SYS_SETRLIMIT = 195 // { int setrlimit(u_int which, \
+ SYS_MMAP = 197 // { caddr_t mmap(caddr_t addr, size_t len, int prot, \
+ // SYS_NOSYS = 198; // { int nosys(void); } __syscall __syscall_args int
+ SYS_LSEEK = 199 // { off_t lseek(int fd, int pad, off_t offset, \
+ SYS_TRUNCATE = 200 // { int truncate(char *path, int pad, off_t length); }
+ SYS_FTRUNCATE = 201 // { int ftruncate(int fd, int pad, off_t length); }
+ SYS___SYSCTL = 202 // { int __sysctl(int *name, u_int namelen, void *old, \
+ SYS_MLOCK = 203 // { int mlock(const void *addr, size_t len); }
+ SYS_MUNLOCK = 204 // { int munlock(const void *addr, size_t len); }
+ SYS_UNDELETE = 205 // { int undelete(char *path); }
+ SYS_FUTIMES = 206 // { int futimes(int fd, struct timeval *tptr); }
+ SYS_GETPGID = 207 // { int getpgid(pid_t pid); }
+ SYS_POLL = 209 // { int poll(struct pollfd *fds, u_int nfds, \
+ SYS___SEMCTL = 220 // { int __semctl(int semid, int semnum, int cmd, \
+ SYS_SEMGET = 221 // { int semget(key_t key, int nsems, int semflg); }
+ SYS_SEMOP = 222 // { int semop(int semid, struct sembuf *sops, \
+ SYS_MSGCTL = 224 // { int msgctl(int msqid, int cmd, \
+ SYS_MSGGET = 225 // { int msgget(key_t key, int msgflg); }
+ SYS_MSGSND = 226 // { int msgsnd(int msqid, void *msgp, size_t msgsz, \
+ SYS_MSGRCV = 227 // { int msgrcv(int msqid, void *msgp, size_t msgsz, \
+ SYS_SHMAT = 228 // { caddr_t shmat(int shmid, const void *shmaddr, \
+ SYS_SHMCTL = 229 // { int shmctl(int shmid, int cmd, \
+ SYS_SHMDT = 230 // { int shmdt(const void *shmaddr); }
+ SYS_SHMGET = 231 // { int shmget(key_t key, size_t size, int shmflg); }
+ SYS_CLOCK_GETTIME = 232 // { int clock_gettime(clockid_t clock_id, \
+ SYS_CLOCK_SETTIME = 233 // { int clock_settime(clockid_t clock_id, \
+ SYS_CLOCK_GETRES = 234 // { int clock_getres(clockid_t clock_id, \
+ SYS_NANOSLEEP = 240 // { int nanosleep(const struct timespec *rqtp, \
+ SYS_MINHERIT = 250 // { int minherit(void *addr, size_t len, int inherit); }
+ SYS_RFORK = 251 // { int rfork(int flags); }
+ SYS_OPENBSD_POLL = 252 // { int openbsd_poll(struct pollfd *fds, u_int nfds, \
+ SYS_ISSETUGID = 253 // { int issetugid(void); }
+ SYS_LCHOWN = 254 // { int lchown(char *path, int uid, int gid); }
+ SYS_LCHMOD = 274 // { int lchmod(char *path, mode_t mode); }
+ SYS_LUTIMES = 276 // { int lutimes(char *path, struct timeval *tptr); }
+ SYS_EXTPREADV = 289 // { ssize_t extpreadv(int fd, struct iovec *iovp, \
+ SYS_EXTPWRITEV = 290 // { ssize_t extpwritev(int fd, struct iovec *iovp,\
+ SYS_FHSTATFS = 297 // { int fhstatfs(const struct fhandle *u_fhp, struct statfs *buf); }
+ SYS_FHOPEN = 298 // { int fhopen(const struct fhandle *u_fhp, int flags); }
+ SYS_MODNEXT = 300 // { int modnext(int modid); }
+ SYS_MODSTAT = 301 // { int modstat(int modid, struct module_stat* stat); }
+ SYS_MODFNEXT = 302 // { int modfnext(int modid); }
+ SYS_MODFIND = 303 // { int modfind(const char *name); }
+ SYS_KLDLOAD = 304 // { int kldload(const char *file); }
+ SYS_KLDUNLOAD = 305 // { int kldunload(int fileid); }
+ SYS_KLDFIND = 306 // { int kldfind(const char *file); }
+ SYS_KLDNEXT = 307 // { int kldnext(int fileid); }
+ SYS_KLDSTAT = 308 // { int kldstat(int fileid, struct kld_file_stat* stat); }
+ SYS_KLDFIRSTMOD = 309 // { int kldfirstmod(int fileid); }
+ SYS_GETSID = 310 // { int getsid(pid_t pid); }
+ SYS_SETRESUID = 311 // { int setresuid(uid_t ruid, uid_t euid, uid_t suid); }
+ SYS_SETRESGID = 312 // { int setresgid(gid_t rgid, gid_t egid, gid_t sgid); }
+ SYS_AIO_RETURN = 314 // { int aio_return(struct aiocb *aiocbp); }
+ SYS_AIO_SUSPEND = 315 // { int aio_suspend(struct aiocb * const * aiocbp, int nent, const struct timespec *timeout); }
+ SYS_AIO_CANCEL = 316 // { int aio_cancel(int fd, struct aiocb *aiocbp); }
+ SYS_AIO_ERROR = 317 // { int aio_error(struct aiocb *aiocbp); }
+ SYS_AIO_READ = 318 // { int aio_read(struct aiocb *aiocbp); }
+ SYS_AIO_WRITE = 319 // { int aio_write(struct aiocb *aiocbp); }
+ SYS_LIO_LISTIO = 320 // { int lio_listio(int mode, struct aiocb * const *acb_list, int nent, struct sigevent *sig); }
+ SYS_YIELD = 321 // { int yield(void); }
+ SYS_MLOCKALL = 324 // { int mlockall(int how); }
+ SYS_MUNLOCKALL = 325 // { int munlockall(void); }
+ SYS___GETCWD = 326 // { int __getcwd(u_char *buf, u_int buflen); }
+ SYS_SCHED_SETPARAM = 327 // { int sched_setparam (pid_t pid, const struct sched_param *param); }
+ SYS_SCHED_GETPARAM = 328 // { int sched_getparam (pid_t pid, struct sched_param *param); }
+ SYS_SCHED_SETSCHEDULER = 329 // { int sched_setscheduler (pid_t pid, int policy, const struct sched_param *param); }
+ SYS_SCHED_GETSCHEDULER = 330 // { int sched_getscheduler (pid_t pid); }
+ SYS_SCHED_YIELD = 331 // { int sched_yield (void); }
+ SYS_SCHED_GET_PRIORITY_MAX = 332 // { int sched_get_priority_max (int policy); }
+ SYS_SCHED_GET_PRIORITY_MIN = 333 // { int sched_get_priority_min (int policy); }
+ SYS_SCHED_RR_GET_INTERVAL = 334 // { int sched_rr_get_interval (pid_t pid, struct timespec *interval); }
+ SYS_UTRACE = 335 // { int utrace(const void *addr, size_t len); }
+ SYS_KLDSYM = 337 // { int kldsym(int fileid, int cmd, void *data); }
+ SYS_JAIL = 338 // { int jail(struct jail *jail); }
+ SYS_SIGPROCMASK = 340 // { int sigprocmask(int how, const sigset_t *set, \
+ SYS_SIGSUSPEND = 341 // { int sigsuspend(const sigset_t *sigmask); }
+ SYS_SIGACTION = 342 // { int sigaction(int sig, const struct sigaction *act, \
+ SYS_SIGPENDING = 343 // { int sigpending(sigset_t *set); }
+ SYS_SIGRETURN = 344 // { int sigreturn(ucontext_t *sigcntxp); }
+ SYS_SIGTIMEDWAIT = 345 // { int sigtimedwait(const sigset_t *set,\
+ SYS_SIGWAITINFO = 346 // { int sigwaitinfo(const sigset_t *set,\
+ SYS___ACL_GET_FILE = 347 // { int __acl_get_file(const char *path, \
+ SYS___ACL_SET_FILE = 348 // { int __acl_set_file(const char *path, \
+ SYS___ACL_GET_FD = 349 // { int __acl_get_fd(int filedes, acl_type_t type, \
+ SYS___ACL_SET_FD = 350 // { int __acl_set_fd(int filedes, acl_type_t type, \
+ SYS___ACL_DELETE_FILE = 351 // { int __acl_delete_file(const char *path, \
+ SYS___ACL_DELETE_FD = 352 // { int __acl_delete_fd(int filedes, acl_type_t type); }
+ SYS___ACL_ACLCHECK_FILE = 353 // { int __acl_aclcheck_file(const char *path, \
+ SYS___ACL_ACLCHECK_FD = 354 // { int __acl_aclcheck_fd(int filedes, acl_type_t type, \
+ SYS_EXTATTRCTL = 355 // { int extattrctl(const char *path, int cmd, \
+ SYS_EXTATTR_SET_FILE = 356 // { int extattr_set_file(const char *path, \
+ SYS_EXTATTR_GET_FILE = 357 // { int extattr_get_file(const char *path, \
+ SYS_EXTATTR_DELETE_FILE = 358 // { int extattr_delete_file(const char *path, \
+ SYS_AIO_WAITCOMPLETE = 359 // { int aio_waitcomplete(struct aiocb **aiocbp, struct timespec *timeout); }
+ SYS_GETRESUID = 360 // { int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid); }
+ SYS_GETRESGID = 361 // { int getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid); }
+ SYS_KQUEUE = 362 // { int kqueue(void); }
+ SYS_KEVENT = 363 // { int kevent(int fd, \
+ SYS_SCTP_PEELOFF = 364 // { int sctp_peeloff(int sd, caddr_t name ); }
+ SYS_LCHFLAGS = 391 // { int lchflags(char *path, int flags); }
+ SYS_UUIDGEN = 392 // { int uuidgen(struct uuid *store, int count); }
+ SYS_SENDFILE = 393 // { int sendfile(int fd, int s, off_t offset, size_t nbytes, \
+ SYS_VARSYM_SET = 450 // { int varsym_set(int level, const char *name, const char *data); }
+ SYS_VARSYM_GET = 451 // { int varsym_get(int mask, const char *wild, char *buf, int bufsize); }
+ SYS_VARSYM_LIST = 452 // { int varsym_list(int level, char *buf, int maxsize, int *marker); }
+ SYS_EXEC_SYS_REGISTER = 465 // { int exec_sys_register(void *entry); }
+ SYS_EXEC_SYS_UNREGISTER = 466 // { int exec_sys_unregister(int id); }
+ SYS_SYS_CHECKPOINT = 467 // { int sys_checkpoint(int type, int fd, pid_t pid, int retval); }
+ SYS_MOUNTCTL = 468 // { int mountctl(const char *path, int op, int fd, const void *ctl, int ctllen, void *buf, int buflen); }
+ SYS_UMTX_SLEEP = 469 // { int umtx_sleep(volatile const int *ptr, int value, int timeout); }
+ SYS_UMTX_WAKEUP = 470 // { int umtx_wakeup(volatile const int *ptr, int count); }
+ SYS_JAIL_ATTACH = 471 // { int jail_attach(int jid); }
+ SYS_SET_TLS_AREA = 472 // { int set_tls_area(int which, struct tls_info *info, size_t infosize); }
+ SYS_GET_TLS_AREA = 473 // { int get_tls_area(int which, struct tls_info *info, size_t infosize); }
+ SYS_CLOSEFROM = 474 // { int closefrom(int fd); }
+ SYS_STAT = 475 // { int stat(const char *path, struct stat *ub); }
+ SYS_FSTAT = 476 // { int fstat(int fd, struct stat *sb); }
+ SYS_LSTAT = 477 // { int lstat(const char *path, struct stat *ub); }
+ SYS_FHSTAT = 478 // { int fhstat(const struct fhandle *u_fhp, struct stat *sb); }
+ SYS_GETDIRENTRIES = 479 // { int getdirentries(int fd, char *buf, u_int count, \
+ SYS_GETDENTS = 480 // { int getdents(int fd, char *buf, size_t count); }
+ SYS_USCHED_SET = 481 // { int usched_set(pid_t pid, int cmd, void *data, \
+ SYS_EXTACCEPT = 482 // { int extaccept(int s, int flags, caddr_t name, int *anamelen); }
+ SYS_EXTCONNECT = 483 // { int extconnect(int s, int flags, caddr_t name, int namelen); }
+ SYS_MCONTROL = 485 // { int mcontrol(void *addr, size_t len, int behav, off_t value); }
+ SYS_VMSPACE_CREATE = 486 // { int vmspace_create(void *id, int type, void *data); }
+ SYS_VMSPACE_DESTROY = 487 // { int vmspace_destroy(void *id); }
+ SYS_VMSPACE_CTL = 488 // { int vmspace_ctl(void *id, int cmd, \
+ SYS_VMSPACE_MMAP = 489 // { int vmspace_mmap(void *id, void *addr, size_t len, \
+ SYS_VMSPACE_MUNMAP = 490 // { int vmspace_munmap(void *id, void *addr, \
+ SYS_VMSPACE_MCONTROL = 491 // { int vmspace_mcontrol(void *id, void *addr, \
+ SYS_VMSPACE_PREAD = 492 // { ssize_t vmspace_pread(void *id, void *buf, \
+ SYS_VMSPACE_PWRITE = 493 // { ssize_t vmspace_pwrite(void *id, const void *buf, \
+ SYS_EXTEXIT = 494 // { void extexit(int how, int status, void *addr); }
+ SYS_LWP_CREATE = 495 // { int lwp_create(struct lwp_params *params); }
+ SYS_LWP_GETTID = 496 // { lwpid_t lwp_gettid(void); }
+ SYS_LWP_KILL = 497 // { int lwp_kill(pid_t pid, lwpid_t tid, int signum); }
+ SYS_LWP_RTPRIO = 498 // { int lwp_rtprio(int function, pid_t pid, lwpid_t tid, struct rtprio *rtp); }
+ SYS_PSELECT = 499 // { int pselect(int nd, fd_set *in, fd_set *ou, \
+ SYS_STATVFS = 500 // { int statvfs(const char *path, struct statvfs *buf); }
+ SYS_FSTATVFS = 501 // { int fstatvfs(int fd, struct statvfs *buf); }
+ SYS_FHSTATVFS = 502 // { int fhstatvfs(const struct fhandle *u_fhp, struct statvfs *buf); }
+ SYS_GETVFSSTAT = 503 // { int getvfsstat(struct statfs *buf, \
+ SYS_OPENAT = 504 // { int openat(int fd, char *path, int flags, int mode); }
+ SYS_FSTATAT = 505 // { int fstatat(int fd, char *path, \
+ SYS_FCHMODAT = 506 // { int fchmodat(int fd, char *path, int mode, \
+ SYS_FCHOWNAT = 507 // { int fchownat(int fd, char *path, int uid, int gid, \
+ SYS_UNLINKAT = 508 // { int unlinkat(int fd, char *path, int flags); }
+ SYS_FACCESSAT = 509 // { int faccessat(int fd, char *path, int amode, \
+ SYS_MQ_OPEN = 510 // { mqd_t mq_open(const char * name, int oflag, \
+ SYS_MQ_CLOSE = 511 // { int mq_close(mqd_t mqdes); }
+ SYS_MQ_UNLINK = 512 // { int mq_unlink(const char *name); }
+ SYS_MQ_GETATTR = 513 // { int mq_getattr(mqd_t mqdes, \
+ SYS_MQ_SETATTR = 514 // { int mq_setattr(mqd_t mqdes, \
+ SYS_MQ_NOTIFY = 515 // { int mq_notify(mqd_t mqdes, \
+ SYS_MQ_SEND = 516 // { int mq_send(mqd_t mqdes, const char *msg_ptr, \
+ SYS_MQ_RECEIVE = 517 // { ssize_t mq_receive(mqd_t mqdes, char *msg_ptr, \
+ SYS_MQ_TIMEDSEND = 518 // { int mq_timedsend(mqd_t mqdes, \
+ SYS_MQ_TIMEDRECEIVE = 519 // { ssize_t mq_timedreceive(mqd_t mqdes, \
+ SYS_IOPRIO_SET = 520 // { int ioprio_set(int which, int who, int prio); }
+ SYS_IOPRIO_GET = 521 // { int ioprio_get(int which, int who); }
+ SYS_CHROOT_KERNEL = 522 // { int chroot_kernel(char *path); }
+ SYS_RENAMEAT = 523 // { int renameat(int oldfd, char *old, int newfd, \
+ SYS_MKDIRAT = 524 // { int mkdirat(int fd, char *path, mode_t mode); }
+ SYS_MKFIFOAT = 525 // { int mkfifoat(int fd, char *path, mode_t mode); }
+ SYS_MKNODAT = 526 // { int mknodat(int fd, char *path, mode_t mode, \
+ SYS_READLINKAT = 527 // { int readlinkat(int fd, char *path, char *buf, \
+ SYS_SYMLINKAT = 528 // { int symlinkat(char *path1, int fd, char *path2); }
+ SYS_SWAPOFF = 529 // { int swapoff(char *name); }
+ SYS_VQUOTACTL = 530 // { int vquotactl(const char *path, \
+ SYS_LINKAT = 531 // { int linkat(int fd1, char *path1, int fd2, \
+ SYS_EACCESS = 532 // { int eaccess(char *path, int flags); }
+ SYS_LPATHCONF = 533 // { int lpathconf(char *path, int name); }
+)
diff --git a/src/pkg/syscall/zsysnum_dragonfly_amd64.go b/src/pkg/syscall/zsysnum_dragonfly_amd64.go
new file mode 100644
index 000000000..68eeb32ac
--- /dev/null
+++ b/src/pkg/syscall/zsysnum_dragonfly_amd64.go
@@ -0,0 +1,303 @@
+// mksysnum_dragonfly.pl
+// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
+
+package syscall
+
+const (
+ // SYS_NOSYS = 0; // { int nosys(void); } syscall nosys_args int
+ SYS_EXIT = 1 // { void exit(int rval); }
+ SYS_FORK = 2 // { int fork(void); }
+ SYS_READ = 3 // { ssize_t read(int fd, void *buf, size_t nbyte); }
+ SYS_WRITE = 4 // { ssize_t write(int fd, const void *buf, size_t nbyte); }
+ SYS_OPEN = 5 // { int open(char *path, int flags, int mode); }
+ SYS_CLOSE = 6 // { int close(int fd); }
+ SYS_WAIT4 = 7 // { int wait4(int pid, int *status, int options, \
+ SYS_LINK = 9 // { int link(char *path, char *link); }
+ SYS_UNLINK = 10 // { int unlink(char *path); }
+ SYS_CHDIR = 12 // { int chdir(char *path); }
+ SYS_FCHDIR = 13 // { int fchdir(int fd); }
+ SYS_MKNOD = 14 // { int mknod(char *path, int mode, int dev); }
+ SYS_CHMOD = 15 // { int chmod(char *path, int mode); }
+ SYS_CHOWN = 16 // { int chown(char *path, int uid, int gid); }
+ SYS_OBREAK = 17 // { int obreak(char *nsize); } break obreak_args int
+ SYS_GETFSSTAT = 18 // { int getfsstat(struct statfs *buf, long bufsize, \
+ SYS_GETPID = 20 // { pid_t getpid(void); }
+ SYS_MOUNT = 21 // { int mount(char *type, char *path, int flags, \
+ SYS_UNMOUNT = 22 // { int unmount(char *path, int flags); }
+ SYS_SETUID = 23 // { int setuid(uid_t uid); }
+ SYS_GETUID = 24 // { uid_t getuid(void); }
+ SYS_GETEUID = 25 // { uid_t geteuid(void); }
+ SYS_PTRACE = 26 // { int ptrace(int req, pid_t pid, caddr_t addr, \
+ SYS_RECVMSG = 27 // { int recvmsg(int s, struct msghdr *msg, int flags); }
+ SYS_SENDMSG = 28 // { int sendmsg(int s, caddr_t msg, int flags); }
+ SYS_RECVFROM = 29 // { int recvfrom(int s, caddr_t buf, size_t len, \
+ SYS_ACCEPT = 30 // { int accept(int s, caddr_t name, int *anamelen); }
+ SYS_GETPEERNAME = 31 // { int getpeername(int fdes, caddr_t asa, int *alen); }
+ SYS_GETSOCKNAME = 32 // { int getsockname(int fdes, caddr_t asa, int *alen); }
+ SYS_ACCESS = 33 // { int access(char *path, int flags); }
+ SYS_CHFLAGS = 34 // { int chflags(char *path, int flags); }
+ SYS_FCHFLAGS = 35 // { int fchflags(int fd, int flags); }
+ SYS_SYNC = 36 // { int sync(void); }
+ SYS_KILL = 37 // { int kill(int pid, int signum); }
+ SYS_GETPPID = 39 // { pid_t getppid(void); }
+ SYS_DUP = 41 // { int dup(u_int fd); }
+ SYS_PIPE = 42 // { int pipe(void); }
+ SYS_GETEGID = 43 // { gid_t getegid(void); }
+ SYS_PROFIL = 44 // { int profil(caddr_t samples, size_t size, \
+ SYS_KTRACE = 45 // { int ktrace(const char *fname, int ops, int facs, \
+ SYS_GETGID = 47 // { gid_t getgid(void); }
+ SYS_GETLOGIN = 49 // { int getlogin(char *namebuf, u_int namelen); }
+ SYS_SETLOGIN = 50 // { int setlogin(char *namebuf); }
+ SYS_ACCT = 51 // { int acct(char *path); }
+ SYS_SIGALTSTACK = 53 // { int sigaltstack(stack_t *ss, stack_t *oss); }
+ SYS_IOCTL = 54 // { int ioctl(int fd, u_long com, caddr_t data); }
+ SYS_REBOOT = 55 // { int reboot(int opt); }
+ SYS_REVOKE = 56 // { int revoke(char *path); }
+ SYS_SYMLINK = 57 // { int symlink(char *path, char *link); }
+ SYS_READLINK = 58 // { int readlink(char *path, char *buf, int count); }
+ SYS_EXECVE = 59 // { int execve(char *fname, char **argv, char **envv); }
+ SYS_UMASK = 60 // { int umask(int newmask); } umask umask_args int
+ SYS_CHROOT = 61 // { int chroot(char *path); }
+ SYS_MSYNC = 65 // { int msync(void *addr, size_t len, int flags); }
+ SYS_VFORK = 66 // { pid_t vfork(void); }
+ SYS_SBRK = 69 // { int sbrk(int incr); }
+ SYS_SSTK = 70 // { int sstk(int incr); }
+ SYS_MUNMAP = 73 // { int munmap(void *addr, size_t len); }
+ SYS_MPROTECT = 74 // { int mprotect(void *addr, size_t len, int prot); }
+ SYS_MADVISE = 75 // { int madvise(void *addr, size_t len, int behav); }
+ SYS_MINCORE = 78 // { int mincore(const void *addr, size_t len, \
+ SYS_GETGROUPS = 79 // { int getgroups(u_int gidsetsize, gid_t *gidset); }
+ SYS_SETGROUPS = 80 // { int setgroups(u_int gidsetsize, gid_t *gidset); }
+ SYS_GETPGRP = 81 // { int getpgrp(void); }
+ SYS_SETPGID = 82 // { int setpgid(int pid, int pgid); }
+ SYS_SETITIMER = 83 // { int setitimer(u_int which, struct itimerval *itv, \
+ SYS_SWAPON = 85 // { int swapon(char *name); }
+ SYS_GETITIMER = 86 // { int getitimer(u_int which, struct itimerval *itv); }
+ SYS_GETDTABLESIZE = 89 // { int getdtablesize(void); }
+ SYS_DUP2 = 90 // { int dup2(u_int from, u_int to); }
+ SYS_FCNTL = 92 // { int fcntl(int fd, int cmd, long arg); }
+ SYS_SELECT = 93 // { int select(int nd, fd_set *in, fd_set *ou, \
+ SYS_FSYNC = 95 // { int fsync(int fd); }
+ SYS_SETPRIORITY = 96 // { int setpriority(int which, int who, int prio); }
+ SYS_SOCKET = 97 // { int socket(int domain, int type, int protocol); }
+ SYS_CONNECT = 98 // { int connect(int s, caddr_t name, int namelen); }
+ SYS_GETPRIORITY = 100 // { int getpriority(int which, int who); }
+ SYS_BIND = 104 // { int bind(int s, caddr_t name, int namelen); }
+ SYS_SETSOCKOPT = 105 // { int setsockopt(int s, int level, int name, \
+ SYS_LISTEN = 106 // { int listen(int s, int backlog); }
+ SYS_GETTIMEOFDAY = 116 // { int gettimeofday(struct timeval *tp, \
+ SYS_GETRUSAGE = 117 // { int getrusage(int who, struct rusage *rusage); }
+ SYS_GETSOCKOPT = 118 // { int getsockopt(int s, int level, int name, \
+ SYS_READV = 120 // { int readv(int fd, struct iovec *iovp, u_int iovcnt); }
+ SYS_WRITEV = 121 // { int writev(int fd, struct iovec *iovp, \
+ SYS_SETTIMEOFDAY = 122 // { int settimeofday(struct timeval *tv, \
+ SYS_FCHOWN = 123 // { int fchown(int fd, int uid, int gid); }
+ SYS_FCHMOD = 124 // { int fchmod(int fd, int mode); }
+ SYS_SETREUID = 126 // { int setreuid(int ruid, int euid); }
+ SYS_SETREGID = 127 // { int setregid(int rgid, int egid); }
+ SYS_RENAME = 128 // { int rename(char *from, char *to); }
+ SYS_FLOCK = 131 // { int flock(int fd, int how); }
+ SYS_MKFIFO = 132 // { int mkfifo(char *path, int mode); }
+ SYS_SENDTO = 133 // { int sendto(int s, caddr_t buf, size_t len, \
+ SYS_SHUTDOWN = 134 // { int shutdown(int s, int how); }
+ SYS_SOCKETPAIR = 135 // { int socketpair(int domain, int type, int protocol, \
+ SYS_MKDIR = 136 // { int mkdir(char *path, int mode); }
+ SYS_RMDIR = 137 // { int rmdir(char *path); }
+ SYS_UTIMES = 138 // { int utimes(char *path, struct timeval *tptr); }
+ SYS_ADJTIME = 140 // { int adjtime(struct timeval *delta, \
+ SYS_SETSID = 147 // { int setsid(void); }
+ SYS_QUOTACTL = 148 // { int quotactl(char *path, int cmd, int uid, \
+ SYS_STATFS = 157 // { int statfs(char *path, struct statfs *buf); }
+ SYS_FSTATFS = 158 // { int fstatfs(int fd, struct statfs *buf); }
+ SYS_GETFH = 161 // { int getfh(char *fname, struct fhandle *fhp); }
+ SYS_GETDOMAINNAME = 162 // { int getdomainname(char *domainname, int len); }
+ SYS_SETDOMAINNAME = 163 // { int setdomainname(char *domainname, int len); }
+ SYS_UNAME = 164 // { int uname(struct utsname *name); }
+ SYS_SYSARCH = 165 // { int sysarch(int op, char *parms); }
+ SYS_RTPRIO = 166 // { int rtprio(int function, pid_t pid, \
+ SYS_SEMSYS = 169 // { int semsys(int which, int a2, int a3, int a4, \
+ SYS_MSGSYS = 170 // { int msgsys(int which, int a2, int a3, int a4, \
+ SYS_SHMSYS = 171 // { int shmsys(int which, int a2, int a3, int a4); }
+ SYS_EXTPREAD = 173 // { ssize_t extpread(int fd, void *buf, \
+ SYS_EXTPWRITE = 174 // { ssize_t extpwrite(int fd, const void *buf, \
+ SYS_NTP_ADJTIME = 176 // { int ntp_adjtime(struct timex *tp); }
+ SYS_SETGID = 181 // { int setgid(gid_t gid); }
+ SYS_SETEGID = 182 // { int setegid(gid_t egid); }
+ SYS_SETEUID = 183 // { int seteuid(uid_t euid); }
+ SYS_PATHCONF = 191 // { int pathconf(char *path, int name); }
+ SYS_FPATHCONF = 192 // { int fpathconf(int fd, int name); }
+ SYS_GETRLIMIT = 194 // { int getrlimit(u_int which, \
+ SYS_SETRLIMIT = 195 // { int setrlimit(u_int which, \
+ SYS_MMAP = 197 // { caddr_t mmap(caddr_t addr, size_t len, int prot, \
+ // SYS_NOSYS = 198; // { int nosys(void); } __syscall __syscall_args int
+ SYS_LSEEK = 199 // { off_t lseek(int fd, int pad, off_t offset, \
+ SYS_TRUNCATE = 200 // { int truncate(char *path, int pad, off_t length); }
+ SYS_FTRUNCATE = 201 // { int ftruncate(int fd, int pad, off_t length); }
+ SYS___SYSCTL = 202 // { int __sysctl(int *name, u_int namelen, void *old, \
+ SYS_MLOCK = 203 // { int mlock(const void *addr, size_t len); }
+ SYS_MUNLOCK = 204 // { int munlock(const void *addr, size_t len); }
+ SYS_UNDELETE = 205 // { int undelete(char *path); }
+ SYS_FUTIMES = 206 // { int futimes(int fd, struct timeval *tptr); }
+ SYS_GETPGID = 207 // { int getpgid(pid_t pid); }
+ SYS_POLL = 209 // { int poll(struct pollfd *fds, u_int nfds, \
+ SYS___SEMCTL = 220 // { int __semctl(int semid, int semnum, int cmd, \
+ SYS_SEMGET = 221 // { int semget(key_t key, int nsems, int semflg); }
+ SYS_SEMOP = 222 // { int semop(int semid, struct sembuf *sops, \
+ SYS_MSGCTL = 224 // { int msgctl(int msqid, int cmd, \
+ SYS_MSGGET = 225 // { int msgget(key_t key, int msgflg); }
+ SYS_MSGSND = 226 // { int msgsnd(int msqid, void *msgp, size_t msgsz, \
+ SYS_MSGRCV = 227 // { int msgrcv(int msqid, void *msgp, size_t msgsz, \
+ SYS_SHMAT = 228 // { caddr_t shmat(int shmid, const void *shmaddr, \
+ SYS_SHMCTL = 229 // { int shmctl(int shmid, int cmd, \
+ SYS_SHMDT = 230 // { int shmdt(const void *shmaddr); }
+ SYS_SHMGET = 231 // { int shmget(key_t key, size_t size, int shmflg); }
+ SYS_CLOCK_GETTIME = 232 // { int clock_gettime(clockid_t clock_id, \
+ SYS_CLOCK_SETTIME = 233 // { int clock_settime(clockid_t clock_id, \
+ SYS_CLOCK_GETRES = 234 // { int clock_getres(clockid_t clock_id, \
+ SYS_NANOSLEEP = 240 // { int nanosleep(const struct timespec *rqtp, \
+ SYS_MINHERIT = 250 // { int minherit(void *addr, size_t len, int inherit); }
+ SYS_RFORK = 251 // { int rfork(int flags); }
+ SYS_OPENBSD_POLL = 252 // { int openbsd_poll(struct pollfd *fds, u_int nfds, \
+ SYS_ISSETUGID = 253 // { int issetugid(void); }
+ SYS_LCHOWN = 254 // { int lchown(char *path, int uid, int gid); }
+ SYS_LCHMOD = 274 // { int lchmod(char *path, mode_t mode); }
+ SYS_LUTIMES = 276 // { int lutimes(char *path, struct timeval *tptr); }
+ SYS_EXTPREADV = 289 // { ssize_t extpreadv(int fd, struct iovec *iovp, \
+ SYS_EXTPWRITEV = 290 // { ssize_t extpwritev(int fd, struct iovec *iovp,\
+ SYS_FHSTATFS = 297 // { int fhstatfs(const struct fhandle *u_fhp, struct statfs *buf); }
+ SYS_FHOPEN = 298 // { int fhopen(const struct fhandle *u_fhp, int flags); }
+ SYS_MODNEXT = 300 // { int modnext(int modid); }
+ SYS_MODSTAT = 301 // { int modstat(int modid, struct module_stat* stat); }
+ SYS_MODFNEXT = 302 // { int modfnext(int modid); }
+ SYS_MODFIND = 303 // { int modfind(const char *name); }
+ SYS_KLDLOAD = 304 // { int kldload(const char *file); }
+ SYS_KLDUNLOAD = 305 // { int kldunload(int fileid); }
+ SYS_KLDFIND = 306 // { int kldfind(const char *file); }
+ SYS_KLDNEXT = 307 // { int kldnext(int fileid); }
+ SYS_KLDSTAT = 308 // { int kldstat(int fileid, struct kld_file_stat* stat); }
+ SYS_KLDFIRSTMOD = 309 // { int kldfirstmod(int fileid); }
+ SYS_GETSID = 310 // { int getsid(pid_t pid); }
+ SYS_SETRESUID = 311 // { int setresuid(uid_t ruid, uid_t euid, uid_t suid); }
+ SYS_SETRESGID = 312 // { int setresgid(gid_t rgid, gid_t egid, gid_t sgid); }
+ SYS_AIO_RETURN = 314 // { int aio_return(struct aiocb *aiocbp); }
+ SYS_AIO_SUSPEND = 315 // { int aio_suspend(struct aiocb * const * aiocbp, int nent, const struct timespec *timeout); }
+ SYS_AIO_CANCEL = 316 // { int aio_cancel(int fd, struct aiocb *aiocbp); }
+ SYS_AIO_ERROR = 317 // { int aio_error(struct aiocb *aiocbp); }
+ SYS_AIO_READ = 318 // { int aio_read(struct aiocb *aiocbp); }
+ SYS_AIO_WRITE = 319 // { int aio_write(struct aiocb *aiocbp); }
+ SYS_LIO_LISTIO = 320 // { int lio_listio(int mode, struct aiocb * const *acb_list, int nent, struct sigevent *sig); }
+ SYS_YIELD = 321 // { int yield(void); }
+ SYS_MLOCKALL = 324 // { int mlockall(int how); }
+ SYS_MUNLOCKALL = 325 // { int munlockall(void); }
+ SYS___GETCWD = 326 // { int __getcwd(u_char *buf, u_int buflen); }
+ SYS_SCHED_SETPARAM = 327 // { int sched_setparam (pid_t pid, const struct sched_param *param); }
+ SYS_SCHED_GETPARAM = 328 // { int sched_getparam (pid_t pid, struct sched_param *param); }
+ SYS_SCHED_SETSCHEDULER = 329 // { int sched_setscheduler (pid_t pid, int policy, const struct sched_param *param); }
+ SYS_SCHED_GETSCHEDULER = 330 // { int sched_getscheduler (pid_t pid); }
+ SYS_SCHED_YIELD = 331 // { int sched_yield (void); }
+ SYS_SCHED_GET_PRIORITY_MAX = 332 // { int sched_get_priority_max (int policy); }
+ SYS_SCHED_GET_PRIORITY_MIN = 333 // { int sched_get_priority_min (int policy); }
+ SYS_SCHED_RR_GET_INTERVAL = 334 // { int sched_rr_get_interval (pid_t pid, struct timespec *interval); }
+ SYS_UTRACE = 335 // { int utrace(const void *addr, size_t len); }
+ SYS_KLDSYM = 337 // { int kldsym(int fileid, int cmd, void *data); }
+ SYS_JAIL = 338 // { int jail(struct jail *jail); }
+ SYS_SIGPROCMASK = 340 // { int sigprocmask(int how, const sigset_t *set, \
+ SYS_SIGSUSPEND = 341 // { int sigsuspend(const sigset_t *sigmask); }
+ SYS_SIGACTION = 342 // { int sigaction(int sig, const struct sigaction *act, \
+ SYS_SIGPENDING = 343 // { int sigpending(sigset_t *set); }
+ SYS_SIGRETURN = 344 // { int sigreturn(ucontext_t *sigcntxp); }
+ SYS_SIGTIMEDWAIT = 345 // { int sigtimedwait(const sigset_t *set,\
+ SYS_SIGWAITINFO = 346 // { int sigwaitinfo(const sigset_t *set,\
+ SYS___ACL_GET_FILE = 347 // { int __acl_get_file(const char *path, \
+ SYS___ACL_SET_FILE = 348 // { int __acl_set_file(const char *path, \
+ SYS___ACL_GET_FD = 349 // { int __acl_get_fd(int filedes, acl_type_t type, \
+ SYS___ACL_SET_FD = 350 // { int __acl_set_fd(int filedes, acl_type_t type, \
+ SYS___ACL_DELETE_FILE = 351 // { int __acl_delete_file(const char *path, \
+ SYS___ACL_DELETE_FD = 352 // { int __acl_delete_fd(int filedes, acl_type_t type); }
+ SYS___ACL_ACLCHECK_FILE = 353 // { int __acl_aclcheck_file(const char *path, \
+ SYS___ACL_ACLCHECK_FD = 354 // { int __acl_aclcheck_fd(int filedes, acl_type_t type, \
+ SYS_EXTATTRCTL = 355 // { int extattrctl(const char *path, int cmd, \
+ SYS_EXTATTR_SET_FILE = 356 // { int extattr_set_file(const char *path, \
+ SYS_EXTATTR_GET_FILE = 357 // { int extattr_get_file(const char *path, \
+ SYS_EXTATTR_DELETE_FILE = 358 // { int extattr_delete_file(const char *path, \
+ SYS_AIO_WAITCOMPLETE = 359 // { int aio_waitcomplete(struct aiocb **aiocbp, struct timespec *timeout); }
+ SYS_GETRESUID = 360 // { int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid); }
+ SYS_GETRESGID = 361 // { int getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid); }
+ SYS_KQUEUE = 362 // { int kqueue(void); }
+ SYS_KEVENT = 363 // { int kevent(int fd, \
+ SYS_SCTP_PEELOFF = 364 // { int sctp_peeloff(int sd, caddr_t name ); }
+ SYS_LCHFLAGS = 391 // { int lchflags(char *path, int flags); }
+ SYS_UUIDGEN = 392 // { int uuidgen(struct uuid *store, int count); }
+ SYS_SENDFILE = 393 // { int sendfile(int fd, int s, off_t offset, size_t nbytes, \
+ SYS_VARSYM_SET = 450 // { int varsym_set(int level, const char *name, const char *data); }
+ SYS_VARSYM_GET = 451 // { int varsym_get(int mask, const char *wild, char *buf, int bufsize); }
+ SYS_VARSYM_LIST = 452 // { int varsym_list(int level, char *buf, int maxsize, int *marker); }
+ SYS_EXEC_SYS_REGISTER = 465 // { int exec_sys_register(void *entry); }
+ SYS_EXEC_SYS_UNREGISTER = 466 // { int exec_sys_unregister(int id); }
+ SYS_SYS_CHECKPOINT = 467 // { int sys_checkpoint(int type, int fd, pid_t pid, int retval); }
+ SYS_MOUNTCTL = 468 // { int mountctl(const char *path, int op, int fd, const void *ctl, int ctllen, void *buf, int buflen); }
+ SYS_UMTX_SLEEP = 469 // { int umtx_sleep(volatile const int *ptr, int value, int timeout); }
+ SYS_UMTX_WAKEUP = 470 // { int umtx_wakeup(volatile const int *ptr, int count); }
+ SYS_JAIL_ATTACH = 471 // { int jail_attach(int jid); }
+ SYS_SET_TLS_AREA = 472 // { int set_tls_area(int which, struct tls_info *info, size_t infosize); }
+ SYS_GET_TLS_AREA = 473 // { int get_tls_area(int which, struct tls_info *info, size_t infosize); }
+ SYS_CLOSEFROM = 474 // { int closefrom(int fd); }
+ SYS_STAT = 475 // { int stat(const char *path, struct stat *ub); }
+ SYS_FSTAT = 476 // { int fstat(int fd, struct stat *sb); }
+ SYS_LSTAT = 477 // { int lstat(const char *path, struct stat *ub); }
+ SYS_FHSTAT = 478 // { int fhstat(const struct fhandle *u_fhp, struct stat *sb); }
+ SYS_GETDIRENTRIES = 479 // { int getdirentries(int fd, char *buf, u_int count, \
+ SYS_GETDENTS = 480 // { int getdents(int fd, char *buf, size_t count); }
+ SYS_USCHED_SET = 481 // { int usched_set(pid_t pid, int cmd, void *data, \
+ SYS_EXTACCEPT = 482 // { int extaccept(int s, int flags, caddr_t name, int *anamelen); }
+ SYS_EXTCONNECT = 483 // { int extconnect(int s, int flags, caddr_t name, int namelen); }
+ SYS_MCONTROL = 485 // { int mcontrol(void *addr, size_t len, int behav, off_t value); }
+ SYS_VMSPACE_CREATE = 486 // { int vmspace_create(void *id, int type, void *data); }
+ SYS_VMSPACE_DESTROY = 487 // { int vmspace_destroy(void *id); }
+ SYS_VMSPACE_CTL = 488 // { int vmspace_ctl(void *id, int cmd, \
+ SYS_VMSPACE_MMAP = 489 // { int vmspace_mmap(void *id, void *addr, size_t len, \
+ SYS_VMSPACE_MUNMAP = 490 // { int vmspace_munmap(void *id, void *addr, \
+ SYS_VMSPACE_MCONTROL = 491 // { int vmspace_mcontrol(void *id, void *addr, \
+ SYS_VMSPACE_PREAD = 492 // { ssize_t vmspace_pread(void *id, void *buf, \
+ SYS_VMSPACE_PWRITE = 493 // { ssize_t vmspace_pwrite(void *id, const void *buf, \
+ SYS_EXTEXIT = 494 // { void extexit(int how, int status, void *addr); }
+ SYS_LWP_CREATE = 495 // { int lwp_create(struct lwp_params *params); }
+ SYS_LWP_GETTID = 496 // { lwpid_t lwp_gettid(void); }
+ SYS_LWP_KILL = 497 // { int lwp_kill(pid_t pid, lwpid_t tid, int signum); }
+ SYS_LWP_RTPRIO = 498 // { int lwp_rtprio(int function, pid_t pid, lwpid_t tid, struct rtprio *rtp); }
+ SYS_PSELECT = 499 // { int pselect(int nd, fd_set *in, fd_set *ou, \
+ SYS_STATVFS = 500 // { int statvfs(const char *path, struct statvfs *buf); }
+ SYS_FSTATVFS = 501 // { int fstatvfs(int fd, struct statvfs *buf); }
+ SYS_FHSTATVFS = 502 // { int fhstatvfs(const struct fhandle *u_fhp, struct statvfs *buf); }
+ SYS_GETVFSSTAT = 503 // { int getvfsstat(struct statfs *buf, \
+ SYS_OPENAT = 504 // { int openat(int fd, char *path, int flags, int mode); }
+ SYS_FSTATAT = 505 // { int fstatat(int fd, char *path, \
+ SYS_FCHMODAT = 506 // { int fchmodat(int fd, char *path, int mode, \
+ SYS_FCHOWNAT = 507 // { int fchownat(int fd, char *path, int uid, int gid, \
+ SYS_UNLINKAT = 508 // { int unlinkat(int fd, char *path, int flags); }
+ SYS_FACCESSAT = 509 // { int faccessat(int fd, char *path, int amode, \
+ SYS_MQ_OPEN = 510 // { mqd_t mq_open(const char * name, int oflag, \
+ SYS_MQ_CLOSE = 511 // { int mq_close(mqd_t mqdes); }
+ SYS_MQ_UNLINK = 512 // { int mq_unlink(const char *name); }
+ SYS_MQ_GETATTR = 513 // { int mq_getattr(mqd_t mqdes, \
+ SYS_MQ_SETATTR = 514 // { int mq_setattr(mqd_t mqdes, \
+ SYS_MQ_NOTIFY = 515 // { int mq_notify(mqd_t mqdes, \
+ SYS_MQ_SEND = 516 // { int mq_send(mqd_t mqdes, const char *msg_ptr, \
+ SYS_MQ_RECEIVE = 517 // { ssize_t mq_receive(mqd_t mqdes, char *msg_ptr, \
+ SYS_MQ_TIMEDSEND = 518 // { int mq_timedsend(mqd_t mqdes, \
+ SYS_MQ_TIMEDRECEIVE = 519 // { ssize_t mq_timedreceive(mqd_t mqdes, \
+ SYS_IOPRIO_SET = 520 // { int ioprio_set(int which, int who, int prio); }
+ SYS_IOPRIO_GET = 521 // { int ioprio_get(int which, int who); }
+ SYS_CHROOT_KERNEL = 522 // { int chroot_kernel(char *path); }
+ SYS_RENAMEAT = 523 // { int renameat(int oldfd, char *old, int newfd, \
+ SYS_MKDIRAT = 524 // { int mkdirat(int fd, char *path, mode_t mode); }
+ SYS_MKFIFOAT = 525 // { int mkfifoat(int fd, char *path, mode_t mode); }
+ SYS_MKNODAT = 526 // { int mknodat(int fd, char *path, mode_t mode, \
+ SYS_READLINKAT = 527 // { int readlinkat(int fd, char *path, char *buf, \
+ SYS_SYMLINKAT = 528 // { int symlinkat(char *path1, int fd, char *path2); }
+ SYS_SWAPOFF = 529 // { int swapoff(char *name); }
+ SYS_VQUOTACTL = 530 // { int vquotactl(const char *path, \
+ SYS_LINKAT = 531 // { int linkat(int fd1, char *path1, int fd2, \
+ SYS_EACCESS = 532 // { int eaccess(char *path, int flags); }
+ SYS_LPATHCONF = 533 // { int lpathconf(char *path, int name); }
+)
diff --git a/src/pkg/syscall/ztypes_darwin_386.go b/src/pkg/syscall/ztypes_darwin_386.go
index 71346fbc1..13724c3cc 100644
--- a/src/pkg/syscall/ztypes_darwin_386.go
+++ b/src/pkg/syscall/ztypes_darwin_386.go
@@ -237,6 +237,15 @@ type Inet6Pktinfo struct {
Ifindex uint32
}
+type IPv6MTUInfo struct {
+ Addr RawSockaddrInet6
+ Mtu uint32
+}
+
+type ICMPv6Filter struct {
+ Filt [8]uint32
+}
+
const (
SizeofSockaddrInet4 = 0x10
SizeofSockaddrInet6 = 0x1c
@@ -250,6 +259,8 @@ const (
SizeofCmsghdr = 0xc
SizeofInet4Pktinfo = 0xc
SizeofInet6Pktinfo = 0x14
+ SizeofIPv6MTUInfo = 0x20
+ SizeofICMPv6Filter = 0x20
)
const (
diff --git a/src/pkg/syscall/ztypes_darwin_amd64.go b/src/pkg/syscall/ztypes_darwin_amd64.go
index f0809fe4a..65b02ae4f 100644
--- a/src/pkg/syscall/ztypes_darwin_amd64.go
+++ b/src/pkg/syscall/ztypes_darwin_amd64.go
@@ -245,6 +245,15 @@ type Inet6Pktinfo struct {
Ifindex uint32
}
+type IPv6MTUInfo struct {
+ Addr RawSockaddrInet6
+ Mtu uint32
+}
+
+type ICMPv6Filter struct {
+ Filt [8]uint32
+}
+
const (
SizeofSockaddrInet4 = 0x10
SizeofSockaddrInet6 = 0x1c
@@ -258,6 +267,8 @@ const (
SizeofCmsghdr = 0xc
SizeofInet4Pktinfo = 0xc
SizeofInet6Pktinfo = 0x14
+ SizeofIPv6MTUInfo = 0x20
+ SizeofICMPv6Filter = 0x20
)
const (
diff --git a/src/pkg/syscall/ztypes_dragonfly_386.go b/src/pkg/syscall/ztypes_dragonfly_386.go
new file mode 100644
index 000000000..c467d8593
--- /dev/null
+++ b/src/pkg/syscall/ztypes_dragonfly_386.go
@@ -0,0 +1,429 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types_dragonfly.go
+
+package syscall
+
+const (
+ sizeofPtr = 0x4
+ sizeofShort = 0x2
+ sizeofInt = 0x4
+ sizeofLong = 0x4
+ sizeofLongLong = 0x8
+)
+
+type (
+ _C_short int16
+ _C_int int32
+ _C_long int32
+ _C_long_long int64
+)
+
+type Timespec struct {
+ Sec int32
+ Nsec int32
+}
+
+type Timeval struct {
+ Sec int32
+ Usec int32
+}
+
+type Rusage struct {
+ Utime Timeval
+ Stime Timeval
+ Maxrss int32
+ Ixrss int32
+ Idrss int32
+ Isrss int32
+ Minflt int32
+ Majflt int32
+ Nswap int32
+ Inblock int32
+ Oublock int32
+ Msgsnd int32
+ Msgrcv int32
+ Nsignals int32
+ Nvcsw int32
+ Nivcsw int32
+}
+
+type Rlimit struct {
+ Cur int64
+ Max int64
+}
+
+type _Gid_t uint32
+
+const (
+ F_DUPFD_CLOEXEC = 0
+)
+
+const (
+ S_IFMT = 0xf000
+ S_IFIFO = 0x1000
+ S_IFCHR = 0x2000
+ S_IFDIR = 0x4000
+ S_IFBLK = 0x6000
+ S_IFREG = 0x8000
+ S_IFLNK = 0xa000
+ S_IFSOCK = 0xc000
+ S_ISUID = 0x800
+ S_ISGID = 0x400
+ S_ISVTX = 0x200
+ S_IRUSR = 0x100
+ S_IWUSR = 0x80
+ S_IXUSR = 0x40
+)
+
+type Stat_t struct {
+ Ino uint64
+ Nlink uint32
+ Dev uint32
+ Mode uint16
+ Padding1 uint16
+ Uid uint32
+ Gid uint32
+ Rdev uint32
+ Atim Timespec
+ Mtim Timespec
+ Ctim Timespec
+ Size int64
+ Blocks int64
+ Blksize uint32
+ Flags uint32
+ Gen uint32
+ Lspare int32
+ Qspare1 int64
+ Qspare2 int64
+}
+
+type Statfs_t struct {
+ Spare2 int32
+ Bsize int32
+ Iosize int32
+ Blocks int32
+ Bfree int32
+ Bavail int32
+ Files int32
+ Ffree int32
+ Fsid Fsid
+ Owner uint32
+ Type int32
+ Flags int32
+ Syncwrites int32
+ Asyncwrites int32
+ Fstypename [16]int8
+ Mntonname [80]int8
+ Syncreads int32
+ Asyncreads int32
+ Spares1 int16
+ Mntfromname [80]int8
+ Spares2 int16
+ Spare [2]int32
+}
+
+type Flock_t struct {
+ Start int64
+ Len int64
+ Pid int32
+ Type int16
+ Whence int16
+}
+
+type Dirent struct {
+ Fileno uint64
+ Namlen uint16
+ Type uint8
+ Unused1 uint8
+ Unused2 uint32
+ Name [256]int8
+}
+
+type Fsid struct {
+ Val [2]int32
+}
+
+type RawSockaddrInet4 struct {
+ Len uint8
+ Family uint8
+ Port uint16
+ Addr [4]byte /* in_addr */
+ Zero [8]int8
+}
+
+type RawSockaddrInet6 struct {
+ Len uint8
+ Family uint8
+ Port uint16
+ Flowinfo uint32
+ Addr [16]byte /* in6_addr */
+ Scope_id uint32
+}
+
+type RawSockaddrUnix struct {
+ Len uint8
+ Family uint8
+ Path [104]int8
+}
+
+type RawSockaddrDatalink struct {
+ Len uint8
+ Family uint8
+ Index uint16
+ Type uint8
+ Nlen uint8
+ Alen uint8
+ Slen uint8
+ Data [12]int8
+ Rcf uint16
+ Route [16]uint16
+}
+
+type RawSockaddr struct {
+ Len uint8
+ Family uint8
+ Data [14]int8
+}
+
+type RawSockaddrAny struct {
+ Addr RawSockaddr
+ Pad [92]int8
+}
+
+type _Socklen uint32
+
+type Linger struct {
+ Onoff int32
+ Linger int32
+}
+
+type Iovec struct {
+ Base *byte
+ Len uint32
+}
+
+type IPMreq struct {
+ Multiaddr [4]byte /* in_addr */
+ Interface [4]byte /* in_addr */
+}
+
+type IPv6Mreq struct {
+ Multiaddr [16]byte /* in6_addr */
+ Interface uint32
+}
+
+type Msghdr struct {
+ Name *byte
+ Namelen uint32
+ Iov *Iovec
+ Iovlen int32
+ Control *byte
+ Controllen uint32
+ Flags int32
+}
+
+type Cmsghdr struct {
+ Len uint32
+ Level int32
+ Type int32
+}
+
+type Inet6Pktinfo struct {
+ Addr [16]byte /* in6_addr */
+ Ifindex uint32
+}
+
+type IPv6MTUInfo struct {
+ Addr RawSockaddrInet6
+ Mtu uint32
+}
+
+type ICMPv6Filter struct {
+ Filt [8]uint32
+}
+
+const (
+ SizeofSockaddrInet4 = 0x10
+ SizeofSockaddrInet6 = 0x1c
+ SizeofSockaddrAny = 0x6c
+ SizeofSockaddrUnix = 0x6a
+ SizeofSockaddrDatalink = 0x36
+ SizeofLinger = 0x8
+ SizeofIPMreq = 0x8
+ SizeofIPv6Mreq = 0x14
+ SizeofMsghdr = 0x1c
+ SizeofCmsghdr = 0xc
+ SizeofInet6Pktinfo = 0x14
+ SizeofIPv6MTUInfo = 0x20
+ SizeofICMPv6Filter = 0x20
+)
+
+const (
+ PTRACE_TRACEME = 0x0
+ PTRACE_CONT = 0x7
+ PTRACE_KILL = 0x8
+)
+
+type Kevent_t struct {
+ Ident uint32
+ Filter int16
+ Flags uint16
+ Fflags uint32
+ Data int32
+ Udata *byte
+}
+
+type FdSet struct {
+ Bits [32]uint32
+}
+
+const (
+ SizeofIfMsghdr = 0x68
+ SizeofIfData = 0x58
+ SizeofIfaMsghdr = 0x14
+ SizeofIfmaMsghdr = 0x10
+ SizeofIfAnnounceMsghdr = 0x18
+ SizeofRtMsghdr = 0x5c
+ SizeofRtMetrics = 0x38
+)
+
+type IfMsghdr struct {
+ Msglen uint16
+ Version uint8
+ Type uint8
+ Addrs int32
+ Flags int32
+ Index uint16
+ Pad_cgo_0 [2]byte
+ Data IfData
+}
+
+type IfData struct {
+ Type uint8
+ Physical uint8
+ Addrlen uint8
+ Hdrlen uint8
+ Recvquota uint8
+ Xmitquota uint8
+ Pad_cgo_0 [2]byte
+ Mtu uint32
+ Metric uint32
+ Link_state uint32
+ Baudrate uint64
+ Ipackets uint32
+ Ierrors uint32
+ Opackets uint32
+ Oerrors uint32
+ Collisions uint32
+ Ibytes uint32
+ Obytes uint32
+ Imcasts uint32
+ Omcasts uint32
+ Iqdrops uint32
+ Noproto uint32
+ Hwassist uint32
+ Unused uint32
+ Lastchange Timeval
+}
+
+type IfaMsghdr struct {
+ Msglen uint16
+ Version uint8
+ Type uint8
+ Addrs int32
+ Flags int32
+ Index uint16
+ Pad_cgo_0 [2]byte
+ Metric int32
+}
+
+type IfmaMsghdr struct {
+ Msglen uint16
+ Version uint8
+ Type uint8
+ Addrs int32
+ Flags int32
+ Index uint16
+ Pad_cgo_0 [2]byte
+}
+
+type IfAnnounceMsghdr struct {
+ Msglen uint16
+ Version uint8
+ Type uint8
+ Index uint16
+ Name [16]int8
+ What uint16
+}
+
+type RtMsghdr struct {
+ Msglen uint16
+ Version uint8
+ Type uint8
+ Index uint16
+ Pad_cgo_0 [2]byte
+ Flags int32
+ Addrs int32
+ Pid int32
+ Seq int32
+ Errno int32
+ Use int32
+ Inits uint32
+ Rmx RtMetrics
+}
+
+type RtMetrics struct {
+ Locks uint32
+ Mtu uint32
+ Pksent uint32
+ Expire uint32
+ Sendpipe uint32
+ Ssthresh uint32
+ Rtt uint32
+ Rttvar uint32
+ Recvpipe uint32
+ Hopcount uint32
+ Mssopt uint16
+ Pad uint16
+ Msl uint32
+ Iwmaxsegs uint32
+ Iwcapsegs uint32
+}
+
+const (
+ SizeofBpfVersion = 0x4
+ SizeofBpfStat = 0x8
+ SizeofBpfProgram = 0x8
+ SizeofBpfInsn = 0x8
+ SizeofBpfHdr = 0x14
+)
+
+type BpfVersion struct {
+ Major uint16
+ Minor uint16
+}
+
+type BpfStat struct {
+ Recv uint32
+ Drop 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_cgo_0 [2]byte
+}
diff --git a/src/pkg/syscall/ztypes_dragonfly_amd64.go b/src/pkg/syscall/ztypes_dragonfly_amd64.go
new file mode 100644
index 000000000..b71bf29f4
--- /dev/null
+++ b/src/pkg/syscall/ztypes_dragonfly_amd64.go
@@ -0,0 +1,435 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types_dragonfly.go
+
+package syscall
+
+const (
+ sizeofPtr = 0x8
+ sizeofShort = 0x2
+ sizeofInt = 0x4
+ sizeofLong = 0x8
+ sizeofLongLong = 0x8
+)
+
+type (
+ _C_short int16
+ _C_int int32
+ _C_long int64
+ _C_long_long int64
+)
+
+type Timespec struct {
+ Sec int64
+ Nsec int64
+}
+
+type Timeval struct {
+ Sec int64
+ Usec int64
+}
+
+type Rusage struct {
+ Utime Timeval
+ Stime Timeval
+ Maxrss int64
+ Ixrss int64
+ Idrss int64
+ Isrss int64
+ Minflt int64
+ Majflt int64
+ Nswap int64
+ Inblock int64
+ Oublock int64
+ Msgsnd int64
+ Msgrcv int64
+ Nsignals int64
+ Nvcsw int64
+ Nivcsw int64
+}
+
+type Rlimit struct {
+ Cur int64
+ Max int64
+}
+
+type _Gid_t uint32
+
+const (
+ F_DUPFD_CLOEXEC = 0
+)
+
+const (
+ S_IFMT = 0xf000
+ S_IFIFO = 0x1000
+ S_IFCHR = 0x2000
+ S_IFDIR = 0x4000
+ S_IFBLK = 0x6000
+ S_IFREG = 0x8000
+ S_IFLNK = 0xa000
+ S_IFSOCK = 0xc000
+ S_ISUID = 0x800
+ S_ISGID = 0x400
+ S_ISVTX = 0x200
+ S_IRUSR = 0x100
+ S_IWUSR = 0x80
+ S_IXUSR = 0x40
+)
+
+type Stat_t struct {
+ Ino uint64
+ Nlink uint32
+ Dev uint32
+ Mode uint16
+ Padding1 uint16
+ Uid uint32
+ Gid uint32
+ Rdev uint32
+ Atim Timespec
+ Mtim Timespec
+ Ctim Timespec
+ Size int64
+ Blocks int64
+ Blksize uint32
+ Flags uint32
+ Gen uint32
+ Lspare int32
+ Qspare1 int64
+ Qspare2 int64
+}
+
+type Statfs_t struct {
+ Spare2 int64
+ Bsize int64
+ Iosize int64
+ Blocks int64
+ Bfree int64
+ Bavail int64
+ Files int64
+ Ffree int64
+ Fsid Fsid
+ Owner uint32
+ Type int32
+ Flags int32
+ Pad_cgo_0 [4]byte
+ Syncwrites int64
+ Asyncwrites int64
+ Fstypename [16]int8
+ Mntonname [80]int8
+ Syncreads int64
+ Asyncreads int64
+ Spares1 int16
+ Mntfromname [80]int8
+ Spares2 int16
+ Pad_cgo_1 [4]byte
+ Spare [2]int64
+}
+
+type Flock_t struct {
+ Start int64
+ Len int64
+ Pid int32
+ Type int16
+ Whence int16
+}
+
+type Dirent struct {
+ Fileno uint64
+ Namlen uint16
+ Type uint8
+ Unused1 uint8
+ Unused2 uint32
+ Name [256]int8
+}
+
+type Fsid struct {
+ Val [2]int32
+}
+
+type RawSockaddrInet4 struct {
+ Len uint8
+ Family uint8
+ Port uint16
+ Addr [4]byte /* in_addr */
+ Zero [8]int8
+}
+
+type RawSockaddrInet6 struct {
+ Len uint8
+ Family uint8
+ Port uint16
+ Flowinfo uint32
+ Addr [16]byte /* in6_addr */
+ Scope_id uint32
+}
+
+type RawSockaddrUnix struct {
+ Len uint8
+ Family uint8
+ Path [104]int8
+}
+
+type RawSockaddrDatalink struct {
+ Len uint8
+ Family uint8
+ Index uint16
+ Type uint8
+ Nlen uint8
+ Alen uint8
+ Slen uint8
+ Data [12]int8
+ Rcf uint16
+ Route [16]uint16
+}
+
+type RawSockaddr struct {
+ Len uint8
+ Family uint8
+ Data [14]int8
+}
+
+type RawSockaddrAny struct {
+ Addr RawSockaddr
+ Pad [92]int8
+}
+
+type _Socklen uint32
+
+type Linger struct {
+ Onoff int32
+ Linger int32
+}
+
+type Iovec struct {
+ Base *byte
+ Len uint64
+}
+
+type IPMreq struct {
+ Multiaddr [4]byte /* in_addr */
+ Interface [4]byte /* in_addr */
+}
+
+type IPv6Mreq struct {
+ Multiaddr [16]byte /* in6_addr */
+ Interface uint32
+}
+
+type Msghdr struct {
+ Name *byte
+ Namelen uint32
+ Pad_cgo_0 [4]byte
+ Iov *Iovec
+ Iovlen int32
+ Pad_cgo_1 [4]byte
+ Control *byte
+ Controllen uint32
+ Flags int32
+}
+
+type Cmsghdr struct {
+ Len uint32
+ Level int32
+ Type int32
+}
+
+type Inet6Pktinfo struct {
+ Addr [16]byte /* in6_addr */
+ Ifindex uint32
+}
+
+type IPv6MTUInfo struct {
+ Addr RawSockaddrInet6
+ Mtu uint32
+}
+
+type ICMPv6Filter struct {
+ Filt [8]uint32
+}
+
+const (
+ SizeofSockaddrInet4 = 0x10
+ SizeofSockaddrInet6 = 0x1c
+ SizeofSockaddrAny = 0x6c
+ SizeofSockaddrUnix = 0x6a
+ SizeofSockaddrDatalink = 0x36
+ SizeofLinger = 0x8
+ SizeofIPMreq = 0x8
+ SizeofIPv6Mreq = 0x14
+ SizeofMsghdr = 0x30
+ SizeofCmsghdr = 0xc
+ SizeofInet6Pktinfo = 0x14
+ SizeofIPv6MTUInfo = 0x20
+ SizeofICMPv6Filter = 0x20
+)
+
+const (
+ PTRACE_TRACEME = 0x0
+ PTRACE_CONT = 0x7
+ PTRACE_KILL = 0x8
+)
+
+type Kevent_t struct {
+ Ident uint64
+ Filter int16
+ Flags uint16
+ Fflags uint32
+ Data int64
+ Udata *byte
+}
+
+type FdSet struct {
+ Bits [16]uint64
+}
+
+const (
+ SizeofIfMsghdr = 0xb0
+ SizeofIfData = 0xa0
+ SizeofIfaMsghdr = 0x14
+ SizeofIfmaMsghdr = 0x10
+ SizeofIfAnnounceMsghdr = 0x18
+ SizeofRtMsghdr = 0x98
+ SizeofRtMetrics = 0x70
+)
+
+type IfMsghdr struct {
+ Msglen uint16
+ Version uint8
+ Type uint8
+ Addrs int32
+ Flags int32
+ Index uint16
+ Pad_cgo_0 [2]byte
+ Data IfData
+}
+
+type IfData struct {
+ Type uint8
+ Physical uint8
+ Addrlen uint8
+ Hdrlen uint8
+ Recvquota uint8
+ Xmitquota uint8
+ Pad_cgo_0 [2]byte
+ Mtu uint64
+ Metric uint64
+ Link_state uint64
+ Baudrate uint64
+ Ipackets uint64
+ Ierrors uint64
+ Opackets uint64
+ Oerrors uint64
+ Collisions uint64
+ Ibytes uint64
+ Obytes uint64
+ Imcasts uint64
+ Omcasts uint64
+ Iqdrops uint64
+ Noproto uint64
+ Hwassist uint64
+ Unused uint64
+ Lastchange Timeval
+}
+
+type IfaMsghdr struct {
+ Msglen uint16
+ Version uint8
+ Type uint8
+ Addrs int32
+ Flags int32
+ Index uint16
+ Pad_cgo_0 [2]byte
+ Metric int32
+}
+
+type IfmaMsghdr struct {
+ Msglen uint16
+ Version uint8
+ Type uint8
+ Addrs int32
+ Flags int32
+ Index uint16
+ Pad_cgo_0 [2]byte
+}
+
+type IfAnnounceMsghdr struct {
+ Msglen uint16
+ Version uint8
+ Type uint8
+ Index uint16
+ Name [16]int8
+ What uint16
+}
+
+type RtMsghdr struct {
+ Msglen uint16
+ Version uint8
+ Type uint8
+ Index uint16
+ Pad_cgo_0 [2]byte
+ Flags int32
+ Addrs int32
+ Pid int32
+ Seq int32
+ Errno int32
+ Use int32
+ Inits uint64
+ Rmx RtMetrics
+}
+
+type RtMetrics struct {
+ Locks uint64
+ Mtu uint64
+ Pksent uint64
+ Expire uint64
+ Sendpipe uint64
+ Ssthresh uint64
+ Rtt uint64
+ Rttvar uint64
+ Recvpipe uint64
+ Hopcount uint64
+ Mssopt uint16
+ Pad uint16
+ Pad_cgo_0 [4]byte
+ Msl uint64
+ Iwmaxsegs uint64
+ Iwcapsegs uint64
+}
+
+const (
+ SizeofBpfVersion = 0x4
+ SizeofBpfStat = 0x8
+ SizeofBpfProgram = 0x10
+ SizeofBpfInsn = 0x8
+ SizeofBpfHdr = 0x20
+)
+
+type BpfVersion struct {
+ Major uint16
+ Minor uint16
+}
+
+type BpfStat struct {
+ Recv uint32
+ Drop uint32
+}
+
+type BpfProgram struct {
+ Len uint32
+ Pad_cgo_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_cgo_0 [6]byte
+}
diff --git a/src/pkg/syscall/ztypes_freebsd_386.go b/src/pkg/syscall/ztypes_freebsd_386.go
index 89de58e53..e77bd4b41 100644
--- a/src/pkg/syscall/ztypes_freebsd_386.go
+++ b/src/pkg/syscall/ztypes_freebsd_386.go
@@ -236,6 +236,15 @@ type Inet6Pktinfo struct {
Ifindex uint32
}
+type IPv6MTUInfo struct {
+ Addr RawSockaddrInet6
+ Mtu uint32
+}
+
+type ICMPv6Filter struct {
+ Filt [8]uint32
+}
+
const (
SizeofSockaddrInet4 = 0x10
SizeofSockaddrInet6 = 0x1c
@@ -249,6 +258,8 @@ const (
SizeofMsghdr = 0x1c
SizeofCmsghdr = 0xc
SizeofInet6Pktinfo = 0x14
+ SizeofIPv6MTUInfo = 0x20
+ SizeofICMPv6Filter = 0x20
)
const (
diff --git a/src/pkg/syscall/ztypes_freebsd_amd64.go b/src/pkg/syscall/ztypes_freebsd_amd64.go
index d5e87682d..922de2ce5 100644
--- a/src/pkg/syscall/ztypes_freebsd_amd64.go
+++ b/src/pkg/syscall/ztypes_freebsd_amd64.go
@@ -238,6 +238,15 @@ type Inet6Pktinfo struct {
Ifindex uint32
}
+type IPv6MTUInfo struct {
+ Addr RawSockaddrInet6
+ Mtu uint32
+}
+
+type ICMPv6Filter struct {
+ Filt [8]uint32
+}
+
const (
SizeofSockaddrInet4 = 0x10
SizeofSockaddrInet6 = 0x1c
@@ -251,6 +260,8 @@ const (
SizeofMsghdr = 0x30
SizeofCmsghdr = 0xc
SizeofInet6Pktinfo = 0x14
+ SizeofIPv6MTUInfo = 0x20
+ SizeofICMPv6Filter = 0x20
)
const (
diff --git a/src/pkg/syscall/ztypes_freebsd_arm.go b/src/pkg/syscall/ztypes_freebsd_arm.go
index 4f67a39e6..b1bf83b4c 100644
--- a/src/pkg/syscall/ztypes_freebsd_arm.go
+++ b/src/pkg/syscall/ztypes_freebsd_arm.go
@@ -238,6 +238,15 @@ type Inet6Pktinfo struct {
Ifindex uint32
}
+type IPv6MTUInfo struct {
+ Addr RawSockaddrInet6
+ Mtu uint32
+}
+
+type ICMPv6Filter struct {
+ Filt [8]uint32
+}
+
const (
SizeofSockaddrInet4 = 0x10
SizeofSockaddrInet6 = 0x1c
@@ -251,6 +260,8 @@ const (
SizeofMsghdr = 0x1c
SizeofCmsghdr = 0xc
SizeofInet6Pktinfo = 0x14
+ SizeofIPv6MTUInfo = 0x20
+ SizeofICMPv6Filter = 0x20
)
const (
diff --git a/src/pkg/syscall/ztypes_linux_386.go b/src/pkg/syscall/ztypes_linux_386.go
index e1c30b649..9abd647ac 100644
--- a/src/pkg/syscall/ztypes_linux_386.go
+++ b/src/pkg/syscall/ztypes_linux_386.go
@@ -245,6 +245,15 @@ type Inet6Pktinfo struct {
Ifindex uint32
}
+type IPv6MTUInfo struct {
+ Addr RawSockaddrInet6
+ Mtu uint32
+}
+
+type ICMPv6Filter struct {
+ Data [8]uint32
+}
+
type Ucred struct {
Pid int32
Uid uint32
@@ -300,6 +309,8 @@ const (
SizeofCmsghdr = 0xc
SizeofInet4Pktinfo = 0xc
SizeofInet6Pktinfo = 0x14
+ SizeofIPv6MTUInfo = 0x20
+ SizeofICMPv6Filter = 0x20
SizeofUcred = 0xc
SizeofTCPInfo = 0x68
)
diff --git a/src/pkg/syscall/ztypes_linux_amd64.go b/src/pkg/syscall/ztypes_linux_amd64.go
index 5800c3c6c..32da4e4b5 100644
--- a/src/pkg/syscall/ztypes_linux_amd64.go
+++ b/src/pkg/syscall/ztypes_linux_amd64.go
@@ -247,6 +247,15 @@ type Inet6Pktinfo struct {
Ifindex uint32
}
+type IPv6MTUInfo struct {
+ Addr RawSockaddrInet6
+ Mtu uint32
+}
+
+type ICMPv6Filter struct {
+ Data [8]uint32
+}
+
type Ucred struct {
Pid int32
Uid uint32
@@ -302,6 +311,8 @@ const (
SizeofCmsghdr = 0x10
SizeofInet4Pktinfo = 0xc
SizeofInet6Pktinfo = 0x14
+ SizeofIPv6MTUInfo = 0x20
+ SizeofICMPv6Filter = 0x20
SizeofUcred = 0xc
SizeofTCPInfo = 0x68
)
diff --git a/src/pkg/syscall/ztypes_linux_arm.go b/src/pkg/syscall/ztypes_linux_arm.go
index 4a81d340c..4a918a8a7 100644
--- a/src/pkg/syscall/ztypes_linux_arm.go
+++ b/src/pkg/syscall/ztypes_linux_arm.go
@@ -247,6 +247,15 @@ type Inet6Pktinfo struct {
Ifindex uint32
}
+type IPv6MTUInfo struct {
+ Addr RawSockaddrInet6
+ Mtu uint32
+}
+
+type ICMPv6Filter struct {
+ Data [8]uint32
+}
+
type Ucred struct {
Pid int32
Uid uint32
@@ -302,6 +311,8 @@ const (
SizeofCmsghdr = 0xc
SizeofInet4Pktinfo = 0xc
SizeofInet6Pktinfo = 0x14
+ SizeofIPv6MTUInfo = 0x20
+ SizeofICMPv6Filter = 0x20
SizeofUcred = 0xc
SizeofTCPInfo = 0x68
)
diff --git a/src/pkg/syscall/ztypes_netbsd_386.go b/src/pkg/syscall/ztypes_netbsd_386.go
index dd9cf221a..59314bad2 100644
--- a/src/pkg/syscall/ztypes_netbsd_386.go
+++ b/src/pkg/syscall/ztypes_netbsd_386.go
@@ -185,6 +185,15 @@ type Inet6Pktinfo struct {
Ifindex uint32
}
+type IPv6MTUInfo struct {
+ Addr RawSockaddrInet6
+ Mtu uint32
+}
+
+type ICMPv6Filter struct {
+ Filt [8]uint32
+}
+
const (
SizeofSockaddrInet4 = 0x10
SizeofSockaddrInet6 = 0x1c
@@ -197,6 +206,8 @@ const (
SizeofMsghdr = 0x1c
SizeofCmsghdr = 0xc
SizeofInet6Pktinfo = 0x14
+ SizeofIPv6MTUInfo = 0x20
+ SizeofICMPv6Filter = 0x20
)
const (
diff --git a/src/pkg/syscall/ztypes_netbsd_amd64.go b/src/pkg/syscall/ztypes_netbsd_amd64.go
index 89d1ef816..a021a5738 100644
--- a/src/pkg/syscall/ztypes_netbsd_amd64.go
+++ b/src/pkg/syscall/ztypes_netbsd_amd64.go
@@ -191,6 +191,15 @@ type Inet6Pktinfo struct {
Ifindex uint32
}
+type IPv6MTUInfo struct {
+ Addr RawSockaddrInet6
+ Mtu uint32
+}
+
+type ICMPv6Filter struct {
+ Filt [8]uint32
+}
+
const (
SizeofSockaddrInet4 = 0x10
SizeofSockaddrInet6 = 0x1c
@@ -203,6 +212,8 @@ const (
SizeofMsghdr = 0x30
SizeofCmsghdr = 0xc
SizeofInet6Pktinfo = 0x14
+ SizeofIPv6MTUInfo = 0x20
+ SizeofICMPv6Filter = 0x20
)
const (
diff --git a/src/pkg/syscall/ztypes_netbsd_arm.go b/src/pkg/syscall/ztypes_netbsd_arm.go
index dd9cf221a..59314bad2 100644
--- a/src/pkg/syscall/ztypes_netbsd_arm.go
+++ b/src/pkg/syscall/ztypes_netbsd_arm.go
@@ -185,6 +185,15 @@ type Inet6Pktinfo struct {
Ifindex uint32
}
+type IPv6MTUInfo struct {
+ Addr RawSockaddrInet6
+ Mtu uint32
+}
+
+type ICMPv6Filter struct {
+ Filt [8]uint32
+}
+
const (
SizeofSockaddrInet4 = 0x10
SizeofSockaddrInet6 = 0x1c
@@ -197,6 +206,8 @@ const (
SizeofMsghdr = 0x1c
SizeofCmsghdr = 0xc
SizeofInet6Pktinfo = 0x14
+ SizeofIPv6MTUInfo = 0x20
+ SizeofICMPv6Filter = 0x20
)
const (
diff --git a/src/pkg/syscall/ztypes_openbsd_386.go b/src/pkg/syscall/ztypes_openbsd_386.go
index 528e92c85..3c9cdf28b 100644
--- a/src/pkg/syscall/ztypes_openbsd_386.go
+++ b/src/pkg/syscall/ztypes_openbsd_386.go
@@ -226,6 +226,15 @@ type Inet6Pktinfo struct {
Ifindex uint32
}
+type IPv6MTUInfo struct {
+ Addr RawSockaddrInet6
+ Mtu uint32
+}
+
+type ICMPv6Filter struct {
+ Filt [8]uint32
+}
+
const (
SizeofSockaddrInet4 = 0x10
SizeofSockaddrInet6 = 0x1c
@@ -238,6 +247,8 @@ const (
SizeofMsghdr = 0x1c
SizeofCmsghdr = 0xc
SizeofInet6Pktinfo = 0x14
+ SizeofIPv6MTUInfo = 0x20
+ SizeofICMPv6Filter = 0x20
)
const (
diff --git a/src/pkg/syscall/ztypes_openbsd_amd64.go b/src/pkg/syscall/ztypes_openbsd_amd64.go
index 4b9393054..3a0ac96fa 100644
--- a/src/pkg/syscall/ztypes_openbsd_amd64.go
+++ b/src/pkg/syscall/ztypes_openbsd_amd64.go
@@ -231,6 +231,15 @@ type Inet6Pktinfo struct {
Ifindex uint32
}
+type IPv6MTUInfo struct {
+ Addr RawSockaddrInet6
+ Mtu uint32
+}
+
+type ICMPv6Filter struct {
+ Filt [8]uint32
+}
+
const (
SizeofSockaddrInet4 = 0x10
SizeofSockaddrInet6 = 0x1c
@@ -243,6 +252,8 @@ const (
SizeofMsghdr = 0x30
SizeofCmsghdr = 0xc
SizeofInet6Pktinfo = 0x14
+ SizeofIPv6MTUInfo = 0x20
+ SizeofICMPv6Filter = 0x20
)
const (
diff --git a/src/pkg/syscall/ztypes_windows.go b/src/pkg/syscall/ztypes_windows.go
index a2006f803..bdc15ce3b 100644
--- a/src/pkg/syscall/ztypes_windows.go
+++ b/src/pkg/syscall/ztypes_windows.go
@@ -10,6 +10,7 @@ const (
ERROR_PATH_NOT_FOUND Errno = 3
ERROR_ACCESS_DENIED Errno = 5
ERROR_NO_MORE_FILES Errno = 18
+ ERROR_HANDLE_EOF Errno = 38
ERROR_FILE_EXISTS Errno = 80
ERROR_BROKEN_PIPE Errno = 109
ERROR_BUFFER_OVERFLOW Errno = 111
@@ -21,6 +22,7 @@ const (
ERROR_OPERATION_ABORTED Errno = 995
ERROR_IO_PENDING Errno = 997
ERROR_NOT_FOUND Errno = 1168
+ WSAEACCES Errno = 10013
)
const (
@@ -150,6 +152,7 @@ const (
CREATE_NEW_PROCESS_GROUP = 0x00000200
CREATE_UNICODE_ENVIRONMENT = 0x00000400
+ PROCESS_TERMINATE = 1
PROCESS_QUERY_INFORMATION = 0x00000400
SYNCHRONIZE = 0x00100000
@@ -962,3 +965,69 @@ var WSAID_CONNECTEX = GUID{
0x4660,
[8]byte{0x8e, 0xe9, 0x76, 0xe5, 0x8c, 0x74, 0x06, 0x3e},
}
+
+const (
+ FILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 1
+ FILE_SKIP_SET_EVENT_ON_HANDLE = 2
+)
+
+const (
+ WSAPROTOCOL_LEN = 255
+ MAX_PROTOCOL_CHAIN = 7
+ BASE_PROTOCOL = 1
+ LAYERED_PROTOCOL = 0
+
+ XP1_CONNECTIONLESS = 0x00000001
+ XP1_GUARANTEED_DELIVERY = 0x00000002
+ XP1_GUARANTEED_ORDER = 0x00000004
+ XP1_MESSAGE_ORIENTED = 0x00000008
+ XP1_PSEUDO_STREAM = 0x00000010
+ XP1_GRACEFUL_CLOSE = 0x00000020
+ XP1_EXPEDITED_DATA = 0x00000040
+ XP1_CONNECT_DATA = 0x00000080
+ XP1_DISCONNECT_DATA = 0x00000100
+ XP1_SUPPORT_BROADCAST = 0x00000200
+ XP1_SUPPORT_MULTIPOINT = 0x00000400
+ XP1_MULTIPOINT_CONTROL_PLANE = 0x00000800
+ XP1_MULTIPOINT_DATA_PLANE = 0x00001000
+ XP1_QOS_SUPPORTED = 0x00002000
+ XP1_UNI_SEND = 0x00008000
+ XP1_UNI_RECV = 0x00010000
+ XP1_IFS_HANDLES = 0x00020000
+ XP1_PARTIAL_MESSAGE = 0x00040000
+ XP1_SAN_SUPPORT_SDP = 0x00080000
+
+ PFL_MULTIPLE_PROTO_ENTRIES = 0x00000001
+ PFL_RECOMMENDED_PROTO_ENTRY = 0x00000002
+ PFL_HIDDEN = 0x00000004
+ PFL_MATCHES_PROTOCOL_ZERO = 0x00000008
+ PFL_NETWORKDIRECT_PROVIDER = 0x00000010
+)
+
+type WSAProtocolInfo struct {
+ ServiceFlags1 uint32
+ ServiceFlags2 uint32
+ ServiceFlags3 uint32
+ ServiceFlags4 uint32
+ ProviderFlags uint32
+ ProviderId GUID
+ CatalogEntryId uint32
+ ProtocolChain WSAProtocolChain
+ Version int32
+ AddressFamily int32
+ MaxSockAddr int32
+ MinSockAddr int32
+ SocketType int32
+ Protocol int32
+ ProtocolMaxOffset int32
+ NetworkByteOrder int32
+ SecurityScheme int32
+ MessageSize uint32
+ ProviderReserved uint32
+ ProtocolName [WSAPROTOCOL_LEN + 1]uint16
+}
+
+type WSAProtocolChain struct {
+ ChainLen int32
+ ChainEntries [MAX_PROTOCOL_CHAIN]uint32
+}
diff --git a/src/pkg/testing/allocs.go b/src/pkg/testing/allocs.go
index d142a330b..9ec47bd46 100644
--- a/src/pkg/testing/allocs.go
+++ b/src/pkg/testing/allocs.go
@@ -9,6 +9,7 @@ import (
)
// AllocsPerRun returns the average number of allocations during calls to f.
+// Although the return value has type float64, it will always be an integral value.
//
// To compute the number of allocations, the function will first be run once as
// a warm-up. The average number of allocations over the specified number of
@@ -36,6 +37,9 @@ func AllocsPerRun(runs int, f func()) (avg float64) {
runtime.ReadMemStats(&memstats)
mallocs += memstats.Mallocs
- // Average the mallocs over the runs (not counting the warm-up)
- return float64(mallocs) / float64(runs)
+ // Average the mallocs over the runs (not counting the warm-up).
+ // We are forced to return a float64 because the API is silly, but do
+ // the division as integers so we can ask if AllocsPerRun()==1
+ // instead of AllocsPerRun()<2.
+ return float64(mallocs / uint64(runs))
}
diff --git a/src/pkg/testing/benchmark.go b/src/pkg/testing/benchmark.go
index 25fb2d619..3473c5b2c 100644
--- a/src/pkg/testing/benchmark.go
+++ b/src/pkg/testing/benchmark.go
@@ -138,7 +138,7 @@ func max(x, y int) int {
func roundDown10(n int) int {
var tens = 0
// tens = floor(log_10(n))
- for n > 10 {
+ for n >= 10 {
n = n / 10
tens++
}
@@ -153,13 +153,16 @@ func roundDown10(n int) int {
// roundUp rounds x up to a number of the form [1eX, 2eX, 5eX].
func roundUp(n int) int {
base := roundDown10(n)
- if n < (2 * base) {
+ switch {
+ case n <= base:
+ return base
+ case n <= (2 * base):
return 2 * base
- }
- if n < (5 * base) {
+ case n <= (5 * base):
return 5 * base
+ default:
+ return 10 * base
}
- return 10 * base
}
// run times the benchmark function in a separate goroutine.
diff --git a/src/pkg/testing/benchmark_test.go b/src/pkg/testing/benchmark_test.go
new file mode 100644
index 000000000..94e994dfa
--- /dev/null
+++ b/src/pkg/testing/benchmark_test.go
@@ -0,0 +1,58 @@
+// Copyright 2013 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 testing_test
+
+import (
+ "testing"
+)
+
+var roundDownTests = []struct {
+ v, expected int
+}{
+ {1, 1},
+ {9, 1},
+ {10, 10},
+ {11, 10},
+ {100, 100},
+ {101, 100},
+ {999, 100},
+ {1000, 1000},
+ {1001, 1000},
+}
+
+func TestRoundDown10(t *testing.T) {
+ for _, tt := range roundDownTests {
+ actual := testing.RoundDown10(tt.v)
+ if tt.expected != actual {
+ t.Errorf("roundDown10(%d): expected %d, actual %d", tt.v, tt.expected, actual)
+ }
+ }
+}
+
+var roundUpTests = []struct {
+ v, expected int
+}{
+ {0, 1},
+ {1, 1},
+ {2, 2},
+ {5, 5},
+ {9, 10},
+ {999, 1000},
+ {1000, 1000},
+ {1400, 2000},
+ {1700, 2000},
+ {4999, 5000},
+ {5000, 5000},
+ {5001, 10000},
+}
+
+func TestRoundUp(t *testing.T) {
+ for _, tt := range roundUpTests {
+ actual := testing.RoundUp(tt.v)
+ if tt.expected != actual {
+ t.Errorf("roundUp(%d): expected %d, actual %d", tt.v, tt.expected, actual)
+ }
+ }
+}
diff --git a/src/pkg/testing/cover.go b/src/pkg/testing/cover.go
new file mode 100644
index 000000000..dd29364d8
--- /dev/null
+++ b/src/pkg/testing/cover.go
@@ -0,0 +1,86 @@
+// Copyright 2013 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.
+
+// Support for test coverage.
+
+package testing
+
+import (
+ "fmt"
+ "os"
+)
+
+// CoverBlock records the coverage data for a single basic block.
+// NOTE: This struct is internal to the testing infrastructure and may change.
+// It is not covered (yet) by the Go 1 compatibility guidelines.
+type CoverBlock struct {
+ Line0 uint32
+ Col0 uint16
+ Line1 uint32
+ Col1 uint16
+ Stmts uint16
+}
+
+var cover Cover
+
+// Cover records information about test coverage checking.
+// NOTE: This struct is internal to the testing infrastructure and may change.
+// It is not covered (yet) by the Go 1 compatibility guidelines.
+type Cover struct {
+ Mode string
+ Counters map[string][]uint32
+ Blocks map[string][]CoverBlock
+ CoveredPackages string
+}
+
+// RegisterCover records the coverage data accumulators for the tests.
+// NOTE: This function is internal to the testing infrastructure and may change.
+// It is not covered (yet) by the Go 1 compatibility guidelines.
+func RegisterCover(c Cover) {
+ cover = c
+}
+
+// mustBeNil checks the error and, if present, reports it and exits.
+func mustBeNil(err error) {
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "testing: %s\n", err)
+ os.Exit(2)
+ }
+}
+
+// coverReport reports the coverage percentage and writes a coverage profile if requested.
+func coverReport() {
+ var f *os.File
+ var err error
+ if *coverProfile != "" {
+ f, err = os.Create(toOutputDir(*coverProfile))
+ mustBeNil(err)
+ fmt.Fprintf(f, "mode: %s\n", cover.Mode)
+ defer func() { mustBeNil(f.Close()) }()
+ }
+
+ var active, total int64
+ for name, counts := range cover.Counters {
+ blocks := cover.Blocks[name]
+ for i, count := range counts {
+ stmts := int64(blocks[i].Stmts)
+ total += stmts
+ if count > 0 {
+ active += stmts
+ }
+ if f != nil {
+ _, err := fmt.Fprintf(f, "%s:%d.%d,%d.%d %d %d\n", name,
+ blocks[i].Line0, blocks[i].Col0,
+ blocks[i].Line1, blocks[i].Col1,
+ stmts,
+ count)
+ mustBeNil(err)
+ }
+ }
+ }
+ if total == 0 {
+ total = 1
+ }
+ fmt.Printf("coverage: %.1f%% of statements%s\n", 100*float64(active)/float64(total), cover.CoveredPackages)
+}
diff --git a/src/pkg/testing/export_test.go b/src/pkg/testing/export_test.go
new file mode 100644
index 000000000..89781b439
--- /dev/null
+++ b/src/pkg/testing/export_test.go
@@ -0,0 +1,10 @@
+// Copyright 2013 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 testing
+
+var (
+ RoundDown10 = roundDown10
+ RoundUp = roundUp
+)
diff --git a/src/pkg/testing/quick/quick.go b/src/pkg/testing/quick/quick.go
index 761a6471b..bc79cc329 100644
--- a/src/pkg/testing/quick/quick.go
+++ b/src/pkg/testing/quick/quick.go
@@ -34,7 +34,7 @@ func randFloat32(rand *rand.Rand) float32 {
// randFloat64 generates a random float taking the full range of a float64.
func randFloat64(rand *rand.Rand) float64 {
- f := rand.Float64()
+ f := rand.Float64() * math.MaxFloat64
if rand.Int()&1 == 1 {
f = -f
}
@@ -56,90 +56,88 @@ func Value(t reflect.Type, rand *rand.Rand) (value reflect.Value, ok bool) {
return m.Generate(rand, complexSize), true
}
+ v := reflect.New(t).Elem()
switch concrete := t; concrete.Kind() {
case reflect.Bool:
- return reflect.ValueOf(rand.Int()&1 == 0), true
+ v.SetBool(rand.Int()&1 == 0)
case reflect.Float32:
- return reflect.ValueOf(randFloat32(rand)), true
+ v.SetFloat(float64(randFloat32(rand)))
case reflect.Float64:
- return reflect.ValueOf(randFloat64(rand)), true
+ v.SetFloat(randFloat64(rand))
case reflect.Complex64:
- return reflect.ValueOf(complex(randFloat32(rand), randFloat32(rand))), true
+ v.SetComplex(complex(float64(randFloat32(rand)), float64(randFloat32(rand))))
case reflect.Complex128:
- return reflect.ValueOf(complex(randFloat64(rand), randFloat64(rand))), true
+ v.SetComplex(complex(randFloat64(rand), randFloat64(rand)))
case reflect.Int16:
- return reflect.ValueOf(int16(randInt64(rand))), true
+ v.SetInt(randInt64(rand))
case reflect.Int32:
- return reflect.ValueOf(int32(randInt64(rand))), true
+ v.SetInt(randInt64(rand))
case reflect.Int64:
- return reflect.ValueOf(randInt64(rand)), true
+ v.SetInt(randInt64(rand))
case reflect.Int8:
- return reflect.ValueOf(int8(randInt64(rand))), true
+ v.SetInt(randInt64(rand))
case reflect.Int:
- return reflect.ValueOf(int(randInt64(rand))), true
+ v.SetInt(randInt64(rand))
case reflect.Uint16:
- return reflect.ValueOf(uint16(randInt64(rand))), true
+ v.SetUint(uint64(randInt64(rand)))
case reflect.Uint32:
- return reflect.ValueOf(uint32(randInt64(rand))), true
+ v.SetUint(uint64(randInt64(rand)))
case reflect.Uint64:
- return reflect.ValueOf(uint64(randInt64(rand))), true
+ v.SetUint(uint64(randInt64(rand)))
case reflect.Uint8:
- return reflect.ValueOf(uint8(randInt64(rand))), true
+ v.SetUint(uint64(randInt64(rand)))
case reflect.Uint:
- return reflect.ValueOf(uint(randInt64(rand))), true
+ v.SetUint(uint64(randInt64(rand)))
case reflect.Uintptr:
- return reflect.ValueOf(uintptr(randInt64(rand))), true
+ v.SetUint(uint64(randInt64(rand)))
case reflect.Map:
numElems := rand.Intn(complexSize)
- m := reflect.MakeMap(concrete)
+ v.Set(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)
+ v.SetMapIndex(key, value)
}
- return m, true
case reflect.Ptr:
- v, ok := Value(concrete.Elem(), rand)
+ elem, ok := Value(concrete.Elem(), rand)
if !ok {
return reflect.Value{}, false
}
- p := reflect.New(concrete.Elem())
- p.Elem().Set(v)
- return p, true
+ v.Set(reflect.New(concrete.Elem()))
+ v.Elem().Set(elem)
case reflect.Slice:
numElems := rand.Intn(complexSize)
- s := reflect.MakeSlice(concrete, numElems, numElems)
+ v.Set(reflect.MakeSlice(concrete, numElems, numElems))
for i := 0; i < numElems; i++ {
- v, ok := Value(concrete.Elem(), rand)
+ elem, ok := Value(concrete.Elem(), rand)
if !ok {
return reflect.Value{}, false
}
- s.Index(i).Set(v)
+ v.Index(i).Set(elem)
}
- return s, true
case reflect.String:
numChars := rand.Intn(complexSize)
codePoints := make([]rune, numChars)
for i := 0; i < numChars; i++ {
codePoints[i] = rune(rand.Intn(0x10ffff))
}
- return reflect.ValueOf(string(codePoints)), true
+ v.SetString(string(codePoints))
case reflect.Struct:
- s := reflect.New(t).Elem()
- for i := 0; i < s.NumField(); i++ {
- v, ok := Value(concrete.Field(i).Type, rand)
+ for i := 0; i < v.NumField(); i++ {
+ elem, ok := Value(concrete.Field(i).Type, rand)
if !ok {
return reflect.Value{}, false
}
- s.Field(i).Set(v)
+ v.Field(i).Set(elem)
}
- return s, true
default:
return reflect.Value{}, false
}
+
+ return v, true
}
// A Config structure contains options for running a test.
diff --git a/src/pkg/testing/quick/quick_test.go b/src/pkg/testing/quick/quick_test.go
index a6cf0dc39..e925ba675 100644
--- a/src/pkg/testing/quick/quick_test.go
+++ b/src/pkg/testing/quick/quick_test.go
@@ -12,32 +12,82 @@ import (
func fBool(a bool) bool { return a }
+type TestBoolAlias bool
+
+func fBoolAlias(a TestBoolAlias) TestBoolAlias { return a }
+
func fFloat32(a float32) float32 { return a }
+type TestFloat32Alias float32
+
+func fFloat32Alias(a TestFloat32Alias) TestFloat32Alias { return a }
+
func fFloat64(a float64) float64 { return a }
+type TestFloat64Alias float64
+
+func fFloat64Alias(a TestFloat64Alias) TestFloat64Alias { return a }
+
func fComplex64(a complex64) complex64 { return a }
+type TestComplex64Alias complex64
+
+func fComplex64Alias(a TestComplex64Alias) TestComplex64Alias { return a }
+
func fComplex128(a complex128) complex128 { return a }
+type TestComplex128Alias complex128
+
+func fComplex128Alias(a TestComplex128Alias) TestComplex128Alias { return a }
+
func fInt16(a int16) int16 { return a }
+type TestInt16Alias int16
+
+func fInt16Alias(a TestInt16Alias) TestInt16Alias { return a }
+
func fInt32(a int32) int32 { return a }
+type TestInt32Alias int32
+
+func fInt32Alias(a TestInt32Alias) TestInt32Alias { return a }
+
func fInt64(a int64) int64 { return a }
+type TestInt64Alias int64
+
+func fInt64Alias(a TestInt64Alias) TestInt64Alias { return a }
+
func fInt8(a int8) int8 { return a }
+type TestInt8Alias int8
+
+func fInt8Alias(a TestInt8Alias) TestInt8Alias { return a }
+
func fInt(a int) int { return a }
-func fUInt8(a uint8) uint8 { return a }
+type TestIntAlias int
+
+func fIntAlias(a TestIntAlias) TestIntAlias { return a }
func fMap(a map[int]int) map[int]int { return a }
+type TestMapAlias map[int]int
+
+func fMapAlias(a TestMapAlias) TestMapAlias { return a }
+
func fSlice(a []byte) []byte { return a }
+type TestSliceAlias []byte
+
+func fSliceAlias(a TestSliceAlias) TestSliceAlias { return a }
+
func fString(a string) string { return a }
+type TestStringAlias string
+
+func fStringAlias(a TestStringAlias) TestStringAlias { return a }
+
type TestStruct struct {
A int
B string
@@ -45,23 +95,55 @@ type TestStruct struct {
func fStruct(a TestStruct) TestStruct { return a }
+type TestStructAlias TestStruct
+
+func fStructAlias(a TestStructAlias) TestStructAlias { return a }
+
func fUint16(a uint16) uint16 { return a }
+type TestUint16Alias uint16
+
+func fUint16Alias(a TestUint16Alias) TestUint16Alias { return a }
+
func fUint32(a uint32) uint32 { return a }
+type TestUint32Alias uint32
+
+func fUint32Alias(a TestUint32Alias) TestUint32Alias { return a }
+
func fUint64(a uint64) uint64 { return a }
+type TestUint64Alias uint64
+
+func fUint64Alias(a TestUint64Alias) TestUint64Alias { return a }
+
func fUint8(a uint8) uint8 { return a }
+type TestUint8Alias uint8
+
+func fUint8Alias(a TestUint8Alias) TestUint8Alias { return a }
+
func fUint(a uint) uint { return a }
+type TestUintAlias uint
+
+func fUintAlias(a TestUintAlias) TestUintAlias { return a }
+
func fUintptr(a uintptr) uintptr { return a }
+type TestUintptrAlias uintptr
+
+func fUintptrAlias(a TestUintptrAlias) TestUintptrAlias { return a }
+
func fIntptr(a *int) *int {
b := *a
return &b
}
+type TestIntptrAlias *int
+
+func fIntptrAlias(a TestIntptrAlias) TestIntptrAlias { return a }
+
func reportError(property string, err error, t *testing.T) {
if err != nil {
t.Errorf("%s: %s", property, err)
@@ -70,28 +152,49 @@ func reportError(property string, err error, t *testing.T) {
func TestCheckEqual(t *testing.T) {
reportError("fBool", CheckEqual(fBool, fBool, nil), t)
+ reportError("fBoolAlias", CheckEqual(fBoolAlias, fBoolAlias, nil), t)
reportError("fFloat32", CheckEqual(fFloat32, fFloat32, nil), t)
+ reportError("fFloat32Alias", CheckEqual(fFloat32Alias, fFloat32Alias, nil), t)
reportError("fFloat64", CheckEqual(fFloat64, fFloat64, nil), t)
+ reportError("fFloat64Alias", CheckEqual(fFloat64Alias, fFloat64Alias, nil), t)
reportError("fComplex64", CheckEqual(fComplex64, fComplex64, nil), t)
+ reportError("fComplex64Alias", CheckEqual(fComplex64Alias, fComplex64Alias, nil), t)
reportError("fComplex128", CheckEqual(fComplex128, fComplex128, nil), t)
+ reportError("fComplex128Alias", CheckEqual(fComplex128Alias, fComplex128Alias, nil), t)
reportError("fInt16", CheckEqual(fInt16, fInt16, nil), t)
+ reportError("fInt16Alias", CheckEqual(fInt16Alias, fInt16Alias, nil), t)
reportError("fInt32", CheckEqual(fInt32, fInt32, nil), t)
+ reportError("fInt32Alias", CheckEqual(fInt32Alias, fInt32Alias, nil), t)
reportError("fInt64", CheckEqual(fInt64, fInt64, nil), t)
+ reportError("fInt64Alias", CheckEqual(fInt64Alias, fInt64Alias, nil), t)
reportError("fInt8", CheckEqual(fInt8, fInt8, nil), t)
+ reportError("fInt8Alias", CheckEqual(fInt8Alias, fInt8Alias, nil), t)
reportError("fInt", CheckEqual(fInt, fInt, nil), t)
- reportError("fUInt8", CheckEqual(fUInt8, fUInt8, nil), t)
+ reportError("fIntAlias", CheckEqual(fIntAlias, fIntAlias, nil), t)
reportError("fInt32", CheckEqual(fInt32, fInt32, nil), t)
+ reportError("fInt32Alias", CheckEqual(fInt32Alias, fInt32Alias, nil), t)
reportError("fMap", CheckEqual(fMap, fMap, nil), t)
+ reportError("fMapAlias", CheckEqual(fMapAlias, fMapAlias, nil), t)
reportError("fSlice", CheckEqual(fSlice, fSlice, nil), t)
+ reportError("fSliceAlias", CheckEqual(fSliceAlias, fSliceAlias, nil), t)
reportError("fString", CheckEqual(fString, fString, nil), t)
+ reportError("fStringAlias", CheckEqual(fStringAlias, fStringAlias, nil), t)
reportError("fStruct", CheckEqual(fStruct, fStruct, nil), t)
+ reportError("fStructAlias", CheckEqual(fStructAlias, fStructAlias, nil), t)
reportError("fUint16", CheckEqual(fUint16, fUint16, nil), t)
+ reportError("fUint16Alias", CheckEqual(fUint16Alias, fUint16Alias, nil), t)
reportError("fUint32", CheckEqual(fUint32, fUint32, nil), t)
+ reportError("fUint32Alias", CheckEqual(fUint32Alias, fUint32Alias, nil), t)
reportError("fUint64", CheckEqual(fUint64, fUint64, nil), t)
+ reportError("fUint64Alias", CheckEqual(fUint64Alias, fUint64Alias, nil), t)
reportError("fUint8", CheckEqual(fUint8, fUint8, nil), t)
+ reportError("fUint8Alias", CheckEqual(fUint8Alias, fUint8Alias, nil), t)
reportError("fUint", CheckEqual(fUint, fUint, nil), t)
+ reportError("fUintAlias", CheckEqual(fUintAlias, fUintAlias, nil), t)
reportError("fUintptr", CheckEqual(fUintptr, fUintptr, nil), t)
+ reportError("fUintptrAlias", CheckEqual(fUintptrAlias, fUintptrAlias, nil), t)
reportError("fIntptr", CheckEqual(fIntptr, fIntptr, nil), t)
+ reportError("fIntptrAlias", CheckEqual(fIntptrAlias, fIntptrAlias, nil), t)
}
// This tests that ArbitraryValue is working by checking that all the arbitrary
diff --git a/src/pkg/testing/testing.go b/src/pkg/testing/testing.go
index 312d28732..52dc166dd 100644
--- a/src/pkg/testing/testing.go
+++ b/src/pkg/testing/testing.go
@@ -23,10 +23,10 @@
// Functions of the form
// func BenchmarkXxx(*testing.B)
// are considered benchmarks, and are executed by the "go test" command when
-// the -test.bench flag is provided. Benchmarks are run sequentially.
+// its -bench flag is provided. Benchmarks are run sequentially.
//
// For a description of the testing flags, see
-// http://golang.org/cmd/go/#Description_of_testing_flags.
+// http://golang.org/cmd/go/#hdr-Description_of_testing_flags.
//
// A sample benchmark function looks like this:
// func BenchmarkHello(b *testing.B) {
@@ -73,17 +73,19 @@
//
// Example functions without output comments are compiled but not executed.
//
-// The naming convention to declare examples for a function F, a type T and
+// The naming convention to declare examples for the package, a function F, a type T and
// method M on type T are:
//
+// func Example() { ... }
// func ExampleF() { ... }
// func ExampleT() { ... }
// func ExampleT_M() { ... }
//
-// Multiple example functions for a type/function/method may be provided by
+// Multiple example functions for a package/type/function/method may be provided by
// appending a distinct suffix to the name. The suffix must start with a
// lower-case letter.
//
+// func Example_suffix() { ... }
// func ExampleF_suffix() { ... }
// func ExampleT_suffix() { ... }
// func ExampleT_M_suffix() { ... }
@@ -114,8 +116,15 @@ var (
// full test of the package.
short = flag.Bool("test.short", false, "run smaller test suite to save time")
+ // The directory in which to create profile files and the like. When run from
+ // "go test", the binary always runs in the source directory for the package;
+ // this flag lets "go test" tell the binary to write the files in the directory where
+ // the "go test" command is run.
+ outputDir = flag.String("test.outputdir", "", "directory in which to write profiles")
+
// Report as tests are run; default is silent for success.
chatty = flag.Bool("test.v", false, "verbose: print additional output")
+ coverProfile = flag.String("test.coverprofile", "", "write a coverage profile to the named file after execution")
match = flag.String("test.run", "", "regular expression to select tests and examples 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")
@@ -189,6 +198,31 @@ func decorate(s string) string {
return buf.String()
}
+// TB is the interface common to T and B.
+type TB interface {
+ Error(args ...interface{})
+ Errorf(format string, args ...interface{})
+ Fail()
+ FailNow()
+ Failed() bool
+ Fatal(args ...interface{})
+ Fatalf(format string, args ...interface{})
+ Log(args ...interface{})
+ Logf(format string, args ...interface{})
+ Skip(args ...interface{})
+ SkipNow()
+ Skipf(format string, args ...interface{})
+ Skipped() bool
+
+ // A private method to prevent users implementing the
+ // interface and so future additions to it will not
+ // violate Go 1 compatibility.
+ private()
+}
+
+var _ TB = (*T)(nil)
+var _ TB = (*B)(nil)
+
// T is a type passed to Test functions to manage test state and support formatted test logs.
// Logs are accumulated during execution and dumped to standard error when done.
type T struct {
@@ -197,6 +231,8 @@ type T struct {
startParallel chan bool // Parallel tests will wait on this.
}
+func (c *common) private() {}
+
// Fail marks the function as having failed but continues execution.
func (c *common) Fail() {
c.mu.Lock()
@@ -323,6 +359,9 @@ func (c *common) Skipped() bool {
func (t *T) Parallel() {
t.signal <- (*T)(nil) // Release main testing loop
<-t.startParallel // Wait for serial tests to finish
+ // Assuming Parallel is the first thing a test does, which is reasonable,
+ // reinitialize the test's start time because it's actually starting now.
+ t.start = time.Now()
}
// An internal type but exported because it is cross-package; part of the implementation
@@ -333,8 +372,6 @@ type InternalTest struct {
}
func tRunner(t *T, test *InternalTest) {
- t.start = time.Now()
-
// When this goroutine is done, either because test.F(t)
// returned normally or because a test failure triggered
// a call to runtime.Goexit, record the duration and send
@@ -350,6 +387,7 @@ func tRunner(t *T, test *InternalTest) {
t.signal <- t
}()
+ t.start = time.Now()
test.F(t)
}
@@ -364,12 +402,12 @@ func Main(matchString func(pat, str string) (bool, error), tests []InternalTest,
haveExamples = len(examples) > 0
testOk := RunTests(matchString, tests)
exampleOk := RunExamples(matchString, examples)
+ stopAlarm()
if !testOk || !exampleOk {
fmt.Println("FAIL")
os.Exit(1)
}
fmt.Println("PASS")
- stopAlarm()
RunBenchmarks(matchString, benchmarks)
after()
}
@@ -466,7 +504,7 @@ func before() {
runtime.MemProfileRate = *memProfileRate
}
if *cpuProfile != "" {
- f, err := os.Create(*cpuProfile)
+ f, err := os.Create(toOutputDir(*cpuProfile))
if err != nil {
fmt.Fprintf(os.Stderr, "testing: %s", err)
return
@@ -481,6 +519,10 @@ func before() {
if *blockProfile != "" && *blockProfileRate >= 0 {
runtime.SetBlockProfileRate(*blockProfileRate)
}
+ if *coverProfile != "" && cover.Mode == "" {
+ fmt.Fprintf(os.Stderr, "testing: cannot use -test.coverprofile because test binary was not built with coverage enabled\n")
+ os.Exit(2)
+ }
}
// after runs after all testing.
@@ -489,27 +531,60 @@ func after() {
pprof.StopCPUProfile() // flushes profile to disk
}
if *memProfile != "" {
- f, err := os.Create(*memProfile)
+ f, err := os.Create(toOutputDir(*memProfile))
if err != nil {
- fmt.Fprintf(os.Stderr, "testing: %s", err)
- return
+ fmt.Fprintf(os.Stderr, "testing: %s\n", err)
+ os.Exit(2)
}
if err = pprof.WriteHeapProfile(f); err != nil {
- fmt.Fprintf(os.Stderr, "testing: can't write %s: %s", *memProfile, err)
+ fmt.Fprintf(os.Stderr, "testing: can't write %s: %s\n", *memProfile, err)
+ os.Exit(2)
}
f.Close()
}
if *blockProfile != "" && *blockProfileRate >= 0 {
- f, err := os.Create(*blockProfile)
+ f, err := os.Create(toOutputDir(*blockProfile))
if err != nil {
- fmt.Fprintf(os.Stderr, "testing: %s", err)
- return
+ fmt.Fprintf(os.Stderr, "testing: %s\n", err)
+ os.Exit(2)
}
if err = pprof.Lookup("block").WriteTo(f, 0); err != nil {
- fmt.Fprintf(os.Stderr, "testing: can't write %s: %s", *blockProfile, err)
+ fmt.Fprintf(os.Stderr, "testing: can't write %s: %s\n", *blockProfile, err)
+ os.Exit(2)
}
f.Close()
}
+ if cover.Mode != "" {
+ coverReport()
+ }
+}
+
+// toOutputDir returns the file name relocated, if required, to outputDir.
+// Simple implementation to avoid pulling in path/filepath.
+func toOutputDir(path string) string {
+ if *outputDir == "" || path == "" {
+ return path
+ }
+ if runtime.GOOS == "windows" {
+ // On Windows, it's clumsy, but we can be almost always correct
+ // by just looking for a drive letter and a colon.
+ // Absolute paths always have a drive letter (ignoring UNC).
+ // Problem: if path == "C:A" and outputdir == "C:\Go" it's unclear
+ // what to do, but even then path/filepath doesn't help.
+ // TODO: Worth doing better? Probably not, because we're here only
+ // under the management of go test.
+ if len(path) >= 2 {
+ letter, colon := path[0], path[1]
+ if ('a' <= letter && letter <= 'z' || 'A' <= letter && letter <= 'Z') && colon == ':' {
+ // If path starts with a drive letter we're stuck with it regardless.
+ return path
+ }
+ }
+ }
+ if os.IsPathSeparator(path[0]) {
+ return path
+ }
+ return fmt.Sprintf("%s%c%s", *outputDir, os.PathSeparator, path)
}
var timer *time.Timer
@@ -517,7 +592,9 @@ var timer *time.Timer
// startAlarm starts an alarm if requested.
func startAlarm() {
if *timeout > 0 {
- timer = time.AfterFunc(*timeout, alarm)
+ timer = time.AfterFunc(*timeout, func() {
+ panic(fmt.Sprintf("test timed out after %v", *timeout))
+ })
}
}
@@ -528,22 +605,20 @@ func stopAlarm() {
}
}
-// alarm is called if the timeout expires.
-func alarm() {
- panic("test timed out")
-}
-
func parseCpuList() {
- if len(*cpuListStr) == 0 {
- cpuList = append(cpuList, runtime.GOMAXPROCS(-1))
- } else {
- for _, val := range strings.Split(*cpuListStr, ",") {
- cpu, err := strconv.Atoi(val)
- if err != nil || cpu <= 0 {
- fmt.Fprintf(os.Stderr, "testing: invalid value %q for -test.cpu", val)
- os.Exit(1)
- }
- cpuList = append(cpuList, cpu)
+ for _, val := range strings.Split(*cpuListStr, ",") {
+ val = strings.TrimSpace(val)
+ if val == "" {
+ continue
+ }
+ cpu, err := strconv.Atoi(val)
+ if err != nil || cpu <= 0 {
+ fmt.Fprintf(os.Stderr, "testing: invalid value %q for -test.cpu\n", val)
+ os.Exit(1)
}
+ cpuList = append(cpuList, cpu)
+ }
+ if cpuList == nil {
+ cpuList = append(cpuList, runtime.GOMAXPROCS(-1))
}
}
diff --git a/src/pkg/text/template/doc.go b/src/pkg/text/template/doc.go
index 2da339ce8..f622ac7dc 100644
--- a/src/pkg/text/template/doc.go
+++ b/src/pkg/text/template/doc.go
@@ -44,7 +44,8 @@ data, defined in detail below.
*/
// {{/* a comment */}}
// A comment; discarded. May contain newlines.
-// Comments do not nest.
+// Comments do not nest and must start and end at the
+// delimiters, as shown here.
/*
{{pipeline}}
@@ -62,6 +63,12 @@ data, defined in detail below.
If the value of the pipeline is empty, T0 is executed;
otherwise, T1 is executed. Dot is unaffected.
+ {{if pipeline}} T1 {{else if pipeline}} T0 {{end}}
+ To simplify the appearance of if-else chains, the else action
+ of an if may include another if directly; the effect is exactly
+ the same as writing
+ {{if pipeline}} T1 {{else}}{{if pipeline}} T0 {{end}}{{end}}
+
{{range pipeline}} T1 {{end}}
The value of the pipeline must be an array, slice, map, or channel.
If the value of the pipeline has length zero, nothing is output;
@@ -300,8 +307,41 @@ Predefined global functions are named as follows.
Returns the escaped value of the textual representation of
its arguments in a form suitable for embedding in a URL query.
-The boolean functions take any zero value to be false and a non-zero value to
-be true.
+The boolean functions take any zero value to be false and a non-zero
+value to be true.
+
+There is also a set of binary comparison operators defined as
+functions:
+
+ eq
+ Returns the boolean truth of arg1 == arg2
+ ne
+ Returns the boolean truth of arg1 != arg2
+ lt
+ Returns the boolean truth of arg1 < arg2
+ le
+ Returns the boolean truth of arg1 <= arg2
+ gt
+ Returns the boolean truth of arg1 > arg2
+ ge
+ Returns the boolean truth of arg1 >= arg2
+
+For simpler multi-way equality tests, eq (only) accepts two or more
+arguments and compares the second and subsequent to the first,
+returning in effect
+
+ arg1==arg2 || arg1==arg3 || arg1==arg4 ...
+
+(Unlike with || in Go, however, eq is a function call and all the
+arguments will be evaluated.)
+
+The comparison functions work on basic types only (or named basic
+types, such as "type Celsius float32"). They implement the Go rules
+for comparison of values, except that size and exact type are
+ignored, so any integer value may be compared with any other integer
+value, any unsigned integer value may be compared with any other
+unsigned integer value, and so on. However, as usual, one may not
+compare an int with a float32 and so on.
Associated templates
diff --git a/src/pkg/text/template/exec.go b/src/pkg/text/template/exec.go
index 8ec8174a1..43b0b266e 100644
--- a/src/pkg/text/template/exec.go
+++ b/src/pkg/text/template/exec.go
@@ -201,7 +201,7 @@ func (s *state) walkIfOrWith(typ parse.NodeType, dot reflect.Value, pipe *parse.
}
}
-// isTrue returns whether the value is 'true', in the sense of not the zero of its type,
+// isTrue reports whether the value is 'true', in the sense of not the zero of its type,
// and whether the value has a meaningful truth value.
func isTrue(val reflect.Value) (truth, ok bool) {
if !val.IsValid() {
@@ -755,12 +755,21 @@ func indirect(v reflect.Value) (rv reflect.Value, isNil bool) {
// the template.
func (s *state) printValue(n parse.Node, v reflect.Value) {
s.at(n)
+ iface, ok := printableValue(v)
+ if !ok {
+ s.errorf("can't print %s of type %s", n, v.Type())
+ }
+ fmt.Fprint(s.wr, iface)
+}
+
+// printableValue returns the, possibly indirected, interface value inside v that
+// is best for a call to formatted printer.
+func printableValue(v reflect.Value) (interface{}, bool) {
if v.Kind() == reflect.Ptr {
v, _ = indirect(v) // fmt.Fprint handles nil.
}
if !v.IsValid() {
- fmt.Fprint(s.wr, "<no value>")
- return
+ return "<no value>", true
}
if !v.Type().Implements(errorType) && !v.Type().Implements(fmtStringerType) {
@@ -769,11 +778,11 @@ func (s *state) printValue(n parse.Node, v reflect.Value) {
} else {
switch v.Kind() {
case reflect.Chan, reflect.Func:
- s.errorf("can't print %s of type %s", n, v.Type())
+ return nil, false
}
}
}
- fmt.Fprint(s.wr, v.Interface())
+ return v.Interface(), true
}
// Types to help sort the keys in a map for reproducible output.
diff --git a/src/pkg/text/template/exec_test.go b/src/pkg/text/template/exec_test.go
index 0ab20acc9..f60702de8 100644
--- a/src/pkg/text/template/exec_test.go
+++ b/src/pkg/text/template/exec_test.go
@@ -24,7 +24,7 @@ type T struct {
U16 uint16
X string
FloatZero float64
- ComplexZero float64
+ ComplexZero complex128
// Nested structs.
U *U
// Struct with String method.
@@ -57,6 +57,7 @@ type T struct {
Err error
// Pointers
PI *int
+ PS *string
PSI *[]int
NIL *int
// Function (not method)
@@ -64,6 +65,7 @@ type T struct {
VariadicFunc func(...string) string
VariadicFuncInt func(int, ...string) string
NilOKFunc func(*int) bool
+ ErrFunc func() (string, error)
// Template to test evaluation of templates.
Tmpl *Template
// Unexported field; cannot be accessed by template.
@@ -124,11 +126,13 @@ var tVal = &T{
Str: bytes.NewBuffer([]byte("foozle")),
Err: errors.New("erroozle"),
PI: newInt(23),
+ PS: newString("a string"),
PSI: newIntSlice(21, 22, 23),
BinaryFunc: func(a, b string) string { return fmt.Sprintf("[%s=%s]", a, b) },
VariadicFunc: func(s ...string) string { return fmt.Sprint("<", strings.Join(s, "+"), ">") },
VariadicFuncInt: func(a int, s ...string) string { return fmt.Sprint(a, "=<", strings.Join(s, "+"), ">") },
NilOKFunc: func(s *int) bool { return s == nil },
+ ErrFunc: func() (string, error) { return "bla", nil },
Tmpl: Must(New("x").Parse("test template")), // "x" is the value of .X
}
@@ -141,9 +145,11 @@ var iVal I = tVal
// Helpers for creation.
func newInt(n int) *int {
- p := new(int)
- *p = n
- return p
+ return &n
+}
+
+func newString(s string) *string {
+ return &s
}
func newIntSlice(n ...int) *[]int {
@@ -280,6 +286,7 @@ var execTests = []execTest{
// Pointers.
{"*int", "{{.PI}}", "23", tVal, true},
+ {"*string", "{{.PS}}", "a string", tVal, true},
{"*[]int", "{{.PSI}}", "[21 22 23]", tVal, true},
{"*[]int[1]", "{{index .PSI 1}}", "22", tVal, true},
{"NIL", "{{.NIL}}", "<nil>", tVal, true},
@@ -322,6 +329,7 @@ var execTests = []execTest{
{"if .BinaryFunc call", "{{ if .BinaryFunc}}{{call .BinaryFunc `1` `2`}}{{end}}", "[1=2]", tVal, true},
{"if not .BinaryFunc call", "{{ if not .BinaryFunc}}{{call .BinaryFunc `1` `2`}}{{else}}No{{end}}", "No", tVal, true},
{"Interface Call", `{{stringer .S}}`, "foozle", map[string]interface{}{"S": bytes.NewBufferString("foozle")}, true},
+ {".ErrFunc", "{{call .ErrFunc}}", "bla", tVal, true},
// Erroneous function calls (check args).
{".BinaryFuncTooFew", "{{call .BinaryFunc `1`}}", "", tVal, false},
@@ -366,6 +374,8 @@ var execTests = []execTest{
{"if map not unset", "{{if not .MXI.none}}ZERO{{else}}NON-ZERO{{end}}", "ZERO", tVal, true},
{"if $x with $y int", "{{if $x := true}}{{with $y := .I}}{{$x}},{{$y}}{{end}}{{end}}", "true,17", tVal, true},
{"if $x with $x int", "{{if $x := true}}{{with $x := .I}}{{$x}},{{end}}{{$x}}{{end}}", "17,true", tVal, true},
+ {"if else if", "{{if false}}FALSE{{else if true}}TRUE{{end}}", "TRUE", tVal, true},
+ {"if else chain", "{{if eq 1 3}}1{{else if eq 2 3}}2{{else if eq 3 3}}3{{end}}", "3", tVal, true},
// Print etc.
{"print", `{{print "hello, print"}}`, "hello, print", tVal, true},
@@ -388,6 +398,7 @@ var execTests = []execTest{
"&lt;script&gt;alert(&#34;XSS&#34;);&lt;/script&gt;", nil, true},
{"html pipeline", `{{printf "<script>alert(\"XSS\");</script>" | html}}`,
"&lt;script&gt;alert(&#34;XSS&#34;);&lt;/script&gt;", nil, true},
+ {"html", `{{html .PS}}`, "a string", tVal, true},
// JavaScript.
{"js", `{{js .}}`, `It\'d be nice.`, `It'd be nice.`, true},
@@ -860,3 +871,111 @@ func TestMessageForExecuteEmpty(t *testing.T) {
t.Fatal(err)
}
}
+
+type cmpTest struct {
+ expr string
+ truth string
+ ok bool
+}
+
+var cmpTests = []cmpTest{
+ {"eq true true", "true", true},
+ {"eq true false", "false", true},
+ {"eq 1+2i 1+2i", "true", true},
+ {"eq 1+2i 1+3i", "false", true},
+ {"eq 1.5 1.5", "true", true},
+ {"eq 1.5 2.5", "false", true},
+ {"eq 1 1", "true", true},
+ {"eq 1 2", "false", true},
+ {"eq `xy` `xy`", "true", true},
+ {"eq `xy` `xyz`", "false", true},
+ {"eq .Xuint .Xuint", "true", true},
+ {"eq .Xuint .Yuint", "false", true},
+ {"eq 3 4 5 6 3", "true", true},
+ {"eq 3 4 5 6 7", "false", true},
+ {"ne true true", "false", true},
+ {"ne true false", "true", true},
+ {"ne 1+2i 1+2i", "false", true},
+ {"ne 1+2i 1+3i", "true", true},
+ {"ne 1.5 1.5", "false", true},
+ {"ne 1.5 2.5", "true", true},
+ {"ne 1 1", "false", true},
+ {"ne 1 2", "true", true},
+ {"ne `xy` `xy`", "false", true},
+ {"ne `xy` `xyz`", "true", true},
+ {"ne .Xuint .Xuint", "false", true},
+ {"ne .Xuint .Yuint", "true", true},
+ {"lt 1.5 1.5", "false", true},
+ {"lt 1.5 2.5", "true", true},
+ {"lt 1 1", "false", true},
+ {"lt 1 2", "true", true},
+ {"lt `xy` `xy`", "false", true},
+ {"lt `xy` `xyz`", "true", true},
+ {"lt .Xuint .Xuint", "false", true},
+ {"lt .Xuint .Yuint", "true", true},
+ {"le 1.5 1.5", "true", true},
+ {"le 1.5 2.5", "true", true},
+ {"le 2.5 1.5", "false", true},
+ {"le 1 1", "true", true},
+ {"le 1 2", "true", true},
+ {"le 2 1", "false", true},
+ {"le `xy` `xy`", "true", true},
+ {"le `xy` `xyz`", "true", true},
+ {"le `xyz` `xy`", "false", true},
+ {"le .Xuint .Xuint", "true", true},
+ {"le .Xuint .Yuint", "true", true},
+ {"le .Yuint .Xuint", "false", true},
+ {"gt 1.5 1.5", "false", true},
+ {"gt 1.5 2.5", "false", true},
+ {"gt 1 1", "false", true},
+ {"gt 2 1", "true", true},
+ {"gt 1 2", "false", true},
+ {"gt `xy` `xy`", "false", true},
+ {"gt `xy` `xyz`", "false", true},
+ {"gt .Xuint .Xuint", "false", true},
+ {"gt .Xuint .Yuint", "false", true},
+ {"gt .Yuint .Xuint", "true", true},
+ {"ge 1.5 1.5", "true", true},
+ {"ge 1.5 2.5", "false", true},
+ {"ge 2.5 1.5", "true", true},
+ {"ge 1 1", "true", true},
+ {"ge 1 2", "false", true},
+ {"ge 2 1", "true", true},
+ {"ge `xy` `xy`", "true", true},
+ {"ge `xy` `xyz`", "false", true},
+ {"ge `xyz` `xy`", "true", true},
+ {"ge .Xuint .Xuint", "true", true},
+ {"ge .Xuint .Yuint", "false", true},
+ {"ge .Yuint .Xuint", "true", true},
+ // Errors
+ {"eq `xy` 1", "", false}, // Different types.
+ {"lt true true", "", false}, // Unordered types.
+ {"lt 1+0i 1+0i", "", false}, // Unordered types.
+}
+
+func TestComparison(t *testing.T) {
+ b := new(bytes.Buffer)
+ var cmpStruct = struct {
+ Xuint, Yuint uint
+ }{3, 4}
+ for _, test := range cmpTests {
+ text := fmt.Sprintf("{{if %s}}true{{else}}false{{end}}", test.expr)
+ tmpl, err := New("empty").Parse(text)
+ if err != nil {
+ t.Fatal(err)
+ }
+ b.Reset()
+ err = tmpl.Execute(b, &cmpStruct)
+ if test.ok && err != nil {
+ t.Errorf("%s errored incorrectly: %s", test.expr, err)
+ continue
+ }
+ if !test.ok && err == nil {
+ t.Errorf("%s did not error", test.expr)
+ continue
+ }
+ if b.String() != test.truth {
+ t.Errorf("%s: want %s; got %s", test.expr, test.truth, b.String())
+ }
+ }
+}
diff --git a/src/pkg/text/template/funcs.go b/src/pkg/text/template/funcs.go
index 818766364..e85412262 100644
--- a/src/pkg/text/template/funcs.go
+++ b/src/pkg/text/template/funcs.go
@@ -6,6 +6,7 @@ package template
import (
"bytes"
+ "errors"
"fmt"
"io"
"net/url"
@@ -35,6 +36,14 @@ var builtins = FuncMap{
"printf": fmt.Sprintf,
"println": fmt.Sprintln,
"urlquery": URLQueryEscaper,
+
+ // Comparisons
+ "eq": eq, // ==
+ "ge": ge, // >=
+ "gt": gt, // >
+ "le": le, // <=
+ "lt": lt, // <
+ "ne": ne, // !=
}
var builtinFuncs = createValueFuncs(builtins)
@@ -199,7 +208,7 @@ func call(fn interface{}, args ...interface{}) (interface{}, error) {
argv[i] = value
}
result := v.Call(argv)
- if len(result) == 2 {
+ if len(result) == 2 && !result[1].IsNil() {
return result[0].Interface(), result[1].Interface().(error)
}
return result[0].Interface(), nil
@@ -248,6 +257,160 @@ func not(arg interface{}) (truth bool) {
return !truth
}
+// Comparison.
+
+// TODO: Perhaps allow comparison between signed and unsigned integers.
+
+var (
+ errBadComparisonType = errors.New("invalid type for comparison")
+ errBadComparison = errors.New("incompatible types for comparison")
+ errNoComparison = errors.New("missing argument for comparison")
+)
+
+type kind int
+
+const (
+ invalidKind kind = iota
+ boolKind
+ complexKind
+ intKind
+ floatKind
+ integerKind
+ stringKind
+ uintKind
+)
+
+func basicKind(v reflect.Value) (kind, error) {
+ switch v.Kind() {
+ case reflect.Bool:
+ return boolKind, nil
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return intKind, nil
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ return uintKind, nil
+ case reflect.Float32, reflect.Float64:
+ return floatKind, nil
+ case reflect.Complex64, reflect.Complex128:
+ return complexKind, nil
+ case reflect.String:
+ return stringKind, nil
+ }
+ return invalidKind, errBadComparisonType
+}
+
+// eq evaluates the comparison a == b || a == c || ...
+func eq(arg1 interface{}, arg2 ...interface{}) (bool, error) {
+ v1 := reflect.ValueOf(arg1)
+ k1, err := basicKind(v1)
+ if err != nil {
+ return false, err
+ }
+ if len(arg2) == 0 {
+ return false, errNoComparison
+ }
+ for _, arg := range arg2 {
+ v2 := reflect.ValueOf(arg)
+ k2, err := basicKind(v2)
+ if err != nil {
+ return false, err
+ }
+ if k1 != k2 {
+ return false, errBadComparison
+ }
+ truth := false
+ switch k1 {
+ case boolKind:
+ truth = v1.Bool() == v2.Bool()
+ case complexKind:
+ truth = v1.Complex() == v2.Complex()
+ case floatKind:
+ truth = v1.Float() == v2.Float()
+ case intKind:
+ truth = v1.Int() == v2.Int()
+ case stringKind:
+ truth = v1.String() == v2.String()
+ case uintKind:
+ truth = v1.Uint() == v2.Uint()
+ default:
+ panic("invalid kind")
+ }
+ if truth {
+ return true, nil
+ }
+ }
+ return false, nil
+}
+
+// ne evaluates the comparison a != b.
+func ne(arg1, arg2 interface{}) (bool, error) {
+ // != is the inverse of ==.
+ equal, err := eq(arg1, arg2)
+ return !equal, err
+}
+
+// lt evaluates the comparison a < b.
+func lt(arg1, arg2 interface{}) (bool, error) {
+ v1 := reflect.ValueOf(arg1)
+ k1, err := basicKind(v1)
+ if err != nil {
+ return false, err
+ }
+ v2 := reflect.ValueOf(arg2)
+ k2, err := basicKind(v2)
+ if err != nil {
+ return false, err
+ }
+ if k1 != k2 {
+ return false, errBadComparison
+ }
+ truth := false
+ switch k1 {
+ case boolKind, complexKind:
+ return false, errBadComparisonType
+ case floatKind:
+ truth = v1.Float() < v2.Float()
+ case intKind:
+ truth = v1.Int() < v2.Int()
+ case stringKind:
+ truth = v1.String() < v2.String()
+ case uintKind:
+ truth = v1.Uint() < v2.Uint()
+ default:
+ panic("invalid kind")
+ }
+ return truth, nil
+}
+
+// le evaluates the comparison <= b.
+func le(arg1, arg2 interface{}) (bool, error) {
+ // <= is < or ==.
+ lessThan, err := lt(arg1, arg2)
+ if lessThan || err != nil {
+ return lessThan, err
+ }
+ return eq(arg1, arg2)
+}
+
+// gt evaluates the comparison a > b.
+func gt(arg1, arg2 interface{}) (bool, error) {
+ // > is the inverse of <=.
+ lessOrEqual, err := le(arg1, arg2)
+ if err != nil {
+ return false, err
+ }
+ return !lessOrEqual, nil
+}
+
+// ge evaluates the comparison a >= b.
+func ge(arg1, arg2 interface{}) (bool, error) {
+ // >= is the inverse of <.
+ lessThan, err := lt(arg1, arg2)
+ if err != nil {
+ return false, err
+ }
+ return !lessThan, nil
+}
+
// HTML escaping.
var (
@@ -298,15 +461,7 @@ func HTMLEscapeString(s string) string {
// HTMLEscaper returns the escaped HTML equivalent of the textual
// representation of its arguments.
func HTMLEscaper(args ...interface{}) string {
- ok := false
- var s string
- if len(args) == 1 {
- s, ok = args[0].(string)
- }
- if !ok {
- s = fmt.Sprint(args...)
- }
- return HTMLEscapeString(s)
+ return HTMLEscapeString(evalArgs(args))
}
// JavaScript escaping.
@@ -391,26 +546,35 @@ func jsIsSpecial(r rune) bool {
// JSEscaper returns the escaped JavaScript equivalent of the textual
// representation of its arguments.
func JSEscaper(args ...interface{}) string {
- ok := false
- var s string
- if len(args) == 1 {
- s, ok = args[0].(string)
- }
- if !ok {
- s = fmt.Sprint(args...)
- }
- return JSEscapeString(s)
+ return JSEscapeString(evalArgs(args))
}
// URLQueryEscaper returns the escaped value of the textual representation of
// its arguments in a form suitable for embedding in a URL query.
func URLQueryEscaper(args ...interface{}) string {
- s, ok := "", false
+ return url.QueryEscape(evalArgs(args))
+}
+
+// evalArgs formats the list of arguments into a string. It is therefore equivalent to
+// fmt.Sprint(args...)
+// except that each argument is indirected (if a pointer), as required,
+// using the same rules as the default string evaluation during template
+// execution.
+func evalArgs(args []interface{}) string {
+ ok := false
+ var s string
+ // Fast path for simple common case.
if len(args) == 1 {
s, ok = args[0].(string)
}
if !ok {
+ for i, arg := range args {
+ a, ok := printableValue(reflect.ValueOf(arg))
+ if ok {
+ args[i] = a
+ } // else left fmt do its thing
+ }
s = fmt.Sprint(args...)
}
- return url.QueryEscape(s)
+ return s
}
diff --git a/src/pkg/text/template/multi_test.go b/src/pkg/text/template/multi_test.go
index bd98bd047..1f6ed5d8e 100644
--- a/src/pkg/text/template/multi_test.go
+++ b/src/pkg/text/template/multi_test.go
@@ -33,10 +33,10 @@ var multiParseTests = []multiParseTest{
nil},
{"one", `{{define "foo"}} FOO {{end}}`, noError,
[]string{"foo"},
- []string{`" FOO "`}},
+ []string{" FOO "}},
{"two", `{{define "foo"}} FOO {{end}}{{define "bar"}} BAR {{end}}`, noError,
[]string{"foo", "bar"},
- []string{`" FOO "`, `" BAR "`}},
+ []string{" FOO ", " BAR "}},
// errors
{"missing end", `{{define "foo"}} FOO `, hasError,
nil,
diff --git a/src/pkg/text/template/parse/lex.go b/src/pkg/text/template/parse/lex.go
index 23c0cf079..1674aaf9c 100644
--- a/src/pkg/text/template/parse/lex.go
+++ b/src/pkg/text/template/parse/lex.go
@@ -243,11 +243,16 @@ func lexLeftDelim(l *lexer) stateFn {
// lexComment scans a comment. The left comment marker is known to be present.
func lexComment(l *lexer) stateFn {
l.pos += Pos(len(leftComment))
- i := strings.Index(l.input[l.pos:], rightComment+l.rightDelim)
+ i := strings.Index(l.input[l.pos:], rightComment)
if i < 0 {
return l.errorf("unclosed comment")
}
- l.pos += Pos(i + len(rightComment) + len(l.rightDelim))
+ l.pos += Pos(i + len(rightComment))
+ if !strings.HasPrefix(l.input[l.pos:], l.rightDelim) {
+ return l.errorf("comment ends before closing delimiter")
+
+ }
+ l.pos += Pos(len(l.rightDelim))
l.ignore()
return lexText
}
diff --git a/src/pkg/text/template/parse/lex_test.go b/src/pkg/text/template/parse/lex_test.go
index d2264c991..d251ccffb 100644
--- a/src/pkg/text/template/parse/lex_test.go
+++ b/src/pkg/text/template/parse/lex_test.go
@@ -336,6 +336,16 @@ var lexTests = []lexTest{
{itemText, 0, "hello-"},
{itemError, 0, `unclosed comment`},
}},
+ {"text with comment close separted from delim", "hello-{{/* */ }}-world", []item{
+ {itemText, 0, "hello-"},
+ {itemError, 0, `comment ends before closing delimiter`},
+ }},
+ // This one is an error that we can't catch because it breaks templates with
+ // minimized JavaScript. Should have fixed it before Go 1.1.
+ {"unmatched right delimiter", "hello-{.}}-world", []item{
+ {itemText, 0, "hello-{.}}-world"},
+ tEOF,
+ }},
}
// collect gathers the emitted items into a slice.
diff --git a/src/pkg/text/template/parse/node.go b/src/pkg/text/template/parse/node.go
index 9d0d09eb5..dc6a3bb92 100644
--- a/src/pkg/text/template/parse/node.go
+++ b/src/pkg/text/template/parse/node.go
@@ -13,6 +13,8 @@ import (
"strings"
)
+var textFormat = "%s" // Changed to "%q" in tests for better error messages.
+
// A Node is an element in the parse tree. The interface is trivial.
// The interface contains an unexported method so that only
// types local to this package can satisfy it.
@@ -125,7 +127,7 @@ func newText(pos Pos, text string) *TextNode {
}
func (t *TextNode) String() string {
- return fmt.Sprintf("%q", t.Text)
+ return fmt.Sprintf(textFormat, t.Text)
}
func (t *TextNode) Copy() Node {
diff --git a/src/pkg/text/template/parse/parse.go b/src/pkg/text/template/parse/parse.go
index 802e298c2..34112fb7b 100644
--- a/src/pkg/text/template/parse/parse.go
+++ b/src/pkg/text/template/parse/parse.go
@@ -14,7 +14,6 @@ import (
"runtime"
"strconv"
"strings"
- "unicode"
)
// Tree is the representation of a single parsed template.
@@ -31,6 +30,19 @@ type Tree struct {
vars []string // variables defined at the moment.
}
+// Copy returns a copy of the Tree. Any parsing state is discarded.
+func (t *Tree) Copy() *Tree {
+ if t == nil {
+ return nil
+ }
+ return &Tree{
+ Name: t.Name,
+ ParseName: t.ParseName,
+ Root: t.Root.CopyList(),
+ text: t.text,
+ }
+}
+
// Parse returns a map from template name to parse.Tree, created by parsing the
// templates described in the argument string. The top-level template will be
// given the specified name. If an error is encountered, parsing stops and an
@@ -200,27 +212,6 @@ func (t *Tree) stopParse() {
t.funcs = nil
}
-// atEOF returns true if, possibly after spaces, we're at EOF.
-func (t *Tree) atEOF() bool {
- for {
- token := t.peek()
- switch token.typ {
- case itemEOF:
- return true
- case itemText:
- for _, r := range token.val {
- if !unicode.IsSpace(r) {
- return false
- }
- }
- t.next() // skip spaces.
- continue
- }
- break
- }
- return false
-}
-
// Parse parses the template definition string to construct a representation of
// the template for execution. If either action delimiter string is empty, the
// default ("{{" or "}}") is used. Embedded template definitions are added to
@@ -431,7 +422,7 @@ func (t *Tree) pipeline(context string) (pipe *PipeNode) {
}
}
-func (t *Tree) parseControl(context string) (pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) {
+func (t *Tree) parseControl(allowElseIf bool, context string) (pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) {
defer t.popVars(len(t.vars))
line = t.lex.lineNumber()
pipe = t.pipeline(context)
@@ -440,6 +431,23 @@ func (t *Tree) parseControl(context string) (pos Pos, line int, pipe *PipeNode,
switch next.Type() {
case nodeEnd: //done
case nodeElse:
+ if allowElseIf {
+ // Special case for "else if". If the "else" is followed immediately by an "if",
+ // the elseControl will have left the "if" token pending. Treat
+ // {{if a}}_{{else if b}}_{{end}}
+ // as
+ // {{if a}}_{{else}}{{if b}}_{{end}}{{end}}.
+ // To do this, parse the if as usual and stop at it {{end}}; the subsequent{{end}}
+ // is assumed. This technique works even for long if-else-if chains.
+ // TODO: Should we allow else-if in with and range?
+ if t.peek().typ == itemIf {
+ t.next() // Consume the "if" token.
+ elseList = newList(next.Position())
+ elseList.append(t.ifControl())
+ // Do not consume the next item - only one {{end}} required.
+ break
+ }
+ }
elseList, next = t.itemList()
if next.Type() != nodeEnd {
t.errorf("expected end; found %s", next)
@@ -453,7 +461,7 @@ func (t *Tree) parseControl(context string) (pos Pos, line int, pipe *PipeNode,
// {{if pipeline}} itemList {{else}} itemList {{end}}
// If keyword is past.
func (t *Tree) ifControl() Node {
- return newIf(t.parseControl("if"))
+ return newIf(t.parseControl(true, "if"))
}
// Range:
@@ -461,7 +469,7 @@ func (t *Tree) ifControl() Node {
// {{range pipeline}} itemList {{else}} itemList {{end}}
// Range keyword is past.
func (t *Tree) rangeControl() Node {
- return newRange(t.parseControl("range"))
+ return newRange(t.parseControl(false, "range"))
}
// With:
@@ -469,7 +477,7 @@ func (t *Tree) rangeControl() Node {
// {{with pipeline}} itemList {{else}} itemList {{end}}
// If keyword is past.
func (t *Tree) withControl() Node {
- return newWith(t.parseControl("with"))
+ return newWith(t.parseControl(false, "with"))
}
// End:
@@ -483,6 +491,12 @@ func (t *Tree) endControl() Node {
// {{else}}
// Else keyword is past.
func (t *Tree) elseControl() Node {
+ // Special case for "else if".
+ peek := t.peekNonSpace()
+ if peek.typ == itemIf {
+ // We see "{{else if ... " but in effect rewrite it to {{else}}{{if ... ".
+ return newElse(peek.pos, t.lex.lineNumber())
+ }
return newElse(t.expect(itemRightDelim, "else").pos, t.lex.lineNumber())
}
diff --git a/src/pkg/text/template/parse/parse_test.go b/src/pkg/text/template/parse/parse_test.go
index 695c76ebf..ba1a18ec5 100644
--- a/src/pkg/text/template/parse/parse_test.go
+++ b/src/pkg/text/template/parse/parse_test.go
@@ -194,6 +194,10 @@ var parseTests = []parseTest{
`{{if .X}}"hello"{{end}}`},
{"if with else", "{{if .X}}true{{else}}false{{end}}", noError,
`{{if .X}}"true"{{else}}"false"{{end}}`},
+ {"if with else if", "{{if .X}}true{{else if .Y}}false{{end}}", noError,
+ `{{if .X}}"true"{{else}}{{if .Y}}"false"{{end}}{{end}}`},
+ {"if else chain", "+{{if .X}}X{{else if .Y}}Y{{else if .Z}}Z{{end}}+", noError,
+ `"+"{{if .X}}"X"{{else}}{{if .Y}}"Y"{{else}}{{if .Z}}"Z"{{end}}{{end}}{{end}}"+"`},
{"simple range", "{{range .X}}hello{{end}}", noError,
`{{range .X}}"hello"{{end}}`},
{"chained field range", "{{range .X.Y.Z}}hello{{end}}", noError,
@@ -238,6 +242,7 @@ var parseTests = []parseTest{
{"dot applied to parentheses", "{{printf (printf .).}}", hasError, ""},
{"adjacent args", "{{printf 3`x`}}", hasError, ""},
{"adjacent args with .", "{{printf `x`.}}", hasError, ""},
+ {"extra end after if", "{{if .X}}a{{else if .Y}}b{{end}}{{end}}", hasError, ""},
// Equals (and other chars) do not assignments make (yet).
{"bug0a", "{{$x := 0}}{{$x}}", noError, "{{$x := 0}}{{$x}}"},
{"bug0b", "{{$x = 1}}{{$x}}", hasError, ""},
@@ -256,6 +261,8 @@ var builtins = map[string]interface{}{
}
func testParse(doCopy bool, t *testing.T) {
+ textFormat = "%q"
+ defer func() { textFormat = "%s" }()
for _, test := range parseTests {
tmpl, err := New(test.name).Parse(test.input, "", "", make(map[string]*Tree), builtins)
switch {
@@ -305,7 +312,7 @@ var isEmptyTests = []isEmptyTest{
{"spaces only", " \t\n \t\n", true},
{"definition", `{{define "x"}}something{{end}}`, true},
{"definitions and space", "{{define `x`}}something{{end}}\n\n{{define `y`}}something{{end}}\n\n", true},
- {"definitions and text", "{{define `x`}}something{{end}}\nx\n{{define `y`}}something{{end}}\ny\n}}", false},
+ {"definitions and text", "{{define `x`}}something{{end}}\nx\n{{define `y`}}something{{end}}\ny\n", false},
{"definition and action", "{{define `x`}}something{{end}}{{if 3}}foo{{end}}", false},
}
@@ -325,6 +332,22 @@ func TestIsEmpty(t *testing.T) {
}
}
+func TestErrorContextWithTreeCopy(t *testing.T) {
+ tree, err := New("root").Parse("{{if true}}{{end}}", "", "", make(map[string]*Tree), nil)
+ if err != nil {
+ t.Fatalf("unexpected tree parse failure: %v", err)
+ }
+ treeCopy := tree.Copy()
+ wantLocation, wantContext := tree.ErrorContext(tree.Root.Nodes[0])
+ gotLocation, gotContext := treeCopy.ErrorContext(treeCopy.Root.Nodes[0])
+ if wantLocation != gotLocation {
+ t.Errorf("wrong error location want %q got %q", wantLocation, gotLocation)
+ }
+ if wantContext != gotContext {
+ t.Errorf("wrong error location want %q got %q", wantContext, gotContext)
+ }
+}
+
// All failures, and the result is a string that must appear in the error message.
var errorTests = []parseTest{
// Check line numbers are accurate.
diff --git a/src/pkg/time/Makefile b/src/pkg/time/Makefile
new file mode 100644
index 000000000..cba58e4e0
--- /dev/null
+++ b/src/pkg/time/Makefile
@@ -0,0 +1,9 @@
+# Copyright 2013 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.
+
+genzabbrs: genzabbrs.go
+ go build genzabbrs.go
+
+windows: genzabbrs
+ ./genzabbrs | gofmt >zoneinfo_abbrs_windows.go
diff --git a/src/pkg/time/export_test.go b/src/pkg/time/export_test.go
index 130ca8f7e..6cd535f6b 100644
--- a/src/pkg/time/export_test.go
+++ b/src/pkg/time/export_test.go
@@ -17,3 +17,8 @@ func ForceUSPacificForTesting() {
ResetLocalOnceForTest()
localOnce.Do(initTestingZone)
}
+
+var (
+ ForceZipFileForTesting = forceZipFileForTesting
+ ParseTimeZone = parseTimeZone
+)
diff --git a/src/pkg/time/export_windows_test.go b/src/pkg/time/export_windows_test.go
new file mode 100644
index 000000000..7e689b829
--- /dev/null
+++ b/src/pkg/time/export_windows_test.go
@@ -0,0 +1,10 @@
+// Copyright 2013 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 time
+
+func ForceAusForTesting() {
+ ResetLocalOnceForTest()
+ localOnce.Do(initAusTestingZone)
+}
diff --git a/src/pkg/time/format.go b/src/pkg/time/format.go
index 7fe040231..6f92c1262 100644
--- a/src/pkg/time/format.go
+++ b/src/pkg/time/format.go
@@ -59,35 +59,39 @@ const (
)
const (
- _ = iota
- stdLongMonth = iota + stdNeedDate // "January"
- stdMonth // "Jan"
- stdNumMonth // "1"
- stdZeroMonth // "01"
- stdLongWeekDay // "Monday"
- stdWeekDay // "Mon"
- stdDay // "2"
- stdUnderDay // "_2"
- stdZeroDay // "02"
- stdHour = iota + stdNeedClock // "15"
- stdHour12 // "3"
- stdZeroHour12 // "03"
- stdMinute // "4"
- stdZeroMinute // "04"
- stdSecond // "5"
- stdZeroSecond // "05"
- stdLongYear = iota + stdNeedDate // "2006"
- stdYear // "06"
- stdPM = iota + stdNeedClock // "PM"
- stdpm // "pm"
- stdTZ = iota // "MST"
- stdISO8601TZ // "Z0700" // prints Z for UTC
- stdISO8601ColonTZ // "Z07:00" // prints Z for UTC
- stdNumTZ // "-0700" // always numeric
- stdNumShortTZ // "-07" // always numeric
- stdNumColonTZ // "-07:00" // always numeric
- stdFracSecond0 // ".0", ".00", ... , trailing zeros included
- stdFracSecond9 // ".9", ".99", ..., trailing zeros omitted
+ _ = iota
+ stdLongMonth = iota + stdNeedDate // "January"
+ stdMonth // "Jan"
+ stdNumMonth // "1"
+ stdZeroMonth // "01"
+ stdLongWeekDay // "Monday"
+ stdWeekDay // "Mon"
+ stdDay // "2"
+ stdUnderDay // "_2"
+ stdZeroDay // "02"
+ stdHour = iota + stdNeedClock // "15"
+ stdHour12 // "3"
+ stdZeroHour12 // "03"
+ stdMinute // "4"
+ stdZeroMinute // "04"
+ stdSecond // "5"
+ stdZeroSecond // "05"
+ stdLongYear = iota + stdNeedDate // "2006"
+ stdYear // "06"
+ stdPM = iota + stdNeedClock // "PM"
+ stdpm // "pm"
+ stdTZ = iota // "MST"
+ stdISO8601TZ // "Z0700" // prints Z for UTC
+ stdISO8601SecondsTZ // "Z070000"
+ stdISO8601ColonTZ // "Z07:00" // prints Z for UTC
+ stdISO8601ColonSecondsTZ // "Z07:00:00"
+ stdNumTZ // "-0700" // always numeric
+ stdNumSecondsTz // "-070000"
+ stdNumShortTZ // "-07" // always numeric
+ stdNumColonTZ // "-07:00" // always numeric
+ stdNumColonSecondsTZ // "-07:00:00"
+ stdFracSecond0 // ".0", ".00", ... , trailing zeros included
+ stdFracSecond9 // ".9", ".99", ..., trailing zeros omitted
stdNeedDate = 1 << 8 // need month, day, year
stdNeedClock = 2 << 8 // need hour, minute, second
@@ -98,6 +102,16 @@ const (
// std0x records the std values for "01", "02", ..., "06".
var std0x = [...]int{stdZeroMonth, stdZeroDay, stdZeroHour12, stdZeroMinute, stdZeroSecond, stdYear}
+// startsWithLowerCase reports whether the the string has a lower-case letter at the beginning.
+// Its purpose is to prevent matching strings like "Month" when looking for "Mon".
+func startsWithLowerCase(str string) bool {
+ if len(str) == 0 {
+ return false
+ }
+ c := str[0]
+ return 'a' <= c && c <= 'z'
+}
+
// nextStdChunk finds the first occurrence of a std string in
// layout and returns the text before, the std string, and the text after.
func nextStdChunk(layout string) (prefix string, std int, suffix string) {
@@ -108,7 +122,9 @@ func nextStdChunk(layout string) (prefix string, std int, suffix string) {
if len(layout) >= i+7 && layout[i:i+7] == "January" {
return layout[0:i], stdLongMonth, layout[i+7:]
}
- return layout[0:i], stdMonth, layout[i+3:]
+ if !startsWithLowerCase(layout[i+3:]) {
+ return layout[0:i], stdMonth, layout[i+3:]
+ }
}
case 'M': // Monday, Mon, MST
@@ -117,7 +133,9 @@ func nextStdChunk(layout string) (prefix string, std int, suffix string) {
if len(layout) >= i+6 && layout[i:i+6] == "Monday" {
return layout[0:i], stdLongWeekDay, layout[i+6:]
}
- return layout[0:i], stdWeekDay, layout[i+3:]
+ if !startsWithLowerCase(layout[i+3:]) {
+ return layout[0:i], stdWeekDay, layout[i+3:]
+ }
}
if layout[i:i+3] == "MST" {
return layout[0:i], stdTZ, layout[i+3:]
@@ -165,7 +183,13 @@ func nextStdChunk(layout string) (prefix string, std int, suffix string) {
return layout[0:i], stdpm, layout[i+2:]
}
- case '-': // -0700, -07:00, -07
+ case '-': // -070000, -07:00:00, -0700, -07:00, -07
+ if len(layout) >= i+7 && layout[i:i+7] == "-070000" {
+ return layout[0:i], stdNumSecondsTz, layout[i+7:]
+ }
+ if len(layout) >= i+9 && layout[i:i+9] == "-07:00:00" {
+ return layout[0:i], stdNumColonSecondsTZ, layout[i+9:]
+ }
if len(layout) >= i+5 && layout[i:i+5] == "-0700" {
return layout[0:i], stdNumTZ, layout[i+5:]
}
@@ -175,13 +199,21 @@ func nextStdChunk(layout string) (prefix string, std int, suffix string) {
if len(layout) >= i+3 && layout[i:i+3] == "-07" {
return layout[0:i], stdNumShortTZ, layout[i+3:]
}
- case 'Z': // Z0700, Z07:00
+
+ case 'Z': // Z070000, Z07:00:00, Z0700, Z07:00,
+ if len(layout) >= i+7 && layout[i:i+7] == "Z070000" {
+ return layout[0:i], stdISO8601SecondsTZ, layout[i+7:]
+ }
+ if len(layout) >= i+9 && layout[i:i+9] == "Z07:00:00" {
+ return layout[0:i], stdISO8601ColonSecondsTZ, layout[i+9:]
+ }
if len(layout) >= i+5 && layout[i:i+5] == "Z0700" {
return layout[0:i], stdISO8601TZ, layout[i+5:]
}
if len(layout) >= i+6 && layout[i:i+6] == "Z07:00" {
return layout[0:i], stdISO8601ColonTZ, layout[i+6:]
}
+
case '.': // .000 or .999 - repeated digits for fractional seconds.
if i+1 < len(layout) && (layout[i+1] == '0' || layout[i+1] == '9') {
ch := layout[i+1]
@@ -321,8 +353,8 @@ var atoiError = errors.New("time: invalid number")
// Duplicates functionality in strconv, but avoids dependency.
func atoi(s string) (x int, err error) {
neg := false
- if s != "" && s[0] == '-' {
- neg = true
+ if s != "" && (s[0] == '-' || s[0] == '+') {
+ neg = s[0] == '-'
s = s[1:]
}
q, rem, err := leadingInt(s)
@@ -507,17 +539,19 @@ func (t Time) Format(layout string) string {
} else {
b = append(b, "am"...)
}
- case stdISO8601TZ, stdISO8601ColonTZ, stdNumTZ, stdNumColonTZ:
+ case stdISO8601TZ, stdISO8601ColonTZ, stdISO8601SecondsTZ, stdISO8601ColonSecondsTZ, stdNumTZ, stdNumColonTZ, stdNumSecondsTz, stdNumColonSecondsTZ:
// Ugly special case. We cheat and take the "Z" variants
// to mean "the time zone as formatted for ISO 8601".
- if offset == 0 && (std == stdISO8601TZ || std == stdISO8601ColonTZ) {
+ if offset == 0 && (std == stdISO8601TZ || std == stdISO8601ColonTZ || std == stdISO8601SecondsTZ || std == stdISO8601ColonSecondsTZ) {
b = append(b, 'Z')
break
}
zone := offset / 60 // convert to minutes
+ absoffset := offset
if zone < 0 {
b = append(b, '-')
zone = -zone
+ absoffset = -absoffset
} else {
b = append(b, '+')
}
@@ -526,6 +560,15 @@ func (t Time) Format(layout string) string {
b = append(b, ':')
}
b = appendUint(b, uint(zone%60), '0')
+
+ // append seconds if appropriate
+ if std == stdISO8601SecondsTZ || std == stdNumSecondsTz || std == stdNumColonSecondsTZ || std == stdISO8601ColonSecondsTZ {
+ if std == stdNumColonSecondsTZ || std == stdISO8601ColonSecondsTZ {
+ b = append(b, ':')
+ }
+ b = appendUint(b, uint(absoffset%60), '0')
+ }
+
case stdTZ:
if name != "" {
b = append(b, name...)
@@ -780,7 +823,7 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error)
// Special case: do we have a fractional second but no
// fractional second in the format?
if len(value) >= 2 && value[0] == '.' && isDigit(value, 1) {
- _, std, _ := nextStdChunk(layout)
+ _, std, _ = nextStdChunk(layout)
std &= stdMask
if std == stdFracSecond0 || std == stdFracSecond9 {
// Fractional second in the layout; proceed normally
@@ -821,13 +864,13 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error)
default:
err = errBad
}
- case stdISO8601TZ, stdISO8601ColonTZ, stdNumTZ, stdNumShortTZ, stdNumColonTZ:
+ case stdISO8601TZ, stdISO8601ColonTZ, stdISO8601SecondsTZ, stdISO8601ColonSecondsTZ, stdNumTZ, stdNumShortTZ, stdNumColonTZ, stdNumSecondsTz, stdNumColonSecondsTZ:
if (std == stdISO8601TZ || std == stdISO8601ColonTZ) && len(value) >= 1 && value[0] == 'Z' {
value = value[1:]
z = UTC
break
}
- var sign, hour, min string
+ var sign, hour, min, seconds string
if std == stdISO8601ColonTZ || std == stdNumColonTZ {
if len(value) < 6 {
err = errBad
@@ -837,26 +880,45 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error)
err = errBad
break
}
- sign, hour, min, value = value[0:1], value[1:3], value[4:6], value[6:]
+ sign, hour, min, seconds, value = value[0:1], value[1:3], value[4:6], "00", value[6:]
} else if std == stdNumShortTZ {
if len(value) < 3 {
err = errBad
break
}
- sign, hour, min, value = value[0:1], value[1:3], "00", value[3:]
+ sign, hour, min, seconds, value = value[0:1], value[1:3], "00", "00", value[3:]
+ } else if std == stdISO8601ColonSecondsTZ || std == stdNumColonSecondsTZ {
+ if len(value) < 9 {
+ err = errBad
+ break
+ }
+ if value[3] != ':' || value[6] != ':' {
+ err = errBad
+ break
+ }
+ sign, hour, min, seconds, value = value[0:1], value[1:3], value[4:6], value[7:9], value[9:]
+ } else if std == stdISO8601SecondsTZ || std == stdNumSecondsTz {
+ if len(value) < 7 {
+ err = errBad
+ break
+ }
+ sign, hour, min, seconds, value = value[0:1], value[1:3], value[3:5], value[5:7], value[7:]
} else {
if len(value) < 5 {
err = errBad
break
}
- sign, hour, min, value = value[0:1], value[1:3], value[3:5], value[5:]
+ sign, hour, min, seconds, value = value[0:1], value[1:3], value[3:5], "00", value[5:]
}
- var hr, mm int
+ var hr, mm, ss int
hr, err = atoi(hour)
if err == nil {
mm, err = atoi(min)
}
- zoneOffset = (hr*60 + mm) * 60 // offset is in seconds
+ if err == nil {
+ ss, err = atoi(seconds)
+ }
+ zoneOffset = (hr*60+mm)*60 + ss // offset is in seconds
switch sign[0] {
case '+':
case '-':
@@ -871,25 +933,12 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error)
value = value[3:]
break
}
-
- if len(value) >= 3 && value[2] == 'T' {
- p, value = value[0:3], value[3:]
- } else if len(value) >= 4 && value[3] == 'T' {
- p, value = value[0:4], value[4:]
- } else {
+ n, ok := parseTimeZone(value)
+ if !ok {
err = errBad
break
}
- for i := 0; i < len(p); i++ {
- if p[i] < 'A' || 'Z' < p[i] {
- err = errBad
- }
- }
- if err != nil {
- break
- }
- // It's a valid format.
- zoneName = p
+ zoneName, value = value[:n], value[n:]
case stdFracSecond0:
// stdFracSecond0 requires the exact number of digits as specified in
@@ -962,7 +1011,11 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error)
}
// Otherwise, create fake zone with unknown offset.
- t.loc = FixedZone(zoneName, 0)
+ if len(zoneName) > 3 && zoneName[:3] == "GMT" {
+ offset, _ = atoi(zoneName[3:]) // Guaranteed OK by parseGMT.
+ offset *= 3600
+ }
+ t.loc = FixedZone(zoneName, offset)
return t, nil
}
@@ -970,6 +1023,81 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error)
return Date(year, Month(month), day, hour, min, sec, nsec, defaultLocation), nil
}
+// parseTimeZone parses a time zone string and returns its length. Time zones
+// are human-generated and unpredictable. We can't do precise error checking.
+// On the other hand, for a correct parse there must be a time zone at the
+// beginning of the string, so it's almost always true that there's one
+// there. We look at the beginning of the string for a run of upper-case letters.
+// If there are more than 5, it's an error.
+// If there are 4 or 5 and the last is a T, it's a time zone.
+// If there are 3, it's a time zone.
+// Otherwise, other than special cases, it's not a time zone.
+// GMT is special because it can have an hour offset.
+func parseTimeZone(value string) (length int, ok bool) {
+ if len(value) < 3 {
+ return 0, false
+ }
+ // Special case 1: This is the only zone with a lower-case letter.
+ if len(value) >= 4 && value[:4] == "ChST" {
+ return 4, true
+ }
+ // Special case 2: GMT may have an hour offset; treat it specially.
+ if value[:3] == "GMT" {
+ length = parseGMT(value)
+ return length, true
+ }
+ // How many upper-case letters are there? Need at least three, at most five.
+ var nUpper int
+ for nUpper = 0; nUpper < 6; nUpper++ {
+ if nUpper >= len(value) {
+ break
+ }
+ if c := value[nUpper]; c < 'A' || 'Z' < c {
+ break
+ }
+ }
+ switch nUpper {
+ case 0, 1, 2, 6:
+ return 0, false
+ case 5: // Must end in T to match.
+ if value[4] == 'T' {
+ return 5, true
+ }
+ case 4: // Must end in T to match.
+ if value[3] == 'T' {
+ return 4, true
+ }
+ case 3:
+ return 3, true
+ }
+ return 0, false
+}
+
+// parseGMT parses a GMT time zone. The input string is known to start "GMT".
+// The function checks whether that is followed by a sign and a number in the
+// range -14 through 12 excluding zero.
+func parseGMT(value string) int {
+ value = value[3:]
+ if len(value) == 0 {
+ return 3
+ }
+ sign := value[0]
+ if sign != '-' && sign != '+' {
+ return 3
+ }
+ x, rem, err := leadingInt(value[1:])
+ if err != nil {
+ return 3
+ }
+ if sign == '-' {
+ x = -x
+ }
+ if x == 0 || x < -14 || 12 < x {
+ return 3
+ }
+ return 3 + len(value) - len(rem)
+}
+
func parseNanoseconds(value string, nbytes int) (ns int, rangeErrString string, err error) {
if value[0] != '.' {
err = errBad
@@ -1076,11 +1204,11 @@ func ParseDuration(s string) (Duration, error) {
if err != nil {
return 0, errors.New("time: invalid duration " + orig)
}
- scale := 1
+ scale := 1.0
for n := pl - len(s); n > 0; n-- {
scale *= 10
}
- g += float64(x) / float64(scale)
+ g += float64(x) / scale
post = pl != len(s)
}
if !pre && !post {
diff --git a/src/pkg/time/genzabbrs.go b/src/pkg/time/genzabbrs.go
new file mode 100644
index 000000000..7c637cb43
--- /dev/null
+++ b/src/pkg/time/genzabbrs.go
@@ -0,0 +1,145 @@
+// Copyright 2013 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.
+
+// +build ignore
+
+//
+// usage:
+//
+// go run genzabbrs.go | gofmt > $GOROOT/src/pkg/time/zoneinfo_abbrs_windows.go
+//
+
+package main
+
+import (
+ "encoding/xml"
+ "io/ioutil"
+ "log"
+ "net/http"
+ "os"
+ "sort"
+ "text/template"
+ "time"
+)
+
+// getAbbrs finds timezone abbreviations (standard and daylight saving time)
+// for location l.
+func getAbbrs(l *time.Location) (st, dt string) {
+ t := time.Date(time.Now().Year(), 0, 0, 0, 0, 0, 0, l)
+ abbr1, off1 := t.Zone()
+ for i := 0; i < 12; i++ {
+ t = t.AddDate(0, 1, 0)
+ abbr2, off2 := t.Zone()
+ if abbr1 != abbr2 {
+ if off2-off1 < 0 { // southern hemisphere
+ abbr1, abbr2 = abbr2, abbr1
+ }
+ return abbr1, abbr2
+ }
+ }
+ return abbr1, abbr1
+}
+
+type zone struct {
+ WinName string
+ UnixName string
+ StTime string
+ DSTime string
+}
+
+type zones []*zone
+
+func (zs zones) Len() int { return len(zs) }
+func (zs zones) Swap(i, j int) { zs[i], zs[j] = zs[j], zs[i] }
+func (zs zones) Less(i, j int) bool { return zs[i].UnixName < zs[j].UnixName }
+
+const wzURL = "http://unicode.org/cldr/data/common/supplemental/windowsZones.xml"
+
+type MapZone struct {
+ Other string `xml:"other,attr"`
+ Territory string `xml:"territory,attr"`
+ Type string `xml:"type,attr"`
+}
+
+type SupplementalData struct {
+ Zones []MapZone `xml:"windowsZones>mapTimezones>mapZone"`
+}
+
+func readWindowsZones() (zones, error) {
+ r, err := http.Get(wzURL)
+ if err != nil {
+ return nil, err
+ }
+ defer r.Body.Close()
+
+ data, err := ioutil.ReadAll(r.Body)
+ if err != nil {
+ return nil, err
+ }
+
+ var sd SupplementalData
+ err = xml.Unmarshal(data, &sd)
+ if err != nil {
+ return nil, err
+ }
+ zs := make(zones, 0)
+ for _, z := range sd.Zones {
+ if z.Territory != "001" {
+ // to avoid dups. I don't know why.
+ continue
+ }
+ l, err := time.LoadLocation(z.Type)
+ if err != nil {
+ return nil, err
+ }
+ st, dt := getAbbrs(l)
+ zs = append(zs, &zone{
+ WinName: z.Other,
+ UnixName: z.Type,
+ StTime: st,
+ DSTime: dt,
+ })
+ }
+ return zs, nil
+}
+
+func main() {
+ zs, err := readWindowsZones()
+ if err != nil {
+ log.Fatal(err)
+ }
+ sort.Sort(zs)
+ var v = struct {
+ URL string
+ Zs zones
+ }{
+ wzURL,
+ zs,
+ }
+ err = template.Must(template.New("prog").Parse(prog)).Execute(os.Stdout, v)
+ if err != nil {
+ log.Fatal(err)
+ }
+}
+
+const prog = `
+// Copyright 2013 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.
+
+// generated by genzabbrs.go from
+// {{.URL}}
+
+package time
+
+type abbr struct {
+ std string
+ dst string
+}
+
+var abbrs = map[string]abbr{
+{{range .Zs}} "{{.WinName}}": {"{{.StTime}}", "{{.DSTime}}"}, // {{.UnixName}}
+{{end}}}
+
+`
diff --git a/src/pkg/time/internal_test.go b/src/pkg/time/internal_test.go
index 918a9f33b..87fdd3216 100644
--- a/src/pkg/time/internal_test.go
+++ b/src/pkg/time/internal_test.go
@@ -4,6 +4,11 @@
package time
+import (
+ "errors"
+ "runtime"
+)
+
func init() {
// force US/Pacific for time zone tests
ForceUSPacificForTesting()
@@ -11,3 +16,65 @@ func init() {
var Interrupt = interrupt
var DaysIn = daysIn
+
+func empty(now int64, arg interface{}) {}
+
+// Test that a runtimeTimer with a duration so large it overflows
+// does not cause other timers to hang.
+//
+// This test has to be in internal_test.go since it fiddles with
+// unexported data structures.
+func CheckRuntimeTimerOverflow() error {
+ // We manually create a runtimeTimer to bypass the overflow
+ // detection logic in NewTimer: we're testing the underlying
+ // runtime.addtimer function.
+ r := &runtimeTimer{
+ when: nano() + (1<<63 - 1),
+ f: empty,
+ arg: nil,
+ }
+ startTimer(r)
+
+ timeout := 100 * Millisecond
+ if runtime.GOOS == "windows" {
+ // Allow more time for gobuilder to succeed.
+ timeout = Second
+ }
+
+ // Start a goroutine that should send on t.C before the timeout.
+ t := NewTimer(1)
+
+ defer func() {
+ // Subsequent tests won't work correctly if we don't stop the
+ // overflow timer and kick the timer proc back into service.
+ //
+ // The timer proc is now sleeping and can only be awoken by
+ // adding a timer to the *beginning* of the heap. We can't
+ // wake it up by calling NewTimer since other tests may have
+ // left timers running that should have expired before ours.
+ // Instead we zero the overflow timer duration and start it
+ // once more.
+ stopTimer(r)
+ t.Stop()
+ r.when = 0
+ startTimer(r)
+ }()
+
+ // Try to receive from t.C before the timeout. It will succeed
+ // iff the previous sleep was able to finish. We're forced to
+ // spin and yield after trying to receive since we can't start
+ // any more timers (they might hang due to the same bug we're
+ // now testing).
+ stop := Now().Add(timeout)
+ for {
+ select {
+ case <-t.C:
+ return nil // It worked!
+ default:
+ if Now().After(stop) {
+ return errors.New("runtime timer stuck: overflow in addtimer")
+ }
+ runtime.Gosched()
+ }
+ }
+}
diff --git a/src/pkg/time/sleep.go b/src/pkg/time/sleep.go
index 591fa27b0..4f55bebe6 100644
--- a/src/pkg/time/sleep.go
+++ b/src/pkg/time/sleep.go
@@ -4,7 +4,8 @@
package time
-// Sleep pauses the current goroutine for the duration d.
+// Sleep pauses the current goroutine for at least the duration d.
+// A negative or zero duration causes Sleep to return immediately.
func Sleep(d Duration)
func nano() int64 {
diff --git a/src/pkg/time/sleep_test.go b/src/pkg/time/sleep_test.go
index 603adc9b8..468725950 100644
--- a/src/pkg/time/sleep_test.go
+++ b/src/pkg/time/sleep_test.go
@@ -9,6 +9,7 @@ import (
"fmt"
"runtime"
"sort"
+ "sync"
"sync/atomic"
"testing"
. "time"
@@ -68,33 +69,94 @@ func TestAfterStress(t *testing.T) {
atomic.StoreUint32(&stop, 1)
}
+func benchmark(b *testing.B, bench func(n int)) {
+ garbage := make([]*Timer, 1<<17)
+ for i := 0; i < len(garbage); i++ {
+ garbage[i] = AfterFunc(Hour, nil)
+ }
+
+ const batch = 1000
+ P := runtime.GOMAXPROCS(-1)
+ N := int32(b.N / batch)
+
+ b.ResetTimer()
+
+ var wg sync.WaitGroup
+ wg.Add(P)
+
+ for p := 0; p < P; p++ {
+ go func() {
+ for atomic.AddInt32(&N, -1) >= 0 {
+ bench(batch)
+ }
+ wg.Done()
+ }()
+ }
+
+ wg.Wait()
+
+ b.StopTimer()
+ for i := 0; i < len(garbage); i++ {
+ garbage[i].Stop()
+ }
+}
+
func BenchmarkAfterFunc(b *testing.B) {
- i := b.N
- c := make(chan bool)
- var f func()
- f = func() {
- i--
- if i >= 0 {
- AfterFunc(0, f)
- } else {
- c <- true
+ benchmark(b, func(n int) {
+ c := make(chan bool)
+ var f func()
+ f = func() {
+ n--
+ if n >= 0 {
+ AfterFunc(0, f)
+ } else {
+ c <- true
+ }
}
- }
- AfterFunc(0, f)
- <-c
+ AfterFunc(0, f)
+ <-c
+ })
}
func BenchmarkAfter(b *testing.B) {
- for i := 0; i < b.N; i++ {
- <-After(1)
- }
+ benchmark(b, func(n int) {
+ for i := 0; i < n; i++ {
+ <-After(1)
+ }
+ })
}
func BenchmarkStop(b *testing.B) {
- for i := 0; i < b.N; i++ {
- NewTimer(1 * Second).Stop()
- }
+ benchmark(b, func(n int) {
+ for i := 0; i < n; i++ {
+ NewTimer(1 * Second).Stop()
+ }
+ })
+}
+
+func BenchmarkSimultaneousAfterFunc(b *testing.B) {
+ benchmark(b, func(n int) {
+ var wg sync.WaitGroup
+ wg.Add(n)
+ for i := 0; i < n; i++ {
+ AfterFunc(0, wg.Done)
+ }
+ wg.Wait()
+ })
+}
+
+func BenchmarkStartStop(b *testing.B) {
+ benchmark(b, func(n int) {
+ timers := make([]*Timer, n)
+ for i := 0; i < n; i++ {
+ timers[i] = AfterFunc(Hour, nil)
+ }
+
+ for i := 0; i < n; i++ {
+ timers[i].Stop()
+ }
+ })
}
func TestAfter(t *testing.T) {
@@ -334,3 +396,9 @@ func TestIssue5745(t *testing.T) {
timer.Stop()
t.Error("Should be unreachable.")
}
+
+func TestOverflowRuntimeTimer(t *testing.T) {
+ if err := CheckRuntimeTimerOverflow(); err != nil {
+ t.Fatalf(err.Error())
+ }
+}
diff --git a/src/pkg/time/sys_unix.go b/src/pkg/time/sys_unix.go
index 7f69b492c..60a3ce08f 100644
--- a/src/pkg/time/sys_unix.go
+++ b/src/pkg/time/sys_unix.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.
-// +build darwin freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd
package time
diff --git a/src/pkg/time/time.go b/src/pkg/time/time.go
index d291672af..c504df740 100644
--- a/src/pkg/time/time.go
+++ b/src/pkg/time/time.go
@@ -39,7 +39,14 @@ type Time struct {
// nsec specifies a non-negative nanosecond
// offset within the second named by Seconds.
// It must be in the range [0, 999999999].
- nsec int32
+ //
+ // It is declared as uintptr instead of int32 or uint32
+ // to avoid garbage collector aliasing in the case where
+ // on a 64-bit system the int32 or uint32 field is written
+ // over the low half of a pointer, creating another pointer.
+ // TODO(rsc): When the garbage collector is completely
+ // precise, change back to int32.
+ nsec uintptr
// loc specifies the Location that should be used to
// determine the minute, hour, month, day, and year
@@ -424,6 +431,11 @@ func (t Time) YearDay() int {
// largest representable duration to approximately 290 years.
type Duration int64
+const (
+ minDuration Duration = -1 << 63
+ maxDuration Duration = 1<<63 - 1
+)
+
// Common durations. There is no definition for units of Day or larger
// to avoid confusion across daylight savings time zone transitions.
//
@@ -600,21 +612,33 @@ func (d Duration) Hours() float64 {
// Add returns the time t+d.
func (t Time) Add(d Duration) Time {
t.sec += int64(d / 1e9)
- t.nsec += int32(d % 1e9)
- if t.nsec >= 1e9 {
+ nsec := int32(t.nsec) + int32(d%1e9)
+ if nsec >= 1e9 {
t.sec++
- t.nsec -= 1e9
- } else if t.nsec < 0 {
+ nsec -= 1e9
+ } else if nsec < 0 {
t.sec--
- t.nsec += 1e9
+ nsec += 1e9
}
+ t.nsec = uintptr(nsec)
return t
}
-// Sub returns the duration t-u.
+// Sub returns the duration t-u. If the result exceeds the maximum (or minimum)
+// value that can be stored in a Duration, the maximum (or minimum) duration
+// will be returned.
// To compute t-d for a duration d, use t.Add(-d).
func (t Time) Sub(u Time) Duration {
- return Duration(t.sec-u.sec)*Second + Duration(t.nsec-u.nsec)
+ d := Duration(t.sec-u.sec)*Second + Duration(int32(t.nsec)-int32(u.nsec))
+ // Check for overflow or underflow.
+ switch {
+ case u.Add(d).Equal(t):
+ return d // d is correct
+ case t.Before(u):
+ return minDuration // t - u is negative out of range
+ default:
+ return maxDuration // t - u is positive out of range
+ }
}
// Since returns the time elapsed since t.
@@ -645,7 +669,6 @@ const (
daysPer400Years = 365*400 + 97
daysPer100Years = 365*100 + 24
daysPer4Years = 365*4 + 1
- days1970To2001 = 31*365 + 8
)
// date computes the year, day of year, and when full=true,
@@ -760,7 +783,7 @@ func now() (sec int64, nsec int32)
// Now returns the current local time.
func Now() Time {
sec, nsec := now()
- return Time{sec + unixToInternal, nsec, Local}
+ return Time{sec + unixToInternal, uintptr(nsec), Local}
}
// UTC returns t with the location set to UTC.
@@ -816,10 +839,10 @@ func (t Time) UnixNano() int64 {
return (t.sec+internalToUnix)*1e9 + int64(t.nsec)
}
-const timeGobVersion byte = 1
+const timeBinaryVersion byte = 1
-// GobEncode implements the gob.GobEncoder interface.
-func (t Time) GobEncode() ([]byte, error) {
+// MarshalBinary implements the encoding.BinaryMarshaler interface.
+func (t Time) MarshalBinary() ([]byte, error) {
var offsetMin int16 // minutes east of UTC. -1 is UTC.
if t.Location() == &utcLoc {
@@ -827,17 +850,17 @@ func (t Time) GobEncode() ([]byte, error) {
} else {
_, offset := t.Zone()
if offset%60 != 0 {
- return nil, errors.New("Time.GobEncode: zone offset has fractional minute")
+ return nil, errors.New("Time.MarshalBinary: zone offset has fractional minute")
}
offset /= 60
if offset < -32768 || offset == -1 || offset > 32767 {
- return nil, errors.New("Time.GobEncode: unexpected zone offset")
+ return nil, errors.New("Time.MarshalBinary: unexpected zone offset")
}
offsetMin = int16(offset)
}
enc := []byte{
- timeGobVersion, // byte 0 : version
+ timeBinaryVersion, // byte 0 : version
byte(t.sec >> 56), // bytes 1-8: seconds
byte(t.sec >> 48),
byte(t.sec >> 40),
@@ -857,18 +880,19 @@ func (t Time) GobEncode() ([]byte, error) {
return enc, nil
}
-// GobDecode implements the gob.GobDecoder interface.
-func (t *Time) GobDecode(buf []byte) error {
+// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface.
+func (t *Time) UnmarshalBinary(data []byte) error {
+ buf := data
if len(buf) == 0 {
- return errors.New("Time.GobDecode: no data")
+ return errors.New("Time.UnmarshalBinary: no data")
}
- if buf[0] != timeGobVersion {
- return errors.New("Time.GobDecode: unsupported version")
+ if buf[0] != timeBinaryVersion {
+ return errors.New("Time.UnmarshalBinary: unsupported version")
}
if len(buf) != /*version*/ 1+ /*sec*/ 8+ /*nsec*/ 4+ /*zone offset*/ 2 {
- return errors.New("Time.GobDecode: invalid length")
+ return errors.New("Time.UnmarshalBinary: invalid length")
}
buf = buf[1:]
@@ -876,7 +900,7 @@ func (t *Time) GobDecode(buf []byte) error {
int64(buf[3])<<32 | int64(buf[2])<<40 | int64(buf[1])<<48 | int64(buf[0])<<56
buf = buf[8:]
- t.nsec = int32(buf[3]) | int32(buf[2])<<8 | int32(buf[1])<<16 | int32(buf[0])<<24
+ t.nsec = uintptr(int32(buf[3]) | int32(buf[2])<<8 | int32(buf[1])<<16 | int32(buf[0])<<24)
buf = buf[4:]
offset := int(int16(buf[1])|int16(buf[0])<<8) * 60
@@ -892,8 +916,22 @@ func (t *Time) GobDecode(buf []byte) error {
return nil
}
+// TODO(rsc): Remove GobEncoder, GobDecoder, MarshalJSON, UnmarshalJSON in Go 2.
+// The same semantics will be provided by the generic MarshalBinary, MarshalText,
+// UnmarshalBinary, UnmarshalText.
+
+// GobEncode implements the gob.GobEncoder interface.
+func (t Time) GobEncode() ([]byte, error) {
+ return t.MarshalBinary()
+}
+
+// GobDecode implements the gob.GobDecoder interface.
+func (t *Time) GobDecode(data []byte) error {
+ return t.UnmarshalBinary(data)
+}
+
// MarshalJSON implements the json.Marshaler interface.
-// Time is formatted as RFC3339.
+// The time is a quoted string in RFC 3339 format, with sub-second precision added if present.
func (t Time) MarshalJSON() ([]byte, error) {
if y := t.Year(); y < 0 || y >= 10000 {
return nil, errors.New("Time.MarshalJSON: year outside of range [0,9999]")
@@ -902,13 +940,30 @@ func (t Time) MarshalJSON() ([]byte, error) {
}
// UnmarshalJSON implements the json.Unmarshaler interface.
-// Time is expected in RFC3339 format.
+// The time is expected to be a quoted string in RFC 3339 format.
func (t *Time) UnmarshalJSON(data []byte) (err error) {
// Fractional seconds are handled implicitly by Parse.
*t, err = Parse(`"`+RFC3339+`"`, string(data))
return
}
+// MarshalText implements the encoding.TextMarshaler interface.
+// The time is formatted in RFC 3339 format, with sub-second precision added if present.
+func (t Time) MarshalText() ([]byte, error) {
+ if y := t.Year(); y < 0 || y >= 10000 {
+ return nil, errors.New("Time.MarshalText: year outside of range [0,9999]")
+ }
+ return []byte(t.Format(RFC3339Nano)), nil
+}
+
+// UnmarshalText implements the encoding.TextUnmarshaler interface.
+// The time is expected to be in RFC 3339 format.
+func (t *Time) UnmarshalText(data []byte) (err error) {
+ // Fractional seconds are handled implicitly by Parse.
+ *t, err = Parse(RFC3339, string(data))
+ return
+}
+
// Unix returns the local Time corresponding to the given Unix time,
// sec seconds and nsec nanoseconds since January 1, 1970 UTC.
// It is valid to pass nsec outside the range [0, 999999999].
@@ -922,7 +977,7 @@ func Unix(sec int64, nsec int64) Time {
sec--
}
}
- return Time{sec + unixToInternal, int32(nsec), Local}
+ return Time{sec + unixToInternal, uintptr(nsec), Local}
}
func isLeap(year int) bool {
@@ -1031,7 +1086,7 @@ func Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) T
unix -= int64(offset)
}
- return Time{unix + unixToInternal, int32(nsec), loc}
+ return Time{unix + unixToInternal, uintptr(nsec), loc}
}
// Truncate returns the result of rounding t down to a multiple of d (since the zero time).
@@ -1063,13 +1118,14 @@ func (t Time) Round(d Duration) Time {
// but it's still here in case we change our minds.
func div(t Time, d Duration) (qmod2 int, r Duration) {
neg := false
+ nsec := int32(t.nsec)
if t.sec < 0 {
// Operate on absolute value.
neg = true
t.sec = -t.sec
- t.nsec = -t.nsec
- if t.nsec < 0 {
- t.nsec += 1e9
+ nsec = -nsec
+ if nsec < 0 {
+ nsec += 1e9
t.sec-- // t.sec >= 1 before the -- so safe
}
}
@@ -1077,14 +1133,14 @@ func div(t Time, d Duration) (qmod2 int, r Duration) {
switch {
// Special case: 2d divides 1 second.
case d < Second && Second%(d+d) == 0:
- qmod2 = int(t.nsec/int32(d)) & 1
- r = Duration(t.nsec % int32(d))
+ qmod2 = int(nsec/int32(d)) & 1
+ r = Duration(nsec % int32(d))
// Special case: d is a multiple of 1 second.
case d%Second == 0:
d1 := int64(d / Second)
qmod2 = int(t.sec/d1) & 1
- r = Duration(t.sec%d1)*Second + Duration(t.nsec)
+ r = Duration(t.sec%d1)*Second + Duration(nsec)
// General case.
// This could be faster if more cleverness were applied,
@@ -1101,7 +1157,7 @@ func div(t Time, d Duration) (qmod2 int, r Duration) {
if u0 < u0x {
u1++
}
- u0x, u0 = u0, u0+uint64(t.nsec)
+ u0x, u0 = u0, u0+uint64(nsec)
if u0 < u0x {
u1++
}
diff --git a/src/pkg/time/time_test.go b/src/pkg/time/time_test.go
index a0ee37ae3..334c4b0cf 100644
--- a/src/pkg/time/time_test.go
+++ b/src/pkg/time/time_test.go
@@ -413,6 +413,8 @@ var formatTests = []FormatTest{
{"am/pm", "3pm", "9pm"},
{"AM/PM", "3PM", "9PM"},
{"two-digit year", "06 01 02", "09 02 04"},
+ // Three-letter months and days must not be followed by lower-case letter.
+ {"Janet", "Hi Janet, the Month is January", "Hi Janet, the Month is February"},
// Time stamps, Fractional seconds.
{"Stamp", Stamp, "Feb 4 21:00:57"},
{"StampMilli", StampMilli, "Feb 4 21:00:57.012"},
@@ -505,6 +507,11 @@ var parseTests = []ParseTest{
// Leading zeros in other places should not be taken as fractional seconds.
{"zero1", "2006.01.02.15.04.05.0", "2010.02.04.21.00.57.0", false, false, 1, 1},
{"zero2", "2006.01.02.15.04.05.00", "2010.02.04.21.00.57.01", false, false, 1, 2},
+ // Month and day names only match when not followed by a lower-case letter.
+ {"Janet", "Hi Janet, the Month is January: Jan _2 15:04:05 2006", "Hi Janet, the Month is February: Feb 4 21:00:57 2010", false, true, 1, 0},
+
+ // GMT with offset.
+ {"GMT-8", UnixDate, "Fri Feb 5 05:00:57 GMT-8 2010", true, true, 1, 0},
// Accept any number of fractional second digits (including none) for .999...
// In Go 1, .999... was completely ignored in the format, meaning the first two
@@ -571,6 +578,16 @@ func TestParseInSydney(t *testing.T) {
}
}
+func TestLoadLocationZipFile(t *testing.T) {
+ ForceZipFileForTesting(true)
+ defer ForceZipFileForTesting(false)
+
+ _, err := LoadLocation("Australia/Sydney")
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
var rubyTests = []ParseTest{
{"RubyDate", RubyDate, "Thu Feb 04 21:00:57 -0800 2010", true, true, 1, 0},
// Ignore the time zone in the test. If it parses, it'll be OK.
@@ -659,6 +676,38 @@ func TestFormatAndParse(t *testing.T) {
}
}
+type ParseTimeZoneTest struct {
+ value string
+ length int
+ ok bool
+}
+
+var parseTimeZoneTests = []ParseTimeZoneTest{
+ {"gmt hi there", 0, false},
+ {"GMT hi there", 3, true},
+ {"GMT+12 hi there", 6, true},
+ {"GMT+00 hi there", 3, true}, // 0 or 00 is not a legal offset.
+ {"GMT-5 hi there", 5, true},
+ {"GMT-51 hi there", 3, true},
+ {"ChST hi there", 4, true},
+ {"MSDx", 3, true},
+ {"MSDY", 0, false}, // four letters must end in T.
+ {"ESAST hi", 5, true},
+ {"ESASTT hi", 0, false}, // run of upper-case letters too long.
+ {"ESATY hi", 0, false}, // five letters must end in T.
+}
+
+func TestParseTimeZone(t *testing.T) {
+ for _, test := range parseTimeZoneTests {
+ length, ok := ParseTimeZone(test.value)
+ if ok != test.ok {
+ t.Errorf("expected %t for %q got %t", test.ok, test.value, ok)
+ } else if length != test.length {
+ t.Errorf("expected %d for %q got %d", test.length, test.value, length)
+ }
+ }
+}
+
type ParseErrorTest struct {
format string
value string
@@ -781,6 +830,44 @@ func TestMinutesInTimeZone(t *testing.T) {
}
}
+type SecondsTimeZoneOffsetTest struct {
+ format string
+ value string
+ expectedoffset int
+}
+
+var secondsTimeZoneOffsetTests = []SecondsTimeZoneOffsetTest{
+ {"2006-01-02T15:04:05-070000", "1871-01-01T05:33:02-003408", -(34*60 + 8)},
+ {"2006-01-02T15:04:05-07:00:00", "1871-01-01T05:33:02-00:34:08", -(34*60 + 8)},
+ {"2006-01-02T15:04:05-070000", "1871-01-01T05:33:02+003408", 34*60 + 8},
+ {"2006-01-02T15:04:05-07:00:00", "1871-01-01T05:33:02+00:34:08", 34*60 + 8},
+ {"2006-01-02T15:04:05Z070000", "1871-01-01T05:33:02-003408", -(34*60 + 8)},
+ {"2006-01-02T15:04:05Z07:00:00", "1871-01-01T05:33:02+00:34:08", 34*60 + 8},
+}
+
+func TestParseSecondsInTimeZone(t *testing.T) {
+ // should accept timezone offsets with seconds like: Zone America/New_York -4:56:02 - LMT 1883 Nov 18 12:03:58
+ for _, test := range secondsTimeZoneOffsetTests {
+ time, err := Parse(test.format, test.value)
+ if err != nil {
+ t.Fatal("error parsing date:", err)
+ }
+ _, offset := time.Zone()
+ if offset != test.expectedoffset {
+ t.Errorf("ZoneOffset = %d, want %d", offset, test.expectedoffset)
+ }
+ }
+}
+
+func TestFormatSecondsInTimeZone(t *testing.T) {
+ d := Date(1871, 9, 17, 20, 4, 26, 0, FixedZone("LMT", -(34*60+8)))
+ timestr := d.Format("2006-01-02T15:04:05Z070000")
+ expected := "1871-09-17T20:04:26-003408"
+ if timestr != expected {
+ t.Errorf("Got %s, want %s", timestr, expected)
+ }
+}
+
type ISOWeekTest struct {
year int // year
month, day int // month and day
@@ -1106,9 +1193,9 @@ var invalidEncodingTests = []struct {
bytes []byte
want string
}{
- {[]byte{}, "Time.GobDecode: no data"},
- {[]byte{0, 2, 3}, "Time.GobDecode: unsupported version"},
- {[]byte{1, 2, 3}, "Time.GobDecode: invalid length"},
+ {[]byte{}, "Time.UnmarshalBinary: no data"},
+ {[]byte{0, 2, 3}, "Time.UnmarshalBinary: unsupported version"},
+ {[]byte{1, 2, 3}, "Time.UnmarshalBinary: invalid length"},
}
func TestInvalidTimeGob(t *testing.T) {
@@ -1118,6 +1205,10 @@ func TestInvalidTimeGob(t *testing.T) {
if err == nil || err.Error() != tt.want {
t.Errorf("time.GobDecode(%#v) error = %v, want %v", tt.bytes, err, tt.want)
}
+ err = ignored.UnmarshalBinary(tt.bytes)
+ if err == nil || err.Error() != tt.want {
+ t.Errorf("time.UnmarshalBinary(%#v) error = %v, want %v", tt.bytes, err, tt.want)
+ }
}
}
@@ -1125,10 +1216,10 @@ var notEncodableTimes = []struct {
time Time
want string
}{
- {Date(0, 1, 2, 3, 4, 5, 6, FixedZone("", 1)), "Time.GobEncode: zone offset has fractional minute"},
- {Date(0, 1, 2, 3, 4, 5, 6, FixedZone("", -1*60)), "Time.GobEncode: unexpected zone offset"},
- {Date(0, 1, 2, 3, 4, 5, 6, FixedZone("", -32769*60)), "Time.GobEncode: unexpected zone offset"},
- {Date(0, 1, 2, 3, 4, 5, 6, FixedZone("", 32768*60)), "Time.GobEncode: unexpected zone offset"},
+ {Date(0, 1, 2, 3, 4, 5, 6, FixedZone("", 1)), "Time.MarshalBinary: zone offset has fractional minute"},
+ {Date(0, 1, 2, 3, 4, 5, 6, FixedZone("", -1*60)), "Time.MarshalBinary: unexpected zone offset"},
+ {Date(0, 1, 2, 3, 4, 5, 6, FixedZone("", -32769*60)), "Time.MarshalBinary: unexpected zone offset"},
+ {Date(0, 1, 2, 3, 4, 5, 6, FixedZone("", 32768*60)), "Time.MarshalBinary: unexpected zone offset"},
}
func TestNotGobEncodableTime(t *testing.T) {
@@ -1137,6 +1228,10 @@ func TestNotGobEncodableTime(t *testing.T) {
if err == nil || err.Error() != tt.want {
t.Errorf("%v GobEncode error = %v, want %v", tt.time, err, tt.want)
}
+ _, err = tt.time.MarshalBinary()
+ if err == nil || err.Error() != tt.want {
+ t.Errorf("%v MarshalBinary error = %v, want %v", tt.time, err, tt.want)
+ }
}
}
@@ -1233,6 +1328,8 @@ var parseDurationTests = []struct {
{"39h9m14.425s", true, 39*Hour + 9*Minute + 14*Second + 425*Millisecond},
// large value
{"52763797000ns", true, 52763797000 * Nanosecond},
+ // more than 9 digits after decimal point, see http://golang.org/issue/6617
+ {"0.3333333333333333333h", true, 20 * Minute},
// errors
{"", false, 0},
@@ -1300,6 +1397,9 @@ var mallocTest = []struct {
}
func TestCountMallocs(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping malloc count in short mode")
+ }
if runtime.GOMAXPROCS(0) > 1 {
t.Skip("skipping; GOMAXPROCS>1")
}
@@ -1327,6 +1427,40 @@ func TestLoadFixed(t *testing.T) {
}
}
+const (
+ minDuration Duration = -1 << 63
+ maxDuration Duration = 1<<63 - 1
+)
+
+var subTests = []struct {
+ t Time
+ u Time
+ d Duration
+}{
+ {Time{}, Time{}, Duration(0)},
+ {Date(2009, 11, 23, 0, 0, 0, 1, UTC), Date(2009, 11, 23, 0, 0, 0, 0, UTC), Duration(1)},
+ {Date(2009, 11, 23, 0, 0, 0, 0, UTC), Date(2009, 11, 24, 0, 0, 0, 0, UTC), -24 * Hour},
+ {Date(2009, 11, 24, 0, 0, 0, 0, UTC), Date(2009, 11, 23, 0, 0, 0, 0, UTC), 24 * Hour},
+ {Date(-2009, 11, 24, 0, 0, 0, 0, UTC), Date(-2009, 11, 23, 0, 0, 0, 0, UTC), 24 * Hour},
+ {Time{}, Date(2109, 11, 23, 0, 0, 0, 0, UTC), Duration(minDuration)},
+ {Date(2109, 11, 23, 0, 0, 0, 0, UTC), Time{}, Duration(maxDuration)},
+ {Time{}, Date(-2109, 11, 23, 0, 0, 0, 0, UTC), Duration(maxDuration)},
+ {Date(-2109, 11, 23, 0, 0, 0, 0, UTC), Time{}, Duration(minDuration)},
+ {Date(2290, 1, 1, 0, 0, 0, 0, UTC), Date(2000, 1, 1, 0, 0, 0, 0, UTC), 290*365*24*Hour + 71*24*Hour},
+ {Date(2300, 1, 1, 0, 0, 0, 0, UTC), Date(2000, 1, 1, 0, 0, 0, 0, UTC), Duration(maxDuration)},
+ {Date(2000, 1, 1, 0, 0, 0, 0, UTC), Date(2290, 1, 1, 0, 0, 0, 0, UTC), -290*365*24*Hour - 71*24*Hour},
+ {Date(2000, 1, 1, 0, 0, 0, 0, UTC), Date(2300, 1, 1, 0, 0, 0, 0, UTC), Duration(minDuration)},
+}
+
+func TestSub(t *testing.T) {
+ for i, st := range subTests {
+ got := st.t.Sub(st.u)
+ if got != st.d {
+ t.Errorf("#%d: Sub(%v, %v): got %v; want %v", i, st.t, st.u, got, st.d)
+ }
+ }
+}
+
func BenchmarkNow(b *testing.B) {
for i := 0; i < b.N; i++ {
t = Now()
diff --git a/src/pkg/time/zoneinfo.go b/src/pkg/time/zoneinfo.go
index c44477f47..1c6186258 100644
--- a/src/pkg/time/zoneinfo.go
+++ b/src/pkg/time/zoneinfo.go
@@ -178,19 +178,6 @@ func (l *Location) lookupName(name string, unix int64) (offset int, isDST bool,
return
}
-// lookupOffset returns information about the time zone with
-// the given offset (such as -5*60*60).
-func (l *Location) lookupOffset(offset int) (name string, isDST bool, ok bool) {
- l = l.get()
- for i := range l.zone {
- zone := &l.zone[i]
- if zone.offset == offset {
- return zone.name, zone.isDST, true
- }
- }
- return
-}
-
// NOTE(rsc): Eventually we will need to accept the POSIX TZ environment
// syntax too, but I don't feel like implementing it today.
diff --git a/src/pkg/time/zoneinfo_abbrs_windows.go b/src/pkg/time/zoneinfo_abbrs_windows.go
new file mode 100644
index 000000000..80334371f
--- /dev/null
+++ b/src/pkg/time/zoneinfo_abbrs_windows.go
@@ -0,0 +1,115 @@
+// Copyright 2013 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.
+
+// generated by genzabbrs.go from
+// http://unicode.org/cldr/data/common/supplemental/windowsZones.xml
+
+package time
+
+type abbr struct {
+ std string
+ dst string
+}
+
+var abbrs = map[string]abbr{
+ "Egypt Standard Time": {"EET", "EET"}, // Africa/Cairo
+ "Morocco Standard Time": {"WET", "WEST"}, // Africa/Casablanca
+ "South Africa Standard Time": {"SAST", "SAST"}, // Africa/Johannesburg
+ "W. Central Africa Standard Time": {"WAT", "WAT"}, // Africa/Lagos
+ "E. Africa Standard Time": {"EAT", "EAT"}, // Africa/Nairobi
+ "Namibia Standard Time": {"WAT", "WAST"}, // Africa/Windhoek
+ "Alaskan Standard Time": {"AKST", "AKDT"}, // America/Anchorage
+ "Paraguay Standard Time": {"PYT", "PYST"}, // America/Asuncion
+ "Bahia Standard Time": {"BRT", "BRST"}, // America/Bahia
+ "SA Pacific Standard Time": {"COT", "COT"}, // America/Bogota
+ "Argentina Standard Time": {"ART", "ART"}, // America/Buenos_Aires
+ "Venezuela Standard Time": {"VET", "VET"}, // America/Caracas
+ "SA Eastern Standard Time": {"GFT", "GFT"}, // America/Cayenne
+ "Central Standard Time": {"CST", "CDT"}, // America/Chicago
+ "Mountain Standard Time (Mexico)": {"MST", "MDT"}, // America/Chihuahua
+ "Central Brazilian Standard Time": {"AMT", "AMST"}, // America/Cuiaba
+ "Mountain Standard Time": {"MST", "MDT"}, // America/Denver
+ "Greenland Standard Time": {"WGT", "WGST"}, // America/Godthab
+ "Central America Standard Time": {"CST", "CST"}, // America/Guatemala
+ "Atlantic Standard Time": {"AST", "ADT"}, // America/Halifax
+ "US Eastern Standard Time": {"EST", "EDT"}, // America/Indianapolis
+ "SA Western Standard Time": {"BOT", "BOT"}, // America/La_Paz
+ "Pacific Standard Time": {"PST", "PDT"}, // America/Los_Angeles
+ "Central Standard Time (Mexico)": {"CST", "CDT"}, // America/Mexico_City
+ "Montevideo Standard Time": {"UYT", "UYST"}, // America/Montevideo
+ "Eastern Standard Time": {"EST", "EDT"}, // America/New_York
+ "US Mountain Standard Time": {"MST", "MST"}, // America/Phoenix
+ "Canada Central Standard Time": {"CST", "CST"}, // America/Regina
+ "Pacific Standard Time (Mexico)": {"PST", "PDT"}, // America/Santa_Isabel
+ "Pacific SA Standard Time": {"CLT", "CLST"}, // America/Santiago
+ "E. South America Standard Time": {"BRT", "BRST"}, // America/Sao_Paulo
+ "Newfoundland Standard Time": {"NST", "NDT"}, // America/St_Johns
+ "Central Asia Standard Time": {"ALMT", "ALMT"}, // Asia/Almaty
+ "Jordan Standard Time": {"EET", "EEST"}, // Asia/Amman
+ "Arabic Standard Time": {"AST", "AST"}, // Asia/Baghdad
+ "Azerbaijan Standard Time": {"AZT", "AZST"}, // Asia/Baku
+ "SE Asia Standard Time": {"ICT", "ICT"}, // Asia/Bangkok
+ "Middle East Standard Time": {"EET", "EEST"}, // Asia/Beirut
+ "India Standard Time": {"IST", "IST"}, // Asia/Calcutta
+ "Sri Lanka Standard Time": {"IST", "IST"}, // Asia/Colombo
+ "Syria Standard Time": {"EET", "EEST"}, // Asia/Damascus
+ "Bangladesh Standard Time": {"BDT", "BDT"}, // Asia/Dhaka
+ "Arabian Standard Time": {"GST", "GST"}, // Asia/Dubai
+ "North Asia East Standard Time": {"IRKT", "IRKT"}, // Asia/Irkutsk
+ "Israel Standard Time": {"IST", "IDT"}, // Asia/Jerusalem
+ "Afghanistan Standard Time": {"AFT", "AFT"}, // Asia/Kabul
+ "Pakistan Standard Time": {"PKT", "PKT"}, // Asia/Karachi
+ "Nepal Standard Time": {"NPT", "NPT"}, // Asia/Katmandu
+ "North Asia Standard Time": {"KRAT", "KRAT"}, // Asia/Krasnoyarsk
+ "Magadan Standard Time": {"MAGT", "MAGT"}, // Asia/Magadan
+ "E. Europe Standard Time": {"EET", "EEST"}, // Asia/Nicosia
+ "N. Central Asia Standard Time": {"NOVT", "NOVT"}, // Asia/Novosibirsk
+ "Myanmar Standard Time": {"MMT", "MMT"}, // Asia/Rangoon
+ "Arab Standard Time": {"AST", "AST"}, // Asia/Riyadh
+ "Korea Standard Time": {"KST", "KST"}, // Asia/Seoul
+ "China Standard Time": {"CST", "CST"}, // Asia/Shanghai
+ "Singapore Standard Time": {"SGT", "SGT"}, // Asia/Singapore
+ "Taipei Standard Time": {"CST", "CST"}, // Asia/Taipei
+ "West Asia Standard Time": {"UZT", "UZT"}, // Asia/Tashkent
+ "Georgian Standard Time": {"GET", "GET"}, // Asia/Tbilisi
+ "Iran Standard Time": {"IRST", "IRDT"}, // Asia/Tehran
+ "Tokyo Standard Time": {"JST", "JST"}, // Asia/Tokyo
+ "Ulaanbaatar Standard Time": {"ULAT", "ULAT"}, // Asia/Ulaanbaatar
+ "Vladivostok Standard Time": {"VLAT", "VLAT"}, // Asia/Vladivostok
+ "Yakutsk Standard Time": {"YAKT", "YAKT"}, // Asia/Yakutsk
+ "Ekaterinburg Standard Time": {"YEKT", "YEKT"}, // Asia/Yekaterinburg
+ "Caucasus Standard Time": {"AMT", "AMT"}, // Asia/Yerevan
+ "Azores Standard Time": {"AZOT", "AZOST"}, // Atlantic/Azores
+ "Cape Verde Standard Time": {"CVT", "CVT"}, // Atlantic/Cape_Verde
+ "Greenwich Standard Time": {"GMT", "GMT"}, // Atlantic/Reykjavik
+ "Cen. Australia Standard Time": {"CST", "CST"}, // Australia/Adelaide
+ "E. Australia Standard Time": {"EST", "EST"}, // Australia/Brisbane
+ "AUS Central Standard Time": {"CST", "CST"}, // Australia/Darwin
+ "Tasmania Standard Time": {"EST", "EST"}, // Australia/Hobart
+ "W. Australia Standard Time": {"WST", "WST"}, // Australia/Perth
+ "AUS Eastern Standard Time": {"EST", "EST"}, // Australia/Sydney
+ "UTC": {"GMT", "GMT"}, // Etc/GMT
+ "UTC-11": {"GMT+11", "GMT+11"}, // Etc/GMT+11
+ "Dateline Standard Time": {"GMT+12", "GMT+12"}, // Etc/GMT+12
+ "UTC-02": {"GMT+2", "GMT+2"}, // Etc/GMT+2
+ "UTC+12": {"GMT-12", "GMT-12"}, // Etc/GMT-12
+ "W. Europe Standard Time": {"CET", "CEST"}, // Europe/Berlin
+ "GTB Standard Time": {"EET", "EEST"}, // Europe/Bucharest
+ "Central Europe Standard Time": {"CET", "CEST"}, // Europe/Budapest
+ "Turkey Standard Time": {"EET", "EEST"}, // Europe/Istanbul
+ "Kaliningrad Standard Time": {"FET", "FET"}, // Europe/Kaliningrad
+ "FLE Standard Time": {"EET", "EEST"}, // Europe/Kiev
+ "GMT Standard Time": {"GMT", "BST"}, // Europe/London
+ "Russian Standard Time": {"MSK", "MSK"}, // Europe/Moscow
+ "Romance Standard Time": {"CET", "CEST"}, // Europe/Paris
+ "Central European Standard Time": {"CET", "CEST"}, // Europe/Warsaw
+ "Mauritius Standard Time": {"MUT", "MUT"}, // Indian/Mauritius
+ "Samoa Standard Time": {"WST", "WST"}, // Pacific/Apia
+ "New Zealand Standard Time": {"NZST", "NZDT"}, // Pacific/Auckland
+ "Fiji Standard Time": {"FJT", "FJT"}, // Pacific/Fiji
+ "Central Pacific Standard Time": {"SBT", "SBT"}, // Pacific/Guadalcanal
+ "Hawaiian Standard Time": {"HST", "HST"}, // Pacific/Honolulu
+ "West Pacific Standard Time": {"PGT", "PGT"}, // Pacific/Port_Moresby
+ "Tonga Standard Time": {"TOT", "TOT"}, // Pacific/Tongatapu
+}
diff --git a/src/pkg/time/zoneinfo_plan9.go b/src/pkg/time/zoneinfo_plan9.go
index 6855238dc..0e8f3811b 100644
--- a/src/pkg/time/zoneinfo_plan9.go
+++ b/src/pkg/time/zoneinfo_plan9.go
@@ -154,3 +154,7 @@ func loadLocation(name string) (*Location, error) {
}
return nil, errors.New("unknown time zone " + name)
}
+
+func forceZipFileForTesting(zipOnly bool) {
+ // We only use the zip file anyway.
+}
diff --git a/src/pkg/time/zoneinfo_read.go b/src/pkg/time/zoneinfo_read.go
index 4519c9962..7714aa9f5 100644
--- a/src/pkg/time/zoneinfo_read.go
+++ b/src/pkg/time/zoneinfo_read.go
@@ -11,10 +11,6 @@ package time
import "errors"
-const (
- headerSize = 4 + 16 + 4*7
-)
-
// Simple I/O interface to binary blob of data.
type data struct {
p []byte
diff --git a/src/pkg/time/zoneinfo_unix.go b/src/pkg/time/zoneinfo_unix.go
index 2c951a983..fc5ae89fe 100644
--- a/src/pkg/time/zoneinfo_unix.go
+++ b/src/pkg/time/zoneinfo_unix.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.
-// +build darwin freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd
// Parse "zoneinfo" time zone file.
// This is a fairly standard file format used on OS X, Linux, BSD, Sun, and others.
@@ -32,7 +32,19 @@ var zoneDirs = []string{
"/usr/share/zoneinfo/",
"/usr/share/lib/zoneinfo/",
"/usr/lib/locale/TZ/",
- runtime.GOROOT() + "/lib/time/zoneinfo/",
+ runtime.GOROOT() + "/lib/time/zoneinfo.zip",
+}
+
+var origZoneDirs = zoneDirs
+
+func forceZipFileForTesting(zipOnly bool) {
+ zoneDirs = make([]string, len(origZoneDirs))
+ copy(zoneDirs, origZoneDirs)
+ if zipOnly {
+ for i := 0; i < len(zoneDirs)-1; i++ {
+ zoneDirs[i] = "/XXXNOEXIST"
+ }
+ }
}
func initLocal() {
diff --git a/src/pkg/time/zoneinfo_windows.go b/src/pkg/time/zoneinfo_windows.go
index a8d3dcbfe..be4e5c13f 100644
--- a/src/pkg/time/zoneinfo_windows.go
+++ b/src/pkg/time/zoneinfo_windows.go
@@ -8,6 +8,7 @@ import (
"errors"
"runtime"
"syscall"
+ "unsafe"
)
// TODO(rsc): Fall back to copy of zoneinfo files.
@@ -16,21 +17,83 @@ import (
// time zone information.
// The implementation assumes that this year's rules for daylight savings
// time apply to all previous and future years as well.
-// Also, time zone abbreviations are unavailable. The implementation constructs
-// them using the capital letters from a longer time zone description.
-
-// abbrev returns the abbreviation to use for the given zone name.
-func abbrev(name []uint16) string {
- // name is 'Pacific Standard Time' but we want 'PST'.
- // Extract just capital letters. It's not perfect but the
- // information we need is not available from the kernel.
- // Because time zone abbreviations are not unique,
- // Windows refuses to expose them.
- //
- // http://social.msdn.microsoft.com/Forums/eu/vclanguage/thread/a87e1d25-fb71-4fe0-ae9c-a9578c9753eb
- // http://stackoverflow.com/questions/4195948/windows-time-zone-abbreviations-in-asp-net
+
+// getKeyValue retrieves the string value kname associated with the open registry key kh.
+func getKeyValue(kh syscall.Handle, kname string) (string, error) {
+ var buf [50]uint16 // buf needs to be large enough to fit zone descriptions
+ var typ uint32
+ n := uint32(len(buf) * 2) // RegQueryValueEx's signature expects array of bytes, not uint16
+ p, _ := syscall.UTF16PtrFromString(kname)
+ if err := syscall.RegQueryValueEx(kh, p, nil, &typ, (*byte)(unsafe.Pointer(&buf[0])), &n); err != nil {
+ return "", err
+ }
+ if typ != syscall.REG_SZ { // null terminated strings only
+ return "", errors.New("Key is not string")
+ }
+ return syscall.UTF16ToString(buf[:]), nil
+}
+
+// matchZoneKey checks if stdname and dstname match the corresponding "Std"
+// and "Dlt" key values in the kname key stored under the open registry key zones.
+func matchZoneKey(zones syscall.Handle, kname string, stdname, dstname string) (matched bool, err2 error) {
+ var h syscall.Handle
+ p, _ := syscall.UTF16PtrFromString(kname)
+ if err := syscall.RegOpenKeyEx(zones, p, 0, syscall.KEY_READ, &h); err != nil {
+ return false, err
+ }
+ defer syscall.RegCloseKey(h)
+
+ s, err := getKeyValue(h, "Std")
+ if err != nil {
+ return false, err
+ }
+ if s != stdname {
+ return false, nil
+ }
+ s, err = getKeyValue(h, "Dlt")
+ if err != nil {
+ return false, err
+ }
+ if s != dstname {
+ return false, nil
+ }
+ return true, nil
+}
+
+// toEnglishName searches the registry for an English name of a time zone
+// whose zone names are stdname and dstname and returns the English name.
+func toEnglishName(stdname, dstname string) (string, error) {
+ var zones syscall.Handle
+ p, _ := syscall.UTF16PtrFromString(`SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones`)
+ if err := syscall.RegOpenKeyEx(syscall.HKEY_LOCAL_MACHINE, p, 0, syscall.KEY_READ, &zones); err != nil {
+ return "", err
+ }
+ defer syscall.RegCloseKey(zones)
+
+ var count uint32
+ if err := syscall.RegQueryInfoKey(zones, nil, nil, nil, &count, nil, nil, nil, nil, nil, nil, nil); err != nil {
+ return "", err
+ }
+
+ var buf [50]uint16 // buf needs to be large enough to fit zone descriptions
+ for i := uint32(0); i < count; i++ {
+ n := uint32(len(buf))
+ if syscall.RegEnumKeyEx(zones, i, &buf[0], &n, nil, nil, nil, nil) != nil {
+ continue
+ }
+ kname := syscall.UTF16ToString(buf[:])
+ matched, err := matchZoneKey(zones, kname, stdname, dstname)
+ if err == nil && matched {
+ return kname, nil
+ }
+ }
+ return "", errors.New(`English name for time zone "` + stdname + `" not found in registry`)
+}
+
+// extractCAPS exracts capital letters from description desc.
+func extractCAPS(desc string) string {
var short []rune
- for _, c := range name {
+ for _, c := range desc {
if 'A' <= c && c <= 'Z' {
short = append(short, rune(c))
}
@@ -38,6 +101,26 @@ func abbrev(name []uint16) string {
return string(short)
}
+// abbrev returns the abbreviations to use for the given zone z.
+func abbrev(z *syscall.Timezoneinformation) (std, dst string) {
+ stdName := syscall.UTF16ToString(z.StandardName[:])
+ a, ok := abbrs[stdName]
+ if !ok {
+ dstName := syscall.UTF16ToString(z.DaylightName[:])
+ // Perhaps stdName is not English. Try to convert it.
+ englishName, err := toEnglishName(stdName, dstName)
+ if err == nil {
+ a, ok = abbrs[englishName]
+ if ok {
+ return a.std, a.dst
+ }
+ }
+ // fallback to using capital letters
+ return extractCAPS(stdName), extractCAPS(dstName)
+ }
+ return a.std, a.dst
+}
+
// pseudoUnix returns the pseudo-Unix time (seconds since Jan 1 1970 *LOCAL TIME*)
// denoted by the system date+time d in the given year.
// It is up to the caller to convert this local time into a UTC-based time.
@@ -75,8 +158,10 @@ func initLocalFromTZI(i *syscall.Timezoneinformation) {
}
l.zone = make([]zone, nzone)
+ stdname, dstname := abbrev(i)
+
std := &l.zone[0]
- std.name = abbrev(i.StandardName[0:])
+ std.name = stdname
if nzone == 1 {
// No daylight savings.
std.offset = -int(i.Bias) * 60
@@ -95,7 +180,7 @@ func initLocalFromTZI(i *syscall.Timezoneinformation) {
std.offset = -int(i.Bias+i.StandardBias) * 60
dst := &l.zone[1]
- dst.name = abbrev(i.DaylightName[0:])
+ dst.name = dstname
dst.offset = -int(i.Bias+i.DaylightBias) * 60
dst.isDST = true
@@ -142,10 +227,27 @@ var usPacific = syscall.Timezoneinformation{
DaylightBias: -60,
}
+var aus = syscall.Timezoneinformation{
+ Bias: -10 * 60,
+ StandardName: [32]uint16{
+ 'A', 'U', 'S', ' ', 'E', 'a', 's', 't', 'e', 'r', 'n', ' ', 'S', 't', 'a', 'n', 'd', 'a', 'r', 'd', ' ', 'T', 'i', 'm', 'e',
+ },
+ StandardDate: syscall.Systemtime{Month: 4, Day: 1, Hour: 3},
+ DaylightName: [32]uint16{
+ 'A', 'U', 'S', ' ', 'E', 'a', 's', 't', 'e', 'r', 'n', ' ', 'D', 'a', 'y', 'l', 'i', 'g', 'h', 't', ' ', 'T', 'i', 'm', 'e',
+ },
+ DaylightDate: syscall.Systemtime{Month: 10, Day: 1, Hour: 2},
+ DaylightBias: -60,
+}
+
func initTestingZone() {
initLocalFromTZI(&usPacific)
}
+func initAusTestingZone() {
+ initLocalFromTZI(&aus)
+}
+
func initLocal() {
var i syscall.Timezoneinformation
if _, err := syscall.GetTimeZoneInformation(&i); err != nil {
@@ -162,3 +264,7 @@ func loadLocation(name string) (*Location, error) {
}
return nil, errors.New("unknown time zone " + name)
}
+
+func forceZipFileForTesting(zipOnly bool) {
+ // We only use the zip file anyway.
+}
diff --git a/src/pkg/time/zoneinfo_windows_test.go b/src/pkg/time/zoneinfo_windows_test.go
new file mode 100644
index 000000000..9db81b7cf
--- /dev/null
+++ b/src/pkg/time/zoneinfo_windows_test.go
@@ -0,0 +1,35 @@
+// Copyright 2013 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 time_test
+
+import (
+ "testing"
+ . "time"
+)
+
+func testZoneAbbr(t *testing.T) {
+ t1 := Now()
+ // discard nsec
+ t1 = Date(t1.Year(), t1.Month(), t1.Day(), t1.Hour(), t1.Minute(), t1.Second(), 0, t1.Location())
+ t2, err := Parse(RFC1123, t1.Format(RFC1123))
+ if err != nil {
+ t.Fatalf("Parse failed: %v", err)
+ }
+ if t1 != t2 {
+ t.Fatalf("t1 (%v) is not equal to t2 (%v)", t1, t2)
+ }
+}
+
+func TestLocalZoneAbbr(t *testing.T) {
+ ResetLocalOnceForTest() // reset the Once to trigger the race
+ defer ForceUSPacificForTesting()
+ testZoneAbbr(t)
+}
+
+func TestAusZoneAbbr(t *testing.T) {
+ ForceAusForTesting()
+ defer ForceUSPacificForTesting()
+ testZoneAbbr(t)
+}
diff --git a/src/pkg/unicode/graphic.go b/src/pkg/unicode/graphic.go
index 5b995fcd0..ba90b4e51 100644
--- a/src/pkg/unicode/graphic.go
+++ b/src/pkg/unicode/graphic.go
@@ -39,7 +39,7 @@ func IsGraphic(r rune) bool {
if uint32(r) <= MaxLatin1 {
return properties[uint8(r)]&pg != 0
}
- return IsOneOf(GraphicRanges, r)
+ return In(r, GraphicRanges...)
}
// IsPrint reports whether the rune is defined as printable by Go. Such
@@ -51,12 +51,23 @@ func IsPrint(r rune) bool {
if uint32(r) <= MaxLatin1 {
return properties[uint8(r)]&pp != 0
}
- return IsOneOf(PrintRanges, r)
+ return In(r, PrintRanges...)
}
// IsOneOf reports whether the rune is a member of one of the ranges.
-func IsOneOf(set []*RangeTable, r rune) bool {
- for _, inside := range set {
+// The function "In" provides a nicer signature and should be used in preference to IsOneOf.
+func IsOneOf(ranges []*RangeTable, r rune) bool {
+ for _, inside := range ranges {
+ if Is(inside, r) {
+ return true
+ }
+ }
+ return false
+}
+
+// In reports whether the rune is a member of one of the ranges.
+func In(r rune, ranges ...*RangeTable) bool {
+ for _, inside := range ranges {
if Is(inside, r) {
return true
}
diff --git a/src/pkg/unicode/graphic_test.go b/src/pkg/unicode/graphic_test.go
index 7b1f6209e..c9f289c7f 100644
--- a/src/pkg/unicode/graphic_test.go
+++ b/src/pkg/unicode/graphic_test.go
@@ -71,7 +71,7 @@ func TestNumberLatin1(t *testing.T) {
func TestIsPrintLatin1(t *testing.T) {
for i := rune(0); i <= MaxLatin1; i++ {
got := IsPrint(i)
- want := IsOneOf(PrintRanges, i)
+ want := In(i, PrintRanges...)
if i == ' ' {
want = true
}
@@ -84,7 +84,7 @@ func TestIsPrintLatin1(t *testing.T) {
func TestIsGraphicLatin1(t *testing.T) {
for i := rune(0); i <= MaxLatin1; i++ {
got := IsGraphic(i)
- want := IsOneOf(GraphicRanges, i)
+ want := In(i, GraphicRanges...)
if got != want {
t.Errorf("%U incorrect: got %t; want %t", i, got, want)
}
diff --git a/src/race.bash b/src/race.bash
index 3cdc5e10c..18201f992 100755
--- a/src/race.bash
+++ b/src/race.bash
@@ -35,6 +35,8 @@ if [ ! -f make.bash ]; then
exit 1
fi
. ./make.bash --no-banner
+# golang.org/issue/5537 - we must build a race enabled cmd/cgo before trying to use it.
+go install -race cmd/cgo
go install -race std
go test -race -short std
go test -race -run=nothingplease -bench=.* -benchtime=.1s -cpu=4 std
diff --git a/src/race.bat b/src/race.bat
index 989a2e7d8..0a6aee9e2 100644
--- a/src/race.bat
+++ b/src/race.bat
@@ -29,6 +29,9 @@ goto fail
:continue
call make.bat --no-banner --no-local
if %GOBUILDFAIL%==1 goto end
+:: golang.org/issue/5537 - we must build a race enabled cmd/cgo before trying to use it.
+echo # go install -race cmd/cgo
+go install -race cmd/cgo
echo # go install -race std
go install -race std
if errorlevel 1 goto fail
diff --git a/src/run.bash b/src/run.bash
index 305ff7f41..6adb7f63d 100755
--- a/src/run.bash
+++ b/src/run.bash
@@ -20,6 +20,11 @@ ulimit -c 0
[ "$(ulimit -H -n)" == "unlimited" ] || ulimit -S -n $(ulimit -H -n)
[ "$(ulimit -H -d)" == "unlimited" ] || ulimit -S -d $(ulimit -H -d)
+# Thread count limit on NetBSD 7.
+if ulimit -T &> /dev/null; then
+ [ "$(ulimit -H -T)" == "unlimited" ] || ulimit -S -T $(ulimit -H -T)
+fi
+
# allow all.bash to avoid double-build of everything
rebuild=true
if [ "$1" = "--no-rebuild" ]; then
@@ -44,7 +49,7 @@ time go test std -short -timeout=$(expr 120 \* $timeout_scale)s
echo
echo '# GOMAXPROCS=2 runtime -cpu=1,2,4'
-GOMAXPROCS=2 go test runtime -short -timeout=$(expr 240 \* $timeout_scale)s -cpu=1,2,4
+GOMAXPROCS=2 go test runtime -short -timeout=$(expr 300 \* $timeout_scale)s -cpu=1,2,4
echo
echo '# sync -cpu=10'
@@ -56,101 +61,136 @@ case "$GOHOSTOS-$GOOS-$GOARCH-$CGO_ENABLED" in
linux-linux-amd64-1 | darwin-darwin-amd64-1)
echo
echo '# Testing race detector.'
- go test -race -i flag
+ go test -race -i runtime/race flag
+ go test -race -run=Output runtime/race
go test -race -short flag
esac
xcd() {
echo
echo '#' $1
- builtin cd "$GOROOT"/src/$1
+ builtin cd "$GOROOT"/src/$1 || exit 1
}
+# NOTE: "set -e" cannot help us in subshells. It works until you test it with ||.
+#
+# $ bash --version
+# GNU bash, version 3.2.48(1)-release (x86_64-apple-darwin12)
+# Copyright (C) 2007 Free Software Foundation, Inc.
+#
+# $ set -e; (set -e; false; echo still here); echo subshell exit status $?
+# subshell exit status 1
+# # subshell stopped early, set exit status, but outer set -e didn't stop.
+#
+# $ set -e; (set -e; false; echo still here) || echo stopped
+# still here
+# # somehow the '|| echo stopped' broke the inner set -e.
+#
+# To avoid this bug, every command in a subshell should have '|| exit 1' on it.
+# Strictly speaking, the test may be unnecessary on the final command of
+# the subshell, but it aids later editing and may avoid future bash bugs.
+
[ "$CGO_ENABLED" != 1 ] ||
[ "$GOHOSTOS" == windows ] ||
(xcd ../misc/cgo/stdio
-go run $GOROOT/test/run.go - .
+go run $GOROOT/test/run.go - . || exit 1
) || exit $?
[ "$CGO_ENABLED" != 1 ] ||
(xcd ../misc/cgo/life
-go run $GOROOT/test/run.go - .
+go run $GOROOT/test/run.go - . || exit 1
) || exit $?
[ "$CGO_ENABLED" != 1 ] ||
(xcd ../misc/cgo/test
-set -e
-go test -ldflags '-linkmode=auto'
-go test -ldflags '-linkmode=internal'
+go test -ldflags '-linkmode=auto' || exit 1
+# linkmode=internal fails on dragonfly since errno is a TLS relocation.
+[ "$GOHOSTOS" == dragonfly ] || go test -ldflags '-linkmode=internal' || exit 1
case "$GOHOSTOS-$GOARCH" in
openbsd-386 | openbsd-amd64)
# test linkmode=external, but __thread not supported, so skip testtls.
- go test -ldflags '-linkmode=external'
+ go test -ldflags '-linkmode=external' || exit 1
;;
darwin-386 | darwin-amd64)
# linkmode=external fails on OS X 10.6 and earlier == Darwin
# 10.8 and earlier.
case $(uname -r) in
[0-9].* | 10.*) ;;
- *) go test -ldflags '-linkmode=external' ;;
+ *) go test -ldflags '-linkmode=external' || exit 1;;
esac
;;
-freebsd-386 | freebsd-amd64 | linux-386 | linux-amd64 | netbsd-386 | netbsd-amd64)
- go test -ldflags '-linkmode=external'
- go test -ldflags '-linkmode=auto' ../testtls
- go test -ldflags '-linkmode=external' ../testtls
+dragonfly-386 | dragonfly-amd64 | freebsd-386 | freebsd-amd64 | linux-386 | linux-amd64 | linux-arm | netbsd-386 | netbsd-amd64)
+ go test -ldflags '-linkmode=external' || exit 1
+ go test -ldflags '-linkmode=auto' ../testtls || exit 1
+ go test -ldflags '-linkmode=external' ../testtls || exit 1
esac
) || exit $?
+# This tests cgo -godefs. That mode is not supported,
+# so it's okay if it doesn't work on some systems.
+# In particular, it works badly with clang on OS X.
+[ "$CGO_ENABLED" != 1 ] || [ "$GOOS" == darwin ] ||
+(xcd ../misc/cgo/testcdefs
+./test.bash || exit 1
+) || exit $?
+
[ "$CGO_ENABLED" != 1 ] ||
[ "$GOHOSTOS" == windows ] ||
-[ "$GOHOSTOS" == darwin ] ||
(xcd ../misc/cgo/testso
-./test.bash
+./test.bash || exit 1
+) || exit $?
+
+[ "$CGO_ENABLED" != 1 ] ||
+[ "$GOHOSTOS-$GOARCH" != linux-amd64 ] ||
+(xcd ../misc/cgo/testasan
+go run main.go || exit 1
+) || exit $?
+
+[ "$CGO_ENABLED" != 1 ] ||
+[ "$GOHOSTOS" == windows ] ||
+(xcd ../misc/cgo/errors
+./test.bash || exit 1
) || exit $?
(xcd ../doc/progs
-time ./run
+time ./run || exit 1
) || exit $?
[ "$GOARCH" == arm ] || # uses network, fails under QEMU
(xcd ../doc/articles/wiki
-make clean
-./test.bash
+make clean || exit 1
+./test.bash || exit 1
) || exit $?
(xcd ../doc/codewalk
-# TODO: test these too.
-set -e
-go build pig.go
-go build urlpoll.go
-rm -f pig urlpoll
+time ./run || exit 1
) || exit $?
echo
-echo '#' ../misc/dashboard/builder ../misc/goplay
-go build ../misc/dashboard/builder ../misc/goplay
+echo '#' ../misc/goplay
+go build ../misc/goplay
+rm -f goplay
[ "$GOARCH" == arm ] ||
(xcd ../test/bench/shootout
-./timing.sh -test
+./timing.sh -test || exit 1
) || exit $?
[ "$GOOS" == openbsd ] || # golang.org/issue/5057
(
echo
echo '#' ../test/bench/go1
-go test ../test/bench/go1
+go test ../test/bench/go1 || exit 1
) || exit $?
(xcd ../test
unset GOMAXPROCS
-time go run run.go
+time go run run.go || exit 1
) || exit $?
echo
echo '# Checking API compatibility.'
-go tool api -c $GOROOT/api/go1.txt,$GOROOT/api/go1.1.txt -next $GOROOT/api/next.txt -except $GOROOT/api/except.txt
+time go run $GOROOT/src/cmd/api/run.go
echo
echo ALL TESTS PASSED
diff --git a/src/run.bat b/src/run.bat
index 6f8ab0e7d..48f6711ff 100644
--- a/src/run.bat
+++ b/src/run.bat
@@ -43,7 +43,7 @@ if errorlevel 1 goto fail
echo.
echo # runtime -cpu=1,2,4
-go test runtime -short -timeout=240s -cpu=1,2,4
+go test runtime -short -timeout=300s -cpu=1,2,4
if errorlevel 1 goto fail
echo.
@@ -54,15 +54,17 @@ echo.
if not "%GOHOSTOS%-%GOOS%-%GOARCH%-%CGO_ENABLED%" == "windows-windows-amd64-1" goto norace
echo # Testing race detector.
-go test -race -i flag
+go test -race -i runtime/race flag
+if errorlevel 1 goto fail
+go test -race -run=Output runtime/race
if errorlevel 1 goto fail
go test -race -short flag
if errorlevel 1 goto fail
echo.
:norace
-echo # ..\misc\dashboard\builder ..\misc\goplay
-go build ..\misc\dashboard\builder ..\misc\goplay
+echo # ..\misc\goplay
+go build ..\misc\goplay
if errorlevel 1 goto fail
echo.
@@ -121,7 +123,7 @@ set GOMAXPROCS=%OLDGOMAXPROCS%
set OLDGOMAXPROCS=
echo # Checking API compatibility.
-go tool api -c ..\api\go1.txt,..\api\go1.1.txt -next ..\api\next.txt -except ..\api\except.txt
+go run "%GOROOT%\src\cmd\api\run.go"
if errorlevel 1 goto fail
echo.
diff --git a/src/run.rc b/src/run.rc
index e493eae36..765b331aa 100755
--- a/src/run.rc
+++ b/src/run.rc
@@ -47,8 +47,8 @@ fn xcd {
}
echo
-echo '#' ../misc/dashboard/builder ../misc/goplay
-go build ../misc/dashboard/builder ../misc/gplay
+echo '#' ../misc/goplay
+go build ../misc/gplay
echo
echo '#' ../test/bench/go1
diff --git a/src/sudo.bash b/src/sudo.bash
index 7b7d4f1cd..33254c2c5 100755
--- a/src/sudo.bash
+++ b/src/sudo.bash
@@ -19,7 +19,7 @@ if ! go help >/dev/null 2>&1; then
fi
eval $(go env)
-if ! [ -x $GOTOOLDIR/cov -a -x $GOTOOLDIR/prof ]; then
+if ! [ -x $GOTOOLDIR/prof ]; then
echo "You don't need to run sudo.bash." >&2
exit 2
fi
@@ -30,7 +30,7 @@ if [[ ! -d /usr/local/bin ]]; then
fi
cd $(dirname $0)
-for i in prof cov
+for i in prof
do
# Remove old binaries if present
sudo rm -f /usr/local/bin/6$i