summaryrefslogtreecommitdiff
path: root/src/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd')
-rw-r--r--src/cmd/5a/Makefile25
-rw-r--r--src/cmd/5a/a.y2
-rw-r--r--src/cmd/5a/lex.c16
-rw-r--r--src/cmd/5a/y.tab.c2949
-rw-r--r--src/cmd/5a/y.tab.h166
-rw-r--r--src/cmd/5c/Makefile33
-rw-r--r--src/cmd/5c/gc.h5
-rw-r--r--src/cmd/5c/swt.c37
-rw-r--r--src/cmd/5c/txt.c7
-rw-r--r--src/cmd/5g/Makefile35
-rw-r--r--src/cmd/5g/cgen.c68
-rw-r--r--src/cmd/5g/cgen64.c12
-rw-r--r--src/cmd/5g/doc.go2
-rw-r--r--src/cmd/5g/galign.c2
-rw-r--r--src/cmd/5g/gg.h21
-rw-r--r--src/cmd/5g/ggen.c9
-rw-r--r--src/cmd/5g/gobj.c63
-rw-r--r--src/cmd/5g/gsubr.c94
-rw-r--r--src/cmd/5g/list.c4
-rw-r--r--src/cmd/5g/peep.c10
-rw-r--r--src/cmd/5g/reg.c177
-rw-r--r--src/cmd/5l/5.out.h3
-rw-r--r--src/cmd/5l/Makefile42
-rw-r--r--src/cmd/5l/asm.c54
-rw-r--r--src/cmd/5l/doc.go32
-rw-r--r--src/cmd/5l/l.h1
-rw-r--r--src/cmd/5l/list.c2
-rw-r--r--src/cmd/5l/noop.c3
-rw-r--r--src/cmd/5l/obj.c30
-rw-r--r--src/cmd/5l/pass.c5
-rw-r--r--src/cmd/5l/softfloat.c1
-rw-r--r--src/cmd/5l/span.c2
-rw-r--r--src/cmd/6a/Makefile25
-rw-r--r--src/cmd/6a/a.y6
-rw-r--r--src/cmd/6a/lex.c21
-rw-r--r--src/cmd/6a/y.tab.c2730
-rw-r--r--src/cmd/6a/y.tab.h135
-rw-r--r--src/cmd/6c/Makefile35
-rw-r--r--src/cmd/6c/cgen.c48
-rw-r--r--src/cmd/6c/gc.h3
-rw-r--r--src/cmd/6c/swt.c31
-rw-r--r--src/cmd/6c/txt.c4
-rw-r--r--src/cmd/6g/Makefile34
-rw-r--r--src/cmd/6g/cgen.c46
-rw-r--r--src/cmd/6g/galign.c2
-rw-r--r--src/cmd/6g/gg.h20
-rw-r--r--src/cmd/6g/ggen.c29
-rw-r--r--src/cmd/6g/gobj.c67
-rw-r--r--src/cmd/6g/gsubr.c99
-rw-r--r--src/cmd/6g/list.c4
-rw-r--r--src/cmd/6g/peep.c5
-rw-r--r--src/cmd/6g/reg.c190
-rw-r--r--src/cmd/6l/6.out.h5
-rw-r--r--src/cmd/6l/Makefile47
-rw-r--r--src/cmd/6l/asm.c75
-rw-r--r--src/cmd/6l/doc.go46
-rw-r--r--src/cmd/6l/l.h3
-rw-r--r--src/cmd/6l/obj.c47
-rw-r--r--src/cmd/6l/optab.c16
-rw-r--r--src/cmd/6l/pass.c25
-rw-r--r--src/cmd/6l/span.c33
-rw-r--r--src/cmd/8a/Makefile25
-rw-r--r--src/cmd/8a/a.y13
-rw-r--r--src/cmd/8a/lex.c28
-rw-r--r--src/cmd/8a/y.tab.c2852
-rw-r--r--src/cmd/8a/y.tab.h135
-rw-r--r--src/cmd/8c/Makefile36
-rw-r--r--src/cmd/8c/cgen.c35
-rw-r--r--src/cmd/8c/gc.h3
-rw-r--r--src/cmd/8c/swt.c36
-rw-r--r--src/cmd/8c/txt.c4
-rw-r--r--src/cmd/8g/Makefile35
-rw-r--r--src/cmd/8g/cgen.c42
-rw-r--r--src/cmd/8g/cgen64.c2
-rw-r--r--src/cmd/8g/doc.go2
-rw-r--r--src/cmd/8g/galign.c2
-rw-r--r--src/cmd/8g/gg.h24
-rw-r--r--src/cmd/8g/ggen.c26
-rw-r--r--src/cmd/8g/gobj.c66
-rw-r--r--src/cmd/8g/gsubr.c83
-rw-r--r--src/cmd/8g/list.c4
-rw-r--r--src/cmd/8g/opt.h2
-rw-r--r--src/cmd/8g/peep.c5
-rw-r--r--src/cmd/8g/reg.c188
-rw-r--r--src/cmd/8l/8.out.h14
-rw-r--r--src/cmd/8l/Makefile48
-rw-r--r--src/cmd/8l/asm.c85
-rw-r--r--src/cmd/8l/doc.go43
-rw-r--r--src/cmd/8l/l.h2
-rw-r--r--src/cmd/8l/obj.c52
-rw-r--r--src/cmd/8l/optab.c14
-rw-r--r--src/cmd/8l/pass.c27
-rw-r--r--src/cmd/8l/span.c39
-rw-r--r--src/cmd/Makefile69
-rw-r--r--src/cmd/addr2line/main.c68
-rw-r--r--src/cmd/api/goapi.go1005
-rw-r--r--src/cmd/api/goapi_test.go75
-rw-r--r--src/cmd/api/testdata/src/pkg/p1/golden.txt73
-rw-r--r--src/cmd/api/testdata/src/pkg/p1/p1.go153
-rw-r--r--src/cmd/api/testdata/src/pkg/p2/golden.txt5
-rw-r--r--src/cmd/api/testdata/src/pkg/p2/p2.go9
-rw-r--r--src/cmd/cc/Makefile36
-rw-r--r--src/cmd/cc/cc.h9
-rw-r--r--src/cmd/cc/com64.c2
-rw-r--r--src/cmd/cc/dcl.c4
-rw-r--r--src/cmd/cc/dpchk.c18
-rw-r--r--src/cmd/cc/funct.c4
-rw-r--r--src/cmd/cc/godefs.c6
-rw-r--r--src/cmd/cc/lex.c2
-rw-r--r--src/cmd/cc/lexbody7
-rw-r--r--src/cmd/cc/macbody4
-rw-r--r--src/cmd/cc/pgen.c18
-rw-r--r--src/cmd/cc/pswt.c2
-rw-r--r--src/cmd/cc/sub.c53
-rw-r--r--src/cmd/cc/y.tab.c3811
-rw-r--r--src/cmd/cc/y.tab.h228
-rw-r--r--src/cmd/cgo/ast.go33
-rw-r--r--src/cmd/cgo/doc.go45
-rw-r--r--src/cmd/cgo/gcc.go391
-rw-r--r--src/cmd/cgo/godefs.go289
-rw-r--r--src/cmd/cgo/main.go125
-rw-r--r--src/cmd/cgo/out.go324
-rw-r--r--src/cmd/cgo/util.go21
-rw-r--r--src/cmd/cov/Makefile39
-rw-r--r--src/cmd/cov/doc.go33
-rw-r--r--src/cmd/cov/main.c480
-rw-r--r--src/cmd/cov/tree.c246
-rw-r--r--src/cmd/cov/tree.h47
-rw-r--r--src/cmd/dist/README45
-rw-r--r--src/cmd/dist/a.h149
-rw-r--r--src/cmd/dist/arg.h50
-rw-r--r--src/cmd/dist/buf.c279
-rw-r--r--src/cmd/dist/build.c1530
-rw-r--r--src/cmd/dist/buildgc.c106
-rw-r--r--src/cmd/dist/buildruntime.c346
-rw-r--r--src/cmd/dist/goc2c.c735
-rw-r--r--src/cmd/dist/main.c41
-rw-r--r--src/cmd/dist/unix.c720
-rw-r--r--src/cmd/dist/windows.c910
-rw-r--r--src/cmd/ebnflint/Makefile15
-rw-r--r--src/cmd/ebnflint/doc.go22
-rw-r--r--src/cmd/ebnflint/ebnflint.go109
-rw-r--r--src/cmd/fix/doc.go36
-rw-r--r--src/cmd/fix/error.go353
-rw-r--r--src/cmd/fix/error_test.go240
-rw-r--r--src/cmd/fix/filepath.go (renamed from src/cmd/gofix/filepath.go)13
-rw-r--r--src/cmd/fix/filepath_test.go (renamed from src/cmd/gofix/filepath_test.go)2
-rw-r--r--src/cmd/fix/fix.go (renamed from src/cmd/gofix/fix.go)344
-rw-r--r--src/cmd/fix/go1pkgrename.go146
-rw-r--r--src/cmd/fix/go1pkgrename_test.go139
-rw-r--r--src/cmd/fix/go1rename.go167
-rw-r--r--src/cmd/fix/go1rename_test.go195
-rw-r--r--src/cmd/fix/googlecode.go41
-rw-r--r--src/cmd/fix/googlecode_test.go31
-rw-r--r--src/cmd/fix/hashsum.go94
-rw-r--r--src/cmd/fix/hashsum_test.go99
-rw-r--r--src/cmd/fix/hmacnew.go61
-rw-r--r--src/cmd/fix/hmacnew_test.go107
-rw-r--r--src/cmd/fix/htmlerr.go47
-rw-r--r--src/cmd/fix/htmlerr_test.go39
-rw-r--r--src/cmd/fix/httpfinalurl.go (renamed from src/cmd/gofix/httpfinalurl.go)9
-rw-r--r--src/cmd/fix/httpfinalurl_test.go (renamed from src/cmd/gofix/httpfinalurl_test.go)2
-rw-r--r--src/cmd/fix/httpfs.go (renamed from src/cmd/gofix/httpfs.go)19
-rw-r--r--src/cmd/fix/httpfs_test.go (renamed from src/cmd/gofix/httpfs_test.go)2
-rw-r--r--src/cmd/fix/httpheaders.go (renamed from src/cmd/gofix/httpheaders.go)15
-rw-r--r--src/cmd/fix/httpheaders_test.go (renamed from src/cmd/gofix/httpheaders_test.go)2
-rw-r--r--src/cmd/fix/httpserver.go (renamed from src/cmd/gofix/httpserver.go)9
-rw-r--r--src/cmd/fix/httpserver_test.go (renamed from src/cmd/gofix/httpserver_test.go)2
-rw-r--r--src/cmd/fix/imagecolor.go85
-rw-r--r--src/cmd/fix/imagecolor_test.go126
-rw-r--r--src/cmd/fix/imagenew.go83
-rw-r--r--src/cmd/fix/imagenew_test.go51
-rw-r--r--src/cmd/fix/imageycbcr.go64
-rw-r--r--src/cmd/fix/imageycbcr_test.go54
-rw-r--r--src/cmd/fix/import_test.go458
-rw-r--r--src/cmd/fix/iocopyn.go41
-rw-r--r--src/cmd/fix/iocopyn_test.go37
-rw-r--r--src/cmd/fix/main.go (renamed from src/cmd/gofix/main.go)111
-rw-r--r--src/cmd/fix/main_test.go (renamed from src/cmd/gofix/main_test.go)31
-rw-r--r--src/cmd/fix/mapdelete.go89
-rw-r--r--src/cmd/fix/mapdelete_test.go43
-rw-r--r--src/cmd/fix/math.go51
-rw-r--r--src/cmd/fix/math_test.go47
-rw-r--r--src/cmd/fix/netdial.go (renamed from src/cmd/gofix/netdial.go)15
-rw-r--r--src/cmd/fix/netdial_test.go (renamed from src/cmd/gofix/netdial_test.go)8
-rw-r--r--src/cmd/fix/netudpgroup.go58
-rw-r--r--src/cmd/fix/netudpgroup_test.go53
-rw-r--r--src/cmd/fix/newwriter.go90
-rw-r--r--src/cmd/fix/newwriter_test.go83
-rw-r--r--src/cmd/fix/oserrorstring.go (renamed from src/cmd/gofix/oserrorstring.go)9
-rw-r--r--src/cmd/fix/oserrorstring_test.go (renamed from src/cmd/gofix/oserrorstring_test.go)2
-rw-r--r--src/cmd/fix/osopen.go (renamed from src/cmd/gofix/osopen.go)9
-rw-r--r--src/cmd/fix/osopen_test.go (renamed from src/cmd/gofix/osopen_test.go)2
-rw-r--r--src/cmd/fix/procattr.go (renamed from src/cmd/gofix/procattr.go)9
-rw-r--r--src/cmd/fix/procattr_test.go (renamed from src/cmd/gofix/procattr_test.go)2
-rw-r--r--src/cmd/fix/reflect.go (renamed from src/cmd/gofix/reflect.go)91
-rw-r--r--src/cmd/fix/reflect_test.go (renamed from src/cmd/gofix/reflect_test.go)6
-rw-r--r--src/cmd/fix/signal.go (renamed from src/cmd/gofix/signal.go)21
-rw-r--r--src/cmd/fix/signal_test.go (renamed from src/cmd/gofix/signal_test.go)2
-rw-r--r--src/cmd/fix/sorthelpers.go (renamed from src/cmd/gofix/sorthelpers.go)13
-rw-r--r--src/cmd/fix/sorthelpers_test.go (renamed from src/cmd/gofix/sorthelpers_test.go)2
-rw-r--r--src/cmd/fix/sortslice.go (renamed from src/cmd/gofix/sortslice.go)13
-rw-r--r--src/cmd/fix/sortslice_test.go (renamed from src/cmd/gofix/sortslice_test.go)2
-rw-r--r--src/cmd/fix/strconv.go127
-rw-r--r--src/cmd/fix/strconv_test.go93
-rw-r--r--src/cmd/fix/stringssplit.go (renamed from src/cmd/gofix/stringssplit.go)9
-rw-r--r--src/cmd/fix/stringssplit_test.go (renamed from src/cmd/gofix/stringssplit_test.go)2
-rw-r--r--src/cmd/fix/template.go111
-rw-r--r--src/cmd/fix/template_test.go55
-rw-r--r--src/cmd/fix/testdata/reflect.asn1.go.in (renamed from src/cmd/gofix/testdata/reflect.asn1.go.in)0
-rw-r--r--src/cmd/fix/testdata/reflect.asn1.go.out (renamed from src/cmd/gofix/testdata/reflect.asn1.go.out)0
-rw-r--r--src/cmd/fix/testdata/reflect.datafmt.go.in (renamed from src/cmd/gofix/testdata/reflect.datafmt.go.in)0
-rw-r--r--src/cmd/fix/testdata/reflect.datafmt.go.out (renamed from src/cmd/gofix/testdata/reflect.datafmt.go.out)0
-rw-r--r--src/cmd/fix/testdata/reflect.decode.go.in (renamed from src/cmd/gofix/testdata/reflect.decode.go.in)0
-rw-r--r--src/cmd/fix/testdata/reflect.decode.go.out (renamed from src/cmd/gofix/testdata/reflect.decode.go.out)0
-rw-r--r--src/cmd/fix/testdata/reflect.decoder.go.in (renamed from src/cmd/gofix/testdata/reflect.decoder.go.in)6
-rw-r--r--src/cmd/fix/testdata/reflect.decoder.go.out (renamed from src/cmd/gofix/testdata/reflect.decoder.go.out)0
-rw-r--r--src/cmd/fix/testdata/reflect.dnsmsg.go.in (renamed from src/cmd/gofix/testdata/reflect.dnsmsg.go.in)0
-rw-r--r--src/cmd/fix/testdata/reflect.dnsmsg.go.out (renamed from src/cmd/gofix/testdata/reflect.dnsmsg.go.out)0
-rw-r--r--src/cmd/fix/testdata/reflect.encode.go.in (renamed from src/cmd/gofix/testdata/reflect.encode.go.in)0
-rw-r--r--src/cmd/fix/testdata/reflect.encode.go.out (renamed from src/cmd/gofix/testdata/reflect.encode.go.out)0
-rw-r--r--src/cmd/fix/testdata/reflect.encoder.go.in (renamed from src/cmd/gofix/testdata/reflect.encoder.go.in)2
-rw-r--r--src/cmd/fix/testdata/reflect.encoder.go.out (renamed from src/cmd/gofix/testdata/reflect.encoder.go.out)0
-rw-r--r--src/cmd/fix/testdata/reflect.export.go.in (renamed from src/cmd/gofix/testdata/reflect.export.go.in)14
-rw-r--r--src/cmd/fix/testdata/reflect.export.go.out (renamed from src/cmd/gofix/testdata/reflect.export.go.out)2
-rw-r--r--src/cmd/fix/testdata/reflect.print.go.in (renamed from src/cmd/gofix/testdata/reflect.print.go.in)2
-rw-r--r--src/cmd/fix/testdata/reflect.print.go.out (renamed from src/cmd/gofix/testdata/reflect.print.go.out)0
-rw-r--r--src/cmd/fix/testdata/reflect.quick.go.in (renamed from src/cmd/gofix/testdata/reflect.quick.go.in)0
-rw-r--r--src/cmd/fix/testdata/reflect.quick.go.out (renamed from src/cmd/gofix/testdata/reflect.quick.go.out)0
-rw-r--r--src/cmd/fix/testdata/reflect.read.go.in (renamed from src/cmd/gofix/testdata/reflect.read.go.in)4
-rw-r--r--src/cmd/fix/testdata/reflect.read.go.out (renamed from src/cmd/gofix/testdata/reflect.read.go.out)0
-rw-r--r--src/cmd/fix/testdata/reflect.scan.go.in (renamed from src/cmd/gofix/testdata/reflect.scan.go.in)8
-rw-r--r--src/cmd/fix/testdata/reflect.scan.go.out (renamed from src/cmd/gofix/testdata/reflect.scan.go.out)0
-rw-r--r--src/cmd/fix/testdata/reflect.script.go.in (renamed from src/cmd/gofix/testdata/reflect.script.go.in)0
-rw-r--r--src/cmd/fix/testdata/reflect.script.go.out (renamed from src/cmd/gofix/testdata/reflect.script.go.out)0
-rw-r--r--src/cmd/fix/testdata/reflect.template.go.in (renamed from src/cmd/gofix/testdata/reflect.template.go.in)0
-rw-r--r--src/cmd/fix/testdata/reflect.template.go.out (renamed from src/cmd/gofix/testdata/reflect.template.go.out)0
-rw-r--r--src/cmd/fix/testdata/reflect.type.go.in (renamed from src/cmd/gofix/testdata/reflect.type.go.in)5
-rw-r--r--src/cmd/fix/testdata/reflect.type.go.out (renamed from src/cmd/gofix/testdata/reflect.type.go.out)1
-rw-r--r--src/cmd/fix/timefileinfo.go298
-rw-r--r--src/cmd/fix/timefileinfo_test.go187
-rw-r--r--src/cmd/fix/typecheck.go (renamed from src/cmd/gofix/typecheck.go)115
-rw-r--r--src/cmd/fix/url.go (renamed from src/cmd/gofix/url.go)29
-rw-r--r--src/cmd/fix/url2.go46
-rw-r--r--src/cmd/fix/url2_test.go31
-rw-r--r--src/cmd/fix/url_test.go (renamed from src/cmd/gofix/url_test.go)6
-rw-r--r--src/cmd/fix/xmlapi.go111
-rw-r--r--src/cmd/fix/xmlapi_test.go85
-rw-r--r--src/cmd/gc/Makefile72
-rw-r--r--src/cmd/gc/align.c12
-rwxr-xr-xsrc/cmd/gc/bisonerrors29
-rw-r--r--src/cmd/gc/bits.c10
-rw-r--r--src/cmd/gc/builtin.c118
-rw-r--r--src/cmd/gc/builtin.c.boot114
-rw-r--r--src/cmd/gc/closure.c9
-rw-r--r--src/cmd/gc/const.c123
-rw-r--r--src/cmd/gc/cplx.c7
-rw-r--r--src/cmd/gc/dcl.c567
-rw-r--r--src/cmd/gc/doc.go9
-rw-r--r--src/cmd/gc/esc.c805
-rw-r--r--src/cmd/gc/export.c345
-rw-r--r--src/cmd/gc/fmt.c1639
-rw-r--r--src/cmd/gc/gen.c171
-rw-r--r--src/cmd/gc/go.errors3
-rw-r--r--src/cmd/gc/go.h301
-rw-r--r--src/cmd/gc/go.y448
-rw-r--r--src/cmd/gc/init.c18
-rw-r--r--src/cmd/gc/inl.c842
-rw-r--r--src/cmd/gc/lex.c427
-rw-r--r--src/cmd/gc/md5.c2
-rwxr-xr-xsrc/cmd/gc/mkbuiltin22
-rw-r--r--src/cmd/gc/mkbuiltin1.c34
-rwxr-xr-xsrc/cmd/gc/mkopnames4
-rw-r--r--src/cmd/gc/mparith1.c12
-rw-r--r--src/cmd/gc/mparith2.c81
-rw-r--r--src/cmd/gc/mparith3.c12
-rw-r--r--src/cmd/gc/obj.c51
-rw-r--r--src/cmd/gc/order.c358
-rw-r--r--src/cmd/gc/pgen.c27
-rw-r--r--src/cmd/gc/print.c480
-rw-r--r--src/cmd/gc/range.c50
-rw-r--r--src/cmd/gc/reflect.c217
-rw-r--r--src/cmd/gc/runtime.go25
-rw-r--r--src/cmd/gc/select.c14
-rw-r--r--src/cmd/gc/sinit.c465
-rw-r--r--src/cmd/gc/subr.c1897
-rw-r--r--src/cmd/gc/swt.c63
-rw-r--r--src/cmd/gc/typecheck.c770
-rw-r--r--src/cmd/gc/unsafe.c25
-rw-r--r--src/cmd/gc/unsafe.go8
-rw-r--r--src/cmd/gc/walk.c812
-rw-r--r--src/cmd/gc/y.tab.c5540
-rw-r--r--src/cmd/gc/y.tab.h171
-rw-r--r--src/cmd/gc/yerr.h73
-rw-r--r--src/cmd/go/bootstrap.go30
-rw-r--r--src/cmd/go/build.go1566
-rw-r--r--src/cmd/go/clean.go199
-rw-r--r--src/cmd/go/discovery.go63
-rw-r--r--src/cmd/go/doc.go769
-rw-r--r--src/cmd/go/env.go89
-rw-r--r--src/cmd/go/fix.go30
-rw-r--r--src/cmd/go/fmt.go58
-rw-r--r--src/cmd/go/get.go428
-rw-r--r--src/cmd/go/help.go238
-rw-r--r--src/cmd/go/http.go87
-rw-r--r--src/cmd/go/list.go168
-rw-r--r--src/cmd/go/main.go541
-rw-r--r--src/cmd/go/match_test.go36
-rwxr-xr-xsrc/cmd/go/mkdoc.sh8
-rw-r--r--src/cmd/go/pkg.go679
-rw-r--r--src/cmd/go/run.go85
-rwxr-xr-xsrc/cmd/go/script23
-rw-r--r--src/cmd/go/script.txt352
-rw-r--r--src/cmd/go/tag_test.go (renamed from src/cmd/goinstall/tag_test.go)24
-rwxr-xr-xsrc/cmd/go/test.bash127
-rw-r--r--src/cmd/go/test.go815
-rw-r--r--src/cmd/go/testdata/errmsg/x.go3
-rw-r--r--src/cmd/go/testdata/errmsg/x1_test.go3
-rw-r--r--src/cmd/go/testdata/errmsg/x_test.go3
-rw-r--r--src/cmd/go/testdata/local/easy.go7
-rw-r--r--src/cmd/go/testdata/local/easysub/easysub.go7
-rw-r--r--src/cmd/go/testdata/local/easysub/main.go9
-rw-r--r--src/cmd/go/testdata/local/hard.go7
-rw-r--r--src/cmd/go/testdata/local/sub/sub.go12
-rw-r--r--src/cmd/go/testdata/local/sub/sub/subsub.go7
-rw-r--r--src/cmd/go/testdata/src/go-cmd-test/helloworld.go5
-rw-r--r--src/cmd/go/testdata/testimport/p.go3
-rw-r--r--src/cmd/go/testdata/testimport/p1/p1.go3
-rw-r--r--src/cmd/go/testdata/testimport/p2/p2.go3
-rw-r--r--src/cmd/go/testdata/testimport/p_test.go13
-rw-r--r--src/cmd/go/testdata/testimport/x_test.go15
-rw-r--r--src/cmd/go/testflag.go235
-rw-r--r--src/cmd/go/tool.go125
-rw-r--r--src/cmd/go/vcs.go674
-rw-r--r--src/cmd/go/version.go25
-rw-r--r--src/cmd/go/vet.go30
-rw-r--r--src/cmd/godefs/Makefile19
-rw-r--r--src/cmd/godefs/a.h104
-rw-r--r--src/cmd/godefs/doc.go99
-rw-r--r--src/cmd/godefs/main.c606
-rw-r--r--src/cmd/godefs/stabs.c456
-rwxr-xr-xsrc/cmd/godefs/test.sh45
-rw-r--r--src/cmd/godefs/testdata.c41
-rw-r--r--src/cmd/godefs/testdata_darwin_386.golden31
-rw-r--r--src/cmd/godefs/testdata_darwin_amd64.golden31
-rw-r--r--src/cmd/godefs/util.c36
-rw-r--r--src/cmd/godoc/Makefile24
-rw-r--r--src/cmd/godoc/README.godoc-app80
-rw-r--r--src/cmd/godoc/appconfig.go19
-rw-r--r--src/cmd/godoc/appinit.go74
-rw-r--r--src/cmd/godoc/codewalk.go74
-rw-r--r--src/cmd/godoc/dirtrees.go133
-rw-r--r--src/cmd/godoc/doc.go83
-rw-r--r--src/cmd/godoc/filesystem.go560
-rw-r--r--src/cmd/godoc/format.go4
-rw-r--r--src/cmd/godoc/godoc.go1042
-rw-r--r--src/cmd/godoc/httpzip.go181
-rw-r--r--src/cmd/godoc/index.go403
-rw-r--r--src/cmd/godoc/main.go328
-rw-r--r--src/cmd/godoc/mapping.go200
-rw-r--r--src/cmd/godoc/parser.go28
-rw-r--r--src/cmd/godoc/setup-godoc-app.bash121
-rw-r--r--[-rwxr-xr-x]src/cmd/godoc/snippet.go18
-rw-r--r--src/cmd/godoc/spec.go135
-rw-r--r--src/cmd/godoc/template.go182
-rw-r--r--src/cmd/godoc/throttle.go88
-rw-r--r--src/cmd/godoc/utils.go87
-rw-r--r--src/cmd/godoc/zip.go87
-rw-r--r--src/cmd/gofix/Makefile34
-rw-r--r--src/cmd/gofix/doc.go36
-rw-r--r--src/cmd/gofmt/Makefile19
-rw-r--r--src/cmd/gofmt/doc.go12
-rw-r--r--src/cmd/gofmt/gofmt.go194
-rw-r--r--src/cmd/gofmt/gofmt_test.go50
-rw-r--r--src/cmd/gofmt/long_test.go159
-rw-r--r--src/cmd/gofmt/rewrite.go13
-rw-r--r--src/cmd/gofmt/simplify.go18
-rwxr-xr-xsrc/cmd/gofmt/test.sh162
-rw-r--r--src/cmd/gofmt/testdata/comments.golden9
-rw-r--r--src/cmd/gofmt/testdata/comments.input9
-rw-r--r--src/cmd/gofmt/testdata/composites.golden98
-rw-r--r--src/cmd/gofmt/testdata/composites.input98
-rw-r--r--src/cmd/gofmt/testdata/import.golden108
-rw-r--r--src/cmd/gofmt/testdata/import.input108
-rw-r--r--src/cmd/gofmt/testdata/old.golden9
-rw-r--r--src/cmd/gofmt/testdata/old.input8
-rw-r--r--src/cmd/gofmt/testdata/rewrite3.golden12
-rw-r--r--src/cmd/gofmt/testdata/rewrite3.input12
-rw-r--r--src/cmd/gofmt/testdata/rewrite4.golden74
-rw-r--r--src/cmd/gofmt/testdata/rewrite4.input74
-rw-r--r--src/cmd/gofmt/testdata/stdin1.golden3
-rw-r--r--src/cmd/gofmt/testdata/stdin1.golden.gofmt3
-rw-r--r--src/cmd/gofmt/testdata/stdin1.input3
-rw-r--r--src/cmd/gofmt/testdata/stdin1.input.gofmt3
-rw-r--r--src/cmd/gofmt/testdata/stdin2.golden11
-rw-r--r--src/cmd/gofmt/testdata/stdin2.golden.gofmt10
-rw-r--r--src/cmd/gofmt/testdata/stdin2.input11
-rw-r--r--src/cmd/gofmt/testdata/stdin2.input.gofmt11
-rw-r--r--src/cmd/gofmt/testdata/stdin3.golden6
-rw-r--r--src/cmd/gofmt/testdata/stdin3.golden.gofmt7
-rw-r--r--src/cmd/gofmt/testdata/stdin3.input4
-rw-r--r--src/cmd/gofmt/testdata/stdin3.input.gofmt7
-rw-r--r--src/cmd/gofmt/testdata/stdin4.golden3
-rw-r--r--src/cmd/gofmt/testdata/stdin4.golden.gofmt3
-rw-r--r--src/cmd/gofmt/testdata/stdin4.input3
-rw-r--r--src/cmd/gofmt/testdata/stdin4.input.gofmt3
-rw-r--r--src/cmd/goinstall/Makefile19
-rw-r--r--src/cmd/goinstall/doc.go196
-rw-r--r--src/cmd/goinstall/download.go353
-rw-r--r--src/cmd/goinstall/main.go334
-rw-r--r--src/cmd/goinstall/make.go175
-rw-r--r--src/cmd/gomake/doc.go36
-rw-r--r--src/cmd/gopack/Makefile12
-rw-r--r--src/cmd/gotest/doc.go113
-rw-r--r--src/cmd/gotest/flag.go159
-rw-r--r--src/cmd/gotest/gotest.go435
-rw-r--r--src/cmd/gotry/Makefile18
-rwxr-xr-xsrc/cmd/gotry/gotry168
-rw-r--r--src/cmd/gotype/Makefile17
-rw-r--r--src/cmd/gotype/doc.go61
-rw-r--r--src/cmd/gotype/gotype.go192
-rw-r--r--src/cmd/gotype/gotype_test.go49
-rw-r--r--src/cmd/gotype/testdata/test1.go6
-rw-r--r--src/cmd/govet/Makefile14
-rw-r--r--src/cmd/goyacc/Makefile17
-rw-r--r--src/cmd/hgpatch/Makefile11
-rw-r--r--src/cmd/hgpatch/doc.go18
-rw-r--r--src/cmd/hgpatch/main.go358
-rw-r--r--src/cmd/ld/data.c147
-rw-r--r--src/cmd/ld/doc.go53
-rw-r--r--src/cmd/ld/dwarf.c8
-rw-r--r--src/cmd/ld/elf.c85
-rw-r--r--src/cmd/ld/elf.h6
-rw-r--r--src/cmd/ld/go.c8
-rw-r--r--src/cmd/ld/ldelf.c1
-rw-r--r--src/cmd/ld/ldpe.c26
-rw-r--r--src/cmd/ld/lib.c95
-rw-r--r--src/cmd/ld/lib.h8
-rw-r--r--src/cmd/ld/macho.c45
-rw-r--r--src/cmd/ld/pe.c104
-rw-r--r--src/cmd/ld/symtab.c17
-rw-r--r--src/cmd/nm/Makefile14
-rw-r--r--src/cmd/nm/doc.go4
-rw-r--r--src/cmd/objdump/main.c68
-rw-r--r--src/cmd/pack/Makefile5
-rw-r--r--src/cmd/pack/ar.c (renamed from src/cmd/gopack/ar.c)159
-rw-r--r--src/cmd/pack/doc.go (renamed from src/cmd/gopack/doc.go)11
-rw-r--r--src/cmd/prof/Makefile38
-rw-r--r--src/cmd/prof/doc.go48
-rwxr-xr-xsrc/cmd/prof/gopprof4975
-rw-r--r--src/cmd/prof/main.c895
-rw-r--r--src/cmd/vet/Makefile (renamed from src/cmd/gotest/Makefile)11
-rw-r--r--src/cmd/vet/doc.go (renamed from src/cmd/govet/doc.go)34
-rw-r--r--src/cmd/vet/main.go239
-rw-r--r--src/cmd/vet/method.go161
-rw-r--r--src/cmd/vet/print.go (renamed from src/cmd/govet/govet.go)306
-rw-r--r--src/cmd/vet/structtag.go34
-rw-r--r--src/cmd/vet/taglit.go121
-rw-r--r--src/cmd/yacc/Makefile (renamed from src/cmd/cgo/Makefile)15
-rw-r--r--src/cmd/yacc/doc.go (renamed from src/cmd/goyacc/doc.go)13
-rw-r--r--src/cmd/yacc/units.txt (renamed from src/cmd/goyacc/units.txt)0
-rw-r--r--src/cmd/yacc/units.y (renamed from src/cmd/goyacc/units.y)64
-rw-r--r--src/cmd/yacc/yacc.go (renamed from src/cmd/goyacc/goyacc.go)133
463 files changed, 53670 insertions, 18684 deletions
diff --git a/src/cmd/5a/Makefile b/src/cmd/5a/Makefile
index f4463c97b..27290ddd7 100644
--- a/src/cmd/5a/Makefile
+++ b/src/cmd/5a/Makefile
@@ -1,25 +1,10 @@
-# Copyright 2009 The Go Authors. All rights reserved.
+# 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 ../../Make.inc
-O:=$(HOST_O)
+include ../../Make.dist
-TARG=5a
+install: y.tab.h
-HFILES=\
- a.h\
- y.tab.h\
- ../5l/5.out.h\
-
-OFILES=\
- y.tab.$O\
- lex.$O\
- ../5l/enam.$O\
-
-YFILES=\
- a.y\
-
-include ../../Make.ccmd
-
-lex.$O: ../cc/macbody ../cc/lexbody
+y.tab.h: a.y
+ LANG=C LANGUAGE=en_US.UTF8 bison -d -v -y a.y
diff --git a/src/cmd/5a/a.y b/src/cmd/5a/a.y
index 9a0efd5e0..512fb5a95 100644
--- a/src/cmd/5a/a.y
+++ b/src/cmd/5a/a.y
@@ -217,7 +217,7 @@ inst:
*/
| LTYPEB name ',' imm
{
- outcode($1, Always, &$2, NREG, &$4);
+ outcode($1, Always, &$2, 0, &$4);
}
| LTYPEB name ',' con ',' imm
{
diff --git a/src/cmd/5a/lex.c b/src/cmd/5a/lex.c
index ad7ed05dd..4bef0219a 100644
--- a/src/cmd/5a/lex.c
+++ b/src/cmd/5a/lex.c
@@ -44,7 +44,11 @@ enum
int
systemtype(int sys)
{
+#ifdef _WIN32
+ return sys&Windows;
+#else
return sys&Plan9;
+#endif
}
void
@@ -643,17 +647,13 @@ outhist(void)
for(h = hist; h != H; h = h->link) {
p = h->name;
op = 0;
- /* on windows skip drive specifier in pathname */
if(systemtype(Windows) && p && p[1] == ':'){
- p += 2;
- c = *p;
- }
- if(p && p[0] != c && h->offset == 0 && pathname){
- /* on windows skip drive specifier in pathname */
+ c = p[2];
+ } else if(p && p[0] != c && h->offset == 0 && pathname){
if(systemtype(Windows) && pathname[1] == ':') {
op = p;
- p = pathname+2;
- c = *p;
+ p = pathname;
+ c = p[2];
} else if(pathname[0] == c){
op = p;
p = pathname;
diff --git a/src/cmd/5a/y.tab.c b/src/cmd/5a/y.tab.c
new file mode 100644
index 000000000..a2fba60b3
--- /dev/null
+++ b/src/cmd/5a/y.tab.c
@@ -0,0 +1,2949 @@
+
+/* A Bison parser, made by GNU Bison 2.4.1. */
+
+/* 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
+ it under the terms of the GNU General Public License as published by
+ 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, 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
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ 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. */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+ simplifying the original so-called "semantic" parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Bison version. */
+#define YYBISON_VERSION "2.4.1"
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* 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 189 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"
+
+
+/* Line 189 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 0
+#endif
+
+/* Enabling the token table. */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ LTYPE1 = 258,
+ LTYPE2 = 259,
+ LTYPE3 = 260,
+ LTYPE4 = 261,
+ LTYPE5 = 262,
+ LTYPE6 = 263,
+ LTYPE7 = 264,
+ LTYPE8 = 265,
+ LTYPE9 = 266,
+ LTYPEA = 267,
+ LTYPEB = 268,
+ 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,
+ LCONST = 282,
+ LSP = 283,
+ LSB = 284,
+ LFP = 285,
+ LPC = 286,
+ LTYPEX = 287,
+ LR = 288,
+ LREG = 289,
+ LF = 290,
+ LFREG = 291,
+ LC = 292,
+ LCREG = 293,
+ LPSR = 294,
+ LFCR = 295,
+ LCOND = 296,
+ LS = 297,
+ LAT = 298,
+ LFCONST = 299,
+ LSCONST = 300,
+ LNAME = 301,
+ LLAB = 302,
+ LVAR = 303
+ };
+#endif
+/* Tokens. */
+#define LTYPE1 258
+#define LTYPE2 259
+#define LTYPE3 260
+#define LTYPE4 261
+#define LTYPE5 262
+#define LTYPE6 263
+#define LTYPE7 264
+#define LTYPE8 265
+#define LTYPE9 266
+#define LTYPEA 267
+#define LTYPEB 268
+#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 LCONST 282
+#define LSP 283
+#define LSB 284
+#define LFP 285
+#define LPC 286
+#define LTYPEX 287
+#define LR 288
+#define LREG 289
+#define LF 290
+#define LFREG 291
+#define LC 292
+#define LCREG 293
+#define LPSR 294
+#define LFCR 295
+#define LCOND 296
+#define LS 297
+#define LAT 298
+#define LFCONST 299
+#define LSCONST 300
+#define LNAME 301
+#define LLAB 302
+#define LVAR 303
+
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+{
+
+/* Line 214 of yacc.c */
+#line 38 "a.y"
+
+ Sym *sym;
+ int32 lval;
+ double dval;
+ char sval[8];
+ Gen gen;
+
+
+
+/* Line 214 of yacc.c */
+#line 223 "y.tab.c"
+} YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+
+/* Copy the second part of user declarations. */
+
+
+/* Line 264 of yacc.c */
+#line 235 "y.tab.c"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+# define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+# define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# else
+# define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if YYENABLE_NLS
+# if ENABLE_NLS
+# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+# define YY_(msgid) dgettext ("bison-runtime", msgid)
+# endif
+# endif
+# ifndef YY_
+# define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E. */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions. */
+#ifndef lint
+# define YYID(n) (n)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int yyi)
+#else
+static int
+YYID (yyi)
+ int yyi;
+#endif
+{
+ return yyi;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# elif defined __BUILTIN_VA_ARG_INCR
+# include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+# elif defined _AIX
+# define YYSTACK_ALLOC __alloca
+# elif defined _MSC_VER
+# include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+# define alloca _alloca
+# else
+# define YYSTACK_ALLOC alloca
+# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (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
+# endif
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's `empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+# ifndef YYSTACK_ALLOC_MAXIMUM
+ /* The OS might guarantee only one guard page at the bottom of the stack,
+ and a page size can be as small as 4096 bytes. So we cannot safely
+ invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
+ to allow for a few compiler-allocated temporary stack slots. */
+# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+# endif
+# else
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# ifndef YYSTACK_ALLOC_MAXIMUM
+# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+# endif
+# if (defined __cplusplus && ! defined _STDLIB_H \
+ && ! ((defined YYMALLOC || defined malloc) \
+ && (defined YYFREE || defined free)))
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef _STDLIB_H
+# define _STDLIB_H 1
+# endif
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# if ! defined malloc && ! defined _STDLIB_H && (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__ \
+ || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+ && (! defined __cplusplus \
+ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ 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)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((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
+
+/* 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
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 2
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 643
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 69
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 35
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 129
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 327
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 303
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
+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, 67, 12, 5, 2,
+ 65, 66, 10, 8, 62, 9, 2, 11, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 59, 61,
+ 6, 60, 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, 63, 2, 64, 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, 68, 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, 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, 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, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 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, 44,
+ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+ YYRHS. */
+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, 234, 237, 240, 241, 244,
+ 249, 252, 255, 258, 261, 266, 269, 271, 274, 278,
+ 280, 284, 288, 290, 292, 294, 299, 301, 303, 305,
+ 307, 309, 311, 313, 317, 319, 324, 326, 331, 333,
+ 335, 337, 339, 342, 344, 350, 355, 360, 365, 370,
+ 372, 374, 376, 378, 383, 385, 387, 389, 394, 396,
+ 398, 400, 405, 410, 416, 424, 425, 428, 431, 433,
+ 435, 437, 439, 441, 444, 447, 450, 454, 455, 458,
+ 460, 464, 468, 472, 476, 480, 485, 490, 494, 498
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yytype_int8 yyrhs[] =
+{
+ 70, 0, -1, -1, -1, 70, 71, 72, -1, -1,
+ 57, 59, 73, 72, -1, -1, 56, 59, 74, 72,
+ -1, 56, 60, 103, 61, -1, 58, 60, 103, 61,
+ -1, 61, -1, 75, 61, -1, 1, 61, -1, 13,
+ 76, 87, 62, 94, 62, 89, -1, 13, 76, 87,
+ 62, 94, 62, -1, 13, 76, 87, 62, 89, -1,
+ 14, 76, 87, 62, 89, -1, 15, 76, 82, 62,
+ 82, -1, 16, 76, 77, 78, -1, 16, 76, 77,
+ 83, -1, 36, 77, 84, -1, 17, 77, 78, -1,
+ 18, 76, 77, 82, -1, 19, 76, 87, 62, 94,
+ 77, -1, 20, 76, 85, 62, 63, 81, 64, -1,
+ 20, 76, 63, 81, 64, 62, 85, -1, 21, 76,
+ 89, 62, 84, 62, 89, -1, 21, 76, 89, 62,
+ 84, 77, -1, 21, 76, 77, 84, 62, 89, -1,
+ 22, 76, 77, -1, 23, 98, 62, 88, -1, 23,
+ 98, 62, 101, 62, 88, -1, 24, 98, 11, 101,
+ 62, 79, -1, 25, 76, 89, 77, -1, 29, 77,
+ 79, -1, 30, 76, 97, 62, 97, -1, 32, 76,
+ 96, 62, 97, -1, 32, 76, 96, 62, 46, 62,
+ 97, -1, 33, 76, 97, 62, 97, 77, -1, 31,
+ 76, 101, 62, 103, 62, 94, 62, 95, 62, 95,
+ 102, -1, 34, 76, 89, 62, 89, 62, 90, -1,
+ 35, 76, 89, 62, 89, 62, 89, 62, 94, -1,
+ 26, 77, -1, -1, 76, 51, -1, 76, 52, -1,
+ -1, 62, 77, -1, 101, 65, 41, 66, -1, 56,
+ 99, -1, 57, 99, -1, 67, 101, -1, 67, 86,
+ -1, 67, 10, 67, 86, -1, 67, 55, -1, 80,
+ -1, 67, 54, -1, 67, 9, 54, -1, 94, -1,
+ 94, 9, 94, -1, 94, 77, 81, -1, 89, -1,
+ 79, -1, 91, -1, 91, 65, 94, 66, -1, 49,
+ -1, 50, -1, 101, -1, 86, -1, 97, -1, 84,
+ -1, 98, -1, 65, 94, 66, -1, 84, -1, 101,
+ 65, 93, 66, -1, 98, -1, 98, 65, 93, 66,
+ -1, 85, -1, 89, -1, 88, -1, 91, -1, 67,
+ 101, -1, 94, -1, 65, 94, 62, 94, 66, -1,
+ 94, 6, 6, 92, -1, 94, 7, 7, 92, -1,
+ 94, 9, 7, 92, -1, 94, 53, 7, 92, -1,
+ 94, -1, 101, -1, 44, -1, 41, -1, 43, 65,
+ 103, 66, -1, 93, -1, 38, -1, 48, -1, 47,
+ 65, 103, 66, -1, 97, -1, 80, -1, 46, -1,
+ 45, 65, 101, 66, -1, 101, 65, 100, 66, -1,
+ 56, 99, 65, 100, 66, -1, 56, 6, 7, 99,
+ 65, 39, 66, -1, -1, 8, 101, -1, 9, 101,
+ -1, 39, -1, 38, -1, 40, -1, 37, -1, 58,
+ -1, 9, 101, -1, 8, 101, -1, 68, 101, -1,
+ 65, 103, 66, -1, -1, 62, 103, -1, 101, -1,
+ 103, 8, 103, -1, 103, 9, 103, -1, 103, 10,
+ 103, -1, 103, 11, 103, -1, 103, 12, 103, -1,
+ 103, 6, 6, 103, -1, 103, 7, 7, 103, -1,
+ 103, 5, 103, -1, 103, 4, 103, -1, 103, 3,
+ 103, -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, 298, 307, 313, 316, 320, 325, 326, 329,
+ 335, 344, 352, 358, 363, 368, 374, 377, 383, 391,
+ 395, 404, 410, 411, 412, 413, 418, 424, 430, 436,
+ 437, 440, 441, 449, 458, 459, 468, 469, 475, 478,
+ 479, 480, 482, 490, 498, 507, 513, 519, 525, 533,
+ 539, 547, 548, 552, 560, 561, 567, 568, 576, 577,
+ 580, 586, 594, 602, 610, 620, 623, 627, 633, 634,
+ 635, 638, 639, 643, 647, 651, 655, 661, 664, 670,
+ 671, 675, 679, 683, 687, 691, 695, 699, 703, 707
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+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", "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
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+ token YYLEX-NUM. */
+static const yytype_uint16 yytoknum[] =
+{
+ 0, 256, 257, 124, 94, 38, 60, 62, 43, 45,
+ 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, 291, 292, 293, 294,
+ 295, 296, 297, 298, 299, 300, 301, 302, 303, 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, 69, 70, 71, 70, 73, 72, 74, 72, 72,
+ 72, 72, 72, 72, 75, 75, 75, 75, 75, 75,
+ 75, 75, 75, 75, 75, 75, 75, 75, 75, 75,
+ 75, 75, 75, 75, 75, 75, 75, 75, 75, 75,
+ 75, 75, 75, 75, 76, 76, 76, 77, 77, 78,
+ 78, 78, 79, 79, 79, 79, 79, 80, 80, 81,
+ 81, 81, 82, 82, 82, 82, 82, 82, 82, 82,
+ 82, 83, 83, 84, 85, 85, 86, 86, 86, 87,
+ 87, 87, 88, 89, 90, 91, 91, 91, 91, 92,
+ 92, 93, 93, 93, 94, 94, 95, 95, 96, 96,
+ 97, 97, 98, 98, 98, 99, 99, 99, 100, 100,
+ 100, 101, 101, 101, 101, 101, 101, 102, 102, 103,
+ 103, 103, 103, 103, 103, 103, 103, 103, 103, 103
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+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, 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
+ means the default is an error. */
+static const yytype_uint8 yydefact[] =
+{
+ 2, 3, 1, 0, 0, 44, 44, 44, 44, 47,
+ 44, 44, 44, 44, 44, 0, 0, 44, 47, 47,
+ 44, 44, 44, 44, 44, 44, 47, 0, 0, 0,
+ 11, 4, 0, 13, 0, 0, 0, 47, 47, 0,
+ 47, 0, 0, 47, 47, 0, 0, 111, 105, 112,
+ 0, 0, 0, 0, 0, 0, 43, 0, 0, 0,
+ 0, 0, 0, 0, 0, 7, 0, 5, 0, 12,
+ 95, 92, 0, 91, 45, 46, 0, 0, 80, 79,
+ 81, 94, 83, 0, 0, 100, 66, 67, 0, 0,
+ 63, 56, 0, 74, 78, 69, 62, 64, 70, 76,
+ 68, 0, 48, 105, 105, 22, 0, 0, 0, 0,
+ 0, 0, 0, 0, 83, 30, 114, 113, 0, 0,
+ 0, 0, 119, 0, 115, 0, 0, 0, 47, 35,
+ 0, 0, 0, 99, 0, 98, 0, 0, 0, 0,
+ 21, 0, 0, 0, 0, 0, 82, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 57, 55, 53,
+ 52, 0, 0, 0, 0, 105, 19, 20, 71, 72,
+ 0, 50, 51, 0, 23, 0, 0, 47, 0, 0,
+ 0, 0, 105, 106, 107, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 116, 31, 0, 109,
+ 108, 110, 0, 0, 34, 0, 0, 0, 0, 0,
+ 0, 0, 8, 9, 6, 10, 0, 16, 83, 0,
+ 0, 0, 0, 17, 0, 73, 58, 0, 18, 0,
+ 0, 0, 50, 0, 0, 47, 0, 0, 0, 0,
+ 0, 47, 0, 0, 129, 128, 127, 0, 0, 120,
+ 121, 122, 123, 124, 0, 102, 0, 36, 0, 100,
+ 37, 47, 0, 0, 93, 15, 85, 89, 90, 86,
+ 87, 88, 101, 54, 0, 65, 77, 75, 49, 24,
+ 0, 60, 61, 0, 29, 47, 28, 0, 103, 125,
+ 126, 32, 33, 0, 0, 39, 0, 0, 14, 26,
+ 25, 27, 0, 0, 38, 0, 41, 0, 104, 0,
+ 0, 0, 0, 96, 0, 0, 42, 0, 0, 0,
+ 0, 117, 84, 97, 0, 40, 118
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const yytype_int16 yydefgoto[] =
+{
+ -1, 1, 3, 31, 143, 141, 32, 34, 102, 105,
+ 90, 91, 176, 92, 167, 93, 94, 95, 77, 78,
+ 79, 306, 80, 266, 81, 114, 314, 134, 98, 99,
+ 121, 202, 122, 325, 123
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -147
+static const yytype_int16 yypact[] =
+{
+ -147, 19, -147, 305, -37, -147, -147, -147, -147, -40,
+ -147, -147, -147, -147, -147, 448, 448, -147, -40, -40,
+ -147, -147, -147, -147, -147, -147, -40, -8, -22, -21,
+ -147, -147, -20, -147, 102, 102, 334, 108, -40, 416,
+ 108, 102, 397, 48, 108, 482, 482, -147, 95, -147,
+ 482, 482, -19, 18, 34, 223, -147, 5, 174, 424,
+ 132, 174, 223, 223, 20, -147, 482, -147, 482, -147,
+ -147, -147, 30, -147, -147, -147, 482, 35, -147, -147,
+ -147, -147, 49, 76, 84, -147, -147, -147, 205, 339,
+ -147, -147, 93, -147, -147, -147, -147, 110, -147, 124,
+ 125, 442, -147, 98, 98, -147, 128, 373, 134, 36,
+ 144, 147, 20, 153, -147, -147, -147, -147, 60, 482,
+ 482, 175, -147, 114, -147, 249, 21, 482, -40, -147,
+ 182, 183, 12, -147, 185, -147, 188, 189, 198, 36,
+ -147, 305, 562, 305, 572, 482, -147, 36, 256, 258,
+ 261, 269, 36, 482, 212, 460, 216, -147, -147, -147,
+ 125, 373, 36, 138, 586, 95, -147, -147, -147, -147,
+ 217, -147, -147, 247, -147, 36, 226, 7, 229, 138,
+ 227, 20, 98, -147, -147, 21, 482, 482, 482, 287,
+ 301, 482, 482, 482, 482, 482, -147, -147, 248, -147,
+ -147, -147, 243, 250, -147, 66, 482, 257, 106, 66,
+ 36, 36, -147, -147, -147, -147, 225, -147, 251, 205,
+ 205, 205, 205, -147, 266, -147, -147, 478, -147, 267,
+ 278, 279, 175, 215, 280, -40, 253, 36, 36, 36,
+ 36, 297, 295, 298, 601, 345, 609, 482, 482, 190,
+ 190, -147, -147, -147, 300, -147, 5, -147, 552, 303,
+ -147, -40, 306, 307, -147, 36, -147, -147, -147, -147,
+ -147, -147, -147, -147, 125, -147, -147, -147, -147, -147,
+ 486, -147, -147, 309, -147, 130, -147, 331, -147, 121,
+ 121, -147, -147, 36, 66, -147, 322, 36, -147, -147,
+ -147, -147, 308, 326, -147, 36, -147, 327, -147, 119,
+ 329, 36, 333, -147, 338, 36, -147, 482, 119, 330,
+ 292, 341, -147, -147, 482, -147, 631
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const yytype_int16 yypgoto[] =
+{
+ -147, -147, -147, -114, -147, -147, -147, 579, 44, 311,
+ -49, 348, 0, -97, -147, -44, -39, -80, -9, -119,
+ -13, -147, -25, 2, -146, -34, 91, -147, -14, -3,
+ -89, 228, -11, -147, -30
+};
+
+/* 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 -60
+static const yytype_int16 yytable[] =
+{
+ 82, 82, 82, 110, 53, 53, 197, 82, 129, 159,
+ 174, 97, 52, 54, 171, 172, 237, 230, 231, 2,
+ 140, 207, 38, 96, 33, 100, 83, 212, 106, 214,
+ 113, 111, 108, 231, 116, 117, 142, 67, 144, 68,
+ 124, 69, 128, 125, 130, 127, 135, 136, 131, 137,
+ 138, 65, 66, 39, 154, 148, 149, 168, 150, 199,
+ 200, 201, 56, 57, 228, 146, 157, 182, 180, 38,
+ 64, -59, 89, 82, 70, 177, 232, 71, 160, 72,
+ 73, 101, 97, 126, 107, 139, 70, 112, 115, 71,
+ 170, 72, 73, 242, 96, 145, 100, 147, 169, 74,
+ 75, 118, 151, 119, 120, 154, 119, 120, 183, 184,
+ 38, 84, 85, 218, 198, 216, 203, 186, 187, 188,
+ 189, 190, 191, 192, 193, 194, 195, 82, 229, 191,
+ 192, 193, 194, 195, 217, 291, 97, 241, 152, 223,
+ 70, 235, 224, 71, 117, 72, 73, 273, 96, 153,
+ 100, 84, 259, 74, 75, 161, 244, 245, 246, 74,
+ 75, 249, 250, 251, 252, 253, 312, 313, 70, 76,
+ 38, 71, 204, 72, 73, 162, 258, 84, 85, 71,
+ 196, 72, 73, 74, 75, 267, 267, 267, 267, 163,
+ 164, 257, 38, 173, 260, 261, 175, 262, 263, 132,
+ 193, 194, 195, 281, 177, 177, 178, 292, 268, 268,
+ 268, 268, 179, 45, 46, 181, 274, 289, 290, 84,
+ 85, 238, 269, 270, 271, 74, 75, 284, 186, 187,
+ 188, 189, 190, 191, 192, 193, 194, 195, 282, 283,
+ 185, 299, 47, 70, 205, 206, 71, 208, 72, 73,
+ 209, 210, 298, 199, 200, 201, 234, 45, 46, 303,
+ 211, 70, 219, 49, 71, 220, 72, 73, 221, 111,
+ 50, 310, 301, 51, 74, 75, 222, 316, 225, 279,
+ 304, 319, 233, 227, 307, 286, 47, 320, 234, 240,
+ 236, 264, 239, 247, 326, 186, 187, 188, 189, 190,
+ 191, 192, 193, 194, 195, 295, 4, 49, 248, 255,
+ 254, 226, 256, 265, 50, 280, 76, 51, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 272, 275, 19, 20, 21, 22, 23, 24,
+ 25, 26, 45, 46, 276, 277, 278, 45, 155, 156,
+ 188, 189, 190, 191, 192, 193, 194, 195, 323, 285,
+ 287, 27, 28, 29, 288, 294, 30, 76, 296, 297,
+ 302, 47, 70, 300, 308, 71, 47, 72, 73, 84,
+ 85, 45, 46, 86, 87, 74, 75, 305, 309, 311,
+ 48, 315, 49, 157, 158, 48, 322, 49, 317, 88,
+ 318, 89, 51, 324, 88, 45, 46, 51, 133, 321,
+ 47, 70, 166, 243, 71, 0, 72, 73, 84, 85,
+ 0, 0, 86, 87, 45, 46, 0, 0, 0, 48,
+ 0, 49, 45, 46, 47, 0, 0, 0, 88, 0,
+ 89, 51, 0, 0, 0, 0, 0, 0, 74, 75,
+ 45, 46, 0, 47, 0, 49, 45, 46, 0, 0,
+ 109, 47, 88, 0, 0, 51, 0, 0, 45, 46,
+ 0, 0, 103, 104, 49, 74, 75, 0, 0, 47,
+ 0, 50, 49, 0, 51, 47, 45, 46, 0, 50,
+ 45, 46, 51, 0, 45, 46, 0, 47, 165, 104,
+ 49, 0, 0, 0, 48, 0, 49, 88, 0, 0,
+ 51, 0, 0, 50, 226, 47, 51, 0, 49, 47,
+ 0, 0, 0, 47, 0, 50, 0, 0, 51, 0,
+ 0, 0, 0, 0, 48, 0, 49, 0, 0, 0,
+ 49, 0, 0, 88, 49, 0, 51, 50, 0, 0,
+ 51, 88, 0, 0, 51, 186, 187, 188, 189, 190,
+ 191, 192, 193, 194, 195, 186, 187, 188, 189, 190,
+ 191, 192, 193, 194, 195, 186, 187, 188, 189, 190,
+ 191, 192, 193, 194, 195, 35, 36, 37, 0, 40,
+ 41, 42, 43, 44, 0, 0, 55, 0, 0, 58,
+ 59, 60, 61, 62, 63, 187, 188, 189, 190, 191,
+ 192, 193, 194, 195, 293, 189, 190, 191, 192, 193,
+ 194, 195, 0, 213, 199, 200, 201, 71, 0, 72,
+ 73, 0, 0, 215, 186, 187, 188, 189, 190, 191,
+ 192, 193, 194, 195
+};
+
+static const yytype_int16 yycheck[] =
+{
+ 34, 35, 36, 42, 15, 16, 125, 41, 57, 89,
+ 107, 36, 15, 16, 103, 104, 9, 163, 164, 0,
+ 64, 9, 62, 36, 61, 36, 35, 141, 39, 143,
+ 43, 42, 41, 179, 45, 46, 66, 59, 68, 60,
+ 51, 61, 55, 62, 58, 11, 60, 61, 59, 62,
+ 63, 59, 60, 9, 88, 6, 7, 101, 9, 38,
+ 39, 40, 18, 19, 161, 76, 54, 7, 112, 62,
+ 26, 64, 67, 107, 38, 109, 165, 41, 89, 43,
+ 44, 37, 107, 65, 40, 65, 38, 43, 44, 41,
+ 101, 43, 44, 182, 107, 65, 107, 62, 101, 51,
+ 52, 6, 53, 8, 9, 139, 8, 9, 119, 120,
+ 62, 45, 46, 147, 125, 145, 127, 3, 4, 5,
+ 6, 7, 8, 9, 10, 11, 12, 161, 162, 8,
+ 9, 10, 11, 12, 147, 254, 161, 181, 62, 152,
+ 38, 175, 153, 41, 155, 43, 44, 227, 161, 65,
+ 161, 45, 46, 51, 52, 62, 186, 187, 188, 51,
+ 52, 191, 192, 193, 194, 195, 47, 48, 38, 67,
+ 62, 41, 128, 43, 44, 65, 206, 45, 46, 41,
+ 66, 43, 44, 51, 52, 219, 220, 221, 222, 65,
+ 65, 205, 62, 65, 208, 209, 62, 210, 211, 67,
+ 10, 11, 12, 237, 238, 239, 62, 256, 219, 220,
+ 221, 222, 65, 8, 9, 62, 227, 247, 248, 45,
+ 46, 177, 220, 221, 222, 51, 52, 240, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 238, 239,
+ 65, 280, 37, 38, 62, 62, 41, 62, 43, 44,
+ 62, 62, 265, 38, 39, 40, 41, 8, 9, 293,
+ 62, 38, 6, 58, 41, 7, 43, 44, 7, 280,
+ 65, 305, 285, 68, 51, 52, 7, 311, 66, 235,
+ 294, 315, 65, 67, 297, 241, 37, 317, 41, 62,
+ 64, 66, 63, 6, 324, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 261, 1, 58, 7, 66,
+ 62, 54, 62, 62, 65, 62, 67, 68, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 66, 66, 29, 30, 31, 32, 33, 34,
+ 35, 36, 8, 9, 66, 66, 66, 8, 9, 10,
+ 5, 6, 7, 8, 9, 10, 11, 12, 66, 62,
+ 65, 56, 57, 58, 66, 62, 61, 67, 62, 62,
+ 39, 37, 38, 64, 66, 41, 37, 43, 44, 45,
+ 46, 8, 9, 49, 50, 51, 52, 65, 62, 62,
+ 56, 62, 58, 54, 55, 56, 66, 58, 65, 65,
+ 62, 67, 68, 62, 65, 8, 9, 68, 60, 318,
+ 37, 38, 101, 185, 41, -1, 43, 44, 45, 46,
+ -1, -1, 49, 50, 8, 9, -1, -1, -1, 56,
+ -1, 58, 8, 9, 37, -1, -1, -1, 65, -1,
+ 67, 68, -1, -1, -1, -1, -1, -1, 51, 52,
+ 8, 9, -1, 37, -1, 58, 8, 9, -1, -1,
+ 63, 37, 65, -1, -1, 68, -1, -1, 8, 9,
+ -1, -1, 56, 57, 58, 51, 52, -1, -1, 37,
+ -1, 65, 58, -1, 68, 37, 8, 9, -1, 65,
+ 8, 9, 68, -1, 8, 9, -1, 37, 56, 57,
+ 58, -1, -1, -1, 56, -1, 58, 65, -1, -1,
+ 68, -1, -1, 65, 54, 37, 68, -1, 58, 37,
+ -1, -1, -1, 37, -1, 65, -1, -1, 68, -1,
+ -1, -1, -1, -1, 56, -1, 58, -1, -1, -1,
+ 58, -1, -1, 65, 58, -1, 68, 65, -1, -1,
+ 68, 65, -1, -1, 68, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 6, 7, 8, -1, 10,
+ 11, 12, 13, 14, -1, -1, 17, -1, -1, 20,
+ 21, 22, 23, 24, 25, 4, 5, 6, 7, 8,
+ 9, 10, 11, 12, 62, 6, 7, 8, 9, 10,
+ 11, 12, -1, 61, 38, 39, 40, 41, -1, 43,
+ 44, -1, -1, 61, 3, 4, 5, 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, 70, 0, 71, 1, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, 22, 23, 24, 25, 26, 29,
+ 30, 31, 32, 33, 34, 35, 36, 56, 57, 58,
+ 61, 72, 75, 61, 76, 76, 76, 76, 62, 77,
+ 76, 76, 76, 76, 76, 8, 9, 37, 56, 58,
+ 65, 68, 98, 101, 98, 76, 77, 77, 76, 76,
+ 76, 76, 76, 76, 77, 59, 60, 59, 60, 61,
+ 38, 41, 43, 44, 51, 52, 67, 87, 88, 89,
+ 91, 93, 94, 87, 45, 46, 49, 50, 65, 67,
+ 79, 80, 82, 84, 85, 86, 89, 91, 97, 98,
+ 101, 77, 77, 56, 57, 78, 101, 77, 87, 63,
+ 85, 101, 77, 89, 94, 77, 101, 101, 6, 8,
+ 9, 99, 101, 103, 101, 62, 65, 11, 89, 79,
+ 97, 101, 67, 80, 96, 97, 97, 89, 89, 65,
+ 84, 74, 103, 73, 103, 65, 101, 62, 6, 7,
+ 9, 53, 62, 65, 94, 9, 10, 54, 55, 86,
+ 101, 62, 65, 65, 65, 56, 78, 83, 84, 98,
+ 101, 99, 99, 65, 82, 62, 81, 94, 62, 65,
+ 84, 62, 7, 101, 101, 65, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 66, 88, 101, 38,
+ 39, 40, 100, 101, 77, 62, 62, 9, 62, 62,
+ 62, 62, 72, 61, 72, 61, 103, 89, 94, 6,
+ 7, 7, 7, 89, 101, 66, 54, 67, 82, 94,
+ 93, 93, 99, 65, 41, 94, 64, 9, 77, 63,
+ 62, 84, 99, 100, 103, 103, 103, 6, 7, 103,
+ 103, 103, 103, 103, 62, 66, 62, 97, 103, 46,
+ 97, 97, 89, 89, 66, 62, 92, 94, 101, 92,
+ 92, 92, 66, 86, 101, 66, 66, 66, 66, 77,
+ 62, 94, 81, 81, 89, 62, 77, 65, 66, 103,
+ 103, 88, 79, 62, 62, 77, 62, 62, 89, 85,
+ 64, 89, 39, 94, 97, 65, 90, 89, 66, 62,
+ 94, 62, 47, 48, 95, 62, 94, 65, 62, 94,
+ 103, 95, 66, 66, 62, 102, 103
+};
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+/* 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. */
+
+#define YYFAIL goto yyerrlab
+
+#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 \
+ { \
+ yyerror (YY_("syntax error: cannot back up")); \
+ YYERROR; \
+ } \
+while (YYID (0))
+
+
+#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. */
+
+#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
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments. */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (YYLEX_PARAM)
+#else
+# define YYLEX yylex ()
+#endif
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yy_symbol_print (stderr, \
+ Type, Value); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+#endif
+{
+ if (!yyvaluep)
+ return;
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+ YYUSE (yyoutput);
+# endif
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+#endif
+{
+ if (yytype < YYNTOKENS)
+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+ else
+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+ yy_symbol_value_print (yyoutput, yytype, yyvaluep);
+ YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop)
+#else
+static void
+yy_stack_print (yybottom, yytop)
+ yytype_int16 *yybottom;
+ yytype_int16 *yytop;
+#endif
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (; yybottom <= yytop; yybottom++)
+ {
+ int yybot = *yybottom;
+ YYFPRINTF (stderr, " %d", yybot);
+ }
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, int yyrule)
+#else
+static void
+yy_reduce_print (yyvsp, yyrule)
+ YYSTYPE *yyvsp;
+ int yyrule;
+#endif
+{
+ int yynrhs = yyr2[yyrule];
+ int yyi;
+ unsigned long int yylno = yyrline[yyrule];
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+ yyrule - 1, yylno);
+ /* The symbols being reduced. */
+ for (yyi = 0; yyi < yynrhs; yyi++)
+ {
+ YYFPRINTF (stderr, " $%d = ", yyi + 1);
+ yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+ &(yyvsp[(yyi + 1) - (yynrhs)])
+ );
+ YYFPRINTF (stderr, "\n");
+ }
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (yyvsp, Rule); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined __GLIBC__ && defined _STRING_H
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+ const char *yystr;
+#endif
+{
+ YYSIZE_T yylen;
+ for (yylen = 0; yystr[yylen]; yylen++)
+ continue;
+ return yylen;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+ char *yydest;
+ const char *yysrc;
+#endif
+{
+ char *yyd = yydest;
+ const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+ quotes and backslashes, so that it's suitable for yyerror. The
+ heuristic is that double-quoting is unnecessary unless the string
+ contains an apostrophe, a comma, or backslash (other than
+ backslash-backslash). YYSTR is taken from yytname. If YYRES is
+ null, do not copy; instead, return the length of what the result
+ would have been. */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+ if (*yystr == '"')
+ {
+ YYSIZE_T yyn = 0;
+ char const *yyp = yystr;
+
+ for (;;)
+ switch (*++yyp)
+ {
+ case '\'':
+ case ',':
+ goto do_not_strip_quotes;
+
+ case '\\':
+ if (*++yyp != '\\')
+ goto do_not_strip_quotes;
+ /* Fall through. */
+ default:
+ if (yyres)
+ yyres[yyn] = *yyp;
+ yyn++;
+ break;
+
+ case '"':
+ if (yyres)
+ yyres[yyn] = '\0';
+ return yyn;
+ }
+ do_not_strip_quotes: ;
+ }
+
+ if (! yyres)
+ return yystrlen (yystr);
+
+ return yystpcpy (yyres, yystr) - yyres;
+}
+# 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];
+
+ 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;
+ }
+
+ yyf = YY_(yyformat);
+ yysize1 = yysize + yystrlen (yyf);
+ yysize_overflow |= (yysize1 < yysize);
+ yysize = yysize1;
+
+ if (yysize_overflow)
+ return YYSIZE_MAXIMUM;
+
+ 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;
+ }
+}
+#endif /* YYERROR_VERBOSE */
+
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep)
+ const char *yymsg;
+ int yytype;
+ YYSTYPE *yyvaluep;
+#endif
+{
+ YYUSE (yyvaluep);
+
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ switch (yytype)
+ {
+
+ default:
+ break;
+ }
+}
+
+/* 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 lookahead symbol. */
+int yychar;
+
+/* The semantic value of the lookahead symbol. */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far. */
+int yynerrs;
+
+
+
+/*-------------------------.
+| yyparse or yypush_parse. |
+`-------------------------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+ void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+
+
+ 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 thru 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;
+ /* 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. */
+
+ /* 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;
+
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. So pushing a state here evens the stacks. */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ 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
+ be undefined if yyoverflow is a macro. */
+ yyoverflow (YY_("memory exhausted"),
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+ &yystacksize);
+
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyexhaustedlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyexhaustedlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ yytype_int16 *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyexhaustedlab;
+ YYSTACK_RELOCATE (yyss_alloc, yyss);
+ YYSTACK_RELOCATE (yyvs_alloc, yyvs);
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ if (yystate == YYFINAL)
+ YYACCEPT;
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+ /* Do appropriate processing given the current state. Read a
+ lookahead token if we need one and don't already have one. */
+
+ /* First try to decide what to do without reference to lookahead token. */
+ yyn = yypact[yystate];
+ if (yyn == YYPACT_NINF)
+ goto yydefault;
+
+ /* Not known => get a lookahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = YYLEX;
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yyn == 0 || yyn == YYTABLE_NINF)
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ /* Shift the lookahead token. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+ /* Discard the shifted token. */
+ yychar = YYEMPTY;
+
+ yystate = yyn;
+ *++yyvsp = yylval;
+
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ `$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 3:
+
+/* Line 1455 of yacc.c */
+#line 69 "a.y"
+ {
+ stmtline = lineno;
+ }
+ break;
+
+ case 5:
+
+/* Line 1455 of yacc.c */
+#line 76 "a.y"
+ {
+ if((yyvsp[(1) - (2)].sym)->value != pc)
+ yyerror("redeclaration of %s", (yyvsp[(1) - (2)].sym)->name);
+ (yyvsp[(1) - (2)].sym)->value = pc;
+ }
+ break;
+
+ case 7:
+
+/* Line 1455 of yacc.c */
+#line 83 "a.y"
+ {
+ (yyvsp[(1) - (2)].sym)->type = LLAB;
+ (yyvsp[(1) - (2)].sym)->value = pc;
+ }
+ break;
+
+ case 9:
+
+/* Line 1455 of yacc.c */
+#line 89 "a.y"
+ {
+ (yyvsp[(1) - (4)].sym)->type = LVAR;
+ (yyvsp[(1) - (4)].sym)->value = (yyvsp[(3) - (4)].lval);
+ }
+ break;
+
+ case 10:
+
+/* Line 1455 of yacc.c */
+#line 94 "a.y"
+ {
+ if((yyvsp[(1) - (4)].sym)->value != (yyvsp[(3) - (4)].lval))
+ yyerror("redeclaration of %s", (yyvsp[(1) - (4)].sym)->name);
+ (yyvsp[(1) - (4)].sym)->value = (yyvsp[(3) - (4)].lval);
+ }
+ break;
+
+ case 14:
+
+/* Line 1455 of yacc.c */
+#line 108 "a.y"
+ {
+ outcode((yyvsp[(1) - (7)].lval), (yyvsp[(2) - (7)].lval), &(yyvsp[(3) - (7)].gen), (yyvsp[(5) - (7)].lval), &(yyvsp[(7) - (7)].gen));
+ }
+ break;
+
+ case 15:
+
+/* Line 1455 of yacc.c */
+#line 112 "a.y"
+ {
+ outcode((yyvsp[(1) - (6)].lval), (yyvsp[(2) - (6)].lval), &(yyvsp[(3) - (6)].gen), (yyvsp[(5) - (6)].lval), &nullgen);
+ }
+ break;
+
+ case 16:
+
+/* Line 1455 of yacc.c */
+#line 116 "a.y"
+ {
+ outcode((yyvsp[(1) - (5)].lval), (yyvsp[(2) - (5)].lval), &(yyvsp[(3) - (5)].gen), NREG, &(yyvsp[(5) - (5)].gen));
+ }
+ break;
+
+ case 17:
+
+/* Line 1455 of yacc.c */
+#line 123 "a.y"
+ {
+ outcode((yyvsp[(1) - (5)].lval), (yyvsp[(2) - (5)].lval), &(yyvsp[(3) - (5)].gen), NREG, &(yyvsp[(5) - (5)].gen));
+ }
+ break;
+
+ case 18:
+
+/* Line 1455 of yacc.c */
+#line 130 "a.y"
+ {
+ outcode((yyvsp[(1) - (5)].lval), (yyvsp[(2) - (5)].lval), &(yyvsp[(3) - (5)].gen), NREG, &(yyvsp[(5) - (5)].gen));
+ }
+ break;
+
+ case 19:
+
+/* Line 1455 of yacc.c */
+#line 137 "a.y"
+ {
+ outcode((yyvsp[(1) - (4)].lval), (yyvsp[(2) - (4)].lval), &nullgen, NREG, &(yyvsp[(4) - (4)].gen));
+ }
+ break;
+
+ case 20:
+
+/* Line 1455 of yacc.c */
+#line 141 "a.y"
+ {
+ outcode((yyvsp[(1) - (4)].lval), (yyvsp[(2) - (4)].lval), &nullgen, NREG, &(yyvsp[(4) - (4)].gen));
+ }
+ break;
+
+ case 21:
+
+/* Line 1455 of yacc.c */
+#line 148 "a.y"
+ {
+ outcode((yyvsp[(1) - (3)].lval), Always, &nullgen, NREG, &(yyvsp[(3) - (3)].gen));
+ }
+ break;
+
+ case 22:
+
+/* Line 1455 of yacc.c */
+#line 155 "a.y"
+ {
+ outcode((yyvsp[(1) - (3)].lval), Always, &nullgen, NREG, &(yyvsp[(3) - (3)].gen));
+ }
+ break;
+
+ case 23:
+
+/* Line 1455 of yacc.c */
+#line 162 "a.y"
+ {
+ outcode((yyvsp[(1) - (4)].lval), (yyvsp[(2) - (4)].lval), &nullgen, NREG, &(yyvsp[(4) - (4)].gen));
+ }
+ break;
+
+ case 24:
+
+/* Line 1455 of yacc.c */
+#line 169 "a.y"
+ {
+ outcode((yyvsp[(1) - (6)].lval), (yyvsp[(2) - (6)].lval), &(yyvsp[(3) - (6)].gen), (yyvsp[(5) - (6)].lval), &nullgen);
+ }
+ break;
+
+ case 25:
+
+/* Line 1455 of yacc.c */
+#line 176 "a.y"
+ {
+ Gen g;
+
+ g = nullgen;
+ g.type = D_CONST;
+ g.offset = (yyvsp[(6) - (7)].lval);
+ outcode((yyvsp[(1) - (7)].lval), (yyvsp[(2) - (7)].lval), &(yyvsp[(3) - (7)].gen), NREG, &g);
+ }
+ break;
+
+ case 26:
+
+/* Line 1455 of yacc.c */
+#line 185 "a.y"
+ {
+ Gen g;
+
+ g = nullgen;
+ g.type = D_CONST;
+ g.offset = (yyvsp[(4) - (7)].lval);
+ outcode((yyvsp[(1) - (7)].lval), (yyvsp[(2) - (7)].lval), &g, NREG, &(yyvsp[(7) - (7)].gen));
+ }
+ break;
+
+ case 27:
+
+/* Line 1455 of yacc.c */
+#line 197 "a.y"
+ {
+ outcode((yyvsp[(1) - (7)].lval), (yyvsp[(2) - (7)].lval), &(yyvsp[(5) - (7)].gen), (yyvsp[(3) - (7)].gen).reg, &(yyvsp[(7) - (7)].gen));
+ }
+ break;
+
+ case 28:
+
+/* Line 1455 of yacc.c */
+#line 201 "a.y"
+ {
+ outcode((yyvsp[(1) - (6)].lval), (yyvsp[(2) - (6)].lval), &(yyvsp[(5) - (6)].gen), (yyvsp[(3) - (6)].gen).reg, &(yyvsp[(3) - (6)].gen));
+ }
+ break;
+
+ case 29:
+
+/* Line 1455 of yacc.c */
+#line 205 "a.y"
+ {
+ outcode((yyvsp[(1) - (6)].lval), (yyvsp[(2) - (6)].lval), &(yyvsp[(4) - (6)].gen), (yyvsp[(6) - (6)].gen).reg, &(yyvsp[(6) - (6)].gen));
+ }
+ break;
+
+ case 30:
+
+/* Line 1455 of yacc.c */
+#line 212 "a.y"
+ {
+ outcode((yyvsp[(1) - (3)].lval), (yyvsp[(2) - (3)].lval), &nullgen, NREG, &nullgen);
+ }
+ break;
+
+ case 31:
+
+/* Line 1455 of yacc.c */
+#line 219 "a.y"
+ {
+ outcode((yyvsp[(1) - (4)].lval), Always, &(yyvsp[(2) - (4)].gen), NREG, &(yyvsp[(4) - (4)].gen));
+ }
+ break;
+
+ case 32:
+
+/* Line 1455 of yacc.c */
+#line 223 "a.y"
+ {
+ 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"
+ {
+ outcode((yyvsp[(1) - (6)].lval), Always, &(yyvsp[(2) - (6)].gen), (yyvsp[(4) - (6)].lval), &(yyvsp[(6) - (6)].gen));
+ }
+ break;
+
+ case 34:
+
+/* Line 1455 of yacc.c */
+#line 237 "a.y"
+ {
+ outcode((yyvsp[(1) - (4)].lval), (yyvsp[(2) - (4)].lval), &(yyvsp[(3) - (4)].gen), NREG, &nullgen);
+ }
+ break;
+
+ case 35:
+
+/* Line 1455 of yacc.c */
+#line 244 "a.y"
+ {
+ outcode((yyvsp[(1) - (3)].lval), Always, &nullgen, NREG, &(yyvsp[(3) - (3)].gen));
+ }
+ break;
+
+ case 36:
+
+/* Line 1455 of yacc.c */
+#line 251 "a.y"
+ {
+ outcode((yyvsp[(1) - (5)].lval), (yyvsp[(2) - (5)].lval), &(yyvsp[(3) - (5)].gen), NREG, &(yyvsp[(5) - (5)].gen));
+ }
+ break;
+
+ case 37:
+
+/* Line 1455 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));
+ }
+ break;
+
+ case 38:
+
+/* Line 1455 of yacc.c */
+#line 259 "a.y"
+ {
+ outcode((yyvsp[(1) - (7)].lval), (yyvsp[(2) - (7)].lval), &(yyvsp[(3) - (7)].gen), (yyvsp[(5) - (7)].lval), &(yyvsp[(7) - (7)].gen));
+ }
+ break;
+
+ case 39:
+
+/* Line 1455 of yacc.c */
+#line 263 "a.y"
+ {
+ outcode((yyvsp[(1) - (6)].lval), (yyvsp[(2) - (6)].lval), &(yyvsp[(3) - (6)].gen), (yyvsp[(5) - (6)].gen).reg, &nullgen);
+ }
+ break;
+
+ case 40:
+
+/* Line 1455 of yacc.c */
+#line 270 "a.y"
+ {
+ Gen g;
+
+ g = nullgen;
+ g.type = D_CONST;
+ g.offset =
+ (0xe << 24) | /* opcode */
+ ((yyvsp[(1) - (12)].lval) << 20) | /* MCR/MRC */
+ ((yyvsp[(2) - (12)].lval) << 28) | /* scond */
+ (((yyvsp[(3) - (12)].lval) & 15) << 8) | /* coprocessor number */
+ (((yyvsp[(5) - (12)].lval) & 7) << 21) | /* coprocessor operation */
+ (((yyvsp[(7) - (12)].lval) & 15) << 12) | /* arm register */
+ (((yyvsp[(9) - (12)].lval) & 15) << 16) | /* Crn */
+ (((yyvsp[(11) - (12)].lval) & 15) << 0) | /* Crm */
+ (((yyvsp[(12) - (12)].lval) & 7) << 5) | /* coprocessor information */
+ (1<<4); /* must be set */
+ outcode(AWORD, Always, &nullgen, NREG, &g);
+ }
+ break;
+
+ case 41:
+
+/* Line 1455 of yacc.c */
+#line 292 "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:
+
+/* Line 1455 of yacc.c */
+#line 299 "a.y"
+ {
+ (yyvsp[(7) - (9)].gen).type = D_REGREG;
+ (yyvsp[(7) - (9)].gen).offset = (yyvsp[(9) - (9)].lval);
+ outcode((yyvsp[(1) - (9)].lval), (yyvsp[(2) - (9)].lval), &(yyvsp[(3) - (9)].gen), (yyvsp[(5) - (9)].gen).reg, &(yyvsp[(7) - (9)].gen));
+ }
+ break;
+
+ case 43:
+
+/* Line 1455 of yacc.c */
+#line 308 "a.y"
+ {
+ outcode((yyvsp[(1) - (2)].lval), Always, &nullgen, NREG, &nullgen);
+ }
+ break;
+
+ case 44:
+
+/* Line 1455 of yacc.c */
+#line 313 "a.y"
+ {
+ (yyval.lval) = Always;
+ }
+ break;
+
+ case 45:
+
+/* Line 1455 of yacc.c */
+#line 317 "a.y"
+ {
+ (yyval.lval) = ((yyvsp[(1) - (2)].lval) & ~C_SCOND) | (yyvsp[(2) - (2)].lval);
+ }
+ break;
+
+ case 46:
+
+/* Line 1455 of yacc.c */
+#line 321 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(1) - (2)].lval) | (yyvsp[(2) - (2)].lval);
+ }
+ break;
+
+ case 49:
+
+/* Line 1455 of yacc.c */
+#line 330 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_BRANCH;
+ (yyval.gen).offset = (yyvsp[(1) - (4)].lval) + pc;
+ }
+ break;
+
+ case 50:
+
+/* Line 1455 of yacc.c */
+#line 336 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ if(pass == 2)
+ yyerror("undefined label: %s", (yyvsp[(1) - (2)].sym)->name);
+ (yyval.gen).type = D_BRANCH;
+ (yyval.gen).sym = (yyvsp[(1) - (2)].sym);
+ (yyval.gen).offset = (yyvsp[(2) - (2)].lval);
+ }
+ break;
+
+ case 51:
+
+/* Line 1455 of yacc.c */
+#line 345 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_BRANCH;
+ (yyval.gen).sym = (yyvsp[(1) - (2)].sym);
+ (yyval.gen).offset = (yyvsp[(1) - (2)].sym)->value + (yyvsp[(2) - (2)].lval);
+ }
+ break;
+
+ case 52:
+
+/* Line 1455 of yacc.c */
+#line 353 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_CONST;
+ (yyval.gen).offset = (yyvsp[(2) - (2)].lval);
+ }
+ break;
+
+ case 53:
+
+/* Line 1455 of yacc.c */
+#line 359 "a.y"
+ {
+ (yyval.gen) = (yyvsp[(2) - (2)].gen);
+ (yyval.gen).type = D_CONST;
+ }
+ break;
+
+ case 54:
+
+/* Line 1455 of yacc.c */
+#line 364 "a.y"
+ {
+ (yyval.gen) = (yyvsp[(4) - (4)].gen);
+ (yyval.gen).type = D_OCONST;
+ }
+ break;
+
+ case 55:
+
+/* Line 1455 of yacc.c */
+#line 369 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_SCONST;
+ memcpy((yyval.gen).sval, (yyvsp[(2) - (2)].sval), sizeof((yyval.gen).sval));
+ }
+ break;
+
+ case 57:
+
+/* Line 1455 of yacc.c */
+#line 378 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_FCONST;
+ (yyval.gen).dval = (yyvsp[(2) - (2)].dval);
+ }
+ break;
+
+ case 58:
+
+/* Line 1455 of yacc.c */
+#line 384 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_FCONST;
+ (yyval.gen).dval = -(yyvsp[(3) - (3)].dval);
+ }
+ break;
+
+ case 59:
+
+/* Line 1455 of yacc.c */
+#line 392 "a.y"
+ {
+ (yyval.lval) = 1 << (yyvsp[(1) - (1)].lval);
+ }
+ break;
+
+ case 60:
+
+/* Line 1455 of yacc.c */
+#line 396 "a.y"
+ {
+ int i;
+ (yyval.lval)=0;
+ for(i=(yyvsp[(1) - (3)].lval); i<=(yyvsp[(3) - (3)].lval); i++)
+ (yyval.lval) |= 1<<i;
+ for(i=(yyvsp[(3) - (3)].lval); i<=(yyvsp[(1) - (3)].lval); i++)
+ (yyval.lval) |= 1<<i;
+ }
+ break;
+
+ case 61:
+
+/* Line 1455 of yacc.c */
+#line 405 "a.y"
+ {
+ (yyval.lval) = (1<<(yyvsp[(1) - (3)].lval)) | (yyvsp[(3) - (3)].lval);
+ }
+ break;
+
+ case 65:
+
+/* Line 1455 of yacc.c */
+#line 414 "a.y"
+ {
+ (yyval.gen) = (yyvsp[(1) - (4)].gen);
+ (yyval.gen).reg = (yyvsp[(3) - (4)].lval);
+ }
+ break;
+
+ case 66:
+
+/* Line 1455 of yacc.c */
+#line 419 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_PSR;
+ (yyval.gen).reg = (yyvsp[(1) - (1)].lval);
+ }
+ break;
+
+ case 67:
+
+/* Line 1455 of yacc.c */
+#line 425 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_FPCR;
+ (yyval.gen).reg = (yyvsp[(1) - (1)].lval);
+ }
+ break;
+
+ case 68:
+
+/* Line 1455 of yacc.c */
+#line 431 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_OREG;
+ (yyval.gen).offset = (yyvsp[(1) - (1)].lval);
+ }
+ break;
+
+ case 72:
+
+/* Line 1455 of yacc.c */
+#line 442 "a.y"
+ {
+ (yyval.gen) = (yyvsp[(1) - (1)].gen);
+ if((yyvsp[(1) - (1)].gen).name != D_EXTERN && (yyvsp[(1) - (1)].gen).name != D_STATIC) {
+ }
+ }
+ break;
+
+ case 73:
+
+/* Line 1455 of yacc.c */
+#line 450 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_OREG;
+ (yyval.gen).reg = (yyvsp[(2) - (3)].lval);
+ (yyval.gen).offset = 0;
+ }
+ break;
+
+ case 75:
+
+/* Line 1455 of yacc.c */
+#line 460 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_OREG;
+ (yyval.gen).reg = (yyvsp[(3) - (4)].lval);
+ (yyval.gen).offset = (yyvsp[(1) - (4)].lval);
+ }
+ break;
+
+ case 77:
+
+/* Line 1455 of yacc.c */
+#line 470 "a.y"
+ {
+ (yyval.gen) = (yyvsp[(1) - (4)].gen);
+ (yyval.gen).type = D_OREG;
+ (yyval.gen).reg = (yyvsp[(3) - (4)].lval);
+ }
+ break;
+
+ case 82:
+
+/* Line 1455 of yacc.c */
+#line 483 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_CONST;
+ (yyval.gen).offset = (yyvsp[(2) - (2)].lval);
+ }
+ break;
+
+ case 83:
+
+/* Line 1455 of yacc.c */
+#line 491 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_REG;
+ (yyval.gen).reg = (yyvsp[(1) - (1)].lval);
+ }
+ break;
+
+ case 84:
+
+/* Line 1455 of yacc.c */
+#line 499 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_REGREG;
+ (yyval.gen).reg = (yyvsp[(2) - (5)].lval);
+ (yyval.gen).offset = (yyvsp[(4) - (5)].lval);
+ }
+ break;
+
+ case 85:
+
+/* Line 1455 of yacc.c */
+#line 508 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_SHIFT;
+ (yyval.gen).offset = (yyvsp[(1) - (4)].lval) | (yyvsp[(4) - (4)].lval) | (0 << 5);
+ }
+ break;
+
+ case 86:
+
+/* Line 1455 of yacc.c */
+#line 514 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_SHIFT;
+ (yyval.gen).offset = (yyvsp[(1) - (4)].lval) | (yyvsp[(4) - (4)].lval) | (1 << 5);
+ }
+ break;
+
+ case 87:
+
+/* Line 1455 of yacc.c */
+#line 520 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_SHIFT;
+ (yyval.gen).offset = (yyvsp[(1) - (4)].lval) | (yyvsp[(4) - (4)].lval) | (2 << 5);
+ }
+ break;
+
+ case 88:
+
+/* Line 1455 of yacc.c */
+#line 526 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_SHIFT;
+ (yyval.gen).offset = (yyvsp[(1) - (4)].lval) | (yyvsp[(4) - (4)].lval) | (3 << 5);
+ }
+ break;
+
+ case 89:
+
+/* Line 1455 of yacc.c */
+#line 534 "a.y"
+ {
+ if((yyval.lval) < 0 || (yyval.lval) >= 16)
+ print("register value out of range\n");
+ (yyval.lval) = (((yyvsp[(1) - (1)].lval)&15) << 8) | (1 << 4);
+ }
+ break;
+
+ case 90:
+
+/* Line 1455 of yacc.c */
+#line 540 "a.y"
+ {
+ if((yyval.lval) < 0 || (yyval.lval) >= 32)
+ print("shift value out of range\n");
+ (yyval.lval) = ((yyvsp[(1) - (1)].lval)&31) << 7;
+ }
+ break;
+
+ case 92:
+
+/* Line 1455 of yacc.c */
+#line 549 "a.y"
+ {
+ (yyval.lval) = REGPC;
+ }
+ break;
+
+ case 93:
+
+/* Line 1455 of yacc.c */
+#line 553 "a.y"
+ {
+ if((yyvsp[(3) - (4)].lval) < 0 || (yyvsp[(3) - (4)].lval) >= NREG)
+ print("register value out of range\n");
+ (yyval.lval) = (yyvsp[(3) - (4)].lval);
+ }
+ break;
+
+ case 95:
+
+/* Line 1455 of yacc.c */
+#line 562 "a.y"
+ {
+ (yyval.lval) = REGSP;
+ }
+ break;
+
+ case 97:
+
+/* Line 1455 of yacc.c */
+#line 569 "a.y"
+ {
+ if((yyvsp[(3) - (4)].lval) < 0 || (yyvsp[(3) - (4)].lval) >= NREG)
+ print("register value out of range\n");
+ (yyval.lval) = (yyvsp[(3) - (4)].lval);
+ }
+ break;
+
+ case 100:
+
+/* Line 1455 of yacc.c */
+#line 581 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_FREG;
+ (yyval.gen).reg = (yyvsp[(1) - (1)].lval);
+ }
+ break;
+
+ case 101:
+
+/* Line 1455 of yacc.c */
+#line 587 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_FREG;
+ (yyval.gen).reg = (yyvsp[(3) - (4)].lval);
+ }
+ break;
+
+ case 102:
+
+/* Line 1455 of yacc.c */
+#line 595 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_OREG;
+ (yyval.gen).name = (yyvsp[(3) - (4)].lval);
+ (yyval.gen).sym = S;
+ (yyval.gen).offset = (yyvsp[(1) - (4)].lval);
+ }
+ break;
+
+ case 103:
+
+/* Line 1455 of yacc.c */
+#line 603 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_OREG;
+ (yyval.gen).name = (yyvsp[(4) - (5)].lval);
+ (yyval.gen).sym = (yyvsp[(1) - (5)].sym);
+ (yyval.gen).offset = (yyvsp[(2) - (5)].lval);
+ }
+ break;
+
+ case 104:
+
+/* Line 1455 of yacc.c */
+#line 611 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_OREG;
+ (yyval.gen).name = D_STATIC;
+ (yyval.gen).sym = (yyvsp[(1) - (7)].sym);
+ (yyval.gen).offset = (yyvsp[(4) - (7)].lval);
+ }
+ break;
+
+ case 105:
+
+/* Line 1455 of yacc.c */
+#line 620 "a.y"
+ {
+ (yyval.lval) = 0;
+ }
+ break;
+
+ case 106:
+
+/* Line 1455 of yacc.c */
+#line 624 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(2) - (2)].lval);
+ }
+ break;
+
+ case 107:
+
+/* Line 1455 of yacc.c */
+#line 628 "a.y"
+ {
+ (yyval.lval) = -(yyvsp[(2) - (2)].lval);
+ }
+ break;
+
+ case 112:
+
+/* Line 1455 of yacc.c */
+#line 640 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(1) - (1)].sym)->value;
+ }
+ break;
+
+ case 113:
+
+/* Line 1455 of yacc.c */
+#line 644 "a.y"
+ {
+ (yyval.lval) = -(yyvsp[(2) - (2)].lval);
+ }
+ break;
+
+ case 114:
+
+/* Line 1455 of yacc.c */
+#line 648 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(2) - (2)].lval);
+ }
+ break;
+
+ case 115:
+
+/* Line 1455 of yacc.c */
+#line 652 "a.y"
+ {
+ (yyval.lval) = ~(yyvsp[(2) - (2)].lval);
+ }
+ break;
+
+ case 116:
+
+/* Line 1455 of yacc.c */
+#line 656 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(2) - (3)].lval);
+ }
+ break;
+
+ case 117:
+
+/* Line 1455 of yacc.c */
+#line 661 "a.y"
+ {
+ (yyval.lval) = 0;
+ }
+ break;
+
+ case 118:
+
+/* Line 1455 of yacc.c */
+#line 665 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(2) - (2)].lval);
+ }
+ break;
+
+ case 120:
+
+/* Line 1455 of yacc.c */
+#line 672 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(1) - (3)].lval) + (yyvsp[(3) - (3)].lval);
+ }
+ break;
+
+ case 121:
+
+/* Line 1455 of yacc.c */
+#line 676 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(1) - (3)].lval) - (yyvsp[(3) - (3)].lval);
+ }
+ break;
+
+ case 122:
+
+/* Line 1455 of yacc.c */
+#line 680 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(1) - (3)].lval) * (yyvsp[(3) - (3)].lval);
+ }
+ break;
+
+ case 123:
+
+/* Line 1455 of yacc.c */
+#line 684 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(1) - (3)].lval) / (yyvsp[(3) - (3)].lval);
+ }
+ break;
+
+ case 124:
+
+/* Line 1455 of yacc.c */
+#line 688 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(1) - (3)].lval) % (yyvsp[(3) - (3)].lval);
+ }
+ break;
+
+ case 125:
+
+/* Line 1455 of yacc.c */
+#line 692 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(1) - (4)].lval) << (yyvsp[(4) - (4)].lval);
+ }
+ break;
+
+ case 126:
+
+/* Line 1455 of yacc.c */
+#line 696 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(1) - (4)].lval) >> (yyvsp[(4) - (4)].lval);
+ }
+ break;
+
+ case 127:
+
+/* Line 1455 of yacc.c */
+#line 700 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(1) - (3)].lval) & (yyvsp[(3) - (3)].lval);
+ }
+ break;
+
+ case 128:
+
+/* Line 1455 of yacc.c */
+#line 704 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(1) - (3)].lval) ^ (yyvsp[(3) - (3)].lval);
+ }
+ break;
+
+ case 129:
+
+/* Line 1455 of yacc.c */
+#line 708 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(1) - (3)].lval) | (yyvsp[(3) - (3)].lval);
+ }
+ break;
+
+
+
+/* Line 1455 of yacc.c */
+#line 2740 "y.tab.c"
+ default: break;
+ }
+ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++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. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if ! YYERROR_VERBOSE
+ yyerror (YY_("syntax error"));
+#else
+ {
+ 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;
+ }
+ }
+#endif
+ }
+
+
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse lookahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ YYABORT;
+ }
+ else
+ {
+ yydestruct ("Error: discarding",
+ yytoken, &yylval);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse lookahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+ /* Pacify compilers like GCC when the user code never invokes
+ YYERROR and the label yyerrorlab therefore never appears in user
+ code. */
+ if (/*CONSTCOND*/ 0)
+ goto yyerrorlab;
+
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYERROR. */
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (yyn != YYPACT_NINF)
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+
+ yydestruct ("Error: popping",
+ yystos[yystate], yyvsp);
+ YYPOPSTACK (1);
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ *++yyvsp = yylval;
+
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#if !defined(yyoverflow) || YYERROR_VERBOSE
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here. |
+`-------------------------------------------------*/
+yyexhaustedlab:
+ yyerror (YY_("memory exhausted"));
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+ if (yychar != YYEMPTY)
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval);
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYABORT or YYACCEPT. */
+ YYPOPSTACK (yylen);
+ YY_STACK_PRINT (yyss, yyssp);
+ while (yyssp != yyss)
+ {
+ yydestruct ("Cleanup: popping",
+ yystos[*yyssp], yyvsp);
+ YYPOPSTACK (1);
+ }
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+#endif
+ /* Make sure YYID is used. */
+ return YYID (yyresult);
+}
+
+
+
diff --git a/src/cmd/5a/y.tab.h b/src/cmd/5a/y.tab.h
new file mode 100644
index 000000000..28039992c
--- /dev/null
+++ b/src/cmd/5a/y.tab.h
@@ -0,0 +1,166 @@
+
+/* A Bison parser, made by GNU Bison 2.4.1. */
+
+/* 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
+ it under the terms of the GNU General Public License as published by
+ 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, 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
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ 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
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ LTYPE1 = 258,
+ LTYPE2 = 259,
+ LTYPE3 = 260,
+ LTYPE4 = 261,
+ LTYPE5 = 262,
+ LTYPE6 = 263,
+ LTYPE7 = 264,
+ LTYPE8 = 265,
+ LTYPE9 = 266,
+ LTYPEA = 267,
+ LTYPEB = 268,
+ 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,
+ LCONST = 282,
+ LSP = 283,
+ LSB = 284,
+ LFP = 285,
+ LPC = 286,
+ LTYPEX = 287,
+ LR = 288,
+ LREG = 289,
+ LF = 290,
+ LFREG = 291,
+ LC = 292,
+ LCREG = 293,
+ LPSR = 294,
+ LFCR = 295,
+ LCOND = 296,
+ LS = 297,
+ LAT = 298,
+ LFCONST = 299,
+ LSCONST = 300,
+ LNAME = 301,
+ LLAB = 302,
+ LVAR = 303
+ };
+#endif
+/* Tokens. */
+#define LTYPE1 258
+#define LTYPE2 259
+#define LTYPE3 260
+#define LTYPE4 261
+#define LTYPE5 262
+#define LTYPE6 263
+#define LTYPE7 264
+#define LTYPE8 265
+#define LTYPE9 266
+#define LTYPEA 267
+#define LTYPEB 268
+#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 LCONST 282
+#define LSP 283
+#define LSB 284
+#define LFP 285
+#define LPC 286
+#define LTYPEX 287
+#define LR 288
+#define LREG 289
+#define LF 290
+#define LFREG 291
+#define LC 292
+#define LCREG 293
+#define LPSR 294
+#define LFCR 295
+#define LCOND 296
+#define LS 297
+#define LAT 298
+#define LFCONST 299
+#define LSCONST 300
+#define LNAME 301
+#define LLAB 302
+#define LVAR 303
+
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+{
+
+/* Line 1676 of yacc.c */
+#line 38 "a.y"
+
+ Sym *sym;
+ int32 lval;
+ double dval;
+ char sval[8];
+ Gen gen;
+
+
+
+/* Line 1676 of yacc.c */
+#line 158 "y.tab.h"
+} YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+extern YYSTYPE yylval;
+
+
diff --git a/src/cmd/5c/Makefile b/src/cmd/5c/Makefile
index 70b614e8a..3f528d751 100644
--- a/src/cmd/5c/Makefile
+++ b/src/cmd/5c/Makefile
@@ -1,34 +1,5 @@
-# Copyright 2009 The Go Authors. All rights reserved.
+# 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 ../../Make.inc
-O:=$(HOST_O)
-
-TARG=5c
-
-HFILES=\
- gc.h\
- ../5l/5.out.h\
- ../cc/cc.h\
-
-OFILES=\
- cgen.$O\
- list.$O\
- sgen.$O\
- swt.$O\
- txt.$O\
- mul.$O\
- reg.$O\
- peep.$O\
- pgen.$O\
- pswt.$O\
- ../5l/enam.$O\
-
-LIB=\
- ../cc/cc.a\
-
-include ../../Make.ccmd
-
-%.$O: ../cc/%.c
- $(HOST_CC) $(HOST_CFLAGS) -c -I. -o $@ ../cc/$*.c
+include ../../Make.dist
diff --git a/src/cmd/5c/gc.h b/src/cmd/5c/gc.h
index ff6d51916..20652682b 100644
--- a/src/cmd/5c/gc.h
+++ b/src/cmd/5c/gc.h
@@ -181,7 +181,7 @@ EXTERN int32 maxargsafe;
EXTERN int mnstring;
EXTERN Multab multab[20];
EXTERN int retok;
-EXTERN int hintabsize;
+extern int hintabsize;
EXTERN Node* nodrat;
EXTERN Node* nodret;
EXTERN Node* nodsafe;
@@ -304,7 +304,8 @@ void gpseudo(int, Sym*, Node*);
int swcmp(const void*, const void*);
void doswit(Node*);
void swit1(C1*, int, int32, Node*);
-void cas(void);
+void swit2(C1*, int, int32, Node*);
+void newcase(void);
void bitload(Node*, Node*, Node*, Node*, Node*);
void bitstore(Node*, Node*, Node*, Node*, Node*);
int mulcon(Node*, Node*);
diff --git a/src/cmd/5c/swt.c b/src/cmd/5c/swt.c
index 7cbaadba9..7268f9af2 100644
--- a/src/cmd/5c/swt.c
+++ b/src/cmd/5c/swt.c
@@ -28,12 +28,31 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-
#include "gc.h"
void
swit1(C1 *q, int nc, int32 def, Node *n)
{
+ Node nreg;
+
+ if(typev[n->type->etype]) {
+ regsalloc(&nreg, n);
+ nreg.type = types[TVLONG];
+ cgen(n, &nreg);
+ swit2(q, nc, def, &nreg);
+ return;
+ }
+
+ regalloc(&nreg, n, Z);
+ nreg.type = types[TLONG];
+ cgen(n, &nreg);
+ swit2(q, nc, def, &nreg);
+ regfree(&nreg);
+}
+
+void
+swit2(C1 *q, int nc, int32 def, Node *n)
+{
C1 *r;
int i;
int32 v;
@@ -65,12 +84,12 @@ swit1(C1 *q, int nc, int32 def, Node *n)
sp = p;
gopcode(OEQ, nodconst(r->val), n, Z); /* just gen the B.EQ */
patch(p, r->label);
- swit1(q, i, def, n);
+ swit2(q, i, def, n);
if(debug['W'])
print("case < %.8ux\n", r->val);
patch(sp, pc);
- swit1(r+1, nc-i-1, def, n);
+ swit2(r+1, nc-i-1, def, n);
return;
direct:
@@ -460,17 +479,13 @@ outhist(Biobuf *b)
for(h = hist; h != H; h = h->link) {
p = h->name;
op = 0;
- /* on windows skip drive specifier in pathname */
if(systemtype(Windows) && p && p[1] == ':'){
- p += 2;
- c = *p;
- }
- if(p && p[0] != c && h->offset == 0 && pathname){
- /* on windows skip drive specifier in pathname */
+ c = p[2];
+ } else if(p && p[0] != c && h->offset == 0 && pathname){
if(systemtype(Windows) && pathname[1] == ':') {
op = p;
- p = pathname+2;
- c = *p;
+ p = pathname;
+ c = p[2];
} else if(pathname[0] == c){
op = p;
p = pathname;
diff --git a/src/cmd/5c/txt.c b/src/cmd/5c/txt.c
index a32387bc1..1a189e3af 100644
--- a/src/cmd/5c/txt.c
+++ b/src/cmd/5c/txt.c
@@ -139,7 +139,9 @@ 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;
@@ -357,7 +359,7 @@ regfree(Node *n)
if(n->op != OREGISTER && n->op != OINDREG)
goto err;
i = n->reg;
- if(i < 0 || i >= sizeof(reg))
+ if(i < 0 || i >= nelem(reg))
goto err;
if(reg[i] <= 0)
goto err;
@@ -1183,7 +1185,8 @@ gpseudo(int a, Sym *s, Node *n)
if(a == ATEXT) {
p->reg = textflag;
textflag = 0;
- }
+ } else if(a == AGLOBL)
+ p->reg = 0;
if(s->class == CSTATIC)
p->from.name = D_STATIC;
naddr(n, &p->to);
diff --git a/src/cmd/5g/Makefile b/src/cmd/5g/Makefile
index b47014a4e..3f528d751 100644
--- a/src/cmd/5g/Makefile
+++ b/src/cmd/5g/Makefile
@@ -1,36 +1,5 @@
-# Copyright 2009 The Go Authors. All rights reserved.
+# 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 ../../Make.inc
-O:=$(HOST_O)
-
-TARG=5g
-
-HFILES=\
- ../gc/go.h\
- ../5l/5.out.h\
- gg.h\
- opt.h\
-
-OFILES=\
- ../5l/enam.$O\
- cgen.$O\
- cgen64.$O\
- cplx.$O\
- galign.$O\
- ggen.$O\
- gobj.$O\
- gsubr.$O\
- list.$O\
- peep.$O\
- pgen.$O\
- reg.$O\
-
-LIB=\
- ../gc/gc.a\
-
-include ../../Make.ccmd
-
-%.$O: ../gc/%.c
- $(HOST_CC) $(HOST_CFLAGS) -c -I. -o $@ ../gc/$*.c
+include ../../Make.dist
diff --git a/src/cmd/5g/cgen.c b/src/cmd/5g/cgen.c
index 6e2fbe20f..cccef94c9 100644
--- a/src/cmd/5g/cgen.c
+++ b/src/cmd/5g/cgen.c
@@ -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 <u.h>
+#include <libc.h>
#include "gg.h"
/*
@@ -62,6 +64,9 @@ cgen(Node *n, Node *res)
if(isslice(n->left->type))
n->addable = n->left->addable;
break;
+ case OITAB:
+ n->addable = n->left->addable;
+ break;
}
// if both are addressable, move
@@ -211,11 +216,11 @@ cgen(Node *n, Node *res)
goto ret;
case OMINUS:
+ regalloc(&n1, nl->type, N);
+ cgen(nl, &n1);
nodconst(&n3, nl->type, 0);
regalloc(&n2, nl->type, res);
- regalloc(&n1, nl->type, N);
gmove(&n3, &n2);
- cgen(nl, &n1);
gins(optoas(OSUB, nl->type), &n1, &n2);
gmove(&n2, res);
regfree(&n1);
@@ -278,6 +283,20 @@ cgen(Node *n, Node *res)
regfree(&n1);
break;
+ case OITAB:
+ // itable of interface value
+ igen(nl, &n1, res);
+ n1.op = OREGISTER; // was OINDREG
+ regalloc(&n2, n->type, &n1);
+ n1.op = OINDREG;
+ n1.type = n->type;
+ n1.xoffset = 0;
+ gmove(&n1, &n2);
+ gmove(&n2, res);
+ regfree(&n1);
+ regfree(&n2);
+ break;
+
case OLEN:
if(istype(nl->type, TMAP) || istype(nl->type, TCHAN)) {
// map has len in the first 32-bit word.
@@ -400,9 +419,9 @@ abop: // asymmetric binary
regalloc(&n2, nr->type, N);
cgen(nr, &n2);
} else {
- regalloc(&n2, nr->type, N);
+ regalloc(&n2, nr->type, res);
cgen(nr, &n2);
- regalloc(&n1, nl->type, res);
+ regalloc(&n1, nl->type, N);
cgen(nl, &n1);
}
gins(a, &n2, &n1);
@@ -848,8 +867,10 @@ bgen(Node *n, int true, Prog *to)
int et, a;
Node *nl, *nr, *r;
Node n1, n2, n3, n4, tmp;
+ NodeList *ll;
Prog *p1, *p2;
+ USED(n4); // in unreachable code below
if(debug['g']) {
dump("\nbgen", n);
}
@@ -860,9 +881,6 @@ bgen(Node *n, int true, Prog *to)
if(n->ninit != nil)
genlist(n->ninit);
- nl = n->left;
- nr = n->right;
-
if(n->type == T) {
convlit(&n, types[TBOOL]);
if(n->type == T)
@@ -875,7 +893,6 @@ bgen(Node *n, int true, Prog *to)
patch(gins(AEND, N, N), to);
goto ret;
}
- nl = N;
nr = N;
switch(n->op) {
@@ -951,7 +968,10 @@ bgen(Node *n, int true, Prog *to)
p1 = gbranch(AB, T);
p2 = gbranch(AB, T);
patch(p1, pc);
+ ll = n->ninit;
+ n->ninit = nil;
bgen(n, 1, p2);
+ n->ninit = ll;
patch(gbranch(AB, T), to);
patch(p2, pc);
goto ret;
@@ -984,6 +1004,7 @@ bgen(Node *n, int true, Prog *to)
regfree(&n1);
break;
+#ifdef NOTDEF
a = optoas(a, types[tptr]);
regalloc(&n1, types[tptr], N);
regalloc(&n3, types[tptr], N);
@@ -1001,6 +1022,7 @@ bgen(Node *n, int true, Prog *to)
regfree(&n3);
regfree(&n1);
break;
+#endif
}
if(isinter(nl->type)) {
@@ -1019,6 +1041,7 @@ bgen(Node *n, int true, Prog *to)
regfree(&n1);
break;
+#ifdef NOTDEF
a = optoas(a, types[tptr]);
regalloc(&n1, types[tptr], N);
regalloc(&n3, types[tptr], N);
@@ -1036,6 +1059,7 @@ bgen(Node *n, int true, Prog *to)
regfree(&n3);
regfree(&n4);
break;
+#endif
}
if(iscomplex[nl->type->etype]) {
@@ -1059,7 +1083,7 @@ bgen(Node *n, int true, Prog *to)
}
if(nr->op == OLITERAL) {
- if(nr->val.ctype == CTINT && mpgetfix(nr->val.u.xval) == 0) {
+ if(isconst(nr, CTINT) && mpgetfix(nr->val.u.xval) == 0) {
gencmp0(nl, nl->type, a, to);
break;
}
@@ -1186,7 +1210,7 @@ stkof(Node *n)
* NB: character copy assumed little endian architecture
*/
void
-sgen(Node *n, Node *res, int32 w)
+sgen(Node *n, Node *res, int64 w)
{
Node dst, src, tmp, nend;
int32 c, odst, osrc;
@@ -1194,25 +1218,34 @@ sgen(Node *n, Node *res, int32 w)
Prog *p, *ploop;
if(debug['g']) {
- print("\nsgen w=%d\n", w);
+ print("\nsgen w=%lld\n", w);
dump("r", n);
dump("res", res);
}
- if(w == 0)
- return;
- if(w < 0)
- fatal("sgen copy %d", w);
+
if(n->ullman >= UINF && res->ullman >= UINF)
fatal("sgen UINF");
+
+ if(w < 0 || (int32)w != w)
+ fatal("sgen copy %lld", w);
+
if(n->type == T)
fatal("sgen: missing type");
+ if(w == 0) {
+ // evaluate side effects only.
+ regalloc(&dst, types[tptr], N);
+ agen(res, &dst);
+ agen(n, &dst);
+ regfree(&dst);
+ return;
+ }
+
// determine alignment.
// want to avoid unaligned access, so have to use
// smaller operations for less aligned types.
// for example moving [4]byte must use 4 MOVB not 1 MOVW.
align = n->type->align;
- op = 0;
switch(align) {
default:
fatal("sgen: invalid alignment %d for %T", align, n->type);
@@ -1227,7 +1260,7 @@ sgen(Node *n, Node *res, int32 w)
break;
}
if(w%align)
- fatal("sgen: unaligned size %d (align=%d) for %T", w, align, n->type);
+ fatal("sgen: unaligned size %lld (align=%d) for %T", w, align, n->type);
c = w / align;
// offset on the stack
@@ -1313,7 +1346,6 @@ sgen(Node *n, Node *res, int32 w)
p->from.type = D_OREG;
p->from.offset = dir;
p->scond |= C_PBIT;
- ploop = p;
p = gins(op, &tmp, &dst);
p->to.type = D_OREG;
diff --git a/src/cmd/5g/cgen64.c b/src/cmd/5g/cgen64.c
index b56df765b..1235d1ace 100644
--- a/src/cmd/5g/cgen64.c
+++ b/src/cmd/5g/cgen64.c
@@ -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 <u.h>
+#include <libc.h>
#include "gg.h"
/*
@@ -240,7 +242,7 @@ cgen64(Node *n, Node *res)
// shift is >= 1<<32
split64(r, &cl, &ch);
gmove(&ch, &s);
- p1 = gins(ATST, &s, N);
+ gins(ATST, &s, N);
p6 = gbranch(ABNE, T);
gmove(&cl, &s);
splitclean();
@@ -248,7 +250,7 @@ cgen64(Node *n, Node *res)
gmove(r, &s);
p6 = P;
}
- p1 = gins(ATST, &s, N);
+ gins(ATST, &s, N);
// shift == 0
p1 = gins(AMOVW, &bl, &al);
@@ -411,7 +413,7 @@ olsh_break:
gmove(r, &s);
p6 = P;
}
- p1 = gins(ATST, &s, N);
+ gins(ATST, &s, N);
// shift == 0
p1 = gins(AMOVW, &bl, &al);
@@ -453,9 +455,9 @@ olsh_break:
p1 = gins(AMOVW, &bh, &al);
p1->scond = C_SCOND_EQ;
if(bh.type->etype == TINT32)
- p1 = gshift(AMOVW, &bh, SHIFT_AR, 31, &ah);
+ gshift(AMOVW, &bh, SHIFT_AR, 31, &ah);
else
- p1 = gins(AEOR, &ah, &ah);
+ gins(AEOR, &ah, &ah);
p4 = gbranch(ABEQ, T);
// check if shift is < 64
diff --git a/src/cmd/5g/doc.go b/src/cmd/5g/doc.go
index e86013bdd..5a4a772fb 100644
--- a/src/cmd/5g/doc.go
+++ b/src/cmd/5g/doc.go
@@ -9,7 +9,5 @@ The $GOARCH for these tools is arm.
It reads .go files and outputs .5 files. The flags are documented in ../gc/doc.go.
-There is no instruction optimizer, so the -N flag is a no-op.
-
*/
package documentation
diff --git a/src/cmd/5g/galign.c b/src/cmd/5g/galign.c
index 12766102f..070804217 100644
--- a/src/cmd/5g/galign.c
+++ b/src/cmd/5g/galign.c
@@ -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 <u.h>
+#include <libc.h>
#include "gg.h"
int thechar = '5';
diff --git a/src/cmd/5g/gg.h b/src/cmd/5g/gg.h
index ce4558e21..7dbf3beec 100644
--- a/src/cmd/5g/gg.h
+++ b/src/cmd/5g/gg.h
@@ -2,16 +2,13 @@
// 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>
+#ifndef EXTERN
+#define EXTERN extern
+#endif
#include "../gc/go.h"
#include "../5l/5.out.h"
-#ifndef EXTERN
-#define EXTERN extern
-#endif
-
typedef struct Addr Addr;
struct Addr
@@ -46,27 +43,26 @@ struct Prog
uchar scond;
};
+#define TEXTFLAG reg
+
#define REGALLOC_R0 0
#define REGALLOC_RMAX REGEXT
#define REGALLOC_F0 (REGALLOC_RMAX+1)
#define REGALLOC_FMAX (REGALLOC_F0 + FREGEXT)
-EXTERN Biobuf* bout;
EXTERN int32 dynloc;
EXTERN uchar reg[REGALLOC_FMAX+1];
EXTERN int32 pcloc; // instruction counter
EXTERN Strlit emptystring;
extern char* anames[];
-EXTERN Hist* hist;
EXTERN Prog zprog;
-EXTERN Node* curfn;
EXTERN Node* newproc;
EXTERN Node* deferproc;
EXTERN Node* deferreturn;
EXTERN Node* panicindex;
EXTERN Node* panicslice;
EXTERN Node* throwreturn;
-EXTERN long unmappedzero;
+extern long unmappedzero;
EXTERN int maxstksize;
/*
@@ -98,7 +94,7 @@ void igen(Node*, Node*, Node*);
void agenr(Node *n, Node *a, Node *res);
vlong fieldoffset(Type*, Node*);
void bgen(Node*, int, Prog*);
-void sgen(Node*, Node*, int32);
+void sgen(Node*, Node*, int64);
void gmove(Node*, Node*);
Prog* gins(int, Node*, Node*);
int samaddr(Node*, Node*);
@@ -169,3 +165,6 @@ int Yconv(Fmt*);
void listinit(void);
void zaddr(Biobuf*, Addr*, int);
+
+#pragma varargck type "D" Addr*
+#pragma varargck type "M" Addr*
diff --git a/src/cmd/5g/ggen.c b/src/cmd/5g/ggen.c
index 3f5f47e7b..de100620b 100644
--- a/src/cmd/5g/ggen.c
+++ b/src/cmd/5g/ggen.c
@@ -4,6 +4,8 @@
#undef EXTERN
#define EXTERN
+#include <u.h>
+#include <libc.h>
#include "gg.h"
#include "opt.h"
@@ -12,7 +14,6 @@ defframe(Prog *ptxt)
{
// fill in argument size
ptxt->to.type = D_CONST2;
- ptxt->reg = 0; // flags
ptxt->to.offset2 = rnd(curfn->type->argwid, widthptr);
// fill in final stack size
@@ -28,10 +29,10 @@ markautoused(Prog* p)
{
for (; p; p = p->link) {
if (p->from.name == D_AUTO && p->from.node)
- p->from.node->used++;
+ p->from.node->used = 1;
if (p->to.name == D_AUTO && p->to.node)
- p->to.node->used++;
+ p->to.node->used = 1;
}
}
@@ -544,7 +545,7 @@ cgen_shift(int op, Node *nl, Node *nr, Node *res)
}
// test for shift being 0
- p1 = gins(ATST, &n1, N);
+ gins(ATST, &n1, N);
p3 = gbranch(ABEQ, T);
// test and fix up large shifts
diff --git a/src/cmd/5g/gobj.c b/src/cmd/5g/gobj.c
index 27c8be67d..b562ba888 100644
--- a/src/cmd/5g/gobj.c
+++ b/src/cmd/5g/gobj.c
@@ -28,6 +28,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#include <u.h>
+#include <libc.h>
#include "gg.h"
void
@@ -266,54 +268,6 @@ dumpfuncs(void)
}
}
-/* deferred DATA output */
-static Prog *strdat;
-static Prog *estrdat;
-static int gflag;
-static Prog *savepc;
-
-void
-data(void)
-{
- gflag = debug['g'];
- debug['g'] = 0;
-
- if(estrdat == nil) {
- strdat = mal(sizeof(*pc));
- clearp(strdat);
- estrdat = strdat;
- }
- if(savepc)
- fatal("data phase error");
- savepc = pc;
- pc = estrdat;
-}
-
-void
-text(void)
-{
- if(!savepc)
- fatal("text phase error");
- debug['g'] = gflag;
- estrdat = pc;
- pc = savepc;
- savepc = nil;
-}
-
-void
-dumpdata(void)
-{
- Prog *p;
-
- if(estrdat == nil)
- return;
- *pc = *strdat;
- if(gflag)
- for(p=pc; p!=estrdat; p=p->link)
- print("%P\n", p);
- pc = estrdat;
-}
-
int
dsname(Sym *sym, int off, char *t, int n)
{
@@ -353,6 +307,7 @@ datastring(char *s, int len, Addr *a)
a->offset = widthptr+4; // skip header
a->reg = NREG;
a->sym = sym;
+ a->node = sym->def;
}
/*
@@ -371,6 +326,7 @@ datagostring(Strlit *sval, Addr *a)
a->offset = 0; // header
a->reg = NREG;
a->sym = sym;
+ a->node = sym->def;
}
void
@@ -379,6 +335,17 @@ gdata(Node *nam, Node *nr, int wid)
Prog *p;
vlong v;
+ if(nr->op == OLITERAL) {
+ switch(nr->val.ctype) {
+ case CTCPLX:
+ gdatacomplex(nam, nr->val.u.cval);
+ return;
+ case CTSTR:
+ gdatastring(nam, nr->val.u.sval);
+ return;
+ }
+ }
+
if(wid == 8 && is64(nr->type)) {
v = mpgetfix(nr->val.u.xval);
p = gins(ADATA, nam, nodintconst(v));
diff --git a/src/cmd/5g/gsubr.c b/src/cmd/5g/gsubr.c
index ddaf52a88..9acf93670 100644
--- a/src/cmd/5g/gsubr.c
+++ b/src/cmd/5g/gsubr.c
@@ -28,6 +28,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#include <u.h>
+#include <libc.h>
#include "gg.h"
// TODO(kaib): Can make this bigger if we move
@@ -50,6 +52,10 @@ clearp(Prog *p)
pcloc++;
}
+static int ddumped;
+static Prog *dfirst;
+static Prog *dpc;
+
/*
* generate and return proc with p->as = as,
* linked into program. pc is next instruction.
@@ -59,10 +65,23 @@ prog(int as)
{
Prog *p;
- p = pc;
- pc = mal(sizeof(*pc));
-
- clearp(pc);
+ if(as == ADATA || as == AGLOBL) {
+ if(ddumped)
+ fatal("already dumped data");
+ if(dpc == nil) {
+ dpc = mal(sizeof(*dpc));
+ dfirst = dpc;
+ }
+ p = dpc;
+ dpc = mal(sizeof(*dpc));
+ p->link = dpc;
+ p->reg = 0; // used for flags
+ } else {
+ p = pc;
+ pc = mal(sizeof(*pc));
+ clearp(pc);
+ p->link = pc;
+ }
if(lineno == 0) {
if(debug['K'])
@@ -71,10 +90,21 @@ prog(int as)
p->as = as;
p->lineno = lineno;
- p->link = pc;
return p;
}
+void
+dumpdata(void)
+{
+ ddumped = 1;
+ if(dfirst == nil)
+ return;
+ newplist();
+ *pc = *dfirst;
+ pc = dpc;
+ clearp(pc);
+}
+
/*
* generate a branch.
* t is ignored.
@@ -84,6 +114,8 @@ gbranch(int as, Type *t)
{
Prog *p;
+ USED(t);
+
p = prog(as);
p->to.type = D_BRANCH;
p->to.branch = P;
@@ -222,6 +254,10 @@ ggloblnod(Node *nam, int32 width)
p->to.sym = S;
p->to.type = D_CONST;
p->to.offset = width;
+ if(nam->readonly)
+ p->reg = RODATA;
+ if(nam->type != T && !haspointers(nam->type))
+ p->reg |= NOPTR;
}
void
@@ -238,6 +274,7 @@ ggloblsym(Sym *s, int32 width, int dupok)
p->to.offset = width;
if(dupok)
p->reg = DUPOK;
+ p->reg |= RODATA;
}
int
@@ -315,6 +352,8 @@ anyregalloc(void)
return 0;
}
+uintptr regpc[REGALLOC_FMAX+1];
+
/*
* allocate register of type t, leave in n.
* if o != N, o is desired fixed register.
@@ -325,7 +364,7 @@ regalloc(Node *n, Type *t, Node *o)
{
int i, et, fixfree, floatfree;
- if(debug['r']) {
+ if(0 && debug['r']) {
fixfree = 0;
for(i=REGALLOC_R0; i<=REGALLOC_RMAX; i++)
if(reg[i] == 0)
@@ -358,9 +397,12 @@ regalloc(Node *n, Type *t, Node *o)
goto out;
}
for(i=REGALLOC_R0; i<=REGALLOC_RMAX; i++)
- if(reg[i] == 0)
+ if(reg[i] == 0) {
+ regpc[i] = (uintptr)getcallerpc(&n);
goto out;
-
+ }
+ for(i=REGALLOC_R0; i<=REGALLOC_RMAX; i++)
+ print("%d %p\n", i, regpc[i]);
yyerror("out of fixed registers");
goto err;
@@ -398,7 +440,7 @@ regfree(Node *n)
{
int i, fixfree, floatfree;
- if(debug['r']) {
+ if(0 && debug['r']) {
fixfree = 0;
for(i=REGALLOC_R0; i<=REGALLOC_RMAX; i++)
if(reg[i] == 0)
@@ -415,11 +457,13 @@ regfree(Node *n)
if(n->op != OREGISTER && n->op != OINDREG)
fatal("regfree: not a register");
i = n->val.u.reg;
- if(i < 0 || i >= sizeof(reg))
+ if(i < 0 || i >= nelem(reg) || i >= nelem(regpc))
fatal("regfree: reg out of range");
if(reg[i] <= 0)
fatal("regfree: reg not allocated");
reg[i]--;
+ if(reg[i] == 0)
+ regpc[i] = 0;
}
/*
@@ -481,8 +525,15 @@ nodarg(Type *t, int fp)
fatal("nodarg: offset not computed for %T", t);
n->xoffset = t->width;
n->addable = 1;
+ n->orig = t->nname;
fp:
+ // Rewrite argument named _ to __,
+ // or else the assignment to _ will be
+ // discarded during code generation.
+ if(isblank(n))
+ n->sym = lookup("__");
+
switch(fp) {
default:
fatal("nodarg %T %d", t, fp);
@@ -1066,7 +1117,8 @@ gins(int as, Node *f, Node *t)
if(f != N)
naddr(f, &af, 1);
if(t != N)
- naddr(t, &at, 1); p = prog(as);
+ naddr(t, &at, 1);
+ p = prog(as);
if(f != N)
p->from = af;
if(t != N)
@@ -1232,6 +1284,7 @@ naddr(Node *n, Addr *a, int canemitcode)
a->sym = n->left->sym;
a->type = D_OREG;
a->name = D_PARAM;
+ a->node = n->left->orig;
break;
case ONAME:
@@ -1242,9 +1295,11 @@ naddr(Node *n, Addr *a, int canemitcode)
a->etype = simtype[n->type->etype];
a->width = n->type->width;
}
- a->pun = n->pun;
a->offset = n->xoffset;
a->sym = n->sym;
+ a->node = n->orig;
+ //if(a->node >= (Node*)&n)
+ // fatal("stack node");
if(a->sym == S)
a->sym = lookup(".noname");
if(n->method) {
@@ -1263,8 +1318,6 @@ naddr(Node *n, Addr *a, int canemitcode)
break;
case PAUTO:
a->name = D_AUTO;
- if (n->sym)
- a->node = n->orig;
break;
case PPARAM:
case PPARAMOUT:
@@ -1287,6 +1340,7 @@ naddr(Node *n, Addr *a, int canemitcode)
a->dval = mpgetflt(n->val.u.fval);
break;
case CTINT:
+ case CTRUNE:
a->sym = S;
a->type = D_CONST;
a->offset = mpgetfix(n->val.u.xval);
@@ -1307,6 +1361,16 @@ naddr(Node *n, Addr *a, int canemitcode)
}
break;
+ case OITAB:
+ // itable of interface value
+ naddr(n->left, a, 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 OLEN:
// len of string or slice
naddr(n->left, a, canemitcode);
@@ -1744,7 +1808,7 @@ sudoaddable(int as, Node *n, Addr *a, int *w)
switch(n->op) {
case OLITERAL:
- if(n->val.ctype != CTINT)
+ if(!isconst(n, CTINT))
break;
v = mpgetfix(n->val.u.xval);
if(v >= 32000 || v <= -32000)
diff --git a/src/cmd/5g/list.c b/src/cmd/5g/list.c
index 0c6dbbf71..9bc3a9a9a 100644
--- a/src/cmd/5g/list.c
+++ b/src/cmd/5g/list.c
@@ -28,6 +28,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#include <u.h>
+#include <libc.h>
#include "gg.h"
// TODO(kaib): make 5g/list.c congruent with 5l/list.c
@@ -57,7 +59,7 @@ Pconv(Fmt *fp)
switch(p->as) {
default:
snprint(str1, sizeof(str1), "%A%C", p->as, p->scond);
- if(p->reg == NREG)
+ if(p->reg == NREG && p->as != AGLOBL)
snprint(str, sizeof(str), "%.4d (%L) %-7s %D,%D",
p->loc, p->lineno, str1, &p->from, &p->to);
else
diff --git a/src/cmd/5g/peep.c b/src/cmd/5g/peep.c
index 6cc93db12..e87f5d697 100644
--- a/src/cmd/5g/peep.c
+++ b/src/cmd/5g/peep.c
@@ -29,6 +29,8 @@
// THE SOFTWARE.
+#include <u.h>
+#include <libc.h>
#include "gg.h"
#include "opt.h"
@@ -45,6 +47,9 @@ peep(void)
Reg *r, *r1, *r2;
Prog *p, *p1;
int t;
+
+ p1 = nil;
+ USED(p1); // ... in unreachable code...
/*
* complete R structure
*/
@@ -115,12 +120,14 @@ loop1:
}
break;
+#ifdef NOTDEF
if(p->scond == C_SCOND_NONE)
if(regtyp(&p->to))
if(isdconst(&p->from)) {
constprop(&p->from, &p->to, r->s1);
}
break;
+#endif
}
}
if(t)
@@ -128,6 +135,7 @@ loop1:
return;
+#ifdef NOTDEF
for(r=firstr; r!=R; r=r->link) {
p = r->prog;
switch(p->as) {
@@ -255,6 +263,7 @@ return;
// }
predicate();
+#endif
}
Reg*
@@ -1159,7 +1168,6 @@ copyu(Prog *p, Adr *v, Adr *s)
return 3;
return 0;
}
- return 0;
}
/*
diff --git a/src/cmd/5g/reg.c b/src/cmd/5g/reg.c
index 2d2a6d01a..93724d032 100644
--- a/src/cmd/5g/reg.c
+++ b/src/cmd/5g/reg.c
@@ -29,6 +29,8 @@
// THE SOFTWARE.
+#include <u.h>
+#include <libc.h>
#include "gg.h"
#include "opt.h"
@@ -40,6 +42,9 @@
int noreturn(Prog *p);
static int first = 0;
+static void fixjmp(Prog*);
+
+
Reg*
rega(void)
{
@@ -90,8 +95,8 @@ setoutvar(void)
ovar.b[z] |= bit.b[z];
t = structnext(&save);
}
-//if(bany(b))
-//print("ovars = %Q\n", &ovar);
+//if(bany(ovar))
+//print("ovar = %Q\n", ovar);
}
void
@@ -112,19 +117,19 @@ setaddrs(Bits bit)
{
int i, n;
Var *v;
- Sym *s;
+ Node *node;
while(bany(&bit)) {
// convert each bit to a variable
i = bnum(bit);
- s = var[i].sym;
+ node = var[i].node;
n = var[i].name;
bit.b[i/32] &= ~(1L<<(i%32));
// disable all pieces of that variable
for(i=0; i<nvar; i++) {
v = var+i;
- if(v->sym == s && v->name == n)
+ if(v->node == node && v->name == n)
v->addr = 2;
}
}
@@ -169,6 +174,8 @@ regopt(Prog *firstp)
if(first == 0) {
fmtinstall('Q', Qconv);
}
+
+ fixjmp(firstp);
first++;
if(debug['K']) {
@@ -202,7 +209,7 @@ regopt(Prog *firstp)
nvar = NREGVAR;
memset(var, 0, NREGVAR*sizeof var[0]);
for(i=0; i<NREGVAR; i++)
- var[i].sym = lookup(regname[i]);
+ var[i].node = newname(lookup(regname[i]));
regbits = RtoB(REGSP)|RtoB(REGLINK)|RtoB(REGPC);
for(z=0; z<BITS; z++) {
@@ -750,9 +757,9 @@ addmove(Reg *r, int bn, int rn, int f)
v = var + bn;
a = &p1->to;
- a->sym = v->sym;
a->name = v->name;
a->node = v->node;
+ a->sym = v->node->sym;
a->offset = v->offset;
a->etype = v->etype;
a->type = D_OREG;
@@ -838,11 +845,10 @@ mkvar(Reg *r, Adr *a)
int i, t, n, et, z, w, flag;
int32 o;
Bits bit;
- Sym *s;
+ Node *node;
// mark registers used
t = a->type;
- n = D_NONE;
flag = 0;
switch(t) {
@@ -909,10 +915,13 @@ mkvar(Reg *r, Adr *a)
break;
}
- s = a->sym;
- if(s == S)
+ node = a->node;
+ if(node == N || node->op != ONAME || node->orig == N)
goto none;
- if(s->name[0] == '.')
+ node = node->orig;
+ if(node->orig != node)
+ fatal("%D: bad node", a);
+ if(node->sym == S || node->sym->name[0] == '.')
goto none;
et = a->etype;
o = a->offset;
@@ -920,7 +929,7 @@ mkvar(Reg *r, Adr *a)
for(i=0; i<nvar; i++) {
v = var+i;
- if(v->sym == s && v->name == n) {
+ if(v->node == node && v->name == n) {
if(v->offset == o)
if(v->etype == et)
if(v->width == w)
@@ -944,7 +953,7 @@ mkvar(Reg *r, Adr *a)
}
if(nvar >= NVAR) {
- if(debug['w'] > 1 && s)
+ if(debug['w'] > 1 && node)
fatal("variable not optimized: %D", a);
goto none;
}
@@ -953,17 +962,16 @@ mkvar(Reg *r, Adr *a)
nvar++;
//print("var %d %E %D %S\n", i, et, a, s);
v = var+i;
- v->sym = s;
v->offset = o;
v->name = n;
// v->gotype = a->gotype;
v->etype = et;
v->width = w;
v->addr = flag; // funny punning
- v->node = a->node;
+ v->node = node;
if(debug['R'])
- print("bit=%2d et=%2d w=%d+%d %S %D flag=%d\n", i, et, o, w, s, a, v->addr);
+ print("bit=%2d et=%2d w=%d+%d %#N %D flag=%d\n", i, et, o, w, node, a, v->addr);
bit = blsh(i);
if(n == D_EXTERN || n == D_STATIC)
@@ -1021,6 +1029,13 @@ prop(Reg *r, Bits ref, Bits cal)
ref.b[z] = 0;
}
break;
+
+ default:
+ // Work around for issue 1304:
+ // flush modified globals before each instruction.
+ for(z=0; z<BITS; z++)
+ cal.b[z] |= externs.b[z];
+ break;
}
for(z=0; z<BITS; z++) {
ref.b[z] = (ref.b[z] & ~r1->set.b[z]) |
@@ -1156,10 +1171,12 @@ loopit(Reg *r, int32 nr)
r1 = rpo2r[i];
me = r1->rpo;
d = -1;
- if(r1->p1 != R && r1->p1->rpo < me)
+ // 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(r1->rpo < me)
+ if(rpo2r[r1->rpo] == r1 && r1->rpo < me)
d = rpolca(idom, d, r1->rpo);
idom[i] = d;
}
@@ -1570,7 +1587,7 @@ dumpone(Reg *r)
if(bany(&r->refahead))
print(" ra:%Q ", r->refahead);
if(bany(&r->calbehind))
- print("cb:%Q ", r->calbehind);
+ print(" cb:%Q ", r->calbehind);
if(bany(&r->calahead))
print(" ca:%Q ", r->calahead);
if(bany(&r->regdiff))
@@ -1605,3 +1622,123 @@ 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.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.branch)
+ mark(p->to.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.branch && p->to.branch->as == AB) {
+ p->to.branch = chasejmp(p->to.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.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 cf86ae48b..569536ebd 100644
--- a/src/cmd/5l/5.out.h
+++ b/src/cmd/5l/5.out.h
@@ -35,7 +35,8 @@
#define NOPROF (1<<0)
#define DUPOK (1<<1)
#define NOSPLIT (1<<2)
-#define ALLTHUMBS (1<<3)
+#define RODATA (1<<3)
+#define NOPTR (1<<4)
#define REGRET 0
/* -1 disables use of REGARG */
diff --git a/src/cmd/5l/Makefile b/src/cmd/5l/Makefile
index 8489abc64..3f528d751 100644
--- a/src/cmd/5l/Makefile
+++ b/src/cmd/5l/Makefile
@@ -1,43 +1,5 @@
-# Copyright 2009 The Go Authors. All rights reserved.
+# 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 ../../Make.inc
-O:=$(HOST_O)
-
-TARG=5l
-
-OFILES=\
- asm.$O\
- data.$O\
- elf.$O\
- enam.$O\
- ldelf.$O\
- ldmacho.$O\
- ldpe.$O\
- lib.$O\
- list.$O\
- noop.$O\
- obj.$O\
- optab.$O\
- pass.$O\
- prof.$O\
- softfloat.$O\
- span.$O\
- symtab.$O\
- go.$O\
-
-HFILES=\
- l.h\
- 5.out.h\
- ../ld/elf.h\
-
-include ../../Make.ccmd
-
-enam.c: 5.out.h
- sh mkenam
-
-CLEANFILES+=enam.c
-
-%.$O: ../ld/%.c
- $(HOST_CC) $(HOST_CFLAGS) -c -I. ../ld/$*.c
+include ../../Make.dist
diff --git a/src/cmd/5l/asm.c b/src/cmd/5l/asm.c
index 5b7f6f111..b36a982d1 100644
--- a/src/cmd/5l/asm.c
+++ b/src/cmd/5l/asm.c
@@ -73,6 +73,9 @@ enum {
ElfStrShstrtab,
ElfStrRelPlt,
ElfStrPlt,
+ ElfStrNoteNetbsdIdent,
+ ElfStrNoPtrData,
+ ElfStrNoPtrBss,
NElfStr
};
@@ -90,6 +93,7 @@ needlib(char *name)
/* reuse hash code in symbol table */
p = smprint(".dynlib.%s", name);
s = lookup(p, 0);
+ free(p);
if(s->type == 0) {
s->type = 100; // avoid SDATA, etc.
return 1;
@@ -162,8 +166,12 @@ doelf(void)
elfstr[ElfStrEmpty] = addstring(shstrtab, "");
elfstr[ElfStrText] = addstring(shstrtab, ".text");
+ elfstr[ElfStrNoPtrData] = addstring(shstrtab, ".noptrdata");
elfstr[ElfStrData] = addstring(shstrtab, ".data");
elfstr[ElfStrBss] = addstring(shstrtab, ".bss");
+ elfstr[ElfStrNoPtrBss] = addstring(shstrtab, ".noptrbss");
+ if(HEADTYPE == Hnetbsd)
+ elfstr[ElfStrNoteNetbsdIdent] = addstring(shstrtab, ".note.netbsd.ident");
addstring(shstrtab, ".rodata");
addstring(shstrtab, ".gosymtab");
addstring(shstrtab, ".gopclntab");
@@ -232,7 +240,7 @@ doelf(void)
/* define dynamic elf table */
s = lookup(".dynamic", 0);
s->reachable = 1;
- s->type = SELFROSECT;
+ s->type = SELFSECT; // writable
/*
* .dynamic table
@@ -251,6 +259,7 @@ doelf(void)
elfwritedynent(s, DT_PLTREL, DT_REL);
elfwritedynentsymsize(s, DT_PLTRELSZ, lookup(".rel.plt", 0));
elfwritedynentsym(s, DT_JMPREL, lookup(".rel.plt", 0));
+ elfwritedynent(s, DT_DEBUG, 0);
elfwritedynent(s, DT_NULL, 0);
}
}
@@ -293,7 +302,7 @@ asmb(void)
{
int32 t;
int a, dynsym;
- uint32 fo, symo, startva;
+ uint32 fo, symo, startva, resoff;
ElfEhdr *eh;
ElfPhdr *ph, *pph;
ElfShdr *sh;
@@ -321,11 +330,6 @@ asmb(void)
cseek(segdata.fileoff);
datblk(segdata.vaddr, segdata.filelen);
- /* output read-only data in text segment */
- sect = segtext.sect->next;
- cseek(sect->vaddr - segtext.vaddr + segtext.fileoff);
- datblk(sect->vaddr, sect->len);
-
if(iself) {
/* index of elf text section; needed by asmelfsym, double-checked below */
/* !debug['d'] causes extra sections before the .text section */
@@ -335,6 +339,8 @@ asmb(void)
if(elfverneed)
elftextsh += 2;
}
+ if(HEADTYPE == Hnetbsd)
+ elftextsh += 1;
}
/* output symbol table */
@@ -370,7 +376,7 @@ asmb(void)
cseek(symo);
if(iself) {
if(debug['v'])
- Bprint(&bso, "%5.2f elfsym\n", cputime());
+ Bprint(&bso, "%5.2f elfsym\n", cputime());
asmelfsym();
cflush();
cwrite(elfstrdat, elfstrsize);
@@ -454,6 +460,7 @@ asmb(void)
eh = getElfEhdr();
fo = HEADR;
startva = INITTEXT - fo; /* va of byte 0 of file */
+ resoff = ELFRESERVE;
/* This null SHdr must appear before all others */
newElfShdr(elfstr[ElfStrEmpty]);
@@ -486,7 +493,7 @@ asmb(void)
sh->addralign = 1;
if(interpreter == nil)
interpreter = linuxdynld;
- elfinterp(sh, startva, interpreter);
+ resoff -= elfinterp(sh, startva, resoff, interpreter);
ph = newElfPhdr();
ph->type = PT_INTERP;
@@ -494,11 +501,24 @@ asmb(void)
phsh(ph, sh);
}
+ if(HEADTYPE == Hnetbsd) {
+ sh = newElfShdr(elfstr[ElfStrNoteNetbsdIdent]);
+ sh->type = SHT_NOTE;
+ sh->flags = SHF_ALLOC;
+ sh->addralign = 4;
+ resoff -= elfnetbsdsig(sh, startva, resoff);
+
+ ph = newElfPhdr();
+ ph->type = PT_NOTE;
+ ph->flags = PF_R;
+ phsh(ph, sh);
+ }
+
elfphload(&segtext);
elfphload(&segdata);
/* Dynamic linking sections */
- if (!debug['d']) { /* -d suppresses dynamic loader format */
+ if(!debug['d']) { /* -d suppresses dynamic loader format */
/* S headers for dynamic linking */
sh = newElfShdr(elfstr[ElfStrGot]);
sh->type = SHT_PROGBITS;
@@ -589,7 +609,7 @@ asmb(void)
for(sect=segdata.sect; sect!=nil; sect=sect->next)
elfshbits(sect);
- if (!debug['s']) {
+ if(!debug['s']) {
sh = newElfShdr(elfstr[ElfStrSymtab]);
sh->type = SHT_SYMTAB;
sh->off = symo;
@@ -631,8 +651,10 @@ asmb(void)
a += elfwritehdr();
a += elfwritephdrs();
a += elfwriteshdrs();
- cflush();
- if(a+elfwriteinterp() > ELFRESERVE)
+ a += elfwriteinterp(elfstr[ElfStrInterp]);
+ if(HEADTYPE == Hnetbsd)
+ a += elfwritenetbsdsig(elfstr[ElfStrNoteNetbsdIdent]);
+ if(a > ELFRESERVE)
diag("ELFRESERVE too small: %d > %d", a, ELFRESERVE);
break;
}
@@ -1827,14 +1849,20 @@ genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*))
case STYPE:
case SSTRING:
case SGOSTRING:
+ case SNOPTRDATA:
+ case SSYMTAB:
+ case SPCLNTAB:
if(!s->reachable)
continue;
put(s, s->name, 'D', s->value, s->size, s->version, s->gotype);
continue;
case SBSS:
+ case SNOPTRBSS:
if(!s->reachable)
continue;
+ if(s->np > 0)
+ diag("%s should not be bss (size=%d type=%d special=%d)", s->name, (int)s->np, s->type, s->special);
put(s, s->name, 'B', s->value, s->size, s->version, s->gotype);
continue;
diff --git a/src/cmd/5l/doc.go b/src/cmd/5l/doc.go
index aa7ccebfc..969f502a7 100644
--- a/src/cmd/5l/doc.go
+++ b/src/cmd/5l/doc.go
@@ -4,36 +4,10 @@
/*
-5l is a modified version of the Plan 9 linker. The original is documented at
+5l is the linker for the ARM.
+The $GOARCH for these tools is arm.
- http://plan9.bell-labs.com/magic/man2html/1/2l
-
-Its target architecture is the ARM, referred to by these tools as arm.
-It reads files in .5 format generated by 5g, 5c, and 5a and emits
-a binary called 5.out by default.
-
-Major changes include:
- - support for segmented stacks (this feature is implemented here, not in the compilers).
-
-
-Original options are listed in the link above.
-
-Options new in this version:
-
--F
- Force use of software floating point.
- Also implied by setting GOARM=5 in the environment.
--Hlinux
- Write Linux ELF binaries (default when $GOOS is linux)
--I interpreter
- Set the ELF dynamic linker to use.
--L dir1 -L dir2
- Search for libraries (package files) in dir1, dir2, etc.
- The default is the single location $GOROOT/pkg/$GOOS_arm.
--r dir1:dir2:...
- Set the dynamic linker search path when using ELF.
--V
- Print the linker version.
+The flags are documented in ../ld/doc.go.
*/
package documentation
diff --git a/src/cmd/5l/l.h b/src/cmd/5l/l.h
index dabe93d37..b1a48ded8 100644
--- a/src/cmd/5l/l.h
+++ b/src/cmd/5l/l.h
@@ -143,6 +143,7 @@ struct Sym
int32 value;
int32 sig;
int32 size;
+ int32 align; // if non-zero, required alignment in bytes
uchar special;
uchar fnptr; // used as fn ptr
Sym* hash; // in hash table
diff --git a/src/cmd/5l/list.c b/src/cmd/5l/list.c
index fa838215b..7b623d78a 100644
--- a/src/cmd/5l/list.c
+++ b/src/cmd/5l/list.c
@@ -65,7 +65,7 @@ Pconv(Fmt *fp)
switch(a) {
default:
fmtprint(fp, "(%d)", p->line);
- if(p->reg == NREG)
+ if(p->reg == NREG && p->as != AGLOBL)
fmtprint(fp, " %A%C %D,%D",
a, p->scond, &p->from, &p->to);
else
diff --git a/src/cmd/5l/noop.c b/src/cmd/5l/noop.c
index eb44344f4..004f9f2fa 100644
--- a/src/cmd/5l/noop.c
+++ b/src/cmd/5l/noop.c
@@ -226,8 +226,7 @@ noops(void)
p->as = AMOVW;
p->scond = C_SCOND_LO;
p->from.type = D_CONST;
- /* 160 comes from 3 calls (3*8) 4 safes (4*8) and 104 guard */
- p->from.offset = autosize+160;
+ p->from.offset = autosize;
p->to.type = D_REG;
p->to.reg = 1;
diff --git a/src/cmd/5l/obj.c b/src/cmd/5l/obj.c
index fc5806aac..a3f816160 100644
--- a/src/cmd/5l/obj.c
+++ b/src/cmd/5l/obj.c
@@ -81,7 +81,7 @@ void
main(int argc, char *argv[])
{
int c, i;
- char *p;
+ char *p, *name, *val;
Binit(&bso, 1, OWRITE);
listinit();
@@ -92,6 +92,7 @@ main(int argc, char *argv[])
INITDAT = -1;
INITRND = -1;
INITENTRY = 0;
+ nuxiinit();
p = getenv("GOARM");
if(p != nil && strcmp(p, "5") == 0)
@@ -136,6 +137,11 @@ main(int argc, char *argv[])
case 'V':
print("%cl version %s\n", thechar, getgoversion());
errorexit();
+ case 'X':
+ name = EARGF(usage());
+ val = EARGF(usage());
+ addstrdata(name, val);
+ break;
} ARGEND
USED(argc);
@@ -235,7 +241,6 @@ main(int argc, char *argv[])
histgen = 0;
pc = 0;
dtype = 4;
- nuxiinit();
version = 0;
cbp = buf.cbuf;
@@ -535,7 +540,7 @@ loop:
s->type = SBSS;
s->value = 0;
}
- if(s->type != SBSS) {
+ if(s->type != SBSS && s->type != SNOPTRBSS && !s->dupok) {
diag("redefinition: %s\n%P", s->name, p);
s->type = SBSS;
s->value = 0;
@@ -544,6 +549,10 @@ loop:
s->size = p->to.offset;
if(p->reg & DUPOK)
s->dupok = 1;
+ if(p->reg & RODATA)
+ s->type = SRODATA;
+ else if(p->reg & NOPTR)
+ s->type = SNOPTRBSS;
break;
case ADATA:
@@ -553,8 +562,8 @@ loop:
// redefinitions.
s = p->from.sym;
if(s->dupok) {
- if(debug['v'])
- Bprint(&bso, "skipping %s in %s: dupok\n", s->name, pn);
+// if(debug['v'])
+// Bprint(&bso, "skipping %s in %s: dupok\n", s->name, pn);
goto loop;
}
if(s->file == nil)
@@ -585,6 +594,10 @@ loop:
errorexit();
}
cursym = s;
+ if(s->type != 0 && s->type != SXREF && (p->reg & DUPOK)) {
+ skip = 1;
+ goto casedef;
+ }
if(ntext++ == 0 && s->type != 0 && s->type != SXREF) {
/* redefinition, so file has probably been seen before */
if(debug['v'])
@@ -592,13 +605,8 @@ loop:
return;
}
skip = 0;
- if(s->type != 0 && s->type != SXREF) {
- if(p->reg & DUPOK) {
- skip = 1;
- goto casedef;
- }
+ if(s->type != 0 && s->type != SXREF)
diag("redefinition: %s\n%P", s->name, p);
- }
if(etextp)
etextp->next = s;
else
diff --git a/src/cmd/5l/pass.c b/src/cmd/5l/pass.c
index c43049459..0f2afbd85 100644
--- a/src/cmd/5l/pass.c
+++ b/src/cmd/5l/pass.c
@@ -213,6 +213,8 @@ patch(void)
if((a == ABL || a == ABX || a == AB || a == ARET) &&
p->to.type != D_BRANCH && p->to.sym != S) {
s = p->to.sym;
+ if(s->text == nil)
+ continue;
switch(s->type) {
default:
diag("undefined: %s", s->name);
@@ -222,7 +224,8 @@ patch(void)
case STEXT:
p->to.offset = s->value;
p->to.type = D_BRANCH;
- break;
+ p->cond = s->text;
+ continue;
}
}
if(p->to.type != D_BRANCH)
diff --git a/src/cmd/5l/softfloat.c b/src/cmd/5l/softfloat.c
index 4f799d17e..401107178 100644
--- a/src/cmd/5l/softfloat.c
+++ b/src/cmd/5l/softfloat.c
@@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#define EXTERN
#include "l.h"
#include "../ld/lib.h"
diff --git a/src/cmd/5l/span.c b/src/cmd/5l/span.c
index 2e1232a1a..13e1848e1 100644
--- a/src/cmd/5l/span.c
+++ b/src/cmd/5l/span.c
@@ -421,6 +421,8 @@ symaddr(Sym *s)
case SDATA:
case SBSS:
case SCONST:
+ case SNOPTRDATA:
+ case SNOPTRBSS:
break;
}
return v;
diff --git a/src/cmd/6a/Makefile b/src/cmd/6a/Makefile
index 30180bd24..27290ddd7 100644
--- a/src/cmd/6a/Makefile
+++ b/src/cmd/6a/Makefile
@@ -1,25 +1,10 @@
-# Copyright 2009 The Go Authors. All rights reserved.
+# 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 ../../Make.inc
-O:=$(HOST_O)
+include ../../Make.dist
-TARG=6a
+install: y.tab.h
-HFILES=\
- a.h\
- y.tab.h\
- ../6l/6.out.h\
-
-OFILES=\
- y.tab.$O\
- lex.$O\
- ../6l/enam.$O\
-
-YFILES=\
- a.y\
-
-include ../../Make.ccmd
-
-lex.$O: ../cc/macbody ../cc/lexbody
+y.tab.h: a.y
+ LANG=C LANGUAGE=en_US.UTF8 bison -d -v -y a.y
diff --git a/src/cmd/6a/a.y b/src/cmd/6a/a.y
index c0fa4106e..8459ff323 100644
--- a/src/cmd/6a/a.y
+++ b/src/cmd/6a/a.y
@@ -429,6 +429,12 @@ imm:
$$.type = D_FCONST;
$$.dval = $3;
}
+| '$' '(' '-' LFCONST ')'
+ {
+ $$ = nullgen;
+ $$.type = D_FCONST;
+ $$.dval = -$4;
+ }
| '$' '-' LFCONST
{
$$ = nullgen;
diff --git a/src/cmd/6a/lex.c b/src/cmd/6a/lex.c
index 42f4b1d11..e013bec2a 100644
--- a/src/cmd/6a/lex.c
+++ b/src/cmd/6a/lex.c
@@ -44,7 +44,11 @@ enum
int
systemtype(int sys)
{
+#ifdef _WIN32
+ return sys&Windows;
+#else
return sys&Plan9;
+#endif
}
int
@@ -392,6 +396,7 @@ struct
"IMULB", LTYPEI, AIMULB,
"IMULL", LTYPEI, AIMULL,
"IMULQ", LTYPEI, AIMULQ,
+ "IMUL3Q", LTYPEX, AIMUL3Q,
"IMULW", LTYPEI, AIMULW,
"INB", LTYPE0, AINB,
"INL", LTYPE0, AINL,
@@ -456,8 +461,8 @@ struct
"JGT", LTYPER, AJGT,
"JG", LTYPER, AJGT, /* alternate */
"JNLE", LTYPER, AJGT, /* alternate */
-
- "JCXZ", LTYPER, AJCXZ,
+ "JCXZL", LTYPER, AJCXZL,
+ "JCXZQ", LTYPER, AJCXZQ,
"JMP", LTYPEC, AJMP,
"LAHF", LTYPE0, ALAHF,
"LARL", LTYPE3, ALARL,
@@ -1251,17 +1256,13 @@ outhist(void)
for(h = hist; h != H; h = h->link) {
p = h->name;
op = 0;
- /* on windows skip drive specifier in pathname */
if(systemtype(Windows) && p && p[1] == ':'){
- p += 2;
- c = *p;
- }
- if(p && p[0] != c && h->offset == 0 && pathname){
- /* on windows skip drive specifier in pathname */
+ c = p[2];
+ } else if(p && p[0] != c && h->offset == 0 && pathname){
if(systemtype(Windows) && pathname[1] == ':') {
op = p;
- p = pathname+2;
- c = *p;
+ p = pathname;
+ c = p[2];
} else if(pathname[0] == c){
op = p;
p = pathname;
diff --git a/src/cmd/6a/y.tab.c b/src/cmd/6a/y.tab.c
new file mode 100644
index 000000000..2da8a1fac
--- /dev/null
+++ b/src/cmd/6a/y.tab.c
@@ -0,0 +1,2730 @@
+/* A Bison parser, made by GNU Bison 2.3. */
+
+/* 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
+ 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.
+
+ 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. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ 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. */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+ simplifying the original so-called "semantic" parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Bison version. */
+#define YYBISON_VERSION "2.3"
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 0
+
+/* Using locations. */
+#define YYLSP_NEEDED 0
+
+
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ LTYPE0 = 258,
+ LTYPE1 = 259,
+ LTYPE2 = 260,
+ LTYPE3 = 261,
+ LTYPE4 = 262,
+ LTYPEC = 263,
+ LTYPED = 264,
+ LTYPEN = 265,
+ 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
+ };
+#endif
+/* Tokens. */
+#define LTYPE0 258
+#define LTYPE1 259
+#define LTYPE2 260
+#define LTYPE3 261
+#define LTYPE4 262
+#define LTYPEC 263
+#define LTYPED 264
+#define LTYPEN 265
+#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"
+
+
+/* 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"
+{
+ Sym *sym;
+ vlong lval;
+ double dval;
+ char sval[8];
+ Gen gen;
+ Gen2 gen2;
+}
+/* Line 193 of yacc.c. */
+#line 182 "y.tab.c"
+ YYSTYPE;
+# 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"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+# define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+# define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# else
+# define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+# if ENABLE_NLS
+# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+# define YY_(msgid) dgettext ("bison-runtime", msgid)
+# endif
+# endif
+# ifndef YY_
+# define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E. */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions. */
+#ifndef lint
+# define YYID(n) (n)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int i)
+#else
+static int
+YYID (i)
+ int i;
+#endif
+{
+ return i;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# elif defined __BUILTIN_VA_ARG_INCR
+# include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+# elif defined _AIX
+# define YYSTACK_ALLOC __alloca
+# elif defined _MSC_VER
+# include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+# define alloca _alloca
+# else
+# define YYSTACK_ALLOC alloca
+# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (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
+# endif
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's `empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+# ifndef YYSTACK_ALLOC_MAXIMUM
+ /* The OS might guarantee only one guard page at the bottom of the stack,
+ and a page size can be as small as 4096 bytes. So we cannot safely
+ invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
+ to allow for a few compiler-allocated temporary stack slots. */
+# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+# endif
+# else
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# ifndef YYSTACK_ALLOC_MAXIMUM
+# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+# endif
+# if (defined __cplusplus && ! defined _STDLIB_H \
+ && ! ((defined YYMALLOC || defined malloc) \
+ && (defined YYFREE || defined free)))
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef _STDLIB_H
+# define _STDLIB_H 1
+# endif
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# if ! defined malloc && ! defined _STDLIB_H && (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__ \
+ || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+ && (! defined __cplusplus \
+ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ yytype_int16 yyss;
+ YYSTYPE yyvs;
+ };
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((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
+
+/* 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
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 2
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 548
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 54
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 40
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 131
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 260
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 290
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
+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, 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, 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, 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, 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, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 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, 44,
+ 45
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+ YYRHS. */
+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, 120, 124, 130, 133, 135, 137, 139, 143,
+ 149, 153, 159, 162, 164, 168, 174, 180, 181, 183,
+ 187, 193, 195, 197, 199, 201, 204, 207, 209, 211,
+ 213, 215, 220, 223, 226, 228, 230, 232, 234, 236,
+ 238, 240, 243, 246, 249, 252, 255, 260, 266, 270,
+ 272, 274, 276, 281, 286, 291, 298, 308, 312, 316,
+ 322, 331, 333, 340, 346, 354, 355, 358, 361, 363,
+ 365, 367, 369, 371, 374, 377, 380, 384, 386, 389,
+ 393, 398, 400, 404, 408, 412, 416, 420, 425, 430,
+ 434, 438
+};
+
+/* 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, 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
+};
+
+/* 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, 181, 189, 194, 202, 207, 214, 215, 218, 223,
+ 233, 238, 248, 253, 258, 265, 273, 283, 287, 294,
+ 299, 307, 308, 311, 312, 313, 317, 321, 322, 325,
+ 326, 329, 335, 344, 353, 358, 363, 368, 373, 378,
+ 383, 389, 397, 403, 414, 420, 426, 432, 438, 446,
+ 447, 450, 456, 462, 468, 474, 483, 492, 497, 502,
+ 510, 520, 524, 533, 540, 549, 552, 556, 562, 563,
+ 567, 570, 571, 575, 579, 583, 587, 593, 597, 601,
+ 606, 613, 614, 618, 622, 626, 630, 634, 638, 642,
+ 646, 650
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+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
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+ token YYLEX-NUM. */
+static const yytype_uint16 yytoknum[] =
+{
+ 0, 256, 257, 124, 94, 38, 60, 62, 43, 45,
+ 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
+};
+# 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, 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
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+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, 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
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+ STATE-NUM 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, 57, 0, 0, 0, 9, 4, 0, 11, 32,
+ 14, 0, 0, 111, 74, 76, 79, 75, 77, 80,
+ 78, 105, 112, 0, 0, 0, 15, 38, 61, 62,
+ 89, 90, 101, 91, 0, 16, 69, 36, 70, 17,
+ 0, 18, 0, 0, 105, 105, 0, 22, 45, 63,
+ 67, 68, 64, 91, 20, 0, 32, 46, 47, 23,
+ 105, 0, 0, 19, 40, 0, 21, 0, 30, 0,
+ 24, 0, 25, 0, 26, 53, 27, 0, 28, 0,
+ 29, 58, 7, 0, 5, 0, 10, 114, 113, 0,
+ 0, 0, 0, 37, 0, 0, 121, 0, 115, 0,
+ 0, 0, 85, 84, 0, 83, 82, 35, 0, 0,
+ 65, 66, 72, 73, 44, 0, 0, 72, 39, 0,
+ 0, 0, 0, 0, 52, 0, 0, 0, 12, 0,
+ 13, 105, 106, 107, 0, 0, 97, 98, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 116, 0,
+ 0, 0, 0, 88, 0, 0, 33, 34, 0, 0,
+ 0, 42, 0, 59, 0, 48, 50, 54, 0, 0,
+ 8, 6, 0, 110, 108, 109, 0, 0, 0, 131,
+ 130, 129, 0, 0, 122, 123, 124, 125, 126, 0,
+ 0, 92, 94, 93, 0, 86, 71, 0, 0, 117,
+ 81, 0, 0, 0, 0, 0, 0, 0, 103, 99,
+ 0, 127, 128, 0, 0, 0, 87, 41, 118, 0,
+ 43, 60, 49, 51, 55, 56, 0, 0, 102, 95,
+ 0, 0, 119, 104, 0, 0, 120, 100, 0, 96
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const yytype_int16 yydefgoto[] =
+{
+ -1, 1, 3, 26, 149, 147, 27, 30, 59, 61,
+ 55, 46, 83, 74, 86, 67, 79, 90, 92, 94,
+ 96, 98, 100, 88, 56, 68, 57, 69, 48, 181,
+ 58, 49, 50, 51, 52, 112, 196, 53, 220, 117
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -97
+static const yytype_int16 yypact[] =
+{
+ -97, 29, -97, 206, -39, -36, 254, 278, 278, 326,
+ 230, 15, 302, 59, 412, 412, 278, 278, 278, 82,
+ -20, -20, -22, -6, 2, -97, -97, 12, -97, -97,
+ -97, 476, 476, -97, -97, -97, -97, -97, -97, -97,
+ -97, 88, -97, 326, 366, 476, -97, -97, -97, -97,
+ -97, -97, 14, 16, 364, -97, -97, 33, -97, -97,
+ 39, -97, 41, 350, 88, 11, 152, -97, -97, -97,
+ -97, -97, -97, 31, -97, 51, 326, -97, -97, -97,
+ 11, 382, 476, -97, -97, 42, -97, 50, -97, 56,
+ -97, 58, -97, 61, -97, 64, -97, 66, -97, 79,
+ -97, -97, -97, 476, -97, 476, -97, -97, -97, 84,
+ 476, 476, 92, -97, 18, 78, -97, 129, -97, 108,
+ -5, 414, -97, -97, 421, -97, -97, -97, 326, 278,
+ -97, -97, 92, -97, -97, 13, 476, -97, -97, 112,
+ 428, 438, 326, 326, 326, 326, 326, 206, 527, 206,
+ 527, 11, -97, -97, 10, 476, 96, -97, 476, 476,
+ 476, 141, 144, 476, 476, 476, 476, 476, -97, 142,
+ 23, 126, 127, -97, 467, 128, -97, -97, 130, 110,
+ 8, -97, 114, -97, 134, 138, 139, -97, 149, 154,
+ -97, -97, 163, -97, -97, -97, 153, 165, 179, 536,
+ 493, 159, 476, 476, 65, 65, -97, -97, -97, 476,
+ 476, 168, -97, -97, 185, -97, -97, -20, 211, 233,
+ -97, 191, -20, 209, 210, 476, 82, 214, -97, -97,
+ 238, 200, 200, 201, 203, 220, -97, -97, 247, 227,
+ -97, -97, -97, -97, -97, -97, 207, 476, -97, -97,
+ 249, 231, -97, -97, 219, 476, -97, -97, 225, -97
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const yytype_int16 yypgoto[] =
+{
+ -97, -97, -97, -96, -97, -97, -97, 259, -97, -97,
+ -97, 265, -97, -97, -97, -97, -97, -97, -97, -97,
+ -97, -97, -97, -97, 30, 212, -2, -11, -9, 60,
+ -16, 32, -3, -1, 7, -53, -97, -10, -97, -80
+};
+
+/* 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 -1
+static const yytype_uint16 yytable[] =
+{
+ 73, 70, 84, 85, 99, 101, 60, 71, 28, 72,
+ 97, 132, 133, 29, 91, 93, 95, 218, 75, 110,
+ 111, 107, 108, 148, 102, 150, 103, 137, 155, 2,
+ 170, 171, 54, 210, 116, 118, 47, 172, 219, 62,
+ 104, 193, 47, 194, 126, 178, 87, 89, 170, 171,
+ 105, 190, 195, 191, 130, 172, 73, 70, 41, 106,
+ 131, 125, 136, 71, 119, 72, 120, 31, 32, 156,
+ 138, 85, 116, 113, 211, 165, 166, 167, 199, 200,
+ 201, 135, 127, 204, 205, 206, 207, 208, 128, 33,
+ 129, 151, 139, 116, 109, 116, 110, 111, 192, 140,
+ 152, 153, 80, 65, 42, 141, 113, 142, 81, 82,
+ 143, 108, 45, 144, 116, 145, 34, 35, 36, 37,
+ 38, 39, 231, 232, 40, 183, 179, 177, 146, 157,
+ 182, 184, 158, 159, 160, 161, 162, 163, 164, 165,
+ 166, 167, 154, 169, 178, 197, 198, 202, 116, 116,
+ 116, 203, 209, 116, 116, 116, 116, 116, 176, 217,
+ 31, 32, 63, 221, 108, 161, 162, 163, 164, 165,
+ 166, 167, 185, 186, 187, 188, 189, 212, 213, 215,
+ 168, 216, 33, 222, 223, 224, 34, 35, 36, 37,
+ 38, 39, 116, 116, 40, 64, 65, 42, 225, 233,
+ 234, 237, 44, 226, 228, 45, 241, 4, 163, 164,
+ 165, 166, 167, 227, 230, 244, 229, 245, 235, 5,
+ 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 236, 254, 31, 32,
+ 63, 238, 239, 180, 242, 258, 243, 246, 247, 22,
+ 23, 24, 248, 25, 249, 250, 251, 252, 253, 255,
+ 33, 256, 31, 32, 34, 35, 36, 37, 38, 39,
+ 257, 77, 40, 64, 65, 42, 259, 78, 134, 66,
+ 44, 240, 0, 45, 33, 0, 31, 32, 34, 35,
+ 36, 37, 38, 39, 0, 0, 40, 41, 0, 42,
+ 0, 0, 0, 43, 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, 121, 31, 32, 44, 0, 0, 45,
+ 33, 0, 0, 0, 34, 35, 36, 37, 38, 39,
+ 31, 32, 40, 0, 33, 42, 33, 0, 0, 0,
+ 44, 114, 0, 45, 122, 123, 0, 41, 115, 42,
+ 0, 42, 33, 0, 124, 0, 82, 45, 0, 45,
+ 31, 32, 31, 32, 0, 80, 65, 42, 0, 31,
+ 174, 0, 82, 0, 0, 45, 31, 32, 0, 0,
+ 0, 0, 33, 0, 33, 0, 31, 32, 0, 0,
+ 0, 33, 0, 0, 173, 41, 0, 42, 33, 42,
+ 0, 175, 44, 0, 82, 45, 42, 45, 33, 0,
+ 0, 82, 0, 42, 45, 31, 32, 0, 82, 0,
+ 180, 45, 0, 42, 31, 32, 0, 0, 82, 0,
+ 54, 45, 0, 0, 0, 0, 0, 33, 160, 161,
+ 162, 163, 164, 165, 166, 167, 33, 214, 0, 0,
+ 0, 0, 42, 0, 0, 0, 0, 82, 0, 0,
+ 45, 42, 0, 0, 0, 0, 82, 0, 0, 45,
+ 158, 159, 160, 161, 162, 163, 164, 165, 166, 167,
+ 159, 160, 161, 162, 163, 164, 165, 166, 167
+};
+
+static const yytype_int16 yycheck[] =
+{
+ 10, 10, 13, 13, 20, 21, 8, 10, 47, 10,
+ 19, 64, 65, 49, 16, 17, 18, 9, 11, 8,
+ 9, 31, 32, 103, 46, 105, 48, 80, 10, 0,
+ 35, 36, 52, 10, 44, 45, 6, 42, 30, 9,
+ 46, 31, 12, 33, 54, 32, 14, 15, 35, 36,
+ 48, 147, 42, 149, 63, 42, 66, 66, 43, 47,
+ 63, 54, 11, 66, 50, 66, 50, 8, 9, 51,
+ 81, 81, 82, 43, 51, 10, 11, 12, 158, 159,
+ 160, 50, 49, 163, 164, 165, 166, 167, 49, 30,
+ 49, 7, 50, 103, 6, 105, 8, 9, 151, 49,
+ 110, 111, 43, 44, 45, 49, 76, 49, 49, 50,
+ 49, 121, 53, 49, 124, 49, 34, 35, 36, 37,
+ 38, 39, 202, 203, 42, 141, 136, 129, 49, 51,
+ 140, 141, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 50, 35, 32, 155, 50, 6, 158, 159,
+ 160, 7, 10, 163, 164, 165, 166, 167, 128, 49,
+ 8, 9, 10, 49, 174, 6, 7, 8, 9, 10,
+ 11, 12, 142, 143, 144, 145, 146, 51, 51, 51,
+ 51, 51, 30, 49, 46, 46, 34, 35, 36, 37,
+ 38, 39, 202, 203, 42, 43, 44, 45, 49, 209,
+ 210, 217, 50, 49, 51, 53, 222, 1, 8, 9,
+ 10, 11, 12, 50, 35, 225, 51, 226, 50, 13,
+ 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 51, 247, 8, 9,
+ 10, 30, 9, 52, 35, 255, 36, 33, 10, 43,
+ 44, 45, 51, 47, 51, 35, 9, 30, 51, 10,
+ 30, 30, 8, 9, 34, 35, 36, 37, 38, 39,
+ 51, 12, 42, 43, 44, 45, 51, 12, 66, 49,
+ 50, 221, -1, 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, -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, 8, 9, 50, -1, -1, 53,
+ 30, -1, -1, -1, 34, 35, 36, 37, 38, 39,
+ 8, 9, 42, -1, 30, 45, 30, -1, -1, -1,
+ 50, 35, -1, 53, 40, 41, -1, 43, 42, 45,
+ -1, 45, 30, -1, 50, -1, 50, 53, -1, 53,
+ 8, 9, 8, 9, -1, 43, 44, 45, -1, 8,
+ 9, -1, 50, -1, -1, 53, 8, 9, -1, -1,
+ -1, -1, 30, -1, 30, -1, 8, 9, -1, -1,
+ -1, 30, -1, -1, 40, 43, -1, 45, 30, 45,
+ -1, 40, 50, -1, 50, 53, 45, 53, 30, -1,
+ -1, 50, -1, 45, 53, 8, 9, -1, 50, -1,
+ 52, 53, -1, 45, 8, 9, -1, -1, 50, -1,
+ 52, 53, -1, -1, -1, -1, -1, 30, 5, 6,
+ 7, 8, 9, 10, 11, 12, 30, 40, -1, -1,
+ -1, -1, 45, -1, -1, -1, -1, 50, -1, -1,
+ 53, 45, -1, -1, -1, -1, 50, -1, -1, 53,
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
+ 4, 5, 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,
+ 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, 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, 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,
+ 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
+};
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+/* 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. */
+
+#define YYFAIL goto yyerrlab
+
+#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 \
+ { \
+ yyerror (YY_("syntax error: cannot back up")); \
+ YYERROR; \
+ } \
+while (YYID (0))
+
+
+#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. */
+
+#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
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments. */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (YYLEX_PARAM)
+#else
+# define YYLEX yylex ()
+#endif
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yy_symbol_print (stderr, \
+ Type, Value); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+#endif
+{
+ if (!yyvaluep)
+ return;
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+ YYUSE (yyoutput);
+# endif
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+#endif
+{
+ if (yytype < YYNTOKENS)
+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+ else
+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+ yy_symbol_value_print (yyoutput, yytype, yyvaluep);
+ YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *bottom, yytype_int16 *top)
+#else
+static void
+yy_stack_print (bottom, top)
+ yytype_int16 *bottom;
+ yytype_int16 *top;
+#endif
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (; bottom <= top; ++bottom)
+ YYFPRINTF (stderr, " %d", *bottom);
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, int yyrule)
+#else
+static void
+yy_reduce_print (yyvsp, yyrule)
+ YYSTYPE *yyvsp;
+ int yyrule;
+#endif
+{
+ int yynrhs = yyr2[yyrule];
+ int yyi;
+ unsigned long int yylno = yyrline[yyrule];
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+ yyrule - 1, yylno);
+ /* The symbols being reduced. */
+ for (yyi = 0; yyi < yynrhs; yyi++)
+ {
+ fprintf (stderr, " $%d = ", yyi + 1);
+ yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+ &(yyvsp[(yyi + 1) - (yynrhs)])
+ );
+ fprintf (stderr, "\n");
+ }
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (yyvsp, Rule); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined __GLIBC__ && defined _STRING_H
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+ const char *yystr;
+#endif
+{
+ YYSIZE_T yylen;
+ for (yylen = 0; yystr[yylen]; yylen++)
+ continue;
+ return yylen;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+ char *yydest;
+ const char *yysrc;
+#endif
+{
+ char *yyd = yydest;
+ const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+ quotes and backslashes, so that it's suitable for yyerror. The
+ heuristic is that double-quoting is unnecessary unless the string
+ contains an apostrophe, a comma, or backslash (other than
+ backslash-backslash). YYSTR is taken from yytname. If YYRES is
+ null, do not copy; instead, return the length of what the result
+ would have been. */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+ if (*yystr == '"')
+ {
+ YYSIZE_T yyn = 0;
+ char const *yyp = yystr;
+
+ for (;;)
+ switch (*++yyp)
+ {
+ case '\'':
+ case ',':
+ goto do_not_strip_quotes;
+
+ case '\\':
+ if (*++yyp != '\\')
+ goto do_not_strip_quotes;
+ /* Fall through. */
+ default:
+ if (yyres)
+ yyres[yyn] = *yyp;
+ yyn++;
+ break;
+
+ case '"':
+ if (yyres)
+ yyres[yyn] = '\0';
+ return yyn;
+ }
+ do_not_strip_quotes: ;
+ }
+
+ if (! yyres)
+ return yystrlen (yystr);
+
+ return yystpcpy (yyres, yystr) - yyres;
+}
+# 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];
+
+ 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;
+ }
+
+ yyf = YY_(yyformat);
+ yysize1 = yysize + yystrlen (yyf);
+ yysize_overflow |= (yysize1 < yysize);
+ yysize = yysize1;
+
+ if (yysize_overflow)
+ return YYSIZE_MAXIMUM;
+
+ 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;
+ }
+}
+#endif /* YYERROR_VERBOSE */
+
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep)
+ const char *yymsg;
+ int yytype;
+ YYSTYPE *yyvaluep;
+#endif
+{
+ YYUSE (yyvaluep);
+
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ switch (yytype)
+ {
+
+ default:
+ break;
+ }
+}
+
+
+/* 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. */
+int yychar;
+
+/* The semantic value of the look-ahead symbol. */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far. */
+int yynerrs;
+
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+ void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void)
+#else
+int
+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;
+
+ /* 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;
+
+ 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;
+
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. So pushing a state here evens the stacks. */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ 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
+ be undefined if yyoverflow is a macro. */
+ yyoverflow (YY_("memory exhausted"),
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+
+ &yystacksize);
+
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyexhaustedlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyexhaustedlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ yytype_int16 *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyexhaustedlab;
+ YYSTACK_RELOCATE (yyss);
+ YYSTACK_RELOCATE (yyvs);
+
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+ /* Do appropriate processing given the current state. Read a
+ look-ahead token if we need one and don't already have one. */
+
+ /* First try to decide what to do without reference to look-ahead token. */
+ yyn = yypact[yystate];
+ if (yyn == YYPACT_NINF)
+ goto yydefault;
+
+ /* Not known => get a look-ahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = YYLEX;
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yyn == 0 || yyn == YYTABLE_NINF)
+ 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. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+ /* Discard the shifted token unless it is eof. */
+ if (yychar != YYEOF)
+ yychar = YYEMPTY;
+
+ yystate = yyn;
+ *++yyvsp = yylval;
+
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ `$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 3:
+#line 66 "a.y"
+ {
+ stmtline = lineno;
+ }
+ break;
+
+ case 5:
+#line 73 "a.y"
+ {
+ if((yyvsp[(1) - (2)].sym)->value != pc)
+ yyerror("redeclaration of %s", (yyvsp[(1) - (2)].sym)->name);
+ (yyvsp[(1) - (2)].sym)->value = pc;
+ }
+ break;
+
+ case 7:
+#line 80 "a.y"
+ {
+ (yyvsp[(1) - (2)].sym)->type = LLAB;
+ (yyvsp[(1) - (2)].sym)->value = pc;
+ }
+ break;
+
+ case 12:
+#line 91 "a.y"
+ {
+ (yyvsp[(1) - (3)].sym)->type = LVAR;
+ (yyvsp[(1) - (3)].sym)->value = (yyvsp[(3) - (3)].lval);
+ }
+ break;
+
+ case 13:
+#line 96 "a.y"
+ {
+ if((yyvsp[(1) - (3)].sym)->value != (yyvsp[(3) - (3)].lval))
+ yyerror("redeclaration of %s", (yyvsp[(1) - (3)].sym)->name);
+ (yyvsp[(1) - (3)].sym)->value = (yyvsp[(3) - (3)].lval);
+ }
+ break;
+
+ case 14:
+#line 101 "a.y"
+ { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
+ break;
+
+ case 15:
+#line 102 "a.y"
+ { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
+ break;
+
+ case 16:
+#line 103 "a.y"
+ { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
+ break;
+
+ case 17:
+#line 104 "a.y"
+ { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
+ break;
+
+ case 18:
+#line 105 "a.y"
+ { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
+ break;
+
+ case 19:
+#line 106 "a.y"
+ { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
+ break;
+
+ case 20:
+#line 107 "a.y"
+ { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
+ break;
+
+ case 21:
+#line 108 "a.y"
+ { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
+ break;
+
+ case 22:
+#line 109 "a.y"
+ { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
+ break;
+
+ case 23:
+#line 110 "a.y"
+ { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
+ break;
+
+ case 24:
+#line 111 "a.y"
+ { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
+ break;
+
+ case 25:
+#line 112 "a.y"
+ { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
+ break;
+
+ case 26:
+#line 113 "a.y"
+ { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
+ break;
+
+ case 27:
+#line 114 "a.y"
+ { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
+ break;
+
+ case 28:
+#line 115 "a.y"
+ { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
+ break;
+
+ case 29:
+#line 116 "a.y"
+ { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
+ break;
+
+ case 30:
+#line 117 "a.y"
+ { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
+ break;
+
+ case 31:
+#line 120 "a.y"
+ {
+ (yyval.gen2).from = nullgen;
+ (yyval.gen2).to = nullgen;
+ }
+ break;
+
+ case 32:
+#line 125 "a.y"
+ {
+ (yyval.gen2).from = nullgen;
+ (yyval.gen2).to = nullgen;
+ }
+ break;
+
+ case 33:
+#line 132 "a.y"
+ {
+ (yyval.gen2).from = (yyvsp[(1) - (3)].gen);
+ (yyval.gen2).to = (yyvsp[(3) - (3)].gen);
+ }
+ break;
+
+ case 34:
+#line 139 "a.y"
+ {
+ (yyval.gen2).from = (yyvsp[(1) - (3)].gen);
+ (yyval.gen2).to = (yyvsp[(3) - (3)].gen);
+ }
+ break;
+
+ case 35:
+#line 146 "a.y"
+ {
+ (yyval.gen2).from = (yyvsp[(1) - (2)].gen);
+ (yyval.gen2).to = nullgen;
+ }
+ break;
+
+ case 36:
+#line 151 "a.y"
+ {
+ (yyval.gen2).from = (yyvsp[(1) - (1)].gen);
+ (yyval.gen2).to = nullgen;
+ }
+ break;
+
+ case 37:
+#line 158 "a.y"
+ {
+ (yyval.gen2).from = nullgen;
+ (yyval.gen2).to = (yyvsp[(2) - (2)].gen);
+ }
+ break;
+
+ case 38:
+#line 163 "a.y"
+ {
+ (yyval.gen2).from = nullgen;
+ (yyval.gen2).to = (yyvsp[(1) - (1)].gen);
+ }
+ break;
+
+ case 39:
+#line 170 "a.y"
+ {
+ (yyval.gen2).from = nullgen;
+ (yyval.gen2).to = (yyvsp[(2) - (2)].gen);
+ }
+ break;
+
+ case 40:
+#line 175 "a.y"
+ {
+ (yyval.gen2).from = nullgen;
+ (yyval.gen2).to = (yyvsp[(1) - (1)].gen);
+ }
+ break;
+
+ case 41:
+#line 182 "a.y"
+ {
+ (yyval.gen2).from = (yyvsp[(1) - (5)].gen);
+ (yyval.gen2).from.scale = (yyvsp[(3) - (5)].lval);
+ (yyval.gen2).to = (yyvsp[(5) - (5)].gen);
+ }
+ break;
+
+ case 42:
+#line 190 "a.y"
+ {
+ (yyval.gen2).from = (yyvsp[(1) - (3)].gen);
+ (yyval.gen2).to = (yyvsp[(3) - (3)].gen);
+ }
+ break;
+
+ case 43:
+#line 195 "a.y"
+ {
+ (yyval.gen2).from = (yyvsp[(1) - (5)].gen);
+ (yyval.gen2).from.scale = (yyvsp[(3) - (5)].lval);
+ (yyval.gen2).to = (yyvsp[(5) - (5)].gen);
+ }
+ break;
+
+ case 44:
+#line 203 "a.y"
+ {
+ (yyval.gen2).from = nullgen;
+ (yyval.gen2).to = (yyvsp[(2) - (2)].gen);
+ }
+ break;
+
+ case 45:
+#line 208 "a.y"
+ {
+ (yyval.gen2).from = nullgen;
+ (yyval.gen2).to = (yyvsp[(1) - (1)].gen);
+ }
+ break;
+
+ case 48:
+#line 219 "a.y"
+ {
+ (yyval.gen2).from = (yyvsp[(1) - (3)].gen);
+ (yyval.gen2).to = (yyvsp[(3) - (3)].gen);
+ }
+ break;
+
+ case 49:
+#line 224 "a.y"
+ {
+ (yyval.gen2).from = (yyvsp[(1) - (5)].gen);
+ (yyval.gen2).to = (yyvsp[(3) - (5)].gen);
+ if((yyval.gen2).from.index != D_NONE)
+ yyerror("dp shift with lhs index");
+ (yyval.gen2).from.index = (yyvsp[(5) - (5)].lval);
+ }
+ break;
+
+ case 50:
+#line 234 "a.y"
+ {
+ (yyval.gen2).from = (yyvsp[(1) - (3)].gen);
+ (yyval.gen2).to = (yyvsp[(3) - (3)].gen);
+ }
+ break;
+
+ case 51:
+#line 239 "a.y"
+ {
+ (yyval.gen2).from = (yyvsp[(1) - (5)].gen);
+ (yyval.gen2).to = (yyvsp[(3) - (5)].gen);
+ if((yyval.gen2).to.index != D_NONE)
+ yyerror("dp move with lhs index");
+ (yyval.gen2).to.index = (yyvsp[(5) - (5)].lval);
+ }
+ break;
+
+ case 52:
+#line 249 "a.y"
+ {
+ (yyval.gen2).from = (yyvsp[(1) - (2)].gen);
+ (yyval.gen2).to = nullgen;
+ }
+ break;
+
+ case 53:
+#line 254 "a.y"
+ {
+ (yyval.gen2).from = (yyvsp[(1) - (1)].gen);
+ (yyval.gen2).to = nullgen;
+ }
+ break;
+
+ case 54:
+#line 259 "a.y"
+ {
+ (yyval.gen2).from = (yyvsp[(1) - (3)].gen);
+ (yyval.gen2).to = (yyvsp[(3) - (3)].gen);
+ }
+ break;
+
+ case 55:
+#line 266 "a.y"
+ {
+ (yyval.gen2).from = (yyvsp[(1) - (5)].gen);
+ (yyval.gen2).to = (yyvsp[(3) - (5)].gen);
+ (yyval.gen2).to.offset = (yyvsp[(5) - (5)].lval);
+ }
+ break;
+
+ case 56:
+#line 274 "a.y"
+ {
+ (yyval.gen2).from = (yyvsp[(3) - (5)].gen);
+ (yyval.gen2).to = (yyvsp[(5) - (5)].gen);
+ if((yyvsp[(1) - (5)].gen).type != D_CONST)
+ yyerror("illegal constant");
+ (yyval.gen2).to.offset = (yyvsp[(1) - (5)].gen).offset;
+ }
+ break;
+
+ case 57:
+#line 283 "a.y"
+ {
+ (yyval.gen2).from = nullgen;
+ (yyval.gen2).to = nullgen;
+ }
+ break;
+
+ case 58:
+#line 288 "a.y"
+ {
+ (yyval.gen2).from = (yyvsp[(1) - (1)].gen);
+ (yyval.gen2).to = nullgen;
+ }
+ break;
+
+ case 59:
+#line 295 "a.y"
+ {
+ (yyval.gen2).from = (yyvsp[(1) - (3)].gen);
+ (yyval.gen2).to = (yyvsp[(3) - (3)].gen);
+ }
+ break;
+
+ case 60:
+#line 300 "a.y"
+ {
+ (yyval.gen2).from = (yyvsp[(1) - (5)].gen);
+ (yyval.gen2).from.scale = (yyvsp[(3) - (5)].lval);
+ (yyval.gen2).to = (yyvsp[(5) - (5)].gen);
+ }
+ break;
+
+ case 65:
+#line 314 "a.y"
+ {
+ (yyval.gen) = (yyvsp[(2) - (2)].gen);
+ }
+ break;
+
+ case 66:
+#line 318 "a.y"
+ {
+ (yyval.gen) = (yyvsp[(2) - (2)].gen);
+ }
+ break;
+
+ case 71:
+#line 330 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_BRANCH;
+ (yyval.gen).offset = (yyvsp[(1) - (4)].lval) + pc;
+ }
+ break;
+
+ case 72:
+#line 336 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ if(pass == 2)
+ yyerror("undefined label: %s", (yyvsp[(1) - (2)].sym)->name);
+ (yyval.gen).type = D_BRANCH;
+ (yyval.gen).sym = (yyvsp[(1) - (2)].sym);
+ (yyval.gen).offset = (yyvsp[(2) - (2)].lval);
+ }
+ break;
+
+ case 73:
+#line 345 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_BRANCH;
+ (yyval.gen).sym = (yyvsp[(1) - (2)].sym);
+ (yyval.gen).offset = (yyvsp[(1) - (2)].sym)->value + (yyvsp[(2) - (2)].lval);
+ }
+ break;
+
+ case 74:
+#line 354 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = (yyvsp[(1) - (1)].lval);
+ }
+ break;
+
+ case 75:
+#line 359 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = (yyvsp[(1) - (1)].lval);
+ }
+ break;
+
+ case 76:
+#line 364 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = (yyvsp[(1) - (1)].lval);
+ }
+ break;
+
+ case 77:
+#line 369 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = (yyvsp[(1) - (1)].lval);
+ }
+ break;
+
+ case 78:
+#line 374 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_SP;
+ }
+ break;
+
+ case 79:
+#line 379 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = (yyvsp[(1) - (1)].lval);
+ }
+ break;
+
+ case 80:
+#line 384 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = (yyvsp[(1) - (1)].lval);
+ }
+ break;
+
+ case 81:
+#line 390 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_CONST;
+ (yyval.gen).offset = (yyvsp[(2) - (2)].lval);
+ }
+ break;
+
+ case 82:
+#line 398 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_CONST;
+ (yyval.gen).offset = (yyvsp[(2) - (2)].lval);
+ }
+ break;
+
+ case 83:
+#line 404 "a.y"
+ {
+ (yyval.gen) = (yyvsp[(2) - (2)].gen);
+ (yyval.gen).index = (yyvsp[(2) - (2)].gen).type;
+ (yyval.gen).type = D_ADDR;
+ /*
+ if($2.type == D_AUTO || $2.type == D_PARAM)
+ yyerror("constant cannot be automatic: %s",
+ $2.sym->name);
+ */
+ }
+ break;
+
+ case 84:
+#line 415 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_SCONST;
+ memcpy((yyval.gen).sval, (yyvsp[(2) - (2)].sval), sizeof((yyval.gen).sval));
+ }
+ break;
+
+ case 85:
+#line 421 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_FCONST;
+ (yyval.gen).dval = (yyvsp[(2) - (2)].dval);
+ }
+ break;
+
+ case 86:
+#line 427 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_FCONST;
+ (yyval.gen).dval = (yyvsp[(3) - (4)].dval);
+ }
+ break;
+
+ case 87:
+#line 433 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_FCONST;
+ (yyval.gen).dval = -(yyvsp[(4) - (5)].dval);
+ }
+ break;
+
+ case 88:
+#line 439 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_FCONST;
+ (yyval.gen).dval = -(yyvsp[(3) - (3)].dval);
+ }
+ break;
+
+ case 91:
+#line 451 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_INDIR+D_NONE;
+ (yyval.gen).offset = (yyvsp[(1) - (1)].lval);
+ }
+ break;
+
+ case 92:
+#line 457 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_INDIR+(yyvsp[(3) - (4)].lval);
+ (yyval.gen).offset = (yyvsp[(1) - (4)].lval);
+ }
+ break;
+
+ case 93:
+#line 463 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_INDIR+D_SP;
+ (yyval.gen).offset = (yyvsp[(1) - (4)].lval);
+ }
+ break;
+
+ case 94:
+#line 469 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_INDIR+(yyvsp[(3) - (4)].lval);
+ (yyval.gen).offset = (yyvsp[(1) - (4)].lval);
+ }
+ break;
+
+ case 95:
+#line 475 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_INDIR+D_NONE;
+ (yyval.gen).offset = (yyvsp[(1) - (6)].lval);
+ (yyval.gen).index = (yyvsp[(3) - (6)].lval);
+ (yyval.gen).scale = (yyvsp[(5) - (6)].lval);
+ checkscale((yyval.gen).scale);
+ }
+ break;
+
+ case 96:
+#line 484 "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 97:
+#line 493 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_INDIR+(yyvsp[(2) - (3)].lval);
+ }
+ break;
+
+ case 98:
+#line 498 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_INDIR+D_SP;
+ }
+ break;
+
+ case 99:
+#line 503 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_INDIR+D_NONE;
+ (yyval.gen).index = (yyvsp[(2) - (5)].lval);
+ (yyval.gen).scale = (yyvsp[(4) - (5)].lval);
+ checkscale((yyval.gen).scale);
+ }
+ break;
+
+ case 100:
+#line 511 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_INDIR+(yyvsp[(2) - (8)].lval);
+ (yyval.gen).index = (yyvsp[(5) - (8)].lval);
+ (yyval.gen).scale = (yyvsp[(7) - (8)].lval);
+ checkscale((yyval.gen).scale);
+ }
+ break;
+
+ case 101:
+#line 521 "a.y"
+ {
+ (yyval.gen) = (yyvsp[(1) - (1)].gen);
+ }
+ break;
+
+ case 102:
+#line 525 "a.y"
+ {
+ (yyval.gen) = (yyvsp[(1) - (6)].gen);
+ (yyval.gen).index = (yyvsp[(3) - (6)].lval);
+ (yyval.gen).scale = (yyvsp[(5) - (6)].lval);
+ checkscale((yyval.gen).scale);
+ }
+ break;
+
+ case 103:
+#line 534 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = (yyvsp[(4) - (5)].lval);
+ (yyval.gen).sym = (yyvsp[(1) - (5)].sym);
+ (yyval.gen).offset = (yyvsp[(2) - (5)].lval);
+ }
+ break;
+
+ case 104:
+#line 541 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_STATIC;
+ (yyval.gen).sym = (yyvsp[(1) - (7)].sym);
+ (yyval.gen).offset = (yyvsp[(4) - (7)].lval);
+ }
+ break;
+
+ case 105:
+#line 549 "a.y"
+ {
+ (yyval.lval) = 0;
+ }
+ break;
+
+ case 106:
+#line 553 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(2) - (2)].lval);
+ }
+ break;
+
+ case 107:
+#line 557 "a.y"
+ {
+ (yyval.lval) = -(yyvsp[(2) - (2)].lval);
+ }
+ break;
+
+ case 109:
+#line 564 "a.y"
+ {
+ (yyval.lval) = D_AUTO;
+ }
+ break;
+
+ case 112:
+#line 572 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(1) - (1)].sym)->value;
+ }
+ break;
+
+ case 113:
+#line 576 "a.y"
+ {
+ (yyval.lval) = -(yyvsp[(2) - (2)].lval);
+ }
+ break;
+
+ case 114:
+#line 580 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(2) - (2)].lval);
+ }
+ break;
+
+ case 115:
+#line 584 "a.y"
+ {
+ (yyval.lval) = ~(yyvsp[(2) - (2)].lval);
+ }
+ break;
+
+ case 116:
+#line 588 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(2) - (3)].lval);
+ }
+ break;
+
+ case 117:
+#line 594 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(1) - (1)].lval) & 0xffffffffLL;
+ }
+ break;
+
+ case 118:
+#line 598 "a.y"
+ {
+ (yyval.lval) = -(yyvsp[(2) - (2)].lval) & 0xffffffffLL;
+ }
+ break;
+
+ case 119:
+#line 602 "a.y"
+ {
+ (yyval.lval) = ((yyvsp[(1) - (3)].lval) & 0xffffffffLL) +
+ (((yyvsp[(3) - (3)].lval) & 0xffffLL) << 32);
+ }
+ break;
+
+ case 120:
+#line 607 "a.y"
+ {
+ (yyval.lval) = (-(yyvsp[(2) - (4)].lval) & 0xffffffffLL) +
+ (((yyvsp[(4) - (4)].lval) & 0xffffLL) << 32);
+ }
+ break;
+
+ case 122:
+#line 615 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(1) - (3)].lval) + (yyvsp[(3) - (3)].lval);
+ }
+ break;
+
+ case 123:
+#line 619 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(1) - (3)].lval) - (yyvsp[(3) - (3)].lval);
+ }
+ break;
+
+ case 124:
+#line 623 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(1) - (3)].lval) * (yyvsp[(3) - (3)].lval);
+ }
+ break;
+
+ case 125:
+#line 627 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(1) - (3)].lval) / (yyvsp[(3) - (3)].lval);
+ }
+ break;
+
+ case 126:
+#line 631 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(1) - (3)].lval) % (yyvsp[(3) - (3)].lval);
+ }
+ break;
+
+ case 127:
+#line 635 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(1) - (4)].lval) << (yyvsp[(4) - (4)].lval);
+ }
+ break;
+
+ case 128:
+#line 639 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(1) - (4)].lval) >> (yyvsp[(4) - (4)].lval);
+ }
+ break;
+
+ case 129:
+#line 643 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(1) - (3)].lval) & (yyvsp[(3) - (3)].lval);
+ }
+ break;
+
+ case 130:
+#line 647 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(1) - (3)].lval) ^ (yyvsp[(3) - (3)].lval);
+ }
+ break;
+
+ case 131:
+#line 651 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(1) - (3)].lval) | (yyvsp[(3) - (3)].lval);
+ }
+ break;
+
+
+/* Line 1267 of yacc.c. */
+#line 2517 "y.tab.c"
+ default: break;
+ }
+ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++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. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if ! YYERROR_VERBOSE
+ yyerror (YY_("syntax error"));
+#else
+ {
+ 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;
+ }
+ }
+#endif
+ }
+
+
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse look-ahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ YYABORT;
+ }
+ else
+ {
+ yydestruct ("Error: discarding",
+ yytoken, &yylval);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse look-ahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+ /* Pacify compilers like GCC when the user code never invokes
+ YYERROR and the label yyerrorlab therefore never appears in user
+ code. */
+ if (/*CONSTCOND*/ 0)
+ goto yyerrorlab;
+
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYERROR. */
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (yyn != YYPACT_NINF)
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+
+ yydestruct ("Error: popping",
+ yystos[yystate], yyvsp);
+ YYPOPSTACK (1);
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ *++yyvsp = yylval;
+
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#ifndef yyoverflow
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here. |
+`-------------------------------------------------*/
+yyexhaustedlab:
+ yyerror (YY_("memory exhausted"));
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+ if (yychar != YYEOF && yychar != YYEMPTY)
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval);
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYABORT or YYACCEPT. */
+ YYPOPSTACK (yylen);
+ YY_STACK_PRINT (yyss, yyssp);
+ while (yyssp != yyss)
+ {
+ yydestruct ("Cleanup: popping",
+ yystos[*yyssp], yyvsp);
+ YYPOPSTACK (1);
+ }
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+#endif
+ /* Make sure YYID is used. */
+ return YYID (yyresult);
+}
+
+
+
diff --git a/src/cmd/6a/y.tab.h b/src/cmd/6a/y.tab.h
new file mode 100644
index 000000000..3cca766d3
--- /dev/null
+++ b/src/cmd/6a/y.tab.h
@@ -0,0 +1,135 @@
+/* A Bison parser, made by GNU Bison 2.3. */
+
+/* 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
+ 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.
+
+ 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. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ 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
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ LTYPE0 = 258,
+ LTYPE1 = 259,
+ LTYPE2 = 260,
+ LTYPE3 = 261,
+ LTYPE4 = 262,
+ LTYPEC = 263,
+ LTYPED = 264,
+ LTYPEN = 265,
+ 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
+ };
+#endif
+/* Tokens. */
+#define LTYPE0 258
+#define LTYPE1 259
+#define LTYPE2 260
+#define LTYPE3 261
+#define LTYPE4 262
+#define LTYPEC 263
+#define LTYPED 264
+#define LTYPEN 265
+#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
+
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 37 "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;
+# 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/Makefile b/src/cmd/6c/Makefile
index 484e16def..3f528d751 100644
--- a/src/cmd/6c/Makefile
+++ b/src/cmd/6c/Makefile
@@ -1,36 +1,5 @@
-# Copyright 2009 The Go Authors. All rights reserved.
+# 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 ../../Make.inc
-O:=$(HOST_O)
-
-TARG=6c
-
-HFILES=\
- gc.h\
- ../6l/6.out.h\
- ../cc/cc.h\
-
-OFILES=\
- cgen.$O\
- list.$O\
- sgen.$O\
- swt.$O\
- txt.$O\
- pgen.$O\
- pswt.$O\
- div.$O\
- mul.$O\
- reg.$O\
- peep.$O\
- machcap.$O\
- ../6l/enam.$O\
-
-LIB=\
- ../cc/cc.a\
-
-include ../../Make.ccmd
-
-%.$O: ../cc/%.c
- $(HOST_CC) $(HOST_CFLAGS) -c -I. -o $@ ../cc/$*.c
+include ../../Make.dist
diff --git a/src/cmd/6c/cgen.c b/src/cmd/6c/cgen.c
index 7f717dcbb..71822586c 100644
--- a/src/cmd/6c/cgen.c
+++ b/src/cmd/6c/cgen.c
@@ -1237,11 +1237,12 @@ void
boolgen(Node *n, int true, Node *nn)
{
int o;
- Prog *p1, *p2;
+ Prog *p1, *p2, *p3;
Node *l, *r, nod, nod1;
int32 curs;
if(debug['g']) {
+ print("boolgen %d\n", true);
prtree(nn, "boolgen lhs");
prtree(n, "boolgen");
}
@@ -1353,6 +1354,15 @@ boolgen(Node *n, int true, Node *nn)
case OLO:
case OLS:
o = n->op;
+ if(true && typefd[l->type->etype] && (o == OEQ || o == ONE)) {
+ // Cannot rewrite !(l == r) into l != r with float64; it breaks NaNs.
+ // Jump around instead.
+ boolgen(n, 0, Z);
+ p1 = p;
+ gbranch(OGOTO);
+ patch(p1, pc);
+ goto com;
+ }
if(true)
o = comrel[relindex(o)];
if(l->complex >= FNX && r->complex >= FNX) {
@@ -1367,6 +1377,10 @@ boolgen(Node *n, int true, Node *nn)
break;
}
if(immconst(l)) {
+ // NOTE: Reversing the comparison here is wrong
+ // for floating point ordering comparisons involving NaN,
+ // but we don't have any of those yet so we don't
+ // bother worrying about it.
o = invrel[relindex(o)];
/* bad, 13 is address of external that becomes constant */
if(r->addable < INDEXED || r->addable == 13) {
@@ -1388,10 +1402,11 @@ boolgen(Node *n, int true, Node *nn)
cgen(r, &nod1);
gopcode(o, l->type, &nod, &nod1);
regfree(&nod1);
- } else
+ } else {
gopcode(o, l->type, &nod, r);
+ }
regfree(&nod);
- goto com;
+ goto fixfloat;
}
regalloc(&nod, r, nn);
cgen(r, &nod);
@@ -1406,6 +1421,33 @@ boolgen(Node *n, int true, Node *nn)
} else
gopcode(o, l->type, l, &nod);
regfree(&nod);
+ fixfloat:
+ if(typefd[l->type->etype]) {
+ switch(o) {
+ case OEQ:
+ // Already emitted AJEQ; want AJEQ and AJPC.
+ p1 = p;
+ gbranch(OGOTO);
+ p2 = p;
+ patch(p1, pc);
+ gins(AJPC, Z, Z);
+ patch(p2, pc);
+ break;
+
+ case ONE:
+ // Already emitted AJNE; want AJNE or AJPS.
+ p1 = p;
+ gins(AJPS, Z, Z);
+ p2 = p;
+ gbranch(OGOTO);
+ p3 = p;
+ patch(p1, pc);
+ patch(p2, pc);
+ gbranch(OGOTO);
+ patch(p3, pc);
+ break;
+ }
+ }
com:
if(nn != Z) {
diff --git a/src/cmd/6c/gc.h b/src/cmd/6c/gc.h
index 0c23b115c..b0081abb5 100644
--- a/src/cmd/6c/gc.h
+++ b/src/cmd/6c/gc.h
@@ -299,7 +299,8 @@ void gpseudo(int, Sym*, Node*);
int swcmp(const void*, const void*);
void doswit(Node*);
void swit1(C1*, int, int32, Node*);
-void cas(void);
+void swit2(C1*, int, int32, Node*);
+void newcase(void);
void bitload(Node*, Node*, Node*, Node*, Node*);
void bitstore(Node*, Node*, Node*, Node*, Node*);
int32 outstring(char*, int32);
diff --git a/src/cmd/6c/swt.c b/src/cmd/6c/swt.c
index d7a917043..f16d0f78a 100644
--- a/src/cmd/6c/swt.c
+++ b/src/cmd/6c/swt.c
@@ -33,6 +33,21 @@
void
swit1(C1 *q, int nc, int32 def, Node *n)
{
+ Node nreg;
+
+ regalloc(&nreg, n, Z);
+ if(typev[n->type->etype])
+ nreg.type = types[TVLONG];
+ else
+ nreg.type = types[TLONG];
+ cgen(n, &nreg);
+ swit2(q, nc, def, &nreg);
+ regfree(&nreg);
+}
+
+void
+swit2(C1 *q, int nc, int32 def, Node *n)
+{
C1 *r;
int i;
Prog *sp;
@@ -58,12 +73,12 @@ swit1(C1 *q, int nc, int32 def, Node *n)
gbranch(OGOTO);
p->as = AJEQ;
patch(p, r->label);
- swit1(q, i, def, n);
+ swit2(q, i, def, n);
if(debug['W'])
print("case < %.8llux\n", r->val);
patch(sp, pc);
- swit1(r+1, nc-i-1, def, n);
+ swit2(r+1, nc-i-1, def, n);
}
void
@@ -331,17 +346,13 @@ outhist(Biobuf *b)
for(h = hist; h != H; h = h->link) {
p = h->name;
op = 0;
- /* on windows skip drive specifier in pathname */
if(systemtype(Windows) && p && p[1] == ':'){
- p += 2;
- c = *p;
- }
- if(p && p[0] != c && h->offset == 0 && pathname){
- /* on windows skip drive specifier in pathname */
+ c = p[2];
+ } else if(p && p[0] != c && h->offset == 0 && pathname){
if(systemtype(Windows) && pathname[1] == ':') {
op = p;
- p = pathname+2;
- c = *p;
+ p = pathname;
+ c = p[2];
} else if(pathname[0] == c){
op = p;
p = pathname;
diff --git a/src/cmd/6c/txt.c b/src/cmd/6c/txt.c
index 12fc5b498..2cb8c1585 100644
--- a/src/cmd/6c/txt.c
+++ b/src/cmd/6c/txt.c
@@ -158,7 +158,9 @@ 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;
@@ -411,7 +413,7 @@ regfree(Node *n)
if(n->op != OREGISTER && n->op != OINDREG)
goto err;
i = n->reg;
- if(i < 0 || i >= sizeof(reg))
+ if(i < 0 || i >= nelem(reg))
goto err;
if(reg[i] <= 0)
goto err;
diff --git a/src/cmd/6g/Makefile b/src/cmd/6g/Makefile
index 64fa15399..3f528d751 100644
--- a/src/cmd/6g/Makefile
+++ b/src/cmd/6g/Makefile
@@ -1,35 +1,5 @@
-# Copyright 2009 The Go Authors. All rights reserved.
+# 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 ../../Make.inc
-O:=$(HOST_O)
-
-TARG=6g
-
-HFILES=\
- ../gc/go.h\
- ../6l/6.out.h\
- gg.h\
- opt.h\
-
-OFILES=\
- ../6l/enam.$O\
- cgen.$O\
- cplx.$O\
- galign.$O\
- ggen.$O\
- gobj.$O\
- gsubr.$O\
- list.$O\
- peep.$O\
- pgen.$O\
- reg.$O\
-
-LIB=\
- ../gc/gc.a\
-
-include ../../Make.ccmd
-
-%.$O: ../gc/%.c
- $(HOST_CC) $(HOST_CFLAGS) -c -I. -o $@ ../gc/$*.c
+include ../../Make.dist
diff --git a/src/cmd/6g/cgen.c b/src/cmd/6g/cgen.c
index 24f88a416..00334e71b 100644
--- a/src/cmd/6g/cgen.c
+++ b/src/cmd/6g/cgen.c
@@ -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 <u.h>
+#include <libc.h>
#include "gg.h"
/*
@@ -123,6 +125,9 @@ cgen(Node *n, Node *res)
if(isslice(n->left->type))
n->addable = n->left->addable;
break;
+ case OITAB:
+ n->addable = n->left->addable;
+ break;
}
if(complexop(n, res)) {
@@ -257,6 +262,14 @@ cgen(Node *n, Node *res)
gmove(&n1, res);
regfree(&n1);
break;
+
+ case OITAB:
+ // interface table is first word of interface value
+ 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)) {
@@ -385,9 +398,9 @@ abop: // asymmetric binary
regalloc(&n2, nr->type, N);
cgen(nr, &n2);
} else {
- regalloc(&n2, nr->type, N);
+ regalloc(&n2, nr->type, res);
cgen(nr, &n2);
- regalloc(&n1, nl->type, res);
+ regalloc(&n1, nl->type, N);
cgen(nl, &n1);
}
gins(a, &n2, &n1);
@@ -715,6 +728,7 @@ bgen(Node *n, int true, Prog *to)
int et, a;
Node *nl, *nr, *l, *r;
Node n1, n2, tmp;
+ NodeList *ll;
Prog *p1, *p2;
if(debug['g']) {
@@ -727,9 +741,6 @@ bgen(Node *n, int true, Prog *to)
if(n->ninit != nil)
genlist(n->ninit);
- nl = n->left;
- nr = n->right;
-
if(n->type == T) {
convlit(&n, types[TBOOL]);
if(n->type == T)
@@ -742,7 +753,6 @@ bgen(Node *n, int true, Prog *to)
patch(gins(AEND, N, N), to);
goto ret;
}
- nl = N;
nr = N;
switch(n->op) {
@@ -836,7 +846,10 @@ bgen(Node *n, int true, Prog *to)
p1 = gbranch(AJMP, T);
p2 = gbranch(AJMP, T);
patch(p1, pc);
+ ll = n->ninit; // avoid re-genning ninit
+ n->ninit = nil;
bgen(n, 1, p2);
+ n->ninit = ll;
patch(gbranch(AJMP, T), to);
patch(p2, pc);
goto ret;
@@ -1021,28 +1034,35 @@ stkof(Node *n)
* memmove(&ns, &n, w);
*/
void
-sgen(Node *n, Node *ns, int32 w)
+sgen(Node *n, Node *ns, int64 w)
{
Node nodl, nodr, oldl, oldr, cx, oldcx, tmp;
int32 c, q, odst, osrc;
if(debug['g']) {
- print("\nsgen w=%d\n", w);
+ print("\nsgen w=%lld\n", w);
dump("r", n);
dump("res", ns);
}
- if(w == 0)
- return;
- if(n->ullman >= UINF && ns->ullman >= UINF) {
+
+ if(n->ullman >= UINF && ns->ullman >= UINF)
fatal("sgen UINF");
- }
if(w < 0)
- fatal("sgen copy %d", w);
+ fatal("sgen copy %lld", w);
if(w == 16)
if(componentgen(n, ns))
return;
+
+ if(w == 0) {
+ // evaluate side effects only
+ regalloc(&nodr, types[tptr], N);
+ agen(ns, &nodr);
+ agen(n, &nodr);
+ regfree(&nodr);
+ return;
+ }
// offset on the stack
osrc = stkof(n);
diff --git a/src/cmd/6g/galign.c b/src/cmd/6g/galign.c
index e366362b3..b03ac1ed6 100644
--- a/src/cmd/6g/galign.c
+++ b/src/cmd/6g/galign.c
@@ -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 <u.h>
+#include <libc.h>
#include "gg.h"
int thechar = '6';
diff --git a/src/cmd/6g/gg.h b/src/cmd/6g/gg.h
index 2493771a0..47a540082 100644
--- a/src/cmd/6g/gg.h
+++ b/src/cmd/6g/gg.h
@@ -2,16 +2,13 @@
// 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>
+#ifndef EXTERN
+#define EXTERN extern
+#endif
#include "../gc/go.h"
#include "../6l/6.out.h"
-#ifndef EXTERN
-#define EXTERN extern
-#endif
-
typedef struct Addr Addr;
struct Addr
@@ -44,22 +41,21 @@ struct Prog
void* reg; // pointer to containing Reg struct
};
-EXTERN Biobuf* bout;
+#define TEXTFLAG from.scale
+
EXTERN int32 dynloc;
EXTERN uchar reg[D_NONE];
EXTERN int32 pcloc; // instruction counter
EXTERN Strlit emptystring;
extern char* anames[];
-EXTERN Hist* hist;
EXTERN Prog zprog;
-EXTERN Node* curfn;
EXTERN Node* newproc;
EXTERN Node* deferproc;
EXTERN Node* deferreturn;
EXTERN Node* panicindex;
EXTERN Node* panicslice;
EXTERN Node* throwreturn;
-EXTERN vlong unmappedzero;
+extern vlong unmappedzero;
/*
* gen.c
@@ -91,7 +87,7 @@ void agen(Node*, Node*);
void igen(Node*, Node*, Node*);
vlong fieldoffset(Type*, Node*);
void bgen(Node*, int, Prog*);
-void sgen(Node*, Node*, int32);
+void sgen(Node*, Node*, int64);
void gmove(Node*, Node*);
Prog* gins(int, Node*, Node*);
int samaddr(Node*, Node*);
@@ -159,3 +155,5 @@ void listinit(void);
void zaddr(Biobuf*, Addr*, int, int);
+#pragma varargck type "D" Addr*
+#pragma varargck type "lD" Addr*
diff --git a/src/cmd/6g/ggen.c b/src/cmd/6g/ggen.c
index a5f278384..02e67d6d4 100644
--- a/src/cmd/6g/ggen.c
+++ b/src/cmd/6g/ggen.c
@@ -4,6 +4,8 @@
#undef EXTERN
#define EXTERN
+#include <u.h>
+#include <libc.h>
#include "gg.h"
#include "opt.h"
@@ -24,10 +26,10 @@ markautoused(Prog* p)
{
for (; p; p = p->link) {
if (p->from.type == D_AUTO && p->from.node)
- p->from.node->used++;
+ p->from.node->used = 1;
if (p->to.type == D_AUTO && p->to.node)
- p->to.node->used++;
+ p->to.node->used = 1;
}
}
@@ -446,8 +448,8 @@ dodiv(int op, Node *nl, Node *nr, Node *res)
{
int a, check;
Node n3, n4, n5;
- Type *t;
- Node ax, dx, oldax, olddx;
+ Type *t, *t0;
+ Node ax, dx, ax1, n31, oldax, olddx;
Prog *p1, *p2, *p3;
// Have to be careful about handling
@@ -459,6 +461,7 @@ dodiv(int op, Node *nl, Node *nr, Node *res)
// For int32 and int64, use explicit test.
// Could use int64 hw for int32.
t = nl->type;
+ t0 = t;
check = 0;
if(issigned[t->etype]) {
check = 1;
@@ -476,18 +479,28 @@ dodiv(int op, Node *nl, Node *nr, Node *res)
}
a = optoas(op, t);
- regalloc(&n3, t, N);
+ regalloc(&n3, t0, N);
if(nl->ullman >= nr->ullman) {
- savex(D_AX, &ax, &oldax, res, t);
+ savex(D_AX, &ax, &oldax, res, t0);
cgen(nl, &ax);
- regalloc(&ax, t, &ax); // mark ax live during cgen
+ regalloc(&ax, t0, &ax); // mark ax live during cgen
cgen(nr, &n3);
regfree(&ax);
} else {
cgen(nr, &n3);
- savex(D_AX, &ax, &oldax, res, t);
+ savex(D_AX, &ax, &oldax, res, t0);
cgen(nl, &ax);
}
+ if(t != t0) {
+ // Convert
+ ax1 = ax;
+ n31 = n3;
+ ax.type = t;
+ n3.type = t;
+ gmove(&ax1, &ax);
+ gmove(&n31, &n3);
+ }
+
p3 = P;
if(check) {
nodconst(&n4, t, -1);
diff --git a/src/cmd/6g/gobj.c b/src/cmd/6g/gobj.c
index ba8a4870e..80de2f750 100644
--- a/src/cmd/6g/gobj.c
+++ b/src/cmd/6g/gobj.c
@@ -28,6 +28,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#include <u.h>
+#include <libc.h>
#include "gg.h"
void
@@ -278,54 +280,6 @@ dumpfuncs(void)
}
}
-/* deferred DATA output */
-static Prog *strdat;
-static Prog *estrdat;
-static int gflag;
-static Prog *savepc;
-
-void
-data(void)
-{
- gflag = debug['g'];
- debug['g'] = 0;
-
- if(estrdat == nil) {
- strdat = mal(sizeof(*pc));
- clearp(strdat);
- estrdat = strdat;
- }
- if(savepc)
- fatal("data phase error");
- savepc = pc;
- pc = estrdat;
-}
-
-void
-text(void)
-{
- if(!savepc)
- fatal("text phase error");
- debug['g'] = gflag;
- estrdat = pc;
- pc = savepc;
- savepc = nil;
-}
-
-void
-dumpdata(void)
-{
- Prog *p;
-
- if(estrdat == nil)
- return;
- *pc = *strdat;
- if(gflag)
- for(p=pc; p!=estrdat; p=p->link)
- print("%P\n", p);
- pc = estrdat;
-}
-
int
dsname(Sym *s, int off, char *t, int n)
{
@@ -356,6 +310,7 @@ datastring(char *s, int len, Addr *a)
sym = stringsym(s, len);
a->type = D_EXTERN;
a->sym = sym;
+ a->node = sym->def;
a->offset = widthptr+4; // skip header
a->etype = TINT32;
}
@@ -372,6 +327,7 @@ datagostring(Strlit *sval, Addr *a)
sym = stringsym(sval->s, sval->len);
a->type = D_EXTERN;
a->sym = sym;
+ a->node = sym->def;
a->offset = 0; // header
a->etype = TINT32;
}
@@ -381,6 +337,16 @@ gdata(Node *nam, Node *nr, int wid)
{
Prog *p;
+ if(nr->op == OLITERAL) {
+ switch(nr->val.ctype) {
+ case CTCPLX:
+ gdatacomplex(nam, nr->val.u.cval);
+ return;
+ case CTSTR:
+ gdatastring(nam, nr->val.u.sval);
+ return;
+ }
+ }
p = gins(ADATA, nam, nr);
p->from.scale = wid;
}
@@ -537,8 +503,10 @@ genembedtramp(Type *rcvr, Type *method, Sym *newnam, int iface)
int c, d, o, mov, add, loaded;
Prog *p;
Type *f;
+
+ USED(iface);
- if(debug['r'])
+ if(0 && debug['r'])
print("genembedtramp %T %T %S\n", rcvr, method, newnam);
e = method->sym;
@@ -619,7 +587,6 @@ out:
// but 6l has a bug, and it can't handle
// JMP instructions too close to the top of
// a new function.
- p = pc;
gins(ANOP, N, N);
}
diff --git a/src/cmd/6g/gsubr.c b/src/cmd/6g/gsubr.c
index d0d6d0c96..ededcf673 100644
--- a/src/cmd/6g/gsubr.c
+++ b/src/cmd/6g/gsubr.c
@@ -28,6 +28,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#include <u.h>
+#include <libc.h>
#include "gg.h"
// TODO(rsc): Can make this bigger if we move
@@ -46,6 +48,10 @@ clearp(Prog *p)
pcloc++;
}
+static int ddumped;
+static Prog *dfirst;
+static Prog *dpc;
+
/*
* generate and return proc with p->as = as,
* linked into program. pc is next instruction.
@@ -55,10 +61,22 @@ prog(int as)
{
Prog *p;
- p = pc;
- pc = mal(sizeof(*pc));
-
- clearp(pc);
+ if(as == ADATA || as == AGLOBL) {
+ if(ddumped)
+ fatal("already dumped data");
+ if(dpc == nil) {
+ dpc = mal(sizeof(*dpc));
+ dfirst = dpc;
+ }
+ p = dpc;
+ dpc = mal(sizeof(*dpc));
+ p->link = dpc;
+ } else {
+ p = pc;
+ pc = mal(sizeof(*pc));
+ clearp(pc);
+ p->link = pc;
+ }
if(lineno == 0) {
if(debug['K'])
@@ -67,10 +85,21 @@ prog(int as)
p->as = as;
p->lineno = lineno;
- p->link = pc;
return p;
}
+void
+dumpdata(void)
+{
+ ddumped = 1;
+ if(dfirst == nil)
+ return;
+ newplist();
+ *pc = *dfirst;
+ pc = dpc;
+ clearp(pc);
+}
+
/*
* generate a branch.
* t is ignored.
@@ -79,6 +108,8 @@ Prog*
gbranch(int as, Type *t)
{
Prog *p;
+
+ USED(t);
p = prog(as);
p->to.type = D_BRANCH;
@@ -200,6 +231,8 @@ ggloblnod(Node *nam, int32 width)
p->to.offset = width;
if(nam->readonly)
p->from.scale = RODATA;
+ if(nam->type != T && !haspointers(nam->type))
+ p->from.scale |= NOPTR;
}
void
@@ -256,8 +289,6 @@ static int resvd[] =
D_CX, // for shift
D_DX, // for divide
D_SP, // for stack
- D_R14, // reserved for m
- D_R15, // reserved for u
};
void
@@ -309,6 +340,8 @@ anyregalloc(void)
return 0;
}
+static uintptr regpc[D_R15+1 - D_AX];
+
/*
* allocate register of type t, leave in n.
* if o != N, o is desired fixed register.
@@ -341,11 +374,15 @@ regalloc(Node *n, Type *t, Node *o)
goto out;
}
for(i=D_AX; i<=D_R15; i++)
- if(reg[i] == 0)
+ if(reg[i] == 0) {
+ regpc[i-D_AX] = (uintptr)getcallerpc(&n);
goto out;
+ }
- yyerror("out of fixed registers");
- goto err;
+ flusherrors();
+ for(i=0; i+D_AX<=D_R15; i++)
+ print("%d %p\n", i, regpc[i]);
+ fatal("out of fixed registers");
case TFLOAT32:
case TFLOAT64:
@@ -357,18 +394,14 @@ regalloc(Node *n, Type *t, Node *o)
for(i=D_X0; i<=D_X7; i++)
if(reg[i] == 0)
goto out;
- yyerror("out of floating registers");
- goto err;
+ fatal("out of floating registers");
case TCOMPLEX64:
case TCOMPLEX128:
tempname(n, t);
return;
}
- yyerror("regalloc: unknown type %T", t);
-
-err:
- nodreg(n, t, 0);
+ fatal("regalloc: unknown type %T", t);
return;
out:
@@ -388,11 +421,13 @@ regfree(Node *n)
i = n->val.u.reg;
if(i == D_SP)
return;
- if(i < 0 || i >= sizeof(reg))
+ if(i < 0 || i >= nelem(reg))
fatal("regfree: reg out of range");
if(reg[i] <= 0)
fatal("regfree: reg not allocated");
reg[i]--;
+ if(reg[i] == 0 && D_AX <= i && i <= D_R15)
+ regpc[i - D_AX] = 0;
}
/*
@@ -450,12 +485,20 @@ nodarg(Type *t, int fp)
n = nod(ONAME, N, N);
n->type = t->type;
n->sym = t->sym;
+
if(t->width == BADWIDTH)
fatal("nodarg: offset not computed for %T", t);
n->xoffset = t->width;
n->addable = 1;
+ n->orig = t->nname;
fp:
+ // Rewrite argument named _ to __,
+ // or else the assignment to _ will be
+ // discarded during code generation.
+ if(isblank(n))
+ n->sym = lookup("__");
+
switch(fp) {
case 0: // output arg
n->op = OINDREG;
@@ -522,6 +565,7 @@ int
ismem(Node *n)
{
switch(n->op) {
+ case OITAB:
case OLEN:
case OCAP:
case OINDREG:
@@ -1088,6 +1132,7 @@ naddr(Node *n, Addr *a, int canemitcode)
a->offset = n->xoffset;
a->sym = n->left->sym;
a->type = D_PARAM;
+ a->node = n->left->orig;
break;
case ONAME:
@@ -1098,9 +1143,11 @@ naddr(Node *n, Addr *a, int canemitcode)
a->width = n->type->width;
a->gotype = ngotype(n);
}
- a->pun = n->pun;
a->offset = n->xoffset;
a->sym = n->sym;
+ a->node = n->orig;
+ //if(a->node >= (Node*)&n)
+ // fatal("stack node");
if(a->sym == S)
a->sym = lookup(".noname");
if(n->method) {
@@ -1118,8 +1165,6 @@ naddr(Node *n, Addr *a, int canemitcode)
break;
case PAUTO:
a->type = D_AUTO;
- if (n->sym)
- a->node = n->orig;
break;
case PPARAM:
case PPARAMOUT:
@@ -1142,6 +1187,7 @@ naddr(Node *n, Addr *a, int canemitcode)
a->dval = mpgetflt(n->val.u.fval);
break;
case CTINT:
+ case CTRUNE:
a->sym = S;
a->type = D_CONST;
a->offset = mpgetfix(n->val.u.xval);
@@ -1176,6 +1222,17 @@ naddr(Node *n, Addr *a, int canemitcode)
break;
}
fatal("naddr: OADDR\n");
+
+ case OITAB:
+ // itable of interface value
+ naddr(n->left, a, canemitcode);
+ if(a->type == D_CONST && a->offset == 0)
+ break; // itab(nil)
+ a->etype = tptr;
+ a->width = widthptr;
+ if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero)
+ checkoffset(a, canemitcode);
+ break;
case OLEN:
// len of string or slice
@@ -1845,7 +1902,7 @@ sudoaddable(int as, Node *n, Addr *a)
switch(n->op) {
case OLITERAL:
- if(n->val.ctype != CTINT)
+ if(!isconst(n, CTINT))
break;
v = mpgetfix(n->val.u.xval);
if(v >= 32000 || v <= -32000)
diff --git a/src/cmd/6g/list.c b/src/cmd/6g/list.c
index c8077c97a..ad63f7d29 100644
--- a/src/cmd/6g/list.c
+++ b/src/cmd/6g/list.c
@@ -28,6 +28,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#include <u.h>
+#include <libc.h>
#include "gg.h"
static int sconsize;
@@ -131,7 +133,7 @@ Dconv(Fmt *fp)
if(fp->flags & FmtLong) {
d1 = a->offset & 0xffffffffLL;
d2 = (a->offset>>32) & 0xffffffffLL;
- snprint(str, sizeof(str), "$%ud-%ud", (ulong)d1, (ulong)d2);
+ snprint(str, sizeof(str), "$%lud-%lud", (ulong)d1, (ulong)d2);
break;
}
snprint(str, sizeof(str), "$%lld", a->offset);
diff --git a/src/cmd/6g/peep.c b/src/cmd/6g/peep.c
index 4432203f2..3710033b2 100644
--- a/src/cmd/6g/peep.c
+++ b/src/cmd/6g/peep.c
@@ -28,6 +28,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#include <u.h>
+#include <libc.h>
#include "gg.h"
#include "opt.h"
@@ -985,13 +987,12 @@ loop:
case 3: // set
if(p->as == p0->as)
if(p->from.type == p0->from.type)
- if(p->from.sym == p0->from.sym)
+ if(p->from.node == p0->from.node)
if(p->from.offset == p0->from.offset)
if(p->from.scale == p0->from.scale)
if(p->from.dval == p0->from.dval)
if(p->from.index == p0->from.index) {
excise(r);
- t++;
goto loop;
}
break;
diff --git a/src/cmd/6g/reg.c b/src/cmd/6g/reg.c
index 4d4263047..bed9f8da6 100644
--- a/src/cmd/6g/reg.c
+++ b/src/cmd/6g/reg.c
@@ -28,9 +28,9 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#include <u.h>
+#include <libc.h>
#include "gg.h"
-#undef EXTERN
-#define EXTERN
#include "opt.h"
#define NREGVAR 32 /* 16 general + 16 floating */
@@ -89,8 +89,8 @@ setoutvar(void)
ovar.b[z] |= bit.b[z];
t = structnext(&save);
}
-//if(bany(b))
-//print("ovars = %Q\n", &ovar);
+//if(bany(&ovar))
+//print("ovars = %Q\n", ovar);
}
static void
@@ -98,19 +98,19 @@ setaddrs(Bits bit)
{
int i, n;
Var *v;
- Sym *s;
+ Node *node;
while(bany(&bit)) {
// convert each bit to a variable
i = bnum(bit);
- s = var[i].sym;
+ node = var[i].node;
n = var[i].name;
bit.b[i/32] &= ~(1L<<(i%32));
// disable all pieces of that variable
for(i=0; i<nvar; i++) {
v = var+i;
- if(v->sym == s && v->name == n)
+ if(v->node == node && v->name == n)
v->addr = 2;
}
}
@@ -151,6 +151,8 @@ static char* regname[] = {
".X15",
};
+static void fixjmp(Prog*);
+
void
regopt(Prog *firstp)
{
@@ -166,6 +168,8 @@ regopt(Prog *firstp)
first = 0;
}
+ fixjmp(firstp);
+
// count instructions
nr = 0;
for(p=firstp; p!=P; p=p->link)
@@ -179,7 +183,6 @@ regopt(Prog *firstp)
r1 = R;
firstr = R;
lastr = R;
- nvar = 0;
/*
* control flow is more complicated in generated go code
@@ -189,7 +192,7 @@ regopt(Prog *firstp)
nvar = NREGVAR;
memset(var, 0, NREGVAR*sizeof var[0]);
for(i=0; i<NREGVAR; i++)
- var[i].sym = lookup(regname[i]);
+ var[i].node = newname(lookup(regname[i]));
regbits = RtoB(D_SP);
for(z=0; z<BITS; z++) {
@@ -801,9 +804,9 @@ brk:
if(ostats.ndelmov)
print(" %4d delmov\n", ostats.ndelmov);
if(ostats.nvar)
- print(" %4d delmov\n", ostats.nvar);
+ print(" %4d var\n", ostats.nvar);
if(ostats.naddr)
- print(" %4d delmov\n", ostats.naddr);
+ print(" %4d addr\n", ostats.naddr);
memset(&ostats, 0, sizeof(ostats));
}
@@ -832,12 +835,12 @@ addmove(Reg *r, int bn, int rn, int f)
v = var + bn;
a = &p1->to;
- a->sym = v->sym;
a->offset = v->offset;
a->etype = v->etype;
a->type = v->name;
a->gotype = v->gotype;
a->node = v->node;
+ a->sym = v->node->sym;
// need to clean this up with wptr and
// some of the defaults
@@ -933,7 +936,7 @@ mkvar(Reg *r, Adr *a)
uint32 regu;
int32 o;
Bits bit;
- Sym *s;
+ Node *node;
/*
* mark registers used
@@ -969,10 +972,14 @@ mkvar(Reg *r, Adr *a)
n = t;
break;
}
- s = a->sym;
- if(s == S)
+
+ node = a->node;
+ if(node == N || node->op != ONAME || node->orig == N)
goto none;
- if(s->name[0] == '.')
+ node = node->orig;
+ if(node->orig != node)
+ fatal("%D: bad node", a);
+ if(node->sym == S || node->sym->name[0] == '.')
goto none;
et = a->etype;
o = a->offset;
@@ -981,7 +988,7 @@ mkvar(Reg *r, Adr *a)
flag = 0;
for(i=0; i<nvar; i++) {
v = var+i;
- if(v->sym == s && v->name == n) {
+ if(v->node == node && v->name == n) {
if(v->offset == o)
if(v->etype == et)
if(v->width == w)
@@ -995,11 +1002,6 @@ mkvar(Reg *r, Adr *a)
}
}
}
- if(a->pun) {
-// print("disable pun %s\n", s->name);
- flag = 1;
-
- }
switch(et) {
case 0:
case TFUNC:
@@ -1007,25 +1009,24 @@ mkvar(Reg *r, Adr *a)
}
if(nvar >= NVAR) {
- if(debug['w'] > 1 && s)
- fatal("variable not optimized: %D", a);
+ if(debug['w'] > 1 && node != N)
+ fatal("variable not optimized: %#N", node);
goto none;
}
i = nvar;
nvar++;
v = var+i;
- v->sym = s;
v->offset = o;
v->name = n;
v->gotype = a->gotype;
v->etype = et;
v->width = w;
v->addr = flag; // funny punning
- v->node = a->node;
+ v->node = node;
if(debug['R'])
- print("bit=%2d et=%2d w=%d %S %D\n", i, et, w, s, a);
+ print("bit=%2d et=%2d w=%d %#N %D\n", i, et, w, node, a);
ostats.nvar++;
bit = blsh(i);
@@ -1084,6 +1085,13 @@ prop(Reg *r, Bits ref, Bits cal)
ref.b[z] = 0;
}
break;
+
+ default:
+ // Work around for issue 1304:
+ // flush modified globals before each instruction.
+ for(z=0; z<BITS; z++)
+ cal.b[z] |= externs.b[z];
+ break;
}
for(z=0; z<BITS; z++) {
ref.b[z] = (ref.b[z] & ~r1->set.b[z]) |
@@ -1220,10 +1228,12 @@ loopit(Reg *r, int32 nr)
r1 = rpo2r[i];
me = r1->rpo;
d = -1;
- if(r1->p1 != R && r1->p1->rpo < me)
+ // 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(r1->rpo < me)
+ if(rpo2r[r1->rpo] == r1 && r1->rpo < me)
d = rpolca(idom, d, r1->rpo);
idom[i] = d;
}
@@ -1628,7 +1638,7 @@ dumpone(Reg *r)
if(bany(&r->refahead))
print(" ra:%Q ", r->refahead);
if(bany(&r->calbehind))
- print("cb:%Q ", r->calbehind);
+ print(" cb:%Q ", r->calbehind);
if(bany(&r->calahead))
print(" ca:%Q ", r->calahead);
if(bany(&r->regdiff))
@@ -1688,3 +1698,123 @@ noreturn(Prog *p)
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.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.branch)
+ mark(p->to.branch);
+ if(p->as == AJMP || p->as == ARET || (p->as == ACALL && 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 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.branch && p->to.branch->as == AJMP) {
+ p->to.branch = chasejmp(p->to.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.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 262da02ab..849915954 100644
--- a/src/cmd/6l/6.out.h
+++ b/src/cmd/6l/6.out.h
@@ -34,6 +34,7 @@
#define DUPOK (1<<1)
#define NOSPLIT (1<<2)
#define RODATA (1<<3)
+#define NOPTR (1<<4)
/*
* amd64
@@ -121,7 +122,7 @@ enum as
AIRETW,
AJCC,
AJCS,
- AJCXZ,
+ AJCXZL,
AJEQ,
AJGE,
AJGT,
@@ -487,6 +488,7 @@ enum as
AIDIVQ,
AIMULQ,
AIRETQ,
+ AJCXZQ,
ALEAQ,
ALEAVEQ,
ALODSQ,
@@ -733,6 +735,7 @@ enum as
AMODE,
ACRC32B,
ACRC32Q,
+ AIMUL3Q,
ALAST
};
diff --git a/src/cmd/6l/Makefile b/src/cmd/6l/Makefile
index 8ed3e1411..3f528d751 100644
--- a/src/cmd/6l/Makefile
+++ b/src/cmd/6l/Makefile
@@ -1,48 +1,5 @@
-# Copyright 2009 The Go Authors. All rights reserved.
+# 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 ../../Make.inc
-O:=$(HOST_O)
-
-TARG=6l
-
-OFILES=\
- asm.$O\
- data.$O\
- dwarf.$O\
- elf.$O\
- enam.$O\
- go.$O\
- ldelf.$O\
- ldmacho.$O\
- ldpe.$O\
- lib.$O\
- list.$O\
- macho.$O\
- obj.$O\
- optab.$O\
- pass.$O\
- pe.$O\
- prof.$O\
- span.$O\
- symtab.$O\
-
-HFILES=\
- l.h\
- 6.out.h\
- ../ld/lib.h\
- ../ld/elf.h\
- ../ld/macho.h\
- ../ld/dwarf.h\
- ../ld/pe.h\
-
-include ../../Make.ccmd
-
-enam.c: 6.out.h
- sh mkenam
-
-CLEANFILES+=enam.c
-
-%.$O: ../ld/%.c
- $(HOST_CC) $(HOST_CFLAGS) -c -I. ../ld/$*.c
+include ../../Make.dist
diff --git a/src/cmd/6l/asm.c b/src/cmd/6l/asm.c
index 3a8223e65..ee31a05cd 100644
--- a/src/cmd/6l/asm.c
+++ b/src/cmd/6l/asm.c
@@ -37,13 +37,12 @@
#include "../ld/macho.h"
#include "../ld/pe.h"
-#define Dbufslop 100
-
#define PADDR(a) ((uint32)(a) & ~0x80000000)
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 zeroes[32];
@@ -95,6 +94,9 @@ enum {
ElfStrPlt,
ElfStrGnuVersion,
ElfStrGnuVersionR,
+ ElfStrNoteNetbsdIdent,
+ ElfStrNoPtrData,
+ ElfStrNoPtrBss,
NElfStr
};
@@ -112,6 +114,7 @@ needlib(char *name)
/* reuse hash code in symbol table */
p = smprint(".elfload.%s", name);
s = lookup(p, 0);
+ free(p);
if(s->type == 0) {
s->type = 100; // avoid SDATA, etc.
return 1;
@@ -558,7 +561,7 @@ doelf(void)
{
Sym *s, *shstrtab, *dynstr;
- if(HEADTYPE != Hlinux && HEADTYPE != Hfreebsd && HEADTYPE != Hopenbsd)
+ if(HEADTYPE != Hlinux && HEADTYPE != Hfreebsd && HEADTYPE != Hopenbsd && HEADTYPE != Hnetbsd)
return;
/* predefine strings we need for section headers */
@@ -568,8 +571,12 @@ doelf(void)
elfstr[ElfStrEmpty] = addstring(shstrtab, "");
elfstr[ElfStrText] = addstring(shstrtab, ".text");
+ elfstr[ElfStrNoPtrData] = addstring(shstrtab, ".noptrdata");
elfstr[ElfStrData] = addstring(shstrtab, ".data");
elfstr[ElfStrBss] = addstring(shstrtab, ".bss");
+ elfstr[ElfStrNoPtrBss] = addstring(shstrtab, ".noptrbss");
+ if(HEADTYPE == Hnetbsd)
+ elfstr[ElfStrNoteNetbsdIdent] = addstring(shstrtab, ".note.netbsd.ident");
addstring(shstrtab, ".elfdata");
addstring(shstrtab, ".rodata");
addstring(shstrtab, ".gosymtab");
@@ -649,7 +656,7 @@ doelf(void)
/* define dynamic elf table */
s = lookup(".dynamic", 0);
s->reachable = 1;
- s->type = SELFROSECT;
+ s->type = SELFSECT; // writable
/*
* .dynamic table
@@ -670,6 +677,8 @@ doelf(void)
elfwritedynentsymsize(s, DT_PLTRELSZ, lookup(".rela.plt", 0));
elfwritedynentsym(s, DT_JMPREL, lookup(".rela.plt", 0));
+ elfwritedynent(s, DT_DEBUG, 0);
+
// Do not write DT_NULL. elfdynhash will finish it.
}
}
@@ -701,7 +710,7 @@ asmb(void)
{
int32 magic;
int a, dynsym;
- vlong vl, startva, symo, machlink;
+ vlong vl, startva, symo, dwarfoff, machlink, resoff;
ElfEhdr *eh;
ElfPhdr *ph, *pph;
ElfShdr *sh;
@@ -736,8 +745,19 @@ asmb(void)
datblk(segdata.vaddr, segdata.filelen);
machlink = 0;
- if(HEADTYPE == Hdarwin)
+ if(HEADTYPE == Hdarwin) {
+ if(debug['v'])
+ Bprint(&bso, "%5.2f dwarf\n", cputime());
+
+ dwarfoff = rnd(HEADR+segtext.len, INITRND) + rnd(segdata.filelen, INITRND);
+ cseek(dwarfoff);
+
+ segdwarf.fileoff = cpos();
+ dwarfemitdebugsections();
+ segdwarf.filelen = cpos() - segdwarf.fileoff;
+
machlink = domacholink();
+ }
switch(HEADTYPE) {
default:
@@ -750,6 +770,7 @@ asmb(void)
break;
case Hlinux:
case Hfreebsd:
+ case Hnetbsd:
case Hopenbsd:
debug['8'] = 1; /* 64-bit addresses */
/* index of elf text section; needed by asmelfsym, double-checked below */
@@ -760,6 +781,8 @@ asmb(void)
if(elfverneed)
elftextsh += 2;
}
+ if(HEADTYPE == Hnetbsd)
+ elftextsh += 1;
break;
case Hwindows:
break;
@@ -785,6 +808,7 @@ asmb(void)
break;
case Hlinux:
case Hfreebsd:
+ case Hnetbsd:
case Hopenbsd:
symo = rnd(HEADR+segtext.len, INITRND)+segdata.filelen;
symo = rnd(symo, INITRND);
@@ -809,7 +833,6 @@ asmb(void)
dwarfemitdebugsections();
}
break;
- case Hdarwin:
case Hwindows:
if(debug['v'])
Bprint(&bso, "%5.2f dwarf\n", cputime());
@@ -855,11 +878,13 @@ asmb(void)
break;
case Hlinux:
case Hfreebsd:
+ case Hnetbsd:
case Hopenbsd:
/* elf amd-64 */
eh = getElfEhdr();
startva = INITTEXT - HEADR;
+ resoff = ELFRESERVE;
/* This null SHdr must appear before all others */
newElfShdr(elfstr[ElfStrEmpty]);
@@ -898,12 +923,15 @@ asmb(void)
case Hfreebsd:
interpreter = freebsddynld;
break;
+ case Hnetbsd:
+ interpreter = netbsddynld;
+ break;
case Hopenbsd:
interpreter = openbsddynld;
break;
}
}
- elfinterp(sh, startva, interpreter);
+ resoff -= elfinterp(sh, startva, resoff, interpreter);
ph = newElfPhdr();
ph->type = PT_INTERP;
@@ -911,11 +939,24 @@ asmb(void)
phsh(ph, sh);
}
+ if(HEADTYPE == Hnetbsd) {
+ sh = newElfShdr(elfstr[ElfStrNoteNetbsdIdent]);
+ sh->type = SHT_NOTE;
+ sh->flags = SHF_ALLOC;
+ sh->addralign = 4;
+ resoff -= elfnetbsdsig(sh, startva, resoff);
+
+ ph = newElfPhdr();
+ ph->type = PT_NOTE;
+ ph->flags = PF_R;
+ phsh(ph, sh);
+ }
+
elfphload(&segtext);
elfphload(&segdata);
/* Dynamic linking sections */
- if (!debug['d']) { /* -d suppresses dynamic loader format */
+ if(!debug['d']) { /* -d suppresses dynamic loader format */
/* S headers for dynamic linking */
sh = newElfShdr(elfstr[ElfStrGot]);
sh->type = SHT_PROGBITS;
@@ -1039,7 +1080,7 @@ asmb(void)
for(sect=segdata.sect; sect!=nil; sect=sect->next)
elfshbits(sect);
- if (!debug['s']) {
+ if(!debug['s']) {
sh = newElfShdr(elfstr[ElfStrSymtab]);
sh->type = SHT_SYMTAB;
sh->off = symo;
@@ -1064,6 +1105,8 @@ asmb(void)
eh->ident[EI_MAG3] = 'F';
if(HEADTYPE == Hfreebsd)
eh->ident[EI_OSABI] = ELFOSABI_FREEBSD;
+ else if(HEADTYPE == Hnetbsd)
+ eh->ident[EI_OSABI] = ELFOSABI_NETBSD;
else if(HEADTYPE == Hopenbsd)
eh->ident[EI_OSABI] = ELFOSABI_OPENBSD;
eh->ident[EI_CLASS] = ELFCLASS64;
@@ -1083,8 +1126,10 @@ asmb(void)
a += elfwritehdr();
a += elfwritephdrs();
a += elfwriteshdrs();
- cflush();
- if(a+elfwriteinterp() > ELFRESERVE)
+ a += elfwriteinterp(elfstr[ElfStrInterp]);
+ if(HEADTYPE == Hnetbsd)
+ a += elfwritenetbsdsig(elfstr[ElfStrNoteNetbsdIdent]);
+ if(a > ELFRESERVE)
diag("ELFRESERVE too small: %d > %d", a, ELFRESERVE);
break;
case Hwindows:
@@ -1125,7 +1170,10 @@ genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*))
switch(s->type&~SSUB) {
case SCONST:
case SRODATA:
+ case SSYMTAB:
+ case SPCLNTAB:
case SDATA:
+ case SNOPTRDATA:
case SELFROSECT:
case SMACHOGOT:
case STYPE:
@@ -1138,8 +1186,11 @@ genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*))
continue;
case SBSS:
+ case SNOPTRBSS:
if(!s->reachable)
continue;
+ if(s->np > 0)
+ diag("%s should not be bss (size=%d type=%d special=%d)", s->name, (int)s->np, s->type, s->special);
put(s, s->name, 'B', symaddr(s), s->size, s->version, s->gotype);
continue;
diff --git a/src/cmd/6l/doc.go b/src/cmd/6l/doc.go
index b8a6013d6..4d94b209b 100644
--- a/src/cmd/6l/doc.go
+++ b/src/cmd/6l/doc.go
@@ -4,50 +4,10 @@
/*
-6l is a modified version of the Plan 9 linker. The original is documented at
-
- http://plan9.bell-labs.com/magic/man2html/1/2l
-
-Its target architecture is the x86-64, referred to by these tools as amd64.
-It reads files in .6 format generated by 6g, 6c, and 6a and emits
-a binary called 6.out by default.
-
-Major changes include:
- - support for ELF and Mach-O binary files
- - support for segmented stacks (this feature is implemented here, not in the compilers).
-
-
-Original options are listed in the link above.
-
-Options new in this version:
-
--d
- Elide the dynamic linking header. With this option, the binary
- is statically linked and does not refer to dynld. Without this option
- (the default), the binary's contents are identical but it is loaded with dynld.
--e
- Emit an extra ELF-compatible symbol table useful with tools such as
- nm, gdb, and oprofile. This option makes the binary file considerably larger.
--Hdarwin
- Write Apple Mach-O binaries (default when $GOOS is darwin)
--Hlinux
- Write Linux ELF binaries (default when $GOOS is linux)
--Hfreebsd
- Write FreeBSD ELF binaries (default when $GOOS is freebsd)
--Hopenbsd
- Write OpenBSD ELF binaries (default when $GOOS is openbsd)
--Hwindows
- Write Windows PE32+ binaries (default when $GOOS is windows)
--I interpreter
- Set the ELF dynamic linker to use.
--L dir1 -L dir2
- Search for libraries (package files) in dir1, dir2, etc.
- The default is the single location $GOROOT/pkg/$GOOS_amd64.
--r dir1:dir2:...
- Set the dynamic linker search path when using ELF.
--V
- Print the linker version.
+6l is the linker for the x86-64.
+The $GOARCH for these tools is amd64.
+The flags are documented in ../ld/doc.go.
*/
package documentation
diff --git a/src/cmd/6l/l.h b/src/cmd/6l/l.h
index b291d5f3d..5f62239a1 100644
--- a/src/cmd/6l/l.h
+++ b/src/cmd/6l/l.h
@@ -133,6 +133,7 @@ struct Sym
int32 sig;
int32 plt;
int32 got;
+ int32 align; // if non-zero, required alignment in bytes
Sym* hash; // in hash table
Sym* allsym; // in all symbol list
Sym* next; // in text or data list
@@ -163,7 +164,7 @@ struct Optab
short as;
uchar* ytab;
uchar prefix;
- uchar op[20];
+ uchar op[22];
};
struct Movtab
{
diff --git a/src/cmd/6l/obj.c b/src/cmd/6l/obj.c
index a7ef58db4..692cab7b8 100644
--- a/src/cmd/6l/obj.c
+++ b/src/cmd/6l/obj.c
@@ -44,16 +44,17 @@ char* thestring = "amd64";
char* paramspace = "FP";
Header headers[] = {
- "plan9x32", Hplan9x32,
- "plan9", Hplan9x64,
- "elf", Helf,
- "darwin", Hdarwin,
- "linux", Hlinux,
- "freebsd", Hfreebsd,
- "openbsd", Hopenbsd,
- "windows", Hwindows,
- "windowsgui", Hwindows,
- 0, 0
+ "plan9x32", Hplan9x32,
+ "plan9", Hplan9x64,
+ "elf", Helf,
+ "darwin", Hdarwin,
+ "linux", Hlinux,
+ "freebsd", Hfreebsd,
+ "netbsd", Hnetbsd,
+ "openbsd", Hopenbsd,
+ "windows", Hwindows,
+ "windowsgui", Hwindows,
+ 0, 0
};
/*
@@ -63,6 +64,7 @@ Header headers[] = {
* -Hdarwin -Tx -Rx is apple MH-exec
* -Hlinux -Tx -Rx is linux elf-exec
* -Hfreebsd -Tx -Rx is FreeBSD elf-exec
+ * -Hnetbsd -Tx -Rx is NetBSD elf-exec
* -Hopenbsd -Tx -Rx is OpenBSD elf-exec
* -Hwindows -Tx -Rx is MS Windows PE32+
*
@@ -80,6 +82,7 @@ void
main(int argc, char *argv[])
{
int c;
+ char *name, *val;
Binit(&bso, 1, OWRITE);
listinit();
@@ -91,6 +94,7 @@ main(int argc, char *argv[])
INITDAT = -1;
INITRND = -1;
INITENTRY = 0;
+ nuxiinit();
ARGBEGIN {
default:
@@ -130,6 +134,11 @@ main(int argc, char *argv[])
case 'V':
print("%cl version %s\n", thechar, getgoversion());
errorexit();
+ case 'X':
+ name = EARGF(usage());
+ val = EARGF(usage());
+ addstrdata(name, val);
+ break;
} ARGEND
if(argc != 1)
@@ -183,7 +192,7 @@ main(int argc, char *argv[])
case Hdarwin: /* apple MACH */
/*
* OS X system constant - offset from 0(GS) to our TLS.
- * Explained in ../../libcgo/darwin_amd64.c.
+ * Explained in ../../pkg/runtime/cgo/gcc_darwin_amd64.c.
*/
tlsoffset = 0x8a0;
machoinit();
@@ -197,12 +206,13 @@ main(int argc, char *argv[])
break;
case Hlinux: /* elf64 executable */
case Hfreebsd: /* freebsd */
+ case Hnetbsd: /* netbsd */
case Hopenbsd: /* openbsd */
/*
* ELF uses TLS offset negative from FS.
* Translate 0(FS) and 8(FS) into -16(FS) and -8(FS).
- * Also known to ../../pkg/runtime/linux/amd64/sys.s
- * and ../../libcgo/linux_amd64.s.
+ * Also known to ../../pkg/runtime/sys_linux_amd64.s
+ * and ../../pkg/runtime/cgo/gcc_linux_amd64.c.
*/
tlsoffset = -16;
elfinit();
@@ -245,7 +255,6 @@ main(int argc, char *argv[])
zprg.mode = 64;
pcstr = "%.6llux ";
- nuxiinit();
histgen = 0;
pc = 0;
dtype = 4;
@@ -462,7 +471,7 @@ loop:
sig = 1729;
if(sig != 0){
if(s->sig != 0 && s->sig != sig)
- diag("incompatible type signatures"
+ diag("incompatible type signatures "
"%ux(%s) and %ux(%s) for %s",
s->sig, s->file, sig, pn, s->name);
s->sig = sig;
@@ -546,7 +555,7 @@ loop:
s->type = SBSS;
s->size = 0;
}
- if(s->type != SBSS && !s->dupok) {
+ if(s->type != SBSS && s->type != SNOPTRBSS && !s->dupok) {
diag("%s: redefinition: %s in %s",
pn, s->name, TNAME);
s->type = SBSS;
@@ -558,6 +567,8 @@ loop:
s->dupok = 1;
if(p->from.scale & RODATA)
s->type = SRODATA;
+ else if(p->from.scale & NOPTR)
+ s->type = SNOPTRBSS;
goto loop;
case ADATA:
@@ -589,6 +600,10 @@ loop:
case ATEXT:
s = p->from.sym;
if(s->text != nil) {
+ if(p->from.scale & DUPOK) {
+ skip = 1;
+ goto casdef;
+ }
diag("%s: %s: redefinition", pn, s->name);
return;
}
diff --git a/src/cmd/6l/optab.c b/src/cmd/6l/optab.c
index 36806ec4b..5746ded19 100644
--- a/src/cmd/6l/optab.c
+++ b/src/cmd/6l/optab.c
@@ -200,7 +200,8 @@ uchar ymovq[] =
Ymm, Ymr, Zm_r_xm, 1, // MMX MOVD
Ymr, Ymm, Zr_m_xm, 1, // MMX MOVD
Yxr, Ymr, Zm_r_xm_nr, 2, // MOVDQ2Q
- Yxr, Ym, Zr_m_xm_nr, 2, // MOVQ xmm store
+ Yxm, Yxr, Zm_r_xm_nr, 2, // MOVQ xmm1/m64 -> xmm2
+ Yxr, Yxm, Zr_m_xm_nr, 2, // MOVQ xmm1 -> xmm2/m64
Yml, Yxr, Zm_r_xm, 2, // MOVD xmm load
Yxr, Yml, Zr_m_xm, 2, // MOVD xmm store
Yiauto, Yrl, Zaut_r, 2, // built-in LEAQ
@@ -266,6 +267,11 @@ uchar yimul[] =
Yml, Yrl, Zm_r, 2,
0
};
+uchar yimul3[] =
+{
+ Yml, Yrl, Zibm_r, 1,
+ 0
+};
uchar ybyte[] =
{
Yi64, Ynone, Zbyte, 1,
@@ -771,6 +777,7 @@ Optab optab[] =
{ AIMULL, yimul, Px, 0xf7,(05),0x6b,0x69,Pm,0xaf },
{ AIMULQ, yimul, Pw, 0xf7,(05),0x6b,0x69,Pm,0xaf },
{ AIMULW, yimul, Pe, 0xf7,(05),0x6b,0x69,Pm,0xaf },
+ { AIMUL3Q, yimul3, Pw, 0x6b },
{ AINB, yin, Pb, 0xe4,0xec },
{ AINCB, yincb, Pb, 0xfe,(00) },
{ AINCL, yincl, Px, 0xff,(00) },
@@ -788,7 +795,8 @@ Optab optab[] =
{ AIRETW, ynone, Pe, 0xcf },
{ AJCC, yjcond, Px, 0x73,0x83,(00) },
{ AJCS, yjcond, Px, 0x72,0x82 },
- { AJCXZ, yloop, Px, 0xe3 },
+ { AJCXZL, yloop, Px, 0xe3 },
+ { AJCXZQ, yloop, Px, 0xe3 },
{ AJEQ, yjcond, Px, 0x74,0x84 },
{ AJGE, yjcond, Px, 0x7d,0x8d },
{ AJGT, yjcond, Px, 0x7f,0x8f },
@@ -861,7 +869,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,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 },
{ AMOVQOZX, ymrxr, Pf3, 0xd6,0x7e },
{ AMOVSB, ynone, Pb, 0xa4 },
{ AMOVSD, yxmov, Pf2, 0x10,0x11 },
@@ -977,7 +985,7 @@ Optab optab[] =
{ APSHUFW, ymshuf, Pm, 0x70 },
{ APSLLO, ypsdq, Pq, 0x73,(07) },
{ APSLLL, yps, Py, 0xf2, 0x72,(06), Pe,0xf2, Pe,0x72,(06) },
- { APSLLQ, yps, Py, 0xf3, 0x73,(06), Pe,0xf3, Pe,0x7e,(06) },
+ { APSLLQ, yps, Py, 0xf3, 0x73,(06), Pe,0xf3, Pe,0x73,(06) },
{ APSLLW, yps, Py, 0xf1, 0x71,(06), Pe,0xf1, Pe,0x71,(06) },
{ APSRAL, yps, Py, 0xe2, 0x72,(04), Pe,0xe2, Pe,0x72,(04) },
{ APSRAW, yps, Py, 0xe1, 0x71,(04), Pe,0xe1, Pe,0x71,(04) },
diff --git a/src/cmd/6l/pass.c b/src/cmd/6l/pass.c
index d9e0b2fc1..c9b477627 100644
--- a/src/cmd/6l/pass.c
+++ b/src/cmd/6l/pass.c
@@ -276,7 +276,7 @@ patch(void)
// Convert
// op n(GS), reg
// to
- // MOVL 0x58(GS), reg
+ // MOVL 0x28(GS), reg
// op n(reg), reg
// The purpose of this patch is to fix some accesses
// to extern register variables (TLS) on Windows, as
@@ -291,11 +291,11 @@ patch(void)
q->as = p->as;
p->as = AMOVQ;
p->from.type = D_INDIR+D_GS;
- p->from.offset = 0x58;
+ p->from.offset = 0x28;
}
}
if(HEADTYPE == Hlinux || HEADTYPE == Hfreebsd
- || HEADTYPE == Hopenbsd) {
+ || HEADTYPE == Hopenbsd || HEADTYPE == Hnetbsd) {
// ELF uses FS instead of GS.
if(p->from.type == D_INDIR+D_GS)
p->from.type = D_INDIR+D_FS;
@@ -421,18 +421,18 @@ dostkoff(void)
p = appendp(p); // load g into CX
p->as = AMOVQ;
if(HEADTYPE == Hlinux || HEADTYPE == Hfreebsd
- || HEADTYPE == Hopenbsd) // ELF uses FS
+ || HEADTYPE == Hopenbsd || HEADTYPE == Hnetbsd) // 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:0x58, %rcx
+ // movq %gs:0x28, %rcx
// movq (%rcx), %rcx
p->as = AMOVQ;
p->from.type = D_INDIR+D_GS;
- p->from.offset = 0x58;
+ p->from.offset = 0x28;
p->to.type = D_CX;
@@ -501,10 +501,17 @@ dostkoff(void)
q = p;
}
- /* 160 comes from 3 calls (3*8) 4 safes (4*8) and 104 guard */
+ // 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(autoffset+160+textarg > 4096)
- moreconst1 = (autoffset+160) & ~7LL;
+ if(StackTop + textarg + PtrSize + autoffset + PtrSize + StackLimit >= StackMin)
+ moreconst1 = autoffset;
moreconst2 = textarg;
// 4 varieties varieties (const1==0 cross const2==0)
diff --git a/src/cmd/6l/span.c b/src/cmd/6l/span.c
index 5d13ad44b..28eb38f40 100644
--- a/src/cmd/6l/span.c
+++ b/src/cmd/6l/span.c
@@ -88,7 +88,10 @@ span1(Sym *s)
loop++;
q->back ^= 2;
}
- s->p[q->pc+1] = v;
+ if(q->as == AJCXZL)
+ s->p[q->pc+2] = v;
+ else
+ s->p[q->pc+1] = v;
} else {
bp = s->p + q->pc + q->mark - 4;
*bp++ = v;
@@ -263,10 +266,6 @@ instinit(void)
ycover[Ym*Ymax + Ymm] = 1;
ycover[Ymr*Ymax + Ymm] = 1;
- ycover[Yax*Ymax + Yxm] = 1;
- ycover[Ycx*Ymax + Yxm] = 1;
- ycover[Yrx*Ymax + Yxm] = 1;
- ycover[Yrl*Ymax + Yxm] = 1;
ycover[Ym*Ymax + Yxm] = 1;
ycover[Yxr*Ymax + Yxm] = 1;
@@ -1439,10 +1438,11 @@ found:
case Zbr:
case Zjmp:
+ case Zloop:
// TODO: jump across functions needs reloc
q = p->pcond;
if(q == nil) {
- diag("jmp/branch without target");
+ diag("jmp/branch/loop without target");
errorexit();
}
if(q->as == ATEXT) {
@@ -1466,8 +1466,12 @@ found:
if(p->back & 1) {
v = q->pc - (p->pc + 2);
if(v >= -128) {
+ if(p->as == AJCXZL)
+ *andptr++ = 0x67;
*andptr++ = op;
*andptr++ = v;
+ } else if(t[2] == Zloop) {
+ diag("loop too far: %P", p);
} else {
v -= 5-2;
if(t[2] == Zbr) {
@@ -1487,8 +1491,12 @@ found:
p->forwd = q->comefrom;
q->comefrom = p;
if(p->back & 2) { // short
+ if(p->as == AJCXZL)
+ *andptr++ = 0x67;
*andptr++ = op;
*andptr++ = 0;
+ } else if(t[2] == Zloop) {
+ diag("loop too far: %P", p);
} else {
if(t[2] == Zbr)
*andptr++ = 0x0f;
@@ -1520,19 +1528,6 @@ found:
*/
break;
- case Zloop:
- q = p->pcond;
- if(q == nil) {
- diag("loop without target");
- errorexit();
- }
- v = q->pc - p->pc - 2;
- if(v < -128 && v > 127)
- diag("loop too far: %P", p);
- *andptr++ = op;
- *andptr++ = v;
- break;
-
case Zbyte:
v = vaddr(&p->from, &rel);
if(rel.siz != 0) {
diff --git a/src/cmd/8a/Makefile b/src/cmd/8a/Makefile
index 78d361dbd..27290ddd7 100644
--- a/src/cmd/8a/Makefile
+++ b/src/cmd/8a/Makefile
@@ -1,25 +1,10 @@
-# Copyright 2009 The Go Authors. All rights reserved.
+# 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 ../../Make.inc
-O:=$(HOST_O)
+include ../../Make.dist
-TARG=8a
+install: y.tab.h
-HFILES=\
- a.h\
- y.tab.h\
- ../8l/8.out.h\
-
-OFILES=\
- y.tab.$O\
- lex.$O\
- ../8l/enam.$O\
-
-YFILES=\
- a.y\
-
-include ../../Make.ccmd
-
-lex.$O: ../cc/macbody ../cc/lexbody
+y.tab.h: a.y
+ LANG=C LANGUAGE=en_US.UTF8 bison -d -v -y a.y
diff --git a/src/cmd/8a/a.y b/src/cmd/8a/a.y
index a8ac773da..f1881808f 100644
--- a/src/cmd/8a/a.y
+++ b/src/cmd/8a/a.y
@@ -210,6 +210,13 @@ spec3: /* JMP/CALL */
$$.from = nullgen;
$$.to = $1;
}
+| '*' nam
+ {
+ $$.from = nullgen;
+ $$.to = $2;
+ $$.to.index = $2.type;
+ $$.to.type = D_INDIR+D_ADDR;
+ }
spec4: /* NOP */
nonnon
@@ -385,6 +392,12 @@ imm:
$$.type = D_FCONST;
$$.dval = $3;
}
+| '$' '(' '-' LFCONST ')'
+ {
+ $$ = nullgen;
+ $$.type = D_FCONST;
+ $$.dval = -$4;
+ }
| '$' '-' LFCONST
{
$$ = nullgen;
diff --git a/src/cmd/8a/lex.c b/src/cmd/8a/lex.c
index e56460e4b..1cc6e591d 100644
--- a/src/cmd/8a/lex.c
+++ b/src/cmd/8a/lex.c
@@ -44,7 +44,11 @@ enum
int
systemtype(int sys)
{
+#ifdef _WIN32
+ return sys&Windows;
+#else
return sys&Plan9;
+#endif
}
int
@@ -309,8 +313,8 @@ struct
"IDIVL", LTYPE2, AIDIVL,
"IDIVW", LTYPE2, AIDIVW,
"IMULB", LTYPE2, AIMULB,
- "IMULL", LTYPE2, AIMULL,
- "IMULW", LTYPE2, AIMULW,
+ "IMULL", LTYPEI, AIMULL,
+ "IMULW", LTYPEI, AIMULW,
"INB", LTYPE0, AINB,
"INL", LTYPE0, AINL,
"INW", LTYPE0, AINW,
@@ -371,7 +375,8 @@ struct
"JG", LTYPER, AJGT, /* alternate */
"JNLE", LTYPER, AJGT, /* alternate */
- "JCXZ", LTYPER, AJCXZ,
+ "JCXZL", LTYPER, AJCXZL,
+ "JCXZW", LTYPER, AJCXZW,
"JMP", LTYPEC, AJMP,
"LAHF", LTYPE0, ALAHF,
"LARL", LTYPE3, ALARL,
@@ -440,6 +445,7 @@ struct
"RCRB", LTYPE3, ARCRB,
"RCRL", LTYPE3, ARCRL,
"RCRW", LTYPE3, ARCRW,
+ "RDTSC", LTYPE0, ARDTSC,
"REP", LTYPE0, AREP,
"REPN", LTYPE0, AREPN,
"RET", LTYPE0, ARET,
@@ -657,6 +663,10 @@ struct
"FXTRACT", LTYPE0, AFXTRACT,
"FYL2X", LTYPE0, AFYL2X,
"FYL2XP1", LTYPE0, AFYL2XP1,
+ "LFENCE", LTYPE0, ALFENCE,
+ "MFENCE", LTYPE0, AMFENCE,
+ "SFENCE", LTYPE0, ASFENCE,
+ "EMMS", LTYPE0, AEMMS,
0
};
@@ -911,17 +921,13 @@ outhist(void)
for(h = hist; h != H; h = h->link) {
p = h->name;
op = 0;
- /* on windows skip drive specifier in pathname */
if(systemtype(Windows) && p && p[1] == ':'){
- p += 2;
- c = *p;
- }
- if(p && p[0] != c && h->offset == 0 && pathname){
- /* on windows skip drive specifier in pathname */
+ c = p[2];
+ } else if(p && p[0] != c && h->offset == 0 && pathname){
if(systemtype(Windows) && pathname[1] == ':') {
op = p;
- p = pathname+2;
- c = *p;
+ p = pathname;
+ c = p[2];
} else if(pathname[0] == c){
op = p;
p = pathname;
diff --git a/src/cmd/8a/y.tab.c b/src/cmd/8a/y.tab.c
new file mode 100644
index 000000000..ccd3a2563
--- /dev/null
+++ b/src/cmd/8a/y.tab.c
@@ -0,0 +1,2852 @@
+
+/* A Bison parser, made by GNU Bison 2.4.1. */
+
+/* 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
+ it under the terms of the GNU General Public License as published by
+ 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, 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
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ 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. */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+ simplifying the original so-called "semantic" parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Bison version. */
+#define YYBISON_VERSION "2.4.1"
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* 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 189 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"
+
+
+/* Line 189 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 0
+#endif
+
+/* Enabling the token table. */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ LTYPE0 = 258,
+ LTYPE1 = 259,
+ LTYPE2 = 260,
+ LTYPE3 = 261,
+ LTYPE4 = 262,
+ LTYPEC = 263,
+ LTYPED = 264,
+ LTYPEN = 265,
+ LTYPER = 266,
+ LTYPET = 267,
+ LTYPES = 268,
+ LTYPEM = 269,
+ LTYPEI = 270,
+ LTYPEG = 271,
+ LCONST = 272,
+ LFP = 273,
+ LPC = 274,
+ LSB = 275,
+ LBREG = 276,
+ LLREG = 277,
+ LSREG = 278,
+ LFREG = 279,
+ LFCONST = 280,
+ LSCONST = 281,
+ LSP = 282,
+ LNAME = 283,
+ LLAB = 284,
+ LVAR = 285
+ };
+#endif
+/* Tokens. */
+#define LTYPE0 258
+#define LTYPE1 259
+#define LTYPE2 260
+#define LTYPE3 261
+#define LTYPE4 262
+#define LTYPEC 263
+#define LTYPED 264
+#define LTYPEN 265
+#define LTYPER 266
+#define LTYPET 267
+#define LTYPES 268
+#define LTYPEM 269
+#define LTYPEI 270
+#define LTYPEG 271
+#define LCONST 272
+#define LFP 273
+#define LPC 274
+#define LSB 275
+#define LBREG 276
+#define LLREG 277
+#define LSREG 278
+#define LFREG 279
+#define LFCONST 280
+#define LSCONST 281
+#define LSP 282
+#define LNAME 283
+#define LLAB 284
+#define LVAR 285
+
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+{
+
+/* Line 214 of yacc.c */
+#line 37 "a.y"
+
+ Sym *sym;
+ int32 lval;
+ struct {
+ int32 v1;
+ int32 v2;
+ } con2;
+ double dval;
+ char sval[8];
+ Gen gen;
+ Gen2 gen2;
+
+
+
+/* Line 214 of yacc.c */
+#line 192 "y.tab.c"
+} YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+
+/* Copy the second part of user declarations. */
+
+
+/* Line 264 of yacc.c */
+#line 204 "y.tab.c"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+# define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+# define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# else
+# define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if YYENABLE_NLS
+# if ENABLE_NLS
+# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+# define YY_(msgid) dgettext ("bison-runtime", msgid)
+# endif
+# endif
+# ifndef YY_
+# define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E. */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions. */
+#ifndef lint
+# define YYID(n) (n)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int yyi)
+#else
+static int
+YYID (yyi)
+ int yyi;
+#endif
+{
+ return yyi;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# elif defined __BUILTIN_VA_ARG_INCR
+# include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+# elif defined _AIX
+# define YYSTACK_ALLOC __alloca
+# elif defined _MSC_VER
+# include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+# define alloca _alloca
+# else
+# define YYSTACK_ALLOC alloca
+# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (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
+# endif
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's `empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+# ifndef YYSTACK_ALLOC_MAXIMUM
+ /* The OS might guarantee only one guard page at the bottom of the stack,
+ and a page size can be as small as 4096 bytes. So we cannot safely
+ invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
+ to allow for a few compiler-allocated temporary stack slots. */
+# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+# endif
+# else
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# ifndef YYSTACK_ALLOC_MAXIMUM
+# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+# endif
+# if (defined __cplusplus && ! defined _STDLIB_H \
+ && ! ((defined YYMALLOC || defined malloc) \
+ && (defined YYFREE || defined free)))
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef _STDLIB_H
+# define _STDLIB_H 1
+# endif
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# if ! defined malloc && ! defined _STDLIB_H && (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__ \
+ || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+ && (! defined __cplusplus \
+ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ 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)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((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
+
+/* 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
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 2
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 483
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 49
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 37
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 124
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 244
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 285
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
+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, 47, 12, 5, 2,
+ 45, 46, 10, 8, 44, 9, 2, 11, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 41, 42,
+ 6, 43, 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, 48, 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, 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, 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, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 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
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+ YYRHS. */
+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, 80,
+ 82, 86, 90, 93, 95, 98, 100, 103, 105, 111,
+ 115, 121, 124, 126, 129, 131, 133, 137, 143, 147,
+ 153, 156, 158, 162, 166, 172, 174, 176, 178, 180,
+ 183, 186, 188, 190, 192, 194, 196, 201, 204, 207,
+ 209, 211, 213, 215, 217, 220, 223, 226, 229, 234,
+ 240, 244, 247, 249, 252, 256, 261, 263, 265, 267,
+ 272, 277, 284, 294, 298, 302, 307, 313, 322, 324,
+ 331, 337, 345, 346, 349, 352, 354, 356, 358, 360,
+ 362, 365, 368, 371, 375, 377, 381, 385, 389, 393,
+ 397, 402, 407, 411, 415
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yytype_int8 yyrhs[] =
+{
+ 50, 0, -1, -1, -1, 50, 51, 52, -1, -1,
+ 39, 41, 53, 52, -1, -1, 38, 41, 54, 52,
+ -1, 42, -1, 55, 42, -1, 1, 42, -1, 38,
+ 43, 85, -1, 40, 43, 85, -1, 13, 56, -1,
+ 14, 60, -1, 15, 59, -1, 16, 57, -1, 17,
+ 58, -1, 21, 61, -1, 19, 62, -1, 22, 63,
+ -1, 18, 64, -1, 20, 65, -1, 23, 66, -1,
+ 24, 67, -1, 25, 68, -1, 26, 69, -1, -1,
+ 44, -1, 72, 44, 70, -1, 70, 44, 72, -1,
+ 72, 44, -1, 72, -1, 44, 70, -1, 70, -1,
+ 44, 73, -1, 73, -1, 81, 11, 84, 44, 75,
+ -1, 78, 44, 76, -1, 78, 44, 84, 44, 76,
+ -1, 44, 71, -1, 71, -1, 10, 81, -1, 56,
+ -1, 60, -1, 72, 44, 70, -1, 72, 44, 70,
+ 41, 32, -1, 72, 44, 70, -1, 72, 44, 70,
+ 41, 33, -1, 72, 44, -1, 72, -1, 72, 44,
+ 70, -1, 78, 44, 75, -1, 78, 44, 84, 44,
+ 75, -1, 74, -1, 78, -1, 73, -1, 80, -1,
+ 10, 74, -1, 10, 79, -1, 74, -1, 79, -1,
+ 75, -1, 70, -1, 75, -1, 84, 45, 29, 46,
+ -1, 38, 82, -1, 39, 82, -1, 31, -1, 34,
+ -1, 32, -1, 37, -1, 33, -1, 47, 84, -1,
+ 47, 81, -1, 47, 36, -1, 47, 35, -1, 47,
+ 45, 35, 46, -1, 47, 45, 9, 35, 46, -1,
+ 47, 9, 35, -1, 47, 77, -1, 27, -1, 9,
+ 27, -1, 27, 9, 27, -1, 9, 27, 9, 27,
+ -1, 79, -1, 80, -1, 84, -1, 84, 45, 32,
+ 46, -1, 84, 45, 37, 46, -1, 84, 45, 32,
+ 10, 84, 46, -1, 84, 45, 32, 46, 45, 32,
+ 10, 84, 46, -1, 45, 32, 46, -1, 45, 37,
+ 46, -1, 84, 45, 33, 46, -1, 45, 32, 10,
+ 84, 46, -1, 45, 32, 46, 45, 32, 10, 84,
+ 46, -1, 81, -1, 81, 45, 32, 10, 84, 46,
+ -1, 38, 82, 45, 83, 46, -1, 38, 6, 7,
+ 82, 45, 30, 46, -1, -1, 8, 84, -1, 9,
+ 84, -1, 30, -1, 37, -1, 28, -1, 27, -1,
+ 40, -1, 9, 84, -1, 8, 84, -1, 48, 84,
+ -1, 45, 85, 46, -1, 84, -1, 85, 8, 85,
+ -1, 85, 9, 85, -1, 85, 10, 85, -1, 85,
+ 11, 85, -1, 85, 12, 85, -1, 85, 6, 6,
+ 85, -1, 85, 7, 7, 85, -1, 85, 5, 85,
+ -1, 85, 4, 85, -1, 85, 3, 85, -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, 121, 125,
+ 132, 139, 146, 151, 158, 163, 170, 175, 182, 190,
+ 195, 203, 208, 213, 222, 223, 226, 231, 241, 246,
+ 256, 261, 266, 273, 278, 286, 287, 290, 291, 292,
+ 296, 300, 301, 302, 305, 306, 309, 315, 324, 333,
+ 338, 343, 348, 353, 360, 366, 377, 383, 389, 395,
+ 401, 409, 418, 423, 428, 433, 440, 441, 444, 450,
+ 456, 462, 471, 480, 485, 490, 496, 504, 514, 518,
+ 527, 534, 543, 546, 550, 556, 557, 561, 564, 565,
+ 569, 573, 577, 581, 587, 588, 592, 596, 600, 604,
+ 608, 612, 616, 620, 624
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "'|'", "'^'", "'&'", "'<'", "'>'", "'+'",
+ "'-'", "'*'", "'/'", "'%'", "LTYPE0", "LTYPE1", "LTYPE2", "LTYPE3",
+ "LTYPE4", "LTYPEC", "LTYPED", "LTYPEN", "LTYPER", "LTYPET", "LTYPES",
+ "LTYPEM", "LTYPEI", "LTYPEG", "LCONST", "LFP", "LPC", "LSB", "LBREG",
+ "LLREG", "LSREG", "LFREG", "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", "rem", "rom",
+ "rim", "rel", "reg", "imm", "imm2", "con2", "mem", "omem", "nmem", "nam",
+ "offset", "pointer", "con", "expr", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+ token YYLEX-NUM. */
+static const yytype_uint16 yytoknum[] =
+{
+ 0, 256, 257, 124, 94, 38, 60, 62, 43, 45,
+ 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, 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, 49, 50, 51, 50, 53, 52, 54, 52, 52,
+ 52, 52, 55, 55, 55, 55, 55, 55, 55, 55,
+ 55, 55, 55, 55, 55, 55, 55, 55, 56, 56,
+ 57, 58, 59, 59, 60, 60, 61, 61, 62, 63,
+ 63, 64, 64, 64, 65, 65, 66, 66, 67, 67,
+ 68, 68, 68, 69, 69, 70, 70, 71, 71, 71,
+ 71, 71, 71, 71, 72, 72, 73, 73, 73, 74,
+ 74, 74, 74, 74, 75, 75, 75, 75, 75, 75,
+ 75, 76, 77, 77, 77, 77, 78, 78, 79, 79,
+ 79, 79, 79, 79, 79, 79, 79, 79, 80, 80,
+ 81, 81, 82, 82, 82, 83, 83, 83, 84, 84,
+ 84, 84, 84, 84, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85, 85
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+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, 0, 1,
+ 3, 3, 2, 1, 2, 1, 2, 1, 5, 3,
+ 5, 2, 1, 2, 1, 1, 3, 5, 3, 5,
+ 2, 1, 3, 3, 5, 1, 1, 1, 1, 2,
+ 2, 1, 1, 1, 1, 1, 4, 2, 2, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 4, 5,
+ 3, 2, 1, 2, 3, 4, 1, 1, 1, 4,
+ 4, 6, 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
+ means the default is an error. */
+static const yytype_uint8 yydefact[] =
+{
+ 2, 3, 1, 0, 0, 28, 0, 0, 0, 0,
+ 0, 0, 28, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 9, 4, 0, 11, 29, 14, 0, 0,
+ 108, 69, 71, 73, 70, 72, 102, 109, 0, 0,
+ 0, 15, 35, 55, 56, 86, 87, 98, 88, 0,
+ 16, 64, 33, 65, 17, 0, 18, 0, 0, 102,
+ 102, 0, 22, 42, 57, 61, 63, 62, 58, 88,
+ 20, 0, 29, 44, 45, 23, 102, 0, 0, 19,
+ 37, 0, 21, 0, 24, 0, 25, 0, 26, 51,
+ 27, 0, 7, 0, 5, 0, 10, 111, 110, 0,
+ 0, 0, 0, 34, 0, 0, 114, 0, 112, 0,
+ 0, 0, 77, 76, 0, 75, 74, 32, 0, 0,
+ 59, 60, 43, 67, 68, 0, 41, 0, 0, 67,
+ 36, 0, 0, 0, 0, 50, 0, 0, 12, 0,
+ 13, 102, 103, 104, 0, 0, 93, 94, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 113, 0,
+ 0, 0, 0, 80, 0, 0, 30, 31, 0, 0,
+ 0, 39, 0, 46, 48, 52, 53, 0, 8, 6,
+ 0, 107, 105, 106, 0, 0, 0, 124, 123, 122,
+ 0, 0, 115, 116, 117, 118, 119, 0, 0, 89,
+ 95, 90, 0, 78, 66, 0, 0, 82, 81, 0,
+ 0, 0, 0, 0, 100, 96, 0, 120, 121, 0,
+ 0, 0, 79, 38, 83, 0, 40, 47, 49, 54,
+ 0, 0, 99, 91, 0, 0, 84, 101, 0, 0,
+ 85, 97, 0, 92
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const yytype_int16 yydefgoto[] =
+{
+ -1, 1, 3, 23, 139, 137, 24, 27, 54, 56,
+ 50, 41, 79, 70, 82, 62, 75, 84, 86, 88,
+ 90, 51, 63, 52, 64, 43, 53, 171, 208, 44,
+ 45, 46, 47, 102, 184, 48, 107
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -107
+static const yytype_int16 yypact[] =
+{
+ -107, 9, -107, 151, -28, -19, 235, 255, 255, 302,
+ 175, 6, 282, 34, 363, 255, 255, 255, 363, -3,
+ -6, 5, -107, -107, 4, -107, -107, -107, 373, 373,
+ -107, -107, -107, -107, -107, -107, 128, -107, 302, 343,
+ 373, -107, -107, -107, -107, -107, -107, 17, 19, 115,
+ -107, -107, 15, -107, -107, 27, -107, 42, 302, 128,
+ 72, 208, -107, -107, -107, -107, -107, -107, -107, 52,
+ -107, 44, 302, -107, -107, -107, 72, 359, 373, -107,
+ -107, 55, -107, 71, -107, 76, -107, 81, -107, 84,
+ -107, 97, -107, 373, -107, 373, -107, -107, -107, 142,
+ 373, 373, 114, -107, 1, 116, -107, 102, -107, 129,
+ 66, 385, -107, -107, 387, -107, -107, -107, 302, 255,
+ -107, -107, -107, 114, -107, 329, -107, 168, 373, -107,
+ -107, 149, 18, 302, 302, 302, 397, 151, 447, 151,
+ 447, 72, -107, -107, 47, 373, 134, -107, 373, 373,
+ 373, 176, 179, 373, 373, 373, 373, 373, -107, 182,
+ 3, 148, 152, -107, 401, 153, -107, -107, 158, 166,
+ 14, -107, 167, 154, 189, -107, -107, 187, -107, -107,
+ 188, -107, -107, -107, 186, 190, 202, 456, 464, 471,
+ 373, 373, 146, 146, -107, -107, -107, 373, 373, 192,
+ -107, -107, 203, -107, -107, 191, 223, 242, -107, 205,
+ 222, 224, 191, 228, -107, -107, 249, 216, 216, 214,
+ 215, 233, -107, -107, 261, 244, -107, -107, -107, -107,
+ 230, 373, -107, -107, 264, 250, -107, -107, 232, 373,
+ -107, -107, 238, -107
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const yytype_int16 yypgoto[] =
+{
+ -107, -107, -107, -106, -107, -107, -107, 269, -107, -107,
+ -107, 273, -107, -107, -107, -107, -107, -107, -107, -107,
+ -107, -2, 236, 0, -1, -8, -9, 85, -107, 10,
+ -4, -5, 11, -39, -107, -10, -61
+};
+
+/* 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 -1
+static const yytype_uint8 yytable[] =
+{
+ 69, 66, 65, 81, 42, 68, 67, 57, 55, 2,
+ 42, 145, 80, 198, 25, 85, 87, 89, 97, 98,
+ 123, 124, 71, 206, 83, 26, 28, 29, 91, 106,
+ 108, 178, 138, 179, 140, 94, 103, 129, 92, 116,
+ 93, 207, 28, 29, 36, 30, 96, 146, 95, 199,
+ 120, 69, 66, 65, 121, 128, 68, 67, 37, 117,
+ 115, 30, 109, 78, 110, 170, 40, 81, 106, 122,
+ 103, 118, 76, 60, 37, 181, 130, 182, 77, 78,
+ 100, 101, 40, 106, 183, 106, 119, 187, 188, 189,
+ 142, 143, 192, 193, 194, 195, 196, 127, 160, 161,
+ 131, 98, 180, 162, 106, 148, 149, 150, 151, 152,
+ 153, 154, 155, 156, 157, 132, 166, 120, 169, 167,
+ 133, 121, 172, 28, 111, 134, 177, 176, 135, 217,
+ 218, 173, 174, 175, 99, 185, 100, 101, 106, 106,
+ 106, 136, 30, 106, 106, 106, 106, 106, 158, 141,
+ 112, 113, 4, 36, 98, 37, 155, 156, 157, 144,
+ 114, 159, 147, 40, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 168, 186,
+ 106, 106, 190, 28, 29, 58, 191, 219, 220, 19,
+ 20, 21, 197, 22, 200, 210, 223, 168, 201, 203,
+ 160, 161, 30, 229, 204, 162, 31, 32, 33, 34,
+ 205, 209, 35, 59, 60, 37, 28, 29, 125, 61,
+ 39, 238, 49, 40, 153, 154, 155, 156, 157, 242,
+ 211, 212, 214, 213, 216, 30, 215, 221, 49, 31,
+ 32, 33, 34, 28, 29, 35, 59, 60, 37, 222,
+ 224, 225, 170, 39, 227, 49, 40, 228, 230, 231,
+ 232, 233, 30, 28, 29, 234, 31, 32, 33, 34,
+ 235, 236, 35, 36, 239, 37, 237, 240, 241, 38,
+ 39, 73, 30, 40, 243, 74, 31, 32, 33, 34,
+ 28, 29, 35, 36, 226, 37, 0, 126, 0, 0,
+ 39, 0, 49, 40, 0, 0, 0, 0, 0, 30,
+ 28, 29, 0, 31, 32, 33, 34, 0, 0, 35,
+ 36, 0, 37, 0, 0, 0, 72, 39, 0, 30,
+ 40, 0, 0, 31, 32, 33, 34, 28, 29, 35,
+ 36, 0, 37, 0, 0, 0, 0, 39, 0, 0,
+ 40, 28, 29, 0, 0, 0, 30, 0, 0, 0,
+ 31, 32, 33, 34, 0, 0, 35, 28, 29, 37,
+ 30, 28, 29, 0, 39, 104, 0, 40, 0, 0,
+ 105, 28, 29, 37, 0, 0, 30, 0, 78, 0,
+ 30, 40, 0, 28, 29, 28, 164, 76, 60, 37,
+ 30, 36, 0, 37, 78, 28, 29, 40, 39, 28,
+ 29, 40, 30, 37, 30, 0, 0, 0, 78, 0,
+ 163, 40, 165, 0, 30, 37, 0, 37, 30, 0,
+ 78, 0, 78, 40, 0, 40, 202, 37, 0, 0,
+ 0, 37, 78, 0, 49, 40, 78, 0, 0, 40,
+ 148, 149, 150, 151, 152, 153, 154, 155, 156, 157,
+ 149, 150, 151, 152, 153, 154, 155, 156, 157, 150,
+ 151, 152, 153, 154, 155, 156, 157, 151, 152, 153,
+ 154, 155, 156, 157
+};
+
+static const yytype_int16 yycheck[] =
+{
+ 10, 10, 10, 13, 6, 10, 10, 9, 8, 0,
+ 12, 10, 13, 10, 42, 15, 16, 17, 28, 29,
+ 59, 60, 11, 9, 14, 44, 8, 9, 18, 39,
+ 40, 137, 93, 139, 95, 41, 38, 76, 41, 49,
+ 43, 27, 8, 9, 38, 27, 42, 46, 43, 46,
+ 58, 61, 61, 61, 58, 11, 61, 61, 40, 44,
+ 49, 27, 45, 45, 45, 47, 48, 77, 78, 58,
+ 72, 44, 38, 39, 40, 28, 77, 30, 44, 45,
+ 8, 9, 48, 93, 37, 95, 44, 148, 149, 150,
+ 100, 101, 153, 154, 155, 156, 157, 45, 32, 33,
+ 45, 111, 141, 37, 114, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 44, 118, 125, 128, 119,
+ 44, 125, 132, 8, 9, 44, 136, 136, 44, 190,
+ 191, 133, 134, 135, 6, 145, 8, 9, 148, 149,
+ 150, 44, 27, 153, 154, 155, 156, 157, 46, 7,
+ 35, 36, 1, 38, 164, 40, 10, 11, 12, 45,
+ 45, 32, 46, 48, 13, 14, 15, 16, 17, 18,
+ 19, 20, 21, 22, 23, 24, 25, 26, 29, 45,
+ 190, 191, 6, 8, 9, 10, 7, 197, 198, 38,
+ 39, 40, 10, 42, 46, 41, 205, 29, 46, 46,
+ 32, 33, 27, 212, 46, 37, 31, 32, 33, 34,
+ 44, 44, 37, 38, 39, 40, 8, 9, 10, 44,
+ 45, 231, 47, 48, 8, 9, 10, 11, 12, 239,
+ 41, 44, 46, 45, 32, 27, 46, 45, 47, 31,
+ 32, 33, 34, 8, 9, 37, 38, 39, 40, 46,
+ 27, 9, 47, 45, 32, 47, 48, 33, 30, 10,
+ 46, 46, 27, 8, 9, 32, 31, 32, 33, 34,
+ 9, 27, 37, 38, 10, 40, 46, 27, 46, 44,
+ 45, 12, 27, 48, 46, 12, 31, 32, 33, 34,
+ 8, 9, 37, 38, 209, 40, -1, 61, -1, -1,
+ 45, -1, 47, 48, -1, -1, -1, -1, -1, 27,
+ 8, 9, -1, 31, 32, 33, 34, -1, -1, 37,
+ 38, -1, 40, -1, -1, -1, 44, 45, -1, 27,
+ 48, -1, -1, 31, 32, 33, 34, 8, 9, 37,
+ 38, -1, 40, -1, -1, -1, -1, 45, -1, -1,
+ 48, 8, 9, -1, -1, -1, 27, -1, -1, -1,
+ 31, 32, 33, 34, -1, -1, 37, 8, 9, 40,
+ 27, 8, 9, -1, 45, 32, -1, 48, -1, -1,
+ 37, 8, 9, 40, -1, -1, 27, -1, 45, -1,
+ 27, 48, -1, 8, 9, 8, 9, 38, 39, 40,
+ 27, 38, -1, 40, 45, 8, 9, 48, 45, 8,
+ 9, 48, 27, 40, 27, -1, -1, -1, 45, -1,
+ 35, 48, 35, -1, 27, 40, -1, 40, 27, -1,
+ 45, -1, 45, 48, -1, 48, 35, 40, -1, -1,
+ -1, 40, 45, -1, 47, 48, 45, -1, -1, 48,
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
+ 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, 50, 0, 51, 1, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, 22, 23, 24, 25, 26, 38,
+ 39, 40, 42, 52, 55, 42, 44, 56, 8, 9,
+ 27, 31, 32, 33, 34, 37, 38, 40, 44, 45,
+ 48, 60, 70, 74, 78, 79, 80, 81, 84, 47,
+ 59, 70, 72, 75, 57, 72, 58, 70, 10, 38,
+ 39, 44, 64, 71, 73, 74, 75, 79, 80, 84,
+ 62, 81, 44, 56, 60, 65, 38, 44, 45, 61,
+ 73, 84, 63, 78, 66, 72, 67, 72, 68, 72,
+ 69, 78, 41, 43, 41, 43, 42, 84, 84, 6,
+ 8, 9, 82, 70, 32, 37, 84, 85, 84, 45,
+ 45, 9, 35, 36, 45, 81, 84, 44, 44, 44,
+ 74, 79, 81, 82, 82, 10, 71, 45, 11, 82,
+ 73, 45, 44, 44, 44, 44, 44, 54, 85, 53,
+ 85, 7, 84, 84, 45, 10, 46, 46, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 46, 32,
+ 32, 33, 37, 35, 9, 35, 70, 72, 29, 84,
+ 47, 76, 84, 70, 70, 70, 75, 84, 52, 52,
+ 82, 28, 30, 37, 83, 84, 45, 85, 85, 85,
+ 6, 7, 85, 85, 85, 85, 85, 10, 10, 46,
+ 46, 46, 35, 46, 46, 44, 9, 27, 77, 44,
+ 41, 41, 44, 45, 46, 46, 32, 85, 85, 84,
+ 84, 45, 46, 75, 27, 9, 76, 32, 33, 75,
+ 30, 10, 46, 46, 32, 9, 27, 46, 84, 10,
+ 27, 46, 84, 46
+};
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+/* 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. */
+
+#define YYFAIL goto yyerrlab
+
+#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 \
+ { \
+ yyerror (YY_("syntax error: cannot back up")); \
+ YYERROR; \
+ } \
+while (YYID (0))
+
+
+#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. */
+
+#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
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments. */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (YYLEX_PARAM)
+#else
+# define YYLEX yylex ()
+#endif
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yy_symbol_print (stderr, \
+ Type, Value); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+#endif
+{
+ if (!yyvaluep)
+ return;
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+ YYUSE (yyoutput);
+# endif
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+#endif
+{
+ if (yytype < YYNTOKENS)
+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+ else
+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+ yy_symbol_value_print (yyoutput, yytype, yyvaluep);
+ YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop)
+#else
+static void
+yy_stack_print (yybottom, yytop)
+ yytype_int16 *yybottom;
+ yytype_int16 *yytop;
+#endif
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (; yybottom <= yytop; yybottom++)
+ {
+ int yybot = *yybottom;
+ YYFPRINTF (stderr, " %d", yybot);
+ }
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, int yyrule)
+#else
+static void
+yy_reduce_print (yyvsp, yyrule)
+ YYSTYPE *yyvsp;
+ int yyrule;
+#endif
+{
+ int yynrhs = yyr2[yyrule];
+ int yyi;
+ unsigned long int yylno = yyrline[yyrule];
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+ yyrule - 1, yylno);
+ /* The symbols being reduced. */
+ for (yyi = 0; yyi < yynrhs; yyi++)
+ {
+ YYFPRINTF (stderr, " $%d = ", yyi + 1);
+ yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+ &(yyvsp[(yyi + 1) - (yynrhs)])
+ );
+ YYFPRINTF (stderr, "\n");
+ }
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (yyvsp, Rule); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined __GLIBC__ && defined _STRING_H
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+ const char *yystr;
+#endif
+{
+ YYSIZE_T yylen;
+ for (yylen = 0; yystr[yylen]; yylen++)
+ continue;
+ return yylen;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+ char *yydest;
+ const char *yysrc;
+#endif
+{
+ char *yyd = yydest;
+ const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+ quotes and backslashes, so that it's suitable for yyerror. The
+ heuristic is that double-quoting is unnecessary unless the string
+ contains an apostrophe, a comma, or backslash (other than
+ backslash-backslash). YYSTR is taken from yytname. If YYRES is
+ null, do not copy; instead, return the length of what the result
+ would have been. */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+ if (*yystr == '"')
+ {
+ YYSIZE_T yyn = 0;
+ char const *yyp = yystr;
+
+ for (;;)
+ switch (*++yyp)
+ {
+ case '\'':
+ case ',':
+ goto do_not_strip_quotes;
+
+ case '\\':
+ if (*++yyp != '\\')
+ goto do_not_strip_quotes;
+ /* Fall through. */
+ default:
+ if (yyres)
+ yyres[yyn] = *yyp;
+ yyn++;
+ break;
+
+ case '"':
+ if (yyres)
+ yyres[yyn] = '\0';
+ return yyn;
+ }
+ do_not_strip_quotes: ;
+ }
+
+ if (! yyres)
+ return yystrlen (yystr);
+
+ return yystpcpy (yyres, yystr) - yyres;
+}
+# 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];
+
+ 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;
+ }
+
+ yyf = YY_(yyformat);
+ yysize1 = yysize + yystrlen (yyf);
+ yysize_overflow |= (yysize1 < yysize);
+ yysize = yysize1;
+
+ if (yysize_overflow)
+ return YYSIZE_MAXIMUM;
+
+ 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;
+ }
+}
+#endif /* YYERROR_VERBOSE */
+
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep)
+ const char *yymsg;
+ int yytype;
+ YYSTYPE *yyvaluep;
+#endif
+{
+ YYUSE (yyvaluep);
+
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ switch (yytype)
+ {
+
+ default:
+ break;
+ }
+}
+
+/* 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 lookahead symbol. */
+int yychar;
+
+/* The semantic value of the lookahead symbol. */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far. */
+int yynerrs;
+
+
+
+/*-------------------------.
+| yyparse or yypush_parse. |
+`-------------------------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+ void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+
+
+ 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 thru 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;
+ /* 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. */
+
+ /* 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;
+
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. So pushing a state here evens the stacks. */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ 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
+ be undefined if yyoverflow is a macro. */
+ yyoverflow (YY_("memory exhausted"),
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+ &yystacksize);
+
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyexhaustedlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyexhaustedlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ yytype_int16 *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyexhaustedlab;
+ YYSTACK_RELOCATE (yyss_alloc, yyss);
+ YYSTACK_RELOCATE (yyvs_alloc, yyvs);
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ if (yystate == YYFINAL)
+ YYACCEPT;
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+ /* Do appropriate processing given the current state. Read a
+ lookahead token if we need one and don't already have one. */
+
+ /* First try to decide what to do without reference to lookahead token. */
+ yyn = yypact[yystate];
+ if (yyn == YYPACT_NINF)
+ goto yydefault;
+
+ /* Not known => get a lookahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = YYLEX;
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yyn == 0 || yyn == YYTABLE_NINF)
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ /* Shift the lookahead token. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+ /* Discard the shifted token. */
+ yychar = YYEMPTY;
+
+ yystate = yyn;
+ *++yyvsp = yylval;
+
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ `$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 3:
+
+/* Line 1455 of yacc.c */
+#line 70 "a.y"
+ {
+ stmtline = lineno;
+ }
+ break;
+
+ case 5:
+
+/* Line 1455 of yacc.c */
+#line 77 "a.y"
+ {
+ if((yyvsp[(1) - (2)].sym)->value != pc)
+ yyerror("redeclaration of %s", (yyvsp[(1) - (2)].sym)->name);
+ (yyvsp[(1) - (2)].sym)->value = pc;
+ }
+ break;
+
+ case 7:
+
+/* Line 1455 of yacc.c */
+#line 84 "a.y"
+ {
+ (yyvsp[(1) - (2)].sym)->type = LLAB;
+ (yyvsp[(1) - (2)].sym)->value = pc;
+ }
+ break;
+
+ case 12:
+
+/* Line 1455 of yacc.c */
+#line 95 "a.y"
+ {
+ (yyvsp[(1) - (3)].sym)->type = LVAR;
+ (yyvsp[(1) - (3)].sym)->value = (yyvsp[(3) - (3)].lval);
+ }
+ break;
+
+ case 13:
+
+/* Line 1455 of yacc.c */
+#line 100 "a.y"
+ {
+ if((yyvsp[(1) - (3)].sym)->value != (yyvsp[(3) - (3)].lval))
+ yyerror("redeclaration of %s", (yyvsp[(1) - (3)].sym)->name);
+ (yyvsp[(1) - (3)].sym)->value = (yyvsp[(3) - (3)].lval);
+ }
+ break;
+
+ case 14:
+
+/* Line 1455 of yacc.c */
+#line 105 "a.y"
+ { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
+ break;
+
+ case 15:
+
+/* Line 1455 of yacc.c */
+#line 106 "a.y"
+ { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
+ break;
+
+ case 16:
+
+/* Line 1455 of yacc.c */
+#line 107 "a.y"
+ { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
+ break;
+
+ case 17:
+
+/* Line 1455 of yacc.c */
+#line 108 "a.y"
+ { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
+ break;
+
+ case 18:
+
+/* Line 1455 of yacc.c */
+#line 109 "a.y"
+ { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
+ break;
+
+ case 19:
+
+/* Line 1455 of yacc.c */
+#line 110 "a.y"
+ { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
+ break;
+
+ case 20:
+
+/* Line 1455 of yacc.c */
+#line 111 "a.y"
+ { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
+ break;
+
+ case 21:
+
+/* Line 1455 of yacc.c */
+#line 112 "a.y"
+ { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
+ break;
+
+ case 22:
+
+/* Line 1455 of yacc.c */
+#line 113 "a.y"
+ { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
+ break;
+
+ case 23:
+
+/* Line 1455 of yacc.c */
+#line 114 "a.y"
+ { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
+ break;
+
+ case 24:
+
+/* Line 1455 of yacc.c */
+#line 115 "a.y"
+ { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
+ break;
+
+ case 25:
+
+/* Line 1455 of yacc.c */
+#line 116 "a.y"
+ { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
+ break;
+
+ case 26:
+
+/* Line 1455 of yacc.c */
+#line 117 "a.y"
+ { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
+ break;
+
+ case 27:
+
+/* Line 1455 of yacc.c */
+#line 118 "a.y"
+ { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); }
+ break;
+
+ case 28:
+
+/* Line 1455 of yacc.c */
+#line 121 "a.y"
+ {
+ (yyval.gen2).from = nullgen;
+ (yyval.gen2).to = nullgen;
+ }
+ break;
+
+ case 29:
+
+/* Line 1455 of yacc.c */
+#line 126 "a.y"
+ {
+ (yyval.gen2).from = nullgen;
+ (yyval.gen2).to = nullgen;
+ }
+ break;
+
+ case 30:
+
+/* Line 1455 of yacc.c */
+#line 133 "a.y"
+ {
+ (yyval.gen2).from = (yyvsp[(1) - (3)].gen);
+ (yyval.gen2).to = (yyvsp[(3) - (3)].gen);
+ }
+ break;
+
+ case 31:
+
+/* Line 1455 of yacc.c */
+#line 140 "a.y"
+ {
+ (yyval.gen2).from = (yyvsp[(1) - (3)].gen);
+ (yyval.gen2).to = (yyvsp[(3) - (3)].gen);
+ }
+ break;
+
+ case 32:
+
+/* Line 1455 of yacc.c */
+#line 147 "a.y"
+ {
+ (yyval.gen2).from = (yyvsp[(1) - (2)].gen);
+ (yyval.gen2).to = nullgen;
+ }
+ break;
+
+ case 33:
+
+/* Line 1455 of yacc.c */
+#line 152 "a.y"
+ {
+ (yyval.gen2).from = (yyvsp[(1) - (1)].gen);
+ (yyval.gen2).to = nullgen;
+ }
+ break;
+
+ case 34:
+
+/* Line 1455 of yacc.c */
+#line 159 "a.y"
+ {
+ (yyval.gen2).from = nullgen;
+ (yyval.gen2).to = (yyvsp[(2) - (2)].gen);
+ }
+ break;
+
+ case 35:
+
+/* Line 1455 of yacc.c */
+#line 164 "a.y"
+ {
+ (yyval.gen2).from = nullgen;
+ (yyval.gen2).to = (yyvsp[(1) - (1)].gen);
+ }
+ break;
+
+ case 36:
+
+/* Line 1455 of yacc.c */
+#line 171 "a.y"
+ {
+ (yyval.gen2).from = nullgen;
+ (yyval.gen2).to = (yyvsp[(2) - (2)].gen);
+ }
+ break;
+
+ case 37:
+
+/* Line 1455 of yacc.c */
+#line 176 "a.y"
+ {
+ (yyval.gen2).from = nullgen;
+ (yyval.gen2).to = (yyvsp[(1) - (1)].gen);
+ }
+ break;
+
+ case 38:
+
+/* Line 1455 of yacc.c */
+#line 183 "a.y"
+ {
+ (yyval.gen2).from = (yyvsp[(1) - (5)].gen);
+ (yyval.gen2).from.scale = (yyvsp[(3) - (5)].lval);
+ (yyval.gen2).to = (yyvsp[(5) - (5)].gen);
+ }
+ break;
+
+ case 39:
+
+/* Line 1455 of yacc.c */
+#line 191 "a.y"
+ {
+ (yyval.gen2).from = (yyvsp[(1) - (3)].gen);
+ (yyval.gen2).to = (yyvsp[(3) - (3)].gen);
+ }
+ break;
+
+ case 40:
+
+/* Line 1455 of yacc.c */
+#line 196 "a.y"
+ {
+ (yyval.gen2).from = (yyvsp[(1) - (5)].gen);
+ (yyval.gen2).from.scale = (yyvsp[(3) - (5)].lval);
+ (yyval.gen2).to = (yyvsp[(5) - (5)].gen);
+ }
+ break;
+
+ case 41:
+
+/* Line 1455 of yacc.c */
+#line 204 "a.y"
+ {
+ (yyval.gen2).from = nullgen;
+ (yyval.gen2).to = (yyvsp[(2) - (2)].gen);
+ }
+ break;
+
+ case 42:
+
+/* Line 1455 of yacc.c */
+#line 209 "a.y"
+ {
+ (yyval.gen2).from = nullgen;
+ (yyval.gen2).to = (yyvsp[(1) - (1)].gen);
+ }
+ break;
+
+ case 43:
+
+/* Line 1455 of yacc.c */
+#line 214 "a.y"
+ {
+ (yyval.gen2).from = nullgen;
+ (yyval.gen2).to = (yyvsp[(2) - (2)].gen);
+ (yyval.gen2).to.index = (yyvsp[(2) - (2)].gen).type;
+ (yyval.gen2).to.type = D_INDIR+D_ADDR;
+ }
+ break;
+
+ case 46:
+
+/* Line 1455 of yacc.c */
+#line 227 "a.y"
+ {
+ (yyval.gen2).from = (yyvsp[(1) - (3)].gen);
+ (yyval.gen2).to = (yyvsp[(3) - (3)].gen);
+ }
+ break;
+
+ case 47:
+
+/* Line 1455 of yacc.c */
+#line 232 "a.y"
+ {
+ (yyval.gen2).from = (yyvsp[(1) - (5)].gen);
+ (yyval.gen2).to = (yyvsp[(3) - (5)].gen);
+ if((yyval.gen2).from.index != D_NONE)
+ yyerror("dp shift with lhs index");
+ (yyval.gen2).from.index = (yyvsp[(5) - (5)].lval);
+ }
+ break;
+
+ case 48:
+
+/* Line 1455 of yacc.c */
+#line 242 "a.y"
+ {
+ (yyval.gen2).from = (yyvsp[(1) - (3)].gen);
+ (yyval.gen2).to = (yyvsp[(3) - (3)].gen);
+ }
+ break;
+
+ case 49:
+
+/* Line 1455 of yacc.c */
+#line 247 "a.y"
+ {
+ (yyval.gen2).from = (yyvsp[(1) - (5)].gen);
+ (yyval.gen2).to = (yyvsp[(3) - (5)].gen);
+ if((yyval.gen2).to.index != D_NONE)
+ yyerror("dp move with lhs index");
+ (yyval.gen2).to.index = (yyvsp[(5) - (5)].lval);
+ }
+ break;
+
+ case 50:
+
+/* Line 1455 of yacc.c */
+#line 257 "a.y"
+ {
+ (yyval.gen2).from = (yyvsp[(1) - (2)].gen);
+ (yyval.gen2).to = nullgen;
+ }
+ break;
+
+ case 51:
+
+/* Line 1455 of yacc.c */
+#line 262 "a.y"
+ {
+ (yyval.gen2).from = (yyvsp[(1) - (1)].gen);
+ (yyval.gen2).to = nullgen;
+ }
+ break;
+
+ case 52:
+
+/* Line 1455 of yacc.c */
+#line 267 "a.y"
+ {
+ (yyval.gen2).from = (yyvsp[(1) - (3)].gen);
+ (yyval.gen2).to = (yyvsp[(3) - (3)].gen);
+ }
+ break;
+
+ case 53:
+
+/* Line 1455 of yacc.c */
+#line 274 "a.y"
+ {
+ (yyval.gen2).from = (yyvsp[(1) - (3)].gen);
+ (yyval.gen2).to = (yyvsp[(3) - (3)].gen);
+ }
+ break;
+
+ case 54:
+
+/* Line 1455 of yacc.c */
+#line 279 "a.y"
+ {
+ (yyval.gen2).from = (yyvsp[(1) - (5)].gen);
+ (yyval.gen2).from.scale = (yyvsp[(3) - (5)].lval);
+ (yyval.gen2).to = (yyvsp[(5) - (5)].gen);
+ }
+ break;
+
+ case 59:
+
+/* Line 1455 of yacc.c */
+#line 293 "a.y"
+ {
+ (yyval.gen) = (yyvsp[(2) - (2)].gen);
+ }
+ break;
+
+ case 60:
+
+/* Line 1455 of yacc.c */
+#line 297 "a.y"
+ {
+ (yyval.gen) = (yyvsp[(2) - (2)].gen);
+ }
+ break;
+
+ case 66:
+
+/* Line 1455 of yacc.c */
+#line 310 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_BRANCH;
+ (yyval.gen).offset = (yyvsp[(1) - (4)].lval) + pc;
+ }
+ break;
+
+ case 67:
+
+/* Line 1455 of yacc.c */
+#line 316 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ if(pass == 2)
+ yyerror("undefined label: %s", (yyvsp[(1) - (2)].sym)->name);
+ (yyval.gen).type = D_BRANCH;
+ (yyval.gen).sym = (yyvsp[(1) - (2)].sym);
+ (yyval.gen).offset = (yyvsp[(2) - (2)].lval);
+ }
+ break;
+
+ case 68:
+
+/* Line 1455 of yacc.c */
+#line 325 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_BRANCH;
+ (yyval.gen).sym = (yyvsp[(1) - (2)].sym);
+ (yyval.gen).offset = (yyvsp[(1) - (2)].sym)->value + (yyvsp[(2) - (2)].lval);
+ }
+ break;
+
+ case 69:
+
+/* Line 1455 of yacc.c */
+#line 334 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = (yyvsp[(1) - (1)].lval);
+ }
+ break;
+
+ case 70:
+
+/* Line 1455 of yacc.c */
+#line 339 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = (yyvsp[(1) - (1)].lval);
+ }
+ break;
+
+ case 71:
+
+/* Line 1455 of yacc.c */
+#line 344 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = (yyvsp[(1) - (1)].lval);
+ }
+ break;
+
+ case 72:
+
+/* Line 1455 of yacc.c */
+#line 349 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_SP;
+ }
+ break;
+
+ case 73:
+
+/* Line 1455 of yacc.c */
+#line 354 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = (yyvsp[(1) - (1)].lval);
+ }
+ break;
+
+ case 74:
+
+/* Line 1455 of yacc.c */
+#line 361 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_CONST;
+ (yyval.gen).offset = (yyvsp[(2) - (2)].lval);
+ }
+ break;
+
+ case 75:
+
+/* Line 1455 of yacc.c */
+#line 367 "a.y"
+ {
+ (yyval.gen) = (yyvsp[(2) - (2)].gen);
+ (yyval.gen).index = (yyvsp[(2) - (2)].gen).type;
+ (yyval.gen).type = D_ADDR;
+ /*
+ if($2.type == D_AUTO || $2.type == D_PARAM)
+ yyerror("constant cannot be automatic: %s",
+ $2.sym->name);
+ */
+ }
+ break;
+
+ case 76:
+
+/* Line 1455 of yacc.c */
+#line 378 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_SCONST;
+ memcpy((yyval.gen).sval, (yyvsp[(2) - (2)].sval), sizeof((yyval.gen).sval));
+ }
+ break;
+
+ case 77:
+
+/* Line 1455 of yacc.c */
+#line 384 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_FCONST;
+ (yyval.gen).dval = (yyvsp[(2) - (2)].dval);
+ }
+ break;
+
+ case 78:
+
+/* Line 1455 of yacc.c */
+#line 390 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_FCONST;
+ (yyval.gen).dval = (yyvsp[(3) - (4)].dval);
+ }
+ break;
+
+ case 79:
+
+/* Line 1455 of yacc.c */
+#line 396 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_FCONST;
+ (yyval.gen).dval = -(yyvsp[(4) - (5)].dval);
+ }
+ break;
+
+ case 80:
+
+/* Line 1455 of yacc.c */
+#line 402 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_FCONST;
+ (yyval.gen).dval = -(yyvsp[(3) - (3)].dval);
+ }
+ break;
+
+ case 81:
+
+/* Line 1455 of yacc.c */
+#line 410 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_CONST2;
+ (yyval.gen).offset = (yyvsp[(2) - (2)].con2).v1;
+ (yyval.gen).offset2 = (yyvsp[(2) - (2)].con2).v2;
+ }
+ break;
+
+ case 82:
+
+/* Line 1455 of yacc.c */
+#line 419 "a.y"
+ {
+ (yyval.con2).v1 = (yyvsp[(1) - (1)].lval);
+ (yyval.con2).v2 = 0;
+ }
+ break;
+
+ case 83:
+
+/* Line 1455 of yacc.c */
+#line 424 "a.y"
+ {
+ (yyval.con2).v1 = -(yyvsp[(2) - (2)].lval);
+ (yyval.con2).v2 = 0;
+ }
+ break;
+
+ case 84:
+
+/* Line 1455 of yacc.c */
+#line 429 "a.y"
+ {
+ (yyval.con2).v1 = (yyvsp[(1) - (3)].lval);
+ (yyval.con2).v2 = (yyvsp[(3) - (3)].lval);
+ }
+ break;
+
+ case 85:
+
+/* Line 1455 of yacc.c */
+#line 434 "a.y"
+ {
+ (yyval.con2).v1 = -(yyvsp[(2) - (4)].lval);
+ (yyval.con2).v2 = (yyvsp[(4) - (4)].lval);
+ }
+ break;
+
+ case 88:
+
+/* Line 1455 of yacc.c */
+#line 445 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_INDIR+D_NONE;
+ (yyval.gen).offset = (yyvsp[(1) - (1)].lval);
+ }
+ break;
+
+ case 89:
+
+/* Line 1455 of yacc.c */
+#line 451 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_INDIR+(yyvsp[(3) - (4)].lval);
+ (yyval.gen).offset = (yyvsp[(1) - (4)].lval);
+ }
+ break;
+
+ case 90:
+
+/* Line 1455 of yacc.c */
+#line 457 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_INDIR+D_SP;
+ (yyval.gen).offset = (yyvsp[(1) - (4)].lval);
+ }
+ break;
+
+ case 91:
+
+/* Line 1455 of yacc.c */
+#line 463 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_INDIR+D_NONE;
+ (yyval.gen).offset = (yyvsp[(1) - (6)].lval);
+ (yyval.gen).index = (yyvsp[(3) - (6)].lval);
+ (yyval.gen).scale = (yyvsp[(5) - (6)].lval);
+ checkscale((yyval.gen).scale);
+ }
+ break;
+
+ case 92:
+
+/* Line 1455 of yacc.c */
+#line 472 "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 93:
+
+/* Line 1455 of yacc.c */
+#line 481 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_INDIR+(yyvsp[(2) - (3)].lval);
+ }
+ break;
+
+ case 94:
+
+/* Line 1455 of yacc.c */
+#line 486 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_INDIR+D_SP;
+ }
+ break;
+
+ case 95:
+
+/* Line 1455 of yacc.c */
+#line 491 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_INDIR+(yyvsp[(3) - (4)].lval);
+ (yyval.gen).offset = (yyvsp[(1) - (4)].lval);
+ }
+ break;
+
+ case 96:
+
+/* Line 1455 of yacc.c */
+#line 497 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_INDIR+D_NONE;
+ (yyval.gen).index = (yyvsp[(2) - (5)].lval);
+ (yyval.gen).scale = (yyvsp[(4) - (5)].lval);
+ checkscale((yyval.gen).scale);
+ }
+ break;
+
+ case 97:
+
+/* Line 1455 of yacc.c */
+#line 505 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_INDIR+(yyvsp[(2) - (8)].lval);
+ (yyval.gen).index = (yyvsp[(5) - (8)].lval);
+ (yyval.gen).scale = (yyvsp[(7) - (8)].lval);
+ checkscale((yyval.gen).scale);
+ }
+ break;
+
+ case 98:
+
+/* Line 1455 of yacc.c */
+#line 515 "a.y"
+ {
+ (yyval.gen) = (yyvsp[(1) - (1)].gen);
+ }
+ break;
+
+ case 99:
+
+/* Line 1455 of yacc.c */
+#line 519 "a.y"
+ {
+ (yyval.gen) = (yyvsp[(1) - (6)].gen);
+ (yyval.gen).index = (yyvsp[(3) - (6)].lval);
+ (yyval.gen).scale = (yyvsp[(5) - (6)].lval);
+ checkscale((yyval.gen).scale);
+ }
+ break;
+
+ case 100:
+
+/* Line 1455 of yacc.c */
+#line 528 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = (yyvsp[(4) - (5)].lval);
+ (yyval.gen).sym = (yyvsp[(1) - (5)].sym);
+ (yyval.gen).offset = (yyvsp[(2) - (5)].lval);
+ }
+ break;
+
+ case 101:
+
+/* Line 1455 of yacc.c */
+#line 535 "a.y"
+ {
+ (yyval.gen) = nullgen;
+ (yyval.gen).type = D_STATIC;
+ (yyval.gen).sym = (yyvsp[(1) - (7)].sym);
+ (yyval.gen).offset = (yyvsp[(4) - (7)].lval);
+ }
+ break;
+
+ case 102:
+
+/* Line 1455 of yacc.c */
+#line 543 "a.y"
+ {
+ (yyval.lval) = 0;
+ }
+ break;
+
+ case 103:
+
+/* Line 1455 of yacc.c */
+#line 547 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(2) - (2)].lval);
+ }
+ break;
+
+ case 104:
+
+/* Line 1455 of yacc.c */
+#line 551 "a.y"
+ {
+ (yyval.lval) = -(yyvsp[(2) - (2)].lval);
+ }
+ break;
+
+ case 106:
+
+/* Line 1455 of yacc.c */
+#line 558 "a.y"
+ {
+ (yyval.lval) = D_AUTO;
+ }
+ break;
+
+ case 109:
+
+/* Line 1455 of yacc.c */
+#line 566 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(1) - (1)].sym)->value;
+ }
+ break;
+
+ case 110:
+
+/* Line 1455 of yacc.c */
+#line 570 "a.y"
+ {
+ (yyval.lval) = -(yyvsp[(2) - (2)].lval);
+ }
+ break;
+
+ case 111:
+
+/* Line 1455 of yacc.c */
+#line 574 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(2) - (2)].lval);
+ }
+ break;
+
+ case 112:
+
+/* Line 1455 of yacc.c */
+#line 578 "a.y"
+ {
+ (yyval.lval) = ~(yyvsp[(2) - (2)].lval);
+ }
+ break;
+
+ case 113:
+
+/* Line 1455 of yacc.c */
+#line 582 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(2) - (3)].lval);
+ }
+ break;
+
+ case 115:
+
+/* Line 1455 of yacc.c */
+#line 589 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(1) - (3)].lval) + (yyvsp[(3) - (3)].lval);
+ }
+ break;
+
+ case 116:
+
+/* Line 1455 of yacc.c */
+#line 593 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(1) - (3)].lval) - (yyvsp[(3) - (3)].lval);
+ }
+ break;
+
+ case 117:
+
+/* Line 1455 of yacc.c */
+#line 597 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(1) - (3)].lval) * (yyvsp[(3) - (3)].lval);
+ }
+ break;
+
+ case 118:
+
+/* Line 1455 of yacc.c */
+#line 601 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(1) - (3)].lval) / (yyvsp[(3) - (3)].lval);
+ }
+ break;
+
+ case 119:
+
+/* Line 1455 of yacc.c */
+#line 605 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(1) - (3)].lval) % (yyvsp[(3) - (3)].lval);
+ }
+ break;
+
+ case 120:
+
+/* Line 1455 of yacc.c */
+#line 609 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(1) - (4)].lval) << (yyvsp[(4) - (4)].lval);
+ }
+ break;
+
+ case 121:
+
+/* Line 1455 of yacc.c */
+#line 613 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(1) - (4)].lval) >> (yyvsp[(4) - (4)].lval);
+ }
+ break;
+
+ case 122:
+
+/* Line 1455 of yacc.c */
+#line 617 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(1) - (3)].lval) & (yyvsp[(3) - (3)].lval);
+ }
+ break;
+
+ case 123:
+
+/* Line 1455 of yacc.c */
+#line 621 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(1) - (3)].lval) ^ (yyvsp[(3) - (3)].lval);
+ }
+ break;
+
+ case 124:
+
+/* Line 1455 of yacc.c */
+#line 625 "a.y"
+ {
+ (yyval.lval) = (yyvsp[(1) - (3)].lval) | (yyvsp[(3) - (3)].lval);
+ }
+ break;
+
+
+
+/* Line 1455 of yacc.c */
+#line 2643 "y.tab.c"
+ default: break;
+ }
+ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++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. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if ! YYERROR_VERBOSE
+ yyerror (YY_("syntax error"));
+#else
+ {
+ 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;
+ }
+ }
+#endif
+ }
+
+
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse lookahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ YYABORT;
+ }
+ else
+ {
+ yydestruct ("Error: discarding",
+ yytoken, &yylval);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse lookahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+ /* Pacify compilers like GCC when the user code never invokes
+ YYERROR and the label yyerrorlab therefore never appears in user
+ code. */
+ if (/*CONSTCOND*/ 0)
+ goto yyerrorlab;
+
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYERROR. */
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (yyn != YYPACT_NINF)
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+
+ yydestruct ("Error: popping",
+ yystos[yystate], yyvsp);
+ YYPOPSTACK (1);
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ *++yyvsp = yylval;
+
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#if !defined(yyoverflow) || YYERROR_VERBOSE
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here. |
+`-------------------------------------------------*/
+yyexhaustedlab:
+ yyerror (YY_("memory exhausted"));
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+ if (yychar != YYEMPTY)
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval);
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYABORT or YYACCEPT. */
+ YYPOPSTACK (yylen);
+ YY_STACK_PRINT (yyss, yyssp);
+ while (yyssp != yyss)
+ {
+ yydestruct ("Cleanup: popping",
+ yystos[*yyssp], yyvsp);
+ YYPOPSTACK (1);
+ }
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+#endif
+ /* Make sure YYID is used. */
+ return YYID (yyresult);
+}
+
+
+
diff --git a/src/cmd/8a/y.tab.h b/src/cmd/8a/y.tab.h
new file mode 100644
index 000000000..69a966a4b
--- /dev/null
+++ b/src/cmd/8a/y.tab.h
@@ -0,0 +1,135 @@
+
+/* A Bison parser, made by GNU Bison 2.4.1. */
+
+/* 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
+ it under the terms of the GNU General Public License as published by
+ 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, 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
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ 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
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ LTYPE0 = 258,
+ LTYPE1 = 259,
+ LTYPE2 = 260,
+ LTYPE3 = 261,
+ LTYPE4 = 262,
+ LTYPEC = 263,
+ LTYPED = 264,
+ LTYPEN = 265,
+ LTYPER = 266,
+ LTYPET = 267,
+ LTYPES = 268,
+ LTYPEM = 269,
+ LTYPEI = 270,
+ LTYPEG = 271,
+ LCONST = 272,
+ LFP = 273,
+ LPC = 274,
+ LSB = 275,
+ LBREG = 276,
+ LLREG = 277,
+ LSREG = 278,
+ LFREG = 279,
+ LFCONST = 280,
+ LSCONST = 281,
+ LSP = 282,
+ LNAME = 283,
+ LLAB = 284,
+ LVAR = 285
+ };
+#endif
+/* Tokens. */
+#define LTYPE0 258
+#define LTYPE1 259
+#define LTYPE2 260
+#define LTYPE3 261
+#define LTYPE4 262
+#define LTYPEC 263
+#define LTYPED 264
+#define LTYPEN 265
+#define LTYPER 266
+#define LTYPET 267
+#define LTYPES 268
+#define LTYPEM 269
+#define LTYPEI 270
+#define LTYPEG 271
+#define LCONST 272
+#define LFP 273
+#define LPC 274
+#define LSB 275
+#define LBREG 276
+#define LLREG 277
+#define LSREG 278
+#define LFREG 279
+#define LFCONST 280
+#define LSCONST 281
+#define LSP 282
+#define LNAME 283
+#define LLAB 284
+#define LVAR 285
+
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+{
+
+/* Line 1676 of yacc.c */
+#line 37 "a.y"
+
+ Sym *sym;
+ int32 lval;
+ struct {
+ int32 v1;
+ int32 v2;
+ } con2;
+ double dval;
+ char sval[8];
+ Gen gen;
+ Gen2 gen2;
+
+
+
+/* Line 1676 of yacc.c */
+#line 127 "y.tab.h"
+} YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+extern YYSTYPE yylval;
+
+
diff --git a/src/cmd/8c/Makefile b/src/cmd/8c/Makefile
index 60f46d3c9..3f528d751 100644
--- a/src/cmd/8c/Makefile
+++ b/src/cmd/8c/Makefile
@@ -1,37 +1,5 @@
-# Copyright 2009 The Go Authors. All rights reserved.
+# 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 ../../Make.inc
-O:=$(HOST_O)
-
-TARG=8c
-
-HFILES=\
- gc.h\
- ../8l/8.out.h\
- ../cc/cc.h\
-
-OFILES=\
- cgen.$O\
- cgen64.$O\
- div.$O\
- list.$O\
- machcap.$O\
- mul.$O\
- pgen.$O\
- pswt.$O\
- peep.$O\
- reg.$O\
- sgen.$O\
- swt.$O\
- txt.$O\
- ../8l/enam.$O\
-
-LIB=\
- ../cc/cc.a\
-
-include ../../Make.ccmd
-
-%.$O: ../cc/%.c
- $(HOST_CC) $(HOST_CFLAGS) -c -I. -o $@ ../cc/$*.c
+include ../../Make.dist
diff --git a/src/cmd/8c/cgen.c b/src/cmd/8c/cgen.c
index 7f02bd96e..869d31ace 100644
--- a/src/cmd/8c/cgen.c
+++ b/src/cmd/8c/cgen.c
@@ -1221,7 +1221,7 @@ void
boolgen(Node *n, int true, Node *nn)
{
int o;
- Prog *p1, *p2;
+ Prog *p1, *p2, *p3;
Node *l, *r, nod, nod1;
int32 curs;
@@ -1346,6 +1346,15 @@ boolgen(Node *n, int true, Node *nn)
cgen64(n, Z);
goto com;
}
+ if(true && typefd[l->type->etype] && (o == OEQ || o == ONE)) {
+ // Cannot rewrite !(l == r) into l != r with float64; it breaks NaNs.
+ // Jump around instead.
+ boolgen(n, 0, Z);
+ p1 = p;
+ gbranch(OGOTO);
+ patch(p1, pc);
+ goto com;
+ }
if(true)
o = comrel[relindex(o)];
if(l->complex >= FNX && r->complex >= FNX) {
@@ -1378,6 +1387,30 @@ boolgen(Node *n, int true, Node *nn)
} else
fgopcode(o, l, &fregnode0, 0, 1);
}
+ switch(o) {
+ case OEQ:
+ // Already emitted AJEQ; want AJEQ and AJPC.
+ p1 = p;
+ gbranch(OGOTO);
+ p2 = p;
+ patch(p1, pc);
+ gins(AJPC, Z, Z);
+ patch(p2, pc);
+ break;
+
+ case ONE:
+ // Already emitted AJNE; want AJNE or AJPS.
+ p1 = p;
+ gins(AJPS, Z, Z);
+ p2 = p;
+ gbranch(OGOTO);
+ p3 = p;
+ patch(p1, pc);
+ patch(p2, pc);
+ gbranch(OGOTO);
+ patch(p3, pc);
+ break;
+ }
goto com;
}
if(l->op == OCONST) {
diff --git a/src/cmd/8c/gc.h b/src/cmd/8c/gc.h
index 32b80e995..4a57f5d3c 100644
--- a/src/cmd/8c/gc.h
+++ b/src/cmd/8c/gc.h
@@ -304,7 +304,8 @@ void gpseudo(int, Sym*, Node*);
int swcmp(const void*, const void*);
void doswit(Node*);
void swit1(C1*, int, int32, Node*);
-void cas(void);
+void swit2(C1*, int, int32, Node*);
+void newcase(void);
void bitload(Node*, Node*, Node*, Node*, Node*);
void bitstore(Node*, Node*, Node*, Node*, Node*);
int32 outstring(char*, int32);
diff --git a/src/cmd/8c/swt.c b/src/cmd/8c/swt.c
index 769ef2c66..f1ca4c25f 100644
--- a/src/cmd/8c/swt.c
+++ b/src/cmd/8c/swt.c
@@ -33,6 +33,26 @@
void
swit1(C1 *q, int nc, int32 def, Node *n)
{
+ Node nreg;
+
+ if(typev[n->type->etype]) {
+ regsalloc(&nreg, n);
+ nreg.type = types[TVLONG];
+ cgen(n, &nreg);
+ swit2(q, nc, def, &nreg);
+ return;
+ }
+
+ regalloc(&nreg, n, Z);
+ nreg.type = types[TLONG];
+ cgen(n, &nreg);
+ swit2(q, nc, def, &nreg);
+ regfree(&nreg);
+}
+
+void
+swit2(C1 *q, int nc, int32 def, Node *n)
+{
C1 *r;
int i;
Prog *sp;
@@ -58,12 +78,12 @@ swit1(C1 *q, int nc, int32 def, Node *n)
gbranch(OGOTO);
p->as = AJEQ;
patch(p, r->label);
- swit1(q, i, def, n);
+ swit2(q, i, def, n);
if(debug['W'])
print("case < %.8ux\n", r->val);
patch(sp, pc);
- swit1(r+1, nc-i-1, def, n);
+ swit2(r+1, nc-i-1, def, n);
}
void
@@ -330,17 +350,13 @@ outhist(Biobuf *b)
for(h = hist; h != H; h = h->link) {
p = h->name;
op = 0;
- /* on windows skip drive specifier in pathname */
if(systemtype(Windows) && p && p[1] == ':'){
- p += 2;
- c = *p;
- }
- if(p && p[0] != c && h->offset == 0 && pathname){
- /* on windows skip drive specifier in pathname */
+ c = p[2];
+ } else if(p && p[0] != c && h->offset == 0 && pathname){
if(systemtype(Windows) && pathname[1] == ':') {
op = p;
- p = pathname+2;
- c = *p;
+ p = pathname;
+ c = p[2];
} else if(pathname[0] == c){
op = p;
p = pathname;
diff --git a/src/cmd/8c/txt.c b/src/cmd/8c/txt.c
index b2e0148a0..3a08da7cd 100644
--- a/src/cmd/8c/txt.c
+++ b/src/cmd/8c/txt.c
@@ -146,7 +146,9 @@ 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;
@@ -372,7 +374,7 @@ regfree(Node *n)
if(n->op != OREGISTER && n->op != OINDREG)
goto err;
i = n->reg;
- if(i < 0 || i >= sizeof(reg))
+ if(i < 0 || i >= nelem(reg))
goto err;
if(reg[i] <= 0)
goto err;
diff --git a/src/cmd/8g/Makefile b/src/cmd/8g/Makefile
index b459782a3..3f528d751 100644
--- a/src/cmd/8g/Makefile
+++ b/src/cmd/8g/Makefile
@@ -1,36 +1,5 @@
-# Copyright 2009 The Go Authors. All rights reserved.
+# 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 ../../Make.inc
-O:=$(HOST_O)
-
-TARG=8g
-
-HFILES=\
- ../gc/go.h\
- ../8l/8.out.h\
- gg.h\
- opt.h\
-
-OFILES=\
- ../8l/enam.$O\
- cgen.$O\
- cgen64.$O\
- cplx.$O\
- galign.$O\
- ggen.$O\
- gobj.$O\
- gsubr.$O\
- list.$O\
- peep.$O\
- pgen.$O\
- reg.$O\
-
-LIB=\
- ../gc/gc.a\
-
-include ../../Make.ccmd
-
-%.$O: ../gc/%.c
- $(HOST_CC) $(HOST_CFLAGS) -c -I. -o $@ ../gc/$*.c
+include ../../Make.dist
diff --git a/src/cmd/8g/cgen.c b/src/cmd/8g/cgen.c
index b316e6e34..48619ac73 100644
--- a/src/cmd/8g/cgen.c
+++ b/src/cmd/8g/cgen.c
@@ -5,6 +5,8 @@
// TODO(rsc):
// assume CLD?
+#include <u.h>
+#include <libc.h>
#include "gg.h"
void
@@ -96,6 +98,9 @@ cgen(Node *n, Node *res)
if(isslice(n->left->type))
n->addable = n->left->addable;
break;
+ case OITAB:
+ n->addable = n->left->addable;
+ break;
}
// if both are addressable, move
@@ -250,6 +255,13 @@ cgen(Node *n, Node *res)
regfree(&n1);
break;
+ case OITAB:
+ igen(nl, &n1, res);
+ n1.type = ptrto(types[TUINTPTR]);
+ 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.
@@ -680,7 +692,6 @@ agen(Node *n, Node *res)
break;
case ODOT:
- t = nl->type;
agen(nl, res);
if(n->xoffset != 0) {
nodconst(&n1, types[tptr], n->xoffset);
@@ -786,6 +797,7 @@ bgen(Node *n, int true, Prog *to)
int et, a;
Node *nl, *nr, *r;
Node n1, n2, tmp, t1, t2, ax;
+ NodeList *ll;
Prog *p1, *p2;
if(debug['g']) {
@@ -798,9 +810,6 @@ bgen(Node *n, int true, Prog *to)
if(n->ninit != nil)
genlist(n->ninit);
- nl = n->left;
- nr = n->right;
-
if(n->type == T) {
convlit(&n, types[TBOOL]);
if(n->type == T)
@@ -813,7 +822,6 @@ bgen(Node *n, int true, Prog *to)
patch(gins(AEND, N, N), to);
return;
}
- nl = N;
nr = N;
switch(n->op) {
@@ -905,7 +913,10 @@ bgen(Node *n, int true, Prog *to)
p1 = gbranch(AJMP, T);
p2 = gbranch(AJMP, T);
patch(p1, pc);
+ ll = n->ninit; // avoid re-genning ninit
+ n->ninit = nil;
bgen(n, 1, p2);
+ n->ninit = ll;
patch(gbranch(AJMP, T), to);
patch(p2, pc);
break;
@@ -1129,24 +1140,29 @@ stkof(Node *n)
* memmove(&res, &n, w);
*/
void
-sgen(Node *n, Node *res, int32 w)
+sgen(Node *n, Node *res, int64 w)
{
Node dst, src, tdst, tsrc;
int32 c, q, odst, osrc;
if(debug['g']) {
- print("\nsgen w=%d\n", w);
+ print("\nsgen w=%lld\n", w);
dump("r", n);
dump("res", res);
}
- if(w == 0)
- return;
- if(n->ullman >= UINF && res->ullman >= UINF) {
+ if(n->ullman >= UINF && res->ullman >= UINF)
fatal("sgen UINF");
- }
- if(w < 0)
- fatal("sgen copy %d", w);
+ if(w < 0 || (int32)w != w)
+ fatal("sgen copy %lld", w);
+
+ if(w == 0) {
+ // evaluate side effects only.
+ tempname(&tdst, types[tptr]);
+ agen(res, &tdst);
+ agen(n, &tdst);
+ return;
+ }
// offset on the stack
osrc = stkof(n);
diff --git a/src/cmd/8g/cgen64.c b/src/cmd/8g/cgen64.c
index ba99cec74..8e568a0f9 100644
--- a/src/cmd/8g/cgen64.c
+++ b/src/cmd/8g/cgen64.c
@@ -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 <u.h>
+#include <libc.h>
#include "gg.h"
/*
diff --git a/src/cmd/8g/doc.go b/src/cmd/8g/doc.go
index 2d9ff9a42..6d678eac8 100644
--- a/src/cmd/8g/doc.go
+++ b/src/cmd/8g/doc.go
@@ -9,7 +9,5 @@ The $GOARCH for these tools is 386.
It reads .go files and outputs .8 files. The flags are documented in ../gc/doc.go.
-There is no instruction optimizer, so the -N flag is a no-op.
-
*/
package documentation
diff --git a/src/cmd/8g/galign.c b/src/cmd/8g/galign.c
index 7734603c4..4526a2efb 100644
--- a/src/cmd/8g/galign.c
+++ b/src/cmd/8g/galign.c
@@ -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 <u.h>
+#include <libc.h>
#include "gg.h"
int thechar = '8';
diff --git a/src/cmd/8g/gg.h b/src/cmd/8g/gg.h
index 9f7a66a29..0a4f0ad2d 100644
--- a/src/cmd/8g/gg.h
+++ b/src/cmd/8g/gg.h
@@ -2,16 +2,13 @@
// 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 "../gc/go.h"
-#include "../8l/8.out.h"
-
#ifndef EXTERN
#define EXTERN extern
#endif
+#include "../gc/go.h"
+#include "../8l/8.out.h"
+
typedef struct Addr Addr;
struct Addr
@@ -46,6 +43,8 @@ struct Prog
void* reg; // pointer to containing Reg struct
};
+#define TEXTFLAG from.scale
+
// foptoas flags
enum
{
@@ -54,15 +53,12 @@ enum
Fpop2 = 1<<2,
};
-EXTERN Biobuf* bout;
EXTERN int32 dynloc;
EXTERN uchar reg[D_NONE];
EXTERN int32 pcloc; // instruction counter
EXTERN Strlit emptystring;
extern char* anames[];
-EXTERN Hist* hist;
EXTERN Prog zprog;
-EXTERN Node* curfn;
EXTERN Node* newproc;
EXTERN Node* deferproc;
EXTERN Node* deferreturn;
@@ -103,7 +99,7 @@ void agenr(Node *n, Node *a, Node *res);
void igen(Node*, Node*, Node*);
vlong fieldoffset(Type*, Node*);
void bgen(Node*, int, Prog*);
-void sgen(Node*, Node*, int32);
+void sgen(Node*, Node*, int64);
void gmove(Node*, Node*);
Prog* gins(int, Node*, Node*);
int samaddr(Node*, Node*);
@@ -168,12 +164,6 @@ void complexgen(Node*, Node*);
void complexbool(int, Node*, Node*, int, Prog*);
/*
- * gobj.c
- */
-void data(void);
-void text(void);
-
-/*
* list.c
*/
int Aconv(Fmt*);
@@ -185,3 +175,5 @@ void listinit(void);
void zaddr(Biobuf*, Addr*, int, int);
+#pragma varargck type "D" Addr*
+#pragma varargck type "lD" Addr*
diff --git a/src/cmd/8g/ggen.c b/src/cmd/8g/ggen.c
index 108c493aa..6a4570199 100644
--- a/src/cmd/8g/ggen.c
+++ b/src/cmd/8g/ggen.c
@@ -4,6 +4,8 @@
#undef EXTERN
#define EXTERN
+#include <u.h>
+#include <libc.h>
#include "gg.h"
#include "opt.h"
@@ -26,10 +28,10 @@ markautoused(Prog* p)
{
for (; p; p = p->link) {
if (p->from.type == D_AUTO && p->from.node)
- p->from.node->used++;
+ p->from.node->used = 1;
if (p->to.type == D_AUTO && p->to.node)
- p->to.node->used++;
+ p->to.node->used = 1;
}
}
@@ -429,7 +431,6 @@ hard:
if(nr->ullman >= nl->ullman || nl->addable) {
mgen(nr, &n2, N);
nr = &n2;
- nr = &n2;
} else {
tempname(&n2, nr->type);
cgen(nr, &n2);
@@ -483,8 +484,8 @@ void
dodiv(int op, Node *nl, Node *nr, Node *res, Node *ax, Node *dx)
{
int check;
- Node n1, t1, t2, n4, nz;
- Type *t;
+ Node n1, t1, t2, t3, t4, n4, nz;
+ Type *t, *t0;
Prog *p1, *p2, *p3;
// Have to be careful about handling
@@ -496,6 +497,7 @@ dodiv(int op, Node *nl, Node *nr, Node *res, Node *ax, Node *dx)
// For int32 and int64, use explicit test.
// Could use int64 hw for int32.
t = nl->type;
+ t0 = t;
check = 0;
if(issigned[t->etype]) {
check = 1;
@@ -514,8 +516,18 @@ dodiv(int op, Node *nl, Node *nr, Node *res, Node *ax, Node *dx)
tempname(&t1, t);
tempname(&t2, t);
- cgen(nl, &t1);
- cgen(nr, &t2);
+ if(t0 != t) {
+ tempname(&t3, t0);
+ tempname(&t4, t0);
+ cgen(nl, &t3);
+ cgen(nr, &t4);
+ // Convert.
+ gmove(&t3, &t1);
+ gmove(&t4, &t2);
+ } else {
+ cgen(nl, &t1);
+ cgen(nr, &t2);
+ }
if(!samereg(ax, res) && !samereg(dx, res))
regalloc(&n1, t, res);
diff --git a/src/cmd/8g/gobj.c b/src/cmd/8g/gobj.c
index 31c42a3f2..d8c8f5ab9 100644
--- a/src/cmd/8g/gobj.c
+++ b/src/cmd/8g/gobj.c
@@ -28,6 +28,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#include <u.h>
+#include <libc.h>
#include "gg.h"
void
@@ -276,54 +278,6 @@ dumpfuncs(void)
}
}
-/* deferred DATA output */
-static Prog *strdat;
-static Prog *estrdat;
-static int gflag;
-static Prog *savepc;
-
-void
-data(void)
-{
- gflag = debug['g'];
- debug['g'] = 0;
-
- if(estrdat == nil) {
- strdat = mal(sizeof(*pc));
- clearp(strdat);
- estrdat = strdat;
- }
- if(savepc)
- fatal("data phase error");
- savepc = pc;
- pc = estrdat;
-}
-
-void
-text(void)
-{
- if(!savepc)
- fatal("text phase error");
- debug['g'] = gflag;
- estrdat = pc;
- pc = savepc;
- savepc = nil;
-}
-
-void
-dumpdata(void)
-{
- Prog *p;
-
- if(estrdat == nil)
- return;
- *pc = *strdat;
- if(gflag)
- for(p=pc; p!=estrdat; p=p->link)
- print("%P\n", p);
- pc = estrdat;
-}
-
int
dsname(Sym *s, int off, char *t, int n)
{
@@ -354,6 +308,7 @@ datastring(char *s, int len, Addr *a)
sym = stringsym(s, len);
a->type = D_EXTERN;
a->sym = sym;
+ a->node = sym->def;
a->offset = widthptr+4; // skip header
a->etype = TINT32;
}
@@ -370,6 +325,7 @@ datagostring(Strlit *sval, Addr *a)
sym = stringsym(sval->s, sval->len);
a->type = D_EXTERN;
a->sym = sym;
+ a->node = sym->def;
a->offset = 0; // header
a->etype = TINT32;
}
@@ -380,6 +336,17 @@ gdata(Node *nam, Node *nr, int wid)
Prog *p;
vlong v;
+ if(nr->op == OLITERAL) {
+ switch(nr->val.ctype) {
+ case CTCPLX:
+ gdatacomplex(nam, nr->val.u.cval);
+ return;
+ case CTSTR:
+ gdatastring(nam, nr->val.u.sval);
+ return;
+ }
+ }
+
if(wid == 8 && is64(nr->type)) {
v = mpgetfix(nr->val.u.xval);
p = gins(ADATA, nam, nodintconst(v));
@@ -547,6 +514,8 @@ genembedtramp(Type *rcvr, Type *method, Sym *newnam, int iface)
Prog *p;
Type *f;
+ USED(iface);
+
e = method->sym;
for(d=0; d<nelem(dotlist); d++) {
c = adddot1(e, rcvr, d, nil, 0);
@@ -626,7 +595,6 @@ out:
// but 6l has a bug, and it can't handle
// JMP instructions too close to the top of
// a new function.
- p = pc;
gins(ANOP, N, N);
}
diff --git a/src/cmd/8g/gsubr.c b/src/cmd/8g/gsubr.c
index a35c81eb1..5e89af04a 100644
--- a/src/cmd/8g/gsubr.c
+++ b/src/cmd/8g/gsubr.c
@@ -28,6 +28,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#include <u.h>
+#include <libc.h>
#include "gg.h"
// TODO(rsc): Can make this bigger if we move
@@ -48,6 +50,10 @@ clearp(Prog *p)
pcloc++;
}
+static int ddumped;
+static Prog *dfirst;
+static Prog *dpc;
+
/*
* generate and return proc with p->as = as,
* linked into program. pc is next instruction.
@@ -57,10 +63,22 @@ prog(int as)
{
Prog *p;
- p = pc;
- pc = mal(sizeof(*pc));
-
- clearp(pc);
+ if(as == ADATA || as == AGLOBL) {
+ if(ddumped)
+ fatal("already dumped data");
+ if(dpc == nil) {
+ dpc = mal(sizeof(*dpc));
+ dfirst = dpc;
+ }
+ p = dpc;
+ dpc = mal(sizeof(*dpc));
+ p->link = dpc;
+ } else {
+ p = pc;
+ pc = mal(sizeof(*pc));
+ clearp(pc);
+ p->link = pc;
+ }
if(lineno == 0) {
if(debug['K'])
@@ -69,10 +87,21 @@ prog(int as)
p->as = as;
p->lineno = lineno;
- p->link = pc;
return p;
}
+void
+dumpdata(void)
+{
+ ddumped = 1;
+ if(dfirst == nil)
+ return;
+ newplist();
+ *pc = *dfirst;
+ pc = dpc;
+ clearp(pc);
+}
+
/*
* generate a branch.
* t is ignored.
@@ -82,6 +111,7 @@ gbranch(int as, Type *t)
{
Prog *p;
+ USED(t);
p = prog(as);
p->to.type = D_BRANCH;
p->to.branch = P;
@@ -202,6 +232,8 @@ ggloblnod(Node *nam, int32 width)
p->to.offset = width;
if(nam->readonly)
p->from.scale = RODATA;
+ if(nam->type != T && !haspointers(nam->type))
+ p->from.scale |= NOPTR;
}
void
@@ -753,7 +785,7 @@ ginit(void)
reg[resvd[i]]++;
}
-ulong regpc[D_NONE];
+uintptr regpc[D_NONE];
void
gclean(void)
@@ -822,7 +854,7 @@ regalloc(Node *n, Type *t, Node *o)
fprint(2, "registers allocated at\n");
for(i=D_AX; i<=D_DI; i++)
- fprint(2, "\t%R\t%#ux\n", i, regpc[i]);
+ fprint(2, "\t%R\t%#lux\n", i, regpc[i]);
yyerror("out of fixed registers");
goto err;
@@ -832,7 +864,6 @@ regalloc(Node *n, Type *t, Node *o)
goto out;
}
yyerror("regalloc: unknown type %T", t);
- i = 0;
err:
nodreg(n, t, 0);
@@ -842,7 +873,7 @@ out:
if (i == D_SP)
print("alloc SP\n");
if(reg[i] == 0) {
- regpc[i] = (ulong)__builtin_return_address(0);
+ regpc[i] = (uintptr)getcallerpc(&n);
if(i == D_AX || i == D_CX || i == D_DX || i == D_SP) {
dump("regalloc-o", o);
fatal("regalloc %R", i);
@@ -864,7 +895,7 @@ regfree(Node *n)
i = n->val.u.reg;
if(i == D_SP)
return;
- if(i < 0 || i >= sizeof(reg))
+ if(i < 0 || i >= nelem(reg))
fatal("regfree: reg out of range");
if(reg[i] <= 0)
fatal("regfree: reg not allocated");
@@ -935,8 +966,15 @@ nodarg(Type *t, int fp)
fatal("nodarg: offset not computed for %T", t);
n->xoffset = t->width;
n->addable = 1;
+ n->orig = t->nname;
break;
}
+
+ // Rewrite argument named _ to __,
+ // or else the assignment to _ will be
+ // discarded during code generation.
+ if(isblank(n))
+ n->sym = lookup("__");
switch(fp) {
default:
@@ -1005,6 +1043,7 @@ int
ismem(Node *n)
{
switch(n->op) {
+ case OITAB:
case OLEN:
case OCAP:
case OINDREG:
@@ -1123,6 +1162,7 @@ memname(Node *n, Type *t)
strcpy(namebuf, n->sym->name);
namebuf[0] = '.'; // keep optimizer from registerizing
n->sym = lookup(namebuf);
+ n->orig->sym = n->sym;
}
void
@@ -1799,6 +1839,7 @@ naddr(Node *n, Addr *a, int canemitcode)
a->offset = n->xoffset;
a->sym = n->left->sym;
a->type = D_PARAM;
+ a->node = n->left->orig;
break;
case ONAME:
@@ -1809,9 +1850,11 @@ naddr(Node *n, Addr *a, int canemitcode)
a->width = n->type->width;
a->gotype = ngotype(n);
}
- a->pun = n->pun;
a->offset = n->xoffset;
a->sym = n->sym;
+ a->node = n->orig;
+ //if(a->node >= (Node*)&n)
+ // fatal("stack node");
if(a->sym == S)
a->sym = lookup(".noname");
if(n->method) {
@@ -1829,8 +1872,6 @@ naddr(Node *n, Addr *a, int canemitcode)
break;
case PAUTO:
a->type = D_AUTO;
- if (n->sym)
- a->node = n->orig;
break;
case PPARAM:
case PPARAMOUT:
@@ -1853,6 +1894,7 @@ naddr(Node *n, Addr *a, int canemitcode)
a->dval = mpgetflt(n->val.u.fval);
break;
case CTINT:
+ case CTRUNE:
a->sym = S;
a->type = D_CONST;
a->offset = mpgetfix(n->val.u.xval);
@@ -1887,6 +1929,17 @@ naddr(Node *n, Addr *a, int canemitcode)
break;
}
fatal("naddr: OADDR\n");
+
+ case OITAB:
+ // itable of interface value
+ naddr(n->left, a, canemitcode);
+ if(a->type == D_CONST && a->offset == 0)
+ break; // len(nil)
+ a->etype = tptr;
+ a->width = widthptr;
+ if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero)
+ checkoffset(a, canemitcode);
+ break;
case OLEN:
// len of string or slice
@@ -1955,5 +2008,9 @@ sudoclean(void)
int
sudoaddable(int as, Node *n, Addr *a)
{
+ USED(as);
+ USED(n);
+ USED(a);
+
return 0;
}
diff --git a/src/cmd/8g/list.c b/src/cmd/8g/list.c
index edb1ece84..88d3d5f7e 100644
--- a/src/cmd/8g/list.c
+++ b/src/cmd/8g/list.c
@@ -28,6 +28,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#include <u.h>
+#include <libc.h>
#include "gg.h"
static int sconsize;
@@ -128,7 +130,7 @@ Dconv(Fmt *fp)
if(fp->flags & FmtLong) {
d1 = a->offset;
d2 = a->offset2;
- snprint(str, sizeof(str), "$%ud-%ud", (ulong)d1, (ulong)d2);
+ snprint(str, sizeof(str), "$%lud-%lud", (ulong)d1, (ulong)d2);
break;
}
snprint(str, sizeof(str), "$%d", a->offset);
diff --git a/src/cmd/8g/opt.h b/src/cmd/8g/opt.h
index 8f31dec3b..ed6eb15ab 100644
--- a/src/cmd/8g/opt.h
+++ b/src/cmd/8g/opt.h
@@ -162,3 +162,5 @@ int32 RtoB(int);
int32 FtoB(int);
int BtoR(int32);
int BtoF(int32);
+
+#pragma varargck type "D" Adr*
diff --git a/src/cmd/8g/peep.c b/src/cmd/8g/peep.c
index 5ad29e1b2..b8a2825e5 100644
--- a/src/cmd/8g/peep.c
+++ b/src/cmd/8g/peep.c
@@ -28,6 +28,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#include <u.h>
+#include <libc.h>
#include "gg.h"
#include "opt.h"
@@ -876,13 +878,12 @@ loop:
case 3: // set
if(p->as == p0->as)
if(p->from.type == p0->from.type)
- if(p->from.sym == p0->from.sym)
+ if(p->from.node == p0->from.node)
if(p->from.offset == p0->from.offset)
if(p->from.scale == p0->from.scale)
if(p->from.dval == p0->from.dval)
if(p->from.index == p0->from.index) {
excise(r);
- t++;
goto loop;
}
break;
diff --git a/src/cmd/8g/reg.c b/src/cmd/8g/reg.c
index 2b878f62a..29270c820 100644
--- a/src/cmd/8g/reg.c
+++ b/src/cmd/8g/reg.c
@@ -28,9 +28,9 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#include <u.h>
+#include <libc.h>
#include "gg.h"
-#undef EXTERN
-#define EXTERN
#include "opt.h"
#define NREGVAR 8
@@ -39,6 +39,8 @@
static int first = 1;
+static void fixjmp(Prog*);
+
Reg*
rega(void)
{
@@ -89,8 +91,8 @@ setoutvar(void)
ovar.b[z] |= bit.b[z];
t = structnext(&save);
}
-//if(bany(b))
-//print("ovars = %Q\n", &ovar);
+//if(bany(ovar))
+//print("ovars = %Q\n", ovar);
}
static void
@@ -98,19 +100,19 @@ setaddrs(Bits bit)
{
int i, n;
Var *v;
- Sym *s;
+ Node *node;
while(bany(&bit)) {
// convert each bit to a variable
i = bnum(bit);
- s = var[i].sym;
+ node = var[i].node;
n = var[i].name;
bit.b[i/32] &= ~(1L<<(i%32));
// disable all pieces of that variable
for(i=0; i<nvar; i++) {
v = var+i;
- if(v->sym == s && v->name == n)
+ if(v->node == node && v->name == n)
v->addr = 2;
}
}
@@ -132,6 +134,8 @@ regopt(Prog *firstp)
exregoffset = D_DI; // no externals
first = 0;
}
+
+ fixjmp(firstp);
// count instructions
nr = 0;
@@ -155,7 +159,7 @@ regopt(Prog *firstp)
nvar = NREGVAR;
memset(var, 0, NREGVAR*sizeof var[0]);
for(i=0; i<NREGVAR; i++)
- var[i].sym = lookup(regname[i]);
+ var[i].node = newname(lookup(regname[i]));
regbits = RtoB(D_SP);
for(z=0; z<BITS; z++) {
@@ -216,6 +220,9 @@ regopt(Prog *firstp)
* funny
*/
case ALEAL:
+ case AFMOVL:
+ case AFMOVW:
+ case AFMOVV:
setaddrs(bit);
break;
@@ -694,9 +701,9 @@ brk:
if(ostats.ndelmov)
print(" %4d delmov\n", ostats.ndelmov);
if(ostats.nvar)
- print(" %4d delmov\n", ostats.nvar);
+ print(" %4d var\n", ostats.nvar);
if(ostats.naddr)
- print(" %4d delmov\n", ostats.naddr);
+ print(" %4d addr\n", ostats.naddr);
memset(&ostats, 0, sizeof(ostats));
}
@@ -725,19 +732,19 @@ addmove(Reg *r, int bn, int rn, int f)
v = var + bn;
a = &p1->to;
- a->sym = v->sym;
a->offset = v->offset;
a->etype = v->etype;
a->type = v->name;
a->gotype = v->gotype;
a->node = v->node;
+ a->sym = v->node->sym;
// need to clean this up with wptr and
// some of the defaults
p1->as = AMOVL;
switch(v->etype) {
default:
- fatal("unknown type\n");
+ fatal("unknown type %E", v->etype);
case TINT8:
case TUINT8:
case TBOOL:
@@ -810,7 +817,7 @@ mkvar(Reg *r, Adr *a)
int i, t, n, et, z, w, flag, regu;
int32 o;
Bits bit;
- Sym *s;
+ Node *node;
/*
* mark registers used
@@ -847,10 +854,13 @@ mkvar(Reg *r, Adr *a)
break;
}
- s = a->sym;
- if(s == S)
+ node = a->node;
+ if(node == N || node->op != ONAME || node->orig == N)
goto none;
- if(s->name[0] == '.')
+ node = node->orig;
+ if(node->orig != node)
+ fatal("%D: bad node", a);
+ if(node->sym == S || node->sym->name[0] == '.')
goto none;
et = a->etype;
o = a->offset;
@@ -859,7 +869,7 @@ mkvar(Reg *r, Adr *a)
flag = 0;
for(i=0; i<nvar; i++) {
v = var+i;
- if(v->sym == s && v->name == n) {
+ if(v->node == node && v->name == n) {
if(v->offset == o)
if(v->etype == et)
if(v->width == w)
@@ -868,7 +878,7 @@ mkvar(Reg *r, Adr *a)
// if they overlap, disable both
if(overlap(v->offset, v->width, o, w)) {
if(debug['R'])
- print("disable %s\n", v->sym->name);
+ print("disable %s\n", node->sym->name);
v->addr = 1;
flag = 1;
}
@@ -882,7 +892,7 @@ mkvar(Reg *r, Adr *a)
}
if(nvar >= NVAR) {
- if(debug['w'] > 1 && s)
+ if(debug['w'] > 1 && node != N)
fatal("variable not optimized: %D", a);
goto none;
}
@@ -890,17 +900,16 @@ mkvar(Reg *r, Adr *a)
i = nvar;
nvar++;
v = var+i;
- v->sym = s;
v->offset = o;
v->name = n;
v->gotype = a->gotype;
v->etype = et;
v->width = w;
v->addr = flag; // funny punning
- v->node = a->node;
+ v->node = node;
if(debug['R'])
- print("bit=%2d et=%2d w=%d+%d %S %D flag=%d\n", i, et, o, w, s, a, v->addr);
+ print("bit=%2d et=%2d w=%d+%d %#N %D flag=%d\n", i, et, o, w, node, a, v->addr);
ostats.nvar++;
bit = blsh(i);
@@ -959,6 +968,13 @@ prop(Reg *r, Bits ref, Bits cal)
ref.b[z] = 0;
}
break;
+
+ default:
+ // Work around for issue 1304:
+ // flush modified globals before each instruction.
+ for(z=0; z<BITS; z++)
+ cal.b[z] |= externs.b[z];
+ break;
}
for(z=0; z<BITS; z++) {
ref.b[z] = (ref.b[z] & ~r1->set.b[z]) |
@@ -1095,10 +1111,12 @@ loopit(Reg *r, int32 nr)
r1 = rpo2r[i];
me = r1->rpo;
d = -1;
- if(r1->p1 != R && r1->p1->rpo < me)
+ // 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(r1->rpo < me)
+ if(rpo2r[r1->rpo] == r1 && r1->rpo < me)
d = rpolca(idom, d, r1->rpo);
idom[i] = d;
}
@@ -1482,7 +1500,7 @@ dumpone(Reg *r)
if(bany(&r->refahead))
print(" ra:%Q ", r->refahead);
if(bany(&r->calbehind))
- print("cb:%Q ", r->calbehind);
+ print(" cb:%Q ", r->calbehind);
if(bany(&r->calahead))
print(" ca:%Q ", r->calahead);
if(bany(&r->regdiff))
@@ -1542,3 +1560,123 @@ noreturn(Prog *p)
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.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.branch)
+ mark(p->to.branch);
+ if(p->as == AJMP || p->as == ARET || (p->as == ACALL && 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 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.branch && p->to.branch->as == AJMP) {
+ p->to.branch = chasejmp(p->to.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.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/8l/8.out.h b/src/cmd/8l/8.out.h
index 9a8483aaf..9d2751cf0 100644
--- a/src/cmd/8l/8.out.h
+++ b/src/cmd/8l/8.out.h
@@ -34,6 +34,7 @@
#define DUPOK (1<<1)
#define NOSPLIT (1<<2)
#define RODATA (1<<3)
+#define NOPTR (1<<4)
enum as
{
@@ -115,7 +116,8 @@ enum as
AIRETW,
AJCC,
AJCS,
- AJCXZ,
+ AJCXZL,
+ AJCXZW,
AJEQ,
AJGE,
AJGT,
@@ -394,7 +396,9 @@ enum as
ACMPXCHGL,
ACMPXCHGW,
ACMPXCHG8B,
-
+
+ ARDTSC,
+
AXADDB,
AXADDL,
AXADDW,
@@ -442,6 +446,12 @@ enum as
AFCMOVNU,
AFCMOVUN,
+ ALFENCE,
+ AMFENCE,
+ ASFENCE,
+
+ AEMMS,
+
ALAST
};
diff --git a/src/cmd/8l/Makefile b/src/cmd/8l/Makefile
index 7d34e1704..3f528d751 100644
--- a/src/cmd/8l/Makefile
+++ b/src/cmd/8l/Makefile
@@ -1,49 +1,5 @@
-# Copyright 2009 The Go Authors. All rights reserved.
+# 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 ../../Make.inc
-O:=$(HOST_O)
-
-TARG=8l
-
-OFILES=\
- asm.$O\
- data.$O\
- dwarf.$O\
- elf.$O\
- enam.$O\
- go.$O\
- ldelf.$O\
- ldmacho.$O\
- ldpe.$O\
- lib.$O\
- list.$O\
- macho.$O\
- obj.$O\
- optab.$O\
- pass.$O\
- pe.$O\
- prof.$O\
- span.$O\
- symtab.$O\
-
-
-HFILES=\
- l.h\
- 8.out.h\
- ../ld/dwarf.h\
- ../ld/elf.h\
- ../ld/macho.h\
- ../ld/pe.h\
-
-include ../../Make.ccmd
-
-enam.c: 8.out.h
- sh mkenam
-
-CLEANFILES+=enam.c
-
-
-%.$O: ../ld/%.c
- $(HOST_CC) $(HOST_CFLAGS) -c -I. ../ld/$*.c
+include ../../Make.dist
diff --git a/src/cmd/8l/asm.c b/src/cmd/8l/asm.c
index 22abd8049..25ffc786f 100644
--- a/src/cmd/8l/asm.c
+++ b/src/cmd/8l/asm.c
@@ -37,10 +37,10 @@
#include "../ld/macho.h"
#include "../ld/pe.h"
-#define Dbufslop 100
-
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";
int32
entryvalue(void)
@@ -90,6 +90,9 @@ enum {
ElfStrPlt,
ElfStrGnuVersion,
ElfStrGnuVersionR,
+ ElfStrNoteNetbsdIdent,
+ ElfStrNoPtrData,
+ ElfStrNoPtrBss,
NElfStr
};
@@ -107,6 +110,7 @@ needlib(char *name)
/* reuse hash code in symbol table */
p = smprint(".dynlib.%s", name);
s = lookup(p, 0);
+ free(p);
if(s->type == 0) {
s->type = 100; // avoid SDATA, etc.
return 1;
@@ -416,7 +420,7 @@ adddynsym(Sym *s)
return;
if(s->dynimpname == nil)
- diag("adddynsym: no dynamic name for %s", s->name, *(int32*)0);
+ diag("adddynsym: no dynamic name for %s", s->name);
if(iself) {
s->dynid = nelfsym++;
@@ -524,8 +528,12 @@ doelf(void)
elfstr[ElfStrEmpty] = addstring(shstrtab, "");
elfstr[ElfStrText] = addstring(shstrtab, ".text");
+ elfstr[ElfStrNoPtrData] = addstring(shstrtab, ".noptrdata");
elfstr[ElfStrData] = addstring(shstrtab, ".data");
elfstr[ElfStrBss] = addstring(shstrtab, ".bss");
+ elfstr[ElfStrNoPtrBss] = addstring(shstrtab, ".noptrbss");
+ if(HEADTYPE == Hnetbsd)
+ elfstr[ElfStrNoteNetbsdIdent] = addstring(shstrtab, ".note.netbsd.ident");
addstring(shstrtab, ".elfdata");
addstring(shstrtab, ".rodata");
addstring(shstrtab, ".gosymtab");
@@ -606,7 +614,7 @@ doelf(void)
/* define dynamic elf table */
s = lookup(".dynamic", 0);
s->reachable = 1;
- s->type = SELFROSECT;
+ s->type = SELFSECT; // writable
/*
* .dynamic table
@@ -626,6 +634,8 @@ doelf(void)
elfwritedynentsymsize(s, DT_PLTRELSZ, lookup(".rel.plt", 0));
elfwritedynentsym(s, DT_JMPREL, lookup(".rel.plt", 0));
+ elfwritedynent(s, DT_DEBUG, 0);
+
// Do not write DT_NULL. elfdynhash will finish it.
}
}
@@ -657,7 +667,7 @@ asmb(void)
{
int32 v, magic;
int a, dynsym;
- uint32 symo, startva, machlink;
+ uint32 symo, startva, dwarfoff, machlink, resoff;
ElfEhdr *eh;
ElfPhdr *ph, *pph;
ElfShdr *sh;
@@ -688,8 +698,19 @@ asmb(void)
datblk(segdata.vaddr, segdata.filelen);
machlink = 0;
- if(HEADTYPE == Hdarwin)
+ if(HEADTYPE == Hdarwin) {
+ if(debug['v'])
+ Bprint(&bso, "%5.2f dwarf\n", cputime());
+
+ dwarfoff = rnd(HEADR+segtext.len, INITRND) + rnd(segdata.filelen, INITRND);
+ cseek(dwarfoff);
+
+ segdwarf.fileoff = cpos();
+ dwarfemitdebugsections();
+ segdwarf.filelen = cpos() - segdwarf.fileoff;
+
machlink = domacholink();
+ }
if(iself) {
/* index of elf text section; needed by asmelfsym, double-checked below */
@@ -700,6 +721,8 @@ asmb(void)
if(elfverneed)
elftextsh += 2;
}
+ if(HEADTYPE == Hnetbsd)
+ elftextsh += 1;
}
symsize = 0;
@@ -746,7 +769,7 @@ asmb(void)
default:
if(iself) {
if(debug['v'])
- Bprint(&bso, "%5.2f elfsym\n", cputime());
+ Bprint(&bso, "%5.2f elfsym\n", cputime());
asmelfsym();
cflush();
cwrite(elfstrdat, elfstrsize);
@@ -769,7 +792,6 @@ asmb(void)
cflush();
}
break;
- case Hdarwin:
case Hwindows:
if(debug['v'])
Bprint(&bso, "%5.2f dwarf\n", cputime());
@@ -918,6 +940,7 @@ asmb(void)
Elfput:
eh = getElfEhdr();
startva = INITTEXT - HEADR;
+ resoff = ELFRESERVE;
/* This null SHdr must appear before all others */
newElfShdr(elfstr[ElfStrEmpty]);
@@ -956,9 +979,15 @@ asmb(void)
case Hfreebsd:
interpreter = freebsddynld;
break;
+ case Hnetbsd:
+ interpreter = netbsddynld;
+ break;
+ case Hopenbsd:
+ interpreter = openbsddynld;
+ break;
}
}
- elfinterp(sh, startva, interpreter);
+ resoff -= elfinterp(sh, startva, resoff, interpreter);
ph = newElfPhdr();
ph->type = PT_INTERP;
@@ -966,11 +995,27 @@ asmb(void)
phsh(ph, sh);
}
+ if(HEADTYPE == Hnetbsd) {
+ sh = newElfShdr(elfstr[ElfStrNoteNetbsdIdent]);
+ sh->type = SHT_NOTE;
+ sh->flags = SHF_ALLOC;
+ sh->addralign = 4;
+ resoff -= elfnetbsdsig(sh, startva, resoff);
+
+ ph = newElfPhdr();
+ ph->type = PT_NOTE;
+ ph->flags = PF_R;
+ phsh(ph, sh);
+ }
+
+ // Additions to the reserved area must be above this line.
+ USED(resoff);
+
elfphload(&segtext);
elfphload(&segdata);
/* Dynamic linking sections */
- if (!debug['d']) { /* -d suppresses dynamic loader format */
+ if(!debug['d']) { /* -d suppresses dynamic loader format */
/* S headers for dynamic linking */
sh = newElfShdr(elfstr[ElfStrGot]);
sh->type = SHT_PROGBITS;
@@ -1094,7 +1139,7 @@ asmb(void)
for(sect=segdata.sect; sect!=nil; sect=sect->next)
elfshbits(sect);
- if (!debug['s']) {
+ if(!debug['s']) {
sh = newElfShdr(elfstr[ElfStrSymtab]);
sh->type = SHT_SYMTAB;
sh->off = symo;
@@ -1122,7 +1167,13 @@ asmb(void)
eh->ident[EI_VERSION] = EV_CURRENT;
switch(HEADTYPE) {
case Hfreebsd:
- eh->ident[EI_OSABI] = 9;
+ eh->ident[EI_OSABI] = ELFOSABI_FREEBSD;
+ break;
+ case Hnetbsd:
+ eh->ident[EI_OSABI] = ELFOSABI_NETBSD;
+ break;
+ case Hopenbsd:
+ eh->ident[EI_OSABI] = ELFOSABI_OPENBSD;
break;
}
@@ -1141,8 +1192,10 @@ asmb(void)
a += elfwritehdr();
a += elfwritephdrs();
a += elfwriteshdrs();
- cflush();
- if(a+elfwriteinterp() > ELFRESERVE)
+ a += elfwriteinterp(elfstr[ElfStrInterp]);
+ if(HEADTYPE == Hnetbsd)
+ a += elfwritenetbsdsig(elfstr[ElfStrNoteNetbsdIdent]);
+ if(a > ELFRESERVE)
diag("ELFRESERVE too small: %d > %d", a, ELFRESERVE);
break;
@@ -1205,12 +1258,16 @@ genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*))
case SSTRING:
case SGOSTRING:
case SWINDOWS:
+ case SNOPTRDATA:
+ case SSYMTAB:
+ case SPCLNTAB:
if(!s->reachable)
continue;
put(s, s->name, 'D', symaddr(s), s->size, s->version, s->gotype);
continue;
case SBSS:
+ case SNOPTRBSS:
if(!s->reachable)
continue;
put(s, s->name, 'B', symaddr(s), s->size, s->version, s->gotype);
diff --git a/src/cmd/8l/doc.go b/src/cmd/8l/doc.go
index b70888907..12301d4f2 100644
--- a/src/cmd/8l/doc.go
+++ b/src/cmd/8l/doc.go
@@ -4,47 +4,10 @@
/*
-8l is a modified version of the Plan 9 linker. The original is documented at
-
- http://plan9.bell-labs.com/magic/man2html/1/2l
-
-Its target architecture is the x86, referred to by these tools for historical reasons as 386.
-It reads files in .8 format generated by 8g, 8c, and 8a and emits
-a binary called 8.out by default.
-
-Major changes include:
- - support for ELF and Mach-O binary files
- - support for segmented stacks (this feature is implemented here, not in the compilers).
-
-
-Original options are listed in the link above.
-
-Options new in this version:
-
--d
- Elide the dynamic linking header. With this option, the binary
- is statically linked and does not refer to dynld. Without this option
- (the default), the binary's contents are identical but it is loaded with dynld.
--Hplan9
- Write Plan 9 32-bit format binaries (default when $GOOS is plan9)
--Hdarwin
- Write Apple Mach-O binaries (default when $GOOS is darwin)
--Hlinux
- Write Linux ELF binaries (default when $GOOS is linux)
--Hfreebsd
- Write FreeBSD ELF binaries (default when $GOOS is freebsd)
--Hwindows
- Write Windows PE32 binaries (default when $GOOS is windows)
--I interpreter
- Set the ELF dynamic linker to use.
--L dir1 -L dir2
- Search for libraries (package files) in dir1, dir2, etc.
- The default is the single location $GOROOT/pkg/$GOOS_386.
--r dir1:dir2:...
- Set the dynamic linker search path when using ELF.
--V
- Print the linker version.
+8l is the linker for the 32-bit x86.
+The $GOARCH for these tools is 386.
+The flags are documented in ../ld/doc.go.
*/
package documentation
diff --git a/src/cmd/8l/l.h b/src/cmd/8l/l.h
index 4ee0db967..b974f464b 100644
--- a/src/cmd/8l/l.h
+++ b/src/cmd/8l/l.h
@@ -134,6 +134,7 @@ struct Sym
int32 dynid;
int32 plt;
int32 got;
+ int32 align; // if non-zero, required alignment in bytes
Sym* hash; // in hash table
Sym* allsym; // in all symbol list
Sym* next; // in text or data list
@@ -209,6 +210,7 @@ enum
Zbr,
Zcall,
Zcallcon,
+ Zcallind,
Zib_,
Zib_rp,
Zibo_m,
diff --git a/src/cmd/8l/obj.c b/src/cmd/8l/obj.c
index a8e1c34a5..af4bc844f 100644
--- a/src/cmd/8l/obj.c
+++ b/src/cmd/8l/obj.c
@@ -47,17 +47,19 @@ char *noname = "<none>";
char *thestring = "386";
Header headers[] = {
- "garbunix", Hgarbunix,
- "unixcoff", Hunixcoff,
- "plan9", Hplan9x32,
- "msdoscom", Hmsdoscom,
- "msdosexe", Hmsdosexe,
- "darwin", Hdarwin,
- "linux", Hlinux,
- "freebsd", Hfreebsd,
- "windows", Hwindows,
- "windowsgui", Hwindows,
- 0, 0
+ "garbunix", Hgarbunix,
+ "unixcoff", Hunixcoff,
+ "plan9", Hplan9x32,
+ "msdoscom", Hmsdoscom,
+ "msdosexe", Hmsdosexe,
+ "darwin", Hdarwin,
+ "linux", Hlinux,
+ "freebsd", Hfreebsd,
+ "netbsd", Hnetbsd,
+ "openbsd", Hopenbsd,
+ "windows", Hwindows,
+ "windowsgui", Hwindows,
+ 0, 0
};
/*
@@ -69,6 +71,8 @@ Header headers[] = {
* -Hdarwin -Tx -Rx is Apple Mach-O
* -Hlinux -Tx -Rx is Linux ELF32
* -Hfreebsd -Tx -Rx is FreeBSD ELF32
+ * -Hnetbsd -Tx -Rx is NetBSD ELF32
+ * -Hopenbsd -Tx -Rx is OpenBSD ELF32
* -Hwindows -Tx -Rx is MS Windows PE32
*/
@@ -83,6 +87,7 @@ void
main(int argc, char *argv[])
{
int c;
+ char *name, *val;
Binit(&bso, 1, OWRITE);
listinit();
@@ -94,6 +99,7 @@ main(int argc, char *argv[])
INITDAT = -1;
INITRND = -1;
INITENTRY = 0;
+ nuxiinit();
ARGBEGIN {
default:
@@ -133,6 +139,11 @@ main(int argc, char *argv[])
case 'V':
print("%cl version %s\n", thechar, getgoversion());
errorexit();
+ case 'X':
+ name = EARGF(usage());
+ val = EARGF(usage());
+ addstrdata(name, val);
+ break;
} ARGEND
if(argc != 1)
@@ -209,7 +220,7 @@ main(int argc, char *argv[])
case Hdarwin: /* apple MACH */
/*
* OS X system constant - offset from %gs to our TLS.
- * Explained in ../../libcgo/darwin_386.c.
+ * Explained in ../../pkg/runtime/cgo/gcc_darwin_386.c.
*/
tlsoffset = 0x468;
machoinit();
@@ -223,11 +234,13 @@ main(int argc, char *argv[])
break;
case Hlinux: /* elf32 executable */
case Hfreebsd:
+ case Hnetbsd:
+ case Hopenbsd:
/*
* ELF uses TLS offsets negative from %gs.
* Translate 0(GS) and 4(GS) into -8(GS) and -4(GS).
- * Also known to ../../pkg/runtime/linux/386/sys.s
- * and ../../libcgo/linux_386.c.
+ * Also known to ../../pkg/runtime/sys_linux_386.s
+ * and ../../pkg/runtime/cgo/gcc_linux_386.c.
*/
tlsoffset = -8;
elfinit();
@@ -269,7 +282,6 @@ main(int argc, char *argv[])
zprg.to = zprg.from;
pcstr = "%.6ux ";
- nuxiinit();
histgen = 0;
pc = 0;
dtype = 4;
@@ -477,7 +489,7 @@ loop:
sig = 1729;
if(sig != 0){
if(s->sig != 0 && s->sig != sig)
- diag("incompatible type signatures"
+ diag("incompatible type signatures "
"%ux(%s) and %ux(%s) for %s",
s->sig, s->file, sig, pn, s->name);
s->sig = sig;
@@ -551,7 +563,7 @@ loop:
s->type = SBSS;
s->size = 0;
}
- if(s->type != SBSS && !s->dupok) {
+ if(s->type != SBSS && s->type != SNOPTRBSS && !s->dupok) {
diag("%s: redefinition: %s in %s",
pn, s->name, TNAME);
s->type = SBSS;
@@ -563,6 +575,8 @@ loop:
s->dupok = 1;
if(p->from.scale & RODATA)
s->type = SRODATA;
+ else if(p->from.scale & NOPTR)
+ s->type = SNOPTRBSS;
goto loop;
case ADATA:
@@ -594,6 +608,10 @@ loop:
case ATEXT:
s = p->from.sym;
if(s->text != nil) {
+ if(p->from.scale & DUPOK) {
+ skip = 1;
+ goto casdef;
+ }
diag("%s: %s: redefinition", pn, s->name);
return;
}
diff --git a/src/cmd/8l/optab.c b/src/cmd/8l/optab.c
index f5c195d75..856482290 100644
--- a/src/cmd/8l/optab.c
+++ b/src/cmd/8l/optab.c
@@ -260,6 +260,7 @@ uchar yloop[] =
uchar ycall[] =
{
Ynone, Yml, Zo_m, 2,
+ Ynone, Ycol, Zcallind, 2,
Ynone, Ybr, Zcall, 0,
Ynone, Yi32, Zcallcon, 1,
0
@@ -383,7 +384,7 @@ Optab optab[] =
{ ABTSL, yml_rl, Pm, 0xab },
{ ABTSW, yml_rl, Pq, 0xab },
{ ABYTE, ybyte, Px, 1 },
- { ACALL, ycall, Px, 0xff,(02),0xe8 },
+ { ACALL, ycall, Px, 0xff,(02),0xff,(0x15),0xe8 },
{ ACLC, ynone, Px, 0xf8 },
{ ACLD, ynone, Px, 0xfc },
{ ACLI, ynone, Px, 0xfa },
@@ -430,7 +431,8 @@ Optab optab[] =
{ AIRETW, ynone, Pe, 0xcf },
{ AJCC, yjcond, Px, 0x73,0x83,(00) },
{ AJCS, yjcond, Px, 0x72,0x82 },
- { AJCXZ, yloop, Px, 0xe3 },
+ { AJCXZL, yloop, Px, 0xe3 },
+ { AJCXZW, yloop, Px, 0xe3 },
{ AJEQ, yjcond, Px, 0x74,0x84 },
{ AJGE, yjcond, Px, 0x7d,0x8d },
{ AJGT, yjcond, Px, 0x7f,0x8f },
@@ -705,6 +707,8 @@ Optab optab[] =
{ ACMPXCHGW, yrl_ml, Pm, 0xb1 },
{ ACMPXCHG8B, yscond, Pm, 0xc7,(01) },
+ { ARDTSC, ynone, Pm, 0x31 },
+
{ AXADDB, yrb_mb, Pb, 0x0f,0xc0 },
{ AXADDL, yrl_ml, Pm, 0xc1 },
{ AXADDW, yrl_ml, Pe, 0x0f,0xc1 },
@@ -751,5 +755,11 @@ Optab optab[] =
{ AFCMOVNU, yfcmv, Px, 0xdb,(03) },
{ AFCMOVUN, yfcmv, Px, 0xda,(03) },
+ { ALFENCE, ynone, Pm, 0xae,0xe8 },
+ { AMFENCE, ynone, Pm, 0xae,0xf0 },
+ { ASFENCE, ynone, Pm, 0xae,0xf8 },
+
+ { AEMMS, ynone, Pm, 0x77 },
+
0
};
diff --git a/src/cmd/8l/pass.c b/src/cmd/8l/pass.c
index 2e0990c5a..9034fdf3a 100644
--- a/src/cmd/8l/pass.c
+++ b/src/cmd/8l/pass.c
@@ -259,7 +259,7 @@ patch(void)
// Convert
// op n(GS), reg
// to
- // MOVL 0x2C(FS), reg
+ // MOVL 0x14(FS), reg
// op n(reg), reg
// The purpose of this patch is to fix some accesses
// to extern register variables (TLS) on Windows, as
@@ -273,7 +273,7 @@ patch(void)
q->as = p->as;
p->as = AMOVL;
p->from.type = D_INDIR+D_FS;
- p->from.offset = 0x2C;
+ p->from.offset = 0x14;
}
}
if(HEADTYPE == Hlinux) {
@@ -307,9 +307,12 @@ patch(void)
p->from.offset = 0;
}
}
- if(p->as == ACALL || (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)) {
s = p->to.sym;
- if(s) {
+ if(p->to.type == D_INDIR+D_ADDR) {
+ /* skip check if this is an indirect call (CALL *symbol(SB)) */
+ continue;
+ } else if(s) {
if(debug['c'])
Bprint(&bso, "%s calls %s\n", TNAME, s->name);
if((s->type&~SSUB) != STEXT) {
@@ -421,7 +424,7 @@ dostkoff(void)
case Hwindows:
p->as = AMOVL;
p->from.type = D_INDIR+D_FS;
- p->from.offset = 0x2c;
+ p->from.offset = 0x14;
p->to.type = D_CX;
p = appendp(p);
@@ -524,10 +527,18 @@ dostkoff(void)
p = appendp(p); // save frame size in DX
p->as = AMOVL;
p->to.type = D_DX;
- /* 160 comes from 3 calls (3*8) 4 safes (4*8) and 104 guard */
p->from.type = D_CONST;
- if(autoffset+160+cursym->text->to.offset2 > 4096)
- p->from.offset = (autoffset+160) & ~7LL;
+
+ // 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;
diff --git a/src/cmd/8l/span.c b/src/cmd/8l/span.c
index a4cba1257..81c1d37eb 100644
--- a/src/cmd/8l/span.c
+++ b/src/cmd/8l/span.c
@@ -83,7 +83,10 @@ span1(Sym *s)
loop++;
q->back ^= 2;
}
- s->p[q->pc+1] = v;
+ if(q->as == AJCXZW)
+ s->p[q->pc+2] = v;
+ else
+ s->p[q->pc+1] = v;
} else {
bp = s->p + q->pc + q->mark - 4;
*bp++ = v;
@@ -282,6 +285,8 @@ oclass(Adr *a)
}
return Yxxx;
}
+ //if(a->type == D_INDIR+D_ADDR)
+ // print("*Ycol\n");
return Ycol;
}
return Ym;
@@ -1056,9 +1061,10 @@ found:
case Zbr:
case Zjmp:
+ case Zloop:
q = p->pcond;
if(q == nil) {
- diag("jmp/branch without target");
+ diag("jmp/branch/loop without target");
errorexit();
}
if(q->as == ATEXT) {
@@ -1084,8 +1090,12 @@ found:
if(p->back & 1) {
v = q->pc - (p->pc + 2);
if(v >= -128) {
+ if(p->as == AJCXZW)
+ *andptr++ = 0x67;
*andptr++ = op;
*andptr++ = v;
+ } else if(t[2] == Zloop) {
+ diag("loop too far: %P", p);
} else {
v -= 5-2;
if(t[2] == Zbr) {
@@ -1105,8 +1115,12 @@ found:
p->forwd = q->comefrom;
q->comefrom = p;
if(p->back & 2) { // short
+ if(p->as == AJCXZW)
+ *andptr++ = 0x67;
*andptr++ = op;
*andptr++ = 0;
+ } else if(t[2] == Zloop) {
+ diag("loop too far: %P", p);
} else {
if(t[2] == Zbr)
*andptr++ = 0x0f;
@@ -1131,18 +1145,17 @@ found:
r->add = p->to.offset;
put4(0);
break;
-
- case Zloop:
- q = p->pcond;
- if(q == nil) {
- diag("loop without target");
- errorexit();
- }
- v = q->pc - p->pc - 2;
- if(v < -128 && v > 127)
- diag("loop too far: %P", p);
+
+ case Zcallind:
*andptr++ = op;
- *andptr++ = v;
+ *andptr++ = o->op[z+1];
+ r = addrel(cursym);
+ r->off = p->pc + andptr - and;
+ r->type = D_ADDR;
+ r->siz = 4;
+ r->add = p->to.offset;
+ r->sym = p->to.sym;
+ put4(0);
break;
case Zbyte:
diff --git a/src/cmd/Makefile b/src/cmd/Makefile
deleted file mode 100644
index 5a37733de..000000000
--- a/src/cmd/Makefile
+++ /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.
-
-include ../Make.inc
-
-all: install
-
-# Only build tools for current architecture, and only tools written in C.
-# The tools written in Go are managed by ../pkg/Makefile.
-DIRS=\
- $(O)a\
- $(O)c\
- $(O)g\
- $(O)l\
- cc\
- cov\
- gc\
- godefs\
- gopack\
- gotry\
- nm\
- prof\
-
-# Clean applies to all directories, even for other architectures or
-# written in Go.
-CLEANDIRS=\
- $(DIRS)\
- 5a\
- 5c\
- 5g\
- 5l\
- 6a\
- 6c\
- 6g\
- 6l\
- 8a\
- 8c\
- 8g\
- 8l\
- cgo\
- ebnflint\
- godoc\
- gofix\
- gofmt\
- goinstall\
- gotest\
- gotype\
- goyacc\
- hgpatch\
-
-install: $(patsubst %,%.install,$(DIRS))
-clean: $(patsubst %,%.clean,$(CLEANDIRS))
-
-%.install:
- @echo
- @echo %%%% making $* %%%%
- @echo
- $(MAKE) -C $* install
-
-gc.install $(O)c.install: cc.install
-$(O)g.install: gc.install
-$(O)a.install $(O)c.install $(O)g.install: $(O)l.install
-
-%.clean:
- $(MAKE) -C $* clean
-
-echo-dirs:
- @echo $(DIRS)
diff --git a/src/cmd/addr2line/main.c b/src/cmd/addr2line/main.c
new file mode 100644
index 000000000..6b2fe5dfe
--- /dev/null
+++ b/src/cmd/addr2line/main.c
@@ -0,0 +1,68 @@
+// 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.
+
+/*
+ * addr2line simulation - only enough to make pprof work on Macs
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+
+void
+usage(void)
+{
+ fprint(2, "usage: addr2line binary\n");
+ fprint(2, "reads addresses from standard input and writes two lines for each:\n");
+ fprint(2, "\tfunction name\n");
+ fprint(2, "\tfile:line\n");
+ exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+ int fd;
+ char *p;
+ uvlong pc;
+ Symbol s;
+ Fhdr fhdr;
+ Biobuf bin, bout;
+ char file[1024];
+
+ ARGBEGIN{
+ default:
+ usage();
+ }ARGEND
+
+ if(argc != 1)
+ usage();
+
+ fd = open(argv[0], OREAD);
+ if(fd < 0)
+ sysfatal("open %s: %r", argv[0]);
+ if(crackhdr(fd, &fhdr) <= 0)
+ sysfatal("crackhdr: %r");
+ machbytype(fhdr.type);
+ if(syminit(fd, &fhdr) <= 0)
+ sysfatal("syminit: %r");
+
+ Binit(&bin, 0, OREAD);
+ Binit(&bout, 1, OWRITE);
+ for(;;) {
+ p = Brdline(&bin, '\n');
+ if(p == nil)
+ break;
+ p[Blinelen(&bin)-1] = '\0';
+ pc = strtoull(p, 0, 16);
+ if(!findsym(pc, CTEXT, &s))
+ s.name = "??";
+ if(!fileline(file, sizeof file, pc))
+ strcpy(file, "??:0");
+ Bprint(&bout, "%s\n%s\n", s.name, file);
+ }
+ Bflush(&bout);
+ exits(0);
+}
diff --git a/src/cmd/api/goapi.go b/src/cmd/api/goapi.go
new file mode 100644
index 000000000..7363f6d82
--- /dev/null
+++ b/src/cmd/api/goapi.go
@@ -0,0 +1,1005 @@
+// 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.
+
+// 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.
+package main
+
+import (
+ "bufio"
+ "bytes"
+ "errors"
+ "flag"
+ "fmt"
+ "go/ast"
+ "go/build"
+ "go/doc"
+ "go/parser"
+ "go/printer"
+ "go/token"
+ "io/ioutil"
+ "log"
+ "os"
+ "os/exec"
+ "path"
+ "path/filepath"
+ "sort"
+ "strconv"
+ "strings"
+)
+
+// Flags
+var (
+ checkFile = flag.String("c", "", "optional filename to check API against")
+ verbose = flag.Bool("v", false, "Verbose debugging")
+)
+
+var contexts = []*build.Context{
+ {GOOS: "linux", GOARCH: "386", CgoEnabled: true},
+ {GOOS: "linux", GOARCH: "386"},
+ {GOOS: "linux", GOARCH: "amd64", CgoEnabled: true},
+ {GOOS: "linux", GOARCH: "amd64"},
+ {GOOS: "darwin", GOARCH: "386", CgoEnabled: true},
+ {GOOS: "darwin", GOARCH: "386"},
+ {GOOS: "darwin", GOARCH: "amd64", CgoEnabled: true},
+ {GOOS: "darwin", GOARCH: "amd64"},
+ {GOOS: "windows", GOARCH: "amd64"},
+ {GOOS: "windows", GOARCH: "386"},
+}
+
+func init() {
+ for _, c := range contexts {
+ c.Compiler = build.Default.Compiler
+ }
+}
+
+func contextName(c *build.Context) string {
+ s := c.GOOS + "-" + c.GOARCH
+ if c.CgoEnabled {
+ return s + "-cgo"
+ }
+ return s
+}
+
+func main() {
+ flag.Parse()
+
+ var pkgs []string
+ if flag.NArg() > 0 {
+ pkgs = flag.Args()
+ } else {
+ stds, err := exec.Command("go", "list", "std").Output()
+ if err != nil {
+ log.Fatal(err)
+ }
+ pkgs = 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/") ||
+ strings.HasPrefix(pkg, "exp/") ||
+ strings.HasPrefix(pkg, "old/") {
+ 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.WalkPackage(pkg)
+ }
+ ctxName := contextName(context)
+ for _, f := range w.Features() {
+ if featureCtx[f] == nil {
+ featureCtx[f] = make(map[string]bool)
+ }
+ featureCtx[f][ctxName] = true
+ }
+ }
+
+ var features []string
+ for f, cmap := range featureCtx {
+ if len(cmap) == len(contexts) {
+ features = append(features, f)
+ continue
+ }
+ comma := strings.Index(f, ",")
+ for cname := range cmap {
+ f2 := fmt.Sprintf("%s (%s)%s", f[:comma], cname, f[comma:])
+ features = append(features, f2)
+ }
+ }
+ sort.Strings(features)
+
+ bw := bufio.NewWriter(os.Stdout)
+ defer bw.Flush()
+
+ if *checkFile != "" {
+ bs, err := ioutil.ReadFile(*checkFile)
+ if err != nil {
+ log.Fatalf("Error reading file %s: %v", *checkFile, err)
+ }
+ v1 := strings.Split(strings.TrimSpace(string(bs)), "\n")
+ sort.Strings(v1)
+ v2 := features
+ take := func(sl *[]string) string {
+ s := (*sl)[0]
+ *sl = (*sl)[1:]
+ return s
+ }
+ changes := false
+ for len(v1) > 0 || len(v2) > 0 {
+ switch {
+ case len(v2) == 0 || v1[0] < v2[0]:
+ fmt.Fprintf(bw, "-%s\n", take(&v1))
+ changes = true
+ case len(v1) == 0 || v1[0] > v2[0]:
+ fmt.Fprintf(bw, "+%s\n", take(&v2))
+ changes = true
+ default:
+ take(&v1)
+ take(&v2)
+ }
+ }
+ if changes {
+ bw.Flush()
+ os.Exit(1)
+ }
+ } else {
+ for _, f := range features {
+ fmt.Fprintf(bw, "%s\n", f)
+ }
+ }
+}
+
+// pkgSymbol represents a symbol in a package
+type pkgSymbol struct {
+ pkg string // "net/http"
+ symbol string // "RoundTripper"
+}
+
+type Walker struct {
+ context *build.Context
+ root string
+ fset *token.FileSet
+ 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
+}
+
+func NewWalker() *Walker {
+ return &Walker{
+ fset: token.NewFileSet(),
+ 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"),
+ }
+}
+
+// loadState is the state of a package's parsing.
+type loadState int
+
+const (
+ notLoaded loadState = iota
+ loading
+ loaded
+)
+
+// hardCodedConstantType is a hack until the type checker is sufficient for our needs.
+// Rather than litter the code with unnecessary type annotations, we'll hard-code
+// the cases we can't handle yet.
+func (w *Walker) hardCodedConstantType(name string) (typ string, ok bool) {
+ switch w.scope[0] {
+ case "pkg syscall":
+ switch name {
+ case "darwinAMD64":
+ return "bool", true
+ }
+ }
+ return "", false
+}
+
+func (w *Walker) Features() (fs []string) {
+ for f := range w.features {
+ fs = append(fs, f)
+ }
+ sort.Strings(fs)
+ 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)
+ if err != nil {
+ log.Fatalf("error unquoting import string %q: %v", is.Path.Value, err)
+ }
+ if fpkg != "C" {
+ pkgs = append(pkgs, fpkg)
+ }
+ }
+ return
+}
+
+// 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
+ }
+ w.packageState[name] = loading
+ defer func() {
+ w.packageState[name] = loaded
+ }()
+ dir := filepath.Join(w.root, filepath.FromSlash(name))
+
+ ctxt := w.context
+ if ctxt == nil {
+ ctxt = &build.Default
+ }
+ 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)
+ }
+
+ apkg := &ast.Package{
+ Files: make(map[string]*ast.File),
+ }
+
+ files := append(append([]string{}, info.GoFiles...), info.CgoFiles...)
+ for _, file := range files {
+ f, err := parser.ParseFile(w.fset, filepath.Join(dir, file), nil, 0)
+ if err != nil {
+ log.Fatalf("error parsing package %s, file %s: %v", name, file, err)
+ }
+ apkg.Files[file] = f
+
+ for _, dep := range fileDeps(f) {
+ w.WalkPackage(dep)
+ }
+ }
+
+ if *verbose {
+ log.Printf("package %s", name)
+ }
+ pop := w.pushScope("pkg " + name)
+ defer pop()
+
+ w.curPackageName = name
+ w.curPackage = apkg
+ w.constDep = map[string]string{}
+
+ for _, afile := range apkg.Files {
+ w.recordTypes(afile)
+ }
+
+ // 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)
+ }
+ }
+ }
+
+ for _, afile := range apkg.Files {
+ w.walkFile(afile)
+ }
+
+ w.resolveConstantDeps()
+
+ // 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)
+
+ for _, t := range dpkg.Types {
+ // Move funcs up to the top-level, not hiding in the Types.
+ dpkg.Funcs = append(dpkg.Funcs, t.Funcs...)
+
+ for _, m := range t.Methods {
+ w.walkFuncDecl(m.Decl)
+ }
+ }
+
+ for _, f := range dpkg.Funcs {
+ w.walkFuncDecl(f.Decl)
+ }
+}
+
+// pushScope enters a new scope (walking a package, type, node, etc)
+// and returns a function that will leave the scope (with sanity checking
+// for mismatched pushes & pops)
+func (w *Walker) pushScope(name string) (popFunc func()) {
+ w.scope = append(w.scope, name)
+ return func() {
+ if len(w.scope) == 0 {
+ log.Fatalf("attempt to leave scope %q with empty scope list", name)
+ }
+ if w.scope[len(w.scope)-1] != name {
+ log.Fatalf("attempt to leave scope %q, but scope is currently %#v", name, w.scope)
+ }
+ w.scope = w.scope[:len(w.scope)-1]
+ }
+}
+
+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.
+ default:
+ log.Printf("unhandled %T, %#v\n", di, di)
+ printer.Fprint(os.Stderr, w.fset, di)
+ os.Stderr.Write([]byte("\n"))
+ }
+ }
+}
+
+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")
+
+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)
+ }
+ 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:
+ 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)
+}
+
+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("unreachable")
+}
+
+// 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
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ 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 {
+ if t, ok := w.hardCodedConstantType(ident.Name); ok {
+ litType = t
+ err = nil
+ } else {
+ log.Fatalf("unknown kind in const %q (%T): %v", ident.Name, vs.Values[0], err)
+ }
+ }
+ }
+ }
+ if strings.HasPrefix(litType, constDepPrefix) {
+ dep := litType[len(constDepPrefix):]
+ w.constDep[ident.Name] = dep
+ continue
+ }
+ if litType == "" {
+ log.Fatalf("unknown kind in const %q", ident.Name)
+ }
+ 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))
+ }
+ }
+}
+
+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) 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, w.fset, node)
+ return b.String()
+}
+
+func (w *Walker) nodeDebug(node interface{}) string {
+ if node == nil {
+ return ""
+ }
+ var b bytes.Buffer
+ ast.Fprint(&b, w.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)
+ default:
+ w.emitFeature(fmt.Sprintf("type %s %s", name, w.nodeString(ts.Type)))
+ }
+}
+
+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))))
+ }
+ }
+ 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)
+ }
+ }
+ }
+}
+
+// method is a method of an interface.
+type method struct {
+ name string // "Read"
+ sig string // "([]byte) (int, error)", from funcSigString
+}
+
+// interfaceMethods returns the expanded list of methods for an interface.
+// pkg is the complete package name ("net/http")
+// iname is the interface name.
+func (w *Walker) interfaceMethods(pkg, iname string) (methods []method) {
+ t, ok := w.interfaces[pkgSymbol{pkg, iname}]
+ if !ok {
+ log.Fatalf("failed to find interface %s.%s", pkg, iname)
+ }
+
+ 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),
+ })
+ }
+ }
+ 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)
+ }
+ methods = append(methods, w.interfaceMethods(pkg, embedded)...)
+ 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)
+ }
+ methods = append(methods, w.interfaceMethods(fpkg, rhs)...)
+ default:
+ log.Fatalf("unknown type %T in interface field", typ)
+ }
+ }
+ return
+}
+
+func (w *Walker) walkInterfaceType(name string, t *ast.InterfaceType) {
+ methNames := []string{}
+
+ pop := w.pushScope("type " + name + " interface")
+ for _, m := range w.interfaceMethods(w.curPackageName, name) {
+ methNames = append(methNames, m.name)
+ w.emitFeature(fmt.Sprintf("%s%s", m.name, m.sig))
+ }
+ pop()
+
+ 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 {
+ 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
+ b.WriteByte('(')
+ if ft.Params != nil {
+ for i, f := range ft.Params.List {
+ if i > 0 {
+ b.WriteString(", ")
+ }
+ b.WriteString(w.nodeString(w.namelessType(f.Type)))
+ }
+ }
+ b.WriteByte(')')
+ if ft.Results != nil {
+ if nr := len(ft.Results.List); nr > 0 {
+ b.WriteByte(' ')
+ if nr > 1 {
+ b.WriteByte('(')
+ }
+ for i, f := range ft.Results.List {
+ if i > 0 {
+ b.WriteString(", ")
+ }
+ b.WriteString(w.nodeString(w.namelessType(f.Type)))
+ }
+ if nr > 1 {
+ b.WriteByte(')')
+ }
+ }
+ }
+ return b.String()
+}
+
+// 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),
+ }
+}
+
+// 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 {
+ fl2.List = append(fl2.List, w.namelessField(f))
+ }
+ }
+ return fl2
+}
+
+// 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) emitFeature(feature string) {
+ if !w.wantedPkg[w.curPackageName] {
+ return
+ }
+ 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
new file mode 100644
index 000000000..c7cc601b1
--- /dev/null
+++ b/src/cmd/api/goapi_test.go
@@ -0,0 +1,75 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "sort"
+ "strings"
+ "testing"
+)
+
+var (
+ updateGolden = flag.Bool("updategolden", false, "update golden files")
+)
+
+func TestGolden(t *testing.T) {
+ td, err := os.Open("testdata/src/pkg")
+ if err != nil {
+ t.Fatal(err)
+ }
+ fis, err := td.Readdir(0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ for _, fi := range fis {
+ 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())
+
+ if *updateGolden {
+ os.Remove(goldenFile)
+ f, err := os.Create(goldenFile)
+ if err != nil {
+ t.Fatal(err)
+ }
+ for _, feat := range w.Features() {
+ fmt.Fprintf(f, "%s\n", feat)
+ }
+ f.Close()
+ }
+
+ bs, err := ioutil.ReadFile(goldenFile)
+ if err != nil {
+ t.Fatalf("opening golden.txt for package %q: %v", fi.Name(), err)
+ }
+ wanted := strings.Split(string(bs), "\n")
+ sort.Strings(wanted)
+ for _, feature := range wanted {
+ if feature == "" {
+ continue
+ }
+ _, ok := w.features[feature]
+ if !ok {
+ t.Errorf("package %s: missing feature %q", fi.Name(), feature)
+ }
+ delete(w.features, feature)
+ }
+
+ for _, feature := range w.Features() {
+ t.Errorf("package %s: extra feature not in golden file: %q", fi.Name(), feature)
+ }
+ }
+}
diff --git a/src/cmd/api/testdata/src/pkg/p1/golden.txt b/src/cmd/api/testdata/src/pkg/p1/golden.txt
new file mode 100644
index 000000000..e334e5776
--- /dev/null
+++ b/src/cmd/api/testdata/src/pkg/p1/golden.txt
@@ -0,0 +1,73 @@
+pkg p1, const A ideal-int
+pkg p1, const A64 int64
+pkg p1, const AIsLowerA ideal-int
+pkg p1, const B ideal-int
+pkg p1, const ConstChase2 ideal-int
+pkg p1, const ConversionConst MyInt
+pkg p1, const FloatConst ideal-float
+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 TakesFunc(func(int) int)
+pkg p1, method (*B) JustOnB()
+pkg p1, method (*B) OnBothTandBPtr()
+pkg p1, method (*Embedded) OnEmbedded()
+pkg p1, method (*S2) SMethod(int8, int16, int64)
+pkg p1, method (*T) JustOnT()
+pkg p1, method (*T) OnBothTandBPtr()
+pkg p1, method (B) OnBothTandBVal()
+pkg p1, method (S) StructValueMethod()
+pkg p1, method (S) StructValueMethodNamedRecv()
+pkg p1, method (S2) StructValueMethod()
+pkg p1, method (S2) StructValueMethodNamedRecv()
+pkg p1, method (T) OnBothTandBVal()
+pkg p1, method (TPtrExported) OnEmbedded()
+pkg p1, method (TPtrUnexported) OnBothTandBPtr()
+pkg p1, method (TPtrUnexported) OnBothTandBVal()
+pkg p1, type B struct
+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 EmbedURLPtr struct
+pkg p1, type EmbedURLPtr struct, embedded *url.URL
+pkg p1, type Embedded struct
+pkg p1, type Error interface { Error, Temporary }
+pkg p1, type Error interface, Error() string
+pkg p1, type Error interface, Temporary() bool
+pkg p1, type I interface { Get, GetNamed, Name, PackageTwoMeth, Set }
+pkg p1, type I interface, Get(string) int64
+pkg p1, type I interface, GetNamed(string) int64
+pkg p1, type I interface, Name() string
+pkg p1, type I interface, PackageTwoMeth()
+pkg p1, type I interface, Set(string, int64)
+pkg p1, type MyInt int
+pkg p1, type Namer interface { Name }
+pkg p1, type Namer interface, Name() string
+pkg p1, type S struct
+pkg p1, type S struct, Public *int
+pkg p1, type S struct, PublicTime time.Time
+pkg p1, type S2 struct
+pkg p1, type S2 struct, Extra bool
+pkg p1, type S2 struct, embedded S
+pkg p1, type SI struct
+pkg p1, type SI struct, I int
+pkg p1, type T struct
+pkg p1, type TPtrExported struct
+pkg p1, type TPtrExported struct, embedded *Embedded
+pkg p1, type TPtrUnexported struct
+pkg p1, var ByteConv []byte
+pkg p1, var ChecksumError error
+pkg p1, var SIPtr *SI
+pkg p1, var SIPtr2 *SI
+pkg p1, var SIVal SI
+pkg p1, var StrConv string
+pkg p1, var V string
+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 Y int
diff --git a/src/cmd/api/testdata/src/pkg/p1/p1.go b/src/cmd/api/testdata/src/pkg/p1/p1.go
new file mode 100644
index 000000000..d965bb75e
--- /dev/null
+++ b/src/cmd/api/testdata/src/pkg/p1/p1.go
@@ -0,0 +1,153 @@
+package p1
+
+import (
+ ptwo "p2"
+)
+
+const (
+ ConstChase2 = constChase // forward declaration to unexported ident
+ constChase = AIsLowerA // forward declaration to exported ident
+
+ A = 1
+ a = 11
+ A64 int64 = 1
+
+ AIsLowerA = a // previously declared
+)
+
+const (
+ ConversionConst = MyInt(5)
+)
+
+// Variables from function calls.
+var (
+ V = ptwo.F()
+ VError = BarE()
+ V1 = Bar1(1, 2, 3)
+ V2 = ptwo.G()
+)
+
+// Variables with conversions:
+var (
+ StrConv = string("foo")
+ ByteConv = []byte("foo")
+)
+
+var ChecksumError = ptwo.NewError("gzip checksum error")
+
+const B = 2
+const StrConst = "foo"
+const FloatConst = 1.5
+
+type myInt int
+
+type MyInt int
+
+type S struct {
+ Public *int
+ private *int
+ PublicTime time.Time
+}
+
+type EmbedURLPtr struct {
+ *url.URL
+}
+
+type S2 struct {
+ S
+ Extra bool
+}
+
+var X int64
+
+var (
+ Y int
+ X I
+)
+
+type Namer interface {
+ Name() string
+}
+
+type I interface {
+ Namer
+ ptwo.Twoer
+ Set(name string, balance int64)
+ Get(string) int64
+ GetNamed(string) (balance int64)
+ private()
+}
+
+type Error interface {
+ error
+ Temporary() bool
+}
+
+func (myInt) privateTypeMethod() {}
+func (myInt) CapitalMethodUnexportedType() {}
+
+func (s *S2) SMethod(x int8, y int16, z int64) {}
+
+type s struct{}
+
+func (s) method()
+func (s) Method()
+
+func (S) StructValueMethod()
+func (ignored S) StructValueMethodNamedRecv()
+
+func (s *S2) unexported(x int8, y int16, z int64) {}
+
+func Bar(x int8, y int16, z int64) {}
+func Bar1(x int8, y int16, z int64) uint64 {}
+func Bar2(x int8, y int16, z int64) (uint8, uint64) {}
+func BarE() Error {}
+
+func unexported(x int8, y int16, z int64) {}
+
+func TakesFunc(f func(dontWantName int) int)
+
+type Codec struct {
+ Func func(x int, y int) (z int)
+}
+
+type SI struct {
+ I int
+}
+
+var SIVal = SI{}
+var SIPtr = &SI{}
+var SIPtr2 *SI
+
+type T struct {
+ common
+}
+
+type B struct {
+ common
+}
+
+type common struct {
+ i int
+}
+
+type TPtrUnexported struct {
+ *common
+}
+
+type TPtrExported struct {
+ *Embedded
+}
+
+type Embedded struct{}
+
+func (*Embedded) OnEmbedded() {}
+
+func (*T) JustOnT() {}
+func (*B) JustOnB() {}
+func (*common) OnBothTandBPtr() {}
+func (common) OnBothTandBVal() {}
+
+type EmbedSelector struct {
+ time.Time
+}
diff --git a/src/cmd/api/testdata/src/pkg/p2/golden.txt b/src/cmd/api/testdata/src/pkg/p2/golden.txt
new file mode 100644
index 000000000..4271620c7
--- /dev/null
+++ b/src/cmd/api/testdata/src/pkg/p2/golden.txt
@@ -0,0 +1,5 @@
+pkg p2, func F() string
+pkg p2, func G() Twoer
+pkg p2, func NewError(string) error
+pkg p2, type Twoer interface { PackageTwoMeth }
+pkg p2, type Twoer interface, PackageTwoMeth()
diff --git a/src/cmd/api/testdata/src/pkg/p2/p2.go b/src/cmd/api/testdata/src/pkg/p2/p2.go
new file mode 100644
index 000000000..6b107b507
--- /dev/null
+++ b/src/cmd/api/testdata/src/pkg/p2/p2.go
@@ -0,0 +1,9 @@
+package p2
+
+type Twoer interface {
+ PackageTwoMeth()
+}
+
+func F() string {}
+func G() Twoer {}
+func NewError(s string) error {}
diff --git a/src/cmd/cc/Makefile b/src/cmd/cc/Makefile
index 8327d9516..109578297 100644
--- a/src/cmd/cc/Makefile
+++ b/src/cmd/cc/Makefile
@@ -1,36 +1,10 @@
-# Copyright 2009 The Go Authors. All rights reserved.
+# 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 ../../Make.inc
-O:=$(HOST_O)
+include ../../Make.dist
-LIB=cc.a
+install: y.tab.h
-HFILES=\
- cc.h\
- y.tab.h\
-
-YFILES=\
- cc.y\
-
-OFILES=\
- y.tab.$O\
- lex.$O\
- mac.$O\
- dcl.$O\
- acid.$O\
- godefs.$O\
- bits.$O\
- com.$O\
- scon.$O\
- funct.$O\
- sub.$O\
- com64.$O\
- dpchk.$O\
- omachcap.$O\
-
-NOINSTALL=1
-include ../../Make.clib
-
-install: $(LIB)
+y.tab.h: cc.y
+ LANG=C LANGUAGE=en_US.UTF8 bison -d -v -y a.y
diff --git a/src/cmd/cc/cc.h b/src/cmd/cc/cc.h
index a38e658ce..4c527a2b3 100644
--- a/src/cmd/cc/cc.h
+++ b/src/cmd/cc/cc.h
@@ -31,8 +31,6 @@
#include <libc.h>
#include <bio.h>
-#pragma lib "../cc/cc.a$O"
-
#ifndef EXTERN
#define EXTERN extern
#endif
@@ -124,6 +122,7 @@ struct Sym
uchar sym;
uchar aused;
uchar sig;
+ uchar dataflag;
};
#define S ((Sym*)0)
@@ -518,14 +517,15 @@ EXTERN int thechar;
EXTERN char* thestring;
EXTERN Type* thisfn;
EXTERN int32 thunk;
-EXTERN Type* types[NTYPE];
-EXTERN Type* fntypes[NTYPE];
+EXTERN Type* types[NALLTYPES];
+EXTERN Type* fntypes[NALLTYPES];
EXTERN Node* initlist;
EXTERN Term term[NTERM];
EXTERN int nterm;
EXTERN int packflg;
EXTERN int fproundflg;
EXTERN int textflag;
+EXTERN int dataflag;
EXTERN int ncontin;
EXTERN int canreach;
EXTERN int warnreach;
@@ -768,6 +768,7 @@ void arginit(void);
void pragvararg(void);
void pragpack(void);
void pragfpround(void);
+void pragdataflag(void);
void pragtextflag(void);
void pragincomplete(void);
void pragdynimport(void);
diff --git a/src/cmd/cc/com64.c b/src/cmd/cc/com64.c
index fb7a3f750..f46fedc16 100644
--- a/src/cmd/cc/com64.c
+++ b/src/cmd/cc/com64.c
@@ -96,7 +96,7 @@ Node* nodmmv;
Node* nodvasop;
-char etconv[NTYPE]; /* for _vasop */
+char etconv[NALLTYPES]; /* for _vasop */
Init initetconv[] =
{
TCHAR, 1, 0,
diff --git a/src/cmd/cc/dcl.c b/src/cmd/cc/dcl.c
index d624bf247..a3ed9772d 100644
--- a/src/cmd/cc/dcl.c
+++ b/src/cmd/cc/dcl.c
@@ -120,6 +120,10 @@ loop:
(*f)(c, t, s);
if(s->class == CLOCAL)
s = mkstatic(s);
+ if(dataflag) {
+ s->dataflag = dataflag;
+ dataflag = 0;
+ }
firstbit = 0;
n->sym = s;
n->type = s->type;
diff --git a/src/cmd/cc/dpchk.c b/src/cmd/cc/dpchk.c
index 084aa0484..c579e20d9 100644
--- a/src/cmd/cc/dpchk.c
+++ b/src/cmd/cc/dpchk.c
@@ -567,13 +567,7 @@ pragfpround(void)
void
pragtextflag(void)
{
- Sym *s;
-
- textflag = 0;
- s = getsym();
- textflag = 7;
- if(s)
- textflag = atoi(s->name+1);
+ textflag = getnsn();
while(getnsc() != '\n')
;
if(debug['f'])
@@ -581,6 +575,16 @@ pragtextflag(void)
}
void
+pragdataflag(void)
+{
+ dataflag = getnsn();
+ while(getnsc() != '\n')
+ ;
+ if(debug['f'])
+ print("%4d: dataflag %d\n", lineno, dataflag);
+}
+
+void
pragincomplete(void)
{
Sym *s;
diff --git a/src/cmd/cc/funct.c b/src/cmd/cc/funct.c
index 99477b2b2..057151987 100644
--- a/src/cmd/cc/funct.c
+++ b/src/cmd/cc/funct.c
@@ -46,7 +46,7 @@ struct Gtab
};
Ftab ftabinit[OEND];
-Gtab gtabinit[NTYPE];
+Gtab gtabinit[NALLTYPES];
int
isfunct(Node *n)
@@ -350,7 +350,7 @@ bad:
diag(Z, "dclfunct bad %T %s\n", t, s->name);
}
-Gtab gtabinit[NTYPE] =
+Gtab gtabinit[NALLTYPES] =
{
TCHAR, "c",
TUCHAR, "uc",
diff --git a/src/cmd/cc/godefs.c b/src/cmd/cc/godefs.c
index 3ba979c8a..4274c5626 100644
--- a/src/cmd/cc/godefs.c
+++ b/src/cmd/cc/godefs.c
@@ -124,11 +124,11 @@ Uconv(Fmt *fp)
if(s && *s) {
if(upper)
- str[0] = toupper(*s);
+ str[0] = toupper((uchar)*s);
else
- str[0] = tolower(*s);
+ str[0] = tolower((uchar)*s);
for(i = 1; i < STRINGSZ && s[i] != 0; i++)
- str[i] = tolower(s[i]);
+ str[i] = tolower((uchar)s[i]);
str[i] = 0;
}
diff --git a/src/cmd/cc/lex.c b/src/cmd/cc/lex.c
index 9fb2f9e4d..8aeb1a334 100644
--- a/src/cmd/cc/lex.c
+++ b/src/cmd/cc/lex.c
@@ -112,7 +112,7 @@ main(int argc, char *argv[])
case 'l': /* for little-endian mips */
if(thechar != 'v'){
- print("can only use -l with vc");
+ print("can only use -l with vc\n");
errorexit();
}
thechar = '0';
diff --git a/src/cmd/cc/lexbody b/src/cmd/cc/lexbody
index f4cc19c2e..d339cf9a2 100644
--- a/src/cmd/cc/lexbody
+++ b/src/cmd/cc/lexbody
@@ -75,6 +75,13 @@ pragtextflag(void)
}
void
+pragdataflag(void)
+{
+ while(getnsc() != '\n')
+ ;
+}
+
+void
pragprofile(void)
{
while(getnsc() != '\n')
diff --git a/src/cmd/cc/macbody b/src/cmd/cc/macbody
index ed66361f1..874e82d25 100644
--- a/src/cmd/cc/macbody
+++ b/src/cmd/cc/macbody
@@ -731,6 +731,10 @@ macprag(void)
pragtextflag();
return;
}
+ if(s && strcmp(s->name, "dataflag") == 0) {
+ pragdataflag();
+ return;
+ }
if(s && strcmp(s->name, "varargck") == 0) {
pragvararg();
return;
diff --git a/src/cmd/cc/pgen.c b/src/cmd/cc/pgen.c
index 0e5e8c059..3a686102f 100644
--- a/src/cmd/cc/pgen.c
+++ b/src/cmd/cc/pgen.c
@@ -266,7 +266,7 @@ loop:
if(cases == C)
diag(n, "case/default outside a switch");
if(l == Z) {
- cas();
+ newcase();
cases->val = 0;
cases->def = 1;
cases->label = pc;
@@ -278,7 +278,7 @@ loop:
goto rloop;
if(l->op == OCONST)
if(typeword[l->type->etype] && l->type->etype != TIND) {
- cas();
+ newcase();
cases->val = l->vconst;
cases->def = 0;
cases->label = pc;
@@ -293,7 +293,7 @@ loop:
complex(l);
if(l->type == T)
break;
- if(!typeword[l->type->etype] || l->type->etype == TIND) {
+ if(!typechlvp[l->type->etype] || l->type->etype == TIND) {
diag(n, "switch expression must be integer");
break;
}
@@ -303,7 +303,7 @@ loop:
cn = cases;
cases = C;
- cas();
+ newcase();
sbc = breakpc;
breakpc = pc;
@@ -320,15 +320,7 @@ loop:
}
patch(sp, pc);
- regalloc(&nod, l, Z);
- /* always signed */
- if(typev[l->type->etype])
- nod.type = types[TVLONG];
- else
- nod.type = types[TLONG];
- cgen(l, &nod);
- doswit(&nod);
- regfree(&nod);
+ doswit(l);
patch(spb, pc);
cases = cn;
diff --git a/src/cmd/cc/pswt.c b/src/cmd/cc/pswt.c
index 0e402dea7..b94035faa 100644
--- a/src/cmd/cc/pswt.c
+++ b/src/cmd/cc/pswt.c
@@ -92,7 +92,7 @@ doswit(Node *n)
}
void
-cas(void)
+newcase(void)
{
Case *c;
diff --git a/src/cmd/cc/sub.c b/src/cmd/cc/sub.c
index e5992e213..72d671b2f 100644
--- a/src/cmd/cc/sub.c
+++ b/src/cmd/cc/sub.c
@@ -156,7 +156,10 @@ typ(int et, Type *d)
t->link = d;
t->down = T;
t->sym = S;
- t->width = ewidth[et];
+ if(et < NTYPE)
+ t->width = ewidth[et];
+ else
+ t->width = -1; // for TDOT or TOLD in prototype
t->offset = 0;
t->shift = 0;
t->nbits = 0;
@@ -1535,92 +1538,92 @@ uchar logrel[12] =
OEQ, ONE, OLS, OLS, OLO, OLO, OHS, OHS, OHI, OHI,
};
-uchar typei[NTYPE];
+uchar typei[NALLTYPES];
int typeiinit[] =
{
TCHAR, TUCHAR, TSHORT, TUSHORT, TINT, TUINT, TLONG, TULONG, TVLONG, TUVLONG, -1,
};
-uchar typeu[NTYPE];
+uchar typeu[NALLTYPES];
int typeuinit[] =
{
TUCHAR, TUSHORT, TUINT, TULONG, TUVLONG, TIND, -1,
};
-uchar typesuv[NTYPE];
+uchar typesuv[NALLTYPES];
int typesuvinit[] =
{
TVLONG, TUVLONG, TSTRUCT, TUNION, -1,
};
-uchar typeilp[NTYPE];
+uchar typeilp[NALLTYPES];
int typeilpinit[] =
{
TINT, TUINT, TLONG, TULONG, TIND, -1
};
-uchar typechl[NTYPE];
-uchar typechlv[NTYPE];
-uchar typechlvp[NTYPE];
+uchar typechl[NALLTYPES];
+uchar typechlv[NALLTYPES];
+uchar typechlvp[NALLTYPES];
int typechlinit[] =
{
TCHAR, TUCHAR, TSHORT, TUSHORT, TINT, TUINT, TLONG, TULONG, -1,
};
-uchar typechlp[NTYPE];
+uchar typechlp[NALLTYPES];
int typechlpinit[] =
{
TCHAR, TUCHAR, TSHORT, TUSHORT, TINT, TUINT, TLONG, TULONG, TIND, -1,
};
-uchar typechlpfd[NTYPE];
+uchar typechlpfd[NALLTYPES];
int typechlpfdinit[] =
{
TCHAR, TUCHAR, TSHORT, TUSHORT, TINT, TUINT, TLONG, TULONG, TFLOAT, TDOUBLE, TIND, -1,
};
-uchar typec[NTYPE];
+uchar typec[NALLTYPES];
int typecinit[] =
{
TCHAR, TUCHAR, -1
};
-uchar typeh[NTYPE];
+uchar typeh[NALLTYPES];
int typehinit[] =
{
TSHORT, TUSHORT, -1,
};
-uchar typeil[NTYPE];
+uchar typeil[NALLTYPES];
int typeilinit[] =
{
TINT, TUINT, TLONG, TULONG, -1,
};
-uchar typev[NTYPE];
+uchar typev[NALLTYPES];
int typevinit[] =
{
TVLONG, TUVLONG, -1,
};
-uchar typefd[NTYPE];
+uchar typefd[NALLTYPES];
int typefdinit[] =
{
TFLOAT, TDOUBLE, -1,
};
-uchar typeaf[NTYPE];
+uchar typeaf[NALLTYPES];
int typeafinit[] =
{
TFUNC, TARRAY, -1,
};
-uchar typesu[NTYPE];
+uchar typesu[NALLTYPES];
int typesuinit[] =
{
TSTRUCT, TUNION, -1,
};
-int32 tasign[NTYPE];
+int32 tasign[NALLTYPES];
Init tasigninit[] =
{
TCHAR, BNUMBER, 0,
@@ -1641,7 +1644,7 @@ Init tasigninit[] =
-1, 0, 0,
};
-int32 tasadd[NTYPE];
+int32 tasadd[NALLTYPES];
Init tasaddinit[] =
{
TCHAR, BNUMBER, 0,
@@ -1660,7 +1663,7 @@ Init tasaddinit[] =
-1, 0, 0,
};
-int32 tcast[NTYPE];
+int32 tcast[NALLTYPES];
Init tcastinit[] =
{
TCHAR, BNUMBER|BIND|BVOID, 0,
@@ -1682,7 +1685,7 @@ Init tcastinit[] =
-1, 0, 0,
};
-int32 tadd[NTYPE];
+int32 tadd[NALLTYPES];
Init taddinit[] =
{
TCHAR, BNUMBER|BIND, 0,
@@ -1701,7 +1704,7 @@ Init taddinit[] =
-1, 0, 0,
};
-int32 tsub[NTYPE];
+int32 tsub[NALLTYPES];
Init tsubinit[] =
{
TCHAR, BNUMBER, 0,
@@ -1720,7 +1723,7 @@ Init tsubinit[] =
-1, 0, 0,
};
-int32 tmul[NTYPE];
+int32 tmul[NALLTYPES];
Init tmulinit[] =
{
TCHAR, BNUMBER, 0,
@@ -1738,7 +1741,7 @@ Init tmulinit[] =
-1, 0, 0,
};
-int32 tand[NTYPE];
+int32 tand[NALLTYPES];
Init tandinit[] =
{
TCHAR, BINTEGER, 0,
@@ -1754,7 +1757,7 @@ Init tandinit[] =
-1, 0, 0,
};
-int32 trel[NTYPE];
+int32 trel[NALLTYPES];
Init trelinit[] =
{
TCHAR, BNUMBER, 0,
diff --git a/src/cmd/cc/y.tab.c b/src/cmd/cc/y.tab.c
new file mode 100644
index 000000000..af8328abd
--- /dev/null
+++ b/src/cmd/cc/y.tab.c
@@ -0,0 +1,3811 @@
+/* A Bison parser, made by GNU Bison 2.3. */
+
+/* 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
+ 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.
+
+ 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. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ 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. */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+ simplifying the original so-called "semantic" parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Bison version. */
+#define YYBISON_VERSION "2.3"
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 0
+
+/* Using locations. */
+#define YYLSP_NEEDED 0
+
+
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ LORE = 258,
+ LXORE = 259,
+ LANDE = 260,
+ LLSHE = 261,
+ LRSHE = 262,
+ LMDE = 263,
+ LDVE = 264,
+ LMLE = 265,
+ LME = 266,
+ LPE = 267,
+ LOROR = 268,
+ LANDAND = 269,
+ LNE = 270,
+ LEQ = 271,
+ LGE = 272,
+ LLE = 273,
+ LRSH = 274,
+ LLSH = 275,
+ LMG = 276,
+ LPP = 277,
+ LMM = 278,
+ LNAME = 279,
+ LTYPE = 280,
+ LFCONST = 281,
+ LDCONST = 282,
+ LCONST = 283,
+ LLCONST = 284,
+ LUCONST = 285,
+ LULCONST = 286,
+ LVLCONST = 287,
+ LUVLCONST = 288,
+ LSTRING = 289,
+ LLSTRING = 290,
+ LAUTO = 291,
+ LBREAK = 292,
+ LCASE = 293,
+ LCHAR = 294,
+ LCONTINUE = 295,
+ LDEFAULT = 296,
+ LDO = 297,
+ LDOUBLE = 298,
+ LELSE = 299,
+ LEXTERN = 300,
+ LFLOAT = 301,
+ LFOR = 302,
+ LGOTO = 303,
+ LIF = 304,
+ LINT = 305,
+ LLONG = 306,
+ LREGISTER = 307,
+ LRETURN = 308,
+ LSHORT = 309,
+ LSIZEOF = 310,
+ LUSED = 311,
+ LSTATIC = 312,
+ LSTRUCT = 313,
+ LSWITCH = 314,
+ LTYPEDEF = 315,
+ LTYPESTR = 316,
+ LUNION = 317,
+ LUNSIGNED = 318,
+ LWHILE = 319,
+ LVOID = 320,
+ LENUM = 321,
+ LSIGNED = 322,
+ LCONSTNT = 323,
+ LVOLATILE = 324,
+ LSET = 325,
+ LSIGNOF = 326,
+ LRESTRICT = 327,
+ LINLINE = 328
+ };
+#endif
+/* Tokens. */
+#define LORE 258
+#define LXORE 259
+#define LANDE 260
+#define LLSHE 261
+#define LRSHE 262
+#define LMDE 263
+#define LDVE 264
+#define LMLE 265
+#define LME 266
+#define LPE 267
+#define LOROR 268
+#define LANDAND 269
+#define LNE 270
+#define LEQ 271
+#define LGE 272
+#define LLE 273
+#define LRSH 274
+#define LLSH 275
+#define LMG 276
+#define LPP 277
+#define LMM 278
+#define LNAME 279
+#define LTYPE 280
+#define LFCONST 281
+#define LDCONST 282
+#define LCONST 283
+#define LLCONST 284
+#define LUCONST 285
+#define LULCONST 286
+#define LVLCONST 287
+#define LUVLCONST 288
+#define LSTRING 289
+#define LLSTRING 290
+#define LAUTO 291
+#define LBREAK 292
+#define LCASE 293
+#define LCHAR 294
+#define LCONTINUE 295
+#define LDEFAULT 296
+#define LDO 297
+#define LDOUBLE 298
+#define LELSE 299
+#define LEXTERN 300
+#define LFLOAT 301
+#define LFOR 302
+#define LGOTO 303
+#define LIF 304
+#define LINT 305
+#define LLONG 306
+#define LREGISTER 307
+#define LRETURN 308
+#define LSHORT 309
+#define LSIZEOF 310
+#define LUSED 311
+#define LSTATIC 312
+#define LSTRUCT 313
+#define LSWITCH 314
+#define LTYPEDEF 315
+#define LTYPESTR 316
+#define LUNION 317
+#define LUNSIGNED 318
+#define LWHILE 319
+#define LVOID 320
+#define LENUM 321
+#define LSIGNED 322
+#define LCONSTNT 323
+#define LVOLATILE 324
+#define LSET 325
+#define LSIGNOF 326
+#define LRESTRICT 327
+#define LINLINE 328
+
+
+
+
+/* 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"
+{
+ Node* node;
+ Sym* sym;
+ Type* type;
+ struct
+ {
+ Type* t;
+ uchar c;
+ } tycl;
+ struct
+ {
+ Type* t1;
+ Type* t2;
+ Type* t3;
+ uchar c;
+ } tyty;
+ struct
+ {
+ char* s;
+ int32 l;
+ } sval;
+ int32 lval;
+ double dval;
+ vlong vval;
+}
+/* Line 193 of yacc.c. */
+#line 274 "y.tab.c"
+ YYSTYPE;
+# 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 287 "y.tab.c"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+# define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+# define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# else
+# define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+# if ENABLE_NLS
+# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+# define YY_(msgid) dgettext ("bison-runtime", msgid)
+# endif
+# endif
+# ifndef YY_
+# define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E. */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions. */
+#ifndef lint
+# define YYID(n) (n)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int i)
+#else
+static int
+YYID (i)
+ int i;
+#endif
+{
+ return i;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# elif defined __BUILTIN_VA_ARG_INCR
+# include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+# elif defined _AIX
+# define YYSTACK_ALLOC __alloca
+# elif defined _MSC_VER
+# include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+# define alloca _alloca
+# else
+# define YYSTACK_ALLOC alloca
+# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (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
+# endif
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's `empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+# ifndef YYSTACK_ALLOC_MAXIMUM
+ /* The OS might guarantee only one guard page at the bottom of the stack,
+ and a page size can be as small as 4096 bytes. So we cannot safely
+ invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
+ to allow for a few compiler-allocated temporary stack slots. */
+# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+# endif
+# else
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# ifndef YYSTACK_ALLOC_MAXIMUM
+# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+# endif
+# if (defined __cplusplus && ! defined _STDLIB_H \
+ && ! ((defined YYMALLOC || defined malloc) \
+ && (defined YYFREE || defined free)))
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef _STDLIB_H
+# define _STDLIB_H 1
+# endif
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# if ! defined malloc && ! defined _STDLIB_H && (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__ \
+ || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+ && (! defined __cplusplus \
+ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ yytype_int16 yyss;
+ YYSTYPE yyvs;
+ };
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((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
+
+/* 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
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 2
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 1188
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 98
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 75
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 246
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 412
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 328
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
+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, 96, 2, 2, 2, 35, 22, 2,
+ 38, 92, 33, 31, 4, 32, 36, 34, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 17, 3,
+ 25, 5, 26, 16, 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, 37, 2, 93, 21, 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, 94, 20, 95, 97, 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, 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, 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, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15, 18, 19,
+ 23, 24, 27, 28, 29, 30, 39, 40, 41, 42,
+ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ 53, 54, 55, 56, 57, 58, 59, 60, 61, 62,
+ 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
+ 73, 74, 75, 76, 77, 78, 79, 80, 81, 82,
+ 83, 84, 85, 86, 87, 88, 89, 90, 91
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+ YYRHS. */
+static const yytype_uint16 yyprhs[] =
+{
+ 0, 0, 3, 4, 7, 10, 14, 15, 16, 23,
+ 25, 26, 31, 35, 37, 41, 43, 47, 52, 57,
+ 60, 64, 66, 67, 72, 76, 77, 82, 84, 88,
+ 89, 94, 95, 101, 102, 104, 106, 110, 112, 116,
+ 119, 120, 122, 125, 129, 131, 133, 138, 143, 146,
+ 150, 154, 156, 160, 164, 167, 170, 173, 177, 179,
+ 182, 184, 186, 189, 190, 192, 194, 197, 200, 204,
+ 208, 212, 213, 216, 219, 221, 224, 228, 231, 234,
+ 237, 239, 242, 244, 247, 250, 251, 254, 260, 268,
+ 269, 280, 286, 294, 298, 304, 307, 310, 314, 320,
+ 326, 327, 329, 330, 332, 334, 336, 340, 342, 346,
+ 350, 354, 358, 362, 366, 370, 374, 378, 382, 386,
+ 390, 394, 398, 402, 406, 410, 414, 420, 424, 428,
+ 432, 436, 440, 444, 448, 452, 456, 460, 464, 466,
+ 472, 480, 482, 485, 488, 491, 494, 497, 500, 503,
+ 506, 509, 512, 516, 522, 528, 533, 538, 542, 546,
+ 549, 552, 554, 556, 558, 560, 562, 564, 566, 568,
+ 570, 572, 574, 576, 579, 581, 584, 585, 587, 589,
+ 593, 594, 599, 600, 602, 604, 606, 608, 611, 614,
+ 618, 621, 625, 627, 629, 632, 633, 638, 641, 644,
+ 645, 650, 653, 656, 657, 658, 666, 667, 673, 675,
+ 677, 680, 681, 684, 686, 688, 690, 692, 695, 697,
+ 699, 701, 705, 708, 712, 714, 716, 718, 720, 722,
+ 724, 726, 728, 730, 732, 734, 736, 738, 740, 742,
+ 744, 746, 748, 750, 752, 754, 756
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yytype_int16 yyrhs[] =
+{
+ 99, 0, -1, -1, 99, 100, -1, 151, 3, -1,
+ 151, 103, 3, -1, -1, -1, 151, 105, 101, 110,
+ 102, 128, -1, 105, -1, -1, 105, 104, 5, 122,
+ -1, 103, 4, 103, -1, 106, -1, 33, 162, 105,
+ -1, 171, -1, 38, 105, 92, -1, 106, 38, 126,
+ 92, -1, 106, 37, 138, 93, -1, 154, 3, -1,
+ 154, 108, 3, -1, 105, -1, -1, 105, 109, 5,
+ 122, -1, 108, 4, 108, -1, -1, 110, 154, 111,
+ 3, -1, 105, -1, 111, 4, 111, -1, -1, 153,
+ 113, 115, 3, -1, -1, 112, 153, 114, 115, 3,
+ -1, -1, 116, -1, 117, -1, 116, 4, 116, -1,
+ 105, -1, 171, 17, 139, -1, 17, 139, -1, -1,
+ 119, -1, 33, 162, -1, 33, 162, 119, -1, 120,
+ -1, 121, -1, 120, 38, 126, 92, -1, 120, 37,
+ 138, 93, -1, 38, 92, -1, 37, 138, 93, -1,
+ 38, 119, 92, -1, 141, -1, 94, 125, 95, -1,
+ 37, 139, 93, -1, 36, 172, -1, 123, 5, -1,
+ 122, 4, -1, 124, 122, 4, -1, 123, -1, 124,
+ 123, -1, 124, -1, 122, -1, 124, 122, -1, -1,
+ 127, -1, 170, -1, 153, 118, -1, 153, 105, -1,
+ 36, 36, 36, -1, 127, 4, 127, -1, 94, 129,
+ 95, -1, -1, 129, 107, -1, 129, 132, -1, 131,
+ -1, 130, 131, -1, 56, 141, 17, -1, 59, 17,
+ -1, 42, 17, -1, 1, 3, -1, 134, -1, 130,
+ 134, -1, 137, -1, 154, 108, -1, 137, 3, -1,
+ -1, 135, 128, -1, 67, 38, 140, 92, 132, -1,
+ 67, 38, 140, 92, 132, 62, 132, -1, -1, 136,
+ 65, 38, 133, 3, 137, 3, 137, 92, 132, -1,
+ 82, 38, 140, 92, 132, -1, 60, 132, 82, 38,
+ 140, 92, 3, -1, 71, 137, 3, -1, 77, 38,
+ 140, 92, 132, -1, 55, 3, -1, 58, 3, -1,
+ 66, 172, 3, -1, 74, 38, 147, 92, 3, -1,
+ 88, 38, 147, 92, 3, -1, -1, 140, -1, -1,
+ 139, -1, 141, -1, 141, -1, 140, 4, 140, -1,
+ 142, -1, 141, 33, 141, -1, 141, 34, 141, -1,
+ 141, 35, 141, -1, 141, 31, 141, -1, 141, 32,
+ 141, -1, 141, 29, 141, -1, 141, 30, 141, -1,
+ 141, 25, 141, -1, 141, 26, 141, -1, 141, 28,
+ 141, -1, 141, 27, 141, -1, 141, 24, 141, -1,
+ 141, 23, 141, -1, 141, 22, 141, -1, 141, 21,
+ 141, -1, 141, 20, 141, -1, 141, 19, 141, -1,
+ 141, 18, 141, -1, 141, 16, 140, 17, 141, -1,
+ 141, 5, 141, -1, 141, 15, 141, -1, 141, 14,
+ 141, -1, 141, 13, 141, -1, 141, 12, 141, -1,
+ 141, 11, 141, -1, 141, 9, 141, -1, 141, 10,
+ 141, -1, 141, 8, 141, -1, 141, 7, 141, -1,
+ 141, 6, 141, -1, 143, -1, 38, 153, 118, 92,
+ 142, -1, 38, 153, 118, 92, 94, 125, 95, -1,
+ 144, -1, 33, 142, -1, 22, 142, -1, 31, 142,
+ -1, 32, 142, -1, 96, 142, -1, 97, 142, -1,
+ 40, 142, -1, 41, 142, -1, 73, 143, -1, 89,
+ 143, -1, 38, 140, 92, -1, 73, 38, 153, 118,
+ 92, -1, 89, 38, 153, 118, 92, -1, 144, 38,
+ 147, 92, -1, 144, 37, 140, 93, -1, 144, 39,
+ 172, -1, 144, 36, 172, -1, 144, 40, -1, 144,
+ 41, -1, 170, -1, 46, -1, 47, -1, 48, -1,
+ 49, -1, 45, -1, 44, -1, 50, -1, 51, -1,
+ 145, -1, 146, -1, 52, -1, 145, 52, -1, 53,
+ -1, 146, 53, -1, -1, 148, -1, 141, -1, 148,
+ 4, 148, -1, -1, 94, 150, 112, 95, -1, -1,
+ 154, -1, 155, -1, 167, -1, 164, -1, 155, 161,
+ -1, 167, 161, -1, 164, 155, 162, -1, 164, 167,
+ -1, 164, 167, 161, -1, 152, -1, 152, -1, 76,
+ 172, -1, -1, 76, 172, 156, 149, -1, 76, 149,
+ -1, 80, 172, -1, -1, 80, 172, 157, 149, -1,
+ 80, 149, -1, 84, 172, -1, -1, -1, 84, 172,
+ 158, 94, 159, 166, 95, -1, -1, 84, 94, 160,
+ 166, 95, -1, 43, -1, 163, -1, 161, 163, -1,
+ -1, 162, 169, -1, 167, -1, 169, -1, 168, -1,
+ 165, -1, 164, 165, -1, 169, -1, 168, -1, 42,
+ -1, 42, 5, 141, -1, 166, 4, -1, 166, 4,
+ 166, -1, 57, -1, 72, -1, 68, -1, 69, -1,
+ 85, -1, 81, -1, 64, -1, 61, -1, 83, -1,
+ 54, -1, 75, -1, 63, -1, 78, -1, 79, -1,
+ 70, -1, 91, -1, 86, -1, 87, -1, 90, -1,
+ 42, -1, 172, -1, 42, -1, 43, -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
+static const yytype_uint16 yyrline[] =
+{
+ 0, 101, 101, 102, 108, 112, 114, 128, 113, 143,
+ 148, 147, 155, 158, 159, 166, 167, 171, 175, 184,
+ 188, 194, 200, 199, 211, 224, 225, 228, 232, 239,
+ 238, 244, 243, 250, 254, 257, 261, 264, 269, 273,
+ 282, 285, 288, 293, 298, 301, 302, 306, 312, 316,
+ 320, 326, 327, 333, 337, 342, 345, 346, 350, 351,
+ 357, 358, 359, 365, 368, 375, 376, 381, 386, 390,
+ 396, 406, 409, 413, 419, 420, 426, 430, 434, 440,
+ 444, 445, 451, 452, 458, 459, 459, 470, 476, 484,
+ 484, 495, 499, 503, 508, 522, 526, 530, 534, 538,
+ 544, 547, 550, 553, 556, 563, 564, 570, 571, 575,
+ 579, 583, 587, 591, 595, 599, 603, 607, 611, 615,
+ 619, 623, 627, 631, 635, 639, 643, 647, 651, 655,
+ 659, 663, 667, 671, 675, 679, 683, 687, 693, 694,
+ 701, 709, 710, 714, 718, 722, 726, 730, 734, 738,
+ 742, 746, 752, 756, 762, 768, 776, 780, 785, 790,
+ 794, 798, 799, 806, 813, 820, 827, 834, 841, 848,
+ 855, 856, 859, 869, 887, 897, 915, 918, 921, 922,
+ 929, 928, 951, 955, 958, 963, 968, 974, 982, 988,
+ 994, 1000, 1008, 1016, 1023, 1029, 1028, 1040, 1048, 1054,
+ 1053, 1065, 1073, 1082, 1086, 1081, 1103, 1102, 1111, 1117,
+ 1118, 1124, 1127, 1133, 1134, 1135, 1138, 1139, 1145, 1146,
+ 1149, 1153, 1157, 1158, 1161, 1162, 1163, 1164, 1165, 1166,
+ 1167, 1168, 1169, 1172, 1173, 1174, 1175, 1176, 1177, 1178,
+ 1181, 1182, 1183, 1186, 1201, 1213, 1214
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "';'", "','", "'='", "LORE", "LXORE",
+ "LANDE", "LLSHE", "LRSHE", "LMDE", "LDVE", "LMLE", "LME", "LPE", "'?'",
+ "':'", "LOROR", "LANDAND", "'|'", "'^'", "'&'", "LNE", "LEQ", "'<'",
+ "'>'", "LGE", "LLE", "LRSH", "LLSH", "'+'", "'-'", "'*'", "'/'", "'%'",
+ "'.'", "'['", "'('", "LMG", "LPP", "LMM", "LNAME", "LTYPE", "LFCONST",
+ "LDCONST", "LCONST", "LLCONST", "LUCONST", "LULCONST", "LVLCONST",
+ "LUVLCONST", "LSTRING", "LLSTRING", "LAUTO", "LBREAK", "LCASE", "LCHAR",
+ "LCONTINUE", "LDEFAULT", "LDO", "LDOUBLE", "LELSE", "LEXTERN", "LFLOAT",
+ "LFOR", "LGOTO", "LIF", "LINT", "LLONG", "LREGISTER", "LRETURN",
+ "LSHORT", "LSIZEOF", "LUSED", "LSTATIC", "LSTRUCT", "LSWITCH",
+ "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",
+ "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
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+ token YYLEX-NUM. */
+static const yytype_uint16 yytoknum[] =
+{
+ 0, 256, 257, 59, 44, 61, 258, 259, 260, 261,
+ 262, 263, 264, 265, 266, 267, 63, 58, 268, 269,
+ 124, 94, 38, 270, 271, 60, 62, 272, 273, 274,
+ 275, 43, 45, 42, 47, 37, 46, 91, 40, 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, 305, 306,
+ 307, 308, 309, 310, 311, 312, 313, 314, 315, 316,
+ 317, 318, 319, 320, 321, 322, 323, 324, 325, 326,
+ 327, 328, 41, 93, 123, 125, 33, 126
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const yytype_uint8 yyr1[] =
+{
+ 0, 98, 99, 99, 100, 100, 101, 102, 100, 103,
+ 104, 103, 103, 105, 105, 106, 106, 106, 106, 107,
+ 107, 108, 109, 108, 108, 110, 110, 111, 111, 113,
+ 112, 114, 112, 115, 115, 116, 116, 117, 117, 117,
+ 118, 118, 119, 119, 119, 120, 120, 120, 121, 121,
+ 121, 122, 122, 123, 123, 123, 124, 124, 124, 124,
+ 125, 125, 125, 126, 126, 127, 127, 127, 127, 127,
+ 128, 129, 129, 129, 130, 130, 131, 131, 131, 132,
+ 132, 132, 133, 133, 134, 135, 134, 134, 134, 136,
+ 134, 134, 134, 134, 134, 134, 134, 134, 134, 134,
+ 137, 137, 138, 138, 139, 140, 140, 141, 141, 141,
+ 141, 141, 141, 141, 141, 141, 141, 141, 141, 141,
+ 141, 141, 141, 141, 141, 141, 141, 141, 141, 141,
+ 141, 141, 141, 141, 141, 141, 141, 141, 142, 142,
+ 142, 143, 143, 143, 143, 143, 143, 143, 143, 143,
+ 143, 143, 144, 144, 144, 144, 144, 144, 144, 144,
+ 144, 144, 144, 144, 144, 144, 144, 144, 144, 144,
+ 144, 144, 145, 145, 146, 146, 147, 147, 148, 148,
+ 150, 149, 151, 151, 152, 152, 152, 152, 152, 152,
+ 152, 152, 153, 154, 155, 156, 155, 155, 155, 157,
+ 155, 155, 155, 158, 159, 155, 160, 155, 155, 161,
+ 161, 162, 162, 163, 163, 163, 164, 164, 165, 165,
+ 166, 166, 166, 166, 167, 167, 167, 167, 167, 167,
+ 167, 167, 167, 168, 168, 168, 168, 168, 168, 168,
+ 169, 169, 169, 170, 171, 172, 172
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+static const yytype_uint8 yyr2[] =
+{
+ 0, 2, 0, 2, 2, 3, 0, 0, 6, 1,
+ 0, 4, 3, 1, 3, 1, 3, 4, 4, 2,
+ 3, 1, 0, 4, 3, 0, 4, 1, 3, 0,
+ 4, 0, 5, 0, 1, 1, 3, 1, 3, 2,
+ 0, 1, 2, 3, 1, 1, 4, 4, 2, 3,
+ 3, 1, 3, 3, 2, 2, 2, 3, 1, 2,
+ 1, 1, 2, 0, 1, 1, 2, 2, 3, 3,
+ 3, 0, 2, 2, 1, 2, 3, 2, 2, 2,
+ 1, 2, 1, 2, 2, 0, 2, 5, 7, 0,
+ 10, 5, 7, 3, 5, 2, 2, 3, 5, 5,
+ 0, 1, 0, 1, 1, 1, 3, 1, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 5, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 1, 5,
+ 7, 1, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 3, 5, 5, 4, 4, 3, 3, 2,
+ 2, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 2, 1, 2, 0, 1, 1, 3,
+ 0, 4, 0, 1, 1, 1, 1, 2, 2, 3,
+ 2, 3, 1, 1, 2, 0, 4, 2, 2, 0,
+ 4, 2, 2, 0, 0, 7, 0, 5, 1, 1,
+ 2, 0, 2, 1, 1, 1, 1, 2, 1, 1,
+ 1, 3, 2, 3, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 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
+ means the default is an error. */
+static const yytype_uint8 yydefact[] =
+{
+ 2, 182, 1, 208, 233, 224, 231, 235, 230, 226,
+ 227, 238, 225, 234, 0, 236, 237, 0, 229, 232,
+ 0, 228, 240, 241, 242, 239, 3, 0, 193, 183,
+ 184, 186, 216, 185, 219, 218, 245, 246, 180, 197,
+ 194, 201, 198, 206, 202, 4, 211, 0, 0, 6,
+ 13, 15, 244, 187, 209, 213, 215, 214, 211, 217,
+ 190, 188, 0, 0, 0, 0, 0, 0, 0, 5,
+ 0, 25, 0, 102, 63, 210, 189, 191, 0, 192,
+ 29, 196, 200, 220, 0, 204, 14, 212, 16, 12,
+ 9, 7, 0, 0, 0, 0, 0, 0, 0, 0,
+ 243, 167, 166, 162, 163, 164, 165, 168, 169, 172,
+ 174, 0, 0, 0, 0, 0, 103, 104, 107, 138,
+ 141, 170, 171, 161, 0, 0, 64, 40, 65, 181,
+ 31, 33, 0, 222, 207, 0, 0, 0, 0, 11,
+ 51, 143, 144, 145, 142, 0, 105, 40, 148, 149,
+ 0, 150, 0, 151, 146, 147, 18, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 176,
+ 0, 159, 160, 173, 175, 0, 17, 0, 211, 102,
+ 0, 67, 66, 41, 44, 45, 33, 0, 37, 0,
+ 34, 35, 15, 221, 223, 0, 71, 8, 27, 0,
+ 0, 0, 61, 58, 60, 0, 0, 152, 211, 0,
+ 0, 40, 40, 127, 137, 136, 135, 133, 134, 132,
+ 131, 130, 129, 128, 0, 125, 124, 123, 122, 121,
+ 120, 119, 115, 116, 118, 117, 113, 114, 111, 112,
+ 108, 109, 110, 158, 0, 178, 0, 177, 157, 68,
+ 69, 42, 0, 48, 0, 102, 63, 0, 39, 30,
+ 0, 0, 205, 0, 26, 0, 54, 0, 56, 55,
+ 62, 59, 52, 106, 42, 0, 0, 0, 0, 156,
+ 155, 0, 43, 49, 50, 0, 0, 32, 36, 38,
+ 0, 243, 0, 0, 0, 0, 0, 0, 0, 100,
+ 0, 0, 0, 0, 70, 72, 85, 74, 73, 80,
+ 0, 0, 0, 101, 0, 28, 53, 57, 0, 139,
+ 153, 154, 126, 179, 47, 46, 79, 78, 95, 0,
+ 96, 77, 0, 0, 0, 0, 176, 0, 0, 176,
+ 75, 81, 86, 0, 84, 19, 21, 0, 0, 76,
+ 0, 97, 0, 93, 0, 0, 0, 0, 100, 0,
+ 20, 0, 140, 0, 0, 0, 0, 0, 0, 0,
+ 82, 0, 0, 24, 0, 87, 98, 94, 91, 99,
+ 100, 83, 23, 0, 0, 0, 92, 88, 100, 0,
+ 0, 90
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const yytype_int16 yydefgoto[] =
+{
+ -1, 1, 26, 71, 136, 48, 72, 208, 50, 325,
+ 367, 379, 91, 219, 78, 131, 206, 209, 210, 211,
+ 202, 203, 204, 205, 222, 223, 224, 225, 125, 126,
+ 217, 283, 326, 327, 328, 389, 329, 330, 331, 332,
+ 115, 116, 333, 146, 118, 119, 120, 121, 122, 266,
+ 267, 39, 62, 27, 79, 127, 29, 30, 63, 64,
+ 66, 135, 65, 53, 67, 54, 31, 32, 84, 33,
+ 34, 35, 123, 51, 52
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -366
+static const yytype_int16 yypact[] =
+{
+ -366, 541, -366, -366, -366, -366, -366, -366, -366, -366,
+ -366, -366, -366, -366, -3, -366, -366, -3, -366, -366,
+ 140, -366, -366, -366, -366, -366, -366, 161, -366, -366,
+ 949, 914, -366, 949, -366, -366, -366, -366, -366, -366,
+ -64, -366, -60, -366, -17, -366, -366, 21, 70, 316,
+ 51, -366, -366, 949, -366, -366, -366, -366, -366, -366,
+ 949, 949, 914, -13, -13, 57, 9, 154, 54, -366,
+ 21, -366, 164, 745, 836, -366, -41, 949, 875, -366,
+ -366, -366, -366, 167, 7, -366, -366, -366, -366, -366,
+ 181, 914, 677, 745, 745, 745, 745, 607, 745, 745,
+ -366, -366, -366, -366, -366, -366, -366, -366, -366, -366,
+ -366, 779, 813, 745, 745, 95, -366, 1067, -366, -366,
+ 422, 141, 145, -366, 176, 139, 225, 128, -366, -366,
+ -366, 289, 745, 57, -366, 57, 149, 21, 169, -366,
+ 1067, -366, -366, -366, -366, 31, 1067, 130, -366, -366,
+ 607, -366, 607, -366, -366, -366, -366, 745, 745, 745,
+ 745, 745, 745, 745, 745, 745, 745, 745, 745, 745,
+ 745, 745, 745, 745, 745, 745, 745, 745, 745, 745,
+ 745, 745, 745, 745, 745, 745, 745, 53, 745, 745,
+ 53, -366, -366, -366, -366, 199, -366, 836, -366, 745,
+ 147, -366, -366, -366, 111, -366, 289, 745, -366, 243,
+ 247, -366, 235, 1067, -366, 13, -366, -366, -366, 222,
+ 53, 745, 260, 255, 169, 172, 745, -366, -366, -6,
+ 180, 130, 130, 1067, 1067, 1067, 1067, 1067, 1067, 1067,
+ 1067, 1067, 1067, 1067, 16, 1084, 1100, 1115, 1129, 1142,
+ 1153, 1153, 319, 319, 319, 319, 303, 303, 295, 295,
+ -366, -366, -366, -366, 11, 1067, 186, 269, -366, -366,
+ -366, 190, 188, -366, 187, 745, 836, 280, -366, -366,
+ 289, 745, -366, 338, -366, 21, -366, 191, -366, -366,
+ 284, 255, -366, -366, 217, 711, 202, 204, 745, -366,
+ -366, 745, -366, -366, -366, 198, 207, -366, -366, -366,
+ 309, 296, 312, 745, 320, 307, 435, 53, 302, 745,
+ 305, 306, 308, 318, -366, -366, 504, -366, -366, -366,
+ 149, 292, 342, 354, 259, -366, -366, -366, 169, -366,
+ -366, -366, 421, -366, -366, -366, -366, -366, -366, 1036,
+ -366, -366, 277, 358, 745, 359, 745, 745, 745, 745,
+ -366, -366, -366, 325, -366, -366, 361, 234, 272, -366,
+ 326, -366, 64, -366, 276, 66, 67, 281, 607, 367,
+ -366, 21, -366, 745, 435, 371, 435, 435, 372, 397,
+ -366, 21, 677, -366, 68, 368, -366, -366, -366, -366,
+ 745, 427, -366, 461, 435, 462, -366, -366, 745, 377,
+ 435, -366
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const yytype_int16 yypgoto[] =
+{
+ -366, -366, -366, -366, -366, 400, -366, -26, -366, -366,
+ -365, -366, -366, 189, -366, -366, -366, 266, 209, -366,
+ -122, -187, -366, -366, -82, 254, -366, 133, 216, 299,
+ 168, -366, -366, 171, -304, -366, 173, -366, -366, -227,
+ -181, -183, -83, -45, -38, 137, -366, -366, -366, -308,
+ 203, 2, -366, -366, -1, 0, -88, 472, -366, -366,
+ -366, -366, -366, -10, -51, 208, -366, 474, 22, 256,
+ 265, -24, -52, -127, -12
+};
+
+/* 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 -204
+static const yytype_int16 yytable[] =
+{
+ 28, 49, 40, 137, 212, 42, 57, 76, 44, 57,
+ 139, 133, 352, 274, 145, 226, 393, 133, 272, 41,
+ 226, 68, 128, 61, 278, 230, 401, 228, 117, 57,
+ -195, 199, 229, 298, -199, 226, 57, 57, 287, 36,
+ 37, 86, 274, 87, 90, 22, 23, 140, 374, 24,
+ 77, 377, 87, 57, 46, 141, 142, 143, 144, 47,
+ 148, 149, 80, 36, 37, 81, 82, 145, 226, 145,
+ 226, 226, 226, 69, 70, 154, 155, -203, 130, 212,
+ 395, 38, 397, 398, 302, 244, 273, 213, 73, 74,
+ 28, 38, 355, 140, 305, 36, 37, 147, 309, 83,
+ 407, 201, 134, 85, 299, 264, 411, 302, 282, 296,
+ 297, 218, 233, 234, 235, 236, 237, 238, 239, 240,
+ 241, 242, 243, 227, 245, 246, 247, 248, 249, 250,
+ 251, 252, 253, 254, 255, 256, 257, 258, 259, 260,
+ 261, 262, 290, 293, 265, 128, 88, 271, 275, 276,
+ 231, 390, 232, 212, 117, 214, 384, 215, 386, 387,
+ 403, 198, 117, 228, 45, 199, 200, 199, 229, 92,
+ 36, 37, 132, 405, 68, 263, 117, 294, 268, 140,
+ 198, 409, 36, 37, 199, 200, -10, 46, 156, 36,
+ 37, 93, 47, 193, 46, 334, 36, 37, 194, 47,
+ 94, 95, 96, 36, 37, 220, 221, 97, 286, 98,
+ 99, 100, 195, 101, 102, 103, 104, 105, 106, 107,
+ 108, 109, 110, 198, 128, 284, 285, 199, 200, 197,
+ 117, 196, 36, 37, 43, 269, 117, 380, 381, 273,
+ 22, 23, 111, 216, 24, 86, 279, 87, 151, 153,
+ 228, 280, 281, 342, 199, 229, 265, 339, 112, 218,
+ 289, 75, 365, 138, 288, 113, 114, 292, 349, 75,
+ 87, 372, 295, 301, 375, 376, 22, 23, 300, 304,
+ 24, 303, 28, 307, 336, 75, 55, 60, 337, 55,
+ 391, 344, 46, 140, 340, 56, 341, 47, 56, 345,
+ 394, 36, 37, 22, 23, 353, 207, 24, 366, 55,
+ 402, 265, 346, 347, 265, 348, 55, 55, 56, -9,
+ -9, -10, 46, 350, 351, 56, 56, 47, 184, 185,
+ 186, 36, 37, 55, 182, 183, 184, 185, 186, 310,
+ 354, -100, 56, 356, 357, 364, 358, 140, 180, 181,
+ 182, 183, 184, 185, 186, 366, 359, 363, 226, 370,
+ 93, 371, 373, 378, 383, 366, -22, 382, 385, 94,
+ 95, 96, 392, 388, 396, 399, 97, 28, 98, 99,
+ 311, 3, 101, 102, 103, 104, 105, 106, 107, 108,
+ 109, 110, 4, 312, 313, 5, 314, 315, 316, 6,
+ 400, 7, 8, -89, 317, 318, 9, 10, 11, 319,
+ 12, 111, 320, 13, 14, 321, 15, 16, 17, 18,
+ 322, 19, 20, 21, 22, 23, 323, 112, 24, 25,
+ 404, 381, -85, 324, 113, 114, 310, 168, -100, 169,
+ 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
+ 180, 181, 182, 183, 184, 185, 186, 93, 187, 188,
+ 189, 190, 191, 192, 406, 408, 94, 95, 96, 410,
+ 89, 368, 277, 97, 335, 98, 99, 311, 291, 101,
+ 102, 103, 104, 105, 106, 107, 108, 109, 110, 308,
+ 312, 313, 306, 314, 315, 316, 270, 360, 362, 361,
+ -89, 317, 318, 58, 343, 59, 319, -100, 111, 320,
+ 0, 0, 321, 0, 0, 0, 0, 322, 0, 0,
+ 0, 0, 0, 323, 112, 0, 93, 0, 0, -85,
+ 0, 113, 114, 0, 0, 94, 95, 96, 0, 0,
+ 0, 2, 97, 0, 98, 99, 311, 0, 101, 102,
+ 103, 104, 105, 106, 107, 108, 109, 110, 0, 312,
+ 313, 0, 314, 315, 316, 0, 0, 0, 0, -89,
+ 317, 318, 0, 0, 0, 319, 0, 111, 320, 0,
+ 0, 321, 0, 0, 3, 0, 322, 0, 0, 0,
+ 0, 0, 323, 112, 0, 4, 0, 0, 5, 0,
+ 113, 114, 6, 0, 7, 8, 0, 0, 0, 9,
+ 10, 11, 0, 12, 0, 0, 13, 14, 0, 15,
+ 16, 17, 18, 0, 19, 20, 21, 22, 23, 93,
+ 0, 24, 25, 0, 0, 0, 0, 0, 94, 95,
+ 96, 0, 0, 0, 0, 97, 0, 98, 99, 100,
+ 3, 101, 102, 103, 104, 105, 106, 107, 108, 109,
+ 110, 4, 0, 0, 5, 0, 0, 0, 6, 0,
+ 7, 8, 0, 0, 0, 9, 10, 11, 0, 12,
+ 111, 0, 13, 14, 0, 15, 16, 17, 18, 0,
+ 19, 20, 21, 22, 23, 0, 112, 24, 25, 93,
+ 0, 0, 0, 113, 114, 0, 0, 0, 94, 95,
+ 96, 0, 0, 0, 0, 97, 0, 98, 99, 100,
+ 0, 101, 102, 103, 104, 105, 106, 107, 108, 109,
+ 110, 0, 0, 93, 0, 0, 0, 0, 0, 0,
+ 0, 0, 94, 95, 96, 0, 0, 0, 0, 97,
+ 111, 98, 99, 100, 0, 101, 102, 103, 104, 105,
+ 106, 107, 108, 109, 110, 0, 112, 93, 0, 0,
+ 0, 138, 0, 113, 114, 0, 94, 95, 96, 0,
+ 0, 0, 0, 97, 111, 98, 99, 100, 0, 101,
+ 102, 103, 104, 105, 106, 107, 108, 109, 110, 0,
+ 112, 93, 0, 0, 0, 338, 0, 113, 114, 0,
+ 94, 95, 96, 0, 0, 0, 0, 150, 111, 98,
+ 99, 100, 0, 101, 102, 103, 104, 105, 106, 107,
+ 108, 109, 110, 0, 112, 93, 0, 0, 0, 0,
+ 0, 113, 114, 0, 94, 95, 96, 0, 0, 0,
+ 0, 152, 111, 98, 99, 100, 0, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 0, 112, 0,
+ 0, 0, 124, 0, 0, 113, 114, 0, 100, 3,
+ 0, 0, 0, 0, 0, 0, 111, 0, 0, 0,
+ 4, 0, 0, 5, 0, 0, 0, 6, 0, 7,
+ 8, 0, 112, 0, 9, 10, 11, 0, 12, 113,
+ 114, 13, 14, 0, 15, 16, 17, 18, 3, 19,
+ 20, 21, 22, 23, 0, 0, 24, 25, 0, 4,
+ 0, 0, 5, 0, 0, 0, 6, 0, 7, 8,
+ 0, 0, 0, 9, 10, 11, 0, 12, 0, 0,
+ 13, 14, 0, 15, 16, 17, 18, 3, 19, 20,
+ 21, 22, 23, 0, 0, 24, 25, 0, 4, 0,
+ 129, 5, 0, 0, 0, 6, 0, 7, 8, 0,
+ 0, 0, 9, 10, 11, 0, 12, 0, 0, 13,
+ 14, 0, 15, 16, 17, 18, 0, 19, 20, 21,
+ 22, 23, 0, 4, 24, 25, 5, 0, 0, 0,
+ 6, 0, 7, 8, 0, 0, 0, 9, 10, 11,
+ 0, 12, 0, 0, 13, 0, 0, 15, 16, 0,
+ 18, 0, 19, 0, 21, 22, 23, 0, 0, 24,
+ 25, 157, 158, 159, 160, 161, 162, 163, 164, 165,
+ 166, 167, 168, 369, 169, 170, 171, 172, 173, 174,
+ 175, 176, 177, 178, 179, 180, 181, 182, 183, 184,
+ 185, 186, 157, 158, 159, 160, 161, 162, 163, 164,
+ 165, 166, 167, 168, 0, 169, 170, 171, 172, 173,
+ 174, 175, 176, 177, 178, 179, 180, 181, 182, 183,
+ 184, 185, 186, 170, 171, 172, 173, 174, 175, 176,
+ 177, 178, 179, 180, 181, 182, 183, 184, 185, 186,
+ 171, 172, 173, 174, 175, 176, 177, 178, 179, 180,
+ 181, 182, 183, 184, 185, 186, 172, 173, 174, 175,
+ 176, 177, 178, 179, 180, 181, 182, 183, 184, 185,
+ 186, 173, 174, 175, 176, 177, 178, 179, 180, 181,
+ 182, 183, 184, 185, 186, 174, 175, 176, 177, 178,
+ 179, 180, 181, 182, 183, 184, 185, 186, 176, 177,
+ 178, 179, 180, 181, 182, 183, 184, 185, 186
+};
+
+static const yytype_int16 yycheck[] =
+{
+ 1, 27, 14, 91, 131, 17, 30, 58, 20, 33,
+ 92, 4, 316, 200, 97, 4, 381, 4, 199, 17,
+ 4, 47, 74, 33, 207, 147, 391, 33, 73, 53,
+ 94, 37, 38, 17, 94, 4, 60, 61, 221, 42,
+ 43, 67, 229, 67, 70, 86, 87, 92, 356, 90,
+ 60, 359, 76, 77, 33, 93, 94, 95, 96, 38,
+ 98, 99, 62, 42, 43, 63, 64, 150, 4, 152,
+ 4, 4, 4, 3, 4, 113, 114, 94, 78, 206,
+ 384, 94, 386, 387, 271, 168, 92, 132, 37, 38,
+ 91, 94, 319, 138, 275, 42, 43, 97, 281, 42,
+ 404, 127, 95, 94, 93, 188, 410, 294, 95, 231,
+ 232, 137, 157, 158, 159, 160, 161, 162, 163, 164,
+ 165, 166, 167, 92, 169, 170, 171, 172, 173, 174,
+ 175, 176, 177, 178, 179, 180, 181, 182, 183, 184,
+ 185, 186, 224, 226, 189, 197, 92, 198, 37, 38,
+ 150, 378, 152, 280, 199, 133, 92, 135, 92, 92,
+ 92, 33, 207, 33, 3, 37, 38, 37, 38, 5,
+ 42, 43, 5, 400, 200, 187, 221, 228, 190, 224,
+ 33, 408, 42, 43, 37, 38, 5, 33, 93, 42,
+ 43, 22, 38, 52, 33, 283, 42, 43, 53, 38,
+ 31, 32, 33, 42, 43, 36, 37, 38, 220, 40,
+ 41, 42, 36, 44, 45, 46, 47, 48, 49, 50,
+ 51, 52, 53, 33, 276, 3, 4, 37, 38, 4,
+ 275, 92, 42, 43, 94, 36, 281, 3, 4, 92,
+ 86, 87, 73, 94, 90, 271, 3, 271, 111, 112,
+ 33, 4, 17, 298, 37, 38, 301, 295, 89, 285,
+ 5, 53, 3, 94, 4, 96, 97, 95, 313, 61,
+ 294, 354, 92, 4, 357, 358, 86, 87, 92, 92,
+ 90, 93, 283, 3, 93, 77, 30, 31, 4, 33,
+ 378, 93, 33, 338, 92, 30, 92, 38, 33, 92,
+ 383, 42, 43, 86, 87, 317, 17, 90, 334, 53,
+ 392, 356, 3, 17, 359, 3, 60, 61, 53, 3,
+ 4, 5, 33, 3, 17, 60, 61, 38, 33, 34,
+ 35, 42, 43, 77, 31, 32, 33, 34, 35, 1,
+ 38, 3, 77, 38, 38, 3, 38, 392, 29, 30,
+ 31, 32, 33, 34, 35, 381, 38, 65, 4, 82,
+ 22, 3, 3, 38, 38, 391, 5, 95, 92, 31,
+ 32, 33, 5, 92, 3, 3, 38, 378, 40, 41,
+ 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
+ 3, 63, 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 77, 78, 79, 80, 81,
+ 82, 83, 84, 85, 86, 87, 88, 89, 90, 91,
+ 62, 4, 94, 95, 96, 97, 1, 16, 3, 18,
+ 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 22, 36, 37,
+ 38, 39, 40, 41, 3, 3, 31, 32, 33, 92,
+ 70, 338, 206, 38, 285, 40, 41, 42, 224, 44,
+ 45, 46, 47, 48, 49, 50, 51, 52, 53, 280,
+ 55, 56, 276, 58, 59, 60, 197, 326, 330, 326,
+ 65, 66, 67, 31, 301, 31, 71, 3, 73, 74,
+ -1, -1, 77, -1, -1, -1, -1, 82, -1, -1,
+ -1, -1, -1, 88, 89, -1, 22, -1, -1, 94,
+ -1, 96, 97, -1, -1, 31, 32, 33, -1, -1,
+ -1, 0, 38, -1, 40, 41, 42, -1, 44, 45,
+ 46, 47, 48, 49, 50, 51, 52, 53, -1, 55,
+ 56, -1, 58, 59, 60, -1, -1, -1, -1, 65,
+ 66, 67, -1, -1, -1, 71, -1, 73, 74, -1,
+ -1, 77, -1, -1, 43, -1, 82, -1, -1, -1,
+ -1, -1, 88, 89, -1, 54, -1, -1, 57, -1,
+ 96, 97, 61, -1, 63, 64, -1, -1, -1, 68,
+ 69, 70, -1, 72, -1, -1, 75, 76, -1, 78,
+ 79, 80, 81, -1, 83, 84, 85, 86, 87, 22,
+ -1, 90, 91, -1, -1, -1, -1, -1, 31, 32,
+ 33, -1, -1, -1, -1, 38, -1, 40, 41, 42,
+ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ 53, 54, -1, -1, 57, -1, -1, -1, 61, -1,
+ 63, 64, -1, -1, -1, 68, 69, 70, -1, 72,
+ 73, -1, 75, 76, -1, 78, 79, 80, 81, -1,
+ 83, 84, 85, 86, 87, -1, 89, 90, 91, 22,
+ -1, -1, -1, 96, 97, -1, -1, -1, 31, 32,
+ 33, -1, -1, -1, -1, 38, -1, 40, 41, 42,
+ -1, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ 53, -1, -1, 22, -1, -1, -1, -1, -1, -1,
+ -1, -1, 31, 32, 33, -1, -1, -1, -1, 38,
+ 73, 40, 41, 42, -1, 44, 45, 46, 47, 48,
+ 49, 50, 51, 52, 53, -1, 89, 22, -1, -1,
+ -1, 94, -1, 96, 97, -1, 31, 32, 33, -1,
+ -1, -1, -1, 38, 73, 40, 41, 42, -1, 44,
+ 45, 46, 47, 48, 49, 50, 51, 52, 53, -1,
+ 89, 22, -1, -1, -1, 94, -1, 96, 97, -1,
+ 31, 32, 33, -1, -1, -1, -1, 38, 73, 40,
+ 41, 42, -1, 44, 45, 46, 47, 48, 49, 50,
+ 51, 52, 53, -1, 89, 22, -1, -1, -1, -1,
+ -1, 96, 97, -1, 31, 32, 33, -1, -1, -1,
+ -1, 38, 73, 40, 41, 42, -1, 44, 45, 46,
+ 47, 48, 49, 50, 51, 52, 53, -1, 89, -1,
+ -1, -1, 36, -1, -1, 96, 97, -1, 42, 43,
+ -1, -1, -1, -1, -1, -1, 73, -1, -1, -1,
+ 54, -1, -1, 57, -1, -1, -1, 61, -1, 63,
+ 64, -1, 89, -1, 68, 69, 70, -1, 72, 96,
+ 97, 75, 76, -1, 78, 79, 80, 81, 43, 83,
+ 84, 85, 86, 87, -1, -1, 90, 91, -1, 54,
+ -1, -1, 57, -1, -1, -1, 61, -1, 63, 64,
+ -1, -1, -1, 68, 69, 70, -1, 72, -1, -1,
+ 75, 76, -1, 78, 79, 80, 81, 43, 83, 84,
+ 85, 86, 87, -1, -1, 90, 91, -1, 54, -1,
+ 95, 57, -1, -1, -1, 61, -1, 63, 64, -1,
+ -1, -1, 68, 69, 70, -1, 72, -1, -1, 75,
+ 76, -1, 78, 79, 80, 81, -1, 83, 84, 85,
+ 86, 87, -1, 54, 90, 91, 57, -1, -1, -1,
+ 61, -1, 63, 64, -1, -1, -1, 68, 69, 70,
+ -1, 72, -1, -1, 75, -1, -1, 78, 79, -1,
+ 81, -1, 83, -1, 85, 86, 87, -1, -1, 90,
+ 91, 5, 6, 7, 8, 9, 10, 11, 12, 13,
+ 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
+ 34, 35, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, 14, 15, 16, -1, 18, 19, 20, 21, 22,
+ 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
+ 33, 34, 35, 19, 20, 21, 22, 23, 24, 25,
+ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
+ 35, 22, 23, 24, 25, 26, 27, 28, 29, 30,
+ 31, 32, 33, 34, 35, 23, 24, 25, 26, 27,
+ 28, 29, 30, 31, 32, 33, 34, 35, 25, 26,
+ 27, 28, 29, 30, 31, 32, 33, 34, 35
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const yytype_uint8 yystos[] =
+{
+ 0, 99, 0, 43, 54, 57, 61, 63, 64, 68,
+ 69, 70, 72, 75, 76, 78, 79, 80, 81, 83,
+ 84, 85, 86, 87, 90, 91, 100, 151, 152, 154,
+ 155, 164, 165, 167, 168, 169, 42, 43, 94, 149,
+ 172, 149, 172, 94, 172, 3, 33, 38, 103, 105,
+ 106, 171, 172, 161, 163, 167, 168, 169, 155, 165,
+ 167, 161, 150, 156, 157, 160, 158, 162, 105, 3,
+ 4, 101, 104, 37, 38, 163, 162, 161, 112, 152,
+ 153, 149, 149, 42, 166, 94, 105, 169, 92, 103,
+ 105, 110, 5, 22, 31, 32, 33, 38, 40, 41,
+ 42, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ 53, 73, 89, 96, 97, 138, 139, 141, 142, 143,
+ 144, 145, 146, 170, 36, 126, 127, 153, 170, 95,
+ 153, 113, 5, 4, 95, 159, 102, 154, 94, 122,
+ 141, 142, 142, 142, 142, 140, 141, 153, 142, 142,
+ 38, 143, 38, 143, 142, 142, 93, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15, 16, 18,
+ 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 52, 53, 36, 92, 4, 33, 37,
+ 38, 105, 118, 119, 120, 121, 114, 17, 105, 115,
+ 116, 117, 171, 141, 166, 166, 94, 128, 105, 111,
+ 36, 37, 122, 123, 124, 125, 4, 92, 33, 38,
+ 118, 153, 153, 141, 141, 141, 141, 141, 141, 141,
+ 141, 141, 141, 141, 140, 141, 141, 141, 141, 141,
+ 141, 141, 141, 141, 141, 141, 141, 141, 141, 141,
+ 141, 141, 141, 172, 140, 141, 147, 148, 172, 36,
+ 127, 162, 138, 92, 119, 37, 38, 115, 139, 3,
+ 4, 17, 95, 129, 3, 4, 172, 139, 4, 5,
+ 122, 123, 95, 140, 162, 92, 118, 118, 17, 93,
+ 92, 4, 119, 93, 92, 138, 126, 3, 116, 139,
+ 1, 42, 55, 56, 58, 59, 60, 66, 67, 71,
+ 74, 77, 82, 88, 95, 107, 130, 131, 132, 134,
+ 135, 136, 137, 140, 154, 111, 93, 4, 94, 142,
+ 92, 92, 141, 148, 93, 92, 3, 17, 3, 141,
+ 3, 17, 132, 172, 38, 137, 38, 38, 38, 38,
+ 131, 134, 128, 65, 3, 3, 105, 108, 125, 17,
+ 82, 3, 140, 3, 147, 140, 140, 147, 38, 109,
+ 3, 4, 95, 38, 92, 92, 92, 92, 92, 133,
+ 137, 154, 5, 108, 140, 132, 3, 132, 132, 3,
+ 3, 108, 122, 92, 62, 137, 3, 132, 3, 137,
+ 92, 132
+};
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+/* 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. */
+
+#define YYFAIL goto yyerrlab
+
+#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 \
+ { \
+ yyerror (YY_("syntax error: cannot back up")); \
+ YYERROR; \
+ } \
+while (YYID (0))
+
+
+#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. */
+
+#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
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments. */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (YYLEX_PARAM)
+#else
+# define YYLEX yylex ()
+#endif
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yy_symbol_print (stderr, \
+ Type, Value); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+#endif
+{
+ if (!yyvaluep)
+ return;
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+ YYUSE (yyoutput);
+# endif
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+#endif
+{
+ if (yytype < YYNTOKENS)
+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+ else
+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+ yy_symbol_value_print (yyoutput, yytype, yyvaluep);
+ YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *bottom, yytype_int16 *top)
+#else
+static void
+yy_stack_print (bottom, top)
+ yytype_int16 *bottom;
+ yytype_int16 *top;
+#endif
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (; bottom <= top; ++bottom)
+ YYFPRINTF (stderr, " %d", *bottom);
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, int yyrule)
+#else
+static void
+yy_reduce_print (yyvsp, yyrule)
+ YYSTYPE *yyvsp;
+ int yyrule;
+#endif
+{
+ int yynrhs = yyr2[yyrule];
+ int yyi;
+ unsigned long int yylno = yyrline[yyrule];
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+ yyrule - 1, yylno);
+ /* The symbols being reduced. */
+ for (yyi = 0; yyi < yynrhs; yyi++)
+ {
+ fprintf (stderr, " $%d = ", yyi + 1);
+ yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+ &(yyvsp[(yyi + 1) - (yynrhs)])
+ );
+ fprintf (stderr, "\n");
+ }
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (yyvsp, Rule); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined __GLIBC__ && defined _STRING_H
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+ const char *yystr;
+#endif
+{
+ YYSIZE_T yylen;
+ for (yylen = 0; yystr[yylen]; yylen++)
+ continue;
+ return yylen;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+ char *yydest;
+ const char *yysrc;
+#endif
+{
+ char *yyd = yydest;
+ const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+ quotes and backslashes, so that it's suitable for yyerror. The
+ heuristic is that double-quoting is unnecessary unless the string
+ contains an apostrophe, a comma, or backslash (other than
+ backslash-backslash). YYSTR is taken from yytname. If YYRES is
+ null, do not copy; instead, return the length of what the result
+ would have been. */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+ if (*yystr == '"')
+ {
+ YYSIZE_T yyn = 0;
+ char const *yyp = yystr;
+
+ for (;;)
+ switch (*++yyp)
+ {
+ case '\'':
+ case ',':
+ goto do_not_strip_quotes;
+
+ case '\\':
+ if (*++yyp != '\\')
+ goto do_not_strip_quotes;
+ /* Fall through. */
+ default:
+ if (yyres)
+ yyres[yyn] = *yyp;
+ yyn++;
+ break;
+
+ case '"':
+ if (yyres)
+ yyres[yyn] = '\0';
+ return yyn;
+ }
+ do_not_strip_quotes: ;
+ }
+
+ if (! yyres)
+ return yystrlen (yystr);
+
+ return yystpcpy (yyres, yystr) - yyres;
+}
+# 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];
+
+ 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;
+ }
+
+ yyf = YY_(yyformat);
+ yysize1 = yysize + yystrlen (yyf);
+ yysize_overflow |= (yysize1 < yysize);
+ yysize = yysize1;
+
+ if (yysize_overflow)
+ return YYSIZE_MAXIMUM;
+
+ 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;
+ }
+}
+#endif /* YYERROR_VERBOSE */
+
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep)
+ const char *yymsg;
+ int yytype;
+ YYSTYPE *yyvaluep;
+#endif
+{
+ YYUSE (yyvaluep);
+
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ switch (yytype)
+ {
+
+ default:
+ break;
+ }
+}
+
+
+/* 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. */
+int yychar;
+
+/* The semantic value of the look-ahead symbol. */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far. */
+int yynerrs;
+
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+ void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void)
+#else
+int
+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;
+
+ /* 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;
+
+ 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;
+
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. So pushing a state here evens the stacks. */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ 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
+ be undefined if yyoverflow is a macro. */
+ yyoverflow (YY_("memory exhausted"),
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+
+ &yystacksize);
+
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyexhaustedlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyexhaustedlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ yytype_int16 *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyexhaustedlab;
+ YYSTACK_RELOCATE (yyss);
+ YYSTACK_RELOCATE (yyvs);
+
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+ /* Do appropriate processing given the current state. Read a
+ look-ahead token if we need one and don't already have one. */
+
+ /* First try to decide what to do without reference to look-ahead token. */
+ yyn = yypact[yystate];
+ if (yyn == YYPACT_NINF)
+ goto yydefault;
+
+ /* Not known => get a look-ahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = YYLEX;
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yyn == 0 || yyn == YYTABLE_NINF)
+ 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. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+ /* Discard the shifted token unless it is eof. */
+ if (yychar != YYEOF)
+ yychar = YYEMPTY;
+
+ yystate = yyn;
+ *++yyvsp = yylval;
+
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ `$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 4:
+#line 109 "cc.y"
+ {
+ dodecl(xdecl, lastclass, lasttype, Z);
+ }
+ break;
+
+ case 6:
+#line 114 "cc.y"
+ {
+ lastdcl = T;
+ firstarg = S;
+ dodecl(xdecl, lastclass, lasttype, (yyvsp[(2) - (2)].node));
+ if(lastdcl == T || lastdcl->etype != TFUNC) {
+ diag((yyvsp[(2) - (2)].node), "not a function");
+ lastdcl = types[TFUNC];
+ }
+ thisfn = lastdcl;
+ markdcl();
+ firstdcl = dclstack;
+ argmark((yyvsp[(2) - (2)].node), 0);
+ }
+ break;
+
+ case 7:
+#line 128 "cc.y"
+ {
+ argmark((yyvsp[(2) - (4)].node), 1);
+ }
+ break;
+
+ case 8:
+#line 132 "cc.y"
+ {
+ Node *n;
+
+ n = revertdcl();
+ if(n)
+ (yyvsp[(6) - (6)].node) = new(OLIST, n, (yyvsp[(6) - (6)].node));
+ if(!debug['a'] && !debug['Z'])
+ codgen((yyvsp[(6) - (6)].node), (yyvsp[(2) - (6)].node));
+ }
+ break;
+
+ case 9:
+#line 144 "cc.y"
+ {
+ dodecl(xdecl, lastclass, lasttype, (yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 10:
+#line 148 "cc.y"
+ {
+ (yyvsp[(1) - (1)].node) = dodecl(xdecl, lastclass, lasttype, (yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 11:
+#line 152 "cc.y"
+ {
+ doinit((yyvsp[(1) - (4)].node)->sym, (yyvsp[(1) - (4)].node)->type, 0L, (yyvsp[(4) - (4)].node));
+ }
+ break;
+
+ case 14:
+#line 160 "cc.y"
+ {
+ (yyval.node) = new(OIND, (yyvsp[(3) - (3)].node), Z);
+ (yyval.node)->garb = simpleg((yyvsp[(2) - (3)].lval));
+ }
+ break;
+
+ case 16:
+#line 168 "cc.y"
+ {
+ (yyval.node) = (yyvsp[(2) - (3)].node);
+ }
+ break;
+
+ case 17:
+#line 172 "cc.y"
+ {
+ (yyval.node) = new(OFUNC, (yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].node));
+ }
+ break;
+
+ case 18:
+#line 176 "cc.y"
+ {
+ (yyval.node) = new(OARRAY, (yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].node));
+ }
+ break;
+
+ case 19:
+#line 185 "cc.y"
+ {
+ (yyval.node) = dodecl(adecl, lastclass, lasttype, Z);
+ }
+ break;
+
+ case 20:
+#line 189 "cc.y"
+ {
+ (yyval.node) = (yyvsp[(2) - (3)].node);
+ }
+ break;
+
+ case 21:
+#line 195 "cc.y"
+ {
+ dodecl(adecl, lastclass, lasttype, (yyvsp[(1) - (1)].node));
+ (yyval.node) = Z;
+ }
+ break;
+
+ case 22:
+#line 200 "cc.y"
+ {
+ (yyvsp[(1) - (1)].node) = dodecl(adecl, lastclass, lasttype, (yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 23:
+#line 204 "cc.y"
+ {
+ int32 w;
+
+ w = (yyvsp[(1) - (4)].node)->sym->type->width;
+ (yyval.node) = doinit((yyvsp[(1) - (4)].node)->sym, (yyvsp[(1) - (4)].node)->type, 0L, (yyvsp[(4) - (4)].node));
+ (yyval.node) = contig((yyvsp[(1) - (4)].node)->sym, (yyval.node), w);
+ }
+ break;
+
+ case 24:
+#line 212 "cc.y"
+ {
+ (yyval.node) = (yyvsp[(1) - (3)].node);
+ if((yyvsp[(3) - (3)].node) != Z) {
+ (yyval.node) = (yyvsp[(3) - (3)].node);
+ if((yyvsp[(1) - (3)].node) != Z)
+ (yyval.node) = new(OLIST, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ }
+ break;
+
+ case 27:
+#line 229 "cc.y"
+ {
+ dodecl(pdecl, lastclass, lasttype, (yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 29:
+#line 239 "cc.y"
+ {
+ lasttype = (yyvsp[(1) - (1)].type);
+ }
+ break;
+
+ case 31:
+#line 244 "cc.y"
+ {
+ lasttype = (yyvsp[(2) - (2)].type);
+ }
+ break;
+
+ case 33:
+#line 250 "cc.y"
+ {
+ lastfield = 0;
+ edecl(CXXX, lasttype, S);
+ }
+ break;
+
+ case 35:
+#line 258 "cc.y"
+ {
+ dodecl(edecl, CXXX, lasttype, (yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 37:
+#line 265 "cc.y"
+ {
+ lastbit = 0;
+ firstbit = 1;
+ }
+ break;
+
+ case 38:
+#line 270 "cc.y"
+ {
+ (yyval.node) = new(OBIT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 39:
+#line 274 "cc.y"
+ {
+ (yyval.node) = new(OBIT, Z, (yyvsp[(2) - (2)].node));
+ }
+ break;
+
+ case 40:
+#line 282 "cc.y"
+ {
+ (yyval.node) = (Z);
+ }
+ break;
+
+ case 42:
+#line 289 "cc.y"
+ {
+ (yyval.node) = new(OIND, (Z), Z);
+ (yyval.node)->garb = simpleg((yyvsp[(2) - (2)].lval));
+ }
+ break;
+
+ case 43:
+#line 294 "cc.y"
+ {
+ (yyval.node) = new(OIND, (yyvsp[(3) - (3)].node), Z);
+ (yyval.node)->garb = simpleg((yyvsp[(2) - (3)].lval));
+ }
+ break;
+
+ case 46:
+#line 303 "cc.y"
+ {
+ (yyval.node) = new(OFUNC, (yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].node));
+ }
+ break;
+
+ case 47:
+#line 307 "cc.y"
+ {
+ (yyval.node) = new(OARRAY, (yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].node));
+ }
+ break;
+
+ case 48:
+#line 313 "cc.y"
+ {
+ (yyval.node) = new(OFUNC, (Z), Z);
+ }
+ break;
+
+ case 49:
+#line 317 "cc.y"
+ {
+ (yyval.node) = new(OARRAY, (Z), (yyvsp[(2) - (3)].node));
+ }
+ break;
+
+ case 50:
+#line 321 "cc.y"
+ {
+ (yyval.node) = (yyvsp[(2) - (3)].node);
+ }
+ break;
+
+ case 52:
+#line 328 "cc.y"
+ {
+ (yyval.node) = new(OINIT, invert((yyvsp[(2) - (3)].node)), Z);
+ }
+ break;
+
+ case 53:
+#line 334 "cc.y"
+ {
+ (yyval.node) = new(OARRAY, (yyvsp[(2) - (3)].node), Z);
+ }
+ break;
+
+ case 54:
+#line 338 "cc.y"
+ {
+ (yyval.node) = new(OELEM, Z, Z);
+ (yyval.node)->sym = (yyvsp[(2) - (2)].sym);
+ }
+ break;
+
+ case 57:
+#line 347 "cc.y"
+ {
+ (yyval.node) = new(OLIST, (yyvsp[(1) - (3)].node), (yyvsp[(2) - (3)].node));
+ }
+ break;
+
+ case 59:
+#line 352 "cc.y"
+ {
+ (yyval.node) = new(OLIST, (yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node));
+ }
+ break;
+
+ case 62:
+#line 360 "cc.y"
+ {
+ (yyval.node) = new(OLIST, (yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node));
+ }
+ break;
+
+ case 63:
+#line 365 "cc.y"
+ {
+ (yyval.node) = Z;
+ }
+ break;
+
+ case 64:
+#line 369 "cc.y"
+ {
+ (yyval.node) = invert((yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 66:
+#line 377 "cc.y"
+ {
+ (yyval.node) = new(OPROTO, (yyvsp[(2) - (2)].node), Z);
+ (yyval.node)->type = (yyvsp[(1) - (2)].type);
+ }
+ break;
+
+ case 67:
+#line 382 "cc.y"
+ {
+ (yyval.node) = new(OPROTO, (yyvsp[(2) - (2)].node), Z);
+ (yyval.node)->type = (yyvsp[(1) - (2)].type);
+ }
+ break;
+
+ case 68:
+#line 387 "cc.y"
+ {
+ (yyval.node) = new(ODOTDOT, Z, Z);
+ }
+ break;
+
+ case 69:
+#line 391 "cc.y"
+ {
+ (yyval.node) = new(OLIST, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 70:
+#line 397 "cc.y"
+ {
+ (yyval.node) = invert((yyvsp[(2) - (3)].node));
+ // if($2 != Z)
+ // $$ = new(OLIST, $2, $$);
+ if((yyval.node) == Z)
+ (yyval.node) = new(OLIST, Z, Z);
+ }
+ break;
+
+ case 71:
+#line 406 "cc.y"
+ {
+ (yyval.node) = Z;
+ }
+ break;
+
+ case 72:
+#line 410 "cc.y"
+ {
+ (yyval.node) = new(OLIST, (yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node));
+ }
+ break;
+
+ case 73:
+#line 414 "cc.y"
+ {
+ (yyval.node) = new(OLIST, (yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node));
+ }
+ break;
+
+ case 75:
+#line 421 "cc.y"
+ {
+ (yyval.node) = new(OLIST, (yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node));
+ }
+ break;
+
+ case 76:
+#line 427 "cc.y"
+ {
+ (yyval.node) = new(OCASE, (yyvsp[(2) - (3)].node), Z);
+ }
+ break;
+
+ case 77:
+#line 431 "cc.y"
+ {
+ (yyval.node) = new(OCASE, Z, Z);
+ }
+ break;
+
+ case 78:
+#line 435 "cc.y"
+ {
+ (yyval.node) = new(OLABEL, dcllabel((yyvsp[(1) - (2)].sym), 1), Z);
+ }
+ break;
+
+ case 79:
+#line 441 "cc.y"
+ {
+ (yyval.node) = Z;
+ }
+ break;
+
+ case 81:
+#line 446 "cc.y"
+ {
+ (yyval.node) = new(OLIST, (yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node));
+ }
+ break;
+
+ case 83:
+#line 453 "cc.y"
+ {
+ (yyval.node) = (yyvsp[(2) - (2)].node);
+ }
+ break;
+
+ case 85:
+#line 459 "cc.y"
+ {
+ markdcl();
+ }
+ break;
+
+ case 86:
+#line 463 "cc.y"
+ {
+ (yyval.node) = revertdcl();
+ if((yyval.node))
+ (yyval.node) = new(OLIST, (yyval.node), (yyvsp[(2) - (2)].node));
+ else
+ (yyval.node) = (yyvsp[(2) - (2)].node);
+ }
+ break;
+
+ case 87:
+#line 471 "cc.y"
+ {
+ (yyval.node) = new(OIF, (yyvsp[(3) - (5)].node), new(OLIST, (yyvsp[(5) - (5)].node), Z));
+ if((yyvsp[(5) - (5)].node) == Z)
+ warn((yyvsp[(3) - (5)].node), "empty if body");
+ }
+ break;
+
+ case 88:
+#line 477 "cc.y"
+ {
+ (yyval.node) = new(OIF, (yyvsp[(3) - (7)].node), new(OLIST, (yyvsp[(5) - (7)].node), (yyvsp[(7) - (7)].node)));
+ if((yyvsp[(5) - (7)].node) == Z)
+ warn((yyvsp[(3) - (7)].node), "empty if body");
+ if((yyvsp[(7) - (7)].node) == Z)
+ warn((yyvsp[(3) - (7)].node), "empty else body");
+ }
+ break;
+
+ case 89:
+#line 484 "cc.y"
+ { markdcl(); }
+ break;
+
+ case 90:
+#line 485 "cc.y"
+ {
+ (yyval.node) = revertdcl();
+ if((yyval.node)){
+ if((yyvsp[(4) - (10)].node))
+ (yyvsp[(4) - (10)].node) = new(OLIST, (yyval.node), (yyvsp[(4) - (10)].node));
+ else
+ (yyvsp[(4) - (10)].node) = (yyval.node);
+ }
+ (yyval.node) = new(OFOR, new(OLIST, (yyvsp[(6) - (10)].node), new(OLIST, (yyvsp[(4) - (10)].node), (yyvsp[(8) - (10)].node))), (yyvsp[(10) - (10)].node));
+ }
+ break;
+
+ case 91:
+#line 496 "cc.y"
+ {
+ (yyval.node) = new(OWHILE, (yyvsp[(3) - (5)].node), (yyvsp[(5) - (5)].node));
+ }
+ break;
+
+ case 92:
+#line 500 "cc.y"
+ {
+ (yyval.node) = new(ODWHILE, (yyvsp[(5) - (7)].node), (yyvsp[(2) - (7)].node));
+ }
+ break;
+
+ case 93:
+#line 504 "cc.y"
+ {
+ (yyval.node) = new(ORETURN, (yyvsp[(2) - (3)].node), Z);
+ (yyval.node)->type = thisfn->link;
+ }
+ break;
+
+ case 94:
+#line 509 "cc.y"
+ {
+ (yyval.node) = new(OCONST, Z, Z);
+ (yyval.node)->vconst = 0;
+ (yyval.node)->type = types[TINT];
+ (yyvsp[(3) - (5)].node) = new(OSUB, (yyval.node), (yyvsp[(3) - (5)].node));
+
+ (yyval.node) = new(OCONST, Z, Z);
+ (yyval.node)->vconst = 0;
+ (yyval.node)->type = types[TINT];
+ (yyvsp[(3) - (5)].node) = new(OSUB, (yyval.node), (yyvsp[(3) - (5)].node));
+
+ (yyval.node) = new(OSWITCH, (yyvsp[(3) - (5)].node), (yyvsp[(5) - (5)].node));
+ }
+ break;
+
+ case 95:
+#line 523 "cc.y"
+ {
+ (yyval.node) = new(OBREAK, Z, Z);
+ }
+ break;
+
+ case 96:
+#line 527 "cc.y"
+ {
+ (yyval.node) = new(OCONTINUE, Z, Z);
+ }
+ break;
+
+ case 97:
+#line 531 "cc.y"
+ {
+ (yyval.node) = new(OGOTO, dcllabel((yyvsp[(2) - (3)].sym), 0), Z);
+ }
+ break;
+
+ case 98:
+#line 535 "cc.y"
+ {
+ (yyval.node) = new(OUSED, (yyvsp[(3) - (5)].node), Z);
+ }
+ break;
+
+ case 99:
+#line 539 "cc.y"
+ {
+ (yyval.node) = new(OSET, (yyvsp[(3) - (5)].node), Z);
+ }
+ break;
+
+ case 100:
+#line 544 "cc.y"
+ {
+ (yyval.node) = Z;
+ }
+ break;
+
+ case 102:
+#line 550 "cc.y"
+ {
+ (yyval.node) = Z;
+ }
+ break;
+
+ case 104:
+#line 557 "cc.y"
+ {
+ (yyval.node) = new(OCAST, (yyvsp[(1) - (1)].node), Z);
+ (yyval.node)->type = types[TLONG];
+ }
+ break;
+
+ case 106:
+#line 565 "cc.y"
+ {
+ (yyval.node) = new(OCOMMA, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 108:
+#line 572 "cc.y"
+ {
+ (yyval.node) = new(OMUL, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 109:
+#line 576 "cc.y"
+ {
+ (yyval.node) = new(ODIV, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 110:
+#line 580 "cc.y"
+ {
+ (yyval.node) = new(OMOD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 111:
+#line 584 "cc.y"
+ {
+ (yyval.node) = new(OADD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 112:
+#line 588 "cc.y"
+ {
+ (yyval.node) = new(OSUB, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 113:
+#line 592 "cc.y"
+ {
+ (yyval.node) = new(OASHR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 114:
+#line 596 "cc.y"
+ {
+ (yyval.node) = new(OASHL, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 115:
+#line 600 "cc.y"
+ {
+ (yyval.node) = new(OLT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 116:
+#line 604 "cc.y"
+ {
+ (yyval.node) = new(OGT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 117:
+#line 608 "cc.y"
+ {
+ (yyval.node) = new(OLE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 118:
+#line 612 "cc.y"
+ {
+ (yyval.node) = new(OGE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 119:
+#line 616 "cc.y"
+ {
+ (yyval.node) = new(OEQ, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 120:
+#line 620 "cc.y"
+ {
+ (yyval.node) = new(ONE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 121:
+#line 624 "cc.y"
+ {
+ (yyval.node) = new(OAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 122:
+#line 628 "cc.y"
+ {
+ (yyval.node) = new(OXOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 123:
+#line 632 "cc.y"
+ {
+ (yyval.node) = new(OOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 124:
+#line 636 "cc.y"
+ {
+ (yyval.node) = new(OANDAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 125:
+#line 640 "cc.y"
+ {
+ (yyval.node) = new(OOROR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 126:
+#line 644 "cc.y"
+ {
+ (yyval.node) = new(OCOND, (yyvsp[(1) - (5)].node), new(OLIST, (yyvsp[(3) - (5)].node), (yyvsp[(5) - (5)].node)));
+ }
+ break;
+
+ case 127:
+#line 648 "cc.y"
+ {
+ (yyval.node) = new(OAS, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 128:
+#line 652 "cc.y"
+ {
+ (yyval.node) = new(OASADD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 129:
+#line 656 "cc.y"
+ {
+ (yyval.node) = new(OASSUB, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 130:
+#line 660 "cc.y"
+ {
+ (yyval.node) = new(OASMUL, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 131:
+#line 664 "cc.y"
+ {
+ (yyval.node) = new(OASDIV, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 132:
+#line 668 "cc.y"
+ {
+ (yyval.node) = new(OASMOD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 133:
+#line 672 "cc.y"
+ {
+ (yyval.node) = new(OASASHL, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 134:
+#line 676 "cc.y"
+ {
+ (yyval.node) = new(OASASHR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 135:
+#line 680 "cc.y"
+ {
+ (yyval.node) = new(OASAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 136:
+#line 684 "cc.y"
+ {
+ (yyval.node) = new(OASXOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 137:
+#line 688 "cc.y"
+ {
+ (yyval.node) = new(OASOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 139:
+#line 695 "cc.y"
+ {
+ (yyval.node) = new(OCAST, (yyvsp[(5) - (5)].node), Z);
+ dodecl(NODECL, CXXX, (yyvsp[(2) - (5)].type), (yyvsp[(3) - (5)].node));
+ (yyval.node)->type = lastdcl;
+ (yyval.node)->xcast = 1;
+ }
+ break;
+
+ case 140:
+#line 702 "cc.y"
+ {
+ (yyval.node) = new(OSTRUCT, (yyvsp[(6) - (7)].node), Z);
+ dodecl(NODECL, CXXX, (yyvsp[(2) - (7)].type), (yyvsp[(3) - (7)].node));
+ (yyval.node)->type = lastdcl;
+ }
+ break;
+
+ case 142:
+#line 711 "cc.y"
+ {
+ (yyval.node) = new(OIND, (yyvsp[(2) - (2)].node), Z);
+ }
+ break;
+
+ case 143:
+#line 715 "cc.y"
+ {
+ (yyval.node) = new(OADDR, (yyvsp[(2) - (2)].node), Z);
+ }
+ break;
+
+ case 144:
+#line 719 "cc.y"
+ {
+ (yyval.node) = new(OPOS, (yyvsp[(2) - (2)].node), Z);
+ }
+ break;
+
+ case 145:
+#line 723 "cc.y"
+ {
+ (yyval.node) = new(ONEG, (yyvsp[(2) - (2)].node), Z);
+ }
+ break;
+
+ case 146:
+#line 727 "cc.y"
+ {
+ (yyval.node) = new(ONOT, (yyvsp[(2) - (2)].node), Z);
+ }
+ break;
+
+ case 147:
+#line 731 "cc.y"
+ {
+ (yyval.node) = new(OCOM, (yyvsp[(2) - (2)].node), Z);
+ }
+ break;
+
+ case 148:
+#line 735 "cc.y"
+ {
+ (yyval.node) = new(OPREINC, (yyvsp[(2) - (2)].node), Z);
+ }
+ break;
+
+ case 149:
+#line 739 "cc.y"
+ {
+ (yyval.node) = new(OPREDEC, (yyvsp[(2) - (2)].node), Z);
+ }
+ break;
+
+ case 150:
+#line 743 "cc.y"
+ {
+ (yyval.node) = new(OSIZE, (yyvsp[(2) - (2)].node), Z);
+ }
+ break;
+
+ case 151:
+#line 747 "cc.y"
+ {
+ (yyval.node) = new(OSIGN, (yyvsp[(2) - (2)].node), Z);
+ }
+ break;
+
+ case 152:
+#line 753 "cc.y"
+ {
+ (yyval.node) = (yyvsp[(2) - (3)].node);
+ }
+ break;
+
+ case 153:
+#line 757 "cc.y"
+ {
+ (yyval.node) = new(OSIZE, Z, Z);
+ dodecl(NODECL, CXXX, (yyvsp[(3) - (5)].type), (yyvsp[(4) - (5)].node));
+ (yyval.node)->type = lastdcl;
+ }
+ break;
+
+ case 154:
+#line 763 "cc.y"
+ {
+ (yyval.node) = new(OSIGN, Z, Z);
+ dodecl(NODECL, CXXX, (yyvsp[(3) - (5)].type), (yyvsp[(4) - (5)].node));
+ (yyval.node)->type = lastdcl;
+ }
+ break;
+
+ case 155:
+#line 769 "cc.y"
+ {
+ (yyval.node) = new(OFUNC, (yyvsp[(1) - (4)].node), Z);
+ if((yyvsp[(1) - (4)].node)->op == ONAME)
+ if((yyvsp[(1) - (4)].node)->type == T)
+ dodecl(xdecl, CXXX, types[TINT], (yyval.node));
+ (yyval.node)->right = invert((yyvsp[(3) - (4)].node));
+ }
+ break;
+
+ case 156:
+#line 777 "cc.y"
+ {
+ (yyval.node) = new(OIND, new(OADD, (yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].node)), Z);
+ }
+ break;
+
+ case 157:
+#line 781 "cc.y"
+ {
+ (yyval.node) = new(ODOT, new(OIND, (yyvsp[(1) - (3)].node), Z), Z);
+ (yyval.node)->sym = (yyvsp[(3) - (3)].sym);
+ }
+ break;
+
+ case 158:
+#line 786 "cc.y"
+ {
+ (yyval.node) = new(ODOT, (yyvsp[(1) - (3)].node), Z);
+ (yyval.node)->sym = (yyvsp[(3) - (3)].sym);
+ }
+ break;
+
+ case 159:
+#line 791 "cc.y"
+ {
+ (yyval.node) = new(OPOSTINC, (yyvsp[(1) - (2)].node), Z);
+ }
+ break;
+
+ case 160:
+#line 795 "cc.y"
+ {
+ (yyval.node) = new(OPOSTDEC, (yyvsp[(1) - (2)].node), Z);
+ }
+ break;
+
+ case 162:
+#line 800 "cc.y"
+ {
+ (yyval.node) = new(OCONST, Z, Z);
+ (yyval.node)->type = types[TINT];
+ (yyval.node)->vconst = (yyvsp[(1) - (1)].vval);
+ (yyval.node)->cstring = strdup(symb);
+ }
+ break;
+
+ case 163:
+#line 807 "cc.y"
+ {
+ (yyval.node) = new(OCONST, Z, Z);
+ (yyval.node)->type = types[TLONG];
+ (yyval.node)->vconst = (yyvsp[(1) - (1)].vval);
+ (yyval.node)->cstring = strdup(symb);
+ }
+ break;
+
+ case 164:
+#line 814 "cc.y"
+ {
+ (yyval.node) = new(OCONST, Z, Z);
+ (yyval.node)->type = types[TUINT];
+ (yyval.node)->vconst = (yyvsp[(1) - (1)].vval);
+ (yyval.node)->cstring = strdup(symb);
+ }
+ break;
+
+ case 165:
+#line 821 "cc.y"
+ {
+ (yyval.node) = new(OCONST, Z, Z);
+ (yyval.node)->type = types[TULONG];
+ (yyval.node)->vconst = (yyvsp[(1) - (1)].vval);
+ (yyval.node)->cstring = strdup(symb);
+ }
+ break;
+
+ case 166:
+#line 828 "cc.y"
+ {
+ (yyval.node) = new(OCONST, Z, Z);
+ (yyval.node)->type = types[TDOUBLE];
+ (yyval.node)->fconst = (yyvsp[(1) - (1)].dval);
+ (yyval.node)->cstring = strdup(symb);
+ }
+ break;
+
+ case 167:
+#line 835 "cc.y"
+ {
+ (yyval.node) = new(OCONST, Z, Z);
+ (yyval.node)->type = types[TFLOAT];
+ (yyval.node)->fconst = (yyvsp[(1) - (1)].dval);
+ (yyval.node)->cstring = strdup(symb);
+ }
+ break;
+
+ case 168:
+#line 842 "cc.y"
+ {
+ (yyval.node) = new(OCONST, Z, Z);
+ (yyval.node)->type = types[TVLONG];
+ (yyval.node)->vconst = (yyvsp[(1) - (1)].vval);
+ (yyval.node)->cstring = strdup(symb);
+ }
+ break;
+
+ case 169:
+#line 849 "cc.y"
+ {
+ (yyval.node) = new(OCONST, Z, Z);
+ (yyval.node)->type = types[TUVLONG];
+ (yyval.node)->vconst = (yyvsp[(1) - (1)].vval);
+ (yyval.node)->cstring = strdup(symb);
+ }
+ break;
+
+ case 172:
+#line 860 "cc.y"
+ {
+ (yyval.node) = new(OSTRING, Z, Z);
+ (yyval.node)->type = typ(TARRAY, types[TCHAR]);
+ (yyval.node)->type->width = (yyvsp[(1) - (1)].sval).l + 1;
+ (yyval.node)->cstring = (yyvsp[(1) - (1)].sval).s;
+ (yyval.node)->sym = symstring;
+ (yyval.node)->etype = TARRAY;
+ (yyval.node)->class = CSTATIC;
+ }
+ break;
+
+ case 173:
+#line 870 "cc.y"
+ {
+ char *s;
+ int n;
+
+ n = (yyvsp[(1) - (2)].node)->type->width - 1;
+ s = alloc(n+(yyvsp[(2) - (2)].sval).l+MAXALIGN);
+
+ memcpy(s, (yyvsp[(1) - (2)].node)->cstring, n);
+ memcpy(s+n, (yyvsp[(2) - (2)].sval).s, (yyvsp[(2) - (2)].sval).l);
+ 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)->cstring = s;
+ }
+ break;
+
+ case 174:
+#line 888 "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)->sym = symstring;
+ (yyval.node)->etype = TARRAY;
+ (yyval.node)->class = CSTATIC;
+ }
+ break;
+
+ case 175:
+#line 898 "cc.y"
+ {
+ char *s;
+ int n;
+
+ n = (yyvsp[(1) - (2)].node)->type->width - sizeof(ushort);
+ 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;
+
+ (yyval.node) = (yyvsp[(1) - (2)].node);
+ (yyval.node)->type->width += (yyvsp[(2) - (2)].sval).l;
+ (yyval.node)->rstring = (ushort*)s;
+ }
+ break;
+
+ case 176:
+#line 915 "cc.y"
+ {
+ (yyval.node) = Z;
+ }
+ break;
+
+ case 179:
+#line 923 "cc.y"
+ {
+ (yyval.node) = new(OLIST, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 180:
+#line 929 "cc.y"
+ {
+ (yyval.tyty).t1 = strf;
+ (yyval.tyty).t2 = strl;
+ (yyval.tyty).t3 = lasttype;
+ (yyval.tyty).c = lastclass;
+ strf = T;
+ strl = T;
+ lastbit = 0;
+ firstbit = 1;
+ lastclass = CXXX;
+ lasttype = T;
+ }
+ break;
+
+ case 181:
+#line 942 "cc.y"
+ {
+ (yyval.type) = strf;
+ strf = (yyvsp[(2) - (4)].tyty).t1;
+ strl = (yyvsp[(2) - (4)].tyty).t2;
+ lasttype = (yyvsp[(2) - (4)].tyty).t3;
+ lastclass = (yyvsp[(2) - (4)].tyty).c;
+ }
+ break;
+
+ case 182:
+#line 951 "cc.y"
+ {
+ lastclass = CXXX;
+ lasttype = types[TINT];
+ }
+ break;
+
+ case 184:
+#line 959 "cc.y"
+ {
+ (yyval.tycl).t = (yyvsp[(1) - (1)].type);
+ (yyval.tycl).c = CXXX;
+ }
+ break;
+
+ case 185:
+#line 964 "cc.y"
+ {
+ (yyval.tycl).t = simplet((yyvsp[(1) - (1)].lval));
+ (yyval.tycl).c = CXXX;
+ }
+ break;
+
+ case 186:
+#line 969 "cc.y"
+ {
+ (yyval.tycl).t = simplet((yyvsp[(1) - (1)].lval));
+ (yyval.tycl).c = simplec((yyvsp[(1) - (1)].lval));
+ (yyval.tycl).t = garbt((yyval.tycl).t, (yyvsp[(1) - (1)].lval));
+ }
+ break;
+
+ case 187:
+#line 975 "cc.y"
+ {
+ (yyval.tycl).t = (yyvsp[(1) - (2)].type);
+ (yyval.tycl).c = simplec((yyvsp[(2) - (2)].lval));
+ (yyval.tycl).t = garbt((yyval.tycl).t, (yyvsp[(2) - (2)].lval));
+ if((yyvsp[(2) - (2)].lval) & ~BCLASS & ~BGARB)
+ diag(Z, "duplicate types given: %T and %Q", (yyvsp[(1) - (2)].type), (yyvsp[(2) - (2)].lval));
+ }
+ break;
+
+ case 188:
+#line 983 "cc.y"
+ {
+ (yyval.tycl).t = simplet(typebitor((yyvsp[(1) - (2)].lval), (yyvsp[(2) - (2)].lval)));
+ (yyval.tycl).c = simplec((yyvsp[(2) - (2)].lval));
+ (yyval.tycl).t = garbt((yyval.tycl).t, (yyvsp[(2) - (2)].lval));
+ }
+ break;
+
+ case 189:
+#line 989 "cc.y"
+ {
+ (yyval.tycl).t = (yyvsp[(2) - (3)].type);
+ (yyval.tycl).c = simplec((yyvsp[(1) - (3)].lval));
+ (yyval.tycl).t = garbt((yyval.tycl).t, (yyvsp[(1) - (3)].lval)|(yyvsp[(3) - (3)].lval));
+ }
+ break;
+
+ case 190:
+#line 995 "cc.y"
+ {
+ (yyval.tycl).t = simplet((yyvsp[(2) - (2)].lval));
+ (yyval.tycl).c = simplec((yyvsp[(1) - (2)].lval));
+ (yyval.tycl).t = garbt((yyval.tycl).t, (yyvsp[(1) - (2)].lval));
+ }
+ break;
+
+ case 191:
+#line 1001 "cc.y"
+ {
+ (yyval.tycl).t = simplet(typebitor((yyvsp[(2) - (3)].lval), (yyvsp[(3) - (3)].lval)));
+ (yyval.tycl).c = simplec((yyvsp[(1) - (3)].lval)|(yyvsp[(3) - (3)].lval));
+ (yyval.tycl).t = garbt((yyval.tycl).t, (yyvsp[(1) - (3)].lval)|(yyvsp[(3) - (3)].lval));
+ }
+ break;
+
+ case 192:
+#line 1009 "cc.y"
+ {
+ (yyval.type) = (yyvsp[(1) - (1)].tycl).t;
+ if((yyvsp[(1) - (1)].tycl).c != CXXX)
+ diag(Z, "illegal combination of class 4: %s", cnames[(yyvsp[(1) - (1)].tycl).c]);
+ }
+ break;
+
+ case 193:
+#line 1017 "cc.y"
+ {
+ lasttype = (yyvsp[(1) - (1)].tycl).t;
+ lastclass = (yyvsp[(1) - (1)].tycl).c;
+ }
+ break;
+
+ case 194:
+#line 1024 "cc.y"
+ {
+ dotag((yyvsp[(2) - (2)].sym), TSTRUCT, 0);
+ (yyval.type) = (yyvsp[(2) - (2)].sym)->suetag;
+ }
+ break;
+
+ case 195:
+#line 1029 "cc.y"
+ {
+ dotag((yyvsp[(2) - (2)].sym), TSTRUCT, autobn);
+ }
+ break;
+
+ case 196:
+#line 1033 "cc.y"
+ {
+ (yyval.type) = (yyvsp[(2) - (4)].sym)->suetag;
+ if((yyval.type)->link != T)
+ diag(Z, "redeclare tag: %s", (yyvsp[(2) - (4)].sym)->name);
+ (yyval.type)->link = (yyvsp[(4) - (4)].type);
+ sualign((yyval.type));
+ }
+ break;
+
+ case 197:
+#line 1041 "cc.y"
+ {
+ taggen++;
+ sprint(symb, "_%d_", taggen);
+ (yyval.type) = dotag(lookup(), TSTRUCT, autobn);
+ (yyval.type)->link = (yyvsp[(2) - (2)].type);
+ sualign((yyval.type));
+ }
+ break;
+
+ case 198:
+#line 1049 "cc.y"
+ {
+ dotag((yyvsp[(2) - (2)].sym), TUNION, 0);
+ (yyval.type) = (yyvsp[(2) - (2)].sym)->suetag;
+ }
+ break;
+
+ case 199:
+#line 1054 "cc.y"
+ {
+ dotag((yyvsp[(2) - (2)].sym), TUNION, autobn);
+ }
+ break;
+
+ case 200:
+#line 1058 "cc.y"
+ {
+ (yyval.type) = (yyvsp[(2) - (4)].sym)->suetag;
+ if((yyval.type)->link != T)
+ diag(Z, "redeclare tag: %s", (yyvsp[(2) - (4)].sym)->name);
+ (yyval.type)->link = (yyvsp[(4) - (4)].type);
+ sualign((yyval.type));
+ }
+ break;
+
+ case 201:
+#line 1066 "cc.y"
+ {
+ taggen++;
+ sprint(symb, "_%d_", taggen);
+ (yyval.type) = dotag(lookup(), TUNION, autobn);
+ (yyval.type)->link = (yyvsp[(2) - (2)].type);
+ sualign((yyval.type));
+ }
+ break;
+
+ case 202:
+#line 1074 "cc.y"
+ {
+ dotag((yyvsp[(2) - (2)].sym), TENUM, 0);
+ (yyval.type) = (yyvsp[(2) - (2)].sym)->suetag;
+ if((yyval.type)->link == T)
+ (yyval.type)->link = types[TINT];
+ (yyval.type) = (yyval.type)->link;
+ }
+ break;
+
+ case 203:
+#line 1082 "cc.y"
+ {
+ dotag((yyvsp[(2) - (2)].sym), TENUM, autobn);
+ }
+ break;
+
+ case 204:
+#line 1086 "cc.y"
+ {
+ en.tenum = T;
+ en.cenum = T;
+ }
+ break;
+
+ case 205:
+#line 1091 "cc.y"
+ {
+ (yyval.type) = (yyvsp[(2) - (7)].sym)->suetag;
+ if((yyval.type)->link != T)
+ diag(Z, "redeclare tag: %s", (yyvsp[(2) - (7)].sym)->name);
+ if(en.tenum == T) {
+ diag(Z, "enum type ambiguous: %s", (yyvsp[(2) - (7)].sym)->name);
+ en.tenum = types[TINT];
+ }
+ (yyval.type)->link = en.tenum;
+ (yyval.type) = en.tenum;
+ }
+ break;
+
+ case 206:
+#line 1103 "cc.y"
+ {
+ en.tenum = T;
+ en.cenum = T;
+ }
+ break;
+
+ case 207:
+#line 1108 "cc.y"
+ {
+ (yyval.type) = en.tenum;
+ }
+ break;
+
+ case 208:
+#line 1112 "cc.y"
+ {
+ (yyval.type) = tcopy((yyvsp[(1) - (1)].sym)->type);
+ }
+ break;
+
+ case 210:
+#line 1119 "cc.y"
+ {
+ (yyval.lval) = typebitor((yyvsp[(1) - (2)].lval), (yyvsp[(2) - (2)].lval));
+ }
+ break;
+
+ case 211:
+#line 1124 "cc.y"
+ {
+ (yyval.lval) = 0;
+ }
+ break;
+
+ case 212:
+#line 1128 "cc.y"
+ {
+ (yyval.lval) = typebitor((yyvsp[(1) - (2)].lval), (yyvsp[(2) - (2)].lval));
+ }
+ break;
+
+ case 217:
+#line 1140 "cc.y"
+ {
+ (yyval.lval) = typebitor((yyvsp[(1) - (2)].lval), (yyvsp[(2) - (2)].lval));
+ }
+ break;
+
+ case 220:
+#line 1150 "cc.y"
+ {
+ doenum((yyvsp[(1) - (1)].sym), Z);
+ }
+ break;
+
+ case 221:
+#line 1154 "cc.y"
+ {
+ doenum((yyvsp[(1) - (3)].sym), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 224:
+#line 1161 "cc.y"
+ { (yyval.lval) = BCHAR; }
+ break;
+
+ case 225:
+#line 1162 "cc.y"
+ { (yyval.lval) = BSHORT; }
+ break;
+
+ case 226:
+#line 1163 "cc.y"
+ { (yyval.lval) = BINT; }
+ break;
+
+ case 227:
+#line 1164 "cc.y"
+ { (yyval.lval) = BLONG; }
+ break;
+
+ case 228:
+#line 1165 "cc.y"
+ { (yyval.lval) = BSIGNED; }
+ break;
+
+ case 229:
+#line 1166 "cc.y"
+ { (yyval.lval) = BUNSIGNED; }
+ break;
+
+ case 230:
+#line 1167 "cc.y"
+ { (yyval.lval) = BFLOAT; }
+ break;
+
+ case 231:
+#line 1168 "cc.y"
+ { (yyval.lval) = BDOUBLE; }
+ break;
+
+ case 232:
+#line 1169 "cc.y"
+ { (yyval.lval) = BVOID; }
+ break;
+
+ case 233:
+#line 1172 "cc.y"
+ { (yyval.lval) = BAUTO; }
+ break;
+
+ case 234:
+#line 1173 "cc.y"
+ { (yyval.lval) = BSTATIC; }
+ break;
+
+ case 235:
+#line 1174 "cc.y"
+ { (yyval.lval) = BEXTERN; }
+ break;
+
+ case 236:
+#line 1175 "cc.y"
+ { (yyval.lval) = BTYPEDEF; }
+ break;
+
+ case 237:
+#line 1176 "cc.y"
+ { (yyval.lval) = BTYPESTR; }
+ break;
+
+ case 238:
+#line 1177 "cc.y"
+ { (yyval.lval) = BREGISTER; }
+ break;
+
+ case 239:
+#line 1178 "cc.y"
+ { (yyval.lval) = 0; }
+ break;
+
+ case 240:
+#line 1181 "cc.y"
+ { (yyval.lval) = BCONSTNT; }
+ break;
+
+ case 241:
+#line 1182 "cc.y"
+ { (yyval.lval) = BVOLATILE; }
+ break;
+
+ case 242:
+#line 1183 "cc.y"
+ { (yyval.lval) = 0; }
+ break;
+
+ case 243:
+#line 1187 "cc.y"
+ {
+ (yyval.node) = new(ONAME, Z, Z);
+ if((yyvsp[(1) - (1)].sym)->class == CLOCAL)
+ (yyvsp[(1) - (1)].sym) = mkstatic((yyvsp[(1) - (1)].sym));
+ (yyval.node)->sym = (yyvsp[(1) - (1)].sym);
+ (yyval.node)->type = (yyvsp[(1) - (1)].sym)->type;
+ (yyval.node)->etype = TVOID;
+ if((yyval.node)->type != T)
+ (yyval.node)->etype = (yyval.node)->type->etype;
+ (yyval.node)->xoffset = (yyvsp[(1) - (1)].sym)->offset;
+ (yyval.node)->class = (yyvsp[(1) - (1)].sym)->class;
+ (yyvsp[(1) - (1)].sym)->aused = 1;
+ }
+ break;
+
+ case 244:
+#line 1202 "cc.y"
+ {
+ (yyval.node) = new(ONAME, Z, Z);
+ (yyval.node)->sym = (yyvsp[(1) - (1)].sym);
+ (yyval.node)->type = (yyvsp[(1) - (1)].sym)->type;
+ (yyval.node)->etype = TVOID;
+ if((yyval.node)->type != T)
+ (yyval.node)->etype = (yyval.node)->type->etype;
+ (yyval.node)->xoffset = (yyvsp[(1) - (1)].sym)->offset;
+ (yyval.node)->class = (yyvsp[(1) - (1)].sym)->class;
+ }
+ break;
+
+
+/* Line 1267 of yacc.c. */
+#line 3596 "y.tab.c"
+ default: break;
+ }
+ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++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. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if ! YYERROR_VERBOSE
+ yyerror (YY_("syntax error"));
+#else
+ {
+ 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;
+ }
+ }
+#endif
+ }
+
+
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse look-ahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ YYABORT;
+ }
+ else
+ {
+ yydestruct ("Error: discarding",
+ yytoken, &yylval);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse look-ahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+ /* Pacify compilers like GCC when the user code never invokes
+ YYERROR and the label yyerrorlab therefore never appears in user
+ code. */
+ if (/*CONSTCOND*/ 0)
+ goto yyerrorlab;
+
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYERROR. */
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (yyn != YYPACT_NINF)
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+
+ yydestruct ("Error: popping",
+ yystos[yystate], yyvsp);
+ YYPOPSTACK (1);
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ *++yyvsp = yylval;
+
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#ifndef yyoverflow
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here. |
+`-------------------------------------------------*/
+yyexhaustedlab:
+ yyerror (YY_("memory exhausted"));
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+ if (yychar != YYEOF && yychar != YYEMPTY)
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval);
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYABORT or YYACCEPT. */
+ YYPOPSTACK (yylen);
+ YY_STACK_PRINT (yyss, yyssp);
+ while (yyssp != yyss)
+ {
+ yydestruct ("Cleanup: popping",
+ yystos[*yyssp], yyvsp);
+ YYPOPSTACK (1);
+ }
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+#endif
+ /* Make sure YYID is used. */
+ return YYID (yyresult);
+}
+
+
+#line 1215 "cc.y"
+
+
diff --git a/src/cmd/cc/y.tab.h b/src/cmd/cc/y.tab.h
new file mode 100644
index 000000000..c56a1d85a
--- /dev/null
+++ b/src/cmd/cc/y.tab.h
@@ -0,0 +1,228 @@
+/* A Bison parser, made by GNU Bison 2.3. */
+
+/* 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
+ 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.
+
+ 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. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ 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
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ LORE = 258,
+ LXORE = 259,
+ LANDE = 260,
+ LLSHE = 261,
+ LRSHE = 262,
+ LMDE = 263,
+ LDVE = 264,
+ LMLE = 265,
+ LME = 266,
+ LPE = 267,
+ LOROR = 268,
+ LANDAND = 269,
+ LNE = 270,
+ LEQ = 271,
+ LGE = 272,
+ LLE = 273,
+ LRSH = 274,
+ LLSH = 275,
+ LMG = 276,
+ LPP = 277,
+ LMM = 278,
+ LNAME = 279,
+ LTYPE = 280,
+ LFCONST = 281,
+ LDCONST = 282,
+ LCONST = 283,
+ LLCONST = 284,
+ LUCONST = 285,
+ LULCONST = 286,
+ LVLCONST = 287,
+ LUVLCONST = 288,
+ LSTRING = 289,
+ LLSTRING = 290,
+ LAUTO = 291,
+ LBREAK = 292,
+ LCASE = 293,
+ LCHAR = 294,
+ LCONTINUE = 295,
+ LDEFAULT = 296,
+ LDO = 297,
+ LDOUBLE = 298,
+ LELSE = 299,
+ LEXTERN = 300,
+ LFLOAT = 301,
+ LFOR = 302,
+ LGOTO = 303,
+ LIF = 304,
+ LINT = 305,
+ LLONG = 306,
+ LREGISTER = 307,
+ LRETURN = 308,
+ LSHORT = 309,
+ LSIZEOF = 310,
+ LUSED = 311,
+ LSTATIC = 312,
+ LSTRUCT = 313,
+ LSWITCH = 314,
+ LTYPEDEF = 315,
+ LTYPESTR = 316,
+ LUNION = 317,
+ LUNSIGNED = 318,
+ LWHILE = 319,
+ LVOID = 320,
+ LENUM = 321,
+ LSIGNED = 322,
+ LCONSTNT = 323,
+ LVOLATILE = 324,
+ LSET = 325,
+ LSIGNOF = 326,
+ LRESTRICT = 327,
+ LINLINE = 328
+ };
+#endif
+/* Tokens. */
+#define LORE 258
+#define LXORE 259
+#define LANDE 260
+#define LLSHE 261
+#define LRSHE 262
+#define LMDE 263
+#define LDVE 264
+#define LMLE 265
+#define LME 266
+#define LPE 267
+#define LOROR 268
+#define LANDAND 269
+#define LNE 270
+#define LEQ 271
+#define LGE 272
+#define LLE 273
+#define LRSH 274
+#define LLSH 275
+#define LMG 276
+#define LPP 277
+#define LMM 278
+#define LNAME 279
+#define LTYPE 280
+#define LFCONST 281
+#define LDCONST 282
+#define LCONST 283
+#define LLCONST 284
+#define LUCONST 285
+#define LULCONST 286
+#define LVLCONST 287
+#define LUVLCONST 288
+#define LSTRING 289
+#define LLSTRING 290
+#define LAUTO 291
+#define LBREAK 292
+#define LCASE 293
+#define LCHAR 294
+#define LCONTINUE 295
+#define LDEFAULT 296
+#define LDO 297
+#define LDOUBLE 298
+#define LELSE 299
+#define LEXTERN 300
+#define LFLOAT 301
+#define LFOR 302
+#define LGOTO 303
+#define LIF 304
+#define LINT 305
+#define LLONG 306
+#define LREGISTER 307
+#define LRETURN 308
+#define LSHORT 309
+#define LSIZEOF 310
+#define LUSED 311
+#define LSTATIC 312
+#define LSTRUCT 313
+#define LSWITCH 314
+#define LTYPEDEF 315
+#define LTYPESTR 316
+#define LUNION 317
+#define LUNSIGNED 318
+#define LWHILE 319
+#define LVOID 320
+#define LENUM 321
+#define LSIGNED 322
+#define LCONSTNT 323
+#define LVOLATILE 324
+#define LSET 325
+#define LSIGNOF 326
+#define LRESTRICT 327
+#define LINLINE 328
+
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 36 "cc.y"
+{
+ Node* node;
+ Sym* sym;
+ Type* type;
+ struct
+ {
+ Type* t;
+ uchar c;
+ } tycl;
+ struct
+ {
+ Type* t1;
+ Type* t2;
+ Type* t3;
+ uchar c;
+ } tyty;
+ struct
+ {
+ char* s;
+ int32 l;
+ } sval;
+ int32 lval;
+ double dval;
+ vlong vval;
+}
+/* Line 1529 of yacc.c. */
+#line 221 "y.tab.h"
+ YYSTYPE;
+# 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/cgo/ast.go b/src/cmd/cgo/ast.go
index 73b7313d6..381e606ef 100644
--- a/src/cmd/cgo/ast.go
+++ b/src/cmd/cgo/ast.go
@@ -9,7 +9,6 @@ package main
import (
"fmt"
"go/ast"
- "go/doc"
"go/parser"
"go/scanner"
"go/token"
@@ -17,7 +16,7 @@ import (
"strings"
)
-func parse(name string, flags uint) *ast.File {
+func parse(name string, flags parser.Mode) *ast.File {
ast1, err := parser.ParseFile(fset, name, nil, flags)
if err != nil {
if list, ok := err.(scanner.ErrorList); ok {
@@ -71,7 +70,7 @@ func (f *File) ReadGo(name string) {
}
sawC = true
if s.Name != nil {
- error(s.Path.Pos(), `cannot rename import "C"`)
+ error_(s.Path.Pos(), `cannot rename import "C"`)
}
cg := s.Doc
if cg == nil && len(d.Specs) == 1 {
@@ -79,12 +78,12 @@ func (f *File) ReadGo(name string) {
}
if cg != nil {
f.Preamble += fmt.Sprintf("#line %d %q\n", sourceLine(cg), name)
- f.Preamble += doc.CommentText(cg) + "\n"
+ f.Preamble += cg.Text() + "\n"
}
}
}
if !sawC {
- error(token.NoPos, `cannot find import "C"`)
+ error_(token.NoPos, `cannot find import "C"`)
}
// In ast2, strip the import "C" line.
@@ -128,6 +127,7 @@ func (f *File) ReadGo(name string) {
f.walk(ast1, "prog", (*File).saveExport)
f.walk(ast2, "prog", (*File).saveExport2)
+ f.Comments = ast1.Comments
f.AST = ast2
}
@@ -147,9 +147,12 @@ func (f *File) saveRef(x interface{}, context string) {
if context == "as2" {
context = "expr"
}
+ if context == "embed-type" {
+ error_(sel.Pos(), "cannot embed C type")
+ }
goname := sel.Sel.Name
if goname == "errno" {
- error(sel.Pos(), "cannot refer to errno directly; see documentation")
+ error_(sel.Pos(), "cannot refer to errno directly; see documentation")
return
}
name := f.Name[goname]
@@ -186,11 +189,11 @@ func (f *File) saveExport(x interface{}, context string) {
name := strings.TrimSpace(string(c.Text[9:]))
if name == "" {
- error(c.Pos(), "export missing name")
+ error_(c.Pos(), "export missing name")
}
if name != n.Name.Name {
- error(c.Pos(), "export comment has wrong name %q, want %q", name, n.Name.Name)
+ error_(c.Pos(), "export comment has wrong name %q, want %q", name, n.Name.Name)
}
f.ExpFunc = append(f.ExpFunc, &ExpFunc{
@@ -225,14 +228,18 @@ func (f *File) walk(x interface{}, context string, visit func(*File, interface{}
// everything else just recurs
default:
- error(token.NoPos, "unexpected type %T in walk", x, visit)
+ error_(token.NoPos, "unexpected type %T in walk", x, visit)
panic("unexpected type")
case nil:
// These are ordered and grouped to match ../../pkg/go/ast/ast.go
case *ast.Field:
- f.walk(&n.Type, "type", visit)
+ if len(n.Names) == 0 && context == "field" {
+ f.walk(&n.Type, "embed-type", visit)
+ } else {
+ f.walk(&n.Type, "type", visit)
+ }
case *ast.FieldList:
for _, field := range n.List {
f.walk(field, context, visit)
@@ -289,9 +296,9 @@ func (f *File) walk(x interface{}, context string, visit func(*File, interface{}
case *ast.StructType:
f.walk(n.Fields, "field", visit)
case *ast.FuncType:
- f.walk(n.Params, "field", visit)
+ f.walk(n.Params, "param", visit)
if n.Results != nil {
- f.walk(n.Results, "field", visit)
+ f.walk(n.Results, "param", visit)
}
case *ast.InterfaceType:
f.walk(n.Methods, "field", visit)
@@ -379,7 +386,7 @@ func (f *File) walk(x interface{}, context string, visit func(*File, interface{}
f.walk(n.Specs, "spec", visit)
case *ast.FuncDecl:
if n.Recv != nil {
- f.walk(n.Recv, "field", visit)
+ f.walk(n.Recv, "param", visit)
}
f.walk(n.Type, "type", visit)
if n.Body != nil {
diff --git a/src/cmd/cgo/doc.go b/src/cmd/cgo/doc.go
index a4219867c..1bb48f44e 100644
--- a/src/cmd/cgo/doc.go
+++ b/src/cmd/cgo/doc.go
@@ -6,7 +6,8 @@
Cgo enables the creation of Go packages that call C code.
-Usage: cgo [compiler options] file.go
+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.
@@ -16,8 +17,8 @@ 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.
If the import of "C" is immediately preceded by a comment, that
-comment is used as a header when compiling the C parts of
-the package. For example:
+comment, called the preamble, is used as a header when compiling
+the C parts of the package. For example:
// #include <stdio.h>
// #include <errno.h>
@@ -43,6 +44,11 @@ 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
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.
@@ -57,9 +63,11 @@ 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.
+Go structs cannot embed fields with C types.
+
Any C function that returns a value may be called in a multiple
assignment context to retrieve both the return value and the
-C errno variable as an os.Error. For example:
+C errno variable as an error. For example:
n, err := C.atoi("abc")
@@ -73,6 +81,9 @@ A few special functions convert between Go and C types
by making copies of the data. In pseudo-Go definitions:
// Go string to C string
+ // The C string is allocated in the C heap using malloc.
+ // It is the caller's responsibility to arrange for it to be
+ // freed, such as by calling C.free.
func C.CString(string) *C.char
// C string to Go string
@@ -84,16 +95,34 @@ by making copies of the data. In pseudo-Go definitions:
// C pointer, length to Go []byte
func C.GoBytes(unsafe.Pointer, C.int) []byte
+Go functions can be exported for use by C code in the following way:
+
+ //export MyFunction
+ func MyFunction(arg1, arg2 int, arg3 string) int64 {...}
+
+ //export MyFunction2
+ func MyFunction2(arg1, arg2 int, arg3 string) (int64, *C.char) {...}
+
+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
+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.
+
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.
-The standard package makefile rules in Make.pkg automate the
-process of using cgo. See $GOROOT/misc/cgo/stdio and
-$GOROOT/misc/cgo/gmp for examples.
+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.
Cgo does not yet work with gccgo.
See "C? Go? Cgo!" for an introduction to using cgo:
-http://blog.golang.org/2011/03/c-go-cgo.html
+http://golang.org/doc/articles/c_go_cgo.html
*/
package documentation
diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go
index 7ec4d8ccf..98a847e6f 100644
--- a/src/cmd/cgo/gcc.go
+++ b/src/cmd/cgo/gcc.go
@@ -14,16 +14,17 @@ import (
"debug/macho"
"debug/pe"
"encoding/binary"
+ "errors"
"flag"
"fmt"
"go/ast"
"go/parser"
"go/token"
"os"
- "runtime"
"strconv"
"strings"
"unicode"
+ "unicode/utf8"
)
var debugDefine = flag.Bool("debug-define", false, "print relevant #defines")
@@ -59,6 +60,9 @@ func cname(s string) string {
if strings.HasPrefix(s, "enum_") {
return "enum " + s[len("enum_"):]
}
+ if strings.HasPrefix(s, "sizeof_") {
+ return "sizeof(" + cname(s[len("sizeof_"):]) + ")"
+ }
return s
}
@@ -72,7 +76,7 @@ func (p *Package) ParseFlags(f *File, srcfile string) {
NextLine:
for _, line := range linesIn {
l := strings.TrimSpace(line)
- if len(l) < 5 || l[:4] != "#cgo" || !unicode.IsSpace(int(l[4])) {
+ if len(l) < 5 || l[:4] != "#cgo" || !unicode.IsSpace(rune(l[4])) {
linesOut = append(linesOut, line)
continue
}
@@ -91,9 +95,9 @@ NextLine:
case 2:
k = kf[1]
switch kf[0] {
- case runtime.GOOS:
- case runtime.GOARCH:
- case runtime.GOOS + "/" + runtime.GOARCH:
+ case goos:
+ case goarch:
+ case goos + "/" + goarch:
default:
continue NextLine
}
@@ -148,10 +152,10 @@ func (p *Package) addToFlag(flag string, args []string) {
// pkgConfig runs pkg-config and extracts --libs and --cflags information
// for packages.
-func pkgConfig(packages []string) (cflags, ldflags []string, err os.Error) {
+func pkgConfig(packages []string) (cflags, ldflags []string, err error) {
for _, name := range packages {
if len(name) == 0 || name[0] == '-' {
- return nil, nil, os.NewError(fmt.Sprintf("invalid name: %q", name))
+ return nil, nil, errors.New(fmt.Sprintf("invalid name: %q", name))
}
}
@@ -159,7 +163,7 @@ func pkgConfig(packages []string) (cflags, ldflags []string, err os.Error) {
stdout, stderr, ok := run(nil, args)
if !ok {
os.Stderr.Write(stderr)
- return nil, nil, os.NewError("pkg-config failed")
+ return nil, nil, errors.New("pkg-config failed")
}
cflags, err = splitQuoted(string(stdout))
if err != nil {
@@ -170,7 +174,7 @@ func pkgConfig(packages []string) (cflags, ldflags []string, err os.Error) {
stdout, stderr, ok = run(nil, args)
if !ok {
os.Stderr.Write(stderr)
- return nil, nil, os.NewError("pkg-config failed")
+ return nil, nil, errors.New("pkg-config failed")
}
ldflags, err = splitQuoted(string(stdout))
return
@@ -192,30 +196,30 @@ func pkgConfig(packages []string) (cflags, ldflags []string, err os.Error) {
//
// []string{"a", "b:c d", "ef", `g"`}
//
-func splitQuoted(s string) (r []string, err os.Error) {
+func splitQuoted(s string) (r []string, err error) {
var args []string
- arg := make([]int, len(s))
+ arg := make([]rune, len(s))
escaped := false
quoted := false
- quote := 0
+ quote := '\x00'
i := 0
- for _, rune := range s {
+ for _, r := range s {
switch {
case escaped:
escaped = false
- case rune == '\\':
+ case r == '\\':
escaped = true
continue
case quote != 0:
- if rune == quote {
+ if r == quote {
quote = 0
continue
}
- case rune == '"' || rune == '\'':
+ case r == '"' || r == '\'':
quoted = true
- quote = rune
+ quote = r
continue
- case unicode.IsSpace(rune):
+ case unicode.IsSpace(r):
if quoted || i > 0 {
quoted = false
args = append(args, string(arg[:i]))
@@ -223,21 +227,21 @@ func splitQuoted(s string) (r []string, err os.Error) {
}
continue
}
- arg[i] = rune
+ arg[i] = r
i++
}
if quoted || i > 0 {
args = append(args, string(arg[:i]))
}
if quote != 0 {
- err = os.NewError("unclosed quote")
+ err = errors.New("unclosed quote")
} else if escaped {
- err = os.NewError("unfinished escaping")
+ err = errors.New("unfinished escaping")
}
return args, err
}
-var safeBytes = []byte("+-.,/0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz")
+var safeBytes = []byte(`+-.,/0123456789:=ABCDEFGHIJKLMNOPQRSTUVWXYZ\_abcdefghijklmnopqrstuvwxyz`)
func safeName(s string) bool {
if s == "" {
@@ -340,14 +344,22 @@ func (p *Package) guessKinds(f *File) []*Name {
if _, err := strconv.Atoi(n.Define); err == nil {
ok = true
} else if n.Define[0] == '"' || n.Define[0] == '\'' {
- _, err := parser.ParseExpr(fset, "", n.Define)
- if err == nil {
+ if _, err := parser.ParseExpr(n.Define); err == nil {
ok = true
}
}
if ok {
n.Kind = "const"
- n.Const = n.Define
+ // Turn decimal into hex, just for consistency
+ // with enum-derived constants. Otherwise
+ // in the cgo -godefs output half the constants
+ // are in hex and half are in whatever the #define used.
+ i, err := strconv.ParseInt(n.Define, 0, 64)
+ if err == nil {
+ n.Const = fmt.Sprintf("%#x", i)
+ } else {
+ n.Const = n.Define
+ }
continue
}
@@ -421,7 +433,7 @@ func (p *Package) guessKinds(f *File) []*Name {
case strings.Contains(line, ": statement with no effect"):
what = "not-type" // const or func or var
case strings.Contains(line, "undeclared"):
- error(token.NoPos, "%s", strings.TrimSpace(line[colon+1:]))
+ error_(token.NoPos, "%s", strings.TrimSpace(line[colon+1:]))
case strings.Contains(line, "is not an integer constant"):
isConst[i] = false
continue
@@ -440,6 +452,11 @@ func (p *Package) guessKinds(f *File) []*Name {
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]
+ }
}
}
for _, n := range toSniff {
@@ -449,7 +466,7 @@ func (p *Package) guessKinds(f *File) []*Name {
if n.Kind != "" {
continue
}
- error(token.NoPos, "could not determine kind of name for C.%s", n.Go)
+ error_(token.NoPos, "could not determine kind of name for C.%s", n.Go)
}
if nerrors > 0 {
fatalf("unresolved names")
@@ -507,6 +524,10 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
for i, n := range names {
nameToIndex[n] = i
}
+ nameToRef := make(map[*Name]*Ref)
+ for _, ref := range f.Ref {
+ nameToRef[ref.Name] = ref
+ }
r := d.Reader()
for {
e, err := r.Next()
@@ -577,21 +598,28 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
var conv typeConv
conv.Init(p.PtrSize)
for i, n := range names {
+ if types[i] == nil {
+ continue
+ }
+ pos := token.NoPos
+ if ref, ok := nameToRef[n]; ok {
+ pos = ref.Pos()
+ }
f, fok := types[i].(*dwarf.FuncType)
if n.Kind != "type" && fok {
n.Kind = "func"
- n.FuncType = conv.FuncType(f)
+ n.FuncType = conv.FuncType(f, pos)
} else {
- n.Type = conv.Type(types[i])
+ n.Type = conv.Type(types[i], pos)
if enums[i] != 0 && n.Type.EnumValues != nil {
k := fmt.Sprintf("__cgo_enum__%d", i)
n.Kind = "const"
- n.Const = strconv.Itoa64(n.Type.EnumValues[k])
+ n.Const = fmt.Sprintf("%#x", n.Type.EnumValues[k])
// Remove injected enum to ensure the value will deep-compare
// equally in future loads of the same constant.
- n.Type.EnumValues[k] = 0, false
+ delete(n.Type.EnumValues, k)
} else if n.Kind == "const" && i < len(enumVal) {
- n.Const = strconv.Itoa64(enumVal[i])
+ n.Const = fmt.Sprintf("%#x", enumVal[i])
}
}
}
@@ -600,7 +628,8 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
// 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.
+// the xxx. In *godefs or *cdefs mode, rewriteRef replaces the names
+// with full definitions instead of mangled names.
func (p *Package) rewriteRef(f *File) {
// Assign mangled names.
for _, n := range f.Name {
@@ -618,7 +647,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", r.Name.Go)
}
var expr ast.Expr = ast.NewIdent(r.Name.Mangle) // default
switch r.Context {
@@ -629,12 +658,12 @@ 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", r.Name.Go)
break
}
if r.Context == "call2" {
if r.Name.FuncType.Result == nil {
- error(r.Pos(), "assignment count mismatch: 2 = 0")
+ error_(r.Pos(), "assignment count mismatch: 2 = 0")
}
// Invent new Name for the two-result function.
n := f.Name["2"+r.Name.Go]
@@ -651,7 +680,7 @@ func (p *Package) rewriteRef(f *File) {
}
case "expr":
if r.Name.Kind == "func" {
- error(r.Pos(), "must call C.%s", r.Name.Go)
+ error_(r.Pos(), "must call C.%s", r.Name.Go)
}
if r.Name.Kind == "type" {
// Okay - might be new(T)
@@ -663,13 +692,28 @@ func (p *Package) rewriteRef(f *File) {
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", 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)
} 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", r.Name.Go)
+ }
+ }
+ if *godefs || *cdefs {
+ // Substitute definition for mangled type name.
+ if id, ok := expr.(*ast.Ident); ok {
+ if t := typedef[id.Name]; t != nil {
+ expr = t.Go
+ }
+ if id.Name == r.Name.Mangle && r.Name.Const != "" {
+ expr = ast.NewIdent(r.Name.Const)
+ }
}
}
*r.Expr = expr
@@ -688,7 +732,7 @@ func (p *Package) gccName() (ret string) {
// gccMachine returns the gcc -m flag to use, either "-m32" or "-m64".
func (p *Package) gccMachine() []string {
- switch runtime.GOARCH {
+ switch goarch {
case "amd64":
return []string{"-m64"}
case "386":
@@ -697,7 +741,9 @@ func (p *Package) gccMachine() []string {
return nil
}
-var gccTmp = objDir + "_cgo_.o"
+func gccTmp() string {
+ return *objDir + "_cgo_.o"
+}
// gccCmd returns the gcc command line to use for compiling
// the input.
@@ -706,7 +752,7 @@ func (p *Package) gccCmd() []string {
p.gccName(),
"-Wall", // many warnings
"-Werror", // warnings are errors
- "-o" + gccTmp, // write object to tmp
+ "-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
@@ -723,10 +769,10 @@ func (p *Package) gccCmd() []string {
func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte) {
runGcc(stdin, p.gccCmd())
- if f, err := macho.Open(gccTmp); err == nil {
+ if f, err := macho.Open(gccTmp()); err == nil {
d, err := f.DWARF()
if err != nil {
- fatalf("cannot load DWARF output from %s: %v", gccTmp, err)
+ fatalf("cannot load DWARF output from %s: %v", gccTmp(), err)
}
var data []byte
if f.Symtab != nil {
@@ -752,23 +798,23 @@ func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte)
// Can skip debug data block in ELF and PE for now.
// The DWARF information is complete.
- if f, err := elf.Open(gccTmp); err == nil {
+ if f, err := elf.Open(gccTmp()); err == nil {
d, err := f.DWARF()
if err != nil {
- fatalf("cannot load DWARF output from %s: %v", gccTmp, err)
+ fatalf("cannot load DWARF output from %s: %v", gccTmp(), err)
}
return d, f.ByteOrder, nil
}
- if f, err := pe.Open(gccTmp); err == nil {
+ if f, err := pe.Open(gccTmp()); err == nil {
d, err := f.DWARF()
if err != nil {
- fatalf("cannot load DWARF output from %s: %v", gccTmp, err)
+ fatalf("cannot load DWARF output from %s: %v", gccTmp(), err)
}
return d, binary.LittleEndian, nil
}
- fatalf("cannot parse gcc output %s as ELF, Mach-O, PE object", gccTmp)
+ fatalf("cannot parse gcc output %s as ELF, Mach-O, PE object", gccTmp())
panic("not reached")
}
@@ -848,7 +894,8 @@ type typeConv struct {
}
var tagGen int
-var typedef = make(map[string]ast.Expr)
+var typedef = make(map[string]*Type)
+var goIdent = make(map[string]*ast.Ident)
func (c *typeConv) Init(ptrSize int64) {
c.ptrSize = ptrSize
@@ -933,10 +980,10 @@ func (tr *TypeRepr) Set(repr string, fargs ...interface{}) {
// Type returns a *Type with the same memory layout as
// dtype when used as the type of a variable or a struct field.
-func (c *typeConv) Type(dtype dwarf.Type) *Type {
+func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
if t, ok := c.m[dtype]; ok {
if t.Go == nil {
- fatalf("type conversion loop at %s", dtype)
+ fatalf("%s: type conversion loop at %s", lineno(pos), dtype)
}
return t
}
@@ -959,11 +1006,11 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type {
switch dt := dtype.(type) {
default:
- fatalf("unexpected type: %s", dtype)
+ fatalf("%s: unexpected type: %s", lineno(pos), dtype)
case *dwarf.AddrType:
if t.Size != c.ptrSize {
- fatalf("unexpected: %d-byte address type - %s", t.Size, dtype)
+ fatalf("%s: unexpected: %d-byte address type - %s", lineno(pos), t.Size, dtype)
}
t.Go = c.uintptr
t.Align = t.Size
@@ -978,7 +1025,7 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type {
Len: c.intExpr(dt.Count),
}
t.Go = gt // publish before recursive call
- sub := c.Type(dt.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)
@@ -989,7 +1036,7 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type {
case *dwarf.CharType:
if t.Size != 1 {
- fatalf("unexpected: %d-byte char type - %s", t.Size, dtype)
+ fatalf("%s: unexpected: %d-byte char type - %s", lineno(pos), t.Size, dtype)
}
t.Go = c.int8
t.Align = 1
@@ -1009,7 +1056,7 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type {
}
switch t.Size + int64(signed) {
default:
- fatalf("unexpected: %d-byte enum type - %s", t.Size, dtype)
+ fatalf("%s: unexpected: %d-byte enum type - %s", lineno(pos), t.Size, dtype)
case 1:
t.Go = c.uint8
case 2:
@@ -1031,7 +1078,7 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type {
case *dwarf.FloatType:
switch t.Size {
default:
- fatalf("unexpected: %d-byte float type - %s", t.Size, dtype)
+ fatalf("%s: unexpected: %d-byte float type - %s", lineno(pos), t.Size, dtype)
case 4:
t.Go = c.float32
case 8:
@@ -1044,7 +1091,7 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type {
case *dwarf.ComplexType:
switch t.Size {
default:
- fatalf("unexpected: %d-byte complex type - %s", t.Size, dtype)
+ fatalf("%s: unexpected: %d-byte complex type - %s", lineno(pos), t.Size, dtype)
case 8:
t.Go = c.complex64
case 16:
@@ -1062,11 +1109,11 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type {
case *dwarf.IntType:
if dt.BitSize > 0 {
- fatalf("unexpected: %d-bit int type - %s", dt.BitSize, dtype)
+ fatalf("%s: unexpected: %d-bit int type - %s", lineno(pos), dt.BitSize, dtype)
}
switch t.Size {
default:
- fatalf("unexpected: %d-byte int type - %s", t.Size, dtype)
+ fatalf("%s: unexpected: %d-byte int type - %s", lineno(pos), t.Size, dtype)
case 1:
t.Go = c.int8
case 2:
@@ -1092,13 +1139,13 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type {
gt := &ast.StarExpr{}
t.Go = gt // publish before recursive call
- sub := c.Type(dt.Type)
+ sub := c.Type(dt.Type, pos)
gt.X = sub.Go
t.C.Set("%s*", sub.C)
case *dwarf.QualType:
// Ignore qualifier.
- t = c.Type(dt.Type)
+ t = c.Type(dt.Type, pos)
c.m[dtype] = t
return t
@@ -1114,19 +1161,26 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type {
}
name := c.Ident("_Ctype_" + dt.Kind + "_" + tag)
t.Go = name // publish before recursive calls
+ goIdent[name.Name] = name
switch dt.Kind {
case "union", "class":
- typedef[name.Name] = c.Opaque(t.Size)
+ t.Go = c.Opaque(t.Size)
if t.C.Empty() {
t.C.Set("typeof(unsigned char[%d])", t.Size)
}
+ typedef[name.Name] = t
case "struct":
- g, csyntax, align := c.Struct(dt)
+ g, csyntax, align := c.Struct(dt, pos)
if t.C.Empty() {
t.C.Set(csyntax)
}
t.Align = align
- typedef[name.Name] = g
+ tt := *t
+ if tag != "" {
+ tt.C = &TypeRepr{"struct %s", []interface{}{tag}}
+ }
+ tt.Go = g
+ typedef[name.Name] = &tt
}
case *dwarf.TypedefType:
@@ -1148,29 +1202,35 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type {
t.Align = c.ptrSize
break
}
- name := c.Ident("_Ctypedef_" + dt.Name)
+ name := c.Ident("_Ctype_" + dt.Name)
+ goIdent[name.Name] = name
t.Go = name // publish before recursive call
- sub := c.Type(dt.Type)
+ sub := c.Type(dt.Type, pos)
t.Size = sub.Size
t.Align = sub.Align
if _, ok := typedef[name.Name]; !ok {
- typedef[name.Name] = sub.Go
+ tt := *t
+ tt.Go = sub.Go
+ typedef[name.Name] = &tt
+ }
+ if *godefs || *cdefs {
+ t.Go = sub.Go
}
case *dwarf.UcharType:
if t.Size != 1 {
- fatalf("unexpected: %d-byte uchar type - %s", t.Size, dtype)
+ fatalf("%s: unexpected: %d-byte uchar type - %s", lineno(pos), t.Size, dtype)
}
t.Go = c.uint8
t.Align = 1
case *dwarf.UintType:
if dt.BitSize > 0 {
- fatalf("unexpected: %d-bit uint type - %s", dt.BitSize, dtype)
+ fatalf("%s: unexpected: %d-bit uint type - %s", lineno(pos), dt.BitSize, dtype)
}
switch t.Size {
default:
- fatalf("unexpected: %d-byte uint type - %s", t.Size, dtype)
+ fatalf("%s: unexpected: %d-byte uint type - %s", lineno(pos), t.Size, dtype)
case 1:
t.Go = c.uint8
case 2:
@@ -1198,13 +1258,16 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type {
}
s = strings.Join(strings.Split(s, " "), "") // strip spaces
name := c.Ident("_Ctype_" + s)
- typedef[name.Name] = t.Go
- t.Go = name
+ tt := *t
+ typedef[name.Name] = &tt
+ if !*godefs && !*cdefs {
+ t.Go = name
+ }
}
}
if t.C.Empty() {
- fatalf("internal error: did not create C name for %s", dtype)
+ fatalf("%s: internal error: did not create C name for %s", lineno(pos), dtype)
}
return t
@@ -1212,8 +1275,8 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type {
// FuncArg returns a Go type with the same memory layout as
// dtype when used as the type of a C function argument.
-func (c *typeConv) FuncArg(dtype dwarf.Type) *Type {
- t := c.Type(dtype)
+func (c *typeConv) FuncArg(dtype dwarf.Type, pos token.Pos) *Type {
+ t := c.Type(dtype, pos)
switch dt := dtype.(type) {
case *dwarf.ArrayType:
// Arrays are passed implicitly as pointers in C.
@@ -1234,9 +1297,18 @@ func (c *typeConv) FuncArg(dtype dwarf.Type) *Type {
if ptr, ok := base(dt.Type).(*dwarf.PtrType); ok {
// Unless the typedef happens to point to void* since
// Go has special rules around using unsafe.Pointer.
- if _, void := base(ptr.Type).(*dwarf.VoidType); !void {
- return c.Type(ptr)
+ if _, void := base(ptr.Type).(*dwarf.VoidType); void {
+ break
+ }
+
+ t = c.Type(ptr, pos)
+ if t == nil {
+ return nil
}
+
+ // Remember the C spelling, in case the struct
+ // has __attribute__((unavailable)) on it. See issue 2888.
+ t.Typedef = dt.Name
}
}
return t
@@ -1244,7 +1316,7 @@ func (c *typeConv) FuncArg(dtype dwarf.Type) *Type {
// FuncType returns the Go type analogous to dtype.
// There is no guarantee about matching memory layout.
-func (c *typeConv) FuncType(dtype *dwarf.FuncType) *FuncType {
+func (c *typeConv) FuncType(dtype *dwarf.FuncType, pos token.Pos) *FuncType {
p := make([]*Type, len(dtype.ParamType))
gp := make([]*ast.Field, len(dtype.ParamType))
for i, f := range dtype.ParamType {
@@ -1257,14 +1329,14 @@ func (c *typeConv) FuncType(dtype *dwarf.FuncType) *FuncType {
p, gp = nil, nil
break
}
- p[i] = c.FuncArg(f)
+ p[i] = c.FuncArg(f, pos)
gp[i] = &ast.Field{Type: p[i].Go}
}
var r *Type
var gr []*ast.Field
if _, ok := dtype.ReturnType.(*dwarf.VoidType); !ok && dtype.ReturnType != nil {
- r = c.Type(dtype.ReturnType)
- gr = []*ast.Field{&ast.Field{Type: r.Go}}
+ r = c.Type(dtype.ReturnType, pos)
+ gr = []*ast.Field{{Type: r.Go}}
}
return &FuncType{
Params: p,
@@ -1293,7 +1365,7 @@ func (c *typeConv) Opaque(n int64) ast.Expr {
func (c *typeConv) intExpr(n int64) ast.Expr {
return &ast.BasicLit{
Kind: token.INT,
- Value: strconv.Itoa64(n),
+ Value: strconv.FormatInt(n, 10),
}
}
@@ -1306,7 +1378,7 @@ func (c *typeConv) pad(fld []*ast.Field, size int64) []*ast.Field {
}
// Struct conversion: return Go and (6g) C syntax for type.
-func (c *typeConv) Struct(dt *dwarf.StructType) (expr *ast.StructType, csyntax string, align int64) {
+func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.StructType, csyntax string, align int64) {
var buf bytes.Buffer
buf.WriteString("struct {")
fld := make([]*ast.Field, 0, 2*len(dt.Field)+1) // enough for padding around every field
@@ -1324,38 +1396,61 @@ func (c *typeConv) Struct(dt *dwarf.StructType) (expr *ast.StructType, csyntax s
ident[f.Name] = f.Name
used[f.Name] = true
}
- for cid, goid := range ident {
- if token.Lookup([]byte(goid)).IsKeyword() {
- // Avoid keyword
- goid = "_" + goid
- // Also avoid existing fields
- for _, exist := used[goid]; exist; _, exist = used[goid] {
+ if !*godefs && !*cdefs {
+ for cid, goid := range ident {
+ if token.Lookup(goid).IsKeyword() {
+ // Avoid keyword
goid = "_" + goid
- }
- used[goid] = true
- ident[cid] = goid
+ // Also avoid existing fields
+ for _, exist := used[goid]; exist; _, exist = used[goid] {
+ goid = "_" + goid
+ }
+
+ used[goid] = true
+ ident[cid] = goid
+ }
}
}
+ anon := 0
for _, f := range dt.Field {
- if f.BitSize > 0 && f.BitSize != f.ByteSize*8 {
- continue
- }
if f.ByteOffset > off {
fld = c.pad(fld, f.ByteOffset-off)
off = f.ByteOffset
}
- t := c.Type(f.Type)
+ t := c.Type(f.Type, pos)
+ tgo := t.Go
+ size := t.Size
+
+ if f.BitSize > 0 {
+ if f.BitSize%8 != 0 {
+ continue
+ }
+ size = f.BitSize / 8
+ name := tgo.(*ast.Ident).String()
+ if strings.HasPrefix(name, "int") {
+ name = "int"
+ } else {
+ name = "uint"
+ }
+ tgo = ast.NewIdent(name + fmt.Sprint(f.BitSize))
+ }
+
n := len(fld)
fld = fld[0 : n+1]
-
- fld[n] = &ast.Field{Names: []*ast.Ident{c.Ident(ident[f.Name])}, Type: t.Go}
- off += t.Size
+ name := f.Name
+ if name == "" {
+ name = fmt.Sprintf("anon%d", anon)
+ anon++
+ ident[name] = name
+ }
+ fld[n] = &ast.Field{Names: []*ast.Ident{c.Ident(ident[name])}, Type: tgo}
+ off += size
buf.WriteString(t.C.String())
buf.WriteString(" ")
- buf.WriteString(f.Name)
+ buf.WriteString(name)
buf.WriteString("; ")
if t.Align > align {
align = t.Align
@@ -1366,10 +1461,100 @@ func (c *typeConv) Struct(dt *dwarf.StructType) (expr *ast.StructType, csyntax s
off = dt.ByteSize
}
if off != dt.ByteSize {
- fatalf("struct size calculation error")
+ fatalf("%s: struct size calculation error off=%d bytesize=%d", lineno(pos), off, dt.ByteSize)
}
buf.WriteString("}")
csyntax = buf.String()
+
+ if *godefs || *cdefs {
+ godefsFields(fld)
+ }
expr = &ast.StructType{Fields: &ast.FieldList{List: fld}}
return
}
+
+func upper(s string) string {
+ if s == "" {
+ return ""
+ }
+ r, size := utf8.DecodeRuneInString(s)
+ if r == '_' {
+ return "X" + s
+ }
+ return string(unicode.ToUpper(r)) + s[size:]
+}
+
+// godefsFields rewrites field names for use in Go or C definitions.
+// It strips leading common prefixes (like tv_ in tv_sec, tv_usec)
+// converts names to upper case, and rewrites _ into Pad_godefs_n,
+// so that all fields are exported.
+func godefsFields(fld []*ast.Field) {
+ prefix := fieldPrefix(fld)
+ npad := 0
+ for _, f := range fld {
+ for _, n := range f.Names {
+ if strings.HasPrefix(n.Name, prefix) && n.Name != prefix {
+ n.Name = n.Name[len(prefix):]
+ }
+ if n.Name == "_" {
+ // Use exported name instead.
+ n.Name = "Pad_cgo_" + strconv.Itoa(npad)
+ npad++
+ }
+ if !*cdefs {
+ n.Name = upper(n.Name)
+ }
+ }
+ p := &f.Type
+ t := *p
+ if star, ok := t.(*ast.StarExpr); ok {
+ star = &ast.StarExpr{X: star.X}
+ *p = star
+ p = &star.X
+ t = *p
+ }
+ if id, ok := t.(*ast.Ident); ok {
+ if id.Name == "unsafe.Pointer" {
+ *p = ast.NewIdent("*byte")
+ }
+ }
+ }
+}
+
+// fieldPrefix returns the prefix that should be removed from all the
+// field names when generating the C or Go code. For generated
+// C, we leave the names as is (tv_sec, tv_usec), since that's what
+// people are used to seeing in C. For generated Go code, such as
+// package syscall's data structures, we drop a common prefix
+// (so sec, usec, which will get turned into Sec, Usec for exporting).
+func fieldPrefix(fld []*ast.Field) string {
+ if *cdefs {
+ return ""
+ }
+ prefix := ""
+ for _, f := range fld {
+ for _, n := range f.Names {
+ // Ignore field names that don't have the prefix we're
+ // looking for. It is common in C headers to have fields
+ // named, say, _pad in an otherwise prefixed header.
+ // If the struct has 3 fields tv_sec, tv_usec, _pad1, then we
+ // still want to remove the tv_ prefix.
+ // The check for "orig_" here handles orig_eax in the
+ // x86 ptrace register sets, which otherwise have all fields
+ // with reg_ prefixes.
+ if strings.HasPrefix(n.Name, "orig_") || strings.HasPrefix(n.Name, "_") {
+ continue
+ }
+ i := strings.Index(n.Name, "_")
+ if i < 0 {
+ continue
+ }
+ if prefix == "" {
+ prefix = n.Name[:i+1]
+ } else if prefix != n.Name[:i+1] {
+ return ""
+ }
+ }
+ }
+ return prefix
+}
diff --git a/src/cmd/cgo/godefs.go b/src/cmd/cgo/godefs.go
new file mode 100644
index 000000000..fec70a334
--- /dev/null
+++ b/src/cmd/cgo/godefs.go
@@ -0,0 +1,289 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "bytes"
+ "fmt"
+ "go/ast"
+ "go/printer"
+ "go/token"
+ "os"
+ "strings"
+)
+
+// godefs returns the output for -godefs mode.
+func (p *Package) godefs(f *File, srcfile string) string {
+ var buf bytes.Buffer
+
+ fmt.Fprintf(&buf, "// Created by cgo -godefs - DO NOT EDIT\n")
+ fmt.Fprintf(&buf, "// %s\n", strings.Join(os.Args, " "))
+ fmt.Fprintf(&buf, "\n")
+
+ override := make(map[string]string)
+
+ // Allow source file to specify override mappings.
+ // For example, the socket data structures refer
+ // to in_addr and in_addr6 structs but we want to be
+ // able to treat them as byte arrays, so the godefs
+ // inputs in package syscall say
+ //
+ // // +godefs map struct_in_addr [4]byte
+ // // +godefs map struct_in_addr6 [16]byte
+ //
+ for _, g := range f.Comments {
+ for _, c := range g.List {
+ i := strings.Index(c.Text, "+godefs map")
+ if i < 0 {
+ continue
+ }
+ s := strings.TrimSpace(c.Text[i+len("+godefs map"):])
+ i = strings.Index(s, " ")
+ if i < 0 {
+ fmt.Fprintf(os.Stderr, "invalid +godefs map comment: %s\n", c.Text)
+ continue
+ }
+ override["_Ctype_"+strings.TrimSpace(s[:i])] = strings.TrimSpace(s[i:])
+ }
+ }
+ for _, n := range f.Name {
+ if s := override[n.Go]; s != "" {
+ override[n.Mangle] = s
+ }
+ }
+
+ // Otherwise, if the source file says type T C.whatever,
+ // use "T" as the mangling of C.whatever,
+ // except in the definition (handled at end of function).
+ refName := make(map[*ast.Expr]*Name)
+ for _, r := range f.Ref {
+ refName[r.Expr] = r.Name
+ }
+ for _, d := range f.AST.Decls {
+ d, ok := d.(*ast.GenDecl)
+ if !ok || d.Tok != token.TYPE {
+ continue
+ }
+ for _, s := range d.Specs {
+ s := s.(*ast.TypeSpec)
+ n := refName[&s.Type]
+ if n != nil && n.Mangle != "" {
+ override[n.Mangle] = s.Name.Name
+ }
+ }
+ }
+
+ // Extend overrides using typedefs:
+ // If we know that C.xxx should format as T
+ // and xxx is a typedef for yyy, make C.yyy format as T.
+ for typ, def := range typedef {
+ if new := override[typ]; new != "" {
+ if id, ok := def.Go.(*ast.Ident); ok {
+ override[id.Name] = new
+ }
+ }
+ }
+
+ // Apply overrides.
+ for old, new := range override {
+ if id := goIdent[old]; id != nil {
+ id.Name = new
+ }
+ }
+
+ // Any names still using the _C syntax are not going to compile,
+ // although in general we don't know whether they all made it
+ // into the file, so we can't warn here.
+ //
+ // The most common case is union types, which begin with
+ // _Ctype_union and for which typedef[name] is a Go byte
+ // array of the appropriate size (such as [4]byte).
+ // Substitute those union types with byte arrays.
+ for name, id := range goIdent {
+ if id.Name == name && strings.Contains(name, "_Ctype_union") {
+ if def := typedef[name]; def != nil {
+ id.Name = gofmt(def)
+ }
+ }
+ }
+
+ conf.Fprint(&buf, fset, f.AST)
+
+ return buf.String()
+}
+
+// cdefs returns the output for -cdefs mode.
+// The easiest way to do this is to translate the godefs Go to C.
+func (p *Package) cdefs(f *File, srcfile string) string {
+ godefsOutput := p.godefs(f, srcfile)
+
+ lines := strings.Split(godefsOutput, "\n")
+ lines[0] = "// Created by cgo -cdefs - DO NOT EDIT"
+
+ for i, line := range lines {
+ lines[i] = strings.TrimSpace(line)
+ }
+
+ var out bytes.Buffer
+ printf := func(format string, args ...interface{}) { fmt.Fprintf(&out, format, args...) }
+
+ didTypedef := false
+ for i := 0; i < len(lines); i++ {
+ line := lines[i]
+
+ // Delete
+ // package x
+ if strings.HasPrefix(line, "package ") {
+ continue
+ }
+
+ // Convert
+ // const (
+ // A = 1
+ // B = 2
+ // )
+ //
+ // to
+ //
+ // enum {
+ // A = 1,
+ // B = 2,
+ // };
+ if line == "const (" {
+ printf("enum {\n")
+ for i++; i < len(lines) && lines[i] != ")"; i++ {
+ line = lines[i]
+ if line != "" {
+ printf("\t%s,", line)
+ }
+ printf("\n")
+ }
+ printf("};\n")
+ continue
+ }
+
+ // Convert
+ // const A = 1
+ // to
+ // enum { A = 1 };
+ if strings.HasPrefix(line, "const ") {
+ printf("enum { %s };\n", line[len("const "):])
+ continue
+ }
+
+ // On first type definition, typedef all the structs
+ // in case there are dependencies between them.
+ if !didTypedef && strings.HasPrefix(line, "type ") {
+ didTypedef = true
+ for _, line := range lines {
+ line = strings.TrimSpace(line)
+ if strings.HasPrefix(line, "type ") && strings.HasSuffix(line, " struct {") {
+ s := line[len("type ") : len(line)-len(" struct {")]
+ printf("typedef struct %s %s;\n", s, s)
+ }
+ }
+ printf("\n")
+ printf("#pragma pack on\n")
+ printf("\n")
+ }
+
+ // Convert
+ // type T struct {
+ // X int64
+ // Y *int32
+ // Z [4]byte
+ // }
+ //
+ // to
+ //
+ // struct T {
+ // int64 X;
+ // int32 *Y;
+ // byte Z[4];
+ // }
+ if strings.HasPrefix(line, "type ") && strings.HasSuffix(line, " struct {") {
+ s := line[len("type ") : len(line)-len(" struct {")]
+ printf("struct %s {\n", s)
+ for i++; i < len(lines) && lines[i] != "}"; i++ {
+ line := lines[i]
+ if line != "" {
+ f := strings.Fields(line)
+ if len(f) != 2 {
+ fmt.Fprintf(os.Stderr, "cgo: cannot parse struct field: %s\n", line)
+ nerrors++
+ continue
+ }
+ printf("\t%s;", cdecl(f[0], f[1]))
+ }
+ printf("\n")
+ }
+ printf("};\n")
+ continue
+ }
+
+ // Convert
+ // type T int
+ // to
+ // typedef int T;
+ if strings.HasPrefix(line, "type ") {
+ f := strings.Fields(line[len("type "):])
+ if len(f) != 2 {
+ fmt.Fprintf(os.Stderr, "cgo: cannot parse type definition: %s\n", line)
+ nerrors++
+ continue
+ }
+ printf("typedef\t%s;\n", cdecl(f[0], f[1]))
+ continue
+ }
+
+ printf("%s\n", line)
+ }
+
+ if didTypedef {
+ printf("\n")
+ printf("#pragma pack off\n")
+ }
+
+ return out.String()
+}
+
+// cdecl returns the C declaration for the given Go name and type.
+// It only handles the specific cases necessary for converting godefs output.
+func cdecl(name, typ string) string {
+ // X *[0]byte -> X *void
+ 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, "[") {
+ i := strings.Index(typ, "]") + 1
+ name = name + typ[:i]
+ typ = typ[i:]
+ }
+ // X T -> T X
+ // Handle the special case: 'unsafe.Pointer' is 'void *'
+ if typ == "unsafe.Pointer" {
+ typ = "void"
+ name = "*" + name
+ }
+ return typ + "\t" + name
+}
+
+var gofmtBuf bytes.Buffer
+
+// gofmt returns the gofmt-formatted string for an AST node.
+func gofmt(n interface{}) string {
+ gofmtBuf.Reset()
+ err := printer.Fprint(&gofmtBuf, fset, n)
+ if err != nil {
+ return "<" + err.Error() + ">"
+ }
+ return gofmtBuf.String()
+}
diff --git a/src/cmd/cgo/main.go b/src/cmd/cgo/main.go
index be9c2bc4f..7449f04c4 100644
--- a/src/cmd/cgo/main.go
+++ b/src/cmd/cgo/main.go
@@ -15,11 +15,13 @@ import (
"flag"
"fmt"
"go/ast"
+ "go/printer"
"go/token"
"io"
"os"
"path/filepath"
"reflect"
+ "runtime"
"strings"
)
@@ -37,11 +39,13 @@ type Package struct {
Decl []ast.Decl
GoFiles []string // list of Go files
GccFiles []string // list of gcc output files
+ Preamble string // collected preamble for _cgo_export.h
}
// A File collects information about a single Go input file.
type File struct {
AST *ast.File // parsed AST
+ Comments []*ast.CommentGroup // comments from file
Package string // Package name
Preamble string // C preamble (doc comment on import "C")
Ref []*Ref // all references to C.xxx in AST
@@ -95,6 +99,7 @@ type Type struct {
C *TypeRepr
Go ast.Expr
EnumValues map[string]int64
+ Typedef string
}
// A FuncType collects information about a function type in both the C and Go worlds.
@@ -121,6 +126,19 @@ var cPrefix string
var fset = token.NewFileSet()
var dynobj = flag.String("dynimport", "", "if non-empty, print dynamic import data for that file")
+var dynout = flag.String("dynout", "", "write -dynobj output to this file")
+
+// These flags are for bootstrapping a new Go implementation,
+// to generate Go and C headers that match the data layout and
+// constant values used in the host's C libraries and system calls.
+var godefs = flag.Bool("godefs", false, "for bootstrap: write Go definitions for C file to standard output")
+var cdefs = flag.Bool("cdefs", false, "for bootstrap: write C definitions for C file to standard output")
+var objDir = flag.String("objdir", "", "object directory")
+
+var gccgo = flag.Bool("gccgo", false, "generate files for use with gccgo")
+var gccgoprefix = flag.String("gccgoprefix", "go", "prefix of symbols generated by gccgo")
+var importRuntimeCgo = flag.Bool("import_runtime_cgo", true, "import runtime/cgo in generated code")
+var goarch, goos string
func main() {
flag.Usage = usage
@@ -130,7 +148,7 @@ func main() {
// cgo -dynimport is essentially a separate helper command
// built into the cgo binary. It scans a gcc-produced executable
// and dumps information about the imported symbols and the
- // imported libraries. The Make.pkg rules for cgo prepare an
+ // imported libraries. The 'go build' rules for cgo prepare an
// appropriate executable and then use its import information
// instead of needing to make the linkers duplicate all the
// specialized knowledge gcc has about where to look for imported
@@ -139,6 +157,18 @@ func main() {
return
}
+ if *godefs && *cdefs {
+ fmt.Fprintf(os.Stderr, "cgo: cannot use -cdefs and -godefs together\n")
+ os.Exit(2)
+ }
+
+ if *godefs || *cdefs {
+ // Generating definitions pulled from header files,
+ // to be checked into Go repositories.
+ // Line numbers are just noise.
+ conf.Mode &^= printer.SourcePos
+ }
+
args := flag.Args()
if len(args) < 1 {
usage()
@@ -156,32 +186,9 @@ func main() {
usage()
}
- // Copy it to a new slice so it can grow.
- gccOptions := make([]string, i)
- copy(gccOptions, args[0:i])
-
goFiles := args[i:]
- arch := os.Getenv("GOARCH")
- if arch == "" {
- fatalf("$GOARCH is not set")
- }
- ptrSize := ptrSizeMap[arch]
- if ptrSize == 0 {
- fatalf("unknown $GOARCH %q", arch)
- }
-
- // Clear locale variables so gcc emits English errors [sic].
- os.Setenv("LANG", "en_US.UTF-8")
- os.Setenv("LC_ALL", "C")
- os.Setenv("LC_CTYPE", "C")
-
- p := &Package{
- PtrSize: ptrSize,
- GccOptions: gccOptions,
- CgoFlags: make(map[string]string),
- Written: make(map[string]bool),
- }
+ p := newPackage(args[:i])
// Need a unique prefix for the global C symbols that
// we use to coordinate between gcc and ourselves.
@@ -197,7 +204,7 @@ func main() {
io.Copy(h, f)
f.Close()
}
- cPrefix = fmt.Sprintf("_%x", h.Sum()[0:6])
+ cPrefix = fmt.Sprintf("_%x", h.Sum(nil)[0:6])
fs := make([]*File, len(goFiles))
for i, input := range goFiles {
@@ -208,9 +215,13 @@ func main() {
fs[i] = f
}
- // make sure that _obj directory exists, so that we can write
- // all the output files there.
- os.Mkdir("_obj", 0777)
+ if *objDir == "" {
+ // make sure that _obj directory exists, so that we can write
+ // all the output files there.
+ os.Mkdir("_obj", 0777)
+ *objDir = "_obj"
+ }
+ *objDir += string(filepath.Separator)
for i, input := range goFiles {
f := fs[i]
@@ -232,23 +243,64 @@ func main() {
pkg = filepath.Join(dir, pkg)
}
p.PackagePath = pkg
- p.writeOutput(f, input)
-
p.Record(f)
+ if *godefs {
+ os.Stdout.WriteString(p.godefs(f, input))
+ } else if *cdefs {
+ os.Stdout.WriteString(p.cdefs(f, input))
+ } else {
+ p.writeOutput(f, input)
+ }
}
- p.writeDefs()
+ if !*godefs && !*cdefs {
+ p.writeDefs()
+ }
if nerrors > 0 {
os.Exit(2)
}
}
+// newPackage returns a new Package that will invoke
+// gcc with the additional arguments specified in args.
+func newPackage(args []string) *Package {
+ // Copy the gcc options to a new slice so the list
+ // can grow without overwriting the slice that args is in.
+ gccOptions := make([]string, len(args))
+ copy(gccOptions, args)
+
+ goarch = runtime.GOARCH
+ if s := os.Getenv("GOARCH"); s != "" {
+ goarch = s
+ }
+ goos = runtime.GOOS
+ if s := os.Getenv("GOOS"); s != "" {
+ goos = s
+ }
+ ptrSize := ptrSizeMap[goarch]
+ if ptrSize == 0 {
+ fatalf("unknown $GOARCH %q", goarch)
+ }
+
+ // Reset locale variables so gcc emits English errors [sic].
+ os.Setenv("LANG", "en_US.UTF-8")
+ os.Setenv("LC_ALL", "C")
+
+ p := &Package{
+ PtrSize: ptrSize,
+ GccOptions: gccOptions,
+ CgoFlags: make(map[string]string),
+ Written: make(map[string]bool),
+ }
+ return p
+}
+
// Record what needs to be recorded about f.
func (p *Package) Record(f *File) {
if p.PackageName == "" {
p.PackageName = f.Package
} else if p.PackageName != f.Package {
- error(token.NoPos, "inconsistent package names: %s, %s", p.PackageName, f.Package)
+ error_(token.NoPos, "inconsistent package names: %s, %s", p.PackageName, f.Package)
}
if p.Name == nil {
@@ -258,11 +310,14 @@ 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", k)
}
}
}
- p.ExpFunc = append(p.ExpFunc, f.ExpFunc...)
+ if f.ExpFunc != nil {
+ p.ExpFunc = append(p.ExpFunc, f.ExpFunc...)
+ p.Preamble += "\n" + f.Preamble
+ }
p.Decl = append(p.Decl, f.AST.Decls...)
}
diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go
index 498ab1566..814250c2e 100644
--- a/src/cmd/cgo/out.go
+++ b/src/cmd/cgo/out.go
@@ -14,20 +14,19 @@ import (
"go/printer"
"go/token"
"os"
- "path/filepath"
"strings"
)
-var objDir = "_obj" + string(filepath.Separator)
+var conf = printer.Config{Mode: printer.SourcePos, Tabwidth: 8}
// writeDefs creates output files to be compiled by 6g, 6c, and gcc.
// (The comments here say 6g and 6c but the code applies to the 8 and 5 tools too.)
func (p *Package) writeDefs() {
- fgo2 := creat(objDir + "_cgo_gotypes.go")
- fc := creat(objDir + "_cgo_defun.c")
- fm := creat(objDir + "_cgo_main.c")
+ fgo2 := creat(*objDir + "_cgo_gotypes.go")
+ fc := creat(*objDir + "_cgo_defun.c")
+ fm := creat(*objDir + "_cgo_main.c")
- fflg := creat(objDir + "_cgo_flags")
+ fflg := creat(*objDir + "_cgo_flags")
for k, v := range p.CgoFlags {
fmt.Fprintf(fflg, "_CGO_%s=%s\n", k, v)
}
@@ -35,7 +34,13 @@ func (p *Package) writeDefs() {
// Write C main file for using gcc to resolve imports.
fmt.Fprintf(fm, "int main() { return 0; }\n")
- fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*, int), void *a, int c) { }\n")
+ if *importRuntimeCgo {
+ fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*, int), void *a, int c) { }\n")
+ } 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 _cgo_allocate(void *a, int c) { }\n")
fmt.Fprintf(fm, "void _cgo_panic(void *a, int c) { }\n")
@@ -45,19 +50,25 @@ func (p *Package) writeDefs() {
fmt.Fprintf(fgo2, "// Created by cgo - DO NOT EDIT\n\n")
fmt.Fprintf(fgo2, "package %s\n\n", p.PackageName)
fmt.Fprintf(fgo2, "import \"unsafe\"\n\n")
- fmt.Fprintf(fgo2, "import \"os\"\n\n")
- fmt.Fprintf(fgo2, "import _ \"runtime/cgo\"\n\n")
+ fmt.Fprintf(fgo2, "import \"syscall\"\n\n")
+ if !*gccgo && *importRuntimeCgo {
+ fmt.Fprintf(fgo2, "import _ \"runtime/cgo\"\n\n")
+ }
fmt.Fprintf(fgo2, "type _ unsafe.Pointer\n\n")
- fmt.Fprintf(fgo2, "func _Cerrno(dst *os.Error, x int) { *dst = os.Errno(x) }\n")
+ fmt.Fprintf(fgo2, "func _Cerrno(dst *error, x int) { *dst = syscall.Errno(x) }\n")
for name, def := range typedef {
fmt.Fprintf(fgo2, "type %s ", name)
- printer.Fprint(fgo2, fset, def)
- fmt.Fprintf(fgo2, "\n")
+ conf.Fprint(fgo2, fset, def.Go)
+ fmt.Fprintf(fgo2, "\n\n")
}
fmt.Fprintf(fgo2, "type _Ctype_void [0]byte\n")
- fmt.Fprintf(fc, cProlog)
+ if *gccgo {
+ fmt.Fprintf(fc, cPrologGccgo)
+ } else {
+ fmt.Fprintf(fc, cProlog)
+ }
cVars := make(map[string]bool)
for _, n := range p.Name {
@@ -78,7 +89,7 @@ func (p *Package) writeDefs() {
fmt.Fprintf(fc, "\n")
fmt.Fprintf(fgo2, "var %s ", n.Mangle)
- printer.Fprint(fgo2, fset, &ast.StarExpr{X: n.Type.Go})
+ conf.Fprint(fgo2, fset, &ast.StarExpr{X: n.Type.Go})
fmt.Fprintf(fgo2, "\n")
}
fmt.Fprintf(fc, "\n")
@@ -96,13 +107,26 @@ func (p *Package) writeDefs() {
}
}
- p.writeExports(fgo2, fc, fm)
+ if *gccgo {
+ p.writeGccgoExports(fgo2, fc, fm)
+ } else {
+ p.writeExports(fgo2, fc, fm)
+ }
fgo2.Close()
fc.Close()
}
func dynimport(obj string) {
+ stdout := os.Stdout
+ if *dynout != "" {
+ f, err := os.Create(*dynout)
+ if err != nil {
+ fatalf("%s", err)
+ }
+ stdout = f
+ }
+
if f, err := elf.Open(obj); err == nil {
sym, err := f.ImportedSymbols()
if err != nil {
@@ -113,14 +137,14 @@ func dynimport(obj string) {
if s.Version != "" {
targ += "@" + s.Version
}
- fmt.Printf("#pragma dynimport %s %s %q\n", s.Name, targ, s.Library)
+ fmt.Fprintf(stdout, "#pragma dynimport %s %s %q\n", s.Name, targ, s.Library)
}
lib, err := f.ImportedLibraries()
if err != nil {
fatalf("cannot load imported libraries from ELF file %s: %v", obj, err)
}
for _, l := range lib {
- fmt.Printf("#pragma dynimport _ _ %q\n", l)
+ fmt.Fprintf(stdout, "#pragma dynimport _ _ %q\n", l)
}
return
}
@@ -134,14 +158,14 @@ func dynimport(obj string) {
if len(s) > 0 && s[0] == '_' {
s = s[1:]
}
- fmt.Printf("#pragma dynimport %s %s %q\n", s, s, "")
+ fmt.Fprintf(stdout, "#pragma dynimport %s %s %q\n", s, s, "")
}
lib, err := f.ImportedLibraries()
if err != nil {
fatalf("cannot load imported libraries from Mach-O file %s: %v", obj, err)
}
for _, l := range lib {
- fmt.Printf("#pragma dynimport _ _ %q\n", l)
+ fmt.Fprintf(stdout, "#pragma dynimport _ _ %q\n", l)
}
return
}
@@ -153,7 +177,7 @@ func dynimport(obj string) {
}
for _, s := range sym {
ss := strings.Split(s, ":")
- fmt.Printf("#pragma dynimport %s %s %q\n", ss[0], ss[0], strings.ToLower(ss[1]))
+ fmt.Fprintf(stdout, "#pragma dynimport %s %s %q\n", ss[0], ss[0], strings.ToLower(ss[1]))
}
return
}
@@ -176,7 +200,11 @@ func (p *Package) structType(n *Name) (string, int64) {
fmt.Fprintf(&buf, "\t\tchar __pad%d[%d];\n", off, pad)
off += pad
}
- fmt.Fprintf(&buf, "\t\t%s p%d;\n", t.C, i)
+ c := t.Typedef
+ if c == "" {
+ c = t.C.String()
+ }
+ fmt.Fprintf(&buf, "\t\t%s p%d;\n", c, i)
off += t.Size
}
if off%p.PtrSize != 0 {
@@ -203,7 +231,7 @@ func (p *Package) structType(n *Name) (string, int64) {
off += pad
}
if n.AddError {
- fmt.Fprint(&buf, "\t\tvoid *e[2]; /* os.Error */\n")
+ fmt.Fprint(&buf, "\t\tvoid *e[2]; /* error */\n")
off += 2 * p.PtrSize
}
if off == 0 {
@@ -217,9 +245,9 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) {
name := n.Go
gtype := n.FuncType.Go
if n.AddError {
- // Add "os.Error" to return type list.
+ // Add "error" to return type list.
// Type list is known to be 0 or 1 element - it's a C function.
- err := &ast.Field{Type: ast.NewIdent("os.Error")}
+ err := &ast.Field{Type: ast.NewIdent("error")}
l := gtype.Results.List
if len(l) == 0 {
l = []*ast.Field{err}
@@ -237,8 +265,45 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) {
Name: ast.NewIdent(n.Mangle),
Type: gtype,
}
- printer.Fprint(fgo2, fset, d)
- fmt.Fprintf(fgo2, "\n")
+
+ if *gccgo {
+ // Gccgo style hooks.
+ // we hook directly into C. gccgo goes not support cgocall yet.
+ if !n.AddError {
+ fmt.Fprintf(fgo2, "//extern %s\n", n.C)
+ conf.Fprint(fgo2, fset, d)
+ fmt.Fprint(fgo2, "\n")
+ } else {
+ // write a small wrapper to retrieve errno.
+ cname := fmt.Sprintf("_cgo%s%s", cPrefix, n.Mangle)
+ paramnames := []string(nil)
+ for i, param := range d.Type.Params.List {
+ paramName := fmt.Sprintf("p%d", i)
+ param.Names = []*ast.Ident{ast.NewIdent(paramName)}
+ paramnames = append(paramnames, paramName)
+ }
+ conf.Fprint(fgo2, fset, d)
+ fmt.Fprintf(fgo2, "{\n")
+ fmt.Fprintf(fgo2, "\tsyscall.SetErrno(0)\n")
+ fmt.Fprintf(fgo2, "\tr := %s(%s)\n", cname, strings.Join(paramnames, ", "))
+ fmt.Fprintf(fgo2, "\te := syscall.GetErrno()\n")
+ fmt.Fprintf(fgo2, "\tif e != 0 {\n")
+ fmt.Fprintf(fgo2, "\t\treturn r, e\n")
+ fmt.Fprintf(fgo2, "\t}\n")
+ fmt.Fprintf(fgo2, "\treturn r, nil\n")
+ fmt.Fprintf(fgo2, "}\n")
+ // declare the C function.
+ fmt.Fprintf(fgo2, "//extern %s\n", n.C)
+ d.Name = ast.NewIdent(cname)
+ l := d.Type.Results.List
+ d.Type.Results.List = l[:len(l)-1]
+ conf.Fprint(fgo2, fset, d)
+ fmt.Fprint(fgo2, "\n")
+ }
+ return
+ }
+ conf.Fprint(fgo2, fset, d)
+ fmt.Fprint(fgo2, "\n")
if name == "CString" || name == "GoString" || name == "GoStringN" || name == "GoBytes" {
// The builtins are already defined in the C prolog.
@@ -276,7 +341,7 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) {
v[0] = 0;
v[1] = 0;
} else {
- ·_Cerrno(v, e); /* fill in v as os.Error for errno e */
+ ·_Cerrno(v, e); /* fill in v as error for errno e */
}
}`)
}
@@ -292,16 +357,15 @@ func (p *Package) writeOutput(f *File, srcfile string) {
base = base[0 : len(base)-3]
}
base = strings.Map(slashToUnderscore, base)
- fgo1 := creat(objDir + base + ".cgo1.go")
- fgcc := creat(objDir + base + ".cgo2.c")
+ fgo1 := creat(*objDir + base + ".cgo1.go")
+ fgcc := creat(*objDir + base + ".cgo2.c")
p.GoFiles = append(p.GoFiles, base+".cgo1.go")
p.GccFiles = append(p.GccFiles, base+".cgo2.c")
// Write Go output: Go input with rewrites of C.xxx to _C_xxx.
fmt.Fprintf(fgo1, "// Created by cgo - DO NOT EDIT\n\n")
- fmt.Fprintf(fgo1, "//line %s:1\n", srcfile)
- printer.Fprint(fgo1, fset, f.AST)
+ conf.Fprint(fgo1, fset, f.AST)
// While we process the vars and funcs, also write 6c and gcc output.
// Gcc output starts with the preamble.
@@ -327,6 +391,11 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) {
}
p.Written[name] = true
+ if *gccgo {
+ // we don't use wrappers with gccgo.
+ return
+ }
+
ctype, _ := p.structType(n)
// Gcc wrapper unpacks the C argument struct
@@ -351,10 +420,20 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) {
}
}
fmt.Fprintf(fgcc, "%s(", n.C)
- for i := range n.FuncType.Params {
+ for i, t := range n.FuncType.Params {
if i > 0 {
fmt.Fprintf(fgcc, ", ")
}
+ // We know the type params are correct, because
+ // the Go equivalents had good type params.
+ // However, our version of the type omits the magic
+ // words const and volatile, which can provoke
+ // C compiler warnings. Silence them by casting
+ // all pointers to void*. (Eventually that will produce
+ // other warnings.)
+ if c := t.C.String(); c[len(c)-1] == '*' {
+ fmt.Fprintf(fgcc, "(void*)")
+ }
fmt.Fprintf(fgcc, "a->p%d", i)
}
fmt.Fprintf(fgcc, ");\n")
@@ -368,10 +447,11 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) {
// Write out the various stubs we need to support functions exported
// from Go so that they are callable from C.
func (p *Package) writeExports(fgo2, fc, fm *os.File) {
- fgcc := creat(objDir + "_cgo_export.c")
- fgcch := creat("_cgo_export.h")
+ fgcc := creat(*objDir + "_cgo_export.c")
+ fgcch := creat(*objDir + "_cgo_export.h")
fmt.Fprintf(fgcch, "/* Created by cgo - DO NOT EDIT. */\n")
+ fmt.Fprintf(fgcch, "%s\n", p.Preamble)
fmt.Fprintf(fgcch, "%s\n", gccExportHeaderProlog)
fmt.Fprintf(fgcc, "/* Created by cgo - DO NOT EDIT. */\n")
@@ -501,8 +581,10 @@ func (p *Package) writeExports(fgo2, fc, fm *os.File) {
if fn.Recv != nil {
goname = "_cgoexpwrap" + cPrefix + "_" + fn.Recv.List[0].Names[0].Name + "_" + goname
}
- fmt.Fprintf(fc, "extern void ·%s();\n", goname)
- fmt.Fprintf(fc, "\nvoid\n")
+ fmt.Fprintf(fc, "#pragma dynexport %s %s\n", goname, goname)
+ fmt.Fprintf(fc, "extern void ·%s();\n\n", goname)
+ fmt.Fprintf(fc, "#pragma textflag 7\n") // no split stack, so no use of m or g
+ fmt.Fprintf(fc, "void\n")
fmt.Fprintf(fc, "_cgoexp%s_%s(void *a, int32 n)\n", cPrefix, exp.ExpName)
fmt.Fprintf(fc, "{\n")
fmt.Fprintf(fc, "\truntime·cgocallback(·%s, a, n);\n", goname)
@@ -514,11 +596,11 @@ func (p *Package) writeExports(fgo2, fc, fm *os.File) {
// a Go wrapper function.
if fn.Recv != nil {
fmt.Fprintf(fgo2, "func %s(recv ", goname)
- printer.Fprint(fgo2, fset, fn.Recv.List[0].Type)
+ conf.Fprint(fgo2, fset, fn.Recv.List[0].Type)
forFieldList(fntype.Params,
func(i int, atype ast.Expr) {
fmt.Fprintf(fgo2, ", p%d ", i)
- printer.Fprint(fgo2, fset, atype)
+ conf.Fprint(fgo2, fset, atype)
})
fmt.Fprintf(fgo2, ")")
if gccResult != "void" {
@@ -528,7 +610,7 @@ func (p *Package) writeExports(fgo2, fc, fm *os.File) {
if i > 0 {
fmt.Fprint(fgo2, ", ")
}
- printer.Fprint(fgo2, fset, atype)
+ conf.Fprint(fgo2, fset, atype)
})
fmt.Fprint(fgo2, ")")
}
@@ -551,6 +633,83 @@ func (p *Package) writeExports(fgo2, fc, fm *os.File) {
}
}
+// Write out the C header allowing C code to call exported gccgo functions.
+func (p *Package) writeGccgoExports(fgo2, fc, fm *os.File) {
+ fgcc := creat(*objDir + "_cgo_export.c")
+ fgcch := creat(*objDir + "_cgo_export.h")
+ _ = fgcc
+
+ fmt.Fprintf(fgcch, "/* Created by cgo - DO NOT EDIT. */\n")
+ fmt.Fprintf(fgcch, "%s\n", p.Preamble)
+ fmt.Fprintf(fgcch, "%s\n", gccExportHeaderProlog)
+ fmt.Fprintf(fm, "#include \"_cgo_export.h\"\n")
+
+ clean := func(r rune) rune {
+ switch {
+ case 'A' <= r && r <= 'Z', 'a' <= r && r <= 'z',
+ '0' <= r && r <= '9':
+ return r
+ }
+ return '_'
+ }
+ gccgoSymbolPrefix := strings.Map(clean, *gccgoprefix)
+
+ for _, exp := range p.ExpFunc {
+ // TODO: support functions with receivers.
+ fn := exp.Func
+ fntype := fn.Type
+
+ if !ast.IsExported(fn.Name.Name) {
+ fatalf("cannot export unexported function %s with gccgo", fn.Name)
+ }
+
+ cdeclBuf := new(bytes.Buffer)
+ resultCount := 0
+ forFieldList(fntype.Results,
+ func(i int, atype ast.Expr) { resultCount++ })
+ switch resultCount {
+ case 0:
+ fmt.Fprintf(cdeclBuf, "void")
+ case 1:
+ forFieldList(fntype.Results,
+ func(i int, atype ast.Expr) {
+ t := p.cgoType(atype)
+ fmt.Fprintf(cdeclBuf, "%s", t.C)
+ })
+ default:
+ // Declare a result struct.
+ fmt.Fprintf(fgcch, "struct %s_result {\n", exp.ExpName)
+ forFieldList(fntype.Results,
+ func(i int, atype ast.Expr) {
+ t := p.cgoType(atype)
+ fmt.Fprintf(fgcch, "\t%s r%d;\n", t.C, i)
+ })
+ fmt.Fprintf(fgcch, "};\n")
+ fmt.Fprintf(cdeclBuf, "struct %s_result", exp.ExpName)
+ }
+
+ // The function name.
+ fmt.Fprintf(cdeclBuf, " "+exp.ExpName)
+ gccgoSymbol := fmt.Sprintf("%s.%s.%s", gccgoSymbolPrefix, p.PackageName, exp.Func.Name)
+ fmt.Fprintf(cdeclBuf, " (")
+ // Function parameters.
+ forFieldList(fntype.Params,
+ func(i int, atype ast.Expr) {
+ if i > 0 {
+ fmt.Fprintf(cdeclBuf, ", ")
+ }
+ t := p.cgoType(atype)
+ fmt.Fprintf(cdeclBuf, "%s p%d", t.C, i)
+ })
+ fmt.Fprintf(cdeclBuf, ")")
+ cdecl := cdeclBuf.String()
+
+ fmt.Fprintf(fgcch, "extern %s __asm__(\"%s\");\n", cdecl, gccgoSymbol)
+ // Dummy declaration for _cgo_main.c
+ fmt.Fprintf(fm, "%s {}\n", cdecl)
+ }
+}
+
// Call a function for each entry in an ast.FieldList, passing the
// index into the list and the type.
func forFieldList(fl *ast.FieldList, fn func(int, ast.Expr)) {
@@ -577,22 +736,25 @@ func c(repr string, args ...interface{}) *TypeRepr {
// Map predeclared Go types to Type.
var goTypes = map[string]*Type{
- "int": &Type{Size: 4, Align: 4, C: c("int")},
- "uint": &Type{Size: 4, Align: 4, C: c("uint")},
- "int8": &Type{Size: 1, Align: 1, C: c("schar")},
- "uint8": &Type{Size: 1, Align: 1, C: c("uchar")},
- "int16": &Type{Size: 2, Align: 2, C: c("short")},
- "uint16": &Type{Size: 2, Align: 2, C: c("ushort")},
- "int32": &Type{Size: 4, Align: 4, C: c("int")},
- "uint32": &Type{Size: 4, Align: 4, C: c("uint")},
- "int64": &Type{Size: 8, Align: 8, C: c("int64")},
- "uint64": &Type{Size: 8, Align: 8, C: c("uint64")},
- "float": &Type{Size: 4, Align: 4, C: c("float")},
- "float32": &Type{Size: 4, Align: 4, C: c("float")},
- "float64": &Type{Size: 8, Align: 8, C: c("double")},
- "complex": &Type{Size: 8, Align: 8, C: c("__complex float")},
- "complex64": &Type{Size: 8, Align: 8, C: c("__complex float")},
- "complex128": &Type{Size: 16, Align: 16, C: c("__complex double")},
+ "bool": {Size: 1, Align: 1, C: c("uchar")},
+ "byte": {Size: 1, Align: 1, C: c("uchar")},
+ "int": {Size: 4, Align: 4, C: c("int")},
+ "uint": {Size: 4, Align: 4, C: c("uint")},
+ "rune": {Size: 4, Align: 4, C: c("int")},
+ "int8": {Size: 1, Align: 1, C: c("schar")},
+ "uint8": {Size: 1, Align: 1, C: c("uchar")},
+ "int16": {Size: 2, Align: 2, C: c("short")},
+ "uint16": {Size: 2, Align: 2, C: c("ushort")},
+ "int32": {Size: 4, Align: 4, C: c("int")},
+ "uint32": {Size: 4, Align: 4, C: c("uint")},
+ "int64": {Size: 8, Align: 8, C: c("int64")},
+ "uint64": {Size: 8, Align: 8, C: c("uint64")},
+ "float": {Size: 4, Align: 4, C: c("float")},
+ "float32": {Size: 4, Align: 4, C: c("float")},
+ "float64": {Size: 8, Align: 8, C: c("double")},
+ "complex": {Size: 8, Align: 8, C: c("__complex float")},
+ "complex64": {Size: 8, Align: 8, C: c("__complex float")},
+ "complex128": {Size: 16, Align: 16, C: c("__complex double")},
}
// Map an ast type to a Type.
@@ -610,7 +772,7 @@ func (p *Package) cgoType(e ast.Expr) *Type {
case *ast.FuncType:
return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("void*")}
case *ast.InterfaceType:
- return &Type{Size: 3 * p.PtrSize, Align: p.PtrSize, C: c("GoInterface")}
+ return &Type{Size: 2 * p.PtrSize, Align: p.PtrSize, C: c("GoInterface")}
case *ast.MapType:
return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("GoMap")}
case *ast.ChanType:
@@ -633,10 +795,8 @@ func (p *Package) cgoType(e ast.Expr) *Type {
}
}
}
- for name, def := range typedef {
- if name == t.Name {
- return p.cgoType(def)
- }
+ if def := typedef[t.Name]; def != nil {
+ return def
}
if t.Name == "uintptr" {
return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("uintptr")}
@@ -644,13 +804,16 @@ func (p *Package) cgoType(e ast.Expr) *Type {
if t.Name == "string" {
return &Type{Size: p.PtrSize + 4, Align: p.PtrSize, C: c("GoString")}
}
+ if t.Name == "error" {
+ return &Type{Size: 2 * p.PtrSize, Align: p.PtrSize, C: c("GoInterface")}
+ }
if r, ok := goTypes[t.Name]; ok {
if r.Align > p.PtrSize {
r.Align = p.PtrSize
}
return r
}
- error(e.Pos(), "unrecognized Go type %s", t.Name)
+ error_(e.Pos(), "unrecognized Go type %s", t.Name)
return &Type{Size: 4, Align: 4, C: c("int")}
case *ast.SelectorExpr:
id, ok := t.X.(*ast.Ident)
@@ -658,7 +821,7 @@ func (p *Package) cgoType(e ast.Expr) *Type {
return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("void*")}
}
}
- error(e.Pos(), "unrecognized Go type %T", e)
+ error_(e.Pos(), "Go type not supported in export: %s", gofmt(e))
return &Type{Size: 4, Align: 4, C: c("int")}
}
@@ -729,6 +892,43 @@ void
}
`
+const cPrologGccgo = `
+#include <stdint.h>
+#include <string.h>
+
+struct __go_string {
+ const unsigned char *__data;
+ int __length;
+};
+
+typedef struct __go_open_array {
+ void* __values;
+ int __count;
+ int __capacity;
+} Slice;
+
+struct __go_string __go_byte_array_to_string(const void* p, int len);
+struct __go_open_array __go_string_to_byte_array (struct __go_string str);
+
+const char *CString(struct __go_string s) {
+ return strndup((const char*)s.__data, s.__length);
+}
+
+struct __go_string GoString(char *p) {
+ int len = (p != NULL) ? strlen(p) : 0;
+ return __go_byte_array_to_string(p, len);
+}
+
+struct __go_string GoStringN(char *p, int n) {
+ return __go_byte_array_to_string(p, n);
+}
+
+Slice GoBytes(char *p, int n) {
+ struct __go_string s = { (const unsigned char *)p, n };
+ return __go_string_to_byte_array(s);
+}
+`
+
const gccExportHeaderProlog = `
typedef unsigned int uint;
typedef signed char schar;
diff --git a/src/cmd/cgo/util.go b/src/cmd/cgo/util.go
index e79b0e1bf..a0f216614 100644
--- a/src/cmd/cgo/util.go
+++ b/src/cmd/cgo/util.go
@@ -5,11 +5,11 @@
package main
import (
- "exec"
"fmt"
"go/token"
"io/ioutil"
"os"
+ "os/exec"
)
// run runs the command argv, feeding in stdin on standard input.
@@ -36,7 +36,6 @@ func run(stdin []byte, argv []string) (stdout, stderr []byte, ok bool) {
if err != nil {
fatalf("%s", err)
}
- defer p.Release()
r0.Close()
w1.Close()
w2.Close()
@@ -56,23 +55,31 @@ func run(stdin []byte, argv []string) (stdout, stderr []byte, ok bool) {
<-c
<-c
- w, err := p.Wait(0)
+ state, err := p.Wait()
if err != nil {
fatalf("%s", err)
}
- ok = w.Exited() && w.ExitStatus() == 0
+ ok = state.Success()
return
}
+func lineno(pos token.Pos) string {
+ return fset.Position(pos).String()
+}
+
// Die with an error message.
func fatalf(msg string, args ...interface{}) {
- fmt.Fprintf(os.Stderr, msg+"\n", args...)
+ // If we've already printed other errors, they might have
+ // caused the fatal condition. Assume they're enough.
+ if nerrors == 0 {
+ fmt.Fprintf(os.Stderr, msg+"\n", args...)
+ }
os.Exit(2)
}
var nerrors int
-func error(pos token.Pos, msg string, args ...interface{}) {
+func error_(pos token.Pos, msg string, args ...interface{}) {
nerrors++
if pos.IsValid() {
fmt.Fprintf(os.Stderr, "%s: ", fset.Position(pos).String())
@@ -102,7 +109,7 @@ func creat(name string) *os.File {
return f
}
-func slashToUnderscore(c int) int {
+func slashToUnderscore(c rune) rune {
if c == '/' || c == '\\' || c == ':' {
c = '_'
}
diff --git a/src/cmd/cov/Makefile b/src/cmd/cov/Makefile
deleted file mode 100644
index 95dba9c60..000000000
--- a/src/cmd/cov/Makefile
+++ /dev/null
@@ -1,39 +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 ../../Make.inc
-O:=$(HOST_O)
-
-# The directory is cov because the source is portable and general.
-# We call the binary 6cov to avoid confusion and because this binary
-# is linked only with amd64 and x86 support.
-
-TARG=6cov
-OFILES=\
- main.$O\
- tree.$O\
-
-HFILES=\
- tree.h\
-
-NOINSTALL=1
-include ../../Make.ccmd
-
-ifeq ($(GOOS),windows)
-NAME=windows
-else
-NAME=$(shell uname | tr A-Z a-z)
-endif
-
-install: install-$(NAME)
-install-linux: install-default
-install-freebsd: install-default
-install-windows: install-default
-
-# on Darwin, have to install and setgid; see $GOROOT/src/sudo.bash
-install-darwin: $(TARG)
- @true
-
-install-default: $(TARG)
- cp $(TARG) "$(GOBIN)"/$(TARG)
diff --git a/src/cmd/cov/doc.go b/src/cmd/cov/doc.go
deleted file mode 100644
index 5de00e19c..000000000
--- a/src/cmd/cov/doc.go
+++ /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.
-
-/*
-
-Cov is a rudimentary code coverage tool.
-
-Given a command to run, it runs the command while tracking which
-sections of code have been executed. When the command finishes,
-cov prints the line numbers of sections of code in the binary that
-were not executed. With no arguments it assumes the command "6.out".
-
-Usage: cov [-lsv] [-g substring] [-m minlines] [6.out args]
-
-The options are:
-
- -l
- print full path names instead of paths relative to the current directory
- -s
- show the source code that didn't execute, in addition to the line numbers.
- -v
- print debugging information during the run.
- -g substring
- restrict the coverage analysis to functions or files whose names contain substring
- -m minlines
- only report uncovered sections of code larger than minlines lines
-
-For reasons of disambiguation it is installed as 6cov although it also serves
-as an 8cov and a 5cov.
-
-*/
-package documentation
diff --git a/src/cmd/cov/main.c b/src/cmd/cov/main.c
deleted file mode 100644
index 5ff22c00a..000000000
--- a/src/cmd/cov/main.c
+++ /dev/null
@@ -1,480 +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.
-
-/*
- * code coverage
- */
-
-#include <u.h>
-#include <time.h>
-#include <libc.h>
-#include <bio.h>
-#include <ctype.h>
-#include "tree.h"
-
-#include <ureg_amd64.h>
-#include <mach.h>
-typedef struct Ureg Ureg;
-
-void
-usage(void)
-{
- fprint(2, "usage: cov [-lsv] [-g substring] [-m minlines] [6.out args...]\n");
- fprint(2, "-g specifies pattern of interesting functions or files\n");
- exits("usage");
-}
-
-typedef struct Range Range;
-struct Range
-{
- uvlong pc;
- uvlong epc;
-};
-
-int chatty;
-int fd;
-int longnames;
-int pid;
-int doshowsrc;
-Map *mem;
-Map *text;
-Fhdr fhdr;
-char *substring;
-char cwd[1000];
-int ncwd;
-int minlines = -1000;
-
-Tree breakpoints; // code ranges not run
-
-/*
- * comparison for Range structures
- * they are "equal" if they overlap, so
- * that a search for [pc, pc+1) finds the
- * Range containing pc.
- */
-int
-rangecmp(void *va, void *vb)
-{
- Range *a = va, *b = vb;
- if(a->epc <= b->pc)
- return 1;
- if(b->epc <= a->pc)
- return -1;
- return 0;
-}
-
-/*
- * remember that we ran the section of code [pc, epc).
- */
-void
-ran(uvlong pc, uvlong epc)
-{
- Range key;
- Range *r;
- uvlong oldepc;
-
- if(chatty)
- print("run %#llux-%#llux\n", pc, epc);
-
- key.pc = pc;
- key.epc = pc+1;
- r = treeget(&breakpoints, &key);
- if(r == nil)
- sysfatal("unchecked breakpoint at %#llux+%d", pc, (int)(epc-pc));
-
- // Might be that the tail of the sequence
- // was run already, so r->epc is before the end.
- // Adjust len.
- if(epc > r->epc)
- epc = r->epc;
-
- if(r->pc == pc) {
- r->pc = epc;
- } else {
- // Chop r to before pc;
- // add new entry for after if needed.
- // Changing r->epc does not affect r's position in the tree.
- oldepc = r->epc;
- r->epc = pc;
- if(epc < oldepc) {
- Range *n;
- n = malloc(sizeof *n);
- n->pc = epc;
- n->epc = oldepc;
- treeput(&breakpoints, n, n);
- }
- }
-}
-
-void
-showsrc(char *file, int line1, int line2)
-{
- Biobuf *b;
- char *p;
- int n, stop;
-
- if((b = Bopen(file, OREAD)) == nil) {
- print("\topen %s: %r\n", file);
- return;
- }
-
- for(n=1; n<line1 && (p = Brdstr(b, '\n', 1)) != nil; n++)
- free(p);
-
- // print up to five lines (this one and 4 more).
- // if there are more than five lines, print 4 and "..."
- stop = n+4;
- if(stop > line2)
- stop = line2;
- if(stop < line2)
- stop--;
- for(; n<=stop && (p = Brdstr(b, '\n', 1)) != nil; n++) {
- print(" %d %s\n", n, p);
- free(p);
- }
- if(n < line2)
- print(" ...\n");
- Bterm(b);
-}
-
-/*
- * if s is in the current directory or below,
- * return the relative path.
- */
-char*
-shortname(char *s)
-{
- if(!longnames && strlen(s) > ncwd && memcmp(s, cwd, ncwd) == 0 && s[ncwd] == '/')
- return s+ncwd+1;
- return s;
-}
-
-/*
- * we've decided that [pc, epc) did not run.
- * do something about it.
- */
-void
-missing(uvlong pc, uvlong epc)
-{
- char file[1000];
- int line1, line2;
- char buf[100];
- Symbol s;
- char *p;
- uvlong uv;
-
- if(!findsym(pc, CTEXT, &s) || !fileline(file, sizeof file, pc)) {
- notfound:
- print("%#llux-%#llux\n", pc, epc);
- return;
- }
- p = strrchr(file, ':');
- *p++ = 0;
- line1 = atoi(p);
- for(uv=pc; uv<epc; ) {
- if(!fileline(file, sizeof file, epc-2))
- goto notfound;
- uv += machdata->instsize(text, uv);
- }
- p = strrchr(file, ':');
- *p++ = 0;
- line2 = atoi(p);
-
- if(line2+1-line2 < minlines)
- return;
-
- if(pc == s.value) {
- // never entered function
- print("%s:%d %s never called (%#llux-%#llux)\n", shortname(file), line1, s.name, pc, epc);
- return;
- }
- if(pc <= s.value+13) {
- // probably stub for stack growth.
- // check whether last instruction is call to morestack.
- // the -5 below is the length of
- // CALL sys.morestack.
- buf[0] = 0;
- machdata->das(text, epc-5, 0, buf, sizeof buf);
- if(strstr(buf, "morestack"))
- return;
- }
-
- if(epc - pc == 5) {
- // check for CALL sys.panicindex
- buf[0] = 0;
- machdata->das(text, pc, 0, buf, sizeof buf);
- if(strstr(buf, "panicindex"))
- return;
- }
-
- if(epc - pc == 2 || epc -pc == 3) {
- // check for XORL inside shift.
- // (on x86 have to implement large left or unsigned right shift with explicit zeroing).
- // f+90 0x00002c9f CMPL CX,$20
- // f+93 0x00002ca2 JCS f+97(SB)
- // f+95 0x00002ca4 XORL AX,AX <<<
- // f+97 0x00002ca6 SHLL CL,AX
- // f+99 0x00002ca8 MOVL $1,CX
- //
- // f+c8 0x00002cd7 CMPL CX,$40
- // f+cb 0x00002cda JCS f+d0(SB)
- // f+cd 0x00002cdc XORQ AX,AX <<<
- // f+d0 0x00002cdf SHLQ CL,AX
- // f+d3 0x00002ce2 MOVQ $1,CX
- buf[0] = 0;
- machdata->das(text, pc, 0, buf, sizeof buf);
- if(strncmp(buf, "XOR", 3) == 0) {
- machdata->das(text, epc, 0, buf, sizeof buf);
- if(strncmp(buf, "SHL", 3) == 0 || strncmp(buf, "SHR", 3) == 0)
- return;
- }
- }
-
- if(epc - pc == 3) {
- // check for SAR inside shift.
- // (on x86 have to implement large signed right shift as >>31).
- // f+36 0x00016216 CMPL CX,$20
- // f+39 0x00016219 JCS f+3e(SB)
- // f+3b 0x0001621b SARL $1f,AX <<<
- // f+3e 0x0001621e SARL CL,AX
- // f+40 0x00016220 XORL CX,CX
- // f+42 0x00016222 CMPL CX,AX
- buf[0] = 0;
- machdata->das(text, pc, 0, buf, sizeof buf);
- if(strncmp(buf, "SAR", 3) == 0) {
- machdata->das(text, epc, 0, buf, sizeof buf);
- if(strncmp(buf, "SAR", 3) == 0)
- return;
- }
- }
-
- // show first instruction to make clear where we were.
- machdata->das(text, pc, 0, buf, sizeof buf);
-
- if(line1 != line2)
- print("%s:%d,%d %#llux-%#llux %s\n",
- shortname(file), line1, line2, pc, epc, buf);
- else
- print("%s:%d %#llux-%#llux %s\n",
- shortname(file), line1, pc, epc, buf);
- if(doshowsrc)
- showsrc(file, line1, line2);
-}
-
-/*
- * walk the tree, calling missing for each non-empty
- * section of missing code.
- */
-void
-walktree(TreeNode *t)
-{
- Range *n;
-
- if(t == nil)
- return;
- walktree(t->left);
- n = t->key;
- if(n->pc < n->epc)
- missing(n->pc, n->epc);
- walktree(t->right);
-}
-
-/*
- * set a breakpoint all over [pc, epc)
- * and remember that we did.
- */
-void
-breakpoint(uvlong pc, uvlong epc)
-{
- Range *r;
-
- r = malloc(sizeof *r);
- r->pc = pc;
- r->epc = epc;
- treeput(&breakpoints, r, r);
-
- for(; pc < epc; pc+=machdata->bpsize)
- put1(mem, pc, machdata->bpinst, machdata->bpsize);
-}
-
-/*
- * install breakpoints over all text symbols
- * that match the pattern.
- */
-void
-cover(void)
-{
- Symbol s;
- char *lastfn;
- uvlong lastpc;
- int i;
- char buf[200];
-
- lastfn = nil;
- lastpc = 0;
- for(i=0; textsym(&s, i); i++) {
- switch(s.type) {
- case 'T':
- case 't':
- if(lastpc != 0) {
- breakpoint(lastpc, s.value);
- lastpc = 0;
- }
- // Ignore second entry for a given name;
- // that's the debugging blob.
- if(lastfn && strcmp(s.name, lastfn) == 0)
- break;
- lastfn = s.name;
- buf[0] = 0;
- fileline(buf, sizeof buf, s.value);
- if(substring == nil || strstr(buf, substring) || strstr(s.name, substring))
- lastpc = s.value;
- }
- }
-}
-
-uvlong
-rgetzero(Map *map, char *reg)
-{
- return 0;
-}
-
-/*
- * remove the breakpoints at pc and successive instructions,
- * up to and including the first jump or other control flow transfer.
- */
-void
-uncover(uvlong pc)
-{
- uchar buf[1000];
- int n, n1, n2;
- uvlong foll[2];
-
- // Double-check that we stopped at a breakpoint.
- if(get1(mem, pc, buf, machdata->bpsize) < 0)
- sysfatal("read mem inst at %#llux: %r", pc);
- if(memcmp(buf, machdata->bpinst, machdata->bpsize) != 0)
- sysfatal("stopped at %#llux; not at breakpoint %d", pc, machdata->bpsize);
-
- // Figure out how many bytes of straight-line code
- // there are in the text starting at pc.
- n = 0;
- while(n < sizeof buf) {
- n1 = machdata->instsize(text, pc+n);
- if(n+n1 > sizeof buf)
- break;
- n2 = machdata->foll(text, pc+n, rgetzero, foll);
- n += n1;
- if(n2 != 1 || foll[0] != pc+n)
- break;
- }
-
- // Record that this section of code ran.
- ran(pc, pc+n);
-
- // Put original instructions back.
- if(get1(text, pc, buf, n) < 0)
- sysfatal("get1: %r");
- if(put1(mem, pc, buf, n) < 0)
- sysfatal("put1: %r");
-}
-
-int
-startprocess(char **argv)
-{
- int pid;
-
- if((pid = fork()) < 0)
- sysfatal("fork: %r");
- if(pid == 0) {
- pid = getpid();
- if(ctlproc(pid, "hang") < 0)
- sysfatal("ctlproc hang: %r");
- execv(argv[0], argv);
- sysfatal("exec %s: %r", argv[0]);
- }
- if(ctlproc(pid, "attached") < 0 || ctlproc(pid, "waitstop") < 0)
- sysfatal("attach %d %s: %r", pid, argv[0]);
- return pid;
-}
-
-int
-go(void)
-{
- uvlong pc;
- char buf[100];
- int n;
-
- for(n = 0;; n++) {
- ctlproc(pid, "startstop");
- if(get8(mem, offsetof(Ureg, ip), &pc) < 0) {
- rerrstr(buf, sizeof buf);
- if(strstr(buf, "exited") || strstr(buf, "No such process"))
- return n;
- sysfatal("cannot read pc: %r");
- }
- pc--;
- if(put8(mem, offsetof(Ureg, ip), pc) < 0)
- sysfatal("cannot write pc: %r");
- uncover(pc);
- }
-}
-
-void
-main(int argc, char **argv)
-{
- int n;
-
- ARGBEGIN{
- case 'g':
- substring = EARGF(usage());
- break;
- case 'l':
- longnames++;
- break;
- case 'n':
- minlines = atoi(EARGF(usage()));
- break;
- case 's':
- doshowsrc = 1;
- break;
- case 'v':
- chatty++;
- break;
- default:
- usage();
- }ARGEND
-
- getwd(cwd, sizeof cwd);
- ncwd = strlen(cwd);
-
- if(argc == 0) {
- *--argv = "6.out";
- argc++;
- }
- fd = open(argv[0], OREAD);
- if(fd < 0)
- sysfatal("open %s: %r", argv[0]);
- if(crackhdr(fd, &fhdr) <= 0)
- sysfatal("crackhdr: %r");
- machbytype(fhdr.type);
- if(syminit(fd, &fhdr) <= 0)
- sysfatal("syminit: %r");
- text = loadmap(nil, fd, &fhdr);
- if(text == nil)
- sysfatal("loadmap: %r");
- pid = startprocess(argv);
- mem = attachproc(pid, &fhdr);
- if(mem == nil)
- sysfatal("attachproc: %r");
- breakpoints.cmp = rangecmp;
- cover();
- n = go();
- walktree(breakpoints.root);
- if(chatty)
- print("%d breakpoints\n", n);
- detachproc(mem);
- exits(0);
-}
-
diff --git a/src/cmd/cov/tree.c b/src/cmd/cov/tree.c
deleted file mode 100644
index 116772e42..000000000
--- a/src/cmd/cov/tree.c
+++ /dev/null
@@ -1,246 +0,0 @@
-// Renamed from Map to Tree to avoid conflict with libmach.
-
-/*
-Copyright (c) 2003-2007 Russ Cox, Tom Bergan, Austin Clements,
- Massachusetts Institute of Technology
-Portions Copyright (c) 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.
-*/
-
-// Mutable map structure, but still based on
-// Okasaki, Red Black Trees in a Functional Setting, JFP 1999,
-// which is a lot easier than the traditional red-black
-// and plenty fast enough for me. (Also I could copy
-// and edit fmap.c.)
-
-#include <u.h>
-#include <libc.h>
-#include "tree.h"
-
-#define TreeNode TreeNode
-#define Tree Tree
-
-enum
-{
- Red = 0,
- Black = 1
-};
-
-
-// Red-black trees are binary trees with this property:
-// 1. No red node has a red parent.
-// 2. Every path from the root to a leaf contains the
-// same number of black nodes.
-
-static TreeNode*
-rwTreeNode(TreeNode *p, int color, TreeNode *left, void *key, void *value, TreeNode *right)
-{
- if(p == nil)
- p = malloc(sizeof *p);
- p->color = color;
- p->left = left;
- p->key = key;
- p->value = value;
- p->right = right;
- return p;
-}
-
-static TreeNode*
-balance(TreeNode *m0)
-{
- void *xk, *xv, *yk, *yv, *zk, *zv;
- TreeNode *a, *b, *c, *d;
- TreeNode *m1, *m2;
- int color;
- TreeNode *left, *right;
- void *key, *value;
-
- color = m0->color;
- left = m0->left;
- key = m0->key;
- value = m0->value;
- right = m0->right;
-
- // Okasaki notation: (T is mkTreeNode, B is Black, R is Red, x, y, z are key-value.
- //
- // balance B (T R (T R a x b) y c) z d
- // balance B (T R a x (T R b y c)) z d
- // balance B a x (T R (T R b y c) z d)
- // balance B a x (T R b y (T R c z d))
- //
- // = T R (T B a x b) y (T B c z d)
-
- if(color == Black){
- if(left && left->color == Red){
- if(left->left && left->left->color == Red){
- a = left->left->left;
- xk = left->left->key;
- xv = left->left->value;
- b = left->left->right;
- yk = left->key;
- yv = left->value;
- c = left->right;
- zk = key;
- zv = value;
- d = right;
- m1 = left;
- m2 = left->left;
- goto hard;
- }else if(left->right && left->right->color == Red){
- a = left->left;
- xk = left->key;
- xv = left->value;
- b = left->right->left;
- yk = left->right->key;
- yv = left->right->value;
- c = left->right->right;
- zk = key;
- zv = value;
- d = right;
- m1 = left;
- m2 = left->right;
- goto hard;
- }
- }else if(right && right->color == Red){
- if(right->left && right->left->color == Red){
- a = left;
- xk = key;
- xv = value;
- b = right->left->left;
- yk = right->left->key;
- yv = right->left->value;
- c = right->left->right;
- zk = right->key;
- zv = right->value;
- d = right->right;
- m1 = right;
- m2 = right->left;
- goto hard;
- }else if(right->right && right->right->color == Red){
- a = left;
- xk = key;
- xv = value;
- b = right->left;
- yk = right->key;
- yv = right->value;
- c = right->right->left;
- zk = right->right->key;
- zv = right->right->value;
- d = right->right->right;
- m1 = right;
- m2 = right->right;
- goto hard;
- }
- }
- }
- return rwTreeNode(m0, color, left, key, value, right);
-
-hard:
- return rwTreeNode(m0, Red, rwTreeNode(m1, Black, a, xk, xv, b),
- yk, yv, rwTreeNode(m2, Black, c, zk, zv, d));
-}
-
-static TreeNode*
-ins0(TreeNode *p, void *k, void *v, TreeNode *rw)
-{
- if(p == nil)
- return rwTreeNode(rw, Red, nil, k, v, nil);
- if(p->key == k){
- if(rw)
- return rwTreeNode(rw, p->color, p->left, k, v, p->right);
- p->value = v;
- return p;
- }
- if(p->key < k)
- p->left = ins0(p->left, k, v, rw);
- else
- p->right = ins0(p->right, k, v, rw);
- return balance(p);
-}
-
-static TreeNode*
-ins1(Tree *m, TreeNode *p, void *k, void *v, TreeNode *rw)
-{
- int i;
-
- if(p == nil)
- return rwTreeNode(rw, Red, nil, k, v, nil);
- i = m->cmp(p->key, k);
- if(i == 0){
- if(rw)
- return rwTreeNode(rw, p->color, p->left, k, v, p->right);
- p->value = v;
- return p;
- }
- if(i < 0)
- p->left = ins1(m, p->left, k, v, rw);
- else
- p->right = ins1(m, p->right, k, v, rw);
- return balance(p);
-}
-
-void
-treeputelem(Tree *m, void *key, void *val, TreeNode *rw)
-{
- if(m->cmp)
- m->root = ins1(m, m->root, key, val, rw);
- else
- m->root = ins0(m->root, key, val, rw);
-}
-
-void
-treeput(Tree *m, void *key, void *val)
-{
- treeputelem(m, key, val, nil);
-}
-
-void*
-treeget(Tree *m, void *key)
-{
- int i;
- TreeNode *p;
-
- p = m->root;
- if(m->cmp){
- for(;;){
- if(p == nil)
- return nil;
- i = m->cmp(p->key, key);
- if(i < 0)
- p = p->left;
- else if(i > 0)
- p = p->right;
- else
- return p->value;
- }
- }else{
- for(;;){
- if(p == nil)
- return nil;
- if(p->key == key)
- return p->value;
- if(p->key < key)
- p = p->left;
- else
- p = p->right;
- }
- }
-}
diff --git a/src/cmd/cov/tree.h b/src/cmd/cov/tree.h
deleted file mode 100644
index a716d83ad..000000000
--- a/src/cmd/cov/tree.h
+++ /dev/null
@@ -1,47 +0,0 @@
-// Renamed from Map to Tree to avoid conflict with libmach.
-
-/*
-Copyright (c) 2003-2007 Russ Cox, Tom Bergan, Austin Clements,
- Massachusetts Institute of Technology
-Portions Copyright (c) 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.
-*/
-
-typedef struct Tree Tree;
-typedef struct TreeNode TreeNode;
-struct Tree
-{
- int (*cmp)(void*, void*);
- TreeNode *root;
-};
-
-struct TreeNode
-{
- int color;
- TreeNode *left;
- void *key;
- void *value;
- TreeNode *right;
-};
-
-void *treeget(Tree*, void*);
-void treeput(Tree*, void*, void*);
-void treeputelem(Tree*, void*, void*, TreeNode*);
diff --git a/src/cmd/dist/README b/src/cmd/dist/README
new file mode 100644
index 000000000..cf194faf0
--- /dev/null
+++ b/src/cmd/dist/README
@@ -0,0 +1,45 @@
+This program, dist, is the bootstrapping tool for the Go distribution.
+It takes care of building the C programs (like the Go compiler) and
+the initial bootstrap copy of the go tool. It also serves as a catch-all
+to replace odd jobs previously done with shell scripts.
+
+Dist is itself written in very simple C. All interaction with C libraries,
+even standard C libraries, is confined to a single system-specific file
+(plan9.c, unix.c, windows.c), to aid portability. Functionality needed
+by other files should be exposed via the portability layer. Functions
+in the portability layer begin with an x prefix when they would otherwise
+use the same name as or be confused for an existing function.
+For example, xprintf is the portable printf.
+
+By far the most common data types in dist are strings and arrays of
+strings. Instead of using char* and char**, though, dist uses two named
+data structures, Buf and Vec, which own all the data they point at.
+The Buf operations are functions beginning with b; the Vec operations
+are functions beginning with v. The basic form of any function declaring
+Bufs or Vecs on the stack should be
+
+ void
+ myfunc(void)
+ {
+ Buf b1, b2;
+ Vec v1;
+
+ binit(&b1);
+ binit(&b2);
+ vinit(&v1);
+
+ ... main code ...
+ bprintf(&b1, "hello, world");
+ vadd(&v1, bstr(&b1)); // v1 takes a copy of its argument
+ bprintf(&b1, "another string");
+ vadd(&v1, bstr(&b1)); // v1 now has two strings
+
+ bfree(&b1);
+ bfree(&b2);
+ vfree(&v1);
+ }
+
+The binit/vinit calls prepare a buffer or vector for use, initializing the
+data structures, and the bfree/vfree calls free any memory they are still
+holding onto. Use of this idiom gives us lexically scoped allocations.
+
diff --git a/src/cmd/dist/a.h b/src/cmd/dist/a.h
new file mode 100644
index 000000000..c19b1f468
--- /dev/null
+++ b/src/cmd/dist/a.h
@@ -0,0 +1,149 @@
+// 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.
+
+typedef int bool;
+
+// The Time unit is unspecified; we just need to
+// be able to compare whether t1 is older than t2 with t1 < t2.
+typedef long long Time;
+
+#define nil ((void*)0)
+#define nelem(x) (sizeof(x)/sizeof((x)[0]))
+#define USED(x) ((void)(x))
+
+// A Buf is a byte buffer, like Go's []byte.
+typedef struct Buf Buf;
+struct Buf
+{
+ char *p;
+ int len;
+ int cap;
+};
+
+// A Vec is a string vector, like Go's []string.
+typedef struct Vec Vec;
+struct Vec
+{
+ char **p;
+ int len;
+ int cap;
+};
+
+// Modes for run.
+enum {
+ CheckExit = 1,
+};
+
+// buf.c
+bool bequal(Buf *s, Buf *t);
+void bsubst(Buf *b, char *x, char *y);
+void bfree(Buf *b);
+void bgrow(Buf *b, int n);
+void binit(Buf *b);
+char* bpathf(Buf *b, char *fmt, ...);
+char* bprintf(Buf *b, char *fmt, ...);
+void bwritef(Buf *b, char *fmt, ...);
+void breset(Buf *b);
+char* bstr(Buf *b);
+char* btake(Buf *b);
+void bwrite(Buf *b, void *v, int n);
+void bwriteb(Buf *dst, Buf *src);
+void bwritestr(Buf *b, char *p);
+void bswap(Buf *b, Buf *b1);
+void vadd(Vec *v, char *p);
+void vcopy(Vec *dst, char **src, int n);
+void vfree(Vec *v);
+void vgrow(Vec *v, int n);
+void vinit(Vec *v);
+void vreset(Vec *v);
+void vuniq(Vec *v);
+void splitlines(Vec*, char*);
+void splitfields(Vec*, char*);
+
+// build.c
+extern char *goarch;
+extern char *gobin;
+extern char *gochar;
+extern char *gohostarch;
+extern char *gohostos;
+extern char *goos;
+extern char *goroot;
+extern char *goroot_final;
+extern char *goversion;
+extern char *workdir;
+extern char *tooldir;
+extern char *slash;
+extern bool rebuildall;
+
+int find(char*, char**, int);
+void init(void);
+void cmdbanner(int, char**);
+void cmdbootstrap(int, char**);
+void cmdclean(int, char**);
+void cmdenv(int, char**);
+void cmdinstall(int, char**);
+void cmdversion(int, char**);
+
+// buildgc.c
+void gcopnames(char*, char*);
+void mkenam(char*, char*);
+
+// buildruntime.c
+void mkzasm(char*, char*);
+void mkzgoarch(char*, char*);
+void mkzgoos(char*, char*);
+void mkzruntimedefs(char*, char*);
+void mkzversion(char*, char*);
+
+// goc2c.c
+void goc2c(char*, char*);
+
+// main.c
+extern int vflag;
+void usage(void);
+void xmain(int argc, char **argv);
+
+// portability layer (plan9.c, unix.c, windows.c)
+bool contains(char *p, char *sep);
+void fatal(char *msg, ...);
+bool hasprefix(char *p, char *prefix);
+bool hassuffix(char *p, char *suffix);
+bool isabs(char*);
+bool isdir(char *p);
+bool isfile(char *p);
+char* lastelem(char*);
+Time mtime(char*);
+void readfile(Buf*, char*);
+void run(Buf *b, char *dir, int mode, char *cmd, ...);
+void runv(Buf *b, char *dir, int mode, Vec *argv);
+void bgrunv(char *dir, int mode, Vec *argv);
+void bgwait(void);
+bool streq(char*, char*);
+void writefile(Buf*, char*, int);
+void xatexit(void (*f)(void));
+void xexit(int);
+void xfree(void*);
+void xgetenv(Buf *b, char *name);
+void xgetwd(Buf *b);
+void* xmalloc(int n);
+void* xmalloc(int);
+int xmemcmp(void*, void*, int);
+void xmemmove(void*, void*, int);
+void xmkdir(char *p);
+void xmkdirall(char*);
+Time xmtime(char *p);
+void xprintf(char*, ...);
+void xqsort(void*, int, int, int(*)(const void*, const void*));
+void xreaddir(Vec *dst, char *dir);
+void* xrealloc(void*, int);
+void xrealwd(Buf *b, char *path);
+void xremove(char *p);
+void xremoveall(char *p);
+void xsetenv(char*, char*);
+int xstrcmp(char*, char*);
+char* xstrdup(char *p);
+int xstrlen(char*);
+char* xstrrchr(char*, int);
+char* xstrstr(char*, char*);
+char* xworkdir(void);
diff --git a/src/cmd/dist/arg.h b/src/cmd/dist/arg.h
new file mode 100644
index 000000000..6eef0353b
--- /dev/null
+++ b/src/cmd/dist/arg.h
@@ -0,0 +1,50 @@
+/*
+Derived from Inferno include/kern.h.
+
+http://code.google.com/p/inferno-os/source/browse/include/kern.h
+
+ 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.
+*/
+
+/* command line */
+extern char *argv0;
+#define ARGBEGIN for((argv0?0:(argv0=(*argv))),argv++,argc--;\
+ argv[0] && argv[0][0]=='-' && argv[0][1];\
+ argc--, argv++) {\
+ char *_args, *_argt;\
+ char _argc;\
+ _args = &argv[0][1];\
+ if(_args[0]=='-' && _args[1]==0){\
+ argc--; argv++; break;\
+ }\
+ _argc = 0;\
+ while((_argc = *_args++) != 0)\
+ switch(_argc)
+#define ARGEND _argt=0;USED(_argt);USED(_argc);USED(_args);}USED(argv);USED(argc);
+#define ARGF() (_argt=_args, _args="",\
+ (*_argt? _argt: argv[1]? (argc--, *++argv): 0))
+#define EARGF(x) (_argt=_args, _args="",\
+ (*_argt? _argt: argv[1]? (argc--, *++argv): ((x), fatal("usage"), (char*)0)))
+
+#define ARGC() _argc
+
diff --git a/src/cmd/dist/buf.c b/src/cmd/dist/buf.c
new file mode 100644
index 000000000..45fb1954d
--- /dev/null
+++ b/src/cmd/dist/buf.c
@@ -0,0 +1,279 @@
+// 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.
+
+// Byte buffers and string vectors.
+
+#include "a.h"
+
+// binit prepares an uninitialized buffer for use.
+void
+binit(Buf *b)
+{
+ b->p = nil;
+ b->len = 0;
+ b->cap = 0;
+}
+
+// breset truncates the buffer back to zero length.
+void
+breset(Buf *b)
+{
+ b->len = 0;
+}
+
+// bfree frees the storage associated with a buffer.
+void
+bfree(Buf *b)
+{
+ xfree(b->p);
+ binit(b);
+}
+
+// bgrow ensures that the buffer has at least n more bytes
+// between its len and cap.
+void
+bgrow(Buf *b, int n)
+{
+ int want;
+
+ want = b->len+n;
+ if(want > b->cap) {
+ b->cap = 2*want;
+ if(b->cap < 64)
+ b->cap = 64;
+ b->p = xrealloc(b->p, b->cap);
+ }
+}
+
+// bwrite appends the n bytes at v to the buffer.
+void
+bwrite(Buf *b, void *v, int n)
+{
+ bgrow(b, n);
+ xmemmove(b->p+b->len, v, n);
+ b->len += n;
+}
+
+// bwritestr appends the string p to the buffer.
+void
+bwritestr(Buf *b, char *p)
+{
+ bwrite(b, p, xstrlen(p));
+}
+
+// bstr returns a pointer to a NUL-terminated string of the
+// buffer contents. The pointer points into the buffer.
+char*
+bstr(Buf *b)
+{
+ bgrow(b, 1);
+ b->p[b->len] = '\0';
+ return b->p;
+}
+
+// btake takes ownership of the string form of the buffer.
+// After this call, the buffer has zero length and does not
+// refer to the memory that btake returned.
+char*
+btake(Buf *b)
+{
+ char *p;
+
+ p = bstr(b);
+ binit(b);
+ return p;
+}
+
+// bwriteb appends the src buffer to the dst buffer.
+void
+bwriteb(Buf *dst, Buf *src)
+{
+ bwrite(dst, src->p, src->len);
+}
+
+// bequal reports whether the buffers have the same content.
+bool
+bequal(Buf *s, Buf *t)
+{
+ return s->len == t->len && xmemcmp(s->p, t->p, s->len) == 0;
+}
+
+// bsubst rewites b to replace all occurrences of x with y.
+void
+bsubst(Buf *b, char *x, char *y)
+{
+ char *p;
+ int nx, ny, pos;
+
+ nx = xstrlen(x);
+ ny = xstrlen(y);
+
+ pos = 0;
+ for(;;) {
+ p = xstrstr(bstr(b)+pos, x);
+ if(p == nil)
+ break;
+ if(nx != ny) {
+ if(nx < ny) {
+ pos = p - b->p;
+ bgrow(b, ny-nx);
+ p = b->p + pos;
+ }
+ xmemmove(p+ny, p+nx, (b->p+b->len)-(p+nx));
+ }
+ xmemmove(p, y, ny);
+ pos = p+ny - b->p;
+ b->len += ny - nx;
+ }
+}
+
+// The invariant with the vectors is that v->p[0:v->len] is allocated
+// strings that are owned by the vector. The data beyond v->len may
+// be garbage.
+
+// vinit prepares an uninitialized vector for use.
+void
+vinit(Vec *v)
+{
+ v->p = nil;
+ v->len = 0;
+ v->cap = 0;
+}
+
+// vreset truncates the vector back to zero length.
+void
+vreset(Vec *v)
+{
+ int i;
+
+ for(i=0; i<v->len; i++) {
+ xfree(v->p[i]);
+ v->p[i] = nil;
+ }
+ v->len = 0;
+}
+
+// vfree frees the storage associated with the vector.
+void
+vfree(Vec *v)
+{
+ vreset(v);
+ xfree(v->p);
+ vinit(v);
+}
+
+
+// vgrow ensures that the vector has room for at least
+// n more entries between len and cap.
+void
+vgrow(Vec *v, int n)
+{
+ int want;
+
+ want = v->len+n;
+ if(want > v->cap) {
+ v->cap = 2*want;
+ if(v->cap < 64)
+ v->cap = 64;
+ v->p = xrealloc(v->p, v->cap*sizeof v->p[0]);
+ }
+}
+
+// vcopy copies the srclen strings at src into the vector.
+void
+vcopy(Vec *dst, char **src, int srclen)
+{
+ int i;
+
+ // use vadd, to make copies of strings
+ for(i=0; i<srclen; i++)
+ vadd(dst, src[i]);
+}
+
+// vadd adds a copy of the string p to the vector.
+void
+vadd(Vec *v, char *p)
+{
+ vgrow(v, 1);
+ if(p != nil)
+ p = xstrdup(p);
+ v->p[v->len++] = p;
+}
+
+// vaddn adds a string consisting of the n bytes at p to the vector.
+void
+vaddn(Vec *v, char *p, int n)
+{
+ char *q;
+
+ vgrow(v, 1);
+ q = xmalloc(n+1);
+ xmemmove(q, p, n);
+ q[n] = '\0';
+ v->p[v->len++] = q;
+}
+
+static int
+strpcmp(const void *a, const void *b)
+{
+ return xstrcmp(*(char**)a, *(char**)b);
+}
+
+// vuniq sorts the vector and then discards duplicates,
+// in the manner of sort | uniq.
+void
+vuniq(Vec *v)
+{
+ int i, n;
+
+ xqsort(v->p, v->len, sizeof(v->p[0]), strpcmp);
+ n = 0;
+ for(i=0; i<v->len; i++) {
+ if(n>0 && streq(v->p[i], v->p[n-1]))
+ xfree(v->p[i]);
+ else
+ v->p[n++] = v->p[i];
+ }
+ v->len = n;
+}
+
+// splitlines replaces the vector v with the result of splitting
+// the input p after each \n.
+void
+splitlines(Vec *v, char *p)
+{
+ int i;
+ char *start;
+
+ vreset(v);
+ start = p;
+ for(i=0; p[i]; i++) {
+ if(p[i] == '\n') {
+ vaddn(v, start, (p+i+1)-start);
+ start = p+i+1;
+ }
+ }
+ if(*start != '\0')
+ vadd(v, start);
+}
+
+// splitfields replaces the vector v with the result of splitting
+// the input p into non-empty fields containing no spaces.
+void
+splitfields(Vec *v, char *p)
+{
+ char *start;
+
+ vreset(v);
+ for(;;) {
+ while(*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')
+ p++;
+ if(*p == '\0')
+ break;
+ start = p;
+ while(*p != ' ' && *p != '\t' && *p != '\r' && *p != '\n' && *p != '\0')
+ p++;
+ vaddn(v, start, p-start);
+ }
+}
diff --git a/src/cmd/dist/build.c b/src/cmd/dist/build.c
new file mode 100644
index 000000000..567c9f336
--- /dev/null
+++ b/src/cmd/dist/build.c
@@ -0,0 +1,1530 @@
+// 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"
+#include "arg.h"
+
+/*
+ * Initialization for any invocation.
+ */
+
+// The usual variables.
+char *goarch;
+char *gobin;
+char *gohostarch;
+char *gohostchar;
+char *gohostos;
+char *goos;
+char *goroot = GOROOT_FINAL;
+char *goroot_final = GOROOT_FINAL;
+char *workdir;
+char *tooldir;
+char *gochar;
+char *goversion;
+char *slash; // / for unix, \ for windows
+
+bool rebuildall = 0;
+
+static bool shouldbuild(char*, char*);
+static void copy(char*, char*, int);
+static char *findgoversion(void);
+
+// The known architecture letters.
+static char *gochars = "568";
+
+// The known architectures.
+static char *okgoarch[] = {
+ // same order as gochars
+ "arm",
+ "amd64",
+ "386",
+};
+
+// The known operating systems.
+static char *okgoos[] = {
+ "darwin",
+ "linux",
+ "freebsd",
+ "netbsd",
+ "openbsd",
+ "plan9",
+ "windows",
+};
+
+static void rmworkdir(void);
+
+// find reports the first index of p in l[0:n], or else -1.
+int
+find(char *p, char **l, int n)
+{
+ int i;
+
+ for(i=0; i<n; i++)
+ if(streq(p, l[i]))
+ return i;
+ return -1;
+}
+
+// init handles initialization of the various global state, like goroot and goarch.
+void
+init(void)
+{
+ char *p;
+ int i;
+ Buf b;
+
+ binit(&b);
+
+ xgetenv(&b, "GOROOT");
+ if(b.len > 0) {
+ // if not "/", then strip trailing path separator
+ if(b.len >= 2 && b.p[b.len - 1] == slash[0])
+ b.len--;
+ goroot = btake(&b);
+ }
+
+ xgetenv(&b, "GOBIN");
+ if(b.len == 0)
+ bprintf(&b, "%s%sbin", goroot, slash);
+ gobin = btake(&b);
+
+ xgetenv(&b, "GOOS");
+ if(b.len == 0)
+ bwritestr(&b, gohostos);
+ goos = btake(&b);
+ if(find(goos, okgoos, nelem(okgoos)) < 0)
+ fatal("unknown $GOOS %s", goos);
+
+ p = bpathf(&b, "%s/include/u.h", goroot);
+ if(!isfile(p)) {
+ fatal("$GOROOT is not set correctly or not exported\n"
+ "\tGOROOT=%s\n"
+ "\t%s does not exist", goroot, p);
+ }
+
+ xgetenv(&b, "GOHOSTARCH");
+ if(b.len > 0)
+ gohostarch = btake(&b);
+
+ i = find(gohostarch, okgoarch, nelem(okgoarch));
+ if(i < 0)
+ fatal("unknown $GOHOSTARCH %s", gohostarch);
+ bprintf(&b, "%c", gochars[i]);
+ gohostchar = btake(&b);
+
+ xgetenv(&b, "GOARCH");
+ if(b.len == 0)
+ bwritestr(&b, gohostarch);
+ goarch = btake(&b);
+ i = find(goarch, okgoarch, nelem(okgoarch));
+ if(i < 0)
+ fatal("unknown $GOARCH %s", goarch);
+ bprintf(&b, "%c", gochars[i]);
+ gochar = btake(&b);
+
+ xsetenv("GOROOT", goroot);
+ xsetenv("GOARCH", goarch);
+ xsetenv("GOOS", goos);
+
+ // Make the environment more predictable.
+ xsetenv("LANG", "C");
+ xsetenv("LANGUAGE", "en_US.UTF8");
+
+ goversion = findgoversion();
+
+ workdir = xworkdir();
+ xatexit(rmworkdir);
+
+ bpathf(&b, "%s/pkg/tool/%s_%s", goroot, gohostos, gohostarch);
+ tooldir = btake(&b);
+
+ bfree(&b);
+}
+
+// rmworkdir deletes the work directory.
+static void
+rmworkdir(void)
+{
+ if(vflag > 1)
+ xprintf("rm -rf %s\n", workdir);
+ xremoveall(workdir);
+}
+
+// Remove trailing spaces.
+static void
+chomp(Buf *b)
+{
+ int c;
+
+ while(b->len > 0 && ((c=b->p[b->len-1]) == ' ' || c == '\t' || c == '\r' || c == '\n'))
+ b->len--;
+}
+
+
+// findgoversion determines the Go version to use in the version string.
+static char*
+findgoversion(void)
+{
+ char *tag, *rev, *p;
+ int i, nrev;
+ Buf b, path, bmore, branch;
+ Vec tags;
+
+ binit(&b);
+ binit(&path);
+ binit(&bmore);
+ binit(&branch);
+ vinit(&tags);
+
+ // The $GOROOT/VERSION file takes priority, for distributions
+ // without the Mercurial repo.
+ bpathf(&path, "%s/VERSION", goroot);
+ if(isfile(bstr(&path))) {
+ readfile(&b, bstr(&path));
+ chomp(&b);
+ // Commands such as "dist version > VERSION" will cause
+ // the shell to create an empty VERSION file and set dist's
+ // stdout to its fd. dist in turn looks at VERSION and uses
+ // its content if available, which is empty at this point.
+ if(b.len > 0)
+ goto done;
+ }
+
+ // The $GOROOT/VERSION.cache file is a cache to avoid invoking
+ // hg every time we run this command. Unlike VERSION, it gets
+ // deleted by the clean command.
+ bpathf(&path, "%s/VERSION.cache", goroot);
+ if(isfile(bstr(&path))) {
+ readfile(&b, bstr(&path));
+ chomp(&b);
+ goto done;
+ }
+
+ // Otherwise, use Mercurial.
+ // What is the current branch?
+ run(&branch, goroot, CheckExit, "hg", "identify", "-b", nil);
+ chomp(&branch);
+
+ // What are the tags along the current branch?
+ tag = "";
+ rev = ".";
+ run(&b, goroot, CheckExit, "hg", "log", "-b", bstr(&branch), "-r", ".:0", "--template", "{tags} + ", nil);
+ splitfields(&tags, bstr(&b));
+ nrev = 0;
+ for(i=0; i<tags.len; i++) {
+ p = tags.p[i];
+ if(streq(p, "+"))
+ nrev++;
+ if(hasprefix(p, "release.") || hasprefix(p, "weekly.") || hasprefix(p, "go")) {
+ tag = xstrdup(p);
+ // If this tag matches the current checkout
+ // exactly (no "+" yet), don't show extra
+ // revision information.
+ if(nrev == 0)
+ rev = "";
+ break;
+ }
+ }
+
+ if(tag[0] == '\0') {
+ // Did not find a tag; use branch name.
+ bprintf(&b, "branch.%s", bstr(&branch));
+ tag = btake(&b);
+ }
+
+ if(rev[0]) {
+ // Tag is before the revision we're building.
+ // Add extra information.
+ run(&bmore, goroot, CheckExit, "hg", "log", "--template", " +{node|short}", "-r", rev, nil);
+ chomp(&bmore);
+ }
+
+ bprintf(&b, "%s", tag);
+ if(bmore.len > 0)
+ bwriteb(&b, &bmore);
+
+ // Cache version.
+ writefile(&b, bstr(&path), 0);
+
+done:
+ p = btake(&b);
+
+
+ bfree(&b);
+ bfree(&path);
+ bfree(&bmore);
+ bfree(&branch);
+ vfree(&tags);
+
+ return p;
+}
+
+/*
+ * Initial tree setup.
+ */
+
+// The old tools that no longer live in $GOBIN or $GOROOT/bin.
+static char *oldtool[] = {
+ "5a", "5c", "5g", "5l",
+ "6a", "6c", "6g", "6l",
+ "8a", "8c", "8g", "8l",
+ "6cov",
+ "6nm",
+ "6prof",
+ "cgo",
+ "ebnflint",
+ "goapi",
+ "gofix",
+ "goinstall",
+ "gomake",
+ "gopack",
+ "gopprof",
+ "gotest",
+ "gotype",
+ "govet",
+ "goyacc",
+ "quietgcc",
+};
+
+// Unreleased directories (relative to $GOROOT) that should
+// not be in release branches.
+static char *unreleased[] = {
+ "src/cmd/cov",
+ "src/cmd/prof",
+ "src/pkg/old",
+ "src/pkg/exp",
+};
+
+// setup sets up the tree for the initial build.
+static void
+setup(void)
+{
+ int i;
+ Buf b;
+ char *p;
+
+ binit(&b);
+
+ // Create bin directory.
+ p = bpathf(&b, "%s/bin", goroot);
+ if(!isdir(p))
+ xmkdir(p);
+
+ // Create package directory.
+ p = bpathf(&b, "%s/pkg", goroot);
+ if(!isdir(p))
+ xmkdir(p);
+ p = bpathf(&b, "%s/pkg/%s_%s", goroot, gohostos, gohostarch);
+ if(rebuildall)
+ xremoveall(p);
+ xmkdirall(p);
+ if(!streq(goos, gohostos) || !streq(goarch, gohostarch)) {
+ p = bpathf(&b, "%s/pkg/%s_%s", goroot, goos, goarch);
+ if(rebuildall)
+ xremoveall(p);
+ xmkdirall(p);
+ }
+
+ // Create object directory.
+ // We keep it in pkg/ so that all the generated binaries
+ // are in one tree. If pkg/obj/libgc.a exists, it is a dreg from
+ // before we used subdirectories of obj. Delete all of obj
+ // to clean up.
+ bpathf(&b, "%s/pkg/obj/libgc.a", goroot);
+ if(isfile(bstr(&b)))
+ xremoveall(bpathf(&b, "%s/pkg/obj", goroot));
+ p = bpathf(&b, "%s/pkg/obj/%s_%s", goroot, gohostos, gohostarch);
+ if(rebuildall)
+ xremoveall(p);
+ xmkdirall(p);
+
+ // Create tool directory.
+ // We keep it in pkg/, just like the object directory above.
+ if(rebuildall)
+ xremoveall(tooldir);
+ xmkdirall(tooldir);
+
+ // Remove tool binaries from before the tool/gohostos_gohostarch
+ xremoveall(bpathf(&b, "%s/bin/tool", goroot));
+
+ // Remove old pre-tool binaries.
+ for(i=0; i<nelem(oldtool); i++)
+ xremove(bpathf(&b, "%s/bin/%s", goroot, oldtool[i]));
+
+ // If $GOBIN is set and has a Go compiler, it must be cleaned.
+ for(i=0; gochars[i]; i++) {
+ if(isfile(bprintf(&b, "%s%s%c%s", gobin, slash, gochars[i], "g"))) {
+ for(i=0; i<nelem(oldtool); i++)
+ xremove(bprintf(&b, "%s%s%s", gobin, slash, oldtool[i]));
+ break;
+ }
+ }
+
+ // For release, make sure excluded things are excluded.
+ if(hasprefix(goversion, "release.") || hasprefix(goversion, "go")) {
+ for(i=0; i<nelem(unreleased); i++)
+ if(isdir(bpathf(&b, "%s/%s", goroot, unreleased[i])))
+ fatal("%s should not exist in release build", bstr(&b));
+ }
+
+ bfree(&b);
+}
+
+/*
+ * C library and tool building
+ */
+
+// gccargs is the gcc command line to use for compiling a single C file.
+static char *proto_gccargs[] = {
+ "-Wall",
+ "-Wno-sign-compare",
+ "-Wno-missing-braces",
+ "-Wno-parentheses",
+ "-Wno-unknown-pragmas",
+ "-Wno-switch",
+ "-Wno-comment",
+ "-Werror",
+ "-fno-common",
+ "-ggdb",
+ "-O2",
+};
+
+static Vec gccargs;
+
+// deptab lists changes to the default dependencies for a given prefix.
+// deps ending in /* read the whole directory; deps beginning with -
+// exclude files with that prefix.
+static struct {
+ char *prefix; // prefix of target
+ char *dep[20]; // dependency tweaks for targets with that prefix
+} deptab[] = {
+ {"lib9", {
+ "$GOROOT/include/u.h",
+ "$GOROOT/include/utf.h",
+ "$GOROOT/include/fmt.h",
+ "$GOROOT/include/libc.h",
+ "fmt/*",
+ "utf/*",
+ }},
+ {"libbio", {
+ "$GOROOT/include/u.h",
+ "$GOROOT/include/utf.h",
+ "$GOROOT/include/fmt.h",
+ "$GOROOT/include/libc.h",
+ "$GOROOT/include/bio.h",
+ }},
+ {"libmach", {
+ "$GOROOT/include/u.h",
+ "$GOROOT/include/utf.h",
+ "$GOROOT/include/fmt.h",
+ "$GOROOT/include/libc.h",
+ "$GOROOT/include/bio.h",
+ "$GOROOT/include/ar.h",
+ "$GOROOT/include/bootexec.h",
+ "$GOROOT/include/mach.h",
+ "$GOROOT/include/ureg_amd64.h",
+ "$GOROOT/include/ureg_arm.h",
+ "$GOROOT/include/ureg_x86.h",
+ }},
+ {"cmd/cc", {
+ "-pgen.c",
+ "-pswt.c",
+ }},
+ {"cmd/gc", {
+ "-cplx.c",
+ "-pgen.c",
+ "-y1.tab.c", // makefile dreg
+ "opnames.h",
+ }},
+ {"cmd/5c", {
+ "../cc/pgen.c",
+ "../cc/pswt.c",
+ "../5l/enam.c",
+ "$GOROOT/pkg/obj/$GOOS_$GOARCH/libcc.a",
+ }},
+ {"cmd/6c", {
+ "../cc/pgen.c",
+ "../cc/pswt.c",
+ "../6l/enam.c",
+ "$GOROOT/pkg/obj/$GOOS_$GOARCH/libcc.a",
+ }},
+ {"cmd/8c", {
+ "../cc/pgen.c",
+ "../cc/pswt.c",
+ "../8l/enam.c",
+ "$GOROOT/pkg/obj/$GOOS_$GOARCH/libcc.a",
+ }},
+ {"cmd/5g", {
+ "../gc/cplx.c",
+ "../gc/pgen.c",
+ "../5l/enam.c",
+ "$GOROOT/pkg/obj/$GOOS_$GOARCH/libgc.a",
+ }},
+ {"cmd/6g", {
+ "../gc/cplx.c",
+ "../gc/pgen.c",
+ "../6l/enam.c",
+ "$GOROOT/pkg/obj/$GOOS_$GOARCH/libgc.a",
+ }},
+ {"cmd/8g", {
+ "../gc/cplx.c",
+ "../gc/pgen.c",
+ "../8l/enam.c",
+ "$GOROOT/pkg/obj/$GOOS_$GOARCH/libgc.a",
+ }},
+ {"cmd/5l", {
+ "../ld/data.c",
+ "../ld/elf.c",
+ "../ld/go.c",
+ "../ld/ldelf.c",
+ "../ld/ldmacho.c",
+ "../ld/ldpe.c",
+ "../ld/lib.c",
+ "../ld/symtab.c",
+ "enam.c",
+ }},
+ {"cmd/6l", {
+ "../ld/*",
+ "enam.c",
+ }},
+ {"cmd/8l", {
+ "../ld/*",
+ "enam.c",
+ }},
+ {"cmd/", {
+ "$GOROOT/pkg/obj/$GOOS_$GOARCH/libmach.a",
+ "$GOROOT/pkg/obj/$GOOS_$GOARCH/libbio.a",
+ "$GOROOT/pkg/obj/$GOOS_$GOARCH/lib9.a",
+ }},
+ {"pkg/runtime", {
+ "zasm_$GOOS_$GOARCH.h",
+ "zgoarch_$GOARCH.go",
+ "zgoos_$GOOS.go",
+ "zruntime_defs_$GOOS_$GOARCH.go",
+ "zversion.go",
+ }},
+};
+
+// depsuffix records the allowed suffixes for source files.
+char *depsuffix[] = {
+ ".c",
+ ".h",
+ ".s",
+ ".go",
+ ".goc",
+};
+
+// gentab records how to generate some trivial files.
+static struct {
+ char *nameprefix;
+ void (*gen)(char*, char*);
+} gentab[] = {
+ {"opnames.h", gcopnames},
+ {"enam.c", mkenam},
+ {"zasm_", mkzasm},
+ {"zgoarch_", mkzgoarch},
+ {"zgoos_", mkzgoos},
+ {"zruntime_defs_", mkzruntimedefs},
+ {"zversion.go", mkzversion},
+};
+
+// install installs the library, package, or binary associated with dir,
+// which is relative to $GOROOT/src.
+static void
+install(char *dir)
+{
+ char *name, *p, *elem, *prefix, *exe;
+ bool islib, ispkg, isgo, stale;
+ Buf b, b1, path;
+ Vec compile, files, link, go, missing, clean, lib, extra;
+ Time ttarg, t;
+ int i, j, k, n, doclean, targ;
+
+ if(vflag) {
+ if(!streq(goos, gohostos) || !streq(goarch, gohostarch))
+ xprintf("%s (%s/%s)\n", dir, goos, goarch);
+ else
+ xprintf("%s\n", dir);
+ }
+
+ binit(&b);
+ binit(&b1);
+ binit(&path);
+ vinit(&compile);
+ vinit(&files);
+ vinit(&link);
+ vinit(&go);
+ vinit(&missing);
+ vinit(&clean);
+ vinit(&lib);
+ vinit(&extra);
+
+ // path = full path to dir.
+ bpathf(&path, "%s/src/%s", goroot, dir);
+ name = lastelem(dir);
+
+ // For misc/prof, copy into the tool directory and we're done.
+ if(hasprefix(dir, "misc/")) {
+ copy(bpathf(&b, "%s/%s", tooldir, name),
+ bpathf(&b1, "%s/misc/%s", goroot, name), 1);
+ goto out;
+ }
+
+ // For release, cmd/prof and cmd/cov are not included.
+ if((streq(dir, "cmd/cov") || streq(dir, "cmd/prof")) && !isdir(bstr(&path))) {
+ if(vflag > 1)
+ xprintf("skipping %s - does not exist\n", dir);
+ goto out;
+ }
+
+ // set up gcc command line on first run.
+ if(gccargs.len == 0) {
+ xgetenv(&b, "CC");
+ if(b.len == 0)
+ bprintf(&b, "gcc");
+ splitfields(&gccargs, bstr(&b));
+ for(i=0; i<nelem(proto_gccargs); i++)
+ vadd(&gccargs, proto_gccargs[i]);
+ if(xstrstr(bstr(&b), "clang") != nil) {
+ vadd(&gccargs, "-Wno-dangling-else");
+ vadd(&gccargs, "-Wno-unused-value");
+ }
+ }
+
+ islib = hasprefix(dir, "lib") || streq(dir, "cmd/cc") || streq(dir, "cmd/gc");
+ ispkg = hasprefix(dir, "pkg");
+ isgo = ispkg || streq(dir, "cmd/go") || streq(dir, "cmd/cgo");
+
+ exe = "";
+ if(streq(gohostos, "windows"))
+ exe = ".exe";
+
+ // Start final link command line.
+ // Note: code below knows that link.p[targ] is the target.
+ if(islib) {
+ // C library.
+ vadd(&link, "ar");
+ vadd(&link, "rsc");
+ prefix = "";
+ if(!hasprefix(name, "lib"))
+ prefix = "lib";
+ targ = link.len;
+ vadd(&link, bpathf(&b, "%s/pkg/obj/%s_%s/%s%s.a", goroot, gohostos, gohostarch, prefix, name));
+ } else if(ispkg) {
+ // Go library (package).
+ vadd(&link, bpathf(&b, "%s/pack", tooldir));
+ vadd(&link, "grc");
+ p = bprintf(&b, "%s/pkg/%s_%s/%s", goroot, goos, goarch, dir+4);
+ *xstrrchr(p, '/') = '\0';
+ xmkdirall(p);
+ targ = link.len;
+ vadd(&link, bpathf(&b, "%s/pkg/%s_%s/%s.a", goroot, goos, goarch, dir+4));
+ } else if(streq(dir, "cmd/go") || streq(dir, "cmd/cgo")) {
+ // Go command.
+ vadd(&link, bpathf(&b, "%s/%sl", tooldir, gochar));
+ vadd(&link, "-o");
+ elem = name;
+ if(streq(elem, "go"))
+ elem = "go_bootstrap";
+ targ = link.len;
+ vadd(&link, bpathf(&b, "%s/%s%s", tooldir, elem, exe));
+ } else {
+ // C command. Use gccargs.
+ vcopy(&link, gccargs.p, gccargs.len);
+ vadd(&link, "-o");
+ targ = link.len;
+ vadd(&link, bpathf(&b, "%s/%s%s", tooldir, name, exe));
+ if(streq(gohostarch, "amd64"))
+ vadd(&link, "-m64");
+ else if(streq(gohostarch, "386"))
+ vadd(&link, "-m32");
+ }
+ ttarg = mtime(link.p[targ]);
+
+ // Gather files that are sources for this target.
+ // Everything in that directory, and any target-specific
+ // additions.
+ xreaddir(&files, bstr(&path));
+
+ // Remove files beginning with . or _,
+ // which are likely to be editor temporary files.
+ // This is the same heuristic build.ScanDir uses.
+ // There do exist real C files beginning with _,
+ // so limit that check to just Go files.
+ n = 0;
+ for(i=0; i<files.len; i++) {
+ p = files.p[i];
+ if(hasprefix(p, ".") || (hasprefix(p, "_") && hassuffix(p, ".go")))
+ xfree(p);
+ else
+ files.p[n++] = p;
+ }
+ files.len = n;
+
+ for(i=0; i<nelem(deptab); i++) {
+ if(hasprefix(dir, deptab[i].prefix)) {
+ for(j=0; (p=deptab[i].dep[j])!=nil; j++) {
+ breset(&b1);
+ bwritestr(&b1, p);
+ bsubst(&b1, "$GOROOT", goroot);
+ bsubst(&b1, "$GOOS", goos);
+ bsubst(&b1, "$GOARCH", goarch);
+ p = bstr(&b1);
+ if(hassuffix(p, ".a")) {
+ vadd(&lib, bpathf(&b, "%s", p));
+ continue;
+ }
+ if(hassuffix(p, "/*")) {
+ bpathf(&b, "%s/%s", bstr(&path), p);
+ b.len -= 2;
+ xreaddir(&extra, bstr(&b));
+ bprintf(&b, "%s", p);
+ b.len -= 2;
+ for(k=0; k<extra.len; k++)
+ vadd(&files, bpathf(&b1, "%s/%s", bstr(&b), extra.p[k]));
+ continue;
+ }
+ if(hasprefix(p, "-")) {
+ p++;
+ n = 0;
+ for(k=0; k<files.len; k++) {
+ if(hasprefix(files.p[k], p))
+ xfree(files.p[k]);
+ else
+ files.p[n++] = files.p[k];
+ }
+ files.len = n;
+ continue;
+ }
+ vadd(&files, p);
+ }
+ }
+ }
+ vuniq(&files);
+
+ // Convert to absolute paths.
+ for(i=0; i<files.len; i++) {
+ if(!isabs(files.p[i])) {
+ bpathf(&b, "%s/%s", bstr(&path), files.p[i]);
+ xfree(files.p[i]);
+ files.p[i] = btake(&b);
+ }
+ }
+
+ // Is the target up-to-date?
+ stale = rebuildall;
+ n = 0;
+ for(i=0; i<files.len; i++) {
+ p = files.p[i];
+ for(j=0; j<nelem(depsuffix); j++)
+ if(hassuffix(p, depsuffix[j]))
+ goto ok;
+ xfree(files.p[i]);
+ continue;
+ ok:
+ t = mtime(p);
+ if(t != 0 && !hassuffix(p, ".a") && !shouldbuild(p, dir)) {
+ xfree(files.p[i]);
+ continue;
+ }
+ if(hassuffix(p, ".go"))
+ vadd(&go, p);
+ if(t > ttarg)
+ stale = 1;
+ if(t == 0) {
+ vadd(&missing, p);
+ files.p[n++] = files.p[i];
+ continue;
+ }
+ files.p[n++] = files.p[i];
+ }
+ files.len = n;
+
+ for(i=0; i<lib.len && !stale; i++)
+ if(mtime(lib.p[i]) > ttarg)
+ stale = 1;
+
+ if(!stale)
+ goto out;
+
+ // For package runtime, copy some files into the work space.
+ if(streq(dir, "pkg/runtime")) {
+ copy(bpathf(&b, "%s/arch_GOARCH.h", workdir),
+ bpathf(&b1, "%s/arch_%s.h", bstr(&path), goarch), 0);
+ copy(bpathf(&b, "%s/defs_GOOS_GOARCH.h", workdir),
+ bpathf(&b1, "%s/defs_%s_%s.h", bstr(&path), goos, goarch), 0);
+ copy(bpathf(&b, "%s/os_GOOS.h", workdir),
+ bpathf(&b1, "%s/os_%s.h", bstr(&path), goos), 0);
+ copy(bpathf(&b, "%s/signals_GOOS.h", workdir),
+ bpathf(&b1, "%s/signals_%s.h", bstr(&path), goos), 0);
+ }
+
+ // Generate any missing files; regenerate existing ones.
+ for(i=0; i<files.len; i++) {
+ p = files.p[i];
+ elem = lastelem(p);
+ for(j=0; j<nelem(gentab); j++) {
+ if(hasprefix(elem, gentab[j].nameprefix)) {
+ if(vflag > 1)
+ xprintf("generate %s\n", p);
+ gentab[j].gen(bstr(&path), p);
+ // Do not add generated file to clean list.
+ // In pkg/runtime, we want to be able to
+ // build the package with the go tool,
+ // and it assumes these generated files already
+ // exist (it does not know how to build them).
+ // The 'clean' command can remove
+ // the generated files.
+ goto built;
+ }
+ }
+ // Did not rebuild p.
+ if(find(p, missing.p, missing.len) >= 0)
+ fatal("missing file %s", p);
+ built:;
+ }
+
+ // One more copy for package runtime.
+ // The last batch was required for the generators.
+ // This one is generated.
+ if(streq(dir, "pkg/runtime")) {
+ copy(bpathf(&b, "%s/zasm_GOOS_GOARCH.h", workdir),
+ bpathf(&b1, "%s/zasm_%s_%s.h", bstr(&path), goos, goarch), 0);
+ }
+
+ // Generate .c files from .goc files.
+ if(streq(dir, "pkg/runtime")) {
+ for(i=0; i<files.len; i++) {
+ p = files.p[i];
+ if(!hassuffix(p, ".goc"))
+ continue;
+ // b = path/zp but with _goarch.c instead of .goc
+ bprintf(&b, "%s%sz%s", bstr(&path), slash, lastelem(p));
+ b.len -= 4;
+ bwritef(&b, "_%s.c", goarch);
+ goc2c(p, bstr(&b));
+ vadd(&files, bstr(&b));
+ }
+ vuniq(&files);
+ }
+
+ if((!streq(goos, gohostos) || !streq(goarch, gohostarch)) && isgo) {
+ // We've generated the right files; the go command can do the build.
+ if(vflag > 1)
+ xprintf("skip build for cross-compile %s\n", dir);
+ goto nobuild;
+ }
+
+ // Compile the files.
+ for(i=0; i<files.len; i++) {
+ if(!hassuffix(files.p[i], ".c") && !hassuffix(files.p[i], ".s"))
+ continue;
+ name = lastelem(files.p[i]);
+
+ vreset(&compile);
+ if(!isgo) {
+ // C library or tool.
+ vcopy(&compile, gccargs.p, gccargs.len);
+ vadd(&compile, "-c");
+ if(streq(gohostarch, "amd64"))
+ vadd(&compile, "-m64");
+ else if(streq(gohostarch, "386"))
+ vadd(&compile, "-m32");
+ if(streq(dir, "lib9"))
+ vadd(&compile, "-DPLAN9PORT");
+
+ vadd(&compile, "-I");
+ vadd(&compile, bpathf(&b, "%s/include", goroot));
+
+ vadd(&compile, "-I");
+ vadd(&compile, bstr(&path));
+
+ // lib9/goos.c gets the default constants hard-coded.
+ if(streq(name, "goos.c")) {
+ vadd(&compile, bprintf(&b, "-DGOOS=\"%s\"", goos));
+ vadd(&compile, bprintf(&b, "-DGOARCH=\"%s\"", goarch));
+ bprintf(&b1, "%s", goroot_final);
+ bsubst(&b1, "\\", "\\\\"); // turn into C string
+ vadd(&compile, bprintf(&b, "-DGOROOT=\"%s\"", bstr(&b1)));
+ vadd(&compile, bprintf(&b, "-DGOVERSION=\"%s\"", goversion));
+ }
+
+ // gc/lex.c records the GOEXPERIMENT setting used during the build.
+ if(streq(name, "lex.c")) {
+ xgetenv(&b, "GOEXPERIMENT");
+ vadd(&compile, bprintf(&b1, "-DGOEXPERIMENT=\"%s\"", bstr(&b)));
+ }
+ } else {
+ // Supporting files for a Go package.
+ if(hassuffix(files.p[i], ".s"))
+ vadd(&compile, bpathf(&b, "%s/%sa", tooldir, gochar));
+ else {
+ vadd(&compile, bpathf(&b, "%s/%sc", tooldir, gochar));
+ vadd(&compile, "-FVw");
+ }
+ vadd(&compile, "-I");
+ vadd(&compile, workdir);
+ vadd(&compile, bprintf(&b, "-DGOOS_%s", goos));
+ vadd(&compile, bprintf(&b, "-DGOARCH_%s", goarch));
+ }
+
+ bpathf(&b, "%s/%s", workdir, lastelem(files.p[i]));
+ doclean = 1;
+ if(!isgo && streq(gohostos, "darwin")) {
+ // To debug C programs on OS X, it is not enough to say -ggdb
+ // on the command line. You have to leave the object files
+ // lying around too. Leave them in pkg/obj/, which does not
+ // get removed when this tool exits.
+ bpathf(&b1, "%s/pkg/obj/%s", goroot, dir);
+ xmkdirall(bstr(&b1));
+ bpathf(&b, "%s/%s", bstr(&b1), lastelem(files.p[i]));
+ doclean = 0;
+ }
+
+ b.p[b.len-1] = 'o'; // was c or s
+ vadd(&compile, "-o");
+ vadd(&compile, bstr(&b));
+ vadd(&compile, files.p[i]);
+ bgrunv(bstr(&path), CheckExit, &compile);
+
+ vadd(&link, bstr(&b));
+ if(doclean)
+ vadd(&clean, bstr(&b));
+ }
+ bgwait();
+
+ if(isgo) {
+ // The last loop was compiling individual files.
+ // Hand the Go files to the compiler en masse.
+ vreset(&compile);
+ vadd(&compile, bpathf(&b, "%s/%sg", tooldir, gochar));
+
+ bpathf(&b, "%s/_go_.%s", workdir, gochar);
+ vadd(&compile, "-o");
+ vadd(&compile, bstr(&b));
+ vadd(&clean, bstr(&b));
+ vadd(&link, bstr(&b));
+
+ vadd(&compile, "-p");
+ if(hasprefix(dir, "pkg/"))
+ vadd(&compile, dir+4);
+ else
+ vadd(&compile, "main");
+
+ if(streq(dir, "pkg/runtime"))
+ vadd(&compile, "-+");
+
+ vcopy(&compile, go.p, go.len);
+
+ runv(nil, bstr(&path), CheckExit, &compile);
+ }
+
+ if(!islib && !isgo) {
+ // C binaries need the libraries explicitly, and -lm.
+ vcopy(&link, lib.p, lib.len);
+ vadd(&link, "-lm");
+ }
+
+ // Remove target before writing it.
+ xremove(link.p[targ]);
+
+ runv(nil, nil, CheckExit, &link);
+
+nobuild:
+ // In package runtime, we install runtime.h and cgocall.h too,
+ // for use by cgo compilation.
+ if(streq(dir, "pkg/runtime")) {
+ copy(bpathf(&b, "%s/pkg/%s_%s/cgocall.h", goroot, goos, goarch),
+ bpathf(&b1, "%s/src/pkg/runtime/cgocall.h", goroot), 0);
+ copy(bpathf(&b, "%s/pkg/%s_%s/runtime.h", goroot, goos, goarch),
+ bpathf(&b1, "%s/src/pkg/runtime/runtime.h", goroot), 0);
+ }
+
+
+out:
+ for(i=0; i<clean.len; i++)
+ xremove(clean.p[i]);
+
+ bfree(&b);
+ bfree(&b1);
+ bfree(&path);
+ vfree(&compile);
+ vfree(&files);
+ vfree(&link);
+ vfree(&go);
+ vfree(&missing);
+ vfree(&clean);
+ vfree(&lib);
+ vfree(&extra);
+}
+
+// matchfield reports whether the field matches this build.
+static bool
+matchfield(char *f)
+{
+ return streq(f, goos) || streq(f, goarch) || streq(f, "cmd_go_bootstrap");
+}
+
+// shouldbuild reports whether we should build this file.
+// It applies the same rules that are used with context tags
+// in package go/build, except that the GOOS and GOARCH
+// can appear anywhere in the file name, not just after _.
+// In particular, they can be the entire file name (like windows.c).
+// We also allow the special tag cmd_go_bootstrap.
+// See ../go/bootstrap.go and package go/build.
+static bool
+shouldbuild(char *file, char *dir)
+{
+ char *name, *p;
+ int i, j, ret;
+ Buf b;
+ Vec lines, fields;
+
+ // Check file name for GOOS or GOARCH.
+ name = lastelem(file);
+ for(i=0; i<nelem(okgoos); i++)
+ if(contains(name, okgoos[i]) && !streq(okgoos[i], goos))
+ return 0;
+ for(i=0; i<nelem(okgoarch); i++)
+ if(contains(name, okgoarch[i]) && !streq(okgoarch[i], goarch))
+ return 0;
+
+ // Omit test files.
+ if(contains(name, "_test"))
+ return 0;
+
+ // cmd/go/doc.go has a giant /* */ comment before
+ // it gets to the important detail that it is not part of
+ // package main. We don't parse those comments,
+ // so special case that file.
+ if(hassuffix(file, "cmd/go/doc.go") || hassuffix(file, "cmd\\go\\doc.go"))
+ return 0;
+ if(hassuffix(file, "cmd/cgo/doc.go") || hassuffix(file, "cmd\\cgo\\doc.go"))
+ return 0;
+
+ // Check file contents for // +build lines.
+ binit(&b);
+ vinit(&lines);
+ vinit(&fields);
+
+ ret = 1;
+ readfile(&b, file);
+ splitlines(&lines, bstr(&b));
+ for(i=0; i<lines.len; i++) {
+ p = lines.p[i];
+ while(*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')
+ p++;
+ if(*p == '\0')
+ continue;
+ if(contains(p, "package documentation")) {
+ ret = 0;
+ goto out;
+ }
+ if(contains(p, "package main") && !streq(dir, "cmd/go") && !streq(dir, "cmd/cgo")) {
+ ret = 0;
+ goto out;
+ }
+ if(!hasprefix(p, "//"))
+ break;
+ if(!contains(p, "+build"))
+ continue;
+ splitfields(&fields, lines.p[i]);
+ if(fields.len < 2 || !streq(fields.p[1], "+build"))
+ continue;
+ for(j=2; j<fields.len; j++) {
+ p = fields.p[j];
+ if((*p == '!' && !matchfield(p+1)) || matchfield(p))
+ goto fieldmatch;
+ }
+ ret = 0;
+ goto out;
+ fieldmatch:;
+ }
+
+out:
+ bfree(&b);
+ vfree(&lines);
+ vfree(&fields);
+
+ return ret;
+}
+
+// copy copies the file src to dst, via memory (so only good for small files).
+static void
+copy(char *dst, char *src, int exec)
+{
+ Buf b;
+
+ if(vflag > 1)
+ xprintf("cp %s %s\n", src, dst);
+
+ binit(&b);
+ readfile(&b, src);
+ writefile(&b, dst, exec);
+ bfree(&b);
+}
+
+// buildorder records the order of builds for the 'go bootstrap' command.
+static char *buildorder[] = {
+ "lib9",
+ "libbio",
+ "libmach",
+
+ "misc/pprof",
+
+ "cmd/addr2line",
+ "cmd/cov",
+ "cmd/nm",
+ "cmd/objdump",
+ "cmd/pack",
+ "cmd/prof",
+
+ "cmd/cc", // must be before c
+ "cmd/gc", // must be before g
+ "cmd/%sl", // must be before a, c, g
+ "cmd/%sa",
+ "cmd/%sc",
+ "cmd/%sg",
+
+ // The dependency order here was copied from a buildscript
+ // back when there were build scripts. Will have to
+ // be maintained by hand, but shouldn't change very
+ // often.
+ "pkg/runtime",
+ "pkg/errors",
+ "pkg/sync/atomic",
+ "pkg/sync",
+ "pkg/io",
+ "pkg/unicode",
+ "pkg/unicode/utf8",
+ "pkg/unicode/utf16",
+ "pkg/bytes",
+ "pkg/math",
+ "pkg/strings",
+ "pkg/strconv",
+ "pkg/bufio",
+ "pkg/sort",
+ "pkg/container/heap",
+ "pkg/encoding/base64",
+ "pkg/syscall",
+ "pkg/time",
+ "pkg/os",
+ "pkg/reflect",
+ "pkg/fmt",
+ "pkg/encoding/json",
+ "pkg/flag",
+ "pkg/path/filepath",
+ "pkg/path",
+ "pkg/io/ioutil",
+ "pkg/log",
+ "pkg/regexp/syntax",
+ "pkg/regexp",
+ "pkg/go/token",
+ "pkg/go/scanner",
+ "pkg/go/ast",
+ "pkg/go/parser",
+ "pkg/os/exec",
+ "pkg/net/url",
+ "pkg/text/template/parse",
+ "pkg/text/template",
+ "pkg/go/doc",
+ "pkg/go/build",
+ "cmd/go",
+};
+
+// cleantab records the directories to clean in 'go clean'.
+// It is bigger than the buildorder because we clean all the
+// compilers but build only the $GOARCH ones.
+static char *cleantab[] = {
+ "cmd/5a",
+ "cmd/5c",
+ "cmd/5g",
+ "cmd/5l",
+ "cmd/6a",
+ "cmd/6c",
+ "cmd/6g",
+ "cmd/6l",
+ "cmd/8a",
+ "cmd/8c",
+ "cmd/8g",
+ "cmd/8l",
+ "cmd/addr2line",
+ "cmd/cc",
+ "cmd/cov",
+ "cmd/gc",
+ "cmd/go",
+ "cmd/nm",
+ "cmd/objdump",
+ "cmd/pack",
+ "cmd/prof",
+ "lib9",
+ "libbio",
+ "libmach",
+ "pkg/bufio",
+ "pkg/bytes",
+ "pkg/container/heap",
+ "pkg/encoding/base64",
+ "pkg/encoding/json",
+ "pkg/errors",
+ "pkg/flag",
+ "pkg/fmt",
+ "pkg/go/ast",
+ "pkg/go/build",
+ "pkg/go/doc",
+ "pkg/go/parser",
+ "pkg/go/scanner",
+ "pkg/go/token",
+ "pkg/io",
+ "pkg/io/ioutil",
+ "pkg/log",
+ "pkg/math",
+ "pkg/net/url",
+ "pkg/os",
+ "pkg/os/exec",
+ "pkg/path",
+ "pkg/path/filepath",
+ "pkg/reflect",
+ "pkg/regexp",
+ "pkg/regexp/syntax",
+ "pkg/runtime",
+ "pkg/sort",
+ "pkg/strconv",
+ "pkg/strings",
+ "pkg/sync",
+ "pkg/sync/atomic",
+ "pkg/syscall",
+ "pkg/text/template",
+ "pkg/text/template/parse",
+ "pkg/time",
+ "pkg/unicode",
+ "pkg/unicode/utf16",
+ "pkg/unicode/utf8",
+};
+
+static void
+clean(void)
+{
+ int i, j, k;
+ Buf b, path;
+ Vec dir;
+
+ binit(&b);
+ binit(&path);
+ vinit(&dir);
+
+ for(i=0; i<nelem(cleantab); i++) {
+ if((streq(cleantab[i], "cmd/cov") || streq(cleantab[i], "cmd/prof")) && !isdir(cleantab[i]))
+ continue;
+ bpathf(&path, "%s/src/%s", goroot, cleantab[i]);
+ xreaddir(&dir, bstr(&path));
+ // Remove generated files.
+ for(j=0; j<dir.len; j++) {
+ for(k=0; k<nelem(gentab); k++) {
+ if(hasprefix(dir.p[j], gentab[k].nameprefix))
+ xremove(bpathf(&b, "%s/%s", bstr(&path), dir.p[j]));
+ }
+ }
+ // Remove generated binary named for directory.
+ if(hasprefix(cleantab[i], "cmd/"))
+ xremove(bpathf(&b, "%s/%s", bstr(&path), cleantab[i]+4));
+ }
+
+ if(rebuildall) {
+ // Remove object tree.
+ xremoveall(bpathf(&b, "%s/pkg/obj/%s_%s", goroot, gohostos, gohostarch));
+
+ // Remove installed packages and tools.
+ xremoveall(bpathf(&b, "%s/pkg/%s_%s", goroot, gohostos, gohostarch));
+ xremoveall(bpathf(&b, "%s/pkg/%s_%s", goroot, goos, goarch));
+ xremoveall(tooldir);
+
+ // Remove cached version info.
+ xremove(bpathf(&b, "%s/VERSION.cache", goroot));
+ }
+
+ bfree(&b);
+ bfree(&path);
+ vfree(&dir);
+}
+
+/*
+ * command implementations
+ */
+
+void
+usage(void)
+{
+ xprintf("usage: go tool dist [command]\n"
+ "Commands are:\n"
+ "\n"
+ "banner print installation banner\n"
+ "bootstrap rebuild everything\n"
+ "clean deletes all built files\n"
+ "env [-p] print environment (-p: include $PATH)\n"
+ "install [dir] install individual directory\n"
+ "version print Go version\n"
+ "\n"
+ "All commands take -v flags to emit extra information.\n"
+ );
+ xexit(2);
+}
+
+// The env command prints the default environment.
+void
+cmdenv(int argc, char **argv)
+{
+ bool pflag;
+ char *sep;
+ Buf b, b1;
+ char *format;
+
+ binit(&b);
+ binit(&b1);
+
+ format = "%s=\"%s\"\n";
+ pflag = 0;
+ ARGBEGIN{
+ case 'p':
+ pflag = 1;
+ break;
+ case 'v':
+ vflag++;
+ break;
+ case 'w':
+ format = "set %s=%s\n";
+ break;
+ default:
+ usage();
+ }ARGEND
+
+ if(argc > 0)
+ usage();
+
+ xprintf(format, "GOROOT", goroot);
+ xprintf(format, "GOBIN", gobin);
+ xprintf(format, "GOARCH", goarch);
+ xprintf(format, "GOOS", goos);
+ xprintf(format, "GOHOSTARCH", gohostarch);
+ xprintf(format, "GOHOSTOS", gohostos);
+ xprintf(format, "GOTOOLDIR", tooldir);
+ xprintf(format, "GOCHAR", gochar);
+
+ if(pflag) {
+ sep = ":";
+ if(streq(gohostos, "windows"))
+ sep = ";";
+ xgetenv(&b, "PATH");
+ bprintf(&b1, "%s%s%s", gobin, sep, bstr(&b));
+ xprintf(format, "PATH", bstr(&b1));
+ }
+
+ bfree(&b);
+ bfree(&b1);
+}
+
+// The bootstrap command runs a build from scratch,
+// stopping at having installed the go_bootstrap command.
+void
+cmdbootstrap(int argc, char **argv)
+{
+ int i;
+ Buf b;
+ char *oldgoos, *oldgoarch, *oldgochar;
+
+ binit(&b);
+
+ ARGBEGIN{
+ case 'a':
+ rebuildall = 1;
+ break;
+ case 'v':
+ vflag++;
+ break;
+ default:
+ usage();
+ }ARGEND
+
+ if(argc > 0)
+ usage();
+
+ if(rebuildall)
+ clean();
+ goversion = findgoversion();
+ setup();
+
+ xsetenv("GOROOT", goroot);
+ xsetenv("GOROOT_FINAL", goroot_final);
+
+ // For the main bootstrap, building for host os/arch.
+ oldgoos = goos;
+ oldgoarch = goarch;
+ oldgochar = gochar;
+ goos = gohostos;
+ goarch = gohostarch;
+ gochar = gohostchar;
+ xsetenv("GOARCH", goarch);
+ xsetenv("GOOS", goos);
+
+ for(i=0; i<nelem(buildorder); i++) {
+ install(bprintf(&b, buildorder[i], gohostchar));
+ if(!streq(oldgochar, gohostchar) && xstrstr(buildorder[i], "%s"))
+ install(bprintf(&b, buildorder[i], oldgochar));
+ }
+
+ goos = oldgoos;
+ goarch = oldgoarch;
+ gochar = oldgochar;
+ xsetenv("GOARCH", goarch);
+ xsetenv("GOOS", goos);
+
+ // Build pkg/runtime for actual goos/goarch too.
+ if(!streq(goos, gohostos) || !streq(goarch, gohostarch))
+ install("pkg/runtime");
+
+ bfree(&b);
+}
+
+static char*
+defaulttarg(void)
+{
+ char *p;
+ Buf pwd, src, real_src;
+
+ binit(&pwd);
+ binit(&src);
+ binit(&real_src);
+
+ // xgetwd might return a path with symlinks fully resolved, and if
+ // there happens to be symlinks in goroot, then the hasprefix test
+ // will never succeed. Instead, we use xrealwd to get a canonical
+ // goroot/src before the comparison to avoid this problem.
+ xgetwd(&pwd);
+ p = btake(&pwd);
+ bpathf(&src, "%s/src/", goroot);
+ xrealwd(&real_src, bstr(&src));
+ if(!hasprefix(p, bstr(&real_src)))
+ fatal("current directory %s is not under %s", p, bstr(&real_src));
+ p += real_src.len;
+ // guard againt xrealwd return the directory without the trailing /
+ if(*p == slash[0])
+ p++;
+
+ bfree(&pwd);
+ bfree(&src);
+ bfree(&real_src);
+
+ return p;
+}
+
+// Install installs the list of packages named on the command line.
+void
+cmdinstall(int argc, char **argv)
+{
+ int i;
+
+ ARGBEGIN{
+ case 'v':
+ vflag++;
+ break;
+ default:
+ usage();
+ }ARGEND
+
+ if(argc == 0)
+ install(defaulttarg());
+
+ for(i=0; i<argc; i++)
+ install(argv[i]);
+}
+
+// Clean deletes temporary objects.
+// Clean -i deletes the installed objects too.
+void
+cmdclean(int argc, char **argv)
+{
+ ARGBEGIN{
+ case 'v':
+ vflag++;
+ break;
+ default:
+ usage();
+ }ARGEND
+
+ if(argc > 0)
+ usage();
+
+ clean();
+}
+
+// Banner prints the 'now you've installed Go' banner.
+void
+cmdbanner(int argc, char **argv)
+{
+ char *pathsep;
+ Buf b, b1, search;
+
+ ARGBEGIN{
+ case 'v':
+ vflag++;
+ break;
+ default:
+ usage();
+ }ARGEND
+
+ if(argc > 0)
+ usage();
+
+ binit(&b);
+ binit(&b1);
+ binit(&search);
+
+ xprintf("\n");
+ xprintf("---\n");
+ xprintf("Installed Go for %s/%s in %s\n", goos, goarch, goroot);
+ xprintf("Installed commands in %s\n", gobin);
+
+ // Check that gobin appears in $PATH.
+ xgetenv(&b, "PATH");
+ pathsep = ":";
+ if(streq(gohostos, "windows"))
+ pathsep = ";";
+ bprintf(&b1, "%s%s%s", pathsep, bstr(&b), pathsep);
+ bprintf(&search, "%s%s%s", pathsep, gobin, pathsep);
+ if(xstrstr(bstr(&b1), bstr(&search)) == nil)
+ xprintf("*** You need to add %s to your PATH.\n", gobin);
+
+ if(streq(gohostos, "darwin")) {
+ xprintf("\n"
+ "On OS X the debuggers must be installed setgid procmod.\n"
+ "Read and run ./sudo.bash to install the debuggers.\n");
+ }
+
+ if(!streq(goroot_final, goroot)) {
+ xprintf("\n"
+ "The binaries expect %s to be copied or moved to %s\n",
+ goroot, goroot_final);
+ }
+
+ bfree(&b);
+ bfree(&b1);
+ bfree(&search);
+}
+
+// Version prints the Go version.
+void
+cmdversion(int argc, char **argv)
+{
+ ARGBEGIN{
+ case 'v':
+ vflag++;
+ break;
+ default:
+ usage();
+ }ARGEND
+
+ if(argc > 0)
+ usage();
+
+ xprintf("%s\n", goversion);
+}
diff --git a/src/cmd/dist/buildgc.c b/src/cmd/dist/buildgc.c
new file mode 100644
index 000000000..da38760c6
--- /dev/null
+++ b/src/cmd/dist/buildgc.c
@@ -0,0 +1,106 @@
+// 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"
+#include <stdio.h>
+
+/*
+ * Helpers for building cmd/gc.
+ */
+
+// gcopnames creates opnames.h from go.h.
+// It finds the OXXX enum, pulls out all the constants
+// from OXXX to OEND, and writes a table mapping
+// op to string.
+void
+gcopnames(char *dir, char *file)
+{
+ char *p, *q;
+ int i, j, end;
+ Buf in, b, out;
+ Vec lines, fields;
+
+ binit(&in);
+ binit(&b);
+ binit(&out);
+ vinit(&lines);
+ vinit(&fields);
+
+ bwritestr(&out, bprintf(&b, "// auto generated by go tool dist\n"));
+ bwritestr(&out, bprintf(&b, "static char *opnames[] = {\n"));
+
+ readfile(&in, bprintf(&b, "%s/go.h", dir));
+ splitlines(&lines, bstr(&in));
+ i = 0;
+ while(i<lines.len && !contains(lines.p[i], "OXXX"))
+ i++;
+ end = 0;
+ for(; i<lines.len && !end; i++) {
+ p = xstrstr(lines.p[i], "//");
+ if(p != nil)
+ *p = '\0';
+ end = contains(lines.p[i], "OEND");
+ splitfields(&fields, lines.p[i]);
+ for(j=0; j<fields.len; j++) {
+ q = fields.p[j];
+ if(*q == 'O')
+ q++;
+ p = q+xstrlen(q)-1;
+ if(*p == ',')
+ *p = '\0';
+ bwritestr(&out, bprintf(&b, " [O%s] = \"%s\",\n", q, q));
+ }
+ }
+
+ bwritestr(&out, bprintf(&b, "};\n"));
+
+ writefile(&out, file, 0);
+
+ bfree(&in);
+ bfree(&b);
+ bfree(&out);
+ vfree(&lines);
+ vfree(&fields);
+}
+
+// mkenam reads [568].out.h and writes enam.c
+// The format is much the same as the Go opcodes above.
+void
+mkenam(char *dir, char *file)
+{
+ int i, ch;
+ Buf in, b, out;
+ Vec lines;
+ char *p;
+
+ binit(&b);
+ binit(&in);
+ binit(&out);
+ vinit(&lines);
+
+ ch = dir[xstrlen(dir)-2];
+ bprintf(&b, "%s/../%cl/%c.out.h", dir, ch, ch);
+ readfile(&in, bstr(&b));
+ splitlines(&lines, bstr(&in));
+ bwritestr(&out, "char* anames[] = {\n");
+ for(i=0; i<lines.len; i++) {
+ if(hasprefix(lines.p[i], "\tA")) {
+ p = xstrstr(lines.p[i], ",");
+ if(p)
+ *p = '\0';
+ p = xstrstr(lines.p[i], "\n");
+ if(p)
+ *p = '\0';
+ p = lines.p[i] + 2;
+ bwritestr(&out, bprintf(&b, "\t\"%s\",\n", p));
+ }
+ }
+ bwritestr(&out, "};\n");
+ writefile(&out, file, 0);
+
+ bfree(&b);
+ bfree(&in);
+ bfree(&out);
+ vfree(&lines);
+}
diff --git a/src/cmd/dist/buildruntime.c b/src/cmd/dist/buildruntime.c
new file mode 100644
index 000000000..a0c62010d
--- /dev/null
+++ b/src/cmd/dist/buildruntime.c
@@ -0,0 +1,346 @@
+// 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"
+#include <stdio.h>
+
+/*
+ * Helpers for building pkg/runtime.
+ */
+
+// mkzversion writes zversion.go:
+//
+// package runtime
+// const defaultGoroot = <goroot>
+// const theVersion = <version>
+//
+void
+mkzversion(char *dir, char *file)
+{
+ Buf b, out;
+
+ binit(&b);
+ binit(&out);
+
+ bwritestr(&out, bprintf(&b,
+ "// auto generated by go tool dist\n"
+ "\n"
+ "package runtime\n"
+ "\n"
+ "const defaultGoroot = `%s`\n"
+ "const theVersion = `%s`\n", goroot_final, goversion));
+
+ writefile(&out, file, 0);
+
+ bfree(&b);
+ bfree(&out);
+}
+
+// mkzgoarch writes zgoarch_$GOARCH.go:
+//
+// package runtime
+// const theGoarch = <goarch>
+//
+void
+mkzgoarch(char *dir, char *file)
+{
+ Buf b, out;
+
+ binit(&b);
+ binit(&out);
+
+ bwritestr(&out, bprintf(&b,
+ "// auto generated by go tool dist\n"
+ "\n"
+ "package runtime\n"
+ "\n"
+ "const theGoarch = `%s`\n", goarch));
+
+ writefile(&out, file, 0);
+
+ bfree(&b);
+ bfree(&out);
+}
+
+// mkzgoos writes zgoos_$GOOS.go:
+//
+// package runtime
+// const theGoos = <goos>
+//
+void
+mkzgoos(char *dir, char *file)
+{
+ Buf b, out;
+
+ binit(&b);
+ binit(&out);
+
+ bwritestr(&out, bprintf(&b,
+ "// auto generated by go tool dist\n"
+ "\n"
+ "package runtime\n"
+ "\n"
+ "const theGoos = `%s`\n", goos));
+
+ writefile(&out, file, 0);
+
+ bfree(&b);
+ bfree(&out);
+}
+
+static struct {
+ char *goarch;
+ char *goos;
+ char *hdr;
+} zasmhdr[] = {
+ {"386", "windows",
+ "#define get_tls(r) MOVL 0x14(FS), r\n"
+ "#define g(r) 0(r)\n"
+ "#define m(r) 4(r)\n"
+ },
+ {"386", "plan9",
+ "#define get_tls(r) MOVL _tos(SB), r \n"
+ "#define g(r) -8(r)\n"
+ "#define m(r) -4(r)\n"
+ },
+ {"386", "linux",
+ "// On Linux systems, what we call 0(GS) and 4(GS) for g and m\n"
+ "// turn into %gs:-8 and %gs:-4 (using gcc syntax to denote\n"
+ "// what the machine sees as opposed to 8l input).\n"
+ "// 8l rewrites 0(GS) and 4(GS) into these.\n"
+ "//\n"
+ "// On Linux Xen, it is not allowed to use %gs:-8 and %gs:-4\n"
+ "// directly. Instead, we have to store %gs:0 into a temporary\n"
+ "// register and then use -8(%reg) and -4(%reg). This kind\n"
+ "// of addressing is correct even when not running Xen.\n"
+ "//\n"
+ "// 8l can rewrite MOVL 0(GS), CX into the appropriate pair\n"
+ "// of mov instructions, using CX as the intermediate register\n"
+ "// (safe because CX is about to be written to anyway).\n"
+ "// But 8l cannot handle other instructions, like storing into 0(GS),\n"
+ "// which is where these macros come into play.\n"
+ "// get_tls sets up the temporary and then g and r use it.\n"
+ "//\n"
+ "// The final wrinkle is that get_tls needs to read from %gs:0,\n"
+ "// but in 8l input it's called 8(GS), because 8l is going to\n"
+ "// subtract 8 from all the offsets, as described above.\n"
+ "#define get_tls(r) MOVL 8(GS), r\n"
+ "#define g(r) -8(r)\n"
+ "#define m(r) -4(r)\n"
+ },
+ {"386", "",
+ "#define get_tls(r)\n"
+ "#define g(r) 0(GS)\n"
+ "#define m(r) 4(GS)\n"
+ },
+
+ {"amd64", "windows",
+ "#define get_tls(r) MOVQ 0x28(GS), r\n"
+ "#define g(r) 0(r)\n"
+ "#define m(r) 8(r)\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"
+ },
+
+ {"arm", "",
+ "#define g R10\n"
+ "#define m R9\n"
+ "#define LR R14\n"
+ },
+};
+
+// mkzasm writes zasm_$GOOS_$GOARCH.h,
+// which contains struct offsets for use by
+// assembly files. It also writes a copy to the work space
+// under the name zasm_GOOS_GOARCH.h (no expansion).
+//
+void
+mkzasm(char *dir, char *file)
+{
+ int i, n;
+ char *aggr, *p;
+ Buf in, b, out;
+ Vec argv, lines, fields;
+
+ binit(&in);
+ binit(&b);
+ binit(&out);
+ vinit(&argv);
+ vinit(&lines);
+ vinit(&fields);
+
+ bwritestr(&out, "// auto generated by go tool dist\n\n");
+ for(i=0; i<nelem(zasmhdr); i++) {
+ if(hasprefix(goarch, zasmhdr[i].goarch) && hasprefix(goos, zasmhdr[i].goos)) {
+ bwritestr(&out, zasmhdr[i].hdr);
+ goto ok;
+ }
+ }
+ fatal("unknown $GOOS/$GOARCH in mkzasm");
+ok:
+
+ // Run 6c -DGOOS_goos -DGOARCH_goarch -Iworkdir -a proc.c
+ // to get acid [sic] output.
+ vreset(&argv);
+ vadd(&argv, bpathf(&b, "%s/%sc", tooldir, gochar));
+ vadd(&argv, bprintf(&b, "-DGOOS_%s", goos));
+ vadd(&argv, bprintf(&b, "-DGOARCH_%s", goarch));
+ vadd(&argv, bprintf(&b, "-I%s", workdir));
+ vadd(&argv, "-a");
+ vadd(&argv, "proc.c");
+ runv(&in, dir, CheckExit, &argv);
+
+ // Convert input like
+ // aggr G
+ // {
+ // Gobuf 24 sched;
+ // 'Y' 48 stack0;
+ // }
+ // into output like
+ // #define g_sched 24
+ // #define g_stack0 48
+ //
+ aggr = nil;
+ splitlines(&lines, bstr(&in));
+ for(i=0; i<lines.len; i++) {
+ splitfields(&fields, lines.p[i]);
+ if(fields.len == 2 && streq(fields.p[0], "aggr")) {
+ if(streq(fields.p[1], "G"))
+ aggr = "g";
+ else if(streq(fields.p[1], "M"))
+ aggr = "m";
+ else if(streq(fields.p[1], "Gobuf"))
+ aggr = "gobuf";
+ else if(streq(fields.p[1], "WinCall"))
+ aggr = "wincall";
+ }
+ if(hasprefix(lines.p[i], "}"))
+ aggr = nil;
+ if(aggr && hasprefix(lines.p[i], "\t") && fields.len >= 2) {
+ n = fields.len;
+ p = fields.p[n-1];
+ if(p[xstrlen(p)-1] == ';')
+ p[xstrlen(p)-1] = '\0';
+ bwritestr(&out, bprintf(&b, "#define %s_%s %s\n", aggr, fields.p[n-1], fields.p[n-2]));
+ }
+ }
+
+ // 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);
+
+ bfree(&in);
+ bfree(&b);
+ bfree(&out);
+ vfree(&argv);
+ vfree(&lines);
+ vfree(&fields);
+}
+
+static char *runtimedefs[] = {
+ "proc.c",
+ "iface.c",
+ "hashmap.c",
+ "chan.c",
+};
+
+// mkzruntimedefs writes zruntime_defs_$GOOS_$GOARCH.h,
+// which contains Go struct definitions equivalent to the C ones.
+// Mostly we just write the output of 6c -q to the file.
+// However, we run it on multiple files, so we have to delete
+// the duplicated definitions, and we don't care about the funcs
+// and consts, so we delete those too.
+//
+void
+mkzruntimedefs(char *dir, char *file)
+{
+ int i, skip;
+ char *p;
+ Buf in, b, out;
+ Vec argv, lines, fields, seen;
+
+ binit(&in);
+ binit(&b);
+ binit(&out);
+ vinit(&argv);
+ vinit(&lines);
+ vinit(&fields);
+ vinit(&seen);
+
+ bwritestr(&out, "// auto generated by go tool dist\n"
+ "\n"
+ "package runtime\n"
+ "import \"unsafe\"\n"
+ "var _ unsafe.Pointer\n"
+ "\n"
+ );
+
+
+ // Run 6c -DGOOS_goos -DGOARCH_goarch -Iworkdir -q
+ // on each of the runtimedefs C files.
+ vadd(&argv, bpathf(&b, "%s/%sc", tooldir, gochar));
+ vadd(&argv, bprintf(&b, "-DGOOS_%s", goos));
+ vadd(&argv, bprintf(&b, "-DGOARCH_%s", goarch));
+ vadd(&argv, bprintf(&b, "-I%s", workdir));
+ vadd(&argv, "-q");
+ vadd(&argv, "");
+ p = argv.p[argv.len-1];
+ for(i=0; i<nelem(runtimedefs); i++) {
+ argv.p[argv.len-1] = runtimedefs[i];
+ runv(&b, dir, CheckExit, &argv);
+ bwriteb(&in, &b);
+ }
+ argv.p[argv.len-1] = p;
+
+ // Process the aggregate output.
+ skip = 0;
+ splitlines(&lines, bstr(&in));
+ for(i=0; i<lines.len; i++) {
+ p = lines.p[i];
+ // Drop comment, func, and const lines.
+ if(hasprefix(p, "//") || hasprefix(p, "const") || hasprefix(p, "func"))
+ continue;
+
+ // Note beginning of type or var decl, which can be multiline.
+ // Remove duplicates. The linear check of seen here makes the
+ // whole processing quadratic in aggregate, but there are only
+ // about 100 declarations, so this is okay (and simple).
+ if(hasprefix(p, "type ") || hasprefix(p, "var ")) {
+ splitfields(&fields, p);
+ if(fields.len < 2)
+ continue;
+ if(find(fields.p[1], seen.p, seen.len) >= 0) {
+ if(streq(fields.p[fields.len-1], "{"))
+ skip = 1; // skip until }
+ continue;
+ }
+ vadd(&seen, fields.p[1]);
+ }
+ if(skip) {
+ if(hasprefix(p, "}"))
+ skip = 0;
+ continue;
+ }
+
+ bwritestr(&out, p);
+ }
+
+ writefile(&out, file, 0);
+
+ bfree(&in);
+ bfree(&b);
+ bfree(&out);
+ vfree(&argv);
+ vfree(&lines);
+ vfree(&fields);
+ vfree(&seen);
+}
diff --git a/src/cmd/dist/goc2c.c b/src/cmd/dist/goc2c.c
new file mode 100644
index 000000000..22f72f8b5
--- /dev/null
+++ b/src/cmd/dist/goc2c.c
@@ -0,0 +1,735 @@
+// 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 "a.h"
+
+/*
+ * Translate a .goc file into a .c file. A .goc file is a combination
+ * of a limited form of Go with C.
+ */
+
+/*
+ package PACKAGENAME
+ {# line}
+ func NAME([NAME TYPE { , NAME TYPE }]) [(NAME TYPE { , NAME TYPE })] \{
+ C code with proper brace nesting
+ \}
+*/
+
+/*
+ * We generate C code which implements the function such that it can
+ * be called from Go and executes the C code.
+ */
+
+static char *input;
+static Buf *output;
+#define EOF -1
+
+static int
+xgetchar(void)
+{
+ int c;
+
+ c = *input;
+ if(c == 0)
+ return EOF;
+ input++;
+ return c;
+}
+
+static void
+xungetc(void)
+{
+ input--;
+}
+
+static void
+xputchar(char c)
+{
+ bwrite(output, &c, 1);
+}
+
+static int
+xisspace(int c)
+{
+ return c == ' ' || c == '\t' || c == '\r' || c == '\n';
+}
+
+/* Whether we're emitting for gcc */
+static int gcc;
+
+/* File and line number */
+static const char *file;
+static unsigned int lineno = 1;
+
+/* List of names and types. */
+struct params {
+ struct params *next;
+ char *name;
+ char *type;
+};
+
+/* index into type_table */
+enum {
+ Bool,
+ Float,
+ Int,
+ Uint,
+ Uintptr,
+ String,
+ Slice,
+ Eface,
+};
+
+static struct {
+ char *name;
+ int size;
+} type_table[] = {
+ /* variable sized first, for easy replacement */
+ /* order matches enum above */
+ /* default is 32-bit architecture sizes */
+ {"bool", 1},
+ {"float", 4},
+ {"int", 4},
+ {"uint", 4},
+ {"uintptr", 4},
+ {"String", 8},
+ {"Slice", 12},
+ {"Eface", 8},
+
+ /* fixed size */
+ {"float32", 4},
+ {"float64", 8},
+ {"byte", 1},
+ {"int8", 1},
+ {"uint8", 1},
+ {"int16", 2},
+ {"uint16", 2},
+ {"int32", 4},
+ {"uint32", 4},
+ {"int64", 8},
+ {"uint64", 8},
+
+ {nil},
+};
+
+/* Fixed structure alignment (non-gcc only) */
+int structround = 4;
+
+/* Unexpected EOF. */
+static void
+bad_eof(void)
+{
+ fatal("%s:%ud: unexpected EOF\n", file, lineno);
+}
+
+/* Free a list of parameters. */
+static void
+free_params(struct params *p)
+{
+ while (p != nil) {
+ struct params *next;
+
+ next = p->next;
+ xfree(p->name);
+ xfree(p->type);
+ xfree(p);
+ p = next;
+ }
+}
+
+/* Read a character, tracking lineno. */
+static int
+getchar_update_lineno(void)
+{
+ int c;
+
+ c = xgetchar();
+ if (c == '\n')
+ ++lineno;
+ return c;
+}
+
+/* Read a character, giving an error on EOF, tracking lineno. */
+static int
+getchar_no_eof(void)
+{
+ int c;
+
+ c = getchar_update_lineno();
+ if (c == EOF)
+ bad_eof();
+ return c;
+}
+
+/* Read a character, skipping comments. */
+static int
+getchar_skipping_comments(void)
+{
+ int c;
+
+ while (1) {
+ c = getchar_update_lineno();
+ if (c != '/')
+ return c;
+
+ c = xgetchar();
+ if (c == '/') {
+ do {
+ c = getchar_update_lineno();
+ } while (c != EOF && c != '\n');
+ return c;
+ } else if (c == '*') {
+ while (1) {
+ c = getchar_update_lineno();
+ if (c == EOF)
+ return EOF;
+ if (c == '*') {
+ do {
+ c = getchar_update_lineno();
+ } while (c == '*');
+ if (c == '/')
+ break;
+ }
+ }
+ } else {
+ xungetc();
+ return '/';
+ }
+ }
+}
+
+/*
+ * Read and return a token. Tokens are string or character literals
+ * or else delimited by whitespace or by [(),{}].
+ * The latter are all returned as single characters.
+ */
+static char *
+read_token(void)
+{
+ int c, q;
+ char *buf;
+ unsigned int alc, off;
+ char* delims = "(),{}";
+
+ while (1) {
+ c = getchar_skipping_comments();
+ if (c == EOF)
+ return nil;
+ if (!xisspace(c))
+ break;
+ }
+ alc = 16;
+ buf = xmalloc(alc + 1);
+ off = 0;
+ if(c == '"' || c == '\'') {
+ q = c;
+ buf[off] = c;
+ ++off;
+ while (1) {
+ if (off+2 >= alc) { // room for c and maybe next char
+ alc *= 2;
+ buf = xrealloc(buf, alc + 1);
+ }
+ c = getchar_no_eof();
+ buf[off] = c;
+ ++off;
+ if(c == q)
+ break;
+ if(c == '\\') {
+ buf[off] = getchar_no_eof();
+ ++off;
+ }
+ }
+ } else if (xstrrchr(delims, c) != nil) {
+ buf[off] = c;
+ ++off;
+ } else {
+ while (1) {
+ if (off >= alc) {
+ alc *= 2;
+ buf = xrealloc(buf, alc + 1);
+ }
+ buf[off] = c;
+ ++off;
+ c = getchar_skipping_comments();
+ if (c == EOF)
+ break;
+ if (xisspace(c) || xstrrchr(delims, c) != nil) {
+ if (c == '\n')
+ lineno--;
+ xungetc();
+ break;
+ }
+ }
+ }
+ buf[off] = '\0';
+ return buf;
+}
+
+/* Read a token, giving an error on EOF. */
+static char *
+read_token_no_eof(void)
+{
+ char *token = read_token();
+ if (token == nil)
+ bad_eof();
+ return token;
+}
+
+/* Read the package clause, and return the package name. */
+static char *
+read_package(void)
+{
+ char *token;
+
+ token = read_token_no_eof();
+ if (token == nil)
+ fatal("%s:%ud: no token\n", file, lineno);
+ if (!streq(token, "package")) {
+ fatal("%s:%ud: expected \"package\", got \"%s\"\n",
+ file, lineno, token);
+ }
+ return read_token_no_eof();
+}
+
+/* Read and copy preprocessor lines. */
+static void
+read_preprocessor_lines(void)
+{
+ while (1) {
+ int c;
+
+ do {
+ c = getchar_skipping_comments();
+ } while (xisspace(c));
+ if (c != '#') {
+ xungetc();
+ break;
+ }
+ xputchar(c);
+ do {
+ c = getchar_update_lineno();
+ xputchar(c);
+ } while (c != '\n');
+ }
+}
+
+/*
+ * Read a type in Go syntax and return a type in C syntax. We only
+ * permit basic types and pointers.
+ */
+static char *
+read_type(void)
+{
+ char *p, *op, *q;
+ int pointer_count;
+ unsigned int len;
+
+ p = read_token_no_eof();
+ if (*p != '*')
+ return p;
+ op = p;
+ pointer_count = 0;
+ while (*p == '*') {
+ ++pointer_count;
+ ++p;
+ }
+ len = xstrlen(p);
+ q = xmalloc(len + pointer_count + 1);
+ xmemmove(q, p, len);
+ while (pointer_count > 0) {
+ q[len] = '*';
+ ++len;
+ --pointer_count;
+ }
+ q[len] = '\0';
+ xfree(op);
+ return q;
+}
+
+/* Return the size of the given type. */
+static int
+type_size(char *p)
+{
+ int i;
+
+ if(p[xstrlen(p)-1] == '*')
+ return type_table[Uintptr].size;
+
+ for(i=0; type_table[i].name; i++)
+ if(streq(type_table[i].name, p))
+ return type_table[i].size;
+ fatal("%s:%ud: unknown type %s\n", file, lineno, p);
+ return 0;
+}
+
+/*
+ * Read a list of parameters. Each parameter is a name and a type.
+ * The list ends with a ')'. We have already read the '('.
+ */
+static struct params *
+read_params(int *poffset)
+{
+ char *token;
+ struct params *ret, **pp, *p;
+ int offset, size, rnd;
+
+ ret = nil;
+ pp = &ret;
+ token = read_token_no_eof();
+ offset = 0;
+ if (!streq(token, ")")) {
+ while (1) {
+ p = xmalloc(sizeof(struct params));
+ p->name = token;
+ p->type = read_type();
+ p->next = nil;
+ *pp = p;
+ pp = &p->next;
+
+ size = type_size(p->type);
+ rnd = size;
+ if(rnd > structround)
+ rnd = structround;
+ if(offset%rnd)
+ offset += rnd - offset%rnd;
+ offset += size;
+
+ token = read_token_no_eof();
+ if (!streq(token, ","))
+ break;
+ token = read_token_no_eof();
+ }
+ }
+ if (!streq(token, ")")) {
+ fatal("%s:%ud: expected '('\n",
+ file, lineno);
+ }
+ if (poffset != nil)
+ *poffset = offset;
+ return ret;
+}
+
+/*
+ * Read a function header. This reads up to and including the initial
+ * '{' character. Returns 1 if it read a header, 0 at EOF.
+ */
+static int
+read_func_header(char **name, struct params **params, int *paramwid, struct params **rets)
+{
+ int lastline;
+ char *token;
+
+ lastline = -1;
+ while (1) {
+ token = read_token();
+ if (token == nil)
+ return 0;
+ if (streq(token, "func")) {
+ if(lastline != -1)
+ bwritef(output, "\n");
+ break;
+ }
+ if (lastline != lineno) {
+ if (lastline == lineno-1)
+ bwritef(output, "\n");
+ else
+ bwritef(output, "\n#line %d \"%s\"\n", lineno, file);
+ lastline = lineno;
+ }
+ bwritef(output, "%s ", token);
+ }
+
+ *name = read_token_no_eof();
+
+ token = read_token();
+ if (token == nil || !streq(token, "(")) {
+ fatal("%s:%ud: expected \"(\"\n",
+ file, lineno);
+ }
+ *params = read_params(paramwid);
+
+ token = read_token();
+ if (token == nil || !streq(token, "("))
+ *rets = nil;
+ else {
+ *rets = read_params(nil);
+ token = read_token();
+ }
+ if (token == nil || !streq(token, "{")) {
+ fatal("%s:%ud: expected \"{\"\n",
+ file, lineno);
+ }
+ return 1;
+}
+
+/* Write out parameters. */
+static void
+write_params(struct params *params, int *first)
+{
+ struct params *p;
+
+ for (p = params; p != nil; p = p->next) {
+ if (*first)
+ *first = 0;
+ else
+ bwritef(output, ", ");
+ bwritef(output, "%s %s", p->type, p->name);
+ }
+}
+
+/* Write a 6g function header. */
+static void
+write_6g_func_header(char *package, char *name, struct params *params,
+ int paramwid, struct params *rets)
+{
+ int first, n;
+
+ bwritef(output, "void\n%s·%s(", package, name);
+ first = 1;
+ write_params(params, &first);
+
+ /* insert padding to align output struct */
+ if(rets != nil && paramwid%structround != 0) {
+ n = structround - paramwid%structround;
+ if(n & 1)
+ bwritef(output, ", uint8");
+ if(n & 2)
+ bwritef(output, ", uint16");
+ if(n & 4)
+ bwritef(output, ", uint32");
+ }
+
+ write_params(rets, &first);
+ bwritef(output, ")\n{\n");
+}
+
+/* Write a 6g function trailer. */
+static void
+write_6g_func_trailer(struct params *rets)
+{
+ struct params *p;
+
+ for (p = rets; p != nil; p = p->next)
+ bwritef(output, "\tFLUSH(&%s);\n", p->name);
+ bwritef(output, "}\n");
+}
+
+/* Define the gcc function return type if necessary. */
+static void
+define_gcc_return_type(char *package, char *name, struct params *rets)
+{
+ struct params *p;
+
+ if (rets == nil || rets->next == nil)
+ return;
+ bwritef(output, "struct %s_%s_ret {\n", package, name);
+ for (p = rets; p != nil; p = p->next)
+ bwritef(output, " %s %s;\n", p->type, p->name);
+ bwritef(output, "};\n");
+}
+
+/* Write out the gcc function return type. */
+static void
+write_gcc_return_type(char *package, char *name, struct params *rets)
+{
+ if (rets == nil)
+ bwritef(output, "void");
+ else if (rets->next == nil)
+ bwritef(output, "%s", rets->type);
+ else
+ bwritef(output, "struct %s_%s_ret", package, name);
+}
+
+/* Write out a gcc function header. */
+static void
+write_gcc_func_header(char *package, char *name, struct params *params,
+ struct params *rets)
+{
+ int first;
+ struct params *p;
+
+ define_gcc_return_type(package, name, rets);
+ write_gcc_return_type(package, name, rets);
+ bwritef(output, " %s_%s(", package, name);
+ first = 1;
+ write_params(params, &first);
+ bwritef(output, ") asm (\"%s.%s\");\n", package, name);
+ write_gcc_return_type(package, name, rets);
+ bwritef(output, " %s_%s(", package, name);
+ first = 1;
+ write_params(params, &first);
+ bwritef(output, ")\n{\n");
+ for (p = rets; p != nil; p = p->next)
+ bwritef(output, " %s %s;\n", p->type, p->name);
+}
+
+/* Write out a gcc function trailer. */
+static void
+write_gcc_func_trailer(char *package, char *name, struct params *rets)
+{
+ if (rets == nil)
+ ;
+ else if (rets->next == nil)
+ bwritef(output, "return %s;\n", rets->name);
+ else {
+ struct params *p;
+
+ bwritef(output, " {\n struct %s_%s_ret __ret;\n", package, name);
+ for (p = rets; p != nil; p = p->next)
+ bwritef(output, " __ret.%s = %s;\n", p->name, p->name);
+ bwritef(output, " return __ret;\n }\n");
+ }
+ bwritef(output, "}\n");
+}
+
+/* Write out a function header. */
+static void
+write_func_header(char *package, char *name,
+ struct params *params, int paramwid,
+ struct params *rets)
+{
+ if (gcc)
+ write_gcc_func_header(package, name, params, rets);
+ else
+ write_6g_func_header(package, name, params, paramwid, rets);
+ bwritef(output, "#line %d \"%s\"\n", lineno, file);
+}
+
+/* Write out a function trailer. */
+static void
+write_func_trailer(char *package, char *name,
+ struct params *rets)
+{
+ if (gcc)
+ write_gcc_func_trailer(package, name, rets);
+ else
+ write_6g_func_trailer(rets);
+}
+
+/*
+ * Read and write the body of the function, ending in an unnested }
+ * (which is read but not written).
+ */
+static void
+copy_body(void)
+{
+ int nesting = 0;
+ while (1) {
+ int c;
+
+ c = getchar_no_eof();
+ if (c == '}' && nesting == 0)
+ return;
+ xputchar(c);
+ switch (c) {
+ default:
+ break;
+ case '{':
+ ++nesting;
+ break;
+ case '}':
+ --nesting;
+ break;
+ case '/':
+ c = getchar_update_lineno();
+ xputchar(c);
+ if (c == '/') {
+ do {
+ c = getchar_no_eof();
+ xputchar(c);
+ } while (c != '\n');
+ } else if (c == '*') {
+ while (1) {
+ c = getchar_no_eof();
+ xputchar(c);
+ if (c == '*') {
+ do {
+ c = getchar_no_eof();
+ xputchar(c);
+ } while (c == '*');
+ if (c == '/')
+ break;
+ }
+ }
+ }
+ break;
+ case '"':
+ case '\'':
+ {
+ int delim = c;
+ do {
+ c = getchar_no_eof();
+ xputchar(c);
+ if (c == '\\') {
+ c = getchar_no_eof();
+ xputchar(c);
+ c = '\0';
+ }
+ } while (c != delim);
+ }
+ break;
+ }
+ }
+}
+
+/* Process the entire file. */
+static void
+process_file(void)
+{
+ char *package, *name;
+ 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);
+ copy_body();
+ write_func_trailer(package, name, rets);
+ xfree(name);
+ free_params(params);
+ free_params(rets);
+ }
+ xfree(package);
+}
+
+void
+goc2c(char *goc, char *c)
+{
+ Buf in, out;
+
+ binit(&in);
+ binit(&out);
+
+ file = goc;
+ readfile(&in, goc);
+
+ // TODO: set gcc=1 when using gcc
+
+ if(!gcc) {
+ if(streq(goarch, "amd64")) {
+ type_table[Uintptr].size = 8;
+ type_table[String].size = 16;
+ type_table[Slice].size = 8+4+4;
+ type_table[Eface].size = 8+8;
+ structround = 8;
+ } else {
+ type_table[Uintptr].size = 4;
+ type_table[String].size = 8;
+ type_table[Slice].size = 16;
+ type_table[Eface].size = 4+4;
+ structround = 4;
+ }
+ }
+
+ bprintf(&out, "// auto generated by go tool dist\n// goos=%s goarch=%s\n\n", goos, goarch);
+ input = bstr(&in);
+ output = &out;
+
+ process_file();
+
+ writefile(&out, c, 0);
+}
diff --git a/src/cmd/dist/main.c b/src/cmd/dist/main.c
new file mode 100644
index 000000000..72a7579d1
--- /dev/null
+++ b/src/cmd/dist/main.c
@@ -0,0 +1,41 @@
+// 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"
+
+int vflag;
+char *argv0;
+
+// cmdtab records the available commands.
+static struct {
+ char *name;
+ void (*f)(int, char**);
+} cmdtab[] = {
+ {"banner", cmdbanner},
+ {"bootstrap", cmdbootstrap},
+ {"clean", cmdclean},
+ {"env", cmdenv},
+ {"install", cmdinstall},
+ {"version", cmdversion},
+};
+
+// The OS-specific main calls into the portable code here.
+void
+xmain(int argc, char **argv)
+{
+ int i;
+
+ if(argc <= 1)
+ usage();
+
+ for(i=0; i<nelem(cmdtab); i++) {
+ if(streq(cmdtab[i].name, argv[1])) {
+ cmdtab[i].f(argc-1, argv+1);
+ return;
+ }
+ }
+
+ xprintf("unknown command %s\n", argv[1]);
+ usage();
+}
diff --git a/src/cmd/dist/unix.c b/src/cmd/dist/unix.c
new file mode 100644
index 000000000..e6d82e14e
--- /dev/null
+++ b/src/cmd/dist/unix.c
@@ -0,0 +1,720 @@
+// 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.
+
+// These #ifdefs are being used as a substitute for
+// build configuration, so that on any system, this
+// tool can be built with the local equivalent of
+// cc *.c
+//
+#ifndef WIN32
+#ifndef PLAN9
+
+#include "a.h"
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/param.h>
+#include <sys/utsname.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdarg.h>
+
+// bprintf replaces the buffer with the result of the printf formatting
+// and returns a pointer to the NUL-terminated buffer contents.
+char*
+bprintf(Buf *b, char *fmt, ...)
+{
+ va_list arg;
+ char buf[4096];
+
+ breset(b);
+ va_start(arg, fmt);
+ vsnprintf(buf, sizeof buf, fmt, arg);
+ va_end(arg);
+ bwritestr(b, buf);
+ return bstr(b);
+}
+
+// bpathf is the same as bprintf (on windows it turns / into \ after the printf).
+// It returns a pointer to the NUL-terminated buffer contents.
+char*
+bpathf(Buf *b, char *fmt, ...)
+{
+ va_list arg;
+ char buf[4096];
+
+ breset(b);
+ va_start(arg, fmt);
+ vsnprintf(buf, sizeof buf, fmt, arg);
+ va_end(arg);
+ bwritestr(b, buf);
+ return bstr(b);
+}
+
+// bwritef is like bprintf but does not reset the buffer
+// and does not return the NUL-terminated string.
+void
+bwritef(Buf *b, char *fmt, ...)
+{
+ va_list arg;
+ char buf[4096];
+
+ va_start(arg, fmt);
+ vsnprintf(buf, sizeof buf, fmt, arg);
+ va_end(arg);
+ bwritestr(b, buf);
+}
+
+// breadfrom appends to b all the data that can be read from fd.
+static void
+breadfrom(Buf *b, int fd)
+{
+ int n;
+
+ for(;;) {
+ bgrow(b, 4096);
+ n = read(fd, b->p+b->len, 4096);
+ if(n < 0)
+ fatal("read: %s", strerror(errno));
+ if(n == 0)
+ break;
+ b->len += n;
+ }
+}
+
+// xgetenv replaces b with the value of the named environment variable.
+void
+xgetenv(Buf *b, char *name)
+{
+ char *p;
+
+ breset(b);
+ p = getenv(name);
+ if(p != NULL)
+ bwritestr(b, p);
+}
+
+static void genrun(Buf *b, char *dir, int mode, Vec *argv, int bg);
+
+// run runs the command named by cmd.
+// If b is not nil, run replaces b with the output of the command.
+// If dir is not nil, run runs the command in that directory.
+// If mode is CheckExit, run calls fatal if the command is not successful.
+void
+run(Buf *b, char *dir, int mode, char *cmd, ...)
+{
+ va_list arg;
+ Vec argv;
+ char *p;
+
+ vinit(&argv);
+ vadd(&argv, cmd);
+ va_start(arg, cmd);
+ while((p = va_arg(arg, char*)) != nil)
+ vadd(&argv, p);
+ va_end(arg);
+
+ runv(b, dir, mode, &argv);
+
+ vfree(&argv);
+}
+
+// runv is like run but takes a vector.
+void
+runv(Buf *b, char *dir, int mode, Vec *argv)
+{
+ genrun(b, dir, mode, argv, 1);
+}
+
+// bgrunv is like run but runs the command in the background.
+// bgwait waits for pending bgrunv to finish.
+void
+bgrunv(char *dir, int mode, Vec *argv)
+{
+ genrun(nil, dir, mode, argv, 0);
+}
+
+#define MAXBG 4 /* maximum number of jobs to run at once */
+
+static struct {
+ int pid;
+ int mode;
+ char *cmd;
+ Buf *b;
+} bg[MAXBG];
+static int nbg;
+static int maxnbg = nelem(bg);
+
+static void bgwait1(void);
+
+// genrun is the generic run implementation.
+static void
+genrun(Buf *b, char *dir, int mode, Vec *argv, int wait)
+{
+ int i, p[2], pid;
+ Buf cmd;
+ char *q;
+
+ while(nbg >= maxnbg)
+ bgwait1();
+
+ // Generate a copy of the command to show in a log.
+ // Substitute $WORK for the work directory.
+ binit(&cmd);
+ for(i=0; i<argv->len; i++) {
+ if(i > 0)
+ bwritestr(&cmd, " ");
+ q = argv->p[i];
+ if(workdir != nil && hasprefix(q, workdir)) {
+ bwritestr(&cmd, "$WORK");
+ q += strlen(workdir);
+ }
+ bwritestr(&cmd, q);
+ }
+ if(vflag > 1)
+ xprintf("%s\n", bstr(&cmd));
+
+ if(b != nil) {
+ breset(b);
+ if(pipe(p) < 0)
+ fatal("pipe: %s", strerror(errno));
+ }
+
+ switch(pid = fork()) {
+ case -1:
+ fatal("fork: %s", strerror(errno));
+ case 0:
+ if(b != nil) {
+ close(0);
+ close(p[0]);
+ dup2(p[1], 1);
+ dup2(p[1], 2);
+ if(p[1] > 2)
+ close(p[1]);
+ }
+ if(dir != nil) {
+ if(chdir(dir) < 0) {
+ fprintf(stderr, "chdir %s: %s\n", dir, strerror(errno));
+ _exit(1);
+ }
+ }
+ vadd(argv, nil);
+ execvp(argv->p[0], argv->p);
+ fprintf(stderr, "%s\n", bstr(&cmd));
+ fprintf(stderr, "exec %s: %s\n", argv->p[0], strerror(errno));
+ _exit(1);
+ }
+ if(b != nil) {
+ close(p[1]);
+ breadfrom(b, p[0]);
+ close(p[0]);
+ }
+
+ if(nbg < 0)
+ fatal("bad bookkeeping");
+ bg[nbg].pid = pid;
+ bg[nbg].mode = mode;
+ bg[nbg].cmd = btake(&cmd);
+ bg[nbg].b = b;
+ nbg++;
+
+ if(wait)
+ bgwait();
+
+ bfree(&cmd);
+}
+
+// bgwait1 waits for a single background job.
+static void
+bgwait1(void)
+{
+ int i, pid, status, mode;
+ char *cmd;
+ Buf *b;
+
+ errno = 0;
+ while((pid = wait(&status)) < 0) {
+ if(errno != EINTR)
+ fatal("waitpid: %s", strerror(errno));
+ }
+ for(i=0; i<nbg; i++)
+ if(bg[i].pid == pid)
+ goto ok;
+ fatal("waitpid: unexpected pid");
+
+ok:
+ cmd = bg[i].cmd;
+ mode = bg[i].mode;
+ bg[i].pid = 0;
+ b = bg[i].b;
+ bg[i].b = nil;
+ bg[i] = bg[--nbg];
+
+ if(mode == CheckExit && (!WIFEXITED(status) || WEXITSTATUS(status) != 0)) {
+ if(b != nil)
+ xprintf("%s\n", bstr(b));
+ fatal("FAILED: %s", cmd);
+ }
+ xfree(cmd);
+}
+
+// bgwait waits for all the background jobs.
+void
+bgwait(void)
+{
+ while(nbg > 0)
+ bgwait1();
+}
+
+// xgetwd replaces b with the current directory.
+void
+xgetwd(Buf *b)
+{
+ char buf[MAXPATHLEN];
+
+ breset(b);
+ if(getcwd(buf, MAXPATHLEN) == nil)
+ fatal("getcwd: %s", strerror(errno));
+ bwritestr(b, buf);
+}
+
+// xrealwd replaces b with the 'real' name for the given path.
+// real is defined as what getcwd returns in that directory.
+void
+xrealwd(Buf *b, char *path)
+{
+ int fd;
+
+ fd = open(".", 0);
+ if(fd < 0)
+ fatal("open .: %s", strerror(errno));
+ if(chdir(path) < 0)
+ fatal("chdir %s: %s", path, strerror(errno));
+ xgetwd(b);
+ if(fchdir(fd) < 0)
+ fatal("fchdir: %s", strerror(errno));
+ close(fd);
+}
+
+// isdir reports whether p names an existing directory.
+bool
+isdir(char *p)
+{
+ struct stat st;
+
+ return stat(p, &st) >= 0 && S_ISDIR(st.st_mode);
+}
+
+// isfile reports whether p names an existing file.
+bool
+isfile(char *p)
+{
+ struct stat st;
+
+ return stat(p, &st) >= 0 && S_ISREG(st.st_mode);
+}
+
+// mtime returns the modification time of the file p.
+Time
+mtime(char *p)
+{
+ struct stat st;
+
+ if(stat(p, &st) < 0)
+ return 0;
+ return (Time)st.st_mtime*1000000000LL;
+}
+
+// isabs reports whether p is an absolute path.
+bool
+isabs(char *p)
+{
+ return hasprefix(p, "/");
+}
+
+// readfile replaces b with the content of the named file.
+void
+readfile(Buf *b, char *file)
+{
+ int fd;
+
+ breset(b);
+ fd = open(file, 0);
+ if(fd < 0)
+ fatal("open %s: %s", file, strerror(errno));
+ breadfrom(b, fd);
+ close(fd);
+}
+
+// writefile writes b to the named file, creating it if needed. if
+// exec is non-zero, marks the file as executable.
+void
+writefile(Buf *b, char *file, int exec)
+{
+ int fd;
+
+ fd = creat(file, 0666);
+ if(fd < 0)
+ fatal("create %s: %s", file, strerror(errno));
+ if(write(fd, b->p, b->len) != b->len)
+ fatal("short write: %s", strerror(errno));
+ if(exec)
+ fchmod(fd, 0755);
+ close(fd);
+}
+
+// xmkdir creates the directory p.
+void
+xmkdir(char *p)
+{
+ if(mkdir(p, 0777) < 0)
+ fatal("mkdir %s: %s", p, strerror(errno));
+}
+
+// xmkdirall creates the directory p and its parents, as needed.
+void
+xmkdirall(char *p)
+{
+ char *q;
+
+ if(isdir(p))
+ return;
+ q = strrchr(p, '/');
+ if(q != nil) {
+ *q = '\0';
+ xmkdirall(p);
+ *q = '/';
+ }
+ xmkdir(p);
+}
+
+// xremove removes the file p.
+void
+xremove(char *p)
+{
+ if(vflag > 2)
+ xprintf("rm %s\n", p);
+ unlink(p);
+}
+
+// xremoveall removes the file or directory tree rooted at p.
+void
+xremoveall(char *p)
+{
+ int i;
+ Buf b;
+ Vec dir;
+
+ binit(&b);
+ vinit(&dir);
+
+ if(isdir(p)) {
+ xreaddir(&dir, p);
+ for(i=0; i<dir.len; i++) {
+ bprintf(&b, "%s/%s", p, dir.p[i]);
+ xremoveall(bstr(&b));
+ }
+ if(vflag > 2)
+ xprintf("rm %s\n", p);
+ rmdir(p);
+ } else {
+ if(vflag > 2)
+ xprintf("rm %s\n", p);
+ unlink(p);
+ }
+
+ bfree(&b);
+ vfree(&dir);
+}
+
+// xreaddir replaces dst with a list of the names of the files in dir.
+// The names are relative to dir; they are not full paths.
+void
+xreaddir(Vec *dst, char *dir)
+{
+ DIR *d;
+ struct dirent *dp;
+
+ vreset(dst);
+ d = opendir(dir);
+ if(d == nil)
+ fatal("opendir %s: %s", dir, strerror(errno));
+ while((dp = readdir(d)) != nil) {
+ if(strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)
+ continue;
+ vadd(dst, dp->d_name);
+ }
+ closedir(d);
+}
+
+// xworkdir creates a new temporary directory to hold object files
+// and returns the name of that directory.
+char*
+xworkdir(void)
+{
+ Buf b;
+ char *p;
+
+ binit(&b);
+
+ 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));
+ p = btake(&b);
+
+ bfree(&b);
+
+ return p;
+}
+
+// fatal prints an error message to standard error and exits.
+void
+fatal(char *msg, ...)
+{
+ va_list arg;
+
+ fflush(stdout);
+ fprintf(stderr, "go tool dist: ");
+ va_start(arg, msg);
+ vfprintf(stderr, msg, arg);
+ va_end(arg);
+ fprintf(stderr, "\n");
+
+ bgwait();
+ exit(1);
+}
+
+// xmalloc returns a newly allocated zeroed block of n bytes of memory.
+// It calls fatal if it runs out of memory.
+void*
+xmalloc(int n)
+{
+ void *p;
+
+ p = malloc(n);
+ if(p == nil)
+ fatal("out of memory");
+ memset(p, 0, n);
+ return p;
+}
+
+// xstrdup returns a newly allocated copy of p.
+// It calls fatal if it runs out of memory.
+char*
+xstrdup(char *p)
+{
+ p = strdup(p);
+ if(p == nil)
+ fatal("out of memory");
+ return p;
+}
+
+// xrealloc grows the allocation p to n bytes and
+// returns the new (possibly moved) pointer.
+// It calls fatal if it runs out of memory.
+void*
+xrealloc(void *p, int n)
+{
+ p = realloc(p, n);
+ if(p == nil)
+ fatal("out of memory");
+ return p;
+}
+
+// xfree frees the result returned by xmalloc, xstrdup, or xrealloc.
+void
+xfree(void *p)
+{
+ free(p);
+}
+
+// hassuffix reports whether p ends with suffix.
+bool
+hassuffix(char *p, char *suffix)
+{
+ int np, ns;
+
+ np = strlen(p);
+ ns = strlen(suffix);
+ return np >= ns && strcmp(p+np-ns, suffix) == 0;
+}
+
+// hasprefix reports whether p begins wtih prefix.
+bool
+hasprefix(char *p, char *prefix)
+{
+ return strncmp(p, prefix, strlen(prefix)) == 0;
+}
+
+// contains reports whether sep appears in p.
+bool
+contains(char *p, char *sep)
+{
+ return strstr(p, sep) != nil;
+}
+
+// streq reports whether p and q are the same string.
+bool
+streq(char *p, char *q)
+{
+ return strcmp(p, q) == 0;
+}
+
+// lastelem returns the final path element in p.
+char*
+lastelem(char *p)
+{
+ char *out;
+
+ out = p;
+ for(; *p; p++)
+ if(*p == '/')
+ out = p+1;
+ return out;
+}
+
+// xmemmove copies n bytes from src to dst.
+void
+xmemmove(void *dst, void *src, int n)
+{
+ memmove(dst, src, n);
+}
+
+// xmemcmp compares the n-byte regions starting at a and at b.
+int
+xmemcmp(void *a, void *b, int n)
+{
+ return memcmp(a, b, n);
+}
+
+// xstrlen returns the length of the NUL-terminated string at p.
+int
+xstrlen(char *p)
+{
+ return strlen(p);
+}
+
+// xexit exits the process with return code n.
+void
+xexit(int n)
+{
+ exit(n);
+}
+
+// xatexit schedules the exit-handler f to be run when the program exits.
+void
+xatexit(void (*f)(void))
+{
+ atexit(f);
+}
+
+// xprintf prints a message to standard output.
+void
+xprintf(char *fmt, ...)
+{
+ va_list arg;
+
+ va_start(arg, fmt);
+ vprintf(fmt, arg);
+ va_end(arg);
+}
+
+// xsetenv sets the environment variable $name to the given value.
+void
+xsetenv(char *name, char *value)
+{
+ setenv(name, value, 1);
+}
+
+// main takes care of OS-specific startup and dispatches to xmain.
+int
+main(int argc, char **argv)
+{
+ Buf b;
+ struct utsname u;
+
+ setvbuf(stdout, nil, _IOLBF, 0);
+ setvbuf(stderr, nil, _IOLBF, 0);
+
+ binit(&b);
+
+ slash = "/";
+
+#if defined(__APPLE__)
+ gohostos = "darwin";
+ // Even on 64-bit platform, darwin uname -m prints i386.
+ run(&b, nil, 0, "sysctl", "machdep.cpu.extfeatures", nil);
+ if(contains(bstr(&b), "EM64T"))
+ gohostarch = "amd64";
+#elif defined(__linux__)
+ gohostos = "linux";
+#elif defined(__FreeBSD__)
+ gohostos = "freebsd";
+#elif defined(__OpenBSD__)
+ gohostos = "openbsd";
+#elif defined(__NetBSD__)
+ gohostos = "netbsd";
+#else
+ fatal("unknown operating system");
+#endif
+
+ if(gohostarch == nil) {
+ if(uname(&u) < 0)
+ fatal("uname: %s", strerror(errno));
+ if(contains(u.machine, "x86_64") || contains(u.machine, "amd64"))
+ gohostarch = "amd64";
+ else if(hassuffix(u.machine, "86"))
+ gohostarch = "386";
+ else if(contains(u.machine, "arm"))
+ gohostarch = "arm";
+ else
+ fatal("unknown architecture: %s", u.machine);
+ }
+
+ if(strcmp(gohostarch, "arm") == 0)
+ maxnbg = 1;
+
+ init();
+ xmain(argc, argv);
+ bfree(&b);
+ return 0;
+}
+
+// xqsort is a wrapper for the C standard qsort.
+void
+xqsort(void *data, int n, int elemsize, int (*cmp)(const void*, const void*))
+{
+ qsort(data, n, elemsize, cmp);
+}
+
+// xstrcmp compares the NUL-terminated strings a and b.
+int
+xstrcmp(char *a, char *b)
+{
+ return strcmp(a, b);
+}
+
+// xstrstr returns a pointer to the first occurrence of b in a.
+char*
+xstrstr(char *a, char *b)
+{
+ return strstr(a, b);
+}
+
+// xstrrchr returns a pointer to the final occurrence of c in p.
+char*
+xstrrchr(char *p, int c)
+{
+ return strrchr(p, c);
+}
+
+#endif // PLAN9
+#endif // __WINDOWS__
diff --git a/src/cmd/dist/windows.c b/src/cmd/dist/windows.c
new file mode 100644
index 000000000..557e4b003
--- /dev/null
+++ b/src/cmd/dist/windows.c
@@ -0,0 +1,910 @@
+// 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.
+
+// These #ifdefs are being used as a substitute for
+// build configuration, so that on any system, this
+// tool can be built with the local equivalent of
+// cc *.c
+//
+#ifdef WIN32
+
+// Portability layer implemented for Windows.
+// See unix.c for doc comments about exported functions.
+
+#include "a.h"
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+/*
+ * Windows uses 16-bit rune strings in the APIs.
+ * Define conversions between Rune* and UTF-8 char*.
+ */
+
+typedef unsigned char uchar;
+typedef unsigned short Rune; // same as Windows
+
+// encoderune encodes the rune r into buf and returns
+// the number of bytes used.
+static int
+encoderune(char *buf, Rune r)
+{
+ if(r < 0x80) { // 7 bits
+ buf[0] = r;
+ return 1;
+ }
+ if(r < 0x800) { // 5+6 bits
+ buf[0] = 0xc0 | (r>>6);
+ buf[1] = 0x80 | (r&0x3f);
+ return 2;
+ }
+ buf[0] = 0xe0 | (r>>12);
+ buf[1] = 0x80 | ((r>>6)&0x3f);
+ buf[2] = 0x80 | (r&0x3f);
+ return 3;
+}
+
+// decoderune decodes the rune encoding at sbuf into r
+// and returns the number of bytes used.
+static int
+decoderune(Rune *r, char *sbuf)
+{
+ uchar *buf;
+
+ buf = (uchar*)sbuf;
+ if(buf[0] < 0x80) {
+ *r = buf[0];
+ return 1;
+ }
+ if((buf[0]&0xe0) == 0xc0 && (buf[1]&0xc0) == 0x80) {
+ *r = (buf[0]&~0xc0)<<6 | (buf[1]&~0x80);
+ if(*r < 0x80)
+ goto err;
+ return 2;
+ }
+ if((buf[0]&0xf0) == 0xe0 && (buf[1]&0xc0) == 0x80 && (buf[2]&0xc0) == 0x80) {
+ *r = (buf[0]&~0xc0)<<12 | (buf[1]&~0x80)<<6 | (buf[2]&~0x80);
+ if(*r < 0x800)
+ goto err;
+ return 3;
+ }
+err:
+ *r = 0xfffd;
+ return 1;
+}
+
+// toutf replaces b with the UTF-8 encoding of the rune string r.
+static void
+toutf(Buf *b, Rune *r)
+{
+ int i, n;
+ char buf[4];
+
+ breset(b);
+ for(i=0; r[i]; i++) {
+ n = encoderune(buf, r[i]);
+ bwrite(b, buf, n);
+ }
+}
+
+// torune replaces *rp with a pointer to a newly allocated
+// rune string equivalent of the UTF-8 string p.
+static void
+torune(Rune **rp, char *p)
+{
+ Rune *r, *w;
+
+ r = xmalloc((strlen(p)+1) * sizeof r[0]);
+ w = r;
+ while(*p)
+ p += decoderune(w++, p);
+ *w = 0;
+ *rp = r;
+}
+
+// errstr returns the most recent Windows error, in string form.
+static char*
+errstr(void)
+{
+ DWORD code;
+ Rune *r;
+ Buf b;
+
+ binit(&b);
+ code = GetLastError();
+ r = nil;
+ FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
+ nil, code, 0, (Rune*)&r, 0, nil);
+ toutf(&b, r);
+ return bstr(&b); // leak but we're dying anyway
+}
+
+void
+xgetenv(Buf *b, char *name)
+{
+ Rune *buf;
+ int n;
+ Rune *r;
+
+ breset(b);
+ torune(&r, name);
+ n = GetEnvironmentVariableW(r, NULL, 0);
+ if(n > 0) {
+ buf = xmalloc((n+1)*sizeof buf[0]);
+ GetEnvironmentVariableW(r, buf, n+1);
+ buf[n] = '\0';
+ toutf(b, buf);
+ xfree(buf);
+ }
+ xfree(r);
+}
+
+void
+xsetenv(char *name, char *value)
+{
+ Rune *rname, *rvalue;
+
+ torune(&rname, name);
+ torune(&rvalue, value);
+ SetEnvironmentVariableW(rname, rvalue);
+ xfree(rname);
+ xfree(rvalue);
+}
+
+char*
+bprintf(Buf *b, char *fmt, ...)
+{
+ va_list arg;
+ char buf[4096];
+
+ breset(b);
+ va_start(arg, fmt);
+ vsnprintf(buf, sizeof buf, fmt, arg);
+ va_end(arg);
+ bwritestr(b, buf);
+ return bstr(b);
+}
+
+void
+bwritef(Buf *b, char *fmt, ...)
+{
+ va_list arg;
+ char buf[4096];
+
+ // no reset
+ va_start(arg, fmt);
+ vsnprintf(buf, sizeof buf, fmt, arg);
+ va_end(arg);
+ bwritestr(b, buf);
+}
+
+// bpathf is like bprintf but replaces / with \ in the result,
+// to make it a canonical windows file path.
+char*
+bpathf(Buf *b, char *fmt, ...)
+{
+ int i;
+ va_list arg;
+ char buf[4096];
+
+ breset(b);
+ va_start(arg, fmt);
+ vsnprintf(buf, sizeof buf, fmt, arg);
+ va_end(arg);
+ bwritestr(b, buf);
+
+ for(i=0; i<b->len; i++)
+ if(b->p[i] == '/')
+ b->p[i] = '\\';
+
+ return bstr(b);
+}
+
+
+static void
+breadfrom(Buf *b, HANDLE h)
+{
+ DWORD n;
+
+ for(;;) {
+ if(b->len > 1<<22)
+ fatal("unlikely file size in readfrom");
+ bgrow(b, 4096);
+ n = 0;
+ if(!ReadFile(h, b->p+b->len, 4096, &n, nil)) {
+ // Happens for pipe reads.
+ break;
+ }
+ if(n == 0)
+ break;
+ b->len += n;
+ }
+}
+
+void
+run(Buf *b, char *dir, int mode, char *cmd, ...)
+{
+ va_list arg;
+ Vec argv;
+ char *p;
+
+ vinit(&argv);
+ vadd(&argv, cmd);
+ va_start(arg, cmd);
+ while((p = va_arg(arg, char*)) != nil)
+ vadd(&argv, p);
+ va_end(arg);
+
+ runv(b, dir, mode, &argv);
+
+ vfree(&argv);
+}
+
+static void genrun(Buf*, char*, int, Vec*, int);
+
+void
+runv(Buf *b, char *dir, int mode, Vec *argv)
+{
+ genrun(b, dir, mode, argv, 1);
+}
+
+void
+bgrunv(char *dir, int mode, Vec *argv)
+{
+ genrun(nil, dir, mode, argv, 0);
+}
+
+#define MAXBG 4 /* maximum number of jobs to run at once */
+
+static struct {
+ PROCESS_INFORMATION pi;
+ int mode;
+ char *cmd;
+} bg[MAXBG];
+
+static int nbg;
+
+static void bgwait1(void);
+
+static void
+genrun(Buf *b, char *dir, int mode, Vec *argv, int wait)
+{
+ int i, j, nslash;
+ Buf cmd;
+ char *q;
+ Rune *rcmd, *rexe, *rdir;
+ STARTUPINFOW si;
+ PROCESS_INFORMATION pi;
+ HANDLE p[2];
+
+ while(nbg >= nelem(bg))
+ bgwait1();
+
+ binit(&cmd);
+
+ for(i=0; i<argv->len; i++) {
+ if(i > 0)
+ bwritestr(&cmd, " ");
+ q = argv->p[i];
+ if(contains(q, " ") || contains(q, "\t") || contains(q, "\"") || contains(q, "\\\\") || hassuffix(q, "\\")) {
+ bwritestr(&cmd, "\"");
+ nslash = 0;
+ for(; *q; q++) {
+ if(*q == '\\') {
+ nslash++;
+ continue;
+ }
+ if(*q == '"') {
+ for(j=0; j<2*nslash+1; j++)
+ bwritestr(&cmd, "\\");
+ nslash = 0;
+ }
+ for(j=0; j<nslash; j++)
+ bwritestr(&cmd, "\\");
+ nslash = 0;
+ bwrite(&cmd, q, 1);
+ }
+ for(j=0; j<2*nslash; j++)
+ bwritestr(&cmd, "\\");
+ bwritestr(&cmd, "\"");
+ } else {
+ bwritestr(&cmd, q);
+ }
+ }
+ if(vflag > 1)
+ xprintf("%s\n", bstr(&cmd));
+
+ torune(&rcmd, bstr(&cmd));
+ rexe = nil;
+ rdir = nil;
+ if(dir != nil)
+ torune(&rdir, dir);
+
+ memset(&si, 0, sizeof si);
+ si.cb = sizeof si;
+ si.dwFlags = STARTF_USESTDHANDLES;
+ si.hStdInput = INVALID_HANDLE_VALUE;
+ if(b == nil) {
+ si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
+ si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+ } else {
+ SECURITY_ATTRIBUTES seci;
+
+ memset(&seci, 0, sizeof seci);
+ seci.nLength = sizeof seci;
+ seci.bInheritHandle = 1;
+ breset(b);
+ if(!CreatePipe(&p[0], &p[1], &seci, 0))
+ fatal("CreatePipe: %s", errstr());
+ si.hStdOutput = p[1];
+ si.hStdError = p[1];
+ }
+
+ if(!CreateProcessW(rexe, rcmd, nil, nil, TRUE, 0, nil, rdir, &si, &pi)) {
+ if(mode!=CheckExit)
+ return;
+ fatal("%s: %s", argv->p[0], errstr());
+ }
+ if(rexe != nil)
+ xfree(rexe);
+ xfree(rcmd);
+ if(rdir != nil)
+ xfree(rdir);
+ if(b != nil) {
+ CloseHandle(p[1]);
+ breadfrom(b, p[0]);
+ CloseHandle(p[0]);
+ }
+
+ if(nbg < 0)
+ fatal("bad bookkeeping");
+ bg[nbg].pi = pi;
+ bg[nbg].mode = mode;
+ bg[nbg].cmd = btake(&cmd);
+ nbg++;
+
+ if(wait)
+ bgwait();
+
+ bfree(&cmd);
+}
+
+// closes the background job for bgwait1
+static void
+bgwaitclose(int i)
+{
+ if(i < 0 || i >= nbg)
+ return;
+
+ CloseHandle(bg[i].pi.hProcess);
+ CloseHandle(bg[i].pi.hThread);
+
+ bg[i] = bg[--nbg];
+}
+
+// bgwait1 waits for a single background job
+static void
+bgwait1(void)
+{
+ int i, mode;
+ char *cmd;
+ HANDLE bgh[MAXBG];
+ DWORD code;
+
+ if(nbg == 0)
+ fatal("bgwait1: nothing left");
+
+ for(i=0; i<nbg; i++)
+ bgh[i] = bg[i].pi.hProcess;
+ i = WaitForMultipleObjects(nbg, bgh, FALSE, INFINITE);
+ if(i < 0 || i >= nbg)
+ fatal("WaitForMultipleObjects: %s", errstr());
+
+ cmd = bg[i].cmd;
+ mode = bg[i].mode;
+ if(!GetExitCodeProcess(bg[i].pi.hProcess, &code)) {
+ bgwaitclose(i);
+ fatal("GetExitCodeProcess: %s", errstr());
+ return;
+ }
+
+ if(mode==CheckExit && code != 0) {
+ bgwaitclose(i);
+ fatal("FAILED: %s", cmd);
+ return;
+ }
+
+ bgwaitclose(i);
+}
+
+void
+bgwait(void)
+{
+ while(nbg > 0)
+ bgwait1();
+}
+
+// rgetwd returns a rune string form of the current directory's path.
+static Rune*
+rgetwd(void)
+{
+ int n;
+ Rune *r;
+
+ n = GetCurrentDirectoryW(0, nil);
+ r = xmalloc((n+1)*sizeof r[0]);
+ GetCurrentDirectoryW(n+1, r);
+ r[n] = '\0';
+ return r;
+}
+
+void
+xgetwd(Buf *b)
+{
+ Rune *r;
+
+ r = rgetwd();
+ breset(b);
+ toutf(b, r);
+ xfree(r);
+}
+
+void
+xrealwd(Buf *b, char *path)
+{
+ Rune *old;
+ Rune *rnew;
+
+ old = rgetwd();
+ torune(&rnew, path);
+ if(!SetCurrentDirectoryW(rnew))
+ fatal("chdir %s: %s", path, errstr());
+ free(rnew);
+ xgetwd(b);
+ if(!SetCurrentDirectoryW(old)) {
+ breset(b);
+ toutf(b, old);
+ fatal("chdir %s: %s", bstr(b), errstr());
+ }
+}
+
+bool
+isdir(char *p)
+{
+ DWORD attr;
+ Rune *r;
+
+ torune(&r, p);
+ attr = GetFileAttributesW(r);
+ xfree(r);
+ return attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY);
+}
+
+bool
+isfile(char *p)
+{
+ DWORD attr;
+ Rune *r;
+
+ torune(&r, p);
+ attr = GetFileAttributesW(r);
+ xfree(r);
+ return attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY);
+}
+
+Time
+mtime(char *p)
+{
+ HANDLE h;
+ WIN32_FIND_DATAW data;
+ Rune *r;
+ FILETIME *ft;
+
+ torune(&r, p);
+ h = FindFirstFileW(r, &data);
+ xfree(r);
+ if(h == INVALID_HANDLE_VALUE)
+ return 0;
+ FindClose(h);
+ ft = &data.ftLastWriteTime;
+ return (Time)ft->dwLowDateTime + ((Time)ft->dwHighDateTime<<32);
+}
+
+bool
+isabs(char *p)
+{
+ // c:/ or c:\ at beginning
+ if(('A' <= p[0] && p[0] <= 'Z') || ('a' <= p[0] && p[0] <= 'z'))
+ return p[1] == ':' && (p[2] == '/' || p[2] == '\\');
+ // / or \ at beginning
+ return p[0] == '/' || p[0] == '\\';
+}
+
+void
+readfile(Buf *b, char *file)
+{
+ HANDLE h;
+ Rune *r;
+
+ if(vflag > 2)
+ xprintf("read %s\n", file);
+ torune(&r, file);
+ h = CreateFileW(r, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
+ if(h == INVALID_HANDLE_VALUE)
+ fatal("open %s: %s", file, errstr());
+ breadfrom(b, h);
+ CloseHandle(h);
+}
+
+void
+writefile(Buf *b, char *file, int exec)
+{
+ HANDLE h;
+ Rune *r;
+ DWORD n;
+
+ USED(exec);
+
+ if(vflag > 2)
+ xprintf("write %s\n", file);
+ torune(&r, file);
+ h = CreateFileW(r, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, nil, CREATE_ALWAYS, 0, 0);
+ if(h == INVALID_HANDLE_VALUE)
+ fatal("create %s: %s", file, errstr());
+ n = 0;
+ if(!WriteFile(h, b->p, b->len, &n, 0))
+ fatal("write %s: %s", file, errstr());
+ CloseHandle(h);
+}
+
+
+void
+xmkdir(char *p)
+{
+ Rune *r;
+
+ torune(&r, p);
+ if(!CreateDirectoryW(r, nil))
+ fatal("mkdir %s: %s", p, errstr());
+ xfree(r);
+}
+
+void
+xmkdirall(char *p)
+{
+ int c;
+ char *q, *q2;
+
+ if(isdir(p))
+ return;
+ q = strrchr(p, '/');
+ q2 = strrchr(p, '\\');
+ if(q2 != nil && (q == nil || q < q2))
+ q = q2;
+ if(q != nil) {
+ c = *q;
+ *q = '\0';
+ xmkdirall(p);
+ *q = c;
+ }
+ xmkdir(p);
+}
+
+void
+xremove(char *p)
+{
+ int attr;
+ Rune *r;
+
+ torune(&r, p);
+ attr = GetFileAttributesW(r);
+ if(attr >= 0) {
+ if(attr & FILE_ATTRIBUTE_DIRECTORY)
+ RemoveDirectoryW(r);
+ else
+ DeleteFileW(r);
+ }
+ xfree(r);
+}
+
+void
+xreaddir(Vec *dst, char *dir)
+{
+ Rune *r;
+ Buf b;
+ HANDLE h;
+ WIN32_FIND_DATAW data;
+ char *p, *q;
+
+ binit(&b);
+ vreset(dst);
+
+ bwritestr(&b, dir);
+ bwritestr(&b, "\\*");
+ torune(&r, bstr(&b));
+
+ h = FindFirstFileW(r, &data);
+ xfree(r);
+ if(h == INVALID_HANDLE_VALUE)
+ goto out;
+ do{
+ toutf(&b, data.cFileName);
+ p = bstr(&b);
+ q = xstrrchr(p, '\\');
+ if(q != nil)
+ p = q+1;
+ if(!streq(p, ".") && !streq(p, ".."))
+ vadd(dst, p);
+ }while(FindNextFileW(h, &data));
+ FindClose(h);
+
+out:
+ bfree(&b);
+}
+
+char*
+xworkdir(void)
+{
+ Rune buf[1024];
+ Rune tmp[MAX_PATH];
+ Rune go[3] = {'g', 'o', '\0'};
+ int n;
+ Buf b;
+
+ n = GetTempPathW(nelem(buf), buf);
+ if(n <= 0)
+ fatal("GetTempPath: %s", errstr());
+ buf[n] = '\0';
+
+ if(GetTempFileNameW(buf, go, 0, tmp) == 0)
+ fatal("GetTempFileName: %s", errstr());
+ DeleteFileW(tmp);
+ if(!CreateDirectoryW(tmp, nil))
+ fatal("create tempdir: %s", errstr());
+
+ binit(&b);
+ toutf(&b, tmp);
+ return btake(&b);
+}
+
+void
+xremoveall(char *p)
+{
+ int i;
+ Buf b;
+ Vec dir;
+ Rune *r;
+
+ binit(&b);
+ vinit(&dir);
+
+ torune(&r, p);
+ if(isdir(p)) {
+ xreaddir(&dir, p);
+ for(i=0; i<dir.len; i++) {
+ bprintf(&b, "%s/%s", p, dir.p[i]);
+ xremoveall(bstr(&b));
+ }
+ RemoveDirectoryW(r);
+ } else {
+ DeleteFileW(r);
+ }
+ xfree(r);
+
+ bfree(&b);
+ vfree(&dir);
+}
+
+void
+fatal(char *msg, ...)
+{
+ static char buf1[1024];
+ va_list arg;
+
+ va_start(arg, msg);
+ vsnprintf(buf1, sizeof buf1, msg, arg);
+ va_end(arg);
+
+ xprintf("go tool dist: %s\n", buf1);
+
+ bgwait();
+ ExitProcess(1);
+}
+
+// HEAP is the persistent handle to the default process heap.
+static HANDLE HEAP = INVALID_HANDLE_VALUE;
+
+void*
+xmalloc(int n)
+{
+ void *p;
+
+ if(HEAP == INVALID_HANDLE_VALUE)
+ HEAP = GetProcessHeap();
+ p = HeapAlloc(HEAP, 0, n);
+ if(p == nil)
+ fatal("out of memory allocating %d: %s", n, errstr());
+ memset(p, 0, n);
+ return p;
+}
+
+char*
+xstrdup(char *p)
+{
+ char *q;
+
+ q = xmalloc(strlen(p)+1);
+ strcpy(q, p);
+ return q;
+}
+
+void
+xfree(void *p)
+{
+ if(HEAP == INVALID_HANDLE_VALUE)
+ HEAP = GetProcessHeap();
+ HeapFree(HEAP, 0, p);
+}
+
+void*
+xrealloc(void *p, int n)
+{
+ if(p == nil)
+ return xmalloc(n);
+ if(HEAP == INVALID_HANDLE_VALUE)
+ HEAP = GetProcessHeap();
+ p = HeapReAlloc(HEAP, 0, p, n);
+ if(p == nil)
+ fatal("out of memory reallocating %d", n);
+ return p;
+}
+
+bool
+hassuffix(char *p, char *suffix)
+{
+ int np, ns;
+
+ np = strlen(p);
+ ns = strlen(suffix);
+ return np >= ns && strcmp(p+np-ns, suffix) == 0;
+}
+
+bool
+hasprefix(char *p, char *prefix)
+{
+ return strncmp(p, prefix, strlen(prefix)) == 0;
+}
+
+bool
+contains(char *p, char *sep)
+{
+ return strstr(p, sep) != nil;
+}
+
+bool
+streq(char *p, char *q)
+{
+ return strcmp(p, q) == 0;
+}
+
+char*
+lastelem(char *p)
+{
+ char *out;
+
+ out = p;
+ for(; *p; p++)
+ if(*p == '/' || *p == '\\')
+ out = p+1;
+ return out;
+}
+
+void
+xmemmove(void *dst, void *src, int n)
+{
+ memmove(dst, src, n);
+}
+
+int
+xmemcmp(void *a, void *b, int n)
+{
+ return memcmp(a, b, n);
+}
+
+int
+xstrlen(char *p)
+{
+ return strlen(p);
+}
+
+void
+xexit(int n)
+{
+ ExitProcess(n);
+}
+
+void
+xatexit(void (*f)(void))
+{
+ atexit(f);
+}
+
+void
+xprintf(char *fmt, ...)
+{
+ va_list arg;
+ char *p;
+ DWORD n, w;
+
+ va_start(arg, fmt);
+ n = vsnprintf(NULL, 0, fmt, arg);
+ p = xmalloc(n+1);
+ vsnprintf(p, n+1, fmt, arg);
+ va_end(arg);
+ w = 0;
+ WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), p, n, &w, 0);
+ xfree(p);
+}
+
+int
+main(int argc, char **argv)
+{
+ SYSTEM_INFO si;
+
+ setvbuf(stdout, nil, _IOLBF, 0);
+ setvbuf(stderr, nil, _IOLBF, 0);
+
+ slash = "\\";
+ gohostos = "windows";
+
+ GetSystemInfo(&si);
+ switch(si.wProcessorArchitecture) {
+ case PROCESSOR_ARCHITECTURE_AMD64:
+ gohostarch = "amd64";
+ break;
+ case PROCESSOR_ARCHITECTURE_INTEL:
+ gohostarch = "386";
+ break;
+ default:
+ fatal("unknown processor architecture");
+ }
+
+ init();
+
+ xmain(argc, argv);
+ return 0;
+}
+
+void
+xqsort(void *data, int n, int elemsize, int (*cmp)(const void*, const void*))
+{
+ qsort(data, n, elemsize, cmp);
+}
+
+int
+xstrcmp(char *a, char *b)
+{
+ return strcmp(a, b);
+}
+
+char*
+xstrstr(char *a, char *b)
+{
+ return strstr(a, b);
+}
+
+char*
+xstrrchr(char *p, int c)
+{
+ char *ep;
+
+ ep = p+strlen(p);
+ for(ep=p+strlen(p); ep >= p; ep--)
+ if(*ep == c)
+ return ep;
+ return nil;
+}
+
+#endif // __WINDOWS__
diff --git a/src/cmd/ebnflint/Makefile b/src/cmd/ebnflint/Makefile
deleted file mode 100644
index 8f030aaef..000000000
--- a/src/cmd/ebnflint/Makefile
+++ /dev/null
@@ -1,15 +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 ../../Make.inc
-
-TARG=ebnflint
-GOFILES=\
- ebnflint.go\
-
-include ../../Make.cmd
-
-test: $(TARG)
- $(TARG) -start="SourceFile" "$(GOROOT)"/doc/go_spec.html
-
diff --git a/src/cmd/ebnflint/doc.go b/src/cmd/ebnflint/doc.go
deleted file mode 100644
index f35976eea..000000000
--- a/src/cmd/ebnflint/doc.go
+++ /dev/null
@@ -1,22 +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.
-
-/*
-
-Ebnflint verifies that EBNF productions are consistent and gramatically correct.
-It reads them from an HTML document such as the Go specification.
-
-Grammar productions are grouped in boxes demarcated by the HTML elements
- <pre class="ebnf">
- </pre>
-
-
-Usage:
- ebnflint [--start production] [file]
-
-The --start flag specifies the name of the start production for
-the grammar; it defaults to "Start".
-
-*/
-package documentation
diff --git a/src/cmd/ebnflint/ebnflint.go b/src/cmd/ebnflint/ebnflint.go
deleted file mode 100644
index 009b336f3..000000000
--- a/src/cmd/ebnflint/ebnflint.go
+++ /dev/null
@@ -1,109 +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"
- "ebnf"
- "flag"
- "fmt"
- "go/scanner"
- "go/token"
- "io/ioutil"
- "os"
- "path/filepath"
-)
-
-var fset = token.NewFileSet()
-var start = flag.String("start", "Start", "name of start production")
-
-func usage() {
- fmt.Fprintf(os.Stderr, "usage: ebnflint [flags] [filename]\n")
- flag.PrintDefaults()
- os.Exit(1)
-}
-
-// Markers around EBNF sections in .html files
-var (
- open = []byte(`<pre class="ebnf">`)
- close = []byte(`</pre>`)
-)
-
-func report(err os.Error) {
- scanner.PrintError(os.Stderr, err)
- os.Exit(1)
-}
-
-func extractEBNF(src []byte) []byte {
- var buf bytes.Buffer
-
- for {
- // i = beginning of EBNF text
- i := bytes.Index(src, open)
- if i < 0 {
- break // no EBNF found - we are done
- }
- i += len(open)
-
- // write as many newlines as found in the excluded text
- // to maintain correct line numbers in error messages
- for _, ch := range src[0:i] {
- if ch == '\n' {
- buf.WriteByte('\n')
- }
- }
-
- // j = end of EBNF text (or end of source)
- j := bytes.Index(src[i:], close) // close marker
- if j < 0 {
- j = len(src) - i
- }
- j += i
-
- // copy EBNF text
- buf.Write(src[i:j])
-
- // advance
- src = src[j:]
- }
-
- return buf.Bytes()
-}
-
-func main() {
- flag.Parse()
-
- var (
- filename string
- src []byte
- err os.Error
- )
- switch flag.NArg() {
- case 0:
- filename = "<stdin>"
- src, err = ioutil.ReadAll(os.Stdin)
- case 1:
- filename = flag.Arg(0)
- src, err = ioutil.ReadFile(filename)
- default:
- usage()
- }
- if err != nil {
- report(err)
- }
-
- if filepath.Ext(filename) == ".html" || bytes.Index(src, open) >= 0 {
- src = extractEBNF(src)
- }
-
- grammar, err := ebnf.Parse(fset, filename, src)
- if err != nil {
- report(err)
- }
-
- if err = ebnf.Verify(fset, grammar, *start); err != nil {
- report(err)
- }
-}
diff --git a/src/cmd/fix/doc.go b/src/cmd/fix/doc.go
new file mode 100644
index 000000000..a92e0fc06
--- /dev/null
+++ b/src/cmd/fix/doc.go
@@ -0,0 +1,36 @@
+// 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.
+
+/*
+Fix finds Go programs that use old APIs and rewrites them to use
+newer ones. After you update to a new Go release, fix helps make
+the necessary changes to your programs.
+
+Usage:
+ go tool fix [-r name,...] [path ...]
+
+Without an explicit path, fix reads standard input and writes the
+result to standard output.
+
+If the named path is a file, fix rewrites the named files in place.
+If the named path is a directory, fix rewrites all .go files in that
+directory tree. When fix rewrites a file, it prints a line to standard
+error giving the name of the file and the rewrite applied.
+
+If the -diff flag is set, no files are rewritten. Instead fix prints
+the differences a rewrite would introduce.
+
+The -r flag restricts the set of rewrites considered to those in the
+named list. By default fix considers all known rewrites. Fix's
+rewrites are idempotent, so that it is safe to apply fix to updated
+or partially updated code even without using the -r flag.
+
+Fix prints the full list of fixes it can apply in its help output;
+to see them, run go tool fix -?.
+
+Fix does not make backup copies of the files that it edits.
+Instead, use a version control system's ``diff'' functionality to inspect
+the changes that fix makes before committing them.
+*/
+package documentation
diff --git a/src/cmd/fix/error.go b/src/cmd/fix/error.go
new file mode 100644
index 000000000..55613210a
--- /dev/null
+++ b/src/cmd/fix/error.go
@@ -0,0 +1,353 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "go/ast"
+ "regexp"
+ "strings"
+)
+
+func init() {
+ register(errorFix)
+}
+
+var errorFix = fix{
+ "error",
+ "2011-11-02",
+ errorFn,
+ `Use error instead of os.Error.
+
+This fix rewrites code using os.Error to use error:
+
+ os.Error -> error
+ os.NewError -> errors.New
+ os.EOF -> io.EOF
+
+Seeing the old names above (os.Error and so on) triggers the following
+heuristic rewrites. The heuristics can be forced using the -force=error flag.
+
+A top-level function, variable, or constant named error is renamed error_.
+
+Error implementations—those types used as os.Error or named
+XxxError—have their String methods renamed to Error. Any existing
+Error field or method is renamed to Err.
+
+Error values—those with type os.Error or named e, err, error, err1,
+and so on—have method calls and field references rewritten just
+as the types do (String to Error, Error to Err). Also, a type assertion
+of the form err.(*os.Waitmsg) becomes err.(*exec.ExitError).
+
+http://codereview.appspot.com/5305066
+`,
+}
+
+// At minimum, this fix applies the following rewrites:
+//
+// os.Error -> error
+// os.NewError -> errors.New
+// os.EOF -> io.EOF
+//
+// However, if can apply any of those rewrites, it assumes that the
+// file predates the error type and tries to update the code to use
+// the new definition for error - an Error method, not a String method.
+// This more heuristic procedure may not be 100% accurate, so it is
+// only run when the file needs updating anyway. The heuristic can
+// be forced to run using -force=error.
+//
+// First, we must identify the implementations of os.Error.
+// These include the type of any value returned as or assigned to an os.Error.
+// To that set we add any type whose name contains "Error" or "error".
+// The heuristic helps for implementations that are not used as os.Error
+// in the file in which they are defined.
+//
+// In any implementation of os.Error, we rename an existing struct field
+// or method named Error to Err and rename the String method to Error.
+//
+// Second, we must identify the values of type os.Error.
+// These include any value that obviously has type os.Error.
+// To that set we add any variable whose name is e or err or error
+// possibly followed by _ or a numeric or capitalized suffix.
+// The heuristic helps for variables that are initialized using calls
+// to functions in other packages. The type checker does not have
+// information about those packages available, and in general cannot
+// (because the packages may themselves not compile).
+//
+// For any value of type os.Error, we replace a call to String with a call to Error.
+// We also replace type assertion err.(*os.Waitmsg) with err.(*exec.ExitError).
+
+// Variables matching this regexp are assumed to have type os.Error.
+var errVar = regexp.MustCompile(`^(e|err|error)_?([A-Z0-9].*)?$`)
+
+// Types matching this regexp are assumed to be implementations of os.Error.
+var errType = regexp.MustCompile(`^\*?([Ee]rror|.*Error)$`)
+
+// Type-checking configuration: tell the type-checker this basic
+// information about types, functions, and variables in external packages.
+var errorTypeConfig = &TypeConfig{
+ Type: map[string]*Type{
+ "os.Error": {},
+ },
+ Func: map[string]string{
+ "fmt.Errorf": "os.Error",
+ "os.NewError": "os.Error",
+ },
+ Var: map[string]string{
+ "os.EPERM": "os.Error",
+ "os.ENOENT": "os.Error",
+ "os.ESRCH": "os.Error",
+ "os.EINTR": "os.Error",
+ "os.EIO": "os.Error",
+ "os.ENXIO": "os.Error",
+ "os.E2BIG": "os.Error",
+ "os.ENOEXEC": "os.Error",
+ "os.EBADF": "os.Error",
+ "os.ECHILD": "os.Error",
+ "os.EDEADLK": "os.Error",
+ "os.ENOMEM": "os.Error",
+ "os.EACCES": "os.Error",
+ "os.EFAULT": "os.Error",
+ "os.EBUSY": "os.Error",
+ "os.EEXIST": "os.Error",
+ "os.EXDEV": "os.Error",
+ "os.ENODEV": "os.Error",
+ "os.ENOTDIR": "os.Error",
+ "os.EISDIR": "os.Error",
+ "os.EINVAL": "os.Error",
+ "os.ENFILE": "os.Error",
+ "os.EMFILE": "os.Error",
+ "os.ENOTTY": "os.Error",
+ "os.EFBIG": "os.Error",
+ "os.ENOSPC": "os.Error",
+ "os.ESPIPE": "os.Error",
+ "os.EROFS": "os.Error",
+ "os.EMLINK": "os.Error",
+ "os.EPIPE": "os.Error",
+ "os.EAGAIN": "os.Error",
+ "os.EDOM": "os.Error",
+ "os.ERANGE": "os.Error",
+ "os.EADDRINUSE": "os.Error",
+ "os.ECONNREFUSED": "os.Error",
+ "os.ENAMETOOLONG": "os.Error",
+ "os.EAFNOSUPPORT": "os.Error",
+ "os.ETIMEDOUT": "os.Error",
+ "os.ENOTCONN": "os.Error",
+ },
+}
+
+func errorFn(f *ast.File) bool {
+ if !imports(f, "os") && !force["error"] {
+ return false
+ }
+
+ // Fix gets called once to run the heuristics described above
+ // when we notice that this file definitely needs fixing
+ // (it mentions os.Error or something similar).
+ var fixed bool
+ var didHeuristic bool
+ heuristic := func() {
+ if didHeuristic {
+ return
+ }
+ didHeuristic = true
+
+ // We have identified a necessary fix (like os.Error -> error)
+ // but have not applied it or any others yet. Prepare the file
+ // for fixing and apply heuristic fixes.
+
+ // Rename error to error_ to make room for error.
+ fixed = renameTop(f, "error", "error_") || fixed
+
+ // Use type checker to build list of error implementations.
+ typeof, assign := typecheck(errorTypeConfig, f)
+
+ isError := map[string]bool{}
+ for _, val := range assign["os.Error"] {
+ t := typeof[val]
+ if strings.HasPrefix(t, "*") {
+ t = t[1:]
+ }
+ if t != "" && !strings.HasPrefix(t, "func(") {
+ isError[t] = true
+ }
+ }
+
+ // We use both the type check results and the "Error" name heuristic
+ // to identify implementations of os.Error.
+ isErrorImpl := func(typ string) bool {
+ return isError[typ] || errType.MatchString(typ)
+ }
+
+ isErrorVar := func(x ast.Expr) bool {
+ if typ := typeof[x]; typ != "" {
+ return isErrorImpl(typ) || typ == "os.Error"
+ }
+ if sel, ok := x.(*ast.SelectorExpr); ok {
+ return sel.Sel.Name == "Error" || sel.Sel.Name == "Err"
+ }
+ if id, ok := x.(*ast.Ident); ok {
+ return errVar.MatchString(id.Name)
+ }
+ return false
+ }
+
+ walk(f, func(n interface{}) {
+ // In method declaration on error implementation type,
+ // rename String() to Error() and Error() to Err().
+ fn, ok := n.(*ast.FuncDecl)
+ if ok &&
+ fn.Recv != nil &&
+ len(fn.Recv.List) == 1 &&
+ isErrorImpl(typeName(fn.Recv.List[0].Type)) {
+ // Rename.
+ switch fn.Name.Name {
+ case "String":
+ fn.Name.Name = "Error"
+ fixed = true
+ case "Error":
+ fn.Name.Name = "Err"
+ fixed = true
+ }
+ return
+ }
+
+ // In type definition of an error implementation type,
+ // rename Error field to Err to make room for method.
+ // Given type XxxError struct { ... Error T } rename field to Err.
+ d, ok := n.(*ast.GenDecl)
+ if ok {
+ for _, s := range d.Specs {
+ switch s := s.(type) {
+ case *ast.TypeSpec:
+ if isErrorImpl(typeName(s.Name)) {
+ st, ok := s.Type.(*ast.StructType)
+ if ok {
+ for _, f := range st.Fields.List {
+ for _, n := range f.Names {
+ if n.Name == "Error" {
+ n.Name = "Err"
+ fixed = true
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // For values that are an error implementation type,
+ // rename .Error to .Err and .String to .Error
+ sel, selok := n.(*ast.SelectorExpr)
+ if selok && isErrorImpl(typeof[sel.X]) {
+ switch sel.Sel.Name {
+ case "Error":
+ sel.Sel.Name = "Err"
+ fixed = true
+ case "String":
+ sel.Sel.Name = "Error"
+ fixed = true
+ }
+ }
+
+ // Assume x.Err is an error value and rename .String to .Error
+ // Children have been processed so the rewrite from Error to Err
+ // has already happened there.
+ if selok {
+ if subsel, ok := sel.X.(*ast.SelectorExpr); ok && subsel.Sel.Name == "Err" && sel.Sel.Name == "String" {
+ sel.Sel.Name = "Error"
+ fixed = true
+ }
+ }
+
+ // For values that are an error variable, rename .String to .Error.
+ if selok && isErrorVar(sel.X) && sel.Sel.Name == "String" {
+ sel.Sel.Name = "Error"
+ fixed = true
+ }
+
+ // Rewrite composite literal of error type to turn Error: into Err:.
+ lit, ok := n.(*ast.CompositeLit)
+ if ok && isErrorImpl(typeof[lit]) {
+ for _, e := range lit.Elts {
+ if kv, ok := e.(*ast.KeyValueExpr); ok && isName(kv.Key, "Error") {
+ kv.Key.(*ast.Ident).Name = "Err"
+ fixed = true
+ }
+ }
+ }
+
+ // Rename os.Waitmsg to exec.ExitError
+ // when used in a type assertion on an error.
+ ta, ok := n.(*ast.TypeAssertExpr)
+ if ok && isErrorVar(ta.X) && isPtrPkgDot(ta.Type, "os", "Waitmsg") {
+ addImport(f, "exec")
+ sel := ta.Type.(*ast.StarExpr).X.(*ast.SelectorExpr)
+ sel.X.(*ast.Ident).Name = "exec"
+ sel.Sel.Name = "ExitError"
+ fixed = true
+ }
+
+ })
+ }
+
+ fix := func() {
+ if fixed {
+ return
+ }
+ fixed = true
+ heuristic()
+ }
+
+ if force["error"] {
+ heuristic()
+ }
+
+ walk(f, func(n interface{}) {
+ p, ok := n.(*ast.Expr)
+ if !ok {
+ return
+ }
+ sel, ok := (*p).(*ast.SelectorExpr)
+ if !ok {
+ return
+ }
+ switch {
+ case isPkgDot(sel, "os", "Error"):
+ fix()
+ *p = &ast.Ident{NamePos: sel.Pos(), Name: "error"}
+ case isPkgDot(sel, "os", "NewError"):
+ fix()
+ addImport(f, "errors")
+ sel.X.(*ast.Ident).Name = "errors"
+ sel.Sel.Name = "New"
+ case isPkgDot(sel, "os", "EOF"):
+ fix()
+ addImport(f, "io")
+ sel.X.(*ast.Ident).Name = "io"
+ }
+ })
+
+ if fixed && !usesImport(f, "os") {
+ deleteImport(f, "os")
+ }
+
+ return fixed
+}
+
+func typeName(typ ast.Expr) string {
+ if p, ok := typ.(*ast.StarExpr); ok {
+ typ = p.X
+ }
+ id, ok := typ.(*ast.Ident)
+ if ok {
+ return id.Name
+ }
+ sel, ok := typ.(*ast.SelectorExpr)
+ if ok {
+ return typeName(sel.X) + "." + sel.Sel.Name
+ }
+ return ""
+}
diff --git a/src/cmd/fix/error_test.go b/src/cmd/fix/error_test.go
new file mode 100644
index 000000000..027eed24f
--- /dev/null
+++ b/src/cmd/fix/error_test.go
@@ -0,0 +1,240 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func init() {
+ addTestCases(errorTests, errorFn)
+}
+
+var errorTests = []testCase{
+ {
+ Name: "error.0",
+ In: `package main
+
+func error() {}
+
+var error int
+`,
+ Out: `package main
+
+func error() {}
+
+var error int
+`,
+ },
+ {
+ Name: "error.1",
+ In: `package main
+
+import "os"
+
+func f() os.Error {
+ return os.EOF
+}
+
+func error() {}
+
+var error int
+
+func g() {
+ error := 1
+ _ = error
+}
+
+func h(os.Error) {}
+
+func i(...os.Error) {}
+`,
+ Out: `package main
+
+import "io"
+
+func f() error {
+ return io.EOF
+}
+
+func error_() {}
+
+var error_ int
+
+func g() {
+ error := 1
+ _ = error
+}
+
+func h(error) {}
+
+func i(...error) {}
+`,
+ },
+ {
+ Name: "error.2",
+ In: `package main
+
+import "os"
+
+func f() os.Error {
+ return os.EOF
+}
+
+func g() string {
+ // these all convert because f is known
+ if err := f(); err != nil {
+ return err.String()
+ }
+ if err1 := f(); err1 != nil {
+ return err1.String()
+ }
+ if e := f(); e != nil {
+ return e.String()
+ }
+ if x := f(); x != nil {
+ return x.String()
+ }
+
+ // only the error names (err, err1, e) convert; u is not known
+ if err := u(); err != nil {
+ return err.String()
+ }
+ if err1 := u(); err1 != nil {
+ return err1.String()
+ }
+ if e := u(); e != nil {
+ return e.String()
+ }
+ if x := u(); x != nil {
+ return x.String()
+ }
+ return ""
+}
+
+type T int
+
+func (t T) String() string { return "t" }
+
+type PT int
+
+func (p *PT) String() string { return "pt" }
+
+type MyError int
+
+func (t MyError) String() string { return "myerror" }
+
+type PMyError int
+
+func (p *PMyError) String() string { return "pmyerror" }
+
+func error() {}
+
+var error int
+`,
+ Out: `package main
+
+import "io"
+
+func f() error {
+ return io.EOF
+}
+
+func g() string {
+ // these all convert because f is known
+ if err := f(); err != nil {
+ return err.Error()
+ }
+ if err1 := f(); err1 != nil {
+ return err1.Error()
+ }
+ if e := f(); e != nil {
+ return e.Error()
+ }
+ if x := f(); x != nil {
+ return x.Error()
+ }
+
+ // only the error names (err, err1, e) convert; u is not known
+ if err := u(); err != nil {
+ return err.Error()
+ }
+ if err1 := u(); err1 != nil {
+ return err1.Error()
+ }
+ if e := u(); e != nil {
+ return e.Error()
+ }
+ if x := u(); x != nil {
+ return x.String()
+ }
+ return ""
+}
+
+type T int
+
+func (t T) String() string { return "t" }
+
+type PT int
+
+func (p *PT) String() string { return "pt" }
+
+type MyError int
+
+func (t MyError) Error() string { return "myerror" }
+
+type PMyError int
+
+func (p *PMyError) Error() string { return "pmyerror" }
+
+func error_() {}
+
+var error_ int
+`,
+ },
+ {
+ Name: "error.3",
+ In: `package main
+
+import "os"
+
+func f() os.Error {
+ return os.EOF
+}
+
+type PathError struct {
+ Name string
+ Error os.Error
+}
+
+func (p *PathError) String() string {
+ return p.Name + ": " + p.Error.String()
+}
+
+func (p *PathError) Error1() string {
+ p = &PathError{Error: nil}
+ return fmt.Sprint(p.Name, ": ", p.Error)
+}
+`,
+ Out: `package main
+
+import "io"
+
+func f() error {
+ return io.EOF
+}
+
+type PathError struct {
+ Name string
+ Err error
+}
+
+func (p *PathError) Error() string {
+ return p.Name + ": " + p.Err.Error()
+}
+
+func (p *PathError) Error1() string {
+ p = &PathError{Err: nil}
+ return fmt.Sprint(p.Name, ": ", p.Err)
+}
+`,
+ },
+}
diff --git a/src/cmd/gofix/filepath.go b/src/cmd/fix/filepath.go
index 1d0ad6879..f31226018 100644
--- a/src/cmd/gofix/filepath.go
+++ b/src/cmd/fix/filepath.go
@@ -9,14 +9,17 @@ import (
)
func init() {
- register(fix{
- "filepath",
- filepathFunc,
- `Adapt code from filepath.[List]SeparatorString to string(filepath.[List]Separator).
+ register(filepathFix)
+}
+
+var filepathFix = fix{
+ "filepath",
+ "2011-06-26",
+ filepathFunc,
+ `Adapt code from filepath.[List]SeparatorString to string(filepath.[List]Separator).
http://codereview.appspot.com/4527090
`,
- })
}
func filepathFunc(f *ast.File) (fixed bool) {
diff --git a/src/cmd/gofix/filepath_test.go b/src/cmd/fix/filepath_test.go
index d170c3ae3..37a2f5d9f 100644
--- a/src/cmd/gofix/filepath_test.go
+++ b/src/cmd/fix/filepath_test.go
@@ -5,7 +5,7 @@
package main
func init() {
- addTestCases(filepathTests)
+ addTestCases(filepathTests, filepathFunc)
}
var filepathTests = []testCase{
diff --git a/src/cmd/gofix/fix.go b/src/cmd/fix/fix.go
index cc85ceafa..a100be794 100644
--- a/src/cmd/gofix/fix.go
+++ b/src/cmd/fix/fix.go
@@ -7,26 +7,37 @@ package main
import (
"fmt"
"go/ast"
+ "go/parser"
"go/token"
"os"
+ "path"
+ "reflect"
"strconv"
"strings"
)
type fix struct {
name string
+ date string // date that fix was introduced, in YYYY-MM-DD format
f func(*ast.File) bool
desc string
}
-// main runs sort.Sort(fixes) after init process is done.
-type fixlist []fix
+// main runs sort.Sort(byName(fixes)) before printing list of fixes.
+type byName []fix
-func (f fixlist) Len() int { return len(f) }
-func (f fixlist) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
-func (f fixlist) Less(i, j int) bool { return f[i].name < f[j].name }
+func (f byName) Len() int { return len(f) }
+func (f byName) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
+func (f byName) Less(i, j int) bool { return f[i].name < f[j].name }
-var fixes fixlist
+// main runs sort.Sort(byDate(fixes)) before applying fixes.
+type byDate []fix
+
+func (f byDate) Len() int { return len(f) }
+func (f byDate) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
+func (f byDate) Less(i, j int) bool { return f[i].date < f[j].date }
+
+var fixes []fix
func register(f fix) {
fixes = append(fixes, f)
@@ -73,6 +84,8 @@ func walkBeforeAfter(x interface{}, before, after func(interface{})) {
walkBeforeAfter(*n, before, after)
case **ast.Ident:
walkBeforeAfter(*n, before, after)
+ case **ast.BasicLit:
+ walkBeforeAfter(*n, before, after)
// pointers to slices
case *[]ast.Decl:
@@ -90,7 +103,9 @@ func walkBeforeAfter(x interface{}, before, after func(interface{})) {
// These are ordered and grouped to match ../../pkg/go/ast/ast.go
case *ast.Field:
+ walkBeforeAfter(&n.Names, before, after)
walkBeforeAfter(&n.Type, before, after)
+ walkBeforeAfter(&n.Tag, before, after)
case *ast.FieldList:
for _, field := range n.List {
walkBeforeAfter(field, before, after)
@@ -98,6 +113,7 @@ func walkBeforeAfter(x interface{}, before, after func(interface{})) {
case *ast.BadExpr:
case *ast.Ident:
case *ast.Ellipsis:
+ walkBeforeAfter(&n.Elt, before, after)
case *ast.BasicLit:
case *ast.FuncLit:
walkBeforeAfter(&n.Type, before, after)
@@ -292,6 +308,20 @@ func importPath(s *ast.ImportSpec) string {
return ""
}
+// declImports reports whether gen contains an import of path.
+func declImports(gen *ast.GenDecl, path string) bool {
+ if gen.Tok != token.IMPORT {
+ return false
+ }
+ for _, spec := range gen.Specs {
+ impspec := spec.(*ast.ImportSpec)
+ if importPath(impspec) == path {
+ return true
+ }
+ }
+ return false
+}
+
// isPkgDot returns true if t is the expression "pkg.name"
// where pkg is an imported identifier.
func isPkgDot(t ast.Expr, pkg, name string) bool {
@@ -446,66 +476,172 @@ func newPkgDot(pos token.Pos, pkg, name string) ast.Expr {
}
}
+// renameTop renames all references to the top-level name old.
+// It returns true if it makes any changes.
+func renameTop(f *ast.File, old, new string) bool {
+ var fixed bool
+
+ // Rename any conflicting imports
+ // (assuming package name is last element of path).
+ for _, s := range f.Imports {
+ if s.Name != nil {
+ if s.Name.Name == old {
+ s.Name.Name = new
+ fixed = true
+ }
+ } else {
+ _, thisName := path.Split(importPath(s))
+ if thisName == old {
+ s.Name = ast.NewIdent(new)
+ fixed = true
+ }
+ }
+ }
+
+ // Rename any top-level declarations.
+ for _, d := range f.Decls {
+ switch d := d.(type) {
+ case *ast.FuncDecl:
+ if d.Recv == nil && d.Name.Name == old {
+ d.Name.Name = new
+ d.Name.Obj.Name = new
+ fixed = true
+ }
+ case *ast.GenDecl:
+ for _, s := range d.Specs {
+ switch s := s.(type) {
+ case *ast.TypeSpec:
+ if s.Name.Name == old {
+ s.Name.Name = new
+ s.Name.Obj.Name = new
+ fixed = true
+ }
+ case *ast.ValueSpec:
+ for _, n := range s.Names {
+ if n.Name == old {
+ n.Name = new
+ n.Obj.Name = new
+ fixed = true
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Rename top-level old to new, both unresolved names
+ // (probably defined in another file) and names that resolve
+ // to a declaration we renamed.
+ walk(f, func(n interface{}) {
+ id, ok := n.(*ast.Ident)
+ if ok && isTopName(id, old) {
+ id.Name = new
+ fixed = true
+ }
+ if ok && id.Obj != nil && id.Name == old && id.Obj.Name == new {
+ id.Name = id.Obj.Name
+ fixed = true
+ }
+ })
+
+ return fixed
+}
+
+// matchLen returns the length of the longest prefix shared by x and y.
+func matchLen(x, y string) int {
+ i := 0
+ for i < len(x) && i < len(y) && x[i] == y[i] {
+ i++
+ }
+ return i
+}
+
// addImport adds the import path to the file f, if absent.
-func addImport(f *ast.File, path string) {
- if imports(f, path) {
- return
+func addImport(f *ast.File, ipath string) (added bool) {
+ if imports(f, ipath) {
+ return false
}
+ // Determine name of import.
+ // Assume added imports follow convention of using last element.
+ _, name := path.Split(ipath)
+
+ // Rename any conflicting top-level references from name to name_.
+ renameTop(f, name, name+"_")
+
newImport := &ast.ImportSpec{
Path: &ast.BasicLit{
Kind: token.STRING,
- Value: strconv.Quote(path),
+ Value: strconv.Quote(ipath),
},
}
- var impdecl *ast.GenDecl
-
// Find an import decl to add to.
- for _, decl := range f.Decls {
+ var (
+ bestMatch = -1
+ lastImport = -1
+ impDecl *ast.GenDecl
+ impIndex = -1
+ )
+ for i, decl := range f.Decls {
gen, ok := decl.(*ast.GenDecl)
-
if ok && gen.Tok == token.IMPORT {
- impdecl = gen
- break
+ lastImport = i
+ // Do not add to import "C", to avoid disrupting the
+ // association with its doc comment, breaking cgo.
+ if declImports(gen, "C") {
+ continue
+ }
+
+ // Compute longest shared prefix with imports in this block.
+ for j, spec := range gen.Specs {
+ impspec := spec.(*ast.ImportSpec)
+ n := matchLen(importPath(impspec), ipath)
+ if n > bestMatch {
+ bestMatch = n
+ impDecl = gen
+ impIndex = j
+ }
+ }
}
}
- // No import decl found. Add one.
- if impdecl == nil {
- impdecl = &ast.GenDecl{
+ // If no import decl found, add one after the last import.
+ if impDecl == nil {
+ impDecl = &ast.GenDecl{
Tok: token.IMPORT,
}
f.Decls = append(f.Decls, nil)
- copy(f.Decls[1:], f.Decls)
- f.Decls[0] = impdecl
+ copy(f.Decls[lastImport+2:], f.Decls[lastImport+1:])
+ f.Decls[lastImport+1] = impDecl
}
// Ensure the import decl has parentheses, if needed.
- if len(impdecl.Specs) > 0 && !impdecl.Lparen.IsValid() {
- impdecl.Lparen = impdecl.Pos()
+ if len(impDecl.Specs) > 0 && !impDecl.Lparen.IsValid() {
+ impDecl.Lparen = impDecl.Pos()
}
- // Assume the import paths are alphabetically ordered.
- // If they are not, the result is ugly, but legal.
- insertAt := len(impdecl.Specs) // default to end of specs
- for i, spec := range impdecl.Specs {
- impspec := spec.(*ast.ImportSpec)
- if importPath(impspec) > path {
- insertAt = i
- break
- }
+ insertAt := impIndex + 1
+ if insertAt == 0 {
+ insertAt = len(impDecl.Specs)
+ }
+ impDecl.Specs = append(impDecl.Specs, nil)
+ copy(impDecl.Specs[insertAt+1:], impDecl.Specs[insertAt:])
+ impDecl.Specs[insertAt] = newImport
+ if insertAt > 0 {
+ // Assign same position as the previous import,
+ // so that the sorter sees it as being in the same block.
+ prev := impDecl.Specs[insertAt-1]
+ newImport.Path.ValuePos = prev.Pos()
+ newImport.EndPos = prev.Pos()
}
-
- impdecl.Specs = append(impdecl.Specs, nil)
- copy(impdecl.Specs[insertAt+1:], impdecl.Specs[insertAt:])
- impdecl.Specs[insertAt] = newImport
f.Imports = append(f.Imports, newImport)
+ return true
}
// deleteImport deletes the import path from the file f, if present.
-func deleteImport(f *ast.File, path string) {
+func deleteImport(f *ast.File, path string) (deleted bool) {
oldImport := importSpec(f, path)
// Find the import node that imports path, if any.
@@ -516,13 +652,13 @@ func deleteImport(f *ast.File, path string) {
}
for j, spec := range gen.Specs {
impspec := spec.(*ast.ImportSpec)
-
if oldImport != impspec {
continue
}
// We found an import spec that imports path.
// Delete it.
+ deleted = true
copy(gen.Specs[j:], gen.Specs[j+1:])
gen.Specs = gen.Specs[:len(gen.Specs)-1]
@@ -534,7 +670,13 @@ func deleteImport(f *ast.File, path string) {
} else if len(gen.Specs) == 1 {
gen.Lparen = token.NoPos // drop parens
}
-
+ if j > 0 {
+ // We deleted an entry but now there will be
+ // a blank line-sized hole where the import was.
+ // Close the hole by making the previous
+ // import appear to "end" where this one did.
+ gen.Specs[j-1].(*ast.ImportSpec).EndPos = impspec.End()
+ }
break
}
}
@@ -547,6 +689,22 @@ func deleteImport(f *ast.File, path string) {
break
}
}
+
+ return
+}
+
+// rewriteImport rewrites any import of path oldPath to path newPath.
+func rewriteImport(f *ast.File, oldPath, newPath string) (rewrote bool) {
+ for _, imp := range f.Imports {
+ if importPath(imp) == oldPath {
+ rewrote = true
+ // record old End, because the default is to compute
+ // it using the length of imp.Path.Value.
+ imp.EndPos = imp.End()
+ imp.Path.Value = strconv.Quote(newPath)
+ }
+ }
+ return
}
func usesImport(f *ast.File, path string) (used bool) {
@@ -580,3 +738,111 @@ func usesImport(f *ast.File, path string) (used bool) {
return
}
+
+func expr(s string) ast.Expr {
+ x, err := parser.ParseExpr(s)
+ if err != nil {
+ panic("parsing " + s + ": " + err.Error())
+ }
+ // Remove position information to avoid spurious newlines.
+ killPos(reflect.ValueOf(x))
+ return x
+}
+
+var posType = reflect.TypeOf(token.Pos(0))
+
+func killPos(v reflect.Value) {
+ switch v.Kind() {
+ case reflect.Ptr, reflect.Interface:
+ if !v.IsNil() {
+ killPos(v.Elem())
+ }
+ case reflect.Slice:
+ n := v.Len()
+ for i := 0; i < n; i++ {
+ killPos(v.Index(i))
+ }
+ case reflect.Struct:
+ n := v.NumField()
+ for i := 0; i < n; i++ {
+ f := v.Field(i)
+ if f.Type() == posType {
+ f.SetInt(0)
+ continue
+ }
+ killPos(f)
+ }
+ }
+}
+
+// A Rename describes a single renaming.
+type rename struct {
+ OldImport string // only apply rename if this import is present
+ NewImport string // add this import during rewrite
+ Old string // old name: p.T or *p.T
+ New string // new name: p.T or *p.T
+}
+
+func renameFix(tab []rename) func(*ast.File) bool {
+ return func(f *ast.File) bool {
+ return renameFixTab(f, tab)
+ }
+}
+
+func parseName(s string) (ptr bool, pkg, nam string) {
+ i := strings.Index(s, ".")
+ if i < 0 {
+ panic("parseName: invalid name " + s)
+ }
+ if strings.HasPrefix(s, "*") {
+ ptr = true
+ s = s[1:]
+ i--
+ }
+ pkg = s[:i]
+ nam = s[i+1:]
+ return
+}
+
+func renameFixTab(f *ast.File, tab []rename) bool {
+ fixed := false
+ added := map[string]bool{}
+ check := map[string]bool{}
+ for _, t := range tab {
+ if !imports(f, t.OldImport) {
+ continue
+ }
+ optr, opkg, onam := parseName(t.Old)
+ walk(f, func(n interface{}) {
+ np, ok := n.(*ast.Expr)
+ if !ok {
+ return
+ }
+ x := *np
+ if optr {
+ p, ok := x.(*ast.StarExpr)
+ if !ok {
+ return
+ }
+ x = p.X
+ }
+ if !isPkgDot(x, opkg, onam) {
+ return
+ }
+ if t.NewImport != "" && !added[t.NewImport] {
+ addImport(f, t.NewImport)
+ added[t.NewImport] = true
+ }
+ *np = expr(t.New)
+ check[t.OldImport] = true
+ fixed = true
+ })
+ }
+
+ for ipath := range check {
+ if !usesImport(f, ipath) {
+ deleteImport(f, ipath)
+ }
+ }
+ return fixed
+}
diff --git a/src/cmd/fix/go1pkgrename.go b/src/cmd/fix/go1pkgrename.go
new file mode 100644
index 000000000..f701f62f0
--- /dev/null
+++ b/src/cmd/fix/go1pkgrename.go
@@ -0,0 +1,146 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "go/ast"
+ "strings"
+)
+
+func init() {
+ register(go1pkgrenameFix)
+}
+
+var go1pkgrenameFix = fix{
+ "go1rename",
+ "2011-11-08",
+ go1pkgrename,
+ `Rewrite imports for packages moved during transition to Go 1.
+
+http://codereview.appspot.com/5316078
+`,
+}
+
+var go1PackageRenames = []struct{ old, new string }{
+ {"asn1", "encoding/asn1"},
+ {"big", "math/big"},
+ {"cmath", "math/cmplx"},
+ {"csv", "encoding/csv"},
+ {"exec", "os/exec"},
+ {"exp/template/html", "html/template"},
+ {"gob", "encoding/gob"},
+ {"http", "net/http"},
+ {"http/cgi", "net/http/cgi"},
+ {"http/fcgi", "net/http/fcgi"},
+ {"http/httptest", "net/http/httptest"},
+ {"http/pprof", "net/http/pprof"},
+ {"json", "encoding/json"},
+ {"mail", "net/mail"},
+ {"rpc", "net/rpc"},
+ {"rpc/jsonrpc", "net/rpc/jsonrpc"},
+ {"scanner", "text/scanner"},
+ {"smtp", "net/smtp"},
+ {"syslog", "log/syslog"},
+ {"tabwriter", "text/tabwriter"},
+ {"template", "text/template"},
+ {"template/parse", "text/template/parse"},
+ {"rand", "math/rand"},
+ {"url", "net/url"},
+ {"utf16", "unicode/utf16"},
+ {"utf8", "unicode/utf8"},
+ {"xml", "encoding/xml"},
+
+ // go.crypto sub-repository
+ {"crypto/bcrypt", "code.google.com/p/go.crypto/bcrypt"},
+ {"crypto/blowfish", "code.google.com/p/go.crypto/blowfish"},
+ {"crypto/cast5", "code.google.com/p/go.crypto/cast5"},
+ {"crypto/md4", "code.google.com/p/go.crypto/md4"},
+ {"crypto/ocsp", "code.google.com/p/go.crypto/ocsp"},
+ {"crypto/openpgp", "code.google.com/p/go.crypto/openpgp"},
+ {"crypto/openpgp/armor", "code.google.com/p/go.crypto/openpgp/armor"},
+ {"crypto/openpgp/elgamal", "code.google.com/p/go.crypto/openpgp/elgamal"},
+ {"crypto/openpgp/errors", "code.google.com/p/go.crypto/openpgp/errors"},
+ {"crypto/openpgp/packet", "code.google.com/p/go.crypto/openpgp/packet"},
+ {"crypto/openpgp/s2k", "code.google.com/p/go.crypto/openpgp/s2k"},
+ {"crypto/ripemd160", "code.google.com/p/go.crypto/ripemd160"},
+ {"crypto/twofish", "code.google.com/p/go.crypto/twofish"},
+ {"crypto/xtea", "code.google.com/p/go.crypto/xtea"},
+ {"exp/ssh", "code.google.com/p/go.crypto/ssh"},
+
+ // go.image sub-repository
+ {"image/bmp", "code.google.com/p/go.image/bmp"},
+ {"image/tiff", "code.google.com/p/go.image/tiff"},
+
+ // go.net sub-repository
+ {"net/dict", "code.google.com/p/go.net/dict"},
+ {"net/websocket", "code.google.com/p/go.net/websocket"},
+ {"exp/spdy", "code.google.com/p/go.net/spdy"},
+ {"http/spdy", "code.google.com/p/go.net/spdy"},
+
+ // go.codereview sub-repository
+ {"encoding/git85", "code.google.com/p/go.codereview/git85"},
+ {"patch", "code.google.com/p/go.codereview/patch"},
+
+ // exp
+ {"ebnf", "exp/ebnf"},
+ {"go/types", "exp/types"},
+
+ // deleted
+ {"container/vector", ""},
+ {"exp/datafmt", ""},
+ {"go/typechecker", ""},
+ {"old/netchan", ""},
+ {"old/regexp", ""},
+ {"old/template", ""},
+ {"try", ""},
+}
+
+var go1PackageNameRenames = []struct{ newPath, old, new string }{
+ {"html/template", "html", "template"},
+ {"math/cmplx", "cmath", "cmplx"},
+}
+
+func go1pkgrename(f *ast.File) bool {
+ fixed := false
+
+ // First update the imports.
+ for _, rename := range go1PackageRenames {
+ spec := importSpec(f, rename.old)
+ if spec == nil {
+ continue
+ }
+ if rename.new == "" {
+ warn(spec.Pos(), "package %q has been deleted in Go 1", rename.old)
+ continue
+ }
+ if rewriteImport(f, rename.old, rename.new) {
+ fixed = true
+ }
+ if strings.HasPrefix(rename.new, "exp/") {
+ warn(spec.Pos(), "package %q is not part of Go 1", rename.new)
+ }
+ }
+ if !fixed {
+ return false
+ }
+
+ // Now update the package names used by importers.
+ for _, rename := range go1PackageNameRenames {
+ // These are rare packages, so do the import test before walking.
+ if imports(f, rename.newPath) {
+ walk(f, func(n interface{}) {
+ if sel, ok := n.(*ast.SelectorExpr); ok {
+ if isTopName(sel.X, rename.old) {
+ // We know Sel.X is an Ident.
+ sel.X.(*ast.Ident).Name = rename.new
+ return
+ }
+ }
+ })
+ }
+ }
+
+ return fixed
+}
diff --git a/src/cmd/fix/go1pkgrename_test.go b/src/cmd/fix/go1pkgrename_test.go
new file mode 100644
index 000000000..840e443b0
--- /dev/null
+++ b/src/cmd/fix/go1pkgrename_test.go
@@ -0,0 +1,139 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func init() {
+ addTestCases(go1pkgrenameTests, go1pkgrename)
+}
+
+var go1pkgrenameTests = []testCase{
+ {
+ Name: "go1rename.0",
+ In: `package main
+
+import (
+ "asn1"
+ "big"
+ "cmath"
+ "csv"
+ "exec"
+ "exp/template/html"
+ "gob"
+ "http"
+ "http/cgi"
+ "http/fcgi"
+ "http/httptest"
+ "http/pprof"
+ "json"
+ "mail"
+ "rand"
+ "rpc"
+ "rpc/jsonrpc"
+ "scanner"
+ "smtp"
+ "syslog"
+ "tabwriter"
+ "template"
+ "template/parse"
+ "url"
+ "utf16"
+ "utf8"
+ "xml"
+
+ "crypto/bcrypt"
+)
+`,
+ Out: `package main
+
+import (
+ "encoding/asn1"
+ "encoding/csv"
+ "encoding/gob"
+ "encoding/json"
+ "encoding/xml"
+ "html/template"
+ "log/syslog"
+ "math/big"
+ "math/cmplx"
+ "math/rand"
+ "net/http"
+ "net/http/cgi"
+ "net/http/fcgi"
+ "net/http/httptest"
+ "net/http/pprof"
+ "net/mail"
+ "net/rpc"
+ "net/rpc/jsonrpc"
+ "net/smtp"
+ "net/url"
+ "os/exec"
+ "text/scanner"
+ "text/tabwriter"
+ "text/template"
+ "text/template/parse"
+ "unicode/utf16"
+ "unicode/utf8"
+
+ "code.google.com/p/go.crypto/bcrypt"
+)
+`,
+ },
+ {
+ Name: "go1rename.1",
+ In: `package main
+
+import "cmath"
+import poot "exp/template/html"
+
+import (
+ "ebnf"
+ "old/regexp"
+)
+
+var _ = cmath.Sin
+var _ = poot.Poot
+`,
+ Out: `package main
+
+import "math/cmplx"
+import poot "html/template"
+
+import (
+ "exp/ebnf"
+ "old/regexp"
+)
+
+var _ = cmplx.Sin
+var _ = poot.Poot
+`,
+ },
+ {
+ Name: "go1rename.2",
+ In: `package foo
+
+import (
+ "fmt"
+ "http"
+ "url"
+
+ "google/secret/project/go"
+)
+
+func main() {}
+`,
+ Out: `package foo
+
+import (
+ "fmt"
+ "net/http"
+ "net/url"
+
+ "google/secret/project/go"
+)
+
+func main() {}
+`,
+ },
+}
diff --git a/src/cmd/fix/go1rename.go b/src/cmd/fix/go1rename.go
new file mode 100644
index 000000000..9266c749c
--- /dev/null
+++ b/src/cmd/fix/go1rename.go
@@ -0,0 +1,167 @@
+// 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
+
+func init() {
+ register(go1renameFix)
+}
+
+var go1renameFix = fix{
+ "go1rename",
+ "2012-02-12",
+ renameFix(go1renameReplace),
+ `Rewrite package-level names that have been renamed in Go 1.
+
+http://codereview.appspot.com/5625045/
+http://codereview.appspot.com/5672072/
+`,
+}
+
+var go1renameReplace = []rename{
+ {
+ OldImport: "crypto/aes",
+ NewImport: "crypto/cipher",
+ Old: "*aes.Cipher",
+ New: "cipher.Block",
+ },
+ {
+ OldImport: "crypto/des",
+ NewImport: "crypto/cipher",
+ Old: "*des.Cipher",
+ New: "cipher.Block",
+ },
+ {
+ OldImport: "crypto/des",
+ NewImport: "crypto/cipher",
+ Old: "*des.TripleDESCipher",
+ New: "cipher.Block",
+ },
+ {
+ OldImport: "encoding/json",
+ NewImport: "",
+ Old: "json.MarshalForHTML",
+ New: "json.Marshal",
+ },
+ {
+ OldImport: "net/url",
+ NewImport: "",
+ Old: "url.ParseWithReference",
+ New: "url.Parse",
+ },
+ {
+ OldImport: "net/url",
+ NewImport: "",
+ Old: "url.ParseRequest",
+ New: "url.ParseRequestURI",
+ },
+ {
+ OldImport: "os",
+ NewImport: "syscall",
+ Old: "os.Exec",
+ New: "syscall.Exec",
+ },
+ {
+ OldImport: "runtime",
+ NewImport: "",
+ Old: "runtime.Cgocalls",
+ New: "runtime.NumCgoCall",
+ },
+ {
+ OldImport: "runtime",
+ NewImport: "",
+ Old: "runtime.Goroutines",
+ New: "runtime.NumGoroutine",
+ },
+ {
+ OldImport: "net/http",
+ NewImport: "net/http/httputil",
+ Old: "http.ErrPersistEOF",
+ New: "httputil.ErrPersistEOF",
+ },
+ {
+ OldImport: "net/http",
+ NewImport: "net/http/httputil",
+ Old: "http.ErrPipeline",
+ New: "httputil.ErrPipeline",
+ },
+ {
+ OldImport: "net/http",
+ NewImport: "net/http/httputil",
+ Old: "http.ErrClosed",
+ New: "httputil.ErrClosed",
+ },
+ {
+ OldImport: "net/http",
+ NewImport: "net/http/httputil",
+ Old: "http.ServerConn",
+ New: "httputil.ServerConn",
+ },
+ {
+ OldImport: "net/http",
+ NewImport: "net/http/httputil",
+ Old: "http.ClientConn",
+ New: "httputil.ClientConn",
+ },
+ {
+ OldImport: "net/http",
+ NewImport: "net/http/httputil",
+ Old: "http.NewChunkedReader",
+ New: "httputil.NewChunkedReader",
+ },
+ {
+ OldImport: "net/http",
+ NewImport: "net/http/httputil",
+ Old: "http.NewChunkedWriter",
+ New: "httputil.NewChunkedWriter",
+ },
+ {
+ OldImport: "net/http",
+ NewImport: "net/http/httputil",
+ Old: "http.ReverseProxy",
+ New: "httputil.ReverseProxy",
+ },
+ {
+ OldImport: "net/http",
+ NewImport: "net/http/httputil",
+ Old: "http.NewSingleHostReverseProxy",
+ New: "httputil.NewSingleHostReverseProxy",
+ },
+ {
+ OldImport: "net/http",
+ NewImport: "net/http/httputil",
+ Old: "http.DumpRequest",
+ New: "httputil.DumpRequest",
+ },
+ {
+ OldImport: "net/http",
+ NewImport: "net/http/httputil",
+ Old: "http.DumpRequestOut",
+ New: "httputil.DumpRequestOut",
+ },
+ {
+ OldImport: "net/http",
+ NewImport: "net/http/httputil",
+ Old: "http.DumpResponse",
+ New: "httputil.DumpResponse",
+ },
+ {
+ OldImport: "net/http",
+ NewImport: "net/http/httputil",
+ Old: "http.NewClientConn",
+ New: "httputil.NewClientConn",
+ },
+ {
+ OldImport: "net/http",
+ NewImport: "net/http/httputil",
+ Old: "http.NewServerConn",
+ New: "httputil.NewServerConn",
+ },
+ {
+ OldImport: "net/http",
+ NewImport: "net/http/httputil",
+ Old: "http.NewProxyClientConn",
+ New: "httputil.NewProxyClientConn",
+ },
+}
diff --git a/src/cmd/fix/go1rename_test.go b/src/cmd/fix/go1rename_test.go
new file mode 100644
index 000000000..90219ba71
--- /dev/null
+++ b/src/cmd/fix/go1rename_test.go
@@ -0,0 +1,195 @@
+// 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
+
+func init() {
+ addTestCases(go1renameTests, go1renameFix.f)
+}
+
+var go1renameTests = []testCase{
+ {
+ Name: "go1rename.0",
+ In: `package main
+
+import (
+ "crypto/aes"
+ "crypto/des"
+ "encoding/json"
+ "net/http"
+ "net/url"
+ "os"
+ "runtime"
+)
+
+var (
+ _ *aes.Cipher
+ _ *des.Cipher
+ _ *des.TripleDESCipher
+ _ = json.MarshalForHTML
+ _ = aes.New()
+ _ = url.Parse
+ _ = url.ParseWithReference
+ _ = url.ParseRequest
+ _ = os.Exec
+ _ = runtime.Cgocalls
+ _ = runtime.Goroutines
+ _ = http.ErrPersistEOF
+ _ = http.ErrPipeline
+ _ = http.ErrClosed
+ _ = http.NewSingleHostReverseProxy
+ _ = http.NewChunkedReader
+ _ = http.NewChunkedWriter
+ _ *http.ReverseProxy
+ _ *http.ClientConn
+ _ *http.ServerConn
+)
+`,
+ Out: `package main
+
+import (
+ "crypto/aes"
+ "crypto/cipher"
+ "encoding/json"
+ "net/http/httputil"
+ "net/url"
+ "runtime"
+ "syscall"
+)
+
+var (
+ _ cipher.Block
+ _ cipher.Block
+ _ cipher.Block
+ _ = json.Marshal
+ _ = aes.New()
+ _ = url.Parse
+ _ = url.Parse
+ _ = url.ParseRequestURI
+ _ = syscall.Exec
+ _ = runtime.NumCgoCall
+ _ = runtime.NumGoroutine
+ _ = httputil.ErrPersistEOF
+ _ = httputil.ErrPipeline
+ _ = httputil.ErrClosed
+ _ = httputil.NewSingleHostReverseProxy
+ _ = httputil.NewChunkedReader
+ _ = httputil.NewChunkedWriter
+ _ *httputil.ReverseProxy
+ _ *httputil.ClientConn
+ _ *httputil.ServerConn
+)
+`,
+ },
+ {
+ Name: "httputil.0",
+ In: `package main
+
+import "net/http"
+
+func f() {
+ http.DumpRequest(nil, false)
+ http.DumpRequestOut(nil, false)
+ http.DumpResponse(nil, false)
+ http.NewChunkedReader(nil)
+ http.NewChunkedWriter(nil)
+ http.NewClientConn(nil, nil)
+ http.NewProxyClientConn(nil, nil)
+ http.NewServerConn(nil, nil)
+ http.NewSingleHostReverseProxy(nil)
+}
+`,
+ Out: `package main
+
+import "net/http/httputil"
+
+func f() {
+ httputil.DumpRequest(nil, false)
+ httputil.DumpRequestOut(nil, false)
+ httputil.DumpResponse(nil, false)
+ httputil.NewChunkedReader(nil)
+ httputil.NewChunkedWriter(nil)
+ httputil.NewClientConn(nil, nil)
+ httputil.NewProxyClientConn(nil, nil)
+ httputil.NewServerConn(nil, nil)
+ httputil.NewSingleHostReverseProxy(nil)
+}
+`,
+ },
+ {
+ Name: "httputil.1",
+ In: `package main
+
+import "net/http"
+
+func f() {
+ http.DumpRequest(nil, false)
+ http.DumpRequestOut(nil, false)
+ http.DumpResponse(nil, false)
+ http.NewChunkedReader(nil)
+ http.NewChunkedWriter(nil)
+ http.NewClientConn(nil, nil)
+ http.NewProxyClientConn(nil, nil)
+ http.NewServerConn(nil, nil)
+ http.NewSingleHostReverseProxy(nil)
+}
+`,
+ Out: `package main
+
+import "net/http/httputil"
+
+func f() {
+ httputil.DumpRequest(nil, false)
+ httputil.DumpRequestOut(nil, false)
+ httputil.DumpResponse(nil, false)
+ httputil.NewChunkedReader(nil)
+ httputil.NewChunkedWriter(nil)
+ httputil.NewClientConn(nil, nil)
+ httputil.NewProxyClientConn(nil, nil)
+ httputil.NewServerConn(nil, nil)
+ httputil.NewSingleHostReverseProxy(nil)
+}
+`,
+ },
+ {
+ Name: "httputil.2",
+ In: `package main
+
+import "net/http"
+
+func f() {
+ http.DumpRequest(nil, false)
+ http.DumpRequestOut(nil, false)
+ http.DumpResponse(nil, false)
+ http.NewChunkedReader(nil)
+ http.NewChunkedWriter(nil)
+ http.NewClientConn(nil, nil)
+ http.NewProxyClientConn(nil, nil)
+ http.NewServerConn(nil, nil)
+ http.NewSingleHostReverseProxy(nil)
+ http.Get("")
+}
+`,
+ Out: `package main
+
+import (
+ "net/http"
+ "net/http/httputil"
+)
+
+func f() {
+ httputil.DumpRequest(nil, false)
+ httputil.DumpRequestOut(nil, false)
+ httputil.DumpResponse(nil, false)
+ httputil.NewChunkedReader(nil)
+ httputil.NewChunkedWriter(nil)
+ httputil.NewClientConn(nil, nil)
+ httputil.NewProxyClientConn(nil, nil)
+ httputil.NewServerConn(nil, nil)
+ httputil.NewSingleHostReverseProxy(nil)
+ http.Get("")
+}
+`,
+ },
+}
diff --git a/src/cmd/fix/googlecode.go b/src/cmd/fix/googlecode.go
new file mode 100644
index 000000000..143781a74
--- /dev/null
+++ b/src/cmd/fix/googlecode.go
@@ -0,0 +1,41 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "go/ast"
+ "regexp"
+)
+
+func init() {
+ register(googlecodeFix)
+}
+
+var googlecodeFix = fix{
+ "googlecode",
+ "2011-11-21",
+ googlecode,
+ `Rewrite Google Code imports from the deprecated form
+"foo.googlecode.com/vcs/path" to "code.google.com/p/foo/path".
+`,
+}
+
+var googlecodeRe = regexp.MustCompile(`^([a-z0-9\-]+)\.googlecode\.com/(svn|git|hg)(/[a-z0-9A-Z_.\-/]+)?$`)
+
+func googlecode(f *ast.File) bool {
+ fixed := false
+
+ for _, s := range f.Imports {
+ old := importPath(s)
+ if m := googlecodeRe.FindStringSubmatch(old); m != nil {
+ new := "code.google.com/p/" + m[1] + m[3]
+ if rewriteImport(f, old, new) {
+ fixed = true
+ }
+ }
+ }
+
+ return fixed
+}
diff --git a/src/cmd/fix/googlecode_test.go b/src/cmd/fix/googlecode_test.go
new file mode 100644
index 000000000..c62ee4f32
--- /dev/null
+++ b/src/cmd/fix/googlecode_test.go
@@ -0,0 +1,31 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func init() {
+ addTestCases(googlecodeTests, googlecode)
+}
+
+var googlecodeTests = []testCase{
+ {
+ Name: "googlecode.0",
+ In: `package main
+
+import (
+ "foo.googlecode.com/hg/bar"
+ "go-qux-23.googlecode.com/svn"
+ "zap.googlecode.com/git/some/path"
+)
+`,
+ Out: `package main
+
+import (
+ "code.google.com/p/foo/bar"
+ "code.google.com/p/go-qux-23"
+ "code.google.com/p/zap/some/path"
+)
+`,
+ },
+}
diff --git a/src/cmd/fix/hashsum.go b/src/cmd/fix/hashsum.go
new file mode 100644
index 000000000..0df6ad749
--- /dev/null
+++ b/src/cmd/fix/hashsum.go
@@ -0,0 +1,94 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "go/ast"
+)
+
+func init() {
+ register(hashSumFix)
+}
+
+var hashSumFix = fix{
+ "hashsum",
+ "2011-11-30",
+ hashSumFn,
+ `Pass a nil argument to calls to hash.Sum
+
+This fix rewrites code so that it passes a nil argument to hash.Sum.
+The additional argument will allow callers to avoid an
+allocation in the future.
+
+http://codereview.appspot.com/5448065
+`,
+}
+
+// Type-checking configuration: tell the type-checker this basic
+// information about types, functions, and variables in external packages.
+var hashSumTypeConfig = &TypeConfig{
+ Var: map[string]string{
+ "crypto.MD4": "crypto.Hash",
+ "crypto.MD5": "crypto.Hash",
+ "crypto.SHA1": "crypto.Hash",
+ "crypto.SHA224": "crypto.Hash",
+ "crypto.SHA256": "crypto.Hash",
+ "crypto.SHA384": "crypto.Hash",
+ "crypto.SHA512": "crypto.Hash",
+ "crypto.MD5SHA1": "crypto.Hash",
+ "crypto.RIPEMD160": "crypto.Hash",
+ },
+
+ Func: map[string]string{
+ "adler32.New": "hash.Hash",
+ "crc32.New": "hash.Hash",
+ "crc32.NewIEEE": "hash.Hash",
+ "crc64.New": "hash.Hash",
+ "fnv.New32a": "hash.Hash",
+ "fnv.New32": "hash.Hash",
+ "fnv.New64a": "hash.Hash",
+ "fnv.New64": "hash.Hash",
+ "hmac.New": "hash.Hash",
+ "hmac.NewMD5": "hash.Hash",
+ "hmac.NewSHA1": "hash.Hash",
+ "hmac.NewSHA256": "hash.Hash",
+ "md4.New": "hash.Hash",
+ "md5.New": "hash.Hash",
+ "ripemd160.New": "hash.Hash",
+ "sha1.New224": "hash.Hash",
+ "sha1.New": "hash.Hash",
+ "sha256.New224": "hash.Hash",
+ "sha256.New": "hash.Hash",
+ "sha512.New384": "hash.Hash",
+ "sha512.New": "hash.Hash",
+ },
+
+ Type: map[string]*Type{
+ "crypto.Hash": {
+ Method: map[string]string{
+ "New": "func() hash.Hash",
+ },
+ },
+ },
+}
+
+func hashSumFn(f *ast.File) bool {
+ typeof, _ := typecheck(hashSumTypeConfig, f)
+
+ fixed := false
+
+ walk(f, func(n interface{}) {
+ call, ok := n.(*ast.CallExpr)
+ if ok && len(call.Args) == 0 {
+ sel, ok := call.Fun.(*ast.SelectorExpr)
+ if ok && sel.Sel.Name == "Sum" && typeof[sel.X] == "hash.Hash" {
+ call.Args = append(call.Args, ast.NewIdent("nil"))
+ fixed = true
+ }
+ }
+ })
+
+ return fixed
+}
diff --git a/src/cmd/fix/hashsum_test.go b/src/cmd/fix/hashsum_test.go
new file mode 100644
index 000000000..241af2020
--- /dev/null
+++ b/src/cmd/fix/hashsum_test.go
@@ -0,0 +1,99 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func init() {
+ addTestCases(hashSumTests, hashSumFn)
+}
+
+var hashSumTests = []testCase{
+ {
+ Name: "hashsum.0",
+ In: `package main
+
+import "crypto/sha256"
+
+func f() []byte {
+ h := sha256.New()
+ return h.Sum()
+}
+`,
+ Out: `package main
+
+import "crypto/sha256"
+
+func f() []byte {
+ h := sha256.New()
+ return h.Sum(nil)
+}
+`,
+ },
+
+ {
+ Name: "hashsum.1",
+ In: `package main
+
+func f(h hash.Hash) []byte {
+ return h.Sum()
+}
+`,
+ Out: `package main
+
+func f(h hash.Hash) []byte {
+ return h.Sum(nil)
+}
+`,
+ },
+
+ {
+ Name: "hashsum.0",
+ In: `package main
+
+import "crypto/sha256"
+
+func f() []byte {
+ h := sha256.New()
+ h.Write([]byte("foo"))
+ digest := h.Sum()
+}
+`,
+ Out: `package main
+
+import "crypto/sha256"
+
+func f() []byte {
+ h := sha256.New()
+ h.Write([]byte("foo"))
+ digest := h.Sum(nil)
+}
+`,
+ },
+
+ {
+ Name: "hashsum.0",
+ In: `package main
+
+import _ "crypto/sha256"
+import "crypto"
+
+func f() []byte {
+ hashType := crypto.SHA256
+ h := hashType.New()
+ digest := h.Sum()
+}
+`,
+ Out: `package main
+
+import _ "crypto/sha256"
+import "crypto"
+
+func f() []byte {
+ hashType := crypto.SHA256
+ h := hashType.New()
+ digest := h.Sum(nil)
+}
+`,
+ },
+}
diff --git a/src/cmd/fix/hmacnew.go b/src/cmd/fix/hmacnew.go
new file mode 100644
index 000000000..c0c44ef3e
--- /dev/null
+++ b/src/cmd/fix/hmacnew.go
@@ -0,0 +1,61 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "go/ast"
+
+func init() {
+ register(hmacNewFix)
+}
+
+var hmacNewFix = fix{
+ "hmacnew",
+ "2012-01-19",
+ hmacnew,
+ `Deprecate hmac.NewMD5, hmac.NewSHA1 and hmac.NewSHA256.
+
+This fix rewrites code using hmac.NewMD5, hmac.NewSHA1 and hmac.NewSHA256 to
+use hmac.New:
+
+ hmac.NewMD5(key) -> hmac.New(md5.New, key)
+ hmac.NewSHA1(key) -> hmac.New(sha1.New, key)
+ hmac.NewSHA256(key) -> hmac.New(sha256.New, key)
+
+`,
+}
+
+func hmacnew(f *ast.File) (fixed bool) {
+ if !imports(f, "crypto/hmac") {
+ return
+ }
+
+ walk(f, func(n interface{}) {
+ ce, ok := n.(*ast.CallExpr)
+ if !ok {
+ return
+ }
+
+ var pkg string
+ switch {
+ case isPkgDot(ce.Fun, "hmac", "NewMD5"):
+ pkg = "md5"
+ case isPkgDot(ce.Fun, "hmac", "NewSHA1"):
+ pkg = "sha1"
+ case isPkgDot(ce.Fun, "hmac", "NewSHA256"):
+ pkg = "sha256"
+ default:
+ return
+ }
+
+ addImport(f, "crypto/"+pkg)
+
+ ce.Fun = ast.NewIdent("hmac.New")
+ ce.Args = append([]ast.Expr{ast.NewIdent(pkg + ".New")}, ce.Args...)
+
+ fixed = true
+ })
+
+ return
+}
diff --git a/src/cmd/fix/hmacnew_test.go b/src/cmd/fix/hmacnew_test.go
new file mode 100644
index 000000000..5aeee8573
--- /dev/null
+++ b/src/cmd/fix/hmacnew_test.go
@@ -0,0 +1,107 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func init() {
+ addTestCases(hmacNewTests, hmacnew)
+}
+
+var hmacNewTests = []testCase{
+ {
+ Name: "hmacnew.0",
+ In: `package main
+
+import "crypto/hmac"
+
+var f = hmac.NewSHA1([]byte("some key"))
+`,
+ Out: `package main
+
+import (
+ "crypto/hmac"
+ "crypto/sha1"
+)
+
+var f = hmac.New(sha1.New, []byte("some key"))
+`,
+ },
+ {
+ Name: "hmacnew.1",
+ In: `package main
+
+import "crypto/hmac"
+
+var key = make([]byte, 8)
+var f = hmac.NewSHA1(key)
+`,
+ Out: `package main
+
+import (
+ "crypto/hmac"
+ "crypto/sha1"
+)
+
+var key = make([]byte, 8)
+var f = hmac.New(sha1.New, key)
+`,
+ },
+ {
+ Name: "hmacnew.2",
+ In: `package main
+
+import "crypto/hmac"
+
+var f = hmac.NewMD5([]byte("some key"))
+`,
+ Out: `package main
+
+import (
+ "crypto/hmac"
+ "crypto/md5"
+)
+
+var f = hmac.New(md5.New, []byte("some key"))
+`,
+ },
+ {
+ Name: "hmacnew.3",
+ In: `package main
+
+import "crypto/hmac"
+
+var f = hmac.NewSHA256([]byte("some key"))
+`,
+ Out: `package main
+
+import (
+ "crypto/hmac"
+ "crypto/sha256"
+)
+
+var f = hmac.New(sha256.New, []byte("some key"))
+`,
+ },
+ {
+ Name: "hmacnew.4",
+ In: `package main
+
+import (
+ "crypto/hmac"
+ "crypto/sha1"
+)
+
+var f = hmac.New(sha1.New, []byte("some key"))
+`,
+ Out: `package main
+
+import (
+ "crypto/hmac"
+ "crypto/sha1"
+)
+
+var f = hmac.New(sha1.New, []byte("some key"))
+`,
+ },
+}
diff --git a/src/cmd/fix/htmlerr.go b/src/cmd/fix/htmlerr.go
new file mode 100644
index 000000000..b5105c822
--- /dev/null
+++ b/src/cmd/fix/htmlerr.go
@@ -0,0 +1,47 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "go/ast"
+)
+
+func init() {
+ register(htmlerrFix)
+}
+
+var htmlerrFix = fix{
+ "htmlerr",
+ "2011-11-04",
+ htmlerr,
+ `Rename html's Tokenizer.Error method to Err.
+
+http://codereview.appspot.com/5327064/
+`,
+}
+
+var htmlerrTypeConfig = &TypeConfig{
+ Func: map[string]string{
+ "html.NewTokenizer": "html.Tokenizer",
+ },
+}
+
+func htmlerr(f *ast.File) bool {
+ if !imports(f, "html") {
+ return false
+ }
+
+ typeof, _ := typecheck(htmlerrTypeConfig, f)
+
+ fixed := false
+ walk(f, func(n interface{}) {
+ s, ok := n.(*ast.SelectorExpr)
+ if ok && typeof[s.X] == "html.Tokenizer" && s.Sel.Name == "Error" {
+ s.Sel.Name = "Err"
+ fixed = true
+ }
+ })
+ return fixed
+}
diff --git a/src/cmd/fix/htmlerr_test.go b/src/cmd/fix/htmlerr_test.go
new file mode 100644
index 000000000..043abc42a
--- /dev/null
+++ b/src/cmd/fix/htmlerr_test.go
@@ -0,0 +1,39 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func init() {
+ addTestCases(htmlerrTests, htmlerr)
+}
+
+var htmlerrTests = []testCase{
+ {
+ Name: "htmlerr.0",
+ In: `package main
+
+import (
+ "html"
+)
+
+func f() {
+ e := errors.New("")
+ t := html.NewTokenizer(r)
+ _, _ = e.Error(), t.Error()
+}
+`,
+ Out: `package main
+
+import (
+ "html"
+)
+
+func f() {
+ e := errors.New("")
+ t := html.NewTokenizer(r)
+ _, _ = e.Error(), t.Err()
+}
+`,
+ },
+}
diff --git a/src/cmd/gofix/httpfinalurl.go b/src/cmd/fix/httpfinalurl.go
index 9e6cbf6bc..49b9f1c51 100644
--- a/src/cmd/gofix/httpfinalurl.go
+++ b/src/cmd/fix/httpfinalurl.go
@@ -8,8 +8,13 @@ import (
"go/ast"
)
+func init() {
+ register(httpFinalURLFix)
+}
+
var httpFinalURLFix = fix{
"httpfinalurl",
+ "2011-05-13",
httpfinalurl,
`Adapt http Get calls to not have a finalURL result parameter.
@@ -17,10 +22,6 @@ http://codereview.appspot.com/4535056/
`,
}
-func init() {
- register(httpFinalURLFix)
-}
-
func httpfinalurl(f *ast.File) bool {
if !imports(f, "http") {
return false
diff --git a/src/cmd/gofix/httpfinalurl_test.go b/src/cmd/fix/httpfinalurl_test.go
index 9e7d6242d..9249f7e18 100644
--- a/src/cmd/gofix/httpfinalurl_test.go
+++ b/src/cmd/fix/httpfinalurl_test.go
@@ -5,7 +5,7 @@
package main
func init() {
- addTestCases(httpfinalurlTests)
+ addTestCases(httpfinalurlTests, httpfinalurl)
}
var httpfinalurlTests = []testCase{
diff --git a/src/cmd/gofix/httpfs.go b/src/cmd/fix/httpfs.go
index 7f2765680..d87b30f9d 100644
--- a/src/cmd/gofix/httpfs.go
+++ b/src/cmd/fix/httpfs.go
@@ -9,8 +9,13 @@ import (
"go/token"
)
+func init() {
+ register(httpFileSystemFix)
+}
+
var httpFileSystemFix = fix{
"httpfs",
+ "2011-06-27",
httpfs,
`Adapt http FileServer to take a FileSystem.
@@ -18,10 +23,6 @@ http://codereview.appspot.com/4629047 http FileSystem interface
`,
}
-func init() {
- register(httpFileSystemFix)
-}
-
func httpfs(f *ast.File) bool {
if !imports(f, "http") {
return false
@@ -38,7 +39,10 @@ func httpfs(f *ast.File) bool {
}
dir, prefix := call.Args[0], call.Args[1]
call.Args = []ast.Expr{&ast.CallExpr{
- Fun: &ast.SelectorExpr{ast.NewIdent("http"), ast.NewIdent("Dir")},
+ Fun: &ast.SelectorExpr{
+ X: ast.NewIdent("http"),
+ Sel: ast.NewIdent("Dir"),
+ },
Args: []ast.Expr{dir},
}}
wrapInStripHandler := true
@@ -52,7 +56,10 @@ func httpfs(f *ast.File) bool {
call.Args = []ast.Expr{
prefix,
&ast.CallExpr{
- Fun: &ast.SelectorExpr{ast.NewIdent("http"), ast.NewIdent("FileServer")},
+ Fun: &ast.SelectorExpr{
+ X: ast.NewIdent("http"),
+ Sel: ast.NewIdent("FileServer"),
+ },
Args: call.Args,
},
}
diff --git a/src/cmd/gofix/httpfs_test.go b/src/cmd/fix/httpfs_test.go
index d1804e93b..dd8ef2cfd 100644
--- a/src/cmd/gofix/httpfs_test.go
+++ b/src/cmd/fix/httpfs_test.go
@@ -5,7 +5,7 @@
package main
func init() {
- addTestCases(httpFileSystemTests)
+ addTestCases(httpFileSystemTests, httpfs)
}
var httpFileSystemTests = []testCase{
diff --git a/src/cmd/gofix/httpheaders.go b/src/cmd/fix/httpheaders.go
index 8a9080e8e..15c21ac86 100644
--- a/src/cmd/gofix/httpheaders.go
+++ b/src/cmd/fix/httpheaders.go
@@ -8,8 +8,13 @@ import (
"go/ast"
)
+func init() {
+ register(httpHeadersFix)
+}
+
var httpHeadersFix = fix{
"httpheaders",
+ "2011-06-16",
httpheaders,
`Rename http Referer, UserAgent, Cookie, SetCookie, which are now methods.
@@ -17,10 +22,6 @@ http://codereview.appspot.com/4620049/
`,
}
-func init() {
- register(httpHeadersFix)
-}
-
func httpheaders(f *ast.File) bool {
if !imports(f, "http") {
return false
@@ -35,7 +36,7 @@ func httpheaders(f *ast.File) bool {
})
fixed := false
- typeof := typecheck(headerTypeConfig, f)
+ typeof, _ := typecheck(headerTypeConfig, f)
walk(f, func(ni interface{}) {
switch n := ni.(type) {
case *ast.SelectorExpr:
@@ -60,7 +61,7 @@ func httpheaders(f *ast.File) bool {
var headerTypeConfig = &TypeConfig{
Type: map[string]*Type{
- "*http.Request": &Type{},
- "*http.Response": &Type{},
+ "*http.Request": {},
+ "*http.Response": {},
},
}
diff --git a/src/cmd/gofix/httpheaders_test.go b/src/cmd/fix/httpheaders_test.go
index cc82b5893..37506b82d 100644
--- a/src/cmd/gofix/httpheaders_test.go
+++ b/src/cmd/fix/httpheaders_test.go
@@ -5,7 +5,7 @@
package main
func init() {
- addTestCases(httpHeadersTests)
+ addTestCases(httpHeadersTests, httpheaders)
}
var httpHeadersTests = []testCase{
diff --git a/src/cmd/gofix/httpserver.go b/src/cmd/fix/httpserver.go
index 37866e88b..7aa651786 100644
--- a/src/cmd/gofix/httpserver.go
+++ b/src/cmd/fix/httpserver.go
@@ -9,8 +9,13 @@ import (
"go/token"
)
+func init() {
+ register(httpserverFix)
+}
+
var httpserverFix = fix{
"httpserver",
+ "2011-03-15",
httpserver,
`Adapt http server methods and functions to changes
made to the http ResponseWriter interface.
@@ -22,10 +27,6 @@ http://codereview.appspot.com/4248075 RemoteAddr, UsingTLS
`,
}
-func init() {
- register(httpserverFix)
-}
-
func httpserver(f *ast.File) bool {
if !imports(f, "http") {
return false
diff --git a/src/cmd/gofix/httpserver_test.go b/src/cmd/fix/httpserver_test.go
index 89bb4fa71..b6ddff27e 100644
--- a/src/cmd/gofix/httpserver_test.go
+++ b/src/cmd/fix/httpserver_test.go
@@ -5,7 +5,7 @@
package main
func init() {
- addTestCases(httpserverTests)
+ addTestCases(httpserverTests, httpserver)
}
var httpserverTests = []testCase{
diff --git a/src/cmd/fix/imagecolor.go b/src/cmd/fix/imagecolor.go
new file mode 100644
index 000000000..1aac40a6f
--- /dev/null
+++ b/src/cmd/fix/imagecolor.go
@@ -0,0 +1,85 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "go/ast"
+)
+
+func init() {
+ register(imagecolorFix)
+}
+
+var imagecolorFix = fix{
+ "imagecolor",
+ "2011-10-04",
+ imagecolor,
+ `Adapt code to types moved from image to color.
+
+http://codereview.appspot.com/5132048
+`,
+}
+
+var colorRenames = []struct{ in, out string }{
+ {"Color", "Color"},
+ {"ColorModel", "Model"},
+ {"ColorModelFunc", "ModelFunc"},
+ {"PalettedColorModel", "Palette"},
+
+ {"RGBAColor", "RGBA"},
+ {"RGBA64Color", "RGBA64"},
+ {"NRGBAColor", "NRGBA"},
+ {"NRGBA64Color", "NRGBA64"},
+ {"AlphaColor", "Alpha"},
+ {"Alpha16Color", "Alpha16"},
+ {"GrayColor", "Gray"},
+ {"Gray16Color", "Gray16"},
+
+ {"RGBAColorModel", "RGBAModel"},
+ {"RGBA64ColorModel", "RGBA64Model"},
+ {"NRGBAColorModel", "NRGBAModel"},
+ {"NRGBA64ColorModel", "NRGBA64Model"},
+ {"AlphaColorModel", "AlphaModel"},
+ {"Alpha16ColorModel", "Alpha16Model"},
+ {"GrayColorModel", "GrayModel"},
+ {"Gray16ColorModel", "Gray16Model"},
+}
+
+func imagecolor(f *ast.File) (fixed bool) {
+ if !imports(f, "image") {
+ return
+ }
+
+ walk(f, func(n interface{}) {
+ s, ok := n.(*ast.SelectorExpr)
+
+ if !ok || !isTopName(s.X, "image") {
+ return
+ }
+
+ switch sel := s.Sel.String(); {
+ case sel == "ColorImage":
+ s.Sel = &ast.Ident{Name: "Uniform"}
+ fixed = true
+ case sel == "NewColorImage":
+ s.Sel = &ast.Ident{Name: "NewUniform"}
+ fixed = true
+ default:
+ for _, rename := range colorRenames {
+ if sel == rename.in {
+ addImport(f, "image/color")
+ s.X.(*ast.Ident).Name = "color"
+ s.Sel.Name = rename.out
+ fixed = true
+ }
+ }
+ }
+ })
+
+ if fixed && !usesImport(f, "image") {
+ deleteImport(f, "image")
+ }
+ return
+}
diff --git a/src/cmd/fix/imagecolor_test.go b/src/cmd/fix/imagecolor_test.go
new file mode 100644
index 000000000..c62365481
--- /dev/null
+++ b/src/cmd/fix/imagecolor_test.go
@@ -0,0 +1,126 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func init() {
+ addTestCases(colorTests, imagecolor)
+}
+
+var colorTests = []testCase{
+ {
+ Name: "color.0",
+ In: `package main
+
+import (
+ "image"
+)
+
+var (
+ _ image.Image
+ _ image.RGBA
+ _ image.Black
+ _ image.Color
+ _ image.ColorModel
+ _ image.ColorModelFunc
+ _ image.PalettedColorModel
+ _ image.RGBAColor
+ _ image.RGBA64Color
+ _ image.NRGBAColor
+ _ image.NRGBA64Color
+ _ image.AlphaColor
+ _ image.Alpha16Color
+ _ image.GrayColor
+ _ image.Gray16Color
+)
+
+func f() {
+ _ = image.RGBAColorModel
+ _ = image.RGBA64ColorModel
+ _ = image.NRGBAColorModel
+ _ = image.NRGBA64ColorModel
+ _ = image.AlphaColorModel
+ _ = image.Alpha16ColorModel
+ _ = image.GrayColorModel
+ _ = image.Gray16ColorModel
+}
+`,
+ Out: `package main
+
+import (
+ "image"
+ "image/color"
+)
+
+var (
+ _ image.Image
+ _ image.RGBA
+ _ image.Black
+ _ color.Color
+ _ color.Model
+ _ color.ModelFunc
+ _ color.Palette
+ _ color.RGBA
+ _ color.RGBA64
+ _ color.NRGBA
+ _ color.NRGBA64
+ _ color.Alpha
+ _ color.Alpha16
+ _ color.Gray
+ _ color.Gray16
+)
+
+func f() {
+ _ = color.RGBAModel
+ _ = color.RGBA64Model
+ _ = color.NRGBAModel
+ _ = color.NRGBA64Model
+ _ = color.AlphaModel
+ _ = color.Alpha16Model
+ _ = color.GrayModel
+ _ = color.Gray16Model
+}
+`,
+ },
+ {
+ Name: "color.1",
+ In: `package main
+
+import (
+ "fmt"
+ "image"
+)
+
+func f() {
+ fmt.Println(image.RGBAColor{1, 2, 3, 4}.RGBA())
+}
+`,
+ Out: `package main
+
+import (
+ "fmt"
+ "image/color"
+)
+
+func f() {
+ fmt.Println(color.RGBA{1, 2, 3, 4}.RGBA())
+}
+`,
+ },
+ {
+ Name: "color.2",
+ In: `package main
+
+import "image"
+
+var c *image.ColorImage = image.NewColorImage(nil)
+`,
+ Out: `package main
+
+import "image"
+
+var c *image.Uniform = image.NewUniform(nil)
+`,
+ },
+}
diff --git a/src/cmd/fix/imagenew.go b/src/cmd/fix/imagenew.go
new file mode 100644
index 000000000..b4e36d4f0
--- /dev/null
+++ b/src/cmd/fix/imagenew.go
@@ -0,0 +1,83 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "go/ast"
+)
+
+func init() {
+ register(imagenewFix)
+}
+
+var imagenewFix = fix{
+ "imagenew",
+ "2011-09-14",
+ imagenew,
+ `Adapt image.NewXxx calls to pass an image.Rectangle instead of (w, h int).
+
+http://codereview.appspot.com/4964073
+`,
+}
+
+var imagenewFuncs = map[string]bool{
+ "NewRGBA": true,
+ "NewRGBA64": true,
+ "NewNRGBA": true,
+ "NewNRGBA64": true,
+ "NewAlpha": true,
+ "NewAlpha16": true,
+ "NewGray": true,
+ "NewGray16": true,
+}
+
+func imagenew(f *ast.File) bool {
+ if !imports(f, "image") {
+ return false
+ }
+
+ fixed := false
+ walk(f, func(n interface{}) {
+ call, ok := n.(*ast.CallExpr)
+ if !ok {
+ return
+ }
+ isNewFunc := false
+ for newFunc := range imagenewFuncs {
+ if len(call.Args) == 2 && isPkgDot(call.Fun, "image", newFunc) {
+ isNewFunc = true
+ break
+ }
+ }
+ if len(call.Args) == 3 && isPkgDot(call.Fun, "image", "NewPaletted") {
+ isNewFunc = true
+ }
+ if !isNewFunc {
+ return
+ }
+ // Replace image.NewXxx(w, h) with image.NewXxx(image.Rect(0, 0, w, h)).
+ rectArgs := []ast.Expr{
+ &ast.BasicLit{Value: "0"},
+ &ast.BasicLit{Value: "0"},
+ }
+ rectArgs = append(rectArgs, call.Args[:2]...)
+ rect := []ast.Expr{
+ &ast.CallExpr{
+ Fun: &ast.SelectorExpr{
+ X: &ast.Ident{
+ Name: "image",
+ },
+ Sel: &ast.Ident{
+ Name: "Rect",
+ },
+ },
+ Args: rectArgs,
+ },
+ }
+ call.Args = append(rect, call.Args[2:]...)
+ fixed = true
+ })
+ return fixed
+}
diff --git a/src/cmd/fix/imagenew_test.go b/src/cmd/fix/imagenew_test.go
new file mode 100644
index 000000000..30abed23c
--- /dev/null
+++ b/src/cmd/fix/imagenew_test.go
@@ -0,0 +1,51 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func init() {
+ addTestCases(imagenewTests, imagenew)
+}
+
+var imagenewTests = []testCase{
+ {
+ Name: "imagenew.0",
+ In: `package main
+
+import (
+ "image"
+)
+
+func f() {
+ image.NewRGBA(1, 2)
+ image.NewRGBA64(1, 2)
+ image.NewNRGBA(1, 2)
+ image.NewNRGBA64(1, 2)
+ image.NewAlpha(1, 2)
+ image.NewAlpha16(1, 2)
+ image.NewGray(1, 2)
+ image.NewGray16(1, 2)
+ image.NewPaletted(1, 2, nil)
+}
+`,
+ Out: `package main
+
+import (
+ "image"
+)
+
+func f() {
+ image.NewRGBA(image.Rect(0, 0, 1, 2))
+ image.NewRGBA64(image.Rect(0, 0, 1, 2))
+ image.NewNRGBA(image.Rect(0, 0, 1, 2))
+ image.NewNRGBA64(image.Rect(0, 0, 1, 2))
+ image.NewAlpha(image.Rect(0, 0, 1, 2))
+ image.NewAlpha16(image.Rect(0, 0, 1, 2))
+ image.NewGray(image.Rect(0, 0, 1, 2))
+ image.NewGray16(image.Rect(0, 0, 1, 2))
+ image.NewPaletted(image.Rect(0, 0, 1, 2), nil)
+}
+`,
+ },
+}
diff --git a/src/cmd/fix/imageycbcr.go b/src/cmd/fix/imageycbcr.go
new file mode 100644
index 000000000..41b96d18d
--- /dev/null
+++ b/src/cmd/fix/imageycbcr.go
@@ -0,0 +1,64 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "go/ast"
+)
+
+func init() {
+ register(imageycbcrFix)
+}
+
+var imageycbcrFix = fix{
+ "imageycbcr",
+ "2011-12-20",
+ imageycbcr,
+ `Adapt code to types moved from image/ycbcr to image and image/color.
+
+http://codereview.appspot.com/5493084
+`,
+}
+
+func imageycbcr(f *ast.File) (fixed bool) {
+ if !imports(f, "image/ycbcr") {
+ return
+ }
+
+ walk(f, func(n interface{}) {
+ s, ok := n.(*ast.SelectorExpr)
+
+ if !ok || !isTopName(s.X, "ycbcr") {
+ return
+ }
+
+ switch s.Sel.String() {
+ case "RGBToYCbCr", "YCbCrToRGB":
+ addImport(f, "image/color")
+ s.X.(*ast.Ident).Name = "color"
+ case "YCbCrColor":
+ addImport(f, "image/color")
+ s.X.(*ast.Ident).Name = "color"
+ s.Sel.Name = "YCbCr"
+ case "YCbCrColorModel":
+ addImport(f, "image/color")
+ s.X.(*ast.Ident).Name = "color"
+ s.Sel.Name = "YCbCrModel"
+ case "SubsampleRatio", "SubsampleRatio444", "SubsampleRatio422", "SubsampleRatio420":
+ addImport(f, "image")
+ s.X.(*ast.Ident).Name = "image"
+ s.Sel.Name = "YCbCr" + s.Sel.Name
+ case "YCbCr":
+ addImport(f, "image")
+ s.X.(*ast.Ident).Name = "image"
+ default:
+ return
+ }
+ fixed = true
+ })
+
+ deleteImport(f, "image/ycbcr")
+ return
+}
diff --git a/src/cmd/fix/imageycbcr_test.go b/src/cmd/fix/imageycbcr_test.go
new file mode 100644
index 000000000..23b599dcd
--- /dev/null
+++ b/src/cmd/fix/imageycbcr_test.go
@@ -0,0 +1,54 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func init() {
+ addTestCases(ycbcrTests, imageycbcr)
+}
+
+var ycbcrTests = []testCase{
+ {
+ Name: "ycbcr.0",
+ In: `package main
+
+import (
+ "image/ycbcr"
+)
+
+func f() {
+ _ = ycbcr.RGBToYCbCr
+ _ = ycbcr.YCbCrToRGB
+ _ = ycbcr.YCbCrColorModel
+ var _ ycbcr.YCbCrColor
+ var _ ycbcr.YCbCr
+ var (
+ _ ycbcr.SubsampleRatio = ycbcr.SubsampleRatio444
+ _ ycbcr.SubsampleRatio = ycbcr.SubsampleRatio422
+ _ ycbcr.SubsampleRatio = ycbcr.SubsampleRatio420
+ )
+}
+`,
+ Out: `package main
+
+import (
+ "image"
+ "image/color"
+)
+
+func f() {
+ _ = color.RGBToYCbCr
+ _ = color.YCbCrToRGB
+ _ = color.YCbCrModel
+ var _ color.YCbCr
+ var _ image.YCbCr
+ var (
+ _ image.YCbCrSubsampleRatio = image.YCbCrSubsampleRatio444
+ _ image.YCbCrSubsampleRatio = image.YCbCrSubsampleRatio422
+ _ image.YCbCrSubsampleRatio = image.YCbCrSubsampleRatio420
+ )
+}
+`,
+ },
+}
diff --git a/src/cmd/fix/import_test.go b/src/cmd/fix/import_test.go
new file mode 100644
index 000000000..730119205
--- /dev/null
+++ b/src/cmd/fix/import_test.go
@@ -0,0 +1,458 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "go/ast"
+
+func init() {
+ addTestCases(importTests, nil)
+}
+
+var importTests = []testCase{
+ {
+ Name: "import.0",
+ Fn: addImportFn("os"),
+ In: `package main
+
+import (
+ "os"
+)
+`,
+ Out: `package main
+
+import (
+ "os"
+)
+`,
+ },
+ {
+ Name: "import.1",
+ Fn: addImportFn("os"),
+ In: `package main
+`,
+ Out: `package main
+
+import "os"
+`,
+ },
+ {
+ Name: "import.2",
+ Fn: addImportFn("os"),
+ In: `package main
+
+// Comment
+import "C"
+`,
+ Out: `package main
+
+// Comment
+import "C"
+import "os"
+`,
+ },
+ {
+ Name: "import.3",
+ Fn: addImportFn("os"),
+ In: `package main
+
+// Comment
+import "C"
+
+import (
+ "io"
+ "utf8"
+)
+`,
+ Out: `package main
+
+// Comment
+import "C"
+
+import (
+ "io"
+ "os"
+ "utf8"
+)
+`,
+ },
+ {
+ Name: "import.4",
+ Fn: deleteImportFn("os"),
+ In: `package main
+
+import (
+ "os"
+)
+`,
+ Out: `package main
+`,
+ },
+ {
+ Name: "import.5",
+ Fn: deleteImportFn("os"),
+ In: `package main
+
+// Comment
+import "C"
+import "os"
+`,
+ Out: `package main
+
+// Comment
+import "C"
+`,
+ },
+ {
+ Name: "import.6",
+ Fn: deleteImportFn("os"),
+ In: `package main
+
+// Comment
+import "C"
+
+import (
+ "io"
+ "os"
+ "utf8"
+)
+`,
+ Out: `package main
+
+// Comment
+import "C"
+
+import (
+ "io"
+ "utf8"
+)
+`,
+ },
+ {
+ Name: "import.7",
+ Fn: deleteImportFn("io"),
+ In: `package main
+
+import (
+ "io" // a
+ "os" // b
+ "utf8" // c
+)
+`,
+ Out: `package main
+
+import (
+ // a
+ "os" // b
+ "utf8" // c
+)
+`,
+ },
+ {
+ Name: "import.8",
+ Fn: deleteImportFn("os"),
+ In: `package main
+
+import (
+ "io" // a
+ "os" // b
+ "utf8" // c
+)
+`,
+ Out: `package main
+
+import (
+ "io" // a
+ // b
+ "utf8" // c
+)
+`,
+ },
+ {
+ Name: "import.9",
+ Fn: deleteImportFn("utf8"),
+ In: `package main
+
+import (
+ "io" // a
+ "os" // b
+ "utf8" // c
+)
+`,
+ Out: `package main
+
+import (
+ "io" // a
+ "os" // b
+ // c
+)
+`,
+ },
+ {
+ Name: "import.10",
+ Fn: deleteImportFn("io"),
+ In: `package main
+
+import (
+ "io"
+ "os"
+ "utf8"
+)
+`,
+ Out: `package main
+
+import (
+ "os"
+ "utf8"
+)
+`,
+ },
+ {
+ Name: "import.11",
+ Fn: deleteImportFn("os"),
+ In: `package main
+
+import (
+ "io"
+ "os"
+ "utf8"
+)
+`,
+ Out: `package main
+
+import (
+ "io"
+ "utf8"
+)
+`,
+ },
+ {
+ Name: "import.12",
+ Fn: deleteImportFn("utf8"),
+ In: `package main
+
+import (
+ "io"
+ "os"
+ "utf8"
+)
+`,
+ Out: `package main
+
+import (
+ "io"
+ "os"
+)
+`,
+ },
+ {
+ Name: "import.13",
+ Fn: rewriteImportFn("utf8", "encoding/utf8"),
+ In: `package main
+
+import (
+ "io"
+ "os"
+ "utf8" // thanks ken
+)
+`,
+ Out: `package main
+
+import (
+ "encoding/utf8" // thanks ken
+ "io"
+ "os"
+)
+`,
+ },
+ {
+ Name: "import.14",
+ Fn: rewriteImportFn("asn1", "encoding/asn1"),
+ In: `package main
+
+import (
+ "asn1"
+ "crypto"
+ "crypto/rsa"
+ _ "crypto/sha1"
+ "crypto/x509"
+ "crypto/x509/pkix"
+ "time"
+)
+
+var x = 1
+`,
+ Out: `package main
+
+import (
+ "crypto"
+ "crypto/rsa"
+ _ "crypto/sha1"
+ "crypto/x509"
+ "crypto/x509/pkix"
+ "encoding/asn1"
+ "time"
+)
+
+var x = 1
+`,
+ },
+ {
+ Name: "import.15",
+ Fn: rewriteImportFn("url", "net/url"),
+ In: `package main
+
+import (
+ "bufio"
+ "net"
+ "path"
+ "url"
+)
+
+var x = 1 // comment on x, not on url
+`,
+ Out: `package main
+
+import (
+ "bufio"
+ "net"
+ "net/url"
+ "path"
+)
+
+var x = 1 // comment on x, not on url
+`,
+ },
+ {
+ Name: "import.16",
+ Fn: rewriteImportFn("http", "net/http", "template", "text/template"),
+ In: `package main
+
+import (
+ "flag"
+ "http"
+ "log"
+ "template"
+)
+
+var addr = flag.String("addr", ":1718", "http service address") // Q=17, R=18
+`,
+ Out: `package main
+
+import (
+ "flag"
+ "log"
+ "net/http"
+ "text/template"
+)
+
+var addr = flag.String("addr", ":1718", "http service address") // Q=17, R=18
+`,
+ },
+ {
+ Name: "import.17",
+ Fn: addImportFn("x/y/z", "x/a/c"),
+ In: `package main
+
+// Comment
+import "C"
+
+import (
+ "a"
+ "b"
+
+ "x/w"
+
+ "d/f"
+)
+`,
+ Out: `package main
+
+// Comment
+import "C"
+
+import (
+ "a"
+ "b"
+
+ "x/a/c"
+ "x/w"
+ "x/y/z"
+
+ "d/f"
+)
+`,
+ },
+ {
+ Name: "import.18",
+ Fn: addDelImportFn("e", "o"),
+ In: `package main
+
+import (
+ "f"
+ "o"
+ "z"
+)
+`,
+ Out: `package main
+
+import (
+ "e"
+ "f"
+ "z"
+)
+`,
+ },
+}
+
+func addImportFn(path ...string) func(*ast.File) bool {
+ return func(f *ast.File) bool {
+ fixed := false
+ for _, p := range path {
+ if !imports(f, p) {
+ addImport(f, p)
+ fixed = true
+ }
+ }
+ return fixed
+ }
+}
+
+func deleteImportFn(path string) func(*ast.File) bool {
+ return func(f *ast.File) bool {
+ if imports(f, path) {
+ deleteImport(f, path)
+ return true
+ }
+ return false
+ }
+}
+
+func addDelImportFn(p1 string, p2 string) func(*ast.File) bool {
+ return func(f *ast.File) bool {
+ fixed := false
+ if !imports(f, p1) {
+ addImport(f, p1)
+ fixed = true
+ }
+ if imports(f, p2) {
+ deleteImport(f, p2)
+ fixed = true
+ }
+ return fixed
+ }
+}
+
+func rewriteImportFn(oldnew ...string) func(*ast.File) bool {
+ return func(f *ast.File) bool {
+ fixed := false
+ for i := 0; i < len(oldnew); i += 2 {
+ if imports(f, oldnew[i]) {
+ rewriteImport(f, oldnew[i], oldnew[i+1])
+ fixed = true
+ }
+ }
+ return fixed
+ }
+}
diff --git a/src/cmd/fix/iocopyn.go b/src/cmd/fix/iocopyn.go
new file mode 100644
index 000000000..720f3c689
--- /dev/null
+++ b/src/cmd/fix/iocopyn.go
@@ -0,0 +1,41 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "go/ast"
+)
+
+func init() {
+ register(ioCopyNFix)
+}
+
+var ioCopyNFix = fix{
+ "iocopyn",
+ "2011-09-30",
+ ioCopyN,
+ `Rename io.Copyn to io.CopyN.
+
+http://codereview.appspot.com/5157045
+`,
+}
+
+func ioCopyN(f *ast.File) bool {
+ if !imports(f, "io") {
+ return false
+ }
+
+ fixed := false
+ walk(f, func(n interface{}) {
+ if expr, ok := n.(ast.Expr); ok {
+ if isPkgDot(expr, "io", "Copyn") {
+ expr.(*ast.SelectorExpr).Sel.Name = "CopyN"
+ fixed = true
+ return
+ }
+ }
+ })
+ return fixed
+}
diff --git a/src/cmd/fix/iocopyn_test.go b/src/cmd/fix/iocopyn_test.go
new file mode 100644
index 000000000..f86fad763
--- /dev/null
+++ b/src/cmd/fix/iocopyn_test.go
@@ -0,0 +1,37 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func init() {
+ addTestCases(ioCopyNTests, ioCopyN)
+}
+
+var ioCopyNTests = []testCase{
+ {
+ Name: "io.CopyN.0",
+ In: `package main
+
+import (
+ "io"
+)
+
+func f() {
+ io.Copyn(dst, src)
+ foo.Copyn(dst, src)
+}
+`,
+ Out: `package main
+
+import (
+ "io"
+)
+
+func f() {
+ io.CopyN(dst, src)
+ foo.Copyn(dst, src)
+}
+`,
+ },
+}
diff --git a/src/cmd/gofix/main.go b/src/cmd/fix/main.go
index e7e7013c5..b151408d7 100644
--- a/src/cmd/gofix/main.go
+++ b/src/cmd/fix/main.go
@@ -6,15 +6,16 @@ package main
import (
"bytes"
- "exec"
"flag"
"fmt"
+ "go/ast"
"go/parser"
"go/printer"
"go/scanner"
"go/token"
"io/ioutil"
"os"
+ "os/exec"
"path/filepath"
"sort"
"strings"
@@ -28,14 +29,21 @@ var (
var allowedRewrites = flag.String("r", "",
"restrict the rewrites to this comma-separated list")
-var allowed map[string]bool
+var forceRewrites = flag.String("force", "",
+ "force these fixes to run even if the code looks updated")
+
+var allowed, force map[string]bool
var doDiff = flag.Bool("diff", false, "display diffs instead of rewriting files")
+// enable for debugging fix failures
+const debug = false // display incorrectly reformatted source and exit
+
func usage() {
- fmt.Fprintf(os.Stderr, "usage: gofix [-diff] [-r fixname,...] [path ...]\n")
+ fmt.Fprintf(os.Stderr, "usage: go tool fix [-diff] [-r fixname,...] [-force fixname,...] [path ...]\n")
flag.PrintDefaults()
fmt.Fprintf(os.Stderr, "\nAvailable rewrites are:\n")
+ sort.Sort(byName(fixes))
for _, f := range fixes {
fmt.Fprintf(os.Stderr, "\n%s\n", f.name)
desc := strings.TrimSpace(f.desc)
@@ -46,11 +54,11 @@ func usage() {
}
func main() {
- sort.Sort(fixes)
-
flag.Usage = usage
flag.Parse()
+ sort.Sort(byDate(fixes))
+
if *allowedRewrites != "" {
allowed = make(map[string]bool)
for _, f := range strings.Split(*allowedRewrites, ",") {
@@ -58,6 +66,13 @@ func main() {
}
}
+ if *forceRewrites != "" {
+ force = make(map[string]bool)
+ for _, f := range strings.Split(*forceRewrites, ",") {
+ force[f] = true
+ }
+ }
+
if flag.NArg() == 0 {
if err := processFile("standard input", true); err != nil {
report(err)
@@ -70,12 +85,12 @@ func main() {
switch dir, err := os.Stat(path); {
case err != nil:
report(err)
- case dir.IsRegular():
+ case dir.IsDir():
+ walkDir(path)
+ default:
if err := processFile(path, false); err != nil {
report(err)
}
- case dir.IsDirectory():
- walkDir(path)
}
}
@@ -89,15 +104,25 @@ const (
)
var printConfig = &printer.Config{
- printerMode,
- tabWidth,
+ Mode: printerMode,
+ Tabwidth: tabWidth,
}
-func processFile(filename string, useStdin bool) os.Error {
+func gofmtFile(f *ast.File) ([]byte, error) {
+ var buf bytes.Buffer
+
+ ast.SortImports(fset, f)
+ err := printConfig.Fprint(&buf, fset, f)
+ if err != nil {
+ return nil, err
+ }
+ return buf.Bytes(), nil
+}
+
+func processFile(filename string, useStdin bool) error {
var f *os.File
- var err os.Error
+ var err error
var fixlog bytes.Buffer
- var buf bytes.Buffer
if useStdin {
f = os.Stdin
@@ -133,14 +158,17 @@ func processFile(filename string, useStdin bool) os.Error {
// AST changed.
// Print and parse, to update any missing scoping
// or position information for subsequent fixers.
- buf.Reset()
- _, err = printConfig.Fprint(&buf, fset, newFile)
+ newSrc, err := gofmtFile(newFile)
if err != nil {
return err
}
- newSrc := buf.Bytes()
newFile, err = parser.ParseFile(fset, filename, newSrc, parserMode)
if err != nil {
+ if debug {
+ fmt.Printf("%s", newSrc)
+ report(err)
+ os.Exit(exitCode)
+ }
return err
}
}
@@ -156,12 +184,10 @@ func processFile(filename string, useStdin bool) os.Error {
// output of the printer run on a standard AST generated by the parser,
// but the source we generated inside the loop above is the
// output of the printer run on a mangled AST generated by a fixer.
- buf.Reset()
- _, err = printConfig.Fprint(&buf, fset, newFile)
+ newSrc, err := gofmtFile(newFile)
if err != nil {
return err
}
- newSrc := buf.Bytes()
if *doDiff {
data, err := diff(src, newSrc)
@@ -185,60 +211,47 @@ var gofmtBuf bytes.Buffer
func gofmt(n interface{}) string {
gofmtBuf.Reset()
- _, err := printConfig.Fprint(&gofmtBuf, fset, n)
+ err := printConfig.Fprint(&gofmtBuf, fset, n)
if err != nil {
- return "<" + err.String() + ">"
+ return "<" + err.Error() + ">"
}
return gofmtBuf.String()
}
-func report(err os.Error) {
+func report(err error) {
scanner.PrintError(os.Stderr, err)
exitCode = 2
}
func walkDir(path string) {
- v := make(fileVisitor)
- go func() {
- filepath.Walk(path, v, v)
- close(v)
- }()
- for err := range v {
- if err != nil {
- report(err)
- }
- }
+ filepath.Walk(path, visitFile)
}
-type fileVisitor chan os.Error
-
-func (v fileVisitor) VisitDir(path string, f *os.FileInfo) bool {
- return true
-}
-
-func (v fileVisitor) VisitFile(path string, f *os.FileInfo) {
- if isGoFile(f) {
- v <- nil // synchronize error handler
- if err := processFile(path, false); err != nil {
- v <- err
- }
+func visitFile(path string, f os.FileInfo, err error) error {
+ if err == nil && isGoFile(f) {
+ err = processFile(path, false)
+ }
+ if err != nil {
+ report(err)
}
+ return nil
}
-func isGoFile(f *os.FileInfo) bool {
+func isGoFile(f os.FileInfo) bool {
// ignore non-Go files
- return f.IsRegular() && !strings.HasPrefix(f.Name, ".") && strings.HasSuffix(f.Name, ".go")
+ name := f.Name()
+ return !f.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go")
}
-func diff(b1, b2 []byte) (data []byte, err os.Error) {
- f1, err := ioutil.TempFile("", "gofix")
+func diff(b1, b2 []byte) (data []byte, err error) {
+ f1, err := ioutil.TempFile("", "go-fix")
if err != nil {
return nil, err
}
defer os.Remove(f1.Name())
defer f1.Close()
- f2, err := ioutil.TempFile("", "gofix")
+ f2, err := ioutil.TempFile("", "go-fix")
if err != nil {
return nil, err
}
diff --git a/src/cmd/gofix/main_test.go b/src/cmd/fix/main_test.go
index 275778e5b..2151bf29e 100644
--- a/src/cmd/gofix/main_test.go
+++ b/src/cmd/fix/main_test.go
@@ -5,10 +5,8 @@
package main
import (
- "bytes"
"go/ast"
"go/parser"
- "go/printer"
"strings"
"testing"
)
@@ -22,27 +20,33 @@ type testCase struct {
var testCases []testCase
-func addTestCases(t []testCase) {
+func addTestCases(t []testCase, fn func(*ast.File) bool) {
+ // Fill in fn to avoid repetition in definitions.
+ if fn != nil {
+ for i := range t {
+ if t[i].Fn == nil {
+ t[i].Fn = fn
+ }
+ }
+ }
testCases = append(testCases, t...)
}
func fnop(*ast.File) bool { return false }
-func parseFixPrint(t *testing.T, fn func(*ast.File) bool, desc, in string) (out string, fixed, ok bool) {
+func parseFixPrint(t *testing.T, fn func(*ast.File) bool, desc, in string, mustBeGofmt bool) (out string, fixed, ok bool) {
file, err := parser.ParseFile(fset, desc, in, parserMode)
if err != nil {
t.Errorf("%s: parsing: %v", desc, err)
return
}
- var buf bytes.Buffer
- buf.Reset()
- _, err = (&printer.Config{printerMode, tabWidth}).Fprint(&buf, fset, file)
+ outb, err := gofmtFile(file)
if err != nil {
t.Errorf("%s: printing: %v", desc, err)
return
}
- if s := buf.String(); in != s && fn != fnop {
+ if s := string(outb); in != s && mustBeGofmt {
t.Errorf("%s: not gofmt-formatted.\n--- %s\n%s\n--- %s | gofmt\n%s",
desc, desc, in, desc, s)
tdiff(t, in, s)
@@ -59,26 +63,25 @@ func parseFixPrint(t *testing.T, fn func(*ast.File) bool, desc, in string) (out
fixed = fn(file)
}
- buf.Reset()
- _, err = (&printer.Config{printerMode, tabWidth}).Fprint(&buf, fset, file)
+ outb, err = gofmtFile(file)
if err != nil {
t.Errorf("%s: printing: %v", desc, err)
return
}
- return buf.String(), fixed, true
+ return string(outb), fixed, true
}
func TestRewrite(t *testing.T) {
for _, tt := range testCases {
// Apply fix: should get tt.Out.
- out, fixed, ok := parseFixPrint(t, tt.Fn, tt.Name, tt.In)
+ out, fixed, ok := parseFixPrint(t, tt.Fn, tt.Name, tt.In, true)
if !ok {
continue
}
// reformat to get printing right
- out, _, ok = parseFixPrint(t, fnop, tt.Name, out)
+ out, _, ok = parseFixPrint(t, fnop, tt.Name, out, false)
if !ok {
continue
}
@@ -98,7 +101,7 @@ func TestRewrite(t *testing.T) {
}
// Should not change if run again.
- out2, fixed2, ok := parseFixPrint(t, tt.Fn, tt.Name+" output", out)
+ out2, fixed2, ok := parseFixPrint(t, tt.Fn, tt.Name+" output", out, true)
if !ok {
continue
}
diff --git a/src/cmd/fix/mapdelete.go b/src/cmd/fix/mapdelete.go
new file mode 100644
index 000000000..db89c7bf4
--- /dev/null
+++ b/src/cmd/fix/mapdelete.go
@@ -0,0 +1,89 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "go/ast"
+
+func init() {
+ register(mapdeleteFix)
+}
+
+var mapdeleteFix = fix{
+ "mapdelete",
+ "2011-10-18",
+ mapdelete,
+ `Use delete(m, k) instead of m[k] = 0, false.
+
+http://codereview.appspot.com/5272045
+`,
+}
+
+func mapdelete(f *ast.File) bool {
+ fixed := false
+ walk(f, func(n interface{}) {
+ stmt, ok := n.(*ast.Stmt)
+ if !ok {
+ return
+ }
+ as, ok := (*stmt).(*ast.AssignStmt)
+ if !ok || len(as.Lhs) != 1 || len(as.Rhs) != 2 {
+ return
+ }
+ ix, ok := as.Lhs[0].(*ast.IndexExpr)
+ if !ok {
+ return
+ }
+ if !isTopName(as.Rhs[1], "false") {
+ warn(as.Pos(), "two-element map assignment with non-false second value")
+ return
+ }
+ if !canDrop(as.Rhs[0]) {
+ warn(as.Pos(), "two-element map assignment with non-trivial first value")
+ return
+ }
+ *stmt = &ast.ExprStmt{
+ X: &ast.CallExpr{
+ Fun: &ast.Ident{
+ NamePos: as.Pos(),
+ Name: "delete",
+ },
+ Args: []ast.Expr{ix.X, ix.Index},
+ },
+ }
+ fixed = true
+ })
+ return fixed
+}
+
+// canDrop reports whether it is safe to drop the
+// evaluation of n from the program.
+// It is very conservative.
+func canDrop(n ast.Expr) bool {
+ switch n := n.(type) {
+ case *ast.Ident, *ast.BasicLit:
+ return true
+ case *ast.ParenExpr:
+ return canDrop(n.X)
+ case *ast.SelectorExpr:
+ return canDrop(n.X)
+ case *ast.CompositeLit:
+ if !canDrop(n.Type) {
+ return false
+ }
+ for _, e := range n.Elts {
+ if !canDrop(e) {
+ return false
+ }
+ }
+ return true
+ case *ast.StarExpr:
+ // Dropping *x is questionable,
+ // but we have to be able to drop (*T)(nil).
+ return canDrop(n.X)
+ case *ast.ArrayType, *ast.ChanType, *ast.FuncType, *ast.InterfaceType, *ast.MapType, *ast.StructType:
+ return true
+ }
+ return false
+}
diff --git a/src/cmd/fix/mapdelete_test.go b/src/cmd/fix/mapdelete_test.go
new file mode 100644
index 000000000..8ed50328e
--- /dev/null
+++ b/src/cmd/fix/mapdelete_test.go
@@ -0,0 +1,43 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func init() {
+ addTestCases(mapdeleteTests, mapdelete)
+}
+
+var mapdeleteTests = []testCase{
+ {
+ Name: "mapdelete.0",
+ In: `package main
+
+func f() {
+ m[x] = 0, false
+ m[x] = g(), false
+ m[x] = 1
+ delete(m, x)
+ m[x] = 0, b
+}
+
+func g(false bool) {
+ m[x] = 0, false
+}
+`,
+ Out: `package main
+
+func f() {
+ delete(m, x)
+ m[x] = g(), false
+ m[x] = 1
+ delete(m, x)
+ m[x] = 0, b
+}
+
+func g(false bool) {
+ m[x] = 0, false
+}
+`,
+ },
+}
diff --git a/src/cmd/fix/math.go b/src/cmd/fix/math.go
new file mode 100644
index 000000000..2ec837eb0
--- /dev/null
+++ b/src/cmd/fix/math.go
@@ -0,0 +1,51 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "go/ast"
+
+func init() {
+ register(mathFix)
+}
+
+var mathFix = fix{
+ "math",
+ "2011-09-29",
+ math,
+ `Remove the leading F from math functions such as Fabs.
+
+http://codereview.appspot.com/5158043
+`,
+}
+
+var mathRenames = []struct{ in, out string }{
+ {"Fabs", "Abs"},
+ {"Fdim", "Dim"},
+ {"Fmax", "Max"},
+ {"Fmin", "Min"},
+ {"Fmod", "Mod"},
+}
+
+func math(f *ast.File) bool {
+ if !imports(f, "math") {
+ return false
+ }
+
+ fixed := false
+
+ walk(f, func(n interface{}) {
+ // Rename functions.
+ if expr, ok := n.(ast.Expr); ok {
+ for _, s := range mathRenames {
+ if isPkgDot(expr, "math", s.in) {
+ expr.(*ast.SelectorExpr).Sel.Name = s.out
+ fixed = true
+ return
+ }
+ }
+ }
+ })
+ return fixed
+}
diff --git a/src/cmd/fix/math_test.go b/src/cmd/fix/math_test.go
new file mode 100644
index 000000000..b8d69d2f2
--- /dev/null
+++ b/src/cmd/fix/math_test.go
@@ -0,0 +1,47 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func init() {
+ addTestCases(mathTests, math)
+}
+
+var mathTests = []testCase{
+ {
+ Name: "math.0",
+ In: `package main
+
+import (
+ "math"
+)
+
+func f() {
+ math.Fabs(1)
+ math.Fdim(1)
+ math.Fmax(1)
+ math.Fmin(1)
+ math.Fmod(1)
+ math.Abs(1)
+ foo.Fabs(1)
+}
+`,
+ Out: `package main
+
+import (
+ "math"
+)
+
+func f() {
+ math.Abs(1)
+ math.Dim(1)
+ math.Max(1)
+ math.Min(1)
+ math.Mod(1)
+ math.Abs(1)
+ foo.Fabs(1)
+}
+`,
+ },
+}
diff --git a/src/cmd/gofix/netdial.go b/src/cmd/fix/netdial.go
index afa98953b..2de994cff 100644
--- a/src/cmd/gofix/netdial.go
+++ b/src/cmd/fix/netdial.go
@@ -8,8 +8,15 @@ import (
"go/ast"
)
+func init() {
+ register(netdialFix)
+ register(tlsdialFix)
+ register(netlookupFix)
+}
+
var netdialFix = fix{
"netdial",
+ "2011-03-28",
netdial,
`Adapt 3-argument calls of net.Dial to use 2-argument form.
@@ -19,6 +26,7 @@ http://codereview.appspot.com/4244055
var tlsdialFix = fix{
"tlsdial",
+ "2011-03-28",
tlsdial,
`Adapt 4-argument calls of tls.Dial to use 3-argument form.
@@ -28,6 +36,7 @@ http://codereview.appspot.com/4244055
var netlookupFix = fix{
"netlookup",
+ "2011-03-28",
netlookup,
`Adapt 3-result calls to net.LookupHost to use 2-result form.
@@ -35,12 +44,6 @@ http://codereview.appspot.com/4244055
`,
}
-func init() {
- register(netdialFix)
- register(tlsdialFix)
- register(netlookupFix)
-}
-
func netdial(f *ast.File) bool {
if !imports(f, "net") {
return false
diff --git a/src/cmd/gofix/netdial_test.go b/src/cmd/fix/netdial_test.go
index 272aa526a..fff00b4ad 100644
--- a/src/cmd/gofix/netdial_test.go
+++ b/src/cmd/fix/netdial_test.go
@@ -1,12 +1,17 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
package main
func init() {
- addTestCases(netdialTests)
+ addTestCases(netdialTests, nil)
}
var netdialTests = []testCase{
{
Name: "netdial.0",
+ Fn: netdial,
In: `package main
import "net"
@@ -29,6 +34,7 @@ func f() {
{
Name: "netlookup.0",
+ Fn: netlookup,
In: `package main
import "net"
diff --git a/src/cmd/fix/netudpgroup.go b/src/cmd/fix/netudpgroup.go
new file mode 100644
index 000000000..b54beb0de
--- /dev/null
+++ b/src/cmd/fix/netudpgroup.go
@@ -0,0 +1,58 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "go/ast"
+)
+
+func init() {
+ register(netudpgroupFix)
+}
+
+var netudpgroupFix = fix{
+ "netudpgroup",
+ "2011-08-18",
+ netudpgroup,
+ `Adapt 1-argument calls of net.(*UDPConn).JoinGroup, LeaveGroup to use 2-argument form.
+
+http://codereview.appspot.com/4815074
+`,
+}
+
+func netudpgroup(f *ast.File) bool {
+ if !imports(f, "net") {
+ return false
+ }
+
+ fixed := false
+ for _, d := range f.Decls {
+ fd, ok := d.(*ast.FuncDecl)
+ if !ok || fd.Body == nil {
+ continue
+ }
+ walk(fd.Body, func(n interface{}) {
+ ce, ok := n.(*ast.CallExpr)
+ if !ok {
+ return
+ }
+ se, ok := ce.Fun.(*ast.SelectorExpr)
+ if !ok || len(ce.Args) != 1 {
+ return
+ }
+ switch se.Sel.String() {
+ case "JoinGroup", "LeaveGroup":
+ // c.JoinGroup(a) -> c.JoinGroup(nil, a)
+ // c.LeaveGroup(a) -> c.LeaveGroup(nil, a)
+ arg := ce.Args[0]
+ ce.Args = make([]ast.Expr, 2)
+ ce.Args[0] = ast.NewIdent("nil")
+ ce.Args[1] = arg
+ fixed = true
+ }
+ })
+ }
+ return fixed
+}
diff --git a/src/cmd/fix/netudpgroup_test.go b/src/cmd/fix/netudpgroup_test.go
new file mode 100644
index 000000000..88c0e093f
--- /dev/null
+++ b/src/cmd/fix/netudpgroup_test.go
@@ -0,0 +1,53 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func init() {
+ addTestCases(netudpgroupTests, netudpgroup)
+}
+
+var netudpgroupTests = []testCase{
+ {
+ Name: "netudpgroup.0",
+ In: `package main
+
+import "net"
+
+func f() {
+ err := x.JoinGroup(gaddr)
+ err = y.LeaveGroup(gaddr)
+}
+`,
+ Out: `package main
+
+import "net"
+
+func f() {
+ err := x.JoinGroup(nil, gaddr)
+ err = y.LeaveGroup(nil, gaddr)
+}
+`,
+ },
+ // Innocent function with no body.
+ {
+ Name: "netudpgroup.1",
+ In: `package main
+
+import "net"
+
+func f()
+
+var _ net.IP
+`,
+ Out: `package main
+
+import "net"
+
+func f()
+
+var _ net.IP
+`,
+ },
+}
diff --git a/src/cmd/fix/newwriter.go b/src/cmd/fix/newwriter.go
new file mode 100644
index 000000000..4befe24fb
--- /dev/null
+++ b/src/cmd/fix/newwriter.go
@@ -0,0 +1,90 @@
+// 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 (
+ "go/ast"
+)
+
+func init() {
+ register(newWriterFix)
+}
+
+var newWriterFix = fix{
+ "newWriter",
+ "2012-02-14",
+ newWriter,
+ `Adapt bufio, gzip and zlib NewWriterXxx calls for whether they return errors.
+
+Also rename gzip.Compressor and gzip.Decompressor to gzip.Writer and gzip.Reader.
+
+http://codereview.appspot.com/5639057 and
+http://codereview.appspot.com/5642054
+`,
+}
+
+func newWriter(f *ast.File) bool {
+ if !imports(f, "bufio") && !imports(f, "compress/gzip") && !imports(f, "compress/zlib") {
+ return false
+ }
+
+ fixed := false
+ walk(f, func(n interface{}) {
+ switch n := n.(type) {
+ case *ast.SelectorExpr:
+ if isTopName(n.X, "gzip") {
+ switch n.Sel.String() {
+ case "Compressor":
+ n.Sel = &ast.Ident{Name: "Writer"}
+ fixed = true
+ case "Decompressor":
+ n.Sel = &ast.Ident{Name: "Reader"}
+ fixed = true
+ }
+ } else if isTopName(n.X, "zlib") {
+ if n.Sel.String() == "NewWriterDict" {
+ n.Sel = &ast.Ident{Name: "NewWriterLevelDict"}
+ fixed = true
+ }
+ }
+
+ case *ast.AssignStmt:
+ // Drop the ", _" in assignments of the form:
+ // w0, _ = gzip.NewWriter(w1)
+ if len(n.Lhs) != 2 || len(n.Rhs) != 1 {
+ return
+ }
+ i, ok := n.Lhs[1].(*ast.Ident)
+ if !ok {
+ return
+ }
+ if i.String() != "_" {
+ return
+ }
+ c, ok := n.Rhs[0].(*ast.CallExpr)
+ if !ok {
+ return
+ }
+ s, ok := c.Fun.(*ast.SelectorExpr)
+ if !ok {
+ return
+ }
+ sel := s.Sel.String()
+ switch {
+ case isTopName(s.X, "bufio") && (sel == "NewReaderSize" || sel == "NewWriterSize"):
+ // No-op.
+ case isTopName(s.X, "gzip") && sel == "NewWriter":
+ // No-op.
+ case isTopName(s.X, "zlib") && sel == "NewWriter":
+ // No-op.
+ default:
+ return
+ }
+ n.Lhs = n.Lhs[:1]
+ fixed = true
+ }
+ })
+ return fixed
+}
diff --git a/src/cmd/fix/newwriter_test.go b/src/cmd/fix/newwriter_test.go
new file mode 100644
index 000000000..1f59628a0
--- /dev/null
+++ b/src/cmd/fix/newwriter_test.go
@@ -0,0 +1,83 @@
+// 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
+
+func init() {
+ addTestCases(newWriterTests, newWriter)
+}
+
+var newWriterTests = []testCase{
+ {
+ Name: "newWriter.0",
+ In: `package main
+
+import (
+ "bufio"
+ "compress/gzip"
+ "compress/zlib"
+ "io"
+
+ "foo"
+)
+
+func f() *gzip.Compressor {
+ var (
+ _ gzip.Compressor
+ _ *gzip.Decompressor
+ _ struct {
+ W *gzip.Compressor
+ R gzip.Decompressor
+ }
+ )
+
+ var w io.Writer
+ br := bufio.NewReader(nil)
+ br, _ = bufio.NewReaderSize(nil, 256)
+ bw, err := bufio.NewWriterSize(w, 256) // Unfixable, as it declares an err variable.
+ bw, _ = bufio.NewWriterSize(w, 256)
+ fw, _ := foo.NewWriter(w)
+ gw, _ := gzip.NewWriter(w)
+ gw, _ = gzip.NewWriter(w)
+ zw, _ := zlib.NewWriter(w)
+ _ = zlib.NewWriterDict(zw, 0, nil)
+ return gw
+}
+`,
+ Out: `package main
+
+import (
+ "bufio"
+ "compress/gzip"
+ "compress/zlib"
+ "io"
+
+ "foo"
+)
+
+func f() *gzip.Writer {
+ var (
+ _ gzip.Writer
+ _ *gzip.Reader
+ _ struct {
+ W *gzip.Writer
+ R gzip.Reader
+ }
+ )
+
+ var w io.Writer
+ br := bufio.NewReader(nil)
+ br = bufio.NewReaderSize(nil, 256)
+ bw, err := bufio.NewWriterSize(w, 256) // Unfixable, as it declares an err variable.
+ bw = bufio.NewWriterSize(w, 256)
+ fw, _ := foo.NewWriter(w)
+ gw := gzip.NewWriter(w)
+ gw = gzip.NewWriter(w)
+ zw := zlib.NewWriter(w)
+ _ = zlib.NewWriterLevelDict(zw, 0, nil)
+ return gw
+}
+`,
+ },
+}
diff --git a/src/cmd/gofix/oserrorstring.go b/src/cmd/fix/oserrorstring.go
index db39ee9dc..a75a2c12d 100644
--- a/src/cmd/gofix/oserrorstring.go
+++ b/src/cmd/fix/oserrorstring.go
@@ -8,8 +8,13 @@ import (
"go/ast"
)
+func init() {
+ register(oserrorstringFix)
+}
+
var oserrorstringFix = fix{
"oserrorstring",
+ "2011-06-22",
oserrorstring,
`Replace os.ErrorString() conversions with calls to os.NewError().
@@ -17,10 +22,6 @@ http://codereview.appspot.com/4607052
`,
}
-func init() {
- register(oserrorstringFix)
-}
-
func oserrorstring(f *ast.File) bool {
if !imports(f, "os") {
return false
diff --git a/src/cmd/gofix/oserrorstring_test.go b/src/cmd/fix/oserrorstring_test.go
index 070d9222b..75551480c 100644
--- a/src/cmd/gofix/oserrorstring_test.go
+++ b/src/cmd/fix/oserrorstring_test.go
@@ -5,7 +5,7 @@
package main
func init() {
- addTestCases(oserrorstringTests)
+ addTestCases(oserrorstringTests, oserrorstring)
}
var oserrorstringTests = []testCase{
diff --git a/src/cmd/gofix/osopen.go b/src/cmd/fix/osopen.go
index 19c19b5b6..af2796ac2 100644
--- a/src/cmd/gofix/osopen.go
+++ b/src/cmd/fix/osopen.go
@@ -8,8 +8,13 @@ import (
"go/ast"
)
+func init() {
+ register(osopenFix)
+}
+
var osopenFix = fix{
"osopen",
+ "2011-04-04",
osopen,
`Adapt os.Open calls to new, easier API and rename O_CREAT O_CREATE.
@@ -17,10 +22,6 @@ http://codereview.appspot.com/4357052
`,
}
-func init() {
- register(osopenFix)
-}
-
func osopen(f *ast.File) bool {
if !imports(f, "os") {
return false
diff --git a/src/cmd/gofix/osopen_test.go b/src/cmd/fix/osopen_test.go
index a33bcd4fb..5797adb7b 100644
--- a/src/cmd/gofix/osopen_test.go
+++ b/src/cmd/fix/osopen_test.go
@@ -5,7 +5,7 @@
package main
func init() {
- addTestCases(osopenTests)
+ addTestCases(osopenTests, osopen)
}
var osopenTests = []testCase{
diff --git a/src/cmd/gofix/procattr.go b/src/cmd/fix/procattr.go
index 0e2190b1f..ea375ec9d 100644
--- a/src/cmd/gofix/procattr.go
+++ b/src/cmd/fix/procattr.go
@@ -9,8 +9,13 @@ import (
"go/token"
)
+func init() {
+ register(procattrFix)
+}
+
var procattrFix = fix{
"procattr",
+ "2011-03-15",
procattr,
`Adapt calls to os.StartProcess to use new ProcAttr type.
@@ -18,10 +23,6 @@ http://codereview.appspot.com/4253052
`,
}
-func init() {
- register(procattrFix)
-}
-
func procattr(f *ast.File) bool {
if !imports(f, "os") && !imports(f, "syscall") {
return false
diff --git a/src/cmd/gofix/procattr_test.go b/src/cmd/fix/procattr_test.go
index b973b9684..9e2b86e74 100644
--- a/src/cmd/gofix/procattr_test.go
+++ b/src/cmd/fix/procattr_test.go
@@ -5,7 +5,7 @@
package main
func init() {
- addTestCases(procattrTests)
+ addTestCases(procattrTests, procattr)
}
var procattrTests = []testCase{
diff --git a/src/cmd/gofix/reflect.go b/src/cmd/fix/reflect.go
index 3c8becaef..151da569d 100644
--- a/src/cmd/gofix/reflect.go
+++ b/src/cmd/fix/reflect.go
@@ -15,8 +15,13 @@ import (
"strings"
)
+func init() {
+ register(reflectFix)
+}
+
var reflectFix = fix{
"reflect",
+ "2011-04-08",
reflectFn,
`Adapt code to new reflect API.
@@ -25,10 +30,6 @@ http://codereview.appspot.com/4433066
`,
}
-func init() {
- register(reflectFix)
-}
-
// The reflect API change dropped the concrete types *reflect.ArrayType etc.
// Any type assertions prior to method calls can be deleted:
// x.(*reflect.ArrayType).Len() -> x.Len()
@@ -48,7 +49,7 @@ func init() {
//
// Not all type checks result in a single Kind check. The rewrite of the type check for
// reflect.ArrayOrSliceType checks x.Kind() against reflect.Array and reflect.Slice.
-// The rewrite for *reflect.IntType checks againt Int, Int8, Int16, Int32, Int64.
+// The rewrite for *reflect.IntType checks against Int, Int8, Int16, Int32, Int64.
// The rewrite for *reflect.UintType adds Uintptr.
//
// A type switch turns into an assignment and a switch on Kind:
@@ -86,8 +87,8 @@ func init() {
// x.(*reflect.MapValue).Elem(v) becomes x.MapIndex(v).
// In general, reflectFn needs to know the type of the receiver expression.
// In most cases (and in all the cases in the Go source tree), the toy
-// type checker in typecheck.go provides enough information for gofix
-// to make the rewrite. If gofix misses a rewrite, the code that is left over
+// type checker in typecheck.go provides enough information for fix
+// to make the rewrite. If fix misses a rewrite, the code that is left over
// will not compile, so it will be noticed immediately.
func reflectFn(f *ast.File) bool {
@@ -99,7 +100,7 @@ func reflectFn(f *ast.File) bool {
// Rewrite names in method calls.
// Needs basic type information (see above).
- typeof := typecheck(reflectTypeConfig, f)
+ typeof, _ := typecheck(reflectTypeConfig, f)
walk(f, func(n interface{}) {
switch n := n.(type) {
case *ast.SelectorExpr:
@@ -187,7 +188,7 @@ func reflectFn(f *ast.File) bool {
}
*n = v
case *ast.IfStmt:
- x := &ast.ExprStmt{n.Cond}
+ x := &ast.ExprStmt{X: n.Cond}
if reflectFixTypecheck(&n.Init, nil, []ast.Stmt{x, n.Body, n.Else}) {
n.Cond = x.X
fixed = true
@@ -616,75 +617,75 @@ func reflectFixAssert(n interface{}) bool {
// which implements Type.)
var reflectTypeConfig = &TypeConfig{
Type: map[string]*Type{
- "reflect.ArrayOrSliceType": &Type{Embed: []string{"reflect.Type"}},
- "reflect.ArrayOrSliceValue": &Type{Embed: []string{"reflect.Value"}},
- "reflect.ArrayType": &Type{Embed: []string{"reflect.Type"}},
- "reflect.ArrayValue": &Type{Embed: []string{"reflect.Value"}},
- "reflect.BoolType": &Type{Embed: []string{"reflect.Type"}},
- "reflect.BoolValue": &Type{Embed: []string{"reflect.Value"}},
- "reflect.ChanType": &Type{Embed: []string{"reflect.Type"}},
- "reflect.ChanValue": &Type{
+ "reflect.ArrayOrSliceType": {Embed: []string{"reflect.Type"}},
+ "reflect.ArrayOrSliceValue": {Embed: []string{"reflect.Value"}},
+ "reflect.ArrayType": {Embed: []string{"reflect.Type"}},
+ "reflect.ArrayValue": {Embed: []string{"reflect.Value"}},
+ "reflect.BoolType": {Embed: []string{"reflect.Type"}},
+ "reflect.BoolValue": {Embed: []string{"reflect.Value"}},
+ "reflect.ChanType": {Embed: []string{"reflect.Type"}},
+ "reflect.ChanValue": {
Method: map[string]string{
"Recv": "func() (reflect.Value, bool)",
"TryRecv": "func() (reflect.Value, bool)",
},
Embed: []string{"reflect.Value"},
},
- "reflect.ComplexType": &Type{Embed: []string{"reflect.Type"}},
- "reflect.ComplexValue": &Type{Embed: []string{"reflect.Value"}},
- "reflect.FloatType": &Type{Embed: []string{"reflect.Type"}},
- "reflect.FloatValue": &Type{Embed: []string{"reflect.Value"}},
- "reflect.FuncType": &Type{
+ "reflect.ComplexType": {Embed: []string{"reflect.Type"}},
+ "reflect.ComplexValue": {Embed: []string{"reflect.Value"}},
+ "reflect.FloatType": {Embed: []string{"reflect.Type"}},
+ "reflect.FloatValue": {Embed: []string{"reflect.Value"}},
+ "reflect.FuncType": {
Method: map[string]string{
"In": "func(int) reflect.Type",
"Out": "func(int) reflect.Type",
},
Embed: []string{"reflect.Type"},
},
- "reflect.FuncValue": &Type{
+ "reflect.FuncValue": {
Method: map[string]string{
"Call": "func([]reflect.Value) []reflect.Value",
},
},
- "reflect.IntType": &Type{Embed: []string{"reflect.Type"}},
- "reflect.IntValue": &Type{Embed: []string{"reflect.Value"}},
- "reflect.InterfaceType": &Type{Embed: []string{"reflect.Type"}},
- "reflect.InterfaceValue": &Type{Embed: []string{"reflect.Value"}},
- "reflect.MapType": &Type{
+ "reflect.IntType": {Embed: []string{"reflect.Type"}},
+ "reflect.IntValue": {Embed: []string{"reflect.Value"}},
+ "reflect.InterfaceType": {Embed: []string{"reflect.Type"}},
+ "reflect.InterfaceValue": {Embed: []string{"reflect.Value"}},
+ "reflect.MapType": {
Method: map[string]string{
"Key": "func() reflect.Type",
},
Embed: []string{"reflect.Type"},
},
- "reflect.MapValue": &Type{
+ "reflect.MapValue": {
Method: map[string]string{
"Keys": "func() []reflect.Value",
},
Embed: []string{"reflect.Value"},
},
- "reflect.Method": &Type{
+ "reflect.Method": {
Field: map[string]string{
"Type": "*reflect.FuncType",
"Func": "*reflect.FuncValue",
},
},
- "reflect.PtrType": &Type{Embed: []string{"reflect.Type"}},
- "reflect.PtrValue": &Type{Embed: []string{"reflect.Value"}},
- "reflect.SliceType": &Type{Embed: []string{"reflect.Type"}},
- "reflect.SliceValue": &Type{
+ "reflect.PtrType": {Embed: []string{"reflect.Type"}},
+ "reflect.PtrValue": {Embed: []string{"reflect.Value"}},
+ "reflect.SliceType": {Embed: []string{"reflect.Type"}},
+ "reflect.SliceValue": {
Method: map[string]string{
"Slice": "func(int, int) *reflect.SliceValue",
},
Embed: []string{"reflect.Value"},
},
- "reflect.StringType": &Type{Embed: []string{"reflect.Type"}},
- "reflect.StringValue": &Type{Embed: []string{"reflect.Value"}},
- "reflect.StructField": &Type{
+ "reflect.StringType": {Embed: []string{"reflect.Type"}},
+ "reflect.StringValue": {Embed: []string{"reflect.Value"}},
+ "reflect.StructField": {
Field: map[string]string{
"Type": "reflect.Type",
},
},
- "reflect.StructType": &Type{
+ "reflect.StructType": {
Method: map[string]string{
"Field": "func() reflect.StructField",
"FieldByIndex": "func() reflect.StructField",
@@ -693,7 +694,7 @@ var reflectTypeConfig = &TypeConfig{
},
Embed: []string{"reflect.Type"},
},
- "reflect.StructValue": &Type{
+ "reflect.StructValue": {
Method: map[string]string{
"Field": "func() reflect.Value",
"FieldByIndex": "func() reflect.Value",
@@ -702,17 +703,17 @@ var reflectTypeConfig = &TypeConfig{
},
Embed: []string{"reflect.Value"},
},
- "reflect.Type": &Type{
+ "reflect.Type": {
Method: map[string]string{
"Elem": "func() reflect.Type",
"Method": "func() reflect.Method",
},
},
- "reflect.UintType": &Type{Embed: []string{"reflect.Type"}},
- "reflect.UintValue": &Type{Embed: []string{"reflect.Value"}},
- "reflect.UnsafePointerType": &Type{Embed: []string{"reflect.Type"}},
- "reflect.UnsafePointerValue": &Type{Embed: []string{"reflect.Value"}},
- "reflect.Value": &Type{
+ "reflect.UintType": {Embed: []string{"reflect.Type"}},
+ "reflect.UintValue": {Embed: []string{"reflect.Value"}},
+ "reflect.UnsafePointerType": {Embed: []string{"reflect.Type"}},
+ "reflect.UnsafePointerValue": {Embed: []string{"reflect.Value"}},
+ "reflect.Value": {
Method: map[string]string{
"Addr": "func() *reflect.PtrValue",
"Elem": "func() reflect.Value",
diff --git a/src/cmd/gofix/reflect_test.go b/src/cmd/fix/reflect_test.go
index 00edf30e9..032cbc745 100644
--- a/src/cmd/gofix/reflect_test.go
+++ b/src/cmd/fix/reflect_test.go
@@ -1,3 +1,7 @@
+// 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 (
@@ -7,7 +11,7 @@ import (
)
func init() {
- addTestCases(reflectTests())
+ addTestCases(reflectTests(), reflectFn)
}
func reflectTests() []testCase {
diff --git a/src/cmd/gofix/signal.go b/src/cmd/fix/signal.go
index 53c338851..5a583d41e 100644
--- a/src/cmd/gofix/signal.go
+++ b/src/cmd/fix/signal.go
@@ -10,14 +10,17 @@ import (
)
func init() {
- register(fix{
- "signal",
- signal,
- `Adapt code to types moved from os/signal to signal.
+ register(signalFix)
+}
+
+var signalFix = fix{
+ "signal",
+ "2011-06-29",
+ signal,
+ `Adapt code to types moved from os/signal to signal.
http://codereview.appspot.com/4437091
`,
- })
}
func signal(f *ast.File) (fixed bool) {
@@ -34,16 +37,14 @@ func signal(f *ast.File) (fixed bool) {
sel := s.Sel.String()
if sel == "Signal" || sel == "UnixSignal" || strings.HasPrefix(sel, "SIG") {
+ addImport(f, "os")
s.X = &ast.Ident{Name: "os"}
fixed = true
}
})
- if fixed {
- addImport(f, "os")
- if !usesImport(f, "os/signal") {
- deleteImport(f, "os/signal")
- }
+ if fixed && !usesImport(f, "os/signal") {
+ deleteImport(f, "os/signal")
}
return
}
diff --git a/src/cmd/gofix/signal_test.go b/src/cmd/fix/signal_test.go
index 4abba3534..7bca7d5c4 100644
--- a/src/cmd/gofix/signal_test.go
+++ b/src/cmd/fix/signal_test.go
@@ -5,7 +5,7 @@
package main
func init() {
- addTestCases(signalTests)
+ addTestCases(signalTests, signal)
}
var signalTests = []testCase{
diff --git a/src/cmd/gofix/sorthelpers.go b/src/cmd/fix/sorthelpers.go
index 4e89fa88f..fa549313e 100644
--- a/src/cmd/gofix/sorthelpers.go
+++ b/src/cmd/fix/sorthelpers.go
@@ -9,12 +9,15 @@ import (
)
func init() {
- register(fix{
- "sorthelpers",
- sorthelpers,
- `Adapt code from sort.Sort[Ints|Float64s|Strings] to sort.[Ints|Float64s|Strings].
+ register(sorthelpersFix)
+}
+
+var sorthelpersFix = fix{
+ "sorthelpers",
+ "2011-07-08",
+ sorthelpers,
+ `Adapt code from sort.Sort[Ints|Float64s|Strings] to sort.[Ints|Float64s|Strings].
`,
- })
}
func sorthelpers(f *ast.File) (fixed bool) {
diff --git a/src/cmd/gofix/sorthelpers_test.go b/src/cmd/fix/sorthelpers_test.go
index 6c37858fd..dd6b58e03 100644
--- a/src/cmd/gofix/sorthelpers_test.go
+++ b/src/cmd/fix/sorthelpers_test.go
@@ -5,7 +5,7 @@
package main
func init() {
- addTestCases(sorthelpersTests)
+ addTestCases(sorthelpersTests, sorthelpers)
}
var sorthelpersTests = []testCase{
diff --git a/src/cmd/gofix/sortslice.go b/src/cmd/fix/sortslice.go
index 7cfa1696b..89267b847 100644
--- a/src/cmd/gofix/sortslice.go
+++ b/src/cmd/fix/sortslice.go
@@ -9,15 +9,18 @@ import (
)
func init() {
- register(fix{
- "sortslice",
- sortslice,
- `Adapt code from sort.[Float64|Int|String]Array to sort.[Float64|Int|String]Slice.
+ register(sortsliceFix)
+}
+
+var sortsliceFix = fix{
+ "sortslice",
+ "2011-06-26",
+ sortslice,
+ `Adapt code from sort.[Float64|Int|String]Array to sort.[Float64|Int|String]Slice.
http://codereview.appspot.com/4602054
http://codereview.appspot.com/4639041
`,
- })
}
func sortslice(f *ast.File) (fixed bool) {
diff --git a/src/cmd/gofix/sortslice_test.go b/src/cmd/fix/sortslice_test.go
index 404feb26f..7b745a232 100644
--- a/src/cmd/gofix/sortslice_test.go
+++ b/src/cmd/fix/sortslice_test.go
@@ -5,7 +5,7 @@
package main
func init() {
- addTestCases(sortsliceTests)
+ addTestCases(sortsliceTests, sortslice)
}
var sortsliceTests = []testCase{
diff --git a/src/cmd/fix/strconv.go b/src/cmd/fix/strconv.go
new file mode 100644
index 000000000..6cd69020b
--- /dev/null
+++ b/src/cmd/fix/strconv.go
@@ -0,0 +1,127 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "go/ast"
+
+func init() {
+ register(strconvFix)
+}
+
+var strconvFix = fix{
+ "strconv",
+ "2011-12-01",
+ strconvFn,
+ `Convert to new strconv API.
+
+http://codereview.appspot.com/5434095
+http://codereview.appspot.com/5434069
+`,
+}
+
+func strconvFn(f *ast.File) bool {
+ if !imports(f, "strconv") {
+ return false
+ }
+
+ fixed := false
+
+ walk(f, func(n interface{}) {
+ // Rename functions.
+ call, ok := n.(*ast.CallExpr)
+ if !ok || len(call.Args) < 1 {
+ return
+ }
+ sel, ok := call.Fun.(*ast.SelectorExpr)
+ if !ok || !isTopName(sel.X, "strconv") {
+ return
+ }
+ change := func(name string) {
+ fixed = true
+ sel.Sel.Name = name
+ }
+ add := func(s string) {
+ call.Args = append(call.Args, expr(s))
+ }
+ switch sel.Sel.Name {
+ case "Atob":
+ change("ParseBool")
+ case "Atof32":
+ change("ParseFloat")
+ add("32") // bitSize
+ warn(call.Pos(), "rewrote strconv.Atof32(_) to strconv.ParseFloat(_, 32) but return value must be converted to float32")
+ case "Atof64":
+ change("ParseFloat")
+ add("64") // bitSize
+ case "AtofN":
+ change("ParseFloat")
+ case "Atoi":
+ // Atoi stayed as a convenience wrapper.
+ case "Atoi64":
+ change("ParseInt")
+ add("10") // base
+ add("64") // bitSize
+ case "Atoui":
+ change("ParseUint")
+ add("10") // base
+ add("0") // bitSize
+ warn(call.Pos(), "rewrote strconv.Atoui(_) to strconv.ParseUint(_, 10, 0) but return value must be converted to uint")
+ case "Atoui64":
+ change("ParseUint")
+ add("10") // base
+ add("64") // bitSize
+ case "Btoa":
+ change("FormatBool")
+ case "Btoi64":
+ change("ParseInt")
+ add("64") // bitSize
+ case "Btoui64":
+ change("ParseUint")
+ add("64") // bitSize
+ case "Ftoa32":
+ change("FormatFloat")
+ call.Args[0] = strconvRewrite("float32", "float64", call.Args[0])
+ add("32") // bitSize
+ case "Ftoa64":
+ change("FormatFloat")
+ add("64") // bitSize
+ case "FtoaN":
+ change("FormatFloat")
+ case "Itoa":
+ // Itoa stayed as a convenience wrapper.
+ case "Itoa64":
+ change("FormatInt")
+ add("10") // base
+ case "Itob":
+ change("FormatInt")
+ call.Args[0] = strconvRewrite("int", "int64", call.Args[0])
+ case "Itob64":
+ change("FormatInt")
+ case "Uitoa":
+ change("FormatUint")
+ call.Args[0] = strconvRewrite("uint", "uint64", call.Args[0])
+ add("10") // base
+ case "Uitoa64":
+ change("FormatUint")
+ add("10") // base
+ case "Uitob":
+ change("FormatUint")
+ call.Args[0] = strconvRewrite("uint", "uint64", call.Args[0])
+ case "Uitob64":
+ change("FormatUint")
+ }
+ })
+ return fixed
+}
+
+// rewrite from type t1 to type t2
+// If the expression x is of the form t1(_), use t2(_). Otherwise use t2(x).
+func strconvRewrite(t1, t2 string, x ast.Expr) ast.Expr {
+ if call, ok := x.(*ast.CallExpr); ok && isTopName(call.Fun, t1) {
+ call.Fun.(*ast.Ident).Name = t2
+ return x
+ }
+ return &ast.CallExpr{Fun: ast.NewIdent(t2), Args: []ast.Expr{x}}
+}
diff --git a/src/cmd/fix/strconv_test.go b/src/cmd/fix/strconv_test.go
new file mode 100644
index 000000000..7fbd4e42e
--- /dev/null
+++ b/src/cmd/fix/strconv_test.go
@@ -0,0 +1,93 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func init() {
+ addTestCases(strconvTests, strconvFn)
+}
+
+var strconvTests = []testCase{
+ {
+ Name: "strconv.0",
+ In: `package main
+
+import "strconv"
+
+func f() {
+ foo.Atob("abc")
+
+ strconv.Atob("true")
+ strconv.Btoa(false)
+
+ strconv.Atof32("1.2")
+ strconv.Atof64("1.2")
+ strconv.AtofN("1.2", 64)
+ strconv.Ftoa32(1.2, 'g', 17)
+ strconv.Ftoa64(1.2, 'g', 17)
+ strconv.FtoaN(1.2, 'g', 17, 64)
+
+ strconv.Atoi("3")
+ strconv.Atoi64("3")
+ strconv.Btoi64("1234", 5)
+
+ strconv.Atoui("3")
+ strconv.Atoui64("3")
+ strconv.Btoui64("1234", 5)
+
+ strconv.Itoa(123)
+ strconv.Itoa64(1234)
+ strconv.Itob(123, 5)
+ strconv.Itob64(1234, 5)
+
+ strconv.Uitoa(123)
+ strconv.Uitoa64(1234)
+ strconv.Uitob(123, 5)
+ strconv.Uitob64(1234, 5)
+
+ strconv.Uitoa(uint(x))
+ strconv.Uitoa(f(x))
+}
+`,
+ Out: `package main
+
+import "strconv"
+
+func f() {
+ foo.Atob("abc")
+
+ strconv.ParseBool("true")
+ strconv.FormatBool(false)
+
+ strconv.ParseFloat("1.2", 32)
+ strconv.ParseFloat("1.2", 64)
+ strconv.ParseFloat("1.2", 64)
+ strconv.FormatFloat(float64(1.2), 'g', 17, 32)
+ strconv.FormatFloat(1.2, 'g', 17, 64)
+ strconv.FormatFloat(1.2, 'g', 17, 64)
+
+ strconv.Atoi("3")
+ strconv.ParseInt("3", 10, 64)
+ strconv.ParseInt("1234", 5, 64)
+
+ strconv.ParseUint("3", 10, 0)
+ strconv.ParseUint("3", 10, 64)
+ strconv.ParseUint("1234", 5, 64)
+
+ strconv.Itoa(123)
+ strconv.FormatInt(1234, 10)
+ strconv.FormatInt(int64(123), 5)
+ strconv.FormatInt(1234, 5)
+
+ strconv.FormatUint(uint64(123), 10)
+ strconv.FormatUint(1234, 10)
+ strconv.FormatUint(uint64(123), 5)
+ strconv.FormatUint(1234, 5)
+
+ strconv.FormatUint(uint64(x), 10)
+ strconv.FormatUint(uint64(f(x)), 10)
+}
+`,
+ },
+}
diff --git a/src/cmd/gofix/stringssplit.go b/src/cmd/fix/stringssplit.go
index 4a1fe93d3..d89ecf039 100644
--- a/src/cmd/gofix/stringssplit.go
+++ b/src/cmd/fix/stringssplit.go
@@ -9,8 +9,13 @@ import (
"go/token"
)
+func init() {
+ register(stringssplitFix)
+}
+
var stringssplitFix = fix{
"stringssplit",
+ "2011-06-28",
stringssplit,
`Restore strings.Split to its original meaning and add strings.SplitN. Bytes too.
@@ -18,10 +23,6 @@ http://codereview.appspot.com/4661051
`,
}
-func init() {
- register(stringssplitFix)
-}
-
func stringssplit(f *ast.File) bool {
if !imports(f, "bytes") && !imports(f, "strings") {
return false
diff --git a/src/cmd/gofix/stringssplit_test.go b/src/cmd/fix/stringssplit_test.go
index b925722af..fa42b1bea 100644
--- a/src/cmd/gofix/stringssplit_test.go
+++ b/src/cmd/fix/stringssplit_test.go
@@ -5,7 +5,7 @@
package main
func init() {
- addTestCases(stringssplitTests)
+ addTestCases(stringssplitTests, stringssplit)
}
var stringssplitTests = []testCase{
diff --git a/src/cmd/fix/template.go b/src/cmd/fix/template.go
new file mode 100644
index 000000000..a3dd1440b
--- /dev/null
+++ b/src/cmd/fix/template.go
@@ -0,0 +1,111 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "go/ast"
+)
+
+func init() {
+ register(templateFix)
+}
+
+var templateFix = fix{
+ "template",
+ "2011-11-22",
+ template,
+ `Rewrite calls to template.ParseFile to template.ParseFiles
+
+http://codereview.appspot.com/5433048
+`,
+}
+
+var templateSetGlobals = []string{
+ "ParseSetFiles",
+ "ParseSetGlob",
+ "ParseTemplateFiles",
+ "ParseTemplateGlob",
+ "Set",
+ "SetMust",
+}
+
+var templateSetMethods = []string{
+ "ParseSetFiles",
+ "ParseSetGlob",
+ "ParseTemplateFiles",
+ "ParseTemplateGlob",
+}
+
+var templateTypeConfig = &TypeConfig{
+ Type: map[string]*Type{
+ "template.Template": {
+ Method: map[string]string{
+ "Funcs": "func() *template.Template",
+ "Delims": "func() *template.Template",
+ "Parse": "func() (*template.Template, error)",
+ "ParseFile": "func() (*template.Template, error)",
+ "ParseInSet": "func() (*template.Template, error)",
+ },
+ },
+ "template.Set": {
+ Method: map[string]string{
+ "ParseSetFiles": "func() (*template.Set, error)",
+ "ParseSetGlob": "func() (*template.Set, error)",
+ "ParseTemplateFiles": "func() (*template.Set, error)",
+ "ParseTemplateGlob": "func() (*template.Set, error)",
+ },
+ },
+ },
+
+ Func: map[string]string{
+ "template.New": "*template.Template",
+ "template.Must": "(*template.Template, error)",
+ "template.SetMust": "(*template.Set, error)",
+ },
+}
+
+func template(f *ast.File) bool {
+ if !imports(f, "text/template") && !imports(f, "html/template") {
+ return false
+ }
+
+ fixed := false
+
+ typeof, _ := typecheck(templateTypeConfig, f)
+
+ // Now update the names used by importers.
+ walk(f, func(n interface{}) {
+ if sel, ok := n.(*ast.SelectorExpr); ok {
+ // Reference to top-level function ParseFile.
+ if isPkgDot(sel, "template", "ParseFile") {
+ sel.Sel.Name = "ParseFiles"
+ fixed = true
+ return
+ }
+ // Reference to ParseFiles method.
+ if typeof[sel.X] == "*template.Template" && sel.Sel.Name == "ParseFile" {
+ sel.Sel.Name = "ParseFiles"
+ fixed = true
+ return
+ }
+ // The Set type and its functions are now gone.
+ for _, name := range templateSetGlobals {
+ if isPkgDot(sel, "template", name) {
+ warn(sel.Pos(), "reference to template.%s must be fixed manually", name)
+ return
+ }
+ }
+ // The methods of Set are now gone.
+ for _, name := range templateSetMethods {
+ if typeof[sel.X] == "*template.Set" && sel.Sel.Name == name {
+ warn(sel.Pos(), "reference to template.*Set.%s must be fixed manually", name)
+ return
+ }
+ }
+ }
+ })
+
+ return fixed
+}
diff --git a/src/cmd/fix/template_test.go b/src/cmd/fix/template_test.go
new file mode 100644
index 000000000..f713a2901
--- /dev/null
+++ b/src/cmd/fix/template_test.go
@@ -0,0 +1,55 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func init() {
+ addTestCases(templateTests, template)
+}
+
+var templateTests = []testCase{
+ {
+ Name: "template.0",
+ In: `package main
+
+import (
+ "text/template"
+)
+
+func f() {
+ template.ParseFile(a)
+ var t template.Template
+ x, y := template.ParseFile()
+ template.New("x").Funcs(m).ParseFile(a) // chained method
+ // Output should complain about these as functions or methods.
+ var s *template.Set
+ s.ParseSetFiles(a)
+ template.ParseSetGlob(a)
+ s.ParseTemplateFiles(a)
+ template.ParseTemplateGlob(a)
+ x := template.SetMust(a())
+}
+`,
+ Out: `package main
+
+import (
+ "text/template"
+)
+
+func f() {
+ template.ParseFiles(a)
+ var t template.Template
+ x, y := template.ParseFiles()
+ template.New("x").Funcs(m).ParseFiles(a) // chained method
+ // Output should complain about these as functions or methods.
+ var s *template.Set
+ s.ParseSetFiles(a)
+ template.ParseSetGlob(a)
+ s.ParseTemplateFiles(a)
+ template.ParseTemplateGlob(a)
+ x := template.SetMust(a())
+}
+`,
+ },
+}
diff --git a/src/cmd/gofix/testdata/reflect.asn1.go.in b/src/cmd/fix/testdata/reflect.asn1.go.in
index 43128f6b2..43128f6b2 100644
--- a/src/cmd/gofix/testdata/reflect.asn1.go.in
+++ b/src/cmd/fix/testdata/reflect.asn1.go.in
diff --git a/src/cmd/gofix/testdata/reflect.asn1.go.out b/src/cmd/fix/testdata/reflect.asn1.go.out
index ba6224e6d..ba6224e6d 100644
--- a/src/cmd/gofix/testdata/reflect.asn1.go.out
+++ b/src/cmd/fix/testdata/reflect.asn1.go.out
diff --git a/src/cmd/gofix/testdata/reflect.datafmt.go.in b/src/cmd/fix/testdata/reflect.datafmt.go.in
index 91f885f9a..91f885f9a 100644
--- a/src/cmd/gofix/testdata/reflect.datafmt.go.in
+++ b/src/cmd/fix/testdata/reflect.datafmt.go.in
diff --git a/src/cmd/gofix/testdata/reflect.datafmt.go.out b/src/cmd/fix/testdata/reflect.datafmt.go.out
index fd447588b..fd447588b 100644
--- a/src/cmd/gofix/testdata/reflect.datafmt.go.out
+++ b/src/cmd/fix/testdata/reflect.datafmt.go.out
diff --git a/src/cmd/gofix/testdata/reflect.decode.go.in b/src/cmd/fix/testdata/reflect.decode.go.in
index f831abee3..f831abee3 100644
--- a/src/cmd/gofix/testdata/reflect.decode.go.in
+++ b/src/cmd/fix/testdata/reflect.decode.go.in
diff --git a/src/cmd/gofix/testdata/reflect.decode.go.out b/src/cmd/fix/testdata/reflect.decode.go.out
index fb7910ee3..fb7910ee3 100644
--- a/src/cmd/gofix/testdata/reflect.decode.go.out
+++ b/src/cmd/fix/testdata/reflect.decode.go.out
diff --git a/src/cmd/gofix/testdata/reflect.decoder.go.in b/src/cmd/fix/testdata/reflect.decoder.go.in
index 34364161a..0ce9b06fd 100644
--- a/src/cmd/gofix/testdata/reflect.decoder.go.in
+++ b/src/cmd/fix/testdata/reflect.decoder.go.in
@@ -44,7 +44,7 @@ func NewDecoder(r io.Reader) *Decoder {
func (dec *Decoder) recvType(id typeId) {
// Have we already seen this type? That's an error
if id < firstUserId || dec.wireType[id] != nil {
- dec.err = os.ErrorString("gob: duplicate type received")
+ dec.err = os.NewError("gob: duplicate type received")
return
}
@@ -143,7 +143,7 @@ func (dec *Decoder) decodeTypeSequence(isInterface bool) typeId {
// will be absorbed by recvMessage.)
if dec.buf.Len() > 0 {
if !isInterface {
- dec.err = os.ErrorString("extra data in buffer")
+ dec.err = os.NewError("extra data in buffer")
break
}
dec.nextUint()
@@ -165,7 +165,7 @@ func (dec *Decoder) Decode(e interface{}) os.Error {
// If e represents a value as opposed to a pointer, the answer won't
// get back to the caller. Make sure it's a pointer.
if value.Type().Kind() != reflect.Ptr {
- dec.err = os.ErrorString("gob: attempt to decode into a non-pointer")
+ dec.err = os.NewError("gob: attempt to decode into a non-pointer")
return dec.err
}
return dec.DecodeValue(value)
diff --git a/src/cmd/gofix/testdata/reflect.decoder.go.out b/src/cmd/fix/testdata/reflect.decoder.go.out
index ece88ecbe..ece88ecbe 100644
--- a/src/cmd/gofix/testdata/reflect.decoder.go.out
+++ b/src/cmd/fix/testdata/reflect.decoder.go.out
diff --git a/src/cmd/gofix/testdata/reflect.dnsmsg.go.in b/src/cmd/fix/testdata/reflect.dnsmsg.go.in
index 3d9c312f2..3d9c312f2 100644
--- a/src/cmd/gofix/testdata/reflect.dnsmsg.go.in
+++ b/src/cmd/fix/testdata/reflect.dnsmsg.go.in
diff --git a/src/cmd/gofix/testdata/reflect.dnsmsg.go.out b/src/cmd/fix/testdata/reflect.dnsmsg.go.out
index c777fe27c..c777fe27c 100644
--- a/src/cmd/gofix/testdata/reflect.dnsmsg.go.out
+++ b/src/cmd/fix/testdata/reflect.dnsmsg.go.out
diff --git a/src/cmd/gofix/testdata/reflect.encode.go.in b/src/cmd/fix/testdata/reflect.encode.go.in
index 26ce47039..26ce47039 100644
--- a/src/cmd/gofix/testdata/reflect.encode.go.in
+++ b/src/cmd/fix/testdata/reflect.encode.go.in
diff --git a/src/cmd/gofix/testdata/reflect.encode.go.out b/src/cmd/fix/testdata/reflect.encode.go.out
index 9a13a75ab..9a13a75ab 100644
--- a/src/cmd/gofix/testdata/reflect.encode.go.out
+++ b/src/cmd/fix/testdata/reflect.encode.go.out
diff --git a/src/cmd/gofix/testdata/reflect.encoder.go.in b/src/cmd/fix/testdata/reflect.encoder.go.in
index e52a4de29..0202d79ac 100644
--- a/src/cmd/gofix/testdata/reflect.encoder.go.in
+++ b/src/cmd/fix/testdata/reflect.encoder.go.in
@@ -50,7 +50,7 @@ func (enc *Encoder) popWriter() {
}
func (enc *Encoder) badType(rt reflect.Type) {
- enc.setError(os.ErrorString("gob: can't encode type " + rt.String()))
+ enc.setError(os.NewError("gob: can't encode type " + rt.String()))
}
func (enc *Encoder) setError(err os.Error) {
diff --git a/src/cmd/gofix/testdata/reflect.encoder.go.out b/src/cmd/fix/testdata/reflect.encoder.go.out
index 925d39301..925d39301 100644
--- a/src/cmd/gofix/testdata/reflect.encoder.go.out
+++ b/src/cmd/fix/testdata/reflect.encoder.go.out
diff --git a/src/cmd/gofix/testdata/reflect.export.go.in b/src/cmd/fix/testdata/reflect.export.go.in
index e91e777e3..ce7940b29 100644
--- a/src/cmd/gofix/testdata/reflect.export.go.in
+++ b/src/cmd/fix/testdata/reflect.export.go.in
@@ -22,8 +22,8 @@ package netchan
// BUG: can't use range clause to receive when using ImportNValues to limit the count.
import (
- "log"
"io"
+ "log"
"net"
"os"
"reflect"
@@ -343,20 +343,20 @@ func (exp *Exporter) Sync(timeout int64) os.Error {
func checkChan(chT interface{}, dir Dir) (*reflect.ChanValue, os.Error) {
chanType, ok := reflect.Typeof(chT).(*reflect.ChanType)
if !ok {
- return nil, os.ErrorString("not a channel")
+ return nil, os.NewError("not a channel")
}
if dir != Send && dir != Recv {
- return nil, os.ErrorString("unknown channel direction")
+ return nil, os.NewError("unknown channel direction")
}
switch chanType.Dir() {
case reflect.BothDir:
case reflect.SendDir:
if dir != Recv {
- return nil, os.ErrorString("to import/export with Send, must provide <-chan")
+ return nil, os.NewError("to import/export with Send, must provide <-chan")
}
case reflect.RecvDir:
if dir != Send {
- return nil, os.ErrorString("to import/export with Recv, must provide chan<-")
+ return nil, os.NewError("to import/export with Recv, must provide chan<-")
}
}
return reflect.NewValue(chT).(*reflect.ChanValue), nil
@@ -376,7 +376,7 @@ func (exp *Exporter) Export(name string, chT interface{}, dir Dir) os.Error {
defer exp.mu.Unlock()
_, present := exp.names[name]
if present {
- return os.ErrorString("channel name already being exported:" + name)
+ return os.NewError("channel name already being exported:" + name)
}
exp.names[name] = &chanDir{ch, dir}
return nil
@@ -393,7 +393,7 @@ func (exp *Exporter) Hangup(name string) os.Error {
// TODO drop all instances of channel from client sets
exp.mu.Unlock()
if !ok {
- return os.ErrorString("netchan export: hangup: no such channel: " + name)
+ return os.NewError("netchan export: hangup: no such channel: " + name)
}
chDir.ch.Close()
return nil
diff --git a/src/cmd/gofix/testdata/reflect.export.go.out b/src/cmd/fix/testdata/reflect.export.go.out
index 460edb40b..7bd73c5e7 100644
--- a/src/cmd/gofix/testdata/reflect.export.go.out
+++ b/src/cmd/fix/testdata/reflect.export.go.out
@@ -22,8 +22,8 @@ package netchan
// BUG: can't use range clause to receive when using ImportNValues to limit the count.
import (
- "log"
"io"
+ "log"
"net"
"os"
"reflect"
diff --git a/src/cmd/gofix/testdata/reflect.print.go.in b/src/cmd/fix/testdata/reflect.print.go.in
index cba1df296..6c9b8e4f9 100644
--- a/src/cmd/gofix/testdata/reflect.print.go.in
+++ b/src/cmd/fix/testdata/reflect.print.go.in
@@ -185,7 +185,7 @@ func Sprintf(format string, a ...interface{}) string {
// Errorf formats according to a format specifier and returns the string
// converted to an os.ErrorString, which satisfies the os.Error interface.
func Errorf(format string, a ...interface{}) os.Error {
- return os.ErrorString(Sprintf(format, a...))
+ return os.NewError(Sprintf(format, a...))
}
// These routines do not take a format string
diff --git a/src/cmd/gofix/testdata/reflect.print.go.out b/src/cmd/fix/testdata/reflect.print.go.out
index b475a2ae1..b475a2ae1 100644
--- a/src/cmd/gofix/testdata/reflect.print.go.out
+++ b/src/cmd/fix/testdata/reflect.print.go.out
diff --git a/src/cmd/gofix/testdata/reflect.quick.go.in b/src/cmd/fix/testdata/reflect.quick.go.in
index a5568b048..a5568b048 100644
--- a/src/cmd/gofix/testdata/reflect.quick.go.in
+++ b/src/cmd/fix/testdata/reflect.quick.go.in
diff --git a/src/cmd/gofix/testdata/reflect.quick.go.out b/src/cmd/fix/testdata/reflect.quick.go.out
index c62305b83..c62305b83 100644
--- a/src/cmd/gofix/testdata/reflect.quick.go.out
+++ b/src/cmd/fix/testdata/reflect.quick.go.out
diff --git a/src/cmd/gofix/testdata/reflect.read.go.in b/src/cmd/fix/testdata/reflect.read.go.in
index 9ae3bb8ee..487994ac6 100644
--- a/src/cmd/gofix/testdata/reflect.read.go.in
+++ b/src/cmd/fix/testdata/reflect.read.go.in
@@ -244,7 +244,7 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error {
switch v := val.(type) {
default:
- return os.ErrorString("unknown type " + v.Type().String())
+ return os.NewError("unknown type " + v.Type().String())
case *reflect.SliceValue:
typ := v.Type().(*reflect.SliceType)
@@ -483,7 +483,7 @@ Loop:
case nil:
// Probably a comment, handled below
default:
- return os.ErrorString("cannot happen: unknown type " + t.Type().String())
+ return os.NewError("cannot happen: unknown type " + t.Type().String())
case *reflect.IntValue:
if !getInt64() {
return err
diff --git a/src/cmd/gofix/testdata/reflect.read.go.out b/src/cmd/fix/testdata/reflect.read.go.out
index a6b126744..a6b126744 100644
--- a/src/cmd/gofix/testdata/reflect.read.go.out
+++ b/src/cmd/fix/testdata/reflect.read.go.out
diff --git a/src/cmd/gofix/testdata/reflect.scan.go.in b/src/cmd/fix/testdata/reflect.scan.go.in
index 83650e605..51898181f 100644
--- a/src/cmd/gofix/testdata/reflect.scan.go.in
+++ b/src/cmd/fix/testdata/reflect.scan.go.in
@@ -167,7 +167,7 @@ type ssave struct {
// satisfies io.Reader. It will never be called when used as
// intended, so there is no need to make it actually work.
func (s *ss) Read(buf []byte) (n int, err os.Error) {
- return 0, os.ErrorString("ScanState's Read should not be called. Use ReadRune")
+ return 0, os.NewError("ScanState's Read should not be called. Use ReadRune")
}
func (s *ss) ReadRune() (rune int, size int, err os.Error) {
@@ -240,7 +240,7 @@ func (s *ss) error(err os.Error) {
}
func (s *ss) errorString(err string) {
- panic(scanError{os.ErrorString(err)})
+ panic(scanError{os.NewError(err)})
}
func (s *ss) Token(skipSpace bool, f func(int) bool) (tok []byte, err os.Error) {
@@ -424,8 +424,8 @@ func (s *ss) typeError(field interface{}, expected string) {
s.errorString("expected field of type pointer to " + expected + "; found " + reflect.Typeof(field).String())
}
-var complexError = os.ErrorString("syntax error scanning complex number")
-var boolError = os.ErrorString("syntax error scanning boolean")
+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.
diff --git a/src/cmd/gofix/testdata/reflect.scan.go.out b/src/cmd/fix/testdata/reflect.scan.go.out
index 956c13bde..956c13bde 100644
--- a/src/cmd/gofix/testdata/reflect.scan.go.out
+++ b/src/cmd/fix/testdata/reflect.scan.go.out
diff --git a/src/cmd/gofix/testdata/reflect.script.go.in b/src/cmd/fix/testdata/reflect.script.go.in
index b341b1f89..b341b1f89 100644
--- a/src/cmd/gofix/testdata/reflect.script.go.in
+++ b/src/cmd/fix/testdata/reflect.script.go.in
diff --git a/src/cmd/gofix/testdata/reflect.script.go.out b/src/cmd/fix/testdata/reflect.script.go.out
index bc5a6a41d..bc5a6a41d 100644
--- a/src/cmd/gofix/testdata/reflect.script.go.out
+++ b/src/cmd/fix/testdata/reflect.script.go.out
diff --git a/src/cmd/gofix/testdata/reflect.template.go.in b/src/cmd/fix/testdata/reflect.template.go.in
index 1f5a8128f..1f5a8128f 100644
--- a/src/cmd/gofix/testdata/reflect.template.go.in
+++ b/src/cmd/fix/testdata/reflect.template.go.in
diff --git a/src/cmd/gofix/testdata/reflect.template.go.out b/src/cmd/fix/testdata/reflect.template.go.out
index f2f56ef3c..f2f56ef3c 100644
--- a/src/cmd/gofix/testdata/reflect.template.go.out
+++ b/src/cmd/fix/testdata/reflect.template.go.out
diff --git a/src/cmd/gofix/testdata/reflect.type.go.in b/src/cmd/fix/testdata/reflect.type.go.in
index 305d41980..34963bef9 100644
--- a/src/cmd/gofix/testdata/reflect.type.go.in
+++ b/src/cmd/fix/testdata/reflect.type.go.in
@@ -67,7 +67,7 @@ func validUserType(rt reflect.Type) (ut *userTypeInfo, err os.Error) {
ut.base = pt.Elem()
if ut.base == slowpoke { // ut.base lapped slowpoke
// recursive pointer type.
- return nil, os.ErrorString("can't represent recursive pointer type " + ut.base.String())
+ return nil, os.NewError("can't represent recursive pointer type " + ut.base.String())
}
if ut.indir%2 == 0 {
slowpoke = slowpoke.(*reflect.PtrType).Elem()
@@ -150,6 +150,7 @@ func userType(rt reflect.Type) *userTypeInfo {
}
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
@@ -524,7 +525,7 @@ func newTypeObject(name string, ut *userTypeInfo, rt reflect.Type) (gobType, os.
return st, nil
default:
- return nil, os.ErrorString("gob NewTypeObject can't handle type: " + rt.String())
+ return nil, os.NewError("gob NewTypeObject can't handle type: " + rt.String())
}
return nil, nil
}
diff --git a/src/cmd/gofix/testdata/reflect.type.go.out b/src/cmd/fix/testdata/reflect.type.go.out
index 9cd78296d..d729ea471 100644
--- a/src/cmd/gofix/testdata/reflect.type.go.out
+++ b/src/cmd/fix/testdata/reflect.type.go.out
@@ -150,6 +150,7 @@ func userType(rt reflect.Type) *userTypeInfo {
}
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
diff --git a/src/cmd/fix/timefileinfo.go b/src/cmd/fix/timefileinfo.go
new file mode 100644
index 000000000..b2ea23d8f
--- /dev/null
+++ b/src/cmd/fix/timefileinfo.go
@@ -0,0 +1,298 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "go/ast"
+ "go/token"
+ "strings"
+)
+
+func init() {
+ register(timefileinfoFix)
+}
+
+var timefileinfoFix = fix{
+ "time+fileinfo",
+ "2011-11-29",
+ timefileinfo,
+ `Rewrite for new time and os.FileInfo APIs.
+
+This fix applies some of the more mechanical changes,
+but most code will still need manual cleanup.
+
+http://codereview.appspot.com/5392041
+http://codereview.appspot.com/5416060
+`,
+}
+
+var timefileinfoTypeConfig = &TypeConfig{
+ Type: map[string]*Type{
+ "os.File": {
+ Method: map[string]string{
+ "Readdir": "func() []*os.FileInfo",
+ "Stat": "func() (*os.FileInfo, error)",
+ },
+ },
+ "time.Time": {
+ Method: map[string]string{
+ "Seconds": "time.raw",
+ "Nanoseconds": "time.raw",
+ },
+ },
+ },
+ Func: map[string]string{
+ "ioutil.ReadDir": "([]*os.FileInfo, error)",
+ "os.Stat": "(*os.FileInfo, error)",
+ "os.Lstat": "(*os.FileInfo, error)",
+ "time.LocalTime": "*time.Time",
+ "time.UTC": "*time.Time",
+ "time.SecondsToLocalTime": "*time.Time",
+ "time.SecondsToUTC": "*time.Time",
+ "time.NanosecondsToLocalTime": "*time.Time",
+ "time.NanosecondsToUTC": "*time.Time",
+ "time.Parse": "(*time.Time, error)",
+ "time.Nanoseconds": "time.raw",
+ "time.Seconds": "time.raw",
+ },
+}
+
+// timefileinfoIsOld reports whether f has evidence of being
+// "old code", from before the API changes. Evidence means:
+//
+// a mention of *os.FileInfo (the pointer)
+// a mention of *time.Time (the pointer)
+// a mention of old functions from package time
+// an attempt to call time.UTC
+//
+func timefileinfoIsOld(f *ast.File, typeof map[interface{}]string) bool {
+ old := false
+
+ // called records the expressions that appear as
+ // the function part of a function call, so that
+ // we can distinguish a ref to the possibly new time.UTC
+ // from the definitely old time.UTC() function call.
+ called := make(map[interface{}]bool)
+
+ before := func(n interface{}) {
+ if old {
+ return
+ }
+ if star, ok := n.(*ast.StarExpr); ok {
+ if isPkgDot(star.X, "os", "FileInfo") || isPkgDot(star.X, "time", "Time") {
+ old = true
+ return
+ }
+ }
+ if sel, ok := n.(*ast.SelectorExpr); ok {
+ if isTopName(sel.X, "time") {
+ if timefileinfoOldTimeFunc[sel.Sel.Name] {
+ old = true
+ return
+ }
+ }
+ if typeof[sel.X] == "os.FileInfo" || typeof[sel.X] == "*os.FileInfo" {
+ switch sel.Sel.Name {
+ case "Mtime_ns", "IsDirectory", "IsRegular":
+ old = true
+ return
+ case "Name", "Mode", "Size":
+ if !called[sel] {
+ old = true
+ return
+ }
+ }
+ }
+ }
+ call, ok := n.(*ast.CallExpr)
+ if ok && isPkgDot(call.Fun, "time", "UTC") {
+ old = true
+ return
+ }
+ if ok {
+ called[call.Fun] = true
+ }
+ }
+ walkBeforeAfter(f, before, nop)
+ return old
+}
+
+var timefileinfoOldTimeFunc = map[string]bool{
+ "LocalTime": true,
+ "SecondsToLocalTime": true,
+ "SecondsToUTC": true,
+ "NanosecondsToLocalTime": true,
+ "NanosecondsToUTC": true,
+ "Seconds": true,
+ "Nanoseconds": true,
+}
+
+var isTimeNow = map[string]bool{
+ "LocalTime": true,
+ "UTC": true,
+ "Seconds": true,
+ "Nanoseconds": true,
+}
+
+func timefileinfo(f *ast.File) bool {
+ if !imports(f, "os") && !imports(f, "time") && !imports(f, "io/ioutil") {
+ return false
+ }
+
+ typeof, _ := typecheck(timefileinfoTypeConfig, f)
+
+ if !timefileinfoIsOld(f, typeof) {
+ return false
+ }
+
+ fixed := false
+ walk(f, func(n interface{}) {
+ p, ok := n.(*ast.Expr)
+ if !ok {
+ return
+ }
+ nn := *p
+
+ // Rewrite *os.FileInfo and *time.Time to drop the pointer.
+ if star, ok := nn.(*ast.StarExpr); ok {
+ if isPkgDot(star.X, "os", "FileInfo") || isPkgDot(star.X, "time", "Time") {
+ fixed = true
+ *p = star.X
+ return
+ }
+ }
+
+ // Rewrite old time API calls to new calls.
+ // The code will still not compile after this edit,
+ // but the compiler will catch that, and the replacement
+ // code will be the correct functions to use in the new API.
+ if sel, ok := nn.(*ast.SelectorExpr); ok && isTopName(sel.X, "time") {
+ fn := sel.Sel.Name
+ if fn == "LocalTime" || fn == "Seconds" || fn == "Nanoseconds" {
+ fixed = true
+ sel.Sel.Name = "Now"
+ return
+ }
+ }
+
+ if call, ok := nn.(*ast.CallExpr); ok {
+ if sel, ok := call.Fun.(*ast.SelectorExpr); ok {
+ // Rewrite time.UTC but only when called (there's a new time.UTC var now).
+ if isPkgDot(sel, "time", "UTC") {
+ fixed = true
+ sel.Sel.Name = "Now"
+ // rewrite time.Now() into time.Now().UTC()
+ *p = &ast.CallExpr{
+ Fun: &ast.SelectorExpr{
+ X: call,
+ Sel: ast.NewIdent("UTC"),
+ },
+ }
+ return
+ }
+
+ // Rewrite conversions.
+ if ok && isTopName(sel.X, "time") && len(call.Args) == 1 {
+ fn := sel.Sel.Name
+ switch fn {
+ case "SecondsToLocalTime", "SecondsToUTC",
+ "NanosecondsToLocalTime", "NanosecondsToUTC":
+ fixed = true
+ sel.Sel.Name = "Unix"
+ call.Args = append(call.Args, nil)
+ if strings.HasPrefix(fn, "Seconds") {
+ // Unix(sec, 0)
+ call.Args[1] = ast.NewIdent("0")
+ } else {
+ // Unix(0, nsec)
+ call.Args[1] = call.Args[0]
+ call.Args[0] = ast.NewIdent("0")
+ }
+ if strings.HasSuffix(fn, "ToUTC") {
+ // rewrite call into call.UTC()
+ *p = &ast.CallExpr{
+ Fun: &ast.SelectorExpr{
+ X: call,
+ Sel: ast.NewIdent("UTC"),
+ },
+ }
+ }
+ return
+ }
+ }
+
+ // Rewrite method calls.
+ switch typeof[sel.X] {
+ case "*time.Time", "time.Time":
+ switch sel.Sel.Name {
+ case "Seconds":
+ fixed = true
+ sel.Sel.Name = "Unix"
+ return
+ case "Nanoseconds":
+ fixed = true
+ sel.Sel.Name = "UnixNano"
+ return
+ }
+
+ case "*os.FileInfo", "os.FileInfo":
+ switch sel.Sel.Name {
+ case "IsDirectory":
+ fixed = true
+ sel.Sel.Name = "IsDir"
+ return
+ case "IsRegular":
+ fixed = true
+ sel.Sel.Name = "IsDir"
+ *p = &ast.UnaryExpr{
+ Op: token.NOT,
+ X: call,
+ }
+ return
+ }
+ }
+ }
+ }
+
+ // Rewrite subtraction of two times.
+ // Cannot handle +=/-=.
+ if bin, ok := nn.(*ast.BinaryExpr); ok &&
+ bin.Op == token.SUB &&
+ (typeof[bin.X] == "time.raw" || typeof[bin.Y] == "time.raw") {
+ fixed = true
+ *p = &ast.CallExpr{
+ Fun: &ast.SelectorExpr{
+ X: bin.X,
+ Sel: ast.NewIdent("Sub"),
+ },
+ Args: []ast.Expr{bin.Y},
+ }
+ }
+
+ // Rewrite field references for os.FileInfo.
+ if sel, ok := nn.(*ast.SelectorExpr); ok {
+ if typ := typeof[sel.X]; typ == "*os.FileInfo" || typ == "os.FileInfo" {
+ addCall := false
+ switch sel.Sel.Name {
+ case "Name", "Size", "Mode":
+ fixed = true
+ addCall = true
+ case "Mtime_ns":
+ fixed = true
+ sel.Sel.Name = "ModTime"
+ addCall = true
+ }
+ if addCall {
+ *p = &ast.CallExpr{
+ Fun: sel,
+ }
+ return
+ }
+ }
+ }
+ })
+
+ return true
+}
diff --git a/src/cmd/fix/timefileinfo_test.go b/src/cmd/fix/timefileinfo_test.go
new file mode 100644
index 000000000..6573b8545
--- /dev/null
+++ b/src/cmd/fix/timefileinfo_test.go
@@ -0,0 +1,187 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func init() {
+ addTestCases(timefileinfoTests, timefileinfo)
+}
+
+var timefileinfoTests = []testCase{
+ {
+ Name: "timefileinfo.0",
+ In: `package main
+
+import "os"
+
+func main() {
+ st, _ := os.Stat("/etc/passwd")
+ _ = st.Name
+}
+`,
+ Out: `package main
+
+import "os"
+
+func main() {
+ st, _ := os.Stat("/etc/passwd")
+ _ = st.Name()
+}
+`,
+ },
+ {
+ Name: "timefileinfo.1",
+ In: `package main
+
+import "os"
+
+func main() {
+ st, _ := os.Stat("/etc/passwd")
+ _ = st.Size
+ _ = st.Mode
+ _ = st.Mtime_ns
+ _ = st.IsDirectory()
+ _ = st.IsRegular()
+}
+`,
+ Out: `package main
+
+import "os"
+
+func main() {
+ st, _ := os.Stat("/etc/passwd")
+ _ = st.Size()
+ _ = st.Mode()
+ _ = st.ModTime()
+ _ = st.IsDir()
+ _ = !st.IsDir()
+}
+`,
+ },
+ {
+ Name: "timefileinfo.2",
+ In: `package main
+
+import "os"
+
+func f(st *os.FileInfo) {
+ _ = st.Name
+ _ = st.Size
+ _ = st.Mode
+ _ = st.Mtime_ns
+ _ = st.IsDirectory()
+ _ = st.IsRegular()
+}
+`,
+ Out: `package main
+
+import "os"
+
+func f(st os.FileInfo) {
+ _ = st.Name()
+ _ = st.Size()
+ _ = st.Mode()
+ _ = st.ModTime()
+ _ = st.IsDir()
+ _ = !st.IsDir()
+}
+`,
+ },
+ {
+ Name: "timefileinfo.3",
+ In: `package main
+
+import "time"
+
+func main() {
+ _ = time.Seconds()
+ _ = time.Nanoseconds()
+ _ = time.LocalTime()
+ _ = time.UTC()
+ _ = time.SecondsToLocalTime(sec)
+ _ = time.SecondsToUTC(sec)
+ _ = time.NanosecondsToLocalTime(nsec)
+ _ = time.NanosecondsToUTC(nsec)
+}
+`,
+ Out: `package main
+
+import "time"
+
+func main() {
+ _ = time.Now()
+ _ = time.Now()
+ _ = time.Now()
+ _ = time.Now().UTC()
+ _ = time.Unix(sec, 0)
+ _ = time.Unix(sec, 0).UTC()
+ _ = time.Unix(0, nsec)
+ _ = time.Unix(0, nsec).UTC()
+}
+`,
+ },
+ {
+ Name: "timefileinfo.4",
+ In: `package main
+
+import "time"
+
+func f(*time.Time)
+
+func main() {
+ t := time.LocalTime()
+ _ = t.Seconds()
+ _ = t.Nanoseconds()
+
+ t1 := time.Nanoseconds()
+ f(nil)
+ t2 := time.Nanoseconds()
+ dt := t2 - t1
+}
+`,
+ Out: `package main
+
+import "time"
+
+func f(time.Time)
+
+func main() {
+ t := time.Now()
+ _ = t.Unix()
+ _ = t.UnixNano()
+
+ t1 := time.Now()
+ f(nil)
+ t2 := time.Now()
+ dt := t2.Sub(t1)
+}
+`,
+ },
+ {
+ Name: "timefileinfo.5", // test for issues 1505, 2636
+ In: `package main
+
+import (
+ "fmt"
+ "time"
+)
+
+func main() {
+ fmt.Println(time.SecondsToUTC(now)) // this comment must not introduce an illegal linebreak
+}
+`,
+ Out: `package main
+
+import (
+ "fmt"
+ "time"
+)
+
+func main() {
+ fmt.Println(time.Unix(now, 0).UTC( // this comment must not introduce an illegal linebreak
+ ))
+}
+`,
+ },
+}
diff --git a/src/cmd/gofix/typecheck.go b/src/cmd/fix/typecheck.go
index 2d81b9710..8e54314d1 100644
--- a/src/cmd/gofix/typecheck.go
+++ b/src/cmd/fix/typecheck.go
@@ -97,8 +97,9 @@ func (cfg *TypeConfig) typeof(name string) string {
// looked for in the Embed list.
type Type struct {
Field map[string]string // map field name to type
- Method map[string]string // map method name to comma-separated return types
+ Method map[string]string // map method name to comma-separated return types (should start with "func ")
Embed []string // list of types this type embeds (for extra methods)
+ Def string // definition of named type
}
// dot returns the type of "typ.name", making its decision
@@ -128,9 +129,16 @@ func (typ *Type) dot(cfg *TypeConfig, name string) string {
}
// typecheck type checks the AST f assuming the information in cfg.
-// It returns a map from AST nodes to type information in gofmt string form.
-func typecheck(cfg *TypeConfig, f *ast.File) map[interface{}]string {
- typeof := make(map[interface{}]string)
+// It returns two maps with type information:
+// typeof maps AST nodes to type information in gofmt string form.
+// assign maps type strings to lists of expressions that were assigned
+// to values of another type that were assigned to that type.
+func typecheck(cfg *TypeConfig, f *ast.File) (typeof map[interface{}]string, assign map[string][]interface{}) {
+ typeof = make(map[interface{}]string)
+ assign = make(map[string][]interface{})
+ cfg1 := &TypeConfig{}
+ *cfg1 = *cfg // make copy so we can add locally
+ copied := false
// gather function declarations
for _, decl := range f.Decls {
@@ -138,7 +146,7 @@ func typecheck(cfg *TypeConfig, f *ast.File) map[interface{}]string {
if !ok {
continue
}
- typecheck1(cfg, fn.Type, typeof)
+ typecheck1(cfg, fn.Type, typeof, assign)
t := typeof[fn.Type]
if fn.Recv != nil {
// The receiver must be a type.
@@ -168,8 +176,43 @@ func typecheck(cfg *TypeConfig, f *ast.File) map[interface{}]string {
}
}
- typecheck1(cfg, f, typeof)
- return typeof
+ // gather struct declarations
+ for _, decl := range f.Decls {
+ d, ok := decl.(*ast.GenDecl)
+ if ok {
+ for _, s := range d.Specs {
+ switch s := s.(type) {
+ case *ast.TypeSpec:
+ if cfg1.Type[s.Name.Name] != nil {
+ break
+ }
+ if !copied {
+ copied = true
+ // Copy map lazily: it's time.
+ cfg1.Type = make(map[string]*Type)
+ for k, v := range cfg.Type {
+ cfg1.Type[k] = v
+ }
+ }
+ t := &Type{Field: map[string]string{}}
+ cfg1.Type[s.Name.Name] = t
+ switch st := s.Type.(type) {
+ case *ast.StructType:
+ for _, f := range st.Fields.List {
+ for _, n := range f.Names {
+ t.Field[n.Name] = gofmt(f.Type)
+ }
+ }
+ case *ast.ArrayType, *ast.StarExpr, *ast.MapType:
+ t.Def = gofmt(st)
+ }
+ }
+ }
+ }
+ }
+
+ typecheck1(cfg1, f, typeof, assign)
+ return typeof, assign
}
func makeExprList(a []*ast.Ident) []ast.Expr {
@@ -183,11 +226,14 @@ func makeExprList(a []*ast.Ident) []ast.Expr {
// Typecheck1 is the recursive form of typecheck.
// It is like typecheck but adds to the information in typeof
// instead of allocating a new map.
-func typecheck1(cfg *TypeConfig, f interface{}, typeof map[interface{}]string) {
+func typecheck1(cfg *TypeConfig, f interface{}, typeof map[interface{}]string, assign map[string][]interface{}) {
// set sets the type of n to typ.
// If isDecl is true, n is being declared.
set := func(n ast.Expr, typ string, isDecl bool) {
if typeof[n] != "" || typ == "" {
+ if typeof[n] != typ {
+ assign[typ] = append(assign[typ], n)
+ }
return
}
typeof[n] = typ
@@ -236,6 +282,14 @@ func typecheck1(cfg *TypeConfig, f interface{}, typeof map[interface{}]string) {
}
}
+ expand := func(s string) string {
+ typ := cfg.Type[s]
+ if typ != nil && typ.Def != "" {
+ return typ.Def
+ }
+ return s
+ }
+
// The main type check is a recursive algorithm implemented
// by walkBeforeAfter(n, before, after).
// Most of it is bottom-up, but in a few places we need
@@ -263,7 +317,7 @@ func typecheck1(cfg *TypeConfig, f interface{}, typeof map[interface{}]string) {
defer func() {
if t := typeof[n]; t != "" {
pos := fset.Position(n.(ast.Node).Pos())
- fmt.Fprintf(os.Stderr, "%s: typeof[%s] = %s\n", pos.String(), gofmt(n), t)
+ fmt.Fprintf(os.Stderr, "%s: typeof[%s] = %s\n", pos, gofmt(n), t)
}
}()
}
@@ -405,6 +459,8 @@ func typecheck1(cfg *TypeConfig, f interface{}, typeof map[interface{}]string) {
// x.(T) has type T.
if t := typeof[n.Type]; isType(t) {
typeof[n] = getType(t)
+ } else {
+ typeof[n] = gofmt(n.Type)
}
case *ast.SliceExpr:
@@ -413,7 +469,7 @@ func typecheck1(cfg *TypeConfig, f interface{}, typeof map[interface{}]string) {
case *ast.IndexExpr:
// x[i] has key type of x's type.
- t := typeof[n.X]
+ t := expand(typeof[n.X])
if strings.HasPrefix(t, "[") || strings.HasPrefix(t, "map[") {
// Lazy: assume there are no nested [] in the array
// length or map key type.
@@ -426,7 +482,7 @@ func typecheck1(cfg *TypeConfig, f interface{}, typeof map[interface{}]string) {
// *x for x of type *T has type T when x is an expr.
// We don't use the result when *x is a type, but
// compute it anyway.
- t := typeof[n.X]
+ t := expand(typeof[n.X])
if isType(t) {
typeof[n] = "type *" + getType(t)
} else if strings.HasPrefix(t, "*") {
@@ -437,7 +493,7 @@ func typecheck1(cfg *TypeConfig, f interface{}, typeof map[interface{}]string) {
// &x for x of type T has type *T.
t := typeof[n.X]
if t != "" && n.Op == token.AND {
- typeof[n] = "&" + t
+ typeof[n] = "*" + t
}
case *ast.CompositeLit:
@@ -448,6 +504,39 @@ func typecheck1(cfg *TypeConfig, f interface{}, typeof map[interface{}]string) {
// (x) has type of x.
typeof[n] = typeof[n.X]
+ case *ast.RangeStmt:
+ t := expand(typeof[n.X])
+ if t == "" {
+ return
+ }
+ var key, value string
+ if t == "string" {
+ key, value = "int", "rune"
+ } else if strings.HasPrefix(t, "[") {
+ key = "int"
+ if i := strings.Index(t, "]"); i >= 0 {
+ value = t[i+1:]
+ }
+ } else if strings.HasPrefix(t, "map[") {
+ if i := strings.Index(t, "]"); i >= 0 {
+ key, value = t[4:i], t[i+1:]
+ }
+ }
+ changed := false
+ if n.Key != nil && key != "" {
+ changed = true
+ set(n.Key, key, n.Tok == token.DEFINE)
+ }
+ if n.Value != nil && value != "" {
+ changed = true
+ set(n.Value, value, n.Tok == token.DEFINE)
+ }
+ // Ugly failure of vision: already type-checked body.
+ // Do it again now that we have that type info.
+ if changed {
+ typecheck1(cfg, n.Body, typeof, assign)
+ }
+
case *ast.TypeSwitchStmt:
// Type of variable changes for each case in type switch,
// but go/parser generates just one variable.
@@ -471,7 +560,7 @@ func typecheck1(cfg *TypeConfig, f interface{}, typeof map[interface{}]string) {
tt = getType(tt)
typeof[varx] = tt
typeof[varx.Obj] = tt
- typecheck1(cfg, cas.Body, typeof)
+ typecheck1(cfg, cas.Body, typeof, assign)
}
}
}
diff --git a/src/cmd/gofix/url.go b/src/cmd/fix/url.go
index 7135d8edf..49aac739b 100644
--- a/src/cmd/gofix/url.go
+++ b/src/cmd/fix/url.go
@@ -4,17 +4,15 @@
package main
-import (
- "fmt"
- "os"
- "go/ast"
-)
+import "go/ast"
-var _ fmt.Stringer
-var _ os.Error
+func init() {
+ register(urlFix)
+}
var urlFix = fix{
"url",
+ "2011-08-17",
url,
`Move the URL pieces of package http into a new package, url.
@@ -22,10 +20,6 @@ http://codereview.appspot.com/4893043
`,
}
-func init() {
- register(urlFix)
-}
-
var urlRenames = []struct{ in, out string }{
{"URL", "URL"},
{"ParseURL", "Parse"},
@@ -46,12 +40,7 @@ func url(f *ast.File) bool {
fixed := false
// Update URL code.
- var skip interface{}
urlWalk := func(n interface{}) {
- if n == skip {
- skip = nil
- return
- }
// Is it an identifier?
if ident, ok := n.(*ast.Ident); ok && ident.Name == "url" {
ident.Name = "url_"
@@ -62,12 +51,6 @@ func url(f *ast.File) bool {
fixed = urlDoFields(fn.Params) || fixed
fixed = urlDoFields(fn.Results) || fixed
}
- // U{url: ...} is likely a struct field.
- if kv, ok := n.(*ast.KeyValueExpr); ok {
- if ident, ok := kv.Key.(*ast.Ident); ok && ident.Name == "url" {
- skip = ident
- }
- }
}
// Fix up URL code and add import, at most once.
@@ -75,8 +58,8 @@ func url(f *ast.File) bool {
if fixed {
return
}
- walkBeforeAfter(f, urlWalk, nop)
addImport(f, "url")
+ walkBeforeAfter(f, urlWalk, nop)
fixed = true
}
diff --git a/src/cmd/fix/url2.go b/src/cmd/fix/url2.go
new file mode 100644
index 000000000..5fd05ad2a
--- /dev/null
+++ b/src/cmd/fix/url2.go
@@ -0,0 +1,46 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "go/ast"
+
+func init() {
+ register(url2Fix)
+}
+
+var url2Fix = fix{
+ "url2",
+ "2012-02-16",
+ url2,
+ `Rename some functions in net/url.
+
+http://codereview.appspot.com/5671061
+`,
+}
+
+func url2(f *ast.File) bool {
+ if !imports(f, "net/url") {
+ return false
+ }
+
+ fixed := false
+
+ walk(f, func(n interface{}) {
+ // Rename functions and methods.
+ sel, ok := n.(*ast.SelectorExpr)
+ if !ok {
+ return
+ }
+ if !isTopName(sel.X, "url") {
+ return
+ }
+ if sel.Sel.Name == "ParseWithReference" {
+ sel.Sel.Name = "ParseWithFragment"
+ fixed = true
+ }
+ })
+
+ return fixed
+}
diff --git a/src/cmd/fix/url2_test.go b/src/cmd/fix/url2_test.go
new file mode 100644
index 000000000..c68dd88f1
--- /dev/null
+++ b/src/cmd/fix/url2_test.go
@@ -0,0 +1,31 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func init() {
+ addTestCases(url2Tests, url2)
+}
+
+var url2Tests = []testCase{
+ {
+ Name: "url2.0",
+ In: `package main
+
+import "net/url"
+
+func f() {
+ url.ParseWithReference("foo")
+}
+`,
+ Out: `package main
+
+import "net/url"
+
+func f() {
+ url.ParseWithFragment("foo")
+}
+`,
+ },
+}
diff --git a/src/cmd/gofix/url_test.go b/src/cmd/fix/url_test.go
index 8d9542cbc..39827f780 100644
--- a/src/cmd/gofix/url_test.go
+++ b/src/cmd/fix/url_test.go
@@ -5,7 +5,7 @@
package main
func init() {
- addTestCases(urlTests)
+ addTestCases(urlTests, url)
}
var urlTests = []testCase{
@@ -103,14 +103,14 @@ func h() (url string) {
import "url"
-type U struct{ url int }
+type U struct{ url_ int }
type M map[int]int
func f() {
url.Parse(a)
var url_ = 23
url_, x := 45, y
- _ = U{url: url_}
+ _ = U{url_: url_}
_ = M{url_ + 1: url_}
}
diff --git a/src/cmd/fix/xmlapi.go b/src/cmd/fix/xmlapi.go
new file mode 100644
index 000000000..e74425914
--- /dev/null
+++ b/src/cmd/fix/xmlapi.go
@@ -0,0 +1,111 @@
+// 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 (
+ "go/ast"
+)
+
+func init() {
+ register(xmlapiFix)
+}
+
+var xmlapiFix = fix{
+ "xmlapi",
+ "2012-01-23",
+ xmlapi,
+ `
+ Make encoding/xml's API look more like the rest of the encoding packages.
+
+http://codereview.appspot.com/5574053
+`,
+}
+
+var xmlapiTypeConfig = &TypeConfig{
+ Func: map[string]string{
+ "xml.NewParser": "*xml.Parser",
+ "os.Open": "*os.File",
+ "os.OpenFile": "*os.File",
+ "bytes.NewBuffer": "*bytes.Buffer",
+ "bytes.NewBufferString": "*bytes.Buffer",
+ "bufio.NewReader": "*bufio.Reader",
+ "bufio.NewReadWriter": "*bufio.ReadWriter",
+ },
+}
+
+var isReader = map[string]bool{
+ "*os.File": true,
+ "*bytes.Buffer": true,
+ "*bufio.Reader": true,
+ "*bufio.ReadWriter": true,
+ "io.Reader": true,
+}
+
+func xmlapi(f *ast.File) bool {
+ if !imports(f, "encoding/xml") {
+ return false
+ }
+
+ typeof, _ := typecheck(xmlapiTypeConfig, f)
+
+ fixed := false
+ walk(f, func(n interface{}) {
+ s, ok := n.(*ast.SelectorExpr)
+ if ok && typeof[s.X] == "*xml.Parser" && s.Sel.Name == "Unmarshal" {
+ s.Sel.Name = "DecodeElement"
+ fixed = true
+ return
+ }
+ if ok && isPkgDot(s, "xml", "Parser") {
+ s.Sel.Name = "Decoder"
+ fixed = true
+ return
+ }
+
+ call, ok := n.(*ast.CallExpr)
+ if !ok {
+ return
+ }
+ switch {
+ case len(call.Args) == 2 && isPkgDot(call.Fun, "xml", "Marshal"):
+ *call = xmlMarshal(call.Args)
+ fixed = true
+ case len(call.Args) == 2 && isPkgDot(call.Fun, "xml", "Unmarshal"):
+ if isReader[typeof[call.Args[0]]] {
+ *call = xmlUnmarshal(call.Args)
+ fixed = true
+ }
+ case len(call.Args) == 1 && isPkgDot(call.Fun, "xml", "NewParser"):
+ sel := call.Fun.(*ast.SelectorExpr).Sel
+ sel.Name = "NewDecoder"
+ fixed = true
+ }
+ })
+ return fixed
+}
+
+func xmlMarshal(args []ast.Expr) ast.CallExpr {
+ return xmlCallChain("NewEncoder", "Encode", args)
+}
+
+func xmlUnmarshal(args []ast.Expr) ast.CallExpr {
+ return xmlCallChain("NewDecoder", "Decode", args)
+}
+
+func xmlCallChain(first, second string, args []ast.Expr) ast.CallExpr {
+ return ast.CallExpr{
+ Fun: &ast.SelectorExpr{
+ X: &ast.CallExpr{
+ Fun: &ast.SelectorExpr{
+ X: ast.NewIdent("xml"),
+ Sel: ast.NewIdent(first),
+ },
+ Args: args[:1],
+ },
+ Sel: ast.NewIdent(second),
+ },
+ Args: args[1:2],
+ }
+}
diff --git a/src/cmd/fix/xmlapi_test.go b/src/cmd/fix/xmlapi_test.go
new file mode 100644
index 000000000..6486c8124
--- /dev/null
+++ b/src/cmd/fix/xmlapi_test.go
@@ -0,0 +1,85 @@
+// 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
+
+func init() {
+ addTestCases(xmlapiTests, xmlapi)
+}
+
+var xmlapiTests = []testCase{
+ {
+ Name: "xmlapi.0",
+ In: `package main
+
+import "encoding/xml"
+
+func f() {
+ xml.Marshal(a, b)
+ xml.Unmarshal(a, b)
+
+ var buf1 bytes.Buffer
+ buf2 := &bytes.Buffer{}
+ buf3 := bytes.NewBuffer(data)
+ buf4 := bytes.NewBufferString(data)
+ buf5 := bufio.NewReader(r)
+ xml.Unmarshal(&buf1, v)
+ xml.Unmarshal(buf2, v)
+ xml.Unmarshal(buf3, v)
+ xml.Unmarshal(buf4, v)
+ xml.Unmarshal(buf5, v)
+
+ f := os.Open("foo.xml")
+ xml.Unmarshal(f, v)
+
+ p1 := xml.NewParser(stream)
+ p1.Unmarshal(v, start)
+
+ var p2 *xml.Parser
+ p2.Unmarshal(v, start)
+}
+
+func g(r io.Reader, f *os.File, b []byte) {
+ xml.Unmarshal(r, v)
+ xml.Unmarshal(f, v)
+ xml.Unmarshal(b, v)
+}
+`,
+ Out: `package main
+
+import "encoding/xml"
+
+func f() {
+ xml.NewEncoder(a).Encode(b)
+ xml.Unmarshal(a, b)
+
+ var buf1 bytes.Buffer
+ buf2 := &bytes.Buffer{}
+ buf3 := bytes.NewBuffer(data)
+ buf4 := bytes.NewBufferString(data)
+ buf5 := bufio.NewReader(r)
+ xml.NewDecoder(&buf1).Decode(v)
+ xml.NewDecoder(buf2).Decode(v)
+ xml.NewDecoder(buf3).Decode(v)
+ xml.NewDecoder(buf4).Decode(v)
+ xml.NewDecoder(buf5).Decode(v)
+
+ f := os.Open("foo.xml")
+ xml.NewDecoder(f).Decode(v)
+
+ p1 := xml.NewDecoder(stream)
+ p1.DecodeElement(v, start)
+
+ var p2 *xml.Decoder
+ p2.DecodeElement(v, start)
+}
+
+func g(r io.Reader, f *os.File, b []byte) {
+ xml.NewDecoder(r).Decode(v)
+ xml.NewDecoder(f).Decode(v)
+ xml.Unmarshal(b, v)
+}
+`,
+ },
+}
diff --git a/src/cmd/gc/Makefile b/src/cmd/gc/Makefile
index 0af7659e4..58e25faaf 100644
--- a/src/cmd/gc/Makefile
+++ b/src/cmd/gc/Makefile
@@ -1,71 +1,17 @@
-# Copyright 2009 The Go Authors. All rights reserved.
+# 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 ../../Make.inc
-O:=$(HOST_O)
+include ../../Make.dist
-LIB=gc.a
+install: y.tab.h builtin.c
-HFILES=\
- go.h\
- y.tab.h\
- md5.h\
-
-YFILES=\
- go.y\
-
-OFILES=\
- align.$O\
- bits.$O\
- builtin.$O\
- closure.$O\
- const.$O\
- dcl.$O\
- export.$O\
- gen.$O\
- init.$O\
- lex.$O\
- md5.$O\
- mparith1.$O\
- mparith2.$O\
- mparith3.$O\
- obj.$O\
- print.$O\
- range.$O\
- reflect.$O\
- select.$O\
- sinit.$O\
- subr.$O\
- swt.$O\
- typecheck.$O\
- unsafe.$O\
- walk.$O\
- y1.tab.$O\
-
-NOINSTALL=1
-include ../../Make.clib
-
-install: $(LIB)
-
-y1.tab.c: y.tab.c # make yystate global, yytname mutable
+y.tab.h: go.y go.errors bisonerrors
+ bison -v -y -d go.y
+ # make yystate global, yytname mutable
cat y.tab.c | sed '/ int yystate;/d; s/int yychar;/int yychar, yystate;/; s/static const char \*const yytname/const char *yytname/; s/char const \*yymsgp/char *yymsgp/' >y1.tab.c
-
-yerr.h: bisonerrors go.errors y.tab.h # y.tab.h rule generates y.output too
+ mv y1.tab.c y.tab.c
awk -f bisonerrors y.output go.errors >yerr.h
-subr.$O: yerr.h
-
-builtin.c: builtin.c.boot
- cp builtin.c.boot builtin.c
-
-subr.$O: opnames.h
-
-opnames.h: mkopnames go.h
- ./mkopnames go.h >opnames.h
-
-CLEANFILES+=*.[568] [568].out y1.tab.c yerr.h mkbuiltin1 builtin.c _builtin.c opnames.h
-
-mkbuiltin1: mkbuiltin1.$O
- $(HOST_LD) -o $@ mkbuiltin1.$O -L"$(GOROOT)"/lib -lbio -l9 -lm $(HOST_LDFLAGS)
-
+builtin.c: runtime.go unsafe.go
+ ./mkbuiltin
diff --git a/src/cmd/gc/align.c b/src/cmd/gc/align.c
index 14c1c4a8d..6982bbe56 100644
--- a/src/cmd/gc/align.c
+++ b/src/cmd/gc/align.c
@@ -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 <u.h>
+#include <libc.h>
#include "go.h"
/*
@@ -283,6 +285,9 @@ dowidth(Type *t)
break;
}
+ if(widthptr == 4 && w != (int32)w)
+ yyerror("type %T too large", t);
+
t->width = w;
if(t->align == 0) {
if(w > 8 || (w&(w-1)) != 0)
@@ -489,12 +494,13 @@ typeinit(void)
okforeq[TPTR64] = 1;
okforeq[TUNSAFEPTR] = 1;
okforeq[TINTER] = 1;
- okforeq[TMAP] = 1;
okforeq[TCHAN] = 1;
- okforeq[TFUNC] = 1;
okforeq[TSTRING] = 1;
okforeq[TBOOL] = 1;
- okforeq[TARRAY] = 1; // refined in typecheck
+ okforeq[TMAP] = 1; // nil only; refined in typecheck
+ okforeq[TFUNC] = 1; // nil only; refined in typecheck
+ okforeq[TARRAY] = 1; // nil slice only; refined in typecheck
+ okforeq[TSTRUCT] = 1; // it's complicated; refined in typecheck
okforcmp[TSTRING] = 1;
diff --git a/src/cmd/gc/bisonerrors b/src/cmd/gc/bisonerrors
index 5110f5350..0f865d086 100755
--- a/src/cmd/gc/bisonerrors
+++ b/src/cmd/gc/bisonerrors
@@ -46,24 +46,36 @@ bison && /^state 0/ { grammar = 0; states = 1 }
states && /^state / { state = $2 }
states { statetext[state] = statetext[state] $0 "\n" }
-states && / shift, and go to state/ {
+states && / shift/ {
n = nshift[state]++
- shift[state,n] = $7
+ if($0 ~ /and go to/)
+ shift[state,n] = $7 # GNU Bison
+ else
+ shift[state,n] = $3 # Plan 9 Yacc
shifttoken[state,n] = $1
next
}
-states && / go to state/ {
+states && / (go to|goto)/ {
n = nshift[state]++
- shift[state,n] = $5
+ if($0 ~ /go to/)
+ shift[state,n] = $5 # GNU Bison
+ else
+ shift[state,n] = $3 # Plan 9 Yacc
shifttoken[state,n] = $1
next
}
-states && / reduce using rule/ {
+states && / reduce/ {
n = nreduce[state]++
- reduce[state,n] = $5
+ if($0 ~ /reduce using rule/)
+ reduce[state,n] = $5 # GNU Bison
+ else
+ reduce[state,n] = $3 # Plan 9 yacc
reducetoken[state,n] = $1
next
-}
+}
+
+# Skip over the summary information printed by Plan 9 yacc.
+/nonterminals$/,/^maximum spread/ { next }
# First // comment marks the beginning of the pattern file.
/^\/\// { bison = 0; grammar = 0; state = 0 }
@@ -96,7 +108,8 @@ $1 == "%" {
if(found)
continue
for(j=0; j<nreduce[state]; j++) {
- if(reducetoken[state,j] == tok || reducetoken[state,j] == "$default") {
+ t = reducetoken[state,j]
+ if(t == tok || t == "$default" || t == ".") {
stack[nstack++] = state
rule = reduce[state,j]
nstack -= rulesize[rule]
diff --git a/src/cmd/gc/bits.c b/src/cmd/gc/bits.c
index 7188ac411..c0fd4d85e 100644
--- a/src/cmd/gc/bits.c
+++ b/src/cmd/gc/bits.c
@@ -28,6 +28,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#include <u.h>
+#include <libc.h>
#include "go.h"
/*
@@ -148,12 +150,12 @@ Qconv(Fmt *fp)
first = 0;
else
fmtprint(fp, " ");
- if(var[i].sym == S)
- fmtprint(fp, "$%lld", var[i].offset);
+ if(var[i].node == N || var[i].node->sym == S)
+ fmtprint(fp, "$%d", i);
else {
- fmtprint(fp, var[i].sym->name);
+ fmtprint(fp, "%s", var[i].node->sym->name);
if(var[i].offset != 0)
- fmtprint(fp, "%+d", var[i].offset);
+ fmtprint(fp, "%+lld", (vlong)var[i].offset);
}
bits.b[i/32] &= ~(1L << (i%32));
}
diff --git a/src/cmd/gc/builtin.c b/src/cmd/gc/builtin.c
new file mode 100644
index 000000000..ca3d6670d
--- /dev/null
+++ b/src/cmd/gc/builtin.c
@@ -0,0 +1,118 @@
+char *runtimeimport =
+ "package runtime\n"
+ "import runtime \"runtime\"\n"
+ "func @\"\".new(@\"\".typ *byte) (? *any)\n"
+ "func @\"\".panicindex()\n"
+ "func @\"\".panicslice()\n"
+ "func @\"\".throwreturn()\n"
+ "func @\"\".throwinit()\n"
+ "func @\"\".panicwrap(? string, ? string, ? string)\n"
+ "func @\"\".panic(? interface {})\n"
+ "func @\"\".recover(? *int32) (? interface {})\n"
+ "func @\"\".printbool(? bool)\n"
+ "func @\"\".printfloat(? float64)\n"
+ "func @\"\".printint(? int64)\n"
+ "func @\"\".printuint(? uint64)\n"
+ "func @\"\".printcomplex(? complex128)\n"
+ "func @\"\".printstring(? string)\n"
+ "func @\"\".printpointer(? any)\n"
+ "func @\"\".printiface(? any)\n"
+ "func @\"\".printeface(? any)\n"
+ "func @\"\".printslice(? any)\n"
+ "func @\"\".printnl()\n"
+ "func @\"\".printsp()\n"
+ "func @\"\".goprintf()\n"
+ "func @\"\".concatstring()\n"
+ "func @\"\".append()\n"
+ "func @\"\".appendslice(@\"\".typ *byte, @\"\".x any, @\"\".y []any) (? any)\n"
+ "func @\"\".appendstr(@\"\".typ *byte, @\"\".x []byte, @\"\".y string) (? []byte)\n"
+ "func @\"\".cmpstring(? string, ? string) (? int)\n"
+ "func @\"\".slicestring(? string, ? int, ? int) (? string)\n"
+ "func @\"\".slicestring1(? string, ? int) (? string)\n"
+ "func @\"\".intstring(? int64) (? string)\n"
+ "func @\"\".slicebytetostring(? []byte) (? string)\n"
+ "func @\"\".slicerunetostring(? []rune) (? string)\n"
+ "func @\"\".stringtoslicebyte(? string) (? []byte)\n"
+ "func @\"\".stringtoslicerune(? string) (? []rune)\n"
+ "func @\"\".stringiter(? string, ? int) (? int)\n"
+ "func @\"\".stringiter2(? string, ? int) (@\"\".retk int, @\"\".retv rune)\n"
+ "func @\"\".copy(@\"\".to any, @\"\".fr any, @\"\".wid uint32) (? int)\n"
+ "func @\"\".slicestringcopy(@\"\".to any, @\"\".fr any) (? int)\n"
+ "func @\"\".convI2E(@\"\".elem any) (@\"\".ret any)\n"
+ "func @\"\".convI2I(@\"\".typ *byte, @\"\".elem any) (@\"\".ret any)\n"
+ "func @\"\".convT2E(@\"\".typ *byte, @\"\".elem any) (@\"\".ret any)\n"
+ "func @\"\".convT2I(@\"\".typ *byte, @\"\".typ2 *byte, @\"\".elem any) (@\"\".ret any)\n"
+ "func @\"\".assertE2E(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any)\n"
+ "func @\"\".assertE2E2(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any, @\"\".ok bool)\n"
+ "func @\"\".assertE2I(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any)\n"
+ "func @\"\".assertE2I2(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any, @\"\".ok bool)\n"
+ "func @\"\".assertE2T(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any)\n"
+ "func @\"\".assertE2T2(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any, @\"\".ok bool)\n"
+ "func @\"\".assertI2E(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any)\n"
+ "func @\"\".assertI2E2(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any, @\"\".ok bool)\n"
+ "func @\"\".assertI2I(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any)\n"
+ "func @\"\".assertI2I2(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any, @\"\".ok bool)\n"
+ "func @\"\".assertI2T(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any)\n"
+ "func @\"\".assertI2T2(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any, @\"\".ok bool)\n"
+ "func @\"\".ifaceeq(@\"\".i1 any, @\"\".i2 any) (@\"\".ret bool)\n"
+ "func @\"\".efaceeq(@\"\".i1 any, @\"\".i2 any) (@\"\".ret bool)\n"
+ "func @\"\".ifacethash(@\"\".i1 any) (@\"\".ret uint32)\n"
+ "func @\"\".efacethash(@\"\".i1 any) (@\"\".ret uint32)\n"
+ "func @\"\".equal(@\"\".typ *byte, @\"\".x1 any, @\"\".x2 any) (@\"\".ret bool)\n"
+ "func @\"\".makemap(@\"\".mapType *byte, @\"\".hint int64) (@\"\".hmap map[any]any)\n"
+ "func @\"\".mapaccess1(@\"\".mapType *byte, @\"\".hmap map[any]any, @\"\".key any) (@\"\".val any)\n"
+ "func @\"\".mapaccess2(@\"\".mapType *byte, @\"\".hmap map[any]any, @\"\".key any) (@\"\".val any, @\"\".pres bool)\n"
+ "func @\"\".mapassign1(@\"\".mapType *byte, @\"\".hmap map[any]any, @\"\".key any, @\"\".val any)\n"
+ "func @\"\".mapassign2(@\"\".mapType *byte, @\"\".hmap map[any]any, @\"\".key any, @\"\".val any, @\"\".pres bool)\n"
+ "func @\"\".mapiterinit(@\"\".mapType *byte, @\"\".hmap map[any]any, @\"\".hiter *any)\n"
+ "func @\"\".mapdelete(@\"\".mapType *byte, @\"\".hmap map[any]any, @\"\".key any)\n"
+ "func @\"\".mapiternext(@\"\".hiter *any)\n"
+ "func @\"\".mapiter1(@\"\".hiter *any) (@\"\".key any)\n"
+ "func @\"\".mapiter2(@\"\".hiter *any) (@\"\".key any, @\"\".val any)\n"
+ "func @\"\".makechan(@\"\".chanType *byte, @\"\".hint int64) (@\"\".hchan chan any)\n"
+ "func @\"\".chanrecv1(@\"\".chanType *byte, @\"\".hchan <-chan any) (@\"\".elem any)\n"
+ "func @\"\".chanrecv2(@\"\".chanType *byte, @\"\".hchan <-chan any) (@\"\".elem any, @\"\".received bool)\n"
+ "func @\"\".chansend1(@\"\".chanType *byte, @\"\".hchan chan<- any, @\"\".elem any)\n"
+ "func @\"\".closechan(@\"\".hchan any)\n"
+ "func @\"\".selectnbsend(@\"\".chanType *byte, @\"\".hchan chan<- any, @\"\".elem any) (? bool)\n"
+ "func @\"\".selectnbrecv(@\"\".chanType *byte, @\"\".elem *any, @\"\".hchan <-chan any) (? bool)\n"
+ "func @\"\".selectnbrecv2(@\"\".chanType *byte, @\"\".elem *any, @\"\".received *bool, @\"\".hchan <-chan any) (? bool)\n"
+ "func @\"\".newselect(@\"\".size int) (@\"\".sel *byte)\n"
+ "func @\"\".selectsend(@\"\".sel *byte, @\"\".hchan chan<- any, @\"\".elem *any) (@\"\".selected bool)\n"
+ "func @\"\".selectrecv(@\"\".sel *byte, @\"\".hchan <-chan any, @\"\".elem *any) (@\"\".selected bool)\n"
+ "func @\"\".selectrecv2(@\"\".sel *byte, @\"\".hchan <-chan any, @\"\".elem *any, @\"\".received *bool) (@\"\".selected bool)\n"
+ "func @\"\".selectdefault(@\"\".sel *byte) (@\"\".selected bool)\n"
+ "func @\"\".selectgo(@\"\".sel *byte)\n"
+ "func @\"\".block()\n"
+ "func @\"\".makeslice(@\"\".typ *byte, @\"\".nel int64, @\"\".cap int64) (@\"\".ary []any)\n"
+ "func @\"\".growslice(@\"\".typ *byte, @\"\".old []any, @\"\".n int64) (@\"\".ary []any)\n"
+ "func @\"\".sliceslice1(@\"\".old []any, @\"\".lb uint64, @\"\".width uint64) (@\"\".ary []any)\n"
+ "func @\"\".sliceslice(@\"\".old []any, @\"\".lb uint64, @\"\".hb uint64, @\"\".width uint64) (@\"\".ary []any)\n"
+ "func @\"\".slicearray(@\"\".old *any, @\"\".nel uint64, @\"\".lb uint64, @\"\".hb uint64, @\"\".width uint64) (@\"\".ary []any)\n"
+ "func @\"\".closure()\n"
+ "func @\"\".memequal(@\"\".eq *bool, @\"\".size uintptr, @\"\".x *any, @\"\".y *any)\n"
+ "func @\"\".memequal8(@\"\".eq *bool, @\"\".size uintptr, @\"\".x *any, @\"\".y *any)\n"
+ "func @\"\".memequal16(@\"\".eq *bool, @\"\".size uintptr, @\"\".x *any, @\"\".y *any)\n"
+ "func @\"\".memequal32(@\"\".eq *bool, @\"\".size uintptr, @\"\".x *any, @\"\".y *any)\n"
+ "func @\"\".memequal64(@\"\".eq *bool, @\"\".size uintptr, @\"\".x *any, @\"\".y *any)\n"
+ "func @\"\".memequal128(@\"\".eq *bool, @\"\".size uintptr, @\"\".x *any, @\"\".y *any)\n"
+ "func @\"\".int64div(? int64, ? int64) (? int64)\n"
+ "func @\"\".uint64div(? uint64, ? uint64) (? uint64)\n"
+ "func @\"\".int64mod(? int64, ? int64) (? int64)\n"
+ "func @\"\".uint64mod(? uint64, ? uint64) (? uint64)\n"
+ "func @\"\".float64toint64(? float64) (? int64)\n"
+ "func @\"\".float64touint64(? float64) (? uint64)\n"
+ "func @\"\".int64tofloat64(? int64) (? float64)\n"
+ "func @\"\".uint64tofloat64(? uint64) (? float64)\n"
+ "func @\"\".complex128div(@\"\".num complex128, @\"\".den complex128) (@\"\".quo complex128)\n"
+ "\n"
+ "$$\n";
+char *unsafeimport =
+ "package unsafe\n"
+ "import runtime \"runtime\"\n"
+ "type @\"\".Pointer uintptr\n"
+ "func @\"\".Offsetof(? any) (? uintptr)\n"
+ "func @\"\".Sizeof(? any) (? uintptr)\n"
+ "func @\"\".Alignof(? any) (? uintptr)\n"
+ "\n"
+ "$$\n";
diff --git a/src/cmd/gc/builtin.c.boot b/src/cmd/gc/builtin.c.boot
deleted file mode 100644
index 190c56008..000000000
--- a/src/cmd/gc/builtin.c.boot
+++ /dev/null
@@ -1,114 +0,0 @@
-char *runtimeimport =
- "package runtime\n"
- "import runtime \"runtime\"\n"
- "func \"\".new (? int32) *any\n"
- "func \"\".panicindex ()\n"
- "func \"\".panicslice ()\n"
- "func \"\".throwreturn ()\n"
- "func \"\".throwinit ()\n"
- "func \"\".panicwrap (? string, ? string, ? string)\n"
- "func \"\".panic (? interface { })\n"
- "func \"\".recover (? *int32) interface { }\n"
- "func \"\".printbool (? bool)\n"
- "func \"\".printfloat (? float64)\n"
- "func \"\".printint (? int64)\n"
- "func \"\".printuint (? uint64)\n"
- "func \"\".printcomplex (? complex128)\n"
- "func \"\".printstring (? string)\n"
- "func \"\".printpointer (? any)\n"
- "func \"\".printiface (? any)\n"
- "func \"\".printeface (? any)\n"
- "func \"\".printslice (? any)\n"
- "func \"\".printnl ()\n"
- "func \"\".printsp ()\n"
- "func \"\".goprintf ()\n"
- "func \"\".concatstring ()\n"
- "func \"\".append ()\n"
- "func \"\".appendslice (typ *uint8, x any, y []any) any\n"
- "func \"\".cmpstring (? string, ? string) int\n"
- "func \"\".slicestring (? string, ? int, ? int) string\n"
- "func \"\".slicestring1 (? string, ? int) string\n"
- "func \"\".intstring (? int64) string\n"
- "func \"\".slicebytetostring (? []uint8) string\n"
- "func \"\".sliceinttostring (? []int) string\n"
- "func \"\".stringtoslicebyte (? string) []uint8\n"
- "func \"\".stringtosliceint (? string) []int\n"
- "func \"\".stringiter (? string, ? int) int\n"
- "func \"\".stringiter2 (? string, ? int) (retk int, retv int)\n"
- "func \"\".slicecopy (to any, fr any, wid uint32) int\n"
- "func \"\".slicestringcopy (to any, fr any) int\n"
- "func \"\".convI2E (elem any) any\n"
- "func \"\".convI2I (typ *uint8, elem any) any\n"
- "func \"\".convT2E (typ *uint8, elem any) any\n"
- "func \"\".convT2I (typ *uint8, typ2 *uint8, elem any) any\n"
- "func \"\".assertE2E (typ *uint8, iface any) any\n"
- "func \"\".assertE2E2 (typ *uint8, iface any) (ret any, ok bool)\n"
- "func \"\".assertE2I (typ *uint8, iface any) any\n"
- "func \"\".assertE2I2 (typ *uint8, iface any) (ret any, ok bool)\n"
- "func \"\".assertE2T (typ *uint8, iface any) any\n"
- "func \"\".assertE2T2 (typ *uint8, iface any) (ret any, ok bool)\n"
- "func \"\".assertI2E (typ *uint8, iface any) any\n"
- "func \"\".assertI2E2 (typ *uint8, iface any) (ret any, ok bool)\n"
- "func \"\".assertI2I (typ *uint8, iface any) any\n"
- "func \"\".assertI2I2 (typ *uint8, iface any) (ret any, ok bool)\n"
- "func \"\".assertI2T (typ *uint8, iface any) any\n"
- "func \"\".assertI2T2 (typ *uint8, iface any) (ret any, ok bool)\n"
- "func \"\".ifaceeq (i1 any, i2 any) bool\n"
- "func \"\".efaceeq (i1 any, i2 any) bool\n"
- "func \"\".ifacethash (i1 any) uint32\n"
- "func \"\".efacethash (i1 any) uint32\n"
- "func \"\".makemap (mapType *uint8, hint int64) map[any] any\n"
- "func \"\".mapaccess1 (mapType *uint8, hmap map[any] any, key any) any\n"
- "func \"\".mapaccess2 (mapType *uint8, hmap map[any] any, key any) (val any, pres bool)\n"
- "func \"\".mapassign1 (mapType *uint8, hmap map[any] any, key any, val any)\n"
- "func \"\".mapassign2 (mapType *uint8, hmap map[any] any, key any, val any, pres bool)\n"
- "func \"\".mapiterinit (mapType *uint8, hmap map[any] any, hiter *any)\n"
- "func \"\".mapiternext (hiter *any)\n"
- "func \"\".mapiter1 (hiter *any) any\n"
- "func \"\".mapiter2 (hiter *any) (key any, val any)\n"
- "func \"\".makechan (chanType *uint8, hint int64) chan any\n"
- "func \"\".chanrecv1 (chanType *uint8, hchan <-chan any) any\n"
- "func \"\".chanrecv2 (chanType *uint8, hchan <-chan any) (elem any, received bool)\n"
- "func \"\".chansend1 (chanType *uint8, hchan chan<- any, elem any)\n"
- "func \"\".closechan (hchan any)\n"
- "func \"\".selectnbsend (chanType *uint8, hchan chan<- any, elem any) bool\n"
- "func \"\".selectnbrecv (chanType *uint8, elem *any, hchan <-chan any) bool\n"
- "func \"\".selectnbrecv2 (chanType *uint8, elem *any, received *bool, hchan <-chan any) bool\n"
- "func \"\".newselect (size int) *uint8\n"
- "func \"\".selectsend (sel *uint8, hchan chan<- any, elem *any) bool\n"
- "func \"\".selectrecv (sel *uint8, hchan <-chan any, elem *any) bool\n"
- "func \"\".selectrecv2 (sel *uint8, hchan <-chan any, elem *any, received *bool) bool\n"
- "func \"\".selectdefault (sel *uint8) bool\n"
- "func \"\".selectgo (sel *uint8)\n"
- "func \"\".block ()\n"
- "func \"\".makeslice (typ *uint8, nel int64, cap int64) []any\n"
- "func \"\".growslice (typ *uint8, old []any, n int64) []any\n"
- "func \"\".sliceslice1 (old []any, lb uint64, width uint64) []any\n"
- "func \"\".sliceslice (old []any, lb uint64, hb uint64, width uint64) []any\n"
- "func \"\".slicearray (old *any, nel uint64, lb uint64, hb uint64, width uint64) []any\n"
- "func \"\".closure ()\n"
- "func \"\".int64div (? int64, ? int64) int64\n"
- "func \"\".uint64div (? uint64, ? uint64) uint64\n"
- "func \"\".int64mod (? int64, ? int64) int64\n"
- "func \"\".uint64mod (? uint64, ? uint64) uint64\n"
- "func \"\".float64toint64 (? float64) int64\n"
- "func \"\".float64touint64 (? float64) uint64\n"
- "func \"\".int64tofloat64 (? int64) float64\n"
- "func \"\".uint64tofloat64 (? uint64) float64\n"
- "func \"\".complex128div (num complex128, den complex128) complex128\n"
- "\n"
- "$$\n";
-char *unsafeimport =
- "package unsafe\n"
- "import runtime \"runtime\"\n"
- "type \"\".Pointer uintptr\n"
- "func \"\".Offsetof (? any) uintptr\n"
- "func \"\".Sizeof (? any) uintptr\n"
- "func \"\".Alignof (? any) uintptr\n"
- "func \"\".Typeof (i interface { }) interface { }\n"
- "func \"\".Reflect (i interface { }) (typ interface { }, addr \"\".Pointer)\n"
- "func \"\".Unreflect (typ interface { }, addr \"\".Pointer) interface { }\n"
- "func \"\".New (typ interface { }) \"\".Pointer\n"
- "func \"\".NewArray (typ interface { }, n int) \"\".Pointer\n"
- "\n"
- "$$\n";
diff --git a/src/cmd/gc/closure.c b/src/cmd/gc/closure.c
index 1261eefb7..fa44e40fa 100644
--- a/src/cmd/gc/closure.c
+++ b/src/cmd/gc/closure.c
@@ -6,6 +6,8 @@
* function literals aka closures
*/
+#include <u.h>
+#include <libc.h>
#include "go.h"
void
@@ -57,7 +59,6 @@ closurebody(NodeList *body)
body = list1(nod(OEMPTY, N, N));
func = curfn;
- l = func->dcl;
func->nbody = body;
funcbody(func);
@@ -129,6 +130,8 @@ makeclosure(Node *func, NodeList **init, int nowrap)
static int closgen;
char *p;
+ USED(init);
+
/*
* wrap body in external function
* with extra closure parameters.
@@ -189,6 +192,10 @@ walkclosure(Node *func, NodeList **init)
Node *xtype, *xfunc, *call, *clos;
NodeList *l, *in;
+ // no closure vars, don't bother wrapping
+ if(func->cvars == nil)
+ return makeclosure(func, init, 1)->nname;
+
/*
* wrap body in external function
* with extra closure parameters.
diff --git a/src/cmd/gc/const.c b/src/cmd/gc/const.c
index 36a64cb97..e27c88338 100644
--- a/src/cmd/gc/const.c
+++ b/src/cmd/gc/const.c
@@ -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 <u.h>
+#include <libc.h>
#include "go.h"
#define TUP(x,y) (((x)<<16)|(y))
@@ -85,6 +87,8 @@ convlit1(Node **np, Type *t, int explicit)
switch(n->op) {
default:
+ if(n->type == idealbool)
+ n->type = types[TBOOL];
if(n->type->etype == TIDEAL) {
convlit(&n->left, t);
convlit(&n->right, t);
@@ -106,7 +110,7 @@ convlit1(Node **np, Type *t, int explicit)
if(t != T && t->etype == TIDEAL && n->val.ctype != CTINT)
n->val = toint(n->val);
if(t != T && !isint[t->etype]) {
- yyerror("invalid operation: %#N (shift of type %T)", n, t);
+ yyerror("invalid operation: %N (shift of type %T)", n, t);
t = T;
}
n->type = t;
@@ -168,6 +172,7 @@ convlit1(Node **np, Type *t, int explicit)
break;
case CTINT:
+ case CTRUNE:
case CTFLT:
case CTCPLX:
ct = n->val.ctype;
@@ -177,6 +182,7 @@ convlit1(Node **np, Type *t, int explicit)
goto bad;
case CTCPLX:
case CTFLT:
+ case CTRUNE:
n->val = toint(n->val);
// flowthrough
case CTINT:
@@ -190,6 +196,7 @@ convlit1(Node **np, Type *t, int explicit)
goto bad;
case CTCPLX:
case CTINT:
+ case CTRUNE:
n->val = toflt(n->val);
// flowthrough
case CTFLT:
@@ -204,6 +211,7 @@ convlit1(Node **np, Type *t, int explicit)
goto bad;
case CTFLT:
case CTINT:
+ case CTRUNE:
n->val = tocplx(n->val);
break;
case CTCPLX:
@@ -211,7 +219,7 @@ convlit1(Node **np, Type *t, int explicit)
break;
}
} else
- if(et == TSTRING && ct == CTINT && explicit)
+ if(et == TSTRING && (ct == CTINT || ct == CTRUNE) && explicit)
n->val = tostr(n->val);
else
goto bad;
@@ -222,7 +230,7 @@ convlit1(Node **np, Type *t, int explicit)
bad:
if(!n->diag) {
- yyerror("cannot convert %#N to type %T", n, t);
+ yyerror("cannot convert %N to type %T", n, t);
n->diag = 1;
}
if(isideal(n->type)) {
@@ -241,6 +249,7 @@ copyval(Val v)
switch(v.ctype) {
case CTINT:
+ case CTRUNE:
i = mal(sizeof(*i));
mpmovefixfix(i, v.u.xval);
v.u.xval = i;
@@ -267,6 +276,7 @@ tocplx(Val v)
switch(v.ctype) {
case CTINT:
+ case CTRUNE:
c = mal(sizeof(*c));
mpmovefixflt(&c->real, v.u.xval);
mpmovecflt(&c->imag, 0.0);
@@ -291,6 +301,7 @@ toflt(Val v)
switch(v.ctype) {
case CTINT:
+ case CTRUNE:
f = mal(sizeof(*f));
mpmovefixflt(f, v.u.xval);
v.ctype = CTFLT;
@@ -314,6 +325,9 @@ toint(Val v)
Mpint *i;
switch(v.ctype) {
+ case CTRUNE:
+ v.ctype = CTINT;
+ break;
case CTFLT:
i = mal(sizeof(*i));
if(mpmovefltfix(i, v.u.fval) < 0)
@@ -343,6 +357,7 @@ overflow(Val v, Type *t)
return;
switch(v.ctype) {
case CTINT:
+ case CTRUNE:
if(!isint[t->etype])
fatal("overflow: %T integer constant", t);
if(mpcmpfixfix(v.u.xval, minintval[t->etype]) < 0 ||
@@ -377,6 +392,7 @@ tostr(Val v)
switch(v.ctype) {
case CTINT:
+ case CTRUNE:
if(mpcmpfixfix(v.u.xval, minintval[TINT]) < 0 ||
mpcmpfixfix(v.u.xval, maxintval[TINT]) > 0)
yyerror("overflow in int -> string");
@@ -413,7 +429,12 @@ consttype(Node *n)
int
isconst(Node *n, int ct)
{
- return consttype(n) == ct;
+ int t;
+
+ t = consttype(n);
+ // If the caller is asking for CTINT, allow CTRUNE too.
+ // Makes life easier for back ends.
+ return t == ct || (ct == CTINT && t == CTRUNE);
}
/*
@@ -422,7 +443,7 @@ isconst(Node *n, int ct)
void
evconst(Node *n)
{
- Node *nl, *nr;
+ Node *nl, *nr, *norig;
int32 len;
Strlit *str;
int wl, wr, lno, et;
@@ -516,7 +537,8 @@ evconst(Node *n)
n->right = nr;
if(nr->type && (issigned[nr->type->etype] || !isint[nr->type->etype]))
goto illegal;
- nl->val = toint(nl->val);
+ if(nl->val.ctype != CTRUNE)
+ nl->val = toint(nl->val);
nr->val = toint(nr->val);
break;
}
@@ -538,6 +560,17 @@ evconst(Node *n)
v = toflt(v);
rv = toflt(rv);
}
+
+ // Rune and int turns into rune.
+ if(v.ctype == CTRUNE && rv.ctype == CTINT)
+ rv.ctype = CTRUNE;
+ if(v.ctype == CTINT && rv.ctype == CTRUNE) {
+ if(n->op == OLSH || n->op == ORSH)
+ rv.ctype = CTINT;
+ else
+ v.ctype = CTRUNE;
+ }
+
if(v.ctype != rv.ctype) {
// Use of undefined name as constant?
if((v.ctype == 0 || rv.ctype == 0) && nerrors > 0)
@@ -557,15 +590,19 @@ evconst(Node *n)
return;
case TUP(OADD, CTINT):
- mpaddfixfix(v.u.xval, rv.u.xval);
+ case TUP(OADD, CTRUNE):
+ mpaddfixfix(v.u.xval, rv.u.xval, 0);
break;
case TUP(OSUB, CTINT):
+ case TUP(OSUB, CTRUNE):
mpsubfixfix(v.u.xval, rv.u.xval);
break;
case TUP(OMUL, CTINT):
+ case TUP(OMUL, CTRUNE):
mpmulfixfix(v.u.xval, rv.u.xval);
break;
case TUP(ODIV, CTINT):
+ case TUP(ODIV, CTRUNE):
if(mpcmpfixc(rv.u.xval, 0) == 0) {
yyerror("division by zero");
mpmovecfix(v.u.xval, 1);
@@ -574,6 +611,7 @@ evconst(Node *n)
mpdivfixfix(v.u.xval, rv.u.xval);
break;
case TUP(OMOD, CTINT):
+ case TUP(OMOD, CTRUNE):
if(mpcmpfixc(rv.u.xval, 0) == 0) {
yyerror("division by zero");
mpmovecfix(v.u.xval, 1);
@@ -583,21 +621,27 @@ evconst(Node *n)
break;
case TUP(OLSH, CTINT):
+ case TUP(OLSH, CTRUNE):
mplshfixfix(v.u.xval, rv.u.xval);
break;
case TUP(ORSH, CTINT):
+ case TUP(ORSH, CTRUNE):
mprshfixfix(v.u.xval, rv.u.xval);
break;
case TUP(OOR, CTINT):
+ case TUP(OOR, CTRUNE):
mporfixfix(v.u.xval, rv.u.xval);
break;
case TUP(OAND, CTINT):
+ case TUP(OAND, CTRUNE):
mpandfixfix(v.u.xval, rv.u.xval);
break;
case TUP(OANDNOT, CTINT):
+ case TUP(OANDNOT, CTRUNE):
mpandnotfixfix(v.u.xval, rv.u.xval);
break;
case TUP(OXOR, CTINT):
+ case TUP(OXOR, CTRUNE):
mpxorfixfix(v.u.xval, rv.u.xval);
break;
@@ -618,6 +662,14 @@ evconst(Node *n)
}
mpdivfltflt(v.u.fval, rv.u.fval);
break;
+ case TUP(OMOD, CTFLT):
+ // The default case above would print 'ideal % ideal',
+ // which is not quite an ideal error.
+ if(!n->diag) {
+ yyerror("illegal constant expression: floating-point %% operation");
+ n->diag = 1;
+ }
+ return;
case TUP(OADD, CTCPLX):
mpaddfltflt(&v.u.cval->real, &rv.u.cval->real);
@@ -647,26 +699,32 @@ evconst(Node *n)
goto setfalse;
case TUP(OEQ, CTINT):
+ case TUP(OEQ, CTRUNE):
if(mpcmpfixfix(v.u.xval, rv.u.xval) == 0)
goto settrue;
goto setfalse;
case TUP(ONE, CTINT):
+ case TUP(ONE, CTRUNE):
if(mpcmpfixfix(v.u.xval, rv.u.xval) != 0)
goto settrue;
goto setfalse;
case TUP(OLT, CTINT):
+ case TUP(OLT, CTRUNE):
if(mpcmpfixfix(v.u.xval, rv.u.xval) < 0)
goto settrue;
goto setfalse;
case TUP(OLE, CTINT):
+ case TUP(OLE, CTRUNE):
if(mpcmpfixfix(v.u.xval, rv.u.xval) <= 0)
goto settrue;
goto setfalse;
case TUP(OGE, CTINT):
+ case TUP(OGE, CTRUNE):
if(mpcmpfixfix(v.u.xval, rv.u.xval) >= 0)
goto settrue;
goto setfalse;
case TUP(OGT, CTINT):
+ case TUP(OGT, CTRUNE):
if(mpcmpfixfix(v.u.xval, rv.u.xval) > 0)
goto settrue;
goto setfalse;
@@ -784,17 +842,21 @@ unary:
}
// fall through
case TUP(OCONV, CTINT):
+ case TUP(OCONV, CTRUNE):
case TUP(OCONV, CTFLT):
case TUP(OCONV, CTSTR):
convlit1(&nl, n->type, 1);
break;
case TUP(OPLUS, CTINT):
+ case TUP(OPLUS, CTRUNE):
break;
case TUP(OMINUS, CTINT):
+ case TUP(OMINUS, CTRUNE):
mpnegfix(v.u.xval);
break;
case TUP(OCOM, CTINT):
+ case TUP(OCOM, CTRUNE):
et = Txxx;
if(nl->type != T)
et = nl->type->etype;
@@ -840,8 +902,15 @@ unary:
}
ret:
- // rewrite n in place.
+ if(n == n->orig) {
+ // duplicate node for n->orig.
+ norig = nod(OLITERAL, N, N);
+ *norig = *n;
+ } else
+ norig = n->orig;
*n = *nl;
+ // restore value of n->orig.
+ n->orig = norig;
n->val = v;
// check range.
@@ -880,6 +949,7 @@ nodlit(Val v)
n->type = idealbool;
break;
case CTINT:
+ case CTRUNE:
case CTFLT:
case CTCPLX:
n->type = types[TIDEAL];
@@ -937,11 +1007,15 @@ defaultlit(Node **np, Type *t)
defaultlit(&n->left, t);
t = n->left->type;
if(t != T && !isint[t->etype]) {
- yyerror("invalid operation: %#N (shift of type %T)", n, t);
+ yyerror("invalid operation: %N (shift of type %T)", n, t);
t = T;
}
n->type = t;
return;
+ case ONOT:
+ defaultlit(&n->left, t);
+ n->type = n->left->type;
+ return;
default:
if(n->left == N) {
dump("defaultlit", n);
@@ -961,13 +1035,18 @@ defaultlit(Node **np, Type *t)
} else if(t == T && (n->left->op == OLSH || n->left->op == ORSH)) {
defaultlit(&n->right, T);
defaultlit(&n->left, n->right->type);
+ } else if(iscmp[n->op]) {
+ defaultlit2(&n->left, &n->right, 1);
} else {
defaultlit(&n->left, t);
defaultlit(&n->right, t);
}
- if(n->type == idealbool || n->type == idealstring)
- n->type = types[n->type->etype];
- else
+ if(n->type == idealbool || n->type == idealstring) {
+ if(t != T && t->etype == n->type->etype)
+ n->type = t;
+ else
+ n->type = types[n->type->etype];
+ } else
n->type = n->left->type;
return;
}
@@ -989,7 +1068,7 @@ defaultlit(Node **np, Type *t)
n->type = types[TSTRING];
break;
}
- yyerror("defaultlit: unknown literal: %#N", n);
+ yyerror("defaultlit: unknown literal: %N", n);
break;
case CTBOOL:
n->type = types[TBOOL];
@@ -999,6 +1078,9 @@ defaultlit(Node **np, Type *t)
case CTINT:
n->type = types[TINT];
goto num;
+ case CTRUNE:
+ n->type = runetype;
+ goto num;
case CTFLT:
n->type = types[TFLOAT64];
goto num;
@@ -1053,6 +1135,10 @@ defaultlit2(Node **lp, Node **rp, int force)
}
if(!force)
return;
+ if(l->type->etype == TBOOL) {
+ convlit(lp, types[TBOOL]);
+ convlit(rp, types[TBOOL]);
+ }
if(isconst(l, CTCPLX) || isconst(r, CTCPLX)) {
convlit(lp, types[TCOMPLEX128]);
convlit(rp, types[TCOMPLEX128]);
@@ -1063,6 +1149,13 @@ defaultlit2(Node **lp, Node **rp, int force)
convlit(rp, types[TFLOAT64]);
return;
}
+
+ if(isconst(l, CTRUNE) || isconst(r, CTRUNE)) {
+ convlit(lp, runetype);
+ convlit(rp, runetype);
+ return;
+ }
+
convlit(lp, types[TINT]);
convlit(rp, types[TINT]);
}
@@ -1099,7 +1192,7 @@ cmpslit(Node *l, Node *r)
int
smallintconst(Node *n)
{
- if(n->op == OLITERAL && n->type != T)
+ if(n->op == OLITERAL && isconst(n, CTINT) && n->type != T)
switch(simtype[n->type->etype]) {
case TINT8:
case TUINT8:
@@ -1110,6 +1203,7 @@ smallintconst(Node *n)
case TBOOL:
case TPTR32:
return 1;
+ case TIDEAL:
case TINT64:
case TUINT64:
if(mpcmpfixfix(n->val.u.xval, minintval[TINT32]) < 0
@@ -1200,6 +1294,7 @@ convconst(Node *con, Type *t, Val *val)
default:
fatal("convconst ctype=%d %lT", val->ctype, t);
case CTINT:
+ case CTRUNE:
i = mpgetfix(val->u.xval);
break;
case CTBOOL:
diff --git a/src/cmd/gc/cplx.c b/src/cmd/gc/cplx.c
index 890cf7f10..dea7bc3bb 100644
--- a/src/cmd/gc/cplx.c
+++ b/src/cmd/gc/cplx.c
@@ -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 <u.h>
+#include <libc.h>
#include "gg.h"
static void subnode(Node *nr, Node *ni, Node *nc);
@@ -131,6 +133,9 @@ complexgen(Node *n, Node *res)
dump("\ncomplexgen-n", n);
dump("complexgen-res", res);
}
+
+ while(n->op == OCONVNOP)
+ n = n->left;
// pick off float/complex opcodes
switch(n->op) {
@@ -199,6 +204,8 @@ complexgen(Node *n, Node *res)
case OIND:
case ONAME: // PHEAP or PPARAMREF var
case OCALLFUNC:
+ case OCALLMETH:
+ case OCALLINTER:
igen(n, &n1, res);
complexmove(&n1, res);
regfree(&n1);
diff --git a/src/cmd/gc/dcl.c b/src/cmd/gc/dcl.c
index 5bfeeb97a..4121a45ab 100644
--- a/src/cmd/gc/dcl.c
+++ b/src/cmd/gc/dcl.c
@@ -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 <u.h>
+#include <libc.h>
#include "go.h"
#include "y.tab.h"
static void funcargs(Node*);
+static void funcargs2(Type*);
static int
dflag(void)
@@ -115,6 +118,8 @@ dumpdcl(char *st)
Sym *s, *d;
int i;
+ USED(st);
+
i = 0;
for(d=dclstack; d!=S; d=d->link) {
i++;
@@ -170,6 +175,11 @@ declare(Node *n, int ctxt)
n->lineno = parserline();
s = n->sym;
+
+ // kludgy: typecheckok means we're past parsing. Eg genwrapper may declare out of package names later.
+ if(importpkg == nil && !typecheckok && s->pkg != localpkg)
+ yyerror("cannot declare name %S", s);
+
gen = 0;
if(ctxt == PEXTERN) {
externdcl = list(externdcl, n);
@@ -188,7 +198,7 @@ declare(Node *n, int ctxt)
n->curfn = curfn;
}
if(ctxt == PAUTO)
- n->xoffset = BADWIDTH;
+ n->xoffset = 0;
if(s->block == block)
redeclare(s, "in this block");
@@ -411,6 +421,7 @@ oldname(Sym *s)
c->funcdepth = funcdepth;
c->outer = n->closure;
n->closure = c;
+ n->addrtaken = 1;
c->closure = n;
c->xoffset = 0;
curfn->cvars = list(curfn->cvars, c);
@@ -468,7 +479,7 @@ colasdefn(NodeList *left, Node *defn)
if(isblank(n))
continue;
if(!colasname(n)) {
- yyerror("non-name %#N on left side of :=", n);
+ yyerror("non-name %N on left side of :=", n);
nerr++;
continue;
}
@@ -542,13 +553,6 @@ ifacedcl(Node *n)
void
funchdr(Node *n)
{
-
- if(n->nname != N) {
- n->nname->op = ONAME;
- declare(n->nname, PFUNC);
- n->nname->defn = n;
- }
-
// change the declaration context from extern to auto
if(funcdepth == 0 && dclcontext != PEXTERN)
fatal("funchdr: dclcontext");
@@ -559,16 +563,19 @@ funchdr(Node *n)
n->outer = curfn;
curfn = n;
+
if(n->nname)
funcargs(n->nname->ntype);
- else
+ else if (n->ntype)
funcargs(n->ntype);
+ else
+ funcargs2(n->type);
}
static void
funcargs(Node *nt)
{
- Node *n;
+ Node *n, *nn;
NodeList *l;
int gen;
@@ -577,11 +584,11 @@ funcargs(Node *nt)
// declare the receiver and in arguments.
// no n->defn because type checking of func header
- // will fill in the types before we can demand them.
+ // will not fill in the types until later
if(nt->left != N) {
n = nt->left;
if(n->op != ODCLFIELD)
- fatal("funcargs1 %O", n->op);
+ fatal("funcargs receiver %O", n->op);
if(n->left != N) {
n->left->op = ONAME;
n->left->ntype = n->right;
@@ -591,7 +598,7 @@ funcargs(Node *nt)
for(l=nt->list; l; l=l->next) {
n = l->n;
if(n->op != ODCLFIELD)
- fatal("funcargs2 %O", n->op);
+ fatal("funcargs in %O", n->op);
if(n->left != N) {
n->left->op = ONAME;
n->left->ntype = n->right;
@@ -604,12 +611,16 @@ funcargs(Node *nt)
for(l=nt->rlist; l; l=l->next) {
n = l->n;
if(n->op != ODCLFIELD)
- fatal("funcargs3 %O", n->op);
+ fatal("funcargs out %O", n->op);
if(n->left != N) {
n->left->op = ONAME;
n->left->ntype = n->right;
if(isblank(n->left)) {
// Give it a name so we can assign to it during return.
+ // preserve the original in ->orig
+ nn = nod(OXXX, N, N);
+ *nn = *n->left;
+ n->left = nn;
snprint(namebuf, sizeof(namebuf), ".anon%d", gen++);
n->left->sym = lookup(namebuf);
}
@@ -619,6 +630,48 @@ funcargs(Node *nt)
}
/*
+ * Same as funcargs, except run over an already constructed TFUNC.
+ * This happens during import, where the hidden_fndcl rule has
+ * used functype directly to parse the function's type.
+ */
+static void
+funcargs2(Type *t)
+{
+ Type *ft;
+ Node *n;
+
+ if(t->etype != TFUNC)
+ fatal("funcargs2 %T", t);
+
+ if(t->thistuple)
+ for(ft=getthisx(t)->type; ft; ft=ft->down) {
+ if(!ft->nname || !ft->nname->sym)
+ continue;
+ n = ft->nname; // no need for newname(ft->nname->sym)
+ n->type = ft->type;
+ declare(n, PPARAM);
+ }
+
+ if(t->intuple)
+ for(ft=getinargx(t)->type; ft; ft=ft->down) {
+ if(!ft->nname || !ft->nname->sym)
+ continue;
+ n = ft->nname;
+ n->type = ft->type;
+ declare(n, PPARAM);
+ }
+
+ if(t->outtuple)
+ for(ft=getoutargx(t)->type; ft; ft=ft->down) {
+ if(!ft->nname || !ft->nname->sym)
+ continue;
+ n = ft->nname;
+ n->type = ft->type;
+ declare(n, PPARAMOUT);
+ }
+}
+
+/*
* finish the body.
* called in auto-declaration context.
* returns in extern-declaration context.
@@ -645,7 +698,7 @@ typedcl0(Sym *s)
{
Node *n;
- n = dclname(s);
+ n = newname(s);
n->op = OTYPE;
declare(n, dclcontext);
return n;
@@ -665,217 +718,265 @@ typedcl1(Node *n, Node *t, int local)
}
/*
- * typedcl1 but during imports
+ * structs, functions, and methods.
+ * they don't belong here, but where do they belong?
*/
-void
-typedcl2(Type *pt, Type *t)
+
+static void
+checkembeddedtype(Type *t)
{
- Node *n;
+ if (t == T)
+ return;
- // override declaration in unsafe.go for Pointer.
- // there is no way in Go code to define unsafe.Pointer
- // so we have to supply it.
- if(incannedimport &&
- strcmp(importpkg->name, "unsafe") == 0 &&
- strcmp(pt->nod->sym->name, "Pointer") == 0) {
- t = types[TUNSAFEPTR];
+ if(t->sym == S && isptr[t->etype]) {
+ t = t->type;
+ if(t->etype == TINTER)
+ yyerror("embedded type cannot be a pointer to interface");
}
+ if(isptr[t->etype])
+ yyerror("embedded type cannot be a pointer");
+ else if(t->etype == TFORW && t->embedlineno == 0)
+ t->embedlineno = lineno;
+}
- if(pt->etype == TFORW)
- goto ok;
- if(!eqtype(pt->orig, t))
- yyerror("inconsistent definition for type %S during import\n\t%lT\n\t%lT", pt->sym, pt->orig, t);
- return;
+static Type*
+structfield(Node *n)
+{
+ Type *f;
+ int lno;
-ok:
- n = pt->nod;
- copytype(pt->nod, t);
- // unzero nod
- pt->nod = n;
+ lno = lineno;
+ lineno = n->lineno;
+
+ if(n->op != ODCLFIELD)
+ fatal("structfield: oops %N\n", n);
+
+ f = typ(TFIELD);
+ f->isddd = n->isddd;
+
+ if(n->right != N) {
+ typecheck(&n->right, Etype);
+ n->type = n->right->type;
+ if(n->left != N)
+ n->left->type = n->type;
+ if(n->embedded)
+ checkembeddedtype(n->type);
+ }
+ n->right = N;
+
+ f->type = n->type;
+ if(f->type == T)
+ f->broke = 1;
+
+ switch(n->val.ctype) {
+ case CTSTR:
+ f->note = n->val.u.sval;
+ break;
+ default:
+ yyerror("field annotation must be string");
+ // fallthrough
+ case CTxxx:
+ f->note = nil;
+ break;
+ }
- pt->sym->lastlineno = parserline();
- declare(n, PEXTERN);
+ if(n->left && n->left->op == ONAME) {
+ f->nname = n->left;
+ f->embedded = n->embedded;
+ f->sym = f->nname->sym;
+ }
- checkwidth(pt);
+ lineno = lno;
+ return f;
}
-/*
- * structs, functions, and methods.
- * they don't belong here, but where do they belong?
- */
+static void
+checkdupfields(Type *t, char* what)
+{
+ Type* t1;
+ int lno;
+ lno = lineno;
+
+ for( ; t; t=t->down)
+ if(t->sym && t->nname && !isblank(t->nname))
+ for(t1=t->down; t1; t1=t1->down)
+ if(t1->sym == t->sym) {
+ lineno = t->nname->lineno;
+ yyerror("duplicate %s %s", what, t->sym->name);
+ break;
+ }
+
+ lineno = lno;
+}
/*
- * turn a parsed struct into a type
+ * convert a parsed id/type list into
+ * a type for struct/interface/arglist
*/
-static Type**
-stotype(NodeList *l, int et, Type **t, int funarg)
+Type*
+tostruct(NodeList *l)
{
- Type *f, *t1, *t2, **t0;
- Strlit *note;
+ Type *t, *f, **tp;
+ t = typ(TSTRUCT);
+
+ for(tp = &t->type; l; l=l->next) {
+ f = structfield(l->n);
+
+ *tp = f;
+ tp = &f->down;
+ }
+
+ for(f=t->type; f && !t->broke; f=f->down)
+ if(f->broke)
+ t->broke = 1;
+
+ checkdupfields(t->type, "field");
+
+ if (!t->broke)
+ checkwidth(t);
+
+ return t;
+}
+
+static Type*
+tofunargs(NodeList *l)
+{
+ Type *t, *f, **tp;
+
+ t = typ(TSTRUCT);
+ t->funarg = 1;
+
+ for(tp = &t->type; l; l=l->next) {
+ f = structfield(l->n);
+ f->funarg = 1;
+
+ // esc.c needs to find f given a PPARAM to add the tag.
+ if(l->n->left && l->n->left->class == PPARAM)
+ l->n->left->paramfld = f;
+
+ *tp = f;
+ tp = &f->down;
+ }
+
+ for(f=t->type; f && !t->broke; f=f->down)
+ if(f->broke)
+ t->broke = 1;
+
+ checkdupfields(t->type, "argument");
+ return t;
+}
+
+static Type*
+interfacefield(Node *n)
+{
+ Type *f;
int lno;
- Node *n, *left;
- char *what;
- t0 = t;
lno = lineno;
- what = "field";
- if(et == TINTER)
- what = "method";
+ lineno = n->lineno;
- for(; l; l=l->next) {
- n = l->n;
- lineno = n->lineno;
- note = nil;
+ if(n->op != ODCLFIELD)
+ fatal("interfacefield: oops %N\n", n);
- if(n->op != ODCLFIELD)
- fatal("stotype: oops %N\n", n);
- left = n->left;
- if(funarg && isblank(left))
- left = N;
- if(n->right != N) {
- if(et == TINTER && left != N) {
- // queue resolution of method type for later.
- // right now all we need is the name list.
- // avoids cycles for recursive interface types.
- n->type = typ(TINTERMETH);
- n->type->nname = n->right;
- n->right = N;
- left->type = n->type;
- queuemethod(n);
- } else {
- typecheck(&n->right, Etype);
- n->type = n->right->type;
- if(n->type == T)
- continue;
- if(left != N)
- left->type = n->type;
- n->right = N;
- if(n->embedded && n->type != T) {
- t1 = n->type;
- if(t1->sym == S && isptr[t1->etype]) {
- t1 = t1->type;
- if(t1->etype == TINTER)
- yyerror("embedded type cannot be a pointer to interface");
- }
- if(isptr[t1->etype])
- yyerror("embedded type cannot be a pointer");
- else if(t1->etype == TFORW && t1->embedlineno == 0)
- t1->embedlineno = lineno;
- }
+ if (n->val.ctype != CTxxx)
+ yyerror("interface method cannot have annotation");
+
+ f = typ(TFIELD);
+ f->isddd = n->isddd;
+
+ if(n->right != N) {
+ if(n->left != N) {
+ // queue resolution of method type for later.
+ // right now all we need is the name list.
+ // avoids cycles for recursive interface types.
+ n->type = typ(TINTERMETH);
+ n->type->nname = n->right;
+ n->left->type = n->type;
+ queuemethod(n);
+
+ if(n->left->op == ONAME) {
+ f->nname = n->left;
+ f->embedded = n->embedded;
+ f->sym = f->nname->sym;
+ if(importpkg && !exportname(f->sym->name))
+ f->sym = pkglookup(f->sym->name, structpkg);
}
- }
- if(n->type == T) {
- // assume error already printed
- continue;
- }
+ } else {
- switch(n->val.ctype) {
- case CTSTR:
- if(et != TSTRUCT)
- yyerror("interface method cannot have annotation");
- note = n->val.u.sval;
- break;
- default:
- if(et != TSTRUCT)
- yyerror("interface method cannot have annotation");
- else
- yyerror("field annotation must be string");
- case CTxxx:
- note = nil;
- break;
- }
+ typecheck(&n->right, Etype);
+ n->type = n->right->type;
+
+ if(n->embedded)
+ checkembeddedtype(n->type);
- if(et == TINTER && left == N) {
- // embedded interface - inline the methods
- if(n->type->etype != TINTER) {
- if(n->type->etype == TFORW)
+ if(n->type)
+ switch(n->type->etype) {
+ case TINTER:
+ break;
+ case TFORW:
yyerror("interface type loop involving %T", n->type);
- else
+ f->broke = 1;
+ break;
+ default:
yyerror("interface contains embedded non-interface %T", n->type);
- continue;
- }
- for(t1=n->type->type; t1!=T; t1=t1->down) {
- f = typ(TFIELD);
- f->type = t1->type;
- f->width = BADWIDTH;
- f->nname = newname(t1->sym);
- f->sym = t1->sym;
- for(t2=*t0; t2!=T; t2=t2->down) {
- if(t2->sym == f->sym) {
- yyerror("duplicate method %s", t2->sym->name);
- break;
- }
+ f->broke = 1;
+ break;
}
- *t = f;
- t = &f->down;
- }
- continue;
}
-
- f = typ(TFIELD);
- f->type = n->type;
- f->note = note;
- f->width = BADWIDTH;
- f->isddd = n->isddd;
-
- if(left != N && left->op == ONAME) {
- f->nname = left;
- f->embedded = n->embedded;
- f->sym = f->nname->sym;
- if(importpkg && !exportname(f->sym->name))
- f->sym = pkglookup(f->sym->name, structpkg);
- if(f->sym && !isblank(f->nname)) {
- for(t1=*t0; t1!=T; t1=t1->down) {
- if(t1->sym == f->sym) {
- yyerror("duplicate %s %s", what, t1->sym->name);
- break;
- }
- }
- }
- }
-
- *t = f;
- t = &f->down;
}
- *t = T;
+ n->right = N;
+
+ f->type = n->type;
+ if(f->type == T)
+ f->broke = 1;
+
lineno = lno;
- return t;
+ return f;
}
Type*
-dostruct(NodeList *l, int et)
+tointerface(NodeList *l)
{
- Type *t;
- int funarg;
+ Type *t, *f, **tp, *t1;
- /*
- * convert a parsed id/type list into
- * a type for struct/interface/arglist
- */
+ t = typ(TINTER);
- funarg = 0;
- if(et == TFUNC) {
- funarg = 1;
- et = TSTRUCT;
- }
- t = typ(et);
- t->funarg = funarg;
- stotype(l, et, &t->type, funarg);
- if(t->type == T && l != nil) {
- t->broke = 1;
- return t;
+ tp = &t->type;
+ for(; l; l=l->next) {
+ f = interfacefield(l->n);
+
+ if (l->n->left == N && f->type->etype == TINTER) {
+ // embedded interface, inline methods
+ for(t1=f->type->type; t1; t1=t1->down) {
+ f = typ(TFIELD);
+ f->type = t1->type;
+ f->broke = t1->broke;
+ f->sym = t1->sym;
+ if(f->sym)
+ f->nname = newname(f->sym);
+ *tp = f;
+ tp = &f->down;
+ }
+ } else {
+ *tp = f;
+ tp = &f->down;
+ }
}
- if(et == TINTER)
- t = sortinter(t);
- if(!funarg)
- checkwidth(t);
+
+ for(f=t->type; f && !t->broke; f=f->down)
+ if(f->broke)
+ t->broke = 1;
+
+ checkdupfields(t->type, "method");
+ t = sortinter(t);
+ checkwidth(t);
+
return t;
}
-
Node*
embedded(Sym *s)
{
@@ -892,7 +993,10 @@ embedded(Sym *s)
*utfrune(name, CenterDot) = 0;
}
- n = newname(lookup(name));
+ if(exportname(name) || s->pkg == builtinpkg) // old behaviour, tests pass, but is it correct?
+ n = newname(lookup(name));
+ else
+ n = newname(pkglookup(name, s->pkg));
n = nod(ODCLFIELD, n, oldname(s));
n->embedded = 1;
return n;
@@ -957,6 +1061,17 @@ checkarglist(NodeList *all, int input)
t = n;
n = N;
}
+
+ // during import l->n->op is OKEY, but l->n->left->sym == S
+ // means it was a '?', not that it was
+ // a lone type This doesn't matter for the exported
+ // declarations, which are parsed by rules that don't
+ // use checkargs, but can happen for func literals in
+ // the inline bodies.
+ // TODO(rsc) this can go when typefmt case TFIELD in exportmode fmt.c prints _ instead of ?
+ if(importpkg && n->sym == S)
+ n = N;
+
if(n != N && n->sym == S) {
t = n;
n = N;
@@ -1030,9 +1145,12 @@ functype(Node *this, NodeList *in, NodeList *out)
rcvr = nil;
if(this)
rcvr = list1(this);
- t->type = dostruct(rcvr, TFUNC);
- t->type->down = dostruct(out, TFUNC);
- t->type->down->down = dostruct(in, TFUNC);
+ t->type = tofunargs(rcvr);
+ t->type->down = tofunargs(out);
+ t->type->down->down = tofunargs(in);
+
+ if (t->type->broke || t->type->down->broke || t->type->down->down->broke)
+ t->broke = 1;
if(this)
t->thistuple = 1;
@@ -1050,21 +1168,22 @@ methodsym(Sym *nsym, Type *t0, int iface)
char *p;
Type *t;
char *suffix;
+ Pkg *spkg;
+ static Pkg *toppkg;
t = t0;
if(t == T)
goto bad;
s = t->sym;
- if(s == S) {
- if(!isptr[t->etype])
- goto bad;
+ if(s == S && isptr[t->etype]) {
t = t->type;
if(t == T)
goto bad;
s = t->sym;
- if(s == S)
- goto bad;
}
+ spkg = nil;
+ if(s != S)
+ spkg = s->pkg;
// if t0 == *t and t0 has a sym,
// we want to see *t, not t0, in the method name.
@@ -1077,11 +1196,23 @@ methodsym(Sym *nsym, Type *t0, int iface)
if(t0->width < types[tptr]->width)
suffix = "·i";
}
- if(t0->sym == S && isptr[t0->etype])
- p = smprint("(%#hT).%s%s", t0, nsym->name, suffix);
- else
- p = smprint("%#hT.%s%s", t0, nsym->name, suffix);
- s = pkglookup(p, s->pkg);
+ if((spkg == nil || nsym->pkg != spkg) && !exportname(nsym->name)) {
+ if(t0->sym == S && isptr[t0->etype])
+ p = smprint("(%-hT).%s.%s%s", t0, nsym->pkg->prefix, nsym->name, suffix);
+ else
+ p = smprint("%-hT.%s.%s%s", t0, nsym->pkg->prefix, nsym->name, suffix);
+ } else {
+ if(t0->sym == S && isptr[t0->etype])
+ p = smprint("(%-hT).%s%s", t0, nsym->name, suffix);
+ else
+ p = smprint("%-hT.%s%s", t0, nsym->name, suffix);
+ }
+ if(spkg == nil) {
+ if(toppkg == nil)
+ toppkg = mkpkg(strlit("go"));
+ spkg = toppkg;
+ }
+ s = pkglookup(p, spkg);
free(p);
return s;
@@ -1114,11 +1245,16 @@ methodname1(Node *n, Node *t)
}
if(t->sym == S || isblank(n))
return newname(n->sym);
+
if(star)
p = smprint("(%s%S).%S", star, t->sym, n->sym);
else
p = smprint("%S.%S", t->sym, n->sym);
- n = newname(pkglookup(p, t->sym->pkg));
+
+ if(exportname(t->sym->name))
+ n = newname(lookup(p));
+ else
+ n = newname(pkglookup(p, t->sym->pkg));
free(p);
return n;
}
@@ -1133,8 +1269,6 @@ addmethod(Sym *sf, Type *t, int local)
Type *f, *d, *pa;
Node *n;
- pa = nil;
-
// get field sym
if(sf == S)
fatal("no method symbol");
@@ -1147,7 +1281,7 @@ addmethod(Sym *sf, Type *t, int local)
}
pa = pa->type;
- f = methtype(pa);
+ f = methtype(pa, 1);
if(f == T) {
t = pa;
if(t != T) {
@@ -1159,6 +1293,8 @@ addmethod(Sym *sf, Type *t, int local)
t = t->type;
}
}
+ if(t->broke) // rely on typecheck having complained before
+ return;
if(t != T) {
if(t->sym == S) {
yyerror("invalid receiver type %T (%T is an unnamed type)", pa, t);
@@ -1180,8 +1316,14 @@ addmethod(Sym *sf, Type *t, int local)
}
pa = f;
- if(importpkg && !exportname(sf->name))
- sf = pkglookup(sf->name, importpkg);
+ if(pa->etype == TSTRUCT) {
+ for(f=pa->type; f; f=f->down) {
+ if(f->sym == sf) {
+ yyerror("type %T has both field and method named %S", pa, sf);
+ return;
+ }
+ }
+ }
n = nod(ODCLFIELD, newname(sf), N);
n->type = t;
@@ -1204,10 +1346,16 @@ addmethod(Sym *sf, Type *t, int local)
return;
}
+ f = structfield(n);
+
+ // during import unexported method names should be in the type's package
+ if(importpkg && f->sym && !exportname(f->sym->name) && f->sym->pkg != structpkg)
+ fatal("imported method name %+S in wrong package %s\n", f->sym, structpkg->name);
+
if(d == T)
- stotype(list1(n), 0, &pa->method, 0);
+ pa->method = f;
else
- stotype(list1(n), 0, &d->down, 0);
+ d->down = f;
return;
}
@@ -1249,6 +1397,3 @@ funccompile(Node *n, int isclosure)
funcdepth = 0;
dclcontext = PEXTERN;
}
-
-
-
diff --git a/src/cmd/gc/doc.go b/src/cmd/gc/doc.go
index 3fe7fafdd..163d3862c 100644
--- a/src/cmd/gc/doc.go
+++ b/src/cmd/gc/doc.go
@@ -26,7 +26,7 @@ package P to read the files of P's dependencies, only the compiled output
of P.
Usage:
- 6g [flags] file...
+ go tool 6g [flags] file...
The specified files must be Go source files and all part of the same package.
Substitute 6g with 8g or 5g where appropriate.
@@ -35,12 +35,17 @@ Flags:
output file, default file.6 for 6g, etc.
-e
normally the compiler quits after 10 errors; -e prints all errors
+ -p path
+ assume that path is the eventual import path for this code,
+ and diagnose any attempt to import a package that depends on it.
+ -D path
+ treat a relative import as relative to path
-L
show entire file path when printing line numbers in errors
-I dir1 -I dir2
add dir1 and dir2 to the list of paths to check for imported packages
-N
- disable optimization
+ disable optimizations
-S
write assembly language text to standard output
-u
diff --git a/src/cmd/gc/esc.c b/src/cmd/gc/esc.c
new file mode 100644
index 000000000..2614b5f35
--- /dev/null
+++ b/src/cmd/gc/esc.c
@@ -0,0 +1,805 @@
+// 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.
+//
+// Escape analysis.
+//
+// First escfunc, esc and escassign recurse over the ast of each
+// function to dig out flow(dst,src) edges between any
+// pointer-containing nodes and store them in dst->escflowsrc. For
+// variables assigned to a variable in an outer scope or used as a
+// return value, they store a flow(theSink, src) edge to a fake node
+// 'the Sink'. For variables referenced in closures, an edge
+// flow(closure, &var) is recorded and the flow of a closure itself to
+// an outer scope is tracked the same way as other variables.
+//
+// Then escflood walks the graph starting at theSink and tags all
+// variables of it can reach an & node as escaping and all function
+// parameters it can reach as leaking.
+//
+// If a value's address is taken but the address does not escape,
+// then the value can stay on the stack. If the value new(T) does
+// not escape, then new(T) can be rewritten into a stack allocation.
+// The same is true of slice literals.
+//
+// If escape analysis is disabled (-s), this code is not used.
+// Instead, the compiler assumes that any value whose address
+// is taken without being immediately dereferenced
+// needs to be moved to the heap, and new(T) and slice
+// literals are always real allocations.
+
+#include <u.h>
+#include <libc.h>
+#include "go.h"
+
+static void escfunc(Node *func);
+static void esclist(NodeList *l);
+static void esc(Node *n);
+static void escloopdepthlist(NodeList *l);
+static void escloopdepth(Node *n);
+static void escassign(Node *dst, Node *src);
+static void esccall(Node*);
+static void escflows(Node *dst, Node *src);
+static void escflood(Node *dst);
+static void escwalk(int level, Node *dst, Node *src);
+static void esctag(Node *func);
+
+// Fake node that all
+// - return values and output variables
+// - parameters on imported functions not marked 'safe'
+// - assignments to global variables
+// flow to.
+static Node theSink;
+
+static NodeList* dsts; // all dst nodes
+static int loopdepth; // for detecting nested loop scopes
+static int pdepth; // for debug printing in recursions.
+static Strlit* safetag; // gets slapped on safe parameters' field types for export
+static int dstcount, edgecount; // diagnostic
+static NodeList* noesc; // list of possible non-escaping nodes, for printing
+
+void
+escapes(NodeList *all)
+{
+ NodeList *l;
+
+ theSink.op = ONAME;
+ theSink.orig = &theSink;
+ theSink.class = PEXTERN;
+ theSink.sym = lookup(".sink");
+ theSink.escloopdepth = -1;
+
+ safetag = strlit("noescape");
+ noesc = nil;
+
+ // flow-analyze functions
+ for(l=all; l; l=l->next)
+ if(l->n->op == ODCLFUNC || l->n->op == OCLOSURE)
+ escfunc(l->n);
+
+ // print("escapes: %d dsts, %d edges\n", dstcount, edgecount);
+
+ // visit the updstream of each dst, mark address nodes with
+ // addrescapes, mark parameters unsafe
+ for(l = dsts; l; l=l->next)
+ escflood(l->n);
+
+ // for all top level functions, tag the typenodes corresponding to the param nodes
+ for(l=all; l; l=l->next)
+ if(l->n->op == ODCLFUNC)
+ esctag(l->n);
+
+ if(debug['m']) {
+ for(l=noesc; l; l=l->next)
+ if(l->n->esc == EscNone)
+ warnl(l->n->lineno, "%S %hN does not escape",
+ (l->n->curfn && l->n->curfn->nname) ? l->n->curfn->nname->sym : S,
+ l->n);
+ }
+}
+
+
+static void
+escfunc(Node *func)
+{
+ Node *savefn, *n;
+ NodeList *ll;
+ int saveld;
+
+ saveld = loopdepth;
+ loopdepth = 1;
+ savefn = curfn;
+ curfn = func;
+
+ for(ll=curfn->dcl; ll; ll=ll->next) {
+ if(ll->n->op != ONAME)
+ continue;
+ switch (ll->n->class) {
+ case PPARAMOUT:
+ // output parameters flow to the sink
+ escflows(&theSink, ll->n);
+ ll->n->escloopdepth = loopdepth;
+ break;
+ case PPARAM:
+ if(ll->n->type && !haspointers(ll->n->type))
+ break;
+ ll->n->esc = EscNone; // prime for escflood later
+ noesc = list(noesc, ll->n);
+ ll->n->escloopdepth = loopdepth;
+ break;
+ }
+ }
+
+ // walk will take the address of cvar->closure later and assign it to cvar.
+ // handle that here by linking a fake oaddr node directly to the closure.
+ for(ll=curfn->cvars; ll; ll=ll->next) {
+ if(ll->n->op == OXXX) // see dcl.c:398
+ continue;
+
+ n = nod(OADDR, ll->n->closure, N);
+ n->lineno = ll->n->lineno;
+ typecheck(&n, Erv);
+ escassign(curfn, n);
+ }
+
+ escloopdepthlist(curfn->nbody);
+ esclist(curfn->nbody);
+ curfn = savefn;
+ loopdepth = saveld;
+}
+
+// Mark labels that have no backjumps to them as not increasing loopdepth.
+// Walk hasn't generated (goto|label)->left->sym->label yet, so we'll cheat
+// and set it to one of the following two. Then in esc we'll clear it again.
+static Label looping;
+static Label nonlooping;
+
+static void
+escloopdepthlist(NodeList *l)
+{
+ for(; l; l=l->next)
+ escloopdepth(l->n);
+}
+
+static void
+escloopdepth(Node *n)
+{
+ if(n == N)
+ return;
+
+ escloopdepthlist(n->ninit);
+
+ switch(n->op) {
+ case OLABEL:
+ if(!n->left || !n->left->sym)
+ fatal("esc:label without label: %+N", n);
+ // Walk will complain about this label being already defined, but that's not until
+ // after escape analysis. in the future, maybe pull label & goto analysis out of walk and put before esc
+ // if(n->left->sym->label != nil)
+ // fatal("escape analysis messed up analyzing label: %+N", n);
+ n->left->sym->label = &nonlooping;
+ break;
+ case OGOTO:
+ if(!n->left || !n->left->sym)
+ fatal("esc:goto without label: %+N", n);
+ // If we come past one that's uninitialized, this must be a (harmless) forward jump
+ // but if it's set to nonlooping the label must have preceded this goto.
+ if(n->left->sym->label == &nonlooping)
+ n->left->sym->label = &looping;
+ break;
+ }
+
+ escloopdepth(n->left);
+ escloopdepth(n->right);
+ escloopdepthlist(n->list);
+ escloopdepth(n->ntest);
+ escloopdepth(n->nincr);
+ escloopdepthlist(n->nbody);
+ escloopdepthlist(n->nelse);
+ escloopdepthlist(n->rlist);
+
+}
+
+static void
+esclist(NodeList *l)
+{
+ for(; l; l=l->next)
+ esc(l->n);
+}
+
+static void
+esc(Node *n)
+{
+ int lno;
+ NodeList *ll, *lr;
+
+ if(n == N)
+ return;
+
+ lno = setlineno(n);
+
+ if(n->op == OFOR || n->op == ORANGE)
+ loopdepth++;
+
+ esc(n->left);
+ esc(n->right);
+ esc(n->ntest);
+ esc(n->nincr);
+ esclist(n->ninit);
+ esclist(n->nbody);
+ esclist(n->nelse);
+ esclist(n->list);
+ esclist(n->rlist);
+
+ if(n->op == OFOR || n->op == ORANGE)
+ loopdepth--;
+
+ if(debug['m'] > 1)
+ print("%L:[%d] %S esc: %N\n", lineno, loopdepth,
+ (curfn && curfn->nname) ? curfn->nname->sym : S, n);
+
+ switch(n->op) {
+ case ODCL:
+ // Record loop depth at declaration.
+ if(n->left)
+ n->left->escloopdepth = loopdepth;
+ break;
+
+ case OLABEL:
+ if(n->left->sym->label == &nonlooping) {
+ if(debug['m'] > 1)
+ print("%L:%N non-looping label\n", lineno, n);
+ } else if(n->left->sym->label == &looping) {
+ if(debug['m'] > 1)
+ print("%L: %N looping label\n", lineno, n);
+ loopdepth++;
+ }
+ // See case OLABEL in escloopdepth above
+ // else if(n->left->sym->label == nil)
+ // fatal("escape anaylysis missed or messed up a label: %+N", n);
+
+ n->left->sym->label = nil;
+
+ case ORANGE:
+ // Everything but fixed array is a dereference.
+ if(isfixedarray(n->type) && n->list->next)
+ escassign(n->list->next->n, n->right);
+ break;
+
+ case OSWITCH:
+ if(n->ntest && n->ntest->op == OTYPESW) {
+ for(ll=n->list; ll; ll=ll->next) { // cases
+ // ntest->right is the argument of the .(type),
+ // ll->n->nname is the variable per case
+ escassign(ll->n->nname, n->ntest->right);
+ }
+ }
+ break;
+
+ case OAS:
+ case OASOP:
+ escassign(n->left, n->right);
+ break;
+
+ case OAS2: // x,y = a,b
+ if(count(n->list) == count(n->rlist))
+ for(ll=n->list, lr=n->rlist; ll; ll=ll->next, lr=lr->next)
+ escassign(ll->n, lr->n);
+ break;
+
+ case OAS2RECV: // v, ok = <-ch
+ case OAS2MAPR: // v, ok = m[k]
+ case OAS2DOTTYPE: // v, ok = x.(type)
+ escassign(n->list->n, n->rlist->n);
+ break;
+
+ case OSEND: // ch <- x
+ escassign(&theSink, n->right);
+ break;
+
+ case ODEFER:
+ if(loopdepth == 1) // top level
+ break;
+ // arguments leak out of scope
+ // TODO: leak to a dummy node instead
+ // fallthrough
+ case OPROC:
+ // go f(x) - f and x escape
+ escassign(&theSink, n->left->left);
+ escassign(&theSink, n->left->right); // ODDDARG for call
+ for(ll=n->left->list; ll; ll=ll->next)
+ escassign(&theSink, ll->n);
+ break;
+
+ case ORETURN:
+ for(ll=n->list; ll; ll=ll->next)
+ escassign(&theSink, ll->n);
+ break;
+
+ case OPANIC:
+ // Argument could leak through recover.
+ escassign(&theSink, n->left);
+ break;
+
+ case OAPPEND:
+ if(!n->isddd)
+ for(ll=n->list->next; ll; ll=ll->next)
+ escassign(&theSink, ll->n); // lose track of assign to dereference
+ break;
+
+ case OCALLMETH:
+ case OCALLFUNC:
+ case OCALLINTER:
+ esccall(n);
+ break;
+
+ case OCONV:
+ case OCONVNOP:
+ case OCONVIFACE:
+ escassign(n, n->left);
+ break;
+
+ case OARRAYLIT:
+ if(isslice(n->type)) {
+ n->esc = EscNone; // until proven otherwise
+ noesc = list(noesc, n);
+ n->escloopdepth = loopdepth;
+ // Values make it to memory, lose track.
+ for(ll=n->list; ll; ll=ll->next)
+ escassign(&theSink, ll->n->right);
+ } else {
+ // Link values to array.
+ for(ll=n->list; ll; ll=ll->next)
+ escassign(n, ll->n->right);
+ }
+ break;
+
+ case OSTRUCTLIT:
+ // Link values to struct.
+ for(ll=n->list; ll; ll=ll->next)
+ escassign(n, ll->n->right);
+ break;
+
+ case OPTRLIT:
+ n->esc = EscNone; // until proven otherwise
+ noesc = list(noesc, n);
+ n->escloopdepth = loopdepth;
+ // Contents make it to memory, lose track.
+ escassign(&theSink, n->left);
+ break;
+
+ case OMAPLIT:
+ n->esc = EscNone; // until proven otherwise
+ noesc = list(noesc, n);
+ n->escloopdepth = loopdepth;
+ // Keys and values make it to memory, lose track.
+ for(ll=n->list; ll; ll=ll->next) {
+ escassign(&theSink, ll->n->left);
+ escassign(&theSink, ll->n->right);
+ }
+ break;
+
+ case OADDR:
+ case OCLOSURE:
+ case OMAKECHAN:
+ case OMAKEMAP:
+ case OMAKESLICE:
+ case ONEW:
+ n->escloopdepth = loopdepth;
+ n->esc = EscNone; // until proven otherwise
+ noesc = list(noesc, n);
+ break;
+ }
+
+ lineno = lno;
+}
+
+// Assert that expr somehow gets assigned to dst, if non nil. for
+// dst==nil, any name node expr still must be marked as being
+// evaluated in curfn. For expr==nil, dst must still be examined for
+// evaluations inside it (e.g *f(x) = y)
+static void
+escassign(Node *dst, Node *src)
+{
+ int lno;
+
+ if(isblank(dst) || dst == N || src == N || src->op == ONONAME || src->op == OXXX)
+ return;
+
+ if(debug['m'] > 1)
+ print("%L:[%d] %S escassign: %hN = %hN\n", lineno, loopdepth,
+ (curfn && curfn->nname) ? curfn->nname->sym : S, dst, src);
+
+ setlineno(dst);
+
+ // Analyze lhs of assignment.
+ // Replace dst with theSink if we can't track it.
+ switch(dst->op) {
+ default:
+ dump("dst", dst);
+ fatal("escassign: unexpected dst");
+
+ case OARRAYLIT:
+ case OCLOSURE:
+ case OCONV:
+ case OCONVIFACE:
+ case OCONVNOP:
+ case OMAPLIT:
+ case OSTRUCTLIT:
+ break;
+
+ case ONAME:
+ if(dst->class == PEXTERN)
+ dst = &theSink;
+ break;
+ case ODOT: // treat "dst.x = src" as "dst = src"
+ escassign(dst->left, src);
+ return;
+ case OINDEX:
+ if(isfixedarray(dst->left->type)) {
+ escassign(dst->left, src);
+ return;
+ }
+ dst = &theSink; // lose track of dereference
+ break;
+ case OIND:
+ case ODOTPTR:
+ dst = &theSink; // lose track of dereference
+ break;
+ case OINDEXMAP:
+ // lose track of key and value
+ escassign(&theSink, dst->right);
+ dst = &theSink;
+ break;
+ }
+
+ lno = setlineno(src);
+ pdepth++;
+
+ switch(src->op) {
+ case OADDR: // dst = &x
+ case OIND: // dst = *x
+ case ODOTPTR: // dst = (*x).f
+ case ONAME:
+ case OPARAM:
+ case ODDDARG:
+ case OPTRLIT:
+ case OARRAYLIT:
+ case OMAPLIT:
+ case OSTRUCTLIT:
+ // loopdepth was set in the defining statement or function header
+ escflows(dst, src);
+ break;
+
+ case ODOT:
+ // A non-pointer escaping from a struct does not concern us.
+ if(src->type && !haspointers(src->type))
+ break;
+ // fallthrough
+ case OCONV:
+ case OCONVIFACE:
+ case OCONVNOP:
+ case ODOTMETH: // treat recv.meth as a value with recv in it, only happens in ODEFER and OPROC
+ // iface.method already leaks iface in esccall, no need to put in extra ODOTINTER edge here
+ case ODOTTYPE:
+ case ODOTTYPE2:
+ case OSLICE:
+ case OSLICEARR:
+ // Conversions, field access, slice all preserve the input value.
+ escassign(dst, src->left);
+ break;
+
+ case OAPPEND:
+ // Append returns first argument.
+ escassign(dst, src->list->n);
+ break;
+
+ case OINDEX:
+ // Index of array preserves input value.
+ if(isfixedarray(src->left->type))
+ escassign(dst, src->left);
+ break;
+
+ case OMAKECHAN:
+ case OMAKEMAP:
+ case OMAKESLICE:
+ case ONEW:
+ escflows(dst, src);
+ break;
+
+ case OCLOSURE:
+ escflows(dst, src);
+ escfunc(src);
+ break;
+
+ case OADD:
+ case OSUB:
+ case OOR:
+ case OXOR:
+ case OMUL:
+ case ODIV:
+ case OMOD:
+ case OLSH:
+ case ORSH:
+ case OAND:
+ case OANDNOT:
+ case OPLUS:
+ case OMINUS:
+ case OCOM:
+ // Might be pointer arithmetic, in which case
+ // the operands flow into the result.
+ // TODO(rsc): Decide what the story is here. This is unsettling.
+ escassign(dst, src->left);
+ escassign(dst, src->right);
+ break;
+
+ }
+
+ pdepth--;
+ lineno = lno;
+}
+
+
+// This is a bit messier than fortunate, pulled out of escassign's big
+// switch for clarity. We either have the paramnodes, which may be
+// connected to other things throug flows or we have the parameter type
+// nodes, which may be marked 'n(ofloworescape)'. Navigating the ast is slightly
+// different for methods vs plain functions and for imported vs
+// this-package
+static void
+esccall(Node *n)
+{
+ NodeList *ll, *lr;
+ Node *a, *fn, *src;
+ Type *t, *fntype;
+
+ fn = N;
+ switch(n->op) {
+ default:
+ fatal("esccall");
+
+ case OCALLFUNC:
+ fn = n->left;
+ fntype = fn->type;
+ break;
+
+ case OCALLMETH:
+ fn = n->left->right->sym->def;
+ if(fn)
+ fntype = fn->type;
+ else
+ fntype = n->left->type;
+ break;
+
+ case OCALLINTER:
+ fntype = n->left->type;
+ break;
+ }
+
+ ll = n->list;
+ if(n->list != nil && n->list->next == nil) {
+ a = n->list->n;
+ if(a->type->etype == TSTRUCT && a->type->funarg) {
+ // f(g()).
+ // Since f's arguments are g's results and
+ // all function results escape, we're done.
+ ll = nil;
+ }
+ }
+
+ if(fn && fn->op == ONAME && fn->class == PFUNC && fn->defn && fn->defn->nbody && fn->ntype) {
+ // Local function. Incorporate into flow graph.
+
+ // Receiver.
+ if(n->op != OCALLFUNC)
+ escassign(fn->ntype->left->left, n->left->left);
+
+ for(lr=fn->ntype->list; ll && lr; ll=ll->next, lr=lr->next) {
+ src = ll->n;
+ if(lr->n->isddd && !n->isddd) {
+ // Introduce ODDDARG node to represent ... allocation.
+ src = nod(ODDDARG, N, N);
+ src->escloopdepth = loopdepth;
+ src->lineno = n->lineno;
+ src->esc = EscNone; // until we find otherwise
+ noesc = list(noesc, src);
+ n->right = src;
+ }
+ if(lr->n->left != N)
+ escassign(lr->n->left, src);
+ if(src != ll->n)
+ break;
+ }
+ // "..." arguments are untracked
+ for(; ll; ll=ll->next)
+ escassign(&theSink, ll->n);
+ return;
+ }
+
+ // Imported function. Use the escape tags.
+ if(n->op != OCALLFUNC) {
+ t = getthisx(fntype)->type;
+ if(!t->note || strcmp(t->note->s, safetag->s) != 0)
+ escassign(&theSink, n->left->left);
+ }
+ for(t=getinargx(fntype)->type; ll; ll=ll->next) {
+ src = ll->n;
+ if(t->isddd && !n->isddd) {
+ // Introduce ODDDARG node to represent ... allocation.
+ src = nod(ODDDARG, N, N);
+ src->escloopdepth = loopdepth;
+ src->lineno = n->lineno;
+ src->esc = EscNone; // until we find otherwise
+ noesc = list(noesc, src);
+ n->right = src;
+ }
+ if(!t->note || strcmp(t->note->s, safetag->s) != 0)
+ escassign(&theSink, src);
+ if(src != ll->n)
+ break;
+ t = t->down;
+ }
+ // "..." arguments are untracked
+ for(; ll; ll=ll->next)
+ escassign(&theSink, ll->n);
+}
+
+// Store the link src->dst in dst, throwing out some quick wins.
+static void
+escflows(Node *dst, Node *src)
+{
+ if(dst == nil || src == nil || dst == src)
+ return;
+
+ // Don't bother building a graph for scalars.
+ if(src->type && !haspointers(src->type))
+ return;
+
+ if(debug['m']>2)
+ print("%L::flows:: %hN <- %hN\n", lineno, dst, src);
+
+ if(dst->escflowsrc == nil) {
+ dsts = list(dsts, dst);
+ dstcount++;
+ }
+ edgecount++;
+
+ dst->escflowsrc = list(dst->escflowsrc, src);
+}
+
+// Whenever we hit a reference node, the level goes up by one, and whenever
+// we hit an OADDR, the level goes down by one. as long as we're on a level > 0
+// finding an OADDR just means we're following the upstream of a dereference,
+// so this address doesn't leak (yet).
+// If level == 0, it means the /value/ of this node can reach the root of this flood.
+// so if this node is an OADDR, it's argument should be marked as escaping iff
+// it's currfn/loopdepth are different from the flood's root.
+// Once an object has been moved to the heap, all of it's upstream should be considered
+// escaping to the global scope.
+static void
+escflood(Node *dst)
+{
+ NodeList *l;
+
+ switch(dst->op) {
+ case ONAME:
+ case OCLOSURE:
+ break;
+ default:
+ return;
+ }
+
+ if(debug['m']>1)
+ print("\nescflood:%d: dst %hN scope:%S[%d]\n", walkgen, dst,
+ (dst->curfn && dst->curfn->nname) ? dst->curfn->nname->sym : S,
+ dst->escloopdepth);
+
+ for(l = dst->escflowsrc; l; l=l->next) {
+ walkgen++;
+ escwalk(0, dst, l->n);
+ }
+}
+
+static void
+escwalk(int level, Node *dst, Node *src)
+{
+ NodeList *ll;
+ int leaks;
+
+ if(src->walkgen == walkgen)
+ return;
+ src->walkgen = walkgen;
+
+ if(debug['m']>1)
+ print("escwalk: level:%d depth:%d %.*s %hN scope:%S[%d]\n",
+ level, pdepth, pdepth, "\t\t\t\t\t\t\t\t\t\t", src,
+ (src->curfn && src->curfn->nname) ? src->curfn->nname->sym : S, src->escloopdepth);
+
+ pdepth++;
+
+ leaks = (level <= 0) && (dst->escloopdepth < src->escloopdepth);
+
+ switch(src->op) {
+ case ONAME:
+ if(src->class == PPARAM && leaks && src->esc == EscNone) {
+ src->esc = EscScope;
+ if(debug['m'])
+ warnl(src->lineno, "leaking param: %hN", src);
+ }
+ break;
+
+ case OPTRLIT:
+ case OADDR:
+ if(leaks) {
+ src->esc = EscHeap;
+ addrescapes(src->left);
+ if(debug['m'])
+ warnl(src->lineno, "%hN escapes to heap", src);
+ }
+ escwalk(level-1, dst, src->left);
+ break;
+
+ case OARRAYLIT:
+ if(isfixedarray(src->type))
+ break;
+ // fall through
+ case ODDDARG:
+ case OMAKECHAN:
+ case OMAKEMAP:
+ case OMAKESLICE:
+ case OMAPLIT:
+ case ONEW:
+ case OCLOSURE:
+ if(leaks) {
+ src->esc = EscHeap;
+ if(debug['m'])
+ warnl(src->lineno, "%hN escapes to heap", src);
+ }
+ break;
+
+ case OINDEX:
+ if(isfixedarray(src->type))
+ break;
+ // fall through
+ case OSLICE:
+ case ODOTPTR:
+ case OINDEXMAP:
+ case OIND:
+ escwalk(level+1, dst, src->left);
+ }
+
+ for(ll=src->escflowsrc; ll; ll=ll->next)
+ escwalk(level, dst, ll->n);
+
+ pdepth--;
+}
+
+static void
+esctag(Node *func)
+{
+ Node *savefn;
+ NodeList *ll;
+
+ // External functions must be assumed unsafe.
+ if(func->nbody == nil)
+ return;
+
+ savefn = curfn;
+ curfn = func;
+
+ for(ll=curfn->dcl; ll; ll=ll->next) {
+ if(ll->n->op != ONAME || ll->n->class != PPARAM)
+ continue;
+
+ switch (ll->n->esc) {
+ case EscNone: // not touched by escflood
+ if(haspointers(ll->n->type)) // don't bother tagging for scalars
+ ll->n->paramfld->note = safetag;
+ case EscHeap: // touched by escflood, moved to heap
+ case EscScope: // touched by escflood, value leaves scope
+ break;
+ }
+ }
+
+ curfn = savefn;
+}
diff --git a/src/cmd/gc/export.c b/src/cmd/gc/export.c
index 014f0c5f0..bbed8ae36 100644
--- a/src/cmd/gc/export.c
+++ b/src/cmd/gc/export.c
@@ -2,14 +2,14 @@
// 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"
#include "y.tab.h"
-static void dumpsym(Sym*);
-static void dumpexporttype(Sym*);
-static void dumpexportvar(Sym*);
-static void dumpexportconst(Sym*);
+static void dumpexporttype(Type *t);
+// Mark n's symbol as exported
void
exportsym(Node *n)
{
@@ -25,6 +25,7 @@ exportsym(Node *n)
exportlist = list(exportlist, n);
}
+// Mark n's symbol as package-local
static void
packagesym(Node *n)
{
@@ -77,7 +78,7 @@ dumppkg(Pkg *p)
{
char *suffix;
- if(p == nil || p == localpkg || p->exported)
+ if(p == nil || p == localpkg || p->exported || p == builtinpkg)
return;
p->exported = 1;
suffix = "";
@@ -86,25 +87,84 @@ dumppkg(Pkg *p)
Bprint(bout, "\timport %s \"%Z\"%s\n", p->name, p->path, suffix);
}
+// Look for anything we need for the inline body
+static void reexportdep(Node *n);
static void
-dumpprereq(Type *t)
+reexportdeplist(NodeList *ll)
{
- if(t == T)
- return;
+ for(; ll ;ll=ll->next)
+ reexportdep(ll->n);
+}
+
+static void
+reexportdep(Node *n)
+{
+ Type *t;
- if(t->printed || t == types[t->etype])
+ if(!n)
return;
- t->printed = 1;
- if(t->sym != S) {
- dumppkg(t->sym->pkg);
- if(t->etype != TFIELD)
- dumpsym(t->sym);
+// print("reexportdep %+hN\n", n);
+ switch(n->op) {
+ case ONAME:
+ switch(n->class&~PHEAP) {
+ case PFUNC:
+ // methods will be printed along with their type
+ if(!n->type || n->type->thistuple > 0)
+ break;
+ // fallthrough
+ case PEXTERN:
+ if (n->sym && n->sym->pkg != localpkg && n->sym->pkg != builtinpkg)
+ exportlist = list(exportlist, n);
+ }
+ break;
+
+
+ case OLITERAL:
+ t = n->type;
+ if(t != types[n->type->etype] && t != idealbool && t != idealstring) {
+ if(isptr[t->etype])
+ t = t->type;
+ if (t && t->sym && t->sym->def && t->sym->pkg != localpkg && t->sym->pkg != builtinpkg) {
+// print("reexport literal type %+hN\n", t->sym->def);
+ exportlist = list(exportlist, t->sym->def);
+ }
+ }
+ // fallthrough
+ case OTYPE:
+ if (n->sym && n->sym->pkg != localpkg && n->sym->pkg != builtinpkg)
+ exportlist = list(exportlist, n);
+ break;
+
+ // for operations that need a type when rendered, put the type on the export list.
+ case OCONV:
+ case OCONVIFACE:
+ case OCONVNOP:
+ case ODOTTYPE:
+ case OSTRUCTLIT:
+ case OPTRLIT:
+ t = n->type;
+ if(!t->sym && t->type)
+ t = t->type;
+ if (t && t->sym && t->sym->def && t->sym->pkg != localpkg && t->sym->pkg != builtinpkg) {
+// print("reexport convnop %+hN\n", t->sym->def);
+ exportlist = list(exportlist, t->sym->def);
+ }
+ break;
}
- dumpprereq(t->type);
- dumpprereq(t->down);
+
+ reexportdep(n->left);
+ reexportdep(n->right);
+ reexportdeplist(n->list);
+ reexportdeplist(n->rlist);
+ reexportdeplist(n->ninit);
+ reexportdep(n->ntest);
+ reexportdep(n->nincr);
+ reexportdeplist(n->nbody);
+ reexportdeplist(n->nelse);
}
+
static void
dumpexportconst(Sym *s)
{
@@ -117,37 +177,12 @@ dumpexportconst(Sym *s)
fatal("dumpexportconst: oconst nil: %S", s);
t = n->type; // may or may not be specified
- if(t != T)
- dumpprereq(t);
+ dumpexporttype(t);
- Bprint(bout, "\t");
- Bprint(bout, "const %#S", s);
if(t != T && !isideal(t))
- Bprint(bout, " %#T", t);
- Bprint(bout, " = ");
-
- switch(n->val.ctype) {
- default:
- fatal("dumpexportconst: unknown ctype: %S %d", s, n->val.ctype);
- case CTINT:
- Bprint(bout, "%B\n", n->val.u.xval);
- break;
- case CTBOOL:
- if(n->val.u.bval)
- Bprint(bout, "true\n");
- else
- Bprint(bout, "false\n");
- break;
- case CTFLT:
- Bprint(bout, "%F\n", n->val.u.fval);
- break;
- case CTCPLX:
- Bprint(bout, "(%F+%F)\n", &n->val.u.cval->real, &n->val.u.cval->imag);
- break;
- case CTSTR:
- Bprint(bout, "\"%Z\"\n", n->val.u.sval);
- break;
- }
+ Bprint(bout, "\tconst %#S %#T = %#V\n", s, t, &n->val);
+ else
+ Bprint(bout, "\tconst %#S = %#V\n", s, &n->val);
}
static void
@@ -157,38 +192,27 @@ dumpexportvar(Sym *s)
Type *t;
n = s->def;
- typecheck(&n, Erv);
+ typecheck(&n, Erv|Ecall);
if(n == N || n->type == T) {
yyerror("variable exported but not defined: %S", s);
return;
}
t = n->type;
- dumpprereq(t);
-
- Bprint(bout, "\t");
- if(t->etype == TFUNC && n->class == PFUNC)
- Bprint(bout, "func %#S %#hhT", s, t);
- else
- Bprint(bout, "var %#S %#T", s, t);
- Bprint(bout, "\n");
-}
-
-static void
-dumpexporttype(Sym *s)
-{
- Type *t;
-
- t = s->def->type;
- dumpprereq(t);
- Bprint(bout, "\t");
- switch (t->etype) {
- case TFORW:
- yyerror("export of incomplete type %T", t);
- return;
- }
- if(Bprint(bout, "type %#T %l#T\n", t, t) < 0)
- fatal("Bprint failed for %T", t);
+ dumpexporttype(t);
+
+ if(t->etype == TFUNC && n->class == PFUNC) {
+ if (n->inl) {
+ // when lazily typechecking inlined bodies, some re-exported ones may not have been typechecked yet.
+ // currently that can leave unresolved ONONAMEs in import-dot-ed packages in the wrong package
+ if(debug['l'] < 2)
+ typecheckinl(n);
+ Bprint(bout, "\tfunc %#S%#hT { %#H }\n", s, t, n->inl);
+ reexportdeplist(n->inl);
+ } else
+ Bprint(bout, "\tfunc %#S%#hT\n", s, t);
+ } else
+ Bprint(bout, "\tvar %#S %#T\n", s, t);
}
static int
@@ -202,12 +226,57 @@ methcmp(const void *va, const void *vb)
}
static void
-dumpsym(Sym *s)
+dumpexporttype(Type *t)
{
- Type *f, *t;
+ Type *f;
Type **m;
int i, n;
+ if(t == T)
+ return;
+ if(t->printed || t == types[t->etype] || t == bytetype || t == runetype || t == errortype)
+ return;
+ t->printed = 1;
+
+ if(t->sym != S && t->etype != TFIELD)
+ dumppkg(t->sym->pkg);
+
+ dumpexporttype(t->type);
+ dumpexporttype(t->down);
+
+ if (t->sym == S || t->etype == TFIELD)
+ return;
+
+ n = 0;
+ for(f=t->method; f!=T; f=f->down) {
+ dumpexporttype(f);
+ n++;
+ }
+
+ m = mal(n*sizeof m[0]);
+ i = 0;
+ for(f=t->method; f!=T; f=f->down)
+ m[i++] = f;
+ qsort(m, n, sizeof m[0], methcmp);
+
+ Bprint(bout, "\ttype %#S %#lT\n", t->sym, t);
+ for(i=0; i<n; i++) {
+ f = m[i];
+ if (f->type->nname && f->type->nname->inl) { // nname was set by caninl
+ // when lazily typechecking inlined bodies, some re-exported ones may not have been typechecked yet.
+ // currently that can leave unresolved ONONAMEs in import-dot-ed packages in the wrong package
+ if(debug['l'] < 2)
+ typecheckinl(f->type->nname);
+ Bprint(bout, "\tfunc (%#T) %#hhS%#hT { %#H }\n", getthisx(f->type)->type, f->sym, f->type, f->type->nname->inl);
+ reexportdeplist(f->type->nname->inl);
+ } else
+ Bprint(bout, "\tfunc (%#T) %#hhS%#hT\n", getthisx(f->type)->type, f->sym, f->type);
+ }
+}
+
+static void
+dumpsym(Sym *s)
+{
if(s->flags & SymExported)
return;
s->flags |= SymExported;
@@ -216,56 +285,31 @@ dumpsym(Sym *s)
yyerror("unknown export symbol: %S", s);
return;
}
-
+// print("dumpsym %O %+S\n", s->def->op, s);
dumppkg(s->pkg);
switch(s->def->op) {
default:
yyerror("unexpected export symbol: %O %S", s->def->op, s);
break;
+
case OLITERAL:
dumpexportconst(s);
break;
+
case OTYPE:
- t = s->def->type;
- n = 0;
- for(f=t->method; f!=T; f=f->down) {
- dumpprereq(f);
- n++;
- }
- m = mal(n*sizeof m[0]);
- i = 0;
- for(f=t->method; f!=T; f=f->down)
- m[i++] = f;
- qsort(m, n, sizeof m[0], methcmp);
-
- dumpexporttype(s);
- for(i=0; i<n; i++) {
- f = m[i];
- Bprint(bout, "\tfunc (%#T) %hS %#hhT\n",
- f->type->type->type, f->sym, f->type);
- }
+ if(s->def->type->etype == TFORW)
+ yyerror("export of incomplete type %S", s);
+ else
+ dumpexporttype(s->def->type);
break;
+
case ONAME:
dumpexportvar(s);
break;
}
}
-static void
-dumptype(Type *t)
-{
- // no need to re-dump type if already exported
- if(t->printed)
- return;
-
- // no need to dump type if it's not ours (was imported)
- if(t->sym != S && t->sym->def == typenod(t) && !t->local)
- return;
-
- Bprint(bout, "type %#T %l#T\n", t, t);
-}
-
void
dumpexport(void)
{
@@ -275,10 +319,7 @@ dumpexport(void)
lno = lineno;
- packagequotes = 1;
- Bprint(bout, "\n$$ // exports\n");
-
- Bprint(bout, " package %s", localpkg->name);
+ Bprint(bout, "\n$$ // exports\n package %s", localpkg->name);
if(safemode)
Bprint(bout, " safe");
Bprint(bout, "\n");
@@ -293,15 +334,7 @@ dumpexport(void)
dumpsym(l->n->sym);
}
- Bprint(bout, "\n$$ // local types\n");
-
- for(l=typelist; l; l=l->next) {
- lineno = l->n->lineno;
- dumptype(l->n->type);
- }
-
- Bprint(bout, "\n$$\n");
- packagequotes = 0;
+ Bprint(bout, "\n$$ // local types\n\n$$\n"); // 6l expects this. (see ld/go.c)
lineno = lno;
}
@@ -344,16 +377,29 @@ pkgtype(Sym *s)
s->def = typenod(t);
}
if(s->def->type == T)
- yyerror("pkgtype %lS", s);
+ yyerror("pkgtype %S", s);
return s->def->type;
}
-static int
-mypackage(Sym *s)
+void
+importimport(Sym *s, Strlit *z)
{
- // we import all definitions for runtime.
- // lowercase ones can only be used by the compiler.
- return s->pkg == localpkg || s->pkg == runtimepkg;
+ // Informational: record package name
+ // associated with import path, for use in
+ // human-readable messages.
+ Pkg *p;
+
+ p = mkpkg(z);
+ if(p->name == nil) {
+ p->name = s->name;
+ pkglookup(s->name, nil)->npkg++;
+ } else if(strcmp(p->name, s->name) != 0)
+ yyerror("conflicting names %s and %s for package \"%Z\"", p->name, s->name, p->path);
+
+ if(!incannedimport && myimportpath != nil && strcmp(z->s, myimportpath) == 0) {
+ yyerror("import \"%Z\": package depends on \"%Z\" (import cycle)", importpkg->path, z);
+ errorexit();
+ }
}
void
@@ -361,24 +407,23 @@ importconst(Sym *s, Type *t, Node *n)
{
Node *n1;
- if(!exportname(s->name) && !mypackage(s))
- return;
importsym(s, OLITERAL);
convlit(&n, t);
- if(s->def != N) {
- // TODO: check if already the same.
+
+ if(s->def != N) // TODO: check if already the same.
return;
- }
if(n->op != OLITERAL) {
yyerror("expression must be a constant");
return;
}
+
if(n->sym != S) {
n1 = nod(OXXX, N, N);
*n1 = *n;
n = n1;
}
+ n->orig = newname(s);
n->sym = s;
declare(n, PEXTERN);
@@ -387,23 +432,19 @@ importconst(Sym *s, Type *t, Node *n)
}
void
-importvar(Sym *s, Type *t, int ctxt)
+importvar(Sym *s, Type *t)
{
Node *n;
- if(!exportname(s->name) && !initname(s->name) && !mypackage(s))
- return;
-
importsym(s, ONAME);
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\n\t%T", s, s->def->type, t);
}
n = newname(s);
n->type = t;
- declare(n, ctxt);
+ declare(n, PEXTERN);
if(debug['E'])
print("import var %S %lT\n", s, t);
@@ -412,17 +453,27 @@ importvar(Sym *s, Type *t, int ctxt)
void
importtype(Type *pt, Type *t)
{
- if(pt != T && t != T)
- typedcl2(pt, t);
+ Node *n;
+
+ // override declaration in unsafe.go for Pointer.
+ // there is no way in Go code to define unsafe.Pointer
+ // so we have to supply it.
+ if(incannedimport &&
+ strcmp(importpkg->name, "unsafe") == 0 &&
+ strcmp(pt->nod->sym->name, "Pointer") == 0) {
+ t = types[TUNSAFEPTR];
+ }
+
+ if(pt->etype == TFORW) {
+ n = pt->nod;
+ copytype(pt->nod, t);
+ pt->nod = n; // unzero nod
+ 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);
if(debug['E'])
print("import type %T %lT\n", pt, t);
}
-
-void
-importmethod(Sym *s, Type *t)
-{
- checkwidth(t);
- addmethod(s, t, 0);
-}
-
diff --git a/src/cmd/gc/fmt.c b/src/cmd/gc/fmt.c
new file mode 100644
index 000000000..5672c0010
--- /dev/null
+++ b/src/cmd/gc/fmt.c
@@ -0,0 +1,1639 @@
+// 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 <u.h>
+#include <libc.h>
+#include "go.h"
+#include "opnames.h"
+
+//
+// Format conversions
+// %L int Line numbers
+//
+// %E int etype values (aka 'Kind')
+//
+// %O int Node Opcodes
+// Flags: "%#O": print go syntax. (automatic unless fmtmode == FDbg)
+//
+// %J Node* Node details
+// Flags: "%hJ" supresses things not relevant until walk.
+//
+// %V Val* Constant values
+//
+// %S Sym* Symbols
+// Flags: +,- #: mode (see below)
+// "%hS" unqualified identifier in any mode
+// "%hhS" in export mode: unqualified identifier if exported, qualified if not
+//
+// %T Type* Types
+// Flags: +,- #: mode (see below)
+// 'l' definition instead of name.
+// 'h' omit "func" and receiver in function types
+// 'u' (only in -/Sym mode) print type identifiers wit package name instead of prefix.
+//
+// %N Node* Nodes
+// Flags: +,- #: mode (see below)
+// 'h' (only in +/debug mode) suppress recursion
+// 'l' (only in Error mode) print "foo (type Bar)"
+//
+// %H NodeList* NodeLists
+// Flags: those of %N
+// ',' separate items with ',' instead of ';'
+//
+// %Z Strlit* String literals
+//
+// In mparith1.c:
+// %B Mpint* Big integers
+// %F Mpflt* Big floats
+//
+// %S, %T and %N obey use the following flags to set the format mode:
+enum {
+ FErr, // error mode (default)
+ FDbg, // "%+N" debug mode
+ FExp, // "%#N" export mode
+ FTypeId, // "%-N" turning-types-into-symbols-mode: identical types give identical strings
+};
+static int fmtmode;
+static int fmtpkgpfx; // %uT stickyness
+//
+// E.g. for %S: %+S %#S %-S print an identifier properly qualified for debug/export/internal mode.
+//
+// The mode flags +, - and # are sticky, meaning they persist through
+// recursions of %N, %T and %S, but not the h and l flags. The u flag is
+// sticky only on %T recursions and only used in %-/Sym mode.
+
+//
+// Useful format combinations:
+//
+// %+N %+H multiline recursive debug dump of node/nodelist
+// %+hN %+hH non recursive debug dump
+//
+// %#N %#T export format
+// %#lT type definition instead of name
+// %#hT omit"func" and receiver in function signature
+//
+// %lN "foo (type Bar)" for error messages
+//
+// %-T type identifiers
+// %-hT type identifiers without "func" and arg names in type signatures (methodsym)
+// %-uT type identifiers with package name instead of prefix (typesym, dcommontype, typehash)
+//
+
+
+static int
+setfmode(unsigned long *flags)
+{
+ int fm;
+
+ fm = fmtmode;
+ if(*flags & FmtSign)
+ fmtmode = FDbg;
+ else if(*flags & FmtSharp)
+ fmtmode = FExp;
+ else if(*flags & FmtLeft)
+ fmtmode = FTypeId;
+
+ *flags &= ~(FmtSharp|FmtLeft|FmtSign);
+ return fm;
+}
+
+// Fmt "%L": Linenumbers
+static int
+Lconv(Fmt *fp)
+{
+ struct
+ {
+ Hist* incl; /* start of this include file */
+ int32 idel; /* delta line number to apply to include */
+ Hist* line; /* start of this #line directive */
+ int32 ldel; /* delta line number to apply to #line */
+ } a[HISTSZ];
+ int32 lno, d;
+ int i, n;
+ Hist *h;
+
+ lno = va_arg(fp->args, int32);
+
+ n = 0;
+ for(h=hist; h!=H; h=h->link) {
+ if(h->offset < 0)
+ continue;
+ if(lno < h->line)
+ break;
+ if(h->name) {
+ if(h->offset > 0) {
+ // #line directive
+ if(n > 0 && n < HISTSZ) {
+ a[n-1].line = h;
+ a[n-1].ldel = h->line - h->offset + 1;
+ }
+ } else {
+ // beginning of file
+ if(n < HISTSZ) {
+ a[n].incl = h;
+ a[n].idel = h->line;
+ a[n].line = 0;
+ }
+ n++;
+ }
+ continue;
+ }
+ n--;
+ if(n > 0 && n < HISTSZ) {
+ d = h->line - a[n].incl->line;
+ a[n-1].ldel += d;
+ a[n-1].idel += d;
+ }
+ }
+
+ if(n > HISTSZ)
+ n = HISTSZ;
+
+ for(i=n-1; i>=0; i--) {
+ if(i != n-1) {
+ if(fp->flags & ~(FmtWidth|FmtPrec))
+ break;
+ fmtprint(fp, " ");
+ }
+ if(debug['L'] || (fp->flags&FmtLong))
+ fmtprint(fp, "%s/", pathname);
+ if(a[i].line)
+ fmtprint(fp, "%s:%d[%s:%d]",
+ a[i].line->name, lno-a[i].ldel+1,
+ a[i].incl->name, lno-a[i].idel+1);
+ else
+ fmtprint(fp, "%s:%d",
+ a[i].incl->name, lno-a[i].idel+1);
+ lno = a[i].incl->line - 1; // now print out start of this file
+ }
+ if(n == 0)
+ fmtprint(fp, "<epoch>");
+
+ return 0;
+}
+
+static char*
+goopnames[] =
+{
+ [OADDR] = "&",
+ [OADD] = "+",
+ [OADDSTR] = "+",
+ [OANDAND] = "&&",
+ [OANDNOT] = "&^",
+ [OAND] = "&",
+ [OAPPEND] = "append",
+ [OAS] = "=",
+ [OAS2] = "=",
+ [OBREAK] = "break",
+ [OCALL] = "function call", // not actual syntax
+ [OCAP] = "cap",
+ [OCASE] = "case",
+ [OCLOSE] = "close",
+ [OCOMPLEX] = "complex",
+ [OCOM] = "^",
+ [OCONTINUE] = "continue",
+ [OCOPY] = "copy",
+ [ODEC] = "--",
+ [ODELETE] = "delete",
+ [ODEFER] = "defer",
+ [ODIV] = "/",
+ [OEQ] = "==",
+ [OFALL] = "fallthrough",
+ [OFOR] = "for",
+ [OGE] = ">=",
+ [OGOTO] = "goto",
+ [OGT] = ">",
+ [OIF] = "if",
+ [OIMAG] = "imag",
+ [OINC] = "++",
+ [OIND] = "*",
+ [OLEN] = "len",
+ [OLE] = "<=",
+ [OLSH] = "<<",
+ [OLT] = "<",
+ [OMAKE] = "make",
+ [OMINUS] = "-",
+ [OMOD] = "%",
+ [OMUL] = "*",
+ [ONEW] = "new",
+ [ONE] = "!=",
+ [ONOT] = "!",
+ [OOROR] = "||",
+ [OOR] = "|",
+ [OPANIC] = "panic",
+ [OPLUS] = "+",
+ [OPRINTN] = "println",
+ [OPRINT] = "print",
+ [ORANGE] = "range",
+ [OREAL] = "real",
+ [ORECV] = "<-",
+ [ORETURN] = "return",
+ [ORSH] = ">>",
+ [OSELECT] = "select",
+ [OSEND] = "<-",
+ [OSUB] = "-",
+ [OSWITCH] = "switch",
+ [OXOR] = "^",
+};
+
+// Fmt "%O": Node opcodes
+static int
+Oconv(Fmt *fp)
+{
+ int o;
+
+ o = va_arg(fp->args, int);
+ if((fp->flags & FmtSharp) || fmtmode != FDbg)
+ if(o >= 0 && o < nelem(goopnames) && goopnames[o] != nil)
+ return fmtstrcpy(fp, goopnames[o]);
+
+ if(o >= 0 && o < nelem(opnames) && opnames[o] != nil)
+ return fmtstrcpy(fp, opnames[o]);
+
+ return fmtprint(fp, "O-%d", o);
+}
+
+static const char* classnames[] = {
+ "Pxxx",
+ "PEXTERN",
+ "PAUTO",
+ "PPARAM",
+ "PPARAMOUT",
+ "PPARAMREF",
+ "PFUNC",
+};
+
+// Fmt "%J": Node details.
+static int
+Jconv(Fmt *fp)
+{
+ Node *n;
+ char *s;
+ int c;
+
+ n = va_arg(fp->args, Node*);
+
+ c = fp->flags&FmtShort;
+
+ if(!c && n->ullman != 0)
+ fmtprint(fp, " u(%d)", n->ullman);
+
+ if(!c && n->addable != 0)
+ fmtprint(fp, " a(%d)", n->addable);
+
+ if(!c && n->vargen != 0)
+ fmtprint(fp, " g(%d)", n->vargen);
+
+ if(n->lineno != 0)
+ fmtprint(fp, " l(%d)", n->lineno);
+
+ if(!c && n->xoffset != BADWIDTH)
+ fmtprint(fp, " x(%lld%+d)", n->xoffset, n->stkdelta);
+
+ if(n->class != 0) {
+ s = "";
+ if(n->class & PHEAP) s = ",heap";
+ if((n->class & ~PHEAP) < nelem(classnames))
+ fmtprint(fp, " class(%s%s)", classnames[n->class&~PHEAP], s);
+ else
+ fmtprint(fp, " class(%d?%s)", n->class&~PHEAP, s);
+ }
+
+ if(n->colas != 0)
+ fmtprint(fp, " colas(%d)", n->colas);
+
+ if(n->funcdepth != 0)
+ fmtprint(fp, " f(%d)", n->funcdepth);
+
+ switch(n->esc) {
+ case EscUnknown:
+ break;
+ case EscHeap:
+ fmtprint(fp, " esc(h)");
+ break;
+ case EscScope:
+ fmtprint(fp, " esc(s)");
+ break;
+ case EscNone:
+ fmtprint(fp, " esc(no)");
+ break;
+ case EscNever:
+ if(!c)
+ fmtprint(fp, " esc(N)");
+ break;
+ default:
+ fmtprint(fp, " esc(%d)", n->esc);
+ break;
+ }
+
+ if(n->escloopdepth)
+ fmtprint(fp, " ld(%d)", n->escloopdepth);
+
+ if(!c && n->typecheck != 0)
+ fmtprint(fp, " tc(%d)", n->typecheck);
+
+ if(!c && n->dodata != 0)
+ fmtprint(fp, " dd(%d)", n->dodata);
+
+ if(n->isddd != 0)
+ fmtprint(fp, " isddd(%d)", n->isddd);
+
+ if(n->implicit != 0)
+ fmtprint(fp, " implicit(%d)", n->implicit);
+
+ if(n->embedded != 0)
+ fmtprint(fp, " embedded(%d)", n->embedded);
+
+ if(!c && n->used != 0)
+ fmtprint(fp, " used(%d)", n->used);
+ return 0;
+}
+
+// Fmt "%V": Values
+static int
+Vconv(Fmt *fp)
+{
+ Val *v;
+ vlong x;
+
+ v = va_arg(fp->args, Val*);
+
+ switch(v->ctype) {
+ case CTINT:
+ return fmtprint(fp, "%B", v->u.xval);
+ case CTRUNE:
+ x = mpgetfix(v->u.xval);
+ if(' ' <= x && x < 0x80 && x != '\\' && x != '\'')
+ return fmtprint(fp, "'%c'", (int)x);
+ if(0 <= x && x < (1<<16))
+ return fmtprint(fp, "'\\u%04ux'", (int)x);
+ if(0 <= x && x <= Runemax)
+ return fmtprint(fp, "'\\U%08llux'", x);
+ return fmtprint(fp, "('\\x00' + %B)", v->u.xval);
+ case CTFLT:
+ if((fp->flags & FmtSharp) || fmtmode == FExp)
+ return fmtprint(fp, "%F", v->u.fval);
+ return fmtprint(fp, "%#F", v->u.fval);
+ case CTCPLX:
+ if((fp->flags & FmtSharp) || fmtmode == FExp)
+ return fmtprint(fp, "(%F+%F)", &v->u.cval->real, &v->u.cval->imag);
+ return fmtprint(fp, "(%#F + %#Fi)", &v->u.cval->real, &v->u.cval->imag);
+ case CTSTR:
+ return fmtprint(fp, "\"%Z\"", v->u.sval);
+ case CTBOOL:
+ if( v->u.bval)
+ return fmtstrcpy(fp, "true");
+ return fmtstrcpy(fp, "false");
+ case CTNIL:
+ return fmtstrcpy(fp, "nil");
+ }
+ return fmtprint(fp, "<%d>", v->ctype);
+}
+
+// Fmt "%Z": escaped string literals
+static int
+Zconv(Fmt *fp)
+{
+ Rune r;
+ Strlit *sp;
+ char *s, *se;
+ int n;
+
+ sp = va_arg(fp->args, Strlit*);
+ if(sp == nil)
+ return fmtstrcpy(fp, "<nil>");
+
+ s = sp->s;
+ se = s + sp->len;
+ while(s < se) {
+ n = chartorune(&r, s);
+ s += n;
+ switch(r) {
+ case Runeerror:
+ if(n == 1) {
+ fmtprint(fp, "\\x%02x", (uchar)*(s-1));
+ break;
+ }
+ // fall through
+ default:
+ if(r < ' ') {
+ fmtprint(fp, "\\x%02x", r);
+ break;
+ }
+ fmtrune(fp, r);
+ break;
+ case '\t':
+ fmtstrcpy(fp, "\\t");
+ break;
+ case '\n':
+ fmtstrcpy(fp, "\\n");
+ break;
+ case '\"':
+ case '\\':
+ fmtrune(fp, '\\');
+ fmtrune(fp, r);
+ break;
+ }
+ }
+ return 0;
+}
+
+/*
+s%,%,\n%g
+s%\n+%\n%g
+s%^[ ]*T%%g
+s%,.*%%g
+s%.+% [T&] = "&",%g
+s%^ ........*\]%&~%g
+s%~ %%g
+*/
+
+static char*
+etnames[] =
+{
+ [TINT] = "INT",
+ [TUINT] = "UINT",
+ [TINT8] = "INT8",
+ [TUINT8] = "UINT8",
+ [TINT16] = "INT16",
+ [TUINT16] = "UINT16",
+ [TINT32] = "INT32",
+ [TUINT32] = "UINT32",
+ [TINT64] = "INT64",
+ [TUINT64] = "UINT64",
+ [TUINTPTR] = "UINTPTR",
+ [TFLOAT32] = "FLOAT32",
+ [TFLOAT64] = "FLOAT64",
+ [TCOMPLEX64] = "COMPLEX64",
+ [TCOMPLEX128] = "COMPLEX128",
+ [TBOOL] = "BOOL",
+ [TPTR32] = "PTR32",
+ [TPTR64] = "PTR64",
+ [TFUNC] = "FUNC",
+ [TARRAY] = "ARRAY",
+ [TSTRUCT] = "STRUCT",
+ [TCHAN] = "CHAN",
+ [TMAP] = "MAP",
+ [TINTER] = "INTER",
+ [TFORW] = "FORW",
+ [TFIELD] = "FIELD",
+ [TSTRING] = "STRING",
+ [TANY] = "ANY",
+};
+
+// Fmt "%E": etype
+static int
+Econv(Fmt *fp)
+{
+ int et;
+
+ et = va_arg(fp->args, int);
+ if(et >= 0 && et < nelem(etnames) && etnames[et] != nil)
+ return fmtstrcpy(fp, etnames[et]);
+ return fmtprint(fp, "E-%d", et);
+}
+
+// Fmt "%S": syms
+static int
+symfmt(Fmt *fp, Sym *s)
+{
+ char *p;
+
+ if(s->pkg && !(fp->flags&FmtShort)) {
+ switch(fmtmode) {
+ case FErr: // This is for the user
+ if(s->pkg == localpkg)
+ return fmtstrcpy(fp, s->name);
+ // If the name was used by multiple packages, display the full path,
+ if(s->pkg->name && pkglookup(s->pkg->name, nil)->npkg > 1)
+ return fmtprint(fp, "\"%Z\".%s", s->pkg->path, s->name);
+ return fmtprint(fp, "%s.%s", s->pkg->name, s->name);
+ case FDbg:
+ return fmtprint(fp, "%s.%s", s->pkg->name, s->name);
+ case FTypeId:
+ if(fp->flags&FmtUnsigned)
+ return fmtprint(fp, "%s.%s", s->pkg->name, s->name); // dcommontype, typehash
+ return fmtprint(fp, "%s.%s", s->pkg->prefix, s->name); // (methodsym), typesym, weaksym
+ case FExp:
+ if(s->pkg != builtinpkg)
+ return fmtprint(fp, "@\"%Z\".%s", s->pkg->path, s->name);
+ }
+ }
+
+ if(fp->flags&FmtByte) { // FmtByte (hh) implies FmtShort (h)
+ // skip leading "type." in method name
+ p = utfrrune(s->name, '.');
+ if(p)
+ p++;
+ else
+ p = s->name;
+
+ // exportname needs to see the name without the prefix too.
+ if((fmtmode == FExp && !exportname(p)) || fmtmode == FDbg)
+ return fmtprint(fp, "@\"%Z\".%s", s->pkg->path, p);
+
+ return fmtstrcpy(fp, p);
+ }
+
+ return fmtstrcpy(fp, s->name);
+}
+
+static char*
+basicnames[] =
+{
+ [TINT] = "int",
+ [TUINT] = "uint",
+ [TINT8] = "int8",
+ [TUINT8] = "uint8",
+ [TINT16] = "int16",
+ [TUINT16] = "uint16",
+ [TINT32] = "int32",
+ [TUINT32] = "uint32",
+ [TINT64] = "int64",
+ [TUINT64] = "uint64",
+ [TUINTPTR] = "uintptr",
+ [TFLOAT32] = "float32",
+ [TFLOAT64] = "float64",
+ [TCOMPLEX64] = "complex64",
+ [TCOMPLEX128] = "complex128",
+ [TBOOL] = "bool",
+ [TANY] = "any",
+ [TSTRING] = "string",
+ [TNIL] = "nil",
+ [TIDEAL] = "ideal",
+ [TBLANK] = "blank",
+};
+
+static int
+typefmt(Fmt *fp, Type *t)
+{
+ Type *t1;
+ Sym *s;
+
+ if(t == T)
+ return fmtstrcpy(fp, "<T>");
+
+ if (t == bytetype || t == runetype) {
+ // in %-T mode collapse rune and byte with their originals.
+ if(fmtmode != FTypeId)
+ return fmtprint(fp, "%hS", t->sym);
+ t = types[t->etype];
+ }
+
+ if(t == errortype)
+ return fmtstrcpy(fp, "error");
+
+ // Unless the 'l' flag was specified, if the type has a name, just print that name.
+ if(!(fp->flags&FmtLong) && t->sym && t->etype != TFIELD && t != types[t->etype]) {
+ switch(fmtmode) {
+ case FTypeId:
+ if(fp->flags&FmtShort)
+ return fmtprint(fp, "%hS", t->sym);
+ if(fp->flags&FmtUnsigned)
+ return fmtprint(fp, "%uS", t->sym);
+ // fallthrough
+ case FExp:
+ if(t->sym->pkg == localpkg && t->vargen)
+ return fmtprint(fp, "%S·%d", t->sym, t->vargen);
+ break;
+ }
+ return fmtprint(fp, "%S", t->sym);
+ }
+
+ if(t->etype < nelem(basicnames) && basicnames[t->etype] != nil) {
+ if(fmtmode == FErr && (t == idealbool || t == idealstring))
+ fmtstrcpy(fp, "ideal ");
+ return fmtstrcpy(fp, basicnames[t->etype]);
+ }
+
+ if(fmtmode == FDbg)
+ fmtprint(fp, "%E-", t->etype);
+
+ switch(t->etype) {
+ case TPTR32:
+ case TPTR64:
+ if(fmtmode == FTypeId && (fp->flags&FmtShort))
+ return fmtprint(fp, "*%hT", t->type);
+ return fmtprint(fp, "*%T", t->type);
+
+ case TARRAY:
+ if(t->bound >= 0)
+ return fmtprint(fp, "[%d]%T", (int)t->bound, t->type);
+ if(t->bound == -100)
+ return fmtprint(fp, "[...]%T", t->type);
+ return fmtprint(fp, "[]%T", t->type);
+
+ case TCHAN:
+ switch(t->chan) {
+ case Crecv:
+ return fmtprint(fp, "<-chan %T", t->type);
+ case Csend:
+ return fmtprint(fp, "chan<- %T", t->type);
+ }
+
+ if(t->type != T && t->type->etype == TCHAN && t->type->sym == S && t->type->chan == Crecv)
+ return fmtprint(fp, "chan (%T)", t->type);
+ return fmtprint(fp, "chan %T", t->type);
+
+ case TMAP:
+ return fmtprint(fp, "map[%T]%T", t->down, t->type);
+
+ case TINTER:
+ fmtstrcpy(fp, "interface {");
+ for(t1=t->type; t1!=T; t1=t1->down)
+ if(exportname(t1->sym->name)) {
+ if(t1->down)
+ fmtprint(fp, " %hS%hT;", t1->sym, t1->type);
+ else
+ fmtprint(fp, " %hS%hT ", t1->sym, t1->type);
+ } else {
+ // non-exported method names must be qualified
+ if(t1->down)
+ fmtprint(fp, " %uS%hT;", t1->sym, t1->type);
+ else
+ fmtprint(fp, " %uS%hT ", t1->sym, t1->type);
+ }
+ fmtstrcpy(fp, "}");
+ return 0;
+
+ case TFUNC:
+ if(fp->flags & FmtShort) {
+ fmtprint(fp, "%T", getinargx(t));
+ } else {
+ if(t->thistuple)
+ fmtprint(fp, "method%T func%T", getthisx(t), getinargx(t));
+ else
+ fmtprint(fp, "func%T", getinargx(t));
+ }
+ switch(t->outtuple) {
+ case 0:
+ break;
+ case 1:
+ if(fmtmode != FExp) {
+ fmtprint(fp, " %T", getoutargx(t)->type->type); // struct->field->field's type
+ break;
+ }
+ default:
+ fmtprint(fp, " %T", getoutargx(t));
+ break;
+ }
+ return 0;
+
+ case TSTRUCT:
+ if(t->funarg) {
+ fmtstrcpy(fp, "(");
+ if(fmtmode == FTypeId || fmtmode == FErr) { // no argument names on function signature, and no "noescape" tags
+ for(t1=t->type; t1!=T; t1=t1->down)
+ if(t1->down)
+ fmtprint(fp, "%hT, ", t1);
+ else
+ fmtprint(fp, "%hT", t1);
+ } else {
+ for(t1=t->type; t1!=T; t1=t1->down)
+ if(t1->down)
+ fmtprint(fp, "%T, ", t1);
+ else
+ fmtprint(fp, "%T", t1);
+ }
+ fmtstrcpy(fp, ")");
+ } else {
+ fmtstrcpy(fp, "struct {");
+ for(t1=t->type; t1!=T; t1=t1->down)
+ if(t1->down)
+ fmtprint(fp, " %lT;", t1);
+ else
+ fmtprint(fp, " %lT ", t1);
+ fmtstrcpy(fp, "}");
+ }
+ return 0;
+
+ case TFIELD:
+ if(!(fp->flags&FmtShort)) {
+ s = t->sym;
+ // Take the name from the original, lest we substituted it with .anon%d
+ if (t->nname && (fmtmode == FErr || fmtmode == FExp))
+ s = t->nname->orig->sym;
+
+ if(s != S && !t->embedded) {
+ if(fp->flags&FmtLong)
+ fmtprint(fp, "%hhS ", s); // qualify non-exported names (used on structs, not on funarg)
+ else
+ fmtprint(fp, "%S ", s);
+ } else if(fmtmode == FExp) {
+ // TODO(rsc) this breaks on the eliding of unused arguments in the backend
+ // when this is fixed, the special case in dcl.c checkarglist can go.
+ //if(t->funarg)
+ // fmtstrcpy(fp, "_ ");
+ //else
+ fmtstrcpy(fp, "? ");
+ }
+ }
+
+ if(t->isddd)
+ fmtprint(fp, "...%T", t->type->type);
+ else
+ fmtprint(fp, "%T", t->type);
+
+ if(!(fp->flags&FmtShort) && t->note)
+ fmtprint(fp, " \"%Z\"", t->note);
+ return 0;
+
+ case TFORW:
+ if(t->sym)
+ return fmtprint(fp, "undefined %S", t->sym);
+ return fmtstrcpy(fp, "undefined");
+
+ case TUNSAFEPTR:
+ if(fmtmode == FExp)
+ return fmtprint(fp, "@\"unsafe\".Pointer");
+ return fmtprint(fp, "unsafe.Pointer");
+ }
+
+ if(fmtmode == FExp)
+ fatal("missing %E case during export", t->etype);
+ // Don't know how to handle - fall back to detailed prints.
+ return fmtprint(fp, "%E <%S> %T", t->etype, t->sym, t->type);
+}
+
+// Statements which may be rendered with a simplestmt as init.
+static int
+stmtwithinit(int op)
+{
+ switch(op) {
+ case OIF:
+ case OFOR:
+ case OSWITCH:
+ return 1;
+ }
+ return 0;
+}
+
+static int
+stmtfmt(Fmt *f, Node *n)
+{
+ int complexinit, simpleinit, extrablock;
+
+ // some statements allow for an init, but at most one,
+ // but we may have an arbitrary number added, eg by typecheck
+ // and inlining. If it doesn't fit the syntax, emit an enclosing
+ // block starting with the init statements.
+
+ // if we can just say "for" n->ninit; ... then do so
+ simpleinit = n->ninit && !n->ninit->next && !n->ninit->n->ninit && stmtwithinit(n->op);
+ // otherwise, print the inits as separate statements
+ complexinit = n->ninit && !simpleinit && (fmtmode != FErr);
+ // but if it was for if/for/switch, put in an extra surrounding block to limit the scope
+ extrablock = complexinit && stmtwithinit(n->op);
+
+ if(extrablock)
+ fmtstrcpy(f, "{");
+
+ if(complexinit)
+ fmtprint(f, " %H; ", n->ninit);
+
+ switch(n->op){
+ case ODCL:
+ fmtprint(f, "var %S %T", n->left->sym, n->left->type);
+ break;
+
+ case ODCLFIELD:
+ if(n->left)
+ fmtprint(f, "%N %N", n->left, n->right);
+ else
+ fmtprint(f, "%N", n->right);
+ break;
+
+ case OAS:
+ if(n->colas && !complexinit)
+ fmtprint(f, "%N := %N", n->left, n->right);
+ else
+ fmtprint(f, "%N = %N", n->left, n->right);
+ break;
+
+ case OASOP:
+ fmtprint(f, "%N %#O= %N", n->left, n->etype, n->right);
+ break;
+
+ case OAS2:
+ if(n->colas && !complexinit) {
+ fmtprint(f, "%,H := %,H", n->list, n->rlist);
+ break;
+ }
+ // fallthrough
+ case OAS2DOTTYPE:
+ case OAS2FUNC:
+ case OAS2MAPR:
+ case OAS2RECV:
+ fmtprint(f, "%,H = %,H", n->list, n->rlist);
+ break;
+
+ case ORETURN:
+ fmtprint(f, "return %,H", n->list);
+ break;
+
+ case OPROC:
+ fmtprint(f, "go %N", n->left);
+ break;
+
+ case ODEFER:
+ fmtprint(f, "defer %N", n->left);
+ break;
+
+ case OIF:
+ if(simpleinit)
+ fmtprint(f, "if %N; %N { %H }", n->ninit->n, n->ntest, n->nbody);
+ else
+ fmtprint(f, "if %N { %H }", n->ntest, n->nbody);
+ if(n->nelse)
+ fmtprint(f, " else { %H }", n->nelse);
+ break;
+
+ case OFOR:
+ if(fmtmode == FErr) { // TODO maybe only if FmtShort, same below
+ fmtstrcpy(f, "for loop");
+ break;
+ }
+
+ fmtstrcpy(f, "for");
+ if(simpleinit)
+ fmtprint(f, " %N;", n->ninit->n);
+ else if(n->nincr)
+ fmtstrcpy(f, " ;");
+
+ if(n->ntest)
+ fmtprint(f, " %N", n->ntest);
+
+ if(n->nincr)
+ fmtprint(f, "; %N", n->nincr);
+ else if(simpleinit)
+ fmtstrcpy(f, ";");
+
+
+ fmtprint(f, " { %H }", n->nbody);
+ break;
+
+ case ORANGE:
+ if(fmtmode == FErr) {
+ fmtstrcpy(f, "for loop");
+ break;
+ }
+
+ fmtprint(f, "for %,H = range %N { %H }", n->list, n->right, n->nbody);
+ break;
+
+ case OSELECT:
+ case OSWITCH:
+ if(fmtmode == FErr) {
+ fmtprint(f, "%O statement", n->op);
+ break;
+ }
+
+ fmtprint(f, "%#O", n->op);
+ if(simpleinit)
+ fmtprint(f, " %N;", n->ninit->n);
+ if(n->ntest)
+ fmtprint(f, "%N", n->ntest);
+
+ fmtprint(f, " { %H }", n->list);
+ break;
+
+ case OCASE:
+ case OXCASE:
+ if(n->list)
+ fmtprint(f, "case %,H: %H", n->list, n->nbody);
+ else
+ fmtprint(f, "default: %H", n->nbody);
+ break;
+
+ case OBREAK:
+ case OCONTINUE:
+ case OGOTO:
+ case OFALL:
+ case OXFALL:
+ if(n->left)
+ fmtprint(f, "%#O %N", n->op, n->left);
+ else
+ fmtprint(f, "%#O", n->op);
+ break;
+
+ case OEMPTY:
+ break;
+
+ case OLABEL:
+ fmtprint(f, "%N: ", n->left);
+ break;
+
+ }
+
+ if(extrablock)
+ fmtstrcpy(f, "}");
+
+ return 0;
+}
+
+
+static int opprec[] = {
+ [OAPPEND] = 8,
+ [OARRAYBYTESTR] = 8,
+ [OARRAYLIT] = 8,
+ [OARRAYRUNESTR] = 8,
+ [OCALLFUNC] = 8,
+ [OCALLINTER] = 8,
+ [OCALLMETH] = 8,
+ [OCALL] = 8,
+ [OCAP] = 8,
+ [OCLOSE] = 8,
+ [OCONVIFACE] = 8,
+ [OCONVNOP] = 8,
+ [OCONV] = 8,
+ [OCOPY] = 8,
+ [ODELETE] = 8,
+ [OLEN] = 8,
+ [OLITERAL] = 8,
+ [OMAKESLICE] = 8,
+ [OMAKE] = 8,
+ [OMAPLIT] = 8,
+ [ONAME] = 8,
+ [ONEW] = 8,
+ [ONONAME] = 8,
+ [OPACK] = 8,
+ [OPANIC] = 8,
+ [OPAREN] = 8,
+ [OPRINTN] = 8,
+ [OPRINT] = 8,
+ [ORECV] = 8,
+ [ORUNESTR] = 8,
+ [OSTRARRAYBYTE] = 8,
+ [OSTRARRAYRUNE] = 8,
+ [OSTRUCTLIT] = 8,
+ [OTARRAY] = 8,
+ [OTCHAN] = 8,
+ [OTFUNC] = 8,
+ [OTINTER] = 8,
+ [OTMAP] = 8,
+ [OTPAREN] = 8,
+ [OTSTRUCT] = 8,
+
+ [OINDEXMAP] = 8,
+ [OINDEX] = 8,
+ [OSLICE] = 8,
+ [OSLICESTR] = 8,
+ [OSLICEARR] = 8,
+ [ODOTINTER] = 8,
+ [ODOTMETH] = 8,
+ [ODOTPTR] = 8,
+ [ODOTTYPE2] = 8,
+ [ODOTTYPE] = 8,
+ [ODOT] = 8,
+ [OXDOT] = 8,
+
+ [OPLUS] = 7,
+ [ONOT] = 7,
+ [OCOM] = 7,
+ [OMINUS] = 7,
+ [OADDR] = 7,
+ [OIND] = 7,
+
+ [OMUL] = 6,
+ [ODIV] = 6,
+ [OMOD] = 6,
+ [OLSH] = 6,
+ [ORSH] = 6,
+ [OAND] = 6,
+ [OANDNOT] = 6,
+
+ [OADD] = 5,
+ [OSUB] = 5,
+ [OOR] = 5,
+ [OXOR] = 5,
+
+ [OEQ] = 4,
+ [OLT] = 4,
+ [OLE] = 4,
+ [OGE] = 4,
+ [OGT] = 4,
+ [ONE] = 4,
+ [OCMPSTR] = 4,
+ [OCMPIFACE] = 4,
+
+ [OSEND] = 3,
+ [OANDAND] = 2,
+ [OOROR] = 1,
+
+ // Statements handled by stmtfmt
+ [OAS] = -1,
+ [OAS2] = -1,
+ [OAS2DOTTYPE] = -1,
+ [OAS2FUNC] = -1,
+ [OAS2MAPR] = -1,
+ [OAS2RECV] = -1,
+ [OASOP] = -1,
+ [OBREAK] = -1,
+ [OCASE] = -1,
+ [OCONTINUE] = -1,
+ [ODCL] = -1,
+ [ODCLFIELD] = -1,
+ [ODEFER] = -1,
+ [OEMPTY] = -1,
+ [OFALL] = -1,
+ [OFOR] = -1,
+ [OIF] = -1,
+ [OLABEL] = -1,
+ [OPROC] = -1,
+ [ORANGE] = -1,
+ [ORETURN] = -1,
+ [OSELECT] = -1,
+ [OSWITCH] = -1,
+ [OXCASE] = -1,
+ [OXFALL] = -1,
+
+ [OEND] = 0
+};
+
+static int
+exprfmt(Fmt *f, Node *n, int prec)
+{
+ int nprec;
+ NodeList *l;
+ Type *t;
+
+ while(n && n->implicit && (n->op == OIND || n->op == OADDR))
+ n = n->left;
+
+ if(n == N)
+ return fmtstrcpy(f, "<N>");
+
+ nprec = opprec[n->op];
+ if(n->op == OTYPE && n->sym != S)
+ nprec = 8;
+
+ if(prec > nprec)
+ return fmtprint(f, "(%N)", n);
+
+ switch(n->op) {
+ case OPAREN:
+ return fmtprint(f, "(%N)", n->left);
+
+ case ODDDARG:
+ return fmtprint(f, "... argument");
+
+ case OREGISTER:
+ return fmtprint(f, "%R", n->val.u.reg);
+
+ case OLITERAL: // this is a bit of a mess
+ if(fmtmode == FErr && n->sym != S)
+ return fmtprint(f, "%S", n->sym);
+ if(n->val.ctype == CTNIL)
+ n = n->orig; // if this node was a nil decorated with at type, print the original naked nil
+ if(n->type != types[n->type->etype] && n->type != idealbool && n->type != idealstring) {
+ // Need parens when type begins with what might
+ // be misinterpreted as a unary operator: * or <-.
+ if(isptr[n->type->etype] || (n->type->etype == TCHAN && n->type->chan == Crecv))
+ return fmtprint(f, "(%T)(%V)", n->type, &n->val);
+ else
+ return fmtprint(f, "%T(%V)", n->type, &n->val);
+ }
+ return fmtprint(f, "%V", &n->val);
+
+ case ONAME:
+ // Special case: explicit name of func (*T) method(...) is turned into pkg.(*T).method,
+ // but for export, this should be rendered as (*pkg.T).meth.
+ // These nodes have the special property that they are names with a left OTYPE and a right ONAME.
+ if(fmtmode == FExp && n->left && n->left->op == OTYPE && n->right && n->right->op == ONAME) {
+ if(isptr[n->left->type->etype])
+ return fmtprint(f, "(%T).%hhS", n->left->type, n->right->sym);
+ else
+ return fmtprint(f, "%T.%hhS", n->left->type, n->right->sym);
+ }
+ //fallthrough
+ case OPACK:
+ case ONONAME:
+ return fmtprint(f, "%S", n->sym);
+
+ case OTYPE:
+ if(n->type == T && n->sym != S)
+ return fmtprint(f, "%S", n->sym);
+ return fmtprint(f, "%T", n->type);
+
+ case OTARRAY:
+ if(n->left)
+ return fmtprint(f, "[]%N", n->left);
+ return fmtprint(f, "[]%N", n->right); // happens before typecheck
+
+ case OTPAREN:
+ return fmtprint(f, "(%N)", n->left);
+
+ case OTMAP:
+ return fmtprint(f, "map[%N]%N", n->left, n->right);
+
+ case OTCHAN:
+ switch(n->etype) {
+ case Crecv:
+ return fmtprint(f, "<-chan %N", n->left);
+ case Csend:
+ return fmtprint(f, "chan<- %N", n->left);
+ default:
+ if(n->left != N && n->left->op == TCHAN && n->left->sym == S && n->left->etype == Crecv)
+ return fmtprint(f, "chan (%N)", n->left);
+ else
+ return fmtprint(f, "chan %N", n->left);
+ }
+
+ case OTSTRUCT:
+ return fmtprint(f, "<struct>");
+
+ case OTINTER:
+ return fmtprint(f, "<inter>");
+
+ case OTFUNC:
+ return fmtprint(f, "<func>");
+
+ case OCLOSURE:
+ if(fmtmode == FErr)
+ return fmtstrcpy(f, "func literal");
+ return fmtprint(f, "%T { %H }", n->type, n->nbody);
+
+ case OCOMPLIT:
+ if(fmtmode == FErr)
+ return fmtstrcpy(f, "composite literal");
+ return fmtprint(f, "%N{ %,H }", n->right, n->list);
+
+ case OPTRLIT:
+ if(fmtmode == FExp && n->left->implicit)
+ return fmtprint(f, "%N", n->left);
+ return fmtprint(f, "&%N", n->left);
+
+ case OSTRUCTLIT:
+ if(fmtmode == FExp) { // requires special handling of field names
+ if(n->implicit)
+ fmtstrcpy(f, "{");
+ else
+ fmtprint(f, "%T{", n->type);
+ for(l=n->list; l; l=l->next) {
+ // another special case: if n->left is an embedded field of builtin type,
+ // it needs to be non-qualified. Can't figure that out in %S, so do it here
+ if(l->n->left->type->embedded) {
+ t = l->n->left->type->type;
+ if(t->sym == S)
+ t = t->type;
+ fmtprint(f, " %T:%N", t, l->n->right);
+ } else
+ fmtprint(f, " %hhS:%N", l->n->left->sym, l->n->right);
+
+ if(l->next)
+ fmtstrcpy(f, ",");
+ else
+ fmtstrcpy(f, " ");
+ }
+ return fmtstrcpy(f, "}");
+ }
+ // fallthrough
+
+ case OARRAYLIT:
+ case OMAPLIT:
+ if(fmtmode == FErr)
+ return fmtprint(f, "%T literal", n->type);
+ if(fmtmode == FExp && n->implicit)
+ return fmtprint(f, "{ %,H }", n->list);
+ return fmtprint(f, "%T{ %,H }", n->type, n->list);
+
+ case OKEY:
+ if(n->left && n->right)
+ return fmtprint(f, "%N:%N", n->left, n->right);
+ if(!n->left && n->right)
+ return fmtprint(f, ":%N", n->right);
+ if(n->left && !n->right)
+ return fmtprint(f, "%N:", n->left);
+ return fmtstrcpy(f, ":");
+
+ case OXDOT:
+ case ODOT:
+ case ODOTPTR:
+ case ODOTINTER:
+ case ODOTMETH:
+ exprfmt(f, n->left, nprec);
+ if(n->right == N || n->right->sym == S)
+ fmtstrcpy(f, ".<nil>");
+ return fmtprint(f, ".%hhS", n->right->sym);
+
+ case ODOTTYPE:
+ case ODOTTYPE2:
+ exprfmt(f, n->left, nprec);
+ if(n->right != N)
+ return fmtprint(f, ".(%N)", n->right);
+ return fmtprint(f, ".(%T)", n->type);
+
+ case OINDEX:
+ case OINDEXMAP:
+ case OSLICE:
+ case OSLICESTR:
+ case OSLICEARR:
+ exprfmt(f, n->left, nprec);
+ return fmtprint(f, "[%N]", n->right);
+
+ case OCOPY:
+ case OCOMPLEX:
+ return fmtprint(f, "%#O(%N, %N)", n->op, n->left, n->right);
+
+ case OCONV:
+ case OCONVIFACE:
+ case OCONVNOP:
+ case OARRAYBYTESTR:
+ case OARRAYRUNESTR:
+ case OSTRARRAYBYTE:
+ case OSTRARRAYRUNE:
+ case ORUNESTR:
+ if(n->type == T || n->type->sym == S)
+ return fmtprint(f, "(%T)(%N)", n->type, n->left);
+ if(n->left)
+ return fmtprint(f, "%T(%N)", n->type, n->left);
+ return fmtprint(f, "%T(%,H)", n->type, n->list);
+
+ case OREAL:
+ case OIMAG:
+ case OAPPEND:
+ case OCAP:
+ case OCLOSE:
+ case ODELETE:
+ case OLEN:
+ case OMAKE:
+ case ONEW:
+ case OPANIC:
+ case OPRINT:
+ case OPRINTN:
+ if(n->left)
+ return fmtprint(f, "%#O(%N)", n->op, n->left);
+ if(n->isddd)
+ return fmtprint(f, "%#O(%,H...)", n->op, n->list);
+ return fmtprint(f, "%#O(%,H)", n->op, n->list);
+
+ case OCALL:
+ case OCALLFUNC:
+ case OCALLINTER:
+ case OCALLMETH:
+ exprfmt(f, n->left, nprec);
+ if(n->isddd)
+ return fmtprint(f, "(%,H...)", n->list);
+ return fmtprint(f, "(%,H)", n->list);
+
+ case OMAKEMAP:
+ case OMAKECHAN:
+ case OMAKESLICE:
+ if(n->list) // pre-typecheck
+ return fmtprint(f, "make(%T, %,H)", n->type, n->list);
+ if(n->right)
+ return fmtprint(f, "make(%T, %N, %N)", n->type, n->left, n->right);
+ if(n->left)
+ return fmtprint(f, "make(%T, %N)", n->type, n->left);
+ return fmtprint(f, "make(%T)", n->type);
+
+ // Unary
+ case OPLUS:
+ case OMINUS:
+ case OADDR:
+ case OCOM:
+ case OIND:
+ case ONOT:
+ case ORECV:
+ if(n->left->op == n->op)
+ fmtprint(f, "%#O ", n->op);
+ else
+ fmtprint(f, "%#O", n->op);
+ return exprfmt(f, n->left, nprec+1);
+
+ // Binary
+ case OADD:
+ case OADDSTR:
+ case OAND:
+ case OANDAND:
+ case OANDNOT:
+ case ODIV:
+ case OEQ:
+ case OGE:
+ case OGT:
+ case OLE:
+ case OLT:
+ case OLSH:
+ case OMOD:
+ case OMUL:
+ case ONE:
+ case OOR:
+ case OOROR:
+ case ORSH:
+ case OSEND:
+ case OSUB:
+ case OXOR:
+ exprfmt(f, n->left, nprec);
+ fmtprint(f, " %#O ", n->op);
+ exprfmt(f, n->right, nprec+1);
+ return 0;
+
+ case OCMPSTR:
+ case OCMPIFACE:
+ exprfmt(f, n->left, nprec);
+ fmtprint(f, " %#O ", n->etype);
+ exprfmt(f, n->right, nprec+1);
+ return 0;
+ }
+
+ return fmtprint(f, "<node %O>", n->op);
+}
+
+static int
+nodefmt(Fmt *f, Node *n)
+{
+ Type *t;
+
+ t = n->type;
+
+ // we almost always want the original, except in export mode for literals
+ // this saves the importer some work, and avoids us having to redo some
+ // special casing for package unsafe
+ if((fmtmode != FExp || n->op != OLITERAL) && n->orig != N)
+ n = n->orig;
+
+ if(f->flags&FmtLong && t != T) {
+ if(t->etype == TNIL)
+ return fmtprint(f, "nil");
+ else
+ return fmtprint(f, "%N (type %T)", n, t);
+ }
+
+ // TODO inlining produces expressions with ninits. we can't print these yet.
+
+ if(opprec[n->op] < 0)
+ return stmtfmt(f, n);
+
+ return exprfmt(f, n, 0);
+}
+
+static int dumpdepth;
+
+static void
+indent(Fmt *fp)
+{
+ int i;
+
+ fmtstrcpy(fp, "\n");
+ for(i = 0; i < dumpdepth; ++i)
+ fmtstrcpy(fp, ". ");
+}
+
+static int
+nodedump(Fmt *fp, Node *n)
+{
+ int recur;
+
+ if(n == N)
+ return 0;
+
+ recur = !(fp->flags&FmtShort);
+
+ if(recur) {
+ indent(fp);
+ if(dumpdepth > 10)
+ return fmtstrcpy(fp, "...");
+
+ if(n->ninit != nil) {
+ fmtprint(fp, "%O-init%H", n->op, n->ninit);
+ indent(fp);
+ }
+ }
+
+// fmtprint(fp, "[%p]", n);
+
+ switch(n->op) {
+ default:
+ fmtprint(fp, "%O%J", n->op, n);
+ break;
+ case OREGISTER:
+ fmtprint(fp, "%O-%R%J", n->op, n->val.u.reg, n);
+ break;
+ case OLITERAL:
+ fmtprint(fp, "%O-%V%J", n->op, &n->val, n);
+ break;
+ case ONAME:
+ case ONONAME:
+ if(n->sym != S)
+ fmtprint(fp, "%O-%S%J", n->op, n->sym, n);
+ else
+ fmtprint(fp, "%O%J", n->op, n);
+ break;
+ case OASOP:
+ fmtprint(fp, "%O-%O%J", n->op, n->etype, n);
+ break;
+ case OTYPE:
+ fmtprint(fp, "%O %S%J type=%T", n->op, n->sym, n, n->type);
+ if(recur && n->type == T && n->ntype) {
+ indent(fp);
+ fmtprint(fp, "%O-ntype%N", n->op, n->ntype);
+ }
+ break;
+ }
+
+ if(n->sym != S && n->op != ONAME)
+ fmtprint(fp, " %S G%d", n->sym, n->vargen);
+
+ if(n->type != T)
+ fmtprint(fp, " %T", n->type);
+
+ if(recur) {
+ if(n->left)
+ fmtprint(fp, "%N", n->left);
+ if(n->right)
+ fmtprint(fp, "%N", n->right);
+ if(n->list) {
+ indent(fp);
+ fmtprint(fp, "%O-list%H", n->op, n->list);
+ }
+ if(n->rlist) {
+ indent(fp);
+ fmtprint(fp, "%O-rlist%H", n->op, n->rlist);
+ }
+ if(n->ntest) {
+ indent(fp);
+ fmtprint(fp, "%O-test%N", n->op, n->ntest);
+ }
+ if(n->nbody) {
+ indent(fp);
+ fmtprint(fp, "%O-body%H", n->op, n->nbody);
+ }
+ if(n->nelse) {
+ indent(fp);
+ fmtprint(fp, "%O-else%H", n->op, n->nelse);
+ }
+ if(n->nincr) {
+ indent(fp);
+ fmtprint(fp, "%O-incr%N", n->op, n->nincr);
+ }
+ }
+
+ return 0;
+}
+
+// Fmt "%S": syms
+// Flags: "%hS" suppresses qualifying with package
+static int
+Sconv(Fmt *fp)
+{
+ Sym *s;
+ int r, sm;
+ unsigned long sf;
+
+ s = va_arg(fp->args, Sym*);
+ if(s == S)
+ return fmtstrcpy(fp, "<S>");
+
+ if(s->name && s->name[0] == '_' && s->name[1] == '\0')
+ return fmtstrcpy(fp, "_");
+
+ sf = fp->flags;
+ sm = setfmode(&fp->flags);
+ r = symfmt(fp, s);
+ fp->flags = sf;
+ fmtmode = sm;
+ return r;
+}
+
+// Fmt "%T": types.
+// Flags: 'l' print definition, not name
+// 'h' omit 'func' and receiver from function types, short type names
+// 'u' package name, not prefix (FTypeId mode, sticky)
+static int
+Tconv(Fmt *fp)
+{
+ Type *t;
+ int r, sm;
+ unsigned long sf;
+
+ t = va_arg(fp->args, Type*);
+ if(t == T)
+ return fmtstrcpy(fp, "<T>");
+
+ if(t->trecur > 4)
+ return fmtstrcpy(fp, "<...>");
+
+ t->trecur++;
+ sf = fp->flags;
+ sm = setfmode(&fp->flags);
+
+ if(fmtmode == FTypeId && (sf&FmtUnsigned))
+ fmtpkgpfx++;
+ if(fmtpkgpfx)
+ fp->flags |= FmtUnsigned;
+
+ r = typefmt(fp, t);
+
+ if(fmtmode == FTypeId && (sf&FmtUnsigned))
+ fmtpkgpfx--;
+
+ fp->flags = sf;
+ fmtmode = sm;
+ t->trecur--;
+ return r;
+}
+
+// Fmt '%N': Nodes.
+// Flags: 'l' suffix with "(type %T)" where possible
+// '+h' in debug mode, don't recurse, no multiline output
+static int
+Nconv(Fmt *fp)
+{
+ Node *n;
+ int r, sm;
+ unsigned long sf;
+
+ n = va_arg(fp->args, Node*);
+ if(n == N)
+ return fmtstrcpy(fp, "<N>");
+ sf = fp->flags;
+ sm = setfmode(&fp->flags);
+
+ r = -1;
+ switch(fmtmode) {
+ case FErr:
+ case FExp:
+ r = nodefmt(fp, n);
+ break;
+ case FDbg:
+ dumpdepth++;
+ r = nodedump(fp, n);
+ dumpdepth--;
+ break;
+ default:
+ fatal("unhandled %%N mode");
+ }
+
+ fp->flags = sf;
+ fmtmode = sm;
+ return r;
+}
+
+// Fmt '%H': NodeList.
+// Flags: all those of %N plus ',': separate with comma's instead of semicolons.
+static int
+Hconv(Fmt *fp)
+{
+ NodeList *l;
+ int r, sm;
+ unsigned long sf;
+ char *sep;
+
+ l = va_arg(fp->args, NodeList*);
+
+ if(l == nil && fmtmode == FDbg)
+ return fmtstrcpy(fp, "<nil>");
+
+ sf = fp->flags;
+ sm = setfmode(&fp->flags);
+ r = 0;
+ sep = "; ";
+ if(fmtmode == FDbg)
+ sep = "\n";
+ else if(fp->flags & FmtComma)
+ sep = ", ";
+
+ for(;l; l=l->next) {
+ r += fmtprint(fp, "%N", l->n);
+ if(l->next)
+ r += fmtstrcpy(fp, sep);
+ }
+
+ fp->flags = sf;
+ fmtmode = sm;
+ return r;
+}
+
+void
+fmtinstallgo(void)
+{
+ fmtmode = FErr;
+ fmtinstall('E', Econv); // etype opcodes
+ fmtinstall('J', Jconv); // all the node flags
+ fmtinstall('H', Hconv); // node lists
+ fmtinstall('L', Lconv); // line number
+ fmtinstall('N', Nconv); // node pointer
+ fmtinstall('O', Oconv); // node opcodes
+ fmtinstall('S', Sconv); // sym pointer
+ fmtinstall('T', Tconv); // type pointer
+ fmtinstall('V', Vconv); // val pointer
+ fmtinstall('Z', Zconv); // escaped string
+
+ // These are in mparith1.c
+ fmtinstall('B', Bconv); // big numbers
+ fmtinstall('F', Fconv); // big float numbers
+
+}
+
+void
+dumplist(char *s, NodeList *l)
+{
+ print("%s\n%+H\n", s, l);
+}
+
+void
+dump(char *s, Node *n)
+{
+ print("%s [%p]\n%+N\n", s, n, n);
+}
diff --git a/src/cmd/gc/gen.c b/src/cmd/gc/gen.c
index cb66921ba..694a10ab5 100644
--- a/src/cmd/gc/gen.c
+++ b/src/cmd/gc/gen.c
@@ -7,11 +7,13 @@
* mainly statements and control flow.
*/
+#include <u.h>
+#include <libc.h>
#include "go.h"
static void cgen_dcl(Node *n);
static void cgen_proc(Node *n, int proc);
-static void checkgoto(Node*, Node*);
+static void checkgoto(Node*, Node*);
static Label *labellist;
static Label *lastlabel;
@@ -26,50 +28,93 @@ sysfunc(char *name)
return n;
}
+/*
+ * the address of n has been taken and might be used after
+ * the current function returns. mark any local vars
+ * as needing to move to the heap.
+ */
void
-allocparams(void)
+addrescapes(Node *n)
{
- NodeList *l;
- Node *n;
- uint32 w;
- Sym *s;
- int lno;
+ char buf[100];
+ Node *oldfn;
+
+ switch(n->op) {
+ default:
+ // probably a type error already.
+ // dump("addrescapes", n);
+ break;
+
+ case ONAME:
+ if(n == nodfp)
+ break;
+
+ // if this is a tmpname (PAUTO), it was tagged by tmpname as not escaping.
+ // on PPARAM it means something different.
+ if(n->class == PAUTO && n->esc == EscNever)
+ break;
+
+ if(debug['N'] && n->esc != EscUnknown)
+ fatal("without escape analysis, only PAUTO's should have esc: %N", n);
- if(stksize < 0)
- fatal("allocparams not during code generation");
-
- /*
- * allocate (set xoffset) the stack
- * slots for all automatics.
- * allocated starting at -w down.
- */
- lno = lineno;
- for(l=curfn->dcl; l; l=l->next) {
- n = l->n;
- if(n->op == ONAME && n->class == PHEAP-1) {
- // heap address variable; finish the job
- // started in addrescapes.
- s = n->sym;
- tempname(n, n->type);
- n->sym = s;
+ switch(n->class) {
+ case PPARAMREF:
+ addrescapes(n->defn);
+ break;
+ case PPARAM:
+ case PPARAMOUT:
+ // if func param, need separate temporary
+ // to hold heap pointer.
+ // the function type has already been checked
+ // (we're in the function body)
+ // so the param already has a valid xoffset.
+
+ // expression to refer to stack copy
+ n->stackparam = nod(OPARAM, n, N);
+ n->stackparam->type = n->type;
+ n->stackparam->addable = 1;
+ if(n->xoffset == BADWIDTH)
+ fatal("addrescapes before param assignment");
+ n->stackparam->xoffset = n->xoffset;
+ // fallthrough
+
+ case PAUTO:
+ n->class |= PHEAP;
+ n->addable = 0;
+ n->ullman = 2;
+ n->xoffset = 0;
+
+ // create stack variable to hold pointer to heap
+ oldfn = curfn;
+ curfn = n->curfn;
+ n->heapaddr = temp(ptrto(n->type));
+ snprint(buf, sizeof buf, "&%S", n->sym);
+ n->heapaddr->sym = lookup(buf);
+ n->heapaddr->orig->sym = n->heapaddr->sym;
+ if(!debug['N'])
+ n->esc = EscHeap;
+ if(debug['m'])
+ print("%L: moved to heap: %N\n", n->lineno, n);
+ curfn = oldfn;
+ break;
}
- if(n->op != ONAME || n->class != PAUTO)
- continue;
- if (n->xoffset != BADWIDTH)
- continue;
- if(n->type == T)
- continue;
- dowidth(n->type);
- w = n->type->width;
- if(w >= MAXWIDTH)
- fatal("bad width");
- stksize += w;
- stksize = rnd(stksize, n->type->align);
- if(thechar == '5')
- stksize = rnd(stksize, widthptr);
- n->xoffset = -stksize;
+ break;
+
+ case OIND:
+ case ODOTPTR:
+ break;
+
+ case ODOT:
+ case OINDEX:
+ // ODOTPTR has already been introduced,
+ // so these are the non-pointer ODOT and OINDEX.
+ // In &x[0], if x is a slice, then x does not
+ // escape--the pointer inside x does, but that
+ // is always a heap pointer anyway.
+ if(!isslice(n->left->type))
+ addrescapes(n->left);
+ break;
}
- lineno = lno;
}
void
@@ -197,7 +242,7 @@ stmtlabel(Node *n)
if(n->sym != S)
if((lab = n->sym->label) != L)
if(lab->def != N)
- if(lab->def->right == n)
+ if(lab->def->defn == n)
return lab;
return L;
}
@@ -227,7 +272,6 @@ gen(Node *n)
if(n == N)
goto ret;
- p3 = pc; // save pc for loop labels
if(n->ninit)
genlist(n->ninit);
@@ -266,13 +310,13 @@ gen(Node *n)
if(lab->labelpc == P)
lab->labelpc = pc;
- if(n->right) {
- switch(n->right->op) {
+ if(n->defn) {
+ switch(n->defn->op) {
case OFOR:
case OSWITCH:
case OSELECT:
// so stmtlabel can find the label
- n->right->sym = lab->sym;
+ n->defn->sym = lab->sym;
}
}
break;
@@ -486,7 +530,7 @@ cgen_callmeth(Node *n, int proc)
/*
* generate code to start new proc running call n.
*/
-void
+static void
cgen_proc(Node *n, int proc)
{
switch(n->left->op) {
@@ -598,15 +642,12 @@ cgen_as(Node *nl, Node *nr)
Type *tl;
int iszer;
- if(nl == N)
- return;
-
if(debug['g']) {
dump("cgen_as", nl);
dump("cgen_as = ", nr);
}
- if(isblank(nl)) {
+ if(nl == N || isblank(nl)) {
cgen_discard(nr);
return;
}
@@ -748,12 +789,8 @@ tempname(Node *nn, Type *t)
{
Node *n;
Sym *s;
- uint32 w;
- if(stksize < 0)
- fatal("tempname not during code generation");
-
- if (curfn == N)
+ if(curfn == N)
fatal("no curfn for tempname");
if(t == T) {
@@ -768,23 +805,27 @@ tempname(Node *nn, Type *t)
s = lookup(namebuf);
n = nod(ONAME, N, N);
n->sym = s;
+ s->def = n;
n->type = t;
n->class = PAUTO;
n->addable = 1;
n->ullman = 1;
- n->noescape = 1;
+ n->esc = EscNever;
n->curfn = curfn;
curfn->dcl = list(curfn->dcl, n);
dowidth(t);
- w = t->width;
- stksize += w;
- stksize = rnd(stksize, t->align);
- if(thechar == '5')
- stksize = rnd(stksize, widthptr);
- n->xoffset = -stksize;
-
- // print("\ttmpname (%d): %N\n", stksize, n);
-
+ n->xoffset = 0;
*nn = *n;
}
+
+Node*
+temp(Type *t)
+{
+ Node *n;
+
+ n = nod(OXXX, N, N);
+ tempname(n, t);
+ n->sym->def->used = 1;
+ return n;
+}
diff --git a/src/cmd/gc/go.errors b/src/cmd/gc/go.errors
index b5af4678c..e29cfff5b 100644
--- a/src/cmd/gc/go.errors
+++ b/src/cmd/gc/go.errors
@@ -67,4 +67,7 @@ static struct {
% loadsys package imports LFUNC LNAME '(' ')' '{' LFUNC LNAME
"nested func not allowed",
+
+ % loadsys package imports LFUNC LNAME '(' ')' '{' LIF if_header loop_body LELSE ';'
+ "else must be followed by if or statement block"
};
diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h
index da0fb5146..8c4fff15a 100644
--- a/src/cmd/gc/go.h
+++ b/src/cmd/gc/go.h
@@ -2,8 +2,6 @@
// 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 <bio.h>
#undef OAPPEND
@@ -18,6 +16,12 @@
#undef BUFSIZ
+// The parser's maximum stack size.
+// We have to use a #define macro here since yacc
+// or bison will check for its definition and use
+// a potentially smaller value if it is undefined.
+#define YYMAXDEPTH 500
+
enum
{
NHUNK = 50000,
@@ -25,7 +29,6 @@ enum
NSYMB = 500,
NHASH = 1024,
STRINGSZ = 200,
- YYMAXDEPTH = 500,
MAXALIGN = 7,
UINF = 100,
HISTSZ = 10,
@@ -34,23 +37,30 @@ enum
AUNK = 100,
- // these values are known by runtime
+ // These values are known by runtime.
+ // The MEMx and NOEQx values must run in parallel. See algtype.
AMEM = 0,
- ANOEQ,
- ASTRING,
- AINTER,
- ANILINTER,
- ASLICE,
+ AMEM0,
AMEM8,
AMEM16,
AMEM32,
AMEM64,
AMEM128,
+ ANOEQ,
+ ANOEQ0,
ANOEQ8,
ANOEQ16,
ANOEQ32,
ANOEQ64,
ANOEQ128,
+ ASTRING,
+ AINTER,
+ ANILINTER,
+ ASLICE,
+ AFLOAT32,
+ AFLOAT64,
+ ACPLX64,
+ ACPLX128,
BADWIDTH = -1000000000,
};
@@ -69,33 +79,6 @@ struct Strlit
char s[3]; // variable
};
-/*
- * note this is the runtime representation
- * of hashmap iterator. it is probably
- * insafe to use it this way, but it puts
- * all the changes in one place.
- * only flag is referenced from go.
- * actual placement does not matter as long
- * as the size is >= actual size.
- */
-typedef struct Hiter Hiter;
-struct Hiter
-{
- uchar data[8]; // return val from next
- int32 elemsize; // size of elements in table */
- int32 changes; // number of changes observed last time */
- int32 i; // stack pointer in subtable_state */
- uchar last[8]; // last hash value returned */
- uchar h[8]; // the hash table */
- struct
- {
- uchar sub[8]; // pointer into subtable */
- uchar start[8]; // pointer into start of subtable */
- uchar end[8]; // pointer into end of subtable */
- uchar pad[8];
- } sub[4];
-};
-
enum
{
Mpscale = 29, // safely smaller than bits in a long
@@ -137,7 +120,7 @@ struct Val
{
short reg; // OREGISTER
short bval; // bool value CTBOOL
- Mpint* xval; // int CTINT
+ Mpint* xval; // int CTINT, rune CTRUNE
Mpflt* fval; // float CTFLT
Mpcplx* cval; // float CTCPLX
Strlit* sval; // string CTSTR
@@ -155,17 +138,16 @@ struct Type
{
uchar etype;
uchar chan;
- uchar recur; // to detect loops
uchar trecur; // to detect loops
uchar printed;
uchar embedded; // TFIELD embedded type
uchar siggen;
- uchar funarg;
+ uchar funarg; // on TSTRUCT and TFIELD
uchar copyany;
uchar local; // created in this file
uchar deferwidth;
uchar broke;
- uchar isddd; // TFIELD is ... argument
+ uchar isddd; // TFIELD is ... argument
uchar align;
Node* nod; // canonical OTYPE node
@@ -203,8 +185,50 @@ struct Type
};
#define T ((Type*)0)
+typedef struct InitEntry InitEntry;
+typedef struct InitPlan InitPlan;
+
+struct InitEntry
+{
+ vlong xoffset; // struct, array only
+ Node *key; // map only
+ Node *expr;
+};
+
+struct InitPlan
+{
+ vlong lit; // bytes of initialized non-zero literals
+ vlong zero; // bytes of zeros
+ vlong expr; // bytes of run-time computed expressions
+
+ InitEntry *e;
+ int len;
+ int cap;
+};
+
+enum
+{
+ EscUnknown,
+ EscHeap,
+ EscScope,
+ EscNone,
+ EscNever,
+};
+
struct Node
{
+ // Tree structure.
+ // Generic recursive walks should follow these fields.
+ Node* left;
+ Node* right;
+ Node* ntest;
+ Node* nincr;
+ NodeList* ninit;
+ NodeList* nbody;
+ NodeList* nelse;
+ NodeList* list;
+ NodeList* rlist;
+
uchar op;
uchar ullman; // sethi/ullman number
uchar addable; // type of addressability - 0 is not addressable
@@ -215,41 +239,26 @@ struct Node
uchar embedded; // ODCLFIELD embedded type
uchar colas; // OAS resulting from :=
uchar diag; // already printed error about this
- uchar noescape; // ONAME never move to heap
+ uchar esc; // EscXXX
uchar funcdepth;
uchar builtin; // built-in name, like len or close
uchar walkdef;
uchar typecheck;
uchar local;
+ uchar dodata;
uchar initorder;
- uchar dodata; // compile literal assignment as data statement
uchar used;
uchar isddd;
- uchar pun; // don't registerize variable ONAME
uchar readonly;
- uchar implicit; // don't show in printout
+ uchar implicit;
+ uchar addrtaken; // address taken, even if not moved to heap
+ uchar dupok; // duplicate definitions ok (for func)
// most nodes
- Node* left;
- Node* right;
Type* type;
Type* realtype; // as determined by typecheck
- NodeList* list;
- NodeList* rlist;
Node* orig; // original form, for printing, and tracking copies of ONAMEs
- // for-body
- NodeList* ninit;
- Node* ntest;
- Node* nincr;
- NodeList* nbody;
-
- // if-body
- NodeList* nelse;
-
- // cases
- Node* ncase;
-
// func
Node* nname;
Node* shortname;
@@ -257,15 +266,17 @@ struct Node
NodeList* exit;
NodeList* cvars; // closure params
NodeList* dcl; // autodcl for this func/closure
+ NodeList* inl; // copy of the body for use in inlining
// OLITERAL/OREGISTER
Val val;
// ONAME
Node* ntype;
- Node* defn;
+ Node* defn; // ONAME: initializing assignment; OLABEL: labeled statement
Node* pack; // real package for import . names
Node* curfn; // function for local variables
+ Type* paramfld; // TFIELD for this PPARAM
// ONAME func param with PHEAP
Node* heapaddr; // temp holding heap address of param
@@ -276,8 +287,18 @@ struct Node
Node* outer; // outer PPARAMREF in nested closure
Node* closure; // ONAME/PHEAP <-> ONAME/PPARAMREF
+ // ONAME substitute while inlining
+ Node* inlvar;
+
// OPACK
Pkg* pkg;
+
+ // OARRAYLIT, OMAPLIT, OSTRUCTLIT.
+ InitPlan* initplan;
+
+ // Escape analysis.
+ NodeList* escflowsrc; // flow(this, src)
+ int escloopdepth; // -1: global, 0: not set, function top level:1, increased inside function for every loop or label to mark scopes
Sym* sym; // various
int32 vargen; // unique name for OTYPE/ONAME
@@ -287,9 +308,25 @@ struct Node
int32 stkdelta; // offset added by stack frame compaction phase.
int32 ostk;
int32 iota;
+ uint32 walkgen;
};
#define N ((Node*)0)
-EXTERN int32 walkgen;
+
+/*
+ * Every node has a walkgen field.
+ * If you want to do a traversal of a node graph that
+ * might contain duplicates and want to avoid
+ * visiting the same nodes twice, increment walkgen
+ * before starting. Then before processing a node, do
+ *
+ * if(n->walkgen == walkgen)
+ * return;
+ * n->walkgen = walkgen;
+ *
+ * Such a walk cannot call another such walk recursively,
+ * because of the use of the global walkgen.
+ */
+EXTERN uint32 walkgen;
struct NodeList
{
@@ -300,9 +337,9 @@ struct NodeList
enum
{
- SymExport = 1<<0,
+ SymExport = 1<<0, // to be exported
SymPackage = 1<<1,
- SymExported = 1<<2,
+ SymExported = 1<<2, // already written out by export
SymUniq = 1<<3,
SymSiggen = 1<<4,
};
@@ -329,10 +366,10 @@ EXTERN Sym* dclstack;
struct Pkg
{
- char* name;
- Strlit* path;
+ char* name; // package name
+ Strlit* path; // string literal used in import statement
Sym* pathsym;
- char* prefix;
+ char* prefix; // escaped path for use in symbol table
Pkg* link;
char exported; // import line written in export data
char direct; // imported directly
@@ -374,20 +411,21 @@ enum
OADDR,
OANDAND,
OAPPEND,
- OARRAY,
OARRAYBYTESTR, OARRAYRUNESTR,
OSTRARRAYBYTE, OSTRARRAYRUNE,
- OAS, OAS2, OAS2MAPW, OAS2FUNC, OAS2RECV, OAS2MAPR, OAS2DOTTYPE, OASOP,
+ OAS, OAS2, OAS2FUNC, OAS2RECV, OAS2MAPR, OAS2DOTTYPE,
+ OASOP,
OBAD,
OCALL, OCALLFUNC, OCALLMETH, OCALLINTER,
OCAP,
OCLOSE,
OCLOSURE,
OCMPIFACE, OCMPSTR,
- OCOMPLIT, OMAPLIT, OSTRUCTLIT, OARRAYLIT,
+ OCOMPLIT, OMAPLIT, OSTRUCTLIT, OARRAYLIT, OPTRLIT,
OCONV, OCONVIFACE, OCONVNOP,
OCOPY,
ODCL, ODCLFUNC, ODCLFIELD, ODCLCONST, ODCLTYPE,
+ ODELETE,
ODOT, ODOTPTR, ODOTMETH, ODOTINTER, OXDOT,
ODOTTYPE,
ODOTTYPE2,
@@ -444,6 +482,9 @@ enum
// misc
ODDD,
+ ODDDARG,
+ OINLCALL, // intermediary representation of an inlined call
+ OITAB, // itable word of interface value
// for back ends
OCMP, ODEC, OEXTEND, OINC, OREGISTER, OINDREG,
@@ -500,6 +541,7 @@ enum
CTxxx,
CTINT,
+ CTRUNE,
CTFLT,
CTCPLX,
CTSTR,
@@ -561,7 +603,6 @@ typedef struct Var Var;
struct Var
{
vlong offset;
- Sym* sym;
Sym* gotype;
Node* node;
int width;
@@ -644,6 +685,7 @@ struct Magic
};
typedef struct Prog Prog;
+#pragma incomplete Prog
struct Label
{
@@ -722,16 +764,22 @@ EXTERN Pkg* gostringpkg; // fake pkg for Go strings
EXTERN Pkg* runtimepkg; // package runtime
EXTERN Pkg* stringpkg; // fake package for C strings
EXTERN Pkg* typepkg; // fake package for runtime type info
+EXTERN Pkg* weaktypepkg; // weak references to runtime type info
EXTERN Pkg* unsafepkg; // package unsafe
EXTERN Pkg* phash[128];
EXTERN int tptr; // either TPTR32 or TPTR64
extern char* runtimeimport;
extern char* unsafeimport;
+EXTERN char* myimportpath;
EXTERN Idir* idirs;
+EXTERN char* localimport;
EXTERN Type* types[NTYPE];
EXTERN Type* idealstring;
EXTERN Type* idealbool;
+EXTERN Type* bytetype;
+EXTERN Type* runetype;
+EXTERN Type* errortype;
EXTERN uchar simtype[NTYPE];
EXTERN uchar isptr[NTYPE];
EXTERN uchar isforw[NTYPE];
@@ -763,7 +811,7 @@ EXTERN NodeList* xtop;
EXTERN NodeList* externdcl;
EXTERN NodeList* closures;
EXTERN NodeList* exportlist;
-EXTERN NodeList* typelist;
+EXTERN NodeList* importlist; // imported functions and methods with inlinable bodies
EXTERN int dclcontext; // PEXTERN/PAUTO
EXTERN int incannedimport;
EXTERN int statuniqgen; // name generator for static temps
@@ -787,18 +835,13 @@ EXTERN Node* nblank;
extern int thechar;
extern char* thestring;
+
EXTERN char* hunk;
EXTERN int32 nhunk;
EXTERN int32 thunk;
-EXTERN int exporting;
-EXTERN int erroring;
-EXTERN int noargnames;
-
EXTERN int funcdepth;
EXTERN int typecheckok;
-EXTERN int packagequotes;
-EXTERN int longsymnames;
EXTERN int compiling_runtime;
/*
@@ -883,7 +926,6 @@ void colasdefn(NodeList *left, Node *defn);
NodeList* constiter(NodeList *vl, Node *t, NodeList *cl);
Node* dclname(Sym *s);
void declare(Node *n, int ctxt);
-Type* dostruct(NodeList *l, int et);
void dumpdcl(char *st);
Node* embedded(Sym *s);
Node* fakethis(void);
@@ -904,30 +946,43 @@ void popdcl(void);
void poptodcl(void);
void redeclare(Sym *s, char *where);
void testdclstack(void);
+Type* tointerface(NodeList *l);
+Type* tostruct(NodeList *l);
Node* typedcl0(Sym *s);
Node* typedcl1(Node *n, Node *t, int local);
-void typedcl2(Type *pt, Type *t);
Node* typenod(Type *t);
NodeList* variter(NodeList *vl, Node *t, NodeList *el);
/*
+ * esc.c
+ */
+void escapes(NodeList*);
+
+/*
* export.c
*/
void autoexport(Node *n, int ctxt);
void dumpexport(void);
int exportname(char *s);
void exportsym(Node *n);
-void importconst(Sym *s, Type *t, Node *n);
-void importmethod(Sym *s, Type *t);
-Sym* importsym(Sym *s, int op);
-void importtype(Type *pt, Type *t);
-void importvar(Sym *s, Type *t, int ctxt);
+void importconst(Sym *s, Type *t, Node *n);
+void importimport(Sym *s, Strlit *z);
+Sym* importsym(Sym *s, int op);
+void importtype(Type *pt, Type *t);
+void importvar(Sym *s, Type *t);
Type* pkgtype(Sym *s);
/*
+ * fmt.c
+ */
+void fmtinstallgo(void);
+void dump(char *s, Node *n);
+void dumplist(char *s, NodeList *l);
+
+/*
* gen.c
*/
-void allocparams(void);
+void addrescapes(Node *n);
void cgen_as(Node *nl, Node *nr);
void cgen_callmeth(Node *n, int proc);
void clearlabels(void);
@@ -937,12 +992,20 @@ void gen(Node *n);
void genlist(NodeList *l);
Node* sysfunc(char *name);
void tempname(Node *n, Type *t);
+Node* temp(Type*);
/*
* init.c
*/
void fninit(NodeList *n);
-Node* renameinit(Node *n);
+Sym* renameinit(void);
+
+/*
+ * inl.c
+ */
+void caninl(Node *fn);
+void inlcalls(Node *fn);
+void typecheckinl(Node *fn);
/*
* lex.c
@@ -950,6 +1013,7 @@ Node* renameinit(Node *n);
void cannedimports(char *file, char *cp);
void importfile(Val *f, int line);
char* lexname(int lex);
+char* expstring(void);
void mkpackage(char* pkgname);
void unimportfile(void);
int32 yylex(void);
@@ -987,7 +1051,7 @@ void mpsubfltflt(Mpflt *a, Mpflt *b);
/*
* mparith2.c
*/
-void mpaddfixfix(Mpint *a, Mpint *b);
+void mpaddfixfix(Mpint *a, Mpint *b, int);
void mpandfixfix(Mpint *a, Mpint *b);
void mpandnotfixfix(Mpint *a, Mpint *b);
void mpdivfract(Mpint *a, Mpint *b);
@@ -1032,10 +1096,9 @@ void ieeedtod(uint64 *ieee, double native);
Sym* stringsym(char*, int);
/*
- * print.c
+ * order.c
*/
-void exprfmt(Fmt *f, Node *n, int prec);
-void exprlistfmt(Fmt *f, NodeList *l);
+void order(Node *fn);
/*
* range.c
@@ -1050,6 +1113,8 @@ void dumptypestructs(void);
Type* methodfunc(Type *f, Type*);
Node* typename(Type *t);
Sym* typesym(Type *t);
+Sym* typesymprefix(char *prefix, Type *t);
+int haspointers(Type *t);
/*
* select.c
@@ -1069,19 +1134,12 @@ int stataddr(Node *nam, Node *n);
/*
* subr.c
*/
-int Econv(Fmt *fp);
-int Jconv(Fmt *fp);
-int Lconv(Fmt *fp);
-int Nconv(Fmt *fp);
-int Oconv(Fmt *fp);
-int Sconv(Fmt *fp);
-int Tconv(Fmt *fp);
-int Tpretty(Fmt *fp, Type *t);
-int Zconv(Fmt *fp);
Node* adddot(Node *n);
int adddot1(Sym *s, Type *t, int d, Type **save, int ignorecase);
+void addinit(Node**, NodeList*);
Type* aindex(Node *b, Type *t);
int algtype(Type *t);
+int algtype1(Type *t, Type **bad);
void argtype(Node *on, Type *t);
Node* assignconv(Node *n, Type *t, char *context);
int assignop(Type *src, Type *dst, char **why);
@@ -1090,20 +1148,21 @@ int brcom(int a);
int brrev(int a);
NodeList* concat(NodeList *a, NodeList *b);
int convertop(Type *src, Type *dst, char **why);
+Node* copyexpr(Node*, Type*, NodeList**);
int count(NodeList *l);
int cplxsubtype(int et);
-void dump(char *s, Node *n);
-void dumplist(char *s, NodeList *l);
int eqtype(Type *t1, Type *t2);
int eqtypenoname(Type *t1, Type *t2);
void errorexit(void);
-void expandmeth(Sym *s, Type *t);
+void expandmeth(Type *t);
void fatal(char *fmt, ...);
void flusherrors(void);
void frame(int context);
Type* funcfirst(Iter *s, Type *t);
Type* funcnext(Iter *s);
void genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface);
+void genhash(Sym *sym, Type *t);
+void geneq(Sym *sym, Type *t);
Type** getinarg(Type *t);
Type* getinargx(Type *t);
Type** getoutarg(Type *t);
@@ -1113,7 +1172,9 @@ Type* getthisx(Type *t);
int implements(Type *t, Type *iface, Type **missing, Type **have, int *ptr);
void importdot(Pkg *opkg, Node *pack);
int is64(Type *t);
+int isbadimport(Strlit *s);
int isblank(Node *n);
+int isblanksym(Sym *s);
int isfixedarray(Type *t);
int isideal(Type *t);
int isinter(Type *t);
@@ -1131,7 +1192,7 @@ NodeList* listtreecopy(NodeList *l);
Sym* lookup(char *name);
void* mal(int32 n);
Type* maptype(Type *key, Type *val);
-Type* methtype(Type *t);
+Type* methtype(Type *t, int mustname);
Pkg* mkpkg(Strlit *path);
Sym* ngotype(Node *n);
int noconv(Type *t1, Type *t2);
@@ -1170,6 +1231,7 @@ uint32 typehash(Type *t);
void ullmancalc(Node *n);
void umagic(Magic *m);
void warn(char *fmt, ...);
+void warnl(int line, char *fmt, ...);
void yyerror(char *fmt, ...);
void yyerrorl(int line, char *fmt, ...);
@@ -1182,7 +1244,6 @@ void walkswitch(Node *sw);
/*
* typecheck.c
*/
-int exportassignok(Type *t, char *desc);
int islvalue(Node *n);
Node* typecheck(Node **np, int top);
void typechecklist(NodeList *l, int top);
@@ -1195,6 +1256,7 @@ void queuemethod(Node *n);
/*
* unsafe.c
*/
+int isunsafebuiltin(Node *n);
Node* unsafenmagic(Node *n);
/*
@@ -1211,6 +1273,7 @@ void walkexprlist(NodeList *l, NodeList **init);
void walkexprlistsafe(NodeList *l, NodeList **init);
void walkstmt(Node **np);
void walkstmtlist(NodeList *l);
+Node* conv(Node*, Type*);
/*
* arch-specific ggen.c/gsubr.c/gobj.c/pgen.c
@@ -1274,6 +1337,30 @@ Prog* unpatch(Prog*);
void zfile(Biobuf *b, char *p, int n);
void zhist(Biobuf *b, int line, vlong offset);
void zname(Biobuf *b, Sym *s, int t);
-void data(void);
-void text(void);
+#pragma varargck type "A" int
+#pragma varargck type "B" Mpint*
+#pragma varargck type "D" Addr*
+#pragma varargck type "lD" Addr*
+#pragma varargck type "E" int
+#pragma varargck type "E" uint
+#pragma varargck type "F" Mpflt*
+#pragma varargck type "H" NodeList*
+#pragma varargck type "J" Node*
+#pragma varargck type "lL" int
+#pragma varargck type "lL" uint
+#pragma varargck type "L" int
+#pragma varargck type "L" uint
+#pragma varargck type "N" Node*
+#pragma varargck type "lN" Node*
+#pragma varargck type "O" uint
+#pragma varargck type "P" Prog*
+#pragma varargck type "Q" Bits
+#pragma varargck type "R" int
+#pragma varargck type "S" Sym*
+#pragma varargck type "lS" Sym*
+#pragma varargck type "T" Type*
+#pragma varargck type "lT" Type*
+#pragma varargck type "V" Val*
+#pragma varargck type "Y" char*
+#pragma varargck type "Z" Strlit*
diff --git a/src/cmd/gc/go.y b/src/cmd/gc/go.y
index 4c7fe6068..f95058721 100644
--- a/src/cmd/gc/go.y
+++ b/src/cmd/gc/go.y
@@ -18,7 +18,9 @@
*/
%{
+#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);
@@ -29,13 +31,13 @@ static void fixlbrace(int);
Type* type;
Sym* sym;
struct Val val;
- int lint;
+ int i;
}
// |sed 's/.* //' |9 fmt -l1 |sort |9 fmt -l50 | sed 's/^/%xxx /'
%token <val> LLITERAL
-%token <lint> LASOP
+%token <i> LASOP
%token <sym> LBREAK LCASE LCHAN LCOLAS LCONST LCONTINUE LDDD
%token <sym> LDEFAULT LDEFER LELSE LFALL LFOR LFUNC LGO LGOTO
%token <sym> LIF LIMPORT LINTERFACE LMAP LNAME
@@ -45,7 +47,7 @@ static void fixlbrace(int);
%token LANDAND LANDNOT LBODY LCOMM LDEC LEQ LGE LGT
%token LIGNORE LINC LLE LLSH LLT LNE LOROR LRSH
-%type <lint> lbrace import_here
+%type <i> lbrace import_here
%type <sym> sym packname
%type <val> oliteral
@@ -54,8 +56,8 @@ static void fixlbrace(int);
%type <node> case caseblock
%type <node> compound_stmt dotname embed expr complitexpr
%type <node> expr_or_type
-%type <node> fndcl fnliteral
-%type <node> for_body for_header for_stmt if_header if_stmt non_dcl_stmt
+%type <node> fndcl hidden_fndcl fnliteral
+%type <node> for_body for_header for_stmt if_header if_stmt else non_dcl_stmt
%type <node> interfacedcl keyval labelname name
%type <node> name_or_type non_expr_type
%type <node> new_name dcl_name oexpr typedclname
@@ -64,7 +66,7 @@ static void fixlbrace(int);
%type <node> pseudocall range_stmt select_stmt
%type <node> simple_stmt
%type <node> switch_stmt uexpr
-%type <node> xfndcl typedcl
+%type <node> xfndcl typedcl start_complit
%type <list> xdcl fnbody fnres loop_body dcl_name_list
%type <list> new_name_list expr_list keyval_list braced_keyval_list expr_or_type_list xdcl_list
@@ -76,12 +78,10 @@ static void fixlbrace(int);
%type <node> indcl interfacetype structtype ptrtype
%type <node> recvchantype non_recvchantype othertype fnret_type fntype
-%type <val> hidden_tag
-
%type <sym> hidden_importsym hidden_pkg_importsym
-%type <node> hidden_constant hidden_literal hidden_dcl
-%type <node> hidden_interfacedcl hidden_structdcl hidden_opt_sym
+%type <node> hidden_constant hidden_literal hidden_funarg
+%type <node> hidden_interfacedcl hidden_structdcl
%type <list> hidden_funres
%type <list> ohidden_funres
@@ -205,7 +205,15 @@ import_stmt:
my->lastlineno = $1;
my->block = 1; // at top level
}
-
+| import_here import_there
+ {
+ // When an invalid import path is passed to importfile,
+ // it calls yyerror and then sets up a fake import with
+ // no package statement. This allows us to test more
+ // than one invalid import statement in a single file.
+ if(nerrors == 0)
+ fatal("phase error in import");
+ }
import_stmt_list:
import_stmt
@@ -235,17 +243,17 @@ import_here:
}
import_package:
- LPACKAGE sym import_safety ';'
+ LPACKAGE LNAME import_safety ';'
{
if(importpkg->name == nil) {
importpkg->name = $2->name;
pkglookup($2->name, nil)->npkg++;
} else if(strcmp(importpkg->name, $2->name) != 0)
- yyerror("conflicting names %s and %s for package %Z", importpkg->name, $2->name, importpkg->path);
+ yyerror("conflicting names %s and %s for package \"%Z\"", importpkg->name, $2->name, importpkg->path);
importpkg->direct = 1;
if(safemode && !curio.importsafe)
- yyerror("cannot import unsafe package %Z", importpkg->path);
+ yyerror("cannot import unsafe package \"%Z\"", importpkg->path);
}
import_safety:
@@ -418,18 +426,15 @@ simple_stmt:
| expr_list LCOLAS expr_list
{
if($3->n->op == OTYPESW) {
- Node *n;
-
- n = N;
+ $$ = nod(OTYPESW, N, $3->n->right);
if($3->next != nil)
yyerror("expr.(type) must be alone in list");
if($1->next != nil)
yyerror("argument count mismatch: %d = %d", count($1), 1);
- else if($1->n->op != ONAME && $1->n->op != OTYPE && $1->n->op != ONONAME)
- yyerror("invalid variable name %#N in type switch", $1->n);
+ else if(($1->n->op != ONAME && $1->n->op != OTYPE && $1->n->op != ONONAME) || isblank($1->n))
+ yyerror("invalid variable name %N in type switch", $1->n);
else
- n = $1->n;
- $$ = nod(OTYPESW, n, $3->n->right);
+ $$->left = dclname($1->n->sym); // it's a colas, so must not re-use an oldname.
break;
}
$$ = colas($1, $3);
@@ -448,7 +453,7 @@ simple_stmt:
case:
LCASE expr_or_type_list ':'
{
- Node *n;
+ Node *n, *nn;
// will be converted to OCASE
// right will point to next case
@@ -458,12 +463,13 @@ case:
$$->list = $2;
if(typesw != N && typesw->right != N && (n=typesw->right->left) != N) {
// type switch - declare variable
- n = newname(n->sym);
- n->used = 1; // TODO(rsc): better job here
- declare(n, dclcontext);
- $$->nname = n;
+ nn = newname(n->sym);
+ declare(nn, dclcontext);
+ $$->nname = nn;
+
+ // keep track of the instances for reporting unused
+ nn->defn = typesw->right;
}
- break;
}
| LCASE expr_or_type_list '=' expr ':'
{
@@ -494,16 +500,18 @@ case:
}
| LDEFAULT ':'
{
- Node *n;
+ Node *n, *nn;
markdcl();
$$ = nod(OXCASE, N, N);
if(typesw != N && typesw->right != N && (n=typesw->right->left) != N) {
// type switch - declare variable
- n = newname(n->sym);
- n->used = 1; // TODO(rsc): better job here
- declare(n, dclcontext);
- $$->nname = n;
+ nn = newname(n->sym);
+ declare(nn, dclcontext);
+ $$->nname = nn;
+
+ // keep track of the instances for reporting unused
+ nn->defn = typesw->right;
}
}
@@ -638,6 +646,7 @@ if_header:
$$->ntest = $3;
}
+/* IF cond body (ELSE IF cond body)* (ELSE block)? */
if_stmt:
LIF
{
@@ -650,9 +659,27 @@ if_stmt:
}
loop_body
{
+ $3->nbody = $5;
+ }
+ else
+ {
+ popdcl();
$$ = $3;
- $$->nbody = $5;
- // no popdcl; maybe there's an LELSE
+ if($7 != N)
+ $$->nelse = list1($7);
+ }
+
+else:
+ {
+ $$ = N;
+ }
+| LELSE if_stmt
+ {
+ $$ = $2;
+ }
+| LELSE compound_stmt
+ {
+ $$ = $2;
}
switch_stmt:
@@ -785,7 +812,14 @@ uexpr:
}
| '&' uexpr
{
- $$ = nod(OADDR, $2, N);
+ if($2->op == OCOMPLIT) {
+ // Special case for &T{...}: turn into (*T){...}.
+ $$ = $2;
+ $$->right = nod(OIND, $$->right, N);
+ $$->right->implicit = 1;
+ } else {
+ $$ = nod(OADDR, $2, N);
+ }
}
| '+' uexpr
{
@@ -874,29 +908,35 @@ pexpr_no_paren:
$$ = nod(OCALL, $1, N);
$$->list = list1($3);
}
-| comptype lbrace braced_keyval_list '}'
+| comptype lbrace start_complit braced_keyval_list '}'
{
- // composite expression
- $$ = nod(OCOMPLIT, N, $1);
- $$->list = $3;
-
+ $$ = $3;
+ $$->right = $1;
+ $$->list = $4;
fixlbrace($2);
}
-| pexpr_no_paren '{' braced_keyval_list '}'
+| pexpr_no_paren '{' start_complit braced_keyval_list '}'
{
- // composite expression
- $$ = nod(OCOMPLIT, N, $1);
- $$->list = $3;
+ $$ = $3;
+ $$->right = $1;
+ $$->list = $4;
}
-| '(' expr_or_type ')' '{' braced_keyval_list '}'
+| '(' expr_or_type ')' '{' start_complit braced_keyval_list '}'
{
yyerror("cannot parenthesize type in composite literal");
- // composite expression
- $$ = nod(OCOMPLIT, N, $2);
- $$->list = $5;
+ $$ = $5;
+ $$->right = $2;
+ $$->list = $6;
}
| fnliteral
+start_complit:
+ {
+ // composite expression.
+ // make node early so we get the right line number.
+ $$ = nod(OCOMPLIT, N, N);
+ }
+
keyval:
expr ':' complitexpr
{
@@ -905,10 +945,10 @@ keyval:
complitexpr:
expr
-| '{' braced_keyval_list '}'
+| '{' start_complit braced_keyval_list '}'
{
- $$ = nod(OCOMPLIT, N, N);
- $$->list = $2;
+ $$ = $2;
+ $$->list = $3;
}
pexpr:
@@ -955,7 +995,10 @@ lbrace:
new_name:
sym
{
- $$ = newname($1);
+ if($1 == S)
+ $$ = N;
+ else
+ $$ = newname($1);
}
dcl_name:
@@ -972,6 +1015,26 @@ onew_name:
sym:
LNAME
+ {
+ $$ = $1;
+ // during imports, unqualified non-exported identifiers are from builtinpkg
+ if(importpkg != nil && !exportname($1->name))
+ $$ = pkglookup($1->name, builtinpkg);
+ }
+| hidden_importsym
+| '?'
+ {
+ $$ = S;
+ }
+
+hidden_importsym:
+ '@' LLITERAL '.' LNAME
+ {
+ if($2.u.sval->len == 0)
+ $$ = pkglookup($4->name, importpkg);
+ else
+ $$ = pkglookup($4->name, mkpkg($2.u.sval));
+ }
name:
sym %prec NotParen
@@ -1144,38 +1207,43 @@ xfndcl:
}
fndcl:
- dcl_name '(' oarg_type_list_ocomma ')' fnres
+ sym '(' oarg_type_list_ocomma ')' fnres
{
- Node *n;
+ Node *t;
+ $$ = N;
$3 = checkarglist($3, 1);
- $$ = nod(ODCLFUNC, N, N);
- $$->nname = $1;
- n = nod(OTFUNC, N, N);
- n->list = $3;
- n->rlist = $5;
- if(strcmp($1->sym->name, "init") == 0) {
- $$->nname = renameinit($1);
+
+ if(strcmp($1->name, "init") == 0) {
+ $1 = renameinit();
if($3 != nil || $5 != nil)
yyerror("func init must have no arguments and no return values");
}
- if(strcmp(localpkg->name, "main") == 0 && strcmp($1->sym->name, "main") == 0) {
+ if(strcmp(localpkg->name, "main") == 0 && strcmp($1->name, "main") == 0) {
if($3 != nil || $5 != nil)
yyerror("func main must have no arguments and no return values");
}
- // TODO: check if nname already has an ntype
- $$->nname->ntype = n;
+
+ t = nod(OTFUNC, N, N);
+ t->list = $3;
+ t->rlist = $5;
+
+ $$ = nod(ODCLFUNC, N, N);
+ $$->nname = newname($1);
+ $$->nname->defn = $$;
+ $$->nname->ntype = t; // TODO: check if nname already has an ntype
+ declare($$->nname, PFUNC);
+
funchdr($$);
}
| '(' oarg_type_list_ocomma ')' sym '(' oarg_type_list_ocomma ')' fnres
{
Node *rcvr, *t;
- Node *name;
-
- name = newname($4);
+
+ $$ = N;
$2 = checkarglist($2, 0);
$6 = checkarglist($6, 1);
- $$ = N;
+
if($2 == nil) {
yyerror("method has no receiver");
break;
@@ -1192,16 +1260,60 @@ fndcl:
if(rcvr->right->op == OTPAREN || (rcvr->right->op == OIND && rcvr->right->left->op == OTPAREN))
yyerror("cannot parenthesize receiver type");
- $$ = nod(ODCLFUNC, N, N);
- $$->nname = methodname1(name, rcvr->right);
t = nod(OTFUNC, rcvr, N);
t->list = $6;
t->rlist = $8;
+
+ $$ = nod(ODCLFUNC, N, N);
+ $$->shortname = newname($4);
+ $$->nname = methodname1($$->shortname, rcvr->right);
+ $$->nname->defn = $$;
$$->nname->ntype = t;
- $$->shortname = name;
+ declare($$->nname, PFUNC);
+
funchdr($$);
}
+hidden_fndcl:
+ hidden_pkg_importsym '(' ohidden_funarg_list ')' ohidden_funres
+ {
+ Sym *s;
+ Type *t;
+
+ $$ = N;
+
+ s = $1;
+ t = functype(N, $3, $5);
+
+ importsym(s, ONAME);
+ if(s->def != N && s->def->op == ONAME) {
+ if(eqtype(t, s->def->type))
+ break;
+ yyerror("inconsistent definition for func %S during import\n\t%T\n\t%T", s, s->def->type, t);
+ }
+
+ $$ = newname(s);
+ $$->type = t;
+ declare($$, PFUNC);
+
+ funchdr($$);
+ }
+| '(' hidden_funarg_list ')' sym '(' ohidden_funarg_list ')' ohidden_funres
+ {
+ $$ = methodname1(newname($4), $2->n->right);
+ $$->type = functype($2->n, $6, $8);
+
+ checkwidth($$->type);
+ addmethod($4, $$->type, 0);
+ funchdr($$);
+
+ // inl.c's inlnode in on a dotmeth node expects to find the inlineable body as
+ // (dotmeth's type)->nname->inl, and dotmeth's type has been pulled
+ // out by typecheck's lookdot as this $$->ttype. So by providing
+ // this back link here we avoid special casing there.
+ $$->type->nname = $$;
+ }
+
fntype:
LFUNC '(' oarg_type_list_ocomma ')' fnres
{
@@ -1317,6 +1429,20 @@ structdcl:
{
NodeList *l;
+ Node *n;
+ l = $1;
+ if(l != nil && l->next == nil && l->n == nil) {
+ // ? symbol, during import
+ n = $2;
+ if(n->op == OIND)
+ n = n->left;
+ n = embedded(n->sym);
+ n->right = $2;
+ n->val = $3;
+ $$ = list1(n);
+ break;
+ }
+
for(l=$1; l; l=l->next) {
l->n = nod(ODCLFIELD, l->n, $2);
l->n->val = $3;
@@ -1472,19 +1598,6 @@ non_dcl_stmt:
| switch_stmt
| select_stmt
| if_stmt
- {
- popdcl();
- $$ = $1;
- }
-| if_stmt LELSE stmt
- {
- if($3->op != OIF && $3->op != OBLOCK)
- yyerror("missing { } after else");
-
- popdcl();
- $$ = $1;
- $$->nelse = list1($3);
- }
| labelname ':'
{
$1 = nod(OLABEL, $1, N);
@@ -1494,7 +1607,7 @@ non_dcl_stmt:
{
NodeList *l;
- $1->right = $4;
+ $1->defn = $4;
l = list1($1);
if($4)
l = list(l, $4);
@@ -1530,6 +1643,18 @@ non_dcl_stmt:
{
$$ = nod(ORETURN, N, N);
$$->list = $2;
+ if($$->list == nil && curfn != N) {
+ NodeList *l;
+
+ for(l=curfn->dcl; l; l=l->next) {
+ if(l->n->class == PPARAM)
+ continue;
+ if(l->n->class != PPARAMOUT)
+ break;
+ if(l->n->sym->def != l->n)
+ yyerror("%s is shadowed during return", l->n->sym->name);
+ }
+ }
}
stmt_list:
@@ -1668,27 +1793,16 @@ oliteral:
| LLITERAL
/*
- * import syntax from header of
- * an output package
+ * import syntax from package header
*/
hidden_import:
- LIMPORT sym LLITERAL ';'
+ LIMPORT LNAME LLITERAL ';'
{
- // Informational: record package name
- // associated with import path, for use in
- // human-readable messages.
- Pkg *p;
-
- p = mkpkg($3.u.sval);
- if(p->name == nil) {
- p->name = $2->name;
- pkglookup($2->name, nil)->npkg++;
- } else if(strcmp(p->name, $2->name) != 0)
- yyerror("conflicting names %s and %s for package %Z", p->name, $2->name, p->path);
+ importimport($2, $3.u.sval);
}
| LVAR hidden_pkg_importsym hidden_type ';'
{
- importvar($2, $3, PEXTERN);
+ importvar($2, $3);
}
| LCONST hidden_pkg_importsym '=' hidden_constant ';'
{
@@ -1702,17 +1816,28 @@ hidden_import:
{
importtype($2, $3);
}
-| LFUNC hidden_pkg_importsym '(' ohidden_funarg_list ')' ohidden_funres ';'
+| LFUNC hidden_fndcl fnbody ';'
{
- importvar($2, functype(N, $4, $6), PFUNC);
+ if($2 == N)
+ break;
+
+ $2->inl = $3;
+
+ funcbody($2);
+ importlist = list(importlist, $2);
+
+ if(debug['E']) {
+ print("import [%Z] func %lN \n", importpkg->path, $2);
+ if(debug['l'] > 2 && $2->inl)
+ print("inl body:%+H\n", $2->inl);
+ }
}
-| LFUNC '(' hidden_funarg_list ')' sym '(' ohidden_funarg_list ')' ohidden_funres ';'
+
+hidden_pkg_importsym:
+ hidden_importsym
{
- if($3->next != nil || $3->n->op != ODCLFIELD) {
- yyerror("bad receiver in method");
- YYERROR;
- }
- importmethod($5, functype($3->n, $7, $9));
+ $$ = $1;
+ structpkg = $$->pkg;
}
hidden_pkgtype:
@@ -1722,6 +1847,10 @@ hidden_pkgtype:
importsym($1, OTYPE);
}
+/*
+ * importing types
+ */
+
hidden_type:
hidden_type_misc
| hidden_type_recv_chan
@@ -1760,11 +1889,11 @@ hidden_type_misc:
}
| LSTRUCT '{' ohidden_structdcl_list '}'
{
- $$ = dostruct($3, TSTRUCT);
+ $$ = tostruct($3);
}
| LINTERFACE '{' ohidden_interfacedcl_list '}'
{
- $$ = dostruct($3, TINTER);
+ $$ = tointerface($3);
}
| '*' hidden_type
{
@@ -1803,61 +1932,45 @@ hidden_type_func:
$$ = functype(nil, $3, $5);
}
-hidden_opt_sym:
- sym
- {
- $$ = newname($1);
- }
-| '?'
- {
- $$ = N;
- }
-
-hidden_dcl:
- hidden_opt_sym hidden_type hidden_tag
+hidden_funarg:
+ sym hidden_type oliteral
{
- $$ = nod(ODCLFIELD, $1, typenod($2));
+ $$ = nod(ODCLFIELD, N, typenod($2));
+ if($1)
+ $$->left = newname($1);
$$->val = $3;
}
-| hidden_opt_sym LDDD hidden_type hidden_tag
+| sym LDDD hidden_type oliteral
{
Type *t;
-
+
t = typ(TARRAY);
t->bound = -1;
t->type = $3;
- $$ = nod(ODCLFIELD, $1, typenod(t));
+
+ $$ = nod(ODCLFIELD, N, typenod(t));
+ if($1)
+ $$->left = newname($1);
$$->isddd = 1;
$$->val = $4;
}
hidden_structdcl:
- sym hidden_type hidden_tag
- {
- $$ = nod(ODCLFIELD, newname($1), typenod($2));
- $$->val = $3;
- }
-| '?' hidden_type hidden_tag
+ sym hidden_type oliteral
{
Sym *s;
- s = $2->sym;
- if(s == S && isptr[$2->etype])
- s = $2->type->sym;
- if(s && s->pkg == builtinpkg)
- s = lookup(s->name);
- $$ = embedded(s);
- $$->right = typenod($2);
- $$->val = $3;
- }
-
-hidden_tag:
- {
- $$.ctype = CTxxx;
- }
-| ':' LLITERAL // extra colon avoids conflict with "" looking like beginning of "".typename
- {
- $$ = $2;
+ if($1 != S) {
+ $$ = nod(ODCLFIELD, newname($1), typenod($2));
+ $$->val = $3;
+ } else {
+ s = $2->sym;
+ if(s == S && isptr[$2->etype])
+ s = $2->type->sym;
+ $$ = embedded(s);
+ $$->right = typenod($2);
+ $$->val = $3;
+ }
}
hidden_interfacedcl:
@@ -1865,9 +1978,9 @@ hidden_interfacedcl:
{
$$ = nod(ODCLFIELD, newname($1), typenod(functype(fakethis(), $3, $5)));
}
-| hidden_importsym '(' ohidden_funarg_list ')' ohidden_funres
+| hidden_type
{
- $$ = nod(ODCLFIELD, newname($1), typenod(functype(fakethis(), $3, $5)));
+ $$ = nod(ODCLFIELD, N, typenod($1));
}
ohidden_funres:
@@ -1886,6 +1999,10 @@ hidden_funres:
$$ = list1(nod(ODCLFIELD, N, typenod($1)));
}
+/*
+ * importing constants
+ */
+
hidden_literal:
LLITERAL
{
@@ -1896,6 +2013,7 @@ hidden_literal:
$$ = nodlit($2);
switch($$->val.ctype){
case CTINT:
+ case CTRUNE:
mpnegfix($$->val.u.xval);
break;
case CTFLT:
@@ -1916,37 +2034,23 @@ hidden_constant:
hidden_literal
| '(' hidden_literal '+' hidden_literal ')'
{
+ if($2->val.ctype == CTRUNE && $4->val.ctype == CTINT) {
+ $$ = $2;
+ mpaddfixfix($2->val.u.xval, $4->val.u.xval, 0);
+ break;
+ }
$$ = nodcplxlit($2->val, $4->val);
}
-hidden_importsym:
- LLITERAL '.' sym
- {
- Pkg *p;
-
- if($1.u.sval->len == 0)
- p = importpkg;
- else
- p = mkpkg($1.u.sval);
- $$ = pkglookup($3->name, p);
- }
-
-hidden_pkg_importsym:
- hidden_importsym
- {
- $$ = $1;
- structpkg = $$->pkg;
- }
-
hidden_import_list:
| hidden_import_list hidden_import
hidden_funarg_list:
- hidden_dcl
+ hidden_funarg
{
$$ = list1($1);
}
-| hidden_funarg_list ',' hidden_dcl
+| hidden_funarg_list ',' hidden_funarg
{
$$ = list($1, $3);
}
diff --git a/src/cmd/gc/init.c b/src/cmd/gc/init.c
index 8818db08c..be402cc0c 100644
--- a/src/cmd/gc/init.c
+++ b/src/cmd/gc/init.c
@@ -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 <u.h>
+#include <libc.h>
#include "go.h"
/*
@@ -11,21 +13,13 @@
* package and also uncallable, the name,
* normally "pkg.init", is altered to "pkg.init·1".
*/
-Node*
-renameinit(Node *n)
+Sym*
+renameinit(void)
{
- Sym *s;
static int initgen;
- s = n->sym;
- if(s == S)
- return n;
- if(strcmp(s->name, "init") != 0)
- return n;
-
snprint(namebuf, sizeof(namebuf), "init·%d", ++initgen);
- s = lookup(namebuf);
- return newname(s);
+ return lookup(namebuf);
}
/*
@@ -123,7 +117,9 @@ fninit(NodeList *n)
fn = nod(ODCLFUNC, N, N);
initsym = lookup(namebuf);
fn->nname = newname(initsym);
+ fn->nname->defn = fn;
fn->nname->ntype = nod(OTFUNC, N, N);
+ declare(fn->nname, PFUNC);
funchdr(fn);
// (3)
diff --git a/src/cmd/gc/inl.c b/src/cmd/gc/inl.c
new file mode 100644
index 000000000..efce56057
--- /dev/null
+++ b/src/cmd/gc/inl.c
@@ -0,0 +1,842 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+//
+// The inlining facility makes 2 passes: first caninl determines which
+// functions are suitable for inlining, and for those that are it
+// saves a copy of the body. Then inlcalls walks each function body to
+// expand calls to inlinable functions.
+//
+// The debug['l'] flag controls the agressiveness. Note that main() swaps level 0 and 1,
+// making 1 the default and -l disable. -ll and more is useful to flush out bugs.
+// These additional levels (beyond -l) may be buggy and are not supported.
+// 0: disabled
+// 1: 40-nodes leaf functions, oneliners, lazy typechecking (default)
+// 2: early typechecking of all imported bodies
+// 3:
+// 4: allow non-leaf functions , (breaks runtime.Caller)
+// 5: transitive inlining
+//
+// At some point this may get another default and become switch-offable with -N.
+//
+// The debug['m'] flag enables diagnostic output. a single -m is useful for verifying
+// which calls get inlined or not, more is for debugging, and may go away at any point.
+//
+// TODO:
+// - inline functions with ... args
+// - handle T.meth(f()) with func f() (t T, arg, arg, )
+
+#include <u.h>
+#include <libc.h>
+#include "go.h"
+
+// Used by caninl.
+static Node* inlcopy(Node *n);
+static NodeList* inlcopylist(NodeList *ll);
+static int ishairy(Node *n, int *budget);
+static int ishairylist(NodeList *ll, int *budget);
+
+// Used by inlcalls
+static void inlnodelist(NodeList *l);
+static void inlnode(Node **np);
+static void mkinlcall(Node **np, Node *fn);
+static Node* inlvar(Node *n);
+static Node* retvar(Type *n, int i);
+static Node* newlabel(void);
+static Node* inlsubst(Node *n);
+static NodeList* inlsubstlist(NodeList *l);
+
+static void setlno(Node*, int);
+
+// Used during inlsubst[list]
+static Node *inlfn; // function currently being inlined
+static Node *inlretlabel; // target of the goto substituted in place of a return
+static NodeList *inlretvars; // temp out variables
+
+// Get the function's package. For ordinary functions it's on the ->sym, but for imported methods
+// the ->sym can be re-used in the local package, so peel it off the receiver's type.
+static Pkg*
+fnpkg(Node *fn)
+{
+ Type *rcvr;
+
+ if(fn->type->thistuple) {
+ // method
+ rcvr = getthisx(fn->type)->type->type;
+ if(isptr[rcvr->etype])
+ rcvr = rcvr->type;
+ if(!rcvr->sym)
+ fatal("receiver with no sym: [%S] %lN (%T)", fn->sym, fn, rcvr);
+ return rcvr->sym->pkg;
+ }
+ // non-method
+ return fn->sym->pkg;
+}
+
+// Lazy typechecking of imported bodies. For local functions, caninl will set ->typecheck
+// because they're a copy of an already checked body.
+void
+typecheckinl(Node *fn)
+{
+ Node *savefn;
+ Pkg *pkg;
+ int save_safemode, lno;
+
+ if(fn->typecheck)
+ return;
+
+ lno = setlineno(fn);
+
+ if (debug['m']>2)
+ print("typecheck import [%S] %lN { %#H }\n", fn->sym, fn, fn->inl);
+
+ // typecheckinl is only used for imported functions;
+ // their bodies may refer to unsafe as long as the package
+ // was marked safe during import (which was checked then).
+ pkg = fnpkg(fn);
+ if (pkg == localpkg || pkg == nil)
+ fatal("typecheckinl on local function %lN", fn);
+
+ save_safemode = safemode;
+ safemode = 0;
+
+ savefn = curfn;
+ curfn = fn;
+ typechecklist(fn->inl, Etop);
+ fn->typecheck = 1;
+ curfn = savefn;
+
+ safemode = save_safemode;
+
+ lineno = lno;
+}
+
+// Caninl determines whether fn is inlineable. Currently that means:
+// fn is exactly 1 statement, either a return or an assignment, and
+// some temporary constraints marked TODO. If fn is inlineable, saves
+// fn->nbody in fn->inl and substitutes it with a copy.
+void
+caninl(Node *fn)
+{
+ Node *savefn;
+ Type *t;
+ int budget;
+
+ if(fn->op != ODCLFUNC)
+ fatal("caninl %N", fn);
+ if(!fn->nname)
+ fatal("caninl no nname %+N", fn);
+
+ // If fn has no body (is defined outside of Go), cannot inline it.
+ if(fn->nbody == nil)
+ return;
+
+ // can't handle ... args yet
+ for(t=fn->type->type->down->down->type; t; t=t->down)
+ if(t->isddd)
+ return;
+
+ budget = 40; // allowed hairyness
+ if(ishairylist(fn->nbody, &budget))
+ return;
+
+ savefn = curfn;
+ curfn = fn;
+
+ fn->nname->inl = fn->nbody;
+ fn->nbody = inlcopylist(fn->nname->inl);
+ // nbody will have been typechecked, so we can set this:
+ fn->typecheck = 1;
+
+ // hack, TODO, check for better way to link method nodes back to the thing with the ->inl
+ // this is so export can find the body of a method
+ fn->type->nname = fn->nname;
+
+ if(debug['m'] > 1)
+ print("%L: can inline %#N as: %#T { %#H }\n", fn->lineno, fn->nname, fn->type, fn->nname->inl);
+ else if(debug['m'])
+ print("%L: can inline %N\n", fn->lineno, fn->nname);
+
+ curfn = savefn;
+}
+
+// Look for anything we want to punt on.
+static int
+ishairylist(NodeList *ll, int* budget)
+{
+ for(;ll;ll=ll->next)
+ if(ishairy(ll->n, budget))
+ return 1;
+ return 0;
+}
+
+static int
+ishairy(Node *n, int *budget)
+{
+ if(!n)
+ return 0;
+
+ // Things that are too hairy, irrespective of the budget
+ switch(n->op) {
+ case OCALL:
+ case OCALLFUNC:
+ case OCALLINTER:
+ case OCALLMETH:
+ case OPANIC:
+ case ORECOVER:
+ if(debug['l'] < 4)
+ return 1;
+ break;
+
+ case OCLOSURE:
+ case ORANGE:
+ case OFOR:
+ case OSELECT:
+ case OSWITCH:
+ case OPROC:
+ case ODEFER:
+ case ODCL: // declares locals as globals b/c of @"". qualification
+ case ODCLTYPE: // can't print yet
+ case ODCLCONST: // can't print yet
+ return 1;
+
+ break;
+ case OAS:
+ // x = <N> zero initializing assignments aren't representible in export yet.
+ // alternatively we may just skip them in printing and hope their DCL printed
+ // as a var will regenerate it
+ if(n->right == N)
+ return 1;
+ break;
+ }
+
+ (*budget)--;
+
+ return *budget < 0 ||
+ ishairy(n->left, budget) ||
+ ishairy(n->right, budget) ||
+ ishairylist(n->list, budget) ||
+ ishairylist(n->rlist, budget) ||
+ ishairylist(n->ninit, budget) ||
+ ishairy(n->ntest, budget) ||
+ ishairy(n->nincr, budget) ||
+ ishairylist(n->nbody, budget) ||
+ ishairylist(n->nelse, budget);
+}
+
+// Inlcopy and inlcopylist recursively copy the body of a function.
+// Any name-like node of non-local class is marked for re-export by adding it to
+// the exportlist.
+static NodeList*
+inlcopylist(NodeList *ll)
+{
+ NodeList *l;
+
+ l = nil;
+ for(; ll; ll=ll->next)
+ l = list(l, inlcopy(ll->n));
+ return l;
+}
+
+static Node*
+inlcopy(Node *n)
+{
+ Node *m;
+
+ if(n == N)
+ return N;
+
+ switch(n->op) {
+ case ONAME:
+ case OTYPE:
+ case OLITERAL:
+ return n;
+ }
+
+ m = nod(OXXX, N, N);
+ *m = *n;
+ m->inl = nil;
+ m->left = inlcopy(n->left);
+ m->right = inlcopy(n->right);
+ m->list = inlcopylist(n->list);
+ m->rlist = inlcopylist(n->rlist);
+ m->ninit = inlcopylist(n->ninit);
+ m->ntest = inlcopy(n->ntest);
+ m->nincr = inlcopy(n->nincr);
+ m->nbody = inlcopylist(n->nbody);
+ m->nelse = inlcopylist(n->nelse);
+
+ return m;
+}
+
+
+// Inlcalls/nodelist/node walks fn's statements and expressions and substitutes any
+// calls made to inlineable functions. This is the external entry point.
+void
+inlcalls(Node *fn)
+{
+ Node *savefn;
+
+ savefn = curfn;
+ curfn = fn;
+ inlnode(&fn);
+ if(fn != curfn)
+ fatal("inlnode replaced curfn");
+ curfn = savefn;
+}
+
+// Turn an OINLCALL into a statement.
+static void
+inlconv2stmt(Node *n)
+{
+ n->op = OBLOCK;
+ // n->ninit stays
+ n->list = n->nbody;
+ n->nbody = nil;
+ n->rlist = nil;
+}
+
+// Turn an OINLCALL into a single valued expression.
+static void
+inlconv2expr(Node **np)
+{
+ Node *n, *r;
+ n = *np;
+ r = n->rlist->n;
+ addinit(&r, concat(n->ninit, n->nbody));
+ *np = r;
+}
+
+// Turn the rlist (with the return values) of the OINLCALL in
+// n into an expression list lumping the ninit and body
+// containing the inlined statements on the first list element so
+// order will be preserved Used in return, oas2func and call
+// statements.
+static NodeList*
+inlconv2list(Node *n)
+{
+ NodeList *l;
+
+ if(n->op != OINLCALL || n->rlist == nil)
+ fatal("inlconv2list %+N\n", n);
+
+ l = n->rlist;
+ addinit(&l->n, concat(n->ninit, n->nbody));
+ return l;
+}
+
+static void
+inlnodelist(NodeList *l)
+{
+ for(; l; l=l->next)
+ inlnode(&l->n);
+}
+
+// inlnode recurses over the tree to find inlineable calls, which will
+// be turned into OINLCALLs by mkinlcall. When the recursion comes
+// back up will examine left, right, list, rlist, ninit, ntest, nincr,
+// nbody and nelse and use one of the 4 inlconv/glue functions above
+// to turn the OINLCALL into an expression, a statement, or patch it
+// in to this nodes list or rlist as appropriate.
+// NOTE it makes no sense to pass the glue functions down the
+// recursion to the level where the OINLCALL gets created because they
+// have to edit /this/ n, so you'd have to push that one down as well,
+// but then you may as well do it here. so this is cleaner and
+// shorter and less complicated.
+static void
+inlnode(Node **np)
+{
+ Node *n;
+ NodeList *l;
+ int lno;
+
+ if(*np == nil)
+ return;
+
+ n = *np;
+
+ switch(n->op) {
+ case ODEFER:
+ case OPROC:
+ // inhibit inlining of their argument
+ switch(n->left->op) {
+ case OCALLFUNC:
+ case OCALLMETH:
+ n->left->etype = n->op;
+ }
+
+ case OCLOSURE:
+ // TODO do them here instead of in lex.c phase 6b, so escape analysis
+ // can avoid more heapmoves.
+ return;
+ }
+
+ lno = setlineno(n);
+
+ inlnodelist(n->ninit);
+ for(l=n->ninit; l; l=l->next)
+ if(l->n->op == OINLCALL)
+ inlconv2stmt(l->n);
+
+ inlnode(&n->left);
+ if(n->left && n->left->op == OINLCALL)
+ inlconv2expr(&n->left);
+
+ inlnode(&n->right);
+ if(n->right && n->right->op == OINLCALL)
+ inlconv2expr(&n->right);
+
+ inlnodelist(n->list);
+ switch(n->op) {
+ case OBLOCK:
+ for(l=n->list; l; l=l->next)
+ if(l->n->op == OINLCALL)
+ inlconv2stmt(l->n);
+ break;
+
+ case ORETURN:
+ case OCALLFUNC:
+ case OCALLMETH:
+ case OCALLINTER:
+ // if we just replaced arg in f(arg()) or return arg with an inlined call
+ // and arg returns multiple values, glue as list
+ if(count(n->list) == 1 && n->list->n->op == OINLCALL && count(n->list->n->rlist) > 1) {
+ n->list = inlconv2list(n->list->n);
+ break;
+ }
+
+ // fallthrough
+ default:
+ for(l=n->list; l; l=l->next)
+ if(l->n->op == OINLCALL)
+ inlconv2expr(&l->n);
+ }
+
+ inlnodelist(n->rlist);
+ switch(n->op) {
+ case OAS2FUNC:
+ if(n->rlist->n->op == OINLCALL) {
+ n->rlist = inlconv2list(n->rlist->n);
+ n->op = OAS2;
+ n->typecheck = 0;
+ typecheck(np, Etop);
+ break;
+ }
+
+ // fallthrough
+ default:
+ for(l=n->rlist; l; l=l->next)
+ if(l->n->op == OINLCALL)
+ inlconv2expr(&l->n);
+
+ }
+
+ inlnode(&n->ntest);
+ if(n->ntest && n->ntest->op == OINLCALL)
+ inlconv2expr(&n->ntest);
+
+ inlnode(&n->nincr);
+ if(n->nincr && n->nincr->op == OINLCALL)
+ inlconv2stmt(n->nincr);
+
+ inlnodelist(n->nbody);
+ for(l=n->nbody; l; l=l->next)
+ if(l->n->op == OINLCALL)
+ inlconv2stmt(l->n);
+
+ inlnodelist(n->nelse);
+ for(l=n->nelse; l; l=l->next)
+ if(l->n->op == OINLCALL)
+ inlconv2stmt(l->n);
+
+ // with all the branches out of the way, it is now time to
+ // transmogrify this node itself unless inhibited by the
+ // switch at the top of this function.
+ switch(n->op) {
+ case OCALLFUNC:
+ case OCALLMETH:
+ if (n->etype == OPROC || n->etype == ODEFER)
+ return;
+ }
+
+ switch(n->op) {
+ case OCALLFUNC:
+ if(debug['m']>3)
+ print("%L:call to func %+N\n", n->lineno, n->left);
+ if(n->left->inl) // normal case
+ mkinlcall(np, n->left);
+ else if(n->left->op == ONAME && n->left->left && n->left->left->op == OTYPE && n->left->right && n->left->right->op == ONAME) // methods called as functions
+ if(n->left->sym->def)
+ mkinlcall(np, n->left->sym->def);
+ break;
+
+ case OCALLMETH:
+ if(debug['m']>3)
+ print("%L:call to meth %lN\n", n->lineno, n->left->right);
+ // typecheck should have resolved ODOTMETH->type, whose nname points to the actual function.
+ if(n->left->type == T)
+ fatal("no function type for [%p] %+N\n", n->left, n->left);
+
+ if(n->left->type->nname == N)
+ fatal("no function definition for [%p] %+T\n", n->left->type, n->left->type);
+
+ mkinlcall(np, n->left->type->nname);
+
+ break;
+ }
+
+ lineno = lno;
+}
+
+static void mkinlcall1(Node **np, Node *fn);
+
+static void
+mkinlcall(Node **np, Node *fn)
+{
+ int save_safemode;
+ Pkg *pkg;
+
+ save_safemode = safemode;
+
+ // imported functions may refer to unsafe as long as the
+ // package was marked safe during import (already checked).
+ pkg = fnpkg(fn);
+ if(pkg != localpkg && pkg != nil)
+ safemode = 0;
+ mkinlcall1(np, fn);
+ safemode = save_safemode;
+}
+// if *np is a call, and fn is a function with an inlinable body, substitute *np with an OINLCALL.
+// On return ninit has the parameter assignments, the nbody is the
+// inlined function body and list, rlist contain the input, output
+// parameters.
+static void
+mkinlcall1(Node **np, Node *fn)
+{
+ int i;
+ Node *n, *call, *saveinlfn, *as, *m;
+ NodeList *dcl, *ll, *ninit, *body;
+ Type *t;
+
+ if (fn->inl == nil)
+ return;
+
+ if (fn == curfn || fn->defn == curfn)
+ return;
+
+ if(debug['l']<2)
+ typecheckinl(fn);
+
+ n = *np;
+
+ // Bingo, we have a function node, and it has an inlineable body
+ if(debug['m']>1)
+ print("%L: inlining call to %S %#T { %#H }\n", n->lineno, fn->sym, fn->type, fn->inl);
+ else if(debug['m'])
+ print("%L: inlining call to %N\n", n->lineno, fn);
+
+ if(debug['m']>2)
+ print("%L: Before inlining: %+N\n", n->lineno, n);
+
+ saveinlfn = inlfn;
+ inlfn = fn;
+
+ ninit = n->ninit;
+
+ if (fn->defn) // local function
+ dcl = fn->defn->dcl;
+ else // imported function
+ dcl = fn->dcl;
+
+ inlretvars = nil;
+ i = 0;
+ // Make temp names to use instead of the originals
+ for(ll = dcl; ll; ll=ll->next)
+ if(ll->n->op == ONAME) {
+ ll->n->inlvar = inlvar(ll->n);
+ ninit = list(ninit, nod(ODCL, ll->n->inlvar, N)); // otherwise gen won't emit the allocations for heapallocs
+ if (ll->n->class == PPARAMOUT) // we rely on the order being correct here
+ inlretvars = list(inlretvars, ll->n->inlvar);
+ }
+
+ // anonymous return values, synthesize names for use in assignment that replaces return
+ if(inlretvars == nil && fn->type->outtuple > 0)
+ for(t = getoutargx(fn->type)->type; t; t = t->down) {
+ m = retvar(t, i++);
+ ninit = list(ninit, nod(ODCL, m, N));
+ inlretvars = list(inlretvars, m);
+ }
+
+ // assign arguments to the parameters' temp names
+ as = N;
+ if(fn->type->thistuple) {
+ t = getthisx(fn->type)->type;
+ if(t != T && t->nname != N && !isblank(t->nname) && !t->nname->inlvar)
+ fatal("missing inlvar for %N\n", t->nname);
+
+ if(n->left->op == ODOTMETH) {
+ if(!n->left->left)
+ fatal("method call without receiver: %+N", n);
+ if(t == T)
+ fatal("method call unknown receiver type: %+N", n);
+ if(t->nname != N && !isblank(t->nname))
+ as = nod(OAS, t->nname->inlvar, n->left->left);
+ else
+ as = nod(OAS, temp(t->type), n->left->left);
+ } else { // non-method call to method
+ if (!n->list)
+ fatal("non-method call to method without first arg: %+N", n);
+ if(t != T && t->nname != N && !isblank(t->nname))
+ as = nod(OAS, t->nname->inlvar, n->list->n);
+ }
+
+ if(as != N) {
+ typecheck(&as, Etop);
+ ninit = list(ninit, as);
+ }
+ }
+
+ as = nod(OAS2, N, N);
+ if(fn->type->intuple > 1 && n->list && !n->list->next) {
+ // TODO check that n->list->n is a call?
+ // TODO: non-method call to T.meth(f()) where f returns t, args...
+ as->rlist = n->list;
+ for(t = getinargx(fn->type)->type; t; t=t->down) {
+ if(t->nname && !isblank(t->nname)) {
+ if(!t->nname->inlvar)
+ fatal("missing inlvar for %N\n", t->nname);
+ as->list = list(as->list, t->nname->inlvar);
+ } else {
+ as->list = list(as->list, temp(t->type));
+ }
+ }
+ } else {
+ ll = n->list;
+ if(fn->type->thistuple && n->left->op != ODOTMETH) // non method call to method
+ ll=ll->next; // was handled above in if(thistuple)
+
+ for(t = getinargx(fn->type)->type; t && ll; t=t->down) {
+ if(t->nname && !isblank(t->nname)) {
+ if(!t->nname->inlvar)
+ fatal("missing inlvar for %N\n", t->nname);
+ as->list = list(as->list, t->nname->inlvar);
+ as->rlist = list(as->rlist, ll->n);
+ }
+ ll=ll->next;
+ }
+ if(ll || t)
+ fatal("arg count mismatch: %#T vs %,H\n", getinargx(fn->type), n->list);
+ }
+
+ if (as->rlist) {
+ typecheck(&as, Etop);
+ ninit = list(ninit, as);
+ }
+
+ // zero the outparams
+ for(ll = inlretvars; ll; ll=ll->next) {
+ as = nod(OAS, ll->n, N);
+ typecheck(&as, Etop);
+ ninit = list(ninit, as);
+ }
+
+ inlretlabel = newlabel();
+ body = inlsubstlist(fn->inl);
+
+ body = list(body, nod(OGOTO, inlretlabel, N)); // avoid 'not used' when function doesnt have return
+ body = list(body, nod(OLABEL, inlretlabel, N));
+
+ typechecklist(body, Etop);
+
+ call = nod(OINLCALL, N, N);
+ call->ninit = ninit;
+ call->nbody = body;
+ call->rlist = inlretvars;
+ call->type = n->type;
+ call->typecheck = 1;
+
+ setlno(call, n->lineno);
+
+ *np = call;
+
+ inlfn = saveinlfn;
+
+ // transitive inlining
+ // TODO do this pre-expansion on fn->inl directly. requires
+ // either supporting exporting statemetns with complex ninits
+ // or saving inl and making inlinl
+ if(debug['l'] >= 5) {
+ body = fn->inl;
+ fn->inl = nil; // prevent infinite recursion
+ inlnodelist(call->nbody);
+ for(ll=call->nbody; ll; ll=ll->next)
+ if(ll->n->op == OINLCALL)
+ inlconv2stmt(ll->n);
+ fn->inl = body;
+ }
+
+ if(debug['m']>2)
+ print("%L: After inlining %+N\n\n", n->lineno, *np);
+
+}
+
+// Every time we expand a function we generate a new set of tmpnames,
+// PAUTO's in the calling functions, and link them off of the
+// PPARAM's, PAUTOS and PPARAMOUTs of the called function.
+static Node*
+inlvar(Node *var)
+{
+ Node *n;
+
+ if(debug['m']>3)
+ print("inlvar %+N\n", var);
+
+ n = newname(var->sym);
+ n->type = var->type;
+ n->class = PAUTO;
+ n->used = 1;
+ n->curfn = curfn; // the calling function, not the called one
+ curfn->dcl = list(curfn->dcl, n);
+ return n;
+}
+
+// Synthesize a variable to store the inlined function's results in.
+static Node*
+retvar(Type *t, int i)
+{
+ Node *n;
+
+ snprint(namebuf, sizeof(namebuf), ".r%d", i);
+ n = newname(lookup(namebuf));
+ n->type = t->type;
+ n->class = PAUTO;
+ n->used = 1;
+ n->curfn = curfn; // the calling function, not the called one
+ curfn->dcl = list(curfn->dcl, n);
+ return n;
+}
+
+static Node*
+newlabel(void)
+{
+ Node *n;
+ static int label;
+
+ label++;
+ snprint(namebuf, sizeof(namebuf), ".inlret%.6d", label);
+ n = newname(lookup(namebuf));
+ n->etype = 1; // flag 'safe' for escape analysis (no backjumps)
+ return n;
+}
+
+// inlsubst and inlsubstlist recursively copy the body of the saved
+// pristine ->inl body of the function while substituting references
+// to input/output parameters with ones to the tmpnames, and
+// substituting returns with assignments to the output.
+static NodeList*
+inlsubstlist(NodeList *ll)
+{
+ NodeList *l;
+
+ l = nil;
+ for(; ll; ll=ll->next)
+ l = list(l, inlsubst(ll->n));
+ return l;
+}
+
+static Node*
+inlsubst(Node *n)
+{
+ Node *m, *as;
+ NodeList *ll;
+
+ if(n == N)
+ return N;
+
+ switch(n->op) {
+ case ONAME:
+ if(n->inlvar) { // These will be set during inlnode
+ if (debug['m']>2)
+ print ("substituting name %+N -> %+N\n", n, n->inlvar);
+ return n->inlvar;
+ }
+ if (debug['m']>2)
+ print ("not substituting name %+N\n", n);
+ return n;
+
+ case OLITERAL:
+ case OTYPE:
+ return n;
+
+ case ORETURN:
+ // Since we don't handle bodies with closures, this return is guaranteed to belong to the current inlined function.
+
+// dump("Return before substitution", n);
+ m = nod(OGOTO, inlretlabel, N);
+ m->ninit = inlsubstlist(n->ninit);
+
+ if(inlretvars && n->list) {
+ as = nod(OAS2, N, N);
+ // shallow copy or OINLCALL->rlist will be the same list, and later walk and typecheck may clobber that.
+ for(ll=inlretvars; ll; ll=ll->next)
+ as->list = list(as->list, ll->n);
+ as->rlist = inlsubstlist(n->list);
+ typecheck(&as, Etop);
+ m->ninit = list(m->ninit, as);
+ }
+
+ typechecklist(m->ninit, Etop);
+ typecheck(&m, Etop);
+// dump("Return after substitution", m);
+ return m;
+ }
+
+
+ m = nod(OXXX, N, N);
+ *m = *n;
+ m->ninit = nil;
+
+ if(n->op == OCLOSURE)
+ fatal("cannot inline function containing closure: %+N", n);
+
+ m->left = inlsubst(n->left);
+ m->right = inlsubst(n->right);
+ m->list = inlsubstlist(n->list);
+ m->rlist = inlsubstlist(n->rlist);
+ m->ninit = concat(m->ninit, inlsubstlist(n->ninit));
+ m->ntest = inlsubst(n->ntest);
+ m->nincr = inlsubst(n->nincr);
+ m->nbody = inlsubstlist(n->nbody);
+ m->nelse = inlsubstlist(n->nelse);
+
+ return m;
+}
+
+// Plaster over linenumbers
+static void
+setlnolist(NodeList *ll, int lno)
+{
+ for(;ll;ll=ll->next)
+ setlno(ll->n, lno);
+}
+
+static void
+setlno(Node *n, int lno)
+{
+ if(!n)
+ return;
+
+ // don't clobber names, unless they're freshly synthesized
+ if(n->op != ONAME || n->lineno == 0)
+ n->lineno = lno;
+
+ setlno(n->left, lno);
+ setlno(n->right, lno);
+ setlnolist(n->list, lno);
+ setlnolist(n->rlist, lno);
+ setlnolist(n->ninit, lno);
+ setlno(n->ntest, lno);
+ setlno(n->nincr, lno);
+ setlnolist(n->nbody, lno);
+ setlnolist(n->nelse, lno);
+}
diff --git a/src/cmd/gc/lex.c b/src/cmd/gc/lex.c
index 29b6d27ff..e71fd3848 100644
--- a/src/cmd/gc/lex.c
+++ b/src/cmd/gc/lex.c
@@ -2,7 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#define EXTERN
+#include <u.h>
+#include <libc.h>
#include "go.h"
#include "y.tab.h"
#include <ar.h>
@@ -18,6 +19,7 @@ int yyprev;
int yylast;
static void lexinit(void);
+static void lexinit1(void);
static void lexfini(void);
static void yytinit(void);
static int getc(void);
@@ -28,6 +30,61 @@ static void addidir(char*);
static int getlinepragma(void);
static char *goos, *goarch, *goroot;
+// Compiler experiments.
+// These are controlled by the GOEXPERIMENT environment
+// variable recorded when the compiler is built.
+static struct {
+ char *name;
+ int *val;
+} exper[] = {
+// {"rune32", &rune32},
+ {nil, nil},
+};
+
+static void
+addexp(char *s)
+{
+ int i;
+
+ for(i=0; exper[i].name != nil; i++) {
+ if(strcmp(exper[i].name, s) == 0) {
+ *exper[i].val = 1;
+ return;
+ }
+ }
+
+ print("unknown experiment %s\n", s);
+ exits("unknown experiment");
+}
+
+static void
+setexp(void)
+{
+ char *f[20];
+ int i, nf;
+
+ // The makefile #defines GOEXPERIMENT for us.
+ nf = getfields(GOEXPERIMENT, f, nelem(f), 1, ",");
+ for(i=0; i<nf; i++)
+ addexp(f[i]);
+}
+
+char*
+expstring(void)
+{
+ int i;
+ static char buf[512];
+
+ strcpy(buf, "X");
+ for(i=0; exper[i].name != nil; i++)
+ if(*exper[i].val)
+ seprint(buf+strlen(buf), buf+sizeof buf, ",%s", exper[i].name);
+ if(strlen(buf) == 1)
+ strcpy(buf, "X,none");
+ buf[1] = ':';
+ return buf;
+}
+
// Our own isdigit, isspace, isalpha, isalnum that take care
// of EOF and other out of range arguments.
static int
@@ -64,7 +121,7 @@ yy_isalnum(int c)
#define isalpha use_yy_isalpha_instead_of_isalpha
#define isalnum use_yy_isalnum_instead_of_isalnum
-#define DBG if(!debug['x']);else print
+#define DBG if(!debug['x']){}else print
enum
{
EOF = -1,
@@ -75,24 +132,48 @@ usage(void)
{
print("gc: usage: %cg [flags] file.go...\n", thechar);
print("flags:\n");
- // -A is allow use of "any" type, for bootstrapping
+ // -A allow use of "any" type, for bootstrapping
+ // -B disable bounds checking
+ // -E print imported declarations
+ // -K warn when lineno is zero
+ // -M print arguments to gmove
+ // -P print peephole diagnostics
+ // -R print optimizer diagnostics
+ // -g print code generation diagnostics
+ // -i print line history
+ // -j print variables to be initialized at runtime
+ // -r print generated helper functions
+ // -s print redundant types in composite literals
+ // -v print more information with -P or -R
+ // -y print declarations in cannedimports (used with -d)
+ // -% print non-static initializers
+ // -+ indicate that the runtime is being compiled
+ print(" -D PATH interpret local imports relative to this import path\n");
print(" -I DIR search for packages in DIR\n");
+ print(" -L show full path in file:line prints\n");
+ print(" -N disable optimizations\n");
+ print(" -S print the assembly language\n");
+ print(" -V print the compiler version\n");
+ print(" -W print the parse tree after typing\n");
print(" -d print declarations\n");
print(" -e no limit on number of errors printed\n");
print(" -f print stack frame structure\n");
print(" -h panic on an error\n");
+ print(" -l disable inlining\n");
+ print(" -m print optimization decisions\n");
print(" -o file specify output file\n");
- print(" -S print the assembly language\n");
- print(" -V print the compiler version\n");
+ print(" -p assumed import path for this code\n");
print(" -u disable package unsafe\n");
- print(" -w print the parse tree after typing\n");
+ print(" -w print type checking details\n");
print(" -x print lex tokens\n");
- exit(0);
+ exits("usage");
}
void
fault(int s)
{
+ USED(s);
+
// If we've already complained about things
// in the program, don't bother complaining
// about the seg fault too; let the user clean up
@@ -106,11 +187,13 @@ int
main(int argc, char *argv[])
{
int i, c;
- NodeList *l;
+ NodeList *l, *batch;
char *p;
-
+
+#ifdef SIGBUS
signal(SIGBUS, fault);
signal(SIGSEGV, fault);
+#endif
localpkg = mkpkg(strlit(""));
localpkg->prefix = "\"\"";
@@ -127,12 +210,18 @@ main(int argc, char *argv[])
typepkg = mkpkg(strlit("type"));
typepkg->name = "type";
+ weaktypepkg = mkpkg(strlit("weak.type"));
+ weaktypepkg->name = "weak.type";
+ weaktypepkg->prefix = "weak.type"; // not weak%2etype
+
unsafepkg = mkpkg(strlit("unsafe"));
unsafepkg->name = "unsafe";
goroot = getgoroot();
goos = getgoos();
goarch = thestring;
+
+ setexp();
outfile = nil;
ARGBEGIN {
@@ -145,19 +234,37 @@ main(int argc, char *argv[])
case 'o':
outfile = EARGF(usage());
break;
-
- case 'I':
- addidir(EARGF(usage()));
- break;
+ case 'p':
+ myimportpath = EARGF(usage());
+ break;
+
case 'u':
safemode = 1;
break;
+ case 'D':
+ localimport = EARGF(usage());
+ break;
+
+ case 'I':
+ addidir(EARGF(usage()));
+ break;
+
case 'V':
- print("%cg version %s\n", thechar, getgoversion());
- exit(0);
+ p = expstring();
+ if(strcmp(p, "X:none") == 0)
+ p = "";
+ print("%cg version %s%s%s\n", thechar, getgoversion(), *p ? " " : "", p);
+ exits(0);
} ARGEND
+
+ // enable inlining. for now:
+ // default: inlining on. (debug['l'] == 1)
+ // -l: inlining off (debug['l'] == 0)
+ // -ll, -lll: inlining on again, with extra debugging (debug['l'] > 1)
+ if(debug['l'] <= 1)
+ debug['l'] = 1 - debug['l'];
if(argc < 1)
usage();
@@ -179,23 +286,14 @@ main(int argc, char *argv[])
*p = '/';
}
- fmtinstall('O', Oconv); // node opcodes
- fmtinstall('E', Econv); // etype opcodes
- fmtinstall('J', Jconv); // all the node flags
- fmtinstall('S', Sconv); // sym pointer
- fmtinstall('T', Tconv); // type pointer
- fmtinstall('N', Nconv); // node pointer
- fmtinstall('Z', Zconv); // escaped string
- fmtinstall('L', Lconv); // line number
- fmtinstall('B', Bconv); // big numbers
- fmtinstall('F', Fconv); // big float numbers
-
+ fmtinstallgo();
betypeinit();
if(widthptr == 0)
fatal("betypeinit failed");
lexinit();
typeinit();
+ lexinit1();
yytinit();
blockgen = 1;
@@ -236,24 +334,24 @@ main(int argc, char *argv[])
if(debug['f'])
frame(1);
- // Process top-level declarations in four phases.
+ // Process top-level declarations in phases.
// Phase 1: const, type, and names and types of funcs.
// This will gather all the information about types
// and methods but doesn't depend on any of it.
- // Phase 2: Variable assignments.
- // To check interface assignments, depends on phase 1.
- // Phase 3: Type check function bodies.
- // Phase 4: Compile function bodies.
defercheckwidth();
for(l=xtop; l; l=l->next)
if(l->n->op != ODCL && l->n->op != OAS)
typecheck(&l->n, Etop);
+
+ // Phase 2: Variable assignments.
+ // To check interface assignments, depends on phase 1.
for(l=xtop; l; l=l->next)
if(l->n->op == ODCL || l->n->op == OAS)
typecheck(&l->n, Etop);
resumetypecopy();
resumecheckwidth();
+ // Phase 3: Type check function bodies.
for(l=xtop; l; l=l->next) {
if(l->n->op == ODCLFUNC || l->n->op == OCLOSURE) {
curfn = l->n;
@@ -269,6 +367,37 @@ main(int argc, char *argv[])
if(nsavederrors+nerrors)
errorexit();
+ // Phase 4: Inlining
+ if (debug['l'] > 1) {
+ // Typecheck imported function bodies if debug['l'] > 1,
+ // otherwise lazily when used or re-exported.
+ for(l=importlist; l; l=l->next)
+ if (l->n->inl) {
+ saveerrors();
+ typecheckinl(l->n);
+ }
+
+ if(nsavederrors+nerrors)
+ errorexit();
+ }
+
+ if (debug['l']) {
+ // Find functions that can be inlined and clone them before walk expands them.
+ for(l=xtop; l; l=l->next)
+ if(l->n->op == ODCLFUNC)
+ caninl(l->n);
+
+ // Expand inlineable calls in all functions
+ for(l=xtop; l; l=l->next)
+ if(l->n->op == ODCLFUNC)
+ inlcalls(l->n);
+ }
+
+ // Phase 5: escape analysis.
+ if(!debug['N'])
+ escapes(xtop);
+
+ // Phase 6: Compile top level functions.
for(l=xtop; l; l=l->next)
if(l->n->op == ODCLFUNC)
funccompile(l->n, 0);
@@ -276,14 +405,21 @@ main(int argc, char *argv[])
if(nsavederrors+nerrors == 0)
fninit(xtop);
+ // Phase 6b: Compile all closures.
+ // Can generate more closures, so run in batches.
while(closures) {
- l = closures;
+ batch = closures;
closures = nil;
- for(; l; l=l->next) {
+ if(debug['l'])
+ for(l=batch; l; l=l->next)
+ inlcalls(l->n);
+ if(!debug['N'])
+ escapes(batch);
+ for(l=batch; l; l=l->next)
funccompile(l->n, 1);
- }
}
+ // Phase 7: check external declarations.
for(l=externdcl; l; l=l->next)
if(l->n->op == ONAME)
typecheck(&l->n, Erv);
@@ -297,7 +433,7 @@ main(int argc, char *argv[])
errorexit();
flusherrors();
- exit(0);
+ exits(0);
return 0;
}
@@ -308,18 +444,30 @@ saveerrors(void)
nerrors = 0;
}
+/*
+ * macro to portably read/write archive header.
+ * 'cmd' is read/write/Bread/Bwrite, etc.
+ */
+#define HEADER_IO(cmd, f, h) cmd(f, h.name, sizeof(h.name)) != sizeof(h.name)\
+ || cmd(f, h.date, sizeof(h.date)) != sizeof(h.date)\
+ || cmd(f, h.uid, sizeof(h.uid)) != sizeof(h.uid)\
+ || cmd(f, h.gid, sizeof(h.gid)) != sizeof(h.gid)\
+ || cmd(f, h.mode, sizeof(h.mode)) != sizeof(h.mode)\
+ || cmd(f, h.size, sizeof(h.size)) != sizeof(h.size)\
+ || cmd(f, h.fmag, sizeof(h.fmag)) != sizeof(h.fmag)
+
static int
arsize(Biobuf *b, char *name)
{
- struct ar_hdr *a;
+ struct ar_hdr a;
- if((a = Brdline(b, '\n')) == nil)
+ if (HEADER_IO(Bread, b, a))
return -1;
- if(Blinelen(b) != sizeof(struct ar_hdr))
- return -1;
- if(strncmp(a->name, name, strlen(name)) != 0)
+
+ if(strncmp(a.name, name, strlen(name)) != 0)
return -1;
- return atoi(a->size);
+
+ return atoi(a.size);
}
static int
@@ -366,15 +514,19 @@ addidir(char* dir)
static int
islocalname(Strlit *name)
{
- if(!windows && name->len >= 1 && name->s[0] == '/')
+ if(name->len >= 1 && name->s[0] == '/')
return 1;
if(windows && name->len >= 3 &&
yy_isalpha(name->s[0]) && name->s[1] == ':' && name->s[2] == '/')
return 1;
if(name->len >= 2 && strncmp(name->s, "./", 2) == 0)
return 1;
+ if(name->len == 1 && strncmp(name->s, ".", 1) == 0)
+ return 1;
if(name->len >= 3 && strncmp(name->s, "../", 3) == 0)
return 1;
+ if(name->len == 2 && strncmp(name->s, "..", 2) == 0)
+ return 1;
return 0;
}
@@ -400,8 +552,8 @@ findpkg(Strlit *name)
}
// local imports should be canonicalized already.
- // don't want to see "container/../container/vector"
- // as different from "container/vector".
+ // don't want to see "encoding/../encoding/base64"
+ // as different from "encoding/base64".
q = mal(name->len+1);
memmove(q, name->s, name->len);
q[name->len] = '\0';
@@ -430,6 +582,13 @@ findpkg(Strlit *name)
return 0;
}
+static void
+fakeimport(void)
+{
+ importpkg = mkpkg(strlit("fake"));
+ cannedimports("fake.6", "$$\n");
+}
+
void
importfile(Val *f, int line)
{
@@ -438,18 +597,27 @@ importfile(Val *f, int line)
int32 c;
int len;
Strlit *path;
- char *cleanbuf;
+ char *cleanbuf, *prefix;
+
+ USED(line);
// TODO(rsc): don't bother reloading imports more than once?
if(f->ctype != CTSTR) {
yyerror("import statement not a string");
+ fakeimport();
return;
}
- if(strlen(f->u.sval->s) != f->u.sval->len) {
- yyerror("import path contains NUL");
- errorexit();
+ if(f->u.sval->len == 0) {
+ yyerror("import path is empty");
+ fakeimport();
+ return;
+ }
+
+ if(isbadimport(f->u.sval)) {
+ fakeimport();
+ return;
}
// The package name main is no longer reserved,
@@ -461,6 +629,11 @@ importfile(Val *f, int line)
errorexit();
}
+ if(myimportpath != nil && strcmp(f->u.sval->s, myimportpath) == 0) {
+ yyerror("import \"%Z\" while compiling that package (import cycle)", f->u.sval);
+ errorexit();
+ }
+
if(strcmp(f->u.sval->s, "unsafe") == 0) {
if(safemode) {
yyerror("cannot import package unsafe");
@@ -473,8 +646,16 @@ importfile(Val *f, int line)
path = f->u.sval;
if(islocalname(path)) {
- cleanbuf = mal(strlen(pathname) + strlen(path->s) + 2);
- strcpy(cleanbuf, pathname);
+ if(path->s[0] == '/') {
+ yyerror("import path cannot be absolute path");
+ fakeimport();
+ return;
+ }
+ prefix = pathname;
+ if(localimport != nil)
+ prefix = localimport;
+ cleanbuf = mal(strlen(prefix) + strlen(path->s) + 2);
+ strcpy(cleanbuf, prefix);
strcat(cleanbuf, "/");
strcat(cleanbuf, path->s);
cleanname(cleanbuf);
@@ -482,14 +663,14 @@ importfile(Val *f, int line)
}
if(!findpkg(path)) {
- yyerror("can't find import: %Z", f->u.sval);
+ yyerror("can't find import: \"%Z\"", f->u.sval);
errorexit();
}
importpkg = mkpkg(path);
imp = Bopen(namebuf, OREAD);
if(imp == nil) {
- yyerror("can't open import: %Z: %r", f->u.sval);
+ yyerror("can't open import: \"%Z\": %r", f->u.sval);
errorexit();
}
file = strdup(namebuf);
@@ -509,7 +690,7 @@ importfile(Val *f, int line)
yyerror("import %s: not a go object file", file);
errorexit();
}
- q = smprint("%s %s %s", getgoos(), thestring, getgoversion());
+ q = smprint("%s %s %s %s", getgoos(), thestring, getgoversion(), expstring());
if(strcmp(p+10, q) != 0) {
yyerror("import %s: object is [%s] expected [%s]", file, p+10, q);
errorexit();
@@ -546,7 +727,7 @@ importfile(Val *f, int line)
continue;
return;
}
- yyerror("no import in: %Z", f->u.sval);
+ yyerror("no import in \"%Z\"", f->u.sval);
unimportfile();
}
@@ -665,7 +846,6 @@ l0:
ep = lexbuf+sizeof lexbuf;
*cp++ = c;
c = c1;
- c1 = 0;
goto casedot;
}
if(c1 == '.') {
@@ -717,6 +897,8 @@ l0:
ncp += ncp;
}
c = getr();
+ if(c == '\r')
+ continue;
if(c == EOF) {
yyerror("eof in string");
break;
@@ -750,7 +932,7 @@ l0:
}
yylval.val.u.xval = mal(sizeof(*yylval.val.u.xval));
mpmovecfix(yylval.val.u.xval, v);
- yylval.val.ctype = CTINT;
+ yylval.val.ctype = CTRUNE;
DBG("lex: codepoint literal\n");
strcpy(litbuf, "string literal");
return LLITERAL;
@@ -1008,7 +1190,7 @@ lx:
return c;
asop:
- yylval.lint = c; // rathole to hold which asop
+ yylval.i = c; // rathole to hold which asop
DBG("lex: TOKEN ASOP %c\n", c);
return LASOP;
@@ -1056,7 +1238,6 @@ talph:
return s->lexical;
tnum:
- c1 = 0;
cp = lexbuf;
ep = lexbuf+sizeof lexbuf;
if(c != '0') {
@@ -1247,7 +1428,7 @@ static int
getlinepragma(void)
{
int i, c, n;
- char *cp, *ep;
+ char *cp, *ep, *linep;
Hist *h;
for(i=0; i<5; i++) {
@@ -1258,32 +1439,36 @@ getlinepragma(void)
cp = lexbuf;
ep = lexbuf+sizeof(lexbuf)-5;
+ linep = nil;
for(;;) {
c = getr();
- if(c == '\n' || c == EOF)
+ if(c == EOF)
goto out;
+ if(c == '\n')
+ break;
if(c == ' ')
continue;
if(c == ':')
- break;
+ linep = cp;
if(cp < ep)
*cp++ = c;
}
*cp = 0;
+ if(linep == nil || linep >= ep)
+ goto out;
+ *linep++ = '\0';
n = 0;
- for(;;) {
- c = getr();
- if(!yy_isdigit(c))
- break;
- n = n*10 + (c-'0');
+ for(cp=linep; *cp; cp++) {
+ if(*cp < '0' || *cp > '9')
+ goto out;
+ n = n*10 + *cp - '0';
if(n > 1e8) {
yyerror("line number out of range");
errorexit();
}
}
-
- if(c != '\n' || n <= 0)
+ if(n <= 0)
goto out;
// try to avoid allocating file name over and over
@@ -1334,7 +1519,7 @@ yylex(void)
// Track last two tokens returned by yylex.
yyprev = yylast;
yylast = lx;
- return lx;
+ return lx;
}
static int
@@ -1561,7 +1746,6 @@ static struct
"complex128", LNAME, TCOMPLEX128, OXXX,
"bool", LNAME, TBOOL, OXXX,
- "byte", LNAME, TUINT8, OXXX,
"string", LNAME, TSTRING, OXXX,
"any", LNAME, TANY, OXXX,
@@ -1592,11 +1776,12 @@ static struct
"type", LTYPE, Txxx, OXXX,
"var", LVAR, Txxx, OXXX,
- "append", LNAME, Txxx, OAPPEND,
+ "append", LNAME, Txxx, OAPPEND,
"cap", LNAME, Txxx, OCAP,
"close", LNAME, Txxx, OCLOSE,
"complex", LNAME, Txxx, OCOMPLEX,
"copy", LNAME, Txxx, OCOPY,
+ "delete", LNAME, Txxx, ODELETE,
"imag", LNAME, Txxx, OIMAG,
"len", LNAME, Txxx, OLEN,
"make", LNAME, Txxx, OMAKE,
@@ -1621,6 +1806,7 @@ lexinit(void)
Sym *s, *s1;
Type *t;
int etype;
+ Val v;
/*
* initialize basic types array
@@ -1649,6 +1835,16 @@ lexinit(void)
s1->def = typenod(t);
continue;
}
+
+ etype = syms[i].op;
+ if(etype != OXXX) {
+ s1 = pkglookup(syms[i].name, builtinpkg);
+ s1->lexical = LNAME;
+ s1->def = nod(ONAME, N, N);
+ s1->def->sym = s1;
+ s1->def->etype = etype;
+ s1->def->builtin = 1;
+ }
}
// logically, the type of a string literal.
@@ -1676,6 +1872,77 @@ lexinit(void)
types[TBLANK] = typ(TBLANK);
s->def->type = types[TBLANK];
nblank = s->def;
+
+ s = pkglookup("_", builtinpkg);
+ s->block = -100;
+ s->def = nod(ONAME, N, N);
+ s->def->sym = s;
+ types[TBLANK] = typ(TBLANK);
+ s->def->type = types[TBLANK];
+
+ types[TNIL] = typ(TNIL);
+ s = pkglookup("nil", builtinpkg);
+ v.ctype = CTNIL;
+ s->def = nodlit(v);
+ s->def->sym = s;
+}
+
+static void
+lexinit1(void)
+{
+ Sym *s, *s1;
+ Type *t, *f, *rcvr, *in, *out;
+
+ // t = interface { Error() string }
+ rcvr = typ(TSTRUCT);
+ rcvr->type = typ(TFIELD);
+ rcvr->type->type = ptrto(typ(TSTRUCT));
+ rcvr->funarg = 1;
+ in = typ(TSTRUCT);
+ in->funarg = 1;
+ out = typ(TSTRUCT);
+ out->type = typ(TFIELD);
+ out->type->type = types[TSTRING];
+ out->funarg = 1;
+ f = typ(TFUNC);
+ *getthis(f) = rcvr;
+ *getoutarg(f) = out;
+ *getinarg(f) = in;
+ f->thistuple = 1;
+ f->intuple = 0;
+ f->outnamed = 0;
+ f->outtuple = 1;
+ t = typ(TINTER);
+ t->type = typ(TFIELD);
+ t->type->sym = lookup("Error");
+ t->type->type = f;
+
+ // error type
+ s = lookup("error");
+ s->lexical = LNAME;
+ errortype = t;
+ errortype->sym = s;
+ s1 = pkglookup("error", builtinpkg);
+ 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);
+ 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);
+ s1->lexical = LNAME;
+ s1->def = typenod(runetype);
}
static void
@@ -1713,7 +1980,18 @@ lexfini(void)
// there's only so much table-driven we can handle.
// these are special cases.
- types[TNIL] = typ(TNIL);
+ s = lookup("byte");
+ if(s->def == N)
+ s->def = typenod(bytetype);
+
+ s = lookup("error");
+ if(s->def == N)
+ s->def = typenod(errortype);
+
+ s = lookup("rune");
+ if(s->def == N)
+ s->def = typenod(runetype);
+
s = lookup("nil");
if(s->def == N) {
v.ctype = CTNIL;
@@ -1740,7 +2018,6 @@ lexfini(void)
}
nodfp = nod(ONAME, N, N);
- nodfp->noescape = 1;
nodfp->type = types[TINT32];
nodfp->xoffset = 0;
nodfp->class = PPARAM;
@@ -1923,7 +2200,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);
+ yyerrorl(s->def->lineno, "imported and not used: \"%Z\"", s->def->pkg->path);
s->def = N;
continue;
}
@@ -1931,7 +2208,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);
+ yyerrorl(s->def->pack->lineno, "imported and not used: \"%Z\"", s->def->pack->pkg->path);
s->def->pack->used = 1;
}
s->def = N;
diff --git a/src/cmd/gc/md5.c b/src/cmd/gc/md5.c
index 7cea1a6cf..5856aab51 100644
--- a/src/cmd/gc/md5.c
+++ b/src/cmd/gc/md5.c
@@ -5,6 +5,8 @@
// 64-bit MD5 (does full MD5 but returns 64 bits only).
// Translation of ../../pkg/crypto/md5/md5*.go.
+#include <u.h>
+#include <libc.h>
#include "go.h"
#include "md5.h"
diff --git a/src/cmd/gc/mkbuiltin b/src/cmd/gc/mkbuiltin
index cfd6e59c1..2f76e6f06 100755
--- a/src/cmd/gc/mkbuiltin
+++ b/src/cmd/gc/mkbuiltin
@@ -3,29 +3,29 @@
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
-# Generate builtin.c and builtin.c.boot from $* (runtime.go and unsafe.go).
+# Generate builtin.c from $* (runtime.go and unsafe.go).
# Run this after changing runtime.go and unsafe.go
# or after changing the export metadata format in the compiler.
# Either way, you need to have a working compiler binary first.
set -e
-eval $(gomake --no-print-directory -f ../../Make.inc go-env)
-if [ -z "$GC" ]; then
- echo 'missing $GC - gomake failed?' 1>&2
+eval $(go tool dist env)
+if [ -z "$GOCHAR" ]; then
+ echo 'missing $GOCHAR - go tool dist failed?' 1>&2
exit 1
fi
-gomake mkbuiltin1
+GC=${GOCHAR}g
+gcc -o mkbuiltin1 mkbuiltin1.c
rm -f _builtin.c
for i in runtime unsafe
do
- $GC -A $i.go
- O=$O ./mkbuiltin1 $i >>_builtin.c
+ go tool $GC -A $i.go
+ O=$GOCHAR ./mkbuiltin1 $i >>_builtin.c
done
-# If _builtin.c has changed vs builtin.c.boot,
+# If _builtin.c has changed vs builtin.c,
# check in the new change.
-cmp -s _builtin.c builtin.c.boot || cp _builtin.c builtin.c.boot
-
-mv _builtin.c builtin.c
+cmp -s _builtin.c builtin.c || cp _builtin.c builtin.c
+rm _builtin.c mkbuiltin1 unsafe.$GOCHAR runtime.$GOCHAR
diff --git a/src/cmd/gc/mkbuiltin1.c b/src/cmd/gc/mkbuiltin1.c
index ad83c0346..f8f61c278 100644
--- a/src/cmd/gc/mkbuiltin1.c
+++ b/src/cmd/gc/mkbuiltin1.c
@@ -2,15 +2,21 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build ignore
+
// Compile .go file, import data from .6 file, and generate C string version.
-#include <u.h>
-#include <libc.h>
#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
void esc(char*);
+void fatal(char*, ...);
-void
+int
main(int argc, char **argv)
{
char *name;
@@ -19,7 +25,7 @@ main(int argc, char **argv)
if(argc != 2) {
fprintf(stderr, "usage: mkbuiltin1 sys\n");
- sysfatal("in file $1.6 s/PACKAGE/$1/\n");
+ fatal("in file $1.6 s/PACKAGE/$1/");
}
name = argv[1];
@@ -27,14 +33,14 @@ main(int argc, char **argv)
snprintf(buf, sizeof(buf), "%s.%s", name, getenv("O"));
if((fin = fopen(buf, "r")) == NULL) {
- sysfatal("open %s: %r\n", buf);
+ fatal("open %s: %s", buf, strerror(errno));
}
// look for $$ that introduces imports
while(fgets(buf, sizeof buf, fin) != NULL)
if(strstr(buf, "$$"))
goto begin;
- sysfatal("did not find beginning of imports\n");
+ fatal("did not find beginning of imports");
begin:
printf("char *%simport =\n", name);
@@ -66,11 +72,11 @@ begin:
esc(p);
printf("\\n\"\n");
}
- sysfatal("did not find end of imports\n");
+ fatal("did not find end of imports");
end:
printf("\t\"$$\\n\";\n");
- exits(0);
+ return 0;
}
void
@@ -82,3 +88,15 @@ esc(char *p)
putchar(*p);
}
}
+
+void
+fatal(char *msg, ...)
+{
+ va_list arg;
+
+ va_start(arg, msg);
+ fprintf(stderr, "fatal: ");
+ vfprintf(stderr, msg, arg);
+ fprintf(stderr, "\n");
+ exit(2);
+}
diff --git a/src/cmd/gc/mkopnames b/src/cmd/gc/mkopnames
index fb2ceec81..d3f27e815 100755
--- a/src/cmd/gc/mkopnames
+++ b/src/cmd/gc/mkopnames
@@ -14,8 +14,8 @@ echo '{'
sed -n '/OXXX/,/OEND/p' go.h |
cpp |
sed 's!//.*!!; /^#/d' |
- tr ' ' '\n' |
- tr -d ' \t,' |
+ tr ' ' '\012' |
+ tr -d ' \011,' |
grep . |
sort |
grep -v '^OEND$' |
diff --git a/src/cmd/gc/mparith1.c b/src/cmd/gc/mparith1.c
index 6cd4e2500..33fa90e2e 100644
--- a/src/cmd/gc/mparith1.c
+++ b/src/cmd/gc/mparith1.c
@@ -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 <u.h>
+#include <libc.h>
#include "go.h"
/// uses arithmetic
@@ -70,7 +72,7 @@ void
mpsubfixfix(Mpint *a, Mpint *b)
{
mpnegfix(a);
- mpaddfixfix(a, b);
+ mpaddfixfix(a, b, 0);
mpnegfix(a);
}
@@ -88,7 +90,7 @@ mpaddcfix(Mpint *a, vlong c)
Mpint b;
mpmovecfix(&b, c);
- mpaddfixfix(a, &b);
+ mpaddfixfix(a, &b, 0);
}
void
@@ -300,7 +302,7 @@ mpatoflt(Mpflt *a, char *as)
if(c >= '0' && c <= '9') {
ex = ex*10 + (c-'0');
if(ex > 1e8) {
- yyerror("exponent out of range");
+ yyerror("constant exponent out of range: %s", as);
errorexit();
}
continue;
@@ -341,7 +343,7 @@ out:
return;
bad:
- yyerror("set ovf in mpatof");
+ yyerror("constant too large: %s", as);
mpmovecflt(a, 0.0);
}
@@ -429,7 +431,7 @@ out:
return;
bad:
- yyerror("set ovf in mpatov: %s", as);
+ yyerror("constant too large: %s", as);
mpmovecfix(a, 0);
}
diff --git a/src/cmd/gc/mparith2.c b/src/cmd/gc/mparith2.c
index 403255005..8e52ff216 100644
--- a/src/cmd/gc/mparith2.c
+++ b/src/cmd/gc/mparith2.c
@@ -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 <u.h>
+#include <libc.h>
#include "go.h"
//
@@ -25,10 +27,10 @@ mplen(Mpint *a)
//
// left shift mpint by one
-// ignores sign and overflow
+// ignores sign
//
static void
-mplsh(Mpint *a)
+mplsh(Mpint *a, int quiet)
{
long *a1, x;
int i, c;
@@ -44,19 +46,27 @@ mplsh(Mpint *a)
}
*a1++ = x;
}
+ a->ovf = c;
+ if(a->ovf && !quiet)
+ yyerror("constant shift overflow");
}
//
// left shift mpint by Mpscale
-// ignores sign and overflow
+// ignores sign
//
static void
-mplshw(Mpint *a)
+mplshw(Mpint *a, int quiet)
{
long *a1;
int i;
a1 = &a->a[Mpprec-1];
+ if(*a1) {
+ a->ovf = 1;
+ if(!quiet)
+ yyerror("constant shift overflow");
+ }
for(i=1; i<Mpprec; i++) {
a1[0] = a1[-1];
a1--;
@@ -119,7 +129,8 @@ mpcmp(Mpint *a, Mpint *b)
int i;
if(a->ovf || b->ovf) {
- yyerror("ovf in cmp");
+ if(nsavederrors+nerrors == 0)
+ yyerror("ovf in cmp");
return 0;
}
@@ -165,11 +176,11 @@ mpshiftfix(Mpint *a, int s)
{
if(s >= 0) {
while(s >= Mpscale) {
- mplshw(a);
+ mplshw(a, 0);
s -= Mpscale;
}
while(s > 0) {
- mplsh(a);
+ mplsh(a, 0);
s--;
}
} else {
@@ -188,13 +199,14 @@ mpshiftfix(Mpint *a, int s)
/// implements fix arihmetic
void
-mpaddfixfix(Mpint *a, Mpint *b)
+mpaddfixfix(Mpint *a, Mpint *b, int quiet)
{
int i, c;
long x, *a1, *b1;
if(a->ovf || b->ovf) {
- yyerror("ovf in mpaddxx");
+ if(nsavederrors+nerrors == 0)
+ yyerror("ovf in mpaddxx");
a->ovf = 1;
return;
}
@@ -216,8 +228,8 @@ mpaddfixfix(Mpint *a, Mpint *b)
*a1++ = x;
}
a->ovf = c;
- if(a->ovf)
- yyerror("set ovf in mpaddxx");
+ if(a->ovf && !quiet)
+ yyerror("constant addition overflow");
return;
@@ -264,7 +276,8 @@ mpmulfixfix(Mpint *a, Mpint *b)
Mpint s, q;
if(a->ovf || b->ovf) {
- yyerror("ovf in mpmulfixfix");
+ if(nsavederrors+nerrors == 0)
+ yyerror("ovf in mpmulfixfix");
a->ovf = 1;
return;
}
@@ -288,8 +301,8 @@ mpmulfixfix(Mpint *a, Mpint *b)
x = *a1++;
for(j=0; j<Mpscale; j++) {
if(x & 1)
- mpaddfixfix(&q, &s);
- mplsh(&s);
+ mpaddfixfix(&q, &s, 1);
+ mplsh(&s, 1);
x >>= 1;
}
}
@@ -297,7 +310,7 @@ mpmulfixfix(Mpint *a, Mpint *b)
q.neg = a->neg ^ b->neg;
mpmovefixfix(a, &q);
if(a->ovf)
- yyerror("set ovf in mpmulfixfix");
+ yyerror("constant multiplication overflow");
}
void
@@ -309,7 +322,8 @@ mpmulfract(Mpint *a, Mpint *b)
Mpint s, q;
if(a->ovf || b->ovf) {
- yyerror("ovf in mpmulflt");
+ if(nsavederrors+nerrors == 0)
+ yyerror("ovf in mpmulflt");
a->ovf = 1;
return;
}
@@ -332,7 +346,7 @@ mpmulfract(Mpint *a, Mpint *b)
for(j=0; j<Mpscale; j++) {
x <<= 1;
if(x & Mpbase)
- mpaddfixfix(&q, &s);
+ mpaddfixfix(&q, &s, 1);
mprsh(&s);
}
}
@@ -340,7 +354,7 @@ mpmulfract(Mpint *a, Mpint *b)
q.neg = a->neg ^ b->neg;
mpmovefixfix(a, &q);
if(a->ovf)
- yyerror("set ovf in mpmulflt");
+ yyerror("constant multiplication overflow");
}
void
@@ -349,8 +363,10 @@ mporfixfix(Mpint *a, Mpint *b)
int i;
long x, *a1, *b1;
+ x = 0;
if(a->ovf || b->ovf) {
- yyerror("ovf in mporfixfix");
+ if(nsavederrors+nerrors == 0)
+ yyerror("ovf in mporfixfix");
mpmovecfix(a, 0);
a->ovf = 1;
return;
@@ -383,8 +399,10 @@ mpandfixfix(Mpint *a, Mpint *b)
int i;
long x, *a1, *b1;
+ x = 0;
if(a->ovf || b->ovf) {
- yyerror("ovf in mpandfixfix");
+ if(nsavederrors+nerrors == 0)
+ yyerror("ovf in mpandfixfix");
mpmovecfix(a, 0);
a->ovf = 1;
return;
@@ -417,8 +435,10 @@ mpandnotfixfix(Mpint *a, Mpint *b)
int i;
long x, *a1, *b1;
+ x = 0;
if(a->ovf || b->ovf) {
- yyerror("ovf in mpandnotfixfix");
+ if(nsavederrors+nerrors == 0)
+ yyerror("ovf in mpandnotfixfix");
mpmovecfix(a, 0);
a->ovf = 1;
return;
@@ -451,8 +471,10 @@ mpxorfixfix(Mpint *a, Mpint *b)
int i;
long x, *a1, *b1;
+ x = 0;
if(a->ovf || b->ovf) {
- yyerror("ovf in mporfixfix");
+ if(nsavederrors+nerrors == 0)
+ yyerror("ovf in mporfixfix");
mpmovecfix(a, 0);
a->ovf = 1;
return;
@@ -485,7 +507,8 @@ mplshfixfix(Mpint *a, Mpint *b)
vlong s;
if(a->ovf || b->ovf) {
- yyerror("ovf in mporfixfix");
+ if(nsavederrors+nerrors == 0)
+ yyerror("ovf in mporfixfix");
mpmovecfix(a, 0);
a->ovf = 1;
return;
@@ -506,7 +529,8 @@ mprshfixfix(Mpint *a, Mpint *b)
vlong s;
if(a->ovf || b->ovf) {
- yyerror("ovf in mprshfixfix");
+ if(nsavederrors+nerrors == 0)
+ yyerror("ovf in mprshfixfix");
mpmovecfix(a, 0);
a->ovf = 1;
return;
@@ -536,7 +560,8 @@ mpgetfix(Mpint *a)
vlong v;
if(a->ovf) {
- yyerror("constant overflow");
+ if(nsavederrors+nerrors == 0)
+ yyerror("constant overflow");
return 0;
}
@@ -589,7 +614,7 @@ mpdivmodfixfix(Mpint *q, Mpint *r, Mpint *n, Mpint *d)
for(i=0; i<Mpprec*Mpscale; i++) {
if(mpcmp(d, r) > 0)
break;
- mplsh(d);
+ mplsh(d, 1);
}
// if it never happens
@@ -599,7 +624,7 @@ mpdivmodfixfix(Mpint *q, Mpint *r, Mpint *n, Mpint *d)
r->ovf = 1;
n->neg = ns;
d->neg = ds;
- yyerror("set ovf in mpdivmodfixfix");
+ yyerror("constant division overflow");
return;
}
@@ -608,7 +633,7 @@ mpdivmodfixfix(Mpint *q, Mpint *r, Mpint *n, Mpint *d)
// when done the remaining numerator
// will be the remainder
for(; i>0; i--) {
- mplsh(q);
+ mplsh(q, 1);
mprsh(d);
if(mpcmp(d, r) <= 0) {
mpaddcfix(q, 1);
diff --git a/src/cmd/gc/mparith3.c b/src/cmd/gc/mparith3.c
index b11a4f5f1..f8344c9b4 100644
--- a/src/cmd/gc/mparith3.c
+++ b/src/cmd/gc/mparith3.c
@@ -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 <u.h>
+#include <libc.h>
#include "go.h"
/*
@@ -87,17 +89,17 @@ mpaddfltflt(Mpflt *a, Mpflt *b)
// a is larger, shift b right
mpmovefltflt(&c, b);
mpshiftfix(&c.val, -s);
- mpaddfixfix(&a->val, &c.val);
+ mpaddfixfix(&a->val, &c.val, 0);
goto out;
}
if(s < 0) {
// b is larger, shift a right
mpshiftfix(&a->val, s);
a->exp -= s;
- mpaddfixfix(&a->val, &b->val);
+ mpaddfixfix(&a->val, &b->val, 0);
goto out;
}
- mpaddfixfix(&a->val, &b->val);
+ mpaddfixfix(&a->val, &b->val, 0);
out:
mpnorm(a);
@@ -151,7 +153,7 @@ mpdivfltflt(Mpflt *a, Mpflt *b)
a->exp = 0;
a->val.neg = 0;
a->val.ovf = 1;
- yyerror("mpdivfltflt divide by zero");
+ yyerror("constant division by zero");
return;
}
@@ -183,7 +185,7 @@ mpgetflt(Mpflt *a)
uvlong v, vm;
double f;
- if(a->val.ovf)
+ if(a->val.ovf && nsavederrors+nerrors == 0)
yyerror("mpgetflt ovf");
s = sigfig(a);
diff --git a/src/cmd/gc/obj.c b/src/cmd/gc/obj.c
index f99f93bda..e45b4e0d4 100644
--- a/src/cmd/gc/obj.c
+++ b/src/cmd/gc/obj.c
@@ -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 <u.h>
+#include <libc.h>
#include "go.h"
/*
@@ -21,7 +23,7 @@ dumpobj(void)
errorexit();
}
- Bprint(bout, "go object %s %s %s\n", getgoos(), thestring, getgoversion());
+ Bprint(bout, "go object %s %s %s %s\n", getgoos(), thestring, getgoversion(), expstring());
Bprint(bout, " exports automatically generated from\n");
Bprint(bout, " %s in package \"%s\"\n", curio.infile, localpkg->name);
dumpexport();
@@ -29,10 +31,6 @@ dumpobj(void)
outhist(bout);
- // add nil plist w AEND to catch
- // auto-generated trampolines, data
- newplist();
-
dumpglobls();
dumptypestructs();
dumpdata();
@@ -54,7 +52,7 @@ dumpglobls(void)
continue;
if(n->type == T)
- fatal("external %#N nil type\n", n);
+ fatal("external %N nil type\n", n);
if(n->class == PFUNC)
continue;
if(n->sym->pkg != localpkg)
@@ -128,10 +126,37 @@ outhist(Biobuf *b)
{
Hist *h;
char *p, ds[] = {'c', ':', '/', 0};
+ char *tofree;
+ int n;
+ static int first = 1;
+ static char *goroot, *goroot_final;
+
+ if(first) {
+ // Decide whether we need to rewrite paths from $GOROOT to $GOROOT_FINAL.
+ first = 0;
+ goroot = getenv("GOROOT");
+ goroot_final = getenv("GOROOT_FINAL");
+ if(goroot == nil)
+ goroot = "";
+ if(goroot_final == nil)
+ goroot_final = goroot;
+ if(strcmp(goroot, goroot_final) == 0) {
+ goroot = nil;
+ goroot_final = nil;
+ }
+ }
+ tofree = nil;
for(h = hist; h != H; h = h->link) {
p = h->name;
if(p) {
+ if(goroot != nil) {
+ n = strlen(goroot);
+ if(strncmp(p, goroot, strlen(goroot)) == 0 && p[n] == '/') {
+ tofree = smprint("%s%s", goroot_final, p+n);
+ p = tofree;
+ }
+ }
if(windows) {
// if windows variable is set, then, we know already,
// pathname is started with windows drive specifier
@@ -163,9 +188,12 @@ outhist(Biobuf *b)
outzfile(b, p);
}
}
-
}
zhist(b, h->line, h->offset);
+ if(tofree) {
+ free(tofree);
+ tofree = nil;
+ }
}
}
@@ -260,7 +288,7 @@ stringsym(char *s, int len)
tmp.lit.len = len;
memmove(tmp.lit.s, s, len);
tmp.lit.s[len] = '\0';
- snprint(namebuf, sizeof(namebuf), "\"%Z\"", &tmp);
+ snprint(namebuf, sizeof(namebuf), "\"%Z\"", &tmp.lit);
pkg = gostringpkg;
}
sym = pkglookup(namebuf, pkg);
@@ -269,8 +297,8 @@ stringsym(char *s, int len)
if(sym->flags & SymUniq)
return sym;
sym->flags |= SymUniq;
-
- data();
+ sym->def = newname(sym);
+
off = 0;
// string header
@@ -287,7 +315,6 @@ stringsym(char *s, int len)
off = duint8(sym, off, 0); // terminating NUL for runtime
off = (off+widthptr-1)&~(widthptr-1); // round to pointer alignment
ggloblsym(sym, off, 1);
- text();
-
+
return sym;
}
diff --git a/src/cmd/gc/order.c b/src/cmd/gc/order.c
new file mode 100644
index 000000000..2cab5fb95
--- /dev/null
+++ b/src/cmd/gc/order.c
@@ -0,0 +1,358 @@
+// 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.
+
+// Rewrite tree to use separate statements to enforce
+// order of evaluation. Makes walk easier, because it
+// can (after this runs) reorder at will within an expression.
+
+#include <u.h>
+#include <libc.h>
+#include "go.h"
+
+static void orderstmt(Node*, NodeList**);
+static void orderstmtlist(NodeList*, NodeList**);
+static void orderblock(NodeList **l);
+static void orderexpr(Node**, NodeList**);
+static void orderexprlist(NodeList*, NodeList**);
+
+void
+order(Node *fn)
+{
+ orderblock(&fn->nbody);
+}
+
+static void
+orderstmtlist(NodeList *l, NodeList **out)
+{
+ for(; l; l=l->next)
+ orderstmt(l->n, out);
+}
+
+// Order the block of statements *l onto a new list,
+// and then replace *l with that list.
+static void
+orderblock(NodeList **l)
+{
+ NodeList *out;
+
+ out = nil;
+ orderstmtlist(*l, &out);
+ *l = out;
+}
+
+// Order the side effects in *np and leave them as
+// the init list of the final *np.
+static void
+orderexprinplace(Node **np)
+{
+ Node *n;
+ NodeList *out;
+
+ n = *np;
+ out = nil;
+ orderexpr(&n, &out);
+ addinit(&n, out);
+ *np = n;
+}
+
+// Like orderblock, but applied to a single statement.
+static void
+orderstmtinplace(Node **np)
+{
+ Node *n;
+ NodeList *out;
+
+ n = *np;
+ out = nil;
+ orderstmt(n, &out);
+ *np = liststmt(out);
+}
+
+// Move n's init list to *out.
+static void
+orderinit(Node *n, NodeList **out)
+{
+ orderstmtlist(n->ninit, out);
+ n->ninit = nil;
+}
+
+// Is the list l actually just f() for a multi-value function?
+static int
+ismulticall(NodeList *l)
+{
+ Node *n;
+
+ // one arg only
+ if(l == nil || l->next != nil)
+ return 0;
+ n = l->n;
+
+ // must be call
+ switch(n->op) {
+ default:
+ return 0;
+ case OCALLFUNC:
+ case OCALLMETH:
+ case OCALLINTER:
+ break;
+ }
+
+ // call must return multiple values
+ return n->left->type->outtuple > 1;
+}
+
+// n is a multi-value function call. Add t1, t2, .. = n to out
+// and return the list t1, t2, ...
+static NodeList*
+copyret(Node *n, NodeList **out)
+{
+ Type *t;
+ Node *tmp, *as;
+ NodeList *l1, *l2;
+ Iter tl;
+
+ if(n->type->etype != TSTRUCT || !n->type->funarg)
+ fatal("copyret %T %d", n->type, n->left->type->outtuple);
+
+ l1 = nil;
+ l2 = nil;
+ for(t=structfirst(&tl, &n->type); t; t=structnext(&tl)) {
+ tmp = temp(t->type);
+ l1 = list(l1, tmp);
+ l2 = list(l2, tmp);
+ }
+
+ as = nod(OAS2, N, N);
+ as->list = l1;
+ as->rlist = list1(n);
+ typecheck(&as, Etop);
+ orderstmt(as, out);
+
+ return l2;
+}
+
+static void
+ordercallargs(NodeList **l, NodeList **out)
+{
+ if(ismulticall(*l)) {
+ // return f() where f() is multiple values.
+ *l = copyret((*l)->n, out);
+ } else {
+ orderexprlist(*l, out);
+ }
+}
+
+static void
+ordercall(Node *n, NodeList **out)
+{
+ orderexpr(&n->left, out);
+ ordercallargs(&n->list, out);
+}
+
+static void
+orderstmt(Node *n, NodeList **out)
+{
+ int lno;
+ NodeList *l;
+ Node *r;
+
+ if(n == N)
+ return;
+
+ lno = setlineno(n);
+
+ orderinit(n, out);
+
+ switch(n->op) {
+ default:
+ fatal("orderstmt %O", n->op);
+
+ case OAS2:
+ case OAS2DOTTYPE:
+ case OAS2MAPR:
+ case OAS:
+ case OASOP:
+ case OCLOSE:
+ case OCOPY:
+ case ODELETE:
+ case OPANIC:
+ case OPRINT:
+ case OPRINTN:
+ case ORECOVER:
+ case ORECV:
+ case OSEND:
+ orderexpr(&n->left, out);
+ orderexpr(&n->right, out);
+ orderexprlist(n->list, out);
+ orderexprlist(n->rlist, out);
+ *out = list(*out, n);
+ break;
+
+ case OAS2FUNC:
+ // Special: avoid copy of func call n->rlist->n.
+ orderexprlist(n->list, out);
+ ordercall(n->rlist->n, out);
+ *out = list(*out, n);
+ break;
+
+ case OAS2RECV:
+ // Special: avoid copy of receive.
+ orderexprlist(n->list, out);
+ orderexpr(&n->rlist->n->left, out); // arg to recv
+ *out = list(*out, n);
+ break;
+
+ case OBLOCK:
+ case OEMPTY:
+ // Special: does not save n onto out.
+ orderstmtlist(n->list, out);
+ break;
+
+ case OBREAK:
+ case OCONTINUE:
+ case ODCL:
+ case ODCLCONST:
+ case ODCLTYPE:
+ case OFALL:
+ case_OFALL:
+ case OGOTO:
+ case OLABEL:
+ // Special: n->left is not an expression; save as is.
+ *out = list(*out, n);
+ break;
+
+ case OCALLFUNC:
+ case OCALLINTER:
+ case OCALLMETH:
+ // Special: handle call arguments.
+ ordercall(n, out);
+ *out = list(*out, n);
+ break;
+
+ case ODEFER:
+ case OPROC:
+ // Special: order arguments to inner call but not call itself.
+ ordercall(n->left, out);
+ *out = list(*out, n);
+ break;
+
+ case OFOR:
+ orderexprinplace(&n->ntest);
+ orderstmtinplace(&n->nincr);
+ orderblock(&n->nbody);
+ *out = list(*out, n);
+ break;
+
+ case OIF:
+ orderexprinplace(&n->ntest);
+ orderblock(&n->nbody);
+ orderblock(&n->nelse);
+ *out = list(*out, n);
+ break;
+
+ case ORANGE:
+ orderexpr(&n->right, out);
+ for(l=n->list; l; l=l->next)
+ orderexprinplace(&l->n);
+ orderblock(&n->nbody);
+ *out = list(*out, n);
+ break;
+
+ case ORETURN:
+ ordercallargs(&n->list, out);
+ *out = list(*out, n);
+ break;
+
+ case OSELECT:
+ for(l=n->list; l; l=l->next) {
+ if(l->n->op != OXCASE)
+ fatal("order select case %O", l->n->op);
+ r = l->n->left;
+ if(r == nil)
+ continue;
+ switch(r->op) {
+ case OSELRECV:
+ case OSELRECV2:
+ orderexprinplace(&r->left);
+ orderexprinplace(&r->ntest);
+ orderexpr(&r->right->left, out);
+ break;
+ case OSEND:
+ orderexpr(&r->left, out);
+ orderexpr(&r->right, out);
+ break;
+ }
+ }
+ *out = list(*out, n);
+ break;
+
+ case OSWITCH:
+ orderexpr(&n->ntest, out);
+ for(l=n->list; l; l=l->next) {
+ if(l->n->op != OXCASE)
+ fatal("order switch case %O", l->n->op);
+ orderexpr(&l->n->left, &l->n->ninit);
+ }
+ *out = list(*out, n);
+ break;
+
+ case OXFALL:
+ yyerror("fallthrough statement out of place");
+ n->op = OFALL;
+ goto case_OFALL;
+ }
+
+ lineno = lno;
+}
+
+static void
+orderexprlist(NodeList *l, NodeList **out)
+{
+ for(; l; l=l->next)
+ orderexpr(&l->n, out);
+}
+
+static void
+orderexpr(Node **np, NodeList **out)
+{
+ Node *n;
+ int lno;
+
+ n = *np;
+ if(n == N)
+ return;
+
+ lno = setlineno(n);
+ orderinit(n, out);
+
+ switch(n->op) {
+ default:
+ orderexpr(&n->left, out);
+ orderexpr(&n->right, out);
+ orderexprlist(n->list, out);
+ orderexprlist(n->rlist, out);
+ break;
+
+ case OANDAND:
+ case OOROR:
+ orderexpr(&n->left, out);
+ orderexprinplace(&n->right);
+ break;
+
+ case OCALLFUNC:
+ case OCALLMETH:
+ case OCALLINTER:
+ ordercall(n, out);
+ n = copyexpr(n, n->type, out);
+ break;
+
+ case ORECV:
+ n = copyexpr(n, n->type, out);
+ break;
+ }
+
+ lineno = lno;
+
+ *np = n;
+}
diff --git a/src/cmd/gc/pgen.c b/src/cmd/gc/pgen.c
index abe8ea892..f2b75d61b 100644
--- a/src/cmd/gc/pgen.c
+++ b/src/cmd/gc/pgen.c
@@ -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 <u.h>
+#include <libc.h>
#include "gg.h"
#include "opt.h"
-static void compactframe(Prog* p);
+static void allocauto(Prog* p);
void
compile(Node *fn)
@@ -52,14 +54,16 @@ compile(Node *fn)
t = structnext(&save);
}
}
-
+
+ order(curfn);
+ if(nerrors != 0)
+ goto ret;
+
hasdefer = 0;
walk(curfn);
if(nerrors != 0)
goto ret;
- allocparams();
-
continpc = P;
breakpc = P;
@@ -70,6 +74,8 @@ compile(Node *fn)
nodconst(&nod1, types[TINT32], 0);
ptxt = gins(ATEXT, isblank(curfn->nname) ? N : curfn->nname, &nod1);
+ if(fn->dupok)
+ ptxt->TEXTFLAG = DUPOK;
afunclit(&ptxt->from);
ginit();
@@ -113,9 +119,13 @@ compile(Node *fn)
}
oldstksize = stksize;
- compactframe(ptxt);
+ allocauto(ptxt);
if(0)
- print("compactframe: %ld to %ld\n", oldstksize, stksize);
+ print("allocauto: %lld to %lld\n", oldstksize, (vlong)stksize);
+
+ setlineno(curfn);
+ if((int64)stksize+maxarg > (1ULL<<31))
+ yyerror("stack frame too large (>2GB)");
defframe(ptxt);
@@ -145,13 +155,13 @@ cmpstackvar(Node *a, Node *b)
// TODO(lvd) find out where the PAUTO/OLITERAL nodes come from.
static void
-compactframe(Prog* ptxt)
+allocauto(Prog* ptxt)
{
NodeList *ll;
Node* n;
vlong w;
- if (stksize == 0)
+ if(curfn->dcl == nil)
return;
// Mark the PAUTO's unused.
@@ -188,6 +198,7 @@ compactframe(Prog* ptxt)
if (n->class != PAUTO || n->op != ONAME)
continue;
+ dowidth(n->type);
w = n->type->width;
if(w >= MAXWIDTH || w < 0)
fatal("bad width");
diff --git a/src/cmd/gc/print.c b/src/cmd/gc/print.c
deleted file mode 100644
index 5913e848a..000000000
--- a/src/cmd/gc/print.c
+++ /dev/null
@@ -1,480 +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 "go.h"
-
-enum
-{
- PFIXME = 0,
-};
-
-void
-exprlistfmt(Fmt *f, NodeList *l)
-{
- for(; l; l=l->next) {
- exprfmt(f, l->n, 0);
- if(l->next)
- fmtprint(f, ", ");
- }
-}
-
-void
-exprfmt(Fmt *f, Node *n, int prec)
-{
- int nprec;
- char *p;
-
- nprec = 0;
- if(n == nil) {
- fmtprint(f, "<nil>");
- return;
- }
-
- if(n->implicit) {
- exprfmt(f, n->left, prec);
- return;
- }
-
- switch(n->op) {
- case OAPPEND:
- case ONAME:
- case ONONAME:
- case OPACK:
- case OLITERAL:
- case ODOT:
- case ODOTPTR:
- case ODOTINTER:
- case ODOTMETH:
- case ODOTTYPE:
- case ODOTTYPE2:
- case OXDOT:
- case OARRAYBYTESTR:
- case OCAP:
- case OCLOSE:
- case OCOPY:
- case OLEN:
- case OMAKE:
- case ONEW:
- case OPANIC:
- case OPRINT:
- case OPRINTN:
- case OCALL:
- case OCALLMETH:
- case OCALLINTER:
- case OCALLFUNC:
- case OCONV:
- case OCONVNOP:
- case OMAKESLICE:
- case ORUNESTR:
- case OADDR:
- case OCOM:
- case OIND:
- case OMINUS:
- case ONOT:
- case OPLUS:
- case ORECV:
- case OCONVIFACE:
- case OTPAREN:
- case OINDEX:
- case OINDEXMAP:
- case OPAREN:
- nprec = 7;
- break;
-
- case OMUL:
- case ODIV:
- case OMOD:
- case OLSH:
- case ORSH:
- case OAND:
- case OANDNOT:
- nprec = 6;
- break;
-
- case OADD:
- case OSUB:
- case OOR:
- case OXOR:
- nprec = 5;
- break;
-
- case OEQ:
- case OLT:
- case OLE:
- case OGE:
- case OGT:
- case ONE:
- nprec = 4;
- break;
-
- case OSEND:
- nprec = 3;
- break;
-
- case OANDAND:
- nprec = 2;
- break;
-
- case OOROR:
- nprec = 1;
- break;
-
- case OTYPE:
- if(n->sym != S)
- nprec = 7;
- break;
- }
-
- if(prec > nprec)
- fmtprint(f, "(");
-
- switch(n->op) {
- default:
- bad:
- fmtprint(f, "(node %O)", n->op);
- break;
-
- case OPAREN:
- fmtprint(f, "(%#N)", n->left);
- break;
-
- case OREGISTER:
- fmtprint(f, "%R", n->val.u.reg);
- break;
-
- case OLITERAL:
- if(n->sym != S) {
- fmtprint(f, "%S", n->sym);
- break;
- }
- switch(n->val.ctype) {
- default:
- goto bad;
- case CTINT:
- fmtprint(f, "%B", n->val.u.xval);
- break;
- case CTBOOL:
- if(n->val.u.bval)
- fmtprint(f, "true");
- else
- fmtprint(f, "false");
- break;
- case CTCPLX:
- fmtprint(f, "%.17g+%.17gi",
- mpgetflt(&n->val.u.cval->real),
- mpgetflt(&n->val.u.cval->imag));
- break;
- case CTFLT:
- fmtprint(f, "%.17g", mpgetflt(n->val.u.fval));
- break;
- case CTSTR:
- fmtprint(f, "\"%Z\"", n->val.u.sval);
- break;
- case CTNIL:
- fmtprint(f, "nil");
- break;
- }
- break;
-
- case ONAME:
- case OPACK:
- case ONONAME:
- fmtprint(f, "%S", n->sym);
- break;
-
- case OTYPE:
- if(n->type == T && n->sym != S) {
- fmtprint(f, "%S", n->sym);
- break;
- }
- fmtprint(f, "%T", n->type);
- break;
-
- case OTARRAY:
- fmtprint(f, "[]");
- exprfmt(f, n->left, PFIXME);
- break;
-
- case OTPAREN:
- fmtprint(f, "(");
- exprfmt(f, n->left, 0);
- fmtprint(f, ")");
- break;
-
- case OTMAP:
- fmtprint(f, "map[");
- exprfmt(f, n->left, 0);
- fmtprint(f, "] ");
- exprfmt(f, n->right, 0);
- break;
-
- case OTCHAN:
- if(n->etype == Crecv)
- fmtprint(f, "<-");
- fmtprint(f, "chan");
- if(n->etype == Csend) {
- fmtprint(f, "<- ");
- exprfmt(f, n->left, 0);
- } else {
- fmtprint(f, " ");
- if(n->left->op == OTCHAN && n->left->sym == S && n->left->etype == Crecv) {
- fmtprint(f, "(");
- exprfmt(f, n->left, 0);
- fmtprint(f, ")");
- } else
- exprfmt(f, n->left, 0);
- }
- break;
-
- case OTSTRUCT:
- fmtprint(f, "<struct>");
- break;
-
- case OTINTER:
- fmtprint(f, "<inter>");
- break;
-
- case OTFUNC:
- fmtprint(f, "<func>");
- break;
-
- case OAS:
- exprfmt(f, n->left, 0);
- fmtprint(f, " = ");
- exprfmt(f, n->right, 0);
- break;
-
- case OASOP:
- exprfmt(f, n->left, 0);
- fmtprint(f, " %#O= ", n->etype);
- exprfmt(f, n->right, 0);
- break;
-
- case OAS2:
- case OAS2DOTTYPE:
- case OAS2FUNC:
- case OAS2MAPR:
- case OAS2MAPW:
- case OAS2RECV:
- exprlistfmt(f, n->list);
- fmtprint(f, " = ");
- exprlistfmt(f, n->rlist);
- break;
-
- case OADD:
- case OANDAND:
- case OANDNOT:
- case ODIV:
- case OEQ:
- case OGE:
- case OGT:
- case OLE:
- case OLT:
- case OLSH:
- case OMOD:
- case OMUL:
- case ONE:
- case OOR:
- case OOROR:
- case ORSH:
- case OSEND:
- case OSUB:
- case OXOR:
- exprfmt(f, n->left, nprec);
- fmtprint(f, " %#O ", n->op);
- exprfmt(f, n->right, nprec+1);
- break;
-
- case OADDR:
- case OCOM:
- case OIND:
- case OMINUS:
- case ONOT:
- case OPLUS:
- case ORECV:
- fmtprint(f, "%#O", n->op);
- if((n->op == OMINUS || n->op == OPLUS) && n->left->op == n->op)
- fmtprint(f, " ");
- exprfmt(f, n->left, 0);
- break;
-
- case OCLOSURE:
- fmtprint(f, "func literal");
- break;
-
- case OCOMPLIT:
- fmtprint(f, "composite literal");
- break;
-
- case OARRAYLIT:
- if(isslice(n->type))
- fmtprint(f, "slice literal");
- else
- fmtprint(f, "array literal");
- break;
-
- case OMAPLIT:
- fmtprint(f, "map literal");
- break;
-
- case OSTRUCTLIT:
- fmtprint(f, "struct literal");
- break;
-
- case OXDOT:
- case ODOT:
- case ODOTPTR:
- case ODOTINTER:
- case ODOTMETH:
- exprfmt(f, n->left, 7);
- if(n->right == N || n->right->sym == S)
- fmtprint(f, ".<nil>");
- else {
- // skip leading type· in method name
- p = utfrrune(n->right->sym->name, 0xb7);
- if(p)
- p+=2;
- else
- p = n->right->sym->name;
- fmtprint(f, ".%s", p);
- }
- break;
-
- case ODOTTYPE:
- case ODOTTYPE2:
- exprfmt(f, n->left, 7);
- fmtprint(f, ".(");
- if(n->right != N)
- exprfmt(f, n->right, 0);
- else
- fmtprint(f, "%T", n->type);
- fmtprint(f, ")");
- break;
-
- case OINDEX:
- case OINDEXMAP:
- exprfmt(f, n->left, 7);
- fmtprint(f, "[");
- exprfmt(f, n->right, 0);
- fmtprint(f, "]");
- break;
-
- case OSLICE:
- case OSLICESTR:
- case OSLICEARR:
- exprfmt(f, n->left, 7);
- fmtprint(f, "[");
- if(n->right->left != N)
- exprfmt(f, n->right->left, 0);
- fmtprint(f, ":");
- if(n->right->right != N)
- exprfmt(f, n->right->right, 0);
- fmtprint(f, "]");
- break;
-
- case OCALL:
- case OCALLFUNC:
- case OCALLINTER:
- case OCALLMETH:
- exprfmt(f, n->left, 7);
- fmtprint(f, "(");
- exprlistfmt(f, n->list);
- if(n->isddd)
- fmtprint(f, "...");
- fmtprint(f, ")");
- break;
-
- case OCOMPLEX:
- fmtprint(f, "complex(");
- exprfmt(f, n->left, 0);
- fmtprint(f, ", ");
- exprfmt(f, n->right, 0);
- fmtprint(f, ")");
- break;
-
- case OREAL:
- fmtprint(f, "real(");
- exprfmt(f, n->left, 0);
- fmtprint(f, ")");
- break;
-
- case OIMAG:
- fmtprint(f, "imag(");
- exprfmt(f, n->left, 0);
- fmtprint(f, ")");
- break;
-
- case OCONV:
- case OCONVIFACE:
- case OCONVNOP:
- case OARRAYBYTESTR:
- case OSTRARRAYBYTE:
- case ORUNESTR:
- if(n->type == T || n->type->sym == S)
- fmtprint(f, "(%T)(", n->type);
- else
- fmtprint(f, "%T(", n->type);
- if(n->left == N)
- exprlistfmt(f, n->list);
- else
- exprfmt(f, n->left, 0);
- fmtprint(f, ")");
- break;
-
- case OAPPEND:
- case OCAP:
- case OCLOSE:
- case OLEN:
- case OCOPY:
- case OMAKE:
- case ONEW:
- case OPANIC:
- case OPRINT:
- case OPRINTN:
- fmtprint(f, "%#O(", n->op);
- if(n->left)
- exprfmt(f, n->left, 0);
- else
- exprlistfmt(f, n->list);
- fmtprint(f, ")");
- break;
-
- case OMAKESLICE:
- fmtprint(f, "make(%#T, ", n->type);
- exprfmt(f, n->left, 0);
- if(count(n->list) > 2) {
- fmtprint(f, ", ");
- exprfmt(f, n->right, 0);
- }
- fmtprint(f, ")");
- break;
-
- case OMAKEMAP:
- case OMAKECHAN:
- fmtprint(f, "make(%#T)", n->type);
- break;
-
- // Some statements
-
- case ODCL:
- fmtprint(f, "var %S %#T", n->left->sym, n->left->type);
- break;
-
- case ORETURN:
- fmtprint(f, "return ");
- exprlistfmt(f, n->list);
- break;
-
- case OPROC:
- fmtprint(f, "go %#N", n->left);
- break;
-
- case ODEFER:
- fmtprint(f, "defer %#N", n->left);
- break;
- }
-
- if(prec > nprec)
- fmtprint(f, ")");
-}
diff --git a/src/cmd/gc/range.c b/src/cmd/gc/range.c
index 5ce693ae3..9bcd833a7 100644
--- a/src/cmd/gc/range.c
+++ b/src/cmd/gc/range.c
@@ -6,6 +6,8 @@
* range
*/
+#include <u.h>
+#include <libc.h>
#include "go.h"
void
@@ -30,7 +32,7 @@ typecheckrange(Node *n)
switch(t->etype) {
default:
- yyerror("cannot range over %+N", n->right);
+ yyerror("cannot range over %lN", n->right);
goto out;
case TARRAY:
@@ -44,6 +46,10 @@ typecheckrange(Node *n)
break;
case TCHAN:
+ if(!(t->chan & Crecv)) {
+ yyerror("invalid operation: range %N (receive from send-only type %T)", n->right, n->right->type);
+ goto out;
+ }
t1 = t->type;
t2 = nil;
if(count(n->list) == 2)
@@ -52,7 +58,7 @@ typecheckrange(Node *n)
case TSTRING:
t1 = types[TINT];
- t2 = types[TINT];
+ t2 = runetype;
break;
}
@@ -69,12 +75,12 @@ typecheckrange(Node *n)
if(v1->defn == n)
v1->type = t1;
else if(v1->type != T && assignop(t1, v1->type, &why) == 0)
- yyerror("cannot assign type %T to %+N in range%s", t1, v1, why);
+ yyerror("cannot assign type %T to %lN in range%s", t1, v1, why);
if(v2) {
if(v2->defn == n)
v2->type = t2;
else if(v2->type != T && assignop(t2, v2->type, &why) == 0)
- yyerror("cannot assign type %T to %+N in range%s", t2, v2, why);
+ yyerror("cannot assign type %T to %lN in range%s", t2, v2, why);
}
out:
@@ -111,8 +117,6 @@ walkrange(Node *n)
}
v1 = n->list->n;
- hv1 = N;
-
v2 = N;
if(n->list->next)
v2 = n->list->next->n;
@@ -123,8 +127,7 @@ walkrange(Node *n)
// no need to make a potentially expensive copy.
ha = a;
} else {
- ha = nod(OXXX, N, N);
- tempname(ha, a->type);
+ ha = temp(a->type);
init = list(init, nod(OAS, ha, a));
}
@@ -133,17 +136,14 @@ walkrange(Node *n)
fatal("walkrange");
case TARRAY:
- hv1 = nod(OXXX, N, n);
- tempname(hv1, types[TINT]);
- hn = nod(OXXX, N, N);
- tempname(hn, types[TINT]);
+ hv1 = temp(types[TINT]);
+ hn = temp(types[TINT]);
hp = nil;
init = list(init, nod(OAS, hv1, N));
init = list(init, nod(OAS, hn, nod(OLEN, ha, N)));
if(v2) {
- hp = nod(OXXX, N, N);
- tempname(hp, ptrto(n->type->type));
+ hp = temp(ptrto(n->type->type));
tmp = nod(OINDEX, ha, nodintconst(0));
tmp->etype = 1; // no bounds check
init = list(init, nod(OAS, hp, nod(OADDR, tmp, N)));
@@ -167,9 +167,10 @@ walkrange(Node *n)
case TMAP:
th = typ(TARRAY);
th->type = ptrto(types[TUINT8]);
- th->bound = (sizeof(struct Hiter) + widthptr - 1) / widthptr;
- hit = nod(OXXX, N, N);
- tempname(hit, th);
+ // see ../../pkg/runtime/hashmap.h:/hash_iter
+ // Size in words.
+ th->bound = 5 + 4*3 + 4*4/widthptr;
+ hit = temp(th);
fn = syslook("mapiterinit", 1);
argtype(fn, t->down);
@@ -200,10 +201,8 @@ walkrange(Node *n)
break;
case TCHAN:
- hv1 = nod(OXXX, N, n);
- tempname(hv1, t->type);
- hb = nod(OXXX, N, N);
- tempname(hb, types[TBOOL]);
+ hv1 = temp(t->type);
+ hb = temp(types[TBOOL]);
n->ntest = nod(ONE, hb, nodbool(0));
a = nod(OAS2RECV, N, N);
@@ -215,18 +214,15 @@ walkrange(Node *n)
break;
case TSTRING:
- ohv1 = nod(OXXX, N, N);
- tempname(ohv1, types[TINT]);
+ ohv1 = temp(types[TINT]);
- hv1 = nod(OXXX, N, N);
- tempname(hv1, types[TINT]);
+ hv1 = temp(types[TINT]);
init = list(init, nod(OAS, hv1, N));
if(v2 == N)
a = nod(OAS, hv1, mkcall("stringiter", types[TINT], nil, ha, hv1));
else {
- hv2 = nod(OXXX, N, N);
- tempname(hv2, types[TINT]);
+ hv2 = temp(runetype);
a = nod(OAS2, N, N);
a->list = list(list1(hv1), hv2);
fn = syslook("stringiter2", 0);
diff --git a/src/cmd/gc/reflect.c b/src/cmd/gc/reflect.c
index 810787d30..07b426508 100644
--- a/src/cmd/gc/reflect.c
+++ b/src/cmd/gc/reflect.c
@@ -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 <u.h>
+#include <libc.h>
#include "go.h"
/*
@@ -11,6 +13,7 @@
static NodeList* signatlist;
static Sym* dtypesym(Type*);
static Sym* weaktypesym(Type*);
+static Sym* dalgsym(Type*);
static int
sigcmp(Sig *a, Sig *b)
@@ -140,13 +143,12 @@ methods(Type *t)
Type *f, *mt, *it, *this;
Sig *a, *b;
Sym *method;
- Prog *oldlist;
- // named method type
- mt = methtype(t);
+ // method type
+ mt = methtype(t, 0);
if(mt == T)
return nil;
- expandmeth(mt->sym, mt);
+ expandmeth(mt);
// type stored in interface word
it = t;
@@ -156,12 +158,14 @@ methods(Type *t)
// make list of methods for t,
// generating code if necessary.
a = nil;
- oldlist = nil;
for(f=mt->xmethod; f; f=f->down) {
- if(f->type->etype != TFUNC)
- continue;
if(f->etype != TFIELD)
- fatal("methods: not field");
+ fatal("methods: not field %T", f);
+ if (f->type->etype != TFUNC || f->type->thistuple == 0)
+ fatal("non-method on %T method %S %T\n", mt, f->sym, f);
+ if (!getthisx(f->type)->type)
+ fatal("receiver with no type on %T method %S %T\n", mt, f->sym, f);
+
method = f->sym;
if(method == nil)
continue;
@@ -195,8 +199,6 @@ methods(Type *t)
if(!(a->isym->flags & SymSiggen)) {
a->isym->flags |= SymSiggen;
if(!eqtype(this, it) || this->width < types[tptr]->width) {
- if(oldlist == nil)
- oldlist = pc;
// Is okay to call genwrapper here always,
// but we can generate more efficient code
// using genembedtramp if all that is necessary
@@ -212,8 +214,6 @@ methods(Type *t)
if(!(a->tsym->flags & SymSiggen)) {
a->tsym->flags |= SymSiggen;
if(!eqtype(this, t)) {
- if(oldlist == nil)
- oldlist = pc;
if(isptr[t->etype] && isptr[this->etype]
&& f->embedded && !isifacemethod(f->type))
genembedtramp(t, f, a->tsym, 0);
@@ -223,16 +223,6 @@ methods(Type *t)
}
}
- // restore data output
- if(oldlist) {
- // old list ended with AEND; change to ANOP
- // so that the trampolines that follow can be found.
- nopout(oldlist);
-
- // start new data list
- newplist();
- }
-
return lsort(a, sigcmp);
}
@@ -245,11 +235,9 @@ imethods(Type *t)
Sig *a, *all, *last;
Type *f;
Sym *method, *isym;
- Prog *oldlist;
all = nil;
last = nil;
- oldlist = nil;
for(f=t->type; f; f=f->down) {
if(f->etype != TFIELD)
fatal("imethods: not field");
@@ -287,21 +275,9 @@ imethods(Type *t)
isym = methodsym(method, t, 0);
if(!(isym->flags & SymSiggen)) {
isym->flags |= SymSiggen;
- if(oldlist == nil)
- oldlist = pc;
genwrapper(t, f, isym, 0);
}
}
-
- if(oldlist) {
- // old list ended with AEND; change to ANOP
- // so that the trampolines that follow can be found.
- nopout(oldlist);
-
- // start new data list
- newplist();
- }
-
return all;
}
@@ -381,7 +357,7 @@ dextratype(Sym *sym, int off, Type *t, int ptroff)
s = sym;
if(t->sym) {
ot = dgostringptr(s, ot, t->sym->name);
- if(t != types[t->etype])
+ if(t != types[t->etype] && t != errortype)
ot = dgopkgpath(s, ot, t->sym->pkg);
else
ot = dgostringptr(s, ot, nil);
@@ -478,57 +454,20 @@ kinds[] =
[TUNSAFEPTR] = KindUnsafePointer,
};
-static char*
-structnames[] =
-{
- [TINT] = "*runtime.IntType",
- [TUINT] = "*runtime.UintType",
- [TINT8] = "*runtime.IntType",
- [TUINT8] = "*runtime.UintType",
- [TINT16] = "*runtime.IntType",
- [TUINT16] = "*runtime.UintType",
- [TINT32] = "*runtime.IntType",
- [TUINT32] = "*runtime.UintType",
- [TINT64] = "*runtime.IntType",
- [TUINT64] = "*runtime.UintType",
- [TUINTPTR] = "*runtime.UintType",
- [TCOMPLEX64] = "*runtime.ComplexType",
- [TCOMPLEX128] = "*runtime.ComplexType",
- [TFLOAT32] = "*runtime.FloatType",
- [TFLOAT64] = "*runtime.FloatType",
- [TBOOL] = "*runtime.BoolType",
- [TSTRING] = "*runtime.StringType",
- [TUNSAFEPTR] = "*runtime.UnsafePointerType",
-
- [TPTR32] = "*runtime.PtrType",
- [TPTR64] = "*runtime.PtrType",
- [TSTRUCT] = "*runtime.StructType",
- [TINTER] = "*runtime.InterfaceType",
- [TCHAN] = "*runtime.ChanType",
- [TMAP] = "*runtime.MapType",
- [TARRAY] = "*runtime.ArrayType",
- [TFUNC] = "*runtime.FuncType",
-};
-
static Sym*
typestruct(Type *t)
{
- char *name;
- int et;
-
- et = t->etype;
- if(et < 0 || et >= nelem(structnames) || (name = structnames[et]) == nil) {
- fatal("typestruct %lT", t);
- return nil; // silence gcc
- }
-
- if(isslice(t))
- name = "*runtime.SliceType";
-
- return pkglookup(name, typepkg);
+ // We use a weak reference to the reflect type
+ // to avoid requiring package reflect in every binary.
+ // If package reflect is available, the interface{} holding
+ // a runtime type will contain a *reflect.commonType.
+ // Otherwise it will use a nil type word but still be usable
+ // by package runtime (because we always use the memory
+ // after the interface value, not the interface value itself).
+ return pkglookup("*reflect.commonType", weaktypepkg);
}
-static int
+int
haspointers(Type *t)
{
Type *t1;
@@ -578,13 +517,20 @@ haspointers(Type *t)
static int
dcommontype(Sym *s, int ot, Type *t)
{
- int i;
- Sym *sptr;
+ int i, alg, sizeofAlg;
+ Sym *sptr, *algsym;
+ static Sym *algarray;
char *p;
+ sizeofAlg = 4*widthptr;
+ if(algarray == nil)
+ algarray = pkglookup("algarray", runtimepkg);
+ alg = algtype(t);
+ algsym = S;
+ if(alg < 0)
+ algsym = dalgsym(t);
+
dowidth(t);
-
- sptr = nil;
if(t->sym != nil && !isptr[t->etype])
sptr = dtypesym(ptrto(t));
else
@@ -597,7 +543,7 @@ dcommontype(Sym *s, int ot, Type *t)
ot = dsymptr(s, ot, typestruct(t), 0);
ot = dsymptr(s, ot, s, 2*widthptr);
- // ../../pkg/runtime/type.go:/commonType
+ // ../../pkg/reflect/type.go:/^type.commonType
// actual type structure
// type commonType struct {
// size uintptr;
@@ -612,7 +558,7 @@ dcommontype(Sym *s, int ot, Type *t)
// }
ot = duintptr(s, ot, t->width);
ot = duint32(s, ot, typehash(t));
- ot = duint8(s, ot, algtype(t));
+ ot = duint8(s, ot, 0); // unused
ot = duint8(s, ot, t->align); // align
ot = duint8(s, ot, t->align); // fieldAlign
i = kinds[t->etype];
@@ -621,9 +567,12 @@ dcommontype(Sym *s, int ot, Type *t)
if(!haspointers(t))
i |= KindNoPointers;
ot = duint8(s, ot, i); // kind
- longsymnames = 1;
- p = smprint("%-T", t);
- longsymnames = 0;
+ if(alg >= 0)
+ ot = dsymptr(s, ot, algarray, alg*sizeofAlg);
+ else
+ ot = dsymptr(s, ot, algsym, 0);
+ p = smprint("%-uT", t);
+ //print("dcommontype: %s\n", p);
ot = dgostringptr(s, ot, p); // string
free(p);
@@ -643,8 +592,22 @@ typesym(Type *t)
char *p;
Sym *s;
- p = smprint("%#-T", t);
+ p = smprint("%-T", t);
s = pkglookup(p, typepkg);
+ //print("typesym: %s -> %+S\n", p, s);
+ free(p);
+ return s;
+}
+
+Sym*
+typesymprefix(char *prefix, Type *t)
+{
+ char *p;
+ Sym *s;
+
+ p = smprint("%s.%-T", prefix, t);
+ s = pkglookup(p, typepkg);
+ //print("algsym: %s -> %+S\n", p, s);
free(p);
return s;
}
@@ -683,16 +646,10 @@ weaktypesym(Type *t)
{
char *p;
Sym *s;
- static Pkg *weak;
-
- if(weak == nil) {
- weak = mkpkg(strlit("weak.type"));
- weak->name = "weak.type";
- weak->prefix = "weak.type"; // not weak%2etype
- }
-
- p = smprint("%#-T", t);
- s = pkglookup(p, weak);
+
+ p = smprint("%-T", t);
+ s = pkglookup(p, weaktypepkg);
+ //print("weaktypesym: %s -> %+S\n", p, s);
free(p);
return s;
}
@@ -721,8 +678,13 @@ dtypesym(Type *t)
tbase = t->type;
dupok = tbase->sym == S;
- if(compiling_runtime && tbase == types[tbase->etype]) // int, float, etc
+ if(compiling_runtime &&
+ (tbase == types[tbase->etype] ||
+ tbase == bytetype ||
+ tbase == runetype ||
+ tbase == errortype)) { // int, float, etc
goto ok;
+ }
// named types from other files are defined only by those files
if(tbase->sym && !tbase->local)
@@ -931,9 +893,56 @@ dumptypestructs(void)
dtypesym(ptrto(types[i]));
dtypesym(ptrto(types[TSTRING]));
dtypesym(ptrto(types[TUNSAFEPTR]));
+
+ // emit type structs for error and func(error) string.
+ // The latter is the type of an auto-generated wrapper.
+ dtypesym(ptrto(errortype));
+ dtypesym(functype(nil,
+ list1(nod(ODCLFIELD, N, typenod(errortype))),
+ list1(nod(ODCLFIELD, N, typenod(types[TSTRING])))));
// add paths for runtime and main, which 6l imports implicitly.
dimportpath(runtimepkg);
dimportpath(mkpkg(strlit("main")));
}
}
+
+static Sym*
+dalgsym(Type *t)
+{
+ int ot;
+ Sym *s, *hash, *eq;
+ char buf[100];
+
+ // dalgsym is only called for a type that needs an algorithm table,
+ // which implies that the type is comparable (or else it would use ANOEQ).
+
+ s = typesymprefix(".alg", t);
+ hash = typesymprefix(".hash", t);
+ genhash(hash, t);
+ eq = typesymprefix(".eq", t);
+ geneq(eq, t);
+
+ // ../../pkg/runtime/runtime.h:/Alg
+ ot = 0;
+ ot = dsymptr(s, ot, hash, 0);
+ ot = dsymptr(s, ot, eq, 0);
+ ot = dsymptr(s, ot, pkglookup("memprint", runtimepkg), 0);
+ switch(t->width) {
+ default:
+ ot = dsymptr(s, ot, pkglookup("memcopy", runtimepkg), 0);
+ break;
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ case 16:
+ snprint(buf, sizeof buf, "memcopy%d", (int)t->width*8);
+ ot = dsymptr(s, ot, pkglookup(buf, runtimepkg), 0);
+ break;
+ }
+
+ ggloblsym(s, ot, 1);
+ return s;
+}
+
diff --git a/src/cmd/gc/runtime.go b/src/cmd/gc/runtime.go
index 549f7abe3..15a61d9ef 100644
--- a/src/cmd/gc/runtime.go
+++ b/src/cmd/gc/runtime.go
@@ -3,14 +3,16 @@
// license that can be found in the LICENSE file.
// NOTE: If you change this file you must run "./mkbuiltin"
-// to update builtin.c.boot. This is not done automatically
+// to update builtin.c. This is not done automatically
// to avoid depending on having a working compiler binary.
+// +build ignore
+
package PACKAGE
// emitted by compiler, not referred to by go programs
-func new(int32) *any
+func new(typ *byte) *any
func panicindex()
func panicslice()
func throwreturn()
@@ -40,18 +42,19 @@ 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 slicestring(string, int, int) string
func slicestring1(string, int) string
func intstring(int64) string
func slicebytetostring([]byte) string
-func sliceinttostring([]int) string
+func slicerunetostring([]rune) string
func stringtoslicebyte(string) []byte
-func stringtosliceint(string) []int
+func stringtoslicerune(string) []rune
func stringiter(string, int) int
-func stringiter2(string, int) (retk int, retv int)
-func slicecopy(to any, fr any, wid uint32) int
+func stringiter2(string, int) (retk int, retv rune)
+func copy(to any, fr any, wid uint32) int
func slicestringcopy(to any, fr any) int
// interface conversions
@@ -79,6 +82,8 @@ func efaceeq(i1 any, i2 any) (ret bool)
func ifacethash(i1 any) (ret uint32)
func efacethash(i1 any) (ret uint32)
+func equal(typ *byte, x1, x2 any) (ret bool)
+
// *byte is really *runtime.Type
func makemap(mapType *byte, hint int64) (hmap map[any]any)
func mapaccess1(mapType *byte, hmap map[any]any, key any) (val any)
@@ -86,6 +91,7 @@ func mapaccess2(mapType *byte, hmap map[any]any, key any) (val any, pres bool)
func mapassign1(mapType *byte, hmap map[any]any, key any, val any)
func mapassign2(mapType *byte, hmap map[any]any, key any, val any, pres bool)
func mapiterinit(mapType *byte, hmap map[any]any, hiter *any)
+func mapdelete(mapType *byte, hmap map[any]any, key any)
func mapiternext(hiter *any)
func mapiter1(hiter *any) (key any)
func mapiter2(hiter *any) (key any, val any)
@@ -117,6 +123,13 @@ func slicearray(old *any, nel uint64, lb uint64, hb uint64, width uint64) (ary [
func closure() // has args, but compiler fills in
+func memequal(eq *bool, size uintptr, x, y *any)
+func memequal8(eq *bool, size uintptr, x, y *any)
+func memequal16(eq *bool, size uintptr, x, y *any)
+func memequal32(eq *bool, size uintptr, x, y *any)
+func memequal64(eq *bool, size uintptr, x, y *any)
+func memequal128(eq *bool, size uintptr, x, y *any)
+
// only used on 32-bit
func int64div(int64, int64) int64
func uint64div(uint64, uint64) uint64
diff --git a/src/cmd/gc/select.c b/src/cmd/gc/select.c
index 909ad3aa4..8ace1d4ee 100644
--- a/src/cmd/gc/select.c
+++ b/src/cmd/gc/select.c
@@ -6,6 +6,8 @@
* select
*/
+#include <u.h>
+#include <libc.h>
#include "go.h"
void
@@ -59,7 +61,7 @@ typecheckselect(Node *sel)
break;
case OAS2RECV:
- // convert x, ok = <-c into OSELRECV(x, <-c) with ntest=ok
+ // convert x, ok = <-c into OSELRECV2(x, <-c) with ntest=ok
if(n->right->op != ORECV) {
yyerror("select assignment must have receive on right hand side");
break;
@@ -73,6 +75,7 @@ typecheckselect(Node *sel)
case ORECV:
// convert <-c into OSELRECV(N, <-c)
n = nod(OSELRECV, N, n);
+ n->typecheck = 1;
ncase->left = n;
break;
@@ -191,8 +194,7 @@ walkselect(Node *sel)
n->ntest->etype = 1; // pointer does not escape
typecheck(&n->ntest, Erv);
} else {
- tmp = nod(OXXX, N, N);
- tempname(tmp, types[TBOOL]);
+ tmp = temp(types[TBOOL]);
a = nod(OADDR, tmp, N);
a->etype = 1; // pointer does not escape
typecheck(&a, Erv);
@@ -212,8 +214,7 @@ walkselect(Node *sel)
n->left->etype = 1; // pointer does not escape
typecheck(&n->left, Erv);
} else {
- tmp = nod(OXXX, N, N);
- tempname(tmp, ch->type->type);
+ tmp = temp(ch->type->type);
a = nod(OADDR, tmp, N);
a->etype = 1; // pointer does not escape
typecheck(&a, Erv);
@@ -284,8 +285,7 @@ walkselect(Node *sel)
// generate sel-struct
setlineno(sel);
- var = nod(OXXX, N, N);
- tempname(var, ptrto(types[TUINT8]));
+ var = temp(ptrto(types[TUINT8]));
r = nod(OAS, var, mkcall("newselect", var->type, nil, nodintconst(sel->xoffset)));
typecheck(&r, Etop);
init = list(init, r);
diff --git a/src/cmd/gc/sinit.c b/src/cmd/gc/sinit.c
index 917e2ae6d..c8796f8b7 100644
--- a/src/cmd/gc/sinit.c
+++ b/src/cmd/gc/sinit.c
@@ -6,11 +6,24 @@
* static initialization
*/
+#include <u.h>
+#include <libc.h>
#include "go.h"
+enum
+{
+ InitNotStarted = 0,
+ InitDone = 1,
+ InitPending = 2,
+};
+
+static int iszero(Node*);
+static void initplan(Node*);
static NodeList *initlist;
static void init2(Node*, NodeList**);
static void init2list(NodeList*, NodeList**);
+static int staticinit(Node*, NodeList**);
+static Node *staticname(Type*, int);
static void
init1(Node *n, NodeList **out)
@@ -31,16 +44,16 @@ init1(Node *n, NodeList **out)
case PFUNC:
break;
default:
- if(isblank(n) && n->defn != N && !n->defn->initorder) {
- n->defn->initorder = 1;
+ if(isblank(n) && n->defn != N && n->defn->initorder == InitNotStarted) {
+ n->defn->initorder = InitDone;
*out = list(*out, n->defn);
}
return;
}
- if(n->initorder == 1)
+ if(n->initorder == InitDone)
return;
- if(n->initorder == 2) {
+ if(n->initorder == InitPending) {
if(n->class == PFUNC)
return;
@@ -52,7 +65,7 @@ init1(Node *n, NodeList **out)
if(nerrors > 0)
errorexit();
- print("initialization loop:\n");
+ print("%L: initialization loop:\n", n->lineno);
for(l=initlist;; l=l->next) {
if(l->next == nil)
break;
@@ -63,7 +76,7 @@ init1(Node *n, NodeList **out)
print("\t%L %S\n", n->lineno, n->sym);
errorexit();
}
- n->initorder = 2;
+ n->initorder = InitPending;
l = malloc(sizeof *l);
l->next = initlist;
l->n = n;
@@ -84,20 +97,38 @@ init1(Node *n, NodeList **out)
case OAS:
if(n->defn->left != n)
goto bad;
+ /*
n->defn->dodata = 1;
init1(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(!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);
+ *out = list(*out, n->defn);
+ }
+ break;
case OAS2FUNC:
case OAS2MAPR:
case OAS2DOTTYPE:
case OAS2RECV:
- if(n->defn->initorder)
+ if(n->defn->initorder != InitNotStarted)
break;
- n->defn->initorder = 1;
+ n->defn->initorder = InitDone;
for(l=n->defn->rlist; l; l=l->next)
init1(l->n, out);
*out = list(*out, n->defn);
@@ -109,7 +140,7 @@ init1(Node *n, NodeList **out)
if(l->n != n)
fatal("bad initlist");
free(l);
- n->initorder = 1;
+ n->initorder = InitDone;
return;
bad:
@@ -121,8 +152,12 @@ bad:
static void
init2(Node *n, NodeList **out)
{
- if(n == N || n->initorder == 1)
+ if(n == N || n->initorder == InitDone)
return;
+
+ if(n->op == ONAME && n->ninit)
+ fatal("name %S with ninit: %+N\n", n->sym, n);
+
init1(n, out);
init2(n->left, out);
init2(n->right, out);
@@ -141,7 +176,6 @@ init2list(NodeList *l, NodeList **out)
init2(l->n, out);
}
-
static void
initreorder(NodeList *l, NodeList **out)
{
@@ -165,13 +199,235 @@ NodeList*
initfix(NodeList *l)
{
NodeList *lout;
+ int lno;
lout = nil;
+ lno = lineno;
initreorder(l, &lout);
+ lineno = lno;
return lout;
}
/*
+ * compilation of top-level (static) assignments
+ * into DATA statements if at all possible.
+ */
+
+static int staticassign(Node*, Node*, NodeList**);
+
+static int
+staticinit(Node *n, NodeList **out)
+{
+ Node *l, *r;
+
+ if(n->op != ONAME || n->class != PEXTERN || n->defn == N || n->defn->op != OAS)
+ fatal("staticinit");
+
+ lineno = n->lineno;
+ l = n->defn->left;
+ r = n->defn->right;
+ return staticassign(l, r, out);
+}
+
+// like staticassign but we are copying an already
+// initialized value r.
+static int
+staticcopy(Node *l, Node *r, NodeList **out)
+{
+ int i;
+ InitEntry *e;
+ InitPlan *p;
+ Node *a, *ll, *rr, *orig, n1;
+
+ if(r->op != ONAME || r->class != PEXTERN || r->sym->pkg != localpkg)
+ return 0;
+ if(r->defn == N) // zeroed
+ return 1;
+ if(r->defn->op != OAS)
+ return 0;
+ orig = r;
+ r = r->defn->right;
+
+ switch(r->op) {
+ case ONAME:
+ if(staticcopy(l, r, out))
+ return 1;
+ *out = list(*out, nod(OAS, l, r));
+ return 1;
+
+ case OLITERAL:
+ if(iszero(r))
+ return 1;
+ gdata(l, r, l->type->width);
+ return 1;
+
+ case OADDR:
+ switch(r->left->op) {
+ case ONAME:
+ gdata(l, r, l->type->width);
+ return 1;
+ }
+ break;
+
+ case OPTRLIT:
+ switch(r->left->op) {
+ default:
+ //dump("not static addr", r);
+ break;
+ case OARRAYLIT:
+ case OSTRUCTLIT:
+ case OMAPLIT:
+ // copy pointer
+ gdata(l, nod(OADDR, r->nname, N), l->type->width);
+ return 1;
+ }
+ break;
+
+ case OARRAYLIT:
+ if(isslice(r->type)) {
+ // copy slice
+ a = r->nname;
+ n1 = *l;
+ n1.xoffset = l->xoffset + Array_array;
+ gdata(&n1, nod(OADDR, a, N), widthptr);
+ n1.xoffset = l->xoffset + Array_nel;
+ gdata(&n1, r->right, 4);
+ n1.xoffset = l->xoffset + Array_cap;
+ gdata(&n1, r->right, 4);
+ return 1;
+ }
+ // fall through
+ case OSTRUCTLIT:
+ p = r->initplan;
+ n1 = *l;
+ for(i=0; i<p->len; i++) {
+ e = &p->e[i];
+ n1.xoffset = l->xoffset + e->xoffset;
+ n1.type = e->expr->type;
+ if(e->expr->op == OLITERAL)
+ gdata(&n1, e->expr, n1.type->width);
+ else {
+ ll = nod(OXXX, N, N);
+ *ll = n1;
+ if(!staticassign(ll, e->expr, out)) {
+ // Requires computation, but we're
+ // copying someone else's computation.
+ rr = nod(OXXX, N, N);
+ *rr = *orig;
+ rr->type = ll->type;
+ rr->xoffset += e->xoffset;
+ *out = list(*out, nod(OAS, ll, rr));
+ }
+ }
+ }
+ return 1;
+ }
+ return 0;
+}
+
+static int
+staticassign(Node *l, Node *r, NodeList **out)
+{
+ Node *a, n1;
+ Type *ta;
+ InitPlan *p;
+ InitEntry *e;
+ int i;
+
+ switch(r->op) {
+ default:
+ //dump("not static", r);
+ break;
+
+ case ONAME:
+ if(r->class == PEXTERN && r->sym->pkg == localpkg)
+ return staticcopy(l, r, out);
+ break;
+
+ case OLITERAL:
+ if(iszero(r))
+ return 1;
+ gdata(l, r, l->type->width);
+ return 1;
+
+ case OADDR:
+ switch(r->left->op) {
+ default:
+ //dump("not static addr", r);
+ break;
+
+ case ONAME:
+ gdata(l, r, l->type->width);
+ return 1;
+ }
+
+ case OPTRLIT:
+ switch(r->left->op) {
+ default:
+ //dump("not static ptrlit", r);
+ break;
+
+ case OARRAYLIT:
+ case OMAPLIT:
+ case OSTRUCTLIT:
+ // Init pointer.
+ a = staticname(r->left->type, 1);
+ r->nname = a;
+ gdata(l, nod(OADDR, a, N), l->type->width);
+ // Init underlying literal.
+ if(!staticassign(a, r->left, out))
+ *out = list(*out, nod(OAS, a, r->left));
+ return 1;
+ }
+ break;
+
+ case OARRAYLIT:
+ initplan(r);
+ if(isslice(r->type)) {
+ // Init slice.
+ ta = typ(TARRAY);
+ ta->type = r->type->type;
+ ta->bound = mpgetfix(r->right->val.u.xval);
+ a = staticname(ta, 1);
+ r->nname = a;
+ n1 = *l;
+ n1.xoffset = l->xoffset + Array_array;
+ gdata(&n1, nod(OADDR, a, N), widthptr);
+ n1.xoffset = l->xoffset + Array_nel;
+ gdata(&n1, r->right, 4);
+ n1.xoffset = l->xoffset + Array_cap;
+ gdata(&n1, r->right, 4);
+ // Fall through to init underlying array.
+ l = a;
+ }
+ // fall through
+ case OSTRUCTLIT:
+ initplan(r);
+ p = r->initplan;
+ n1 = *l;
+ for(i=0; i<p->len; i++) {
+ e = &p->e[i];
+ n1.xoffset = l->xoffset + e->xoffset;
+ n1.type = e->expr->type;
+ if(e->expr->op == OLITERAL)
+ gdata(&n1, e->expr, n1.type->width);
+ else {
+ a = nod(OXXX, N, N);
+ *a = n1;
+ if(!staticassign(a, e->expr, out))
+ *out = list(*out, nod(OAS, a, e->expr));
+ }
+ }
+ return 1;
+
+ case OMAPLIT:
+ // TODO: Table-driven map insert.
+ break;
+ }
+ return 0;
+}
+
+/*
* from here down is the walk analysis
* of composite literals.
* most of the work is to generate
@@ -408,7 +664,6 @@ slicelit(int ctxt, Node *n, Node *var, NodeList **init)
dowidth(t);
if(ctxt != 0) {
-
// put everything into static array
vstat = staticname(t, ctxt);
arraylit(ctxt, 1, n, vstat, init);
@@ -452,12 +707,18 @@ slicelit(int ctxt, Node *n, Node *var, NodeList **init)
}
// make new auto *array (3 declare)
- vauto = nod(OXXX, N, N);
- tempname(vauto, ptrto(t));
+ vauto = temp(ptrto(t));
- // set auto to point at new heap (3 assign)
- a = nod(ONEW, N, N);
- a->list = list1(typenod(t));
+ // set auto to point at new temp or heap (3 assign)
+ if(n->esc == EscNone) {
+ a = nod(OAS, temp(t), N);
+ typecheck(&a, Etop);
+ *init = list(*init, a); // zero new temp
+ a = nod(OADDR, a->left, N);
+ } else {
+ a = nod(ONEW, N, N);
+ a->list = list1(typenod(t));
+ }
a = nod(OAS, vauto, a);
typecheck(&a, Etop);
walkexpr(&a, init);
@@ -522,6 +783,7 @@ maplit(int ctxt, Node *n, Node *var, NodeList **init)
Node *vstat, *index, *value;
Sym *syma, *symb;
+USED(ctxt);
ctxt = 0;
// make the map var
@@ -545,7 +807,6 @@ ctxt = 0;
b++;
}
- t = T;
if(b != 0) {
// build type [count]struct { a Tindex, b Tvalue }
t = n->type;
@@ -615,8 +876,7 @@ ctxt = 0;
// for i = 0; i < len(vstat); i++ {
// map[vstat[i].a] = vstat[i].b
// }
- index = nod(OXXX, N, N);
- tempname(index, types[TINT]);
+ index = temp(types[TINT]);
a = nod(OINDEX, vstat, index);
a->etype = 1; // no bounds checking
@@ -677,6 +937,19 @@ anylit(int ctxt, Node *n, Node *var, NodeList **init)
default:
fatal("anylit: not lit");
+ case OPTRLIT:
+ if(!isptr[t->etype])
+ fatal("anylit: not ptr");
+
+ a = nod(OAS, var, callnew(t->type));
+ typecheck(&a, Etop);
+ *init = list(*init, a);
+
+ var = nod(OIND, var, N);
+ typecheck(&var, Erv | Easgn);
+ anylit(ctxt, n->left, var, init);
+ break;
+
case OSTRUCTLIT:
if(t->etype != TSTRUCT)
fatal("anylit: not struct");
@@ -917,18 +1190,15 @@ gen_as_init(Node *n)
case TPTR64:
case TFLOAT32:
case TFLOAT64:
- gused(N); // in case the data is the dest of a goto
gdata(&nam, nr, nr->type->width);
break;
case TCOMPLEX64:
case TCOMPLEX128:
- gused(N); // in case the data is the dest of a goto
gdatacomplex(&nam, nr->val.u.cval);
break;
case TSTRING:
- gused(N); // in case the data is the dest of a goto
gdatastring(&nam, nr->val.u.sval);
break;
}
@@ -969,3 +1239,152 @@ no:
return 0;
}
+static int iszero(Node*);
+static int isvaluelit(Node*);
+static InitEntry* entry(InitPlan*);
+static void addvalue(InitPlan*, vlong, Node*, Node*);
+
+static void
+initplan(Node *n)
+{
+ InitPlan *p;
+ Node *a;
+ NodeList *l;
+
+ if(n->initplan != nil)
+ return;
+ p = mal(sizeof *p);
+ n->initplan = p;
+ switch(n->op) {
+ default:
+ fatal("initplan");
+ case OARRAYLIT:
+ for(l=n->list; l; l=l->next) {
+ a = l->n;
+ if(a->op != OKEY || !smallintconst(a->left))
+ fatal("initplan arraylit");
+ addvalue(p, n->type->type->width*mpgetfix(a->left->val.u.xval), N, a->right);
+ }
+ break;
+ case OSTRUCTLIT:
+ for(l=n->list; l; l=l->next) {
+ a = l->n;
+ if(a->op != OKEY || a->left->type == T)
+ fatal("initplan structlit");
+ addvalue(p, a->left->type->width, N, a->right);
+ }
+ break;
+ case OMAPLIT:
+ for(l=n->list; l; l=l->next) {
+ a = l->n;
+ if(a->op != OKEY)
+ fatal("initplan maplit");
+ addvalue(p, -1, a->left, a->right);
+ }
+ break;
+ }
+}
+
+static void
+addvalue(InitPlan *p, vlong xoffset, Node *key, Node *n)
+{
+ int i;
+ InitPlan *q;
+ InitEntry *e;
+
+ USED(key);
+
+ // special case: zero can be dropped entirely
+ if(iszero(n)) {
+ p->zero += n->type->width;
+ return;
+ }
+
+ // special case: inline struct and array (not slice) literals
+ if(isvaluelit(n)) {
+ initplan(n);
+ q = n->initplan;
+ for(i=0; i<q->len; i++) {
+ e = entry(p);
+ *e = q->e[i];
+ e->xoffset += xoffset;
+ }
+ return;
+ }
+
+ // add to plan
+ if(n->op == OLITERAL)
+ p->lit += n->type->width;
+ else
+ p->expr += n->type->width;
+
+ e = entry(p);
+ e->xoffset = xoffset;
+ e->expr = n;
+}
+
+static int
+iszero(Node *n)
+{
+ NodeList *l;
+
+ switch(n->op) {
+ case OLITERAL:
+ switch(n->val.ctype) {
+ default:
+ dump("unexpected literal", n);
+ fatal("iszero");
+
+ case CTNIL:
+ return 1;
+
+ case CTSTR:
+ return n->val.u.sval == nil || n->val.u.sval->len == 0;
+
+ case CTBOOL:
+ return n->val.u.bval == 0;
+
+ case CTINT:
+ case CTRUNE:
+ return mpcmpfixc(n->val.u.xval, 0) == 0;
+
+ case CTFLT:
+ return mpcmpfltc(n->val.u.fval, 0) == 0;
+
+ case CTCPLX:
+ return mpcmpfltc(&n->val.u.cval->real, 0) == 0 && mpcmpfltc(&n->val.u.cval->imag, 0) == 0;
+ }
+ break;
+ case OARRAYLIT:
+ if(isslice(n->type))
+ break;
+ // fall through
+ case OSTRUCTLIT:
+ for(l=n->list; l; l=l->next)
+ if(!iszero(l->n->right))
+ return 0;
+ return 1;
+ }
+ return 0;
+}
+
+static int
+isvaluelit(Node *n)
+{
+ return (n->op == OARRAYLIT && isfixedarray(n->type)) || n->op == OSTRUCTLIT;
+}
+
+static InitEntry*
+entry(InitPlan *p)
+{
+ if(p->len >= p->cap) {
+ if(p->cap == 0)
+ p->cap = 4;
+ else
+ p->cap *= 2;
+ p->e = realloc(p->e, p->cap*sizeof p->e[0]);
+ if(p->e == nil)
+ fatal("out of memory");
+ }
+ return &p->e[p->len++];
+}
diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c
index 1a05d43d0..681c023a0 100644
--- a/src/cmd/gc/subr.c
+++ b/src/cmd/gc/subr.c
@@ -2,14 +2,13 @@
// 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"
#include "md5.h"
#include "y.tab.h"
-#include "opnames.h"
#include "yerr.h"
-static void dodump(Node*, int);
-
typedef struct Error Error;
struct Error
{
@@ -27,7 +26,7 @@ errorexit(void)
flusherrors();
if(outfile)
remove(outfile);
- exit(1);
+ exits("error");
}
extern int yychar;
@@ -45,12 +44,10 @@ adderr(int line, char *fmt, va_list arg)
Fmt f;
Error *p;
- erroring++;
fmtstrinit(&f);
fmtprint(&f, "%L: ", line);
fmtvprint(&f, fmt, arg);
fmtprint(&f, "\n");
- erroring--;
if(nerr >= merr) {
if(merr == 0)
@@ -106,7 +103,7 @@ hcrash(void)
if(debug['h']) {
flusherrors();
if(outfile)
- unlink(outfile);
+ remove(outfile);
*(volatile int*)0 = 0;
}
}
@@ -122,7 +119,7 @@ yyerrorl(int line, char *fmt, ...)
hcrash();
nerrors++;
- if(nerrors >= 10 && !debug['e']) {
+ if(nsavederrors+nerrors >= 10 && !debug['e']) {
flusherrors();
print("%L: too many errors\n", line);
errorexit();
@@ -190,7 +187,7 @@ yyerror(char *fmt, ...)
hcrash();
nerrors++;
- if(nerrors >= 10 && !debug['e']) {
+ if(nsavederrors+nerrors >= 10 && !debug['e']) {
flusherrors();
print("%L: too many errors\n", parserline());
errorexit();
@@ -210,6 +207,16 @@ warn(char *fmt, ...)
}
void
+warnl(int line, char *fmt, ...)
+{
+ va_list arg;
+
+ va_start(arg, fmt);
+ adderr(line, fmt, arg);
+ va_end(arg);
+}
+
+void
fatal(char *fmt, ...)
{
va_list arg;
@@ -393,7 +400,7 @@ importdot(Pkg *opkg, Node *pack)
}
if(n == 0) {
// can't possibly be used - there were no symbols
- yyerrorl(pack->lineno, "imported and not used: %Z", opkg->path);
+ yyerrorl(pack->lineno, "imported and not used: \"%Z\"", opkg->path);
}
}
@@ -483,49 +490,125 @@ nod(int op, Node *nleft, Node *nright)
n->lineno = parserline();
n->xoffset = BADWIDTH;
n->orig = n;
+ n->curfn = curfn;
return n;
}
int
+algtype1(Type *t, Type **bad)
+{
+ int a, ret;
+ Type *t1;
+
+ if(bad)
+ *bad = 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 TPTR32:
+ case TPTR64:
+ case TCHAN:
+ case TUNSAFEPTR:
+ return AMEM;
+
+ case TFUNC:
+ case TMAP:
+ if(bad)
+ *bad = t;
+ return ANOEQ;
+
+ case TFLOAT32:
+ return AFLOAT32;
+
+ case TFLOAT64:
+ return AFLOAT64;
+
+ case TCOMPLEX64:
+ return ACPLX64;
+
+ case TCOMPLEX128:
+ return ACPLX128;
+
+ case TSTRING:
+ return ASTRING;
+
+ case TINTER:
+ if(isnilinter(t))
+ return ANILINTER;
+ return AINTER;
+
+ case TARRAY:
+ if(isslice(t)) {
+ if(bad)
+ *bad = t;
+ return ANOEQ;
+ }
+ if(t->bound == 0)
+ return AMEM;
+ a = algtype1(t->type, bad);
+ if(a == ANOEQ || a == AMEM) {
+ if(a == ANOEQ && bad)
+ *bad = t;
+ return a;
+ }
+ return -1; // needs special compare
+
+ case TSTRUCT:
+ if(t->type != T && t->type->down == T) {
+ // 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) {
+ if(isblanksym(t1->sym))
+ continue;
+ a = algtype1(t1->type, bad);
+ if(a == ANOEQ)
+ return ANOEQ; // not comparable
+ if(a != AMEM)
+ ret = -1; // needs special compare
+ }
+ return ret;
+ }
+
+ fatal("algtype1: unexpected type %T", t);
+ return 0;
+}
+
+int
algtype(Type *t)
{
int a;
-
- if(issimple[t->etype] || isptr[t->etype] ||
- t->etype == TCHAN || t->etype == TFUNC || t->etype == TMAP) {
- if(t->width == 1)
- a = AMEM8;
- else if(t->width == 2)
- a = AMEM16;
- else if(t->width == 4)
- a = AMEM32;
- else if(t->width == 8)
- a = AMEM64;
- else if(t->width == 16)
- a = AMEM128;
- else
- a = AMEM; // just bytes (int, ptr, etc)
- } else if(t->etype == TSTRING)
- a = ASTRING; // string
- else if(isnilinter(t))
- a = ANILINTER; // nil interface
- else if(t->etype == TINTER)
- a = AINTER; // interface
- else if(isslice(t))
- a = ASLICE; // slice
- else {
- if(t->width == 1)
- a = ANOEQ8;
- else if(t->width == 2)
- a = ANOEQ16;
- else if(t->width == 4)
- a = ANOEQ32;
- else if(t->width == 8)
- a = ANOEQ64;
- else if(t->width == 16)
- a = ANOEQ128;
- else
- a = ANOEQ; // just bytes, but no hash/eq
+
+ a = algtype1(t, nil);
+ if(a == AMEM || a == ANOEQ) {
+ if(isslice(t))
+ return ASLICE;
+ switch(t->width) {
+ case 0:
+ return a + AMEM0 - AMEM;
+ case 1:
+ return a + AMEM8 - AMEM;
+ case 2:
+ return a + AMEM16 - AMEM;
+ case 4:
+ return a + AMEM32 - AMEM;
+ case 8:
+ return a + AMEM64 - AMEM;
+ case 16:
+ return a + AMEM128 - AMEM;
+ }
}
return a;
}
@@ -535,9 +618,16 @@ maptype(Type *key, Type *val)
{
Type *t;
-
- if(key != nil && key->etype != TANY && algtype(key) == ANOEQ) {
- if(key->etype == TFORW) {
+ if(key != nil) {
+ switch(key->etype) {
+ default:
+ if(algtype1(key, nil) == ANOEQ)
+ yyerror("invalid map key type %T", key);
+ break;
+ case TANY:
+ // will be resolved later.
+ break;
+ case TFORW:
// map[key] used during definition of key.
// postpone check until key is fully defined.
// if there are multiple uses of map[key]
@@ -546,8 +636,8 @@ maptype(Type *key, Type *val)
// good enough.
if(key->maplineno == 0)
key->maplineno = lineno;
- } else
- yyerror("invalid map key type %T", key);
+ break;
+ }
}
t = typ(TMAP);
t->down = key;
@@ -696,6 +786,7 @@ aindex(Node *b, Type *t)
yyerror("array bound must be an integer expression");
break;
case CTINT:
+ case CTRUNE:
bound = mpgetfix(b->val.u.xval);
if(bound < 0)
yyerror("array bound must be non negative");
@@ -710,892 +801,6 @@ aindex(Node *b, Type *t)
return r;
}
-static void
-indent(int dep)
-{
- int i;
-
- for(i=0; i<dep; i++)
- print(". ");
-}
-
-static void
-dodumplist(NodeList *l, int dep)
-{
- for(; l; l=l->next)
- dodump(l->n, dep);
-}
-
-static void
-dodump(Node *n, int dep)
-{
- if(n == N)
- return;
-
- indent(dep);
- if(dep > 10) {
- print("...\n");
- return;
- }
-
- if(n->ninit != nil) {
- print("%O-init\n", n->op);
- dodumplist(n->ninit, dep+1);
- indent(dep);
- }
-
- switch(n->op) {
- default:
- print("%N\n", n);
- dodump(n->left, dep+1);
- dodump(n->right, dep+1);
- break;
-
- case OTYPE:
- print("%O %S type=%T\n", n->op, n->sym, n->type);
- if(n->type == T && n->ntype) {
- indent(dep);
- print("%O-ntype\n", n->op);
- dodump(n->ntype, dep+1);
- }
- break;
-
- case OIF:
- print("%O%J\n", n->op, n);
- dodump(n->ntest, dep+1);
- if(n->nbody != nil) {
- indent(dep);
- print("%O-then\n", n->op);
- dodumplist(n->nbody, dep+1);
- }
- if(n->nelse != nil) {
- indent(dep);
- print("%O-else\n", n->op);
- dodumplist(n->nelse, dep+1);
- }
- break;
-
- case OSELECT:
- print("%O%J\n", n->op, n);
- dodumplist(n->nbody, dep+1);
- break;
-
- case OSWITCH:
- case OFOR:
- print("%O%J\n", n->op, n);
- dodump(n->ntest, dep+1);
-
- if(n->nbody != nil) {
- indent(dep);
- print("%O-body\n", n->op);
- dodumplist(n->nbody, dep+1);
- }
-
- if(n->nincr != N) {
- indent(dep);
- print("%O-incr\n", n->op);
- dodump(n->nincr, dep+1);
- }
- break;
-
- case OCASE:
- // the right side points to label of the body
- if(n->right != N && n->right->op == OGOTO && n->right->left->op == ONAME)
- print("%O%J GOTO %N\n", n->op, n, n->right->left);
- else
- print("%O%J\n", n->op, n);
- dodump(n->left, dep+1);
- break;
-
- case OXCASE:
- print("%N\n", n);
- dodump(n->left, dep+1);
- dodump(n->right, dep+1);
- indent(dep);
- print("%O-nbody\n", n->op);
- dodumplist(n->nbody, dep+1);
- break;
- }
-
- if(0 && n->ntype != nil) {
- indent(dep);
- print("%O-ntype\n", n->op);
- dodump(n->ntype, dep+1);
- }
- if(n->list != nil) {
- indent(dep);
- print("%O-list\n", n->op);
- dodumplist(n->list, dep+1);
- }
- if(n->rlist != nil) {
- indent(dep);
- print("%O-rlist\n", n->op);
- dodumplist(n->rlist, dep+1);
- }
- if(n->op != OIF && n->nbody != nil) {
- indent(dep);
- print("%O-nbody\n", n->op);
- dodumplist(n->nbody, dep+1);
- }
-}
-
-void
-dumplist(char *s, NodeList *l)
-{
- print("%s\n", s);
- dodumplist(l, 1);
-}
-
-void
-dump(char *s, Node *n)
-{
- print("%s [%p]\n", s, n);
- dodump(n, 1);
-}
-
-static char*
-goopnames[] =
-{
- [OADDR] = "&",
- [OADD] = "+",
- [OANDAND] = "&&",
- [OANDNOT] = "&^",
- [OAND] = "&",
- [OAPPEND] = "append",
- [OAS] = "=",
- [OAS2] = "=",
- [OBREAK] = "break",
- [OCALL] = "function call",
- [OCAP] = "cap",
- [OCASE] = "case",
- [OCLOSE] = "close",
- [OCOMPLEX] = "complex",
- [OCOM] = "^",
- [OCONTINUE] = "continue",
- [OCOPY] = "copy",
- [ODEC] = "--",
- [ODEFER] = "defer",
- [ODIV] = "/",
- [OEQ] = "==",
- [OFALL] = "fallthrough",
- [OFOR] = "for",
- [OGE] = ">=",
- [OGOTO] = "goto",
- [OGT] = ">",
- [OIF] = "if",
- [OIMAG] = "imag",
- [OINC] = "++",
- [OIND] = "*",
- [OLEN] = "len",
- [OLE] = "<=",
- [OLSH] = "<<",
- [OLT] = "<",
- [OMAKE] = "make",
- [OMINUS] = "-",
- [OMOD] = "%",
- [OMUL] = "*",
- [ONEW] = "new",
- [ONE] = "!=",
- [ONOT] = "!",
- [OOROR] = "||",
- [OOR] = "|",
- [OPANIC] = "panic",
- [OPLUS] = "+",
- [OPRINTN] = "println",
- [OPRINT] = "print",
- [ORANGE] = "range",
- [OREAL] = "real",
- [ORECV] = "<-",
- [ORETURN] = "return",
- [ORSH] = ">>",
- [OSELECT] = "select",
- [OSEND] = "<-",
- [OSUB] = "-",
- [OSWITCH] = "switch",
- [OXOR] = "^",
-};
-
-int
-Oconv(Fmt *fp)
-{
- int o;
-
- o = va_arg(fp->args, int);
- if((fp->flags & FmtSharp) && o >= 0 && o < nelem(goopnames) && goopnames[o] != nil)
- return fmtstrcpy(fp, goopnames[o]);
- if(o < 0 || o >= nelem(opnames) || opnames[o] == nil)
- return fmtprint(fp, "O-%d", o);
- return fmtstrcpy(fp, opnames[o]);
-}
-
-int
-Lconv(Fmt *fp)
-{
- struct
- {
- Hist* incl; /* start of this include file */
- int32 idel; /* delta line number to apply to include */
- Hist* line; /* start of this #line directive */
- int32 ldel; /* delta line number to apply to #line */
- } a[HISTSZ];
- int32 lno, d;
- int i, n;
- Hist *h;
-
- lno = va_arg(fp->args, int32);
-
- n = 0;
- for(h=hist; h!=H; h=h->link) {
- if(h->offset < 0)
- continue;
- if(lno < h->line)
- break;
- if(h->name) {
- if(h->offset > 0) {
- // #line directive
- if(n > 0 && n < HISTSZ) {
- a[n-1].line = h;
- a[n-1].ldel = h->line - h->offset + 1;
- }
- } else {
- // beginning of file
- if(n < HISTSZ) {
- a[n].incl = h;
- a[n].idel = h->line;
- a[n].line = 0;
- }
- n++;
- }
- continue;
- }
- n--;
- if(n > 0 && n < HISTSZ) {
- d = h->line - a[n].incl->line;
- a[n-1].ldel += d;
- a[n-1].idel += d;
- }
- }
-
- if(n > HISTSZ)
- n = HISTSZ;
-
- for(i=n-1; i>=0; i--) {
- if(i != n-1) {
- if(fp->flags & ~(FmtWidth|FmtPrec))
- break;
- fmtprint(fp, " ");
- }
- if(debug['L'])
- fmtprint(fp, "%s/", pathname);
- if(a[i].line)
- fmtprint(fp, "%s:%d[%s:%d]",
- a[i].line->name, lno-a[i].ldel+1,
- a[i].incl->name, lno-a[i].idel+1);
- else
- fmtprint(fp, "%s:%d",
- a[i].incl->name, lno-a[i].idel+1);
- lno = a[i].incl->line - 1; // now print out start of this file
- }
- if(n == 0)
- fmtprint(fp, "<epoch>");
-
- return 0;
-}
-
-/*
-s%,%,\n%g
-s%\n+%\n%g
-s%^[ ]*T%%g
-s%,.*%%g
-s%.+% [T&] = "&",%g
-s%^ ........*\]%&~%g
-s%~ %%g
-*/
-
-static char*
-etnames[] =
-{
- [TINT] = "INT",
- [TUINT] = "UINT",
- [TINT8] = "INT8",
- [TUINT8] = "UINT8",
- [TINT16] = "INT16",
- [TUINT16] = "UINT16",
- [TINT32] = "INT32",
- [TUINT32] = "UINT32",
- [TINT64] = "INT64",
- [TUINT64] = "UINT64",
- [TUINTPTR] = "UINTPTR",
- [TFLOAT32] = "FLOAT32",
- [TFLOAT64] = "FLOAT64",
- [TCOMPLEX64] = "COMPLEX64",
- [TCOMPLEX128] = "COMPLEX128",
- [TBOOL] = "BOOL",
- [TPTR32] = "PTR32",
- [TPTR64] = "PTR64",
- [TFUNC] = "FUNC",
- [TARRAY] = "ARRAY",
- [TSTRUCT] = "STRUCT",
- [TCHAN] = "CHAN",
- [TMAP] = "MAP",
- [TINTER] = "INTER",
- [TFORW] = "FORW",
- [TFIELD] = "FIELD",
- [TSTRING] = "STRING",
- [TANY] = "ANY",
-};
-
-int
-Econv(Fmt *fp)
-{
- int et;
-
- et = va_arg(fp->args, int);
- if(et < 0 || et >= nelem(etnames) || etnames[et] == nil)
- return fmtprint(fp, "E-%d", et);
- return fmtstrcpy(fp, etnames[et]);
-}
-
-static const char* classnames[] = {
- "Pxxx",
- "PEXTERN",
- "PAUTO",
- "PPARAM",
- "PPARAMOUT",
- "PPARAMREF",
- "PFUNC",
-};
-
-int
-Jconv(Fmt *fp)
-{
- Node *n;
- char *s;
- int c;
-
- n = va_arg(fp->args, Node*);
-
- c = fp->flags&FmtShort;
-
- if(!c && n->ullman != 0)
- fmtprint(fp, " u(%d)", n->ullman);
-
- if(!c && n->addable != 0)
- fmtprint(fp, " a(%d)", n->addable);
-
- if(!c && n->vargen != 0)
- fmtprint(fp, " g(%d)", n->vargen);
-
- if(n->lineno != 0)
- fmtprint(fp, " l(%d)", n->lineno);
-
- if(!c && n->xoffset != BADWIDTH)
- fmtprint(fp, " x(%lld%+d)", n->xoffset, n->stkdelta);
-
- if(n->class != 0) {
- s = "";
- if (n->class & PHEAP) s = ",heap";
- if ((n->class & ~PHEAP) < nelem(classnames))
- fmtprint(fp, " class(%s%s)", classnames[n->class&~PHEAP], s);
- else
- fmtprint(fp, " class(%d?%s)", n->class&~PHEAP, s);
- }
-
- if(n->colas != 0)
- fmtprint(fp, " colas(%d)", n->colas);
-
- if(n->funcdepth != 0)
- fmtprint(fp, " f(%d)", n->funcdepth);
-
- if(n->noescape != 0)
- fmtprint(fp, " ne(%d)", n->noescape);
-
- if(!c && n->typecheck != 0)
- fmtprint(fp, " tc(%d)", n->typecheck);
-
- if(!c && n->dodata != 0)
- fmtprint(fp, " dd(%d)", n->dodata);
-
- if(n->isddd != 0)
- fmtprint(fp, " isddd(%d)", n->isddd);
-
- if(n->implicit != 0)
- fmtprint(fp, " implicit(%d)", n->implicit);
-
- if(!c && n->pun != 0)
- fmtprint(fp, " pun(%d)", n->pun);
-
- if(!c && n->used != 0)
- fmtprint(fp, " used(%d)", n->used);
- return 0;
-}
-
-int
-Sconv(Fmt *fp)
-{
- Sym *s;
-
- s = va_arg(fp->args, Sym*);
- if(s == S) {
- fmtstrcpy(fp, "<S>");
- return 0;
- }
-
- if(fp->flags & FmtShort)
- goto shrt;
-
- if(exporting || (fp->flags & FmtSharp)) {
- if(packagequotes)
- fmtprint(fp, "\"%Z\"", s->pkg->path);
- else
- fmtprint(fp, "%s", s->pkg->prefix);
- fmtprint(fp, ".%s", s->name);
- return 0;
- }
-
- if(s->pkg && s->pkg != localpkg || longsymnames || (fp->flags & FmtLong)) {
- // This one is for the user. If the package name
- // was used by multiple packages, give the full
- // import path to disambiguate.
- if(erroring && pkglookup(s->pkg->name, nil)->npkg > 1) {
- fmtprint(fp, "\"%Z\".%s", s->pkg->path, s->name);
- return 0;
- }
- fmtprint(fp, "%s.%s", s->pkg->name, s->name);
- return 0;
- }
-
-shrt:
- fmtstrcpy(fp, s->name);
- return 0;
-}
-
-static char*
-basicnames[] =
-{
- [TINT] = "int",
- [TUINT] = "uint",
- [TINT8] = "int8",
- [TUINT8] = "uint8",
- [TINT16] = "int16",
- [TUINT16] = "uint16",
- [TINT32] = "int32",
- [TUINT32] = "uint32",
- [TINT64] = "int64",
- [TUINT64] = "uint64",
- [TUINTPTR] = "uintptr",
- [TFLOAT32] = "float32",
- [TFLOAT64] = "float64",
- [TCOMPLEX64] = "complex64",
- [TCOMPLEX128] = "complex128",
- [TBOOL] = "bool",
- [TANY] = "any",
- [TSTRING] = "string",
- [TNIL] = "nil",
- [TIDEAL] = "ideal",
- [TBLANK] = "blank",
-};
-
-int
-Tpretty(Fmt *fp, Type *t)
-{
- Type *t1;
- Sym *s;
-
- if(0 && debug['r']) {
- debug['r'] = 0;
- fmtprint(fp, "%T (orig=%T)", t, t->orig);
- debug['r'] = 1;
- return 0;
- }
-
- if(t->etype != TFIELD
- && t->sym != S
- && !(fp->flags&FmtLong)) {
- s = t->sym;
- if(t == types[t->etype] && t->etype != TUNSAFEPTR)
- return fmtprint(fp, "%s", s->name);
- if(exporting) {
- if(fp->flags & FmtShort)
- fmtprint(fp, "%hS", s);
- else
- fmtprint(fp, "%S", s);
- if(s->pkg != localpkg)
- return 0;
- if(t->vargen)
- fmtprint(fp, "·%d", t->vargen);
- return 0;
- }
- return fmtprint(fp, "%S", s);
- }
-
- if(t->etype < nelem(basicnames) && basicnames[t->etype] != nil) {
- if(isideal(t) && t->etype != TIDEAL && t->etype != TNIL)
- fmtprint(fp, "ideal ");
- return fmtprint(fp, "%s", basicnames[t->etype]);
- }
-
- switch(t->etype) {
- case TPTR32:
- case TPTR64:
- if(fp->flags&FmtShort) // pass flag thru for methodsym
- return fmtprint(fp, "*%hT", t->type);
- return fmtprint(fp, "*%T", t->type);
-
- case TCHAN:
- switch(t->chan) {
- case Crecv:
- return fmtprint(fp, "<-chan %T", t->type);
- case Csend:
- return fmtprint(fp, "chan<- %T", t->type);
- }
- if(t->type != T && t->type->etype == TCHAN && t->type->sym == S && t->type->chan == Crecv)
- return fmtprint(fp, "chan (%T)", t->type);
- return fmtprint(fp, "chan %T", t->type);
-
- case TMAP:
- return fmtprint(fp, "map[%T] %T", t->down, t->type);
-
- case TFUNC:
- // t->type is method struct
- // t->type->down is result struct
- // t->type->down->down is arg struct
- if(t->thistuple && !(fp->flags&FmtSharp) && !(fp->flags&FmtShort)) {
- fmtprint(fp, "method(");
- for(t1=getthisx(t)->type; t1; t1=t1->down) {
- fmtprint(fp, "%T", t1);
- if(t1->down)
- fmtprint(fp, ", ");
- }
- fmtprint(fp, ")");
- }
-
- if(!(fp->flags&FmtByte))
- fmtprint(fp, "func");
- fmtprint(fp, "(");
- for(t1=getinargx(t)->type; t1; t1=t1->down) {
- if(noargnames && t1->etype == TFIELD) {
- if(t1->isddd)
- fmtprint(fp, "...%T", t1->type->type);
- else
- fmtprint(fp, "%T", t1->type);
- } else
- fmtprint(fp, "%T", t1);
- if(t1->down)
- fmtprint(fp, ", ");
- }
- fmtprint(fp, ")");
- switch(t->outtuple) {
- case 0:
- break;
- case 1:
- t1 = getoutargx(t)->type;
- if(t1 == T) {
- // failure to typecheck earlier; don't know the type
- fmtprint(fp, " ?unknown-type?");
- break;
- }
- if(t1->etype == TFIELD)
- t1 = t1->type;
- fmtprint(fp, " %T", t1);
- break;
- default:
- t1 = getoutargx(t)->type;
- fmtprint(fp, " (");
- for(; t1; t1=t1->down) {
- if(noargnames && t1->etype == TFIELD)
- fmtprint(fp, "%T", t1->type);
- else
- fmtprint(fp, "%T", t1);
- if(t1->down)
- fmtprint(fp, ", ");
- }
- fmtprint(fp, ")");
- break;
- }
- return 0;
-
- case TARRAY:
- if(t->bound >= 0)
- return fmtprint(fp, "[%d]%T", (int)t->bound, t->type);
- if(t->bound == -100)
- return fmtprint(fp, "[...]%T", t->type);
- return fmtprint(fp, "[]%T", t->type);
-
- case TINTER:
- fmtprint(fp, "interface {");
- for(t1=t->type; t1!=T; t1=t1->down) {
- fmtprint(fp, " ");
- if(exportname(t1->sym->name))
- fmtprint(fp, "%hS", t1->sym);
- else
- fmtprint(fp, "%S", t1->sym);
- fmtprint(fp, "%hhT", t1->type);
- if(t1->down)
- fmtprint(fp, ";");
- }
- return fmtprint(fp, " }");
-
- case TSTRUCT:
- if(t->funarg) {
- fmtprint(fp, "(");
- for(t1=t->type; t1!=T; t1=t1->down) {
- fmtprint(fp, "%T", t1);
- if(t1->down)
- fmtprint(fp, ", ");
- }
- return fmtprint(fp, ")");
- }
- fmtprint(fp, "struct {");
- for(t1=t->type; t1!=T; t1=t1->down) {
- fmtprint(fp, " %T", t1);
- if(t1->down)
- fmtprint(fp, ";");
- }
- return fmtprint(fp, " }");
-
- case TFIELD:
- if(t->sym == S || t->embedded) {
- if(exporting)
- fmtprint(fp, "? ");
- } else
- fmtprint(fp, "%hS ", t->sym);
- if(t->isddd)
- fmtprint(fp, "...%T", t->type->type);
- else
- fmtprint(fp, "%T", t->type);
- if(t->note) {
- fmtprint(fp, " ");
- if(exporting)
- fmtprint(fp, ":");
- fmtprint(fp, "\"%Z\"", t->note);
- }
- return 0;
-
- case TFORW:
- if(exporting)
- yyerror("undefined type %S", t->sym);
- if(t->sym)
- return fmtprint(fp, "undefined %S", t->sym);
- return fmtprint(fp, "undefined");
-
- case TUNSAFEPTR:
- if(exporting)
- return fmtprint(fp, "\"unsafe\".Pointer");
- return fmtprint(fp, "unsafe.Pointer");
- }
-
- // Don't know how to handle - fall back to detailed prints.
- return -1;
-}
-
-int
-Tconv(Fmt *fp)
-{
- Type *t, *t1;
- int r, et, sharp, minus;
-
- sharp = (fp->flags & FmtSharp);
- minus = (fp->flags & FmtLeft);
- fp->flags &= ~(FmtSharp|FmtLeft);
-
- t = va_arg(fp->args, Type*);
- if(t == T)
- return fmtstrcpy(fp, "<T>");
-
- t->trecur++;
- if(t->trecur > 5) {
- fmtprint(fp, "...");
- goto out;
- }
-
- if(!debug['t']) {
- if(sharp)
- exporting++;
- if(minus)
- noargnames++;
- r = Tpretty(fp, t);
- if(sharp)
- exporting--;
- if(minus)
- noargnames--;
- if(r >= 0) {
- t->trecur--;
- return 0;
- }
- }
-
- if(sharp || exporting)
- fatal("missing %E case during export", t->etype);
-
- et = t->etype;
- fmtprint(fp, "%E ", et);
- if(t->sym != S)
- fmtprint(fp, "<%S>", t->sym);
-
- switch(et) {
- default:
- if(t->type != T)
- fmtprint(fp, " %T", t->type);
- break;
-
- case TFIELD:
- fmtprint(fp, "%T", t->type);
- break;
-
- case TFUNC:
- if(fp->flags & FmtLong)
- fmtprint(fp, "%d%d%d(%lT,%lT)%lT",
- t->thistuple, t->intuple, t->outtuple,
- t->type, t->type->down->down, t->type->down);
- else
- fmtprint(fp, "%d%d%d(%T,%T)%T",
- t->thistuple, t->intuple, t->outtuple,
- t->type, t->type->down->down, t->type->down);
- break;
-
- case TINTER:
- fmtprint(fp, "{");
- if(fp->flags & FmtLong)
- for(t1=t->type; t1!=T; t1=t1->down)
- fmtprint(fp, "%lT;", t1);
- fmtprint(fp, "}");
- break;
-
- case TSTRUCT:
- fmtprint(fp, "{");
- if(fp->flags & FmtLong)
- for(t1=t->type; t1!=T; t1=t1->down)
- fmtprint(fp, "%lT;", t1);
- fmtprint(fp, "}");
- break;
-
- case TMAP:
- fmtprint(fp, "[%T]%T", t->down, t->type);
- break;
-
- case TARRAY:
- if(t->bound >= 0)
- fmtprint(fp, "[%d]%T", t->bound, t->type);
- else
- fmtprint(fp, "[]%T", t->type);
- break;
-
- case TPTR32:
- case TPTR64:
- fmtprint(fp, "%T", t->type);
- break;
- }
-
-out:
- t->trecur--;
- return 0;
-}
-
-int
-Nconv(Fmt *fp)
-{
- char buf1[500];
- Node *n;
-
- n = va_arg(fp->args, Node*);
- if(n == N) {
- fmtprint(fp, "<N>");
- goto out;
- }
-
- if(fp->flags & FmtSign) {
- if(n->type == T)
- fmtprint(fp, "%#N", n);
- else if(n->type->etype == TNIL)
- fmtprint(fp, "nil");
- else
- fmtprint(fp, "%#N (type %T)", n, n->type);
- goto out;
- }
-
- if(fp->flags & FmtSharp) {
- if(n->orig != N)
- n = n->orig;
- exprfmt(fp, n, 0);
- goto out;
- }
-
- switch(n->op) {
- default:
- if (fp->flags & FmtShort)
- fmtprint(fp, "%O%hJ", n->op, n);
- else
- fmtprint(fp, "%O%J", n->op, n);
- break;
-
- case ONAME:
- case ONONAME:
- if(n->sym == S) {
- if (fp->flags & FmtShort)
- fmtprint(fp, "%O%hJ", n->op, n);
- else
- fmtprint(fp, "%O%J", n->op, n);
- break;
- }
- if (fp->flags & FmtShort)
- fmtprint(fp, "%O-%S%hJ", n->op, n->sym, n);
- else
- fmtprint(fp, "%O-%S%J", n->op, n->sym, n);
- goto ptyp;
-
- case OREGISTER:
- fmtprint(fp, "%O-%R%J", n->op, n->val.u.reg, n);
- break;
-
- case OLITERAL:
- switch(n->val.ctype) {
- default:
- snprint(buf1, sizeof(buf1), "LITERAL-ctype=%d", n->val.ctype);
- break;
- case CTINT:
- snprint(buf1, sizeof(buf1), "I%B", n->val.u.xval);
- break;
- case CTFLT:
- snprint(buf1, sizeof(buf1), "F%g", mpgetflt(n->val.u.fval));
- break;
- case CTCPLX:
- snprint(buf1, sizeof(buf1), "(F%g+F%gi)",
- mpgetflt(&n->val.u.cval->real),
- mpgetflt(&n->val.u.cval->imag));
- break;
- case CTSTR:
- snprint(buf1, sizeof(buf1), "S\"%Z\"", n->val.u.sval);
- break;
- case CTBOOL:
- snprint(buf1, sizeof(buf1), "B%d", n->val.u.bval);
- break;
- case CTNIL:
- snprint(buf1, sizeof(buf1), "N");
- break;
- }
- fmtprint(fp, "%O-%s%J", n->op, buf1, n);
- break;
-
- case OASOP:
- fmtprint(fp, "%O-%O%J", n->op, n->etype, n);
- break;
-
- case OTYPE:
- fmtprint(fp, "%O %T", n->op, n->type);
- break;
- }
- if(n->sym != S)
- fmtprint(fp, " %S G%d", n->sym, n->vargen);
-
-ptyp:
- if(n->type != T)
- fmtprint(fp, " %T", n->type);
-
-out:
- return 0;
-}
-
Node*
treecopy(Node *n)
{
@@ -1636,52 +841,6 @@ treecopy(Node *n)
return m;
}
-int
-Zconv(Fmt *fp)
-{
- Rune r;
- Strlit *sp;
- char *s, *se;
- int n;
-
- sp = va_arg(fp->args, Strlit*);
- if(sp == nil)
- return fmtstrcpy(fp, "<nil>");
-
- s = sp->s;
- se = s + sp->len;
- while(s < se) {
- n = chartorune(&r, s);
- s += n;
- switch(r) {
- case Runeerror:
- if(n == 1) {
- fmtprint(fp, "\\x%02x", (uchar)*(s-1));
- break;
- }
- // fall through
- default:
- if(r < ' ') {
- fmtprint(fp, "\\x%02x", r);
- break;
- }
- fmtrune(fp, r);
- break;
- case '\t':
- fmtstrcpy(fp, "\\t");
- break;
- case '\n':
- fmtstrcpy(fp, "\\n");
- break;
- case '\"':
- case '\\':
- fmtrune(fp, '\\');
- fmtrune(fp, r);
- break;
- }
- }
- return 0;
-}
int
isnil(Node *n)
@@ -1731,11 +890,19 @@ isslice(Type *t)
int
isblank(Node *n)
{
+ if(n == N)
+ return 0;
+ return isblanksym(n->sym);
+}
+
+int
+isblanksym(Sym *s)
+{
char *p;
- if(n == N || n->sym == S)
+ if(s == S)
return 0;
- p = n->sym->name;
+ p = s->name;
if(p == nil)
return 0;
return p[0] == '_' && p[1] == '\0';
@@ -1777,7 +944,7 @@ isideal(Type *t)
* return type to hang methods off (r).
*/
Type*
-methtype(Type *t)
+methtype(Type *t, int mustname)
{
if(t == T)
return T;
@@ -1792,7 +959,7 @@ methtype(Type *t)
}
// need a type name
- if(t->sym == S)
+ if(t->sym == S && (mustname || t->etype != TSTRUCT))
return T;
// check types
@@ -1837,6 +1004,25 @@ eqnote(Strlit *a, Strlit *b)
return memcmp(a->s, b->s, a->len) == 0;
}
+typedef struct TypePairList TypePairList;
+struct TypePairList
+{
+ Type *t1;
+ Type *t2;
+ TypePairList *next;
+};
+
+static int
+onlist(TypePairList *l, Type *t1, Type *t2)
+{
+ for(; l; l=l->next)
+ if((l->t1 == t1 && l->t2 == t2) || (l->t1 == t2 && l->t2 == t1))
+ return 1;
+ return 0;
+}
+
+static int eqtype1(Type*, Type*, TypePairList*);
+
// Return 1 if t1 and t2 are identical, following the spec rules.
//
// Any cyclic type must go through a named type, and if one is
@@ -1846,10 +1032,40 @@ eqnote(Strlit *a, Strlit *b)
int
eqtype(Type *t1, Type *t2)
{
+ return eqtype1(t1, t2, nil);
+}
+
+static int
+eqtype1(Type *t1, Type *t2, TypePairList *assumed_equal)
+{
+ TypePairList l;
+
if(t1 == t2)
return 1;
- if(t1 == T || t2 == T || t1->etype != t2->etype || t1->sym || t2->sym)
+ if(t1 == T || t2 == T || t1->etype != t2->etype)
return 0;
+ if(t1->sym || t2->sym) {
+ // Special case: we keep byte and uint8 separate
+ // for error messages. Treat them as equal.
+ switch(t1->etype) {
+ case TUINT8:
+ if((t1 == types[TUINT8] || t1 == bytetype) && (t2 == types[TUINT8] || t2 == bytetype))
+ return 1;
+ break;
+ case TINT:
+ case TINT32:
+ if((t1 == types[runetype->etype] || t1 == runetype) && (t2 == types[runetype->etype] || t2 == runetype))
+ return 1;
+ break;
+ }
+ return 0;
+ }
+
+ if(onlist(assumed_equal, t1, t2))
+ return 1;
+ l.next = assumed_equal;
+ l.t1 = t1;
+ l.t2 = t2;
switch(t1->etype) {
case TINTER:
@@ -1857,10 +1073,12 @@ eqtype(Type *t1, Type *t2)
for(t1=t1->type, t2=t2->type; t1 && t2; t1=t1->down, t2=t2->down) {
if(t1->etype != TFIELD || t2->etype != TFIELD)
fatal("struct/interface missing field: %T %T", t1, t2);
- if(t1->sym != t2->sym || t1->embedded != t2->embedded || !eqtype(t1->type, t2->type) || !eqnote(t1->note, t2->note))
- return 0;
+ if(t1->sym != t2->sym || t1->embedded != t2->embedded || !eqtype1(t1->type, t2->type, &l) || !eqnote(t1->note, t2->note))
+ goto no;
}
- return t1 == T && t2 == T;
+ if(t1 == T && t2 == T)
+ goto yes;
+ goto no;
case TFUNC:
// Loop over structs: receiver, in, out.
@@ -1874,26 +1092,36 @@ eqtype(Type *t1, Type *t2)
for(ta=t1->type, tb=t2->type; ta && tb; ta=ta->down, tb=tb->down) {
if(ta->etype != TFIELD || tb->etype != TFIELD)
fatal("func struct missing field: %T %T", ta, tb);
- if(ta->isddd != tb->isddd || !eqtype(ta->type, tb->type))
- return 0;
+ if(ta->isddd != tb->isddd || !eqtype1(ta->type, tb->type, &l))
+ goto no;
}
if(ta != T || tb != T)
- return 0;
+ goto no;
}
- return t1 == T && t2 == T;
+ if(t1 == T && t2 == T)
+ goto yes;
+ goto no;
case TARRAY:
if(t1->bound != t2->bound)
- return 0;
+ goto no;
break;
case TCHAN:
if(t1->chan != t2->chan)
- return 0;
+ goto no;
break;
}
- return eqtype(t1->down, t2->down) && eqtype(t1->type, t2->type);
+ if(eqtype1(t1->down, t2->down, &l) && eqtype1(t1->type, t2->type, &l))
+ goto yes;
+ goto no;
+
+yes:
+ return 1;
+
+no:
+ return 0;
}
// Are t1 and t2 equal struct types when field names are ignored?
@@ -1920,9 +1148,6 @@ eqtypenoname(Type *t1, Type *t2)
// Is type src assignment compatible to type dst?
// If so, return op code to use in conversion.
// If not, return 0.
-//
-// It is the caller's responsibility to call exportassignok
-// to check for assignments to other packages' unexported fields,
int
assignop(Type *src, Type *dst, char **why)
{
@@ -1932,7 +1157,9 @@ assignop(Type *src, Type *dst, char **why)
if(why != nil)
*why = "";
- if(safemode && src != T && src->etype == TUNSAFEPTR) {
+ // TODO(rsc,lvd): This behaves poorly in the presence of inlining.
+ // https://code.google.com/p/go/issues/detail?id=2795
+ if(safemode && importpkg == nil && src != T && src->etype == TUNSAFEPTR) {
yyerror("cannot use unsafe.Pointer");
errorexit();
}
@@ -1956,6 +1183,11 @@ assignop(Type *src, Type *dst, char **why)
if(dst->etype == TINTER && src->etype != TNIL) {
if(implements(src, dst, &missing, &have, &ptr))
return OCONVIFACE;
+
+ // we'll have complained about this method anyway, supress spurious messages.
+ if(have && have->sym == missing->sym && (have->type->broke || missing->type->broke))
+ return OCONVIFACE;
+
if(why != nil) {
if(isptrto(src, TINTER))
*why = smprint(":\n\t%T is pointer to interface, not interface", src);
@@ -2072,29 +1304,25 @@ convertop(Type *src, Type *dst, char **why)
return OCONV;
}
- // 6. src is an integer or has type []byte or []int
+ // 6. src is an integer or has type []byte or []rune
// and dst is a string type.
if(isint[src->etype] && dst->etype == TSTRING)
return ORUNESTR;
- if(isslice(src) && src->sym == nil && src->type == types[src->type->etype] && dst->etype == TSTRING) {
- switch(src->type->etype) {
- case TUINT8:
+ if(isslice(src) && dst->etype == TSTRING) {
+ if(src->type->etype == bytetype->etype)
return OARRAYBYTESTR;
- case TINT:
+ if(src->type->etype == runetype->etype)
return OARRAYRUNESTR;
- }
}
- // 7. src is a string and dst is []byte or []int.
+ // 7. src is a string and dst is []byte or []rune.
// String to slice.
- if(src->etype == TSTRING && isslice(dst) && dst->sym == nil && dst->type == types[dst->type->etype]) {
- switch(dst->type->etype) {
- case TUINT8:
+ if(src->etype == TSTRING && isslice(dst)) {
+ if(dst->type->etype == bytetype->etype)
return OSTRARRAYBYTE;
- case TINT:
+ if(dst->type->etype == runetype->etype)
return OSTRARRAYRUNE;
- }
}
// 8. src is a pointer or uintptr and dst is unsafe.Pointer.
@@ -2126,13 +1354,24 @@ assignconv(Node *n, Type *t, char *context)
if(t->etype == TBLANK)
return n;
- exportassignok(n->type, context);
+ // Convert ideal bool from comparison to plain bool
+ // if the next step is non-bool (like interface{}).
+ if(n->type == idealbool && t->etype != TBOOL) {
+ if(n->op == ONAME || n->op == OLITERAL) {
+ r = nod(OCONVNOP, n, N);
+ r->type = types[TBOOL];
+ r->typecheck = 1;
+ r->implicit = 1;
+ n = r;
+ }
+ }
+
if(eqtype(n->type, t))
return n;
op = assignop(n->type, t, &why);
if(op == 0) {
- yyerror("cannot use %+N as type %T in %s%s", n, t, context, why);
+ yyerror("cannot use %lN as type %T in %s%s", n, t, context, why);
op = OCONV;
}
@@ -2357,7 +1596,7 @@ syslook(char *name, int copy)
* compute a hash value for type t.
* if t is a method type, ignore the receiver
* so that the hash can be used in interface checks.
- * %-T (which calls Tpretty, above) already contains
+ * %T already contains
* all the necessary logic to generate a representation
* of the type that completely describes it.
* using smprint here avoids duplicating that code.
@@ -2371,15 +1610,14 @@ typehash(Type *t)
char *p;
MD5 d;
- longsymnames = 1;
if(t->thistuple) {
// hide method receiver from Tpretty
t->thistuple = 0;
- p = smprint("%-T", t);
+ p = smprint("%-uT", t);
t->thistuple = 1;
- }else
- p = smprint("%-T", t);
- longsymnames = 0;
+ } else
+ p = smprint("%-uT", t);
+ //print("typehash: %s\n", p);
md5reset(&d);
md5write(&d, (uchar*)p, strlen(p));
free(p);
@@ -2392,7 +1630,7 @@ ptrto(Type *t)
Type *t1;
if(tptr == 0)
- fatal("ptrto: nil");
+ fatal("ptrto: no tptr");
t1 = typ(tptr);
t1->type = t;
t1->width = widthptr;
@@ -2452,6 +1690,11 @@ ullmancalc(Node *n)
if(n == N)
return;
+ if(n->ninit != nil) {
+ ul = UINF;
+ goto out;
+ }
+
switch(n->op) {
case OREGISTER:
case OLITERAL:
@@ -2749,13 +1992,12 @@ safeexpr(Node *n, NodeList **init)
return cheapexpr(n, init);
}
-static Node*
+Node*
copyexpr(Node *n, Type *t, NodeList **init)
{
Node *a, *l;
- l = nod(OXXX, N, N);
- tempname(l, t);
+ l = temp(t);
a = nod(OAS, l, n);
typecheck(&a, Etop);
walkexpr(&a, init);
@@ -2806,10 +2048,12 @@ setmaxarg(Type *t)
maxarg = w;
}
-/* unicode-aware case-insensitive strcmp */
+/*
+ * unicode-aware case-insensitive strcmp
+ */
static int
-cistrcmp(char *p, char *q)
+ucistrcmp(char *p, char *q)
{
Rune rp, rq;
@@ -2851,16 +2095,16 @@ 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 && cistrcmp(f->sym->name, s->name) == 0)) {
+ if(f->sym == s || (ignorecase && ucistrcmp(f->sym->name, s->name) == 0)) {
if(save)
*save = f;
c++;
}
}
- u = methtype(t);
+ u = methtype(t, 0);
if(u != T) {
for(f=u->method; f!=T; f=f->down)
- if(f->embedded == 0 && (f->sym == s || (ignorecase && cistrcmp(f->sym->name, s->name) == 0))) {
+ if(f->embedded == 0 && (f->sym == s || (ignorecase && ucistrcmp(f->sym->name, s->name) == 0))) {
if(save)
*save = f;
c++;
@@ -2869,7 +2113,7 @@ lookdot0(Sym *s, Type *t, Type **save, int ignorecase)
return c;
}
-// search depth d --
+// search depth d for field/method s --
// return count of fields+methods
// found at search depth.
// answer is in dotlist array and
@@ -2946,8 +2190,11 @@ adddot(Node *n)
goto ret;
out:
- if(c > 1)
- yyerror("ambiguous DOT reference %T.%S", t, s);
+ if(c > 1) {
+ yyerror("ambiguous selector %N", n);
+ n->left = N;
+ return n;
+ }
// rebuild elided dots
for(c=d-1; c>=0; c--)
@@ -2992,8 +2239,6 @@ expand0(Type *t, int followptr)
if(u->etype == TINTER) {
for(f=u->type; f!=T; f=f->down) {
- if(!exportname(f->sym->name) && f->sym->pkg != localpkg)
- continue;
if(f->sym->flags & SymUniq)
continue;
f->sym->flags |= SymUniq;
@@ -3006,11 +2251,9 @@ expand0(Type *t, int followptr)
return;
}
- u = methtype(t);
+ u = methtype(t, 0);
if(u != T) {
for(f=u->method; f!=T; f=f->down) {
- if(!exportname(f->sym->name) && f->sym->pkg != localpkg)
- continue;
if(f->sym->flags & SymUniq)
continue;
f->sym->flags |= SymUniq;
@@ -3058,14 +2301,12 @@ out:
}
void
-expandmeth(Sym *s, Type *t)
+expandmeth(Type *t)
{
Symlink *sl;
Type *f;
int c, d;
- if(s == S)
- return;
if(t == T || t->xmethod != nil)
return;
@@ -3086,8 +2327,11 @@ expandmeth(Sym *s, Type *t)
if(c == 0)
continue;
if(c == 1) {
- sl->good = 1;
- sl->field = f;
+ // addot1 may have dug out arbitrary fields, we only want methods.
+ if(f->type->etype == TFUNC && f->type->thistuple > 0) {
+ sl->good = 1;
+ sl->field = f;
+ }
}
break;
}
@@ -3128,13 +2372,12 @@ structargs(Type **tl, int mustname)
gen = 0;
for(t = structfirst(&savet, tl); t != T; t = structnext(&savet)) {
n = N;
- if(t->sym)
- n = newname(t->sym);
- else if(mustname) {
- // have to give it a name so we can refer to it in trampoline
+ if(mustname && (t->sym == nil || strcmp(t->sym->name, "_") == 0)) {
+ // invent a name so that we can refer to it in the trampoline
snprint(buf, sizeof buf, ".anon%d", gen++);
n = newname(lookup(buf));
- }
+ } else if(t->sym)
+ n = newname(t->sym);
a = nod(ODCLFIELD, n, typenod(t->type));
a->isddd = t->isddd;
if(n != N)
@@ -3190,8 +2433,6 @@ genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface)
in = structargs(getinarg(method->type), 1);
out = structargs(getoutarg(method->type), 0);
- fn = nod(ODCLFUNC, N, N);
- fn->nname = newname(newnam);
t = nod(OTFUNC, N, N);
l = list1(this);
if(iface && rcvr->width < types[tptr]->width) {
@@ -3208,7 +2449,12 @@ genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface)
}
t->list = concat(l, in);
t->rlist = out;
+
+ fn = nod(ODCLFUNC, N, N);
+ fn->nname = newname(newnam);
+ fn->nname->defn = fn;
fn->nname->ntype = t;
+ declare(fn->nname, PFUNC);
funchdr(fn);
// arg list
@@ -3262,6 +2508,448 @@ genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface)
funccompile(fn, 0);
}
+static Node*
+hashmem(Type *t)
+{
+ Node *tfn, *n;
+ Sym *sym;
+
+ sym = pkglookup("memhash", runtimepkg);
+
+ n = newname(sym);
+ n->class = PFUNC;
+ tfn = nod(OTFUNC, N, N);
+ tfn->list = list(tfn->list, nod(ODCLFIELD, N, typenod(ptrto(types[TUINTPTR]))));
+ tfn->list = list(tfn->list, nod(ODCLFIELD, N, typenod(types[TUINTPTR])));
+ tfn->list = list(tfn->list, nod(ODCLFIELD, N, typenod(ptrto(t))));
+ typecheck(&tfn, Etype);
+ n->type = tfn->type;
+ return n;
+}
+
+static Node*
+hashfor(Type *t)
+{
+ int a;
+ Sym *sym;
+ Node *tfn, *n;
+
+ a = algtype1(t, nil);
+ switch(a) {
+ case AMEM:
+ return hashmem(t);
+ case AINTER:
+ sym = pkglookup("interhash", runtimepkg);
+ break;
+ case ANILINTER:
+ sym = pkglookup("nilinterhash", runtimepkg);
+ break;
+ case ASTRING:
+ sym = pkglookup("strhash", runtimepkg);
+ break;
+ case AFLOAT32:
+ sym = pkglookup("f32hash", runtimepkg);
+ break;
+ case AFLOAT64:
+ sym = pkglookup("f64hash", runtimepkg);
+ break;
+ case ACPLX64:
+ sym = pkglookup("c64hash", runtimepkg);
+ break;
+ case ACPLX128:
+ sym = pkglookup("c128hash", runtimepkg);
+ break;
+ default:
+ sym = typesymprefix(".hash", t);
+ break;
+ }
+
+ n = newname(sym);
+ n->class = PFUNC;
+ tfn = nod(OTFUNC, N, N);
+ tfn->list = list(tfn->list, nod(ODCLFIELD, N, typenod(ptrto(types[TUINTPTR]))));
+ tfn->list = list(tfn->list, nod(ODCLFIELD, N, typenod(types[TUINTPTR])));
+ tfn->list = list(tfn->list, nod(ODCLFIELD, N, typenod(ptrto(t))));
+ typecheck(&tfn, Etype);
+ n->type = tfn->type;
+ return n;
+}
+
+/*
+ * Generate a helper function to compute the hash of a value of type t.
+ */
+void
+genhash(Sym *sym, Type *t)
+{
+ Node *n, *fn, *np, *nh, *ni, *call, *nx, *na, *tfn;
+ Node *hashel;
+ Type *first, *t1;
+ int old_safemode;
+ int64 size, mul;
+
+ if(debug['r'])
+ print("genhash %S %T\n", sym, t);
+
+ lineno = 1; // less confusing than end of input
+ dclcontext = PEXTERN;
+ markdcl();
+
+ // func sym(h *uintptr, s uintptr, p *T)
+ fn = nod(ODCLFUNC, N, N);
+ fn->nname = newname(sym);
+ fn->nname->class = PFUNC;
+ tfn = nod(OTFUNC, N, N);
+ fn->nname->ntype = tfn;
+
+ n = nod(ODCLFIELD, newname(lookup("h")), typenod(ptrto(types[TUINTPTR])));
+ tfn->list = list(tfn->list, n);
+ nh = n->left;
+ n = nod(ODCLFIELD, newname(lookup("s")), typenod(types[TUINTPTR]));
+ tfn->list = list(tfn->list, n);
+ n = nod(ODCLFIELD, newname(lookup("p")), typenod(ptrto(t)));
+ tfn->list = list(tfn->list, n);
+ np = n->left;
+
+ funchdr(fn);
+ typecheck(&fn->nname->ntype, Etype);
+
+ // genhash is only called for types that have equality but
+ // cannot be handled by the standard algorithms,
+ // so t must be either an array or a struct.
+ switch(t->etype) {
+ default:
+ fatal("genhash %T", t);
+ case TARRAY:
+ if(isslice(t))
+ fatal("genhash %T", t);
+ // An array of pure memory would be handled by the
+ // standard algorithm, so the element type must not be
+ // pure memory.
+ hashel = hashfor(t->type);
+ n = nod(ORANGE, N, nod(OIND, np, N));
+ ni = newname(lookup("i"));
+ ni->type = types[TINT];
+ n->list = list1(ni);
+ n->colas = 1;
+ colasdefn(n->list, n);
+ ni = n->list->n;
+
+ // *h = *h<<3 | *h>>61
+ n->nbody = list(n->nbody,
+ nod(OAS,
+ nod(OIND, nh, N),
+ nod(OOR,
+ nod(OLSH, nod(OIND, nh, N), nodintconst(3)),
+ nod(ORSH, nod(OIND, nh, N), nodintconst(widthptr*8-3)))));
+
+ // *h *= mul
+ // Same multipliers as in runtime.memhash.
+ if(widthptr == 4)
+ mul = 3267000013LL;
+ else
+ mul = 23344194077549503LL;
+ n->nbody = list(n->nbody,
+ nod(OAS,
+ nod(OIND, nh, N),
+ nod(OMUL, nod(OIND, nh, N), nodintconst(mul))));
+
+ // hashel(h, sizeof(p[i]), &p[i])
+ call = nod(OCALL, hashel, N);
+ call->list = list(call->list, nh);
+ call->list = list(call->list, nodintconst(t->type->width));
+ nx = nod(OINDEX, np, ni);
+ nx->etype = 1; // no bounds check
+ na = nod(OADDR, nx, N);
+ na->etype = 1; // no escape to heap
+ call->list = list(call->list, na);
+ n->nbody = list(n->nbody, call);
+
+ fn->nbody = list(fn->nbody, n);
+ break;
+
+ case TSTRUCT:
+ // Walk the struct using memhash for runs of AMEM
+ // and calling specific hash functions for the others.
+ first = T;
+ for(t1=t->type;; t1=t1->down) {
+ if(t1 != T && (isblanksym(t1->sym) || algtype1(t1->type, nil) == AMEM)) {
+ if(first == T)
+ first = t1;
+ continue;
+ }
+ // Run memhash for fields up to this one.
+ while(first != T && isblanksym(first->sym))
+ first = first->down;
+ if(first != T) {
+ if(first->down == t1)
+ size = first->type->width;
+ else if(t1 == T)
+ size = t->width - first->width; // first->width is offset
+ else
+ size = t1->width - first->width; // both are offsets
+ hashel = hashmem(first->type);
+ // hashel(h, size, &p.first)
+ call = nod(OCALL, hashel, N);
+ call->list = list(call->list, nh);
+ call->list = list(call->list, nodintconst(size));
+ nx = nod(OXDOT, np, newname(first->sym)); // TODO: fields from other packages?
+ na = nod(OADDR, nx, N);
+ na->etype = 1; // no escape to heap
+ call->list = list(call->list, na);
+ fn->nbody = list(fn->nbody, call);
+
+ first = T;
+ }
+ if(t1 == T)
+ break;
+
+ // Run hash for this field.
+ hashel = hashfor(t1->type);
+ // hashel(h, size, &p.t1)
+ call = nod(OCALL, hashel, N);
+ call->list = list(call->list, nh);
+ call->list = list(call->list, nodintconst(t1->type->width));
+ nx = nod(OXDOT, np, newname(t1->sym)); // TODO: fields from other packages?
+ na = nod(OADDR, nx, N);
+ na->etype = 1; // no escape to heap
+ call->list = list(call->list, na);
+ fn->nbody = list(fn->nbody, call);
+ }
+ break;
+ }
+
+ if(debug['r'])
+ dumplist("genhash body", fn->nbody);
+
+ funcbody(fn);
+ curfn = fn;
+ fn->dupok = 1;
+ typecheck(&fn, Etop);
+ typechecklist(fn->nbody, Etop);
+ curfn = nil;
+
+ // 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;
+ funccompile(fn, 0);
+ safemode = old_safemode;
+}
+
+// Return node for
+// if p.field != q.field { *eq = false; return }
+static Node*
+eqfield(Node *p, Node *q, Node *field, Node *eq)
+{
+ Node *nif, *nx, *ny;
+
+ nx = nod(OXDOT, p, field);
+ ny = nod(OXDOT, q, field);
+ nif = nod(OIF, N, N);
+ nif->ntest = nod(ONE, nx, ny);
+ nif->nbody = list(nif->nbody, nod(OAS, nod(OIND, eq, N), nodbool(0)));
+ nif->nbody = list(nif->nbody, nod(ORETURN, N, N));
+ return nif;
+}
+
+static Node*
+eqmemfunc(vlong size, Type *type)
+{
+ char buf[30];
+ Node *fn;
+
+ switch(size) {
+ default:
+ fn = syslook("memequal", 1);
+ break;
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ case 16:
+ snprint(buf, sizeof buf, "memequal%d", (int)size*8);
+ fn = syslook(buf, 1);
+ break;
+ }
+ argtype(fn, type);
+ argtype(fn, type);
+ return fn;
+}
+
+// Return node for
+// if memequal(size, &p.field, &q.field, eq); !*eq { return }
+static Node*
+eqmem(Node *p, Node *q, Node *field, vlong size, Node *eq)
+{
+ Node *nif, *nx, *ny, *call;
+
+ nx = nod(OADDR, nod(OXDOT, p, field), N);
+ nx->etype = 1; // does not escape
+ ny = nod(OADDR, nod(OXDOT, q, field), N);
+ ny->etype = 1; // does not escape
+ typecheck(&nx, Erv);
+ typecheck(&ny, Erv);
+
+ call = nod(OCALL, eqmemfunc(size, nx->type->type), N);
+ call->list = list(call->list, eq);
+ call->list = list(call->list, nodintconst(size));
+ call->list = list(call->list, nx);
+ call->list = list(call->list, ny);
+
+ nif = nod(OIF, N, N);
+ nif->ninit = list(nif->ninit, call);
+ nif->ntest = nod(ONOT, nod(OIND, eq, N), N);
+ nif->nbody = list(nif->nbody, nod(ORETURN, N, N));
+ return nif;
+}
+
+/*
+ * Generate a helper function to check equality of two values of type t.
+ */
+void
+geneq(Sym *sym, Type *t)
+{
+ Node *n, *fn, *np, *neq, *nq, *tfn, *nif, *ni, *nx, *ny, *nrange;
+ Type *t1, *first;
+ int old_safemode;
+ int64 size;
+
+ if(debug['r'])
+ print("geneq %S %T\n", sym, t);
+
+ lineno = 1; // less confusing than end of input
+ dclcontext = PEXTERN;
+ markdcl();
+
+ // func sym(eq *bool, s uintptr, p, q *T)
+ fn = nod(ODCLFUNC, N, N);
+ fn->nname = newname(sym);
+ fn->nname->class = PFUNC;
+ tfn = nod(OTFUNC, N, N);
+ fn->nname->ntype = tfn;
+
+ n = nod(ODCLFIELD, newname(lookup("eq")), typenod(ptrto(types[TBOOL])));
+ tfn->list = list(tfn->list, n);
+ neq = n->left;
+ n = nod(ODCLFIELD, newname(lookup("s")), typenod(types[TUINTPTR]));
+ tfn->list = list(tfn->list, n);
+ n = nod(ODCLFIELD, newname(lookup("p")), typenod(ptrto(t)));
+ tfn->list = list(tfn->list, n);
+ np = n->left;
+ n = nod(ODCLFIELD, newname(lookup("q")), typenod(ptrto(t)));
+ tfn->list = list(tfn->list, n);
+ nq = n->left;
+
+ funchdr(fn);
+
+ // geneq is only called for types that have equality but
+ // cannot be handled by the standard algorithms,
+ // so t must be either an array or a struct.
+ switch(t->etype) {
+ default:
+ fatal("geneq %T", t);
+ case TARRAY:
+ if(isslice(t))
+ fatal("geneq %T", t);
+ // An array of pure memory would be handled by the
+ // standard memequal, so the element type must not be
+ // pure memory. Even if we unrolled the range loop,
+ // each iteration would be a function call, so don't bother
+ // unrolling.
+ nrange = nod(ORANGE, N, nod(OIND, np, N));
+ ni = newname(lookup("i"));
+ ni->type = types[TINT];
+ nrange->list = list1(ni);
+ nrange->colas = 1;
+ colasdefn(nrange->list, nrange);
+ ni = nrange->list->n;
+
+ // if p[i] != q[i] { *eq = false; return }
+ nx = nod(OINDEX, np, ni);
+ nx->etype = 1; // no bounds check
+ ny = nod(OINDEX, nq, ni);
+ ny->etype = 1; // no bounds check
+
+ nif = nod(OIF, N, N);
+ nif->ntest = nod(ONE, nx, ny);
+ nif->nbody = list(nif->nbody, nod(OAS, nod(OIND, neq, N), nodbool(0)));
+ nif->nbody = list(nif->nbody, nod(ORETURN, N, N));
+ nrange->nbody = list(nrange->nbody, nif);
+ fn->nbody = list(fn->nbody, nrange);
+
+ // *eq = true;
+ fn->nbody = list(fn->nbody, nod(OAS, nod(OIND, neq, N), nodbool(1)));
+ break;
+
+ case TSTRUCT:
+ // Walk the struct using memequal for runs of AMEM
+ // and calling specific equality tests for the others.
+ first = T;
+ for(t1=t->type;; t1=t1->down) {
+ if(t1 != T && (isblanksym(t1->sym) || algtype1(t1->type, nil) == AMEM)) {
+ if(first == T)
+ first = t1;
+ continue;
+ }
+ // Run memequal for fields up to this one.
+ // TODO(rsc): All the calls to newname are wrong for
+ // cross-package unexported fields.
+ while(first != T && isblanksym(first->sym))
+ first = first->down;
+ if(first != T) {
+ if(first->down == t1) {
+ fn->nbody = list(fn->nbody, eqfield(np, nq, newname(first->sym), neq));
+ } else if(first->down->down == t1) {
+ fn->nbody = list(fn->nbody, eqfield(np, nq, newname(first->sym), neq));
+ first = first->down;
+ if(!isblanksym(first->sym))
+ fn->nbody = list(fn->nbody, eqfield(np, nq, newname(first->sym), neq));
+ } else {
+ // More than two fields: use memequal.
+ if(t1 == T)
+ size = t->width - first->width; // first->width is offset
+ else
+ size = t1->width - first->width; // both are offsets
+ fn->nbody = list(fn->nbody, eqmem(np, nq, newname(first->sym), size, neq));
+ }
+ first = T;
+ }
+ if(t1 == T)
+ break;
+
+ // Check this field, which is not just memory.
+ fn->nbody = list(fn->nbody, eqfield(np, nq, newname(t1->sym), neq));
+ }
+
+ // *eq = true;
+ fn->nbody = list(fn->nbody, nod(OAS, nod(OIND, neq, N), nodbool(1)));
+ break;
+ }
+
+ if(debug['r'])
+ dumplist("geneq body", fn->nbody);
+
+ funcbody(fn);
+ curfn = fn;
+ fn->dupok = 1;
+ typecheck(&fn, Etop);
+ typechecklist(fn->nbody, Etop);
+ curfn = nil;
+
+ // 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;
+ funccompile(fn, 0);
+ safemode = old_safemode;
+}
+
static Type*
ifacelookdot(Sym *s, Type *t, int *followptr, int ignorecase)
{
@@ -3331,9 +3019,9 @@ implements(Type *t, Type *iface, Type **m, Type **samename, int *ptr)
return 1;
}
- t = methtype(t);
+ t = methtype(t, 0);
if(t != T)
- expandmeth(t->sym, t);
+ expandmeth(t);
for(im=iface->type; im; im=im->down) {
imtype = methodfunc(im->type, 0);
tm = ifacelookdot(im->sym, t, &followptr, 0);
@@ -3410,8 +3098,13 @@ list1(Node *n)
if(n == nil)
return nil;
- if(n->op == OBLOCK && n->ninit == nil)
- return n->list;
+ if(n->op == OBLOCK && n->ninit == nil) {
+ // Flatten list and steal storage.
+ // Poison pointer to catch errant uses.
+ l = n->list;
+ n->list = (NodeList*)1;
+ return l;
+ }
l = mal(sizeof *l);
l->n = n;
l->end = l;
@@ -3453,7 +3146,7 @@ listsort(NodeList** l, int(*f)(Node*, Node*))
listsort(&l1, f);
listsort(&l2, f);
- if ((*f)(l1->n, l2->n) < 0) {
+ if((*f)(l1->n, l2->n) < 0) {
*l = l1;
} else {
*l = l2;
@@ -3469,7 +3162,7 @@ listsort(NodeList** l, int(*f)(Node*, Node*))
// l1 is last one from l1 that is < l2
le = l1->next; // le is the rest of l1, first one that is >= l2
- if (le != nil)
+ if(le != nil)
le->end = (*l)->end;
(*l)->end = l1; // cut *l at l1
@@ -3814,21 +3507,31 @@ ngotype(Node *n)
}
/*
- * Convert raw string to the prefix that will be used in the symbol table.
- * Invalid bytes turn into %xx. Right now the only bytes that need
- * escaping are %, ., and ", but we escape all control characters too.
+ * Convert raw string to the prefix that will be used in the symbol
+ * table. All control characters, space, '%' and '"', as well as
+ * non-7-bit clean bytes turn into %xx. The period needs escaping
+ * only in the last segment of the path, and it makes for happier
+ * users if we escape that as little as possible.
+ *
+ * If you edit this, edit ../ld/lib.c:/^pathtoprefix copy too.
*/
static char*
pathtoprefix(char *s)
{
static char hex[] = "0123456789abcdef";
- char *p, *r, *w;
+ char *p, *r, *w, *l;
int n;
+ // find first character past the last slash, if any.
+ l = s;
+ for(r=s; *r; r++)
+ if(*r == '/')
+ l = r+1;
+
// check for chars that need escaping
n = 0;
for(r=s; *r; r++)
- if(*r <= ' ' || *r == '.' || *r == '%' || *r == '"')
+ if(*r <= ' ' || (*r == '.' && r >= l) || *r == '%' || *r == '"' || *r >= 0x7f)
n++;
// quick exit
@@ -3838,7 +3541,7 @@ pathtoprefix(char *s)
// escape
p = mal((r-s)+1+2*n);
for(r=s, w=p; *r; r++) {
- if(*r <= ' ' || *r == '.' || *r == '%' || *r == '"') {
+ if(*r <= ' ' || (*r == '.' && r >= l) || *r == '%' || *r == '"' || *r >= 0x7f) {
*w++ = '%';
*w++ = hex[(*r>>4)&0xF];
*w++ = hex[*r&0xF];
@@ -3854,11 +3557,9 @@ mkpkg(Strlit *path)
{
Pkg *p;
int h;
-
- if(strlen(path->s) != path->len) {
- yyerror("import path contains NUL byte");
+
+ if(isbadimport(path))
errorexit();
- }
h = stringhash(path->s) & (nelem(phash)-1);
for(p=phash[h]; p; p=p->link)
@@ -3883,3 +3584,65 @@ strlit(char *s)
t->len = strlen(s);
return t;
}
+
+void
+addinit(Node **np, NodeList *init)
+{
+ Node *n;
+
+ if(init == nil)
+ return;
+
+ n = *np;
+ switch(n->op) {
+ case ONAME:
+ case OLITERAL:
+ // There may be multiple refs to this node;
+ // introduce OCONVNOP to hold init list.
+ n = nod(OCONVNOP, n, N);
+ n->type = n->left->type;
+ n->typecheck = 1;
+ *np = n;
+ break;
+ }
+ n->ninit = concat(init, n->ninit);
+ n->ullman = UINF;
+}
+
+int
+isbadimport(Strlit *path)
+{
+ char *s;
+ Rune r;
+
+ if(strlen(path->s) != path->len) {
+ yyerror("import path contains NUL");
+ return 1;
+ }
+
+ s = path->s;
+ while(*s) {
+ s += chartorune(&r, s);
+ if(r == Runeerror) {
+ yyerror("import path contains invalid UTF-8 sequence: \"%Z\"", path);
+ return 1;
+ }
+ if(r < 0x20 || r == 0x7f) {
+ yyerror("import path contains control character: \"%Z\"", path);
+ return 1;
+ }
+ if(r == '\\') {
+ yyerror("import path contains backslash; use slash: \"%Z\"", path);
+ return 1;
+ }
+ if(isspacerune(r)) {
+ yyerror("import path contains space character: \"%Z\"", path);
+ return 1;
+ }
+ if(utfrune("!\"#$%&'()*,:;<=>?[]^`{|}", r)) {
+ yyerror("import path contains invalid character '%C': \"%Z\"", r, path);
+ return 1;
+ }
+ }
+ return 0;
+}
diff --git a/src/cmd/gc/swt.c b/src/cmd/gc/swt.c
index c2968c44b..f1a95587f 100644
--- a/src/cmd/gc/swt.c
+++ b/src/cmd/gc/swt.c
@@ -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 <u.h>
+#include <libc.h>
#include "go.h"
enum
@@ -130,6 +132,7 @@ exprcmp(Case *c1, Case *c2)
n = mpcmpfltflt(n1->val.u.fval, n2->val.u.fval);
break;
case CTINT:
+ case CTRUNE:
n = mpcmpfixfix(n1->val.u.xval, n2->val.u.xval);
break;
case CTSTR:
@@ -378,6 +381,7 @@ mkcaselist(Node *sw, int arg)
switch(consttype(n->left)) {
case CTFLT:
case CTINT:
+ case CTRUNE:
case CTSTR:
c->type = Texprconst;
}
@@ -513,8 +517,7 @@ exprswitch(Node *sw)
exprname = N;
cas = nil;
if(arg != Strue && arg != Sfalse) {
- exprname = nod(OXXX, N, N);
- tempname(exprname, sw->ntest->type);
+ exprname = temp(sw->ntest->type);
cas = list1(nod(OAS, exprname, sw->ntest));
typechecklist(cas, Etop);
}
@@ -537,7 +540,7 @@ loop:
}
// deal with the variables one-at-a-time
- if(c0->type != Texprconst) {
+ if(!okforcmp[t->etype] || c0->type != Texprconst) {
a = exprbsw(c0, 1, arg);
cas = list(cas, a);
c0 = c0->link;
@@ -671,20 +674,17 @@ typeswitch(Node *sw)
* predeclare temporary variables
* and the boolean var
*/
- facename = nod(OXXX, N, N);
- tempname(facename, sw->ntest->right->type);
+ facename = temp(sw->ntest->right->type);
a = nod(OAS, facename, sw->ntest->right);
typecheck(&a, Etop);
cas = list(cas, a);
casebody(sw, facename);
- boolname = nod(OXXX, N, N);
- tempname(boolname, types[TBOOL]);
+ boolname = temp(types[TBOOL]);
typecheck(&boolname, Erv);
- hashname = nod(OXXX, N, N);
- tempname(hashname, types[TUINT32]);
+ hashname = temp(types[TUINT32]);
typecheck(&hashname, Erv);
t = sw->ntest->right->type;
@@ -792,7 +792,6 @@ walkswitch(Node *sw)
* cases have OGOTO into statements.
* both have inserted OBREAK statements
*/
- walkstmtlist(sw->ninit);
if(sw->ntest == N) {
sw->ntest = nodbool(1);
typecheck(&sw->ntest, Erv);
@@ -812,14 +811,16 @@ walkswitch(Node *sw)
void
typecheckswitch(Node *n)
{
- int top, lno;
- Type *t;
+ int top, lno, ptr;
+ char *nilonly;
+ Type *t, *missing, *have;
NodeList *l, *ll;
Node *ncase, *nvar;
Node *def;
lno = lineno;
typechecklist(n->ninit, Etop);
+ nilonly = nil;
if(n->ntest != N && n->ntest->op == OTYPESW) {
// type switch
@@ -827,7 +828,7 @@ typecheckswitch(Node *n)
typecheck(&n->ntest->right, Erv);
t = n->ntest->right->type;
if(t != T && t->etype != TINTER)
- yyerror("cannot type switch on non-interface value %+N", n->ntest->right);
+ yyerror("cannot type switch on non-interface value %lN", n->ntest->right);
} else {
// value switch
top = Erv;
@@ -837,6 +838,16 @@ typecheckswitch(Node *n)
t = n->ntest->type;
} else
t = types[TBOOL];
+ if(t) {
+ if(!okforeq[t->etype] || isfixedarray(t))
+ yyerror("cannot switch on %lN", n->ntest);
+ else if(t->etype == TARRAY)
+ nilonly = "slice";
+ else if(t->etype == TFUNC)
+ nilonly = "func";
+ else if(t->etype == TMAP)
+ nilonly = "map";
+ }
}
n->type = t;
@@ -856,21 +867,37 @@ typecheckswitch(Node *n)
typecheck(&ll->n, Erv | Etype);
if(ll->n->type == T || t == T)
continue;
+ setlineno(ncase);
switch(top) {
case Erv: // expression switch
defaultlit(&ll->n, t);
if(ll->n->op == OTYPE)
yyerror("type %T is not an expression", ll->n->type);
- else if(ll->n->type != T && !eqtype(ll->n->type, t))
- yyerror("case %+N in %T switch", ll->n, t);
+ else if(ll->n->type != T && !assignop(ll->n->type, t, nil) && !assignop(t, ll->n->type, nil)) {
+ if(n->ntest)
+ yyerror("invalid case %N in switch on %N (mismatched types %T and %T)", ll->n, n->ntest, ll->n->type, t);
+ else
+ yyerror("invalid case %N in switch (mismatched types %T and bool)", ll->n, ll->n->type);
+ } else if(nilonly && !isconst(ll->n, CTNIL)) {
+ yyerror("invalid case %N in switch (can only compare %s %N to nil)", ll->n, nilonly, n->ntest);
+ }
break;
case Etype: // type switch
- if(ll->n->op == OLITERAL && istype(ll->n->type, TNIL))
+ if(ll->n->op == OLITERAL && istype(ll->n->type, TNIL)) {
;
- else if(ll->n->op != OTYPE && ll->n->type != T) {
- yyerror("%#N is not a type", ll->n);
+ } else if(ll->n->op != OTYPE && ll->n->type != T) { // should this be ||?
+ yyerror("%lN is not a type", ll->n);
// reset to original type
ll->n = n->ntest->right;
+ } else if(ll->n->type->etype != TINTER && !implements(ll->n->type, t, &missing, &have, &ptr)) {
+ if(have && !missing->broke && !have->broke)
+ yyerror("impossible type switch case: %lN cannot have dynamic type %T"
+ " (wrong type for %S method)\n\thave %S%hT\n\twant %S%hT",
+ n->ntest->right, ll->n->type, missing->sym, have->sym, have->type,
+ missing->sym, missing->type);
+ else if(!missing->broke)
+ yyerror("impossible type switch case: %lN cannot have dynamic type %T"
+ " (missing %S method)", n->ntest->right, ll->n->type, missing->sym);
}
break;
}
diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c
index 78cdb5bf2..e98d53857 100644
--- a/src/cmd/gc/typecheck.c
+++ b/src/cmd/gc/typecheck.c
@@ -10,6 +10,8 @@
* rewrites n->op to be more specific in some cases.
*/
+#include <u.h>
+#include <libc.h>
#include "go.h"
static void implicitstar(Node**);
@@ -18,10 +20,9 @@ static int twoarg(Node*);
static int lookdot(Node*, Type*, int);
static int looktypedot(Node*, Type*, int);
static void typecheckaste(int, Node*, int, Type*, NodeList*, char*);
-static Type* lookdot1(Sym *s, Type *t, Type *f, int);
+static Type* lookdot1(Node*, Sym *s, Type *t, Type *f, int);
static int nokeys(NodeList*);
static void typecheckcomplit(Node**);
-static void addrescapes(Node*);
static void typecheckas2(Node*);
static void typecheckas(Node*);
static void typecheckfunc(Node*);
@@ -37,12 +38,12 @@ static NodeList* typecheckdefstack;
/*
* resolve ONONAME to definition, if any.
*/
-Node*
+static Node*
resolve(Node *n)
{
Node *r;
- if(n != N && n->op == ONONAME && (r = n->sym->def) != N) {
+ if(n != N && n->op == ONONAME && n->sym != S && (r = n->sym->def) != N) {
if(r->op != OIOTA)
n = r;
else if(n->iota >= 0)
@@ -78,6 +79,7 @@ static char* _typekind[] = {
[TSTRING] = "string",
[TPTR32] = "pointer",
[TPTR64] = "pointer",
+ [TUNSAFEPTR] = "unsafe.Pointer",
[TSTRUCT] = "struct",
[TINTER] = "interface",
[TCHAN] = "chan",
@@ -89,11 +91,15 @@ static char* _typekind[] = {
};
static char*
-typekind(int et)
+typekind(Type *t)
{
+ int et;
static char buf[50];
char *s;
+ if(isslice(t))
+ return "slice";
+ et = t->etype;
if(0 <= et && et < nelem(_typekind) && (s=_typekind[et]) != nil)
return s;
snprint(buf, sizeof buf, "etype=%d", et);
@@ -105,17 +111,15 @@ typekind(int et)
* replaces *np with a new pointer in some cases.
* returns the final value of *np as a convenience.
*/
+static void typecheck1(Node **, int);
Node*
typecheck(Node **np, int top)
{
- int et, aop, op, ptr;
- Node *n, *l, *r;
- NodeList *args;
- int lno, ok, ntop;
- Type *t, *tp, *ft, *missing, *have;
- Sym *sym;
- Val v;
- char *why;
+ Node *n;
+ int lno;
+ Fmt fmt;
+ NodeList *l;
+ static NodeList *tcstack, *tcfree;
// cannot type check until all the source has been parsed
if(!typecheckok)
@@ -152,11 +156,92 @@ typecheck(Node **np, int top)
}
if(n->typecheck == 2) {
- yyerror("typechecking loop");
+ if(nsavederrors+nerrors == 0) {
+ fmtstrinit(&fmt);
+ for(l=tcstack; l; l=l->next)
+ fmtprint(&fmt, "\n\t%L %N", l->n->lineno, l->n);
+ yyerror("typechecking loop involving %N%s", n, fmtstrflush(&fmt));
+ }
lineno = lno;
return n;
}
n->typecheck = 2;
+
+ if(tcfree != nil) {
+ l = tcfree;
+ tcfree = l->next;
+ } else
+ l = mal(sizeof *l);
+ l->next = tcstack;
+ l->n = n;
+ tcstack = l;
+
+ typecheck1(&n, top);
+ *np = n;
+ n->typecheck = 1;
+
+ if(tcstack != l)
+ fatal("typecheck stack out of sync");
+ tcstack = l->next;
+ l->next = tcfree;
+ tcfree = l;
+
+ lineno = lno;
+ return n;
+}
+
+/*
+ * does n contain a call or receive operation?
+ */
+static int callrecvlist(NodeList*);
+
+static int
+callrecv(Node *n)
+{
+ if(n == nil)
+ return 0;
+
+ switch(n->op) {
+ case OCALL:
+ case OCALLMETH:
+ case OCALLINTER:
+ case OCALLFUNC:
+ case ORECV:
+ return 1;
+ }
+
+ return callrecv(n->left) ||
+ callrecv(n->right) ||
+ callrecv(n->ntest) ||
+ callrecv(n->nincr) ||
+ callrecvlist(n->ninit) ||
+ callrecvlist(n->nbody) ||
+ callrecvlist(n->nelse) ||
+ callrecvlist(n->list) ||
+ callrecvlist(n->rlist);
+}
+
+static int
+callrecvlist(NodeList *l)
+{
+ for(; l; l=l->next)
+ if(callrecv(l->n))
+ return 1;
+ return 0;
+}
+
+static void
+typecheck1(Node **np, int top)
+{
+ int et, aop, op, ptr;
+ Node *n, *l, *r;
+ NodeList *args;
+ int ok, ntop;
+ Type *t, *tp, *ft, *missing, *have, *badtype;
+ Val v;
+ char *why;
+
+ n = *np;
if(n->sym) {
if(n->op == ONAME && n->etype != 0 && !(top & Ecall)) {
@@ -209,6 +294,10 @@ reswitch:
}
n->used = 1;
}
+ if(!(top &Ecall) && isunsafebuiltin(n)) {
+ yyerror("%N is not an expression, must be called", n);
+ goto error;
+ }
ok |= Erv;
goto ret;
@@ -253,13 +342,14 @@ reswitch:
l = typecheck(&n->left, Erv);
switch(consttype(l)) {
case CTINT:
+ case CTRUNE:
v = l->val;
break;
case CTFLT:
v = toint(l->val);
break;
default:
- yyerror("invalid array bound %#N", l);
+ yyerror("invalid array bound %N", l);
goto error;
}
t->bound = mpgetfix(v.u.xval);
@@ -310,7 +400,7 @@ reswitch:
case OTSTRUCT:
ok |= Etype;
n->op = OTYPE;
- n->type = dostruct(n->list, TSTRUCT);
+ n->type = tostruct(n->list);
if(n->type == T)
goto error;
n->list = nil;
@@ -319,7 +409,7 @@ reswitch:
case OTINTER:
ok |= Etype;
n->op = OTYPE;
- n->type = dostruct(n->list, TINTER);
+ n->type = tointerface(n->list);
if(n->type == T)
goto error;
break;
@@ -337,8 +427,9 @@ reswitch:
*/
case OIND:
ntop = Erv | Etype;
- if(!(top & Eaddr))
+ if(!(top & Eaddr)) // The *x in &*x is not an indirect.
ntop |= Eindir;
+ ntop |= top & Ecomplit;
l = typecheck(&n->left, ntop);
if((t = l->type) == T)
goto error;
@@ -350,7 +441,7 @@ reswitch:
goto ret;
}
if(!isptr[t->etype]) {
- yyerror("invalid indirect of %+N", n->left);
+ yyerror("invalid indirect of %lN", n->left);
goto error;
}
ok |= Erv;
@@ -413,15 +504,25 @@ reswitch:
if(iscmp[n->op] && t->etype != TIDEAL && !eqtype(l->type, r->type)) {
// comparison is okay as long as one side is
// assignable to the other. convert so they have
- // the same type. (the only conversion that isn't
- // a no-op is concrete == interface.)
+ // the same type.
+ //
+ // the only conversion that isn't a no-op is concrete == interface.
+ // in that case, check comparability of the concrete type.
if(r->type->etype != TBLANK && (aop = assignop(l->type, r->type, nil)) != 0) {
+ if(isinter(r->type) && !isinter(l->type) && algtype1(l->type, nil) == ANOEQ) {
+ yyerror("invalid operation: %N (operator %O not defined on %s)", n, op, typekind(l->type));
+ goto error;
+ }
l = nod(aop, l, N);
l->type = r->type;
l->typecheck = 1;
n->left = l;
t = l->type;
} else if(l->type->etype != TBLANK && (aop = assignop(r->type, l->type, nil)) != 0) {
+ if(isinter(l->type) && !isinter(r->type) && algtype1(r->type, nil) == ANOEQ) {
+ yyerror("invalid operation: %N (operator %O not defined on %s)", n, op, typekind(r->type));
+ goto error;
+ }
r = nod(aop, r, N);
r->type = l->type;
r->typecheck = 1;
@@ -432,28 +533,40 @@ reswitch:
}
if(t->etype != TIDEAL && !eqtype(l->type, r->type)) {
defaultlit2(&l, &r, 1);
- yyerror("invalid operation: %#N (mismatched types %T and %T)", n, l->type, r->type);
+ yyerror("invalid operation: %N (mismatched types %T and %T)", n, l->type, r->type);
goto error;
}
if(!okfor[op][et]) {
- notokfor:
- yyerror("invalid operation: %#N (operator %#O not defined on %s)", n, op, typekind(et));
+ yyerror("invalid operation: %N (operator %O not defined on %s)", n, op, typekind(t));
+ goto error;
+ }
+ // okfor allows any array == array, map == map, func == func.
+ // restrict to slice/map/func == nil and nil == slice/map/func.
+ if(isfixedarray(l->type) && algtype1(l->type, nil) == ANOEQ) {
+ yyerror("invalid operation: %N (%T cannot be compared)", n, l->type);
goto error;
}
- // okfor allows any array == array;
- // restrict to slice == nil and nil == slice.
- if(l->type->etype == TARRAY && !isslice(l->type))
- goto notokfor;
- if(r->type->etype == TARRAY && !isslice(r->type))
- goto notokfor;
if(isslice(l->type) && !isnil(l) && !isnil(r)) {
- yyerror("invalid operation: %#N (slice can only be compared to nil)", n);
+ yyerror("invalid operation: %N (slice can only be compared to nil)", n);
goto error;
}
+ if(l->type->etype == TMAP && !isnil(l) && !isnil(r)) {
+ yyerror("invalid operation: %N (map can only be compared to nil)", n);
+ goto error;
+ }
+ if(l->type->etype == TFUNC && !isnil(l) && !isnil(r)) {
+ yyerror("invalid operation: %N (func can only be compared to nil)", n);
+ goto error;
+ }
+ if(l->type->etype == TSTRUCT && algtype1(l->type, &badtype) == ANOEQ) {
+ yyerror("invalid operation: %N (struct containing %T cannot be compared)", n, badtype);
+ goto error;
+ }
+
t = l->type;
if(iscmp[n->op]) {
evconst(n);
- t = types[TBOOL];
+ t = idealbool;
if(n->op != OLITERAL) {
defaultlit2(&l, &r, 1);
n->left = l;
@@ -487,12 +600,12 @@ reswitch:
n->right = r;
t = r->type;
if(!isint[t->etype] || issigned[t->etype]) {
- yyerror("invalid operation: %#N (shift count type %T, must be unsigned integer)", n, r->type);
+ yyerror("invalid operation: %N (shift count type %T, must be unsigned integer)", n, r->type);
goto error;
}
t = l->type;
if(t != T && t->etype != TIDEAL && !isint[t->etype]) {
- yyerror("invalid operation: %#N (shift of type %T)", n, t);
+ yyerror("invalid operation: %N (shift of type %T)", n, t);
goto error;
}
// no defaultlit for left
@@ -509,7 +622,7 @@ reswitch:
if((t = l->type) == T)
goto error;
if(!okfor[n->op][t->etype]) {
- yyerror("invalid operation: %#O %T", n->op, t);
+ yyerror("invalid operation: %O %T", n->op, t);
goto error;
}
n->type = t;
@@ -523,19 +636,17 @@ reswitch:
typecheck(&n->left, Erv | Eaddr);
if(n->left->type == T)
goto error;
- switch(n->left->op) {
- case OMAPLIT:
- case OSTRUCTLIT:
- case OARRAYLIT:
- break;
- default:
- checklvalue(n->left, "take the address of");
- }
+ checklvalue(n->left, "take the address of");
+ for(l=n->left; l->op == ODOT; l=l->left)
+ l->addrtaken = 1;
+ l->addrtaken = 1;
defaultlit(&n->left, T);
l = n->left;
if((t = l->type) == T)
goto error;
- if(!(top & Eindir) && !n->etype)
+ // top&Eindir means this is &x in *&x. (or the arg to built-in print)
+ // n->etype means code generator flagged it as non-escaping.
+ if(debug['N'] && !(top & Eindir) && !n->etype)
addrescapes(n->left);
n->type = ptrto(t);
goto ret;
@@ -550,40 +661,40 @@ reswitch:
case OXDOT:
n = adddot(n);
n->op = ODOT;
+ if(n->left == N)
+ goto error;
// fall through
case ODOT:
typecheck(&n->left, Erv|Etype);
defaultlit(&n->left, T);
- l = n->left;
- if((t = l->type) == T)
+ if((t = n->left->type) == T)
goto error;
if(n->right->op != ONAME) {
yyerror("rhs of . must be a name"); // impossible
goto error;
}
- sym = n->right->sym;
- if(l->op == OTYPE) {
+
+ if(n->left->op == OTYPE) {
if(!looktypedot(n, t, 0)) {
if(looktypedot(n, t, 1))
- yyerror("%#N undefined (cannot refer to unexported method %S)", n, n->right->sym);
+ yyerror("%N undefined (cannot refer to unexported method %S)", n, n->right->sym);
else
- yyerror("%#N undefined (type %T has no method %S)", n, t, n->right->sym);
+ yyerror("%N undefined (type %T has no method %S)", n, t, n->right->sym);
goto error;
}
if(n->type->etype != TFUNC || n->type->thistuple != 1) {
- yyerror("type %T has no method %hS", n->left->type, sym);
+ yyerror("type %T has no method %hS", n->left->type, n->right->sym);
n->type = T;
goto error;
}
n->op = ONAME;
- n->sym = methodsym(sym, l->type, 0);
- n->type = methodfunc(n->type, l->type);
+ n->sym = n->right->sym;
+ n->type = methodfunc(n->type, n->left->type);
n->xoffset = 0;
n->class = PFUNC;
ok = Erv;
goto ret;
}
- tp = t;
if(isptr[t->etype] && t->type->etype != TINTER) {
t = t->type;
if(t == T)
@@ -593,9 +704,9 @@ reswitch:
}
if(!lookdot(n, t, 0)) {
if(lookdot(n, t, 1))
- yyerror("%#N undefined (cannot refer to unexported field or method %S)", n, n->right->sym);
+ yyerror("%N undefined (cannot refer to unexported field or method %S)", n, n->right->sym);
else
- yyerror("%#N undefined (type %T has no field or method %S)", n, tp, n->right->sym);
+ yyerror("%N undefined (type %T has no field or method %S)", n, n->left->type, n->right->sym);
goto error;
}
switch(n->op) {
@@ -617,7 +728,7 @@ reswitch:
if((t = l->type) == T)
goto error;
if(!isinter(t)) {
- yyerror("invalid type assertion: %#N (non-interface type %T on left)", n, t);
+ yyerror("invalid type assertion: %N (non-interface type %T on left)", n, t);
goto error;
}
if(n->right != N) {
@@ -630,12 +741,12 @@ reswitch:
if(n->type != T && n->type->etype != TINTER)
if(!implements(n->type, t, &missing, &have, &ptr)) {
if(have)
- yyerror("impossible type assertion: %+N cannot have dynamic type %T"
- " (wrong type for %S method)\n\thave %S%hhT\n\twant %S%hhT",
+ yyerror("impossible type assertion: %lN cannot have dynamic type %T"
+ " (wrong type for %S method)\n\thave %S%hT\n\twant %S%hT",
l, n->type, missing->sym, have->sym, have->type,
missing->sym, missing->type);
else
- yyerror("impossible type assertion: %+N cannot have dynamic type %T"
+ yyerror("impossible type assertion: %lN cannot have dynamic type %T"
" (missing %S method)", l, n->type, missing->sym);
goto error;
}
@@ -653,13 +764,13 @@ reswitch:
goto error;
switch(t->etype) {
default:
- yyerror("invalid operation: %#N (index of type %T)", n, t);
+ yyerror("invalid operation: %N (index of type %T)", n, t);
goto error;
case TARRAY:
defaultlit(&n->right, T);
if(n->right->type != T && !isint[n->right->type->etype])
- yyerror("non-integer array index %#N", n->right);
+ yyerror("non-integer array index %N", n->right);
n->type = t->type;
break;
@@ -675,7 +786,7 @@ reswitch:
case TSTRING:
defaultlit(&n->right, types[TUINT]);
if(n->right->type != T && !isint[n->right->type->etype])
- yyerror("non-integer string index %#N", n->right);
+ yyerror("non-integer string index %N", n->right);
n->type = types[TUINT8];
break;
}
@@ -689,11 +800,11 @@ reswitch:
if((t = l->type) == T)
goto error;
if(t->etype != TCHAN) {
- yyerror("invalid operation: %#N (receive from non-chan type %T)", n, t);
+ yyerror("invalid operation: %N (receive from non-chan type %T)", n, t);
goto error;
}
if(!(t->chan & Crecv)) {
- yyerror("invalid operation: %#N (receive from send-only type %T)", n, t);
+ yyerror("invalid operation: %N (receive from send-only type %T)", n, t);
goto error;
}
n->type = t->type;
@@ -701,7 +812,7 @@ reswitch:
case OSEND:
if(top & Erv) {
- yyerror("send statement %#N used as value; use select for non-blocking send", n);
+ yyerror("send statement %N used as value; use select for non-blocking send", n);
goto error;
}
ok |= Etop | Erv;
@@ -712,16 +823,16 @@ reswitch:
if((t = l->type) == T)
goto error;
if(t->etype != TCHAN) {
- yyerror("invalid operation: %#N (send to non-chan type %T)", n, t);
+ yyerror("invalid operation: %N (send to non-chan type %T)", n, t);
goto error;
}
if(!(t->chan & Csend)) {
- yyerror("invalid operation: %#N (send to receive-only type %T)", n, t);
+ yyerror("invalid operation: %N (send to receive-only type %T)", n, t);
goto error;
}
defaultlit(&n->right, t->type);
r = n->right;
- if((t = r->type) == T)
+ if(r->type == T)
goto error;
r = assignconv(r, l->type->type, "send");
// TODO: more aggressive
@@ -738,14 +849,19 @@ reswitch:
defaultlit(&n->right->left, T);
defaultlit(&n->right->right, T);
if(isfixedarray(n->left->type)) {
+ if(!islvalue(n->left)) {
+ yyerror("invalid operation %N (slice of unaddressable value)", n);
+ goto error;
+ }
n->left = nod(OADDR, n->left, N);
- typecheck(&n->left, top);
+ n->left->implicit = 1;
+ typecheck(&n->left, Erv);
}
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);
+ yyerror("invalid slice index %N (type %T)", n->right->left, t);
goto error;
}
}
@@ -753,7 +869,7 @@ reswitch:
if((t = n->right->right->type) == T)
goto error;
if(!isint[t->etype]) {
- yyerror("invalid slice index %#N (type %T)", n->right->right, t);
+ yyerror("invalid slice index %N (type %T)", n->right->right, t);
goto error;
}
}
@@ -777,7 +893,7 @@ reswitch:
n->type = t;
goto ret;
}
- yyerror("cannot slice %#N (type %T)", l, t);
+ yyerror("cannot slice %N (type %T)", l, t);
goto error;
/*
@@ -787,7 +903,7 @@ reswitch:
l = n->left;
if(l->op == ONAME && (r = unsafenmagic(n)) != N) {
if(n->isddd)
- yyerror("invalid use of ... with builtin %#N", l);
+ yyerror("invalid use of ... with builtin %N", l);
n = r;
goto reswitch;
}
@@ -795,7 +911,7 @@ reswitch:
l = n->left;
if(l->op == ONAME && l->etype != 0) {
if(n->isddd && l->etype != OAPPEND)
- yyerror("invalid use of ... with builtin %#N", l);
+ yyerror("invalid use of ... with builtin %N", l);
// builtin: OLEN, OCAP, etc.
n->op = l->etype;
n->left = n->right;
@@ -845,7 +961,7 @@ reswitch:
default:
n->op = OCALLFUNC;
if(t->etype != TFUNC) {
- yyerror("cannot call non-function %#N (type %T)", l, t);
+ yyerror("cannot call non-function %N (type %T)", l, t);
goto error;
}
break;
@@ -866,7 +982,7 @@ reswitch:
}
// multiple return
if(!(top & (Efnstruct | Etop))) {
- yyerror("multiple-value %#N() in single-value context", l);
+ yyerror("multiple-value %N() in single-value context", l);
goto ret;
}
n->type = getoutargx(l->type);
@@ -877,7 +993,7 @@ reswitch:
case OREAL:
case OIMAG:
ok |= Erv;
- if(onearg(n, "%#O", n->op) < 0)
+ if(onearg(n, "%O", n->op) < 0)
goto error;
typecheck(&n->left, Erv);
defaultlit(&n->left, T);
@@ -919,12 +1035,14 @@ reswitch:
}
break;
case TARRAY:
- if(t->bound >= 0 && l->op == ONAME) {
- r = nod(OXXX, N, N);
- nodconst(r, types[TINT], t->bound);
- r->orig = n;
- n = r;
- }
+ if(t->bound < 0) // slice
+ break;
+ if(callrecv(l)) // has call or receive
+ break;
+ r = nod(OXXX, N, N);
+ nodconst(r, types[TINT], t->bound);
+ r->orig = n;
+ n = r;
break;
}
n->type = types[TINT];
@@ -943,7 +1061,7 @@ reswitch:
n->right = r;
if(l->type->etype != r->type->etype) {
badcmplx:
- yyerror("invalid operation: %#N (complex of types %T, %T)", n, l->type, r->type);
+ yyerror("invalid operation: %N (complex of types %T, %T)", n, l->type, r->type);
goto error;
}
switch(l->type->etype) {
@@ -967,7 +1085,7 @@ reswitch:
goto ret;
case OCLOSE:
- if(onearg(n, "%#O", n->op) < 0)
+ if(onearg(n, "%O", n->op) < 0)
goto error;
typecheck(&n->left, Erv);
defaultlit(&n->left, T);
@@ -975,12 +1093,41 @@ reswitch:
if((t = l->type) == T)
goto error;
if(t->etype != TCHAN) {
- yyerror("invalid operation: %#N (non-chan type %T)", n, t);
+ yyerror("invalid operation: %N (non-chan type %T)", n, t);
+ goto error;
+ }
+ if(!(t->chan & Csend)) {
+ yyerror("invalid operation: %N (cannot close receive-only channel)", n);
goto error;
}
ok |= Etop;
goto ret;
+ case ODELETE:
+ args = n->list;
+ if(args == nil) {
+ yyerror("missing arguments to delete");
+ goto error;
+ }
+ if(args->next == nil) {
+ yyerror("missing second (key) argument to delete");
+ goto error;
+ }
+ if(args->next->next != nil) {
+ yyerror("too many arguments to delete");
+ goto error;
+ }
+ ok |= Etop;
+ typechecklist(args, Erv);
+ l = args->n;
+ r = args->next->n;
+ if(l->type != T && l->type->etype != TMAP) {
+ yyerror("first argument to delete must be map; have %lT", l->type);
+ goto error;
+ }
+ args->next->n = assignconv(r, l->type->down, "delete");
+ goto ret;
+
case OAPPEND:
ok |= Erv;
args = n->list;
@@ -996,6 +1143,7 @@ reswitch:
yyerror("first argument to append must be slice; have %lT", t);
goto error;
}
+
if(n->isddd) {
if(args->next == nil) {
yyerror("cannot use ... on first argument to append");
@@ -1005,6 +1153,10 @@ reswitch:
yyerror("too many arguments to append");
goto error;
}
+ if(istype(t->type, TUINT8) && istype(args->next->n->type, TSTRING)) {
+ defaultlit(&args->next->n, types[TSTRING]);
+ goto ret;
+ }
args->next->n = assignconv(args->next->n, t->orig, "append");
goto ret;
}
@@ -1028,6 +1180,7 @@ reswitch:
}
n->left = args->n;
n->right = args->next->n;
+ n->list = nil;
n->type = types[TINT];
typecheck(&n->left, Erv);
typecheck(&n->right, Erv);
@@ -1035,15 +1188,15 @@ reswitch:
goto error;
defaultlit(&n->left, T);
defaultlit(&n->right, T);
-
+
// copy([]byte, string)
if(isslice(n->left->type) && n->right->type->etype == TSTRING) {
- if (n->left->type->type == types[TUINT8])
+ if(eqtype(n->left->type->type, bytetype))
goto ret;
yyerror("arguments to copy have different element types: %lT and string", n->left->type);
goto error;
}
-
+
if(!isslice(n->left->type) || !isslice(n->right->type)) {
if(!isslice(n->left->type) && !isslice(n->right->type))
yyerror("arguments to copy must be slices; have %lT, %lT", n->left->type, n->right->type);
@@ -1067,8 +1220,8 @@ reswitch:
if((t = n->left->type) == T || n->type == T)
goto error;
if((n->op = convertop(t, n->type, &why)) == 0) {
- yyerror("cannot convert %+N to type %T%s", n->left, n->type, why);
- op = OCONV;
+ yyerror("cannot convert %lN to type %T%s", n->left, n->type, why);
+ n->op = OCONV;
}
switch(n->op) {
case OCONVNOP:
@@ -1092,6 +1245,7 @@ reswitch:
yyerror("missing argument to make");
goto error;
}
+ n->list = nil;
l = args->n;
args = args->next;
typecheck(&l, Etype);
@@ -1205,6 +1359,13 @@ reswitch:
case OPRINTN:
ok |= Etop;
typechecklist(n->list, Erv | Eindir); // Eindir: address does not escape
+ for(args=n->list; args; args=args->next) {
+ // Special case for print: int constant is int64, not int.
+ if(isconst(args->n, CTINT))
+ defaultlit(&args->n, types[TINT64]);
+ else
+ defaultlit(&args->n, T);
+ }
goto ret;
case OPANIC:
@@ -1232,6 +1393,16 @@ reswitch:
if(n->type == T)
goto error;
goto ret;
+
+ case OITAB:
+ ok |= Erv;
+ typecheck(&n->left, Erv);
+ if((t = n->left->type) == T)
+ goto error;
+ if(t->etype != TINTER)
+ fatal("OITAB of %T", t);
+ n->type = ptrto(types[TUINTPTR]);
+ goto ret;
/*
* statements
@@ -1271,7 +1442,7 @@ reswitch:
typechecklist(n->ninit, Etop);
typecheck(&n->ntest, Erv);
if(n->ntest != N && (t = n->ntest->type) != T && t->etype != TBOOL)
- yyerror("non-bool %+N used as for condition", n->ntest);
+ yyerror("non-bool %lN used as for condition", n->ntest);
typecheck(&n->nincr, Etop);
typechecklist(n->nbody, Etop);
goto ret;
@@ -1281,14 +1452,17 @@ reswitch:
typechecklist(n->ninit, Etop);
typecheck(&n->ntest, Erv);
if(n->ntest != N && (t = n->ntest->type) != T && t->etype != TBOOL)
- yyerror("non-bool %+N used as if condition", n->ntest);
+ yyerror("non-bool %lN used as if condition", n->ntest);
typechecklist(n->nbody, Etop);
typechecklist(n->nelse, Etop);
goto ret;
case ORETURN:
ok |= Etop;
- typechecklist(n->list, Erv | Efnstruct);
+ if(count(n->list) == 1)
+ typechecklist(n->list, Erv | Efnstruct);
+ else
+ typechecklist(n->list, Erv);
if(curfn == N) {
yyerror("return outside function");
goto error;
@@ -1368,21 +1542,21 @@ ret:
goto error;
}
if((top & (Erv|Etype)) == Etype && n->op != OTYPE) {
- yyerror("%#N is not a type", n);
+ yyerror("%N is not a type", n);
goto error;
}
if((ok & Ecall) && !(top & Ecall)) {
- yyerror("method %#N is not an expression, must be called", n);
+ yyerror("method %N is not an expression, must be called", n);
goto error;
}
// TODO(rsc): simplify
if((top & (Ecall|Erv|Etype)) && !(top & Etop) && !(ok & (Erv|Etype|Ecall))) {
- yyerror("%#N used as value", n);
+ yyerror("%N used as value", n);
goto error;
}
if((top & Etop) && !(top & (Ecall|Erv|Etype)) && !(ok & Etop)) {
if(n->diag == 0) {
- yyerror("%#N not used", n);
+ yyerror("%N not used", n);
n->diag = 1;
}
goto error;
@@ -1395,17 +1569,14 @@ ret:
goto out;
badcall1:
- yyerror("invalid argument %#N (type %T) for %#O", n->left, n->left->type, n->op);
+ yyerror("invalid argument %lN for %O", n->left, n->op);
goto error;
error:
n->type = T;
out:
- lineno = lno;
- n->typecheck = 1;
*np = n;
- return n;
}
static void
@@ -1425,6 +1596,7 @@ implicitstar(Node **nn)
if(!isfixedarray(t))
return;
n = nod(OIND, n, N);
+ n->implicit = 1;
typecheck(&n, Erv);
*nn = n;
}
@@ -1441,14 +1613,14 @@ onearg(Node *n, char *f, ...)
va_start(arg, f);
p = vsmprint(f, arg);
va_end(arg);
- yyerror("missing argument to %s: %#N", p, n);
+ yyerror("missing argument to %s: %N", p, n);
return -1;
}
if(n->list->next != nil) {
va_start(arg, f);
p = vsmprint(f, arg);
va_end(arg);
- yyerror("too many arguments to %s: %#N", p, n);
+ yyerror("too many arguments to %s: %N", p, n);
n->left = n->list->n;
n->list = nil;
return -1;
@@ -1464,17 +1636,17 @@ twoarg(Node *n)
if(n->left != N)
return 0;
if(n->list == nil) {
- yyerror("missing argument to %#O - %#N", n->op, n);
+ yyerror("missing argument to %O - %N", n->op, n);
return -1;
}
n->left = n->list->n;
if(n->list->next == nil) {
- yyerror("missing argument to %#O - %#N", n->op, n);
+ yyerror("missing argument to %O - %N", n->op, n);
n->list = nil;
return -1;
}
if(n->list->next->next != nil) {
- yyerror("too many arguments to %#O - %#N", n->op, n);
+ yyerror("too many arguments to %O - %N", n->op, n);
n->list = nil;
return -1;
}
@@ -1484,7 +1656,7 @@ twoarg(Node *n)
}
static Type*
-lookdot1(Sym *s, Type *t, Type *f, int dostrcmp)
+lookdot1(Node *errnode, Sym *s, Type *t, Type *f, int dostrcmp)
{
Type *r;
@@ -1495,7 +1667,12 @@ lookdot1(Sym *s, Type *t, Type *f, int dostrcmp)
if(f->sym != s)
continue;
if(r != T) {
- yyerror("ambiguous DOT reference %T.%S", t, s);
+ if(errnode)
+ yyerror("ambiguous selector %N", errnode);
+ else if(isptr[t->etype])
+ yyerror("ambiguous selector (%T).%S", t, s);
+ else
+ yyerror("ambiguous selector %T.%S", t, s);
break;
}
r = f;
@@ -1512,7 +1689,7 @@ looktypedot(Node *n, Type *t, int dostrcmp)
s = n->right->sym;
if(t->etype == TINTER) {
- f1 = lookdot1(s, t, t->type, dostrcmp);
+ f1 = lookdot1(n, s, t, t->type, dostrcmp);
if(f1 == T)
return 0;
@@ -1529,12 +1706,12 @@ looktypedot(Node *n, Type *t, int dostrcmp)
if(t->sym == S && isptr[t->etype])
tt = t->type;
- f2 = methtype(tt);
+ f2 = methtype(tt, 0);
if(f2 == T)
return 0;
- expandmeth(f2->sym, f2);
- f2 = lookdot1(s, f2, f2->xmethod, dostrcmp);
+ expandmeth(f2);
+ f2 = lookdot1(n, s, f2, f2->xmethod, dostrcmp);
if(f2 == T)
return 0;
@@ -1543,7 +1720,7 @@ looktypedot(Node *n, Type *t, int dostrcmp)
&& !isptr[t->etype]
&& f2->embedded != 2
&& !isifacemethod(f2->type)) {
- yyerror("invalid method expression %#N (needs pointer receiver: (*%T).%s)", n, t, f2->sym->name);
+ yyerror("invalid method expression %N (needs pointer receiver: (*%T).%hS)", n, t, f2->sym);
return 0;
}
@@ -1554,6 +1731,14 @@ looktypedot(Node *n, Type *t, int dostrcmp)
return 1;
}
+static Type*
+derefall(Type* t)
+{
+ while(t && t->etype == tptr)
+ t = t->type;
+ return t;
+}
+
static int
lookdot(Node *n, Type *t, int dostrcmp)
{
@@ -1565,21 +1750,21 @@ lookdot(Node *n, Type *t, int dostrcmp)
dowidth(t);
f1 = T;
if(t->etype == TSTRUCT || t->etype == TINTER)
- f1 = lookdot1(s, t, t->type, dostrcmp);
+ f1 = lookdot1(n, s, t, t->type, dostrcmp);
f2 = T;
if(n->left->type == t || n->left->type->sym == S) {
- f2 = methtype(t);
+ f2 = methtype(t, 0);
if(f2 != T) {
// Use f2->method, not f2->xmethod: adddot has
// already inserted all the necessary embedded dots.
- f2 = lookdot1(s, f2, f2->method, dostrcmp);
+ f2 = lookdot1(n, s, f2, f2->method, dostrcmp);
}
}
if(f1 != T) {
if(f2 != T)
- yyerror("ambiguous DOT reference %S as both field and method",
+ yyerror("%S is both field and method",
n->right->sym);
if(f1->width == BADWIDTH)
fatal("lookdot badwidth %T %p", f1, f1);
@@ -1588,6 +1773,7 @@ lookdot(Node *n, Type *t, int dostrcmp)
if(t->etype == TINTER) {
if(isptr[n->left->type->etype]) {
n->left = nod(OIND, n->left, N); // implicitstar
+ n->left->implicit = 1;
typecheck(&n->left, Erv);
}
n->op = ODOTINTER;
@@ -1602,7 +1788,8 @@ lookdot(Node *n, Type *t, int dostrcmp)
if(!eqtype(rcvr, tt)) {
if(rcvr->etype == tptr && eqtype(rcvr->type, tt)) {
checklvalue(n->left, "call pointer method on");
- addrescapes(n->left);
+ if(debug['N'])
+ addrescapes(n->left);
n->left = nod(OADDR, n->left, N);
n->left->implicit = 1;
typecheck(&n->left, Etype|Erv);
@@ -1610,14 +1797,22 @@ lookdot(Node *n, Type *t, int dostrcmp)
n->left = nod(OIND, n->left, N);
n->left->implicit = 1;
typecheck(&n->left, Etype|Erv);
+ } else if(tt->etype == tptr && tt->type->etype == tptr && eqtype(derefall(tt), rcvr)) {
+ yyerror("calling method %N with receiver %lN requires explicit dereference", n->right, n->left);
+ while(tt->etype == tptr) {
+ n->left = nod(OIND, n->left, N);
+ n->left->implicit = 1;
+ typecheck(&n->left, Etype|Erv);
+ tt = tt->type;
+ }
} else {
- // method is attached to wrong type?
fatal("method mismatch: %T for %T", rcvr, tt);
}
}
n->right = methodname(n->right, n->left->type);
n->xoffset = f2->width;
n->type = f2->type;
+// print("lookdot found [%p] %T\n", f2->type, f2->type);
n->op = ODOTMETH;
return 1;
}
@@ -1656,22 +1851,20 @@ typecheckaste(int op, Node *call, int isddd, Type *tstruct, NodeList *nl, char *
for(tl=tstruct->type; tl; tl=tl->down) {
if(tl->isddd) {
for(; tn; tn=tn->down) {
- exportassignok(tn->type, desc);
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->type, call, why);
+ yyerror("cannot use %T as type %T in argument to %N%s", tn->type, tl->type, call, why);
else
- yyerror("cannot use %T as type %T in %s%s", tn->type, tl->type->type, desc, why);
+ yyerror("cannot use %T as type %T in %s%s", tn->type, tl->type, desc, why);
}
}
goto out;
}
if(tn == T)
goto notenough;
- exportassignok(tn->type, desc);
if(assignop(tn->type, tl->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, call, why);
else
yyerror("cannot use %T as type %T in %s%s", tn->type, tl->type, desc, why);
}
@@ -1716,9 +1909,9 @@ typecheckaste(int op, Node *call, int isddd, Type *tstruct, NodeList *nl, char *
goto toomany;
if(isddd) {
if(call != N)
- yyerror("invalid use of ... in call to %#N", call);
+ yyerror("invalid use of ... in call to %N", call);
else
- yyerror("invalid use of ... in %#O", op);
+ yyerror("invalid use of ... in %O", op);
}
out:
@@ -1727,80 +1920,20 @@ out:
notenough:
if(call != N)
- yyerror("not enough arguments in call to %#N", call);
+ yyerror("not enough arguments in call to %N", call);
else
- yyerror("not enough arguments to %#O", op);
+ yyerror("not enough arguments to %O", op);
goto out;
toomany:
if(call != N)
- yyerror("too many arguments in call to %#N", call);
+ yyerror("too many arguments in call to %N", call);
else
- yyerror("too many arguments to %#O", op);
+ yyerror("too many arguments to %O", op);
goto out;
}
/*
- * do the export rules allow writing to this type?
- * cannot be implicitly assigning to any type with
- * an unavailable field.
- */
-int
-exportassignok(Type *t, char *desc)
-{
- Type *f;
- Sym *s;
-
- if(t == T)
- return 1;
- if(t->trecur)
- return 1;
- t->trecur = 1;
-
- switch(t->etype) {
- default:
- // most types can't contain others; they're all fine.
- break;
- case TSTRUCT:
- for(f=t->type; f; f=f->down) {
- if(f->etype != TFIELD)
- fatal("structas: not field");
- s = f->sym;
- // s == nil doesn't happen for embedded fields (they get the type symbol).
- // it only happens for fields in a ... struct.
- if(s != nil && !exportname(s->name) && s->pkg != localpkg) {
- char *prefix;
-
- prefix = "";
- if(desc != nil)
- prefix = " in ";
- else
- desc = "";
- yyerror("implicit assignment of unexported field '%s' of %T%s%s", s->name, t, prefix, desc);
- goto no;
- }
- if(!exportassignok(f->type, desc))
- goto no;
- }
- break;
-
- case TARRAY:
- if(t->bound < 0) // slices are pointers; that's fine
- break;
- if(!exportassignok(t->type, desc))
- goto no;
- break;
- }
- t->trecur = 0;
- return 1;
-
-no:
- t->trecur = 0;
- return 0;
-}
-
-
-/*
* type check composite
*/
@@ -1845,6 +1978,7 @@ keydup(Node *n, Node *hash[], ulong nhash)
b = 23;
break;
case CTINT:
+ case CTRUNE:
b = mpgetfix(n->val.u.xval);
break;
case CTFLT:
@@ -1872,7 +2006,7 @@ keydup(Node *n, Node *hash[], ulong nhash)
b = cmp.val.u.bval;
if(b) {
// too lazy to print the literal
- yyerror("duplicate key in map literal");
+ yyerror("duplicate key %N in map literal", n);
return;
}
}
@@ -1953,13 +2087,52 @@ inithash(Node *n, Node ***hash, Node **autohash, ulong nautohash)
return h;
}
+static int
+iscomptype(Type *t)
+{
+ switch(t->etype) {
+ case TARRAY:
+ case TSTRUCT:
+ case TMAP:
+ return 1;
+ case TPTR32:
+ case TPTR64:
+ switch(t->type->etype) {
+ case TARRAY:
+ case TSTRUCT:
+ case TMAP:
+ return 1;
+ }
+ break;
+ }
+ return 0;
+}
+
+static void
+pushtype(Node *n, Type *t)
+{
+ if(n == N || n->op != OCOMPLIT || !iscomptype(t))
+ return;
+
+ if(n->right == N) {
+ n->right = typenod(t);
+ n->implicit = 1; // don't print
+ n->right->implicit = 1; // * is okay
+ }
+ else if(debug['s']) {
+ typecheck(&n->right, Etype);
+ if(n->right->type != T && eqtype(n->right->type, t))
+ print("%lL: redundant type: %T\n", n->lineno, t);
+ }
+}
+
static void
typecheckcomplit(Node **np)
{
int bad, i, len, nerr;
- Node *l, *n, **hash;
+ Node *l, *n, *r, **hash;
NodeList *ll;
- Type *t, *f, *pushtype;
+ Type *t, *f;
Sym *s;
int32 lno;
ulong nhash;
@@ -1974,30 +2147,28 @@ typecheckcomplit(Node **np)
yyerror("missing type in composite literal");
goto error;
}
-
+
setlineno(n->right);
l = typecheck(&n->right /* sic */, Etype|Ecomplit);
if((t = l->type) == T)
goto error;
nerr = nerrors;
-
- // can omit type on composite literal values if the outer
- // composite literal is array, slice, or map, and the
- // element type is itself a struct, array, slice, or map.
- pushtype = T;
- if(t->etype == TARRAY || t->etype == TMAP) {
- pushtype = t->type;
- if(pushtype != T) {
- switch(pushtype->etype) {
- case TSTRUCT:
- case TARRAY:
- case TMAP:
- break;
- default:
- pushtype = T;
- break;
- }
+ n->type = t;
+
+ if(isptr[t->etype]) {
+ // For better or worse, we don't allow pointers as the composite literal type,
+ // except when using the &T syntax, which sets implicit on the OIND.
+ if(!n->right->implicit) {
+ yyerror("invalid pointer type %T for composite literal (use &%T instead)", t, t->type);
+ goto error;
+ }
+
+ // Also, the underlying type must be a struct, map, slice, or array.
+ if(!iscomptype(t)) {
+ yyerror("invalid pointer type %T for composite literal", t);
+ goto error;
}
+ t = t->type;
}
switch(t->etype) {
@@ -2040,11 +2211,11 @@ typecheckcomplit(Node **np)
}
}
- if(l->right->op == OCOMPLIT && l->right->right == N && pushtype != T)
- l->right->right = typenod(pushtype);
- typecheck(&l->right, Erv);
- defaultlit(&l->right, t->type);
- l->right = assignconv(l->right, t->type, "array element");
+ r = l->right;
+ pushtype(r, t->type);
+ typecheck(&r, Erv);
+ defaultlit(&r, t->type);
+ l->right = assignconv(r, t->type, "array element");
}
if(t->bound == -100)
t->bound = len;
@@ -2068,13 +2239,14 @@ typecheckcomplit(Node **np)
typecheck(&l->left, Erv);
defaultlit(&l->left, t->down);
l->left = assignconv(l->left, t->down, "map key");
- keydup(l->left, hash, nhash);
+ if (l->left->op != OCONV)
+ keydup(l->left, hash, nhash);
- if(l->right->op == OCOMPLIT && l->right->right == N && pushtype != T)
- l->right->right = typenod(pushtype);
- typecheck(&l->right, Erv);
- defaultlit(&l->right, t->type);
- l->right = assignconv(l->right, t->type, "map value");
+ r = l->right;
+ pushtype(r, t->type);
+ typecheck(&r, Erv);
+ defaultlit(&r, t->type);
+ l->right = assignconv(r, t->type, "map value");
}
n->op = OMAPLIT;
break;
@@ -2095,8 +2267,10 @@ typecheckcomplit(Node **np)
s = f->sym;
if(s != nil && !exportname(s->name) && s->pkg != localpkg)
yyerror("implicit assignment of unexported field '%s' in %T literal", s->name, t);
+ // No pushtype allowed here. Must name fields for that.
ll->n = assignconv(ll->n, f->type, "field value");
ll->n = nod(OKEY, newname(f->sym), ll->n);
+ ll->n->left->type = f;
ll->n->left->typecheck = 1;
f = f->down;
}
@@ -2117,26 +2291,31 @@ typecheckcomplit(Node **np)
}
s = l->left->sym;
if(s == S) {
- yyerror("invalid field name %#N in struct initializer", l->left);
+ yyerror("invalid field name %N in struct initializer", l->left);
typecheck(&l->right, Erv);
continue;
}
+
// Sym might have resolved to name in other top-level
// package, because of import dot. Redirect to correct sym
// before we do the lookup.
- if(s->pkg != localpkg)
+ if(s->pkg != localpkg && exportname(s->name))
s = lookup(s->name);
- l->left = newname(s);
- l->left->typecheck = 1;
- f = lookdot1(s, t, t->type, 0);
- typecheck(&l->right, Erv);
+
+ f = lookdot1(nil, s, t, t->type, 0);
if(f == nil) {
- yyerror("unknown %T field '%s' in struct literal", t, s->name);
+ yyerror("unknown %T field '%S' in struct literal", t, s);
continue;
}
+ l->left = newname(s);
+ l->left->typecheck = 1;
+ l->left->type = f;
s = f->sym;
fielddup(newname(s), hash, nhash);
- l->right = assignconv(l->right, f->type, "field value");
+ r = l->right;
+ // No pushtype allowed here. Tried and rejected.
+ typecheck(&r, Erv);
+ l->right = assignconv(r, f->type, "field value");
}
}
n->op = OSTRUCTLIT;
@@ -2144,7 +2323,14 @@ typecheckcomplit(Node **np)
}
if(nerr != nerrors)
goto error;
- n->type = t;
+
+ if(isptr[n->type->etype]) {
+ n = nod(OPTRLIT, n, N);
+ n->typecheck = 1;
+ n->type = n->left->type;
+ n->left->type = t;
+ n->left->typecheck = 1;
+ }
*np = n;
lineno = lno;
@@ -2157,82 +2343,6 @@ error:
}
/*
- * the address of n has been taken and might be used after
- * the current function returns. mark any local vars
- * as needing to move to the heap.
- */
-static void
-addrescapes(Node *n)
-{
- char buf[100];
- switch(n->op) {
- default:
- // probably a type error already.
- // dump("addrescapes", n);
- break;
-
- case ONAME:
- if(n->noescape)
- break;
- switch(n->class) {
- case PPARAMREF:
- addrescapes(n->defn);
- break;
- case PPARAM:
- case PPARAMOUT:
- // if func param, need separate temporary
- // to hold heap pointer.
- // the function type has already been checked
- // (we're in the function body)
- // so the param already has a valid xoffset.
-
- // expression to refer to stack copy
- n->stackparam = nod(OPARAM, n, N);
- n->stackparam->type = n->type;
- n->stackparam->addable = 1;
- if(n->xoffset == BADWIDTH)
- fatal("addrescapes before param assignment");
- n->stackparam->xoffset = n->xoffset;
- n->xoffset = 0;
- // fallthrough
- case PAUTO:
-
- n->class |= PHEAP;
- n->addable = 0;
- n->ullman = 2;
- n->xoffset = 0;
-
- // create stack variable to hold pointer to heap
- n->heapaddr = nod(ONAME, N, N);
- n->heapaddr->type = ptrto(n->type);
- snprint(buf, sizeof buf, "&%S", n->sym);
- n->heapaddr->sym = lookup(buf);
- n->heapaddr->class = PHEAP-1; // defer tempname to allocparams
- n->heapaddr->ullman = 1;
- n->curfn->dcl = list(n->curfn->dcl, n->heapaddr);
-
- break;
- }
- break;
-
- case OIND:
- case ODOTPTR:
- break;
-
- case ODOT:
- case OINDEX:
- // ODOTPTR has already been introduced,
- // so these are the non-pointer ODOT and OINDEX.
- // In &x[0], if x is a slice, then x does not
- // escape--the pointer inside x does, but that
- // is always a heap pointer anyway.
- if(!isslice(n->left->type))
- addrescapes(n->left);
- break;
- }
-}
-
-/*
* lvalue etc
*/
int
@@ -2262,7 +2372,7 @@ static void
checklvalue(Node *n, char *verb)
{
if(!islvalue(n))
- yyerror("cannot %s %#N", verb, n);
+ yyerror("cannot %s %N", verb, n);
}
static void
@@ -2274,7 +2384,7 @@ checkassign(Node *n)
n->etype = 1;
return;
}
- yyerror("cannot assign to %#N", n);
+ yyerror("cannot assign to %N", n);
}
static void
@@ -2309,8 +2419,6 @@ typecheckas(Node *n)
if(n->right && n->right->type != T) {
if(n->left->type != T)
n->right = assignconv(n->right, n->left->type, "assignment");
- else if(!isblank(n->left))
- exportassignok(n->right->type, "assignment");
}
if(n->left->defn == n && n->left->ntype == N) {
defaultlit(&n->right, T);
@@ -2331,10 +2439,9 @@ checkassignto(Type *src, Node *dst)
char *why;
if(assignop(src, dst->type, &why) == 0) {
- yyerror("cannot assign %T to %+N in multiple assignment%s", src, dst, why);
+ yyerror("cannot assign %T to %lN in multiple assignment%s", src, dst, why);
return;
}
- exportassignok(dst->type, "multiple assignment");
}
static void
@@ -2381,10 +2488,7 @@ typecheckas2(Node *n)
if(cl == 1 && cr == 2 && l->op == OINDEXMAP) {
if(l->type == T)
goto out;
- n->op = OAS2MAPW;
- n->rlist->n = assignconv(r, l->type, "assignment");
- r = n->rlist->next->n;
- n->rlist->next->n = assignconv(r, types[TBOOL], "assignment");
+ yyerror("assignment count mismatch: %d = %d (use delete)", cl, cr);
goto out;
}
@@ -2462,12 +2566,11 @@ typecheckfunc(Node *n)
{
Type *t, *rcvr;
-//dump("nname", n->nname);
typecheck(&n->nname, Erv | Easgn);
if((t = n->nname->type) == T)
return;
n->type = t;
-
+ t->nname = n->nname;
rcvr = getthisx(t)->type;
if(rcvr != nil && n->shortname != N && !isblank(n->shortname))
addmethod(n->shortname->sym, t, 1);
@@ -2497,7 +2600,7 @@ stringtoarraylit(Node **np)
while(p < ep)
l = list(l, nod(OKEY, nodintconst(i++), nodintconst((uchar)*p++)));
} else {
- // utf-8 []int
+ // utf-8 []rune
while(p < ep) {
p += chartorune(&r, p);
l = list(l, nod(OKEY, nodintconst(i++), nodintconst(r)));
@@ -2514,7 +2617,7 @@ getforwtype(Node *n)
{
Node *f1, *f2;
- for(f1=f2=n; ; n=n->ntype) {
+ for(f2=n; ; n=n->ntype) {
if((n = resolve(n)) == N || n->op != OTYPE)
return T;
@@ -2537,6 +2640,7 @@ static void
domethod(Node *n)
{
Node *nt;
+ Type *t;
nt = n->type->nname;
typecheck(&nt, Etype);
@@ -2546,6 +2650,20 @@ domethod(Node *n)
n->type->nod = N;
return;
}
+
+ // If we have
+ // type I interface {
+ // M(_ int)
+ // }
+ // then even though I.M looks like it doesn't care about the
+ // value of its argument, a specific implementation of I may
+ // care. The _ would suppress the assignment to that argument
+ // while generating a call, so remove it.
+ for(t=getinargx(nt->type)->type; t; t=t->down) {
+ if(t->sym != nil && strcmp(t->sym->name, "_") == 0)
+ t->sym = nil;
+ }
+
*n->type = *nt->type;
n->type->nod = N;
checkwidth(n->type);
@@ -2602,6 +2720,7 @@ copytype(Node *n, Type *t)
t->vargen = n->vargen;
t->siggen = 0;
t->method = nil;
+ t->xmethod = nil;
t->nod = N;
t->printed = 0;
t->deferwidth = 0;
@@ -2759,7 +2878,7 @@ typecheckdef(Node *n)
goto ret;
}
if(!isideal(e->type) && !eqtype(t, e->type)) {
- yyerror("cannot use %+N as type %T in const initializer", e, t);
+ yyerror("cannot use %lN as type %T in const initializer", e, t);
goto ret;
}
convlit(&e, t);
@@ -2772,6 +2891,7 @@ typecheckdef(Node *n)
if(n->ntype != N) {
typecheck(&n->ntype, Etype);
n->type = n->ntype->type;
+
if(n->type == T) {
n->diag = 1;
goto ret;
@@ -2816,6 +2936,8 @@ typecheckdef(Node *n)
}
ret:
+ if(n->op != OLITERAL && n->type != T && isideal(n->type))
+ fatal("got %T for %N", n->type, n);
if(typecheckdefstack->n != n)
fatal("typecheckdefstack mismatch");
l = typecheckdefstack;
diff --git a/src/cmd/gc/unsafe.c b/src/cmd/gc/unsafe.c
index d304077c8..95200ad41 100644
--- a/src/cmd/gc/unsafe.c
+++ b/src/cmd/gc/unsafe.c
@@ -2,12 +2,15 @@
// 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"
/*
* look for
* unsafe.Sizeof
* unsafe.Offsetof
+ * unsafe.Alignof
* rewrite with a constant
*/
Node*
@@ -20,7 +23,7 @@ unsafenmagic(Node *nn)
Val val;
Node *fn;
NodeList *args;
-
+
fn = nn->left;
args = nn->list;
@@ -78,10 +81,10 @@ no:
return N;
bad:
- yyerror("invalid expression %#N", nn);
+ yyerror("invalid expression %N", nn);
v = 0;
goto ret;
-
+
yes:
if(args->next != nil)
yyerror("extra arguments for %S", s);
@@ -91,7 +94,23 @@ ret:
val.u.xval = mal(sizeof(*n->val.u.xval));
mpmovecfix(val.u.xval, v);
n = nod(OLITERAL, N, N);
+ n->orig = nn;
n->val = val;
n->type = types[TUINTPTR];
+ nn->type = types[TUINTPTR];
return n;
}
+
+int
+isunsafebuiltin(Node *n)
+{
+ if(n == N || n->op != ONAME || n->sym == S || n->sym->pkg != unsafepkg)
+ return 0;
+ if(strcmp(n->sym->name, "Sizeof") == 0)
+ return 1;
+ if(strcmp(n->sym->name, "Offsetof") == 0)
+ return 1;
+ if(strcmp(n->sym->name, "Alignof") == 0)
+ return 1;
+ return 0;
+}
diff --git a/src/cmd/gc/unsafe.go b/src/cmd/gc/unsafe.go
index db27d7425..c3c627815 100644
--- a/src/cmd/gc/unsafe.go
+++ b/src/cmd/gc/unsafe.go
@@ -6,6 +6,8 @@
// to update builtin.c.boot. This is not done automatically
// to avoid depending on having a working compiler binary.
+// +build ignore
+
package PACKAGE
type Pointer uintptr // not really; filled in by compiler
@@ -14,9 +16,3 @@ type Pointer uintptr // not really; filled in by compiler
func Offsetof(any) uintptr
func Sizeof(any) uintptr
func Alignof(any) uintptr
-
-func Typeof(i interface{}) (typ interface{})
-func Reflect(i interface{}) (typ interface{}, addr Pointer)
-func Unreflect(typ interface{}, addr Pointer) (ret interface{})
-func New(typ interface{}) Pointer
-func NewArray(typ interface{}, n int) Pointer
diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c
index 9cd4ee919..7dfd34a7a 100644
--- a/src/cmd/gc/walk.c
+++ b/src/cmd/gc/walk.c
@@ -2,16 +2,17 @@
// 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"
static Node* walkprint(Node*, NodeList**, int);
-static Node* conv(Node*, Type*);
static Node* mapfn(char*, Type*);
-static Node* makenewvar(Type*, NodeList**, Node**);
+static Node* mapfndel(char*, Type*);
static Node* ascompatee1(int, Node*, Node*, NodeList**);
static NodeList* ascompatee(int, NodeList*, NodeList*, NodeList**);
static NodeList* ascompatet(int, NodeList*, Type**, int, NodeList**);
-static NodeList* ascompatte(int, int, Type**, NodeList*, int, NodeList**);
+static NodeList* ascompatte(int, Node*, int, Type**, NodeList*, int, NodeList**);
static Node* convas(Node*, NodeList**);
static void heapmoves(void);
static NodeList* paramstoheap(Type **argin, int out);
@@ -20,6 +21,7 @@ static NodeList* reorder3(NodeList*);
static Node* addstr(Node*, NodeList**);
static Node* appendslice(Node*, NodeList**);
static Node* append(Node*, NodeList**);
+static void walkcompare(Node**, NodeList**);
// can this code branch reach the end
// without an unconditional RETURN
@@ -60,7 +62,6 @@ walk(Node *fn)
{
char s[50];
NodeList *l;
- Node *n;
int lno;
curfn = fn;
@@ -74,15 +75,33 @@ walk(Node *fn)
yyerror("function ends without a return statement");
lno = lineno;
+
+ // Final typecheck for any unused variables.
+ // It's hard to be on the heap when not-used, but best to be consistent about &~PHEAP here and below.
+ for(l=fn->dcl; l; l=l->next)
+ if(l->n->op == ONAME && (l->n->class&~PHEAP) == PAUTO)
+ typecheck(&l->n, Erv | Easgn);
+
+ // Propagate the used flag for typeswitch variables up to the NONAME in it's definition.
+ for(l=fn->dcl; l; l=l->next)
+ if(l->n->op == ONAME && (l->n->class&~PHEAP) == PAUTO && l->n->defn && l->n->defn->op == OTYPESW && l->n->used)
+ l->n->defn->left->used++;
+
for(l=fn->dcl; l; l=l->next) {
- n = l->n;
- if(n->op != ONAME || n->class != PAUTO)
+ if(l->n->op != ONAME || (l->n->class&~PHEAP) != PAUTO || l->n->sym->name[0] == '&' || l->n->used)
continue;
- lineno = n->lineno;
- typecheck(&n, Erv | Easgn); // only needed for unused variables
- if(!n->used && n->sym->name[0] != '&' && !nsyntaxerrors)
- yyerror("%S declared and not used", n->sym);
- }
+ if(l->n->defn && l->n->defn->op == OTYPESW) {
+ if(l->n->defn->left->used)
+ continue;
+ lineno = l->n->defn->left->lineno;
+ yyerror("%S declared and not used", l->n->sym);
+ l->n->defn->left->used = 1; // suppress repeats
+ } else {
+ lineno = l->n->lineno;
+ yyerror("%S declared and not used", l->n->sym);
+ }
+ }
+
lineno = lno;
if(nerrors != 0)
return;
@@ -119,11 +138,12 @@ static int
paramoutheap(Node *fn)
{
NodeList *l;
-
+
for(l=fn->dcl; l; l=l->next) {
switch(l->n->class) {
+ case PPARAMOUT:
case PPARAMOUT|PHEAP:
- return 1;
+ return l->n->addrtaken;
case PAUTO:
case PAUTO|PHEAP:
// stop early - parameters are over
@@ -147,6 +167,8 @@ walkstmt(Node **np)
setlineno(n);
+ walkstmtlist(n->ninit);
+
switch(n->op) {
default:
if(n->op == ONAME)
@@ -162,7 +184,6 @@ walkstmt(Node **np)
case OAS2DOTTYPE:
case OAS2RECV:
case OAS2FUNC:
- case OAS2MAPW:
case OAS2MAPR:
case OCLOSE:
case OCOPY:
@@ -170,6 +191,7 @@ walkstmt(Node **np)
case OCALLINTER:
case OCALL:
case OCALLFUNC:
+ case ODELETE:
case OSEND:
case ORECV:
case OPRINT:
@@ -177,14 +199,12 @@ walkstmt(Node **np)
case OPANIC:
case OEMPTY:
case ORECOVER:
- if(n->typecheck == 0) {
- dump("missing typecheck:", n);
- fatal("missing typecheck");
- }
+ if(n->typecheck == 0)
+ fatal("missing typecheck: %+N", n);
init = n->ninit;
n->ninit = nil;
walkexpr(&n, &init);
- n->ninit = concat(init, n->ninit);
+ addinit(&n, init);
break;
case OBREAK:
@@ -223,20 +243,18 @@ walkstmt(Node **np)
break;
case OFOR:
- walkstmtlist(n->ninit);
if(n->ntest != N) {
walkstmtlist(n->ntest->ninit);
init = n->ntest->ninit;
n->ntest->ninit = nil;
walkexpr(&n->ntest, &init);
- n->ntest->ninit = concat(init, n->ntest->ninit);
+ addinit(&n->ntest, init);
}
walkstmt(&n->nincr);
walkstmtlist(n->nbody);
break;
case OIF:
- walkstmtlist(n->ninit);
walkexpr(&n->ntest, &n->ninit);
walkstmtlist(n->nbody);
walkstmtlist(n->nelse);
@@ -279,15 +297,18 @@ walkstmt(Node **np)
// OAS2FUNC in disguise
f = n->list->n;
if(f->op != OCALLFUNC && f->op != OCALLMETH && f->op != OCALLINTER)
- fatal("expected return of call, have %#N", f);
+ fatal("expected return of call, have %N", f);
n->list = concat(list1(f), ascompatet(n->op, rl, &f->type, 0, &n->ninit));
break;
}
+
+ // move function calls out, to make reorder3's job easier.
+ walkexprlistsafe(n->list, &n->ninit);
ll = ascompatee(n->op, rl, n->list, &n->ninit);
n->list = reorder3(ll);
break;
}
- ll = ascompatte(n->op, 0, getoutarg(curfn->type), n->list, 1, &n->ninit);
+ ll = ascompatte(n->op, nil, 0, getoutarg(curfn->type), n->list, 1, &n->ninit);
n->list = ll;
break;
@@ -309,6 +330,9 @@ walkstmt(Node **np)
break;
}
+ if(n->op == ONAME)
+ fatal("walkstmt ended up with name: %+N", n);
+
*np = n;
}
@@ -361,6 +385,12 @@ walkexpr(Node **np, NodeList **init)
fatal("walkexpr init == &n->ninit");
}
+ if(n->ninit != nil) {
+ walkstmtlist(n->ninit);
+ *init = concat(*init, n->ninit);
+ n->ninit = nil;
+ }
+
// annoying case - not typechecked
if(n->op == OKEY) {
walkexpr(&n->left, init);
@@ -373,19 +403,14 @@ walkexpr(Node **np, NodeList **init)
if(debug['w'] > 1)
dump("walk-before", n);
- if(n->typecheck != 1) {
- dump("missed typecheck", n);
- fatal("missed typecheck");
- }
-
- t = T;
- et = Txxx;
+ if(n->typecheck != 1)
+ fatal("missed typecheck: %+N\n", n);
switch(n->op) {
default:
dump("walk", n);
fatal("walkexpr: switch 1 unknown op %N", n);
- goto ret;
+ break;
case OTYPE:
case ONONAME:
@@ -407,10 +432,14 @@ walkexpr(Node **np, NodeList **init)
walkexpr(&n->left, init);
goto ret;
+ case OITAB:
+ walkexpr(&n->left, init);
+ goto ret;
+
case OLEN:
case OCAP:
walkexpr(&n->left, init);
-
+
// replace len(*[10]int) with 10.
// delayed until now to preserve side effects.
t = n->left->type;
@@ -422,7 +451,7 @@ walkexpr(Node **np, NodeList **init)
n->typecheck = 1;
}
goto ret;
-
+
case OLSH:
case ORSH:
case OAND:
@@ -430,8 +459,6 @@ walkexpr(Node **np, NodeList **init)
case OXOR:
case OSUB:
case OMUL:
- case OEQ:
- case ONE:
case OLT:
case OLE:
case OGE:
@@ -441,7 +468,14 @@ walkexpr(Node **np, NodeList **init)
walkexpr(&n->left, init);
walkexpr(&n->right, init);
goto ret;
-
+
+ case OEQ:
+ case ONE:
+ walkexpr(&n->left, init);
+ walkexpr(&n->right, init);
+ walkcompare(&n, init);
+ goto ret;
+
case OANDAND:
case OOROR:
walkexpr(&n->left, init);
@@ -450,7 +484,7 @@ walkexpr(Node **np, NodeList **init)
// save elsewhere and store on the eventual n->right.
ll = nil;
walkexpr(&n->right, &ll);
- n->right->ninit = concat(n->right->ninit, ll);
+ addinit(&n->right, ll);
goto ret;
case OPRINT:
@@ -482,7 +516,7 @@ walkexpr(Node **np, NodeList **init)
goto ret;
walkexpr(&n->left, init);
walkexprlist(n->list, init);
- ll = ascompatte(n->op, n->isddd, getinarg(t), n->list, 0, init);
+ ll = ascompatte(n->op, n, n->isddd, getinarg(t), n->list, 0, init);
n->list = reorder1(ll);
goto ret;
@@ -499,7 +533,7 @@ walkexpr(Node **np, NodeList **init)
walkexpr(&n->left, init);
walkexprlist(n->list, init);
- ll = ascompatte(n->op, n->isddd, getinarg(t), n->list, 0, init);
+ ll = ascompatte(n->op, n, n->isddd, getinarg(t), n->list, 0, init);
n->list = reorder1(ll);
goto ret;
@@ -509,8 +543,8 @@ walkexpr(Node **np, NodeList **init)
goto ret;
walkexpr(&n->left, init);
walkexprlist(n->list, init);
- ll = ascompatte(n->op, 0, getthis(t), list1(n->left->left), 0, init);
- lr = ascompatte(n->op, n->isddd, getinarg(t), n->list, 0, init);
+ ll = ascompatte(n->op, n, 0, getthis(t), list1(n->left->left), 0, init);
+ lr = ascompatte(n->op, n, n->isddd, getinarg(t), n->list, 0, init);
ll = concat(ll, lr);
n->left->left = N;
ullmancalc(n->left);
@@ -554,7 +588,7 @@ walkexpr(Node **np, NodeList **init)
walkexprlistsafe(n->list, init);
walkexpr(&r, init);
l = n->list->n;
-
+
// all the really hard stuff - explicit function calls and so on -
// is gone, but map assignments remain.
// if there are map assignments here, assign via
@@ -563,8 +597,7 @@ walkexpr(Node **np, NodeList **init)
// and map index has an implicit one.
lpost = nil;
if(l->op == OINDEXMAP) {
- var = nod(OXXX, N, N);
- tempname(var, l->type);
+ var = temp(l->type);
n->list->n = var;
a = nod(OAS, l, var);
typecheck(&a, Etop);
@@ -572,8 +605,7 @@ walkexpr(Node **np, NodeList **init)
}
l = n->list->next->n;
if(l->op == OINDEXMAP) {
- var = nod(OXXX, N, N);
- tempname(var, l->type);
+ var = temp(l->type);
n->list->next->n = var;
a = nod(OAS, l, var);
typecheck(&a, Etop);
@@ -609,15 +641,13 @@ walkexpr(Node **np, NodeList **init)
n->op = OAS2FUNC;
goto as2func;
- case OAS2MAPW:
- // map[] = a,b - mapassign2
- // a,b = m[i];
+ case ODELETE:
*init = concat(*init, n->ninit);
n->ninit = nil;
- walkexprlistsafe(n->list, init);
l = n->list->n;
- t = l->left->type;
- n = mkcall1(mapfn("mapassign2", t), T, init, typename(t), l->left, l->right, n->rlist->n, n->rlist->next->n);
+ r = n->list->next->n;
+ t = l->type;
+ n = mkcall1(mapfndel("mapdelete", t), t->down, init, typename(t), l, r);
goto ret;
case OAS2DOTTYPE:
@@ -651,7 +681,7 @@ walkexpr(Node **np, NodeList **init)
if(n->op == ODOTTYPE2)
*p++ = '2';
*p = '\0';
-
+
fn = syslook(buf, 1);
ll = list1(typename(n->type));
ll = list(ll, n->left);
@@ -682,7 +712,7 @@ walkexpr(Node **np, NodeList **init)
else
*p++ = 'I';
*p = '\0';
-
+
fn = syslook(buf, 1);
ll = nil;
if(!isinter(n->left->type))
@@ -843,6 +873,7 @@ walkexpr(Node **np, NodeList **init)
// delayed until now because "abc"[2] is not
// an ideal constant.
nodconst(n, n->type, n->left->val.u.sval->s[v]);
+ n->typecheck = 1;
}
}
goto ret;
@@ -897,7 +928,7 @@ walkexpr(Node **np, NodeList **init)
}
if(v1 >= 0 && v2 >= 0 && v1 > v2)
yyerror("inverted slice range");
-
+
if(n->op == OSLICEARR)
goto slicearray;
@@ -928,7 +959,7 @@ walkexpr(Node **np, NodeList **init)
l,
nodintconst(t->type->width));
}
- n->etype = et; // preserve no-typecheck flag from OSLICE to the slice* call.
+ n->etype = et; // preserve no-typecheck flag from OSLICE to the slice* call.
goto ret;
slicearray:
@@ -953,29 +984,22 @@ walkexpr(Node **np, NodeList **init)
nodintconst(t->type->width));
goto ret;
- case OADDR:;
- Node *nvar, *nstar;
-
- // turn &Point(1, 2) or &[]int(1, 2) or &[...]int(1, 2) into allocation.
- // initialize with
- // nvar := new(*Point);
- // *nvar = Point(1, 2);
- // and replace expression with nvar
- switch(n->left->op) {
- case OARRAYLIT:
- case OMAPLIT:
- case OSTRUCTLIT:
- nvar = makenewvar(n->type, init, &nstar);
- anylit(0, n->left, nstar, init);
- n = nvar;
- goto ret;
- }
-
+ case OADDR:
walkexpr(&n->left, init);
goto ret;
case ONEW:
- n = callnew(n->type->type);
+ if(n->esc == EscNone && n->type->type->width < (1<<16)) {
+ r = temp(n->type->type);
+ r = nod(OAS, r, N); // zero temp
+ typecheck(&r, Etop);
+ *init = list(*init, r);
+ r = nod(OADDR, r->left, N);
+ typecheck(&r, Erv);
+ n = r;
+ } else {
+ n = callnew(n->type->type);
+ }
goto ret;
case OCMPSTR:
@@ -987,6 +1011,7 @@ walkexpr(Node **np, NodeList **init)
r = nod(n->etype, nod(OLEN, n->left, N), nod(OLEN, n->right, N));
typecheck(&r, Erv);
walkexpr(&r, init);
+ r->type = n->type;
n = r;
goto ret;
}
@@ -999,6 +1024,7 @@ walkexpr(Node **np, NodeList **init)
r = nod(n->etype, nod(OLEN, n->left->left, N), nodintconst(0));
typecheck(&r, Erv);
walkexpr(&r, init);
+ r->type = n->type;
n = r;
goto ret;
}
@@ -1025,6 +1051,8 @@ walkexpr(Node **np, NodeList **init)
walkexpr(&r, nil);
}
typecheck(&r, Erv);
+ if(n->type->etype != TBOOL) fatal("cmp %T", n->type);
+ r->type = n->type;
n = r;
goto ret;
@@ -1049,10 +1077,14 @@ walkexpr(Node **np, NodeList **init)
l);
}
goto ret;
-
+
case OAPPEND:
- if(n->isddd)
- n = appendslice(n, init);
+ 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);
+ }
else
n = append(n, init);
goto ret;
@@ -1061,7 +1093,7 @@ walkexpr(Node **np, NodeList **init)
if(n->right->type->etype == TSTRING)
fn = syslook("slicestringcopy", 1);
else
- fn = syslook("slicecopy", 1);
+ fn = syslook("copy", 1);
argtype(fn, n->left->type);
argtype(fn, n->right->type);
n = mkcall1(fn, n->type, init,
@@ -1121,8 +1153,8 @@ walkexpr(Node **np, NodeList **init)
goto ret;
case OARRAYRUNESTR:
- // sliceinttostring([]int) string;
- n = mkcall("sliceinttostring", n->type, init, n->left);
+ // slicerunetostring([]rune) string;
+ n = mkcall("slicerunetostring", n->type, init, n->left);
goto ret;
case OSTRARRAYBYTE:
@@ -1131,8 +1163,8 @@ walkexpr(Node **np, NodeList **init)
goto ret;
case OSTRARRAYRUNE:
- // stringtosliceint(string) []int
- n = mkcall("stringtosliceint", n->type, init, n->left);
+ // stringtoslicerune(string) []rune
+ n = mkcall("stringtoslicerune", n->type, init, n->left);
goto ret;
case OCMPIFACE:
@@ -1146,20 +1178,27 @@ walkexpr(Node **np, NodeList **init)
argtype(fn, n->right->type);
argtype(fn, n->left->type);
r = mkcall1(fn, n->type, init, n->left, n->right);
- if(n->etype == ONE) {
+ if(n->etype == ONE)
r = nod(ONOT, r, N);
- typecheck(&r, Erv);
- }
+
+ // check itable/type before full compare.
+ if(n->etype == OEQ)
+ r = nod(OANDAND, nod(OEQ, nod(OITAB, n->left, N), nod(OITAB, n->right, N)), r);
+ else
+ r = nod(OOROR, nod(ONE, nod(OITAB, n->left, N), nod(OITAB, n->right, N)), r);
+ typecheck(&r, Erv);
+ walkexpr(&r, nil);
+ r->type = n->type;
n = r;
goto ret;
case OARRAYLIT:
case OMAPLIT:
case OSTRUCTLIT:
- nvar = nod(OXXX, N, N);
- tempname(nvar, n->type);
- anylit(0, n, nvar, init);
- n = nvar;
+ case OPTRLIT:
+ var = temp(n->type);
+ anylit(0, n, var, init);
+ n = var;
goto ret;
case OSEND:
@@ -1173,34 +1212,20 @@ walkexpr(Node **np, NodeList **init)
fatal("missing switch %O", n->op);
ret:
+ ullmancalc(n);
+
if(debug['w'] && n != N)
dump("walk", n);
- ullmancalc(n);
lineno = lno;
*np = n;
}
static Node*
-makenewvar(Type *t, NodeList **init, Node **nstar)
-{
- Node *nvar, *nas;
-
- nvar = nod(OXXX, N, N);
- tempname(nvar, t);
- nas = nod(OAS, nvar, callnew(t->type));
- typecheck(&nas, Etop);
- walkexpr(&nas, init);
- *init = list(*init, nas);
-
- *nstar = nod(OIND, nvar, N);
- typecheck(nstar, Erv);
- return nvar;
-}
-
-static Node*
ascompatee1(int op, Node *l, Node *r, NodeList **init)
{
+ USED(op);
+
return convas(nod(OAS, l, r), init);
}
@@ -1227,7 +1252,7 @@ ascompatee(int op, NodeList *nl, NodeList *nr, NodeList **init)
// cannot happen: caller checked that lists had same length
if(ll || lr)
- yyerror("error in shape across %O", op);
+ yyerror("error in shape across %+H %O %+H", nl, op, nr);
return nn;
}
@@ -1257,6 +1282,8 @@ ascompatet(int op, NodeList *nl, Type **nr, int fp, NodeList **init)
int ucount;
NodeList *nn, *mm;
+ USED(op);
+
/*
* check assign type list to
* a expression list. called in
@@ -1279,8 +1306,7 @@ ascompatet(int op, NodeList *nl, Type **nr, int fp, NodeList **init)
// deferred until all the return arguments
// have been pulled from the output arguments
if(fncall(l, r->type)) {
- tmp = nod(OXXX, N, N);
- tempname(tmp, r->type);
+ tmp = temp(r->type);
typecheck(&tmp, Erv);
a = nod(OAS, l, tmp);
a = convas(a, init);
@@ -1298,10 +1324,11 @@ ascompatet(int op, NodeList *nl, Type **nr, int fp, NodeList **init)
}
if(ll != nil || r != T)
- yyerror("assignment count mismatch: %d = %d",
+ yyerror("ascompatet: assignment count mismatch: %d = %d",
count(nl), structcount(*nr));
+
if(ucount)
- fatal("reorder2: too many function calls evaluating parameters");
+ fatal("ascompatet: too many function calls evaluating parameters");
return concat(nn, mm);
}
@@ -1309,7 +1336,7 @@ ascompatet(int op, NodeList *nl, Type **nr, int fp, NodeList **init)
* package all the arguments that match a ... T parameter into a []T.
*/
static NodeList*
-mkdotargslice(NodeList *lr0, NodeList *nn, Type *l, int fp, NodeList **init)
+mkdotargslice(NodeList *lr0, NodeList *nn, Type *l, int fp, NodeList **init, int esc)
{
Node *a, *n;
Type *tslice;
@@ -1318,12 +1345,18 @@ mkdotargslice(NodeList *lr0, NodeList *nn, Type *l, int fp, NodeList **init)
tslice->type = l->type->type;
tslice->bound = -1;
- n = nod(OCOMPLIT, N, typenod(tslice));
- n->list = lr0;
- typecheck(&n, Erv);
- if(n->type == T)
- fatal("mkdotargslice: typecheck failed");
- walkexpr(&n, init);
+ if(count(lr0) == 0) {
+ n = nodnil();
+ n->type = tslice;
+ } else {
+ n = nod(OCOMPLIT, N, typenod(tslice));
+ n->list = lr0;
+ n->esc = esc;
+ typecheck(&n, Erv);
+ if(n->type == T)
+ fatal("mkdotargslice: typecheck failed");
+ walkexpr(&n, init);
+ }
a = nod(OAS, nodarg(l, fp), n);
nn = list(nn, convas(a, init));
@@ -1343,7 +1376,6 @@ dumptypes(Type **nl, char *what)
fmtstrinit(&fmt);
fmtprint(&fmt, "\t");
- l = structfirst(&savel, nl);
first = 1;
for(l = structfirst(&savel, nl); l != T; l = structnext(&savel)) {
if(first)
@@ -1387,8 +1419,9 @@ dumpnodetypes(NodeList *l, char *what)
* func(expr-list)
*/
static NodeList*
-ascompatte(int op, int isddd, Type **nl, NodeList *lr, int fp, NodeList **init)
+ascompatte(int op, Node *call, int isddd, Type **nl, NodeList *lr, int fp, NodeList **init)
{
+ int esc;
Type *l, *ll;
Node *r, *a;
NodeList *nn, *lr0, *alist;
@@ -1401,7 +1434,7 @@ ascompatte(int op, int isddd, Type **nl, NodeList *lr, int fp, NodeList **init)
if(lr)
r = lr->n;
nn = nil;
-
+
// f(g()) where g has multiple return values
if(r != N && lr->next == nil && r->type->etype == TSTRUCT && r->type->funarg) {
// optimization - can do block copy
@@ -1411,13 +1444,12 @@ ascompatte(int op, int isddd, Type **nl, NodeList *lr, int fp, NodeList **init)
nn = list1(convas(nod(OAS, a, r), init));
goto ret;
}
-
+
// conversions involved.
// copy into temporaries.
alist = nil;
for(l=structfirst(&savel, &r->type); l; l=structnext(&savel)) {
- a = nod(OXXX, N, N);
- tempname(a, l->type);
+ a = temp(l->type);
alist = list(alist, a);
}
a = nod(OAS2, N, N);
@@ -1452,7 +1484,10 @@ loop:
// normal case -- make a slice of all
// remaining arguments and pass it to
// the ddd parameter.
- nn = mkdotargslice(lr, nn, l, fp, init);
+ esc = EscUnknown;
+ if(call->right)
+ esc = call->right->esc;
+ nn = mkdotargslice(lr, nn, l, fp, init, esc);
goto ret;
}
@@ -1526,6 +1561,9 @@ walkprint(Node *nn, NodeList **init, int defer)
n = l->n;
if(n->op == OLITERAL) {
switch(n->val.ctype) {
+ case CTRUNE:
+ defaultlit(&n, runetype);
+ break;
case CTINT:
defaultlit(&n, types[TINT64]);
break;
@@ -1668,7 +1706,7 @@ callnew(Type *t)
dowidth(t);
fn = syslook("new", 1);
argtype(fn, t);
- return mkcall1(fn, ptrto(t), nil, nodintconst(t->width));
+ return mkcall1(fn, ptrto(t), nil, typename(t));
}
static Node*
@@ -1700,10 +1738,10 @@ convas(Node *n, NodeList **init)
n->left->left, n->left->right, n->right);
goto out;
}
-
+
if(eqtype(lt, rt))
goto out;
-
+
n->right = assignconv(n->right, lt, "assignment");
walkexpr(&n->right, init);
@@ -1720,7 +1758,7 @@ out:
* then it is done first. otherwise must
* make temp variables
*/
-NodeList*
+static NodeList*
reorder1(NodeList *all)
{
Node *f, *a, *n;
@@ -1757,8 +1795,7 @@ reorder1(NodeList *all)
}
// make assignment of fncall to tempname
- a = nod(OXXX, N, N);
- tempname(a, n->right->type);
+ a = temp(n->right->type);
a = nod(OAS, a, n->right);
g = list(g, a);
@@ -1773,28 +1810,242 @@ reorder1(NodeList *all)
return concat(g, r);
}
+static void reorder3save(Node**, NodeList*, NodeList*, NodeList**);
+static int aliased(Node*, NodeList*, NodeList*);
+
/*
* from ascompat[ee]
* a,b = c,d
* simultaneous assignment. there cannot
* be later use of an earlier lvalue.
+ *
+ * function calls have been removed.
*/
+static NodeList*
+reorder3(NodeList *all)
+{
+ NodeList *list, *early;
+ Node *l;
+
+ // If a needed expression may be affected by an
+ // earlier assignment, make an early copy of that
+ // expression and use the copy instead.
+ early = nil;
+ for(list=all; list; list=list->next) {
+ l = list->n->left;
+
+ // Save subexpressions needed on left side.
+ // Drill through non-dereferences.
+ for(;;) {
+ if(l->op == ODOT || l->op == OPAREN) {
+ l = l->left;
+ continue;
+ }
+ if(l->op == OINDEX && isfixedarray(l->left->type)) {
+ reorder3save(&l->right, all, list, &early);
+ l = l->left;
+ continue;
+ }
+ break;
+ }
+ switch(l->op) {
+ default:
+ fatal("reorder3 unexpected lvalue %#O", l->op);
+ case ONAME:
+ break;
+ case OINDEX:
+ reorder3save(&l->left, all, list, &early);
+ reorder3save(&l->right, all, list, &early);
+ break;
+ case OIND:
+ case ODOTPTR:
+ reorder3save(&l->left, all, list, &early);
+ }
+ // Save expression on right side.
+ reorder3save(&list->n->right, all, list, &early);
+ }
+
+ return concat(early, all);
+}
+
+static int vmatch2(Node*, Node*);
+static int varexpr(Node*);
+
+/*
+ * if the evaluation of *np would be affected by the
+ * assignments in all up to but not including stop,
+ * copy into a temporary during *early and
+ * replace *np with that temp.
+ */
+static void
+reorder3save(Node **np, NodeList *all, NodeList *stop, NodeList **early)
+{
+ Node *n, *q;
+
+ n = *np;
+ if(!aliased(n, all, stop))
+ return;
+
+ q = temp(n->type);
+ q = nod(OAS, q, n);
+ typecheck(&q, Etop);
+ *early = list(*early, q);
+ *np = q->left;
+}
+
+/*
+ * what's the outer value that a write to n affects?
+ * outer value means containing struct or array.
+ */
+static Node*
+outervalue(Node *n)
+{
+ for(;;) {
+ if(n->op == ODOT || n->op == OPAREN) {
+ n = n->left;
+ continue;
+ }
+ if(n->op == OINDEX && isfixedarray(n->left->type)) {
+ n = n->left;
+ continue;
+ }
+ break;
+ }
+ return n;
+}
+
+/*
+ * Is it possible that the computation of n might be
+ * affected by writes in as up to but not including stop?
+ */
+static int
+aliased(Node *n, NodeList *all, NodeList *stop)
+{
+ int memwrite, varwrite;
+ Node *a;
+ NodeList *l;
+
+ if(n == N)
+ return 0;
+
+ // Look for obvious aliasing: a variable being assigned
+ // during the all list and appearing in n.
+ // Also record whether there are any writes to main memory.
+ // Also record whether there are any writes to variables
+ // whose addresses have been taken.
+ memwrite = 0;
+ varwrite = 0;
+ for(l=all; l!=stop; l=l->next) {
+ a = outervalue(l->n->left);
+ if(a->op != ONAME) {
+ memwrite = 1;
+ continue;
+ }
+ switch(n->class) {
+ default:
+ varwrite = 1;
+ continue;
+ case PAUTO:
+ case PPARAM:
+ case PPARAMOUT:
+ if(n->addrtaken) {
+ varwrite = 1;
+ continue;
+ }
+ if(vmatch2(a, n)) {
+ // Direct hit.
+ return 1;
+ }
+ }
+ }
+
+ // The variables being written do not appear in n.
+ // However, n might refer to computed addresses
+ // that are being written.
+
+ // If no computed addresses are affected by the writes, no aliasing.
+ if(!memwrite && !varwrite)
+ return 0;
+
+ // If n does not refer to computed addresses
+ // (that is, if n only refers to variables whose addresses
+ // have not been taken), no aliasing.
+ if(varexpr(n))
+ return 0;
+
+ // Otherwise, both the writes and n refer to computed memory addresses.
+ // Assume that they might conflict.
+ return 1;
+}
+
+/*
+ * does the evaluation of n only refer to variables
+ * whose addresses have not been taken?
+ * (and no other memory)
+ */
+static int
+varexpr(Node *n)
+{
+ if(n == N)
+ return 1;
+
+ switch(n->op) {
+ case OLITERAL:
+ return 1;
+ case ONAME:
+ switch(n->class) {
+ case PAUTO:
+ case PPARAM:
+ case PPARAMOUT:
+ if(!n->addrtaken)
+ return 1;
+ }
+ return 0;
+
+ case OADD:
+ case OSUB:
+ case OOR:
+ case OXOR:
+ case OMUL:
+ case ODIV:
+ case OMOD:
+ case OLSH:
+ case ORSH:
+ case OAND:
+ case OANDNOT:
+ case OPLUS:
+ case OMINUS:
+ case OCOM:
+ case OPAREN:
+ case OANDAND:
+ case OOROR:
+ case ODOT: // but not ODOTPTR
+ case OCONV:
+ case OCONVNOP:
+ case OCONVIFACE:
+ case ODOTTYPE:
+ return varexpr(n->left) && varexpr(n->right);
+ }
+
+ // Be conservative.
+ return 0;
+}
+
+/*
+ * is the name l mentioned in r?
+ */
static int
vmatch2(Node *l, Node *r)
{
NodeList *ll;
- /*
- * isolate all right sides
- */
if(r == N)
return 0;
switch(r->op) {
case ONAME:
// match each right given left
- if(l == r)
- return 1;
+ return l == r;
case OLITERAL:
return 0;
}
@@ -1808,6 +2059,10 @@ vmatch2(Node *l, Node *r)
return 0;
}
+/*
+ * is any name mentioned in l also mentioned in r?
+ * called by sinit.c
+ */
int
vmatch1(Node *l, Node *r)
{
@@ -1846,34 +2101,6 @@ vmatch1(Node *l, Node *r)
return 0;
}
-NodeList*
-reorder3(NodeList *all)
-{
- Node *n1, *n2, *q;
- int c1, c2;
- NodeList *l1, *l2, *r;
-
- r = nil;
- for(l1=all, c1=0; l1; l1=l1->next, c1++) {
- n1 = l1->n;
- for(l2=all, c2=0; l2; l2=l2->next, c2++) {
- n2 = l2->n;
- if(c2 > c1) {
- if(vmatch1(n1->left, n2->right)) {
- // delay assignment to n1->left
- q = nod(OXXX, N, N);
- tempname(q, n1->right->type);
- q = nod(OAS, n1->left, q);
- n1->left = q->right;
- r = list(r, q);
- break;
- }
- }
- }
- }
- return concat(all, r);
-}
-
/*
* walk through argin parameters.
* generate and return code to allocate
@@ -1940,7 +2167,7 @@ heapmoves(void)
{
NodeList *nn;
int32 lno;
-
+
lno = lineno;
lineno = curfn->lineno;
nn = paramstoheap(getthis(curfn->type), 0);
@@ -1960,7 +2187,7 @@ vmkcall(Node *fn, Type *t, NodeList **init, va_list va)
NodeList *args;
if(fn->type == T || fn->type->etype != TFUNC)
- fatal("mkcall %#N %T", fn, fn->type);
+ fatal("mkcall %N %T", fn, fn->type);
args = nil;
n = fn->type->intuple;
@@ -2002,7 +2229,7 @@ mkcall1(Node *fn, Type *t, NodeList **init, ...)
return r;
}
-static Node*
+Node*
conv(Node *n, Type *t)
{
if(eqtype(n->type, t))
@@ -2043,12 +2270,26 @@ mapfn(char *name, Type *t)
}
static Node*
+mapfndel(char *name, Type *t)
+{
+ Node *fn;
+
+ if(t->etype != TMAP)
+ fatal("mapfn %T", t);
+ fn = syslook(name, 1);
+ argtype(fn, t->down);
+ argtype(fn, t->type);
+ argtype(fn, t->down);
+ return fn;
+}
+
+static Node*
addstr(Node *n, NodeList **init)
{
Node *r, *cat, *typstr;
NodeList *in, *args;
int i, count;
-
+
count = 0;
for(r=n; r->op == OADDSTR; r=r->left)
count++; // r->right
@@ -2077,7 +2318,7 @@ addstr(Node *n, NodeList **init)
typecheck(&r, Erv);
walkexpr(&r, init);
r->type = n->type;
-
+
return r;
}
@@ -2085,7 +2326,7 @@ static Node*
appendslice(Node *n, NodeList **init)
{
Node *f;
-
+
f = syslook("appendslice", 1);
argtype(f, n->type);
argtype(f, n->type->type);
@@ -2099,7 +2340,7 @@ appendslice(Node *n, NodeList **init)
// s := src
// const argc = len(args) - 1
// if cap(s) - len(s) < argc {
-// s = growslice(s, argc)
+// s = growslice(s, argc)
// }
// n := len(s)
// s = s[:n+argc]
@@ -2117,6 +2358,12 @@ append(Node *n, NodeList **init)
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);
+
nsrc = n->list->n;
argc = count(n->list) - 1;
if (argc < 1) {
@@ -2125,17 +2372,16 @@ append(Node *n, NodeList **init)
l = nil;
- ns = nod(OXXX, N, N); // var s
- tempname(ns, nsrc->type);
+ ns = temp(nsrc->type);
l = list(l, nod(OAS, ns, nsrc)); // s = src
- na = nodintconst(argc); // const argc
- nx = nod(OIF, N, N); // if cap(s) - len(s) < argc
+ na = nodintconst(argc); // const argc
+ nx = nod(OIF, N, N); // if cap(s) - len(s) < argc
nx->ntest = nod(OLT, nod(OSUB, nod(OCAP, ns, N), nod(OLEN, ns, N)), na);
- fn = syslook("growslice", 1); // growslice(<type>, old []T, n int64) (ret []T)
- argtype(fn, ns->type->type); // 1 old []any
- argtype(fn, ns->type->type); // 2 ret []any
+ fn = syslook("growslice", 1); // growslice(<type>, old []T, n int64) (ret []T)
+ argtype(fn, ns->type->type); // 1 old []any
+ argtype(fn, ns->type->type); // 2 ret []any
nx->nbody = list1(nod(OAS, ns, mkcall1(fn, ns->type, &nx->ninit,
typename(ns->type),
@@ -2143,18 +2389,17 @@ append(Node *n, NodeList **init)
conv(na, types[TINT64]))));
l = list(l, nx);
- nn = nod(OXXX, N, N); // var n
- tempname(nn, types[TINT]);
- l = list(l, nod(OAS, nn, nod(OLEN, ns, N))); // n = len(s)
+ nn = temp(types[TINT]);
+ 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->etype = 1; // disable bounds check
- l = list(l, nod(OAS, ns, nx)); // s = s[:n+argc]
+ nx = nod(OSLICE, ns, nod(OKEY, N, nod(OADD, nn, na))); // ...s[:n+argc]
+ nx->etype = 1; // disable bounds check
+ l = list(l, nod(OAS, ns, nx)); // s = s[:n+argc]
- for (a = n->list->next; a != nil; a = a->next) {
- nx = nod(OINDEX, ns, nn); // s[n] ...
- nx->etype = 1; // disable bounds check
- l = list(l, nod(OAS, nx, a->n)); // s[n] = arg
+ for (a = n->list->next; a != nil; a = a->next) {
+ nx = nod(OINDEX, ns, nn); // s[n] ...
+ nx->etype = 1; // disable bounds check
+ l = list(l, nod(OAS, nx, a->n)); // s[n] = arg
if (a->next != nil)
l = list(l, nod(OAS, nn, nod(OADD, nn, nodintconst(1)))); // n = n + 1
}
@@ -2164,3 +2409,188 @@ append(Node *n, NodeList **init)
*init = concat(*init, l);
return ns;
}
+
+static Node*
+eqfor(Type *t)
+{
+ int a;
+ Node *n;
+ Node *ntype;
+ Sym *sym;
+
+ // Should only arrive here with large memory or
+ // a struct/array containing a non-memory field/element.
+ // Small memory is handled inline, and single non-memory
+ // is handled during type check (OCMPSTR etc).
+ a = algtype1(t, nil);
+ if(a != AMEM && a != -1)
+ fatal("eqfor %T", t);
+
+ if(a == AMEM) {
+ n = syslook("memequal", 1);
+ argtype(n, t);
+ argtype(n, t);
+ return n;
+ }
+
+ sym = typesymprefix(".eq", t);
+ n = newname(sym);
+ n->class = PFUNC;
+ ntype = nod(OTFUNC, N, N);
+ ntype->list = list(ntype->list, nod(ODCLFIELD, N, typenod(ptrto(types[TBOOL]))));
+ ntype->list = list(ntype->list, nod(ODCLFIELD, N, typenod(types[TUINTPTR])));
+ ntype->list = list(ntype->list, nod(ODCLFIELD, N, typenod(ptrto(t))));
+ ntype->list = list(ntype->list, nod(ODCLFIELD, N, typenod(ptrto(t))));
+ typecheck(&ntype, Etype);
+ n->type = ntype->type;
+ return n;
+}
+
+static int
+countfield(Type *t)
+{
+ Type *t1;
+ int n;
+
+ n = 0;
+ for(t1=t->type; t1!=T; t1=t1->down)
+ n++;
+ return n;
+}
+
+static void
+walkcompare(Node **np, NodeList **init)
+{
+ Node *n, *l, *r, *fn, *call, *a, *li, *ri, *expr;
+ int andor, i;
+ Type *t, *t1;
+ static Node *tempbool;
+
+ n = *np;
+
+ // Must be comparison of array or struct.
+ // Otherwise back end handles it.
+ t = n->left->type;
+ switch(t->etype) {
+ default:
+ return;
+ case TARRAY:
+ if(isslice(t))
+ return;
+ break;
+ case TSTRUCT:
+ break;
+ }
+
+ if(!islvalue(n->left) || !islvalue(n->right))
+ goto hard;
+
+ l = temp(ptrto(t));
+ a = nod(OAS, l, nod(OADDR, n->left, N));
+ a->right->etype = 1; // addr does not escape
+ typecheck(&a, Etop);
+ *init = list(*init, a);
+
+ r = temp(ptrto(t));
+ a = nod(OAS, r, nod(OADDR, n->right, N));
+ a->right->etype = 1; // addr does not escape
+ typecheck(&a, Etop);
+ *init = list(*init, a);
+
+ expr = N;
+ andor = OANDAND;
+ if(n->op == ONE)
+ andor = OOROR;
+
+ if(t->etype == TARRAY &&
+ t->bound <= 4 &&
+ issimple[t->type->etype]) {
+ // Four or fewer elements of a basic type.
+ // Unroll comparisons.
+ for(i=0; i<t->bound; i++) {
+ li = nod(OINDEX, l, nodintconst(i));
+ ri = nod(OINDEX, r, nodintconst(i));
+ a = nod(n->op, li, ri);
+ if(expr == N)
+ expr = a;
+ else
+ expr = nod(andor, expr, a);
+ }
+ if(expr == N)
+ expr = nodbool(n->op == OEQ);
+ typecheck(&expr, Erv);
+ walkexpr(&expr, init);
+ expr->type = n->type;
+ *np = expr;
+ return;
+ }
+
+ if(t->etype == TSTRUCT && countfield(t) <= 4) {
+ // Struct of four or fewer fields.
+ // Inline comparisons.
+ for(t1=t->type; t1; t1=t1->down) {
+ li = nod(OXDOT, l, newname(t1->sym));
+ ri = nod(OXDOT, r, newname(t1->sym));
+ a = nod(n->op, li, ri);
+ if(expr == N)
+ expr = a;
+ else
+ expr = nod(andor, expr, a);
+ }
+ if(expr == N)
+ expr = nodbool(n->op == OEQ);
+ typecheck(&expr, Erv);
+ walkexpr(&expr, init);
+ expr->type = n->type;
+ *np = expr;
+ return;
+ }
+
+ // Chose not to inline, but still have addresses.
+ // Call equality function directly.
+ // The equality function requires a bool pointer for
+ // storing its address, because it has to be callable
+ // from C, and C can't access an ordinary Go return value.
+ // To avoid creating many temporaries, cache one per function.
+ if(tempbool == N || tempbool->curfn != curfn)
+ tempbool = temp(types[TBOOL]);
+
+ call = nod(OCALL, eqfor(t), N);
+ a = nod(OADDR, tempbool, N);
+ a->etype = 1; // does not escape
+ call->list = list(call->list, a);
+ call->list = list(call->list, nodintconst(t->width));
+ call->list = list(call->list, l);
+ call->list = list(call->list, r);
+ typecheck(&call, Etop);
+ walkstmt(&call);
+ *init = list(*init, call);
+
+ if(n->op == OEQ)
+ r = tempbool;
+ else
+ r = nod(ONOT, tempbool, N);
+ typecheck(&r, Erv);
+ walkexpr(&r, init);
+ *np = r;
+ return;
+
+hard:
+ // Cannot take address of one or both of the operands.
+ // Instead, pass directly to runtime helper function.
+ // Easier on the stack than passing the address
+ // of temporary variables, because we are better at reusing
+ // the argument space than temporary variable space.
+ fn = syslook("equal", 1);
+ l = n->left;
+ r = n->right;
+ argtype(fn, n->left->type);
+ argtype(fn, n->left->type);
+ r = mkcall1(fn, n->type, init, typename(n->left->type), l, r);
+ if(n->op == ONE) {
+ r = nod(ONOT, r, N);
+ typecheck(&r, Erv);
+ }
+ *np = r;
+ return;
+}
diff --git a/src/cmd/gc/y.tab.c b/src/cmd/gc/y.tab.c
new file mode 100644
index 000000000..97bf233eb
--- /dev/null
+++ b/src/cmd/gc/y.tab.c
@@ -0,0 +1,5540 @@
+/* A Bison parser, made by GNU Bison 2.5. */
+
+/* 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 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, 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
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ 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. */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+ simplifying the original so-called "semantic" parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Bison version. */
+#define YYBISON_VERSION "2.5"
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* 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
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ LLITERAL = 258,
+ LASOP = 259,
+ LBREAK = 260,
+ LCASE = 261,
+ LCHAN = 262,
+ LCOLAS = 263,
+ LCONST = 264,
+ LCONTINUE = 265,
+ LDDD = 266,
+ LDEFAULT = 267,
+ LDEFER = 268,
+ LELSE = 269,
+ LFALL = 270,
+ LFOR = 271,
+ LFUNC = 272,
+ LGO = 273,
+ LGOTO = 274,
+ LIF = 275,
+ LIMPORT = 276,
+ LINTERFACE = 277,
+ LMAP = 278,
+ LNAME = 279,
+ LPACKAGE = 280,
+ LRANGE = 281,
+ LRETURN = 282,
+ LSELECT = 283,
+ LSTRUCT = 284,
+ LSWITCH = 285,
+ LTYPE = 286,
+ LVAR = 287,
+ LANDAND = 288,
+ LANDNOT = 289,
+ LBODY = 290,
+ LCOMM = 291,
+ LDEC = 292,
+ LEQ = 293,
+ LGE = 294,
+ LGT = 295,
+ LIGNORE = 296,
+ LINC = 297,
+ LLE = 298,
+ LLSH = 299,
+ LLT = 300,
+ LNE = 301,
+ LOROR = 302,
+ LRSH = 303,
+ NotPackage = 304,
+ NotParen = 305,
+ PreferToRightParen = 306
+ };
+#endif
+/* Tokens. */
+#define LLITERAL 258
+#define LASOP 259
+#define LBREAK 260
+#define LCASE 261
+#define LCHAN 262
+#define LCOLAS 263
+#define LCONST 264
+#define LCONTINUE 265
+#define LDDD 266
+#define LDEFAULT 267
+#define LDEFER 268
+#define LELSE 269
+#define LFALL 270
+#define LFOR 271
+#define LFUNC 272
+#define LGO 273
+#define LGOTO 274
+#define LIF 275
+#define LIMPORT 276
+#define LINTERFACE 277
+#define LMAP 278
+#define LNAME 279
+#define LPACKAGE 280
+#define LRANGE 281
+#define LRETURN 282
+#define LSELECT 283
+#define LSTRUCT 284
+#define LSWITCH 285
+#define LTYPE 286
+#define LVAR 287
+#define LANDAND 288
+#define LANDNOT 289
+#define LBODY 290
+#define LCOMM 291
+#define LDEC 292
+#define LEQ 293
+#define LGE 294
+#define LGT 295
+#define LIGNORE 296
+#define LINC 297
+#define LLE 298
+#define LLSH 299
+#define LLT 300
+#define LNE 301
+#define LOROR 302
+#define LRSH 303
+#define NotPackage 304
+#define NotParen 305
+#define PreferToRightParen 306
+
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+{
+
+/* Line 293 of yacc.c */
+#line 28 "go.y"
+
+ Node* node;
+ NodeList* list;
+ Type* type;
+ Sym* sym;
+ struct Val val;
+ int i;
+
+
+
+/* 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
+#endif
+
+
+/* Copy the second part of user declarations. */
+
+
+/* Line 343 of yacc.c */
+#line 242 "y.tab.c"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+# define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+# define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# else
+# define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+# if ENABLE_NLS
+# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+# define YY_(msgid) dgettext ("bison-runtime", msgid)
+# endif
+# endif
+# ifndef YY_
+# define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E. */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions. */
+#ifndef lint
+# define YYID(n) (n)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int yyi)
+#else
+static int
+YYID (yyi)
+ int yyi;
+#endif
+{
+ return yyi;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# elif defined __BUILTIN_VA_ARG_INCR
+# include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+# elif defined _AIX
+# define YYSTACK_ALLOC __alloca
+# elif defined _MSC_VER
+# include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+# define alloca _alloca
+# else
+# define YYSTACK_ALLOC alloca
+# 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 EXIT_SUCCESS
+# define EXIT_SUCCESS 0
+# endif
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's `empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+# ifndef YYSTACK_ALLOC_MAXIMUM
+ /* The OS might guarantee only one guard page at the bottom of the stack,
+ and a page size can be as small as 4096 bytes. So we cannot safely
+ invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
+ to allow for a few compiler-allocated temporary stack slots. */
+# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+# endif
+# else
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# ifndef YYSTACK_ALLOC_MAXIMUM
+# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+# endif
+# if (defined __cplusplus && ! defined EXIT_SUCCESS \
+ && ! ((defined YYMALLOC || defined malloc) \
+ && (defined YYFREE || defined free)))
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
+# endif
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# 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 EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+ && (! defined __cplusplus \
+ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ 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)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((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
+# 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 4
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 2131
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 76
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 138
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 344
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 653
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 306
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
+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, 69, 2, 2, 64, 55, 56, 2,
+ 59, 60, 53, 49, 75, 50, 63, 54, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 66, 62,
+ 2, 65, 2, 73, 74, 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, 71, 2, 72, 52, 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, 67, 51, 68, 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,
+ 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, 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,
+ 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, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 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, 44,
+ 45, 46, 47, 48, 57, 58, 61
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+ YYRHS. */
+static const yytype_uint16 yyprhs[] =
+{
+ 0, 0, 3, 8, 9, 13, 14, 18, 19, 23,
+ 26, 32, 36, 40, 43, 45, 49, 51, 54, 57,
+ 62, 63, 65, 66, 71, 72, 74, 76, 78, 80,
+ 83, 89, 93, 96, 102, 110, 114, 117, 123, 127,
+ 129, 132, 137, 141, 146, 150, 152, 155, 157, 159,
+ 162, 164, 168, 172, 176, 179, 182, 186, 192, 198,
+ 201, 202, 207, 208, 212, 213, 216, 217, 222, 227,
+ 232, 238, 240, 242, 245, 246, 250, 252, 256, 257,
+ 258, 259, 267, 268, 271, 274, 275, 276, 284, 285,
+ 291, 293, 297, 301, 305, 309, 313, 317, 321, 325,
+ 329, 333, 337, 341, 345, 349, 353, 357, 361, 365,
+ 369, 373, 375, 378, 381, 384, 387, 390, 393, 396,
+ 399, 403, 409, 416, 418, 420, 424, 430, 436, 441,
+ 448, 450, 455, 461, 467, 475, 477, 478, 482, 484,
+ 489, 491, 495, 497, 499, 501, 503, 505, 507, 509,
+ 510, 512, 514, 516, 518, 523, 525, 527, 529, 532,
+ 534, 536, 538, 540, 542, 546, 548, 550, 552, 555,
+ 557, 559, 561, 563, 567, 569, 571, 573, 575, 577,
+ 579, 581, 583, 585, 589, 594, 599, 602, 606, 612,
+ 614, 616, 619, 623, 629, 633, 639, 643, 647, 653,
+ 662, 668, 677, 683, 684, 688, 689, 691, 695, 697,
+ 702, 705, 706, 710, 712, 716, 718, 722, 724, 728,
+ 730, 734, 736, 740, 744, 747, 752, 756, 762, 768,
+ 770, 774, 776, 779, 781, 785, 790, 792, 795, 798,
+ 800, 802, 806, 807, 810, 811, 813, 815, 817, 819,
+ 821, 823, 825, 827, 829, 830, 835, 837, 840, 843,
+ 846, 849, 852, 855, 857, 861, 863, 867, 869, 873,
+ 875, 879, 881, 885, 887, 889, 893, 897, 898, 901,
+ 902, 904, 905, 907, 908, 910, 911, 913, 914, 916,
+ 917, 919, 920, 922, 923, 925, 926, 928, 933, 938,
+ 944, 951, 956, 961, 963, 965, 967, 969, 971, 973,
+ 975, 977, 979, 983, 988, 994, 999, 1004, 1007, 1010,
+ 1015, 1019, 1023, 1029, 1033, 1038, 1042, 1048, 1050, 1051,
+ 1053, 1057, 1059, 1061, 1064, 1066, 1068, 1074, 1075, 1078,
+ 1080, 1084, 1086, 1090, 1092
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yytype_int16 yyrhs[] =
+{
+ 77, 0, -1, 79, 78, 81, 162, -1, -1, 25,
+ 137, 62, -1, -1, 80, 86, 88, -1, -1, 81,
+ 82, 62, -1, 21, 83, -1, 21, 59, 84, 186,
+ 60, -1, 21, 59, 60, -1, 85, 86, 88, -1,
+ 85, 88, -1, 83, -1, 84, 62, 83, -1, 3,
+ -1, 137, 3, -1, 63, 3, -1, 25, 24, 87,
+ 62, -1, -1, 24, -1, -1, 89, 210, 64, 64,
+ -1, -1, 91, -1, 154, -1, 177, -1, 1, -1,
+ 32, 93, -1, 32, 59, 163, 186, 60, -1, 32,
+ 59, 60, -1, 92, 94, -1, 92, 59, 94, 186,
+ 60, -1, 92, 59, 94, 62, 164, 186, 60, -1,
+ 92, 59, 60, -1, 31, 97, -1, 31, 59, 165,
+ 186, 60, -1, 31, 59, 60, -1, 9, -1, 181,
+ 142, -1, 181, 142, 65, 182, -1, 181, 65, 182,
+ -1, 181, 142, 65, 182, -1, 181, 65, 182, -1,
+ 94, -1, 181, 142, -1, 181, -1, 137, -1, 96,
+ 142, -1, 123, -1, 123, 4, 123, -1, 182, 65,
+ 182, -1, 182, 8, 182, -1, 123, 42, -1, 123,
+ 37, -1, 6, 183, 66, -1, 6, 183, 65, 123,
+ 66, -1, 6, 183, 8, 123, 66, -1, 12, 66,
+ -1, -1, 67, 101, 179, 68, -1, -1, 99, 103,
+ 179, -1, -1, 104, 102, -1, -1, 35, 106, 179,
+ 68, -1, 182, 65, 26, 123, -1, 182, 8, 26,
+ 123, -1, 190, 62, 190, 62, 190, -1, 190, -1,
+ 107, -1, 108, 105, -1, -1, 16, 111, 109, -1,
+ 190, -1, 190, 62, 190, -1, -1, -1, -1, 20,
+ 114, 112, 115, 105, 116, 117, -1, -1, 14, 113,
+ -1, 14, 100, -1, -1, -1, 30, 119, 112, 120,
+ 35, 104, 68, -1, -1, 28, 122, 35, 104, 68,
+ -1, 124, -1, 123, 47, 123, -1, 123, 33, 123,
+ -1, 123, 38, 123, -1, 123, 46, 123, -1, 123,
+ 45, 123, -1, 123, 43, 123, -1, 123, 39, 123,
+ -1, 123, 40, 123, -1, 123, 49, 123, -1, 123,
+ 50, 123, -1, 123, 51, 123, -1, 123, 52, 123,
+ -1, 123, 53, 123, -1, 123, 54, 123, -1, 123,
+ 55, 123, -1, 123, 56, 123, -1, 123, 34, 123,
+ -1, 123, 44, 123, -1, 123, 48, 123, -1, 123,
+ 36, 123, -1, 130, -1, 53, 124, -1, 56, 124,
+ -1, 49, 124, -1, 50, 124, -1, 69, 124, -1,
+ 70, 124, -1, 52, 124, -1, 36, 124, -1, 130,
+ 59, 60, -1, 130, 59, 183, 187, 60, -1, 130,
+ 59, 183, 11, 187, 60, -1, 3, -1, 139, -1,
+ 130, 63, 137, -1, 130, 63, 59, 131, 60, -1,
+ 130, 63, 59, 31, 60, -1, 130, 71, 123, 72,
+ -1, 130, 71, 188, 66, 188, 72, -1, 125, -1,
+ 145, 59, 123, 60, -1, 146, 133, 127, 185, 68,
+ -1, 126, 67, 127, 185, 68, -1, 59, 131, 60,
+ 67, 127, 185, 68, -1, 161, -1, -1, 123, 66,
+ 129, -1, 123, -1, 67, 127, 185, 68, -1, 126,
+ -1, 59, 131, 60, -1, 123, -1, 143, -1, 142,
+ -1, 35, -1, 67, -1, 137, -1, 137, -1, -1,
+ 134, -1, 24, -1, 138, -1, 73, -1, 74, 3,
+ 63, 24, -1, 137, -1, 134, -1, 11, -1, 11,
+ 142, -1, 151, -1, 157, -1, 149, -1, 150, -1,
+ 148, -1, 59, 142, 60, -1, 151, -1, 157, -1,
+ 149, -1, 53, 143, -1, 157, -1, 149, -1, 150,
+ -1, 148, -1, 59, 142, 60, -1, 157, -1, 149,
+ -1, 149, -1, 151, -1, 157, -1, 149, -1, 150,
+ -1, 148, -1, 139, -1, 139, 63, 137, -1, 71,
+ 188, 72, 142, -1, 71, 11, 72, 142, -1, 7,
+ 144, -1, 7, 36, 142, -1, 23, 71, 142, 72,
+ 142, -1, 152, -1, 153, -1, 53, 142, -1, 36,
+ 7, 142, -1, 29, 133, 166, 186, 68, -1, 29,
+ 133, 68, -1, 22, 133, 167, 186, 68, -1, 22,
+ 133, 68, -1, 17, 155, 158, -1, 137, 59, 175,
+ 60, 159, -1, 59, 175, 60, 137, 59, 175, 60,
+ 159, -1, 196, 59, 191, 60, 206, -1, 59, 211,
+ 60, 137, 59, 191, 60, 206, -1, 17, 59, 175,
+ 60, 159, -1, -1, 67, 179, 68, -1, -1, 147,
+ -1, 59, 175, 60, -1, 157, -1, 160, 133, 179,
+ 68, -1, 160, 1, -1, -1, 162, 90, 62, -1,
+ 93, -1, 163, 62, 93, -1, 95, -1, 164, 62,
+ 95, -1, 97, -1, 165, 62, 97, -1, 168, -1,
+ 166, 62, 168, -1, 171, -1, 167, 62, 171, -1,
+ 180, 142, 194, -1, 170, 194, -1, 59, 170, 60,
+ 194, -1, 53, 170, 194, -1, 59, 53, 170, 60,
+ 194, -1, 53, 59, 170, 60, 194, -1, 24, -1,
+ 24, 63, 137, -1, 169, -1, 134, 172, -1, 169,
+ -1, 59, 169, 60, -1, 59, 175, 60, 159, -1,
+ 132, -1, 137, 132, -1, 137, 141, -1, 141, -1,
+ 173, -1, 174, 75, 173, -1, -1, 174, 187, -1,
+ -1, 100, -1, 91, -1, 177, -1, 1, -1, 98,
+ -1, 110, -1, 118, -1, 121, -1, 113, -1, -1,
+ 140, 66, 178, 176, -1, 15, -1, 5, 136, -1,
+ 10, 136, -1, 18, 125, -1, 13, 125, -1, 19,
+ 134, -1, 27, 189, -1, 176, -1, 179, 62, 176,
+ -1, 134, -1, 180, 75, 134, -1, 135, -1, 181,
+ 75, 135, -1, 123, -1, 182, 75, 123, -1, 131,
+ -1, 183, 75, 131, -1, 128, -1, 129, -1, 184,
+ 75, 128, -1, 184, 75, 129, -1, -1, 184, 187,
+ -1, -1, 62, -1, -1, 75, -1, -1, 123, -1,
+ -1, 182, -1, -1, 98, -1, -1, 211, -1, -1,
+ 212, -1, -1, 213, -1, -1, 3, -1, 21, 24,
+ 3, 62, -1, 32, 196, 198, 62, -1, 9, 196,
+ 65, 209, 62, -1, 9, 196, 198, 65, 209, 62,
+ -1, 31, 197, 198, 62, -1, 17, 156, 158, 62,
+ -1, 138, -1, 196, -1, 200, -1, 201, -1, 202,
+ -1, 200, -1, 202, -1, 138, -1, 24, -1, 71,
+ 72, 198, -1, 71, 3, 72, 198, -1, 23, 71,
+ 198, 72, 198, -1, 29, 67, 192, 68, -1, 22,
+ 67, 193, 68, -1, 53, 198, -1, 7, 199, -1,
+ 7, 59, 201, 60, -1, 7, 36, 198, -1, 36,
+ 7, 198, -1, 17, 59, 191, 60, 206, -1, 137,
+ 198, 194, -1, 137, 11, 198, 194, -1, 137, 198,
+ 194, -1, 137, 59, 191, 60, 206, -1, 198, -1,
+ -1, 207, -1, 59, 191, 60, -1, 198, -1, 3,
+ -1, 50, 3, -1, 137, -1, 208, -1, 59, 208,
+ 49, 208, 60, -1, -1, 210, 195, -1, 203, -1,
+ 211, 75, 203, -1, 204, -1, 212, 62, 204, -1,
+ 205, -1, 213, 62, 205, -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,
+ 259, 260, 267, 267, 280, 284, 285, 289, 294, 300,
+ 304, 308, 312, 318, 324, 330, 335, 339, 343, 349,
+ 355, 359, 363, 369, 373, 379, 380, 384, 390, 399,
+ 405, 409, 414, 426, 442, 447, 454, 474, 492, 501,
+ 520, 519, 531, 530, 561, 564, 571, 570, 581, 587,
+ 596, 607, 613, 616, 624, 623, 634, 640, 652, 656,
+ 661, 651, 673, 676, 680, 687, 691, 686, 709, 708,
+ 724, 725, 729, 733, 737, 741, 745, 749, 753, 757,
+ 761, 765, 769, 773, 777, 781, 785, 789, 793, 797,
+ 802, 808, 809, 813, 824, 828, 832, 836, 841, 845,
+ 855, 859, 864, 872, 876, 877, 888, 892, 896, 900,
+ 904, 905, 911, 918, 924, 931, 934, 941, 947, 948,
+ 955, 956, 974, 975, 978, 981, 985, 996, 1005, 1011,
+ 1014, 1017, 1024, 1025, 1031, 1040, 1048, 1060, 1065, 1071,
+ 1072, 1073, 1074, 1075, 1076, 1082, 1083, 1084, 1085, 1091,
+ 1092, 1093, 1094, 1095, 1101, 1102, 1105, 1108, 1109, 1110,
+ 1111, 1112, 1115, 1116, 1129, 1133, 1138, 1143, 1148, 1152,
+ 1153, 1156, 1162, 1169, 1175, 1182, 1188, 1199, 1210, 1239,
+ 1278, 1301, 1318, 1327, 1330, 1338, 1342, 1346, 1353, 1359,
+ 1364, 1376, 1379, 1387, 1388, 1394, 1395, 1401, 1405, 1411,
+ 1412, 1418, 1422, 1428, 1451, 1456, 1462, 1468, 1475, 1484,
+ 1493, 1508, 1514, 1519, 1523, 1530, 1543, 1544, 1550, 1556,
+ 1559, 1563, 1569, 1572, 1581, 1584, 1585, 1589, 1590, 1596,
+ 1597, 1598, 1599, 1600, 1602, 1601, 1616, 1621, 1625, 1629,
+ 1633, 1637, 1642, 1661, 1667, 1675, 1679, 1685, 1689, 1695,
+ 1699, 1705, 1709, 1718, 1722, 1726, 1730, 1736, 1739, 1747,
+ 1748, 1750, 1751, 1754, 1757, 1760, 1763, 1766, 1769, 1772,
+ 1775, 1778, 1781, 1784, 1787, 1790, 1793, 1799, 1803, 1807,
+ 1811, 1815, 1819, 1837, 1844, 1855, 1856, 1857, 1860, 1861,
+ 1864, 1868, 1878, 1882, 1886, 1890, 1894, 1898, 1902, 1908,
+ 1914, 1922, 1930, 1936, 1943, 1959, 1977, 1981, 1987, 1990,
+ 1993, 1997, 2007, 2011, 2026, 2034, 2035, 2045, 2046, 2049,
+ 2053, 2059, 2063, 2069, 2073
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+const char *yytname[] =
+{
+ "$end", "error", "$undefined", "LLITERAL", "LASOP", "LBREAK", "LCASE",
+ "LCHAN", "LCOLAS", "LCONST", "LCONTINUE", "LDDD", "LDEFAULT", "LDEFER",
+ "LELSE", "LFALL", "LFOR", "LFUNC", "LGO", "LGOTO", "LIF", "LIMPORT",
+ "LINTERFACE", "LMAP", "LNAME", "LPACKAGE", "LRANGE", "LRETURN",
+ "LSELECT", "LSTRUCT", "LSWITCH", "LTYPE", "LVAR", "LANDAND", "LANDNOT",
+ "LBODY", "LCOMM", "LDEC", "LEQ", "LGE", "LGT", "LIGNORE", "LINC", "LLE",
+ "LLSH", "LLT", "LNE", "LOROR", "LRSH", "'+'", "'-'", "'|'", "'^'", "'*'",
+ "'/'", "'%'", "'&'", "NotPackage", "NotParen", "'('", "')'",
+ "PreferToRightParen", "';'", "'.'", "'$'", "'='", "':'", "'{'", "'}'",
+ "'!'", "'~'", "'['", "']'", "'?'", "'@'", "','", "$accept", "file",
+ "package", "loadsys", "$@1", "imports", "import", "import_stmt",
+ "import_stmt_list", "import_here", "import_package", "import_safety",
+ "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", "else",
+ "switch_stmt", "$@10", "$@11", "select_stmt", "$@12", "expr", "uexpr",
+ "pseudocall", "pexpr_no_paren", "start_complit", "keyval", "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", "fnret_type", "dotname", "othertype", "ptrtype",
+ "recvchantype", "structtype", "interfacetype", "xfndcl", "fndcl",
+ "hidden_fndcl", "fntype", "fnbody", "fnres", "fnlitdcl", "fnliteral",
+ "xdcl_list", "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", "$@13", "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", "ohidden_interfacedcl_list", "oliteral",
+ "hidden_import", "hidden_pkg_importsym", "hidden_pkgtype", "hidden_type",
+ "hidden_type_non_recv_chan", "hidden_type_misc", "hidden_type_recv_chan",
+ "hidden_type_func", "hidden_funarg", "hidden_structdcl",
+ "hidden_interfacedcl", "ohidden_funres", "hidden_funres",
+ "hidden_literal", "hidden_constant", "hidden_import_list",
+ "hidden_funarg_list", "hidden_structdcl_list",
+ "hidden_interfacedcl_list", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+ token YYLEX-NUM. */
+static const yytype_uint16 yytoknum[] =
+{
+ 0, 256, 257, 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, 291, 292, 293, 294,
+ 295, 296, 297, 298, 299, 300, 301, 302, 303, 43,
+ 45, 124, 94, 42, 47, 37, 38, 304, 305, 40,
+ 41, 306, 59, 46, 36, 61, 58, 123, 125, 33,
+ 126, 91, 93, 63, 64, 44
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const yytype_uint8 yyr1[] =
+{
+ 0, 76, 77, 78, 78, 80, 79, 81, 81, 82,
+ 82, 82, 83, 83, 84, 84, 85, 85, 85, 86,
+ 87, 87, 89, 88, 90, 90, 90, 90, 90, 91,
+ 91, 91, 91, 91, 91, 91, 91, 91, 91, 92,
+ 93, 93, 93, 94, 94, 95, 95, 95, 96, 97,
+ 98, 98, 98, 98, 98, 98, 99, 99, 99, 99,
+ 101, 100, 103, 102, 104, 104, 106, 105, 107, 107,
+ 108, 108, 108, 109, 111, 110, 112, 112, 114, 115,
+ 116, 113, 117, 117, 117, 119, 120, 118, 122, 121,
+ 123, 123, 123, 123, 123, 123, 123, 123, 123, 123,
+ 123, 123, 123, 123, 123, 123, 123, 123, 123, 123,
+ 123, 124, 124, 124, 124, 124, 124, 124, 124, 124,
+ 125, 125, 125, 126, 126, 126, 126, 126, 126, 126,
+ 126, 126, 126, 126, 126, 126, 127, 128, 129, 129,
+ 130, 130, 131, 131, 132, 133, 133, 134, 135, 136,
+ 136, 137, 137, 137, 138, 139, 140, 141, 141, 142,
+ 142, 142, 142, 142, 142, 143, 143, 143, 143, 144,
+ 144, 144, 144, 144, 145, 145, 146, 147, 147, 147,
+ 147, 147, 148, 148, 149, 149, 149, 149, 149, 149,
+ 149, 150, 151, 152, 152, 153, 153, 154, 155, 155,
+ 156, 156, 157, 158, 158, 159, 159, 159, 160, 161,
+ 161, 162, 162, 163, 163, 164, 164, 165, 165, 166,
+ 166, 167, 167, 168, 168, 168, 168, 168, 168, 169,
+ 169, 170, 171, 171, 171, 172, 173, 173, 173, 173,
+ 174, 174, 175, 175, 176, 176, 176, 176, 176, 177,
+ 177, 177, 177, 177, 178, 177, 177, 177, 177, 177,
+ 177, 177, 177, 179, 179, 180, 180, 181, 181, 182,
+ 182, 183, 183, 184, 184, 184, 184, 185, 185, 186,
+ 186, 187, 187, 188, 188, 189, 189, 190, 190, 191,
+ 191, 192, 192, 193, 193, 194, 194, 195, 195, 195,
+ 195, 195, 195, 196, 197, 198, 198, 198, 199, 199,
+ 200, 200, 200, 200, 200, 200, 200, 200, 200, 200,
+ 200, 201, 202, 203, 203, 204, 205, 205, 206, 206,
+ 207, 207, 208, 208, 208, 209, 209, 210, 210, 211,
+ 211, 212, 212, 213, 213
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+static const yytype_uint8 yyr2[] =
+{
+ 0, 2, 4, 0, 3, 0, 3, 0, 3, 2,
+ 5, 3, 3, 2, 1, 3, 1, 2, 2, 4,
+ 0, 1, 0, 4, 0, 1, 1, 1, 1, 2,
+ 5, 3, 2, 5, 7, 3, 2, 5, 3, 1,
+ 2, 4, 3, 4, 3, 1, 2, 1, 1, 2,
+ 1, 3, 3, 3, 2, 2, 3, 5, 5, 2,
+ 0, 4, 0, 3, 0, 2, 0, 4, 4, 4,
+ 5, 1, 1, 2, 0, 3, 1, 3, 0, 0,
+ 0, 7, 0, 2, 2, 0, 0, 7, 0, 5,
+ 1, 3, 3, 3, 3, 3, 3, 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, 4, 5, 5, 7, 1, 0, 3, 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, 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
+};
+
+/* 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, 151,
+ 153, 0, 0, 152, 211, 20, 6, 337, 0, 4,
+ 0, 0, 0, 21, 0, 0, 0, 16, 0, 0,
+ 9, 22, 0, 8, 28, 123, 149, 0, 39, 149,
+ 0, 256, 74, 0, 0, 0, 78, 0, 0, 285,
+ 88, 0, 85, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 283, 0, 25, 0, 249, 250,
+ 253, 251, 252, 50, 90, 130, 140, 111, 156, 155,
+ 124, 0, 0, 0, 176, 189, 190, 26, 208, 0,
+ 135, 27, 0, 19, 0, 0, 0, 0, 0, 0,
+ 338, 154, 11, 14, 279, 18, 22, 13, 17, 150,
+ 257, 147, 0, 0, 0, 0, 155, 182, 186, 172,
+ 170, 171, 169, 258, 130, 0, 287, 242, 0, 203,
+ 130, 261, 287, 145, 146, 0, 0, 269, 286, 262,
+ 0, 0, 287, 0, 0, 36, 48, 0, 29, 267,
+ 148, 0, 119, 114, 115, 118, 112, 113, 0, 0,
+ 142, 0, 143, 167, 165, 166, 116, 117, 0, 284,
+ 0, 212, 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, 136, 0,
+ 0, 283, 254, 0, 136, 210, 0, 0, 0, 0,
+ 303, 0, 0, 203, 0, 0, 304, 0, 0, 23,
+ 280, 0, 12, 242, 0, 0, 187, 163, 161, 162,
+ 159, 160, 191, 0, 0, 288, 72, 0, 75, 0,
+ 71, 157, 236, 155, 239, 144, 240, 281, 0, 242,
+ 0, 197, 79, 76, 151, 0, 196, 0, 279, 233,
+ 221, 0, 64, 0, 0, 194, 265, 279, 219, 231,
+ 295, 0, 86, 38, 217, 279, 49, 31, 213, 279,
+ 0, 0, 40, 0, 168, 141, 0, 0, 35, 279,
+ 0, 0, 51, 92, 107, 110, 93, 97, 98, 96,
+ 108, 95, 94, 91, 109, 99, 100, 101, 102, 103,
+ 104, 105, 106, 277, 120, 271, 281, 0, 125, 284,
+ 0, 0, 0, 277, 248, 60, 246, 245, 263, 247,
+ 0, 53, 52, 270, 0, 0, 0, 0, 311, 0,
+ 0, 0, 0, 0, 310, 0, 305, 306, 307, 0,
+ 339, 0, 0, 289, 0, 0, 0, 15, 10, 0,
+ 0, 0, 173, 183, 66, 73, 0, 0, 287, 158,
+ 237, 238, 282, 243, 205, 0, 0, 0, 287, 0,
+ 229, 0, 242, 232, 280, 0, 0, 0, 0, 295,
+ 0, 0, 280, 0, 296, 224, 0, 295, 0, 280,
+ 0, 280, 0, 42, 268, 0, 0, 0, 192, 163,
+ 161, 162, 160, 136, 185, 184, 280, 0, 44, 0,
+ 136, 138, 273, 274, 281, 0, 281, 282, 0, 0,
+ 0, 128, 283, 255, 131, 0, 0, 0, 209, 0,
+ 0, 318, 308, 309, 289, 293, 0, 291, 0, 317,
+ 332, 0, 0, 334, 335, 0, 0, 0, 0, 0,
+ 295, 0, 0, 302, 0, 290, 297, 301, 298, 205,
+ 164, 0, 0, 0, 0, 241, 242, 155, 206, 181,
+ 179, 180, 177, 178, 202, 205, 204, 80, 77, 230,
+ 234, 0, 222, 195, 188, 0, 0, 89, 62, 65,
+ 0, 226, 0, 295, 220, 193, 266, 223, 64, 218,
+ 37, 214, 30, 41, 0, 277, 45, 215, 279, 47,
+ 33, 43, 277, 0, 282, 278, 133, 282, 0, 272,
+ 121, 127, 126, 0, 132, 0, 264, 320, 0, 0,
+ 311, 0, 310, 0, 327, 343, 294, 0, 0, 0,
+ 341, 292, 321, 333, 0, 299, 0, 312, 0, 295,
+ 323, 0, 340, 328, 0, 69, 68, 287, 0, 242,
+ 198, 82, 205, 0, 59, 0, 295, 295, 225, 0,
+ 164, 0, 280, 0, 46, 0, 138, 137, 275, 276,
+ 122, 129, 61, 319, 328, 289, 316, 0, 0, 295,
+ 315, 0, 0, 313, 300, 324, 289, 289, 331, 200,
+ 329, 67, 70, 207, 0, 0, 81, 235, 0, 0,
+ 56, 0, 63, 228, 227, 87, 134, 216, 34, 139,
+ 322, 0, 344, 314, 325, 342, 0, 0, 0, 205,
+ 84, 83, 0, 0, 328, 336, 328, 330, 199, 58,
+ 57, 326, 201
+};
+
+/* 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, 516, 517,
+ 144, 145, 68, 498, 327, 436, 499, 575, 387, 365,
+ 471, 236, 237, 238, 69, 126, 252, 70, 132, 377,
+ 571, 616, 71, 142, 398, 72, 140, 73, 74, 75,
+ 76, 313, 422, 423, 77, 315, 242, 135, 78, 149,
+ 110, 116, 13, 80, 81, 244, 245, 162, 118, 82,
+ 83, 478, 227, 84, 229, 230, 85, 86, 87, 129,
+ 213, 88, 251, 484, 89, 90, 22, 279, 518, 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, 464, 549, 543, 395, 100,
+ 211, 217, 608, 441, 346, 347, 348, 350, 550, 545,
+ 609, 610, 454, 455, 25, 465, 551, 546
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -551
+static const yytype_int16 yypact[] =
+{
+ -551, 46, 48, 121, -551, 58, -551, 135, -551, -551,
+ -551, 65, 72, -551, 147, 156, -551, -551, 112, -551,
+ 50, 134, 904, -551, 162, 463, 209, -551, 54, 237,
+ -551, 121, 245, -551, -551, -551, 58, 1666, -551, 58,
+ 288, -551, -551, 34, 288, 58, -551, 36, 184, 1467,
+ -551, 36, -551, 316, 462, 1467, 1467, 1467, 1467, 1467,
+ 1467, 1522, 1467, 1467, 965, 198, -551, 506, -551, -551,
+ -551, -551, -551, 796, -551, -551, 195, 1, -551, 199,
+ -551, 207, 216, 36, 221, -551, -551, -551, 226, 53,
+ -551, -551, 108, -551, 214, 110, 269, 214, 214, 233,
+ -551, -551, -551, -551, 238, -551, -551, -551, -551, -551,
+ -551, -551, 242, 1691, 1691, 1691, -551, 240, -551, -551,
+ -551, -551, -551, -551, 154, 1, 1467, 1658, 247, 246,
+ 290, -551, 1467, -551, -551, 419, 1691, 2004, 229, -551,
+ 272, 333, 1467, 258, 1691, -551, -551, 485, -551, -551,
+ -551, 656, -551, -551, -551, -551, -551, -551, 1577, 1522,
+ 2004, 255, -551, 10, -551, 51, -551, -551, 251, 2004,
+ 256, -551, 508, -551, 1632, 1467, 1467, 1467, 1467, -551,
+ 1467, 1467, 1467, -551, 1467, 1467, 1467, 1467, 1467, 1467,
+ 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, -551, 1192,
+ 552, 1467, -551, 1467, -551, -551, 1134, 1467, 1467, 1467,
+ -551, 1203, 58, 246, 260, 332, -551, 1838, 1838, -551,
+ 76, 282, -551, 1658, 344, 1691, -551, -551, -551, -551,
+ -551, -551, -551, 293, 58, -551, -551, 328, -551, 178,
+ 303, 1691, -551, 1658, -551, -551, -551, 295, 308, 1658,
+ 1134, -551, -551, 309, 271, 364, -551, 334, 337, -551,
+ -551, 330, -551, 11, 66, -551, -551, 342, -551, -551,
+ 397, 664, -551, -551, -551, 351, -551, -551, -551, 352,
+ 1467, 58, 343, 1719, -551, 349, 1691, 1691, -551, 356,
+ 1467, 357, 2004, 2075, -551, 2028, 692, 692, 692, 692,
+ -551, 692, 692, 2052, -551, 593, 593, 593, 593, -551,
+ -551, -551, -551, 1247, -551, -551, 22, 1302, -551, 1877,
+ 355, 1060, 1979, 1247, -551, -551, -551, -551, -551, -551,
+ 86, 229, 229, 2004, 1777, 367, 361, 359, -551, 366,
+ 427, 1838, 52, 29, -551, 370, -551, -551, -551, 784,
+ -551, 118, 379, 58, 396, 400, 401, -551, -551, 404,
+ 1691, 409, -551, -551, -551, -551, 1357, 1412, 1467, -551,
+ -551, -551, 1658, -551, 1744, 414, 109, 328, 1467, 58,
+ 416, 423, 1658, -551, 561, 417, 1691, 44, 364, 397,
+ 364, 428, 451, 421, -551, -551, 58, 397, 456, 58,
+ 436, 58, 438, 229, -551, 1467, 1752, 1691, -551, 24,
+ 171, 287, 338, -551, -551, -551, 58, 439, 229, 1467,
+ -551, 1907, -551, -551, 425, 435, 430, 1522, 446, 448,
+ 453, -551, 1467, -551, -551, 443, 1134, 1060, -551, 1838,
+ 479, -551, -551, -551, 58, 1805, 1838, 58, 1838, -551,
+ -551, 514, 161, -551, -551, 460, 454, 1838, 52, 1838,
+ 397, 58, 58, -551, 458, 459, -551, -551, -551, 1744,
+ -551, 1134, 1467, 1467, 467, -551, 1658, 472, -551, -551,
+ -551, -551, -551, -551, -551, 1744, -551, -551, -551, -551,
+ -551, 477, -551, -551, -551, 1522, 474, -551, -551, -551,
+ 482, -551, 500, 397, -551, -551, -551, -551, -551, -551,
+ -551, -551, -551, 229, 501, 1247, -551, -551, 505, 1632,
+ -551, 229, 1247, 1247, 1247, -551, -551, -551, 503, -551,
+ -551, -551, -551, 511, -551, 137, -551, -551, 518, 528,
+ 532, 534, 535, 530, -551, -551, 537, 533, 1838, 536,
+ -551, 538, -551, -551, 560, -551, 1838, -551, 551, 397,
+ -551, 557, -551, 1830, 151, 2004, 2004, 1467, 558, 1658,
+ -551, 605, 1744, 125, -551, 1060, 397, 397, -551, 75,
+ 360, 554, 58, 563, 357, 556, 2004, -551, -551, -551,
+ -551, -551, -551, -551, 1830, 58, -551, 1805, 1838, 397,
+ -551, 58, 161, -551, -551, -551, 58, 58, -551, -551,
+ -551, -551, -551, -551, 570, 78, -551, -551, 1467, 1467,
+ -551, 1522, 569, -551, -551, -551, -551, -551, -551, -551,
+ -551, 576, -551, -551, -551, -551, 578, 582, 584, 1744,
+ -551, -551, 1931, 1955, 1830, -551, 1830, -551, -551, -551,
+ -551, -551, -551
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const yytype_int16 yypgoto[] =
+{
+ -551, -551, -551, -551, -551, -551, -551, -6, -551, -551,
+ 608, -551, -11, -551, -551, 623, -551, -134, -25, 68,
+ -551, -135, -121, -551, 39, -551, -551, -551, 145, 278,
+ -551, -551, -551, -551, -551, -551, 515, 41, -551, -551,
+ -551, -551, -551, -551, -551, -551, -551, 579, 493, 45,
+ -551, -192, 138, -246, 192, -47, 415, 200, -20, 380,
+ 626, -5, 449, 346, -551, 426, 95, 509, -551, -551,
+ -551, -551, -33, 38, -31, -18, -551, -551, -551, -551,
+ -551, 43, 457, -467, -551, -551, -551, -551, -551, -551,
+ -551, -551, 280, -126, -227, 292, -551, 302, -551, -220,
+ -297, 662, -551, -248, -551, -66, 18, 194, -551, -295,
+ -228, -289, -191, -551, -119, -403, -551, -551, -305, -551,
+ -32, -551, 127, -551, 362, 250, 363, 232, 90, 98,
+ -550, -551, -426, 241, -551, 486, -551, -551
+};
+
+/* 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 YYTABLE_NINF, syntax error. */
+#define YYTABLE_NINF -270
+static const yytype_int16 yytable[] =
+{
+ 12, 174, 376, 359, 119, 235, 121, 240, 274, 259,
+ 320, 235, 323, 278, 161, 32, 109, 79, 570, 109,
+ 107, 235, 103, 32, 433, 131, 554, 428, 435, 375,
+ 385, 111, 456, 426, 111, 380, 389, 391, 128, 393,
+ 111, 539, 173, 164, 630, -176, 4, 400, 146, 150,
+ 495, 402, -208, 27, 205, 450, 496, 27, 9, -172,
+ 199, 417, 150, 214, 200, 216, 218, 138, 18, -175,
+ 388, 133, 201, 5, 9, 120, 9, -176, 9, 27,
+ 122, 495, 9, -172, 501, 124, -208, 496, 133, 130,
+ 380, -172, 507, 127, 651, 222, 652, 427, 46, 163,
+ 9, 457, 451, 134, 165, 617, 174, 10, 11, 28,
+ -174, 452, 497, 29, 102, 257, 207, 29, -208, 390,
+ 134, 266, 243, 10, 11, 10, 11, 10, 11, 381,
+ 111, 10, 11, 618, 19, 525, 111, 528, 146, 29,
+ 536, 164, 150, 625, 239, 325, 7, 289, 437, 10,
+ 11, 228, 228, 228, 438, 560, 231, 231, 231, 15,
+ -260, 500, 491, 502, 450, 228, -260, 150, 20, 212,
+ 231, 437, 648, 208, 228, 26, 636, 486, 461, 231,
+ 23, 164, 228, 209, 11, 9, 366, 231, 535, 228,
+ 619, 620, 631, 462, 231, 318, 33, 163, 578, 437,
+ 621, 79, 165, 637, 638, 592, -170, 349, 226, 232,
+ 233, 451, 228, 437, 357, 32, -260, 231, 243, 611,
+ 581, 515, -260, 564, 93, 331, 332, 585, 522, 363,
+ -170, 261, 125, 101, 10, 11, 125, 163, -170, 276,
+ 105, 533, 165, 367, 243, 79, 282, 235, 108, 474,
+ 409, 141, 411, 209, 605, 136, 568, 235, 259, 488,
+ 171, 228, 198, 228, 509, -147, 231, 511, 231, 291,
+ 430, 623, 624, 202, -229, 203, 150, 587, 589, 228,
+ -175, 228, 9, 204, 231, -174, 231, 228, 11, 206,
+ 583, 35, 231, 215, 634, 37, -259, 219, 403, 164,
+ 220, 223, -259, 234, 209, 112, 249, 262, 418, 228,
+ 47, 48, 9, 250, 231, 285, 79, 51, 273, 353,
+ 361, 410, -171, 286, 228, 228, 412, 622, 287, 231,
+ 231, 10, 11, -229, 379, 354, 369, 453, 345, -229,
+ 9, 479, 358, 481, 355, 356, -171, 61, 349, 614,
+ 519, 360, -259, 362, -171, 163, 482, 254, -259, 64,
+ 165, 10, 11, 364, 257, 368, 397, 243, 374, 477,
+ 372, 378, 266, -169, 489, 143, 506, 243, 408, 111,
+ 529, 414, 415, 117, 331, 332, 263, 111, 380, 10,
+ 11, 111, 264, 382, 146, -173, 150, -169, 228, 384,
+ 394, 265, 386, 231, 392, -169, 10, 11, 405, 164,
+ 228, 150, 480, 399, 401, 231, 413, 483, 416, -173,
+ 228, 432, 419, 513, 228, 231, 444, -173, 445, 231,
+ 446, 79, 79, 447, 448, 458, 479, 521, 481, 349,
+ 541, 463, 548, 254, 228, 228, 235, 453, 612, 231,
+ 231, 482, 479, 453, 481, 408, 561, 349, 466, 117,
+ 117, 117, 467, 468, 469, 163, 79, 482, 449, 470,
+ 165, 243, 94, 117, 485, 254, 460, 164, 255, 379,
+ 95, 494, 117, 490, 96, 493, 9, 256, 503, 505,
+ 117, 508, 10, 11, 97, 98, 510, 117, 512, 520,
+ 524, 226, 514, 526, 263, 527, 530, 480, 531, 9,
+ 264, 534, 483, 532, 228, 340, 519, 553, 563, 231,
+ 117, 147, 555, 480, 10, 11, 556, 99, 483, 567,
+ 9, 569, 9, 163, 462, 10, 11, 572, 165, 479,
+ 574, 481, 576, 210, 210, 277, 210, 210, 152, 153,
+ 154, 155, 156, 157, 482, 166, 167, 228, 10, 11,
+ 577, 580, 231, 590, 243, 172, 537, 582, 288, 117,
+ 79, 117, 544, 547, 529, 552, 9, 150, 593, 10,
+ 11, 10, 11, 591, 557, 254, 559, 117, 594, 117,
+ 349, -151, 541, 595, -152, 117, 548, 453, 596, 597,
+ 601, 349, 349, 164, 600, 598, 479, 228, 481, 602,
+ 480, 317, 231, 604, 584, 483, 606, 117, 613, 615,
+ 255, 482, 626, 628, 629, 10, 11, 177, 137, 117,
+ 639, 437, 117, 117, 10, 11, 644, 185, 645, 106,
+ 160, 189, 646, 169, 647, 66, 194, 195, 196, 197,
+ 627, 152, 156, 579, 640, 487, 641, 272, 370, 163,
+ 344, 404, 588, 37, 165, 123, 344, 344, 284, 371,
+ 352, 37, 504, 112, 475, 599, 492, 480, 47, 48,
+ 9, 112, 483, 603, 91, 51, 47, 48, 9, 573,
+ 538, 635, 224, 51, 562, 632, 442, 443, 351, 558,
+ 224, 0, 0, 0, 0, 0, 117, 0, 0, 114,
+ 0, 0, 0, 0, 0, 225, 0, 114, 117, 0,
+ 117, 280, 0, 225, 544, 633, 177, 64, 117, 10,
+ 11, 281, 117, 0, 0, 64, 185, 10, 11, 396,
+ 189, 190, 191, 192, 193, 194, 195, 196, 197, 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, 344, 0, 0, 137, 137, 333, 0,
+ 344, 334, 0, 0, 0, 459, 0, 0, 344, 0,
+ 175, 335, 0, 0, -269, 0, 336, 337, 338, 0,
+ 0, 0, 0, 339, 0, 117, 0, 0, 0, 0,
+ 340, 0, 117, 0, 0, 0, 0, 0, 0, 176,
+ 177, 117, 178, 179, 180, 181, 182, 341, 183, 184,
+ 185, 186, 187, 188, 189, 190, 191, 192, 193, 194,
+ 195, 196, 197, 0, 0, 343, 0, 0, 11, 137,
+ 0, -269, 0, 0, 0, 117, 0, 0, 0, 137,
+ 0, -269, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 344, 0,
+ 0, 0, 421, 0, 542, 344, 160, 344, 0, 0,
+ 0, 0, 421, 0, -2, 34, 344, 35, 344, 36,
+ 0, 37, 0, 38, 39, 117, 0, 40, 117, 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, 137, 137, 0, 0, 0,
+ 0, 0, 0, 56, 57, 0, 58, 59, 0, 0,
+ 60, 0, 0, 61, 0, 0, -24, 0, 35, 0,
+ 0, 0, 37, 62, 63, 64, 168, 10, 11, 0,
+ 0, 0, 112, 0, 137, 117, 0, 47, 48, 9,
+ 0, 0, 0, 0, 51, 0, 0, 344, 137, 0,
+ 0, 55, 0, 0, 0, 344, 160, 0, 0, 0,
+ 0, 169, 344, 0, 56, 57, 0, 58, 59, 0,
+ 0, 60, 0, 0, 61, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 62, 63, 64, 0, 10, 11,
+ 0, 0, 0, 344, 0, 0, 542, 344, 0, 0,
+ 0, 565, 566, 0, 0, 0, 0, 0, 0, 0,
+ 0, 324, 0, 35, 0, 36, -244, 37, 0, 38,
+ 39, 0, -244, 40, 160, 41, 42, 112, 44, 45,
+ 46, 0, 47, 48, 9, 0, 0, 49, 50, 51,
+ 52, 53, 54, 344, 421, 344, 55, 0, 0, 0,
+ 0, 421, 586, 421, 0, 0, 0, 0, 0, 56,
+ 57, 0, 58, 59, 0, 0, 60, 0, 0, 61,
+ 0, 0, -244, 0, 0, 0, 0, 325, -244, 62,
+ 63, 64, 0, 10, 11, 324, 0, 35, 0, 36,
+ 0, 37, 0, 38, 39, 0, 0, 40, 0, 41,
+ 42, 112, 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, 35, -244, 642, 643, 37,
+ 160, 325, -244, 62, 63, 64, 0, 10, 11, 112,
+ 334, 0, 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, 37, 0, 341, 0, 0, 0,
+ 0, 62, 63, 64, 112, 10, 11, 0, 342, 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, 37,
+ 0, 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, 0, 0, 0, 0, 0, 0, 0,
+ 0, 56, 57, 0, 58, 159, 0, 0, 60, 0,
+ 35, 61, 0, 0, 37, 0, 0, 0, 0, 0,
+ 0, 62, 63, 64, 112, 10, 11, 0, 0, 47,
+ 48, 9, 0, 472, 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, 37,
+ 0, 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, 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, 37, 0, 0, 0, 0, 0,
+ 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, 0, 0, 0, 56, 57, 0, 58,
+ 59, 0, 0, 60, 0, 35, 61, 0, 0, 37,
+ 0, 0, 0, 0, 0, 0, 62, 63, 64, 112,
+ 10, 11, 0, 0, 47, 48, 9, 0, 0, 0,
+ 0, 51, 0, 0, 0, 0, 0, 0, 158, 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, 283, 0, 0, 0, 0, 0,
+ 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, 0, 0, 0, 56, 57, 0, 58,
+ 59, 0, 0, 60, 0, 0, 61, 0, 0, 37,
+ 0, 0, 0, 0, 0, 0, 62, 63, 64, 112,
+ 10, 11, 0, 0, 47, 48, 9, 0, 0, 0,
+ 0, 51, 0, 0, 0, 37, 0, 0, 224, 241,
+ 0, 0, 0, 37, 0, 112, 0, 0, 0, 0,
+ 47, 48, 9, 112, 0, 114, 0, 51, 47, 48,
+ 9, 225, 0, 0, 224, 51, 0, 290, 37, 0,
+ 0, 0, 113, 64, 0, 10, 11, 281, 112, 0,
+ 0, 114, 0, 47, 48, 9, 0, 225, 0, 114,
+ 51, 0, 0, 0, 0, 115, 37, 224, 0, 64,
+ 0, 10, 11, 0, 0, 0, 112, 64, 0, 10,
+ 11, 47, 48, 9, 114, 0, 0, 0, 51, 0,
+ 225, 37, 0, 0, 0, 406, 0, 0, 0, 283,
+ 0, 112, 64, 0, 10, 11, 47, 48, 9, 112,
+ 0, 0, 114, 51, 47, 48, 9, 0, 407, 0,
+ 224, 51, 0, 0, 334, 0, 0, 0, 224, 0,
+ 64, 0, 10, 11, 335, 0, 0, 114, 0, 336,
+ 337, 338, 0, 476, 0, 114, 339, 0, 0, 0,
+ 0, 225, 334, 439, 0, 64, 0, 10, 11, 0,
+ 0, 0, 335, 64, 0, 10, 11, 336, 337, 540,
+ 341, 0, 0, 0, 339, 0, 440, 334, 0, 0,
+ 0, 340, 0, 0, 0, 334, 0, 335, 343, 0,
+ 0, 11, 336, 337, 338, 335, 0, 0, 341, 339,
+ 336, 337, 338, 0, 0, 0, 340, 339, 0, 0,
+ 0, 0, 0, 0, 340, 0, 343, 0, 10, 11,
+ 0, 0, 0, 341, 0, 0, 0, 0, 0, 607,
+ 0, 341, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 343, 0, 0, 11, 0, 0, 0, 0, 343,
+ 176, 177, 11, 178, 0, 180, 181, 182, 0, 0,
+ 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, 523, 184, 185, 186, 187, 188, 189,
+ 190, 191, 192, 193, 194, 195, 196, 197, 176, 177,
+ 0, 178, 0, 180, 181, 182, 0, 649, 184, 185,
+ 186, 187, 188, 189, 190, 191, 192, 193, 194, 195,
+ 196, 197, 176, 177, 0, 178, 0, 180, 181, 182,
+ 0, 650, 184, 185, 186, 187, 188, 189, 190, 191,
+ 192, 193, 194, 195, 196, 197, 0, 176, 177, 434,
+ 178, 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, 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, 177,
+ 0, 0, 0, 180, 181, 182, 0, 0, 184, 185,
+ 186, 187, 0, 189, 190, 191, 192, 193, 194, 195,
+ 196, 197
+};
+
+#define yypact_value_is_default(yystate) \
+ ((yystate) == (-551))
+
+#define yytable_value_is_error(yytable_value) \
+ YYID (0)
+
+static const yytype_int16 yycheck[] =
+{
+ 5, 67, 250, 223, 37, 126, 37, 126, 143, 135,
+ 201, 132, 204, 147, 61, 20, 36, 22, 485, 39,
+ 31, 142, 28, 28, 321, 45, 452, 316, 323, 249,
+ 258, 36, 3, 11, 39, 24, 263, 264, 43, 267,
+ 45, 444, 67, 61, 594, 35, 0, 275, 53, 54,
+ 6, 279, 1, 3, 1, 3, 12, 3, 24, 35,
+ 59, 289, 67, 95, 63, 97, 98, 49, 3, 59,
+ 59, 35, 71, 25, 24, 37, 24, 67, 24, 3,
+ 37, 6, 24, 59, 389, 40, 35, 12, 35, 44,
+ 24, 67, 397, 59, 644, 106, 646, 75, 20, 61,
+ 24, 72, 50, 67, 61, 572, 172, 73, 74, 59,
+ 59, 59, 68, 63, 60, 135, 8, 63, 67, 53,
+ 67, 141, 127, 73, 74, 73, 74, 73, 74, 255,
+ 135, 73, 74, 8, 62, 424, 141, 426, 143, 63,
+ 437, 159, 147, 68, 126, 67, 25, 172, 62, 73,
+ 74, 113, 114, 115, 68, 460, 113, 114, 115, 24,
+ 6, 388, 382, 390, 3, 127, 12, 172, 21, 59,
+ 127, 62, 639, 65, 136, 63, 602, 68, 60, 136,
+ 24, 199, 144, 75, 74, 24, 8, 144, 436, 151,
+ 65, 66, 595, 75, 151, 200, 62, 159, 503, 62,
+ 75, 206, 159, 606, 607, 68, 35, 212, 113, 114,
+ 115, 50, 174, 62, 220, 220, 62, 174, 223, 68,
+ 515, 413, 68, 471, 62, 207, 208, 522, 420, 234,
+ 59, 136, 40, 24, 73, 74, 44, 199, 67, 144,
+ 3, 432, 199, 65, 249, 250, 151, 368, 3, 368,
+ 283, 51, 283, 75, 559, 71, 476, 378, 384, 378,
+ 62, 223, 67, 225, 399, 66, 223, 401, 225, 174,
+ 317, 576, 577, 66, 3, 59, 281, 523, 524, 241,
+ 59, 243, 24, 83, 241, 59, 243, 249, 74, 89,
+ 518, 3, 249, 24, 599, 7, 6, 64, 280, 317,
+ 62, 59, 12, 63, 75, 17, 59, 35, 290, 271,
+ 22, 23, 24, 67, 271, 60, 321, 29, 60, 59,
+ 225, 283, 35, 72, 286, 287, 283, 575, 72, 286,
+ 287, 73, 74, 62, 63, 3, 241, 342, 211, 68,
+ 24, 374, 60, 374, 217, 218, 59, 59, 353, 569,
+ 416, 7, 62, 60, 67, 317, 374, 24, 68, 71,
+ 317, 73, 74, 35, 384, 62, 271, 372, 60, 374,
+ 75, 62, 392, 35, 379, 59, 396, 382, 283, 384,
+ 427, 286, 287, 37, 366, 367, 53, 392, 24, 73,
+ 74, 396, 59, 59, 399, 35, 401, 59, 360, 62,
+ 3, 68, 72, 360, 62, 67, 73, 74, 65, 427,
+ 372, 416, 374, 62, 62, 372, 67, 374, 62, 59,
+ 382, 66, 65, 405, 386, 382, 59, 67, 67, 386,
+ 71, 436, 437, 67, 7, 65, 469, 419, 469, 444,
+ 445, 62, 447, 24, 406, 407, 567, 452, 567, 406,
+ 407, 469, 485, 458, 485, 360, 461, 462, 62, 113,
+ 114, 115, 62, 62, 60, 427, 471, 485, 341, 60,
+ 427, 476, 9, 127, 60, 24, 349, 495, 59, 63,
+ 17, 386, 136, 60, 21, 68, 24, 68, 60, 68,
+ 144, 35, 73, 74, 31, 32, 60, 151, 60, 60,
+ 75, 406, 407, 68, 53, 75, 60, 469, 60, 24,
+ 59, 68, 469, 60, 476, 36, 582, 3, 60, 476,
+ 174, 59, 62, 485, 73, 74, 72, 64, 485, 62,
+ 24, 59, 24, 495, 75, 73, 74, 60, 495, 572,
+ 66, 572, 60, 94, 95, 60, 97, 98, 55, 56,
+ 57, 58, 59, 60, 572, 62, 63, 519, 73, 74,
+ 60, 60, 519, 60, 569, 59, 439, 62, 60, 223,
+ 575, 225, 445, 446, 621, 448, 24, 582, 60, 73,
+ 74, 73, 74, 72, 457, 24, 459, 241, 60, 243,
+ 595, 59, 597, 59, 59, 249, 601, 602, 68, 62,
+ 62, 606, 607, 621, 68, 72, 639, 569, 639, 49,
+ 572, 59, 569, 62, 519, 572, 59, 271, 60, 14,
+ 59, 639, 68, 60, 68, 73, 74, 34, 49, 283,
+ 60, 62, 286, 287, 73, 74, 60, 44, 60, 31,
+ 61, 48, 60, 64, 60, 22, 53, 54, 55, 56,
+ 582, 158, 159, 508, 615, 377, 615, 142, 243, 621,
+ 211, 281, 524, 7, 621, 39, 217, 218, 159, 243,
+ 213, 7, 392, 17, 372, 548, 384, 639, 22, 23,
+ 24, 17, 639, 556, 22, 29, 22, 23, 24, 495,
+ 440, 601, 36, 29, 462, 597, 334, 334, 212, 458,
+ 36, -1, -1, -1, -1, -1, 360, -1, -1, 53,
+ -1, -1, -1, -1, -1, 59, -1, 53, 372, -1,
+ 374, 65, -1, 59, 597, 598, 34, 71, 382, 73,
+ 74, 75, 386, -1, -1, 71, 44, 73, 74, 75,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, -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, 334, -1, -1, 207, 208, 209, -1,
+ 341, 7, -1, -1, -1, 11, -1, -1, 349, -1,
+ 4, 17, -1, -1, 8, -1, 22, 23, 24, -1,
+ -1, -1, -1, 29, -1, 469, -1, -1, -1, -1,
+ 36, -1, 476, -1, -1, -1, -1, -1, -1, 33,
+ 34, 485, 36, 37, 38, 39, 40, 53, 42, 43,
+ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
+ 54, 55, 56, -1, -1, 71, -1, -1, 74, 280,
+ -1, 65, -1, -1, -1, 519, -1, -1, -1, 290,
+ -1, 75, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 439, -1,
+ -1, -1, 313, -1, 445, 446, 317, 448, -1, -1,
+ -1, -1, 323, -1, 0, 1, 457, 3, 459, 5,
+ -1, 7, -1, 9, 10, 569, -1, 13, 572, 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, 366, 367, -1, -1, -1,
+ -1, -1, -1, 49, 50, -1, 52, 53, -1, -1,
+ 56, -1, -1, 59, -1, -1, 62, -1, 3, -1,
+ -1, -1, 7, 69, 70, 71, 11, 73, 74, -1,
+ -1, -1, 17, -1, 405, 639, -1, 22, 23, 24,
+ -1, -1, -1, -1, 29, -1, -1, 548, 419, -1,
+ -1, 36, -1, -1, -1, 556, 427, -1, -1, -1,
+ -1, 432, 563, -1, 49, 50, -1, 52, 53, -1,
+ -1, 56, -1, -1, 59, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 69, 70, 71, -1, 73, 74,
+ -1, -1, -1, 594, -1, -1, 597, 598, -1, -1,
+ -1, 472, 473, -1, -1, -1, -1, -1, -1, -1,
+ -1, 1, -1, 3, -1, 5, 6, 7, -1, 9,
+ 10, -1, 12, 13, 495, 15, 16, 17, 18, 19,
+ 20, -1, 22, 23, 24, -1, -1, 27, 28, 29,
+ 30, 31, 32, 644, 515, 646, 36, -1, -1, -1,
+ -1, 522, 523, 524, -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, 5,
+ -1, 7, -1, 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, 3, 62, 618, 619, 7,
+ 621, 67, 68, 69, 70, 71, -1, 73, 74, 17,
+ 7, -1, -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, 7, -1, 53, -1, -1, -1,
+ -1, 69, 70, 71, 17, 73, 74, -1, 65, 22,
+ 23, 24, -1, -1, 71, -1, 29, 74, -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, 7,
+ -1, -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, 7, -1, -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, 7,
+ -1, -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, 7, -1, -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, 7,
+ -1, -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, 7, -1, -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, -1, 59, -1, -1, 7,
+ -1, -1, -1, -1, -1, -1, 69, 70, 71, 17,
+ 73, 74, -1, -1, 22, 23, 24, -1, -1, -1,
+ -1, 29, -1, -1, -1, 7, -1, -1, 36, 11,
+ -1, -1, -1, 7, -1, 17, -1, -1, -1, -1,
+ 22, 23, 24, 17, -1, 53, -1, 29, 22, 23,
+ 24, 59, -1, -1, 36, 29, -1, 65, 7, -1,
+ -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, 7, 36, -1, 71,
+ -1, 73, 74, -1, -1, -1, 17, 71, -1, 73,
+ 74, 22, 23, 24, 53, -1, -1, -1, 29, -1,
+ 59, 7, -1, -1, -1, 36, -1, -1, -1, 7,
+ -1, 17, 71, -1, 73, 74, 22, 23, 24, 17,
+ -1, -1, 53, 29, 22, 23, 24, -1, 59, -1,
+ 36, 29, -1, -1, 7, -1, -1, -1, 36, -1,
+ 71, -1, 73, 74, 17, -1, -1, 53, -1, 22,
+ 23, 24, -1, 59, -1, 53, 29, -1, -1, -1,
+ -1, 59, 7, 36, -1, 71, -1, 73, 74, -1,
+ -1, -1, 17, 71, -1, 73, 74, 22, 23, 24,
+ 53, -1, -1, -1, 29, -1, 59, 7, -1, -1,
+ -1, 36, -1, -1, -1, 7, -1, 17, 71, -1,
+ -1, 74, 22, 23, 24, 17, -1, -1, 53, 29,
+ 22, 23, 24, -1, -1, -1, 36, 29, -1, -1,
+ -1, -1, -1, -1, 36, -1, 71, -1, 73, 74,
+ -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,
+ 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, -1, 33, 34, 60,
+ 36, -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, 34,
+ -1, -1, -1, 38, 39, 40, -1, -1, 43, 44,
+ 45, 46, -1, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const yytype_uint8 yystos[] =
+{
+ 0, 77, 79, 80, 0, 25, 78, 25, 86, 24,
+ 73, 74, 137, 138, 81, 24, 88, 89, 3, 62,
+ 21, 82, 162, 24, 87, 210, 63, 3, 59, 63,
+ 83, 85, 137, 62, 1, 3, 5, 7, 9, 10,
+ 13, 15, 16, 17, 18, 19, 20, 22, 23, 27,
+ 28, 29, 30, 31, 32, 36, 49, 50, 52, 53,
+ 56, 59, 69, 70, 71, 90, 91, 92, 98, 110,
+ 113, 118, 121, 123, 124, 125, 126, 130, 134, 137,
+ 139, 140, 145, 146, 149, 152, 153, 154, 157, 160,
+ 161, 177, 182, 62, 9, 17, 21, 31, 32, 64,
+ 195, 24, 60, 83, 84, 3, 86, 88, 3, 134,
+ 136, 137, 17, 36, 53, 59, 137, 139, 144, 148,
+ 149, 150, 157, 136, 125, 130, 111, 59, 137, 155,
+ 125, 134, 114, 35, 67, 133, 71, 123, 182, 189,
+ 122, 133, 119, 59, 96, 97, 137, 59, 93, 135,
+ 137, 181, 124, 124, 124, 124, 124, 124, 36, 53,
+ 123, 131, 143, 149, 151, 157, 124, 124, 11, 123,
+ 188, 62, 59, 94, 181, 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, 133, 1, 133, 8, 65, 75,
+ 138, 196, 59, 156, 196, 24, 196, 197, 196, 64,
+ 62, 186, 88, 59, 36, 59, 142, 148, 149, 150,
+ 151, 157, 142, 142, 63, 98, 107, 108, 109, 182,
+ 190, 11, 132, 137, 141, 142, 173, 174, 175, 59,
+ 67, 158, 112, 190, 24, 59, 68, 134, 167, 169,
+ 171, 142, 35, 53, 59, 68, 134, 166, 168, 169,
+ 170, 180, 112, 60, 97, 165, 142, 60, 93, 163,
+ 65, 75, 142, 7, 143, 60, 72, 72, 60, 94,
+ 65, 142, 123, 123, 123, 123, 123, 123, 123, 123,
+ 123, 123, 123, 123, 123, 123, 123, 123, 123, 123,
+ 123, 123, 123, 127, 60, 131, 183, 59, 137, 123,
+ 188, 178, 123, 127, 1, 67, 91, 100, 176, 177,
+ 179, 182, 182, 123, 7, 17, 22, 23, 24, 29,
+ 36, 53, 65, 71, 138, 198, 200, 201, 202, 137,
+ 203, 211, 158, 59, 3, 198, 198, 83, 60, 175,
+ 7, 142, 60, 137, 35, 105, 8, 65, 62, 142,
+ 132, 141, 75, 187, 60, 175, 179, 115, 62, 63,
+ 24, 169, 59, 172, 62, 186, 72, 104, 59, 170,
+ 53, 170, 62, 186, 3, 194, 75, 142, 120, 62,
+ 186, 62, 186, 182, 135, 65, 36, 59, 142, 148,
+ 149, 150, 157, 67, 142, 142, 62, 186, 182, 65,
+ 67, 123, 128, 129, 184, 185, 11, 75, 187, 31,
+ 131, 72, 66, 176, 60, 185, 101, 62, 68, 36,
+ 59, 199, 200, 202, 59, 67, 71, 67, 7, 198,
+ 3, 50, 59, 137, 208, 209, 3, 72, 65, 11,
+ 198, 60, 75, 62, 191, 211, 62, 62, 62, 60,
+ 60, 106, 26, 26, 190, 173, 59, 137, 147, 148,
+ 149, 150, 151, 157, 159, 60, 68, 105, 190, 137,
+ 60, 175, 171, 68, 142, 6, 12, 68, 99, 102,
+ 170, 194, 170, 60, 168, 68, 134, 194, 35, 97,
+ 60, 93, 60, 182, 142, 127, 94, 95, 164, 181,
+ 60, 182, 127, 66, 75, 187, 68, 75, 187, 131,
+ 60, 60, 60, 188, 68, 179, 176, 198, 201, 191,
+ 24, 137, 138, 193, 198, 205, 213, 198, 137, 192,
+ 204, 212, 198, 3, 208, 62, 72, 198, 209, 198,
+ 194, 137, 203, 60, 179, 123, 123, 62, 175, 59,
+ 159, 116, 60, 183, 66, 103, 60, 60, 194, 104,
+ 60, 185, 62, 186, 142, 185, 123, 129, 128, 129,
+ 60, 72, 68, 60, 60, 59, 68, 62, 72, 198,
+ 68, 62, 49, 198, 62, 194, 59, 59, 198, 206,
+ 207, 68, 190, 60, 175, 14, 117, 159, 8, 65,
+ 66, 75, 179, 194, 194, 68, 68, 95, 60, 68,
+ 206, 191, 205, 198, 194, 204, 208, 191, 191, 60,
+ 100, 113, 123, 123, 60, 60, 60, 60, 159, 66,
+ 66, 206, 206
+};
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+/* 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. 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); \
+ YYPOPSTACK (1); \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror (YY_("syntax error: cannot back up")); \
+ YYERROR; \
+ } \
+while (YYID (0))
+
+
+#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
+
+
+/* This macro is provided for backward compatibility. */
+
+#ifndef YY_LOCATION_PRINT
+# 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
+# define YYLEX yylex ()
+#endif
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yy_symbol_print (stderr, \
+ Type, Value); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+#endif
+{
+ if (!yyvaluep)
+ return;
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+ YYUSE (yyoutput);
+# endif
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+#endif
+{
+ if (yytype < YYNTOKENS)
+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+ else
+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+ yy_symbol_value_print (yyoutput, yytype, yyvaluep);
+ YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop)
+#else
+static void
+yy_stack_print (yybottom, yytop)
+ yytype_int16 *yybottom;
+ yytype_int16 *yytop;
+#endif
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (; yybottom <= yytop; yybottom++)
+ {
+ int yybot = *yybottom;
+ YYFPRINTF (stderr, " %d", yybot);
+ }
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, int yyrule)
+#else
+static void
+yy_reduce_print (yyvsp, yyrule)
+ YYSTYPE *yyvsp;
+ int yyrule;
+#endif
+{
+ int yynrhs = yyr2[yyrule];
+ int yyi;
+ unsigned long int yylno = yyrline[yyrule];
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+ yyrule - 1, yylno);
+ /* The symbols being reduced. */
+ for (yyi = 0; yyi < yynrhs; yyi++)
+ {
+ YYFPRINTF (stderr, " $%d = ", yyi + 1);
+ yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+ &(yyvsp[(yyi + 1) - (yynrhs)])
+ );
+ YYFPRINTF (stderr, "\n");
+ }
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (yyvsp, Rule); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined __GLIBC__ && defined _STRING_H
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+ const char *yystr;
+#endif
+{
+ YYSIZE_T yylen;
+ for (yylen = 0; yystr[yylen]; yylen++)
+ continue;
+ return yylen;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+ char *yydest;
+ const char *yysrc;
+#endif
+{
+ char *yyd = yydest;
+ const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+ quotes and backslashes, so that it's suitable for yyerror. The
+ heuristic is that double-quoting is unnecessary unless the string
+ contains an apostrophe, a comma, or backslash (other than
+ backslash-backslash). YYSTR is taken from yytname. If YYRES is
+ null, do not copy; instead, return the length of what the result
+ would have been. */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+ if (*yystr == '"')
+ {
+ YYSIZE_T yyn = 0;
+ char const *yyp = yystr;
+
+ for (;;)
+ switch (*++yyp)
+ {
+ case '\'':
+ case ',':
+ goto do_not_strip_quotes;
+
+ case '\\':
+ if (*++yyp != '\\')
+ goto do_not_strip_quotes;
+ /* Fall through. */
+ default:
+ if (yyres)
+ yyres[yyn] = *yyp;
+ yyn++;
+ break;
+
+ case '"':
+ if (yyres)
+ yyres[yyn] = '\0';
+ return yyn;
+ }
+ do_not_strip_quotes: ;
+ }
+
+ if (! yyres)
+ return yystrlen (yystr);
+
+ return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* 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.
+
+ 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;
+ }
+ }
+ }
+
+ 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_
+ }
+
+ yysize1 = yysize + yystrlen (yyformat);
+ if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+ return 2;
+ yysize = yysize1;
+
+ 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. |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep)
+ const char *yymsg;
+ int yytype;
+ YYSTYPE *yyvaluep;
+#endif
+{
+ YYUSE (yyvaluep);
+
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ switch (yytype)
+ {
+
+ default:
+ break;
+ }
+}
+
+
+/* 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 lookahead symbol. */
+int yychar, yystate;
+
+/* The semantic value of the lookahead symbol. */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far. */
+int yynerrs;
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+ void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+ /* 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 thru 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;
+ /* 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. */
+
+ /* 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;
+
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. So pushing a state here evens the stacks. */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ 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
+ be undefined if yyoverflow is a macro. */
+ yyoverflow (YY_("memory exhausted"),
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+ &yystacksize);
+
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyexhaustedlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyexhaustedlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ yytype_int16 *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyexhaustedlab;
+ YYSTACK_RELOCATE (yyss_alloc, yyss);
+ YYSTACK_RELOCATE (yyvs_alloc, yyvs);
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ if (yystate == YYFINAL)
+ YYACCEPT;
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+ /* Do appropriate processing given the current state. Read a
+ lookahead token if we need one and don't already have one. */
+
+ /* First try to decide what to do without reference to lookahead token. */
+ yyn = yypact[yystate];
+ if (yypact_value_is_default (yyn))
+ goto yydefault;
+
+ /* Not known => get a lookahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = YYLEX;
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yytable_value_is_error (yyn))
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ /* Shift the lookahead token. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+ /* Discard the shifted token. */
+ yychar = YYEMPTY;
+
+ yystate = yyn;
+ *++yyvsp = yylval;
+
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ `$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 2:
+
+/* Line 1806 of yacc.c */
+#line 128 "go.y"
+ {
+ xtop = concat(xtop, (yyvsp[(4) - (4)].list));
+ }
+ break;
+
+ case 3:
+
+/* Line 1806 of yacc.c */
+#line 134 "go.y"
+ {
+ prevlineno = lineno;
+ yyerror("package statement must be first");
+ flusherrors();
+ mkpackage("main");
+ }
+ break;
+
+ case 4:
+
+/* Line 1806 of yacc.c */
+#line 141 "go.y"
+ {
+ mkpackage((yyvsp[(2) - (3)].sym)->name);
+ }
+ break;
+
+ case 5:
+
+/* Line 1806 of yacc.c */
+#line 151 "go.y"
+ {
+ importpkg = runtimepkg;
+
+ if(debug['A'])
+ cannedimports("runtime.builtin", "package runtime\n\n$$\n\n");
+ else
+ cannedimports("runtime.builtin", runtimeimport);
+ curio.importsafe = 1;
+ }
+ break;
+
+ case 6:
+
+/* Line 1806 of yacc.c */
+#line 162 "go.y"
+ {
+ importpkg = nil;
+ }
+ break;
+
+ case 12:
+
+/* Line 1806 of yacc.c */
+#line 176 "go.y"
+ {
+ Pkg *ipkg;
+ Sym *my;
+ Node *pack;
+
+ ipkg = importpkg;
+ my = importmyname;
+ importpkg = nil;
+ importmyname = S;
+
+ if(my == nil)
+ my = lookup(ipkg->name);
+
+ pack = nod(OPACK, N, N);
+ pack->sym = my;
+ pack->pkg = ipkg;
+ pack->lineno = (yyvsp[(1) - (3)].i);
+
+ if(my->name[0] == '.') {
+ importdot(ipkg, pack);
+ break;
+ }
+ if(my->name[0] == '_' && my->name[1] == '\0')
+ break;
+ if(my->def) {
+ lineno = (yyvsp[(1) - (3)].i);
+ redeclare(my, "as imported package name");
+ }
+ my->def = pack;
+ my->lastlineno = (yyvsp[(1) - (3)].i);
+ my->block = 1; // at top level
+ }
+ break;
+
+ case 13:
+
+/* Line 1806 of yacc.c */
+#line 209 "go.y"
+ {
+ // When an invalid import path is passed to importfile,
+ // it calls yyerror and then sets up a fake import with
+ // no package statement. This allows us to test more
+ // than one invalid import statement in a single file.
+ if(nerrors == 0)
+ fatal("phase error in import");
+ }
+ break;
+
+ case 16:
+
+/* Line 1806 of yacc.c */
+#line 224 "go.y"
+ {
+ // import with original name
+ (yyval.i) = parserline();
+ importmyname = S;
+ importfile(&(yyvsp[(1) - (1)].val), (yyval.i));
+ }
+ break;
+
+ case 17:
+
+/* Line 1806 of yacc.c */
+#line 231 "go.y"
+ {
+ // import with given name
+ (yyval.i) = parserline();
+ importmyname = (yyvsp[(1) - (2)].sym);
+ importfile(&(yyvsp[(2) - (2)].val), (yyval.i));
+ }
+ break;
+
+ case 18:
+
+/* Line 1806 of yacc.c */
+#line 238 "go.y"
+ {
+ // import into my name space
+ (yyval.i) = parserline();
+ importmyname = lookup(".");
+ importfile(&(yyvsp[(2) - (2)].val), (yyval.i));
+ }
+ break;
+
+ case 19:
+
+/* Line 1806 of yacc.c */
+#line 247 "go.y"
+ {
+ if(importpkg->name == nil) {
+ importpkg->name = (yyvsp[(2) - (4)].sym)->name;
+ pkglookup((yyvsp[(2) - (4)].sym)->name, nil)->npkg++;
+ } else if(strcmp(importpkg->name, (yyvsp[(2) - (4)].sym)->name) != 0)
+ yyerror("conflicting names %s and %s for package \"%Z\"", importpkg->name, (yyvsp[(2) - (4)].sym)->name, importpkg->path);
+ importpkg->direct = 1;
+
+ if(safemode && !curio.importsafe)
+ yyerror("cannot import unsafe package \"%Z\"", importpkg->path);
+ }
+ break;
+
+ case 21:
+
+/* Line 1806 of yacc.c */
+#line 261 "go.y"
+ {
+ if(strcmp((yyvsp[(1) - (1)].sym)->name, "safe") == 0)
+ curio.importsafe = 1;
+ }
+ break;
+
+ case 22:
+
+/* Line 1806 of yacc.c */
+#line 267 "go.y"
+ {
+ defercheckwidth();
+ }
+ break;
+
+ case 23:
+
+/* Line 1806 of yacc.c */
+#line 271 "go.y"
+ {
+ resumecheckwidth();
+ unimportfile();
+ }
+ break;
+
+ case 24:
+
+/* Line 1806 of yacc.c */
+#line 280 "go.y"
+ {
+ yyerror("empty top-level declaration");
+ (yyval.list) = nil;
+ }
+ break;
+
+ case 26:
+
+/* Line 1806 of yacc.c */
+#line 286 "go.y"
+ {
+ (yyval.list) = list1((yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 27:
+
+/* Line 1806 of yacc.c */
+#line 290 "go.y"
+ {
+ yyerror("non-declaration statement outside function body");
+ (yyval.list) = nil;
+ }
+ break;
+
+ case 28:
+
+/* Line 1806 of yacc.c */
+#line 295 "go.y"
+ {
+ (yyval.list) = nil;
+ }
+ break;
+
+ case 29:
+
+/* Line 1806 of yacc.c */
+#line 301 "go.y"
+ {
+ (yyval.list) = (yyvsp[(2) - (2)].list);
+ }
+ break;
+
+ case 30:
+
+/* Line 1806 of yacc.c */
+#line 305 "go.y"
+ {
+ (yyval.list) = (yyvsp[(3) - (5)].list);
+ }
+ break;
+
+ case 31:
+
+/* Line 1806 of yacc.c */
+#line 309 "go.y"
+ {
+ (yyval.list) = nil;
+ }
+ break;
+
+ case 32:
+
+/* Line 1806 of yacc.c */
+#line 313 "go.y"
+ {
+ (yyval.list) = (yyvsp[(2) - (2)].list);
+ iota = -100000;
+ lastconst = nil;
+ }
+ break;
+
+ case 33:
+
+/* Line 1806 of yacc.c */
+#line 319 "go.y"
+ {
+ (yyval.list) = (yyvsp[(3) - (5)].list);
+ iota = -100000;
+ lastconst = nil;
+ }
+ break;
+
+ case 34:
+
+/* Line 1806 of yacc.c */
+#line 325 "go.y"
+ {
+ (yyval.list) = concat((yyvsp[(3) - (7)].list), (yyvsp[(5) - (7)].list));
+ iota = -100000;
+ lastconst = nil;
+ }
+ break;
+
+ case 35:
+
+/* Line 1806 of yacc.c */
+#line 331 "go.y"
+ {
+ (yyval.list) = nil;
+ iota = -100000;
+ }
+ break;
+
+ case 36:
+
+/* Line 1806 of yacc.c */
+#line 336 "go.y"
+ {
+ (yyval.list) = list1((yyvsp[(2) - (2)].node));
+ }
+ break;
+
+ case 37:
+
+/* Line 1806 of yacc.c */
+#line 340 "go.y"
+ {
+ (yyval.list) = (yyvsp[(3) - (5)].list);
+ }
+ break;
+
+ case 38:
+
+/* Line 1806 of yacc.c */
+#line 344 "go.y"
+ {
+ (yyval.list) = nil;
+ }
+ break;
+
+ case 39:
+
+/* Line 1806 of yacc.c */
+#line 350 "go.y"
+ {
+ iota = 0;
+ }
+ break;
+
+ case 40:
+
+/* Line 1806 of yacc.c */
+#line 356 "go.y"
+ {
+ (yyval.list) = variter((yyvsp[(1) - (2)].list), (yyvsp[(2) - (2)].node), nil);
+ }
+ break;
+
+ case 41:
+
+/* Line 1806 of yacc.c */
+#line 360 "go.y"
+ {
+ (yyval.list) = variter((yyvsp[(1) - (4)].list), (yyvsp[(2) - (4)].node), (yyvsp[(4) - (4)].list));
+ }
+ break;
+
+ case 42:
+
+/* Line 1806 of yacc.c */
+#line 364 "go.y"
+ {
+ (yyval.list) = variter((yyvsp[(1) - (3)].list), nil, (yyvsp[(3) - (3)].list));
+ }
+ break;
+
+ case 43:
+
+/* Line 1806 of yacc.c */
+#line 370 "go.y"
+ {
+ (yyval.list) = constiter((yyvsp[(1) - (4)].list), (yyvsp[(2) - (4)].node), (yyvsp[(4) - (4)].list));
+ }
+ break;
+
+ case 44:
+
+/* Line 1806 of yacc.c */
+#line 374 "go.y"
+ {
+ (yyval.list) = constiter((yyvsp[(1) - (3)].list), N, (yyvsp[(3) - (3)].list));
+ }
+ break;
+
+ case 46:
+
+/* Line 1806 of yacc.c */
+#line 381 "go.y"
+ {
+ (yyval.list) = constiter((yyvsp[(1) - (2)].list), (yyvsp[(2) - (2)].node), nil);
+ }
+ break;
+
+ case 47:
+
+/* Line 1806 of yacc.c */
+#line 385 "go.y"
+ {
+ (yyval.list) = constiter((yyvsp[(1) - (1)].list), N, nil);
+ }
+ break;
+
+ case 48:
+
+/* Line 1806 of yacc.c */
+#line 391 "go.y"
+ {
+ // different from dclname because the name
+ // becomes visible right here, not at the end
+ // of the declaration.
+ (yyval.node) = typedcl0((yyvsp[(1) - (1)].sym));
+ }
+ break;
+
+ case 49:
+
+/* Line 1806 of yacc.c */
+#line 400 "go.y"
+ {
+ (yyval.node) = typedcl1((yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node), 1);
+ }
+ break;
+
+ case 50:
+
+/* Line 1806 of yacc.c */
+#line 406 "go.y"
+ {
+ (yyval.node) = (yyvsp[(1) - (1)].node);
+ }
+ break;
+
+ case 51:
+
+/* Line 1806 of yacc.c */
+#line 410 "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
+ }
+ break;
+
+ case 52:
+
+/* Line 1806 of yacc.c */
+#line 415 "go.y"
+ {
+ if((yyvsp[(1) - (3)].list)->next == nil && (yyvsp[(3) - (3)].list)->next == nil) {
+ // simple
+ (yyval.node) = nod(OAS, (yyvsp[(1) - (3)].list)->n, (yyvsp[(3) - (3)].list)->n);
+ break;
+ }
+ // multiple
+ (yyval.node) = nod(OAS2, N, N);
+ (yyval.node)->list = (yyvsp[(1) - (3)].list);
+ (yyval.node)->rlist = (yyvsp[(3) - (3)].list);
+ }
+ break;
+
+ case 53:
+
+/* Line 1806 of yacc.c */
+#line 427 "go.y"
+ {
+ if((yyvsp[(3) - (3)].list)->n->op == OTYPESW) {
+ (yyval.node) = nod(OTYPESW, N, (yyvsp[(3) - (3)].list)->n->right);
+ if((yyvsp[(3) - (3)].list)->next != nil)
+ yyerror("expr.(type) must be alone in list");
+ if((yyvsp[(1) - (3)].list)->next != nil)
+ yyerror("argument count mismatch: %d = %d", count((yyvsp[(1) - (3)].list)), 1);
+ else if(((yyvsp[(1) - (3)].list)->n->op != ONAME && (yyvsp[(1) - (3)].list)->n->op != OTYPE && (yyvsp[(1) - (3)].list)->n->op != ONONAME) || isblank((yyvsp[(1) - (3)].list)->n))
+ yyerror("invalid variable name %N in type switch", (yyvsp[(1) - (3)].list)->n);
+ else
+ (yyval.node)->left = dclname((yyvsp[(1) - (3)].list)->n->sym); // it's a colas, so must not re-use an oldname.
+ break;
+ }
+ (yyval.node) = colas((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].list));
+ }
+ break;
+
+ case 54:
+
+/* Line 1806 of yacc.c */
+#line 443 "go.y"
+ {
+ (yyval.node) = nod(OASOP, (yyvsp[(1) - (2)].node), nodintconst(1));
+ (yyval.node)->etype = OADD;
+ }
+ break;
+
+ case 55:
+
+/* Line 1806 of yacc.c */
+#line 448 "go.y"
+ {
+ (yyval.node) = nod(OASOP, (yyvsp[(1) - (2)].node), nodintconst(1));
+ (yyval.node)->etype = OSUB;
+ }
+ break;
+
+ case 56:
+
+/* Line 1806 of yacc.c */
+#line 455 "go.y"
+ {
+ Node *n, *nn;
+
+ // will be converted to OCASE
+ // right will point to next case
+ // done in casebody()
+ markdcl();
+ (yyval.node) = nod(OXCASE, N, N);
+ (yyval.node)->list = (yyvsp[(2) - (3)].list);
+ if(typesw != N && typesw->right != N && (n=typesw->right->left) != N) {
+ // type switch - declare variable
+ nn = newname(n->sym);
+ declare(nn, dclcontext);
+ (yyval.node)->nname = nn;
+
+ // keep track of the instances for reporting unused
+ nn->defn = typesw->right;
+ }
+ }
+ break;
+
+ case 57:
+
+/* Line 1806 of yacc.c */
+#line 475 "go.y"
+ {
+ Node *n;
+
+ // will be converted to OCASE
+ // right will point to next case
+ // done in casebody()
+ markdcl();
+ (yyval.node) = nod(OXCASE, N, N);
+ if((yyvsp[(2) - (5)].list)->next == nil)
+ n = nod(OAS, (yyvsp[(2) - (5)].list)->n, (yyvsp[(4) - (5)].node));
+ else {
+ n = nod(OAS2, N, N);
+ n->list = (yyvsp[(2) - (5)].list);
+ n->rlist = list1((yyvsp[(4) - (5)].node));
+ }
+ (yyval.node)->list = list1(n);
+ }
+ break;
+
+ case 58:
+
+/* Line 1806 of yacc.c */
+#line 493 "go.y"
+ {
+ // will be converted to OCASE
+ // right will point to next case
+ // done in casebody()
+ markdcl();
+ (yyval.node) = nod(OXCASE, N, N);
+ (yyval.node)->list = list1(colas((yyvsp[(2) - (5)].list), list1((yyvsp[(4) - (5)].node))));
+ }
+ break;
+
+ case 59:
+
+/* Line 1806 of yacc.c */
+#line 502 "go.y"
+ {
+ Node *n, *nn;
+
+ markdcl();
+ (yyval.node) = nod(OXCASE, N, N);
+ if(typesw != N && typesw->right != N && (n=typesw->right->left) != N) {
+ // type switch - declare variable
+ nn = newname(n->sym);
+ declare(nn, dclcontext);
+ (yyval.node)->nname = nn;
+
+ // keep track of the instances for reporting unused
+ nn->defn = typesw->right;
+ }
+ }
+ break;
+
+ case 60:
+
+/* Line 1806 of yacc.c */
+#line 520 "go.y"
+ {
+ markdcl();
+ }
+ break;
+
+ case 61:
+
+/* Line 1806 of yacc.c */
+#line 524 "go.y"
+ {
+ (yyval.node) = liststmt((yyvsp[(3) - (4)].list));
+ popdcl();
+ }
+ break;
+
+ case 62:
+
+/* Line 1806 of yacc.c */
+#line 531 "go.y"
+ {
+ // If the last token read by the lexer was consumed
+ // as part of the case, clear it (parser has cleared yychar).
+ // If the last token read by the lexer was the lookahead
+ // leave it alone (parser has it cached in yychar).
+ // This is so that the stmt_list action doesn't look at
+ // the case tokens if the stmt_list is empty.
+ yylast = yychar;
+ }
+ break;
+
+ case 63:
+
+/* Line 1806 of yacc.c */
+#line 541 "go.y"
+ {
+ int last;
+
+ // This is the only place in the language where a statement
+ // list is not allowed to drop the final semicolon, because
+ // it's the only place where a statement list is not followed
+ // by a closing brace. Handle the error for pedantry.
+
+ // Find the final token of the statement list.
+ // yylast is lookahead; yyprev is last of stmt_list
+ last = yyprev;
+
+ if(last > 0 && last != ';' && yychar != '}')
+ yyerror("missing statement after label");
+ (yyval.node) = (yyvsp[(1) - (3)].node);
+ (yyval.node)->nbody = (yyvsp[(3) - (3)].list);
+ popdcl();
+ }
+ break;
+
+ case 64:
+
+/* Line 1806 of yacc.c */
+#line 561 "go.y"
+ {
+ (yyval.list) = nil;
+ }
+ break;
+
+ case 65:
+
+/* Line 1806 of yacc.c */
+#line 565 "go.y"
+ {
+ (yyval.list) = list((yyvsp[(1) - (2)].list), (yyvsp[(2) - (2)].node));
+ }
+ break;
+
+ case 66:
+
+/* Line 1806 of yacc.c */
+#line 571 "go.y"
+ {
+ markdcl();
+ }
+ break;
+
+ case 67:
+
+/* Line 1806 of yacc.c */
+#line 575 "go.y"
+ {
+ (yyval.list) = (yyvsp[(3) - (4)].list);
+ popdcl();
+ }
+ break;
+
+ case 68:
+
+/* Line 1806 of yacc.c */
+#line 582 "go.y"
+ {
+ (yyval.node) = nod(ORANGE, N, (yyvsp[(4) - (4)].node));
+ (yyval.node)->list = (yyvsp[(1) - (4)].list);
+ (yyval.node)->etype = 0; // := flag
+ }
+ break;
+
+ case 69:
+
+/* Line 1806 of yacc.c */
+#line 588 "go.y"
+ {
+ (yyval.node) = nod(ORANGE, N, (yyvsp[(4) - (4)].node));
+ (yyval.node)->list = (yyvsp[(1) - (4)].list);
+ (yyval.node)->colas = 1;
+ colasdefn((yyvsp[(1) - (4)].list), (yyval.node));
+ }
+ break;
+
+ case 70:
+
+/* Line 1806 of yacc.c */
+#line 597 "go.y"
+ {
+ // init ; test ; incr
+ if((yyvsp[(5) - (5)].node) != N && (yyvsp[(5) - (5)].node)->colas != 0)
+ yyerror("cannot declare in the for-increment");
+ (yyval.node) = nod(OFOR, N, N);
+ if((yyvsp[(1) - (5)].node) != N)
+ (yyval.node)->ninit = list1((yyvsp[(1) - (5)].node));
+ (yyval.node)->ntest = (yyvsp[(3) - (5)].node);
+ (yyval.node)->nincr = (yyvsp[(5) - (5)].node);
+ }
+ break;
+
+ case 71:
+
+/* Line 1806 of yacc.c */
+#line 608 "go.y"
+ {
+ // normal test
+ (yyval.node) = nod(OFOR, N, N);
+ (yyval.node)->ntest = (yyvsp[(1) - (1)].node);
+ }
+ break;
+
+ case 73:
+
+/* Line 1806 of yacc.c */
+#line 617 "go.y"
+ {
+ (yyval.node) = (yyvsp[(1) - (2)].node);
+ (yyval.node)->nbody = concat((yyval.node)->nbody, (yyvsp[(2) - (2)].list));
+ }
+ break;
+
+ case 74:
+
+/* Line 1806 of yacc.c */
+#line 624 "go.y"
+ {
+ markdcl();
+ }
+ break;
+
+ case 75:
+
+/* Line 1806 of yacc.c */
+#line 628 "go.y"
+ {
+ (yyval.node) = (yyvsp[(3) - (3)].node);
+ popdcl();
+ }
+ break;
+
+ case 76:
+
+/* Line 1806 of yacc.c */
+#line 635 "go.y"
+ {
+ // test
+ (yyval.node) = nod(OIF, N, N);
+ (yyval.node)->ntest = (yyvsp[(1) - (1)].node);
+ }
+ break;
+
+ case 77:
+
+/* Line 1806 of yacc.c */
+#line 641 "go.y"
+ {
+ // init ; test
+ (yyval.node) = nod(OIF, N, N);
+ if((yyvsp[(1) - (3)].node) != N)
+ (yyval.node)->ninit = list1((yyvsp[(1) - (3)].node));
+ (yyval.node)->ntest = (yyvsp[(3) - (3)].node);
+ }
+ break;
+
+ case 78:
+
+/* Line 1806 of yacc.c */
+#line 652 "go.y"
+ {
+ markdcl();
+ }
+ break;
+
+ case 79:
+
+/* Line 1806 of yacc.c */
+#line 656 "go.y"
+ {
+ if((yyvsp[(3) - (3)].node)->ntest == N)
+ yyerror("missing condition in if statement");
+ }
+ break;
+
+ case 80:
+
+/* Line 1806 of yacc.c */
+#line 661 "go.y"
+ {
+ (yyvsp[(3) - (5)].node)->nbody = (yyvsp[(5) - (5)].list);
+ }
+ break;
+
+ case 81:
+
+/* Line 1806 of yacc.c */
+#line 665 "go.y"
+ {
+ popdcl();
+ (yyval.node) = (yyvsp[(3) - (7)].node);
+ if((yyvsp[(7) - (7)].node) != N)
+ (yyval.node)->nelse = list1((yyvsp[(7) - (7)].node));
+ }
+ break;
+
+ case 82:
+
+/* Line 1806 of yacc.c */
+#line 673 "go.y"
+ {
+ (yyval.node) = N;
+ }
+ break;
+
+ case 83:
+
+/* Line 1806 of yacc.c */
+#line 677 "go.y"
+ {
+ (yyval.node) = (yyvsp[(2) - (2)].node);
+ }
+ break;
+
+ case 84:
+
+/* Line 1806 of yacc.c */
+#line 681 "go.y"
+ {
+ (yyval.node) = (yyvsp[(2) - (2)].node);
+ }
+ break;
+
+ case 85:
+
+/* Line 1806 of yacc.c */
+#line 687 "go.y"
+ {
+ markdcl();
+ }
+ break;
+
+ case 86:
+
+/* Line 1806 of yacc.c */
+#line 691 "go.y"
+ {
+ Node *n;
+ n = (yyvsp[(3) - (3)].node)->ntest;
+ if(n != N && n->op != OTYPESW)
+ n = N;
+ typesw = nod(OXXX, typesw, n);
+ }
+ break;
+
+ case 87:
+
+/* Line 1806 of yacc.c */
+#line 699 "go.y"
+ {
+ (yyval.node) = (yyvsp[(3) - (7)].node);
+ (yyval.node)->op = OSWITCH;
+ (yyval.node)->list = (yyvsp[(6) - (7)].list);
+ typesw = typesw->left;
+ popdcl();
+ }
+ break;
+
+ case 88:
+
+/* Line 1806 of yacc.c */
+#line 709 "go.y"
+ {
+ typesw = nod(OXXX, typesw, N);
+ }
+ break;
+
+ case 89:
+
+/* Line 1806 of yacc.c */
+#line 713 "go.y"
+ {
+ (yyval.node) = nod(OSELECT, N, N);
+ (yyval.node)->lineno = typesw->lineno;
+ (yyval.node)->list = (yyvsp[(4) - (5)].list);
+ typesw = typesw->left;
+ }
+ break;
+
+ case 91:
+
+/* Line 1806 of yacc.c */
+#line 726 "go.y"
+ {
+ (yyval.node) = nod(OOROR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 92:
+
+/* Line 1806 of yacc.c */
+#line 730 "go.y"
+ {
+ (yyval.node) = nod(OANDAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 93:
+
+/* Line 1806 of yacc.c */
+#line 734 "go.y"
+ {
+ (yyval.node) = nod(OEQ, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 94:
+
+/* Line 1806 of yacc.c */
+#line 738 "go.y"
+ {
+ (yyval.node) = nod(ONE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 95:
+
+/* Line 1806 of yacc.c */
+#line 742 "go.y"
+ {
+ (yyval.node) = nod(OLT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 96:
+
+/* Line 1806 of yacc.c */
+#line 746 "go.y"
+ {
+ (yyval.node) = nod(OLE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 97:
+
+/* Line 1806 of yacc.c */
+#line 750 "go.y"
+ {
+ (yyval.node) = nod(OGE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 98:
+
+/* Line 1806 of yacc.c */
+#line 754 "go.y"
+ {
+ (yyval.node) = nod(OGT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 99:
+
+/* Line 1806 of yacc.c */
+#line 758 "go.y"
+ {
+ (yyval.node) = nod(OADD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 100:
+
+/* Line 1806 of yacc.c */
+#line 762 "go.y"
+ {
+ (yyval.node) = nod(OSUB, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 101:
+
+/* Line 1806 of yacc.c */
+#line 766 "go.y"
+ {
+ (yyval.node) = nod(OOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 102:
+
+/* Line 1806 of yacc.c */
+#line 770 "go.y"
+ {
+ (yyval.node) = nod(OXOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 103:
+
+/* Line 1806 of yacc.c */
+#line 774 "go.y"
+ {
+ (yyval.node) = nod(OMUL, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 104:
+
+/* Line 1806 of yacc.c */
+#line 778 "go.y"
+ {
+ (yyval.node) = nod(ODIV, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 105:
+
+/* Line 1806 of yacc.c */
+#line 782 "go.y"
+ {
+ (yyval.node) = nod(OMOD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 106:
+
+/* Line 1806 of yacc.c */
+#line 786 "go.y"
+ {
+ (yyval.node) = nod(OAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 107:
+
+/* Line 1806 of yacc.c */
+#line 790 "go.y"
+ {
+ (yyval.node) = nod(OANDNOT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 108:
+
+/* Line 1806 of yacc.c */
+#line 794 "go.y"
+ {
+ (yyval.node) = nod(OLSH, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 109:
+
+/* Line 1806 of yacc.c */
+#line 798 "go.y"
+ {
+ (yyval.node) = nod(ORSH, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 110:
+
+/* Line 1806 of yacc.c */
+#line 803 "go.y"
+ {
+ (yyval.node) = nod(OSEND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 112:
+
+/* Line 1806 of yacc.c */
+#line 810 "go.y"
+ {
+ (yyval.node) = nod(OIND, (yyvsp[(2) - (2)].node), N);
+ }
+ break;
+
+ case 113:
+
+/* Line 1806 of yacc.c */
+#line 814 "go.y"
+ {
+ if((yyvsp[(2) - (2)].node)->op == OCOMPLIT) {
+ // Special case for &T{...}: turn into (*T){...}.
+ (yyval.node) = (yyvsp[(2) - (2)].node);
+ (yyval.node)->right = nod(OIND, (yyval.node)->right, N);
+ (yyval.node)->right->implicit = 1;
+ } else {
+ (yyval.node) = nod(OADDR, (yyvsp[(2) - (2)].node), N);
+ }
+ }
+ break;
+
+ case 114:
+
+/* Line 1806 of yacc.c */
+#line 825 "go.y"
+ {
+ (yyval.node) = nod(OPLUS, (yyvsp[(2) - (2)].node), N);
+ }
+ break;
+
+ case 115:
+
+/* Line 1806 of yacc.c */
+#line 829 "go.y"
+ {
+ (yyval.node) = nod(OMINUS, (yyvsp[(2) - (2)].node), N);
+ }
+ break;
+
+ case 116:
+
+/* Line 1806 of yacc.c */
+#line 833 "go.y"
+ {
+ (yyval.node) = nod(ONOT, (yyvsp[(2) - (2)].node), N);
+ }
+ break;
+
+ case 117:
+
+/* Line 1806 of yacc.c */
+#line 837 "go.y"
+ {
+ yyerror("the bitwise complement operator is ^");
+ (yyval.node) = nod(OCOM, (yyvsp[(2) - (2)].node), N);
+ }
+ break;
+
+ case 118:
+
+/* Line 1806 of yacc.c */
+#line 842 "go.y"
+ {
+ (yyval.node) = nod(OCOM, (yyvsp[(2) - (2)].node), N);
+ }
+ break;
+
+ case 119:
+
+/* Line 1806 of yacc.c */
+#line 846 "go.y"
+ {
+ (yyval.node) = nod(ORECV, (yyvsp[(2) - (2)].node), N);
+ }
+ break;
+
+ case 120:
+
+/* Line 1806 of yacc.c */
+#line 856 "go.y"
+ {
+ (yyval.node) = nod(OCALL, (yyvsp[(1) - (3)].node), N);
+ }
+ break;
+
+ case 121:
+
+/* Line 1806 of yacc.c */
+#line 860 "go.y"
+ {
+ (yyval.node) = nod(OCALL, (yyvsp[(1) - (5)].node), N);
+ (yyval.node)->list = (yyvsp[(3) - (5)].list);
+ }
+ break;
+
+ case 122:
+
+/* Line 1806 of yacc.c */
+#line 865 "go.y"
+ {
+ (yyval.node) = nod(OCALL, (yyvsp[(1) - (6)].node), N);
+ (yyval.node)->list = (yyvsp[(3) - (6)].list);
+ (yyval.node)->isddd = 1;
+ }
+ break;
+
+ case 123:
+
+/* Line 1806 of yacc.c */
+#line 873 "go.y"
+ {
+ (yyval.node) = nodlit((yyvsp[(1) - (1)].val));
+ }
+ break;
+
+ case 125:
+
+/* Line 1806 of yacc.c */
+#line 878 "go.y"
+ {
+ if((yyvsp[(1) - (3)].node)->op == OPACK) {
+ Sym *s;
+ s = restrictlookup((yyvsp[(3) - (3)].sym)->name, (yyvsp[(1) - (3)].node)->pkg);
+ (yyvsp[(1) - (3)].node)->used = 1;
+ (yyval.node) = oldname(s);
+ break;
+ }
+ (yyval.node) = nod(OXDOT, (yyvsp[(1) - (3)].node), newname((yyvsp[(3) - (3)].sym)));
+ }
+ break;
+
+ case 126:
+
+/* Line 1806 of yacc.c */
+#line 889 "go.y"
+ {
+ (yyval.node) = nod(ODOTTYPE, (yyvsp[(1) - (5)].node), (yyvsp[(4) - (5)].node));
+ }
+ break;
+
+ case 127:
+
+/* Line 1806 of yacc.c */
+#line 893 "go.y"
+ {
+ (yyval.node) = nod(OTYPESW, N, (yyvsp[(1) - (5)].node));
+ }
+ break;
+
+ case 128:
+
+/* Line 1806 of yacc.c */
+#line 897 "go.y"
+ {
+ (yyval.node) = nod(OINDEX, (yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].node));
+ }
+ break;
+
+ case 129:
+
+/* Line 1806 of yacc.c */
+#line 901 "go.y"
+ {
+ (yyval.node) = nod(OSLICE, (yyvsp[(1) - (6)].node), nod(OKEY, (yyvsp[(3) - (6)].node), (yyvsp[(5) - (6)].node)));
+ }
+ break;
+
+ case 131:
+
+/* Line 1806 of yacc.c */
+#line 906 "go.y"
+ {
+ // conversion
+ (yyval.node) = nod(OCALL, (yyvsp[(1) - (4)].node), N);
+ (yyval.node)->list = list1((yyvsp[(3) - (4)].node));
+ }
+ break;
+
+ case 132:
+
+/* Line 1806 of yacc.c */
+#line 912 "go.y"
+ {
+ (yyval.node) = (yyvsp[(3) - (5)].node);
+ (yyval.node)->right = (yyvsp[(1) - (5)].node);
+ (yyval.node)->list = (yyvsp[(4) - (5)].list);
+ fixlbrace((yyvsp[(2) - (5)].i));
+ }
+ break;
+
+ case 133:
+
+/* Line 1806 of yacc.c */
+#line 919 "go.y"
+ {
+ (yyval.node) = (yyvsp[(3) - (5)].node);
+ (yyval.node)->right = (yyvsp[(1) - (5)].node);
+ (yyval.node)->list = (yyvsp[(4) - (5)].list);
+ }
+ break;
+
+ case 134:
+
+/* Line 1806 of yacc.c */
+#line 925 "go.y"
+ {
+ yyerror("cannot parenthesize type in composite literal");
+ (yyval.node) = (yyvsp[(5) - (7)].node);
+ (yyval.node)->right = (yyvsp[(2) - (7)].node);
+ (yyval.node)->list = (yyvsp[(6) - (7)].list);
+ }
+ break;
+
+ case 136:
+
+/* Line 1806 of yacc.c */
+#line 934 "go.y"
+ {
+ // composite expression.
+ // make node early so we get the right line number.
+ (yyval.node) = nod(OCOMPLIT, N, N);
+ }
+ break;
+
+ case 137:
+
+/* Line 1806 of yacc.c */
+#line 942 "go.y"
+ {
+ (yyval.node) = nod(OKEY, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 139:
+
+/* Line 1806 of yacc.c */
+#line 949 "go.y"
+ {
+ (yyval.node) = (yyvsp[(2) - (4)].node);
+ (yyval.node)->list = (yyvsp[(3) - (4)].list);
+ }
+ break;
+
+ case 141:
+
+/* Line 1806 of yacc.c */
+#line 957 "go.y"
+ {
+ (yyval.node) = (yyvsp[(2) - (3)].node);
+
+ // Need to know on lhs of := whether there are ( ).
+ // Don't bother with the OPAREN in other cases:
+ // it's just a waste of memory and time.
+ switch((yyval.node)->op) {
+ case ONAME:
+ case ONONAME:
+ case OPACK:
+ case OTYPE:
+ case OLITERAL:
+ (yyval.node) = nod(OPAREN, (yyval.node), N);
+ }
+ }
+ break;
+
+ case 145:
+
+/* Line 1806 of yacc.c */
+#line 982 "go.y"
+ {
+ (yyval.i) = LBODY;
+ }
+ break;
+
+ case 146:
+
+/* Line 1806 of yacc.c */
+#line 986 "go.y"
+ {
+ (yyval.i) = '{';
+ }
+ break;
+
+ case 147:
+
+/* Line 1806 of yacc.c */
+#line 997 "go.y"
+ {
+ if((yyvsp[(1) - (1)].sym) == S)
+ (yyval.node) = N;
+ else
+ (yyval.node) = newname((yyvsp[(1) - (1)].sym));
+ }
+ break;
+
+ case 148:
+
+/* Line 1806 of yacc.c */
+#line 1006 "go.y"
+ {
+ (yyval.node) = dclname((yyvsp[(1) - (1)].sym));
+ }
+ break;
+
+ case 149:
+
+/* Line 1806 of yacc.c */
+#line 1011 "go.y"
+ {
+ (yyval.node) = N;
+ }
+ break;
+
+ case 151:
+
+/* Line 1806 of yacc.c */
+#line 1018 "go.y"
+ {
+ (yyval.sym) = (yyvsp[(1) - (1)].sym);
+ // during imports, unqualified non-exported identifiers are from builtinpkg
+ if(importpkg != nil && !exportname((yyvsp[(1) - (1)].sym)->name))
+ (yyval.sym) = pkglookup((yyvsp[(1) - (1)].sym)->name, builtinpkg);
+ }
+ break;
+
+ case 153:
+
+/* Line 1806 of yacc.c */
+#line 1026 "go.y"
+ {
+ (yyval.sym) = S;
+ }
+ break;
+
+ case 154:
+
+/* Line 1806 of yacc.c */
+#line 1032 "go.y"
+ {
+ if((yyvsp[(2) - (4)].val).u.sval->len == 0)
+ (yyval.sym) = pkglookup((yyvsp[(4) - (4)].sym)->name, importpkg);
+ else
+ (yyval.sym) = pkglookup((yyvsp[(4) - (4)].sym)->name, mkpkg((yyvsp[(2) - (4)].val).u.sval));
+ }
+ break;
+
+ case 155:
+
+/* Line 1806 of yacc.c */
+#line 1041 "go.y"
+ {
+ (yyval.node) = oldname((yyvsp[(1) - (1)].sym));
+ if((yyval.node)->pack != N)
+ (yyval.node)->pack->used = 1;
+ }
+ break;
+
+ case 157:
+
+/* Line 1806 of yacc.c */
+#line 1061 "go.y"
+ {
+ yyerror("final argument in variadic function missing type");
+ (yyval.node) = nod(ODDD, typenod(typ(TINTER)), N);
+ }
+ break;
+
+ case 158:
+
+/* Line 1806 of yacc.c */
+#line 1066 "go.y"
+ {
+ (yyval.node) = nod(ODDD, (yyvsp[(2) - (2)].node), N);
+ }
+ break;
+
+ case 164:
+
+/* Line 1806 of yacc.c */
+#line 1077 "go.y"
+ {
+ (yyval.node) = nod(OTPAREN, (yyvsp[(2) - (3)].node), N);
+ }
+ break;
+
+ case 168:
+
+/* Line 1806 of yacc.c */
+#line 1086 "go.y"
+ {
+ (yyval.node) = nod(OIND, (yyvsp[(2) - (2)].node), N);
+ }
+ break;
+
+ case 173:
+
+/* Line 1806 of yacc.c */
+#line 1096 "go.y"
+ {
+ (yyval.node) = nod(OTPAREN, (yyvsp[(2) - (3)].node), N);
+ }
+ break;
+
+ case 183:
+
+/* Line 1806 of yacc.c */
+#line 1117 "go.y"
+ {
+ if((yyvsp[(1) - (3)].node)->op == OPACK) {
+ Sym *s;
+ s = restrictlookup((yyvsp[(3) - (3)].sym)->name, (yyvsp[(1) - (3)].node)->pkg);
+ (yyvsp[(1) - (3)].node)->used = 1;
+ (yyval.node) = oldname(s);
+ break;
+ }
+ (yyval.node) = nod(OXDOT, (yyvsp[(1) - (3)].node), newname((yyvsp[(3) - (3)].sym)));
+ }
+ break;
+
+ case 184:
+
+/* Line 1806 of yacc.c */
+#line 1130 "go.y"
+ {
+ (yyval.node) = nod(OTARRAY, (yyvsp[(2) - (4)].node), (yyvsp[(4) - (4)].node));
+ }
+ break;
+
+ case 185:
+
+/* Line 1806 of yacc.c */
+#line 1134 "go.y"
+ {
+ // array literal of nelem
+ (yyval.node) = nod(OTARRAY, nod(ODDD, N, N), (yyvsp[(4) - (4)].node));
+ }
+ break;
+
+ case 186:
+
+/* Line 1806 of yacc.c */
+#line 1139 "go.y"
+ {
+ (yyval.node) = nod(OTCHAN, (yyvsp[(2) - (2)].node), N);
+ (yyval.node)->etype = Cboth;
+ }
+ break;
+
+ case 187:
+
+/* Line 1806 of yacc.c */
+#line 1144 "go.y"
+ {
+ (yyval.node) = nod(OTCHAN, (yyvsp[(3) - (3)].node), N);
+ (yyval.node)->etype = Csend;
+ }
+ break;
+
+ case 188:
+
+/* Line 1806 of yacc.c */
+#line 1149 "go.y"
+ {
+ (yyval.node) = nod(OTMAP, (yyvsp[(3) - (5)].node), (yyvsp[(5) - (5)].node));
+ }
+ break;
+
+ case 191:
+
+/* Line 1806 of yacc.c */
+#line 1157 "go.y"
+ {
+ (yyval.node) = nod(OIND, (yyvsp[(2) - (2)].node), N);
+ }
+ break;
+
+ case 192:
+
+/* Line 1806 of yacc.c */
+#line 1163 "go.y"
+ {
+ (yyval.node) = nod(OTCHAN, (yyvsp[(3) - (3)].node), N);
+ (yyval.node)->etype = Crecv;
+ }
+ break;
+
+ case 193:
+
+/* Line 1806 of yacc.c */
+#line 1170 "go.y"
+ {
+ (yyval.node) = nod(OTSTRUCT, N, N);
+ (yyval.node)->list = (yyvsp[(3) - (5)].list);
+ fixlbrace((yyvsp[(2) - (5)].i));
+ }
+ break;
+
+ case 194:
+
+/* Line 1806 of yacc.c */
+#line 1176 "go.y"
+ {
+ (yyval.node) = nod(OTSTRUCT, N, N);
+ fixlbrace((yyvsp[(2) - (3)].i));
+ }
+ break;
+
+ case 195:
+
+/* Line 1806 of yacc.c */
+#line 1183 "go.y"
+ {
+ (yyval.node) = nod(OTINTER, N, N);
+ (yyval.node)->list = (yyvsp[(3) - (5)].list);
+ fixlbrace((yyvsp[(2) - (5)].i));
+ }
+ break;
+
+ case 196:
+
+/* Line 1806 of yacc.c */
+#line 1189 "go.y"
+ {
+ (yyval.node) = nod(OTINTER, N, N);
+ fixlbrace((yyvsp[(2) - (3)].i));
+ }
+ break;
+
+ case 197:
+
+/* Line 1806 of yacc.c */
+#line 1200 "go.y"
+ {
+ (yyval.node) = (yyvsp[(2) - (3)].node);
+ if((yyval.node) == N)
+ break;
+ (yyval.node)->nbody = (yyvsp[(3) - (3)].list);
+ (yyval.node)->endlineno = lineno;
+ funcbody((yyval.node));
+ }
+ break;
+
+ case 198:
+
+/* Line 1806 of yacc.c */
+#line 1211 "go.y"
+ {
+ Node *t;
+
+ (yyval.node) = N;
+ (yyvsp[(3) - (5)].list) = checkarglist((yyvsp[(3) - (5)].list), 1);
+
+ if(strcmp((yyvsp[(1) - (5)].sym)->name, "init") == 0) {
+ (yyvsp[(1) - (5)].sym) = renameinit();
+ if((yyvsp[(3) - (5)].list) != nil || (yyvsp[(5) - (5)].list) != nil)
+ yyerror("func init must have no arguments and no return values");
+ }
+ if(strcmp(localpkg->name, "main") == 0 && strcmp((yyvsp[(1) - (5)].sym)->name, "main") == 0) {
+ if((yyvsp[(3) - (5)].list) != nil || (yyvsp[(5) - (5)].list) != nil)
+ yyerror("func main must have no arguments and no return values");
+ }
+
+ t = nod(OTFUNC, N, N);
+ t->list = (yyvsp[(3) - (5)].list);
+ t->rlist = (yyvsp[(5) - (5)].list);
+
+ (yyval.node) = nod(ODCLFUNC, N, N);
+ (yyval.node)->nname = newname((yyvsp[(1) - (5)].sym));
+ (yyval.node)->nname->defn = (yyval.node);
+ (yyval.node)->nname->ntype = t; // TODO: check if nname already has an ntype
+ declare((yyval.node)->nname, PFUNC);
+
+ funchdr((yyval.node));
+ }
+ break;
+
+ case 199:
+
+/* Line 1806 of yacc.c */
+#line 1240 "go.y"
+ {
+ Node *rcvr, *t;
+
+ (yyval.node) = N;
+ (yyvsp[(2) - (8)].list) = checkarglist((yyvsp[(2) - (8)].list), 0);
+ (yyvsp[(6) - (8)].list) = checkarglist((yyvsp[(6) - (8)].list), 1);
+
+ if((yyvsp[(2) - (8)].list) == nil) {
+ yyerror("method has no receiver");
+ break;
+ }
+ if((yyvsp[(2) - (8)].list)->next != nil) {
+ yyerror("method has multiple receivers");
+ break;
+ }
+ rcvr = (yyvsp[(2) - (8)].list)->n;
+ if(rcvr->op != ODCLFIELD) {
+ yyerror("bad receiver in method");
+ break;
+ }
+ if(rcvr->right->op == OTPAREN || (rcvr->right->op == OIND && rcvr->right->left->op == OTPAREN))
+ yyerror("cannot parenthesize receiver type");
+
+ t = nod(OTFUNC, rcvr, N);
+ t->list = (yyvsp[(6) - (8)].list);
+ t->rlist = (yyvsp[(8) - (8)].list);
+
+ (yyval.node) = nod(ODCLFUNC, N, N);
+ (yyval.node)->shortname = newname((yyvsp[(4) - (8)].sym));
+ (yyval.node)->nname = methodname1((yyval.node)->shortname, rcvr->right);
+ (yyval.node)->nname->defn = (yyval.node);
+ (yyval.node)->nname->ntype = t;
+ declare((yyval.node)->nname, PFUNC);
+
+ funchdr((yyval.node));
+ }
+ break;
+
+ case 200:
+
+/* Line 1806 of yacc.c */
+#line 1279 "go.y"
+ {
+ Sym *s;
+ Type *t;
+
+ (yyval.node) = N;
+
+ s = (yyvsp[(1) - (5)].sym);
+ t = functype(N, (yyvsp[(3) - (5)].list), (yyvsp[(5) - (5)].list));
+
+ importsym(s, ONAME);
+ if(s->def != N && s->def->op == ONAME) {
+ if(eqtype(t, s->def->type))
+ break;
+ yyerror("inconsistent definition for func %S during import\n\t%T\n\t%T", s, s->def->type, t);
+ }
+
+ (yyval.node) = newname(s);
+ (yyval.node)->type = t;
+ declare((yyval.node), PFUNC);
+
+ funchdr((yyval.node));
+ }
+ break;
+
+ case 201:
+
+/* Line 1806 of yacc.c */
+#line 1302 "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));
+
+ checkwidth((yyval.node)->type);
+ addmethod((yyvsp[(4) - (8)].sym), (yyval.node)->type, 0);
+ funchdr((yyval.node));
+
+ // inl.c's inlnode in on a dotmeth node expects to find the inlineable body as
+ // (dotmeth's type)->nname->inl, and dotmeth's type has been pulled
+ // out by typecheck's lookdot as this $$->ttype. So by providing
+ // this back link here we avoid special casing there.
+ (yyval.node)->type->nname = (yyval.node);
+ }
+ break;
+
+ case 202:
+
+/* Line 1806 of yacc.c */
+#line 1319 "go.y"
+ {
+ (yyvsp[(3) - (5)].list) = checkarglist((yyvsp[(3) - (5)].list), 1);
+ (yyval.node) = nod(OTFUNC, N, N);
+ (yyval.node)->list = (yyvsp[(3) - (5)].list);
+ (yyval.node)->rlist = (yyvsp[(5) - (5)].list);
+ }
+ break;
+
+ case 203:
+
+/* Line 1806 of yacc.c */
+#line 1327 "go.y"
+ {
+ (yyval.list) = nil;
+ }
+ break;
+
+ case 204:
+
+/* Line 1806 of yacc.c */
+#line 1331 "go.y"
+ {
+ (yyval.list) = (yyvsp[(2) - (3)].list);
+ if((yyval.list) == nil)
+ (yyval.list) = list1(nod(OEMPTY, N, N));
+ }
+ break;
+
+ case 205:
+
+/* Line 1806 of yacc.c */
+#line 1339 "go.y"
+ {
+ (yyval.list) = nil;
+ }
+ break;
+
+ case 206:
+
+/* Line 1806 of yacc.c */
+#line 1343 "go.y"
+ {
+ (yyval.list) = list1(nod(ODCLFIELD, N, (yyvsp[(1) - (1)].node)));
+ }
+ break;
+
+ case 207:
+
+/* Line 1806 of yacc.c */
+#line 1347 "go.y"
+ {
+ (yyvsp[(2) - (3)].list) = checkarglist((yyvsp[(2) - (3)].list), 0);
+ (yyval.list) = (yyvsp[(2) - (3)].list);
+ }
+ break;
+
+ case 208:
+
+/* Line 1806 of yacc.c */
+#line 1354 "go.y"
+ {
+ closurehdr((yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 209:
+
+/* Line 1806 of yacc.c */
+#line 1360 "go.y"
+ {
+ (yyval.node) = closurebody((yyvsp[(3) - (4)].list));
+ fixlbrace((yyvsp[(2) - (4)].i));
+ }
+ break;
+
+ case 210:
+
+/* Line 1806 of yacc.c */
+#line 1365 "go.y"
+ {
+ (yyval.node) = closurebody(nil);
+ }
+ break;
+
+ case 211:
+
+/* Line 1806 of yacc.c */
+#line 1376 "go.y"
+ {
+ (yyval.list) = nil;
+ }
+ break;
+
+ case 212:
+
+/* Line 1806 of yacc.c */
+#line 1380 "go.y"
+ {
+ (yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(2) - (3)].list));
+ if(nsyntaxerrors == 0)
+ testdclstack();
+ }
+ break;
+
+ case 214:
+
+/* Line 1806 of yacc.c */
+#line 1389 "go.y"
+ {
+ (yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].list));
+ }
+ break;
+
+ case 216:
+
+/* Line 1806 of yacc.c */
+#line 1396 "go.y"
+ {
+ (yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].list));
+ }
+ break;
+
+ case 217:
+
+/* Line 1806 of yacc.c */
+#line 1402 "go.y"
+ {
+ (yyval.list) = list1((yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 218:
+
+/* Line 1806 of yacc.c */
+#line 1406 "go.y"
+ {
+ (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 220:
+
+/* Line 1806 of yacc.c */
+#line 1413 "go.y"
+ {
+ (yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].list));
+ }
+ break;
+
+ case 221:
+
+/* Line 1806 of yacc.c */
+#line 1419 "go.y"
+ {
+ (yyval.list) = list1((yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 222:
+
+/* Line 1806 of yacc.c */
+#line 1423 "go.y"
+ {
+ (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 223:
+
+/* Line 1806 of yacc.c */
+#line 1429 "go.y"
+ {
+ NodeList *l;
+
+ Node *n;
+ l = (yyvsp[(1) - (3)].list);
+ if(l != nil && l->next == nil && l->n == nil) {
+ // ? symbol, during import
+ n = (yyvsp[(2) - (3)].node);
+ if(n->op == OIND)
+ n = n->left;
+ n = embedded(n->sym);
+ n->right = (yyvsp[(2) - (3)].node);
+ n->val = (yyvsp[(3) - (3)].val);
+ (yyval.list) = list1(n);
+ break;
+ }
+
+ for(l=(yyvsp[(1) - (3)].list); l; l=l->next) {
+ l->n = nod(ODCLFIELD, l->n, (yyvsp[(2) - (3)].node));
+ l->n->val = (yyvsp[(3) - (3)].val);
+ }
+ }
+ break;
+
+ case 224:
+
+/* Line 1806 of yacc.c */
+#line 1452 "go.y"
+ {
+ (yyvsp[(1) - (2)].node)->val = (yyvsp[(2) - (2)].val);
+ (yyval.list) = list1((yyvsp[(1) - (2)].node));
+ }
+ break;
+
+ case 225:
+
+/* Line 1806 of yacc.c */
+#line 1457 "go.y"
+ {
+ (yyvsp[(2) - (4)].node)->val = (yyvsp[(4) - (4)].val);
+ (yyval.list) = list1((yyvsp[(2) - (4)].node));
+ yyerror("cannot parenthesize embedded type");
+ }
+ break;
+
+ case 226:
+
+/* Line 1806 of yacc.c */
+#line 1463 "go.y"
+ {
+ (yyvsp[(2) - (3)].node)->right = nod(OIND, (yyvsp[(2) - (3)].node)->right, N);
+ (yyvsp[(2) - (3)].node)->val = (yyvsp[(3) - (3)].val);
+ (yyval.list) = list1((yyvsp[(2) - (3)].node));
+ }
+ break;
+
+ case 227:
+
+/* Line 1806 of yacc.c */
+#line 1469 "go.y"
+ {
+ (yyvsp[(3) - (5)].node)->right = nod(OIND, (yyvsp[(3) - (5)].node)->right, N);
+ (yyvsp[(3) - (5)].node)->val = (yyvsp[(5) - (5)].val);
+ (yyval.list) = list1((yyvsp[(3) - (5)].node));
+ yyerror("cannot parenthesize embedded type");
+ }
+ break;
+
+ case 228:
+
+/* Line 1806 of yacc.c */
+#line 1476 "go.y"
+ {
+ (yyvsp[(3) - (5)].node)->right = nod(OIND, (yyvsp[(3) - (5)].node)->right, N);
+ (yyvsp[(3) - (5)].node)->val = (yyvsp[(5) - (5)].val);
+ (yyval.list) = list1((yyvsp[(3) - (5)].node));
+ yyerror("cannot parenthesize embedded type");
+ }
+ break;
+
+ case 229:
+
+/* Line 1806 of yacc.c */
+#line 1485 "go.y"
+ {
+ Node *n;
+
+ (yyval.sym) = (yyvsp[(1) - (1)].sym);
+ n = oldname((yyvsp[(1) - (1)].sym));
+ if(n->pack != N)
+ n->pack->used = 1;
+ }
+ break;
+
+ case 230:
+
+/* Line 1806 of yacc.c */
+#line 1494 "go.y"
+ {
+ Pkg *pkg;
+
+ if((yyvsp[(1) - (3)].sym)->def == N || (yyvsp[(1) - (3)].sym)->def->op != OPACK) {
+ yyerror("%S is not a package", (yyvsp[(1) - (3)].sym));
+ pkg = localpkg;
+ } else {
+ (yyvsp[(1) - (3)].sym)->def->used = 1;
+ pkg = (yyvsp[(1) - (3)].sym)->def->pkg;
+ }
+ (yyval.sym) = restrictlookup((yyvsp[(3) - (3)].sym)->name, pkg);
+ }
+ break;
+
+ case 231:
+
+/* Line 1806 of yacc.c */
+#line 1509 "go.y"
+ {
+ (yyval.node) = embedded((yyvsp[(1) - (1)].sym));
+ }
+ break;
+
+ case 232:
+
+/* Line 1806 of yacc.c */
+#line 1515 "go.y"
+ {
+ (yyval.node) = nod(ODCLFIELD, (yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node));
+ ifacedcl((yyval.node));
+ }
+ break;
+
+ case 233:
+
+/* Line 1806 of yacc.c */
+#line 1520 "go.y"
+ {
+ (yyval.node) = nod(ODCLFIELD, N, oldname((yyvsp[(1) - (1)].sym)));
+ }
+ break;
+
+ case 234:
+
+/* Line 1806 of yacc.c */
+#line 1524 "go.y"
+ {
+ (yyval.node) = nod(ODCLFIELD, N, oldname((yyvsp[(2) - (3)].sym)));
+ yyerror("cannot parenthesize embedded type");
+ }
+ break;
+
+ case 235:
+
+/* Line 1806 of yacc.c */
+#line 1531 "go.y"
+ {
+ // without func keyword
+ (yyvsp[(2) - (4)].list) = checkarglist((yyvsp[(2) - (4)].list), 1);
+ (yyval.node) = nod(OTFUNC, fakethis(), N);
+ (yyval.node)->list = (yyvsp[(2) - (4)].list);
+ (yyval.node)->rlist = (yyvsp[(4) - (4)].list);
+ }
+ break;
+
+ case 237:
+
+/* Line 1806 of yacc.c */
+#line 1545 "go.y"
+ {
+ (yyval.node) = nod(ONONAME, N, N);
+ (yyval.node)->sym = (yyvsp[(1) - (2)].sym);
+ (yyval.node) = nod(OKEY, (yyval.node), (yyvsp[(2) - (2)].node));
+ }
+ break;
+
+ case 238:
+
+/* Line 1806 of yacc.c */
+#line 1551 "go.y"
+ {
+ (yyval.node) = nod(ONONAME, N, N);
+ (yyval.node)->sym = (yyvsp[(1) - (2)].sym);
+ (yyval.node) = nod(OKEY, (yyval.node), (yyvsp[(2) - (2)].node));
+ }
+ break;
+
+ case 240:
+
+/* Line 1806 of yacc.c */
+#line 1560 "go.y"
+ {
+ (yyval.list) = list1((yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 241:
+
+/* Line 1806 of yacc.c */
+#line 1564 "go.y"
+ {
+ (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 242:
+
+/* Line 1806 of yacc.c */
+#line 1569 "go.y"
+ {
+ (yyval.list) = nil;
+ }
+ break;
+
+ case 243:
+
+/* Line 1806 of yacc.c */
+#line 1573 "go.y"
+ {
+ (yyval.list) = (yyvsp[(1) - (2)].list);
+ }
+ break;
+
+ case 244:
+
+/* Line 1806 of yacc.c */
+#line 1581 "go.y"
+ {
+ (yyval.node) = N;
+ }
+ break;
+
+ case 246:
+
+/* Line 1806 of yacc.c */
+#line 1586 "go.y"
+ {
+ (yyval.node) = liststmt((yyvsp[(1) - (1)].list));
+ }
+ break;
+
+ case 248:
+
+/* Line 1806 of yacc.c */
+#line 1591 "go.y"
+ {
+ (yyval.node) = N;
+ }
+ break;
+
+ case 254:
+
+/* Line 1806 of yacc.c */
+#line 1602 "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 255:
+
+/* Line 1806 of yacc.c */
+#line 1607 "go.y"
+ {
+ NodeList *l;
+
+ (yyvsp[(1) - (4)].node)->defn = (yyvsp[(4) - (4)].node);
+ l = list1((yyvsp[(1) - (4)].node));
+ if((yyvsp[(4) - (4)].node))
+ l = list(l, (yyvsp[(4) - (4)].node));
+ (yyval.node) = liststmt(l);
+ }
+ break;
+
+ case 256:
+
+/* Line 1806 of yacc.c */
+#line 1617 "go.y"
+ {
+ // will be converted to OFALL
+ (yyval.node) = nod(OXFALL, N, N);
+ }
+ break;
+
+ case 257:
+
+/* Line 1806 of yacc.c */
+#line 1622 "go.y"
+ {
+ (yyval.node) = nod(OBREAK, (yyvsp[(2) - (2)].node), N);
+ }
+ break;
+
+ case 258:
+
+/* Line 1806 of yacc.c */
+#line 1626 "go.y"
+ {
+ (yyval.node) = nod(OCONTINUE, (yyvsp[(2) - (2)].node), N);
+ }
+ break;
+
+ case 259:
+
+/* Line 1806 of yacc.c */
+#line 1630 "go.y"
+ {
+ (yyval.node) = nod(OPROC, (yyvsp[(2) - (2)].node), N);
+ }
+ break;
+
+ case 260:
+
+/* Line 1806 of yacc.c */
+#line 1634 "go.y"
+ {
+ (yyval.node) = nod(ODEFER, (yyvsp[(2) - (2)].node), N);
+ }
+ break;
+
+ case 261:
+
+/* Line 1806 of yacc.c */
+#line 1638 "go.y"
+ {
+ (yyval.node) = nod(OGOTO, (yyvsp[(2) - (2)].node), N);
+ (yyval.node)->sym = dclstack; // context, for goto restrictions
+ }
+ break;
+
+ case 262:
+
+/* Line 1806 of yacc.c */
+#line 1643 "go.y"
+ {
+ (yyval.node) = nod(ORETURN, N, N);
+ (yyval.node)->list = (yyvsp[(2) - (2)].list);
+ if((yyval.node)->list == nil && curfn != N) {
+ NodeList *l;
+
+ for(l=curfn->dcl; l; l=l->next) {
+ if(l->n->class == PPARAM)
+ continue;
+ if(l->n->class != PPARAMOUT)
+ break;
+ if(l->n->sym->def != l->n)
+ yyerror("%s is shadowed during return", l->n->sym->name);
+ }
+ }
+ }
+ break;
+
+ case 263:
+
+/* Line 1806 of yacc.c */
+#line 1662 "go.y"
+ {
+ (yyval.list) = nil;
+ if((yyvsp[(1) - (1)].node) != N)
+ (yyval.list) = list1((yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 264:
+
+/* Line 1806 of yacc.c */
+#line 1668 "go.y"
+ {
+ (yyval.list) = (yyvsp[(1) - (3)].list);
+ if((yyvsp[(3) - (3)].node) != N)
+ (yyval.list) = list((yyval.list), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 265:
+
+/* Line 1806 of yacc.c */
+#line 1676 "go.y"
+ {
+ (yyval.list) = list1((yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 266:
+
+/* Line 1806 of yacc.c */
+#line 1680 "go.y"
+ {
+ (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 267:
+
+/* Line 1806 of yacc.c */
+#line 1686 "go.y"
+ {
+ (yyval.list) = list1((yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 268:
+
+/* Line 1806 of yacc.c */
+#line 1690 "go.y"
+ {
+ (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 269:
+
+/* Line 1806 of yacc.c */
+#line 1696 "go.y"
+ {
+ (yyval.list) = list1((yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 270:
+
+/* Line 1806 of yacc.c */
+#line 1700 "go.y"
+ {
+ (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 271:
+
+/* Line 1806 of yacc.c */
+#line 1706 "go.y"
+ {
+ (yyval.list) = list1((yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 272:
+
+/* Line 1806 of yacc.c */
+#line 1710 "go.y"
+ {
+ (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 273:
+
+/* Line 1806 of yacc.c */
+#line 1719 "go.y"
+ {
+ (yyval.list) = list1((yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 274:
+
+/* Line 1806 of yacc.c */
+#line 1723 "go.y"
+ {
+ (yyval.list) = list1((yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 275:
+
+/* Line 1806 of yacc.c */
+#line 1727 "go.y"
+ {
+ (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 276:
+
+/* Line 1806 of yacc.c */
+#line 1731 "go.y"
+ {
+ (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 277:
+
+/* Line 1806 of yacc.c */
+#line 1736 "go.y"
+ {
+ (yyval.list) = nil;
+ }
+ break;
+
+ case 278:
+
+/* Line 1806 of yacc.c */
+#line 1740 "go.y"
+ {
+ (yyval.list) = (yyvsp[(1) - (2)].list);
+ }
+ break;
+
+ case 283:
+
+/* Line 1806 of yacc.c */
+#line 1754 "go.y"
+ {
+ (yyval.node) = N;
+ }
+ break;
+
+ case 285:
+
+/* Line 1806 of yacc.c */
+#line 1760 "go.y"
+ {
+ (yyval.list) = nil;
+ }
+ break;
+
+ case 287:
+
+/* Line 1806 of yacc.c */
+#line 1766 "go.y"
+ {
+ (yyval.node) = N;
+ }
+ break;
+
+ case 289:
+
+/* Line 1806 of yacc.c */
+#line 1772 "go.y"
+ {
+ (yyval.list) = nil;
+ }
+ break;
+
+ case 291:
+
+/* Line 1806 of yacc.c */
+#line 1778 "go.y"
+ {
+ (yyval.list) = nil;
+ }
+ break;
+
+ case 293:
+
+/* Line 1806 of yacc.c */
+#line 1784 "go.y"
+ {
+ (yyval.list) = nil;
+ }
+ break;
+
+ case 295:
+
+/* Line 1806 of yacc.c */
+#line 1790 "go.y"
+ {
+ (yyval.val).ctype = CTxxx;
+ }
+ break;
+
+ case 297:
+
+/* Line 1806 of yacc.c */
+#line 1800 "go.y"
+ {
+ importimport((yyvsp[(2) - (4)].sym), (yyvsp[(3) - (4)].val).u.sval);
+ }
+ break;
+
+ case 298:
+
+/* Line 1806 of yacc.c */
+#line 1804 "go.y"
+ {
+ importvar((yyvsp[(2) - (4)].sym), (yyvsp[(3) - (4)].type));
+ }
+ break;
+
+ case 299:
+
+/* Line 1806 of yacc.c */
+#line 1808 "go.y"
+ {
+ importconst((yyvsp[(2) - (5)].sym), types[TIDEAL], (yyvsp[(4) - (5)].node));
+ }
+ break;
+
+ case 300:
+
+/* Line 1806 of yacc.c */
+#line 1812 "go.y"
+ {
+ importconst((yyvsp[(2) - (6)].sym), (yyvsp[(3) - (6)].type), (yyvsp[(5) - (6)].node));
+ }
+ break;
+
+ case 301:
+
+/* Line 1806 of yacc.c */
+#line 1816 "go.y"
+ {
+ importtype((yyvsp[(2) - (4)].type), (yyvsp[(3) - (4)].type));
+ }
+ break;
+
+ case 302:
+
+/* Line 1806 of yacc.c */
+#line 1820 "go.y"
+ {
+ if((yyvsp[(2) - (4)].node) == N)
+ break;
+
+ (yyvsp[(2) - (4)].node)->inl = (yyvsp[(3) - (4)].list);
+
+ funcbody((yyvsp[(2) - (4)].node));
+ importlist = list(importlist, (yyvsp[(2) - (4)].node));
+
+ if(debug['E']) {
+ print("import [%Z] func %lN \n", importpkg->path, (yyvsp[(2) - (4)].node));
+ if(debug['l'] > 2 && (yyvsp[(2) - (4)].node)->inl)
+ print("inl body:%+H\n", (yyvsp[(2) - (4)].node)->inl);
+ }
+ }
+ break;
+
+ case 303:
+
+/* Line 1806 of yacc.c */
+#line 1838 "go.y"
+ {
+ (yyval.sym) = (yyvsp[(1) - (1)].sym);
+ structpkg = (yyval.sym)->pkg;
+ }
+ break;
+
+ case 304:
+
+/* Line 1806 of yacc.c */
+#line 1845 "go.y"
+ {
+ (yyval.type) = pkgtype((yyvsp[(1) - (1)].sym));
+ importsym((yyvsp[(1) - (1)].sym), OTYPE);
+ }
+ break;
+
+ case 310:
+
+/* Line 1806 of yacc.c */
+#line 1865 "go.y"
+ {
+ (yyval.type) = pkgtype((yyvsp[(1) - (1)].sym));
+ }
+ break;
+
+ case 311:
+
+/* Line 1806 of yacc.c */
+#line 1869 "go.y"
+ {
+ // predefined name like uint8
+ (yyvsp[(1) - (1)].sym) = pkglookup((yyvsp[(1) - (1)].sym)->name, builtinpkg);
+ if((yyvsp[(1) - (1)].sym)->def == N || (yyvsp[(1) - (1)].sym)->def->op != OTYPE) {
+ yyerror("%s is not a type", (yyvsp[(1) - (1)].sym)->name);
+ (yyval.type) = T;
+ } else
+ (yyval.type) = (yyvsp[(1) - (1)].sym)->def->type;
+ }
+ break;
+
+ case 312:
+
+/* Line 1806 of yacc.c */
+#line 1879 "go.y"
+ {
+ (yyval.type) = aindex(N, (yyvsp[(3) - (3)].type));
+ }
+ break;
+
+ case 313:
+
+/* Line 1806 of yacc.c */
+#line 1883 "go.y"
+ {
+ (yyval.type) = aindex(nodlit((yyvsp[(2) - (4)].val)), (yyvsp[(4) - (4)].type));
+ }
+ break;
+
+ case 314:
+
+/* Line 1806 of yacc.c */
+#line 1887 "go.y"
+ {
+ (yyval.type) = maptype((yyvsp[(3) - (5)].type), (yyvsp[(5) - (5)].type));
+ }
+ break;
+
+ case 315:
+
+/* Line 1806 of yacc.c */
+#line 1891 "go.y"
+ {
+ (yyval.type) = tostruct((yyvsp[(3) - (4)].list));
+ }
+ break;
+
+ case 316:
+
+/* Line 1806 of yacc.c */
+#line 1895 "go.y"
+ {
+ (yyval.type) = tointerface((yyvsp[(3) - (4)].list));
+ }
+ break;
+
+ case 317:
+
+/* Line 1806 of yacc.c */
+#line 1899 "go.y"
+ {
+ (yyval.type) = ptrto((yyvsp[(2) - (2)].type));
+ }
+ break;
+
+ case 318:
+
+/* Line 1806 of yacc.c */
+#line 1903 "go.y"
+ {
+ (yyval.type) = typ(TCHAN);
+ (yyval.type)->type = (yyvsp[(2) - (2)].type);
+ (yyval.type)->chan = Cboth;
+ }
+ break;
+
+ case 319:
+
+/* Line 1806 of yacc.c */
+#line 1909 "go.y"
+ {
+ (yyval.type) = typ(TCHAN);
+ (yyval.type)->type = (yyvsp[(3) - (4)].type);
+ (yyval.type)->chan = Cboth;
+ }
+ break;
+
+ case 320:
+
+/* Line 1806 of yacc.c */
+#line 1915 "go.y"
+ {
+ (yyval.type) = typ(TCHAN);
+ (yyval.type)->type = (yyvsp[(3) - (3)].type);
+ (yyval.type)->chan = Csend;
+ }
+ break;
+
+ case 321:
+
+/* Line 1806 of yacc.c */
+#line 1923 "go.y"
+ {
+ (yyval.type) = typ(TCHAN);
+ (yyval.type)->type = (yyvsp[(3) - (3)].type);
+ (yyval.type)->chan = Crecv;
+ }
+ break;
+
+ case 322:
+
+/* Line 1806 of yacc.c */
+#line 1931 "go.y"
+ {
+ (yyval.type) = functype(nil, (yyvsp[(3) - (5)].list), (yyvsp[(5) - (5)].list));
+ }
+ break;
+
+ case 323:
+
+/* Line 1806 of yacc.c */
+#line 1937 "go.y"
+ {
+ (yyval.node) = nod(ODCLFIELD, N, typenod((yyvsp[(2) - (3)].type)));
+ if((yyvsp[(1) - (3)].sym))
+ (yyval.node)->left = newname((yyvsp[(1) - (3)].sym));
+ (yyval.node)->val = (yyvsp[(3) - (3)].val);
+ }
+ break;
+
+ case 324:
+
+/* Line 1806 of yacc.c */
+#line 1944 "go.y"
+ {
+ Type *t;
+
+ t = typ(TARRAY);
+ t->bound = -1;
+ t->type = (yyvsp[(3) - (4)].type);
+
+ (yyval.node) = nod(ODCLFIELD, N, typenod(t));
+ if((yyvsp[(1) - (4)].sym))
+ (yyval.node)->left = newname((yyvsp[(1) - (4)].sym));
+ (yyval.node)->isddd = 1;
+ (yyval.node)->val = (yyvsp[(4) - (4)].val);
+ }
+ break;
+
+ case 325:
+
+/* Line 1806 of yacc.c */
+#line 1960 "go.y"
+ {
+ Sym *s;
+
+ if((yyvsp[(1) - (3)].sym) != S) {
+ (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);
+ (yyval.node)->right = typenod((yyvsp[(2) - (3)].type));
+ (yyval.node)->val = (yyvsp[(3) - (3)].val);
+ }
+ }
+ break;
+
+ case 326:
+
+/* Line 1806 of yacc.c */
+#line 1978 "go.y"
+ {
+ (yyval.node) = nod(ODCLFIELD, newname((yyvsp[(1) - (5)].sym)), typenod(functype(fakethis(), (yyvsp[(3) - (5)].list), (yyvsp[(5) - (5)].list))));
+ }
+ break;
+
+ case 327:
+
+/* Line 1806 of yacc.c */
+#line 1982 "go.y"
+ {
+ (yyval.node) = nod(ODCLFIELD, N, typenod((yyvsp[(1) - (1)].type)));
+ }
+ break;
+
+ case 328:
+
+/* Line 1806 of yacc.c */
+#line 1987 "go.y"
+ {
+ (yyval.list) = nil;
+ }
+ break;
+
+ case 330:
+
+/* Line 1806 of yacc.c */
+#line 1994 "go.y"
+ {
+ (yyval.list) = (yyvsp[(2) - (3)].list);
+ }
+ break;
+
+ case 331:
+
+/* Line 1806 of yacc.c */
+#line 1998 "go.y"
+ {
+ (yyval.list) = list1(nod(ODCLFIELD, N, typenod((yyvsp[(1) - (1)].type))));
+ }
+ break;
+
+ case 332:
+
+/* Line 1806 of yacc.c */
+#line 2008 "go.y"
+ {
+ (yyval.node) = nodlit((yyvsp[(1) - (1)].val));
+ }
+ break;
+
+ case 333:
+
+/* Line 1806 of yacc.c */
+#line 2012 "go.y"
+ {
+ (yyval.node) = nodlit((yyvsp[(2) - (2)].val));
+ switch((yyval.node)->val.ctype){
+ case CTINT:
+ case CTRUNE:
+ mpnegfix((yyval.node)->val.u.xval);
+ break;
+ case CTFLT:
+ mpnegflt((yyval.node)->val.u.fval);
+ break;
+ default:
+ yyerror("bad negated constant");
+ }
+ }
+ break;
+
+ case 334:
+
+/* Line 1806 of yacc.c */
+#line 2027 "go.y"
+ {
+ (yyval.node) = oldname(pkglookup((yyvsp[(1) - (1)].sym)->name, builtinpkg));
+ if((yyval.node)->op != OLITERAL)
+ yyerror("bad constant %S", (yyval.node)->sym);
+ }
+ break;
+
+ case 336:
+
+/* Line 1806 of yacc.c */
+#line 2036 "go.y"
+ {
+ if((yyvsp[(2) - (5)].node)->val.ctype == CTRUNE && (yyvsp[(4) - (5)].node)->val.ctype == CTINT) {
+ (yyval.node) = (yyvsp[(2) - (5)].node);
+ mpaddfixfix((yyvsp[(2) - (5)].node)->val.u.xval, (yyvsp[(4) - (5)].node)->val.u.xval, 0);
+ break;
+ }
+ (yyval.node) = nodcplxlit((yyvsp[(2) - (5)].node)->val, (yyvsp[(4) - (5)].node)->val);
+ }
+ break;
+
+ case 339:
+
+/* Line 1806 of yacc.c */
+#line 2050 "go.y"
+ {
+ (yyval.list) = list1((yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 340:
+
+/* Line 1806 of yacc.c */
+#line 2054 "go.y"
+ {
+ (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 341:
+
+/* Line 1806 of yacc.c */
+#line 2060 "go.y"
+ {
+ (yyval.list) = list1((yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 342:
+
+/* Line 1806 of yacc.c */
+#line 2064 "go.y"
+ {
+ (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 343:
+
+/* Line 1806 of yacc.c */
+#line 2070 "go.y"
+ {
+ (yyval.list) = list1((yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 344:
+
+/* Line 1806 of yacc.c */
+#line 2074 "go.y"
+ {
+ (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+
+
+/* Line 1806 of yacc.c */
+#line 5298 "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);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++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. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*------------------------------------.
+| 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)
+ {
+ ++yynerrs;
+#if ! YYERROR_VERBOSE
+ yyerror (YY_("syntax error"));
+#else
+# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \
+ yyssp, yytoken)
+ {
+ 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
+ }
+
+
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse lookahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ YYABORT;
+ }
+ else
+ {
+ yydestruct ("Error: discarding",
+ yytoken, &yylval);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse lookahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+ /* Pacify compilers like GCC when the user code never invokes
+ YYERROR and the label yyerrorlab therefore never appears in user
+ code. */
+ if (/*CONSTCOND*/ 0)
+ goto yyerrorlab;
+
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYERROR. */
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (!yypact_value_is_default (yyn))
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+
+ yydestruct ("Error: popping",
+ yystos[yystate], yyvsp);
+ YYPOPSTACK (1);
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ *++yyvsp = yylval;
+
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#if !defined(yyoverflow) || YYERROR_VERBOSE
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here. |
+`-------------------------------------------------*/
+yyexhaustedlab:
+ yyerror (YY_("memory exhausted"));
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+ 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);
+ YY_STACK_PRINT (yyss, yyssp);
+ while (yyssp != yyss)
+ {
+ yydestruct ("Cleanup: popping",
+ yystos[*yyssp], yyvsp);
+ YYPOPSTACK (1);
+ }
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+#endif
+ /* Make sure YYID is used. */
+ return YYID (yyresult);
+}
+
+
+
+/* Line 2067 of yacc.c */
+#line 2078 "go.y"
+
+
+static void
+fixlbrace(int lbr)
+{
+ // If the opening brace was an LBODY,
+ // set up for another one now that we're done.
+ // See comment in lex.c about loophack.
+ if(lbr == LBODY)
+ loophack = 1;
+}
+
+
diff --git a/src/cmd/gc/y.tab.h b/src/cmd/gc/y.tab.h
new file mode 100644
index 000000000..bc6c47d6d
--- /dev/null
+++ b/src/cmd/gc/y.tab.h
@@ -0,0 +1,171 @@
+/* A Bison parser, made by GNU Bison 2.5. */
+
+/* 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 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, 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
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ 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
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ LLITERAL = 258,
+ LASOP = 259,
+ LBREAK = 260,
+ LCASE = 261,
+ LCHAN = 262,
+ LCOLAS = 263,
+ LCONST = 264,
+ LCONTINUE = 265,
+ LDDD = 266,
+ LDEFAULT = 267,
+ LDEFER = 268,
+ LELSE = 269,
+ LFALL = 270,
+ LFOR = 271,
+ LFUNC = 272,
+ LGO = 273,
+ LGOTO = 274,
+ LIF = 275,
+ LIMPORT = 276,
+ LINTERFACE = 277,
+ LMAP = 278,
+ LNAME = 279,
+ LPACKAGE = 280,
+ LRANGE = 281,
+ LRETURN = 282,
+ LSELECT = 283,
+ LSTRUCT = 284,
+ LSWITCH = 285,
+ LTYPE = 286,
+ LVAR = 287,
+ LANDAND = 288,
+ LANDNOT = 289,
+ LBODY = 290,
+ LCOMM = 291,
+ LDEC = 292,
+ LEQ = 293,
+ LGE = 294,
+ LGT = 295,
+ LIGNORE = 296,
+ LINC = 297,
+ LLE = 298,
+ LLSH = 299,
+ LLT = 300,
+ LNE = 301,
+ LOROR = 302,
+ LRSH = 303,
+ NotPackage = 304,
+ NotParen = 305,
+ PreferToRightParen = 306
+ };
+#endif
+/* Tokens. */
+#define LLITERAL 258
+#define LASOP 259
+#define LBREAK 260
+#define LCASE 261
+#define LCHAN 262
+#define LCOLAS 263
+#define LCONST 264
+#define LCONTINUE 265
+#define LDDD 266
+#define LDEFAULT 267
+#define LDEFER 268
+#define LELSE 269
+#define LFALL 270
+#define LFOR 271
+#define LFUNC 272
+#define LGO 273
+#define LGOTO 274
+#define LIF 275
+#define LIMPORT 276
+#define LINTERFACE 277
+#define LMAP 278
+#define LNAME 279
+#define LPACKAGE 280
+#define LRANGE 281
+#define LRETURN 282
+#define LSELECT 283
+#define LSTRUCT 284
+#define LSWITCH 285
+#define LTYPE 286
+#define LVAR 287
+#define LANDAND 288
+#define LANDNOT 289
+#define LBODY 290
+#define LCOMM 291
+#define LDEC 292
+#define LEQ 293
+#define LGE 294
+#define LGT 295
+#define LIGNORE 296
+#define LINC 297
+#define LLE 298
+#define LLSH 299
+#define LLT 300
+#define LNE 301
+#define LOROR 302
+#define LRSH 303
+#define NotPackage 304
+#define NotParen 305
+#define PreferToRightParen 306
+
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+{
+
+/* Line 2068 of yacc.c */
+#line 28 "go.y"
+
+ Node* node;
+ NodeList* list;
+ Type* type;
+ Sym* sym;
+ struct Val val;
+ int i;
+
+
+
+/* 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
+#endif
+
+extern YYSTYPE yylval;
+
+
diff --git a/src/cmd/gc/yerr.h b/src/cmd/gc/yerr.h
new file mode 100644
index 000000000..588890d0e
--- /dev/null
+++ b/src/cmd/gc/yerr.h
@@ -0,0 +1,73 @@
+// 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.
+
+// Example-based syntax error messages.
+// See bisonerrors, Makefile, go.y.
+
+static struct {
+ int yystate;
+ int yychar;
+ char *msg;
+} yymsg[] = {
+ // Each line of the form % token list
+ // is converted by bisonerrors into the yystate and yychar caused
+ // by that token list.
+
+ 221, ',',
+ "unexpected comma during import block",
+
+ 377, ';',
+ "unexpected semicolon or newline before {",
+
+ 398, ';',
+ "unexpected semicolon or newline before {",
+
+ 237, ';',
+ "unexpected semicolon or newline before {",
+
+ 474, LBODY,
+ "unexpected semicolon or newline before {",
+
+ 22, '{',
+ "unexpected semicolon or newline before {",
+
+ 144, ';',
+ "unexpected semicolon or newline in type declaration",
+
+ 37, '}',
+ "unexpected } in channel type",
+
+ 37, ')',
+ "unexpected ) in channel type",
+
+ 37, ',',
+ "unexpected comma in channel type",
+
+ 437, LELSE,
+ "unexpected semicolon or newline before else",
+
+ 257, ',',
+ "name list not allowed in interface type",
+
+ 237, LVAR,
+ "var declaration not allowed in for initializer",
+
+ 65, '{',
+ "unexpected { at end of statement",
+
+ 376, '{',
+ "unexpected { at end of statement",
+
+ 125, ';',
+ "argument to go/defer must be function call",
+
+ 425, ';',
+ "need trailing comma before newline in composite literal",
+
+ 112, LNAME,
+ "nested func not allowed",
+
+ 615, ';',
+ "else must be followed by if or statement block"
+};
diff --git a/src/cmd/go/bootstrap.go b/src/cmd/go/bootstrap.go
new file mode 100644
index 000000000..32941404c
--- /dev/null
+++ b/src/cmd/go/bootstrap.go
@@ -0,0 +1,30 @@
+// 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 cmd_go_bootstrap
+
+// This code is compiled only into the bootstrap 'go' binary.
+// These stubs avoid importing packages with large dependency
+// trees, like the use of "net/http" in vcs.go.
+
+package main
+
+import (
+ "errors"
+ "io"
+)
+
+var errHTTP = errors.New("no http in bootstrap go command")
+
+func httpGET(url string) ([]byte, error) {
+ return nil, errHTTP
+}
+
+func httpsOrHTTP(importPath string) (string, io.ReadCloser, error) {
+ return "", nil, errHTTP
+}
+
+func parseMetaGoImports(r io.Reader) (imports []metaImport) {
+ panic("unreachable")
+}
diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go
new file mode 100644
index 000000000..4bb83f161
--- /dev/null
+++ b/src/cmd/go/build.go
@@ -0,0 +1,1566 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "bytes"
+ "container/heap"
+ "errors"
+ "fmt"
+ "go/build"
+ "io"
+ "io/ioutil"
+ "log"
+ "os"
+ "os/exec"
+ "path"
+ "path/filepath"
+ "regexp"
+ "runtime"
+ "strings"
+ "sync"
+ "time"
+)
+
+var cmdBuild = &Command{
+ UsageLine: "build [-o output] [build flags] [packages]",
+ Short: "compile packages and dependencies",
+ Long: `
+Build compiles the packages named by the import paths,
+along with their dependencies, but it does not install the results.
+
+If the arguments are a list of .go files, build treats them as a list
+of source files specifying a single package.
+
+When the command line specifies a single main package,
+build writes the resulting executable to output.
+Otherwise build compiles the packages but discards the results,
+serving only as a check that the packages can be built.
+
+The -o flag specifies the output file name. If not specified, the
+name is packagename.a (for a non-main package) or the base
+name of the first source file (for a main package).
+
+The build flags are shared by the build, install, run, and test commands:
+
+ -a
+ force rebuilding of packages that are already up-to-date.
+ -n
+ print the commands but do not run them.
+ -p n
+ the number of builds that can be run in parallel.
+ The default is the number of CPUs available.
+ -v
+ print the names of packages as they are compiled.
+ -work
+ print the name of the temporary work directory and
+ do not delete it when exiting.
+ -x
+ print the commands.
+
+ -compiler name
+ name of compiler to use, as in runtime.Compiler (gccgo or gc)
+ -gccgoflags 'arg list'
+ arguments to pass on each gccgo compiler/linker invocation
+ -gcflags 'arg list'
+ arguments to pass on each 5g, 6g, or 8g compiler invocation
+ -ldflags 'flag list'
+ arguments to pass on each 5l, 6l, or 8l linker invocation
+ -tags 'tag list'
+ a list of build tags to consider satisfied during the build.
+ See the documentation for the go/build package for
+ more information about build tags.
+
+For more about specifying packages, see 'go help packages'.
+For more about where packages and binaries are installed,
+see 'go help gopath'.
+
+See also: go install, go get, go clean.
+ `,
+}
+
+func init() {
+ // break init cycle
+ cmdBuild.Run = runBuild
+ cmdInstall.Run = runInstall
+
+ addBuildFlags(cmdBuild)
+ addBuildFlags(cmdInstall)
+}
+
+// Flags set by multiple commands.
+var buildA bool // -a flag
+var buildN bool // -n flag
+var buildP = runtime.NumCPU() // -p flag
+var buildV bool // -v flag
+var buildX bool // -x flag
+var buildO = cmdBuild.Flag.String("o", "", "output file")
+var buildWork bool // -work flag
+var buildGcflags []string // -gcflags flag
+var buildLdflags []string // -ldflags flag
+var buildGccgoflags []string // -gccgoflags flag
+
+var buildContext = build.Default
+var buildToolchain toolchain = noToolchain{}
+
+// buildCompiler implements flag.Var.
+// It implements Set by updating both
+// buildToolchain and buildContext.Compiler.
+type buildCompiler struct{}
+
+func (c buildCompiler) Set(value string) error {
+ switch value {
+ case "gc":
+ buildToolchain = gcToolchain{}
+ case "gccgo":
+ buildToolchain = gccgcToolchain{}
+ default:
+ return fmt.Errorf("unknown compiler %q", value)
+ }
+ buildContext.Compiler = value
+ return nil
+}
+
+func (c buildCompiler) String() string {
+ return buildContext.Compiler
+}
+
+func init() {
+ switch build.Default.Compiler {
+ case "gc":
+ buildToolchain = gcToolchain{}
+ case "gccgo":
+ buildToolchain = gccgcToolchain{}
+ }
+}
+
+// addBuildFlags adds the flags common to the build and install commands.
+func addBuildFlags(cmd *Command) {
+ // NOTE: If you add flags here, also add them to testflag.go.
+ cmd.Flag.BoolVar(&buildA, "a", false, "")
+ cmd.Flag.BoolVar(&buildN, "n", false, "")
+ cmd.Flag.IntVar(&buildP, "p", buildP, "")
+ cmd.Flag.BoolVar(&buildV, "v", false, "")
+ cmd.Flag.BoolVar(&buildX, "x", false, "")
+ cmd.Flag.BoolVar(&buildWork, "work", false, "")
+ cmd.Flag.Var((*stringsFlag)(&buildGcflags), "gcflags", "")
+ cmd.Flag.Var((*stringsFlag)(&buildLdflags), "ldflags", "")
+ cmd.Flag.Var((*stringsFlag)(&buildGccgoflags), "gccgoflags", "")
+ cmd.Flag.Var((*stringsFlag)(&buildContext.BuildTags), "tags", "")
+ cmd.Flag.Var(buildCompiler{}, "compiler", "")
+}
+
+type stringsFlag []string
+
+func (v *stringsFlag) Set(s string) error {
+ *v = strings.Fields(s)
+ return nil
+}
+
+func (v *stringsFlag) String() string {
+ return "<stringsFlag>"
+}
+
+func runBuild(cmd *Command, args []string) {
+ var b builder
+ b.init()
+
+ pkgs := packagesForBuild(args)
+
+ if len(pkgs) == 1 && pkgs[0].Name == "main" && *buildO == "" {
+ _, *buildO = path.Split(pkgs[0].ImportPath)
+ *buildO += exeSuffix
+ }
+
+ if *buildO != "" {
+ if len(pkgs) > 1 {
+ fatalf("go build: cannot use -o with multiple packages")
+ }
+ p := pkgs[0]
+ p.target = "" // must build - not up to date
+ a := b.action(modeInstall, modeBuild, p)
+ a.target = *buildO
+ b.do(a)
+ return
+ }
+
+ a := &action{}
+ for _, p := range packages(args) {
+ a.deps = append(a.deps, b.action(modeBuild, modeBuild, p))
+ }
+ b.do(a)
+}
+
+var cmdInstall = &Command{
+ UsageLine: "install [build flags] [packages]",
+ Short: "compile and install packages and dependencies",
+ Long: `
+Install compiles and installs the packages named by the import paths,
+along with their dependencies.
+
+For more about the build flags, see 'go help build'.
+For more about specifying packages, see 'go help packages'.
+
+See also: go build, go get, go clean.
+ `,
+}
+
+func runInstall(cmd *Command, args []string) {
+ pkgs := packagesForBuild(args)
+
+ for _, p := range pkgs {
+ if p.Target == "" && (!p.Standard || p.ImportPath != "unsafe") {
+ errorf("go install: no install location for %s", p.ImportPath)
+ }
+ }
+ exitIfErrors()
+
+ var b builder
+ b.init()
+ a := &action{}
+ for _, p := range pkgs {
+ a.deps = append(a.deps, b.action(modeInstall, modeInstall, p))
+ }
+ b.do(a)
+}
+
+// Global build parameters (used during package load)
+var (
+ goarch string
+ goos string
+ archChar string
+ exeSuffix string
+)
+
+func init() {
+ goarch = buildContext.GOARCH
+ goos = buildContext.GOOS
+ if goos == "windows" {
+ exeSuffix = ".exe"
+ }
+ var err error
+ archChar, err = build.ArchChar(goarch)
+ if err != nil {
+ fatalf("%s", err)
+ }
+}
+
+// A builder holds global state about a build.
+// It does not hold per-package state, because we
+// build packages in parallel, and the builder is shared.
+type builder struct {
+ work string // the temporary work directory (ends in filepath.Separator)
+ actionCache map[cacheKey]*action // a cache of already-constructed actions
+ mkdirCache map[string]bool // a cache of created directories
+ print func(args ...interface{}) (int, error)
+
+ output sync.Mutex
+ scriptDir string // current directory in printed script
+
+ exec sync.Mutex
+ readySema chan bool
+ ready actionQueue
+}
+
+// An action represents a single action in the action graph.
+type action struct {
+ p *Package // the package this action works on
+ deps []*action // actions that must happen before this one
+ triggers []*action // inverse of deps
+ cgo *action // action for cgo binary if needed
+ args []string // additional args for runProgram
+ testOutput *bytes.Buffer // test output buffer
+
+ f func(*builder, *action) error // the action itself (nil = no-op)
+ ignoreFail bool // whether to run f even if dependencies fail
+
+ // Generated files, directories.
+ link bool // target is executable, not just package
+ pkgdir string // the -I or -L argument to use when importing this package
+ objdir string // directory for intermediate objects
+ objpkg string // the intermediate package .a file created during the action
+ target string // goal of the action: the created package or executable
+
+ // Execution state.
+ pending int // number of deps yet to complete
+ priority int // relative execution priority
+ failed bool // whether the action failed
+}
+
+// cacheKey is the key for the action cache.
+type cacheKey struct {
+ mode buildMode
+ p *Package
+}
+
+// buildMode specifies the build mode:
+// are we just building things or also installing the results?
+type buildMode int
+
+const (
+ modeBuild buildMode = iota
+ modeInstall
+)
+
+var (
+ goroot = filepath.Clean(runtime.GOROOT())
+ gobin = os.Getenv("GOBIN")
+ gorootBin = filepath.Join(goroot, "bin")
+ gorootSrcPkg = filepath.Join(goroot, "src/pkg")
+ gorootPkg = filepath.Join(goroot, "pkg")
+ gorootSrc = filepath.Join(goroot, "src")
+)
+
+func (b *builder) init() {
+ var err error
+ b.print = fmt.Print
+ b.actionCache = make(map[cacheKey]*action)
+ b.mkdirCache = make(map[string]bool)
+
+ if buildN {
+ b.work = "$WORK"
+ } else {
+ b.work, err = ioutil.TempDir("", "go-build")
+ if err != nil {
+ fatalf("%s", err)
+ }
+ if buildX || buildWork {
+ fmt.Printf("WORK=%s\n", b.work)
+ }
+ if !buildWork {
+ atexit(func() { os.RemoveAll(b.work) })
+ }
+ }
+}
+
+// goFilesPackage creates a package for building a collection of Go files
+// (typically named on the command line). The target is named p.a for
+// package p or named after the first Go file for package main.
+func goFilesPackage(gofiles []string) *Package {
+ // TODO: Remove this restriction.
+ for _, f := range gofiles {
+ if !strings.HasSuffix(f, ".go") {
+ fatalf("named files must be .go files")
+ }
+ }
+
+ var stk importStack
+ ctxt := buildContext
+ ctxt.UseAllFiles = true
+
+ // Synthesize fake "directory" that only shows the named files,
+ // to make it look like this is a standard package or
+ // command directory. So that local imports resolve
+ // consistently, the files must all be in the same directory.
+ var dirent []os.FileInfo
+ var dir string
+ for _, file := range gofiles {
+ fi, err := os.Stat(file)
+ if err != nil {
+ fatalf("%s", err)
+ }
+ if fi.IsDir() {
+ fatalf("%s is a directory, should be a Go file", file)
+ }
+ dir1, _ := filepath.Split(file)
+ if dir == "" {
+ dir = dir1
+ } else if dir != dir1 {
+ fatalf("named files must all be in one directory; have %s and %s", dir, dir1)
+ }
+ dirent = append(dirent, fi)
+ }
+ ctxt.ReadDir = func(string) ([]os.FileInfo, error) { return dirent, nil }
+
+ if !filepath.IsAbs(dir) {
+ dir = filepath.Join(cwd, dir)
+ }
+
+ bp, err := ctxt.ImportDir(dir, 0)
+ pkg := new(Package)
+ pkg.local = true
+ pkg.load(&stk, bp, err)
+ pkg.localPrefix = dirToImportPath(dir)
+ pkg.ImportPath = "command-line-arguments"
+ pkg.target = ""
+
+ if pkg.Name == "main" {
+ _, elem := filepath.Split(gofiles[0])
+ exe := elem[:len(elem)-len(".go")] + exeSuffix
+ if *buildO == "" {
+ *buildO = exe
+ }
+ if gobin != "" {
+ pkg.target = filepath.Join(gobin, exe)
+ }
+ } else {
+ if *buildO == "" {
+ *buildO = pkg.Name + ".a"
+ }
+ }
+ pkg.Target = pkg.target
+ pkg.Stale = true
+
+ computeStale(pkg)
+ return pkg
+}
+
+// action returns the action for applying the given operation (mode) to the package.
+// depMode is the action to use when building dependencies.
+func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action {
+ key := cacheKey{mode, p}
+ a := b.actionCache[key]
+ if a != nil {
+ return a
+ }
+
+ a = &action{p: p, pkgdir: p.build.PkgRoot}
+ if p.pkgdir != "" { // overrides p.t
+ a.pkgdir = p.pkgdir
+ }
+
+ b.actionCache[key] = a
+
+ for _, p1 := range p.imports {
+ a.deps = append(a.deps, b.action(depMode, depMode, p1))
+ }
+
+ // If we are not doing a cross-build, then record the binary we'll
+ // generate for cgo as a dependency of the build of any package
+ // using cgo, to make sure we do not overwrite the binary while
+ // a package is using it. If this is a cross-build, then the cgo we
+ // are writing is not the cgo we need to use.
+ if goos == runtime.GOOS && goarch == runtime.GOARCH {
+ if len(p.CgoFiles) > 0 || p.Standard && p.ImportPath == "runtime/cgo" {
+ var stk importStack
+ p1 := loadPackage("cmd/cgo", &stk)
+ if p1.Error != nil {
+ fatalf("load cmd/cgo: %v", p1.Error)
+ }
+ a.cgo = b.action(depMode, depMode, p1)
+ a.deps = append(a.deps, a.cgo)
+ }
+ }
+
+ if p.Standard {
+ switch p.ImportPath {
+ case "builtin", "unsafe":
+ // Fake packages - nothing to build.
+ return a
+ }
+ // gccgo standard library is "fake" too.
+ if _, ok := buildToolchain.(gccgcToolchain); ok {
+ // the target name is needed for cgo.
+ a.target = p.target
+ return a
+ }
+ }
+
+ if !p.Stale && p.target != "" {
+ // p.Stale==false implies that p.target is up-to-date.
+ // Record target name for use by actions depending on this one.
+ a.target = p.target
+ return a
+ }
+
+ if p.local && p.target == "" {
+ // 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)
+ a.link = p.Name == "main"
+
+ switch mode {
+ case modeInstall:
+ a.f = (*builder).install
+ a.deps = []*action{b.action(modeBuild, depMode, p)}
+ a.target = a.p.target
+ case modeBuild:
+ a.f = (*builder).build
+ a.target = a.objpkg
+ if a.link {
+ // An executable file.
+ // (This is the name of a temporary file.)
+ a.target = a.objdir + "a.out" + exeSuffix
+ }
+ }
+
+ return a
+}
+
+// actionList returns the list of actions in the dag rooted at root
+// as visited in a depth-first post-order traversal.
+func actionList(root *action) []*action {
+ seen := map[*action]bool{}
+ all := []*action{}
+ var walk func(*action)
+ walk = func(a *action) {
+ if seen[a] {
+ return
+ }
+ seen[a] = true
+ for _, a1 := range a.deps {
+ walk(a1)
+ }
+ all = append(all, a)
+ }
+ walk(root)
+ return all
+}
+
+// do runs the action graph rooted at root.
+func (b *builder) do(root *action) {
+ // Build list of all actions, assigning depth-first post-order priority.
+ // The original implementation here was a true queue
+ // (using a channel) but it had the effect of getting
+ // distracted by low-level leaf actions to the detriment
+ // of completing higher-level actions. The order of
+ // work does not matter much to overall execution time,
+ // but when running "go test std" it is nice to see each test
+ // results as soon as possible. The priorities assigned
+ // ensure that, all else being equal, the execution prefers
+ // to do what it would have done first in a simple depth-first
+ // dependency order traversal.
+ all := actionList(root)
+ for i, a := range all {
+ a.priority = i
+ }
+
+ b.readySema = make(chan bool, len(all))
+ done := make(chan bool)
+
+ // Initialize per-action execution state.
+ for _, a := range all {
+ for _, a1 := range a.deps {
+ a1.triggers = append(a1.triggers, a)
+ }
+ a.pending = len(a.deps)
+ if a.pending == 0 {
+ b.ready.push(a)
+ b.readySema <- true
+ }
+ }
+
+ // Handle runs a single action and takes care of triggering
+ // any actions that are runnable as a result.
+ handle := func(a *action) {
+ var err error
+ if a.f != nil && (!a.failed || a.ignoreFail) {
+ err = a.f(b, a)
+ }
+
+ // The actions run in parallel but all the updates to the
+ // shared work state are serialized through b.exec.
+ b.exec.Lock()
+ defer b.exec.Unlock()
+
+ if err != nil {
+ if err == errPrintedOutput {
+ setExitStatus(2)
+ } else {
+ errorf("%s", err)
+ }
+ a.failed = true
+ }
+
+ for _, a0 := range a.triggers {
+ if a.failed {
+ a0.failed = true
+ }
+ if a0.pending--; a0.pending == 0 {
+ b.ready.push(a0)
+ b.readySema <- true
+ }
+ }
+
+ if a == root {
+ close(b.readySema)
+ done <- true
+ }
+ }
+
+ // Kick off goroutines according to parallelism.
+ // If we are using the -n flag (just printing commands)
+ // drop the parallelism to 1, both to make the output
+ // deterministic and because there is no real work anyway.
+ par := buildP
+ if buildN {
+ par = 1
+ }
+ for i := 0; i < par; i++ {
+ go func() {
+ for _ = range b.readySema {
+ // Receiving a value from b.sema entitles
+ // us to take from the ready queue.
+ b.exec.Lock()
+ a := b.ready.pop()
+ b.exec.Unlock()
+ handle(a)
+ }
+ }()
+ }
+
+ <-done
+}
+
+// build is the action for building a single package or command.
+func (b *builder) build(a *action) (err error) {
+ defer func() {
+ if err != nil && err != errPrintedOutput {
+ err = fmt.Errorf("go build %s: %v", a.p.ImportPath, err)
+ }
+ }()
+ if buildN {
+ // In -n mode, print a banner between packages.
+ // The banner is five lines so that when changes to
+ // different sections of the bootstrap script have to
+ // be merged, the banners give patch something
+ // to use to find its context.
+ fmt.Printf("\n#\n# %s\n#\n\n", a.p.ImportPath)
+ }
+
+ if buildV {
+ fmt.Fprintf(os.Stderr, "%s\n", a.p.ImportPath)
+ }
+
+ // Make build directory.
+ obj := a.objdir
+ if err := b.mkdir(obj); err != nil {
+ return err
+ }
+
+ var gofiles, cfiles, sfiles, objects, cgoObjects []string
+ 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.
+ // 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.
+ var gccfiles []string
+ if a.p.Standard && a.p.ImportPath == "runtime/cgo" {
+ filter := func(files, nongcc, gcc []string) ([]string, []string) {
+ for _, f := range files {
+ if strings.HasPrefix(f, "gcc_") {
+ gcc = append(gcc, f)
+ } else {
+ nongcc = append(nongcc, f)
+ }
+ }
+ return nongcc, gcc
+ }
+ cfiles, gccfiles = filter(cfiles, cfiles[:0], gccfiles)
+ sfiles, gccfiles = filter(sfiles, sfiles[:0], gccfiles)
+ } else {
+ gccfiles = append(cfiles, sfiles...)
+ cfiles = nil
+ sfiles = nil
+ }
+
+ cgoExe := tool("cgo")
+ if a.cgo != nil && a.cgo.target != "" {
+ cgoExe = a.cgo.target
+ }
+ outGo, outObj, err := b.cgo(a.p, cgoExe, obj, gccfiles)
+ if err != nil {
+ return err
+ }
+ cgoObjects = append(cgoObjects, outObj...)
+ gofiles = append(gofiles, outGo...)
+ }
+
+ // Prepare Go import path list.
+ inc := b.includeArgs("-I", a.deps)
+
+ // Compile Go.
+ if len(gofiles) > 0 {
+ if out, err := buildToolchain.gc(b, a.p, obj, inc, gofiles); err != nil {
+ return err
+ } else {
+ objects = append(objects, out)
+ }
+ }
+
+ // 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"
+ for _, file := range a.p.HFiles {
+ switch {
+ case strings.HasSuffix(file, _goos_goarch):
+ targ := file[:len(file)-len(_goos_goarch)] + "_GOOS_GOARCH.h"
+ 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"
+ 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"
+ if err := b.copyFile(a, obj+targ, filepath.Join(a.p.Dir, file), 0666); err != nil {
+ return err
+ }
+ }
+ }
+
+ for _, file := range cfiles {
+ out := file[:len(file)-len(".c")] + "." + archChar
+ if err := buildToolchain.cc(b, a.p, obj, obj+out, file); err != nil {
+ return err
+ }
+ objects = append(objects, out)
+ }
+
+ // Assemble .s files.
+ for _, file := range sfiles {
+ out := file[:len(file)-len(".s")] + "." + archChar
+ if err := buildToolchain.asm(b, a.p, obj, obj+out, file); err != nil {
+ return err
+ }
+ objects = append(objects, out)
+ }
+
+ // NOTE(rsc): On Windows, it is critically important that the
+ // gcc-compiled objects (cgoObjects) be listed after the ordinary
+ // objects in the archive. I do not know why this is.
+ // http://golang.org/issue/2601
+ objects = append(objects, cgoObjects...)
+
+ // Add system object files.
+ for _, syso := range a.p.SysoFiles {
+ objects = append(objects, filepath.Join(a.p.Dir, syso))
+ }
+
+ // Pack into archive in obj directory
+ if err := buildToolchain.pack(b, a.p, obj, a.objpkg, objects); err != nil {
+ return err
+ }
+
+ // Link if needed.
+ if a.link {
+ // The compiler only cares about direct imports, but the
+ // linker needs the whole dependency tree.
+ all := actionList(a)
+ all = all[:len(all)-1] // drop a
+ if err := buildToolchain.ld(b, a.p, a.target, all, a.objpkg, objects); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// install is the action for installing a single package or executable.
+func (b *builder) install(a *action) (err error) {
+ defer func() {
+ if err != nil && err != errPrintedOutput {
+ err = fmt.Errorf("go install %s: %v", a.p.ImportPath, err)
+ }
+ }()
+ a1 := a.deps[0]
+ perm := os.FileMode(0666)
+ if a1.link {
+ perm = 0777
+ }
+
+ // make target directory
+ dir, _ := filepath.Split(a.target)
+ if dir != "" {
+ if err := b.mkdir(dir); err != nil {
+ return err
+ }
+ }
+
+ // remove object dir to keep the amount of
+ // garbage down in a large build. On an operating system
+ // with aggressive buffering, cleaning incrementally like
+ // this keeps the intermediate objects from hitting the disk.
+ if !buildWork {
+ defer os.RemoveAll(a1.objdir)
+ defer os.Remove(a1.target)
+ }
+
+ return b.copyFile(a, a.target, a1.target, perm)
+}
+
+// includeArgs returns the -I or -L directory list for access
+// to the results of the list of actions.
+func (b *builder) includeArgs(flag string, all []*action) []string {
+ inc := []string{}
+ incMap := map[string]bool{
+ b.work: true, // handled later
+ gorootPkg: true,
+ "": true, // ignore empty strings
+ }
+
+ // Look in the temporary space for results of test-specific actions.
+ // This is the $WORK/my/package/_test directory for the
+ // package being built, so there are few of these.
+ for _, a1 := range all {
+ if dir := a1.pkgdir; dir != a1.p.build.PkgRoot && !incMap[dir] {
+ incMap[dir] = true
+ inc = append(inc, flag, dir)
+ }
+ }
+
+ // Also look in $WORK for any non-test packages that have
+ // been built but not installed.
+ inc = append(inc, flag, b.work)
+
+ // Finally, look in the installed package directories for each action.
+ for _, a1 := range all {
+ if dir := a1.pkgdir; dir == a1.p.build.PkgRoot && !incMap[dir] {
+ incMap[dir] = true
+ if _, ok := buildToolchain.(gccgcToolchain); ok {
+ dir = filepath.Join(dir, "gccgo")
+ } else {
+ dir = filepath.Join(dir, goos+"_"+goarch)
+ }
+ inc = append(inc, flag, dir)
+ }
+ }
+
+ return inc
+}
+
+// copyFile is like 'cp src dst'.
+func (b *builder) copyFile(a *action, dst, src string, perm os.FileMode) error {
+ if buildN || buildX {
+ b.showcmd("", "cp %s %s", src, dst)
+ if buildN {
+ return nil
+ }
+ }
+
+ sf, err := os.Open(src)
+ if err != nil {
+ return err
+ }
+ defer sf.Close()
+
+ // Be careful about removing/overwriting dst.
+ // Do not remove/overwrite if dst exists and is a directory
+ // or a non-object file.
+ if fi, err := os.Stat(dst); err == nil {
+ if fi.IsDir() {
+ return fmt.Errorf("build output %q already exists and is a directory", dst)
+ }
+ if !isObject(dst) {
+ return fmt.Errorf("build output %q already exists and is not an object file", dst)
+ }
+ }
+
+ // On Windows, remove lingering ~ file from last attempt.
+ if toolIsWindows {
+ if _, err := os.Stat(dst + "~"); err == nil {
+ os.Remove(dst + "~")
+ }
+ }
+
+ os.Remove(dst)
+ df, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
+ 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
+ // next time we do an install of this binary.
+ if err := os.Rename(dst, dst+"~"); err == nil {
+ os.Remove(dst + "~")
+ }
+ df, err = os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
+ }
+ if err != nil {
+ return err
+ }
+
+ _, err = io.Copy(df, sf)
+ df.Close()
+ if err != nil {
+ os.Remove(dst)
+ return fmt.Errorf("copying %s to %s: %v", src, dst, err)
+ }
+ return nil
+}
+
+var objectMagic = [][]byte{
+ {'!', '<', 'a', 'r', 'c', 'h', '>', '\n'}, // Package archive
+ {'\x7F', 'E', 'L', 'F'}, // ELF
+ {0xFE, 0xED, 0xFA, 0xCE}, // Mach-O big-endian 32-bit
+ {0xFE, 0xED, 0xFA, 0xCF}, // Mach-O big-endian 64-bit
+ {0xCE, 0xFA, 0xED, 0xFE}, // Mach-O little-endian 32-bit
+ {0xCF, 0xFA, 0xED, 0xFE}, // Mach-O little-endian 64-bit
+ {0x4d, 0x5a, 0x90, 0x00, 0x03, 0x00, 0x04, 0x00}, // PE (Windows) as generated by 6l/8l
+}
+
+func isObject(s string) bool {
+ f, err := os.Open(s)
+ if err != nil {
+ return false
+ }
+ defer f.Close()
+ buf := make([]byte, 64)
+ io.ReadFull(f, buf)
+ for _, magic := range objectMagic {
+ if bytes.HasPrefix(buf, magic) {
+ return true
+ }
+ }
+ return false
+}
+
+// fmtcmd formats a command in the manner of fmt.Sprintf but also:
+//
+// If dir is non-empty and the script is not in dir right now,
+// fmtcmd inserts "cd dir\n" before the command.
+//
+// fmtcmd replaces the value of b.work with $WORK.
+// fmtcmd replaces the value of goroot with $GOROOT.
+// fmtcmd replaces the value of b.gobin with $GOBIN.
+//
+// fmtcmd replaces the name of the current directory with dot (.)
+// but only when it is at the beginning of a space-separated token.
+//
+func (b *builder) fmtcmd(dir string, format string, args ...interface{}) string {
+ cmd := fmt.Sprintf(format, args...)
+ if dir != "" && dir != "/" {
+ cmd = strings.Replace(" "+cmd, " "+dir, " .", -1)[1:]
+ if b.scriptDir != dir {
+ b.scriptDir = dir
+ cmd = "cd " + dir + "\n" + cmd
+ }
+ }
+ if b.work != "" {
+ cmd = strings.Replace(cmd, b.work, "$WORK", -1)
+ }
+ return cmd
+}
+
+// showcmd prints the given command to standard output
+// for the implementation of -n or -x.
+func (b *builder) showcmd(dir string, format string, args ...interface{}) {
+ b.output.Lock()
+ defer b.output.Unlock()
+ b.print(b.fmtcmd(dir, format, args...) + "\n")
+}
+
+// showOutput prints "# desc" followed by the given output.
+// The output is expected to contain references to 'dir', usually
+// the source directory for the package that has failed to build.
+// showOutput rewrites mentions of dir with a relative path to dir
+// when the relative path is shorter. This is usually more pleasant.
+// For example, if fmt doesn't compile and we are in src/pkg/html,
+// the output is
+//
+// $ go build
+// # fmt
+// ../fmt/print.go:1090: undefined: asdf
+// $
+//
+// instead of
+//
+// $ go build
+// # fmt
+// /usr/gopher/go/src/pkg/fmt/print.go:1090: undefined: asdf
+// $
+//
+// showOutput also replaces references to the work directory with $WORK.
+//
+func (b *builder) showOutput(dir, desc, out string) {
+ prefix := "# " + desc
+ suffix := "\n" + out
+ if reldir := shortPath(dir); reldir != dir {
+ suffix = strings.Replace(suffix, " "+dir, " "+reldir, -1)
+ suffix = strings.Replace(suffix, "\n"+dir, "\n"+reldir, -1)
+ }
+ suffix = strings.Replace(suffix, " "+b.work, " $WORK", -1)
+
+ b.output.Lock()
+ defer b.output.Unlock()
+ b.print(prefix, suffix)
+}
+
+// shortPath returns an absolute or relative name for path, whatever is shorter.
+func shortPath(path string) string {
+ if rel, err := filepath.Rel(cwd, path); err == nil && len(rel) < len(path) {
+ return rel
+ }
+ return path
+}
+
+// relPaths returns a copy of paths with absolute paths
+// made relative to the current directory if they would be shorter.
+func relPaths(paths []string) []string {
+ var out []string
+ pwd, _ := os.Getwd()
+ for _, p := range paths {
+ rel, err := filepath.Rel(pwd, p)
+ if err == nil && len(rel) < len(p) {
+ p = rel
+ }
+ out = append(out, p)
+ }
+ return out
+}
+
+// errPrintedOutput is a special error indicating that a command failed
+// but that it generated output as well, and that output has already
+// been printed, so there's no point showing 'exit status 1' or whatever
+// the wait status was. The main executor, builder.do, knows not to
+// print this error.
+var errPrintedOutput = errors.New("already printed output - no need to show error")
+
+// run runs the command given by cmdline in the directory dir.
+// If the command fails, run prints information about the failure
+// and returns a non-nil error.
+func (b *builder) run(dir string, desc string, cmdargs ...interface{}) error {
+ out, err := b.runOut(dir, desc, cmdargs...)
+ if len(out) > 0 {
+ if out[len(out)-1] != '\n' {
+ out = append(out, '\n')
+ }
+ if desc == "" {
+ desc = b.fmtcmd(dir, "%s", strings.Join(stringList(cmdargs...), " "))
+ }
+ b.showOutput(dir, desc, string(out))
+ if err != nil {
+ err = errPrintedOutput
+ }
+ }
+ return err
+}
+
+// runOut runs the command given by cmdline in the directory dir.
+// It returns the command output and any errors that occurred.
+func (b *builder) runOut(dir string, desc string, cmdargs ...interface{}) ([]byte, error) {
+ cmdline := stringList(cmdargs...)
+ if buildN || buildX {
+ b.showcmd(dir, "%s", strings.Join(cmdline, " "))
+ if buildN {
+ return nil, nil
+ }
+ }
+
+ nbusy := 0
+ for {
+ var buf bytes.Buffer
+ cmd := exec.Command(cmdline[0], cmdline[1:]...)
+ cmd.Stdout = &buf
+ cmd.Stderr = &buf
+ cmd.Dir = dir
+ // TODO: cmd.Env
+ err := cmd.Run()
+
+ // cmd.Run will fail on Unix if some other process has the binary
+ // we want to run open for writing. This can happen here because
+ // we build and install the cgo command and then run it.
+ // If another command was kicked off while we were writing the
+ // cgo binary, the child process for that command may be holding
+ // a reference to the fd, keeping us from running exec.
+ //
+ // But, you might reasonably wonder, how can this happen?
+ // The cgo fd, like all our fds, is close-on-exec, so that we need
+ // not worry about other processes inheriting the fd accidentally.
+ // The answer is that running a command is fork and exec.
+ // A child forked while the cgo fd is open inherits that fd.
+ // Until the child has called exec, it holds the fd open and the
+ // kernel will not let us run cgo. Even if the child were to close
+ // the fd explicitly, it would still be open from the time of the fork
+ // until the time of the explicit close, and the race would remain.
+ //
+ // On Unix systems, this results in ETXTBSY, which formats
+ // as "text file busy". Rather than hard-code specific error cases,
+ // we just look for that string. If this happens, sleep a little
+ // and try again. We let this happen three times, with increasing
+ // sleep lengths: 100+200+400 ms = 0.7 seconds.
+ //
+ // An alternate solution might be to split the cmd.Run into
+ // separate cmd.Start and cmd.Wait, and then use an RWLock
+ // to make sure that copyFile only executes when no cmd.Start
+ // call is in progress. However, cmd.Start (really syscall.forkExec)
+ // only guarantees that when it returns, the exec is committed to
+ // happen and succeed. It uses a close-on-exec file descriptor
+ // itself to determine this, so we know that when cmd.Start returns,
+ // at least one close-on-exec file descriptor has been closed.
+ // However, we cannot be sure that all of them have been closed,
+ // so the program might still encounter ETXTBSY even with such
+ // an RWLock. The race window would be smaller, perhaps, but not
+ // guaranteed to be gone.
+ //
+ // Sleeping when we observe the race seems to be the most reliable
+ // option we have.
+ //
+ // http://golang.org/issue/3001
+ //
+ if err != nil && nbusy < 3 && strings.Contains(err.Error(), "text file busy") {
+ time.Sleep(100 * time.Millisecond << uint(nbusy))
+ nbusy++
+ continue
+ }
+
+ return buf.Bytes(), err
+ }
+ panic("unreachable")
+}
+
+// mkdir makes the named directory.
+func (b *builder) mkdir(dir string) error {
+ b.exec.Lock()
+ defer b.exec.Unlock()
+ // We can be a little aggressive about being
+ // sure directories exist. Skip repeated calls.
+ if b.mkdirCache[dir] {
+ return nil
+ }
+ b.mkdirCache[dir] = true
+
+ if buildN || buildX {
+ b.showcmd("", "mkdir -p %s", dir)
+ if buildN {
+ return nil
+ }
+ }
+
+ if err := os.MkdirAll(dir, 0777); err != nil {
+ return err
+ }
+ return nil
+}
+
+// mkAbs returns an absolute path corresponding to
+// evaluating f in the directory dir.
+// We always pass absolute paths of source files so that
+// the error messages will include the full path to a file
+// in need of attention.
+func mkAbs(dir, f string) string {
+ // Leave absolute paths alone.
+ // Also, during -n mode we use the pseudo-directory $WORK
+ // instead of creating an actual work directory that won't be used.
+ // Leave paths beginning with $WORK alone too.
+ if filepath.IsAbs(f) || strings.HasPrefix(f, "$WORK") {
+ return f
+ }
+ return filepath.Join(dir, f)
+}
+
+type toolchain interface {
+ // gc runs the compiler in a specific directory on a set of files
+ // and returns the name of the generated output file.
+ // The compiler runs in the directory dir.
+ gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, err error)
+ // cc runs the toolchain's C compiler in a directory on a C file
+ // to produce an output file.
+ cc(b *builder, p *Package, objdir, ofile, cfile string) error
+ // asm runs the assembler in a specific directory on a specific file
+ // to generate the named output file.
+ asm(b *builder, p *Package, obj, ofile, sfile string) error
+ // pkgpath builds an appropriate path for a temporary package file.
+ pkgpath(basedir string, p *Package) string
+ // pack runs the archive packer in a specific directory to create
+ // an archive from a set of object files.
+ // typically it is run in the object directory.
+ pack(b *builder, p *Package, objDir, afile string, ofiles []string) error
+ // ld runs the linker to create a package starting at mainpkg.
+ ld(b *builder, p *Package, out string, allactions []*action, mainpkg string, ofiles []string) error
+
+ compiler() string
+ linker() string
+}
+
+type noToolchain struct{}
+
+func noCompiler() error {
+ log.Fatalf("unknown compiler %q", buildContext.Compiler)
+ return nil
+}
+
+func (noToolchain) compiler() string {
+ noCompiler()
+ return ""
+}
+
+func (noToolchain) linker() string {
+ noCompiler()
+ return ""
+}
+
+func (noToolchain) gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, err error) {
+ return "", noCompiler()
+}
+
+func (noToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error {
+ return noCompiler()
+}
+
+func (noToolchain) pkgpath(basedir string, p *Package) string {
+ noCompiler()
+ return ""
+}
+
+func (noToolchain) pack(b *builder, p *Package, objDir, afile string, ofiles []string) error {
+ return noCompiler()
+}
+
+func (noToolchain) ld(b *builder, p *Package, out string, allactions []*action, mainpkg string, ofiles []string) error {
+ return noCompiler()
+}
+
+func (noToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error {
+ return noCompiler()
+}
+
+// The Go toolchain.
+type gcToolchain struct{}
+
+func (gcToolchain) compiler() string {
+ return tool(archChar + "g")
+}
+
+func (gcToolchain) linker() string {
+ return tool(archChar + "l")
+}
+
+func (gcToolchain) gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, err error) {
+ out := "_go_." + archChar
+ ofile = obj + out
+ gcargs := []string{"-p", p.ImportPath}
+ if p.Standard && p.ImportPath == "runtime" {
+ // runtime compiles with a special 6g flag to emit
+ // additional reflect type data.
+ gcargs = append(gcargs, "-+")
+ }
+
+ args := stringList(tool(archChar+"g"), "-o", ofile, buildGcflags, gcargs, "-D", p.localPrefix, importArgs)
+ for _, f := range gofiles {
+ args = append(args, mkAbs(p.Dir, f))
+ }
+ return ofile, b.run(p.Dir, p.ImportPath, args)
+}
+
+func (gcToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error {
+ sfile = mkAbs(p.Dir, sfile)
+ return b.run(p.Dir, p.ImportPath, tool(archChar+"a"), "-I", obj, "-o", ofile, "-DGOOS_"+goos, "-DGOARCH_"+goarch, sfile)
+}
+
+func (gcToolchain) pkgpath(basedir string, p *Package) string {
+ end := filepath.FromSlash(p.ImportPath + ".a")
+ return filepath.Join(basedir, end)
+}
+
+func (gcToolchain) pack(b *builder, p *Package, objDir, afile string, ofiles []string) error {
+ var absOfiles []string
+ for _, f := range ofiles {
+ absOfiles = append(absOfiles, mkAbs(objDir, f))
+ }
+ return b.run(p.Dir, p.ImportPath, tool("pack"), "grc", mkAbs(objDir, afile), absOfiles)
+}
+
+func (gcToolchain) ld(b *builder, p *Package, out string, allactions []*action, mainpkg string, ofiles []string) error {
+ importArgs := b.includeArgs("-L", allactions)
+ return b.run(".", p.ImportPath, tool(archChar+"l"), "-o", out, importArgs, buildLdflags, mainpkg)
+}
+
+func (gcToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error {
+ inc := filepath.Join(goroot, "pkg", fmt.Sprintf("%s_%s", goos, goarch))
+ cfile = mkAbs(p.Dir, cfile)
+ return b.run(p.Dir, p.ImportPath, tool(archChar+"c"), "-FVw",
+ "-I", objdir, "-I", inc, "-o", ofile,
+ "-DGOOS_"+goos, "-DGOARCH_"+goarch, cfile)
+}
+
+// The Gccgo toolchain.
+type gccgcToolchain struct{}
+
+var gccgoBin, _ = exec.LookPath("gccgo")
+
+func (gccgcToolchain) compiler() string {
+ return gccgoBin
+}
+
+func (gccgcToolchain) linker() string {
+ return gccgoBin
+}
+
+func (gccgcToolchain) gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, err error) {
+ out := p.Name + ".o"
+ ofile = obj + out
+ gcargs := []string{"-g"}
+ if prefix := gccgoPrefix(p); prefix != "" {
+ gcargs = append(gcargs, "-fgo-prefix="+gccgoPrefix(p))
+ }
+ args := stringList("gccgo", importArgs, "-c", gcargs, "-o", ofile, buildGccgoflags)
+ for _, f := range gofiles {
+ args = append(args, mkAbs(p.Dir, f))
+ }
+ return ofile, b.run(p.Dir, p.ImportPath, args)
+}
+
+func (gccgcToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error {
+ sfile = mkAbs(p.Dir, sfile)
+ return b.run(p.Dir, p.ImportPath, "gccgo", "-I", obj, "-o", ofile, "-DGOOS_"+goos, "-DGOARCH_"+goarch, sfile)
+}
+
+func (gccgcToolchain) pkgpath(basedir string, p *Package) string {
+ end := filepath.FromSlash(p.ImportPath + ".a")
+ afile := filepath.Join(basedir, end)
+ // add "lib" to the final element
+ return filepath.Join(filepath.Dir(afile), "lib"+filepath.Base(afile))
+}
+
+func (gccgcToolchain) pack(b *builder, p *Package, objDir, afile string, ofiles []string) error {
+ var absOfiles []string
+ for _, f := range ofiles {
+ absOfiles = append(absOfiles, mkAbs(objDir, f))
+ }
+ return b.run(p.Dir, p.ImportPath, "ar", "cru", mkAbs(objDir, afile), absOfiles)
+}
+
+func (tools gccgcToolchain) ld(b *builder, p *Package, out string, allactions []*action, mainpkg string, ofiles []string) error {
+ // gccgo needs explicit linking with all package dependencies,
+ // and all LDFLAGS from cgo dependencies.
+ afiles := make(map[*Package]string)
+ ldflags := []string{}
+ cgoldflags := []string{}
+ for _, a := range allactions {
+ if a.p != nil {
+ if !a.p.Standard {
+ if afiles[a.p] == "" || a.objpkg != a.target {
+ afiles[a.p] = a.target
+ }
+ }
+ cgoldflags = append(cgoldflags, a.p.CgoLDFLAGS...)
+ }
+ }
+ for _, afile := range afiles {
+ ldflags = append(ldflags, afile)
+ }
+ ldflags = append(ldflags, cgoldflags...)
+ return b.run(".", p.ImportPath, "gccgo", "-o", out, buildGccgoflags, ofiles, "-Wl,-(", ldflags, "-Wl,-)")
+}
+
+func (gccgcToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error {
+ inc := filepath.Join(goroot, "pkg", fmt.Sprintf("%s_%s", goos, goarch))
+ cfile = mkAbs(p.Dir, cfile)
+ return b.run(p.Dir, p.ImportPath, "gcc", "-Wall", "-g",
+ "-I", objdir, "-I", inc, "-o", ofile,
+ "-DGOOS_"+goos, "-DGOARCH_"+goarch, "-c", cfile)
+}
+
+func gccgoPrefix(p *Package) string {
+ switch {
+ case p.build.IsCommand() && !p.forceLibrary:
+ return ""
+ case p.fake:
+ return "fake_" + p.ImportPath
+ }
+ return "go_" + p.ImportPath
+}
+
+// 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, b.gccCmd(p.Dir), flags, "-o", out, "-c", cfile)
+}
+
+// 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, b.gccCmd(p.Dir), "-o", out, obj, flags)
+}
+
+// gccCmd returns a gcc command line prefix
+func (b *builder) gccCmd(objdir string) []string {
+ // NOTE: env.go's mkEnv knows that the first three
+ // strings returned are "gcc", "-I", objdir (and cuts them off).
+
+ // TODO: HOST_CC?
+ a := []string{"gcc", "-I", objdir, "-g", "-O2"}
+
+ // Definitely want -fPIC but on Windows gcc complains
+ // "-fPIC ignored for target (all code is position independent)"
+ if goos != "windows" {
+ a = append(a, "-fPIC")
+ }
+ switch archChar {
+ case "8":
+ a = append(a, "-m32")
+ case "6":
+ a = append(a, "-m64")
+ }
+ // gcc-4.5 and beyond require explicit "-pthread" flag
+ // for multithreading with pthread library.
+ if buildContext.CgoEnabled {
+ switch goos {
+ case "windows":
+ a = append(a, "-mthreads")
+ default:
+ a = append(a, "-pthread")
+ }
+ }
+
+ // On OS X, some of the compilers behave as if -fno-common
+ // is always set, and the Mach-O linker in 6l/8l assumes this.
+ // See http://golang.org/issue/3253.
+ if goos == "darwin" {
+ a = append(a, "-fno-common")
+ }
+
+ return a
+}
+
+func envList(key string) []string {
+ return strings.Fields(os.Getenv(key))
+}
+
+var cgoRe = regexp.MustCompile(`[/\\:]`)
+
+func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, outObj []string, err error) {
+ if goos != toolGOOS {
+ return nil, nil, errors.New("cannot use cgo when compiling for a different operating system")
+ }
+
+ cgoCFLAGS := stringList(envList("CGO_CFLAGS"), p.CgoCFLAGS)
+ cgoLDFLAGS := stringList(envList("CGO_LDFLAGS"), p.CgoLDFLAGS)
+
+ if pkgs := p.CgoPkgConfig; len(pkgs) > 0 {
+ out, err := b.runOut(p.Dir, p.ImportPath, "pkg-config", "--cflags", pkgs)
+ if err != nil {
+ b.showOutput(p.Dir, "pkg-config --cflags "+strings.Join(pkgs, " "), string(out))
+ b.print(err.Error() + "\n")
+ return nil, nil, errPrintedOutput
+ }
+ if len(out) > 0 {
+ cgoCFLAGS = append(cgoCFLAGS, strings.Fields(string(out))...)
+ }
+ out, err = b.runOut(p.Dir, p.ImportPath, "pkg-config", "--libs", pkgs)
+ if err != nil {
+ b.showOutput(p.Dir, "pkg-config --libs "+strings.Join(pkgs, " "), string(out))
+ b.print(err.Error() + "\n")
+ return nil, nil, errPrintedOutput
+ }
+ if len(out) > 0 {
+ cgoLDFLAGS = append(cgoLDFLAGS, strings.Fields(string(out))...)
+ }
+ }
+
+ // Allows including _cgo_export.h from .[ch] files in the package.
+ cgoCFLAGS = append(cgoCFLAGS, "-I", obj)
+
+ // cgo
+ // TODO: CGOPKGPATH, CGO_FLAGS?
+ gofiles := []string{obj + "_cgo_gotypes.go"}
+ cfiles := []string{"_cgo_main.c", "_cgo_export.c"}
+ for _, fn := range p.CgoFiles {
+ f := cgoRe.ReplaceAllString(fn[:len(fn)-2], "_")
+ gofiles = append(gofiles, obj+f+"cgo1.go")
+ cfiles = append(cfiles, f+"cgo2.c")
+ }
+ defunC := obj + "_cgo_defun.c"
+
+ cgoflags := []string{}
+ // TODO: make cgo not depend on $GOARCH?
+
+ objExt := archChar
+
+ if p.Standard && p.ImportPath == "runtime/cgo" {
+ cgoflags = append(cgoflags, "-import_runtime_cgo=false")
+ }
+ if _, ok := buildToolchain.(gccgcToolchain); ok {
+ cgoflags = append(cgoflags, "-gccgo")
+ if prefix := gccgoPrefix(p); prefix != "" {
+ cgoflags = append(cgoflags, "-gccgoprefix="+gccgoPrefix(p))
+ }
+ objExt = "o"
+ }
+ if err := b.run(p.Dir, p.ImportPath, cgoExe, "-objdir", obj, cgoflags, "--", cgoCFLAGS, p.CgoFiles); err != nil {
+ return nil, nil, err
+ }
+ outGo = append(outGo, gofiles...)
+
+ // cc _cgo_defun.c
+ defunObj := obj + "_cgo_defun." + objExt
+ if err := buildToolchain.cc(b, p, obj, defunObj, defunC); err != nil {
+ return nil, nil, err
+ }
+ outObj = append(outObj, defunObj)
+
+ // gcc
+ var linkobj []string
+ for _, cfile := range cfiles {
+ ofile := obj + cfile[:len(cfile)-1] + "o"
+ if err := b.gcc(p, ofile, cgoCFLAGS, obj+cfile); err != nil {
+ return nil, nil, err
+ }
+ linkobj = append(linkobj, ofile)
+ if !strings.HasSuffix(ofile, "_cgo_main.o") {
+ 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 {
+ return nil, nil, err
+ }
+ linkobj = append(linkobj, ofile)
+ outObj = append(outObj, ofile)
+ }
+ dynobj := obj + "_cgo_.o"
+ if err := b.gccld(p, dynobj, cgoLDFLAGS, linkobj); err != nil {
+ return nil, nil, err
+ }
+
+ if _, ok := buildToolchain.(gccgcToolchain); ok {
+ // we don't use dynimport when using gccgo.
+ return outGo, outObj, nil
+ }
+
+ // cgo -dynimport
+ importC := obj + "_cgo_import.c"
+ if err := b.run(p.Dir, p.ImportPath, cgoExe, "-objdir", obj, "-dynimport", dynobj, "-dynout", importC); err != nil {
+ return nil, nil, err
+ }
+
+ // cc _cgo_import.ARCH
+ importObj := obj + "_cgo_import." + objExt
+ if err := buildToolchain.cc(b, p, obj, importObj, importC); err != nil {
+ return nil, nil, err
+ }
+
+ // NOTE(rsc): The importObj is a 5c/6c/8c object and on Windows
+ // must be processed before the gcc-generated objects.
+ // Put it first. http://golang.org/issue/2601
+ outObj = append([]string{importObj}, outObj...)
+
+ return outGo, outObj, nil
+}
+
+// An actionQueue is a priority queue of actions.
+type actionQueue []*action
+
+// Implement heap.Interface
+func (q *actionQueue) Len() int { return len(*q) }
+func (q *actionQueue) Swap(i, j int) { (*q)[i], (*q)[j] = (*q)[j], (*q)[i] }
+func (q *actionQueue) Less(i, j int) bool { return (*q)[i].priority < (*q)[j].priority }
+func (q *actionQueue) Push(x interface{}) { *q = append(*q, x.(*action)) }
+func (q *actionQueue) Pop() interface{} {
+ n := len(*q) - 1
+ x := (*q)[n]
+ *q = (*q)[:n]
+ return x
+}
+
+func (q *actionQueue) push(a *action) {
+ heap.Push(q, a)
+}
+
+func (q *actionQueue) pop() *action {
+ return heap.Pop(q).(*action)
+}
diff --git a/src/cmd/go/clean.go b/src/cmd/go/clean.go
new file mode 100644
index 000000000..773951826
--- /dev/null
+++ b/src/cmd/go/clean.go
@@ -0,0 +1,199 @@
+// 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"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+var cmdClean = &Command{
+ UsageLine: "clean [-i] [-r] [-n] [-x] [packages]",
+ Short: "remove object files",
+ Long: `
+Clean removes object files from package source directories.
+The go command builds most objects in a temporary directory,
+so go clean is mainly concerned with object files left by other
+tools or by manual invocations of go build.
+
+Specifically, clean removes the following files from each of the
+source directories corresponding to the import paths:
+
+ _obj/ old object directory, left from Makefiles
+ _test/ old test directory, left from Makefiles
+ _testmain.go old gotest file, left from Makefiles
+ test.out old test log, left from Makefiles
+ build.out old test log, left from Makefiles
+ *.[568ao] object files, left from Makefiles
+
+ DIR(.exe) from go build
+ DIR.test(.exe) from go test -c
+ MAINFILE(.exe) from go build MAINFILE.go
+
+In the list, DIR represents the final path element of the
+directory, and MAINFILE is the base name of any Go source
+file in the directory that is not included when building
+the package.
+
+The -i flag causes clean to remove the corresponding installed
+archive or binary (what 'go install' would create).
+
+The -n flag causes clean to print the remove commands it would execute,
+but not run them.
+
+The -r flag causes clean to be applied recursively to all the
+dependencies of the packages named by the import paths.
+
+The -x flag causes clean to print remove commands as it executes them.
+
+For more about specifying packages, see 'go help packages'.
+ `,
+}
+
+var cleanI bool // clean -i flag
+var cleanN bool // clean -n flag
+var cleanR bool // clean -r flag
+var cleanX bool // clean -x flag
+
+func init() {
+ // break init cycle
+ cmdClean.Run = runClean
+
+ cmdClean.Flag.BoolVar(&cleanI, "i", false, "")
+ cmdClean.Flag.BoolVar(&cleanN, "n", false, "")
+ cmdClean.Flag.BoolVar(&cleanR, "r", false, "")
+ cmdClean.Flag.BoolVar(&cleanX, "x", false, "")
+}
+
+func runClean(cmd *Command, args []string) {
+ for _, pkg := range packagesAndErrors(args) {
+ clean(pkg)
+ }
+}
+
+var cleaned = map[*Package]bool{}
+
+// TODO: These are dregs left by Makefile-based builds.
+// Eventually, can stop deleting these.
+var cleanDir = map[string]bool{
+ "_test": true,
+ "_obj": true,
+}
+
+var cleanFile = map[string]bool{
+ "_testmain.go": true,
+ "test.out": true,
+ "build.out": true,
+ "a.out": true,
+}
+
+var cleanExt = map[string]bool{
+ ".5": true,
+ ".6": true,
+ ".8": true,
+ ".a": true,
+ ".o": true,
+}
+
+func clean(p *Package) {
+ if cleaned[p] {
+ return
+ }
+ if p.Dir == "" {
+ errorf("can't load package: %v", p.Error)
+ return
+ }
+ dirs, err := ioutil.ReadDir(p.Dir)
+ if err != nil {
+ errorf("go clean %s: %v", p.Dir, err)
+ return
+ }
+
+ var b builder
+ b.print = fmt.Print
+
+ packageFile := map[string]bool{}
+ if p.Name != "main" {
+ // Record which files are not in package main.
+ // The others are.
+ keep := func(list []string) {
+ for _, f := range list {
+ packageFile[f] = true
+ }
+ }
+ keep(p.GoFiles)
+ keep(p.CgoFiles)
+ keep(p.TestGoFiles)
+ keep(p.XTestGoFiles)
+ }
+
+ _, elem := filepath.Split(p.Dir)
+ allRemove := []string{
+ elem,
+ elem + ".exe",
+ elem + ".test",
+ elem + ".test.exe",
+ }
+ for _, dir := range dirs {
+ name := dir.Name()
+ if packageFile[name] {
+ continue
+ }
+ if !dir.IsDir() && strings.HasSuffix(name, ".go") {
+ 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, " "))
+ }
+
+ toRemove := map[string]bool{}
+ for _, name := range allRemove {
+ toRemove[name] = true
+ }
+ for _, dir := range dirs {
+ name := dir.Name()
+ if dir.IsDir() {
+ // TODO: Remove once Makefiles are forgotten.
+ if cleanDir[name] {
+ if cleanN || cleanX {
+ b.showcmd(p.Dir, "rm -r %s", name)
+ if cleanN {
+ continue
+ }
+ }
+ os.RemoveAll(filepath.Join(p.Dir, name))
+ }
+ continue
+ }
+
+ if cleanN {
+ continue
+ }
+
+ if cleanFile[name] || cleanExt[filepath.Ext(name)] || toRemove[name] {
+ os.Remove(filepath.Join(p.Dir, name))
+ }
+ }
+
+ if cleanI && p.target != "" {
+ if cleanN || cleanX {
+ b.showcmd("", "rm -f %s", p.target)
+ }
+ if !cleanN {
+ os.Remove(p.target)
+ }
+ }
+
+ if cleanR {
+ for _, p1 := range p.imports {
+ clean(p1)
+ }
+ }
+}
diff --git a/src/cmd/go/discovery.go b/src/cmd/go/discovery.go
new file mode 100644
index 000000000..d9f930867
--- /dev/null
+++ b/src/cmd/go/discovery.go
@@ -0,0 +1,63 @@
+// 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 !cmd_go_bootstrap
+
+// This code is compiled into the real 'go' binary, but it is not
+// compiled into the binary that is built during all.bash, so as
+// to avoid needing to build net (and thus use cgo) during the
+// bootstrap process.
+
+package main
+
+import (
+ "encoding/xml"
+ "io"
+ "strings"
+)
+
+// 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) {
+ d := xml.NewDecoder(r)
+ d.Strict = false
+ for {
+ t, err := d.Token()
+ if err != nil {
+ return
+ }
+ if e, ok := t.(xml.StartElement); ok && strings.EqualFold(e.Name.Local, "body") {
+ return
+ }
+ if e, ok := t.(xml.EndElement); ok && strings.EqualFold(e.Name.Local, "head") {
+ return
+ }
+ e, ok := t.(xml.StartElement)
+ if !ok || !strings.EqualFold(e.Name.Local, "meta") {
+ continue
+ }
+ if attrValue(e.Attr, "name") != "go-import" {
+ continue
+ }
+ if f := strings.Fields(attrValue(e.Attr, "content")); len(f) == 3 {
+ imports = append(imports, metaImport{
+ Prefix: f[0],
+ VCS: f[1],
+ RepoRoot: f[2],
+ })
+ }
+ }
+ return
+}
+
+// attrValue returns the attribute value for the case-insensitive key
+// `name', or the empty string if nothing is found.
+func attrValue(attrs []xml.Attr, name string) string {
+ for _, a := range attrs {
+ if strings.EqualFold(a.Name.Local, name) {
+ return a.Value
+ }
+ }
+ return ""
+}
diff --git a/src/cmd/go/doc.go b/src/cmd/go/doc.go
new file mode 100644
index 000000000..4bfd5236d
--- /dev/null
+++ b/src/cmd/go/doc.go
@@ -0,0 +1,769 @@
+// 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.
+
+/*
+Go is a tool for managing Go source code.
+
+Usage:
+
+ go command [arguments]
+
+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
+ get download and install packages and dependencies
+ install compile and install packages and dependencies
+ list list packages
+ run compile and run Go program
+ test test packages
+ tool run specified go tool
+ version print Go version
+ vet run go tool vet on packages
+
+Use "go help [command]" for more information about a command.
+
+Additional help topics:
+
+ gopath GOPATH environment variable
+ packages description of package lists
+ remote remote import path syntax
+ testflag description of testing flags
+ testfunc description of testing functions
+
+Use "go help [topic]" for more information about that topic.
+
+
+Compile packages and dependencies
+
+Usage:
+
+ go build [-o output] [build flags] [packages]
+
+Build compiles the packages named by the import paths,
+along with their dependencies, but it does not install the results.
+
+If the arguments are a list of .go files, build treats them as a list
+of source files specifying a single package.
+
+When the command line specifies a single main package,
+build writes the resulting executable to output.
+Otherwise build compiles the packages but discards the results,
+serving only as a check that the packages can be built.
+
+The -o flag specifies the output file name. If not specified, the
+name is packagename.a (for a non-main package) or the base
+name of the first source file (for a main package).
+
+The build flags are shared by the build, install, run, and test commands:
+
+ -a
+ force rebuilding of packages that are already up-to-date.
+ -n
+ print the commands but do not run them.
+ -p n
+ the number of builds that can be run in parallel.
+ The default is the number of CPUs available.
+ -v
+ print the names of packages as they are compiled.
+ -work
+ print the name of the temporary work directory and
+ do not delete it when exiting.
+ -x
+ print the commands.
+
+ -compiler name
+ name of compiler to use, as in runtime.Compiler (gccgo or gc)
+ -gccgoflags 'arg list'
+ arguments to pass on each gccgo compiler/linker invocation
+ -gcflags 'arg list'
+ arguments to pass on each 5g, 6g, or 8g compiler invocation
+ -ldflags 'flag list'
+ arguments to pass on each 5l, 6l, or 8l linker invocation
+ -tags 'tag list'
+ a list of build tags to consider satisfied during the build.
+ See the documentation for the go/build package for
+ more information about build tags.
+
+For more about specifying packages, see 'go help packages'.
+For more about where packages and binaries are installed,
+see 'go help gopath'.
+
+See also: go install, go get, go clean.
+
+
+Remove object files
+
+Usage:
+
+ go clean [-i] [-r] [-n] [-x] [packages]
+
+Clean removes object files from package source directories.
+The go command builds most objects in a temporary directory,
+so go clean is mainly concerned with object files left by other
+tools or by manual invocations of go build.
+
+Specifically, clean removes the following files from each of the
+source directories corresponding to the import paths:
+
+ _obj/ old object directory, left from Makefiles
+ _test/ old test directory, left from Makefiles
+ _testmain.go old gotest file, left from Makefiles
+ test.out old test log, left from Makefiles
+ build.out old test log, left from Makefiles
+ *.[568ao] object files, left from Makefiles
+
+ DIR(.exe) from go build
+ DIR.test(.exe) from go test -c
+ MAINFILE(.exe) from go build MAINFILE.go
+
+In the list, DIR represents the final path element of the
+directory, and MAINFILE is the base name of any Go source
+file in the directory that is not included when building
+the package.
+
+The -i flag causes clean to remove the corresponding installed
+archive or binary (what 'go install' would create).
+
+The -n flag causes clean to print the remove commands it would execute,
+but not run them.
+
+The -r flag causes clean to be applied recursively to all the
+dependencies of the packages named by the import paths.
+
+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 [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'.
+
+To run godoc with specific options, run godoc itself.
+
+See also: go fix, go fmt, go vet.
+
+
+Print Go environment information
+
+Usage:
+
+ go env [var ...]
+
+Env prints Go environment information.
+
+By default env prints information as a shell script
+(on Windows, a batch file). If one or more variable
+names is given as arguments, env prints the value of
+each named variable on its own line.
+
+
+Run go tool fix on packages
+
+Usage:
+
+ go fix [packages]
+
+Fix runs the Go fix command on the packages named by the import paths.
+
+For more about fix, see 'godoc fix'.
+For more about specifying packages, see 'go help packages'.
+
+To run fix with specific options, run 'go tool fix'.
+
+See also: go fmt, go vet.
+
+
+Run gofmt on package sources
+
+Usage:
+
+ go fmt [packages]
+
+Fmt runs the command 'gofmt -l -w' on the packages named
+by the import paths. It prints the names of the files that are modified.
+
+For more about gofmt, see 'godoc gofmt'.
+For more about specifying packages, see 'go help packages'.
+
+To run gofmt with specific options, run gofmt itself.
+
+See also: go doc, go fix, go vet.
+
+
+Download and install packages and dependencies
+
+Usage:
+
+ go get [-a] [-d] [-fix] [-n] [-p n] [-u] [-v] [-x] [packages]
+
+Get downloads and installs the packages named by the import paths,
+along with their dependencies.
+
+The -a, -n, -v, -x, and -p flags have the same meaning as in 'go build'
+and 'go install'. See 'go help build'.
+
+The -d flag instructs get to stop after downloading the packages; that is,
+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 -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.
+
+When checking out or updating a package, get looks for a branch or
+tag that matches the locally installed version of Go. If the local
+version "is release.rNN", it searches for "go.rNN". (For an
+installation using Go version "weekly.YYYY-MM-DD", it searches for a
+package version labeled "go.YYYY-MM-DD".) If the desired version
+cannot be found but others exist with labels in the correct format,
+get retrieves the most recent version before the desired label.
+Finally, if all else fails it 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'.
+
+See also: go build, go install, go clean.
+
+
+Compile and install packages and dependencies
+
+Usage:
+
+ go install [build flags] [packages]
+
+Install compiles and installs the packages named by the import paths,
+along with their dependencies.
+
+For more about the build flags, see 'go help build'.
+For more about specifying packages, see 'go help packages'.
+
+See also: go build, go get, go clean.
+
+
+List packages
+
+Usage:
+
+ go list [-e] [-f format] [-json] [packages]
+
+List lists the packages named by the import paths, one per line.
+
+The default output shows the package import path:
+
+ code.google.com/p/google-api-go-client/books/v1
+ code.google.com/p/goauth2/oauth
+ code.google.com/p/sqlite
+
+The -f flag specifies an alternate format for the list,
+using the syntax of package template. The default output
+is equivalent to -f '{{.ImportPath}}'. The struct
+being passed to the template is:
+
+ type Package struct {
+ Dir string // directory containing package sources
+ ImportPath string // import path of package in dir
+ Name string // package name
+ Doc string // package documentation string
+ Target string // install path
+ Goroot bool // is this package in the Go root?
+ Standard bool // is this package part of the standard Go library?
+ Stale bool // would 'go install' do anything for this package?
+ Root string // Go root or Go path dir containing this package
+
+ // Source files
+ GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
+ CgoFiles []string // .go sources files that import "C"
+ CFiles []string // .c source files
+ HFiles []string // .h source files
+ SFiles []string // .s source files
+ SysoFiles []string // .syso object files to add to archive
+
+ // Cgo directives
+ CgoCFLAGS []string // cgo: flags for C compiler
+ CgoLDFLAGS []string // cgo: flags for linker
+ CgoPkgConfig []string // cgo: pkg-config names
+
+ // Dependency information
+ Imports []string // import paths used by this package
+ Deps []string // all (recursively) imported dependencies
+
+ // Error information
+ Incomplete bool // this package or a dependency has an error
+ Error *PackageError // error loading package
+ DepsErrors []*PackageError // errors loading dependencies
+
+ TestGoFiles []string // _test.go files in package
+ TestImports []string // imports from TestGoFiles
+ XTestGoFiles []string // _test.go files outside package
+ XTestImports []string // imports from XTestGoFiles
+ }
+
+The -json flag causes the package data to be printed in JSON format
+instead of using the template format.
+
+The -e flag changes the handling of erroneous packages, those that
+cannot be found or are malformed. By default, the list command
+prints an error to standard error for each erroneous package and
+omits the packages from consideration during the usual printing.
+With the -e flag, the list command never prints errors to standard
+error and instead processes the erroneous packages with the usual
+printing. Erroneous packages will have a non-empty ImportPath and
+a non-nil Error field; other information may or may not be missing
+(zeroed).
+
+For more about specifying packages, see 'go help packages'.
+
+
+Compile and run Go program
+
+Usage:
+
+ go run [build flags] gofiles... [arguments...]
+
+Run compiles and runs the main package comprising the named Go source files.
+
+For more about build flags, see 'go help build'.
+
+See also: go build.
+
+
+Test packages
+
+Usage:
+
+ go test [-c] [-i] [build 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:
+
+ ok archive/tar 0.011s
+ FAIL archive/zip 0.022s
+ ok compress/gzip 0.033s
+ ...
+
+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.
+
+By default, go test needs no arguments. It compiles and tests the package
+with source in the current directory, including tests, and runs the tests.
+
+The package is built in a temporary directory so it does not interfere with the
+non-test installation.
+
+In addition to the build flags, the flags handled by 'go test' itself are:
+
+ -c Compile the test binary to pkg.test but do not run it.
+
+ -i
+ Install packages that are dependencies of the test.
+ Do not run the test.
+
+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.
+
+For more about build flags, see 'go help build'.
+For more about specifying packages, see 'go help packages'.
+
+See also: go build, go vet.
+
+
+Run specified go tool
+
+Usage:
+
+ go tool [-n] command [args...]
+
+Tool runs the go tool command identified by the arguments.
+With no arguments it prints the list of known tools.
+
+The -n flag causes tool to print the command that would be
+executed but not execute it.
+
+For more about each tool command, see 'go tool command -h'.
+
+
+Print Go version
+
+Usage:
+
+ go version
+
+Version prints the Go version, as reported by runtime.Version.
+
+
+Run go tool vet on packages
+
+Usage:
+
+ go vet [packages]
+
+Vet runs the Go vet command on the packages named by the import paths.
+
+For more about vet, see 'godoc vet'.
+For more about specifying packages, see 'go help packages'.
+
+To run the vet tool with specific options, run 'go tool vet'.
+
+See also: go fmt, go fix.
+
+
+GOPATH environment variable
+
+The Go path is used to resolve import statements.
+It is implemented by and documented in the go/build package.
+
+The GOPATH environment variable lists places to look for Go code.
+On Unix, the value is a colon-separated string.
+On Windows, the value is a semicolon-separated string.
+On Plan 9, the value is a list.
+
+GOPATH must be set to build and install packages outside the
+standard Go tree.
+
+Each directory listed in GOPATH must have a prescribed structure:
+
+The src/ directory holds source code. The path below 'src'
+determines the import path or executable name.
+
+The pkg/ directory holds installed package objects.
+As in the Go tree, each target operating system and
+architecture pair has its own subdirectory of pkg
+(pkg/GOOS_GOARCH).
+
+If DIR is a directory listed in the GOPATH, a package with
+source in DIR/src/foo/bar can be imported as "foo/bar" and
+has its compiled form installed to "DIR/pkg/GOOS_GOARCH/foo/bar.a".
+
+The bin/ directory holds compiled commands.
+Each command is named for its source directory, but only
+the final element, not the entire path. That is, the
+command with source in DIR/src/foo/quux is installed into
+DIR/bin/quux, not DIR/bin/foo/quux. The foo/ is stripped
+so that you can add DIR/bin to your PATH to get at the
+installed commands. If the GOBIN environment variable is
+set, commands are installed to the directory it names instead
+of DIR/bin.
+
+Here's an example directory layout:
+
+ GOPATH=/home/user/gocode
+
+ /home/user/gocode/
+ src/
+ foo/
+ bar/ (go code in package bar)
+ x.go
+ quux/ (go code in package main)
+ y.go
+ bin/
+ quux (installed command)
+ pkg/
+ linux_amd64/
+ foo/
+ bar.a (installed package object)
+
+Go searches each directory listed in GOPATH to find source code,
+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
+
+An import path (see 'go help importpath') denotes a package
+stored in the local file system. Certain import paths also
+describe how to obtain the source code for the package using
+a revision control system.
+
+A few common code hosting sites have special syntax:
+
+ BitBucket (Mercurial)
+
+ import "bitbucket.org/user/project"
+ import "bitbucket.org/user/project/sub/directory"
+
+ GitHub (Git)
+
+ import "github.com/user/project"
+ import "github.com/user/project/sub/directory"
+
+ Google Code Project Hosting (Git, Mercurial, Subversion)
+
+ import "code.google.com/p/project"
+ import "code.google.com/p/project/sub/directory"
+
+ import "code.google.com/p/project.subrepository"
+ import "code.google.com/p/project.subrepository/sub/directory"
+
+ Launchpad (Bazaar)
+
+ import "launchpad.net/project"
+ import "launchpad.net/project/series"
+ import "launchpad.net/project/series/sub/directory"
+
+ import "launchpad.net/~user/project/branch"
+ import "launchpad.net/~user/project/branch/sub/directory"
+
+For code hosted on other servers, import paths may either be qualified
+with the version control type, or the go tool can dynamically fetch
+the import path over https/http and discover where the code resides
+from a <meta> tag in the HTML.
+
+To declare the code location, an import path of the form
+
+ repository.vcs/path
+
+specifies the given repository, with or without the .vcs suffix,
+using the named version control system, and then the path inside
+that repository. The supported version control systems are:
+
+ Bazaar .bzr
+ Git .git
+ Mercurial .hg
+ Subversion .svn
+
+For example,
+
+ import "example.org/user/foo.hg"
+
+denotes the root directory of the Mercurial repository at
+example.org/user/foo or foo.hg, and
+
+ import "example.org/repo.git/foo/bar"
+
+denotes the foo/bar directory of the Git repository at
+example.com/repo or repo.git.
+
+When a version control system supports multiple protocols,
+each is tried in turn when downloading. For example, a Git
+download tries git://, then https://, then http://.
+
+If the import path is not a known code hosting site and also lacks a
+version control qualifier, the go tool attempts to fetch the import
+over https/http and looks for a <meta> tag in the document's HTML
+<head>.
+
+The meta tag has the form:
+
+ <meta name="go-import" content="import-prefix vcs repo-root">
+
+The import-prefix is the import path correponding to the repository
+root. It must be a prefix or an exact match of the package being
+fetched with "go get". If it's not an exact match, another http
+request is made at the prefix to verify the <meta> tags match.
+
+The vcs is one of "git", "hg", "svn", etc,
+
+The repo-root is the root of the version control system
+containing a scheme and not containing a .vcs qualifier.
+
+For example,
+
+ import "example.org/pkg/foo"
+
+will result in the following request(s):
+
+ https://example.org/pkg/foo?go-get=1 (preferred)
+ http://example.org/pkg/foo?go-get=1 (fallback)
+
+If that page contains the meta tag
+
+ <meta name="go-import" content="example.org git https://code.org/r/p/exproj">
+
+the go tool will verify that https://example.org/?go-get=1 contains the
+same meta tag and then git clone https://code.org/r/p/exproj into
+GOPATH/src/example.org.
+
+New downloaded packages are written to the first directory
+listed in the GOPATH environment variable (see 'go help gopath').
+
+The go command attempts to download the version of the
+package appropriate for the Go release being used.
+Run 'go help install' for more.
+
+
+Description of testing flags
+
+The 'go test' command takes both flags that apply to 'go test' itself
+and flags that apply to the resulting test binary.
+
+The test binary, called pkg.test, where pkg is the name of the
+directory containing the package sources, has its own flags:
+
+ -test.v
+ Verbose output: log all tests as they are run.
+
+ -test.run pattern
+ Run only those tests and examples matching the regular
+ expression.
+
+ -test.bench pattern
+ Run benchmarks matching the regular expression.
+ By default, no benchmarks run.
+
+ -test.cpuprofile cpu.out
+ Write a CPU profile to the specified file before exiting.
+
+ -test.memprofile mem.out
+ Write a memory profile to the specified file when all tests
+ are complete.
+
+ -test.memprofilerate n
+ Enable more precise (and expensive) memory profiles by setting
+ runtime.MemProfileRate. See 'godoc runtime MemProfileRate'.
+ To profile all memory allocations, use -test.memprofilerate=1
+ and set the environment variable GOGC=off to disable the
+ garbage collector, provided the test can run in the available
+ memory without garbage collection.
+
+ -test.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
+ simultaneously; by default, it is set to the value of GOMAXPROCS.
+
+ -test.short
+ Tell long-running tests to shorten their run time.
+ It is off by default but set during all.bash so that installing
+ the Go tree can run a sanity check but not spend time running
+ exhaustive tests.
+
+ -test.timeout t
+ If a test runs longer than t, panic.
+
+ -test.benchtime n
+ Run enough iterations of each benchmark to take n seconds.
+ The default is 1 second.
+
+ -test.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
+ of GOMAXPROCS.
+
+For convenience, each of these -test.X flags of the test binary is
+also available as the flag -X in 'go test' itself. Flags not listed
+here are passed through unaltered. For instance, the command
+
+ go test -x -v -cpuprofile=prof.out -dir=testdata -update
+
+will compile the test binary and then run it as
+
+ pkg.test -test.v -test.cpuprofile=prof.out -dir=testdata -update
+
+
+Description of testing functions
+
+The 'go test' command expects to find test, benchmark, and example functions
+in the "*_test.go" files corresponding to the package under test.
+
+A test function is one named TestXXX (where XXX is any alphanumeric string
+not starting with a lower case letter) and should have the signature,
+
+ func TestXXX(t *testing.T) { ... }
+
+A benchmark function is one named BenchmarkXXX and should have the signature,
+
+ func BenchmarkXXX(b *testing.B) { ... }
+
+An example function is similar to a test function but, instead of using *testing.T
+to report success or failure, prints output to os.Stdout and os.Stderr.
+That output is compared against the function's "Output:" comment, which
+must be the last comment in the function body (see example below). An
+example with no such comment, or with no text after "Output:" is compiled
+but not executed.
+
+Godoc displays the body of ExampleXXX to demonstrate the use
+of the function, constant, or variable XXX. An example of a method M with
+receiver type T or *T is named ExampleT_M. There may be multiple examples
+for a given function, constant, or variable, distinguished by a trailing _xxx,
+where xxx is a suffix not beginning with an upper case letter.
+
+Here is an example of an example:
+
+ func ExamplePrintln() {
+ Println("The output of\nthis example.")
+ // Output: The output of
+ // this example.
+ }
+
+The entire test file is presented as the example when it contains a single
+example function, at least one other function, type, variable, or constant
+declaration, and no test or benchmark functions.
+
+See the documentation of the testing package for more information.
+
+
+*/
+package documentation
+
+// NOTE: cmdDoc is in fmt.go.
diff --git a/src/cmd/go/env.go b/src/cmd/go/env.go
new file mode 100644
index 000000000..d5b034809
--- /dev/null
+++ b/src/cmd/go/env.go
@@ -0,0 +1,89 @@
+// 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"
+ "runtime"
+ "strings"
+)
+
+var cmdEnv = &Command{
+ Run: runEnv,
+ UsageLine: "env [var ...]",
+ Short: "print Go environment information",
+ Long: `
+Env prints Go environment information.
+
+By default env prints information as a shell script
+(on Windows, a batch file). If one or more variable
+names is given as arguments, env prints the value of
+each named variable on its own line.
+ `,
+}
+
+type envVar struct {
+ name, value string
+}
+
+func mkEnv() []envVar {
+ var b builder
+ b.init()
+
+ env := []envVar{
+ {"GOROOT", goroot},
+ {"GOBIN", gobin},
+ {"GOARCH", goarch},
+ {"GOCHAR", archChar},
+ {"GOOS", goos},
+ {"GOEXE", exeSuffix},
+ {"GOHOSTARCH", runtime.GOARCH},
+ {"GOHOSTOS", runtime.GOOS},
+ {"GOTOOLDIR", toolDir},
+ {"GOGCCFLAGS", strings.Join(b.gccCmd(".")[3:], " ")},
+ }
+
+ if buildContext.CgoEnabled {
+ env = append(env, envVar{"CGO_ENABLED", "1"})
+ } else {
+ env = append(env, envVar{"CGO_ENABLED", "0"})
+ }
+
+ return env
+}
+
+func findEnv(env []envVar, name string) string {
+ for _, e := range env {
+ if e.name == name {
+ return e.value
+ }
+ }
+ return ""
+}
+
+func runEnv(cmd *Command, args []string) {
+ env := mkEnv()
+ if len(args) > 0 {
+ for _, name := range args {
+ fmt.Printf("%s\n", findEnv(env, name))
+ }
+ return
+ }
+
+ switch runtime.GOOS {
+ default:
+ for _, e := range env {
+ fmt.Printf("%s=\"%s\"\n", e.name, e.value)
+ }
+ case "plan9":
+ for _, e := range env {
+ fmt.Printf("%s='%s'\n", e.name, strings.Replace(e.value, "'", "''", -1))
+ }
+ case "windows":
+ for _, e := range env {
+ fmt.Printf("set %s=%s\n", e.name, e.value)
+ }
+ }
+}
diff --git a/src/cmd/go/fix.go b/src/cmd/go/fix.go
new file mode 100644
index 000000000..ef02b5739
--- /dev/null
+++ b/src/cmd/go/fix.go
@@ -0,0 +1,30 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+var cmdFix = &Command{
+ Run: runFix,
+ UsageLine: "fix [packages]",
+ Short: "run go tool fix on packages",
+ Long: `
+Fix runs the Go fix command on the packages named by the import paths.
+
+For more about fix, see 'godoc fix'.
+For more about specifying packages, see 'go help packages'.
+
+To run fix with specific options, run 'go tool fix'.
+
+See also: go fmt, go vet.
+ `,
+}
+
+func runFix(cmd *Command, args []string) {
+ for _, pkg := range packages(args) {
+ // Use pkg.gofiles instead of pkg.Dir so that
+ // the command only applies to this package,
+ // not to packages in subdirectories.
+ run(stringList(tool("fix"), relPaths(pkg.gofiles)))
+ }
+}
diff --git a/src/cmd/go/fmt.go b/src/cmd/go/fmt.go
new file mode 100644
index 000000000..cea9b0a51
--- /dev/null
+++ b/src/cmd/go/fmt.go
@@ -0,0 +1,58 @@
+// 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
+
+var cmdFmt = &Command{
+ Run: runFmt,
+ UsageLine: "fmt [packages]",
+ Short: "run gofmt on package sources",
+ Long: `
+Fmt runs the command 'gofmt -l -w' on the packages named
+by the import paths. It prints the names of the files that are modified.
+
+For more about gofmt, see 'godoc gofmt'.
+For more about specifying packages, see 'go help packages'.
+
+To run gofmt with specific options, run gofmt itself.
+
+See also: go doc, go fix, go vet.
+ `,
+}
+
+func runFmt(cmd *Command, args []string) {
+ for _, pkg := range packages(args) {
+ // Use pkg.gofiles instead of pkg.Dir so that
+ // the command only applies to this package,
+ // not to packages in subdirectories.
+ run(stringList("gofmt", "-l", "-w", relPaths(pkg.gofiles)))
+ }
+}
+
+var cmdDoc = &Command{
+ Run: runDoc,
+ UsageLine: "doc [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'.
+
+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
+ }
+ run("godoc", pkg.Dir)
+ }
+}
diff --git a/src/cmd/go/get.go b/src/cmd/go/get.go
new file mode 100644
index 000000000..f70b6761d
--- /dev/null
+++ b/src/cmd/go/get.go
@@ -0,0 +1,428 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// TODO: Dashboard upload
+
+package main
+
+import (
+ "fmt"
+ "go/build"
+ "os"
+ "path/filepath"
+ "regexp"
+ "runtime"
+ "strconv"
+ "strings"
+)
+
+var cmdGet = &Command{
+ UsageLine: "get [-a] [-d] [-fix] [-n] [-p n] [-u] [-v] [-x] [packages]",
+ Short: "download and install packages and dependencies",
+ Long: `
+Get downloads and installs the packages named by the import paths,
+along with their dependencies.
+
+The -a, -n, -v, -x, and -p flags have the same meaning as in 'go build'
+and 'go install'. See 'go help build'.
+
+The -d flag instructs get to stop after downloading the packages; that is,
+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 -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.
+
+When checking out or updating a package, get looks for a branch or
+tag that matches the locally installed version of Go. If the local
+version "is release.rNN", it searches for "go.rNN". (For an
+installation using Go version "weekly.YYYY-MM-DD", it searches for a
+package version labeled "go.YYYY-MM-DD".) If the desired version
+cannot be found but others exist with labels in the correct format,
+get retrieves the most recent version before the desired label.
+Finally, if all else fails it 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'.
+
+See also: go build, go install, go clean.
+ `,
+}
+
+var getD = cmdGet.Flag.Bool("d", false, "")
+var getU = cmdGet.Flag.Bool("u", false, "")
+var getFix = cmdGet.Flag.Bool("fix", false, "")
+
+func init() {
+ addBuildFlags(cmdGet)
+ cmdGet.Run = runGet // break init loop
+}
+
+func runGet(cmd *Command, args []string) {
+ // Phase 1. Download/update.
+ var stk importStack
+ for _, arg := range downloadPaths(args) {
+ download(arg, &stk)
+ }
+ exitIfErrors()
+
+ // Phase 2. Rescan packages and reevaluate args list.
+
+ // Code we downloaded and all code that depends on it
+ // needs to be evicted from the package cache so that
+ // the information will be recomputed. Instead of keeping
+ // track of the reverse dependency information, evict
+ // everything.
+ for name := range packageCache {
+ delete(packageCache, name)
+ }
+
+ args = importPaths(args)
+
+ // Phase 3. Install.
+ if *getD {
+ // Download only.
+ // Check delayed until now so that importPaths
+ // has a chance to print errors.
+ return
+ }
+
+ runInstall(cmd, args)
+}
+
+// downloadPath prepares the list of paths to pass to download.
+// It expands ... patterns that can be expanded. If there is no match
+// for a particular pattern, downloadPaths leaves it in the result list,
+// in the hope that we can figure out the repository from the
+// initial ...-free prefix.
+func downloadPaths(args []string) []string {
+ args = importPathsNoDotExpansion(args)
+ var out []string
+ for _, a := range args {
+ if strings.Contains(a, "...") {
+ var expand []string
+ // Use matchPackagesInFS to avoid printing
+ // warnings. They will be printed by the
+ // eventual call to importPaths instead.
+ if build.IsLocalImport(a) {
+ expand = matchPackagesInFS(a)
+ } else {
+ expand = matchPackages(a)
+ }
+ if len(expand) > 0 {
+ out = append(out, expand...)
+ continue
+ }
+ }
+ out = append(out, a)
+ }
+ return out
+}
+
+// downloadCache records the import paths we have already
+// considered during the download, to avoid duplicate work when
+// there is more than one dependency sequence leading to
+// a particular package.
+var downloadCache = map[string]bool{}
+
+// downloadRootCache records the version control repository
+// root directories we have already considered during the download.
+// For example, all the packages in the code.google.com/p/codesearch repo
+// share the same root (the directory for that path), and we only need
+// to run the hg commands to consider each repository once.
+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) {
+ p := loadPackage(arg, stk)
+
+ // There's nothing to do if this is a package in the standard library.
+ if p.Standard {
+ return
+ }
+
+ // Only process each package once.
+ if downloadCache[arg] {
+ return
+ }
+ downloadCache[arg] = true
+
+ pkgs := []*Package{p}
+ wildcardOkay := len(*stk) == 0
+
+ // Download if the package is missing, or update if we're using -u.
+ if p.Dir == "" || *getU {
+ // The actual download.
+ stk.push(p.ImportPath)
+ err := downloadPackage(p)
+ if err != nil {
+ errorf("%s", &PackageError{ImportStack: stk.copy(), Err: err.Error()})
+ stk.pop()
+ return
+ }
+
+ args := []string{arg}
+ // If the argument has a wildcard in it, re-evaluate the wildcard.
+ // We delay this until after reloadPackage so that the old entry
+ // for p has been replaced in the package cache.
+ if wildcardOkay && strings.Contains(arg, "...") {
+ if build.IsLocalImport(arg) {
+ args = matchPackagesInFS(arg)
+ } else {
+ args = matchPackages(arg)
+ }
+ }
+
+ // Clear all relevant package cache entries before
+ // doing any new loads.
+ for _, arg := range args {
+ p := packageCache[arg]
+ if p != nil {
+ delete(packageCache, p.Dir)
+ delete(packageCache, p.ImportPath)
+ }
+ }
+
+ pkgs = pkgs[:0]
+ for _, arg := range args {
+ stk.push(arg)
+ p := loadPackage(arg, stk)
+ stk.pop()
+ if p.Error != nil {
+ errorf("%s", p.Error)
+ continue
+ }
+ pkgs = append(pkgs, p)
+ }
+ }
+
+ // Process package, which might now be multiple packages
+ // due to wildcard expansion.
+ for _, p := range pkgs {
+ if *getFix {
+ run(stringList(tool("fix"), relPaths(p.gofiles)))
+
+ // The imports might have changed, so reload again.
+ p = reloadPackage(arg, stk)
+ if p.Error != nil {
+ errorf("%s", p.Error)
+ return
+ }
+ }
+
+ // Process dependencies, now that we know what they are.
+ for _, dep := range p.deps {
+ download(dep.ImportPath, stk)
+ }
+ }
+}
+
+// downloadPackage runs the create or download command
+// to make the first copy of or update a copy of the given package.
+func downloadPackage(p *Package) error {
+ var (
+ vcs *vcsCmd
+ repo, rootPath string
+ err error
+ )
+ if p.build.SrcRoot != "" {
+ // Directory exists. Look for checkout along path to src.
+ vcs, rootPath, err = vcsForDir(p)
+ if err != nil {
+ return err
+ }
+ repo = "<local>" // should be unused; make distinctive
+ } else {
+ // Analyze the import path to determine the version control system,
+ // repository, and the import path for the root of the repository.
+ rr, err := repoRootForImportPath(p.ImportPath)
+ if err != nil {
+ return err
+ }
+ vcs, repo, rootPath = rr.vcs, rr.repo, rr.root
+ }
+
+ if p.build.SrcRoot == "" {
+ // Package not found. Put in first directory of $GOPATH or else $GOROOT.
+ // Guard against people setting GOPATH=$GOROOT. We have to use
+ // $GOROOT's directory hierarchy (src/pkg, not just src) in that case.
+ if list := filepath.SplitList(buildContext.GOPATH); len(list) > 0 && list[0] != goroot {
+ p.build.SrcRoot = filepath.Join(list[0], "src")
+ p.build.PkgRoot = filepath.Join(list[0], "pkg")
+ } else {
+ p.build.SrcRoot = filepath.Join(goroot, "src", "pkg")
+ p.build.PkgRoot = filepath.Join(goroot, "pkg")
+ }
+ }
+ root := filepath.Join(p.build.SrcRoot, rootPath)
+ // If we've considered this repository already, don't do it again.
+ if downloadRootCache[root] {
+ return nil
+ }
+ downloadRootCache[root] = true
+
+ if buildV {
+ fmt.Fprintf(os.Stderr, "%s (download)\n", rootPath)
+ }
+
+ // Check that this is an appropriate place for the repo to be checked out.
+ // The target directory must either not exist or have a repo checked out already.
+ meta := filepath.Join(root, "."+vcs.cmd)
+ st, err := os.Stat(meta)
+ if err == nil && !st.IsDir() {
+ return fmt.Errorf("%s exists but is not a directory", meta)
+ }
+ if err != nil {
+ // Metadata directory does not exist. Prepare to checkout new copy.
+ // Some version control tools require the target directory not to exist.
+ // We require that too, just to avoid stepping on existing work.
+ if _, err := os.Stat(root); err == nil {
+ return fmt.Errorf("%s exists but %s does not - stale checkout?", root, meta)
+ }
+ // Some version control tools require the parent of the target to exist.
+ parent, _ := filepath.Split(root)
+ if err := os.MkdirAll(parent, 0777); err != nil {
+ return err
+ }
+ if err = vcs.create(root, repo); err != nil {
+ return err
+ }
+ } else {
+ // Metadata directory does exist; download incremental updates.
+ if err = vcs.download(root); err != nil {
+ return err
+ }
+ }
+
+ if buildN {
+ // Do not show tag sync in -n; it's noise more than anything,
+ // and since we're not running commands, no tag will be found.
+ // But avoid printing nothing.
+ fmt.Fprintf(os.Stderr, "# cd %s; %s sync/update\n", root, vcs.cmd)
+ return nil
+ }
+
+ // Select and sync to appropriate version of the repository.
+ tags, err := vcs.tags(root)
+ if err != nil {
+ return err
+ }
+ vers := runtime.Version()
+ if i := strings.Index(vers, " "); i >= 0 {
+ vers = vers[:i]
+ }
+ if err := vcs.tagSync(root, selectTag(vers, tags)); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// goTag matches go release tags such as go1 and go1.2.3.
+// The numbers involved must be small (at most 4 digits),
+// have no unnecessary leading zeros, and the version cannot
+// end in .0 - it is go1, not go1.0 or go1.0.0.
+var goTag = regexp.MustCompile(
+ `^go((0|[1-9][0-9]{0,3})\.)*([1-9][0-9]{0,3})$`,
+)
+
+// selectTag returns the closest matching tag for a given version.
+// Closest means the latest one that is not after the current release.
+// Version "goX" (or "goX.Y" or "goX.Y.Z") matches tags of the same form.
+// Version "release.rN" matches tags of the form "go.rN" (N being a floating-point number).
+// Version "weekly.YYYY-MM-DD" matches tags like "go.weekly.YYYY-MM-DD".
+func selectTag(goVersion string, tags []string) (match string) {
+ const rPrefix = "release.r"
+ if strings.HasPrefix(goVersion, rPrefix) {
+ p := "go.r"
+ v, err := strconv.ParseFloat(goVersion[len(rPrefix):], 64)
+ if err != nil {
+ return ""
+ }
+ var matchf float64
+ for _, t := range tags {
+ if !strings.HasPrefix(t, p) {
+ continue
+ }
+ tf, err := strconv.ParseFloat(t[len(p):], 64)
+ if err != nil {
+ continue
+ }
+ if matchf < tf && tf <= v {
+ match, matchf = t, tf
+ }
+ }
+ }
+
+ const wPrefix = "weekly."
+ if strings.HasPrefix(goVersion, wPrefix) {
+ p := "go.weekly."
+ v := goVersion[len(wPrefix):]
+ for _, t := range tags {
+ if !strings.HasPrefix(t, p) {
+ continue
+ }
+ if match < t && t[len(p):] <= v {
+ match = t
+ }
+ }
+ }
+
+ if goTag.MatchString(goVersion) {
+ v := goVersion
+ for _, t := range tags {
+ if !goTag.MatchString(t) {
+ continue
+ }
+ if cmpGoVersion(match, t) < 0 && cmpGoVersion(t, v) <= 0 {
+ match = t
+ }
+ }
+ }
+
+ return match
+}
+
+// cmpGoVersion returns -1, 0, +1 reporting whether
+// x < y, x == y, or x > y.
+func cmpGoVersion(x, y string) int {
+ // Malformed strings compare less than well-formed strings.
+ if !goTag.MatchString(x) {
+ return -1
+ }
+ if !goTag.MatchString(y) {
+ return +1
+ }
+
+ // Compare numbers in sequence.
+ xx := strings.Split(x[len("go"):], ".")
+ yy := strings.Split(y[len("go"):], ".")
+
+ for i := 0; i < len(xx) && i < len(yy); i++ {
+ // The Atoi are guaranteed to succeed
+ // because the versions match goTag.
+ xi, _ := strconv.Atoi(xx[i])
+ yi, _ := strconv.Atoi(yy[i])
+ if xi < yi {
+ return -1
+ } else if xi > yi {
+ return +1
+ }
+ }
+
+ if len(xx) < len(yy) {
+ return -1
+ }
+ if len(xx) > len(yy) {
+ return +1
+ }
+ return 0
+}
diff --git a/src/cmd/go/help.go b/src/cmd/go/help.go
new file mode 100644
index 000000000..47ea0c711
--- /dev/null
+++ b/src/cmd/go/help.go
@@ -0,0 +1,238 @@
+// 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
+
+var helpPackages = &Command{
+ UsageLine: "packages",
+ Short: "description of package lists",
+ Long: `
+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.
+ `,
+}
+
+var helpRemote = &Command{
+ UsageLine: "remote",
+ Short: "remote import path syntax",
+ Long: `
+
+An import path (see 'go help importpath') denotes a package
+stored in the local file system. Certain import paths also
+describe how to obtain the source code for the package using
+a revision control system.
+
+A few common code hosting sites have special syntax:
+
+ BitBucket (Mercurial)
+
+ import "bitbucket.org/user/project"
+ import "bitbucket.org/user/project/sub/directory"
+
+ GitHub (Git)
+
+ import "github.com/user/project"
+ import "github.com/user/project/sub/directory"
+
+ Google Code Project Hosting (Git, Mercurial, Subversion)
+
+ import "code.google.com/p/project"
+ import "code.google.com/p/project/sub/directory"
+
+ import "code.google.com/p/project.subrepository"
+ import "code.google.com/p/project.subrepository/sub/directory"
+
+ Launchpad (Bazaar)
+
+ import "launchpad.net/project"
+ import "launchpad.net/project/series"
+ import "launchpad.net/project/series/sub/directory"
+
+ import "launchpad.net/~user/project/branch"
+ import "launchpad.net/~user/project/branch/sub/directory"
+
+For code hosted on other servers, import paths may either be qualified
+with the version control type, or the go tool can dynamically fetch
+the import path over https/http and discover where the code resides
+from a <meta> tag in the HTML.
+
+To declare the code location, an import path of the form
+
+ repository.vcs/path
+
+specifies the given repository, with or without the .vcs suffix,
+using the named version control system, and then the path inside
+that repository. The supported version control systems are:
+
+ Bazaar .bzr
+ Git .git
+ Mercurial .hg
+ Subversion .svn
+
+For example,
+
+ import "example.org/user/foo.hg"
+
+denotes the root directory of the Mercurial repository at
+example.org/user/foo or foo.hg, and
+
+ import "example.org/repo.git/foo/bar"
+
+denotes the foo/bar directory of the Git repository at
+example.com/repo or repo.git.
+
+When a version control system supports multiple protocols,
+each is tried in turn when downloading. For example, a Git
+download tries git://, then https://, then http://.
+
+If the import path is not a known code hosting site and also lacks a
+version control qualifier, the go tool attempts to fetch the import
+over https/http and looks for a <meta> tag in the document's HTML
+<head>.
+
+The meta tag has the form:
+
+ <meta name="go-import" content="import-prefix vcs repo-root">
+
+The import-prefix is the import path correponding to the repository
+root. It must be a prefix or an exact match of the package being
+fetched with "go get". If it's not an exact match, another http
+request is made at the prefix to verify the <meta> tags match.
+
+The vcs is one of "git", "hg", "svn", etc,
+
+The repo-root is the root of the version control system
+containing a scheme and not containing a .vcs qualifier.
+
+For example,
+
+ import "example.org/pkg/foo"
+
+will result in the following request(s):
+
+ https://example.org/pkg/foo?go-get=1 (preferred)
+ http://example.org/pkg/foo?go-get=1 (fallback)
+
+If that page contains the meta tag
+
+ <meta name="go-import" content="example.org git https://code.org/r/p/exproj">
+
+the go tool will verify that https://example.org/?go-get=1 contains the
+same meta tag and then git clone https://code.org/r/p/exproj into
+GOPATH/src/example.org.
+
+New downloaded packages are written to the first directory
+listed in the GOPATH environment variable (see 'go help gopath').
+
+The go command attempts to download the version of the
+package appropriate for the Go release being used.
+Run 'go help install' for more.
+ `,
+}
+
+var helpGopath = &Command{
+ UsageLine: "gopath",
+ Short: "GOPATH environment variable",
+ Long: `
+The Go path is used to resolve import statements.
+It is implemented by and documented in the go/build package.
+
+The GOPATH environment variable lists places to look for Go code.
+On Unix, the value is a colon-separated string.
+On Windows, the value is a semicolon-separated string.
+On Plan 9, the value is a list.
+
+GOPATH must be set to build and install packages outside the
+standard Go tree.
+
+Each directory listed in GOPATH must have a prescribed structure:
+
+The src/ directory holds source code. The path below 'src'
+determines the import path or executable name.
+
+The pkg/ directory holds installed package objects.
+As in the Go tree, each target operating system and
+architecture pair has its own subdirectory of pkg
+(pkg/GOOS_GOARCH).
+
+If DIR is a directory listed in the GOPATH, a package with
+source in DIR/src/foo/bar can be imported as "foo/bar" and
+has its compiled form installed to "DIR/pkg/GOOS_GOARCH/foo/bar.a".
+
+The bin/ directory holds compiled commands.
+Each command is named for its source directory, but only
+the final element, not the entire path. That is, the
+command with source in DIR/src/foo/quux is installed into
+DIR/bin/quux, not DIR/bin/foo/quux. The foo/ is stripped
+so that you can add DIR/bin to your PATH to get at the
+installed commands. If the GOBIN environment variable is
+set, commands are installed to the directory it names instead
+of DIR/bin.
+
+Here's an example directory layout:
+
+ GOPATH=/home/user/gocode
+
+ /home/user/gocode/
+ src/
+ foo/
+ bar/ (go code in package bar)
+ x.go
+ quux/ (go code in package main)
+ y.go
+ bin/
+ quux (installed command)
+ pkg/
+ linux_amd64/
+ foo/
+ bar.a (installed package object)
+
+Go searches each directory listed in GOPATH to find source code,
+but new packages are always downloaded into the first directory
+in the list.
+ `,
+}
diff --git a/src/cmd/go/http.go b/src/cmd/go/http.go
new file mode 100644
index 000000000..6de9a3e1e
--- /dev/null
+++ b/src/cmd/go/http.go
@@ -0,0 +1,87 @@
+// 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 !cmd_go_bootstrap
+
+// This code is compiled into the real 'go' binary, but it is not
+// compiled into the binary that is built during all.bash, so as
+// to avoid needing to build net (and thus use cgo) during the
+// bootstrap process.
+
+package main
+
+import (
+ "fmt"
+ "io"
+ "io/ioutil"
+ "log"
+ "net/http"
+ "net/url"
+)
+
+// httpGET returns the data from an HTTP GET request for the given URL.
+func httpGET(url string) ([]byte, error) {
+ resp, err := http.Get(url)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+ if resp.StatusCode != 200 {
+ return nil, fmt.Errorf("%s: %s", url, resp.Status)
+ }
+ b, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return nil, fmt.Errorf("%s: %v", url, err)
+ }
+ return b, nil
+}
+
+// httpClient is the default HTTP client, but a variable so it can be
+// changed by tests, without modifying http.DefaultClient.
+var httpClient = http.DefaultClient
+
+// httpsOrHTTP returns the body of either the importPath's
+// https resource or, if unavailable, the http resource.
+func httpsOrHTTP(importPath string) (urlStr string, body io.ReadCloser, err error) {
+ fetch := func(scheme string) (urlStr string, res *http.Response, err error) {
+ u, err := url.Parse(scheme + "://" + importPath)
+ if err != nil {
+ return "", nil, err
+ }
+ u.RawQuery = "go-get=1"
+ urlStr = u.String()
+ if buildV {
+ log.Printf("Fetching %s", urlStr)
+ }
+ res, err = httpClient.Get(urlStr)
+ return
+ }
+ closeBody := func(res *http.Response) {
+ if res != nil {
+ res.Body.Close()
+ }
+ }
+ urlStr, res, err := fetch("https")
+ if err != nil || res.StatusCode != 200 {
+ if buildV {
+ if err != nil {
+ log.Printf("https fetch failed.")
+ } else {
+ log.Printf("ignoring https fetch with status code %d", res.StatusCode)
+ }
+ }
+ closeBody(res)
+ urlStr, res, err = fetch("http")
+ }
+ if err != nil {
+ closeBody(res)
+ return "", nil, err
+ }
+ // Note: accepting a non-200 OK here, so people can serve a
+ // meta import in their http 404 page.
+ if buildV {
+ log.Printf("Parsing meta tags from %s (status code %d)", urlStr, res.StatusCode)
+ }
+ return urlStr, res.Body, nil
+}
diff --git a/src/cmd/go/list.go b/src/cmd/go/list.go
new file mode 100644
index 000000000..edb59aa79
--- /dev/null
+++ b/src/cmd/go/list.go
@@ -0,0 +1,168 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "bufio"
+ "encoding/json"
+ "io"
+ "os"
+ "text/template"
+)
+
+var cmdList = &Command{
+ UsageLine: "list [-e] [-f format] [-json] [packages]",
+ Short: "list packages",
+ Long: `
+List lists the packages named by the import paths, one per line.
+
+The default output shows the package import path:
+
+ code.google.com/p/google-api-go-client/books/v1
+ code.google.com/p/goauth2/oauth
+ code.google.com/p/sqlite
+
+The -f flag specifies an alternate format for the list,
+using the syntax of package template. The default output
+is equivalent to -f '{{.ImportPath}}'. The struct
+being passed to the template is:
+
+ type Package struct {
+ Dir string // directory containing package sources
+ ImportPath string // import path of package in dir
+ Name string // package name
+ Doc string // package documentation string
+ Target string // install path
+ Goroot bool // is this package in the Go root?
+ Standard bool // is this package part of the standard Go library?
+ Stale bool // would 'go install' do anything for this package?
+ Root string // Go root or Go path dir containing this package
+
+ // Source files
+ GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
+ CgoFiles []string // .go sources files that import "C"
+ CFiles []string // .c source files
+ HFiles []string // .h source files
+ SFiles []string // .s source files
+ SysoFiles []string // .syso object files to add to archive
+
+ // Cgo directives
+ CgoCFLAGS []string // cgo: flags for C compiler
+ CgoLDFLAGS []string // cgo: flags for linker
+ CgoPkgConfig []string // cgo: pkg-config names
+
+ // Dependency information
+ Imports []string // import paths used by this package
+ Deps []string // all (recursively) imported dependencies
+
+ // Error information
+ Incomplete bool // this package or a dependency has an error
+ Error *PackageError // error loading package
+ DepsErrors []*PackageError // errors loading dependencies
+
+ TestGoFiles []string // _test.go files in package
+ TestImports []string // imports from TestGoFiles
+ XTestGoFiles []string // _test.go files outside package
+ XTestImports []string // imports from XTestGoFiles
+ }
+
+The -json flag causes the package data to be printed in JSON format
+instead of using the template format.
+
+The -e flag changes the handling of erroneous packages, those that
+cannot be found or are malformed. By default, the list command
+prints an error to standard error for each erroneous package and
+omits the packages from consideration during the usual printing.
+With the -e flag, the list command never prints errors to standard
+error and instead processes the erroneous packages with the usual
+printing. Erroneous packages will have a non-empty ImportPath and
+a non-nil Error field; other information may or may not be missing
+(zeroed).
+
+For more about specifying packages, see 'go help packages'.
+ `,
+}
+
+func init() {
+ cmdList.Run = runList // break init cycle
+ cmdList.Flag.Var(buildCompiler{}, "compiler", "")
+}
+
+var listE = cmdList.Flag.Bool("e", false, "")
+var listFmt = cmdList.Flag.String("f", "{{.ImportPath}}", "")
+var listJson = cmdList.Flag.Bool("json", false, "")
+var nl = []byte{'\n'}
+
+func runList(cmd *Command, args []string) {
+ out := newCountingWriter(os.Stdout)
+ defer out.w.Flush()
+
+ var do func(*Package)
+ if *listJson {
+ do = func(p *Package) {
+ b, err := json.MarshalIndent(p, "", "\t")
+ if err != nil {
+ out.Flush()
+ fatalf("%s", err)
+ }
+ out.Write(b)
+ out.Write(nl)
+ }
+ } else {
+ tmpl, err := template.New("main").Parse(*listFmt)
+ if err != nil {
+ fatalf("%s", err)
+ }
+ do = func(p *Package) {
+ out.Reset()
+ if err := tmpl.Execute(out, p); err != nil {
+ out.Flush()
+ fatalf("%s", err)
+ }
+ if out.Count() > 0 {
+ out.w.WriteRune('\n')
+ }
+ }
+ }
+
+ load := packages
+ if *listE {
+ load = packagesAndErrors
+ }
+
+ for _, pkg := range load(args) {
+ do(pkg)
+ }
+}
+
+// CountingWriter counts its data, so we can avoid appending a newline
+// if there was no actual output.
+type CountingWriter struct {
+ w *bufio.Writer
+ count int64
+}
+
+func newCountingWriter(w io.Writer) *CountingWriter {
+ return &CountingWriter{
+ w: bufio.NewWriter(w),
+ }
+}
+
+func (cw *CountingWriter) Write(p []byte) (n int, err error) {
+ cw.count += int64(len(p))
+ return cw.w.Write(p)
+}
+
+func (cw *CountingWriter) Flush() {
+ cw.w.Flush()
+}
+
+func (cw *CountingWriter) Reset() {
+ cw.count = 0
+}
+
+func (cw *CountingWriter) Count() int64 {
+ return cw.count
+}
diff --git a/src/cmd/go/main.go b/src/cmd/go/main.go
new file mode 100644
index 000000000..73c2f54a7
--- /dev/null
+++ b/src/cmd/go/main.go
@@ -0,0 +1,541 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "bytes"
+ "flag"
+ "fmt"
+ "go/build"
+ "io"
+ "log"
+ "os"
+ "os/exec"
+ "path"
+ "path/filepath"
+ "regexp"
+ "runtime"
+ "strings"
+ "sync"
+ "text/template"
+ "unicode"
+ "unicode/utf8"
+)
+
+// A Command is an implementation of a go command
+// like go build or go fix.
+type Command struct {
+ // Run runs the command.
+ // The args are the arguments after the command name.
+ Run func(cmd *Command, args []string)
+
+ // UsageLine is the one-line usage message.
+ // The first word in the line is taken to be the command name.
+ UsageLine string
+
+ // Short is the short description shown in the 'go help' output.
+ Short string
+
+ // Long is the long message shown in the 'go help <this-command>' output.
+ Long string
+
+ // Flag is a set of flags specific to this command.
+ Flag flag.FlagSet
+
+ // CustomFlags indicates that the command will do its own
+ // flag parsing.
+ CustomFlags bool
+}
+
+// Name returns the command's name: the first word in the usage line.
+func (c *Command) Name() string {
+ name := c.UsageLine
+ i := strings.Index(name, " ")
+ if i >= 0 {
+ name = name[:i]
+ }
+ return name
+}
+
+func (c *Command) Usage() {
+ fmt.Fprintf(os.Stderr, "usage: %s\n\n", c.UsageLine)
+ fmt.Fprintf(os.Stderr, "%s\n", strings.TrimSpace(c.Long))
+ os.Exit(2)
+}
+
+// Runnable reports whether the command can be run; otherwise
+// it is a documentation pseudo-command such as importpath.
+func (c *Command) Runnable() bool {
+ return c.Run != nil
+}
+
+// Commands lists the available commands and help topics.
+// The order here is the order in which they are printed by 'go help'.
+var commands = []*Command{
+ cmdBuild,
+ cmdClean,
+ cmdDoc,
+ cmdEnv,
+ cmdFix,
+ cmdFmt,
+ cmdGet,
+ cmdInstall,
+ cmdList,
+ cmdRun,
+ cmdTest,
+ cmdTool,
+ cmdVersion,
+ cmdVet,
+
+ helpGopath,
+ helpPackages,
+ helpRemote,
+ helpTestflag,
+ helpTestfunc,
+}
+
+var exitStatus = 0
+var exitMu sync.Mutex
+
+func setExitStatus(n int) {
+ exitMu.Lock()
+ if exitStatus < n {
+ exitStatus = n
+ }
+ exitMu.Unlock()
+}
+
+func main() {
+ flag.Usage = usage
+ flag.Parse()
+ log.SetFlags(0)
+
+ args := flag.Args()
+ if len(args) < 1 {
+ usage()
+ }
+
+ if args[0] == "help" {
+ help(args[1:])
+ return
+ }
+
+ // Diagnose common mistake: GOPATH==GOROOT.
+ // This setting is equivalent to not setting GOPATH at all,
+ // which is not what most people want when they do it.
+ if gopath := os.Getenv("GOPATH"); gopath == runtime.GOROOT() {
+ fmt.Fprintf(os.Stderr, "warning: GOPATH set to GOROOT (%s) has no effect\n", gopath)
+ }
+
+ for _, cmd := range commands {
+ if cmd.Name() == args[0] && cmd.Run != nil {
+ cmd.Flag.Usage = func() { cmd.Usage() }
+ if cmd.CustomFlags {
+ args = args[1:]
+ } else {
+ cmd.Flag.Parse(args[1:])
+ args = cmd.Flag.Args()
+ }
+ cmd.Run(cmd, args)
+ exit()
+ return
+ }
+ }
+
+ fmt.Fprintf(os.Stderr, "Unknown command %#q\n\n", args[0])
+ usage()
+}
+
+var usageTemplate = `Go is a tool for managing Go source code.
+
+Usage:
+
+ go command [arguments]
+
+The commands are:
+{{range .}}{{if .Runnable}}
+ {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}}
+
+Use "go help [command]" for more information about a command.
+
+Additional help topics:
+{{range .}}{{if not .Runnable}}
+ {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}}
+
+Use "go help [topic]" for more information about that topic.
+
+`
+
+var helpTemplate = `{{if .Runnable}}usage: go {{.UsageLine}}
+
+{{end}}{{.Long | trim}}
+`
+
+var documentationTemplate = `// 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.
+
+/*
+{{range .}}{{if .Short}}{{.Short | capitalize}}
+
+{{end}}{{if .Runnable}}Usage:
+
+ go {{.UsageLine}}
+
+{{end}}{{.Long | trim}}
+
+
+{{end}}*/
+package documentation
+
+// NOTE: cmdDoc is in fmt.go.
+`
+
+// tmpl executes the given template text on data, writing the result to w.
+func tmpl(w io.Writer, text string, data interface{}) {
+ t := template.New("top")
+ t.Funcs(template.FuncMap{"trim": strings.TrimSpace, "capitalize": capitalize})
+ template.Must(t.Parse(text))
+ if err := t.Execute(w, data); err != nil {
+ panic(err)
+ }
+}
+
+func capitalize(s string) string {
+ if s == "" {
+ return s
+ }
+ r, n := utf8.DecodeRuneInString(s)
+ return string(unicode.ToTitle(r)) + s[n:]
+}
+
+func printUsage(w io.Writer) {
+ tmpl(w, usageTemplate, commands)
+}
+
+func usage() {
+ printUsage(os.Stderr)
+ os.Exit(2)
+}
+
+// help implements the 'help' command.
+func help(args []string) {
+ if len(args) == 0 {
+ printUsage(os.Stdout)
+ // not exit 2: succeeded at 'go help'.
+ return
+ }
+ if len(args) != 1 {
+ fmt.Fprintf(os.Stderr, "usage: go help command\n\nToo many arguments given.\n")
+ os.Exit(2) // failed at 'go help'
+ }
+
+ arg := args[0]
+
+ // 'go help documentation' generates doc.go.
+ if arg == "documentation" {
+ buf := new(bytes.Buffer)
+ printUsage(buf)
+ usage := &Command{Long: buf.String()}
+ tmpl(os.Stdout, documentationTemplate, append([]*Command{usage}, commands...))
+ return
+ }
+
+ for _, cmd := range commands {
+ if cmd.Name() == arg {
+ tmpl(os.Stdout, helpTemplate, cmd)
+ // not exit 2: succeeded at 'go help cmd'.
+ return
+ }
+ }
+
+ fmt.Fprintf(os.Stderr, "Unknown help topic %#q. Run 'go help'.\n", arg)
+ os.Exit(2) // failed at 'go help cmd'
+}
+
+// importPathsNoDotExpansion returns the import paths to use for the given
+// command line, but it does no ... expansion.
+func importPathsNoDotExpansion(args []string) []string {
+ if len(args) == 0 {
+ return []string{"."}
+ }
+ var out []string
+ for _, a := range args {
+ // Arguments are supposed to be import paths, but
+ // as a courtesy to Windows developers, rewrite \ to /
+ // in command-line arguments. Handles .\... and so on.
+ if filepath.Separator == '\\' {
+ a = strings.Replace(a, `\`, `/`, -1)
+ }
+
+ // Put argument in canonical form, but preserve leading ./.
+ if strings.HasPrefix(a, "./") {
+ a = "./" + path.Clean(a)
+ if a == "./." {
+ a = "."
+ }
+ } else {
+ a = path.Clean(a)
+ }
+ if a == "all" || a == "std" {
+ out = append(out, allPackages(a)...)
+ continue
+ }
+ out = append(out, a)
+ }
+ return out
+}
+
+// importPaths returns the import paths to use for the given command line.
+func importPaths(args []string) []string {
+ args = importPathsNoDotExpansion(args)
+ var out []string
+ for _, a := range args {
+ if strings.Contains(a, "...") {
+ if build.IsLocalImport(a) {
+ out = append(out, allPackagesInFS(a)...)
+ } else {
+ out = append(out, allPackages(a)...)
+ }
+ continue
+ }
+ out = append(out, a)
+ }
+ return out
+}
+
+var atexitFuncs []func()
+
+func atexit(f func()) {
+ atexitFuncs = append(atexitFuncs, f)
+}
+
+func exit() {
+ for _, f := range atexitFuncs {
+ f()
+ }
+ os.Exit(exitStatus)
+}
+
+func fatalf(format string, args ...interface{}) {
+ errorf(format, args...)
+ exit()
+}
+
+func errorf(format string, args ...interface{}) {
+ log.Printf(format, args...)
+ setExitStatus(1)
+}
+
+var logf = log.Printf
+
+func exitIfErrors() {
+ if exitStatus != 0 {
+ exit()
+ }
+}
+
+func run(cmdargs ...interface{}) {
+ cmdline := stringList(cmdargs...)
+ cmd := exec.Command(cmdline[0], cmdline[1:]...)
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ if err := cmd.Run(); err != nil {
+ errorf("%v", err)
+ }
+}
+
+func runOut(dir string, cmdargs ...interface{}) []byte {
+ cmdline := stringList(cmdargs...)
+ cmd := exec.Command(cmdline[0], cmdline[1:]...)
+ cmd.Dir = dir
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ os.Stderr.Write(out)
+ errorf("%v", err)
+ out = nil
+ }
+ return out
+}
+
+// matchPattern(pattern)(name) reports whether
+// name matches pattern. Pattern is a limited glob
+// pattern in which '...' means 'any string' and there
+// is no other special syntax.
+func matchPattern(pattern string) func(name string) bool {
+ re := regexp.QuoteMeta(pattern)
+ re = strings.Replace(re, `\.\.\.`, `.*`, -1)
+ // Special case: foo/... matches foo too.
+ if strings.HasSuffix(re, `/.*`) {
+ re = re[:len(re)-len(`/.*`)] + `(/.*)?`
+ }
+ reg := regexp.MustCompile(`^` + re + `$`)
+ return func(name string) bool {
+ return reg.MatchString(name)
+ }
+}
+
+// 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)
+// or a path including "...".
+func allPackages(pattern string) []string {
+ pkgs := matchPackages(pattern)
+ if len(pkgs) == 0 {
+ fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
+ }
+ return pkgs
+}
+
+func matchPackages(pattern string) []string {
+ match := func(string) bool { return true }
+ if pattern != "all" && pattern != "std" {
+ match = matchPattern(pattern)
+ }
+
+ have := map[string]bool{
+ "builtin": true, // ignore pseudo-package that exists only for documentation
+ }
+ if !buildContext.CgoEnabled {
+ have["runtime/cgo"] = true // ignore during walk
+ }
+ var pkgs []string
+
+ // Commands
+ cmd := filepath.Join(goroot, "src/cmd") + string(filepath.Separator)
+ filepath.Walk(cmd, func(path string, fi os.FileInfo, err error) error {
+ if err != nil || !fi.IsDir() || path == cmd {
+ return nil
+ }
+ name := path[len(cmd):]
+ // Commands are all in cmd/, not in subdirectories.
+ if strings.Contains(name, string(filepath.Separator)) {
+ return filepath.SkipDir
+ }
+
+ _, err = build.ImportDir(path, 0)
+ if err != nil {
+ return nil
+ }
+
+ // We use, e.g., cmd/gofmt as the pseudo import path for gofmt.
+ name = "cmd/" + name
+ if !have[name] {
+ have[name] = true
+ if match(name) {
+ pkgs = append(pkgs, name)
+ }
+ }
+ return nil
+ })
+
+ for _, src := range buildContext.SrcDirs() {
+ if pattern == "std" && src != gorootSrcPkg {
+ continue
+ }
+ src = filepath.Clean(src) + string(filepath.Separator)
+ filepath.Walk(src, func(path string, fi os.FileInfo, err error) error {
+ if err != nil || !fi.IsDir() || path == src {
+ return nil
+ }
+
+ // Avoid .foo, _foo, and testdata directory trees.
+ _, elem := filepath.Split(path)
+ if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" {
+ return filepath.SkipDir
+ }
+
+ name := filepath.ToSlash(path[len(src):])
+ if pattern == "std" && strings.Contains(name, ".") {
+ return filepath.SkipDir
+ }
+ if have[name] {
+ return nil
+ }
+ have[name] = true
+
+ _, err = build.ImportDir(path, 0)
+ if err != nil && strings.Contains(err.Error(), "no Go source files") {
+ return nil
+ }
+ if match(name) {
+ pkgs = append(pkgs, name)
+ }
+ return nil
+ })
+ }
+ return pkgs
+}
+
+// allPackagesInFS is like allPackages but is passed a pattern
+// beginning ./ or ../, meaning it should scan the tree rooted
+// at the given directory. There are ... in the pattern too.
+func allPackagesInFS(pattern string) []string {
+ pkgs := matchPackagesInFS(pattern)
+ if len(pkgs) == 0 {
+ fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
+ }
+ return pkgs
+}
+
+func matchPackagesInFS(pattern string) []string {
+ // Find directory to begin the scan.
+ // Could be smarter but this one optimization
+ // is enough for now, since ... is usually at the
+ // end of a path.
+ i := strings.Index(pattern, "...")
+ dir, _ := path.Split(pattern[:i])
+
+ // pattern begins with ./ or ../.
+ // path.Clean will discard the ./ but not the ../.
+ // We need to preserve the ./ for pattern matching
+ // and in the returned import paths.
+ prefix := ""
+ if strings.HasPrefix(pattern, "./") {
+ prefix = "./"
+ }
+ match := matchPattern(pattern)
+
+ var pkgs []string
+ filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error {
+ if err != nil || !fi.IsDir() || path == dir {
+ return nil
+ }
+
+ // Avoid .foo, _foo, and testdata directory trees.
+ _, elem := filepath.Split(path)
+ if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" {
+ return filepath.SkipDir
+ }
+
+ name := prefix + filepath.ToSlash(path)
+ if !match(name) {
+ return nil
+ }
+ if _, err = build.ImportDir(path, 0); err != nil {
+ return nil
+ }
+ pkgs = append(pkgs, name)
+ return nil
+ })
+ return pkgs
+}
+
+// stringList's arguments should be a sequence of string or []string values.
+// stringList flattens them into a single []string.
+func stringList(args ...interface{}) []string {
+ var x []string
+ for _, arg := range args {
+ switch arg := arg.(type) {
+ case []string:
+ x = append(x, arg...)
+ case string:
+ x = append(x, arg)
+ default:
+ panic("stringList: invalid argument")
+ }
+ }
+ return x
+}
diff --git a/src/cmd/go/match_test.go b/src/cmd/go/match_test.go
new file mode 100644
index 000000000..f058f235a
--- /dev/null
+++ b/src/cmd/go/match_test.go
@@ -0,0 +1,36 @@
+// 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 "testing"
+
+var matchTests = []struct {
+ pattern string
+ path string
+ match bool
+}{
+ {"...", "foo", true},
+ {"net", "net", true},
+ {"net", "net/http", false},
+ {"net/http", "net", false},
+ {"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},
+}
+
+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)
+ }
+ }
+}
diff --git a/src/cmd/go/mkdoc.sh b/src/cmd/go/mkdoc.sh
new file mode 100755
index 000000000..7768baeb6
--- /dev/null
+++ b/src/cmd/go/mkdoc.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+# 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.
+
+go help documentation > doc.go
+gofmt -w doc.go
+
diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go
new file mode 100644
index 000000000..30bbfad55
--- /dev/null
+++ b/src/cmd/go/pkg.go
@@ -0,0 +1,679 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "go/build"
+ "go/scanner"
+ "go/token"
+ "os"
+ pathpkg "path"
+ "path/filepath"
+ "sort"
+ "strings"
+ "time"
+ "unicode"
+)
+
+// A Package describes a single package found in a directory.
+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
+
+ // Source files
+ GoFiles []string `json:",omitempty"` // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
+ CgoFiles []string `json:",omitempty"` // .go sources files that import "C"
+ CFiles []string `json:",omitempty"` // .c source files
+ HFiles []string `json:",omitempty"` // .h source files
+ SFiles []string `json:",omitempty"` // .s source files
+ SysoFiles []string `json:",omitempty"` // .syso system object files added to package
+
+ // Cgo directives
+ CgoCFLAGS []string `json:",omitempty"` // cgo: flags for C compiler
+ CgoLDFLAGS []string `json:",omitempty"` // cgo: flags for linker
+ CgoPkgConfig []string `json:",omitempty"` // cgo: pkg-config names
+
+ // Dependency information
+ Imports []string `json:",omitempty"` // import paths used by this package
+ Deps []string `json:",omitempty"` // all (recursively) imported dependencies
+
+ // Error information
+ Incomplete bool `json:",omitempty"` // was there an error loading this package or dependencies?
+ Error *PackageError `json:",omitempty"` // error loading this package (not dependencies)
+ DepsErrors []*PackageError `json:",omitempty"` // errors loading dependencies
+
+ // Test information
+ TestGoFiles []string `json:",omitempty"` // _test.go files in package
+ TestImports []string `json:",omitempty"` // imports from TestGoFiles
+ XTestGoFiles []string `json:",omitempty"` // _test.go files outside package
+ XTestImports []string `json:",omitempty"` // imports from XTestGoFiles
+
+ // Unexported fields are not part of the public API.
+ build *build.Package
+ pkgdir string // overrides build.PkgDir
+ imports []*Package
+ deps []*Package
+ gofiles []string // GoFiles+CgoFiles+TestGoFiles+XTestGoFiles files, 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
+}
+
+func (p *Package) copyBuild(pp *build.Package) {
+ p.build = pp
+
+ p.Dir = pp.Dir
+ p.ImportPath = pp.ImportPath
+ p.Name = pp.Name
+ p.Doc = pp.Doc
+ p.Root = pp.Root
+ // TODO? Target
+ p.Goroot = pp.Goroot
+ p.Standard = p.Goroot && p.ImportPath != "" && !strings.Contains(p.ImportPath, ".")
+ p.GoFiles = pp.GoFiles
+ p.CgoFiles = pp.CgoFiles
+ p.CFiles = pp.CFiles
+ p.HFiles = pp.HFiles
+ p.SFiles = pp.SFiles
+ p.SysoFiles = pp.SysoFiles
+ p.CgoCFLAGS = pp.CgoCFLAGS
+ p.CgoLDFLAGS = pp.CgoLDFLAGS
+ p.CgoPkgConfig = pp.CgoPkgConfig
+ p.Imports = pp.Imports
+ p.TestGoFiles = pp.TestGoFiles
+ p.TestImports = pp.TestImports
+ p.XTestGoFiles = pp.XTestGoFiles
+ p.XTestImports = pp.XTestImports
+}
+
+// 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
+}
+
+func (p *PackageError) Error() string {
+ if p.Pos != "" {
+ // Omit import stack. The full path to the file where the error
+ // is the most important thing.
+ return p.Pos + ": " + p.Err
+ }
+ return "package " + strings.Join(p.ImportStack, "\n\timports ") + ": " + p.Err
+}
+
+// An importStack is a stack of import paths.
+type importStack []string
+
+func (s *importStack) push(p string) {
+ *s = append(*s, p)
+}
+
+func (s *importStack) pop() {
+ *s = (*s)[0 : len(*s)-1]
+}
+
+func (s *importStack) copy() []string {
+ return append([]string{}, *s...)
+}
+
+// shorterThan returns true if sp is shorter than t.
+// We use this to record the shortest import sequence
+// that leads to a particular package.
+func (sp *importStack) shorterThan(t []string) bool {
+ s := *sp
+ if len(s) != len(t) {
+ return len(s) < len(t)
+ }
+ // If they are the same length, settle ties using string ordering.
+ for i := range s {
+ if s[i] != t[i] {
+ return s[i] < t[i]
+ }
+ }
+ return false // they are equal
+}
+
+// packageCache is a lookup cache for loadPackage,
+// so that if we look up a package multiple times
+// we return the same pointer each time.
+var packageCache = map[string]*Package{}
+
+// reloadPackage is like loadPackage but makes sure
+// not to use the package cache.
+func reloadPackage(arg string, stk *importStack) *Package {
+ p := packageCache[arg]
+ if p != nil {
+ delete(packageCache, p.Dir)
+ delete(packageCache, p.ImportPath)
+ }
+ return loadPackage(arg, stk)
+}
+
+// dirToImportPath returns the pseudo-import path we use for a package
+// outside the Go path. It begins with _/ and then contains the full path
+// to the directory. If the package lives in c:\home\gopher\my\pkg then
+// the pseudo-import path is _/c_/home/gopher/my/pkg.
+// Using a pseudo-import path like this makes the ./ imports no longer
+// a special case, so that all the code to deal with ordinary imports works
+// automatically.
+func dirToImportPath(dir string) string {
+ return pathpkg.Join("_", strings.Map(makeImportValid, filepath.ToSlash(dir)))
+}
+
+func makeImportValid(r rune) rune {
+ // Should match Go spec, compilers, and ../../pkg/go/parser/parser.go:/isValidImport.
+ const illegalChars = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD"
+ if !unicode.IsGraphic(r) || unicode.IsSpace(r) || strings.ContainsRune(illegalChars, r) {
+ return '_'
+ }
+ return r
+}
+
+// loadImport scans the directory named by path, which must be an import path,
+// but possibly a local import path (an absolute file system path or one beginning
+// with ./ or ../). A local relative path is interpreted relative to srcDir.
+// It returns a *Package describing the package found in that directory.
+func loadImport(path string, srcDir string, stk *importStack, importPos []token.Position) *Package {
+ stk.push(path)
+ defer stk.pop()
+
+ // Determine canonical identifier for this package.
+ // For a local import the identifier is the pseudo-import path
+ // we create from the full directory to the package.
+ // Otherwise it is the usual import path.
+ importPath := path
+ isLocal := build.IsLocalImport(path)
+ if isLocal {
+ importPath = dirToImportPath(filepath.Join(srcDir, path))
+ }
+ if p := packageCache[importPath]; p != nil {
+ return reusePackage(p, stk)
+ }
+
+ p := new(Package)
+ p.local = isLocal
+ p.ImportPath = importPath
+ packageCache[importPath] = p
+
+ // Load package.
+ // Import always returns bp != nil, even if an error occurs,
+ // in order to return partial information.
+ //
+ // TODO: After Go 1, decide when to pass build.AllowBinary here.
+ // See issue 3268 for mistakes to avoid.
+ bp, err := buildContext.Import(path, srcDir, 0)
+ bp.ImportPath = importPath
+ if gobin != "" {
+ bp.BinDir = gobin
+ }
+ p.load(stk, bp, err)
+ if p.Error != nil && len(importPos) > 0 {
+ pos := importPos[0]
+ pos.Filename = shortPath(pos.Filename)
+ p.Error.Pos = pos.String()
+ }
+
+ return p
+}
+
+// reusePackage reuses package p to satisfy the import at the top
+// of the import stack stk. If this use causes an import loop,
+// reusePackage updates p's error information to record the loop.
+func reusePackage(p *Package, stk *importStack) *Package {
+ // We use p.imports==nil to detect a package that
+ // is in the midst of its own loadPackage call
+ // (all the recursion below happens before p.imports gets set).
+ if p.imports == nil {
+ if p.Error == nil {
+ p.Error = &PackageError{
+ ImportStack: stk.copy(),
+ Err: "import loop",
+ }
+ }
+ p.Incomplete = true
+ }
+ if p.Error != nil && 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,
+ "exp/gotype": true,
+ "exp/ebnflint": true,
+}
+
+// expandScanner expands a scanner.List error into all the errors in the list.
+// The default Error method only shows the first error.
+func expandScanner(err error) error {
+ // Look for parser errors.
+ if err, ok := err.(scanner.ErrorList); ok {
+ // Prepare error with \n before each message.
+ // When printed in something like context: %v
+ // this will put the leading file positions each on
+ // its own line. It will also show all the errors
+ // instead of just the first, as err.Error does.
+ var buf bytes.Buffer
+ for _, e := range err {
+ e.Pos.Filename = shortPath(e.Pos.Filename)
+ buf.WriteString("\n")
+ buf.WriteString(e.Error())
+ }
+ return errors.New(buf.String())
+ }
+ return err
+}
+
+// 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 {
+ p.copyBuild(bp)
+
+ // The localPrefix is the path we interpret ./ imports relative to.
+ // Synthesized main packages sometimes override this.
+ p.localPrefix = dirToImportPath(p.Dir)
+
+ if err != nil {
+ p.Incomplete = true
+ err = expandScanner(err)
+ p.Error = &PackageError{
+ ImportStack: stk.copy(),
+ Err: err.Error(),
+ }
+ return p
+ }
+
+ if p.Name == "main" {
+ _, elem := filepath.Split(p.Dir)
+ full := buildContext.GOOS + "_" + buildContext.GOARCH + "/" + elem
+ if buildContext.GOOS != toolGOOS || buildContext.GOARCH != toolGOARCH {
+ // Install cross-compiled binaries to subdirectories of bin.
+ elem = full
+ }
+ p.target = filepath.Join(p.build.BinDir, elem)
+ if p.Goroot && isGoTool[p.ImportPath] {
+ p.target = filepath.Join(gorootPkg, "tool", full)
+ }
+ if buildContext.GOOS == "windows" {
+ p.target += ".exe"
+ }
+ } else if p.local {
+ // Local import turned into absolute path.
+ // No permanent install target.
+ p.target = ""
+ } else {
+ p.target = p.build.PkgObj
+ }
+
+ 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") {
+ importPaths = append(importPaths, "runtime/cgo")
+ }
+ // Everything depends on runtime, except runtime and unsafe.
+ if !p.Standard || (p.ImportPath != "runtime" && p.ImportPath != "unsafe") {
+ importPaths = append(importPaths, "runtime")
+ }
+
+ // Build list of full paths to all Go files in the package,
+ // for use by commands like go fmt.
+ p.gofiles = stringList(p.GoFiles, p.CgoFiles, p.TestGoFiles, p.XTestGoFiles)
+ for i := range p.gofiles {
+ p.gofiles[i] = filepath.Join(p.Dir, p.gofiles[i])
+ }
+ sort.Strings(p.gofiles)
+
+ // Build list of imported packages and full dependency list.
+ imports := make([]*Package, 0, len(p.Imports))
+ deps := make(map[string]bool)
+ for i, path := range importPaths {
+ if path == "C" {
+ continue
+ }
+ p1 := loadImport(path, p.Dir, stk, p.build.ImportPos[path])
+ if p1.local {
+ if !p.local && p.Error == nil {
+ p.Error = &PackageError{
+ ImportStack: stk.copy(),
+ Err: fmt.Sprintf("local import %q in non-local package", path),
+ }
+ pos := p.build.ImportPos[path]
+ if len(pos) > 0 {
+ p.Error.Pos = pos[0].String()
+ }
+ }
+ path = p1.ImportPath
+ importPaths[i] = path
+ }
+ deps[path] = true
+ imports = append(imports, p1)
+ for _, dep := range p1.Deps {
+ deps[dep] = true
+ }
+ if p1.Incomplete {
+ p.Incomplete = true
+ }
+ }
+ p.imports = imports
+
+ p.Deps = make([]string, 0, len(deps))
+ for dep := range deps {
+ p.Deps = append(p.Deps, dep)
+ }
+ sort.Strings(p.Deps)
+ for _, dep := range p.Deps {
+ p1 := packageCache[dep]
+ if p1 == nil {
+ panic("impossible: missing entry in package cache for " + dep + " imported by " + p.ImportPath)
+ }
+ p.deps = append(p.deps, p1)
+ if p1.Error != nil {
+ p.DepsErrors = append(p.DepsErrors, p1.Error)
+ }
+ }
+
+ // unsafe is a fake package.
+ if p.Standard && (p.ImportPath == "unsafe" || buildContext.Compiler == "gccgo") {
+ p.target = ""
+ }
+
+ p.Target = p.target
+ return p
+}
+
+// packageList returns the list of packages in the dag rooted at roots
+// as visited in a depth-first post-order traversal.
+func packageList(roots []*Package) []*Package {
+ seen := map[*Package]bool{}
+ all := []*Package{}
+ var walk func(*Package)
+ walk = func(p *Package) {
+ if seen[p] {
+ return
+ }
+ seen[p] = true
+ for _, p1 := range p.imports {
+ walk(p1)
+ }
+ all = append(all, p)
+ }
+ for _, root := range roots {
+ walk(root)
+ }
+ return all
+}
+
+// computeStale computes the Stale flag in the package dag that starts
+// at the named pkgs (command-line arguments).
+func computeStale(pkgs ...*Package) {
+ topRoot := map[string]bool{}
+ for _, p := range pkgs {
+ topRoot[p.Root] = true
+ }
+
+ for _, p := range packageList(pkgs) {
+ p.Stale = isStale(p, topRoot)
+ }
+}
+
+// isStale reports whether package p needs to be rebuilt.
+func isStale(p *Package, topRoot map[string]bool) bool {
+ if p.Standard && (p.ImportPath == "unsafe" || buildContext.Compiler == "gccgo") {
+ // fake, builtin package
+ return false
+ }
+ if p.Error != nil {
+ return true
+ }
+
+ // A package without Go sources means we only found
+ // the installed .a file. Since we don't know how to rebuild
+ // it, it can't be stale, even if -a is set. This enables binary-only
+ // distributions of Go packages, although such binaries are
+ // only useful with the specific version of the toolchain that
+ // created them.
+ if len(p.gofiles) == 0 {
+ return false
+ }
+
+ if buildA || p.target == "" || p.Stale {
+ return true
+ }
+
+ // Package is stale if completely unbuilt.
+ var built time.Time
+ if fi, err := os.Stat(p.target); err == nil {
+ built = fi.ModTime()
+ }
+ if built.IsZero() {
+ return true
+ }
+
+ olderThan := func(file string) bool {
+ fi, err := os.Stat(file)
+ return err != nil || fi.ModTime().After(built)
+ }
+
+ // Package is stale if a dependency is, or if a dependency is newer.
+ for _, p1 := range p.deps {
+ if p1.Stale || p1.target != "" && olderThan(p1.target) {
+ return true
+ }
+ }
+
+ // As a courtesy to developers installing new versions of the compiler
+ // frequently, define that packages are stale if they are
+ // older than the compiler, and commands if they are older than
+ // the linker. This heuristic will not work if the binaries are back-dated,
+ // as some binary distributions may do, but it does handle a very
+ // common case. See issue 3036.
+ if olderThan(buildToolchain.compiler()) {
+ return true
+ }
+ if p.build.IsCommand() && olderThan(buildToolchain.linker()) {
+ return true
+ }
+
+ // Have installed copy, probably built using current compilers,
+ // and built after its imported packages. The only reason now
+ // that we'd have to rebuild it is if the sources were newer than
+ // the package. If a package p is not in the same tree as any
+ // package named on the command-line, assume it is up-to-date
+ // no matter what the modification times on the source files indicate.
+ // This avoids rebuilding $GOROOT packages when people are
+ // working outside the Go root, and it effectively makes each tree
+ // listed in $GOPATH a separate compilation world.
+ // See issue 3149.
+ if p.Root != "" && !topRoot[p.Root] {
+ return false
+ }
+
+ srcs := stringList(p.GoFiles, p.CFiles, p.HFiles, p.SFiles, p.CgoFiles, p.SysoFiles)
+ for _, src := range srcs {
+ if olderThan(filepath.Join(p.Dir, src)) {
+ return true
+ }
+ }
+
+ return false
+}
+
+var cwd, _ = os.Getwd()
+
+var cmdCache = map[string]*Package{}
+
+// loadPackage is like loadImport but is used for command-line arguments,
+// not for paths found in import statements. In addition to ordinary import paths,
+// loadPackage accepts pseudo-paths beginning with cmd/ to denote commands
+// in the Go command directory, as well as paths to those directories.
+func loadPackage(arg string, stk *importStack) *Package {
+ if build.IsLocalImport(arg) {
+ dir := arg
+ if !filepath.IsAbs(dir) {
+ if abs, err := filepath.Abs(dir); err == nil {
+ // interpret relative to current directory
+ dir = abs
+ }
+ }
+ if sub, ok := hasSubdir(gorootSrc, dir); ok && strings.HasPrefix(sub, "cmd/") && !strings.Contains(sub[4:], "/") {
+ arg = sub
+ }
+ }
+ if strings.HasPrefix(arg, "cmd/") && !strings.Contains(arg[4:], "/") {
+ if p := cmdCache[arg]; p != nil {
+ return p
+ }
+ stk.push(arg)
+ defer stk.pop()
+ bp, err := build.ImportDir(filepath.Join(gorootSrc, arg), 0)
+ bp.ImportPath = arg
+ bp.Goroot = true
+ bp.BinDir = gorootBin
+ if gobin != "" {
+ bp.BinDir = gobin
+ }
+ bp.Root = goroot
+ bp.SrcRoot = gorootSrc
+ p := new(Package)
+ cmdCache[arg] = p
+ p.load(stk, bp, err)
+ if p.Error == nil && p.Name != "main" {
+ p.Error = &PackageError{
+ ImportStack: stk.copy(),
+ Err: fmt.Sprintf("expected package main but found package %s in %s", p.Name, p.Dir),
+ }
+ }
+ return p
+ }
+
+ // Wasn't a command; must be a package.
+ // If it is a local import path but names a standard package,
+ // we treat it as if the user specified the standard package.
+ // This lets you run go test ./ioutil in package io and be
+ // referring to io/ioutil rather than a hypothetical import of
+ // "./ioutil".
+ if build.IsLocalImport(arg) {
+ bp, _ := build.ImportDir(filepath.Join(cwd, arg), build.FindOnly)
+ if bp.ImportPath != "" && bp.ImportPath != "." {
+ arg = bp.ImportPath
+ }
+ }
+
+ return loadImport(arg, cwd, stk, nil)
+}
+
+// packages returns the packages named by the
+// command line arguments 'args'. If a named package
+// cannot be loaded at all (for example, if the directory does not exist),
+// then packages prints an error and does not include that
+// package in the results. However, if errors occur trying
+// to load dependencies of a named package, the named
+// package is still returned, with p.Incomplete = true
+// and details in p.DepsErrors.
+func packages(args []string) []*Package {
+ var pkgs []*Package
+ for _, pkg := range packagesAndErrors(args) {
+ if pkg.Error != nil {
+ errorf("can't load package: %s", pkg.Error)
+ continue
+ }
+ pkgs = append(pkgs, pkg)
+ }
+ return pkgs
+}
+
+// packagesAndErrors is like 'packages' but returns a
+// *Package for every argument, even the ones that
+// cannot be loaded at all.
+// The packages that fail to load will have p.Error != nil.
+func packagesAndErrors(args []string) []*Package {
+ if len(args) > 0 && strings.HasSuffix(args[0], ".go") {
+ return []*Package{goFilesPackage(args)}
+ }
+
+ args = importPaths(args)
+ var pkgs []*Package
+ var stk importStack
+ for _, arg := range args {
+ pkgs = append(pkgs, loadPackage(arg, &stk))
+ }
+
+ computeStale(pkgs...)
+
+ return pkgs
+}
+
+// packagesForBuild is like 'packages' but fails if any of
+// the packages or their dependencies have errors
+// (cannot be built).
+func packagesForBuild(args []string) []*Package {
+ pkgs := packagesAndErrors(args)
+ printed := map[*PackageError]bool{}
+ for _, pkg := range pkgs {
+ if pkg.Error != nil {
+ errorf("can't load package: %s", pkg.Error)
+ }
+ for _, err := range pkg.DepsErrors {
+ // Since these are errors in dependencies,
+ // the same error might show up multiple times,
+ // once in each package that depends on it.
+ // Only print each once.
+ if !printed[err] {
+ printed[err] = true
+ errorf("%s", err)
+ }
+ }
+ }
+ exitIfErrors()
+ return pkgs
+}
+
+// hasSubdir reports whether dir is a subdirectory of
+// (possibly multiple levels below) root.
+// If so, it sets rel to the path fragment that must be
+// appended to root to reach dir.
+func hasSubdir(root, dir string) (rel string, ok bool) {
+ if p, err := filepath.EvalSymlinks(root); err == nil {
+ root = p
+ }
+ if p, err := filepath.EvalSymlinks(dir); err == nil {
+ dir = p
+ }
+ const sep = string(filepath.Separator)
+ root = filepath.Clean(root)
+ if !strings.HasSuffix(root, sep) {
+ root += sep
+ }
+ dir = filepath.Clean(dir)
+ if !strings.HasPrefix(dir, root) {
+ return "", false
+ }
+ return filepath.ToSlash(dir[len(root):]), true
+}
diff --git a/src/cmd/go/run.go b/src/cmd/go/run.go
new file mode 100644
index 000000000..94cd59296
--- /dev/null
+++ b/src/cmd/go/run.go
@@ -0,0 +1,85 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "fmt"
+ "os"
+ "os/exec"
+ "strings"
+)
+
+var cmdRun = &Command{
+ UsageLine: "run [build flags] gofiles... [arguments...]",
+ Short: "compile and run Go program",
+ Long: `
+Run compiles and runs the main package comprising the named Go source files.
+
+For more about build flags, see 'go help build'.
+
+See also: go build.
+ `,
+}
+
+func init() {
+ cmdRun.Run = runRun // break init loop
+
+ addBuildFlags(cmdRun)
+}
+
+func printStderr(args ...interface{}) (int, error) {
+ return fmt.Fprint(os.Stderr, args...)
+}
+
+func runRun(cmd *Command, args []string) {
+ var b builder
+ b.init()
+ b.print = printStderr
+ i := 0
+ for i < len(args) && strings.HasSuffix(args[i], ".go") {
+ i++
+ }
+ files, cmdArgs := args[:i], args[i:]
+ if len(files) == 0 {
+ fatalf("go run: no go files listed")
+ }
+ p := goFilesPackage(files)
+ if p.Error != nil {
+ fatalf("%s", p.Error)
+ }
+ if p.Name != "main" {
+ fatalf("go run: cannot run non-main package")
+ }
+ p.target = "" // must build - not up to date
+ a1 := b.action(modeBuild, modeBuild, p)
+ a := &action{f: (*builder).runProgram, args: cmdArgs, deps: []*action{a1}}
+ b.do(a)
+}
+
+// runProgram is the action for running a binary that has already
+// been compiled. We ignore exit status.
+func (b *builder) runProgram(a *action) error {
+ if buildN || buildX {
+ b.showcmd("", "%s %s", a.deps[0].target, strings.Join(a.args, " "))
+ if buildN {
+ return nil
+ }
+ }
+
+ runStdin(a.deps[0].target, a.args)
+ return nil
+}
+
+// runStdin is like run, but connects Stdin.
+func runStdin(cmdargs ...interface{}) {
+ cmdline := stringList(cmdargs...)
+ cmd := exec.Command(cmdline[0], cmdline[1:]...)
+ cmd.Stdin = os.Stdin
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ if err := cmd.Run(); err != nil {
+ errorf("%v", err)
+ }
+}
diff --git a/src/cmd/go/script b/src/cmd/go/script
new file mode 100755
index 000000000..340a7e824
--- /dev/null
+++ b/src/cmd/go/script
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+x() {
+ echo '--- ' "$@"
+ "$@"
+ echo '---'
+ echo
+}
+
+x go help
+x go help build
+x go help clean
+x go help install
+x go help fix
+x go help fmt
+x go help get
+x go help list
+x go help test
+x go help version
+x go help vet
+x go help gopath
+x go help importpath
+x go help remote
diff --git a/src/cmd/go/script.txt b/src/cmd/go/script.txt
new file mode 100644
index 000000000..a67214658
--- /dev/null
+++ b/src/cmd/go/script.txt
@@ -0,0 +1,352 @@
+--- go help
+usage: go command [arguments]
+
+go manages Go source code.
+
+The commands are:
+
+ build compile and install packages and dependencies
+ clean remove intermediate objects
+ fix run gofix on packages
+ fmt run gofmt -w on packages
+ get download and install packages and dependencies
+ install install packages and dependencies
+ list list packages
+ test test packages
+ version print Go version
+ vet run govet on packages
+
+Use "go help [command]" for more information about a command.
+
+Additional help topics:
+
+ gopath GOPATH environment variable
+ importpath description of import paths
+ remote remote import path syntax
+
+Use "go help [topic]" for more information about that topic.
+
+---
+
+--- go help build
+usage: go build [-n] [-v] [importpath...]
+
+Build compiles the packages named by the import paths,
+along with their dependencies, but it does not install the results.
+
+The -n flag prints the commands but does not run them.
+The -v flag prints the commands.
+
+For more about import paths, see 'go help importpath'.
+
+See also: go install, go get, go clean.
+---
+
+--- go help clean
+usage: go clean [-nuke] [importpath...]
+
+Clean removes intermediate object files generated during
+the compilation of the packages named by the import paths,
+but by default it does not remove the installed package binaries.
+
+The -nuke flag causes clean to remove the installed package binaries too.
+
+TODO: Clean does not clean dependencies of the packages.
+
+For more about import paths, see 'go help importpath'.
+---
+
+--- go help install
+usage: go install [-n] [-v] [importpath...]
+
+Install compiles and installs the packages named by the import paths,
+along with their dependencies.
+
+The -n flag prints the commands but does not run them.
+The -v flag prints the commands.
+
+For more about import paths, see 'go help importpath'.
+
+See also: go build, go get, go clean.
+---
+
+--- go help fix
+usage: go fix [importpath...]
+
+Fix runs the gofix command on the packages named by the import paths.
+
+For more about gofix, see 'godoc gofix'.
+For more about import paths, see 'go help importpath'.
+
+To run gofix with specific options, run gofix itself.
+
+See also: go fmt, go vet.
+---
+
+--- go help fmt
+usage: go fmt [importpath...]
+
+Fmt runs the command 'gofmt -w' on the packages named by the import paths.
+
+For more about gofmt, see 'godoc gofmt'.
+For more about import paths, see 'go help importpath'.
+
+To run gofmt with specific options, run gofmt itself.
+
+See also: go fix, go vet.
+---
+
+--- go help get
+usage: go get [importpath...]
+
+Get downloads and installs the packages named by the import paths,
+along with their dependencies.
+
+After downloading the code, 'go get' looks for a tag beginning
+with "go." that corresponds to the local Go version.
+For Go "release.r58" it looks for a tag named "go.r58".
+For "weekly.2011-06-03" it looks for "go.weekly.2011-06-03".
+If the specific "go.X" tag is not found, it uses the latest earlier
+version it can find. Otherwise, it uses the default version for
+the version control system: HEAD for git, tip for Mercurial,
+and so on.
+
+TODO: Explain versions better.
+
+For more about import paths, see 'go help importpath'.
+
+For more about how 'go get' finds source code to
+download, see 'go help remote'.
+
+See also: go build, go install, go clean.
+---
+
+--- go help list
+usage: go list [-f format] [-json] [importpath...]
+
+List lists the packages named by the import paths.
+
+The default output shows the package name and file system location:
+
+ books /home/you/src/google-api-go-client.googlecode.com/hg/books/v1
+ oauth /home/you/src/goauth2.googlecode.com/hg/oauth
+ sqlite /home/you/src/gosqlite.googlecode.com/hg/sqlite
+
+The -f flag specifies an alternate format for the list,
+using the syntax of package template. The default output
+is equivalent to -f '{{.Name}} {{.Dir}}' The struct
+being passed to the template is:
+
+ type Package struct {
+ Name string // package name
+ Doc string // package documentation string
+ GoFiles []string // names of Go source files in package
+ ImportPath string // import path denoting package
+ Imports []string // import paths used by this package
+ Deps []string // all (recursively) imported dependencies
+ Dir string // directory containing package sources
+ Version string // version of installed package
+ }
+
+The -json flag causes the package data to be printed in JSON format.
+
+For more about import paths, see 'go help importpath'.
+---
+
+--- go help test
+usage: go test [importpath...]
+
+Test runs gotest to test the packages named by the import paths.
+It prints a summary of the test results in the format:
+
+ test archive/tar
+ FAIL archive/zip
+ test compress/gzip
+ ...
+
+followed by gotest output for each failed package.
+
+For more about import paths, see 'go help importpath'.
+
+See also: go build, go compile, go vet.
+---
+
+--- go help version
+usage: go version
+
+Version prints the Go version, as reported by runtime.Version.
+---
+
+--- go help vet
+usage: go vet [importpath...]
+
+Vet runs the govet command on the packages named by the import paths.
+
+For more about govet, see 'godoc govet'.
+For more about import paths, see 'go help importpath'.
+
+To run govet with specific options, run govet itself.
+
+See also: go fmt, go fix.
+---
+
+--- go help gopath
+The GOPATH environment variable lists places to look for Go code.
+On Unix, the value is a colon-separated string.
+On Windows, the value is a semicolon-separated string.
+On Plan 9, the value is a list.
+
+GOPATH must be set to build and install packages outside the
+standard Go tree.
+
+Each directory listed in GOPATH must have a prescribed structure:
+
+The src/ directory holds source code. The path below 'src'
+determines the import path or executable name.
+
+The pkg/ directory holds installed package objects.
+As in the Go tree, each target operating system and
+architecture pair has its own subdirectory of pkg
+(pkg/GOOS_GOARCH).
+
+If DIR is a directory listed in the GOPATH, a package with
+source in DIR/src/foo/bar can be imported as "foo/bar" and
+has its compiled form installed to "DIR/pkg/GOOS_GOARCH/foo/bar.a".
+
+The bin/ directory holds compiled commands.
+Each command is named for its source directory, but only
+the final element, not the entire path. That is, the
+command with source in DIR/src/foo/quux is installed into
+DIR/bin/quux, not DIR/bin/foo/quux. The foo/ is stripped
+so that you can add DIR/bin to your PATH to get at the
+installed commands.
+
+Here's an example directory layout:
+
+ GOPATH=/home/user/gocode
+
+ /home/user/gocode/
+ src/
+ foo/
+ bar/ (go code in package bar)
+ x.go
+ quux/ (go code in package main)
+ y.go
+ bin/
+ quux (installed command)
+ pkg/
+ linux_amd64/
+ foo/
+ bar.a (installed package object)
+
+Go searches each directory listed in GOPATH to find source code,
+but new packages are always downloaded into the first directory
+in the list.
+---
+
+--- go help importpath
+Many commands apply to a set of packages named by import paths:
+
+ go action [importpath...]
+
+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.
+
+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 'project.googlecode.com/'.
+---
+
+--- go help remote
+An import path (see 'go help importpath') denotes a package
+stored in the local file system. Certain import paths also
+describe how to obtain the source code for the package using
+a revision control system.
+
+A few common code hosting sites have special syntax:
+
+ BitBucket (Mercurial)
+
+ import "bitbucket.org/user/project"
+ import "bitbucket.org/user/project/sub/directory"
+
+ GitHub (Git)
+
+ import "github.com/user/project"
+ import "github.com/user/project/sub/directory"
+
+ Google Code Project Hosting (Git, Mercurial, Subversion)
+
+ import "project.googlecode.com/git"
+ import "project.googlecode.com/git/sub/directory"
+
+ import "project.googlecode.com/hg"
+ import "project.googlecode.com/hg/sub/directory"
+
+ import "project.googlecode.com/svn/trunk"
+ import "project.googlecode.com/svn/trunk/sub/directory"
+
+ Launchpad (Bazaar)
+
+ import "launchpad.net/project"
+ import "launchpad.net/project/series"
+ import "launchpad.net/project/series/sub/directory"
+
+ import "launchpad.net/~user/project/branch"
+ import "launchpad.net/~user/project/branch/sub/directory"
+
+For code hosted on other servers, an import path of the form
+
+ repository.vcs/path
+
+specifies the given repository, with or without the .vcs suffix,
+using the named version control system, and then the path inside
+that repository. The supported version control systems are:
+
+ Bazaar .bzr
+ Git .git
+ Mercurial .hg
+ Subversion .svn
+
+For example,
+
+ import "example.org/user/foo.hg"
+
+denotes the root directory of the Mercurial repository at
+example.org/user/foo or foo.hg, and
+
+ import "example.org/repo.git/foo/bar"
+
+denotes the foo/bar directory of the Git repository at
+example.com/repo or repo.git.
+
+When a version control system supports multiple protocols,
+each is tried in turn when downloading. For example, a Git
+download tries git://, then https://, then http://.
+
+New downloaded packages are written to the first directory
+listed in the GOPATH environment variable (see 'go help gopath').
+
+The go command attempts to download the version of the
+package appropriate for the Go release being used.
+Run 'go help install' for more.
+---
+
diff --git a/src/cmd/goinstall/tag_test.go b/src/cmd/go/tag_test.go
index a23a7ea82..556a84a8e 100644
--- a/src/cmd/goinstall/tag_test.go
+++ b/src/cmd/go/tag_test.go
@@ -18,6 +18,12 @@ var selectTagTestTags = []string{
"go.weekly.2011-10-12.1",
"go.weekly.2011-10-14",
"go.weekly.2011-11-01",
+ "go1",
+ "go1.0.1",
+ "go1.999",
+ "go1.9.2",
+ "go5",
+
// these should be ignored:
"release.r59",
"release.r59.1",
@@ -30,6 +36,14 @@ var selectTagTestTags = []string{
"go.f00",
"go!r60",
"go.1999-01-01",
+ "go.2x",
+ "go.20000000000000",
+ "go.2.",
+ "go.2.0",
+ "go2x",
+ "go20000000000000",
+ "go2.",
+ "go2.0",
}
var selectTagTests = []struct {
@@ -56,11 +70,21 @@ var selectTagTests = []struct {
{"weekly.2011-11-01", "go.weekly.2011-11-01"},
{"weekly.2014-01-01", "go.weekly.2011-11-01"},
{"weekly.3000-01-01", "go.weekly.2011-11-01"},
+ {"go1", "go1"},
+ {"go1.1", "go1.0.1"},
+ {"go1.998", "go1.9.2"},
+ {"go1.1000", "go1.999"},
+ {"go6", "go5"},
+
// faulty versions:
{"release.f00", ""},
{"weekly.1999-01-01", ""},
{"junk", ""},
{"", ""},
+ {"go2x", ""},
+ {"go200000000000", ""},
+ {"go2.", ""},
+ {"go2.0", ""},
}
func TestSelectTag(t *testing.T) {
diff --git a/src/cmd/go/test.bash b/src/cmd/go/test.bash
new file mode 100755
index 000000000..fe186d4bb
--- /dev/null
+++ b/src/cmd/go/test.bash
@@ -0,0 +1,127 @@
+#!/bin/bash
+# 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.
+
+set -e
+go build -o testgo
+
+ok=true
+
+unset GOPATH
+unset GOBIN
+
+# Test that error messages have file:line information
+# at beginning of line.
+for i in testdata/errmsg/*.go
+do
+ # TODO: |cat should not be necessary here but is.
+ ./testgo test $i 2>&1 | cat >err.out || true
+ if ! grep -q "^$i:" err.out; then
+ echo "$i: missing file:line in error message"
+ cat err.out
+ ok=false
+ fi
+done
+
+# Test local (./) imports.
+testlocal() {
+ local="$1"
+ ./testgo build -o hello "testdata/$local/easy.go"
+ ./hello >hello.out
+ if ! grep -q '^easysub\.Hello' hello.out; then
+ echo "testdata/$local/easy.go did not generate expected output"
+ cat hello.out
+ ok=false
+ fi
+
+ ./testgo build -o hello "testdata/$local/easysub/main.go"
+ ./hello >hello.out
+ if ! grep -q '^easysub\.Hello' hello.out; then
+ echo "testdata/$local/easysub/main.go did not generate expected output"
+ cat hello.out
+ ok=false
+ fi
+
+ ./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
+ echo "testdata/$local/hard.go did not generate expected output"
+ cat hello.out
+ ok=false
+ fi
+
+ rm -f err.out hello.out hello
+
+ # Test that go install x.go fails.
+ if ./testgo install "testdata/$local/easy.go" >/dev/null 2>&1; then
+ echo "go install testdata/$local/easy.go succeeded"
+ ok=false
+ fi
+}
+
+# Test local imports
+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"
+rm -rf "testdata/$bad"
+
+# Test tests with relative imports.
+if ! ./testgo test ./testdata/testimport; then
+ echo "go test ./testdata/testimport failed"
+ ok=false
+fi
+
+# Test tests with relative imports in packages synthesized
+# from Go files named on the command line.
+if ! ./testgo test ./testdata/testimport/*.go; then
+ echo "go test ./testdata/testimport/*.go failed"
+ ok=false
+fi
+
+# Test that without $GOBIN set, binaries get installed
+# into the GOPATH bin directory.
+rm -rf testdata/bin
+if ! GOPATH=$(pwd)/testdata ./testgo install go-cmd-test; then
+ echo "go install go-cmd-test failed"
+ ok=false
+elif ! test -x testdata/bin/go-cmd-test; then
+ echo "go install go-cmd-test did not write to testdata/bin/go-cmd-test"
+ ok=false
+fi
+
+# And with $GOBIN set, binaries get installed to $GOBIN.
+if ! GOBIN=$(pwd)/testdata/bin1 GOPATH=$(pwd)/testdata ./testgo install go-cmd-test; then
+ echo "go install go-cmd-test failed"
+ ok=false
+elif ! test -x testdata/bin1/go-cmd-test; then
+ echo "go install go-cmd-test did not write to testdata/bin1/go-cmd-test"
+ ok=false
+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
+ echo "go install testdata/src/go-cmd-test/helloworld.go should have failed, did not"
+ ok=false
+fi
+
+# With $GOBIN set, should install there.
+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
+elif ! test -x testdata/bin1/helloworld; then
+ echo "go install testdata/src/go-cmd-test/helloworld.go did not write testdata/bin1/helloworld"
+ ok=false
+fi
+
+if $ok; then
+ echo PASS
+else
+ echo FAIL
+ exit 1
+fi
diff --git a/src/cmd/go/test.go b/src/cmd/go/test.go
new file mode 100644
index 000000000..870ab190f
--- /dev/null
+++ b/src/cmd/go/test.go
@@ -0,0 +1,815 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "bytes"
+ "fmt"
+ "go/ast"
+ "go/build"
+ "go/doc"
+ "go/parser"
+ "go/token"
+ "os"
+ "os/exec"
+ "path"
+ "path/filepath"
+ "runtime"
+ "sort"
+ "strings"
+ "text/template"
+ "time"
+ "unicode"
+ "unicode/utf8"
+)
+
+// Break init loop.
+func init() {
+ cmdTest.Run = runTest
+}
+
+var cmdTest = &Command{
+ CustomFlags: true,
+ UsageLine: "test [-c] [-i] [build flags] [packages] [flags for test binary]",
+ Short: "test packages",
+ Long: `
+'Go test' automates testing the packages named by the import paths.
+It prints a summary of the test results in the format:
+
+ ok archive/tar 0.011s
+ FAIL archive/zip 0.022s
+ ok compress/gzip 0.033s
+ ...
+
+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.
+
+By default, go test needs no arguments. It compiles and tests the package
+with source in the current directory, including tests, and runs the tests.
+
+The package is built in a temporary directory so it does not interfere with the
+non-test installation.
+
+In addition to the build flags, the flags handled by 'go test' itself are:
+
+ -c Compile the test binary to pkg.test but do not run it.
+
+ -i
+ Install packages that are dependencies of the test.
+ Do not run the test.
+
+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.
+
+For more about build flags, see 'go help build'.
+For more about specifying packages, see 'go help packages'.
+
+See also: go build, go vet.
+`,
+}
+
+var helpTestflag = &Command{
+ UsageLine: "testflag",
+ Short: "description of testing flags",
+ Long: `
+The 'go test' command takes both flags that apply to 'go test' itself
+and flags that apply to the resulting test binary.
+
+The test binary, called pkg.test, where pkg is the name of the
+directory containing the package sources, has its own flags:
+
+ -test.v
+ Verbose output: log all tests as they are run.
+
+ -test.run pattern
+ Run only those tests and examples matching the regular
+ expression.
+
+ -test.bench pattern
+ Run benchmarks matching the regular expression.
+ By default, no benchmarks run.
+
+ -test.cpuprofile cpu.out
+ Write a CPU profile to the specified file before exiting.
+
+ -test.memprofile mem.out
+ Write a memory profile to the specified file when all tests
+ are complete.
+
+ -test.memprofilerate n
+ Enable more precise (and expensive) memory profiles by setting
+ runtime.MemProfileRate. See 'godoc runtime MemProfileRate'.
+ To profile all memory allocations, use -test.memprofilerate=1
+ and set the environment variable GOGC=off to disable the
+ garbage collector, provided the test can run in the available
+ memory without garbage collection.
+
+ -test.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
+ simultaneously; by default, it is set to the value of GOMAXPROCS.
+
+ -test.short
+ Tell long-running tests to shorten their run time.
+ It is off by default but set during all.bash so that installing
+ the Go tree can run a sanity check but not spend time running
+ exhaustive tests.
+
+ -test.timeout t
+ If a test runs longer than t, panic.
+
+ -test.benchtime n
+ Run enough iterations of each benchmark to take n seconds.
+ The default is 1 second.
+
+ -test.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
+ of GOMAXPROCS.
+
+For convenience, each of these -test.X flags of the test binary is
+also available as the flag -X in 'go test' itself. Flags not listed
+here are passed through unaltered. For instance, the command
+
+ go test -x -v -cpuprofile=prof.out -dir=testdata -update
+
+will compile the test binary and then run it as
+
+ pkg.test -test.v -test.cpuprofile=prof.out -dir=testdata -update
+`,
+}
+
+var helpTestfunc = &Command{
+ UsageLine: "testfunc",
+ Short: "description of testing functions",
+ Long: `
+The 'go test' command expects to find test, benchmark, and example functions
+in the "*_test.go" files corresponding to the package under test.
+
+A test function is one named TestXXX (where XXX is any alphanumeric string
+not starting with a lower case letter) and should have the signature,
+
+ func TestXXX(t *testing.T) { ... }
+
+A benchmark function is one named BenchmarkXXX and should have the signature,
+
+ func BenchmarkXXX(b *testing.B) { ... }
+
+An example function is similar to a test function but, instead of using *testing.T
+to report success or failure, prints output to os.Stdout and os.Stderr.
+That output is compared against the function's "Output:" comment, which
+must be the last comment in the function body (see example below). An
+example with no such comment, or with no text after "Output:" is compiled
+but not executed.
+
+Godoc displays the body of ExampleXXX to demonstrate the use
+of the function, constant, or variable XXX. An example of a method M with
+receiver type T or *T is named ExampleT_M. There may be multiple examples
+for a given function, constant, or variable, distinguished by a trailing _xxx,
+where xxx is a suffix not beginning with an upper case letter.
+
+Here is an example of an example:
+
+ func ExamplePrintln() {
+ Println("The output of\nthis example.")
+ // Output: The output of
+ // this example.
+ }
+
+The entire test file is presented as the example when it contains a single
+example function, at least one other function, type, variable, or constant
+declaration, and no test or benchmark functions.
+
+See the documentation of the testing package for more information.
+`,
+}
+
+var (
+ testC bool // -c flag
+ 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
+ testShowPass bool // show passing output
+
+ testKillTimeout = 10 * time.Minute
+)
+
+func runTest(cmd *Command, args []string) {
+ var pkgArgs []string
+ pkgArgs, testArgs = testFlags(args)
+
+ pkgs := packagesForBuild(pkgArgs)
+ if len(pkgs) == 0 {
+ fatalf("no packages to test")
+ }
+
+ if testC && len(pkgs) != 1 {
+ fatalf("cannot use -c flag with multiple packages")
+ }
+
+ // If a test timeout was given and is parseable, set our kill timeout
+ // to that timeout plus one minute. This is a backup alarm in case
+ // the test wedges with a goroutine spinning and its background
+ // timer does not get a chance to fire.
+ if dt, err := time.ParseDuration(testTimeout); err == nil {
+ testKillTimeout = dt + 1*time.Minute
+ }
+
+ // show passing test output (after buffering) with -v flag.
+ // must buffer because tests are running in parallel, and
+ // otherwise the output will get mixed.
+ testShowPass = testV
+
+ // stream test output (no buffering) when no package has
+ // been given on the command line (implicit current directory)
+ // or when benchmarking.
+ // Also stream if we're showing output anyway with a
+ // single package under test. In that case, streaming the
+ // output produces the same result as not streaming,
+ // just more immediately.
+ testStreamOutput = len(pkgArgs) == 0 || testBench ||
+ (len(pkgs) <= 1 && testShowPass)
+
+ var b builder
+ b.init()
+
+ if testI {
+ buildV = testV
+
+ deps := map[string]bool{
+ // Dependencies for testmain.
+ "testing": true,
+ "regexp": true,
+ }
+ for _, p := range pkgs {
+ // Dependencies for each test.
+ for _, path := range p.Imports {
+ deps[path] = true
+ }
+ for _, path := range p.TestImports {
+ deps[path] = true
+ }
+ for _, path := range p.XTestImports {
+ deps[path] = true
+ }
+ }
+
+ // translate C to runtime/cgo
+ if deps["C"] {
+ delete(deps, "C")
+ deps["runtime/cgo"] = true
+ if buildContext.GOOS == runtime.GOOS && buildContext.GOARCH == runtime.GOARCH {
+ deps["cmd/cgo"] = true
+ }
+ }
+ // Ignore pseudo-packages.
+ delete(deps, "unsafe")
+
+ all := []string{}
+ for path := range deps {
+ all = append(all, path)
+ }
+ sort.Strings(all)
+
+ a := &action{}
+ for _, p := range packagesForBuild(all) {
+ a.deps = append(a.deps, b.action(modeInstall, modeInstall, p))
+ }
+ b.do(a)
+ if !testC {
+ return
+ }
+ b.init()
+ }
+
+ var builds, runs, prints []*action
+
+ // Prepare build + run + print actions for all packages being tested.
+ for _, p := range pkgs {
+ buildTest, runTest, printTest, err := b.test(p)
+ if err != nil {
+ str := err.Error()
+ if strings.HasPrefix(str, "\n") {
+ str = str[1:]
+ }
+ if p.ImportPath != "" {
+ errorf("# %s\n%s", p.ImportPath, str)
+ } else {
+ errorf("%s", str)
+ }
+ continue
+ }
+ builds = append(builds, buildTest)
+ runs = append(runs, runTest)
+ prints = append(prints, printTest)
+ }
+
+ // Ultimately the goal is to print the output.
+ root := &action{deps: prints}
+
+ // Force the printing of results to happen in order,
+ // one at a time.
+ for i, a := range prints {
+ if i > 0 {
+ a.deps = append(a.deps, prints[i-1])
+ }
+ }
+
+ // 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])
+ }
+ }
+ }
+
+ // If we are building any out-of-date packages other
+ // than those under test, warn.
+ okBuild := map[*Package]bool{}
+ 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 !warned {
+ fmt.Fprintf(os.Stderr, "warning: building out-of-date packages:\n")
+ warned = true
+ }
+ fmt.Fprintf(os.Stderr, "\t%s\n", a.p.ImportPath)
+ }
+ }
+ if warned {
+ args := strings.Join(pkgArgs, " ")
+ if args != "" {
+ args = " " + args
+ }
+ fmt.Fprintf(os.Stderr, "installing these packages with 'go test -i%s' will speed future tests.\n\n", args)
+ }
+
+ b.do(root)
+}
+
+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}}
+ return build, run, print, nil
+ }
+
+ // Build Package structs describing:
+ // ptest - package + test files
+ // pxtest - package of external test files
+ // pmain - pkg.test binary
+ var ptest, pxtest, pmain *Package
+
+ var imports, ximports []*Package
+ var stk importStack
+ stk.push(p.ImportPath + "_test")
+ for _, path := range p.TestImports {
+ p1 := loadImport(path, p.Dir, &stk, p.build.TestImportPos[path])
+ if p1.Error != nil {
+ return nil, nil, nil, p1.Error
+ }
+ imports = append(imports, p1)
+ }
+ for _, path := range p.XTestImports {
+ if path == p.ImportPath {
+ continue
+ }
+ p1 := loadImport(path, p.Dir, &stk, p.build.XTestImportPos[path])
+ if p1.Error != nil {
+ return nil, nil, nil, p1.Error
+ }
+ ximports = append(ximports, p1)
+ }
+ stk.pop()
+
+ // Use last element of import path, not package name.
+ // They differ when package name is "main".
+ _, elem := path.Split(p.ImportPath)
+ testBinary := elem + ".test"
+
+ // The ptest package needs to be importable under the
+ // same import path that p has, but we cannot put it in
+ // the usual place in the temporary tree, because then
+ // other tests will see it as the real package.
+ // Instead we make a _test directory under the import path
+ // and then repeat the import path there. We tell the
+ // compiler and linker to look in that _test directory first.
+ //
+ // That is, if the package under test is unicode/utf8,
+ // then the normal place to write the package archive is
+ // $WORK/unicode/utf8.a, but we write the test package archive to
+ // $WORK/unicode/utf8/_test/unicode/utf8.a.
+ // We write the external test package archive to
+ // $WORK/unicode/utf8/_test/unicode/utf8_test.a.
+ testDir := filepath.Join(b.work, filepath.FromSlash(p.ImportPath+"/_test"))
+ ptestObj := buildToolchain.pkgpath(testDir, p)
+
+ // Create the directory for the .a files.
+ ptestDir, _ := filepath.Split(ptestObj)
+ 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
+ }
+
+ // Test package.
+ if len(p.TestGoFiles) > 0 {
+ ptest = new(Package)
+ *ptest = *p
+ ptest.GoFiles = nil
+ ptest.GoFiles = append(ptest.GoFiles, p.GoFiles...)
+ ptest.GoFiles = append(ptest.GoFiles, p.TestGoFiles...)
+ ptest.target = ""
+ ptest.Imports = stringList(p.Imports, p.TestImports)
+ ptest.imports = append(append([]*Package{}, p.imports...), imports...)
+ ptest.pkgdir = testDir
+ ptest.fake = true
+ ptest.forceLibrary = true
+ ptest.Stale = true
+ ptest.build = new(build.Package)
+ *ptest.build = *p.build
+ m := map[string][]token.Position{}
+ for k, v := range p.build.ImportPos {
+ m[k] = append(m[k], v...)
+ }
+ for k, v := range p.build.TestImportPos {
+ m[k] = append(m[k], v...)
+ }
+ ptest.build.ImportPos = m
+ } else {
+ ptest = p
+ }
+
+ // External test package.
+ if len(p.XTestGoFiles) > 0 {
+ pxtest = &Package{
+ Name: p.Name + "_test",
+ ImportPath: p.ImportPath + "_test",
+ localPrefix: p.localPrefix,
+ Root: p.Root,
+ Dir: p.Dir,
+ GoFiles: p.XTestGoFiles,
+ Imports: p.XTestImports,
+ build: &build.Package{
+ ImportPos: p.build.XTestImportPos,
+ },
+ imports: append(ximports, ptest),
+ pkgdir: testDir,
+ fake: true,
+ Stale: true,
+ }
+ }
+
+ // Action for building pkg.test.
+ pmain = &Package{
+ Name: "main",
+ Dir: testDir,
+ GoFiles: []string{"_testmain.go"},
+ ImportPath: "testmain",
+ Root: p.Root,
+ imports: []*Package{ptest},
+ build: &build.Package{Name: "main"},
+ fake: true,
+ Stale: true,
+ }
+ if pxtest != nil {
+ pmain.imports = append(pmain.imports, pxtest)
+ }
+
+ // 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
+ }
+ pregexp := loadImport("regexp", "", &stk, nil)
+ if pregexp.Error != nil {
+ return nil, nil, nil, pregexp.Error
+ }
+ pmain.imports = append(pmain.imports, ptesting, pregexp)
+ computeStale(pmain)
+
+ if ptest != p {
+ a := b.action(modeBuild, modeBuild, ptest)
+ a.objdir = testDir + string(filepath.Separator)
+ a.objpkg = ptestObj
+ a.target = ptestObj
+ a.link = false
+ }
+
+ if pxtest != nil {
+ a := b.action(modeBuild, modeBuild, pxtest)
+ a.objdir = testDir + string(filepath.Separator)
+ a.objpkg = buildToolchain.pkgpath(testDir, pxtest)
+ a.target = a.objpkg
+ }
+
+ a := b.action(modeBuild, modeBuild, pmain)
+ a.objdir = testDir + string(filepath.Separator)
+ a.objpkg = filepath.Join(testDir, "main.a")
+ a.target = filepath.Join(testDir, testBinary) + exeSuffix
+ pmainAction := a
+
+ if testC {
+ // -c flag: create action to copy binary to ./test.out.
+ runAction = &action{
+ f: (*builder).install,
+ deps: []*action{pmainAction},
+ p: pmain,
+ target: testBinary + exeSuffix,
+ }
+ printAction = &action{p: p, deps: []*action{runAction}} // nop
+ } else {
+ // run test
+ runAction = &action{
+ f: (*builder).runTest,
+ deps: []*action{pmainAction},
+ p: p,
+ ignoreFail: true,
+ }
+ cleanAction := &action{
+ f: (*builder).cleanTest,
+ deps: []*action{runAction},
+ p: p,
+ }
+ printAction = &action{
+ f: (*builder).printTest,
+ deps: []*action{cleanAction},
+ p: p,
+ }
+ }
+
+ return pmainAction, runAction, printAction, nil
+}
+
+// runTest is the action for running a test binary.
+func (b *builder) runTest(a *action) error {
+ args := stringList(a.deps[0].target, testArgs)
+ a.testOutput = new(bytes.Buffer)
+
+ if buildN || buildX {
+ b.showcmd("", "%s", strings.Join(args, " "))
+ if buildN {
+ return nil
+ }
+ }
+
+ if a.failed {
+ // We were unable to build the binary.
+ a.failed = false
+ fmt.Fprintf(a.testOutput, "FAIL\t%s [build failed]\n", a.p.ImportPath)
+ setExitStatus(1)
+ return nil
+ }
+
+ cmd := exec.Command(args[0], args[1:]...)
+ cmd.Dir = a.p.Dir
+ var buf bytes.Buffer
+ if testStreamOutput {
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ } else {
+ cmd.Stdout = &buf
+ cmd.Stderr = &buf
+ }
+
+ t0 := time.Now()
+ err := cmd.Start()
+
+ // This is a last-ditch deadline to detect and
+ // stop wedged test binaries, to keep the builders
+ // running.
+ tick := time.NewTimer(testKillTimeout)
+ if err == nil {
+ done := make(chan error)
+ go func() {
+ done <- cmd.Wait()
+ }()
+ select {
+ case err = <-done:
+ // ok
+ case <-tick.C:
+ cmd.Process.Kill()
+ err = <-done
+ fmt.Fprintf(&buf, "*** Test killed: ran too long.\n")
+ }
+ tick.Stop()
+ }
+ out := buf.Bytes()
+ t1 := time.Now()
+ t := fmt.Sprintf("%.3fs", t1.Sub(t0).Seconds())
+ if err == nil {
+ if testShowPass {
+ a.testOutput.Write(out)
+ }
+ fmt.Fprintf(a.testOutput, "ok \t%s\t%s\n", a.p.ImportPath, t)
+ return nil
+ }
+
+ setExitStatus(1)
+ if len(out) > 0 {
+ a.testOutput.Write(out)
+ // assume printing the test binary's exit status is superfluous
+ } else {
+ fmt.Fprintf(a.testOutput, "%s\n", err)
+ }
+ fmt.Fprintf(a.testOutput, "FAIL\t%s\t%s\n", a.p.ImportPath, t)
+
+ return nil
+}
+
+// cleanTest is the action for cleaning up after a test.
+func (b *builder) cleanTest(a *action) error {
+ if buildWork {
+ return nil
+ }
+ run := a.deps[0]
+ testDir := filepath.Join(b.work, filepath.FromSlash(run.p.ImportPath+"/_test"))
+ os.RemoveAll(testDir)
+ return nil
+}
+
+// printTest is the action for printing a test result.
+func (b *builder) printTest(a *action) error {
+ clean := a.deps[0]
+ run := clean.deps[0]
+ os.Stdout.Write(run.testOutput.Bytes())
+ run.testOutput = nil
+ return nil
+}
+
+// notest is the action for testing a package with no test files.
+func (b *builder) notest(a *action) error {
+ fmt.Printf("? \t%s\t[no test files]\n", a.p.ImportPath)
+ return nil
+}
+
+// isTest tells whether name looks like a test (or benchmark, according to prefix).
+// It is a Test (say) if there is a character after Test that is not a lower-case letter.
+// We don't want TesticularCancer.
+func isTest(name, prefix string) bool {
+ if !strings.HasPrefix(name, prefix) {
+ return false
+ }
+ if len(name) == len(prefix) { // "Test" is ok
+ return true
+ }
+ rune, _ := utf8.DecodeRuneInString(name[len(prefix):])
+ return !unicode.IsLower(rune)
+}
+
+// writeTestmain writes the _testmain.go file for package p to
+// the file named out.
+func writeTestmain(out string, p *Package) error {
+ t := &testFuncs{
+ Package: p,
+ }
+ for _, file := range p.TestGoFiles {
+ if err := t.load(filepath.Join(p.Dir, file), "_test", &t.NeedTest); err != nil {
+ return err
+ }
+ }
+ for _, file := range p.XTestGoFiles {
+ if err := t.load(filepath.Join(p.Dir, file), "_xtest", &t.NeedXtest); err != nil {
+ return err
+ }
+ }
+
+ f, err := os.Create(out)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+
+ if err := testmainTmpl.Execute(f, t); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+type testFuncs struct {
+ Tests []testFunc
+ Benchmarks []testFunc
+ Examples []testFunc
+ Package *Package
+ NeedTest bool
+ NeedXtest bool
+}
+
+type testFunc struct {
+ Package string // imported package name (_test or _xtest)
+ Name string // function name
+ Output string // output, for examples
+}
+
+var testFileSet = token.NewFileSet()
+
+func (t *testFuncs) load(filename, pkg string, seen *bool) error {
+ f, err := parser.ParseFile(testFileSet, filename, nil, parser.ParseComments)
+ if err != nil {
+ return expandScanner(err)
+ }
+ for _, d := range f.Decls {
+ n, ok := d.(*ast.FuncDecl)
+ if !ok {
+ continue
+ }
+ if n.Recv != nil {
+ continue
+ }
+ name := n.Name.String()
+ switch {
+ case isTest(name, "Test"):
+ t.Tests = append(t.Tests, testFunc{pkg, name, ""})
+ *seen = true
+ case isTest(name, "Benchmark"):
+ t.Benchmarks = append(t.Benchmarks, testFunc{pkg, name, ""})
+ *seen = true
+ }
+ }
+ for _, e := range doc.Examples(f) {
+ if e.Output == "" {
+ // Don't run examples with no output.
+ continue
+ }
+ t.Examples = append(t.Examples, testFunc{pkg, "Example" + e.Name, e.Output})
+ *seen = true
+ }
+ return nil
+}
+
+var testmainTmpl = template.Must(template.New("main").Parse(`
+package main
+
+import (
+ "regexp"
+ "testing"
+
+{{if .NeedTest}}
+ _test {{.Package.ImportPath | printf "%q"}}
+{{end}}
+{{if .NeedXtest}}
+ _xtest {{.Package.ImportPath | printf "%s_test" | printf "%q"}}
+{{end}}
+)
+
+var tests = []testing.InternalTest{
+{{range .Tests}}
+ {"{{.Name}}", {{.Package}}.{{.Name}}},
+{{end}}
+}
+
+var benchmarks = []testing.InternalBenchmark{
+{{range .Benchmarks}}
+ {"{{.Name}}", {{.Package}}.{{.Name}}},
+{{end}}
+}
+
+var examples = []testing.InternalExample{
+{{range .Examples}}
+ {"{{.Name}}", {{.Package}}.{{.Name}}, {{.Output | printf "%q"}}},
+{{end}}
+}
+
+var matchPat string
+var matchRe *regexp.Regexp
+
+func matchString(pat, str string) (result bool, err error) {
+ if matchRe == nil || matchPat != pat {
+ matchPat = pat
+ matchRe, err = regexp.Compile(matchPat)
+ if err != nil {
+ return
+ }
+ }
+ return matchRe.MatchString(str), nil
+}
+
+func main() {
+ testing.Main(matchString, tests, benchmarks, examples)
+}
+
+`))
diff --git a/src/cmd/go/testdata/errmsg/x.go b/src/cmd/go/testdata/errmsg/x.go
new file mode 100644
index 000000000..60f5b6e98
--- /dev/null
+++ b/src/cmd/go/testdata/errmsg/x.go
@@ -0,0 +1,3 @@
+package foo
+
+import "bar"
diff --git a/src/cmd/go/testdata/errmsg/x1_test.go b/src/cmd/go/testdata/errmsg/x1_test.go
new file mode 100644
index 000000000..eb1a6798c
--- /dev/null
+++ b/src/cmd/go/testdata/errmsg/x1_test.go
@@ -0,0 +1,3 @@
+package foo_test
+
+import "bar"
diff --git a/src/cmd/go/testdata/errmsg/x_test.go b/src/cmd/go/testdata/errmsg/x_test.go
new file mode 100644
index 000000000..60f5b6e98
--- /dev/null
+++ b/src/cmd/go/testdata/errmsg/x_test.go
@@ -0,0 +1,3 @@
+package foo
+
+import "bar"
diff --git a/src/cmd/go/testdata/local/easy.go b/src/cmd/go/testdata/local/easy.go
new file mode 100644
index 000000000..4eeb517da
--- /dev/null
+++ b/src/cmd/go/testdata/local/easy.go
@@ -0,0 +1,7 @@
+package main
+
+import "./easysub"
+
+func main() {
+ easysub.Hello()
+}
diff --git a/src/cmd/go/testdata/local/easysub/easysub.go b/src/cmd/go/testdata/local/easysub/easysub.go
new file mode 100644
index 000000000..07040daee
--- /dev/null
+++ b/src/cmd/go/testdata/local/easysub/easysub.go
@@ -0,0 +1,7 @@
+package easysub
+
+import "fmt"
+
+func Hello() {
+ fmt.Println("easysub.Hello")
+}
diff --git a/src/cmd/go/testdata/local/easysub/main.go b/src/cmd/go/testdata/local/easysub/main.go
new file mode 100644
index 000000000..6c30b5236
--- /dev/null
+++ b/src/cmd/go/testdata/local/easysub/main.go
@@ -0,0 +1,9 @@
+// +build ignore
+
+package main
+
+import "."
+
+func main() {
+ easysub.Hello()
+}
diff --git a/src/cmd/go/testdata/local/hard.go b/src/cmd/go/testdata/local/hard.go
new file mode 100644
index 000000000..2ffac3fd7
--- /dev/null
+++ b/src/cmd/go/testdata/local/hard.go
@@ -0,0 +1,7 @@
+package main
+
+import "./sub"
+
+func main() {
+ sub.Hello()
+}
diff --git a/src/cmd/go/testdata/local/sub/sub.go b/src/cmd/go/testdata/local/sub/sub.go
new file mode 100644
index 000000000..d5dbf6d5f
--- /dev/null
+++ b/src/cmd/go/testdata/local/sub/sub.go
@@ -0,0 +1,12 @@
+package sub
+
+import (
+ "fmt"
+
+ subsub "./sub"
+)
+
+func Hello() {
+ fmt.Println("sub.Hello")
+ subsub.Hello()
+}
diff --git a/src/cmd/go/testdata/local/sub/sub/subsub.go b/src/cmd/go/testdata/local/sub/sub/subsub.go
new file mode 100644
index 000000000..4cc72233e
--- /dev/null
+++ b/src/cmd/go/testdata/local/sub/sub/subsub.go
@@ -0,0 +1,7 @@
+package subsub
+
+import "fmt"
+
+func Hello() {
+ fmt.Println("subsub.Hello")
+}
diff --git a/src/cmd/go/testdata/src/go-cmd-test/helloworld.go b/src/cmd/go/testdata/src/go-cmd-test/helloworld.go
new file mode 100644
index 000000000..002a5c740
--- /dev/null
+++ b/src/cmd/go/testdata/src/go-cmd-test/helloworld.go
@@ -0,0 +1,5 @@
+package main
+
+func main() {
+ println("hello world")
+}
diff --git a/src/cmd/go/testdata/testimport/p.go b/src/cmd/go/testdata/testimport/p.go
new file mode 100644
index 000000000..f94d2cd0e
--- /dev/null
+++ b/src/cmd/go/testdata/testimport/p.go
@@ -0,0 +1,3 @@
+package p
+
+func F() int { return 1 }
diff --git a/src/cmd/go/testdata/testimport/p1/p1.go b/src/cmd/go/testdata/testimport/p1/p1.go
new file mode 100644
index 000000000..fd315272e
--- /dev/null
+++ b/src/cmd/go/testdata/testimport/p1/p1.go
@@ -0,0 +1,3 @@
+package p1
+
+func F() int { return 1 }
diff --git a/src/cmd/go/testdata/testimport/p2/p2.go b/src/cmd/go/testdata/testimport/p2/p2.go
new file mode 100644
index 000000000..d4888865d
--- /dev/null
+++ b/src/cmd/go/testdata/testimport/p2/p2.go
@@ -0,0 +1,3 @@
+package p2
+
+func F() int { return 1 }
diff --git a/src/cmd/go/testdata/testimport/p_test.go b/src/cmd/go/testdata/testimport/p_test.go
new file mode 100644
index 000000000..a3fb4a9e2
--- /dev/null
+++ b/src/cmd/go/testdata/testimport/p_test.go
@@ -0,0 +1,13 @@
+package p
+
+import (
+ "./p1"
+
+ "testing"
+)
+
+func TestF(t *testing.T) {
+ if F() != p1.F() {
+ t.Fatal(F())
+ }
+}
diff --git a/src/cmd/go/testdata/testimport/x_test.go b/src/cmd/go/testdata/testimport/x_test.go
new file mode 100644
index 000000000..b253e3fd2
--- /dev/null
+++ b/src/cmd/go/testdata/testimport/x_test.go
@@ -0,0 +1,15 @@
+package p_test
+
+import (
+ . "../testimport"
+
+ "./p2"
+
+ "testing"
+)
+
+func TestF1(t *testing.T) {
+ if F() != p2.F() {
+ t.Fatal(F())
+ }
+}
diff --git a/src/cmd/go/testflag.go b/src/cmd/go/testflag.go
new file mode 100644
index 000000000..ecf5bf456
--- /dev/null
+++ b/src/cmd/go/testflag.go
@@ -0,0 +1,235 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "fmt"
+ "os"
+ "strconv"
+ "strings"
+)
+
+// The flag handling part of go test is large and distracting.
+// We can't use the flag package because some of the flags from
+// our command line are for us, and some are for 6.out, and
+// some are for both.
+
+var usageMessage = `Usage of go test:
+ -c=false: compile but do not run the test binary
+ -file=file_test.go: specify file to use for tests;
+ use multiple times for multiple files
+ -p=n: build and test up to n packages in parallel
+ -x=false: print command lines as they are executed
+
+ // These flags can be passed with or without a "test." prefix: -v or -test.v.
+ -bench="": passes -test.bench to test
+ -benchtime=1: passes -test.benchtime to test
+ -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
+ -parallel=0: passes -test.parallel to test
+ -run="": passes -test.run to test
+ -short=false: passes -test.short to test
+ -timeout=0: passes -test.timeout to test
+ -v=false: passes -test.v to test
+`
+
+// usage prints a usage message and exits.
+func testUsage() {
+ fmt.Fprint(os.Stderr, usageMessage)
+ setExitStatus(2)
+ exit()
+}
+
+// testFlagSpec defines a flag we know about.
+type testFlagSpec struct {
+ name string
+ boolVar *bool
+ passToTest bool // pass to Test
+ multiOK bool // OK to have multiple instances
+ present bool // flag has been seen
+}
+
+// testFlagDefn is the set of flags we process.
+var testFlagDefn = []*testFlagSpec{
+ // local.
+ {name: "c", boolVar: &testC},
+ {name: "file", multiOK: true},
+ {name: "i", boolVar: &testI},
+
+ // build flags.
+ {name: "a", boolVar: &buildA},
+ {name: "n", boolVar: &buildN},
+ {name: "p"},
+ {name: "x", boolVar: &buildX},
+ {name: "work", boolVar: &buildWork},
+ {name: "gcflags"},
+ {name: "ldflags"},
+ {name: "gccgoflags"},
+ {name: "tags"},
+ {name: "compiler"},
+
+ // passed to 6.out, adding a "test." prefix to the name if necessary: -v becomes -test.v.
+ {name: "bench", passToTest: true},
+ {name: "benchtime", passToTest: true},
+ {name: "cpu", passToTest: true},
+ {name: "cpuprofile", passToTest: true},
+ {name: "memprofile", passToTest: true},
+ {name: "memprofilerate", passToTest: true},
+ {name: "parallel", passToTest: true},
+ {name: "run", passToTest: true},
+ {name: "short", boolVar: new(bool), passToTest: true},
+ {name: "timeout", passToTest: true},
+ {name: "v", boolVar: &testV, passToTest: true},
+}
+
+// testFlags processes the command line, grabbing -x and -c, rewriting known flags
+// to have "test" before them, and reading the command line for the 6.out.
+// Unfortunately for us, we need to do our own flag processing because go test
+// grabs some flags but otherwise its command line is just a holding place for
+// pkg.test's arguments.
+// We allow known flags both before and after the package name list,
+// to allow both
+// go test fmt -custom-flag-for-fmt-test
+// go test -x math
+func testFlags(args []string) (packageNames, passToTest []string) {
+ inPkg := false
+ for i := 0; i < len(args); i++ {
+ if !strings.HasPrefix(args[i], "-") {
+ if !inPkg && packageNames == nil {
+ // First package name we've seen.
+ inPkg = true
+ }
+ if inPkg {
+ packageNames = append(packageNames, args[i])
+ continue
+ }
+ }
+
+ if inPkg {
+ // Found an argument beginning with "-"; end of package list.
+ inPkg = false
+ }
+
+ f, value, extraWord := testFlag(args, i)
+ if f == nil {
+ // This is a flag we do not know; we must assume
+ // that any args we see after this might be flag
+ // arguments, not package names.
+ inPkg = false
+ if packageNames == nil {
+ // make non-nil: we have seen the empty package list
+ packageNames = []string{}
+ }
+ passToTest = append(passToTest, args[i])
+ continue
+ }
+ switch f.name {
+ // bool flags.
+ case "a", "c", "i", "n", "x", "v", "work":
+ setBoolFlag(f.boolVar, value)
+ case "p":
+ setIntFlag(&buildP, value)
+ case "gcflags":
+ buildGcflags = strings.Fields(value)
+ case "ldflags":
+ buildLdflags = strings.Fields(value)
+ case "gccgoflags":
+ buildGccgoflags = strings.Fields(value)
+ case "tags":
+ buildContext.BuildTags = strings.Fields(value)
+ case "compiler":
+ buildCompiler{}.Set(value)
+ case "file":
+ testFiles = append(testFiles, value)
+ case "bench":
+ // record that we saw the flag; don't care about the value
+ testBench = true
+ case "timeout":
+ testTimeout = value
+ }
+ if extraWord {
+ i++
+ }
+ if f.passToTest {
+ passToTest = append(passToTest, "-test."+f.name+"="+value)
+ }
+ }
+ return
+}
+
+// testFlag sees if argument i is a known flag and returns its definition, value, and whether it consumed an extra word.
+func testFlag(args []string, i int) (f *testFlagSpec, value string, extra bool) {
+ arg := args[i]
+ if strings.HasPrefix(arg, "--") { // reduce two minuses to one
+ arg = arg[1:]
+ }
+ switch arg {
+ case "-?", "-h", "-help":
+ usage()
+ }
+ if arg == "" || arg[0] != '-' {
+ return
+ }
+ name := arg[1:]
+ // If there's already "test.", drop it for now.
+ if strings.HasPrefix(name, "test.") {
+ name = name[5:]
+ }
+ equals := strings.Index(name, "=")
+ if equals >= 0 {
+ value = name[equals+1:]
+ name = name[:equals]
+ }
+ for _, f = range testFlagDefn {
+ if name == f.name {
+ // Booleans are special because they have modes -x, -x=true, -x=false.
+ if f.boolVar != nil {
+ if equals < 0 { // otherwise, it's been set and will be verified in setBoolFlag
+ value = "true"
+ } else {
+ // verify it parses
+ setBoolFlag(new(bool), value)
+ }
+ } else { // Non-booleans must have a value.
+ extra = equals < 0
+ if extra {
+ if i+1 >= len(args) {
+ usage()
+ }
+ value = args[i+1]
+ }
+ }
+ if f.present && !f.multiOK {
+ usage()
+ }
+ f.present = true
+ return
+ }
+ }
+ f = nil
+ return
+}
+
+// setBoolFlag sets the addressed boolean to the value.
+func setBoolFlag(flag *bool, value string) {
+ x, err := strconv.ParseBool(value)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "go test: illegal bool flag value %s\n", value)
+ usage()
+ }
+ *flag = x
+}
+
+// setIntFlag sets the addressed integer to the value.
+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()
+ }
+ *flag = x
+}
diff --git a/src/cmd/go/tool.go b/src/cmd/go/tool.go
new file mode 100644
index 000000000..cb463a2e7
--- /dev/null
+++ b/src/cmd/go/tool.go
@@ -0,0 +1,125 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "fmt"
+ "go/build"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "sort"
+ "strings"
+)
+
+var cmdTool = &Command{
+ Run: runTool,
+ UsageLine: "tool [-n] command [args...]",
+ Short: "run specified go tool",
+ Long: `
+Tool runs the go tool command identified by the arguments.
+With no arguments it prints the list of known tools.
+
+The -n flag causes tool to print the command that would be
+executed but not execute it.
+
+For more about each tool command, see 'go tool command -h'.
+`,
+}
+
+var (
+ toolGOOS = runtime.GOOS
+ toolGOARCH = runtime.GOARCH
+ toolIsWindows = toolGOOS == "windows"
+ toolDir = build.ToolDir
+
+ toolN bool
+)
+
+func init() {
+ cmdTool.Flag.BoolVar(&toolN, "n", false, "")
+}
+
+const toolWindowsExtension = ".exe"
+
+func tool(name string) string {
+ p := filepath.Join(toolDir, name)
+ if toolIsWindows {
+ p += toolWindowsExtension
+ }
+ return p
+}
+
+func runTool(cmd *Command, args []string) {
+ if len(args) == 0 {
+ listTools()
+ return
+ }
+ toolName := args[0]
+ // The tool name must be lower-case letters, numbers or underscores.
+ for _, c := range toolName {
+ switch {
+ case 'a' <= c && c <= 'z', '0' <= c && c <= '9', c == '_':
+ default:
+ fmt.Fprintf(os.Stderr, "go tool: bad tool name %q\n", toolName)
+ setExitStatus(2)
+ return
+ }
+ }
+ 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)
+ return
+ }
+
+ if toolN {
+ fmt.Printf("%s %s\n", toolPath, strings.Join(args[1:], " "))
+ return
+ }
+ toolCmd := &exec.Cmd{
+ Path: toolPath,
+ Args: args,
+ Stdin: os.Stdin,
+ Stdout: os.Stdout,
+ Stderr: os.Stderr,
+ }
+ err := toolCmd.Run()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "go tool %s: %s\n", toolName, err)
+ setExitStatus(1)
+ return
+ }
+}
+
+// listTools prints a list of the available tools in the tools directory.
+func listTools() {
+ f, err := os.Open(toolDir)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "go tool: no tool directory: %s\n", err)
+ setExitStatus(2)
+ return
+ }
+ defer f.Close()
+ names, err := f.Readdirnames(-1)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "go tool: can't read directory: %s\n", err)
+ setExitStatus(2)
+ return
+ }
+
+ sort.Strings(names)
+ for _, name := range names {
+ // Unify presentation by going to lower case.
+ name = strings.ToLower(name)
+ // If it's windows, don't show the .exe suffix.
+ if toolIsWindows && strings.HasSuffix(name, toolWindowsExtension) {
+ name = name[:len(name)-len(toolWindowsExtension)]
+ }
+ fmt.Println(name)
+ }
+}
diff --git a/src/cmd/go/vcs.go b/src/cmd/go/vcs.go
new file mode 100644
index 000000000..5f63f8b56
--- /dev/null
+++ b/src/cmd/go/vcs.go
@@ -0,0 +1,674 @@
+// 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 (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "log"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "regexp"
+ "strings"
+)
+
+// A vcsCmd describes how to use a version control system
+// like Mercurial, Git, or Subversion.
+type vcsCmd struct {
+ name string
+ cmd string // name of binary to invoke command
+
+ createCmd string // command to download a fresh copy of a repository
+ downloadCmd string // command to download updates into an existing repository
+
+ tagCmd []tagCmd // commands to list tags
+ tagLookupCmd []tagCmd // commands to lookup tags before running tagSyncCmd
+ tagSyncCmd string // command to sync to specific tag
+ tagSyncDefault string // command to sync to default tag
+
+ scheme []string
+ pingCmd string
+}
+
+// A tagCmd describes a command to list available tags
+// that can be passed to tagSyncCmd.
+type tagCmd struct {
+ cmd string // command to list tags
+ pattern string // regexp to extract tags from list
+}
+
+// vcsList lists the known version control systems
+var vcsList = []*vcsCmd{
+ vcsHg,
+ vcsGit,
+ vcsSvn,
+ vcsBzr,
+}
+
+// vcsByCmd returns the version control system for the given
+// command name (hg, git, svn, bzr).
+func vcsByCmd(cmd string) *vcsCmd {
+ for _, vcs := range vcsList {
+ if vcs.cmd == cmd {
+ return vcs
+ }
+ }
+ return nil
+}
+
+// vcsHg describes how to use Mercurial.
+var vcsHg = &vcsCmd{
+ name: "Mercurial",
+ cmd: "hg",
+
+ createCmd: "clone -U {repo} {dir}",
+ downloadCmd: "pull",
+
+ // We allow both tag and branch names as 'tags'
+ // for selecting a version. This lets people have
+ // a go.release.r60 branch and a go1 branch
+ // and make changes in both, without constantly
+ // editing .hgtags.
+ tagCmd: []tagCmd{
+ {"tags", `^(\S+)`},
+ {"branches", `^(\S+)`},
+ },
+ tagSyncCmd: "update -r {tag}",
+ tagSyncDefault: "update default",
+
+ scheme: []string{"https", "http"},
+ pingCmd: "identify {scheme}://{repo}",
+}
+
+// vcsGit describes how to use Git.
+var vcsGit = &vcsCmd{
+ name: "Git",
+ cmd: "git",
+
+ createCmd: "clone {repo} {dir}",
+ downloadCmd: "fetch",
+
+ tagCmd: []tagCmd{
+ // tags/xxx matches a git tag named xxx
+ // origin/xxx matches a git branch named xxx on the default remote repository
+ {"show-ref", `(?:tags|origin)/(\S+)$`},
+ },
+ tagLookupCmd: []tagCmd{
+ {"show-ref tags/{tag} origin/{tag}", `((?:tags|origin)/\S+)$`},
+ },
+ tagSyncCmd: "checkout {tag}",
+ tagSyncDefault: "checkout origin/master",
+
+ scheme: []string{"git", "https", "http", "git+ssh"},
+ pingCmd: "ls-remote {scheme}://{repo}",
+}
+
+// vcsBzr describes how to use Bazaar.
+var vcsBzr = &vcsCmd{
+ name: "Bazaar",
+ cmd: "bzr",
+
+ createCmd: "branch {repo} {dir}",
+
+ // Without --overwrite bzr will not pull tags that changed.
+ // Replace by --overwrite-tags after http://pad.lv/681792 goes in.
+ downloadCmd: "pull --overwrite",
+
+ tagCmd: []tagCmd{{"tags", `^(\S+)`}},
+ tagSyncCmd: "update -r {tag}",
+ tagSyncDefault: "update -r revno:-1",
+
+ scheme: []string{"https", "http", "bzr", "bzr+ssh"},
+ pingCmd: "info {scheme}://{repo}",
+}
+
+// vcsSvn describes how to use Subversion.
+var vcsSvn = &vcsCmd{
+ name: "Subversion",
+ cmd: "svn",
+
+ createCmd: "checkout {repo} {dir}",
+ downloadCmd: "update",
+
+ // There is no tag command in subversion.
+ // The branch information is all in the path names.
+
+ scheme: []string{"https", "http", "svn", "svn+ssh"},
+ pingCmd: "info {scheme}://{repo}",
+}
+
+func (v *vcsCmd) String() string {
+ return v.name
+}
+
+// run runs the command line cmd in the given directory.
+// keyval is a list of key, value pairs. run expands
+// instances of {key} in cmd into value, but only after
+// splitting cmd into individual arguments.
+// If an error occurs, run prints the command line and the
+// command's combined stdout+stderr to standard error.
+// Otherwise run discards the command's output.
+func (v *vcsCmd) run(dir string, cmd string, keyval ...string) error {
+ _, err := v.run1(dir, cmd, keyval, true)
+ return err
+}
+
+// runVerboseOnly is like run but only generates error output to standard error in verbose mode.
+func (v *vcsCmd) runVerboseOnly(dir string, cmd string, keyval ...string) error {
+ _, err := v.run1(dir, cmd, keyval, false)
+ return err
+}
+
+// runOutput is like run but returns the output of the command.
+func (v *vcsCmd) runOutput(dir string, cmd string, keyval ...string) ([]byte, error) {
+ return v.run1(dir, cmd, keyval, true)
+}
+
+// run1 is the generalized implementation of run and runOutput.
+func (v *vcsCmd) run1(dir string, cmdline string, keyval []string, verbose bool) ([]byte, error) {
+ m := make(map[string]string)
+ for i := 0; i < len(keyval); i += 2 {
+ m[keyval[i]] = keyval[i+1]
+ }
+ args := strings.Fields(cmdline)
+ for i, arg := range args {
+ args[i] = expand(m, arg)
+ }
+
+ cmd := exec.Command(v.cmd, args...)
+ cmd.Dir = dir
+ if buildX {
+ fmt.Printf("cd %s\n", dir)
+ fmt.Printf("%s %s\n", v.cmd, strings.Join(args, " "))
+ }
+ var buf bytes.Buffer
+ cmd.Stdout = &buf
+ cmd.Stderr = &buf
+ err := cmd.Run()
+ out := buf.Bytes()
+ if err != nil {
+ if verbose || buildV {
+ fmt.Fprintf(os.Stderr, "# cd %s; %s %s\n", dir, v.cmd, strings.Join(args, " "))
+ os.Stderr.Write(out)
+ }
+ return nil, err
+ }
+ return out, nil
+}
+
+// ping pings to determine scheme to use.
+func (v *vcsCmd) ping(scheme, repo string) error {
+ return v.runVerboseOnly(".", v.pingCmd, "scheme", scheme, "repo", repo)
+}
+
+// create creates a new copy of repo in dir.
+// The parent of dir must exist; dir must not.
+func (v *vcsCmd) create(dir, repo string) error {
+ return v.run(".", v.createCmd, "dir", dir, "repo", repo)
+}
+
+// download downloads any new changes for the repo in dir.
+func (v *vcsCmd) download(dir string) error {
+ return v.run(dir, v.downloadCmd)
+}
+
+// tags returns the list of available tags for the repo in dir.
+func (v *vcsCmd) tags(dir string) ([]string, error) {
+ var tags []string
+ for _, tc := range v.tagCmd {
+ out, err := v.runOutput(dir, tc.cmd)
+ if err != nil {
+ return nil, err
+ }
+ re := regexp.MustCompile(`(?m-s)` + tc.pattern)
+ for _, m := range re.FindAllStringSubmatch(string(out), -1) {
+ tags = append(tags, m[1])
+ }
+ }
+ return tags, nil
+}
+
+// tagSync syncs the repo in dir to the named tag,
+// which either is a tag returned by tags or is v.tagDefault.
+func (v *vcsCmd) tagSync(dir, tag string) error {
+ if v.tagSyncCmd == "" {
+ return nil
+ }
+ if tag != "" {
+ for _, tc := range v.tagLookupCmd {
+ out, err := v.runOutput(dir, tc.cmd, "tag", tag)
+ if err != nil {
+ return err
+ }
+ re := regexp.MustCompile(`(?m-s)` + tc.pattern)
+ m := re.FindStringSubmatch(string(out))
+ if len(m) > 1 {
+ tag = m[1]
+ break
+ }
+ }
+ }
+ if tag == "" && v.tagSyncDefault != "" {
+ return v.run(dir, v.tagSyncDefault)
+ }
+ return v.run(dir, v.tagSyncCmd, "tag", tag)
+}
+
+// A vcsPath describes how to convert an import path into a
+// version control system and repository name.
+type vcsPath struct {
+ prefix string // prefix this description applies to
+ re string // pattern for import path
+ repo string // repository to use (expand with match of re)
+ vcs string // version control system to use (expand with match of re)
+ check func(match map[string]string) error // additional checks
+ ping bool // ping for scheme to use to download repo
+
+ regexp *regexp.Regexp // cached compiled form of re
+}
+
+// vcsForDir inspects dir and its parents to determine the
+// version control system and code repository to use.
+// On return, root is the import path
+// corresponding to the root of the repository
+// (thus root is a prefix of importPath).
+func vcsForDir(p *Package) (vcs *vcsCmd, root string, err error) {
+ // Clean and double-check that dir is in (a subdirectory of) srcRoot.
+ dir := filepath.Clean(p.Dir)
+ srcRoot := filepath.Clean(p.build.SrcRoot)
+ if len(dir) <= len(srcRoot) || dir[len(srcRoot)] != filepath.Separator {
+ return nil, "", fmt.Errorf("directory %q is outside source root %q", dir, srcRoot)
+ }
+
+ for len(dir) > len(srcRoot) {
+ for _, vcs := range vcsList {
+ if fi, err := os.Stat(filepath.Join(dir, "."+vcs.cmd)); err == nil && fi.IsDir() {
+ return vcs, dir[len(srcRoot)+1:], nil
+ }
+ }
+
+ // Move to parent.
+ ndir := filepath.Dir(dir)
+ if len(ndir) >= len(dir) {
+ // Shouldn't happen, but just in case, stop.
+ break
+ }
+ dir = ndir
+ }
+
+ return nil, "", fmt.Errorf("directory %q is not using a known version control system", dir)
+}
+
+// repoRoot represents a version control system, a repo, and a root of
+// where to put it on disk.
+type repoRoot struct {
+ vcs *vcsCmd
+
+ // repo is the repository URL, including scheme
+ repo string
+
+ // root is the import path corresponding to the root of the
+ // repository
+ root string
+}
+
+// repoRootForImportPath analyzes importPath to determine the
+// version control system, and code repository to use.
+func repoRootForImportPath(importPath string) (*repoRoot, error) {
+ rr, err := repoRootForImportPathStatic(importPath, "")
+ if err == errUnknownSite {
+ rr, err = repoRootForImportDynamic(importPath)
+
+ // repoRootForImportDynamic returns error detail
+ // that is irrelevant if the user didn't intend to use a
+ // dynamic import in the first place.
+ // Squelch it.
+ if err != nil {
+ if buildV {
+ log.Printf("import %q: %v", importPath, err)
+ }
+ err = fmt.Errorf("unrecognized import path %q", importPath)
+ }
+ }
+
+ if err == nil && strings.Contains(importPath, "...") && strings.Contains(rr.root, "...") {
+ // Do not allow wildcards in the repo root.
+ rr = nil
+ err = fmt.Errorf("cannot expand ... in %q", importPath)
+ }
+ return rr, err
+}
+
+var errUnknownSite = errors.New("dynamic lookup required to find mapping")
+
+// repoRootForImportPathStatic attempts to map importPath to a
+// repoRoot using the commonly-used VCS hosting sites in vcsPaths
+// (github.com/user/dir), or from a fully-qualified importPath already
+// containing its VCS type (foo.com/repo.git/dir)
+//
+// If scheme is non-empty, that scheme is forced.
+func repoRootForImportPathStatic(importPath, scheme string) (*repoRoot, error) {
+ if strings.Contains(importPath, "://") {
+ return nil, fmt.Errorf("invalid import path %q", importPath)
+ }
+ for _, srv := range vcsPaths {
+ if !strings.HasPrefix(importPath, srv.prefix) {
+ continue
+ }
+ m := srv.regexp.FindStringSubmatch(importPath)
+ if m == nil {
+ if srv.prefix != "" {
+ return nil, fmt.Errorf("invalid %s import path %q", srv.prefix, importPath)
+ }
+ continue
+ }
+
+ // Build map of named subexpression matches for expand.
+ match := map[string]string{
+ "prefix": srv.prefix,
+ "import": importPath,
+ }
+ for i, name := range srv.regexp.SubexpNames() {
+ if name != "" && match[name] == "" {
+ match[name] = m[i]
+ }
+ }
+ if srv.vcs != "" {
+ match["vcs"] = expand(match, srv.vcs)
+ }
+ if srv.repo != "" {
+ match["repo"] = expand(match, srv.repo)
+ }
+ if srv.check != nil {
+ if err := srv.check(match); err != nil {
+ return nil, err
+ }
+ }
+ vcs := vcsByCmd(match["vcs"])
+ if vcs == nil {
+ return nil, fmt.Errorf("unknown version control system %q", match["vcs"])
+ }
+ if srv.ping {
+ if scheme != "" {
+ match["repo"] = scheme + "://" + match["repo"]
+ } else {
+ for _, scheme := range vcs.scheme {
+ if vcs.ping(scheme, match["repo"]) == nil {
+ match["repo"] = scheme + "://" + match["repo"]
+ break
+ }
+ }
+ }
+ }
+ rr := &repoRoot{
+ vcs: vcs,
+ repo: match["repo"],
+ root: match["root"],
+ }
+ return rr, nil
+ }
+ return nil, errUnknownSite
+}
+
+// repoRootForImportDynamic finds a *repoRoot for a custom domain that's not
+// statically known by repoRootForImportPathStatic.
+//
+// This handles "vanity import paths" like "name.tld/pkg/foo".
+func repoRootForImportDynamic(importPath string) (*repoRoot, error) {
+ slash := strings.Index(importPath, "/")
+ if slash < 0 {
+ return nil, fmt.Errorf("missing / in import %q", importPath)
+ }
+ urlStr, body, err := httpsOrHTTP(importPath)
+ if err != nil {
+ return nil, fmt.Errorf("http/https fetch for import %q: %v", importPath, err)
+ }
+ defer body.Close()
+ metaImport, err := matchGoImport(parseMetaGoImports(body), importPath)
+ if err != nil {
+ if err != errNoMatch {
+ return nil, fmt.Errorf("parse %s: %v", urlStr, err)
+ }
+ return nil, fmt.Errorf("parse %s: no go-import meta tags", urlStr)
+ }
+ if buildV {
+ log.Printf("get %q: found meta tag %#v at %s", importPath, metaImport, urlStr)
+ }
+ // If the import was "uni.edu/bob/project", which said the
+ // prefix was "uni.edu" and the RepoRoot was "evilroot.com",
+ // make sure we don't trust Bob and check out evilroot.com to
+ // "uni.edu" yet (possibly overwriting/preempting another
+ // non-evil student). Instead, first verify the root and see
+ // if it matches Bob's claim.
+ if metaImport.Prefix != importPath {
+ if buildV {
+ log.Printf("get %q: verifying non-authoritative meta tag", importPath)
+ }
+ urlStr0 := urlStr
+ urlStr, body, err = httpsOrHTTP(metaImport.Prefix)
+ if err != nil {
+ return nil, fmt.Errorf("fetch %s: %v", urlStr, err)
+ }
+ imports := parseMetaGoImports(body)
+ if len(imports) == 0 {
+ return nil, fmt.Errorf("fetch %s: no go-import meta tag", urlStr)
+ }
+ metaImport2, err := matchGoImport(imports, importPath)
+ if err != nil || metaImport != metaImport2 {
+ return nil, fmt.Errorf("%s and %s disagree about go-import for %s", urlStr0, urlStr, metaImport.Prefix)
+ }
+ }
+
+ if !strings.Contains(metaImport.RepoRoot, "://") {
+ return nil, fmt.Errorf("%s: invalid repo root %q; no scheme", urlStr, metaImport.RepoRoot)
+ }
+ rr := &repoRoot{
+ vcs: vcsByCmd(metaImport.VCS),
+ repo: metaImport.RepoRoot,
+ root: metaImport.Prefix,
+ }
+ if rr.vcs == nil {
+ return nil, fmt.Errorf("%s: unknown vcs %q", urlStr, metaImport.VCS)
+ }
+ return rr, nil
+}
+
+// metaImport represents the parsed <meta name="go-import"
+// content="prefix vcs reporoot" /> tags from HTML files.
+type metaImport struct {
+ Prefix, VCS, RepoRoot string
+}
+
+// errNoMatch is returned from matchGoImport when there's no applicable match.
+var errNoMatch = errors.New("no import match")
+
+// matchGoImport returns the metaImport from imports matching importPath.
+// An error is returned if there are multiple matches.
+// errNoMatch is returned if none match.
+func matchGoImport(imports []metaImport, importPath string) (_ metaImport, err error) {
+ match := -1
+ for i, im := range imports {
+ if !strings.HasPrefix(importPath, im.Prefix) {
+ continue
+ }
+ if match != -1 {
+ err = fmt.Errorf("multiple meta tags match import path %q", importPath)
+ return
+ }
+ match = i
+ }
+ if match == -1 {
+ err = errNoMatch
+ return
+ }
+ return imports[match], nil
+}
+
+// expand rewrites s to replace {k} with match[k] for each key k in match.
+func expand(match map[string]string, s string) string {
+ for k, v := range match {
+ s = strings.Replace(s, "{"+k+"}", v, -1)
+ }
+ return s
+}
+
+// vcsPaths lists the known vcs paths.
+var vcsPaths = []*vcsPath{
+ // Google Code - new syntax
+ {
+ prefix: "code.google.com/",
+ re: `^(?P<root>code\.google\.com/p/(?P<project>[a-z0-9\-]+)(\.(?P<subrepo>[a-z0-9\-]+))?)(/[A-Za-z0-9_.\-]+)*$`,
+ repo: "https://{root}",
+ check: googleCodeVCS,
+ },
+
+ // Google Code - old syntax
+ {
+ re: `^(?P<project>[a-z0-9_\-.]+)\.googlecode\.com/(git|hg|svn)(?P<path>/.*)?$`,
+ check: oldGoogleCode,
+ },
+
+ // Github
+ {
+ prefix: "github.com/",
+ re: `^(?P<root>github\.com/[A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+)(/[A-Za-z0-9_.\-]+)*$`,
+ vcs: "git",
+ repo: "https://{root}",
+ check: noVCSSuffix,
+ },
+
+ // Bitbucket
+ {
+ prefix: "bitbucket.org/",
+ re: `^(?P<root>bitbucket\.org/(?P<bitname>[A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+))(/[A-Za-z0-9_.\-]+)*$`,
+ repo: "https://{root}",
+ check: bitbucketVCS,
+ },
+
+ // Launchpad
+ {
+ prefix: "launchpad.net/",
+ re: `^(?P<root>launchpad\.net/((?P<project>[A-Za-z0-9_.\-]+)(?P<series>/[A-Za-z0-9_.\-]+)?|~[A-Za-z0-9_.\-]+/(\+junk|[A-Za-z0-9_.\-]+)/[A-Za-z0-9_.\-]+))(/[A-Za-z0-9_.\-]+)*$`,
+ vcs: "bzr",
+ repo: "https://{root}",
+ check: launchpadVCS,
+ },
+
+ // General syntax for any server.
+ {
+ re: `^(?P<root>(?P<repo>([a-z0-9.\-]+\.)+[a-z0-9.\-]+(:[0-9]+)?/[A-Za-z0-9_.\-/]*?)\.(?P<vcs>bzr|git|hg|svn))(/[A-Za-z0-9_.\-]+)*$`,
+ ping: true,
+ },
+}
+
+func init() {
+ // fill in cached regexps.
+ // Doing this eagerly discovers invalid regexp syntax
+ // without having to run a command that needs that regexp.
+ for _, srv := range vcsPaths {
+ srv.regexp = regexp.MustCompile(srv.re)
+ }
+}
+
+// noVCSSuffix checks that the repository name does not
+// end in .foo for any version control system foo.
+// The usual culprit is ".git".
+func noVCSSuffix(match map[string]string) error {
+ repo := match["repo"]
+ for _, vcs := range vcsList {
+ if strings.HasSuffix(repo, "."+vcs.cmd) {
+ return fmt.Errorf("invalid version control suffix in %s path", match["prefix"])
+ }
+ }
+ return nil
+}
+
+var googleCheckout = regexp.MustCompile(`id="checkoutcmd">(hg|git|svn)`)
+
+// googleCodeVCS determines the version control system for
+// a code.google.com repository, by scraping the project's
+// /source/checkout page.
+func googleCodeVCS(match map[string]string) error {
+ if err := noVCSSuffix(match); err != nil {
+ return err
+ }
+ data, err := httpGET(expand(match, "https://code.google.com/p/{project}/source/checkout?repo={subrepo}"))
+ if err != nil {
+ return err
+ }
+
+ if m := googleCheckout.FindSubmatch(data); m != nil {
+ if vcs := vcsByCmd(string(m[1])); vcs != nil {
+ // Subversion requires the old URLs.
+ // TODO: Test.
+ if vcs == vcsSvn {
+ if match["subrepo"] != "" {
+ return fmt.Errorf("sub-repositories not supported in Google Code Subversion projects")
+ }
+ match["repo"] = expand(match, "https://{project}.googlecode.com/svn")
+ }
+ match["vcs"] = vcs.cmd
+ return nil
+ }
+ }
+
+ return fmt.Errorf("unable to detect version control system for code.google.com/ path")
+}
+
+// oldGoogleCode is invoked for old-style foo.googlecode.com paths.
+// It prints an error giving the equivalent new path.
+func oldGoogleCode(match map[string]string) error {
+ return fmt.Errorf("invalid Google Code import path: use %s instead",
+ expand(match, "code.google.com/p/{project}{path}"))
+}
+
+// bitbucketVCS determines the version control system for a
+// BitBucket repository, by using the BitBucket API.
+func bitbucketVCS(match map[string]string) error {
+ if err := noVCSSuffix(match); err != nil {
+ return err
+ }
+
+ var resp struct {
+ SCM string `json:"scm"`
+ }
+ url := expand(match, "https://api.bitbucket.org/1.0/repositories/{bitname}")
+ data, err := httpGET(url)
+ if err != nil {
+ return err
+ }
+ if err := json.Unmarshal(data, &resp); err != nil {
+ return fmt.Errorf("decoding %s: %v", url, err)
+ }
+
+ if vcsByCmd(resp.SCM) != nil {
+ match["vcs"] = resp.SCM
+ if resp.SCM == "git" {
+ match["repo"] += ".git"
+ }
+ return nil
+ }
+
+ return fmt.Errorf("unable to detect version control system for bitbucket.org/ path")
+}
+
+// launchpadVCS solves the ambiguity for "lp.net/project/foo". In this case,
+// "foo" could be a series name registered in Launchpad with its own branch,
+// and it could also be the name of a directory within the main project
+// branch one level up.
+func launchpadVCS(match map[string]string) error {
+ if match["project"] == "" || match["series"] == "" {
+ return nil
+ }
+ _, err := httpGET(expand(match, "https://code.launchpad.net/{project}{series}/.bzr/branch-format"))
+ if err != nil {
+ match["root"] = expand(match, "launchpad.net/{project}")
+ match["repo"] = expand(match, "https://{root}")
+ }
+ return nil
+}
diff --git a/src/cmd/go/version.go b/src/cmd/go/version.go
new file mode 100644
index 000000000..09e2f1633
--- /dev/null
+++ b/src/cmd/go/version.go
@@ -0,0 +1,25 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "fmt"
+ "runtime"
+)
+
+var cmdVersion = &Command{
+ Run: runVersion,
+ UsageLine: "version",
+ Short: "print Go version",
+ Long: `Version prints the Go version, as reported by runtime.Version.`,
+}
+
+func runVersion(cmd *Command, args []string) {
+ if len(args) != 0 {
+ cmd.Usage()
+ }
+
+ fmt.Printf("go version %s\n", runtime.Version())
+}
diff --git a/src/cmd/go/vet.go b/src/cmd/go/vet.go
new file mode 100644
index 000000000..a672b9910
--- /dev/null
+++ b/src/cmd/go/vet.go
@@ -0,0 +1,30 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+var cmdVet = &Command{
+ Run: runVet,
+ UsageLine: "vet [packages]",
+ Short: "run go tool vet on packages",
+ 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 specifying packages, see 'go help packages'.
+
+To run the vet tool with specific options, run 'go tool vet'.
+
+See also: go fmt, go fix.
+ `,
+}
+
+func runVet(cmd *Command, args []string) {
+ for _, pkg := range packages(args) {
+ // Use pkg.gofiles instead of pkg.Dir so that
+ // the command only applies to this package,
+ // not to packages in subdirectories.
+ run(tool("vet"), relPaths(pkg.gofiles))
+ }
+}
diff --git a/src/cmd/godefs/Makefile b/src/cmd/godefs/Makefile
deleted file mode 100644
index 77cd26c04..000000000
--- a/src/cmd/godefs/Makefile
+++ /dev/null
@@ -1,19 +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 ../../Make.inc
-O:=$(HOST_O)
-
-TARG=godefs
-OFILES=\
- main.$O\
- stabs.$O\
- util.$O\
-
-HFILES=a.h
-
-include ../../Make.ccmd
-
-test: $(TARG)
- ./test.sh
diff --git a/src/cmd/godefs/a.h b/src/cmd/godefs/a.h
deleted file mode 100644
index 9b4957467..000000000
--- a/src/cmd/godefs/a.h
+++ /dev/null
@@ -1,104 +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 <u.h>
-#include <libc.h>
-#include <bio.h>
-
-enum
-{
- Void = 1,
- Int8,
- Uint8,
- Int16,
- Uint16,
- Int32,
- Uint32,
- Int64,
- Uint64,
- Float32,
- Float64,
- Ptr,
- Struct,
- Array,
- Union,
- Typedef,
-};
-
-typedef struct Field Field;
-typedef struct Type Type;
-
-struct Type
-{
- Type *next; // next in hash table
-
- // stabs name and two-integer id
- char *name;
- int n1;
- int n2;
-
- // int kind
- int kind;
-
- // sub-type for ptr, array
- Type *type;
-
- // struct fields
- Field *f;
- int nf;
- int size;
-
- int saved; // recorded in typ array
- int warned; // warned about needing type
- int printed; // has the definition been printed yet?
-};
-
-struct Field
-{
- char *name;
- Type *type;
- int offset;
- int size;
-};
-
-// Constants
-typedef struct Const Const;
-struct Const
-{
- char *name;
- vlong value;
-};
-
-// Recorded constants and types, to be printed.
-extern Const *con;
-extern int ncon;
-extern Type **typ;
-extern int ntyp;
-extern int kindsize[];
-
-// Language output
-typedef struct Lang Lang;
-struct Lang
-{
- char *constbegin;
- char *constfmt;
- char *constend;
-
- char *typdef;
- char *typdefend;
-
- char *structbegin;
- char *unionbegin;
- char *structpadfmt;
- char *structend;
-
- int (*typefmt)(Fmt*);
-};
-
-extern Lang go, c;
-
-void* emalloc(int);
-char* estrdup(char*);
-void* erealloc(void*, int);
-void parsestabtype(char*);
diff --git a/src/cmd/godefs/doc.go b/src/cmd/godefs/doc.go
deleted file mode 100644
index 365c7cf6e..000000000
--- a/src/cmd/godefs/doc.go
+++ /dev/null
@@ -1,99 +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.
-
-/*
-
-Godefs is a bootstrapping tool for porting the Go runtime to new systems.
-It translates C type declarations into C or Go type declarations
-with the same memory layout.
-
-Usage: godefs [-g package] [-c cc] [-f cc-arg]... [defs.c ...]
-
-Godefs takes as input a host-compilable C file that includes
-standard system headers. From that input file, it generates
-a standalone (no #includes) C or Go file containing equivalent
-definitions.
-
-The input to godefs is a C input file that can be compiled by
-the host system's standard C compiler (typically gcc).
-This file is expected to define new types and enumerated constants
-whose names begin with $ (a legal identifier character in gcc).
-Godefs compile the given input file with the host compiler and
-then parses the debug info embedded in the assembly output.
-This is far easier than reading system headers on most machines.
-
-The output from godefs is either C output intended for the
-Plan 9 C compiler tool chain (6c, 8c, or 5c) or Go output.
-
-The options are:
-
- -g package
- generate Go output using the given package name.
- In the Go output, struct fields have leading xx_ prefixes
- removed and the first character capitalized (exported).
-
- -c cc
- set the name of the host system's C compiler (default "gcc")
-
- -f cc-arg
- add cc-arg to the command line when invoking the system C compiler
- (for example, -f -m64 to invoke gcc -m64).
- Repeating this option adds multiple flags to the command line.
-
-For example, if this is x.c:
-
- #include <sys/stat.h>
-
- typedef struct timespec $Timespec;
- enum {
- $S_IFMT = S_IFMT,
- $S_IFIFO = S_IFIFO,
- $S_IFCHR = S_IFCHR,
- };
-
-then "godefs x.c" generates:
-
- // godefs x.c
- // MACHINE GENERATED - DO NOT EDIT.
-
- // Constants
- enum {
- S_IFMT = 0xf000,
- S_IFIFO = 0x1000,
- S_IFCHR = 0x2000,
- };
-
- // Types
- #pragma pack on
-
- typedef struct Timespec Timespec;
- struct Timespec {
- int64 tv_sec;
- int64 tv_nsec;
- };
- #pragma pack off
-
-and "godefs -g MyPackage x.c" generates:
-
- // godefs -g MyPackage x.c
- // MACHINE GENERATED - DO NOT EDIT.
-
- package MyPackage
-
- // Constants
- const (
- S_IFMT = 0xf000;
- S_IFIFO = 0x1000;
- S_IFCHR = 0x2000;
- )
-
- // Types
-
- type Timespec struct {
- Sec int64;
- Nsec int64;
- }
-
-*/
-package documentation
diff --git a/src/cmd/godefs/main.c b/src/cmd/godefs/main.c
deleted file mode 100644
index 6a8630179..000000000
--- a/src/cmd/godefs/main.c
+++ /dev/null
@@ -1,606 +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.
-
-// Godefs takes as input a host-compilable C file that includes
-// standard system headers. From that input file, it generates
-// a standalone (no #includes) C or Go file containing equivalent
-// definitions.
-//
-// The input C file is expected to define new types and enumerated
-// constants whose names begin with $ (a legal identifier character
-// in gcc). The output is the standalone definitions of those names,
-// with the $ removed.
-//
-// For example, if this is x.c:
-//
-// #include <sys/stat.h>
-//
-// typedef struct timespec $Timespec;
-// typedef struct stat $Stat;
-// enum {
-// $S_IFMT = S_IFMT,
-// $S_IFIFO = S_IFIFO,
-// $S_IFCHR = S_IFCHR,
-// };
-//
-// then "godefs x.c" generates:
-//
-// // godefs x.c
-//
-// // MACHINE GENERATED - DO NOT EDIT.
-//
-// // Constants
-// enum {
-// S_IFMT = 0xf000,
-// S_IFIFO = 0x1000,
-// S_IFCHR = 0x2000,
-// };
-//
-// // Types
-// #pragma pack on
-//
-// typedef struct Timespec Timespec;
-// struct Timespec {
-// int32 tv_sec;
-// int32 tv_nsec;
-// };
-//
-// typedef struct Stat Stat;
-// struct Stat {
-// int32 st_dev;
-// uint32 st_ino;
-// uint16 st_mode;
-// uint16 st_nlink;
-// uint32 st_uid;
-// uint32 st_gid;
-// int32 st_rdev;
-// Timespec st_atimespec;
-// Timespec st_mtimespec;
-// Timespec st_ctimespec;
-// int64 st_size;
-// int64 st_blocks;
-// int32 st_blksize;
-// uint32 st_flags;
-// uint32 st_gen;
-// int32 st_lspare;
-// int64 st_qspare[2];
-// };
-// #pragma pack off
-//
-// The -g flag to godefs causes it to generate Go output, not C.
-// In the Go output, struct fields have leading xx_ prefixes removed
-// and the first character capitalized (exported).
-//
-// Godefs works by invoking gcc to compile the given input file
-// and then parses the debug info embedded in the assembly output.
-// This is far easier than reading system headers on most machines.
-//
-// The -c flag sets the compiler (default "gcc").
-//
-// The -f flag adds a flag to pass to the compiler (e.g., -f -m64).
-
-#include "a.h"
-
-#ifdef _WIN32
-int
-spawn(char *prog, char **argv)
-{
- return _spawnvp(P_NOWAIT, prog, (const char**)argv);
-}
-#undef waitfor
-void
-waitfor(int pid)
-{
- _cwait(0, pid, 0);
-}
-#else
-int
-spawn(char *prog, char **argv)
-{
- int pid = fork();
- if(pid < 0)
- sysfatal("fork: %r");
- if(pid == 0) {
- exec(argv[0], argv);
- fprint(2, "exec gcc: %r\n");
- exit(1);
- }
- return pid;
-}
-#endif
-
-void
-usage(void)
-{
- fprint(2, "usage: godefs [-g package] [-c cc] [-f cc-arg] [defs.c ...]\n");
- exit(1);
-}
-
-int gotypefmt(Fmt*);
-int ctypefmt(Fmt*);
-int prefixlen(Type*);
-int cutprefix(char*);
-
-Lang go =
-{
- "const (\n",
- "\t%s = %#llx;\n",
- ")\n",
-
- "type",
- "\n",
-
- "type %s struct {\n",
- "type %s struct {\n",
- "\tPad_godefs_%d [%d]byte;\n",
- "}\n",
-
- gotypefmt,
-};
-
-Lang c =
-{
- "enum {\n",
- "\t%s = %#llx,\n",
- "};\n",
-
- "typedef",
- ";\n",
-
- "typedef struct %s %s;\nstruct %s {\n",
- "typedef union %s %s;\nunion %s {\n",
- "\tbyte pad_godefs_%d[%d];\n",
- "};\n",
-
- ctypefmt,
-};
-
-char *pkg;
-
-int oargc;
-char **oargv;
-Lang *lang = &c;
-
-Const *con;
-int ncon;
-
-Type **typ;
-int ntyp;
-
-void
-waitforgcc(void)
-{
- waitpid();
-}
-
-void
-main(int argc, char **argv)
-{
- int p[2], pid, i, j, n, off, npad, prefix;
- char **av, *q, *r, *tofree, *name;
- char nambuf[100];
- Biobuf *bin, *bout;
- Type *t, *tt;
- Field *f;
- int orig_output_fd;
-
- quotefmtinstall();
-
- oargc = argc;
- oargv = argv;
- av = emalloc((30+argc)*sizeof av[0]);
- atexit(waitforgcc);
-
- n = 0;
- av[n++] = "gcc";
- av[n++] = "-fdollars-in-identifiers";
- av[n++] = "-S"; // write assembly
- av[n++] = "-gstabs+"; // include stabs info
- av[n++] = "-o"; // to ...
- av[n++] = "-"; // ... stdout
- av[n++] = "-xc"; // read C
-
- ARGBEGIN{
- case 'g':
- lang = &go;
- pkg = EARGF(usage());
- break;
- case 'c':
- av[0] = EARGF(usage());
- break;
- case 'f':
- av[n++] = EARGF(usage());
- break;
- default:
- usage();
- }ARGEND
-
- if(argc == 0)
- av[n++] = "-";
- else
- av[n++] = argv[0];
- av[n] = nil;
-
- orig_output_fd = dup(1, -1);
- for(i=0; i==0 || i < argc; i++) {
- // Some versions of gcc do not accept -S with multiple files.
- // Run gcc once for each file.
- // Write assembly and stabs debugging to p[1].
- if(pipe(p) < 0)
- sysfatal("pipe: %r");
- dup(p[1], 1);
- close(p[1]);
- if (argc)
- av[n-1] = argv[i];
- pid = spawn(av[0], av);
- dup(orig_output_fd, 1);
-
- // Read assembly, pulling out .stabs lines.
- bin = Bfdopen(p[0], OREAD);
- while((q = Brdstr(bin, '\n', 1)) != nil) {
- // .stabs "float:t(0,12)=r(0,1);4;0;",128,0,0,0
- tofree = q;
- while(*q == ' ' || *q == '\t')
- q++;
- if(strncmp(q, ".stabs", 6) != 0)
- goto Continue;
- q += 6;
- while(*q == ' ' || *q == '\t')
- q++;
- if(*q++ != '\"') {
-Bad:
- sysfatal("cannot parse .stabs line:\n%s", tofree);
- }
-
- r = strchr(q, '\"');
- if(r == nil)
- goto Bad;
- *r++ = '\0';
- if(*r++ != ',')
- goto Bad;
- if(*r < '0' || *r > '9')
- goto Bad;
- if(atoi(r) != 128) // stabs kind = local symbol
- goto Continue;
-
- parsestabtype(q);
-
-Continue:
- free(tofree);
- }
- Bterm(bin);
- waitfor(pid);
- }
- close(orig_output_fd);
-
- // Write defs to standard output.
- bout = Bfdopen(1, OWRITE);
- fmtinstall('T', lang->typefmt);
-
- // Echo original command line in header.
- Bprint(bout, "//");
- for(i=0; i<oargc; i++)
- Bprint(bout, " %q", oargv[i]);
- Bprint(bout, "\n");
- Bprint(bout, "\n");
- Bprint(bout, "// MACHINE GENERATED - DO NOT EDIT.\n");
- Bprint(bout, "\n");
-
- if(pkg)
- Bprint(bout, "package %s\n\n", pkg);
-
- // Constants.
- Bprint(bout, "// Constants\n");
- if(ncon > 0) {
- Bprint(bout, lang->constbegin);
- for(i=0; i<ncon; i++) {
- // Go can handle negative constants,
- // but C enums may not be able to.
- if(lang == &go)
- Bprint(bout, lang->constfmt, con[i].name, con[i].value);
- else
- Bprint(bout, lang->constfmt, con[i].name, con[i].value & 0xFFFFFFFF);
- }
- Bprint(bout, lang->constend);
- }
- Bprint(bout, "\n");
-
- // Types
-
- // push our names down
- for(i=0; i<ntyp; i++) {
- t = typ[i];
- name = t->name;
- while(t && t->kind == Typedef)
- t = t->type;
- if(t)
- t->name = name;
- }
-
- Bprint(bout, "// Types\n");
-
- // Have to turn off structure padding in Plan 9 compiler,
- // mainly because it is more aggressive than gcc tends to be.
- if(lang == &c)
- Bprint(bout, "#pragma pack on\n");
-
- for(i=0; i<ntyp; i++) {
- Bprint(bout, "\n");
- t = typ[i];
- name = t->name;
- while(t && t->kind == Typedef) {
- if(name == nil && t->name != nil) {
- name = t->name;
- if(t->printed)
- break;
- }
- t = t->type;
- }
- if(name == nil && t->name != nil) {
- name = t->name;
- if(t->printed)
- continue;
- t->printed = 1;
- }
- if(name == nil) {
- fprint(2, "unknown name for %T", typ[i]);
- continue;
- }
- if(name[0] == '$')
- name++;
- npad = 0;
- off = 0;
- switch(t->kind) {
- case 0:
- fprint(2, "unknown type definition for %s\n", name);
- break;
- default: // numeric, array, or pointer
- case Array:
- case Ptr:
- Bprint(bout, "%s %lT%s", lang->typdef, name, t, lang->typdefend);
- break;
- case Union:
- // In Go, print union as struct with only first element,
- // padded the rest of the way.
- Bprint(bout, lang->unionbegin, name, name, name);
- goto StructBody;
- case Struct:
- Bprint(bout, lang->structbegin, name, name, name);
- StructBody:
- prefix = 0;
- if(lang == &go)
- prefix = prefixlen(t);
- for(j=0; j<t->nf; j++) {
- f = &t->f[j];
- if(f->type->kind == 0 && f->size <= 64 && (f->size&(f->size-1)) == 0) {
- // unknown type but <= 64 bits and bit size is a power of two.
- // could be enum - make Uint64 and then let it reduce
- tt = emalloc(sizeof *tt);
- *tt = *f->type;
- f->type = tt;
- tt->kind = Uint64;
- while(tt->kind > Uint8 && kindsize[tt->kind] > f->size)
- tt->kind -= 2;
- }
- // padding
- if(t->kind == Struct || lang == &go) {
- if(f->offset%8 != 0 || f->size%8 != 0) {
- fprint(2, "ignoring bitfield %s.%s\n", t->name, f->name);
- continue;
- }
- if(f->offset < off)
- sysfatal("%s: struct fields went backward", t->name);
- if(off < f->offset) {
- Bprint(bout, lang->structpadfmt, npad++, (f->offset - off) / 8);
- off = f->offset;
- }
- off += f->size;
- }
- name = f->name;
- if(cutprefix(name))
- name += prefix;
- if(strcmp(name, "") == 0) {
- snprint(nambuf, sizeof nambuf, "Pad_godefs_%d", npad++);
- name = nambuf;
- }
- Bprint(bout, "\t%#lT;\n", name, f->type);
- if(t->kind == Union && lang == &go)
- break;
- }
- // final padding
- if(t->kind == Struct || lang == &go) {
- if(off/8 < t->size)
- Bprint(bout, lang->structpadfmt, npad++, t->size - off/8);
- }
- Bprint(bout, lang->structend);
- }
- }
- if(lang == &c)
- Bprint(bout, "#pragma pack off\n");
- Bterm(bout);
- exit(0);
-}
-
-char *kindnames[] = {
- "void", // actually unknown, but byte is good for pointers
- "void",
- "int8",
- "uint8",
- "int16",
- "uint16",
- "int32",
- "uint32",
- "int64",
- "uint64",
- "float32",
- "float64",
- "ptr",
- "struct",
- "array",
- "union",
- "typedef",
-};
-
-int
-ctypefmt(Fmt *f)
-{
- char *name, *s;
- Type *t;
-
- name = nil;
- if(f->flags & FmtLong) {
- name = va_arg(f->args, char*);
- if(name == nil || name[0] == '\0')
- name = "_anon_";
- }
- t = va_arg(f->args, Type*);
- while(t && t->kind == Typedef)
- t = t->type;
- switch(t->kind) {
- case Struct:
- case Union:
- // must be named
- s = t->name;
- if(s == nil) {
- fprint(2, "need name for anonymous struct\n");
- goto bad;
- }
- else if(s[0] != '$')
- fprint(2, "need name for struct %s\n", s);
- else
- s++;
- fmtprint(f, "%s", s);
- if(name)
- fmtprint(f, " %s", name);
- break;
-
- case Array:
- if(name)
- fmtprint(f, "%T %s[%d]", t->type, name, t->size);
- else
- fmtprint(f, "%T[%d]", t->type, t->size);
- break;
-
- case Ptr:
- if(name)
- fmtprint(f, "%T *%s", t->type, name);
- else
- fmtprint(f, "%T*", t->type);
- break;
-
- default:
- fmtprint(f, "%s", kindnames[t->kind]);
- if(name)
- fmtprint(f, " %s", name);
- break;
-
- bad:
- if(name)
- fmtprint(f, "byte %s[%d]", name, t->size);
- else
- fmtprint(f, "byte[%d]", t->size);
- break;
- }
-
- return 0;
-}
-
-int
-gotypefmt(Fmt *f)
-{
- char *name, *s;
- Type *t;
-
- if(f->flags & FmtLong) {
- name = va_arg(f->args, char*);
- if('a' <= name[0] && name[0] <= 'z')
- name[0] += 'A' - 'a';
- if(name[0] == '_' && (f->flags & FmtSharp))
- fmtprint(f, "X");
- fmtprint(f, "%s ", name);
- }
- t = va_arg(f->args, Type*);
- while(t && t->kind == Typedef)
- t = t->type;
-
- switch(t->kind) {
- case Struct:
- case Union:
- // must be named
- s = t->name;
- if(s == nil) {
- fprint(2, "need name for anonymous struct\n");
- fmtprint(f, "STRUCT");
- }
- else if(s[0] != '$') {
- fprint(2, "warning: missing name for struct %s\n", s);
- fmtprint(f, "[%d]byte /* %s */", t->size, s);
- } else
- fmtprint(f, "%s", s+1);
- break;
-
- case Array:
- fmtprint(f, "[%d]%T", t->size, t->type);
- break;
-
- case Ptr:
- fmtprint(f, "*%T", t->type);
- break;
-
- default:
- s = kindnames[t->kind];
- if(strcmp(s, "void") == 0)
- s = "byte";
- fmtprint(f, "%s", s);
- }
-
- return 0;
-}
-
-// Is this the kind of name we should cut a prefix from?
-// The rule is that the name cannot begin with underscore
-// and must have an underscore eventually.
-int
-cutprefix(char *name)
-{
- char *p;
-
- // special case: orig_ in register struct
- if(strncmp(name, "orig_", 5) == 0)
- return 0;
-
- for(p=name; *p; p++) {
- if(*p == '_')
- return p-name > 0;
- }
- return 0;
-}
-
-// Figure out common struct prefix len
-int
-prefixlen(Type *t)
-{
- int i;
- int len;
- char *p, *name;
- Field *f;
-
- len = 0;
- name = nil;
- for(i=0; i<t->nf; i++) {
- f = &t->f[i];
- if(!cutprefix(f->name))
- continue;
- p = strchr(f->name, '_');
- if(p == nil)
- return 0;
- if(name == nil) {
- name = f->name;
- len = p+1 - name;
- }
- else if(strncmp(f->name, name, len) != 0)
- return 0;
- }
- return len;
-}
diff --git a/src/cmd/godefs/stabs.c b/src/cmd/godefs/stabs.c
deleted file mode 100644
index 2c3d431b8..000000000
--- a/src/cmd/godefs/stabs.c
+++ /dev/null
@@ -1,456 +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.
-
-// Parse stabs debug info.
-
-#include "a.h"
-
-int stabsdebug = 1;
-
-// Hash table for type lookup by number.
-Type *hash[1024];
-
-// Look up type by number pair.
-// TODO(rsc): Iant points out that n1 and n2 are always small and dense,
-// so an array of arrays would be a better representation.
-Type*
-typebynum(uint n1, uint n2)
-{
- uint h;
- Type *t;
-
- h = (n1*53+n2) % nelem(hash);
- for(t=hash[h]; t; t=t->next)
- if(t->n1 == n1 && t->n2 == n2)
- return t;
- t = emalloc(sizeof *t);
- t->next = hash[h];
- hash[h] = t;
- t->n1 = n1;
- t->n2 = n2;
- return t;
-}
-
-// Parse name and colon from *pp, leaving copy in *sp.
-static int
-parsename(char **pp, char **sp)
-{
- char *p;
- char *s;
-
- p = *pp;
- while(*p != '\0' && *p != ':')
- p++;
- if(*p == '\0') {
- fprint(2, "parsename expected colon\n");
- return -1;
- }
- s = emalloc(p - *pp + 1);
- memmove(s, *pp, p - *pp);
- *sp = s;
- *pp = p+1;
- return 0;
-}
-
-// Parse single number from *pp.
-static int
-parsenum1(char **pp, vlong *np)
-{
- char *p;
-
- p = *pp;
- if(*p != '-' && (*p < '0' || *p > '9')) {
- fprint(2, "parsenum expected minus or digit\n");
- return -1;
- }
- *np = strtoll(p, pp, 10);
- return 0;
-}
-
-// Parse type number - either single number or (n1, n2).
-static int
-parsetypenum(char **pp, vlong *n1p, vlong *n2p)
-{
- char *p;
-
- p = *pp;
- if(*p == '(') {
- p++;
- if(parsenum1(&p, n1p) < 0)
- return -1;
- if(*p++ != ',') {
- if(stabsdebug)
- fprint(2, "parsetypenum expected comma\n");
- return -1;
- }
- if(parsenum1(&p, n2p) < 0)
- return -1;
- if(*p++ != ')') {
- if(stabsdebug)
- fprint(2, "parsetypenum expected right paren\n");
- return -1;
- }
- *pp = p;
- return 0;
- }
-
- if(parsenum1(&p, n1p) < 0)
- return -1;
- *n2p = 0;
- *pp = p;
- return 0;
-}
-
-// Written to parse max/min of vlong correctly.
-static vlong
-parseoctal(char **pp)
-{
- char *p;
- vlong n;
-
- p = *pp;
- if(*p++ != '0')
- return 0;
- n = 0;
- while(*p >= '0' && *p <= '9')
- n = n << 3 | *p++ - '0';
- *pp = p;
- return n;
-}
-
-// Integer types are represented in stabs as a "range"
-// type with a lo and a hi value. The lo and hi used to
-// be lo and hi for the type, but there are now odd
-// extensions for floating point and 64-bit numbers.
-//
-// Have to keep signs separate from values because
-// Int64's lo is -0.
-typedef struct Intrange Intrange;
-struct Intrange
-{
- vlong lo;
- vlong hi;
- int kind;
-};
-
-Intrange intranges[] = {
- 0, 127, Int8, // char
- -128, 127, Int8, // signed char
- 0, 255, Uint8,
- -32768, 32767, Int16,
- 0, 65535, Uint16,
- -2147483648LL, 2147483647LL, Int32,
- 0, 4294967295LL, Uint32,
- 1LL << 63, ~(1LL << 63), Int64,
- 0, -1, Uint64,
- 4, 0, Float32,
- 8, 0, Float64,
- 16, 0, Void,
-};
-
-int kindsize[] = {
- 0,
- 0,
- 8,
- 8,
- 16,
- 16,
- 32,
- 32,
- 64,
- 64,
-};
-
-// Parse a single type definition from *pp.
-static Type*
-parsedef(char **pp, char *name)
-{
- char *p;
- Type *t, *tt;
- int i;
- vlong n1, n2, lo, hi;
- Field *f;
- Intrange *r;
-
- p = *pp;
-
- // reference to another type?
- if(isdigit(*p) || *p == '(') {
- if(parsetypenum(&p, &n1, &n2) < 0)
- return nil;
- t = typebynum(n1, n2);
- if(name && t->name == nil) {
- t->name = name;
- // save definitions of names beginning with $
- if(name[0] == '$' && !t->saved) {
- typ = erealloc(typ, (ntyp+1)*sizeof typ[0]);
- typ[ntyp] = t;
- ntyp++;
- }
- }
-
- // is there an =def suffix?
- if(*p == '=') {
- p++;
- tt = parsedef(&p, name);
- if(tt == nil)
- return nil;
-
- if(tt == t) {
- tt->kind = Void;
- } else {
- t->type = tt;
- t->kind = Typedef;
- }
-
- // assign given name, but do not record in typ.
- // assume the name came from a typedef
- // which will be recorded.
- if(name)
- tt->name = name;
- }
-
- *pp = p;
- return t;
- }
-
- // otherwise a type literal. first letter identifies kind
- t = emalloc(sizeof *t);
- switch(*p) {
- default:
- fprint(2, "unknown type char %c in %s\n", *p, p);
- *pp = "";
- return t;
-
- case '@': // type attribute
- while (*++p != ';');
- *pp = ++p;
- return parsedef(pp, nil);
-
- case '*': // pointer
- p++;
- t->kind = Ptr;
- tt = parsedef(&p, nil);
- if(tt == nil)
- return nil;
- t->type = tt;
- break;
-
- case 'a': // array
- p++;
- t->kind = Array;
- // index type
- tt = parsedef(&p, nil);
- if(tt == nil)
- return nil;
- t->size = tt->size;
- // element type
- tt = parsedef(&p, nil);
- if(tt == nil)
- return nil;
- t->type = tt;
- break;
-
- case 'e': // enum type - record $names in con array.
- p++;
- for(;;) {
- if(*p == '\0')
- return nil;
- if(*p == ';') {
- p++;
- break;
- }
- if(parsename(&p, &name) < 0)
- return nil;
- if(parsenum1(&p, &n1) < 0)
- return nil;
- if(name[0] == '$') {
- con = erealloc(con, (ncon+1)*sizeof con[0]);
- name++;
- con[ncon].name = name;
- con[ncon].value = n1;
- ncon++;
- }
- if(*p != ',')
- return nil;
- p++;
- }
- break;
-
- case 'f': // function
- p++;
- if(parsedef(&p, nil) == nil)
- return nil;
- break;
-
- case 'B': // volatile
- case 'k': // const
- ++*pp;
- return parsedef(pp, nil);
-
- case 'r': // sub-range (used for integers)
- p++;
- if(parsedef(&p, nil) == nil)
- return nil;
- // usually, the return from parsedef == t, but not always.
-
- if(*p != ';' || *++p == ';') {
- if(stabsdebug)
- fprint(2, "range expected number: %s\n", p);
- return nil;
- }
- if(*p == '0')
- lo = parseoctal(&p);
- else
- lo = strtoll(p, &p, 10);
- if(*p != ';' || *++p == ';') {
- if(stabsdebug)
- fprint(2, "range expected number: %s\n", p);
- return nil;
- }
- if(*p == '0')
- hi = parseoctal(&p);
- else
- hi = strtoll(p, &p, 10);
- if(*p != ';') {
- if(stabsdebug)
- fprint(2, "range expected trailing semi: %s\n", p);
- return nil;
- }
- p++;
- t->size = hi+1; // might be array size
- for(i=0; i<nelem(intranges); i++) {
- r = &intranges[i];
- if(r->lo == lo && r->hi == hi) {
- t->kind = r->kind;
- break;
- }
- }
- break;
-
- case 's': // struct
- case 'u': // union
- t->kind = Struct;
- if(*p == 'u')
- t->kind = Union;
-
- // assign given name, but do not record in typ.
- // assume the name came from a typedef
- // which will be recorded.
- if(name)
- t->name = name;
- p++;
- if(parsenum1(&p, &n1) < 0)
- return nil;
- t->size = n1;
- for(;;) {
- if(*p == '\0')
- return nil;
- if(*p == ';') {
- p++;
- break;
- }
- t->f = erealloc(t->f, (t->nf+1)*sizeof t->f[0]);
- f = &t->f[t->nf];
- if(parsename(&p, &f->name) < 0)
- return nil;
- f->type = parsedef(&p, nil);
- if(f->type == nil)
- return nil;
- if(*p != ',') {
- fprint(2, "expected comma after def of %s:\n%s\n", f->name, p);
- return nil;
- }
- p++;
- if(parsenum1(&p, &n1) < 0)
- return nil;
- f->offset = n1;
- if(*p != ',') {
- fprint(2, "expected comma after offset of %s:\n%s\n", f->name, p);
- return nil;
- }
- p++;
- if(parsenum1(&p, &n1) < 0)
- return nil;
- f->size = n1;
- if(*p != ';') {
- fprint(2, "expected semi after size of %s:\n%s\n", f->name, p);
- return nil;
- }
-
- while(f->type->kind == Typedef)
- f->type = f->type->type;
-
- // rewrite
- // uint32 x : 8;
- // into
- // uint8 x;
- // hooray for bitfields.
- while(Int16 <= f->type->kind && f->type->kind <= Uint64 && kindsize[f->type->kind] > f->size) {
- tt = emalloc(sizeof *tt);
- *tt = *f->type;
- f->type = tt;
- f->type->kind -= 2;
- }
- p++;
- t->nf++;
- }
- break;
-
- case 'x':
- // reference to struct, union not yet defined.
- p++;
- switch(*p) {
- case 's':
- t->kind = Struct;
- break;
- case 'u':
- t->kind = Union;
- break;
- default:
- fprint(2, "unknown x type char x%c", *p);
- *pp = "";
- return t;
- }
- if(parsename(&p, &t->name) < 0)
- return nil;
- break;
- }
- *pp = p;
- return t;
-}
-
-
-// Parse a stab type in p, saving info in the type hash table
-// and also in the list of recorded types if appropriate.
-void
-parsestabtype(char *p)
-{
- char *p0, *name;
-
- p0 = p;
-
- // p is the quoted string output from gcc -gstabs on a .stabs line.
- // name:t(1,2)
- // name:t(1,2)=def
- if(parsename(&p, &name) < 0) {
- Bad:
- // Use fprint instead of sysfatal to avoid
- // sysfatal's internal buffer size limit.
- fprint(2, "cannot parse stabs type:\n%s\n(at %s)\n", p0, p);
- sysfatal("stabs parse");
- }
- if(*p != 't' && *p != 'T')
- goto Bad;
- p++;
-
- // parse the definition.
- if(name[0] == '\0')
- name = nil;
- if(parsedef(&p, name) == nil)
- goto Bad;
- if(*p != '\0')
- goto Bad;
-}
-
diff --git a/src/cmd/godefs/test.sh b/src/cmd/godefs/test.sh
deleted file mode 100755
index c035af8f4..000000000
--- a/src/cmd/godefs/test.sh
+++ /dev/null
@@ -1,45 +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.
-
-eval $(gomake --no-print-directory -f ../../Make.inc go-env)
-
-TMP="testdata_tmp.go"
-TEST="testdata.c"
-GOLDEN="testdata_${GOOS}_${GOARCH}.golden"
-
-case ${GOARCH} in
-"amd64") CCARG="-f-m64";;
-"386") CCARG="-f-m32";;
-*) CCARG="";;
-esac
-
-cleanup() {
- rm ${TMP}
-}
-
-error() {
- cleanup
- echo $1
- exit 1
-}
-
-if [ ! -e ${GOLDEN} ]; then
- echo "skipping - no golden defined for this platform"
- exit
-fi
-
-./godefs -g test ${CCARG} ${TEST} > ${TMP}
-if [ $? != 0 ]; then
- error "Error: Could not run godefs for ${TEST}"
-fi
-
-diff ${TMP} ${GOLDEN}
-if [ $? != 0 ]; then
- error "FAIL: godefs for ${TEST} did not match ${GOLDEN}"
-fi
-
-cleanup
-
-echo "PASS"
diff --git a/src/cmd/godefs/testdata.c b/src/cmd/godefs/testdata.c
deleted file mode 100644
index 3f459c41b..000000000
--- a/src/cmd/godefs/testdata.c
+++ /dev/null
@@ -1,41 +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.
-
-#include <stdint.h>
-
-// Issue 432 - enum fields in struct can cause misaligned struct fields
-typedef enum {
- a
-} T1;
-
-struct T2 {
- uint8_t a;
- T1 b;
- T1 c;
- uint16_t d;
-};
-
-typedef struct T2 T2;
-typedef T2 $T2;
-
-// Issue 1162 - structs with fields named Pad[0-9]+ conflict with field
-// names used by godefs for padding
-struct T3 {
- uint8_t a;
- int Pad0;
-};
-
-typedef struct T3 $T3;
-
-// Issue 1466 - forward references to types in stabs debug info were
-// always treated as enums
-struct T4 {};
-
-struct T5 {
- struct T4 *a;
-};
-
-typedef struct T5 T5;
-typedef struct T4 $T4;
-typedef T5 $T5; \ No newline at end of file
diff --git a/src/cmd/godefs/testdata_darwin_386.golden b/src/cmd/godefs/testdata_darwin_386.golden
deleted file mode 100644
index d929238b0..000000000
--- a/src/cmd/godefs/testdata_darwin_386.golden
+++ /dev/null
@@ -1,31 +0,0 @@
-// ./godefs -g test -f-m32 testdata.c
-
-// MACHINE GENERATED - DO NOT EDIT.
-
-package test
-
-// Constants
-
-// Types
-
-type T2 struct {
- A uint8;
- Pad_godefs_0 [3]byte;
- B uint32;
- C uint32;
- D uint16;
- Pad_godefs_1 [2]byte;
-}
-
-type T3 struct {
- A uint8;
- Pad_godefs_0 [3]byte;
- Pad0 int32;
-}
-
-type T4 struct {
-}
-
-type T5 struct {
- A *T4;
-}
diff --git a/src/cmd/godefs/testdata_darwin_amd64.golden b/src/cmd/godefs/testdata_darwin_amd64.golden
deleted file mode 100644
index a694f4a73..000000000
--- a/src/cmd/godefs/testdata_darwin_amd64.golden
+++ /dev/null
@@ -1,31 +0,0 @@
-// ./godefs -g test -f-m64 testdata.c
-
-// MACHINE GENERATED - DO NOT EDIT.
-
-package test
-
-// Constants
-
-// Types
-
-type T2 struct {
- A uint8;
- Pad_godefs_0 [3]byte;
- B uint32;
- C uint32;
- D uint16;
- Pad_godefs_1 [2]byte;
-}
-
-type T3 struct {
- A uint8;
- Pad_godefs_0 [3]byte;
- Pad0 int32;
-}
-
-type T4 struct {
-}
-
-type T5 struct {
- A *T4;
-}
diff --git a/src/cmd/godefs/util.c b/src/cmd/godefs/util.c
deleted file mode 100644
index 18be00453..000000000
--- a/src/cmd/godefs/util.c
+++ /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.
-
-#include "a.h"
-
-void*
-emalloc(int n)
-{
- void *p;
-
- p = malloc(n);
- if(p == nil)
- sysfatal("out of memory");
- memset(p, 0, n);
- return p;
-}
-
-char*
-estrdup(char *s)
-{
- s = strdup(s);
- if(s == nil)
- sysfatal("out of memory");
- return s;
-}
-
-void*
-erealloc(void *v, int n)
-{
- v = realloc(v, n);
- if(v == nil)
- sysfatal("out of memory");
- return v;
-}
-
diff --git a/src/cmd/godoc/Makefile b/src/cmd/godoc/Makefile
deleted file mode 100644
index f40d71703..000000000
--- a/src/cmd/godoc/Makefile
+++ /dev/null
@@ -1,24 +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 ../../Make.inc
-
-TARG=godoc
-GOFILES=\
- codewalk.go\
- dirtrees.go\
- filesystem.go\
- format.go\
- godoc.go\
- httpzip.go\
- index.go\
- main.go\
- mapping.go\
- parser.go\
- snippet.go\
- spec.go\
- utils.go\
- zip.go\
-
-include ../../Make.cmd
diff --git a/src/cmd/godoc/README.godoc-app b/src/cmd/godoc/README.godoc-app
new file mode 100644
index 000000000..88cfee41e
--- /dev/null
+++ b/src/cmd/godoc/README.godoc-app
@@ -0,0 +1,80 @@
+Copyright 2011 The Go Authors. All rights reserved.
+Use of this source code is governed by a BSD-style
+license that can be found in the LICENSE file.
+
+godoc on appengine
+------------------
+
+Prerequisites
+-------------
+
+* Go appengine SDK 1.5.5 - 2011-10-11
+ http://code.google.com/appengine/downloads.html#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):
+
+ alt/
+ encoding/binary/
+ go/*
+ index/suffixarray/
+ 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-5-5
+ runtime: go
+ api_version: 3
+
+ handlers:
+ - url: /.*
+ script: _go_app
+
+* The godoc/ directory contains a copy of the files under $GOROOT/src/cmd/godoc
+ with modifications:
+
+ - doc.go is excluded (it belongs to pseudo-package ÒdocumentationÓ)
+ - main.go is excluded (appinit.go is taking its place)
+
+ Additional manual modifications are required to refer to the alt/ packages
+ where the app-engine library is not up-to-date with the godoc version.
+
+* The alt/ directory contains up-to-date copies of Go packages that a tip-based
+ godoc is dependent on but which do not yet exist in the current app-engine SDK.
+ At the time of this writing (10/14/2011) this is the entire go directory tree
+ (for the missing FileSet serialization code in go/token) as well as the
+ index/suffixarray package (for the missing suffix array serialization code).
+ The latest (alt/)index/suffixarray package internally requires the latest
+ version of encoding/binary, which is why it also needs to be present under
+ alt/.
+
+
+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/appconfig.go b/src/cmd/godoc/appconfig.go
deleted file mode 100644
index 9cbe7a443..000000000
--- a/src/cmd/godoc/appconfig.go
+++ /dev/null
@@ -1,19 +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 configuration information used by
-// godoc when running on app engine. Adjust as needed
-// (typically when the .zip file changes).
-
-package main
-
-const (
- // zipFilename is the name of the .zip file
- // containing the file system served by godoc.
- zipFilename = "go.zip"
-
- // zipGoroot is the path of the goroot directory
- // in the .zip file.
- zipGoroot = "/home/username/go"
-)
diff --git a/src/cmd/godoc/appinit.go b/src/cmd/godoc/appinit.go
index 9b8987223..70da00110 100644
--- a/src/cmd/godoc/appinit.go
+++ b/src/cmd/godoc/appinit.go
@@ -2,58 +2,38 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// To run godoc under app engine, substitute main.go with
-// this file (appinit.go), provide a .zip file containing
-// the file system to serve, and adjust the configuration
-// parameters in appconfig.go accordingly.
-//
-// The current app engine SDK may be based on an older Go
-// release version. To correct for version skew, copy newer
-// packages into the alt directory (e.g. alt/strings) and
-// adjust the imports in the godoc source files (e.g. from
-// `import "strings"` to `import "alt/strings"`). Both old
-// and new packages may be used simultaneously as long as
-// there is no package global state that needs to be shared.
-//
-// The directory structure should look as follows:
-//
-// godoc // directory containing the app engine app
-// alt // alternative packages directory to
-// // correct for version skew
-// strings // never version of the strings package
-// ... //
-// app.yaml // app engine control file
-// go.zip // zip file containing the file system to serve
-// godoc // contains godoc sources
-// appinit.go // this file instead of godoc/main.go
-// appconfig.go // godoc for app engine configuration
-// ... //
-//
-// To run app the engine emulator locally:
-//
-// dev_appserver.py -a 0 godoc
-//
-// godoc is the top-level "goroot" directory.
-// The godoc home page is served at: <hostname>:8080 and localhost:8080.
+// +build appengine
package main
+// This file replaces main.go when running godoc under app-engine.
+// See README.godoc-app for details.
+
import (
- "alt/archive/zip"
- "http"
+ "archive/zip"
"log"
- "os"
+ "net/http"
+ "path"
)
-func serveError(w http.ResponseWriter, r *http.Request, relpath string, err os.Error) {
+func serveError(w http.ResponseWriter, r *http.Request, relpath string, err error) {
contents := applyTemplate(errorHTML, "errorHTML", err) // err may contain an absolute path!
w.WriteHeader(http.StatusNotFound)
- servePage(w, "File "+relpath, "", "", contents)
+ servePage(w, relpath, "File "+relpath, "", "", contents)
}
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)
// read .zip file and set up file systems
const zipfile = zipFilename
@@ -61,26 +41,24 @@ func init() {
if err != nil {
log.Fatalf("%s: %s\n", zipfile, err)
}
- fs = NewZipFS(rc)
- fsHttp = NewHttpZipFS(rc, *goroot)
+ // rc is never closed (app running forever)
+ fs.Bind("/", NewZipFS(rc, zipFilename), *goroot, bindReplace)
// initialize http handlers
- initHandlers()
readTemplates()
+ initHandlers()
registerPublicHandlers(http.DefaultServeMux)
// initialize default directory tree with corresponding timestamp.
initFSTree()
- // initialize directory trees for user-defined file systems (-path flag).
- initDirTrees()
+ // Immediately update metadata.
+ updateMetadata()
- // create search index
- // TODO(gri) Disabled for now as it takes too long. Find a solution for this.
- /*
- *indexEnabled = true
+ // 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
index e2643e466..f7f51d0a0 100644
--- a/src/cmd/godoc/codewalk.go
+++ b/src/cmd/godoc/codewalk.go
@@ -13,25 +13,25 @@
package main
import (
- "container/vector"
+ "encoding/xml"
+ "errors"
"fmt"
- "http"
"io"
"log"
+ "net/http"
"os"
"regexp"
"sort"
"strconv"
"strings"
- "template"
- "utf8"
- "xml"
+ "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 := absolutePath(r.URL.Path[1:], *goroot)
+ abspath := r.URL.Path
r.ParseForm()
if f := r.FormValue("fileprint"); f != "" {
@@ -41,7 +41,7 @@ func codewalk(w http.ResponseWriter, r *http.Request) {
// If directory exists, serve list of code walks.
dir, err := fs.Lstat(abspath)
- if err == nil && dir.IsDirectory() {
+ if err == nil && dir.IsDir() {
codewalkDir(w, r, relpath, abspath)
return
}
@@ -53,7 +53,9 @@ func codewalk(w http.ResponseWriter, r *http.Request) {
}
// Otherwise append .xml and hope to find
- // a codewalk description.
+ // a codewalk description, but before trim
+ // the trailing /.
+ abspath = strings.TrimRight(abspath, "/")
cw, err := loadCodewalk(abspath + ".xml")
if err != nil {
log.Print(err)
@@ -67,25 +69,25 @@ func codewalk(w http.ResponseWriter, r *http.Request) {
}
b := applyTemplate(codewalkHTML, "codewalk", cw)
- servePage(w, "Codewalk: "+cw.Title, "", "", b)
+ servePage(w, cw.Title, "Codewalk: "+cw.Title, "", "", b)
}
// A Codewalk represents a single codewalk read from an XML file.
type Codewalk struct {
- Title string `xml:"attr"`
- File []string
- Step []*Codestep
+ 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:"attr"`
- Title string `xml:"attr"`
- XML string `xml:"innerxml"`
+ Src string `xml:"src,attr"`
+ Title string `xml:"title,attr"`
+ XML string `xml:",innerxml"`
// Derived from Src; not in XML.
- Err os.Error
+ Err error
File string
Lo int
LoByte int
@@ -108,18 +110,18 @@ func (st *Codestep) String() string {
}
// loadCodewalk reads a codewalk from the named XML file.
-func loadCodewalk(filename string) (*Codewalk, os.Error) {
+func loadCodewalk(filename string) (*Codewalk, error) {
f, err := fs.Open(filename)
if err != nil {
return nil, err
}
defer f.Close()
cw := new(Codewalk)
- p := xml.NewParser(f)
- p.Entity = xml.HTMLEntity
- err = p.Unmarshal(cw, nil)
+ d := xml.NewDecoder(f)
+ d.Entity = xml.HTMLEntity
+ err = d.Decode(cw)
if err != nil {
- return nil, &os.PathError{"parsing", filename, err}
+ return nil, &os.PathError{Op: "parsing", Path: filename, Err: err}
}
// Compute file list, evaluate line numbers for addresses.
@@ -130,7 +132,7 @@ func loadCodewalk(filename string) (*Codewalk, os.Error) {
i = len(st.Src)
}
filename := st.Src[0:i]
- data, err := fs.ReadFile(absolutePath(filename, *goroot))
+ data, err := ReadFile(fs, filename)
if err != nil {
st.Err = err
continue
@@ -183,22 +185,22 @@ func codewalkDir(w http.ResponseWriter, r *http.Request, relpath, abspath string
serveError(w, r, relpath, err)
return
}
- var v vector.Vector
+ var v []interface{}
for _, fi := range dir {
name := fi.Name()
- if fi.IsDirectory() {
- v.Push(&elem{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.Push(&elem{name[0 : len(name)-len(".xml")], cw.Title})
+ v = append(v, &elem{name[0 : len(name)-len(".xml")], cw.Title})
}
}
b := applyTemplate(codewalkdirHTML, "codewalkdir", v)
- servePage(w, "Codewalks", "", "", b)
+ servePage(w, "", "Codewalks", "", "", b)
}
// codewalkFileprint serves requests with ?fileprint=f&lo=lo&hi=hi.
@@ -208,8 +210,8 @@ func codewalkDir(w http.ResponseWriter, r *http.Request, relpath, abspath string
// 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 := absolutePath(f, *goroot)
- data, err := fs.ReadFile(abspath)
+ abspath := f
+ data, err := ReadFile(fs, abspath)
if err != nil {
log.Print(err)
serveError(w, r, f, err)
@@ -253,7 +255,7 @@ func codewalkFileprint(w http.ResponseWriter, r *http.Request, f string) {
// 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 os.Error) {
+func addrToByteRange(addr string, start int, data []byte) (lo, hi int, err error) {
var (
dir byte
prevc byte
@@ -265,7 +267,7 @@ func addrToByteRange(addr string, start int, data []byte) (lo, hi int, err os.Er
c := addr[0]
switch c {
default:
- err = os.NewError("invalid address syntax near " + string(c))
+ err = errors.New("invalid address syntax near " + string(c))
case ',':
if len(addr) == 1 {
hi = len(data)
@@ -349,7 +351,7 @@ func addrToByteRange(addr string, start int, data []byte) (lo, hi int, err os.Er
// (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, os.Error) {
+func addrNumber(data []byte, lo, hi int, dir byte, n int, charOffset bool) (int, int, error) {
switch dir {
case 0:
lo = 0
@@ -425,13 +427,13 @@ func addrNumber(data []byte, lo, hi int, dir byte, n int, charOffset bool) (int,
}
}
- return 0, 0, os.NewError("address out of range")
+ 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, os.Error) {
+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
@@ -439,7 +441,7 @@ func addrRegexp(data []byte, lo, hi int, dir byte, pattern string) (int, int, os
if dir == '-' {
// Could implement reverse search using binary search
// through file, but that seems like overkill.
- return 0, 0, os.NewError("reverse search not implemented")
+ return 0, 0, errors.New("reverse search not implemented")
}
m := re.FindIndex(data[hi:])
if len(m) > 0 {
@@ -450,7 +452,7 @@ func addrRegexp(data []byte, lo, hi int, dir byte, pattern string) (int, int, os
m = re.FindIndex(data)
}
if len(m) == 0 {
- return 0, 0, os.NewError("no match for " + pattern)
+ return 0, 0, errors.New("no match for " + pattern)
}
return m[0], m[1], nil
}
diff --git a/src/cmd/godoc/dirtrees.go b/src/cmd/godoc/dirtrees.go
index aa590b363..b9b529f87 100644
--- a/src/cmd/godoc/dirtrees.go
+++ b/src/cmd/godoc/dirtrees.go
@@ -12,79 +12,49 @@ import (
"go/parser"
"go/token"
"log"
- "path/filepath"
+ "os"
+ pathpkg "path"
"strings"
- "unicode"
)
+// Conventional name for directories containing test data.
+// Excluded from directory trees.
+//
+const testdataDirName = "testdata"
+
type Directory struct {
- Depth int
- Path string // includes Name
- Name string
- Text string // package documentation, if any
- Dirs []*Directory // subdirectories
+ 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 FileInfo) bool {
+func isGoFile(fi os.FileInfo) bool {
name := fi.Name()
- return fi.IsRegular() &&
+ return !fi.IsDir() &&
len(name) > 0 && name[0] != '.' && // ignore .files
- filepath.Ext(name) == ".go"
+ pathpkg.Ext(name) == ".go"
}
-func isPkgFile(fi FileInfo) bool {
+func isPkgFile(fi os.FileInfo) bool {
return isGoFile(fi) &&
!strings.HasSuffix(fi.Name(), "_test.go") // ignore test files
}
-func isPkgDir(fi FileInfo) bool {
+func isPkgDir(fi os.FileInfo) bool {
name := fi.Name()
- return fi.IsDirectory() && len(name) > 0 &&
+ return fi.IsDir() && len(name) > 0 &&
name[0] != '_' && name[0] != '.' // ignore _files and .files
}
-func firstSentence(s string) string {
- i := -1 // index+1 of first terminator (punctuation ending a sentence)
- j := -1 // index+1 of first terminator followed by white space
- prev := 'A'
- for k, ch := range s {
- k1 := k + 1
- if ch == '.' || ch == '!' || ch == '?' {
- if i < 0 {
- i = k1 // first terminator
- }
- if k1 < len(s) && s[k1] <= ' ' {
- if j < 0 {
- j = k1 // first terminator followed by white space
- }
- if !unicode.IsUpper(prev) {
- j = k1
- break
- }
- }
- }
- prev = ch
- }
-
- if j < 0 {
- // use the next best terminator
- j = i
- if j < 0 {
- // no terminator at all, use the entire string
- j = len(s)
- }
- }
-
- return s[0:j]
-}
-
type treeBuilder struct {
- pathFilter func(string) bool
- maxDepth int
+ maxDepth int
}
func (b *treeBuilder) newDirTree(fset *token.FileSet, path, name string, depth int) *Directory {
- if b.pathFilter != nil && !b.pathFilter(path) {
+ if name == testdataDirName {
return nil
}
@@ -92,16 +62,14 @@ func (b *treeBuilder) newDirTree(fset *token.FileSet, path, name string, depth i
// return a dummy directory so that the parent directory
// doesn't get discarded just because we reached the max
// directory depth
- return &Directory{depth, path, name, "", nil}
+ return &Directory{
+ Depth: depth,
+ Path: path,
+ Name: name,
+ }
}
- list, err := fs.ReadDir(path)
- if err != nil {
- // newDirTree is called with a path that should be a package
- // directory; errors here should not happen, but if they do,
- // we want to know about them
- log.Printf("ReadDir(%s): %s", path, err)
- }
+ list, _ := fs.ReadDir(path)
// determine number of subdirectories and if there are package files
ndirs := 0
@@ -117,7 +85,7 @@ func (b *treeBuilder) newDirTree(fset *token.FileSet, path, name string, depth i
// 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 := parser.ParseFile(fset, filepath.Join(path, d.Name()), nil,
+ file, err := parseFile(fset, pathpkg.Join(path, d.Name()),
parser.ParseComments|parser.PackageClauseOnly)
if err == nil {
hasPkgFiles = true
@@ -135,7 +103,7 @@ func (b *treeBuilder) newDirTree(fset *token.FileSet, path, name string, depth i
i = 3 // none of the above
}
if 0 <= i && i < len(synopses) && synopses[i] == "" {
- synopses[i] = firstSentence(doc.CommentText(file.Doc))
+ synopses[i] = doc.Synopsis(file.Doc.Text())
}
}
}
@@ -151,7 +119,7 @@ func (b *treeBuilder) newDirTree(fset *token.FileSet, path, name string, depth i
for _, d := range list {
if isPkgDir(d) {
name := d.Name()
- dd := b.newDirTree(fset, filepath.Join(path, name), name, depth+1)
+ dd := b.newDirTree(fset, pathpkg.Join(path, name), name, depth+1)
if dd != nil {
dirs[i] = dd
i++
@@ -175,7 +143,14 @@ func (b *treeBuilder) newDirTree(fset *token.FileSet, path, name string, depth i
}
}
- return &Directory{depth, path, name, synopsis, dirs}
+ 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
@@ -188,7 +163,7 @@ func (b *treeBuilder) newDirTree(fset *token.FileSet, path, name string, depth i
// 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, pathFilter func(string) bool, maxDepth int) *Directory {
+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
@@ -204,7 +179,7 @@ func newDirectory(root string, pathFilter func(string) bool, maxDepth int) *Dire
if maxDepth < 0 {
maxDepth = 1e6 // "infinity"
}
- b := treeBuilder{pathFilter, maxDepth}
+ 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)
@@ -253,10 +228,20 @@ func (dir *Directory) lookupLocal(name string) *Directory {
return nil
}
+func splitPath(p string) []string {
+ if strings.HasPrefix(p, "/") {
+ p = p[1:]
+ }
+ 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 := strings.Split(dir.Path, string(filepath.Separator))
- p := strings.Split(path, string(filepath.Separator))
+ d := splitPath(dir.Path)
+ p := splitPath(path)
i := 0
for i < len(d) {
if i >= len(p) || d[i] != p[i] {
@@ -277,9 +262,10 @@ func (dir *Directory) lookup(path string) *Directory {
type DirEntry struct {
Depth int // >= 0
Height int // = DirList.MaxHeight - Depth, > 0
- Path string // includes Name, relative to DirList root
- Name string
- Synopsis string
+ 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 {
@@ -328,13 +314,14 @@ func (root *Directory) listing(skipRoot bool) *DirList {
if strings.HasPrefix(d.Path, root.Path) {
path = d.Path[len(root.Path):]
}
- // remove trailing separator if any - path must be relative
- if len(path) > 0 && path[0] == filepath.Separator {
+ // remove leading separator if any - path must be relative
+ if len(path) > 0 && path[0] == '/' {
path = path[1:]
}
p.Path = path
p.Name = d.Name
- p.Synopsis = d.Text
+ p.HasPkg = d.HasPkg
+ p.Synopsis = d.Synopsis
i++
}
diff --git a/src/cmd/godoc/doc.go b/src/cmd/godoc/doc.go
index 088889d2a..39ecc6e63 100644
--- a/src/cmd/godoc/doc.go
+++ b/src/cmd/godoc/doc.go
@@ -9,12 +9,15 @@ 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 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:
+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
@@ -22,7 +25,7 @@ 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 Writer
+ godoc -q Reader
godoc -q math.Sin
godoc -server=:6060 -q sin
@@ -50,6 +53,17 @@ The flags are:
-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)
+ -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)
@@ -63,23 +77,22 @@ The flags are:
HTTP service address (e.g., '127.0.0.1:6060' or just ':6060')
-server=addr
webserver address for command line searches
- -sync="command"
- if this and -sync_minutes are set, run the argument as a
- command every sync_minutes; it is intended to update the
- repository holding the source files.
- -sync_minutes=0
- sync interval in minutes; sync is disabled if <= 0
- -filter=""
- filter file containing permitted package directory paths
- -filter_minutes=0
- filter file update interval in minutes; update is disabled if <= 0
+ -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
-The -path flag accepts a list of colon-separated paths; unrooted paths are relative
-to the current working directory. Each path is considered as an additional root for
-packages in order of appearance. The last (absolute) path element is the prefix for
-the package path. For instance, given the flag value:
+By default, godoc looks at the packages it finds via $GOROOT and $GOPATH (if set).
+Additional directories may be specified via the -path flag which accepts a list
+of colon-separated paths; unrooted paths are relative to the current working
+directory. Each path is considered as an additional root for packages in order
+of appearance. The last (absolute) path element is the prefix for the package
+path. For instance, given the flag value:
path=".:/home/bar:/public"
@@ -90,29 +103,27 @@ as follows:
/home/bar/x -> bar/x
/public/x -> public/x
-Paths provided via -path may point to very large file systems that contain
-non-Go files. Creating the subtree of directories with Go packages may take
-a long amount of time. A file containing newline-separated directory paths
-may be provided with the -filter flag; if it exists, only directories
-on those paths are considered. If -filter_minutes is set, the filter_file is
-updated regularly by walking the entire directory tree.
-
When godoc runs as a web server and -index is set, a search index is maintained.
-The index is created at startup and is automatically updated every time the
--sync command terminates with exit status 0, indicating that files have changed.
-
-If the sync exit status is 1, godoc assumes that it succeeded without errors
-but that no files changed; the index is not updated in this case.
-
-In all other cases, sync is assumed to have failed and godoc backs off running
-sync exponentially (up to 1 day). As soon as sync succeeds again (exit status 0
-or 1), the normal sync rhythm is re-established.
+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
@@ -126,8 +137,8 @@ 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://blog.golang.org/2011/03/godoc-documenting-go-code.html
+http://golang.org/doc/articles/godoc_documenting_go_code.html
+
*/
package documentation
diff --git a/src/cmd/godoc/filesystem.go b/src/cmd/godoc/filesystem.go
index a68c08592..09d7b2463 100644
--- a/src/cmd/godoc/filesystem.go
+++ b/src/cmd/godoc/filesystem.go
@@ -12,58 +12,93 @@ import (
"fmt"
"io"
"io/ioutil"
+ "net/http"
"os"
+ pathpkg "path"
+ "path/filepath"
+ "sort"
+ "strings"
+ "time"
)
-// The FileInfo interface provides access to file information.
-type FileInfo interface {
- Name() string
- Size() int64
- Mtime_ns() int64
- IsRegular() bool
- IsDirectory() bool
-}
+// 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) (io.ReadCloser, os.Error)
- Lstat(path string) (FileInfo, os.Error)
- Stat(path string) (FileInfo, os.Error)
- ReadDir(path string) ([]FileInfo, os.Error)
- ReadFile(path string) ([]byte, os.Error)
-}
-
-// ----------------------------------------------------------------------------
-// OS-specific FileSystem implementation
-
-var OS FileSystem = osFS{}
-
-// osFI is the OS-specific implementation of FileInfo.
-type osFI struct {
- *os.FileInfo
+ 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
}
-func (fi osFI) Name() string {
- return fi.FileInfo.Name
+type readSeekCloser interface {
+ io.Reader
+ io.Seeker
+ io.Closer
}
-func (fi osFI) Size() int64 {
- if fi.IsDirectory() {
- return 0
+// 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
}
- return fi.FileInfo.Size
+ defer rc.Close()
+ return ioutil.ReadAll(rc)
}
-func (fi osFI) Mtime_ns() int64 {
- return fi.FileInfo.Mtime_ns
+// 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)
}
-// osFS is the OS-specific implementation of FileSystem
-type osFS struct{}
+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 (osFS) Open(path string) (io.ReadCloser, os.Error) {
- f, err := os.Open(path)
+func (root osFS) Open(path string) (readSeekCloser, error) {
+ f, err := os.Open(root.resolve(path))
if err != nil {
return nil, err
}
@@ -71,34 +106,459 @@ func (osFS) Open(path string) (io.ReadCloser, os.Error) {
if err != nil {
return nil, err
}
- if fi.IsDirectory() {
+ if fi.IsDir() {
return nil, fmt.Errorf("Open: %s is a directory", path)
}
return f, nil
}
-func (osFS) Lstat(path string) (FileInfo, os.Error) {
- fi, err := os.Lstat(path)
- return osFI{fi}, err
+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):]
+ if strings.HasPrefix(elem, "/") {
+ elem = elem[1:]
+ }
+ 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
}
-func (osFS) Stat(path string) (FileInfo, os.Error) {
- fi, err := os.Stat(path)
- return osFI{fi}, err
+// 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 (osFS) ReadDir(path string) ([]FileInfo, os.Error) {
- l0, err := ioutil.ReadDir(path) // l0 is sorted
+func (h *httpFS) Open(name string) (http.File, error) {
+ fi, err := h.fs.Stat(name)
if err != nil {
return nil, err
}
- l1 := make([]FileInfo, len(l0))
- for i, e := range l0 {
- l1[i] = osFI{e}
+ 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)
}
- return l1, nil
+ 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 (osFS) ReadFile(path string) ([]byte, os.Error) {
- return ioutil.ReadFile(path)
+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
index 78dde4166..3b1b9a822 100644
--- a/src/cmd/godoc/format.go
+++ b/src/cmd/godoc/format.go
@@ -17,7 +17,7 @@ import (
"io"
"regexp"
"strconv"
- "template"
+ "text/template"
)
// ----------------------------------------------------------------------------
@@ -231,7 +231,7 @@ func commentSelection(src []byte) Selection {
var s scanner.Scanner
fset := token.NewFileSet()
file := fset.AddFile("", fset.Base(), len(src))
- s.Init(file, src, nil, scanner.ScanComments+scanner.InsertSemis)
+ s.Init(file, src, nil, scanner.ScanComments)
return func() (seg []int) {
for {
pos, tok, lit := s.Scan()
diff --git a/src/cmd/godoc/godoc.go b/src/cmd/godoc/godoc.go
index b8a839404..f6dc678b4 100644
--- a/src/cmd/godoc/godoc.go
+++ b/src/cmd/godoc/godoc.go
@@ -6,6 +6,7 @@ package main
import (
"bytes"
+ "encoding/json"
"flag"
"fmt"
"go/ast"
@@ -13,18 +14,22 @@ import (
"go/doc"
"go/printer"
"go/token"
- "http"
"io"
+ "io/ioutil"
"log"
+ "net/http"
+ "net/url"
"os"
- "path"
+ pathpkg "path"
"path/filepath"
"regexp"
"runtime"
"sort"
"strings"
- "template"
+ "text/template"
"time"
+ "unicode"
+ "unicode/utf8"
)
// ----------------------------------------------------------------------------
@@ -34,9 +39,9 @@ type delayTime struct {
RWValue
}
-func (dt *delayTime) backoff(max int) {
+func (dt *delayTime) backoff(max time.Duration) {
dt.mutex.Lock()
- v := dt.value.(int) * 2
+ v := dt.value.(time.Duration) * 2
if v > max {
v = max
}
@@ -50,233 +55,68 @@ var (
// 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)")
- pkgPath = flag.String("path", "", "additional package directories (colon-separated)")
- filter = flag.String("filter", "", "filter file containing permitted package directory paths")
- filterMin = flag.Int("filter_minutes", 0, "filter file update interval in minutes; disabled if <= 0")
- filterDelay delayTime // actual filter update interval in minutes; usually filterDelay == filterMin, but filterDelay may back off exponentially
+ goroot = flag.String("goroot", runtime.GOROOT(), "Go root directory")
+ testDir = flag.String("testdir", "", "Go root subdirectory - for testing only (faster startups)")
+ pkgPath = flag.String("path", "", "additional package directories (colon-separated)")
// layout control
tabwidth = flag.Int("tabwidth", 4, "tab width")
- showTimestamps = flag.Bool("timestamps", true, "show timestamps with directory listings")
+ showTimestamps = flag.Bool("timestamps", false, "show timestamps with directory listings")
templateDir = flag.String("templates", "", "directory containing alternate template files")
// search index
indexEnabled = flag.Bool("index", false, "enable search index")
- maxResults = flag.Int("maxresults", 10000, "maximum number of full text search results shown")
+ 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 mapping
- fs FileSystem // the underlying file system for godoc
- fsHttp http.FileSystem // the underlying file system for http
- fsMap Mapping // user-defined mapping
- fsTree RWValue // *Directory tree of packages, updated with each sync
- pathFilter RWValue // filter used when building fsMap directory trees
- fsModified RWValue // timestamp of last call to invalidateIndex
+ // 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 httpHandler
- pkgHandler httpHandler
+ cmdHandler docServer
+ pkgHandler docServer
)
func initHandlers() {
- paths := filepath.SplitList(*pkgPath)
- for _, t := range build.Path {
- if t.Goroot {
- continue
+ // Add named directories in -path argument as
+ // subdirectories of src/pkg.
+ for _, p := range filepath.SplitList(*pkgPath) {
+ _, elem := filepath.Split(p)
+ if elem == "" {
+ log.Fatalf("invalid -path argument: %q has no final element", p)
}
- paths = append(paths, t.SrcDir())
+ fs.Bind("/src/pkg/"+elem, OS(p), "/", bindReplace)
}
- fsMap.Init(paths)
- fileServer = http.FileServer(fsHttp)
- cmdHandler = httpHandler{"/cmd/", filepath.Join(*goroot, "src", "cmd"), false}
- pkgHandler = httpHandler{"/pkg/", filepath.Join(*goroot, "src", "pkg"), true}
+ fileServer = http.FileServer(&httpFS{fs})
+ cmdHandler = docServer{"/cmd/", "/src/cmd", false}
+ pkgHandler = docServer{"/pkg/", "/src/pkg", true}
}
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() {
- fsTree.set(newDirectory(filepath.Join(*goroot, *testDir), nil, -1))
- invalidateIndex()
-}
-
-// ----------------------------------------------------------------------------
-// Directory filters
-
-// isParentOf returns true if p is a parent of (or the same as) q
-// where p and q are directory paths.
-func isParentOf(p, q string) bool {
- n := len(p)
- return strings.HasPrefix(q, p) && (len(q) <= n || q[n] == '/')
-}
-
-func setPathFilter(list []string) {
- if len(list) == 0 {
- pathFilter.set(nil)
+ dir := newDirectory(pathpkg.Join("/", *testDir), -1)
+ if dir == nil {
+ log.Println("Warning: FSTree is nil")
return
}
-
- // len(list) > 0
- pathFilter.set(func(path string) bool {
- // list is sorted in increasing order and for each path all its children are removed
- i := sort.Search(len(list), func(i int) bool { return list[i] > path })
- // Now we have list[i-1] <= path < list[i].
- // Path may be a child of list[i-1] or a parent of list[i].
- return i > 0 && isParentOf(list[i-1], path) || i < len(list) && isParentOf(path, list[i])
- })
-}
-
-func getPathFilter() func(string) bool {
- f, _ := pathFilter.get()
- if f != nil {
- return f.(func(string) bool)
- }
- return nil
-}
-
-// readDirList reads a file containing a newline-separated list
-// of directory paths and returns the list of paths.
-func readDirList(filename string) ([]string, os.Error) {
- contents, err := fs.ReadFile(filename)
- if err != nil {
- return nil, err
- }
- // create a sorted list of valid directory names
- filter := func(path string) bool {
- d, e := fs.Lstat(path)
- if e != nil && err == nil {
- // remember first error and return it from readDirList
- // so we have at least some information if things go bad
- err = e
- }
- return e == nil && isPkgDir(d)
- }
- list := canonicalizePaths(strings.Split(string(contents), "\n"), filter)
- // for each parent path, remove all its children q
- // (requirement for binary search to work when filtering)
- i := 0
- for _, q := range list {
- if i == 0 || !isParentOf(list[i-1], q) {
- list[i] = q
- i++
- }
- }
- return list[0:i], err
-}
-
-// updateMappedDirs computes the directory tree for
-// each user-defined file system mapping. If a filter
-// is provided, it is used to filter directories.
-//
-func updateMappedDirs(filter func(string) bool) {
- if !fsMap.IsEmpty() {
- fsMap.Iterate(func(path string, value *RWValue) bool {
- value.set(newDirectory(path, filter, -1))
- return true
- })
- invalidateIndex()
- }
-}
-
-func updateFilterFile() {
- updateMappedDirs(nil) // no filter for accuracy
-
- // collect directory tree leaf node paths
- var buf bytes.Buffer
- fsMap.Iterate(func(_ string, value *RWValue) bool {
- v, _ := value.get()
- if v != nil && v.(*Directory) != nil {
- v.(*Directory).writeLeafs(&buf)
- }
- return true
- })
-
- // update filter file
- if err := writeFileAtomically(*filter, buf.Bytes()); err != nil {
- log.Printf("writeFileAtomically(%s): %s", *filter, err)
- filterDelay.backoff(24 * 60) // back off exponentially, but try at least once a day
- } else {
- filterDelay.set(*filterMin) // revert to regular filter update schedule
- }
-}
-
-func initDirTrees() {
- // setup initial path filter
- if *filter != "" {
- list, err := readDirList(*filter)
- if err != nil {
- log.Printf("readDirList(%s): %s", *filter, err)
- }
- if *verbose || len(list) == 0 {
- log.Printf("found %d directory paths in file %s", len(list), *filter)
- }
- setPathFilter(list)
- }
-
- go updateMappedDirs(getPathFilter()) // use filter for speed
-
- // start filter update goroutine, if enabled.
- if *filter != "" && *filterMin > 0 {
- filterDelay.set(*filterMin) // initial filter update delay
- go func() {
- for {
- if *verbose {
- log.Printf("start update of %s", *filter)
- }
- updateFilterFile()
- delay, _ := filterDelay.get()
- if *verbose {
- log.Printf("next filter update in %dmin", delay.(int))
- }
- time.Sleep(int64(delay.(int)) * 60e9)
- }
- }()
- }
-}
-
-// ----------------------------------------------------------------------------
-// Path mapping
-
-// Absolute paths are file system paths (backslash-separated on Windows),
-// but relative paths are always slash-separated.
-
-func absolutePath(relpath, defaultRoot string) string {
- abspath := fsMap.ToAbsolute(relpath)
- if abspath == "" {
- // no user-defined mapping found; use default mapping
- abspath = filepath.Join(defaultRoot, filepath.FromSlash(relpath))
- }
- return abspath
-}
-
-func relativeURL(abspath string) string {
- relpath := fsMap.ToRelative(abspath)
- if relpath == "" {
- // prefix must end in a path separator
- prefix := *goroot
- if len(prefix) > 0 && prefix[len(prefix)-1] != filepath.Separator {
- prefix += string(filepath.Separator)
- }
- if strings.HasPrefix(abspath, prefix) {
- // no user-defined mapping found; use default mapping
- relpath = filepath.ToSlash(abspath[len(prefix):])
- }
- }
- // Only if path is an invalid absolute path is relpath == ""
- // at this point. This should never happen since absolute paths
- // are only created via godoc for files that do exist. However,
- // it is ok to return ""; it will simply provide a link to the
- // top of the pkg or src directories.
- return relpath
+ fsTree.set(dir)
+ invalidateIndex()
}
// ----------------------------------------------------------------------------
@@ -296,7 +136,7 @@ type tconv struct {
indent int // valid if state == indenting
}
-func (p *tconv) writeIndent() (err os.Error) {
+func (p *tconv) writeIndent() (err error) {
i := p.indent
for i >= len(spaces) {
i -= len(spaces)
@@ -311,7 +151,7 @@ func (p *tconv) writeIndent() (err os.Error) {
return
}
-func (p *tconv) Write(data []byte) (n int, err os.Error) {
+func (p *tconv) Write(data []byte) (n int, err error) {
if len(data) == 0 {
return
}
@@ -368,25 +208,28 @@ func writeNode(w io.Writer, fset *token.FileSet, x interface{}) {
// with an another printer mode (which is more efficiently
// implemented in the printer than here with another layer)
mode := printer.TabIndent | printer.UseSpaces
- (&printer.Config{mode, *tabwidth}).Fprint(&tconv{output: w}, fset, x)
+ 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 := filepath.Split(path)
+ _, localname := pathpkg.Split(path)
return localname
}
-func fileInfoNameFunc(fi FileInfo) string {
+func fileInfoNameFunc(fi os.FileInfo) string {
name := fi.Name()
- if fi.IsDirectory() {
+ if fi.IsDir() {
name += "/"
}
return name
}
-func fileInfoTimeFunc(fi FileInfo) string {
- if t := fi.Mtime_ns(); t != 0 {
- return time.SecondsToLocalTime(t / 1e9).String()
+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
}
@@ -403,8 +246,8 @@ var infoKinds = [nKinds]string{
Use: "use",
}
-func infoKind_htmlFunc(kind SpotKind) string {
- return infoKinds[kind] // infoKind entries are html-escaped
+func infoKind_htmlFunc(info SpotInfo) string {
+ return infoKinds[info.Kind()] // infoKind entries are html-escaped
}
func infoLineFunc(info SpotInfo) int {
@@ -451,12 +294,118 @@ 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, []byte(comment), nil) // does html-escaping
+ 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:`)
+
+func example_htmlFunc(funcName string, examples []*doc.Example, fset *token.FileSet) string {
+ var buf bytes.Buffer
+ for _, eg := range examples {
+ name := eg.Name
+
+ // strip lowercase braz in Foo_braz or Foo_Bar_braz from name
+ // while keeping uppercase Braz in Foo_Braz
+ if i := strings.LastIndex(name, "_"); i != -1 {
+ if i < len(name)-1 && !startsWithUppercase(name[i+1:]) {
+ name = name[:i]
+ }
+ }
+
+ if name != funcName {
+ continue
+ }
+
+ // print code
+ cnode := &printer.CommentedNode{Node: eg.Code, Comments: eg.Comments}
+ code := node_htmlFunc(cnode, fset)
+ out := eg.Output
+
+ // 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)
+ // remove output comment
+ if loc := exampleOutputRx.FindStringIndex(code); loc != nil {
+ code = strings.TrimSpace(code[:loc[0]])
+ }
+ } else {
+ // drop output, as the output comment will appear in the code
+ out = ""
+ }
+
+ err := exampleHTML.Execute(&buf, struct {
+ Name, Doc, Code, Output string
+ }{eg.Name, eg.Doc, code, 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 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 := relativeURL(path)
+ relpath := path[1:]
// because of the irregular mapping under goroot
// we need to correct certain relative paths
if strings.HasPrefix(relpath, "src/pkg/") {
@@ -472,7 +421,7 @@ func posLink_urlFunc(node ast.Node, fset *token.FileSet) string {
if p := node.Pos(); p.IsValid() {
pos := fset.Position(p)
- relpath = relativeURL(pos.Filename)
+ relpath = pos.Filename
line = pos.Line
low = pos.Offset
}
@@ -501,6 +450,10 @@ func posLink_urlFunc(node ast.Node, fset *token.FileSet) string {
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
@@ -523,24 +476,34 @@ var fmap = template.FuncMap{
"node": nodeFunc,
"node_html": node_htmlFunc,
"comment_html": comment_htmlFunc,
+ "comment_text": comment_textFunc,
// support for URL attributes
"pkgLink": pkgLinkFunc,
- "srcLink": relativeURL,
+ "srcLink": srcLinkFunc,
"posLink_url": posLink_urlFunc,
+
+ // formatting of Examples
+ "example_html": example_htmlFunc,
+ "example_name": example_nameFunc,
+ "example_suffix": example_suffixFunc,
}
func readTemplate(name string) *template.Template {
- path := filepath.Join(*goroot, "lib", "godoc", name)
- if *templateDir != "" {
- defaultpath := path
- path = filepath.Join(*templateDir, name)
- if _, err := fs.Stat(path); err != nil {
- log.Print("readTemplate:", err)
- path = defaultpath
- }
+ 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 template.Must(template.New(name).Funcs(fmap).ParseFile(path))
+ return t
}
var (
@@ -548,11 +511,13 @@ var (
codewalkdirHTML,
dirlistHTML,
errorHTML,
+ exampleHTML,
godocHTML,
packageHTML,
packageText,
searchHTML,
- searchText *template.Template
+ searchText,
+ searchDescXML *template.Template
)
func readTemplates() {
@@ -561,30 +526,35 @@ func readTemplates() {
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
-func servePage(w http.ResponseWriter, title, subtitle, query string, content []byte) {
+func servePage(w http.ResponseWriter, tabtitle, title, subtitle, query string, content []byte) {
+ if tabtitle == "" {
+ tabtitle = title
+ }
d := struct {
+ Tabtitle string
Title string
Subtitle string
- PkgRoots []string
SearchBox bool
Query string
Version string
Menu []byte
Content []byte
}{
+ tabtitle,
title,
subtitle,
- fsMap.PrefixList(),
*indexEnabled,
query,
runtime.Version(),
@@ -606,22 +576,14 @@ func serveText(w http.ResponseWriter, text []byte) {
// Files
var (
- titleRx = regexp.MustCompile(`<!-- title ([^\-]*)-->`)
- subtitleRx = regexp.MustCompile(`<!-- subtitle ([^\-]*)-->`)
- firstCommentRx = regexp.MustCompile(`<!--([^\-]*)-->`)
+ doctype = []byte("<!DOCTYPE ")
+ jsonStart = []byte("<!--{")
+ jsonEnd = []byte("}-->")
)
-func extractString(src []byte, rx *regexp.Regexp) (s string) {
- m := rx.FindSubmatch(src)
- if m != nil {
- s = strings.TrimSpace(string(m[1]))
- }
- return
-}
-
func serveHTMLDoc(w http.ResponseWriter, r *http.Request, abspath, relpath string) {
// get HTML body contents
- src, err := fs.ReadFile(abspath)
+ src, err := ReadFile(fs, abspath)
if err != nil {
log.Printf("ReadFile: %s", err)
serveError(w, r, relpath, err)
@@ -630,27 +592,42 @@ func serveHTMLDoc(w http.ResponseWriter, r *http.Request, abspath, relpath strin
// if it begins with "<!DOCTYPE " assume it is standalone
// html that doesn't need the template wrapping.
- if bytes.HasPrefix(src, []byte("<!DOCTYPE ")) {
+ if bytes.HasPrefix(src, doctype) {
w.Write(src)
return
}
- // if it's the language spec, add tags to EBNF productions
- if strings.HasSuffix(abspath, "go_spec.html") {
+ // 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
- linkify(&buf, src)
+ 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()
}
- // get title and subtitle, if any
- title := extractString(src, titleRx)
- if title == "" {
- // no title found; try first comment for backward-compatibility
- title = extractString(src, firstCommentRx)
+ // 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()
}
- subtitle := extractString(src, subtitleRx)
- servePage(w, title, subtitle, "", src)
+ servePage(w, "", meta.Title, meta.Subtitle, "", src)
}
func applyTemplate(t *template.Template, name string, data interface{}) []byte {
@@ -662,7 +639,11 @@ func applyTemplate(t *template.Template, name string, data interface{}) []byte {
}
func redirect(w http.ResponseWriter, r *http.Request) (redirected bool) {
- if canonical := path.Clean(r.URL.Path) + "/"; r.URL.Path != canonical {
+ canonical := pathpkg.Clean(r.URL.Path)
+ if !strings.HasSuffix("/", canonical) {
+ canonical += "/"
+ }
+ if r.URL.Path != canonical {
http.Redirect(w, r, canonical, http.StatusMovedPermanently)
redirected = true
}
@@ -670,7 +651,7 @@ func redirect(w http.ResponseWriter, r *http.Request) (redirected bool) {
}
func serveTextFile(w http.ResponseWriter, r *http.Request, abspath, relpath, title string) {
- src, err := fs.ReadFile(abspath)
+ src, err := ReadFile(fs, abspath)
if err != nil {
log.Printf("ReadFile: %s", err)
serveError(w, r, relpath, err)
@@ -679,10 +660,10 @@ func serveTextFile(w http.ResponseWriter, r *http.Request, abspath, relpath, tit
var buf bytes.Buffer
buf.WriteString("<pre>")
- FormatText(&buf, src, 1, filepath.Ext(abspath) == ".go", r.FormValue("h"), rangeSelection(r.FormValue("s")))
+ FormatText(&buf, src, 1, pathpkg.Ext(abspath) == ".go", r.FormValue("h"), rangeSelection(r.FormValue("s")))
buf.WriteString("</pre>")
- servePage(w, title+" "+relpath, "", "", buf.Bytes())
+ servePage(w, relpath, title+" "+relpath, "", "", buf.Bytes())
}
func serveDirectory(w http.ResponseWriter, r *http.Request, abspath, relpath string) {
@@ -692,32 +673,32 @@ func serveDirectory(w http.ResponseWriter, r *http.Request, abspath, relpath str
list, err := fs.ReadDir(abspath)
if err != nil {
- log.Printf("ReadDir: %s", err)
serveError(w, r, relpath, err)
return
}
contents := applyTemplate(dirlistHTML, "dirlistHTML", list)
- servePage(w, "Directory "+relpath, "", "", contents)
+ servePage(w, relpath, "Directory "+relpath, "", "", contents)
}
func serveFile(w http.ResponseWriter, r *http.Request) {
- relpath := r.URL.Path[1:] // serveFile URL paths start with '/'
- abspath := absolutePath(relpath, *goroot)
-
- // pick off special cases and hand the rest to the standard file server
- switch r.URL.Path {
- case "/":
- serveHTMLDoc(w, r, filepath.Join(*goroot, "doc", "root.html"), "doc/root.html")
- return
+ relpath := r.URL.Path
- case "/doc/root.html":
- // hide landing page from its real name
- http.Redirect(w, r, "/", http.StatusMovedPermanently)
- return
+ // 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
}
- switch path.Ext(relpath) {
+ 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.
@@ -740,12 +721,12 @@ func serveFile(w http.ResponseWriter, r *http.Request) {
return
}
- if dir != nil && dir.IsDirectory() {
+ if dir != nil && dir.IsDir() {
if redirect(w, r) {
return
}
- if index := filepath.Join(abspath, "index.html"); isTextFile(index) {
- serveHTMLDoc(w, r, index, relativeURL(index))
+ if index := pathpkg.Join(abspath, "index.html"); isTextFile(index) {
+ serveHTMLDoc(w, r, index, index)
return
}
serveDirectory(w, r, abspath, relpath)
@@ -760,6 +741,16 @@ func serveFile(w http.ResponseWriter, r *http.Request) {
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
@@ -769,37 +760,106 @@ const fakePkgName = "documentation"
// 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/"
+const builtinPkgPath = "builtin"
type PageInfoMode uint
const (
- exportsOnly PageInfoMode = 1 << iota // only keep exported stuff
- genDoc // generate documentation
+ 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
- PList []string // list of package names found
- FSet *token.FileSet // corresponding file set
- PAst *ast.File // nil if no single AST with package exports
- PDoc *doc.PackageDoc // nil if no single package documentation
- Dirs *DirList // nil if no directory information
- DirTime int64 // directory time stamp in seconds since epoch
- IsPkg bool // false if this is not documenting a real package
- Err os.Error // directory read error or nil
+ Dirname string // directory containing the package
+ PList []string // list of package names found
+ FSet *token.FileSet // corresponding file set
+ PAst *ast.File // nil if no single AST with package exports
+ PDoc *doc.Package // nil if no single package documentation
+ Examples []*doc.Example // nil if no example code
+ 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
+ IsPkg bool // false if this is not documenting a real package
+ Err error // I/O error or nil
}
func (info *PageInfo) IsEmpty() bool {
return info.Err != nil || info.PAst == nil && info.PDoc == nil && info.Dirs == nil
}
-type httpHandler struct {
+type docServer struct {
pattern string // url pattern; e.g. "/pkg/"
fsRoot string // file system root to which the pattern is mapped
isPkg bool // true if this handler serves real package documentation (as opposed to command documentation)
}
+// fsReadDir implements ReadDir for the go/build package.
+func fsReadDir(dir string) ([]os.FileInfo, error) {
+ return fs.ReadDir(dir)
+}
+
+// fsOpenFile implements OpenFile for the go/build package.
+func fsOpenFile(name string) (r io.ReadCloser, err error) {
+ data, err := ReadFile(fs, name)
+ if err != nil {
+ return nil, err
+ }
+ return ioutil.NopCloser(bytes.NewReader(data)), nil
+}
+
+func inList(name string, list []string) bool {
+ for _, l := range list {
+ if name == l {
+ return true
+ }
+ }
+ return false
+}
+
// 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)
@@ -808,12 +868,42 @@ type httpHandler struct {
// directories, PageInfo.Dirs is nil. If a directory read error occurred,
// PageInfo.Err is set to the respective error but the error is not logged.
//
-func (h *httpHandler) getPageInfo(abspath, relpath, pkgname string, mode PageInfoMode) PageInfo {
+func (h *docServer) getPageInfo(abspath, relpath, pkgname string, mode PageInfoMode) PageInfo {
+ var pkgFiles []string
+
+ // If we're showing the default package, restrict to the ones
+ // 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.
+ if pkgname == "" {
+ // 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.ScanDir.
+ ctxt := build.Default
+ ctxt.IsAbsPath = pathpkg.IsAbs
+ ctxt.ReadDir = fsReadDir
+ ctxt.OpenFile = fsOpenFile
+ dir, err := ctxt.ImportDir(abspath, 0)
+ if err == nil {
+ pkgFiles = append(dir.GoFiles, dir.CgoFiles...)
+ }
+ }
+
// filter function to select the desired .go files
- filter := func(d FileInfo) bool {
+ filter := func(d os.FileInfo) bool {
+ // Only Go files.
+ if !isPkgFile(d) {
+ return false
+ }
// If we are looking at cmd documentation, only accept
// the special fakePkgFile containing the documentation.
- return isPkgFile(d) && (h.isPkg || d.Name() == fakePkgFile)
+ if !h.isPkg {
+ return d.Name() == fakePkgFile
+ }
+ // Also restrict file list to pkgFiles.
+ return pkgFiles == nil || inList(d.Name(), pkgFiles)
}
// get package ASTs
@@ -840,13 +930,13 @@ func (h *httpHandler) getPageInfo(abspath, relpath, pkgname string, mode PageInf
// the package with dirname, and the 3rd choice is a package
// that is not called "main" if there is exactly one such
// package. Otherwise, don't select a package.
- dirpath, dirname := filepath.Split(abspath)
+ dirpath, dirname := pathpkg.Split(abspath)
// If the dirname is "go" we might be in a sub-directory for
// .go files - use the outer directory name instead for better
// results.
if dirname == "go" {
- _, dirname = filepath.Split(filepath.Clean(dirpath))
+ _, dirname = pathpkg.Split(pathpkg.Clean(dirpath))
}
var choice3 *ast.Package
@@ -877,25 +967,54 @@ func (h *httpHandler) getPageInfo(abspath, relpath, pkgname string, mode PageInf
}
}
plist = plist[0:i]
+ sort.Strings(plist)
+ }
+
+ // get examples from *_test.go files
+ var examples []*doc.Example
+ filter = func(d os.FileInfo) bool {
+ return isGoFile(d) && strings.HasSuffix(d.Name(), "_test.go")
+ }
+ if testpkgs, err := parseDir(fset, abspath, filter); err != nil {
+ log.Println("parsing test files:", err)
+ } else {
+ for _, testpkg := range testpkgs {
+ var files []*ast.File
+ for _, f := range testpkg.Files {
+ files = append(files, f)
+ }
+ examples = append(examples, doc.Examples(files...)...)
+ }
}
// compute package documentation
var past *ast.File
- var pdoc *doc.PackageDoc
+ var pdoc *doc.Package
if pkg != nil {
- if mode&exportsOnly != 0 {
- ast.PackageExports(pkg)
- }
- if mode&genDoc != 0 {
- pdoc = doc.NewPackageDoc(pkg, path.Clean(relpath)) // no trailing '/' in importpath
+ 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
+ }
+ pdoc = doc.New(pkg, pathpkg.Clean(relpath), m) // no trailing '/' in importpath
} else {
+ // show source code
+ // TODO(gri) Consider eliminating export filtering in this mode,
+ // or perhaps eliminating the mode altogether.
+ if mode&noFiltering == 0 {
+ ast.PackageExports(pkg)
+ }
past = ast.MergePackageFiles(pkg, ast.FilterUnassociatedComments)
}
}
// get directory information
var dir *Directory
- var timestamp int64
+ 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
@@ -904,53 +1023,39 @@ func (h *httpHandler) getPageInfo(abspath, relpath, pkgname string, mode PageInf
timestamp = ts
}
if dir == nil {
- // the path may refer to a user-specified file system mapped
- // via fsMap; lookup that mapping and corresponding RWValue
- // if any
- var v *RWValue
- fsMap.Iterate(func(path string, value *RWValue) bool {
- if isParentOf(path, abspath) {
- // mapping found
- v = value
- return false
- }
- return true
- })
- if v != nil {
- // found a RWValue associated with a user-specified file
- // system; a non-nil RWValue stores a (possibly out-of-date)
- // directory tree for that file system
- if tree, ts := v.get(); tree != nil && tree.(*Directory) != nil {
- 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, nil, 1)
- timestamp = time.Seconds()
+ dir = newDirectory(abspath, 1)
+ timestamp = time.Now()
+ }
+
+ return PageInfo{
+ Dirname: abspath,
+ PList: plist,
+ FSet: fset,
+ PAst: past,
+ PDoc: pdoc,
+ Examples: examples,
+ Dirs: dir.listing(true),
+ DirTime: timestamp,
+ DirFlat: mode&flatDir != 0,
+ IsPkg: h.isPkg,
+ Err: nil,
}
-
- return PageInfo{abspath, plist, fset, past, pdoc, dir.listing(true), timestamp, h.isPkg, nil}
}
-func (h *httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+func (h *docServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if redirect(w, r) {
return
}
- relpath := r.URL.Path[len(h.pattern):]
- abspath := absolutePath(relpath, h.fsRoot)
- var mode PageInfoMode
- if relpath != builtinPkgPath {
- mode = exportsOnly
- }
- if r.FormValue("m") != "src" {
- mode |= genDoc
+ 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, r.FormValue("p"), mode)
if info.Err != nil {
@@ -959,36 +1064,47 @@ func (h *httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
- if r.FormValue("f") == "text" {
+ if mode&noHtml != 0 {
contents := applyTemplate(packageText, "packageText", info)
serveText(w, contents)
return
}
- var title, subtitle string
+ var tabtitle, title, subtitle string
switch {
case info.PAst != nil:
- title = "Package " + info.PAst.Name.Name
+ tabtitle = info.PAst.Name.Name
+ title = "Package " + tabtitle
case info.PDoc != nil:
- switch {
- case info.IsPkg:
- title = "Package " + info.PDoc.PackageName
- case info.PDoc.PackageName == fakePkgName:
+ if info.PDoc.Name == fakePkgName {
// assume that the directory name is the command name
- _, pkgname := path.Split(path.Clean(relpath))
- title = "Command " + pkgname
- default:
- title = "Command " + info.PDoc.PackageName
+ _, tabtitle = pathpkg.Split(relpath)
+ } else {
+ tabtitle = info.PDoc.Name
+ }
+ if info.IsPkg {
+ title = "Package " + tabtitle
+ } else {
+ title = "Command " + tabtitle
}
default:
- title = "Directory " + relativeURL(info.Dirname)
+ tabtitle = info.Dirname
+ title = "Directory " + tabtitle
if *showTimestamps {
- subtitle = "Last update: " + time.SecondsToLocalTime(info.DirTime).String()
+ subtitle = "Last update: " + info.DirTime.String()
}
}
+ // special cases for top-level package/command directories
+ switch tabtitle {
+ case "/src/pkg":
+ tabtitle = "Packages"
+ case "/src/cmd":
+ tabtitle = "Commands"
+ }
+
contents := applyTemplate(packageHTML, "packageHTML", info)
- servePage(w, title, subtitle, "", contents)
+ servePage(w, tabtitle, title, subtitle, "", contents)
}
// ----------------------------------------------------------------------------
@@ -1001,6 +1117,7 @@ type SearchResult struct {
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
@@ -1018,12 +1135,12 @@ func lookup(query string) (result SearchResult) {
index := index.(*Index)
// identifier search
- var err os.Error
- result.Hit, result.Alt, err = index.Lookup(query)
+ 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.String()
+ result.Alert = "Error in query string: " + err.Error()
return
}
@@ -1031,7 +1148,7 @@ func lookup(query string) (result SearchResult) {
if *maxResults > 0 && query != "" {
rx, err := regexp.Compile(query)
if err != nil {
- result.Alert = "Error in query regular expression: " + err.String()
+ result.Alert = "Error in query regular expression: " + err.Error()
return
}
// If we get maxResults+1 results we know that there are more than
@@ -1048,10 +1165,8 @@ func lookup(query string) (result SearchResult) {
// is the result accurate?
if *indexEnabled {
- if _, ts := fsModified.get(); timestamp < ts {
- // The index is older than the latest file system change
- // under godoc's observation. Indexing may be in progress
- // or start shortly (see indexer()).
+ 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 {
@@ -1065,7 +1180,7 @@ func search(w http.ResponseWriter, r *http.Request) {
query := strings.TrimSpace(r.FormValue("q"))
result := lookup(query)
- if r.FormValue("f") == "text" {
+ if getPageInfoMode(r)&noHtml != 0 {
contents := applyTemplate(searchText, "searchText", result)
serveText(w, contents)
return
@@ -1079,7 +1194,133 @@ func search(w http.ResponseWriter, r *http.Request) {
}
contents := applyTemplate(searchHTML, "searchHTML", result)
- servePage(w, title, "", query, contents)
+ servePage(w, query, title, "", query, contents)
+}
+
+// ----------------------------------------------------------------------------
+// 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
}
// ----------------------------------------------------------------------------
@@ -1090,6 +1331,7 @@ func search(w http.ResponseWriter, r *http.Request) {
//
func invalidateIndex() {
fsModified.set(nil)
+ refreshMetadata()
}
// indexUpToDate() returns true if the search index is not older
@@ -1098,7 +1340,7 @@ func invalidateIndex() {
func indexUpToDate() bool {
_, fsTime := fsModified.get()
_, siTime := searchIndex.get()
- return fsTime <= siTime
+ return !fsTime.After(siTime)
}
// feedDirnames feeds the directory names of all directories
@@ -1116,43 +1358,77 @@ func feedDirnames(root *RWValue, c chan<- string) {
// of all the file systems under godoc's observation.
//
func fsDirnames() <-chan string {
- c := make(chan string, 256) // asynchronous for fewer context switches
+ c := make(chan string, 256) // buffered for fewer context switches
go func() {
feedDirnames(&fsTree, c)
- fsMap.Iterate(func(_ string, root *RWValue) bool {
- feedDirnames(root, c)
- return true
- })
close(c)
}()
return c
}
+func readIndex(filenames string) error {
+ matches, err := filepath.Glob(filenames)
+ if err != nil {
+ return err
+ }
+ 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
- if *verbose {
- log.Printf("updating index...")
- }
- start := time.Nanoseconds()
- index := NewIndex(fsDirnames(), *maxResults > 0)
- stop := time.Nanoseconds()
- searchIndex.set(index)
- if *verbose {
- secs := float64((stop-start)/1e6) / 1e3
- 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)
- }
- log.Printf("before GC: bytes = %d footprint = %d", runtime.MemStats.HeapAlloc, runtime.MemStats.Sys)
- runtime.GC()
- log.Printf("after GC: bytes = %d footprint = %d", runtime.MemStats.HeapAlloc, runtime.MemStats.Sys)
+ updateIndex()
}
- var delay int64 = 60 * 1e9 // by default, try every 60s
+ delay := 60 * time.Second // by default, try every 60s
if *testDir != "" {
// in test mode, try once a second for fast startup
- delay = 1 * 1e9
+ delay = 1 * time.Second
}
time.Sleep(delay)
}
diff --git a/src/cmd/godoc/httpzip.go b/src/cmd/godoc/httpzip.go
deleted file mode 100644
index cb8322ee4..000000000
--- a/src/cmd/godoc/httpzip.go
+++ /dev/null
@@ -1,181 +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 http.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 are considered relative to
-// the root specified with NewHttpZipFS (even if the paths start with a '/').
-
-// TODO(gri) Should define a commonly used FileSystem API that is the same
-// for http and godoc. Then we only need one zip-file based file
-// system implementation.
-
-package main
-
-import (
- "archive/zip"
- "fmt"
- "http"
- "io"
- "os"
- "path"
- "sort"
- "strings"
-)
-
-// We cannot import syscall on app engine.
-// TODO(gri) Once we have a truly abstract FileInfo implementation
-// this won't be needed anymore.
-const (
- S_IFDIR = 0x4000 // == syscall.S_IFDIR
- S_IFREG = 0x8000 // == syscall.S_IFREG
-)
-
-// httpZipFile is the zip-file based implementation of http.File
-type httpZipFile struct {
- path string // absolute path within zip FS without leading '/'
- info os.FileInfo
- io.ReadCloser // nil for directory
- list zipList
-}
-
-func (f *httpZipFile) Close() os.Error {
- if f.info.IsRegular() {
- return f.ReadCloser.Close()
- }
- f.list = nil
- return nil
-}
-
-func (f *httpZipFile) Stat() (*os.FileInfo, os.Error) {
- return &f.info, nil
-}
-
-func (f *httpZipFile) Readdir(count int) ([]os.FileInfo, os.Error) {
- var list []os.FileInfo
- dirname := f.path + "/"
- prevname := ""
- for i, e := range f.list {
- if count == 0 {
- f.list = f.list[i:]
- break
- }
- if !strings.HasPrefix(e.Name, dirname) {
- f.list = nil
- break // not in the same directory anymore
- }
- name := e.Name[len(dirname):] // local name
- var mode uint32
- var size, mtime_ns int64
- 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
- mode = S_IFDIR
- // no size or mtime_ns for directories
- } else {
- mode = S_IFREG
- size = int64(e.UncompressedSize)
- mtime_ns = e.Mtime_ns()
- }
- // 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, os.FileInfo{
- Name: name,
- Mode: mode,
- Size: size,
- Mtime_ns: mtime_ns,
- })
- prevname = name
- count--
- }
- }
-
- if count >= 0 && len(list) == 0 {
- return nil, os.EOF
- }
-
- return list, nil
-}
-
-func (f *httpZipFile) Seek(offset int64, whence int) (int64, os.Error) {
- return 0, fmt.Errorf("Seek not implemented for zip file entry: %s", f.info.Name)
-}
-
-// httpZipFS is the zip-file based implementation of http.FileSystem
-type httpZipFS struct {
- *zip.ReadCloser
- list zipList
- root string
-}
-
-func (fs *httpZipFS) Open(name string) (http.File, os.Error) {
- // fs.root does not start with '/'.
- path := path.Join(fs.root, name) // path is clean
- index, exact := fs.list.lookup(path)
- if index < 0 || !strings.HasPrefix(path, fs.root) {
- // file not found or not under root
- return nil, fmt.Errorf("file not found: %s", name)
- }
-
- if exact {
- // exact match found - must be a file
- f := fs.list[index]
- rc, err := f.Open()
- if err != nil {
- return nil, err
- }
- return &httpZipFile{
- path,
- os.FileInfo{
- Name: name,
- Mode: S_IFREG,
- Size: int64(f.UncompressedSize),
- Mtime_ns: f.Mtime_ns(),
- },
- rc,
- nil,
- }, nil
- }
-
- // not an exact match - must be a directory
- return &httpZipFile{
- path,
- os.FileInfo{
- Name: name,
- Mode: S_IFDIR,
- // no size or mtime_ns for directories
- },
- nil,
- fs.list[index:],
- }, nil
-}
-
-func (fs *httpZipFS) Close() os.Error {
- fs.list = nil
- return fs.ReadCloser.Close()
-}
-
-// NewHttpZipFS creates a new http.FileSystem based on the contents of
-// the zip file rc restricted to the directory tree specified by root;
-// root must be an absolute path.
-func NewHttpZipFS(rc *zip.ReadCloser, root string) http.FileSystem {
- list := make(zipList, len(rc.File))
- copy(list, rc.File) // sort a copy of rc.File
- sort.Sort(list)
- return &httpZipFS{rc, list, zipPath(root)}
-}
diff --git a/src/cmd/godoc/index.go b/src/cmd/godoc/index.go
index 9b4f31514..1bef79693 100644
--- a/src/cmd/godoc/index.go
+++ b/src/cmd/godoc/index.go
@@ -7,7 +7,7 @@
//
// Algorithm for identifier index:
// - traverse all .go files of the file tree specified by root
-// - for each word (identifier) encountered, collect all occurrences (spots)
+// - 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
@@ -38,62 +38,80 @@
package main
import (
+ "bufio"
"bytes"
- "container/vector"
+ "encoding/gob"
+ "errors"
"go/ast"
"go/parser"
"go/token"
- "go/scanner"
"index/suffixarray"
+ "io"
"os"
- "path/filepath"
+ 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 vector of entries that can be sorted according to some
+// 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 struct {
- vector.Vector
- less func(x, y interface{}) bool
-}
-
-func (h *RunList) Less(i, j int) bool { return h.less(h.At(i), h.At(j)) }
+type RunList []interface{}
-func (h *RunList) sort(less func(x, y interface{}) bool) {
- h.less = less
- sort.Sort(h)
+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 func(x, y interface{}) bool, newRun func(h *RunList, i, j int) interface{}) *RunList {
+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 := 0
- for j := 0; j < h.Len(); j++ {
- if less(h.At(i), h.At(j)) {
- hh.Push(newRun(h, i, j))
- i = j // start a new run
+ 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 < h.Len() {
- hh.Push(newRun(h, i, h.Len()))
+ if i < len(h) {
+ hh = append(hh, newRun(h[i:]))
}
- return &hh
+ return hh
}
// ----------------------------------------------------------------------------
@@ -164,30 +182,25 @@ func (x SpotInfo) IsIndex() bool { return x&1 != 0 }
const removeDuplicates = true
// A KindRun is a run of SpotInfos of the same kind in a given file.
-type KindRun struct {
- Kind SpotKind
- Infos []SpotInfo
-}
+// 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 (f *KindRun) Len() int { return len(f.Infos) }
-func (f *KindRun) Less(i, j int) bool { return f.Infos[i].Lori() < f.Infos[j].Lori() }
-func (f *KindRun) Swap(i, j int) { f.Infos[i], f.Infos[j] = f.Infos[j], f.Infos[i] }
+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 [i, j) in h.
-func newKindRun(h *RunList, i, j int) interface{} {
- kind := h.At(i).(SpotInfo).Kind()
- infos := make([]SpotInfo, j-i)
- k := 0
- for ; i < j; i++ {
- infos[k] = h.At(i).(SpotInfo)
- k++
+// 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)
}
- run := &KindRun{kind, infos}
// Spots were sorted by file and kind to create this run.
// Within this run, sort them by line number or index.
@@ -199,15 +212,15 @@ func newKindRun(h *RunList, i, j int) interface{} {
// bit is always the same for all infos in one
// list we can simply compare the entire info.
k := 0
- var prev SpotInfo
- for i, x := range infos {
- if x != prev || i == 0 {
- infos[k] = x
+ prev := SpotInfo(1<<32 - 1) // an unlikely value
+ for _, x := range run {
+ if x != prev {
+ run[k] = x
k++
prev = x
}
}
- run.Infos = infos[0:k]
+ run = run[0:k]
}
return run
@@ -229,8 +242,13 @@ func (p *Pak) less(q *Pak) bool {
// A File describes a Go file.
type File struct {
- Path string // complete file name
- Pak Pak // the package to which the file belongs
+ 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.
@@ -242,30 +260,34 @@ type Spot struct {
// A FileRun is a list of KindRuns belonging to the same file.
type FileRun struct {
File *File
- Groups []*KindRun
+ Groups []KindRun
}
-// Spots are sorted by path for the reduction into FileRuns.
-func lessSpot(x, y interface{}) bool { return x.(Spot).File.Path < y.(Spot).File.Path }
+// 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 [i, j) in h.
-func newFileRun(h0 *RunList, i, j int) interface{} {
- file := h0.At(i).(Spot).File
+// 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
- var h1 RunList
- h1.Vector.Resize(j-i, 0)
- k := 0
- for ; i < j; i++ {
- h1.Set(k, h0.At(i).(Spot).Info)
- k++
+ 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, h2.Len())
- for i := 0; i < h2.Len(); i++ {
- groups[i] = h2.At(i).(*KindRun)
+ groups := make([]KindRun, len(h2))
+ for i, x := range h2 {
+ groups[i] = x.(KindRun)
}
return &FileRun{file, groups}
}
@@ -275,28 +297,26 @@ func newFileRun(h0 *RunList, i, j int) interface{} {
// A PakRun describes a run of *FileRuns of a package.
type PakRun struct {
- Pak Pak
+ 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.Path < p.Files[j].File.Path }
+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)
+ return x.(*FileRun).File.Pak.less(y.(*FileRun).File.Pak)
}
-// newPakRun allocates a new PakRun from the *FileRun run [i, j) in h.
-func newPakRun(h *RunList, i, j int) interface{} {
- pak := h.At(i).(*FileRun).File.Pak
- files := make([]*FileRun, j-i)
- k := 0
- for ; i < j; i++ {
- files[k] = h.At(i).(*FileRun)
- k++
+// 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
@@ -310,9 +330,9 @@ func newPakRun(h *RunList, i, j int) interface{} {
type HitList []*PakRun
// PakRuns are sorted by package.
-func lessPakRun(x, y interface{}) bool { return x.(*PakRun).Pak.less(&y.(*PakRun).Pak) }
+func lessPakRun(x, y interface{}) bool { return x.(*PakRun).Pak.less(y.(*PakRun).Pak) }
-func reduce(h0 *RunList) HitList {
+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
@@ -320,28 +340,20 @@ func reduce(h0 *RunList) HitList {
// sort the list of PakRuns by package
h2.sort(lessPakRun)
// create a HitList
- h := make(HitList, h2.Len())
- for i := 0; i < h2.Len(); i++ {
- h[i] = h2.At(i).(*PakRun)
+ 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 {
- // determine number of matching packages (most of the time just one)
- n := 0
- for _, p := range h {
- if p.Pak.Name == pakname {
- n++
- }
- }
- // create filtered HitList
- hh := make(HitList, n)
- i := 0
+ var hh HitList
for _, p := range h {
if p.Pak.Name == pakname {
- hh[i] = p
- i++
+ hh = append(hh, p)
}
}
return hh
@@ -365,34 +377,27 @@ type AltWords struct {
// 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 [i, j) in h.
-func newAltWords(h *RunList, i, j int) interface{} {
- canon := h.At(i).(*wordPair).canon
- alts := make([]string, j-i)
- k := 0
- for ; i < j; i++ {
- alts[k] = h.At(i).(*wordPair).alt
- k++
+// 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 {
- if len(a.Alts) == 1 && a.Alts[0] == s {
- // there are no different alternatives
- return nil
- }
-
- // make a new AltWords with the current spelling removed
- alts := make([]string, len(a.Alts))
- i := 0
+ var alts []string
for _, w := range a.Alts {
if w != s {
- alts[i] = w
- i++
+ alts = append(alts, w)
}
}
- return &AltWords{a.Canon, alts[0:i]}
+ if len(alts) > 0 {
+ return &AltWords{a.Canon, alts}
+ }
+ return nil
}
// ----------------------------------------------------------------------------
@@ -423,17 +428,32 @@ type Statistics struct {
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 vector.Vector // vector of *Snippets, indexed by snippet indices
+ 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 := x.snippets.Len()
- x.snippets.Push(s)
+ index := len(x.snippets)
+ x.snippets = append(x.snippets, s)
return index
}
@@ -454,12 +474,12 @@ func (x *Indexer) visitIdent(kind SpotKind, id *ast.Ident) {
if kind == Use || x.decl == nil {
// not a declaration or no snippet required
info := makeSpotInfo(kind, x.current.Line(id.Pos()), false)
- lists.Others.Push(Spot{x.file, info})
+ 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.Push(Spot{x.file, info})
+ lists.Decls = append(lists.Decls, Spot{x.file, info})
}
x.stats.Spots++
@@ -675,7 +695,7 @@ var whitelisted = map[string]bool{
// of "permitted" files for indexing. The filename must
// be the directory-local name of the file.
func isWhitelisted(filename string) bool {
- key := filepath.Ext(filename)
+ key := pathpkg.Ext(filename)
if key == "" {
// file has no extension - use entire filename
key = filename
@@ -683,12 +703,12 @@ func isWhitelisted(filename string) bool {
return whitelisted[key]
}
-func (x *Indexer) visitFile(dirname string, f FileInfo, fulltextIndex bool) {
- if !f.IsRegular() {
+func (x *Indexer) visitFile(dirname string, f os.FileInfo, fulltextIndex bool) {
+ if f.IsDir() {
return
}
- filename := filepath.Join(dirname, f.Name())
+ filename := pathpkg.Join(dirname, f.Name())
goFile := false
switch {
@@ -713,9 +733,8 @@ func (x *Indexer) visitFile(dirname string, f FileInfo, fulltextIndex bool) {
if fast != nil {
// we've got a Go file to index
x.current = file
- dir, _ := filepath.Split(filename)
- pak := Pak{dir, fast.Name.Name}
- x.file = &File{filename, pak}
+ pak := x.lookupPackage(dirname, fast.Name.Name)
+ x.file = &File{f.Name(), pak}
ast.Walk(x, fast)
}
@@ -747,12 +766,15 @@ 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) *Index {
+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.words = make(map[string]*IndexResult)
+ 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 {
@@ -761,9 +783,10 @@ func NewIndex(dirnames <-chan string, fulltextIndex bool) *Index {
continue // ignore this directory
}
for _, f := range list {
- if !f.IsDirectory() {
+ if !f.IsDir() {
x.visitFile(dirname, f, fulltextIndex)
}
+ th.Throttle()
}
}
@@ -782,13 +805,14 @@ func NewIndex(dirnames <-chan string, fulltextIndex bool) *Index {
words := make(map[string]*LookupResult)
var wlist RunList
for w, h := range x.words {
- decls := reduce(&h.Decls)
- others := reduce(&h.Others)
+ decls := reduce(h.Decls)
+ others := reduce(h.Others)
words[w] = &LookupResult{
Decls: decls,
Others: others,
}
- wlist.Push(&wordPair{canonical(w), w})
+ wlist = append(wlist, &wordPair{canonical(w), w})
+ th.Throttle()
}
x.stats.Words = len(words)
@@ -798,24 +822,92 @@ func NewIndex(dirnames <-chan string, fulltextIndex bool) *Index {
// convert alist into a map of alternative spellings
alts := make(map[string]*AltWords)
- for i := 0; i < alist.Len(); i++ {
- a := alist.At(i).(*AltWords)
+ for i := 0; i < len(alist); i++ {
+ a := alist[i].(*AltWords)
alts[a.Canon] = a
}
- // convert snippet vector into a list
- snippets := make([]*Snippet, x.snippets.Len())
- for i := 0; i < x.snippets.Len(); i++ {
- snippets[i] = x.snippets.At(i).(*Snippet)
- }
-
// create text index
var suffixes *suffixarray.Index
if fulltextIndex {
suffixes = suffixarray.New(x.sources.Bytes())
}
- return &Index{x.fset, suffixes, words, alts, snippets, x.stats}
+ 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.
@@ -823,7 +915,7 @@ func (x *Index) Stats() Statistics {
return x.stats
}
-func (x *Index) LookupWord(w string) (match *LookupResult, alt *AltWords) {
+func (x *Index) lookupWord(w string) (match *LookupResult, alt *AltWords) {
match = x.words[w]
alt = x.alts[canonical(w)]
// remove current spelling from alternatives
@@ -835,47 +927,56 @@ func (x *Index) LookupWord(w string) (match *LookupResult, alt *AltWords) {
return
}
+// isIdentifier reports whether s is a Go identifier.
func isIdentifier(s string) bool {
- var S scanner.Scanner
- fset := token.NewFileSet()
- S.Init(fset.AddFile("", fset.Base(), len(s)), []byte(s), nil, 0)
- if _, tok, _ := S.Scan(); tok == token.IDENT {
- _, tok, _ := S.Scan()
- return tok == token.EOF
+ for i, ch := range s {
+ if unicode.IsLetter(ch) || ch == ' ' || i > 0 && unicode.IsDigit(ch) {
+ continue
+ }
+ return false
}
- return false
+ return len(s) > 0
}
// For a given query, which is either a single identifier or a qualified
-// identifier, Lookup returns a LookupResult, and a list of alternative
-// spellings, if any. If the query syntax is wrong, an error is reported.
-func (x *Index) Lookup(query string) (match *LookupResult, alt *AltWords, err os.Error) {
+// 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 = os.NewError("all query parts must be identifiers")
+ err = errors.New("all query parts must be identifiers")
return
}
}
+ // handle simple and qualified identifiers
switch len(ss) {
case 1:
- match, alt = x.LookupWord(ss[0])
+ 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 := ss[0]
- match, alt = x.LookupWord(ss[1])
+ 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 = os.NewError("query is not a (qualified) identifier")
+ err = errors.New("query is not a (qualified) identifier")
}
return
diff --git a/src/cmd/godoc/main.go b/src/cmd/godoc/main.go
index 89b12b9ac..da4fc63b5 100644
--- a/src/cmd/godoc/main.go
+++ b/src/cmd/godoc/main.go
@@ -7,7 +7,7 @@
// Web server tree:
//
// http://godoc/ main landing page
-// http://godoc/doc/ serve from $GOROOT/doc - spec, mem, tutorial, etc.
+// 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
@@ -28,23 +28,23 @@ package main
import (
"archive/zip"
"bytes"
+ "errors"
_ "expvar" // to serve /debug/vars
"flag"
"fmt"
"go/ast"
"go/build"
- "http"
- _ "http/pprof" // to serve /debug/pprof/*
"io"
"log"
+ "net/http"
+ _ "net/http/pprof" // to serve /debug/pprof/*
+ "net/url"
"os"
- "path"
+ pathpkg "path"
"path/filepath"
"regexp"
"runtime"
"strings"
- "time"
- "url"
)
const defaultAddr = ":6060" // default webserver address
@@ -54,10 +54,8 @@ var (
// (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")
- // periodic sync
- syncCmd = flag.String("sync", "", "sync command; disabled if empty")
- syncMin = flag.Int("sync_minutes", 0, "sync interval in minutes; disabled if <= 0")
- syncDelay delayTime // actual sync interval in minutes; usually syncDelay == syncMin, but syncDelay may back off exponentially
+ // 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+"')")
@@ -66,83 +64,16 @@ var (
// 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 os.Error) {
+func serveError(w http.ResponseWriter, r *http.Request, relpath string, err error) {
contents := applyTemplate(errorHTML, "errorHTML", err) // err may contain an absolute path!
w.WriteHeader(http.StatusNotFound)
- servePage(w, "File "+relpath, "", "", contents)
-}
-
-func exec(rw http.ResponseWriter, args []string) (status int) {
- r, w, err := os.Pipe()
- if err != nil {
- log.Printf("os.Pipe(): %v", err)
- return 2
- }
-
- bin := args[0]
- fds := []*os.File{nil, w, w}
- if *verbose {
- log.Printf("executing %v", args)
- }
- p, err := os.StartProcess(bin, args, &os.ProcAttr{Files: fds, Dir: *goroot})
- defer r.Close()
- w.Close()
- if err != nil {
- log.Printf("os.StartProcess(%q): %v", bin, err)
- return 2
- }
- defer p.Release()
-
- var buf bytes.Buffer
- io.Copy(&buf, r)
- wait, err := p.Wait(0)
- if err != nil {
- os.Stderr.Write(buf.Bytes())
- log.Printf("os.Wait(%d, 0): %v", p.Pid, err)
- return 2
- }
- status = wait.ExitStatus()
- if !wait.Exited() || status > 1 {
- os.Stderr.Write(buf.Bytes())
- log.Printf("executing %v failed (exit status = %d)", args, status)
- return
- }
-
- if *verbose {
- os.Stderr.Write(buf.Bytes())
- }
- if rw != nil {
- rw.Header().Set("Content-Type", "text/plain; charset=utf-8")
- rw.Write(buf.Bytes())
- }
-
- return
-}
-
-func dosync(w http.ResponseWriter, r *http.Request) {
- args := []string{"/bin/sh", "-c", *syncCmd}
- switch exec(w, args) {
- case 0:
- // sync succeeded and some files have changed;
- // update package tree.
- // TODO(gri): The directory tree may be temporarily out-of-sync.
- // Consider keeping separate time stamps so the web-
- // page can indicate this discrepancy.
- initFSTree()
- fallthrough
- case 1:
- // sync failed because no files changed;
- // don't change the package tree
- syncDelay.set(*syncMin) // revert to regular sync schedule
- default:
- // sync failed because of an error - back off exponentially, but try at least once a day
- syncDelay.backoff(24 * 60)
- }
+ servePage(w, relpath, "File "+relpath, "", "", contents)
}
func usage() {
@@ -160,9 +91,7 @@ func loggingHandler(h http.Handler) http.Handler {
})
}
-func remoteSearch(query string) (res *http.Response, err os.Error) {
- search := "/search?f=text&q=" + url.QueryEscape(query)
-
+func remoteSearch(query string) (res *http.Response, err error) {
// list of addresses to try
var addrs []string
if *serverAddr != "" {
@@ -176,6 +105,7 @@ func remoteSearch(query string) (res *http.Response, err os.Error) {
}
// remote search
+ search := remoteSearchURL(query, *html)
for _, addr := range addrs {
url := "http://" + addr + search
res, err = http.Get(url)
@@ -185,7 +115,7 @@ func remoteSearch(query string) (res *http.Response, err os.Error) {
}
if err == nil && res.StatusCode != http.StatusOK {
- err = os.NewError(res.Status)
+ err = errors.New(res.Status)
}
return
@@ -221,8 +151,8 @@ func main() {
flag.Usage = usage
flag.Parse()
- // Check usage: either server and no args, or command line and args
- if (*httpAddr != "") != (flag.NArg() == 0) {
+ // Check usage: either server and no args, command line and args, or index creation mode
+ if (*httpAddr != "" || *urlFlag != "") != (flag.NArg() == 0) && !*writeIndex {
usage()
}
@@ -236,22 +166,93 @@ func main() {
// same is true for the http handlers in initHandlers.
if *zipfile == "" {
// use file system of underlying OS
- *goroot = filepath.Clean(*goroot) // normalize path separator
- fs = OS
- fsHttp = http.Dir(*goroot)
+ 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)
}
- *goroot = path.Join("/", *goroot) // fsHttp paths are relative to '/'
- fs = NewZipFS(rc)
- fsHttp = NewHttpZipFS(rc, *goroot)
+ 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)
}
- initHandlers()
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)
+ // 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.
@@ -270,41 +271,26 @@ func main() {
default:
log.Print("identifier search index enabled")
}
- if !fsMap.IsEmpty() {
- log.Print("user-defined mapping:")
- fsMap.Fprint(os.Stderr)
- }
+ fs.Fprint(os.Stderr)
handler = loggingHandler(handler)
}
registerPublicHandlers(http.DefaultServeMux)
- if *syncCmd != "" {
- http.Handle("/debug/sync", http.HandlerFunc(dosync))
- }
+
+ // Playground handlers are not available in local godoc.
+ http.HandleFunc("/compile", disabledHandler)
+ http.HandleFunc("/share", disabledHandler)
// Initialize default directory tree with corresponding timestamp.
// (Do it in a goroutine so that launch is quick.)
go initFSTree()
- // Initialize directory trees for user-defined file systems (-path flag).
- initDirTrees()
-
- // Start sync goroutine, if enabled.
- if *syncCmd != "" && *syncMin > 0 {
- syncDelay.set(*syncMin) // initial sync delay
- go func() {
- for {
- dosync(nil, nil)
- delay, _ := syncDelay.get()
- if *verbose {
- log.Printf("next sync in %dmin", delay.(int))
- }
- time.Sleep(int64(delay.(int)) * 60e9)
- }
- }()
- }
+ // Immediately update metadata.
+ updateMetadata()
+ // Periodically refresh metadata.
+ go refreshMetadataLoop()
- // Start indexing goroutine.
+ // Initialize search index.
if *indexEnabled {
go indexer()
}
@@ -335,52 +321,92 @@ func main() {
return
}
- // determine paths
+ // 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)
- if len(path) > 0 && path[0] == '.' {
- // assume cwd; don't assume -goroot
+ 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)
- }
- relpath := path
- abspath := path
- if t, pkg, err := build.FindTree(path); err == nil {
- relpath = pkg
- abspath = filepath.Join(t.SrcDir(), pkg)
- } else if !filepath.IsAbs(path) {
- abspath = absolutePath(path, pkgHandler.fsRoot)
+ fs.Bind(target, OS(path), "/", bindReplace)
+ abspath = target
+ } else if strings.HasPrefix(path, cmdPrefix) {
+ path = path[len(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 {
- relpath = relativeURL(path)
+ 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 |= exportsOnly
+ if flag.NArg() > 1 {
+ mode |= noFiltering
}
- } else {
- mode = exportsOnly | genDoc
+ mode |= showSource
}
// TODO(gri): Provide a mechanism (flag?) to select a package
// if there are multiple packages in a directory.
- info := pkgHandler.getPageInfo(abspath, relpath, "", mode)
+ // 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.IsEmpty() {
- // try again, this time assume it's a command
- if !filepath.IsAbs(path) {
- abspath = absolutePath(path, cmdHandler.fsRoot)
+ if !cinfo.IsEmpty() {
+ // only cinfo exists - switch to cinfo
+ info = cinfo
}
- cmdInfo := cmdHandler.getPageInfo(abspath, relpath, "", mode)
- // only use the cmdInfo if it actually contains a result
- // (don't hide errors reported from looking up a package)
- if !cmdInfo.IsEmpty() {
- info = cmdInfo
+ } else if !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.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 {
@@ -421,3 +447,19 @@ func main() {
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 }
+
+// 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/mapping.go b/src/cmd/godoc/mapping.go
deleted file mode 100644
index 51f23ab98..000000000
--- a/src/cmd/godoc/mapping.go
+++ /dev/null
@@ -1,200 +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 implements the Mapping data structure.
-
-package main
-
-import (
- "fmt"
- "io"
- "path"
- "path/filepath"
- "sort"
- "strings"
-)
-
-// A Mapping object maps relative paths (e.g. from URLs)
-// to absolute paths (of the file system) and vice versa.
-//
-// A Mapping object consists of a list of individual mappings
-// of the form: prefix -> path which are interpreted as follows:
-// A relative path of the form prefix/tail is to be mapped to
-// the absolute path/tail, if that absolute path exists in the file
-// system. Given a Mapping object, a relative path is mapped to an
-// absolute path by trying each of the individual mappings in order,
-// until a valid mapping is found. For instance, for the mapping:
-//
-// user -> /home/user
-// public -> /home/user/public
-// public -> /home/build/public
-//
-// the relative paths below are mapped to absolute paths as follows:
-//
-// user/foo -> /home/user/foo
-// public/net/rpc/file1.go -> /home/user/public/net/rpc/file1.go
-//
-// If there is no /home/user/public/net/rpc/file2.go, the next public
-// mapping entry is used to map the relative path to:
-//
-// public/net/rpc/file2.go -> /home/build/public/net/rpc/file2.go
-//
-// (assuming that file exists).
-//
-// Each individual mapping also has a RWValue associated with it that
-// may be used to store mapping-specific information. See the Iterate
-// method.
-//
-type Mapping struct {
- list []mapping
- prefixes []string // lazily computed from list
-}
-
-type mapping struct {
- prefix, path string
- value *RWValue
-}
-
-// Init initializes the Mapping from a list of paths.
-// Empty paths are ignored; relative paths are assumed to be relative to
-// the current working directory and converted to absolute paths.
-// For each path of the form:
-//
-// dirname/localname
-//
-// a mapping
-//
-// localname -> path
-//
-// is added to the Mapping object, in the order of occurrence.
-// For instance, under Unix, the argument:
-//
-// /home/user:/home/build/public
-//
-// leads to the following mapping:
-//
-// user -> /home/user
-// public -> /home/build/public
-//
-func (m *Mapping) Init(paths []string) {
- pathlist := canonicalizePaths(paths, nil)
- list := make([]mapping, len(pathlist))
-
- // create mapping list
- for i, path := range pathlist {
- _, prefix := filepath.Split(path)
- list[i] = mapping{prefix, path, new(RWValue)}
- }
-
- m.list = list
-}
-
-// IsEmpty returns true if there are no mappings specified.
-func (m *Mapping) IsEmpty() bool { return len(m.list) == 0 }
-
-// PrefixList returns a list of all prefixes, with duplicates removed.
-// For instance, for the mapping:
-//
-// user -> /home/user
-// public -> /home/user/public
-// public -> /home/build/public
-//
-// the prefix list is:
-//
-// user, public
-//
-func (m *Mapping) PrefixList() []string {
- // compute the list lazily
- if m.prefixes == nil {
- list := make([]string, len(m.list))
-
- // populate list
- for i, e := range m.list {
- list[i] = e.prefix
- }
-
- // sort the list and remove duplicate entries
- sort.Strings(list)
- i := 0
- prev := ""
- for _, path := range list {
- if path != prev {
- list[i] = path
- i++
- prev = path
- }
- }
-
- m.prefixes = list[0:i]
- }
-
- return m.prefixes
-}
-
-// Fprint prints the mapping.
-func (m *Mapping) Fprint(w io.Writer) {
- for _, e := range m.list {
- fmt.Fprintf(w, "\t%s -> %s\n", e.prefix, e.path)
- }
-}
-
-func splitFirst(path string) (head, tail string) {
- i := strings.Index(path, string(filepath.Separator))
- if i > 0 {
- // 0 < i < len(path)
- return path[0:i], path[i+1:]
- }
- return "", path
-}
-
-// ToAbsolute maps a slash-separated relative path to an absolute filesystem
-// path using the Mapping specified by the receiver. If the path cannot
-// be mapped, the empty string is returned.
-//
-func (m *Mapping) ToAbsolute(spath string) string {
- fpath := filepath.FromSlash(spath)
- prefix, tail := splitFirst(fpath)
- for _, e := range m.list {
- switch {
- case e.prefix == prefix:
- // use tail
- case e.prefix == "":
- tail = fpath
- default:
- continue // no match
- }
- abspath := filepath.Join(e.path, tail)
- if _, err := fs.Stat(abspath); err == nil {
- return abspath
- }
- }
-
- return "" // no match
-}
-
-// ToRelative maps an absolute filesystem path to a relative slash-separated
-// path using the Mapping specified by the receiver. If the path cannot
-// be mapped, the empty string is returned.
-//
-func (m *Mapping) ToRelative(fpath string) string {
- for _, e := range m.list {
- if strings.HasPrefix(fpath, e.path) {
- spath := filepath.ToSlash(fpath)
- // /absolute/prefix/foo -> prefix/foo
- return path.Join(e.prefix, spath[len(e.path):]) // Join will remove a trailing '/'
- }
- }
- return "" // no match
-}
-
-// Iterate calls f for each path and RWValue in the mapping (in uspecified order)
-// until f returns false.
-//
-func (m *Mapping) Iterate(f func(path string, value *RWValue) bool) {
- for _, e := range m.list {
- if !f(e.path, e.value) {
- return
- }
- }
-}
diff --git a/src/cmd/godoc/parser.go b/src/cmd/godoc/parser.go
index da4b3853c..c6b7c2dc8 100644
--- a/src/cmd/godoc/parser.go
+++ b/src/cmd/godoc/parser.go
@@ -14,21 +14,21 @@ import (
"go/parser"
"go/token"
"os"
- "path/filepath"
+ pathpkg "path"
)
-func parseFiles(fset *token.FileSet, filenames []string) (pkgs map[string]*ast.Package, first os.Error) {
+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, filenames []string) (pkgs map[string]*ast.Package, first error) {
pkgs = make(map[string]*ast.Package)
for _, filename := range filenames {
- src, err := fs.ReadFile(filename)
- if err != nil {
- if first == nil {
- first = err
- }
- continue
- }
-
- file, err := parser.ParseFile(fset, filename, src, parser.ParseComments)
+ file, err := parseFile(fset, filename, parser.ParseComments)
if err != nil {
if first == nil {
first = err
@@ -40,7 +40,7 @@ func parseFiles(fset *token.FileSet, filenames []string) (pkgs map[string]*ast.P
pkg, found := pkgs[name]
if !found {
// TODO(gri) Use NewPackage here; reconsider ParseFiles API.
- pkg = &ast.Package{name, nil, nil, make(map[string]*ast.File)}
+ pkg = &ast.Package{Name: name, Files: make(map[string]*ast.File)}
pkgs[name] = pkg
}
pkg.Files[filename] = file
@@ -48,7 +48,7 @@ func parseFiles(fset *token.FileSet, filenames []string) (pkgs map[string]*ast.P
return
}
-func parseDir(fset *token.FileSet, path string, filter func(FileInfo) bool) (map[string]*ast.Package, os.Error) {
+func parseDir(fset *token.FileSet, path string, filter func(os.FileInfo) bool) (map[string]*ast.Package, error) {
list, err := fs.ReadDir(path)
if err != nil {
return nil, err
@@ -58,7 +58,7 @@ func parseDir(fset *token.FileSet, path string, filter func(FileInfo) bool) (map
i := 0
for _, d := range list {
if filter == nil || filter(d) {
- filenames[i] = filepath.Join(path, d.Name())
+ filenames[i] = pathpkg.Join(path, d.Name())
i++
}
}
diff --git a/src/cmd/godoc/setup-godoc-app.bash b/src/cmd/godoc/setup-godoc-app.bash
new file mode 100644
index 000000000..755d965d5
--- /dev/null
+++ b/src/cmd/godoc/setup-godoc-app.bash
@@ -0,0 +1,121 @@
+#!/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 the .zip, index, and configuration files for running
+# godoc on app-engine.
+#
+# 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, the
+# current working directory is assumed to be $GOROOT. Various sanity checks
+# prevent accidents.
+#
+# 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=$(pwd)
+ echo "GOROOT not set, using cwd 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/src/cmd/godoc/godoc ]; then
+ error "$GOROOT/src/cmd/godoc/godoc does not exist or is not executable"
+ fi
+ if [ ! -d $APPDIR ]; then
+ error "$APPDIR is not a directory"
+ fi
+ if [ ! -e $APPDIR/app.yaml ]; then
+ error "$APPDIR is not an app-engine directory; missing file app.yaml"
+ fi
+ if [ ! -d $APPDIR/godoc ]; then
+ error "$APPDIR is missing directory godoc"
+ fi
+
+ # reporting
+ echo "GOROOT = $GOROOT"
+ echo "APPDIR = $APPDIR"
+}
+
+cleanup() {
+ echo "*** cleanup $APPDIR"
+ rm $APPDIR/$ZIPFILE
+ rm $APPDIR/$INDEXFILE
+ rm $APPDIR/$SPLITFILES*
+ rm $APPDIR/$CONFIGFILE
+}
+
+makeZipfile() {
+ echo "*** make $APPDIR/$ZIPFILE"
+ zip -q -r $APPDIR/$ZIPFILE $GOROOT -i \*.go -i \*.html -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/src/cmd/godoc/godoc -write_index -index_files=$APPDIR/$INDEXFILE -zip=$APPDIR/$ZIPFILE 2> $OUT
+ if [ $? != 0 ]; then
+ error "$GOROOT/src/cmd/godoc/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 "$@"
+cleanup
+makeZipfile
+makeIndexfile
+splitIndexfile
+makeConfigfile
+
+echo "*** setup complete"
diff --git a/src/cmd/godoc/snippet.go b/src/cmd/godoc/snippet.go
index 68e27d9a0..b482b7487 100755..100644
--- a/src/cmd/godoc/snippet.go
+++ b/src/cmd/godoc/snippet.go
@@ -11,9 +11,9 @@ package main
import (
"bytes"
+ "fmt"
"go/ast"
"go/token"
- "fmt"
)
type Snippet struct {
@@ -62,7 +62,14 @@ func genSnippet(fset *token.FileSet, d *ast.GenDecl, id *ast.Ident) *Snippet {
}
// only use the spec containing the id for the snippet
- dd := &ast.GenDecl{d.Doc, d.Pos(), d.Tok, d.Lparen, []ast.Spec{s}, d.Rparen}
+ 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)
}
@@ -73,7 +80,12 @@ func funcSnippet(fset *token.FileSet, d *ast.FuncDecl, id *ast.Ident) *Snippet {
}
// only use the function signature for the snippet
- dd := &ast.FuncDecl{d.Doc, d.Recv, d.Name, d.Type, nil}
+ dd := &ast.FuncDecl{
+ Doc: d.Doc,
+ Recv: d.Recv,
+ Name: d.Name,
+ Type: d.Type,
+ }
return newSnippet(fset, dd, id)
}
diff --git a/src/cmd/godoc/spec.go b/src/cmd/godoc/spec.go
index 3f69add86..c11f25d20 100644
--- a/src/cmd/godoc/spec.go
+++ b/src/cmd/godoc/spec.go
@@ -2,118 +2,103 @@
// 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.
-package main
-
import (
"bytes"
"fmt"
- "go/scanner"
- "go/token"
"io"
+ "text/scanner"
)
type ebnfParser struct {
- out io.Writer // parser output
- src []byte // parser source
- file *token.File // for position information
+ out io.Writer // parser output
+ src []byte // parser input
scanner scanner.Scanner
- prev int // offset of previous token
- pos token.Pos // token position
- tok token.Token // one token look-ahead
- lit string // token literal
+ 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() {
- offs := p.file.Offset(p.pos)
- p.out.Write(p.src[p.prev:offs])
- p.prev = offs
+ p.out.Write(p.src[p.prev:p.pos])
+ p.prev = p.pos
}
func (p *ebnfParser) next() {
- if p.pos.IsValid() {
- p.flush()
- }
- p.pos, p.tok, p.lit = p.scanner.Scan()
- if p.tok.IsKeyword() {
- // TODO Should keyword mapping always happen outside scanner?
- // Or should there be a flag to scanner to enable keyword mapping?
- p.tok = token.IDENT
- }
+ p.tok = p.scanner.Scan()
+ p.pos = p.scanner.Position.Offset
+ p.lit = p.scanner.TokenText()
}
-func (p *ebnfParser) Error(pos token.Position, msg string) {
- fmt.Fprintf(p.out, `<span class="alert">error: %s</span>`, msg)
+func (p *ebnfParser) printf(format string, args ...interface{}) {
+ p.flush()
+ fmt.Fprintf(p.out, format, args...)
}
-func (p *ebnfParser) errorExpected(pos token.Pos, msg string) {
- msg = "expected " + msg
- if pos == p.pos {
- // the error happened at the current position;
- // make the error message more specific
- msg += ", found '" + p.tok.String() + "'"
- if p.tok.IsLiteral() {
- msg += " " + p.lit
- }
- }
- p.Error(p.file.Position(pos), msg)
+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 token.Token) token.Pos {
- pos := p.pos
+func (p *ebnfParser) expect(tok rune) {
if p.tok != tok {
- p.errorExpected(pos, "'"+tok.String()+"'")
+ p.errorExpected(scanner.TokenString(tok))
}
p.next() // make progress in any case
- return pos
}
func (p *ebnfParser) parseIdentifier(def bool) {
- name := p.lit
- p.expect(token.IDENT)
- if def {
- fmt.Fprintf(p.out, `<a id="%s">%s</a>`, name, name)
+ 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 {
- fmt.Fprintf(p.out, `<a href="#%s" class="noline">%s</a>`, name, name)
+ p.expect(scanner.Ident)
}
- p.prev += len(name) // skip identifier when calling flush
}
func (p *ebnfParser) parseTerm() bool {
switch p.tok {
- case token.IDENT:
+ case scanner.Ident:
p.parseIdentifier(false)
- case token.STRING:
+ case scanner.String:
p.next()
- const ellipsis = "…" // U+2026, the horizontal ellipsis character
- if p.tok == token.ILLEGAL && p.lit == ellipsis {
+ const ellipsis = '…' // U+2026, the horizontal ellipsis character
+ if p.tok == ellipsis {
p.next()
- p.expect(token.STRING)
+ p.expect(scanner.String)
}
- case token.LPAREN:
+ case '(':
p.next()
p.parseExpression()
- p.expect(token.RPAREN)
+ p.expect(')')
- case token.LBRACK:
+ case '[':
p.next()
p.parseExpression()
- p.expect(token.RBRACK)
+ p.expect(']')
- case token.LBRACE:
+ case '{':
p.next()
p.parseExpression()
- p.expect(token.RBRACE)
+ p.expect('}')
default:
- return false
+ return false // no term found
}
return true
@@ -121,7 +106,7 @@ func (p *ebnfParser) parseTerm() bool {
func (p *ebnfParser) parseSequence() {
if !p.parseTerm() {
- p.errorExpected(p.pos, "term")
+ p.errorExpected("term")
}
for p.parseTerm() {
}
@@ -130,7 +115,7 @@ func (p *ebnfParser) parseSequence() {
func (p *ebnfParser) parseExpression() {
for {
p.parseSequence()
- if p.tok != token.OR {
+ if p.tok != '|' {
break
}
p.next()
@@ -139,23 +124,22 @@ func (p *ebnfParser) parseExpression() {
func (p *ebnfParser) parseProduction() {
p.parseIdentifier(true)
- p.expect(token.ASSIGN)
- if p.tok != token.PERIOD {
+ p.expect('=')
+ if p.tok != '.' {
p.parseExpression()
}
- p.expect(token.PERIOD)
+ p.expect('.')
}
-func (p *ebnfParser) parse(fset *token.FileSet, out io.Writer, src []byte) {
+func (p *ebnfParser) parse(out io.Writer, src []byte) {
// initialize ebnfParser
p.out = out
p.src = src
- p.file = fset.AddFile("", fset.Base(), len(src))
- p.scanner.Init(p.file, src, p, scanner.AllowIllegalChars)
+ p.scanner.Init(bytes.NewBuffer(src))
p.next() // initializes pos, tok, lit
// process source
- for p.tok != token.EOF {
+ for p.tok != scanner.EOF {
p.parseProduction()
}
p.flush()
@@ -167,32 +151,29 @@ var (
closeTag = []byte(`</pre>`)
)
-func linkify(out io.Writer, src []byte) {
- fset := token.NewFileSet()
+func Linkify(out io.Writer, src []byte) {
for len(src) > 0 {
- n := len(src)
-
// i: beginning of EBNF text (or end of source)
i := bytes.Index(src, openTag)
if i < 0 {
- i = n - len(openTag)
+ i = len(src) - len(openTag)
}
i += len(openTag)
// j: end of EBNF text (or end of source)
- j := bytes.Index(src[i:n], closeTag) // close marker
+ j := bytes.Index(src[i:], closeTag) // close marker
if j < 0 {
- j = n - i
+ j = len(src) - i
}
j += i
// write text before EBNF
out.Write(src[0:i])
- // parse and write EBNF
+ // process EBNF
var p ebnfParser
- p.parse(fset, out, src[i:j])
+ p.parse(out, src[i:j])
// advance
- src = src[j:n]
+ src = src[j:]
}
}
diff --git a/src/cmd/godoc/template.go b/src/cmd/godoc/template.go
new file mode 100644
index 000000000..d709baef4
--- /dev/null
+++ b/src/cmd/godoc/template.go
@@ -0,0 +1,182 @@
+// 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)
+}
+
+// format returns a textual representation of the arg, formatted according to its nature.
+func format(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, format(arg[0]))
+ text = oneLine(file, text, arg[0])
+ case 2:
+ command = fmt.Sprintf("code %q %s %s", file, format(arg[0]), format(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
new file mode 100644
index 000000000..ac18b44e0
--- /dev/null
+++ b/src/cmd/godoc/throttle.go
@@ -0,0 +1,88 @@
+// 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
index 11e46aee5..7def015c8 100644
--- a/src/cmd/godoc/utils.go
+++ b/src/cmd/godoc/utils.go
@@ -7,15 +7,10 @@
package main
import (
- "io"
- "io/ioutil"
- "os"
- "path/filepath"
- "sort"
- "strings"
+ pathpkg "path"
"sync"
"time"
- "utf8"
+ "unicode/utf8"
)
// An RWValue wraps a value and permits mutually exclusive
@@ -24,92 +19,22 @@ import (
type RWValue struct {
mutex sync.RWMutex
value interface{}
- timestamp int64 // time of last set(), in seconds since epoch
+ timestamp time.Time // time of last set()
}
func (v *RWValue) set(value interface{}) {
v.mutex.Lock()
v.value = value
- v.timestamp = time.Seconds()
+ v.timestamp = time.Now()
v.mutex.Unlock()
}
-func (v *RWValue) get() (interface{}, int64) {
+func (v *RWValue) get() (interface{}, time.Time) {
v.mutex.RLock()
defer v.mutex.RUnlock()
return v.value, v.timestamp
}
-// TODO(gri) For now, using os.Getwd() is ok here since the functionality
-// based on this code is not invoked for the appengine version,
-// but this is fragile. Determine what the right thing to do is,
-// here (possibly have some Getwd-equivalent in FileSystem).
-var cwd, _ = os.Getwd() // ignore errors
-
-// canonicalizePaths takes a list of (directory/file) paths and returns
-// the list of corresponding absolute paths in sorted (increasing) order.
-// Relative paths are assumed to be relative to the current directory,
-// empty and duplicate paths as well as paths for which filter(path) is
-// false are discarded. filter may be nil in which case it is not used.
-//
-func canonicalizePaths(list []string, filter func(path string) bool) []string {
- i := 0
- for _, path := range list {
- path = strings.TrimSpace(path)
- if len(path) == 0 {
- continue // ignore empty paths (don't assume ".")
- }
- // len(path) > 0: normalize path
- if filepath.IsAbs(path) {
- path = filepath.Clean(path)
- } else {
- path = filepath.Join(cwd, path)
- }
- // we have a non-empty absolute path
- if filter != nil && !filter(path) {
- continue
- }
- // keep the path
- list[i] = path
- i++
- }
- list = list[0:i]
-
- // sort the list and remove duplicate entries
- sort.Strings(list)
- i = 0
- prev := ""
- for _, path := range list {
- if path != prev {
- list[i] = path
- i++
- prev = path
- }
- }
-
- return list[0:i]
-}
-
-// writeFileAtomically writes data to a temporary file and then
-// atomically renames that file to the file named by filename.
-//
-func writeFileAtomically(filename string, data []byte) os.Error {
- // TODO(gri) this won't work on appengine
- f, err := ioutil.TempFile(filepath.Split(filename))
- if err != nil {
- return err
- }
- n, err := f.Write(data)
- f.Close()
- if err != nil {
- return err
- }
- if n < len(data) {
- return io.ErrShortWrite
- }
- return os.Rename(f.Name(), filename)
-}
-
// 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.
//
@@ -146,7 +71,7 @@ var textExt = map[string]bool{
//
func isTextFile(filename string) bool {
// if the extension is known, use it for decision making
- if isText, found := textExt[filepath.Ext(filename)]; found {
+ if isText, found := textExt[pathpkg.Ext(filename)]; found {
return isText
}
diff --git a/src/cmd/godoc/zip.go b/src/cmd/godoc/zip.go
index 27dc142f5..620eb4f3c 100644
--- a/src/cmd/godoc/zip.go
+++ b/src/cmd/godoc/zip.go
@@ -22,11 +22,11 @@ import (
"archive/zip"
"fmt"
"io"
- "io/ioutil"
"os"
"path"
"sort"
"strings"
+ "time"
)
// zipFI is the zip-file based implementation of FileInfo
@@ -46,28 +46,41 @@ func (fi zipFI) Size() int64 {
return 0 // directory
}
-func (fi zipFI) Mtime_ns() int64 {
+func (fi zipFI) ModTime() time.Time {
if f := fi.file; f != nil {
- return f.Mtime_ns()
+ return f.ModTime()
}
- return 0 // directory has no modified time entry
+ return time.Time{} // directory has no modified time entry
}
-func (fi zipFI) IsDirectory() bool {
+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) IsRegular() 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() os.Error {
+func (fs *zipFS) Close() error {
fs.list = nil
return fs.ReadCloser.Close()
}
@@ -80,7 +93,7 @@ func zipPath(name string) string {
return name[1:] // strip leading '/'
}
-func (fs *zipFS) stat(abspath string) (int, zipFI, os.Error) {
+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
@@ -94,38 +107,60 @@ func (fs *zipFS) stat(abspath string) (int, zipFI, os.Error) {
return i, zipFI{name, file}, nil
}
-func (fs *zipFS) Open(abspath string) (io.ReadCloser, os.Error) {
+func (fs *zipFS) Open(abspath string) (readSeekCloser, error) {
_, fi, err := fs.stat(zipPath(abspath))
if err != nil {
return nil, err
}
- if fi.IsDirectory() {
+ if fi.IsDir() {
return nil, fmt.Errorf("Open: %s is a directory", abspath)
}
- return fi.file.Open()
+ 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) (FileInfo, os.Error) {
+func (fs *zipFS) Lstat(abspath string) (os.FileInfo, error) {
_, fi, err := fs.stat(zipPath(abspath))
return fi, err
}
-func (fs *zipFS) Stat(abspath string) (FileInfo, os.Error) {
+func (fs *zipFS) Stat(abspath string) (os.FileInfo, error) {
_, fi, err := fs.stat(zipPath(abspath))
return fi, err
}
-func (fs *zipFS) ReadDir(abspath string) ([]FileInfo, os.Error) {
+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.IsDirectory() {
+ if !fi.IsDir() {
return nil, fmt.Errorf("ReadDir: %s is not a directory", abspath)
}
- var list []FileInfo
+ var list []os.FileInfo
dirname := path + "/"
prevname := ""
for _, e := range fs.list[i:] {
@@ -153,19 +188,11 @@ func (fs *zipFS) ReadDir(abspath string) ([]FileInfo, os.Error) {
return list, nil
}
-func (fs *zipFS) ReadFile(abspath string) ([]byte, os.Error) {
- rc, err := fs.Open(abspath)
- if err != nil {
- return nil, err
- }
- return ioutil.ReadAll(rc)
-}
-
-func NewZipFS(rc *zip.ReadCloser) FileSystem {
+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}
+ return &zipFS{rc, list, name}
}
type zipList []*zip.File
@@ -183,9 +210,10 @@ func (z zipList) lookup(name string) (index int, exact bool) {
i := sort.Search(len(z), func(i int) bool {
return name <= z[i].Name
})
- if i < 0 {
+ if i >= len(z) {
return -1, false
}
+ // 0 <= i < len(z)
if z[i].Name == name {
return i, true
}
@@ -196,9 +224,10 @@ func (z zipList) lookup(name string) (index int, exact bool) {
j := sort.Search(len(z), func(i int) bool {
return name <= z[i].Name
})
- if j < 0 {
+ if j >= len(z) {
return -1, false
}
+ // 0 <= j < len(z)
if strings.HasPrefix(z[j].Name, name) {
return i + j, false
}
diff --git a/src/cmd/gofix/Makefile b/src/cmd/gofix/Makefile
deleted file mode 100644
index 22033d7f8..000000000
--- a/src/cmd/gofix/Makefile
+++ /dev/null
@@ -1,34 +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.
-
-include ../../Make.inc
-
-TARG=gofix
-GOFILES=\
- filepath.go\
- fix.go\
- httpfinalurl.go\
- httpfs.go\
- httpheaders.go\
- httpserver.go\
- main.go\
- netdial.go\
- oserrorstring.go\
- osopen.go\
- procattr.go\
- reflect.go\
- signal.go\
- sorthelpers.go\
- sortslice.go\
- stringssplit.go\
- typecheck.go\
- url.go\
-
-include ../../Make.cmd
-
-test:
- gotest
-
-testshort:
- gotest -test.short
diff --git a/src/cmd/gofix/doc.go b/src/cmd/gofix/doc.go
deleted file mode 100644
index a9790e685..000000000
--- a/src/cmd/gofix/doc.go
+++ /dev/null
@@ -1,36 +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.
-
-/*
-Gofix finds Go programs that use old APIs and rewrites them to use
-newer ones. After you update to a new Go release, gofix helps make
-the necessary changes to your programs.
-
-Usage:
- gofix [-r name,...] [path ...]
-
-Without an explicit path, gofix reads standard input and writes the
-result to standard output.
-
-If the named path is a file, gofix rewrites the named files in place.
-If the named path is a directory, gofix rewrites all .go files in that
-directory tree. When gofix rewrites a file, it prints a line to standard
-error giving the name of the file and the rewrite applied.
-
-If the -diff flag is set, no files are rewritten. Instead gofix prints
-the differences a rewrite would introduce.
-
-The -r flag restricts the set of rewrites considered to those in the
-named list. By default gofix considers all known rewrites. Gofix's
-rewrites are idempotent, so that it is safe to apply gofix to updated
-or partially updated code even without using the -r flag.
-
-Gofix prints the full list of fixes it can apply in its help output;
-to see them, run gofix -?.
-
-Gofix does not make backup copies of the files that it edits.
-Instead, use a version control system's ``diff'' functionality to inspect
-the changes that gofix makes before committing them.
-*/
-package documentation
diff --git a/src/cmd/gofmt/Makefile b/src/cmd/gofmt/Makefile
deleted file mode 100644
index dc5b060e6..000000000
--- a/src/cmd/gofmt/Makefile
+++ /dev/null
@@ -1,19 +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 ../../Make.inc
-
-TARG=gofmt
-GOFILES=\
- gofmt.go\
- rewrite.go\
- simplify.go\
-
-include ../../Make.cmd
-
-test: $(TARG)
- ./test.sh
-
-testshort:
- gotest -test.short
diff --git a/src/cmd/gofmt/doc.go b/src/cmd/gofmt/doc.go
index fca42b76b..65842a3b1 100644
--- a/src/cmd/gofmt/doc.go
+++ b/src/cmd/gofmt/doc.go
@@ -36,10 +36,8 @@ The flags are:
Formatting control flags:
-comments=true
Print comments; if false, all comments are elided from the output.
- -spaces
- Align with spaces instead of tabs.
- -tabindent
- Indent with tabs independent of -spaces.
+ -tabs=true
+ Indent with tabs; if false, spaces are used instead.
-tabwidth=8
Tab width in spaces.
@@ -53,6 +51,12 @@ In the pattern, single-character lowercase identifiers serve as
wildcards matching arbitrary sub-expressions; those expressions
will be substituted for the same identifiers in the replacement.
+When gofmt reads from standard input, it accepts either a full Go program
+or a program fragment. A program fragment must be a syntactically
+valid declaration list, statement list, or expression. When formatting
+such a fragment, gofmt preserves leading indentation as well as leading
+and trailing spaces, so that individual sections of a Go program can be
+formatted by piping them through gofmt.
Examples
diff --git a/src/cmd/gofmt/gofmt.go b/src/cmd/gofmt/gofmt.go
index 975ae6ac6..0bc385b5b 100644
--- a/src/cmd/gofmt/gofmt.go
+++ b/src/cmd/gofmt/gofmt.go
@@ -6,7 +6,6 @@ package main
import (
"bytes"
- "exec"
"flag"
"fmt"
"go/ast"
@@ -17,6 +16,7 @@ import (
"io"
"io/ioutil"
"os"
+ "os/exec"
"path/filepath"
"runtime/pprof"
"strings"
@@ -26,7 +26,7 @@ var (
// main operation modes
list = flag.Bool("l", false, "list files whose formatting differs from gofmt's")
write = flag.Bool("w", false, "write result to (source) file instead of stdout")
- rewriteRule = flag.String("r", "", "rewrite rule (e.g., 'α[β:len(α)] -> α[β:]')")
+ rewriteRule = flag.String("r", "", "rewrite rule (e.g., 'a[b:len(a)] -> a[b:]')")
simplifyAST = flag.Bool("s", false, "simplify code")
doDiff = flag.Bool("d", false, "display diffs instead of rewriting files")
allErrors = flag.Bool("e", false, "print all (including spurious) errors")
@@ -34,22 +34,21 @@ var (
// layout control
comments = flag.Bool("comments", true, "print comments")
tabWidth = flag.Int("tabwidth", 8, "tab width")
- tabIndent = flag.Bool("tabindent", true, "indent with tabs independent of -spaces")
- useSpaces = flag.Bool("spaces", true, "align with spaces instead of tabs")
+ tabIndent = flag.Bool("tabs", true, "indent with tabs")
// debugging
cpuprofile = flag.String("cpuprofile", "", "write cpu profile to this file")
)
var (
- fset = token.NewFileSet()
+ fileSet = token.NewFileSet() // per process FileSet
exitCode = 0
rewrite func(*ast.File) *ast.File
- parserMode uint
- printerMode uint
+ parserMode parser.Mode
+ printerMode printer.Mode
)
-func report(err os.Error) {
+func report(err error) {
scanner.PrintError(os.Stderr, err)
exitCode = 2
}
@@ -61,7 +60,7 @@ func usage() {
}
func initParserMode() {
- parserMode = uint(0)
+ parserMode = parser.Mode(0)
if *comments {
parserMode |= parser.ParseComments
}
@@ -71,22 +70,20 @@ func initParserMode() {
}
func initPrinterMode() {
- printerMode = uint(0)
+ printerMode = printer.UseSpaces
if *tabIndent {
printerMode |= printer.TabIndent
}
- if *useSpaces {
- printerMode |= printer.UseSpaces
- }
}
-func isGoFile(f *os.FileInfo) bool {
+func isGoFile(f os.FileInfo) bool {
// ignore non-Go files
- return f.IsRegular() && !strings.HasPrefix(f.Name, ".") && strings.HasSuffix(f.Name, ".go")
+ name := f.Name()
+ return !f.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go")
}
// If in == nil, the source is the contents of the file with the given filename.
-func processFile(filename string, in io.Reader, out io.Writer) os.Error {
+func processFile(filename string, in io.Reader, out io.Writer, stdin bool) error {
if in == nil {
f, err := os.Open(filename)
if err != nil {
@@ -101,25 +98,34 @@ func processFile(filename string, in io.Reader, out io.Writer) os.Error {
return err
}
- file, err := parser.ParseFile(fset, filename, src, parserMode)
+ file, adjust, err := parse(fileSet, filename, src, stdin)
if err != nil {
return err
}
if rewrite != nil {
- file = rewrite(file)
+ if adjust == nil {
+ file = rewrite(file)
+ } else {
+ fmt.Fprintf(os.Stderr, "warning: rewrite ignored for incomplete programs\n")
+ }
}
+ ast.SortImports(fileSet, file)
+
if *simplifyAST {
simplify(file)
}
var buf bytes.Buffer
- _, err = (&printer.Config{printerMode, *tabWidth}).Fprint(&buf, fset, file)
+ err = (&printer.Config{Mode: printerMode, Tabwidth: *tabWidth}).Fprint(&buf, fileSet, file)
if err != nil {
return err
}
res := buf.Bytes()
+ if adjust != nil {
+ res = adjust(src, res)
+ }
if !bytes.Equal(src, res) {
// formatting has changed
@@ -149,32 +155,18 @@ func processFile(filename string, in io.Reader, out io.Writer) os.Error {
return err
}
-type fileVisitor chan os.Error
-
-func (v fileVisitor) VisitDir(path string, f *os.FileInfo) bool {
- return true
-}
-
-func (v fileVisitor) VisitFile(path string, f *os.FileInfo) {
- if isGoFile(f) {
- v <- nil // synchronize error handler
- if err := processFile(path, nil, os.Stdout); err != nil {
- v <- err
- }
+func visitFile(path string, f os.FileInfo, err error) error {
+ if err == nil && isGoFile(f) {
+ err = processFile(path, nil, os.Stdout, false)
+ }
+ if err != nil {
+ report(err)
}
+ return nil
}
func walkDir(path string) {
- v := make(fileVisitor)
- go func() {
- filepath.Walk(path, v, v)
- close(v)
- }()
- for err := range v {
- if err != nil {
- report(err)
- }
- }
+ filepath.Walk(path, visitFile)
}
func main() {
@@ -211,7 +203,7 @@ func gofmtMain() {
initRewrite()
if flag.NArg() == 0 {
- if err := processFile("<standard input>", os.Stdin, os.Stdout); err != nil {
+ if err := processFile("<standard input>", os.Stdin, os.Stdout, true); err != nil {
report(err)
}
return
@@ -222,17 +214,17 @@ func gofmtMain() {
switch dir, err := os.Stat(path); {
case err != nil:
report(err)
- case dir.IsRegular():
- if err := processFile(path, nil, os.Stdout); err != nil {
+ case dir.IsDir():
+ walkDir(path)
+ default:
+ if err := processFile(path, nil, os.Stdout, false); err != nil {
report(err)
}
- case dir.IsDirectory():
- walkDir(path)
}
}
}
-func diff(b1, b2 []byte) (data []byte, err os.Error) {
+func diff(b1, b2 []byte) (data []byte, err error) {
f1, err := ioutil.TempFile("", "gofmt")
if err != nil {
return
@@ -259,3 +251,111 @@ func diff(b1, b2 []byte) (data []byte, err os.Error) {
return
}
+
+// parse parses src, which was read from filename,
+// as a Go source file or statement list.
+func parse(fset *token.FileSet, filename string, src []byte, stdin bool) (*ast.File, func(orig, src []byte) []byte, error) {
+ // Try as whole source file.
+ file, err := parser.ParseFile(fset, filename, src, parserMode)
+ if err == nil {
+ return file, nil, nil
+ }
+ // If the error is that the source file didn't begin with a
+ // package line and this is standard input, fall through to
+ // try as a source fragment. Stop and return on any other error.
+ if !stdin || !strings.Contains(err.Error(), "expected 'package'") {
+ return nil, nil, err
+ }
+
+ // If this is a declaration list, make it a source file
+ // by inserting a package clause.
+ // Insert using a ;, not a newline, so that the line numbers
+ // in psrc match the ones in src.
+ psrc := append([]byte("package p;"), src...)
+ file, err = parser.ParseFile(fset, filename, psrc, parserMode)
+ if err == nil {
+ adjust := func(orig, src []byte) []byte {
+ // Remove the package clause.
+ // Gofmt has turned the ; into a \n.
+ src = src[len("package p\n"):]
+ return matchSpace(orig, src)
+ }
+ return file, adjust, nil
+ }
+ // If the error is that the source file didn't begin with a
+ // declaration, fall through to try as a statement list.
+ // Stop and return on any other error.
+ if !strings.Contains(err.Error(), "expected declaration") {
+ return nil, nil, err
+ }
+
+ // If this is a statement list, make it a source file
+ // by inserting a package clause and turning the list
+ // into a function body. This handles expressions too.
+ // Insert using a ;, not a newline, so that the line numbers
+ // in fsrc match the ones in src.
+ fsrc := append(append([]byte("package p; func _() {"), src...), '}')
+ file, err = parser.ParseFile(fset, filename, fsrc, parserMode)
+ if err == nil {
+ adjust := func(orig, src []byte) []byte {
+ // Remove the wrapping.
+ // Gofmt has turned the ; into a \n\n.
+ src = src[len("package p\n\nfunc _() {"):]
+ src = src[:len(src)-len("}\n")]
+ // Gofmt has also indented the function body one level.
+ // Remove that indent.
+ src = bytes.Replace(src, []byte("\n\t"), []byte("\n"), -1)
+ return matchSpace(orig, src)
+ }
+ return file, adjust, nil
+ }
+
+ // Failed, and out of options.
+ return nil, nil, err
+}
+
+func cutSpace(b []byte) (before, middle, after []byte) {
+ i := 0
+ for i < len(b) && (b[i] == ' ' || b[i] == '\t' || b[i] == '\n') {
+ i++
+ }
+ j := len(b)
+ for j > 0 && (b[j-1] == ' ' || b[j-1] == '\t' || b[j-1] == '\n') {
+ j--
+ }
+ if i <= j {
+ return b[:i], b[i:j], b[j:]
+ }
+ return nil, nil, b[j:]
+}
+
+// matchSpace reformats src to use the same space context as orig.
+// 1) If orig begins with blank lines, matchSpace inserts them at the beginning of src.
+// 2) matchSpace copies the indentation of the first non-blank line in orig
+// to every non-blank line in src.
+// 3) matchSpace copies the trailing space from orig and uses it in place
+// of src's trailing space.
+func matchSpace(orig []byte, src []byte) []byte {
+ before, _, after := cutSpace(orig)
+ i := bytes.LastIndex(before, []byte{'\n'})
+ before, indent := before[:i+1], before[i+1:]
+
+ _, src, _ = cutSpace(src)
+
+ var b bytes.Buffer
+ b.Write(before)
+ for len(src) > 0 {
+ line := src
+ if i := bytes.IndexByte(line, '\n'); i >= 0 {
+ line, src = line[:i+1], line[i+1:]
+ } else {
+ src = nil
+ }
+ if len(line) > 0 && line[0] != '\n' { // not blank
+ b.Write(indent)
+ }
+ b.Write(line)
+ }
+ b.Write(after)
+ return b.Bytes()
+}
diff --git a/src/cmd/gofmt/gofmt_test.go b/src/cmd/gofmt/gofmt_test.go
index 2e35ce9a4..4b2805009 100644
--- a/src/cmd/gofmt/gofmt_test.go
+++ b/src/cmd/gofmt/gofmt_test.go
@@ -12,13 +12,11 @@ import (
"testing"
)
-func runTest(t *testing.T, dirname, in, out, flags string) {
- in = filepath.Join(dirname, in)
- out = filepath.Join(dirname, out)
-
+func runTest(t *testing.T, in, out, flags string) {
// process flags
*simplifyAST = false
*rewriteRule = ""
+ stdin := false
for _, flag := range strings.Split(flags, " ") {
elts := strings.SplitN(flag, "=", 2)
name := elts[0]
@@ -33,6 +31,9 @@ func runTest(t *testing.T, dirname, in, out, flags string) {
*rewriteRule = value
case "-s":
*simplifyAST = true
+ case "-stdin":
+ // fake flag - pretend input is from stdin
+ stdin = true
default:
t.Errorf("unrecognized flag name: %s", name)
}
@@ -43,7 +44,7 @@ func runTest(t *testing.T, dirname, in, out, flags string) {
initRewrite()
var buf bytes.Buffer
- err := processFile(in, nil, &buf)
+ err := processFile(in, nil, &buf, stdin)
if err != nil {
t.Error(err)
return
@@ -57,23 +58,48 @@ func runTest(t *testing.T, dirname, in, out, flags string) {
if got := buf.Bytes(); bytes.Compare(got, expected) != 0 {
t.Errorf("(gofmt %s) != %s (see %s.gofmt)", in, out, in)
+ d, err := diff(expected, got)
+ if err == nil {
+ t.Errorf("%s", d)
+ }
ioutil.WriteFile(in+".gofmt", got, 0666)
}
}
// TODO(gri) Add more test cases!
var tests = []struct {
- dirname, in, out, flags string
+ in, flags string
}{
- {".", "gofmt.go", "gofmt.go", ""},
- {".", "gofmt_test.go", "gofmt_test.go", ""},
- {"testdata", "composites.input", "composites.golden", "-s"},
- {"testdata", "rewrite1.input", "rewrite1.golden", "-r=Foo->Bar"},
- {"testdata", "rewrite2.input", "rewrite2.golden", "-r=int->bool"},
+ {"gofmt.go", ""},
+ {"gofmt_test.go", ""},
+ {"testdata/composites.input", "-s"},
+ {"testdata/old.input", ""},
+ {"testdata/rewrite1.input", "-r=Foo->Bar"},
+ {"testdata/rewrite2.input", "-r=int->bool"},
+ {"testdata/rewrite3.input", "-r=x->x"},
+ {"testdata/rewrite4.input", "-r=(x)->x"},
+ {"testdata/stdin*.input", "-stdin"},
+ {"testdata/comments.input", ""},
+ {"testdata/import.input", ""},
}
func TestRewrite(t *testing.T) {
for _, test := range tests {
- runTest(t, test.dirname, test.in, test.out, test.flags)
+ match, err := filepath.Glob(test.in)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+ for _, in := range match {
+ out := in
+ if strings.HasSuffix(in, ".input") {
+ out = in[:len(in)-len(".input")] + ".golden"
+ }
+ runTest(t, in, out, test.flags)
+ if in != out {
+ // Check idempotence.
+ runTest(t, out, out, test.flags)
+ }
+ }
}
}
diff --git a/src/cmd/gofmt/long_test.go b/src/cmd/gofmt/long_test.go
new file mode 100644
index 000000000..edbce606a
--- /dev/null
+++ b/src/cmd/gofmt/long_test.go
@@ -0,0 +1,159 @@
+// 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 test applies gofmt to all Go files under -root.
+// To test specific files provide a list of comma-separated
+// filenames via the -files flag: go test -files=gofmt.go .
+
+package main
+
+import (
+ "bytes"
+ "flag"
+ "fmt"
+ "go/ast"
+ "go/printer"
+ "go/token"
+ "io"
+ "os"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "testing"
+)
+
+var (
+ root = flag.String("root", runtime.GOROOT(), "test root directory")
+ files = flag.String("files", "", "comma-separated list of files to test")
+ ngo = flag.Int("n", runtime.NumCPU(), "number of goroutines used")
+ verbose = flag.Bool("verbose", false, "verbose mode")
+ nfiles int // number of files processed
+)
+
+func gofmt(fset *token.FileSet, filename string, src *bytes.Buffer) error {
+ f, _, err := parse(fset, filename, src.Bytes(), false)
+ if err != nil {
+ return err
+ }
+ ast.SortImports(fset, f)
+ src.Reset()
+ return (&printer.Config{Mode: printerMode, Tabwidth: *tabWidth}).Fprint(src, fset, f)
+}
+
+func testFile(t *testing.T, b1, b2 *bytes.Buffer, filename string) {
+ // open file
+ f, err := os.Open(filename)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ // read file
+ b1.Reset()
+ _, err = io.Copy(b1, f)
+ f.Close()
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ // exclude files w/ syntax errors (typically test cases)
+ fset := token.NewFileSet()
+ if _, _, err = parse(fset, filename, b1.Bytes(), false); err != nil {
+ if *verbose {
+ fmt.Fprintf(os.Stderr, "ignoring %s\n", err)
+ }
+ return
+ }
+
+ // gofmt file
+ if err = gofmt(fset, filename, b1); err != nil {
+ t.Errorf("1st gofmt failed: %v", err)
+ return
+ }
+
+ // make a copy of the result
+ b2.Reset()
+ b2.Write(b1.Bytes())
+
+ // gofmt result again
+ if err = gofmt(fset, filename, b2); err != nil {
+ t.Errorf("2nd gofmt failed: %v", err)
+ return
+ }
+
+ // the first and 2nd result should be identical
+ if bytes.Compare(b1.Bytes(), b2.Bytes()) != 0 {
+ t.Errorf("gofmt %s not idempotent", filename)
+ }
+}
+
+func testFiles(t *testing.T, filenames <-chan string, done chan<- int) {
+ b1 := new(bytes.Buffer)
+ b2 := new(bytes.Buffer)
+ for filename := range filenames {
+ testFile(t, b1, b2, filename)
+ }
+ done <- 0
+}
+
+func genFilenames(t *testing.T, filenames chan<- string) {
+ defer close(filenames)
+
+ handleFile := func(filename string, fi os.FileInfo, err error) error {
+ if err != nil {
+ t.Error(err)
+ return nil
+ }
+ if isGoFile(fi) {
+ filenames <- filename
+ nfiles++
+ }
+ return nil
+ }
+
+ // test Go files provided via -files, if any
+ if *files != "" {
+ for _, filename := range strings.Split(*files, ",") {
+ fi, err := os.Stat(filename)
+ handleFile(filename, fi, err)
+ }
+ return // ignore files under -root
+ }
+
+ // otherwise, test all Go files under *root
+ filepath.Walk(*root, handleFile)
+}
+
+func TestAll(t *testing.T) {
+ if testing.Short() {
+ return
+ }
+
+ if *ngo < 1 {
+ *ngo = 1 // make sure test is run
+ }
+ if *verbose {
+ fmt.Printf("running test using %d goroutines\n", *ngo)
+ }
+
+ // generate filenames
+ filenames := make(chan string, 32)
+ go genFilenames(t, filenames)
+
+ // launch test goroutines
+ done := make(chan int)
+ for i := 0; i < *ngo; i++ {
+ go testFiles(t, filenames, done)
+ }
+
+ // wait for all test goroutines to complete
+ for i := 0; i < *ngo; i++ {
+ <-done
+ }
+
+ if *verbose {
+ fmt.Printf("processed %d files\n", nfiles)
+ }
+}
diff --git a/src/cmd/gofmt/rewrite.go b/src/cmd/gofmt/rewrite.go
index 3d74dea0f..3c7861f0d 100644
--- a/src/cmd/gofmt/rewrite.go
+++ b/src/cmd/gofmt/rewrite.go
@@ -13,7 +13,7 @@ import (
"reflect"
"strings"
"unicode"
- "utf8"
+ "unicode/utf8"
)
func initRewrite() {
@@ -36,7 +36,7 @@ func initRewrite() {
// but there are problems with preserving formatting and also
// with what a wildcard for a statement looks like.
func parseExpr(s string, what string) ast.Expr {
- x, err := parser.ParseExpr(fset, "input", s)
+ x, err := parser.ParseExpr(s)
if err != nil {
fmt.Fprintf(os.Stderr, "parsing %s %s: %s\n", what, s, err)
os.Exit(2)
@@ -65,7 +65,7 @@ func rewriteFile(pattern, replace ast.Expr, p *ast.File) *ast.File {
return reflect.Value{}
}
for k := range m {
- m[k] = reflect.Value{}, false
+ delete(m, k)
}
val = apply(f, val)
if match(m, pat, val) {
@@ -85,7 +85,8 @@ func setValue(x, y reflect.Value) {
}
defer func() {
if x := recover(); x != nil {
- if s, ok := x.(string); ok && strings.HasPrefix(s, "type mismatch") {
+ if s, ok := x.(string); ok &&
+ (strings.Contains(s, "type mismatch") || strings.Contains(s, "not assignable")) {
// x cannot be set to y - ignore this rewrite
return
}
@@ -158,8 +159,8 @@ func match(m map[string]reflect.Value, pattern, val reflect.Value) bool {
if m != nil && pattern.IsValid() && pattern.Type() == identType {
name := pattern.Interface().(*ast.Ident).Name
if isWildcard(name) && val.IsValid() {
- // wildcards only match expressions
- if _, ok := val.Interface().(ast.Expr); ok {
+ // wildcards only match valid (non-nil) expressions.
+ if _, ok := val.Interface().(ast.Expr); ok && !val.IsNil() {
if old, ok := m[name]; ok {
return match(nil, old, val)
}
diff --git a/src/cmd/gofmt/simplify.go b/src/cmd/gofmt/simplify.go
index d9afc0e7b..470c00625 100644
--- a/src/cmd/gofmt/simplify.go
+++ b/src/cmd/gofmt/simplify.go
@@ -6,6 +6,7 @@ package main
import (
"go/ast"
+ "go/token"
"reflect"
)
@@ -26,10 +27,12 @@ func (s *simplifier) Visit(node ast.Node) ast.Visitor {
if eltType != nil {
typ := reflect.ValueOf(eltType)
- for _, x := range outer.Elts {
+ for i, x := range outer.Elts {
+ px := &outer.Elts[i]
// look at value of indexed/named elements
if t, ok := x.(*ast.KeyValueExpr); ok {
x = t.Value
+ px = &t.Value
}
simplify(x)
// if the element is a composite literal and its literal type
@@ -40,6 +43,19 @@ func (s *simplifier) Visit(node ast.Node) ast.Visitor {
inner.Type = nil
}
}
+ // if the outer literal's element type is a pointer type *T
+ // and the element is & of a composite literal of type T,
+ // the inner &T may be omitted.
+ if ptr, ok := eltType.(*ast.StarExpr); ok {
+ if addr, ok := x.(*ast.UnaryExpr); ok && addr.Op == token.AND {
+ if inner, ok := addr.X.(*ast.CompositeLit); ok {
+ if match(nil, reflect.ValueOf(ptr.X), reflect.ValueOf(inner.Type)) {
+ inner.Type = nil // drop T
+ *px = inner // drop &
+ }
+ }
+ }
+ }
}
// node was simplified - stop walk (there are no subnodes to simplify)
diff --git a/src/cmd/gofmt/test.sh b/src/cmd/gofmt/test.sh
deleted file mode 100755
index 063a0727f..000000000
--- a/src/cmd/gofmt/test.sh
+++ /dev/null
@@ -1,162 +0,0 @@
-#!/usr/bin/env bash
-# Copyright 2009 The Go Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file.
-
-eval $(gomake --no-print-directory -f ../../Make.inc go-env)
-if [ -z "$O" ]; then
- echo 'missing $O - maybe no Make.$GOARCH?' 1>&2
- exit 1
-fi
-
-CMD="./gofmt"
-TMP1=test_tmp1.go
-TMP2=test_tmp2.go
-TMP3=test_tmp3.go
-COUNT=0
-
-count() {
- #echo $1
- let COUNT=$COUNT+1
- let M=$COUNT%10
- if [ $M == 0 ]; then
- echo -n "."
- fi
-}
-
-
-error() {
- echo $1
- exit 1
-}
-
-
-# apply to one file
-apply1() {
- # the following files are skipped because they are test cases
- # for syntax errors and thus won't parse in the first place:
- case `basename "$F"` in
- func3.go | const2.go | char_lit1.go | blank1.go | ddd1.go | \
- bug014.go | bug050.go | bug068.go | bug083.go | bug088.go | \
- bug106.go | bug121.go | bug125.go | bug133.go | bug160.go | \
- bug163.go | bug166.go | bug169.go | bug217.go | bug222.go | \
- bug226.go | bug228.go | bug248.go | bug274.go | bug280.go | \
- bug282.go | bug287.go | bug298.go | bug299.go | bug300.go | \
- bug302.go | bug306.go | bug322.go | bug324.go | bug335.go | \
- bug340.go | bug349.go | bug351.go | bug358.go ) return ;;
- esac
- # the following directories are skipped because they contain test
- # cases for syntax errors and thus won't parse in the first place:
- case `dirname "$F"` in
- $GOROOT/test/syntax ) return ;;
- esac
- #echo $1 $2
- "$1" "$2"; count "$F"
-}
-
-
-# apply to local files
-applydot() {
- for F in `find . -name "*.go" | grep -v "._"`; do
- apply1 "$1" $F
- done
-}
-
-
-# apply to all .go files we can find
-apply() {
- for F in `find "$GOROOT" -name "*.go" | grep -v "._"`; do
- apply1 "$1" $F
- done
-}
-
-
-cleanup() {
- rm -f $TMP1 $TMP2 $TMP3
-}
-
-
-silent() {
- cleanup
- $CMD "$1" > /dev/null 2> $TMP1
- if [ $? != 0 ]; then
- cat $TMP1
- error "Error (silent mode test): test.sh $1"
- fi
-}
-
-
-idempotent() {
- cleanup
- $CMD "$1" > $TMP1
- if [ $? != 0 ]; then
- error "Error (step 1 of idempotency test): test.sh $1"
- fi
-
- $CMD $TMP1 > $TMP2
- if [ $? != 0 ]; then
- error "Error (step 2 of idempotency test): test.sh $1"
- fi
-
- $CMD $TMP2 > $TMP3
- if [ $? != 0 ]; then
- error "Error (step 3 of idempotency test): test.sh $1"
- fi
-
- cmp -s $TMP2 $TMP3
- if [ $? != 0 ]; then
- diff $TMP2 $TMP3
- error "Error (step 4 of idempotency test): test.sh $1"
- fi
-}
-
-
-valid() {
- cleanup
- $CMD "$1" > $TMP1
- if [ $? != 0 ]; then
- error "Error (step 1 of validity test): test.sh $1"
- fi
-
- $GC -o /dev/null $TMP1
- if [ $? != 0 ]; then
- error "Error (step 2 of validity test): test.sh $1"
- fi
-}
-
-
-runtest() {
- #echo "Testing silent mode"
- cleanup
- "$1" silent "$2"
-
- #echo "Testing idempotency"
- cleanup
- "$1" idempotent "$2"
-}
-
-
-runtests() {
- if [ $# = 0 ]; then
- runtest apply
- # verify the pretty-printed files can be compiled with $GC again
- # do it in local directory only because of the prerequisites required
- #echo "Testing validity"
- # Disabled for now due to dependency problems
- # cleanup
- # applydot valid
- else
- for F in "$@"; do
- runtest apply1 "$F"
- done
- fi
-}
-
-
-# run over all .go files
-runtests "$@"
-cleanup
-
-# done
-echo
-echo "PASSED ($COUNT tests)"
diff --git a/src/cmd/gofmt/testdata/comments.golden b/src/cmd/gofmt/testdata/comments.golden
new file mode 100644
index 000000000..ad6bcafaf
--- /dev/null
+++ b/src/cmd/gofmt/testdata/comments.golden
@@ -0,0 +1,9 @@
+package main
+
+func main() {}
+
+// comment here
+
+func f() {}
+
+//line foo.go:1
diff --git a/src/cmd/gofmt/testdata/comments.input b/src/cmd/gofmt/testdata/comments.input
new file mode 100644
index 000000000..ad6bcafaf
--- /dev/null
+++ b/src/cmd/gofmt/testdata/comments.input
@@ -0,0 +1,9 @@
+package main
+
+func main() {}
+
+// comment here
+
+func f() {}
+
+//line foo.go:1
diff --git a/src/cmd/gofmt/testdata/composites.golden b/src/cmd/gofmt/testdata/composites.golden
index 1fd5847c1..b2825e732 100644
--- a/src/cmd/gofmt/testdata/composites.golden
+++ b/src/cmd/gofmt/testdata/composites.golden
@@ -102,3 +102,101 @@ var pieces4 = []Piece{
{2, 0, Point{4, 1}, []Point{{0, 0}, {1, 0}, {1, 0}, {1, 0}}, nil, nil},
{3, 0, Point{1, 4}, []Point{{0, 0}, {0, 1}, {0, 1}, {0, 1}}, nil, nil},
}
+
+var _ = [42]*T{
+ {},
+ {1, 2},
+ {3, 4},
+}
+
+var _ = [...]*T{
+ {},
+ {1, 2},
+ {3, 4},
+}
+
+var _ = []*T{
+ {},
+ {1, 2},
+ {3, 4},
+}
+
+var _ = []*T{
+ {},
+ 10: {1, 2},
+ 20: {3, 4},
+}
+
+var _ = []*struct {
+ x, y int
+}{
+ {},
+ 10: {1, 2},
+ 20: {3, 4},
+}
+
+var _ = []interface{}{
+ &T{},
+ 10: &T{1, 2},
+ 20: &T{3, 4},
+}
+
+var _ = []*[]int{
+ {},
+ {1, 2},
+ {3, 4},
+}
+
+var _ = []*[]int{
+ (&[]int{}),
+ (&[]int{1, 2}),
+ {3, 4},
+}
+
+var _ = []*[]*[]int{
+ {},
+ {
+ {},
+ {0, 1, 2, 3},
+ {4, 5},
+ },
+}
+
+var _ = map[string]*T{
+ "foo": {},
+ "bar": {1, 2},
+ "bal": {3, 4},
+}
+
+var _ = map[string]*struct {
+ x, y int
+}{
+ "foo": {},
+ "bar": {1, 2},
+ "bal": {3, 4},
+}
+
+var _ = map[string]interface{}{
+ "foo": &T{},
+ "bar": &T{1, 2},
+ "bal": &T{3, 4},
+}
+
+var _ = map[string]*[]int{
+ "foo": {},
+ "bar": {1, 2},
+ "bal": {3, 4},
+}
+
+var _ = map[string]*[]int{
+ "foo": (&[]int{}),
+ "bar": (&[]int{1, 2}),
+ "bal": {3, 4},
+}
+
+var pieces4 = []*Piece{
+ {0, 0, Point{4, 1}, []Point{{0, 0}, {1, 0}, {1, 0}, {1, 0}}, nil, nil},
+ {1, 0, Point{1, 4}, []Point{{0, 0}, {0, 1}, {0, 1}, {0, 1}}, nil, nil},
+ {2, 0, Point{4, 1}, []Point{{0, 0}, {1, 0}, {1, 0}, {1, 0}}, nil, nil},
+ {3, 0, Point{1, 4}, []Point{{0, 0}, {0, 1}, {0, 1}, {0, 1}}, nil, nil},
+}
diff --git a/src/cmd/gofmt/testdata/composites.input b/src/cmd/gofmt/testdata/composites.input
index 15afd9e5c..7210dafc9 100644
--- a/src/cmd/gofmt/testdata/composites.input
+++ b/src/cmd/gofmt/testdata/composites.input
@@ -102,3 +102,101 @@ var pieces4 = []Piece{
Piece{2, 0, Point{4, 1}, []Point{Point{0, 0}, Point{1, 0}, Point{1, 0}, Point{1, 0}}, nil, nil},
Piece{3, 0, Point{1, 4}, []Point{Point{0, 0}, Point{0, 1}, Point{0, 1}, Point{0, 1}}, nil, nil},
}
+
+var _ = [42]*T{
+ &T{},
+ &T{1, 2},
+ &T{3, 4},
+}
+
+var _ = [...]*T{
+ &T{},
+ &T{1, 2},
+ &T{3, 4},
+}
+
+var _ = []*T{
+ &T{},
+ &T{1, 2},
+ &T{3, 4},
+}
+
+var _ = []*T{
+ &T{},
+ 10: &T{1, 2},
+ 20: &T{3, 4},
+}
+
+var _ = []*struct {
+ x, y int
+}{
+ &struct{ x, y int }{},
+ 10: &struct{ x, y int }{1, 2},
+ 20: &struct{ x, y int }{3, 4},
+}
+
+var _ = []interface{}{
+ &T{},
+ 10: &T{1, 2},
+ 20: &T{3, 4},
+}
+
+var _ = []*[]int{
+ &[]int{},
+ &[]int{1, 2},
+ &[]int{3, 4},
+}
+
+var _ = []*[]int{
+ (&[]int{}),
+ (&[]int{1, 2}),
+ &[]int{3, 4},
+}
+
+var _ = []*[]*[]int{
+ &[]*[]int{},
+ &[]*[]int{
+ &[]int{},
+ &[]int{0, 1, 2, 3},
+ &[]int{4, 5},
+ },
+}
+
+var _ = map[string]*T{
+ "foo": &T{},
+ "bar": &T{1, 2},
+ "bal": &T{3, 4},
+}
+
+var _ = map[string]*struct {
+ x, y int
+}{
+ "foo": &struct{ x, y int }{},
+ "bar": &struct{ x, y int }{1, 2},
+ "bal": &struct{ x, y int }{3, 4},
+}
+
+var _ = map[string]interface{}{
+ "foo": &T{},
+ "bar": &T{1, 2},
+ "bal": &T{3, 4},
+}
+
+var _ = map[string]*[]int{
+ "foo": &[]int{},
+ "bar": &[]int{1, 2},
+ "bal": &[]int{3, 4},
+}
+
+var _ = map[string]*[]int{
+ "foo": (&[]int{}),
+ "bar": (&[]int{1, 2}),
+ "bal": &[]int{3, 4},
+}
+
+var pieces4 = []*Piece{
+ &Piece{0, 0, Point{4, 1}, []Point{Point{0, 0}, Point{1, 0}, Point{1, 0}, Point{1, 0}}, nil, nil},
+ &Piece{1, 0, Point{1, 4}, []Point{Point{0, 0}, Point{0, 1}, Point{0, 1}, Point{0, 1}}, nil, nil},
+ &Piece{2, 0, Point{4, 1}, []Point{Point{0, 0}, Point{1, 0}, Point{1, 0}, Point{1, 0}}, nil, nil},
+ &Piece{3, 0, Point{1, 4}, []Point{Point{0, 0}, Point{0, 1}, Point{0, 1}, Point{0, 1}}, nil, nil},
+}
diff --git a/src/cmd/gofmt/testdata/import.golden b/src/cmd/gofmt/testdata/import.golden
new file mode 100644
index 000000000..e8ee44988
--- /dev/null
+++ b/src/cmd/gofmt/testdata/import.golden
@@ -0,0 +1,108 @@
+package main
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "log"
+ "math"
+)
+
+import (
+ "fmt"
+
+ "math"
+
+ "log"
+
+ "errors"
+
+ "io"
+)
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "log"
+ "math"
+
+ "fmt"
+
+ "math"
+
+ "log"
+
+ "errors"
+
+ "io"
+)
+
+import (
+ // a block with comments
+ "errors"
+ "fmt" // for Printf
+ "io" // for Reader
+ "log" // for Fatal
+ "math"
+)
+
+import (
+ "fmt" // for Printf
+
+ "math"
+
+ "log" // for Fatal
+
+ "errors"
+
+ "io" // for Reader
+)
+
+import (
+ // for Printf
+ "fmt"
+
+ "math"
+
+ // for Fatal
+ "log"
+
+ "errors"
+
+ // for Reader
+ "io"
+)
+
+import (
+ "errors"
+ "fmt" // for Printf
+ "io" // for Reader
+ "log" // for Fatal
+ "math"
+
+ "fmt" // for Printf
+
+ "math"
+
+ "log" // for Fatal
+
+ "errors"
+
+ "io" // for Reader
+)
+
+import (
+ "fmt" // for Printf
+
+ "errors"
+ "io" // for Reader
+ "log" // for Fatal
+ "math"
+
+ "errors"
+ "fmt" // for Printf
+ "io" // for Reader
+ "log" // for Fatal
+ "math"
+)
diff --git a/src/cmd/gofmt/testdata/import.input b/src/cmd/gofmt/testdata/import.input
new file mode 100644
index 000000000..cc36c3e01
--- /dev/null
+++ b/src/cmd/gofmt/testdata/import.input
@@ -0,0 +1,108 @@
+package main
+
+import (
+ "fmt"
+ "math"
+ "log"
+ "errors"
+ "io"
+)
+
+import (
+ "fmt"
+
+ "math"
+
+ "log"
+
+ "errors"
+
+ "io"
+)
+
+import (
+ "fmt"
+ "math"
+ "log"
+ "errors"
+ "io"
+
+ "fmt"
+
+ "math"
+
+ "log"
+
+ "errors"
+
+ "io"
+)
+
+import (
+ // a block with comments
+ "fmt" // for Printf
+ "math"
+ "log" // for Fatal
+ "errors"
+ "io" // for Reader
+)
+
+import (
+ "fmt" // for Printf
+
+ "math"
+
+ "log" // for Fatal
+
+ "errors"
+
+ "io" // for Reader
+)
+
+import (
+ // for Printf
+ "fmt"
+
+ "math"
+
+ // for Fatal
+ "log"
+
+ "errors"
+
+ // for Reader
+ "io"
+)
+
+import (
+ "fmt" // for Printf
+ "math"
+ "log" // for Fatal
+ "errors"
+ "io" // for Reader
+
+ "fmt" // for Printf
+
+ "math"
+
+ "log" // for Fatal
+
+ "errors"
+
+ "io" // for Reader
+)
+
+import (
+ "fmt" // for Printf
+
+ "math"
+ "log" // for Fatal
+ "errors"
+ "io" // for Reader
+
+ "fmt" // for Printf
+ "math"
+ "log" // for Fatal
+ "errors"
+ "io" // for Reader
+)
diff --git a/src/cmd/gofmt/testdata/old.golden b/src/cmd/gofmt/testdata/old.golden
new file mode 100644
index 000000000..95a0b72a0
--- /dev/null
+++ b/src/cmd/gofmt/testdata/old.golden
@@ -0,0 +1,9 @@
+package P
+
+func f() {
+ if x {
+ y
+ } else {
+ z
+ }
+}
diff --git a/src/cmd/gofmt/testdata/old.input b/src/cmd/gofmt/testdata/old.input
new file mode 100644
index 000000000..e24eed215
--- /dev/null
+++ b/src/cmd/gofmt/testdata/old.input
@@ -0,0 +1,8 @@
+package P
+
+func f() {
+ if x {
+ y
+ } else
+ z
+}
diff --git a/src/cmd/gofmt/testdata/rewrite3.golden b/src/cmd/gofmt/testdata/rewrite3.golden
new file mode 100644
index 000000000..0d16d1601
--- /dev/null
+++ b/src/cmd/gofmt/testdata/rewrite3.golden
@@ -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 main
+
+// Field tags are *ast.BasicLit nodes that are nil when the tag is
+// absent. These nil nodes must not be mistaken for expressions,
+// the rewriter should not try to dereference them. Was issue 2410.
+type Foo struct {
+ Field int
+}
diff --git a/src/cmd/gofmt/testdata/rewrite3.input b/src/cmd/gofmt/testdata/rewrite3.input
new file mode 100644
index 000000000..0d16d1601
--- /dev/null
+++ b/src/cmd/gofmt/testdata/rewrite3.input
@@ -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 main
+
+// Field tags are *ast.BasicLit nodes that are nil when the tag is
+// absent. These nil nodes must not be mistaken for expressions,
+// the rewriter should not try to dereference them. Was issue 2410.
+type Foo struct {
+ Field int
+}
diff --git a/src/cmd/gofmt/testdata/rewrite4.golden b/src/cmd/gofmt/testdata/rewrite4.golden
new file mode 100644
index 000000000..8dfc81a07
--- /dev/null
+++ b/src/cmd/gofmt/testdata/rewrite4.golden
@@ -0,0 +1,74 @@
+// 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.
+
+// Rewriting of parenthesized expressions (x) -> x
+// must not drop parentheses if that would lead to
+// wrong association of the operands.
+// Was issue 1847.
+
+package main
+
+// From example 1 of issue 1847.
+func _() {
+ var t = (&T{1000}).Id()
+}
+
+// From example 2 of issue 1847.
+func _() {
+ fmt.Println((*xpp).a)
+}
+
+// Some more test cases.
+func _() {
+ _ = (-x).f
+ _ = (*x).f
+ _ = (&x).f
+ _ = (!x).f
+ _ = -x.f
+ _ = *x.f
+ _ = &x.f
+ _ = !x.f
+ (-x).f()
+ (*x).f()
+ (&x).f()
+ (!x).f()
+ _ = -x.f()
+ _ = *x.f()
+ _ = &x.f()
+ _ = !x.f()
+
+ _ = (-x).f
+ _ = (*x).f
+ _ = (&x).f
+ _ = (!x).f
+ _ = -x.f
+ _ = *x.f
+ _ = &x.f
+ _ = !x.f
+ (-x).f()
+ (*x).f()
+ (&x).f()
+ (!x).f()
+ _ = -x.f()
+ _ = *x.f()
+ _ = &x.f()
+ _ = !x.f()
+
+ _ = -x.f
+ _ = *x.f
+ _ = &x.f
+ _ = !x.f
+ _ = -x.f
+ _ = *x.f
+ _ = &x.f
+ _ = !x.f
+ _ = -x.f()
+ _ = *x.f()
+ _ = &x.f()
+ _ = !x.f()
+ _ = -x.f()
+ _ = *x.f()
+ _ = &x.f()
+ _ = !x.f()
+}
diff --git a/src/cmd/gofmt/testdata/rewrite4.input b/src/cmd/gofmt/testdata/rewrite4.input
new file mode 100644
index 000000000..164cc0451
--- /dev/null
+++ b/src/cmd/gofmt/testdata/rewrite4.input
@@ -0,0 +1,74 @@
+// 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.
+
+// Rewriting of parenthesized expressions (x) -> x
+// must not drop parentheses if that would lead to
+// wrong association of the operands.
+// Was issue 1847.
+
+package main
+
+// From example 1 of issue 1847.
+func _() {
+ var t = (&T{1000}).Id()
+}
+
+// From example 2 of issue 1847.
+func _() {
+ fmt.Println((*xpp).a)
+}
+
+// Some more test cases.
+func _() {
+ _ = (-x).f
+ _ = (*x).f
+ _ = (&x).f
+ _ = (!x).f
+ _ = (-x.f)
+ _ = (*x.f)
+ _ = (&x.f)
+ _ = (!x.f)
+ (-x).f()
+ (*x).f()
+ (&x).f()
+ (!x).f()
+ _ = (-x.f())
+ _ = (*x.f())
+ _ = (&x.f())
+ _ = (!x.f())
+
+ _ = ((-x)).f
+ _ = ((*x)).f
+ _ = ((&x)).f
+ _ = ((!x)).f
+ _ = ((-x.f))
+ _ = ((*x.f))
+ _ = ((&x.f))
+ _ = ((!x.f))
+ ((-x)).f()
+ ((*x)).f()
+ ((&x)).f()
+ ((!x)).f()
+ _ = ((-x.f()))
+ _ = ((*x.f()))
+ _ = ((&x.f()))
+ _ = ((!x.f()))
+
+ _ = -(x).f
+ _ = *(x).f
+ _ = &(x).f
+ _ = !(x).f
+ _ = -x.f
+ _ = *x.f
+ _ = &x.f
+ _ = !x.f
+ _ = -(x).f()
+ _ = *(x).f()
+ _ = &(x).f()
+ _ = !(x).f()
+ _ = -x.f()
+ _ = *x.f()
+ _ = &x.f()
+ _ = !x.f()
+}
diff --git a/src/cmd/gofmt/testdata/stdin1.golden b/src/cmd/gofmt/testdata/stdin1.golden
new file mode 100644
index 000000000..ff8b0b7ab
--- /dev/null
+++ b/src/cmd/gofmt/testdata/stdin1.golden
@@ -0,0 +1,3 @@
+ if x {
+ y
+ }
diff --git a/src/cmd/gofmt/testdata/stdin1.golden.gofmt b/src/cmd/gofmt/testdata/stdin1.golden.gofmt
new file mode 100644
index 000000000..1f888877d
--- /dev/null
+++ b/src/cmd/gofmt/testdata/stdin1.golden.gofmt
@@ -0,0 +1,3 @@
+ if x {
+ y
+}
diff --git a/src/cmd/gofmt/testdata/stdin1.input b/src/cmd/gofmt/testdata/stdin1.input
new file mode 100644
index 000000000..ff8b0b7ab
--- /dev/null
+++ b/src/cmd/gofmt/testdata/stdin1.input
@@ -0,0 +1,3 @@
+ if x {
+ y
+ }
diff --git a/src/cmd/gofmt/testdata/stdin1.input.gofmt b/src/cmd/gofmt/testdata/stdin1.input.gofmt
new file mode 100644
index 000000000..1f888877d
--- /dev/null
+++ b/src/cmd/gofmt/testdata/stdin1.input.gofmt
@@ -0,0 +1,3 @@
+ if x {
+ y
+}
diff --git a/src/cmd/gofmt/testdata/stdin2.golden b/src/cmd/gofmt/testdata/stdin2.golden
new file mode 100644
index 000000000..7eb1b54fe
--- /dev/null
+++ b/src/cmd/gofmt/testdata/stdin2.golden
@@ -0,0 +1,11 @@
+
+
+var x int
+
+func f() {
+ y := z
+ /* this is a comment */
+ // this is a comment too
+}
+
+
diff --git a/src/cmd/gofmt/testdata/stdin2.golden.gofmt b/src/cmd/gofmt/testdata/stdin2.golden.gofmt
new file mode 100644
index 000000000..85e800300
--- /dev/null
+++ b/src/cmd/gofmt/testdata/stdin2.golden.gofmt
@@ -0,0 +1,10 @@
+
+
+
+var x int
+
+func f() {
+ y := z
+}
+
+
diff --git a/src/cmd/gofmt/testdata/stdin2.input b/src/cmd/gofmt/testdata/stdin2.input
new file mode 100644
index 000000000..99defd2d1
--- /dev/null
+++ b/src/cmd/gofmt/testdata/stdin2.input
@@ -0,0 +1,11 @@
+
+
+var x int
+
+
+func f() { y := z
+ /* this is a comment */
+ // this is a comment too
+}
+
+
diff --git a/src/cmd/gofmt/testdata/stdin2.input.gofmt b/src/cmd/gofmt/testdata/stdin2.input.gofmt
new file mode 100644
index 000000000..7eb1b54fe
--- /dev/null
+++ b/src/cmd/gofmt/testdata/stdin2.input.gofmt
@@ -0,0 +1,11 @@
+
+
+var x int
+
+func f() {
+ y := z
+ /* this is a comment */
+ // this is a comment too
+}
+
+
diff --git a/src/cmd/gofmt/testdata/stdin3.golden b/src/cmd/gofmt/testdata/stdin3.golden
new file mode 100644
index 000000000..1bf2f5a48
--- /dev/null
+++ b/src/cmd/gofmt/testdata/stdin3.golden
@@ -0,0 +1,6 @@
+
+ /* note: no newline at end of file */
+ for i := 0; i < 10; i++ {
+ s += i
+ }
+ \ No newline at end of file
diff --git a/src/cmd/gofmt/testdata/stdin3.golden.gofmt b/src/cmd/gofmt/testdata/stdin3.golden.gofmt
new file mode 100644
index 000000000..b4d1d4663
--- /dev/null
+++ b/src/cmd/gofmt/testdata/stdin3.golden.gofmt
@@ -0,0 +1,7 @@
+
+
+ /* note: no newline at end of file */
+ for i := 0; i < 10; i++ {
+ s += i
+ }
+ \ No newline at end of file
diff --git a/src/cmd/gofmt/testdata/stdin3.input b/src/cmd/gofmt/testdata/stdin3.input
new file mode 100644
index 000000000..d963bd0d2
--- /dev/null
+++ b/src/cmd/gofmt/testdata/stdin3.input
@@ -0,0 +1,4 @@
+
+ /* note: no newline at end of file */
+ for i := 0; i < 10; i++ { s += i }
+ \ No newline at end of file
diff --git a/src/cmd/gofmt/testdata/stdin3.input.gofmt b/src/cmd/gofmt/testdata/stdin3.input.gofmt
new file mode 100644
index 000000000..b4d1d4663
--- /dev/null
+++ b/src/cmd/gofmt/testdata/stdin3.input.gofmt
@@ -0,0 +1,7 @@
+
+
+ /* note: no newline at end of file */
+ for i := 0; i < 10; i++ {
+ s += i
+ }
+ \ No newline at end of file
diff --git a/src/cmd/gofmt/testdata/stdin4.golden b/src/cmd/gofmt/testdata/stdin4.golden
new file mode 100644
index 000000000..5f7343551
--- /dev/null
+++ b/src/cmd/gofmt/testdata/stdin4.golden
@@ -0,0 +1,3 @@
+ // comment
+
+ i := 0
diff --git a/src/cmd/gofmt/testdata/stdin4.golden.gofmt b/src/cmd/gofmt/testdata/stdin4.golden.gofmt
new file mode 100644
index 000000000..5f7343551
--- /dev/null
+++ b/src/cmd/gofmt/testdata/stdin4.golden.gofmt
@@ -0,0 +1,3 @@
+ // comment
+
+ i := 0
diff --git a/src/cmd/gofmt/testdata/stdin4.input b/src/cmd/gofmt/testdata/stdin4.input
new file mode 100644
index 000000000..f02a54fb1
--- /dev/null
+++ b/src/cmd/gofmt/testdata/stdin4.input
@@ -0,0 +1,3 @@
+ // comment
+
+ i := 0
diff --git a/src/cmd/gofmt/testdata/stdin4.input.gofmt b/src/cmd/gofmt/testdata/stdin4.input.gofmt
new file mode 100644
index 000000000..5f7343551
--- /dev/null
+++ b/src/cmd/gofmt/testdata/stdin4.input.gofmt
@@ -0,0 +1,3 @@
+ // comment
+
+ i := 0
diff --git a/src/cmd/goinstall/Makefile b/src/cmd/goinstall/Makefile
deleted file mode 100644
index b90646973..000000000
--- a/src/cmd/goinstall/Makefile
+++ /dev/null
@@ -1,19 +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 ../../Make.inc
-
-TARG=goinstall
-GOFILES=\
- download.go\
- main.go\
- make.go\
-
-include ../../Make.cmd
-
-test:
- gotest
-
-testshort:
- gotest -test.short
diff --git a/src/cmd/goinstall/doc.go b/src/cmd/goinstall/doc.go
deleted file mode 100644
index 47c615364..000000000
--- a/src/cmd/goinstall/doc.go
+++ /dev/null
@@ -1,196 +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.
-
-/*
-Goinstall is an experiment in automatic package installation.
-It installs packages, possibly downloading them from the internet.
-It maintains a list of public Go packages at
-http://godashboard.appspot.com/package.
-
-Usage:
- goinstall [flags] importpath...
- goinstall [flags] -a
-
-Flags and default settings:
- -a=false install all previously installed packages
- -clean=false clean the package directory before installing
- -dashboard=true tally public packages on godashboard.appspot.com
- -install=true build and install the package and its dependencies
- -nuke=false remove the target object and clean before installing
- -u=false update already-downloaded packages
- -v=false verbose operation
-
-Goinstall installs each of the packages identified on the command line. It
-installs a package's prerequisites before trying to install the package
-itself. Unless -log=false is specified, goinstall logs the import path of each
-installed package to $GOROOT/goinstall.log for use by goinstall -a.
-
-If the -a flag is given, goinstall reinstalls all previously installed
-packages, reading the list from $GOROOT/goinstall.log. After updating to a
-new Go release, which deletes all package binaries, running
-
- goinstall -a
-
-will recompile and reinstall goinstalled packages.
-
-Another common idiom is to use
-
- goinstall -a -u
-
-to update, recompile, and reinstall all goinstalled packages.
-
-The source code for a package with import path foo/bar is expected
-to be in the directory $GOROOT/src/pkg/foo/bar/ or $GOPATH/src/foo/bar/.
-See "The GOPATH Environment Variable" for more about GOPATH.
-
-By default, goinstall prints output only when it encounters an error.
-The -v flag causes goinstall to print information about packages
-being considered and installed.
-
-Goinstall ignores Makefiles.
-
-
-Remote Repositories
-
-If a package import path refers to a remote repository, goinstall will
-download the code if necessary.
-
-Goinstall recognizes packages from a few common code hosting sites:
-
- BitBucket (Mercurial)
-
- import "bitbucket.org/user/project"
- import "bitbucket.org/user/project/sub/directory"
-
- GitHub (Git)
-
- import "github.com/user/project"
- import "github.com/user/project/sub/directory"
-
- Google Code Project Hosting (Git, Mercurial, Subversion)
-
- import "project.googlecode.com/git"
- import "project.googlecode.com/git/sub/directory"
-
- import "project.googlecode.com/hg"
- import "project.googlecode.com/hg/sub/directory"
-
- import "project.googlecode.com/svn/trunk"
- import "project.googlecode.com/svn/trunk/sub/directory"
-
- Launchpad (Bazaar)
-
- import "launchpad.net/project"
- import "launchpad.net/project/series"
- import "launchpad.net/project/series/sub/directory"
-
- import "launchpad.net/~user/project/branch"
- import "launchpad.net/~user/project/branch/sub/directory"
-
-If the destination directory (e.g., $GOROOT/src/pkg/bitbucket.org/user/project)
-already exists and contains an appropriate checkout, goinstall will not
-attempt to fetch updates. The -u flag changes this behavior,
-causing goinstall to update all remote packages encountered during
-the installation.
-
-When downloading or updating, goinstall looks for a tag with the "go." prefix
-that corresponds to the local Go version. For Go "release.r58" it looks for a
-tag named "go.r58". For "weekly.2011-06-03" it looks for "go.weekly.2011-06-03".
-If the specific "go.X" tag is not found, it chooses the closest earlier version.
-If an appropriate tag is found, goinstall uses that version of the code.
-Otherwise it uses the default version selected by the version control
-system, typically HEAD for git, tip for Mercurial.
-
-After a successful download and installation of one of these import paths,
-goinstall reports the installation to godashboard.appspot.com, which
-increments a count associated with the package and the time of its most
-recent installation. This mechanism powers the package list at
-http://godashboard.appspot.com/package, allowing Go programmers to learn about
-popular packages that might be worth looking at.
-The -dashboard=false flag disables this reporting.
-
-For code hosted on other servers, goinstall recognizes the general form
-
- repository.vcs/path
-
-as denoting the given repository, with or without the .vcs suffix, using
-the named version control system, and then the path inside that repository.
-The supported version control systems are:
-
- Bazaar .bzr
- Git .git
- Mercurial .hg
- Subversion .svn
-
-For example,
-
- import "example.org/user/foo.hg"
-
-denotes the root directory of the Mercurial repository at example.org/user/foo
-or foo.hg, and
-
- import "example.org/repo.git/foo/bar"
-
-denotes the foo/bar directory of the Git repository at example.com/repo or
-repo.git.
-
-When a version control system supports multiple protocols, goinstall tries each
-in turn.
-For example, for Git it tries git://, then https://, then http://.
-
-
-The GOPATH Environment Variable
-
-GOPATH may be set to a colon-separated list of paths inside which Go code,
-package objects, and executables may be found.
-
-Set a GOPATH to use goinstall to build and install your own code and
-external libraries outside of the Go tree (and to avoid writing Makefiles).
-
-The top-level directory structure of a GOPATH is prescribed:
-
-The 'src' directory is for source code. The directory naming inside 'src'
-determines the package import path or executable name.
-
-The 'pkg' directory is for package objects. Like the Go tree, package objects
-are stored inside a directory named after the target operating system and
-processor architecture ('pkg/$GOOS_$GOARCH').
-A package whose source is located at '$GOPATH/src/foo/bar' would be imported
-as 'foo/bar' and installed as '$GOPATH/pkg/$GOOS_$GOARCH/foo/bar.a'.
-
-The 'bin' directory is for executable files.
-Goinstall installs program binaries using the name of the source folder.
-A binary whose source is at 'src/foo/qux' would be built and installed to
-'$GOPATH/bin/qux'. (Note 'bin/qux', not 'bin/foo/qux' - this is such that
-you can put the bin directory in your PATH.)
-
-Here's an example directory layout:
-
- GOPATH=/home/user/gocode
-
- /home/user/gocode/
- src/foo/
- bar/ (go code in package bar)
- qux/ (go code in package main)
- bin/qux (executable file)
- pkg/linux_amd64/foo/bar.a (object file)
-
-Run 'goinstall foo/bar' to build and install the package 'foo/bar'
-(and its dependencies).
-Goinstall will search each GOPATH (in order) for 'src/foo/bar'.
-If the directory cannot be found, goinstall will attempt to fetch the
-source from a remote repository and write it to the 'src' directory of the
-first GOPATH (or $GOROOT/src/pkg if GOPATH is not set).
-
-Goinstall recognizes relative and absolute paths (paths beginning with / or .).
-The following commands would build our example packages:
-
- goinstall /home/user/gocode/src/foo/bar # build and install foo/bar
- cd /home/user/gocode/src/foo
- goinstall ./bar # build and install foo/bar (again)
- cd qux
- goinstall . # build and install foo/qux
-
-*/
-package documentation
diff --git a/src/cmd/goinstall/download.go b/src/cmd/goinstall/download.go
deleted file mode 100644
index cc873150a..000000000
--- a/src/cmd/goinstall/download.go
+++ /dev/null
@@ -1,353 +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.
-
-// Download remote packages.
-
-package main
-
-import (
- "bytes"
- "exec"
- "fmt"
- "http"
- "os"
- "path/filepath"
- "regexp"
- "runtime"
- "strconv"
- "strings"
-)
-
-const dashboardURL = "http://godashboard.appspot.com/package"
-
-// maybeReportToDashboard reports path to dashboard unless
-// -dashboard=false is on command line. It ignores errors.
-func maybeReportToDashboard(path string) {
- // if -dashboard=false was on command line, do nothing
- if !*reportToDashboard {
- return
- }
-
- // otherwise lob url to dashboard
- r, _ := http.Post(dashboardURL, "application/x-www-form-urlencoded", strings.NewReader("path="+path))
- if r != nil && r.Body != nil {
- r.Body.Close()
- }
-}
-
-// a vcs represents a version control system
-// like Mercurial, Git, or Subversion.
-type vcs struct {
- name string
- cmd string
- metadir string
- checkout string
- clone string
- update string
- updateRevFlag string
- pull string
- pullForceFlag string
- tagList string
- tagListRe *regexp.Regexp
- check string
- protocols []string
- suffix string
- defaultHosts []host
-}
-
-type host struct {
- pattern *regexp.Regexp
- protocol string
- suffix string
-}
-
-var hg = vcs{
- name: "Mercurial",
- cmd: "hg",
- metadir: ".hg",
- checkout: "checkout",
- clone: "clone",
- update: "update",
- pull: "pull",
- tagList: "tags",
- tagListRe: regexp.MustCompile("([^ ]+)[^\n]+\n"),
- check: "identify",
- protocols: []string{"https", "http"},
- suffix: ".hg",
- defaultHosts: []host{
- {regexp.MustCompile(`^([a-z0-9\-]+\.googlecode\.com/hg)(/[a-z0-9A-Z_.\-/]*)?$`), "https", ""},
- {regexp.MustCompile(`^(bitbucket\.org/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]*)?$`), "http", ""},
- },
-}
-
-var git = vcs{
- name: "Git",
- cmd: "git",
- metadir: ".git",
- checkout: "checkout",
- clone: "clone",
- update: "pull",
- pull: "fetch",
- tagList: "tag",
- tagListRe: regexp.MustCompile("([^\n]+)\n"),
- check: "ls-remote",
- protocols: []string{"git", "https", "http"},
- suffix: ".git",
- defaultHosts: []host{
- {regexp.MustCompile(`^([a-z0-9\-]+\.googlecode\.com/git)(/[a-z0-9A-Z_.\-/]*)?$`), "https", ""},
- {regexp.MustCompile(`^(github\.com/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]*)?$`), "http", ".git"},
- },
-}
-
-var svn = vcs{
- name: "Subversion",
- cmd: "svn",
- metadir: ".svn",
- checkout: "checkout",
- clone: "checkout",
- update: "update",
- check: "info",
- protocols: []string{"https", "http", "svn"},
- suffix: ".svn",
- defaultHosts: []host{
- {regexp.MustCompile(`^([a-z0-9\-]+\.googlecode\.com/svn)(/[a-z0-9A-Z_.\-/]*)?$`), "https", ""},
- },
-}
-
-var bzr = vcs{
- name: "Bazaar",
- cmd: "bzr",
- metadir: ".bzr",
- checkout: "update",
- clone: "branch",
- update: "update",
- updateRevFlag: "-r",
- pull: "pull",
- pullForceFlag: "--overwrite",
- tagList: "tags",
- tagListRe: regexp.MustCompile("([^ ]+)[^\n]+\n"),
- check: "info",
- protocols: []string{"https", "http", "bzr"},
- suffix: ".bzr",
- defaultHosts: []host{
- {regexp.MustCompile(`^(launchpad\.net/([a-z0-9A-Z_.\-]+(/[a-z0-9A-Z_.\-]+)?|~[a-z0-9A-Z_.\-]+/(\+junk|[a-z0-9A-Z_.\-]+)/[a-z0-9A-Z_.\-]+))(/[a-z0-9A-Z_.\-/]+)?$`), "https", ""},
- },
-}
-
-var vcsList = []*vcs{&git, &hg, &bzr, &svn}
-
-type vcsMatch struct {
- *vcs
- prefix, repo string
-}
-
-// findPublicRepo checks whether pkg is located at one of
-// the supported code hosting sites and, if so, returns a match.
-func findPublicRepo(pkg string) (*vcsMatch, os.Error) {
- for _, v := range vcsList {
- for _, host := range v.defaultHosts {
- if hm := host.pattern.FindStringSubmatch(pkg); hm != nil {
- if host.suffix != "" && strings.HasSuffix(hm[1], host.suffix) {
- return nil, os.NewError("repository " + pkg + " should not have " + v.suffix + " suffix")
- }
- repo := host.protocol + "://" + hm[1] + host.suffix
- return &vcsMatch{v, hm[1], repo}, nil
- }
- }
- }
- return nil, nil
-}
-
-// findAnyRepo looks for a vcs suffix in pkg (.git, etc) and returns a match.
-func findAnyRepo(pkg string) (*vcsMatch, os.Error) {
- for _, v := range vcsList {
- i := strings.Index(pkg+"/", v.suffix+"/")
- if i < 0 {
- continue
- }
- if !strings.Contains(pkg[:i], "/") {
- continue // don't match vcs suffix in the host name
- }
- if m := v.find(pkg[:i]); m != nil {
- return m, nil
- }
- return nil, fmt.Errorf("couldn't find %s repository", v.name)
- }
- return nil, nil
-}
-
-func (v *vcs) find(pkg string) *vcsMatch {
- for _, proto := range v.protocols {
- for _, suffix := range []string{"", v.suffix} {
- repo := proto + "://" + pkg + suffix
- out, err := exec.Command(v.cmd, v.check, repo).CombinedOutput()
- if err == nil {
- printf("find %s: found %s\n", pkg, repo)
- return &vcsMatch{v, pkg + v.suffix, repo}
- }
- printf("find %s: %s %s %s: %v\n%s\n", pkg, v.cmd, v.check, repo, err, out)
- }
- }
- return nil
-}
-
-// isRemote returns true if the first part of the package name looks like a
-// hostname - i.e. contains at least one '.' and the last part is at least 2
-// characters.
-func isRemote(pkg string) bool {
- parts := strings.SplitN(pkg, "/", 2)
- if len(parts) != 2 {
- return false
- }
- parts = strings.Split(parts[0], ".")
- if len(parts) < 2 || len(parts[len(parts)-1]) < 2 {
- return false
- }
- return true
-}
-
-// download checks out or updates pkg from the remote server.
-func download(pkg, srcDir string) (public bool, err os.Error) {
- if strings.Contains(pkg, "..") {
- err = os.NewError("invalid path (contains ..)")
- return
- }
- m, err := findPublicRepo(pkg)
- if err != nil {
- return
- }
- if m != nil {
- public = true
- } else {
- m, err = findAnyRepo(pkg)
- if err != nil {
- return
- }
- }
- if m == nil {
- err = os.NewError("cannot download: " + pkg)
- return
- }
- err = m.checkoutRepo(srcDir, m.prefix, m.repo)
- return
-}
-
-// updateRepo gets a list of tags in the repository and
-// checks out the tag closest to the current runtime.Version.
-// If no matching tag is found, it just updates to tip.
-func (v *vcs) updateRepo(dst string) os.Error {
- if v.tagList == "" || v.tagListRe == nil {
- // TODO(adg): fix for svn
- return run(dst, nil, v.cmd, v.update)
- }
-
- // Get tag list.
- stderr := new(bytes.Buffer)
- cmd := exec.Command(v.cmd, v.tagList)
- cmd.Dir = dst
- cmd.Stderr = stderr
- b, err := cmd.Output()
- if err != nil {
- errorf("%s %s: %s\n", v.cmd, v.tagList, stderr)
- return err
- }
- var tags []string
- for _, m := range v.tagListRe.FindAllStringSubmatch(string(b), -1) {
- tags = append(tags, m[1])
- }
-
- // Only use the tag component of runtime.Version.
- ver := strings.Split(runtime.Version(), " ")[0]
-
- // Select tag.
- if tag := selectTag(ver, tags); tag != "" {
- printf("selecting revision %q\n", tag)
- return run(dst, nil, v.cmd, v.checkout, v.updateRevFlag+tag)
- }
-
- // No matching tag found, make default selection.
- printf("selecting tip\n")
- return run(dst, nil, v.cmd, v.update)
-}
-
-// selectTag returns the closest matching tag for a given version.
-// Closest means the latest one that is not after the current release.
-// Version "release.rN" matches tags of the form "go.rN" (N being a decimal).
-// Version "weekly.YYYY-MM-DD" matches tags like "go.weekly.YYYY-MM-DD".
-func selectTag(goVersion string, tags []string) (match string) {
- const rPrefix = "release.r"
- if strings.HasPrefix(goVersion, rPrefix) {
- p := "go.r"
- v, err := strconv.Atof64(goVersion[len(rPrefix):])
- if err != nil {
- return ""
- }
- var matchf float64
- for _, t := range tags {
- if !strings.HasPrefix(t, p) {
- continue
- }
- tf, err := strconv.Atof64(t[len(p):])
- if err != nil {
- continue
- }
- if matchf < tf && tf <= v {
- match, matchf = t, tf
- }
- }
- }
- const wPrefix = "weekly."
- if strings.HasPrefix(goVersion, wPrefix) {
- p := "go.weekly."
- v := goVersion[len(wPrefix):]
- for _, t := range tags {
- if !strings.HasPrefix(t, p) {
- continue
- }
- if match < t && t[len(p):] <= v {
- match = t
- }
- }
- }
- return match
-}
-
-// checkoutRepo checks out repo into dst using vcs.
-// It tries to check out (or update, if the dst already
-// exists and -u was specified on the command line)
-// the repository at tag/branch "release". If there is no
-// such tag or branch, it falls back to the repository tip.
-func (vcs *vcs) checkoutRepo(srcDir, pkgprefix, repo string) os.Error {
- dst := filepath.Join(srcDir, filepath.FromSlash(pkgprefix))
- dir, err := os.Stat(filepath.Join(dst, vcs.metadir))
- if err == nil && !dir.IsDirectory() {
- return os.NewError("not a directory: " + dst)
- }
- if err != nil {
- parent, _ := filepath.Split(dst)
- if err = os.MkdirAll(parent, 0777); err != nil {
- return err
- }
- if err = run(string(filepath.Separator), nil, vcs.cmd, vcs.clone, repo, dst); err != nil {
- return err
- }
- return vcs.updateRepo(dst)
- }
- if *update {
- // Retrieve new revisions from the remote branch, if the VCS
- // supports this operation independently (e.g. svn doesn't)
- if vcs.pull != "" {
- if vcs.pullForceFlag != "" {
- if err = run(dst, nil, vcs.cmd, vcs.pull, vcs.pullForceFlag); err != nil {
- return err
- }
- } else if err = run(dst, nil, vcs.cmd, vcs.pull); err != nil {
- return err
- }
- }
- // Update to release or latest revision
- return vcs.updateRepo(dst)
- }
- return nil
-}
diff --git a/src/cmd/goinstall/main.go b/src/cmd/goinstall/main.go
deleted file mode 100644
index acda6efbb..000000000
--- a/src/cmd/goinstall/main.go
+++ /dev/null
@@ -1,334 +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 main
-
-import (
- "bytes"
- "exec"
- "flag"
- "fmt"
- "go/build"
- "go/token"
- "io/ioutil"
- "os"
- "path/filepath"
- "regexp"
- "runtime"
- "strings"
-)
-
-func usage() {
- fmt.Fprint(os.Stderr, "usage: goinstall importpath...\n")
- fmt.Fprintf(os.Stderr, "\tgoinstall -a\n")
- flag.PrintDefaults()
- os.Exit(2)
-}
-
-const logfile = "goinstall.log"
-
-var (
- fset = token.NewFileSet()
- argv0 = os.Args[0]
- errors = false
- parents = make(map[string]string)
- visit = make(map[string]status)
- installedPkgs = make(map[string]map[string]bool)
- schemeRe = regexp.MustCompile(`^[a-z]+://`)
-
- allpkg = flag.Bool("a", false, "install all previously installed packages")
- reportToDashboard = flag.Bool("dashboard", true, "report public packages at "+dashboardURL)
- update = flag.Bool("u", false, "update already-downloaded packages")
- doInstall = flag.Bool("install", true, "build and install")
- clean = flag.Bool("clean", false, "clean the package directory before installing")
- nuke = flag.Bool("nuke", false, "clean the package directory and target before installing")
- useMake = flag.Bool("make", true, "use make to build and install")
- verbose = flag.Bool("v", false, "verbose")
-)
-
-type status int // status for visited map
-const (
- unvisited status = iota
- visiting
- done
-)
-
-func logf(format string, args ...interface{}) {
- format = "%s: " + format
- args = append([]interface{}{argv0}, args...)
- fmt.Fprintf(os.Stderr, format, args...)
-}
-
-func printf(format string, args ...interface{}) {
- if *verbose {
- logf(format, args...)
- }
-}
-
-func errorf(format string, args ...interface{}) {
- errors = true
- logf(format, args...)
-}
-
-func terrorf(tree *build.Tree, format string, args ...interface{}) {
- if tree != nil && tree.Goroot && os.Getenv("GOPATH") == "" {
- format = strings.TrimRight(format, "\n") + " ($GOPATH not set)\n"
- }
- errorf(format, args...)
-}
-
-func main() {
- flag.Usage = usage
- flag.Parse()
- if runtime.GOROOT() == "" {
- fmt.Fprintf(os.Stderr, "%s: no $GOROOT\n", argv0)
- os.Exit(1)
- }
- readPackageList()
-
- // special case - "unsafe" is already installed
- visit["unsafe"] = done
-
- args := flag.Args()
- if *allpkg {
- if len(args) != 0 {
- usage() // -a and package list both provided
- }
- // install all packages that were ever installed
- n := 0
- for _, pkgs := range installedPkgs {
- for pkg := range pkgs {
- args = append(args, pkg)
- n++
- }
- }
- if n == 0 {
- logf("no installed packages\n")
- os.Exit(1)
- }
- }
- if len(args) == 0 {
- usage()
- }
- for _, path := range args {
- if s := schemeRe.FindString(path); s != "" {
- errorf("%q used in import path, try %q\n", s, path[len(s):])
- continue
- }
-
- install(path, "")
- }
- if errors {
- os.Exit(1)
- }
-}
-
-// printDeps prints the dependency path that leads to pkg.
-func printDeps(pkg string) {
- if pkg == "" {
- return
- }
- if visit[pkg] != done {
- printDeps(parents[pkg])
- }
- fmt.Fprintf(os.Stderr, "\t%s ->\n", pkg)
-}
-
-// readPackageList reads the list of installed packages from the
-// goinstall.log files in GOROOT and the GOPATHs and initalizes
-// the installedPkgs variable.
-func readPackageList() {
- for _, t := range build.Path {
- installedPkgs[t.Path] = make(map[string]bool)
- name := filepath.Join(t.Path, logfile)
- pkglistdata, err := ioutil.ReadFile(name)
- if err != nil {
- printf("%s\n", err)
- continue
- }
- pkglist := strings.Fields(string(pkglistdata))
- for _, pkg := range pkglist {
- installedPkgs[t.Path][pkg] = true
- }
- }
-}
-
-// logPackage logs the named package as installed in the goinstall.log file
-// in the given tree if the package is not already in that file.
-func logPackage(pkg string, tree *build.Tree) (logged bool) {
- if installedPkgs[tree.Path][pkg] {
- return false
- }
- name := filepath.Join(tree.Path, logfile)
- fout, err := os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
- if err != nil {
- terrorf(tree, "package log: %s\n", err)
- return false
- }
- fmt.Fprintf(fout, "%s\n", pkg)
- fout.Close()
- return true
-}
-
-// install installs the package named by path, which is needed by parent.
-func install(pkg, parent string) {
- // Make sure we're not already trying to install pkg.
- switch visit[pkg] {
- case done:
- return
- case visiting:
- fmt.Fprintf(os.Stderr, "%s: package dependency cycle\n", argv0)
- printDeps(parent)
- fmt.Fprintf(os.Stderr, "\t%s\n", pkg)
- os.Exit(2)
- }
- parents[pkg] = parent
- visit[pkg] = visiting
- defer func() {
- visit[pkg] = done
- }()
-
- // Don't allow trailing '/'
- if _, f := filepath.Split(pkg); f == "" {
- errorf("%s should not have trailing '/'\n", pkg)
- return
- }
-
- // Check whether package is local or remote.
- // If remote, download or update it.
- tree, pkg, err := build.FindTree(pkg)
- // Don't build the standard library.
- if err == nil && tree.Goroot && isStandardPath(pkg) {
- if parent == "" {
- errorf("%s: can not goinstall the standard library\n", pkg)
- } else {
- printf("%s: skipping standard library\n", pkg)
- }
- return
- }
- // Download remote packages if not found or forced with -u flag.
- remote, public := isRemote(pkg), false
- if remote {
- if err == build.ErrNotFound || (err == nil && *update) {
- // Download remote package.
- printf("%s: download\n", pkg)
- public, err = download(pkg, tree.SrcDir())
- } else {
- // Test if this is a public repository
- // (for reporting to dashboard).
- m, _ := findPublicRepo(pkg)
- public = m != nil
- }
- }
- if err != nil {
- terrorf(tree, "%s: %v\n", pkg, err)
- return
- }
- dir := filepath.Join(tree.SrcDir(), pkg)
-
- // Install prerequisites.
- dirInfo, err := build.ScanDir(dir, parent == "")
- if err != nil {
- terrorf(tree, "%s: %v\n", pkg, err)
- return
- }
- if len(dirInfo.GoFiles)+len(dirInfo.CgoFiles) == 0 {
- terrorf(tree, "%s: package has no files\n", pkg)
- return
- }
- for _, p := range dirInfo.Imports {
- if p != "C" {
- install(p, pkg)
- }
- }
- if errors {
- return
- }
-
- // Install this package.
- if *useMake {
- err := domake(dir, pkg, tree, dirInfo.IsCommand())
- if err != nil {
- terrorf(tree, "%s: install: %v\n", pkg, err)
- return
- }
- } else {
- script, err := build.Build(tree, pkg, dirInfo)
- if err != nil {
- terrorf(tree, "%s: install: %v\n", pkg, err)
- return
- }
- if *nuke {
- printf("%s: nuke\n", pkg)
- script.Nuke()
- } else if *clean {
- printf("%s: clean\n", pkg)
- script.Clean()
- }
- if *doInstall {
- if script.Stale() {
- printf("%s: install\n", pkg)
- if err := script.Run(); err != nil {
- terrorf(tree, "%s: install: %v\n", pkg, err)
- return
- }
- } else {
- printf("%s: up-to-date\n", pkg)
- }
- }
- }
-
- if remote {
- // mark package as installed in goinstall.log
- logged := logPackage(pkg, tree)
-
- // report installation to the dashboard if this is the first
- // install from a public repository.
- if logged && public {
- maybeReportToDashboard(pkg)
- }
- }
-}
-
-// Is this a standard package path? strings container/vector etc.
-// Assume that if the first element has a dot, it's a domain name
-// and is not the standard package path.
-func isStandardPath(s string) bool {
- dot := strings.Index(s, ".")
- slash := strings.Index(s, "/")
- return dot < 0 || 0 < slash && slash < dot
-}
-
-// run runs the command cmd in directory dir with standard input stdin.
-// If the command fails, run prints the command and output on standard error
-// in addition to returning a non-nil os.Error.
-func run(dir string, stdin []byte, cmd ...string) os.Error {
- return genRun(dir, stdin, cmd, false)
-}
-
-// quietRun is like run but prints nothing on failure unless -v is used.
-func quietRun(dir string, stdin []byte, cmd ...string) os.Error {
- return genRun(dir, stdin, cmd, true)
-}
-
-// genRun implements run and quietRun.
-func genRun(dir string, stdin []byte, arg []string, quiet bool) os.Error {
- cmd := exec.Command(arg[0], arg[1:]...)
- cmd.Stdin = bytes.NewBuffer(stdin)
- cmd.Dir = dir
- printf("%s: %s %s\n", dir, cmd.Path, strings.Join(arg[1:], " "))
- out, err := cmd.CombinedOutput()
- if err != nil {
- if !quiet || *verbose {
- if dir != "" {
- dir = "cd " + dir + "; "
- }
- fmt.Fprintf(os.Stderr, "%s: === %s%s\n", cmd.Path, dir, strings.Join(cmd.Args, " "))
- os.Stderr.Write(out)
- fmt.Fprintf(os.Stderr, "--- %s\n", err)
- }
- return os.NewError("running " + arg[0] + ": " + err.String())
- }
- return nil
-}
diff --git a/src/cmd/goinstall/make.go b/src/cmd/goinstall/make.go
deleted file mode 100644
index 38a70ddfd..000000000
--- a/src/cmd/goinstall/make.go
+++ /dev/null
@@ -1,175 +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.
-
-// Run "make install" to build package.
-
-package main
-
-import (
- "bytes"
- "go/build"
- "os"
- "path/filepath"
- "strings"
- "template"
-)
-
-// domake builds the package in dir.
-// domake generates a standard Makefile and passes it
-// to make on standard input.
-func domake(dir, pkg string, tree *build.Tree, isCmd bool) (err os.Error) {
- makefile, err := makeMakefile(dir, pkg, tree, isCmd)
- if err != nil {
- return err
- }
- cmd := []string{"bash", "gomake", "-f-"}
- if *nuke {
- cmd = append(cmd, "nuke")
- } else if *clean {
- cmd = append(cmd, "clean")
- }
- cmd = append(cmd, "install")
- return run(dir, makefile, cmd...)
-}
-
-// makeMakefile computes the standard Makefile for the directory dir
-// installing as package pkg. It includes all *.go files in the directory
-// except those in package main and those ending in _test.go.
-func makeMakefile(dir, pkg string, tree *build.Tree, isCmd bool) ([]byte, os.Error) {
- if !safeName(pkg) {
- return nil, os.NewError("unsafe name: " + pkg)
- }
- targ := pkg
- targDir := tree.PkgDir()
- if isCmd {
- // use the last part of the package name for targ
- _, targ = filepath.Split(pkg)
- targDir = tree.BinDir()
- }
- dirInfo, err := build.ScanDir(dir, isCmd)
- if err != nil {
- return nil, err
- }
-
- cgoFiles := dirInfo.CgoFiles
- isCgo := make(map[string]bool, len(cgoFiles))
- for _, file := range cgoFiles {
- if !safeName(file) {
- return nil, os.NewError("bad name: " + file)
- }
- isCgo[file] = true
- }
-
- goFiles := make([]string, 0, len(dirInfo.GoFiles))
- for _, file := range dirInfo.GoFiles {
- if !safeName(file) {
- return nil, os.NewError("unsafe name: " + file)
- }
- if !isCgo[file] {
- goFiles = append(goFiles, file)
- }
- }
-
- oFiles := make([]string, 0, len(dirInfo.CFiles)+len(dirInfo.SFiles))
- cgoOFiles := make([]string, 0, len(dirInfo.CFiles))
- for _, file := range dirInfo.CFiles {
- if !safeName(file) {
- return nil, os.NewError("unsafe name: " + file)
- }
- // When cgo is in use, C files are compiled with gcc,
- // otherwise they're compiled with gc.
- if len(cgoFiles) > 0 {
- cgoOFiles = append(cgoOFiles, file[:len(file)-2]+".o")
- } else {
- oFiles = append(oFiles, file[:len(file)-2]+".$O")
- }
- }
-
- for _, file := range dirInfo.SFiles {
- if !safeName(file) {
- return nil, os.NewError("unsafe name: " + file)
- }
- oFiles = append(oFiles, file[:len(file)-2]+".$O")
- }
-
- var imports []string
- for _, t := range build.Path {
- imports = append(imports, t.PkgDir())
- }
-
- var buf bytes.Buffer
- md := makedata{targ, targDir, "pkg", goFiles, oFiles, cgoFiles, cgoOFiles, imports}
- if isCmd {
- md.Type = "cmd"
- }
- if err := makefileTemplate.Execute(&buf, &md); err != nil {
- return nil, err
- }
- return buf.Bytes(), nil
-}
-
-var safeBytes = []byte("+-./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz")
-
-func safeName(s string) bool {
- if s == "" {
- return false
- }
- if strings.Contains(s, "..") {
- return false
- }
- for i := 0; i < len(s); i++ {
- if c := s[i]; c < 0x80 && bytes.IndexByte(safeBytes, c) < 0 {
- return false
- }
- }
- return true
-}
-
-// makedata is the data type for the makefileTemplate.
-type makedata struct {
- Targ string // build target
- TargDir string // build target directory
- Type string // build type: "pkg" or "cmd"
- GoFiles []string // list of non-cgo .go files
- OFiles []string // list of .$O files
- CgoFiles []string // list of cgo .go files
- CgoOFiles []string // list of cgo .o files, without extension
- Imports []string // gc/ld import paths
-}
-
-var makefileTemplate = template.Must(template.New("Makefile").Parse(`
-include $(GOROOT)/src/Make.inc
-
-TARG={{.Targ}}
-TARGDIR={{.TargDir}}
-
-{{with .GoFiles}}
-GOFILES=\
-{{range .}} {{.}}\
-{{end}}
-
-{{end}}
-{{with .OFiles}}
-OFILES=\
-{{range .}} {{.}}\
-{{end}}
-
-{{end}}
-{{with .CgoFiles}}
-CGOFILES=\
-{{range .}} {{.}}\
-{{end}}
-
-{{end}}
-{{with .CgoOFiles}}
-CGO_OFILES=\
-{{range .}} {{.}}\
-{{end}}
-
-{{end}}
-GCIMPORTS={{range .Imports}}-I "{{.}}" {{end}}
-LDIMPORTS={{range .Imports}}-L "{{.}}" {{end}}
-
-include $(GOROOT)/src/Make.{{.Type}}
-`))
diff --git a/src/cmd/gomake/doc.go b/src/cmd/gomake/doc.go
deleted file mode 100644
index 2f35fd9dd..000000000
--- a/src/cmd/gomake/doc.go
+++ /dev/null
@@ -1,36 +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 gomake command runs GNU make with an appropriate environment
-for using the conventional Go makefiles. If $GOROOT is already
-set in the environment, running gomake is exactly the same
-as running make (or, on BSD systems, running gmake).
-
-Usage: gomake [ target ... ]
-
-Common targets are:
-
- all (default)
- build the package or command, but do not install it.
-
- install
- build and install the package or command
-
- test
- run the tests (packages only)
-
- bench
- run benchmarks (packages only)
-
- clean
- remove object files from the current directory
-
- nuke
- make clean and remove the installed package or command
-
-See http://golang.org/doc/code.html for information about
-writing makefiles.
-*/
-package documentation
diff --git a/src/cmd/gopack/Makefile b/src/cmd/gopack/Makefile
deleted file mode 100644
index 91a8ac2df..000000000
--- a/src/cmd/gopack/Makefile
+++ /dev/null
@@ -1,12 +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 ../../Make.inc
-O:=$(HOST_O)
-
-TARG=gopack
-OFILES=\
- ar.$O\
-
-include ../../Make.ccmd
diff --git a/src/cmd/gotest/doc.go b/src/cmd/gotest/doc.go
deleted file mode 100644
index 5be06f817..000000000
--- a/src/cmd/gotest/doc.go
+++ /dev/null
@@ -1,113 +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.
-
-/*
-
-Gotest is an automated testing tool for Go packages.
-
-Normally a Go package is compiled without its test files. Gotest is a
-tool that recompiles the package whose source is in the current
-directory, along with any files whose names match the pattern
-"[^.]*_test.go". Functions in the test source named TestXXX (where
-XXX is any alphanumeric string not starting with a lower case letter)
-will be run when the binary is executed. Gotest requires that the
-package have a standard package Makefile, one that includes
-go/src/Make.pkg.
-
-The test functions are run in the order they appear in the source.
-They should have the signature,
-
- func TestXXX(t *testing.T) { ... }
-
-Benchmark functions can be written as well; they will be run only when
-the -test.bench flag is provided. Benchmarks should have the
-signature,
-
- func BenchmarkXXX(b *testing.B) { ... }
-
-See the documentation of the testing package for more information.
-
-By default, gotest needs no arguments. It compiles all the .go files
-in the directory, including tests, and runs the tests. If file names
-are given (with flag -file=test.go, one per extra test source file),
-only those test files are added to the package. (The non-test files
-are always compiled.)
-
-The package is built in a special subdirectory so it does not
-interfere with the non-test installation.
-
-Usage:
- gotest [-file a.go -file b.go ...] [-c] [-x] [args for test binary]
-
-The flags specific to gotest are:
- -c Compile the test binary but do not run it.
- -file a.go Use only the tests in the source file a.go.
- Multiple -file flags may be provided.
- -x Print each subcommand gotest executes.
-
-Everything else on the command line is passed to the test binary.
-
-The resulting test binary, called (for amd64) 6.out, has several flags.
-
-Usage:
- 6.out [-test.v] [-test.run pattern] [-test.bench pattern] \
- [-test.cpuprofile=cpu.out] \
- [-test.memprofile=mem.out] [-test.memprofilerate=1] \
- [-test.timeout=10] [-test.short] \
- [-test.benchtime=3] [-test.cpu=1,2,3,4]
-
-The -test.v flag causes the tests to be logged as they run. The
--test.run flag causes only those tests whose names match the regular
-expression pattern to be run. By default all tests are run silently.
-
-If all specified tests pass, 6.out prints the word PASS and exits with
-a 0 exit code. If any tests fail, it prints error details, the word
-FAIL, and exits with a non-zero code. The -test.bench flag is
-analogous to the -test.run flag, but applies to benchmarks. No
-benchmarks run by default.
-
-The -test.cpuprofile flag causes the testing software to write a CPU
-profile to the specified file before exiting.
-
-The -test.memprofile flag causes the testing software to write a
-memory profile to the specified file when all tests are complete. The
--test.memprofilerate flag enables more precise (and expensive)
-profiles by setting runtime.MemProfileRate; run
- godoc runtime MemProfileRate
-for details. The defaults are no memory profile and the standard
-setting of MemProfileRate. The memory profile records a sampling of
-the memory in use at the end of the test. To profile all memory
-allocations, use -test.memprofilerate=1 to sample every byte and set
-the environment variable GOGC=off to disable the garbage collector,
-provided the test can run in the available memory without garbage
-collection.
-
-Use -test.run or -test.bench to limit profiling to a particular test
-or benchmark.
-
-The -test.short flag tells long-running tests to shorten their run
-time. It is off by default but set by all.bash so installations of
-the Go tree can do a sanity check but not spend time running
-exhaustive tests.
-
-The -test.timeout flag sets a timeout for the test in seconds. If the
-test runs for longer than that, it will panic, dumping a stack trace
-of all existing goroutines.
-
-The -test.benchtime flag specifies the number of seconds to run each benchmark.
-The default is one second.
-
-The -test.cpu flag specifies a list of GOMAXPROCS values for which
-the tests or benchmarks are executed. The default is the current
-value of GOMAXPROCS.
-
-For convenience, each of these -test.X flags of the test binary is
-also available as the flag -X in gotest itself. Flags not listed here
-are unaffected. For instance, the command
- gotest -x -v -cpuprofile=prof.out -dir=testdata -update -file x_test.go
-will compile the test binary using x_test.go and then run it as
- 6.out -test.v -test.cpuprofile=prof.out -dir=testdata -update
-
-*/
-package documentation
diff --git a/src/cmd/gotest/flag.go b/src/cmd/gotest/flag.go
deleted file mode 100644
index c3a28f9a3..000000000
--- a/src/cmd/gotest/flag.go
+++ /dev/null
@@ -1,159 +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 (
- "fmt"
- "os"
- "strconv"
- "strings"
-)
-
-// The flag handling part of gotest is large and distracting.
-// We can't use the flag package because some of the flags from
-// our command line are for us, and some are for 6.out, and
-// some are for both.
-
-var usageMessage = `Usage of %s:
- -c=false: compile but do not run the test binary
- -file=file:
- -x=false: print command lines as they are executed
-
- // These flags can be passed with or without a "test." prefix: -v or -test.v.
- -bench="": passes -test.bench to test
- -benchtime=1: passes -test.benchtime to test
- -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
- -run="": passes -test.run to test
- -short=false: passes -test.short to test
- -timeout=0: passes -test.timeout to test
- -v=false: passes -test.v to test
-`
-
-// usage prints a usage message and exits.
-func usage() {
- fmt.Fprintf(os.Stdout, usageMessage, os.Args[0])
- os.Exit(2)
-}
-
-// flagSpec defines a flag we know about.
-type flagSpec struct {
- name string
- isBool bool
- passToTest bool // pass to Test
- multiOK bool // OK to have multiple instances
- present bool // flag has been seen
-}
-
-// flagDefn is the set of flags we process.
-var flagDefn = []*flagSpec{
- // gotest-local.
- &flagSpec{name: "c", isBool: true},
- &flagSpec{name: "file", multiOK: true},
- &flagSpec{name: "x", isBool: true},
-
- // passed to 6.out, adding a "test." prefix to the name if necessary: -v becomes -test.v.
- &flagSpec{name: "bench", passToTest: true},
- &flagSpec{name: "benchtime", passToTest: true},
- &flagSpec{name: "cpu", passToTest: true},
- &flagSpec{name: "cpuprofile", passToTest: true},
- &flagSpec{name: "memprofile", passToTest: true},
- &flagSpec{name: "memprofilerate", passToTest: true},
- &flagSpec{name: "run", passToTest: true},
- &flagSpec{name: "short", isBool: true, passToTest: true},
- &flagSpec{name: "timeout", passToTest: true},
- &flagSpec{name: "v", isBool: true, passToTest: true},
-}
-
-// flags processes the command line, grabbing -x and -c, rewriting known flags
-// to have "test" before them, and reading the command line for the 6.out.
-// Unfortunately for us, we need to do our own flag processing because gotest
-// grabs some flags but otherwise its command line is just a holding place for
-// 6.out's arguments.
-func flags() {
- for i := 1; i < len(os.Args); i++ {
- arg := os.Args[i]
- f, value, extraWord := flag(i)
- if f == nil {
- args = append(args, arg)
- continue
- }
- switch f.name {
- case "c":
- setBoolFlag(&cFlag, value)
- case "x":
- setBoolFlag(&xFlag, value)
- case "file":
- fileNames = append(fileNames, value)
- }
- if extraWord {
- i++
- }
- if f.passToTest {
- args = append(args, "-test."+f.name+"="+value)
- }
- }
-}
-
-// flag sees if argument i is a known flag and returns its definition, value, and whether it consumed an extra word.
-func flag(i int) (f *flagSpec, value string, extra bool) {
- arg := os.Args[i]
- if strings.HasPrefix(arg, "--") { // reduce two minuses to one
- arg = arg[1:]
- }
- if arg == "" || arg[0] != '-' {
- return
- }
- name := arg[1:]
- // If there's already "test.", drop it for now.
- if strings.HasPrefix(name, "test.") {
- name = name[5:]
- }
- equals := strings.Index(name, "=")
- if equals >= 0 {
- value = name[equals+1:]
- name = name[:equals]
- }
- for _, f = range flagDefn {
- if name == f.name {
- // Booleans are special because they have modes -x, -x=true, -x=false.
- if f.isBool {
- if equals < 0 { // otherwise, it's been set and will be verified in setBoolFlag
- value = "true"
- } else {
- // verify it parses
- setBoolFlag(new(bool), value)
- }
- } else { // Non-booleans must have a value.
- extra = equals < 0
- if extra {
- if i+1 >= len(os.Args) {
- usage()
- }
- value = os.Args[i+1]
- }
- }
- if f.present && !f.multiOK {
- usage()
- }
- f.present = true
- return
- }
- }
- f = nil
- return
-}
-
-// setBoolFlag sets the addressed boolean to the value.
-func setBoolFlag(flag *bool, value string) {
- x, err := strconv.Atob(value)
- if err != nil {
- fmt.Fprintf(os.Stderr, "gotest: illegal bool flag value %s\n", value)
- usage()
- }
- *flag = x
-}
diff --git a/src/cmd/gotest/gotest.go b/src/cmd/gotest/gotest.go
deleted file mode 100644
index 4cb3da23c..000000000
--- a/src/cmd/gotest/gotest.go
+++ /dev/null
@@ -1,435 +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 (
- "bufio"
- "exec"
- "fmt"
- "go/ast"
- "go/parser"
- "go/token"
- "io/ioutil"
- "os"
- "path/filepath"
- "runtime"
- "strings"
- "time"
- "unicode"
- "utf8"
-)
-
-// Environment for commands.
-var (
- XGC []string // 6g -I _test -o _xtest_.6
- GC []string // 6g -I _test _testmain.go
- GL []string // 6l -L _test _testmain.6
- GOARCH string
- GOROOT string
- GORUN string
- O string
- args []string // arguments passed to gotest; also passed to the binary
- fileNames []string
- env = os.Environ()
-)
-
-// These strings are created by getTestNames.
-var (
- insideFileNames []string // list of *.go files inside the package.
- outsideFileNames []string // list of *.go files outside the package (in package foo_test).
-)
-
-var (
- files []*File
- importPath string
-)
-
-// Flags for our own purposes. We do our own flag processing.
-var (
- cFlag bool
- xFlag bool
-)
-
-// elapsed returns the number of seconds since gotest started.
-func elapsed() float64 {
- return float64(time.Nanoseconds()-start) / 1e9
-}
-
-var start = time.Nanoseconds()
-
-// File represents a file that contains tests.
-type File struct {
- name string
- pkg string
- file *os.File
- astFile *ast.File
- tests []string // The names of the TestXXXs.
- benchmarks []string // The names of the BenchmarkXXXs.
-}
-
-func main() {
- flags()
- needMakefile()
- setEnvironment()
- getTestFileNames()
- parseFiles()
- getTestNames()
- run("gomake", "testpackage-clean")
- run("gomake", "testpackage", fmt.Sprintf("GOTESTFILES=%s", strings.Join(insideFileNames, " ")))
- if len(outsideFileNames) > 0 {
- run(append(XGC, outsideFileNames...)...)
- }
- importPath = runWithStdout("gomake", "-s", "importpath")
- writeTestmainGo()
- run(GC...)
- run(GL...)
- if !cFlag {
- runTestWithArgs("./" + O + ".out")
- }
- if xFlag {
- fmt.Printf("gotest %.2fs: done\n", elapsed())
- }
-}
-
-// needMakefile tests that we have a Makefile in this directory.
-func needMakefile() {
- if _, err := os.Stat("Makefile"); err != nil {
- Fatalf("please create a Makefile for gotest; see http://golang.org/doc/code.html for details")
- }
-}
-
-// Fatalf formats its arguments, prints the message with a final newline, and exits.
-func Fatalf(s string, args ...interface{}) {
- fmt.Fprintf(os.Stderr, "gotest: "+s+"\n", args...)
- os.Exit(2)
-}
-
-// theChar is the map from architecture to object character.
-var theChar = map[string]string{
- "arm": "5",
- "amd64": "6",
- "386": "8",
-}
-
-// addEnv adds a name=value pair to the environment passed to subcommands.
-// If the item is already in the environment, addEnv replaces the value.
-func addEnv(name, value string) {
- for i := 0; i < len(env); i++ {
- if strings.HasPrefix(env[i], name+"=") {
- env[i] = name + "=" + value
- return
- }
- }
- env = append(env, name+"="+value)
-}
-
-// setEnvironment assembles the configuration for gotest and its subcommands.
-func setEnvironment() {
- // Basic environment.
- GOROOT = runtime.GOROOT()
- addEnv("GOROOT", GOROOT)
- GOARCH = os.Getenv("GOARCH")
- if GOARCH == "" {
- GOARCH = runtime.GOARCH
- }
- addEnv("GOARCH", GOARCH)
- O = theChar[GOARCH]
- if O == "" {
- Fatalf("unknown architecture %s", GOARCH)
- }
-
- // Commands and their flags.
- gc := os.Getenv("GC")
- if gc == "" {
- gc = O + "g"
- }
- XGC = []string{gc, "-I", "_test", "-o", "_xtest_." + O}
- GC = []string{gc, "-I", "_test", "_testmain.go"}
- gl := os.Getenv("GL")
- if gl == "" {
- gl = O + "l"
- }
- GL = []string{gl, "-L", "_test", "_testmain." + O}
-
- // Silence make on Linux
- addEnv("MAKEFLAGS", "")
- addEnv("MAKELEVEL", "")
-}
-
-// getTestFileNames gets the set of files we're looking at.
-// If gotest has no arguments, it scans for file names matching "[^.]*_test.go".
-func getTestFileNames() {
- names := fileNames
- if len(names) == 0 {
- var err os.Error
- names, err = filepath.Glob("[^.]*_test.go")
- if err != nil {
- Fatalf("Glob pattern error: %s", err)
- }
- if len(names) == 0 {
- Fatalf(`no test files found: no match for "[^.]*_test.go"`)
- }
- }
- for _, n := range names {
- fd, err := os.Open(n)
- if err != nil {
- Fatalf("%s: %s", n, err)
- }
- f := &File{name: n, file: fd}
- files = append(files, f)
- }
-}
-
-// parseFiles parses the files and remembers the packages we find.
-func parseFiles() {
- fileSet := token.NewFileSet()
- for _, f := range files {
- // Report declaration errors so we can abort if the files are incorrect Go.
- file, err := parser.ParseFile(fileSet, f.name, nil, parser.DeclarationErrors)
- if err != nil {
- Fatalf("parse error: %s", err)
- }
- f.astFile = file
- f.pkg = file.Name.String()
- if f.pkg == "" {
- Fatalf("cannot happen: no package name in %s", f.name)
- }
- }
-}
-
-// getTestNames extracts the names of tests and benchmarks. They are all
-// top-level functions that are not methods.
-func getTestNames() {
- for _, f := range files {
- for _, d := range f.astFile.Decls {
- n, ok := d.(*ast.FuncDecl)
- if !ok {
- continue
- }
- if n.Recv != nil { // a method, not a function.
- continue
- }
- name := n.Name.String()
- if isTest(name, "Test") {
- f.tests = append(f.tests, name)
- } else if isTest(name, "Benchmark") {
- f.benchmarks = append(f.benchmarks, name)
- }
- // TODO: worth checking the signature? Probably not.
- }
- if strings.HasSuffix(f.pkg, "_test") {
- outsideFileNames = append(outsideFileNames, f.name)
- } else {
- insideFileNames = append(insideFileNames, f.name)
- }
- }
-}
-
-// isTest tells whether name looks like a test (or benchmark, according to prefix).
-// It is a Test (say) if there is a character after Test that is not a lower-case letter.
-// We don't want TesticularCancer.
-func isTest(name, prefix string) bool {
- if !strings.HasPrefix(name, prefix) {
- return false
- }
- if len(name) == len(prefix) { // "Test" is ok
- return true
- }
- rune, _ := utf8.DecodeRuneInString(name[len(prefix):])
- return !unicode.IsLower(rune)
-}
-
-func run(args ...string) {
- doRun(args, false)
-}
-
-// runWithStdout is like run, but returns the text of standard output with the last newline dropped.
-func runWithStdout(argv ...string) string {
- s := doRun(argv, true)
- if strings.HasSuffix(s, "\r\n") {
- s = s[:len(s)-2]
- } else if strings.HasSuffix(s, "\n") {
- s = s[:len(s)-1]
- }
- if len(s) == 0 {
- Fatalf("no output from command %s", strings.Join(argv, " "))
- }
- return s
-}
-
-// runTestWithArgs appends gotest's runs the provided binary with the args passed on the command line.
-func runTestWithArgs(binary string) {
- doRun(append([]string{binary}, args...), false)
-}
-
-// doRun is the general command runner. The flag says whether we want to
-// retrieve standard output.
-func doRun(argv []string, returnStdout bool) string {
- if xFlag {
- fmt.Printf("gotest %.2fs: %s\n", elapsed(), strings.Join(argv, " "))
- t := -time.Nanoseconds()
- defer func() {
- t += time.Nanoseconds()
- fmt.Printf(" [+%.2fs]\n", float64(t)/1e9)
- }()
- }
- command := argv[0]
- if runtime.GOOS == "windows" && command == "gomake" {
- // gomake is a shell script and it cannot be executed directly on Windows.
- cmd := ""
- for i, v := range argv {
- if i > 0 {
- cmd += " "
- }
- cmd += `"` + v + `"`
- }
- command = "bash"
- argv = []string{"bash", "-c", cmd}
- }
- var err os.Error
- argv[0], err = exec.LookPath(argv[0])
- if err != nil {
- Fatalf("can't find %s: %s", command, err)
- }
- procAttr := &os.ProcAttr{
- Env: env,
- Files: []*os.File{
- os.Stdin,
- os.Stdout,
- os.Stderr,
- },
- }
- var r, w *os.File
- if returnStdout {
- r, w, err = os.Pipe()
- if err != nil {
- Fatalf("can't create pipe: %s", err)
- }
- procAttr.Files[1] = w
- }
- proc, err := os.StartProcess(argv[0], argv, procAttr)
- if err != nil {
- Fatalf("%s failed to start: %s", command, err)
- }
- if returnStdout {
- defer r.Close()
- w.Close()
- }
- waitMsg, err := proc.Wait(0)
- if err != nil || waitMsg == nil {
- Fatalf("%s failed: %s", command, err)
- }
- if !waitMsg.Exited() || waitMsg.ExitStatus() != 0 {
- Fatalf("%q failed: %s", strings.Join(argv, " "), waitMsg)
- }
- if returnStdout {
- b, err := ioutil.ReadAll(r)
- if err != nil {
- Fatalf("can't read output from command: %s", err)
- }
- return string(b)
- }
- return ""
-}
-
-// writeTestmainGo generates the test program to be compiled, "./_testmain.go".
-func writeTestmainGo() {
- f, err := os.Create("_testmain.go")
- if err != nil {
- Fatalf("can't create _testmain.go: %s", err)
- }
- defer f.Close()
- b := bufio.NewWriter(f)
- defer b.Flush()
-
- // Package and imports.
- fmt.Fprint(b, "package main\n\n")
- // Are there tests from a package other than the one we're testing?
- // We can't just use file names because some of the things we compiled
- // contain no tests.
- outsideTests := false
- insideTests := false
- for _, f := range files {
- //println(f.name, f.pkg)
- if len(f.tests) == 0 && len(f.benchmarks) == 0 {
- continue
- }
- if strings.HasSuffix(f.pkg, "_test") {
- outsideTests = true
- } else {
- insideTests = true
- }
- }
- if insideTests {
- switch importPath {
- case "testing":
- case "main":
- // Import path main is reserved, so import with
- // explicit reference to ./_test/main instead.
- // Also, the file we are writing defines a function named main,
- // so rename this import to __main__ to avoid name conflict.
- fmt.Fprintf(b, "import __main__ %q\n", "./_test/main")
- default:
- fmt.Fprintf(b, "import %q\n", importPath)
- }
- }
- if outsideTests {
- fmt.Fprintf(b, "import %q\n", "./_xtest_")
- }
- fmt.Fprintf(b, "import %q\n", "testing")
- fmt.Fprintf(b, "import __os__ %q\n", "os") // rename in case tested package is called os
- fmt.Fprintf(b, "import __regexp__ %q\n", "regexp") // rename in case tested package is called regexp
- fmt.Fprintln(b) // for gofmt
-
- // Tests.
- fmt.Fprintln(b, "var tests = []testing.InternalTest{")
- for _, f := range files {
- for _, t := range f.tests {
- fmt.Fprintf(b, "\t{\"%s.%s\", %s.%s},\n", f.pkg, t, notMain(f.pkg), t)
- }
- }
- fmt.Fprintln(b, "}")
- fmt.Fprintln(b)
-
- // Benchmarks.
- fmt.Fprintf(b, "var benchmarks = []testing.InternalBenchmark{")
- for _, f := range files {
- for _, bm := range f.benchmarks {
- fmt.Fprintf(b, "\t{\"%s.%s\", %s.%s},\n", f.pkg, bm, notMain(f.pkg), bm)
- }
- }
- fmt.Fprintln(b, "}")
-
- // Body.
- fmt.Fprintln(b, testBody)
-}
-
-// notMain returns the package, renaming as appropriate if it's "main".
-func notMain(pkg string) string {
- if pkg == "main" {
- return "__main__"
- }
- return pkg
-}
-
-// testBody is just copied to the output. It's the code that runs the tests.
-var testBody = `
-var matchPat string
-var matchRe *__regexp__.Regexp
-
-func matchString(pat, str string) (result bool, err __os__.Error) {
- if matchRe == nil || matchPat != pat {
- matchPat = pat
- matchRe, err = __regexp__.Compile(matchPat)
- if err != nil {
- return
- }
- }
- return matchRe.MatchString(str), nil
-}
-
-func main() {
- testing.Main(matchString, tests, benchmarks)
-}`
diff --git a/src/cmd/gotry/Makefile b/src/cmd/gotry/Makefile
deleted file mode 100644
index 6a32bbf2d..000000000
--- a/src/cmd/gotry/Makefile
+++ /dev/null
@@ -1,18 +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.
-
-include ../../Make.inc
-
-TARG=install
-
-clean:
- @true
-
-install: install-gotry
-
-install-%: %
- ! test -f "$(GOBIN)"/$* || chmod u+w "$(GOBIN)"/$*
- sed 's`@@GOROOT@@`$(GOROOT_FINAL)`' $* >"$(GOBIN)"/$*
- chmod +x "$(GOBIN)"/$*
-
diff --git a/src/cmd/gotry/gotry b/src/cmd/gotry/gotry
deleted file mode 100755
index 3cc7a9864..000000000
--- a/src/cmd/gotry/gotry
+++ /dev/null
@@ -1,168 +0,0 @@
-#!/usr/bin/env bash
-# Copyright 2010 The Go Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file.
-
-# Using all the non-test *.go files in the named directory, write
-# out a file /tmp/$USER.try.go to evaluate the expressions on the
-# command line, perhaps to discover a function or method that
-# gives the desired results. See usage message.
-# Compile the program and run it.
-
-# Makes egrep,grep work better in general if we put them
-# in ordinary C mode instead of what the current language is.
-unset LANG
-export LC_ALL=C
-export LC_CTYPE=C
-
-export GOROOT=${GOROOT:-"@@GOROOT@@"}
-eval $(gomake -j1 --no-print-directory -f "$GOROOT"/src/Make.inc go-env)
-if [ -z "$O" ]; then
- echo 'missing $O - maybe no Make.$GOARCH?' 1>&2
- exit 2
-fi
-
-# Allow overrides
-GC="${_GC:-$GC} -I _test"
-GL="${GL:-$LD} -L _test"
-AS="$AS"
-CC="$CC"
-LD="$LD"
-export GC GL O AS CC LD
-
-# Macros for tab and quotes for easier readability.
-T=' '
-BQ='`'
-SQ="'"
-DQ='"'
-SD="$SQ$DQ"
-DS="$DQ$SQ"
-
-usage="usage: gotry [packagedirectory] expression ...
-Given one expression, gotry attempts to evaluate that expression.
-Given multiple expressions, gotry treats them as a list of arguments
-and result values and attempts to find a function in the package
-that, given the first few expressions as arguments, evaluates to
-the remaining expressions as results. If the first expression has
-methods, it will also search for applicable methods.
-
-If there are multiple expressions, a package directory must be
-specified. If there is a package argument, the expressions are
-evaluated in an environment that includes
- import . ${DQ}packagedirectory${DQ}
-
-Examples:
- gotry 3+4
- # evaluates to 7
- gotry strings ${SD}abc${DS} ${SD}c${DS} 7-5
- # finds strings.Index etc.
- gotry regexp ${SQ}MustCompile(${DQ}^[0-9]+${DQ})${SQ} ${SD}12345${DS} true
- # finds Regexp.MatchString
-
-"
-
-function fail() {
- echo 2>&1 "$@"
- exit 2
-}
-
-case $# in
- 0)
- fail "$usage"
- ;;
- *)
- case "$1" in
- -*help|-*'?'|'?')
- fail "$usage"
- esac
- if test -d "$GOROOT/src/pkg/$1"
- then
- pkg=$(basename $1)
- dir=$GOROOT/src/pkg/$1
- importdir=$1
- shift
- case "$pkg" in
- os|syscall)
- fail "gotry: cannot try packages os or syscall; they are too dangerous"
- esac
- fi
- ;;
-esac
-
-spaces='[ ][ ]*'
-
-function getFunctions() {
- if [ "$pkg" = "" ]
- then
- return
- fi
- for file in $dir/*.go
- do
- case $file in
- *_test*)
- continue
- esac
- grep "func$spaces[A-Z]" $file | # TODO: should be Unicode upper case
- sed "s/func$spaces//;s/(.*//"
- done | sort -u
-}
-
-# Generate list of public functions.
-functions=$(getFunctions)
-
-# Write file to compile
-file="/tmp/$USER.try"
-rm -f "file.go"
-(
-cat <<'!'
-package main
-
-import (
- "os"
- "try"
-!
-
-if [ "$pkg" != "" ]
-then
- echo "$T" . '"'$importdir'"'
-fi
-
-cat <<!
-)
-func main() {
- try.Main("$pkg", firstArg, functions, args)
-}
-var functions = map[string] interface{}{
-!
-
-for i in $functions
-do
- echo "$T"'"'$i'": '$i','
-done
-echo "}"
-
-echo 'var args = []interface{}{'
-
-if [ $# = 1 ]
-then
- echo "${T}toSlice($1)",
-else
-for i
- do
- echo "$T$i",
- done
-fi
-echo "}"
-
-cat <<!
-var firstArg = $BQ$1$BQ
-var _ os.Error
-func toSlice(a ...interface{}) []interface{} { return a }
-!
-
-)>"$file.go"
-
-$GC -o "$file.$O" "$file.go" &&
-$GL -o "$file" "$file.$O" &&
-"$file" "_$@"
-rm -f "$file" "$file.go" "$file.$O"
diff --git a/src/cmd/gotype/Makefile b/src/cmd/gotype/Makefile
deleted file mode 100644
index 18171945d..000000000
--- a/src/cmd/gotype/Makefile
+++ /dev/null
@@ -1,17 +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.
-
-include ../../Make.inc
-
-TARG=gotype
-GOFILES=\
- gotype.go\
-
-include ../../Make.cmd
-
-test:
- gotest
-
-testshort:
- gotest -test.short
diff --git a/src/cmd/gotype/doc.go b/src/cmd/gotype/doc.go
deleted file mode 100644
index 1aa0faa75..000000000
--- a/src/cmd/gotype/doc.go
+++ /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.
-
-/*
-The gotype command does syntactic and semantic analysis of Go files
-and packages similar to the analysis performed by the front-end of
-a Go compiler. Errors are reported if the analysis fails; otherwise
-gotype is quiet (unless -v is set).
-
-Without a list of paths, gotype processes the standard input, which must
-be the source of a single package file.
-
-Given a list of file names, each file must be a source file belonging to
-the same package unless the package name is explicitly specified with the
--p flag.
-
-Given a directory name, gotype collects all .go files in the directory
-and processes them as if they were provided as an explicit list of file
-names. Each directory is processed independently. Files starting with .
-or not ending in .go are ignored.
-
-Usage:
- gotype [flags] [path ...]
-
-The flags are:
- -e
- Print all (including spurious) errors.
- -p pkgName
- Process only those files in package pkgName.
- -r
- Recursively process subdirectories.
- -v
- Verbose mode.
-
-Debugging flags:
- -ast
- Print AST (disables concurrent parsing).
- -trace
- Print parse trace (disables concurrent parsing).
-
-
-Examples
-
-To check the files file.go, old.saved, and .ignored:
-
- gotype file.go old.saved .ignored
-
-To check all .go files belonging to package main in the current directory
-and recursively in all subdirectories:
-
- gotype -p main -r .
-
-To verify the output of a pipe:
-
- echo "package foo" | gotype
-
-*/
-package documentation
-
-// BUG(gri): At the moment, only single-file scope analysis is performed.
diff --git a/src/cmd/gotype/gotype.go b/src/cmd/gotype/gotype.go
deleted file mode 100644
index e5e9417ff..000000000
--- a/src/cmd/gotype/gotype.go
+++ /dev/null
@@ -1,192 +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 (
- "flag"
- "fmt"
- "go/ast"
- "go/parser"
- "go/scanner"
- "go/token"
- "go/types"
- "io/ioutil"
- "os"
- "path/filepath"
- "strings"
-)
-
-var (
- // main operation modes
- pkgName = flag.String("p", "", "process only those files in package pkgName")
- recursive = flag.Bool("r", false, "recursively process subdirectories")
- verbose = flag.Bool("v", false, "verbose mode")
- allErrors = flag.Bool("e", false, "print all (including spurious) errors")
-
- // debugging support
- printTrace = flag.Bool("trace", false, "print parse trace")
- printAST = flag.Bool("ast", false, "print AST")
-)
-
-var exitCode = 0
-
-func usage() {
- fmt.Fprintf(os.Stderr, "usage: gotype [flags] [path ...]\n")
- flag.PrintDefaults()
- os.Exit(2)
-}
-
-func report(err os.Error) {
- scanner.PrintError(os.Stderr, err)
- exitCode = 2
-}
-
-// parse returns the AST for the Go source src.
-// The filename is for error reporting only.
-// The result is nil if there were errors or if
-// the file does not belong to the -p package.
-func parse(fset *token.FileSet, filename string, src []byte) *ast.File {
- if *verbose {
- fmt.Println(filename)
- }
-
- // ignore files with different package name
- if *pkgName != "" {
- file, err := parser.ParseFile(fset, filename, src, parser.PackageClauseOnly)
- if err != nil {
- report(err)
- return nil
- }
- if file.Name.Name != *pkgName {
- if *verbose {
- fmt.Printf("\tignored (package %s)\n", file.Name.Name)
- }
- return nil
- }
- }
-
- // parse entire file
- mode := parser.DeclarationErrors
- if *allErrors {
- mode |= parser.SpuriousErrors
- }
- if *printTrace {
- mode |= parser.Trace
- }
- file, err := parser.ParseFile(fset, filename, src, mode)
- if err != nil {
- report(err)
- return nil
- }
- if *printAST {
- ast.Print(fset, file)
- }
-
- return file
-}
-
-func parseStdin(fset *token.FileSet) (files map[string]*ast.File) {
- files = make(map[string]*ast.File)
- src, err := ioutil.ReadAll(os.Stdin)
- if err != nil {
- report(err)
- return
- }
- const filename = "<standard input>"
- if file := parse(fset, filename, src); file != nil {
- files[filename] = file
- }
- return
-}
-
-func parseFiles(fset *token.FileSet, filenames []string) (files map[string]*ast.File) {
- files = make(map[string]*ast.File)
- for _, filename := range filenames {
- src, err := ioutil.ReadFile(filename)
- if err != nil {
- report(err)
- continue
- }
- if file := parse(fset, filename, src); file != nil {
- if files[filename] != nil {
- report(os.NewError(fmt.Sprintf("%q: duplicate file", filename)))
- continue
- }
- files[filename] = file
- }
- }
- return
-}
-
-func isGoFilename(filename string) bool {
- // ignore non-Go files
- return !strings.HasPrefix(filename, ".") && strings.HasSuffix(filename, ".go")
-}
-
-func processDirectory(dirname string) {
- f, err := os.Open(dirname)
- if err != nil {
- report(err)
- return
- }
- filenames, err := f.Readdirnames(-1)
- f.Close()
- if err != nil {
- report(err)
- // continue since filenames may not be empty
- }
- for i, filename := range filenames {
- filenames[i] = filepath.Join(dirname, filename)
- }
- processFiles(filenames, false)
-}
-
-func processFiles(filenames []string, allFiles bool) {
- i := 0
- for _, filename := range filenames {
- switch info, err := os.Stat(filename); {
- case err != nil:
- report(err)
- case info.IsRegular():
- if allFiles || isGoFilename(info.Name) {
- filenames[i] = filename
- i++
- }
- case info.IsDirectory():
- if allFiles || *recursive {
- processDirectory(filename)
- }
- }
- }
- fset := token.NewFileSet()
- processPackage(fset, parseFiles(fset, filenames[0:i]))
-}
-
-func processPackage(fset *token.FileSet, files map[string]*ast.File) {
- // make a package (resolve all identifiers)
- pkg, err := ast.NewPackage(fset, files, types.GcImporter, types.Universe)
- if err != nil {
- report(err)
- return
- }
- _, err = types.Check(fset, pkg)
- if err != nil {
- report(err)
- }
-}
-
-func main() {
- flag.Usage = usage
- flag.Parse()
-
- if flag.NArg() == 0 {
- fset := token.NewFileSet()
- processPackage(fset, parseStdin(fset))
- } else {
- processFiles(flag.Args(), true)
- }
-
- os.Exit(exitCode)
-}
diff --git a/src/cmd/gotype/gotype_test.go b/src/cmd/gotype/gotype_test.go
deleted file mode 100644
index ad0bc8903..000000000
--- a/src/cmd/gotype/gotype_test.go
+++ /dev/null
@@ -1,49 +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 (
- "path/filepath"
- "runtime"
- "testing"
-)
-
-func runTest(t *testing.T, path, pkg string) {
- exitCode = 0
- *pkgName = pkg
- *recursive = false
-
- if pkg == "" {
- processFiles([]string{path}, true)
- } else {
- processDirectory(path)
- }
-
- if exitCode != 0 {
- t.Errorf("processing %s failed: exitCode = %d", path, exitCode)
- }
-}
-
-var tests = []struct {
- path string
- pkg string
-}{
- // individual files
- {"testdata/test1.go", ""},
-
- // directories
- {filepath.Join(runtime.GOROOT(), "src/pkg/go/ast"), "ast"},
- {filepath.Join(runtime.GOROOT(), "src/pkg/go/doc"), "doc"},
- {filepath.Join(runtime.GOROOT(), "src/pkg/go/token"), "scanner"},
- {filepath.Join(runtime.GOROOT(), "src/pkg/go/scanner"), "scanner"},
- {filepath.Join(runtime.GOROOT(), "src/pkg/go/parser"), "parser"},
- {filepath.Join(runtime.GOROOT(), "src/pkg/go/types"), "types"},
-}
-
-func Test(t *testing.T) {
- for _, test := range tests {
- runTest(t, test.path, test.pkg)
- }
-}
diff --git a/src/cmd/gotype/testdata/test1.go b/src/cmd/gotype/testdata/test1.go
deleted file mode 100644
index 0bd46568d..000000000
--- a/src/cmd/gotype/testdata/test1.go
+++ /dev/null
@@ -1,6 +0,0 @@
-package p
-
-func _() {
- // the scope of a local type declaration starts immediately after the type name
- type T struct{ _ *T }
-}
diff --git a/src/cmd/govet/Makefile b/src/cmd/govet/Makefile
deleted file mode 100644
index f565b78f5..000000000
--- a/src/cmd/govet/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.
-
-include ../../Make.inc
-
-TARG=govet
-GOFILES=\
- govet.go\
-
-include ../../Make.cmd
-
-test testshort: $(TARG)
- ../../../test/errchk $(TARG) -printfuncs='Warn:1,Warnf:1' govet.go
diff --git a/src/cmd/goyacc/Makefile b/src/cmd/goyacc/Makefile
deleted file mode 100644
index ac0f427cc..000000000
--- a/src/cmd/goyacc/Makefile
+++ /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.
-
-include ../../Make.inc
-
-TARG=goyacc
-GOFILES=\
- goyacc.go\
-
-include ../../Make.cmd
-
-units: goyacc units.y
- ./goyacc -p units_ units.y
- $(GC) y.go
- $(LD) -o units y.$O
-
diff --git a/src/cmd/hgpatch/Makefile b/src/cmd/hgpatch/Makefile
deleted file mode 100644
index 1ef98d7f9..000000000
--- a/src/cmd/hgpatch/Makefile
+++ /dev/null
@@ -1,11 +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 ../../Make.inc
-
-TARG=hgpatch
-GOFILES=\
- main.go\
-
-include ../../Make.cmd
diff --git a/src/cmd/hgpatch/doc.go b/src/cmd/hgpatch/doc.go
deleted file mode 100644
index 1e0f1da38..000000000
--- a/src/cmd/hgpatch/doc.go
+++ /dev/null
@@ -1,18 +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.
-
-/*
-
-Hgpatch applies a patch to the local Mercurial repository.
-The patch should have been been generated by
-a version control system like CVS, Git, Mercurial, or Subversion.
-If successful, hgpatch writes a list of affected files to standard output.
-
-Hgpatch is meant to be used by the Mercurial codereview extension.
-
-Usage:
- hgpatch [patchfile]
-
-*/
-package documentation
diff --git a/src/cmd/hgpatch/main.go b/src/cmd/hgpatch/main.go
deleted file mode 100644
index 9e338abcb..000000000
--- a/src/cmd/hgpatch/main.go
+++ /dev/null
@@ -1,358 +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"
- "container/vector"
- "exec"
- "flag"
- "fmt"
- "io/ioutil"
- "os"
- "patch"
- "path/filepath"
- "sort"
- "strings"
-)
-
-var checkSync = flag.Bool("checksync", true, "check whether repository is out of sync")
-
-func usage() {
- fmt.Fprintf(os.Stderr, "usage: hgpatch [options] [patchfile]\n")
- flag.PrintDefaults()
- os.Exit(2)
-}
-
-func main() {
- flag.Usage = usage
- flag.Parse()
-
- args := flag.Args()
- var data []byte
- var err os.Error
- switch len(args) {
- case 0:
- data, err = ioutil.ReadAll(os.Stdin)
- case 1:
- data, err = ioutil.ReadFile(args[0])
- default:
- usage()
- }
- chk(err)
-
- pset, err := patch.Parse(data)
- chk(err)
-
- // Change to hg root directory, because
- // patch paths are relative to root.
- root, err := hgRoot()
- chk(err)
- chk(os.Chdir(root))
-
- // Make sure there are no pending changes on the server.
- if *checkSync && hgIncoming() {
- fmt.Fprintf(os.Stderr, "incoming changes waiting; run hg sync first\n")
- os.Exit(2)
- }
-
- // Make sure we won't be editing files with local pending changes.
- dirtylist, err := hgModified()
- chk(err)
- dirty := make(map[string]bool)
- for _, f := range dirtylist {
- dirty[f] = true
- }
- conflict := make(map[string]bool)
- for _, f := range pset.File {
- if f.Verb == patch.Delete || f.Verb == patch.Rename {
- if dirty[f.Src] {
- conflict[f.Src] = true
- }
- }
- if f.Verb != patch.Delete {
- if dirty[f.Dst] {
- conflict[f.Dst] = true
- }
- }
- }
- if len(conflict) > 0 {
- fmt.Fprintf(os.Stderr, "cannot apply patch to locally modified files:\n")
- for name := range conflict {
- fmt.Fprintf(os.Stderr, "\t%s\n", name)
- }
- os.Exit(2)
- }
-
- // Apply changes in memory.
- op, err := pset.Apply(ioutil.ReadFile)
- chk(err)
-
- // Write changes to disk copy: order of commands matters.
- // Accumulate undo log as we go, in case there is an error.
- // Also accumulate list of modified files to print at end.
- changed := make(map[string]int)
-
- // Copy, Rename create the destination file, so they
- // must happen before we write the data out.
- // A single patch may have a Copy and a Rename
- // with the same source, so we have to run all the
- // Copy in one pass, then all the Rename.
- for i := range op {
- o := &op[i]
- if o.Verb == patch.Copy {
- makeParent(o.Dst)
- chk(hgCopy(o.Dst, o.Src))
- undoRevert(o.Dst)
- changed[o.Dst] = 1
- }
- }
- for i := range op {
- o := &op[i]
- if o.Verb == patch.Rename {
- makeParent(o.Dst)
- chk(hgRename(o.Dst, o.Src))
- undoRevert(o.Dst)
- undoRevert(o.Src)
- changed[o.Src] = 1
- changed[o.Dst] = 1
- }
- }
-
- // Run Delete before writing to files in case one of the
- // deleted paths is becoming a directory.
- for i := range op {
- o := &op[i]
- if o.Verb == patch.Delete {
- chk(hgRemove(o.Src))
- undoRevert(o.Src)
- changed[o.Src] = 1
- }
- }
-
- // Write files.
- for i := range op {
- o := &op[i]
- if o.Verb == patch.Delete {
- continue
- }
- if o.Verb == patch.Add {
- makeParent(o.Dst)
- changed[o.Dst] = 1
- }
- if o.Data != nil {
- chk(ioutil.WriteFile(o.Dst, o.Data, 0644))
- if o.Verb == patch.Add {
- undoRm(o.Dst)
- } else {
- undoRevert(o.Dst)
- }
- changed[o.Dst] = 1
- }
- if o.Mode != 0 {
- chk(os.Chmod(o.Dst, uint32(o.Mode&0755)))
- undoRevert(o.Dst)
- changed[o.Dst] = 1
- }
- }
-
- // hg add looks at the destination file, so it must happen
- // after we write the data out.
- for i := range op {
- o := &op[i]
- if o.Verb == patch.Add {
- chk(hgAdd(o.Dst))
- undoRevert(o.Dst)
- changed[o.Dst] = 1
- }
- }
-
- // Finished editing files. Write the list of changed files to stdout.
- list := make([]string, len(changed))
- i := 0
- for f := range changed {
- list[i] = f
- i++
- }
- sort.Strings(list)
- for _, f := range list {
- fmt.Printf("%s\n", f)
- }
-}
-
-// make parent directory for name, if necessary
-func makeParent(name string) {
- parent, _ := filepath.Split(name)
- chk(mkdirAll(parent, 0755))
-}
-
-// Copy of os.MkdirAll but adds to undo log after
-// creating a directory.
-func mkdirAll(path string, perm uint32) os.Error {
- dir, err := os.Lstat(path)
- if err == nil {
- if dir.IsDirectory() {
- return nil
- }
- return &os.PathError{"mkdir", path, os.ENOTDIR}
- }
-
- i := len(path)
- for i > 0 && path[i-1] == '/' { // Skip trailing slashes.
- i--
- }
-
- j := i
- for j > 0 && path[j-1] != '/' { // Scan backward over element.
- j--
- }
-
- if j > 0 {
- err = mkdirAll(path[0:j-1], perm)
- if err != nil {
- return err
- }
- }
-
- err = os.Mkdir(path, perm)
- if err != nil {
- // Handle arguments like "foo/." by
- // double-checking that directory doesn't exist.
- dir, err1 := os.Lstat(path)
- if err1 == nil && dir.IsDirectory() {
- return nil
- }
- return err
- }
- undoRm(path)
- return nil
-}
-
-// If err != nil, process the undo log and exit.
-func chk(err os.Error) {
- if err != nil {
- fmt.Fprintf(os.Stderr, "%s\n", err)
- runUndo()
- os.Exit(2)
- }
-}
-
-// Undo log
-type undo func() os.Error
-
-var undoLog vector.Vector // vector of undo
-
-func undoRevert(name string) { undoLog.Push(undo(func() os.Error { return hgRevert(name) })) }
-
-func undoRm(name string) { undoLog.Push(undo(func() os.Error { return os.Remove(name) })) }
-
-func runUndo() {
- for i := undoLog.Len() - 1; i >= 0; i-- {
- if err := undoLog.At(i).(undo)(); err != nil {
- fmt.Fprintf(os.Stderr, "%s\n", err)
- }
- }
-}
-
-// hgRoot returns the root directory of the repository.
-func hgRoot() (string, os.Error) {
- out, err := run([]string{"hg", "root"}, nil)
- if err != nil {
- return "", err
- }
- return strings.TrimSpace(out), nil
-}
-
-// hgIncoming returns true if hg sync will pull in changes.
-func hgIncoming() bool {
- // hg -q incoming exits 0 when there is nothing incoming, 1 otherwise.
- _, err := run([]string{"hg", "-q", "incoming"}, nil)
- return err == nil
-}
-
-// hgModified returns a list of the modified files in the
-// repository.
-func hgModified() ([]string, os.Error) {
- out, err := run([]string{"hg", "status", "-n"}, nil)
- if err != nil {
- return nil, err
- }
- return strings.Split(strings.TrimSpace(out), "\n"), nil
-}
-
-// hgAdd adds name to the repository.
-func hgAdd(name string) os.Error {
- _, err := run([]string{"hg", "add", name}, nil)
- return err
-}
-
-// hgRemove removes name from the repository.
-func hgRemove(name string) os.Error {
- _, err := run([]string{"hg", "rm", name}, nil)
- return err
-}
-
-// hgRevert reverts name.
-func hgRevert(name string) os.Error {
- _, err := run([]string{"hg", "revert", name}, nil)
- return err
-}
-
-// hgCopy copies src to dst in the repository.
-// Note that the argument order matches io.Copy, not "hg cp".
-func hgCopy(dst, src string) os.Error {
- _, err := run([]string{"hg", "cp", src, dst}, nil)
- return err
-}
-
-// hgRename renames src to dst in the repository.
-// Note that the argument order matches io.Copy, not "hg mv".
-func hgRename(dst, src string) os.Error {
- _, err := run([]string{"hg", "mv", src, dst}, nil)
- return err
-}
-
-func dup(a []string) []string {
- b := make([]string, len(a))
- copy(b, a)
- return b
-}
-
-var lookPathCache = make(map[string]string)
-
-// run runs the command argv, resolving argv[0] if necessary by searching $PATH.
-// It provides input on standard input to the command.
-func run(argv []string, input []byte) (out string, err os.Error) {
- if len(argv) < 1 {
- return "", &runError{dup(argv), os.EINVAL}
- }
-
- prog, ok := lookPathCache[argv[0]]
- if !ok {
- prog, err = exec.LookPath(argv[0])
- if err != nil {
- return "", &runError{dup(argv), err}
- }
- lookPathCache[argv[0]] = prog
- }
-
- cmd := exec.Command(prog, argv[1:]...)
- if len(input) > 0 {
- cmd.Stdin = bytes.NewBuffer(input)
- }
- bs, err := cmd.CombinedOutput()
- if err != nil {
- return "", &runError{dup(argv), err}
- }
- return string(bs), nil
-}
-
-// A runError represents an error that occurred while running a command.
-type runError struct {
- cmd []string
- err os.Error
-}
-
-func (e *runError) String() string { return strings.Join(e.cmd, " ") + ": " + e.err.String() }
diff --git a/src/cmd/ld/data.c b/src/cmd/ld/data.c
index e7269169e..786c10b64 100644
--- a/src/cmd/ld/data.c
+++ b/src/cmd/ld/data.c
@@ -182,7 +182,11 @@ relocsym(Sym *s)
o = symaddr(r->sym) + r->add;
break;
case D_PCREL:
- o = symaddr(r->sym) + r->add - (s->value + r->off + r->siz);
+ // r->sym can be null when CALL $(constant) is transformed from absoulte PC to relative PC call.
+ o = 0;
+ if(r->sym)
+ o += symaddr(r->sym);
+ o += r->add - (s->value + r->off + r->siz);
break;
case D_SIZE:
o = r->sym->size + r->add;
@@ -410,13 +414,13 @@ savedata(Sym *s, Prog *p, char *pn)
}
static void
-blk(Sym *allsym, int32 addr, int32 size)
+blk(Sym *start, int32 addr, int32 size)
{
Sym *sym;
int32 eaddr;
uchar *p, *ep;
- for(sym = allsym; sym != nil; sym = sym->next)
+ for(sym = start; sym != nil; sym = sym->next)
if(!(sym->type&SSUB) && sym->value >= addr)
break;
@@ -585,6 +589,25 @@ strnput(char *s, int n)
}
}
+void
+addstrdata(char *name, char *value)
+{
+ Sym *s, *sp;
+ char *p;
+
+ p = smprint("%s.str", name);
+ sp = lookup(p, 0);
+ free(p);
+ addstring(sp, value);
+
+ s = lookup(name, 0);
+ s->dupok = 1;
+ addaddr(s, sp);
+ adduint32(s, strlen(value));
+ if(PtrSize == 8)
+ adduint32(s, 0); // round struct to pointer width
+}
+
vlong
addstring(Sym *s, char *str)
{
@@ -592,7 +615,7 @@ addstring(Sym *s, char *str)
int32 r;
if(s->type == 0)
- s->type = SDATA;
+ s->type = SNOPTRDATA;
s->reachable = 1;
r = s->size;
n = strlen(str)+1;
@@ -756,10 +779,25 @@ addsize(Sym *s, Sym *t)
}
void
+dosymtype(void)
+{
+ Sym *s;
+
+ for(s = allsym; s != nil; s = s->allsym) {
+ if(s->np > 0) {
+ if(s->type == SBSS)
+ s->type = SDATA;
+ if(s->type == SNOPTRBSS)
+ s->type = SNOPTRDATA;
+ }
+ }
+}
+
+void
dodata(void)
{
int32 t, datsize;
- Section *sect;
+ Section *sect, *noptr;
Sym *s, *last, **l;
if(debug['v'])
@@ -783,13 +821,12 @@ dodata(void)
}
for(s = datap; s != nil; s = s->next) {
- if(s->np > 0 && s->type == SBSS)
- s->type = SDATA;
if(s->np > s->size)
diag("%s: initialize bounds (%lld < %d)",
s->name, (vlong)s->size, s->np);
}
-
+
+
/*
* now that we have the datap list, but before we start
* to assign addresses, record all the necessary
@@ -820,6 +857,8 @@ dodata(void)
datsize = 0;
s = datap;
for(; s != nil && s->type < SSYMTAB; s = s->next) {
+ if(s->align != 0)
+ datsize = rnd(datsize, s->align);
s->type = SRODATA;
s->value = datsize;
datsize += rnd(s->size, PtrSize);
@@ -851,6 +890,8 @@ dodata(void)
/* read-only ELF sections */
for(; s != nil && s->type < SELFSECT; s = s->next) {
sect = addsection(&segtext, s->name, 04);
+ if(s->align != 0)
+ datsize = rnd(datsize, s->align);
sect->vaddr = datsize;
s->type = SRODATA;
s->value = datsize;
@@ -860,30 +901,43 @@ dodata(void)
/* writable ELF sections */
datsize = 0;
- for(; s != nil && s->type < SDATA; s = s->next) {
+ for(; s != nil && s->type < SNOPTRDATA; s = s->next) {
sect = addsection(&segdata, s->name, 06);
+ if(s->align != 0)
+ datsize = rnd(datsize, s->align);
sect->vaddr = datsize;
s->type = SDATA;
s->value = datsize;
datsize += rnd(s->size, PtrSize);
sect->len = datsize - sect->vaddr;
}
-
- /* data */
- sect = addsection(&segdata, ".data", 06);
+
+ /* pointer-free data, then data */
+ sect = addsection(&segdata, ".noptrdata", 06);
sect->vaddr = datsize;
- for(; s != nil && s->type < SBSS; s = s->next) {
+ noptr = sect;
+ for(; ; s = s->next) {
+ if((s == nil || s->type >= SDATA) && sect == noptr) {
+ // finish noptrdata, start data
+ datsize = rnd(datsize, 8);
+ sect->len = datsize - sect->vaddr;
+ sect = addsection(&segdata, ".data", 06);
+ sect->vaddr = datsize;
+ }
+ if(s == nil || s->type >= SBSS) {
+ // finish data
+ sect->len = datsize - sect->vaddr;
+ break;
+ }
s->type = SDATA;
t = s->size;
- if(t == 0 && s->name[0] != '.') {
- diag("%s: no size", s->name);
- t = 1;
- }
if(t >= PtrSize)
t = rnd(t, PtrSize);
else if(t > 2)
t = rnd(t, 4);
- if(t & 1) {
+ if(s->align != 0)
+ datsize = rnd(datsize, s->align);
+ else if(t & 1) {
;
} else if(t & 2)
datsize = rnd(datsize, 2);
@@ -894,13 +948,25 @@ dodata(void)
s->value = datsize;
datsize += t;
}
- sect->len = datsize - sect->vaddr;
- /* bss */
+ /* bss, then pointer-free bss */
+ noptr = nil;
sect = addsection(&segdata, ".bss", 06);
sect->vaddr = datsize;
- for(; s != nil; s = s->next) {
- if(s->type != SBSS) {
+ for(; ; s = s->next) {
+ if((s == nil || s->type >= SNOPTRBSS) && noptr == nil) {
+ // finish bss, start noptrbss
+ datsize = rnd(datsize, 8);
+ sect->len = datsize - sect->vaddr;
+ sect = addsection(&segdata, ".noptrbss", 06);
+ sect->vaddr = datsize;
+ noptr = sect;
+ }
+ if(s == nil) {
+ sect->len = datsize - sect->vaddr;
+ break;
+ }
+ if(s->type > SNOPTRBSS) {
cursym = s;
diag("unexpected symbol type %d", s->type);
}
@@ -909,7 +975,9 @@ dodata(void)
t = rnd(t, PtrSize);
else if(t > 2)
t = rnd(t, 4);
- if(t & 1) {
+ if(s->align != 0)
+ datsize = rnd(datsize, s->align);
+ else if(t & 1) {
;
} else if(t & 2)
datsize = rnd(datsize, 2);
@@ -920,7 +988,6 @@ dodata(void)
s->value = datsize;
datsize += t;
}
- sect->len = datsize - sect->vaddr;
}
// assign addresses to text
@@ -943,6 +1010,8 @@ textaddress(void)
for(sym = textp; sym != nil; sym = sym->next) {
if(sym->type & SSUB)
continue;
+ if(sym->align != 0)
+ va = rnd(va, sym->align);
sym->value = 0;
for(sub = sym; sub != S; sub = sub->sub) {
sub->value += va;
@@ -954,6 +1023,11 @@ textaddress(void)
}
va += sym->size;
}
+
+ // Align end of code so that rodata starts aligned.
+ // 128 bytes is likely overkill but definitely cheap.
+ va = rnd(va, 128);
+
sect->len = va - sect->vaddr;
}
@@ -961,7 +1035,7 @@ textaddress(void)
void
address(void)
{
- Section *s, *text, *data, *rodata, *symtab, *pclntab;
+ Section *s, *text, *data, *rodata, *symtab, *pclntab, *noptr, *bss, *noptrbss;
Sym *sym, *sub;
uvlong va;
@@ -987,6 +1061,9 @@ address(void)
if(HEADTYPE == Hplan9x32)
segdata.fileoff = segtext.fileoff + segtext.filelen;
data = nil;
+ noptr = nil;
+ bss = nil;
+ noptrbss = nil;
for(s=segdata.sect; s != nil; s=s->next) {
s->vaddr = va;
va += s->len;
@@ -994,8 +1071,14 @@ address(void)
segdata.len = va - segdata.vaddr;
if(strcmp(s->name, ".data") == 0)
data = s;
+ if(strcmp(s->name, ".noptrdata") == 0)
+ noptr = s;
+ if(strcmp(s->name, ".bss") == 0)
+ bss = s;
+ if(strcmp(s->name, ".noptrbss") == 0)
+ noptrbss = s;
}
- segdata.filelen -= data->next->len; // deduct .bss
+ segdata.filelen -= bss->len + noptrbss->len; // deduct .bss
text = segtext.sect;
rodata = text->next;
@@ -1004,7 +1087,7 @@ address(void)
for(sym = datap; sym != nil; sym = sym->next) {
cursym = sym;
- if(sym->type < SDATA)
+ if(sym->type < SNOPTRDATA)
sym->value += rodata->vaddr;
else
sym->value += segdata.sect->vaddr;
@@ -1020,7 +1103,13 @@ address(void)
xdefine("esymtab", SRODATA, symtab->vaddr + symtab->len);
xdefine("pclntab", SRODATA, pclntab->vaddr);
xdefine("epclntab", SRODATA, pclntab->vaddr + pclntab->len);
- xdefine("data", SBSS, data->vaddr);
- xdefine("edata", SBSS, data->vaddr + data->len);
+ xdefine("noptrdata", SNOPTRDATA, noptr->vaddr);
+ xdefine("enoptrdata", SNOPTRDATA, noptr->vaddr + noptr->len);
+ xdefine("bss", SBSS, bss->vaddr);
+ xdefine("ebss", SBSS, bss->vaddr + bss->len);
+ xdefine("data", SDATA, data->vaddr);
+ xdefine("edata", SDATA, data->vaddr + data->len);
+ xdefine("noptrbss", SNOPTRBSS, noptrbss->vaddr);
+ xdefine("enoptrbss", SNOPTRBSS, noptrbss->vaddr + noptrbss->len);
xdefine("end", SBSS, segdata.vaddr + segdata.len);
}
diff --git a/src/cmd/ld/doc.go b/src/cmd/ld/doc.go
index 972e2a32c..e99e50466 100644
--- a/src/cmd/ld/doc.go
+++ b/src/cmd/ld/doc.go
@@ -4,8 +4,57 @@
/*
-This directory contains the portable section of the Plan 9 C linkers.
-See ../6l, ../8l, and ../5l for more information.
+Ld is the portable code for a modified version of the Plan 9 linker. The original is documented at
+ http://plan9.bell-labs.com/magic/man2html/1/2l
+
+It reads object files (.5, .6, or .8 files) and writes a binary named for the
+architecture (5.out, 6.out, 8.out) by default (if $GOOS is windows, a .exe suffix
+will be appended).
+
+Major changes include:
+ - support for ELF, Mach-O and PE binary files
+ - support for segmented stacks (this feature is implemented here, not in the compilers).
+
+Original options are listed on the manual page linked above.
+
+Usage:
+ go tool 6l [flags] mainObj
+Substitute 6l with 8l or 5l as appropriate.
+
+Options new in this version:
+
+ -d
+ Elide the dynamic linking header. With this option, the binary
+ is statically linked and does not refer to a dynamic linker. Without this option
+ (the default), the binary's contents are identical but it is loaded with a dynamic
+ linker. This flag cannot be used when $GOOS is windows.
+ -Hdarwin (only in 6l/8l)
+ Write Apple Mach-O binaries (default when $GOOS is darwin)
+ -Hlinux
+ Write Linux ELF binaries (default when $GOOS is linux)
+ -Hfreebsd (only in 6l/8l)
+ Write FreeBSD ELF binaries (default when $GOOS is freebsd)
+ -Hnetbsd (only in 6l/8l)
+ Write NetBSD ELF binaries (default when $GOOS is netbsd)
+ -Hopenbsd (only in 6l/8l)
+ Write OpenBSD ELF binaries (default when $GOOS is openbsd)
+ -Hwindows (only in 6l/8l)
+ Write Windows PE32+ Console binaries (default when $GOOS is windows)
+ -Hwindowsgui (only in 6l/8l)
+ Write Windows PE32+ GUI binaries
+ -I interpreter
+ Set the ELF dynamic linker to use.
+ -L dir1 -L dir2
+ Search for libraries (package files) in dir1, dir2, etc.
+ The default is the single location $GOROOT/pkg/$GOOS_$GOARCH.
+ -r dir1:dir2:...
+ Set the dynamic linker search path when using ELF.
+ -V
+ Print the linker version.
+ -X symbol value
+ Set the value of an otherwise uninitialized string variable.
+ The symbol name should be of the form importpath.name,
+ as displayed in the symbol table printed by "go tool nm".
*/
package documentation
diff --git a/src/cmd/ld/dwarf.c b/src/cmd/ld/dwarf.c
index 373cf5523..57e5a4283 100644
--- a/src/cmd/ld/dwarf.c
+++ b/src/cmd/ld/dwarf.c
@@ -775,7 +775,7 @@ enum {
KindNoPointers = 1<<7,
// size of Type interface header + CommonType structure.
- CommonSize = 2*PtrSize+ 4*PtrSize + 8,
+ CommonSize = 2*PtrSize+ 5*PtrSize + 8,
};
static Reloc*
@@ -1439,7 +1439,7 @@ defdwsymb(Sym* sym, char *s, int t, vlong v, vlong size, int ver, Sym *gotype)
if (strncmp(s, "go.string.", 10) == 0)
return;
- if (strncmp(s, "type.", 5) == 0 && strcmp(s, "type.*") != 0) {
+ if (strncmp(s, "type.", 5) == 0 && strcmp(s, "type.*") != 0 && strncmp(s, "type..", 6) != 0) {
defgotype(sym);
return;
}
@@ -1601,7 +1601,7 @@ finddebugruntimepath(void)
char *c;
for (i = 1; i < histfilesize; i++) {
- if ((c = strstr(histfile[i], "runtime/runtime_defs.go")) != nil) {
+ if ((c = strstr(histfile[i], "runtime/zruntime_defs")) != nil) {
l = c - histfile[i];
memmove(gdbscript, histfile[i], l);
memmove(gdbscript + l, "runtime/runtime-gdb.py", strlen("runtime/runtime-gdb.py") + 1);
@@ -2317,7 +2317,7 @@ dwarfemitdebugsections(void)
// Needed by the prettyprinter code for interface inspection.
defgotype(lookup_or_diag("type.runtime.commonType"));
- defgotype(lookup_or_diag("type.runtime.InterfaceType"));
+ defgotype(lookup_or_diag("type.runtime.interfaceType"));
defgotype(lookup_or_diag("type.runtime.itab"));
genasmsym(defdwsymb);
diff --git a/src/cmd/ld/elf.c b/src/cmd/ld/elf.c
index f9f9ef6b2..de9e6b854 100644
--- a/src/cmd/ld/elf.c
+++ b/src/cmd/ld/elf.c
@@ -77,6 +77,19 @@ elf64phdr(ElfPhdr *e)
void
elf32phdr(ElfPhdr *e)
{
+ int frag;
+
+ if(e->type == PT_LOAD) {
+ // Correct ELF loaders will do this implicitly,
+ // but buggy ELF loaders like the one in some
+ // versions of QEMU won't.
+ frag = e->vaddr&(e->align-1);
+ e->off -= frag;
+ e->vaddr -= frag;
+ e->paddr -= frag;
+ e->filesz += frag;
+ e->memsz += frag;
+ }
LPUT(e->type);
LPUT(e->off);
LPUT(e->vaddr);
@@ -305,29 +318,77 @@ elfwritedynentsymsize(Sym *s, int tag, Sym *t)
}
int
-elfwriteinterp(void)
+elfinterp(ElfShdr *sh, uint64 startva, uint64 resoff, char *p)
{
int n;
- if(interp == nil)
- return 0;
-
+ interp = p;
n = strlen(interp)+1;
- cseek(ELFRESERVE-n);
- cwrite(interp, n);
+ sh->addr = startva + resoff - n;
+ sh->off = resoff - n;
+ sh->size = n;
+
return n;
}
-void
-elfinterp(ElfShdr *sh, uint64 startva, char *p)
+int
+elfwriteinterp(vlong stridx)
+{
+ ElfShdr *sh = nil;
+ int i;
+
+ for(i = 0; i < hdr.shnum; i++)
+ if(shdr[i]->name == stridx)
+ sh = shdr[i];
+ if(sh == nil || interp == nil)
+ return 0;
+
+ cseek(sh->off);
+ cwrite(interp, sh->size);
+ return sh->size;
+}
+
+// Defined in NetBSD's sys/exec_elf.h
+#define ELF_NOTE_TYPE_NETBSD_TAG 1
+#define ELF_NOTE_NETBSD_NAMESZ 7
+#define ELF_NOTE_NETBSD_DESCSZ 4
+#define ELF_NOTE_NETBSD_NAME "NetBSD\0\0"
+#define ELF_NOTE_NETBSD_VERSION 599000000 /* NetBSD 5.99 */
+
+int
+elfnetbsdsig(ElfShdr *sh, uint64 startva, uint64 resoff)
{
int n;
- interp = p;
- n = strlen(interp)+1;
- sh->addr = startva + ELFRESERVE - n;
- sh->off = ELFRESERVE - n;
+ n = sizeof(Elf_Note) + ELF_NOTE_NETBSD_NAMESZ + ELF_NOTE_NETBSD_DESCSZ + 1;
+ n += resoff % 4;
+ sh->addr = startva + resoff - n;
+ sh->off = resoff - n;
sh->size = n;
+
+ return n;
+}
+
+int
+elfwritenetbsdsig(vlong stridx) {
+ ElfShdr *sh = nil;
+ int i;
+
+ for(i = 0; i < hdr.shnum; i++)
+ if(shdr[i]->name == stridx)
+ sh = shdr[i];
+ if(sh == nil)
+ return 0;
+
+ // Write Elf_Note header followed by NetBSD string.
+ cseek(sh->off);
+ LPUT(ELF_NOTE_NETBSD_NAMESZ);
+ LPUT(ELF_NOTE_NETBSD_DESCSZ);
+ LPUT(ELF_NOTE_TYPE_NETBSD_TAG);
+ cwrite(ELF_NOTE_NETBSD_NAME, 8);
+ LPUT(ELF_NOTE_NETBSD_VERSION);
+
+ return sh->size;
}
extern int nelfsym;
diff --git a/src/cmd/ld/elf.h b/src/cmd/ld/elf.h
index c63df2241..690ade975 100644
--- a/src/cmd/ld/elf.h
+++ b/src/cmd/ld/elf.h
@@ -968,8 +968,10 @@ extern int numelfphdr;
extern int numelfshdr;
extern int iself;
extern int elfverneed;
-int elfwriteinterp(void);
-void elfinterp(ElfShdr*, uint64, char*);
+int elfinterp(ElfShdr*, uint64, uint64, char*);
+int elfwriteinterp(vlong);
+int elfnetbsdsig(ElfShdr*, uint64, uint64);
+int elfwritenetbsdsig(vlong);
void elfdynhash(void);
ElfPhdr* elfphload(Segment*);
ElfShdr* elfshbits(Section*);
diff --git a/src/cmd/ld/go.c b/src/cmd/ld/go.c
index fd7278a7b..3271be1f5 100644
--- a/src/cmd/ld/go.c
+++ b/src/cmd/ld/go.c
@@ -235,7 +235,7 @@ loadpkgdata(char *file, char *pkg, char *data, int len)
x = ilookup(name);
if(x->prefix == nil) {
x->prefix = prefix;
- x->def = def;
+ x->def = strdup(def);
x->file = file;
} else if(strcmp(x->prefix, prefix) != 0) {
fprint(2, "%s: conflicting definitions for %s\n", argv0, name);
@@ -248,7 +248,10 @@ loadpkgdata(char *file, char *pkg, char *data, int len)
fprint(2, "%s:\t%s %s %s\n", file, prefix, name, def);
nerrors++;
}
+ free(name);
+ free(def);
}
+ free(file);
}
// replace all "". with pkg.
@@ -264,7 +267,7 @@ expandpkg(char *t0, char *pkg)
n++;
if(n == 0)
- return t0;
+ return strdup(t0);
// use malloc, not mal, so that caller can free
w0 = malloc(strlen(t0) + strlen(pkg)*n);
@@ -479,6 +482,7 @@ loaddynimport(char *file, char *pkg, char *p, int n)
if(q)
*q++ = '\0';
s = lookup(name, 0);
+ free(name);
if(s->type == 0 || s->type == SXREF) {
s->dynimplib = lib;
s->dynimpname = def;
diff --git a/src/cmd/ld/ldelf.c b/src/cmd/ld/ldelf.c
index 924687867..bd4f3e7d8 100644
--- a/src/cmd/ld/ldelf.c
+++ b/src/cmd/ld/ldelf.c
@@ -538,6 +538,7 @@ ldelf(Biobuf *f, char *pkg, int64 len, char *pn)
s->np = sect->size;
}
s->size = sect->size;
+ s->align = sect->align;
if(s->type == STEXT) {
if(etextp)
etextp->next = s;
diff --git a/src/cmd/ld/ldpe.c b/src/cmd/ld/ldpe.c
index 680557075..feb8620bd 100644
--- a/src/cmd/ld/ldpe.c
+++ b/src/cmd/ld/ldpe.c
@@ -282,8 +282,10 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn)
diag("%s: unknown relocation type %d;", pn, type);
case IMAGE_REL_I386_REL32:
case IMAGE_REL_AMD64_REL32:
+ case IMAGE_REL_AMD64_ADDR32: // R_X86_64_PC32
+ case IMAGE_REL_AMD64_ADDR32NB:
rp->type = D_PCREL;
- rp->add = 0;
+ rp->add = le32(rsect->base+rp->off);
break;
case IMAGE_REL_I386_DIR32NB:
case IMAGE_REL_I386_DIR32:
@@ -291,10 +293,6 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn)
// load addend from image
rp->add = le32(rsect->base+rp->off);
break;
- case IMAGE_REL_AMD64_ADDR32: // R_X86_64_PC32
- rp->type = D_PCREL;
- rp->add += 4;
- break;
case IMAGE_REL_AMD64_ADDR64: // R_X86_64_64
rp->siz = 8;
rp->type = D_ADDR;
@@ -408,13 +406,15 @@ readsym(PeObj *obj, int i, PeSym **y)
sym = &obj->pesym[i];
*y = sym;
- name = sym->name;
- if(sym->sclass == IMAGE_SYM_CLASS_STATIC && sym->value == 0) // section
+ if(sym->name[0] == '.') // .section
name = obj->sect[sym->sectnum-1].sym->name;
- if(strncmp(sym->name, "__imp__", 7) == 0)
- name = &sym->name[7]; // __imp__Name => Name
- else if(sym->name[0] == '_')
- name = &sym->name[1]; // _Name => Name
+ else {
+ name = sym->name;
+ if(strncmp(name, "__imp_", 6) == 0)
+ name = &name[6]; // __imp_Name => Name
+ if(thechar == '8' && name[0] == '_')
+ name = &name[1]; // _Name => Name
+ }
// remove last @XXX
p = strchr(name, '@');
if(p)
@@ -443,8 +443,8 @@ readsym(PeObj *obj, int i, PeSym **y)
if(s != nil && s->type == 0 && !(sym->sclass == IMAGE_SYM_CLASS_STATIC && sym->value == 0))
s->type = SXREF;
- if(strncmp(sym->name, "__imp__", 7) == 0)
- s->got = -2; // flag for __imp__
+ if(strncmp(sym->name, "__imp_", 6) == 0)
+ s->got = -2; // flag for __imp_
sym->sym = s;
return 0;
diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c
index 5d1e6d61b..4a100cac3 100644
--- a/src/cmd/ld/lib.c
+++ b/src/cmd/ld/lib.c
@@ -39,20 +39,32 @@ int iconv(Fmt*);
char symname[] = SYMDEF;
char pkgname[] = "__.PKGDEF";
-char* libdir[16];
+char** libdir;
int nlibdir = 0;
+static int maxlibdir = 0;
static int cout = -1;
char* goroot;
char* goarch;
char* goos;
+char* theline;
void
Lflag(char *arg)
{
- if(nlibdir >= nelem(libdir)-1) {
- print("too many -L's: %d\n", nlibdir);
- usage();
+ char **p;
+
+ if(nlibdir >= maxlibdir) {
+ if (maxlibdir == 0)
+ maxlibdir = 8;
+ else
+ maxlibdir *= 2;
+ p = realloc(libdir, maxlibdir * sizeof(*p));
+ if (p == nil) {
+ print("too many -L's: %d\n", nlibdir);
+ usage();
+ }
+ libdir = p;
}
libdir[nlibdir++] = arg;
}
@@ -68,9 +80,14 @@ libinit(void)
print("goarch is not known: %s\n", goarch);
// add goroot to the end of the libdir list.
- libdir[nlibdir++] = smprint("%s/pkg/%s_%s", goroot, goos, goarch);
+ Lflag(smprint("%s/pkg/%s_%s", goroot, goos, goarch));
+ // Unix doesn't like it when we write to a running (or, sometimes,
+ // recently run) binary, so remove the output file before writing it.
+ // On Windows 7, remove() can force the following create() to fail.
+#ifndef _WIN32
remove(outfile);
+#endif
cout = create(outfile, 1, 0775);
if(cout < 0) {
diag("cannot create %s", outfile);
@@ -109,7 +126,7 @@ addlib(char *src, char *obj)
sprint(name, "");
i = 1;
} else
- if(isalpha(histfrog[0]->name[1]) && histfrog[0]->name[2] == ':') {
+ if(isalpha((uchar)histfrog[0]->name[1]) && histfrog[0]->name[2] == ':') {
strcpy(name, histfrog[0]->name+1);
i = 1;
} else
@@ -268,6 +285,7 @@ loadlib(void)
for(i=0; i<libraryp; i++) {
if(debug['v'])
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);
}
@@ -295,19 +313,27 @@ nextar(Biobuf *bp, int off, struct ar_hdr *a)
{
int r;
int32 arsize;
+ char *buf;
if (off&01)
off++;
Bseek(bp, off, 0);
- r = Bread(bp, a, SAR_HDR);
+ buf = Brdline(bp, '\n');
+ r = Blinelen(bp);
+ if(buf == nil) {
+ if(r == 0)
+ return 0;
+ return -1;
+ }
if(r != SAR_HDR)
- return 0;
- if(strncmp(a->fmag, ARFMAG, sizeof(a->fmag)))
+ return -1;
+ memmove(a, buf, SAR_HDR);
+ if(strncmp(a->fmag, ARFMAG, sizeof a->fmag))
return -1;
arsize = strtol(a->size, 0, 0);
if (arsize&1)
arsize++;
- return arsize + SAR_HDR;
+ return arsize + r;
}
void
@@ -336,6 +362,7 @@ objfile(char *file, char *pkg)
Bseek(f, 0L, 0);
ldobj(f, pkg, l, file, FileObj);
Bterm(f);
+ free(pkg);
return;
}
@@ -397,6 +424,7 @@ objfile(char *file, char *pkg)
out:
Bterm(f);
+ free(pkg);
}
void
@@ -424,14 +452,17 @@ ldobj(Biobuf *f, char *pkg, int64 len, char *pn, int whence)
magic = c1<<24 | c2<<16 | c3<<8 | c4;
if(magic == 0x7f454c46) { // \x7F E L F
ldelf(f, pkg, len, pn);
+ free(pn);
return;
}
if((magic&~1) == 0xfeedface || (magic&~0x01000000) == 0xcefaedfe) {
ldmacho(f, pkg, len, pn);
+ free(pn);
return;
}
if(c1 == 0x4c && c2 == 0x01 || c1 == 0x64 && c2 == 0x86) {
ldpe(f, pkg, len, pn);
+ free(pn);
return;
}
@@ -457,14 +488,36 @@ ldobj(Biobuf *f, char *pkg, int64 len, char *pn, int whence)
return;
}
diag("%s: not an object file", pn);
+ free(pn);
return;
}
- t = smprint("%s %s %s", getgoos(), thestring, getgoversion());
- if(strcmp(line+10, t) != 0 && !debug['f']) {
+
+ // First, check that the basic goos, string, and version match.
+ t = smprint("%s %s %s ", goos, thestring, getgoversion());
+ line[n] = ' ';
+ if(strncmp(line+10, t, strlen(t)) != 0 && !debug['f']) {
+ line[n] = '\0';
diag("%s: object is [%s] expected [%s]", pn, line+10, t);
free(t);
+ free(pn);
return;
}
+
+ // Second, check that longer lines match each other exactly,
+ // so that the Go compiler and write additional information
+ // that must be the same from run to run.
+ line[n] = '\0';
+ if(n-10 > strlen(t)) {
+ if(theline == nil)
+ theline = strdup(line+10);
+ else if(strcmp(theline, line+10) != 0) {
+ line[n] = '\0';
+ diag("%s: object is [%s] expected [%s]", pn, line+10, theline);
+ free(t);
+ free(pn);
+ return;
+ }
+ }
free(t);
line[n] = '\n';
@@ -487,10 +540,12 @@ ldobj(Biobuf *f, char *pkg, int64 len, char *pn, int whence)
Bseek(f, import1, 0);
ldobj1(f, pkg, eof - Boffset(f), pn);
+ free(pn);
return;
eof:
diag("truncated object file: %s", pn);
+ free(pn);
}
static Sym*
@@ -870,18 +925,26 @@ unmal(void *v, uint32 n)
* Convert raw string to the prefix that will be used in the symbol table.
* Invalid bytes turn into %xx. Right now the only bytes that need
* escaping are %, ., and ", but we escape all control characters too.
+ *
+ * Must be same as ../gc/subr.c:/^pathtoprefix.
*/
static char*
pathtoprefix(char *s)
{
static char hex[] = "0123456789abcdef";
- char *p, *r, *w;
+ char *p, *r, *w, *l;
int n;
+ // find first character past the last slash, if any.
+ l = s;
+ for(r=s; *r; r++)
+ if(*r == '/')
+ l = r+1;
+
// check for chars that need escaping
n = 0;
for(r=s; *r; r++)
- if(*r <= ' ' || *r == '.' || *r == '%' || *r == '"')
+ if(*r <= ' ' || (*r == '.' && r >= l) || *r == '%' || *r == '"' || *r >= 0x7f)
n++;
// quick exit
@@ -891,7 +954,7 @@ pathtoprefix(char *s)
// escape
p = mal((r-s)+1+2*n);
for(r=s, w=p; *r; r++) {
- if(*r <= ' ' || *r == '.' || *r == '%' || *r == '"') {
+ if(*r <= ' ' || (*r == '.' && r >= l) || *r == '%' || *r == '"' || *r >= 0x7f) {
*w++ = '%';
*w++ = hex[(*r>>4)&0xF];
*w++ = hex[*r&0xF];
@@ -1332,7 +1395,7 @@ Yconv(Fmt *fp)
fmtprint(fp, "<nil>");
} else {
fmtstrinit(&fmt);
- fmtprint(&fmt, "%s @0x%08x [%d]", s->name, s->value, s->size);
+ fmtprint(&fmt, "%s @0x%08llx [%lld]", s->name, (vlong)s->value, (vlong)s->size);
for (i = 0; i < s->size; i++) {
if (!(i%8)) fmtprint(&fmt, "\n\t0x%04x ", i);
fmtprint(&fmt, "%02x ", s->p[i]);
diff --git a/src/cmd/ld/lib.h b/src/cmd/ld/lib.h
index d13eea31e..02dac6e1c 100644
--- a/src/cmd/ld/lib.h
+++ b/src/cmd/ld/lib.h
@@ -43,11 +43,13 @@ enum
SPCLNTAB,
SELFROSECT,
SELFSECT,
+ SNOPTRDATA,
SDATA,
SMACHO, /* Mach-O __nl_symbol_ptr */
SMACHOGOT,
SWINDOWS,
SBSS,
+ SNOPTRBSS,
SXREF,
SMACHODYNSTR,
@@ -101,7 +103,7 @@ struct Section
};
extern char symname[];
-extern char *libdir[];
+extern char **libdir;
extern int nlibdir;
EXTERN char* INITENTRY;
@@ -125,10 +127,12 @@ EXTERN int32 nsymbol;
EXTERN char* thestring;
EXTERN int ndynexp;
EXTERN int havedynamic;
+EXTERN int iscgo;
EXTERN Segment segtext;
EXTERN Segment segdata;
EXTERN Segment segsym;
+EXTERN Segment segdwarf;
void addlib(char *src, char *obj);
void addlibpath(char *srcref, char *objref, char *file, char *pkg);
@@ -177,6 +181,7 @@ void reloc(void);
void relocsym(Sym*);
void savedata(Sym*, Prog*, char*);
void symgrow(Sym*, int32);
+void addstrdata(char*, char*);
vlong addstring(Sym*, char*);
vlong adduint32(Sym*, uint32);
vlong adduint64(Sym*, uint64);
@@ -191,6 +196,7 @@ void asmelfsym(void);
void asmplan9sym(void);
void strnput(char*, int);
void dodata(void);
+void dosymtype(void);
void address(void);
void textaddress(void);
void genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*));
diff --git a/src/cmd/ld/macho.c b/src/cmd/ld/macho.c
index 70133d665..6781c25a4 100644
--- a/src/cmd/ld/macho.c
+++ b/src/cmd/ld/macho.c
@@ -413,9 +413,9 @@ asmbmacho(void)
// must match domacholink below
s1 = lookup(".dynsym", 0);
- s2 = lookup(".dynstr", 0);
- s3 = lookup(".linkedit.plt", 0);
- s4 = lookup(".linkedit.got", 0);
+ s2 = lookup(".linkedit.plt", 0);
+ s3 = lookup(".linkedit.got", 0);
+ s4 = lookup(".dynstr", 0);
ms = newMachoSeg("__LINKEDIT", 0);
ms->vaddr = va+v+rnd(segdata.len, INITRND);
@@ -428,8 +428,8 @@ asmbmacho(void)
ml = newMachoLoad(2, 4); /* LC_SYMTAB */
ml->data[0] = linkoff; /* symoff */
ml->data[1] = s1->size / (macho64 ? 16 : 12); /* nsyms */
- ml->data[2] = linkoff + s1->size; /* stroff */
- ml->data[3] = s2->size; /* strsize */
+ ml->data[2] = linkoff + s1->size + s2->size + s3->size; /* stroff */
+ ml->data[3] = s4->size; /* strsize */
ml = newMachoLoad(11, 18); /* LC_DYSYMTAB */
ml->data[0] = 0; /* ilocalsym */
@@ -444,8 +444,8 @@ asmbmacho(void)
ml->data[9] = 0; /* nmodtab */
ml->data[10] = 0; /* extrefsymoff */
ml->data[11] = 0; /* nextrefsyms */
- ml->data[12] = linkoff + s1->size + s2->size; /* indirectsymoff */
- ml->data[13] = (s3->size + s4->size) / 4; /* nindirectsyms */
+ ml->data[12] = linkoff + s1->size; /* indirectsymoff */
+ ml->data[13] = (s2->size + s3->size) / 4; /* nindirectsyms */
ml->data[14] = 0; /* extreloff */
ml->data[15] = 0; /* nextrel */
ml->data[16] = 0; /* locreloff */
@@ -495,17 +495,34 @@ domacholink(void)
// write data that will be linkedit section
s1 = lookup(".dynsym", 0);
relocsym(s1);
- s2 = lookup(".dynstr", 0);
- s3 = lookup(".linkedit.plt", 0);
- s4 = lookup(".linkedit.got", 0);
-
- while(s2->size%4)
- adduint8(s2, 0);
+ s2 = lookup(".linkedit.plt", 0);
+ s3 = lookup(".linkedit.got", 0);
+ s4 = lookup(".dynstr", 0);
+
+ // Force the linkedit section to end on a 16-byte
+ // boundary. This allows pure (non-cgo) Go binaries
+ // to be code signed correctly.
+ //
+ // Apple's codesign_allocate (a helper utility for
+ // the codesign utility) can do this fine itself if
+ // it is run on a dynamic Mach-O binary. However,
+ // when it is run on a pure (non-cgo) Go binary, where
+ // the linkedit section is mostly empty, it fails to
+ // account for the extra padding that it itself adds
+ // when adding the LC_CODE_SIGNATURE load command
+ // (which must be aligned on a 16-byte boundary).
+ //
+ // By forcing the linkedit section to end on a 16-byte
+ // boundary, codesign_allocate will not need to apply
+ // any alignment padding itself, working around the
+ // issue.
+ while(s4->size%16)
+ adduint8(s4, 0);
size = s1->size + s2->size + s3->size + s4->size;
if(size > 0) {
- linkoff = rnd(HEADR+segtext.len, INITRND) + rnd(segdata.filelen, INITRND);
+ linkoff = rnd(HEADR+segtext.len, INITRND) + rnd(segdata.filelen, INITRND) + rnd(segdwarf.filelen, INITRND);
cseek(linkoff);
cwrite(s1->p, s1->size);
diff --git a/src/cmd/ld/pe.c b/src/cmd/ld/pe.c
index 334c9959f..1d70b4808 100644
--- a/src/cmd/ld/pe.c
+++ b/src/cmd/ld/pe.c
@@ -32,6 +32,11 @@ static char dosstub[] =
0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
+// Note: currently only up to 8 chars plus \0.
+static char *symlabels[] = {
+ "symtab", "esymtab", "pclntab", "epclntab"
+};
+
static Sym *rsrcsym;
static char symnames[256];
@@ -44,6 +49,7 @@ static int pe64;
static int nsect;
static int nextsectoff;
static int nextfileoff;
+static int textsect;
static IMAGE_FILE_HEADER fh;
static IMAGE_OPTIONAL_HEADER oh;
@@ -449,20 +455,29 @@ addsymtable(void)
{
IMAGE_SECTION_HEADER *h;
int i, size;
+ Sym *s;
- if(nextsymoff == 0)
- return;
-
- size = nextsymoff + 4 + 18;
+ fh.NumberOfSymbols = sizeof(symlabels)/sizeof(symlabels[0]);
+ size = nextsymoff + 4 + 18*fh.NumberOfSymbols;
h = addpesection(".symtab", size, size);
h->Characteristics = IMAGE_SCN_MEM_READ|
IMAGE_SCN_MEM_DISCARDABLE;
chksectoff(h, cpos());
fh.PointerToSymbolTable = cpos();
- fh.NumberOfSymbols = 1;
- strnput("", 18); // one empty symbol
- // put symbol string table
- lputl(size);
+
+ // put COFF symbol table
+ for (i=0; i<fh.NumberOfSymbols; i++) {
+ s = rlookup(symlabels[i], 0);
+ strnput(s->name, 8);
+ lputl(datoff(s->value));
+ wputl(textsect);
+ wputl(0x0308); // "array of structs"
+ cput(2); // storage class: external
+ cput(0); // no aux entries
+ }
+
+ // put COFF string table
+ lputl(nextsymoff + 4);
for (i=0; i<nextsymoff; i++)
cput(symnames[i]);
strnput("", h->SizeOfRawData - size);
@@ -510,6 +525,48 @@ addpersrc(void)
dd[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = h->VirtualSize;
}
+static void
+addexcept(IMAGE_SECTION_HEADER *text)
+{
+ IMAGE_SECTION_HEADER *pdata, *xdata;
+ vlong startoff;
+ uvlong n;
+ Sym *sym;
+
+ if(thechar != '6')
+ return;
+
+ // write unwind info
+ sym = lookup("runtime.sigtramp", 0);
+ startoff = cpos();
+ lputl(9); // version=1, flags=UNW_FLAG_EHANDLER, rest 0
+ lputl(sym->value - PEBASE);
+ lputl(0);
+
+ n = cpos() - startoff;
+ xdata = addpesection(".xdata", n, n);
+ xdata->Characteristics = IMAGE_SCN_MEM_READ|
+ IMAGE_SCN_CNT_INITIALIZED_DATA;
+ chksectoff(xdata, startoff);
+ strnput("", xdata->SizeOfRawData - n);
+
+ // write a function table entry for the whole text segment
+ startoff = cpos();
+ lputl(text->VirtualAddress);
+ lputl(text->VirtualAddress + text->VirtualSize);
+ lputl(xdata->VirtualAddress);
+
+ n = cpos() - startoff;
+ pdata = addpesection(".pdata", n, n);
+ pdata->Characteristics = IMAGE_SCN_MEM_READ|
+ IMAGE_SCN_CNT_INITIALIZED_DATA;
+ chksectoff(pdata, startoff);
+ strnput("", pdata->SizeOfRawData - n);
+
+ dd[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress = pdata->VirtualAddress;
+ dd[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size = pdata->VirtualSize;
+}
+
void
asmbpe(void)
{
@@ -532,6 +589,7 @@ asmbpe(void)
IMAGE_SCN_CNT_INITIALIZED_DATA|
IMAGE_SCN_MEM_EXECUTE|IMAGE_SCN_MEM_READ;
chksectseg(t, &segtext);
+ textsect = nsect;
d = addpesection(".data", segdata.len, segdata.filelen);
d->Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA|
@@ -546,7 +604,8 @@ asmbpe(void)
addexports();
addsymtable();
addpersrc();
-
+ addexcept(t);
+
fh.NumberOfSections = nsect;
fh.TimeDateStamp = time(0);
fh.Characteristics = IMAGE_FILE_RELOCS_STRIPPED|
@@ -561,7 +620,7 @@ asmbpe(void)
set(Magic, 0x10b); // PE32
oh.BaseOfData = d->VirtualAddress;
}
- set(MajorLinkerVersion, 1);
+ set(MajorLinkerVersion, 3);
set(MinorLinkerVersion, 0);
set(SizeOfCode, t->SizeOfRawData);
set(SizeOfInitializedData, d->SizeOfRawData);
@@ -583,8 +642,29 @@ asmbpe(void)
set(Subsystem, IMAGE_SUBSYSTEM_WINDOWS_GUI);
else
set(Subsystem, IMAGE_SUBSYSTEM_WINDOWS_CUI);
- set(SizeOfStackReserve, 0x0040000);
- set(SizeOfStackCommit, 0x00001000);
+
+ // Disable stack growth as we don't want Windows to
+ // fiddle with the thread stack limits, which we set
+ // ourselves to circumvent the stack checks in the
+ // Windows exception dispatcher.
+ // Commit size must be strictly less than reserve
+ // size otherwise reserve will be rounded up to a
+ // larger size, as verified with VMMap.
+
+ // Go code would be OK with 64k stacks, but we need larger stacks for cgo.
+ // That default stack reserve size affects only the main thread,
+ // for other threads we specify stack size in runtime explicitly
+ // (runtime knows whether cgo is enabled or not).
+ // If you change stack reserve sizes here,
+ // change them in runtime/cgo/windows_386/amd64.c as well.
+ if(!iscgo) {
+ set(SizeOfStackReserve, 0x00010000);
+ set(SizeOfStackCommit, 0x0000ffff);
+ } else {
+ set(SizeOfStackReserve, pe64 ? 0x00200000 : 0x00100000);
+ // account for 2 guard pages
+ set(SizeOfStackCommit, (pe64 ? 0x00200000 : 0x00100000) - 0x2000);
+ }
set(SizeOfHeapReserve, 0x00100000);
set(SizeOfHeapCommit, 0x00001000);
set(NumberOfRvaAndSizes, 16);
diff --git a/src/cmd/ld/symtab.c b/src/cmd/ld/symtab.c
index 60e146b35..129b13ea0 100644
--- a/src/cmd/ld/symtab.c
+++ b/src/cmd/ld/symtab.c
@@ -34,10 +34,7 @@
#include "../ld/lib.h"
#include "../ld/elf.h"
-char *elfstrdat;
-int elfstrsize;
-int maxelfstr;
-int elftextsh;
+static int maxelfstr;
int
putelfstr(char *s)
@@ -327,14 +324,22 @@ symtab(void)
{
Sym *s;
+ dosymtype();
+
// Define these so that they'll get put into the symbol table.
// data.c:/^address will provide the actual values.
xdefine("text", STEXT, 0);
xdefine("etext", STEXT, 0);
xdefine("rodata", SRODATA, 0);
xdefine("erodata", SRODATA, 0);
- xdefine("data", SBSS, 0);
- xdefine("edata", SBSS, 0);
+ xdefine("noptrdata", SNOPTRDATA, 0);
+ xdefine("enoptrdata", SNOPTRDATA, 0);
+ xdefine("data", SDATA, 0);
+ xdefine("edata", SDATA, 0);
+ xdefine("bss", SBSS, 0);
+ xdefine("ebss", SBSS, 0);
+ xdefine("noptrbss", SNOPTRBSS, 0);
+ xdefine("enoptrbss", SNOPTRBSS, 0);
xdefine("end", SBSS, 0);
xdefine("epclntab", SRODATA, 0);
xdefine("esymtab", SRODATA, 0);
diff --git a/src/cmd/nm/Makefile b/src/cmd/nm/Makefile
index 81bc348de..3f528d751 100644
--- a/src/cmd/nm/Makefile
+++ b/src/cmd/nm/Makefile
@@ -1,15 +1,5 @@
-# Copyright 2009 The Go Authors. All rights reserved.
+# 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 ../../Make.inc
-O:=$(HOST_O)
-
-# The directory is nm because the source is portable and general.
-# We call the binary 6nm to avoid confusion with the host nm.
-
-TARG=6nm
-OFILES=\
- nm.$O\
-
-include ../../Make.ccmd
+include ../../Make.dist
diff --git a/src/cmd/nm/doc.go b/src/cmd/nm/doc.go
index 2a37dd835..c84369a5f 100644
--- a/src/cmd/nm/doc.go
+++ b/src/cmd/nm/doc.go
@@ -14,8 +14,8 @@ Plan 9 C compiler.
This implementation adds the flag -S, which prints each symbol's size
in decimal after its address.
-For reasons of disambiguation it is installed as 6nm although it also serves
-as an 8nm and a 5nm.
+Usage:
+ go tool nm [-aghnsTu] file
*/
package documentation
diff --git a/src/cmd/objdump/main.c b/src/cmd/objdump/main.c
new file mode 100644
index 000000000..b684be7fb
--- /dev/null
+++ b/src/cmd/objdump/main.c
@@ -0,0 +1,68 @@
+// 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.
+
+/*
+ * objdump simulation - only enough to make pprof work on Macs
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+
+void
+usage(void)
+{
+ fprint(2, "usage: objdump binary start stop\n");
+ fprint(2, "Disassembles binary from PC start up to stop.\n");
+ exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+ int fd, n;
+ uvlong pc, start, stop;
+ Fhdr fhdr;
+ Biobuf bout;
+ char buf[1024];
+ Map *text;
+
+ ARGBEGIN{
+ default:
+ usage();
+ }ARGEND
+
+ if(argc != 3)
+ usage();
+ start = strtoull(argv[1], 0, 16);
+ stop = strtoull(argv[2], 0, 16);
+
+ fd = open(argv[0], OREAD);
+ if(fd < 0)
+ sysfatal("open %s: %r", argv[0]);
+ if(crackhdr(fd, &fhdr) <= 0)
+ sysfatal("crackhdr: %r");
+ machbytype(fhdr.type);
+ if(syminit(fd, &fhdr) <= 0)
+ sysfatal("syminit: %r");
+ text = loadmap(nil, fd, &fhdr);
+ if(text == nil)
+ sysfatal("loadmap: %r");
+
+ Binit(&bout, 1, OWRITE);
+ for(pc=start; pc<stop; ) {
+ if(fileline(buf, sizeof buf, pc))
+ Bprint(&bout, "%s\n", buf);
+ buf[0] = '\0';
+ machdata->das(text, pc, 0, buf, sizeof buf);
+ Bprint(&bout, " %llx: %s\n", pc, buf);
+ n = machdata->instsize(text, pc);
+ if(n <= 0)
+ break;
+ pc += n;
+ }
+ Bflush(&bout);
+ exits(0);
+}
diff --git a/src/cmd/pack/Makefile b/src/cmd/pack/Makefile
new file mode 100644
index 000000000..3f528d751
--- /dev/null
+++ b/src/cmd/pack/Makefile
@@ -0,0 +1,5 @@
+# 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 ../../Make.dist
diff --git a/src/cmd/gopack/ar.c b/src/cmd/pack/ar.c
index 0b5e608c7..7e07fbc89 100644
--- a/src/cmd/gopack/ar.c
+++ b/src/cmd/pack/ar.c
@@ -37,7 +37,6 @@
#define rcmd your_rcmd
#include <u.h>
-#include <time.h>
#include <libc.h>
#include <bio.h>
#include <mach.h>
@@ -228,12 +227,12 @@ main(int argc, char *argv[])
case 'S': Sflag = 1; break;
case 'P': Pflag = 1; break;
default:
- fprint(2, "gopack: bad option `%c'\n", *cp);
+ fprint(2, "pack: bad option `%c'\n", *cp);
exits("error");
}
}
if (aflag && bflag) {
- fprint(2, "gopack: only one of 'a' and 'b' can be specified\n");
+ fprint(2, "pack: only one of 'a' and 'b' can be specified\n");
usage();
}
if(aflag || bflag) {
@@ -245,7 +244,7 @@ main(int argc, char *argv[])
}
if(Pflag) {
if(argc < 4) {
- fprint(2, "gopack: P flag requires prefix argument\n");
+ fprint(2, "pack: P flag requires prefix argument\n");
usage();
}
prefix = argv[2];
@@ -254,7 +253,7 @@ main(int argc, char *argv[])
}
if(comfun == 0) {
if(uflag == 0) {
- fprint(2, "gopack: one of [%s] must be specified\n", man);
+ fprint(2, "pack: one of [%s] must be specified\n", man);
usage();
}
setcom(rcmd);
@@ -268,7 +267,7 @@ main(int argc, char *argv[])
cp = 0;
while (argc--) {
if (*argv) {
- fprint(2, "gopack: %s not found\n", *argv);
+ fprint(2, "pack: %s not found\n", *argv);
cp = "error";
}
argv++;
@@ -285,7 +284,7 @@ setcom(void (*fun)(char *, int, char**))
{
if(comfun != 0) {
- fprint(2, "gopack: only one of [%s] allowed\n", man);
+ fprint(2, "pack: only one of [%s] allowed\n", man);
usage();
}
comfun = fun;
@@ -346,7 +345,7 @@ rcmd(char *arname, int count, char **files)
bfile = Bopen(file, OREAD);
if (!bfile) {
if (count != 0) {
- fprint(2, "gopack: cannot open %s\n", file);
+ fprint(2, "pack: cannot open %s\n", file);
errors++;
}
scanobj(&bar, ap, bp->size);
@@ -355,7 +354,7 @@ rcmd(char *arname, int count, char **files)
}
d = dirfstat(Bfildes(bfile));
if(d == nil)
- fprint(2, "gopack: cannot stat %s: %r\n", file);
+ fprint(2, "pack: cannot stat %s: %r\n", file);
if (uflag && (d==nil || d->mtime <= bp->date)) {
scanobj(&bar, ap, bp->size);
arcopy(&bar, ap, bp);
@@ -380,7 +379,7 @@ rcmd(char *arname, int count, char **files)
files[i] = 0;
bfile = Bopen(file, OREAD);
if (!bfile) {
- fprint(2, "gopack: cannot open %s\n", file);
+ fprint(2, "pack: cannot open %s\n", file);
errors++;
} else {
mesg('a', file);
@@ -448,7 +447,7 @@ xcmd(char *arname, int count, char **files)
mode = strtoul(bp->hdr.mode, 0, 8) & 0777;
f = create(file, OWRITE, mode);
if(f < 0) {
- fprint(2, "gopack: %s cannot create\n", file);
+ fprint(2, "pack: %s cannot create\n", file);
skip(&bar, bp->size);
} else {
mesg('x', file);
@@ -542,7 +541,7 @@ mcmd(char *arname, int count, char **files)
}
close(fd);
if (poname[0] && aend == 0)
- fprint(2, "gopack: %s not found - files moved to end.\n", poname);
+ fprint(2, "pack: %s not found - files moved to end.\n", poname);
install(arname, astart, amiddle, aend, 0);
}
void
@@ -575,13 +574,13 @@ qcmd(char *arname, int count, char **files)
Biobuf *bfile;
if(aflag || bflag) {
- fprint(2, "gopack: abi not allowed with q\n");
+ fprint(2, "pack: abi not allowed with q\n");
exits("error");
}
fd = openar(arname, ORDWR, 1);
if (fd < 0) {
if(!cflag)
- fprint(2, "gopack: creating %s\n", arname);
+ fprint(2, "pack: creating %s\n", arname);
fd = arcreate(arname);
}
Binit(&bar, fd, OREAD);
@@ -595,7 +594,7 @@ qcmd(char *arname, int count, char **files)
files[i] = 0;
bfile = Bopen(file, OREAD);
if(!bfile) {
- fprint(2, "gopack: cannot open %s\n", file);
+ fprint(2, "pack: cannot open %s\n", file);
errors++;
} else {
mesg('q', file);
@@ -612,12 +611,49 @@ qcmd(char *arname, int count, char **files)
}
/*
+ * does the object header line p match the last one we saw?
+ * update *lastp if it gets more specific.
+ */
+int
+matchhdr(char *p, char **lastp)
+{
+ int n;
+ char *last;
+
+ // no information?
+ last = *lastp;
+ if(last == nil) {
+ *lastp = strdup(p);
+ return 1;
+ }
+
+ // identical match?
+ if(strcmp(last, p) == 0)
+ return 1;
+
+ // last has extra fields
+ n = strlen(p);
+ if(n < strlen(last) && last[n] == ' ')
+ return 1;
+
+ // p has extra fields - save in last
+ n = strlen(last);
+ if(n < strlen(p) && p[n] == ' ') {
+ free(last);
+ *lastp = strdup(p);
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
* extract the symbol references from an object file
*/
void
scanobj(Biobuf *b, Arfile *ap, long size)
{
- int obj;
+ int obj, goobject;
vlong offset, offset1;
Dir *d;
static int lastobj = -1;
@@ -644,13 +680,13 @@ scanobj(Biobuf *b, Arfile *ap, long size)
}
if (!gflag || strcmp(file, pkgdef) != 0) { /* don't clear allobj if it's pkg defs */
- fprint(2, "gopack: non-object file %s\n", file);
+ fprint(2, "pack: non-object file %s\n", file);
errors++;
allobj = 0;
}
d = dirfstat(Bfildes(b));
if (d != nil && d->length == 0) {
- fprint(2, "gopack: zero length file %s\n", file);
+ fprint(2, "pack: zero length file %s\n", file);
errors++;
}
free(d);
@@ -658,33 +694,48 @@ scanobj(Biobuf *b, Arfile *ap, long size)
return;
}
+ goobject = 1;
offset1 = Boffset(b);
Bseek(b, offset, 0);
p = Brdstr(b, '\n', 1);
+
+ // After the go object header comes the Go metadata,
+ // followed by ! on a line by itself. If this is not a Go object,
+ // 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) == '!')
+ goobject = 0;
+
Bseek(b, offset1, 0);
if(p == nil || strncmp(p, "go object ", 10) != 0) {
- fprint(2, "gopack: malformed object file %s\n", file);
+ fprint(2, "pack: malformed object file %s\n", file);
errors++;
Bseek(b, offset, 0);
free(p);
return;
}
- if ((lastobj >= 0 && obj != lastobj) || (objhdr != nil && strcmp(p, objhdr) != 0)) {
- fprint(2, "gopack: inconsistent object file %s\n", file);
+ if (!matchhdr(p, &objhdr)) {
+ fprint(2, "pack: inconsistent object file %s: [%s] vs [%s]\n", file, p, objhdr);
errors++;
allobj = 0;
free(p);
return;
}
+ free(p);
+
+ // Old check. Should be impossible since objhdrs match, but keep the check anyway.
+ if (lastobj >= 0 && obj != lastobj) {
+ fprint(2, "pack: inconsistent object file %s\n", file);
+ errors++;
+ allobj = 0;
+ return;
+ }
lastobj = obj;
- if(objhdr == nil)
- objhdr = p;
- else
- free(p);
if (!readar(b, obj, offset+size, 0)) {
- fprint(2, "gopack: invalid symbol reference in file %s\n", file);
+ fprint(2, "pack: invalid symbol reference in file %s\n", file);
errors++;
allobj = 0;
Bseek(b, offset, 0);
@@ -692,7 +743,7 @@ scanobj(Biobuf *b, Arfile *ap, long size)
}
Bseek(b, offset, 0);
objtraverse(objsym, ap);
- if (gflag) {
+ if (gflag && goobject) {
scanpkg(b, size);
Bseek(b, offset, 0);
}
@@ -774,7 +825,7 @@ scanpkg(Biobuf *b, long size)
continue;
goto foundstart;
}
- // fprint(2, "gopack: warning: no package import section in %s\n", file);
+ // fprint(2, "pack: warning: no package import section in %s\n", file);
if(b != &bar || !pkgdefsafe)
safe = 0; // non-Go file (C or assembly)
return;
@@ -786,7 +837,6 @@ foundstart:
goto bad;
/* how big is it? */
- pkg = nil;
first = 1;
start = end = 0;
for (n=0; n<size; n+=Blinelen(b)) {
@@ -825,7 +875,7 @@ foundstart:
free(line);
}
bad:
- fprint(2, "gopack: bad package import section in %s\n", file);
+ fprint(2, "pack: bad package import section in %s\n", file);
errors++;
return;
@@ -835,7 +885,7 @@ foundend:
if (end == 0)
goto bad;
if(importblock != nil) {
- fprint(2, "gopack: multiple Go object files\n");
+ fprint(2, "pack: multiple Go object files\n");
errors++;
return;
}
@@ -843,7 +893,7 @@ foundend:
data = armalloc(end - start + 1);
Bseek(b, start, 0);
if (Bread(b, data, pkgsize) != pkgsize) {
- fprint(2, "gopack: error reading package import section in %s\n", file);
+ fprint(2, "pack: error reading package import section in %s\n", file);
errors++;
return;
}
@@ -943,11 +993,11 @@ openar(char *arname, int mode, int errok)
fd = open(arname, mode);
if(fd >= 0){
if(read(fd, mbuf, SARMAG) != SARMAG || strncmp(mbuf, ARMAG, SARMAG)) {
- fprint(2, "gopack: %s not in archive format\n", arname);
+ fprint(2, "pack: %s not in archive format\n", arname);
exits("error");
}
}else if(!errok){
- fprint(2, "gopack: cannot open %s: %r\n", arname);
+ fprint(2, "pack: cannot open %s: %r\n", arname);
exits("error");
}
return fd;
@@ -963,7 +1013,7 @@ arcreate(char *arname)
fd = create(arname, OWRITE, 0664);
if(fd < 0){
- fprint(2, "gopack: cannot create %s: %r\n", arname);
+ fprint(2, "pack: cannot create %s: %r\n", arname);
exits("error");
}
if(write(fd, ARMAG, SARMAG) != SARMAG)
@@ -977,28 +1027,28 @@ arcreate(char *arname)
void
wrerr(void)
{
- perror("gopack: write error");
+ perror("pack: write error");
exits("error");
}
void
rderr(void)
{
- perror("gopack: read error");
+ perror("pack: read error");
exits("error");
}
void
phaseerr(int offset)
{
- fprint(2, "gopack: phase error at offset %d\n", offset);
+ fprint(2, "pack: phase error at offset %d\n", offset);
exits("error");
}
void
usage(void)
{
- fprint(2, "usage: gopack [%s][%s][P prefix] archive files ...\n", opt, man);
+ fprint(2, "usage: pack [%s][%s][P prefix] archive files ...\n", opt, man);
exits("error");
}
@@ -1042,7 +1092,7 @@ armove(Biobuf *b, Arfile *ap, Armember *bp)
d = dirfstat(Bfildes(b));
if (d == nil) {
- fprint(2, "gopack: cannot stat %s\n", file);
+ fprint(2, "pack: cannot stat %s\n", file);
return;
}
@@ -1050,7 +1100,7 @@ armove(Biobuf *b, Arfile *ap, Armember *bp)
for (cp = strchr(bp->hdr.name, 0); /* blank pad on right */
cp < bp->hdr.name+sizeof(bp->hdr.name); cp++)
*cp = ' ';
- sprint(bp->hdr.date, "%-12ld", 0); // was d->mtime but removed for idempotent builds
+ sprint(bp->hdr.date, "%-12ld", 0L); // was d->mtime but removed for idempotent builds
sprint(bp->hdr.uid, "%-6d", 0);
sprint(bp->hdr.gid, "%-6d", 0);
sprint(bp->hdr.mode, "%-8lo", d->mode);
@@ -1143,7 +1193,7 @@ install(char *arname, Arfile *astart, Arfile *amiddle, Arfile *aend, int createf
rfork(RFNOTEG);
if(createflag)
- fprint(2, "gopack: creating %s\n", arname);
+ fprint(2, "pack: creating %s\n", arname);
fd = arcreate(arname);
if(allobj)
@@ -1184,7 +1234,7 @@ rl(int fd)
len = symdefsize;
if(len&01)
len++;
- sprint(a.date, "%-12ld", 0); // time(0)
+ sprint(a.date, "%-12ld", 0L); // time(0)
sprint(a.uid, "%-6d", 0);
sprint(a.gid, "%-6d", 0);
sprint(a.mode, "%-8lo", 0644L);
@@ -1221,7 +1271,7 @@ rl(int fd)
if (gflag) {
len = pkgdefsize;
- sprint(a.date, "%-12ld", 0); // time(0)
+ sprint(a.date, "%-12ld", 0L); // time(0)
sprint(a.uid, "%-6d", 0);
sprint(a.gid, "%-6d", 0);
sprint(a.mode, "%-8lo", 0644L);
@@ -1332,11 +1382,14 @@ mesg(int c, char *file)
void
trim(char *s, char *buf, int n)
{
- char *p;
+ char *p, *q;
for(;;) {
p = strrchr(s, '/');
- if (!p) { /* no slash in name */
+ q = strrchr(s, '\\');
+ if (q > p)
+ p = q;
+ if (!p) { /* no (back)slash in name */
strncpy(buf, s, n);
return;
}
@@ -1344,7 +1397,7 @@ trim(char *s, char *buf, int n)
strncpy(buf, p+1, n);
return;
}
- *p = 0; /* strip trailing slash */
+ *p = 0; /* strip trailing (back)slash */
}
}
@@ -1368,15 +1421,12 @@ void
longt(Armember *bp)
{
char *cp;
- time_t date;
pmode(strtoul(bp->hdr.mode, 0, 8));
Bprint(&bout, "%3ld/%1ld", strtol(bp->hdr.uid, 0, 0), strtol(bp->hdr.gid, 0, 0));
Bprint(&bout, "%7ld", bp->size);
- date = bp->date;
- cp = ctime(&date);
- /* using unix ctime, not plan 9 time, so cp+20 for year, not cp+24 */
- Bprint(&bout, " %-12.12s %-4.4s ", cp+4, cp+20);
+ cp = ctime(bp->date);
+ Bprint(&bout, " %-12.12s %-4.4s ", cp+4, cp+24);
}
int m1[] = { 1, ROWN, 'r', '-' };
@@ -1532,6 +1582,8 @@ arwrite(int fd, Armember *bp)
int
page(Arfile *ap)
{
+ USED(ap);
+
sysfatal("page");
return 1;
}
@@ -1544,7 +1596,6 @@ page(Arfile *ap)
int
getspace(void)
{
-fprint(2, "IN GETSPACE\n");
if (astart && astart->head && page(astart))
return 1;
if (amiddle && amiddle->head && page(amiddle))
@@ -1589,7 +1640,7 @@ armalloc(int n)
return cp;
}
} while (getspace());
- fprint(2, "gopack: out of memory\n");
+ fprint(2, "pack: out of memory\n");
exits("malloc");
return 0;
}
diff --git a/src/cmd/gopack/doc.go b/src/cmd/pack/doc.go
index 1551a275f..8b17f3ca2 100644
--- a/src/cmd/gopack/doc.go
+++ b/src/cmd/pack/doc.go
@@ -4,7 +4,7 @@
/*
-Gopack is a variant of the Plan 9 ar tool. The original is documented at
+Pack is a variant of the Plan 9 ar tool. The original is documented at
http://plan9.bell-labs.com/magic/man2html/1/ar
@@ -12,14 +12,15 @@ It adds a special Go-specific section __.PKGDEF that collects all the
Go type information from the files in the archive; that section is
used by the compiler when importing the package during compilation.
-Usage: gopack [uvnbailogS][mrxtdpq][P prefix] archive files ...
+Usage:
+ go tool pack [uvnbailogS][mrxtdpq][P prefix] archive files ...
-The new option 'g' causes gopack to maintain the __.PKGDEF section
+The new option 'g' causes pack to maintain the __.PKGDEF section
as files are added to the archive.
-The new option 'S' forces gopack to mark the archive as safe.
+The new option 'S' forces pack to mark the archive as safe.
-The new option 'P' causes gopack to remove the given prefix
+The new option 'P' causes pack to remove the given prefix
from file names in the line number information in object files
that are already stored in or added to the archive.
*/
diff --git a/src/cmd/prof/Makefile b/src/cmd/prof/Makefile
deleted file mode 100644
index 8a1a2f308..000000000
--- a/src/cmd/prof/Makefile
+++ /dev/null
@@ -1,38 +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 ../../Make.inc
-O:=$(HOST_O)
-
-# The directory is prof because the source is portable and general.
-# We call the binary 6prof to avoid confusion and because this binary
-# is linked only with amd64 and x86 support.
-
-TARG=6prof
-OFILES=\
- main.$O\
-
-NOINSTALL=1
-include ../../Make.ccmd
-
-ifeq ($(GOOS),windows)
-NAME=windows
-else
-NAME=$(shell uname | tr A-Z a-z)
-endif
-
-install: install-$(NAME) install-pprof
-install-linux: install-default
-install-freebsd: install-default
-install-windows: install-default
-
-# on Darwin, have to install and setgid; see $GOROOT/src/sudo.bash
-install-darwin: $(TARG)
- @true
-
-install-default: $(TARG)
- cp $(TARG) "$(GOBIN)"/$(TARG)
-
-install-pprof: gopprof
- cp gopprof "$(GOBIN)"/gopprof
diff --git a/src/cmd/prof/doc.go b/src/cmd/prof/doc.go
deleted file mode 100644
index 1f2209f04..000000000
--- a/src/cmd/prof/doc.go
+++ /dev/null
@@ -1,48 +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.
-
-/*
-
-Prof is a rudimentary real-time profiler.
-
-Given a command to run or the process id (pid) of a command already
-running, it samples the program's state at regular intervals and reports
-on its behavior. With no options, it prints a histogram of the locations
-in the code that were sampled during execution.
-
-Since it is a real-time profiler, unlike a traditional profiler it samples
-the program's state even when it is not running, such as when it is
-asleep or waiting for I/O. Each thread contributes equally to the
-statistics.
-
-
-Usage: prof -p pid [-t total_secs] [-d delta_msec] [6.out args ...]
-
-The output modes (default -h) are:
-
- -P file.prof:
- Write the profile information to file.prof, in the format used by pprof.
- At the moment, this only works on Linux amd64 binaries and requires that the
- binary be written using 6l -e to produce ELF debug info.
- See http://code.google.com/p/google-perftools for details.
- -h: histograms
- How many times a sample occurred at each location.
- -f: dynamic functions
- At each sample period, print the name of the executing function.
- -l: dynamic file and line numbers
- At each sample period, print the file and line number of the executing instruction.
- -r: dynamic registers
- At each sample period, print the register contents.
- -s: dynamic function stack traces
- At each sample period, print the symbolic stack trace.
-
-Flag -t sets the maximum real time to sample, in seconds, and -d
-sets the sampling interval in milliseconds. The default is to sample
-every 100ms until the program completes.
-
-For reasons of disambiguation it is installed as 6prof although it also serves
-as an 8prof and a 5prof.
-
-*/
-package documentation
diff --git a/src/cmd/prof/gopprof b/src/cmd/prof/gopprof
deleted file mode 100755
index be5f84e9e..000000000
--- a/src/cmd/prof/gopprof
+++ /dev/null
@@ -1,4975 +0,0 @@
-#! /usr/bin/env perl
-
-# This is a copy of http://google-perftools.googlecode.com/svn/trunk/src/pprof
-# with local modifications to handle generation of SVG images and
-# the Go-style pprof paths. These modifications will probably filter
-# back into the official source before long.
-# It's convenient to have a copy here because we need just the one
-# Perl script, not all the C++ libraries that surround it.
-
-# Copyright (c) 1998-2007, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-# * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-# ---
-# Program for printing the profile generated by common/profiler.cc,
-# or by the heap profiler (common/debugallocation.cc)
-#
-# The profile contains a sequence of entries of the form:
-# <count> <stack trace>
-# This program parses the profile, and generates user-readable
-# output.
-#
-# Examples:
-#
-# % tools/pprof "program" "profile"
-# Enters "interactive" mode
-#
-# % tools/pprof --text "program" "profile"
-# Generates one line per procedure
-#
-# % tools/pprof --gv "program" "profile"
-# Generates annotated call-graph and displays via "gv"
-#
-# % tools/pprof --gv --focus=Mutex "program" "profile"
-# Restrict to code paths that involve an entry that matches "Mutex"
-#
-# % tools/pprof --gv --focus=Mutex --ignore=string "program" "profile"
-# Restrict to code paths that involve an entry that matches "Mutex"
-# and does not match "string"
-#
-# % tools/pprof --list=IBF_CheckDocid "program" "profile"
-# Generates disassembly listing of all routines with at least one
-# sample that match the --list=<regexp> pattern. The listing is
-# annotated with the flat and cumulative sample counts at each line.
-#
-# % tools/pprof --disasm=IBF_CheckDocid "program" "profile"
-# Generates disassembly listing of all routines with at least one
-# sample that match the --disasm=<regexp> pattern. The listing is
-# annotated with the flat and cumulative sample counts at each PC value.
-#
-# TODO: Use color to indicate files?
-
-use strict;
-use warnings;
-use Getopt::Long;
-
-my $PPROF_VERSION = "1.5";
-
-# These are the object tools we use which can come from a
-# user-specified location using --tools, from the PPROF_TOOLS
-# environment variable, or from the environment.
-my %obj_tool_map = (
- "objdump" => "objdump",
- "nm" => "nm",
- "addr2line" => "addr2line",
- "c++filt" => "c++filt",
- ## ConfigureObjTools may add architecture-specific entries:
- #"nm_pdb" => "nm-pdb", # for reading windows (PDB-format) executables
- #"addr2line_pdb" => "addr2line-pdb", # ditto
- #"otool" => "otool", # equivalent of objdump on OS X
-);
-my $DOT = "dot"; # leave non-absolute, since it may be in /usr/local
-my $GV = "gv";
-my $KCACHEGRIND = "kcachegrind";
-my $PS2PDF = "ps2pdf";
-# These are used for dynamic profiles
-my $CURL = "curl";
-
-# These are the web pages that servers need to support for dynamic profiles
-my $HEAP_PAGE = "/pprof/heap";
-my $PROFILE_PAGE = "/pprof/profile"; # must support cgi-param "?seconds=#"
-my $PMUPROFILE_PAGE = "/pprof/pmuprofile(?:\\?.*)?"; # must support cgi-param
- # ?seconds=#&event=x&period=n
-my $GROWTH_PAGE = "/pprof/growth";
-my $CONTENTION_PAGE = "/pprof/contention";
-my $WALL_PAGE = "/pprof/wall(?:\\?.*)?"; # accepts options like namefilter
-my $FILTEREDPROFILE_PAGE = "/pprof/filteredprofile(?:\\?.*)?";
-my $SYMBOL_PAGE = "/pprof/symbol"; # must support symbol lookup via POST
-my $PROGRAM_NAME_PAGE = "/pprof/cmdline";
-
-# default binary name
-my $UNKNOWN_BINARY = "(unknown)";
-
-# There is a pervasive dependency on the length (in hex characters,
-# i.e., nibbles) of an address, distinguishing between 32-bit and
-# 64-bit profiles. To err on the safe size, default to 64-bit here:
-my $address_length = 16;
-
-# A list of paths to search for shared object files
-my @prefix_list = ();
-
-# Special routine name that should not have any symbols.
-# Used as separator to parse "addr2line -i" output.
-my $sep_symbol = '_fini';
-my $sep_address = undef;
-
-##### Argument parsing #####
-
-sub usage_string {
- return <<EOF;
-Usage:
-pprof [options] <program> <profiles>
- <profiles> is a space separated list of profile names.
-pprof [options] <symbolized-profiles>
- <symbolized-profiles> is a list of profile files where each file contains
- the necessary symbol mappings as well as profile data (likely generated
- with --raw).
-pprof [options] <profile>
- <profile> is a remote form. Symbols are obtained from host:port$SYMBOL_PAGE
-
- Each name can be:
- /path/to/profile - a path to a profile file
- host:port[/<service>] - a location of a service to get profile from
-
- The /<service> can be $HEAP_PAGE, $PROFILE_PAGE, /pprof/pmuprofile,
- $GROWTH_PAGE, $CONTENTION_PAGE, /pprof/wall,
- or /pprof/filteredprofile.
- For instance:
- pprof http://myserver.com:80$HEAP_PAGE
- If /<service> is omitted, the service defaults to $PROFILE_PAGE (cpu profiling).
-pprof --symbols <program>
- Maps addresses to symbol names. In this mode, stdin should be a
- list of library mappings, in the same format as is found in the heap-
- and cpu-profile files (this loosely matches that of /proc/self/maps
- on linux), followed by a list of hex addresses to map, one per line.
-
- For more help with querying remote servers, including how to add the
- necessary server-side support code, see this filename (or one like it):
-
- /usr/doc/google-perftools-$PPROF_VERSION/pprof_remote_servers.html
-
-Options:
- --cum Sort by cumulative data
- --base=<base> Subtract <base> from <profile> before display
- --interactive Run in interactive mode (interactive "help" gives help) [default]
- --seconds=<n> Length of time for dynamic profiles [default=30 secs]
- --add_lib=<file> Read additional symbols and line info from the given library
- --lib_prefix=<dir> Comma separated list of library path prefixes
-
-Reporting Granularity:
- --addresses Report at address level
- --lines Report at source line level
- --functions Report at function level [default]
- --files Report at source file level
-
-Output type:
- --text Generate text report
- --callgrind Generate callgrind format to stdout
- --gv Generate Postscript and display
- --web Generate SVG and display
- --list=<regexp> Generate source listing of matching routines
- --disasm=<regexp> Generate disassembly of matching routines
- --symbols Print demangled symbol names found at given addresses
- --dot Generate DOT file to stdout
- --ps Generate Postcript to stdout
- --pdf Generate PDF to stdout
- --svg Generate SVG to stdout
- --gif Generate GIF to stdout
- --raw Generate symbolized pprof data (useful with remote fetch)
-
-Heap-Profile Options:
- --inuse_space Display in-use (mega)bytes [default]
- --inuse_objects Display in-use objects
- --alloc_space Display allocated (mega)bytes
- --alloc_objects Display allocated objects
- --show_bytes Display space in bytes
- --drop_negative Ignore negative differences
-
-Contention-profile options:
- --total_delay Display total delay at each region [default]
- --contentions Display number of delays at each region
- --mean_delay Display mean delay at each region
-
-Call-graph Options:
- --nodecount=<n> Show at most so many nodes [default=80]
- --nodefraction=<f> Hide nodes below <f>*total [default=.005]
- --edgefraction=<f> Hide edges below <f>*total [default=.001]
- --focus=<regexp> Focus on nodes matching <regexp>
- --ignore=<regexp> Ignore nodes matching <regexp>
- --scale=<n> Set GV scaling [default=0]
- --heapcheck Make nodes with non-0 object counts
- (i.e. direct leak generators) more visible
-
-Miscellaneous:
- --tools=<prefix> Prefix for object tool pathnames
- --test Run unit tests
- --help This message
- --version Version information
-
-Environment Variables:
- PPROF_TMPDIR Profiles directory. Defaults to \$HOME/pprof
- PPROF_TOOLS Prefix for object tools pathnames
-
-Examples:
-
-pprof /bin/ls ls.prof
- Enters "interactive" mode
-pprof --text /bin/ls ls.prof
- Outputs one line per procedure
-pprof --web /bin/ls ls.prof
- Displays annotated call-graph in web browser
-pprof --gv /bin/ls ls.prof
- Displays annotated call-graph via 'gv'
-pprof --gv --focus=Mutex /bin/ls ls.prof
- Restricts to code paths including a .*Mutex.* entry
-pprof --gv --focus=Mutex --ignore=string /bin/ls ls.prof
- Code paths including Mutex but not string
-pprof --list=getdir /bin/ls ls.prof
- (Per-line) annotated source listing for getdir()
-pprof --disasm=getdir /bin/ls ls.prof
- (Per-PC) annotated disassembly for getdir()
-
-pprof http://localhost:1234/
- Enters "interactive" mode
-pprof --text localhost:1234
- Outputs one line per procedure for localhost:1234
-pprof --raw localhost:1234 > ./local.raw
-pprof --text ./local.raw
- Fetches a remote profile for later analysis and then
- analyzes it in text mode.
-EOF
-}
-
-sub version_string {
- return <<EOF
-pprof (part of google-perftools $PPROF_VERSION)
-
-Copyright 1998-2007 Google Inc.
-
-This is BSD licensed software; see the source for copying conditions
-and license information.
-There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
-PARTICULAR PURPOSE.
-EOF
-}
-
-sub usage {
- my $msg = shift;
- print STDERR "$msg\n\n";
- print STDERR usage_string();
- print STDERR "\nFATAL ERROR: $msg\n"; # just as a reminder
- exit(1);
-}
-
-sub Init() {
- # Setup tmp-file name and handler to clean it up.
- # We do this in the very beginning so that we can use
- # error() and cleanup() function anytime here after.
- $main::tmpfile_sym = "/tmp/pprof$$.sym";
- $main::tmpfile_ps = "/tmp/pprof$$";
- $main::next_tmpfile = 0;
- $SIG{'INT'} = \&sighandler;
-
- # Cache from filename/linenumber to source code
- $main::source_cache = ();
-
- $main::opt_help = 0;
- $main::opt_version = 0;
-
- $main::opt_cum = 0;
- $main::opt_base = '';
- $main::opt_addresses = 0;
- $main::opt_lines = 0;
- $main::opt_functions = 0;
- $main::opt_files = 0;
- $main::opt_lib_prefix = "";
-
- $main::opt_text = 0;
- $main::opt_callgrind = 0;
- $main::opt_list = "";
- $main::opt_disasm = "";
- $main::opt_symbols = 0;
- $main::opt_gv = 0;
- $main::opt_web = 0;
- $main::opt_dot = 0;
- $main::opt_ps = 0;
- $main::opt_pdf = 0;
- $main::opt_gif = 0;
- $main::opt_svg = 0;
- $main::opt_raw = 0;
-
- $main::opt_nodecount = 80;
- $main::opt_nodefraction = 0.005;
- $main::opt_edgefraction = 0.001;
- $main::opt_focus = '';
- $main::opt_ignore = '';
- $main::opt_scale = 0;
- $main::opt_heapcheck = 0;
- $main::opt_seconds = 30;
- $main::opt_lib = "";
-
- $main::opt_inuse_space = 0;
- $main::opt_inuse_objects = 0;
- $main::opt_alloc_space = 0;
- $main::opt_alloc_objects = 0;
- $main::opt_show_bytes = 0;
- $main::opt_drop_negative = 0;
- $main::opt_interactive = 0;
-
- $main::opt_total_delay = 0;
- $main::opt_contentions = 0;
- $main::opt_mean_delay = 0;
-
- $main::opt_tools = "";
- $main::opt_debug = 0;
- $main::opt_test = 0;
-
- # These are undocumented flags used only by unittests.
- $main::opt_test_stride = 0;
-
- # Are we using $SYMBOL_PAGE?
- $main::use_symbol_page = 0;
-
- # Files returned by TempName.
- %main::tempnames = ();
-
- # Type of profile we are dealing with
- # Supported types:
- # cpu
- # heap
- # growth
- # contention
- $main::profile_type = ''; # Empty type means "unknown"
-
- GetOptions("help!" => \$main::opt_help,
- "version!" => \$main::opt_version,
- "cum!" => \$main::opt_cum,
- "base=s" => \$main::opt_base,
- "seconds=i" => \$main::opt_seconds,
- "add_lib=s" => \$main::opt_lib,
- "lib_prefix=s" => \$main::opt_lib_prefix,
- "functions!" => \$main::opt_functions,
- "lines!" => \$main::opt_lines,
- "addresses!" => \$main::opt_addresses,
- "files!" => \$main::opt_files,
- "text!" => \$main::opt_text,
- "callgrind!" => \$main::opt_callgrind,
- "list=s" => \$main::opt_list,
- "disasm=s" => \$main::opt_disasm,
- "symbols!" => \$main::opt_symbols,
- "gv!" => \$main::opt_gv,
- "web!" => \$main::opt_web,
- "dot!" => \$main::opt_dot,
- "ps!" => \$main::opt_ps,
- "pdf!" => \$main::opt_pdf,
- "svg!" => \$main::opt_svg,
- "gif!" => \$main::opt_gif,
- "raw!" => \$main::opt_raw,
- "interactive!" => \$main::opt_interactive,
- "nodecount=i" => \$main::opt_nodecount,
- "nodefraction=f" => \$main::opt_nodefraction,
- "edgefraction=f" => \$main::opt_edgefraction,
- "focus=s" => \$main::opt_focus,
- "ignore=s" => \$main::opt_ignore,
- "scale=i" => \$main::opt_scale,
- "heapcheck" => \$main::opt_heapcheck,
- "inuse_space!" => \$main::opt_inuse_space,
- "inuse_objects!" => \$main::opt_inuse_objects,
- "alloc_space!" => \$main::opt_alloc_space,
- "alloc_objects!" => \$main::opt_alloc_objects,
- "show_bytes!" => \$main::opt_show_bytes,
- "drop_negative!" => \$main::opt_drop_negative,
- "total_delay!" => \$main::opt_total_delay,
- "contentions!" => \$main::opt_contentions,
- "mean_delay!" => \$main::opt_mean_delay,
- "tools=s" => \$main::opt_tools,
- "test!" => \$main::opt_test,
- "debug!" => \$main::opt_debug,
- # Undocumented flags used only by unittests:
- "test_stride=i" => \$main::opt_test_stride,
- ) || usage("Invalid option(s)");
-
- # Deal with the standard --help and --version
- if ($main::opt_help) {
- print usage_string();
- exit(0);
- }
-
- if ($main::opt_version) {
- print version_string();
- exit(0);
- }
-
- # Disassembly/listing/symbols mode requires address-level info
- if ($main::opt_disasm || $main::opt_list || $main::opt_symbols) {
- $main::opt_functions = 0;
- $main::opt_lines = 0;
- $main::opt_addresses = 1;
- $main::opt_files = 0;
- }
-
- # Check heap-profiling flags
- if ($main::opt_inuse_space +
- $main::opt_inuse_objects +
- $main::opt_alloc_space +
- $main::opt_alloc_objects > 1) {
- usage("Specify at most on of --inuse/--alloc options");
- }
-
- # Check output granularities
- my $grains =
- $main::opt_functions +
- $main::opt_lines +
- $main::opt_addresses +
- $main::opt_files +
- 0;
- if ($grains > 1) {
- usage("Only specify one output granularity option");
- }
- if ($grains == 0) {
- $main::opt_functions = 1;
- }
-
- # Check output modes
- my $modes =
- $main::opt_text +
- $main::opt_callgrind +
- ($main::opt_list eq '' ? 0 : 1) +
- ($main::opt_disasm eq '' ? 0 : 1) +
- ($main::opt_symbols == 0 ? 0 : 1) +
- $main::opt_gv +
- $main::opt_web +
- $main::opt_dot +
- $main::opt_ps +
- $main::opt_pdf +
- $main::opt_svg +
- $main::opt_gif +
- $main::opt_raw +
- $main::opt_interactive +
- 0;
- if ($modes > 1) {
- usage("Only specify one output mode");
- }
- if ($modes == 0) {
- if (-t STDOUT) { # If STDOUT is a tty, activate interactive mode
- $main::opt_interactive = 1;
- } else {
- $main::opt_text = 1;
- }
- }
-
- if ($main::opt_test) {
- RunUnitTests();
- # Should not return
- exit(1);
- }
-
- # Binary name and profile arguments list
- $main::prog = "";
- @main::pfile_args = ();
-
- # Remote profiling without a binary (using $SYMBOL_PAGE instead)
- if (IsProfileURL($ARGV[0])) {
- $main::use_symbol_page = 1;
- } elsif (IsSymbolizedProfileFile($ARGV[0])) {
- $main::use_symbolized_profile = 1;
- $main::prog = $UNKNOWN_BINARY; # will be set later from the profile file
- }
-
- if ($main::use_symbol_page || $main::use_symbolized_profile) {
- # We don't need a binary!
- my %disabled = ('--lines' => $main::opt_lines,
- '--disasm' => $main::opt_disasm);
- for my $option (keys %disabled) {
- usage("$option cannot be used without a binary") if $disabled{$option};
- }
- # Set $main::prog later...
- scalar(@ARGV) || usage("Did not specify profile file");
- } elsif ($main::opt_symbols) {
- # --symbols needs a binary-name (to run nm on, etc) but not profiles
- $main::prog = shift(@ARGV) || usage("Did not specify program");
- } else {
- $main::prog = shift(@ARGV) || usage("Did not specify program");
- scalar(@ARGV) || usage("Did not specify profile file");
- }
-
- # Parse profile file/location arguments
- foreach my $farg (@ARGV) {
- if ($farg =~ m/(.*)\@([0-9]+)(|\/.*)$/ ) {
- my $machine = $1;
- my $num_machines = $2;
- my $path = $3;
- for (my $i = 0; $i < $num_machines; $i++) {
- unshift(@main::pfile_args, "$i.$machine$path");
- }
- } else {
- unshift(@main::pfile_args, $farg);
- }
- }
-
- if ($main::use_symbol_page) {
- unless (IsProfileURL($main::pfile_args[0])) {
- error("The first profile should be a remote form to use $SYMBOL_PAGE\n");
- }
- CheckSymbolPage();
- $main::prog = FetchProgramName();
- } elsif (!$main::use_symbolized_profile) { # may not need objtools!
- ConfigureObjTools($main::prog)
- }
-
- # Break the opt_lib_prefix into the prefix_list array
- @prefix_list = split (',', $main::opt_lib_prefix);
-
- # Remove trailing / from the prefixes, in the list to prevent
- # searching things like /my/path//lib/mylib.so
- foreach (@prefix_list) {
- s|/+$||;
- }
-}
-
-sub Main() {
- Init();
- $main::collected_profile = undef;
- @main::profile_files = ();
- $main::op_time = time();
-
- # Printing symbols is special and requires a lot less info that most.
- if ($main::opt_symbols) {
- PrintSymbols(*STDIN); # Get /proc/maps and symbols output from stdin
- return;
- }
-
- # Fetch all profile data
- FetchDynamicProfiles();
-
- # this will hold symbols that we read from the profile files
- my $symbol_map = {};
-
- # Read one profile, pick the last item on the list
- my $data = ReadProfile($main::prog, pop(@main::profile_files));
- my $profile = $data->{profile};
- my $pcs = $data->{pcs};
- my $libs = $data->{libs}; # Info about main program and shared libraries
- $symbol_map = MergeSymbols($symbol_map, $data->{symbols});
-
- # Add additional profiles, if available.
- if (scalar(@main::profile_files) > 0) {
- foreach my $pname (@main::profile_files) {
- my $data2 = ReadProfile($main::prog, $pname);
- $profile = AddProfile($profile, $data2->{profile});
- $pcs = AddPcs($pcs, $data2->{pcs});
- $symbol_map = MergeSymbols($symbol_map, $data2->{symbols});
- }
- }
-
- # Subtract base from profile, if specified
- if ($main::opt_base ne '') {
- my $base = ReadProfile($main::prog, $main::opt_base);
- $profile = SubtractProfile($profile, $base->{profile});
- $pcs = AddPcs($pcs, $base->{pcs});
- $symbol_map = MergeSymbols($symbol_map, $base->{symbols});
- }
-
- # Get total data in profile
- my $total = TotalProfile($profile);
-
- # Collect symbols
- my $symbols;
- if ($main::use_symbolized_profile) {
- $symbols = FetchSymbols($pcs, $symbol_map);
- } elsif ($main::use_symbol_page) {
- $symbols = FetchSymbols($pcs);
- } else {
- $symbols = ExtractSymbols($libs, $pcs);
- }
-
- # Remove uniniteresting stack items
- $profile = RemoveUninterestingFrames($symbols, $profile);
-
- # Focus?
- if ($main::opt_focus ne '') {
- $profile = FocusProfile($symbols, $profile, $main::opt_focus);
- }
-
- # Ignore?
- if ($main::opt_ignore ne '') {
- $profile = IgnoreProfile($symbols, $profile, $main::opt_ignore);
- }
-
- my $calls = ExtractCalls($symbols, $profile);
-
- # Reduce profiles to required output granularity, and also clean
- # each stack trace so a given entry exists at most once.
- my $reduced = ReduceProfile($symbols, $profile);
-
- # Get derived profiles
- my $flat = FlatProfile($reduced);
- my $cumulative = CumulativeProfile($reduced);
-
- # Print
- if (!$main::opt_interactive) {
- if ($main::opt_disasm) {
- PrintDisassembly($libs, $flat, $cumulative, $main::opt_disasm, $total);
- } elsif ($main::opt_list) {
- PrintListing($total, $libs, $flat, $cumulative, $main::opt_list, 0);
- } elsif ($main::opt_text) {
- # Make sure the output is empty when have nothing to report
- # (only matters when --heapcheck is given but we must be
- # compatible with old branches that did not pass --heapcheck always):
- if ($total != 0) {
- printf("Total: %s %s\n", Unparse($total), Units());
- }
- PrintText($symbols, $flat, $cumulative, $total, -1);
- } elsif ($main::opt_raw) {
- PrintSymbolizedProfile($symbols, $profile, $main::prog);
- } elsif ($main::opt_callgrind) {
- PrintCallgrind($calls);
- } else {
- if (PrintDot($main::prog, $symbols, $profile, $flat, $cumulative, $total)) {
- if ($main::opt_gv) {
- RunGV(TempName($main::next_tmpfile, "ps"), "");
- } elsif ($main::opt_web) {
- my $tmp = TempName($main::next_tmpfile, "svg");
- RunWeb($tmp);
- # The command we run might hand the file name off
- # to an already running browser instance and then exit.
- # Normally, we'd remove $tmp on exit (right now),
- # but fork a child to remove $tmp a little later, so that the
- # browser has time to load it first.
- delete $main::tempnames{$tmp};
- if (fork() == 0) {
- sleep 5;
- unlink($tmp);
- exit(0);
- }
- }
- } else {
- exit(1);
- }
- }
- } else {
- InteractiveMode($profile, $symbols, $libs, $total);
- }
-
- cleanup();
- exit(0);
-}
-
-##### Entry Point #####
-
-Main();
-
-# Temporary code to detect if we're running on a Goobuntu system.
-# These systems don't have the right stuff installed for the special
-# Readline libraries to work, so as a temporary workaround, we default
-# to using the normal stdio code, rather than the fancier readline-based
-# code
-sub ReadlineMightFail {
- if (-e '/lib/libtermcap.so.2') {
- return 0; # libtermcap exists, so readline should be okay
- } else {
- return 1;
- }
-}
-
-sub RunGV {
- my $fname = shift;
- my $bg = shift; # "" or " &" if we should run in background
- if (!system("$GV --version >/dev/null 2>&1")) {
- # Options using double dash are supported by this gv version.
- # Also, turn on noantialias to better handle bug in gv for
- # postscript files with large dimensions.
- # TODO: Maybe we should not pass the --noantialias flag
- # if the gv version is known to work properly without the flag.
- system("$GV --scale=$main::opt_scale --noantialias " . $fname . $bg);
- } else {
- # Old gv version - only supports options that use single dash.
- print STDERR "$GV -scale $main::opt_scale\n";
- system("$GV -scale $main::opt_scale " . $fname . $bg);
- }
-}
-
-sub RunWeb {
- my $fname = shift;
- print STDERR "Loading web page file:///$fname\n";
-
- if (`uname` =~ /Darwin/) {
- # OS X: open will use standard preference for SVG files.
- system("/usr/bin/open", $fname);
- return;
- }
-
- # Some kind of Unix; try generic symlinks, then specific browsers.
- # (Stop once we find one.)
- # Works best if the browser is already running.
- my @alt = (
- "/etc/alternatives/gnome-www-browser",
- "/etc/alternatives/x-www-browser",
- "google-chrome",
- "firefox",
- );
- foreach my $b (@alt) {
- if (-f $b) {
- if (system($b, $fname) == 0) {
- return;
- }
- }
- }
-
- print STDERR "Could not load web browser.\n";
-}
-
-sub RunKcachegrind {
- my $fname = shift;
- my $bg = shift; # "" or " &" if we should run in background
- print STDERR "Starting '$KCACHEGRIND " . $fname . $bg . "'\n";
- system("$KCACHEGRIND " . $fname . $bg);
-}
-
-
-##### Interactive helper routines #####
-
-sub InteractiveMode {
- $| = 1; # Make output unbuffered for interactive mode
- my ($orig_profile, $symbols, $libs, $total) = @_;
-
- print STDERR "Welcome to pprof! For help, type 'help'.\n";
-
- # Use ReadLine if it's installed and input comes from a console.
- if ( -t STDIN &&
- !ReadlineMightFail() &&
- defined(eval {require Term::ReadLine}) ) {
- my $term = new Term::ReadLine 'pprof';
- while ( defined ($_ = $term->readline('(pprof) '))) {
- $term->addhistory($_) if /\S/;
- if (!InteractiveCommand($orig_profile, $symbols, $libs, $total, $_)) {
- last; # exit when we get an interactive command to quit
- }
- }
- } else { # don't have readline
- while (1) {
- print STDERR "(pprof) ";
- $_ = <STDIN>;
- last if ! defined $_ ;
- s/\r//g; # turn windows-looking lines into unix-looking lines
-
- # Save some flags that might be reset by InteractiveCommand()
- my $save_opt_lines = $main::opt_lines;
-
- if (!InteractiveCommand($orig_profile, $symbols, $libs, $total, $_)) {
- last; # exit when we get an interactive command to quit
- }
-
- # Restore flags
- $main::opt_lines = $save_opt_lines;
- }
- }
-}
-
-# Takes two args: orig profile, and command to run.
-# Returns 1 if we should keep going, or 0 if we were asked to quit
-sub InteractiveCommand {
- my($orig_profile, $symbols, $libs, $total, $command) = @_;
- $_ = $command; # just to make future m//'s easier
- if (!defined($_)) {
- print STDERR "\n";
- return 0;
- }
- if (m/^\s*quit/) {
- return 0;
- }
- if (m/^\s*help/) {
- InteractiveHelpMessage();
- return 1;
- }
- # Clear all the mode options -- mode is controlled by "$command"
- $main::opt_text = 0;
- $main::opt_callgrind = 0;
- $main::opt_disasm = 0;
- $main::opt_list = 0;
- $main::opt_gv = 0;
- $main::opt_cum = 0;
-
- if (m/^\s*(text|top)(\d*)\s*(.*)/) {
- $main::opt_text = 1;
-
- my $line_limit = ($2 ne "") ? int($2) : 10;
-
- my $routine;
- my $ignore;
- ($routine, $ignore) = ParseInteractiveArgs($3);
-
- my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore);
- my $reduced = ReduceProfile($symbols, $profile);
-
- # Get derived profiles
- my $flat = FlatProfile($reduced);
- my $cumulative = CumulativeProfile($reduced);
-
- PrintText($symbols, $flat, $cumulative, $total, $line_limit);
- return 1;
- }
- if (m/^\s*callgrind\s*([^ \n]*)/) {
- $main::opt_callgrind = 1;
-
- # Get derived profiles
- my $calls = ExtractCalls($symbols, $orig_profile);
- my $filename = $1;
- if ( $1 eq '' ) {
- $filename = TempName($main::next_tmpfile, "callgrind");
- }
- PrintCallgrind($calls, $filename);
- if ( $1 eq '' ) {
- RunKcachegrind($filename, " & ");
- $main::next_tmpfile++;
- }
-
- return 1;
- }
- if (m/^\s*(web)?list\s*(.+)/) {
- my $html = (defined($1) && ($1 eq "web"));
- $main::opt_list = 1;
-
- my $routine;
- my $ignore;
- ($routine, $ignore) = ParseInteractiveArgs($2);
-
- my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore);
- my $reduced = ReduceProfile($symbols, $profile);
-
- # Get derived profiles
- my $flat = FlatProfile($reduced);
- my $cumulative = CumulativeProfile($reduced);
-
- PrintListing($total, $libs, $flat, $cumulative, $routine, $html);
- return 1;
- }
- if (m/^\s*disasm\s*(.+)/) {
- $main::opt_disasm = 1;
-
- my $routine;
- my $ignore;
- ($routine, $ignore) = ParseInteractiveArgs($1);
-
- # Process current profile to account for various settings
- my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore);
- my $reduced = ReduceProfile($symbols, $profile);
-
- # Get derived profiles
- my $flat = FlatProfile($reduced);
- my $cumulative = CumulativeProfile($reduced);
-
- PrintDisassembly($libs, $flat, $cumulative, $routine, $total);
- return 1;
- }
- if (m/^\s*(gv|web)\s*(.*)/) {
- $main::opt_gv = 0;
- $main::opt_web = 0;
- if ($1 eq "gv") {
- $main::opt_gv = 1;
- } elsif ($1 eq "web") {
- $main::opt_web = 1;
- }
-
- my $focus;
- my $ignore;
- ($focus, $ignore) = ParseInteractiveArgs($2);
-
- # Process current profile to account for various settings
- my $profile = ProcessProfile($total, $orig_profile, $symbols, $focus, $ignore);
- my $reduced = ReduceProfile($symbols, $profile);
-
- # Get derived profiles
- my $flat = FlatProfile($reduced);
- my $cumulative = CumulativeProfile($reduced);
-
- if (PrintDot($main::prog, $symbols, $profile, $flat, $cumulative, $total)) {
- if ($main::opt_gv) {
- RunGV(TempName($main::next_tmpfile, "ps"), " &");
- } elsif ($main::opt_web) {
- RunWeb(TempName($main::next_tmpfile, "svg"));
- }
- $main::next_tmpfile++;
- }
- return 1;
- }
- if (m/^\s*$/) {
- return 1;
- }
- print STDERR "Unknown command: try 'help'.\n";
- return 1;
-}
-
-
-sub ProcessProfile {
- my $total_count = shift;
- my $orig_profile = shift;
- my $symbols = shift;
- my $focus = shift;
- my $ignore = shift;
-
- # Process current profile to account for various settings
- my $profile = $orig_profile;
- printf("Total: %s %s\n", Unparse($total_count), Units());
- if ($focus ne '') {
- $profile = FocusProfile($symbols, $profile, $focus);
- my $focus_count = TotalProfile($profile);
- printf("After focusing on '%s': %s %s of %s (%0.1f%%)\n",
- $focus,
- Unparse($focus_count), Units(),
- Unparse($total_count), ($focus_count*100.0) / $total_count);
- }
- if ($ignore ne '') {
- $profile = IgnoreProfile($symbols, $profile, $ignore);
- my $ignore_count = TotalProfile($profile);
- printf("After ignoring '%s': %s %s of %s (%0.1f%%)\n",
- $ignore,
- Unparse($ignore_count), Units(),
- Unparse($total_count),
- ($ignore_count*100.0) / $total_count);
- }
-
- return $profile;
-}
-
-sub InteractiveHelpMessage {
- print STDERR <<ENDOFHELP;
-Interactive pprof mode
-
-Commands:
- gv
- gv [focus] [-ignore1] [-ignore2]
- Show graphical hierarchical display of current profile. Without
- any arguments, shows all samples in the profile. With the optional
- "focus" argument, restricts the samples shown to just those where
- the "focus" regular expression matches a routine name on the stack
- trace.
-
- web
- web [focus] [-ignore1] [-ignore2]
- Like GV, but displays profile in your web browser instead of using
- Ghostview. Works best if your web browser is already running.
- To change the browser that gets used:
- On Linux, set the /etc/alternatives/gnome-www-browser symlink.
- On OS X, change the Finder association for SVG files.
-
- list [routine_regexp] [-ignore1] [-ignore2]
- Show source listing of routines whose names match "routine_regexp"
-
- weblist [routine_regexp] [-ignore1] [-ignore2]
- Displays a source listing of routines whose names match "routine_regexp"
- in a web browser. You can click on source lines to view the
- corresponding disassembly.
-
- top [--cum] [-ignore1] [-ignore2]
- top20 [--cum] [-ignore1] [-ignore2]
- top37 [--cum] [-ignore1] [-ignore2]
- Show top lines ordered by flat profile count, or cumulative count
- if --cum is specified. If a number is present after 'top', the
- top K routines will be shown (defaults to showing the top 10)
-
- disasm [routine_regexp] [-ignore1] [-ignore2]
- Show disassembly of routines whose names match "routine_regexp",
- annotated with sample counts.
-
- callgrind
- callgrind [filename]
- Generates callgrind file. If no filename is given, kcachegrind is called.
-
- help - This listing
- quit or ^D - End pprof
-
-For commands that accept optional -ignore tags, samples where any routine in
-the stack trace matches the regular expression in any of the -ignore
-parameters will be ignored.
-
-Further pprof details are available at this location (or one similar):
-
- /usr/doc/google-perftools-$PPROF_VERSION/cpu_profiler.html
- /usr/doc/google-perftools-$PPROF_VERSION/heap_profiler.html
-
-ENDOFHELP
-}
-sub ParseInteractiveArgs {
- my $args = shift;
- my $focus = "";
- my $ignore = "";
- my @x = split(/ +/, $args);
- foreach $a (@x) {
- if ($a =~ m/^(--|-)lines$/) {
- $main::opt_lines = 1;
- } elsif ($a =~ m/^(--|-)cum$/) {
- $main::opt_cum = 1;
- } elsif ($a =~ m/^-(.*)/) {
- $ignore .= (($ignore ne "") ? "|" : "" ) . $1;
- } else {
- $focus .= (($focus ne "") ? "|" : "" ) . $a;
- }
- }
- if ($ignore ne "") {
- print STDERR "Ignoring samples in call stacks that match '$ignore'\n";
- }
- return ($focus, $ignore);
-}
-
-##### Output code #####
-
-sub TempName {
- my $fnum = shift;
- my $ext = shift;
- my $file = "$main::tmpfile_ps.$fnum.$ext";
- $main::tempnames{$file} = 1;
- return $file;
-}
-
-# Print profile data in packed binary format (64-bit) to standard out
-sub PrintProfileData {
- my $profile = shift;
-
- # print header (64-bit style)
- # (zero) (header-size) (version) (sample-period) (zero)
- print pack('L*', 0, 0, 3, 0, 0, 0, 1, 0, 0, 0);
-
- foreach my $k (keys(%{$profile})) {
- my $count = $profile->{$k};
- my @addrs = split(/\n/, $k);
- if ($#addrs >= 0) {
- my $depth = $#addrs + 1;
- # int(foo / 2**32) is the only reliable way to get rid of bottom
- # 32 bits on both 32- and 64-bit systems.
- print pack('L*', $count & 0xFFFFFFFF, int($count / 2**32));
- print pack('L*', $depth & 0xFFFFFFFF, int($depth / 2**32));
-
- foreach my $full_addr (@addrs) {
- my $addr = $full_addr;
- $addr =~ s/0x0*//; # strip off leading 0x, zeroes
- if (length($addr) > 16) {
- print STDERR "Invalid address in profile: $full_addr\n";
- next;
- }
- my $low_addr = substr($addr, -8); # get last 8 hex chars
- my $high_addr = substr($addr, -16, 8); # get up to 8 more hex chars
- print pack('L*', hex('0x' . $low_addr), hex('0x' . $high_addr));
- }
- }
- }
-}
-
-# Print symbols and profile data
-sub PrintSymbolizedProfile {
- my $symbols = shift;
- my $profile = shift;
- my $prog = shift;
-
- $SYMBOL_PAGE =~ m,[^/]+$,; # matches everything after the last slash
- my $symbol_marker = $&;
-
- print '--- ', $symbol_marker, "\n";
- if (defined($prog)) {
- print 'binary=', $prog, "\n";
- }
- while (my ($pc, $name) = each(%{$symbols})) {
- my $sep = ' ';
- print '0x', $pc;
- # We have a list of function names, which include the inlined
- # calls. They are separated (and terminated) by --, which is
- # illegal in function names.
- for (my $j = 2; $j <= $#{$name}; $j += 3) {
- print $sep, $name->[$j];
- $sep = '--';
- }
- print "\n";
- }
- print '---', "\n";
-
- $PROFILE_PAGE =~ m,[^/]+$,; # matches everything after the last slash
- my $profile_marker = $&;
- print '--- ', $profile_marker, "\n";
- if (defined($main::collected_profile)) {
- # if used with remote fetch, simply dump the collected profile to output.
- open(SRC, "<$main::collected_profile");
- while (<SRC>) {
- print $_;
- }
- close(SRC);
- } else {
- # dump a cpu-format profile to standard out
- PrintProfileData($profile);
- }
-}
-
-# Print text output
-sub PrintText {
- my $symbols = shift;
- my $flat = shift;
- my $cumulative = shift;
- my $total = shift;
- my $line_limit = shift;
-
- # Which profile to sort by?
- my $s = $main::opt_cum ? $cumulative : $flat;
-
- my $running_sum = 0;
- my $lines = 0;
- foreach my $k (sort { GetEntry($s, $b) <=> GetEntry($s, $a) || $a cmp $b }
- keys(%{$cumulative})) {
- my $f = GetEntry($flat, $k);
- my $c = GetEntry($cumulative, $k);
- $running_sum += $f;
-
- my $sym = $k;
- if (exists($symbols->{$k})) {
- $sym = $symbols->{$k}->[0] . " " . $symbols->{$k}->[1];
- if ($main::opt_addresses) {
- $sym = $k . " " . $sym;
- }
- }
-
- if ($f != 0 || $c != 0) {
- printf("%8s %6s %6s %8s %6s %s\n",
- Unparse($f),
- Percent($f, $total),
- Percent($running_sum, $total),
- Unparse($c),
- Percent($c, $total),
- $sym);
- }
- $lines++;
- last if ($line_limit >= 0 && $lines >= $line_limit);
- }
-}
-
-# Print the call graph in a way that's suiteable for callgrind.
-sub PrintCallgrind {
- my $calls = shift;
- my $filename;
- if ($main::opt_interactive) {
- $filename = shift;
- print STDERR "Writing callgrind file to '$filename'.\n"
- } else {
- $filename = "&STDOUT";
- }
- open(CG, ">".$filename );
- printf CG ("events: Hits\n\n");
- foreach my $call ( map { $_->[0] }
- sort { $a->[1] cmp $b ->[1] ||
- $a->[2] <=> $b->[2] }
- map { /([^:]+):(\d+):([^ ]+)( -> ([^:]+):(\d+):(.+))?/;
- [$_, $1, $2] }
- keys %$calls ) {
- my $count = int($calls->{$call});
- $call =~ /([^:]+):(\d+):([^ ]+)( -> ([^:]+):(\d+):(.+))?/;
- my ( $caller_file, $caller_line, $caller_function,
- $callee_file, $callee_line, $callee_function ) =
- ( $1, $2, $3, $5, $6, $7 );
-
- printf CG ("fl=$caller_file\nfn=$caller_function\n");
- if (defined $6) {
- printf CG ("cfl=$callee_file\n");
- printf CG ("cfn=$callee_function\n");
- printf CG ("calls=$count $callee_line\n");
- }
- printf CG ("$caller_line $count\n\n");
- }
-}
-
-# Print disassembly for all all routines that match $main::opt_disasm
-sub PrintDisassembly {
- my $libs = shift;
- my $flat = shift;
- my $cumulative = shift;
- my $disasm_opts = shift;
- my $total = shift;
-
- foreach my $lib (@{$libs}) {
- my $symbol_table = GetProcedureBoundaries($lib->[0], $disasm_opts);
- my $offset = AddressSub($lib->[1], $lib->[3]);
- foreach my $routine (sort ByName keys(%{$symbol_table})) {
- my $start_addr = $symbol_table->{$routine}->[0];
- my $end_addr = $symbol_table->{$routine}->[1];
- # See if there are any samples in this routine
- my $length = hex(AddressSub($end_addr, $start_addr));
- my $addr = AddressAdd($start_addr, $offset);
- for (my $i = 0; $i < $length; $i++) {
- if (defined($cumulative->{$addr})) {
- PrintDisassembledFunction($lib->[0], $offset,
- $routine, $flat, $cumulative,
- $start_addr, $end_addr, $total);
- last;
- }
- $addr = AddressInc($addr);
- }
- }
- }
-}
-
-# Return reference to array of tuples of the form:
-# [start_address, filename, linenumber, instruction, limit_address]
-# E.g.,
-# ["0x806c43d", "/foo/bar.cc", 131, "ret", "0x806c440"]
-sub Disassemble {
- my $prog = shift;
- my $offset = shift;
- my $start_addr = shift;
- my $end_addr = shift;
-
- my $objdump = $obj_tool_map{"objdump"};
- my $cmd = sprintf("$objdump -C -d -l --no-show-raw-insn " .
- "--start-address=0x$start_addr " .
- "--stop-address=0x$end_addr $prog");
- open(OBJDUMP, "$cmd |") || error("$objdump: $!\n");
- my @result = ();
- my $filename = "";
- my $linenumber = -1;
- my $last = ["", "", "", ""];
- while (<OBJDUMP>) {
- s/\r//g; # turn windows-looking lines into unix-looking lines
- chop;
- if (m|\s*([^:\s]+):(\d+)\s*$|) {
- # Location line of the form:
- # <filename>:<linenumber>
- $filename = $1;
- $linenumber = $2;
- } elsif (m/^ +([0-9a-f]+):\s*(.*)/) {
- # Disassembly line -- zero-extend address to full length
- my $addr = HexExtend($1);
- my $k = AddressAdd($addr, $offset);
- $last->[4] = $k; # Store ending address for previous instruction
- $last = [$k, $filename, $linenumber, $2, $end_addr];
- push(@result, $last);
- }
- }
- close(OBJDUMP);
- return @result;
-}
-
-# The input file should contain lines of the form /proc/maps-like
-# output (same format as expected from the profiles) or that looks
-# like hex addresses (like "0xDEADBEEF"). We will parse all
-# /proc/maps output, and for all the hex addresses, we will output
-# "short" symbol names, one per line, in the same order as the input.
-sub PrintSymbols {
- my $maps_and_symbols_file = shift;
-
- # ParseLibraries expects pcs to be in a set. Fine by us...
- my @pclist = (); # pcs in sorted order
- my $pcs = {};
- my $map = "";
- foreach my $line (<$maps_and_symbols_file>) {
- $line =~ s/\r//g; # turn windows-looking lines into unix-looking lines
- if ($line =~ /\b(0x[0-9a-f]+)\b/i) {
- push(@pclist, HexExtend($1));
- $pcs->{$pclist[-1]} = 1;
- } else {
- $map .= $line;
- }
- }
-
- my $libs = ParseLibraries($main::prog, $map, $pcs);
- my $symbols = ExtractSymbols($libs, $pcs);
-
- foreach my $pc (@pclist) {
- # ->[0] is the shortname, ->[2] is the full name
- print(($symbols->{$pc}->[0] || "??") . "\n");
- }
-}
-
-
-# For sorting functions by name
-sub ByName {
- return ShortFunctionName($a) cmp ShortFunctionName($b);
-}
-
-# Print source-listing for all all routines that match $main::opt_list
-sub PrintListing {
- my $total = shift;
- my $libs = shift;
- my $flat = shift;
- my $cumulative = shift;
- my $list_opts = shift;
- my $html = shift;
-
- my $output = \*STDOUT;
- my $fname = "";
-
-
- if ($html) {
- # Arrange to write the output to a temporary file
- $fname = TempName($main::next_tmpfile, "html");
- $main::next_tmpfile++;
- if (!open(TEMP, ">$fname")) {
- print STDERR "$fname: $!\n";
- return;
- }
- $output = \*TEMP;
- print $output HtmlListingHeader();
- printf $output ("<div class=\"legend\">%s<br>Total: %s %s</div>\n",
- $main::prog, Unparse($total), Units());
- }
-
- my $listed = 0;
- foreach my $lib (@{$libs}) {
- my $symbol_table = GetProcedureBoundaries($lib->[0], $list_opts);
- my $offset = AddressSub($lib->[1], $lib->[3]);
- foreach my $routine (sort ByName keys(%{$symbol_table})) {
- # Print if there are any samples in this routine
- my $start_addr = $symbol_table->{$routine}->[0];
- my $end_addr = $symbol_table->{$routine}->[1];
- my $length = hex(AddressSub($end_addr, $start_addr));
- my $addr = AddressAdd($start_addr, $offset);
- for (my $i = 0; $i < $length; $i++) {
- if (defined($cumulative->{$addr})) {
- $listed += PrintSource(
- $lib->[0], $offset,
- $routine, $flat, $cumulative,
- $start_addr, $end_addr,
- $html,
- $output);
- last;
- }
- $addr = AddressInc($addr);
- }
- }
- }
-
- if ($html) {
- if ($listed > 0) {
- print $output HtmlListingFooter();
- close($output);
- RunWeb($fname);
- } else {
- close($output);
- unlink($fname);
- }
- }
-}
-
-sub HtmlListingHeader {
- return <<'EOF';
-<DOCTYPE html>
-<html>
-<head>
-<title>Pprof listing</title>
-<style type="text/css">
-body {
- font-family: sans-serif;
-}
-h1 {
- font-size: 1.5em;
- margin-bottom: 4px;
-}
-.legend {
- font-size: 1.25em;
-}
-.line {
- color: #aaaaaa;
-}
-.livesrc {
- color: #0000ff;
- cursor: pointer;
-}
-.livesrc:hover {
- background-color: #cccccc;
-}
-.asm {
- color: #888888;
- display: none;
-}
-</style>
-<script type="text/javascript">
-function pprof_toggle_asm(e) {
- var target;
- if (!e) e = window.event;
- if (e.target) target = e.target;
- else if (e.srcElement) target = e.srcElement;
-
- if (target && target.className == "livesrc") {
- var asm = target.nextSibling;
- if (asm && asm.className == "asm") {
- asm.style.display = (asm.style.display == "block" ? "none" : "block");
- e.preventDefault();
- return false;
- }
- }
-}
-</script>
-</head>
-<body>
-EOF
-}
-
-sub HtmlListingFooter {
- return <<'EOF';
-</body>
-</html>
-EOF
-}
-
-sub HtmlEscape {
- my $text = shift;
- $text =~ s/&/&amp;/g;
- $text =~ s/</&lt;/g;
- $text =~ s/>/&gt;/g;
- return $text;
-}
-
-# Returns the indentation of the line, if it has any non-whitespace
-# characters. Otherwise, returns -1.
-sub Indentation {
- my $line = shift;
- if (m/^(\s*)\S/) {
- return length($1);
- } else {
- return -1;
- }
-}
-
-# Print source-listing for one routine
-sub PrintSource {
- my $prog = shift;
- my $offset = shift;
- my $routine = shift;
- my $flat = shift;
- my $cumulative = shift;
- my $start_addr = shift;
- my $end_addr = shift;
- my $html = shift;
- my $output = shift;
-
- # Disassemble all instructions (just to get line numbers)
- my @instructions = Disassemble($prog, $offset, $start_addr, $end_addr);
-
- # Hack 1: assume that the first source file encountered in the
- # disassembly contains the routine
- my $filename = undef;
- for (my $i = 0; $i <= $#instructions; $i++) {
- if ($instructions[$i]->[2] >= 0) {
- $filename = $instructions[$i]->[1];
- last;
- }
- }
- if (!defined($filename)) {
- print STDERR "no filename found in $routine\n";
- return 0;
- }
-
- # Hack 2: assume that the largest line number from $filename is the
- # end of the procedure. This is typically safe since if P1 contains
- # an inlined call to P2, then P2 usually occurs earlier in the
- # source file. If this does not work, we might have to compute a
- # density profile or just print all regions we find.
- my $lastline = 0;
- for (my $i = 0; $i <= $#instructions; $i++) {
- my $f = $instructions[$i]->[1];
- my $l = $instructions[$i]->[2];
- if (($f eq $filename) && ($l > $lastline)) {
- $lastline = $l;
- }
- }
-
- # Hack 3: assume the first source location from "filename" is the start of
- # the source code.
- my $firstline = 1;
- for (my $i = 0; $i <= $#instructions; $i++) {
- if ($instructions[$i]->[1] eq $filename) {
- $firstline = $instructions[$i]->[2];
- last;
- }
- }
-
- # Hack 4: Extend last line forward until its indentation is less than
- # the indentation we saw on $firstline
- my $oldlastline = $lastline;
- {
- if (!open(FILE, "<$filename")) {
- print STDERR "$filename: $!\n";
- return 0;
- }
- my $l = 0;
- my $first_indentation = -1;
- while (<FILE>) {
- s/\r//g; # turn windows-looking lines into unix-looking lines
- $l++;
- my $indent = Indentation($_);
- if ($l >= $firstline) {
- if ($first_indentation < 0 && $indent >= 0) {
- $first_indentation = $indent;
- last if ($first_indentation == 0);
- }
- }
- if ($l >= $lastline && $indent >= 0) {
- if ($indent >= $first_indentation) {
- $lastline = $l+1;
- } else {
- last;
- }
- }
- }
- close(FILE);
- }
-
- # Assign all samples to the range $firstline,$lastline,
- # Hack 4: If an instruction does not occur in the range, its samples
- # are moved to the next instruction that occurs in the range.
- my $samples1 = {}; # Map from line number to flat count
- my $samples2 = {}; # Map from line number to cumulative count
- my $running1 = 0; # Unassigned flat counts
- my $running2 = 0; # Unassigned cumulative counts
- my $total1 = 0; # Total flat counts
- my $total2 = 0; # Total cumulative counts
- my %disasm = (); # Map from line number to disassembly
- my $running_disasm = ""; # Unassigned disassembly
- my $skip_marker = "---\n";
- if ($html) {
- $skip_marker = "";
- for (my $l = $firstline; $l <= $lastline; $l++) {
- $disasm{$l} = "";
- }
- }
- foreach my $e (@instructions) {
- # Add up counts for all address that fall inside this instruction
- my $c1 = 0;
- my $c2 = 0;
- for (my $a = $e->[0]; $a lt $e->[4]; $a = AddressInc($a)) {
- $c1 += GetEntry($flat, $a);
- $c2 += GetEntry($cumulative, $a);
- }
-
- if ($html) {
- $running_disasm .= sprintf(" %6s %6s \t\t%8s: %s\n",
- HtmlPrintNumber($c1),
- HtmlPrintNumber($c2),
- $e->[0],
- CleanDisassembly($e->[3]));
- }
-
- $running1 += $c1;
- $running2 += $c2;
- $total1 += $c1;
- $total2 += $c2;
- my $file = $e->[1];
- my $line = $e->[2];
- if (($file eq $filename) &&
- ($line >= $firstline) &&
- ($line <= $lastline)) {
- # Assign all accumulated samples to this line
- AddEntry($samples1, $line, $running1);
- AddEntry($samples2, $line, $running2);
- $running1 = 0;
- $running2 = 0;
- if ($html) {
- $disasm{$line} .= $running_disasm;
- $running_disasm = '';
- }
- }
- }
-
- # Assign any leftover samples to $lastline
- AddEntry($samples1, $lastline, $running1);
- AddEntry($samples2, $lastline, $running2);
-
- if ($html) {
- printf $output (
- "<h1>%s</h1>%s\n<pre onClick=\"pprof_toggle_asm()\">\n" .
- "Total:%6s %6s (flat / cumulative %s)\n",
- HtmlEscape(ShortFunctionName($routine)),
- HtmlEscape($filename),
- Unparse($total1),
- Unparse($total2),
- Units());
- } else {
- printf $output (
- "ROUTINE ====================== %s in %s\n" .
- "%6s %6s Total %s (flat / cumulative)\n",
- ShortFunctionName($routine),
- $filename,
- Unparse($total1),
- Unparse($total2),
- Units());
- }
- if (!open(FILE, "<$filename")) {
- print STDERR "$filename: $!\n";
- return 0;
- }
- my $l = 0;
- while (<FILE>) {
- s/\r//g; # turn windows-looking lines into unix-looking lines
- $l++;
- if ($l >= $firstline - 5 &&
- (($l <= $oldlastline + 5) || ($l <= $lastline))) {
- chop;
- my $text = $_;
- if ($l == $firstline) { print $output $skip_marker; }
- my $n1 = GetEntry($samples1, $l);
- my $n2 = GetEntry($samples2, $l);
- if ($html) {
- my $dis = $disasm{$l};
- if (!defined($dis) || $n1 + $n2 == 0) {
- # No samples/disassembly for this source line
- printf $output (
- "<span class=\"line\">%5d</span> " .
- "<span class=\"deadsrc\">%6s %6s %s</span>\n",
- $l,
- HtmlPrintNumber($n1),
- HtmlPrintNumber($n2),
- HtmlEscape($text));
- } else {
- printf $output (
- "<span class=\"line\">%5d</span> " .
- "<span class=\"livesrc\">%6s %6s %s</span>" .
- "<span class=\"asm\">%s</span>\n",
- $l,
- HtmlPrintNumber($n1),
- HtmlPrintNumber($n2),
- HtmlEscape($text),
- HtmlEscape($dis));
- }
- } else {
- printf $output(
- "%6s %6s %4d: %s\n",
- UnparseAlt($n1),
- UnparseAlt($n2),
- $l,
- $text);
- }
- if ($l == $lastline) { print $output $skip_marker; }
- };
- }
- close(FILE);
- if ($html) {
- print $output "</pre>\n";
- }
- return 1;
-}
-
-# Return the source line for the specified file/linenumber.
-# Returns undef if not found.
-sub SourceLine {
- my $file = shift;
- my $line = shift;
-
- # Look in cache
- if (!defined($main::source_cache{$file})) {
- if (100 < scalar keys(%main::source_cache)) {
- # Clear the cache when it gets too big
- $main::source_cache = ();
- }
-
- # Read all lines from the file
- if (!open(FILE, "<$file")) {
- print STDERR "$file: $!\n";
- $main::source_cache{$file} = []; # Cache the negative result
- return undef;
- }
- my $lines = [];
- push(@{$lines}, ""); # So we can use 1-based line numbers as indices
- while (<FILE>) {
- push(@{$lines}, $_);
- }
- close(FILE);
-
- # Save the lines in the cache
- $main::source_cache{$file} = $lines;
- }
-
- my $lines = $main::source_cache{$file};
- if (($line < 0) || ($line > $#{$lines})) {
- return undef;
- } else {
- return $lines->[$line];
- }
-}
-
-# Print disassembly for one routine with interspersed source if available
-sub PrintDisassembledFunction {
- my $prog = shift;
- my $offset = shift;
- my $routine = shift;
- my $flat = shift;
- my $cumulative = shift;
- my $start_addr = shift;
- my $end_addr = shift;
- my $total = shift;
-
- # Disassemble all instructions
- my @instructions = Disassemble($prog, $offset, $start_addr, $end_addr);
-
- # Make array of counts per instruction
- my @flat_count = ();
- my @cum_count = ();
- my $flat_total = 0;
- my $cum_total = 0;
- foreach my $e (@instructions) {
- # Add up counts for all address that fall inside this instruction
- my $c1 = 0;
- my $c2 = 0;
- for (my $a = $e->[0]; $a lt $e->[4]; $a = AddressInc($a)) {
- $c1 += GetEntry($flat, $a);
- $c2 += GetEntry($cumulative, $a);
- }
- push(@flat_count, $c1);
- push(@cum_count, $c2);
- $flat_total += $c1;
- $cum_total += $c2;
- }
-
- # Print header with total counts
- printf("ROUTINE ====================== %s\n" .
- "%6s %6s %s (flat, cumulative) %.1f%% of total\n",
- ShortFunctionName($routine),
- Unparse($flat_total),
- Unparse($cum_total),
- Units(),
- ($cum_total * 100.0) / $total);
-
- # Process instructions in order
- my $current_file = "";
- for (my $i = 0; $i <= $#instructions; ) {
- my $e = $instructions[$i];
-
- # Print the new file name whenever we switch files
- if ($e->[1] ne $current_file) {
- $current_file = $e->[1];
- my $fname = $current_file;
- $fname =~ s|^\./||; # Trim leading "./"
-
- # Shorten long file names
- if (length($fname) >= 58) {
- $fname = "..." . substr($fname, -55);
- }
- printf("-------------------- %s\n", $fname);
- }
-
- # TODO: Compute range of lines to print together to deal with
- # small reorderings.
- my $first_line = $e->[2];
- my $last_line = $first_line;
- my %flat_sum = ();
- my %cum_sum = ();
- for (my $l = $first_line; $l <= $last_line; $l++) {
- $flat_sum{$l} = 0;
- $cum_sum{$l} = 0;
- }
-
- # Find run of instructions for this range of source lines
- my $first_inst = $i;
- while (($i <= $#instructions) &&
- ($instructions[$i]->[2] >= $first_line) &&
- ($instructions[$i]->[2] <= $last_line)) {
- $e = $instructions[$i];
- $flat_sum{$e->[2]} += $flat_count[$i];
- $cum_sum{$e->[2]} += $cum_count[$i];
- $i++;
- }
- my $last_inst = $i - 1;
-
- # Print source lines
- for (my $l = $first_line; $l <= $last_line; $l++) {
- my $line = SourceLine($current_file, $l);
- if (!defined($line)) {
- $line = "?\n";
- next;
- } else {
- $line =~ s/^\s+//;
- }
- printf("%6s %6s %5d: %s",
- UnparseAlt($flat_sum{$l}),
- UnparseAlt($cum_sum{$l}),
- $l,
- $line);
- }
-
- # Print disassembly
- for (my $x = $first_inst; $x <= $last_inst; $x++) {
- my $e = $instructions[$x];
- my $address = $e->[0];
- $address = AddressSub($address, $offset); # Make relative to section
- $address =~ s/^0x//;
- $address =~ s/^0*//;
-
- printf("%6s %6s %8s: %6s\n",
- UnparseAlt($flat_count[$x]),
- UnparseAlt($cum_count[$x]),
- $address,
- CleanDisassembly($e->[3]));
- }
- }
-}
-
-# Print DOT graph
-sub PrintDot {
- my $prog = shift;
- my $symbols = shift;
- my $raw = shift;
- my $flat = shift;
- my $cumulative = shift;
- my $overall_total = shift;
-
- # Get total
- my $local_total = TotalProfile($flat);
- my $nodelimit = int($main::opt_nodefraction * $local_total);
- my $edgelimit = int($main::opt_edgefraction * $local_total);
- my $nodecount = $main::opt_nodecount;
-
- # Find nodes to include
- my @list = (sort { abs(GetEntry($cumulative, $b)) <=>
- abs(GetEntry($cumulative, $a))
- || $a cmp $b }
- keys(%{$cumulative}));
- my $last = $nodecount - 1;
- if ($last > $#list) {
- $last = $#list;
- }
- while (($last >= 0) &&
- (abs(GetEntry($cumulative, $list[$last])) <= $nodelimit)) {
- $last--;
- }
- if ($last < 0) {
- print STDERR "No nodes to print\n";
- cleanup();
- return 0;
- }
-
- if ($nodelimit > 0 || $edgelimit > 0) {
- printf STDERR ("Dropping nodes with <= %s %s; edges with <= %s abs(%s)\n",
- Unparse($nodelimit), Units(),
- Unparse($edgelimit), Units());
- }
-
- # Open DOT output file
- my $output;
- if ($main::opt_gv) {
- $output = "| $DOT -Tps2 >" . TempName($main::next_tmpfile, "ps");
- } elsif ($main::opt_ps) {
- $output = "| $DOT -Tps2";
- } elsif ($main::opt_pdf) {
- $output = "| $DOT -Tps2 | $PS2PDF - -";
- } elsif ($main::opt_web || $main::opt_svg) {
- # We need to post-process the SVG, so write to a temporary file always.
- $output = "| $DOT -Tsvg >" . TempName($main::next_tmpfile, "svg");
- } elsif ($main::opt_gif) {
- $output = "| $DOT -Tgif";
- } else {
- $output = ">&STDOUT";
- }
- open(DOT, $output) || error("$output: $!\n");
-
- # Title
- printf DOT ("digraph \"%s; %s %s\" {\n",
- $prog,
- Unparse($overall_total),
- Units());
- if ($main::opt_pdf) {
- # The output is more printable if we set the page size for dot.
- printf DOT ("size=\"8,11\"\n");
- }
- printf DOT ("node [width=0.375,height=0.25];\n");
-
- # Print legend
- printf DOT ("Legend [shape=box,fontsize=24,shape=plaintext," .
- "label=\"%s\\l%s\\l%s\\l%s\\l%s\\l\"];\n",
- $prog,
- sprintf("Total %s: %s", Units(), Unparse($overall_total)),
- sprintf("Focusing on: %s", Unparse($local_total)),
- sprintf("Dropped nodes with <= %s abs(%s)",
- Unparse($nodelimit), Units()),
- sprintf("Dropped edges with <= %s %s",
- Unparse($edgelimit), Units())
- );
-
- # Print nodes
- my %node = ();
- my $nextnode = 1;
- foreach my $a (@list[0..$last]) {
- # Pick font size
- my $f = GetEntry($flat, $a);
- my $c = GetEntry($cumulative, $a);
-
- my $fs = 8;
- if ($local_total > 0) {
- $fs = 8 + (50.0 * sqrt(abs($f * 1.0 / $local_total)));
- }
-
- $node{$a} = $nextnode++;
- my $sym = $a;
- $sym =~ s/\s+/\\n/g;
- $sym =~ s/::/\\n/g;
-
- # Extra cumulative info to print for non-leaves
- my $extra = "";
- if ($f != $c) {
- $extra = sprintf("\\rof %s (%s)",
- Unparse($c),
- Percent($c, $overall_total));
- }
- my $style = "";
- if ($main::opt_heapcheck) {
- if ($f > 0) {
- # make leak-causing nodes more visible (add a background)
- $style = ",style=filled,fillcolor=gray"
- } elsif ($f < 0) {
- # make anti-leak-causing nodes (which almost never occur)
- # stand out as well (triple border)
- $style = ",peripheries=3"
- }
- }
-
- printf DOT ("N%d [label=\"%s\\n%s (%s)%s\\r" .
- "\",shape=box,fontsize=%.1f%s];\n",
- $node{$a},
- $sym,
- Unparse($f),
- Percent($f, $overall_total),
- $extra,
- $fs,
- $style,
- );
- }
-
- # Get edges and counts per edge
- my %edge = ();
- my $n;
- foreach my $k (keys(%{$raw})) {
- # TODO: omit low %age edges
- $n = $raw->{$k};
- my @translated = TranslateStack($symbols, $k);
- for (my $i = 1; $i <= $#translated; $i++) {
- my $src = $translated[$i];
- my $dst = $translated[$i-1];
- #next if ($src eq $dst); # Avoid self-edges?
- if (exists($node{$src}) && exists($node{$dst})) {
- my $edge_label = "$src\001$dst";
- if (!exists($edge{$edge_label})) {
- $edge{$edge_label} = 0;
- }
- $edge{$edge_label} += $n;
- }
- }
- }
-
- # Print edges
- foreach my $e (keys(%edge)) {
- my @x = split(/\001/, $e);
- $n = $edge{$e};
-
- if (abs($n) > $edgelimit) {
- # Compute line width based on edge count
- my $fraction = abs($local_total ? (3 * ($n / $local_total)) : 0);
- if ($fraction > 1) { $fraction = 1; }
- my $w = $fraction * 2;
- if ($w < 1 && ($main::opt_web || $main::opt_svg)) {
- # SVG output treats line widths < 1 poorly.
- $w = 1;
- }
-
- # Dot sometimes segfaults if given edge weights that are too large, so
- # we cap the weights at a large value
- my $edgeweight = abs($n) ** 0.7;
- if ($edgeweight > 100000) { $edgeweight = 100000; }
- $edgeweight = int($edgeweight);
-
- my $style = sprintf("setlinewidth(%f)", $w);
- if ($x[1] =~ m/\(inline\)/) {
- $style .= ",dashed";
- }
-
- # Use a slightly squashed function of the edge count as the weight
- printf DOT ("N%s -> N%s [label=%s, weight=%d, style=\"%s\"];\n",
- $node{$x[0]},
- $node{$x[1]},
- Unparse($n),
- $edgeweight,
- $style);
- }
- }
-
- print DOT ("}\n");
- close(DOT);
-
- if ($main::opt_web || $main::opt_svg) {
- # Rewrite SVG to be more usable inside web browser.
- RewriteSvg(TempName($main::next_tmpfile, "svg"));
- }
-
- return 1;
-}
-
-sub RewriteSvg {
- my $svgfile = shift;
-
- open(SVG, $svgfile) || die "open temp svg: $!";
- my @svg = <SVG>;
- close(SVG);
- unlink $svgfile;
- my $svg = join('', @svg);
-
- # Dot's SVG output is
- #
- # <svg width="___" height="___"
- # viewBox="___" xmlns=...>
- # <g id="graph0" transform="...">
- # ...
- # </g>
- # </svg>
- #
- # Change it to
- #
- # <svg width="100%" height="100%"
- # xmlns=...>
- # $svg_javascript
- # <g id="viewport" transform="translate(0,0)">
- # <g id="graph0" transform="...">
- # ...
- # </g>
- # </g>
- # </svg>
-
- # Fix width, height; drop viewBox.
- $svg =~ s/(?s)<svg width="[^"]+" height="[^"]+"(.*?)viewBox="[^"]+"/<svg width="100%" height="100%"$1/;
-
- # Insert script, viewport <g> above first <g>
- my $svg_javascript = SvgJavascript();
- my $viewport = "<g id=\"viewport\" transform=\"translate(0,0)\">\n";
- $svg =~ s/<g id="graph\d"/$svg_javascript$viewport$&/;
-
- # Insert final </g> above </svg>.
- $svg =~ s/(.*)(<\/svg>)/$1<\/g>$2/;
- $svg =~ s/<g id="graph\d"(.*?)/<g id="viewport"$1/;
-
- if ($main::opt_svg) {
- # --svg: write to standard output.
- print $svg;
- } else {
- # Write back to temporary file.
- open(SVG, ">$svgfile") || die "open $svgfile: $!";
- print SVG $svg;
- close(SVG);
- }
-}
-
-sub SvgJavascript {
- return <<'EOF';
-<script type="text/ecmascript"><![CDATA[
-// SVGPan
-// http://www.cyberz.org/blog/2009/12/08/svgpan-a-javascript-svg-panzoomdrag-library/
-// Local modification: if(true || ...) below to force panning, never moving.
-// Local modification: add clamping to fix bug in handleMouseWheel.
-
-/**
- * SVGPan library 1.2
- * ====================
- *
- * Given an unique existing element with id "viewport", including the
- * the library into any SVG adds the following capabilities:
- *
- * - Mouse panning
- * - Mouse zooming (using the wheel)
- * - Object dargging
- *
- * Known issues:
- *
- * - Zooming (while panning) on Safari has still some issues
- *
- * Releases:
- *
- * 1.2, Sat Mar 20 08:42:50 GMT 2010, Zeng Xiaohui
- * Fixed a bug with browser mouse handler interaction
- *
- * 1.1, Wed Feb 3 17:39:33 GMT 2010, Zeng Xiaohui
- * Updated the zoom code to support the mouse wheel on Safari/Chrome
- *
- * 1.0, Andrea Leofreddi
- * First release
- *
- * This code is licensed under the following BSD license:
- *
- * Copyright 2009-2010 Andrea Leofreddi <a.leofreddi@itcharm.com>. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification, are
- * permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this list of
- * conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright notice, this list
- * of conditions and the following disclaimer in the documentation and/or other materials
- * provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY Andrea Leofreddi ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Andrea Leofreddi OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * The views and conclusions contained in the software and documentation are those of the
- * authors and should not be interpreted as representing official policies, either expressed
- * or implied, of Andrea Leofreddi.
- */
-
-var root = document.documentElement;
-
-var state = 'none', stateTarget, stateOrigin, stateTf;
-
-setupHandlers(root);
-
-/**
- * Register handlers
- */
-function setupHandlers(root){
- setAttributes(root, {
- "onmouseup" : "add(evt)",
- "onmousedown" : "handleMouseDown(evt)",
- "onmousemove" : "handleMouseMove(evt)",
- "onmouseup" : "handleMouseUp(evt)",
- //"onmouseout" : "handleMouseUp(evt)", // Decomment this to stop the pan functionality when dragging out of the SVG element
- });
-
- if(navigator.userAgent.toLowerCase().indexOf('webkit') >= 0)
- window.addEventListener('mousewheel', handleMouseWheel, false); // Chrome/Safari
- else
- window.addEventListener('DOMMouseScroll', handleMouseWheel, false); // Others
-
- var g = svgDoc.getElementById("svg");
- g.width = "100%";
- g.height = "100%";
-}
-
-/**
- * Instance an SVGPoint object with given event coordinates.
- */
-function getEventPoint(evt) {
- var p = root.createSVGPoint();
-
- p.x = evt.clientX;
- p.y = evt.clientY;
-
- return p;
-}
-
-/**
- * Sets the current transform matrix of an element.
- */
-function setCTM(element, matrix) {
- var s = "matrix(" + matrix.a + "," + matrix.b + "," + matrix.c + "," + matrix.d + "," + matrix.e + "," + matrix.f + ")";
-
- element.setAttribute("transform", s);
-}
-
-/**
- * Dumps a matrix to a string (useful for debug).
- */
-function dumpMatrix(matrix) {
- var s = "[ " + matrix.a + ", " + matrix.c + ", " + matrix.e + "\n " + matrix.b + ", " + matrix.d + ", " + matrix.f + "\n 0, 0, 1 ]";
-
- return s;
-}
-
-/**
- * Sets attributes of an element.
- */
-function setAttributes(element, attributes){
- for (i in attributes)
- element.setAttributeNS(null, i, attributes[i]);
-}
-
-/**
- * Handle mouse move event.
- */
-function handleMouseWheel(evt) {
- if(evt.preventDefault)
- evt.preventDefault();
-
- evt.returnValue = false;
-
- var svgDoc = evt.target.ownerDocument;
-
- var delta;
-
- if(evt.wheelDelta)
- delta = evt.wheelDelta / 3600; // Chrome/Safari
- else
- delta = evt.detail / -90; // Mozilla
-
- var z = 1 + delta; // Zoom factor: 0.9/1.1
-
- // Clamp to reasonable values.
- // The 0.1 check is important because
- // a very large scroll can turn into a
- // negative z, which rotates the image 180 degrees.
- if(z < 0.1)
- z = 0.1;
- if(z > 10.0)
- z = 10.0;
-
- var g = svgDoc.getElementById("viewport");
-
- var p = getEventPoint(evt);
-
- p = p.matrixTransform(g.getCTM().inverse());
-
- // Compute new scale matrix in current mouse position
- var k = root.createSVGMatrix().translate(p.x, p.y).scale(z).translate(-p.x, -p.y);
-
- setCTM(g, g.getCTM().multiply(k));
-
- stateTf = stateTf.multiply(k.inverse());
-}
-
-/**
- * Handle mouse move event.
- */
-function handleMouseMove(evt) {
- if(evt.preventDefault)
- evt.preventDefault();
-
- evt.returnValue = false;
-
- var svgDoc = evt.target.ownerDocument;
-
- var g = svgDoc.getElementById("viewport");
-
- if(state == 'pan') {
- // Pan mode
- var p = getEventPoint(evt).matrixTransform(stateTf);
-
- setCTM(g, stateTf.inverse().translate(p.x - stateOrigin.x, p.y - stateOrigin.y));
- } else if(state == 'move') {
- // Move mode
- var p = getEventPoint(evt).matrixTransform(g.getCTM().inverse());
-
- setCTM(stateTarget, root.createSVGMatrix().translate(p.x - stateOrigin.x, p.y - stateOrigin.y).multiply(g.getCTM().inverse()).multiply(stateTarget.getCTM()));
-
- stateOrigin = p;
- }
-}
-
-/**
- * Handle click event.
- */
-function handleMouseDown(evt) {
- if(evt.preventDefault)
- evt.preventDefault();
-
- evt.returnValue = false;
-
- var svgDoc = evt.target.ownerDocument;
-
- var g = svgDoc.getElementById("viewport");
-
- if(true || evt.target.tagName == "svg") {
- // Pan mode
- state = 'pan';
-
- stateTf = g.getCTM().inverse();
-
- stateOrigin = getEventPoint(evt).matrixTransform(stateTf);
- } else {
- // Move mode
- state = 'move';
-
- stateTarget = evt.target;
-
- stateTf = g.getCTM().inverse();
-
- stateOrigin = getEventPoint(evt).matrixTransform(stateTf);
- }
-}
-
-/**
- * Handle mouse button release event.
- */
-function handleMouseUp(evt) {
- if(evt.preventDefault)
- evt.preventDefault();
-
- evt.returnValue = false;
-
- var svgDoc = evt.target.ownerDocument;
-
- if(state == 'pan' || state == 'move') {
- // Quit pan mode
- state = '';
- }
-}
-
-]]></script>
-EOF
-}
-
-# Translate a stack of addresses into a stack of symbols
-sub TranslateStack {
- my $symbols = shift;
- my $k = shift;
-
- my @addrs = split(/\n/, $k);
- my @result = ();
- for (my $i = 0; $i <= $#addrs; $i++) {
- my $a = $addrs[$i];
-
- # Skip large addresses since they sometimes show up as fake entries on RH9
- if (length($a) > 8 && $a gt "7fffffffffffffff") {
- next;
- }
-
- if ($main::opt_disasm || $main::opt_list) {
- # We want just the address for the key
- push(@result, $a);
- next;
- }
-
- my $symlist = $symbols->{$a};
- if (!defined($symlist)) {
- $symlist = [$a, "", $a];
- }
-
- # We can have a sequence of symbols for a particular entry
- # (more than one symbol in the case of inlining). Callers
- # come before callees in symlist, so walk backwards since
- # the translated stack should contain callees before callers.
- for (my $j = $#{$symlist}; $j >= 2; $j -= 3) {
- my $func = $symlist->[$j-2];
- my $fileline = $symlist->[$j-1];
- my $fullfunc = $symlist->[$j];
- if ($j > 2) {
- $func = "$func (inline)";
- }
- if ($main::opt_addresses) {
- push(@result, "$a $func $fileline");
- } elsif ($main::opt_lines) {
- if ($func eq '??' && $fileline eq '??:0') {
- push(@result, "$a");
- } else {
- push(@result, "$func $fileline");
- }
- } elsif ($main::opt_functions) {
- if ($func eq '??') {
- push(@result, "$a");
- } else {
- push(@result, $func);
- }
- } elsif ($main::opt_files) {
- if ($fileline eq '??:0' || $fileline eq '') {
- push(@result, "$a");
- } else {
- my $f = $fileline;
- $f =~ s/:\d+$//;
- push(@result, $f);
- }
- } else {
- push(@result, $a);
- last; # Do not print inlined info
- }
- }
- }
-
- # print join(",", @addrs), " => ", join(",", @result), "\n";
- return @result;
-}
-
-# Generate percent string for a number and a total
-sub Percent {
- my $num = shift;
- my $tot = shift;
- if ($tot != 0) {
- return sprintf("%.1f%%", $num * 100.0 / $tot);
- } else {
- return ($num == 0) ? "nan" : (($num > 0) ? "+inf" : "-inf");
- }
-}
-
-# Generate pretty-printed form of number
-sub Unparse {
- my $num = shift;
- if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') {
- if ($main::opt_inuse_objects || $main::opt_alloc_objects) {
- return sprintf("%d", $num);
- } else {
- if ($main::opt_show_bytes) {
- return sprintf("%d", $num);
- } else {
- return sprintf("%.1f", $num / 1048576.0);
- }
- }
- } elsif ($main::profile_type eq 'contention' && !$main::opt_contentions) {
- return sprintf("%.3f", $num / 1e9); # Convert nanoseconds to seconds
- } else {
- return sprintf("%d", $num);
- }
-}
-
-# Alternate pretty-printed form: 0 maps to "."
-sub UnparseAlt {
- my $num = shift;
- if ($num == 0) {
- return ".";
- } else {
- return Unparse($num);
- }
-}
-
-# Alternate pretty-printed form: 0 maps to ""
-sub HtmlPrintNumber {
- my $num = shift;
- if ($num == 0) {
- return "";
- } else {
- return Unparse($num);
- }
-}
-
-# Return output units
-sub Units {
- if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') {
- if ($main::opt_inuse_objects || $main::opt_alloc_objects) {
- return "objects";
- } else {
- if ($main::opt_show_bytes) {
- return "B";
- } else {
- return "MB";
- }
- }
- } elsif ($main::profile_type eq 'contention' && !$main::opt_contentions) {
- return "seconds";
- } else {
- return "samples";
- }
-}
-
-##### Profile manipulation code #####
-
-# Generate flattened profile:
-# If count is charged to stack [a,b,c,d], in generated profile,
-# it will be charged to [a]
-sub FlatProfile {
- my $profile = shift;
- my $result = {};
- foreach my $k (keys(%{$profile})) {
- my $count = $profile->{$k};
- my @addrs = split(/\n/, $k);
- if ($#addrs >= 0) {
- AddEntry($result, $addrs[0], $count);
- }
- }
- return $result;
-}
-
-# Generate cumulative profile:
-# If count is charged to stack [a,b,c,d], in generated profile,
-# it will be charged to [a], [b], [c], [d]
-sub CumulativeProfile {
- my $profile = shift;
- my $result = {};
- foreach my $k (keys(%{$profile})) {
- my $count = $profile->{$k};
- my @addrs = split(/\n/, $k);
- foreach my $a (@addrs) {
- AddEntry($result, $a, $count);
- }
- }
- return $result;
-}
-
-# If the second-youngest PC on the stack is always the same, returns
-# that pc. Otherwise, returns undef.
-sub IsSecondPcAlwaysTheSame {
- my $profile = shift;
-
- my $second_pc = undef;
- foreach my $k (keys(%{$profile})) {
- my @addrs = split(/\n/, $k);
- if ($#addrs < 1) {
- return undef;
- }
- if (not defined $second_pc) {
- $second_pc = $addrs[1];
- } else {
- if ($second_pc ne $addrs[1]) {
- return undef;
- }
- }
- }
- return $second_pc;
-}
-
-sub ExtractSymbolLocation {
- my $symbols = shift;
- my $address = shift;
- # 'addr2line' outputs "??:0" for unknown locations; we do the
- # same to be consistent.
- my $location = "??:0:unknown";
- if (exists $symbols->{$address}) {
- my $file = $symbols->{$address}->[1];
- if ($file eq "?") {
- $file = "??:0"
- }
- $location = $file . ":" . $symbols->{$address}->[0];
- }
- return $location;
-}
-
-# Extracts a graph of calls.
-sub ExtractCalls {
- my $symbols = shift;
- my $profile = shift;
-
- my $calls = {};
- while( my ($stack_trace, $count) = each %$profile ) {
- my @address = split(/\n/, $stack_trace);
- my $destination = ExtractSymbolLocation($symbols, $address[0]);
- AddEntry($calls, $destination, $count);
- for (my $i = 1; $i <= $#address; $i++) {
- my $source = ExtractSymbolLocation($symbols, $address[$i]);
- my $call = "$source -> $destination";
- AddEntry($calls, $call, $count);
- $destination = $source;
- }
- }
-
- return $calls;
-}
-
-sub RemoveUninterestingFrames {
- my $symbols = shift;
- my $profile = shift;
-
- # List of function names to skip
- my %skip = ();
- my $skip_regexp = 'NOMATCH';
- if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') {
- foreach my $name ('calloc',
- 'cfree',
- 'malloc',
- 'free',
- 'memalign',
- 'posix_memalign',
- 'pvalloc',
- 'valloc',
- 'realloc',
- 'tc_calloc',
- 'tc_cfree',
- 'tc_malloc',
- 'tc_free',
- 'tc_memalign',
- 'tc_posix_memalign',
- 'tc_pvalloc',
- 'tc_valloc',
- 'tc_realloc',
- 'tc_new',
- 'tc_delete',
- 'tc_newarray',
- 'tc_deletearray',
- 'tc_new_nothrow',
- 'tc_newarray_nothrow',
- 'do_malloc',
- '::do_malloc', # new name -- got moved to an unnamed ns
- '::do_malloc_or_cpp_alloc',
- 'DoSampledAllocation',
- 'simple_alloc::allocate',
- '__malloc_alloc_template::allocate',
- '__builtin_delete',
- '__builtin_new',
- '__builtin_vec_delete',
- '__builtin_vec_new',
- 'operator new',
- 'operator new[]',
- # Go
- 'catstring',
- 'copyin',
- 'gostring',
- 'gostringsize',
- 'growslice1',
- 'appendslice1',
- 'hash_init',
- 'hash_subtable_new',
- 'hash_conv',
- 'hash_grow',
- 'hash_insert_internal',
- 'hash_insert',
- 'mapassign',
- 'runtime.mapassign',
- 'runtime.appendslice',
- 'runtime.mapassign1',
- 'makechan',
- 'makemap',
- 'mal',
- 'runtime.new',
- 'makeslice1',
- 'runtime.gostringsize',
- 'runtime.malloc',
- 'unsafe.New',
- 'runtime.mallocgc',
- 'runtime.catstring',
- 'runtime.growslice',
- 'runtime.ifaceT2E',
- 'runtime.ifaceT2I',
- 'runtime.makechan',
- 'runtime.makechan_c',
- 'runtime.makemap',
- 'runtime.makemap_c',
- 'runtime.makeslice',
- 'runtime.mal',
- 'runtime.slicebytetostring',
- 'runtime.sliceinttostring',
- 'runtime.stringtoslicebyte',
- 'runtime.stringtosliceint',
- # These mark the beginning/end of our custom sections
- '__start_google_malloc',
- '__stop_google_malloc',
- '__start_malloc_hook',
- '__stop_malloc_hook') {
- $skip{$name} = 1;
- $skip{"_" . $name} = 1; # Mach (OS X) adds a _ prefix to everything
- }
- # TODO: Remove TCMalloc once everything has been
- # moved into the tcmalloc:: namespace and we have flushed
- # old code out of the system.
- $skip_regexp = "TCMalloc|^tcmalloc::";
- } elsif ($main::profile_type eq 'contention') {
- foreach my $vname ('Mutex::Unlock', 'Mutex::UnlockSlow') {
- $skip{$vname} = 1;
- }
- } elsif ($main::profile_type eq 'cpu') {
- # Drop signal handlers used for CPU profile collection
- # TODO(dpeng): this should not be necessary; it's taken
- # care of by the general 2nd-pc mechanism below.
- foreach my $name ('ProfileData::Add', # historical
- 'ProfileData::prof_handler', # historical
- 'CpuProfiler::prof_handler',
- '__FRAME_END__',
- '__pthread_sighandler',
- '__restore') {
- $skip{$name} = 1;
- }
- } else {
- # Nothing skipped for unknown types
- }
-
- # Go doesn't have the problem that this heuristic tries to fix. Disable.
- if (0 && $main::profile_type eq 'cpu') {
- # If all the second-youngest program counters are the same,
- # this STRONGLY suggests that it is an artifact of measurement,
- # i.e., stack frames pushed by the CPU profiler signal handler.
- # Hence, we delete them.
- # (The topmost PC is read from the signal structure, not from
- # the stack, so it does not get involved.)
- while (my $second_pc = IsSecondPcAlwaysTheSame($profile)) {
- my $result = {};
- my $func = '';
- if (exists($symbols->{$second_pc})) {
- $second_pc = $symbols->{$second_pc}->[0];
- }
- print STDERR "Removing $second_pc from all stack traces.\n";
- foreach my $k (keys(%{$profile})) {
- my $count = $profile->{$k};
- my @addrs = split(/\n/, $k);
- splice @addrs, 1, 1;
- my $reduced_path = join("\n", @addrs);
- AddEntry($result, $reduced_path, $count);
- }
- $profile = $result;
- }
- }
-
- my $result = {};
- foreach my $k (keys(%{$profile})) {
- my $count = $profile->{$k};
- my @addrs = split(/\n/, $k);
- my @path = ();
- foreach my $a (@addrs) {
- if (exists($symbols->{$a})) {
- my $func = $symbols->{$a}->[0];
- if ($skip{$func} || ($func =~ m/$skip_regexp/)) {
- next;
- }
- }
- push(@path, $a);
- }
- my $reduced_path = join("\n", @path);
- AddEntry($result, $reduced_path, $count);
- }
- return $result;
-}
-
-# Reduce profile to granularity given by user
-sub ReduceProfile {
- my $symbols = shift;
- my $profile = shift;
- my $result = {};
- foreach my $k (keys(%{$profile})) {
- my $count = $profile->{$k};
- my @translated = TranslateStack($symbols, $k);
- my @path = ();
- my %seen = ();
- $seen{''} = 1; # So that empty keys are skipped
- foreach my $e (@translated) {
- # To avoid double-counting due to recursion, skip a stack-trace
- # entry if it has already been seen
- if (!$seen{$e}) {
- $seen{$e} = 1;
- push(@path, $e);
- }
- }
- my $reduced_path = join("\n", @path);
- AddEntry($result, $reduced_path, $count);
- }
- return $result;
-}
-
-# Does the specified symbol array match the regexp?
-sub SymbolMatches {
- my $sym = shift;
- my $re = shift;
- if (defined($sym)) {
- for (my $i = 0; $i < $#{$sym}; $i += 3) {
- if ($sym->[$i] =~ m/$re/ || $sym->[$i+1] =~ m/$re/) {
- return 1;
- }
- }
- }
- return 0;
-}
-
-# Focus only on paths involving specified regexps
-sub FocusProfile {
- my $symbols = shift;
- my $profile = shift;
- my $focus = shift;
- my $result = {};
- foreach my $k (keys(%{$profile})) {
- my $count = $profile->{$k};
- my @addrs = split(/\n/, $k);
- foreach my $a (@addrs) {
- # Reply if it matches either the address/shortname/fileline
- if (($a =~ m/$focus/) || SymbolMatches($symbols->{$a}, $focus)) {
- AddEntry($result, $k, $count);
- last;
- }
- }
- }
- return $result;
-}
-
-# Focus only on paths not involving specified regexps
-sub IgnoreProfile {
- my $symbols = shift;
- my $profile = shift;
- my $ignore = shift;
- my $result = {};
- foreach my $k (keys(%{$profile})) {
- my $count = $profile->{$k};
- my @addrs = split(/\n/, $k);
- my $matched = 0;
- foreach my $a (@addrs) {
- # Reply if it matches either the address/shortname/fileline
- if (($a =~ m/$ignore/) || SymbolMatches($symbols->{$a}, $ignore)) {
- $matched = 1;
- last;
- }
- }
- if (!$matched) {
- AddEntry($result, $k, $count);
- }
- }
- return $result;
-}
-
-# Get total count in profile
-sub TotalProfile {
- my $profile = shift;
- my $result = 0;
- foreach my $k (keys(%{$profile})) {
- $result += $profile->{$k};
- }
- return $result;
-}
-
-# Add A to B
-sub AddProfile {
- my $A = shift;
- my $B = shift;
-
- my $R = {};
- # add all keys in A
- foreach my $k (keys(%{$A})) {
- my $v = $A->{$k};
- AddEntry($R, $k, $v);
- }
- # add all keys in B
- foreach my $k (keys(%{$B})) {
- my $v = $B->{$k};
- AddEntry($R, $k, $v);
- }
- return $R;
-}
-
-# Merges symbol maps
-sub MergeSymbols {
- my $A = shift;
- my $B = shift;
-
- my $R = {};
- foreach my $k (keys(%{$A})) {
- $R->{$k} = $A->{$k};
- }
- if (defined($B)) {
- foreach my $k (keys(%{$B})) {
- $R->{$k} = $B->{$k};
- }
- }
- return $R;
-}
-
-
-# Add A to B
-sub AddPcs {
- my $A = shift;
- my $B = shift;
-
- my $R = {};
- # add all keys in A
- foreach my $k (keys(%{$A})) {
- $R->{$k} = 1
- }
- # add all keys in B
- foreach my $k (keys(%{$B})) {
- $R->{$k} = 1
- }
- return $R;
-}
-
-# Subtract B from A
-sub SubtractProfile {
- my $A = shift;
- my $B = shift;
-
- my $R = {};
- foreach my $k (keys(%{$A})) {
- my $v = $A->{$k} - GetEntry($B, $k);
- if ($v < 0 && $main::opt_drop_negative) {
- $v = 0;
- }
- AddEntry($R, $k, $v);
- }
- if (!$main::opt_drop_negative) {
- # Take care of when subtracted profile has more entries
- foreach my $k (keys(%{$B})) {
- if (!exists($A->{$k})) {
- AddEntry($R, $k, 0 - $B->{$k});
- }
- }
- }
- return $R;
-}
-
-# Get entry from profile; zero if not present
-sub GetEntry {
- my $profile = shift;
- my $k = shift;
- if (exists($profile->{$k})) {
- return $profile->{$k};
- } else {
- return 0;
- }
-}
-
-# Add entry to specified profile
-sub AddEntry {
- my $profile = shift;
- my $k = shift;
- my $n = shift;
- if (!exists($profile->{$k})) {
- $profile->{$k} = 0;
- }
- $profile->{$k} += $n;
-}
-
-# Add a stack of entries to specified profile, and add them to the $pcs
-# list.
-sub AddEntries {
- my $profile = shift;
- my $pcs = shift;
- my $stack = shift;
- my $count = shift;
- my @k = ();
-
- foreach my $e (split(/\s+/, $stack)) {
- my $pc = HexExtend($e);
- $pcs->{$pc} = 1;
- push @k, $pc;
- }
- AddEntry($profile, (join "\n", @k), $count);
-}
-
-sub IsSymbolizedProfileFile {
- my $file_name = shift;
-
- if (!(-e $file_name) || !(-r $file_name)) {
- return 0;
- }
-
- $SYMBOL_PAGE =~ m,[^/]+$,; # matches everything after the last slash
- my $symbol_marker = $&;
- # Check if the file contains a symbol-section marker.
- open(TFILE, "<$file_name");
- my @lines = <TFILE>;
- my $result = grep(/^--- *$symbol_marker/, @lines);
- close(TFILE);
- return $result > 0;
-}
-
-##### Code to profile a server dynamically #####
-
-sub CheckSymbolPage {
- my $url = SymbolPageURL();
-print STDERR "Read $url\n";
- open(SYMBOL, "$CURL -s '$url' |");
- my $line = <SYMBOL>;
- $line =~ s/\r//g; # turn windows-looking lines into unix-looking lines
- close(SYMBOL);
- unless (defined($line)) {
- error("$url doesn't exist\n");
- }
-
- if ($line =~ /^num_symbols:\s+(\d+)$/) {
- if ($1 == 0) {
- error("Stripped binary. No symbols available.\n");
- }
- } else {
- error("Failed to get the number of symbols from $url\n");
- }
-}
-
-sub IsProfileURL {
- my $profile_name = shift;
- my ($host, $port, $prefix, $path) = ParseProfileURL($profile_name);
- return defined($host) and defined($port) and defined($path);
-}
-
-sub ParseProfileURL {
- my $profile_name = shift;
- if (defined($profile_name) &&
- $profile_name =~ m,^(http://|)([^/:]+):(\d+)(|\@\d+)(|/|(.*?)($PROFILE_PAGE|$PMUPROFILE_PAGE|$HEAP_PAGE|$GROWTH_PAGE|$CONTENTION_PAGE|$WALL_PAGE|$FILTEREDPROFILE_PAGE))$,o) {
- # $7 is $PROFILE_PAGE/$HEAP_PAGE/etc. $5 is *everything* after
- # the hostname, as long as that everything is the empty string,
- # a slash, or something ending in $PROFILE_PAGE/$HEAP_PAGE/etc.
- # So "$7 || $5" is $PROFILE_PAGE/etc if there, or else it's "/" or "".
- return ($2, $3, $6, $7 || $5);
- }
- return ();
-}
-
-# We fetch symbols from the first profile argument.
-sub SymbolPageURL {
- my ($host, $port, $prefix, $path) = ParseProfileURL($main::pfile_args[0]);
- return "http://$host:$port$prefix$SYMBOL_PAGE";
-}
-
-sub FetchProgramName() {
- my ($host, $port, $prefix, $path) = ParseProfileURL($main::pfile_args[0]);
- my $url = "http://$host:$port$prefix$PROGRAM_NAME_PAGE";
- my $command_line = "$CURL -s '$url'";
- open(CMDLINE, "$command_line |") or error($command_line);
- my $cmdline = <CMDLINE>;
- $cmdline =~ s/\r//g; # turn windows-looking lines into unix-looking lines
- close(CMDLINE);
- error("Failed to get program name from $url\n") unless defined($cmdline);
- $cmdline =~ s/\x00.+//; # Remove argv[1] and latters.
- $cmdline =~ s!\n!!g; # Remove LFs.
- return $cmdline;
-}
-
-# Gee, curl's -L (--location) option isn't reliable at least
-# with its 7.12.3 version. Curl will forget to post data if
-# there is a redirection. This function is a workaround for
-# curl. Redirection happens on borg hosts.
-sub ResolveRedirectionForCurl {
- my $url = shift;
- my $command_line = "$CURL -s --head '$url'";
- open(CMDLINE, "$command_line |") or error($command_line);
- while (<CMDLINE>) {
- s/\r//g; # turn windows-looking lines into unix-looking lines
- if (/^Location: (.*)/) {
- $url = $1;
- }
- }
- close(CMDLINE);
- return $url;
-}
-
-# Reads a symbol map from the file handle name given as $1, returning
-# the resulting symbol map. Also processes variables relating to symbols.
-# Currently, the only variable processed is 'binary=<value>' which updates
-# $main::prog to have the correct program name.
-sub ReadSymbols {
- my $in = shift;
- my $map = shift;
- while (<$in>) {
- s/\r//g; # turn windows-looking lines into unix-looking lines
- # Removes all the leading zeroes from the symbols, see comment below.
- if (m/^0x0*([0-9a-f]+)\s+(.+)/) {
- $map->{$1} = $2;
- } elsif (m/^---/) {
- last;
- } elsif (m/^([a-z][^=]*)=(.*)$/ ) {
- my ($variable, $value) = ($1, $2);
- for ($variable, $value) {
- s/^\s+//;
- s/\s+$//;
- }
- if ($variable eq "binary") {
- if ($main::prog ne $UNKNOWN_BINARY && $main::prog ne $value) {
- printf STDERR ("Warning: Mismatched binary name '%s', using '%s'.\n",
- $main::prog, $value);
- }
- $main::prog = $value;
- } else {
- printf STDERR ("Ignoring unknown variable in symbols list: " .
- "'%s' = '%s'\n", $variable, $value);
- }
- }
- }
- return $map;
-}
-
-# Fetches and processes symbols to prepare them for use in the profile output
-# code. If the optional 'symbol_map' arg is not given, fetches symbols from
-# $SYMBOL_PAGE for all PC values found in profile. Otherwise, the raw symbols
-# are assumed to have already been fetched into 'symbol_map' and are simply
-# extracted and processed.
-sub FetchSymbols {
- my $pcset = shift;
- my $symbol_map = shift;
-
- my %seen = ();
- my @pcs = grep { !$seen{$_}++ } keys(%$pcset); # uniq
-
- if (!defined($symbol_map)) {
- $symbol_map = {};
- my @toask = @pcs;
- while (@toask > 0) {
- my $n = @toask;
- # NOTE(rsc): Limiting the number of PCs requested per round
- # used to be necessary, but I think it was a bug in
- # debug/pprof/symbol's implementation. Leaving here
- # in case I am wrong.
- # if ($n > 49) { $n = 49; }
- my @thisround = @toask[0..$n];
- @toask = @toask[($n+1)..(@toask-1)];
- my $post_data = join("+", sort((map {"0x" . "$_"} @thisround)));
- open(POSTFILE, ">$main::tmpfile_sym");
- print POSTFILE $post_data;
- close(POSTFILE);
-
- my $url = SymbolPageURL();
- $url = ResolveRedirectionForCurl($url);
- my $command_line = "$CURL -sd '\@$main::tmpfile_sym' '$url'";
- # We use c++filt in case $SYMBOL_PAGE gives us mangled symbols.
- my $cppfilt = $obj_tool_map{"c++filt"};
- open(SYMBOL, "$command_line | $cppfilt |") or error($command_line);
- ReadSymbols(*SYMBOL{IO}, $symbol_map);
- close(SYMBOL);
- }
- }
-
- my $symbols = {};
- foreach my $pc (@pcs) {
- my $fullname;
- # For 64 bits binaries, symbols are extracted with 8 leading zeroes.
- # Then /symbol reads the long symbols in as uint64, and outputs
- # the result with a "0x%08llx" format which get rid of the zeroes.
- # By removing all the leading zeroes in both $pc and the symbols from
- # /symbol, the symbols match and are retrievable from the map.
- my $shortpc = $pc;
- $shortpc =~ s/^0*//;
- # Each line may have a list of names, which includes the function
- # and also other functions it has inlined. They are separated
- # (in PrintSymbolizedFile), by --, which is illegal in function names.
- my $fullnames;
- if (defined($symbol_map->{$shortpc})) {
- $fullnames = $symbol_map->{$shortpc};
- } else {
- $fullnames = "0x" . $pc; # Just use addresses
- }
- my $sym = [];
- $symbols->{$pc} = $sym;
- foreach my $fullname (split("--", $fullnames)) {
- my $name = ShortFunctionName($fullname);
- push(@{$sym}, $name, "?", $fullname);
- }
- }
- return $symbols;
-}
-
-sub BaseName {
- my $file_name = shift;
- $file_name =~ s!^.*/!!; # Remove directory name
- return $file_name;
-}
-
-sub MakeProfileBaseName {
- my ($binary_name, $profile_name) = @_;
- my ($host, $port, $prefix, $path) = ParseProfileURL($profile_name);
- my $binary_shortname = BaseName($binary_name);
- return sprintf("%s.%s.%s-port%s",
- $binary_shortname, $main::op_time, $host, $port);
-}
-
-sub FetchDynamicProfile {
- my $binary_name = shift;
- my $profile_name = shift;
- my $fetch_name_only = shift;
- my $encourage_patience = shift;
-
- if (!IsProfileURL($profile_name)) {
- return $profile_name;
- } else {
- my ($host, $port, $prefix, $path) = ParseProfileURL($profile_name);
- if ($path eq "" || $path eq "/") {
- # Missing type specifier defaults to cpu-profile
- $path = $PROFILE_PAGE;
- }
-
- my $profile_file = MakeProfileBaseName($binary_name, $profile_name);
-
- my $url;
- my $curl_timeout;
- if (($path =~ m/$PROFILE_PAGE/) || ($path =~ m/$PMUPROFILE_PAGE/)) {
- if ($path =~ m/$PROFILE_PAGE/) {
- $url = sprintf("http://$host:$port$prefix$path?seconds=%d",
- $main::opt_seconds);
- } else {
- if ($profile_name =~ m/[?]/) {
- $profile_name .= "&"
- } else {
- $profile_name .= "?"
- }
- $url = sprintf("http://$profile_name" . "seconds=%d",
- $main::opt_seconds);
- }
- $curl_timeout = sprintf("--max-time %d",
- int($main::opt_seconds * 1.01 + 60));
- } else {
- # For non-CPU profiles, we add a type-extension to
- # the target profile file name.
- my $suffix = $path;
- $suffix =~ s,/,.,g;
- $profile_file .= "$suffix";
- $url = "http://$host:$port$prefix$path";
- $curl_timeout = "";
- }
-
- my $profile_dir = $ENV{"PPROF_TMPDIR"} || ($ENV{HOME} . "/pprof");
- if (!(-d $profile_dir)) {
- mkdir($profile_dir)
- || die("Unable to create profile directory $profile_dir: $!\n");
- }
- my $tmp_profile = "$profile_dir/.tmp.$profile_file";
- my $real_profile = "$profile_dir/$profile_file";
-
- if ($fetch_name_only > 0) {
- return $real_profile;
- }
-
- my $cmd = "$CURL $curl_timeout -s -o $tmp_profile '$url'";
- if (($path =~ m/$PROFILE_PAGE/) || ($path =~ m/$PMUPROFILE_PAGE/)){
- print STDERR "Gathering CPU profile from $url for $main::opt_seconds seconds to\n ${real_profile}\n";
- if ($encourage_patience) {
- print STDERR "Be patient...\n";
- }
- } else {
- print STDERR "Fetching $path profile from $host:$port to\n ${real_profile}\n";
- }
-
- (system($cmd) == 0) || error("Failed to get profile: $cmd: $!\n");
- (system("mv $tmp_profile $real_profile") == 0) || error("Unable to rename profile\n");
- print STDERR "Wrote profile to $real_profile\n";
- $main::collected_profile = $real_profile;
- return $main::collected_profile;
- }
-}
-
-# Collect profiles in parallel
-sub FetchDynamicProfiles {
- my $items = scalar(@main::pfile_args);
- my $levels = log($items) / log(2);
-
- if ($items == 1) {
- $main::profile_files[0] = FetchDynamicProfile($main::prog, $main::pfile_args[0], 0, 1);
- } else {
- # math rounding issues
- if ((2 ** $levels) < $items) {
- $levels++;
- }
- my $count = scalar(@main::pfile_args);
- for (my $i = 0; $i < $count; $i++) {
- $main::profile_files[$i] = FetchDynamicProfile($main::prog, $main::pfile_args[$i], 1, 0);
- }
- print STDERR "Fetching $count profiles, Be patient...\n";
- FetchDynamicProfilesRecurse($levels, 0, 0);
- $main::collected_profile = join(" \\\n ", @main::profile_files);
- }
-}
-
-# Recursively fork a process to get enough processes
-# collecting profiles
-sub FetchDynamicProfilesRecurse {
- my $maxlevel = shift;
- my $level = shift;
- my $position = shift;
-
- if (my $pid = fork()) {
- $position = 0 | ($position << 1);
- TryCollectProfile($maxlevel, $level, $position);
- wait;
- } else {
- $position = 1 | ($position << 1);
- TryCollectProfile($maxlevel, $level, $position);
- exit(0);
- }
-}
-
-# Collect a single profile
-sub TryCollectProfile {
- my $maxlevel = shift;
- my $level = shift;
- my $position = shift;
-
- if ($level >= ($maxlevel - 1)) {
- if ($position < scalar(@main::pfile_args)) {
- FetchDynamicProfile($main::prog, $main::pfile_args[$position], 0, 0);
- }
- } else {
- FetchDynamicProfilesRecurse($maxlevel, $level+1, $position);
- }
-}
-
-##### Parsing code #####
-
-# Provide a small streaming-read module to handle very large
-# cpu-profile files. Stream in chunks along a sliding window.
-# Provides an interface to get one 'slot', correctly handling
-# endian-ness differences. A slot is one 32-bit or 64-bit word
-# (depending on the input profile). We tell endianness and bit-size
-# for the profile by looking at the first 8 bytes: in cpu profiles,
-# the second slot is always 3 (we'll accept anything that's not 0).
-BEGIN {
- package CpuProfileStream;
-
- sub new {
- my ($class, $file, $fname) = @_;
- my $self = { file => $file,
- base => 0,
- stride => 512 * 1024, # must be a multiple of bitsize/8
- slots => [],
- unpack_code => "", # N for big-endian, V for little
- };
- bless $self, $class;
- # Let unittests adjust the stride
- if ($main::opt_test_stride > 0) {
- $self->{stride} = $main::opt_test_stride;
- }
- # Read the first two slots to figure out bitsize and endianness.
- my $slots = $self->{slots};
- my $str;
- read($self->{file}, $str, 8);
- # Set the global $address_length based on what we see here.
- # 8 is 32-bit (8 hexadecimal chars); 16 is 64-bit (16 hexadecimal chars).
- $address_length = ($str eq (chr(0)x8)) ? 16 : 8;
- if ($address_length == 8) {
- if (substr($str, 6, 2) eq chr(0)x2) {
- $self->{unpack_code} = 'V'; # Little-endian.
- } elsif (substr($str, 4, 2) eq chr(0)x2) {
- $self->{unpack_code} = 'N'; # Big-endian
- } else {
- ::error("$fname: header size >= 2**16\n");
- }
- @$slots = unpack($self->{unpack_code} . "*", $str);
- } else {
- # If we're a 64-bit profile, make sure we're a 64-bit-capable
- # perl. Otherwise, each slot will be represented as a float
- # instead of an int64, losing precision and making all the
- # 64-bit addresses right. We *could* try to handle this with
- # software emulation of 64-bit ints, but that's added complexity
- # for no clear benefit (yet). We use 'Q' to test for 64-bit-ness;
- # perl docs say it's only available on 64-bit perl systems.
- my $has_q = 0;
- eval { $has_q = pack("Q", "1") ? 1 : 1; };
- if (!$has_q) {
- ::error("$fname: need a 64-bit perl to process this 64-bit profile.\n");
- }
- read($self->{file}, $str, 8);
- if (substr($str, 4, 4) eq chr(0)x4) {
- # We'd love to use 'Q', but it's a) not universal, b) not endian-proof.
- $self->{unpack_code} = 'V'; # Little-endian.
- } elsif (substr($str, 0, 4) eq chr(0)x4) {
- $self->{unpack_code} = 'N'; # Big-endian
- } else {
- ::error("$fname: header size >= 2**32\n");
- }
- my @pair = unpack($self->{unpack_code} . "*", $str);
- # Since we know one of the pair is 0, it's fine to just add them.
- @$slots = (0, $pair[0] + $pair[1]);
- }
- return $self;
- }
-
- # Load more data when we access slots->get(X) which is not yet in memory.
- sub overflow {
- my ($self) = @_;
- my $slots = $self->{slots};
- $self->{base} += $#$slots + 1; # skip over data we're replacing
- my $str;
- read($self->{file}, $str, $self->{stride});
- if ($address_length == 8) { # the 32-bit case
- # This is the easy case: unpack provides 32-bit unpacking primitives.
- @$slots = unpack($self->{unpack_code} . "*", $str);
- } else {
- # We need to unpack 32 bits at a time and combine.
- my @b32_values = unpack($self->{unpack_code} . "*", $str);
- my @b64_values = ();
- for (my $i = 0; $i < $#b32_values; $i += 2) {
- # TODO(csilvers): if this is a 32-bit perl, the math below
- # could end up in a too-large int, which perl will promote
- # to a double, losing necessary precision. Deal with that.
- if ($self->{unpack_code} eq 'V') { # little-endian
- push(@b64_values, $b32_values[$i] + $b32_values[$i+1] * (2**32));
- } else {
- push(@b64_values, $b32_values[$i] * (2**32) + $b32_values[$i+1]);
- }
- }
- @$slots = @b64_values;
- }
- }
-
- # Access the i-th long in the file (logically), or -1 at EOF.
- sub get {
- my ($self, $idx) = @_;
- my $slots = $self->{slots};
- while ($#$slots >= 0) {
- if ($idx < $self->{base}) {
- # The only time we expect a reference to $slots[$i - something]
- # after referencing $slots[$i] is reading the very first header.
- # Since $stride > |header|, that shouldn't cause any lookback
- # errors. And everything after the header is sequential.
- print STDERR "Unexpected look-back reading CPU profile";
- return -1; # shrug, don't know what better to return
- } elsif ($idx > $self->{base} + $#$slots) {
- $self->overflow();
- } else {
- return $slots->[$idx - $self->{base}];
- }
- }
- # If we get here, $slots is [], which means we've reached EOF
- return -1; # unique since slots is supposed to hold unsigned numbers
- }
-}
-
-# Parse profile generated by common/profiler.cc and return a reference
-# to a map:
-# $result->{version} Version number of profile file
-# $result->{period} Sampling period (in microseconds)
-# $result->{profile} Profile object
-# $result->{map} Memory map info from profile
-# $result->{pcs} Hash of all PC values seen, key is hex address
-sub ReadProfile {
- my $prog = shift;
- my $fname = shift;
-
- if (IsSymbolizedProfileFile($fname) && !$main::use_symbolized_profile) {
- # we have both a binary and symbolized profiles, abort
- usage("Symbolized profile '$fname' cannot be used with a binary arg. " .
- "Try again without passing '$prog'.");
- }
-
- $main::profile_type = '';
-
- $CONTENTION_PAGE =~ m,[^/]+$,; # matches everything after the last slash
- my $contention_marker = $&;
- $GROWTH_PAGE =~ m,[^/]+$,; # matches everything after the last slash
- my $growth_marker = $&;
- $SYMBOL_PAGE =~ m,[^/]+$,; # matches everything after the last slash
- my $symbol_marker = $&;
- $PROFILE_PAGE =~ m,[^/]+$,; # matches everything after the last slash
- my $profile_marker = $&;
-
- # Look at first line to see if it is a heap or a CPU profile.
- # CPU profile may start with no header at all, and just binary data
- # (starting with \0\0\0\0) -- in that case, don't try to read the
- # whole firstline, since it may be gigabytes(!) of data.
- open(PROFILE, "<$fname") || error("$fname: $!\n");
- binmode PROFILE; # New perls do UTF-8 processing
- my $firstchar = "";
- my $header = "";
- read(PROFILE, $firstchar, 1);
- seek(PROFILE, -1, 1); # unread the firstchar
- if ($firstchar ne "\0") {
- $header = <PROFILE>;
- if (!defined($header)) {
- error("Profile is empty.\n");
- }
- $header =~ s/\r//g; # turn windows-looking lines into unix-looking lines
- }
-
- my $symbols;
- if ($header =~ m/^--- *$symbol_marker/o) {
- # read the symbol section of the symbolized profile file
- $symbols = ReadSymbols(*PROFILE{IO});
-
- # read the next line to get the header for the remaining profile
- $header = "";
- read(PROFILE, $firstchar, 1);
- seek(PROFILE, -1, 1); # unread the firstchar
- if ($firstchar ne "\0") {
- $header = <PROFILE>;
- $header =~ s/\r//g;
- }
- }
-
- my $result;
-
- if ($header =~ m/^heap profile:.*$growth_marker/o) {
- $main::profile_type = 'growth';
- $result = ReadHeapProfile($prog, $fname, $header);
- } elsif ($header =~ m/^heap profile:/) {
- $main::profile_type = 'heap';
- $result = ReadHeapProfile($prog, $fname, $header);
- } elsif ($header =~ m/^--- *$contention_marker/o) {
- $main::profile_type = 'contention';
- $result = ReadSynchProfile($prog, $fname);
- } elsif ($header =~ m/^--- *Stacks:/) {
- print STDERR
- "Old format contention profile: mistakenly reports " .
- "condition variable signals as lock contentions.\n";
- $main::profile_type = 'contention';
- $result = ReadSynchProfile($prog, $fname);
- } elsif ($header =~ m/^--- *$profile_marker/) {
- # the binary cpu profile data starts immediately after this line
- $main::profile_type = 'cpu';
- $result = ReadCPUProfile($prog, $fname);
- } else {
- if (defined($symbols)) {
- # a symbolized profile contains a format we don't recognize, bail out
- error("$fname: Cannot recognize profile section after symbols.\n");
- }
- # no ascii header present -- must be a CPU profile
- $main::profile_type = 'cpu';
- $result = ReadCPUProfile($prog, $fname);
- }
-
- # if we got symbols along with the profile, return those as well
- if (defined($symbols)) {
- $result->{symbols} = $symbols;
- }
-
- return $result;
-}
-
-# Subtract one from caller pc so we map back to call instr.
-# However, don't do this if we're reading a symbolized profile
-# file, in which case the subtract-one was done when the file
-# was written.
-#
-# We apply the same logic to all readers, though ReadCPUProfile uses an
-# independent implementation.
-sub FixCallerAddresses {
- my $stack = shift;
- if ($main::use_symbolized_profile) {
- return $stack;
- } else {
- $stack =~ /(\s)/;
- my $delimiter = $1;
- my @addrs = split(' ', $stack);
- my @fixedaddrs;
- $#fixedaddrs = $#addrs;
- if ($#addrs >= 0) {
- $fixedaddrs[0] = $addrs[0];
- }
- for (my $i = 1; $i <= $#addrs; $i++) {
- $fixedaddrs[$i] = AddressSub($addrs[$i], "0x1");
- }
- return join $delimiter, @fixedaddrs;
- }
-}
-
-# CPU profile reader
-sub ReadCPUProfile {
- my $prog = shift;
- my $fname = shift;
- my $version;
- my $period;
- my $i;
- my $profile = {};
- my $pcs = {};
-
- # Parse string into array of slots.
- my $slots = CpuProfileStream->new(*PROFILE, $fname);
-
- # Read header. The current header version is a 5-element structure
- # containing:
- # 0: header count (always 0)
- # 1: header "words" (after this one: 3)
- # 2: format version (0)
- # 3: sampling period (usec)
- # 4: unused padding (always 0)
- if ($slots->get(0) != 0 ) {
- error("$fname: not a profile file, or old format profile file\n");
- }
- $i = 2 + $slots->get(1);
- $version = $slots->get(2);
- $period = $slots->get(3);
- # Do some sanity checking on these header values.
- if ($version > (2**32) || $period > (2**32) || $i > (2**32) || $i < 5) {
- error("$fname: not a profile file, or corrupted profile file\n");
- }
-
- # Parse profile
- while ($slots->get($i) != -1) {
- my $n = $slots->get($i++);
- my $d = $slots->get($i++);
- if ($d > (2**16)) { # TODO(csilvers): what's a reasonable max-stack-depth?
- my $addr = sprintf("0%o", $i * ($address_length == 8 ? 4 : 8));
- print STDERR "At index $i (address $addr):\n";
- error("$fname: stack trace depth >= 2**32\n");
- }
- if ($slots->get($i) == 0) {
- # End of profile data marker
- $i += $d;
- last;
- }
-
- # Make key out of the stack entries
- my @k = ();
- for (my $j = 0; $j < $d; $j++) {
- my $pc = $slots->get($i+$j);
- # Subtract one from caller pc so we map back to call instr.
- # However, don't do this if we're reading a symbolized profile
- # file, in which case the subtract-one was done when the file
- # was written.
- if ($j > 0 && !$main::use_symbolized_profile) {
- $pc--;
- }
- $pc = sprintf("%0*x", $address_length, $pc);
- $pcs->{$pc} = 1;
- push @k, $pc;
- }
-
- AddEntry($profile, (join "\n", @k), $n);
- $i += $d;
- }
-
- # Parse map
- my $map = '';
- seek(PROFILE, $i * 4, 0);
- read(PROFILE, $map, (stat PROFILE)[7]);
- close(PROFILE);
-
- my $r = {};
- $r->{version} = $version;
- $r->{period} = $period;
- $r->{profile} = $profile;
- $r->{libs} = ParseLibraries($prog, $map, $pcs);
- $r->{pcs} = $pcs;
-
- return $r;
-}
-
-sub ReadHeapProfile {
- my $prog = shift;
- my $fname = shift;
- my $header = shift;
-
- my $index = 1;
- if ($main::opt_inuse_space) {
- $index = 1;
- } elsif ($main::opt_inuse_objects) {
- $index = 0;
- } elsif ($main::opt_alloc_space) {
- $index = 3;
- } elsif ($main::opt_alloc_objects) {
- $index = 2;
- }
-
- # Find the type of this profile. The header line looks like:
- # heap profile: 1246: 8800744 [ 1246: 8800744] @ <heap-url>/266053
- # There are two pairs <count: size>, the first inuse objects/space, and the
- # second allocated objects/space. This is followed optionally by a profile
- # type, and if that is present, optionally by a sampling frequency.
- # For remote heap profiles (v1):
- # The interpretation of the sampling frequency is that the profiler, for
- # each sample, calculates a uniformly distributed random integer less than
- # the given value, and records the next sample after that many bytes have
- # been allocated. Therefore, the expected sample interval is half of the
- # given frequency. By default, if not specified, the expected sample
- # interval is 128KB. Only remote-heap-page profiles are adjusted for
- # sample size.
- # For remote heap profiles (v2):
- # The sampling frequency is the rate of a Poisson process. This means that
- # the probability of sampling an allocation of size X with sampling rate Y
- # is 1 - exp(-X/Y)
- # For version 2, a typical header line might look like this:
- # heap profile: 1922: 127792360 [ 1922: 127792360] @ <heap-url>_v2/524288
- # the trailing number (524288) is the sampling rate. (Version 1 showed
- # double the 'rate' here)
- my $sampling_algorithm = 0;
- my $sample_adjustment = 0;
- chomp($header);
- my $type = "unknown";
- if ($header =~ m"^heap profile:\s*(\d+):\s+(\d+)\s+\[\s*(\d+):\s+(\d+)\](\s*@\s*([^/]*)(/(\d+))?)?") {
- if (defined($6) && ($6 ne '')) {
- $type = $6;
- my $sample_period = $8;
- # $type is "heapprofile" for profiles generated by the
- # heap-profiler, and either "heap" or "heap_v2" for profiles
- # generated by sampling directly within tcmalloc. It can also
- # be "growth" for heap-growth profiles. The first is typically
- # found for profiles generated locally, and the others for
- # remote profiles.
- if (($type eq "heapprofile") || ($type !~ /heap/) ) {
- # No need to adjust for the sampling rate with heap-profiler-derived data
- $sampling_algorithm = 0;
- } elsif ($type =~ /_v2/) {
- $sampling_algorithm = 2; # version 2 sampling
- if (defined($sample_period) && ($sample_period ne '')) {
- $sample_adjustment = int($sample_period);
- }
- } else {
- $sampling_algorithm = 1; # version 1 sampling
- if (defined($sample_period) && ($sample_period ne '')) {
- $sample_adjustment = int($sample_period)/2;
- }
- }
- } else {
- # We detect whether or not this is a remote-heap profile by checking
- # that the total-allocated stats ($n2,$s2) are exactly the
- # same as the in-use stats ($n1,$s1). It is remotely conceivable
- # that a non-remote-heap profile may pass this check, but it is hard
- # to imagine how that could happen.
- # In this case it's so old it's guaranteed to be remote-heap version 1.
- my ($n1, $s1, $n2, $s2) = ($1, $2, $3, $4);
- if (($n1 == $n2) && ($s1 == $s2)) {
- # This is likely to be a remote-heap based sample profile
- $sampling_algorithm = 1;
- }
- }
- }
-
- if ($sampling_algorithm > 0) {
- # For remote-heap generated profiles, adjust the counts and sizes to
- # account for the sample rate (we sample once every 128KB by default).
- if ($sample_adjustment == 0) {
- # Turn on profile adjustment.
- $sample_adjustment = 128*1024;
- print STDERR "Adjusting heap profiles for 1-in-128KB sampling rate\n";
- } else {
- printf STDERR ("Adjusting heap profiles for 1-in-%d sampling rate\n",
- $sample_adjustment);
- }
- if ($sampling_algorithm > 1) {
- # We don't bother printing anything for the original version (version 1)
- printf STDERR "Heap version $sampling_algorithm\n";
- }
- }
-
- my $profile = {};
- my $pcs = {};
- my $map = "";
-
- while (<PROFILE>) {
- s/\r//g; # turn windows-looking lines into unix-looking lines
- if (/^MAPPED_LIBRARIES:/) {
- # Read the /proc/self/maps data
- while (<PROFILE>) {
- s/\r//g; # turn windows-looking lines into unix-looking lines
- $map .= $_;
- }
- last;
- }
-
- if (/^--- Memory map:/) {
- # Read /proc/self/maps data as formatted by DumpAddressMap()
- my $buildvar = "";
- while (<PROFILE>) {
- s/\r//g; # turn windows-looking lines into unix-looking lines
- # Parse "build=<dir>" specification if supplied
- if (m/^\s*build=(.*)\n/) {
- $buildvar = $1;
- }
-
- # Expand "$build" variable if available
- $_ =~ s/\$build\b/$buildvar/g;
-
- $map .= $_;
- }
- last;
- }
-
- # Read entry of the form:
- # <count1>: <bytes1> [<count2>: <bytes2>] @ a1 a2 a3 ... an
- s/^\s*//;
- s/\s*$//;
- if (m/^\s*(\d+):\s+(\d+)\s+\[\s*(\d+):\s+(\d+)\]\s+@\s+(.*)$/) {
- my $stack = $5;
- my ($n1, $s1, $n2, $s2) = ($1, $2, $3, $4);
-
- if ($sample_adjustment) {
- if ($sampling_algorithm == 2) {
- # Remote-heap version 2
- # The sampling frequency is the rate of a Poisson process.
- # This means that the probability of sampling an allocation of
- # size X with sampling rate Y is 1 - exp(-X/Y)
- my $ratio;
- $ratio = (($s1*1.0)/$n1)/($sample_adjustment);
- my $scale_factor;
- $scale_factor = 1/(1 - exp(-$ratio));
- $n1 *= $scale_factor;
- $s1 *= $scale_factor;
- $ratio = (($s2*1.0)/$n2)/($sample_adjustment);
- $scale_factor = 1/(1 - exp(-$ratio));
- $n2 *= $scale_factor;
- $s2 *= $scale_factor;
- } else {
- # Remote-heap version 1
- my $ratio;
- $ratio = (($s1*1.0)/$n1)/($sample_adjustment);
- if ($ratio < 1) {
- $n1 /= $ratio;
- $s1 /= $ratio;
- }
- $ratio = (($s2*1.0)/$n2)/($sample_adjustment);
- if ($ratio < 1) {
- $n2 /= $ratio;
- $s2 /= $ratio;
- }
- }
- }
-
- my @counts = ($n1, $s1, $n2, $s2);
- AddEntries($profile, $pcs, FixCallerAddresses($stack), $counts[$index]);
- }
- }
-
- my $r = {};
- $r->{version} = "heap";
- $r->{period} = 1;
- $r->{profile} = $profile;
- $r->{libs} = ParseLibraries($prog, $map, $pcs);
- $r->{pcs} = $pcs;
- return $r;
-}
-
-sub ReadSynchProfile {
- my ($prog, $fname, $header) = @_;
-
- my $map = '';
- my $profile = {};
- my $pcs = {};
- my $sampling_period = 1;
- my $cyclespernanosec = 2.8; # Default assumption for old binaries
- my $seen_clockrate = 0;
- my $line;
-
- my $index = 0;
- if ($main::opt_total_delay) {
- $index = 0;
- } elsif ($main::opt_contentions) {
- $index = 1;
- } elsif ($main::opt_mean_delay) {
- $index = 2;
- }
-
- while ( $line = <PROFILE> ) {
- $line =~ s/\r//g; # turn windows-looking lines into unix-looking lines
- if ( $line =~ /^\s*(\d+)\s+(\d+) \@\s*(.*?)\s*$/ ) {
- my ($cycles, $count, $stack) = ($1, $2, $3);
-
- # Convert cycles to nanoseconds
- $cycles /= $cyclespernanosec;
-
- # Adjust for sampling done by application
- $cycles *= $sampling_period;
- $count *= $sampling_period;
-
- my @values = ($cycles, $count, $cycles / $count);
- AddEntries($profile, $pcs, FixCallerAddresses($stack), $values[$index]);
-
- } elsif ( $line =~ /^(slow release).*thread \d+ \@\s*(.*?)\s*$/ ||
- $line =~ /^\s*(\d+) \@\s*(.*?)\s*$/ ) {
- my ($cycles, $stack) = ($1, $2);
- if ($cycles !~ /^\d+$/) {
- next;
- }
-
- # Convert cycles to nanoseconds
- $cycles /= $cyclespernanosec;
-
- # Adjust for sampling done by application
- $cycles *= $sampling_period;
-
- AddEntries($profile, $pcs, FixCallerAddresses($stack), $cycles);
-
- } elsif ( $line =~ m/^([a-z][^=]*)=(.*)$/ ) {
- my ($variable, $value) = ($1,$2);
- for ($variable, $value) {
- s/^\s+//;
- s/\s+$//;
- }
- if ($variable eq "cycles/second") {
- $cyclespernanosec = $value / 1e9;
- $seen_clockrate = 1;
- } elsif ($variable eq "sampling period") {
- $sampling_period = $value;
- } elsif ($variable eq "ms since reset") {
- # Currently nothing is done with this value in pprof
- # So we just silently ignore it for now
- } elsif ($variable eq "discarded samples") {
- # Currently nothing is done with this value in pprof
- # So we just silently ignore it for now
- } else {
- printf STDERR ("Ignoring unnknown variable in /contention output: " .
- "'%s' = '%s'\n",$variable,$value);
- }
- } else {
- # Memory map entry
- $map .= $line;
- }
- }
- close PROFILE;
-
- if (!$seen_clockrate) {
- printf STDERR ("No cycles/second entry in profile; Guessing %.1f GHz\n",
- $cyclespernanosec);
- }
-
- my $r = {};
- $r->{version} = 0;
- $r->{period} = $sampling_period;
- $r->{profile} = $profile;
- $r->{libs} = ParseLibraries($prog, $map, $pcs);
- $r->{pcs} = $pcs;
- return $r;
-}
-
-# Given a hex value in the form "0x1abcd" return "0001abcd" or
-# "000000000001abcd", depending on the current address length.
-# There's probably a more idiomatic (or faster) way to do this...
-sub HexExtend {
- my $addr = shift;
-
- $addr =~ s/^0x//;
-
- if (length $addr > $address_length) {
- printf STDERR "Warning: address $addr is longer than address length $address_length\n";
- }
-
- return substr("000000000000000".$addr, -$address_length);
-}
-
-##### Symbol extraction #####
-
-# Aggressively search the lib_prefix values for the given library
-# If all else fails, just return the name of the library unmodified.
-# If the lib_prefix is "/my/path,/other/path" and $file is "/lib/dir/mylib.so"
-# it will search the following locations in this order, until it finds a file:
-# /my/path/lib/dir/mylib.so
-# /other/path/lib/dir/mylib.so
-# /my/path/dir/mylib.so
-# /other/path/dir/mylib.so
-# /my/path/mylib.so
-# /other/path/mylib.so
-# /lib/dir/mylib.so (returned as last resort)
-sub FindLibrary {
- my $file = shift;
- my $suffix = $file;
-
- # Search for the library as described above
- do {
- foreach my $prefix (@prefix_list) {
- my $fullpath = $prefix . $suffix;
- if (-e $fullpath) {
- return $fullpath;
- }
- }
- } while ($suffix =~ s|^/[^/]+/|/|);
- return $file;
-}
-
-# Return path to library with debugging symbols.
-# For libc libraries, the copy in /usr/lib/debug contains debugging symbols
-sub DebuggingLibrary {
- my $file = shift;
- if ($file =~ m|^/| && -f "/usr/lib/debug$file") {
- return "/usr/lib/debug$file";
- }
- return undef;
-}
-
-# Parse text section header of a library using objdump
-sub ParseTextSectionHeaderFromObjdump {
- my $lib = shift;
-
- my $size = undef;
- my $vma;
- my $file_offset;
- # Get objdump output from the library file to figure out how to
- # map between mapped addresses and addresses in the library.
- my $objdump = $obj_tool_map{"objdump"};
- open(OBJDUMP, "$objdump -h $lib |")
- || error("$objdump $lib: $!\n");
- while (<OBJDUMP>) {
- s/\r//g; # turn windows-looking lines into unix-looking lines
- # Idx Name Size VMA LMA File off Algn
- # 10 .text 00104b2c 420156f0 420156f0 000156f0 2**4
- # For 64-bit objects, VMA and LMA will be 16 hex digits, size and file
- # offset may still be 8. But AddressSub below will still handle that.
- my @x = split;
- if (($#x >= 6) && ($x[1] eq '.text')) {
- $size = $x[2];
- $vma = $x[3];
- $file_offset = $x[5];
- last;
- }
- }
- close(OBJDUMP);
-
- if (!defined($size)) {
- return undef;
- }
-
- my $r = {};
- $r->{size} = $size;
- $r->{vma} = $vma;
- $r->{file_offset} = $file_offset;
-
- return $r;
-}
-
-# Parse text section header of a library using otool (on OS X)
-sub ParseTextSectionHeaderFromOtool {
- my $lib = shift;
-
- my $size = undef;
- my $vma = undef;
- my $file_offset = undef;
- # Get otool output from the library file to figure out how to
- # map between mapped addresses and addresses in the library.
- my $otool = $obj_tool_map{"otool"};
- open(OTOOL, "$otool -l $lib |")
- || error("$otool $lib: $!\n");
- my $cmd = "";
- my $sectname = "";
- my $segname = "";
- foreach my $line (<OTOOL>) {
- $line =~ s/\r//g; # turn windows-looking lines into unix-looking lines
- # Load command <#>
- # cmd LC_SEGMENT
- # [...]
- # Section
- # sectname __text
- # segname __TEXT
- # addr 0x000009f8
- # size 0x00018b9e
- # offset 2552
- # align 2^2 (4)
- # We will need to strip off the leading 0x from the hex addresses,
- # and convert the offset into hex.
- if ($line =~ /Load command/) {
- $cmd = "";
- $sectname = "";
- $segname = "";
- } elsif ($line =~ /Section/) {
- $sectname = "";
- $segname = "";
- } elsif ($line =~ /cmd (\w+)/) {
- $cmd = $1;
- } elsif ($line =~ /sectname (\w+)/) {
- $sectname = $1;
- } elsif ($line =~ /segname (\w+)/) {
- $segname = $1;
- } elsif (!(($cmd eq "LC_SEGMENT" || $cmd eq "LC_SEGMENT_64") &&
- $sectname eq "__text" &&
- $segname eq "__TEXT")) {
- next;
- } elsif ($line =~ /\baddr 0x([0-9a-fA-F]+)/) {
- $vma = $1;
- } elsif ($line =~ /\bsize 0x([0-9a-fA-F]+)/) {
- $size = $1;
- } elsif ($line =~ /\boffset ([0-9]+)/) {
- $file_offset = sprintf("%016x", $1);
- }
- if (defined($vma) && defined($size) && defined($file_offset)) {
- last;
- }
- }
- close(OTOOL);
-
- if (!defined($vma) || !defined($size) || !defined($file_offset)) {
- return undef;
- }
-
- my $r = {};
- $r->{size} = $size;
- $r->{vma} = $vma;
- $r->{file_offset} = $file_offset;
-
- return $r;
-}
-
-sub ParseTextSectionHeader {
- # obj_tool_map("otool") is only defined if we're in a Mach-O environment
- if (defined($obj_tool_map{"otool"})) {
- my $r = ParseTextSectionHeaderFromOtool(@_);
- if (defined($r)){
- return $r;
- }
- }
- # If otool doesn't work, or we don't have it, fall back to objdump
- return ParseTextSectionHeaderFromObjdump(@_);
-}
-
-# Split /proc/pid/maps dump into a list of libraries
-sub ParseLibraries {
- return if $main::use_symbol_page; # We don't need libraries info.
- my $prog = shift;
- my $map = shift;
- my $pcs = shift;
-
- my $result = [];
- my $h = "[a-f0-9]+";
- my $zero_offset = HexExtend("0");
-
- my $buildvar = "";
- foreach my $l (split("\n", $map)) {
- if ($l =~ m/^\s*build=(.*)$/) {
- $buildvar = $1;
- }
-
- my $start;
- my $finish;
- my $offset;
- my $lib;
- if ($l =~ /^($h)-($h)\s+..x.\s+($h)\s+\S+:\S+\s+\d+\s+(\S+\.(so|dll|dylib|bundle)((\.\d+)+\w*(\.\d+){0,3})?)$/i) {
- # Full line from /proc/self/maps. Example:
- # 40000000-40015000 r-xp 00000000 03:01 12845071 /lib/ld-2.3.2.so
- $start = HexExtend($1);
- $finish = HexExtend($2);
- $offset = HexExtend($3);
- $lib = $4;
- $lib =~ s|\\|/|g; # turn windows-style paths into unix-style paths
- } elsif ($l =~ /^\s*($h)-($h):\s*(\S+\.so(\.\d+)*)/) {
- # Cooked line from DumpAddressMap. Example:
- # 40000000-40015000: /lib/ld-2.3.2.so
- $start = HexExtend($1);
- $finish = HexExtend($2);
- $offset = $zero_offset;
- $lib = $3;
- } else {
- next;
- }
-
- # Expand "$build" variable if available
- $lib =~ s/\$build\b/$buildvar/g;
-
- $lib = FindLibrary($lib);
-
- # Check for pre-relocated libraries, which use pre-relocated symbol tables
- # and thus require adjusting the offset that we'll use to translate
- # VM addresses into symbol table addresses.
- # Only do this if we're not going to fetch the symbol table from a
- # debugging copy of the library.
- if (!DebuggingLibrary($lib)) {
- my $text = ParseTextSectionHeader($lib);
- if (defined($text)) {
- my $vma_offset = AddressSub($text->{vma}, $text->{file_offset});
- $offset = AddressAdd($offset, $vma_offset);
- }
- }
-
- push(@{$result}, [$lib, $start, $finish, $offset]);
- }
-
- # Append special entry for additional library (not relocated)
- if ($main::opt_lib ne "") {
- my $text = ParseTextSectionHeader($main::opt_lib);
- if (defined($text)) {
- my $start = $text->{vma};
- my $finish = AddressAdd($start, $text->{size});
-
- push(@{$result}, [$main::opt_lib, $start, $finish, $start]);
- }
- }
-
- # Append special entry for the main program. This covers
- # 0..max_pc_value_seen, so that we assume pc values not found in one
- # of the library ranges will be treated as coming from the main
- # program binary.
- my $min_pc = HexExtend("0");
- my $max_pc = $min_pc; # find the maximal PC value in any sample
- foreach my $pc (keys(%{$pcs})) {
- if (HexExtend($pc) gt $max_pc) { $max_pc = HexExtend($pc); }
- }
- push(@{$result}, [$prog, $min_pc, $max_pc, $zero_offset]);
-
- return $result;
-}
-
-# Add two hex addresses of length $address_length.
-# Run pprof --test for unit test if this is changed.
-sub AddressAdd {
- my $addr1 = shift;
- my $addr2 = shift;
- my $sum;
-
- if ($address_length == 8) {
- # Perl doesn't cope with wraparound arithmetic, so do it explicitly:
- $sum = (hex($addr1)+hex($addr2)) % (0x10000000 * 16);
- return sprintf("%08x", $sum);
-
- } else {
- # Do the addition in 7-nibble chunks to trivialize carry handling.
-
- if ($main::opt_debug and $main::opt_test) {
- print STDERR "AddressAdd $addr1 + $addr2 = ";
- }
-
- my $a1 = substr($addr1,-7);
- $addr1 = substr($addr1,0,-7);
- my $a2 = substr($addr2,-7);
- $addr2 = substr($addr2,0,-7);
- $sum = hex($a1) + hex($a2);
- my $c = 0;
- if ($sum > 0xfffffff) {
- $c = 1;
- $sum -= 0x10000000;
- }
- my $r = sprintf("%07x", $sum);
-
- $a1 = substr($addr1,-7);
- $addr1 = substr($addr1,0,-7);
- $a2 = substr($addr2,-7);
- $addr2 = substr($addr2,0,-7);
- $sum = hex($a1) + hex($a2) + $c;
- $c = 0;
- if ($sum > 0xfffffff) {
- $c = 1;
- $sum -= 0x10000000;
- }
- $r = sprintf("%07x", $sum) . $r;
-
- $sum = hex($addr1) + hex($addr2) + $c;
- if ($sum > 0xff) { $sum -= 0x100; }
- $r = sprintf("%02x", $sum) . $r;
-
- if ($main::opt_debug and $main::opt_test) { print STDERR "$r\n"; }
-
- return $r;
- }
-}
-
-
-# Subtract two hex addresses of length $address_length.
-# Run pprof --test for unit test if this is changed.
-sub AddressSub {
- my $addr1 = shift;
- my $addr2 = shift;
- my $diff;
-
- if ($address_length == 8) {
- # Perl doesn't cope with wraparound arithmetic, so do it explicitly:
- $diff = (hex($addr1)-hex($addr2)) % (0x10000000 * 16);
- return sprintf("%08x", $diff);
-
- } else {
- # Do the addition in 7-nibble chunks to trivialize borrow handling.
- # if ($main::opt_debug) { print STDERR "AddressSub $addr1 - $addr2 = "; }
-
- my $a1 = hex(substr($addr1,-7));
- $addr1 = substr($addr1,0,-7);
- my $a2 = hex(substr($addr2,-7));
- $addr2 = substr($addr2,0,-7);
- my $b = 0;
- if ($a2 > $a1) {
- $b = 1;
- $a1 += 0x10000000;
- }
- $diff = $a1 - $a2;
- my $r = sprintf("%07x", $diff);
-
- $a1 = hex(substr($addr1,-7));
- $addr1 = substr($addr1,0,-7);
- $a2 = hex(substr($addr2,-7)) + $b;
- $addr2 = substr($addr2,0,-7);
- $b = 0;
- if ($a2 > $a1) {
- $b = 1;
- $a1 += 0x10000000;
- }
- $diff = $a1 - $a2;
- $r = sprintf("%07x", $diff) . $r;
-
- $a1 = hex($addr1);
- $a2 = hex($addr2) + $b;
- if ($a2 > $a1) { $a1 += 0x100; }
- $diff = $a1 - $a2;
- $r = sprintf("%02x", $diff) . $r;
-
- # if ($main::opt_debug) { print STDERR "$r\n"; }
-
- return $r;
- }
-}
-
-# Increment a hex addresses of length $address_length.
-# Run pprof --test for unit test if this is changed.
-sub AddressInc {
- my $addr = shift;
- my $sum;
-
- if ($address_length == 8) {
- # Perl doesn't cope with wraparound arithmetic, so do it explicitly:
- $sum = (hex($addr)+1) % (0x10000000 * 16);
- return sprintf("%08x", $sum);
-
- } else {
- # Do the addition in 7-nibble chunks to trivialize carry handling.
- # We are always doing this to step through the addresses in a function,
- # and will almost never overflow the first chunk, so we check for this
- # case and exit early.
-
- # if ($main::opt_debug) { print STDERR "AddressInc $addr1 = "; }
-
- my $a1 = substr($addr,-7);
- $addr = substr($addr,0,-7);
- $sum = hex($a1) + 1;
- my $r = sprintf("%07x", $sum);
- if ($sum <= 0xfffffff) {
- $r = $addr . $r;
- # if ($main::opt_debug) { print STDERR "$r\n"; }
- return HexExtend($r);
- } else {
- $r = "0000000";
- }
-
- $a1 = substr($addr,-7);
- $addr = substr($addr,0,-7);
- $sum = hex($a1) + 1;
- $r = sprintf("%07x", $sum) . $r;
- if ($sum <= 0xfffffff) {
- $r = $addr . $r;
- # if ($main::opt_debug) { print STDERR "$r\n"; }
- return HexExtend($r);
- } else {
- $r = "00000000000000";
- }
-
- $sum = hex($addr) + 1;
- if ($sum > 0xff) { $sum -= 0x100; }
- $r = sprintf("%02x", $sum) . $r;
-
- # if ($main::opt_debug) { print STDERR "$r\n"; }
- return $r;
- }
-}
-
-# Extract symbols for all PC values found in profile
-sub ExtractSymbols {
- my $libs = shift;
- my $pcset = shift;
-
- my $symbols = {};
-
- # Map each PC value to the containing library
- my %seen = ();
- foreach my $lib (@{$libs}) {
- my $libname = $lib->[0];
- my $start = $lib->[1];
- my $finish = $lib->[2];
- my $offset = $lib->[3];
-
- # Get list of pcs that belong in this library.
- my $contained = [];
- foreach my $pc (keys(%{$pcset})) {
- if (!$seen{$pc} && ($pc ge $start) && ($pc le $finish)) {
- $seen{$pc} = 1;
- push(@{$contained}, $pc);
- }
- }
- # Map to symbols
- MapToSymbols($libname, AddressSub($start, $offset), $contained, $symbols);
- }
-
- return $symbols;
-}
-
-# Map list of PC values to symbols for a given image
-sub MapToSymbols {
- my $image = shift;
- my $offset = shift;
- my $pclist = shift;
- my $symbols = shift;
-
- my $debug = 0;
-
- # Ignore empty binaries
- if ($#{$pclist} < 0) { return; }
-
- # Figure out the addr2line command to use
- my $addr2line = $obj_tool_map{"addr2line"};
- my $cmd = "$addr2line -f -C -e $image";
- if (exists $obj_tool_map{"addr2line_pdb"}) {
- $addr2line = $obj_tool_map{"addr2line_pdb"};
- $cmd = "$addr2line --demangle -f -C -e $image";
- }
-
- # If "addr2line" isn't installed on the system at all, just use
- # nm to get what info we can (function names, but not line numbers).
- if (system("$addr2line --help >/dev/null 2>&1") != 0) {
- MapSymbolsWithNM($image, $offset, $pclist, $symbols);
- return;
- }
-
- # "addr2line -i" can produce a variable number of lines per input
- # address, with no separator that allows us to tell when data for
- # the next address starts. So we find the address for a special
- # symbol (_fini) and interleave this address between all real
- # addresses passed to addr2line. The name of this special symbol
- # can then be used as a separator.
- $sep_address = undef; # May be filled in by MapSymbolsWithNM()
- my $nm_symbols = {};
- MapSymbolsWithNM($image, $offset, $pclist, $nm_symbols);
- # TODO(csilvers): only add '-i' if addr2line supports it.
- if (defined($sep_address)) {
- # Only add " -i" to addr2line if the binary supports it.
- # addr2line --help returns 0, but not if it sees an unknown flag first.
- if (system("$cmd -i --help >/dev/null 2>&1") == 0) {
- $cmd .= " -i";
- } else {
- $sep_address = undef; # no need for sep_address if we don't support -i
- }
- }
-
- # Make file with all PC values with intervening 'sep_address' so
- # that we can reliably detect the end of inlined function list
- open(ADDRESSES, ">$main::tmpfile_sym") || error("$main::tmpfile_sym: $!\n");
- if ($debug) { print("---- $image ---\n"); }
- for (my $i = 0; $i <= $#{$pclist}; $i++) {
- # addr2line always reads hex addresses, and does not need '0x' prefix.
- if ($debug) { printf STDERR ("%s\n", $pclist->[$i]); }
- printf ADDRESSES ("%s\n", AddressSub($pclist->[$i], $offset));
- if (defined($sep_address)) {
- printf ADDRESSES ("%s\n", $sep_address);
- }
- }
- close(ADDRESSES);
- if ($debug) {
- print("----\n");
- system("cat $main::tmpfile_sym");
- print("----\n");
- system("$cmd <$main::tmpfile_sym");
- print("----\n");
- }
-
- open(SYMBOLS, "$cmd <$main::tmpfile_sym |") || error("$cmd: $!\n");
- my $count = 0; # Index in pclist
- while (<SYMBOLS>) {
- # Read fullfunction and filelineinfo from next pair of lines
- s/\r?\n$//g;
- my $fullfunction = $_;
- $_ = <SYMBOLS>;
- s/\r?\n$//g;
- my $filelinenum = $_;
-
- if (defined($sep_address) && $fullfunction eq $sep_symbol) {
- # Terminating marker for data for this address
- $count++;
- next;
- }
-
- $filelinenum =~ s|\\|/|g; # turn windows-style paths into unix-style paths
-
- my $pcstr = $pclist->[$count];
- my $function = ShortFunctionName($fullfunction);
- if ($fullfunction eq '??') {
- # See if nm found a symbol
- my $nms = $nm_symbols->{$pcstr};
- if (defined($nms)) {
- $function = $nms->[0];
- $fullfunction = $nms->[2];
- }
- }
-
- # Prepend to accumulated symbols for pcstr
- # (so that caller comes before callee)
- my $sym = $symbols->{$pcstr};
- if (!defined($sym)) {
- $sym = [];
- $symbols->{$pcstr} = $sym;
- }
- unshift(@{$sym}, $function, $filelinenum, $fullfunction);
- if ($debug) { printf STDERR ("%s => [%s]\n", $pcstr, join(" ", @{$sym})); }
- if (!defined($sep_address)) {
- # Inlining is off, se this entry ends immediately
- $count++;
- }
- }
- close(SYMBOLS);
-}
-
-# Use nm to map the list of referenced PCs to symbols. Return true iff we
-# are able to read procedure information via nm.
-sub MapSymbolsWithNM {
- my $image = shift;
- my $offset = shift;
- my $pclist = shift;
- my $symbols = shift;
-
- # Get nm output sorted by increasing address
- my $symbol_table = GetProcedureBoundaries($image, ".");
- if (!%{$symbol_table}) {
- return 0;
- }
- # Start addresses are already the right length (8 or 16 hex digits).
- my @names = sort { $symbol_table->{$a}->[0] cmp $symbol_table->{$b}->[0] }
- keys(%{$symbol_table});
-
- if ($#names < 0) {
- # No symbols: just use addresses
- foreach my $pc (@{$pclist}) {
- my $pcstr = "0x" . $pc;
- $symbols->{$pc} = [$pcstr, "?", $pcstr];
- }
- return 0;
- }
-
- # Sort addresses so we can do a join against nm output
- my $index = 0;
- my $fullname = $names[0];
- my $name = ShortFunctionName($fullname);
- foreach my $pc (sort { $a cmp $b } @{$pclist}) {
- # Adjust for mapped offset
- my $mpc = AddressSub($pc, $offset);
- while (($index < $#names) && ($mpc ge $symbol_table->{$fullname}->[1])){
- $index++;
- $fullname = $names[$index];
- $name = ShortFunctionName($fullname);
- }
- if ($mpc lt $symbol_table->{$fullname}->[1]) {
- $symbols->{$pc} = [$name, "?", $fullname];
- } else {
- my $pcstr = "0x" . $pc;
- $symbols->{$pc} = [$pcstr, "?", $pcstr];
- }
- }
- return 1;
-}
-
-sub ShortFunctionName {
- my $function = shift;
- while ($function =~ s/\([^()]*\)(\s*const)?//g) { } # Argument types
- while ($function =~ s/<[^<>]*>//g) { } # Remove template arguments
- $function =~ s/^.*\s+(\w+::)/$1/; # Remove leading type
- return $function;
-}
-
-# Trim overly long symbols found in disassembler output
-sub CleanDisassembly {
- my $d = shift;
- while ($d =~ s/\([^()%]*\)(\s*const)?//g) { } # Argument types, not (%rax)
- while ($d =~ s/(\w+)<[^<>]*>/$1/g) { } # Remove template arguments
- return $d;
-}
-
-##### Miscellaneous #####
-
-# Find the right versions of the above object tools to use. The
-# argument is the program file being analyzed, and should be an ELF
-# 32-bit or ELF 64-bit executable file. The location of the tools
-# is determined by considering the following options in this order:
-# 1) --tools option, if set
-# 2) PPROF_TOOLS environment variable, if set
-# 3) the environment
-sub ConfigureObjTools {
- my $prog_file = shift;
-
- # Check for the existence of $prog_file because /usr/bin/file does not
- # predictably return error status in prod.
- (-e $prog_file) || error("$prog_file does not exist.\n");
-
- # Follow symlinks (at least for systems where "file" supports that)
- my $file_type = `/usr/bin/file -L $prog_file 2>/dev/null || /usr/bin/file $prog_file`;
- if ($file_type =~ /64-bit/) {
- # Change $address_length to 16 if the program file is ELF 64-bit.
- # We can't detect this from many (most?) heap or lock contention
- # profiles, since the actual addresses referenced are generally in low
- # memory even for 64-bit programs.
- $address_length = 16;
- }
-
- if ($file_type =~ /MS Windows/) {
- # For windows, we provide a version of nm and addr2line as part of
- # the opensource release, which is capable of parsing
- # Windows-style PDB executables. It should live in the path, or
- # in the same directory as pprof.
- $obj_tool_map{"nm_pdb"} = "nm-pdb";
- $obj_tool_map{"addr2line_pdb"} = "addr2line-pdb";
- }
-
- if ($file_type =~ /Mach-O/) {
- # OS X uses otool to examine Mach-O files, rather than objdump.
- $obj_tool_map{"otool"} = "otool";
- $obj_tool_map{"addr2line"} = "false"; # no addr2line
- $obj_tool_map{"objdump"} = "false"; # no objdump
- }
-
- # Go fill in %obj_tool_map with the pathnames to use:
- foreach my $tool (keys %obj_tool_map) {
- $obj_tool_map{$tool} = ConfigureTool($obj_tool_map{$tool});
- }
-}
-
-# Returns the path of a caller-specified object tool. If --tools or
-# PPROF_TOOLS are specified, then returns the full path to the tool
-# with that prefix. Otherwise, returns the path unmodified (which
-# means we will look for it on PATH).
-sub ConfigureTool {
- my $tool = shift;
- my $path;
-
- if ($main::opt_tools ne "") {
- # Use a prefix specified by the --tools option...
- $path = $main::opt_tools . $tool;
- if (!-x $path) {
- error("No '$tool' found with prefix specified by --tools $main::opt_tools\n");
- }
- } elsif (exists $ENV{"PPROF_TOOLS"} &&
- $ENV{"PPROF_TOOLS"} ne "") {
- #... or specified with the PPROF_TOOLS environment variable...
- $path = $ENV{"PPROF_TOOLS"} . $tool;
- if (!-x $path) {
- error("No '$tool' found with prefix specified by PPROF_TOOLS=$ENV{PPROF_TOOLS}\n");
- }
- } else {
- # ... otherwise use the version that exists in the same directory as
- # pprof. If there's nothing there, use $PATH.
- $0 =~ m,[^/]*$,; # this is everything after the last slash
- my $dirname = $`; # this is everything up to and including the last slash
- if (-x "$dirname$tool") {
- $path = "$dirname$tool";
- } else {
- $path = $tool;
- }
- }
- if ($main::opt_debug) { print STDERR "Using '$path' for '$tool'.\n"; }
- return $path;
-}
-
-sub cleanup {
- unlink($main::tmpfile_sym);
- unlink(keys %main::tempnames);
-
- # We leave any collected profiles in $HOME/pprof in case the user wants
- # to look at them later. We print a message informing them of this.
- if ((scalar(@main::profile_files) > 0) &&
- defined($main::collected_profile)) {
- if (scalar(@main::profile_files) == 1) {
- print STDERR "Dynamically gathered profile is in $main::collected_profile\n";
- }
- print STDERR "If you want to investigate this profile further, you can do:\n";
- print STDERR "\n";
- print STDERR " pprof \\\n";
- print STDERR " $main::prog \\\n";
- print STDERR " $main::collected_profile\n";
- print STDERR "\n";
- }
-}
-
-sub sighandler {
- cleanup();
- exit(1);
-}
-
-sub error {
- my $msg = shift;
- print STDERR $msg;
- cleanup();
- exit(1);
-}
-
-
-# Run $nm_command and get all the resulting procedure boundaries whose
-# names match "$regexp" and returns them in a hashtable mapping from
-# procedure name to a two-element vector of [start address, end address]
-sub GetProcedureBoundariesViaNm {
- my $nm_command = shift;
- my $regexp = shift;
-
- my $symbol_table = {};
- open(NM, "$nm_command |") || error("$nm_command: $!\n");
- my $last_start = "0";
- my $routine = "";
- while (<NM>) {
- s/\r//g; # turn windows-looking lines into unix-looking lines
- if (m/^\s*([0-9a-f]+) (.) (..*)/) {
- my $start_val = $1;
- my $type = $2;
- my $this_routine = $3;
-
- # It's possible for two symbols to share the same address, if
- # one is a zero-length variable (like __start_google_malloc) or
- # one symbol is a weak alias to another (like __libc_malloc).
- # In such cases, we want to ignore all values except for the
- # actual symbol, which in nm-speak has type "T". The logic
- # below does this, though it's a bit tricky: what happens when
- # we have a series of lines with the same address, is the first
- # one gets queued up to be processed. However, it won't
- # *actually* be processed until later, when we read a line with
- # a different address. That means that as long as we're reading
- # lines with the same address, we have a chance to replace that
- # item in the queue, which we do whenever we see a 'T' entry --
- # that is, a line with type 'T'. If we never see a 'T' entry,
- # we'll just go ahead and process the first entry (which never
- # got touched in the queue), and ignore the others.
- if ($start_val eq $last_start && $type =~ /t/i) {
- # We are the 'T' symbol at this address, replace previous symbol.
- $routine = $this_routine;
- next;
- } elsif ($start_val eq $last_start) {
- # We're not the 'T' symbol at this address, so ignore us.
- next;
- }
-
- if ($this_routine eq $sep_symbol) {
- $sep_address = HexExtend($start_val);
- }
-
- # Tag this routine with the starting address in case the image
- # has multiple occurrences of this routine. We use a syntax
- # that resembles template paramters that are automatically
- # stripped out by ShortFunctionName()
- $this_routine .= "<$start_val>";
-
- if (defined($routine) && $routine =~ m/$regexp/) {
- $symbol_table->{$routine} = [HexExtend($last_start),
- HexExtend($start_val)];
- }
- $last_start = $start_val;
- $routine = $this_routine;
- } elsif (m/^Loaded image name: (.+)/) {
- # The win32 nm workalike emits information about the binary it is using.
- if ($main::opt_debug) { print STDERR "Using Image $1\n"; }
- } elsif (m/^PDB file name: (.+)/) {
- # The win32 nm workalike emits information about the pdb it is using.
- if ($main::opt_debug) { print STDERR "Using PDB $1\n"; }
- }
- }
- close(NM);
- # Handle the last line in the nm output. Unfortunately, we don't know
- # how big this last symbol is, because we don't know how big the file
- # is. For now, we just give it a size of 0.
- # TODO(csilvers): do better here.
- if (defined($routine) && $routine =~ m/$regexp/) {
- $symbol_table->{$routine} = [HexExtend($last_start),
- HexExtend($last_start)];
- }
- return $symbol_table;
-}
-
-# Gets the procedure boundaries for all routines in "$image" whose names
-# match "$regexp" and returns them in a hashtable mapping from procedure
-# name to a two-element vector of [start address, end address].
-# Will return an empty map if nm is not installed or not working properly.
-sub GetProcedureBoundaries {
- my $image = shift;
- my $regexp = shift;
-
- # For libc libraries, the copy in /usr/lib/debug contains debugging symbols
- my $debugging = DebuggingLibrary($image);
- if ($debugging) {
- $image = $debugging;
- }
-
- my $nm = $obj_tool_map{"nm"};
- my $cppfilt = $obj_tool_map{"c++filt"};
-
- # nm can fail for two reasons: 1) $image isn't a debug library; 2) nm
- # binary doesn't support --demangle. In addition, for OS X we need
- # to use the -f flag to get 'flat' nm output (otherwise we don't sort
- # properly and get incorrect results). Unfortunately, GNU nm uses -f
- # in an incompatible way. So first we test whether our nm supports
- # --demangle and -f.
- my $demangle_flag = "";
- my $cppfilt_flag = "";
- if (system("$nm --demangle $image >/dev/null 2>&1") == 0) {
- # In this mode, we do "nm --demangle <foo>"
- $demangle_flag = "--demangle";
- $cppfilt_flag = "";
- } elsif (system("$cppfilt $image >/dev/null 2>&1") == 0) {
- # In this mode, we do "nm <foo> | c++filt"
- $cppfilt_flag = " | $cppfilt";
- };
- my $flatten_flag = "";
- if (system("$nm -f $image >/dev/null 2>&1") == 0) {
- $flatten_flag = "-f";
- }
-
- # Finally, in the case $imagie isn't a debug library, we try again with
- # -D to at least get *exported* symbols. If we can't use --demangle,
- # we use c++filt instead, if it exists on this system.
- my @nm_commands = ("$nm -n $flatten_flag $demangle_flag" .
- " $image 2>/dev/null $cppfilt_flag",
- "$nm -D -n $flatten_flag $demangle_flag" .
- " $image 2>/dev/null $cppfilt_flag",
- # 6nm is for Go binaries
- "6nm $image 2>/dev/null | sort");
-
- # If the executable is an MS Windows PDB-format executable, we'll
- # have set up obj_tool_map("nm_pdb"). In this case, we actually
- # want to use both unix nm and windows-specific nm_pdb, since
- # PDB-format executables can apparently include dwarf .o files.
- if (exists $obj_tool_map{"nm_pdb"}) {
- my $nm_pdb = $obj_tool_map{"nm_pdb"};
- push(@nm_commands, "$nm_pdb --demangle $image 2>/dev/null");
- }
-
- foreach my $nm_command (@nm_commands) {
- my $symbol_table = GetProcedureBoundariesViaNm($nm_command, $regexp);
- return $symbol_table if (%{$symbol_table});
- }
- my $symbol_table = {};
- return $symbol_table;
-}
-
-
-# The test vectors for AddressAdd/Sub/Inc are 8-16-nibble hex strings.
-# To make them more readable, we add underscores at interesting places.
-# This routine removes the underscores, producing the canonical representation
-# used by pprof to represent addresses, particularly in the tested routines.
-sub CanonicalHex {
- my $arg = shift;
- return join '', (split '_',$arg);
-}
-
-
-# Unit test for AddressAdd:
-sub AddressAddUnitTest {
- my $test_data_8 = shift;
- my $test_data_16 = shift;
- my $error_count = 0;
- my $fail_count = 0;
- my $pass_count = 0;
- # print STDERR "AddressAddUnitTest: ", 1+$#{$test_data_8}, " tests\n";
-
- # First a few 8-nibble addresses. Note that this implementation uses
- # plain old arithmetic, so a quick sanity check along with verifying what
- # happens to overflow (we want it to wrap):
- $address_length = 8;
- foreach my $row (@{$test_data_8}) {
- if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
- my $sum = AddressAdd ($row->[0], $row->[1]);
- if ($sum ne $row->[2]) {
- printf STDERR "ERROR: %s != %s + %s = %s\n", $sum,
- $row->[0], $row->[1], $row->[2];
- ++$fail_count;
- } else {
- ++$pass_count;
- }
- }
- printf STDERR "AddressAdd 32-bit tests: %d passes, %d failures\n",
- $pass_count, $fail_count;
- $error_count = $fail_count;
- $fail_count = 0;
- $pass_count = 0;
-
- # Now 16-nibble addresses.
- $address_length = 16;
- foreach my $row (@{$test_data_16}) {
- if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
- my $sum = AddressAdd (CanonicalHex($row->[0]), CanonicalHex($row->[1]));
- my $expected = join '', (split '_',$row->[2]);
- if ($sum ne CanonicalHex($row->[2])) {
- printf STDERR "ERROR: %s != %s + %s = %s\n", $sum,
- $row->[0], $row->[1], $row->[2];
- ++$fail_count;
- } else {
- ++$pass_count;
- }
- }
- printf STDERR "AddressAdd 64-bit tests: %d passes, %d failures\n",
- $pass_count, $fail_count;
- $error_count += $fail_count;
-
- return $error_count;
-}
-
-
-# Unit test for AddressSub:
-sub AddressSubUnitTest {
- my $test_data_8 = shift;
- my $test_data_16 = shift;
- my $error_count = 0;
- my $fail_count = 0;
- my $pass_count = 0;
- # print STDERR "AddressSubUnitTest: ", 1+$#{$test_data_8}, " tests\n";
-
- # First a few 8-nibble addresses. Note that this implementation uses
- # plain old arithmetic, so a quick sanity check along with verifying what
- # happens to overflow (we want it to wrap):
- $address_length = 8;
- foreach my $row (@{$test_data_8}) {
- if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
- my $sum = AddressSub ($row->[0], $row->[1]);
- if ($sum ne $row->[3]) {
- printf STDERR "ERROR: %s != %s - %s = %s\n", $sum,
- $row->[0], $row->[1], $row->[3];
- ++$fail_count;
- } else {
- ++$pass_count;
- }
- }
- printf STDERR "AddressSub 32-bit tests: %d passes, %d failures\n",
- $pass_count, $fail_count;
- $error_count = $fail_count;
- $fail_count = 0;
- $pass_count = 0;
-
- # Now 16-nibble addresses.
- $address_length = 16;
- foreach my $row (@{$test_data_16}) {
- if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
- my $sum = AddressSub (CanonicalHex($row->[0]), CanonicalHex($row->[1]));
- if ($sum ne CanonicalHex($row->[3])) {
- printf STDERR "ERROR: %s != %s - %s = %s\n", $sum,
- $row->[0], $row->[1], $row->[3];
- ++$fail_count;
- } else {
- ++$pass_count;
- }
- }
- printf STDERR "AddressSub 64-bit tests: %d passes, %d failures\n",
- $pass_count, $fail_count;
- $error_count += $fail_count;
-
- return $error_count;
-}
-
-
-# Unit test for AddressInc:
-sub AddressIncUnitTest {
- my $test_data_8 = shift;
- my $test_data_16 = shift;
- my $error_count = 0;
- my $fail_count = 0;
- my $pass_count = 0;
- # print STDERR "AddressIncUnitTest: ", 1+$#{$test_data_8}, " tests\n";
-
- # First a few 8-nibble addresses. Note that this implementation uses
- # plain old arithmetic, so a quick sanity check along with verifying what
- # happens to overflow (we want it to wrap):
- $address_length = 8;
- foreach my $row (@{$test_data_8}) {
- if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
- my $sum = AddressInc ($row->[0]);
- if ($sum ne $row->[4]) {
- printf STDERR "ERROR: %s != %s + 1 = %s\n", $sum,
- $row->[0], $row->[4];
- ++$fail_count;
- } else {
- ++$pass_count;
- }
- }
- printf STDERR "AddressInc 32-bit tests: %d passes, %d failures\n",
- $pass_count, $fail_count;
- $error_count = $fail_count;
- $fail_count = 0;
- $pass_count = 0;
-
- # Now 16-nibble addresses.
- $address_length = 16;
- foreach my $row (@{$test_data_16}) {
- if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
- my $sum = AddressInc (CanonicalHex($row->[0]));
- if ($sum ne CanonicalHex($row->[4])) {
- printf STDERR "ERROR: %s != %s + 1 = %s\n", $sum,
- $row->[0], $row->[4];
- ++$fail_count;
- } else {
- ++$pass_count;
- }
- }
- printf STDERR "AddressInc 64-bit tests: %d passes, %d failures\n",
- $pass_count, $fail_count;
- $error_count += $fail_count;
-
- return $error_count;
-}
-
-
-# Driver for unit tests.
-# Currently just the address add/subtract/increment routines for 64-bit.
-sub RunUnitTests {
- my $error_count = 0;
-
- # This is a list of tuples [a, b, a+b, a-b, a+1]
- my $unit_test_data_8 = [
- [qw(aaaaaaaa 50505050 fafafafa 5a5a5a5a aaaaaaab)],
- [qw(50505050 aaaaaaaa fafafafa a5a5a5a6 50505051)],
- [qw(ffffffff aaaaaaaa aaaaaaa9 55555555 00000000)],
- [qw(00000001 ffffffff 00000000 00000002 00000002)],
- [qw(00000001 fffffff0 fffffff1 00000011 00000002)],
- ];
- my $unit_test_data_16 = [
- # The implementation handles data in 7-nibble chunks, so those are the
- # interesting boundaries.
- [qw(aaaaaaaa 50505050
- 00_000000f_afafafa 00_0000005_a5a5a5a 00_000000a_aaaaaab)],
- [qw(50505050 aaaaaaaa
- 00_000000f_afafafa ff_ffffffa_5a5a5a6 00_0000005_0505051)],
- [qw(ffffffff aaaaaaaa
- 00_000001a_aaaaaa9 00_0000005_5555555 00_0000010_0000000)],
- [qw(00000001 ffffffff
- 00_0000010_0000000 ff_ffffff0_0000002 00_0000000_0000002)],
- [qw(00000001 fffffff0
- 00_000000f_ffffff1 ff_ffffff0_0000011 00_0000000_0000002)],
-
- [qw(00_a00000a_aaaaaaa 50505050
- 00_a00000f_afafafa 00_a000005_a5a5a5a 00_a00000a_aaaaaab)],
- [qw(0f_fff0005_0505050 aaaaaaaa
- 0f_fff000f_afafafa 0f_ffefffa_5a5a5a6 0f_fff0005_0505051)],
- [qw(00_000000f_fffffff 01_800000a_aaaaaaa
- 01_800001a_aaaaaa9 fe_8000005_5555555 00_0000010_0000000)],
- [qw(00_0000000_0000001 ff_fffffff_fffffff
- 00_0000000_0000000 00_0000000_0000002 00_0000000_0000002)],
- [qw(00_0000000_0000001 ff_fffffff_ffffff0
- ff_fffffff_ffffff1 00_0000000_0000011 00_0000000_0000002)],
- ];
-
- $error_count += AddressAddUnitTest($unit_test_data_8, $unit_test_data_16);
- $error_count += AddressSubUnitTest($unit_test_data_8, $unit_test_data_16);
- $error_count += AddressIncUnitTest($unit_test_data_8, $unit_test_data_16);
- if ($error_count > 0) {
- print STDERR $error_count, " errors: FAILED\n";
- } else {
- print STDERR "PASS\n";
- }
- exit ($error_count);
-}
diff --git a/src/cmd/prof/main.c b/src/cmd/prof/main.c
deleted file mode 100644
index f36759cd3..000000000
--- a/src/cmd/prof/main.c
+++ /dev/null
@@ -1,895 +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 <u.h>
-#include <time.h>
-#include <libc.h>
-#include <bio.h>
-#include <ctype.h>
-
-#define Ureg Ureg_amd64
- #include <ureg_amd64.h>
-#undef Ureg
-#define Ureg Ureg_x86
- #include <ureg_x86.h>
-#undef Ureg
-#include <mach.h>
-
-char* file = "6.out";
-static Fhdr fhdr;
-int have_syms;
-int fd;
-struct Ureg_amd64 ureg_amd64;
-struct Ureg_x86 ureg_x86;
-int total_sec = 0;
-int delta_msec = 100;
-int nsample;
-int nsamplethread;
-
-// pprof data, stored as sequences of N followed by N PC values.
-// See http://code.google.com/p/google-perftools .
-uvlong *ppdata; // traces
-Biobuf* pproffd; // file descriptor to write trace info
-long ppstart; // start position of current trace
-long nppdata; // length of data
-long ppalloc; // size of allocated data
-char ppmapdata[10*1024]; // the map information for the output file
-
-// output formats
-int pprof; // print pprof output to named file
-int functions; // print functions
-int histograms; // print histograms
-int linenums; // print file and line numbers rather than function names
-int registers; // print registers
-int stacks; // print stack traces
-
-int pid; // main process pid
-
-int nthread; // number of threads
-int thread[32]; // thread pids
-Map *map[32]; // thread maps
-
-void
-Usage(void)
-{
- fprint(2, "Usage: prof -p pid [-t total_secs] [-d delta_msec]\n");
- fprint(2, " prof [-t total_secs] [-d delta_msec] 6.out args ...\n");
- fprint(2, "\tformats (default -h):\n");
- fprint(2, "\t\t-P file.prof: write [c]pprof output to file.prof\n");
- fprint(2, "\t\t-h: histograms\n");
- fprint(2, "\t\t-f: dynamic functions\n");
- fprint(2, "\t\t-l: dynamic file and line numbers\n");
- fprint(2, "\t\t-r: dynamic registers\n");
- fprint(2, "\t\t-s: dynamic function stack traces\n");
- fprint(2, "\t\t-hs: include stack info in histograms\n");
- exit(2);
-}
-
-typedef struct PC PC;
-struct PC {
- uvlong pc;
- uvlong callerpc;
- unsigned int count;
- PC* next;
-};
-
-enum {
- Ncounters = 256
-};
-
-PC *counters[Ncounters];
-
-// Set up by setarch() to make most of the code architecture-independent.
-typedef struct Arch Arch;
-struct Arch {
- char* name;
- void (*regprint)(void);
- int (*getregs)(Map*);
- int (*getPC)(Map*);
- int (*getSP)(Map*);
- uvlong (*uregPC)(void);
- uvlong (*uregSP)(void);
- void (*ppword)(uvlong w);
-};
-
-void
-amd64_regprint(void)
-{
- fprint(2, "ax\t0x%llux\n", ureg_amd64.ax);
- fprint(2, "bx\t0x%llux\n", ureg_amd64.bx);
- fprint(2, "cx\t0x%llux\n", ureg_amd64.cx);
- fprint(2, "dx\t0x%llux\n", ureg_amd64.dx);
- fprint(2, "si\t0x%llux\n", ureg_amd64.si);
- fprint(2, "di\t0x%llux\n", ureg_amd64.di);
- fprint(2, "bp\t0x%llux\n", ureg_amd64.bp);
- fprint(2, "r8\t0x%llux\n", ureg_amd64.r8);
- fprint(2, "r9\t0x%llux\n", ureg_amd64.r9);
- fprint(2, "r10\t0x%llux\n", ureg_amd64.r10);
- fprint(2, "r11\t0x%llux\n", ureg_amd64.r11);
- fprint(2, "r12\t0x%llux\n", ureg_amd64.r12);
- fprint(2, "r13\t0x%llux\n", ureg_amd64.r13);
- fprint(2, "r14\t0x%llux\n", ureg_amd64.r14);
- fprint(2, "r15\t0x%llux\n", ureg_amd64.r15);
- fprint(2, "ds\t0x%llux\n", ureg_amd64.ds);
- fprint(2, "es\t0x%llux\n", ureg_amd64.es);
- fprint(2, "fs\t0x%llux\n", ureg_amd64.fs);
- fprint(2, "gs\t0x%llux\n", ureg_amd64.gs);
- fprint(2, "type\t0x%llux\n", ureg_amd64.type);
- fprint(2, "error\t0x%llux\n", ureg_amd64.error);
- fprint(2, "pc\t0x%llux\n", ureg_amd64.ip);
- fprint(2, "cs\t0x%llux\n", ureg_amd64.cs);
- fprint(2, "flags\t0x%llux\n", ureg_amd64.flags);
- fprint(2, "sp\t0x%llux\n", ureg_amd64.sp);
- fprint(2, "ss\t0x%llux\n", ureg_amd64.ss);
-}
-
-int
-amd64_getregs(Map *map)
-{
- int i;
- union {
- uvlong regs[1];
- struct Ureg_amd64 ureg;
- } u;
-
- for(i = 0; i < sizeof ureg_amd64; i+=8) {
- if(get8(map, (uvlong)i, &u.regs[i/8]) < 0)
- return -1;
- }
- ureg_amd64 = u.ureg;
- return 0;
-}
-
-int
-amd64_getPC(Map *map)
-{
- uvlong x;
- int r;
-
- r = get8(map, offsetof(struct Ureg_amd64, ip), &x);
- ureg_amd64.ip = x;
- return r;
-}
-
-int
-amd64_getSP(Map *map)
-{
- uvlong x;
- int r;
-
- r = get8(map, offsetof(struct Ureg_amd64, sp), &x);
- ureg_amd64.sp = x;
- return r;
-}
-
-uvlong
-amd64_uregPC(void)
-{
- return ureg_amd64.ip;
-}
-
-uvlong
-amd64_uregSP(void) {
- return ureg_amd64.sp;
-}
-
-void
-amd64_ppword(uvlong w)
-{
- uchar buf[8];
-
- buf[0] = w;
- buf[1] = w >> 8;
- buf[2] = w >> 16;
- buf[3] = w >> 24;
- buf[4] = w >> 32;
- buf[5] = w >> 40;
- buf[6] = w >> 48;
- buf[7] = w >> 56;
- Bwrite(pproffd, buf, 8);
-}
-
-void
-x86_regprint(void)
-{
- fprint(2, "ax\t0x%ux\n", ureg_x86.ax);
- fprint(2, "bx\t0x%ux\n", ureg_x86.bx);
- fprint(2, "cx\t0x%ux\n", ureg_x86.cx);
- fprint(2, "dx\t0x%ux\n", ureg_x86.dx);
- fprint(2, "si\t0x%ux\n", ureg_x86.si);
- fprint(2, "di\t0x%ux\n", ureg_x86.di);
- fprint(2, "bp\t0x%ux\n", ureg_x86.bp);
- fprint(2, "ds\t0x%ux\n", ureg_x86.ds);
- fprint(2, "es\t0x%ux\n", ureg_x86.es);
- fprint(2, "fs\t0x%ux\n", ureg_x86.fs);
- fprint(2, "gs\t0x%ux\n", ureg_x86.gs);
- fprint(2, "cs\t0x%ux\n", ureg_x86.cs);
- fprint(2, "flags\t0x%ux\n", ureg_x86.flags);
- fprint(2, "pc\t0x%ux\n", ureg_x86.pc);
- fprint(2, "sp\t0x%ux\n", ureg_x86.sp);
- fprint(2, "ss\t0x%ux\n", ureg_x86.ss);
-}
-
-int
-x86_getregs(Map *map)
-{
- int i;
-
- for(i = 0; i < sizeof ureg_x86; i+=4) {
- if(get4(map, (uvlong)i, &((uint32*)&ureg_x86)[i/4]) < 0)
- return -1;
- }
- return 0;
-}
-
-int
-x86_getPC(Map* map)
-{
- return get4(map, offsetof(struct Ureg_x86, pc), &ureg_x86.pc);
-}
-
-int
-x86_getSP(Map* map)
-{
- return get4(map, offsetof(struct Ureg_x86, sp), &ureg_x86.sp);
-}
-
-uvlong
-x86_uregPC(void)
-{
- return (uvlong)ureg_x86.pc;
-}
-
-uvlong
-x86_uregSP(void)
-{
- return (uvlong)ureg_x86.sp;
-}
-
-void
-x86_ppword(uvlong w)
-{
- uchar buf[4];
-
- buf[0] = w;
- buf[1] = w >> 8;
- buf[2] = w >> 16;
- buf[3] = w >> 24;
- Bwrite(pproffd, buf, 4);
-}
-
-Arch archtab[] = {
- {
- "amd64",
- amd64_regprint,
- amd64_getregs,
- amd64_getPC,
- amd64_getSP,
- amd64_uregPC,
- amd64_uregSP,
- amd64_ppword,
- },
- {
- "386",
- x86_regprint,
- x86_getregs,
- x86_getPC,
- x86_getSP,
- x86_uregPC,
- x86_uregSP,
- x86_ppword,
- },
- {
- nil
- }
-};
-
-Arch *arch;
-
-int
-setarch(void)
-{
- int i;
-
- if(mach != nil) {
- for(i = 0; archtab[i].name != nil; i++) {
- if (strcmp(mach->name, archtab[i].name) == 0) {
- arch = &archtab[i];
- return 0;
- }
- }
- }
- return -1;
-}
-
-int
-getthreads(void)
-{
- int i, j, curn, found;
- Map *curmap[nelem(map)];
- int curthread[nelem(map)];
- static int complained = 0;
-
- curn = procthreadpids(pid, curthread, nelem(curthread));
- if(curn <= 0)
- return curn;
-
- if(curn > nelem(map)) {
- if(complained == 0) {
- fprint(2, "prof: too many threads; limiting to %d\n", nthread, nelem(map));
- complained = 1;
- }
- curn = nelem(map);
- }
- if(curn == nthread && memcmp(thread, curthread, curn*sizeof(*thread)) == 0)
- return curn; // no changes
-
- // Number of threads has changed (might be the init case).
- // A bit expensive but rare enough not to bother being clever.
- for(i = 0; i < curn; i++) {
- found = 0;
- for(j = 0; j < nthread; j++) {
- if(curthread[i] == thread[j]) {
- found = 1;
- curmap[i] = map[j];
- map[j] = nil;
- break;
- }
- }
- if(found)
- continue;
-
- // map new thread
- curmap[i] = attachproc(curthread[i], &fhdr);
- if(curmap[i] == nil) {
- fprint(2, "prof: can't attach to %d: %r\n", curthread[i]);
- return -1;
- }
- }
-
- for(j = 0; j < nthread; j++)
- if(map[j] != nil)
- detachproc(map[j]);
-
- nthread = curn;
- memmove(thread, curthread, nthread*sizeof thread[0]);
- memmove(map, curmap, sizeof map);
- return nthread;
-}
-
-int
-sample(Map *map)
-{
- static int n;
-
- n++;
- if(registers) {
- if(arch->getregs(map) < 0)
- goto bad;
- } else {
- // we need only two registers
- if(arch->getPC(map) < 0)
- goto bad;
- if(arch->getSP(map) < 0)
- goto bad;
- }
- return 1;
-bad:
- if(n == 1)
- fprint(2, "prof: can't read registers: %r\n");
- return 0;
-}
-
-void
-addtohistogram(uvlong pc, uvlong callerpc, uvlong sp)
-{
- int h;
- PC *x;
-
- h = (pc + callerpc*101) % Ncounters;
- for(x = counters[h]; x != NULL; x = x->next) {
- if(x->pc == pc && x->callerpc == callerpc) {
- x->count++;
- return;
- }
- }
- x = malloc(sizeof(PC));
- x->pc = pc;
- x->callerpc = callerpc;
- x->count = 1;
- x->next = counters[h];
- counters[h] = x;
-}
-
-void
-addppword(uvlong pc)
-{
- if(pc == 0) {
- return;
- }
- if(nppdata == ppalloc) {
- ppalloc = (1000+nppdata)*2;
- ppdata = realloc(ppdata, ppalloc * sizeof ppdata[0]);
- if(ppdata == nil) {
- fprint(2, "prof: realloc failed: %r\n");
- exit(2);
- }
- }
- ppdata[nppdata++] = pc;
-}
-
-void
-startpptrace()
-{
- ppstart = nppdata;
- addppword(~0);
-}
-
-void
-endpptrace()
-{
- ppdata[ppstart] = nppdata-ppstart-1;
-}
-
-uvlong nextpc;
-
-void
-xptrace(Map *map, uvlong pc, uvlong sp, Symbol *sym)
-{
- char buf[1024];
- if(sym == nil){
- fprint(2, "syms\n");
- return;
- }
- if(histograms)
- addtohistogram(nextpc, pc, sp);
- if(!histograms || stacks > 1 || pprof) {
- if(nextpc == 0)
- nextpc = sym->value;
- if(stacks){
- fprint(2, "%s(", sym->name);
- fprint(2, ")");
- if(nextpc != sym->value)
- fprint(2, "+%#llux ", nextpc - sym->value);
- if(have_syms && linenums && fileline(buf, sizeof buf, pc)) {
- fprint(2, " %s", buf);
- }
- fprint(2, "\n");
- }
- if (pprof) {
- addppword(nextpc);
- }
- }
- nextpc = pc;
-}
-
-void
-stacktracepcsp(Map *map, uvlong pc, uvlong sp)
-{
- nextpc = pc;
- if(pprof){
- startpptrace();
- }
- if(machdata->ctrace==nil)
- fprint(2, "no machdata->ctrace\n");
- else if(machdata->ctrace(map, pc, sp, 0, xptrace) <= 0)
- fprint(2, "no stack frame: pc=%#p sp=%#p\n", pc, sp);
- else {
- addtohistogram(nextpc, 0, sp);
- if(stacks)
- fprint(2, "\n");
- }
- if(pprof){
- endpptrace();
- }
-}
-
-void
-printpc(Map *map, uvlong pc, uvlong sp)
-{
- char buf[1024];
- if(registers)
- arch->regprint();
- if(have_syms > 0 && linenums && fileline(buf, sizeof buf, pc))
- fprint(2, "%s\n", buf);
- if(have_syms > 0 && functions) {
- symoff(buf, sizeof(buf), pc, CANY);
- fprint(2, "%s\n", buf);
- }
- if(stacks || pprof){
- stacktracepcsp(map, pc, sp);
- }
- else if(histograms){
- addtohistogram(pc, 0, sp);
- }
-}
-
-void
-ppmaps(void)
-{
- int fd, n;
- char tmp[100];
- Seg *seg;
-
- // If it's Linux, the info is in /proc/$pid/maps
- snprint(tmp, sizeof tmp, "/proc/%d/maps", pid);
- fd = open(tmp, 0);
- if(fd >= 0) {
- n = read(fd, ppmapdata, sizeof ppmapdata - 1);
- close(fd);
- if(n < 0) {
- fprint(2, "prof: can't read %s: %r\n", tmp);
- exit(2);
- }
- ppmapdata[n] = 0;
- return;
- }
-
- // It's probably a mac. Synthesize an entry for the text file.
- // The register segment may come first but it has a zero offset, so grab the first non-zero offset segment.
- for(n = 0; n < 3; n++){
- seg = &map[0]->seg[n];
- if(seg->b == 0) {
- continue;
- }
- snprint(ppmapdata, sizeof ppmapdata,
- "%.16x-%.16x r-xp %d 00:00 34968549 %s\n",
- seg->b, seg->e, seg->f, "/home/r/6.out"
- );
- return;
- }
- fprint(2, "prof: no text segment in maps for %s\n", file);
- exit(2);
-}
-
-void
-samples(void)
-{
- int i, pid, msec;
- struct timespec req;
- int getmaps;
-
- req.tv_sec = delta_msec/1000;
- req.tv_nsec = 1000000*(delta_msec % 1000);
- getmaps = 0;
- if(pprof)
- getmaps= 1;
- for(msec = 0; total_sec <= 0 || msec < 1000*total_sec; msec += delta_msec) {
- nsample++;
- nsamplethread += nthread;
- for(i = 0; i < nthread; i++) {
- pid = thread[i];
- if(ctlproc(pid, "stop") < 0)
- return;
- if(!sample(map[i])) {
- ctlproc(pid, "start");
- return;
- }
- printpc(map[i], arch->uregPC(), arch->uregSP());
- ctlproc(pid, "start");
- }
- nanosleep(&req, NULL);
- getthreads();
- if(nthread == 0)
- break;
- if(getmaps) {
- getmaps = 0;
- ppmaps();
- }
- }
-}
-
-typedef struct Func Func;
-struct Func
-{
- Func *next;
- Symbol s;
- uint onstack;
- uint leaf;
-};
-
-Func *func[257];
-int nfunc;
-
-Func*
-findfunc(uvlong pc)
-{
- Func *f;
- uint h;
- Symbol s;
-
- if(pc == 0)
- return nil;
-
- if(!findsym(pc, CTEXT, &s))
- return nil;
-
- h = s.value % nelem(func);
- for(f = func[h]; f != NULL; f = f->next)
- if(f->s.value == s.value)
- return f;
-
- f = malloc(sizeof *f);
- memset(f, 0, sizeof *f);
- f->s = s;
- f->next = func[h];
- func[h] = f;
- nfunc++;
- return f;
-}
-
-int
-compareleaf(const void *va, const void *vb)
-{
- Func *a, *b;
-
- a = *(Func**)va;
- b = *(Func**)vb;
- if(a->leaf != b->leaf)
- return b->leaf - a->leaf;
- if(a->onstack != b->onstack)
- return b->onstack - a->onstack;
- return strcmp(a->s.name, b->s.name);
-}
-
-void
-dumphistogram()
-{
- int i, h, n;
- PC *x;
- Func *f, **ff;
-
- if(!histograms)
- return;
-
- // assign counts to functions.
- for(h = 0; h < Ncounters; h++) {
- for(x = counters[h]; x != NULL; x = x->next) {
- f = findfunc(x->pc);
- if(f) {
- f->onstack += x->count;
- f->leaf += x->count;
- }
- f = findfunc(x->callerpc);
- if(f)
- f->leaf -= x->count;
- }
- }
-
- // build array
- ff = malloc(nfunc*sizeof ff[0]);
- n = 0;
- for(h = 0; h < nelem(func); h++)
- for(f = func[h]; f != NULL; f = f->next)
- ff[n++] = f;
-
- // sort by leaf counts
- qsort(ff, nfunc, sizeof ff[0], compareleaf);
-
- // print.
- fprint(2, "%d samples (avg %.1g threads)\n", nsample, (double)nsamplethread/nsample);
- for(i = 0; i < nfunc; i++) {
- f = ff[i];
- fprint(2, "%6.2f%%\t", 100.0*(double)f->leaf/nsample);
- if(stacks)
- fprint(2, "%6.2f%%\t", 100.0*(double)f->onstack/nsample);
- fprint(2, "%s\n", f->s.name);
- }
-}
-
-typedef struct Trace Trace;
-struct Trace {
- int count;
- int npc;
- uvlong *pc;
- Trace *next;
-};
-
-void
-dumppprof()
-{
- uvlong i, n, *p, *e;
- int ntrace;
- Trace *trace, *tp, *up, *prev;
-
- if(!pprof)
- return;
- e = ppdata + nppdata;
- // Create list of traces. First, count the traces
- ntrace = 0;
- for(p = ppdata; p < e;) {
- n = *p++;
- p += n;
- if(n == 0)
- continue;
- ntrace++;
- }
- if(ntrace <= 0)
- return;
- // Allocate and link the traces together.
- trace = malloc(ntrace * sizeof(Trace));
- tp = trace;
- for(p = ppdata; p < e;) {
- n = *p++;
- if(n == 0)
- continue;
- tp->count = 1;
- tp->npc = n;
- tp->pc = p;
- tp->next = tp+1;
- tp++;
- p += n;
- }
- trace[ntrace-1].next = nil;
- // Eliminate duplicates. Lousy algorithm, although not as bad as it looks because
- // the list collapses fast.
- for(tp = trace; tp != nil; tp = tp->next) {
- prev = tp;
- for(up = tp->next; up != nil; up = up->next) {
- if(up->npc == tp->npc && memcmp(up->pc, tp->pc, up->npc*sizeof up->pc[0]) == 0) {
- tp->count++;
- prev->next = up->next;
- } else {
- prev = up;
- }
- }
- }
- // Write file.
- // See http://code.google.com/p/google-perftools/source/browse/trunk/doc/cpuprofile-fileformat.html
- // 1) Header
- arch->ppword(0); // must be zero
- arch->ppword(3); // 3 words follow in header
- arch->ppword(0); // must be zero
- arch->ppword(delta_msec * 1000); // sampling period in microseconds
- arch->ppword(0); // must be zero (padding)
- // 2) One record for each trace.
- for(tp = trace; tp != nil; tp = tp->next) {
- arch->ppword(tp->count);
- arch->ppword(tp->npc);
- for(i = 0; i < tp->npc; i++) {
- arch->ppword(tp->pc[i]);
- }
- }
- // 3) Binary trailer
- arch->ppword(0); // must be zero
- arch->ppword(1); // must be one
- arch->ppword(0); // must be zero
- // 4) Mapped objects.
- Bwrite(pproffd, ppmapdata, strlen(ppmapdata));
- // 5) That's it.
- Bterm(pproffd);
-}
-
-int
-startprocess(char **argv)
-{
- int pid;
-
- if((pid = fork()) == 0) {
- pid = getpid();
- if(ctlproc(pid, "hang") < 0){
- fprint(2, "prof: child process could not hang\n");
- exits(0);
- }
- execv(argv[0], argv);
- fprint(2, "prof: could not exec %s: %r\n", argv[0]);
- exits(0);
- }
-
- if(pid == -1) {
- fprint(2, "prof: could not fork\n");
- exit(1);
- }
- if(ctlproc(pid, "attached") < 0 || ctlproc(pid, "waitstop") < 0) {
- fprint(2, "prof: could not attach to child process: %r\n");
- exit(1);
- }
- return pid;
-}
-
-void
-detach(void)
-{
- int i;
-
- for(i = 0; i < nthread; i++)
- detachproc(map[i]);
-}
-
-int
-main(int argc, char *argv[])
-{
- int i;
- char *ppfile;
-
- ARGBEGIN{
- case 'P':
- pprof =1;
- ppfile = EARGF(Usage());
- pproffd = Bopen(ppfile, OWRITE);
- if(pproffd == nil) {
- fprint(2, "prof: cannot open %s: %r\n", ppfile);
- exit(2);
- }
- break;
- case 'd':
- delta_msec = atoi(EARGF(Usage()));
- break;
- case 't':
- total_sec = atoi(EARGF(Usage()));
- break;
- case 'p':
- pid = atoi(EARGF(Usage()));
- break;
- case 'f':
- functions = 1;
- break;
- case 'h':
- histograms = 1;
- break;
- case 'l':
- linenums = 1;
- break;
- case 'r':
- registers = 1;
- break;
- case 's':
- stacks++;
- break;
- default:
- Usage();
- }ARGEND
- if(pid <= 0 && argc == 0)
- Usage();
- if(functions+linenums+registers+stacks+pprof == 0)
- histograms = 1;
- if(!machbyname("amd64")) {
- fprint(2, "prof: no amd64 support\n", pid);
- exit(1);
- }
- if(argc > 0)
- file = argv[0];
- else if(pid) {
- file = proctextfile(pid);
- if (file == NULL) {
- fprint(2, "prof: can't find file for pid %d: %r\n", pid);
- fprint(2, "prof: on Darwin, need to provide file name explicitly\n");
- exit(1);
- }
- }
- fd = open(file, 0);
- if(fd < 0) {
- fprint(2, "prof: can't open %s: %r\n", file);
- exit(1);
- }
- if(crackhdr(fd, &fhdr)) {
- have_syms = syminit(fd, &fhdr);
- if(!have_syms) {
- fprint(2, "prof: no symbols for %s: %r\n", file);
- }
- } else {
- fprint(2, "prof: crack header for %s: %r\n", file);
- exit(1);
- }
- if(pid <= 0)
- pid = startprocess(argv);
- attachproc(pid, &fhdr); // initializes thread list
- if(setarch() < 0) {
- detach();
- fprint(2, "prof: can't identify binary architecture for pid %d\n", pid);
- exit(1);
- }
- if(getthreads() <= 0) {
- detach();
- fprint(2, "prof: can't find threads for pid %d\n", pid);
- exit(1);
- }
- for(i = 0; i < nthread; i++)
- ctlproc(thread[i], "start");
- samples();
- detach();
- dumphistogram();
- dumppprof();
- exit(0);
-}
diff --git a/src/cmd/gotest/Makefile b/src/cmd/vet/Makefile
index 5c1154537..2a35d1ae3 100644
--- a/src/cmd/gotest/Makefile
+++ b/src/cmd/vet/Makefile
@@ -2,11 +2,6 @@
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
-include ../../Make.inc
-
-TARG=gotest
-GOFILES=\
- flag.go\
- gotest.go\
-
-include ../../Make.cmd
+test testshort:
+ go build
+ ../../../test/errchk ./vet -printfuncs='Warn:1,Warnf:1' print.go
diff --git a/src/cmd/govet/doc.go b/src/cmd/vet/doc.go
index 5a2489fca..620964aaf 100644
--- a/src/cmd/govet/doc.go
+++ b/src/cmd/vet/doc.go
@@ -4,22 +4,46 @@
/*
-Govet does simple checking of Go source code.
+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.
-It checks for simple errors in calls to functions named
+Available checks:
+
+1. Printf family
+
+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, govet
+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
+
+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
+
+Struct tags that do not follow the format understood by reflect.StructTag.Get.
+
Usage:
- govet [flag] [file.go ...]
- govet [flag] [directory ...] # Scan all .go files under directory, recursively
+ go tool vet [flag] [file.go ...]
+ go tool vet [flag] [directory ...] # Scan all .go files under directory, recursively
The flags are:
-v
diff --git a/src/cmd/vet/main.go b/src/cmd/vet/main.go
new file mode 100644
index 000000000..625133315
--- /dev/null
+++ b/src/cmd/vet/main.go
@@ -0,0 +1,239 @@
+// 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/parser"
+ "go/token"
+ "io"
+ "os"
+ "path/filepath"
+ "strconv"
+ "strings"
+)
+
+var verbose = flag.Bool("v", false, "verbose")
+var exitCode = 0
+
+// 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])
+ 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 {
+ fset *token.FileSet
+ file *ast.File
+ b bytes.Buffer // for use by methods
+}
+
+func main() {
+ flag.Usage = Usage
+ flag.Parse()
+
+ 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 {
+ doFile("stdin", os.Stdin)
+ } else {
+ for _, name := range flag.Args() {
+ // Is it a directory?
+ if fi, err := os.Stat(name); err == nil && fi.IsDir() {
+ walkDir(name)
+ } else {
+ doFile(name, nil)
+ }
+ }
+ }
+ os.Exit(exitCode)
+}
+
+// doFile analyzes one file. If the reader is nil, the source code is read from the
+// named file.
+func doFile(name string, reader io.Reader) {
+ fs := token.NewFileSet()
+ parsedFile, err := parser.ParseFile(fs, name, reader, 0)
+ if err != nil {
+ errorf("%s: %s", name, err)
+ return
+ }
+ file := &File{fset: fs, file: parsedFile}
+ file.walkFile(name, parsedFile)
+}
+
+func visit(path string, f os.FileInfo, err error) error {
+ if err != nil {
+ errorf("walk error: %s", err)
+ return nil
+ }
+ if !f.IsDir() && strings.HasSuffix(path, ".go") {
+ doFile(path, nil)
+ }
+ return nil
+}
+
+// walkDir recursively walks the tree looking for .go files.
+func walkDir(root string) {
+ filepath.Walk(root, visit)
+}
+
+// error formats the error to standard error, adding program
+// identification and a newline
+func errorf(format string, args ...interface{}) {
+ fmt.Fprintf(os.Stderr, "vet: "+format+"\n", args...)
+ setExit(2)
+}
+
+// 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)
+}
+
+// Warn reports an error but does not set the exit code.
+func (f *File) Warn(pos token.Pos, args ...interface{}) {
+ loc := f.fset.Position(pos).String() + ": "
+ fmt.Fprint(os.Stderr, loc+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{}) {
+ loc := f.fset.Position(pos).String() + ": "
+ fmt.Fprintf(os.Stderr, loc+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.CallExpr:
+ f.walkCallExpr(n)
+ case *ast.CompositeLit:
+ f.walkCompositeLit(n)
+ case *ast.Field:
+ f.walkFieldTag(n)
+ case *ast.FuncDecl:
+ f.walkMethodDecl(n)
+ case *ast.InterfaceType:
+ f.walkInterfaceType(n)
+ }
+ return f
+}
+
+// walkCall walks a call expression.
+func (f *File) walkCall(call *ast.CallExpr, name string) {
+ f.checkFmtPrintfCall(call, 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)
+}
+
+// walkMethodDecl walks the method's signature.
+func (f *File) walkMethod(id *ast.Ident, t *ast.FuncType) {
+ f.checkCanonicalMethod(id, t)
+}
+
+// walkMethodDecl walks the method signature in the declaration.
+func (f *File) walkMethodDecl(d *ast.FuncDecl) {
+ if d.Recv == nil {
+ // not a method
+ return
+ }
+ f.walkMethod(d.Name, d.Type)
+}
+
+// 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))
+ }
+ }
+}
+
+// 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)
+ }
+}
diff --git a/src/cmd/vet/method.go b/src/cmd/vet/method.go
new file mode 100644
index 000000000..41cb40ff9
--- /dev/null
+++ b/src/cmd/vet/method.go
@@ -0,0 +1,161 @@
+// 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) {
+ // 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()
+ if strings.HasPrefix(actual, "func(") {
+ actual = actual[4:]
+ }
+ actual = id.Name + actual
+
+ f.Warnf(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/govet/govet.go b/src/cmd/vet/print.go
index 98d3d5c17..ee9a33c70 100644
--- a/src/cmd/govet/govet.go
+++ b/src/cmd/vet/print.go
@@ -2,229 +2,20 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Govet is a simple checker for static errors in Go source code.
-// See doc.go for more information.
+// This file contains the printf-checker.
+
package main
import (
"flag"
"fmt"
- "io"
"go/ast"
- "go/parser"
"go/token"
- "os"
- "path/filepath"
- "reflect"
- "strconv"
"strings"
- "utf8"
+ "unicode/utf8"
)
-var verbose = flag.Bool("v", false, "verbose")
var printfuncs = flag.String("printfuncs", "", "comma-separated list of print function names to check")
-var exitCode = 0
-
-// 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])
- 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 {
- file *token.File
-}
-
-func main() {
- flag.Usage = Usage
- flag.Parse()
-
- 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 os.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 {
- doFile("stdin", os.Stdin)
- } else {
- for _, name := range flag.Args() {
- // Is it a directory?
- if fi, err := os.Stat(name); err == nil && fi.IsDirectory() {
- walkDir(name)
- } else {
- doFile(name, nil)
- }
- }
- }
- os.Exit(exitCode)
-}
-
-// doFile analyzes one file. If the reader is nil, the source code is read from the
-// named file.
-func doFile(name string, reader io.Reader) {
- fs := token.NewFileSet()
- parsedFile, err := parser.ParseFile(fs, name, reader, 0)
- if err != nil {
- errorf("%s: %s", name, err)
- return
- }
- file := &File{fs.File(parsedFile.Pos())}
- file.checkFile(name, parsedFile)
-}
-
-// Visitor for filepath.Walk - trivial. Just calls doFile on each file.
-// TODO: if govet becomes richer, might want to process
-// a directory (package) at a time.
-type V struct{}
-
-func (v V) VisitDir(path string, f *os.FileInfo) bool {
- return true
-}
-
-func (v V) VisitFile(path string, f *os.FileInfo) {
- if strings.HasSuffix(path, ".go") {
- doFile(path, nil)
- }
-}
-
-// walkDir recursively walks the tree looking for .go files.
-func walkDir(root string) {
- errors := make(chan os.Error)
- done := make(chan bool)
- go func() {
- for e := range errors {
- errorf("walk error: %s", e)
- }
- done <- true
- }()
- filepath.Walk(root, V{}, errors)
- close(errors)
- <-done
-}
-
-// error formats the error to standard error, adding program
-// identification and a newline
-func errorf(format string, args ...interface{}) {
- fmt.Fprintf(os.Stderr, "govet: "+format+"\n", args...)
- setExit(2)
-}
-
-// 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)
-}
-
-// Warn reports an error but does not set the exit code.
-func (f *File) Warn(pos token.Pos, args ...interface{}) {
- loc := f.file.Position(pos).String() + ": "
- fmt.Fprint(os.Stderr, loc+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{}) {
- loc := f.file.Position(pos).String() + ": "
- fmt.Fprintf(os.Stderr, loc+format+"\n", args...)
-}
-
-// checkFile checks all the top-level declarations in a file.
-func (f *File) checkFile(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.CallExpr:
- f.checkCallExpr(n)
- case *ast.Field:
- f.checkFieldTag(n)
- }
- return f
-}
-
-// checkField checks a struct field tag.
-func (f *File) checkFieldTag(field *ast.Field) {
- if field.Tag == nil {
- return
- }
-
- tag, err := strconv.Unquote(field.Tag.Value)
- if err != nil {
- f.Warnf(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.Warnf(field.Pos(), "struct field tag %s not compatible with reflect.StructTag.Get", field.Tag.Value)
- return
- }
-}
-
-// checkCallExpr checks a call expression.
-func (f *File) checkCallExpr(call *ast.CallExpr) {
- switch x := call.Fun.(type) {
- case *ast.Ident:
- f.checkCall(call, x.Name)
- case *ast.SelectorExpr:
- f.checkCall(call, x.Sel.Name)
- }
-}
// printfList records the formatted-print functions. The value is the location
// of the format parameter. Names are lower-cased so the lookup is
@@ -251,7 +42,7 @@ var printList = map[string]int{
}
// checkCall triggers the print-specific checks if the call invokes a print function.
-func (f *File) checkCall(call *ast.CallExpr, Name string) {
+func (f *File) checkFmtPrintfCall(call *ast.CallExpr, Name string) {
name := strings.ToLower(Name)
if skip, ok := printfList[name]; ok {
f.checkPrintf(call, Name, skip)
@@ -276,7 +67,7 @@ func (f *File) checkPrintf(call *ast.CallExpr, name string, skip int) {
if !ok {
// Too hard to check.
if *verbose {
- f.Warn(call.Pos(), "can't check args for call to", name)
+ f.Warn(call.Pos(), "can't check non-literal format in call to", name)
}
return
}
@@ -294,7 +85,7 @@ func (f *File) checkPrintf(call *ast.CallExpr, name string, skip int) {
for i, w := 0, 0; i < len(lit.Value); i += w {
w = 1
if lit.Value[i] == '%' {
- nbytes, nargs := parsePrintfVerb(lit.Value[i:])
+ nbytes, nargs := f.parsePrintfVerb(call, lit.Value[i:])
w = nbytes
numArgs += nargs
}
@@ -308,8 +99,9 @@ func (f *File) checkPrintf(call *ast.CallExpr, name string, skip int) {
// parsePrintfVerb returns the number of bytes and number of arguments
// consumed by the Printf directive that begins s, including its percent sign
// and verb.
-func parsePrintfVerb(s string) (nbytes, nargs int) {
+func (f *File) parsePrintfVerb(call *ast.CallExpr, s string) (nbytes, nargs int) {
// There's guaranteed a percent sign.
+ flags := make([]byte, 0, 5)
nbytes = 1
end := len(s)
// There may be flags.
@@ -317,6 +109,7 @@ FlagLoop:
for nbytes < end {
switch s[nbytes] {
case '#', '0', '+', '-', ' ':
+ flags = append(flags, s[nbytes])
nbytes++
default:
break FlagLoop
@@ -336,6 +129,7 @@ FlagLoop:
getNum()
// If there's a period, there may be a precision.
if nbytes < end && s[nbytes] == '.' {
+ flags = append(flags, '.') // Treat precision as a flag.
nbytes++
getNum()
}
@@ -344,16 +138,87 @@ FlagLoop:
nbytes += w
if c != '%' {
nargs++
+ f.checkPrintfVerb(call, c, flags)
}
return
}
+type printVerb struct {
+ verb rune
+ flags string // known flags are all ASCII
+}
+
+// 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},
+ {'c', "-"},
+ {'d', numFlag},
+ {'e', numFlag},
+ {'E', numFlag},
+ {'f', numFlag},
+ {'F', numFlag},
+ {'g', numFlag},
+ {'G', numFlag},
+ {'o', sharpNumFlag},
+ {'p', "-#"},
+ {'q', "-+#."},
+ {'s', "-."},
+ {'t', "-"},
+ {'T', "-"},
+ {'U', "-#"},
+ {'v', allFlags},
+ {'x', sharpNumFlag},
+ {'X', sharpNumFlag},
+}
+
+const printfVerbs = "bcdeEfFgGopqstTvxUX"
+
+func (f *File) checkPrintfVerb(call *ast.CallExpr, verb rune, flags []byte) {
+ // 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
+ }
+ }
+ f.Badf(call.Pos(), "unrecognized printf verb %q", verb)
+}
+
// checkPrint checks a call to an unformatted print routine such as Println.
// The skip argument records how many arguments to ignore; that is,
// call.Args[skip] is the first argument to be printed.
func (f *File) checkPrint(call *ast.CallExpr, name string, skip int) {
isLn := strings.HasSuffix(name, "ln")
+ isF := strings.HasPrefix(name, "F")
args := call.Args
+ // check for Println(os.Stderr, ...)
+ if skip == 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.Warnf(call.Pos(), "first argument to %s is %s.%s", name, x.Name, sel.Sel.Name)
+ }
+ }
+ }
+ }
if len(args) <= skip {
if *verbose && !isLn {
f.Badf(call.Pos(), "no args in %s call", name)
@@ -392,12 +257,21 @@ func BadFunctionUsedInTests() {
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 in Warnf call"
+ f.Warnf(0, "%r", "hello") // ERROR "unrecognized printf verb"
+ f.Warnf(0, "%#s", "hello") // ERROR "unrecognized printf flag"
}
type BadTypeUsedInTests struct {
X int "hello" // ERROR "struct field tag"
}
+func (t *BadTypeUsedInTests) Scan(x fmt.ScanState, c byte) { // ERROR "method Scan[(]x fmt.ScanState, c byte[)] should have signature Scan[(]fmt.ScanState, rune[)] error"
+}
+
+type BadInterfaceUsedInTests interface {
+ ReadByte() byte // ERROR "method ReadByte[(][)] byte should have signature ReadByte[(][)] [(]byte, error[)]"
+}
+
// printf is used by the test.
func printf(format string, args ...interface{}) {
panic("don't call - testing only")
diff --git a/src/cmd/vet/structtag.go b/src/cmd/vet/structtag.go
new file mode 100644
index 000000000..ea2a9d863
--- /dev/null
+++ b/src/cmd/vet/structtag.go
@@ -0,0 +1,34 @@
+// 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 field.Tag == nil {
+ return
+ }
+
+ tag, err := strconv.Unquote(field.Tag.Value)
+ if err != nil {
+ f.Warnf(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.Warnf(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
new file mode 100644
index 000000000..c3c4f3234
--- /dev/null
+++ b/src/cmd/vet/taglit.go
@@ -0,0 +1,121 @@
+// 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 (
+ "go/ast"
+ "strings"
+)
+
+// checkUntaggedLiteral checks if a composite literal is an struct literal with
+// untagged fields.
+func (f *File) checkUntaggedLiteral(c *ast.CompositeLit) {
+ // 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.Warnf(c.Pos(), "unresolvable package for %s.%s literal", pkg.Name, s.Sel.Name)
+ return
+ }
+ typ := path + "." + s.Sel.Name
+ if untaggedLiteralWhitelist[typ] {
+ return
+ }
+
+ f.Warnf(c.Pos(), "%s struct literal uses untagged fields", typ)
+}
+
+// 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,
+ "exp/norm.Decomposition": true,
+ "exp/types.ObjList": 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/cgo/Makefile b/src/cmd/yacc/Makefile
index 5458c3e4f..56e954289 100644
--- a/src/cmd/cgo/Makefile
+++ b/src/cmd/yacc/Makefile
@@ -2,14 +2,9 @@
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
-include ../../Make.inc
+units: yacc.go units.y
+ go run yacc.go -p units_ units.y
+ go build -o units y.go
-TARG=cgo
-GOFILES=\
- ast.go\
- gcc.go\
- main.go\
- out.go\
- util.go\
-
-include ../../Make.cmd
+clean:
+ rm -f y.go y.output units
diff --git a/src/cmd/goyacc/doc.go b/src/cmd/yacc/doc.go
index 5dd6abe69..4a2c2a314 100644
--- a/src/cmd/goyacc/doc.go
+++ b/src/cmd/yacc/doc.go
@@ -4,16 +4,21 @@
/*
-Goyacc is a version of yacc for Go.
+Yacc is a version of yacc for Go.
It is written in Go and generates parsers written in Go.
+Usage:
+
+ go tool yacc args...
+
It is largely transliterated from the Inferno version written in Limbo
which in turn was largely transliterated from the Plan 9 version
written in C and documented at
http://plan9.bell-labs.com/magic/man2html/1/yacc
-Yacc adepts will have no trouble adapting to this form of the tool.
+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
@@ -37,9 +42,9 @@ which holds the yyLexer passed to Parse.
Multiple grammars compiled into a single program should be placed in
distinct packages. If that is impossible, the "-p prefix" flag to
-goyacc sets the prefix, by default yy, that begins the names of
+yacc sets the prefix, by default yy, that begins the names of
symbols, including types, the parser, and the lexer, generated and
-referenced by goyacc's generated code. Setting it to distinct values
+referenced by yacc's generated code. Setting it to distinct values
allows multiple grammars to be placed in a single package.
*/
diff --git a/src/cmd/goyacc/units.txt b/src/cmd/yacc/units.txt
index ddb2bc294..ddb2bc294 100644
--- a/src/cmd/goyacc/units.txt
+++ b/src/cmd/yacc/units.txt
diff --git a/src/cmd/goyacc/units.y b/src/cmd/yacc/units.y
index d9ef663d9..7258e3e59 100644
--- a/src/cmd/goyacc/units.y
+++ b/src/cmd/yacc/units.y
@@ -7,17 +7,16 @@
// See http://plan9.bell-labs.com/plan9/license.html
// Generate parser with prefix "units_":
-// goyacc -p "units_"
+// go tool yacc -p "units_"
%{
// units.y
-// example of a goyacc program
+// example of a Go yacc program
// usage is
-// goyacc units.y (produces y.go)
-// 6g y.go
-// 6l y.6
-// ./6.out $GOROOT/src/cmd/goyacc/units
+// 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
@@ -33,7 +32,7 @@ import (
"os"
"math"
"strconv"
- "utf8"
+ "unicode/utf8"
)
const (
@@ -58,21 +57,19 @@ 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 int // backup runt from input
+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
+%union {
+ node Node
+ vvar *Var
+ numb int
+ vval float64
}
%type <node> prog expr expr0 expr1 expr2 expr3 expr4
@@ -85,7 +82,6 @@ prog:
':' VAR expr
{
var f int
-
f = int($2.node.dim[0])
$2.node = $3
$2.node.dim[0] = 1
@@ -98,26 +94,23 @@ prog:
| ':' VAR '#'
{
var f, i int
-
- for i=1; i<Ndim; i++ {
+ for i = 1; i < Ndim; i++ {
if fund[i] == nil {
break
}
}
if i >= Ndim {
Error("too many dimensions")
- i = Ndim-1
+ 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 {
+ } else if vflag {
fmt.Printf("%v\t#\n", $2.name)
}
}
@@ -171,8 +164,7 @@ expr2:
| expr2 '^' expr1
{
var i int
-
- for i=1; i<Ndim; i++ {
+ for i = 1; i < Ndim; i++ {
if $3.dim[i] != 0 {
Error("exponent has units")
$$ = $1
@@ -219,7 +211,8 @@ expr0:
type UnitsLex int
func (UnitsLex) Lex(yylval *units_SymType) int {
- var c, i int
+ var c rune
+ var i int
c = peekrune
peekrune = ' '
@@ -249,7 +242,7 @@ loop:
yylval.numb = 3
return SUP
}
- return c
+ return int(c)
alpha:
sym = ""
@@ -274,7 +267,7 @@ numb:
}
}
peekrune = c
- f, err := strconv.Atof64(sym)
+ f, err := strconv.ParseFloat(sym, 64)
if err != nil {
fmt.Printf("error converting %v\n", sym)
f = 0
@@ -294,9 +287,14 @@ func main() {
flag.Parse()
- file = os.Getenv("GOROOT") + "/src/cmd/goyacc/units.txt"
+ if dir := os.Getenv("GOROOT"); dir != "" {
+ file = dir + "/src/cmd/yacc/units.txt"
+ }
if flag.NArg() > 0 {
file = flag.Arg(0)
+ } else if file == "" {
+ fmt.Fprintf(os.Stderr, "can not find data file units.txt; provide it as argument or set $GOROOT\n")
+ os.Exit(1)
}
f, err := os.Open(file)
@@ -369,7 +367,7 @@ func main() {
* all characters that have some
* meaning. rest are usable as names
*/
-func ralpha(c int) bool {
+func ralpha(c rune) bool {
switch c {
case 0, '+', '-', '*', '/', '[', ']', '(', ')',
'^', ':', '?', ' ', '\t', '.', '|', '#',
@@ -382,7 +380,7 @@ func ralpha(c int) bool {
/*
* number forming character
*/
-func rdigit(c int) bool {
+func rdigit(c rune) bool {
switch c {
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'.', 'e', '+', '-':
@@ -584,8 +582,9 @@ func readline() bool {
return false
}
-func getrune() int {
- var c, n int
+func getrune() rune {
+ var c rune
+ var n int
if linep >= len(line) {
return 0
@@ -690,7 +689,6 @@ func pname() float64 {
return 0
}
-
// careful multiplication
// exponents (log) are checked before multiply
func fmul(a, b float64) float64 {
diff --git a/src/cmd/goyacc/goyacc.go b/src/cmd/yacc/yacc.go
index 481540188..e94228152 100644
--- a/src/cmd/goyacc/goyacc.go
+++ b/src/cmd/yacc/yacc.go
@@ -45,12 +45,12 @@ package main
//
import (
+ "bufio"
+ "bytes"
"flag"
"fmt"
- "bufio"
"os"
"strings"
- "bytes"
)
// the following are adjustable
@@ -506,19 +506,20 @@ outer:
// non-literals
c := tokset[i].name[0]
if c != ' ' && c != '$' {
- fmt.Fprintf(ftable, "const\t%v\t= %v\n", tokset[i].name, tokset[i].value)
+ fmt.Fprintf(ftable, "const %v = %v\n", tokset[i].name, tokset[i].value)
}
}
// put out names of token names
- fmt.Fprintf(ftable, "var\t%sToknames\t =[]string {\n", prefix)
+ ftable.WriteRune('\n')
+ fmt.Fprintf(ftable, "var %sToknames = []string{\n", prefix)
for i := TOKSTART; i <= ntokens; i++ {
fmt.Fprintf(ftable, "\t\"%v\",\n", tokset[i].name)
}
fmt.Fprintf(ftable, "}\n")
// put out names of state names
- fmt.Fprintf(ftable, "var\t%sStatenames\t =[]string {\n", prefix)
+ fmt.Fprintf(ftable, "var %sStatenames = []string{", prefix)
// for i:=TOKSTART; i<=ntokens; i++ {
// fmt.Fprintf(ftable, "\t\"%v\",\n", tokset[i].name);
// }
@@ -544,7 +545,7 @@ outer:
// put into prdptr array in the format
// target
// followed by id's of terminals and non-terminals
- // followd by -nprod
+ // followed by -nprod
for t != MARK && t != ENDFILE {
mem := 0
@@ -595,7 +596,7 @@ outer:
break
}
levprd[nprod] |= ACTFLAG
- fmt.Fprintf(fcode, "\ncase %v:", nprod)
+ fmt.Fprintf(fcode, "\n\tcase %v:", nprod)
cpyact(curprod, mem)
// action within rule...
@@ -652,8 +653,8 @@ outer:
if tempty != nontrst[curprod[0]-NTBASE].value {
errorf("default action causes potential type clash")
}
- fmt.Fprintf(fcode, "\ncase %v:", nprod)
- fmt.Fprintf(fcode, "\n\t%sVAL.%v = %sS[%spt-0].%v;",
+ fmt.Fprintf(fcode, "\n\tcase %v:", nprod)
+ fmt.Fprintf(fcode, "\n\t\t%sVAL.%v = %sS[%spt-0].%v",
prefix, typeset[tempty], prefix, prefix, typeset[tempty])
}
moreprod()
@@ -671,9 +672,10 @@ outer:
fmt.Fprintf(fcode, "\n\t}")
- fmt.Fprintf(ftable, "const %sEofCode = 1\n", prefix)
- fmt.Fprintf(ftable, "const %sErrCode = 2\n", prefix)
- fmt.Fprintf(ftable, "const %sMaxDepth = %v\n", prefix, stacksize)
+ ftable.WriteRune('\n')
+ fmt.Fprintf(ftable, "const %sEofCode = 1\n", prefix)
+ fmt.Fprintf(ftable, "const %sErrCode = 2\n", prefix)
+ fmt.Fprintf(ftable, "const %sMaxDepth = %v\n", prefix, stacksize)
//
// copy any postfix code
@@ -811,7 +813,8 @@ func defin(nt int, s string) int {
var peekline = 0
func gettok() int {
- var i, match, c int
+ var i int
+ var match, c rune
tokname = ""
for {
@@ -917,25 +920,25 @@ func gettok() int {
getword(c)
// find a reserved word
- for c = 0; c < len(resrv); c++ {
- if tokname == resrv[c].name {
+ for i := range resrv {
+ if tokname == resrv[i].name {
if tokflag {
fmt.Printf(">>> %%%v %v %v\n", tokname,
- resrv[c].value-PRIVATE, lineno)
+ resrv[i].value-PRIVATE, lineno)
}
- return resrv[c].value
+ return resrv[i].value
}
}
errorf("invalid escape, or illegal reserved word: %v", tokname)
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
- numbval = c - '0'
+ numbval = int(c - '0')
for {
c = getrune(finput)
if !isdigit(c) {
break
}
- numbval = numbval*10 + c - '0'
+ numbval = numbval*10 + int(c-'0')
}
ungetrune(finput, c)
if tokflag {
@@ -951,7 +954,7 @@ func gettok() int {
if tokflag {
fmt.Printf(">>> OPERATOR %v %v\n", string(c), lineno)
}
- return c
+ return int(c)
}
// look ahead to distinguish IDENTIFIER from IDENTCOLON
@@ -980,7 +983,7 @@ func gettok() int {
return IDENTIFIER
}
-func getword(c int) {
+func getword(c rune) {
tokname = ""
for isword(c) || isdigit(c) || c == '_' || c == '.' || c == '$' {
tokname += string(c)
@@ -1039,7 +1042,7 @@ func cpyunion() {
if !lflag {
fmt.Fprintf(ftable, "\n//line %v:%v\n", infile, lineno)
}
- fmt.Fprintf(ftable, "type\t%sSymType\tstruct", prefix)
+ fmt.Fprintf(ftable, "type %sSymType struct", prefix)
level := 0
@@ -1055,7 +1058,7 @@ out:
lineno++
case '{':
if level == 0 {
- fmt.Fprintf(ftable, "\n\tyys\tint;")
+ fmt.Fprintf(ftable, "\n\tyys int")
}
level++
case '}':
@@ -1065,7 +1068,7 @@ out:
}
}
}
- fmt.Fprintf(ftable, "\n")
+ fmt.Fprintf(ftable, "\n\n")
}
//
@@ -1105,7 +1108,7 @@ func cpycode() {
// skipcom is called after reading a '/'
//
func skipcom() int {
- var c int
+ var c rune
c = getrune(finput)
if c == '/' {
@@ -1163,7 +1166,7 @@ func dumpprod(curprod []int, max int) {
func cpyact(curprod []int, max int) {
if !lflag {
- fmt.Fprintf(fcode, "\n//line %v:%v\n", infile, lineno)
+ fmt.Fprintf(fcode, "\n\t\t//line %v:%v\n\t\t", infile, lineno)
}
lno := lineno
@@ -1177,14 +1180,13 @@ loop:
switch c {
case ';':
if brac == 0 {
- ftable.WriteRune(c)
+ fcode.WriteRune(c)
return
}
case '{':
if brac == 0 {
}
- ftable.WriteRune('\t')
brac++
case '$':
@@ -1220,7 +1222,7 @@ loop:
j := 0
if isdigit(c) {
for isdigit(c) {
- j = j*10 + c - '0'
+ j = j*10 + int(c-'0')
c = getrune(finput)
}
ungetrune(finput, c)
@@ -1345,7 +1347,9 @@ loop:
errorf("action does not terminate")
case '\n':
+ fmt.Fprint(fcode, "\n\t")
lineno++
+ continue loop
}
fcode.WriteRune(c)
@@ -2072,7 +2076,7 @@ func output() {
var c, u, v int
fmt.Fprintf(ftable, "\n//line yacctab:1\n")
- fmt.Fprintf(ftable, "var\t%sExca = []int {\n", prefix)
+ fmt.Fprintf(ftable, "var %sExca = []int{\n", prefix)
noset := mkset()
@@ -2145,10 +2149,12 @@ func output() {
}
fmt.Fprintf(ftable, "}\n")
- fmt.Fprintf(ftable, "const\t%sNprod\t= %v\n", prefix, nprod)
- fmt.Fprintf(ftable, "const\t%sPrivate\t= %v\n", prefix, PRIVATE)
- fmt.Fprintf(ftable, "var\t%sTokenNames []string\n", prefix)
- fmt.Fprintf(ftable, "var\t%sStates []string\n", prefix)
+ ftable.WriteRune('\n')
+ fmt.Fprintf(ftable, "const %sNprod = %v\n", prefix, nprod)
+ fmt.Fprintf(ftable, "const %sPrivate = %v\n", prefix, PRIVATE)
+ ftable.WriteRune('\n')
+ fmt.Fprintf(ftable, "var %sTokenNames []string\n", prefix)
+ fmt.Fprintf(ftable, "var %sStates []string\n", prefix)
}
//
@@ -2264,7 +2270,7 @@ func wract(i int) {
continue
}
if flag == 0 {
- fmt.Fprintf(ftable, "-1, %v,\n", i)
+ fmt.Fprintf(ftable, "\t-1, %v,\n", i)
}
flag++
fmt.Fprintf(ftable, "\t%v, %v,\n", p, p1)
@@ -2723,7 +2729,8 @@ nextn:
// write out the optimized parser
//
func aoutput() {
- fmt.Fprintf(ftable, "const\t%sLast\t= %v\n", prefix, maxa+1)
+ ftable.WriteRune('\n')
+ fmt.Fprintf(ftable, "const %sLast = %v\n\n", prefix, maxa+1)
arout("Act", amem, maxa+1)
arout("Pact", indgo, nstate)
arout("Pgo", pgo, nnonter+1)
@@ -2805,7 +2812,7 @@ func others() {
arout("Tok2", temp1, c+1)
// table 3 has everything else
- fmt.Fprintf(ftable, "var\t%sTok3\t= []int {\n", prefix)
+ fmt.Fprintf(ftable, "var %sTok3 = []int{\n\t", prefix)
c = 0
for i = 1; i <= ntokens; i++ {
j = tokset[i].value
@@ -2816,19 +2823,25 @@ func others() {
continue
}
- fmt.Fprintf(ftable, "%4d,%4d,", j, i)
+ if c%5 != 0 {
+ ftable.WriteRune(' ')
+ }
+ fmt.Fprintf(ftable, "%d, %d,", j, i)
c++
if c%5 == 0 {
- ftable.WriteRune('\n')
+ fmt.Fprint(ftable, "\n\t")
}
}
- fmt.Fprintf(ftable, "%4d,\n };\n", 0)
+ if c%5 != 0 {
+ ftable.WriteRune(' ')
+ }
+ fmt.Fprintf(ftable, "%d,\n}\n", 0)
// copy parser text
- c = getrune(finput)
- for c != EOF {
- ftable.WriteRune(c)
- c = getrune(finput)
+ ch := getrune(finput)
+ for ch != EOF {
+ ftable.WriteRune(ch)
+ ch = getrune(finput)
}
// copy yaccpar
@@ -2842,15 +2855,16 @@ func others() {
func arout(s string, v []int, n int) {
s = prefix + s
- fmt.Fprintf(ftable, "var\t%v\t= []int {\n", s)
+ fmt.Fprintf(ftable, "var %v = []int{\n", s)
for i := 0; i < n; i++ {
if i%10 == 0 {
- ftable.WriteRune('\n')
+ fmt.Fprintf(ftable, "\n\t")
+ } else {
+ ftable.WriteRune(' ')
}
- fmt.Fprintf(ftable, "%4d", v[i])
- ftable.WriteRune(',')
+ fmt.Fprintf(ftable, "%d,", v[i])
}
- fmt.Fprintf(ftable, "\n};\n")
+ fmt.Fprintf(ftable, "\n}\n")
}
//
@@ -2919,7 +2933,7 @@ func chcopy(q string) string {
}
func usage() {
- fmt.Fprintf(stderr, "usage: goyacc [-o output] [-v parsetable] input\n")
+ fmt.Fprintf(stderr, "usage: yacc [-o output] [-v parsetable] input\n")
exit(1)
}
@@ -2963,11 +2977,11 @@ func prlook(p Lkset) {
//
// utility routines
//
-var peekrune int
+var peekrune rune
-func isdigit(c int) bool { return c >= '0' && c <= '9' }
+func isdigit(c rune) bool { return c >= '0' && c <= '9' }
-func isword(c int) bool {
+func isword(c rune) bool {
return c >= 0xa0 || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
}
@@ -2997,8 +3011,8 @@ func putrune(f *bufio.Writer, c int) {
}
}
-func getrune(f *bufio.Reader) int {
- var r int
+func getrune(f *bufio.Reader) rune {
+ var r rune
if peekrune != 0 {
if peekrune == EOF {
@@ -3020,7 +3034,7 @@ func getrune(f *bufio.Reader) int {
return c
}
-func ungetrune(f *bufio.Reader, c int) {
+func ungetrune(f *bufio.Reader, c rune) {
if f != finput {
panic("ungetc - not finput")
}
@@ -3257,10 +3271,9 @@ $$default:
}
}
- /* the current p has no shift onn "error", pop stack */
+ /* the current p has no shift on "error", pop stack */
if $$Debug >= 2 {
- fmt.Printf("error recovery pops state %d, uncovers %d\n",
- $$S[$$p].yys, $$S[$$p-1].yys)
+ fmt.Printf("error recovery pops state %d\n", $$S[$$p].yys)
}
$$p--
}
@@ -3286,7 +3299,7 @@ $$default:
$$nt := $$n
$$pt := $$p
- _ = $$pt // guard against "declared and not used"
+ _ = $$pt // guard against "declared and not used"
$$p -= $$R2[$$n]
$$VAL = $$S[$$p+1]